From fe01eea7332ae6c04a085168d7f38ef5dab637fe Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 23 Nov 2007 15:13:27 -0500 Subject: [PATCH] Import 2.1.44 --- CREDITS | 3 +- Documentation/binfmt_misc.txt | 18 +- Documentation/m68k/amiboot.txt | 6 +- Documentation/networking/ax25.txt | 4 +- Documentation/networking/x25.txt | 1 - arch/alpha/defconfig | 18 +- arch/alpha/kernel/osf_sys.c | 7 +- arch/i386/defconfig | 10 +- arch/i386/kernel/irq.c | 2 +- arch/i386/kernel/setup.c | 2 +- arch/m68k/amiga/amifb.c | 6 - arch/m68k/amiga/config.c | 21 +- arch/m68k/amiga/cyberfb.c | 2 +- arch/m68k/atari/atakeyb.c | 2 +- arch/m68k/boot/amiga/bootstrap.c | 4 +- arch/m68k/boot/amiga/linuxboot.c | 53 +- arch/m68k/config.in | 6 + arch/m68k/console/fbcon.c | 24 +- arch/m68k/defconfig | 3 + arch/m68k/kernel/console.c | 4 + arch/m68k/mm/memory.c | 22 +- arch/mips/Makefile | 42 +- arch/mips/defconfig | 3 + arch/mips/jazz/g364.c | 11 +- arch/mips/kernel/gdb-stub.c | 4 +- arch/mips/kernel/head.S | 3 +- arch/mips/kernel/irixelf.c | 3 +- arch/mips/kernel/irq.c | 14 +- arch/mips/kernel/r2300_fpu.S | 6 +- arch/mips/kernel/r4k_fpu.S | 6 +- arch/mips/kernel/r6000_fpu.S | 6 +- arch/mips/kernel/signal.c | 30 +- arch/mips/kernel/syscalls.h | 3 +- arch/mips/kernel/sysirix.c | 17 +- arch/mips/kernel/sysmips.c | 7 +- arch/mips/kernel/time.c | 5 +- arch/mips/lib/checksum.c | 8 +- arch/mips/mm/r2300.c | 8 +- arch/mips/mm/r4xx0.c | 258 +++-- arch/mips/sgi/kernel/indy_int.c | 13 +- arch/mips/sgi/kernel/indy_timer.c | 6 +- arch/mips/sgi/kernel/setup.c | 4 +- arch/mips/sgi/kernel/system.c | 8 +- arch/mips/tools/offset.c | 20 + arch/sparc/kernel/cpu.c | 1 + arch/sparc/kernel/sparc_ksyms.c | 5 +- arch/sparc/mm/Makefile | 11 +- arch/sparc/mm/srmmu.c | 100 +- arch/sparc/mm/turbosparc.S | 46 + arch/sparc/prom/bootstr.c | 11 +- arch/sparc/prom/tree.c | 8 +- arch/sparc64/Makefile | 6 +- arch/sparc64/config.in | 5 +- arch/sparc64/defconfig | 48 +- arch/sparc64/kernel/Makefile | 19 +- arch/sparc64/kernel/binfmt_aout32.c | 477 +++++++++ arch/sparc64/kernel/cpu.c | 17 +- arch/sparc64/kernel/devices.c | 5 +- arch/sparc64/kernel/dtlb_miss.S | 33 +- arch/sparc64/kernel/entry.S | 554 ++++++---- arch/sparc64/kernel/etrap.S | 192 ++-- arch/sparc64/kernel/hack.S | 170 --- arch/sparc64/kernel/head.S | 255 ++--- arch/sparc64/kernel/ioport.c | 13 +- arch/sparc64/kernel/irq.c | 29 +- arch/sparc64/kernel/process.c | 262 ++--- arch/sparc64/kernel/ptrace.c | 370 ++++--- arch/sparc64/kernel/rtrap.S | 217 ++-- arch/sparc64/kernel/setup.c | 37 +- arch/sparc64/kernel/signal.c | 208 +++- arch/sparc64/kernel/signal32.c | 136 ++- arch/sparc64/kernel/sparc64_ksyms.c | 19 +- arch/sparc64/kernel/sunos_ioctl32.c | 276 +++++ arch/sparc64/kernel/sys32.S | 427 ++++++++ arch/sparc64/kernel/sys_sparc.c | 3 +- arch/sparc64/kernel/sys_sparc32.c | 760 +++++--------- arch/sparc64/kernel/sys_sunos32.c | 1468 ++++++++++++++++++++++++++ arch/sparc64/kernel/systbls.S | 94 +- arch/sparc64/kernel/time.c | 9 +- arch/sparc64/kernel/traps.c | 396 +++---- arch/sparc64/kernel/ttable.S | 16 +- arch/sparc64/kernel/unaligned.c | 58 +- arch/sparc64/kernel/winfixup.S | 119 +-- arch/sparc64/lib/Makefile | 48 +- arch/sparc64/lib/VIS.h | 113 ++ arch/sparc64/lib/VISbzero.S | 246 +++++ arch/sparc64/lib/VIScopy.S | 1052 +++++++++++++++++++ arch/sparc64/lib/VISmemset.S | 228 ++++ arch/sparc64/lib/blockops.S | 204 +--- arch/sparc64/lib/checksum.S | 1072 ++++++++++--------- arch/sparc64/lib/copy_from_user.S | 469 --------- arch/sparc64/lib/copy_to_user.S | 469 --------- arch/sparc64/lib/memcpy.S | 526 ---------- arch/sparc64/lib/memset.S | 197 ---- arch/sparc64/mm/Makefile | 10 +- arch/sparc64/mm/fault.c | 178 ++-- arch/sparc64/mm/generic.c | 36 +- arch/sparc64/mm/init.c | 231 +++-- arch/sparc64/mm/modutil.c | 66 ++ arch/sparc64/mm/ultra.S | 101 ++ arch/sparc64/prom/bootstr.c | 20 +- arch/sparc64/prom/misc.c | 7 +- arch/sparc64/prom/p1275.c | 84 +- arch/sparc64/vmlinux.lds | 12 +- drivers/block/floppy.c | 2 + drivers/char/psaux.c | 5 +- drivers/char/pty.c | 97 +- drivers/char/random.c | 1 + drivers/char/rtc.c | 2 +- drivers/char/sysrq.c | 4 +- drivers/char/tty_io.c | 1 + drivers/char/vc_screen.c | 5 + drivers/isdn/avmb1/capiutil.c | 1 + drivers/net/Config.in | 8 +- drivers/net/cops.c | 1 + drivers/net/cops_ffdrv.h | 1 + drivers/net/cops_ltdrv.h | 1 + drivers/pnp/parport_procfs.c | 1 - drivers/sbus/char/cgsix.c | 25 +- drivers/sbus/char/openprom.c | 10 +- drivers/sbus/char/sunfb.c | 3 +- drivers/sbus/char/sunkbd.c | 20 +- drivers/sbus/char/sunmouse.c | 2 +- drivers/sbus/char/sunserial.c | 11 +- drivers/scsi/BusLogic.h | 2 + drivers/scsi/amiga7xx.c | 13 +- drivers/scsi/ppa.c | 2 + drivers/scsi/sr_ioctl.c | 1 + drivers/sound/dev_table.h | 1 + fs/affs/namei.c | 4 +- fs/autofs/autofs_i.h | 11 +- fs/autofs/dir.c | 21 +- fs/autofs/dirhash.c | 28 +- fs/autofs/inode.c | 15 +- fs/autofs/root.c | 159 ++- fs/autofs/symlink.c | 14 +- fs/autofs/waitq.c | 16 +- fs/binfmt_elf.c | 37 +- fs/binfmt_misc.c | 45 +- fs/buffer.c | 1 + fs/dcache.c | 858 +++------------ fs/dquot.c | 2 +- fs/ext2/balloc.c | 6 +- fs/ext2/dir.c | 1 + fs/ext2/file.c | 1 + fs/ext2/namei.c | 323 ++---- fs/ext2/super.c | 3 +- fs/ext2/symlink.c | 35 +- fs/fat/misc.c | 4 +- fs/inode.c | 224 ++-- fs/minix/namei.c | 4 +- fs/namei.c | 1498 +++++++++++---------------- fs/nfs/dir.c | 534 +++------- fs/nfs/file.c | 1 + fs/nfs/inode.c | 5 +- fs/nfs/symlink.c | 36 +- fs/open.c | 109 +- fs/proc/arbitrary.c | 1 - fs/proc/array.c | 2 + fs/proc/base.c | 1 + fs/proc/fd.c | 29 +- fs/proc/generic.c | 11 + fs/proc/inode.c | 5 +- fs/proc/kmsg.c | 1 + fs/proc/link.c | 114 +- fs/proc/mem.c | 1 + fs/proc/net.c | 1 + fs/proc/omirr.c | 1 + fs/proc/openpromfs.c | 3 + fs/proc/root.c | 124 +-- fs/proc/scsi.c | 1 + fs/readdir.c | 173 +--- fs/stat.c | 21 +- fs/super.c | 150 ++- include/asm-alpha/pgtable.h | 3 + include/asm-i386/pgtable.h | 3 + include/asm-m68k/hardirq.h | 2 + include/asm-m68k/pgtable.h | 26 +- include/asm-mips/atomic.h | 19 +- include/asm-mips/byteorder.h | 68 +- include/asm-mips/checksum.h | 53 +- include/asm-mips/offset.h | 16 + include/asm-mips/pgtable.h | 3 + include/asm-mips/r4kcache.h | 5 +- include/asm-mips/sigcontext.h | 37 +- include/asm-mips/uaccess.h | 162 +-- include/asm-ppc/pgtable.h | 3 + include/asm-sparc/asi.h | 6 +- include/asm-sparc/ioctls.h | 4 +- include/asm-sparc/mbus.h | 6 +- include/asm-sparc/oplib.h | 6 +- include/asm-sparc/pgtable.h | 5 +- include/asm-sparc/turbosparc.h | 114 ++ include/asm-sparc64/asm_offsets.h | 70 +- include/asm-sparc64/atomic.h | 86 +- include/asm-sparc64/bitops.h | 16 +- include/asm-sparc64/checksum.h | 51 +- include/asm-sparc64/delay.h | 6 +- include/asm-sparc64/fpumacro.h | 68 +- include/asm-sparc64/head.h | 19 +- include/asm-sparc64/ioctls.h | 6 +- include/asm-sparc64/mmu_context.h | 33 +- include/asm-sparc64/page.h | 22 +- include/asm-sparc64/pgtable.h | 281 ++--- include/asm-sparc64/processor.h | 69 +- include/asm-sparc64/psrcompat.h | 28 +- include/asm-sparc64/pstate.h | 8 +- include/asm-sparc64/ptrace.h | 28 +- include/asm-sparc64/reg.h | 31 +- include/asm-sparc64/sigcontext.h | 14 +- include/asm-sparc64/string.h | 43 +- include/asm-sparc64/system.h | 55 +- include/asm-sparc64/uaccess.h | 67 +- include/asm-sparc64/uctx.h | 71 ++ include/asm-sparc64/vaddrs.h | 17 +- include/linux/binfmts.h | 1 + include/linux/console_struct.h | 9 +- include/linux/dalloc.h | 108 +- include/linux/ext2_fs.h | 20 +- include/linux/fs.h | 108 +- include/linux/ghash.h | 218 ++++ include/linux/proc_fs.h | 10 +- include/linux/rose.h | 23 +- include/linux/sched.h | 2 +- include/linux/selection.h | 6 +- include/linux/simp.h | 39 + include/linux/slab.h | 2 +- include/linux/tty.h | 12 +- include/linux/x25.h | 42 +- include/net/ax25.h | 59 +- include/net/lapb.h | 20 +- include/net/netrom.h | 47 +- include/net/rose.h | 65 +- include/net/x25.h | 35 +- kernel/exit.c | 4 +- kernel/fork.c | 6 +- kernel/ksyms.c | 6 +- kernel/module.c | 12 +- kernel/sysctl.c | 1 + mm/mmap.c | 2 + mm/slab.c | 2 +- mm/swapfile.c | 7 +- mm/vmalloc.c | 8 +- net/Makefile | 3 - net/README | 10 +- net/ax25/af_ax25.c | 308 +++--- net/ax25/ax25_addr.c | 46 +- net/ax25/ax25_dev.c | 2 +- net/ax25/ax25_ds_in.c | 86 +- net/ax25/ax25_ds_subr.c | 14 +- net/ax25/ax25_ds_timer.c | 172 +-- net/ax25/ax25_iface.c | 21 +- net/ax25/ax25_in.c | 17 +- net/ax25/ax25_ip.c | 31 +- net/ax25/ax25_out.c | 152 ++- net/ax25/ax25_route.c | 80 +- net/ax25/ax25_std_in.c | 132 +-- net/ax25/ax25_std_subr.c | 14 +- net/ax25/ax25_std_timer.c | 139 +-- net/ax25/ax25_subr.c | 34 +- net/ax25/ax25_timer.c | 202 +++- net/ax25/ax25_uid.c | 2 +- net/ax25/sysctl_net_ax25.c | 10 +- net/core/sysctl_net_core.c | 4 + net/ipv4/tcp_ipv4.c | 8 +- net/lapb/lapb_iface.c | 55 +- net/lapb/lapb_in.c | 110 +- net/lapb/lapb_out.c | 22 +- net/lapb/lapb_subr.c | 8 +- net/lapb/lapb_timer.c | 123 +-- net/netrom/af_netrom.c | 184 ++-- net/netrom/nr_dev.c | 2 +- net/netrom/nr_in.c | 60 +- net/netrom/nr_out.c | 104 +- net/netrom/nr_route.c | 27 +- net/netrom/nr_subr.c | 26 +- net/netrom/nr_timer.c | 208 ++-- net/netrom/sysctl_net_netrom.c | 16 +- net/rose/af_rose.c | 224 ++-- net/rose/rose_dev.c | 4 +- net/rose/rose_in.c | 95 +- net/rose/rose_link.c | 111 +- net/rose/rose_out.c | 58 +- net/rose/rose_route.c | 162 ++- net/rose/rose_subr.c | 56 +- net/rose/rose_timer.c | 172 ++- net/rose/sysctl_net_rose.c | 12 +- net/socket.c | 2 + net/unix/sysctl_net_unix.c | 4 + net/x25/af_x25.c | 168 +-- net/x25/sysctl_net_x25.c | 4 +- net/x25/x25_dev.c | 4 +- net/x25/x25_facilities.c | 2 +- net/x25/x25_in.c | 62 +- net/x25/x25_link.c | 79 +- net/x25/x25_out.c | 52 +- net/x25/x25_route.c | 16 +- net/x25/x25_subr.c | 24 +- net/x25/x25_timer.c | 134 ++- 299 files changed, 13209 insertions(+), 10978 deletions(-) create mode 100644 arch/sparc/mm/turbosparc.S create mode 100644 arch/sparc64/kernel/binfmt_aout32.c delete mode 100644 arch/sparc64/kernel/hack.S create mode 100644 arch/sparc64/kernel/sunos_ioctl32.c create mode 100644 arch/sparc64/kernel/sys32.S create mode 100644 arch/sparc64/kernel/sys_sunos32.c create mode 100644 arch/sparc64/lib/VIS.h create mode 100644 arch/sparc64/lib/VISbzero.S create mode 100644 arch/sparc64/lib/VIScopy.S create mode 100644 arch/sparc64/lib/VISmemset.S delete mode 100644 arch/sparc64/lib/copy_from_user.S delete mode 100644 arch/sparc64/lib/copy_to_user.S delete mode 100644 arch/sparc64/lib/memcpy.S delete mode 100644 arch/sparc64/lib/memset.S create mode 100644 arch/sparc64/mm/modutil.c create mode 100644 arch/sparc64/mm/ultra.S create mode 100644 include/asm-sparc/turbosparc.h create mode 100644 include/asm-sparc64/uctx.h create mode 100644 include/linux/ghash.h create mode 100644 include/linux/simp.h diff --git a/CREDITS b/CREDITS index 33d2c8b89289..ba5c57c52978 100644 --- a/CREDITS +++ b/CREDITS @@ -1055,8 +1055,9 @@ S: FIN-00330 Helsingfors S: Finland N: Jonathan Naylor -E: jsn@cs.nott.ac.uk +E: g4klx@g4klx.demon.co.uk E: g4klx@amsat.org +W: http://zone.pspt.fi/~jsn/ D: AX.25, NET/ROM and ROSE amateur radio protocol suites D: CCITT X.25 PLP and LAPB. S: 24 Castle View Drive diff --git a/Documentation/binfmt_misc.txt b/Documentation/binfmt_misc.txt index 81d49b2bcaa2..4fc550e91015 100644 --- a/Documentation/binfmt_misc.txt +++ b/Documentation/binfmt_misc.txt @@ -1,5 +1,5 @@ Kernel Support for miscellaneous (your favourite) Binary Formats v1.1 - ==================================================================== + ===================================================================== This Kernel feature allows to invoke almost (for restrictions see below) every program by simply typing it's name in the shell. @@ -47,15 +47,18 @@ A few examples (assumed you are in /proc/sys/fs/binfmt_misc): - enable Java(TM)-support (like binfmt_java): echo ":Java:M::\xca\xfe\xba\xbe::/usr/local/bin/java:" > register echo :Applet:M::\<\!--applet::/usr/local/bin/appletviewer: > register + - enable support for em86 (like binfmt_em86, for Alpha AXP only): echo ":i386:M::\x7fELF\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x03:\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfb\xff\xff:/bin/em86:" > register echo ":i486:M::\x7fELF\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x06:\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfb\xff\xff:/bin/em86:" > register + - enable support for packed DOS applications (pre-configured dosemu hdimages): echo ":DEXE:M::\x0eDEX::/usr/bin/dosexec:" > register + - enable support for DOS/Windows executables (using mzloader and dosemu/wine): echo ":DOSWin:M::MZ::/usr/sbin/mzloader:" > register echo ":DOS:E::com::/usr/sbin/mzloader:" > register - + echo ":DOS2:E::exe::/usr/sbin/mzloader:" > register You can enable/disable binfmt_misc or one binary type by echoing 0 (to disable) or 1 (to enable) to /proc/sys/fs/binfmt_misc/status or /proc/.../the_name. @@ -68,13 +71,12 @@ or /proc/sys/fs/binfmt_misc/status. HINTS: ====== -If your interpreter does not look at the PATH to determine the full name of the -program, you need to invoke a wrapper-script (like the following for java) first: +If you want to pass special arguments to your interpreter, you can +write a wrapper script for it. -#!/bin/sh -FOO=`which $1` || exit 1 -shift -/usr/local/bin/java $FOO ${1+$@} +Your interpreter should NOT look in the PATH for the filename; the +kernel passes it the full filename to use. Using the PATH can cause +unexpected behaviour and be a security hazard. There is a web page about binfmt_misc at diff --git a/Documentation/m68k/amiboot.txt b/Documentation/m68k/amiboot.txt index c119c6357b49..c6b369e28a87 100644 --- a/Documentation/m68k/amiboot.txt +++ b/Documentation/m68k/amiboot.txt @@ -1,9 +1,9 @@ - Linux/m68k Amiga Bootstrap version 5.5 + Linux/m68k Amiga Bootstrap version 5.6 -------------------------------------- Maintained by Geert Uytterhoeven (Geert.Uytterhoeven@cs.kuleuven.ac.be) -Last revised: March 27, 1997 +Last revised: June 12, 1997 0. Introduction @@ -22,7 +22,7 @@ and the Installation Guide first. Although the Installation Guide is getting a bit outdated, it's still a good starting point. -Amiboot 5.5 is meant for Linux/m68k 2.0.x, 2.1.x or higher (kernel bootinfo +Amiboot 5.6 is meant for Linux/m68k 2.0.x, 2.1.x or higher (kernel bootinfo interface versions 1.x and 2.x). Please use an older version for older kernels. diff --git a/Documentation/networking/ax25.txt b/Documentation/networking/ax25.txt index 937b9efe6839..7572cf7332d0 100644 --- a/Documentation/networking/ax25.txt +++ b/Documentation/networking/ax25.txt @@ -1,6 +1,6 @@ To use the amateur radio protocols within Linux you will need to get a suitable copy of the AX.25 Utilities. More detailed information about these -and associated programs can be found on http://www.cs.nott.ac.uk/~jsn/. +and associated programs can be found on http://zone.pspt.fi/~jsn/. For more information about the AX.25, NET/ROM and ROSE protocol stacks, see the AX25-HOWTO written by Terry Dawson @@ -13,4 +13,4 @@ of the message, the subject field is ignored. Jonathan G4KLX -jsn@cs.nott.ac.uk +g4klx@g4klx.demon.co.uk diff --git a/Documentation/networking/x25.txt b/Documentation/networking/x25.txt index 09681d565f6c..67f17e64419e 100644 --- a/Documentation/networking/x25.txt +++ b/Documentation/networking/x25.txt @@ -41,5 +41,4 @@ The contents of the Subject line are ignored. Jonathan -jsn@cs.nott.ac.uk g4klx@g4klx.demon.co.uk diff --git a/arch/alpha/defconfig b/arch/alpha/defconfig index 0c3c65d28509..6b21f96df9b5 100644 --- a/arch/alpha/defconfig +++ b/arch/alpha/defconfig @@ -83,6 +83,7 @@ CONFIG_INET=y # CONFIG_IP_ACCT is not set # CONFIG_IP_ROUTER is not set # CONFIG_NET_IPIP is not set +# CONFIG_SYN_COOKIES is not set # # (it is safe to leave these untouched) @@ -194,20 +195,22 @@ CONFIG_DE4X5=y # Filesystems # # CONFIG_QUOTA is not set +# CONFIG_DCACHE_PRELOAD is not set +# CONFIG_OMIRR is not set +# CONFIG_TRANS_NAMES is not set # CONFIG_MINIX_FS is not set CONFIG_EXT2_FS=y -CONFIG_FAT_FS=y -CONFIG_MSDOS_FS=y +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set # CONFIG_VFAT_FS is not set # CONFIG_UMSDOS_FS is not set CONFIG_PROC_FS=y -CONFIG_NFS_FS=y -# CONFIG_ROOT_NFS is not set +# CONFIG_NFS_FS is not set # CONFIG_NFSD is not set -CONFIG_SUNRPC=y -CONFIG_LOCKD=y +# CONFIG_SUNRPC is not set +# CONFIG_LOCKD is not set # CONFIG_SMB_FS is not set -CONFIG_ISO9660_FS=y +# CONFIG_ISO9660_FS is not set # CONFIG_HPFS_FS is not set # CONFIG_SYSV_FS is not set # CONFIG_AFFS_FS is not set @@ -245,3 +248,4 @@ CONFIG_PSMOUSE=y # Kernel hacking # # CONFIG_PROFILE is not set +# CONFIG_MAGIC_SYSRQ is not set diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c index 3b3d8574b944..96ecca700bcd 100644 --- a/arch/alpha/kernel/osf_sys.c +++ b/arch/alpha/kernel/osf_sys.c @@ -295,7 +295,7 @@ asmlinkage int osf_statfs(char *path, struct osf_statfs *buffer, unsigned long b retval = verify_area(VERIFY_WRITE, buffer, bufsiz); if (retval) goto out; - retval = namei(NAM_FOLLOW_LINK, path, &inode); + retval = namei(path, &inode); if (retval) goto out; retval = -ENOSYS; @@ -376,7 +376,7 @@ static int getdev(const char *name, int rdonly, struct inode **ino) struct file_operations *fops; int retval; - retval = namei(NAM_FOLLOW_LINK, name, &inode); + retval = namei(name, &inode); if (retval) return retval; if (!S_ISBLK(inode->i_mode)) { @@ -876,6 +876,9 @@ asmlinkage unsigned long osf_getsysinfo(unsigned long op, void *buffer, return -EOPNOTSUPP; } +/* Dummy functions for now */ +#define wrfpcr(x) do { } while (0) +#define rdfpcr() 0 asmlinkage unsigned long osf_setsysinfo(unsigned long op, void *buffer, unsigned long nbytes, diff --git a/arch/i386/defconfig b/arch/i386/defconfig index fd248746dc54..3fab47288c6d 100644 --- a/arch/i386/defconfig +++ b/arch/i386/defconfig @@ -195,11 +195,11 @@ CONFIG_EEXPRESS_PRO100=y # CONFIG_DCACHE_PRELOAD is not set # CONFIG_OMIRR is not set # CONFIG_TRANS_NAMES is not set -CONFIG_MINIX_FS=y +# CONFIG_MINIX_FS is not set CONFIG_EXT2_FS=y -CONFIG_FAT_FS=y -CONFIG_MSDOS_FS=y -CONFIG_VFAT_FS=y +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_VFAT_FS is not set # CONFIG_UMSDOS_FS is not set CONFIG_PROC_FS=y CONFIG_NFS_FS=y @@ -208,7 +208,7 @@ CONFIG_NFS_FS=y CONFIG_SUNRPC=y CONFIG_LOCKD=y # CONFIG_SMB_FS is not set -CONFIG_ISO9660_FS=y +# CONFIG_ISO9660_FS is not set # CONFIG_HPFS_FS is not set # CONFIG_SYSV_FS is not set # CONFIG_AFFS_FS is not set diff --git a/arch/i386/kernel/irq.c b/arch/i386/kernel/irq.c index c6183993193d..2e0f3e0843e1 100644 --- a/arch/i386/kernel/irq.c +++ b/arch/i386/kernel/irq.c @@ -208,7 +208,7 @@ static void math_error_irq(int cpl, void *dev_id, struct pt_regs *regs) math_error(); } -static struct irqaction irq13 = { math_error_irq, 0, 0, "math error", NULL, NULL }; +static struct irqaction irq13 = { math_error_irq, 0, 0, "fpu", NULL, NULL }; /* * IRQ2 is cascade interrupt to second interrupt controller diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index f62744d11cd0..feebfed90953 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -219,7 +219,7 @@ __initfunc(void setup_arch(char **cmdline_p, request_region(0x40,0x20,"timer"); request_region(0x80,0x10,"dma page reg"); request_region(0xc0,0x20,"dma2"); - request_region(0xf0,0x10,"npu"); + request_region(0xf0,0x10,"fpu"); } static const char * i486model(unsigned int nr) diff --git a/arch/m68k/amiga/amifb.c b/arch/m68k/amiga/amifb.c index 15e424ea71f5..eb72970d7e5b 100644 --- a/arch/m68k/amiga/amifb.c +++ b/arch/m68k/amiga/amifb.c @@ -1307,7 +1307,6 @@ static void ami_rebuild_copper(void); */ extern unsigned short ami_intena_vals[]; -extern void amiga_init_sound(void); /* * Support for Graphics Boards @@ -1810,11 +1809,6 @@ __initfunc(struct fb_info *amiga_fb_init(long *mem_start)) int err, tag, i; u_long chipptr; - /* - * Our beloved beeper - */ - amiga_init_sound(); - /* * Check for a Graphics Board */ diff --git a/arch/m68k/amiga/config.c b/arch/m68k/amiga/config.c index e36016306238..1571e1a0fe2c 100644 --- a/arch/m68k/amiga/config.c +++ b/arch/m68k/amiga/config.c @@ -85,6 +85,7 @@ static void amiga_serial_console_write(const char *s, unsigned int count); static void amiga_debug_init(void); extern void amiga_video_setup(char *, int *); +extern void amiga_init_sound(void); static struct console amiga_console_driver = { NULL, NULL, amiga_wait_key @@ -799,14 +800,18 @@ void amiga_serial_gets(char *s, int len) __initfunc(static void amiga_debug_init(void)) { - if (!strcmp( m68k_debug_device, "ser" )) { - /* no initialization required (?) */ - amiga_console_driver.write = amiga_serial_console_write; - } else if (!strcmp( m68k_debug_device, "mem" )) { - amiga_savekmsg_init(); - amiga_console_driver.write = amiga_mem_console_write; - } - register_console(&amiga_console_driver); + if (!strcmp( m68k_debug_device, "ser" )) { + /* no initialization required (?) */ + amiga_console_driver.write = amiga_serial_console_write; + } else if (!strcmp( m68k_debug_device, "mem" )) { + amiga_savekmsg_init(); + amiga_console_driver.write = amiga_mem_console_write; + } + register_console(&amiga_console_driver); + + /* our beloved beeper */ + if (AMIGAHW_PRESENT(AMI_AUDIO)) + amiga_init_sound(); } diff --git a/arch/m68k/amiga/cyberfb.c b/arch/m68k/amiga/cyberfb.c index 31e2a460858a..b7802a67c566 100644 --- a/arch/m68k/amiga/cyberfb.c +++ b/arch/m68k/amiga/cyberfb.c @@ -671,7 +671,7 @@ Cyber_WaitQueue (0x8000); * Rectangle Fill Solid */ void Cyber_RectFill (u_short x, u_short y, u_short width, u_short height, - u_short mode, u_short color) + u_short mode, u_short fcolor) { u_short blitcmd = S3_FILLEDRECT; diff --git a/arch/m68k/atari/atakeyb.c b/arch/m68k/atari/atakeyb.c index d5d45be01e8f..e09743e79182 100644 --- a/arch/m68k/atari/atakeyb.c +++ b/arch/m68k/atari/atakeyb.c @@ -419,7 +419,7 @@ static void keyboard_interrupt(int irq, void *dummy, struct pt_regs *fp) * break_flag... * */ int keyval = plain_map[scancode], keytyp; - + set_bit( scancode, broken_keys ); self_test_last_rcv = jiffies; keyval = plain_map[scancode]; diff --git a/arch/m68k/boot/amiga/bootstrap.c b/arch/m68k/boot/amiga/bootstrap.c index b109466ed06f..41ac75ada88c 100644 --- a/arch/m68k/boot/amiga/bootstrap.c +++ b/arch/m68k/boot/amiga/bootstrap.c @@ -36,10 +36,12 @@ #include #include #include -#include #include /* required Linux/m68k include files */ +#define __KERNEL_STRICT_NAMES /* This is ugly, I know */ +#define _LINUX_POSIX_TYPES_H +#include #include #include #include diff --git a/arch/m68k/boot/amiga/linuxboot.c b/arch/m68k/boot/amiga/linuxboot.c index 23b7fa9d0a98..74b873bf2a45 100644 --- a/arch/m68k/boot/amiga/linuxboot.c +++ b/arch/m68k/boot/amiga/linuxboot.c @@ -22,9 +22,11 @@ * for more details. * * History: + * 11 Jun 1997 Fix for unpadded gzipped ramdisks with bootinfo interface + * version 1.0 * 27 Mar 1997 FPU-less machines couldn't boot kernels that use bootinfo * interface version 1.0 (Geert) - * 03 Feb 1997 Implemented kernel decompression (Geert, based on Roman's + * 3 Feb 1997 Implemented kernel decompression (Geert, based on Roman's * code for ataboot) * 30 Dec 1996 Reverted the CPU detection to the old scheme * New boot parameter override scheme (Geert) @@ -55,7 +57,6 @@ #include #include #include -#include #include #include @@ -70,6 +71,10 @@ #undef custom #define custom ((*(volatile struct CUSTOM *)(CUSTOM_PHYSADDR))) +/* a.out linkage conventions */ +#undef SYMBOL_NAME_STR +#define SYMBOL_NAME_STR(X) "_"#X + /* temporary stack size */ #define TEMP_STACKSIZE (256) @@ -130,10 +135,9 @@ static int add_bi_record(u_short tag, u_short size, const void *data); static int add_bi_string(u_short tag, const u_char *s); static int check_bootinfo_version(const char *memptr); static void start_kernel(void (*startfunc)(), char *stackp, char *memptr, - u_long start_mem, u_long mem_size, u_long rd_size, - u_long kernel_size) __attribute__ ((noreturn)); + u_long start_mem, u_long kernel_size, u_long rd_dest, + u_long rd_size) __attribute__ ((noreturn)); asmlinkage u_long maprommed(void); -asmlinkage u_long check346(void); #ifdef ZKERNEL static int load_zkernel(int fd); static int KRead(int fd, void *buf, int cnt); @@ -682,7 +686,7 @@ u_long linuxboot(const struct linuxboot_args *args) if (debugflag) { if (bi.ramdisk.size) Printf("RAM disk at 0x%08lx, size is %ldK\n", - (u_long)memptr+kernel_size, bi.ramdisk.size>>10); + (u_long)memptr+kernel_size+bi_size, bi.ramdisk.size>>10); if (elf_kernel) { PutChar('\n'); @@ -703,11 +707,11 @@ u_long linuxboot(const struct linuxboot_args *args) Printf("\nKernel entry is 0x%08lx\n", elf_kernel ? kexec_elf.e_entry : kexec.a_entry); - Printf("ramdisk dest top is 0x%08lx\n", start_mem+mem_size); + Printf("ramdisk dest is 0x%08lx\n", bi.ramdisk.addr); Printf("ramdisk lower limit is 0x%08lx\n", - (u_long)(memptr+kernel_size)); + (u_long)memptr+kernel_size+bi_size); Printf("ramdisk src top is 0x%08lx\n", - (u_long)(memptr+kernel_size)+rd_size); + (u_long)memptr+kernel_size+bi_size+rd_size); Puts("\nType a key to continue the Linux/m68k boot..."); GetChar(); @@ -743,7 +747,7 @@ u_long linuxboot(const struct linuxboot_args *args) /* execute the copy-and-go code (from CHIP RAM) */ start_kernel(startfunc, (char *)stack+TEMP_STACKSIZE, memptr, start_mem, - mem_size, rd_size, kernel_size); + kernel_size, bi.ramdisk.addr, rd_size); /* Clean up and exit in case of a failure */ Fail: @@ -1065,6 +1069,7 @@ static int create_compat_bootinfo(void) compat_bootinfo.memory[i].size = bi.memory[i].size; } if (bi.ramdisk.size) { + bi.ramdisk.addr &= 0xfffffc00; compat_bootinfo.ramdisk_size = (bi.ramdisk.size+1023)/1024; compat_bootinfo.ramdisk_addr = bi.ramdisk.addr; } else { @@ -1151,14 +1156,14 @@ static int check_bootinfo_version(const char *memptr) */ static void start_kernel(void (*startfunc)(), char *stackp, char *memptr, - u_long start_mem, u_long mem_size, u_long rd_size, - u_long kernel_size) + u_long start_mem, u_long kernel_size, u_long rd_dest, + u_long rd_size) { register void (*a0)() __asm("a0") = startfunc; register char *a2 __asm("a2") = stackp; register char *a3 __asm("a3") = memptr; register u_long a4 __asm("a4") = start_mem; - register u_long d0 __asm("d0") = mem_size; + register u_long d0 __asm("d0") = rd_dest; register u_long d1 __asm("d1") = rd_size; register u_long d2 __asm("d2") = kernel_size; register u_long d3 __asm("d3") = bi_size; @@ -1182,7 +1187,7 @@ static void start_kernel(void (*startfunc)(), char *stackp, char *memptr, * * a3 = memptr * a4 = start_mem - * d0 = mem_size + * d0 = rd_dest * d1 = rd_size * d2 = kernel_size * d3 = bi_size @@ -1210,18 +1215,16 @@ SYMBOL_NAME_STR(copyall) ": dbra d7,1b | *dest++ = *src++ | /* copy the ramdisk to the top of memory */ - | /* (from back to front) */ - movel a4,a1 | dest = (u_long *)(start_mem+mem_size); - addl d0,a1 - movel a3,a2 | limit = (u_long *)(memptr+kernel_size + - addl d2,a2 | bi_size); - addl d3,a2 - movel a2,a0 | src = (u_long *)((u_long)limit+rd_size); - addl d1,a0 + movel a3,a0 | src = (u_long *)(memptr+kernel_size+bi_size); + addl d2,a0 + addl d3,a0 + movel d0,a1 | dest = (u_long *)rd_dest; + movel a0,a2 | limit = (u_long *)(memptr+kernel_size+ + addl d1,a2 | bi_size+rd_size); 1: cmpl a0,a2 - beqs 2f | while (src > limit) - moveb a0@-,a1@- | *--dest = *--src; - bras 1b + jeq 2f | while (src > limit) + moveb a0@+,a1@+ | *dest++ = *src++; + jra 1b 2: | /* jump to start of kernel */ movel a4,a0 | jump_to (start_mem); diff --git a/arch/m68k/config.in b/arch/m68k/config.in index df6a45a9d123..b963687aecb0 100644 --- a/arch/m68k/config.in +++ b/arch/m68k/config.in @@ -52,6 +52,10 @@ bool 'System V IPC' CONFIG_SYSVIPC bool 'Sysctl support' CONFIG_SYSCTL tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC +fi + if [ "$CONFIG_AMIGA" = "y" ]; then bool 'Amiga AutoConfig Identification' CONFIG_ZORRO bool 'Amiga OCS chipset support' CONFIG_AMIFB_OCS @@ -153,6 +157,7 @@ if [ "$CONFIG_AMIGA" = "y" ]; then bool 'A4000T SCSI support' CONFIG_A4000T_SCSI bool 'A4091 SCSI support' CONFIG_A4091_SCSI bool 'WarpEngine SCSI support' CONFIG_WARPENGINE_SCSI + bool 'GVP Turbo 040/060 SCSI support' CONFIG_GVP_TURBO_SCSI fi fi if [ "$CONFIG_ATARI" = "y" ]; then @@ -273,5 +278,6 @@ bool 'Kernel profiling support' CONFIG_PROFILE if [ "$CONFIG_PROFILE" = "y" ]; then int ' Profile shift count' CONFIG_PROFILE_SHIFT 2 fi +bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ bool 'Remote debugging support' CONFIG_KGDB endmenu diff --git a/arch/m68k/console/fbcon.c b/arch/m68k/console/fbcon.c index 62457c0cbaf2..5065e576958f 100644 --- a/arch/m68k/console/fbcon.c +++ b/arch/m68k/console/fbcon.c @@ -97,7 +97,6 @@ extern int console_blanked; #undef CONFIG_FBCON_CYBER #undef CONFIG_FBCON_RETINAZ3 - /* Monochrome is default */ #define CONFIG_FBCON_MONO @@ -450,14 +449,17 @@ static void putcs_cyber(struct vc_data *conp, struct display *p, const char *s, int count, int yy, int xx); static void rev_char_cyber(struct display *p, int xx, int yy); -extern void Cyber_WaitQueue(u_short fifo); +extern void Cyber_WaitQueue(unsigned short fifo); extern void Cyber_WaitBlit(void); -extern void Cyber_BitBLT(u_short curx, u_short cury, u_short destx, - u_short desty, u_short width, u_short height, - u_short mode); -extern void Cyber_RectFill(u_short xx, u_short yy, u_short width, u_short height, - u_short mode, u_short color); -extern void Cyber_MoveCursor(u_short xx, u_short yy); +extern void Cyber_BitBLT(unsigned short curx, unsigned short cury, + unsigned short destx, unsigned short desty, + unsigned short width, unsigned short height, + unsigned short mode); +extern void Cyber_RectFill(unsigned short xx, unsigned short yy, + unsigned short width, unsigned short + height, unsigned short mode, + unsigned short fcolor); +extern void Cyber_MoveCursor(unsigned short xx, unsigned short yy); #endif /* CONFIG_FBCON_CYBER */ #ifdef CONFIG_FBCON_RETINAZ3 @@ -3835,7 +3837,7 @@ static void putc_cyber(struct vc_data *conp, struct display *p, int c, int yy, c &= 0xff; - dest = p->screen_base+y*p->fontheight*p->next_line+8*x; + dest = p->screen_base + yy * p->fontheight * p->next_line + 8 * xx; cdat = p->fontdata+(c*p->fontheight); fg = disp->fgcol; bg = disp->bgcol; @@ -3874,7 +3876,7 @@ static void putcs_cyber(struct vc_data *conp, struct display *p, const char *s, u_char c, d; u_char fg, bg; - dest0 = p->screen_base+y*p->fontheight*p->next_line+8*x; + dest0 = p->screen_base + yy * p->fontheight * p->next_line + 8 * xx; fg = disp->fgcol; bg = disp->bgcol; revs = conp->vc_reverse; @@ -3918,7 +3920,7 @@ static void rev_char_cyber(struct display *p, int xx, int yy) fg = disp->fgcol; bg = disp->bgcol; - dest = p->screen_base+y*p->fontheight*p->next_line+8*x; + dest = p->screen_base + yy * p->fontheight * p->next_line + 8 * xx; Cyber_WaitBlit(); for (rows = p->fontheight; rows--; dest += p->next_line) { *dest = (*dest == fg) ? bg : fg; diff --git a/arch/m68k/defconfig b/arch/m68k/defconfig index 65f71d5a9abd..18763c58bfbe 100644 --- a/arch/m68k/defconfig +++ b/arch/m68k/defconfig @@ -152,6 +152,9 @@ CONFIG_NETDEVICES=y # Filesystems # # CONFIG_QUOTA is not set +# CONFIG_DCACHE_PRELOAD is not set +# CONFIG_OMIRR is not set +# CONFIG_TRANS_NAMES is not set CONFIG_MINIX_FS=y CONFIG_EXT2_FS=y CONFIG_FAT_FS=y diff --git a/arch/m68k/kernel/console.c b/arch/m68k/kernel/console.c index 31608e904f60..204f4d3cc55e 100644 --- a/arch/m68k/kernel/console.c +++ b/arch/m68k/kernel/console.c @@ -109,6 +109,7 @@ #include #include #include +#include #include #include #include @@ -164,9 +165,12 @@ extern void set_palette(void); void poke_blanked_console(void); void do_blank_screen(int); +#if 0 +/* Make sure there are no references left to this variables. */ unsigned long video_num_lines; unsigned long video_num_columns; unsigned long video_size_row; +#endif static int printable = 0; /* Is console ready for printing? */ unsigned long video_font_height; /* Height of current screen font */ diff --git a/arch/m68k/mm/memory.c b/arch/m68k/mm/memory.c index dea7695e849f..a135143b25c0 100644 --- a/arch/m68k/mm/memory.c +++ b/arch/m68k/mm/memory.c @@ -555,16 +555,18 @@ void cache_clear (unsigned long paddr, int len) int tmp; /* - * cwe need special treatment for the first page, in case it - * is not page-aligned. + * We need special treatment for the first page, in case it + * is not page-aligned. Page align the addresses to work + * around bug I17 in the 68060. */ if ((tmp = -paddr & (PAGE_SIZE - 1))) { - pushcl040(paddr); + pushcl040(paddr & PAGE_MASK); if ((len -= tmp) <= 0) return; paddr += tmp; } tmp = PAGE_SIZE; + paddr &= PAGE_MASK; while ((len -= tmp) >= 0) { clear040(paddr); paddr += tmp; @@ -600,6 +602,13 @@ void cache_push (unsigned long paddr, int len) * the '060! */ len += paddr & (PAGE_SIZE - 1); + + /* + * Work around bug I17 in the 68060 affecting some instruction + * lines not being invalidated properly. + */ + paddr &= PAGE_MASK; + do { pushcli040(paddr); paddr += tmp; @@ -638,6 +647,13 @@ void cache_push_v (unsigned long vaddr, int len) /* on 68040, push cache lines for pages in the range */ len += vaddr & (PAGE_SIZE - 1); + + /* + * Work around bug I17 in the 68060 affecting some instruction + * lines not being invalidated properly. + */ + vaddr &= PAGE_MASK; + do { pushv040(vaddr); vaddr += tmp; diff --git a/arch/mips/Makefile b/arch/mips/Makefile index 4266aa991ddb..7eb56f4bf98e 100644 --- a/arch/mips/Makefile +++ b/arch/mips/Makefile @@ -1,6 +1,4 @@ # -# $Id: Makefile,v 1.5 1997/06/16 00:34:01 ralf Exp $ -# # arch/mips/Makefile # # This file is included by the global makefile so that you can add your own @@ -15,27 +13,26 @@ # Copyright (C) 1994, 1995, 1996 by Ralf Baechle # DECStation modifications by Paul M. Antoine, 1996 # +# $Id: Makefile,v 1.7 1997/06/30 15:52:03 ralf Exp $ +# +# +# Select the object file format to substitute into the linker script. +# ifdef CONFIG_CPU_LITTLE_ENDIAN -CROSS_COMPILE = mipsel- +CROSS_COMPILE = mipsel-linux- ifdef CONFIG_MIPS_ECOFF -oformat = ecoff-littlemips +oformat = ecoff-littlemips else -oformat = a.out-mips-big-linux +oformat = elf32-littlemips endif else -CROSS_COMPILE = mips- +CROSS_COMPILE = mips-linux- ifdef CONFIG_MIPS_ECOFF -oformat = ecoff-bigmips +oformat = ecoff-bigmips else -oformat = a.out-mips-big-linux -endif +oformat = elf32-bigmips endif - -ifdef CONFIG_EXTRA_ELF_COMPILER -CROSS_COMPILE := $(CROSS_COMPILE)linuxelf- -else -CROSS_COMPILE := $(CROSS_COMPILE)linux- endif LINKFLAGS = -static -N @@ -95,11 +92,6 @@ CORE_FILES += arch/mips/algor/algor.o SUBDIRS += arch/mips/algor #LOADADDR += 0x80000000 endif -ifdef CONFIG_ACER_PICA_61 -CORE_FILES += arch/mips/jazz/jazz.o -SUBDIRS += arch/mips/jazz -LOADADDR += 0x80000000 -endif ifdef CONFIG_DECSTATION CORE_FILES += arch/mips/dec/dec.o SUBDIRS += arch/mips/dec @@ -116,17 +108,15 @@ CORE_FILES += arch/mips/deskstation/deskstation.o SUBDIRS += arch/mips/deskstation LOADADDR += 0x80000000 endif -ifdef CONFIG_MIPS_MAGNUM_3000 -LOADADDR += 0x80000000 -endif -ifdef CONFIG_MIPS_MAGNUM_4000 +# +# Acer PICA 61, Mips Magnum 4000 and Olivetti M700. +# +ifdef CONFIG_MIPS_JAZZ CORE_FILES += arch/mips/jazz/jazz.o SUBDIRS += arch/mips/jazz LOADADDR += 0x80000000 endif -ifdef CONFIG_OLIVETTI_M700 -CORE_FILES += arch/mips/jazz/jazz.o -SUBDIRS += arch/mips/jazz +ifdef CONFIG_MIPS_MAGNUM_3000 LOADADDR += 0x80000000 endif ifdef CONFIG_SNI_RM200_PCI diff --git a/arch/mips/defconfig b/arch/mips/defconfig index 33f54a301f99..9c6fb90739c0 100644 --- a/arch/mips/defconfig +++ b/arch/mips/defconfig @@ -153,6 +153,9 @@ CONFIG_PCNET32=y # Filesystems # # CONFIG_QUOTA is not set +# CONFIG_DCACHE_PRELOAD is not set +# CONFIG_OMIRR is not set +# CONFIG_TRANS_NAMES is not set # CONFIG_MINIX_FS is not set CONFIG_EXT2_FS=y # CONFIG_FAT_FS is not set diff --git a/arch/mips/jazz/g364.c b/arch/mips/jazz/g364.c index 92e13052282a..1503cc559ffe 100644 --- a/arch/mips/jazz/g364.c +++ b/arch/mips/jazz/g364.c @@ -25,6 +25,11 @@ #include #include #include +#include +#include +#include +#include +#include #include #include @@ -33,12 +38,6 @@ #include #include -#include "../../../drivers/char/kbd_kern.h" -#include "../../../drivers/char/vt_kern.h" -#include "../../../drivers/char/consolemap.h" -#include "../../../drivers/char/selection.h" -#include "../../../drivers/char/console_struct.h" - extern void register_console(void (*proc)(const char *)); extern void console_print(const char *); unsigned video_res_x; diff --git a/arch/mips/kernel/gdb-stub.c b/arch/mips/kernel/gdb-stub.c index 255ec2228635..452e419e3bae 100644 --- a/arch/mips/kernel/gdb-stub.c +++ b/arch/mips/kernel/gdb-stub.c @@ -11,6 +11,8 @@ * Send complaints, suggestions etc. to * * Copyright (C) 1995 Andreas Busse + * + * $Id: gdb-stub.c,v 1.4 1997/06/30 15:52:25 ralf Exp $ */ /* @@ -328,7 +330,7 @@ void set_debug_traps(void) unsigned long flags; unsigned char c; - save_flags(flags); cli(); + save_and_cli(flags); for (ht = hard_trap_info; ht->tt && ht->signo; ht++) set_except_vector(ht->tt, trap_low); diff --git a/arch/mips/kernel/head.S b/arch/mips/kernel/head.S index 27c700a95c2a..3251c89d9bec 100644 --- a/arch/mips/kernel/head.S +++ b/arch/mips/kernel/head.S @@ -347,6 +347,8 @@ probe_done: jal prom_init /* prom_init(argc, argv, envp); */ nop + jal sgi_sysinit + nop #endif /* Get the very one tags we need early in the boot process */ nop @@ -405,7 +407,6 @@ probe_done: mfc0 t0, CP0_STATUS li t1, ~(ST0_CU1|ST0_CU2|ST0_CU3|ST0_KX|ST0_SX) and t0, t1 - li t1, ST0_CU0 or t0, ST0_CU0 mtc0 t0, CP0_STATUS diff --git a/arch/mips/kernel/irixelf.c b/arch/mips/kernel/irixelf.c index d994155d05b1..fc0d542fc154 100644 --- a/arch/mips/kernel/irixelf.c +++ b/arch/mips/kernel/irixelf.c @@ -465,7 +465,7 @@ static inline int look_for_irix_interpreter(char **name, goto losing; old_fs = get_fs(); set_fs(get_ds()); - retval = namei(*name, interpreter_inode); + retval = namei(NAM_FOLLOW_LINK, *name, interpreter_inode); set_fs(old_fs); if(retval < 0) goto losing; @@ -973,6 +973,7 @@ unsigned long irix_mapelf(int fd, struct elf_phdr *user_phdrp, int cnt) */ static int dump_write(struct file *file, const void *addr, int nr) { + file->f_inode->i_status |= ST_MODIFIED; return file->f_op->write(file->f_inode, file, addr, nr) == nr; } diff --git a/arch/mips/kernel/irq.c b/arch/mips/kernel/irq.c index 0896ed1c7e74..89631b41614f 100644 --- a/arch/mips/kernel/irq.c +++ b/arch/mips/kernel/irq.c @@ -10,6 +10,8 @@ * should be easier. * * Mips support by Ralf Baechle and Andreas Busse + * + * $Id: irq.c,v 1.6 1997/06/30 15:52:34 ralf Exp $ */ #include #include @@ -78,8 +80,7 @@ void disable_irq(unsigned int irq_nr) { unsigned long flags; - save_flags(flags); - cli(); + save_and_cli(flags); mask_irq(irq_nr); restore_flags(flags); } @@ -87,8 +88,7 @@ void disable_irq(unsigned int irq_nr) void enable_irq(unsigned int irq_nr) { unsigned long flags; - save_flags(flags); - cli(); + save_and_cli(flags); unmask_irq(irq_nr); restore_flags(flags); } @@ -226,8 +226,7 @@ int setup_x86_irq(int irq, struct irqaction * new) if (new->flags & SA_SAMPLE_RANDOM) rand_initialize_irq(irq); - save_flags(flags); - cli(); + save_and_cli(flags); *p = new; if (!shared) { @@ -287,8 +286,7 @@ void free_irq(unsigned int irq, void *dev_id) continue; /* Found it - now free it */ - save_flags(flags); - cli(); + save_and_cli(flags); *p = action->next; if (!irq[irq_action]) { mask_irq(irq); diff --git a/arch/mips/kernel/r2300_fpu.S b/arch/mips/kernel/r2300_fpu.S index 2cea754077e9..c2a0453c4faa 100644 --- a/arch/mips/kernel/r2300_fpu.S +++ b/arch/mips/kernel/r2300_fpu.S @@ -1,4 +1,4 @@ -/* $Id: r2300_fpu.S,v 1.1 1997/06/06 09:32:55 ralf Exp $ +/* * r2300_fpu.S: Save/restore floating point context for signal handlers. * * This file is subject to the terms and conditions of the GNU General Public @@ -9,12 +9,14 @@ * * Multi-arch abstraction and asm macros for easier reading: * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * + * $Id: r2300_fpu.S,v 1.2 1997/06/25 16:57:15 ralf Exp $ */ #include #include #include +#include #include -#include .set mips3 .set noreorder diff --git a/arch/mips/kernel/r4k_fpu.S b/arch/mips/kernel/r4k_fpu.S index 1bc4538dbdcc..b020266d175a 100644 --- a/arch/mips/kernel/r4k_fpu.S +++ b/arch/mips/kernel/r4k_fpu.S @@ -1,4 +1,4 @@ -/* $Id: r4k_fpu.S,v 1.1 1997/06/06 09:33:04 ralf Exp $ +/* * r4k_fpu.S: Save/restore floating point context for signal handlers. * * This file is subject to the terms and conditions of the GNU General Public @@ -9,12 +9,14 @@ * * Multi-arch abstraction and asm macros for easier reading: * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * + * $Id: r4k_fpu.S,v 1.2 1997/06/25 16:57:18 ralf Exp $ */ #include #include #include +#include #include -#include .set noreorder .set mips3 diff --git a/arch/mips/kernel/r6000_fpu.S b/arch/mips/kernel/r6000_fpu.S index 215e05027af1..6fb8f122eb55 100644 --- a/arch/mips/kernel/r6000_fpu.S +++ b/arch/mips/kernel/r6000_fpu.S @@ -1,4 +1,4 @@ -/* $Id: r6000_fpu.S,v 1.1 1997/06/06 09:33:14 ralf Exp $ +/* * r6000_fpu.S: Save/restore floating point context for signal handlers. * * This file is subject to the terms and conditions of the GNU General Public @@ -9,12 +9,14 @@ * * Multi-arch abstraction and asm macros for easier reading: * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * + * $Id: r6000_fpu.S,v 1.2 1997/06/25 16:57:19 ralf Exp $ */ #include #include #include +#include #include -#include .set noreorder /* Save floating point context */ diff --git a/arch/mips/kernel/signal.c b/arch/mips/kernel/signal.c index 4f975af1ae25..8c6cde991b30 100644 --- a/arch/mips/kernel/signal.c +++ b/arch/mips/kernel/signal.c @@ -3,6 +3,8 @@ * * Copyright (C) 1991, 1992 Linus Torvalds * Copyright (C) 1994, 1995, 1996 Ralf Baechle + * + * $Id: signal.c,v 1.7 1997/06/25 19:25:08 ralf Exp $ */ #include #include @@ -69,24 +71,36 @@ asmlinkage int sys_sigreturn(struct pt_regs *regs) (regs->regs[29] & (SZREG - 1))) goto badframe; - current->blocked = context->sc_sigset & _BLOCKABLE; - regs->cp0_epc = context->sc_pc; + current->blocked = context->sc_sigset & _BLOCKABLE; /* XXX */ + regs->cp0_epc = context->sc_pc; /* XXX */ +/* + * Disabled because we only use the lower 32 bit of the registers. + */ +#if 0 /* * We only allow user processes in 64bit mode (n32, 64 bit ABI) to * restore the upper half of registers. */ - if (read_32bit_cp0_register(CP0_STATUS) & ST0_UX) + if (read_32bit_cp0_register(CP0_STATUS) & ST0_UX) { for(i = 31;i >= 0;i--) __get_user(regs->regs[i], &context->sc_regs[i]); - else + __get_user(regs->hi, &context->sc_mdhi); + __get_user(regs->lo, &context->sc_mdlo); + } else +#endif + { + long long reg; for(i = 31;i >= 0;i--) { - __get_user(regs->regs[i], &context->sc_regs[i]); - regs->regs[i] = (int) regs->regs[i]; + __get_user(reg, &context->sc_regs[i]); + regs->regs[i] = (int) reg; } + __get_user(reg, &context->sc_mdhi); + regs->hi = (int) reg; + __get_user(reg, &context->sc_mdlo); + regs->lo = (int) reg; + } - __get_user(regs->hi, &context->sc_mdhi); - __get_user(regs->lo, &context->sc_mdlo); restore_fp_context(context); /* diff --git a/arch/mips/kernel/syscalls.h b/arch/mips/kernel/syscalls.h index 60f58297d982..c448ce99eee7 100644 --- a/arch/mips/kernel/syscalls.h +++ b/arch/mips/kernel/syscalls.h @@ -6,6 +6,8 @@ * for more details. * * Copyright (C) 1995, 1996 by Ralf Baechle + * + * $Id: syscalls.h,v 1.5 1997/06/25 17:08:35 ralf Exp $ */ /* @@ -174,7 +176,6 @@ SYS(sys_mlock, 2) SYS(sys_munlock, 2) /* 4155 */ SYS(sys_mlockall, 1) SYS(sys_munlockall, 0) -SYS(sys_nfsservctl, 3) SYS(sys_sched_setparam,2) SYS(sys_sched_getparam,2) SYS(sys_sched_setscheduler,3) /* 4160 */ diff --git a/arch/mips/kernel/sysirix.c b/arch/mips/kernel/sysirix.c index 671eb5f73028..2e8363f41460 100644 --- a/arch/mips/kernel/sysirix.c +++ b/arch/mips/kernel/sysirix.c @@ -1,4 +1,4 @@ -/* $Id: sysirix.c,v 1.1 1997/06/06 09:33:25 ralf Exp $ +/* $Id: sysirix.c,v 1.2 1997/06/17 15:24:26 ralf Exp $ * sysirix.c: IRIX system call emulation. * * Copyright (C) 1996 David S. Miller @@ -664,7 +664,7 @@ asmlinkage int irix_statfs(const char *path, struct irix_statfs *buf, error = verify_area(VERIFY_WRITE, buf, sizeof(struct irix_statfs)); if (error) goto out; - error = namei(path,&inode); + error = namei(NAM_FOLLOW_LINK, path, &inode); if (error) goto out; if (!inode->i_sb->s_op->statfs) { @@ -1390,7 +1390,7 @@ asmlinkage int irix_statvfs(char *fname, struct irix_statvfs *buf) error = verify_area(VERIFY_WRITE, buf, sizeof(struct irix_statvfs)); if(error) goto out; - error = namei(fname, &inode); + error = namei(NAM_FOLLOW_LINK, fname, &inode); if(error) goto out; if(!inode->i_sb->s_op->statfs) { @@ -1489,8 +1489,8 @@ out: return error; } -#define NOFOLLOW_LINKS 0 -#define FOLLOW_LINKS 1 +#define NOFOLLOW_LINKS NAM_FOLLOW_TRAILSLASH +#define FOLLOW_LINKS NAM_FOLLOW_LINK static inline int chown_common(char *filename, uid_t user, gid_t group, int follow) { @@ -1498,10 +1498,7 @@ static inline int chown_common(char *filename, uid_t user, gid_t group, int foll int error; struct iattr newattrs; - if(follow == NOFOLLOW_LINKS) - error = lnamei(filename,&inode); - else - error = namei(filename,&inode); + error = namei(follow, filename,&inode); if (error) return error; if (IS_RDONLY(inode)) { @@ -1734,7 +1731,7 @@ asmlinkage int irix_statvfs64(char *fname, struct irix_statvfs64 *buf) error = verify_area(VERIFY_WRITE, buf, sizeof(struct irix_statvfs)); if(error) goto out; - error = namei(fname, &inode); + error = namei(NAM_FOLLOW_LINK, fname, &inode); if(error) goto out; if(!inode->i_sb->s_op->statfs) { diff --git a/arch/mips/kernel/sysmips.c b/arch/mips/kernel/sysmips.c index c5e394d565f9..32dce029d2e5 100644 --- a/arch/mips/kernel/sysmips.c +++ b/arch/mips/kernel/sysmips.c @@ -5,7 +5,9 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 1995 by Ralf Baechle + * Copyright (C) 1995, 1996, 1997 by Ralf Baechle + * + * $Id: sysmips.c,v 1.4 1997/06/30 15:52:37 ralf Exp $ */ #include #include @@ -79,8 +81,7 @@ sys_sysmips(int cmd, int arg1, int arg2, int arg3) retval = verify_area(VERIFY_WRITE, p, sizeof(*p)); if (retval) goto out; - save_flags(flags); - cli(); + save_and_cli(flags); retval = *p; *p = arg2; restore_flags(flags); diff --git a/arch/mips/kernel/time.c b/arch/mips/kernel/time.c index fd57f6d0ec82..e00ebc76d1b3 100644 --- a/arch/mips/kernel/time.c +++ b/arch/mips/kernel/time.c @@ -5,6 +5,8 @@ * * This file contains the time handling details for PC-style clocks as * found in some MIPS systems. + * + * $Id: time.c,v 1.4 1997/06/30 15:52:40 ralf Exp $ */ #include #include @@ -134,8 +136,7 @@ void do_gettimeofday(struct timeval *tv) { unsigned long flags; - save_flags(flags); - cli(); + save_and_cli(flags); *tv = xtime; tv->tv_usec += do_gettimeoffset(); diff --git a/arch/mips/lib/checksum.c b/arch/mips/lib/checksum.c index ee48f659a91d..208c0c3cc81e 100644 --- a/arch/mips/lib/checksum.c +++ b/arch/mips/lib/checksum.c @@ -13,8 +13,12 @@ * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. + * + * $Id: checksum.c,v 1.4 1997/07/03 09:43:16 ralf Exp $ */ #include +#include +#include #include #include @@ -36,7 +40,7 @@ static inline unsigned long do_csum(const unsigned char * buff, int len) goto out; odd = 1 & (unsigned long) buff; if (odd) { - result = *buff; + result = be16_to_cpu(*buff); len--; buff++; } @@ -68,7 +72,7 @@ static inline unsigned long do_csum(const unsigned char * buff, int len) } } if (len & 1) - result += (*buff << 8); + result += le16_to_cpu(*buff); result = from32to16(result); if (odd) result = ((result >> 8) & 0xff) | ((result & 0xff) << 8); diff --git a/arch/mips/mm/r2300.c b/arch/mips/mm/r2300.c index 276674c68ed1..bbf8fbe1a474 100644 --- a/arch/mips/mm/r2300.c +++ b/arch/mips/mm/r2300.c @@ -1,7 +1,9 @@ -/* $Id: r2300.c,v 1.1 1997/06/06 09:35:14 ralf Exp $ +/* * r2300.c: R2000 and R3000 specific mmu/cache code. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * + * $Id: r2300.c,v 1.2 1997/06/30 15:52:51 ralf Exp $ */ #include @@ -126,7 +128,7 @@ static inline void r2300_flush_tlb_all(void) unsigned long flags; int entry; - save_flags(flags); cli(); + save_and_cli(flags); write_32bit_cp0_register(CP0_ENTRYLO0, 0); for(entry = 0; entry < mips_tlb_entries; entry++) { write_32bit_cp0_register(CP0_INDEX, entry); @@ -160,7 +162,7 @@ static void r2300_load_pgd(unsigned long pg_dir) { unsigned long flags; - save_flags(flags); cli(); + save_and_cli(flags); write_32bit_cp0_register(CP0_ENTRYHI, TLB_ROOT); write_32bit_cp0_register(CP0_INDEX, 0); write_32bit_cp0_register(CP0_ENTRYLO0, ((pg_dir >> 6) | 0x00e0)); diff --git a/arch/mips/mm/r4xx0.c b/arch/mips/mm/r4xx0.c index 974343069ada..c4eb3cd82d79 100644 --- a/arch/mips/mm/r4xx0.c +++ b/arch/mips/mm/r4xx0.c @@ -2,6 +2,8 @@ * r4xx0.c: R4000 processor variant specific MMU/Cache routines. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * + * $Id: r4xx0.c,v 1.4 1997/06/30 15:52:53 ralf Exp $ */ #include @@ -101,6 +103,69 @@ static void r4k_clear_page_d32(unsigned long page) } +/* + * This flavour of r4k_clear_page is for the R4600 V1.x. Cite from the + * IDT R4600 V1.7 errata: + * + * 18. The CACHE instructions Hit_Writeback_Invalidate_D, Hit_Writeback_D, + * Hit_Invalidate_D and Create_Dirty_Exclusive_D should only be + * executed if there is no other dcache activity. If the dcache is + * accessed for another instruction immeidately preceding when these + * cache instructions are executing, it is possible that the dcache + * tag match outputs used by these cache instructions will be + * incorrect. These cache instructions should be preceded by at least + * four instructions that are not any kind of load or store + * instruction. + * + * This is not allowed: lw + * nop + * nop + * nop + * cache Hit_Writeback_Invalidate_D + * + * This is allowed: lw + * nop + * nop + * nop + * nop + * cache Hit_Writeback_Invalidate_D + */ +static void r4k_clear_page_r4600_v1(unsigned long page) +{ + __asm__ __volatile__( + ".set\tnoreorder\n\t" + ".set\tnoat\n\t" + ".set\tmips3\n\t" + "daddiu\t$1,%0,%2\n" + "1:\tnop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "cache\t%3,(%0)\n\t" + "sd\t$0,(%0)\n\t" + "sd\t$0,8(%0)\n\t" + "sd\t$0,16(%0)\n\t" + "sd\t$0,24(%0)\n\t" + "daddiu\t%0,64\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "cache\t%3,-32(%0)\n\t" + "sd\t$0,-32(%0)\n\t" + "sd\t$0,-24(%0)\n\t" + "sd\t$0,-16(%0)\n\t" + "bne\t$1,%0,1b\n\t" + "sd\t$0,-8(%0)\n\t" + ".set\tmips0\n\t" + ".set\tat\n\t" + ".set\treorder" + :"=r" (page) + :"0" (page), + "I" (PAGE_SIZE), + "i" (Create_Dirty_Excl_D) + :"$1","memory"); +} + /* * This is still inefficient. We only can do better if we know the * virtual address where the copy will be accessed. @@ -222,6 +287,74 @@ static void r4k_copy_page_d32(unsigned long to, unsigned long from) "i" (Create_Dirty_Excl_D)); } +/* + * Again a special version for the R4600 V1.x + */ +static void r4k_copy_page_r4600_v1(unsigned long to, unsigned long from) +{ + unsigned long dummy1, dummy2; + unsigned long reg1, reg2, reg3, reg4; + + __asm__ __volatile__( + ".set\tnoreorder\n\t" + ".set\tnoat\n\t" + ".set\tmips3\n\t" + "daddiu\t$1,%0,%8\n" + "1:\tnop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "\tcache\t%9,(%0)\n\t" + "lw\t%2,(%1)\n\t" + "lw\t%3,4(%1)\n\t" + "lw\t%4,8(%1)\n\t" + "lw\t%5,12(%1)\n\t" + "sw\t%2,(%0)\n\t" + "sw\t%3,4(%0)\n\t" + "sw\t%4,8(%0)\n\t" + "sw\t%5,12(%0)\n\t" + "lw\t%2,16(%1)\n\t" + "lw\t%3,20(%1)\n\t" + "lw\t%4,24(%1)\n\t" + "lw\t%5,28(%1)\n\t" + "sw\t%2,16(%0)\n\t" + "sw\t%3,20(%0)\n\t" + "sw\t%4,24(%0)\n\t" + "sw\t%5,28(%0)\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "cache\t%9,32(%0)\n\t" + "daddiu\t%0,64\n\t" + "daddiu\t%1,64\n\t" + "lw\t%2,-32(%1)\n\t" + "lw\t%3,-28(%1)\n\t" + "lw\t%4,-24(%1)\n\t" + "lw\t%5,-20(%1)\n\t" + "sw\t%2,-32(%0)\n\t" + "sw\t%3,-28(%0)\n\t" + "sw\t%4,-24(%0)\n\t" + "sw\t%5,-20(%0)\n\t" + "lw\t%2,-16(%1)\n\t" + "lw\t%3,-12(%1)\n\t" + "lw\t%4,-8(%1)\n\t" + "lw\t%5,-4(%1)\n\t" + "sw\t%2,-16(%0)\n\t" + "sw\t%3,-12(%0)\n\t" + "sw\t%4,-8(%0)\n\t" + "bne\t$1,%0,1b\n\t" + "sw\t%5,-4(%0)\n\t" + ".set\tmips0\n\t" + ".set\tat\n\t" + ".set\treorder" + :"=r" (dummy1), "=r" (dummy2), + "=&r" (reg1), "=&r" (reg2), "=&r" (reg3), "=&r" (reg4) + :"0" (to), "1" (from), + "I" (PAGE_SIZE), + "i" (Create_Dirty_Excl_D)); +} + /* * If you think for one second that this stuff coming up is a lot * of bulky code eating too many kernel cache lines. Think _again_. @@ -239,7 +372,7 @@ static inline void r4k_flush_cache_all_s16d16i16(void) { unsigned long flags; - save_flags(flags); cli(); + save_and_cli(flags); blast_dcache16(); blast_icache16(); blast_scache16(); restore_flags(flags); } @@ -248,7 +381,7 @@ static inline void r4k_flush_cache_all_s32d16i16(void) { unsigned long flags; - save_flags(flags); cli(); + save_and_cli(flags); blast_dcache16(); blast_icache16(); blast_scache32(); restore_flags(flags); } @@ -257,7 +390,7 @@ static inline void r4k_flush_cache_all_s64d16i16(void) { unsigned long flags; - save_flags(flags); cli(); + save_and_cli(flags); blast_dcache16(); blast_icache16(); blast_scache64(); restore_flags(flags); } @@ -266,7 +399,7 @@ static inline void r4k_flush_cache_all_s128d16i16(void) { unsigned long flags; - save_flags(flags); cli(); + save_and_cli(flags); blast_dcache16(); blast_icache16(); blast_scache128(); restore_flags(flags); } @@ -275,7 +408,7 @@ static inline void r4k_flush_cache_all_s16d32i32(void) { unsigned long flags; - save_flags(flags); cli(); + save_and_cli(flags); blast_dcache32(); blast_icache32(); blast_scache16(); restore_flags(flags); } @@ -284,7 +417,7 @@ static inline void r4k_flush_cache_all_s32d32i32(void) { unsigned long flags; - save_flags(flags); cli(); + save_and_cli(flags); blast_dcache32(); blast_icache32(); blast_scache32(); restore_flags(flags); } @@ -293,7 +426,7 @@ static inline void r4k_flush_cache_all_s64d32i32(void) { unsigned long flags; - save_flags(flags); cli(); + save_and_cli(flags); blast_dcache32(); blast_icache32(); blast_scache64(); restore_flags(flags); } @@ -302,7 +435,7 @@ static inline void r4k_flush_cache_all_s128d32i32(void) { unsigned long flags; - save_flags(flags); cli(); + save_and_cli(flags); blast_dcache32(); blast_icache32(); blast_scache128(); restore_flags(flags); } @@ -311,7 +444,7 @@ static inline void r4k_flush_cache_all_d16i16(void) { unsigned long flags; - save_flags(flags); cli(); + save_and_cli(flags); blast_dcache16(); blast_icache16(); restore_flags(flags); } @@ -320,7 +453,7 @@ static inline void r4k_flush_cache_all_d32i32(void) { unsigned long flags; - save_flags(flags); cli(); + save_and_cli(flags); blast_dcache32(); blast_icache32(); restore_flags(flags); } @@ -350,7 +483,7 @@ r4k_flush_cache_range_s16d16i16(struct mm_struct *mm, pte_t *pte; int text; - save_flags(flags); cli(); + save_and_cli(flags); text = vma->vm_flags & VM_EXEC; while(start < end) { pgd = pgd_offset(mm, start); @@ -395,7 +528,7 @@ r4k_flush_cache_range_s32d16i16(struct mm_struct *mm, pte_t *pte; int text; - save_flags(flags); cli(); + save_and_cli(flags); text = vma->vm_flags & VM_EXEC; while(start < end) { pgd = pgd_offset(mm, start); @@ -439,7 +572,7 @@ static void r4k_flush_cache_range_s64d16i16(struct mm_struct *mm, pte_t *pte; int text; - save_flags(flags); cli(); + save_and_cli(flags); text = vma->vm_flags & VM_EXEC; while(start < end) { pgd = pgd_offset(mm, start); @@ -483,7 +616,7 @@ static void r4k_flush_cache_range_s128d16i16(struct mm_struct *mm, pte_t *pte; int text; - save_flags(flags); cli(); + save_and_cli(flags); text = vma->vm_flags & VM_EXEC; while(start < end) { pgd = pgd_offset(mm, start); @@ -527,7 +660,7 @@ static void r4k_flush_cache_range_s16d32i32(struct mm_struct *mm, pte_t *pte; int text; - save_flags(flags); cli(); + save_and_cli(flags); text = vma->vm_flags & VM_EXEC; while(start < end) { pgd = pgd_offset(mm, start); @@ -571,7 +704,7 @@ static void r4k_flush_cache_range_s32d32i32(struct mm_struct *mm, pte_t *pte; int text; - save_flags(flags); cli(); + save_and_cli(flags); text = vma->vm_flags & VM_EXEC; while(start < end) { pgd = pgd_offset(mm, start); @@ -615,7 +748,7 @@ static void r4k_flush_cache_range_s64d32i32(struct mm_struct *mm, pte_t *pte; int text; - save_flags(flags); cli(); + save_and_cli(flags); text = vma->vm_flags & VM_EXEC; while(start < end) { pgd = pgd_offset(mm, start); @@ -659,7 +792,7 @@ static void r4k_flush_cache_range_s128d32i32(struct mm_struct *mm, pte_t *pte; int text; - save_flags(flags); cli(); + save_and_cli(flags); text = vma->vm_flags & VM_EXEC; while(start < end) { pgd = pgd_offset(mm, start); @@ -689,7 +822,7 @@ static void r4k_flush_cache_range_d16i16(struct mm_struct *mm, #ifdef DEBUG_CACHE printk("crange[%d,%08lx,%08lx]", (int)mm->context, start, end); #endif - save_flags(flags); cli(); + save_and_cli(flags); blast_dcache16(); blast_icache16(); restore_flags(flags); } @@ -705,7 +838,7 @@ static void r4k_flush_cache_range_d32i32(struct mm_struct *mm, #ifdef DEBUG_CACHE printk("crange[%d,%08lx,%08lx]", (int)mm->context, start, end); #endif - save_flags(flags); cli(); + save_and_cli(flags); blast_dcache32(); blast_icache32(); restore_flags(flags); } @@ -836,7 +969,7 @@ static void r4k_flush_cache_page_s16d16i16(struct vm_area_struct *vma, #ifdef DEBUG_CACHE printk("cpage[%d,%08lx]", (int)mm->context, page); #endif - save_flags(flags); cli(); + save_and_cli(flags); page &= PAGE_MASK; pgdp = pgd_offset(mm, page); pmdp = pmd_offset(pgdp, page); @@ -894,7 +1027,7 @@ static void r4k_flush_cache_page_s32d16i16(struct vm_area_struct *vma, #ifdef DEBUG_CACHE printk("cpage[%d,%08lx]", (int)mm->context, page); #endif - save_flags(flags); cli(); + save_and_cli(flags); page &= PAGE_MASK; pgdp = pgd_offset(mm, page); pmdp = pmd_offset(pgdp, page); @@ -951,7 +1084,7 @@ static void r4k_flush_cache_page_s64d16i16(struct vm_area_struct *vma, #ifdef DEBUG_CACHE printk("cpage[%d,%08lx]", (int)mm->context, page); #endif - save_flags(flags); cli(); + save_and_cli(flags); page &= PAGE_MASK; pgdp = pgd_offset(mm, page); pmdp = pmd_offset(pgdp, page); @@ -1009,7 +1142,7 @@ static void r4k_flush_cache_page_s128d16i16(struct vm_area_struct *vma, #ifdef DEBUG_CACHE printk("cpage[%d,%08lx]", (int)mm->context, page); #endif - save_flags(flags); cli(); + save_and_cli(flags); page &= PAGE_MASK; pgdp = pgd_offset(mm, page); pmdp = pmd_offset(pgdp, page); @@ -1068,7 +1201,7 @@ static void r4k_flush_cache_page_s16d32i32(struct vm_area_struct *vma, #ifdef DEBUG_CACHE printk("cpage[%d,%08lx]", (int)mm->context, page); #endif - save_flags(flags); cli(); + save_and_cli(flags); page &= PAGE_MASK; pgdp = pgd_offset(mm, page); pmdp = pmd_offset(pgdp, page); @@ -1126,7 +1259,7 @@ static void r4k_flush_cache_page_s32d32i32(struct vm_area_struct *vma, #ifdef DEBUG_CACHE printk("cpage[%d,%08lx]", (int)mm->context, page); #endif - save_flags(flags); cli(); + save_and_cli(flags); page &= PAGE_MASK; pgdp = pgd_offset(mm, page); pmdp = pmd_offset(pgdp, page); @@ -1186,7 +1319,7 @@ static void r4k_flush_cache_page_s64d32i32(struct vm_area_struct *vma, #ifdef DEBUG_CACHE printk("cpage[%d,%08lx]", (int)mm->context, page); #endif - save_flags(flags); cli(); + save_and_cli(flags); page &= PAGE_MASK; pgdp = pgd_offset(mm, page); pmdp = pmd_offset(pgdp, page); @@ -1246,7 +1379,7 @@ static void r4k_flush_cache_page_s128d32i32(struct vm_area_struct *vma, #ifdef DEBUG_CACHE printk("cpage[%d,%08lx]", (int)mm->context, page); #endif - save_flags(flags); cli(); + save_and_cli(flags); page &= PAGE_MASK; pgdp = pgd_offset(mm, page); pmdp = pmd_offset(pgdp, page); @@ -1303,7 +1436,7 @@ static void r4k_flush_cache_page_d16i16(struct vm_area_struct *vma, #ifdef DEBUG_CACHE printk("cpage[%d,%08lx]", (int)mm->context, page); #endif - save_flags(flags); cli(); + save_and_cli(flags); page &= PAGE_MASK; pgdp = pgd_offset(mm, page); pmdp = pmd_offset(pgdp, page); @@ -1362,7 +1495,7 @@ static void r4k_flush_cache_page_d32i32(struct vm_area_struct *vma, #ifdef DEBUG_CACHE printk("cpage[%d,%08lx]", (int)mm->context, page); #endif - save_flags(flags); cli(); + save_and_cli(flags); page &= PAGE_MASK; pgdp = pgd_offset(mm, page); pmdp = pmd_offset(pgdp, page); @@ -1420,7 +1553,7 @@ static void r4k_flush_cache_page_d32i32_r4600(struct vm_area_struct *vma, #ifdef DEBUG_CACHE printk("cpage[%d,%08lx]", (int)mm->context, page); #endif - save_flags(flags); cli(); + save_and_cli(flags); page &= PAGE_MASK; pgdp = pgd_offset(mm, page); pmdp = pmd_offset(pgdp, page); @@ -1478,7 +1611,7 @@ static void r4k_flush_page_to_ram_s16d16i16(unsigned long page) #ifdef DEBUG_CACHE printk("cram[%08lx]", page); #endif - save_flags(flags); cli(); + save_and_cli(flags); blast_dcache16_page(page); blast_scache16_page(page); restore_flags(flags); @@ -1494,7 +1627,7 @@ static void r4k_flush_page_to_ram_s32d16i16(unsigned long page) #ifdef DEBUG_CACHE printk("cram[%08lx]", page); #endif - save_flags(flags); cli(); + save_and_cli(flags); blast_dcache16_page(page); blast_scache32_page(page); restore_flags(flags); @@ -1510,7 +1643,7 @@ static void r4k_flush_page_to_ram_s64d16i16(unsigned long page) #ifdef DEBUG_CACHE printk("cram[%08lx]", page); #endif - save_flags(flags); cli(); + save_and_cli(flags); blast_dcache16_page(page); blast_scache64_page(page); restore_flags(flags); @@ -1526,7 +1659,7 @@ static void r4k_flush_page_to_ram_s128d16i16(unsigned long page) #ifdef DEBUG_CACHE printk("cram[%08lx]", page); #endif - save_flags(flags); cli(); + save_and_cli(flags); blast_dcache16_page(page); blast_scache128_page(page); restore_flags(flags); @@ -1542,7 +1675,7 @@ static void r4k_flush_page_to_ram_s16d32i32(unsigned long page) #ifdef DEBUG_CACHE printk("cram[%08lx]", page); #endif - save_flags(flags); cli(); + save_and_cli(flags); blast_dcache32_page(page); blast_scache16_page(page); restore_flags(flags); @@ -1558,7 +1691,7 @@ static void r4k_flush_page_to_ram_s32d32i32(unsigned long page) #ifdef DEBUG_CACHE printk("cram[%08lx]", page); #endif - save_flags(flags); cli(); + save_and_cli(flags); blast_dcache32_page(page); blast_scache32_page(page); restore_flags(flags); @@ -1574,7 +1707,7 @@ static void r4k_flush_page_to_ram_s64d32i32(unsigned long page) #ifdef DEBUG_CACHE printk("cram[%08lx]", page); #endif - save_flags(flags); cli(); + save_and_cli(flags); blast_dcache32_page(page); blast_scache64_page(page); restore_flags(flags); @@ -1590,7 +1723,7 @@ static void r4k_flush_page_to_ram_s128d32i32(unsigned long page) #ifdef DEBUG_CACHE printk("cram[%08lx]", page); #endif - save_flags(flags); cli(); + save_and_cli(flags); blast_dcache32_page(page); blast_scache128_page(page); restore_flags(flags); @@ -1606,7 +1739,7 @@ static void r4k_flush_page_to_ram_d16i16(unsigned long page) #ifdef DEBUG_CACHE printk("cram[%08lx]", page); #endif - save_flags(flags); cli(); + save_and_cli(flags); blast_dcache16_page(page); restore_flags(flags); } @@ -1621,7 +1754,7 @@ static void r4k_flush_page_to_ram_d32i32(unsigned long page) #ifdef DEBUG_CACHE printk("cram[%08lx]", page); #endif - save_flags(flags); cli(); + save_and_cli(flags); blast_dcache32_page(page); restore_flags(flags); } @@ -1651,7 +1784,7 @@ static void r4k_flush_page_to_ram_d32i32_r4600(unsigned long page) */ *(volatile unsigned long *)KSEG1; - save_flags(flags); cli(); + save_and_cli(flags); blast_dcache32_page(page); blast_dcache32_page(page ^ 0x2000); #ifdef CONFIG_SGI @@ -1678,21 +1811,21 @@ static void r4k_flush_page_to_ram_d32i32_r4600(unsigned long page) daddu %0, 32 mtc0 $0, $12 nop; nop; nop; nop; - mtc0 %3, $12 - nop; nop; nop; nop; .set mips0 .set reorder" : "=&r" (tmp1), "=&r" (tmp2), - "=&r" (page), "=&r" (flags) - : "2" (page & 0x0007f000), "3" (flags)); + "=&r" (page) + : "2" (page & 0x0007f000)); } #endif /* CONFIG_SGI */ + restore_flags(flags); } } static void r4k_flush_cache_sigtramp(unsigned long addr) { addr &= ~(dc_lsize - 1); + __asm__ __volatile__("nop;nop;nop;nop"); flush_dcache_line(addr); flush_dcache_line(addr + dc_lsize); flush_icache_line(addr); @@ -1715,7 +1848,7 @@ static inline void r4k_flush_tlb_all(void) printk("[tlball]"); #endif - save_flags(flags); cli(); + save_and_cli(flags); /* Save old context and create impossible VPN2 value */ old_ctx = (get_entryhi() & 0xff); set_entryhi(KSEG0); @@ -1746,7 +1879,7 @@ static void r4k_flush_tlb_mm(struct mm_struct *mm) #ifdef DEBUG_TLB printk("[tlbmm<%d>]", mm->context); #endif - save_flags(flags); cli(); + save_and_cli(flags); get_new_mmu_context(mm, asid_cache); if(mm == current->mm) set_entryhi(mm->context & 0xff); @@ -1765,7 +1898,7 @@ static void r4k_flush_tlb_range(struct mm_struct *mm, unsigned long start, printk("[tlbrange<%02x,%08lx,%08lx>]", (mm->context & 0xff), start, end); #endif - save_flags(flags); cli(); + save_and_cli(flags); size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; size = (size + 1) >> 1; if(size <= NTLB_ENTRIES_HALF) { @@ -1814,7 +1947,7 @@ static void r4k_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) #endif newpid = (vma->vm_mm->context & 0xff); page &= (PAGE_MASK << 1); - save_flags(flags); cli(); + save_and_cli(flags); oldpid = (get_entryhi() & 0xff); set_entryhi(page | newpid); BARRIER; @@ -1886,7 +2019,7 @@ static void r4k_update_mmu_cache(struct vm_area_struct * vma, } #endif - save_flags(flags); cli(); + save_and_cli(flags); address &= (PAGE_MASK << 1); set_entryhi(address | (pid)); pgdp = pgd_offset(vma->vm_mm, address); @@ -1945,7 +2078,7 @@ static void r4k_update_mmu_cache_hwbug(struct vm_area_struct * vma, pte_t *ptep; int idx; - save_flags(flags); cli(); + save_and_cli(flags); address &= (PAGE_MASK << 1); set_entryhi(address | (get_entryhi() & 0xff)); pgdp = pgd_offset(vma->vm_mm, address); @@ -2112,16 +2245,16 @@ static int probe_scache_eeprom(unsigned long config) __asm__ __volatile__(" .set noreorder .set mips3 - li %0, 0x1 - dsll %0, 31 - lui %1, 0x9000 - dsll32 %1, 0 - or %0, %1, %0 mfc0 %2, $12 nop; nop; nop; nop; li %1, 0x80 mtc0 %1, $12 nop; nop; nop; nop; + li %0, 0x1 + dsll %0, 31 + lui %1, 0x9000 + dsll32 %1, 0 + or %0, %1, %0 sb $0, 0(%0) mtc0 $0, $12 nop; nop; nop; nop; @@ -2189,7 +2322,7 @@ static int probe_scache(unsigned long config) /* This is such a bitch, you'd think they would make it * easy to do this. Away you daemons of stupidity! */ - save_flags(flags); cli(); + save_and_cli(flags); /* Fill each size-multiple cache line with a valid tag. */ pow2 = (64 * 1024); @@ -2254,8 +2387,13 @@ static void setup_noscache_funcs(void) flush_page_to_ram = r4k_flush_page_to_ram_d16i16; break; case 32: - clear_page = r4k_clear_page_d32; - copy_page = r4k_copy_page_d32; + if ((read_32bit_cp0_register(CP0_PRID) & 0xfff0) == 0x2010) { + clear_page = r4k_clear_page_r4600_v1; + copy_page = r4k_copy_page_r4600_v1; + } else { + clear_page = r4k_clear_page_d32; + copy_page = r4k_copy_page_d32; + } flush_cache_all = r4k_flush_cache_all_d32i32; flush_cache_mm = r4k_flush_cache_mm_d32i32; flush_cache_range = r4k_flush_cache_range_d32i32; diff --git a/arch/mips/sgi/kernel/indy_int.c b/arch/mips/sgi/kernel/indy_int.c index 9f73cc58d3f2..92b1cf0e44b4 100644 --- a/arch/mips/sgi/kernel/indy_int.c +++ b/arch/mips/sgi/kernel/indy_int.c @@ -1,8 +1,10 @@ -/* $Id: indy_int.c,v 1.1 1997/06/06 09:36:21 ralf Exp $ +/* * indy_int.c: Routines for generic manipulation of the INT[23] ASIC * found on INDY workstations.. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * + * $Id: indy_int.c,v 1.2 1997/06/30 15:53:01 ralf Exp $ */ #include @@ -67,8 +69,7 @@ void disable_local_irq(unsigned int irq_nr) { unsigned long flags; - save_flags(flags); - cli(); + save_and_cli(flags); switch(irq_nr) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: ioc_icontrol->imask0 &= ~(1 << irq_nr); @@ -95,8 +96,7 @@ void disable_local_irq(unsigned int irq_nr) void enable_local_irq(unsigned int irq_nr) { unsigned long flags; - save_flags(flags); - cli(); + save_and_cli(flags); switch(irq_nr) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: ioc_icontrol->imask0 |= (1 << irq_nr); @@ -408,8 +408,7 @@ void free_irq(unsigned int irq, void *dev_id) continue; /* Found it - now free it */ - save_flags(flags); - cli(); + save_and_cli(flags); *p = action->next; restore_flags(flags); kfree(action); diff --git a/arch/mips/sgi/kernel/indy_timer.c b/arch/mips/sgi/kernel/indy_timer.c index ce8fc60a96f7..24a0d0148baa 100644 --- a/arch/mips/sgi/kernel/indy_timer.c +++ b/arch/mips/sgi/kernel/indy_timer.c @@ -1,7 +1,9 @@ -/* $Id: indy_timer.c,v 1.1 1997/06/06 09:36:28 ralf Exp $ +/* * indy_timer.c: Setting up the clock on the INDY 8254 controller. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * + * $Id: indy_timer.c,v 1.2 1997/06/30 15:53:04 ralf Exp $ */ #include @@ -280,7 +282,7 @@ void do_gettimeofday(struct timeval *tv) { unsigned long flags; - save_flags(flags); cli(); + save_and_cli(flags); *tv = xtime; restore_flags(flags); } diff --git a/arch/mips/sgi/kernel/setup.c b/arch/mips/sgi/kernel/setup.c index 64fa44c6379d..cec9f3600954 100644 --- a/arch/mips/sgi/kernel/setup.c +++ b/arch/mips/sgi/kernel/setup.c @@ -1,4 +1,4 @@ -/* $Id: setup.c,v 1.1 1997/06/06 09:36:33 ralf Exp $ +/* $Id: setup.c,v 1.2 1997/06/30 15:26:24 ralf Exp $ * setup.c: SGI specific setup, including init of the feature struct. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) @@ -59,8 +59,6 @@ void sgi_setup(void) /* register_console(sgi_print); */ - sgi_sysinit(); - /* Init the INDY HPC I/O controller. Need to call this before * fucking with the memory controller because it needs to know the * boardID and whether this is a Guiness or a FullHouse machine. diff --git a/arch/mips/sgi/kernel/system.c b/arch/mips/sgi/kernel/system.c index b4d963882d26..c1f8538929e6 100644 --- a/arch/mips/sgi/kernel/system.c +++ b/arch/mips/sgi/kernel/system.c @@ -1,7 +1,9 @@ -/* $Id: system.c,v 1.1 1997/06/06 09:36:36 ralf Exp $ +/* * system.c: Probe the system type using ARCS prom interface library. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * + * $Id: system.c,v 1.2 1997/06/30 15:26:32 ralf Exp $ */ #include #include @@ -88,6 +90,10 @@ static int string_to_cpu(char *s) return 0; } +/* + * We' call this early before loadmmu(). If we do the other way around + * the firmware will crash and burn. + */ void sgi_sysinit(void) { pcomponent *p, *toplev, *cpup = 0; diff --git a/arch/mips/tools/offset.c b/arch/mips/tools/offset.c index a8660dcf6c6f..3ea39e133be5 100644 --- a/arch/mips/tools/offset.c +++ b/arch/mips/tools/offset.c @@ -124,4 +124,24 @@ void output_mm_defines(void) linefeed; } +void output_sc_defines(void) +{ + text("/* Linux sigcontext offsets. */"); + offset("#define SC_REGMASK ", struct sigcontext, sc_regmask); + offset("#define SC_STATUS ", struct sigcontext, sc_status); + offset("#define SC_PC ", struct sigcontext, sc_pc); + offset("#define SC_REGS ", struct sigcontext, sc_regs); + offset("#define SC_FPREGS ", struct sigcontext, sc_fpregs); + offset("#define SC_OWNEDFP ", struct sigcontext, sc_ownedfp); + offset("#define SC_FPC_CSR ", struct sigcontext, sc_fpc_csr); + offset("#define SC_FPC_EIR ", struct sigcontext, sc_fpc_eir); + offset("#define SC_SSFLAGS ", struct sigcontext, sc_ssflags); + offset("#define SC_MDHI ", struct sigcontext, sc_mdhi); + offset("#define SC_MDLO ", struct sigcontext, sc_mdlo); + offset("#define SC_CAUSE ", struct sigcontext, sc_cause); + offset("#define SC_BADVADDR ", struct sigcontext, sc_badvaddr); + offset("#define SC_SIGSET ", struct sigcontext, sc_sigset); + linefeed; +} + text("#endif /* !(_MIPS_OFFSET_H) */"); diff --git a/arch/sparc/kernel/cpu.c b/arch/sparc/kernel/cpu.c index f94ef8b91b7c..aeb5a46c816a 100644 --- a/arch/sparc/kernel/cpu.c +++ b/arch/sparc/kernel/cpu.c @@ -76,6 +76,7 @@ struct cpu_iu_info linux_sparc_chips[] = { { 0, 0, "Fujitsu MB86900/1A or LSI L64831 SparcKIT-40"}, /* borned STP1012PGA */ { 0, 4, "Fujitsu MB86904"}, + { 0, 5, "Fujitsu TurboSparc MB86907"}, /* SparcStation2, SparcServer 490 & 690 */ { 1, 0, "LSI Logic Corporation - L64811"}, /* SparcStation2 */ diff --git a/arch/sparc/kernel/sparc_ksyms.c b/arch/sparc/kernel/sparc_ksyms.c index 38896ab22846..cf4146c4aa81 100644 --- a/arch/sparc/kernel/sparc_ksyms.c +++ b/arch/sparc/kernel/sparc_ksyms.c @@ -1,4 +1,4 @@ -/* $Id: sparc_ksyms.c,v 1.59 1997/05/08 17:45:20 davem Exp $ +/* $Id: sparc_ksyms.c,v 1.60 1997/06/17 13:25:13 jj Exp $ * arch/sparc/kernel/ksyms.c: Sparc specific ksyms support. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -59,6 +59,7 @@ extern void *__memscan_zero(void *, size_t); extern void *__memscan_generic(void *, int, size_t); extern int __memcmp(const void *, const void *, __kernel_size_t); extern int __strncmp(const char *, const char *, __kernel_size_t); +extern char saved_command_line[]; extern void bcopy (const char *, char *, int); extern int __ashrdi3(int, int); @@ -200,7 +201,7 @@ EXPORT_SYMBOL(prom_getproplen); EXPORT_SYMBOL(prom_getproperty); EXPORT_SYMBOL(prom_node_has_property); EXPORT_SYMBOL(prom_setprop); -EXPORT_SYMBOL(prom_getbootargs); +EXPORT_SYMBOL(saved_command_line); EXPORT_SYMBOL(prom_apply_obio_ranges); EXPORT_SYMBOL(prom_getname); EXPORT_SYMBOL(prom_feval); diff --git a/arch/sparc/mm/Makefile b/arch/sparc/mm/Makefile index f7b9b367ce71..84cd1d2e2f67 100644 --- a/arch/sparc/mm/Makefile +++ b/arch/sparc/mm/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.25 1997/05/03 05:09:11 davem Exp $ +# $Id: Makefile,v 1.26 1997/06/24 15:48:06 jj Exp $ # Makefile for the linux Sparc-specific parts of the memory manager. # # Note! Dependencies are done automagically by 'make dep', which also @@ -9,7 +9,8 @@ O_TARGET := mm.o O_OBJS := fault.o init.o sun4c.o srmmu.o hypersparc.o viking.o \ - tsunami.o loadmmu.o generic.o asyncd.o extable.o + tsunami.o loadmmu.o generic.o asyncd.o extable.o \ + turbosparc.o include $(TOPDIR)/Rules.make @@ -18,6 +19,9 @@ ifdef SMP hypersparc.o: hypersparc.S $(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c -o hypersparc.o hypersparc.S +turbosparc.o: turbosparc.S + $(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c -o turbosparc.o turbosparc.S + viking.o: viking.S $(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c -o viking.o viking.S @@ -29,6 +33,9 @@ else hypersparc.o: hypersparc.S $(CC) -D__ASSEMBLY__ -ansi -c -o hypersparc.o hypersparc.S +turbosparc.o: turbosparc.S + $(CC) -D__ASSEMBLY__ -ansi -c -o turbosparc.o turbosparc.S + viking.o: viking.S $(CC) -D__ASSEMBLY__ -ansi -c -o viking.o viking.S diff --git a/arch/sparc/mm/srmmu.c b/arch/sparc/mm/srmmu.c index 7420b98cb2ca..3b997055116a 100644 --- a/arch/sparc/mm/srmmu.c +++ b/arch/sparc/mm/srmmu.c @@ -1,9 +1,10 @@ -/* $Id: srmmu.c,v 1.147 1997/05/20 07:58:42 jj Exp $ +/* $Id: srmmu.c,v 1.148 1997/06/24 15:48:02 jj Exp $ * srmmu.c: SRMMU specific routines for memory management. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1995 Peter A. Zaitcev (zaitcev@ithil.mcst.ru) * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) + * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ #include @@ -36,6 +37,7 @@ #include #include #include +#include enum mbus_module srmmu_modtype; unsigned int hwbug_bitmask; @@ -2595,6 +2597,78 @@ __initfunc(static void init_swift(void)) poke_srmmu = poke_swift; } +/* turbosparc.S */ +extern void turbosparc_flush_cache_all(); +extern void turbosparc_flush_sig_insns(struct mm_struct *mm, unsigned long insn_addr); + +static void poke_turbosparc(void) +{ + unsigned long mreg = srmmu_get_mmureg(); + unsigned long ccreg; + + /* Clear any crap from the cache or else... */ + turbosparc_flush_cache_all(); + mreg &= ~(TURBOSPARC_ICENABLE | TURBOSPARC_DCENABLE); /* Temporarily disable I & D caches */ + mreg &= ~(TURBOSPARC_PCENABLE); /* Don't check parity */ + srmmu_set_mmureg(mreg); + + ccreg = turbosparc_get_ccreg(); + +#ifdef TURBOSPARC_WRITEBACK + ccreg |= (TURBOSPARC_SNENABLE); /* Do DVMA snooping in Dcache */ + ccreg &= ~(TURBOSPARC_uS2 | TURBOSPARC_WTENABLE); + /* Write-back D-cache, emulate VLSI + * abortion number three, not number one */ +#else + /* For now let's play safe, optimize later */ + ccreg |= (TURBOSPARC_SNENABLE | TURBOSPARC_WTENABLE); + /* Do DVMA snooping in Dcache, Write-thru D-cache */ + ccreg &= ~(TURBOSPARC_uS2); + /* Emulate VLSI abortion number three, not number one */ +#endif + switch (ccreg & 7) { + case 0: /* No SE cache */ + case 7: /* Test mode */ + break; + default: + ccreg |= (TURBOSPARC_SCENABLE); + } + turbosparc_set_ccreg (ccreg); + + mreg |= (TURBOSPARC_ICENABLE | TURBOSPARC_DCENABLE); /* I & D caches on */ + mreg |= (TURBOSPARC_ICSNOOP); /* Icache snooping on */ + srmmu_set_mmureg(mreg); +} + +__initfunc(static void init_turbosparc(void)) +{ + srmmu_name = "Fujitsu TurboSparc"; + srmmu_modtype = TurboSparc; + + flush_cache_all = turbosparc_flush_cache_all; + flush_cache_mm = hypersparc_flush_cache_mm; + flush_cache_page = hypersparc_flush_cache_page; + flush_cache_range = hypersparc_flush_cache_range; + + flush_tlb_all = hypersparc_flush_tlb_all; + flush_tlb_mm = hypersparc_flush_tlb_mm; + flush_tlb_page = hypersparc_flush_tlb_page; + flush_tlb_range = hypersparc_flush_tlb_range; + +#ifdef TURBOSPARC_WRITEBACK + flush_page_to_ram = hypersparc_flush_page_to_ram; + flush_chunk = hypersparc_flush_chunk; +#else + flush_page_to_ram = swift_flush_page_to_ram; + flush_chunk = swift_flush_chunk; +#endif + + flush_sig_insns = turbosparc_flush_sig_insns; + flush_page_for_dma = hypersparc_flush_page_for_dma; + + poke_srmmu = poke_turbosparc; +} + static void poke_tsunami(void) { unsigned long mreg = srmmu_get_mmureg(); @@ -2785,9 +2859,33 @@ __initfunc(static void get_srmmu_type(void)) }; return; } + + /* Now Fujitsu TurboSparc. It might happen that it is + in Swift emulation mode, so we will check later... */ + if (psr_typ == 0 && psr_vers == 5) { + init_turbosparc(); + return; + } /* Next check for Fujitsu Swift. */ if(psr_typ == 0 && psr_vers == 4) { + int cpunode; + char node_str[128]; + + /* Look if it is not a TurboSparc emulating Swift... */ + cpunode = prom_getchild(prom_root_node); + while((cpunode = prom_getsibling(cpunode)) != 0) { + prom_getstring(cpunode, "device_type", node_str, sizeof(node_str)); + if(!strcmp(node_str, "cpu")) { + if (!prom_getintdefault(cpunode, "psr-implementation", 1) && + prom_getintdefault(cpunode, "psr-version", 1) == 5) { + init_turbosparc(); + return; + } + break; + } + } + init_swift(); return; } diff --git a/arch/sparc/mm/turbosparc.S b/arch/sparc/mm/turbosparc.S new file mode 100644 index 000000000000..fa5e5a981d78 --- /dev/null +++ b/arch/sparc/mm/turbosparc.S @@ -0,0 +1,46 @@ +/* $Id: turbosparc.S,v 1.1 1997/06/24 15:48:05 jj Exp $ + * turbosparc.S: High speed Hypersparc mmu/cache operations. + * + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + */ + +#include +#include +#include +#include +#include + +#define WINDOW_FLUSH(tmp1, tmp2) \ + mov 0, tmp1; \ +98: ld [%g6 + AOFF_task_tss + AOFF_thread_uwinmask], tmp2; \ + orcc %g0, tmp2, %g0; \ + add tmp1, 1, tmp1; \ + bne 98b; \ + save %sp, -64, %sp; \ +99: subcc tmp1, 1, tmp1; \ + bne 99b; \ + restore %g0, %g0, %g0; + + .text + .align 4 + + .globl turbosparc_flush_cache_all + .globl turbosparc_flush_sig_insns + +turbosparc_flush_cache_all: + WINDOW_FLUSH(%g4, %g5) + sethi %hi(vac_cache_size), %g4 + ld [%g4 + %lo(vac_cache_size)], %g5 + sethi %hi(vac_line_size), %g1 + ld [%g1 + %lo(vac_line_size)], %g2 +1: + subcc %g5, %g2, %g5 + bne 1b + sta %g0, [%g5] ASI_M_DATAC_TAG + retl + sta %g0, [%g0] ASI_M_IC_FLCLEAR + +turbosparc_flush_sig_insns: + retl + nop diff --git a/arch/sparc/prom/bootstr.c b/arch/sparc/prom/bootstr.c index 1722d9fdcea0..e7bd9b06d852 100644 --- a/arch/sparc/prom/bootstr.c +++ b/arch/sparc/prom/bootstr.c @@ -1,4 +1,4 @@ -/* $Id: bootstr.c,v 1.12 1996/12/18 06:46:54 tridge Exp $ +/* $Id: bootstr.c,v 1.14 1997/06/19 16:28:49 jj Exp $ * bootstr.c: Boot string/argument acquisition from the PROM. * * Copyright(C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -7,13 +7,14 @@ #include #include #include +#include #define BARG_LEN 256 -static char barg_buf[BARG_LEN]; -static char fetched = 0; +static char barg_buf[BARG_LEN] __initdata = { 0 }; +static char fetched __initdata = 0; -char * -prom_getbootargs(void) +__initfunc(char * +prom_getbootargs(void)) { int iter; char *cp, *arg; diff --git a/arch/sparc/prom/tree.c b/arch/sparc/prom/tree.c index 251b2ee6a0c8..295ce5355525 100644 --- a/arch/sparc/prom/tree.c +++ b/arch/sparc/prom/tree.c @@ -1,4 +1,4 @@ -/* $Id: tree.c,v 1.18 1997/05/14 20:45:03 davem Exp $ +/* $Id: tree.c,v 1.19 1997/06/27 14:52:54 jj Exp $ * tree.c: Basic device tree traversal/scanning for the Linux * prom library. * @@ -231,7 +231,7 @@ int prom_getname (int node, char *buffer, int len) /* Return the first property type for node 'node'. */ -char * prom_firstprop(int node) +char * prom_firstprop(int node, char *buffer) { unsigned long flags; char *ret; @@ -248,7 +248,7 @@ char * prom_firstprop(int node) * at node 'node' . Returns NULL string if no more * property types for this node. */ -char * prom_nextprop(int node, char *oprop) +char * prom_nextprop(int node, char *oprop, char *buffer) { char *ret; unsigned long flags; @@ -293,7 +293,7 @@ int prom_node_has_property(int node, char *prop) char *current_property = ""; do { - current_property = prom_nextprop(node, current_property); + current_property = prom_nextprop(node, current_property, NULL); if(!strcmp(current_property, prop)) return 1; } while (*current_property); diff --git a/arch/sparc64/Makefile b/arch/sparc64/Makefile index a70f9ebf8164..3704091eb5f1 100644 --- a/arch/sparc64/Makefile +++ b/arch/sparc64/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.16 1997/05/04 07:21:08 davem Exp $ +# $Id: Makefile,v 1.19 1997/06/26 12:48:45 jj Exp $ # sparc64/Makefile # # Makefile for the architecture dependent flags and dependencies on the @@ -24,8 +24,8 @@ ELF2AOUT64 = elf2aout64 # debugging of the kernel to get the proper debugging information. #CFLAGS := $(CFLAGS) -g -pipe -fcall-used-g5 -fcall-used-g7 -CFLAGS := $(CFLAGS) -pipe \ - -fcall-used-g5 -fcall-used-g7 -Wno-sign-compare +CFLAGS := $(CFLAGS) -pipe -mno-fpu -mtune=ultrasparc -mmedlow \ + -ffixed-g4 -fcall-used-g5 -fcall-used-g7 -Wno-sign-compare LINKFLAGS = -T arch/sparc64/vmlinux.lds diff --git a/arch/sparc64/config.in b/arch/sparc64/config.in index 00f687c92666..fcbac5c1aff0 100644 --- a/arch/sparc64/config.in +++ b/arch/sparc64/config.in @@ -1,4 +1,4 @@ -# $Id: config.in,v 1.7 1997/06/17 03:54:52 davem Exp $ +# $Id: config.in,v 1.9 1997/07/04 11:33:05 davem Exp $ # For a description of the syntax of this configuration file, # see the Configure script. # @@ -51,10 +51,10 @@ bool 'Networking support' CONFIG_NET bool 'System V IPC' CONFIG_SYSVIPC bool 'Sysctl support' CONFIG_SYSCTL bool 'Kernel support for Linux/Sparc 32bit binary compatibility' CONFIG_SPARC32_COMPAT -tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT tristate 'Kernel support for 64-bit ELF binaries' CONFIG_BINFMT_ELF if [ "$CONFIG_SPARC32_COMPAT" != "n" ]; then tristate 'Kernel support for 32-bit ELF binaries' CONFIG_BINFMT_ELF32 + bool 'Kernel support for 32-bit (ie. SunOS) a.out binaries' CONFIG_BINFMT_AOUT32 fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate 'Kernel support for JAVA binaries' CONFIG_BINFMT_JAVA @@ -157,4 +157,5 @@ bool 'Kernel profiling support' CONFIG_PROFILE if [ "$CONFIG_PROFILE" = "y" ]; then int ' Profile shift count' CONFIG_PROFILE_SHIFT 2 fi +bool 'ECache flush trap support at ta 0x72' CONFIG_EC_FLUSH_TRAP endmenu diff --git a/arch/sparc64/defconfig b/arch/sparc64/defconfig index 6b08fd41bbf7..949ea1d0211d 100644 --- a/arch/sparc64/defconfig +++ b/arch/sparc64/defconfig @@ -5,12 +5,14 @@ # # Code maturity level options # -# CONFIG_EXPERIMENTAL is not set +CONFIG_EXPERIMENTAL=y # # Loadable module support # -# CONFIG_MODULES is not set +CONFIG_MODULES=y +# CONFIG_MODVERSIONS is not set +# CONFIG_KERNELD is not set # # General setup @@ -45,25 +47,31 @@ SUN_FB_CREATOR=y # # Misc Linux/SPARC drivers # -# CONFIG_SUN_OPENPROMIO is not set +CONFIG_SUN_OPENPROMIO=m CONFIG_SUN_MOSTEK_RTC=y -# CONFIG_SUN_OPENPROMFS is not set +# CONFIG_SUN_BPP is not set +# CONFIG_SUN_VIDEOPIX is not set +CONFIG_SUN_OPENPROMFS=m CONFIG_NET=y CONFIG_SYSVIPC=y CONFIG_SYSCTL=y CONFIG_SPARC32_COMPAT=y -CONFIG_BINFMT_AOUT=y CONFIG_BINFMT_ELF=y CONFIG_BINFMT_ELF32=y +CONFIG_BINFMT_AOUT32=y +CONFIG_BINFMT_JAVA=m +CONFIG_BINFMT_MISC=m # # Floppy, IDE, and other block devices # # CONFIG_BLK_DEV_FD is not set -# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_MD=y +CONFIG_MD_LINEAR=m +CONFIG_MD_STRIPED=m CONFIG_BLK_DEV_RAM=y -# CONFIG_BLK_DEV_INITRD is not set -# CONFIG_BLK_DEV_LOOP is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_BLK_DEV_LOOP=m # # Networking options @@ -86,15 +94,22 @@ CONFIG_INET=y CONFIG_PATH_MTU_DISCOVERY=y CONFIG_IP_NOSR=y CONFIG_SKB_LARGE=y +# CONFIG_IPV6 is not set # # # -CONFIG_IPX=y +CONFIG_IPX=m # CONFIG_IPX_INTERN is not set -CONFIG_ATALK=y +# CONFIG_IPX_PPROP_ROUTING is not set +CONFIG_ATALK=m # CONFIG_IPDDP is not set # CONFIG_AX25 is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_BRIDGE is not set +# CONFIG_LLC is not set +# CONFIG_WAN_ROUTER is not set # # SCSI support @@ -108,7 +123,7 @@ CONFIG_BLK_DEV_SD=y # CONFIG_CHR_DEV_ST is not set CONFIG_BLK_DEV_SR=y CONFIG_BLK_DEV_SR_VENDOR=y -# CONFIG_CHR_DEV_SG is not set +CONFIG_CHR_DEV_SG=m # # Some SCSI devices (e.g. CD jukebox) support multiple LUNs @@ -120,7 +135,7 @@ CONFIG_SCSI_CONSTANTS=y # SCSI low-level drivers # CONFIG_SCSI_SUNESP=y -# CONFIG_SCSI_QLOGICPTI is not set +CONFIG_SCSI_QLOGICPTI=m # # Network device support @@ -130,8 +145,8 @@ CONFIG_DUMMY=y # CONFIG_PPP is not set # CONFIG_SLIP is not set CONFIG_SUNLANCE=y -# CONFIG_HAPPYMEAL is not set -# CONFIG_SUNQE is not set +CONFIG_HAPPYMEAL=m +CONFIG_SUNQE=m # CONFIG_MYRI_SBUS is not set # @@ -152,7 +167,7 @@ CONFIG_NFS_FS=y CONFIG_ROOT_NFS=y CONFIG_RNFS_BOOTP=y # CONFIG_RNFS_RARP is not set -# CONFIG_NFSD is not set +CONFIG_NFSD=m CONFIG_SUNRPC=y CONFIG_LOCKD=y # CONFIG_SMB_FS is not set @@ -162,7 +177,7 @@ CONFIG_ISO9660_FS=y # CONFIG_SYSV_FS is not set # CONFIG_AFFS_FS is not set # CONFIG_ROMFS_FS is not set -# CONFIG_AUTOFS_FS is not set +CONFIG_AUTOFS_FS=m CONFIG_UFS_FS=y CONFIG_BSD_DISKLABEL=y CONFIG_SMD_DISKLABEL=y @@ -171,3 +186,4 @@ CONFIG_SMD_DISKLABEL=y # Kernel hacking # # CONFIG_PROFILE is not set +# CONFIG_EC_FLUSH_TRAP is not set diff --git a/arch/sparc64/kernel/Makefile b/arch/sparc64/kernel/Makefile index c7bb1064afbb..9e9013735c6a 100644 --- a/arch/sparc64/kernel/Makefile +++ b/arch/sparc64/kernel/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.23 1997/06/06 10:56:20 jj Exp $ +# $Id: Makefile,v 1.28 1997/07/05 09:52:20 davem Exp $ # Makefile for the linux kernel. # # Note! Dependencies are done automagically by 'make dep', which also @@ -16,21 +16,26 @@ all: kernel.o head.o init_task.o O_TARGET := kernel.o -O_OBJS := etrap.o rtrap.o hack.o process.o setup.o cpu.o idprom.o \ - systbls.o traps.o entry.o devices.o auxio.o ioport.o \ - irq.o ptrace.o time.o sys_sparc.o signal.o winfixup.o \ - unaligned.o +O_OBJS := process.o setup.o cpu.o idprom.o \ + systbls.o traps.o devices.o auxio.o ioport.o \ + irq.o ptrace.o time.o sys_sparc.o signal.o \ + unaligned.o sys_sunos32.o sunos_ioctl32.o OX_OBJS := sparc64_ksyms.o ifdef CONFIG_SPARC32_COMPAT - O_OBJS += sys_sparc32.o signal32.o ioctl32.o + O_OBJS += sys32.o sys_sparc32.o signal32.o ioctl32.o endif ifdef CONFIG_BINFMT_ELF32 O_OBJS += binfmt_elf32.o endif -head.o: head.S ttable.S itlb_miss.S dtlb_miss.S dtlb_prot.S +ifdef CONFIG_BINFMT_AOUT32 + O_OBJS += binfmt_aout32.o +endif + +head.o: head.S ttable.S itlb_miss.S dtlb_miss.S dtlb_prot.S etrap.S rtrap.S \ + winfixup.S entry.S $(CC) -D__ASSEMBLY__ -ansi -c $*.S -o $*.o # diff --git a/arch/sparc64/kernel/binfmt_aout32.c b/arch/sparc64/kernel/binfmt_aout32.c new file mode 100644 index 000000000000..ecd8d55778c5 --- /dev/null +++ b/arch/sparc64/kernel/binfmt_aout32.c @@ -0,0 +1,477 @@ +/* + * linux/fs/binfmt_aout.c + * + * Copyright (C) 1991, 1992, 1996 Linus Torvalds + * + * Hacked a bit by DaveM to make it work with 32-bit SunOS + * binaries on the sparc64 port. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static int load_aout32_binary(struct linux_binprm *, struct pt_regs * regs); +static int load_aout32_library(int fd); +static int aout32_core_dump(long signr, struct pt_regs * regs); + +extern void dump_thread(struct pt_regs *, struct user *); + +static struct linux_binfmt aout32_format = { + NULL, NULL, load_aout32_binary, load_aout32_library, aout32_core_dump +}; + +static void set_brk(unsigned long start, unsigned long end) +{ + start = PAGE_ALIGN(start); + end = PAGE_ALIGN(end); + if (end <= start) + return; + do_mmap(NULL, start, end - start, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_FIXED | MAP_PRIVATE, 0); +} + +/* + * These are the only things you should do on a core-file: use only these + * macros to write out all the necessary info. + */ +#define DUMP_WRITE(addr,nr) \ +while (file.f_op->write(inode,&file,(char *)(addr),(nr)) != (nr)) goto close_coredump + +#define DUMP_SEEK(offset) \ +if (file.f_op->llseek) { \ + if (file.f_op->llseek(inode,&file,(offset),0) != (offset)) \ + goto close_coredump; \ +} else file.f_pos = (offset) + +/* + * Routine writes a core dump image in the current directory. + * Currently only a stub-function. + * + * Note that setuid/setgid files won't make a core-dump if the uid/gid + * changed due to the set[u|g]id. It's enforced by the "current->dumpable" + * field, which also makes sure the core-dumps won't be recursive if the + * dumping of the process results in another error.. + */ + +static inline int +do_aout32_core_dump(long signr, struct pt_regs * regs) +{ + struct inode * inode = NULL; + struct file file; + unsigned short fs; + int has_dumped = 0; + char corefile[6+sizeof(current->comm)]; + unsigned long dump_start, dump_size; + struct user dump; +# define START_DATA(u) (u.u_tsize) +# define START_STACK(u) ((regs->u_regs[UREG_FP]) & ~(PAGE_SIZE - 1)) + + if (!current->dumpable || current->mm->count != 1) + return 0; + current->dumpable = 0; + +/* See if we have enough room to write the upage. */ + if (current->rlim[RLIMIT_CORE].rlim_cur < PAGE_SIZE) + return 0; + fs = get_fs(); + set_fs(KERNEL_DS); + memcpy(corefile,"core.",5); +#if 0 + memcpy(corefile+5,current->comm,sizeof(current->comm)); +#else + corefile[4] = '\0'; +#endif + if (open_namei(corefile,O_CREAT | 2 | O_TRUNC,0600,&inode,NULL)) { + inode = NULL; + goto end_coredump; + } + if (!S_ISREG(inode->i_mode)) + goto end_coredump; + if (!inode->i_op || !inode->i_op->default_file_ops) + goto end_coredump; + if (get_write_access(inode)) + goto end_coredump; + file.f_mode = 3; + file.f_flags = 0; + file.f_count = 1; + file.f_inode = inode; + file.f_pos = 0; + file.f_reada = 0; + file.f_op = inode->i_op->default_file_ops; + if (file.f_op->open) + if (file.f_op->open(inode,&file)) + goto done_coredump; + if (!file.f_op->write) + goto close_coredump; + has_dumped = 1; + current->flags |= PF_DUMPCORE; + strncpy(dump.u_comm, current->comm, sizeof(current->comm)); + dump.signal = signr; + dump_thread(regs, &dump); + +/* If the size of the dump file exceeds the rlimit, then see what would happen + if we wrote the stack, but not the data area. */ + if ((dump.u_dsize+dump.u_ssize) > + current->rlim[RLIMIT_CORE].rlim_cur) + dump.u_dsize = 0; + +/* Make sure we have enough room to write the stack and data areas. */ + if ((dump.u_ssize) > + current->rlim[RLIMIT_CORE].rlim_cur) + dump.u_ssize = 0; + +/* make sure we actually have a data and stack area to dump */ + set_fs(USER_DS); + if (verify_area(VERIFY_READ, (void *) START_DATA(dump), dump.u_dsize)) + dump.u_dsize = 0; + if (verify_area(VERIFY_READ, (void *) START_STACK(dump), dump.u_ssize)) + dump.u_ssize = 0; + + set_fs(KERNEL_DS); +/* struct user */ + DUMP_WRITE(&dump,sizeof(dump)); +/* now we start writing out the user space info */ + set_fs(USER_DS); +/* Dump the data area */ + if (dump.u_dsize != 0) { + dump_start = START_DATA(dump); + dump_size = dump.u_dsize; + DUMP_WRITE(dump_start,dump_size); + } +/* Now prepare to dump the stack area */ + if (dump.u_ssize != 0) { + dump_start = START_STACK(dump); + dump_size = dump.u_ssize; + DUMP_WRITE(dump_start,dump_size); + } +/* Finally dump the task struct. Not be used by gdb, but could be useful */ + set_fs(KERNEL_DS); + DUMP_WRITE(current,sizeof(*current)); + inode->i_status |= ST_MODIFIED; +close_coredump: + if (file.f_op->release) + file.f_op->release(inode,&file); +done_coredump: + put_write_access(inode); +end_coredump: + set_fs(fs); + iput(inode); + return has_dumped; +} + +static int +aout32_core_dump(long signr, struct pt_regs * regs) +{ + int retval; + + MOD_INC_USE_COUNT; + retval = do_aout32_core_dump(signr, regs); + MOD_DEC_USE_COUNT; + return retval; +} + +/* + * create_aout32_tables() parses the env- and arg-strings in new user + * memory and creates the pointer tables from them, and puts their + * addresses on the "stack", returning the new stack pointer value. + */ +#define A(x) ((unsigned long)x) +static u32 *create_aout32_tables(char * p, struct linux_binprm * bprm) +{ + u32 *argv, *envp; + u32 *sp; + int argc = bprm->argc; + int envc = bprm->envc; + + sp = (u32 *) ((-(unsigned long)sizeof(char *)) & (unsigned long) p); + + /* This imposes the proper stack alignment for a new process. */ + sp = (u32 *) (((unsigned long) sp) & ~7); + if ((envc+argc+3)&1) + --sp; + + sp -= envc+1; + envp = (u32 *) sp; + sp -= argc+1; + argv = (u32 *) sp; + put_user(argc,--sp); + current->mm->arg_start = (unsigned long) p; + while (argc-->0) { + char c; + put_user(((u32)A(p)),argv++); + do { + get_user(c,p++); + } while (c); + } + put_user(NULL,argv); + current->mm->arg_end = current->mm->env_start = (unsigned long) p; + while (envc-->0) { + char c; + put_user(((u32)A(p)),envp++); + do { + get_user(c,p++); + } while (c); + } + put_user(NULL,envp); + current->mm->env_end = (unsigned long) p; + return sp; +} + +/* + * These are the functions used to load a.out style executables and shared + * libraries. There is no binary dependent code anywhere else. + */ + +static inline int do_load_aout32_binary(struct linux_binprm * bprm, + struct pt_regs * regs) +{ + struct exec ex; + struct file * file; + int fd; + unsigned long error; + unsigned long p = bprm->p; + unsigned long fd_offset; + unsigned long rlim; + + ex = *((struct exec *) bprm->buf); /* exec-header */ + if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC && + N_MAGIC(ex) != QMAGIC && N_MAGIC(ex) != NMAGIC) || + N_TRSIZE(ex) || N_DRSIZE(ex) || + bprm->inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) { + return -ENOEXEC; + } + + current->personality = PER_LINUX; + fd_offset = N_TXTOFF(ex); + + /* Check initial limits. This avoids letting people circumvent + * size limits imposed on them by creating programs with large + * arrays in the data or bss. + */ + rlim = current->rlim[RLIMIT_DATA].rlim_cur; + if (rlim >= RLIM_INFINITY) + rlim = ~0; + if (ex.a_data + ex.a_bss > rlim) + return -ENOMEM; + + /* OK, This is the point of no return */ + flush_old_exec(bprm); + memcpy(¤t->tss.core_exec, &ex, sizeof(struct exec)); + + current->mm->end_code = ex.a_text + + (current->mm->start_code = N_TXTADDR(ex)); + current->mm->end_data = ex.a_data + + (current->mm->start_data = N_DATADDR(ex)); + current->mm->brk = ex.a_bss + + (current->mm->start_brk = N_BSSADDR(ex)); + + current->mm->rss = 0; + current->mm->mmap = NULL; + current->suid = current->euid = current->fsuid = bprm->e_uid; + current->sgid = current->egid = current->fsgid = bprm->e_gid; + current->flags &= ~PF_FORKNOEXEC; + if (N_MAGIC(ex) == NMAGIC) { + /* Fuck me plenty... */ + error = do_mmap(NULL, N_TXTADDR(ex), ex.a_text, + PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_FIXED|MAP_PRIVATE, 0); + read_exec(bprm->inode, fd_offset, (char *) N_TXTADDR(ex), + ex.a_text, 0); + error = do_mmap(NULL, N_DATADDR(ex), ex.a_data, + PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_FIXED|MAP_PRIVATE, 0); + read_exec(bprm->inode, fd_offset + ex.a_text, (char *) N_DATADDR(ex), + ex.a_data, 0); + goto beyond_if; + } + + if (N_MAGIC(ex) == OMAGIC) { + do_mmap(NULL, N_TXTADDR(ex) & PAGE_MASK, + ex.a_text+ex.a_data + PAGE_SIZE - 1, + PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_FIXED|MAP_PRIVATE, 0); + read_exec(bprm->inode, fd_offset, (char *) N_TXTADDR(ex), + ex.a_text+ex.a_data, 0); + } else { + if ((ex.a_text & 0xfff || ex.a_data & 0xfff) && + (N_MAGIC(ex) != NMAGIC)) + printk(KERN_NOTICE "executable not page aligned\n"); + + fd = open_inode(bprm->inode, O_RDONLY); + + if (fd < 0) + return fd; + file = current->files->fd[fd]; + if (!file->f_op || !file->f_op->mmap) { + sys_close(fd); + do_mmap(NULL, 0, ex.a_text+ex.a_data, + PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_FIXED|MAP_PRIVATE, 0); + read_exec(bprm->inode, fd_offset, + (char *) N_TXTADDR(ex), ex.a_text+ex.a_data, 0); + goto beyond_if; + } + + error = do_mmap(file, N_TXTADDR(ex), ex.a_text, + PROT_READ | PROT_EXEC, + MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE, + fd_offset); + + if (error != N_TXTADDR(ex)) { + sys_close(fd); + send_sig(SIGKILL, current, 0); + return error; + } + + error = do_mmap(file, N_DATADDR(ex), ex.a_data, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE, + fd_offset + ex.a_text); + sys_close(fd); + if (error != N_DATADDR(ex)) { + send_sig(SIGKILL, current, 0); + return error; + } + } +beyond_if: + if (current->exec_domain && current->exec_domain->module) + __MOD_DEC_USE_COUNT(current->exec_domain->module); + if (current->binfmt && current->binfmt->module) + __MOD_DEC_USE_COUNT(current->binfmt->module); + current->exec_domain = lookup_exec_domain(current->personality); + current->binfmt = &aout32_format; + if (current->exec_domain && current->exec_domain->module) + __MOD_INC_USE_COUNT(current->exec_domain->module); + if (current->binfmt && current->binfmt->module) + __MOD_INC_USE_COUNT(current->binfmt->module); + + set_brk(current->mm->start_brk, current->mm->brk); + + p = setup_arg_pages(p, bprm); + + p = (unsigned long) create_aout32_tables((char *)p, bprm); + current->mm->start_stack = p; + start_thread32(regs, ex.a_entry, p); + if (current->flags & PF_PTRACED) + send_sig(SIGTRAP, current, 0); + return 0; +} + + +static int +load_aout32_binary(struct linux_binprm * bprm, struct pt_regs * regs) +{ + int retval; + + MOD_INC_USE_COUNT; + retval = do_load_aout32_binary(bprm, regs); + MOD_DEC_USE_COUNT; + return retval; +} + +static inline int +do_load_aout32_library(int fd) +{ + struct file * file; + struct exec ex; + struct inode * inode; + unsigned int len; + unsigned int bss; + unsigned int start_addr; + unsigned long error; + + file = current->files->fd[fd]; + inode = file->f_inode; + + if (!file || !file->f_op) + return -EACCES; + + /* Seek into the file */ + if (file->f_op->llseek) { + if ((error = file->f_op->llseek(inode, file, 0, 0)) != 0) + return -ENOEXEC; + } else + file->f_pos = 0; + + set_fs(KERNEL_DS); + error = file->f_op->read(inode, file, (char *) &ex, sizeof(ex)); + set_fs(USER_DS); + if (error != sizeof(ex)) + return -ENOEXEC; + + /* We come in here for the regular a.out style of shared libraries */ + if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != QMAGIC) || N_TRSIZE(ex) || + N_DRSIZE(ex) || ((ex.a_entry & 0xfff) && N_MAGIC(ex) == ZMAGIC) || + inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) { + return -ENOEXEC; + } + if (N_MAGIC(ex) == ZMAGIC && N_TXTOFF(ex) && + (N_TXTOFF(ex) < inode->i_sb->s_blocksize)) { + printk("N_TXTOFF < BLOCK_SIZE. Please convert library\n"); + return -ENOEXEC; + } + + if (N_FLAGS(ex)) return -ENOEXEC; + + /* For QMAGIC, the starting address is 0x20 into the page. We mask + this off to get the starting address for the page */ + + start_addr = ex.a_entry & 0xfffff000; + + /* Now use mmap to map the library into memory. */ + error = do_mmap(file, start_addr, ex.a_text + ex.a_data, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE, + N_TXTOFF(ex)); + if (error != start_addr) + return error; + len = PAGE_ALIGN(ex.a_text + ex.a_data); + bss = ex.a_text + ex.a_data + ex.a_bss; + if (bss > len) { + error = do_mmap(NULL, start_addr + len, bss-len, + PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_PRIVATE|MAP_FIXED, 0); + if (error != start_addr + len) + return error; + } + return 0; +} + +static int +load_aout32_library(int fd) +{ + int retval; + + MOD_INC_USE_COUNT; + retval = do_load_aout32_library(fd); + MOD_DEC_USE_COUNT; + return retval; +} + + +__initfunc(int init_aout32_binfmt(void)) +{ + return register_binfmt(&aout32_format); +} diff --git a/arch/sparc64/kernel/cpu.c b/arch/sparc64/kernel/cpu.c index 695ad680e91f..96dc599e83f5 100644 --- a/arch/sparc64/kernel/cpu.c +++ b/arch/sparc64/kernel/cpu.c @@ -6,7 +6,9 @@ #include #include +#include #include +#include struct cpu_iu_info { short manuf; @@ -50,11 +52,20 @@ __initfunc(void cpu_probe(void)) int manuf, impl; unsigned i, cpuid; long ver, fpu_vers; - - cpuid = get_cpuid(); + long fprs; +#ifndef __SMP__ + cpuid = 0; +#else +#error SMP not supported on sparc64 yet + /* cpuid = get_cpuid(); */ +#endif + + fprs = fprs_read (); + fprs_write (FPRS_FEF); __asm__ __volatile__ ("rdpr %%ver, %0; stx %%fsr, [%1]" : "=r" (ver) : "r" (&fpu_vers)); - + fprs_write (fprs); + manuf = ((ver >> 48)&0xffff); impl = ((ver >> 32)&0xffff); diff --git a/arch/sparc64/kernel/devices.c b/arch/sparc64/kernel/devices.c index 6aadd14e014e..5e6705896048 100644 --- a/arch/sparc64/kernel/devices.c +++ b/arch/sparc64/kernel/devices.c @@ -6,7 +6,6 @@ #include #include -#include #include #include @@ -15,7 +14,7 @@ #include struct prom_cpuinfo linux_cpus[NCPUS]; -int linux_num_cpus; +int linux_num_cpus = 0; extern void cpu_probe(void); @@ -54,7 +53,7 @@ device_scan(unsigned long mem_start)) }; if(cpu_ctr == 0) { printk("No CPU nodes found, cannot continue.\n"); - halt(); + prom_halt(); } printk("Found %d CPU prom device tree node(s).\n", cpu_ctr); }; diff --git a/arch/sparc64/kernel/dtlb_miss.S b/arch/sparc64/kernel/dtlb_miss.S index 31b87f3de8e6..b034ef407e5a 100644 --- a/arch/sparc64/kernel/dtlb_miss.S +++ b/arch/sparc64/kernel/dtlb_miss.S @@ -1,4 +1,4 @@ -/* $Id: dtlb_miss.S,v 1.11 1997/04/10 01:59:35 davem Exp $ +/* $Id: dtlb_miss.S,v 1.12 1997/06/26 12:47:08 jj Exp $ * dtlb_miss.S: Data TLB miss code, this is included directly * into the trap table. * @@ -19,9 +19,11 @@ * } * goto longer_processing; * } else { - * if(fault_address >= KERNBASE && - * fault_address < VMALLOC_START) { - * tlb_load(__pa(fault_address) | PAGE_KERNEL); + * if(fault_address >= PAGE_OFFSET) { + * pte_val = PAGE_KERNEL; + * if (fault_address & 0x10000000000) + * pte_val = PAGE_KERNEL_IO; + * tlb_load(__pa(fault_address) | pte_val); * return_from_trap(); * } else { * pgd = pgd_offset(swapper_pg_dir, fault_address); @@ -32,9 +34,9 @@ * This is optimized for user TLB misses on purpose. */ -#define KERN_HIGHBITS (_PAGE_VALID | _PAGE_SZ4MB) +#define KERN_HIGHBITS ((_PAGE_VALID | _PAGE_SZ4MB) ^ 0xfffff80000000000) #define KERN_LOWBITS (_PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_W) -#define KERN_LOWBITS_IO (_PAGE_E | _PAGE_P | _PAGE_W) +#define KERN_LOWBITS_IO ((_PAGE_E | _PAGE_P | _PAGE_W) ^ KERN_LOWBITS) /* ICACHE line 1 */ /*0x00*/ ldxa [%g0] ASI_DMMU, %g1 ! Get TAG_TARGET @@ -57,17 +59,17 @@ 1:/*0x3c*/ retry ! Trap return 3: /* ICACHE line 3 */ - /*0x40*/ sllx %g1, 43, %g5 ! This gets >= VMALLOC_START... - /*0x44*/ brlz,pn %g5, 4f ! ...if now less than zero. - /*0x48*/ andncc %g1, 0x3ff, %g0 ! Slick trick... - /*0x4c*/ be,pn %xcc, 4f ! Yes, it is some PROM mapping - /*0x50*/ srlx %g5, 21, %g5 ! This is now physical page - /*0x54*/ sethi %uhi(KERN_HIGHBITS), %g1 ! Construct PTE - /*0x58*/ sllx %g1, 32, %g1 ! Move priv bits up - /*0x5c*/ or %g1, %g5, %g1 ! Or in the page + /*0x40*/ sllx %g1, 22, %g5 ! This is now physical page + PAGE_OFFSET + /*0x44*/ brgez,pn %g5, 4f ! If >= 0, then walk down page tables + /*0x48*/ sethi %uhi(KERN_HIGHBITS), %g1 ! Construct PTE ^ PAGE_OFFSET + /*0x4c*/ andcc %g3, 0x80, %g0 ! Slick trick... + /*0x50*/ sllx %g1, 32, %g1 ! Move high bits up + /*0x54*/ or %g1, (KERN_LOWBITS), %g1 ! Assume not IO + /*0x58*/ bne,a,pn %icc, 5f ! Is it an IO page? + /*0x5c*/ xor %g1, (KERN_LOWBITS_IO), %g1 ! Aha, it is IO... /* ICACHE line 4 */ - /*0x60*/ or %g1, (KERN_LOWBITS), %g1 ! Set low priv bits +5:/*0x60*/ xor %g1, %g5, %g1 ! Slick trick II... /*0x64*/ stxa %g1, [%g0] ASI_DTLB_DATA_IN ! TLB load /*0x68*/ retry ! Trap return 4:/*0x6c*/ ldxa [%g0] ASI_DMMU_TSB_8KB_PTR, %g1 ! For PTE offset @@ -78,3 +80,4 @@ #undef KERN_HIGHBITS #undef KERN_LOWBITS +#undef KERN_LOWBITS_IO diff --git a/arch/sparc64/kernel/entry.S b/arch/sparc64/kernel/entry.S index 0d95e1b75b25..2f012e184984 100644 --- a/arch/sparc64/kernel/entry.S +++ b/arch/sparc64/kernel/entry.S @@ -1,13 +1,12 @@ -/* $Id: entry.S,v 1.31 1997/06/02 06:33:25 davem Exp $ +/* $Id: entry.S,v 1.45 1997/07/05 09:52:25 davem Exp $ * arch/sparc64/kernel/entry.S: Sparc64 trap low-level entry points. * - * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ -#include #include #include @@ -25,7 +24,6 @@ #define NR_SYSCALLS 256 /* Each OS is different... */ .text - .align 4 .globl sparc64_dtlb_prot_catch, sparc64_dtlb_refbit_catch .globl sparc64_itlb_refbit_catch @@ -38,24 +36,27 @@ * to update the dirty bit) and since we left crap in the sfsr * it will not get updated properly. */ + .align 32 sparc64_dtlb_prot_catch: wr %g0, ASI_DMMU, %asi rdpr %pstate, %g1 wrpr %g1, PSTATE_AG|PSTATE_MG, %pstate rdpr %tl, %g3 ldxa [%g0 + TLB_TAG_ACCESS] %asi, %g5 - ldxa [%g0 + TLB_SFSR] %asi, %g4 - cmp %g3, 1 stxa %g0, [%g0 + TLB_SFSR] %asi + membar #Sync + cmp %g3, 1 + bgu,a,pn %icc, winfix_trampoline rdpr %tpc, %g3 ba,pt %xcc, etrap rd %pc, %g7 - b,a,pt %xcc, 1f - + b,pt %xcc, 1f + mov 1, %o2 sparc64_dtlb_refbit_catch: srlx %g5, 9, %g4 and %g4, ((_PAGE_PRESENT | _PAGE_READ) >> 9), %g4 + cmp %g4, ((_PAGE_PRESENT | _PAGE_READ) >> 9) be,a,pt %xcc, 2f mov 1, %g4 @@ -64,23 +65,24 @@ sparc64_dtlb_refbit_catch: wrpr %g1, PSTATE_AG|PSTATE_MG, %pstate rdpr %tl, %g3 ldxa [%g0 + TLB_TAG_ACCESS] %asi, %g5 + cmp %g3, 1 - clr %g4 ! sfsr not updated for tlb misses - bgu,a,pn %icc, winfix_trampoline + bgu,pn %icc, winfix_trampoline rdpr %tpc, %g3 - ba,pt %xcc, etrap + b,pt %xcc, etrap rd %pc, %g7 -1: - mov %l5, %o4 ! raw tag access - mov %l4, %o5 ! raw sfsr - srlx %l5, PAGE_SHIFT, %o3 - clr %o1 ! text_fault == 0 - sllx %o3, PAGE_SHIFT, %o3 ! address - and %l4, 0x4, %o2 ! write == sfsr.W + clr %o2 +1: srlx %l5, PAGE_SHIFT, %o1 + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + call do_sparc64_fault - add %sp, STACK_BIAS + REGWIN_SZ, %o0 ! pt_regs ptr - ba,pt %xcc, rtrap + sllx %o1, PAGE_SHIFT, %o1 + b,pt %xcc, rtrap clr %l6 + nop + nop + nop + nop sparc64_itlb_refbit_catch: srlx %g5, 9, %g4 @@ -90,35 +92,105 @@ sparc64_itlb_refbit_catch: mov 1, %g4 rdpr %pstate, %g1 wrpr %g1, PSTATE_AG|PSTATE_MG, %pstate - ba,pt %xcc, etrap - rd %pc, %g7 - - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC], %o3 - mov 1, %o1 ! text_fault == 1 - clr %o2 ! write == 0 - clr %o4 ! tag access (N/A) - clr %o5 ! raw sfsr (N/A) - call do_sparc64_fault - add %sp, STACK_BIAS + REGWIN_SZ, %o0 ! pt_regs ptr - ba,pt %xcc, rtrap - clr %l6 + rdpr %tpc, %g5 -2: - sllx %g4, 63, %g4 ! _PAGE_VALID + b,pt %xcc, etrap + rd %pc, %g7 + b,pt %xcc, 1b + clr %o2 +2: sllx %g4, 63, %g4 ! _PAGE_VALID or %g5, _PAGE_ACCESSED, %g5 or %g5, %g4, %g5 stxa %g5, [%g3 + %g1] ASI_PHYS_USE_EC ! store new PTE + stxa %g5, [%g0] ASI_DTLB_DATA_IN ! TLB load retry - -3: - sllx %g4, 63, %g4 ! _PAGE_VALID +3: sllx %g4, 63, %g4 ! _PAGE_VALID or %g5, _PAGE_ACCESSED, %g5 or %g5, %g4, %g5 stxa %g5, [%g3 + %g1] ASI_PHYS_USE_EC ! store new PTE stxa %g5, [%g0] ASI_ITLB_DATA_IN ! TLB load retry + /* This is trivial with the new code... */ + .align 32 + .globl do_fpdis +do_fpdis: + wr %g0, FPRS_FEF, %fprs + ldx [%g6 + AOFF_task_flags], %g2 + sethi %hi(0x00100000), %g4 ! XXX PF_USEDFPU + andcc %g2, %g4, %g0 + + bne,a,pt %xcc, fpload_fromkstk + sethi %hi((((PAGE_SIZE<<1)-((64*4)+(2*8))) & ~(64 - 1))), %g2 + fzero %f0 + fzero %f2 + faddd %f0, %f2, %f4 + fmuld %f0, %f2, %f6 + faddd %f0, %f2, %f8 + fmuld %f0, %f2, %f10 + + faddd %f0, %f2, %f12 + fmuld %f0, %f2, %f14 + faddd %f0, %f2, %f16 + fmuld %f0, %f2, %f18 + faddd %f0, %f2, %f20 + fmuld %f0, %f2, %f22 + faddd %f0, %f2, %f24 + fmuld %f0, %f2, %f26 + + faddd %f0, %f2, %f28 + fmuld %f0, %f2, %f30 + faddd %f0, %f2, %f32 + fmuld %f0, %f2, %f34 + faddd %f0, %f2, %f36 + fmuld %f0, %f2, %f38 + faddd %f0, %f2, %f40 + fmuld %f0, %f2, %f42 + + faddd %f0, %f2, %f44 + fmuld %f0, %f2, %f46 + ldx [%g6 + AOFF_task_flags], %g2 + faddd %f0, %f2, %f48 + fmuld %f0, %f2, %f50 + or %g2, %g4, %g2 + faddd %f0, %f2, %f52 + fmuld %f0, %f2, %f54 + + stx %g2, [%g6 + AOFF_task_flags] + faddd %f0, %f2, %f56 + sethi %hi(empty_zero_page), %g3 + fmuld %f0, %f2, %f58 + + faddd %f0, %f2, %f60 + ldx [%g3], %fsr ! wheee, empty_zero_page + b,pt %xcc, fpdis_exit + wr %g0, 0, %gsr + +fpload_fromkstk: + or %g2, %lo((((PAGE_SIZE<<1)-((64*4)+(2*8))) & ~(64 - 1))), %g2 + add %g6, %g2, %g2 + mov SECONDARY_CONTEXT, %g3 + stxa %g0, [%g3] ASI_DMMU + flush %g2 + wr %g0, ASI_BLK_S, %asi ! grrr, where is ASI_BLK_NUCLEUS 8-( + membar #StoreLoad | #LoadLoad + + ldda [%g2 + 0x000] %asi, %f0 + ldda [%g2 + 0x040] %asi, %f16 + ldda [%g2 + 0x080] %asi, %f32 + ldda [%g2 + 0x0c0] %asi, %f48 + ldx [%g2 + 0x100], %fsr + ldx [%g2 + 0x108], %g2 + membar #Sync + wr %g2, 0, %gsr +fpdis_exit: + rdpr %tstate, %g3 + sethi %hi(TSTATE_PEF), %g4 + or %g3, %g4, %g3 ! anal... + wrpr %g3, %tstate + retry + /* Note check out head.h, this code isn't even used for UP, * for SMP things will be different. In particular the data * registers for cross calls will be: @@ -130,7 +202,7 @@ sparc64_itlb_refbit_catch: * With this method we can do most of the cross-call tlb/cache * flushing in very quickly. */ - .align 4 + .align 32 .globl do_ivec do_ivec: ldxa [%g0] ASI_INTR_RECEIVE, %g1 @@ -139,16 +211,12 @@ do_ivec: mov 0x40, %g2 /* Load up Interrupt Vector Data 0 register. */ - sethi %uhi(ivector_to_mask), %g4 ldxa [%g2] ASI_UDB_INTR_R, %g3 - or %g4, %ulo(ivector_to_mask), %g4 - and %g3, 0x7ff, %g3 - sllx %g4, 32, %g4 sethi %hi(ivector_to_mask), %g5 + and %g3, 0x7ff, %g3 + orcc %g5, %lo(ivector_to_mask), %g5 sllx %g3, 3, %g3 - or %g5, %lo(ivector_to_mask), %g5 - add %g5, %g4, %g4 - ldx [%g4 + %g3], %g2 + ldx [%g5 + %g3], %g2 brz,pn %g2, do_ivec_spurious nop @@ -166,6 +234,7 @@ do_ivec_return: do_ivec_spurious: stxa %g0, [%g0] ASI_INTR_RECEIVE + membar #Sync rdpr %pstate, %g1 wrpr %g1, PSTATE_IG | PSTATE_AG, %pstate ba,pt %xcc, etrap @@ -175,7 +244,36 @@ do_ivec_spurious: ba,pt %xcc, rtrap clr %l6 - .globl do_mna + .globl getcc, setcc +getcc: + ldx [%o0 + PT_V9_TSTATE], %o1 + srlx %o1, 32, %o1 + and %o1, 0xf, %o1 + retl + stx %o1, [%o0 + PT_V9_G1] +setcc: + ldx [%o0 + PT_V9_TSTATE], %o1 + ldx [%o0 + PT_V9_G1], %o2 + or %g0, %ulo(TSTATE_ICC), %o3 + sllx %o3, 32, %o3 + andn %o1, %o3, %o1 + sllx %o2, 32, %o2 + and %o2, %o3, %o2 + or %o1, %o2, %o1 + retl + stx %o1, [%o0 + PT_V9_TSTATE] + + /* XXX Here is stuff we still need to write... -DaveM XXX */ + .globl floppy_hardint, indirect_syscall, netbsd_syscall + .globl solaris_syscall +floppy_hardint: +indirect_syscall: +netbsd_syscall: +solaris_syscall: + retl + nop + + .globl do_mna do_mna: rdpr %tl, %g3 cmp %g3, 1 @@ -195,187 +293,211 @@ breakpoint_trap: ba,pt %xcc, rtrap nop - .globl sys_pipe, sys_execve, sys_sigpause, sys_nis_syscall - .globl sys_sigsuspend, sys_sigreturn - .globl sys32_execve, sys_ptrace - -sys_pipe: - sethi %hi(sparc_pipe), %g1 - add %g1, %g4, %g1 - jmpl %g1 + %lo(sparc_pipe), %g0 - add %sp, STACK_BIAS + REGWIN_SZ, %o0 - -sys_nis_syscall: - sethi %hi(c_sys_nis_syscall), %g1 - add %g1, %g4, %g1 - jmpl %g1 + %lo(c_sys_nis_syscall), %g0 - add %sp, STACK_BIAS + REGWIN_SZ, %o0 - -sys_execve: - sethi %hi(sparc_execve), %g1 - add %g1, %g4, %g1 - jmpl %g1 + %lo(sparc_execve), %g0 - add %sp, STACK_BIAS + REGWIN_SZ, %o0 - -sys32_execve: - sethi %hi(sparc32_execve), %g1 - add %g1, %g4, %g1 - jmpl %g1 + %lo(sparc32_execve), %g0 - add %sp, STACK_BIAS + REGWIN_SZ, %o0 - -sys_sigpause: - /* NOTE: %o0 has a correct value already */ - call do_sigpause - add %sp, STACK_BIAS + REGWIN_SZ, %o1 - - ld [%curptr + AOFF_task_flags], %l5 - andcc %l5, 0x20, %g0 - be,pt %icc, rtrap - clr %l6 - call syscall_trace + /* SunOS uses syscall zero as the 'indirect syscall' it looks + * like indir_syscall(scall_num, arg0, arg1, arg2...); etc. + * This is complete brain damage. + */ + .globl sunos_indir +sunos_indir: + srl %o0, 0, %o0 + mov %o7, %l4 + cmp %o0, NR_SYSCALLS + blu,a,pt %icc, 1f + sll %o0, 0x3, %o0 + sethi %hi(sunos_nosys), %l6 + b,pt %xcc, 2f + or %l6, %lo(sunos_nosys), %l6 +1: sethi %hi(sunos_sys_table), %l7 + or %l7, %lo(sunos_sys_table), %l7 + ldx [%l7 + %o0], %l6 +2: mov %o1, %o0 + mov %o2, %o1 + mov %o3, %o2 + mov %o4, %o3 + mov %o5, %o4 + call %l6 + mov %l4, %o7 + + .globl sunos_getpid +sunos_getpid: + call sys_getppid nop - ba,pt %xcc, rtrap - clr %l6 - -sys_sigsuspend: - call do_sigsuspend - add %sp, STACK_BIAS + REGWIN_SZ, %o0 - - ld [%curptr + AOFF_task_flags], %l5 - andcc %l5, 0x20, %g0 - be,pt %icc, rtrap - clr %l6 - call syscall_trace + call sys_getpid + stx %o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I1] + b,pt %xcc, ret_sys_call + stx %o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0] + + /* SunOS getuid() returns uid in %o0 and euid in %o1 */ + .globl sunos_getuid +sunos_getuid: + call sys_geteuid nop - ba,pt %xcc, rtrap - clr %l6 - -sys_sigreturn: - call do_sigreturn - add %sp, STACK_BIAS + REGWIN_SZ, %o0 - - ld [%curptr + AOFF_task_flags], %l5 - andcc %l5, 0x20, %g0 - be,pt %icc, rtrap - clr %l6 - call syscall_trace + call sys_getuid + stx %o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I1] + b,pt %xcc, ret_sys_call + stx %o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0] + + /* SunOS getgid() returns gid in %o0 and egid in %o1 */ + .globl sunos_getgid +sunos_getgid: + call sys_getegid nop - ba,pt %xcc, rtrap - clr %l6 + call sys_getgid + stx %o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I1] + b,pt %xcc, ret_sys_call + stx %o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0] -sys_ptrace: - call do_ptrace - add %sp, STACK_BIAS + REGWIN_SZ, %o0 + /* SunOS's execv() call only specifies the argv argument, the + * environment settings are the same as the calling processes. + */ + .globl sunos_execv +sunos_execv: + sethi %hi(sparc32_execve), %g1 + stx %g0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I2] + jmpl %g1 + %lo(sparc32_execve), %g0 + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + + .globl sys_pipe, sys_execve, sys_sigpause, sys_nis_syscall + .globl sys_sigsuspend, sys_sigreturn + .globl sys32_execve, sys_ptrace + .align 32 +sys_pipe: sethi %hi(sparc_pipe), %g1 + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + jmpl %g1 + %lo(sparc_pipe), %g0 + nop +sys_nis_syscall:sethi %hi(c_sys_nis_syscall), %g1 + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + jmpl %g1 + %lo(c_sys_nis_syscall), %g0 + nop + +sys_execve: sethi %hi(sparc_execve), %g1 + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + jmpl %g1 + %lo(sparc_execve), %g0 + nop +sys32_execve: sethi %hi(sparc32_execve), %g1 + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + jmpl %g1 + %lo(sparc32_execve), %g0 + nop + + /* NOTE: %o0 has a correct value already */ +sys_sigpause: call do_sigpause + add %sp, STACK_BIAS + REGWIN_SZ, %o1 + ldx [%curptr + AOFF_task_flags], %l5 + andcc %l5, 0x20, %g0 + be,pt %icc, rtrap + clr %l6 + call syscall_trace + nop + + ba,pt %xcc, rtrap + clr %l6 +linux_sparc_ni_syscall: + sethi %hi(sys_ni_syscall), %l7 + b,pt %xcc,syscall_is_too_hard + or %l7, %lo(sys_ni_syscall), %l7 + nop + + .align 32 +sys_sigsuspend: call do_sigsuspend + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + ldx [%curptr + AOFF_task_flags], %l5 + andcc %l5, 0x20, %g0 + be,pt %icc, rtrap + clr %l6 + call syscall_trace + nop + + ba,pt %xcc, rtrap + clr %l6 - ld [%curptr + AOFF_task_flags], %l5 - andcc %l5, 0x20, %g0 - be,pt %icc, rtrap - clr %l6 - call syscall_trace - nop - ba,pt %xcc, rtrap - clr %l6 + .align 32 +sys_sigreturn: call do_sigreturn + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + ldx [%curptr + AOFF_task_flags], %l5 + andcc %l5, 0x20, %g0 + be,pt %icc, rtrap + clr %l6 + call syscall_trace + nop + + ba,pt %xcc, rtrap + clr %l6 + + .align 32 +sys_ptrace: call do_ptrace + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + ldx [%curptr + AOFF_task_flags], %l5 + andcc %l5, 0x20, %g0 + be,pt %icc, rtrap + clr %l6 + call syscall_trace + nop + + ba,pt %xcc, rtrap + clr %l6 /* This is how fork() was meant to be done, 12 instruction entry. -DaveM */ - .globl sys_fork, sys_vfork, sys_clone + .globl sys_fork, sys_vfork, sys_clone, ret_from_syscall + .align 32 sys_fork: -sys_vfork: - mov SIGCHLD, %o0 - clr %o1 -sys_clone: - mov %o7, %l5 - save %sp, -REGWIN_SZ, %sp - flushw - restore %g0, %g0, %g0 - rdpr %cwp, %o4 - add %sp, STACK_BIAS + REGWIN_SZ, %o2 - movrz %o1, %fp, %o1 - - /* Don't try this at home. */ - stx %o4, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G0] - call do_fork - mov %l5, %o7 - -linux_sparc_ni_syscall: - sethi %hi(sys_ni_syscall), %l7 - or %l7, %lo(sys_ni_syscall), %l7 - ba,pt %xcc,syscall_is_too_hard - add %l7, %g4, %l7 - -linux_fast_syscall: - andn %l7, 3, %l7 - mov %i0, %o0 - mov %i1, %o1 - mov %i2, %o2 - jmpl %l7 + %g0, %g0 - mov %i3, %o3 +sys_vfork: mov SIGCHLD, %o0 + clr %o1 +sys_clone: mov %o7, %l5 + save %sp, -REGWIN_SZ, %sp + flushw + restore %g0, %g0, %g0 + rdpr %cwp, %o4 + add %sp, STACK_BIAS + REGWIN_SZ, %o2 + + movrz %o1, %fp, %o1 + stx %o4, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G0] + call do_fork + mov %l5, %o7 +ret_from_syscall:b,pt %xcc, ret_sys_call + ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0], %o0 + nop + nop linux_syscall_trace: - call syscall_trace - nop - mov %i0, %o0 - mov %i1, %o1 - mov %i2, %o2 - mov %i3, %o3 - ba,pt %xcc, 2f - mov %i4, %o4 - - .globl ret_from_syscall -ret_from_syscall: - ba,pt %xcc, ret_sys_call - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0], %o0 + call syscall_trace + nop + mov %i0, %o0 + mov %i1, %o1 + mov %i2, %o2 + mov %i3, %o3 + b,pt %xcc, 2f + mov %i4, %o4 /* Linux native and SunOS system calls enter here... */ - .align 4 - .globl linux_sparc_syscall + .align 32 + .globl linux_sparc_syscall, syscall_is_too_hard, ret_sys_call linux_sparc_syscall: /* Direct access to user regs, must faster. */ - cmp %g1, NR_SYSCALLS - add %l7, %g4, %l7 - bgeu,pn %xcc, linux_sparc_ni_syscall - sll %g1, 3, %l4 - ldx [%l7 + %l4], %l7 - andcc %l7, 1, %g0 - bne,pn %icc, linux_fast_syscall - /* Just do the next insn in the delay slot */ - - .globl syscall_is_too_hard + cmp %g1, NR_SYSCALLS ! IEU1 Group + bgeu,pn %xcc, linux_sparc_ni_syscall ! CTI + mov %i0, %o0 ! IEU0 + sll %g1, 3, %l4 ! IEU0 Group + mov %i1, %o1 ! IEU1 + ldx [%l7 + %l4], %l7 ! Load syscall_is_too_hard: -#ifdef SYSCALL_TRACING /* Debugging... */ - mov %g1, %o0 ! o0=scall, o1=ptregs - call syscall_trace_entry - add %sp, STACK_BIAS + REGWIN_SZ, %o1 -#endif - mov %i0, %o0 - mov %i1, %o1 - mov %i2, %o2 - - ldx [%curptr + AOFF_task_flags], %l5 - mov %i3, %o3 - mov %i4, %o4 - andcc %l5, 0x20, %g0 - bne,pn %icc, linux_syscall_trace - mov %i0, %l5 -2: - call %l7 - mov %i5, %o5 - -#ifdef SYSCALL_TRACING /* Debugging... */ - call syscall_trace_exit ! o0=sysret, o1=ptregs - add %sp, STACK_BIAS + REGWIN_SZ, %o1 -#endif + mov %i2, %o2 ! IEU0 Group + ldx [%curptr + AOFF_task_flags], %l5 ! Load + + st %g0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_FPRS] + mov %i3, %o3 ! IEU1 + mov %i4, %o4 ! IEU0 Group + andcc %l5, 0x20, %g0 ! IEU1 2 bubbles + bne,pn %icc, linux_syscall_trace ! CTI Group + mov %i0, %l5 ! IEU0 +2: call %l7 ! CTI Group brk forced + mov %i5, %o5 ! IEU0 stx %o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0] - .globl ret_sys_call ret_sys_call: ldx [%curptr + AOFF_task_flags], %l6 - ldx [%curptr + AOFF_task_tss + AOFF_thread_flags], %l2 + sra %o0, 0, %o0 mov %ulo(TSTATE_XCARRY | TSTATE_ICARRY), %g2 - and %l2, SPARC_FLAG_32BIT, %l2 ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TSTATE], %g3 - brnz,a,pn %l2, 1f - sra %o0, 0, %o0 -1: cmp %o0, -ENOIOCTLCMD sllx %g2, 32, %g2 bgeu,pn %xcc, 1f @@ -383,13 +505,12 @@ ret_sys_call: /* System call success, clear Carry condition code. */ andn %g3, %g2, %g3 - clr %l6 stx %g3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TSTATE] bne,pn %icc, linux_syscall_trace2 ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC], %l1 ! pc = npc add %l1, 0x4, %l2 !npc = npc+4 stx %l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC] - ba,pt %xcc, rtrap + b,pt %xcc, rtrap_clr_l6 stx %l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC] 1: /* System call failure, set Carry condition code. @@ -403,10 +524,10 @@ ret_sys_call: bne,pn %icc, linux_syscall_trace2 ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC], %l1 ! pc = npc add %l1, 0x4, %l2 !npc = npc+4 + stx %l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC] - ba,pt %xcc, rtrap + b,pt %xcc, rtrap stx %l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC] - linux_syscall_trace2: call syscall_trace add %l1, 0x4, %l2 /* npc = npc+4 */ @@ -414,4 +535,15 @@ linux_syscall_trace2: ba,pt %xcc, rtrap stx %l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC] -/* End of entry.S */ + .align 32 + .globl __flushw_user +__flushw_user: +1: save %sp, -128, %sp + rdpr %otherwin, %g1 + brnz,pt %g1, 1b + add %g2, 1, %g2 +1: sub %g2, 1, %g2 + brnz,pt %g2, 1b + restore %g0, %g0, %g0 +2: retl + mov %g3, %o7 diff --git a/arch/sparc64/kernel/etrap.S b/arch/sparc64/kernel/etrap.S index e3751c326757..4daf30e21bab 100644 --- a/arch/sparc64/kernel/etrap.S +++ b/arch/sparc64/kernel/etrap.S @@ -1,4 +1,4 @@ -/* $Id: etrap.S,v 1.22 1997/06/13 14:02:40 davem Exp $ +/* $Id: etrap.S,v 1.30 1997/06/30 10:31:37 jj Exp $ * etrap.S: Preparing for entry into the kernel on Sparc V9. * * Copyright (C) 1996, 1997 David S. Miller (davem@caip.rutgers.edu) @@ -12,91 +12,121 @@ #include #include - /* We assume that pstate, when entering this, has AG and - * IE bits set, MG and IG clear. - * - * We also guarentee for caller that AG %g4 and %g5 will have - * their values preserved and left in %l4 and %l5 respectively - * for him (fault handling needs this). - */ +#define FPUREG_SZ ((64 * 4) + (2 * 8)) +#define TASK_REGOFF ((((PAGE_SIZE<<1)-FPUREG_SZ)&~(64-1)) - \ + TRACEREG_SZ-REGWIN_SZ) - .text - .align 32 - .globl etrap, etrap_irq, etraptl1 -etrap: - rdpr %pil, %g2 -etrap_irq: - rdpr %tstate, %g1 - sllx %g2, 20, %g2 - or %g1, %g2, %g1 - andcc %g1, TSTATE_PRIV, %g0 - bne,a,pn %xcc, 1f - sub %sp, REGWIN_SZ + TRACEREG_SZ - STACK_BIAS, %g2 - rd %pic, %g3 + .text + .align 32 + .globl etrap, etrap_irq, etraptl1 - sethi %hi((PAGE_SIZE<<1)-TRACEREG_SZ-REGWIN_SZ), %g2 - or %g2, %lo((PAGE_SIZE<<1)-TRACEREG_SZ-REGWIN_SZ), %g2 - add %g3, %g2, %g2 -1: stx %g1, [%g2 + REGWIN_SZ + PT_V9_TSTATE] - rdpr %tpc, %g1 - rdpr %tnpc, %g3 - stx %g1, [%g2 + REGWIN_SZ + PT_V9_TPC] - rd %y, %g1 +etrap: rdpr %pil, %g2 +etrap_irq: rdpr %tstate, %g1 + sllx %g2, 20, %g2 + or %g1, %g2, %g1 + andcc %g1, TSTATE_PRIV, %g0 + bne,pn %xcc, etrap_maybe_fpu + sub %sp, REGWIN_SZ + TRACEREG_SZ - STACK_BIAS, %g2 + sethi %hi(TASK_REGOFF), %g2 - stx %g3, [%g2 + REGWIN_SZ + PT_V9_TNPC] - stx %g1, [%g2 + REGWIN_SZ + PT_V9_Y] - save %g2, -STACK_BIAS, %sp ! The ordering of these two instructions - rdpr %pstate, %g1 ! is critical, see winfixup.S for details - bne,pn %xcc, 2f - rdpr %canrestore, %g3 - rdpr %wstate, %g6 - wrpr %g0, 7, %cleanwin + or %g2, %lo(TASK_REGOFF), %g2 + add %g6, %g2, %g2 +etrap_maybe_fpu:rd %fprs, %g3 + brnz,pn %g3, etrap_save_fpu + st %g0, [%g2 + REGWIN_SZ + PT_V9_FPRS] +etrap_after_fpu:rdpr %tpc, %g3 + stx %g1, [%g2 + REGWIN_SZ + PT_V9_TSTATE] + rdpr %tnpc, %g1 - wrpr %g0, 0, %canrestore - sll %g6, 3, %g6 - wrpr %g3, 0, %otherwin - wrpr %g6, %wstate - sethi %uhi(KERNBASE), %g3 - sllx %g3, 32, %g3 - mov PRIMARY_CONTEXT, %g2 - ldxa [%g2] ASI_DMMU, %g6 - stxa %g0, [%g2] ASI_DMMU ! XXX fixup cache if this stays... - mov SECONDARY_CONTEXT, %g2 - stxa %g6, [%g2] ASI_DMMU + stx %g3, [%g2 + REGWIN_SZ + PT_V9_TPC] + rd %y, %g3 + stx %g1, [%g2 + REGWIN_SZ + PT_V9_TNPC] + st %g3, [%g2 + REGWIN_SZ + PT_V9_Y] + save %g2, -STACK_BIAS, %sp ! The ordering here is + rdpr %pstate, %g1 ! critical, see winfixup + bne,pn %xcc, 2f + rdpr %canrestore, %g3 - flush %g3 -2: wrpr %g0, 0x0, %tl - mov %g1, %l1 - mov %g4, %l4 - mov %g5, %l5 - mov %g7, %l2 - wrpr %l1, PSTATE_AG, %pstate - stx %g1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G1] + rdpr %wstate, %g2 + wrpr %g0, 7, %cleanwin + wrpr %g0, 0, %canrestore + sll %g2, 3, %g2 + wrpr %g3, 0, %otherwin + wrpr %g2, 0, %wstate + wr %g0, ASI_DMMU, %asi + ldxa [%g0 + PRIMARY_CONTEXT] %asi, %g2 - stx %g2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G2] - stx %g3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G3] - stx %g4, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G4] - stx %g5, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G5] - stx %g6, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G6] - stx %g7, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G7] - stx %i0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0] - stx %i1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I1] + stxa %g0, [%g0 + PRIMARY_CONTEXT] %asi + stxa %g2, [%g0 + SECONDARY_CONTEXT] %asi + flush %g6 +2: wrpr %g0, 0x0, %tl + or %g1, 0, %l1 + add %g4, 0, %l4 + or %g5, 0, %l5 + add %g7, 0, %l2 - stx %i2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I2] - stx %i3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I3] - stx %i4, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I4] - stx %i5, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I5] - stx %i6, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I6] - stx %i7, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I7] - wrpr %l1, (PSTATE_IE | PSTATE_AG), %pstate - sethi %uhi(KERNBASE), %g4 + or %g6, 0, %l6 + wrpr %l1, (PSTATE_AG|PSTATE_RMO), %pstate + stx %g1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G1] + stx %g2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G2] + stx %g3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G3] + stx %g4, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G4] + stx %g5, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G5] + stx %g6, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G6] - rd %pic, %g6 - jmpl %l2 + 0x4, %g0 - sllx %g4, 32, %g4 -etraptl1: - rdpr %tstate, %g1 - sub %sp, REGWIN_SZ + TRACEREG_SZ - STACK_BIAS, %g2 - ba,pt %xcc, 1b - andcc %g1, TSTATE_PRIV, %g0 - nop + stx %g7, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G7] + stx %i0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0] + stx %i1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I1] + stx %i2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I2] + stx %i3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I3] + stx %i4, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I4] + sethi %uhi(PAGE_OFFSET), %g4 + stx %i5, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I5] + + stx %i6, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I6] + sllx %g4, 32, %g4 + stx %i7, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I7] + wrpr %l1, (PSTATE_IE|PSTATE_AG|PSTATE_RMO), %pstate + jmpl %l2 + 0x4, %g0 + mov %l6, %g6 +etrap_save_fpu: and %g3, FPRS_FEF, %g3 + brz,pn %g3, 2f + + nop + be,a,pt %xcc, 3f + add %g2, (TRACEREG_SZ + REGWIN_SZ), %g2 + wr %g0, ASI_BLK_P, %asi + add %g2, ((TRACEREG_SZ+REGWIN_SZ)-FPUREG_SZ), %g2 + andn %g2, (64 - 1), %g2 +1: st %g3, [%g2 - 0x4 /*REGWIN_SZ + PT_V9_FPRS*/] + rd %gsr, %g3 + + stx %fsr, [%g2 + 0x100] + stx %g3, [%g2 + 0x108] + membar #StoreStore | #LoadStore + stda %f0, [%g2 + 0x000] %asi + stda %f16, [%g2 + 0x040] %asi + stda %f32, [%g2 + 0x080] %asi + stda %f48, [%g2 + 0x0c0] %asi + membar #Sync + + sub %g2, (TRACEREG_SZ + REGWIN_SZ), %g2 +2: b,pt %xcc, etrap_after_fpu + wr %g0, 0, %fprs +3: /* Because Ultra lacks ASI_BLK_NUCLEUS a hack has to take place. */ + mov SECONDARY_CONTEXT, %g3 + stxa %g0, [%g3] ASI_DMMU + flush %g2 + wr %g0, ASI_BLK_S, %asi + nop + + b,pt %xcc, 1b + mov FPRS_FEF, %g3 + nop +etraptl1: rdpr %tstate, %g1 + sub %sp, REGWIN_SZ + TRACEREG_SZ - STACK_BIAS, %g2 + ba,pt %xcc, etrap_maybe_fpu + andcc %g1, TSTATE_PRIV, %g0 + nop +#undef TASK_REGOFF +#undef FPUREG_SZ diff --git a/arch/sparc64/kernel/hack.S b/arch/sparc64/kernel/hack.S deleted file mode 100644 index 84322139544e..000000000000 --- a/arch/sparc64/kernel/hack.S +++ /dev/null @@ -1,170 +0,0 @@ -/* - This is just a huge ugly hack to get things compiled. - Hopefully will disappear quickly, once we get everything - to compile... */ - .text - .align 8 - .globl breakpoint -breakpoint: retl;nop - .globl do_cee -do_cee: retl;nop - .globl do_cee_tl1 -do_cee_tl1: retl;nop - .globl do_dae_tl1 -do_dae_tl1: retl;nop - .globl do_div0_tl1 -do_div0_tl1: retl;nop - .globl do_fpdis_tl1 -do_fpdis_tl1: retl;nop - .globl do_fpieee_tl1 -do_fpieee_tl1: retl;nop - .globl do_fpother_tl1 -do_fpother_tl1: retl;nop - .globl do_iae_tl1 -do_iae_tl1: retl;nop - .globl do_ill_tl1 -do_ill_tl1: retl;nop - .globl do_irq_tl1 -do_irq_tl1: retl;nop - .globl do_lddfmna -do_lddfmna: retl;nop - .globl do_lddfmna_tl1 -do_lddfmna_tl1: retl;nop - .globl do_paw -do_paw: retl;nop - .globl do_paw_tl1 -do_paw_tl1: retl;nop - .globl do_stdfmna -do_stdfmna: retl;nop - .globl do_stdfmna_tl1 -do_stdfmna_tl1: retl;nop - .globl do_tof_tl1 -do_tof_tl1: retl;nop - .globl do_vaw -do_vaw: retl;nop - .globl do_vaw_tl1 -do_vaw_tl1: retl;nop - .globl floppy_hardint -floppy_hardint: retl;nop - .globl get_cpuid -get_cpuid: retl;mov 0, %o0 - .globl getcc -getcc: retl;nop - .globl halt -halt: retl;nop - .globl indirect_syscall -indirect_syscall: retl;nop - .globl install_linux_ticker -install_linux_ticker: retl;nop - .globl install_obp_ticker -install_obp_ticker: retl;nop - .globl linux_dbvec -linux_dbvec: retl;nop - .globl linux_num_cpus -linux_num_cpus: retl;nop - .globl netbsd_syscall -netbsd_syscall: retl;nop - .globl setcc -setcc: retl;nop - .globl solaris_syscall -solaris_syscall: retl;nop - .globl sunos_mmap -sunos_mmap: retl;nop - .globl sunos_syscall -sunos_syscall: retl;nop - .globl svr4_getcontext -svr4_getcontext: retl;nop - .globl svr4_setcontext -svr4_setcontext: retl;nop - .globl sunos_accept -sunos_accept: retl;nop - .globl sunos_audit -sunos_audit: retl;nop - .globl sunos_brk -sunos_brk: retl;nop - .globl sunos_execv -sunos_execv: retl;nop - .globl sunos_fpathconf -sunos_fpathconf: retl;nop - .globl sunos_getdents -sunos_getdents: retl;nop - .globl sunos_getdirentries -sunos_getdirentries: retl;nop - .globl sunos_getdomainname -sunos_getdomainname: retl;nop - .globl sunos_getdtablesize -sunos_getdtablesize: retl;nop - .globl sunos_getgid -sunos_getgid: retl;nop - .globl sunos_gethostid -sunos_gethostid: retl;nop - .globl sunos_getpid -sunos_getpid: retl;nop - .globl sunos_getsockopt -sunos_getsockopt: retl;nop - .globl sunos_getuid -sunos_getuid: retl;nop - .globl sunos_indir -sunos_indir: retl;nop - .globl sunos_ioctl -sunos_ioctl: retl;nop - .globl sunos_killpg -sunos_killpg: retl;nop - .globl sunos_madvise -sunos_madvise: retl;nop - .globl sunos_mctl -sunos_mctl: retl;nop - .globl sunos_mincore -sunos_mincore: retl;nop - .globl sunos_mount -sunos_mount: retl;nop - .globl sunos_nop -sunos_nop: retl;nop - .globl sunos_nosys -sunos_nosys: retl;nop - .globl sunos_open -sunos_open: retl;nop - .globl sunos_pathconf -sunos_pathconf: retl;nop - .globl sunos_poll -sunos_poll: retl;nop - .globl sunos_read -sunos_read: retl;nop - .globl sunos_readv -sunos_readv: retl;nop - .globl sunos_recv -sunos_recv: retl;nop - .globl sunos_sbrk -sunos_sbrk: retl;nop - .globl sunos_select -sunos_select: retl;nop - .globl sunos_semsys -sunos_semsys: retl;nop - .globl sunos_send -sunos_send: retl;nop - .globl sunos_setpgrp -sunos_setpgrp: retl;nop - .globl sunos_setsockopt -sunos_setsockopt: retl;nop - .globl sunos_shmsys -sunos_shmsys: retl;nop - .globl sunos_sigaction -sunos_sigaction: retl;nop - .globl sunos_sigblock -sunos_sigblock: retl;nop - .globl sunos_sigsetmask -sunos_sigsetmask: retl;nop - .globl sunos_sstk -sunos_sstk: retl;nop - .globl sunos_sysconf -sunos_sysconf: retl;nop - .globl sunos_uname -sunos_uname: retl;nop - .globl sunos_vadvise -sunos_vadvise: retl;nop - .globl sunos_wait4 -sunos_wait4: retl;nop - .globl sunos_write -sunos_write: retl;nop - .globl sunos_writev -sunos_writev: retl;nop diff --git a/arch/sparc64/kernel/head.S b/arch/sparc64/kernel/head.S index 3844c24c3842..0ed975aff584 100644 --- a/arch/sparc64/kernel/head.S +++ b/arch/sparc64/kernel/head.S @@ -1,4 +1,4 @@ -/* $Id: head.S,v 1.31 1997/05/30 22:35:28 davem Exp $ +/* $Id: head.S,v 1.43 1997/07/07 03:05:25 davem Exp $ * head.S: Initial boot code for the Sparc64 port of Linux. * * Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu) @@ -8,20 +8,25 @@ */ #include +#include +#include +#include #include #include #include #include #include #include +#include +#include #include #include /* This section from from _start to sparc64_boot_end should fit into - * 0xffff.f800.0000.4000 to 0xffff.f800.0000.8000 and will be sharing space - * with bootup_user_stack, which is from 0xffff.f800.0000.4000 to - * 0xffff.f800.0000.6000 and bootup_kernel_stack, which is from - * 0xffff.f800.0000.6000 to 0xffff.f800.0000.8000. + * 0x0000.0000.0040.4000 to 0x0000.0000.0040.8000 and will be sharing space + * with bootup_user_stack, which is from 0x0000.0000.0040.4000 to + * 0x0000.0000.0040.6000 and bootup_kernel_stack, which is from + * 0x0000.0000.0040.6000 to 0x0000.0000.0040.8000. */ .text @@ -31,7 +36,7 @@ start: _stext: stext: bootup_user_stack: -! 0xfffff80000004000 +! 0x0000000000404000 b sparc64_boot flushw /* Flush register file. */ @@ -41,10 +46,11 @@ bootup_user_stack: */ .global root_flags, ram_flags, root_dev .global ramdisk_image, ramdisk_size + .globl silo_args .ascii "HdrS" .word LINUX_VERSION_CODE - .half 0x0201 /* HdrS version */ + .half 0x0202 /* HdrS version */ root_flags: .half 1 root_dev: @@ -55,7 +61,8 @@ ramdisk_image: .word 0 ramdisk_size: .word 0 - .xword reboot_command + .xword reboot_command + .xword bootstr_len /* We must be careful, 32-bit OpenBOOT will get confused if it * tries to save away a register window to a 64-bit kernel @@ -80,26 +87,7 @@ sparc64_boot: * Again, typically PROM has left %pil at 13 or similar, and * (PSTATE_PRIV | PSTATE_PEF | PSTATE_IE) in %pstate. */ - wrpr %g0, 0xf, %pil /* Interrupts off. */ - wrpr %g0, (PSTATE_PRIV|PSTATE_PEF), %pstate - - /* Check if we are mapped where we expect to be in virtual - * memory. The Solaris /boot elf format bootloader - * will peek into our elf header and load us where - * we want to be, otherwise we have to re-map. - */ -current_pc: - rd %pc, %g3 - sethi %uhi(KERNBASE), %g4 - sllx %g4, 32, %g4 - - /* Check the run time program counter. */ - - set current_pc, %g5 - add %g5, %g4, %g5 - cmp %g3, %g5 - be %xcc, sun4u_init - nop + wrpr %g0, (PSTATE_PRIV|PSTATE_PEF|PSTATE_IE), %pstate create_mappings: /* %g5 holds the tlb data */ @@ -136,15 +124,10 @@ create_mappings: cmp %g1, %g2 be,a,pn %xcc, got_tlbentry ldxa [%l0] ASI_ITLB_DATA_ACCESS, %g1 - cmp %l1, (63 << 3) + cmp %l0, (63 << 3) blu,pt %xcc, 1b add %l0, (1 << 3), %l0 -boot_failed: - /* Debugging 8-) */ - set 0xdeadbeef, %g1 - t 0x11 - got_tlbentry: /* Nops here again, perhaps Cheetah/Blackbird are better behaved... */ nop @@ -159,33 +142,73 @@ got_tlbentry: or %g5, %g1, %g5 /* Or it into TAG being built. */ + clr %l0 /* TLB entry walker. */ + sethi %hi(KERNBASE), %g3 /* 4M lower limit */ + sethi %hi(KERNBASE<<1), %g7 /* 8M upper limit */ + mov TLB_TAG_ACCESS, %l7 +1: + /* Yes, the nops seem to be necessary for now, don't ask me why. -DaveM */ + ldxa [%l0] ASI_ITLB_TAG_READ, %g1 + nop + nop + nop + andn %g1, %l2, %g1 /* Get vaddr */ + cmp %g1, %g3 + blu,pn %xcc, 2f + cmp %g1, %g7 + bgeu,pn %xcc, 2f + nop + stxa %g0, [%l7] ASI_IMMU + stxa %g0, [%l0] ASI_ITLB_DATA_ACCESS +2: + cmp %l0, (63 << 3) + blu,pt %xcc, 1b + add %l0, (1 << 3), %l0 + + nop; nop; nop + + clr %l0 /* TLB entry walker. */ +1: + /* Yes, the nops seem to be necessary for now, don't ask me why. -DaveM */ + ldxa [%l0] ASI_DTLB_TAG_READ, %g1 + nop + nop + nop + andn %g1, %l2, %g1 /* Get vaddr */ + cmp %g1, %g3 + blu,pn %xcc, 2f + cmp %g1, %g7 + bgeu,pn %xcc, 2f + nop + stxa %g0, [%l7] ASI_DMMU + stxa %g0, [%l0] ASI_DTLB_DATA_ACCESS +2: + cmp %l0, (63 << 3) + blu,pt %xcc, 1b + add %l0, (1 << 3), %l0 + + nop; nop; nop + + /* PROM never puts any TLB entries into the MMU with the lock bit - * set. So we gladly use tlb entry 63 for KERNBASE, 62 for - * boot time locked PROM CIF handler page, we remove the locked - * bit for the CIF page in paging_init(). + * set. So we gladly use tlb entry 63 for KERNBASE. */ - mov TLB_TAG_ACCESS, %g3 - mov (63 << 3), %g7 - stxa %g4, [%g3] ASI_IMMU /* KERNBASE into TLB TAG */ - stxa %g5, [%g7] ASI_ITLB_DATA_ACCESS /* TTE into TLB DATA */ - membar #Sync - /* Same for DTLB */ - stxa %g4, [%g3] ASI_DMMU /* KERNBASE into TLB TAG */ + sethi %hi(KERNBASE), %g3 + mov (63 << 3), %g7 + stxa %g3, [%l7] ASI_DMMU /* KERNBASE into TLB TAG */ stxa %g5, [%g7] ASI_DTLB_DATA_ACCESS /* TTE into TLB DATA */ membar #Sync - - /* Kill instruction prefetch queues. */ - flush %g4 + stxa %g3, [%l7] ASI_IMMU /* KERNBASE into TLB TAG */ + stxa %g5, [%g7] ASI_ITLB_DATA_ACCESS /* TTE into TLB DATA */ membar #Sync - - ba,pt %xcc, go_to_highmem + flush %g3 + membar #Sync + ba,pt %xcc, 1f nop - -go_to_highmem: - /* Now do a non-relative jump so that PC is in high-memory */ +1: set sun4u_init, %g2 - jmpl %g2 + %g4, %g0 + jmpl %g2 + %g0, %g0 nop sun4u_init: @@ -198,42 +221,16 @@ sun4u_init: stxa %g0, [%g7] ASI_DMMU membar #Sync - /* The lock bit has to be removed from this page later on, - * but before firing up init we will use PROM a lot, so we - * lock it there now... - */ - - /* Compute PROM CIF interface page TTE. */ - sethi %hi(__p1275_loc), %g7 - or %g7, (_PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_W | _PAGE_L), %g7 - sethi %uhi(_PAGE_VALID), %g5 - sethi %hi(0x8000), %g3 - sllx %g5, 32, %g5 - mov TLB_TAG_ACCESS, %g6 - or %g5, %g7, %g5 - add %g5, %g1, %g5 /* Add in physbase. */ - - mov (62 << 3), %g7 /* TLB entry 62 */ - stxa %g3, [%g6] ASI_IMMU /* CIF page into TLB TAG */ - stxa %g5, [%g7] ASI_ITLB_DATA_ACCESS /* TTE into TLB DATA */ - membar #Sync - - /* Same for DTLB */ - stxa %g3, [%g6] ASI_DMMU /* CIF page into TLB TAG */ - stxa %g5, [%g7] ASI_DTLB_DATA_ACCESS /* TTE into TLB DATA */ - membar #Sync - - /* Kill instruction prefetch queues. */ - flush %g3 - membar #Sync + sethi %uhi(PAGE_OFFSET), %g4 + sllx %g4, 32, %g4 /* We are now safely (we hope) in Nucleus context (0), rewrite * the KERNBASE TTE's so they no longer have the global bit set. * Don't forget to setup TAG_ACCESS first 8-) */ mov TLB_TAG_ACCESS, %g2 - stxa %g4, [%g2] ASI_IMMU - stxa %g4, [%g2] ASI_DMMU + stxa %g3, [%g2] ASI_IMMU + stxa %g3, [%g2] ASI_DMMU mov (63 << 3), %g7 ldxa [%g7] ASI_ITLB_DATA_ACCESS, %g1 @@ -247,30 +244,22 @@ sun4u_init: membar #Sync /* Kill instruction prefetch queues. */ - flush %g4 + flush %g3 membar #Sync - /* Compute the number of windows in this machine - * store this in nwindows and nwindowsm1 - */ - rdpr %ver, %g1 /* Get VERSION register. */ - sethi %hi(nwindows), %g2 - and %g1, VERS_MAXWIN, %g5 - or %g2,%lo(nwindows),%g2 - add %g5, 1, %g6 - add %g2, (nwindows - nwindowsm1), %g3 - stx %g6, [%g2 + %g4] - stx %g5, [%g3 + %g4] - sethi %hi(init_task_union), %g6 or %g6, %lo(init_task_union), %g6 - add %g6, %g4, %g6 ! g6 usage is fixed as well mov %sp, %l6 mov %o4, %l7 +#if 0 /* We don't do it like this anymore, but for historical hack value + * I leave this snippet here to show how crazy we can be sometimes. 8-) + */ + /* Setup "Linux Current Register", thanks Sun 8-) */ wr %g0, 0x1, %pcr wr %g6, 0x0, %pic +#endif mov 1, %g5 sllx %g5, (PAGE_SHIFT + 1), %g5 @@ -291,23 +280,19 @@ sun4u_init: add %l1, %l2, %l1 andn %l1, %l2, %l1 add %l2, 1, %l2 - add %l0, %g4, %o0 + add %l0, %g0, %o0 1: - clr %o1 - sethi %hi(PAGE_SIZE), %o2 - or %o2, %lo(PAGE_SIZE), %o2 - call __memset + mov %l2, %o1 + call __bzero add %l0, %l2, %l0 cmp %l0, %l1 blu,pt %xcc, 1b - add %l0, %g4, %o0 + add %l0, %g0, %o0 /* Now clear empty_zero_page */ - clr %o1 - sethi %hi(PAGE_SIZE), %o2 - or %o2, %lo(PAGE_SIZE), %o2 - call __memset - mov %g4, %o0 + mov %l2, %o1 + call __bzero + mov %g3, %o0 mov %l6, %o1 ! OpenPROM stack call prom_init @@ -320,36 +305,45 @@ sun4u_init: .globl setup_tba setup_tba: + save %sp, -160, %sp + + rdpr %tba, %g7 + sethi %hi(prom_tba), %o1 + or %o1, %lo(prom_tba), %o1 + stx %g7, [%o1] + + /* Setup "Linux" globals 8-) */ + rdpr %pstate, %o1 + mov %g6, %o2 + wrpr %o1, (PSTATE_AG|PSTATE_IE), %pstate sethi %hi(sparc64_ttable_tl0), %g5 - add %g5, %g4, %g5 wrpr %g5, %tba + mov %o2, %g6 /* Set up MMU globals */ - rdpr %pstate, %o1 - wrpr %o1, PSTATE_MG, %pstate + wrpr %o1, (PSTATE_MG|PSTATE_IE), %pstate /* PGD/PMD offset mask, used by TLB miss handlers. */ sethi %hi(0x1ff8), %g2 or %g2, %lo(0x1ff8), %g2 /* Kernel PGDIR used by TLB miss handlers. */ - mov %o0, %g6 + mov %i0, %g6 /* To catch bootup bugs, this is user PGDIR for TLB miss handlers. */ clr %g7 /* Setup Interrupt globals */ - wrpr %o1, PSTATE_IG, %pstate - sethi %uhi(ivector_to_mask), %g4 - or %g4, %ulo(ivector_to_mask), %g4 + wrpr %o1, (PSTATE_IG|PSTATE_IE), %pstate sethi %hi(ivector_to_mask), %g5 - or %g5, %lo(ivector_to_mask), %g5 - or %g5, %g4, %g1 /* IVECTOR table */ + or %g5, %lo(ivector_to_mask), %g1 /* IVECTOR table */ mov 0x40, %g2 /* INTR data 0 register */ - andn %o1, PSTATE_IE, %o1 + /* Ok, we're done setting up all the state our trap mechanims needs, + * now get back into normal globals and let the PROM know what it up. + */ wrpr %g0, %g0, %wstate - wrpr %o1, %g0, %pstate + wrpr %o1, PSTATE_IE, %pstate /* Zap TSB BASE to zero with TSB_size==1. */ mov TSB_REG, %o4 @@ -359,8 +353,16 @@ setup_tba: membar #Sync - retl - nop + sethi %hi(sparc64_ttable_tl0), %g5 + call prom_set_trap_table + mov %g5, %o0 + + rdpr %pstate, %o1 + or %o1, PSTATE_IE, %o1 + wrpr %o1, 0, %pstate + + ret + restore sparc64_boot_end: .skip 0x2000 + _start - sparc64_boot_end @@ -369,18 +371,21 @@ bootup_user_stack_end: bootup_kernel_stack: .skip 0x2000 -! 0xfffff80000008000 +! 0x0000000000408000 #include "ttable.S" +#include "etrap.S" +#include "rtrap.S" +#include "winfixup.S" +#include "entry.S" /* This is just anal retentiveness on my part... */ .align 16384 .data .align 8 - .globl nwindows, nwindowsm1 -nwindows: .xword 0 -nwindowsm1: .xword 0 + .globl prom_tba +prom_tba: .xword 0 .section ".fixup",#alloc,#execinstr .globl __ret_efault __ret_efault: diff --git a/arch/sparc64/kernel/ioport.c b/arch/sparc64/kernel/ioport.c index 2f94e9102042..390c33517b12 100644 --- a/arch/sparc64/kernel/ioport.c +++ b/arch/sparc64/kernel/ioport.c @@ -1,4 +1,4 @@ -/* $Id: ioport.c,v 1.7 1997/04/10 05:13:01 davem Exp $ +/* $Id: ioport.c,v 1.10 1997/06/30 09:24:02 jj Exp $ * ioport.c: Simple io mapping allocator. * * Copyright (C) 1995,1996 David S. Miller (davem@caip.rutgers.edu) @@ -64,13 +64,7 @@ void *sparc_alloc_io (u32 address, void *virtual, int len, char *name, /* Tell Linux resource manager about the mapping */ request_region ((vaddr | offset), len, name); } else { - vaddr = occupy_region(sparc_iobase_vaddr, IOBASE_END, - (offset + len + PAGE_SIZE-1) & PAGE_MASK, PAGE_SIZE, name); - if (vaddr == 0) { - /* Usually we cannot see printks in this case. */ - prom_printf("alloc_io: cannot occupy %d region\n", len); - prom_halt(); - } + return __va(addr); } base_address = vaddr; @@ -88,6 +82,9 @@ void sparc_free_io (void *virtual, int len) { unsigned long vaddr = (unsigned long) virtual & PAGE_MASK; unsigned long plen = (((unsigned long)virtual & ~PAGE_MASK) + len + PAGE_SIZE-1) & PAGE_MASK; + + if (virtual >= PAGE_OFFSET + 0x10000000000UL) + return; release_region(vaddr, plen); diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c index 3c9b1a89e3d7..6c2225d1da3c 100644 --- a/arch/sparc64/kernel/irq.c +++ b/arch/sparc64/kernel/irq.c @@ -1,4 +1,4 @@ -/* $Id: irq.c,v 1.13 1997/05/27 07:54:28 davem Exp $ +/* $Id: irq.c,v 1.14 1997/06/24 17:30:26 davem Exp $ * irq.c: UltraSparc IRQ handling/init/registry. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -558,6 +558,8 @@ struct sun5_timer { volatile u32 limit1, _unused3; } *prom_timers; +static u32 prom_limit0, prom_limit1; + static void map_prom_timers(void) { unsigned int addr[3]; @@ -590,24 +592,39 @@ static void kill_prom_timer(void) if(!prom_timers) return; + /* Save them away for later. */ + prom_limit0 = prom_timers->limit0; + prom_limit1 = prom_timers->limit1; + /* Just as in sun4c/sun4m PROM uses timer which ticks at IRQ 14. * We turn both off here just to be paranoid. */ prom_timers->limit0 = 0; prom_timers->limit1 = 0; + + /* Wheee, eat the interrupt packet too... */ + __asm__ __volatile__(" + mov 0x40, %%g2 + ldxa [%%g0] %0, %%g1 + ldxa [%%g2] %1, %%g1 + stxa %%g0, [%%g0] %0 + membar #Sync +" : /* no outputs */ + : "i" (ASI_INTR_RECEIVE), "i" (ASI_UDB_INTR_R) + : "g1", "g2"); } -#if 0 /* Unused at this time. -DaveM */ -static void enable_prom_timer(void) +void enable_prom_timer(void) { if(!prom_timers) return; - /* Set it to fire off every 10ms. */ - prom_timers->limit1 = 0xa000270f; + /* Set it to whatever was there before. */ + prom_timers->limit1 = prom_limit1; prom_timers->count1 = 0; + prom_timers->limit0 = prom_limit0; + prom_timers->count0 = 0; } -#endif __initfunc(void init_IRQ(void)) { diff --git a/arch/sparc64/kernel/process.c b/arch/sparc64/kernel/process.c index 626ad73cb474..08ed1bc670ad 100644 --- a/arch/sparc64/kernel/process.c +++ b/arch/sparc64/kernel/process.c @@ -1,4 +1,4 @@ -/* $Id: process.c,v 1.18 1997/06/13 14:02:42 davem Exp $ +/* $Id: process.c,v 1.26 1997/07/01 21:15:07 jj Exp $ * arch/sparc64/kernel/process.c * * Copyright (C) 1995, 1996 David S. Miller (davem@caip.rutgers.edu) @@ -137,7 +137,7 @@ void machine_restart(char * cmd) prom_reboot(cmd); if (*reboot_command) prom_reboot(reboot_command); - prom_feval ("reset"); + prom_reboot(""); panic("Reboot failed!"); } @@ -146,8 +146,55 @@ void machine_power_off(void) machine_halt(); } -void show_regwindow(struct reg_window *rw) +static void show_regwindow32(struct pt_regs *regs) { + struct reg_window32 *rw; + struct reg_window32 r_w; + unsigned long old_fs; + + __asm__ __volatile__ ("flushw"); + rw = (struct reg_window32 *)((long)(unsigned)regs->u_regs[14]); + old_fs = get_fs(); + set_fs (USER_DS); + if (copy_from_user (&r_w, rw, sizeof(r_w))) { + set_fs (old_fs); + return; + } + rw = &r_w; + set_fs (old_fs); + printk("l0: %016x l1: %016x l2: %016x l3: %016x\n" + "l4: %016x l5: %016x l6: %016x l7: %016x\n", + rw->locals[0], rw->locals[1], rw->locals[2], rw->locals[3], + rw->locals[4], rw->locals[5], rw->locals[6], rw->locals[7]); + printk("i0: %016x i1: %016x i2: %016x i3: %016x\n" + "i4: %016x i5: %016x i6: %016x i7: %016x\n", + rw->ins[0], rw->ins[1], rw->ins[2], rw->ins[3], + rw->ins[4], rw->ins[5], rw->ins[6], rw->ins[7]); +} + +static void show_regwindow(struct pt_regs *regs) +{ + struct reg_window *rw; + struct reg_window r_w; + unsigned long old_fs; + + if ((regs->tstate & TSTATE_PRIV) || !(current->tss.flags & SPARC_FLAG_32BIT)) { + __asm__ __volatile__ ("flushw"); + rw = (struct reg_window *)(regs->u_regs[14] + STACK_BIAS); + if (!(regs->tstate & TSTATE_PRIV)) { + old_fs = get_fs(); + set_fs (USER_DS); + if (copy_from_user (&r_w, rw, sizeof(r_w))) { + set_fs (old_fs); + return; + } + rw = &r_w; + set_fs (old_fs); + } + } else { + show_regwindow32(regs); + return; + } printk("l0: %016lx l1: %016lx l2: %016lx l3: %016lx\n", rw->locals[0], rw->locals[1], rw->locals[2], rw->locals[3]); printk("l4: %016lx l5: %016lx l6: %016lx l7: %016lx\n", @@ -158,18 +205,6 @@ void show_regwindow(struct reg_window *rw) rw->ins[4], rw->ins[5], rw->ins[6], rw->ins[7]); } -void show_regwindow32(struct reg_window32 *rw) -{ - printk("l0: %08x l1: %08x l2: %08x l3: %08x\n" - "l4: %08x l5: %08x l6: %08x l7: %08x\n", - rw->locals[0], rw->locals[1], rw->locals[2], rw->locals[3], - rw->locals[4], rw->locals[5], rw->locals[6], rw->locals[7]); - printk("i0: %08x i1: %08x i2: %08x i3: %08x\n" - "i4: %08x i5: %08x i6: %08x i7: %08x\n", - rw->ins[0], rw->ins[1], rw->ins[2], rw->ins[3], - rw->ins[4], rw->ins[5], rw->ins[6], rw->ins[7]); -} - void show_stackframe(struct sparc_stackf *sf) { unsigned long size; @@ -231,7 +266,7 @@ void show_regs(struct pt_regs * regs) #if __MPP__ printk("CID: %d\n",mpp_cid()); #endif - printk("TSTATE: %016lx TPC: %016lx TNPC: %016lx Y: %016lx\n", regs->tstate, + printk("TSTATE: %016lx TPC: %016lx TNPC: %016lx Y: %08x\n", regs->tstate, regs->tpc, regs->tnpc, regs->y); printk("g0: %016lx g1: %016lx g2: %016lx g3: %016lx\n", regs->u_regs[0], regs->u_regs[1], regs->u_regs[2], @@ -245,9 +280,7 @@ void show_regs(struct pt_regs * regs) printk("o4: %016lx o5: %016lx sp: %016lx ret_pc: %016lx\n", regs->u_regs[12], regs->u_regs[13], regs->u_regs[14], regs->u_regs[15]); -#if 0 - show_regwindow((struct reg_window *)(regs->u_regs[14] + STACK_BIAS)); -#endif + show_regwindow(regs); } void show_regs32(struct pt_regs32 *regs) @@ -269,7 +302,6 @@ void show_regs32(struct pt_regs32 *regs) printk("o4: %08x o5: %08x sp: %08x ret_pc: %08x\n", regs->u_regs[12], regs->u_regs[13], regs->u_regs[14], regs->u_regs[15]); - show_regwindow32((struct reg_window32 *)((unsigned long)regs->u_regs[14])); } void show_thread(struct thread_struct *tss) @@ -290,46 +322,22 @@ void show_thread(struct thread_struct *tss) continue; printk("reg_window[%d]:\n", i); printk("stack ptr: 0x%016lx\n", tss->rwbuf_stkptrs[i]); - show_regwindow(&tss->reg_window[i]); } printk("w_saved: 0x%08lx\n", tss->w_saved); - /* XXX missing: float_regs */ - printk("fsr: 0x%016lx\n", tss->fsr); - printk("sstk_info.stack: 0x%016lx\n", (unsigned long)tss->sstk_info.the_stack); printk("sstk_info.status: 0x%016lx\n", (unsigned long)tss->sstk_info.cur_status); - printk("flags: 0x%016lx\n", tss->flags); - printk("current_ds: 0x%016x\n", tss->current_ds); + printk("flags: 0x%08x\n", tss->flags); + printk("current_ds: 0x%016lx\n", tss->current_ds); /* XXX missing: core_exec */ } -/* - * Free current thread data structures etc.. - */ +/* Free current thread data structures etc.. */ void exit_thread(void) { -#ifndef __SMP__ - if(last_task_used_math == current) { -#else - if(current->flags & PF_USEDFPU) { -#endif - fprs_write(FPRS_FEF); - if(current->tss.flags & SPARC_FLAG_32BIT) - fpsave32((unsigned long *)¤t->tss.float_regs[0], - ¤t->tss.fsr); - else - fpsave((unsigned long *)¤t->tss.float_regs[0], - ¤t->tss.fsr); -#ifndef __SMP__ - last_task_used_math = NULL; -#else - current->flags &= ~PF_USEDFPU; -#endif - } } void flush_thread(void) @@ -338,26 +346,9 @@ void flush_thread(void) current->tss.sstk_info.cur_status = 0; current->tss.sstk_info.the_stack = 0; - /* No new signal delivery by default */ + /* No new signal delivery by default. */ current->tss.new_signal = 0; -#ifndef __SMP__ - if(last_task_used_math == current) { -#else - if(current->flags & PF_USEDFPU) { -#endif - fprs_write(FPRS_FEF); - if(current->tss.flags & SPARC_FLAG_32BIT) - fpsave32((unsigned long *)¤t->tss.float_regs[0], - ¤t->tss.fsr); - else - fpsave((unsigned long *)¤t->tss.float_regs[0], - ¤t->tss.fsr); -#ifndef __SMP__ - last_task_used_math = NULL; -#else - current->flags &= ~PF_USEDFPU; -#endif - } + current->flags &= ~PF_USEDFPU; /* Now, this task is no longer a kernel thread. */ current->tss.current_ds = USER_DS; @@ -371,54 +362,7 @@ void flush_thread(void) } current->tss.ctx = current->mm->context & 0x1fff; spitfire_set_secondary_context (current->tss.ctx); -} - -static __inline__ void copy_regs(struct pt_regs *dst, struct pt_regs *src) -{ - __asm__ __volatile__("ldd\t[%1 + 0x00], %%g2\n\t" - "ldd\t[%1 + 0x08], %%g4\n\t" - "ldd\t[%1 + 0x10], %%o4\n\t" - "std\t%%g2, [%0 + 0x00]\n\t" - "std\t%%g4, [%0 + 0x08]\n\t" - "std\t%%o4, [%0 + 0x10]\n\t" - "ldd\t[%1 + 0x18], %%g2\n\t" - "ldd\t[%1 + 0x20], %%g4\n\t" - "ldd\t[%1 + 0x28], %%o4\n\t" - "std\t%%g2, [%0 + 0x18]\n\t" - "std\t%%g4, [%0 + 0x20]\n\t" - "std\t%%o4, [%0 + 0x28]\n\t" - "ldd\t[%1 + 0x30], %%g2\n\t" - "ldd\t[%1 + 0x38], %%g4\n\t" - "ldd\t[%1 + 0x40], %%o4\n\t" - "std\t%%g2, [%0 + 0x30]\n\t" - "std\t%%g4, [%0 + 0x38]\n\t" - "ldd\t[%1 + 0x48], %%g2\n\t" - "std\t%%o4, [%0 + 0x40]\n\t" - "std\t%%g2, [%0 + 0x48]\n\t" : : - "r" (dst), "r" (src) : - "g2", "g3", "g4", "g5", "o4", "o5"); -} - -static __inline__ void copy_regwin(struct reg_window *dst, struct reg_window *src) -{ - __asm__ __volatile__("ldd\t[%1 + 0x00], %%g2\n\t" - "ldd\t[%1 + 0x08], %%g4\n\t" - "ldd\t[%1 + 0x10], %%o4\n\t" - "std\t%%g2, [%0 + 0x00]\n\t" - "std\t%%g4, [%0 + 0x08]\n\t" - "std\t%%o4, [%0 + 0x10]\n\t" - "ldd\t[%1 + 0x18], %%g2\n\t" - "ldd\t[%1 + 0x20], %%g4\n\t" - "ldd\t[%1 + 0x28], %%o4\n\t" - "std\t%%g2, [%0 + 0x18]\n\t" - "std\t%%g4, [%0 + 0x20]\n\t" - "std\t%%o4, [%0 + 0x28]\n\t" - "ldd\t[%1 + 0x30], %%g2\n\t" - "ldd\t[%1 + 0x38], %%g4\n\t" - "std\t%%g2, [%0 + 0x30]\n\t" - "std\t%%g4, [%0 + 0x38]\n\t" : : - "r" (dst), "r" (src) : - "g2", "g3", "g4", "g5", "o4", "o5"); + __asm__ __volatile__("flush %g6"); } static __inline__ struct sparc_stackf * @@ -462,16 +406,19 @@ void synchronize_user_stack(void) flush_user_windows(); if((window = tp->w_saved) != 0) { int winsize = REGWIN_SZ; + int bias = 0; #ifdef DEBUG_WINFIXUPS printk("sus(%d", (int)window); #endif if(tp->flags & SPARC_FLAG_32BIT) winsize = REGWIN32_SZ; + else + bias = STACK_BIAS; window -= 1; do { - unsigned long sp = tp->rwbuf_stkptrs[window]; + unsigned long sp = (tp->rwbuf_stkptrs[window] + bias); struct reg_window *rwin = &tp->reg_window[window]; if(!copy_to_user((char *)sp, rwin, winsize)) { @@ -490,9 +437,12 @@ void fault_in_user_windows(struct pt_regs *regs) struct thread_struct *tp = ¤t->tss; unsigned long window; int winsize = REGWIN_SZ; + int bias = 0; if(tp->flags & SPARC_FLAG_32BIT) winsize = REGWIN32_SZ; + else + bias = STACK_BIAS; flush_user_windows(); window = tp->w_saved; #ifdef DEBUG_WINFIXUPS @@ -501,7 +451,7 @@ void fault_in_user_windows(struct pt_regs *regs) if(window != 0) { window -= 1; do { - unsigned long sp = tp->rwbuf_stkptrs[window]; + unsigned long sp = (tp->rwbuf_stkptrs[window] + bias); struct reg_window *rwin = &tp->reg_window[window]; if(copy_to_user((char *)sp, rwin, winsize)) @@ -531,51 +481,34 @@ extern void ret_from_syscall(void); int copy_thread(int nr, unsigned long clone_flags, unsigned long sp, struct task_struct *p, struct pt_regs *regs) { - struct pt_regs *childregs; - struct reg_window *new_stack, *old_stack; unsigned long stack_offset; + char *child_trap_frame; + int tframe_size; -#ifndef __SMP__ - if(last_task_used_math == current) { -#else - if(current->flags & PF_USEDFPU) { -#endif - fprs_write(FPRS_FEF); - fpsave((unsigned long *)&p->tss.float_regs[0], &p->tss.fsr); -#ifdef __SMP__ - current->flags &= ~PF_USEDFPU; +#if 0 /* Now all syscall entries flip off the fpu. */ + if(regs->tstate & TSTATE_PRIV) + regs->fprs = 0; #endif - } - /* Calculate offset to stack_frame & pt_regs */ - stack_offset = ((PAGE_SIZE<<1) - TRACEREG_SZ); - - if(regs->tstate & TSTATE_PRIV) - stack_offset -= REGWIN_SZ; - - childregs = ((struct pt_regs *) (((unsigned long)p) + stack_offset)); - *childregs = *regs; - new_stack = (((struct reg_window *) childregs) - 1); - old_stack = (((struct reg_window *) regs) - 1); - *new_stack = *old_stack; - - p->tss.ksp = ((unsigned long) new_stack) - STACK_BIAS; + stack_offset = (((PAGE_SIZE << 1) - + ((sizeof(unsigned int)*64) + (2*sizeof(unsigned long)))) & + ~(64 - 1)) - (TRACEREG_SZ+REGWIN_SZ); + tframe_size = (TRACEREG_SZ + REGWIN_SZ) + + (sizeof(unsigned int) * 64) + (2 * sizeof(unsigned long)); + child_trap_frame = ((char *)p) + stack_offset; + memcpy(child_trap_frame, (((struct reg_window *)regs)-1), tframe_size); + p->tss.ksp = ((unsigned long) child_trap_frame) - STACK_BIAS; p->tss.kpc = ((unsigned long) ret_from_syscall) - 0x8; - p->tss.kregs = childregs; - - /* Don't look... */ + p->tss.kregs = (struct pt_regs *)(child_trap_frame+sizeof(struct reg_window)); p->tss.cwp = regs->u_regs[UREG_G0]; - - /* tss.wstate was copied by do_fork() */ - if(regs->tstate & TSTATE_PRIV) { - childregs->u_regs[UREG_FP] = p->tss.ksp; + p->tss.kregs->u_regs[UREG_FP] = p->tss.ksp; p->tss.flags |= SPARC_FLAG_KTHREAD; p->tss.current_ds = KERNEL_DS; p->tss.ctx = 0; - childregs->u_regs[UREG_G6] = (unsigned long) p; + p->tss.kregs->u_regs[UREG_G6] = (unsigned long) p; } else { - childregs->u_regs[UREG_FP] = sp; + p->tss.kregs->u_regs[UREG_FP] = sp; p->tss.flags &= ~SPARC_FLAG_KTHREAD; p->tss.current_ds = USER_DS; p->tss.ctx = (p->mm->context & 0x1fff); @@ -590,39 +523,20 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long sp, */ childstack = (struct sparc_stackf *)sp; parentstack = (struct sparc_stackf *)regs->u_regs[UREG_FP]; - -#if 0 - printk("clone: parent stack:\n"); - show_stackframe(parentstack); -#endif - childstack = clone_stackframe(childstack, parentstack); if (!childstack) return -EFAULT; - -#if 0 - printk("clone: child stack:\n"); - show_stackframe(childstack); -#endif - childregs->u_regs[UREG_FP] = (unsigned long)childstack; } #endif } /* Set the return value for the child. */ - childregs->u_regs[UREG_I0] = current->pid; - childregs->u_regs[UREG_I1] = 1; + p->tss.kregs->u_regs[UREG_I0] = current->pid; + p->tss.kregs->u_regs[UREG_I1] = 1; - /* Set the return value for the parent. */ + /* Set the second return value for the parent. */ regs->u_regs[UREG_I1] = 0; -#if 0 - printk("CHILD register dump\n"); - show_regs(childregs); - show_regwindow(new_stack); - while(1) - barrier(); -#endif return 0; } @@ -684,5 +598,9 @@ asmlinkage int sparc_execve(struct pt_regs *regs) error = do_execve(filename, (char **) regs->u_regs[base + UREG_I1], (char **) regs->u_regs[base + UREG_I2], regs); putname(filename); + if(!error) { + fprs_write(0); + regs->fprs = 0; + } return error; } diff --git a/arch/sparc64/kernel/ptrace.c b/arch/sparc64/kernel/ptrace.c index 24fe052cde8e..48c5e17ca5bb 100644 --- a/arch/sparc64/kernel/ptrace.c +++ b/arch/sparc64/kernel/ptrace.c @@ -100,7 +100,16 @@ static inline void put_long(struct task_struct * tsk, struct vm_area_struct * vm /* this is a hack for non-kernel-mapped video buffers and similar */ flush_cache_page(vma, addr); if (MAP_NR(page) < max_mapnr) { - *(unsigned long *) (page + (addr & ~PAGE_MASK)) = data; + unsigned long pgaddr; + + pgaddr = page + (addr & ~PAGE_MASK); + *(unsigned long *) (pgaddr) = data; + + __asm__ __volatile__(" + membar #StoreStore + flush %0 +" : : "r" (pgaddr & ~7) : "memory"); + flush_page_to_ram(page); } /* we're bypassing pagetables, so we have to set the dirty bit ourselves */ @@ -138,7 +147,16 @@ static inline void put_int(struct task_struct * tsk, struct vm_area_struct * vma /* this is a hack for non-kernel-mapped video buffers and similar */ flush_cache_page(vma, addr); if (MAP_NR(page) < max_mapnr) { - *(unsigned int *) (page + (addr & ~PAGE_MASK)) = data; + unsigned long pgaddr; + + pgaddr = page + (addr & ~PAGE_MASK); + *(unsigned int *) (pgaddr) = data; + + __asm__ __volatile__(" + membar #StoreStore + flush %0 +" : : "r" (pgaddr & ~7) : "memory"); + flush_page_to_ram(page); } /* we're bypassing pagetables, so we have to set the dirty bit ourselves */ @@ -570,6 +588,21 @@ asmlinkage void do_ptrace(struct pt_regs *regs) pt_error_return(regs, ESRCH); goto out; } + + if(!(child->tss.flags & SPARC_FLAG_32BIT) && + ((request == PTRACE_READDATA64) || + (request == PTRACE_WRITEDATA64) || + (request == PTRACE_READTEXT64) || + (request == PTRACE_WRITETEXT64) || + (request == PTRACE_PEEKTEXT64) || + (request == PTRACE_POKETEXT64) || + (request == PTRACE_PEEKDATA64) || + (request == PTRACE_POKEDATA64))) { + addr = regs->u_regs[UREG_G2]; + addr2 = regs->u_regs[UREG_G3]; + request -= 30; /* wheee... */ + } + switch(request) { case PTRACE_PEEKTEXT: /* read word at location addr. */ case PTRACE_PEEKDATA: { @@ -641,195 +674,207 @@ asmlinkage void do_ptrace(struct pt_regs *regs) goto out; } - case PTRACE_GETREGS: - if (current->tss.flags & SPARC_FLAG_32BIT) { - struct pt_regs32 *pregs = (struct pt_regs32 *) addr; - struct pt_regs *cregs = child->tss.kregs; - int rval; - - if (__put_user(tstate_to_psr(cregs->tstate), (&pregs->psr)) || - __put_user(cregs->tpc, (&pregs->pc)) || - __put_user(cregs->tnpc, (&pregs->npc)) || - __put_user(cregs->y, (&pregs->y))) { + case PTRACE_GETREGS: { + struct pt_regs32 *pregs = (struct pt_regs32 *) addr; + struct pt_regs *cregs = child->tss.kregs; + int rval; + + if (__put_user(tstate_to_psr(cregs->tstate), (&pregs->psr)) || + __put_user(cregs->tpc, (&pregs->pc)) || + __put_user(cregs->tnpc, (&pregs->npc)) || + __put_user(cregs->y, (&pregs->y))) { + pt_error_return(regs, EFAULT); + goto out; + } + for(rval = 1; rval < 16; rval++) + if (__put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1]))) { pt_error_return(regs, EFAULT); goto out; } - for(rval = 1; rval < 16; rval++) - if (__put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1]))) { - pt_error_return(regs, EFAULT); - goto out; - } - pt_succ_return(regs, 0); + pt_succ_return(regs, 0); #ifdef DEBUG_PTRACE - printk ("PC=%lx nPC=%lx o7=%lx\n", cregs->tpc, cregs->tnpc, cregs->u_regs [15]); + printk ("PC=%lx nPC=%lx o7=%lx\n", cregs->tpc, cregs->tnpc, cregs->u_regs [15]); #endif + goto out; + } + + case PTRACE_GETREGS64: { + struct pt_regs *pregs = (struct pt_regs *) addr; + struct pt_regs *cregs = child->tss.kregs; + int rval; + + if (__put_user(cregs->tstate, (&pregs->tstate)) || + __put_user(cregs->tpc, (&pregs->tpc)) || + __put_user(cregs->tnpc, (&pregs->tnpc)) || + __put_user(cregs->y, (&pregs->y))) { + pt_error_return(regs, EFAULT); goto out; - } else { - struct pt_regs *pregs = (struct pt_regs *) addr; - struct pt_regs *cregs = child->tss.kregs; - int rval; - - if (__put_user(cregs->tstate, (&pregs->tstate)) || - __put_user(cregs->tpc, (&pregs->tpc)) || - __put_user(cregs->tnpc, (&pregs->tnpc)) || - __put_user(cregs->y, (&pregs->y))) { + } + for(rval = 1; rval < 16; rval++) + if (__put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1]))) { pt_error_return(regs, EFAULT); goto out; } - for(rval = 1; rval < 16; rval++) - if (__put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1]))) { - pt_error_return(regs, EFAULT); - goto out; - } - pt_succ_return(regs, 0); + pt_succ_return(regs, 0); #ifdef DEBUG_PTRACE - printk ("PC=%lx nPC=%lx o7=%lx\n", cregs->tpc, cregs->tnpc, cregs->u_regs [15]); + printk ("PC=%lx nPC=%lx o7=%lx\n", cregs->tpc, cregs->tnpc, cregs->u_regs [15]); #endif + goto out; + } + + case PTRACE_SETREGS: { + struct pt_regs32 *pregs = (struct pt_regs32 *) addr; + struct pt_regs *cregs = child->tss.kregs; + unsigned int psr, pc, npc, y; + int i; + + /* Must be careful, tracing process can only set certain + * bits in the psr. + */ + if (__get_user(psr, (&pregs->psr)) || + __get_user(pc, (&pregs->pc)) || + __get_user(npc, (&pregs->npc)) || + __get_user(y, (&pregs->y))) { + pt_error_return(regs, EFAULT); goto out; } - - case PTRACE_SETREGS: - if (current->tss.flags & SPARC_FLAG_32BIT) { - struct pt_regs32 *pregs = (struct pt_regs32 *) addr; - struct pt_regs *cregs = child->tss.kregs; - unsigned int psr, pc, npc, y; - int i; - - /* Must be careful, tracing process can only set certain - * bits in the psr. - */ - if (__get_user(psr, (&pregs->psr)) || - __get_user(pc, (&pregs->pc)) || - __get_user(npc, (&pregs->npc)) || - __get_user(y, (&pregs->y))) { + cregs->tstate &= ~(TSTATE_ICC); + cregs->tstate |= psr_to_tstate_icc(psr); + if(!((pc | npc) & 3)) { + cregs->tpc = pc; + cregs->tpc = npc; + } + cregs->y = y; + for(i = 1; i < 16; i++) + if (__get_user(cregs->u_regs[i], (&pregs->u_regs[i-1]))) { pt_error_return(regs, EFAULT); goto out; } - cregs->tstate &= ~(TSTATE_ICC); - cregs->tstate |= psr_to_tstate_icc(psr); - if(!((pc | npc) & 3)) { - cregs->tpc = pc; - cregs->tpc = npc; - } - cregs->y = y; - for(i = 1; i < 16; i++) - if (__get_user(cregs->u_regs[i], (&pregs->u_regs[i-1]))) { - pt_error_return(regs, EFAULT); - goto out; - } - pt_succ_return(regs, 0); - goto out; - } else { - struct pt_regs *pregs = (struct pt_regs *) addr; - struct pt_regs *cregs = child->tss.kregs; - unsigned long tstate, tpc, tnpc, y; - int i; + pt_succ_return(regs, 0); + goto out; + } - /* Must be careful, tracing process can only set certain - * bits in the psr. - */ - if (__get_user(tstate, (&pregs->tstate)) || - __get_user(tpc, (&pregs->tpc)) || - __get_user(tnpc, (&pregs->tnpc)) || - __get_user(y, (&pregs->y))) { - pt_error_return(regs, EFAULT); - goto out; - } - tstate &= (TSTATE_ICC | TSTATE_XCC); - cregs->tstate &= ~(TSTATE_ICC | TSTATE_XCC); - cregs->tstate |= tstate; - if(!((tpc | tnpc) & 3)) { - cregs->tpc = tpc; - cregs->tnpc = tnpc; - } - cregs->y = y; - for(i = 1; i < 16; i++) - if (__get_user(cregs->u_regs[i], (&pregs->u_regs[i-1]))) { - pt_error_return(regs, EFAULT); - goto out; - } - pt_succ_return(regs, 0); + case PTRACE_SETREGS64: { + struct pt_regs *pregs = (struct pt_regs *) addr; + struct pt_regs *cregs = child->tss.kregs; + unsigned long tstate, tpc, tnpc, y; + int i; + + /* Must be careful, tracing process can only set certain + * bits in the psr. + */ + if (__get_user(tstate, (&pregs->tstate)) || + __get_user(tpc, (&pregs->tpc)) || + __get_user(tnpc, (&pregs->tnpc)) || + __get_user(y, (&pregs->y))) { + pt_error_return(regs, EFAULT); goto out; } - - case PTRACE_GETFPREGS: - if (current->tss.flags & SPARC_FLAG_32BIT) { - struct fps { - unsigned int regs[32]; - unsigned int fsr; - unsigned int flags; - unsigned int extra; - unsigned int fpqd; - struct fq { - unsigned int insnaddr; - unsigned int insn; - } fpq[16]; - } *fps = (struct fps *) addr; - - if (copy_to_user(&fps->regs[0], &child->tss.float_regs[0], (32 * sizeof(unsigned int))) || - __put_user(child->tss.fsr, (&fps->fsr)) || - __put_user(0, (&fps->fpqd)) || - __put_user(0, (&fps->flags)) || - __put_user(0, (&fps->extra)) || - clear_user(&fps->fpq[0], 32 * sizeof(unsigned int))) { + tstate &= (TSTATE_ICC | TSTATE_XCC); + cregs->tstate &= ~(TSTATE_ICC | TSTATE_XCC); + cregs->tstate |= tstate; + if(!((tpc | tnpc) & 3)) { + cregs->tpc = tpc; + cregs->tnpc = tnpc; + } + cregs->y = y; + for(i = 1; i < 16; i++) + if (__get_user(cregs->u_regs[i], (&pregs->u_regs[i-1]))) { pt_error_return(regs, EFAULT); goto out; } - pt_succ_return(regs, 0); + pt_succ_return(regs, 0); + goto out; + } + + case PTRACE_GETFPREGS: { + struct fps { + unsigned int regs[32]; + unsigned int fsr; + unsigned int flags; + unsigned int extra; + unsigned int fpqd; + struct fq { + unsigned int insnaddr; + unsigned int insn; + } fpq[16]; + } *fps = (struct fps *) addr; + unsigned long *fpregs = (unsigned long *)(child->tss.kregs+1); + + if (copy_to_user(&fps->regs[0], fpregs, + (32 * sizeof(unsigned int))) || + __put_user(((unsigned int)fpregs[32]), (&fps->fsr)) || + __put_user(0, (&fps->fpqd)) || + __put_user(0, (&fps->flags)) || + __put_user(0, (&fps->extra)) || + clear_user(&fps->fpq[0], 32 * sizeof(unsigned int))) { + pt_error_return(regs, EFAULT); goto out; - } else { - struct fps { - unsigned int regs[64]; - unsigned long fsr; - } *fps = (struct fps *) addr; + } + pt_succ_return(regs, 0); + goto out; + } - if (copy_to_user(&fps->regs[0], &child->tss.float_regs[0], (64 * sizeof(unsigned int))) || - __put_user(child->tss.fsr, (&fps->fsr))) { - pt_error_return(regs, EFAULT); - goto out; - } - pt_succ_return(regs, 0); + case PTRACE_GETFPREGS64: { + struct fps { + unsigned int regs[64]; + unsigned long fsr; + } *fps = (struct fps *) addr; + unsigned long *fpregs = (unsigned long *)(child->tss.kregs+1); + + if (copy_to_user(&fps->regs[0], fpregs, + (64 * sizeof(unsigned int))) || + __put_user(fpregs[32], (&fps->fsr))) { + pt_error_return(regs, EFAULT); goto out; } + pt_succ_return(regs, 0); + goto out; + } - case PTRACE_SETFPREGS: - if (current->tss.flags & SPARC_FLAG_32BIT) { - struct fps { - unsigned int regs[32]; - unsigned int fsr; - unsigned int flags; - unsigned int extra; - unsigned int fpqd; - struct fq { - unsigned int insnaddr; - unsigned int insn; - } fpq[16]; - } *fps = (struct fps *) addr; - unsigned fsr; - - if (copy_from_user(&child->tss.float_regs[0], &fps->regs[0], (32 * sizeof(unsigned int))) || - __get_user(fsr, (&fps->fsr))) { - pt_error_return(regs, EFAULT); - goto out; - } - child->tss.fsr &= 0xffffffff00000000UL; - child->tss.fsr |= fsr; - pt_succ_return(regs, 0); + case PTRACE_SETFPREGS: { + struct fps { + unsigned int regs[32]; + unsigned int fsr; + unsigned int flags; + unsigned int extra; + unsigned int fpqd; + struct fq { + unsigned int insnaddr; + unsigned int insn; + } fpq[16]; + } *fps = (struct fps *) addr; + unsigned long *fpregs = (unsigned long *)(child->tss.kregs+1); + unsigned fsr; + + if (copy_from_user(fpregs, &fps->regs[0], + (32 * sizeof(unsigned int))) || + __get_user(fsr, (&fps->fsr))) { + pt_error_return(regs, EFAULT); goto out; - } else { - struct fps { - unsigned int regs[64]; - unsigned long fsr; - } *fps = (struct fps *) addr; + } + fpregs[32] &= 0xffffffff00000000UL; + fpregs[32] |= fsr; + pt_succ_return(regs, 0); + goto out; + } - if (copy_from_user(&child->tss.float_regs[0], &fps->regs[0], (64 * sizeof(unsigned int))) || - __get_user(child->tss.fsr, (&fps->fsr))) { - pt_error_return(regs, EFAULT); - goto out; - } - pt_succ_return(regs, 0); + case PTRACE_SETFPREGS64: { + struct fps { + unsigned int regs[64]; + unsigned long fsr; + } *fps = (struct fps *) addr; + unsigned long *fpregs = (unsigned long *)(child->tss.kregs+1); + + if (copy_from_user(fpregs, &fps->regs[0], + (64 * sizeof(unsigned int))) || + __get_user(fpregs[32], (&fps->fsr))) { + pt_error_return(regs, EFAULT); goto out; } + pt_succ_return(regs, 0); + goto out; + } case PTRACE_READTEXT: case PTRACE_READDATA: { @@ -1022,7 +1067,10 @@ asmlinkage void syscall_trace(void) current->pid, current->exit_code); #endif if (current->exit_code) { - set_bit(current->exit_code + 31, ¤t->signal); + /* spin_lock_irq(¤t->sigmask_lock); */ + current->signal |= (1 << (current->exit_code - 1)); + /* spin_unlock_irq(¤t->sigmask_lock); */ } + current->exit_code = 0; } diff --git a/arch/sparc64/kernel/rtrap.S b/arch/sparc64/kernel/rtrap.S index 36999d2a055e..9f087a969a3f 100644 --- a/arch/sparc64/kernel/rtrap.S +++ b/arch/sparc64/kernel/rtrap.S @@ -1,4 +1,4 @@ -/* $Id: rtrap.S,v 1.23 1997/06/16 07:38:41 davem Exp $ +/* $Id: rtrap.S,v 1.28 1997/06/30 10:31:39 jj Exp $ * rtrap.S: Preparing for return from trap on Sparc V9. * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -11,121 +11,124 @@ #include #include - /* We assume here that this is entered with AG, MG and IG bits - * in pstate clear. - */ + .text + .align 32 + .globl rtrap_clr_l6, rtrap +#define PTREGS_OFF (STACK_BIAS + REGWIN_SZ) +rtrap_clr_l6: ba,pt %xcc, rtrap + clr %l6 +rtrap: sethi %hi(bh_active), %l2 + sethi %hi(bh_mask), %l1 + ldx [%l2 + %lo(bh_active)], %l4 + ldx [%l1 + %lo(bh_mask)], %l7 - .text - .align 32 - .globl rtrap_clr_l6, rtrap -rtrap_clr_l6: - ba,pt %xcc, rtrap - clr %l6 -rtrap: sethi %hi(bh_active), %l2 - or %l2, %lo(bh_active), %l2 - sethi %hi(bh_mask), %l1 - or %l1, %lo(bh_mask), %l1 - ldx [%l2 + %g4], %l4 - ldx [%l1 + %g4], %l7 + andcc %l4, %l7, %g0 + be,pt %xcc, 2f + nop + call do_bottom_half + nop +2: ldx [%sp + PTREGS_OFF + PT_V9_TSTATE], %l1 + sethi %hi(0xf << 20), %l4 + andcc %l1, TSTATE_PRIV, %l3 - andcc %l4, %l7, %g0 - be,pt %xcc, 2f - nop - call do_bottom_half - nop -2: ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TSTATE], %l1 - sethi %hi(0xf << 20), %l4 - andcc %l1, TSTATE_PRIV, %l3 + and %l1, %l4, %l4 + rdpr %pstate, %l7 + andn %l1, %l4, %l1 + be,pt %icc, to_user + andn %l7, PSTATE_IE, %l7 +rt_continue: ld [%sp + PTREGS_OFF + PT_V9_FPRS], %l2 + ld [%g6 + AOFF_task_tss + AOFF_thread_ctx], %l0 + ldx [%sp + PTREGS_OFF + PT_V9_G1], %g1 - and %l1, %l4, %l4 - rdpr %pstate, %l7 - andn %l1, %l4, %l1 - be,pt %icc, to_user - andn %l7, PSTATE_IE, %l7 -3: ldx [%g6 + AOFF_task_tss + AOFF_thread_ctx], %l0 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G1], %g1 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G2], %g2 + brnz,pn %l2, rt_fpu_restore + ldx [%sp + PTREGS_OFF + PT_V9_G2], %g2 +rt_after_fpu: ldx [%sp + PTREGS_OFF + PT_V9_G3], %g3 + mov %g6, %l6 + ldx [%sp + PTREGS_OFF + PT_V9_G4], %g4 + ldx [%sp + PTREGS_OFF + PT_V9_G5], %g5 + ldx [%sp + PTREGS_OFF + PT_V9_G6], %g6 + ldx [%sp + PTREGS_OFF + PT_V9_G7], %g7 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G3], %g3 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G4], %g4 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G5], %g5 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G6], %g6 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G7], %g7 - wrpr %l7, PSTATE_AG, %pstate - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0], %i0 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I1], %i1 + wrpr %l7, PSTATE_AG, %pstate + ldx [%sp + PTREGS_OFF + PT_V9_I0], %i0 + ldx [%sp + PTREGS_OFF + PT_V9_I1], %i1 + ldx [%sp + PTREGS_OFF + PT_V9_I2], %i2 + ldx [%sp + PTREGS_OFF + PT_V9_I3], %i3 + ldx [%sp + PTREGS_OFF + PT_V9_I4], %i4 + ldx [%sp + PTREGS_OFF + PT_V9_I5], %i5 + ldx [%sp + PTREGS_OFF + PT_V9_I6], %i6 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I2], %i2 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I3], %i3 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I4], %i4 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I5], %i5 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I6], %i6 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I7], %i7 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_Y], %o3 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC], %l2 + ldx [%sp + PTREGS_OFF + PT_V9_I7], %i7 + ld [%sp + PTREGS_OFF + PT_V9_Y], %o3 + ldx [%sp + PTREGS_OFF + PT_V9_TPC], %l2 + ldx [%sp + PTREGS_OFF + PT_V9_TNPC], %o2 + wr %o3, %g0, %y + srl %l4, 20, %l4 + wrpr %l4, 0x0, %pil + wrpr %g0, 0x1, %tl - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC], %o2 - wr %o3, %g0, %y - srl %l4, 20, %l4 - wrpr %l4, 0x0, %pil - wrpr %g0, 0x1, %tl - wrpr %l1, %g0, %tstate - wrpr %l2, %g0, %tpc - mov PRIMARY_CONTEXT, %l7 + wrpr %l1, %g0, %tstate + wrpr %l2, %g0, %tpc + mov PRIMARY_CONTEXT, %l7 + brnz,pn %l3, kern_rtt + wrpr %o2, %g0, %tnpc + stxa %l0, [%l7] ASI_DMMU + flush %l6 + rdpr %wstate, %l1 - wrpr %o2, %g0, %tnpc - brnz,a,pn %l3, 1f - restore - sethi %uhi(KERNBASE), %l5 - sllx %l5, 32, %l5 - stxa %l0, [%l7] ASI_DMMU - flush %l5 - rdpr %wstate, %l1 + rdpr %otherwin, %l2 + srl %l1, 3, %l1 + wrpr %l2, %g0, %canrestore + wrpr %l1, %g0, %wstate + wrpr %g0, %g0, %otherwin + restore + rdpr %canrestore, %g1 + wrpr %g1, 0x0, %cleanwin - rdpr %otherwin, %l2 - srl %l1, 3, %l1 - wrpr %l2, %g0, %canrestore - wrpr %l1, %g0, %wstate - wrpr %g0, %g0, %otherwin - restore - rdpr %canrestore, %g1 - wrpr %g1, 0x0, %cleanwin + retry +kern_rtt: restore + retry +to_user: sethi %hi(need_resched), %l0 + ld [%l0 + %lo(need_resched)], %l0 + wrpr %l7, PSTATE_IE, %pstate + brz,pt %l0, check_signal + ldx [%g6 + AOFF_task_signal], %l0 -1: retry -to_user: - sethi %hi(need_resched), %l0 - or %l0, %lo(need_resched), %l0 - ld [%l0 + %g4], %l0 - wrpr %l7, PSTATE_IE, %pstate - brz,pt %l0, check_signal - ldx [%g6 + AOFF_task_signal], %l0 - nop + call schedule + nop + ldx [%g6 + AOFF_task_signal], %l0 + nop +check_signal: ldx [%g6 + AOFF_task_blocked], %o0 + andncc %l0, %o0, %g0 + be,pt %xcc, check_user_wins + ldx [%g6 + AOFF_task_tss + AOFF_thread_w_saved], %o2 - call schedule - nop - ba,pt %xcc, check_signal - ldx [%g6 + AOFF_task_signal], %l0 -check_signal: - ldx [%g6 + AOFF_task_blocked], %o0 - andncc %l0, %o0, %g0 - be,a,pt %xcc, check_user_wins - ldx [%g6 + AOFF_task_tss + AOFF_thread_w_saved], %o2 - - mov %l5, %o2 - mov %l6, %o3 - call do_signal - add %sp, STACK_BIAS + REGWIN_SZ, %o1 - ldx [%g6 + AOFF_task_tss + AOFF_thread_w_saved], %o2 - clr %l6 + mov %l5, %o2 + mov %l6, %o3 + call do_signal + add %sp, STACK_BIAS + REGWIN_SZ, %o1 + ldx [%g6 + AOFF_task_tss + AOFF_thread_w_saved], %o2 + clr %l6 check_user_wins: - brz,pt %o2, 3b - nop + brz,pt %o2, rt_continue + nop + + call fault_in_user_windows + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + ba,a,pt %xcc, rt_continue +rt_fpu_restore: wr %g0, FPRS_FEF, %fprs + add %sp, PTREGS_OFF + TRACEREG_SZ, %g4 + wr %g0, ASI_BLK_P, %asi + + membar #StoreLoad | #LoadLoad + ldda [%g4 + 0x000] %asi, %f0 + ldda [%g4 + 0x040] %asi, %f16 + ldda [%g4 + 0x080] %asi, %f32 + ldda [%g4 + 0x0c0] %asi, %f48 + ldx [%g4 + 0x100], %fsr + ldx [%g4 + 0x108], %g3 + membar #Sync - call fault_in_user_windows - add %sp, STACK_BIAS + REGWIN_SZ, %o0 - ba,a,pt %xcc, 3b - nop - nop - nop - nop - nop + b,pt %xcc, rt_after_fpu + wr %g3, 0, %gsr +#undef PTREGS_OFF diff --git a/arch/sparc64/kernel/setup.c b/arch/sparc64/kernel/setup.c index 832d3b97f9c7..096d93f5a2f9 100644 --- a/arch/sparc64/kernel/setup.c +++ b/arch/sparc64/kernel/setup.c @@ -1,4 +1,4 @@ -/* $Id: setup.c,v 1.7 1997/05/20 07:58:56 jj Exp $ +/* $Id: setup.c,v 1.9 1997/07/05 09:52:29 davem Exp $ * linux/arch/sparc64/kernel/setup.c * * Copyright (C) 1995,1996 David S. Miller (davem@caip.rutgers.edu) @@ -62,7 +62,6 @@ unsigned long bios32_init(unsigned long memory_start, unsigned long memory_end) */ extern unsigned long sparc64_ttable_tl0; -extern void breakpoint(void); #if CONFIG_SUN_CONSOLE extern void console_restore_palette(void); #endif @@ -108,23 +107,13 @@ static int console_fb = 0; #endif static unsigned long memory_size = 0; +/* XXX Implement this at some point... */ void kernel_enter_debugger(void) { -#if 0 - if (boot_flags & BOOTME_KGDB) { - printk("KGDB: Entered\n"); - breakpoint(); - } -#endif } int obp_system_intr(void) { - if (boot_flags & BOOTME_KGDB) { - printk("KGDB: system interrupted\n"); - breakpoint(); - return 1; - } if (boot_flags & BOOTME_DEBUG) { printk("OBP: system interrupted\n"); prom_halt(); @@ -148,7 +137,7 @@ __initfunc(static void process_switch(char c)) break; case 'h': prom_printf("boot_flags_init: Halt!\n"); - halt(); + prom_halt(); break; default: printk("Unknown boot switch (-%c)\n", c); @@ -266,23 +255,9 @@ __initfunc(void setup_arch(char **cmdline_p, *cmdline_p = prom_getbootargs(); strcpy(saved_command_line, *cmdline_p); - prom_printf("BOOT: args[%s] saved[%s]\n", *cmdline_p, saved_command_line); - printk("ARCH: SUN4U\n"); boot_flags_init(*cmdline_p); -#if 0 - if((boot_flags&BOOTME_DEBUG) && (linux_dbvec!=0) && - ((*(short *)linux_dbvec) != -1)) { - printk("Booted under KADB. Syncing trap table.\n"); - (*(linux_dbvec->teach_debugger))(); - } - if((boot_flags & BOOTME_KGDB)) { - set_debug_traps(); - prom_printf ("Breakpoint!\n"); - breakpoint(); - } -#endif idprom_init(); total = prom_probe_memory(); @@ -424,7 +399,11 @@ extern char *mmu_info(void); int get_cpuinfo(char *buffer) { - int cpuid=get_cpuid(); +#ifndef __SMP__ + int cpuid=0; +#else +#error SMP not supported on sparc64 yet +#endif return sprintf(buffer, "cpu\t\t: %s\n" "fpu\t\t: %s\n" diff --git a/arch/sparc64/kernel/signal.c b/arch/sparc64/kernel/signal.c index f1e02382bf4a..c0f0714962b8 100644 --- a/arch/sparc64/kernel/signal.c +++ b/arch/sparc64/kernel/signal.c @@ -1,4 +1,4 @@ -/* $Id: signal.c,v 1.7 1997/06/16 06:49:59 davem Exp $ +/* $Id: signal.c,v 1.17 1997/07/05 09:52:31 davem Exp $ * arch/sparc64/kernel/signal.c * * Copyright (C) 1991, 1992 Linus Torvalds @@ -8,6 +8,7 @@ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ +#include #include #include #include @@ -23,6 +24,7 @@ #include #include #include +#include #include #define _S(nr) (1<<((nr)-1)) @@ -38,6 +40,124 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs, /* This turned off for production... */ /* #define DEBUG_SIGNALS 1 */ +/* {set, get}context() needed for 64-bit SparcLinux userland. */ +asmlinkage void sparc64_set_context(struct pt_regs *regs) +{ + struct ucontext *ucp = (struct ucontext *) regs->u_regs[UREG_I0]; + struct thread_struct *tp = ¤t->tss; + mc_gregset_t *grp; + unsigned long pc, npc, tstate; + unsigned long fp, i7; + unsigned char fenab; + + __asm__ __volatile__("flushw"); + if(tp->w_saved || + (((unsigned long)ucp) & (sizeof(unsigned long)-1)) || + (!__access_ok((unsigned long)ucp, sizeof(*ucp)))) + do_exit(SIGSEGV); + grp = &ucp->uc_mcontext.mc_gregs; + __get_user(pc, &((*grp)[MC_PC])); + __get_user(npc, &((*grp)[MC_NPC])); + if((pc | npc) & 3) + do_exit(SIGSEGV); + if(regs->u_regs[UREG_I1]) { + __get_user(current->blocked, &ucp->uc_sigmask); + current->blocked &= _BLOCKABLE; + } + regs->tpc = pc; + regs->tnpc = npc; + __get_user(regs->y, &((*grp)[MC_Y])); + __get_user(tstate, &((*grp)[MC_Y])); + regs->tstate &= ~(TSTATE_ICC | TSTATE_XCC); + regs->tstate |= (tstate & (TSTATE_ICC | TSTATE_XCC)); + __get_user(regs->u_regs[UREG_G1], (&(*grp)[MC_G1])); + __get_user(regs->u_regs[UREG_G2], (&(*grp)[MC_G2])); + __get_user(regs->u_regs[UREG_G3], (&(*grp)[MC_G3])); + __get_user(regs->u_regs[UREG_G4], (&(*grp)[MC_G4])); + __get_user(regs->u_regs[UREG_G5], (&(*grp)[MC_G5])); + __get_user(regs->u_regs[UREG_G6], (&(*grp)[MC_G6])); + __get_user(regs->u_regs[UREG_G7], (&(*grp)[MC_G7])); + __get_user(regs->u_regs[UREG_I0], (&(*grp)[MC_O0])); + __get_user(regs->u_regs[UREG_I1], (&(*grp)[MC_O1])); + __get_user(regs->u_regs[UREG_I2], (&(*grp)[MC_O2])); + __get_user(regs->u_regs[UREG_I3], (&(*grp)[MC_O3])); + __get_user(regs->u_regs[UREG_I4], (&(*grp)[MC_O4])); + __get_user(regs->u_regs[UREG_I5], (&(*grp)[MC_O5])); + __get_user(regs->u_regs[UREG_I6], (&(*grp)[MC_O6])); + __get_user(regs->u_regs[UREG_I7], (&(*grp)[MC_O7])); + + __get_user(fp, &(ucp->uc_mcontext.mc_fp)); + __get_user(i7, &(ucp->uc_mcontext.mc_i7)); + __put_user(fp, (&(((struct reg_window *)(STACK_BIAS+regs->u_regs[UREG_I6]))->ins[6]))); + __put_user(i7, (&(((struct reg_window *)(STACK_BIAS+regs->u_regs[UREG_I6]))->ins[7]))); + + __get_user(fenab, &(ucp->uc_mcontext.mc_fpregs.mcfpu_enab)); + if(fenab) { + unsigned long *fpregs = (unsigned long *)(regs+1); + copy_from_user(fpregs, &(ucp->uc_mcontext.mc_fpregs.mcfpu_fregs), + (sizeof(unsigned long) * 32)); + __get_user(fpregs[32], &(ucp->uc_mcontext.mc_fpregs.mcfpu_fsr)); + __get_user(fpregs[33], &(ucp->uc_mcontext.mc_fpregs.mcfpu_gsr)); + regs->fprs = FPRS_FEF; + } +} + +asmlinkage void sparc64_get_context(struct pt_regs *regs) +{ + struct ucontext *ucp = (struct ucontext *) regs->u_regs[UREG_I0]; + struct thread_struct *tp = ¤t->tss; + mc_gregset_t *grp; + mcontext_t *mcp; + unsigned long fp, i7; + unsigned char fenab = (current->flags & PF_USEDFPU); + + synchronize_user_stack(); + if(tp->w_saved || clear_user(ucp, sizeof(*ucp))) + do_exit(SIGSEGV); + mcp = &ucp->uc_mcontext; + grp = &mcp->mc_gregs; + + /* Skip over the trap instruction, first. */ + regs->tpc = regs->tnpc; + regs->tnpc += 4; + + __put_user(current->blocked, &ucp->uc_sigmask); + __put_user(regs->tstate, &((*grp)[MC_TSTATE])); + __put_user(regs->tpc, &((*grp)[MC_PC])); + __put_user(regs->tnpc, &((*grp)[MC_NPC])); + __put_user(regs->y, &((*grp)[MC_Y])); + __put_user(regs->u_regs[UREG_G1], &((*grp)[MC_G1])); + __put_user(regs->u_regs[UREG_G2], &((*grp)[MC_G2])); + __put_user(regs->u_regs[UREG_G3], &((*grp)[MC_G3])); + __put_user(regs->u_regs[UREG_G4], &((*grp)[MC_G4])); + __put_user(regs->u_regs[UREG_G5], &((*grp)[MC_G5])); + __put_user(regs->u_regs[UREG_G6], &((*grp)[MC_G6])); + __put_user(regs->u_regs[UREG_G6], &((*grp)[MC_G7])); + __put_user(regs->u_regs[UREG_I0], &((*grp)[MC_O0])); + __put_user(regs->u_regs[UREG_I1], &((*grp)[MC_O1])); + __put_user(regs->u_regs[UREG_I2], &((*grp)[MC_O2])); + __put_user(regs->u_regs[UREG_I3], &((*grp)[MC_O3])); + __put_user(regs->u_regs[UREG_I4], &((*grp)[MC_O4])); + __put_user(regs->u_regs[UREG_I5], &((*grp)[MC_O5])); + __put_user(regs->u_regs[UREG_I6], &((*grp)[MC_O6])); + __put_user(regs->u_regs[UREG_I7], &((*grp)[MC_O7])); + + __get_user(fp, (&(((struct reg_window *)(STACK_BIAS+regs->u_regs[UREG_I6]))->ins[6]))); + __get_user(i7, (&(((struct reg_window *)(STACK_BIAS+regs->u_regs[UREG_I6]))->ins[7]))); + __put_user(fp, &(mcp->mc_fp)); + __put_user(i7, &(mcp->mc_i7)); + + __put_user(fenab, &(mcp->mc_fpregs.mcfpu_enab)); + if(fenab) { + unsigned long *fpregs = (unsigned long *)(regs+1); + copy_to_user(&(mcp->mc_fpregs.mcfpu_fregs), fpregs, + (sizeof(unsigned long) * 32)); + __put_user(fpregs[32], &(mcp->mc_fpregs.mcfpu_fsr)); + __put_user(fpregs[33], &(mcp->mc_fpregs.mcfpu_gsr)); + __put_user(FPRS_FEF, &(mcp->mc_fpregs.mcfpu_fprs)); + } +} + /* * The new signal frame, intended to be used for Linux applications only * (we have enough in there to work with clone). @@ -65,7 +185,8 @@ asmlinkage void _sigpause_common(unsigned int set, struct pt_regs *regs) #ifdef CONFIG_SPARC32_COMPAT if (current->tss.flags & SPARC_FLAG_32BIT) { - extern asmlinkage void _sigpause32_common(unsigned int, struct pt_regs *); + extern asmlinkage void _sigpause32_common(unsigned int, + struct pt_regs *); _sigpause32_common(set, regs); return; } @@ -111,22 +232,12 @@ asmlinkage void do_sigsuspend(struct pt_regs *regs) static inline void restore_fpu_state(struct pt_regs *regs, __siginfo_fpu_t *fpu) { -#ifdef __SMP__ - if (current->flags & PF_USEDFPU) - regs->tstate &= ~(TSTATE_PEF); -#else - if (current == last_task_used_math) { - last_task_used_math = 0; - regs->tstate &= ~(TSTATE_PEF); - } -#endif - current->used_math = 1; - current->flags &= ~PF_USEDFPU; - - copy_from_user(¤t->tss.float_regs[0], - &fpu->si_float_regs[0], + unsigned long *fpregs = (unsigned long *)(regs+1); + copy_from_user(fpregs, &fpu->si_float_regs[0], (sizeof(unsigned int) * 64)); - __get_user(current->tss.fsr, &fpu->si_fsr); + __get_user(fpregs[32], &fpu->si_fsr); + __get_user(fpregs[33], &fpu->si_gsr); + regs->fprs = FPRS_FEF; } void do_sigreturn(struct pt_regs *regs) @@ -139,8 +250,7 @@ void do_sigreturn(struct pt_regs *regs) #ifdef CONFIG_SPARC32_COMPAT if (current->tss.flags & SPARC_FLAG_32BIT) { extern asmlinkage void do_sigreturn32(struct pt_regs *); - do_sigreturn32(regs); - return; + return do_sigreturn32(regs); } #endif synchronize_user_stack (); @@ -167,9 +277,9 @@ void do_sigreturn(struct pt_regs *regs) __get_user(tstate, &sf->info.si_regs.tstate); copy_from_user(regs->u_regs, sf->info.si_regs.u_regs, sizeof(regs->u_regs)); - /* User can only change condition codes and FPU enabling in %tstate. */ - regs->tstate &= ~(TSTATE_ICC | TSTATE_PEF); - regs->tstate |= (tstate & (TSTATE_ICC | TSTATE_PEF)); + /* User can only change condition codes in %tstate. */ + regs->tstate &= ~(TSTATE_ICC); + regs->tstate |= (tstate & TSTATE_ICC); __get_user(fpu_save, &sf->fpu_save); if (fpu_save) @@ -193,27 +303,12 @@ static int invalid_frame_pointer(void *fp, int fplen) static inline void save_fpu_state(struct pt_regs *regs, __siginfo_fpu_t *fpu) { -#ifdef __SMP__ - if (current->flags & PF_USEDFPU) { - fprs_write(FPRS_FEF); - fpsave((unsigned long *)¤t->tss.float_regs[0], - ¤t->tss.fsr); - regs->tstate &= ~(TSTATE_PEF); - current->flags &= ~(PF_USEDFPU); - } -#else - if (current == last_task_used_math) { - fprs_write(FPRS_FEF); - fpsave((unsigned long *)¤t->tss.float_regs[0], - ¤t->tss.fsr); - last_task_used_math = 0; - regs->tstate &= ~(TSTATE_PEF); - } -#endif - copy_to_user(&fpu->si_float_regs[0], ¤t->tss.float_regs[0], + unsigned long *fpregs = (unsigned long *)(regs+1); + copy_to_user(&fpu->si_float_regs[0], fpregs, (sizeof(unsigned int) * 64)); - __put_user(current->tss.fsr, &fpu->si_fsr); - current->used_math = 0; + __put_user(fpregs[32], &fpu->si_fsr); + __put_user(fpregs[33], &fpu->si_gsr); + regs->fprs = 0; } static inline void @@ -228,7 +323,7 @@ new_setup_frame(struct sigaction *sa, struct pt_regs *regs, /* 1. Make sure everything is clean */ synchronize_user_stack(); sigframe_size = NF_ALIGNEDSZ; - if (!current->used_math) + if (!(current->flags & PF_USEDFPU)) sigframe_size -= sizeof(__siginfo_fpu_t); sf = (struct new_signal_frame *) @@ -249,7 +344,7 @@ new_setup_frame(struct sigaction *sa, struct pt_regs *regs, /* 2. Save the current process state */ copy_to_user(&sf->info.si_regs, regs, sizeof (*regs)); - if (current->used_math) { + if (current->flags & PF_USEDFPU) { save_fpu_state(regs, &sf->fpu_state); __put_user((u64)&sf->fpu_state, &sf->fpu_save); } else { @@ -277,15 +372,22 @@ new_setup_frame(struct sigaction *sa, struct pt_regs *regs, regs->tnpc = (regs->tpc + 4); /* Flush instruction space. */ - __asm__ __volatile__(" - membar #StoreStore - stxa %%g0, [%0] %2 - stxa %%g0, [%1] %2 - flush %%g4 - " : /* no outputs */ - : "r" (((unsigned long)&(sf->insns[0])) & ~(PAGE_MASK)), - "r" ((((unsigned long)&(sf->insns[0])) & ~(PAGE_MASK)) + PAGE_SIZE), - "i" (ASI_IC_TAG)); + { + unsigned long address = ((unsigned long)&(sf->insns[0])); + pgd_t *pgdp = pgd_offset(current->mm, address); + pmd_t *pmdp = pmd_offset(pgdp, address); + pte_t *ptep = pte_offset(pmdp, address); + + if(pte_present(*ptep)) { + unsigned long page = pte_page(*ptep); + + __asm__ __volatile__(" + membar #StoreStore + flush %0 + %1" + : : "r" (page), "r" (address & (PAGE_SIZE - 1)) + : "memory"); + } + } } static inline void handle_signal(unsigned long signr, struct sigaction *sa, diff --git a/arch/sparc64/kernel/signal32.c b/arch/sparc64/kernel/signal32.c index c0454658b53a..1fe5cbaafb05 100644 --- a/arch/sparc64/kernel/signal32.c +++ b/arch/sparc64/kernel/signal32.c @@ -1,4 +1,4 @@ -/* $Id: signal32.c,v 1.13 1997/06/01 05:46:09 davem Exp $ +/* $Id: signal32.c,v 1.23 1997/07/05 07:09:15 davem Exp $ * arch/sparc64/kernel/signal32.c * * Copyright (C) 1991, 1992 Linus Torvalds @@ -67,13 +67,12 @@ struct signal_sframe32 { * (we have enough in there to work with clone). * All the interesting bits are in the info field. */ - struct new_signal_frame32 { struct sparc_stackf32 ss; __siginfo32_t info; /* __siginfo_fpu32_t * */ u32 fpu_save; unsigned int insns [2]; - __siginfo_fpu32_t fpu_state; + __siginfo_fpu_t fpu_state; }; /* Align macros */ @@ -115,25 +114,12 @@ asmlinkage void _sigpause32_common(unsigned int set, struct pt_regs *regs) } } -static inline void -restore_fpu_state32(struct pt_regs *regs, __siginfo_fpu32_t *fpu) +static inline void restore_fpu_state32(struct pt_regs *regs, __siginfo_fpu_t *fpu) { -#ifdef __SMP__ - if (current->flags & PF_USEDFPU) - regs->tstate &= ~(TSTATE_PEF); -#else - if (current == last_task_used_math) { - last_task_used_math = 0; - regs->tstate &= ~(TSTATE_PEF); - } -#endif - current->used_math = 1; - current->flags &= ~PF_USEDFPU; - - copy_from_user(¤t->tss.float_regs[0], - &fpu->si_float_regs[0], - (sizeof(unsigned int) * 32)); - __get_user(current->tss.fsr, &fpu->si_fsr); + unsigned long *fpregs = (unsigned long *)(regs + 1); + copy_from_user(fpregs, &fpu->si_float_regs[0], (sizeof(unsigned int) * 64)); + __get_user(fpregs[32], &fpu->si_fsr); + __get_user(fpregs[33], &fpu->si_gsr); } void do_new_sigreturn32(struct pt_regs *regs) @@ -142,6 +128,7 @@ void do_new_sigreturn32(struct pt_regs *regs) unsigned int psr; unsigned pc, npc, fpu_save, mask; + regs->u_regs[UREG_FP] &= 0x00000000ffffffffUL; sf = (struct new_signal_frame32 *) regs->u_regs [UREG_FP]; /* 1. Make sure we are not getting garbage from the user */ @@ -178,12 +165,12 @@ void do_new_sigreturn32(struct pt_regs *regs) __get_user(regs->u_regs[UREG_I6], &sf->info.si_regs.u_regs[UREG_I6]); __get_user(regs->u_regs[UREG_I7], &sf->info.si_regs.u_regs[UREG_I7]); - /* User can only change condition codes and FPU enabling in %tstate. */ - regs->tstate &= ~(TSTATE_ICC | TSTATE_PEF); + /* User can only change condition codes in %tstate. */ + regs->tstate &= ~(TSTATE_ICC); regs->tstate |= psr_to_tstate_icc(psr); if (psr & PSR_EF) - regs->tstate |= TSTATE_PEF; + regs->fprs = FPRS_FEF; __get_user(fpu_save, &sf->fpu_save); if (fpu_save) @@ -206,7 +193,8 @@ asmlinkage void do_sigreturn32(struct pt_regs *regs) if (current->tss.new_signal) return do_new_sigreturn32(regs); - scptr = (struct sigcontext32 *) regs->u_regs[UREG_I0]; + scptr = (struct sigcontext32 *) + (regs->u_regs[UREG_I0] & 0x00000000ffffffffUL); /* Check sanity of the user arg. */ if(verify_area(VERIFY_READ, scptr, sizeof(struct sigcontext32)) || (((unsigned long) scptr) & 3)) @@ -260,6 +248,7 @@ setup_frame32(struct sigaction *sa, unsigned long pc, unsigned long npc, int i; synchronize_user_stack(); + regs->u_regs[UREG_FP] &= 0x00000000ffffffffUL; sframep = (struct signal_sframe32 *) regs->u_regs[UREG_FP]; sframep = (struct signal_sframe32 *) (((unsigned long) sframep)-SF_ALIGNEDSZ); if (invalid_frame_pointer (sframep, sizeof(*sframep))){ @@ -285,6 +274,8 @@ setup_frame32(struct sigaction *sa, unsigned long pc, unsigned long npc, __put_user(pc, &sc->sigc_pc); __put_user(npc, &sc->sigc_npc); psr = tstate_to_psr (regs->tstate); + if(current->flags & PF_USEDFPU) + psr |= PSR_EF; __put_user(psr, &sc->sigc_psr); __put_user(regs->u_regs[UREG_G1], &sc->sigc_g1); __put_user(regs->u_regs[UREG_I0], &sc->sigc_o0); @@ -329,35 +320,17 @@ setup_frame32(struct sigaction *sa, unsigned long pc, unsigned long npc, } -static inline void -save_fpu_state32(struct pt_regs *regs, __siginfo_fpu32_t *fpu) +static inline void save_fpu_state32(struct pt_regs *regs, __siginfo_fpu_t *fpu) { -#ifdef __SMP__ - if (current->flags & PF_USEDFPU) { - fprs_write(FPRS_FEF); - fpsave32((unsigned long *)¤t->tss.float_regs[0], - ¤t->tss.fsr); - regs->tstate &= ~(TSTATE_PEF); - current->flags &= ~(PF_USEDFPU); - } -#else - if (current == last_task_used_math) { - fprs_write(FPRS_FEF); - fpsave32((unsigned long *)¤t->tss.float_regs[0], - ¤t->tss.fsr); - last_task_used_math = 0; - regs->tstate &= ~(TSTATE_PEF); - } -#endif - copy_to_user(&fpu->si_float_regs[0], ¤t->tss.float_regs[0], - (sizeof(unsigned int) * 32)); - __put_user(current->tss.fsr, &fpu->si_fsr); - current->used_math = 0; + unsigned long *fpregs = (unsigned long *)(regs+1); + copy_to_user(&fpu->si_float_regs[0], fpregs, (sizeof(unsigned int) * 64)); + __put_user(fpregs[32], &fpu->si_fsr); + __put_user(fpregs[33], &fpu->si_gsr); + regs->fprs = 0; } -static inline void -new_setup_frame32(struct sigaction *sa, struct pt_regs *regs, - int signo, unsigned long oldmask) +static inline void new_setup_frame32(struct sigaction *sa, struct pt_regs *regs, + int signo, unsigned long oldmask) { struct new_signal_frame32 *sf; int sigframe_size; @@ -367,19 +340,26 @@ new_setup_frame32(struct sigaction *sa, struct pt_regs *regs, /* 1. Make sure everything is clean */ synchronize_user_stack(); sigframe_size = NF_ALIGNEDSZ; - if (!current->used_math) - sigframe_size -= sizeof(__siginfo_fpu32_t); + if (!(current->flags & PF_USEDFPU)) + sigframe_size -= sizeof(__siginfo_fpu_t); + regs->u_regs[UREG_FP] &= 0x00000000ffffffffUL; sf = (struct new_signal_frame32 *)(regs->u_regs[UREG_FP] - sigframe_size); if (invalid_frame_pointer (sf, sigframe_size)) { +#ifdef DEBUG_SIGNALS + printk("new_setup_frame32(%s:%d): invalid_frame_pointer(%p, %d)\n", + current->comm, current->pid, sf, sigframe_size); +#endif lock_kernel (); do_exit(SIGILL); } if (current->tss.w_saved != 0) { +#ifdef DEBUG_SIGNALS printk ("%s[%d]: Invalid user stack frame for " "signal delivery.\n", current->comm, current->pid); +#endif lock_kernel (); do_exit (SIGILL); } @@ -389,11 +369,13 @@ new_setup_frame32(struct sigaction *sa, struct pt_regs *regs, __put_user(regs->tnpc, &sf->info.si_regs.npc); __put_user(regs->y, &sf->info.si_regs.y); psr = tstate_to_psr (regs->tstate); + if(current->flags & PF_USEDFPU) + psr |= PSR_EF; __put_user(psr, &sf->info.si_regs.psr); for (i = 0; i < 16; i++) __put_user(regs->u_regs[i], &sf->info.si_regs.u_regs[i]); - if (current->used_math) { + if (psr & PSR_EF) { save_fpu_state32(regs, &sf->fpu_state); __put_user((u64)&sf->fpu_state, &sf->fpu_save); } else { @@ -425,15 +407,22 @@ new_setup_frame32(struct sigaction *sa, struct pt_regs *regs, regs->tnpc = (regs->tpc + 4); /* Flush instruction space. */ - __asm__ __volatile__(" - membar #StoreStore - stxa %%g0, [%0] %2 - stxa %%g0, [%1] %2 - flush %%g4 - " : /* no outputs */ - : "r" (((unsigned long)&(sf->insns[0])) & ~(PAGE_MASK)), - "r" ((((unsigned long)&(sf->insns[0])) & ~(PAGE_MASK)) + PAGE_SIZE), - "i" (ASI_IC_TAG)); + { + unsigned long address = ((unsigned long)&(sf->insns[0])); + pgd_t *pgdp = pgd_offset(current->mm, address); + pmd_t *pmdp = pmd_offset(pgdp, address); + pte_t *ptep = pte_offset(pmdp, address); + + if(pte_present(*ptep)) { + unsigned long page = pte_page(*ptep); + + __asm__ __volatile__(" + membar #StoreStore + flush %0 + %1" + : : "r" (page), "r" (address & (PAGE_SIZE - 1)) + : "memory"); + } + } } /* Setup a Solaris stack frame */ @@ -454,6 +443,7 @@ setup_svr4_frame32(struct sigaction *sa, unsigned long pc, unsigned long npc, int i; synchronize_user_stack(); + regs->u_regs[UREG_FP] &= 0x00000000ffffffffUL; sfp = (svr4_signal_frame_t *) regs->u_regs[UREG_FP] - REGWIN_SZ; sfp = (svr4_signal_frame_t *) (((unsigned long) sfp)-SVR4_SF_ALIGNED); @@ -485,6 +475,8 @@ setup_svr4_frame32(struct sigaction *sa, unsigned long pc, unsigned long npc, __put_user(regs->tpc, &((*gr) [SVR4_PC])); __put_user(regs->tnpc, &((*gr) [SVR4_NPC])); psr = tstate_to_psr (regs->tstate); + if(current->flags & PF_USEDFPU) + psr |= PSR_EF; __put_user(psr, &((*gr) [SVR4_PSR])); __put_user(regs->y, &((*gr) [SVR4_Y])); @@ -546,7 +538,8 @@ setup_svr4_frame32(struct sigaction *sa, unsigned long pc, unsigned long npc, #endif /* Arguments passed to signal handler */ if (regs->u_regs [14]){ - struct reg_window32 *rw = (struct reg_window32 *) regs->u_regs [14]; + struct reg_window32 *rw = (struct reg_window32 *) + (regs->u_regs [14] & 0x00000000ffffffffUL); __put_user(signr, &rw->ins [0]); __put_user((u64)si, &rw->ins [1]); @@ -583,7 +576,9 @@ svr4_getcontext32(svr4_ucontext_t *uc, struct pt_regs *regs) /* Store registers */ __put_user(regs->tpc, &uc->mcontext.greg [SVR4_PC]); __put_user(regs->tnpc, &uc->mcontext.greg [SVR4_NPC]); - __put_user(tstate_to_psr(regs->tstate), &uc->mcontext.greg [SVR4_PSR]); + __put_user((tstate_to_psr(regs->tstate) | + ((current->flags & PF_USEDFPU) ? PSR_EF : 0)), + &uc->mcontext.greg [SVR4_PSR]); __put_user(regs->y, &uc->mcontext.greg [SVR4_Y]); /* Copy g [1..7] and o [0..7] registers */ @@ -650,12 +645,14 @@ asmlinkage int svr4_setcontext32(svr4_ucontext_t *c, struct pt_regs *regs) __get_user(psr, &((*gr) [SVR4_PSR])); regs->tstate &= ~(TSTATE_ICC); regs->tstate |= psr_to_tstate_icc(psr); + if(psr & PSR_EF) + regs->fprs = FPRS_FEF; /* Restore g[1..7] and o[0..7] registers */ for (i = 0; i < 7; i++) - __put_user(regs->u_regs[UREG_G1+i], (&(*gr)[SVR4_G1])+i); + __get_user(regs->u_regs[UREG_G1+i], (&(*gr)[SVR4_G1])+i); for (i = 0; i < 8; i++) - __put_user(regs->u_regs[UREG_I0+i], (&(*gr)[SVR4_O0])+i); + __get_user(regs->u_regs[UREG_I0+i], (&(*gr)[SVR4_O0])+i); return -EINTR; } @@ -805,9 +802,10 @@ struct sigstack32 { int cur_status; }; -asmlinkage int -sys32_sigstack(struct sigstack32 *ssptr, struct sigstack32 *ossptr) +asmlinkage int sys32_sigstack(u32 u_ssptr, u32 u_ossptr) { + struct sigstack32 *ssptr = (struct sigstack32 *)((unsigned long)(u_ssptr)); + struct sigstack32 *ossptr = (struct sigstack32 *)((unsigned long)(u_ossptr)); int ret = -EFAULT; lock_kernel(); diff --git a/arch/sparc64/kernel/sparc64_ksyms.c b/arch/sparc64/kernel/sparc64_ksyms.c index 91426c814f21..0a32bc093445 100644 --- a/arch/sparc64/kernel/sparc64_ksyms.c +++ b/arch/sparc64/kernel/sparc64_ksyms.c @@ -1,5 +1,5 @@ -/* $Id: sparc64_ksyms.c,v 1.4 1997/04/14 17:04:43 jj Exp $ - * arch/sparc/kernel/ksyms.c: Sparc specific ksyms support. +/* $Id: sparc64_ksyms.c,v 1.8 1997/07/07 04:58:14 davem Exp $ + * arch/sparc64/kernel/sparc64_ksyms.c: Sparc64 specific ksyms support. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) @@ -38,19 +38,17 @@ struct poll { short revents; }; -extern int svr4_getcontext (svr4_ucontext_t *, struct pt_regs *); -extern int svr4_setcontext (svr4_ucontext_t *, struct pt_regs *); extern unsigned long sunos_mmap(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long); void _sigpause_common (unsigned int set, struct pt_regs *); -extern void __copy_1page(void *, const void *); -extern void *bzero_1page(void *); +extern void *__bzero_1page(void *); extern void *__bzero(void *, size_t); extern void *__memscan_zero(void *, size_t); extern void *__memscan_generic(void *, int, size_t); extern int __memcmp(const void *, const void *, __kernel_size_t); extern int __strncmp(const char *, const char *, __kernel_size_t); extern unsigned int __csum_partial_copy_sparc_generic (const char *, char *); +extern char saved_command_line[]; extern void bcopy (const char *, char *, int); extern int __ashrdi3(int, int); @@ -75,6 +73,8 @@ EXPORT_SYMBOL(klock_info); EXPORT_SYMBOL_PRIVATE(_lock_kernel); EXPORT_SYMBOL_PRIVATE(_unlock_kernel); +EXPORT_SYMBOL_PRIVATE(flushw_user); + EXPORT_SYMBOL(mstk48t02_regs); EXPORT_SYMBOL(request_fast_irq); EXPORT_SYMBOL(sparc_alloc_io); @@ -99,8 +99,6 @@ EXPORT_SYMBOL(dma_chain); #endif /* Solaris/SunOS binary compatibility */ -EXPORT_SYMBOL(svr4_setcontext); -EXPORT_SYMBOL(svr4_getcontext); EXPORT_SYMBOL(_sigpause_common); EXPORT_SYMBOL(sunos_mmap); @@ -119,7 +117,7 @@ EXPORT_SYMBOL(prom_getproplen); EXPORT_SYMBOL(prom_getproperty); EXPORT_SYMBOL(prom_node_has_property); EXPORT_SYMBOL(prom_setprop); -EXPORT_SYMBOL(prom_getbootargs); +EXPORT_SYMBOL(saved_command_line); EXPORT_SYMBOL(prom_getname); EXPORT_SYMBOL(prom_feval); EXPORT_SYMBOL(prom_getstring); @@ -148,10 +146,9 @@ EXPORT_SYMBOL(strstr); EXPORT_SYMBOL(strspn); /* Special internal versions of library functions. */ -EXPORT_SYMBOL(__copy_1page); EXPORT_SYMBOL(__memcpy); EXPORT_SYMBOL(__memset); -EXPORT_SYMBOL(bzero_1page); +EXPORT_SYMBOL(__bzero_1page); EXPORT_SYMBOL(__bzero); EXPORT_SYMBOL(__memscan_zero); EXPORT_SYMBOL(__memscan_generic); diff --git a/arch/sparc64/kernel/sunos_ioctl32.c b/arch/sparc64/kernel/sunos_ioctl32.c new file mode 100644 index 000000000000..8676985bd568 --- /dev/null +++ b/arch/sparc64/kernel/sunos_ioctl32.c @@ -0,0 +1,276 @@ +/* $Id: sunos_ioctl32.c,v 1.2 1997/07/05 07:09:16 davem Exp $ + * sunos_ioctl32.c: SunOS ioctl compatability on sparc64. + * + * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx) + * Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu) + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define A(x) ((unsigned long)x) + +#define SUNOS_NR_OPEN 256 + +struct rtentry32 { + u32 rt_pad1; + struct sockaddr rt_dst; /* target address */ + struct sockaddr rt_gateway; /* gateway addr (RTF_GATEWAY) */ + struct sockaddr rt_genmask; /* target network mask (IP) */ + unsigned short rt_flags; + short rt_pad2; + u32 rt_pad3; + unsigned char rt_tos; + unsigned char rt_class; + short rt_pad4; + short rt_metric; /* +1 for binary compatibility! */ + /* char * */ u32 rt_dev; /* forcing the device at add */ + u32 rt_mtu; /* per route MTU/Window */ + u32 rt_window; /* Window clamping */ + unsigned short rt_irtt; /* Initial RTT */ + +}; + +struct ifmap32 { + u32 mem_start; + u32 mem_end; + unsigned short base_addr; + unsigned char irq; + unsigned char dma; + unsigned char port; +}; + +struct ifreq32 { +#define IFHWADDRLEN 6 +#define IFNAMSIZ 16 + union { + char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */ + } ifr_ifrn; + union { + struct sockaddr ifru_addr; + struct sockaddr ifru_dstaddr; + struct sockaddr ifru_broadaddr; + struct sockaddr ifru_netmask; + struct sockaddr ifru_hwaddr; + short ifru_flags; + int ifru_ivalue; + int ifru_mtu; + struct ifmap32 ifru_map; + char ifru_slave[IFNAMSIZ]; /* Just fits the size */ + __kernel_caddr_t32 ifru_data; + } ifr_ifru; +}; + +struct ifconf32 { + int ifc_len; /* size of buffer */ + __kernel_caddr_t32 ifcbuf; +}; + +extern asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg); + +extern asmlinkage int sys32_ioctl(unsigned int, unsigned int, u32); +extern asmlinkage int sys_setsid(void); + +asmlinkage int sunos_ioctl (int fd, unsigned long cmd, u32 arg) +{ + struct file *filp; + int ret = -EBADF; + + lock_kernel(); + if(fd >= SUNOS_NR_OPEN || !(filp = current->files->fd[fd])) + goto out; + if(cmd == TIOCSETD) { + unsigned long old_fs = get_fs(); + int *p, ntty = N_TTY; + int tmp; + + p = (int *)A(arg); + ret = -EFAULT; + if(get_user(tmp, p)) + goto out; + if(tmp == 2) { + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, cmd, (unsigned long) &ntty); + set_fs(old_fs); + ret = (ret == -EINVAL ? -EOPNOTSUPP : ret); + goto out; + } + } + if(cmd == TIOCNOTTY) { + ret = sys_setsid(); + goto out; + } + switch(cmd) { + case _IOW('r', 10, struct rtentry32): + ret = sys32_ioctl(fd, SIOCADDRT, arg); + goto out; + case _IOW('r', 11, struct rtentry32): + ret = sys32_ioctl(fd, SIOCDELRT, arg); + goto out; + + case _IOW('i', 12, struct ifreq32): + ret = sys32_ioctl(fd, SIOCSIFADDR, arg); + goto out; + case _IOWR('i', 13, struct ifreq32): + ret = sys32_ioctl(fd, SIOCGIFADDR, arg); + goto out; + case _IOW('i', 14, struct ifreq32): + ret = sys32_ioctl(fd, SIOCSIFDSTADDR, arg); + goto out; + case _IOWR('i', 15, struct ifreq32): + ret = sys32_ioctl(fd, SIOCGIFDSTADDR, arg); + goto out; + case _IOW('i', 16, struct ifreq32): + ret = sys32_ioctl(fd, SIOCSIFFLAGS, arg); + goto out; + case _IOWR('i', 17, struct ifreq32): + ret = sys32_ioctl(fd, SIOCGIFFLAGS, arg); + goto out; + case _IOW('i', 18, struct ifreq32): + ret = sys32_ioctl(fd, SIOCSIFMEM, arg); + goto out; + case _IOWR('i', 19, struct ifreq32): + ret = sys32_ioctl(fd, SIOCGIFMEM, arg); + goto out; + + case _IOWR('i', 20, struct ifconf32): + ret = sys32_ioctl(fd, SIOCGIFCONF, arg); + goto out; + + case _IOW('i', 21, struct ifreq): /* SIOCSIFMTU */ + ret = sys_ioctl(fd, SIOCSIFMTU, arg); + goto out; + case _IOWR('i', 22, struct ifreq): /* SIOCGIFMTU */ + ret = sys_ioctl(fd, SIOCGIFMTU, arg); + goto out; + + case _IOWR('i', 23, struct ifreq32): + ret = sys32_ioctl(fd, SIOCGIFBRDADDR, arg); + goto out; + case _IOW('i', 24, struct ifreq32): + ret = sys32_ioctl(fd, SIOCGIFBRDADDR, arg); + goto out; + case _IOWR('i', 25, struct ifreq32): + ret = sys32_ioctl(fd, SIOCGIFNETMASK, arg); + goto out; + case _IOW('i', 26, struct ifreq32): + ret = sys32_ioctl(fd, SIOCSIFNETMASK, arg); + goto out; + case _IOWR('i', 27, struct ifreq32): + ret = sys32_ioctl(fd, SIOCGIFMETRIC, arg); + goto out; + case _IOW('i', 28, struct ifreq32): + ret = sys32_ioctl(fd, SIOCSIFMETRIC, arg); + goto out; + + case _IOW('i', 30, struct arpreq): + ret = sys32_ioctl(fd, SIOCSARP, arg); + goto out; + case _IOWR('i', 31, struct arpreq): + ret = sys32_ioctl(fd, SIOCGARP, arg); + goto out; + case _IOW('i', 32, struct arpreq): + ret = sys32_ioctl(fd, SIOCDARP, arg); + goto out; + + case _IOW('i', 40, struct ifreq32): /* SIOCUPPER */ + case _IOW('i', 41, struct ifreq32): /* SIOCLOWER */ + case _IOW('i', 44, struct ifreq32): /* SIOCSETSYNC */ + case _IOW('i', 45, struct ifreq32): /* SIOCGETSYNC */ + case _IOW('i', 46, struct ifreq32): /* SIOCSSDSTATS */ + case _IOW('i', 47, struct ifreq32): /* SIOCSSESTATS */ + case _IOW('i', 48, struct ifreq32): /* SIOCSPROMISC */ + ret = -EOPNOTSUPP; + goto out; + + case _IOW('i', 49, struct ifreq32): + ret = sys32_ioctl(fd, SIOCADDMULTI, arg); + goto out; + case _IOW('i', 50, struct ifreq32): + ret = sys32_ioctl(fd, SIOCDELMULTI, arg); + goto out; + + /* FDDI interface ioctls, unsupported. */ + + case _IOW('i', 51, struct ifreq32): /* SIOCFDRESET */ + case _IOW('i', 52, struct ifreq32): /* SIOCFDSLEEP */ + case _IOW('i', 53, struct ifreq32): /* SIOCSTRTFMWAR */ + case _IOW('i', 54, struct ifreq32): /* SIOCLDNSTRTFW */ + case _IOW('i', 55, struct ifreq32): /* SIOCGETFDSTAT */ + case _IOW('i', 56, struct ifreq32): /* SIOCFDNMIINT */ + case _IOW('i', 57, struct ifreq32): /* SIOCFDEXUSER */ + case _IOW('i', 58, struct ifreq32): /* SIOCFDGNETMAP */ + case _IOW('i', 59, struct ifreq32): /* SIOCFDGIOCTL */ + printk("FDDI ioctl, returning EOPNOTSUPP\n"); + ret = -EOPNOTSUPP; + goto out; + + case _IOW('t', 125, int): + /* More stupid tty sunos ioctls, just + * say it worked. + */ + ret = 0; + goto out; + + /* Non posix grp */ + case _IOW('t', 118, int): { + int oldval, newval, *ptr; + + cmd = TIOCSPGRP; + ptr = (int *) A(arg); + ret = -EFAULT; + if(get_user(oldval, ptr)) + goto out; + ret = sys32_ioctl(fd, cmd, arg); + __get_user(newval, ptr); + if(newval == -1) { + __put_user(oldval, ptr); + ret = -EIO; + } + if(ret == -ENOTTY) + ret = -EIO; + goto out; + } + + case _IOR('t', 119, int): { + int oldval, newval, *ptr; + + cmd = TIOCGPGRP; + ptr = (int *) A(arg); + ret = -EFAULT; + if(get_user(oldval, ptr)) + goto out; + ret = sys32_ioctl(fd, cmd, arg); + __get_user(newval, ptr); + if(newval == -1) { + __put_user(oldval, ptr); + ret = -EIO; + } + if(ret == -ENOTTY) + ret = -EIO; + goto out; + } + }; + + ret = sys32_ioctl(fd, cmd, arg); + /* so stupid... */ + ret = (ret == -EINVAL ? -EOPNOTSUPP : ret); +out: + unlock_kernel(); + return ret; +} diff --git a/arch/sparc64/kernel/sys32.S b/arch/sparc64/kernel/sys32.S new file mode 100644 index 000000000000..ed14462485ca --- /dev/null +++ b/arch/sparc64/kernel/sys32.S @@ -0,0 +1,427 @@ +/* $Id: sys32.S,v 1.1 1997/06/29 03:38:56 davem Exp $ + * sys32.S: I-cache tricks for 32-bit compatability layer simple + * conversions. + * + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + */ + + .text + + .align 32 + .globl sys32_mmap, sys32_mprotect, sys32_munmap, sys32_msync + .globl sys32_mlock, sys32_munlock, sys32_mremap, sparc32_brk +sys32_mmap: + srl %o0, 0, %o0 ! IEU0 Group + sethi %hi(0xffffffff), %g2 ! IEU1 + srl %o1, 0, %o1 ! IEU0 Group + or %g2, %lo(0xffffffff), %g2 ! IEU1 + srl %o2, 0, %o2 ! IEU0 Group + mov %o7, %g1 ! IEU1 + and %o3, %g2, %o3 ! IEU0 Group + and %o4, %g2, %o4 ! IEU1 + and %o5, %g2, %o5 ! IEU0 Group + call sys_mmap ! CTI Group brk forced + mov %g1, %o7 ! IEU0 Group (regdep) +sys32_mprotect: + srl %o0, 0, %o0 + mov %o7, %g1 + srl %o1, 0, %o1 + srl %o2, 0, %o2 + call sys_mprotect + mov %g1, %o7 +sys32_munmap: + srl %o0, 0, %o0 + mov %o7, %g1 + srl %o1, 0, %o1 + call sys_munmap + mov %g1, %o7 +sparc32_brk: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_brk + mov %g1, %o7 +sys32_msync: + srl %o0, 0, %o0 + mov %o7, %g1 + srl %o1, 0, %o1 + call sys_msync + mov %g1, %o7 +sys32_mlock: + srl %o0, 0, %o0 + mov %o7, %g1 + srl %o1, 0, %o1 + call sys_mlock + mov %g1, %o7 +sys32_munlock: + srl %o0, 0, %o0 + mov %o7, %g1 + srl %o1, 0, %o1 + call sys_munlock + mov %g1, %o7 +sys32_mremap: + srl %o0, 0, %o0 + mov %o7, %g1 + srl %o1, 0, %o1 + srl %o2, 0, %o2 + srl %o3, 0, %o3 + call sys_mremap + mov %g1, %o7 + + .align 32 + .globl sys32_read, sys32_write, sys32_open, sys32_access + .globl sys32_chdir, sys32_lseek, sys32_llseek, sys32_poll + .globl sys32_readlink, sys32_unlink, sys32_rmdir, sys32_symlink + .globl sys32_link, sys32_rename, sys32_truncate, sys32_ftruncate + .globl sys32_chroot, sys32_chmod, sys32_chown, sys32_creat + .globl sys32_mkdir, sys32_mknod, sys32_utimes, sys32_ustat +sys32_read: + srl %o1, 0, %o1 + mov %o7, %g1 + srl %o2, 0, %o2 + call sys_read + mov %g1, %o7 +sys32_write: + srl %o1, 0, %o1 + mov %o7, %g1 + srl %o2, 0, %o2 + call sys_write + mov %g1, %o7 +sys32_open: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_open + mov %g1, %o7 +sys32_access: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_access + mov %g1, %o7 +sys32_chdir: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_chdir + mov %g1, %o7 +sys32_lseek: + sra %o1, 0, %o1 + mov %o7, %g1 + call sys_lseek + mov %g1, %o7 +sys32_llseek: + srl %o1, 0, %o1 + mov %o7, %g1 + srl %o2, 0, %o2 + srl %o3, 0, %o3 + call sys_llseek + mov %g1, %o7 +sys32_poll: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_poll + mov %g1, %o7 +sys32_readlink: + srl %o0, 0, %o0 + mov %o7, %g1 + srl %o1, 0, %o1 + call sys_readlink + mov %g1, %o7 +sys32_unlink: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_unlink + mov %g1, %o7 +sys32_rmdir: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_rmdir + mov %g1, %o7 +sys32_symlink: + srl %o0, 0, %o0 + mov %o7, %g1 + srl %o1, 0, %o1 + call sys_symlink + mov %g1, %o7 +sys32_link: + srl %o0, 0, %o0 + mov %o7, %g1 + srl %o1, 0, %o1 + call sys_link + mov %g1, %o7 +sys32_rename: + srl %o0, 0, %o0 + mov %o7, %g1 + srl %o1, 0, %o1 + call sys_rename + mov %g1, %o7 + nop +sys32_truncate: + srl %o0, 0, %o0 + mov %o7, %g1 + srl %o1, 0, %o1 + call sys_truncate + mov %g1, %o7 +sys32_ftruncate: + srl %o1, 0, %o1 + mov %o7, %g1 + call sys_ftruncate + mov %g1, %o7 +sys32_chroot: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_chroot + mov %g1, %o7 +sys32_chmod: + sll %o1, 16, %o1 + mov %o7, %g1 + srl %o0, 0, %o0 + srl %o1, 16, %o1 + call sys_chmod + mov %g1, %o7 +sys32_chown: + sll %o1, 16, %o1 + mov %o7, %g1 + sll %o2, 16, %o2 + srl %o0, 0, %o0 + srl %o1, 16, %o1 + srl %o2, 16, %o2 + call sys_chown + mov %g1, %o7 +sys32_creat: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_creat + mov %g1, %o7 +sys32_mkdir: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_mkdir + mov %g1, %o7 +sys32_mknod: + sll %o2, 16, %o2 + mov %o7, %g1 + srl %o0, 0, %o0 + srl %o2, 16, %o2 + call sys_mknod + mov %g1, %o7 +sys32_utimes: + srl %o0, 0, %o0 + mov %o7, %g1 + srl %o1, 0, %o1 + call sys_utimes + mov %g1, %o7 +sys32_ustat: + srl %o1, 0, %o1 + mov %o7, %g1 + call sys_ustat + mov %g1, %o7 + + .align 32 + .globl sys32_bind, sys32_accept, sys32_connect, sys32_getsockname + .globl sys32_getpeername, sys32_send, sys32_sendto, sys32_recv + .globl sys32_recvfrom, sys32_setsockopt, sys32_getsockopt +sys32_bind: + srl %o1, 0, %o1 + mov %o7, %g1 + call sys_bind + mov %g1, %o7 +sys32_accept: + srl %o1, 0, %o1 + mov %o7, %g1 + srl %o2, 0, %o2 + call sys_accept + mov %g1, %o7 +sys32_connect: + srl %o1, 0, %o1 + mov %o7, %g1 + call sys_connect + mov %g1, %o7 +sys32_getsockname: + srl %o1, 0, %o1 + mov %o7, %g1 + srl %o2, 0, %o2 + call sys_getsockname + mov %g1, %o7 +sys32_getpeername: + srl %o1, 0, %o1 + mov %o7, %g1 + srl %o2, 0, %o2 + call sys_getpeername + mov %g1, %o7 +sys32_send: + srl %o1, 0, %o1 + mov %o7, %g1 + srl %o2, 0, %o2 + call sys_send + mov %g1, %o7 +sys32_sendto: + srl %o1, 0, %o1 + mov %o7, %g1 + srl %o2, 0, %o2 + srl %o4, 0, %o4 + call sys_sendto + mov %g1, %o7 +sys32_recv: + srl %o1, 0, %o1 + mov %o7, %g1 + srl %o2, 0, %o2 + call sys_recv + mov %g1, %o7 +sys32_recvfrom: + srl %o1, 0, %o1 + mov %o7, %g1 + srl %o2, 0, %o2 + srl %o4, 0, %o4 + srl %o5, 0, %o5 + call sys_recvfrom + mov %g1, %o7 +sys32_setsockopt: + srl %o3, 0, %o3 + mov %o7, %g1 + call sys_setsockopt + mov %g1, %o7 +sys32_getsockopt: + srl %o3, 0, %o3 + mov %o7, %g1 + srl %o4, 0, %o4 + call sys_setsockopt + mov %g1, %o7 + + .align 32 + .globl sys32_gettimeofday, sys32_settimeofday +sys32_gettimeofday: + srl %o0, 0, %o0 + mov %o7, %g1 + srl %o1, 0, %o1 + call sys_gettimeofday + mov %g1, %o7 +sys32_settimeofday: + srl %o0, 0, %o0 + mov %o7, %g1 + srl %o1, 0, %o1 + call sys_settimeofday + mov %g1, %o7 + + .globl sys32_bdflush, sys32_uselib, sys32_umount, sys32_syslog + .globl sys32_personality, sys32_waitpid, sys32_getitimer + .globl sys32_setitimer, sys32_sched_setscheduler + .globl sys32_sched_setparam, sys32_sched_getparam, sys32_signal + .globl sys32_reboot, sys32_acct, sys32_newuname, sys32_olduname + .globl sys32_sethostname, sys32_gethostname, sys32_setdomainname + .globl sys32_time, sys32_swapoff, sys32_swapon, sys32_nfsservctl + .globl sys32_create_module, sys32_init_module, sys32_delete_module +sys32_bdflush: + sra %o1, 0, %o1 + mov %o7, %g1 + call sys_bdflush + mov %g1, %o7 +sys32_uselib: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_uselib + mov %g1, %o7 +sys32_umount: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_umount + mov %g1, %o7 +sys32_syslog: + srl %o1, 0, %o1 + mov %o7, %g1 + call sys_syslog + mov %g1, %o7 +sys32_personality: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_personality + mov %g1, %o7 +sys32_waitpid: + srl %o1, 0, %o1 + mov %o7, %g1 + call sys_waitpid + mov %g1, %o7 +sys32_getitimer: + srl %o1, 0, %o1 + mov %o7, %g1 + call sys_getitimer + mov %g1, %o7 +sys32_setitimer: + srl %o1, 0, %o1 + mov %o7, %g1 + srl %o2, 0, %o2 + call sys_setitimer + mov %g1, %o7 +sys32_sched_setscheduler: + srl %o2, 0, %o2 + mov %o7, %g1 + call sys_sched_setscheduler + mov %g1, %o7 +sys32_sched_setparam: + srl %o1, 0, %o1 + mov %o7, %g1 + call sys_sched_setparam + mov %g1, %o7 +sys32_sched_getparam: + srl %o1, 0, %o1 + mov %o7, %g1 + call sys_sched_getparam + mov %g1, %o7 +sys32_signal: + srl %o1, 0, %o1 + mov %o7, %g1 + call sys_signal + mov %g1, %o7 +sys32_reboot: + srl %o3, 0, %o3 + mov %o7, %g1 + call sys_reboot + mov %g1, %o7 +sys32_acct: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_acct + mov %g1, %o7 +sys32_newuname: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_newuname + mov %g1, %o7 +sys32_olduname: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_olduname + mov %g1, %o7 +sys32_sethostname: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_sethostname + mov %g1, %o7 +sys32_gethostname: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_gethostname + mov %g1, %o7 +sys32_setdomainname: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_setdomainname + mov %g1, %o7 +sys32_time: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_time + mov %g1, %o7 +sys32_swapoff: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_swapoff + mov %g1, %o7 +sys32_swapon: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_swapon + mov %g1, %o7 +sys32_nfsservctl: + srl %o1, 0, %o1 + mov %o7, %g1 + srl %o2, 0, %o2 + call sys_nfsservctl + mov %g1, %o7 diff --git a/arch/sparc64/kernel/sys_sparc.c b/arch/sparc64/kernel/sys_sparc.c index 851d1550c06f..c827df7a1aba 100644 --- a/arch/sparc64/kernel/sys_sparc.c +++ b/arch/sparc64/kernel/sys_sparc.c @@ -1,4 +1,4 @@ -/* $Id: sys_sparc.c,v 1.1 1997/04/09 08:25:18 jj Exp $ +/* $Id: sys_sparc.c,v 1.2 1997/07/05 09:52:34 davem Exp $ * linux/arch/sparc64/kernel/sys_sparc.c * * This file contains various random system calls that @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc64/kernel/sys_sparc32.c index 3ecaee05f92e..3281d2f5837c 100644 --- a/arch/sparc64/kernel/sys_sparc32.c +++ b/arch/sparc64/kernel/sys_sparc32.c @@ -1,4 +1,4 @@ -/* $Id: sys_sparc32.c,v 1.32 1997/06/17 05:36:40 davem Exp $ +/* $Id: sys_sparc32.c,v 1.42 1997/07/05 09:52:36 davem Exp $ * sys_sparc32.c: Conversion between 32bit and 64bit native syscalls. * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -8,6 +8,7 @@ * environment. */ +#include #include #include #include @@ -30,11 +31,13 @@ #include #include #include +#include #include #include #include #include +#include /* As gcc will warn about casting u32 to some ptr, we have to cast it to * unsigned long first, and that's what is A() for. @@ -372,11 +375,12 @@ asmlinkage int sys32_ipc (u32 call, int first, int second, int third, u32 ptr, u switch (version) { case 0: default: { unsigned long raddr; + u32 *uptr = (u32 *) A(((u32)third)); err = sys_shmat (first, (char *)A(ptr), second, &raddr); if (err) goto out; err = -EFAULT; - if(put_user (raddr, ((u32 *)A(third)))) + if(put_user (raddr, uptr)) goto out; err = 0; goto out; @@ -469,32 +473,6 @@ out: return err; } -extern asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len, - unsigned long prot, unsigned long flags, - unsigned long fd, unsigned long off); - -asmlinkage unsigned long sys32_mmap(u32 addr, u32 len, u32 prot, - u32 flags, u32 fd, u32 off) -{ - return sys_mmap((unsigned long)addr, (unsigned long)len, - (unsigned long)prot, (unsigned long)flags, - (unsigned long)fd, (unsigned long)off); -} - -extern asmlinkage int sys_bdflush(int func, long data); - -asmlinkage int sys32_bdflush(int func, s32 data) -{ - return sys_bdflush(func, (long)data); -} - -extern asmlinkage int sys_uselib(const char * library); - -asmlinkage int sys32_uselib(u32 library) -{ - return sys_uselib((const char *)A(library)); -} - static inline int get_flock(struct flock *kfl, struct flock32 *ufl) { if(get_user(kfl->l_type, &ufl->l_type) || @@ -544,55 +522,6 @@ asmlinkage long sys32_fcntl(unsigned int fd, unsigned int cmd, u32 arg) } } -extern asmlinkage int sys_mknod(const char * filename, int mode, dev_t dev); - -asmlinkage int sys32_mknod(u32 filename, int mode, __kernel_dev_t32 dev) -{ - return sys_mknod((const char *)A(filename), mode, dev); -} - -extern asmlinkage int sys_mkdir(const char * pathname, int mode); - -asmlinkage int sys32_mkdir(u32 pathname, int mode) -{ - return sys_mkdir((const char *)A(pathname), mode); -} - -extern asmlinkage int sys_rmdir(const char * pathname); - -asmlinkage int sys32_rmdir(u32 pathname) -{ - return sys_rmdir((const char *)A(pathname)); -} - -extern asmlinkage int sys_unlink(const char * pathname); - -asmlinkage int sys32_unlink(u32 pathname) -{ - return sys_unlink((const char *)A(pathname)); -} - -extern asmlinkage int sys_symlink(const char * oldname, const char * newname); - -asmlinkage int sys32_symlink(u32 oldname, u32 newname) -{ - return sys_symlink((const char *)A(oldname), (const char *)A(newname)); -} - -extern asmlinkage int sys_link(const char * oldname, const char * newname); - -asmlinkage int sys32_link(u32 oldname, u32 newname) -{ - return sys_link((const char *)A(oldname), (const char *)A(newname)); -} - -extern asmlinkage int sys_rename(const char * oldname, const char * newname); - -asmlinkage int sys32_rename(u32 oldname, u32 newname) -{ - return sys_rename((const char *)A(oldname), (const char *)A(newname)); -} - struct dqblk32 { __u32 dqb_bhardlimit; __u32 dqb_bsoftlimit; @@ -701,20 +630,6 @@ asmlinkage int sys32_fstatfs(unsigned int fd, u32 buf) return ret; } -extern asmlinkage int sys_truncate(const char * path, unsigned long length); - -asmlinkage int sys32_truncate(u32 path, u32 length) -{ - return sys_truncate((const char *)A(path), (unsigned long)length); -} - -extern asmlinkage int sys_ftruncate(unsigned int fd, unsigned long length); - -asmlinkage int sys32_ftruncate(unsigned int fd, u32 length) -{ - return sys_ftruncate(fd, (unsigned long)length); -} - extern asmlinkage int sys_utime(char * filename, struct utimbuf * times); asmlinkage int sys32_utime(u32 filename, u32 times) @@ -741,96 +656,6 @@ asmlinkage int sys32_utime(u32 filename, u32 times) return ret; } -extern asmlinkage int sys_utimes(char * filename, struct timeval * utimes); - -asmlinkage int sys32_utimes(u32 filename, u32 utimes) -{ - /* struct timeval is the same :)) */ - return sys_utimes((char *)A(filename), (struct timeval *)A(utimes)); -} - -extern asmlinkage int sys_access(const char * filename, int mode); - -asmlinkage int sys32_access(u32 filename, int mode) -{ - return sys_access((const char *)A(filename), mode); -} - -extern asmlinkage int sys_chdir(const char * filename); - -asmlinkage int sys32_chdir(u32 filename) -{ - return sys_chdir((const char *)A(filename)); -} - -extern asmlinkage int sys_chroot(const char * filename); - -asmlinkage int sys32_chroot(u32 filename) -{ - return sys_chroot((const char *)A(filename)); -} - -extern asmlinkage int sys_chmod(const char * filename, mode_t mode); - -asmlinkage int sys32_chmod(u32 filename, __kernel_mode_t32 mode) -{ - return sys_chmod((const char *)A(filename), mode); -} - -extern asmlinkage int sys_chown(const char * filename, uid_t user, gid_t group); - -asmlinkage int sys32_chown(u32 filename, __kernel_uid_t32 user, __kernel_gid_t32 group) -{ - return sys_chown((const char *)A(filename), user, group); -} - -extern asmlinkage int sys_open(const char * filename,int flags,int mode); - -asmlinkage int sys32_open(u32 filename, int flags, int mode) -{ - return sys_open((const char *)A(filename), flags, mode); -} - -extern asmlinkage int sys_creat(const char * pathname, int mode); - -asmlinkage int sys32_creat(u32 pathname, int mode) -{ - return sys_creat((const char *)A(pathname), mode); -} - -extern asmlinkage long sys_lseek(unsigned int fd, off_t offset, unsigned int origin); - -asmlinkage long sys32_lseek(unsigned int fd, s32 offset, unsigned int origin) -{ - return sys_lseek(fd, (off_t)offset, origin); -} - -extern asmlinkage int sys_llseek(unsigned int fd, unsigned long offset_high, - unsigned long offset_low, - loff_t *result, unsigned int origin); - -asmlinkage int sys32_llseek(unsigned int fd, u32 offset_high, - u32 offset_low, u32 result, unsigned int origin) -{ - /* loff_t is the same :)) */ - return sys_llseek(fd, (unsigned long)offset_high, (unsigned long)offset_low, - (loff_t *)A(result), origin); -} - -extern asmlinkage long sys_read(unsigned int fd, char * buf, unsigned long count); - -asmlinkage long sys32_read(unsigned int fd, u32 buf, u32 count) -{ - return sys_read(fd, (char *)A(buf), (unsigned long)count); -} - -extern asmlinkage long sys_write(unsigned int fd, const char * buf, unsigned long count); - -asmlinkage long sys32_write(unsigned int fd, u32 buf, u32 count) -{ - return sys_write(fd, (const char *)A(buf), (unsigned long)count); -} - struct iovec32 { u32 iov_base; __kernel_size_t32 iov_len; }; typedef long (*IO_fn_t)(struct inode *, struct file *, char *, unsigned long); @@ -1196,13 +1021,6 @@ out: return ret; } -extern asmlinkage int sys_poll(struct pollfd * ufds, unsigned int nfds, int timeout); - -asmlinkage int sys32_poll(u32 ufds, unsigned int nfds, int timeout) -{ - return sys_poll((struct pollfd *)A(ufds), nfds, timeout); -} - static inline int putstat(struct stat32 *ubuf, struct stat *kbuf) { if (put_user (kbuf->st_dev, &ubuf->st_dev) || @@ -1280,13 +1098,6 @@ asmlinkage int sys32_newfstat(unsigned int fd, u32 statbuf) return ret; } -extern asmlinkage int sys_readlink(const char * path, char * buf, int bufsiz); - -asmlinkage int sys32_readlink(u32 path, u32 buf, int bufsiz) -{ - return sys_readlink((const char *)A(path), (char *)A(buf), bufsiz); -} - extern asmlinkage int sys_sysfs(int option, ...); asmlinkage int sys32_sysfs(int option, ...) @@ -1312,21 +1123,6 @@ asmlinkage int sys32_sysfs(int option, ...) return ret; } -extern asmlinkage int sys_ustat(dev_t dev, struct ustat * ubuf); - -asmlinkage int sys32_ustat(dev_t dev, u32 ubuf) -{ - /* ustat is the same :)) */ - return sys_ustat(dev, (struct ustat *)A(ubuf)); -} - -extern asmlinkage int sys_umount(char * name); - -asmlinkage int sys32_umount(u32 name) -{ - return sys_umount((char *)A(name)); -} - struct ncp_mount_data32 { int version; unsigned int ncp_fd; @@ -1485,20 +1281,6 @@ asmlinkage int sys32_mount(u32 dev_name, u32 dir_name, u32 type, u32 new_flags, } } -extern asmlinkage int sys_syslog(int type, char * bug, int count); - -asmlinkage int sys32_syslog(int type, u32 bug, int count) -{ - return sys_syslog(type, (char *)A(bug), count); -} - -extern asmlinkage int sys_personality(unsigned long personality); - -asmlinkage int sys32_personality(u32 personality) -{ - return sys_personality((unsigned long)personality); -} - struct rusage32 { struct timeval ru_utime; struct timeval ru_stime; @@ -1565,13 +1347,6 @@ asmlinkage int sys32_wait4(__kernel_pid_t32 pid, u32 stat_addr, int options, u32 } } -extern asmlinkage int sys_waitpid(pid_t pid,unsigned int * stat_addr, int options); - -asmlinkage int sys32_waitpid(__kernel_pid_t32 pid, u32 stat_addr, int options) -{ - return sys_waitpid(pid, (unsigned int *)A(stat_addr), options); -} - struct sysinfo32 { s32 uptime; u32 loads[3]; @@ -1611,46 +1386,6 @@ asmlinkage int sys32_sysinfo(u32 info) return ret; } -extern asmlinkage int sys_getitimer(int which, struct itimerval *value); - -asmlinkage int sys32_getitimer(int which, u32 value) -{ - /* itimerval is the same :)) */ - return sys_getitimer(which, (struct itimerval *)A(value)); -} - -extern asmlinkage int sys_setitimer(int which, struct itimerval *value, - struct itimerval *ovalue); - -asmlinkage int sys32_setitimer(int which, u32 value, u32 ovalue) -{ - return sys_setitimer(which, (struct itimerval *)A(value), - (struct itimerval *)A(ovalue)); -} - -extern asmlinkage int sys_sched_setscheduler(pid_t pid, int policy, - struct sched_param *param); - -asmlinkage int sys32_sched_setscheduler(__kernel_pid_t32 pid, int policy, u32 param) -{ - /* sched_param is the same :)) */ - return sys_sched_setscheduler(pid, policy, (struct sched_param *)A(param)); -} - -extern asmlinkage int sys_sched_setparam(pid_t pid, struct sched_param *param); - -asmlinkage int sys32_sched_setparam(__kernel_pid_t32 pid, u32 param) -{ - return sys_sched_setparam(pid, (struct sched_param *)A(param)); -} - -extern asmlinkage int sys_sched_getparam(pid_t pid, struct sched_param *param); - -asmlinkage int sys32_sched_getparam(__kernel_pid_t32 pid, u32 param) -{ - return sys_sched_getparam(pid, (struct sched_param *)A(param)); -} - struct timespec32 { s32 tv_sec; s32 tv_nsec; @@ -1726,27 +1461,6 @@ asmlinkage int sys32_sigpending(u32 set) return ret; } -extern asmlinkage unsigned long sys_signal(int signum, __sighandler_t handler); - -asmlinkage unsigned long sys32_signal(int signum, u32 handler) -{ - return sys_signal(signum, (__sighandler_t)A(handler)); -} - -extern asmlinkage int sys_reboot(int magic1, int magic2, int cmd, void * arg); - -asmlinkage int sys32_reboot(int magic1, int magic2, int cmd, u32 arg) -{ - return sys_reboot(magic1, magic2, cmd, (void *)A(arg)); -} - -extern asmlinkage int sys_acct(const char *name); - -asmlinkage int sys32_acct(u32 name) -{ - return sys_acct((const char *)A(name)); -} - extern asmlinkage int sys_setreuid(uid_t ruid, uid_t euid); asmlinkage int sys32_setreuid(__kernel_uid_t32 ruid, __kernel_uid_t32 euid) @@ -1854,42 +1568,6 @@ asmlinkage int sys32_setgroups(int gidsetsize, u32 grouplist) return ret; } -extern asmlinkage int sys_newuname(struct new_utsname * name); - -asmlinkage int sys32_newuname(u32 name) -{ - /* utsname is the same :)) */ - return sys_newuname((struct new_utsname *)A(name)); -} - -extern asmlinkage int sys_olduname(struct oldold_utsname * name); - -asmlinkage int sys32_olduname(u32 name) -{ - return sys_olduname((struct oldold_utsname *)A(name)); -} - -extern asmlinkage int sys_sethostname(char *name, int len); - -asmlinkage int sys32_sethostname(u32 name, int len) -{ - return sys_sethostname((char *)A(name), len); -} - -extern asmlinkage int sys_gethostname(char *name, int len); - -asmlinkage int sys32_gethostname(u32 name, int len) -{ - return sys_gethostname((char *)A(name), len); -} - -extern asmlinkage int sys_setdomainname(char *name, int len); - -asmlinkage int sys32_setdomainname(u32 name, int len) -{ - return sys_setdomainname((char *)A(name), len); -} - #define RLIM_INFINITY32 0x7fffffff #define RESOURCE32(x) ((x > RLIM_INFINITY32) ? RLIM_INFINITY32 : x) @@ -1953,28 +1631,6 @@ asmlinkage int sys32_getrusage(int who, u32 ru) return ret; } -extern asmlinkage int sys_time(int * tloc); - -asmlinkage int sys32_time(u32 tloc) -{ - return sys_time((int *)A(tloc)); -} - -extern asmlinkage int sys_gettimeofday(struct timeval *tv, struct timezone *tz); - -asmlinkage int sys32_gettimeofday(u32 tv, u32 tz) -{ - /* both timeval and timezone are ok :)) */ - return sys_gettimeofday((struct timeval *)A(tv), (struct timezone *)A(tz)); -} - -extern asmlinkage int sys_settimeofday(struct timeval *tv, struct timezone *tz); - -asmlinkage int sys32_settimeofday(u32 tv, u32 tz) -{ - return sys_settimeofday((struct timeval *)A(tv), (struct timezone *)A(tz)); -} - struct timex32 { unsigned int modes; s32 offset; @@ -2046,170 +1702,6 @@ asmlinkage int sys32_adjtimex(u32 txc_p) return ret; } -extern asmlinkage int sys_msync(unsigned long start, size_t len, int flags); - -asmlinkage int sys32_msync(u32 start, __kernel_size_t32 len, int flags) -{ - return sys_msync((unsigned long)start, (size_t)len, flags); -} - -extern asmlinkage int sys_mlock(unsigned long start, size_t len); - -asmlinkage int sys32_mlock(u32 start, __kernel_size_t32 len) -{ - return sys_mlock((unsigned long)start, (size_t)len); -} - -extern asmlinkage int sys_munlock(unsigned long start, size_t len); - -asmlinkage int sys32_munlock(u32 start, __kernel_size_t32 len) -{ - return sys_munlock((unsigned long)start, (size_t)len); -} - -extern asmlinkage unsigned long sys_brk(unsigned long brk); - -asmlinkage unsigned long sparc32_brk(u32 brk) -{ - return sys_brk((unsigned long)brk); -} - -extern asmlinkage int sys_munmap(unsigned long addr, size_t len); - -asmlinkage int sys32_munmap(u32 addr, __kernel_size_t32 len) -{ - return sys_munmap((unsigned long)addr, (size_t)len); -} - -extern asmlinkage int sys_mprotect(unsigned long start, size_t len, unsigned long prot); - -asmlinkage int sys32_mprotect(u32 start, __kernel_size_t32 len, u32 prot) -{ - return sys_mprotect((unsigned long)start, (size_t)len, (unsigned long)prot); -} - -extern asmlinkage unsigned long sys_mremap(unsigned long addr, unsigned long old_len, - unsigned long new_len, unsigned long flags); - -asmlinkage unsigned long sys32_mremap(u32 addr, u32 old_len, u32 new_len, u32 flags) -{ - return sys_mremap((unsigned long)addr, (unsigned long)old_len, - (unsigned long)new_len, (unsigned long)flags); -} - -extern asmlinkage int sys_swapoff(const char * specialfile); - -asmlinkage int sys32_swapoff(u32 specialfile) -{ - return sys_swapoff((const char *)A(specialfile)); -} - -extern asmlinkage int sys_swapon(const char * specialfile, int swap_flags); - -asmlinkage int sys32_swapon(u32 specialfile, int swap_flags) -{ - return sys_swapon((const char *)A(specialfile), swap_flags); -} - -extern asmlinkage int sys_bind(int fd, struct sockaddr *umyaddr, int addrlen); - -asmlinkage inline int sys32_bind(int fd, u32 umyaddr, int addrlen) -{ - /* sockaddr is the same :)) */ - return sys_bind(fd, (struct sockaddr *)A(umyaddr), addrlen); -} - -extern asmlinkage int sys_accept(int fd, struct sockaddr *upeer_sockaddr, - int *upeer_addrlen); - -asmlinkage inline int sys32_accept(int fd, u32 upeer_sockaddr, u32 upeer_addrlen) -{ - return sys_accept(fd, (struct sockaddr *)A(upeer_sockaddr), - (int *)A(upeer_addrlen)); -} - -extern asmlinkage int sys_connect(int fd, struct sockaddr *uservaddr, int addrlen); - -asmlinkage inline int sys32_connect(int fd, u32 uservaddr, int addrlen) -{ - return sys_connect(fd, (struct sockaddr *)A(uservaddr), addrlen); -} - -extern asmlinkage int sys_getsockname(int fd, struct sockaddr *usockaddr, - int *usockaddr_len); - -asmlinkage int sys32_getsockname(int fd, u32 usockaddr, u32 usockaddr_len) -{ - return sys_getsockname(fd, (struct sockaddr *)A(usockaddr), - (int *)A(usockaddr_len)); -} - -extern asmlinkage int sys_getpeername(int fd, struct sockaddr *usockaddr, - int *usockaddr_len); - -asmlinkage int sys32_getpeername(int fd, u32 usockaddr, u32 usockaddr_len) -{ - return sys_getpeername(fd, (struct sockaddr *)A(usockaddr), - (int *)A(usockaddr_len)); -} - -extern asmlinkage int sys_send(int fd, void * buff, size_t len, unsigned flags); - -asmlinkage inline int sys32_send(int fd, u32 buff, - __kernel_size_t32 len, unsigned flags) -{ - return sys_send(fd, (void *)A(buff), (size_t)len, flags); -} - -extern asmlinkage int sys_sendto(int fd, void * buff, size_t len, unsigned flags, - struct sockaddr *addr, int addr_len); - -asmlinkage inline int sys32_sendto(int fd, u32 buff, __kernel_size_t32 len, - unsigned flags, u32 addr, int addr_len) -{ - return sys_sendto(fd, (void *)A(buff), (size_t)len, flags, - (struct sockaddr *)A(addr), addr_len); -} - -extern asmlinkage int sys_recv(int fd, void * ubuf, size_t size, unsigned flags); - -asmlinkage inline int sys32_recv(int fd, u32 ubuf, - __kernel_size_t32 size, unsigned flags) -{ - return sys_recv(fd, (void *)A(ubuf), (size_t)size, flags); -} - -extern asmlinkage int sys_recvfrom(int fd, void * ubuf, size_t size, unsigned flags, - struct sockaddr *addr, int *addr_len); - -asmlinkage inline int sys32_recvfrom(int fd, u32 ubuf, __kernel_size_t32 size, - unsigned flags, u32 addr, u32 addr_len) -{ - return sys_recvfrom(fd, (void *)A(ubuf), (size_t)size, flags, - (struct sockaddr *)A(addr), (int *)A(addr_len)); -} - -extern asmlinkage int sys_setsockopt(int fd, int level, int optname, - char *optval, int optlen); - -asmlinkage inline int sys32_setsockopt(int fd, int level, int optname, - u32 optval, int optlen) -{ - /* XXX handle ip_fw32->ip_fw conversion for IP firewalling and accounting. - Do it using some macro in ip_sockglue.c - Other optval arguments are mostly just ints or 32<->64bit transparent */ - return sys_setsockopt(fd, level, optname, (char *)A(optval), optlen); -} - -extern asmlinkage int sys_getsockopt(int fd, int level, int optname, - char *optval, int *optlen); - -asmlinkage inline int sys32_getsockopt(int fd, int level, int optname, - u32 optval, u32 optlen) -{ - return sys_getsockopt(fd, level, optname, (char *)A(optval), (int *)A(optlen)); -} - /* XXX This really belongs in some header file... -DaveM */ #define MAX_SOCK_ADDR 128 /* 108 for Unix domain - 16 for IP, 16 for IPX, @@ -2474,6 +1966,24 @@ static unsigned char nargs[18]={AL(0),AL(3),AL(3),AL(3),AL(2),AL(3), AL(6),AL(2),AL(5),AL(5),AL(3),AL(3)}; #undef AL +extern asmlinkage int sys32_bind(int fd, u32 umyaddr, int addrlen); +extern asmlinkage int sys32_connect(int fd, u32 uservaddr, int addrlen); +extern asmlinkage int sys32_accept(int fd, u32 upeer_sockaddr, u32 upeer_addrlen); +extern asmlinkage int sys32_getsockname(int fd, u32 usockaddr, u32 usockaddr_len); +extern asmlinkage int sys32_getpeername(int fd, u32 usockaddr, u32 usockaddr_len); +extern asmlinkage int sys32_send(int fd, u32 buff, __kernel_size_t32 len, + unsigned flags); +extern asmlinkage int sys32_sendto(int fd, u32 buff, __kernel_size_t32 len, + unsigned flags, u32 addr, int addr_len); +extern asmlinkage int sys32_recv(int fd, u32 ubuf, __kernel_size_t32 size, + unsigned flags); +extern asmlinkage int sys32_recvfrom(int fd, u32 ubuf, __kernel_size_t32 size, + unsigned flags, u32 addr, u32 addr_len); +extern asmlinkage int sys32_setsockopt(int fd, int level, int optname, + u32 optval, int optlen); +extern asmlinkage int sys32_getsockopt(int fd, int level, int optname, + u32 optval, u32 optlen); + extern asmlinkage int sys_socket(int family, int type, int protocol); extern asmlinkage int sys_socketpair(int family, int type, int protocol, int usockvec[2]); @@ -2570,7 +2080,7 @@ asmlinkage int sparc32_sigaction (int signum, u32 action, u32 oldaction) old_sa.sa_mask = (sigset_t32)(p->sa_mask); old_sa.sa_flags = (unsigned)(p->sa_flags); old_sa.sa_restorer = (unsigned)(u64)(p->sa_restorer); - if (copy_to_user(A(oldaction), p, sizeof(struct sigaction32))) + if (copy_to_user(A(oldaction), &old_sa, sizeof(struct sigaction32))) goto out; } @@ -2588,14 +2098,6 @@ out: return err; } -extern asmlinkage int sys_nfsservctl(int cmd, void *argp, void *resp); - -asmlinkage int sys32_nfsservctl(int cmd, u32 argp, u32 resp) -{ - /* XXX handle argp and resp args */ - return sys_nfsservctl(cmd, (void *)A(argp), (void *)A(resp)); -} - /* * count32() counts the number of arguments/envelopes */ @@ -2731,11 +2233,219 @@ asmlinkage int sparc32_execve(struct pt_regs *regs) (u32 *)A((u32)regs->u_regs[base + UREG_I1]), (u32 *)A((u32)regs->u_regs[base + UREG_I2]), regs); putname(filename); + if(!error) { + fprs_write(0); + regs->fprs = 0; + } return error; } -/* Modules will be supported with 64bit modutils only */ -asmlinkage int sys32_no_modules(void) +#ifdef CONFIG_MODULES + +extern asmlinkage unsigned long sys_create_module(const char *name_user, size_t size); + +asmlinkage unsigned long sys32_create_module(u32 name_user, __kernel_size_t32 size) +{ + return sys_create_module((const char *)A(name_user), (size_t)size); +} + +extern asmlinkage int sys_init_module(const char *name_user, struct module *mod_user); + +/* Hey, when you're trying to init module, take time and prepare us a nice 64bit + * module structure, even if from 32bit modutils... Why to pollute kernel... :)) + */ +asmlinkage int sys32_init_module(u32 nameuser, u32 mod_user) +{ + return sys_init_module((const char *)A(nameuser), (struct module *)A(mod_user)); +} + +extern asmlinkage int sys_delete_module(const char *name_user); + +asmlinkage int sys32_delete_module(u32 name_user) +{ + return sys_delete_module((const char *)A(name_user)); +} + +struct module_info32 { + u32 addr; + u32 size; + u32 flags; + s32 usecount; +}; + +extern asmlinkage int sys_query_module(const char *name_user, int which, char *buf, size_t bufsize, size_t *ret); + +asmlinkage int sys32_query_module(u32 name_user, int which, u32 buf, __kernel_size_t32 bufsize, u32 retv) +{ + char *buff; + unsigned long old_fs = get_fs(); + size_t val; + int ret, i, j; + unsigned long *p; + char *usernam = NULL; + int bufsiz = bufsize; + struct module_info mi; + + switch (which) { + case 0: return sys_query_module ((const char *)A(name_user), which, (char *)A(buf), (size_t)bufsize, (size_t *)A(retv)); + case QM_SYMBOLS: + bufsiz <<= 1; + case QM_MODULES: + case QM_REFS: + case QM_DEPS: + if (name_user && (ret = getname32 (name_user, &usernam))) + return ret; + buff = kmalloc (bufsiz, GFP_KERNEL); + if (!buff) { + if (name_user) putname32 (usernam); + return -ENOMEM; + } +qmsym_toshort: + set_fs (KERNEL_DS); + ret = sys_query_module (usernam, which, buff, bufsiz, &val); + set_fs (old_fs); + if (which != QM_SYMBOLS) { + if (ret == -ENOSPC || !ret) { + if (put_user (val, (__kernel_size_t32 *)A(retv))) + ret = -EFAULT; + } + if (!ret) { + if (copy_to_user ((char *)A(buf), buff, bufsize)) + ret = -EFAULT; + } + } else { + if (ret == -ENOSPC) { + if (put_user (2 * val, (__kernel_size_t32 *)A(retv))) + ret = -EFAULT; + } + p = (unsigned long *)buff; + if (!ret) { + if (put_user (val, (__kernel_size_t32 *)A(retv))) + ret = -EFAULT; + } + if (!ret) { + j = val * 8; + for (i = 0; i < val; i++, p += 2) { + if (bufsize < (2 * sizeof (u32))) { + bufsiz = 0; + goto qmsym_toshort; + } + if (put_user (p[0], (u32 *)A(buf)) || + __put_user (p[1] - j, (((u32 *)A(buf))+1))) { + ret = -EFAULT; + break; + } + bufsize -= (2 * sizeof (u32)); + buf += (2 * sizeof (u32)); + } + } + if (!ret && val) { + char *strings = buff + ((unsigned long *)buff)[1]; + j = *(p - 1) - ((unsigned long *)buff)[1]; + j = j + strlen (buff + j) + 1; + if (bufsize < j) { + bufsiz = 0; + goto qmsym_toshort; + } + if (copy_to_user ((char *)A(buf), strings, j)) + ret = -EFAULT; + } + } + kfree (buff); + if (name_user) putname32 (usernam); + return ret; + case QM_INFO: + if (name_user && (ret = getname32 (name_user, &usernam))) + return ret; + set_fs (KERNEL_DS); + ret = sys_query_module (usernam, which, (char *)&mi, sizeof (mi), &val); + set_fs (old_fs); + if (!ret) { + if (put_user (sizeof (struct module_info32), (__kernel_size_t32 *)A(retv))) + ret = -EFAULT; + else if (bufsize < sizeof (struct module_info32)) + ret = -ENOSPC; + } + if (!ret) { + if (put_user (mi.addr, &(((struct module_info32 *)A(buf))->addr)) || + __put_user (mi.size, &(((struct module_info32 *)A(buf))->size)) || + __put_user (mi.flags, &(((struct module_info32 *)A(buf))->flags)) || + __put_user (mi.usecount, &(((struct module_info32 *)A(buf))->usecount))) + ret = -EFAULT; + } + if (name_user) putname32 (usernam); + return ret; + default: + return -EINVAL; + } +} + +struct kernel_sym32 { + u32 value; + char name[60]; +}; + +extern asmlinkage int sys_get_kernel_syms(struct kernel_sym *table); + +asmlinkage int sys32_get_kernel_syms(u32 table) +{ + int len, i; + struct kernel_sym *tbl; + unsigned long old_fs; + + len = sys_get_kernel_syms(NULL); + if (!table) return len; + tbl = kmalloc (len * sizeof (struct kernel_sym), GFP_KERNEL); + if (!tbl) return -ENOMEM; + old_fs = get_fs(); + set_fs (KERNEL_DS); + sys_get_kernel_syms(tbl); + set_fs (old_fs); + for (i = 0; i < len; i++, table += sizeof (struct kernel_sym32)) { + if (put_user (tbl[i].value, &(((struct kernel_sym32 *)A(table))->value)) || + copy_to_user (((struct kernel_sym32 *)A(table))->name, tbl[i].name, 60)) + break; + } + kfree (tbl); + return i; +} + +#else /* CONFIG_MODULES */ + +asmlinkage unsigned long +sys_create_module(const char *name_user, size_t size) +{ + return -ENOSYS; +} + +asmlinkage int +sys_init_module(const char *name_user, struct module *mod_user) +{ + return -ENOSYS; +} + +asmlinkage int +sys_delete_module(const char *name_user) { return -ENOSYS; } + +asmlinkage int +sys_query_module(const char *name_user, int which, char *buf, size_t bufsize, + size_t *ret) +{ + /* Let the program know about the new interface. Not that + it'll do them much good. */ + if (which == 0) + return 0; + + return -ENOSYS; +} + +asmlinkage int +sys_get_kernel_syms(struct kernel_sym *table) +{ + return -ENOSYS; +} + +#endif /* CONFIG_MODULES */ diff --git a/arch/sparc64/kernel/sys_sunos32.c b/arch/sparc64/kernel/sys_sunos32.c new file mode 100644 index 000000000000..381d7e3816ea --- /dev/null +++ b/arch/sparc64/kernel/sys_sunos32.c @@ -0,0 +1,1468 @@ +/* $Id: sys_sunos32.c,v 1.2 1997/07/05 07:09:16 davem Exp $ + * sys_sunos32.c: SunOS binary compatability layer on sparc64. + * + * Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx) + * + * Based upon preliminary work which is: + * + * Copyright (C) 1995 Adrian M. Rodriguez (adrian@remus.rutgers.edu) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include /* for gethostid() */ +#include +#include + +/* For the nfs mount emulation */ +#include +#include +#include +#include + +/* for sunos_select */ +#include +#include + +#define A(x) ((unsigned long)x) + +#define SUNOS_NR_OPEN 256 + +extern unsigned long get_unmapped_area(unsigned long addr, unsigned long len); + +asmlinkage u32 sunos_mmap(u32 addr, u32 len, u32 prot, u32 flags, u32 fd, u32 off) +{ + struct file *file = NULL; + unsigned long retval, ret_type; + + lock_kernel(); + current->personality |= PER_BSD; + if(flags & MAP_NORESERVE) { + printk("%s: unimplemented SunOS MAP_NORESERVE mmap() flag\n", + current->comm); + flags &= ~MAP_NORESERVE; + } + retval = -EBADF; + if(!(flags & MAP_ANONYMOUS)) + if(fd >= SUNOS_NR_OPEN || !(file = current->files->fd[fd])) + goto out; + retval = -ENOMEM; + if(!(flags & MAP_FIXED) && !addr) { + unsigned long attempt = get_unmapped_area(addr, len); + if(!attempt || (attempt >= 0xf0000000UL)) + goto out; + addr = (u32) attempt; + } + if(MAJOR(file->f_inode->i_rdev) == MEM_MAJOR && + MINOR(file->f_inode->i_rdev) == 5) { + flags |= MAP_ANONYMOUS; + file = 0; + } + if(!(flags & MAP_FIXED)) + addr = 0; + ret_type = flags & _MAP_NEW; + flags &= ~_MAP_NEW; + + retval = do_mmap(file, + (unsigned long) addr, (unsigned long) len, + (unsigned long) prot, (unsigned long) flags, + (unsigned long) off); + if(!ret_type) + retval = ((retval < 0xf0000000) ? 0 : retval); +out: + unlock_kernel(); + return (u32) retval; +} + +asmlinkage int sunos_mctl(u32 addr, u32 len, int function, u32 arg) +{ + return 0; +} + +asmlinkage int sunos_brk(u32 baddr) +{ + int freepages, retval = -ENOMEM; + unsigned long rlim; + unsigned long newbrk, oldbrk, brk = (unsigned long) baddr; + + lock_kernel(); + if (brk < current->mm->end_code) + goto out; + newbrk = PAGE_ALIGN(brk); + oldbrk = PAGE_ALIGN(current->mm->brk); + retval = 0; + if (oldbrk == newbrk) { + current->mm->brk = brk; + goto out; + } + /* Always allow shrinking brk. */ + if (brk <= current->mm->brk) { + current->mm->brk = brk; + do_munmap(newbrk, oldbrk-newbrk); + goto out; + } + /* Check against rlimit and stack.. */ + retval = -ENOMEM; + rlim = current->rlim[RLIMIT_DATA].rlim_cur; + if (rlim >= RLIM_INFINITY) + rlim = ~0; + if (brk - current->mm->end_code > rlim) + goto out; + /* Check against existing mmap mappings. */ + if (find_vma_intersection(current->mm, oldbrk, newbrk+PAGE_SIZE)) + goto out; + /* stupid algorithm to decide if we have enough memory: while + * simple, it hopefully works in most obvious cases.. Easy to + * fool it, but this should catch most mistakes. + */ + freepages = buffermem >> PAGE_SHIFT; + freepages += page_cache_size; + freepages >>= 1; + freepages += nr_free_pages; + freepages += nr_swap_pages; + freepages -= num_physpages >> 4; + freepages -= (newbrk-oldbrk) >> PAGE_SHIFT; + if (freepages < 0) + goto out; + /* Ok, we have probably got enough memory - let it rip. */ + current->mm->brk = brk; + do_mmap(NULL, oldbrk, newbrk-oldbrk, + PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_FIXED|MAP_PRIVATE, 0); + retval = 0; +out: + unlock_kernel(); + return retval; +} + +asmlinkage u32 sunos_sbrk(int increment) +{ + int error, oldbrk; + + /* This should do it hopefully... */ + lock_kernel(); + oldbrk = (int)current->mm->brk; + error = sunos_brk(((int) current->mm->brk) + increment); + if(!error) + error = oldbrk; + unlock_kernel(); + return error; +} + +asmlinkage u32 sunos_sstk(int increment) +{ + lock_kernel(); + printk("%s: Call to sunos_sstk(increment<%d>) is unsupported\n", + current->comm, increment); + unlock_kernel(); + return (u32)-1; +} + +/* Give hints to the kernel as to what paging strategy to use... + * Completely bogus, don't remind me. + */ +#define VA_NORMAL 0 /* Normal vm usage expected */ +#define VA_ABNORMAL 1 /* Abnormal/random vm usage probable */ +#define VA_SEQUENTIAL 2 /* Accesses will be of a sequential nature */ +#define VA_INVALIDATE 3 /* Page table entries should be flushed ??? */ +static char *vstrings[] = { + "VA_NORMAL", + "VA_ABNORMAL", + "VA_SEQUENTIAL", + "VA_INVALIDATE", +}; + +asmlinkage void sunos_vadvise(u32 strategy) +{ + /* I wanna see who uses this... */ + lock_kernel(); + printk("%s: Advises us to use %s paging strategy\n", + current->comm, + strategy <= 3 ? vstrings[strategy] : "BOGUS"); + unlock_kernel(); +} + +/* Same as vadvise, and just as bogus, but for a range of virtual + * process address space. + */ +#define MADV_NORMAL 0 /* Nothing special... */ +#define MADV_RANDOM 1 /* I am emacs... */ +#define MADV_SEQUENTIAL 2 /* I am researcher code... */ +#define MADV_WILLNEED 3 /* Pages in this range will be needed */ +#define MADV_DONTNEED 4 /* Pages in this range won't be needed */ + +static char *mstrings[] = { + "MADV_NORMAL", + "MADV_RANDOM", + "MADV_SEQUENTIAL", + "MADV_WILLNEED", + "MADV_DONTNEED", +}; + +asmlinkage void sunos_madvise(u32 address, u32 len, u32 strategy) +{ + /* I wanna see who uses this... */ + lock_kernel(); + printk("%s: Advises us to use %s paging strategy for addr<%08x> len<%08x>\n", + current->comm, strategy <= 4 ? mstrings[strategy] : "BOGUS", + address, len); + unlock_kernel(); +} + +/* Places into character array, the status of all the pages in the passed + * range from 'addr' to 'addr + len'. -1 on failure, 0 on success... + * The encoding in each character is: + * low-bit is zero == Page is not in physical ram right now + * low-bit is one == Page is currently residing in core + * All other bits are undefined within the character so there... + * Also, if you try to get stats on an area outside of the user vm area + * *or* the passed base address is not aligned on a page boundary you + * get an error. + */ +asmlinkage int sunos_mincore(u32 addr, u32 len, u32 u_array) +{ + pgd_t *pgdp; + pmd_t *pmdp; + pte_t *ptep; + unsigned long limit; + int num_pages, pnum, retval = -EINVAL; + char *array = (char *)A(u_array); + + lock_kernel(); + if(addr & ~(4096)) + goto out; + num_pages = (len / 4096); + retval = -EFAULT; + if(verify_area(VERIFY_WRITE, array, num_pages)) + goto out; + retval = -ENOMEM; + if((addr >= 0xf0000000) || ((addr + len) > 0xf0000000)) + goto out; /* I'm sure you're curious about kernel mappings.. */ + /* Wheee, go through pte's */ + pnum = 0; + for(limit = addr + len; addr < limit; addr += 4096, pnum++) { + pgdp = pgd_offset(current->mm, addr); + if(pgd_none(*pgdp)) + goto out; /* As per SunOS manpage */ + pmdp = pmd_offset(pgdp, addr); + if(pmd_none(*pmdp)) + goto out; /* As per SunOS manpage */ + ptep = pte_offset(pmdp, addr); + if(pte_none(*ptep)) + goto out; /* As per SunOS manpage */ + /* Page in core or Swapped page? */ + __put_user((pte_present(*ptep) ? 1 : 0), &array[pnum]); + } + retval = 0; /* Success... I think... */ +out: + unlock_kernel(); + return retval; +} + +/* This just wants the soft limit (ie. rlim_cur element) of the RLIMIT_NOFILE + * resource limit and is for backwards compatibility with older sunos + * revs. + */ +asmlinkage int sunos_getdtablesize(void) +{ + return SUNOS_NR_OPEN; +} + +#define _S(nr) (1<<((nr)-1)) + +#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) + +asmlinkage u32 sunos_sigblock(u32 blk_mask) +{ + unsigned long flags; + u32 old; + + lock_kernel(); + save_and_cli(flags); + old = (u32) current->blocked; + current->blocked |= (blk_mask & _BLOCKABLE); + restore_flags(flags); + unlock_kernel(); + return old; +} + +asmlinkage u32 sunos_sigsetmask(u32 newmask) +{ + unsigned long flags; + u32 retval; + + lock_kernel(); + save_and_cli(flags); + retval = (u32) current->blocked; + current->blocked = (newmask & _BLOCKABLE); + restore_flags(flags); + unlock_kernel(); + return retval; +} + +/* SunOS getdents is very similar to the newer Linux (iBCS2 compliant) */ +/* getdents system call, the format of the structure just has a different */ +/* layout (d_off+d_ino instead of d_ino+d_off) */ +struct sunos_dirent { + s32 d_off; + u32 d_ino; + u16 d_reclen; + u16 d_namlen; + char d_name[1]; +}; + +struct sunos_dirent_callback { + struct sunos_dirent *curr; + struct sunos_dirent *previous; + int count; + int error; +}; + +#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) +#define ROUND_UP(x) (((x)+sizeof(s32)-1) & ~(sizeof(s32)-1)) + +static int sunos_filldir(void * __buf, const char * name, int namlen, + off_t offset, ino_t ino) +{ + struct sunos_dirent * dirent; + struct sunos_dirent_callback * buf = (struct sunos_dirent_callback *) __buf; + int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1); + + buf->error = -EINVAL; /* only used if we fail.. */ + if (reclen > buf->count) + return -EINVAL; + dirent = buf->previous; + if (dirent) + put_user(offset, &dirent->d_off); + dirent = buf->curr; + buf->previous = dirent; + put_user(ino, &dirent->d_ino); + put_user(namlen, &dirent->d_namlen); + put_user(reclen, &dirent->d_reclen); + copy_to_user(dirent->d_name, name, namlen); + put_user(0, dirent->d_name + namlen); + ((char *) dirent) += reclen; + buf->curr = dirent; + buf->count -= reclen; + return 0; +} + +asmlinkage int sunos_getdents(unsigned int fd, u32 u_dirent, int cnt) +{ + struct file * file; + struct sunos_dirent * lastdirent; + struct sunos_dirent_callback buf; + int error = -EBADF; + void *dirent = (void *)A(u_dirent); + + lock_kernel(); + if (fd >= SUNOS_NR_OPEN || !(file = current->files->fd[fd])) + goto out; + error = -ENOTDIR; + if (!file->f_op || !file->f_op->readdir) + goto out; + error = -EINVAL; + if(cnt < (sizeof(struct sunos_dirent) + 255)) + goto out; + + buf.curr = (struct sunos_dirent *) dirent; + buf.previous = NULL; + buf.count = cnt; + buf.error = 0; + error = file->f_op->readdir(file->f_inode, file, &buf, sunos_filldir); + if (error < 0) + goto out; + lastdirent = buf.previous; + if (!lastdirent) { + error = buf.error; + } else { + put_user(file->f_pos, &lastdirent->d_off); + error = cnt - buf.count; + } +out: + unlock_kernel(); + return error; +} + +/* Old sunos getdirentries, severely broken compatibility stuff here. */ +struct sunos_direntry { + u32 d_ino; + u16 d_reclen; + u16 d_namlen; + char d_name[1]; +}; + +struct sunos_direntry_callback { + struct sunos_direntry *curr; + struct sunos_direntry *previous; + int count; + int error; +}; + +static int sunos_filldirentry(void * __buf, const char * name, int namlen, + off_t offset, ino_t ino) +{ + struct sunos_direntry * dirent; + struct sunos_direntry_callback * buf = (struct sunos_direntry_callback *) __buf; + int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1); + + buf->error = -EINVAL; /* only used if we fail.. */ + if (reclen > buf->count) + return -EINVAL; + dirent = buf->previous; + dirent = buf->curr; + buf->previous = dirent; + put_user(ino, &dirent->d_ino); + put_user(namlen, &dirent->d_namlen); + put_user(reclen, &dirent->d_reclen); + copy_to_user(dirent->d_name, name, namlen); + put_user(0, dirent->d_name + namlen); + ((char *) dirent) += reclen; + buf->curr = dirent; + buf->count -= reclen; + return 0; +} + +asmlinkage int sunos_getdirentries(unsigned int fd, u32 u_dirent, + int cnt, u32 u_basep) +{ + struct file * file; + struct sunos_direntry * lastdirent; + struct sunos_direntry_callback buf; + int error = -EBADF; + void *dirent = (void *) A(u_dirent); + unsigned int *basep = (unsigned int *)A(u_basep); + + lock_kernel(); + if (fd >= SUNOS_NR_OPEN || !(file = current->files->fd[fd])) + goto out; + error = -ENOTDIR; + if (!file->f_op || !file->f_op->readdir) + goto out; + error = -EINVAL; + if(cnt < (sizeof(struct sunos_direntry) + 255)) + goto out; + + buf.curr = (struct sunos_direntry *) dirent; + buf.previous = NULL; + buf.count = cnt; + buf.error = 0; + error = file->f_op->readdir(file->f_inode, file, &buf, sunos_filldirentry); + if (error < 0) + goto out; + lastdirent = buf.previous; + if (!lastdirent) { + error = buf.error; + } else { + put_user(file->f_pos, basep); + error = cnt - buf.count; + } +out: + unlock_kernel(); + return error; +} + +asmlinkage int sunos_getdomainname(u32 u_name, int len) +{ + int nlen = strlen(system_utsname.domainname); + int ret = -EFAULT; + char *name = (char *)A(u_name); + + lock_kernel(); + if (nlen < len) + len = nlen; + + if(len > __NEW_UTS_LEN) + goto out; + if(copy_to_user(name, system_utsname.domainname, len)) + goto out; + ret = 0; +out: + unlock_kernel(); + return ret; +} + +struct sunos_utsname { + char sname[9]; + char nname[9]; + char nnext[56]; + char rel[9]; + char ver[9]; + char mach[9]; +}; + +asmlinkage int sunos_uname(u32 u_name) +{ + struct sunos_utsname *name = (struct sunos_utsname *)A(u_name); + int ret = -EFAULT; + + lock_kernel(); + if(!name) + goto out; + if(copy_to_user(&name->sname[0], + &system_utsname.sysname[0], + sizeof(name->sname) - 1)) + goto out; + copy_to_user(&name->nname[0], + &system_utsname.nodename[0], + sizeof(name->nname) - 1); + put_user('\0', &name->nname[8]); + copy_to_user(&name->rel[0], &system_utsname.release[0], sizeof(name->rel) - 1); + copy_to_user(&name->ver[0], &system_utsname.version[0], sizeof(name->ver) - 1); + copy_to_user(&name->mach[0], &system_utsname.machine[0], sizeof(name->mach) - 1); + ret = 0; +out: + unlock_kernel(); + return ret; +} + +asmlinkage int sunos_nosys(void) +{ + struct pt_regs *regs; + + lock_kernel(); + regs = current->tss.kregs; + current->tss.sig_address = regs->tpc; + current->tss.sig_desc = regs->u_regs[UREG_G1]; + send_sig(SIGSYS, current, 1); + printk("Process makes ni_syscall number %d, register dump:\n", + (int) regs->u_regs[UREG_G1]); + show_regs(regs); + unlock_kernel(); + return -ENOSYS; +} + +/* This is not a real and complete implementation yet, just to keep + * the easy SunOS binaries happy. + */ +asmlinkage int sunos_fpathconf(int fd, int name) +{ + int ret; + + lock_kernel(); + switch(name) { + case _PCONF_LINK: + ret = LINK_MAX; + break; + case _PCONF_CANON: + ret = MAX_CANON; + break; + case _PCONF_INPUT: + ret = MAX_INPUT; + break; + case _PCONF_NAME: + ret = NAME_MAX; + break; + case _PCONF_PATH: + ret = PATH_MAX; + break; + case _PCONF_PIPE: + ret = PIPE_BUF; + break; + case _PCONF_CHRESTRICT: /* XXX Investigate XXX */ + ret = 1; + break; + case _PCONF_NOTRUNC: /* XXX Investigate XXX */ + case _PCONF_VDISABLE: + ret = 0; + break; + default: + ret = -EINVAL; + break; + } + unlock_kernel(); + return ret; +} + +asmlinkage int sunos_pathconf(u32 u_path, int name) +{ + int ret; + + lock_kernel(); + ret = sunos_fpathconf(0, name); /* XXX cheese XXX */ + unlock_kernel(); + return ret; +} + +/* SunOS mount system call emulation */ +extern asmlinkage int +sys32_select(int n, u32 inp, u32 outp, u32 exp, u32 tvp); + +asmlinkage int sunos_select(int width, u32 inp, u32 outp, u32 exp, u32 tvp) +{ + int ret; + + /* SunOS binaries expect that select won't change the tvp contents */ + lock_kernel(); + current->personality |= STICKY_TIMEOUTS; + ret = sys32_select (width, inp, outp, exp, tvp); + unlock_kernel(); + return ret; +} + +asmlinkage void sunos_nop(void) +{ + return; +} + +/* XXXXXXXXXX SunOS mount/umount. XXXXXXXXXXX */ +#define SMNT_RDONLY 1 +#define SMNT_NOSUID 2 +#define SMNT_NEWTYPE 4 +#define SMNT_GRPID 8 +#define SMNT_REMOUNT 16 +#define SMNT_NOSUB 32 +#define SMNT_MULTI 64 +#define SMNT_SYS5 128 + +struct sunos_fh_t { + char fh_data [NFS_FHSIZE]; +}; + +struct sunos_nfs_mount_args { + struct sockaddr_in *addr; /* file server address */ + struct nfs_fh *fh; /* File handle to be mounted */ + int flags; /* flags */ + int wsize; /* write size in bytes */ + int rsize; /* read size in bytes */ + int timeo; /* initial timeout in .1 secs */ + int retrans; /* times to retry send */ + char *hostname; /* server's hostname */ + int acregmin; /* attr cache file min secs */ + int acregmax; /* attr cache file max secs */ + int acdirmin; /* attr cache dir min secs */ + int acdirmax; /* attr cache dir max secs */ + char *netname; /* server's netname */ +}; + +extern int do_mount(kdev_t, const char *, const char *, char *, int, void *); +extern dev_t get_unnamed_dev(void); +extern void put_unnamed_dev(dev_t); +extern asmlinkage int sys_mount(char *, char *, char *, unsigned long, void *); +extern asmlinkage int sys_connect(int fd, struct sockaddr *uservaddr, int addrlen); +extern asmlinkage int sys_socket(int family, int type, int protocol); +extern asmlinkage int sys_bind(int fd, struct sockaddr *umyaddr, int addrlen); + + +/* Bind the socket on a local reserved port and connect it to the + * remote server. This on Linux/i386 is done by the mount program, + * not by the kernel. + */ +/* XXXXXXXXXXXXXXXXXXXX */ +static int +sunos_nfs_get_server_fd (int fd, struct sockaddr_in *addr) +{ + struct sockaddr_in local; + struct sockaddr_in server; + int try_port; + int ret; + struct socket *socket; + struct inode *inode; + struct file *file; + + file = current->files->fd [fd]; + inode = file->f_inode; + if (!inode || !inode->i_sock) + return 0; + + socket = &inode->u.socket_i; + local.sin_family = AF_INET; + local.sin_addr.s_addr = INADDR_ANY; + + /* IPPORT_RESERVED = 1024, can't find the definition in the kernel */ + try_port = 1024; + do { + local.sin_port = htons (--try_port); + ret = socket->ops->bind(socket, (struct sockaddr*)&local, + sizeof(local)); + } while (ret && try_port > (1024 / 2)); + + if (ret) + return 0; + + server.sin_family = AF_INET; + server.sin_addr = addr->sin_addr; + server.sin_port = NFS_PORT; + + /* Call sys_connect */ + ret = socket->ops->connect (socket, (struct sockaddr *) &server, + sizeof (server), file->f_flags); + if (ret < 0) + return 0; + return 1; +} + +/* XXXXXXXXXXXXXXXXXXXX */ +static int get_default (int value, int def_value) +{ + if (value) + return value; + else + return def_value; +} + +/* XXXXXXXXXXXXXXXXXXXX */ +asmlinkage int sunos_nfs_mount(char *dir_name, int linux_flags, void *data) +{ + int ret = -ENODEV; + int server_fd; + char *the_name; + struct nfs_mount_data linux_nfs_mount; + struct sunos_nfs_mount_args *sunos_mount = data; + dev_t dev; + + /* Ok, here comes the fun part: Linux's nfs mount needs a + * socket connection to the server, but SunOS mount does not + * require this, so we use the information on the destination + * address to create a socket and bind it to a reserved + * port on this system + */ + server_fd = sys_socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (server_fd < 0) + return -ENXIO; + + if (!sunos_nfs_get_server_fd (server_fd, sunos_mount->addr)){ + sys_close (server_fd); + return -ENXIO; + } + + /* Now, bind it to a locally reserved port */ + linux_nfs_mount.version = NFS_MOUNT_VERSION; + linux_nfs_mount.flags = sunos_mount->flags; + linux_nfs_mount.addr = *sunos_mount->addr; + linux_nfs_mount.root = *sunos_mount->fh; + linux_nfs_mount.fd = server_fd; + + linux_nfs_mount.rsize = get_default (sunos_mount->rsize, 8192); + linux_nfs_mount.wsize = get_default (sunos_mount->wsize, 8192); + linux_nfs_mount.timeo = get_default (sunos_mount->timeo, 10); + linux_nfs_mount.retrans = sunos_mount->retrans; + + linux_nfs_mount.acregmin = sunos_mount->acregmin; + linux_nfs_mount.acregmax = sunos_mount->acregmax; + linux_nfs_mount.acdirmin = sunos_mount->acdirmin; + linux_nfs_mount.acdirmax = sunos_mount->acdirmax; + + if (getname (sunos_mount->hostname, &the_name)) + return -EFAULT; + + strncpy (linux_nfs_mount.hostname, the_name, 254); + linux_nfs_mount.hostname [255] = 0; + putname (the_name); + + dev = get_unnamed_dev (); + + ret = do_mount (dev, "", dir_name, "nfs", linux_flags, &linux_nfs_mount); + if (ret) + put_unnamed_dev(dev); + + return ret; +} + +/* XXXXXXXXXXXXXXXXXXXX */ +asmlinkage int +sunos_mount(char *type, char *dir, int flags, void *data) +{ + int linux_flags = MS_MGC_MSK; /* new semantics */ + int ret = -EINVAL; + char *dev_fname = 0; + + lock_kernel(); + /* We don't handle the integer fs type */ + if ((flags & SMNT_NEWTYPE) == 0) + goto out; + + /* Do not allow for those flags we don't support */ + if (flags & (SMNT_GRPID|SMNT_NOSUB|SMNT_MULTI|SMNT_SYS5)) + goto out; + + if(flags & SMNT_REMOUNT) + linux_flags |= MS_REMOUNT; + if(flags & SMNT_RDONLY) + linux_flags |= MS_RDONLY; + if(flags & SMNT_NOSUID) + linux_flags |= MS_NOSUID; + if(strcmp(type, "ext2") == 0) { + dev_fname = (char *) data; + } else if(strcmp(type, "iso9660") == 0) { + dev_fname = (char *) data; + } else if(strcmp(type, "minix") == 0) { + dev_fname = (char *) data; + } else if(strcmp(type, "nfs") == 0) { + ret = sunos_nfs_mount (dir, flags, data); + goto out; + } else if(strcmp(type, "ufs") == 0) { + printk("Warning: UFS filesystem mounts unsupported.\n"); + ret = -ENODEV; + goto out; + } else if(strcmp(type, "proc")) { + ret = -ENODEV; + goto out; + } + ret = sys_mount(dev_fname, dir, type, linux_flags, NULL); +out: + unlock_kernel(); + return ret; +} + +extern asmlinkage int sys_setsid(void); +extern asmlinkage int sys_setpgid(pid_t, pid_t); + +asmlinkage int sunos_setpgrp(pid_t pid, pid_t pgid) +{ + int ret; + + /* So stupid... */ + lock_kernel(); + if((!pid || pid == current->pid) && + !pgid) { + sys_setsid(); + ret = 0; + } else { + ret = sys_setpgid(pid, pgid); + } + unlock_kernel(); + return ret; +} + +/* So stupid... */ +extern asmlinkage int sys32_wait4(__kernel_pid_t32 pid, + u32 stat_addr, int options, u32 ru); + +asmlinkage int sunos_wait4(__kernel_pid_t32 pid, u32 stat_addr, int options, u32 ru) +{ + int ret; + + lock_kernel(); + ret = sys32_wait4((pid ? pid : ((__kernel_pid_t32)-1)), + stat_addr, options, ru); + unlock_kernel(); + return ret; +} + +extern int kill_pg(int, int, int); +asmlinkage int sunos_killpg(int pgrp, int sig) +{ + int ret; + + lock_kernel(); + ret = kill_pg(pgrp, sig, 0); + unlock_kernel(); + return ret; +} + +asmlinkage int sunos_audit(void) +{ + lock_kernel(); + printk ("sys_audit\n"); + unlock_kernel(); + return -1; +} + +extern asmlinkage u32 sunos_gethostid(void) +{ + u32 ret; + + lock_kernel(); + ret = (((u32)idprom->id_machtype << 24) | ((u32)idprom->id_sernum)); + unlock_kernel(); + return ret; +} + +/* sysconf options, for SunOS compatibility */ +#define _SC_ARG_MAX 1 +#define _SC_CHILD_MAX 2 +#define _SC_CLK_TCK 3 +#define _SC_NGROUPS_MAX 4 +#define _SC_OPEN_MAX 5 +#define _SC_JOB_CONTROL 6 +#define _SC_SAVED_IDS 7 +#define _SC_VERSION 8 + +extern asmlinkage s32 sunos_sysconf (int name) +{ + s32 ret; + + lock_kernel(); + switch (name){ + case _SC_ARG_MAX: + ret = ARG_MAX; + break; + case _SC_CHILD_MAX: + ret = CHILD_MAX; + break; + case _SC_CLK_TCK: + ret = HZ; + break; + case _SC_NGROUPS_MAX: + ret = NGROUPS_MAX; + break; + case _SC_OPEN_MAX: + ret = OPEN_MAX; + break; + case _SC_JOB_CONTROL: + ret = 1; /* yes, we do support job control */ + break; + case _SC_SAVED_IDS: + ret = 1; /* yes, we do support saved uids */ + break; + case _SC_VERSION: + /* mhm, POSIX_VERSION is in /usr/include/unistd.h + * should it go on /usr/include/linux? + */ + ret = 199009; + break; + default: + ret = -1; + break; + }; + unlock_kernel(); + return ret; +} + +extern asmlinkage int sys_semctl (int semid, int semnum, int cmd, union semun arg); +extern asmlinkage int sys_semget (key_t key, int nsems, int semflg); +extern asmlinkage int sys_semop (int semid, struct sembuf *tsops, unsigned nsops); + +asmlinkage int sunos_semsys(int op, u32 arg1, u32 arg2, u32 arg3, u32 ptr) +{ + union semun arg4; + int ret; + + lock_kernel(); + switch (op) { + case 0: + /* Most arguments match on a 1:1 basis but cmd doesn't */ + switch(arg3) { + case 4: + arg3=GETPID; break; + case 5: + arg3=GETVAL; break; + case 6: + arg3=GETALL; break; + case 3: + arg3=GETNCNT; break; + case 7: + arg3=GETZCNT; break; + case 8: + arg3=SETVAL; break; + case 9: + arg3=SETALL; break; + } + /* sys_semctl(): */ + arg4.__pad=(void *)A(ptr); /* value to modify semaphore to */ + ret = sys_semctl((int)arg1, (int)arg2, (int)arg3, arg4); + break; + case 1: + /* sys_semget(): */ + ret = sys_semget((key_t)arg1, (int)arg2, (int)arg3); + break; + case 2: + /* sys_semop(): */ + ret = sys_semop((int)arg1, (struct sembuf *)A(arg2), (unsigned)arg3); + break; + default: + ret = -EINVAL; + break; + }; + unlock_kernel(); + return ret; +} + +struct msgbuf32 { + s32 mtype; + char mtext[1]; +}; + +struct ipc_perm32 +{ + key_t key; + __kernel_uid_t32 uid; + __kernel_gid_t32 gid; + __kernel_uid_t32 cuid; + __kernel_gid_t32 cgid; + __kernel_mode_t32 mode; + unsigned short seq; +}; + +struct msqid_ds32 +{ + struct ipc_perm32 msg_perm; + u32 msg_first; + u32 msg_last; + __kernel_time_t32 msg_stime; + __kernel_time_t32 msg_rtime; + __kernel_time_t32 msg_ctime; + u32 wwait; + u32 rwait; + unsigned short msg_cbytes; + unsigned short msg_qnum; + unsigned short msg_qbytes; + __kernel_ipc_pid_t32 msg_lspid; + __kernel_ipc_pid_t32 msg_lrpid; +}; + +static inline int sunos_msqid_get(struct msqid_ds32 *user, + struct msqid_ds *kern) +{ + if(get_user(kern->msg_perm.key, &user->msg_perm.key) || + __get_user(kern->msg_perm.uid, &user->msg_perm.uid) || + __get_user(kern->msg_perm.gid, &user->msg_perm.gid) || + __get_user(kern->msg_perm.cuid, &user->msg_perm.cuid) || + __get_user(kern->msg_perm.cgid, &user->msg_perm.cgid) || + __get_user(kern->msg_stime, &user->msg_stime) || + __get_user(kern->msg_rtime, &user->msg_rtime) || + __get_user(kern->msg_ctime, &user->msg_ctime) || + __get_user(kern->msg_ctime, &user->msg_cbytes) || + __get_user(kern->msg_ctime, &user->msg_qnum) || + __get_user(kern->msg_ctime, &user->msg_qbytes) || + __get_user(kern->msg_ctime, &user->msg_lspid) || + __get_user(kern->msg_ctime, &user->msg_lrpid)) + return -EFAULT; + return 0; +} + +static inline int sunos_msqid_put(struct msqid_ds32 *user, + struct msqid_ds *kern) +{ + if(put_user(kern->msg_perm.key, &user->msg_perm.key) || + __put_user(kern->msg_perm.uid, &user->msg_perm.uid) || + __put_user(kern->msg_perm.gid, &user->msg_perm.gid) || + __put_user(kern->msg_perm.cuid, &user->msg_perm.cuid) || + __put_user(kern->msg_perm.cgid, &user->msg_perm.cgid) || + __put_user(kern->msg_stime, &user->msg_stime) || + __put_user(kern->msg_rtime, &user->msg_rtime) || + __put_user(kern->msg_ctime, &user->msg_ctime) || + __put_user(kern->msg_ctime, &user->msg_cbytes) || + __put_user(kern->msg_ctime, &user->msg_qnum) || + __put_user(kern->msg_ctime, &user->msg_qbytes) || + __put_user(kern->msg_ctime, &user->msg_lspid) || + __put_user(kern->msg_ctime, &user->msg_lrpid)) + return -EFAULT; + return 0; +} + +static inline int sunos_msgbuf_get(struct msgbuf32 *user, struct msgbuf *kern, int len) +{ + if(get_user(kern->mtype, &user->mtype) || + __copy_from_user(kern->mtext, &user->mtext, len)) + return -EFAULT; + return 0; +} + +static inline int sunos_msgbuf_put(struct msgbuf32 *user, struct msgbuf *kern, int len) +{ + if(put_user(kern->mtype, &user->mtype) || + __copy_to_user(user->mtext, kern->mtext, len)) + return -EFAULT; + return 0; +} + +extern asmlinkage int sys_msgget (key_t key, int msgflg); +extern asmlinkage int sys_msgrcv (int msqid, struct msgbuf *msgp, + size_t msgsz, long msgtyp, int msgflg); +extern asmlinkage int sys_msgsnd (int msqid, struct msgbuf *msgp, + size_t msgsz, int msgflg); +extern asmlinkage int sys_msgctl (int msqid, int cmd, struct msqid_ds *buf); + +asmlinkage int sunos_msgsys(int op, u32 arg1, u32 arg2, u32 arg3, u32 arg4) +{ + struct sparc_stackf32 *sp; + struct msqid_ds kds; + struct msgbuf *kmbuf; + unsigned long old_fs = get_fs(); + u32 arg5; + int rval; + + lock_kernel(); + switch(op) { + case 0: + rval = sys_msgget((key_t)arg1, (int)arg2); + break; + case 1: + if(!sunos_msqid_get((struct msqid_ds32 *)A(arg3), &kds)) { + set_fs(KERNEL_DS); + rval = sys_msgctl((int)arg1, (int)arg2, + (struct msqid_ds *)A(arg3)); + set_fs(old_fs); + if(!rval) + rval = sunos_msqid_put((struct msqid_ds32 *)A(arg3), + &kds); + } else + rval = -EFAULT; + break; + case 2: + rval = -EFAULT; + kmbuf = (struct msgbuf *)kmalloc(sizeof(struct msgbuf) + arg3, + GFP_KERNEL); + if(!kmbuf) + break; + sp = (struct sparc_stackf32 *) + (current->tss.kregs->u_regs[UREG_FP] & 0xffffffffUL); + if(get_user(arg5, &sp->xxargs[0])) { + rval = -EFAULT; + break; + } + set_fs(KERNEL_DS); + rval = sys_msgrcv((int)arg1, kmbuf, (size_t)arg3, + (long)arg4, (int)arg5); + set_fs(old_fs); + if(!rval) + rval = sunos_msgbuf_put((struct msgbuf32 *)A(arg2), + kmbuf, arg3); + kfree(kmbuf); + break; + case 3: + rval = -EFAULT; + kmbuf = (struct msgbuf *)kmalloc(sizeof(struct msgbuf) + arg3, + GFP_KERNEL); + if(!kmbuf || sunos_msgbuf_get((struct msgbuf32 *)A(arg2), + kmbuf, arg3)) + break; + set_fs(KERNEL_DS); + rval = sys_msgsnd((int)arg1, kmbuf, (size_t)arg3, (int)arg4); + set_fs(old_fs); + kfree(kmbuf); + break; + default: + rval = -EINVAL; + break; + } + unlock_kernel(); + return rval; +} + +struct shmid_ds32 { + struct ipc_perm32 shm_perm; + int shm_segsz; + __kernel_time_t32 shm_atime; + __kernel_time_t32 shm_dtime; + __kernel_time_t32 shm_ctime; + __kernel_ipc_pid_t32 shm_cpid; + __kernel_ipc_pid_t32 shm_lpid; + unsigned short shm_nattch; + unsigned short shm_npages; + u32 shm_pages; + u32 attaches; +}; + +static inline int sunos_shmid_get(struct shmid_ds32 *user, + struct shmid_ds *kern) +{ + if(get_user(kern->shm_perm.key, &user->shm_perm.key) || + __get_user(kern->shm_perm.uid, &user->shm_perm.uid) || + __get_user(kern->shm_perm.gid, &user->shm_perm.gid) || + __get_user(kern->shm_perm.cuid, &user->shm_perm.cuid) || + __get_user(kern->shm_perm.cgid, &user->shm_perm.cgid) || + __get_user(kern->shm_segsz, &user->shm_segsz) || + __get_user(kern->shm_atime, &user->shm_atime) || + __get_user(kern->shm_dtime, &user->shm_dtime) || + __get_user(kern->shm_ctime, &user->shm_ctime) || + __get_user(kern->shm_cpid, &user->shm_cpid) || + __get_user(kern->shm_lpid, &user->shm_lpid) || + __get_user(kern->shm_nattch, &user->shm_nattch) || + __get_user(kern->shm_npages, &user->shm_npages)) + return -EFAULT; + return 0; +} + +static inline int sunos_shmid_put(struct shmid_ds32 *user, + struct shmid_ds *kern) +{ + if(put_user(kern->shm_perm.key, &user->shm_perm.key) || + __put_user(kern->shm_perm.uid, &user->shm_perm.uid) || + __put_user(kern->shm_perm.gid, &user->shm_perm.gid) || + __put_user(kern->shm_perm.cuid, &user->shm_perm.cuid) || + __put_user(kern->shm_perm.cgid, &user->shm_perm.cgid) || + __put_user(kern->shm_segsz, &user->shm_segsz) || + __put_user(kern->shm_atime, &user->shm_atime) || + __put_user(kern->shm_dtime, &user->shm_dtime) || + __put_user(kern->shm_ctime, &user->shm_ctime) || + __put_user(kern->shm_cpid, &user->shm_cpid) || + __put_user(kern->shm_lpid, &user->shm_lpid) || + __put_user(kern->shm_nattch, &user->shm_nattch) || + __put_user(kern->shm_npages, &user->shm_npages)) + return -EFAULT; + return 0; +} + +extern asmlinkage int sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr); +extern asmlinkage int sys_shmctl (int shmid, int cmd, struct shmid_ds *buf); +extern asmlinkage int sys_shmdt (char *shmaddr); +extern asmlinkage int sys_shmget (key_t key, int size, int shmflg); + +asmlinkage int sunos_shmsys(int op, u32 arg1, u32 arg2, u32 arg3) +{ + struct shmid_ds ksds; + unsigned long raddr, old_fs = get_fs(); + int rval; + + lock_kernel(); + switch(op) { + case 0: + /* sys_shmat(): attach a shared memory area */ + rval = sys_shmat((int)arg1,(char *)A(arg2),(int)arg3,&raddr); + if(!rval) + rval = (int) raddr; + break; + case 1: + /* sys_shmctl(): modify shared memory area attr. */ + if(!sunos_shmid_get((struct shmid_ds32 *)A(arg3), &ksds)) { + set_fs(KERNEL_DS); + rval = sys_shmctl((int)arg1,(int)arg2, &ksds); + set_fs(old_fs); + if(!rval) + rval = sunos_shmid_put((struct shmid_ds32 *)A(arg3), + &ksds); + } else + rval = -EFAULT; + break; + case 2: + /* sys_shmdt(): detach a shared memory area */ + rval = sys_shmdt((char *)A(arg1)); + break; + case 3: + /* sys_shmget(): get a shared memory area */ + rval = sys_shmget((key_t)arg1,(int)arg2,(int)arg3); + break; + default: + rval = -EINVAL; + break; + }; + unlock_kernel(); + return rval; +} + +asmlinkage int sunos_open(u32 filename, int flags, int mode) +{ + int ret; + + lock_kernel(); + current->personality |= PER_BSD; + ret = sys_open ((char *)A(filename), flags, mode); + unlock_kernel(); + return ret; +} + +#define SUNOS_EWOULDBLOCK 35 + +/* see the sunos man page read(2v) for an explanation + of this garbage. We use O_NDELAY to mark + file descriptors that have been set non-blocking + using 4.2BSD style calls. (tridge) */ + +static inline int check_nonblock(int ret, int fd) +{ + if (ret == -EAGAIN && (current->files->fd[fd]->f_flags & O_NDELAY)) + return -SUNOS_EWOULDBLOCK; + return ret; +} + +extern asmlinkage int sys32_read(unsigned int fd, u32 buf, int count); +extern asmlinkage int sys32_write(unsigned int fd, u32 buf,int count); +extern asmlinkage int sys32_recv(int fd, u32 ubuf, int size, unsigned flags); +extern asmlinkage int sys32_send(int fd, u32 buff, int len, unsigned flags); +extern asmlinkage int sys32_accept(int fd, u32 sa, u32 addrlen); +extern asmlinkage int sys32_readv(u32 fd, u32 vector, s32 count); +extern asmlinkage int sys32_writev(u32 fd, u32 vector, s32 count); + +asmlinkage int sunos_read(unsigned int fd, u32 buf, int count) +{ + int ret; + + lock_kernel(); + ret = check_nonblock(sys32_read(fd, buf, count), fd); + unlock_kernel(); + return ret; +} + +asmlinkage int sunos_readv(u32 fd, u32 vector, s32 count) +{ + int ret; + + lock_kernel(); + ret = check_nonblock(sys32_readv(fd, vector, count), fd); + lock_kernel(); + return ret; +} + +asmlinkage int sunos_write(unsigned int fd, u32 buf, int count) +{ + int ret; + + lock_kernel(); + ret = check_nonblock(sys32_write(fd, buf, count), fd); + unlock_kernel(); + return ret; +} + +asmlinkage int sunos_writev(u32 fd, u32 vector, s32 count) +{ + int ret; + + lock_kernel(); + ret = check_nonblock(sys32_writev(fd, vector, count), fd); + unlock_kernel(); + return ret; +} + +asmlinkage int sunos_recv(int fd, u32 ubuf, int size, unsigned flags) +{ + int ret; + + lock_kernel(); + ret = check_nonblock(sys32_recv(fd, ubuf, size, flags), fd); + unlock_kernel(); + return ret; +} + +asmlinkage int sunos_send(int fd, u32 buff, int len, unsigned flags) +{ + int ret; + + lock_kernel(); + ret = check_nonblock(sys32_send(fd, buff, len, flags), fd); + unlock_kernel(); + return ret; +} + +asmlinkage int sunos_accept(int fd, u32 sa, u32 addrlen) +{ + int ret; + + lock_kernel(); + ret = check_nonblock(sys32_accept(fd, sa, addrlen), fd); + unlock_kernel(); + return ret; +} + +#define SUNOS_SV_INTERRUPT 2 + +extern void check_pending(int signum); + +asmlinkage int sunos_sigaction(int signum, u32 action, u32 oldaction) +{ + struct sigaction32 new_sa, old_sa; + struct sigaction *p; + const int sigaction_size = sizeof (struct sigaction32) - sizeof (u32); + + current->personality |= PER_BSD; + if(signum < 1 || signum > 32) + return -EINVAL; + + p = signum - 1 + current->sig->action; + + if(action) { + if (signum==SIGKILL || signum==SIGSTOP) + return -EINVAL; + memset(&new_sa, 0, sizeof(struct sigaction32)); + if(copy_from_user(&new_sa, (struct sigaction32 *)A(action), + sigaction_size)) + return -EFAULT; + if (((__sighandler_t)A(new_sa.sa_handler) != SIG_DFL) && + (__sighandler_t)A(new_sa.sa_handler) != SIG_IGN) { + if(verify_area(VERIFY_READ, + (__sighandler_t)A(new_sa.sa_handler), 1)) + return -EFAULT; + } + new_sa.sa_flags ^= SUNOS_SV_INTERRUPT; + } + + if (oldaction) { + /* In the clone() case we could copy half consistant + * state to the user, however this could sleep and + * deadlock us if we held the signal lock on SMP. So for + * now I take the easy way out and do no locking. + * But then again we don't support SunOS lwp's anyways ;-) + */ + old_sa.sa_handler = (unsigned)(u64)(p->sa_handler); + old_sa.sa_mask = (sigset_t32)(p->sa_mask); + old_sa.sa_flags = (unsigned)(p->sa_flags); + + if (old_sa.sa_flags & SA_RESTART) + old_sa.sa_flags &= ~SA_RESTART; + else + old_sa.sa_flags |= SUNOS_SV_INTERRUPT; + if (copy_to_user((struct sigaction32 *)A(oldaction), + &old_sa, sigaction_size)) + return -EFAULT; + } + + if (action) { + spin_lock_irq(¤t->sig->siglock); + p->sa_handler = (__sighandler_t)A(new_sa.sa_handler); + p->sa_mask = (sigset_t)(new_sa.sa_mask); + p->sa_flags = new_sa.sa_flags; + p->sa_restorer = (void (*)(void))0; + check_pending(signum); + spin_unlock_irq(¤t->sig->siglock); + } + return 0; +} + + +extern asmlinkage int sys32_setsockopt(int fd, int level, int optname, + u32 optval, int optlen); +extern asmlinkage int sys32_getsockopt(int fd, int level, int optname, + u32 optval, u32 optlen); + +asmlinkage int sunos_setsockopt(int fd, int level, int optname, u32 optval, + int optlen) +{ + int tr_opt = optname; + int ret; + + lock_kernel(); + if (level == SOL_IP) { + /* Multicast socketopts (ttl, membership) */ + if (tr_opt >=2 && tr_opt <= 6) + tr_opt += 30; + } + ret = sys32_setsockopt(fd, level, tr_opt, optval, optlen); + unlock_kernel(); + return ret; +} + +asmlinkage int sunos_getsockopt(int fd, int level, int optname, + u32 optval, u32 optlen) +{ + int tr_opt = optname; + int ret; + + lock_kernel(); + if (level == SOL_IP) { + /* Multicast socketopts (ttl, membership) */ + if (tr_opt >=2 && tr_opt <= 6) + tr_opt += 30; + } + ret = sys32_getsockopt(fd, level, tr_opt, optval, optlen); + unlock_kernel(); + return ret; +} diff --git a/arch/sparc64/kernel/systbls.S b/arch/sparc64/kernel/systbls.S index 61ed19b468f9..eda0ff32632f 100644 --- a/arch/sparc64/kernel/systbls.S +++ b/arch/sparc64/kernel/systbls.S @@ -1,4 +1,4 @@ -/* $Id: systbls.S,v 1.16 1997/06/16 05:37:41 davem Exp $ +/* $Id: systbls.S,v 1.21 1997/07/05 07:09:17 davem Exp $ * systbls.S: System call entry point tables for OS compatibility. * The native Linux system call table lives here also. * @@ -53,15 +53,15 @@ sys_call_table32: .xword sys32_quotactl, sys_nis_syscall, sys32_mount, sys32_ustat, sys_nis_syscall /*170*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_getdents .xword sys_setsid, sys_fchdir, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall -/*180*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_sigpending, sys32_no_modules +/*180*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_sigpending, sys32_query_module .xword sys_setpgid, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_newuname -/*190*/ .xword sys32_no_modules, sys32_personality, sys_prof, sys_break, sys_lock +/*190*/ .xword sys32_init_module, sys32_personality, sys_prof, sys_break, sys_lock .xword sys_mpx, sys_ulimit, sys_getppid, sparc32_sigaction, sys_sgetmask /*200*/ .xword sys_ssetmask, sys_sigsuspend, sys32_newlstat, sys32_uselib, old32_readdir .xword sys_nis_syscall, sys32_socketcall, sys32_syslog, sys32_olduname, sys_nis_syscall /*210*/ .xword sys_idle, sys_nis_syscall, sys32_waitpid, sys32_swapoff, sys32_sysinfo .xword sys32_ipc, sys_sigreturn, sys_clone, sys_nis_syscall, sys32_adjtimex -/*220*/ .xword sys32_sigprocmask, sys32_no_modules, sys32_no_modules, sys32_no_modules, sys_getpgid +/*220*/ .xword sys32_sigprocmask, sys32_create_module, sys32_delete_module, sys32_get_kernel_syms, sys_getpgid .xword sys32_bdflush, sys32_sysfs, sys_nis_syscall, sys_setfsuid, sys_setfsgid /*230*/ .xword sys32_llseek, sys32_time, sys_nis_syscall, sys_stime, sys_nis_syscall .xword sys_nis_syscall, sys32_llseek, sys32_mlock, sys32_munlock, sys_mlockall @@ -111,7 +111,7 @@ sys_call_table: .xword sys_quotactl, sys_nis_syscall, sys_mount, sys_ustat, sys_nis_syscall /*170*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_getdents .xword sys_setsid, sys_fchdir, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall -/*180*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_sigpending, sys_nis_syscall +/*180*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_sigpending, sys_query_module .xword sys_setpgid, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_newuname /*190*/ .xword sys_init_module, sys_personality, sys_prof, sys_break, sys_lock .xword sys_mpx, sys_ulimit, sys_getppid, sparc_sigaction, sys_sgetmask @@ -130,72 +130,72 @@ sys_call_table: /* Now the 32-bit SunOS syscall table. */ - .align 4 + .align 8 .globl sunos_sys_table sunos_sys_table: /*0*/ .xword sunos_indir, sys_exit, sys_fork .xword sunos_read, sunos_write, sunos_open - .xword sys_close, sunos_wait4, sys_creat - .xword sys_link, sys_unlink, sunos_execv - .xword sys_chdir, sunos_nosys, sys_mknod - .xword sys_chmod, sys_chown, sunos_brk - .xword sunos_nosys, sys_lseek, sunos_getpid + .xword sys_close, sunos_wait4, sys32_creat + .xword sys32_link, sys32_unlink, sunos_execv + .xword sys32_chdir, sunos_nosys, sys32_mknod + .xword sys32_chmod, sys32_chown, sunos_brk + .xword sunos_nosys, sys32_lseek, sunos_getpid .xword sunos_nosys, sunos_nosys, sunos_nosys .xword sunos_getuid, sunos_nosys, sys_ptrace .xword sunos_nosys, sunos_nosys, sunos_nosys .xword sunos_nosys, sunos_nosys, sunos_nosys - .xword sys_access, sunos_nosys, sunos_nosys - .xword sys_sync, sys_kill, sys_newstat - .xword sunos_nosys, sys_newlstat, sys_dup + .xword sys32_access, sunos_nosys, sunos_nosys + .xword sys_sync, sys_kill, sys32_newstat + .xword sunos_nosys, sys32_newlstat, sys_dup .xword sys_pipe, sunos_nosys, sys_profil .xword sunos_nosys, sunos_nosys, sunos_getgid .xword sunos_nosys, sunos_nosys -/*50*/ .xword sunos_nosys, sys_acct, sunos_nosys - .xword sunos_mctl, sunos_ioctl, sys_reboot - .xword sunos_nosys, sys_symlink, sys_readlink - .xword sys32_execve, sys_umask, sys_chroot - .xword sys_newfstat, sunos_nosys, sys_getpagesize - .xword sys_msync, sys_vfork, sunos_nosys +/*50*/ .xword sunos_nosys, sys32_acct, sunos_nosys + .xword sunos_mctl, sunos_ioctl, sys32_reboot + .xword sunos_nosys, sys32_symlink, sys32_readlink + .xword sys32_execve, sys_umask, sys32_chroot + .xword sys32_newfstat, sunos_nosys, sys_getpagesize + .xword sys32_msync, sys_vfork, sunos_nosys .xword sunos_nosys, sunos_sbrk, sunos_sstk - .xword sunos_mmap, sunos_vadvise, sys_munmap - .xword sys_mprotect, sunos_madvise, sys_vhangup - .xword sunos_nosys, sunos_mincore, sys_getgroups - .xword sys_setgroups, sys_getpgrp, sunos_setpgrp - .xword sys_setitimer, sunos_nosys, sys_swapon - .xword sys_getitimer, sys_gethostname, sys_sethostname + .xword sunos_mmap, sunos_vadvise, sys32_munmap + .xword sys32_mprotect, sunos_madvise, sys_vhangup + .xword sunos_nosys, sunos_mincore, sys32_getgroups + .xword sys32_setgroups, sys_getpgrp, sunos_setpgrp + .xword sys32_setitimer, sunos_nosys, sys32_swapon + .xword sys32_getitimer, sys32_gethostname, sys32_sethostname .xword sunos_getdtablesize, sys_dup2, sunos_nop - .xword sys_fcntl, sunos_select, sunos_nop + .xword sys32_fcntl, sunos_select, sunos_nop .xword sys_fsync, sys_setpriority, sys_socket - .xword sys_connect, sunos_accept + .xword sys32_connect, sunos_accept /*100*/ .xword sys_getpriority, sunos_send, sunos_recv - .xword sunos_nosys, sys_bind, sunos_setsockopt + .xword sunos_nosys, sys32_bind, sunos_setsockopt .xword sys_listen, sunos_nosys, sunos_sigaction .xword sunos_sigblock, sunos_sigsetmask, sys_sigpause - .xword sys_sigstack, sys_recvmsg, sys_sendmsg - .xword sunos_nosys, sys_gettimeofday, sys_getrusage + .xword sys32_sigstack, sys32_recvmsg, sys32_sendmsg + .xword sunos_nosys, sys_gettimeofday, sys32_getrusage .xword sunos_getsockopt, sunos_nosys, sunos_readv .xword sunos_writev, sys_settimeofday, sys_fchown - .xword sys_fchmod, sys_recvfrom, sys_setreuid - .xword sys_setregid, sys_rename, sys_truncate - .xword sys_ftruncate, sys_flock, sunos_nosys - .xword sys_sendto, sys_shutdown, sys_socketpair - .xword sys_mkdir, sys_rmdir, sys_utimes - .xword sys_sigreturn, sunos_nosys, sys_getpeername - .xword sunos_gethostid, sunos_nosys, sys_getrlimit - .xword sys_setrlimit, sunos_killpg, sunos_nosys + .xword sys_fchmod, sys32_recvfrom, sys32_setreuid + .xword sys_setregid, sys32_rename, sys32_truncate + .xword sys32_ftruncate, sys_flock, sunos_nosys + .xword sys32_sendto, sys_shutdown, sys_socketpair + .xword sys32_mkdir, sys32_rmdir, sys32_utimes + .xword sys_sigreturn, sunos_nosys, sys32_getpeername + .xword sunos_gethostid, sunos_nosys, sys32_getrlimit + .xword sys32_setrlimit, sunos_killpg, sunos_nosys .xword sunos_nosys, sunos_nosys -/*150*/ .xword sys_getsockname, sunos_nosys, sunos_nosys - .xword sunos_poll, sunos_nosys, sunos_nosys - .xword sunos_getdirentries, sys_statfs, sys_fstatfs - .xword sys_umount, sunos_nosys, sunos_nosys - .xword sunos_getdomainname, sys_setdomainname - .xword sunos_nosys, sys_quotactl, sunos_nosys - .xword sunos_mount, sys_ustat, sunos_semsys +/*150*/ .xword sys32_getsockname, sunos_nosys, sunos_nosys + .xword sys32_poll, sunos_nosys, sunos_nosys + .xword sunos_getdirentries, sys32_statfs, sys32_fstatfs + .xword sys32_umount, sunos_nosys, sunos_nosys + .xword sunos_getdomainname, sys32_setdomainname + .xword sunos_nosys, sys32_quotactl, sunos_nosys + .xword sunos_mount, sys32_ustat, sunos_semsys .xword sunos_nosys, sunos_shmsys, sunos_audit .xword sunos_nosys, sunos_getdents, sys_setsid .xword sys_fchdir, sunos_nosys, sunos_nosys .xword sunos_nosys, sunos_nosys, sunos_nosys - .xword sunos_nosys, sys_sigpending, sunos_nosys + .xword sunos_nosys, sys32_sigpending, sunos_nosys .xword sys_setpgid, sunos_pathconf, sunos_fpathconf .xword sunos_sysconf, sunos_uname, sunos_nosys .xword sunos_nosys, sunos_nosys, sunos_nosys diff --git a/arch/sparc64/kernel/time.c b/arch/sparc64/kernel/time.c index 3f15fcb54e65..ad40a5fb5317 100644 --- a/arch/sparc64/kernel/time.c +++ b/arch/sparc64/kernel/time.c @@ -1,4 +1,4 @@ -/* $Id: time.c,v 1.2 1997/04/10 03:02:35 davem Exp $ +/* $Id: time.c,v 1.3 1997/06/17 13:25:29 jj Exp $ * time.c: UltraSparc timer and TOD clock support. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -146,9 +146,6 @@ static int has_low_battery(void) return (data1 == data2); /* Was the write blocked? */ } -/* XXX HACK HACK HACK, delete me soon */ -static struct linux_prom_ranges XXX_sbus_ranges[PROMREG_MAX]; -static int XXX_sbus_nranges; /* Probe for the real time clock chip. */ __initfunc(static void clock_probe(void)) @@ -157,6 +154,10 @@ __initfunc(static void clock_probe(void)) char model[128]; int node, sbusnd, err; + /* XXX HACK HACK HACK, delete me soon */ + struct linux_prom_ranges XXX_sbus_ranges[PROMREG_MAX]; + int XXX_sbus_nranges; + node = prom_getchild(prom_root_node); sbusnd = prom_searchsiblings(node, "sbus"); node = prom_getchild(sbusnd); diff --git a/arch/sparc64/kernel/traps.c b/arch/sparc64/kernel/traps.c index 824a3ddb4974..ac3e79958b5d 100644 --- a/arch/sparc64/kernel/traps.c +++ b/arch/sparc64/kernel/traps.c @@ -1,14 +1,15 @@ -/* $Id: traps.c,v 1.19 1997/06/05 06:22:49 davem Exp $ - * arch/sparc/kernel/traps.c +/* $Id: traps.c,v 1.29 1997/07/05 09:52:38 davem Exp $ + * arch/sparc64/kernel/traps.c * * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ /* - * I hate traps on the sparc, grrr... + * I like traps on v9, :)))) */ +#include #include /* for jiffies */ #include #include @@ -123,6 +124,8 @@ void syscall_trace_entry(unsigned long g1, struct pt_regs *regs) int i; #endif + if(strcmp(current->comm, "bash.sunos")) + return; printk("SYS[%s:%d]: PC(%016lx) <%3d> ", current->comm, current->pid, regs->tpc, (int)g1); #ifdef VERBOSE_SYSCALL_TRACING @@ -153,53 +156,12 @@ void syscall_trace_entry(unsigned long g1, struct pt_regs *regs) unsigned long syscall_trace_exit(unsigned long retval, struct pt_regs *regs) { - printk("ret[%016lx]\n", retval); + if(!strcmp(current->comm, "bash.sunos")) + printk("ret[%016lx]\n", retval); return retval; } #endif /* SYSCALL_TRACING */ -#if 0 -void user_rtrap_report(struct pt_regs *regs) -{ - static int hits = 0; - - /* Bwahhhhrggg... */ - if(regs->tpc == 0x1f294UL && ++hits == 2) { - register unsigned long ctx asm("o4"); - register unsigned long paddr asm("o5"); - unsigned long cwp, wstate; - - printk("RT[%016lx:%016lx] ", regs->tpc, regs->u_regs[UREG_I6]); - __asm__ __volatile__("rdpr %%cwp, %0" : "=r" (cwp)); - __asm__ __volatile__("rdpr %%wstate, %0" : "=r" (wstate)); - printk("CWP[%d] WSTATE[%016lx]\n" - "TSS( ksp[%016lx] kpc[%016lx] wstate[%016lx] w_saved[%d] flgs[%x]" - " cur_ds[%d] )\n", cwp, wstate, - current->tss.ksp, current->tss.kpc, current->tss.wstate, - (int) current->tss.w_saved, current->tss.flags, - current->tss.current_ds); - __asm__ __volatile__(" - rdpr %%pstate, %%o3 - wrpr %%o3, %2, %%pstate - mov %%g7, %%o5 - mov 0x10, %%o4 - ldxa [%%o4] %3, %%o4 - wrpr %%o3, 0x0, %%pstate - " : "=r" (ctx), "=r" (paddr) - : "i" (PSTATE_MG|PSTATE_IE), "i" (ASI_DMMU)); - - printk("MMU[ppgd(%016lx)sctx(%d)] ", paddr, ctx); - printk("mm->context(%016lx) mm->pgd(%p)\n", - current->mm->context, current->mm->pgd); - printk("TASK: signal[%016lx] blocked[%016lx]\n", - current->signal, current->blocked); - show_regs(regs); - while(1) - barrier(); - } -} -#endif - void bad_trap (struct pt_regs *regs, long lvl) { lock_kernel (); @@ -221,168 +183,44 @@ void bad_trap_tl1 (struct pt_regs *regs, long lvl) { char buffer[24]; - lock_kernel (); + lock_kernel(); sprintf (buffer, "Bad trap %lx at tl>0", lvl); die_if_kernel (buffer, regs); + unlock_kernel(); } void data_access_exception (struct pt_regs *regs) { - lock_kernel (); - printk ("Unhandled data access exception "); - printk("sfsr %016lx sfar %016lx\n", spitfire_get_dsfsr(), spitfire_get_sfar()); - die_if_kernel("Data access exception", regs); + send_sig(SIGSEGV, current, 1); } void do_dae(struct pt_regs *regs) { - printk("DAE: at %016lx\n", regs->tpc); - while(1) - barrier(); + send_sig(SIGSEGV, current, 1); } void instruction_access_exception (struct pt_regs *regs) { - lock_kernel (); - printk ("Unhandled instruction access exception "); - printk("sfsr %016lx\n", spitfire_get_isfsr()); - die_if_kernel("Instruction access exception", regs); + send_sig(SIGSEGV, current, 1); } void do_iae(struct pt_regs *regs) { - printk("IAE at %016lx\n", regs->tpc); - while(1) - barrier(); -} - -static unsigned long init_fsr = 0x0UL; -static unsigned int init_fregs[64] __attribute__ ((aligned (64))) = - { ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, - ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, - ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, - ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, - ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, - ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, - ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, - ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U }; - -void do_fpdis(struct pt_regs *regs) -{ - lock_kernel(); - - regs->tstate |= TSTATE_PEF; - fprs_write(FPRS_FEF); - - /* This is allowed now because the V9 ABI varargs passes floating - * point args in floating point registers, so vsprintf() and sprintf() - * cause problems. Luckily we never actually pass floating point values - * to those routines in the kernel and the code generated just does - * stores of them to the stack. Therefore, for the moment this fix - * is sufficient. -DaveM - */ - if(regs->tstate & TSTATE_PRIV) - goto out; - -#ifndef __SMP__ - if(last_task_used_math == current) - goto out; - if(last_task_used_math) { - struct task_struct *fptask = last_task_used_math; - - if(fptask->tss.flags & SPARC_FLAG_32BIT) - fpsave32((unsigned long *)&fptask->tss.float_regs[0], - &fptask->tss.fsr); - else - fpsave((unsigned long *)&fptask->tss.float_regs[0], - &fptask->tss.fsr); - } - last_task_used_math = current; - if(current->used_math) { - if(current->tss.flags & SPARC_FLAG_32BIT) - fpload32(¤t->tss.float_regs[0], - ¤t->tss.fsr); - else - fpload(¤t->tss.float_regs[0], - ¤t->tss.fsr); - } else { - /* Set inital sane state. */ - fpload(&init_fregs[0], &init_fsr); - current->used_math = 1; - } -#else - if(!current->used_math) { - fpload(&init_fregs[0], &init_fsr); - current->used_math = 1; - } else { - if(current->tss.flags & SPARC_FLAG_32BIT) - fpload32(¤t->tss.float_regs[0], - ¤t->tss.fsr); - else - fpload(¤t->tss.float_regs[0], - ¤t->tss.fsr); - } - current->flags |= PF_USEDFPU; -#endif -#ifndef __SMP__ -out: -#endif - unlock_kernel(); + send_sig(SIGSEGV, current, 1); } -static unsigned long fake_regs[32] __attribute__ ((aligned (8))); -static unsigned long fake_fsr; - void do_fpe_common(struct pt_regs *regs) { - static int calls = 0; -#ifndef __SMP__ - struct task_struct *fpt = last_task_used_math; -#else - struct task_struct *fpt = current; -#endif - - lock_kernel(); - fprs_write(FPRS_FEF); - -#ifndef __SMP__ - if(!fpt) { -#else - if(!(fpt->flags & PF_USEDFPU)) { -#endif - fpsave(&fake_regs[0], &fake_fsr); - regs->tstate &= ~(TSTATE_PEF); - goto out; - } - if(fpt->tss.flags & SPARC_FLAG_32BIT) - fpsave32((unsigned long *)&fpt->tss.float_regs[0], &fpt->tss.fsr); - else - fpsave((unsigned long *)&fpt->tss.float_regs[0], &fpt->tss.fsr); - fpt->tss.sig_address = regs->tpc; - fpt->tss.sig_desc = SUBSIG_FPERROR; -#ifdef __SMP__ - fpt->flags &= ~PF_USEDFPU; -#endif if(regs->tstate & TSTATE_PRIV) { - printk("WARNING: FPU exception from kernel mode. at pc=%016lx\n", - regs->tpc); regs->tpc = regs->tnpc; regs->tnpc += 4; - calls++; - if(calls > 2) - die_if_kernel("Too many Penguin-FPU traps from kernel mode", - regs); - goto out; + } else { + lock_kernel(); + current->tss.sig_address = regs->tpc; + current->tss.sig_desc = SUBSIG_FPERROR; + send_sig(SIGFPE, current, 1); + unlock_kernel(); } - send_sig(SIGFPE, fpt, 1); -#ifndef __SMP__ - last_task_used_math = NULL; -#endif - regs->tstate &= ~TSTATE_PEF; - if(calls > 0) - calls = 0; -out: - unlock_kernel(); } void do_fpieee(struct pt_regs *regs) @@ -397,16 +235,16 @@ void do_fpother(struct pt_regs *regs) void do_tof(struct pt_regs *regs) { - printk("TOF: at %016lx\n", regs->tpc); - while(1) - barrier(); + if(regs->tstate & TSTATE_PRIV) + die_if_kernel("Penguin overflow trap from kernel mode", regs); + current->tss.sig_address = regs->tpc; + current->tss.sig_desc = SUBSIG_TAG; /* as good as any */ + send_sig(SIGEMT, current, 1); } void do_div0(struct pt_regs *regs) { - printk("DIV0: at %016lx\n", regs->tpc); - while(1) - barrier(); + send_sig(SIGILL, current, 1); } void instruction_dump (unsigned int *pc) @@ -426,7 +264,7 @@ void die_if_kernel(char *str, struct pt_regs *regs) /* Amuse the user. */ printk( " \\|/ ____ \\|/\n" -" \"@'/ .` \\`@\"\n" +" \"@'/ .. \\`@\"\n" " /_| \\__/ |_\\\n" " \\__U_/\n"); @@ -437,17 +275,15 @@ void die_if_kernel(char *str, struct pt_regs *regs) struct reg_window *rw = (struct reg_window *) (regs->u_regs[UREG_FP] + STACK_BIAS); - if(rw) { + /* Stop the back trace when we hit userland or we + * find some badly aligned kernel stack. + */ + while(rw && + (((unsigned long) rw) >= PAGE_OFFSET) && + !(((unsigned long) rw) & 0x7)) { printk("Caller[%016lx]\n", rw->ins[7]); rw = (struct reg_window *) (rw->ins[6] + STACK_BIAS); - if(rw) { - printk("Caller[%016lx]\n", rw->ins[7]); - rw = (struct reg_window *) - (rw->ins[6] + STACK_BIAS); - if(rw) - printk("Caller[%016lx]\n", rw->ins[7]); - } } } printk("Instruction DUMP:"); @@ -465,16 +301,6 @@ void do_illegal_instruction(struct pt_regs *regs) lock_kernel(); if(tstate & TSTATE_PRIV) die_if_kernel("Kernel illegal instruction", regs); -#if 1 - { - unsigned int insn; - - printk("Ill instr. at pc=%016lx ", pc); - get_user(insn, ((unsigned int *)pc)); - printk("insn=[%08x]\n", insn); - show_regs(regs); - } -#endif current->tss.sig_address = pc; current->tss.sig_desc = SUBSIG_ILLINST; send_sig(SIGILL, current, 1); @@ -483,13 +309,11 @@ void do_illegal_instruction(struct pt_regs *regs) void mem_address_unaligned(struct pt_regs *regs) { - printk("AIEEE: do_mna at %016lx\n", regs->tpc); - show_regs(regs); if(regs->tstate & TSTATE_PRIV) { - printk("MNA from kernel, spinning\n"); - sti(); - while(1) - barrier(); + extern void kernel_unaligned_trap(struct pt_regs *regs, + unsigned int insn); + + return kernel_unaligned_trap(regs, *((unsigned int *)regs->tpc)); } else { current->tss.sig_address = regs->tpc; current->tss.sig_desc = SUBSIG_PRIVINST; @@ -499,16 +323,17 @@ void mem_address_unaligned(struct pt_regs *regs) void do_privop(struct pt_regs *regs) { - printk("PRIVOP: at %016lx\n", regs->tpc); - while(1) - barrier(); + current->tss.sig_address = regs->tpc; + current->tss.sig_desc = SUBSIG_PRIVINST; + send_sig(SIGILL, current, 1); } void do_privact(struct pt_regs *regs) { - printk("PRIVACT: at %016lx\n", regs->tpc); - while(1) - barrier(); + current->tss.sig_address = regs->tpc; + current->tss.sig_desc = SUBSIG_PRIVINST; + send_sig(SIGILL, current, 1); + unlock_kernel(); } void do_priv_instruction(struct pt_regs *regs, unsigned long pc, unsigned long npc, @@ -537,11 +362,6 @@ void do_memaccess_unaligned(struct pt_regs *regs, unsigned long pc, unsigned lon } current->tss.sig_address = pc; current->tss.sig_desc = SUBSIG_PRIVINST; -#if 0 - show_regs (regs); - instruction_dump ((unsigned long *) regs->tpc); - printk ("do_MNA!\n"); -#endif send_sig(SIGBUS, current, 1); unlock_kernel(); } @@ -554,6 +374,134 @@ void handle_hw_divzero(struct pt_regs *regs, unsigned long pc, unsigned long npc unlock_kernel(); } +/* Trap level 1 stuff or other traps we should never see... */ +void do_cee(struct pt_regs *regs) +{ + die_if_kernel("TL0: Cache Error Exception", regs); +} + +void do_cee_tl1(struct pt_regs *regs) +{ + die_if_kernel("TL1: Cache Error Exception", regs); +} + +void do_dae_tl1(struct pt_regs *regs) +{ + die_if_kernel("TL1: Data Access Exception", regs); +} + +void do_iae_tl1(struct pt_regs *regs) +{ + die_if_kernel("TL1: Instruction Access Exception", regs); +} + +void do_div0_tl1(struct pt_regs *regs) +{ + die_if_kernel("TL1: DIV0 Exception", regs); +} + +void do_fpdis_tl1(struct pt_regs *regs) +{ + die_if_kernel("TL1: FPU Disabled", regs); +} + +void do_fpieee_tl1(struct pt_regs *regs) +{ + die_if_kernel("TL1: FPU IEEE Exception", regs); +} + +void do_fpother_tl1(struct pt_regs *regs) +{ + die_if_kernel("TL1: FPU Other Exception", regs); +} + +void do_ill_tl1(struct pt_regs *regs) +{ + die_if_kernel("TL1: Illegal Instruction Exception", regs); +} + +void do_irq_tl1(struct pt_regs *regs) +{ + die_if_kernel("TL1: IRQ Exception", regs); +} + +void do_lddfmna(struct pt_regs *regs) +{ + die_if_kernel("TL0: LDDF Exception", regs); +} + +void do_lddfmna_tl1(struct pt_regs *regs) +{ + die_if_kernel("TL1: LDDF Exception", regs); +} + +void do_stdfmna(struct pt_regs *regs) +{ + die_if_kernel("TL0: STDF Exception", regs); +} + +void do_stdfmna_tl1(struct pt_regs *regs) +{ + die_if_kernel("TL1: STDF Exception", regs); +} + +void do_paw(struct pt_regs *regs) +{ + die_if_kernel("TL0: Phys Watchpoint Exception", regs); +} + +void do_paw_tl1(struct pt_regs *regs) +{ + die_if_kernel("TL1: Phys Watchpoint Exception", regs); +} + +void do_vaw(struct pt_regs *regs) +{ + die_if_kernel("TL0: Virt Watchpoint Exception", regs); +} + +void do_vaw_tl1(struct pt_regs *regs) +{ + die_if_kernel("TL1: Virt Watchpoint Exception", regs); +} + +void do_tof_tl1(struct pt_regs *regs) +{ + die_if_kernel("TL1: Tag Overflow Exception", regs); +} + +#ifdef CONFIG_EC_FLUSH_TRAP +void cache_flush_trap(struct pt_regs *regs) +{ +#ifndef __SMP__ + unsigned node = linux_cpus[get_cpuid()].prom_node; +#else +#error SMP not supported on sparc64 yet +#endif + int size = prom_getintdefault(node, "ecache-size", 512*1024); + int i, j; + unsigned long addr, page_nr; + + regs->tpc = regs->tnpc; + regs->tnpc = regs->tnpc + 4; + if (!suser()) return; + size >>= PAGE_SHIFT; + addr = PAGE_OFFSET - PAGE_SIZE; + for (i = 0; i < size; i++) { + do { + addr += PAGE_SIZE; + page_nr = MAP_NR(addr); + if (page_nr >= max_mapnr) { + return; + } + } while (!PageReserved (mem_map + page_nr)); + /* E-Cache line size is 64B. Let us pollute it :)) */ + for (j = 0; j < PAGE_SIZE; j += 64) + __asm__ __volatile__ ("ldx [%0 + %1], %%g1" : : "r" (j), "r" (addr) : "g1"); + } +} +#endif + void trap_init(void) { } diff --git a/arch/sparc64/kernel/ttable.S b/arch/sparc64/kernel/ttable.S index 8db708f076dd..73bda96d9ea8 100644 --- a/arch/sparc64/kernel/ttable.S +++ b/arch/sparc64/kernel/ttable.S @@ -1,9 +1,11 @@ -/* $Id: ttable.S,v 1.13 1997/06/02 06:33:34 davem Exp $ +/* $Id: ttable.S,v 1.18 1997/07/05 09:52:41 davem Exp $ * ttable.S: Sparc V9 Trap Table(s) with SpitFire extensions. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) */ +#include + .globl sparc64_ttable_tl0, sparc64_ttable_tl1 sparc64_ttable_tl0: @@ -18,7 +20,7 @@ tl0_privop: TRAP(do_privop) tl0_resv012: BTRAP(0x12) BTRAP(0x13) BTRAP(0x14) BTRAP(0x15) BTRAP(0x16) BTRAP(0x17) tl0_resv018: BTRAP(0x18) BTRAP(0x19) BTRAP(0x1a) BTRAP(0x1b) BTRAP(0x1c) BTRAP(0x1d) tl0_resv01e: BTRAP(0x1e) BTRAP(0x1f) -tl0_fpdis: TRAP(do_fpdis) +tl0_fpdis: TRAP_NOSAVE(do_fpdis) tl0_fpieee: TRAP(do_fpieee) tl0_fpother: TRAP(do_fpother) tl0_tof: TRAP(do_tof) @@ -124,7 +126,13 @@ tl0_resv15a: BTRAP(0x15a) BTRAP(0x15b) BTRAP(0x15c) BTRAP(0x15d) BTRAP(0x15e) tl0_resv15f: BTRAP(0x15f) BTRAP(0x160) BTRAP(0x161) BTRAP(0x162) BTRAP(0x163) tl0_resv164: BTRAP(0x164) BTRAP(0x165) BTRAP(0x166) BTRAP(0x167) BTRAP(0x168) tl0_resv169: BTRAP(0x169) BTRAP(0x16a) BTRAP(0x16b) BTRAP(0x16c) BTRAP(0x16d) -tl0_resv16e: BTRAP(0x16e) BTRAP(0x16f) BTRAP(0x170) BTRAP(0x171) BTRAP(0x172) +tl0_gsctx: TRAP(sparc64_get_context) TRAP(sparc64_set_context) +tl0_resv170: BTRAP(0x170) BTRAP(0x171) +#ifdef CONFIG_EC_FLUSH_TRAP + TRAP(cache_flush_trap) +#else + BTRAP(0x172) +#endif tl0_resv173: BTRAP(0x173) BTRAP(0x174) BTRAP(0x175) BTRAP(0x176) BTRAP(0x177) tl0_resv178: BTRAP(0x178) BTRAP(0x179) BTRAP(0x17a) BTRAP(0x17b) BTRAP(0x17c) tl0_resv17d: BTRAP(0x17d) BTRAP(0x17e) BTRAP(0x17f) @@ -151,7 +159,7 @@ tl1_resv012: BTRAPTL1(0x12) BTRAPTL1(0x13) BTRAPTL1(0x14) BTRAPTL1(0x15) tl1_resv016: BTRAPTL1(0x16) BTRAPTL1(0x17) BTRAPTL1(0x18) BTRAPTL1(0x19) tl1_resv01a: BTRAPTL1(0x1a) BTRAPTL1(0x1b) BTRAPTL1(0x1c) BTRAPTL1(0x1d) tl1_resv01e: BTRAPTL1(0x1e) BTRAPTL1(0x1f) -tl1_fpdis: TRAPTL1(do_fpdis_tl1) +tl1_fpdis: TRAP_NOSAVE(do_fpdis) tl1_fpieee: TRAPTL1(do_fpieee_tl1) tl1_fpother: TRAPTL1(do_fpother_tl1) tl1_tof: TRAPTL1(do_tof_tl1) diff --git a/arch/sparc64/kernel/unaligned.c b/arch/sparc64/kernel/unaligned.c index 685f8c892afe..12f90c21ca52 100644 --- a/arch/sparc64/kernel/unaligned.c +++ b/arch/sparc64/kernel/unaligned.c @@ -1,4 +1,4 @@ -/* $Id: unaligned.c,v 1.1 1997/06/06 10:56:19 jj Exp $ +/* $Id: unaligned.c,v 1.3 1997/06/25 10:12:15 jj Exp $ * unaligned.c: Unaligned load/store trap handling with special * cases for the kernel to do them more quickly. * @@ -18,7 +18,7 @@ #include #include -#define DEBUG_MNA +/* #define DEBUG_MNA */ enum direction { load, /* ld, ldd, ldh, ldsh */ @@ -205,19 +205,19 @@ __asm__ __volatile__ ( \ "stx %%g7, [%0 + 8]\n" \ "0:\n\n\t" \ ".section __ex_table\n\t" \ - ".word 4b, " #errh "\n\t" \ - ".word 5b, " #errh "\n\t" \ - ".word 6b, " #errh "\n\t" \ - ".word 7b, " #errh "\n\t" \ - ".word 8b, " #errh "\n\t" \ - ".word 9b, " #errh "\n\t" \ - ".word 10b, " #errh "\n\t" \ - ".word 11b, " #errh "\n\t" \ - ".word 12b, " #errh "\n\t" \ - ".word 13b, " #errh "\n\t" \ - ".word 14b, " #errh "\n\t" \ - ".word 15b, " #errh "\n\t" \ - ".word 16b, " #errh "\n\n\t" \ + ".xword 4b, " #errh "\n\t" \ + ".xword 5b, " #errh "\n\t" \ + ".xword 6b, " #errh "\n\t" \ + ".xword 7b, " #errh "\n\t" \ + ".xword 8b, " #errh "\n\t" \ + ".xword 9b, " #errh "\n\t" \ + ".xword 10b, " #errh "\n\t" \ + ".xword 11b, " #errh "\n\t" \ + ".xword 12b, " #errh "\n\t" \ + ".xword 13b, " #errh "\n\t" \ + ".xword 14b, " #errh "\n\t" \ + ".xword 15b, " #errh "\n\t" \ + ".xword 16b, " #errh "\n\n\t" \ ".previous\n\t" \ : : "r" (dest_reg), "r" (size), "r" (saddr), "r" (is_signed), "r" (asi) \ : "l1", "l2", "g7", "g1", "cc"); \ @@ -259,20 +259,20 @@ __asm__ __volatile__ ( \ "17:\t" "stba %%l1, [%0 + 1] %%asi\n" \ "0:\n\n\t" \ ".section __ex_table\n\t" \ - ".word 4b, " #errh "\n\t" \ - ".word 5b, " #errh "\n\t" \ - ".word 6b, " #errh "\n\t" \ - ".word 7b, " #errh "\n\t" \ - ".word 8b, " #errh "\n\t" \ - ".word 9b, " #errh "\n\t" \ - ".word 10b, " #errh "\n\t" \ - ".word 11b, " #errh "\n\t" \ - ".word 12b, " #errh "\n\t" \ - ".word 13b, " #errh "\n\t" \ - ".word 14b, " #errh "\n\t" \ - ".word 15b, " #errh "\n\t" \ - ".word 16b, " #errh "\n\t" \ - ".word 17b, " #errh "\n\n\t" \ + ".xword 4b, " #errh "\n\t" \ + ".xword 5b, " #errh "\n\t" \ + ".xword 6b, " #errh "\n\t" \ + ".xword 7b, " #errh "\n\t" \ + ".xword 8b, " #errh "\n\t" \ + ".xword 9b, " #errh "\n\t" \ + ".xword 10b, " #errh "\n\t" \ + ".xword 11b, " #errh "\n\t" \ + ".xword 12b, " #errh "\n\t" \ + ".xword 13b, " #errh "\n\t" \ + ".xword 14b, " #errh "\n\t" \ + ".xword 15b, " #errh "\n\t" \ + ".xword 16b, " #errh "\n\t" \ + ".xword 17b, " #errh "\n\n\t" \ ".previous\n\t" \ : : "r" (dst_addr), "r" (size), "r" (src_val), "r" (asi) \ : "l1", "l2", "g7", "g1", "cc"); \ diff --git a/arch/sparc64/kernel/winfixup.S b/arch/sparc64/kernel/winfixup.S index 2ac19a440260..9502ac403694 100644 --- a/arch/sparc64/kernel/winfixup.S +++ b/arch/sparc64/kernel/winfixup.S @@ -1,4 +1,4 @@ -/* $Id: winfixup.S,v 1.8 1997/06/02 06:33:35 davem Exp $ +/* $Id: winfixup.S,v 1.15 1997/07/04 01:41:07 davem Exp $ * * winfixup.S: Handle cases where user stack pointer is found to be bogus. * @@ -31,6 +31,7 @@ fill_fixup: rdpr %tstate, %g1 andcc %g1, TSTATE_PRIV, %g0 + clr %g4 be,pt %xcc, window_scheisse_from_user_common and %g1, TSTATE_CWP, %g1 @@ -53,25 +54,26 @@ fill_fixup: rdpr %wstate, %g2 ! Grab user mode wstate. wrpr %g1, %cwp ! Get into the right window. sll %g2, 3, %g2 ! NORMAL-->OTHER - wrpr %g0, 0x0, %canrestore ! Standard etrap stuff. + wrpr %g0, 0x0, %canrestore ! Standard etrap stuff. + wr %g0, 0x0, %fprs ! zap FPU just in case... wrpr %g2, 0x0, %wstate ! This must be consistant. wrpr %g0, 0x0, %otherwin ! We know this. - sethi %uhi(KERNBASE), %g2 ! Set this up - sllx %g2, 32, %g2 ! for the iflush mov PRIMARY_CONTEXT, %g1 ! Change contexts... stxa %g0, [%g1] ASI_DMMU ! Back into the nucleus. - flush %g2 ! Flush instruction buffers + flush %g6 ! Flush instruction buffers rdpr %pstate, %l1 ! Prepare to change globals. - mov %g4, %o5 ! Setup args for - mov %g5, %o4 ! final call to do_sparc64_fault. + mov %g6, %o7 ! Get current. + mov %g5, %l5 ! Fault address + clr %l4 ! It was a load, not a store wrpr %g0, 0x0, %tl ! Out of trap levels. wrpr %l1, (PSTATE_IE | PSTATE_AG), %pstate - sethi %uhi(KERNBASE), %g4 ! Restore med-any global reg. - rd %pic, %g6 ! Get current as well. + sethi %uhi(PAGE_OFFSET), %g4 ! Prepare page_offset global reg + mov %o7, %g6 b,pt %xcc, window_scheisse_merge ! And merge. - sllx %g4, 32, %g4 ! Finish med-any reg setup. + + sllx %g4, 32, %g4 ! and finish it... /* Be very careful about usage of the alternate globals here. * You cannot touch %g4/%g5 as that has the fault information @@ -82,17 +84,16 @@ fill_fixup: * do not touch %g7 or %g2 so we handle the two cases fine. */ spill_fixup: - rd %pic, %g1 - ldx [%g1 + AOFF_task_tss + AOFF_thread_flags], %g6 - andcc %g6, SPARC_FLAG_32BIT, %g0 - ldx [%g1 + AOFF_task_tss + AOFF_thread_w_saved], %g6 - sll %g6, 3, %g3 - add %g1, %g3, %g3 + ld [%g6 + AOFF_task_tss + AOFF_thread_flags], %g1 + andcc %g1, SPARC_FLAG_32BIT, %g0 + ldx [%g6 + AOFF_task_tss + AOFF_thread_w_saved], %g1 + sll %g1, 3, %g3 + add %g6, %g3, %g3 stx %sp, [%g3 + AOFF_task_tss + AOFF_thread_rwbuf_stkptrs] - sll %g6, 7, %g3 + sll %g1, 7, %g3 bne,pt %xcc, 1f - add %g1, %g3, %g3 + add %g6, %g3, %g3 stx %l0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x00] stx %l1, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x08] stx %l2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x10] @@ -110,43 +111,45 @@ spill_fixup: stx %i5, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x68] stx %i6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x70] - stx %i7, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x78] b,pt %xcc, 2f - add %g6, 1, %g6 -1: std %l0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x00] - std %l2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x08] - std %l4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x10] - std %l6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x18] - - std %i0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x20] - std %i2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x28] - std %i4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x30] - std %i6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x38] - add %g6, 1, %g6 -2: stx %g6, [%g1 + AOFF_task_tss + AOFF_thread_w_saved] + stx %i7, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x78] +1: stw %l0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x00] + stw %l1, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x04] + stw %l2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x08] + stw %l3, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x0c] + stw %l4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x10] + + stw %l5, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x14] + stw %l6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x18] + stw %l7, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x1c] + stw %i0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x20] + stw %i1, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x24] + stw %i2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x28] + stw %i3, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x2c] + stw %i4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x30] + + stw %i5, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x34] + stw %i6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x38] + stw %i7, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x3c] +2: add %g1, 1, %g1 + stx %g1, [%g6 + AOFF_task_tss + AOFF_thread_w_saved] rdpr %tstate, %g1 - nop - andcc %g1, TSTATE_PRIV, %g0 saved + and %g1, TSTATE_CWP, %g1 be,a,pn %xcc, window_scheisse_from_user_common or %g4, 0x4, %g4 ! we know it was a write retry window_scheisse_from_user_common: - nop wrpr %g1, %cwp - ba,pt %xcc, etrap rd %pc, %g7 - mov %l5, %o4 - mov %l4, %o5 window_scheisse_merge: - srlx %o4, PAGE_SHIFT, %o3 - clr %o1 - sllx %o3, PAGE_SHIFT, %o3 - and %o5, 0x4, %o2 + srlx %l5, PAGE_SHIFT, %o1 + and %l4, 0x4, %o2 + sllx %o1, PAGE_SHIFT, %o1 call do_sparc64_fault add %sp, STACK_BIAS + REGWIN_SZ, %o0 ba,pt %xcc, rtrap @@ -154,6 +157,7 @@ window_scheisse_merge: winfix_trampoline: andn %g3, 0x7f, %g3 add %g3, 0x7c, %g3 + wrpr %g3, %tnpc done @@ -174,32 +178,31 @@ fill_fixup_mna: wrpr %g0, 0x0, %canrestore ! Standard etrap stuff. wrpr %g2, 0x0, %wstate ! This must be consistant. wrpr %g0, 0x0, %otherwin ! We know this. - sethi %uhi(KERNBASE), %g2 ! Set this up - sllx %g2, 32, %g2 ! for the iflush mov PRIMARY_CONTEXT, %g1 ! Change contexts... stxa %g0, [%g1] ASI_DMMU ! Back into the nucleus. - flush %g2 ! Flush instruction buffers + flush %g6 ! Flush instruction buffers rdpr %pstate, %l1 ! Prepare to change globals. mov %g4, %o5 ! Setup args for mov %g5, %o4 ! final call to do_sparc64_fault. + mov %g6, %o7 ! Stash away current. wrpr %g0, 0x0, %tl ! Out of trap levels. wrpr %l1, (PSTATE_IE | PSTATE_AG), %pstate - sethi %uhi(KERNBASE), %g4 ! Restore med-any global reg. - rd %pic, %g6 ! Get current as well. + sethi %uhi(PAGE_OFFSET), %g4 ! Set page_offset global reg. + mov %o7, %g6 ! Get current back. b,pt %xcc, window_mna_merge ! And merge. - sllx %g4, 32, %g4 ! Finish med-any reg setup. + sllx %g4, 32, %g4 ! Finish it. + spill_fixup_mna: - rd %pic, %g1 - ldx [%g1 + AOFF_task_tss + AOFF_thread_flags], %g6 - andcc %g6, SPARC_FLAG_32BIT, %g0 - ldx [%g1 + AOFF_task_tss + AOFF_thread_w_saved], %g6 - sll %g6, 3, %g3 - add %g1, %g3, %g3 + ld [%g6 + AOFF_task_tss + AOFF_thread_flags], %g1 + andcc %g1, SPARC_FLAG_32BIT, %g0 + ldx [%g6 + AOFF_task_tss + AOFF_thread_w_saved], %g1 + sll %g1, 3, %g3 + add %g6, %g3, %g3 stx %sp, [%g3 + AOFF_task_tss + AOFF_thread_rwbuf_stkptrs] - sll %g6, 7, %g3 + sll %g1, 7, %g3 bne,pt %xcc, 1f - add %g1, %g3, %g3 + add %g6, %g3, %g3 stx %l0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x00] stx %l1, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x08] stx %l2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x10] @@ -219,7 +222,7 @@ spill_fixup_mna: stx %i6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x70] stx %i7, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x78] b,pt %xcc, 2f - add %g6, 1, %g6 + add %g1, 1, %g1 1: std %l0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x00] std %l2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x08] std %l4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x10] @@ -229,8 +232,8 @@ spill_fixup_mna: std %i2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x28] std %i4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x30] std %i6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x38] - add %g6, 1, %g6 -2: stx %g6, [%g1 + AOFF_task_tss + AOFF_thread_w_saved] + add %g1, 1, %g1 +2: stx %g1, [%g6 + AOFF_task_tss + AOFF_thread_w_saved] rdpr %tstate, %g1 nop diff --git a/arch/sparc64/lib/Makefile b/arch/sparc64/lib/Makefile index 56c506507cb9..2377d99e26ba 100644 --- a/arch/sparc64/lib/Makefile +++ b/arch/sparc64/lib/Makefile @@ -1,55 +1,25 @@ -# $Id: Makefile,v 1.7 1997/04/07 18:57:05 jj Exp $ +# $Id: Makefile,v 1.12 1997/06/25 10:12:18 jj Exp $ # Makefile for Sparc library files.. # CFLAGS := $(CFLAGS) -ansi -OBJS = memset.o blockops.o locks.o memcpy.o strlen.o strncmp.o \ +OBJS = blockops.o locks.o strlen.o strncmp.o \ memscan.o strncpy_from_user.o strlen_user.o memcmp.o checksum.o \ - copy_to_user.o copy_from_user.o + VIScopy.o VISbzero.o VISmemset.o lib.a: $(OBJS) $(AR) rcs lib.a $(OBJS) sync -blockops.o: blockops.S - $(CC) -ansi -c -o blockops.o blockops.S +VIScopy.o: VIScopy.S VIS.h +VISbzero.o: VISbzero.S VIS.h -memset.o: memset.S - $(CC) -D__ASSEMBLY__ -ansi -c -o memset.o memset.S +.S.s: + $(CPP) -D__ASSEMBLY__ -ansi $< -o $*.s -copy_to_user.o: copy_to_user.S - $(CC) -D__ASSEMBLY__ -ansi -c -o copy_to_user.o copy_to_user.S - -copy_from_user.o: copy_from_user.S - $(CC) -D__ASSEMBLY__ -ansi -c -o copy_from_user.o copy_from_user.S - -memcpy.o: memcpy.S - $(CC) -D__ASSEMBLY__ -ansi -c -o memcpy.o memcpy.S - -strlen.o: strlen.S - $(CC) -D__ASSEMBLY__ -ansi -c -o strlen.o strlen.S - -strncmp.o: strncmp.S - $(CC) -D__ASSEMBLY__ -ansi -c -o strncmp.o strncmp.S - -memcmp.o: memcmp.S - $(CC) -D__ASSEMBLY__ -ansi -c -o memcmp.o memcmp.S - -locks.o: locks.S - $(CC) -D__ASSEMBLY__ -ansi -c -o locks.o locks.S - -checksum.o: checksum.S - $(CC) -D__ASSEMBLY__ -ansi -c -o checksum.o checksum.S - -memscan.o: memscan.S - $(CC) -D__ASSEMBLY__ -ansi -c -o memscan.o memscan.S - -strncpy_from_user.o: strncpy_from_user.S - $(CC) -D__ASSEMBLY__ -ansi -c -o strncpy_from_user.o strncpy_from_user.S - -strlen_user.o: strlen_user.S - $(CC) -D__ASSEMBLY__ -ansi -c -o strlen_user.o strlen_user.S +.S.o: + $(CC) -D__ASSEMBLY__ -ansi -c $< -o $*.o dep: diff --git a/arch/sparc64/lib/VIS.h b/arch/sparc64/lib/VIS.h new file mode 100644 index 000000000000..01232a82b142 --- /dev/null +++ b/arch/sparc64/lib/VIS.h @@ -0,0 +1,113 @@ +/* $Id: VIS.h,v 1.3 1997/06/27 14:53:18 jj Exp $ + * VIS.h: High speed copy/clear operations utilizing the UltraSparc + * Visual Instruction Set. + * + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1996, 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + */ + + /* VIS code can be used for numerous copy/set operation variants. + * It can be made to work in the kernel, one single instance, + * for all of memcpy, copy_to_user, and copy_from_user by setting + * the ASI src/dest globals correctly. Furthermore it can + * be used for kernel-->kernel page copies as well, a hook label + * is put in here just for this purpose. + * + * For userland, compiling this without __KERNEL__ defined makes + * it work just fine as a generic libc bcopy and memcpy. + * If for userland it is compiled with a 32bit gcc (but you need + * -Wa,-Av9a), the code will just rely on lower 32bits of + * IEU registers, if you compile it with 64bit gcc (ie. define + * __sparc_v9__), the code will use full 64bit. + */ + +#ifndef __VIS_H +#define __VIS_H + +#ifdef __KERNEL__ +#include +#include +#else +#define ASI_P 0x80 /* Primary, implicit */ +#define ASI_S 0x81 /* Secondary, implicit */ +#define ASI_BLK_COMMIT_P 0xe0 /* Primary, blk store commit */ +#define ASI_BLK_COMMIT_S 0xe1 /* Secondary, blk store commit */ +#define ASI_BLK_P 0xf0 /* Primary, blk ld/st */ +#define ASI_BLK_S 0xf1 /* Secondary, blk ld/st */ +#define FPRS_FEF 0x04 +#endif + + /* I'm telling you, they really did this chip right. + * Perhaps the SunSoft folks should visit some of the + * people in Sun Microelectronics and start some brain + * cell exchange program... + */ +#define ASI_BLK_XOR (ASI_P ^ ASI_BLK_P) + +#define asi_src %o3 +#define asi_dest %o4 + +#ifdef __KERNEL__ +#define ASI_SETSRC_BLK wr asi_src, 0, %asi; +#define ASI_SETSRC_NOBLK wr asi_src, ASI_BLK_XOR, %asi; +#define ASI_SETDST_BLK wr asi_dest, 0, %asi; +#define ASI_SETDST_NOBLK wr asi_dest, ASI_BLK_XOR, %asi; +#define ASIBLK %asi +#define ASINORMAL %asi +#define LDUB lduba +#define LDUH lduha +#define LDUW lduwa +#define LDX ldxa +#define LDD ldda +#define LDDF ldda +#define LDBLK ldda +#define STB stba +#define STH stha +#define STW stwa +#define STD stda +#define STX stxa +#define STDF stda +#define STBLK stda +#else +#define ASI_SETSRC_BLK +#define ASI_SETSRC_NOBLK +#define ASI_SETDST_BLK +#define ASI_SETDST_NOBLK +#define ASI_SETDST_SPECIAL +#define ASIBLK %asi +#define ASINORMAL +#define LDUB ldub +#define LDUH lduh +#define LDUW lduw +#define LDD ldd +#define LDX ldx +#define LDDF ldd +#define LDBLK ldda +#define STB stb +#define STH sth +#define STW stw +#define STD std +#define STX stx +#define STDF std +#define STBLK stda +#endif + +#ifdef __KERNEL__ + +#define REGS_64BIT + +#else + +#ifndef REGS_64BIT +#ifdef __sparc_v9__ +#define REGS_64BIT +#endif +#endif + +#endif + +#ifndef REGS_64BIT +#define xcc icc +#endif + +#endif diff --git a/arch/sparc64/lib/VISbzero.S b/arch/sparc64/lib/VISbzero.S new file mode 100644 index 000000000000..1afaf35300c7 --- /dev/null +++ b/arch/sparc64/lib/VISbzero.S @@ -0,0 +1,246 @@ +/* $Id: VISbzero.S,v 1.4 1997/06/28 17:21:21 jj Exp $ + * VISbzero.S: High speed clear operations utilizing the UltraSparc + * Visual Instruction Set. + * + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1996, 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + */ + +#include "VIS.h" + +#ifdef __KERNEL__ +#define EXN(x,y,a,b,z) \ +98: x,y; \ + .section .fixup; \ + .align 4; \ +99: ba VISbzerofixup_ret##z; \ + a, b, %o0; \ + .section __ex_table; \ + .align 8; \ + .xword 98b, 99b; \ + .text; \ + .align 4; +#define EXC(x,y,a,b,c...) \ +98: x,y; \ + .section .fixup; \ + .align 4; \ +99: c; \ + ba VISbzerofixup_ret0; \ + a, b, %o0; \ + .section __ex_table; \ + .align 8; \ + .xword 98b, 99b; \ + .text; \ + .align 4; +#define EXO1(x,y) \ +98: x,y; \ + .section __ex_table; \ + .align 8; \ + .xword 98b, VISbzerofixup_reto1; \ + .text; \ + .align 4; +#define EX(x,y,a,b) EXN(x,y,a,b,0) +#define EX1(x,y,a,b) EXN(x,y,a,b,1) +#define EX2(x,y,a,b) EXN(x,y,a,b,2) +#define EXT(start,end,handler) \ + .section __ex_table; \ + .align 8; \ + .xword start, 0, end, handler; \ + .text; \ + .align 4 +#else +#define EX(x,y,a,b) x,y +#define EX1(x,y,a,b) x,y +#define EX2(x,y,a,b) x,y +#define EXC(x,y,a,b,c...) x,y +#define EXO1(x,y) x,y +#define EXT(a,b,c) +#endif + +#define ZERO_BLOCKS(base, offset, source) \ + STX source, [base - offset - 0x38] ASINORMAL; \ + STX source, [base - offset - 0x30] ASINORMAL; \ + STX source, [base - offset - 0x28] ASINORMAL; \ + STX source, [base - offset - 0x20] ASINORMAL; \ + STX source, [base - offset - 0x18] ASINORMAL; \ + STX source, [base - offset - 0x10] ASINORMAL; \ + STX source, [base - offset - 0x08] ASINORMAL; \ + STX source, [base - offset - 0x00] ASINORMAL; + +#ifdef __KERNEL__ +#define RETL clr %o0 +#else +#define RETL mov %g3, %o0 +#endif + + /* Well, bzero is a lot easier to get right than bcopy... */ +#ifdef __KERNEL__ + .section __ex_table,#alloc + .section .fixup,#alloc,#execinstr +#endif + .text + .align 32 +#ifdef __KERNEL__ + .globl __bzero, __bzero_noasi +__bzero: + wr %g0, ASI_P, %asi ! LSU Group +__bzero_noasi: +#else + .globl bzero +bzero_private: +bzero: +#ifndef REGS_64BIT + srl %o1, 0, %o1 +#endif + mov %o0, %g3 +#endif + cmp %o1, 7 + bleu,pn %xcc, 17f + andcc %o0, 3, %o2 + be,a,pt %xcc, 4f + andcc %o0, 4, %g0 + cmp %o2, 3 + be,pn %xcc, 2f + EXO1(STB %g0, [%o0 + 0x00] ASINORMAL) + cmp %o2, 2 + be,pt %xcc, 2f + EX(STB %g0, [%o0 + 0x01] ASINORMAL, sub %o1, 1) + EX(STB %g0, [%o0 + 0x02] ASINORMAL, sub %o1, 2) +2: sub %o2, 4, %o2 + sub %o0, %o2, %o0 + add %o1, %o2, %o1 + andcc %o0, 4, %g0 +4: be,pt %xcc, 2f + cmp %o1, 128 + EXO1(STW %g0, [%o0] ASINORMAL) + sub %o1, 4, %o1 + add %o0, 4, %o0 +2: blu,pn %xcc, 9f + andcc %o0, 0x38, %o2 + be,pn %icc, 6f + mov 64, %o5 + andcc %o0, 8, %g0 + be,pn %icc, 1f + sub %o5, %o2, %o5 + EX(STX %g0, [%o0] ASINORMAL, sub %o1, 0) + add %o0, 8, %o0 +1: andcc %o5, 16, %g0 + be,pn %icc, 1f + sub %o1, %o5, %o1 + EX1(STX %g0, [%o0] ASINORMAL, add %g0, 0) + EX1(STX %g0, [%o0 + 8] ASINORMAL, sub %g0, 8) + add %o0, 16, %o0 +1: andcc %o5, 32, %g0 + be,pn %icc, 7f + andncc %o1, 0x3f, %o3 + EX(STX %g0, [%o0] ASINORMAL, add %o1, 32) + EX(STX %g0, [%o0 + 8] ASINORMAL, add %o1, 24) + EX(STX %g0, [%o0 + 16] ASINORMAL, add %o1, 16) + EX(STX %g0, [%o0 + 24] ASINORMAL, add %o1, 8) + add %o0, 32, %o0 +6: andncc %o1, 0x3f, %o3 +7: be,pn %xcc, 9f +#ifdef __KERNEL__ + rd %asi, %g7 + wr %g0, FPRS_FEF, %fprs + wr %g7, ASI_BLK_XOR, %asi +#else + wr %g0, ASI_BLK_P, %asi +#endif + membar #StoreStore | #LoadStore + fzero %f0 + andcc %o3, 0xc0, %o2 + and %o1, 0x3f, %o1 + fzero %f2 + andn %o3, 0xff, %o3 + faddd %f0, %f2, %f4 + fmuld %f0, %f2, %f6 + cmp %o2, 64 + faddd %f0, %f2, %f8 + fmuld %f0, %f2, %f10 + faddd %f0, %f2, %f12 + brz,pn %o2, 10f + fmuld %f0, %f2, %f14 + be,pn %icc, 2f + EXC(STBLK %f0, [%o0 + 0x00] ASIBLK, add %o3, %o2, add %o2, %o1, %o2) + cmp %o2, 128 + be,pn %icc, 2f + EXC(STBLK %f0, [%o0 + 0x40] ASIBLK, add %o3, %o2, add %o2, %o1, %o2; sub %o2, 64, %o2) + EXC(STBLK %f0, [%o0 + 0x80] ASIBLK, add %o3, %o2, add %o2, %o1, %o2; sub %o2, 128, %o2) +2: brz,pn %o3, 12f + add %o0, %o2, %o0 +10: EX(STBLK %f0, [%o0 + 0x00] ASIBLK, add %o3, %o1) + EXC(STBLK %f0, [%o0 + 0x40] ASIBLK, add %o3, %o1, sub %o1, 64, %o1) + EXC(STBLK %f0, [%o0 + 0x80] ASIBLK, add %o3, %o1, sub %o1, 128, %o1) + EXC(STBLK %f0, [%o0 + 0xc0] ASIBLK, add %o3, %o1, sub %o1, 192, %o1) +11: subcc %o3, 256, %o3 + bne,pt %xcc, 10b + add %o0, 256, %o0 +12: +#ifdef __KERNEL__ + wr %g0, 0, %fprs + wr %g7, 0x0, %asi +#endif + membar #Sync +9: andcc %o1, 0xf8, %o2 + be,pn %xcc, 13f + andcc %o1, 7, %o1 +14: rd %pc, %o4 + srl %o2, 1, %o3 + sub %o4, %o3, %o4 + jmpl %o4 + (13f - 14b), %g0 + add %o0, %o2, %o0 +12: ZERO_BLOCKS(%o0, 0xc8, %g0) + ZERO_BLOCKS(%o0, 0x88, %g0) + ZERO_BLOCKS(%o0, 0x48, %g0) + ZERO_BLOCKS(%o0, 0x08, %g0) + EXT(12b,13f,VISbzerofixup_zb) +13: be,pn %xcc, 8f + andcc %o1, 4, %g0 + be,pn %xcc, 1f + andcc %o1, 2, %g0 + EX(STW %g0, [%o0] ASINORMAL, and %o1, 7) + add %o0, 4, %o0 +1: be,pn %xcc, 1f + andcc %o1, 1, %g0 + EX(STH %g0, [%o0] ASINORMAL, and %o1, 3) + add %o0, 2, %o0 +1: bne,a,pn %xcc, 8f + EX(STB %g0, [%o0] ASINORMAL, add %g0, 1) +8: retl + RETL +17: be,pn %xcc, 13b + orcc %o1, 0, %g0 + be,pn %xcc, 0f +8: add %o0, 1, %o0 + subcc %o1, 1, %o1 + bne,pt %xcc, 8b + EX(STB %g0, [%o0 - 1] ASINORMAL, add %o1, 1) +0: retl + RETL + +#ifdef __KERNEL__ + .section .fixup + .align 4 +VISbzerofixup_reto1: + mov %o1, %o0 +VISbzerofixup_ret0: + retl + wr %g0, 0, %fprs +VISbzerofixup_ret1: + and %o5, 0x30, %o5 + add %o5, %o1, %o5 + ba,pt %xcc, VISbzerofixup_ret0 + add %o0, %o5, %o0 +VISbzerofixup_ret2: + and %o5, 0x20, %o5 + add %o5, %o1, %o5 + ba,pt %xcc, VISbzerofixup_ret0 + add %o0, %o5, %o0 +VISbzerofixup_zb: + andcc %o1, 7, %o1 + sll %g2, 3, %g2 + add %o1, 256, %o1 + ba,pt %xcc, VISbzerofixup_ret0 + sub %o1, %g2, %o0 +#endif diff --git a/arch/sparc64/lib/VIScopy.S b/arch/sparc64/lib/VIScopy.S new file mode 100644 index 000000000000..ced9422c3575 --- /dev/null +++ b/arch/sparc64/lib/VIScopy.S @@ -0,0 +1,1052 @@ +/* $Id: VIScopy.S,v 1.8 1997/06/28 17:21:22 jj Exp $ + * VIScopy.S: High speed copy operations utilizing the UltraSparc + * Visual Instruction Set. + * + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1996, 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + */ + +#include "VIS.h" + + /* VIS code can be used for numerous copy/set operation variants. + * It can be made to work in the kernel, one single instance, + * for all of memcpy, copy_to_user, and copy_from_user by setting + * the ASI src/dest globals correctly. Furthermore it can + * be used for kernel-->kernel page copies as well, a hook label + * is put in here just for this purpose. + * + * For userland, compiling this without __KERNEL__ defined makes + * it work just fine as a generic libc bcopy and memcpy. + * If for userland it is compiled with a 32bit gcc (but you need + * -Wa,-Av9a for as), the code will just rely on lower 32bits of + * IEU registers, if you compile it with 64bit gcc (ie. define + * __sparc_v9__), the code will use full 64bit. + */ + +#ifdef __KERNEL__ +#define FPU_CLEAN_RETL \ + wr %g0, 0, %fprs; \ + retl; \ + clr %o0; +#define FPU_RETL \ + wr %g0, 0, %fprs; \ + retl; \ + clr %o0; +#define NORMAL_RETL \ + retl; \ + clr %o0; +#define EX(x,y,a,b) \ +98: x,y; \ + .section .fixup; \ + .align 4; \ +99: ba VIScopyfixup_ret; \ + a, b, %o0; \ + .section __ex_table; \ + .align 8; \ + .xword 98b, 99b; \ + .text; \ + .align 4; +#define EX2(x,y,c,d,e,a,b) \ +98: x,y; \ + .section .fixup; \ + .align 4; \ +99: c, d, e; \ + ba VIScopyfixup_ret; \ + a, b, %o0; \ + .section __ex_table; \ + .align 8; \ + .xword 98b, 99b; \ + .text; \ + .align 4; +#define EXO2(x,y) \ +98: x,y; \ + .section __ex_table; \ + .align 8; \ + .xword 98b, VIScopyfixup_reto2; \ + .text; \ + .align 4; +#define EXVISN(x,y,n) \ +98: x,y; \ + .section __ex_table; \ + .align 8; \ + .xword 98b, VIScopyfixup_vis##n; \ + .text; \ + .align 4; +#define EXT(start,end,handler) \ + .section __ex_table; \ + .align 8; \ + .xword start, 0, end, handler; \ + .text; \ + .align 4; +#else +#define FPU_CLEAN_RETL \ + retl; \ + mov %g6, %o0; +#define FPU_RETL \ + retl; \ + mov %g6, %o0; +#define NORMAL_RETL \ + retl; \ + mov %g6, %o0; +#define EX(x,y,a,b) x,y +#define EX2(x,y,c,d,e,a,b) x,y +#define EXO2(x,y) x,y +#define EXVISN(x,y,n) x,y +#define EXT(a,b,c) +#endif +#define EXVIS(x,y) EXVISN(x,y,0) +#define EXVIS1(x,y) EXVISN(x,y,1) +#define EXVIS2(x,y) EXVISN(x,y,2) +#define EXVIS3(x,y) EXVISN(x,y,3) +#define EXVIS4(x,y) EXVISN(x,y,4) +#define EXVIS5(x,y) EXVISN(x,y,5) + +#define FREG_FROB(f1, f2, f3, f4, f5, f6, f7, f8, f9) \ + faligndata %f1, %f2, %f48; \ + faligndata %f2, %f3, %f50; \ + faligndata %f3, %f4, %f52; \ + faligndata %f4, %f5, %f54; \ + faligndata %f5, %f6, %f56; \ + faligndata %f6, %f7, %f58; \ + faligndata %f7, %f8, %f60; \ + faligndata %f8, %f9, %f62; + +#define MAIN_LOOP_CHUNK(src, dest, fdest, fsrc, len, jmptgt) \ + EXVIS(LDBLK [%src] ASIBLK, %fdest); \ + add %src, 0x40, %src; \ + ASI_SETDST_BLK \ + add %dest, 0x40, %dest; \ + subcc %len, 0x40, %len; \ + be,pn %xcc, jmptgt; \ + EXVIS2(STBLK %fsrc, [%dest - 0x40] ASIBLK); \ + ASI_SETSRC_BLK + +#define LOOP_CHUNK1(src, dest, len, branch_dest) \ + MAIN_LOOP_CHUNK(src, dest, f0, f48, len, branch_dest) +#define LOOP_CHUNK2(src, dest, len, branch_dest) \ + MAIN_LOOP_CHUNK(src, dest, f16, f48, len, branch_dest) +#define LOOP_CHUNK3(src, dest, len, branch_dest) \ + MAIN_LOOP_CHUNK(src, dest, f32, f48, len, branch_dest) + +#define STORE_SYNC(dest, fsrc) \ + EXVIS(STBLK %fsrc, [%dest] ASIBLK); \ + add %dest, 0x40, %dest; + +#define STORE_JUMP(dest, fsrc, target) \ + EXVIS3(STBLK %fsrc, [%dest] ASIBLK); \ + add %dest, 0x40, %dest; \ + ba,pt %xcc, target; + +#ifndef __KERNEL__ +#define VISLOOP_PAD nop; nop; nop; nop; \ + nop; nop; nop; nop; \ + nop; nop; nop; nop; \ + nop; nop; nop; +#else +#define VISLOOP_PAD nop; nop; nop; nop; \ + nop; nop; nop; nop; \ + nop; +#endif + +#define FINISH_VISCHUNK(dest, f0, f1, left) \ + ASI_SETDST_NOBLK \ + subcc %left, 8, %left; \ + bl,pn %xcc, vis_out; \ + faligndata %f0, %f1, %f48; \ + EXVIS4(STDF %f48, [%dest] ASINORMAL); \ + add %dest, 8, %dest; + +#define UNEVEN_VISCHUNK(dest, f0, f1, left) \ + subcc %left, 8, %left; \ + bl,pn %xcc, vis_out; \ + fsrc1 %f0, %f1; \ + ba,a,pt %xcc, vis_slk; + + /* Macros for non-VIS memcpy code. */ +#ifdef REGS_64BIT + +#define MOVE_BIGCHUNK(src, dst, offset, t0, t1, t2, t3) \ + ASI_SETSRC_NOBLK \ + LDX [%src + offset + 0x00] ASINORMAL, %t0; \ + LDX [%src + offset + 0x08] ASINORMAL, %t1; \ + LDX [%src + offset + 0x10] ASINORMAL, %t2; \ + LDX [%src + offset + 0x18] ASINORMAL, %t3; \ + ASI_SETDST_NOBLK \ + STW %t0, [%dst + offset + 0x04] ASINORMAL; \ + srlx %t0, 32, %t0; \ + STW %t0, [%dst + offset + 0x00] ASINORMAL; \ + STW %t1, [%dst + offset + 0x0c] ASINORMAL; \ + srlx %t1, 32, %t1; \ + STW %t1, [%dst + offset + 0x08] ASINORMAL; \ + STW %t2, [%dst + offset + 0x14] ASINORMAL; \ + srlx %t2, 32, %t2; \ + STW %t2, [%dst + offset + 0x10] ASINORMAL; \ + STW %t3, [%dst + offset + 0x1c] ASINORMAL; \ + srlx %t3, 32, %t3; \ + STW %t3, [%dst + offset + 0x18] ASINORMAL; + +#define MOVE_BIGALIGNCHUNK(src, dst, offset, t0, t1, t2, t3) \ + ASI_SETSRC_NOBLK \ + LDX [%src + offset + 0x00] ASINORMAL, %t0; \ + LDX [%src + offset + 0x08] ASINORMAL, %t1; \ + LDX [%src + offset + 0x10] ASINORMAL, %t2; \ + LDX [%src + offset + 0x18] ASINORMAL, %t3; \ + ASI_SETDST_NOBLK \ + STX %t0, [%dst + offset + 0x00] ASINORMAL; \ + STX %t1, [%dst + offset + 0x08] ASINORMAL; \ + STX %t2, [%dst + offset + 0x10] ASINORMAL; \ + STX %t3, [%dst + offset + 0x18] ASINORMAL; \ + ASI_SETSRC_NOBLK \ + LDX [%src + offset + 0x20] ASINORMAL, %t0; \ + LDX [%src + offset + 0x28] ASINORMAL, %t1; \ + LDX [%src + offset + 0x30] ASINORMAL, %t2; \ + LDX [%src + offset + 0x38] ASINORMAL, %t3; \ + ASI_SETDST_NOBLK \ + STX %t0, [%dst + offset + 0x20] ASINORMAL; \ + STX %t1, [%dst + offset + 0x28] ASINORMAL; \ + STX %t2, [%dst + offset + 0x30] ASINORMAL; \ + STX %t3, [%dst + offset + 0x38] ASINORMAL; + +#define MOVE_LASTCHUNK(src, dst, offset, t0, t1, t2, t3) \ + ASI_SETSRC_NOBLK \ + LDX [%src - offset - 0x10] ASINORMAL, %t0; \ + LDX [%src - offset - 0x08] ASINORMAL, %t1; \ + ASI_SETDST_NOBLK \ + STW %t0, [%dst - offset - 0x0c] ASINORMAL; \ + srlx %t0, 32, %t2; \ + STW %t2, [%dst - offset - 0x10] ASINORMAL; \ + STW %t1, [%dst - offset - 0x04] ASINORMAL; \ + srlx %t1, 32, %t3; \ + STW %t3, [%dst - offset - 0x08] ASINORMAL; + +#define MOVE_LASTALIGNCHUNK(src, dst, offset, t0, t1) \ + ASI_SETSRC_NOBLK \ + LDX [%src - offset - 0x10] ASINORMAL, %t0; \ + LDX [%src - offset - 0x08] ASINORMAL, %t1; \ + ASI_SETDST_NOBLK \ + STX %t0, [%dst - offset - 0x10] ASINORMAL; \ + STX %t1, [%dst - offset - 0x08] ASINORMAL; + +#else /* !REGS_64BIT */ + +#define MOVE_BIGCHUNK(src, dst, offset, t0, t1, t2, t3) \ + lduw [%src + offset + 0x00], %t0; \ + lduw [%src + offset + 0x04], %t1; \ + lduw [%src + offset + 0x08], %t2; \ + lduw [%src + offset + 0x0c], %t3; \ + stw %t0, [%dst + offset + 0x00]; \ + stw %t1, [%dst + offset + 0x04]; \ + stw %t2, [%dst + offset + 0x08]; \ + stw %t3, [%dst + offset + 0x0c]; \ + lduw [%src + offset + 0x10], %t0; \ + lduw [%src + offset + 0x14], %t1; \ + lduw [%src + offset + 0x18], %t2; \ + lduw [%src + offset + 0x1c], %t3; \ + stw %t0, [%dst + offset + 0x10]; \ + stw %t1, [%dst + offset + 0x14]; \ + stw %t2, [%dst + offset + 0x18]; \ + stw %t3, [%dst + offset + 0x1c]; + +#define MOVE_LASTCHUNK(src, dst, offset, t0, t1, t2, t3) \ + lduw [%src - offset - 0x10], %t0; \ + lduw [%src - offset - 0x0c], %t1; \ + lduw [%src - offset - 0x08], %t2; \ + lduw [%src - offset - 0x04], %t3; \ + stw %t0, [%dst - offset - 0x10]; \ + stw %t1, [%dst - offset - 0x0c]; \ + stw %t2, [%dst - offset - 0x08]; \ + stw %t3, [%dst - offset - 0x04]; + +#endif /* !REGS_64BIT */ + +#ifdef __KERNEL__ + .section __ex_table,#alloc + .section .fixup,#alloc,#execinstr +#endif + + .text + .align 32 + .globl memcpy + .type memcpy,@function + + .globl bcopy + .type bcopy,@function + +#ifdef __KERNEL__ + .globl __memcpy + .type __memcpy,@function + + .globl __memcpy_384plus + .type __memcpy_384plus,@function + + .globl __memcpy_16plus + .type __memcpy_16plus,@function + + .globl __memcpy_short + .type __memcpy_short,@function + + .globl __memcpy_entry + .type __memcpy_entry,@function + + .globl copy_page + .type copy_page,@function + +memcpy_private: +__memcpy: +memcpy: mov ASI_BLK_P, asi_src ! IEU0 Group + brnz,pt %o2, __memcpy_entry ! CTI + mov ASI_BLK_P, asi_dest ! IEU1 + retl + clr %o0 + +copy_page: wr %g0, FPRS_FEF, %fprs ! FPU Group + sethi %hi(8192), %o2 ! IEU0 Group + mov ASI_BLK_P, asi_src ! IEU1 + b,pt %xcc, dest_is_64byte_aligned ! CTI + mov ASI_BLK_COMMIT_P, asi_dest ! IEU0 Group + + .align 32 + .globl __copy_from_user + .type __copy_from_user,@function +__copy_from_user:mov ASI_BLK_S, asi_src ! IEU0 Group + brnz,pt %o2, __memcpy_entry ! CTI + mov ASI_BLK_P, asi_dest ! IEU1 + + .globl __copy_to_user + .type __copy_to_user,@function +__copy_to_user: mov ASI_BLK_P, asi_src ! IEU0 Group + brnz,pt %o2, __memcpy_entry ! CTI + mov ASI_BLK_S, asi_dest ! IEU1 + retl ! CTI Group + clr %o0 ! IEU0 Group +#endif + +bcopy: or %o0, 0, %g3 ! IEU0 Group + addcc %o1, 0, %o0 ! IEU1 + brgez,pt %o2, memcpy_private ! CTI + or %g3, 0, %o1 ! IEU0 Group + retl ! CTI Group brk forced + clr %o0 ! IEU0 + + + .align 32 +#ifdef __KERNEL__ +__memcpy_384plus: + andcc %o0, 7, %g2 ! IEU1 Group +#endif +VIS_enter: + be,pt %xcc, dest_is_8byte_aligned ! CTI + andcc %o0, 0x38, %g5 ! IEU1 Group +do_dest_8byte_align: + mov 8, %g1 ! IEU0 + sub %g1, %g2, %g2 ! IEU0 Group + andcc %o0, 1, %g0 ! IEU1 + be,pt %icc, 2f ! CTI + sub %o2, %g2, %o2 ! IEU0 Group +1: ASI_SETSRC_NOBLK ! LSU Group + EX(LDUB [%o1] ASINORMAL, %o5, + add %o2, %g2) ! Load Group + add %o1, 1, %o1 ! IEU0 + add %o0, 1, %o0 ! IEU1 + ASI_SETDST_NOBLK ! LSU Group + subcc %g2, 1, %g2 ! IEU1 Group + be,pn %xcc, 3f ! CTI + EX2(STB %o5, [%o0 - 1] ASINORMAL, + add %g2, 1, %g2, + add %o2, %g2) ! Store +2: ASI_SETSRC_NOBLK ! LSU Group + EX(LDUB [%o1] ASINORMAL, %o5, + add %o2, %g2) ! Load Group + add %o0, 2, %o0 ! IEU0 + EX(LDUB [%o1 + 1] ASINORMAL, %g3, + add %o2, %g2) ! Load Group + ASI_SETDST_NOBLK ! LSU Group + subcc %g2, 2, %g2 ! IEU1 Group + EX2(STB %o5, [%o0 - 2] ASINORMAL, + add %g2, 2, %g2, + add %o2, %g2) ! Store + add %o1, 2, %o1 ! IEU0 + bne,pt %xcc, 2b ! CTI Group + EX2(STB %g3, [%o0 - 1] ASINORMAL, + add %g2, 1, %g2, + add %o2, %g2) ! Store +3: andcc %o0, 0x38, %g5 ! IEU1 Group +dest_is_8byte_aligned: + be,pt %icc, dest_is_64byte_aligned ! CTI +#ifdef __KERNEL__ + wr %g0, FPRS_FEF, %fprs ! FPU Group +do_dest_64byte_align: + mov 64, %g1 ! IEU0 Group +#else + mov 64, %g1 ! IEU0 Group +do_dest_64byte_align: +#endif + fmovd %f0, %f2 ! FPU + sub %g1, %g5, %g5 ! IEU0 Group + ASI_SETSRC_NOBLK ! LSU Group + alignaddr %o1, %g0, %g1 ! GRU Group + EXO2(LDDF [%g1] ASINORMAL, %f4) ! Load Group + sub %o2, %g5, %o2 ! IEU0 +1: EX(LDDF [%g1 + 0x8] ASINORMAL, %f6, + add %o2, %g5) ! Load Group + add %g1, 0x8, %g1 ! IEU0 Group + subcc %g5, 8, %g5 ! IEU1 + ASI_SETDST_NOBLK ! LSU Group + faligndata %f4, %f6, %f0 ! GRU Group + EX2(STDF %f0, [%o0] ASINORMAL, + add %g5, 8, %g5, + add %o2, %g5) ! Store + add %o1, 8, %o1 ! IEU0 Group + be,pn %xcc, dest_is_64byte_aligned ! CTI + add %o0, 8, %o0 ! IEU1 + ASI_SETSRC_NOBLK ! LSU Group + EX(LDDF [%g1 + 0x8] ASINORMAL, %f4, + add %o2, %g5) ! Load Group + add %g1, 8, %g1 ! IEU0 + subcc %g5, 8, %g5 ! IEU1 + ASI_SETDST_NOBLK ! LSU Group + faligndata %f6, %f4, %f0 ! GRU Group + EX2(STDF %f0, [%o0] ASINORMAL, + add %g5, 8, %g5, + add %o2, %g5) ! Store + add %o1, 8, %o1 ! IEU0 + ASI_SETSRC_NOBLK ! LSU Group + bne,pt %xcc, 1b ! CTI Group + add %o0, 8, %o0 ! IEU0 +dest_is_64byte_aligned: + membar #LoadStore | #StoreStore | #StoreLoad ! LSU Group +#ifndef __KERNEL__ + wr %g0, ASI_BLK_P, %asi ! LSU Group +#endif + subcc %o2, 0x40, %g7 ! IEU1 Group + mov %o1, %g1 ! IEU0 + andncc %g7, (0x40 - 1), %g7 ! IEU1 Group + srl %g1, 3, %g2 ! IEU0 + sub %o2, %g7, %g3 ! IEU0 Group + andn %o1, (0x40 - 1), %o1 ! IEU1 + and %g2, 7, %g2 ! IEU0 Group + andncc %g3, 0x7, %g3 ! IEU1 + fmovd %f0, %f2 ! FPU + sub %g3, 0x10, %g3 ! IEU0 Group + sub %o2, %g7, %o2 ! IEU1 + alignaddr %g1, %g0, %g0 ! GRU Group + add %g1, %g7, %g1 ! IEU0 Group + subcc %o2, %g3, %o2 ! IEU1 + ASI_SETSRC_BLK ! LSU Group + EXVIS1(LDBLK [%o1 + 0x00] ASIBLK, %f0) ! LSU Group + add %g1, %g3, %g1 ! IEU0 + EXVIS1(LDBLK [%o1 + 0x40] ASIBLK, %f16) ! LSU Group + sub %g7, 0x80, %g7 ! IEU0 + EXVIS(LDBLK [%o1 + 0x80] ASIBLK, %f32) ! LSU Group + ! Clk1 Group 8-( + ! Clk2 Group 8-( + ! Clk3 Group 8-( + ! Clk4 Group 8-( +vispc: rd %pc, %g5 ! PDU Group 8-( + addcc %g5, %lo(vis00 - vispc), %g5 ! IEU1 Group + sll %g2, 9, %g2 ! IEU0 + jmpl %g5 + %g2, %g0 ! CTI Group brk forced + addcc %o1, 0xc0, %o1 ! IEU1 Group + .align 512 /* OK, here comes the fun part... */ +vis00:FREG_FROB(f0, f2, f4, f6, f8, f10,f12,f14,f16) LOOP_CHUNK1(o1, o0, g7, vis01) + FREG_FROB(f16,f18,f20,f22,f24,f26,f28,f30,f32) LOOP_CHUNK2(o1, o0, g7, vis02) + FREG_FROB(f32,f34,f36,f38,f40,f42,f44,f46,f0) LOOP_CHUNK3(o1, o0, g7, vis03) + b,pt %xcc, vis00+4; faligndata %f0, %f2, %f48 +vis01:FREG_FROB(f16,f18,f20,f22,f24,f26,f28,f30,f32) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f32,f34,f36,f38,f40,f42,f44,f46,f0) STORE_JUMP(o0, f48, finish_f0) membar #Sync +vis02:FREG_FROB(f32,f34,f36,f38,f40,f42,f44,f46,f0) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f0, f2, f4, f6, f8, f10,f12,f14,f16) STORE_JUMP(o0, f48, check_finish_f16) add %o2, %g3, %g7 +vis03:FREG_FROB(f0, f2, f4, f6, f8, f10,f12,f14,f16) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f16,f18,f20,f22,f24,f26,f28,f30,f32) STORE_JUMP(o0, f48, finish_f32) membar #Sync + VISLOOP_PAD +vis10:FREG_FROB(f2, f4, f6, f8, f10,f12,f14,f16,f18) LOOP_CHUNK1(o1, o0, g7, vis11) + FREG_FROB(f18,f20,f22,f24,f26,f28,f30,f32,f34) LOOP_CHUNK2(o1, o0, g7, vis12) + FREG_FROB(f34,f36,f38,f40,f42,f44,f46,f0, f2) LOOP_CHUNK3(o1, o0, g7, vis13) + b,pt %xcc, vis10+4; faligndata %f2, %f4, %f48 +vis11:FREG_FROB(f18,f20,f22,f24,f26,f28,f30,f32,f34) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f34,f36,f38,f40,f42,f44,f46,f0, f2) STORE_JUMP(o0, f48, finish_f2) membar #Sync +vis12:FREG_FROB(f34,f36,f38,f40,f42,f44,f46,f0, f2) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f2, f4, f6, f8, f10,f12,f14,f16,f18) STORE_JUMP(o0, f48, finish_f18) membar #Sync +vis13:FREG_FROB(f2, f4, f6, f8, f10,f12,f14,f16,f18) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f18,f20,f22,f24,f26,f28,f30,f32,f34) STORE_JUMP(o0, f48, finish_f34) membar #Sync + VISLOOP_PAD +vis20:FREG_FROB(f4, f6, f8, f10,f12,f14,f16,f18,f20) LOOP_CHUNK1(o1, o0, g7, vis21) + FREG_FROB(f20,f22,f24,f26,f28,f30,f32,f34,f36) LOOP_CHUNK2(o1, o0, g7, vis22) + FREG_FROB(f36,f38,f40,f42,f44,f46,f0, f2, f4) LOOP_CHUNK3(o1, o0, g7, vis23) + b,pt %xcc, vis20+4; faligndata %f4, %f6, %f48 +vis21:FREG_FROB(f20,f22,f24,f26,f28,f30,f32,f34,f36) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f36,f38,f40,f42,f44,f46,f0, f2, f4) STORE_JUMP(o0, f48, finish_f4) membar #Sync +vis22:FREG_FROB(f36,f38,f40,f42,f44,f46,f0, f2, f4) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f4, f6, f8, f10,f12,f14,f16,f18,f20) STORE_JUMP(o0, f48, finish_f20) membar #Sync +vis23:FREG_FROB(f4, f6, f8, f10,f12,f14,f16,f18,f20) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f20,f22,f24,f26,f28,f30,f32,f34,f36) STORE_JUMP(o0, f48, finish_f36) membar #Sync + VISLOOP_PAD +vis30:FREG_FROB(f6, f8, f10,f12,f14,f16,f18,f20,f22) LOOP_CHUNK1(o1, o0, g7, vis31) + FREG_FROB(f22,f24,f26,f28,f30,f32,f34,f36,f38) LOOP_CHUNK2(o1, o0, g7, vis32) + FREG_FROB(f38,f40,f42,f44,f46,f0, f2, f4, f6) LOOP_CHUNK3(o1, o0, g7, vis33) + b,pt %xcc, vis30+4; faligndata %f6, %f8, %f48 +vis31:FREG_FROB(f22,f24,f26,f28,f30,f32,f34,f36,f38) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f38,f40,f42,f44,f46,f0, f2, f4, f6) STORE_JUMP(o0, f48, finish_f6) membar #Sync +vis32:FREG_FROB(f38,f40,f42,f44,f46,f0, f2, f4, f6) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f6, f8, f10,f12,f14,f16,f18,f20,f22) STORE_JUMP(o0, f48, finish_f22) membar #Sync +vis33:FREG_FROB(f6, f8, f10,f12,f14,f16,f18,f20,f22) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f22,f24,f26,f28,f30,f32,f34,f36,f38) STORE_JUMP(o0, f48, finish_f38) membar #Sync + VISLOOP_PAD +vis40:FREG_FROB(f8, f10,f12,f14,f16,f18,f20,f22,f24) LOOP_CHUNK1(o1, o0, g7, vis41) + FREG_FROB(f24,f26,f28,f30,f32,f34,f36,f38,f40) LOOP_CHUNK2(o1, o0, g7, vis42) + FREG_FROB(f40,f42,f44,f46,f0, f2, f4, f6, f8) LOOP_CHUNK3(o1, o0, g7, vis43) + b,pt %xcc, vis40+4; faligndata %f8, %f10, %f48 +vis41:FREG_FROB(f24,f26,f28,f30,f32,f34,f36,f38,f40) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f40,f42,f44,f46,f0, f2, f4, f6, f8) STORE_JUMP(o0, f48, finish_f8) membar #Sync +vis42:FREG_FROB(f40,f42,f44,f46,f0, f2, f4, f6, f8) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f8, f10,f12,f14,f16,f18,f20,f22,f24) STORE_JUMP(o0, f48, finish_f24) membar #Sync +vis43:FREG_FROB(f8, f10,f12,f14,f16,f18,f20,f22,f24) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f24,f26,f28,f30,f32,f34,f36,f38,f40) STORE_JUMP(o0, f48, finish_f40) membar #Sync + VISLOOP_PAD +vis50:FREG_FROB(f10,f12,f14,f16,f18,f20,f22,f24,f26) LOOP_CHUNK1(o1, o0, g7, vis51) + FREG_FROB(f26,f28,f30,f32,f34,f36,f38,f40,f42) LOOP_CHUNK2(o1, o0, g7, vis52) + FREG_FROB(f42,f44,f46,f0, f2, f4, f6, f8, f10) LOOP_CHUNK3(o1, o0, g7, vis53) + b,pt %xcc, vis50+4; faligndata %f10, %f12, %f48 +vis51:FREG_FROB(f26,f28,f30,f32,f34,f36,f38,f40,f42) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f42,f44,f46,f0, f2, f4, f6, f8, f10) STORE_JUMP(o0, f48, finish_f10) membar #Sync +vis52:FREG_FROB(f42,f44,f46,f0, f2, f4, f6, f8, f10) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f10,f12,f14,f16,f18,f20,f22,f24,f26) STORE_JUMP(o0, f48, finish_f26) membar #Sync +vis53:FREG_FROB(f10,f12,f14,f16,f18,f20,f22,f24,f26) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f26,f28,f30,f32,f34,f36,f38,f40,f42) STORE_JUMP(o0, f48, finish_f42) membar #Sync + VISLOOP_PAD +vis60:FREG_FROB(f12,f14,f16,f18,f20,f22,f24,f26,f28) LOOP_CHUNK1(o1, o0, g7, vis61) + FREG_FROB(f28,f30,f32,f34,f36,f38,f40,f42,f44) LOOP_CHUNK2(o1, o0, g7, vis62) + FREG_FROB(f44,f46,f0, f2, f4, f6, f8, f10,f12) LOOP_CHUNK3(o1, o0, g7, vis63) + b,pt %xcc, vis60+4; faligndata %f12, %f14, %f48 +vis61:FREG_FROB(f28,f30,f32,f34,f36,f38,f40,f42,f44) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f44,f46,f0, f2, f4, f6, f8, f10,f12) STORE_JUMP(o0, f48, finish_f12) membar #Sync +vis62:FREG_FROB(f44,f46,f0, f2, f4, f6, f8, f10,f12) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f12,f14,f16,f18,f20,f22,f24,f26,f28) STORE_JUMP(o0, f48, finish_f28) membar #Sync +vis63:FREG_FROB(f12,f14,f16,f18,f20,f22,f24,f26,f28) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f28,f30,f32,f34,f36,f38,f40,f42,f44) STORE_JUMP(o0, f48, finish_f44) membar #Sync + VISLOOP_PAD +vis70:FREG_FROB(f14,f16,f18,f20,f22,f24,f26,f28,f30) LOOP_CHUNK1(o1, o0, g7, vis71) + FREG_FROB(f30,f32,f34,f36,f38,f40,f42,f44,f46) LOOP_CHUNK2(o1, o0, g7, vis72) + FREG_FROB(f46,f0, f2, f4, f6, f8, f10,f12,f14) LOOP_CHUNK3(o1, o0, g7, vis73) + b,pt %xcc, vis70+4; faligndata %f14, %f16, %f48 +vis71:FREG_FROB(f30,f32,f34,f36,f38,f40,f42,f44,f46) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f46,f0, f2, f4, f6, f8, f10,f12,f14) STORE_JUMP(o0, f48, finish_f14) membar #Sync +vis72:FREG_FROB(f46,f0, f2, f4, f6, f8, f10,f12,f14) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f14,f16,f18,f20,f22,f24,f26,f28,f30) STORE_JUMP(o0, f48, finish_f30) membar #Sync +vis73:FREG_FROB(f14,f16,f18,f20,f22,f24,f26,f28,f30) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f30,f32,f34,f36,f38,f40,f42,f44,f46) STORE_JUMP(o0, f48, finish_f46) membar #Sync + VISLOOP_PAD +finish_f0: FINISH_VISCHUNK(o0, f0, f2, g3) +finish_f2: FINISH_VISCHUNK(o0, f2, f4, g3) +finish_f4: FINISH_VISCHUNK(o0, f4, f6, g3) +finish_f6: FINISH_VISCHUNK(o0, f6, f8, g3) +finish_f8: FINISH_VISCHUNK(o0, f8, f10, g3) +finish_f10: FINISH_VISCHUNK(o0, f10, f12, g3) +finish_f12: FINISH_VISCHUNK(o0, f12, f14, g3) +finish_f14: UNEVEN_VISCHUNK(o0, f14, f0, g3) +/* This is a special hack to speed up 8K page copy */ +check_finish_f16: + andcc %g1, 7, %g0 + bne,pn %icc, finish_f16 + cmp %g7, 0x40 + bne,pn %icc, finish_f16 + FREG_FROB(f16,f18,f20,f22,f24,f26,f28,f30,f32) + membar #Sync + EXVIS1(STBLK %f48, [%o0] ASIBLK) + b,pt %xcc, vis_ret +finish_f16: membar #Sync + FINISH_VISCHUNK(o0, f16, f18, g3) +finish_f18: FINISH_VISCHUNK(o0, f18, f20, g3) +finish_f20: FINISH_VISCHUNK(o0, f20, f22, g3) +finish_f22: FINISH_VISCHUNK(o0, f22, f24, g3) +finish_f24: FINISH_VISCHUNK(o0, f24, f26, g3) +finish_f26: FINISH_VISCHUNK(o0, f26, f28, g3) +finish_f28: FINISH_VISCHUNK(o0, f28, f30, g3) +finish_f30: UNEVEN_VISCHUNK(o0, f30, f0, g3) +finish_f32: FINISH_VISCHUNK(o0, f32, f34, g3) +finish_f34: FINISH_VISCHUNK(o0, f34, f36, g3) +finish_f36: FINISH_VISCHUNK(o0, f36, f38, g3) +finish_f38: FINISH_VISCHUNK(o0, f38, f40, g3) +finish_f40: FINISH_VISCHUNK(o0, f40, f42, g3) +finish_f42: FINISH_VISCHUNK(o0, f42, f44, g3) +finish_f44: FINISH_VISCHUNK(o0, f44, f46, g3) +finish_f46: UNEVEN_VISCHUNK(o0, f46, f0, g3) +vis_slk:ASI_SETSRC_NOBLK ! LSU Group + EXVIS4(LDDF [%o1] ASINORMAL, %f2) ! Load Group + add %o1, 8, %o1 ! IEU0 + subcc %g3, 8, %g3 ! IEU1 + ASI_SETDST_NOBLK ! LSU Group + faligndata %f0, %f2, %f8 ! GRU Group + EXVIS5(STDF %f8, [%o0] ASINORMAL) ! Store + bl,pn %xcc, vis_out ! CTI + add %o0, 8, %o0 ! IEU0 Group + ASI_SETSRC_NOBLK ! LSU Group + EXVIS4(LDDF [%o1] ASINORMAL, %f0) ! Load Group + add %o1, 8, %o1 ! IEU0 + subcc %g3, 8, %g3 ! IEU1 + ASI_SETDST_NOBLK ! LSU Group + faligndata %f2, %f0, %f8 ! GRU Group + EXVIS5(STDF %f8, [%o0] ASINORMAL) ! Store + bge,pt %xcc, vis_slk ! CTI + add %o0, 8, %o0 ! IEU0 Group +vis_out:brz,pt %o2, vis_ret ! CTI Group + mov %g1, %o1 ! IEU0 +vis_slp:ASI_SETSRC_NOBLK ! LSU Group + EXO2(LDUB [%o1] ASINORMAL, %g5) ! LOAD + add %o1, 1, %o1 ! IEU0 + add %o0, 1, %o0 ! IEU1 + ASI_SETDST_NOBLK ! LSU Group + subcc %o2, 1, %o2 ! IEU1 + bne,pt %xcc, vis_slp ! CTI + EX(STB %g5, [%o0 - 1] ASINORMAL, + add %o2, 1) ! Store Group +vis_ret:membar #StoreLoad | #StoreStore ! LSU Group + FPU_CLEAN_RETL + + +__memcpy_short: + andcc %o2, 1, %g0 ! IEU1 Group + be,pt %icc, 2f ! CTI +1: ASI_SETSRC_NOBLK ! LSU Group + EXO2(LDUB [%o1] ASINORMAL, %g5) ! LOAD Group + add %o1, 1, %o1 ! IEU0 + add %o0, 1, %o0 ! IEU1 + ASI_SETDST_NOBLK ! LSU Group + subcc %o2, 1, %o2 ! IEU1 Group + be,pn %xcc, short_ret ! CTI + EX(STB %g5, [%o0 - 1] ASINORMAL, + add %o2, 1) ! Store +2: ASI_SETSRC_NOBLK ! LSU Group + EXO2(LDUB [%o1] ASINORMAL, %g5) ! LOAD Group + add %o0, 2, %o0 ! IEU0 + EXO2(LDUB [%o1 + 1] ASINORMAL, %o5) ! LOAD Group + add %o1, 2, %o1 ! IEU0 + ASI_SETDST_NOBLK ! LSU Group + subcc %o2, 2, %o2 ! IEU1 Group + EX(STB %g5, [%o0 - 2] ASINORMAL, + add %o2, 2) ! Store + bne,pt %xcc, 2b ! CTI + EX(STB %o5, [%o0 - 1] ASINORMAL, + add %o2, 1) ! Store +short_ret: + NORMAL_RETL + +#ifndef __KERNEL__ +memcpy_private: +memcpy: +#ifndef REGS_64BIT + srl %o2, 0, %o2 ! IEU1 Group +#endif + brz,pn %o2, short_ret ! CTI Group + mov %o0, %g6 ! IEU0 +#endif +__memcpy_entry: + cmp %o2, 15 ! IEU1 Group + bleu,pn %xcc, __memcpy_short ! CTI + cmp %o2, (64 * 6) ! IEU1 Group + bgeu,pn %xcc, VIS_enter ! CTI +#ifdef __KERNEL__ +__memcpy_16plus: +#endif + andcc %o0, 7, %g2 ! IEU1 Group + sub %o0, %o1, %g5 ! IEU0 + andcc %g5, 3, %o5 ! IEU1 Group + bne,pn %xcc, memcpy_noVIS_misaligned ! CTI + andcc %o1, 3, %g0 ! IEU1 Group +#ifdef REGS_64BIT + be,a,pt %xcc, 3f ! CTI + andcc %o1, 4, %g0 ! IEU1 Group + andcc %o1, 1, %g0 ! IEU1 Group +#else /* !REGS_64BIT */ + be,pt %xcc, 5f ! CTI + andcc %o1, 1, %g0 ! IEU1 Group +#endif /* !REGS_64BIT */ + be,pn %xcc, 4f ! CTI + andcc %o1, 2, %g0 ! IEU1 Group + ASI_SETSRC_NOBLK ! LSU Group + EXO2(LDUB [%o1] ASINORMAL, %g2) ! Load Group + add %o1, 1, %o1 ! IEU0 + add %o0, 1, %o0 ! IEU1 + sub %o2, 1, %o2 ! IEU0 Group + ASI_SETDST_NOBLK ! LSU Group + bne,pn %xcc, 5f ! CTI Group + EX(STB %g2, [%o0 - 1] ASINORMAL, + add %o2, 1) ! Store +4: ASI_SETSRC_NOBLK ! LSU Group + EXO2(LDUH [%o1] ASINORMAL, %g2) ! Load Group + add %o1, 2, %o1 ! IEU0 + add %o0, 2, %o0 ! IEU1 + ASI_SETDST_NOBLK ! LSU Group + sub %o2, 2, %o2 ! IEU0 + EX(STH %g2, [%o0 - 2] ASINORMAL, + add %o2, 2) ! Store Group + bubble +#ifdef REGS_64BIT +5: andcc %o1, 4, %g0 ! IEU1 +3: be,a,pn %xcc, 2f ! CTI + andcc %o2, -128, %g7 ! IEU1 Group + ASI_SETSRC_NOBLK ! LSU Group + EXO2(LDUW [%o1] ASINORMAL, %g5) ! Load Group + add %o1, 4, %o1 ! IEU0 + add %o0, 4, %o0 ! IEU1 + ASI_SETDST_NOBLK ! LSU Group + sub %o2, 4, %o2 ! IEU0 Group + EX(STW %g5, [%o0 - 4] ASINORMAL, + add %o2, 4) ! Store + andcc %o2, -128, %g7 ! IEU1 Group +2: be,pn %xcc, 3f ! CTI + andcc %o0, 4, %g0 ! IEU1 Group + be,pn %xcc, 82f + 4 ! CTI Group +#else /* !REGS_64BIT */ +5: andcc %o2, -128, %g7 ! IEU1 + be,a,pn %xcc, 41f ! CTI + andcc %o2, 0x70, %g7 ! IEU1 Group +#endif /* !REGS_64BIT */ +5: MOVE_BIGCHUNK(o1, o0, 0x00, g1, g3, g5, o5) + MOVE_BIGCHUNK(o1, o0, 0x20, g1, g3, g5, o5) + MOVE_BIGCHUNK(o1, o0, 0x40, g1, g3, g5, o5) + MOVE_BIGCHUNK(o1, o0, 0x60, g1, g3, g5, o5) + EXT(5b,35f,VIScopyfixup1) +35: subcc %g7, 128, %g7 ! IEU1 Group + add %o1, 128, %o1 ! IEU0 + bne,pt %xcc, 5b ! CTI + add %o0, 128, %o0 ! IEU0 Group +3: andcc %o2, 0x70, %g7 ! IEU1 Group +41: be,pn %xcc, 80f ! CTI + andcc %o2, 8, %g0 ! IEU1 Group + ! Clk1 8-( + ! Clk2 8-( + ! Clk3 8-( + ! Clk4 8-( +79: rd %pc, %o5 ! PDU Group +#ifdef __KERNEL__ + sll %g7, 1, %g5 ! IEU0 Group + add %o1, %g7, %o1 ! IEU1 + srl %g7, 1, %g2 ! IEU0 Group + sub %o5, %g5, %o5 ! IEU1 + sub %o5, %g2, %o5 ! IEU0 Group + jmpl %o5 + %lo(80f - 79b), %g0 ! CTI Group brk forced + add %o0, %g7, %o0 ! IEU0 Group +#else + sll %g7, 1, %g5 ! IEU0 Group + add %o1, %g7, %o1 ! IEU1 + sub %o5, %g5, %o5 ! IEU0 Group + jmpl %o5 + %lo(80f - 79b), %g0 ! CTI Group brk forced + add %o0, %g7, %o0 ! IEU0 Group +#endif +36: MOVE_LASTCHUNK(o1, o0, 0x60, g2, g3, g5, o5) + MOVE_LASTCHUNK(o1, o0, 0x50, g2, g3, g5, o5) + MOVE_LASTCHUNK(o1, o0, 0x40, g2, g3, g5, o5) + MOVE_LASTCHUNK(o1, o0, 0x30, g2, g3, g5, o5) + MOVE_LASTCHUNK(o1, o0, 0x20, g2, g3, g5, o5) + MOVE_LASTCHUNK(o1, o0, 0x10, g2, g3, g5, o5) + MOVE_LASTCHUNK(o1, o0, 0x00, g2, g3, g5, o5) + EXT(36b,80f,VIScopyfixup2) +80: be,pt %xcc, 81f ! CTI + andcc %o2, 4, %g0 ! IEU1 +#ifdef REGS_64BIT + ASI_SETSRC_NOBLK ! LSU Group + EX(LDX [%o1] ASINORMAL, %g2, + and %o2, 0xf) ! Load Group + add %o0, 8, %o0 ! IEU0 + ASI_SETDST_NOBLK ! LSU Group + EX(STW %g2, [%o0 - 0x4] ASINORMAL, + and %o2, 0xf) ! Store Group + add %o1, 8, %o1 ! IEU1 + srlx %g2, 32, %g2 ! IEU0 Group + EX2(STW %g2, [%o0 - 0x8] ASINORMAL, + and %o2, 0xf, %o2, + sub %o2, 4) ! Store +#else /* !REGS_64BIT */ + lduw [%o1], %g2 ! Load Group + add %o0, 8, %o0 ! IEU0 + lduw [%o1 + 0x4], %g3 ! Load Group + add %o1, 8, %o1 ! IEU0 + stw %g2, [%o0 - 0x8] ! Store Group + stw %g3, [%o0 - 0x4] ! Store Group +#endif /* !REGS_64BIT */ +81: be,pt %xcc, 1f ! CTI + andcc %o2, 2, %g0 ! IEU1 Group + ASI_SETSRC_NOBLK ! LSU Group + EX(LDUW [%o1] ASINORMAL, %g2, + and %o2, 0x7) ! Load Group + add %o1, 4, %o1 ! IEU0 + ASI_SETDST_NOBLK ! LSU Group + EX(STW %g2, [%o0] ASINORMAL, + and %o2, 0x7) ! Store Group + add %o0, 4, %o0 ! IEU0 +1: be,pt %xcc, 1f ! CTI + andcc %o2, 1, %g0 ! IEU1 Group + ASI_SETSRC_NOBLK ! LSU Group + EX(LDUH [%o1] ASINORMAL, %g2, + and %o2, 0x3) ! Load Group + add %o1, 2, %o1 ! IEU0 + ASI_SETDST_NOBLK ! LSU Group + EX(STH %g2, [%o0] ASINORMAL, + and %o2, 0x3) ! Store Group + add %o0, 2, %o0 ! IEU0 +1: be,pt %xcc, normal_retl ! CTI + nop ! IEU1 + ASI_SETSRC_NOBLK ! LSU Group + EX(LDUB [%o1] ASINORMAL, %g2, + add %g0, 1) ! Load Group + ASI_SETDST_NOBLK ! LSU Group + EX(STB %g2, [%o0] ASINORMAL, + add %g0, 1) ! Store Group + bubble +normal_retl: + NORMAL_RETL + +#ifdef REGS_64BIT +82: MOVE_BIGALIGNCHUNK(o1, o0, 0x00, g1, g3, g5, o5) + MOVE_BIGALIGNCHUNK(o1, o0, 0x40, g1, g3, g5, o5) + EXT(82b,37f,VIScopyfixup3) +37: subcc %g7, 128, %g7 ! IEU1 Group + add %o1, 128, %o1 ! IEU0 + bne,pt %xcc, 82b ! CTI + add %o0, 128, %o0 ! IEU0 Group + andcc %o2, 0x70, %g7 ! IEU1 + be,pn %xcc, 84f ! CTI + andcc %o2, 8, %g0 ! IEU1 Group + ! Clk1 8-( + ! Clk2 8-( + ! Clk3 8-( + ! Clk4 8-( +83: rd %pc, %o5 ! PDU Group +#ifdef __KERNEL__ + srl %g7, 1, %g5 ! IEU0 Group + add %g7, %g5, %g5 ! IEU0 Group + add %o1, %g7, %o1 ! IEU1 + sub %o5, %g5, %o5 ! IEU0 Group + jmpl %o5 + %lo(84f - 83b), %g0 ! CTI Group brk forced + add %o0, %g7, %o0 ! IEU0 Group +#else + add %o1, %g7, %o1 ! IEU0 Group + sub %o5, %g7, %o5 ! IEU1 + jmpl %o5 + %lo(84f - 83b), %g0 ! CTI Group brk forced + add %o0, %g7, %o0 ! IEU0 Group +#endif +38: MOVE_LASTALIGNCHUNK(o1, o0, 0x60, g2, g3) + MOVE_LASTALIGNCHUNK(o1, o0, 0x50, g2, g3) + MOVE_LASTALIGNCHUNK(o1, o0, 0x40, g2, g3) + MOVE_LASTALIGNCHUNK(o1, o0, 0x30, g2, g3) + MOVE_LASTALIGNCHUNK(o1, o0, 0x20, g2, g3) + MOVE_LASTALIGNCHUNK(o1, o0, 0x10, g2, g3) + MOVE_LASTALIGNCHUNK(o1, o0, 0x00, g2, g3) + EXT(38b,84f,VIScopyfixup4) +84: be,pt %xcc, 85f ! CTI Group + andcc %o2, 4, %g0 ! IEU1 + ASI_SETSRC_NOBLK ! LSU Group + EX(LDX [%o1] ASINORMAL, %g2, + and %o2, 0xf) ! Load Group + add %o1, 8, %o1 ! IEU0 + ASI_SETDST_NOBLK ! LSU Group + add %o0, 8, %o0 ! IEU0 Group + EX(STX %g2, [%o0 - 0x8] ASINORMAL, + and %o2, 0xf) ! Store +85: be,pt %xcc, 1f ! CTI + andcc %o2, 2, %g0 ! IEU1 Group + ASI_SETSRC_NOBLK ! LSU Group + EX(LDUW [%o1] ASINORMAL, %g2, + and %o2, 0x7) ! Load Group + add %o1, 4, %o1 ! IEU0 + ASI_SETDST_NOBLK ! LSU Group + add %o0, 4, %o0 ! IEU0 Group + EX(STW %g2, [%o0 - 0x4] ASINORMAL, + and %o2, 0x7) ! Store +1: be,pt %xcc, 1f ! CTI + andcc %o2, 1, %g0 ! IEU1 Group + ASI_SETSRC_NOBLK ! LSU Group + EX(LDUH [%o1] ASINORMAL, %g2, + and %o2, 0x3) ! Load Group + add %o1, 2, %o1 ! IEU0 + ASI_SETDST_NOBLK ! LSU Group + add %o0, 2, %o0 ! IEU0 Group + EX(STH %g2, [%o0 - 0x2] ASINORMAL, + and %o2, 0x3) ! Store +1: be,pt %xcc, 1f ! CTI + nop ! IEU0 Group + ASI_SETSRC_NOBLK ! LSU Group + EX(LDUB [%o1] ASINORMAL, %g2, + add %g0, 1) ! Load Group + ASI_SETDST_NOBLK ! LSU Group + EX(STB %g2, [%o0] ASINORMAL, + add %g0, 1) ! Store Group + bubble +1: NORMAL_RETL +#endif /* REGS_64BIT */ + +memcpy_noVIS_misaligned: + brz,pt %g2, 2f ! CTI Group + mov 8, %g1 ! IEU0 + sub %g1, %g2, %g2 ! IEU0 Group + sub %o2, %g2, %o2 ! IEU0 Group +1: ASI_SETSRC_NOBLK ! LSU Group + EX(LDUB [%o1] ASINORMAL, %g5, + add %o2, %g2) ! Load Group + add %o1, 1, %o1 ! IEU0 + add %o0, 1, %o0 ! IEU1 + ASI_SETDST_NOBLK ! LSU Group + subcc %g2, 1, %g2 ! IEU1 Group + bne,pt %xcc, 1b ! CTI + EX2(STB %g5, [%o0 - 1] ASINORMAL, + add %o2, %g2, %o2, + add %o2, 1) ! Store +2: +#ifdef __KERNEL__ + wr %g0, FPRS_FEF, %fprs ! FPU Group +#endif + andn %o2, 7, %g5 ! IEU0 Group + and %o2, 7, %o2 ! IEU1 + fmovd %f0, %f2 ! FPU + ASI_SETSRC_NOBLK ! LSU Group + alignaddr %o1, %g0, %g1 ! GRU Group + EXO2(LDDF [%g1] ASINORMAL, %f4) ! Load Group +1: EX(LDDF [%g1 + 0x8] ASINORMAL, %f6, + add %o2, %g5) ! Load Group + add %g1, 0x8, %g1 ! IEU0 Group + subcc %g5, 8, %g5 ! IEU1 + ASI_SETDST_NOBLK ! LSU Group + faligndata %f4, %f6, %f0 ! GRU Group + EX2(STDF %f0, [%o0] ASINORMAL, + add %o2, %g5, %o2, + add %o2, 8) ! Store + add %o1, 8, %o1 ! IEU0 Group + be,pn %xcc, end_cruft ! CTI + add %o0, 8, %o0 ! IEU1 + ASI_SETSRC_NOBLK ! LSU Group + EX(LDDF [%g1 + 0x8] ASINORMAL, %f4, + add %o2, %g5) ! Load Group + add %g1, 8, %g1 ! IEU0 + subcc %g5, 8, %g5 ! IEU1 + ASI_SETDST_NOBLK ! LSU Group + faligndata %f6, %f4, %f0 ! GRU Group + EX2(STDF %f0, [%o0] ASINORMAL, + add %o2, %g5, %o2, + add %o2, 8) ! Store + add %o1, 8, %o1 ! IEU0 + ASI_SETSRC_NOBLK ! LSU Group + bne,pn %xcc, 1b ! CTI Group + add %o0, 8, %o0 ! IEU0 +end_cruft: + brz,pn %o2, fpu_retl ! CTI Group +#ifndef __KERNEL__ + nop ! IEU0 +#else + ASI_SETSRC_NOBLK ! LSU Group +#endif + EXO2(LDUB [%o1] ASINORMAL, %g5) ! LOAD + add %o1, 1, %o1 ! IEU0 + add %o0, 1, %o0 ! IEU1 + ASI_SETDST_NOBLK ! LSU Group + subcc %o2, 1, %o2 ! IEU1 + bne,pt %xcc, vis_slp ! CTI + EX(STB %g5, [%o0 - 1] ASINORMAL, + add %o2, 1) ! Store Group +fpu_retl: + FPU_RETL + +#ifdef __KERNEL__ + .section .fixup + .align 4 +VIScopyfixup_reto2: + mov %o2, %o0 +VIScopyfixup_ret: + retl + wr %g0, 0, %fprs +VIScopyfixup1: subcc %g2, 18, %g2 + bgeu,a,pt %icc, VIScopyfixup1 + sub %g7, 32, %g7 + rd %pc, %g5 + add %g2, 18, %g2 + add %g2, 20, %g2 + ldub [%g5 + %g2], %g2 + ba,a,pt %xcc, 2f +.byte 0, 0, 0, 0, 0, 0, 0, 4, 4, 8, 12, 12, 16, 20, 20, 24, 28, 28 + .align 4 +VIScopyfixup2: mov (7 * 16), %g7 +1: subcc %g2, 10, %g2 + bgeu,a,pt %icc, 1b + sub %g7, 16, %g7 + rd %pc, %g5 + add %g2, 10, %g2 + add %g2, 20, %g2 + ldub [%g5 + %g2], %g2 + ba,a,pt %xcc, 4f +.byte 0, 0, 0, 0, 0, 4, 4, 8, 12, 12 + .align 4 +VIScopyfixup3: subcc %g2, 10, %g2 + bgeu,a,pt %icc, VIScopyfixup3 + sub %g7, 32, %g7 + rd %pc, %g5 + add %g2, 10, %g2 + add %g2, 20, %g2 + ldub [%g5 + %g2], %g2 + ba,a,pt %xcc, 2f +.byte 0, 0, 0, 0, 0, 0, 0, 8, 16, 24 + .align 4 +2: and %g1, 0x7f, %g1 + sub %g7, %g2, %g7 + ba,pt %xcc, VIScopyfixup_ret + add %g7, %g1, %o0 +VIScopyfixup4: mov (7 * 16), %g7 +3: subcc %g2, 6, %g2 + bgeu,a,pt %icc, 3b + sub %g7, 16, %g7 + rd %pc, %g5 + add %g2, 6, %g2 + add %g2, 20, %g2 + ldub [%g5 + %g2], %g2 + ba,a,pt %xcc, 4f +.byte 0, 0, 0, 0, 0, 8 + .align 4 +4: and %g1, 7, %g1 + ba,pt %xcc, VIScopyfixup_ret + add %g7, %g1, %o0 +VIScopyfixup_vis3: + sub %o2, 0x80, %o2 +VIScopyfixup_vis2: + add %o2, 0x40, %o2 +VIScopyfixup_vis0: + add %o2, 0x80, %o2 +VIScopyfixup_vis1: + add %g7, %g3, %g7 + ba,pt %xcc, VIScopyfixup_ret + add %o2, %g7, %o0 +VIScopyfixup_vis5: + add %g3, 8, %g3 +VIScopyfixup_vis4: + add %g3, 8, %g3 + ba,pt %xcc, VIScopyfixup_ret + add %o2, %g3, %o0 +#endif + +#ifdef __KERNEL__ + .text + .align 32 + + .globl __memmove + .type __memmove,@function + + .globl memmove + .type memmove,@function + +memmove: +__memmove: cmp %o0, %o1 + blu,pt %xcc, memcpy_private + sub %o0, %o1, %g5 + add %o1, %o2, %g3 + cmp %g3, %o0 + bleu,pt %xcc, memcpy_private + add %o1, %o2, %g5 + add %o0, %o2, %o5 + + sub %g5, 1, %o1 + sub %o5, 1, %o0 +1: ldub [%o1], %g5 + subcc %o2, 1, %o2 + sub %o1, 1, %o1 + stb %g5, [%o0] + bne,pt %icc, 1b + sub %o0, 1, %o0 + + retl + clr %o0 +#endif diff --git a/arch/sparc64/lib/VISmemset.S b/arch/sparc64/lib/VISmemset.S new file mode 100644 index 000000000000..bdcba34fc76c --- /dev/null +++ b/arch/sparc64/lib/VISmemset.S @@ -0,0 +1,228 @@ +/* $Id: VISmemset.S,v 1.4 1997/07/02 19:00:39 jj Exp $ + * VISmemset.S: High speed memset operations utilizing the UltraSparc + * Visual Instruction Set. + * + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1996, 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + */ + +#include "VIS.h" + +#ifdef REGS_64BIT +#define SET_BLOCKS(base, offset, source) \ + stx source, [base - offset - 0x18]; \ + stx source, [base - offset - 0x10]; \ + stx source, [base - offset - 0x08]; \ + stx source, [base - offset - 0x00]; +#else +#define SET_BLOCKS(base, offset, source) \ + stw source, [base - offset - 0x18]; \ + stw source, [base - offset - 0x14]; \ + stw source, [base - offset - 0x10]; \ + stw source, [base - offset - 0x0c]; \ + stw source, [base - offset - 0x08]; \ + stw source, [base - offset - 0x04]; \ + stw source, [base - offset - 0x00]; \ + stw source, [base - offset + 0x04]; +#endif + +#ifndef __KERNEL__ +/* So that the brz,a,pt in memset doesn't have to get through PLT, here we go... */ +#include "VISbzero.S" +#endif + +#ifdef __KERNEL__ +#define RETL clr %o0 +#else +#define RETL mov %g3, %o0 +#endif + + /* Well, memset is a lot easier to get right than bcopy... */ + .text + .align 32 +#ifdef __KERNEL__ + .globl __memset +__memset: +#endif + .globl memset +memset: +#ifndef __KERNEL__ + brz,a,pt %o1, bzero_private + mov %o2, %o1 +#ifndef REGS_64BIT + srl %o2, 0, %o2 +#endif + mov %o0, %g3 +#endif + cmp %o2, 7 + bleu,pn %xcc, 17f + andcc %o0, 3, %g5 + be,pt %xcc, 4f + and %o1, 0xff, %o1 + cmp %g5, 3 + be,pn %xcc, 2f + stb %o1, [%o0 + 0x00] + cmp %g5, 2 + be,pt %xcc, 2f + stb %o1, [%o0 + 0x01] + stb %o1, [%o0 + 0x02] +2: sub %g5, 4, %g5 + sub %o0, %g5, %o0 + add %o2, %g5, %o2 +4: sllx %o1, 8, %g1 + andcc %o0, 4, %g0 + or %o1, %g1, %o1 + sllx %o1, 16, %g1 + or %o1, %g1, %o1 + be,pt %xcc, 2f +#ifdef REGS_64BIT + sllx %o1, 32, %g1 +#else + cmp %o2, 128 +#endif + stw %o1, [%o0] + sub %o2, 4, %o2 + add %o0, 4, %o0 +2: +#ifdef REGS_64BIT + cmp %o2, 128 + or %o1, %g1, %o1 +#endif + blu,pn %xcc, 9f + andcc %o0, 0x38, %g5 + be,pn %icc, 6f + mov 64, %o5 + andcc %o0, 8, %g0 + be,pn %icc, 1f + sub %o5, %g5, %o5 +#ifdef REGS_64BIT + stx %o1, [%o0] +#else + stw %o1, [%o0] + stw %o1, [%o0 + 4] +#endif + add %o0, 8, %o0 +1: andcc %o5, 16, %g0 + be,pn %icc, 1f + sub %o2, %o5, %o2 +#ifdef REGS_64BIT + stx %o1, [%o0] + stx %o1, [%o0 + 8] +#else + stw %o1, [%o0] + stw %o1, [%o0 + 4] + stw %o1, [%o0 + 8] + stw %o1, [%o0 + 12] +#endif + add %o0, 16, %o0 +1: andcc %o5, 32, %g0 + be,pn %icc, 7f + andncc %o2, 0x3f, %o3 +#ifdef REGS_64BIT + stx %o1, [%o0] + stx %o1, [%o0 + 8] + stx %o1, [%o0 + 16] + stx %o1, [%o0 + 24] +#else + stw %o1, [%o0] + stw %o1, [%o0 + 4] + stw %o1, [%o0 + 8] + stw %o1, [%o0 + 12] + stw %o1, [%o0 + 16] + stw %o1, [%o0 + 20] + stw %o1, [%o0 + 24] + stw %o1, [%o0 + 28] +#endif + add %o0, 32, %o0 +7: be,pn %xcc, 9f +#ifdef __KERNEL__ + wr %g0, FPRS_FEF, %fprs +#endif + ldd [%o0 - 8], %f0 +18: wr %g0, ASI_BLK_P, %asi + membar #StoreStore | #LoadStore + andcc %o3, 0xc0, %g5 + and %o2, 0x3f, %o2 + fmovd %f0, %f2 + fmovd %f0, %f4 + andn %o3, 0xff, %o3 + fmovd %f0, %f6 + cmp %g5, 64 + fmovd %f0, %f8 + fmovd %f0, %f10 + fmovd %f0, %f12 + brz,pn %g5, 10f + fmovd %f0, %f14 + be,pn %icc, 2f + stda %f0, [%o0 + 0x00] %asi + cmp %g5, 128 + be,pn %icc, 2f + stda %f0, [%o0 + 0x40] %asi + stda %f0, [%o0 + 0x80] %asi +2: brz,pn %o3, 12f + add %o0, %g5, %o0 +10: stda %f0, [%o0 + 0x00] %asi + stda %f0, [%o0 + 0x40] %asi + stda %f0, [%o0 + 0x80] %asi + stda %f0, [%o0 + 0xc0] %asi +11: subcc %o3, 256, %o3 + bne,pt %xcc, 10b + add %o0, 256, %o0 +12: +#ifdef __KERNEL__ + wr %g0, 0, %fprs +#endif + membar #Sync +9: andcc %o2, 0x78, %g5 + be,pn %xcc, 13f + andcc %o2, 7, %o2 +14: rd %pc, %o4 +#ifdef REGS_64BIT + srl %g5, 1, %o3 + sub %o4, %o3, %o4 +#else + sub %o4, %g5, %o4 +#endif + jmpl %o4 + (13f - 14b), %g0 + add %o0, %g5, %o0 +12: SET_BLOCKS(%o0, 0x68, %o1) + SET_BLOCKS(%o0, 0x48, %o1) + SET_BLOCKS(%o0, 0x28, %o1) + SET_BLOCKS(%o0, 0x08, %o1) +13: be,pn %xcc, 8f + andcc %o2, 4, %g0 + be,pn %xcc, 1f + andcc %o2, 2, %g0 + stw %o1, [%o0] + add %o0, 4, %o0 +1: be,pn %xcc, 1f + andcc %o2, 1, %g0 + sth %o1, [%o0] + add %o0, 2, %o0 +1: bne,a,pn %xcc, 8f + stb %o1, [%o0] +8: retl + RETL +17: brz,pn %o2, 0f +8: add %o0, 1, %o0 + subcc %o2, 1, %o2 + bne,pt %xcc, 8b + stb %o1, [%o0 - 1] +0: retl + RETL +6: +#ifdef REGS_64BIT + stx %o1, [%o0] +#else + stw %o1, [%o0] + stw %o1, [%o0 + 4] +#endif + andncc %o2, 0x3f, %o3 + be,pn %xcc, 9b +#ifdef __KERNEL__ + wr %g0, FPRS_FEF, %fprs +#else + nop +#endif + ba,pt %xcc, 18b + ldd [%o0], %f0 diff --git a/arch/sparc64/lib/blockops.S b/arch/sparc64/lib/blockops.S index d3ed456ca2de..59083aa028b6 100644 --- a/arch/sparc64/lib/blockops.S +++ b/arch/sparc64/lib/blockops.S @@ -1,170 +1,70 @@ -/* $Id: blockops.S,v 1.7 1997/06/14 21:27:55 davem Exp $ +/* $Id: blockops.S,v 1.10 1997/06/24 17:29:10 jj Exp $ * arch/sparc64/lib/blockops.S: UltraSparc block zero optimized routines. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ -#include +#include "VIS.h" -#undef VIS_BLOCKOPS + .text + .align 32 - /* Zero out 256 bytes of memory at (buf + offset). */ -#define BLAST_BLOCK(buf, offset) \ - stda %f48, [buf + offset + 0x00] %asi; \ - stda %f48, [buf + offset + 0x40] %asi; \ - stda %f48, [buf + offset + 0x80] %asi; \ - stda %f48, [buf + offset + 0xc0] %asi; + .globl __bfill64 +__bfill64: /* %o0 = buf, %o1= ptr to pattern */ + wr %g0, FPRS_FEF, %fprs ! FPU Group + ldd [%o1], %f48 ! Load Group + wr %g0, ASI_BLK_P, %asi ! LSU Group + membar #StoreStore | #LoadStore ! LSU Group + mov 32, %g2 ! IEU0 Group - /* Copy 256 bytes of memory at (src + offset) to - * (dst + offset). + /* Cannot perform real arithmatic on the pattern, that can + * lead to fp_exception_other ;-) */ -#define MIRROR_BLOCK(dst, src, offset, sync) \ - ldda [src + offset + 0x000] %asi, %f0; \ - ldda [src + offset + 0x040] %asi, %f16; \ - ldda [src + offset + 0x080] %asi, %f32; \ - ldda [src + offset + 0x0c0] %asi, %f48; \ - membar sync; \ - stda %f0, [dst + offset + 0x000] %asi; \ - stda %f16, [dst + offset + 0x040] %asi; \ - stda %f32, [dst + offset + 0x080] %asi; \ - stda %f48, [dst + offset + 0x0c0] %asi; + fmovd %f48, %f50 ! FPA Group + fmovd %f48, %f52 ! FPA Group + fmovd %f48, %f54 ! FPA Group + fmovd %f48, %f56 ! FPA Group + fmovd %f48, %f58 ! FPA Group + fmovd %f48, %f60 ! FPA Group + fmovd %f48, %f62 ! FPA Group - .text - .align 4 - -#ifdef VIS_BLOCKOPS - .globl __bzero_1page -__bzero_1page: - /* %o0 = buf */ - mov %o0, %o1 - wr %g0, ASI_BLK_P, %asi - mov 0x08, %g2 - membar #Sync|#StoreLoad - fzero %f48 - fzero %f50 - fzero %f52 - fzero %f54 - fzero %f56 - fzero %f58 - fzero %f60 - fzero %f62 -1: - BLAST_BLOCK(%o0, 0x000) - BLAST_BLOCK(%o0, 0x100) - BLAST_BLOCK(%o0, 0x200) - BLAST_BLOCK(%o0, 0x300) - subcc %g2, 1, %g2 - bne,pt %icc, 1b - add %o0, 0x400, %o0 +1: stda %f48, [%o0 + 0x00] %asi ! Store Group + stda %f48, [%o0 + 0x40] %asi ! Store Group + stda %f48, [%o0 + 0x80] %asi ! Store Group + stda %f48, [%o0 + 0xc0] %asi ! Store Group + subcc %g2, 1, %g2 ! IEU1 Group + bne,pt %icc, 1b ! CTI + add %o0, 0x100, %o0 ! IEU0 + membar #Sync ! LSU Group - membar #Sync|#LoadStore|#StoreStore - - retl - mov %o1, %o0 -#else -#define BZERO_256BYTES(base) \ - stx %g0, [base + 0x00]; stx %g0, [base + 0x08]; \ - stx %g0, [base + 0x10]; stx %g0, [base + 0x18]; \ - stx %g0, [base + 0x20]; stx %g0, [base + 0x28]; \ - stx %g0, [base + 0x30]; stx %g0, [base + 0x38]; \ - stx %g0, [base + 0x40]; stx %g0, [base + 0x48]; \ - stx %g0, [base + 0x50]; stx %g0, [base + 0x58]; \ - stx %g0, [base + 0x60]; stx %g0, [base + 0x68]; \ - stx %g0, [base + 0x70]; stx %g0, [base + 0x78]; \ - stx %g0, [base + 0x80]; stx %g0, [base + 0x88]; \ - stx %g0, [base + 0x90]; stx %g0, [base + 0x98]; \ - stx %g0, [base + 0xa0]; stx %g0, [base + 0xa8]; \ - stx %g0, [base + 0xb0]; stx %g0, [base + 0xb8]; \ - stx %g0, [base + 0xc0]; stx %g0, [base + 0xc8]; \ - stx %g0, [base + 0xd0]; stx %g0, [base + 0xd8]; \ - stx %g0, [base + 0xe0]; stx %g0, [base + 0xe8]; \ - stx %g0, [base + 0xf0]; stx %g0, [base + 0xf8]; + jmpl %o7 + 0x8, %g0 ! CTI Group brk forced + wr %g0, 0, %fprs ! FPU Group .align 32 .globl __bzero_1page __bzero_1page: - mov 32, %g1 -1: - BZERO_256BYTES(%g2) - subcc %g1, 1, %g1 - bne,pt %icc, 1b - add %g2, 0x100, %g2 - jmpl %o7 + 0x8, %g0 - mov %g3, %o7 -#endif - - .globl __bfill64 -__bfill64: -#if 1 - /* %o0 = buf, %o1 = 64-bit pattern */ -#define FILL_BLOCK(buf, offset) \ - stx %o1, [buf + offset + 0x38]; \ - stx %o1, [buf + offset + 0x30]; \ - stx %o1, [buf + offset + 0x28]; \ - stx %o1, [buf + offset + 0x20]; \ - stx %o1, [buf + offset + 0x18]; \ - stx %o1, [buf + offset + 0x10]; \ - stx %o1, [buf + offset + 0x08]; \ - stx %o1, [buf + offset + 0x00]; - - mov 0x20, %g2 -1: - FILL_BLOCK(%o0, 0x00) - FILL_BLOCK(%o0, 0x40) - FILL_BLOCK(%o0, 0x80) - FILL_BLOCK(%o0, 0xc0) - subcc %g2, 1, %g2 - bne,pt %icc, 1b - add %o0, 0x100, %o0 - retl - nop -#undef FILL_BLOCK - -#else - /* %o0 = buf */ - stx %o1, [%sp + 0x7ff + 128] - wr %g0, ASI_BLK_P, %asi - mov 0x08, %g2 - ldd [%sp + 0x7ff + 128], %f48 - membar #Sync|#StoreLoad - fmovd %f48, %f50 - fmovd %f48, %f52 - fmovd %f48, %f54 - fmovd %f48, %f56 - fmovd %f48, %f58 - fmovd %f48, %f60 - fmovd %f48, %f62 -1: - BLAST_BLOCK(%o0, 0x000) - BLAST_BLOCK(%o0, 0x100) - BLAST_BLOCK(%o0, 0x200) - BLAST_BLOCK(%o0, 0x300) - subcc %g2, 1, %g2 - bne,pt %icc, 1b - add %o0, 0x400, %o0 - - retl - membar #Sync|#LoadStore|#StoreStore -#endif + wr %g0, FPRS_FEF, %fprs ! FPU Group + fzero %f0 ! FPA Group + mov 32, %g1 ! IEU0 + fzero %f2 ! FPA Group + faddd %f0, %f2, %f4 ! FPA Group + fmuld %f0, %f2, %f6 ! FPM + faddd %f0, %f2, %f8 ! FPA Group + fmuld %f0, %f2, %f10 ! FPM -#if 0 - .globl __copy_1page -__copy_1page: - /* %o0 = dst, %o1 = src */ - or %g0, 0x08, %g1 - wr %g0, ASI_BLK_P, %asi - membar #Sync|#StoreLoad -1: - MIRROR_BLOCK(%o0, %o1, 0x000, #Sync) - MIRROR_BLOCK(%o0, %o1, 0x100, #Sync) - MIRROR_BLOCK(%o0, %o1, 0x200, #Sync) - MIRROR_BLOCK(%o0, %o1, 0x300, #Sync) - subcc %g1, 1, %g1 - add %o0, 0x400, %o0 - bne,pt %icc, 1b - add %o1, 0x400, %o1 + faddd %f0, %f2, %f12 ! FPA Group + fmuld %f0, %f2, %f14 ! FPM + wr %g0, ASI_BLK_P, %asi ! LSU Group + membar #StoreStore | #LoadStore ! LSU Group +1: stda %f0, [%o0 + 0x00] %asi ! Store Group + stda %f0, [%o0 + 0x40] %asi ! Store Group + stda %f0, [%o0 + 0x80] %asi ! Store Group + stda %f0, [%o0 + 0xc0] %asi ! Store Group - retl - membar #Sync|#LoadStore|#StoreStore -#endif + subcc %g1, 1, %g1 ! IEU1 + bne,pt %icc, 1b ! CTI + add %o0, 0x100, %o0 ! IEU0 Group + membar #Sync ! LSU Group + jmpl %o7 + 0x8, %g0 ! CTI Group brk forced + wr %g0, 0, %fprs ! FPU Group diff --git a/arch/sparc64/lib/checksum.S b/arch/sparc64/lib/checksum.S index 10eebb8df335..179e17960e76 100644 --- a/arch/sparc64/lib/checksum.S +++ b/arch/sparc64/lib/checksum.S @@ -17,383 +17,609 @@ #include #include #include +#include -#define CSUM_BIGCHUNK(buf, offset, sum, t0, t1, t2, t3, t4, t5) \ - ldd [buf + offset + 0x00], t0; \ - ldd [buf + offset + 0x08], t2; \ - addccc t0, sum, sum; \ - addccc t1, sum, sum; \ - ldd [buf + offset + 0x10], t4; \ - addccc t2, sum, sum; \ - addccc t3, sum, sum; \ - ldd [buf + offset + 0x18], t0; \ - addccc t4, sum, sum; \ - addccc t5, sum, sum; \ - addccc t0, sum, sum; \ - addccc t1, sum, sum; + /* The problem with the "add with carry" instructions on Ultra + * are two fold. Firstly, they cannot pair with jack shit, + * and also they only add in the 32-bit carry condition bit + * into the accumulated sum. The following is much better. + * + * This should run at max bandwidth for ecache hits, a better + * technique is to use VIS and fpu operations somehow, but + * this requires more reasoning on my part... + * + * Assuming ecache hits and branches predicted well, this + * can be expected to run at a rate of 16 cycles per 64-bytes + * of data summed. (the old code summed 32 bytes in 20 + * cycles, with numerous bubbles and unnecessary stalls) + */ +#define CSUM_ECACHE_LOAD(buf, offset, t0, t1, t2, t3, t4, t5, t6, t7) \ + ldx [buf + offset + 0x00], t0; \ + ldx [buf + offset + 0x08], t1; \ + ldx [buf + offset + 0x10], t2; \ + ldx [buf + offset + 0x18], t3; \ + ldx [buf + offset + 0x20], t4; \ + ldx [buf + offset + 0x28], t5; \ + ldx [buf + offset + 0x30], t6; \ + ldx [buf + offset + 0x38], t7; \ + nop; nop; /* THIS IS CRITICAL!!!!!!!!! */ + +#define CSUM_ECACHE_BLOCK_LDNEXT(buf, offset, sum, t0, t1, t2, t3, t4, t5, t6, t7) \ + addcc sum, t0, sum; \ + bcc,pt %xcc, 11f; \ + ldx [buf + offset + 0x00], t0; \ + add sum, 1, sum; \ +11: addcc sum, t1, sum; \ + bcc,pt %xcc, 12f; \ + ldx [buf + offset + 0x08], t1; \ + add sum, 1, sum; \ +12: addcc sum, t2, sum; \ + bcc,pt %xcc, 13f; \ + ldx [buf + offset + 0x10], t2; \ + add sum, 1, sum; \ +13: addcc sum, t3, sum; \ + bcc,pt %xcc, 14f; \ + ldx [buf + offset + 0x18], t3; \ + add sum, 1, sum; \ +14: addcc sum, t4, sum; \ + bcc,pt %xcc, 15f; \ + ldx [buf + offset + 0x20], t4; \ + add sum, 1, sum; \ +15: addcc sum, t5, sum; \ + bcc,pt %xcc, 16f; \ + ldx [buf + offset + 0x28], t5; \ + add sum, 1, sum; \ +16: addcc sum, t6, sum; \ + bcc,pt %xcc, 17f; \ + ldx [buf + offset + 0x30], t6; \ + add sum, 1, sum; \ +17: addcc sum, t7, sum; \ + bcc,pt %xcc, 18f; \ + ldx [buf + offset + 0x38], t7; \ + add sum, 1, sum; \ +18: nop; nop; /* DO NOT TOUCH! */ + +#define CSUM_ECACHE_BLOCK(sum, t0, t1, t2, t3, t4, t5, t6, t7) \ + addcc sum, t0, sum; \ + bcs,a,pn %xcc, 21f; \ + add sum, 1, sum; \ +21: addcc sum, t1, sum; \ + bcs,a,pn %xcc, 22f; \ + add sum, 1, sum; \ +22: addcc sum, t2, sum; \ + bcs,a,pn %xcc, 23f; \ + add sum, 1, sum; \ +23: addcc sum, t3, sum; \ + bcs,a,pn %xcc, 24f; \ + add sum, 1, sum; \ +24: addcc sum, t4, sum; \ + bcs,a,pn %xcc, 25f; \ + add sum, 1, sum; \ +25: addcc sum, t5, sum; \ + bcs,a,pn %xcc, 26f; \ + add sum, 1, sum; \ +26: addcc sum, t6, sum; \ + bcs,a,pn %xcc, 27f; \ + add sum, 1, sum; \ +27: addcc sum, t7, sum; \ + bcs,a,pn %xcc, 28f; \ + add sum, 1, sum; \ +28: -#define CSUM_LASTCHUNK(buf, offset, sum, t0, t1, t2, t3) \ - ldd [buf - offset - 0x08], t0; \ - ldd [buf - offset - 0x00], t2; \ - addccc t0, sum, sum; \ - addccc t1, sum, sum; \ - addccc t2, sum, sum; \ - addccc t3, sum, sum; +#define CSUM_LASTCHUNK(buf, offset, sum, t0, t1) \ + ldx [buf - offset - 0x08], t0; \ + ldx [buf - offset - 0x00], t1; \ + addcc t0, sum, sum; \ + bcs,a,pn %xcc, 31f; \ + add sum, 1, sum; \ +31: addcc t1, sum, sum; \ + bcs,a,pn %xcc, 32f; \ + add sum, 1, sum; \ +32: - /* Do end cruft out of band to get better cache patterns. */ + .text + /* Keep this garbage from swiping the icache. */ csum_partial_end_cruft: - andcc %o1, 8, %g0 ! check how much - be,pn %icc, 1f ! caller asks %o1 & 0x8 - and %o1, 4, %g5 ! nope, check for word remaining - ldd [%o0], %g2 ! load two - addcc %g2, %o2, %o2 ! add first word to sum - addccc %g3, %o2, %o2 ! add second word as well - add %o0, 8, %o0 ! advance buf ptr - addc %g0, %o2, %o2 ! add in final carry -1: brz,pn %g5, 1f ! nope, skip this code - andcc %o1, 3, %o1 ! check for trailing bytes - ld [%o0], %g2 ! load it - addcc %g2, %o2, %o2 ! add to sum - add %o0, 4, %o0 ! advance buf ptr - addc %g0, %o2, %o2 ! add in final carry -1: brz,pn %o1, 1f ! no trailing bytes, return - addcc %o1, -1, %g0 ! only one byte remains? - bne,pn %icc, 2f ! at least two bytes more - subcc %o1, 2, %o1 ! only two bytes more? - ba,pt %xcc, 4f ! only one byte remains - clr %o4 ! clear fake hword value -2: lduh [%o0], %o4 ! get hword - be,pn %icc, 6f ! jmp if only hword remains - add %o0, 2, %o0 ! advance buf ptr either way - sll %o4, 16, %o4 ! create upper hword -4: ldub [%o0], %o5 ! get final byte - sll %o5, 8, %o5 ! put into place - or %o5, %o4, %o4 ! coalese with hword (if any) -6: addcc %o4, %o2, %o2 ! add to sum -1: sllx %g4, 32, %g4 ! give gfp back - addc %g0, %o2, %o0 ! add final carry into retval - retl ! get outta here - srl %o0, 0, %o0 + andcc %o1, 8, %g0 ! IEU1 Group + be,pn %icc, 1f ! CTI + and %o1, 4, %g5 ! IEU0 + ldx [%o0 + 0x00], %g2 ! Load Group + add %o0, 0x8, %o0 ! IEU0 + addcc %g2, %o2, %o2 ! IEU1 Group + 2 bubbles + bcs,a,pn %xcc, 1f ! CTI + add %o2, 1, %o2 ! IEU0 4 clocks (mispredict) +1: andcc %o1, 2, %g0 ! IEU1 Group + brz,pn %g5, 1f ! CTI Group (needs IEU1) + clr %g2 ! IEU0 + ld [%o0], %g2 ! Load + add %o0, 4, %o0 ! IEU0 Group + sllx %g2, 32, %g2 ! IEU0 Group + 2 bubbles +1: and %o1, 1, %o1 ! IEU1 + be,pn %icc, 1f ! CTI + clr %o4 ! IEU0 Group + lduh [%o0], %o4 ! Load + add %o0, 2, %o0 ! IEU1 + sll %o4, 16, %o4 ! IEU0 Group + 2 bubbles +1: brz,pn %o1, 1f ! CTI + clr %o5 ! IEU1 + ldub [%o0], %o5 ! Load Group + sll %o5, 8, %o5 ! IEU0 Group + 2 bubbles +1: or %g2, %o4, %o4 ! IEU1 + or %o5, %o4, %o4 ! IEU0 Group + addcc %o4, %o2, %o2 ! IEU1 Group (regdep) + bcc,pt %xcc, cfold ! CTI + sethi %uhi(PAGE_OFFSET), %g4 ! IEU0 +1: b,pt %xcc, cfold ! CTI Group + add %o2, 1, %o2 ! IEU0 - /* Also do alignment out of band to get better cache patterns. */ -csum_partial_fix_alignment: +csum_partial_fixit: + bl,pn %icc, cpte ! CTI Group + and %o1, 0xf, %o3 ! IEU0 + andcc %o0, 0x2, %g0 ! IEU1 + be,pn %icc, 1f ! CTI Group + and %o0, 0x4, %g7 ! IEU0 + lduh [%o0 + 0x00], %g2 ! Load + sub %o1, 2, %o1 ! IEU0 Group + addcc %o0, 2, %o0 ! IEU1 + and %o0, 0x4, %g7 ! IEU0 Group + sll %g2, 16, %g2 ! IEU0 Group (no load stall) + addcc %g2, %o2, %o2 ! IEU1 Group (regdep) + bcc,pt %icc, 0f ! CTI + andn %o1, 0xff, %o3 ! IEU0 + srl %o2, 16, %g2 ! IEU0 Group + b,pt %xcc, 9f ! CTI + add %g2, 1, %g2 ! IEU1 +0: srl %o2, 16, %g2 ! IEU0 Group 8-( +9: sll %o2, 16, %o2 ! IEU0 Group 8-( + sll %g2, 16, %g3 ! IEU0 Group 8-( + srl %o2, 16, %o2 ! IEU0 Group 8-( + or %g3, %o2, %o2 ! IEU1 +1: brnz,pt %g7, 2f ! CTI Group + sub %o1, 4, %o1 ! IEU0 + b,pt %xcc, csum_partial_aligned ! CTI Group + add %o1, 4, %o1 ! IEU0 +2: ld [%o0 + 0x00], %g2 ! Load Group + add %o0, 4, %o0 ! IEU0 + andn %o1, 0xff, %o3 ! IEU1 + addcc %g2, %o2, %o2 ! IEU1 Group + 2 bubbles + bcc,pt %xcc, csum_partial_aligned ! CTI + nop ! IEU0 + b,pt %xcc, csum_partial_aligned ! CTI Group + add %o2, 1, %o2 ! IEU0 - /* The common case is to get called with a nicely aligned - * buffer of size 0x20. Follow the code path for that case. - */ - .globl csum_partial + .align 32 + .globl csum_partial csum_partial: /* %o0=buf, %o1=len, %o2=sum */ - srl %o1, 0, %o1 ! doof scheiss - andcc %o0, 0x7, %g0 ! alignment problems? - srl %o2, 0, %o2 - be,pt %icc, csum_partial_fix_aligned ! yep, handle it - andn %o1, 0x7f, %o3 ! num loop iterations - cmp %o1, 6 - bl,pn %icc, cpte - 0x4 - andcc %o0, 0x2, %g0 - be,pn %icc, 1f - and %o0, 0x4, %g7 - lduh [%o0 + 0x00], %g2 - sub %o1, 2, %o1 - add %o0, 2, %o0 - sll %g2, 16, %g2 - addcc %g2, %o2, %o2 - srl %o2, 16, %g3 - addc %g0, %g3, %g2 - sll %o2, 16, %o2 - and %o0, 0x4, %g7 - sll %g2, 16, %g3 - srl %o2, 16, %o2 - or %g3, %o2, %o2 -1: brz,pn %g7, csum_partial_fix_aligned - andn %o1, 0x7f, %o3 - ld [%o0 + 0x00], %g2 - sub %o1, 4, %o1 - addcc %g2, %o2, %o2 - add %o0, 4, %o0 - andn %o1, 0x7f, %o3 - addc %g0, %o2, %o2 -csum_partial_fix_aligned: - brz,pt %o3, 3f ! none to do - andcc %o1, 0x70, %g1 ! clears carry flag too -5: CSUM_BIGCHUNK(%o0, 0x00, %o2, %o4, %o5, %g2, %g3, %g4, %g5) - CSUM_BIGCHUNK(%o0, 0x20, %o2, %o4, %o5, %g2, %g3, %g4, %g5) - CSUM_BIGCHUNK(%o0, 0x40, %o2, %o4, %o5, %g2, %g3, %g4, %g5) - CSUM_BIGCHUNK(%o0, 0x60, %o2, %o4, %o5, %g2, %g3, %g4, %g5) - addc %g0, %o2, %o2 ! sink in final carry - subcc %o3, 128, %o3 ! detract from loop iters - bne,pt %icc, 5b ! more to do - add %o0, 128, %o0 ! advance buf ptr -3: brz,pn %g1, cpte ! nope - andcc %o1, 0xf, %o3 ! anything left at all? -10: rd %pc, %g7 ! get pc - srl %g1, 1, %o4 ! compute offset - sub %g7, %g1, %g7 ! adjust jmp ptr - sub %g7, %o4, %g7 ! final jmp ptr adjust - jmp %g7 + (11f-10b) ! enter the table - add %o0, %g1, %o0 ! advance buf ptr -cptbl: CSUM_LASTCHUNK(%o0, 0x68, %o2, %g2, %g3, %g4, %g5) - CSUM_LASTCHUNK(%o0, 0x58, %o2, %g2, %g3, %g4, %g5) - CSUM_LASTCHUNK(%o0, 0x48, %o2, %g2, %g3, %g4, %g5) - CSUM_LASTCHUNK(%o0, 0x38, %o2, %g2, %g3, %g4, %g5) - CSUM_LASTCHUNK(%o0, 0x28, %o2, %g2, %g3, %g4, %g5) - CSUM_LASTCHUNK(%o0, 0x18, %o2, %g2, %g3, %g4, %g5) - CSUM_LASTCHUNK(%o0, 0x08, %o2, %g2, %g3, %g4, %g5) -11: addc %g0, %o2, %o2 ! fetch final carry - andcc %o1, 0xf, %o3 ! anything left at all? -cpte: brnz,pn %o3, csum_partial_end_cruft ! yep, handle it - sethi %uhi(KERNBASE), %g4 - mov %o2, %o0 ! return computed csum - retl ! get outta here - sllx %g4, 32, %g4 ! give gfp back + andcc %o0, 0x7, %g0 ! IEU1 Group + srl %o1, 0, %o1 ! IEU0 + srl %o2, 0, %o2 ! IEU0 Group + be,pt %icc, csum_partial_aligned ! CTI + andn %o1, 0xff, %o3 ! IEU1 + b,pt %xcc, csum_partial_fixit ! CTI Group + cmp %o1, 6 ! IEU0 + nop +csum_partial_aligned: + brz,pt %o3, 3f ! CTI Group + and %o1, 0xf0, %g1 ! IEU0 +5: CSUM_ECACHE_LOAD( %o0, 0x000, %o4, %o5, %g2, %g3, %g4, %g5, %g1, %g7) + CSUM_ECACHE_BLOCK_LDNEXT(%o0, 0x040, %o2, %o4, %o5, %g2, %g3, %g4, %g5, %g1, %g7) + CSUM_ECACHE_BLOCK_LDNEXT(%o0, 0x080, %o2, %o4, %o5, %g2, %g3, %g4, %g5, %g1, %g7) + CSUM_ECACHE_BLOCK_LDNEXT(%o0, 0x0c0, %o2, %o4, %o5, %g2, %g3, %g4, %g5, %g1, %g7) + CSUM_ECACHE_BLOCK( %o2, %o4, %o5, %g2, %g3, %g4, %g5, %g1, %g7) + subcc %o3, 256, %o3 ! IEU1 Group + bne,pt %icc, 5b ! CTI + add %o0, 256, %o0 ! IEU0 + and %o1, 0xf0, %g1 ! IEU0 Group +3: brz,pn %g1, cpte ! CTI + and %o1, 0xf, %o3 ! IEU1 Group +10: rd %pc, %g7 ! LSU Group + 4 clocks + sll %g1, 1, %o4 ! IEU0 Group + sub %g7, %o4, %g7 ! IEU1 + jmp %g7 + %lo(cpte - 10b) ! CTI Group brk forced + add %o0, %g1, %o0 ! IEU0 +cptbl: CSUM_LASTCHUNK(%o0, 0xe8, %o2, %g2, %g3) + CSUM_LASTCHUNK(%o0, 0xd8, %o2, %g2, %g3) + CSUM_LASTCHUNK(%o0, 0xc8, %o2, %g2, %g3) + CSUM_LASTCHUNK(%o0, 0xb8, %o2, %g2, %g3) + CSUM_LASTCHUNK(%o0, 0xa8, %o2, %g2, %g3) + CSUM_LASTCHUNK(%o0, 0x98, %o2, %g2, %g3) + CSUM_LASTCHUNK(%o0, 0x88, %o2, %g2, %g3) + CSUM_LASTCHUNK(%o0, 0x78, %o2, %g2, %g3) + CSUM_LASTCHUNK(%o0, 0x68, %o2, %g2, %g3) + CSUM_LASTCHUNK(%o0, 0x58, %o2, %g2, %g3) + CSUM_LASTCHUNK(%o0, 0x48, %o2, %g2, %g3) + CSUM_LASTCHUNK(%o0, 0x38, %o2, %g2, %g3) + CSUM_LASTCHUNK(%o0, 0x28, %o2, %g2, %g3) + CSUM_LASTCHUNK(%o0, 0x18, %o2, %g2, %g3) + CSUM_LASTCHUNK(%o0, 0x08, %o2, %g2, %g3) +cpte: brnz,pn %o3, csum_partial_end_cruft ! CTI Group + sethi %uhi(PAGE_OFFSET), %g4 ! IEU0 +cfold: sllx %o2, 32, %o0 ! IEU0 Group + addcc %o2, %o0, %o0 ! IEU1 Group (regdep) + srlx %o0, 32, %o0 ! IEU0 Group (regdep) + bcs,a,pn %xcc, 1f ! CTI + add %o0, 1, %o0 ! IEU1 4 clocks (mispredict) +1: retl ! CTI Group brk forced + sllx %g4, 32, %g4 ! IEU0 Group .globl __csum_partial_copy_start, __csum_partial_copy_end __csum_partial_copy_start: -#define EX(x,y,a,b,z) \ -98: x,y; \ - .section .fixup,z##alloc,z##execinstr; \ - .align 4; \ -99: ba,pt %xcc, 30f; \ - a, b, %o3; \ - .section __ex_table,z##alloc; \ - .align 8; \ - .xword 98b, 99b; \ - .text; \ - .align 4 + /* I think I have an erection... Once _AGAIN_ the SunSoft + * engineers are caught asleep at the keyboard, tsk tsk... + */ +#define CSUMCOPY_ECACHE_LOAD(src, off, t0, t1, t2, t3, t4, t5, t6, t7) \ + ldxa [src + off + 0x00] %asi, t0; \ + ldxa [src + off + 0x08] %asi, t1; \ + ldxa [src + off + 0x10] %asi, t2; \ + ldxa [src + off + 0x18] %asi, t3; \ + ldxa [src + off + 0x20] %asi, t4; \ + ldxa [src + off + 0x28] %asi, t5; \ + ldxa [src + off + 0x30] %asi, t6; \ + ldxa [src + off + 0x38] %asi, t7; \ + nop; nop; /* DO NOT TOUCH THIS!!!!! */ -#define EX2(x,y,z) \ -98: x,y; \ - .section __ex_table,z##alloc; \ - .align 8; \ - .xword 98b, 30f; \ - .text; \ - .align 4 +#define CSUMCOPY_EC_STALIGNED_LDNXT(src, dest, off, sum, t0, t1, t2, t3, t4, t5, t6, t7)\ + stx t0, [dest + off - 0x40]; \ + addcc sum, t0, sum; \ + bcc,pt %xcc, 11f; \ + ldxa [src + off + 0x00] %asi, t0; \ + add sum, 1, sum; \ +11: stx t1, [dest + off - 0x38]; \ + addcc sum, t1, sum; \ + bcc,pt %xcc, 12f; \ + ldxa [src + off + 0x08] %asi, t1; \ + add sum, 1, sum; \ +12: stx t2, [dest + off - 0x30]; \ + addcc sum, t2, sum; \ + bcc,pt %xcc, 13f; \ + ldxa [src + off + 0x10] %asi, t2; \ + add sum, 1, sum; \ +13: stx t3, [dest + off - 0x28]; \ + addcc sum, t3, sum; \ + bcc,pt %xcc, 14f; \ + ldxa [src + off + 0x18] %asi, t3; \ + add sum, 1, sum; \ +14: stx t4, [dest + off - 0x20]; \ + addcc sum, t4, sum; \ + bcc,pt %xcc, 15f; \ + ldxa [src + off + 0x20] %asi, t4; \ + add sum, 1, sum; \ +15: stx t5, [dest + off - 0x18]; \ + addcc sum, t5, sum; \ + bcc,pt %xcc, 16f; \ + ldxa [src + off + 0x28] %asi, t5; \ + add sum, 1, sum; \ +16: stx t6, [dest + off - 0x10]; \ + addcc sum, t6, sum; \ + bcc,pt %xcc, 17f; \ + ldxa [src + off + 0x30] %asi, t6; \ + add sum, 1, sum; \ +17: stx t7, [dest + off - 0x08]; \ + addcc sum, t7, sum; \ + bcc,pt %xcc, 18f; \ + ldxa [src + off + 0x38] %asi, t7; \ + add sum, 1, sum; \ +18: -#define EX3(x,y,z) \ -98: x,y; \ - .section __ex_table,z##alloc; \ - .align 8; \ - .xword 98b, 96f; \ - .text; \ - .align 4 +#define CSUMCOPY_EC_STUNALIGN_LDNXT(src, dest, off, sum, t0, t1, t2, t3, t4, t5, t6, t7)\ + stw t0, [dest + off - 0x3c]; \ + addcc sum, t0, sum; \ + srlx t0, 32, t0; \ + stw t0, [dest + off - 0x40]; \ + bcc,pt %xcc, 21f; \ + ldxa [src + off + 0x00] %asi, t0; \ + add sum, 1, sum; \ +21: stw t1, [dest + off - 0x34]; \ + addcc sum, t1, sum; \ + srlx t1, 32, t1; \ + stw t1, [dest + off - 0x38]; \ + bcc,pt %xcc, 22f; \ + ldxa [src + off + 0x08] %asi, t1; \ + add sum, 1, sum; \ +22: stw t2, [dest + off - 0x2c]; \ + addcc sum, t2, sum; \ + srlx t2, 32, t2; \ + stw t2, [dest + off - 0x30]; \ + bcc,pt %xcc, 23f; \ + ldxa [src + off + 0x10] %asi, t2; \ + add sum, 1, sum; \ +23: stw t3, [dest + off - 0x24]; \ + addcc sum, t3, sum; \ + srlx t3, 32, t3; \ + stw t3, [dest + off - 0x28]; \ + bcc,pt %xcc, 24f; \ + ldxa [src + off + 0x18] %asi, t3; \ + add sum, 1, sum; \ +24: stw t4, [dest + off - 0x1c]; \ + addcc sum, t4, sum; \ + srlx t4, 32, t4; \ + stw t4, [dest + off - 0x20]; \ + bcc,pt %xcc, 25f; \ + ldxa [src + off + 0x20] %asi, t4; \ + add sum, 1, sum; \ +25: stw t5, [dest + off - 0x14]; \ + addcc sum, t5, sum; \ + srlx t5, 32, t5; \ + stw t5, [dest + off - 0x18]; \ + bcc,pt %xcc, 26f; \ + ldxa [src + off + 0x28] %asi, t5; \ + add sum, 1, sum; \ +26: stw t6, [dest + off - 0x0c]; \ + addcc sum, t6, sum; \ + srlx t6, 32, t6; \ + stw t6, [dest + off - 0x10]; \ + bcc,pt %xcc, 27f; \ + ldxa [src + off + 0x30] %asi, t6; \ + add sum, 1, sum; \ +27: stw t7, [dest + off - 0x04]; \ + addcc sum, t7, sum; \ + srlx t7, 32, t7; \ + stw t7, [dest + off - 0x08]; \ + bcc,pt %xcc, 28f; \ + ldxa [src + off + 0x38] %asi, t7; \ + add sum, 1, sum; \ +28: -#define EXT(start,end,handler,z) \ - .section __ex_table,z##alloc; \ - .align 8; \ - .xword start, 0, end, handler; \ - .text; \ - .align 4 +#define CSUMCOPY_EC_STALIGNED(dest, off, sum, t0, t1, t2, t3, t4, t5, t6, t7) \ + addcc sum, t0, sum; \ + bcc,pt %xcc, 31f; \ + stx t0, [dest + off + 0x00]; \ + add sum, 1, sum; \ +31: addcc sum, t1, sum; \ + bcc,pt %xcc, 32f; \ + stx t1, [dest + off + 0x08]; \ + add sum, 1, sum; \ +32: addcc sum, t2, sum; \ + bcc,pt %xcc, 33f; \ + stx t2, [dest + off + 0x10]; \ + add sum, 1, sum; \ +33: addcc sum, t3, sum; \ + bcc,pt %xcc, 34f; \ + stx t3, [dest + off + 0x18]; \ + add sum, 1, sum; \ +34: addcc sum, t4, sum; \ + bcc,pt %xcc, 35f; \ + stx t4, [dest + off + 0x20]; \ + add sum, 1, sum; \ +35: addcc sum, t5, sum; \ + bcc,pt %xcc, 36f; \ + stx t5, [dest + off + 0x28]; \ + add sum, 1, sum; \ +36: addcc sum, t6, sum; \ + bcc,pt %xcc, 37f; \ + stx t6, [dest + off + 0x30]; \ + add sum, 1, sum; \ +37: addcc sum, t7, sum; \ + bcc,pt %xcc, 38f; \ + stx t7, [dest + off + 0x38]; \ + add sum, 1, sum; \ +38: - /* This aligned version executes typically in 8.5 superscalar cycles, this - * is the best I can do. I say 8.5 because the final add will pair with - * the next ldd in the main unrolled loop. Thus the pipe is always full. - * If you change these macros (including order of instructions), - * please check the fixup code below as well. - */ -#define CSUMCOPY_BIGCHUNK_ALIGNED(src, dst, sum, off, t0, t1, t2, t3, t4, t5, t6, t7) \ - ldda [src + off + 0x00] %asi, t0; \ - ldda [src + off + 0x08] %asi, t2; \ - addccc t0, sum, sum; \ - ldda [src + off + 0x10] %asi, t4; \ - addccc t1, sum, sum; \ - ldda [src + off + 0x18] %asi, t6; \ - addccc t2, sum, sum; \ - std t0, [dst + off + 0x00]; \ - addccc t3, sum, sum; \ - std t2, [dst + off + 0x08]; \ - addccc t4, sum, sum; \ - std t4, [dst + off + 0x10]; \ - addccc t5, sum, sum; \ - std t6, [dst + off + 0x18]; \ - addccc t6, sum, sum; \ - addccc t7, sum, sum; +#define CSUMCOPY_EC_STUNALIGN(dest, off, sum, t0, t1, t2, t3, t4, t5, t6, t7) \ + stw t0, [dest + off + 0x04]; \ + addcc sum, t0, sum; \ + srlx t0, 32, t0; \ + bcc,pt %xcc, 41f; \ + stw t0, [dest + off + 0x00]; \ + add sum, 1, sum; \ +41: stw t1, [dest + off + 0x0c]; \ + addcc sum, t1, sum; \ + srlx t1, 32, t1; \ + bcc,pt %xcc, 42f; \ + stw t1, [dest + off + 0x08]; \ + add sum, 1, sum; \ +42: stw t2, [dest + off + 0x14]; \ + addcc sum, t2, sum; \ + srlx t2, 32, t2; \ + bcc,pt %xcc, 43f; \ + stw t2, [dest + off + 0x10]; \ + add sum, 1, sum; \ +43: stw t3, [dest + off + 0x1c]; \ + addcc sum, t3, sum; \ + srlx t3, 32, t3; \ + bcc,pt %xcc, 44f; \ + stw t3, [dest + off + 0x18]; \ + add sum, 1, sum; \ +44: stw t4, [dest + off + 0x24]; \ + addcc sum, t4, sum; \ + srlx t4, 32, t4; \ + bcc,pt %xcc, 45f; \ + stw t4, [dest + off + 0x20]; \ + add sum, 1, sum; \ +45: stw t5, [dest + off + 0x2c]; \ + addcc sum, t5, sum; \ + srlx t5, 32, t5; \ + bcc,pt %xcc, 46f; \ + stw t5, [dest + off + 0x28]; \ + add sum, 1, sum; \ +46: stw t6, [dest + off + 0x34]; \ + addcc sum, t6, sum; \ + srlx t6, 32, t6; \ + bcc,pt %xcc, 47f; \ + stw t6, [dest + off + 0x30]; \ + add sum, 1, sum; \ +47: stw t7, [dest + off + 0x3c]; \ + addcc sum, t7, sum; \ + srlx t7, 32, t7; \ + bcc,pt %xcc, 48f; \ + stw t7, [dest + off + 0x38]; \ + add sum, 1, sum; \ +48: - /* 12 superscalar cycles seems to be the limit for this case, - * because of this we thus do all the ldd's together to get - * Viking MXCC into streaming mode. Ho hum... - */ -#define CSUMCOPY_BIGCHUNK(src, dst, sum, off, t0, t1, t2, t3, t4, t5, t6, t7) \ - ldda [src + off + 0x00] %asi, t0; \ - ldda [src + off + 0x08] %asi, t2; \ - ldda [src + off + 0x10] %asi, t4; \ - ldda [src + off + 0x18] %asi, t6; \ - st t0, [dst + off + 0x00]; \ - addccc t0, sum, sum; \ - st t1, [dst + off + 0x04]; \ - addccc t1, sum, sum; \ - st t2, [dst + off + 0x08]; \ - addccc t2, sum, sum; \ - st t3, [dst + off + 0x0c]; \ - addccc t3, sum, sum; \ - st t4, [dst + off + 0x10]; \ - addccc t4, sum, sum; \ - st t5, [dst + off + 0x14]; \ - addccc t5, sum, sum; \ - st t6, [dst + off + 0x18]; \ - addccc t6, sum, sum; \ - st t7, [dst + off + 0x1c]; \ - addccc t7, sum, sum; +#define CSUMCOPY_LASTCHUNK(src, dst, sum, off, t0, t1) \ + ldxa [src - off - 0x08] %asi, t0; \ + ldxa [src - off - 0x00] %asi, t1; \ + nop; nop; \ + addcc t0, sum, sum; \ + stw t0, [dst - off - 0x04]; \ + srlx t0, 32, t0; \ + bcc,pt %xcc, 51f; \ + stw t0, [dst - off - 0x08]; \ + add sum, 1, sum; \ +51: addcc t1, sum, sum; \ + stw t1, [dst - off + 0x04]; \ + srlx t1, 32, t1; \ + bcc,pt %xcc, 52f; \ + stw t1, [dst - off - 0x00]; \ + add sum, 1, sum; \ +52: - /* Yuck, 6 superscalar cycles... */ -#define CSUMCOPY_LASTCHUNK(src, dst, sum, off, t0, t1, t2, t3) \ - ldda [src - off - 0x08] %asi, t0; \ - ldda [src - off - 0x00] %asi, t2; \ - addccc t0, sum, sum; \ - st t0, [dst - off - 0x08]; \ - addccc t1, sum, sum; \ - st t1, [dst - off - 0x04]; \ - addccc t2, sum, sum; \ - st t2, [dst - off - 0x00]; \ - addccc t3, sum, sum; \ - st t3, [dst - off + 0x04]; - - /* Handle the end cruft code out of band for better cache patterns. */ cc_end_cruft: - andcc %o3, 8, %g0 ! begin checks for that code - be,pn %icc, 1f - and %o3, 4, %g5 - EX(ldda [%o0 + 0x00] %asi, %g2, and %o3, 0xf,#) - add %o1, 8, %o1 - addcc %g2, %g7, %g7 - add %o0, 8, %o0 - addccc %g3, %g7, %g7 - EX2(st %g2, [%o1 - 0x08],#) - addc %g0, %g7, %g7 - EX2(st %g3, [%o1 - 0x04],#) -1: brz,pt %g5, 1f - andcc %o3, 3, %o3 - EX(lda [%o0 + 0x00] %asi, %g2, add %o3, 4,#) - add %o1, 4, %o1 - addcc %g2, %g7, %g7 - EX2(st %g2, [%o1 - 0x04],#) - addc %g0, %g7, %g7 - add %o0, 4, %o0 -1: brz,pn %o3, 1f - addcc %o3, -1, %g0 - bne,pn %icc, 2f - subcc %o3, 2, %o3 - ba,pt %xcc, 4f - clr %o4 -2: EX(lduha [%o0 + 0x00] %asi, %o4, add %o3, 2,#) - add %o0, 2, %o0 - EX2(sth %o4, [%o1 + 0x00],#) - be,pn %icc, 6f - add %o1, 2, %o1 - sll %o4, 16, %o4 -4: EX(lduba [%o0 + 0x00] %asi, %o5, add %g0, 1,#) - EX2(stb %o5, [%o1 + 0x00],#) - sll %o5, 8, %o5 - or %o5, %o4, %o4 -6: addcc %o4, %g7, %g7 -1: sllx %g4, 32, %g4 - addc %g0, %g7, %o0 - retl - srl %o0, 0, %o0 + andcc %o3, 8, %g0 ! IEU1 Group + be,pn %icc, 1f ! CTI + and %o3, 4, %g5 ! IEU0 + ldxa [%o0 + 0x00] %asi, %g2 ! Load Group + add %o1, 8, %o1 ! IEU0 + add %o0, 8, %o0 ! IEU1 + addcc %g2, %g7, %g7 ! IEU1 Group + 2 bubbles + stw %g2, [%o1 - 0x04] ! Store + srlx %g2, 32, %g2 ! IEU0 + bcc,pt %xcc, 1f ! CTI Group + stw %g2, [%o1 - 0x08] ! Store + add %g7, 1, %g7 ! IEU0 +1: brz,pt %g5, 1f ! CTI Group + clr %g2 ! IEU0 + lduwa [%o0 + 0x00] %asi, %g2 ! Load + add %o1, 4, %o1 ! IEU0 Group + add %o0, 4, %o0 ! IEU1 + stw %g2, [%o1 - 0x04] ! Store Group + 2 bubbles + sllx %g2, 32, %g2 ! IEU0 +1: andcc %o3, 2, %g0 ! IEU1 + be,pn %icc, 1f ! CTI Group + clr %o4 ! IEU1 + lduha [%o0 + 0x00] %asi, %o4 ! Load + add %o0, 2, %o0 ! IEU0 Group + add %o1, 2, %o1 ! IEU1 + sth %o4, [%o1 - 0x2] ! Store Group + 2 bubbles + sll %o4, 16, %o4 ! IEU0 +1: andcc %o3, 1, %g0 ! IEU1 + be,pn %icc, 1f ! CTI Group + clr %o5 ! IEU0 + lduba [%o0 + 0x00] %asi, %o5 ! Load + stb %o5, [%o1 + 0x00] ! Store Group + 2 bubbles + sll %o5, 8, %o5 ! IEU0 +1: or %g2, %o4, %o4 ! IEU1 + or %o5, %o4, %o4 ! IEU0 Group + addcc %o4, %g7, %g7 ! IEU1 + bcc,pt %xcc, ccfold ! CTI + sethi %uhi(PAGE_OFFSET), %g4 ! IEU0 Group + b,pt %xcc, ccfold ! CTI + add %g7, 1, %g7 ! IEU1 - /* Sun, you just can't beat me, you just can't. Stop trying, - * give up. I'm serious, I am going to kick the living shit - * out of you, game over, lights out. - */ - .align 8 - .globl __csum_partial_copy_sparc_generic -__csum_partial_copy_sparc_generic: - /* %o0=src, %o1=dest, %g1=len, %g7=sum */ - srl %g7, 0, %g7 ! you neve know... - xor %o0, %o1, %o4 ! get changing bits - srl %g1, 0, %g1 ! doof scheiss - andcc %o4, 3, %g0 ! check for mismatched alignment - bne,pn %icc, ccslow ! better this than unaligned/fixups - andcc %o0, 7, %g0 ! need to align things? - be,pt %icc, cc_dword_aligned ! yes, we check for short lengths there - andn %g1, 0x7f, %g2 ! can we use unrolled loop? - cmp %g1, 6 - bl,a,pn %icc, ccte - andcc %g1, 0xf, %o3 - andcc %o0, 0x1, %g0 - bne,pn %icc, ccslow - andcc %o0, 0x2, %g0 - be,pn %icc, 1f - andcc %o0, 0x4, %g0 - EX(lduha [%o0 + 0x00] %asi, %g4, add %g1, 0,#) - sub %g1, 2, %g1 - EX2(sth %g4, [%o1 + 0x00],#) - add %o0, 2, %o0 - sll %g4, 16, %g4 - addcc %g4, %g7, %g7 - add %o1, 2, %o1 - srl %g7, 16, %g3 - addc %g0, %g3, %g4 - sll %g7, 16, %g7 - sll %g4, 16, %g3 - srl %g7, 16, %g7 - andcc %o0, 0x4, %g0 - or %g3, %g7, %g7 -1: be,pt %icc, 3f - andn %g1, 0x7f, %g2 - EX(lda [%o0 + 0x00] %asi, %g4, add %g1, 0,#) - sub %g1, 4, %g1 - EX2(st %g4, [%o1 + 0x00],#) - add %o0, 4, %o0 - addcc %g4, %g7, %g7 - add %o1, 4, %o1 - andn %g1, 0x7f, %g2 - addc %g0, %g7, %g7 +cc_fixit: + bl,a,pn %icc, ccte ! CTI + andcc %g1, 0xf, %o3 ! IEU1 Group + andcc %o0, 1, %g0 ! IEU1 Group + bne,pn %icc, ccslow ! CTI + andcc %o0, 2, %g0 ! IEU1 Group + be,pn %icc, 1f ! CTI + andcc %o0, 0x4, %g0 ! IEU1 Group + lduha [%o0 + 0x00] %asi, %g4 ! Load + sub %g1, 2, %g1 ! IEU0 + add %o0, 2, %o0 ! IEU0 Group + add %o1, 2, %o1 ! IEU1 + sll %g4, 16, %g3 ! IEU0 Group + 1 bubble + addcc %g3, %g7, %g7 ! IEU1 + bcc,pt %xcc, 0f ! CTI + srl %g7, 16, %g3 ! IEU0 Group + add %g3, 1, %g3 ! IEU0 4 clocks (mispredict) +0: andcc %o0, 0x4, %g0 ! IEU1 Group + sth %g4, [%o1 - 0x2] ! Store + sll %g7, 16, %g7 ! IEU0 + sll %g3, 16, %g3 ! IEU0 Group + srl %g7, 16, %g7 ! IEU0 Group + or %g3, %g7, %g7 ! IEU0 Group (regdep) +1: be,pt %icc, cc_dword_aligned ! CTI + andn %g1, 0xff, %g2 ! IEU1 + lduwa [%o0 + 0x00] %asi, %g4 ! Load Group + sub %g1, 4, %g1 ! IEU0 + add %o0, 4, %o0 ! IEU1 + add %o1, 4, %o1 ! IEU0 Group + addcc %g4, %g7, %g7 ! IEU1 Group + 1 bubble + stw %g4, [%o1 - 0x4] ! Store + bcc,pt %xcc, cc_dword_aligned ! CTI + andn %g1, 0xff, %g2 ! IEU0 Group + b,pt %xcc, cc_dword_aligned ! CTI 4 clocks (mispredict) + add %g7, 1, %g7 ! IEU0 + + .align 32 + .globl __csum_partial_copy_sparc_generic, csum_partial_copy +csum_partial_copy: +__csum_partial_copy_sparc_generic: /* %o0=src, %o1=dest, %g1=len, %g7=sum */ + xorcc %o0, %o1, %o4 ! IEU1 Group + srl %g7, 0, %g7 ! IEU0 + andcc %o4, 3, %g0 ! IEU1 Group + srl %g1, 0, %g1 ! IEU0 + bne,pn %icc, ccslow ! CTI + andcc %o0, 7, %g0 ! IEU1 Group + be,pt %icc, cc_dword_aligned ! CTI + andn %g1, 0xff, %g2 ! IEU0 + b,pt %xcc, cc_fixit ! CTI Group + cmp %g1, 6 ! IEU1 cc_dword_aligned: -3: brz,pn %g2, 3f ! nope, less than one loop remains - andcc %o1, 4, %g0 ! dest aligned on 4 or 8 byte boundry? - be,pn %icc, ccdbl + 4 ! 8 byte aligned, kick ass -5: CSUMCOPY_BIGCHUNK(%o0,%o1,%g7,0x00,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) - CSUMCOPY_BIGCHUNK(%o0,%o1,%g7,0x20,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) - CSUMCOPY_BIGCHUNK(%o0,%o1,%g7,0x40,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) - CSUMCOPY_BIGCHUNK(%o0,%o1,%g7,0x60,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) -10: EXT(5b, 10b, 20f,#) ! note for exception handling - sub %g1, 128, %g1 ! detract from length - addc %g0, %g7, %g7 ! add in last carry bit - andncc %g1, 0x7f, %g0 ! more to csum? - add %o0, 128, %o0 ! advance src ptr - bne,pt %icc, 5b ! we did not go negative, continue looping - add %o1, 128, %o1 ! advance dest ptr -3: andcc %g1, 0x70, %o2 ! can use table? -ccmerge:be,pn %icc, ccte ! nope, go and check for end cruft - andcc %g1, 0xf, %o3 ! get low bits of length (clears carry btw) - srl %o2, 1, %o4 ! begin negative offset computation -13: rd %pc, %o5 ! set up table ptr end - add %o0, %o2, %o0 ! advance src ptr - sub %o5, %o4, %o5 ! continue table calculation - sll %o2, 1, %g2 ! constant multiplies are fun... - sub %o5, %g2, %o5 ! some more adjustments - jmpl %o5 + (12f-13b), %g0 ! jump into it, duff style, wheee... - add %o1, %o2, %o1 ! advance dest ptr (carry is clear btw) -cctbl: CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x68,%g2,%g3,%g4,%g5) - CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x58,%g2,%g3,%g4,%g5) - CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x48,%g2,%g3,%g4,%g5) - CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x38,%g2,%g3,%g4,%g5) - CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x28,%g2,%g3,%g4,%g5) - CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x18,%g2,%g3,%g4,%g5) - CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x08,%g2,%g3,%g4,%g5) -12: EXT(cctbl, 12b, 22f,#) ! note for exception table handling - addc %g0, %g7, %g7 - andcc %g1, 0xf, %o3 ! check for low bits set -ccte: bne,pn %icc, cc_end_cruft ! something left, handle it out of band - sethi %uhi(KERNBASE), %g4 ! restore gfp - mov %g7, %o0 ! give em the computed checksum - sllx %g4, 32, %g4 ! finish gfp restoration - retl ! return - srl %o0, 0, %o0 -ccdbl: CSUMCOPY_BIGCHUNK_ALIGNED(%o0,%o1,%g7,0x00,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) - CSUMCOPY_BIGCHUNK_ALIGNED(%o0,%o1,%g7,0x20,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) - CSUMCOPY_BIGCHUNK_ALIGNED(%o0,%o1,%g7,0x40,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) - CSUMCOPY_BIGCHUNK_ALIGNED(%o0,%o1,%g7,0x60,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) -11: EXT(ccdbl, 11b, 21f,#) ! note for exception table handling - sub %g1, 128, %g1 ! detract from length - addc %g0, %g7, %g7 ! add in last carry bit - andncc %g1, 0x7f, %g0 ! more to csum? - add %o0, 128, %o0 ! advance src ptr - bne,pt %icc, ccdbl ! we did not go negative, continue looping - add %o1, 128, %o1 ! advance dest ptr - ba,pt %xcc, ccmerge ! finish it off, above - andcc %g1, 0x70, %o2 ! can use table? (clears carry btw) + brz,pn %g2, 3f ! CTI Group + andcc %o1, 4, %g0 ! IEU1 Group (brz uses IEU1) + be,pn %icc, ccdbl + 4 ! CTI +5: CSUMCOPY_ECACHE_LOAD( %o0, 0x00, %o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) + CSUMCOPY_EC_STUNALIGN_LDNXT(%o0,%o1,0x40,%g7,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) + CSUMCOPY_EC_STUNALIGN_LDNXT(%o0,%o1,0x80,%g7,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) + CSUMCOPY_EC_STUNALIGN_LDNXT(%o0,%o1,0xc0,%g7,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) + CSUMCOPY_EC_STUNALIGN( %o1,0xc0,%g7,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) +10: + sub %g1, 256, %g1 ! IEU0 Group + add %o0, 256, %o0 ! IEU1 + andncc %g1, 0xff, %g0 ! IEU1 Group + bne,pt %icc, 5b ! CTI + add %o1, 256, %o1 ! IEU0 +3: andcc %g1, 0xf0, %o2 ! IEU1 Group +ccmerge:be,pn %icc, ccte ! CTI + andcc %g1, 0xf, %o3 ! IEU1 Group + sll %o2, 2, %o4 ! IEU0 +13: rd %pc, %o5 ! LSU Group + 4 clocks + add %o0, %o2, %o0 ! IEU0 Group + sub %o5, %o4, %o5 ! IEU1 Group + jmpl %o5 + (12f - 13b), %g0 ! CTI Group brk forced + add %o1, %o2, %o1 ! IEU0 Group +cctbl: CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0xe8,%g2,%g3) + CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0xd8,%g2,%g3) + CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0xc8,%g2,%g3) + CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0xb8,%g2,%g3) + CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0xa8,%g2,%g3) + CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x98,%g2,%g3) + CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x88,%g2,%g3) + CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x78,%g2,%g3) + CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x68,%g2,%g3) + CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x58,%g2,%g3) + CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x48,%g2,%g3) + CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x38,%g2,%g3) + CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x28,%g2,%g3) + CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x18,%g2,%g3) + CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x08,%g2,%g3) +12: + andcc %g1, 0xf, %o3 ! IEU1 Group +ccte: bne,pn %icc, cc_end_cruft ! CTI + sethi %uhi(PAGE_OFFSET), %g4 ! IEU0 +ccfold: sllx %g7, 32, %o0 ! IEU0 Group + addcc %g7, %o0, %o0 ! IEU1 Group (regdep) + srlx %o0, 32, %o0 ! IEU0 Group (regdep) + bcs,a,pn %xcc, 1f ! CTI + add %o0, 1, %o0 ! IEU1 4 clocks (mispredict) +1: retl ! CTI Group brk forced + sllx %g4, 32,%g4 ! IEU0 Group +ccdbl: CSUMCOPY_ECACHE_LOAD( %o0, 0x00, %o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) + CSUMCOPY_EC_STALIGNED_LDNXT(%o0,%o1,0x40,%g7,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) + CSUMCOPY_EC_STALIGNED_LDNXT(%o0,%o1,0x80,%g7,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) + CSUMCOPY_EC_STALIGNED_LDNXT(%o0,%o1,0xc0,%g7,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) + CSUMCOPY_EC_STALIGNED( %o1,0xc0,%g7,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) +11: + sub %g1, 256, %g1 ! IEU0 Group + add %o0, 256, %o0 ! IEU1 + andncc %g1, 0xff, %g0 ! IEU1 Group + bne,pt %icc, ccdbl ! CTI + add %o1, 256, %o1 ! IEU0 + b,pt %xcc, ccmerge ! CTI Group + andcc %g1, 0xf0, %o2 ! IEU1 ccslow: mov 0, %g5 brlez,pn %g1, 4f @@ -401,9 +627,9 @@ ccslow: mov 0, %g5 be,a,pt %icc, 1f srl %g1, 1, %o3 sub %g1, 1, %g1 - EX(lduba [%o0] %asi, %g5, add %g1, 1,#) + lduba [%o0] %asi, %g5 add %o0, 1, %o0 - EX2(stb %g5, [%o1],#) + stb %g5, [%o1] srl %g1, 1, %o3 add %o1, 1, %o1 1: brz,a,pn %o3, 3f @@ -411,33 +637,33 @@ ccslow: mov 0, %g5 andcc %o0, 2, %g0 be,a,pt %icc, 1f srl %o3, 1, %o3 - EX(lduha [%o0] %asi, %o4, add %g1, 0,#) + lduha [%o0] %asi, %o4 sub %g1, 2, %g1 srl %o4, 8, %g2 sub %o3, 1, %o3 - EX2(stb %g2, [%o1],#) + stb %g2, [%o1] add %o4, %g5, %g5 - EX2(stb %o4, [%o1 + 1],#) + stb %o4, [%o1 + 1] add %o0, 2, %o0 srl %o3, 1, %o3 add %o1, 2, %o1 1: brz,a,pn %o3, 2f andcc %g1, 2, %g0 - EX3(lda [%o0] %asi, %o4,#) + lda [%o0] %asi, %o4 5: srl %o4, 24, %g2 srl %o4, 16, %g3 - EX2(stb %g2, [%o1],#) + stb %g2, [%o1] srl %o4, 8, %g2 - EX2(stb %g3, [%o1 + 1],#) + stb %g3, [%o1 + 1] add %o0, 4, %o0 - EX2(stb %g2, [%o1 + 2],#) + stb %g2, [%o1 + 2] addcc %o4, %g5, %g5 - EX2(stb %o4, [%o1 + 3],#) + stb %o4, [%o1 + 3] addc %g5, %g0, %g5 ! I am now to lazy to optimize this (question is if it add %o1, 4, %o1 ! is worthy). Maybe some day - with the sll/srl subcc %o3, 1, %o3 ! tricks bne,a,pt %icc, 5b - EX3(lda [%o0] %asi, %o4,#) + lda [%o0] %asi, %o4 sll %g5, 16, %g2 srl %g5, 16, %g5 srl %g2, 16, %g2 @@ -445,19 +671,19 @@ ccslow: mov 0, %g5 add %g2, %g5, %g5 2: be,a,pt %icc, 3f andcc %g1, 1, %g0 - EX(lduha [%o0] %asi, %o4, and %g1, 3,#) + lduha [%o0] %asi, %o4 andcc %g1, 1, %g0 srl %o4, 8, %g2 add %o0, 2, %o0 - EX2(stb %g2, [%o1],#) + stb %g2, [%o1] add %g5, %o4, %g5 - EX2(stb %o4, [%o1 + 1],#) + stb %o4, [%o1 + 1] add %o1, 2, %o1 3: be,a,pt %icc, 1f sll %g5, 16, %o4 - EX(lduba [%o0] %asi, %g2, add %g0, 1,#) + lduba [%o0] %asi, %g2 sll %g2, 8, %o4 - EX2(stb %g2, [%o1],#) + stb %g2, [%o1] add %g5, %o4, %g5 sll %g5, 16, %o4 1: addcc %o4, %g5, %g5 @@ -474,103 +700,3 @@ ccslow: mov 0, %g5 retl srl %o0, 0, %o0 __csum_partial_copy_end: - - .section .fixup,#alloc,#execinstr - .align 4 -/* We do these strange calculations for the csum_*_from_user case only, ie. - * we only bother with faults on loads... */ - -/* o2 = ((g2%20)&3)*8 - * o3 = g1 - (g2/20)*32 - o2 */ -20: - cmp %g2, 20 - blu,a,pn %icc, 1f - and %g2, 3, %o2 - sub %g1, 32, %g1 - ba,pt %xcc, 20b - sub %g2, 20, %g2 -1: - sll %o2, 3, %o2 - ba,pt %xcc, 31f - sub %g1, %o2, %o3 - -/* o2 = (!(g2 & 15) ? 0 : (((g2 & 15) + 1) & ~1)*8) - * o3 = g1 - (g2/16)*32 - o2 */ -21: - andcc %g2, 15, %o3 - srl %g2, 4, %g2 - be,a,pn %icc, 1f - clr %o2 - add %o3, 1, %o3 - and %o3, 14, %o3 - sll %o3, 3, %o2 -1: - sll %g2, 5, %g2 - sub %g1, %g2, %o3 - ba,pt %xcc, 31f - sub %o3, %o2, %o3 - -/* o0 += (g2/10)*16 - 0x70 - * 01 += (g2/10)*16 - 0x70 - * o2 = (g2 % 10) ? 8 : 0 - * o3 += 0x70 - (g2/10)*16 - o2 */ -22: - cmp %g2, 10 - blu,a,pt %xcc, 1f - sub %o0, 0x70, %o0 - add %o0, 16, %o0 - add %o1, 16, %o1 - sub %o3, 16, %o3 - ba,pt %xcc, 22b - sub %g2, 10, %g2 -1: - sub %o1, 0x70, %o1 - add %o3, 0x70, %o3 - clr %o2 - movrnz %g2, 8, %o2 - ba,pt %xcc, 31f - sub %o3, %o2, %o3 -96: - and %g1, 3, %g1 - sll %o3, 2, %o3 - add %g1, %o3, %o3 -30: -/* %o1 is dst - * %o3 is # bytes to zero out - * %o4 is faulting address - * %o5 is %pc where fault occured */ - clr %o2 -31: -/* %o0 is src - * %o1 is dst - * %o2 is # of bytes to copy from src to dst - * %o3 is # bytes to zero out - * %o4 is faulting address - * %o5 is %pc where fault occured */ - save %sp, -136, %sp - mov %i5, %o0 - mov %i7, %o1 - mov %i4, %o2 - call lookup_fault - mov %g7, %i4 - cmp %o0, 2 - bne,pn %icc, 1f - add %g0, -EFAULT, %i5 - brz,pn %i2, 2f - mov %i0, %o1 - mov %i1, %o0 - call __copy_from_user - mov %i2, %o2 - brnz,a,pn %o0, 2f - add %i3, %i2, %i3 - add %i1, %i2, %i1 -2: - mov %i1, %o0 - wr %g0, ASI_S, %asi - call __bzero_noasi - mov %i3, %o1 -1: - ldx [%sp + STACK_BIAS + 264], %o2 ! struct_ptr of parent - st %i5, [%o2] - ret - restore diff --git a/arch/sparc64/lib/copy_from_user.S b/arch/sparc64/lib/copy_from_user.S deleted file mode 100644 index 196435aedfa7..000000000000 --- a/arch/sparc64/lib/copy_from_user.S +++ /dev/null @@ -1,469 +0,0 @@ -/* copy_user.S: Sparc optimized copy_from_user code. - * - * Copyright(C) 1995 Linus Torvalds - * Copyright(C) 1996 David S. Miller - * Copyright(C) 1996 Eddie C. Dost - * Copyright(C) 1996,1997 Jakub Jelinek - * - * derived from: - * e-mail between David and Eddie. - * - * Returns 0 if successful, otherwise count of bytes not copied yet - * - * FIXME: This code should be optimized for sparc64... -jj - */ - -#include -#include -#include - -#define PRE_RETL sethi %uhi(KERNBASE), %g4; sllx %g4, 32, %g4; - -#define EX(x,y,a,b,z) \ -98: x,y; \ - .section .fixup,z##alloc,z##execinstr; \ - .align 4; \ -99: PRE_RETL \ - retl; \ - a, b, %o0; \ - .section __ex_table,z##alloc; \ - .align 8; \ - .xword 98b, 99b; \ - .text; \ - .align 4 - -#define EX2(x,y,c,d,e,a,b,z) \ -98: x,y; \ - .section .fixup,z##alloc,z##execinstr; \ - .align 4; \ -99: c, d, e; \ - PRE_RETL \ - retl; \ - a, b, %o0; \ - .section __ex_table,z##alloc; \ - .align 8; \ - .xword 98b, 99b; \ - .text; \ - .align 4 - -#define EXO2(x,y,z) \ -98: x,##y; \ - .section __ex_table,z##alloc; \ - .align 8; \ - .xword 98b, 97f; \ - .text; \ - .align 4 - -#define EXT(start,end,handler,z) \ - .section __ex_table,z##alloc; \ - .align 8; \ - .xword start, 0, end, handler; \ - .text; \ - .align 4 - -/* Please do not change following macros unless you change logic used - * in .fixup at the end of this file as well - */ - -/* Both these macros have to start with exactly the same insn */ -#define MOVE_BIGCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \ - ldda [%src + offset + 0x00] %asi, %t0; \ - ldda [%src + offset + 0x08] %asi, %t2; \ - ldda [%src + offset + 0x10] %asi, %t4; \ - ldda [%src + offset + 0x18] %asi, %t6; \ - st %t0, [%dst + offset + 0x00]; \ - st %t1, [%dst + offset + 0x04]; \ - st %t2, [%dst + offset + 0x08]; \ - st %t3, [%dst + offset + 0x0c]; \ - st %t4, [%dst + offset + 0x10]; \ - st %t5, [%dst + offset + 0x14]; \ - st %t6, [%dst + offset + 0x18]; \ - st %t7, [%dst + offset + 0x1c]; - -#define MOVE_BIGALIGNCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \ - ldda [%src + offset + 0x00] %asi, %t0; \ - ldda [%src + offset + 0x08] %asi, %t2; \ - ldda [%src + offset + 0x10] %asi, %t4; \ - ldda [%src + offset + 0x18] %asi, %t6; \ - std %t0, [%dst + offset + 0x00]; \ - std %t2, [%dst + offset + 0x08]; \ - std %t4, [%dst + offset + 0x10]; \ - std %t6, [%dst + offset + 0x18]; - -#define MOVE_LASTCHUNK(src, dst, offset, t0, t1, t2, t3) \ - ldda [%src - offset - 0x10] %asi, %t0; \ - ldda [%src - offset - 0x08] %asi, %t2; \ - st %t0, [%dst - offset - 0x10]; \ - st %t1, [%dst - offset - 0x0c]; \ - st %t2, [%dst - offset - 0x08]; \ - st %t3, [%dst - offset - 0x04]; - -#define MOVE_HALFCHUNK(src, dst, offset, t0, t1, t2, t3) \ - lduha [%src + offset + 0x00] %asi, %t0; \ - lduha [%src + offset + 0x02] %asi, %t1; \ - lduha [%src + offset + 0x04] %asi, %t2; \ - lduha [%src + offset + 0x06] %asi, %t3; \ - sth %t0, [%dst + offset + 0x00]; \ - sth %t1, [%dst + offset + 0x02]; \ - sth %t2, [%dst + offset + 0x04]; \ - sth %t3, [%dst + offset + 0x06]; - -#define MOVE_SHORTCHUNK(src, dst, offset, t0, t1) \ - lduba [%src - offset - 0x02] %asi, %t0; \ - lduba [%src - offset - 0x01] %asi, %t1; \ - stb %t0, [%dst - offset - 0x02]; \ - stb %t1, [%dst - offset - 0x01]; - - .text - .align 4 - - .globl __copy_from_user -dword_align: - andcc %o1, 1, %g0 - be 4f - andcc %o1, 2, %g0 - - EXO2(lduba [%o1] %asi, %g2,#) - add %o1, 1, %o1 - stb %g2, [%o0] - sub %o2, 1, %o2 - bne 3f - add %o0, 1, %o0 - - EXO2(lduha [%o1] %asi, %g2,#) - add %o1, 2, %o1 - sth %g2, [%o0] - sub %o2, 2, %o2 - ba,pt %xcc, 3f - add %o0, 2, %o0 -4: - EXO2(lduha [%o1] %asi, %g2,#) - add %o1, 2, %o1 - sth %g2, [%o0] - sub %o2, 2, %o2 - ba,pt %xcc, 3f - add %o0, 2, %o0 - -__copy_from_user: /* %o0=dst %o1=src %o2=len */ - wr %g0, ASI_S, %asi - xor %o0, %o1, %o4 -1: - andcc %o4, 3, %o5 -2: - bne,pn %icc, cannot_optimize - cmp %o2, 15 - - bleu,pn %xcc, short_aligned_end - andcc %o1, 3, %g0 - - bne,pn %icc, dword_align -3: - andcc %o1, 4, %g0 - - be,pt %icc, 2f - mov %o2, %g1 - - EXO2(lda [%o1] %asi, %o4,#) - sub %g1, 4, %g1 - st %o4, [%o0] - add %o1, 4, %o1 - add %o0, 4, %o0 -2: - andcc %g1, 0xffffffffffffff80, %g7 - be,pn %xcc, 3f - andcc %o0, 4, %g0 - - be,pn %icc, ldd_std + 4 -5: - MOVE_BIGCHUNK(o1, o0, 0x00, o2, o3, o4, o5, g2, g3, g4, g5) - MOVE_BIGCHUNK(o1, o0, 0x20, o2, o3, o4, o5, g2, g3, g4, g5) - MOVE_BIGCHUNK(o1, o0, 0x40, o2, o3, o4, o5, g2, g3, g4, g5) - MOVE_BIGCHUNK(o1, o0, 0x60, o2, o3, o4, o5, g2, g3, g4, g5) -80: - EXT(5b, 80b, 50f,#) - subcc %g7, 128, %g7 - add %o1, 128, %o1 - bne,pt %xcc, 5b - add %o0, 128, %o0 -3: - andcc %g1, 0x70, %g7 - be,pn %icc, copy_user_table_end - andcc %g1, 8, %g0 -100: - rd %pc, %o5 - srl %g7, 1, %o4 - add %g7, %o4, %o4 - add %o1, %g7, %o1 - sub %o5, %o4, %o5 - jmpl %o5 + (copy_user_table_end - 100b), %g0 - add %o0, %g7, %o0 - -copy_user_table: - MOVE_LASTCHUNK(o1, o0, 0x60, g2, g3, g4, g5) - MOVE_LASTCHUNK(o1, o0, 0x50, g2, g3, g4, g5) - MOVE_LASTCHUNK(o1, o0, 0x40, g2, g3, g4, g5) - MOVE_LASTCHUNK(o1, o0, 0x30, g2, g3, g4, g5) - MOVE_LASTCHUNK(o1, o0, 0x20, g2, g3, g4, g5) - MOVE_LASTCHUNK(o1, o0, 0x10, g2, g3, g4, g5) - MOVE_LASTCHUNK(o1, o0, 0x00, g2, g3, g4, g5) -copy_user_table_end: - EXT(copy_user_table, copy_user_table_end, 51f,#) - be,pt %icc, copy_user_last7 - andcc %g1, 4, %g0 - - EX(ldda [%o1] %asi, %g2, and %g1, 0xf,#) - add %o0, 8, %o0 - add %o1, 8, %o1 - st %g2, [%o0 - 0x08] - st %g3, [%o0 - 0x04] -copy_user_last7: - be,pn %icc, 1f - andcc %g1, 2, %g0 - - EX(lda [%o1] %asi, %g2, and %g1, 7,#) - add %o1, 4, %o1 - st %g2, [%o0] - add %o0, 4, %o0 -1: - be,pn %icc, 1f - andcc %g1, 1, %g0 - - EX(lduha [%o1] %asi, %g2, and %g1, 3,#) - add %o1, 2, %o1 - sth %g2, [%o0] - add %o0, 2, %o0 -1: - be,pn %icc, 1f - nop - - EX(lduba [%o1] %asi, %g2, add %g0, 1,#) - stb %g2, [%o0] -1: - PRE_RETL - retl - clr %o0 - -ldd_std: - MOVE_BIGALIGNCHUNK(o1, o0, 0x00, o2, o3, o4, o5, g2, g3, g4, g5) - MOVE_BIGALIGNCHUNK(o1, o0, 0x20, o2, o3, o4, o5, g2, g3, g4, g5) - MOVE_BIGALIGNCHUNK(o1, o0, 0x40, o2, o3, o4, o5, g2, g3, g4, g5) - MOVE_BIGALIGNCHUNK(o1, o0, 0x60, o2, o3, o4, o5, g2, g3, g4, g5) -81: - EXT(ldd_std, 81b, 52f,#) - subcc %g7, 128, %g7 - add %o1, 128, %o1 - bne,pt %xcc, ldd_std - add %o0, 128, %o0 - - andcc %g1, 0x70, %g7 - be,pn %icc, copy_user_table_end - andcc %g1, 8, %g0 -101: - rd %pc, %o5 - srl %g7, 1, %o4 - add %g7, %o4, %o4 - add %o1, %g7, %o1 - sub %o5, %o4, %o5 - jmpl %o5 + (copy_user_table_end - 101b), %g0 - add %o0, %g7, %o0 - -cannot_optimize: - bleu short_end - cmp %o5, 2 - - bne byte_chunk - and %o2, 0xfffffffffffffff0, %o3 - - andcc %o1, 1, %g0 - be 10f - nop - - EXO2(lduba [%o1] %asi, %g2,#) - add %o1, 1, %o1 - stb %g2, [%o0] - sub %o2, 1, %o2 - andcc %o2, 0xfffffffffffffff0, %o3 - be short_end - add %o0, 1, %o0 -10: - MOVE_HALFCHUNK(o1, o0, 0x00, g2, g3, g4, g5) - MOVE_HALFCHUNK(o1, o0, 0x08, g2, g3, g4, g5) -82: - EXT(10b, 82b, 53f,#) - subcc %o3, 0x10, %o3 - add %o1, 0x10, %o1 - bne 10b - add %o0, 0x10, %o0 - ba,pt %xcc, 2f - and %o2, 0xe, %o3 - -byte_chunk: - MOVE_SHORTCHUNK(o1, o0, -0x02, g2, g3) - MOVE_SHORTCHUNK(o1, o0, -0x04, g2, g3) - MOVE_SHORTCHUNK(o1, o0, -0x06, g2, g3) - MOVE_SHORTCHUNK(o1, o0, -0x08, g2, g3) - MOVE_SHORTCHUNK(o1, o0, -0x0a, g2, g3) - MOVE_SHORTCHUNK(o1, o0, -0x0c, g2, g3) - MOVE_SHORTCHUNK(o1, o0, -0x0e, g2, g3) - MOVE_SHORTCHUNK(o1, o0, -0x10, g2, g3) -83: - EXT(byte_chunk, 83b, 54f,#) - subcc %o3, 0x10, %o3 - add %o1, 0x10, %o1 - bne,pt %xcc, byte_chunk - add %o0, 0x10, %o0 - -short_end: - and %o2, 0xe, %o3 -2: - rd %pc, %o5 - sll %o3, 3, %o4 - add %o0, %o3, %o0 - sub %o5, %o4, %o5 - add %o1, %o3, %o1 - jmpl %o5 + (short_table_end - 2b), %g0 - andcc %o2, 1, %g0 -84: - MOVE_SHORTCHUNK(o1, o0, 0x0c, g2, g3) - MOVE_SHORTCHUNK(o1, o0, 0x0a, g2, g3) - MOVE_SHORTCHUNK(o1, o0, 0x08, g2, g3) - MOVE_SHORTCHUNK(o1, o0, 0x06, g2, g3) - MOVE_SHORTCHUNK(o1, o0, 0x04, g2, g3) - MOVE_SHORTCHUNK(o1, o0, 0x02, g2, g3) - MOVE_SHORTCHUNK(o1, o0, 0x00, g2, g3) -short_table_end: - EXT(84b, short_table_end, 55f,#) - be 1f - nop - EX(lduba [%o1] %asi, %g2, add %g0, 1,#) - stb %g2, [%o0] -1: - PRE_RETL - retl - clr %o0 - -short_aligned_end: - bne short_end - andcc %o2, 8, %g0 - - be 1f - andcc %o2, 4, %g0 - - EXO2(lda [%o1 + 0x00] %asi, %g2,#) - EX(lda [%o1 + 0x04] %asi, %g3, sub %o2, 4,#) - add %o1, 8, %o1 - st %g2, [%o0 + 0x00] - st %g3, [%o0 + 0x04] - add %o0, 8, %o0 -1: - ba,pt %xcc, copy_user_last7 - mov %o2, %g1 - - .section .fixup,#alloc,#execinstr - .align 4 -97: - PRE_RETL - retl - mov %o2, %o0 -/* exception routine sets %g2 to (broken_insn - first_insn)>>2 */ -50: -/* This magic counts how many bytes are left when crash in MOVE_BIGCHUNK - * happens. This is derived from the amount ldd reads, st stores, etc. - * x = g2 % 12; - * o0 = g1 + g7 - ((g2 / 12) * 32 + (x < 4) ? x * 8 : (x - 4) * 4) - */ - cmp %g2, 12 - bcs 1f - cmp %g2, 24 - bcs 2f - cmp %g2, 36 - bcs 3f - nop - sub %g2, 12, %g2 - sub %g7, 32, %g7 -3: - sub %g2, 12, %g2 - sub %g7, 32, %g7 -2: - sub %g2, 12, %g2 - sub %g7, 32, %g7 -1: - cmp %g2, 4 - bcs,a 1f - sll %g2, 3, %g2 - sub %g2, 4, %g2 - sll %g2, 2, %g2 -1: - and %g1, 0x7f, %o0 - add %o0, %g7, %o0 - PRE_RETL - retl - sub %o0, %g2, %o0 -51: -/* i = 41 - g2; j = i % 6; - * o0 = (g1 & 15) + (i / 6) * 16 + (j < 4) ? (j + 1) * 4 : (j - 3) * 8; - */ - neg %g2 - and %g1, 0xf, %g1 - add %g2, 41, %g2 -1: - cmp %g2, 6 - bcs,a 2f - cmp %g2, 4 - add %g1, 16, %g1 - b 1b - sub %g2, 6, %g2 -2: - bcs,a 3f - inc %g2 - sub %g2, 3, %g2 - b 2f - sll %g2, 3, %g2 -3: - sll %g2, 2, %g2 -2: - PRE_RETL - retl - add %g1, %g2, %o0 -52: -/* o0 = g1 + g7 - (g2 / 8) * 32 + (x & 3) * 8 */ - and %g2, 0xfffffffffffffff8, %g4 - and %g2, 3, %g2 - sll %g4, 2, %g4 - sll %g2, 3, %g2 - add %g2, %g4, %g2 - b,a 1b -53: -/* o0 = o3 + (o2 & 15) - (g2 & 8) - (g2 & 3) * 2 */ - and %g2, 3, %g4 - and %g2, 0xfffffffffffffff8, %g2 - sll %g4, 1, %g4 - add %g2, %g4, %g2 - and %o2, 0xf, %o0 - add %o0, %o3, %o0 - PRE_RETL - retl - sub %o0, %g2, %o0 -54: -/* o0 = o3 + (o2 & 15) - (g2 / 4) * 2 - (g2 & 1) */ - srl %g2, 2, %o4 - and %g2, 1, %o1 - sll %o4, 1, %o4 - and %o2, 0xf, %o2 - sub %o3, %o1, %o3 - sub %o2, %o4, %o2 - PRE_RETL - retl - add %o2, %o3, %o0 -55: -/* o0 = (o2 & 1) + (27 - g2)/4 * 2 + ((27 - g2) & 1) */ - neg %g2 - and %o2, 1, %o2 - add %g2, 27, %g2 - srl %g2, 2, %o1 - and %g2, 1, %g2 - sll %o1, 1, %o1 - add %o2, %g2, %o0 - PRE_RETL - retl - add %o0, %o1, %o0 diff --git a/arch/sparc64/lib/copy_to_user.S b/arch/sparc64/lib/copy_to_user.S deleted file mode 100644 index cc6db141f4f3..000000000000 --- a/arch/sparc64/lib/copy_to_user.S +++ /dev/null @@ -1,469 +0,0 @@ -/* copy_user.S: Sparc optimized copy_to_user code. - * - * Copyright(C) 1995 Linus Torvalds - * Copyright(C) 1996 David S. Miller - * Copyright(C) 1996 Eddie C. Dost - * Copyright(C) 1996,1997 Jakub Jelinek - * - * derived from: - * e-mail between David and Eddie. - * - * Returns 0 if successful, otherwise count of bytes not copied yet - * - * FIXME: This code should be optimized for sparc64... -jj - */ - -#include -#include -#include - -#define PRE_RETL sethi %uhi(KERNBASE), %g4; sllx %g4, 32, %g4; - -#define EX(x,y,a,b,z) \ -98: x,y; \ - .section .fixup,z##alloc,z##execinstr; \ - .align 4; \ -99: PRE_RETL \ - retl; \ - a, b, %o0; \ - .section __ex_table,z##alloc; \ - .align 8; \ - .xword 98b, 99b; \ - .text; \ - .align 4 - -#define EX2(x,y,c,d,e,a,b,z) \ -98: x,y; \ - .section .fixup,z##alloc,z##execinstr; \ - .align 4; \ -99: c, d, e; \ - PRE_RETL \ - retl; \ - a, b, %o0; \ - .section __ex_table,z##alloc; \ - .align 8; \ - .xword 98b, 99b; \ - .text; \ - .align 4 - -#define EXO2(x,y,z) \ -98: x,##y; \ - .section __ex_table,z##alloc; \ - .align 8; \ - .xword 98b, 97f; \ - .text; \ - .align 4 - -#define EXT(start,end,handler,z) \ - .section __ex_table,z##alloc; \ - .align 8; \ - .xword start, 0, end, handler; \ - .text; \ - .align 4 - -/* Please do not change following macros unless you change logic used - * in .fixup at the end of this file as well - */ - -/* Both these macros have to start with exactly the same insn */ -#define MOVE_BIGCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \ - ldd [%src + offset + 0x00], %t0; \ - ldd [%src + offset + 0x08], %t2; \ - ldd [%src + offset + 0x10], %t4; \ - ldd [%src + offset + 0x18], %t6; \ - sta %t0, [%dst + offset + 0x00] %asi; \ - sta %t1, [%dst + offset + 0x04] %asi; \ - sta %t2, [%dst + offset + 0x08] %asi; \ - sta %t3, [%dst + offset + 0x0c] %asi; \ - sta %t4, [%dst + offset + 0x10] %asi; \ - sta %t5, [%dst + offset + 0x14] %asi; \ - sta %t6, [%dst + offset + 0x18] %asi; \ - sta %t7, [%dst + offset + 0x1c] %asi; - -#define MOVE_BIGALIGNCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \ - ldd [%src + offset + 0x00], %t0; \ - ldd [%src + offset + 0x08], %t2; \ - ldd [%src + offset + 0x10], %t4; \ - ldd [%src + offset + 0x18], %t6; \ - stda %t0, [%dst + offset + 0x00] %asi; \ - stda %t2, [%dst + offset + 0x08] %asi; \ - stda %t4, [%dst + offset + 0x10] %asi; \ - stda %t6, [%dst + offset + 0x18] %asi; - -#define MOVE_LASTCHUNK(src, dst, offset, t0, t1, t2, t3) \ - ldd [%src - offset - 0x10], %t0; \ - ldd [%src - offset - 0x08], %t2; \ - sta %t0, [%dst - offset - 0x10] %asi; \ - sta %t1, [%dst - offset - 0x0c] %asi; \ - sta %t2, [%dst - offset - 0x08] %asi; \ - sta %t3, [%dst - offset - 0x04] %asi; - -#define MOVE_HALFCHUNK(src, dst, offset, t0, t1, t2, t3) \ - lduh [%src + offset + 0x00], %t0; \ - lduh [%src + offset + 0x02], %t1; \ - lduh [%src + offset + 0x04], %t2; \ - lduh [%src + offset + 0x06], %t3; \ - stha %t0, [%dst + offset + 0x00] %asi; \ - stha %t1, [%dst + offset + 0x02] %asi; \ - stha %t2, [%dst + offset + 0x04] %asi; \ - stha %t3, [%dst + offset + 0x06] %asi; - -#define MOVE_SHORTCHUNK(src, dst, offset, t0, t1) \ - ldub [%src - offset - 0x02], %t0; \ - ldub [%src - offset - 0x01], %t1; \ - stba %t0, [%dst - offset - 0x02] %asi; \ - stba %t1, [%dst - offset - 0x01] %asi; - - .text - .align 4 - - .globl __copy_to_user -dword_align: - andcc %o1, 1, %g0 - be 4f - andcc %o1, 2, %g0 - - ldub [%o1], %g2 - add %o1, 1, %o1 - EXO2(stba %g2, [%o0] %asi,#) - sub %o2, 1, %o2 - bne 3f - add %o0, 1, %o0 - - lduh [%o1], %g2 - add %o1, 2, %o1 - EXO2(stha %g2, [%o0] %asi,#) - sub %o2, 2, %o2 - ba,pt %xcc, 3f - add %o0, 2, %o0 -4: - lduh [%o1], %g2 - add %o1, 2, %o1 - EXO2(stha %g2, [%o0] %asi,#) - sub %o2, 2, %o2 - ba,pt %xcc, 3f - add %o0, 2, %o0 - -__copy_to_user: /* %o0=dst %o1=src %o2=len */ - wr %g0, ASI_S, %asi - xor %o0, %o1, %o4 -1: - andcc %o4, 3, %o5 -2: - bne,pn %icc, cannot_optimize - cmp %o2, 15 - - bleu,pn %xcc, short_aligned_end - andcc %o1, 3, %g0 - - bne,pn %icc, dword_align -3: - andcc %o1, 4, %g0 - - be,pt %icc, 2f - mov %o2, %g1 - - ld [%o1], %o4 - sub %g1, 4, %g1 - EXO2(sta %o4, [%o0] %asi,#) - add %o1, 4, %o1 - add %o0, 4, %o0 -2: - andcc %g1, 0xffffffffffffff80, %g7 - be,pn %xcc, 3f - andcc %o0, 4, %g0 - - be,pn %icc, ldd_std + 4 -5: - MOVE_BIGCHUNK(o1, o0, 0x00, o2, o3, o4, o5, g2, g3, g4, g5) - MOVE_BIGCHUNK(o1, o0, 0x20, o2, o3, o4, o5, g2, g3, g4, g5) - MOVE_BIGCHUNK(o1, o0, 0x40, o2, o3, o4, o5, g2, g3, g4, g5) - MOVE_BIGCHUNK(o1, o0, 0x60, o2, o3, o4, o5, g2, g3, g4, g5) -80: - EXT(5b, 80b, 50f,#) - subcc %g7, 128, %g7 - add %o1, 128, %o1 - bne,pt %xcc, 5b - add %o0, 128, %o0 -3: - andcc %g1, 0x70, %g7 - be,pn %icc, copy_user_table_end - andcc %g1, 8, %g0 -100: - rd %pc, %o5 - srl %g7, 1, %o4 - add %g7, %o4, %o4 - add %o1, %g7, %o1 - sub %o5, %o4, %o5 - jmpl %o5 + (copy_user_table_end - 100b), %g0 - add %o0, %g7, %o0 - -copy_user_table: - MOVE_LASTCHUNK(o1, o0, 0x60, g2, g3, g4, g5) - MOVE_LASTCHUNK(o1, o0, 0x50, g2, g3, g4, g5) - MOVE_LASTCHUNK(o1, o0, 0x40, g2, g3, g4, g5) - MOVE_LASTCHUNK(o1, o0, 0x30, g2, g3, g4, g5) - MOVE_LASTCHUNK(o1, o0, 0x20, g2, g3, g4, g5) - MOVE_LASTCHUNK(o1, o0, 0x10, g2, g3, g4, g5) - MOVE_LASTCHUNK(o1, o0, 0x00, g2, g3, g4, g5) -copy_user_table_end: - EXT(copy_user_table, copy_user_table_end, 51f,#) - be,pt %icc, copy_user_last7 - andcc %g1, 4, %g0 - - ldd [%o1], %g2 - add %o0, 8, %o0 - add %o1, 8, %o1 - EX(sta %g2, [%o0 - 0x08] %asi, and %g1, 0xf,#) - EX2(sta %g3, [%o0 - 0x04] %asi, and %g1, 0xf, %g1, sub %g1, 4,#) -copy_user_last7: - be,pn %icc, 1f - andcc %g1, 2, %g0 - - ld [%o1], %g2 - add %o1, 4, %o1 - EX(sta %g2, [%o0] %asi, and %g1, 7,#) - add %o0, 4, %o0 -1: - be,pn %icc, 1f - andcc %g1, 1, %g0 - - lduh [%o1], %g2 - add %o1, 2, %o1 - EX(stha %g2, [%o0] %asi, and %g1, 3,#) - add %o0, 2, %o0 -1: - be,pn %icc, 1f - nop - - ldub [%o1], %g2 - EX(stba %g2, [%o0] %asi, add %g0, 1,#) -1: - PRE_RETL - retl - clr %o0 - -ldd_std: - MOVE_BIGALIGNCHUNK(o1, o0, 0x00, o2, o3, o4, o5, g2, g3, g4, g5) - MOVE_BIGALIGNCHUNK(o1, o0, 0x20, o2, o3, o4, o5, g2, g3, g4, g5) - MOVE_BIGALIGNCHUNK(o1, o0, 0x40, o2, o3, o4, o5, g2, g3, g4, g5) - MOVE_BIGALIGNCHUNK(o1, o0, 0x60, o2, o3, o4, o5, g2, g3, g4, g5) -81: - EXT(ldd_std, 81b, 52f,#) - subcc %g7, 128, %g7 - add %o1, 128, %o1 - bne,pt %xcc, ldd_std - add %o0, 128, %o0 - - andcc %g1, 0x70, %g7 - be,pn %icc, copy_user_table_end - andcc %g1, 8, %g0 -101: - rd %pc, %o5 - srl %g7, 1, %o4 - add %g7, %o4, %o4 - add %o1, %g7, %o1 - sub %o5, %o4, %o5 - jmpl %o5 + (copy_user_table_end - 101b), %g0 - add %o0, %g7, %o0 - -cannot_optimize: - bleu short_end - cmp %o5, 2 - - bne byte_chunk - and %o2, 0xfffffffffffffff0, %o3 - - andcc %o1, 1, %g0 - be 10f - nop - - ldub [%o1], %g2 - add %o1, 1, %o1 - EXO2(stba %g2, [%o0] %asi,#) - sub %o2, 1, %o2 - andcc %o2, 0xfffffffffffffff0, %o3 - be short_end - add %o0, 1, %o0 -10: - MOVE_HALFCHUNK(o1, o0, 0x00, g2, g3, g4, g5) - MOVE_HALFCHUNK(o1, o0, 0x08, g2, g3, g4, g5) -82: - EXT(10b, 82b, 53f,#) - subcc %o3, 0x10, %o3 - add %o1, 0x10, %o1 - bne 10b - add %o0, 0x10, %o0 - ba,pt %xcc, 2f - and %o2, 0xe, %o3 - -byte_chunk: - MOVE_SHORTCHUNK(o1, o0, -0x02, g2, g3) - MOVE_SHORTCHUNK(o1, o0, -0x04, g2, g3) - MOVE_SHORTCHUNK(o1, o0, -0x06, g2, g3) - MOVE_SHORTCHUNK(o1, o0, -0x08, g2, g3) - MOVE_SHORTCHUNK(o1, o0, -0x0a, g2, g3) - MOVE_SHORTCHUNK(o1, o0, -0x0c, g2, g3) - MOVE_SHORTCHUNK(o1, o0, -0x0e, g2, g3) - MOVE_SHORTCHUNK(o1, o0, -0x10, g2, g3) -83: - EXT(byte_chunk, 83b, 54f,#) - subcc %o3, 0x10, %o3 - add %o1, 0x10, %o1 - bne,pt %xcc, byte_chunk - add %o0, 0x10, %o0 - -short_end: - and %o2, 0xe, %o3 -2: - rd %pc, %o5 - sll %o3, 3, %o4 - add %o0, %o3, %o0 - sub %o5, %o4, %o5 - add %o1, %o3, %o1 - jmpl %o5 + (short_table_end - 2b), %g0 - andcc %o2, 1, %g0 -84: - MOVE_SHORTCHUNK(o1, o0, 0x0c, g2, g3) - MOVE_SHORTCHUNK(o1, o0, 0x0a, g2, g3) - MOVE_SHORTCHUNK(o1, o0, 0x08, g2, g3) - MOVE_SHORTCHUNK(o1, o0, 0x06, g2, g3) - MOVE_SHORTCHUNK(o1, o0, 0x04, g2, g3) - MOVE_SHORTCHUNK(o1, o0, 0x02, g2, g3) - MOVE_SHORTCHUNK(o1, o0, 0x00, g2, g3) -short_table_end: - EXT(84b, short_table_end, 55f,#) - be 1f - nop - ldub [%o1], %g2 - EX(stba %g2, [%o0] %asi, add %g0, 1,#) -1: - PRE_RETL - retl - clr %o0 - -short_aligned_end: - bne short_end - andcc %o2, 8, %g0 - - be 1f - andcc %o2, 4, %g0 - - ld [%o1 + 0x00], %g2 - ld [%o1 + 0x04], %g3 - add %o1, 8, %o1 - EXO2(sta %g2, [%o0 + 0x00] %asi,#) - EX(sta %g3, [%o0 + 0x04] %asi, sub %o2, 4,#) - add %o0, 8, %o0 -1: - ba,pt %xcc, copy_user_last7 - mov %o2, %g1 - - .section .fixup,#alloc,#execinstr - .align 4 -97: - PRE_RETL - retl - mov %o2, %o0 -/* exception routine sets %g2 to (broken_insn - first_insn)>>2 */ -50: -/* This magic counts how many bytes are left when crash in MOVE_BIGCHUNK - * happens. This is derived from the amount ldd reads, st stores, etc. - * x = g2 % 12; - * o0 = g1 + g7 - ((g2 / 12) * 32 + (x < 4) ? x * 8 : (x - 4) * 4) - */ - cmp %g2, 12 - bcs 1f - cmp %g2, 24 - bcs 2f - cmp %g2, 36 - bcs 3f - nop - sub %g2, 12, %g2 - sub %g7, 32, %g7 -3: - sub %g2, 12, %g2 - sub %g7, 32, %g7 -2: - sub %g2, 12, %g2 - sub %g7, 32, %g7 -1: - cmp %g2, 4 - bcs,a 1f - sll %g2, 3, %g2 - sub %g2, 4, %g2 - sll %g2, 2, %g2 -1: - and %g1, 0x7f, %o0 - add %o0, %g7, %o0 - PRE_RETL - retl - sub %o0, %g2, %o0 -51: -/* i = 41 - g2; j = i % 6; - * o0 = (g1 & 15) + (i / 6) * 16 + (j < 4) ? (j + 1) * 4 : (j - 3) * 8; - */ - neg %g2 - and %g1, 0xf, %g1 - add %g2, 41, %g2 -1: - cmp %g2, 6 - bcs,a 2f - cmp %g2, 4 - add %g1, 16, %g1 - b 1b - sub %g2, 6, %g2 -2: - bcs,a 3f - inc %g2 - sub %g2, 3, %g2 - b 2f - sll %g2, 3, %g2 -3: - sll %g2, 2, %g2 -2: - PRE_RETL - retl - add %g1, %g2, %o0 -52: -/* o0 = g1 + g7 - (g2 / 8) * 32 + (x & 3) * 8 */ - and %g2, 0xfffffffffffffff8, %g4 - and %g2, 3, %g2 - sll %g4, 2, %g4 - sll %g2, 3, %g2 - add %g2, %g4, %g2 - b,a 1b -53: -/* o0 = o3 + (o2 & 15) - (g2 & 8) - (g2 & 3) * 2 */ - and %g2, 3, %g4 - and %g2, 0xfffffffffffffff8, %g2 - sll %g4, 1, %g4 - add %g2, %g4, %g2 - and %o2, 0xf, %o0 - add %o0, %o3, %o0 - PRE_RETL - retl - sub %o0, %g2, %o0 -54: -/* o0 = o3 + (o2 & 15) - (g2 / 4) * 2 - (g2 & 1) */ - srl %g2, 2, %o4 - and %g2, 1, %o1 - sll %o4, 1, %o4 - and %o2, 0xf, %o2 - sub %o3, %o1, %o3 - sub %o2, %o4, %o2 - PRE_RETL - retl - add %o2, %o3, %o0 -55: -/* o0 = (o2 & 1) + (27 - g2)/4 * 2 + ((27 - g2) & 1) */ - neg %g2 - and %o2, 1, %o2 - add %g2, 27, %g2 - srl %g2, 2, %o1 - and %g2, 1, %g2 - sll %o1, 1, %o1 - add %o2, %g2, %o0 - PRE_RETL - retl - add %o0, %o1, %o0 diff --git a/arch/sparc64/lib/memcpy.S b/arch/sparc64/lib/memcpy.S deleted file mode 100644 index 86adbc0e92af..000000000000 --- a/arch/sparc64/lib/memcpy.S +++ /dev/null @@ -1,526 +0,0 @@ -/* memcpy.S: Sparc optimized memcpy, bcopy and memmove code - * Hand optimized from GNU libc's memcpy, bcopy and memmove - * for UltraSparc - * Copyright (C) 1991,1996 Free Software Foundation - * Copyright (C) 1995 Linus Torvalds (Linus.Torvalds@helsinki.fi) - * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) - * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) - * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) - */ - -#include -#include - -#ifdef __KERNEL__ - -#define FUNC(x) \ - .globl x; \ - .type x,@function; \ - .align 4; \ -x: - -#define FASTER_ALIGNED - -/* In kernel these functions don't return a value. - * One should use macros in asm/string.h for that purpose. - * We return 0, so that bugs are more apparent. - */ -#define SETUP_RETL -#define PRE_RETL sethi %uhi(KERNBASE), %g4; clr %o0 -#define RETL_INSN sllx %g4, 32, %g4 - -#else - -/* libc */ - -#define FASTER_ALIGNED - -#ifdef DEBUG -#define FUNC(x) \ - .globl jj##x##1; \ - .type jj##x##1,@function; \ - .align 4; \ -jj##x##1: -#else -#include "DEFS.h" -#endif - -#define SETUP_RETL mov %o0, %g6 -#define PRE_RETL -#define RETL_INSN mov %g6, %o0 - -#endif - -#define MOVE_BIGCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \ - ldd [%src + offset + 0x00], %t0; \ - ldd [%src + offset + 0x08], %t2; \ - ldd [%src + offset + 0x10], %t4; \ - ldd [%src + offset + 0x18], %t6; \ - stw %t0, [%dst + offset + 0x00]; \ - stw %t1, [%dst + offset + 0x04]; \ - stw %t2, [%dst + offset + 0x08]; \ - stw %t3, [%dst + offset + 0x0c]; \ - stw %t4, [%dst + offset + 0x10]; \ - stw %t5, [%dst + offset + 0x14]; \ - stw %t6, [%dst + offset + 0x18]; \ - stw %t7, [%dst + offset + 0x1c]; - -#define MOVE_BIGALIGNCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \ - ldx [%src + offset + 0x00], %t0; \ - ldx [%src + offset + 0x08], %t1; \ - ldx [%src + offset + 0x10], %t2; \ - ldx [%src + offset + 0x18], %t3; \ - ldx [%src + offset + 0x20], %t4; \ - ldx [%src + offset + 0x28], %t5; \ - ldx [%src + offset + 0x30], %t6; \ - ldx [%src + offset + 0x38], %t7; \ - stx %t0, [%dst + offset + 0x00]; \ - stx %t1, [%dst + offset + 0x08]; \ - stx %t2, [%dst + offset + 0x10]; \ - stx %t3, [%dst + offset + 0x18]; \ - stx %t4, [%dst + offset + 0x20]; \ - stx %t5, [%dst + offset + 0x28]; \ - stx %t6, [%dst + offset + 0x30]; \ - stx %t7, [%dst + offset + 0x38]; - -#define MOVE_LASTCHUNK(src, dst, offset, t0, t1, t2, t3) \ - ldd [%src - offset - 0x10], %t0; \ - ldd [%src - offset - 0x08], %t2; \ - stw %t0, [%dst - offset - 0x10]; \ - stw %t1, [%dst - offset - 0x0c]; \ - stw %t2, [%dst - offset - 0x08]; \ - stw %t3, [%dst - offset - 0x04]; - -#define MOVE_LASTALIGNCHUNK(src, dst, offset, t0, t1) \ - ldx [%src - offset - 0x10], %t0; \ - ldx [%src - offset - 0x08], %t1; \ - stx %t0, [%dst - offset - 0x10]; \ - stx %t1, [%dst - offset - 0x08]; - -#define MOVE_SHORTCHUNK(src, dst, offset, t0, t1) \ - ldub [%src - offset - 0x02], %t0; \ - ldub [%src - offset - 0x01], %t1; \ - stb %t0, [%dst - offset - 0x02]; \ - stb %t1, [%dst - offset - 0x01]; - - .text - .align 4 - -FUNC(bcopy) - - mov %o0, %o3 - mov %o1, %o0 - mov %o3, %o1 - brgez,a,pt %o2, 1f - cmp %o0, %o1 - - retl - nop ! Only bcopy returns here and it retuns void... - -#ifdef __KERNEL__ -FUNC(amemmove) -FUNC(__memmove) -#endif -FUNC(memmove) - - cmp %o0, %o1 -1: - SETUP_RETL - bleu,pt %xcc, 9f - sub %o0, %o1, %o4 - - add %o1, %o2, %o3 - cmp %o3, %o0 - bleu,pt %xcc, 0f - andcc %o4, 3, %o5 - - add %o1, %o2, %o1 - add %o0, %o2, %o0 - sub %o1, 1, %o1 - sub %o0, 1, %o0 - -1: - ldub [%o1], %o4 - subcc %o2, 1, %o2 - sub %o1, 1, %o1 - stb %o4, [%o0] - bne,pt %icc, 1b - sub %o0, 1, %o0 - - PRE_RETL - retl - RETL_INSN - -#ifdef __KERNEL__ -FUNC(__memcpy) -#endif -FUNC(memcpy) /* %o0=dst %o1=src %o2=len */ - - sub %o0, %o1, %o4 - SETUP_RETL -9: - andcc %o4, 3, %o5 -0: - bne,pn %icc, 86f - cmp %o2, 15 - - bleu,pn %xcc, 90f - andcc %o1, 3, %g0 - - be,a,pt %icc, 3f ! check if we need to align - andcc %o1, 4, %g0 - - andcc %o1, 1, %g0 - be,pn %icc, 4f - andcc %o1, 2, %g0 - - ldub [%o1], %g2 - add %o1, 1, %o1 - sub %o2, 1, %o2 - stb %g2, [%o0] - bne,pn %icc, 5f - add %o0, 1, %o0 -4: - lduh [%o1], %g2 - add %o1, 2, %o1 - sub %o2, 2, %o2 - sth %g2, [%o0] - add %o0, 2, %o0 -5: - andcc %o1, 4, %g0 -3: - be,pn %icc, 2f - mov %o2, %g1 - - lduw [%o1], %o4 - sub %g1, 4, %g1 - stw %o4, [%o0] - add %o1, 4, %o1 - add %o0, 4, %o0 -2: - andcc %g1, -128, %g7 - be,pn %xcc, 3f - andcc %o0, 4, %g0 - - be,a,pn %icc, 82f + 4 - ldx [%o1], %o2 -5: - MOVE_BIGCHUNK(o1, o0, 0x00, o2, o3, o4, o5, g2, g3, g4, g5) - MOVE_BIGCHUNK(o1, o0, 0x20, o2, o3, o4, o5, g2, g3, g4, g5) - MOVE_BIGCHUNK(o1, o0, 0x40, o2, o3, o4, o5, g2, g3, g4, g5) - MOVE_BIGCHUNK(o1, o0, 0x60, o2, o3, o4, o5, g2, g3, g4, g5) - subcc %g7, 128, %g7 - add %o1, 128, %o1 - bne,pt %xcc, 5b - add %o0, 128, %o0 -3: - andcc %g1, 0x70, %g7 - be,pn %icc, 80f - andcc %g1, 8, %g0 -79: - rd %pc, %o5 - srl %g7, 1, %o4 - add %g7, %o4, %o4 - add %o1, %g7, %o1 - sub %o5, %o4, %o5 - jmpl %o5 + %lo(80f-79b), %g0 - add %o0, %g7, %o0 - - MOVE_LASTCHUNK(o1, o0, 0x60, g2, g3, g4, g5) - MOVE_LASTCHUNK(o1, o0, 0x50, g2, g3, g4, g5) - MOVE_LASTCHUNK(o1, o0, 0x40, g2, g3, g4, g5) - MOVE_LASTCHUNK(o1, o0, 0x30, g2, g3, g4, g5) - MOVE_LASTCHUNK(o1, o0, 0x20, g2, g3, g4, g5) - MOVE_LASTCHUNK(o1, o0, 0x10, g2, g3, g4, g5) - MOVE_LASTCHUNK(o1, o0, 0x00, g2, g3, g4, g5) - -80: /* memcpy_table_end */ - be,pt %icc, 81f - andcc %g1, 4, %g0 - - ldd [%o1], %g2 - add %o0, 8, %o0 - stw %g2, [%o0 - 0x08] - add %o1, 8, %o1 - stw %g3, [%o0 - 0x04] - -81: /* memcpy_last7 */ - - be,pt %icc, 1f - andcc %g1, 2, %g0 - - lduw [%o1], %g2 - add %o1, 4, %o1 - stw %g2, [%o0] - add %o0, 4, %o0 -1: - be,pt %icc, 1f - andcc %g1, 1, %g0 - - lduh [%o1], %g2 - add %o1, 2, %o1 - sth %g2, [%o0] - add %o0, 2, %o0 -1: - be,pt %icc, 1f - nop - - ldub [%o1], %g2 - stb %g2, [%o0] -1: - PRE_RETL - retl - RETL_INSN - -82: /* ldx_stx */ - MOVE_BIGALIGNCHUNK(o1, o0, 0x00, o2, o3, o4, o5, g2, g3, g4, g5) - MOVE_BIGALIGNCHUNK(o1, o0, 0x40, o2, o3, o4, o5, g2, g3, g4, g5) - subcc %g7, 128, %g7 - add %o1, 128, %o1 - bne,pt %xcc, 82b - add %o0, 128, %o0 - -#ifndef FASTER_ALIGNED - - andcc %g1, 0x70, %g7 - be,pn %icc, 80b - andcc %g1, 8, %g0 -83: - rd %pc, %o5 - srl %g7, 1, %o4 - add %g7, %o4, %o4 - add %o1, %g7, %o1 - sub %o5, %o4, %o5 - jmpl %o5 + %lo(80b - 83b), %g0 - add %o0, %g7, %o0 - -#else /* FASTER_ALIGNED */ - - andcc %g1, 0x70, %g7 - be,pn %icc, 84f - andcc %g1, 8, %g0 -83: - rd %pc, %o5 - add %o1, %g7, %o1 - sub %o5, %g7, %o5 - jmpl %o5 + %lo(84f - 83b), %g0 - add %o0, %g7, %o0 - - MOVE_LASTALIGNCHUNK(o1, o0, 0x60, g2, g3) - MOVE_LASTALIGNCHUNK(o1, o0, 0x50, g2, g3) - MOVE_LASTALIGNCHUNK(o1, o0, 0x40, g2, g3) - MOVE_LASTALIGNCHUNK(o1, o0, 0x30, g2, g3) - MOVE_LASTALIGNCHUNK(o1, o0, 0x20, g2, g3) - MOVE_LASTALIGNCHUNK(o1, o0, 0x10, g2, g3) - MOVE_LASTALIGNCHUNK(o1, o0, 0x00, g2, g3) - -84: /* amemcpy_table_end */ - be,pt %icc, 85f - andcc %g1, 4, %g0 - - ldx [%o1], %g2 - add %o1, 8, %o1 - stx %g2, [%o0] - add %o0, 8, %o0 -85: /* amemcpy_last7 */ - be,pt %icc, 1f - andcc %g1, 2, %g0 - - lduw [%o1], %g2 - add %o1, 4, %o1 - stw %g2, [%o0] - add %o0, 4, %o0 -1: - be,pt %icc, 1f - andcc %g1, 1, %g0 - - lduh [%o1], %g2 - add %o1, 2, %o1 - sth %g2, [%o0] - add %o0, 2, %o0 -1: - be,pt %icc, 1f - nop - - ldub [%o1], %g2 - stb %g2, [%o0] -1: - PRE_RETL - retl - RETL_INSN - -#endif /* FASTER_ALIGNED */ - -86: /* non_aligned */ - cmp %o2, 15 - bleu,pn %xcc, 88f - - andcc %o0, 3, %g0 - be,pn %icc, 61f - andcc %o0, 1, %g0 - be,pn %icc, 60f - andcc %o0, 2, %g0 - - ldub [%o1], %g5 - add %o1, 1, %o1 - stb %g5, [%o0] - sub %o2, 1, %o2 - bne,pn %icc, 61f - add %o0, 1, %o0 -60: - ldub [%o1], %g3 - add %o1, 2, %o1 - stb %g3, [%o0] - sub %o2, 2, %o2 - ldub [%o1 - 1], %g3 - add %o0, 2, %o0 - stb %g3, [%o0 - 1] -61: - and %o1, 3, %g2 - and %o2, 0xc, %g3 - and %o1, -4, %o1 - cmp %g3, 4 - sll %g2, 3, %g4 - mov 32, %g2 - be,pn %icc, 4f - sub %g2, %g4, %g7 - - blu,pn %icc, 3f - cmp %g3, 0x8 - - be,pn %icc, 2f - srl %o2, 2, %g3 - - lduw [%o1], %o3 - add %o0, -8, %o0 - lduw [%o1 + 4], %o4 - ba,pt %xcc, 8f - add %g3, 1, %g3 -2: - lduw [%o1], %o4 - add %o0, -12, %o0 - lduw [%o1 + 4], %o5 - add %g3, 2, %g3 - ba,pt %xcc, 9f - add %o1, -4, %o1 -3: - lduw [%o1], %g1 - add %o0, -4, %o0 - lduw [%o1 + 4], %o3 - srl %o2, 2, %g3 - ba,pt %xcc, 7f - add %o1, 4, %o1 -4: - lduw [%o1], %o5 - cmp %o2, 7 - lduw [%o1 + 4], %g1 - srl %o2, 2, %g3 - bleu,pn %xcc, 10f - add %o1, 8, %o1 - - lduw [%o1], %o3 - add %g3, -1, %g3 -5: - sll %o5, %g4, %g2 - srl %g1, %g7, %g5 - or %g2, %g5, %g2 - stw %g2, [%o0] -7: - lduw [%o1 + 4], %o4 - sll %g1, %g4, %g2 - srl %o3, %g7, %g5 - or %g2, %g5, %g2 - stw %g2, [%o0 + 4] -8: - lduw [%o1 + 8], %o5 - sll %o3, %g4, %g2 - srl %o4, %g7, %g5 - or %g2, %g5, %g2 - stw %g2, [%o0 + 8] -9: - lduw [%o1 + 12], %g1 - sll %o4, %g4, %g2 - srl %o5, %g7, %g5 - addcc %g3, -4, %g3 - or %g2, %g5, %g2 - add %o1, 16, %o1 - stw %g2, [%o0 + 12] - add %o0, 16, %o0 - bne,a,pt %xcc, 5b - lduw [%o1], %o3 -10: - sll %o5, %g4, %g2 - srl %g1, %g7, %g5 - srl %g7, 3, %g3 - or %g2, %g5, %g2 - sub %o1, %g3, %o1 - andcc %o2, 2, %g0 - stw %g2, [%o0] - be,pt %icc, 1f - andcc %o2, 1, %g0 - - ldub [%o1], %g2 - add %o1, 2, %o1 - stb %g2, [%o0 + 4] - add %o0, 2, %o0 - ldub [%o1 - 1], %g2 - stb %g2, [%o0 + 3] -1: - be,pt %icc, 1f - nop - - ldub [%o1], %g2 - stb %g2, [%o0 + 4] -1: - PRE_RETL - retl - RETL_INSN - -88: /* short_end */ - - and %o2, 0xe, %o3 -20: - rd %pc, %o5 - sll %o3, 3, %o4 - add %o0, %o3, %o0 - sub %o5, %o4, %o5 - add %o1, %o3, %o1 - jmpl %o5 + %lo(89f - 20b), %g0 - andcc %o2, 1, %g0 - - MOVE_SHORTCHUNK(o1, o0, 0x0c, g2, g3) - MOVE_SHORTCHUNK(o1, o0, 0x0a, g2, g3) - MOVE_SHORTCHUNK(o1, o0, 0x08, g2, g3) - MOVE_SHORTCHUNK(o1, o0, 0x06, g2, g3) - MOVE_SHORTCHUNK(o1, o0, 0x04, g2, g3) - MOVE_SHORTCHUNK(o1, o0, 0x02, g2, g3) - MOVE_SHORTCHUNK(o1, o0, 0x00, g2, g3) - -89: /* short_table_end */ - - be,pt %icc, 1f - nop - - ldub [%o1], %g2 - stb %g2, [%o0] -1: - PRE_RETL - retl - RETL_INSN - -90: /* short_aligned_end */ - bne,pn %xcc, 88b - andcc %o2, 8, %g0 - - be,pt %icc, 1f - andcc %o2, 4, %g0 - - lduw [%o1 + 0x00], %g2 - lduw [%o1 + 0x04], %g3 - add %o1, 8, %o1 - stw %g2, [%o0 + 0x00] - stw %g3, [%o0 + 0x04] - add %o0, 8, %o0 -1: - ba,pt %xcc, 81b - mov %o2, %g1 diff --git a/arch/sparc64/lib/memset.S b/arch/sparc64/lib/memset.S deleted file mode 100644 index a327316e66b9..000000000000 --- a/arch/sparc64/lib/memset.S +++ /dev/null @@ -1,197 +0,0 @@ -/* linux/arch/sparc64/lib/memset.S: Sparc optimized memset, bzero and clear_user code - * Copyright (C) 1991,1996 Free Software Foundation - * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) - * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) - * - * Returns 0, if ok, and number of bytes not yet set if exception - * occurs and we were called as clear_user. - */ - -#include -#include - -#define EX(x,y,a,b,z) \ -98: x,y; \ - .section .fixup,z##alloc,z##execinstr; \ - .align 4; \ -99: ba,pt %xcc, 30f; \ - a, b, %o0; \ - .section __ex_table,z##alloc; \ - .align 8; \ - .xword 98b, 99b; \ - .text; \ - .align 4 - -#define EXT(start,end,handler,z) \ - .section __ex_table,z##alloc; \ - .align 8; \ - .xword start, 0, end, handler; \ - .text; \ - .align 4 - -/* Please don't change these macros, unless you change the logic - * in the .fixup section below as well. - * Store 64 bytes at (BASE + OFFSET) using value SOURCE. - */ -#define ZERO_BIG_BLOCK(base, offset, source) \ - stxa source, [base + offset + 0x00] %asi; \ - stxa source, [base + offset + 0x08] %asi; \ - stxa source, [base + offset + 0x10] %asi; \ - stxa source, [base + offset + 0x18] %asi; \ - stxa source, [base + offset + 0x20] %asi; \ - stxa source, [base + offset + 0x28] %asi; \ - stxa source, [base + offset + 0x30] %asi; \ - stxa source, [base + offset + 0x38] %asi; - -#define ZERO_LAST_BLOCKS(base, offset, source) \ - stxa source, [base - offset - 0x38] %asi; \ - stxa source, [base - offset - 0x30] %asi; \ - stxa source, [base - offset - 0x28] %asi; \ - stxa source, [base - offset - 0x20] %asi; \ - stxa source, [base - offset - 0x18] %asi; \ - stxa source, [base - offset - 0x10] %asi; \ - stxa source, [base - offset - 0x08] %asi; \ - stxa source, [base - offset - 0x00] %asi; - - .text - .align 4 - - .globl __bzero, __memset, __bzero_noasi - .globl memset, __memset_start, __memset_end -__memset_start: -__memset: -memset: - and %o1, 0xff, %g3 - sll %g3, 8, %g2 - or %g3, %g2, %g3 - sll %g3, 16, %g2 - or %g3, %g2, %g3 - mov %o2, %o1 - wr %g0, ASI_P, %asi - sllx %g3, 32, %g2 - ba,pt %xcc, 1f - or %g3, %g2, %g3 -__bzero: - wr %g0, ASI_P, %asi -__bzero_noasi: - mov %g0, %g3 -1: - cmp %o1, 7 - bleu,pn %xcc, 7f - andcc %o0, 3, %o2 - - be,a,pt %icc, 4f - andcc %o0, 4, %g0 - - cmp %o2, 3 - be,pn %icc, 2f - EX(stba %g3, [%o0] %asi, sub %o1, 0,#) - - cmp %o2, 2 - be,pt %icc, 2f - EX(stba %g3, [%o0 + 0x01] %asi, sub %o1, 1,#) - - EX(stba %g3, [%o0 + 0x02] %asi, sub %o1, 2,#) -2: - sub %o2, 4, %o2 - sub %o0, %o2, %o0 - add %o1, %o2, %o1 - andcc %o0, 4, %g0 -4: - be,a,pt %icc, 2f - andncc %o1, 0x7f, %o3 - - EX(sta %g3, [%o0] %asi, sub %o1, 0,#) - sub %o1, 4, %o1 - add %o0, 4, %o0 - andncc %o1, 0x7f, %o3 ! Now everything is 8 aligned and o1 is len to run -2: - be,pn %xcc, 9f - andcc %o1, 0x78, %o2 -10: - ZERO_BIG_BLOCK(%o0, 0x00, %g3) - subcc %o3, 128, %o3 - ZERO_BIG_BLOCK(%o0, 0x40, %g3) -11: - EXT(10b, 11b, 20f,#) - bne,pt %xcc, 10b - add %o0, 128, %o0 - - tst %o2 -9: - be,pn %xcc, 13f - andcc %o1, 7, %o1 -14: - rd %pc, %o4 - srl %o2, 1, %o3 - sub %o4, %o3, %o4 - jmpl %o4 + (13f - 14b), %g0 - add %o0, %o2, %o0 -12: - ZERO_LAST_BLOCKS(%o0, 0x48, %g3) - ZERO_LAST_BLOCKS(%o0, 0x08, %g3) -13: - be,pn %icc, 8f - andcc %o1, 4, %g0 - - be,pn %icc, 1f - andcc %o1, 2, %g0 - - EX(sta %g3, [%o0] %asi, and %o1, 7,#) - add %o0, 4, %o0 -1: - be,pn %icc, 1f - andcc %o1, 1, %g0 - - EX(stha %g3, [%o0] %asi, and %o1, 3,#) - add %o0, 2, %o0 -1: - bne,a,pn %icc, 8f - EX(stba %g3, [%o0] %asi, and %o1, 1,#) -8: - retl - clr %o0 -7: - be,pn %icc, 13b - orcc %o1, 0, %g0 - - be,pn %icc, 0f -8: - add %o0, 1, %o0 - subcc %o1, 1, %o1 - bne,a,pt %icc, 8b - EX(stba %g3, [%o0 - 1] %asi, add %o1, 1,#) -0: - retl - clr %o0 -__memset_end: - - .section .fixup,#alloc,#execinstr - .align 4 -20: - cmp %g2, 8 - bleu,pn %xcc, 1f - and %o1, 0x7f, %o1 - sub %g2, 9, %g2 - add %o3, 64, %o3 -1: - sll %g2, 3, %g2 - add %o3, %o1, %o0 - ba,pt %xcc, 30f - sub %o0, %g2, %o0 -21: - mov 8, %o0 - and %o1, 7, %o1 - sub %o0, %g2, %o0 - sll %o0, 3, %o0 - ba,pt %xcc, 30f - add %o0, %o1, %o0 -30: -/* %o4 is faulting address, %o5 is %pc where fault occured */ - save %sp, -160, %sp - mov %i5, %o0 - mov %i7, %o1 - call lookup_fault - mov %i4, %o2 - ret - restore diff --git a/arch/sparc64/mm/Makefile b/arch/sparc64/mm/Makefile index c41c7a938d8b..dddf3153bb25 100644 --- a/arch/sparc64/mm/Makefile +++ b/arch/sparc64/mm/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.1 1996/12/26 10:24:22 davem Exp $ +# $Id: Makefile,v 1.3 1997/06/27 14:53:38 jj Exp $ # Makefile for the linux Sparc64-specific parts of the memory manager. # # Note! Dependencies are done automagically by 'make dep', which also @@ -7,7 +7,13 @@ # # Note 2! The CFLAGS definition is now in the main makefile... +.S.s: + $(CPP) -D__ASSEMBLY__ -ansi $< -o $*.s + +.S.o: + $(CC) -D__ASSEMBLY__ -ansi -c $< -o $*.o + O_TARGET := mm.o -O_OBJS := fault.o init.o generic.o asyncd.o extable.o +O_OBJS := ultra.o fault.o init.o generic.o asyncd.o extable.o modutil.o include $(TOPDIR)/Rules.make diff --git a/arch/sparc64/mm/fault.c b/arch/sparc64/mm/fault.c index b8da1997c686..d7b48d530181 100644 --- a/arch/sparc64/mm/fault.c +++ b/arch/sparc64/mm/fault.c @@ -1,4 +1,4 @@ -/* $Id: fault.c,v 1.12 1997/06/13 14:02:52 davem Exp $ +/* $Id: fault.c,v 1.17 1997/07/04 01:41:10 davem Exp $ * arch/sparc64/mm/fault.c: Page fault handlers for the 64-bit Sparc. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -134,61 +134,14 @@ asmlinkage int lookup_fault(unsigned long pc, unsigned long ret_pc, return 0; } -/* #define FAULT_TRACER */ -/* #define FAULT_TRACER_VERBOSE */ +/* #define DEBUG_EXCEPTIONS */ -#ifdef FAULT_TRACER -/* Set and clear this elsewhere at critical moment, for oodles of debugging fun. */ -int fault_trace_enable = 0; -#endif - -#include - -asmlinkage void do_sparc64_fault(struct pt_regs *regs, int text_fault, int write, - unsigned long address, unsigned long tag, - unsigned long sfsr) +asmlinkage void do_sparc64_fault(struct pt_regs *regs, unsigned long address, int write) { + struct mm_struct *mm = current->mm; struct vm_area_struct *vma; - struct task_struct *tsk = current; - struct mm_struct *mm = tsk->mm; - unsigned long fixup; - unsigned long g2; - int from_user = !(regs->tstate & TSTATE_PRIV); -#ifdef FAULT_TRACER - static unsigned long last_addr = 0; - static int rcnt = 0; - - if(fault_trace_enable) { -#ifdef FAULT_TRACER_VERBOSE - printk("FAULT(PC[%016lx],t[%d],w[%d],addr[%016lx])...", - regs->tpc, text_fault, write, address); -#else - printk("F[%016lx:%016lx:w(%d)", regs->tpc, address, write); -#endif - if(address == last_addr) { - if(rcnt++ > 15) { - printk("Wheee lotsa bogus faults, something wrong, " - "spinning\n"); - printk("pctx[%016lx]sctx[%016lx]mmctx[%016lx]DS(%x)" - "tctx[%016lx] flgs[%016lx]\n", - spitfire_get_primary_context(), - spitfire_get_secondary_context(), - mm->context, (unsigned)current->tss.current_ds, - current->tss.ctx, current->tss.flags); - __asm__ __volatile__("flushw"); - printk("o7[%016lx] i7[%016lx]\n", - regs->u_regs[UREG_I7], - ((struct reg_window *)(regs->u_regs[UREG_FP]+STACK_BIAS))->ins[7]); - sti(); - while(1) - barrier(); - } - } else rcnt = 0; - last_addr = address; - } -#endif - lock_kernel (); + lock_kernel(); down(&mm->mmap_sem); vma = find_vma(mm, address); if(!vma) @@ -221,42 +174,99 @@ good_area: */ bad_area: up(&mm->mmap_sem); - /* Is this in ex_table? */ + + { + unsigned long g2 = regs->u_regs[UREG_G2]; + + /* Is this in ex_table? */ + if (regs->tstate & TSTATE_PRIV) { + unsigned char asi = ASI_P; + unsigned int insn; + unsigned long fixup; + + insn = *(unsigned int *)regs->tpc; + if ((insn & 0xc0800000) == 0xc0800000) { + if (insn & 0x2000) + asi = (regs->tstate >> 24); + else + asi = (insn >> 5); + } - g2 = regs->u_regs[UREG_G2]; - if (!from_user && (fixup = search_exception_table (regs->tpc, &g2))) { - printk("Exception: PC<%016lx> faddr<%016lx>\n", regs->tpc, address); - printk("EX_TABLE: insn<%016lx> fixup<%016lx> g2<%016lx>\n", - regs->tpc, fixup, g2); - regs->tpc = fixup; - regs->tnpc = regs->tpc + 4; - regs->u_regs[UREG_G2] = g2; - goto out; - } - if(from_user) { -#if 1 - unsigned long cpc; - __asm__ __volatile__("mov %%i7, %0" : "=r" (cpc)); - printk("[%s:%d] SIGSEGV pc[%016lx] addr[%016lx] w[%d] sfsr[%016lx] " - "caller[%016lx]\n", current->comm, current->pid, regs->tpc, - address, write, sfsr, cpc); + /* Look in asi.h: All _S asis have LS bit set */ + if ((asi & 0x1) && + (fixup = search_exception_table (regs->tpc, &g2))) { +#ifdef DEBUG_EXCEPTIONS + printk("Exception: PC<%016lx> faddr<%016lx>\n", + regs->tpc, address); + printk("EX_TABLE: insn<%016lx> fixup<%016lx> " + "g2<%016lx>\n", regs->tpc, fixup, g2); #endif - tsk->tss.sig_address = address; - tsk->tss.sig_desc = SUBSIG_NOMAPPING; - send_sig(SIGSEGV, tsk, 1); - goto out; + regs->tpc = fixup; + regs->tnpc = regs->tpc + 4; + regs->u_regs[UREG_G2] = g2; + goto out; + } + } else { + current->tss.sig_address = address; + current->tss.sig_desc = SUBSIG_NOMAPPING; + send_sig(SIGSEGV, current, 1); + goto out; + } + unhandled_fault (address, current, regs); } - unhandled_fault (address, tsk, regs); out: unlock_kernel(); -#ifdef FAULT_TRACER - if(fault_trace_enable) { -#ifdef FAULT_TRACER_VERBOSE - printk(" done\n"); -#else - printk("]"); -#endif - } -#endif } +void fixup_dcache_alias(struct vm_area_struct *vma, unsigned long address, pte_t pte) +{ + struct vm_area_struct *vmaring; + struct inode *inode; + unsigned long vaddr, offset, start; + pgd_t *pgdp; + pmd_t *pmdp; + pte_t *ptep; + int alias_found = 0; + + inode = vma->vm_inode; + if(!inode) + return; + + offset = (address & PAGE_MASK) - vma->vm_start; + vmaring = inode->i_mmap; + do { + vaddr = vmaring->vm_start + offset; + + /* This conditional is misleading... */ + if((vaddr ^ address) & PAGE_SIZE) { + alias_found++; + start = vmaring->vm_start; + while(start < vmaring->vm_end) { + pgdp = pgd_offset(vmaring->vm_mm, start); + if(!pgdp) goto next; + pmdp = pmd_offset(pgdp, start); + if(!pmdp) goto next; + ptep = pte_offset(pmdp, start); + if(!ptep) goto next; + + if(pte_val(*ptep) & _PAGE_PRESENT) { + flush_cache_page(vmaring, start); + *ptep = __pte(pte_val(*ptep) & + ~(_PAGE_CV)); + flush_tlb_page(vmaring, start); + } + next: + start += PAGE_SIZE; + } + } + } while((vmaring = vmaring->vm_next_share) != NULL); + + if(alias_found && (pte_val(pte) & _PAGE_CV)) { + pgdp = pgd_offset(vma->vm_mm, address); + pmdp = pmd_offset(pgdp, address); + ptep = pte_offset(pmdp, address); + flush_cache_page(vma, address); + *ptep = __pte(pte_val(*ptep) & ~(_PAGE_CV)); + flush_tlb_page(vma, address); + } +} diff --git a/arch/sparc64/mm/generic.c b/arch/sparc64/mm/generic.c index 289ddd411c46..730e8cb323aa 100644 --- a/arch/sparc64/mm/generic.c +++ b/arch/sparc64/mm/generic.c @@ -1,4 +1,4 @@ -/* $Id: generic.c,v 1.1 1996/12/26 10:24:23 davem Exp $ +/* $Id: generic.c,v 1.2 1997/07/01 09:11:42 jj Exp $ * generic.c: Generic Sparc mm routines that are not dependent upon * MMU type but are Sparc specific. * @@ -66,13 +66,35 @@ static inline void io_remap_pte_range(pte_t * pte, unsigned long address, unsign if (end > PMD_SIZE) end = PMD_SIZE; do { - pte_t oldpage = *pte; - pte_clear(pte); - set_pte(pte, mk_pte_io(offset, prot, space)); - forget_pte(oldpage); - address += PAGE_SIZE; + pte_t oldpage; + pte_t entry; + unsigned long curend = address + PAGE_SIZE; + + entry = mk_pte_io(offset, prot, space); offset += PAGE_SIZE; - pte++; + if (!(address & 0xffff)) { + if (!(address & 0x3fffff) && !(offset & 0x3fffff) && end >= address + 0x400000) { + entry = mk_pte_io(offset, __pgprot(pgprot_val (prot) | _PAGE_SZ4MB), space); + curend = address + 0x400000; + offset += 0x400000 - PAGE_SIZE; + } else if (!(address & 0x7ffff) && !(offset & 0x7ffff) && end >= address + 0x80000) { + entry = mk_pte_io(offset, __pgprot(pgprot_val (prot) | _PAGE_SZ512K), space); + curend = address + 0x80000; + offset += 0x80000 - PAGE_SIZE; + } else if (!(offset & 0xffff) && end >= address + 0x10000) { + entry = mk_pte_io(offset, __pgprot(pgprot_val (prot) | _PAGE_SZ64K), space); + curend = address + 0x10000; + offset += 0x10000 - PAGE_SIZE; + } + } + do { + oldpage = *pte; + pte_clear(pte); + set_pte(pte, entry); + forget_pte(oldpage); + address += PAGE_SIZE; + pte++; + } while (address < curend); } while (address < end); } diff --git a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c index a43820257120..a8d903b25655 100644 --- a/arch/sparc64/mm/init.c +++ b/arch/sparc64/mm/init.c @@ -1,15 +1,17 @@ -/* $Id: init.c,v 1.30 1997/06/06 10:56:21 jj Exp $ +/* $Id: init.c,v 1.39 1997/07/07 02:50:57 davem Exp $ * arch/sparc64/mm/init.c * * Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ +#include #include #include #include #include +#include #include #include #include @@ -33,7 +35,7 @@ extern unsigned long empty_null_pte_table; unsigned long tlb_context_cache = CTX_FIRST_VERSION; /* References to section boundaries */ -extern char __init_begin, __init_end, etext, __p1275_loc, __bss_start; +extern char __init_begin, __init_end, etext, __bss_start; /* * BAD_PAGE is the page that is used for page faults when linux @@ -59,13 +61,15 @@ pmd_t *__bad_pmd(void) pte_t *__bad_pte(void) { memset((void *) &empty_bad_pte_table, 0, PAGE_SIZE); - return (pte_t *) (((unsigned long)&empty_bad_pte_table) + phys_base); + return (pte_t *) (((unsigned long)&empty_bad_pte_table) + - ((unsigned long)&empty_zero_page) + phys_base + PAGE_OFFSET); } pte_t __bad_page(void) { memset((void *) &empty_bad_page, 0, PAGE_SIZE); - return pte_mkdirty(mk_pte((((unsigned long) &empty_bad_page)+phys_base), + return pte_mkdirty(mk_pte((((unsigned long) &empty_bad_page) + - ((unsigned long)&empty_zero_page) + phys_base + PAGE_OFFSET), PAGE_SHARED)); } @@ -288,7 +292,7 @@ struct linux_prom_translation { }; #define MAX_TRANSLATIONS 64 -static void inherit_prom_mappings(void) +static inline void inherit_prom_mappings(void) { struct linux_prom_translation transl[MAX_TRANSLATIONS]; pgd_t *pgdp; @@ -332,7 +336,11 @@ static void inherit_prom_mappings(void) } } -static void inherit_locked_prom_mappings(void) +int prom_itlb_ent, prom_dtlb_ent; +unsigned long prom_itlb_tag, prom_itlb_data; +unsigned long prom_dtlb_tag, prom_dtlb_data; + +static inline void inherit_locked_prom_mappings(void) { int i; int dtlb_seen = 0; @@ -359,6 +367,9 @@ static void inherit_locked_prom_mappings(void) data = spitfire_get_dtlb_data(i); if(!dtlb_seen && (data & _PAGE_L)) { unsigned long tag = spitfire_get_dtlb_tag(i); + prom_dtlb_ent = i; + prom_dtlb_tag = tag; + prom_dtlb_data = data; __asm__ __volatile__("stxa %%g0, [%0] %1" : : "r" (TLB_TAG_ACCESS), "i" (ASI_DMMU)); membar("#Sync"); @@ -379,6 +390,9 @@ static void inherit_locked_prom_mappings(void) data = spitfire_get_itlb_data(i); if(!itlb_seen && (data & _PAGE_L)) { unsigned long tag = spitfire_get_itlb_tag(i); + prom_itlb_ent = i; + prom_itlb_tag = tag; + prom_itlb_data = data; __asm__ __volatile__("stxa %%g0, [%0] %1" : : "r" (TLB_TAG_ACCESS), "i" (ASI_IMMU)); membar("#Sync"); @@ -399,6 +413,64 @@ static void inherit_locked_prom_mappings(void) } } +/* Give PROM back his world, done during reboots... */ +void prom_reload_locked(void) +{ + __asm__ __volatile__("stxa %0, [%1] %2" + : : "r" (prom_dtlb_tag), "r" (TLB_TAG_ACCESS), + "i" (ASI_DMMU)); + membar("#Sync"); + spitfire_put_dtlb_data(prom_dtlb_ent, prom_dtlb_data); + membar("#Sync"); + + __asm__ __volatile__("stxa %0, [%1] %2" + : : "r" (prom_itlb_tag), "r" (TLB_TAG_ACCESS), + "i" (ASI_IMMU)); + membar("#Sync"); + spitfire_put_itlb_data(prom_itlb_ent, prom_itlb_data); + membar("#Sync"); +} + +/* If not locked, zap it. */ +void flush_tlb_all(void) +{ + unsigned long flags; + int i; + + save_flags(flags); cli(); + for(i = 0; i < 64; i++) { + if(!(spitfire_get_dtlb_data(i) & _PAGE_L)) { + __asm__ __volatile__("stxa %%g0, [%0] %1" + : /* no outputs */ + : "r" (TLB_TAG_ACCESS), "i" (ASI_DMMU)); + membar("#Sync"); + spitfire_put_dtlb_data(i, 0x0UL); + membar("#Sync"); + } + if(!(spitfire_get_itlb_data(i) & _PAGE_L)) { + __asm__ __volatile__("stxa %%g0, [%0] %1" + : /* no outputs */ + : "r" (TLB_TAG_ACCESS), "i" (ASI_IMMU)); + membar("#Sync"); + spitfire_put_itlb_data(i, 0x0UL); + membar("#Sync"); + } + } + restore_flags(flags); +} + +void get_new_mmu_context(struct mm_struct *mm, unsigned long ctx) +{ + if((ctx & ~(CTX_VERSION_MASK)) == 0) { + flush_tlb_all(); + ctx = (ctx & CTX_VERSION_MASK) + CTX_FIRST_VERSION; + if(ctx == 1) + ctx = CTX_FIRST_VERSION; + } + tlb_context_cache = ctx + 1; + mm->context = ctx; +} + __initfunc(static void allocate_ptable_skeleton(unsigned long start, unsigned long end)) { @@ -500,51 +572,42 @@ paging_init(unsigned long start_mem, unsigned long end_mem)) { extern unsigned long phys_base; extern void setup_tba(unsigned long kpgdir); - extern void __bfill64(void *, unsigned long); - pgd_t *pgdp; + extern void __bfill64(void *, unsigned long *); pmd_t *pmdp; - pte_t *ptep, pte; int i; - - /* Must create 2nd locked DTLB entry if physical ram starts at - * 4MB absolute or higher, kernel image has been placed in the - * right place at PAGE_OFFSET but references to start_mem and pages - * will be to the perfect alias mapping, so set it up now. + unsigned long alias_base = phys_base + PAGE_OFFSET; + unsigned long pt; + unsigned long flags; + unsigned long shift = alias_base - ((unsigned long)&empty_zero_page); + + /* We assume physical memory starts at some 4mb multiple, + * if this were not true we wouldn't boot up to this point + * anyways. */ - if(phys_base >= (4 * 1024 * 1024)) { - unsigned long alias_base = phys_base + PAGE_OFFSET; - unsigned long pte; - unsigned long flags; - - /* We assume physical memory starts at some 4mb multiple, - * if this were not true we wouldn't boot up to this point - * anyways. - */ - pte = phys_base | _PAGE_VALID | _PAGE_SZ4MB; - pte |= _PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_L | _PAGE_W; - save_flags(flags); cli(); - __asm__ __volatile__(" - stxa %1, [%0] %3 - stxa %2, [%5] %4 - membar #Sync - flush %%g4 - nop - nop - nop" - : /* No outputs */ - : "r" (TLB_TAG_ACCESS), "r" (alias_base), "r" (pte), - "i" (ASI_DMMU), "i" (ASI_DTLB_DATA_ACCESS), "r" (61 << 3) - : "memory"); - restore_flags(flags); - - /* Now set kernel pgd to upper alias so physical page computations - * work. - */ - init_mm.pgd += (phys_base / (sizeof(pgd_t *))); - } + pt = phys_base | _PAGE_VALID | _PAGE_SZ4MB; + pt |= _PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_L | _PAGE_W; + save_flags(flags); cli(); + __asm__ __volatile__(" + stxa %1, [%0] %3 + stxa %2, [%5] %4 + membar #Sync + flush %%g6 + nop + nop + nop" + : /* No outputs */ + : "r" (TLB_TAG_ACCESS), "r" (alias_base), "r" (pt), + "i" (ASI_DMMU), "i" (ASI_DTLB_DATA_ACCESS), "r" (61 << 3) + : "memory"); + restore_flags(flags); + + /* Now set kernel pgd to upper alias so physical page computations + * work. + */ + init_mm.pgd += ((shift) / (sizeof(pgd_t *))); - null_pmd_table = __pa(((unsigned long)&empty_null_pmd_table) + phys_base); - null_pte_table = __pa(((unsigned long)&empty_null_pte_table) + phys_base); + null_pmd_table = __pa(((unsigned long)&empty_null_pmd_table) + shift); + null_pte_table = __pa(((unsigned long)&empty_null_pte_table) + shift); pmdp = (pmd_t *) &empty_null_pmd_table; for(i = 0; i < 1024; i++) @@ -553,13 +616,13 @@ paging_init(unsigned long start_mem, unsigned long end_mem)) memset((void *) &empty_null_pte_table, 0, PAGE_SIZE); /* Now can init the kernel/bad page tables. */ - __bfill64((void *)swapper_pg_dir, null_pmd_table); - __bfill64((void *)&empty_bad_pmd_table, null_pte_table); + __bfill64((void *)swapper_pg_dir, &null_pmd_table); + __bfill64((void *)&empty_bad_pmd_table, &null_pte_table); /* We use mempool to create page tables, therefore adjust it up * such that __pa() macros etc. work. */ - mempool = PAGE_ALIGN(start_mem) + phys_base; + mempool = PAGE_ALIGN(start_mem) + shift; /* FIXME: This should be done much nicer. * Just now we allocate 64M for each. @@ -567,48 +630,29 @@ paging_init(unsigned long start_mem, unsigned long end_mem)) allocate_ptable_skeleton(IOBASE_VADDR, IOBASE_VADDR + 0x4000000); allocate_ptable_skeleton(DVMA_VADDR, DVMA_VADDR + 0x4000000); inherit_prom_mappings(); - allocate_ptable_skeleton(0, 0x8000 + PAGE_SIZE); - - /* Map prom interface page. */ - pgdp = pgd_offset(init_task.mm, 0x8000); - pmdp = pmd_offset(pgdp, 0x8000); - ptep = pte_offset(pmdp, 0x8000); - pte = mk_pte(((unsigned long)&__p1275_loc)+phys_base, PAGE_KERNEL); - set_pte(ptep, pte); - + /* Ok, we can use our TLB miss and window trap handlers safely. */ setup_tba((unsigned long)init_mm.pgd); - /* Kill locked PROM interface page mapping, the mapping will - * re-enter on the next PROM interface call via our TLB miss - * handlers. - */ - spitfire_flush_dtlb_primary_page(0x8000); - membar("#Sync"); - spitfire_flush_itlb_primary_page(0x8000); - membar("#Sync"); - /* Really paranoid. */ - flushi(PAGE_OFFSET); + flushi((long)&empty_zero_page); membar("#Sync"); /* Cleanup the extra locked TLB entry we created since we have the * nice TLB miss handlers of ours installed now. */ - if(phys_base >= (4 * 1024 * 1024)) { - /* We only created DTLB mapping of this stuff. */ - spitfire_flush_dtlb_nucleus_page(phys_base + PAGE_OFFSET); - membar("#Sync"); - - /* Paranoid */ - flushi(PAGE_OFFSET); - membar("#Sync"); - } + /* We only created DTLB mapping of this stuff. */ + spitfire_flush_dtlb_nucleus_page(alias_base); + membar("#Sync"); - inherit_locked_prom_mappings(); + /* Paranoid */ + flushi((long)&empty_zero_page); + membar("#Sync"); + inherit_locked_prom_mappings(); + flush_tlb_all(); - + start_mem = free_area_init(PAGE_ALIGN(mempool), end_mem); return device_scan (PAGE_ALIGN (start_mem)); @@ -642,9 +686,8 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) int codepages = 0; int datapages = 0; int initpages = 0; - int prompages = 0; unsigned long tmp2, addr; - unsigned long data_end; + unsigned long alias_base = phys_base + PAGE_OFFSET - (long)(&empty_zero_page); end_mem &= PAGE_MASK; max_mapnr = MAP_NR(end_mem); @@ -665,16 +708,14 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) } taint_real_pages(start_mem, end_mem); - data_end = start_mem - phys_base; for (addr = PAGE_OFFSET; addr < end_mem; addr += PAGE_SIZE) { if(PageReserved(mem_map + MAP_NR(addr))) { - if ((addr < (unsigned long) &etext) && (addr >= PAGE_OFFSET)) + if ((addr < ((unsigned long) &etext) + alias_base) && (addr >= alias_base)) codepages++; - else if((addr >= (unsigned long)&__init_begin && addr < (unsigned long)&__init_end)) + else if((addr >= ((unsigned long)&__init_begin) + alias_base) + && (addr < ((unsigned long)&__init_end) + alias_base)) initpages++; - else if((addr >= (unsigned long)&__p1275_loc && addr < (unsigned long)&__bss_start)) - prompages++; - else if((addr < data_end) && (addr >= PAGE_OFFSET)) + else if((addr < start_mem) && (addr >= alias_base)) datapages++; continue; } @@ -689,12 +730,11 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) tmp2 = nr_free_pages << PAGE_SHIFT; - printk("Memory: %luk available (%dk kernel code, %dk data, %dk init, %dk prom) [%016lx,%016lx]\n", + printk("Memory: %luk available (%dk kernel code, %dk data, %dk init) [%016lx,%016lx]\n", tmp2 >> 10, codepages << (PAGE_SHIFT-10), datapages << (PAGE_SHIFT-10), initpages << (PAGE_SHIFT-10), - prompages << (PAGE_SHIFT-10), PAGE_OFFSET, end_mem); min_free_pages = nr_free_pages >> 7; @@ -702,11 +742,6 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) min_free_pages = 16; free_pages_low = min_free_pages + (min_free_pages >> 1); free_pages_high = min_free_pages + min_free_pages; - -#if 0 - printk("Testing fault handling...\n"); - *(char *)0x00000deadbef0000UL = 0; -#endif } void free_initmem (void) @@ -715,10 +750,8 @@ void free_initmem (void) addr = (unsigned long)(&__init_begin); for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) { - unsigned long page = addr; - - if(page < ((unsigned long)__va(phys_base))) - page += phys_base; + unsigned long page = addr + (long)__va(phys_base) + - (long)(&empty_zero_page); mem_map[MAP_NR(page)].flags &= ~(1 << PG_reserved); atomic_set(&mem_map[MAP_NR(page)].count, 1); diff --git a/arch/sparc64/mm/modutil.c b/arch/sparc64/mm/modutil.c new file mode 100644 index 000000000000..e6b4b2223ba0 --- /dev/null +++ b/arch/sparc64/mm/modutil.c @@ -0,0 +1,66 @@ +/* $Id: modutil.c,v 1.1 1997/06/27 14:53:35 jj Exp $ + * arch/sparc64/mm/modutil.c + * + * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Based upon code written by Linus Torvalds and others. + */ + +#include +#include + +#include +#include +#include + +static struct vm_struct * modvmlist = NULL; + +void module_unmap (void * addr) +{ + struct vm_struct **p, *tmp; + + if (!addr) + return; + if ((PAGE_SIZE-1) & (unsigned long) addr) { + printk("Trying to vfree() bad address (%p)\n", addr); + return; + } + for (p = &modvmlist ; (tmp = *p) ; p = &tmp->next) { + if (tmp->addr == addr) { + *p = tmp->next; + vmfree_area_pages(VMALLOC_VMADDR(tmp->addr), tmp->size); + kfree(tmp); + return; + } + } + printk("Trying to unmap nonexistent module vm area (%p)\n", addr); +} + +void * module_map (unsigned long size) +{ + void * addr; + struct vm_struct **p, *tmp, *area; + + size = PAGE_ALIGN(size); + if (!size || size > MODULES_LEN) return NULL; + + addr = (void *) MODULES_VADDR; + for (p = &modvmlist; (tmp = *p) ; p = &tmp->next) { + if (size + (unsigned long) addr < (unsigned long) tmp->addr) + break; + addr = (void *) (tmp->size + (unsigned long) tmp->addr); + } + if ((unsigned long) addr + size >= MODULES_END) return NULL; + + area = (struct vm_struct *) kmalloc(sizeof(*area), GFP_KERNEL); + if (!area) return NULL; + area->size = size + PAGE_SIZE; + area->addr = addr; + area->next = *p; + *p = area; + + if (vmalloc_area_pages(VMALLOC_VMADDR(addr), size)) { + vfree(addr); + return NULL; + } + return addr; +} diff --git a/arch/sparc64/mm/ultra.S b/arch/sparc64/mm/ultra.S new file mode 100644 index 000000000000..de9893e6e648 --- /dev/null +++ b/arch/sparc64/mm/ultra.S @@ -0,0 +1,101 @@ +/* $Id: ultra.S,v 1.6 1997/06/30 10:31:43 jj Exp $ + * ultra.S: Don't expand these all over the place... + * + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + */ + +#include +#include + + /* All callers check mm->context != NO_CONTEXT for us. */ + .text + .align 32 + .globl __flush_tlb_mm, __flush_tlb_range, __flush_tlb_page +__flush_tlb_mm: /* %o0 == (mm->context & 0x1fff) */ + rdpr %otherwin, %g1 + brz,pt %g1, 1f + mov %o7, %g3 + call __flushw_user + clr %g2 +1: rdpr %pil, %g1 +9: mov SECONDARY_CONTEXT, %g7 + wrpr %g0, 15, %pil + + ldxa [%g7] ASI_DMMU, %g2 + cmp %g2, %o0 + be,pt %icc, 1f + mov 0x50, %g3 + stxa %o0, [%g7] ASI_DMMU +1: stxa %g0, [%g3] ASI_DMMU_DEMAP + be,pt %icc, 1f + stxa %g0, [%g3] ASI_IMMU_DEMAP + + stxa %g2, [%g7] ASI_DMMU +1: wrpr %g1, 0x0, %pil + retl + flush %g6 +__flush_tlb_range: /* %o0 == (mm->context & 0x1fff), %o1 == start, %o2 == end */ + sethi %hi(8192 - 1), %g5 + or %g5, %lo(8192 - 1), %g5 + andn %o1, %g5, %o1 + andn %o2, %g5, %o2 + + sub %o2, %o1, %o3 + add %g5, 1, %g5 + orcc %o1, 0x50, %o1 + srlx %o3, 13, %o4 + rdpr %otherwin, %g1 + brz,pt %g1, 1f + mov %o7, %g3 + call __flushw_user + + clr %g2 +1: cmp %o4, 96 + bgu,pn %icc, 9b + rdpr %pil, %g1 + mov SECONDARY_CONTEXT, %g7 + wrpr %g0, 15, %pil + ldxa [%g7] ASI_DMMU, %g2 + cmp %g2, %o0 + + be,pt %icc, 1f + sub %o3, %g5, %o3 + stxa %o0, [%g7] ASI_DMMU +1: stxa %g0, [%o1 + %o3] ASI_DMMU_DEMAP + stxa %g0, [%o1 + %o3] ASI_IMMU_DEMAP + brnz,pt %o3, 1b + sub %o3, %g5, %o3 + nop + + be,pt %icc, 1f + wrpr %g1, 0x0, %pil + stxa %g2, [%g7] ASI_DMMU +1: retl + flush %g6 + + .align 32 +__flush_tlb_page: /* %o0 == (mm->context & 0x1fff), %o1 == page & PAGE_MASK */ + rdpr %otherwin, %g1 + brz,pt %g1, 1f + mov %o7, %g3 + call __flushw_user + clr %g2 +1: rdpr %pil, %g1 + mov SECONDARY_CONTEXT, %g7 + wrpr %g0, 15, %pil + + ldxa [%g7] ASI_DMMU, %g2 + cmp %g2, %o0 + be,pt %icc, 1f + or %o1, 0x10, %g3 + stxa %o0, [%g7] ASI_DMMU +1: stxa %g0, [%g3] ASI_DMMU_DEMAP + be,pt %icc, 1f + stxa %g0, [%g3] ASI_IMMU_DEMAP + stxa %g2, [%g7] ASI_DMMU +1: wrpr %g1, 0x0, %pil + retl + flush %g6 + + + diff --git a/arch/sparc64/prom/bootstr.c b/arch/sparc64/prom/bootstr.c index e226c6e9585f..7ef17159d3a9 100644 --- a/arch/sparc64/prom/bootstr.c +++ b/arch/sparc64/prom/bootstr.c @@ -1,4 +1,4 @@ -/* $Id: bootstr.c,v 1.3 1997/03/04 16:27:06 jj Exp $ +/* $Id: bootstr.c,v 1.4 1997/06/17 13:25:35 jj Exp $ * bootstr.c: Boot string/argument acquisition from the PROM. * * Copyright(C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -6,18 +6,20 @@ */ #include +#include #include #define BARG_LEN 256 -static char barg_buf[BARG_LEN]; -static char fetched = 0; +int bootstr_len __initdata = BARG_LEN; +static int bootstr_valid __initdata = 0; +static char bootstr_buf[BARG_LEN] __initdata = { 0 }; -char * -prom_getbootargs(void) +__initfunc(char * +prom_getbootargs(void)) { /* This check saves us from a panic when bootfd patches args. */ - if (fetched) return barg_buf; - prom_getstring(prom_chosen_node, "bootargs", barg_buf, BARG_LEN); - fetched = 1; - return barg_buf; + if (bootstr_valid) return bootstr_buf; + prom_getstring(prom_chosen_node, "bootargs", bootstr_buf, BARG_LEN); + bootstr_valid = 1; + return bootstr_buf; } diff --git a/arch/sparc64/prom/misc.c b/arch/sparc64/prom/misc.c index fe9bf9c6b057..9c11d75b3378 100644 --- a/arch/sparc64/prom/misc.c +++ b/arch/sparc64/prom/misc.c @@ -1,4 +1,4 @@ -/* $Id: misc.c,v 1.6 1997/04/10 05:13:05 davem Exp $ +/* $Id: misc.c,v 1.7 1997/07/05 09:52:51 davem Exp $ * misc.c: Miscellaneous prom functions that don't belong * anywhere else. * @@ -6,6 +6,7 @@ * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ +#include #include #include #include @@ -45,8 +46,8 @@ void prom_cmdline(void) { extern void kernel_enter_debugger(void); - extern void install_obp_ticker(void); - extern void install_linux_ticker(void); + /* extern void install_obp_ticker(void); */ + /* extern void install_linux_ticker(void); */ unsigned long flags; /* kernel_enter_debugger(); */ diff --git a/arch/sparc64/prom/p1275.c b/arch/sparc64/prom/p1275.c index 3eb0311dfcc8..18de40debe77 100644 --- a/arch/sparc64/prom/p1275.c +++ b/arch/sparc64/prom/p1275.c @@ -1,4 +1,4 @@ -/* $Id: p1275.c,v 1.8 1997/04/03 09:29:21 davem Exp $ +/* $Id: p1275.c,v 1.10 1997/06/27 04:18:30 davem Exp $ * p1275.c: Sun IEEE 1275 PROM low level interface routines * * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -14,50 +14,42 @@ #include #include -/* If you change layout of this structure, please change the prom_doit - function below as well. */ -typedef struct { - unsigned prom_doit_code [24]; /* 0x8000 */ - long prom_sync_routine; /* 0x8060 */ - void (*prom_cif_handler)(long *); /* 0x8068 */ - unsigned long prom_cif_stack; /* 0x8070 */ - unsigned long prom_args [23]; /* 0x8078 */ - char prom_buffer [7888]; -} at0x8000; +struct { + long prom_sync_routine; /* 0x00 */ + void (*prom_cif_handler)(long *); /* 0x08 */ + unsigned long prom_cif_stack; /* 0x10 */ + unsigned long prom_args [23]; /* 0x18 */ + char prom_buffer [3000]; +} p1275buf; -static void (*prom_do_it)(void); - -void prom_cif_interface (void) __attribute__ ((__section__ (".p1275"))); - -/* At most 14 insns */ void prom_cif_interface (void) { __asm__ __volatile__ (" - sethi %%hi(0x8000), %%o0 - ldx [%%o0 + 0x070], %%o1 ! prom_cif_stack + mov %0, %%o0 + ldx [%%o0 + 0x010], %%o1 ! prom_cif_stack save %%o1, -0xc0, %%sp - ldx [%%i0 + 0x068], %%l2 ! prom_cif_handler + ldx [%%i0 + 0x008], %%l2 ! prom_cif_handler rdpr %%pstate, %%l4 mov %%g4, %%l0 mov %%g6, %%l1 - wrpr %%l4, %0, %%pstate ! turn on address masking + wrpr %%l4, %1, %%pstate ! turn on address masking call %%l2 - or %%i0, 0x078, %%o0 ! prom_args + add %%i0, 0x018, %%o0 ! prom_args wrpr %%l4, 0, %%pstate ! put pstate back mov %%l0, %%g4 ret restore %%l1, 0, %%g6 save %%sp, -0xc0, %%sp ! If you change the offset of the save rdpr %%pstate, %%l4 ! here, please change the 0x8038 - andn %%l4, %0, %%l3 ! constant below as well + andn %%l4, %1, %%l3 ! constant below as well wrpr %%l3, %%pstate - ldx [%%o0 + 0x060], %%l2 + ldx [%%o0 + 0x000], %%l2 call %%l2 nop wrpr %%l4, 0, %%pstate ret restore - " : : "i" (PSTATE_AM)); + " : : "r" (&p1275buf), "i" (0 /* PSTATE_AM */)); } long p1275_cmd (char *service, long fmt, ...) @@ -68,61 +60,60 @@ long p1275_cmd (char *service, long fmt, ...) va_list list; long attrs, x; long ctx = 0; - at0x8000 *low = (at0x8000 *)(0x8000); - p = low->prom_buffer; + p = p1275buf.prom_buffer; save_and_cli(flags); ctx = spitfire_get_primary_context (); if (ctx) { flushw_user (); spitfire_set_primary_context (0); } - low->prom_args[0] = (unsigned long)p; /* service */ + p1275buf.prom_args[0] = (unsigned long)p; /* service */ strcpy (p, service); p = (char *)(((long)(strchr (p, 0) + 8)) & ~7); - low->prom_args[1] = nargs = (fmt & 0x0f); /* nargs */ - low->prom_args[2] = nrets = ((fmt & 0xf0) >> 4); /* nrets */ + p1275buf.prom_args[1] = nargs = (fmt & 0x0f); /* nargs */ + p1275buf.prom_args[2] = nrets = ((fmt & 0xf0) >> 4); /* nrets */ attrs = fmt >> 8; va_start(list, fmt); for (i = 0; i < nargs; i++, attrs >>= 3) { switch (attrs & 0x7) { case P1275_ARG_NUMBER: - low->prom_args[i + 3] = (unsigned)va_arg(list, long); break; + p1275buf.prom_args[i + 3] = (unsigned)va_arg(list, long); break; case P1275_ARG_IN_STRING: strcpy (p, va_arg(list, char *)); - low->prom_args[i + 3] = (unsigned long)p; + p1275buf.prom_args[i + 3] = (unsigned long)p; p = (char *)(((long)(strchr (p, 0) + 8)) & ~7); break; case P1275_ARG_OUT_BUF: (void) va_arg(list, char *); - low->prom_args[i + 3] = (unsigned long)p; + p1275buf.prom_args[i + 3] = (unsigned long)p; x = va_arg(list, long); i++; attrs >>= 3; p = (char *)(((long)(p + (int)x + 7)) & ~7); - low->prom_args[i + 3] = x; + p1275buf.prom_args[i + 3] = x; break; case P1275_ARG_IN_BUF: q = va_arg(list, char *); - low->prom_args[i + 3] = (unsigned long)p; + p1275buf.prom_args[i + 3] = (unsigned long)p; x = va_arg(list, long); i++; attrs >>= 3; memcpy (p, q, (int)x); p = (char *)(((long)(p + (int)x + 7)) & ~7); - low->prom_args[i + 3] = x; + p1275buf.prom_args[i + 3] = x; break; case P1275_ARG_OUT_32B: (void) va_arg(list, char *); - low->prom_args[i + 3] = (unsigned long)p; + p1275buf.prom_args[i + 3] = (unsigned long)p; p += 32; break; case P1275_ARG_IN_FUNCTION: - low->prom_args[i + 3] = 0x8038; - low->prom_sync_routine = va_arg(list, long); break; + p1275buf.prom_args[i + 3] = (unsigned long)prom_cif_interface + 0x38; + p1275buf.prom_sync_routine = va_arg(list, long); break; } } va_end(list); - - (*prom_do_it)(); + + prom_cif_interface(); attrs = fmt >> 8; va_start(list, fmt); @@ -142,17 +133,17 @@ long p1275_cmd (char *service, long fmt, ...) case P1275_ARG_OUT_BUF: p = va_arg(list, char *); x = va_arg(list, long); - memcpy (p, (char *)(low->prom_args[i + 3]), (int)x); + memcpy (p, (char *)(p1275buf.prom_args[i + 3]), (int)x); i++; attrs >>= 3; break; case P1275_ARG_OUT_32B: p = va_arg(list, char *); - memcpy (p, (char *)(low->prom_args[i + 3]), 32); + memcpy (p, (char *)(p1275buf.prom_args[i + 3]), 32); break; } } va_end(list); - x = low->prom_args [nargs + 3]; + x = p1275buf.prom_args [nargs + 3]; if (ctx) spitfire_set_primary_context (ctx); @@ -162,9 +153,6 @@ long p1275_cmd (char *service, long fmt, ...) void prom_cif_init(void *cif_handler, void *cif_stack) { - at0x8000 *low = (at0x8000 *)(0x8000); - - low->prom_cif_handler = (void (*)(long *))cif_handler; - low->prom_cif_stack = (unsigned long)cif_stack; - prom_do_it = (void (*)(void))(0x8000); + p1275buf.prom_cif_handler = (void (*)(long *))cif_handler; + p1275buf.prom_cif_stack = (unsigned long)cif_stack; } diff --git a/arch/sparc64/vmlinux.lds b/arch/sparc64/vmlinux.lds index d2d0cac34138..eac8314cac4c 100644 --- a/arch/sparc64/vmlinux.lds +++ b/arch/sparc64/vmlinux.lds @@ -5,10 +5,10 @@ ENTRY(_start) SECTIONS { - empty_zero_page = 0xfffff80000000000; - swapper_pg_dir = 0xfffff80000002000; + empty_zero_page = 0x0000000000400000; + swapper_pg_dir = 0x0000000000402000; . = 0x4000; - .text 0xfffff80000004000 : + .text 0x0000000000404000 : { *(.text) *(.gnu.warning) @@ -40,12 +40,6 @@ SECTIONS .data.init : { *(.data.init) } . = ALIGN(8192); __init_end = .; - __p1275_loc = .; - .p1275 : - { - *(.p1275) - . = ALIGN(8192); - } __bss_start = .; .sbss : { *(.sbss) *(.scommon) } .bss : diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index fdc01f3d272f..a1fc2927de98 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -2896,6 +2896,8 @@ static void raw_cmd_done(int flag) raw_cmd->flags |= FD_RAW_HARDFAILURE; } else { raw_cmd->reply_count = inr; + if(raw_cmd->reply_count > MAX_REPLIES) + raw_cmd->reply_count=0; for (i=0; i< raw_cmd->reply_count; i++) raw_cmd->reply[i] = reply_buffer[i]; diff --git a/drivers/char/psaux.c b/drivers/char/psaux.c index edf88356febb..0be35e1ad7bd 100644 --- a/drivers/char/psaux.c +++ b/drivers/char/psaux.c @@ -628,7 +628,10 @@ __initfunc(int psaux_init(void)) poll_aux_status_nosleep(); #endif /* INITIALIZE_DEVICE */ outb_p(KBD_CCMD_MOUSE_DISABLE, KBD_CNTL_REG); /* Disable Aux device */ - aux_write_dev_nosleep(AUX_INTS_OFF); /* Disable controller interrupts */ + poll_aux_status_nosleep(); + outb_p(KBD_CCMD_WRITE_MODE, KBD_CNTL_REG); + poll_aux_status_nosleep(); + outb_p(AUX_INTS_OFF, KBD_DATA_REG); } return 0; } diff --git a/drivers/char/pty.c b/drivers/char/pty.c index 8f101539726c..929bb2f857a2 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c @@ -26,20 +26,6 @@ struct pty_struct { #define PTY_MAGIC 0x5001 -#define PTY_BUF_SIZE PAGE_SIZE/2 - -/* - * tmp_buf is used as a temporary buffer by pty_write. We need to - * lock it in case the copy_from_user blocks while swapping in a page, - * and some other program tries to do a pty write at the same time. - * Since the lock will only come under contention when the system is - * swapping and available memory is low, it makes sense to share one - * buffer across all the PTY's, since it significantly saves memory if - * large numbers of PTY's are open. - */ -static unsigned char *tmp_buf; -static struct semaphore tmp_buf_sem = MUTEX; - static struct tty_driver pty_driver, pty_slave_driver; static struct tty_driver old_pty_driver, old_pty_slave_driver; static int pty_refcount; @@ -104,37 +90,51 @@ static void pty_unthrottle(struct tty_struct * tty) set_bit(TTY_THROTTLED, &tty->flags); } +/* + * WSH 05/24/97: modified to + * (1) use space in tty->flip instead of a shared temp buffer + * The flip buffers aren't being used for a pty, so there's lots + * of space available. The buffer is protected by a per-pty + * semaphore that should almost never come under contention. + * (2) avoid redundant copying for cases where count >> receive_room + * N.B. Calls from user space may now return an error code instead of + * a count. + */ static int pty_write(struct tty_struct * tty, int from_user, const unsigned char *buf, int count) { struct tty_struct *to = tty->link; - int c=0, n, r; + int c=0, n; char *temp_buffer; if (!to || tty->stopped) return 0; - + if (from_user) { - down(&tmp_buf_sem); - temp_buffer = tmp_buf + - ((tty->driver.subtype-1) * PTY_BUF_SIZE); + down(&tty->flip.pty_sem); + temp_buffer = &tty->flip.char_buf[0]; while (count > 0) { - n = MIN(count, PTY_BUF_SIZE); + /* check space so we don't copy needlessly */ + n = MIN(count, to->ldisc.receive_room(to)); + if (!n) break; + + n = MIN(n, PTY_BUF_SIZE); n -= copy_from_user(temp_buffer, buf, n); if (!n) { if (!c) c = -EFAULT; break; } - r = to->ldisc.receive_room(to); - if (r <= 0) - break; - n = MIN(n, r); - to->ldisc.receive_buf(to, temp_buffer, 0, n); - buf += n; c+= n; + + /* check again in case the buffer filled up */ + n = MIN(n, to->ldisc.receive_room(to)); + if (!n) break; + buf += n; + c += n; count -= n; + to->ldisc.receive_buf(to, temp_buffer, 0, n); } - up(&tmp_buf_sem); + up(&tty->flip.pty_sem); } else { c = MIN(count, to->ldisc.receive_room(to)); to->ldisc.receive_buf(to, buf, 0, c); @@ -153,14 +153,42 @@ static int pty_write_room(struct tty_struct *tty) return to->ldisc.receive_room(to); } +/* + * WSH 05/24/97: Modified for asymmetric MASTER/SLAVE behavior + * The chars_in_buffer() value is used by the ldisc select() function + * to hold off writing when chars_in_buffer > WAKEUP_CHARS (== 256). + * The pty driver chars_in_buffer() Master/Slave must behave differently: + * + * The Master side needs to allow typed-ahead commands to accumulate + * while being canonicalized, so we report "our buffer" as empty until + * some threshold is reached, and then report the count. (Any count > + * WAKEUP_CHARS is regarded by select() as "full".) To avoid deadlock + * the count returned must be 0 if no canonical data is available to be + * read. (The N_TTY ldisc.chars_in_buffer now knows this.) + * + * The Slave side passes all characters in raw mode to the Master side's + * buffer where they can be read immediately, so in this case we can + * return the true count in the buffer. + */ static int pty_chars_in_buffer(struct tty_struct *tty) { struct tty_struct *to = tty->link; + int count; if (!to || !to->ldisc.chars_in_buffer) return 0; - return to->ldisc.chars_in_buffer(to); + /* The ldisc must report 0 if no characters available to be read */ + count = to->ldisc.chars_in_buffer(to); + + if (tty->driver.subtype == PTY_TYPE_SLAVE) return count; + + /* Master side driver ... if the other side's read buffer is less than + * half full, return 0 to allow writers to proceed; otherwise return + * the count. This leaves a comfortable margin to avoid overflow, + * and still allows half a buffer's worth of typed-ahead commands. + */ + return ((count < N_TTY_BUF_SIZE/2) ? 0 : count); } static void pty_flush_buffer(struct tty_struct *tty) @@ -194,17 +222,6 @@ static int pty_open(struct tty_struct *tty, struct file * filp) pty = pty_state + line; tty->driver_data = pty; - if (!tmp_buf) { - unsigned long page = __get_free_page(GFP_KERNEL); - if (!tmp_buf) { - retval = -ENOMEM; - if (!page) - goto out; - tmp_buf = (unsigned char *) page; - memset((void *) page, 0, PAGE_SIZE); - } else - free_page(page); - } retval = -EIO; if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) goto out; @@ -288,8 +305,6 @@ __initfunc(int pty_init(void)) old_pty_slave_driver.num = (NR_PTYS > 64) ? 64 : NR_PTYS; old_pty_slave_driver.other = &old_pty_driver; - tmp_buf = 0; - if (tty_register_driver(&pty_driver)) panic("Couldn't register pty driver"); if (tty_register_driver(&pty_slave_driver)) diff --git a/drivers/char/random.c b/drivers/char/random.c index 2e905fadd663..4cccb8faf769 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -227,6 +227,7 @@ */ #include +#include #include #include #include diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c index a4fb2cb175f2..d7c09833c3c7 100644 --- a/drivers/char/rtc.c +++ b/drivers/char/rtc.c @@ -152,7 +152,7 @@ static long rtc_read(struct inode *inode, struct file *file, char *buf, unsigned long count) { struct wait_queue wait = { current, NULL }; - int retval; + int retval = 0; if (count < sizeof(unsigned long)) return -EINVAL; diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c index e2044c086590..cd44d36aaa5c 100644 --- a/drivers/char/sysrq.c +++ b/drivers/char/sysrq.c @@ -1,6 +1,6 @@ /* -*- linux-c -*- * - * $Id: sysrq.c,v 1.2 1997/05/31 18:33:11 mj Exp $ + * $Id: sysrq.c,v 1.3 1997/06/18 09:42:12 mj Exp $ * * Linux Magic System Request Key Hacks * @@ -112,7 +112,7 @@ void handle_sysrq(int key, struct pt_regs *pt_regs, show_mem(); break; case 2 ... 11: /* 0-9 -- set console logging level */ - key -= 2; + key--; if (key == 10) key = 0; orig_log_level = key; diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 7678e75e9af5..be5e75fe8bed 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -1654,6 +1654,7 @@ static void initialize_tty_struct(struct tty_struct *tty) tty->flip.flag_buf_ptr = tty->flip.flag_buf; tty->flip.tqueue.routine = flush_to_ldisc; tty->flip.tqueue.data = tty; + tty->flip.pty_sem = MUTEX; } /* diff --git a/drivers/char/vc_screen.c b/drivers/char/vc_screen.c index 603250b81ccf..c0d7440c3f07 100644 --- a/drivers/char/vc_screen.c +++ b/drivers/char/vc_screen.c @@ -237,6 +237,11 @@ vcs_write(struct inode *inode, struct file *file, const char *buf, unsigned long func_scr_writew((func_scr_readw(org) & 0xff00) | c, org); } } +#ifdef CONFIG_FB_CONSOLE + if (currcons == fg_console) + /* Horribly inefficient if count < screen size. */ + update_screen(currcons); +#endif written = buf - buf0; file->f_pos += written; RETURN( written ); diff --git a/drivers/isdn/avmb1/capiutil.c b/drivers/isdn/avmb1/capiutil.c index b3c25cd2af9f..9eb60afed999 100644 --- a/drivers/isdn/avmb1/capiutil.c +++ b/drivers/isdn/avmb1/capiutil.c @@ -26,6 +26,7 @@ * */ #include +#include #include #include #include diff --git a/drivers/net/Config.in b/drivers/net/Config.in index 1cc7b5bae850..7188ee0dab16 100644 --- a/drivers/net/Config.in +++ b/drivers/net/Config.in @@ -151,8 +151,12 @@ if [ "$CONFIG_NET_RADIO" != "n" ]; then bool 'Soundmodem support for 2400 baud AFSK modulation (8MHz crystal)' CONFIG_SOUNDMODEM_AFSK2400_8 bool 'Soundmodem support for 4800 baud HAPN-1 modulation' CONFIG_SOUNDMODEM_HAPN4800 bool 'Soundmodem support for 9600 baud FSK G3RUH modulation' CONFIG_SOUNDMODEM_FSK9600 - #bool 'Soundmodem support for 2666 baud AFSK modulation' CONFIG_SOUNDMODEM_AFSK2666 - #bool 'Soundmodem support for 4800 baud PSK modulation' CONFIG_SOUNDMODEM_PSK4800 + if [ -f drivers/net/soundmodem/sm_afsk2666.c ]; then + bool 'Soundmodem support for 2666 baud AFSK modulation' CONFIG_SOUNDMODEM_AFSK2666 + fi + if [ -f drivers/net/soundmodem/sm_psk4800.c ]; then + bool 'Soundmodem support for 4800 baud PSK modulation' CONFIG_SOUNDMODEM_PSK4800 + fi fi fi tristate 'STRIP (Metricom starmode radio IP)' CONFIG_STRIP diff --git a/drivers/net/cops.c b/drivers/net/cops.c index a7e7c1303d0a..fb353dc61887 100644 --- a/drivers/net/cops.c +++ b/drivers/net/cops.c @@ -45,6 +45,7 @@ static const char *version = #include #endif +#include #include #include #include diff --git a/drivers/net/cops_ffdrv.h b/drivers/net/cops_ffdrv.h index 12a488cbc269..d3e337afc8d9 100644 --- a/drivers/net/cops_ffdrv.h +++ b/drivers/net/cops_ffdrv.h @@ -24,6 +24,7 @@ * - Jay Schulist */ +#include #ifdef CONFIG_COPS_DAYNA diff --git a/drivers/net/cops_ltdrv.h b/drivers/net/cops_ltdrv.h index 6ec334031e03..33f3d9a06932 100644 --- a/drivers/net/cops_ltdrv.h +++ b/drivers/net/cops_ltdrv.h @@ -23,6 +23,7 @@ * - Jay Schulist */ +#include #ifdef CONFIG_COPS_TANGENT diff --git a/drivers/pnp/parport_procfs.c b/drivers/pnp/parport_procfs.c index 77574991b9ca..46ff5b5d9b12 100644 --- a/drivers/pnp/parport_procfs.c +++ b/drivers/pnp/parport_procfs.c @@ -13,7 +13,6 @@ #include #include -#include #include #include #include diff --git a/drivers/sbus/char/cgsix.c b/drivers/sbus/char/cgsix.c index 8c0764acb239..2ced97580225 100644 --- a/drivers/sbus/char/cgsix.c +++ b/drivers/sbus/char/cgsix.c @@ -1,4 +1,4 @@ -/* $Id: cgsix.c,v 1.32 1997/06/14 15:26:08 davem Exp $ +/* $Id: cgsix.c,v 1.33 1997/07/01 09:12:05 jj Exp $ * cgsix.c: cgsix frame buffer driver * * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) @@ -234,11 +234,32 @@ cg6_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, { uint size, page, r, map_size; unsigned long map_offset = 0; - + size = vma->vm_end - vma->vm_start; if (vma->vm_offset & ~PAGE_MASK) return -ENXIO; +#ifdef __sparc_v9__ + /* Try to align RAM */ +#define ALIGNMENT 0x80000 + map_offset = vma->vm_offset + size; + if (vma->vm_offset <= CG6_RAM && map_offset >= CG6_RAM + fb->type.fb_size) { + struct vm_area_struct *vmm = find_vma(current->mm, vma->vm_start); + int alignment = ALIGNMENT - ((vma->vm_start + CG6_RAM - vma->vm_offset) & (ALIGNMENT - 1)); + int sz = 0, fbsz; + + if (alignment == ALIGNMENT) alignment = 0; + fbsz = ((fb->type.fb_size + ALIGNMENT - 1) & ~(ALIGNMENT - 1)); + if (map_offset < CG6_RAM + fbsz) + sz = fbsz - map_offset + CG6_RAM; + if ((sz || alignment) && (!vmm || vmm->vm_start >= vma->vm_end + sz + alignment)) { + vma->vm_start += alignment; + vma->vm_end += alignment + sz; + } + } +#undef ALIGNMENT +#endif + /* To stop the swapper from even considering these pages */ vma->vm_flags |= FB_MMAP_VM_FLAGS; diff --git a/drivers/sbus/char/openprom.c b/drivers/sbus/char/openprom.c index 548abb601079..75b950071544 100644 --- a/drivers/sbus/char/openprom.c +++ b/drivers/sbus/char/openprom.c @@ -137,6 +137,7 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file, struct openpromio *opp; unsigned long flags; int bufsize, len, error = 0; + extern char saved_command_line[]; if (cmd == OPROMSETOPT) bufsize = getstrings((void *)arg, &opp); @@ -172,7 +173,7 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file, case OPROMNXTOPT: case OPROMNXTPROP: save_and_cli(flags); - buf = prom_nextprop(node, opp->oprom_array); + buf = prom_nextprop(node, opp->oprom_array, buffer); restore_flags(flags); len = strlen(buf); @@ -229,9 +230,7 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file, break; case OPROMGETBOOTARGS: - save_and_cli(flags); - buf = prom_getbootargs(); - restore_flags(flags); + buf = saved_command_line; len = strlen(buf); @@ -315,6 +314,7 @@ static int openprom_bsd_ioctl(struct inode * inode, struct file * file, unsigned long flags; int error, node, len; char *str, *tmp; + char buffer[64]; switch (cmd) { case OPIOCGET: @@ -378,7 +378,7 @@ static int openprom_bsd_ioctl(struct inode * inode, struct file * file, return error; save_and_cli(flags); - tmp = prom_nextprop(op.op_nodeid,str); + tmp = prom_nextprop(op.op_nodeid,str,buffer); restore_flags(flags); if (tmp) { diff --git a/drivers/sbus/char/sunfb.c b/drivers/sbus/char/sunfb.c index d0a5db4e5763..227660457b05 100644 --- a/drivers/sbus/char/sunfb.c +++ b/drivers/sbus/char/sunfb.c @@ -1,4 +1,4 @@ -/* $Id: sunfb.c,v 1.24 1997/06/06 10:56:24 jj Exp $ +/* $Id: sunfb.c,v 1.25 1997/07/01 09:12:06 jj Exp $ * sunfb.c: Sun generic frame buffer support. * * Copyright (C) 1995, 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) @@ -276,6 +276,7 @@ fb_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma) v = (*fb->mmap)(inode, file, vma, fb->base, fb); if (v) return v; + vma->vm_flags |= VM_IO; if (!fb->mmaped) { fb->mmaped = 1; if (!minor) { diff --git a/drivers/sbus/char/sunkbd.c b/drivers/sbus/char/sunkbd.c index 1ddaaecd42aa..54b09cd7604d 100644 --- a/drivers/sbus/char/sunkbd.c +++ b/drivers/sbus/char/sunkbd.c @@ -419,27 +419,27 @@ void sunkbd_inchar(unsigned char ch, struct pt_regs *regs) if(ch == SKBD_RESET) { kbd_reset_pending = 1; - return; + goto out; } if(ch == SKBD_LYOUT) { kbd_layout_pending = 1; - return; + goto out; } if(kbd_reset_pending) { sunkbd_type = ch; kbd_reset_pending = 0; if(ch == SUNKBD_TYPE4) send_cmd(SKBDCMD_GLAYOUT); - return; + goto out; } else if(kbd_layout_pending) { sunkbd_layout = ch; kbd_layout_pending = 0; - return; + goto out; } else if(ch == SKBD_ALLUP) { del_timer (&auto_repeat_timer); memset(key_down, 0, sizeof(key_down)); compute_shiftstate(); - return; + goto out; } #ifdef SKBD_DEBUG if(ch == 0x7f) @@ -456,11 +456,11 @@ void sunkbd_inchar(unsigned char ch, struct pt_regs *regs) } else { keycode = ch; } - add_keyboard_randomness(keycode); - mark_bh(KEYBOARD_BH); do_poke_blanked_console = 1; mark_bh(CONSOLE_BH); + add_keyboard_randomness(keycode); + kbd = kbd_table + fg_console; tty = ttytab[fg_console]; if((raw_mode = (kbd->kbdmode == VC_RAW))) { @@ -491,11 +491,11 @@ void sunkbd_inchar(unsigned char ch, struct pt_regs *regs) } if(raw_mode) - return; + goto out; if(kbd->kbdmode == VC_MEDIUMRAW) { put_queue(keycode + up_flag); - return; + goto out; } /* @@ -545,6 +545,8 @@ void sunkbd_inchar(unsigned char ch, struct pt_regs *regs) compute_shiftstate(); } } +out: + mark_bh(KEYBOARD_BH); } static void put_queue(int ch) diff --git a/drivers/sbus/char/sunmouse.c b/drivers/sbus/char/sunmouse.c index 371fb300404d..789a332c0353 100644 --- a/drivers/sbus/char/sunmouse.c +++ b/drivers/sbus/char/sunmouse.c @@ -171,7 +171,7 @@ void mouse_baud_detection(unsigned char c) ctr = 0; wait_for_synchron = 1; if(mouse_baud_changing == 1) { - printk("sunmouse: Successfully adjusted to %d baud.\n", + printk(KERN_DEBUG "sunmouse: Successfully adjusted to %d baud.\n", mouse_baud); mouse_baud_changing = 0; } diff --git a/drivers/sbus/char/sunserial.c b/drivers/sbus/char/sunserial.c index e89384a3a97b..5331f1bdebb5 100644 --- a/drivers/sbus/char/sunserial.c +++ b/drivers/sbus/char/sunserial.c @@ -1,4 +1,4 @@ -/* $Id: sunserial.c,v 1.42 1997/05/26 20:10:20 davem Exp $ +/* $Id: sunserial.c,v 1.43 1997/07/05 09:53:23 davem Exp $ * serial.c: Serial port driver for the Sparc. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -348,10 +348,12 @@ static void batten_down_hatches(void) */ printk("\n"); flush_user_windows(); +#ifndef __sparc_v9__ if((((unsigned long)linux_dbvec)>=DEBUG_FIRSTVADDR) && (((unsigned long)linux_dbvec)<=DEBUG_LASTVADDR)) sp_enter_debugger(); else +#endif prom_cmdline(); /* XXX We want to notify the keyboard driver that all @@ -396,7 +398,9 @@ static _INLINE_ void rs_sched_event(struct sun_serial *info, mark_bh(SERIAL_BH); } +#ifndef __sparc_v9__ extern void breakpoint(void); /* For the KGDB frame character */ +#endif static _INLINE_ void receive_chars(struct sun_serial *info, struct pt_regs *regs) { @@ -453,6 +457,7 @@ static _INLINE_ void receive_chars(struct sun_serial *info, struct pt_regs *regs /* It is a 'keyboard interrupt' ;-) */ wake_up(&keypress_wait); } +#ifndef __sparc_v9__ /* Look for kgdb 'stop' character, consult the gdb * documentation for remote target debugging and * arch/sparc/kernel/sparc-stub.c to see how all this works. @@ -461,7 +466,7 @@ static _INLINE_ void receive_chars(struct sun_serial *info, struct pt_regs *regs breakpoint(); return; } - +#endif if(!tty) return; @@ -1867,7 +1872,7 @@ int rs_open(struct tty_struct *tty, struct file * filp) static void show_serial_version(void) { - char *revision = "$Revision: 1.42 $"; + char *revision = "$Revision: 1.43 $"; char *version, *p; version = strchr(revision, ' '); diff --git a/drivers/scsi/BusLogic.h b/drivers/scsi/BusLogic.h index 7cd6ba4361ba..10d78a3ae0ac 100644 --- a/drivers/scsi/BusLogic.h +++ b/drivers/scsi/BusLogic.h @@ -27,6 +27,8 @@ */ +#include + /* Define types for some of the structures that interface with the rest of the Linux Kernel and SCSI Subsystem. diff --git a/drivers/scsi/amiga7xx.c b/drivers/scsi/amiga7xx.c index 6edd01320855..f07d5e2cd839 100644 --- a/drivers/scsi/amiga7xx.c +++ b/drivers/scsi/amiga7xx.c @@ -36,7 +36,7 @@ struct proc_dir_entry proc_scsi_amiga7xx = { int amiga7xx_detect(Scsi_Host_Template *tpnt) { static unsigned char called = 0; - int key; + int key, clock; int num = 0; unsigned long address; long long options; @@ -59,8 +59,7 @@ int amiga7xx_detect(Scsi_Host_Template *tpnt) clock = 50000000; /* 50MHz SCSI Clock */ ncr53c7xx_init(tpnt, 0, 710, (u32)(unsigned char *)(address + 0x40000), - 0, IRQ_AMIGA_PORTS, DMA_NONE, - options, clock); + 0, IRQ_AMIGA_PORTS, DMA_NONE, options, clock); zorro_config_board(key, 0); num++; @@ -74,16 +73,16 @@ int amiga7xx_detect(Scsi_Host_Template *tpnt) clock = 50000000; /* 50MHz SCSI Clock */ - ncr53c7xx_init(tpnt, 0, 710, (u32)(unsigned char *)ZTWO_VADDR(0xDD0040), - 0, IRQ_AMIGA_PORTS, DMA_NONE, - options, clock); + ncr53c7xx_init(tpnt, 0, 710, + (u32)(unsigned char *)ZTWO_VADDR(0xDD0040), + 0, IRQ_AMIGA_PORTS, DMA_NONE, options, clock); num++; } #endif #ifdef CONFIG_A4091_SCSI while ( (key = zorro_find(MANUF_COMMODORE, PROD_A4091, 0, 0)) || - (key = zorro_find(MANUF_COMMODORE2, PROD_A4091_2, 0, 0)) ) + (key = zorro_find(MANUF_COMMODORE2, PROD_A4091_2, 0, 0)) ) { cd = zorro_get_board(key); address = (unsigned long)kernel_map((unsigned long)cd->cd_BoardAddr, diff --git a/drivers/scsi/ppa.c b/drivers/scsi/ppa.c index 581f280e8fbe..e9bdcee9b37d 100644 --- a/drivers/scsi/ppa.c +++ b/drivers/scsi/ppa.c @@ -115,6 +115,8 @@ * [Curtin-1-08-STABLE] */ +#include + /* The following #define is to avoid a clash with hosts.c */ #define PPA_CODE 1 #include "ppa.h" diff --git a/drivers/scsi/sr_ioctl.c b/drivers/scsi/sr_ioctl.c index a10e9545d4ec..bc870dfea727 100644 --- a/drivers/scsi/sr_ioctl.c +++ b/drivers/scsi/sr_ioctl.c @@ -1,3 +1,4 @@ +#include #include #include #include diff --git a/drivers/sound/dev_table.h b/drivers/sound/dev_table.h index 7013c3c8df3d..a7e4026e85a0 100644 --- a/drivers/sound/dev_table.h +++ b/drivers/sound/dev_table.h @@ -15,6 +15,7 @@ #ifndef _DEV_TABLE_H_ #define _DEV_TABLE_H_ +#include /* * Sound card numbers 27 to 999. (1 to 26 are defined in soundcard.h) diff --git a/fs/affs/namei.c b/fs/affs/namei.c index 5ea64942536f..d9d48401d95d 100644 --- a/fs/affs/namei.c +++ b/fs/affs/namei.c @@ -566,12 +566,12 @@ start_up: old_bh = affs_find_entry(old_dir,old_name,old_len,&old_ino); if (!old_bh) goto end_rename; - old_inode = __iget(old_dir->i_sb,old_ino,0); + old_inode = __iget(old_dir->i_sb,old_ino); if (!old_inode) goto end_rename; new_bh = affs_find_entry(new_dir,new_name,new_len,&new_ino); if (new_bh) { - new_inode = __iget(new_dir->i_sb,new_ino,0); + new_inode = __iget(new_dir->i_sb,new_ino); if (!new_inode) { /* What does this mean? */ affs_brelse(new_bh); new_bh = NULL; diff --git a/fs/autofs/autofs_i.h b/fs/autofs/autofs_i.h index cc38577aa8cd..f2bb6c3392ee 100644 --- a/fs/autofs/autofs_i.h +++ b/fs/autofs/autofs_i.h @@ -69,10 +69,8 @@ static inline int copy_from_user(void *dst, void *src, unsigned long len) #define AUTOFS_HASH_SIZE 67 -typedef u32 autofs_hash_t; /* Type returned by autofs_hash() */ - struct autofs_dir_ent { - autofs_hash_t hash; + int hash; struct autofs_dir_ent *next; struct autofs_dir_ent **back; char *name; @@ -94,7 +92,7 @@ struct autofs_wait_queue { struct wait_queue *queue; struct autofs_wait_queue *next; /* We use the following to see what we are waiting for */ - autofs_hash_t hash; + int hash; int len; char *name; /* This is for status reporting upon return */ @@ -151,9 +149,8 @@ void autofs_check_waitlist_integrity(struct autofs_sb_info *,char *); /* Hash operations */ -autofs_hash_t autofs_hash(const char *,int); void autofs_initialize_hash(struct autofs_dirhash *); -struct autofs_dir_ent *autofs_hash_lookup(const struct autofs_dirhash *,autofs_hash_t,const char *,int); +struct autofs_dir_ent *autofs_hash_lookup(const struct autofs_dirhash *,struct qstr *); void autofs_hash_insert(struct autofs_dirhash *,struct autofs_dir_ent *); void autofs_hash_delete(struct autofs_dir_ent *); struct autofs_dir_ent *autofs_hash_enum(const struct autofs_dirhash *,off_t *); @@ -176,7 +173,7 @@ struct super_block *autofs_read_super(struct super_block *, void *,int); /* Queue management functions */ -int autofs_wait(struct autofs_sb_info *,autofs_hash_t,const char *,int); +int autofs_wait(struct autofs_sb_info *,struct qstr *); int autofs_wait_release(struct autofs_sb_info *,unsigned long,int); void autofs_catatonic_mode(struct autofs_sb_info *); diff --git a/fs/autofs/dir.c b/fs/autofs/dir.c index 0f529c900d4d..226b180e8d21 100644 --- a/fs/autofs/dir.c +++ b/fs/autofs/dir.c @@ -34,24 +34,11 @@ static int autofs_dir_readdir(struct inode *inode, struct file *filp, return 1; } -static int autofs_dir_lookup(struct inode *dir, const char *name, int len, - struct inode **result) +/* + * No entries except for "." and "..", both of which are handled by the VFS layer + */ +static int autofs_dir_lookup(struct inode *dir, struct qstr *str, struct inode **result) { - *result = dir; - if (!len) - return 0; - if (name[0] == '.') { - if (len == 1) - return 0; - if (name[1] == '.' && len == 2) { - /* Return the root directory */ - *result = iget(dir->i_sb,AUTOFS_ROOT_INO); - iput(dir); - return 0; - } - } - *result = NULL; - iput(dir); return -ENOENT; /* No other entries */ } diff --git a/fs/autofs/dirhash.c b/fs/autofs/dirhash.c index 90c18695a094..60a3c6933b23 100644 --- a/fs/autofs/dirhash.c +++ b/fs/autofs/dirhash.c @@ -48,35 +48,23 @@ struct autofs_dir_ent *autofs_expire(struct autofs_dirhash *dh, return (jiffies - ent->last_usage >= timeout) ? ent : NULL; } -/* Adapted from the Dragon Book, page 436 */ -/* This particular hashing algorithm requires autofs_hash_t == u32 */ -autofs_hash_t autofs_hash(const char *name, int len) -{ - autofs_hash_t h = 0; - while ( len-- ) { - h = (h << 4) + (unsigned char) (*name++); - h ^= ((h & 0xf0000000) >> 24); - } - return h; -} - void autofs_initialize_hash(struct autofs_dirhash *dh) { memset(&dh->h, 0, AUTOFS_HASH_SIZE*sizeof(struct autofs_dir_ent *)); dh->expiry_head.exp_next = dh->expiry_head.exp_prev = &dh->expiry_head; } -struct autofs_dir_ent *autofs_hash_lookup(const struct autofs_dirhash *dh, autofs_hash_t hash, const char *name, int len) +struct autofs_dir_ent *autofs_hash_lookup(const struct autofs_dirhash *dh, struct qstr *name) { struct autofs_dir_ent *dhn; - DPRINTK(("autofs_hash_lookup: hash = 0x%08x, name = ", hash)); - autofs_say(name,len); + DPRINTK(("autofs_hash_lookup: hash = 0x%08x, name = ", name->hash)); + autofs_say(name->name,name->len); - for ( dhn = dh->h[hash % AUTOFS_HASH_SIZE] ; dhn ; dhn = dhn->next ) { - if ( hash == dhn->hash && - len == dhn->len && - !memcmp(name, dhn->name, len) ) + for ( dhn = dh->h[(unsigned) name->hash % AUTOFS_HASH_SIZE] ; dhn ; dhn = dhn->next ) { + if ( name->hash == dhn->hash && + name->len == dhn->len && + !memcmp(name->name, dhn->name, name->len) ) break; } @@ -92,7 +80,7 @@ void autofs_hash_insert(struct autofs_dirhash *dh, struct autofs_dir_ent *ent) autofs_init_usage(dh,ent); - dhnp = &dh->h[ent->hash % AUTOFS_HASH_SIZE]; + dhnp = &dh->h[(unsigned) ent->hash % AUTOFS_HASH_SIZE]; ent->next = *dhnp; ent->back = dhnp; *dhnp = ent; diff --git a/fs/autofs/inode.c b/fs/autofs/inode.c index 20ca0907ad9c..5560989a11b1 100644 --- a/fs/autofs/inode.c +++ b/fs/autofs/inode.c @@ -163,7 +163,8 @@ struct super_block *autofs_read_super(struct super_block *s, void *data, s->s_magic = AUTOFS_SUPER_MAGIC; s->s_op = &autofs_sops; unlock_super(s); - if (!(s->s_mounted = iget(s, AUTOFS_ROOT_INO))) { + s->s_root = d_alloc_root(iget(s, AUTOFS_ROOT_INO), NULL); + if (!s->s_root) { s->s_dev = 0; kfree(sbi); printk("autofs: get root inode failed\n"); @@ -171,8 +172,8 @@ struct super_block *autofs_read_super(struct super_block *s, void *data, return NULL; } - if ( parse_options(data,&pipefd,&s->s_mounted->i_uid,&s->s_mounted->i_gid,&sbi->oz_pgrp,&minproto,&maxproto) ) { - iput(s->s_mounted); + if ( parse_options(data,&pipefd,&s->s_root->d_inode->i_uid,&s->s_root->d_inode->i_gid,&sbi->oz_pgrp,&minproto,&maxproto) ) { + d_delete(s->s_root); s->s_dev = 0; kfree(sbi); printk("autofs: called with bogus options\n"); @@ -181,7 +182,7 @@ struct super_block *autofs_read_super(struct super_block *s, void *data, } if ( minproto > AUTOFS_PROTO_VERSION || maxproto < AUTOFS_PROTO_VERSION ) { - iput(s->s_mounted); + d_delete(s->s_root); s->s_dev = 0; kfree(sbi); printk("autofs: kernel does not match daemon version\n"); @@ -198,7 +199,7 @@ struct super_block *autofs_read_super(struct super_block *s, void *data, } else { printk("autofs: could not open pipe file descriptor\n"); } - iput(s->s_mounted); + d_delete(s->s_root); s->s_dev = 0; kfree(sbi); MOD_DEC_USE_COUNT; @@ -244,8 +245,8 @@ static void autofs_read_inode(struct inode *inode) return; } - inode->i_uid = inode->i_sb->s_mounted->i_uid; - inode->i_gid = inode->i_sb->s_mounted->i_gid; + inode->i_uid = inode->i_sb->s_root->d_inode->i_uid; + inode->i_gid = inode->i_sb->s_root->d_inode->i_gid; if ( ino >= AUTOFS_FIRST_SYMLINK && ino < AUTOFS_FIRST_DIR_INO ) { /* Symlink inode - should be in symlink list */ diff --git a/fs/autofs/root.c b/fs/autofs/root.c index a615ede2903d..30c9e41b05ce 100644 --- a/fs/autofs/root.c +++ b/fs/autofs/root.c @@ -16,11 +16,11 @@ #include "autofs_i.h" static int autofs_root_readdir(struct inode *,struct file *,void *,filldir_t); -static int autofs_root_lookup(struct inode *,const char *,int,struct inode **); -static int autofs_root_symlink(struct inode *,const char *,int,const char *); -static int autofs_root_unlink(struct inode *,const char *,int); -static int autofs_root_rmdir(struct inode *,const char *,int); -static int autofs_root_mkdir(struct inode *,const char *,int,int); +static int autofs_root_lookup(struct inode *,struct qstr *,struct inode **); +static int autofs_root_symlink(struct inode *,struct dentry *,const char *); +static int autofs_root_unlink(struct inode *,struct dentry *); +static int autofs_root_rmdir(struct inode *,struct dentry *); +static int autofs_root_mkdir(struct inode *,struct dentry *,int); static int autofs_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); static struct file_operations autofs_root_operations = { @@ -48,6 +48,7 @@ struct inode_operations autofs_root_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -92,95 +93,70 @@ static int autofs_root_readdir(struct inode *inode, struct file *filp, return 0; } -static int autofs_root_lookup(struct inode *dir, const char *name, int len, - struct inode **result) +static int autofs_root_lookup(struct inode *dir, struct qstr *str, struct inode **result) { struct autofs_sb_info *sbi; struct autofs_dir_ent *ent; struct inode *res; - autofs_hash_t hash; int status, oz_mode; DPRINTK(("autofs_root_lookup: name = ")); - autofs_say(name,len); + autofs_say(str->name,str->len); *result = NULL; if (!dir) return -ENOENT; - if (!S_ISDIR(dir->i_mode)) { - iput(dir); + if (!S_ISDIR(dir->i_mode)) return -ENOTDIR; - } - - /* Handle special cases: . and ..; since this is a root directory, - they both point to the inode itself */ - *result = dir; - if (!len) - return 0; - if (name[0] == '.') { - if (len == 1) - return 0; - if (name[1] == '.' && len == 2) - return 0; - } *result = res = NULL; sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp; - hash = autofs_hash(name,len); - oz_mode = autofs_oz_mode(sbi); DPRINTK(("autofs_lookup: pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d\n", current->pid, current->pgrp, sbi->catatonic, oz_mode)); do { - while ( !(ent = autofs_hash_lookup(&sbi->dirhash,hash,name,len)) ) { + while ( !(ent = autofs_hash_lookup(&sbi->dirhash,str)) ) { DPRINTK(("lookup failed, pid = %u, pgrp = %u\n", current->pid, current->pgrp)); - if ( oz_mode ) { - iput(dir); + if ( oz_mode ) return -ENOENT; - } else { - status = autofs_wait(sbi,hash,name,len); - DPRINTK(("autofs_wait returned %d\n", status)); - if ( status ) { - iput(dir); - return status; - } - } + up(&dir->i_sem); + status = autofs_wait(sbi,str); + down(&dir->i_sem); + DPRINTK(("autofs_wait returned %d\n", status)); + if ( status ) + return status; } DPRINTK(("lookup successful, inode = %08x\n", (unsigned int)ent->ino)); if (!(res = iget(dir->i_sb,ent->ino))) { printk("autofs: iget returned null!\n"); - iput(dir); return -EACCES; } if ( !oz_mode && S_ISDIR(res->i_mode) && res->i_sb == dir->i_sb ) { /* Not a mount point yet, call 1-800-DAEMON */ DPRINTK(("autofs: waiting on non-mountpoint dir, inode = %lu, pid = %u, pgrp = %u\n", res->i_ino, current->pid, current->pgrp)); - iput(res); res = NULL; - status = autofs_wait(sbi,hash,name,len); - if ( status ) { - iput(dir); + up(&dir->i_sem); + status = autofs_wait(sbi,str); + down(&dir->i_sem); + if ( status ) return status; - } } } while(!res); autofs_update_usage(&sbi->dirhash,ent); *result = res; - iput(dir); return 0; } -static int autofs_root_symlink(struct inode *dir, const char *name, int len, const char *symname) +static int autofs_root_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { struct autofs_sb_info *sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp; struct autofs_dirhash *dh = &sbi->dirhash; - autofs_hash_t hash = autofs_hash(name,len); struct autofs_dir_ent *ent; unsigned int n; int slsize; @@ -189,70 +165,64 @@ static int autofs_root_symlink(struct inode *dir, const char *name, int len, con DPRINTK(("autofs_root_symlink: %s <- ", symname)); autofs_say(name,len); - if ( !autofs_oz_mode(sbi) ) { - iput(dir); + if ( !autofs_oz_mode(sbi) ) return -EPERM; - } - if ( autofs_hash_lookup(dh,hash,name,len) ) { - iput(dir); + + if ( autofs_hash_lookup(dh, &dentry->d_name) ) return -EEXIST; - } + n = find_first_zero_bit(sbi->symlink_bitmap,AUTOFS_MAX_SYMLINKS); - if ( n >= AUTOFS_MAX_SYMLINKS ) { - iput(dir); + if ( n >= AUTOFS_MAX_SYMLINKS ) return -ENOSPC; - } + set_bit(n,sbi->symlink_bitmap); sl = &sbi->symlink[n]; sl->len = strlen(symname); sl->data = kmalloc(slsize = sl->len+1, GFP_KERNEL); if ( !sl->data ) { clear_bit(n,sbi->symlink_bitmap); - iput(dir); return -ENOSPC; } + ent = kmalloc(sizeof(struct autofs_dir_ent), GFP_KERNEL); if ( !ent ) { kfree(sl->data); clear_bit(n,sbi->symlink_bitmap); - iput(dir); return -ENOSPC; } - ent->name = kmalloc(len, GFP_KERNEL); + + ent->name = kmalloc(dentry->d_name.len, GFP_KERNEL); if ( !ent->name ) { kfree(sl->data); kfree(ent); clear_bit(n,sbi->symlink_bitmap); - iput(dir); return -ENOSPC; } + memcpy(sl->data,symname,slsize); sl->mtime = CURRENT_TIME; ent->ino = AUTOFS_FIRST_SYMLINK + n; - ent->hash = hash; - memcpy(ent->name,name,ent->len = len); + ent->hash = dentry->d_name.hash; + memcpy(ent->name, dentry->d_name.name,ent->len = dentry->d_name.len); autofs_hash_insert(dh,ent); - iput(dir); + d_instantiate(dentry, iget(dir->i_sb,ent->ino), 0); return 0; } -static int autofs_root_unlink(struct inode *dir, const char *name, int len) +static int autofs_root_unlink(struct inode *dir, struct dentry *dentry) { struct autofs_sb_info *sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp; struct autofs_dirhash *dh = &sbi->dirhash; - autofs_hash_t hash = autofs_hash(name,len); struct autofs_dir_ent *ent; unsigned int n; - iput(dir); /* Nothing below can sleep */ - if ( !autofs_oz_mode(sbi) ) return -EPERM; - ent = autofs_hash_lookup(dh,hash,name,len); + ent = autofs_hash_lookup(dh, &dentry->d_name); if ( !ent ) return -ENOENT; @@ -263,75 +233,68 @@ static int autofs_root_unlink(struct inode *dir, const char *name, int len) autofs_hash_delete(ent); clear_bit(n,sbi->symlink_bitmap); kfree(sbi->symlink[n].data); + d_delete(dentry); return 0; } -static int autofs_root_rmdir(struct inode *dir, const char *name, int len) +static int autofs_root_rmdir(struct inode *dir, struct dentry *dentry) { struct autofs_sb_info *sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp; struct autofs_dirhash *dh = &sbi->dirhash; - autofs_hash_t hash = autofs_hash(name,len); struct autofs_dir_ent *ent; - if ( !autofs_oz_mode(sbi) ) { - iput(dir); + if ( !autofs_oz_mode(sbi) ) return -EPERM; - } - ent = autofs_hash_lookup(dh,hash,name,len); - if ( !ent ) { - iput(dir); + + ent = autofs_hash_lookup(dh, &dentry->d_name); + if ( !ent ) return -ENOENT; - } - if ( (unsigned int)ent->ino < AUTOFS_FIRST_DIR_INO ) { - iput(dir); + + if ( (unsigned int)ent->ino < AUTOFS_FIRST_DIR_INO ) return -ENOTDIR; /* Not a directory */ - } + autofs_hash_delete(ent); dir->i_nlink--; - iput(dir); + d_delete(dentry); return 0; } -static int autofs_root_mkdir(struct inode *dir, const char *name, int len, int mode) +static int autofs_root_mkdir(struct inode *dir, struct dentry *dentry, int mode) { struct autofs_sb_info *sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp; struct autofs_dirhash *dh = &sbi->dirhash; - autofs_hash_t hash = autofs_hash(name,len); struct autofs_dir_ent *ent; - if ( !autofs_oz_mode(sbi) ) { - iput(dir); + if ( !autofs_oz_mode(sbi) ) return -EPERM; - } - ent = autofs_hash_lookup(dh,hash,name,len); - if ( ent ) { - iput(dir); + + ent = autofs_hash_lookup(dh, &dentry->d_name); + if ( ent ) return -EEXIST; - } + if ( sbi->next_dir_ino < AUTOFS_FIRST_DIR_INO ) { printk("autofs: Out of inode numbers -- what the heck did you do??\n"); - iput(dir); return -ENOSPC; } + ent = kmalloc(sizeof(struct autofs_dir_ent), GFP_KERNEL); - if ( !ent ) { - iput(dir); + if ( !ent ) return -ENOSPC; - } - ent->name = kmalloc(len, GFP_KERNEL); + + ent->name = kmalloc(dentry->d_name.len, GFP_KERNEL); if ( !ent->name ) { kfree(ent); - iput(dir); return -ENOSPC; } - ent->hash = hash; - memcpy(ent->name, name, ent->len = len); + + ent->hash = dentry->d_name.hash; + memcpy(ent->name, dentry->d_name.name, ent->len = dentry->d_name.len); ent->ino = sbi->next_dir_ino++; autofs_hash_insert(dh,ent); dir->i_nlink++; - iput(dir); + d_instantiate(dentry, iget(dir->i_sb,ent->ino), D_DIR); return 0; } diff --git a/fs/autofs/symlink.c b/fs/autofs/symlink.c index d6ac82ed40ac..e3ff3d31bec0 100644 --- a/fs/autofs/symlink.c +++ b/fs/autofs/symlink.c @@ -19,18 +19,21 @@ static int autofs_readlink(struct inode *inode, char *buffer, int buflen) struct autofs_symlink *sl; int len; - if (!S_ISLNK(inode->i_mode)) { - iput(inode); - return -EINVAL; - } sl = (struct autofs_symlink *)inode->u.generic_ip; len = sl->len; if (len > buflen) len = buflen; copy_to_user(buffer,sl->data,len); - iput(inode); return len; } +static struct dentry * autofs_follow_link(struct inode *inode, struct dentry *base) +{ + struct autofs_symlink *sl; + + sl = (struct autofs_symlink *)inode->u.generic_ip; + return lookup_dentry(sl->data, base, 1); +} + struct inode_operations autofs_symlink_inode_operations = { NULL, /* file operations */ NULL, /* create */ @@ -43,6 +46,7 @@ struct inode_operations autofs_symlink_inode_operations = { NULL, /* mknod */ NULL, /* rename */ autofs_readlink, /* readlink */ + autofs_follow_link, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ diff --git a/fs/autofs/waitq.c b/fs/autofs/waitq.c index cbe270a5338e..ec222d61cd2a 100644 --- a/fs/autofs/waitq.c +++ b/fs/autofs/waitq.c @@ -90,15 +90,15 @@ static void autofs_notify_daemon(struct autofs_sb_info *sbi, struct autofs_wait_ autofs_catatonic_mode(sbi); } -int autofs_wait(struct autofs_sb_info *sbi, autofs_hash_t hash, const char *name, int len) +int autofs_wait(struct autofs_sb_info *sbi, struct qstr * name) { struct autofs_wait_queue *wq; int status; for ( wq = sbi->queues ; wq ; wq = wq->next ) { - if ( wq->hash == hash && - wq->len == len && - wq->name && !memcmp(wq->name,name,len) ) + if ( wq->hash == name->hash && + wq->len == name->len && + wq->name && !memcmp(wq->name,name->name,name->len) ) break; } @@ -108,17 +108,17 @@ int autofs_wait(struct autofs_sb_info *sbi, autofs_hash_t hash, const char *name if ( !wq ) return -ENOMEM; - wq->name = kmalloc(len,GFP_KERNEL); + wq->name = kmalloc(name->len,GFP_KERNEL); if ( !wq->name ) { kfree(wq); return -ENOMEM; } wq->wait_queue_token = autofs_next_wait_queue++; init_waitqueue(&wq->queue); - wq->hash = hash; - wq->len = len; + wq->hash = name->hash; + wq->len = name->len; wq->status = -EINTR; /* Status return if interrupted */ - memcpy(wq->name, name, len); + memcpy(wq->name, name->name, name->len); wq->next = sbi->queues; sbi->queues = wq; diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index a097e804ffba..f62a001916cd 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -111,23 +111,20 @@ create_elf_tables(char *p, int argc, int envc, { elf_caddr_t *argv; elf_caddr_t *envp; - elf_addr_t *sp; + elf_addr_t *sp, *csp; /* - * Force 16 byte alignment here for generality. + * Force 16 byte _final_ alignment here for generality. */ sp = (elf_addr_t *) (~15UL & (unsigned long) p); -#ifdef __sparc__ -{ - elf_addr_t *csp; csp = sp; csp -= exec ? DLINFO_ITEMS*2 : 2; csp -= envc+1; csp -= argc+1; - if (!(((unsigned long) csp) & 4)) - sp--; -} -#endif + csp -= (!ibcs ? 3 : 1); /* argc itself */ + if ((unsigned long)csp & 15UL) { + sp -= (16UL - ((unsigned long)csp & 15UL)) / sizeof(*sp); + } /* * Put the ELF interpreter info on the stack @@ -142,17 +139,17 @@ create_elf_tables(char *p, int argc, int envc, if (exec) { sp -= 11*2; - NEW_AUX_ENT (0, AT_PHDR, load_addr + exec->e_phoff); - NEW_AUX_ENT (1, AT_PHENT, sizeof (struct elf_phdr)); - NEW_AUX_ENT (2, AT_PHNUM, exec->e_phnum); - NEW_AUX_ENT (3, AT_PAGESZ, ELF_EXEC_PAGESIZE); - NEW_AUX_ENT (4, AT_BASE, interp_load_addr); - NEW_AUX_ENT (5, AT_FLAGS, 0); - NEW_AUX_ENT (6, AT_ENTRY, (elf_addr_t) exec->e_entry); - NEW_AUX_ENT (7, AT_UID, (elf_addr_t) current->uid); - NEW_AUX_ENT (8, AT_EUID, (elf_addr_t) current->euid); - NEW_AUX_ENT (9, AT_GID, (elf_addr_t) current->gid); - NEW_AUX_ENT (10, AT_EGID, (elf_addr_t) current->egid); + NEW_AUX_ENT (0, AT_PHDR, load_addr + exec->e_phoff); + NEW_AUX_ENT (1, AT_PHENT, sizeof (struct elf_phdr)); + NEW_AUX_ENT (2, AT_PHNUM, exec->e_phnum); + NEW_AUX_ENT (3, AT_PAGESZ, ELF_EXEC_PAGESIZE); + NEW_AUX_ENT (4, AT_BASE, interp_load_addr); + NEW_AUX_ENT (5, AT_FLAGS, 0); + NEW_AUX_ENT (6, AT_ENTRY, (elf_addr_t) exec->e_entry); + NEW_AUX_ENT (7, AT_UID, (elf_addr_t) current->uid); + NEW_AUX_ENT (8, AT_EUID, (elf_addr_t) current->euid); + NEW_AUX_ENT (9, AT_GID, (elf_addr_t) current->gid); + NEW_AUX_ENT (10, AT_EGID, (elf_addr_t) current->egid); } #undef NEW_AUX_ENT diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index 28dced394723..5ef090761bb5 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -7,9 +7,11 @@ * a specified wrapper. This should obsolete binfmt_java, binfmt_em86 and * binfmt_mz. * - * 25.4.97 first version - * [...] - * 19.5.97 cleanup + * 1997-04-25 first version + * [...] + * 1997-05-19 cleanup + * 1997-06-26 hpa: pass the real filename rather than argv[0] + * 1997-06-30 minor cleanup */ #include @@ -85,7 +87,6 @@ static void clear_entry(int id) *ep = e->next; entry_proc_cleanup(e); kfree(e); - MOD_DEC_USE_COUNT; } write_unlock(&entries_lock); } @@ -102,7 +103,6 @@ static void clear_entries(void) entries = entries->next; entry_proc_cleanup(e); kfree(e); - MOD_DEC_USE_COUNT; } write_unlock(&entries_lock); } @@ -161,7 +161,6 @@ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs) char *iname_addr = iname, *p; int retval, fmt_flags = 0; - MOD_INC_USE_COUNT; if (!enabled) { retval = -ENOEXEC; goto _ret; @@ -185,12 +184,11 @@ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs) /* Build args for interpreter */ if ((fmt_flags & ENTRY_STRIP_EXT) && - (p = strrchr(bprm->filename, '.'))) { + (p = strrchr(bprm->filename, '.'))) *p = '\0'; - remove_arg_zero(bprm); - bprm->p = copy_strings(1, &bprm->filename, bprm->page, bprm->p, 2); - bprm->argc++; - } + remove_arg_zero(bprm); + bprm->p = copy_strings(1, &bprm->filename, bprm->page, bprm->p, 2); + bprm->argc++; bprm->p = copy_strings(1, &iname_addr, bprm->page, bprm->p, 2); bprm->argc++; if (!bprm->p) { @@ -206,7 +204,6 @@ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs) if ((retval = prepare_binprm(bprm)) >= 0) retval = search_binary_handler(bprm, regs); _ret: - MOD_DEC_USE_COUNT; return retval; } @@ -265,18 +262,13 @@ static int proc_write_register(struct file *file, const char *buffer, struct binfmt_entry *e; int memsize, cnt = count - 1, err = 0; - MOD_INC_USE_COUNT; /* some sanity checks */ - if ((count < 11) || (count > 256)) { - err = -EINVAL; - goto _err; - } + if ((count < 11) || (count > 256)) + return -EINVAL; memsize = sizeof(struct binfmt_entry) + count; - if (!(e = (struct binfmt_entry *) kmalloc(memsize, GFP_USER))) { - err = -ENOMEM; - goto _err; - } + if (!(e = (struct binfmt_entry *) kmalloc(memsize, GFP_USER))) + return -ENOMEM; sp = buffer + 1; del = buffer[0]; @@ -313,8 +305,7 @@ static int proc_write_register(struct file *file, const char *buffer, !(e->proc_name) || !(e->interpreter) || entry_proc_setup(e)) { kfree(e); - err = -EINVAL; - goto _err; + return -EINVAL; } write_lock(&entries_lock); @@ -323,9 +314,6 @@ static int proc_write_register(struct file *file, const char *buffer, write_unlock(&entries_lock); return count; -_err: - MOD_DEC_USE_COUNT; - return err; } /* @@ -340,7 +328,6 @@ static int proc_read_status(char *page, char **start, off_t off, char *dp; int elen, i; - MOD_INC_USE_COUNT; #ifndef VERBOSE_STATUS if (data) { read_lock(&entries_lock); @@ -400,7 +387,6 @@ _out: *eof = (elen <= count) ? 1 : 0; *start = page + off; - MOD_DEC_USE_COUNT; return elen; } @@ -414,7 +400,6 @@ static int proc_write_status(struct file *file, const char *buffer, struct binfmt_entry *e; int res = count; - MOD_INC_USE_COUNT; if (((buffer[0] == '1') || (buffer[0] == '0')) && ((count == 1) || ((count == 2) && (buffer[1] == '\n')))) { if (data) { @@ -434,7 +419,6 @@ static int proc_write_status(struct file *file, const char *buffer, } else { res = -EINVAL; } - MOD_DEC_USE_COUNT; return res; } @@ -499,6 +483,7 @@ void cleanup_module(void) unregister_binfmt(&misc_format); remove_proc_entry("register", bm_dir); remove_proc_entry("status", bm_dir); + clear_entries(); remove_proc_entry("sys/fs/binfmt_misc", NULL); } #endif diff --git a/fs/buffer.c b/fs/buffer.c index 87b6470273d9..96f47d263058 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -1612,6 +1612,7 @@ asmlinkage int sync_old_buffers(void) next->b_count--; } } + run_task_queue(&tq_disk); #ifdef DEBUG if (ncount) printk("sync_old_buffers: %d dirty buffers not on dirty list\n", ncount); printk("Wrote %d/%d buffers\n", nwritten, ndirty); diff --git a/fs/dcache.c b/fs/dcache.c index dcf4c46a6ec3..3b9697c29534 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -5,67 +5,14 @@ * (C) 1997 Thomas Schoebel-Theuer */ -/* The new dcache is exclusively called from the VFS, not from - * the specific fs'es any more. Despite having the same name as in the - * old code, it has less to do with it. - * - * It serves many purposes: - * - * 1) Any inode that has been retrieved with lookup() and is in use - * (i_count>0), has access to its full absolute path name, by going - * to inode->i_dentry and then recursively following the entry->d_parent - * chain. Use d_path() as predefined method for that. - * You may find out the corresponding inode belonging to - * a dentry by calling d_inode(). This can be used as an easy way for - * determining .. and its absolute pathname, an old UNIX problem that - * deserved a solution for a long time. - * Note that hardlinked inodes may have multiple dentries assigned to - * (via the d_next chain), reflecting multiple alias pathnames. - * - * 2) If not disabled by filesystem types specifying FS_NO_DCACHE, - * the dentries of unused (aged) inodes are retained for speeding up - * lookup()s, by allowing hashed inquiry starting from the dentry of - * the parent directory. - * - * 3) It can remeber so-called "negative entries", that is dentries for - * pathnames that are known to *not* exist, so unneccessary repeated - * lookup()s for non-existant names can be saved. - * - * 4) It provides a means for keeping deleted files (inode->i_nlink==0) - * accessible in the so-called *basket*. Inodes in the basket have been - * removed with unlink() while being in use (i_count>0), so they would - * normally use up space on the disk and be accessile through their - * filedescriptor, but would not be accessible for lookup() any more. - * The basket simply keeps such files in the dcache (for potential - * dcache lookup) until they are either eventually removed completely, - * or transferred to the second-level basket, the so-called *ibasket*. - * The ibasket is implemented in the new inode code, on request of - * filesystem types that have the flag FS_IBASKET set, and proliferates - * the unlinked files when i_count has gone to zero, at least as long - * as there is space on the disk and enough inodes remain available - * and no umount() has started. - * - * 5) Preliminary dentries can be added by readdir(). While normal dentries - * directly point to the inode via u.d_inode only the inode number is - * known from readdir(), but not more. They can be converted to - * normal dentries by using d_inode(). - */ - /* * Notes on the allocation strategy: * - * The dcache is a full slave cache of the inodes. Whenever an inode - * is cleared, all the dentries associated with it will recursively - * disappear. dentries have no own reference counting; this has to - * be obeyed for SMP. - * If directories could go out of inode cache while - * successors are alive, this would interrupt the d_parent chain of - * the live successors. To prevent this without using zombies, all - * directories are thus prevented from __iput() as long as successors - * are alive. + * The dcache is a master of the icache - whenever a dcache entry + * exists, the inode will always exist. "iput()" is done either when + * the dcache entry is deleted or garbage collected. */ -#include #include #include #include @@ -74,38 +21,11 @@ #include /* this should be removed after the beta phase */ -/* #define DEBUG */ +/*#define DEBUG*/ /*#undef DEBUG*/ -/* #define DEBUG_DDIR_COUNT */ - -#define D_HASHSIZE 64 +/*#define DEBUG_DDIR_COUNT*/ -/* local flags for d_flag */ -#define D_DIR 32 -#define D_HASHED 64 -#define D_ZOMBIE 128 -#define D_PRELIMINARY 256 -#define D_INC_DDIR 512 - -/* local flags for d_del() */ -#define D_RECURSIVE 4 -#define D_NO_FREE 8 - -/* This is only used for directory dentries. Think of it as an extension - * of the dentry. - * It is defined as separate struct, so it uses up space only - * where necessary. - */ -struct ddir { - struct dentry * dd_hashtable[D_HASHSIZE]; - struct dentry * dd_neglist; - struct dentry * dd_basketlist; - struct dentry * dd_zombielist; - unsigned short dd_alloced; /* # d_alloc()ed, but not yet d_add()ed */ - unsigned short dd_hashed; /* # of entries in hashtable */ - unsigned short dd_true_hashed; /* # non-preliminaries in hashtable */ - unsigned short dd_negs; /* # of negative entries */ -}; +void printpath(struct dentry * entry); DEF_INSERT(alias,struct dentry,d_next,d_prev) DEF_REMOVE(alias,struct dentry,d_next,d_prev) @@ -113,11 +33,28 @@ DEF_REMOVE(alias,struct dentry,d_next,d_prev) DEF_INSERT(hash,struct dentry,d_hash_next,d_hash_prev) DEF_REMOVE(hash,struct dentry,d_hash_next,d_hash_prev) -DEF_INSERT(basket,struct dentry,d_basket_next,d_basket_prev) -DEF_REMOVE(basket,struct dentry,d_basket_next,d_basket_prev) - struct dentry * the_root = NULL; +/* + * This is the single most critical data structure when it comes + * to the dcache: the hashtable for lookups. Somebody should try + * to make this good - I've just made it work. + * + * This hash-function tries to avoid losing too many bits of hash + * information, yet avoid using a prime hash-size or similar. + */ +#define D_HASHBITS 10 +#define D_HASHSIZE (1UL << D_HASHBITS) +#define D_HASHMASK (D_HASHSIZE-1) +struct dentry * dentry_hashtable[D_HASHSIZE]; + +static inline unsigned long dentry_hash(struct dentry *dir, int name_hash) +{ + unsigned long hash = name_hash + (unsigned long) dir; + hash = hash ^ (hash >> D_HASHBITS) ^ (hash >> D_HASHBITS*2); + return hash & D_HASHMASK; +} + unsigned long name_cache_init(unsigned long mem_start, unsigned long mem_end) { return mem_start; @@ -142,7 +79,7 @@ static void ins(void* ptr) if(cnt % 1000 == 0) printk("------%d allocated: %d: %d %d %d\n", inodes_stat, cnt, x_alloc, x_freed, x_free); - if(cnt>=20000) panic("stop"); + if (cnt>=20000) panic("stop"); } #if 0 @@ -150,7 +87,7 @@ static inline int search(void* ptr) { int i; for(i = cnt-1; i>=0; i--) - if(tst[i] == ptr) + if (tst[i] == ptr) return i; return -1; } @@ -163,55 +100,18 @@ static inline int search(void* ptr) void LOG(char * txt, struct dentry * entry) { static int count = 0; - if(entry) { + if (entry) { TST(txt,entry); } - if(count) { + if (count) { count--; printk("%s: entry=%p\n", txt, entry); } } #ifdef DEBUG_DDIR_COUNT -static struct ddir * d_dir(struct dentry * entry); void recursive_test(struct dentry * entry) { - int i; - struct ddir * ddir = d_dir(entry); - int sons = 0; - - if(ddir->dd_zombielist) - sons++; - for(i=0; i < D_HASHSIZE; i++) { - struct dentry ** base = &ddir->dd_hashtable[i]; - struct dentry * tmp = *base; - if(tmp) do { - TST("__clear",tmp); - if(!(tmp->d_flag & D_HASHED)) { - printk("VFS: dcache entry not hashed!\n"); - printpath(*base); printk("\n"); - printpath(tmp); - } - if(!(tmp->d_flag & D_PRELIMINARY)) - sons++; - if(tmp->d_flag & D_DIR) - recursive_test(tmp); - tmp = tmp->d_hash_next; - } while(tmp && tmp != *base); - } - if(!sons && !(entry->d_flag & D_PRELIMINARY) && entry->u.d_inode) { - struct inode * inode = entry->u.d_inode; - if(!atomic_read(&inode->i_count)) { - if(!(inode->i_status & 1/*ST_AGED*/)) { - printpath(entry); - printk(" is not aged!\n"); - } - if(inode->i_ddir_count) { - printpath(entry); - printk(" has ddir_count blockage!\n"); - } - } - } } #else #define recursive_test(e) /*nothing*/ @@ -226,604 +126,280 @@ void recursive_test(struct dentry * entry) void printpath(struct dentry * entry) { - if(!IS_ROOT(entry)) + if (!IS_ROOT(entry)) printpath(entry->d_parent); printk("/%s", entry->d_name.name); } -static inline long has_sons(struct ddir * ddir) -{ - return ((ddir->dd_alloced | ddir->dd_hashed) || - ddir->dd_neglist || - ddir->dd_basketlist || - ddir->dd_zombielist); -} - -static inline int has_true_sons(struct ddir * ddir) -{ - return (ddir->dd_alloced | ddir->dd_true_hashed); -} - -/* Only hold the i_ddir_count pseudo refcount when neccessary (i.e. when - * they have true_sons), to prevent keeping too much dir inodes in use. - */ -static inline void inc_ddir(struct dentry * entry, struct inode * inode) -{ - if(!(entry->d_flag & D_INC_DDIR)) { - entry->d_flag |= D_INC_DDIR; -#ifdef DEBUG - if(inode->i_ddir_count) { - printpath(entry); - printk(" ddir_count=%d\n", inode->i_ddir_count); - } -#endif - inode->i_ddir_count++; - _get_inode(inode); - } -} - -static inline blocking void dec_ddir(struct dentry * entry, struct inode * inode) +void d_free(struct dentry *dentry) { - if(entry->d_flag & D_INC_DDIR) { - entry->d_flag &= ~D_INC_DDIR; - inode->i_ddir_count--; - if(!inode->i_ddir_count) - __iput(inode); + if (dentry) { + kfree(dentry->d_name.name); + kfree(dentry); } } -/* Do not inline this many times. */ -static void d_panic(void) -{ - panic("VFS: dcache directory corruption"); -} - -/* - * IF this is a directory, the ddir has been allocated right - * after the dentry. - */ -static inline struct ddir * d_dir(struct dentry * entry) -{ - if(!(entry->d_flag & D_DIR)) - d_panic(); - return (struct ddir *) (entry+1); -} - #define NAME_ALLOC_LEN(len) ((len+16) & ~15) -struct dentry * d_alloc(struct dentry * parent, int len, int isdir) +struct dentry * d_alloc(struct dentry * parent, struct qstr *name, int isdir) { + char *str; struct dentry *res; - int size = sizeof(struct dentry); - int flag = 0; - if (isdir) { - size += sizeof(struct ddir); - flag = D_DIR; - } - res = kmalloc(size, GFP_KERNEL); + res = kmalloc(sizeof(struct dentry), GFP_KERNEL); if (!res) return NULL; - memset(res, 0, size); - res->d_flag = flag; - res->d_name.name = kmalloc(NAME_ALLOC_LEN(len), GFP_KERNEL); - if (!res->d_name.name) { + str = kmalloc(NAME_ALLOC_LEN(name->len), GFP_KERNEL); + if (!str) { kfree(res); return NULL; } - res->d_name.len = len; + memcpy(str, name->name, name->len); + str[name->len] = 0; + + memset(res, 0, sizeof(struct dentry)); + res->d_parent = parent; - if(parent) { - struct ddir * pdir = d_dir(parent); -#ifdef DEBUG - if(pdir->dd_alloced > 1 && !IS_ROOT(parent)) { - printpath(parent); - printk(" dd_alloced=%d\n", pdir->dd_alloced); - } -#endif - pdir->dd_alloced++; - } + res->d_mounts = res; + res->d_covers = res; + res->d_flag = isdir ? D_DIR : 0; + + res->d_name.name = str; + res->d_name.len = name->len; + res->d_name.hash = name->hash; + #ifdef DEBUG x_alloc++; #endif return res; } -extern blocking struct dentry * d_alloc_root(struct inode * root_inode) +extern blocking struct dentry * d_alloc_root(struct inode * root_inode, struct dentry *old_root) { - struct dentry * res = the_root; + struct dentry *res; + struct qstr name = { "/", 1, 0 }; /* dummy qstr */ - if(res) { - d_del(res, D_NO_CLEAR_INODE); /* invalidate everything beyond */ - } else { - struct ddir * ddir; - - the_root = res = d_alloc(NULL, 0, 1); - LOG("d_alloc_root", res); - res->d_parent = res; - res->d_name.name[0]='\0'; - ddir = d_dir(res); - ddir->dd_alloced = 999; /* protect from deletion */ - } - insert_alias(&root_inode->i_dentry, res); - root_inode->i_dent_count++; - root_inode->i_ddir_count++; - res->u.d_inode = root_inode; + if (!root_inode) + return NULL; + res = d_alloc(NULL, &name, 1); + LOG("d_alloc_root", res); + res->d_parent = res; + d_instantiate(res, root_inode, D_DIR); return res; } -static inline unsigned long d_hash(char first, char last) -{ - return ((unsigned long)first ^ ((unsigned long)last << 4)) & (D_HASHSIZE-1); -} - -static inline struct dentry ** d_base_entry(struct ddir * pdir, struct dentry * entry) +static inline struct dentry ** d_base_qstr(struct dentry * parent, struct qstr * s) { - return &pdir->dd_hashtable[d_hash(entry->d_name.name[0], - entry->d_name.name[entry->d_name.len-1])]; + return dentry_hashtable + dentry_hash(parent, s->hash); } -static inline struct dentry ** d_base_qstr(struct ddir * pdir, - struct qstr * s1, - struct qstr * s2) +static inline struct dentry ** d_base_entry(struct dentry * pdir, struct dentry * entry) { - unsigned long hash; - - if(s2 && s2->len) { - hash = d_hash(s1->name[0], s2->name[s2->len-1]); - } else { - hash = d_hash(s1->name[0], s1->name[s1->len-1]); - } - return &pdir->dd_hashtable[hash]; + return d_base_qstr(pdir, &entry->d_name); } static /*inline*/ blocking void _d_remove_from_parent(struct dentry * entry, - struct ddir * pdir, - struct inode * inode, - int flags) + struct dentry * parent) { - if(entry->d_flag & D_HASHED) { - struct dentry ** base = d_base_entry(pdir, entry); + if (entry->d_flag & D_HASHED) { + struct dentry ** base = d_base_entry(parent, entry); remove_hash(base, entry); entry->d_flag &= ~D_HASHED; - pdir->dd_hashed--; - if(!(entry->d_flag & D_PRELIMINARY)) { - pdir->dd_true_hashed--; - if(!inode) { -#ifdef DEBUG - if(!entry->d_next || !entry->d_prev) { - printpath(entry); - printk(" flags=%x d_flag=%x negs=%d " - "hashed=%d\n", flags, entry->d_flag, - pdir->dd_negs, pdir->dd_hashed); - } -#endif - remove_alias(&pdir->dd_neglist, entry); - pdir->dd_negs--; - } - } - } else if(!(entry->d_flag & D_ZOMBIE)) { -#ifdef DEBUG - if(!pdir->dd_alloced) printk("dd_alloced is 0!\n"); -#endif - pdir->dd_alloced--; - } - if(entry->d_flag & D_BASKET) { - remove_basket(&pdir->dd_basketlist, entry); - entry->d_flag &= ~D_BASKET; - } -} - -/* Theoretically, zombies should never or extremely seldom appear, - * so this code is nearly superfluous. - * A way to get zombies is while using inodes (i_count>0), unlink() - * them as well as rmdir() the parent dir => the parent dir becomes a zombie. - * Zombies are *not* in the hashtable, because somebody could re-creat() - * that filename in it's parent dir again. - * Besides coding errors during beta phase, when forcing an umount() - * (e.g. at shutdown time), inodes could be in use such that the parent - * dir is cleared, resulting also in zombies. - */ -static /*inline*/ void _d_handle_zombie(struct dentry * entry, - struct ddir * ddir, - struct ddir * pdir) -{ - if(entry->d_flag & D_DIR) { - if(entry->d_flag & D_ZOMBIE) { - if(!has_sons(ddir)) { - entry->d_flag &= ~D_ZOMBIE; - remove_hash(&pdir->dd_zombielist, entry); - if(!pdir->dd_zombielist && - (entry->d_parent->d_flag & D_ZOMBIE)) { - d_del(entry->d_parent, D_NORMAL); - } - } - } else if(has_sons(ddir)) { - entry->d_flag |= D_ZOMBIE; - insert_hash(&pdir->dd_zombielist, entry); - - /* This condition is no longer a bug, with the removal - * of recursive_clear() this happens naturally during - * an unmount attempt of a filesystem which is busy. - */ -#if 0 - /* Not sure when this message should show up... */ - if(!IS_ROOT(entry)) { - printk("VFS: clearing dcache directory " - "with successors\n"); -#ifdef DEBUG - printpath(entry); - printk(" d_flag=%x alloced=%d negs=%d hashed=%d " - "basket=%p zombies=%p\n", - entry->d_flag, ddir->dd_alloced, - ddir->dd_negs, ddir->dd_hashed, - ddir->dd_basketlist, ddir->dd_zombielist); -#endif - } -#endif - } - } -} - -static /*inline*/ blocking void _d_del(struct dentry * entry, - int flags) -{ - struct ddir * ddir = NULL; - struct ddir * pdir; - struct inode * inode = entry->d_flag & D_PRELIMINARY ? NULL : entry->u.d_inode; - -#ifdef DEBUG - if(inode) - xcheck("_d_del", inode); -#endif - if(!entry->d_parent) { - printk("VFS: dcache parent is NULL\n"); - return; - } - pdir = d_dir(entry->d_parent); - if(!IS_ROOT(entry)) - _d_remove_from_parent(entry, pdir, inode, flags); - - /* This may block, be careful! _d_remove_from_parent() is - * thus called before. - */ - if(entry->d_flag & D_DIR) - ddir = d_dir(entry); - if(IS_ROOT(entry)) - return; - - if(flags & D_NO_FREE) { - /* Make it re-d_add()able */ - pdir->dd_alloced++; - entry->d_flag &= D_DIR; - } else - _d_handle_zombie(entry, ddir, pdir); - - /* This dec_ddir() must occur after zombie handling. */ - if(!has_true_sons(pdir)) - dec_ddir(entry->d_parent, entry->d_parent->u.d_inode); - - entry->u.d_inode = NULL; - if(inode) { - remove_alias(&inode->i_dentry, entry); - inode->i_dent_count--; - if (entry->d_flag & D_DIR) - dec_ddir(entry, inode); - - if(!(flags & D_NO_CLEAR_INODE) && - !(atomic_read(&inode->i_count) + - inode->i_ddir_count + - inode->i_dent_count)) { -#ifdef DEBUG - printk("#"); -#endif - /* This may block also. */ - _clear_inode(inode, 0, 0); - } - } - if(!(flags & D_NO_FREE) && !(entry->d_flag & D_ZOMBIE)) { - kfree(entry->d_name.name); - kfree(entry); -#ifdef DEBUG - x_freed++; -#endif } -#ifdef DEBUG - x_free++; -#endif -} - -blocking void d_del(struct dentry * entry, int flags) -{ - if(!entry) - return; - LOG("d_clear", entry); - _d_del(entry, flags); } -static inline struct dentry * __dlookup(struct dentry ** base, - struct qstr * name, - struct qstr * appendix) +static inline struct dentry * __dlookup(struct dentry * base, struct dentry * parent, struct qstr * name) { - struct dentry * tmp = *base; - - if(tmp && name->len) { - int totallen = name->len; + if (base) { + struct dentry * tmp = base; + int len = name->len; + int hash = name->hash; + const unsigned char *str = name->name; - if(appendix) - totallen += appendix->len; do { - if(tmp->d_name.len == totallen && - !(tmp->d_flag & D_DUPLICATE) && - !strncmp(tmp->d_name.name, name->name, name->len) && - (!appendix || !strncmp(tmp->d_name.name+name->len, - appendix->name, appendix->len))) + if (tmp->d_name.hash == hash && + tmp->d_name.len == len && + tmp->d_parent == parent && + !(tmp->d_flag & D_DUPLICATE) && + !memcmp(tmp->d_name.name, str, len)) return tmp; tmp = tmp->d_hash_next; - } while(tmp != *base); + } while(tmp != base); } return NULL; } -struct dentry * d_lookup(struct inode * dir, - struct qstr * name, - struct qstr * appendix) +struct dentry * d_lookup(struct dentry * dir, struct qstr * name) { - if(dir->i_dentry) { - struct ddir * ddir = d_dir(dir->i_dentry); - struct dentry ** base = d_base_qstr(ddir, name, appendix); + struct dentry ** base = d_base_qstr(dir, name); - return __dlookup(base, name, appendix); - } - return NULL; + return __dlookup(*base, dir, name); } static /*inline*/ blocking void _d_insert_to_parent(struct dentry * entry, - struct ddir * pdir, + struct dentry * parent, struct inode * inode, - struct qstr * ininame, int flags) { struct dentry ** base; - struct dentry * parent = entry->d_parent; -#ifdef DEBUG - if(!pdir->dd_alloced) - printk("dd_alloced is 0!\n"); -#endif - base = d_base_qstr(pdir, ininame, NULL); - if(!(flags & (D_NOCHECKDUP|D_DUPLICATE)) && - __dlookup(base, ininame, NULL)) { - d_del(entry, D_NO_CLEAR_INODE); - return; - } - if(entry->d_flag & D_HASHED) { + base = d_base_qstr(parent, &entry->d_name); + if (entry->d_flag & D_HASHED) { printk("VFS: dcache entry is already hashed\n"); return; } - if(!(flags & D_PRELIMINARY)) - pdir->dd_true_hashed++; - pdir->dd_hashed++; insert_hash(base, entry); entry->d_flag |= D_HASHED; - pdir->dd_alloced--; - if(flags & D_BASKET) - insert_basket(&pdir->dd_basketlist, entry); +} -#ifdef DEBUG - if(inode && inode->i_dentry && (entry->d_flag & D_DIR)) { - struct dentry * tmp = inode->i_dentry; - printk("Auweia inode=%p entry=%p (%p %p %s)\n", - inode, entry, parent->u.d_inode, parent, parent->d_name.name); - printk("entry path="); printpath(entry); printk("\n"); - do { - TST("auweia",tmp); - printk("alias path="); printpath(tmp); printk("\n"); - tmp = tmp->d_next; - } while(tmp != inode->i_dentry); - printk("\n"); +/* + * Fill in inode information in the entry. + * + * This turns negative dentries into productive full members + * of society. + * + * NOTE! This assumes that the inode count has been incremented + * (or otherwise set) by the caller to indicate that it is now + * in use by the dcache.. + */ +void d_instantiate(struct dentry *entry, struct inode * inode, int flags) +{ + entry->d_flag = (entry->d_flag & ~D_NEGATIVE) | flags; + + if (inode && !(flags & D_NEGATIVE)) { + if (entry->d_flag & D_DIR) { + if (inode->i_dentry) { + printk("VFS: creating dcache directory alias\n"); + return; + } + } + insert_alias(&inode->i_dentry, entry); } -#endif - if(has_true_sons(pdir)) - inc_ddir(parent, parent->u.d_inode); - if(!inode && !(flags & D_PRELIMINARY)) { - insert_alias(&pdir->dd_neglist, entry); - pdir->dd_negs++; - - /* Don't allow the negative list to grow too much ... */ - while(pdir->dd_negs > (pdir->dd_true_hashed >> 1) + 5) - d_del(pdir->dd_neglist->d_prev, D_REMOVE); + + entry->d_inode = inode; +} + +/* + * Remove the inode from the dentry.. This removes + * it from the parent hashes but otherwise leaves it + * around - it may be a "zombie", part of a path + * that is still in use... + * + * "The Night of the Living Dead IV - the Dentry" + */ +void d_delete(struct dentry * dentry) +{ + struct inode * inode = dentry->d_inode; + + _d_remove_from_parent(dentry, dentry->d_parent); + if (inode) { + remove_alias(&inode->i_dentry, dentry); + dentry->d_inode = NULL; + iput(inode); } } -blocking void d_add(struct dentry * entry, struct inode * inode, - struct qstr * ininame, int flags) +blocking void d_add(struct dentry * entry, struct inode * inode, int flags) { struct dentry * parent = entry->d_parent; - struct qstr dummy; - struct ddir * pdir; #ifdef DEBUG - if(inode) + if (inode) xcheck("d_add", inode); - if(IS_ROOT(entry)) { + if (IS_ROOT(entry)) { printk("VFS: d_add for root dentry "); printpath(entry); printk(" -> "); - if(ininame) - printk("%s", ininame->name); printk("\n"); return; } - if(!parent) + if (!parent) panic("d_add with parent==NULL"); LOG("d_add", entry); #endif - if(ininame) { - if(ininame->len != entry->d_name.len) { - printk("VFS: d_add with wrong string length"); - entry->d_name.len = ininame->len; /* kludge */ - } - memcpy(entry->d_name.name, ininame->name, ininame->len); - entry->d_name.name[ininame->len] = '\0'; - } else { - dummy.name = entry->d_name.name; - dummy.len = entry->d_name.len; - ininame = &dummy; - } if(entry->d_flag & D_HASHED) printk("VFS: d_add of already added dcache entry\n"); - pdir = d_dir(parent); - _d_insert_to_parent(entry, pdir, inode, ininame, flags); - entry->d_flag |= flags; - if(inode && !(flags & D_PRELIMINARY)) { - if(entry->d_flag & D_DIR) { - if(inode->i_dentry) { - printk("VFS: creating dcache directory alias\n"); - return; - } - } - insert_alias(&inode->i_dentry, entry); - inode->i_dent_count++; - } - entry->u.d_inode = inode; + _d_insert_to_parent(entry, parent, inode, flags); + d_instantiate(entry, inode, flags); } -blocking struct dentry * d_entry(struct dentry * parent, - struct qstr * name, - struct inode * inode) +static inline void alloc_new_name(struct dentry * entry, struct qstr *newname) { - struct ddir * pdir = d_dir(parent); - struct dentry ** base = d_base_qstr(pdir, name, NULL); - struct dentry * found = __dlookup(base, name, NULL); - - if(!found) { - int isdir = (inode && S_ISDIR(inode->i_mode)); - - found = d_alloc(parent, name->len, isdir); - if(found) { - d_add(found, inode, name, - isdir ? (D_DIR|D_NOCHECKDUP) : D_NOCHECKDUP); - } else - printk("VFS: problem with d_alloc\n"); - } - return found; -} + int len = newname->len; + int hash = newname->hash; + char *name = (char *) entry->d_name.name; -blocking void d_entry_preliminary(struct dentry * parent, - struct qstr * name, - unsigned long ino) -{ - struct ddir * pdir = d_dir(parent); - struct dentry ** base = d_base_qstr(pdir, name, NULL); - struct dentry * found = __dlookup(base, name, NULL); - - if(!found && ino) { - struct dentry * new = d_alloc(parent, name->len, 0); - - if(new) { - d_add(new, NULL, name, D_PRELIMINARY|D_NOCHECKDUP); - new->u.d_ino = ino; - } else - printk("VFS: problem with d_alloc\n"); + if (NAME_ALLOC_LEN(len) != NAME_ALLOC_LEN(entry->d_name.len)) { + name = kmalloc(NAME_ALLOC_LEN(len), GFP_KERNEL); + if (!name) + printk("out of memory for dcache\n"); + kfree(entry->d_name.name); + entry->d_name.name = name; } -} - -static inline void alloc_new_name(struct dentry * entry, int len) -{ - int alloc_len = NAME_ALLOC_LEN(len); - char *name; - - if (alloc_len == NAME_ALLOC_LEN(entry->d_name.len)) - return; - name = kmalloc(alloc_len, GFP_KERNEL); - if (!name) - printk("out of memory for dcache\n"); - kfree(entry->d_name.name); - entry->d_name.name = name; + memcpy(name, newname->name, len); + name[len] = 0; + entry->d_name.len = len; + entry->d_name.hash = hash; } static inline void d_remove_old_parent(struct dentry * entry) { - struct ddir * pdir; + struct dentry * parent; struct inode * inode; - pdir = d_dir(entry->d_parent); - inode = entry->u.d_inode; - _d_remove_from_parent(entry, pdir, inode, D_NO_CLEAR_INODE); + parent = entry->d_parent; + inode = entry->d_inode; + _d_remove_from_parent(entry, parent); } -static inline void d_add_new_parent(struct dentry * entry, struct inode * new_parent) +static inline void d_add_new_parent(struct dentry * entry, struct dentry * parent) { - struct ddir * pdir; struct inode * inode; - pdir = d_dir(entry->d_parent = new_parent->i_dentry); - inode = entry->u.d_inode; + entry->d_parent = parent; + inode = entry->d_inode; - _d_insert_to_parent(entry, pdir, inode, &entry->d_name, entry->d_flag); + _d_insert_to_parent(entry, parent, inode, entry->d_flag); } -blocking void d_move(struct dentry * entry, struct inode * newdir, - struct qstr * newname, struct qstr * newapp) +blocking void d_move(struct dentry * entry, struct dentry * newdir, struct qstr * newname) { struct inode * inode; - int len; int flags; - if(!entry) + if (!entry) return; - inode = entry->u.d_inode; + inode = entry->d_inode; flags = entry->d_flag; - if((flags & D_PRELIMINARY) || !inode) { - if(!(flags & D_PRELIMINARY)) - printk("VFS: trying to move negative dcache entry\n"); - d_del(entry, D_NO_CLEAR_INODE); - return; + if (!inode) { + printk("VFS: moving negative dcache entry\n"); } -#if 0 -printk("d_move %p '%s' -> '%s%s' dent_count=%d\n", inode, entry->d_name.name, - newname->name, newapp ? newapp->name : "", inode->i_dent_count); -#endif - if(flags & D_ZOMBIE) { + + if (flags & D_ZOMBIE) { printk("VFS: moving zombie entry\n"); } d_remove_old_parent(entry); - - len = newname->len; - if(newapp) { - len += newapp->len; - flags |= D_BASKET; - } else - flags &= ~D_BASKET; - alloc_new_name(entry, len); - memcpy(entry->d_name.name, newname->name, newname->len); - if(newapp) - memcpy(entry->d_name.name+newname->len, newapp->name, newapp->len); - entry->d_name.name[len] = '\0'; - entry->d_name.len = len; - + alloc_new_name(entry, newname); d_add_new_parent(entry, newdir); } -int d_path(struct dentry * entry, struct inode * chroot, char * buf) +int d_path(struct dentry * entry, struct dentry * chroot, char * buf) { - if(IS_ROOT(entry) || (chroot && entry->u.d_inode == chroot && - !(entry->d_flag & D_PRELIMINARY))) { + if (IS_ROOT(entry) || (chroot && entry == chroot)) { *buf = '/'; return 1; } else { int len = d_path(entry->d_parent, chroot, buf); buf += len; - if(len > 1) { + if (len > 1) { *buf++ = '/'; len++; } @@ -831,97 +407,3 @@ int d_path(struct dentry * entry, struct inode * chroot, char * buf) return len + entry->d_name.len; } } - -struct dentry * d_basket(struct dentry * dir_entry) -{ - if(dir_entry && (dir_entry->d_flag & D_DIR)) { - struct ddir * ddir = d_dir(dir_entry); - - return ddir->dd_basketlist; - } else - return NULL; -} - -int d_isbasket(struct dentry * entry) -{ - return entry->d_flag & D_BASKET; -} - -blocking struct inode * d_inode(struct dentry ** changing_entry) -{ - struct dentry * entry = *changing_entry; - struct inode * inode; - -#ifdef CONFIG_DCACHE_PRELOAD - if(entry->d_flag & D_PRELIMINARY) { - struct qstr name = { entry->d_name.name, entry->d_name.len }; - struct ddir * pdir = d_dir(entry->d_parent); - struct dentry ** base = d_base_qstr(pdir, &name, NULL); - struct dentry * found; - unsigned long ino; - struct inode * dir = entry->d_parent->u.d_inode; - TST("d_inode",entry); - ino = entry->u.d_ino; - if(!dir) - d_panic(); - - /* Prevent concurrent d_lookup()s or d_inode()s before - * giving up vfs_lock. This just removes from the parent, - * but does not deallocate it. - */ - - /* !!!!!!! Aiee, here is an unresolved race if somebody - * unlink()s the inode during the iget(). The problem is - * that we need to synchronize externally. Proposed solution: - * put a rw_lock (read-mode) on the parent dir for each - * iget(), lookup() and so on, and a write-mode lock for - * everything that changes the dir (e.g. unlink()), and do - * this consistently everywhere in the generic VFS (not in - * the concrete filesystems). This should kill similar - * races everywhere, with a single clean concept. - * Later, the synchronization stuff can be cleaned out - * of the concrete fs'es. - */ - d_del(entry, D_NO_CLEAR_INODE|D_NO_FREE); - vfs_unlock(); - - /* This circumvents the normal lookup() of pathnames. - * Therefore, preliminary entries must not be used - * (see FS_NO_DCACHE and FS_NO_PRELIM) if the fs does not - * permit fetching *valid* inodes with plain iget(). - */ - inode = __iget(dir->i_sb, ino, 0); - vfs_lock(); - if(!inode) { - printk("VFS: preliminary dcache entry was invalid\n"); - *changing_entry = NULL; - return NULL; - } - xcheck("d_inode iget()", inode); - if((found = __dlookup(base, &name, NULL))) { - d_del(entry, D_NO_CLEAR_INODE); - *changing_entry = found; - } else if(S_ISDIR(inode->i_mode)) { - struct dentry * new = d_alloc(entry->d_parent, entry->d_name.len, 1); - if(new) - d_add(new, inode, &name, D_DIR); - *changing_entry = new; - - /* Finally deallocate old entry. */ - d_del(entry, D_NO_CLEAR_INODE); - } else { - /* Re-insert to the parent, but now as normal dentry. */ - d_add(entry, inode, NULL, 0); - } - return inode; - } -#endif - inode = entry->u.d_inode; - if(inode) { -#ifdef DEBUG - xcheck("d_inode", inode); -#endif - iinc_zero(inode); - } - return inode; -} diff --git a/fs/dquot.c b/fs/dquot.c index 59d2112d9462..b830193d0e99 100644 --- a/fs/dquot.c +++ b/fs/dquot.c @@ -1038,7 +1038,7 @@ asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr) if (special == (char *)NULL && (cmds == Q_SYNC || cmds == Q_GETSTATS)) dev = 0; else { - int error = namei(NAM_FOLLOW_LINK, special, &ino); + int error = namei(special, &ino); if(error) goto out; dev = ino->i_rdev; diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index 4d2b561ee62b..7e159e7d286d 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -291,7 +291,7 @@ int ext2_new_block (const struct inode * inode, unsigned long goal, printk ("ext2_new_block: nonexistent device"); return 0; } -retry: + lock_super (sb); es = sb->u.ext2_sb.s_es; if (le32_to_cpu(es->s_free_blocks_count) <= le32_to_cpu(es->s_r_blocks_count) && @@ -299,8 +299,6 @@ retry: (sb->u.ext2_sb.s_resgid == 0 || !in_group_p (sb->u.ext2_sb.s_resgid)))) { unlock_super (sb); - if(sb->s_ibasket && free_ibasket(sb)) - goto retry; return 0; } @@ -392,8 +390,6 @@ repeat: } if (k >= sb->u.ext2_sb.s_groups_count) { unlock_super (sb); - if(sb->s_ibasket && free_ibasket(sb)) - goto retry; return 0; } bitmap_nr = load_block_bitmap (sb, i); diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c index d9b1957e3172..bf64a2a1aead 100644 --- a/fs/ext2/dir.c +++ b/fs/ext2/dir.c @@ -65,6 +65,7 @@ struct inode_operations ext2_dir_inode_operations = { ext2_mknod, /* mknod */ ext2_rename, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ diff --git a/fs/ext2/file.c b/fs/ext2/file.c index dbb8c3619b9e..61d552d8c7c0 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c @@ -72,6 +72,7 @@ struct inode_operations ext2_file_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ generic_readpage, /* readpage */ NULL, /* writepage */ ext2_bmap, /* bmap */ diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index 7c578ae6f1b5..85981a7982ea 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -27,6 +27,7 @@ #include #include + /* * define how far ahead to read directories while searching them. */ @@ -154,8 +155,7 @@ failure: return NULL; } -int ext2_lookup (struct inode * dir, const char * name, int len, - struct inode ** result) +int ext2_lookup(struct inode * dir, struct qstr *name, struct inode ** result) { unsigned long ino; struct ext2_dir_entry * de; @@ -164,26 +164,22 @@ int ext2_lookup (struct inode * dir, const char * name, int len, *result = NULL; if (!dir) return -ENOENT; - if (!S_ISDIR(dir->i_mode)) { - iput (dir); + + if (!S_ISDIR(dir->i_mode)) return -ENOTDIR; - } - if (len > EXT2_NAME_LEN) { - iput (dir); + + if (name->len > EXT2_NAME_LEN) return -ENAMETOOLONG; - } + ino = dir->i_version; - if (!(bh = ext2_find_entry (dir, name, len, &de))) { - iput (dir); + if (!(bh = ext2_find_entry (dir, name->name, name->len, &de))) return -ENOENT; - } + ino = le32_to_cpu(de->inode); brelse (bh); - if (!(*result = iget (dir->i_sb, ino))) { - iput (dir); + if (!(*result = iget (dir->i_sb, ino))) return -EACCES; - } - iput (dir); + return 0; } @@ -347,31 +343,35 @@ static int ext2_delete_entry (struct ext2_dir_entry * dir, return -ENOENT; } -int ext2_create (struct inode * dir,const char * name, int len, int mode, - struct inode ** result) +/* + * By the time this is called, we already have created + * the directory cache entry for the new file, but it + * is so far marked "D_NEGATIVE". + * + * If the create succeeds, remove the D_NEGATIVE flag, + * and fill in the inode information with d_instantiate(). + */ +int ext2_create (struct inode * dir, struct dentry * dentry, int mode) { struct inode * inode; struct buffer_head * bh; struct ext2_dir_entry * de; int err; - *result = NULL; if (!dir) return -ENOENT; inode = ext2_new_inode (dir, mode, &err); - if (!inode) { - iput (dir); + if (!inode) return err; - } + inode->i_op = &ext2_file_inode_operations; inode->i_mode = mode; inode->i_dirt = 1; - bh = ext2_add_entry (dir, name, len, &de, &err); + bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err); if (!bh) { inode->i_nlink--; inode->i_dirt = 1; iput (inode); - iput (dir); return err; } de->inode = cpu_to_le32(inode->i_ino); @@ -382,13 +382,11 @@ int ext2_create (struct inode * dir,const char * name, int len, int mode, wait_on_buffer (bh); } brelse (bh); - iput (dir); - *result = inode; + d_instantiate(dentry, inode, 0); return 0; } -int ext2_mknod (struct inode * dir, const char * name, int len, int mode, - int rdev) +int ext2_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev) { struct inode * inode; struct buffer_head * bh; @@ -398,21 +396,13 @@ int ext2_mknod (struct inode * dir, const char * name, int len, int mode, if (!dir) return -ENOENT; - if (len > EXT2_NAME_LEN) { - iput (dir); + if (dentry->d_name.len > EXT2_NAME_LEN) return -ENAMETOOLONG; - } - bh = ext2_find_entry (dir, name, len, &de); - if (bh) { - brelse (bh); - iput (dir); - return -EEXIST; - } + inode = ext2_new_inode (dir, mode, &err); - if (!inode) { - iput (dir); + if (!inode) return err; - } + inode->i_uid = current->fsuid; inode->i_mode = mode; inode->i_op = NULL; @@ -434,12 +424,11 @@ int ext2_mknod (struct inode * dir, const char * name, int len, int mode, if (S_ISBLK(mode) || S_ISCHR(mode)) inode->i_rdev = to_kdev_t(rdev); inode->i_dirt = 1; - bh = ext2_add_entry (dir, name, len, &de, &err); + bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err); if (!bh) { inode->i_nlink--; inode->i_dirt = 1; - iput (inode); - iput (dir); + iput(inode); return err; } de->inode = cpu_to_le32(inode->i_ino); @@ -449,45 +438,32 @@ int ext2_mknod (struct inode * dir, const char * name, int len, int mode, ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); } - brelse (bh); - iput (dir); - iput (inode); + brelse(bh); + d_instantiate(dentry, inode, 0); return 0; } -int ext2_mkdir (struct inode * dir, const char * name, int len, int mode) +int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode) { struct inode * inode; struct buffer_head * bh, * dir_block; struct ext2_dir_entry * de; int err; - if (!dir) - return -ENOENT; - if (len > EXT2_NAME_LEN) { - iput (dir); + if (dentry->d_name.len > EXT2_NAME_LEN) return -ENAMETOOLONG; - } - bh = ext2_find_entry (dir, name, len, &de); - if (bh) { - brelse (bh); - iput (dir); - return -EEXIST; - } - if (dir->i_nlink >= EXT2_LINK_MAX) { - iput (dir); + + if (dir->i_nlink >= EXT2_LINK_MAX) return -EMLINK; - } + inode = ext2_new_inode (dir, S_IFDIR, &err); - if (!inode) { - iput (dir); + if (!inode) return err; - } + inode->i_op = &ext2_dir_inode_operations; inode->i_size = inode->i_sb->s_blocksize; dir_block = ext2_bread (inode, 0, 1, &err); if (!dir_block) { - iput (dir); inode->i_nlink--; inode->i_dirt = 1; iput (inode); @@ -511,9 +487,8 @@ int ext2_mkdir (struct inode * dir, const char * name, int len, int mode) if (dir->i_mode & S_ISGID) inode->i_mode |= S_ISGID; inode->i_dirt = 1; - bh = ext2_add_entry (dir, name, len, &de, &err); + bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err); if (!bh) { - iput (dir); inode->i_nlink = 0; inode->i_dirt = 1; iput (inode); @@ -528,8 +503,7 @@ int ext2_mkdir (struct inode * dir, const char * name, int len, int mode) } dir->i_nlink++; dir->i_dirt = 1; - iput (dir); - iput (inode); + d_instantiate(dentry, inode, D_DIR); brelse (bh); return 0; } @@ -593,51 +567,46 @@ static int empty_dir (struct inode * inode) return 1; } -int ext2_rmdir (struct inode * dir, const char * name, int len) +int ext2_rmdir (struct inode * dir, struct dentry *dentry) { int retval; struct inode * inode; struct buffer_head * bh; struct ext2_dir_entry * de; -repeat: if (!dir) return -ENOENT; inode = NULL; - if (len > EXT2_NAME_LEN) { - iput (dir); + if (dentry->d_name.len > EXT2_NAME_LEN) return -ENAMETOOLONG; - } - bh = ext2_find_entry (dir, name, len, &de); + + bh = ext2_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de); retval = -ENOENT; if (!bh) goto end_rmdir; retval = -EPERM; - if (!(inode = iget (dir->i_sb, le32_to_cpu(de->inode)))) - goto end_rmdir; + inode = dentry->d_inode; + if (inode->i_sb->dq_op) inode->i_sb->dq_op->initialize (inode, -1); - if (inode->i_dev != dir->i_dev) { - retval = -EBUSY; - goto end_rmdir; - } - if (le32_to_cpu(de->inode) != inode->i_ino) { - iput(inode); - brelse(bh); - current->counter = 0; - schedule(); - goto repeat; - } + if ((dir->i_mode & S_ISVTX) && !fsuser() && current->fsuid != inode->i_uid && current->fsuid != dir->i_uid) goto end_rmdir; if (inode == dir) /* we may not delete ".", but "../dir" is ok */ goto end_rmdir; - if (!S_ISDIR(inode->i_mode)) { - retval = -ENOTDIR; + + retval = -ENOTDIR; + if (!S_ISDIR(inode->i_mode)) goto end_rmdir; - } + + retval = -EIO; + if (inode->i_dev != dir->i_dev) + goto end_rmdir; + if (le32_to_cpu(de->inode) != inode->i_ino) + goto end_rmdir; + down(&inode->i_sem); if (!empty_dir (inode)) retval = -ENOTEMPTY; @@ -675,52 +644,47 @@ repeat: dir->i_nlink--; inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; dir->i_dirt = 1; + d_delete(dentry); + end_rmdir: - iput (dir); - iput (inode); brelse (bh); return retval; } -int ext2_unlink (struct inode * dir, const char * name, int len) +int ext2_unlink(struct inode * dir, struct dentry *dentry) { int retval; struct inode * inode; struct buffer_head * bh; struct ext2_dir_entry * de; -repeat: - if (!dir) - return -ENOENT; retval = -ENOENT; inode = NULL; - if (len > EXT2_NAME_LEN) { - iput (dir); + if (dentry->d_name.len > EXT2_NAME_LEN) return -ENAMETOOLONG; - } - bh = ext2_find_entry (dir, name, len, &de); + + bh = ext2_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de); if (!bh) goto end_unlink; - if (!(inode = iget (dir->i_sb, le32_to_cpu(de->inode)))) - goto end_unlink; + + inode = dentry->d_inode; if (inode->i_sb->dq_op) inode->i_sb->dq_op->initialize (inode, -1); + retval = -EPERM; if (S_ISDIR(inode->i_mode)) goto end_unlink; if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) goto end_unlink; - if (le32_to_cpu(de->inode) != inode->i_ino) { - iput(inode); - brelse(bh); - current->counter = 0; - schedule(); - goto repeat; - } if ((dir->i_mode & S_ISVTX) && !fsuser() && current->fsuid != inode->i_uid && current->fsuid != dir->i_uid) goto end_unlink; + + retval = -EIO; + if (le32_to_cpu(de->inode) != inode->i_ino) + goto end_unlink; + if (!inode->i_nlink) { ext2_warning (inode->i_sb, "ext2_unlink", "Deleting nonexistent file (%lu), %d", @@ -742,15 +706,14 @@ repeat: inode->i_dirt = 1; inode->i_ctime = dir->i_ctime; retval = 0; + d_delete(dentry); /* This also frees the inode */ + end_unlink: brelse (bh); - iput (inode); - iput (dir); return retval; } -int ext2_symlink (struct inode * dir, const char * name, int len, - const char * symname) +int ext2_symlink (struct inode * dir, struct dentry *dentry, const char * symname) { struct ext2_dir_entry * de; struct inode * inode = NULL; @@ -761,7 +724,6 @@ int ext2_symlink (struct inode * dir, const char * name, int len, char c; if (!(inode = ext2_new_inode (dir, S_IFLNK, &err))) { - iput (dir); return err; } inode->i_mode = S_IFLNK | S_IRWXUGO; @@ -775,7 +737,6 @@ int ext2_symlink (struct inode * dir, const char * name, int len, name_block = ext2_bread (inode, 0, 1, &err); if (!name_block) { - iput (dir); inode->i_nlink--; inode->i_dirt = 1; iput (inode); @@ -799,21 +760,11 @@ int ext2_symlink (struct inode * dir, const char * name, int len, inode->i_size = i; inode->i_dirt = 1; - bh = ext2_find_entry (dir, name, len, &de); - if (bh) { - inode->i_nlink--; - inode->i_dirt = 1; - iput (inode); - brelse (bh); - iput (dir); - return -EEXIST; - } - bh = ext2_add_entry (dir, name, len, &de, &err); + bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err); if (!bh) { inode->i_nlink--; inode->i_dirt = 1; iput (inode); - iput (dir); return err; } de->inode = cpu_to_le32(inode->i_ino); @@ -824,47 +775,30 @@ int ext2_symlink (struct inode * dir, const char * name, int len, wait_on_buffer (bh); } brelse (bh); - iput (dir); - iput (inode); + d_instantiate(dentry, inode, 0); return 0; } -int ext2_link (struct inode * oldinode, struct inode * dir, - const char * name, int len) +int ext2_link (struct inode * inode, struct inode * dir, struct dentry *dentry) { struct ext2_dir_entry * de; struct buffer_head * bh; int err; - if (S_ISDIR(oldinode->i_mode)) { - iput (oldinode); - iput (dir); + if (S_ISDIR(inode->i_mode)) return -EPERM; - } - if (IS_APPEND(oldinode) || IS_IMMUTABLE(oldinode)) { - iput (oldinode); - iput (dir); + + if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) return -EPERM; - } - if (oldinode->i_nlink >= EXT2_LINK_MAX) { - iput (oldinode); - iput (dir); + + if (inode->i_nlink >= EXT2_LINK_MAX) return -EMLINK; - } - bh = ext2_find_entry (dir, name, len, &de); - if (bh) { - brelse (bh); - iput (dir); - iput (oldinode); - return -EEXIST; - } - bh = ext2_add_entry (dir, name, len, &de, &err); - if (!bh) { - iput (dir); - iput (oldinode); + + bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err); + if (!bh) return err; - } - de->inode = cpu_to_le32(oldinode->i_ino); + + de->inode = cpu_to_le32(inode->i_ino); dir->i_version = ++event; mark_buffer_dirty(bh, 1); if (IS_SYNC(dir)) { @@ -872,11 +806,11 @@ int ext2_link (struct inode * oldinode, struct inode * dir, wait_on_buffer (bh); } brelse (bh); - iput (dir); - oldinode->i_nlink++; - oldinode->i_ctime = CURRENT_TIME; - oldinode->i_dirt = 1; - iput (oldinode); + inode->i_nlink++; + inode->i_ctime = CURRENT_TIME; + inode->i_dirt = 1; + atomic_inc(&inode->i_count); + d_instantiate(dentry, inode, 0); return 0; } @@ -895,7 +829,7 @@ static int subdir (struct inode * new_inode, struct inode * old_inode) if (new_inode->i_dev != old_inode->i_dev) break; ino = new_inode->i_ino; - if (ext2_lookup (new_inode, "..", 2, &new_inode)) + if (ext2_lookup (new_inode, &(struct qstr) { "..", 2, 0 }, &new_inode)) break; if (new_inode->i_ino == ino) break; @@ -923,43 +857,27 @@ static int subdir (struct inode * new_inode, struct inode * old_inode) * Anybody can rename anything with this: the permission checks are left to the * higher-level routines. */ -static int do_ext2_rename (struct inode * old_dir, const char * old_name, - int old_len, struct inode * new_dir, - const char * new_name, int new_len) +static int do_ext2_rename (struct inode * old_dir, struct dentry *old_dentry, + struct inode * new_dir,struct dentry *new_dentry) { struct inode * old_inode, * new_inode; struct buffer_head * old_bh, * new_bh, * dir_bh; struct ext2_dir_entry * old_de, * new_de; int retval; - goto start_up; -try_again: - if (new_bh && new_de) { - ext2_delete_entry(new_de, new_bh); - new_dir->i_version = ++event; - } - brelse (old_bh); - brelse (new_bh); - brelse (dir_bh); - iput (old_inode); - iput (new_inode); - current->counter = 0; - schedule (); -start_up: old_inode = new_inode = NULL; old_bh = new_bh = dir_bh = NULL; new_de = NULL; retval = -ENAMETOOLONG; - if (old_len > EXT2_NAME_LEN) + if (old_dentry->d_name.len > EXT2_NAME_LEN) goto end_rename; - old_bh = ext2_find_entry (old_dir, old_name, old_len, &old_de); + old_bh = ext2_find_entry (old_dir, old_dentry->d_name.name, old_dentry->d_name.len, &old_de); retval = -ENOENT; if (!old_bh) goto end_rename; - old_inode = __iget (old_dir->i_sb, le32_to_cpu(old_de->inode), 0); /* don't cross mnt-points */ - if (!old_inode) - goto end_rename; + old_inode = old_dentry->d_inode; + retval = -EPERM; if ((old_dir->i_mode & S_ISVTX) && current->fsuid != old_inode->i_uid && @@ -967,9 +885,10 @@ start_up: goto end_rename; if (IS_APPEND(old_inode) || IS_IMMUTABLE(old_inode)) goto end_rename; - new_bh = ext2_find_entry (new_dir, new_name, new_len, &new_de); + + new_inode = new_dentry->d_inode; + new_bh = ext2_find_entry (new_dir, new_dentry->d_name.name, new_dentry->d_name.len, &new_de); if (new_bh) { - new_inode = __iget (new_dir->i_sb, le32_to_cpu(new_de->inode), 0); /* no mntp cross */ if (!new_inode) { brelse (new_bh); new_bh = NULL; @@ -1018,29 +937,18 @@ start_up: goto end_rename; } if (!new_bh) - new_bh = ext2_add_entry (new_dir, new_name, new_len, &new_de, + new_bh = ext2_add_entry (new_dir, new_dentry->d_name.name, new_dentry->d_name.len, &new_de, &retval); if (!new_bh) goto end_rename; new_dir->i_version = ++event; - /* - * sanity checking before doing the rename - avoid races - */ - if (new_inode && (le32_to_cpu(new_de->inode) != new_inode->i_ino)) - goto try_again; - if (le32_to_cpu(new_de->inode) && !new_inode) - goto try_again; - if (le32_to_cpu(old_de->inode) != old_inode->i_ino) - goto try_again; + /* * ok, that's it */ new_de->inode = le32_to_cpu(old_inode->i_ino); - retval = ext2_delete_entry (old_de, old_bh); - if (retval == -ENOENT) - goto try_again; - if (retval) - goto end_rename; + ext2_delete_entry (old_de, old_bh); + old_dir->i_version = ++event; if (new_inode) { new_inode->i_nlink--; @@ -1072,15 +980,15 @@ start_up: ll_rw_block (WRITE, 1, &new_bh); wait_on_buffer (new_bh); } + + /* Update the dcache */ + d_move(old_dentry, new_dentry->d_parent, &new_dentry->d_name); + d_delete(new_dentry); retval = 0; end_rename: brelse (dir_bh); brelse (old_bh); brelse (new_bh); - iput (old_inode); - iput (new_inode); - iput (old_dir); - iput (new_dir); return retval; } @@ -1097,16 +1005,15 @@ end_rename: * super-block. This way, we really lock other renames only if they occur * on the same file system */ -int ext2_rename (struct inode * old_dir, const char * old_name, int old_len, - struct inode * new_dir, const char * new_name, int new_len) +int ext2_rename (struct inode * old_dir, struct dentry *old_dentry, + struct inode * new_dir, struct dentry *new_dentry) { int result; while (old_dir->i_sb->u.ext2_sb.s_rename_lock) sleep_on (&old_dir->i_sb->u.ext2_sb.s_rename_wait); old_dir->i_sb->u.ext2_sb.s_rename_lock = 1; - result = do_ext2_rename (old_dir, old_name, old_len, new_dir, - new_name, new_len); + result = do_ext2_rename (old_dir, old_dentry, new_dir, new_dentry); old_dir->i_sb->u.ext2_sb.s_rename_lock = 0; wake_up (&old_dir->i_sb->u.ext2_sb.s_rename_wait); return result; diff --git a/fs/ext2/super.c b/fs/ext2/super.c index 635a45692c04..5a654f686b71 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -632,7 +632,8 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data, */ sb->s_dev = dev; sb->s_op = &ext2_sops; - if (!(sb->s_mounted = iget (sb, EXT2_ROOT_INO))) { + sb->s_root = d_alloc_root(iget(sb, EXT2_ROOT_INO), NULL); + if (!sb->s_root) { sb->s_dev = 0; for (i = 0; i < db_count; i++) if (sb->u.ext2_sb.s_group_desc[i]) diff --git a/fs/ext2/symlink.c b/fs/ext2/symlink.c index 4d5a5cadab5f..654736c0df01 100644 --- a/fs/ext2/symlink.c +++ b/fs/ext2/symlink.c @@ -25,6 +25,7 @@ #include static int ext2_readlink (struct inode *, char *, int); +static struct dentry *ext2_follow_link(struct inode *, struct dentry *); /* * symlinks can't do much... @@ -41,6 +42,7 @@ struct inode_operations ext2_symlink_inode_operations = { NULL, /* mknod */ NULL, /* rename */ ext2_readlink, /* readlink */ + ext2_follow_link, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -49,26 +51,50 @@ struct inode_operations ext2_symlink_inode_operations = { NULL /* smap */ }; +static struct dentry * ext2_follow_link(struct inode * inode, struct dentry *base) +{ + int error; + struct buffer_head * bh = NULL; + char * link; + + link = (char *) inode->u.ext2_i.i_data; + if (inode->i_blocks) { + if (!(bh = ext2_bread (inode, 0, 0, &error))) { + dput(base); + return ERR_PTR(-EIO); + } + link = bh->b_data; + } + if (!IS_RDONLY(inode)) { + inode->i_atime = CURRENT_TIME; + inode->i_dirt = 1; + } + base = lookup_dentry(link, base, 1); + if (bh) + brelse(bh); + return base; +} + static int ext2_readlink (struct inode * inode, char * buffer, int buflen) { struct buffer_head * bh = NULL; char * link; - int i, err; + int i; if (buflen > inode->i_sb->s_blocksize - 1) buflen = inode->i_sb->s_blocksize - 1; + + link = (char *) inode->u.ext2_i.i_data; if (inode->i_blocks) { + int err; bh = ext2_bread (inode, 0, 0, &err); if (!bh) { - iput (inode); if(err < 0) /* indicate type of error */ return err; return 0; } link = bh->b_data; } - else - link = (char *) inode->u.ext2_i.i_data; i = 0; while (i < buflen && link[i]) @@ -79,7 +105,6 @@ static int ext2_readlink (struct inode * inode, char * buffer, int buflen) inode->i_atime = CURRENT_TIME; inode->i_dirt = 1; } - iput (inode); if (bh) brelse (bh); return i; diff --git a/fs/fat/misc.c b/fs/fat/misc.c index 3fdc0be0aef8..dfd06929d287 100644 --- a/fs/fat/misc.c +++ b/fs/fat/misc.c @@ -23,8 +23,8 @@ static char ascii_extensions[] = "TXT" "ME " "HTM" "1ST" "LOG" " " /* text files */ "C " "H " "CPP" "LIS" "PAS" "FOR" /* programming languages */ "F " "MAK" "INC" "BAS" /* programming languages */ - "BAT" "SH" /* program code :) */ - "INI " /* config files */ + "BAT" "SH " /* program code :) */ + "INI" /* config files */ "PBM" "PGM" "DXF" /* graphics */ "TEX"; /* TeX */ diff --git a/fs/inode.c b/fs/inode.c index 7215e12049fb..0f11cde829dd 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -110,7 +110,7 @@ void xcheck(char * txt, struct inode * p) { int i; for(i=xcnt-1; i>=0; i--) - if(xtst[i] == p) + if (xtst[i] == p) return; printk("Bogus inode %p in %s\n", p, txt); } @@ -123,7 +123,7 @@ static inline struct inode * grow_inodes(void) struct inode * res; struct inode * inode = res = (struct inode*)__get_free_page(GFP_KERNEL); int size = PAGE_SIZE; - if(!inode) + if (!inode) return NULL; size -= sizeof(struct inode); @@ -178,7 +178,7 @@ static inline blocking int release_io(struct inode * inode, unsigned short flags int res = 0; vfs_lock(); inode->i_status &= ~flags; - if(inode->i_status & ST_WAITING) { + if (inode->i_status & ST_WAITING) { inode->i_status &= ~ST_WAITING; vfs_unlock(); wake_up(&inode->i_wait); @@ -191,10 +191,10 @@ static inline blocking void _io(void (*op)(struct inode*), struct inode * inode, unsigned short waitflags, unsigned short setflags) { /* Do nothing if the same op is already in progress. */ - if(op && !(inode->i_status & setflags)) { + if (op && !(inode->i_status & setflags)) { set_io(inode, waitflags, setflags); op(inode); - if(release_io(inode, setflags)) { + if (release_io(inode, setflags)) { /* Somebody grabbed my inode from under me. */ #ifdef DEBUG printk("_io grab!\n"); @@ -204,36 +204,10 @@ static inline blocking void _io(void (*op)(struct inode*), struct inode * inode, } } -blocking int _free_ibasket(struct super_block * sb) -{ - if(sb->s_ibasket) { - struct inode * delinquish = sb->s_ibasket->i_basket_prev; -#if 0 -printpath(delinquish->i_dentry); -printk(" delinquish\n"); -#endif - _clear_inode(delinquish, 0, 1); - return 1; - } - return 0; -} - -static /*inline*/ void _put_ibasket(struct inode * inode) -{ - struct super_block * sb = inode->i_sb; - if(!(inode->i_status & ST_IBASKET)) { - inode->i_status |= ST_IBASKET; - insert_ibasket(&sb->s_ibasket, inode); - sb->s_ibasket_count++; - if(sb->s_ibasket_count > sb->s_ibasket_max) - (void)_free_ibasket(sb); - } -} - blocking void _clear_inode(struct inode * inode, int external, int verbose) { xcheck("_clear_inode",inode); - if(inode->i_status & ST_IBASKET) { + if (inode->i_status & ST_IBASKET) { struct super_block * sb = inode->i_sb; remove_ibasket(&sb->s_ibasket, inode); sb->s_ibasket_count--; @@ -243,32 +217,29 @@ printpath(inode->i_dentry); printk(" put_inode\n"); #endif _io(sb->s_op->put_inode, inode, ST_TO_PUT|ST_TO_WRITE, ST_TO_PUT); - if(inode->i_status & ST_EMPTY) + if (inode->i_status & ST_EMPTY) return; } - if(inode->i_status & ST_HASHED) + if (inode->i_status & ST_HASHED) remove_hash(&hashtable[hash(inode->i_dev, inode->i_ino)], inode); - if(inode->i_status & ST_AGED) { + if (inode->i_status & ST_AGED) { /* "cannot happen" when called from an fs because at least * the caller must use it. Can happen when called from * invalidate_inodes(). */ - if(verbose) + if (verbose) printk("VFS: clearing aged inode\n"); - if(atomic_read(&inode->i_count)) + if (atomic_read(&inode->i_count)) printk("VFS: aged inode is in use\n"); remove_lru(&aged_i[inode->i_level], inode); inodes_stat.aged_count[inode->i_level]--; } - if(!external && inode->i_status & ST_IO) { + if (!external && inode->i_status & ST_IO) { printk("VFS: clearing inode during IO operation\n"); } - if(!(inode->i_status & ST_EMPTY)) { + if (!(inode->i_status & ST_EMPTY)) { remove_all(&all_i, inode); inode->i_status = ST_EMPTY; - while(inode->i_dentry) { - d_del(inode->i_dentry, D_NO_CLEAR_INODE); - } - if(inode->i_pages) { + if (inode->i_pages) { vfs_unlock(); /* may block, can that be revised? */ truncate_inode_pages(inode, 0); vfs_lock(); @@ -291,7 +262,7 @@ void insert_inode_hash(struct inode * inode) { xcheck("insert_inode_hash",inode); vfs_lock(); - if(!(inode->i_status & ST_HASHED)) { + if (!(inode->i_status & ST_HASHED)) { insert_hash(&hashtable[hash(inode->i_dev, inode->i_ino)], inode); inode->i_status |= ST_HASHED; } else @@ -306,42 +277,42 @@ blocking struct inode * _get_empty_inode(void) retry: inode = empty_i; - if(inode) { + if (inode) { remove_all(&empty_i, inode); inodes_stat.nr_free_inodes--; } else if(inodes_stat.nr_inodes < max_inodes || retry > 2) { inode = grow_inodes(); } - if(!inode) { + if (!inode) { int level; int usable = 0; for(level = 0; level <= NR_LEVELS; level++) - if(aged_i[level]) { + if (aged_i[level]) { inode = aged_i[level]->i_lru_prev; /* Here is the picking strategy, tune this */ - if(aged_reused[level] < (usable++ ? + if (aged_reused[level] < (usable++ ? inodes_stat.aged_count[level] : 2)) break; aged_reused[level] = 0; } - if(inode) { - if(!(inode->i_status & ST_AGED)) + if (inode) { + if (!(inode->i_status & ST_AGED)) printk("VFS: inode aging inconsistency\n"); - if(atomic_read(&inode->i_count) + inode->i_ddir_count) + if (atomic_read(&inode->i_count)) printk("VFS: i_count of aged inode is not zero\n"); - if(inode->i_dirt) + if (inode->i_dirt) printk("VFS: Hey, somebody made my aged inode dirty\n"); _clear_inode(inode, 0, 0); goto retry; } } - if(!inode) { + if (!inode) { vfs_unlock(); schedule(); - if(retry > 10) + if (retry > 10) panic("VFS: cannot repair inode shortage"); - if(retry > 2) + if (retry > 2) printk("VFS: no free inodes\n"); retry++; vfs_lock(); @@ -363,8 +334,8 @@ static inline blocking struct inode * _get_empty_inode_hashed(dev_t i_dev, { struct inode ** base = &hashtable[hash(i_dev, i_ino)]; struct inode * inode = *base; - if(inode) do { - if(inode->i_ino == i_ino && inode->i_dev == i_dev) { + if (inode) do { + if (inode->i_ino == i_ino && inode->i_dev == i_dev) { atomic_inc(&inode->i_count); printk("VFS: inode %lx is already in use\n", i_ino); return inode; @@ -391,17 +362,17 @@ blocking struct inode * get_empty_inode_hashed(dev_t i_dev, unsigned long i_ino) void _get_inode(struct inode * inode) { - if(inode->i_status & ST_IBASKET) { + if (inode->i_status & ST_IBASKET) { inode->i_status &= ~ST_IBASKET; remove_ibasket(&inode->i_sb->s_ibasket, inode); inode->i_sb->s_ibasket_count--; } - if(inode->i_status & ST_AGED) { + if (inode->i_status & ST_AGED) { inode->i_status &= ~ST_AGED; remove_lru(&aged_i[inode->i_level], inode); inodes_stat.aged_count[inode->i_level]--; aged_reused[inode->i_level]++; - if(S_ISDIR(inode->i_mode)) + if (S_ISDIR(inode->i_mode)) /* make dirs less thrashable */ inode->i_level = NR_LEVELS-1; else if(inode->i_nlink > 1) @@ -410,30 +381,28 @@ void _get_inode(struct inode * inode) else if(++inode->i_reuse_count >= age_table[inode->i_level] && inode->i_level < NR_LEVELS-1) inode->i_level++; - if(atomic_read(&inode->i_count) != 1) - printk("VFS: inode count was not zero\n"); + if (atomic_read(&inode->i_count) != 1) + printk("VFS: inode count was not zero (%d after ++)\n", atomic_read(&inode->i_count)); } else if(inode->i_status & ST_EMPTY) printk("VFS: invalid reuse of empty inode\n"); } -blocking struct inode * __iget(struct super_block * sb, - unsigned long i_ino, - int crossmntp) +blocking struct inode * iget(struct super_block * sb, unsigned long i_ino) { struct inode ** base; struct inode * inode; dev_t i_dev; - if(!sb) + if (!sb) panic("VFS: iget with sb == NULL"); i_dev = sb->s_dev; - if(!i_dev) + if (!i_dev) panic("VFS: sb->s_dev is NULL\n"); base = &hashtable[hash(i_dev, i_ino)]; vfs_lock(); inode = *base; - if(inode) do { - if(inode->i_ino == i_ino && inode->i_dev == i_dev) { + if (inode) do { + if (inode->i_ino == i_ino && inode->i_dev == i_dev) { atomic_inc(&inode->i_count); _get_inode(inode); @@ -455,21 +424,14 @@ blocking struct inode * __iget(struct super_block * sb, inode = _get_empty_inode_hashed(i_dev, i_ino); inode->i_sb = sb; inode->i_flags = sb->s_flags; - if(sb->s_op && sb->s_op->read_inode) { + if (sb->s_op && sb->s_op->read_inode) { set_io(inode, 0, ST_TO_READ); /* do not wait at all */ sb->s_op->read_inode(inode); - if(release_io(inode, ST_TO_READ)) + if (release_io(inode, ST_TO_READ)) goto done; } vfs_unlock(); done: - while(crossmntp && inode->i_mount) { - struct inode * tmp = inode->i_mount; - iinc(tmp); - iput(inode); - inode = tmp; - } -xcheck("_iget",inode); return inode; } @@ -477,68 +439,43 @@ blocking void __iput(struct inode * inode) { struct super_block * sb; xcheck("_iput",inode); - if(atomic_read(&inode->i_count) + inode->i_ddir_count < 0) + + if (atomic_read(&inode->i_count) < 0) printk("VFS: i_count is negative\n"); - if((atomic_read(&inode->i_count) + inode->i_ddir_count) || - (inode->i_status & ST_FREEING)) { + + if (atomic_read(&inode->i_count) || (inode->i_status & ST_FREEING)) return; - } + inode->i_status |= ST_FREEING; -#ifdef CONFIG_OMIRR - if(inode->i_status & ST_MODIFIED) { - inode->i_status &= ~ST_MODIFIED; - omirr_printall(inode, " W %ld ", CURRENT_TIME); - } -#endif - if(inode->i_pipe) { + if (inode->i_pipe) { free_page((unsigned long)PIPE_BASE(*inode)); PIPE_BASE(*inode)= NULL; } - if((sb = inode->i_sb)) { - if(sb->s_type && (sb->s_type->fs_flags & FS_NO_DCACHE)) { - while(inode->i_dentry) - d_del(inode->i_dentry, D_NO_CLEAR_INODE); - if(atomic_read(&inode->i_count) + inode->i_ddir_count) - goto done; - } - if(sb->s_op) { - if(inode->i_nlink <= 0 && inode->i_dent_count && - !(inode->i_status & (ST_EMPTY|ST_IBASKET)) && - (sb->s_type->fs_flags & FS_IBASKET)) { - _put_ibasket(inode); + if ((sb = inode->i_sb)) { + if (sb->s_op) { + if (inode->i_nlink <= 0 && + !(inode->i_status & (ST_EMPTY|ST_IBASKET))) { + _clear_inode(inode, 0, 1); goto done; } - if(!inode->i_dent_count || - (sb->s_type->fs_flags & FS_NO_DCACHE)) { - _io(sb->s_op->put_inode, inode, - ST_TO_PUT|ST_TO_WRITE, ST_TO_PUT); - if(atomic_read(&inode->i_count) + inode->i_ddir_count) - goto done; - if(inode->i_nlink <= 0) { - if(!(inode->i_status & ST_EMPTY)) { - _clear_inode(inode, 0, 1); - } - goto done; - } - } - if(inode->i_dirt) { + if (inode->i_dirt) { inode->i_dirt = 0; _io(sb->s_op->write_inode, inode, ST_TO_PUT|ST_TO_WRITE, ST_TO_WRITE); - if(atomic_read(&inode->i_count) + inode->i_ddir_count) + if (atomic_read(&inode->i_count)) goto done; } } - if(IS_WRITABLE(inode) && sb->dq_op) { + if (IS_WRITABLE(inode) && sb->dq_op) { /* can operate in parallel to other ops ? */ _io(sb->dq_op->drop, inode, 0, ST_TO_DROP); - if(atomic_read(&inode->i_count) + inode->i_ddir_count) + if (atomic_read(&inode->i_count)) goto done; } } - if(inode->i_mmap) + if (inode->i_mmap) printk("VFS: inode has mappings\n"); - if(inode->i_status & ST_AGED) { + if (inode->i_status & ST_AGED) { printk("VFS: reaging inode\n"); #if defined(DEBUG) printpath(inode->i_dentry); @@ -546,11 +483,11 @@ printk("\n"); #endif goto done; } - if(!(inode->i_status & (ST_HASHED|ST_EMPTY))) { + if (!(inode->i_status & (ST_HASHED|ST_EMPTY))) { _clear_inode(inode, 0, 1); goto done; } - if(inode->i_status & ST_EMPTY) { + if (inode->i_status & ST_EMPTY) { printk("VFS: aging an empty inode\n"); goto done; } @@ -573,10 +510,10 @@ blocking void sync_inodes(kdev_t dev) struct inode * inode; vfs_lock(); inode = all_i; - if(inode) do { + if (inode) do { xcheck("sync_inodes",inode); - if(inode->i_dirt && (inode->i_dev == dev || !dev)) { - if(inode->i_sb && inode->i_sb->s_op && + if (inode->i_dirt && (inode->i_dev == dev || !dev)) { + if (inode->i_sb && inode->i_sb->s_op && !(inode->i_status & ST_FREEING)) { inode->i_dirt = 0; _io(inode->i_sb->s_op->write_inode, inode, @@ -596,12 +533,12 @@ blocking int _check_inodes(kdev_t dev, int complain) vfs_lock(); startover: inode = all_i; - if(inode) do { + if (inode) do { struct inode * next; xcheck("_check_inodes",inode); next = inode->i_next; - if(inode->i_dev == dev) { - if(inode->i_dirt || atomic_read(&inode->i_count)) { + if (inode->i_dev == dev) { + if (inode->i_dirt || atomic_read(&inode->i_count)) { bad++; } else { _clear_inode(inode, 0, 0); @@ -609,14 +546,14 @@ xcheck("_check_inodes",inode); /* _clear_inode() may recursively clear other * inodes, probably also the next one. */ - if(next->i_status & ST_EMPTY) + if (next->i_status & ST_EMPTY) goto startover; } } inode = next; } while(inode != all_i); vfs_unlock(); - if(complain && bad) + if (complain && bad) printk("VFS: %d inode(s) busy on removed device `%s'\n", bad, kdevname(dev)); return (bad == 0); @@ -642,16 +579,14 @@ int fs_may_remount_ro(kdev_t dev) return 1; /* not checked any more */ } -int fs_may_umount(kdev_t dev, struct inode * mount_root) +int fs_may_umount(kdev_t dev, struct dentry * root) { struct inode * inode; vfs_lock(); inode = all_i; - if(inode) do { -xcheck("fs_may_umount",inode); - if(inode->i_dev == dev && atomic_read(&inode->i_count)) - if(inode != mount_root || atomic_read(&inode->i_count) > - (inode->i_mount == inode ? 2 : 1)) { + if (inode) do { + if (inode->i_dev == dev && atomic_read(&inode->i_count)) + if (inode != root->d_inode) { vfs_unlock(); return 0; } @@ -668,7 +603,7 @@ blocking struct inode * get_pipe_inode(void) struct inode * inode = get_empty_inode(); PIPE_BASE(*inode) = (char*)__get_free_page(GFP_USER); - if(!(PIPE_BASE(*inode))) { + if (!(PIPE_BASE(*inode))) { iput(inode); return NULL; } @@ -682,21 +617,6 @@ blocking struct inode * get_pipe_inode(void) inode->i_op = &pipe_inode_operations; PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 1; - /* I hope this does not introduce security problems. - * Please check and give me response. - */ - { - char dummyname[32]; - struct qstr dummy = { dummyname, 0 }; - struct dentry * new; - sprintf(dummyname, ".anonymous-pipe-%06lud", inode->i_ino); - dummy.len = strlen(dummyname); - vfs_lock(); - new = d_alloc(the_root, dummy.len, 0); - if(new) - d_add(new, inode, &dummy, D_BASKET); - vfs_unlock(); - } return inode; } diff --git a/fs/minix/namei.c b/fs/minix/namei.c index b6041ad92205..30d52fbe4006 100644 --- a/fs/minix/namei.c +++ b/fs/minix/namei.c @@ -697,7 +697,7 @@ start_up: retval = -ENOENT; if (!old_bh) goto end_rename; - old_inode = __iget(old_dir->i_sb, old_de->inode,0); /* don't cross mnt-points */ + old_inode = __iget(old_dir->i_sb, old_de->inode); if (!old_inode) goto end_rename; retval = -EPERM; @@ -707,7 +707,7 @@ start_up: goto end_rename; new_bh = minix_find_entry(new_dir,new_name,new_len,&new_de); if (new_bh) { - new_inode = __iget(new_dir->i_sb, new_de->inode, 0); + new_inode = __iget(new_dir->i_sb, new_de->inode); if (!new_inode) { brelse(new_bh); new_bh = NULL; diff --git a/fs/namei.c b/fs/namei.c index c95b1a367336..55a135ca1b7d 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -12,7 +12,6 @@ * lookup logic. */ -#include #include #include #include @@ -37,10 +36,6 @@ #undef DEBUG /* some other debugging */ -/* local flags for __namei() */ -#define NAM_SEMLOCK 8 /* set a semlock on the last dir */ -#define NAM_TRANSCREATE 16 /* last component may be created, try "=CREATE#" suffix*/ -#define NAM_NO_TRAILSLASH 32 /* disallow trailing slashes by returning EISDIR */ #define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE]) /* [Feb-1997 T. Schoebel-Theuer] @@ -56,7 +51,7 @@ * is done solely in the VFS level, such that _follow_link() is not * used any more and could be removed in future. As a side effect, * dir_namei(), _namei() and follow_link() are now replaced with a single - * function __namei() that can handle all the special cases of the former + * function lookup_dentry() that can handle all the special cases of the former * code. * * With the new dcache, the pathname is stored at each inode, at least as @@ -95,13 +90,13 @@ static inline char * get_page(void) char * res; down(&quicklock); res = quicklist; - if(res) { + if (res) { #ifdef DEBUG char * tmp = res; int i; for(i=0; i (unsigned long)(-1000)) + inline void putname(char * name) { - if(name) { + if (name) { down(&quicklock); *(char**)name = quicklist; quicklist = name; @@ -160,7 +167,7 @@ int getname(const char * filename, char **result) int retval; tmp = get_page(); - if(!tmp) + if (!tmp) return -ENOMEM; retval = do_getname(filename, tmp); if (retval < 0) @@ -222,417 +229,216 @@ void put_write_access(struct inode * inode) inode->i_writecount--; } -static /*inline */ int concat(struct qstr * name, struct qstr * appendix, char * buf) -{ - int totallen = name->len; - if(name->len > MAX_TRANS_FILELEN || - appendix->len > MAX_TRANS_SUFFIX) { - return -ENAMETOOLONG; - } - memcpy(buf, name->name, name->len); - memcpy(buf + name->len, appendix->name, appendix->len); - totallen += appendix->len; - buf[totallen] = '\0'; - return totallen; -} - -/* Internal lookup() using the new generic dcache. - * buf must only be supplied if appendix!=NULL. +/* + * This is called when everything else fails, and we actually have + * to go to the low-level filesystem to find out what we should do.. + * + * We get the directory semaphore, and after getting that we also + * make sure that nobody added the entry to the dcache in the meantime.. */ -static int cached_lookup(struct inode * dir, struct qstr * name, - struct qstr * appendix, char * buf, - struct qstr * res_name, struct dentry ** res_entry, - struct inode ** result) +static struct dentry * real_lookup(struct dentry * dentry, struct qstr * name) { - struct qstr tmp = { name->name, name->len }; - int error; - struct dentry * cached; + struct inode *dir = dentry->d_inode; + struct dentry *result; + struct inode *inode; + int error = -ENOTDIR; - *result = NULL; - if(name->len >= D_MAXLEN) - return -ENAMETOOLONG; - vfs_lock(); - cached = d_lookup(dir, name, appendix); - if(cached) { - struct inode *inode = NULL; + down(&dir->i_sem); - if(cached->u.d_inode && (inode = d_inode(&cached))) { - error = 0; - if(appendix && res_name) { - tmp.len = error = concat(name, appendix, buf); - tmp.name = buf; - if(error > 0) - error = 0; - } - } else { - error = -ENOENT; + error = -ENOTDIR; + if (dir->i_op && dir->i_op->lookup) + error = dir->i_op->lookup(dir, name, &inode); + result = ERR_PTR(error); + + if (!error || error == -ENOENT) { + struct dentry *new; + int isdir = 0, flags = D_NOCHECKDUP; + + if (error) { + flags = D_NEGATIVE; + inode = NULL; + } else if (S_ISDIR(inode->i_mode)) { + isdir = 1; + flags = D_DIR|D_NOCHECKDUP; } - vfs_unlock(); - if(res_entry) - *res_entry = cached; + new = d_alloc(dentry, name, isdir); - /* Since we are bypassing the iget() mechanism, we have to - * fabricate the act of crossing any mount points. + /* + * Ok, now we can't sleep any more. Double-check that + * nobody else added this in the meantime.. */ - if(!error && inode && inode->i_mount) { - do { - struct inode *mnti = inode->i_mount; - iinc(mnti); - iput(inode); - inode = mnti; - } while(inode->i_mount); + result = d_lookup(dentry, name); + if (result) { + d_free(new); + } else { + d_add(new, inode, flags); + result = new; } - *result = inode; - goto done; - } else - vfs_unlock(); - - if(appendix) { - tmp.len = error = concat(name, appendix, buf); - tmp.name = buf; - if(error < 0) - goto done; - } - atomic_inc(&dir->i_count); - error = dir->i_op->lookup(dir, tmp.name, tmp.len, result); - if(dir->i_dentry && tmp.len && - (!error || (error == -ENOENT && (!dir->i_sb || !dir->i_sb->s_type || - !(dir->i_sb->s_type->fs_flags & FS_NO_DCACHE))))) { - struct dentry * res; - vfs_lock(); - res = d_entry(dir->i_dentry, &tmp, error ? NULL : *result); - vfs_unlock(); - if(res_entry) - *res_entry = res; } -done: - if(res_name) { - if(error) { - res_name->name = name->name; - res_name->len = name->len; - } else { - res_name->name = tmp.name; - res_name->len = tmp.len; - } - } - return error; + up(&dir->i_sem); + return result; +} + +/* Internal lookup() using the new generic dcache. */ +static inline struct dentry * cached_lookup(struct dentry * dentry, struct qstr * name) +{ + return d_lookup(dentry, name); } -#ifdef CONFIG_TRANS_NAMES -/* If a normal filename is seen, try to determine whether a - * "#keyword=context#" file exists and return the new filename. - * If the name is to be created (create_mode), check whether a - * "#keyword=CREATE" name exists and optionally return the corresponding - * context name even if it didn't exist before. +/* + * "." and ".." are special - ".." especially so because it has to be able + * to know about the current root directory and parent relationships */ -static int check_suffixes(struct inode * dir, struct qstr * name, - int create_mode, char * buf, - struct qstr * res_name, struct dentry ** res_entry, - struct inode ** result) +static struct dentry * reserved_lookup(struct dentry * dir, struct qstr * name) { - struct translations * trans; - char * env; - struct qstr * suffixes; - int i; - int error = -ENOENT; - - if(!buf) - panic("buf==NULL"); - env = env_transl(); -#ifdef CONFIG_TRANS_RESTRICT - if(!env && dir->i_gid != CONFIG_TRANS_GID) { - return error; - } -#endif - trans = get_translations(env); - suffixes = create_mode ? trans->c_name : trans->name; - for(i = 0; i < trans->count; i++) { - error = cached_lookup(dir, name, &suffixes[i], - buf, res_name, res_entry, result); - if(!error) { - if(res_name && create_mode) { - /* buf == res_name->name, but is writable */ - memcpy(buf + name->len, - trans->name[i].name, - trans->name[i].len); - res_name->len = name->len + trans->name[i].len; - buf[res_name->len] = '\0'; + struct dentry *result = NULL; + if (name->name[0] == '.') { + switch (name->len) { + default: + break; + case 2: + if (name->name[1] != '.') + break; + + if (dir != current->fs->root) { + dir = dir->d_covers->d_parent; } + /* fallthrough */ + case 1: + result = dget(dir); break; } } - if(env) - free_page((unsigned long)trans); - return error; + return result; } -#endif - -/* Any operations involving reserved names at the VFS level should go here. */ -static /*inline*/ int reserved_lookup(struct inode * dir, struct qstr * name, - int create_mode, char * buf, - struct inode ** result) +static inline int is_reserved(struct dentry *dentry) { - int error = -ENOENT; - if(name->name[0] == '.') { - if(name->len == 1) { - *result = dir; - error = 0; - } else if (name->len==2 && name->name[1] == '.') { - if (dir == current->fs->root) { - *result = dir; - error = 0; - } - else if(dir->i_dentry) { - error = 0; - *result = dir->i_dentry->d_parent->u.d_inode; - if(!*result) { - printk("dcache parent directory is lost"); - error = -ESTALE; /* random error */ - } - } + if (dentry->d_name.name[0] == '.') { + switch (dentry->d_name.len) { + case 2: + if (dentry->d_name.name[1] != '.') + break; + /* fallthrough */ + case 1: + return 1; } - if(!error) - atomic_inc(&(*result)->i_count); } - return error; + return 0; } /* In difference to the former version, lookup() no longer eats the dir. */ -static /*inline*/ int lookup(struct inode * dir, struct qstr * name, int create_mode, - char * buf, struct qstr * res_name, - struct dentry ** res_entry, struct inode ** result) +static struct dentry * lookup(struct dentry * dir, struct qstr * name) { - int perm; - - *result = NULL; - perm = -ENOENT; - if (!dir) - goto done; + int err; + struct dentry * result; /* Check permissions before traversing mount-points. */ - perm = permission(dir,MAY_EXEC); - if (perm) + err = permission(dir->d_inode, MAY_EXEC); + result = ERR_PTR(err); + if (err) goto done; - perm = reserved_lookup(dir, name, create_mode, buf, result); - if(!perm) { - if(res_name) { - res_name->name = name->name; - res_name->len = name->len; - } + + result = reserved_lookup(dir, name); + if (result) goto done; - } - perm = -ENOTDIR; - if (!dir->i_op || !dir->i_op->lookup) + + result = cached_lookup(dir, name); + if (result) goto done; -#ifdef CONFIG_TRANS_NAMES /* try suffixes */ - perm = check_suffixes(dir, name, 0, buf, res_name, res_entry, result); - if(perm) /* try original name */ -#endif - perm = cached_lookup(dir, name, NULL, buf, res_name, res_entry, result); -#ifdef CONFIG_TRANS_NAMES - if(perm == -ENOENT && create_mode) { /* try the =CREATE# suffix */ - struct inode * dummy; - if(!check_suffixes(dir, name, 1, buf, res_name, NULL, &dummy)) { - iput(dummy); - } - } -#endif + + result = real_lookup(dir, name); + if (!result) + result = ERR_PTR(-ENOTDIR); done: - return perm; + if (!IS_ERR(result)) + result = dget(result->d_mounts); + + return result; } -/* [8-Feb-97 T. Schoebel-Theuer] follow_link() modified for generic operation - * on the VFS layer: first call _readlink() and then open_namei(). - * All _follow_link() are not used any more and may be eliminated - * (by Linus; I refrained in order to not break other patches). - * Single exeption is procfs, where proc_follow_link() is used - * internally (and perhaps should be rewritten). - * Note: [partly obsolete] I removed parameters flag and mode, since now - * __namei() is called instead of open_namei(). In the old semantics, - * the _last_ instance of open_namei() did the real create() if O_CREAT was - * set and the name existed already in form of a symlink. This has been - * simplified now, and also the semantics when combined with O_EXCL has changed. - **************************************************************************** - * [13-Feb-97] Complete rewrite -> functionality of reading symlinks factored - * out into _read_link(). The above notes remain valid in principle. +/* + * This should check "link_count", but doesn't do that yet.. */ -static /*inline*/ int _read_link(struct inode * inode, char ** linkname, int loopcount) +static struct dentry * do_follow_link(struct dentry *base, struct dentry *dentry) { - unsigned long old_fs; - int error; + struct inode * inode = dentry->d_inode; - error = -ENOSYS; - if (!inode->i_op || !inode->i_op->readlink) - goto done; - error = -ELOOP; - if (current->link_count + loopcount > 10) - goto done; - error = -ENOMEM; - if(!*linkname && !(*linkname = get_page())) - goto done; - if (DO_UPDATE_ATIME(inode)) { - inode->i_atime = CURRENT_TIME; - inode->i_dirt = 1; - } - atomic_inc(&inode->i_count); - old_fs = get_fs(); - set_fs(KERNEL_DS); - error = inode->i_op->readlink(inode, *linkname, PAGE_SIZE); - set_fs(old_fs); - if(!error) { - error = -ENOENT; /* ? or other error code ? */ - } else if(error > 0) { - (*linkname)[error] = '\0'; - error = 0; + if (inode->i_op && inode->i_op->follow_link) { + struct dentry *result; + + /* This eats the base */ + result = inode->i_op->follow_link(inode, base); + base = dentry; + dentry = result; } -done: - iput(inode); - return error; + dput(base); + return dentry; } -/* [13-Feb-97 T. Schoebel-Theuer] complete rewrite: - * merged dir_name(), _namei() and follow_link() into one new routine - * that obeys all the special cases hidden in the old routines in a - * (hopefully) systematic way: - * parameter retrieve_mode is bitwise or'ed of the ST_* flags. - * if res_inode is a NULL pointer, dont try to retrieve the last component - * at all. Parameters with prefix last_ are used only if res_inode is - * non-NULL and refer to the last component of the path only. +/* + * Name resolution. + * + * This is the basic name resolution function, turning a pathname + * into the final dentry. */ -int __namei(int retrieve_mode, const char * name, struct inode * base, - char * buf, struct inode ** res_dir, struct inode ** res_inode, - struct qstr * last_name, struct dentry ** last_entry, - int * last_error) +struct dentry * lookup_dentry(const char * name, struct dentry * base, int follow_link) { - char c; - struct qstr this; - char * linkname = NULL; - char * oldlinkname = NULL; - int trail_flag = 0; - int loopcount = 0; - int error; -#ifdef DEBUG - if(last_name) { - last_name->name = "(Uninitialized)"; - last_name->len = 15; - } -#endif -again: - error = -ENOENT; - this.name = name; - if (this.name[0] == '/') { - if(base) - iput(base); - if (__prefix_namei(retrieve_mode, this.name, base, buf, - res_dir, res_inode, - last_name, last_entry, last_error) == 0) - return 0; - base = current->fs->root; - atomic_inc(&base->i_count); - this.name++; + struct dentry * dentry; + + if (*name == '/') { + if (base) + dput(base); + base = dget(current->fs->root); + do { + name++; + } while (*name == '/'); } else if (!base) { - base = current->fs->pwd; - atomic_inc(&base->i_count); + base = dget(current->fs->pwd); } + + if (*name == '\0') + return base; + + /* At this point we know we have a real path component. */ for(;;) { - struct inode * inode; - const char * tmp = this.name; int len; + unsigned long hash; + struct qstr this; + char c, trailing; + + this.name = name; + hash = init_name_hash(); + for (len = 0; (c = *name++) && (c != '/') ; len++) + hash = partial_name_hash(c, hash); - for(len = 0; (c = *tmp++) && (c != '/'); len++) ; this.len = len; - if(!c) - break; - while((c = *tmp) == '/') /* remove embedded/trailing slashes */ - tmp++; - if(!c) { - trail_flag = 1; - if(retrieve_mode & NAM_NO_TRAILSLASH) { - error = -EISDIR; - goto alldone; - } - break; - } -#if 0 - if(atomic_read(&base->i_count) == 0) - printk("vor lookup this=%s tmp=%s\n", this.name, tmp); -#endif - error = lookup(base, &this, 0, buf, NULL, NULL, &inode); -#if 0 - if(atomic_read(&base->i_count) == 0) - printk("nach lookup this=%s tmp=%s\n", this.name, tmp); -#endif - if (error) - goto alldone; - if(S_ISLNK(inode->i_mode)) { - error = _read_link(inode, &linkname, loopcount); - if(error) - goto alldone; - current->link_count++; - error = __namei((retrieve_mode & - ~(NAM_SEMLOCK|NAM_TRANSCREATE|NAM_NO_TRAILSLASH)) - | NAM_FOLLOW_LINK, - linkname, base, buf, - &base, &inode, NULL, NULL, NULL); - current->link_count--; - if(error) - goto alldone; - } -#if 0 - if(atomic_read(&base->i_count) == 0) - printk("this=%s tmp=%s\n", this.name, tmp); -#endif - this.name = tmp; - iput(base); - base = inode; - } - if(res_inode) { - if(retrieve_mode & NAM_SEMLOCK) - down(&base->i_sem); - error = lookup(base, &this, retrieve_mode & NAM_TRANSCREATE, - buf, last_name, last_entry, res_inode); - if(!error && S_ISLNK((*res_inode)->i_mode) && - ((retrieve_mode & NAM_FOLLOW_LINK) || - (trail_flag && (retrieve_mode & NAM_FOLLOW_TRAILSLASH)))) { - char * tmp; - - error = _read_link(*res_inode, &linkname, loopcount); - if(error) - goto lastdone; - if(retrieve_mode & NAM_SEMLOCK) - up(&base->i_sem); - /* exchange pages */ - name = tmp = linkname; - linkname = oldlinkname; oldlinkname = tmp; - loopcount++; - goto again; /* Tail recursion elimination "by hand", - * uses less dynamic memory. - */ - - /* Note that trail_flag is not reset, so it - * does not matter in a symlink chain where a - * trailing slash indicates a directory endpoint. - */ - } - if(!error && trail_flag && !S_ISDIR((*res_inode)->i_mode)) { - iput(*res_inode); - error = -ENOTDIR; + this.hash = end_name_hash(hash); + + /* remove trailing slashes? */ + trailing = c; + if (c) { + while ((c = *name) == '/') + name++; } - lastdone: - if(last_error) { - *last_error = error; - error = 0; + + dentry = lookup(base, &this); + if (IS_ERR(dentry)) + break; + if (dentry->d_flag & D_NEGATIVE) + break; + + /* Last component? */ + if (!c) { + if (!trailing && !follow_link) + break; + return do_follow_link(base, dentry); } + + base = do_follow_link(base, dentry); } -alldone: - if(!error && res_dir) - *res_dir = base; - else - iput(base); - putname(linkname); - putname(oldlinkname); - return error; + dput(base); + return dentry; } /* @@ -641,26 +447,55 @@ alldone: * is used by most simple commands to get the inode of a specified name. * Open, link etc use their own routines, but this is enough for things * like 'chmod' etc. + * + * namei exists in two versions: namei/lnamei. The only difference is + * that namei follows links, while lnamei does not. */ - -/* [Feb 1997 T.Schoebel-Theuer] lnamei() completely removed; can be - * simulated when calling with retrieve_mode==NAM_FOLLOW_TRAILSLASH. - */ -int namei(int retrieve_mode, const char *pathname, struct inode **res_inode) +int __namei(const char *pathname, struct inode **res_inode, int follow_link) { int error; - char * tmp; + char * name; - error = getname(pathname, &tmp); + error = getname(pathname, &name); if (!error) { - char buf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2]; - error = __namei(retrieve_mode, tmp, NULL, - buf, NULL, res_inode, NULL, NULL, NULL); - putname(tmp); + struct dentry * dentry; + + dentry = lookup_dentry(name, NULL, follow_link); + putname(name); + error = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + error = -ENOENT; + if (dentry) { + if (!(dentry->d_flag & D_NEGATIVE)) { + struct inode *inode = dentry->d_inode; + atomic_inc(&inode->i_count); + *res_inode = inode; + error = 0; + } + dput(dentry); + } + } } return error; } +static inline struct inode *get_parent(struct dentry *dentry) +{ + struct inode *dir = dentry->d_parent->d_inode; + + atomic_inc(&dir->i_count); + return dir; +} + +static inline struct inode *lock_parent(struct dentry *dentry) +{ + struct inode *dir = dentry->d_parent->d_inode; + + atomic_inc(&dir->i_count); + down(&dir->i_sem); + return dir; +} + /* * open_namei() * @@ -675,172 +510,155 @@ int namei(int retrieve_mode, const char *pathname, struct inode **res_inode) * for symlinks (where the permissions are checked later). */ int open_namei(const char * pathname, int flag, int mode, - struct inode ** res_inode, struct inode * base) + struct inode ** res_inode, struct dentry * base) { - char buf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2]; - struct qstr last; int error; - int lasterror; - struct inode * dir, * inode; - int namei_mode; + struct inode *inode; + struct dentry *dentry; mode &= S_IALLUGO & ~current->fs->umask; mode |= S_IFREG; - namei_mode = NAM_FOLLOW_LINK; - if(flag & O_CREAT) - namei_mode |= NAM_SEMLOCK|NAM_TRANSCREATE|NAM_NO_TRAILSLASH; - error = __namei(namei_mode, pathname, base, buf, - &dir, &inode, &last, NULL, &lasterror); - if (error) - goto exit; - error = lasterror; + dentry = lookup_dentry(pathname, base, 1); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + if (flag & O_CREAT) { - if (!error) { - if (flag & O_EXCL) { + struct inode *dir; + + dir = lock_parent(dentry); + /* + * The existence test must be done _after_ getting the directory + * semaphore - the dentry might otherwise change. + */ + if (!(dentry->d_flag & D_NEGATIVE)) { + error = 0; + if (flag & O_EXCL) error = -EEXIST; - } } else if (IS_RDONLY(dir)) error = -EROFS; else if (!dir->i_op || !dir->i_op->create) error = -EACCES; - else if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) != 0) - ; /* error is already set! */ - else { - d_del(d_lookup(dir, &last, NULL), D_REMOVE); - atomic_inc(&dir->i_count); /* create eats the dir */ + else if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) == 0) { if (dir->i_sb && dir->i_sb->dq_op) dir->i_sb->dq_op->initialize(dir, -1); - error = dir->i_op->create(dir, last.name, last.len, - mode, res_inode); -#ifdef CONFIG_OMIRR - if(!error) - omirr_print(dir->i_dentry, NULL, &last, - " c %ld %d ", CURRENT_TIME, mode); -#endif - up(&dir->i_sem); - goto exit_dir; + error = dir->i_op->create(dir, dentry, mode); } up(&dir->i_sem); + iput(dir); + if (error) + goto exit; } + + error = -ENOENT; + if (dentry->d_flag & D_NEGATIVE) + goto exit; + + inode = dentry->d_inode; + error = -EISDIR; + if (S_ISDIR(inode->i_mode) && (flag & 2)) + goto exit; + + error = permission(inode,ACC_MODE(flag)); if (error) - goto exit_inode; + goto exit; - if (S_ISDIR(inode->i_mode) && (flag & 2)) { - error = -EISDIR; - goto exit_inode; - } - if ((error = permission(inode,ACC_MODE(flag))) != 0) { - goto exit_inode; - } + /* + * FIFO's, sockets and device files are special: they don't + * actually live on the filesystem itself, and as such you + * can write to them even if the filesystem is read-only. + */ if (S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) { - /* - * 2-Feb-1995 Bruce Perens - * Allow opens of Unix domain sockets and FIFOs for write on - * read-only filesystems. Their data does not live on the disk. - * - * If there was something like IS_NODEV(inode) for - * pipes and/or sockets I'd check it here. - */ flag &= ~O_TRUNC; - } - else if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) { - if (IS_NODEV(inode)) { - error = -EACCES; - goto exit_inode; - } + } else if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) { + error = -EACCES; + if (IS_NODEV(inode)) + goto exit; + flag &= ~O_TRUNC; } else { - if (IS_RDONLY(inode) && (flag & 2)) { - error = -EROFS; - goto exit_inode; - } + error = -EROFS; + if (IS_RDONLY(inode) && (flag & 2)) + goto exit; } /* * An append-only file must be opened in append mode for writing. */ - if (IS_APPEND(inode) && ((flag & FMODE_WRITE) && !(flag & O_APPEND))) { - error = -EPERM; - goto exit_inode; - } + error = -EPERM; + if (IS_APPEND(inode) && ((flag & FMODE_WRITE) && !(flag & O_APPEND))) + goto exit; + if (flag & O_TRUNC) { - if ((error = get_write_access(inode))) - goto exit_inode; + error = get_write_access(inode); + if (error) + goto exit; + /* * Refuse to truncate files with mandatory locks held on them. */ error = locks_verify_locked(inode); - if (error) - goto exit_inode; - if (inode->i_sb && inode->i_sb->dq_op) - inode->i_sb->dq_op->initialize(inode, -1); + if (!error) { + if (inode->i_sb && inode->i_sb->dq_op) + inode->i_sb->dq_op->initialize(inode, -1); - error = do_truncate(inode, 0); + error = do_truncate(inode, 0); + } put_write_access(inode); + if (error) + goto exit; } else if (flag & FMODE_WRITE) if (inode->i_sb && inode->i_sb->dq_op) inode->i_sb->dq_op->initialize(inode, -1); -exit_inode: - if(error) { - if(!lasterror) - iput(inode); - } else - *res_inode = inode; -exit_dir: - iput(dir); + + *res_inode = inode; + atomic_inc(&inode->i_count); + error = 0; + exit: + dput(dentry); return error; } int do_mknod(const char * filename, int mode, dev_t dev) { - char buf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2]; - struct qstr last; - int error, lasterror; - struct inode * dir; - struct inode * inode; + int error; + struct inode *dir; + struct dentry *dentry; mode &= ~current->fs->umask; - error = __namei(NAM_FOLLOW_LINK|NAM_TRANSCREATE|NAM_NO_TRAILSLASH, - filename, NULL, buf, - &dir, &inode, &last, NULL, &lasterror); - if (error) + dentry = lookup_dentry(filename, NULL, 1); + + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) goto exit; - if(!lasterror) { - error = -EEXIST; - goto exit_inode; - } - if (!last.len) { - error = -ENOENT; - goto exit_inode; - } - if (IS_RDONLY(dir)) { - error = -EROFS; - goto exit_inode; - } - if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) != 0) - goto exit_inode; - if (!dir->i_op || !dir->i_op->mknod) { - error = -ENOSYS; /* instead of EPERM, what does Posix say? */ - goto exit_inode; - } - atomic_inc(&dir->i_count); + + dir = lock_parent(dentry); + + error = -EEXIST; + if (!(dentry->d_flag & D_NEGATIVE)) + goto exit_lock; + + error = -EROFS; + if (IS_RDONLY(dir)) + goto exit_lock; + + error = permission(dir,MAY_WRITE | MAY_EXEC); + if (error) + goto exit_lock; + + error = -EPERM; + if (!dir->i_op || !dir->i_op->mknod) + goto exit_lock; + if (dir->i_sb && dir->i_sb->dq_op) dir->i_sb->dq_op->initialize(dir, -1); - down(&dir->i_sem); - d_del(d_lookup(dir, &last, NULL), D_REMOVE); - error = dir->i_op->mknod(dir, last.name, last.len, mode, dev); -#ifdef CONFIG_OMIRR - if(!error) - omirr_print(dir->i_dentry, NULL, &last, " n %ld %d %d ", - CURRENT_TIME, mode, dev); -#endif + error = dir->i_op->mknod(dir, dentry, mode, dev); + +exit_lock: up(&dir->i_sem); -exit_inode: - if(!lasterror) - iput(inode); iput(dir); + dput(dentry); exit: return error; } @@ -874,58 +692,48 @@ out: return error; } -/* [Feb-97 T. Schoebel-Theuer] remove_trailing_slashes() is now obsolete, - * its functionality is handled by observing trailing slashes in __namei(). +/* + * Look out: this function may change a normal dentry + * into a directory dentry (different size).. */ static inline int do_mkdir(const char * pathname, int mode) { - char buf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2]; - struct qstr last; - int error, lasterror; - struct inode * dir; - struct inode * inode; + int error; + struct inode *dir; + struct dentry *dentry; - mode &= 0777 & ~current->fs->umask; + dentry = lookup_dentry(pathname, NULL, 1); + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) + goto exit; - error = __namei(NAM_FOLLOW_LINK|NAM_TRANSCREATE, pathname, NULL, buf, - &dir, &inode, &last, NULL, &lasterror); + dir = lock_parent(dentry); + + error = -EEXIST; + if (!(dentry->d_flag & D_NEGATIVE)) + goto exit_lock; + + error = -EROFS; + if (IS_RDONLY(dir)) + goto exit_lock; + + error = permission(dir,MAY_WRITE | MAY_EXEC); if (error) - goto exit; - if(!lasterror) { - error = -EEXIST; - goto exit_inode; - } - if (!last.len) { - error = -ENOENT; - goto exit_inode; - } - if (IS_RDONLY(dir)) { - error = -EROFS; - goto exit_inode; - } - if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) != 0) - goto exit_inode; - if (!dir->i_op || !dir->i_op->mkdir) { - error = -ENOSYS; /* instead of EPERM, what does Posix say? */ - goto exit_inode; - } - atomic_inc(&dir->i_count); + goto exit_lock; + + error = -EPERM; + if (!dir->i_op || !dir->i_op->mkdir) + goto exit_lock; + if (dir->i_sb && dir->i_sb->dq_op) dir->i_sb->dq_op->initialize(dir, -1); - down(&dir->i_sem); - d_del(d_lookup(dir, &last, NULL), D_REMOVE); - mode &= 01777 & ~current->fs->umask; - error = dir->i_op->mkdir(dir, last.name, last.len, mode); -#ifdef CONFIG_OMIRR - if(!error) - omirr_print(dir->i_dentry, NULL, &last, " d %ld %d ", - CURRENT_TIME, mode); -#endif + mode &= 0777 & ~current->fs->umask; + error = dir->i_op->mkdir(dir, dentry, mode); + +exit_lock: up(&dir->i_sem); -exit_inode: - if(!lasterror) - iput(inode); iput(dir); + dput(dentry); exit: return error; } @@ -945,124 +753,55 @@ asmlinkage int sys_mkdir(const char * pathname, int mode) return error; } -#if 0 /* We need a "deletefs", someone please write it. -DaveM */ -/* Perhaps this could be moved out into a new file. */ -static void basket_name(struct inode * dir, struct dentry * entry) -{ - char prefix[32]; - struct qstr prename = { prefix, 14 }; - struct qstr entname = { entry->d_name.name, entry->d_name.len }; - struct inode * inode; - struct dentry * old = entry; /* dummy */ - int i; - if(!entry || !(inode = d_inode(&entry))) - return; -#if 0 - if(atomic_read(&inode->i_count) > 2) { - extern void printpath(struct dentry *entry); - - printk("Caution: in use "); - if(inode->i_dentry) - printpath(inode->i_dentry); - printk(" i_nlink=%d i_count=%d i_ddir_count=%d i_dent_count=%d\n", - inode->i_nlink, atomic_read(&inode->i_count), - inode->i_ddir_count, inode->i_dent_count); - } -#endif - vfs_lock(); - for(i = 1; old; i++) { - sprintf(prefix, ".deleted-%04d.", i); - old = d_lookup(dir, &prename, &entname); - } - d_move(entry, dir, &prename, &entname); - vfs_unlock(); - iput(inode); -} -#endif - static inline int do_rmdir(const char * name) { - char buf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2]; - struct qstr last; - struct dentry * lastent = NULL; int error; - struct inode * dir; - struct inode * inode; - - /* [T.Schoebel-Theuer] I'm not sure which flags to use here. - * Try the following on different platforms: - * [0] rm -rf test test2 - * [1] ln -s test2 test - * [2] mkdir test || mkdir test2 - * [3] rmdir test && mkdir test2 - * [4] rmdir test/ - * Now the rusults: - * cmd | HP-UX | SunOS | Solaris | Old Linux | New Linux | - * ---------------------------------------------------------------- - * [2] | (OK) | EEXIST | EEXIST | EEXIST | (OK) - * [3] | ENOTDIR | ENOTDIR | ENOTDIR | ENOTDIR | ENOTDIR - * [4] | (OK) | EINVAL | ENOTDIR | ENOTDIR | (OK) - * So I implemented the HP-UX semantics. If this is not right - * for Posix compliancy, change the flags accordingly. If Posix - * let the question open, I'd suggest to stay at the new semantics. - * I'd even make case [3] work by adding 2 to the flags parameter - * if Posix tolerates that. - */ - error = __namei(NAM_FOLLOW_TRAILSLASH, name, NULL, buf, - &dir, &inode, &last, &lastent, NULL); - if (error) + struct inode *dir; + struct dentry *dentry; + + dentry = lookup_dentry(name, NULL, 1); + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) goto exit; - if (IS_RDONLY(dir)) { - error = -EROFS; - goto exit_dir; - } - if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) != 0) - goto exit_dir; + + dir = lock_parent(dentry); + error = -ENOENT; + if (dentry->d_flag & D_NEGATIVE) + goto exit_lock; + + error = -EROFS; + if (IS_RDONLY(dir)) + goto exit_lock; + + error = permission(dir,MAY_WRITE | MAY_EXEC); + if (error) + goto exit_lock; + /* * A subdirectory cannot be removed from an append-only directory. */ - if (IS_APPEND(dir)) { - error = -EPERM; - goto exit_dir; - } - if (!dir->i_op || !dir->i_op->rmdir) { - error = -ENOSYS; /* was EPERM */ - goto exit_dir; - } + error = -EPERM; + if (IS_APPEND(dir)) + goto exit_lock; + /* Disallow removals of mountpoints. */ - if(inode->i_mount) { - error = -EBUSY; - goto exit_dir; - } + error = -EBUSY; + if (dentry->d_covers != dentry) + goto exit_lock; + + error = -EPERM; + if (!dir->i_op || !dir->i_op->rmdir) + goto exit_lock; + if (dir->i_sb && dir->i_sb->dq_op) dir->i_sb->dq_op->initialize(dir, -1); - down(&dir->i_sem); -#if 0 - if(lastent && d_isbasket(lastent)) { - d_del(lastent, D_REMOVE); - error = 0; - goto exit_lock; - } -#endif - atomic_inc(&dir->i_count); - error = dir->i_op->rmdir(dir, last.name, last.len); -#ifdef CONFIG_OMIRR - if(!error) - omirr_print(lastent, NULL, NULL, " r %ld ", CURRENT_TIME); -#endif -#if 0 - if(!error && lastent) - basket_name(dir, lastent); + error = dir->i_op->rmdir(dir, dentry); + exit_lock: -#else - if(!error && lastent) - d_del(lastent, D_REMOVE); -#endif up(&dir->i_sem); -exit_dir: - iput(inode); - iput(dir); + iput(dir); + dput(dentry); exit: return error; } @@ -1084,90 +823,45 @@ asmlinkage int sys_rmdir(const char * pathname) static inline int do_unlink(const char * name) { - char buf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2]; - struct qstr last; - struct dentry * lastent = NULL; int error; - struct inode * dir; - struct inode * inode; - - /* HP-UX shows a strange behaviour: - * touch y; ln -s y x; rm x/ - * this succeeds and removes the file y, not the symlink x! - * Solaris and old Linux remove the symlink instead, and - * old SunOS complains ENOTDIR. - * I chose the SunOS behaviour (by not using NAM_FOLLOW_TRAILSLASH), - * but I'm not shure whether I should. - * The current code generally prohibits using trailing slashes with - * non-directories if the name already exists, but not if - * it is to be newly created. - * Perhaps this should be further strengthened (by introducing - * an additional flag bit indicating whether trailing slashes are - * allowed) to get it as consistant as possible, but I don't know - * what Posix says. - */ - error = __namei(NAM_NO_TRAILSLASH, name, NULL, buf, - &dir, &inode, &last, &lastent, NULL); - if (error) + struct inode *dir; + struct dentry *dentry; + + dentry = lookup_dentry(name, NULL, 0); + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) goto exit; - if (IS_RDONLY(dir)) { - error = -EROFS; - goto exit_dir; - } - if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) != 0) - goto exit_dir; + + dir = lock_parent(dentry); + + error = -EROFS; + if (IS_RDONLY(dir)) + goto exit_lock; + + error = permission(dir,MAY_WRITE | MAY_EXEC); + if (error) + goto exit_lock; + /* * A file cannot be removed from an append-only directory. */ - if (IS_APPEND(dir)) { - error = -EPERM; - goto exit_dir; - } - if (!dir->i_op || !dir->i_op->unlink) { - error = -ENOSYS; /* was EPERM */ - goto exit_dir; - } + error = -EPERM; + if (IS_APPEND(dir)) + goto exit_lock; + + error = -EPERM; + if (!dir->i_op || !dir->i_op->unlink) + goto exit_lock; + if (dir->i_sb && dir->i_sb->dq_op) dir->i_sb->dq_op->initialize(dir, -1); - down(&dir->i_sem); -#if 0 - if(atomic_read(&inode->i_count) > 1) { - extern void printpath(struct dentry *entry); - - printk("Fire "); - if(lastent) - printpath(lastent); - printk(" i_nlink=%d i_count=%d i_ddir_count=%d i_dent_count=%d\n", - inode->i_nlink, atomic_read(&inode->i_count), - inode->i_ddir_count, inode->i_dent_count); - } -#endif -#if 0 - if(lastent && d_isbasket(lastent)) { - d_del(lastent, D_REMOVE); - error = 0; - goto exit_lock; - } -#endif - atomic_inc(&dir->i_count); - error = dir->i_op->unlink(dir, last.name, last.len); -#ifdef CONFIG_OMIRR - if(!error) - omirr_print(lastent, NULL, NULL, " u %ld ", CURRENT_TIME); -#endif -#if 0 - if(!error && lastent) - basket_name(dir, lastent); + error = dir->i_op->unlink(dir, dentry); + exit_lock: -#else - if(!error && lastent) - d_del(lastent, D_REMOVE); -#endif up(&dir->i_sem); -exit_dir: - iput(inode); - iput(dir); + iput(dir); + dput(dentry); exit: return error; } @@ -1189,62 +883,42 @@ asmlinkage int sys_unlink(const char * pathname) static inline int do_symlink(const char * oldname, const char * newname) { - char buf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2]; - struct qstr last; - int error, lasterror; - struct inode * dir; - struct inode * inode; - - /* The following works on HP-UX and Solaris, by producing - * a symlink chain: - * rm -rf ? ; mkdir z ; ln -s z y ; ln -s y x/ - * Under old SunOS, the following occurs: - * ln: x/: No such file or directory - * Under old Linux, very strange things occur: - * ln: cannot create symbolic link `x//y' to `y': No such file or directory - * This is very probably a bug, but may be caused by the ln program - * when checking for a directory target. - * - * I'm not shure whether to add NAM_NO_TRAILSLASH to inhibit trailing - * slashes in the target generally. - */ - error = __namei(NAM_TRANSCREATE, newname, NULL, buf, - &dir, &inode, &last, NULL, &lasterror); - if (error) + int error; + struct inode *dir; + struct dentry *dentry; + + dentry = lookup_dentry(newname, NULL, 0); + + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) goto exit; - if(!lasterror) { - iput(inode); - error = -EEXIST; - goto exit_dir; - } - if (!last.len) { - error = -ENOENT; - goto exit_dir; - } - if (IS_RDONLY(dir)) { - error = -EROFS; - goto exit_dir; - } - if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) != 0) - goto exit_dir; - if (!dir->i_op || !dir->i_op->symlink) { - error = -ENOSYS; /* was EPERM */ - goto exit_dir; - } - atomic_inc(&dir->i_count); + + error = -EEXIST; + if (!(dentry->d_flag & D_NEGATIVE)) + goto exit; + + dir = lock_parent(dentry); + + error = -EROFS; + if (IS_RDONLY(dir)) + goto exit_lock; + + error = permission(dir,MAY_WRITE | MAY_EXEC); + if (error) + goto exit_lock; + + error = -EPERM; + if (!dir->i_op || !dir->i_op->symlink) + goto exit_lock; + if (dir->i_sb && dir->i_sb->dq_op) dir->i_sb->dq_op->initialize(dir, -1); - down(&dir->i_sem); - d_del(d_lookup(dir, &last, NULL), D_REMOVE); - error = dir->i_op->symlink(dir, last.name, last.len, oldname); -#ifdef CONFIG_OMIRR - if(!error) - omirr_print(dir->i_dentry, NULL, &last, - " s %ld %s\0", CURRENT_TIME, oldname); -#endif + error = dir->i_op->symlink(dir, dentry, oldname); + +exit_lock: up(&dir->i_sem); -exit_dir: iput(dir); + dput(dentry); exit: return error; } @@ -1270,73 +944,64 @@ asmlinkage int sys_symlink(const char * oldname, const char * newname) static inline int do_link(const char * oldname, const char * newname) { - char oldbuf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2]; - char newbuf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2]; - struct qstr oldlast; - struct qstr newlast; - struct dentry * oldent = NULL; - struct inode * oldinode; - struct inode * newinode; - struct inode * newdir; - int error, lasterror; - - error = __namei(NAM_FOLLOW_LINK|NAM_NO_TRAILSLASH, - oldname, NULL, oldbuf, - NULL, &oldinode, &oldlast, &oldent, NULL); - if (error) + struct dentry *old_dentry, *new_dentry; + struct inode *dir, *inode; + int error; + + old_dentry = lookup_dentry(oldname, NULL, 1); + error = PTR_ERR(old_dentry); + if (IS_ERR(old_dentry)) goto exit; - error = __namei(NAM_FOLLOW_LINK|NAM_TRANSCREATE, newname, NULL, newbuf, - &newdir, &newinode, &newlast, NULL, &lasterror); + new_dentry = lookup_dentry(newname, NULL, 1); + error = PTR_ERR(new_dentry); + if (IS_ERR(new_dentry)) + goto exit_old; + + dir = lock_parent(new_dentry); + + error = -ENOENT; + if (old_dentry->d_flag & D_NEGATIVE) + goto exit_lock; + + error = -EEXIST; + if (!(new_dentry->d_flag & D_NEGATIVE)) + goto exit_lock; + + error = -EROFS; + if (IS_RDONLY(dir)) + goto exit_lock; + + inode = old_dentry->d_inode; + error = -EXDEV; + if (dir->i_dev != inode->i_dev) + goto exit_lock; + + error = permission(dir, MAY_WRITE | MAY_EXEC); if (error) - goto old_exit; - if(!lasterror) { - iput(newinode); - error = -EEXIST; - goto new_exit; - } - if (!newlast.len) { - error = -EPERM; - goto new_exit; - } - if (IS_RDONLY(newdir)) { - error = -EROFS; - goto new_exit; - } - if (newdir->i_dev != oldinode->i_dev) { - error = -EXDEV; - goto new_exit; - } - if ((error = permission(newdir,MAY_WRITE | MAY_EXEC)) != 0) - goto new_exit; + goto exit_lock; + /* * A link to an append-only or immutable file cannot be created. */ - if (IS_APPEND(oldinode) || IS_IMMUTABLE(oldinode)) { - error = -EPERM; - goto new_exit; - } - if (!newdir->i_op || !newdir->i_op->link) { - error = -ENOSYS; /* was EPERM */ - goto new_exit; - } - atomic_inc(&oldinode->i_count); - atomic_inc(&newdir->i_count); - if (newdir->i_sb && newdir->i_sb->dq_op) - newdir->i_sb->dq_op->initialize(newdir, -1); - down(&newdir->i_sem); - d_del(d_lookup(newdir, &newlast, NULL), D_REMOVE); - error = newdir->i_op->link(oldinode, newdir, newlast.name, newlast.len); -#ifdef CONFIG_OMIRR - if(!error) - omirr_print(oldent, newdir->i_dentry, &newlast, - " l %ld ", CURRENT_TIME); -#endif - up(&newdir->i_sem); -new_exit: - iput(newdir); -old_exit: - iput(oldinode); + error = -EPERM; + if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) + goto exit_lock; + + error = -EPERM; + if (!dir->i_op || !dir->i_op->link) + goto exit_lock; + + if (dir->i_sb && dir->i_sb->dq_op) + dir->i_sb->dq_op->initialize(dir, -1); + error = dir->i_op->link(inode, dir, new_dentry); + +exit_lock: + up(&dir->i_sem); + iput(dir); + dput(new_dentry); +exit_old: + dput(old_dentry); exit: return error; } @@ -1360,105 +1025,98 @@ asmlinkage int sys_link(const char * oldname, const char * newname) return error; } +/* + * Whee.. Deadlock country. Happily there is only one VFS + * operation that does this.. + */ +static inline void double_down(struct semaphore *s1, struct semaphore *s2) +{ + if ((unsigned long) s1 < (unsigned long) s2) { + down(s1); + down(s2); + } else if (s1 == s2) { + down(s1); + atomic_dec(&s1->count); + } else { + down(s2); + down(s1); + } +} + static inline int do_rename(const char * oldname, const char * newname) { - char oldbuf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2]; - struct qstr oldlast; - char newbuf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2]; - struct qstr newlast; - struct dentry * oldent = NULL; - struct inode * olddir, * newdir; - struct inode * oldinode, * newinode; - int error, newlasterror; - - error = __namei(NAM_FOLLOW_TRAILSLASH, oldname, NULL, oldbuf, - &olddir, &oldinode, &oldlast, &oldent, NULL); - if (error) + int error; + struct inode * old_dir, * new_dir; + struct dentry * old_dentry, *new_dentry; + + old_dentry = lookup_dentry(oldname, NULL, 1); + + error = PTR_ERR(old_dentry); + if (IS_ERR(old_dentry)) goto exit; - if ((error = permission(olddir,MAY_WRITE | MAY_EXEC)) != 0) - goto old_exit; - if (!oldlast.len || (oldlast.name[0] == '.' && - (oldlast.len == 1 || (oldlast.name[1] == '.' && - oldlast.len == 2)))) { - error = -EPERM; - goto old_exit; - } - /* Disallow moves of mountpoints. */ - if(oldinode->i_mount) { - error = -EBUSY; - goto old_exit; - } - error = __namei(NAM_FOLLOW_LINK|NAM_TRANSCREATE, newname, NULL, newbuf, - &newdir, &newinode, &newlast, NULL, &newlasterror); + new_dentry = lookup_dentry(newname, NULL, 1); + + error = PTR_ERR(new_dentry); + if (IS_ERR(new_dentry)) + goto exit_old; + + new_dir = get_parent(new_dentry); + old_dir = get_parent(old_dentry); + + double_down(&new_dir->i_sem, &old_dir->i_sem); + + error = -ENOENT; + if (old_dentry->d_flag & D_NEGATIVE) + goto exit_lock; + + error = permission(old_dir,MAY_WRITE | MAY_EXEC); if (error) - goto old_exit; - if ((error = permission(newdir,MAY_WRITE | MAY_EXEC)) != 0) - goto new_exit; - if (!newlast.len || (newlast.name[0] == '.' && - (newlast.len == 1 || (newlast.name[1] == '.' && - newlast.len == 2)))) { - error = -EPERM; - goto new_exit; - } - if (newdir->i_dev != olddir->i_dev) { - error = -EXDEV; - goto new_exit; - } - if (IS_RDONLY(newdir) || IS_RDONLY(olddir)) { - error = -EROFS; - goto new_exit; - } + goto exit_lock; + error = permission(new_dir,MAY_WRITE | MAY_EXEC); + if (error) + goto exit_lock; + + error = -EPERM; + if (is_reserved(new_dentry) || is_reserved(old_dentry)) + goto exit_lock; + + /* Disallow moves of mountpoints. */ + error = -EBUSY; + if (old_dentry->d_covers != old_dentry) + goto exit_lock; + + error = -EXDEV; + if (new_dir->i_dev != old_dir->i_dev) + goto exit_lock; + + error = -EROFS; + if (IS_RDONLY(new_dir) || IS_RDONLY(old_dir)) + goto exit_lock; + /* * A file cannot be removed from an append-only directory. */ - if (IS_APPEND(olddir)) { - error = -EPERM; - goto new_exit; - } - if (!olddir->i_op || !olddir->i_op->rename) { - error = -ENOSYS; /* was EPERM */ - goto new_exit; - } -#ifdef CONFIG_TRANS_NAMES - /* if oldname has been translated, but newname not (and - * has not already a suffix), take over the suffix from oldname. - */ - if(oldlast.name == oldbuf && newlast.name != newbuf && - newlast.name[newlast.len-1] != '#') { - int i = oldlast.len - 2; - while (i > 0 && oldlast.name[i] != '#') - i--; - memcpy(newbuf, newlast.name, newlast.len); - memcpy(newbuf+newlast.len, oldlast.name+i, oldlast.len - i); - newlast.len += oldlast.len - i; - newlast.name = newbuf; - } -#endif - atomic_inc(&olddir->i_count); - atomic_inc(&newdir->i_count); - if (newdir->i_sb && newdir->i_sb->dq_op) - newdir->i_sb->dq_op->initialize(newdir, -1); - down(&newdir->i_sem); - error = olddir->i_op->rename(olddir, oldlast.name, oldlast.len, - newdir, newlast.name, newlast.len); -#ifdef CONFIG_OMIRR - if(!error) - omirr_print(oldent, newdir->i_dentry, &newlast, - " m %ld ", CURRENT_TIME); -#endif - if(!error) { - d_del(d_lookup(newdir, &newlast, NULL), D_REMOVE); - d_move(d_lookup(olddir, &oldlast, NULL), newdir, &newlast, NULL); - } - up(&newdir->i_sem); -new_exit: - if(!newlasterror) - iput(newinode); - iput(newdir); -old_exit: - iput(oldinode); - iput(olddir); + error = -EPERM; + if (IS_APPEND(old_dir)) + goto exit_lock; + + error = -EPERM; + if (!old_dir->i_op || !old_dir->i_op->rename) + goto exit_lock; + + if (new_dir->i_sb && new_dir->i_sb->dq_op) + new_dir->i_sb->dq_op->initialize(new_dir, -1); + error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry); + +exit_lock: + up(&new_dir->i_sem); + up(&old_dir->i_sem); + iput(old_dir); + iput(new_dir); + dput(new_dentry); +exit_old: + dput(old_dentry); exit: return error; } diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 71835c2556db..aa0d1345baaa 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -42,16 +42,15 @@ struct nfs_dirent { static int nfs_dir_open(struct inode * inode, struct file * file); static long nfs_dir_read(struct inode *, struct file *, char *, unsigned long); static int nfs_readdir(struct inode *, struct file *, void *, filldir_t); -static int nfs_lookup(struct inode *, const char *, int, struct inode **); -static int nfs_create(struct inode *, const char *, int, int, struct inode **); -static int nfs_mkdir(struct inode *, const char *, int, int); -static int nfs_rmdir(struct inode *, const char *, int); -static int nfs_unlink(struct inode *, const char *, int); -static int nfs_symlink(struct inode *, const char *, int, const char *); -static int nfs_link(struct inode *, struct inode *, const char *, int); -static int nfs_mknod(struct inode *, const char *, int, int, int); -static int nfs_rename(struct inode *, const char *, int, - struct inode *, const char *, int); +static int nfs_lookup(struct inode *, struct qstr *, struct inode **); +static int nfs_create(struct inode *, struct dentry *, int); +static int nfs_mkdir(struct inode *, struct dentry *, int); +static int nfs_rmdir(struct inode *, struct dentry *); +static int nfs_unlink(struct inode *, struct dentry *); +static int nfs_symlink(struct inode *, struct dentry *, const char *); +static int nfs_link(struct inode *, struct inode *, struct dentry *); +static int nfs_mknod(struct inode *, struct dentry *, int, int); +static int nfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *); static struct file_operations nfs_dir_operations = { NULL, /* lseek - default */ @@ -78,6 +77,7 @@ struct inode_operations nfs_dir_inode_operations = { nfs_mknod, /* mknod */ nfs_rename, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -328,488 +328,268 @@ nfs_free_dircache(void) } -/* - * Lookup caching is a big win for performance but this is just - * a trial to see how well it works on a small scale. - * For example, bash does a lookup on ".." 13 times for each path - * element when running pwd. Yes, hard to believe but true. - * Try pwd in a filesystem mounted with noac. - * - * It trades a little cpu time and memory for a lot of network bandwidth. - * Since the cache is not hashed yet, it is a good idea not to make it too - * large because every lookup looks through the entire cache even - * though most of them will fail. - * - * FIXME: The lookup cache should also cache failed lookups. This can - * be a considerable win on diskless clients. - */ - -static struct nfs_lookup_cache_entry { - kdev_t dev; - ino_t inode; - char filename[NFS_MAXNAMLEN + 1]; - struct nfs_fh fhandle; - struct nfs_fattr fattr; - unsigned long expiration_date; -} nfs_lookup_cache[NFS_LOOKUP_CACHE_SIZE]; - -static struct nfs_lookup_cache_entry *nfs_lookup_cache_index(struct inode *dir, - const char *filename) -{ - struct nfs_lookup_cache_entry *entry; - int i; - - for (i = 0; i < NFS_LOOKUP_CACHE_SIZE; i++) { - entry = nfs_lookup_cache + i; - if (entry->dev == dir->i_dev - && entry->inode == dir->i_ino - && !strncmp(filename, entry->filename, NFS_MAXNAMLEN)) - return entry; - } - return NULL; -} - -static int nfs_lookup_cache_lookup(struct inode *dir, const char *filename, - struct nfs_fh *fhandle, - struct nfs_fattr *fattr) -{ - static int nfs_lookup_cache_in_use = 0; - - struct nfs_lookup_cache_entry *entry; - - dfprintk(LOOKUPCACHE, "NFS: lookup_cache_lookup(%x/%ld, %s)\n", - dir->i_dev, dir->i_ino, filename); - if (!nfs_lookup_cache_in_use) { - memset(nfs_lookup_cache, 0, sizeof(nfs_lookup_cache)); - nfs_lookup_cache_in_use = 1; - } - if ((entry = nfs_lookup_cache_index(dir, filename))) { - if (jiffies > entry->expiration_date) { - entry->dev = 0; - return 0; - } - *fhandle = entry->fhandle; - *fattr = entry->fattr; - return 1; - } - return 0; -} - -static void nfs_lookup_cache_add(struct inode *dir, const char *filename, - struct nfs_fh *fhandle, - struct nfs_fattr *fattr) -{ - static int nfs_lookup_cache_pos = 0; - struct nfs_lookup_cache_entry *entry; - - dfprintk(LOOKUPCACHE, "NFS: lookup_cache_add(%x/%ld, %s\n", - dir->i_dev, dir->i_ino, filename); - - /* compensate for bug in SGI NFS server */ - if (fattr->size == -1 || fattr->uid == -1 || fattr->gid == -1 - || fattr->atime.seconds == -1 || fattr->mtime.seconds == -1) - return; - if (!(entry = nfs_lookup_cache_index(dir, filename))) { - entry = nfs_lookup_cache + nfs_lookup_cache_pos++; - if (nfs_lookup_cache_pos == NFS_LOOKUP_CACHE_SIZE) - nfs_lookup_cache_pos = 0; - } - - entry->dev = dir->i_dev; - entry->inode = dir->i_ino; - strcpy(entry->filename, filename); - entry->fhandle = *fhandle; - entry->fattr = *fattr; - entry->expiration_date = jiffies + (S_ISDIR(fattr->mode) - ? NFS_SERVER(dir)->acdirmin : NFS_SERVER(dir)->acregmin); -} - -static void nfs_lookup_cache_remove(struct inode *dir, struct inode *inode, - const char *filename) -{ - struct nfs_lookup_cache_entry *entry; - kdev_t dev; - ino_t fileid; - int i; - - if (inode) { - dev = inode->i_dev; - fileid = inode->i_ino; - } - else if ((entry = nfs_lookup_cache_index(dir, filename))) { - dev = entry->dev; - fileid = entry->fattr.fileid; - } - else - return; - - dfprintk(LOOKUPCACHE, "NFS: lookup_cache_remove(%x/%ld)\n", - dev, (long)fileid); - - for (i = 0; i < NFS_LOOKUP_CACHE_SIZE; i++) { - entry = nfs_lookup_cache + i; - if (entry->dev == dev && entry->fattr.fileid == fileid) - entry->dev = 0; - } -} - -static void nfs_lookup_cache_refresh(struct inode *file, - struct nfs_fattr *fattr) -{ - struct nfs_lookup_cache_entry *entry; - kdev_t dev = file->i_dev; - int fileid = file->i_ino; - int i; - - for (i = 0; i < NFS_LOOKUP_CACHE_SIZE; i++) { - entry = nfs_lookup_cache + i; - if (entry->dev == dev && entry->fattr.fileid == fileid) - entry->fattr = *fattr; - } -} - -static int nfs_lookup(struct inode *dir, const char *__name, int len, +static int nfs_lookup(struct inode *dir, struct qstr * __name, struct inode **result) { struct nfs_fh fhandle; struct nfs_fattr fattr; + int len = __name->len; char name[len > NFS_MAXNAMLEN? 1 : len+1]; int error; dfprintk(VFS, "NFS: lookup(%x/%ld, %.*s)\n", - dir->i_dev, dir->i_ino, len, __name); + dir->i_dev, dir->i_ino, len, __name->name); *result = NULL; if (!dir || !S_ISDIR(dir->i_mode)) { printk("nfs_lookup: inode is NULL or not a directory\n"); - iput(dir); return -ENOENT; } - if (len > NFS_MAXNAMLEN) { - iput(dir); + + if (len > NFS_MAXNAMLEN) return -ENAMETOOLONG; - } - memcpy(name,__name,len); + + memcpy(name,__name->name,len); name[len] = '\0'; - if (len == 0 || (len == 1 && name[0] == '.')) { /* cheat for "" and "." */ - *result = dir; - return 0; - } - if ((NFS_SERVER(dir)->flags & NFS_MOUNT_NOAC) - || !nfs_lookup_cache_lookup(dir, name, &fhandle, &fattr)) { - if ((error = nfs_proc_lookup(NFS_SERVER(dir), NFS_FH(dir), - name, &fhandle, &fattr))) { - iput(dir); - return error; - } - nfs_lookup_cache_add(dir, name, &fhandle, &fattr); - } - if (!(*result = nfs_fhget(dir->i_sb, &fhandle, &fattr))) { - iput(dir); + + error = nfs_proc_lookup(NFS_SERVER(dir), NFS_FH(dir), + name, &fhandle, &fattr); + if (error) + return error; + + if (!(*result = nfs_fhget(dir->i_sb, &fhandle, &fattr))) return -EACCES; - } - iput(dir); + return 0; } -static int nfs_create(struct inode *dir, const char *name, int len, int mode, - struct inode **result) +static int nfs_create(struct inode *dir, struct dentry * dentry, int mode) { struct nfs_sattr sattr; struct nfs_fattr fattr; struct nfs_fh fhandle; + struct inode *inode; int error; dfprintk(VFS, "NFS: create(%x/%ld, %s\n", - dir->i_dev, dir->i_ino, name); + dir->i_dev, dir->i_ino, dentry->d_name.name); - *result = NULL; if (!dir || !S_ISDIR(dir->i_mode)) { printk("nfs_create: inode is NULL or not a directory\n"); - iput(dir); return -ENOENT; } - if (len > NFS_MAXNAMLEN) { - iput(dir); + + if (dentry->d_name.len > NFS_MAXNAMLEN) return -ENAMETOOLONG; - } + sattr.mode = mode; sattr.uid = sattr.gid = sattr.size = (unsigned) -1; sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1; - if ((error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir), - name, &sattr, &fhandle, &fattr))) { - iput(dir); + error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir), + dentry->d_name.name, &sattr, &fhandle, &fattr); + + if (error) return error; - } - if (!(*result = nfs_fhget(dir->i_sb, &fhandle, &fattr))) { - iput(dir); + + inode = nfs_fhget(dir->i_sb, &fhandle, &fattr); + if (!inode) return -EACCES; - } - nfs_lookup_cache_add(dir, name, &fhandle, &fattr); - nfs_invalidate_dircache(dir); - iput(dir); + + d_instantiate(dentry, inode, 0); return 0; } -static int nfs_mknod(struct inode *dir, const char *name, int len, - int mode, int rdev) +static int nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev) { struct nfs_sattr sattr; struct nfs_fattr fattr; struct nfs_fh fhandle; + struct inode *inode; int error; dfprintk(VFS, "NFS: mknod(%x/%ld, %s\n", - dir->i_dev, dir->i_ino, name); + dir->i_dev, dir->i_ino, dentry->d_name.name); if (!dir || !S_ISDIR(dir->i_mode)) { printk("nfs_mknod: inode is NULL or not a directory\n"); - iput(dir); return -ENOENT; } - if (len > NFS_MAXNAMLEN) { - iput(dir); + + if (dentry->d_name.len > NFS_MAXNAMLEN) return -ENAMETOOLONG; - } + sattr.mode = mode; - sattr.uid = sattr.gid = (unsigned) -1; + sattr.uid = sattr.gid = sattr.size = (unsigned) -1; if (S_ISCHR(mode) || S_ISBLK(mode)) sattr.size = rdev; /* get out your barf bag */ - else - sattr.size = (unsigned) -1; + sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1; error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir), - name, &sattr, &fhandle, &fattr); - if (!error) - nfs_lookup_cache_add(dir, name, &fhandle, &fattr); - nfs_invalidate_dircache(dir); - iput(dir); - return error; + dentry->d_name.name, &sattr, &fhandle, &fattr); + + if (error) + return error; + + inode = nfs_fhget(dir->i_sb, &fhandle, &fattr); + if (!inode) + return -EACCES; + + d_instantiate(dentry, inode, 0); + return 0; } -static int nfs_mkdir(struct inode *dir, const char *name, int len, int mode) +static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) { struct nfs_sattr sattr; struct nfs_fattr fattr; struct nfs_fh fhandle; + struct inode * inode; int error; dfprintk(VFS, "NFS: mkdir(%x/%ld, %s\n", - dir->i_dev, dir->i_ino, name); + dir->i_dev, dir->i_ino, dentry->d_name.name); if (!dir || !S_ISDIR(dir->i_mode)) { printk("nfs_mkdir: inode is NULL or not a directory\n"); - iput(dir); return -ENOENT; } - if (len > NFS_MAXNAMLEN) { - iput(dir); + + if (dentry->d_name.len > NFS_MAXNAMLEN) return -ENAMETOOLONG; - } + sattr.mode = mode; sattr.uid = sattr.gid = sattr.size = (unsigned) -1; sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1; + error = nfs_proc_mkdir(NFS_SERVER(dir), NFS_FH(dir), - name, &sattr, &fhandle, &fattr); - if (!error) - nfs_lookup_cache_add(dir, name, &fhandle, &fattr); - nfs_invalidate_dircache(dir); - iput(dir); - return error; + dentry->d_name.name, &sattr, &fhandle, &fattr); + + if (error) + return error; + + inode = nfs_fhget(dir->i_sb, &fhandle, &fattr); + if (!inode) + return -EACCES; + + d_instantiate(dentry, inode, D_DIR); + return 0; } -static int nfs_rmdir(struct inode *dir, const char *name, int len) +static int nfs_rmdir(struct inode *dir, struct dentry *dentry) { int error; dfprintk(VFS, "NFS: rmdir(%x/%ld, %s\n", - dir->i_dev, dir->i_ino, name); + dir->i_dev, dir->i_ino, dentry->d_name.name); if (!dir || !S_ISDIR(dir->i_mode)) { printk("nfs_rmdir: inode is NULL or not a directory\n"); - iput(dir); return -ENOENT; } - if (len > NFS_MAXNAMLEN) { - iput(dir); - return -ENAMETOOLONG; - } - error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dir), name); - if (!error) - nfs_lookup_cache_remove(dir, NULL, name); - nfs_invalidate_dircache(dir); - iput(dir); - return error; -} -static int nfs_sillyrename(struct inode *dir, const char *name, int len) -{ - struct inode *inode; - char silly[16]; - int slen, ret; - - atomic_inc(&dir->i_count); - if (nfs_lookup(dir, name, len, &inode) < 0) - return -EIO; /* arbitrary */ - - if (atomic_read(&inode->i_count) == 1) { - iput(inode); - return -EIO; - } - if (NFS_RENAMED_DIR(inode)) { - iput(NFS_RENAMED_DIR(inode)); - NFS_RENAMED_DIR(inode) = NULL; - iput(inode); - return -EIO; - } - - slen = sprintf(silly, ".nfs%ld", inode->i_ino); - if (len == slen && !strncmp(name, silly, len)) { - iput(inode); - return -EIO; /* DWIM */ - } + if (dentry->d_name.len > NFS_MAXNAMLEN) + return -ENAMETOOLONG; - dfprintk(VFS, "NFS: sillyrename(%x/%ld, %s)\n", - dir->i_dev, dir->i_ino, name); + error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dir), dentry->d_name.name); + if (error) + return error; - ret = nfs_proc_rename(NFS_SERVER(dir), NFS_FH(dir), name, - NFS_FH(dir), silly); - if (ret >= 0) { - nfs_lookup_cache_remove(dir, NULL, name); - nfs_lookup_cache_remove(dir, NULL, silly); - NFS_RENAMED_DIR(inode) = dir; - atomic_inc(&dir->i_count); - } - nfs_invalidate_dircache(dir); - iput(inode); - return ret; + d_delete(dentry); + return 0; } /* - * When releasing the inode, finally remove any unlinked but open files. - * Note that we have to clear the set of pending signals temporarily; - * otherwise the RPC call will fail. + * We should do silly-rename here, but I'm too lazy to fix + * up the directory entry implications of it.. */ -void nfs_sillyrename_cleanup(struct inode *inode) -{ - unsigned long oldsig; - struct inode *dir = NFS_RENAMED_DIR(inode); - char silly[14]; - int error, slen; - - dfprintk(VFS, "NFS: sillyrename cleanup(%x/%ld)\n", - inode->i_dev, inode->i_ino); - - oldsig = current->signal; - current->signal = 0; - - slen = sprintf(silly, ".nfs%ld", inode->i_ino); - error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dir), silly); - if (error < 0) - printk("NFS: silly_rename cleanup failed (err %d)\n", -error); - - nfs_lookup_cache_remove(dir, NULL, silly); - nfs_invalidate_dircache(dir); - NFS_RENAMED_DIR(inode) = NULL; - iput(dir); - - current->signal |= oldsig; -} - -static int nfs_unlink(struct inode *dir, const char *name, int len) +static int nfs_unlink(struct inode *dir, struct dentry *dentry) { int error; dfprintk(VFS, "NFS: unlink(%x/%ld, %s)\n", - dir->i_dev, dir->i_ino, name); + dir->i_dev, dir->i_ino, dentry->d_name.name); if (!dir || !S_ISDIR(dir->i_mode)) { printk("nfs_unlink: inode is NULL or not a directory\n"); - iput(dir); return -ENOENT; } - if (len > NFS_MAXNAMLEN) { - iput(dir); + + if (dentry->d_name.len > NFS_MAXNAMLEN) return -ENAMETOOLONG; - } - if ((error = nfs_sillyrename(dir, name, len)) < 0) { - error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dir), name); - if (!error) - nfs_lookup_cache_remove(dir, NULL, name); - } - nfs_invalidate_dircache(dir); - iput(dir); - return error; + + error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dir), dentry->d_name.name); + if (error) + return error; + + d_delete(dentry); + return 0; } -static int nfs_symlink(struct inode *dir, const char *name, int len, - const char *symname) +static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { struct nfs_sattr sattr; + struct nfs_fattr fattr; + struct nfs_fh fhandle; + struct inode * inode; int error; dfprintk(VFS, "NFS: symlink(%x/%ld, %s, %s)\n", - dir->i_dev, dir->i_ino, name, symname); + dir->i_dev, dir->i_ino, dentry->d_name.name, symname); if (!dir || !S_ISDIR(dir->i_mode)) { printk("nfs_symlink: inode is NULL or not a directory\n"); - iput(dir); return -ENOENT; } - if (len > NFS_MAXNAMLEN) { - iput(dir); + + if (dentry->d_name.len > NFS_MAXNAMLEN) return -ENAMETOOLONG; - } - if (strlen(symname) > NFS_MAXPATHLEN) { - iput(dir); + + if (strlen(symname) > NFS_MAXPATHLEN) return -ENAMETOOLONG; - } + sattr.mode = S_IFLNK | S_IRWXUGO; /* SunOS 4.1.2 crashes without this! */ sattr.uid = sattr.gid = sattr.size = (unsigned) -1; sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1; + error = nfs_proc_symlink(NFS_SERVER(dir), NFS_FH(dir), - name, symname, &sattr); - nfs_invalidate_dircache(dir); - iput(dir); - return error; + dentry->d_name.name, symname, &sattr); + + if (error) + return error; + + inode = nfs_fhget(dir->i_sb, &fhandle, &fattr); + if (!inode) + return -EACCES; + + d_instantiate(dentry, inode, 0); + return 0; } -static int nfs_link(struct inode *oldinode, struct inode *dir, - const char *name, int len) +static int nfs_link(struct inode *inode, struct inode *dir, struct dentry *dentry) { int error; dfprintk(VFS, "NFS: link(%x/%ld -> %x/%ld, %s)\n", - oldinode->i_dev, oldinode->i_ino, - dir->i_dev, dir->i_ino, name); + inode->i_dev, inode->i_ino, + dir->i_dev, dir->i_ino, dentry->d_name.name); - if (!oldinode) { - printk("nfs_link: old inode is NULL\n"); - iput(oldinode); - iput(dir); - return -ENOENT; - } if (!dir || !S_ISDIR(dir->i_mode)) { printk("nfs_link: dir is NULL or not a directory\n"); - iput(oldinode); - iput(dir); return -ENOENT; } - if (len > NFS_MAXNAMLEN) { - iput(oldinode); - iput(dir); + + if (dentry->d_name.len > NFS_MAXNAMLEN) return -ENAMETOOLONG; - } - error = nfs_proc_link(NFS_SERVER(oldinode), NFS_FH(oldinode), - NFS_FH(dir), name); - if (!error) { - nfs_lookup_cache_remove(dir, oldinode, NULL); - NFS_READTIME(oldinode) = 0; /* force getattr */ - } - nfs_invalidate_dircache(dir); - iput(oldinode); - iput(dir); - return error; + + error = nfs_proc_link(NFS_SERVER(inode), NFS_FH(inode), + NFS_FH(dir), dentry->d_name.name); + + if (error) + return error; + + atomic_inc(&inode->i_count); + d_instantiate(dentry, inode, 0); + return 0; } /* @@ -821,45 +601,39 @@ static int nfs_link(struct inode *oldinode, struct inode *dir, * rename the old file using the silly_rename stuff. This way, the original * file in old_dir will go away when the last process iput()s the inode. */ -static int nfs_rename(struct inode *old_dir, const char *old_name, int old_len, - struct inode *new_dir, const char *new_name, int new_len) +static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) { int error; dfprintk(VFS, "NFS: rename(%x/%ld, %s -> %x/%ld, %s)\n", - old_dir->i_dev, old_dir->i_ino, old_name, - new_dir->i_dev, new_dir->i_ino, new_name); + old_dir->i_dev, old_dir->i_ino, old_dentry->d_name.name, + new_dir->i_dev, new_dir->i_ino, new_dentry->d_name.name); if (!old_dir || !S_ISDIR(old_dir->i_mode)) { printk("nfs_rename: old inode is NULL or not a directory\n"); - iput(old_dir); - iput(new_dir); return -ENOENT; } + if (!new_dir || !S_ISDIR(new_dir->i_mode)) { printk("nfs_rename: new inode is NULL or not a directory\n"); - iput(old_dir); - iput(new_dir); return -ENOENT; } - if (old_len > NFS_MAXNAMLEN || new_len > NFS_MAXNAMLEN) { - iput(old_dir); - iput(new_dir); + + if (old_dentry->d_name.len > NFS_MAXNAMLEN || new_dentry->d_name.len > NFS_MAXNAMLEN) return -ENAMETOOLONG; - } error = nfs_proc_rename(NFS_SERVER(old_dir), - NFS_FH(old_dir), old_name, - NFS_FH(new_dir), new_name); - if (!error) { - nfs_lookup_cache_remove(old_dir, NULL, old_name); - nfs_lookup_cache_remove(new_dir, NULL, new_name); - } - nfs_invalidate_dircache(old_dir); - nfs_invalidate_dircache(new_dir); - iput(old_dir); - iput(new_dir); - return error; + NFS_FH(old_dir), old_dentry->d_name.name, + NFS_FH(new_dir), new_dentry->d_name.name); + + if (error) + return error; + + /* Update the dcache */ + d_move(old_dentry, new_dentry->d_parent, &new_dentry->d_name); + d_delete(new_dentry); + return 0; } /* @@ -925,6 +699,4 @@ void nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) init_fifo(inode); } else inode->i_op = NULL; - nfs_lookup_cache_refresh(inode, fattr); } - diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 56540bbdc5e3..4688954762c0 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -69,6 +69,7 @@ struct inode_operations nfs_file_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ nfs_readpage, /* readpage */ nfs_writepage, /* writepage */ NULL, /* bmap */ diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index b9156417836b..871f5fd4d7e1 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -74,8 +74,6 @@ nfs_put_inode(struct inode * inode) { dprintk("NFS: put_inode(%x/%ld)\n", inode->i_dev, inode->i_ino); - if (NFS_RENAMED_DIR(inode)) - nfs_sillyrename_cleanup(inode); if (inode->i_pipe) clear_inode(inode); } @@ -230,7 +228,8 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent) /* Unlock super block and try to get root fh attributes */ unlock_super(sb); - if ((sb->s_mounted = nfs_fhget(sb, &data->root, NULL)) != NULL) { + sb->s_root = d_alloc_root(nfs_fhget(sb, &data->root, NULL), NULL); + if (sb->s_root != NULL) { /* We're airborne */ if (!(server->flags & NFS_MOUNT_NONLM)) lockd_up(); diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c index a22f9623987f..3d545f7d8e70 100644 --- a/fs/nfs/symlink.c +++ b/fs/nfs/symlink.c @@ -19,6 +19,7 @@ #include static int nfs_readlink(struct inode *, char *, int); +static struct dentry *nfs_follow_link(struct inode *, struct dentry *); /* * symlinks can't do much... @@ -35,6 +36,7 @@ struct inode_operations nfs_symlink_inode_operations = { NULL, /* mknod */ NULL, /* rename */ nfs_readlink, /* readlink */ + nfs_follow_link, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -55,7 +57,6 @@ static int nfs_readlink(struct inode *inode, char *buffer, int buflen) buflen = NFS_MAXPATHLEN; error = nfs_proc_readlink(NFS_SERVER(inode), NFS_FH(inode), &mem, &res, &len, buflen); - iput(inode); if (! error) { copy_to_user(buffer, res, len); put_user('\0', buffer + len); @@ -64,3 +65,36 @@ static int nfs_readlink(struct inode *inode, char *buffer, int buflen) kfree(mem); return error; } + +static struct dentry * nfs_follow_link(struct inode * inode, struct dentry *base) +{ + int error; + unsigned int len; + char *res; + void *mem; + char *path; + + dfprintk(VFS, "nfs: follow_link(%x/%ld)\n", inode->i_dev, inode->i_ino); + + error = nfs_proc_readlink(NFS_SERVER(inode), NFS_FH(inode), &mem, + &res, &len, NFS_MAXPATHLEN); + + if (error) { + dput(base); + kfree(mem); + return ERR_PTR(error); + } + path = kmalloc(len + 1, GFP_KERNEL); + if (!path) { + dput(base); + kfree(mem); + return ERR_PTR(-ENOMEM); + } + memcpy(path, res, len); + path[len] = 0; + kfree(mem); + + base = lookup_dentry(path, base, 1); + kfree(path); + return base; +} diff --git a/fs/open.c b/fs/open.c index 70b8a8a58281..4444653d5664 100644 --- a/fs/open.c +++ b/fs/open.c @@ -35,7 +35,7 @@ asmlinkage int sys_statfs(const char * path, struct statfs * buf) error = verify_area(VERIFY_WRITE, buf, sizeof(struct statfs)); if (error) goto out; - error = namei(NAM_FOLLOW_LINK, path, &inode); + error = namei(path, &inode); if (error) goto out; error = -ENOSYS; @@ -102,7 +102,7 @@ asmlinkage int sys_truncate(const char * path, unsigned long length) int error; lock_kernel(); - error = namei(NAM_FOLLOW_LINK, path, &inode); + error = namei(path, &inode); if (error) goto out; @@ -189,7 +189,7 @@ asmlinkage int sys_utime(char * filename, struct utimbuf * times) lock_kernel(); /* Hmm, should I always follow symlinks or not ? */ - error = namei(NAM_FOLLOW_LINK, filename, &inode); + error = namei(filename, &inode); if (error) goto out; error = -EROFS; @@ -237,7 +237,7 @@ asmlinkage int sys_utimes(char * filename, struct timeval * utimes) struct iattr newattrs; lock_kernel(); - error = namei(NAM_FOLLOW_LINK, filename, &inode); + error = namei(filename, &inode); if (error) goto out; error = -EROFS; @@ -287,7 +287,7 @@ asmlinkage int sys_access(const char * filename, int mode) old_fsgid = current->fsgid; current->fsuid = current->uid; current->fsgid = current->gid; - res = namei(NAM_FOLLOW_LINK, filename, &inode); + res = namei(filename, &inode); if (!res) { res = permission(inode, mode); iput(inode); @@ -301,24 +301,37 @@ out: asmlinkage int sys_chdir(const char * filename) { - struct inode * inode; - struct inode * tmpi; int error; + struct inode *inode; + struct dentry *dentry, *tmp; lock_kernel(); - error = namei(NAM_FOLLOW_LINK, filename, &inode); - if (error) + + dentry = lookup_dentry(filename, NULL, 1); + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) goto out; + + error = -ENOENT; + if (dentry->d_flag & D_NEGATIVE) + goto dput_and_out; + error = -ENOTDIR; + inode = dentry->d_inode; if (!S_ISDIR(inode->i_mode)) - goto iput_and_out; - if ((error = permission(inode,MAY_EXEC)) != 0) - goto iput_and_out; + goto dput_and_out; - /* exchange inodes */ - tmpi = current->fs->pwd; current->fs->pwd = inode; inode = tmpi; -iput_and_out: - iput(inode); + error = permission(inode,MAY_EXEC); + if (error) + goto dput_and_out; + + /* exchange dentries */ + tmp = current->fs->pwd; + current->fs->pwd = dentry; + dentry = tmp; + +dput_and_out: + dput(dentry); out: unlock_kernel(); return error; @@ -326,24 +339,33 @@ out: asmlinkage int sys_fchdir(unsigned int fd) { - struct inode * inode; - struct file * file; - int error = -EBADF; + struct file *file; + struct inode *inode; + struct dentry *dentry, *tmp; + int error; lock_kernel(); + + error = -EBADF; if (fd >= NR_OPEN || !(file = current->files->fd[fd])) goto out; + error = -ENOENT; if (!(inode = file->f_inode)) goto out; + error = -ENOTDIR; if (!S_ISDIR(inode->i_mode)) goto out; - if ((error = permission(inode,MAY_EXEC)) != 0) + + error = permission(inode,MAY_EXEC); + if (error) goto out; - iput(current->fs->pwd); - current->fs->pwd = inode; - atomic_inc(&inode->i_count); + + dentry = dget(inode->i_dentry); + tmp = current->fs->pwd; + current->fs->pwd = dentry; + dput(tmp); out: unlock_kernel(); return error; @@ -351,24 +373,41 @@ out: asmlinkage int sys_chroot(const char * filename) { - struct inode * inode; - struct inode * tmpi; int error; + struct inode *inode; + struct dentry *dentry, *tmp; lock_kernel(); - error = namei(NAM_FOLLOW_LINK, filename, &inode); - if (error) + + dentry = lookup_dentry(filename, NULL, 1); + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) goto out; + + error = -ENOENT; + if (dentry->d_flag & D_NEGATIVE) + goto dput_and_out; + error = -ENOTDIR; + inode = dentry->d_inode; if (!S_ISDIR(inode->i_mode)) - goto iput_and_out; + goto dput_and_out; + + error = permission(inode,MAY_EXEC); + if (error) + goto dput_and_out; + error = -EPERM; if (!fsuser()) - goto iput_and_out; - tmpi = current->fs->root; current->fs->root = inode; inode = tmpi; - error = 0; -iput_and_out: - iput(inode); + goto dput_and_out; + + /* exchange dentries */ + tmp = current->fs->root; + current->fs->root = dentry; + dentry = tmp; + +dput_and_out: + dput(dentry); out: unlock_kernel(); return error; @@ -419,7 +458,7 @@ asmlinkage int sys_chmod(const char * filename, mode_t mode) * because permissions on symlinks now can never be changed, * but on the other hand they are never needed. */ - error = namei(NAM_FOLLOW_LINK, filename, &inode); + error = namei(filename, &inode); if (error) goto out; error = -EROFS; @@ -517,7 +556,7 @@ asmlinkage int sys_chown(const char * filename, uid_t user, gid_t group) struct iattr newattrs; lock_kernel(); - error = namei(NAM_FOLLOW_TRAILSLASH, filename, &inode); + error = namei(filename, &inode); if (error) goto out; error = -EROFS; diff --git a/fs/proc/arbitrary.c b/fs/proc/arbitrary.c index 1e18e594ec26..390934e25575 100644 --- a/fs/proc/arbitrary.c +++ b/fs/proc/arbitrary.c @@ -53,6 +53,5 @@ int proc_arbitrary_lookup(struct inode * dir, const char * name, error = 0; } done: - iput(dir); return error; } diff --git a/fs/proc/array.c b/fs/proc/array.c index e8aefc08ca9e..82a3257d2056 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -1216,6 +1216,7 @@ struct inode_operations proc_array_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -1261,6 +1262,7 @@ struct inode_operations proc_arraylong_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ diff --git a/fs/proc/base.c b/fs/proc/base.c index b983e73f6706..7e9a65e088e7 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -42,6 +42,7 @@ static struct inode_operations proc_base_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ diff --git a/fs/proc/fd.c b/fs/proc/fd.c index 884631db80ad..1249c14809e1 100644 --- a/fs/proc/fd.c +++ b/fs/proc/fd.c @@ -14,7 +14,7 @@ #include static int proc_readfd(struct inode *, struct file *, void *, filldir_t); -static int proc_lookupfd(struct inode *,const char *,int,struct inode **); +static int proc_lookupfd(struct inode *,struct qstr *,struct inode **); static struct file_operations proc_fd_operations = { NULL, /* lseek - default */ @@ -44,6 +44,7 @@ struct inode_operations proc_fd_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -51,12 +52,13 @@ struct inode_operations proc_fd_inode_operations = { NULL /* permission */ }; -static int proc_lookupfd(struct inode * dir, const char * name, int len, - struct inode ** result) +static int proc_lookupfd(struct inode * dir, struct qstr *str, struct inode ** result) { unsigned int ino, pid, fd, c; struct task_struct * p; struct super_block * sb; + const char *name; + int len; *result = NULL; ino = dir->i_ino; @@ -65,25 +67,12 @@ static int proc_lookupfd(struct inode * dir, const char * name, int len, if (!dir) return -ENOENT; sb = dir->i_sb; - if (!pid || ino != PROC_PID_FD || !S_ISDIR(dir->i_mode)) { - iput(dir); + if (!pid || ino != PROC_PID_FD || !S_ISDIR(dir->i_mode)) return -ENOENT; - } - if (!len || (name[0] == '.' && (len == 1 || - (name[1] == '.' && len == 2)))) { - if (len < 2) { - *result = dir; - return 0; - } - if (!(*result = proc_get_inode(sb, (pid << 16)+PROC_PID_INO, &proc_pid))) { - iput(dir); - return -ENOENT; - } - iput(dir); - return 0; - } - iput(dir); + fd = 0; + len = str->len; + name = str->name; while (len-- > 0) { c = *name - '0'; name++; diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 1424dd1efb44..358060020451 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -23,6 +23,15 @@ static long proc_file_write(struct inode * inode, struct file * file, static long long proc_file_lseek(struct inode * inode, struct file * file, long long offset, int orig); +int proc_match(int len, const char *name,struct proc_dir_entry * de) +{ + if (!de || !de->low_ino) + return 0; + if (de->namelen != len) + return 0; + return !memcmp(name, de->name, len); +} + static struct file_operations proc_file_operations = { proc_file_lseek, /* lseek */ proc_file_read, /* read */ @@ -51,6 +60,7 @@ struct inode_operations proc_file_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -73,6 +83,7 @@ struct inode_operations proc_net_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ diff --git a/fs/proc/inode.c b/fs/proc/inode.c index 943137bf4d6b..18a40ffd85b4 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -131,12 +131,13 @@ struct super_block *proc_read_super(struct super_block *s,void *data, s->s_magic = PROC_SUPER_MAGIC; s->s_op = &proc_sops; unlock_super(s); - if (!(s->s_mounted = proc_get_inode(s, PROC_ROOT_INO, &proc_root))) { + s->s_root = d_alloc_root(proc_get_inode(s, PROC_ROOT_INO, &proc_root), NULL); + if (!s->s_root) { s->s_dev = 0; printk("get root inode failed\n"); return NULL; } - parse_options(data, &s->s_mounted->i_uid, &s->s_mounted->i_gid); + parse_options(data, &s->s_root->d_inode->i_uid, &s->s_root->d_inode->i_gid); return s; } diff --git a/fs/proc/kmsg.c b/fs/proc/kmsg.c index 6ef386ffaba0..1cc6a9c83559 100644 --- a/fs/proc/kmsg.c +++ b/fs/proc/kmsg.c @@ -70,6 +70,7 @@ struct inode_operations proc_kmsg_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ diff --git a/fs/proc/link.c b/fs/proc/link.c index 695ed9bba72b..42b7ae392b76 100644 --- a/fs/proc/link.c +++ b/fs/proc/link.c @@ -17,6 +17,7 @@ #include static int proc_readlink(struct inode *, char *, int); +static struct dentry * proc_follow_link(struct inode *, struct dentry *); /* * PLAN9_SEMANTICS won't work any more: it used an ugly hack that broke @@ -52,6 +53,7 @@ struct inode_operations proc_link_inode_operations = { NULL, /* mknod */ NULL, /* rename */ proc_readlink, /* readlink */ + proc_follow_link, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -59,59 +61,52 @@ struct inode_operations proc_link_inode_operations = { NULL /* permission */ }; -/* [Feb-1997 T. Schoebel-Theuer] This is no longer called from the - * VFS, but only from proc_readlink(). All the functionality - * should the moved there (without using temporary inodes any more) - * and then it could be eliminated. - */ -static int proc_follow_link(struct inode * dir, struct inode * inode, - int flag, int mode, struct inode ** res_inode) +static struct dentry * proc_follow_link(struct inode *inode, struct dentry *base) { - unsigned int pid, ino; - struct task_struct * p; - struct inode * new_inode; + struct task_struct *p; + struct dentry * result; + int ino, pid; int error; - *res_inode = NULL; - if (dir) - iput(dir); - if (!inode) - return -ENOENT; - if ((error = permission(inode, MAY_EXEC)) != 0){ - iput(inode); - return error; - } + /* We don't need a base pointer in the /proc filesystem */ + dput(base); + + error = permission(inode, MAY_EXEC); + result = ERR_PTR(error); + if (error) + return result; + ino = inode->i_ino; pid = ino >> 16; ino &= 0x0000ffff; p = find_task_by_pid(pid); - if (!p) { - iput(inode); - return -ENOENT; - } - new_inode = NULL; + result = ERR_PTR(-ENOENT); + if (!p) + return result; + switch (ino) { case PROC_PID_CWD: - if (!p->fs) + if (!p->fs || !p->fs->pwd) break; - new_inode = p->fs->pwd; + result = dget(p->fs->pwd); break; + case PROC_PID_ROOT: - if (!p->fs) + if (!p->fs || !p->fs->root) break; - new_inode = p->fs->root; + result = dget(p->fs->root); break; + case PROC_PID_EXE: { struct vm_area_struct * vma; if (!p->mm) break; vma = p->mm->mmap; while (vma) { - if (vma->vm_flags & VM_EXECUTABLE) { - new_inode = vma->vm_inode; - break; - } + if (vma->vm_flags & VM_EXECUTABLE) + return dget(vma->vm_inode->i_dentry); + vma = vma->vm_next; } break; @@ -122,45 +117,38 @@ static int proc_follow_link(struct inode * dir, struct inode * inode, if (!p->files) break; ino &= 0xff; - if (ino < NR_OPEN && p->files->fd[ino]) { - new_inode = p->files->fd[ino]->f_inode; - } + if (ino >= NR_OPEN) + break; + if (!p->files->fd[ino]) + break; + if (!p->files->fd[ino]->f_inode) + break; + result = dget(p->files->fd[ino]->f_inode->i_dentry); break; } } - iput(inode); - if (!new_inode) - return -ENOENT; - *res_inode = new_inode; - atomic_inc(&new_inode->i_count); - return 0; + return result; } static int proc_readlink(struct inode * inode, char * buffer, int buflen) { - int error = proc_follow_link(NULL, inode, 0, 0, &inode); + int error; + struct dentry * dentry = proc_follow_link(inode, NULL); - if (error) - return error; - if (!inode) - return -EIO; - - /* This will return *one* of the alias names (which is not quite - * correct). I have to rethink the problem, so this is only a - * quick hack... - */ - if(inode->i_dentry) { - char * tmp = (char*)__get_free_page(GFP_KERNEL); - int len = d_path(inode->i_dentry, current->fs->root, tmp); - int min = buflenfs->root, tmp); + int min = buflenpid); if (buflen < len) len = buflen; @@ -358,6 +359,15 @@ static int proc_self_readlink(struct inode * inode, char * buffer, int buflen) return len; } +static struct dentry * proc_self_follow_link(struct inode *inode, struct dentry *base) +{ + int len; + char tmp[30]; + + len = sprintf(tmp, "%d", current->pid); + return lookup_dentry(tmp, base, 1); +} + static struct inode_operations proc_self_inode_operations = { NULL, /* no file-ops */ NULL, /* create */ @@ -370,6 +380,7 @@ static struct inode_operations proc_self_inode_operations = { NULL, /* mknod */ NULL, /* rename */ proc_self_readlink, /* readlink */ + proc_self_follow_link, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -612,90 +623,41 @@ void proc_root_init(void) } -int proc_match(int len,const char * name,struct proc_dir_entry * de) -{ - if (!de || !de->low_ino) - return 0; - /* "" means "." ---> so paths like "/usr/lib//libc.a" work */ - if (!len && (de->name[0]=='.') && (de->name[1]=='\0')) - return 1; - if (de->namelen != len) - return 0; - return !memcmp(name, de->name, len); -} - -int proc_lookup(struct inode * dir,const char * name, int len, - struct inode ** result) +int proc_lookup(struct inode * dir, struct qstr * str, struct inode ** result) { struct proc_dir_entry * de; - int ino; *result = NULL; - if (!dir || !S_ISDIR(dir->i_mode)) { - iput(dir); + if (!dir || !S_ISDIR(dir->i_mode)) return -ENOTDIR; - } de = (struct proc_dir_entry *) dir->u.generic_ip; - if (!de) { - iput(dir); + if (!de) return -EINVAL; - } - /* Either remove this as soon as possible due to security problems, - * or uncomment the root-only usage. - */ - - /* Allow generic inode lookups everywhere. - * No other name in /proc must begin with a '['. - */ - if(/*!current->uid &&*/ name[0] == '[') - return proc_arbitrary_lookup(dir,name,len,result); - - /* Special case "." and "..": they aren't on the directory list */ - *result = dir; - if (!len) - return 0; - if (name[0] == '.') { - if (len == 1) - return 0; - if (name[1] == '.' && len == 2) { - struct inode * inode; - inode = proc_get_inode(dir->i_sb, de->parent->low_ino, de->parent); - iput(dir); - if (!inode) + *result = NULL; + for (de = de->subdir; de ; de = de->next) { + if (!de || !de->low_ino) + continue; + if (de->namelen != str->len) + continue; + if (!memcmp(str->name, de->name, str->len)) { + int ino = de->low_ino | (dir->i_ino & ~(0xffff)); + if (!(*result = proc_get_inode(dir->i_sb, ino, de))) return -EINVAL; - *result = inode; return 0; } } - - *result = NULL; - for (de = de->subdir; de ; de = de->next) { - if (proc_match(len, name, de)) - break; - } - if (!de) { - iput(dir); - return -ENOENT; - } - - ino = de->low_ino | (dir->i_ino & ~(0xffff)); - - if (!(*result = proc_get_inode(dir->i_sb, ino, de))) { - iput(dir); - return -EINVAL; - } - iput(dir); - return 0; + return -ENOENT; } -static int proc_root_lookup(struct inode * dir,const char * name, int len, - struct inode ** result) +static int proc_root_lookup(struct inode * dir,struct qstr *str, struct inode ** result) { unsigned int pid, c; int ino, retval; struct task_struct *p; + const char *name; + int len; atomic_inc(&dir->i_count); @@ -710,13 +672,13 @@ static int proc_root_lookup(struct inode * dir,const char * name, int len, read_unlock(&tasklist_lock); } - retval = proc_lookup(dir, name, len, result); - if (retval != -ENOENT) { - iput(dir); + retval = proc_lookup(dir, str, result); + if (retval != -ENOENT) return retval; - } pid = 0; + name = str->name; + len = str->len; while (len-- > 0) { c = *name - '0'; name++; @@ -732,16 +694,12 @@ static int proc_root_lookup(struct inode * dir,const char * name, int len, } } p = find_task_by_pid(pid); - if (!pid || !p) { - iput(dir); + if (!pid || !p) return -ENOENT; - } + ino = (pid << 16) + PROC_PID_INO; - if (!(*result = proc_get_inode(dir->i_sb, ino, &proc_pid))) { - iput(dir); + if (!(*result = proc_get_inode(dir->i_sb, ino, &proc_pid))) return -EINVAL; - } - iput(dir); return 0; } diff --git a/fs/proc/scsi.c b/fs/proc/scsi.c index fd629a75c08f..b1e77398c4a9 100644 --- a/fs/proc/scsi.c +++ b/fs/proc/scsi.c @@ -69,6 +69,7 @@ struct inode_operations proc_scsi_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ diff --git a/fs/readdir.c b/fs/readdir.c index df2f5fb84b20..aaea5b45fca2 100644 --- a/fs/readdir.c +++ b/fs/readdir.c @@ -1,35 +1,20 @@ /* - * fs/readdir.c + * linux/fs/readdir.c * * Copyright (C) 1995 Linus Torvalds */ -#include #include #include #include #include #include #include -#ifdef CONFIG_TRANS_NAMES -#include -#endif -#include #include #include #include -/* [T.Schoebel-Theuer] I am assuming that directories never get too large. - * The problem is that getdents() delivers d_offset's that can be used - * for lseek() by the user, so I must encode the status information for - * name translation and dcache baskets in the offset. - * Note that the linux man page getdents(2) does not mention that - * the d_offset is fs-specific and can be used for lseek(). - */ -#define BASKET_BIT (1<<30) /* 31 is already used by affs */ -#define TRANS_BIT (1<<29) - /* * Traditional linux readdir() handling.. * @@ -50,9 +35,6 @@ struct old_linux_dirent { struct readdir_callback { struct old_linux_dirent * dirent; - struct file * file; - int translate; - off_t oldoffset; int count; }; @@ -65,26 +47,11 @@ static int fillonedir(void * __buf, const char * name, int namlen, off_t offset, return -EINVAL; buf->count++; dirent = buf->dirent; - copy_to_user(dirent->d_name, name, namlen); - put_user(0, dirent->d_name + namlen); -#ifdef CONFIG_TRANS_NAMES - if(!buf->translate) { - char * cut; -#ifdef CONFIG_TRANS_RESTRICT - struct inode * inode = buf->file->f_inode; - cut = testname(inode && inode->i_gid != CONFIG_TRANS_GID, dirent->d_name); -#else - cut = testname(1, dirent->d_name); -#endif - if(cut) { - put_user(0, cut); - buf->translate = 1; - } - } -#endif put_user(ino, &dirent->d_ino); put_user(offset, &dirent->d_offset); put_user(namlen, &dirent->d_namlen); + copy_to_user(dirent->d_name, name, namlen); + put_user(0, dirent->d_name + namlen); return 0; } @@ -93,7 +60,6 @@ asmlinkage int old_readdir(unsigned int fd, void * dirent, unsigned int count) int error = -EBADF; struct file * file; struct readdir_callback buf; - off_t oldpos; lock_kernel(); if (fd >= NR_OPEN || !(file = current->files->fd[fd])) @@ -104,21 +70,11 @@ asmlinkage int old_readdir(unsigned int fd, void * dirent, unsigned int count) error = verify_area(VERIFY_WRITE, dirent, sizeof(struct old_linux_dirent)); if (error) goto out; - oldpos = file->f_pos; - buf.file = file; - buf.dirent = dirent; buf.count = 0; - buf.translate = 0; - if(file->f_pos & TRANS_BIT) { - file->f_pos &= ~TRANS_BIT; - buf.translate = 1; - } + buf.dirent = dirent; error = file->f_op->readdir(file->f_inode, file, &buf, fillonedir); if (error < 0) goto out; - if(buf.translate) { - file->f_pos = oldpos | TRANS_BIT; - } error = buf.count; out: unlock_kernel(); @@ -139,11 +95,8 @@ struct linux_dirent { struct getdents_callback { struct linux_dirent * current_dir; struct linux_dirent * previous; - struct file * file; int count; - int error; - int restricted; - int do_preload; + int error; }; static int filldir(void * __buf, const char * name, int namlen, off_t offset, ino_t ino) @@ -152,51 +105,18 @@ static int filldir(void * __buf, const char * name, int namlen, off_t offset, in struct getdents_callback * buf = (struct getdents_callback *) __buf; int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1); - /* Do not touch buf->error any more if everything is ok! */ + buf->error = -EINVAL; /* only used if we fail.. */ if (reclen > buf->count) - return (buf->error = -EINVAL); -#ifdef CONFIG_DCACHE_PRELOAD - if(buf->do_preload && (name[0] != '.' || namlen > 2)) { - struct qstr qname = { name, namlen }; - struct inode * dir = buf->file->f_inode; - d_entry_preliminary(dir->i_dentry, &qname, ino); - } -#endif + return -EINVAL; + dirent = buf->previous; + if (dirent) + put_user(offset, &dirent->d_off); dirent = buf->current_dir; - copy_to_user(dirent->d_name, name, namlen); - put_user(0, dirent->d_name + namlen); -#ifdef CONFIG_TRANS_NAMES - { - char * cut; -#ifdef CONFIG_TRANS_RESTRICT - cut = testname(buf->restricted, dirent->d_name); -#else - cut = testname(1, dirent->d_name); -#endif - if(cut) { - int newlen = (int)cut - (int)dirent->d_name; - int newreclen = ROUND_UP(NAME_OFFSET(dirent) + newlen + 1); - /* Either both must fit or none. This way we need - * no status information in f_pos */ - if (reclen+newlen > buf->count) - return -EINVAL; - put_user(0, cut); - put_user(ino, &dirent->d_ino); - put_user(newreclen, &dirent->d_reclen); - put_user(offset, &dirent->d_off); - ((char *) dirent) += newreclen; - buf->count -= newreclen; - put_user(offset, &dirent->d_off); - copy_to_user(dirent->d_name, name, namlen); - put_user(0, dirent->d_name + namlen); - } - } -#endif + buf->previous = dirent; put_user(ino, &dirent->d_ino); put_user(reclen, &dirent->d_reclen); - if (buf->previous) - put_user(buf->file->f_pos, &buf->previous->d_off); - buf->previous = dirent; + copy_to_user(dirent->d_name, name, namlen); + put_user(0, dirent->d_name + namlen); ((char *) dirent) += reclen; buf->current_dir = dirent; buf->count -= reclen; @@ -206,6 +126,7 @@ static int filldir(void * __buf, const char * name, int namlen, off_t offset, in asmlinkage int sys_getdents(unsigned int fd, void * dirent, unsigned int count) { struct file * file; + struct linux_dirent * lastdirent; struct getdents_callback buf; int error = -EBADF; @@ -218,72 +139,18 @@ asmlinkage int sys_getdents(unsigned int fd, void * dirent, unsigned int count) error = verify_area(VERIFY_WRITE, dirent, count); if (error) goto out; - buf.file = file; buf.current_dir = (struct linux_dirent *) dirent; buf.previous = NULL; buf.count = count; buf.error = 0; - buf.restricted = 0; -#ifdef CONFIG_TRANS_RESTRICT - buf.restricted = file->f_inode && file->f_inode->i_gid != CONFIG_TRANS_GID; -#endif - buf.do_preload = 0; -#ifdef CONFIG_DCACHE_PRELOAD - if(file->f_inode && file->f_inode->i_dentry && - !(file->f_inode->i_sb->s_type->fs_flags & (FS_NO_DCACHE|FS_NO_PRELIM)) && - !(file->f_inode->i_dentry->d_flag & D_PRELOADED)) - buf.do_preload = 1; -#endif - - if(!(file->f_pos & BASKET_BIT)) { - int oldcount; - do { - oldcount = buf.count; - error = file->f_op->readdir(file->f_inode, file, &buf, filldir); - if (error < 0) - goto out; - } while(!buf.error && buf.count != oldcount); - } - if(!buf.error) { - int nr = 0; - struct dentry * list = file->f_inode ? - d_basket(file->f_inode->i_dentry) : NULL; - struct dentry * ptr = list; -#ifdef CONFIG_DCACHE_PRELOAD - if(buf.do_preload) { - buf.do_preload = 0; - file->f_inode->i_dentry->d_flag |= D_PRELOADED; - } -#endif - if(ptr) { - if(!(file->f_pos & BASKET_BIT)) - file->f_pos = BASKET_BIT; - do { - struct dentry * next = ptr->d_basket_next; - struct inode * inode; - /* vfs_locks() are missing here */ - inode = d_inode(&ptr); - if(inode) { - nr++; - if(nr > (file->f_pos & ~BASKET_BIT)) { - int err = filldir(&buf, ptr->d_name.name, - ptr->d_name.len, - file->f_pos, - inode->i_ino); - if(err) - break; - file->f_pos++; - } - iput(inode); - } - ptr = next; - } while(ptr != list); - } - } - if (!buf.previous) { + error = file->f_op->readdir(file->f_inode, file, &buf, filldir); + if (error < 0) + goto out; + lastdirent = buf.previous; + if (!lastdirent) { error = buf.error; } else { - put_user(file->f_pos, &buf.previous->d_off); + put_user(file->f_pos, &lastdirent->d_off); error = count - buf.count; } out: diff --git a/fs/stat.c b/fs/stat.c index 03f6855528f7..0ab291256c0a 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -127,7 +127,7 @@ asmlinkage int sys_stat(char * filename, struct __old_kernel_stat * statbuf) int error; lock_kernel(); - error = namei(NAM_FOLLOW_LINK, filename, &inode); + error = namei(filename, &inode); if (error) goto out; if ((error = do_revalidate(inode)) == 0) @@ -145,7 +145,7 @@ asmlinkage int sys_newstat(char * filename, struct stat * statbuf) int error; lock_kernel(); - error = namei(NAM_FOLLOW_LINK, filename, &inode); + error = namei(filename, &inode); if (error) goto out; if ((error = do_revalidate(inode)) == 0) @@ -168,7 +168,7 @@ asmlinkage int sys_lstat(char * filename, struct __old_kernel_stat * statbuf) int error; lock_kernel(); - error = namei(NAM_FOLLOW_TRAILSLASH, filename, &inode); + error = lnamei(filename, &inode); if (error) goto out; if ((error = do_revalidate(inode)) == 0) @@ -187,7 +187,7 @@ asmlinkage int sys_newlstat(char * filename, struct stat * statbuf) int error; lock_kernel(); - error = namei(NAM_FOLLOW_TRAILSLASH, filename, &inode); + error = lnamei(filename, &inode); if (error) goto out; if ((error = do_revalidate(inode)) == 0) @@ -241,15 +241,13 @@ out: asmlinkage int sys_readlink(const char * path, char * buf, int bufsiz) { struct inode * inode; - int error = -EINVAL; + int error; - lock_kernel(); if (bufsiz <= 0) - goto out; - error = verify_area(VERIFY_WRITE,buf,bufsiz); - if (error) - goto out; - error = namei(NAM_FOLLOW_TRAILSLASH, path, &inode); + return -EINVAL; + + lock_kernel(); + error = lnamei(path, &inode); if (error) goto out; error = -EINVAL; @@ -263,6 +261,7 @@ asmlinkage int sys_readlink(const char * path, char * buf, int bufsiz) inode->i_dirt = 1; } error = inode->i_op->readlink(inode,buf,bufsiz); + iput(inode); out: unlock_kernel(); return error; diff --git a/fs/super.c b/fs/super.c index 94fc31fc05b1..e2b9eec925c9 100644 --- a/fs/super.c +++ b/fs/super.c @@ -465,7 +465,7 @@ void put_super(kdev_t dev) } if (!(sb = get_super(dev))) return; - if (sb->s_covered) { + if (sb->s_root != sb->s_root->d_mounts) { printk("VFS: Mounted device %s - tssk, tssk\n", kdevname(dev)); return; @@ -535,7 +535,6 @@ static struct super_block * read_super(kdev_t dev,const char *name,int flags, return NULL; } s->s_dev = dev; - s->s_covered = NULL; s->s_rd_only = 0; s->s_dirt = 0; s->s_type = type; @@ -570,6 +569,30 @@ void put_unnamed_dev(kdev_t dev) kdevname(dev)); } +static void d_umount(struct dentry *dentry) +{ + struct dentry * covers = dentry->d_covers; + + if (covers == dentry) { + printk("VFS: unmount - covers == dentry?\n"); + return; + } + covers->d_mounts = covers; + dentry->d_covers = dentry; + dput(covers); + dput(dentry); +} + +static void d_mount(struct dentry *covers, struct dentry *dentry) +{ + if (covers->d_mounts != covers) { + printk("VFS: mount - already mounted\n"); + return; + } + covers->d_mounts = dentry; + dentry->d_covers = covers; +} + static int do_umount(kdev_t dev,int unmount_root) { struct super_block * sb; @@ -597,11 +620,9 @@ static int do_umount(kdev_t dev,int unmount_root) } return 0; } - if (!(sb=get_super(dev)) || !(sb->s_covered)) + sb=get_super(dev); + if (!sb) return -ENOENT; - if (!sb->s_covered->i_mount) - printk("VFS: umount(%s): mounted inode has i_mount=NULL\n", - kdevname(dev)); /* * Before checking if the filesystem is still busy make sure the kernel @@ -609,20 +630,16 @@ static int do_umount(kdev_t dev,int unmount_root) * too bad there are no quotas running anymore. Turn them on again by hand. */ quota_off(dev, -1); - if (!fs_may_umount(dev, sb->s_mounted)) + if (!fs_may_umount(dev, sb->s_root)) return -EBUSY; /* Clear up the dcache tree. This should be cleaner.. */ - while (sb->s_ibasket) - free_ibasket(sb); - if (sb->s_mounted->i_dentry) - d_del(sb->s_mounted->i_dentry, D_NO_CLEAR_INODE); - - sb->s_covered->i_mount = NULL; - iput(sb->s_covered); - sb->s_covered = NULL; - iput(sb->s_mounted); - sb->s_mounted = NULL; + if (sb->s_root) { + d_umount(sb->s_root); + d_delete(sb->s_root); + } + + sb->s_root = NULL; if (sb->s_op && sb->s_op->write_super && sb->s_dirt) sb->s_op->write_super(sb); put_super(dev); @@ -651,12 +668,7 @@ asmlinkage int sys_umount(char * name) lock_kernel(); if (!suser()) goto out; - retval = namei(NAM_FOLLOW_LINK, name, &inode); - if (retval) { - retval = namei(NAM_FOLLOW_TRAILSLASH, name, &inode); - if (retval) - goto out; - } + retval = namei(name, &inode); if (S_ISBLK(inode->i_mode)) { dev = inode->i_rdev; retval = -EACCES; @@ -666,7 +678,7 @@ asmlinkage int sys_umount(char * name) } } else { retval = -EINVAL; - if (!inode->i_sb || inode != inode->i_sb->s_mounted) { + if (!inode->i_sb || inode != inode->i_sb->s_root->d_inode) { iput(inode); goto out; } @@ -719,45 +731,44 @@ out: int do_mount(kdev_t dev, const char * dev_name, const char * dir_name, const char * type, int flags, void * data) { - struct inode * dir_i = NULL; + struct dentry * dir_d = NULL; struct super_block * sb; struct vfsmount *vfsmnt; int error; - int override = 0; - if(dir_name) { - char c; - - get_user(c, dir_name); - override = (c == '!'); - } if (!(flags & MS_RDONLY) && dev && is_read_only(dev)) return -EACCES; /*flags |= MS_RDONLY;*/ - if(override) - dir_name++; - error = namei(NAM_FOLLOW_LINK, dir_name, &dir_i); - if (error) + + dir_d = lookup_dentry(dir_name, NULL, 1); + error = PTR_ERR(dir_d); + if (IS_ERR(dir_d)) return error; - if (!override && (atomic_read(&dir_i->i_count) != 1 || dir_i->i_mount)) { - iput(dir_i); + + if (dir_d->d_flag & D_NEGATIVE) { + dput(dir_d); + return -ENOENT; + } + + if (dir_d->d_covers != dir_d) { + dput(dir_d); return -EBUSY; } - if (!S_ISDIR(dir_i->i_mode)) { - iput(dir_i); + if (!S_ISDIR(dir_d->d_inode->i_mode)) { + dput(dir_d); return -ENOTDIR; } - if (!fs_may_mount(dev) && !override) { - iput(dir_i); + if (!fs_may_mount(dev)) { + dput(dir_d); return -EBUSY; } sb = read_super(dev,type,flags,data,0); if (!sb) { - iput(dir_i); + dput(dir_d); return -EINVAL; } - if (sb->s_covered) { - iput(dir_i); + if (sb->s_root->d_covers != sb->s_root) { + dput(dir_d); return -EBUSY; } vfsmnt = add_vfsmnt(dev, dev_name, dir_name); @@ -765,25 +776,8 @@ int do_mount(kdev_t dev, const char * dev_name, const char * dir_name, const cha vfsmnt->mnt_sb = sb; vfsmnt->mnt_flags = flags; } - { - struct dentry * old = dir_i->i_dentry; - struct dentry * new; - vfs_lock(); - new = d_alloc(old->d_parent, old->d_name.len, 1); - if(new) { - struct qstr copy = { old->d_name.name, old->d_name.len }; - d_add(new, sb->s_mounted, ©, D_DUPLICATE); - vfs_unlock(); - } else { - printk("VFS: cannot setup dentry for mount\n"); - iput(dir_i); - return -ENOMEM; - } - vfs_unlock(); - } - sb->s_covered = dir_i; - dir_i->i_mount = sb->s_mounted; - return 0; /* we don't iput(dir_i) - see umount */ + d_mount(dir_d, sb->s_root); + return 0; /* we don't dput(dir) - see umount */ } @@ -824,10 +818,10 @@ static int do_remount(const char *dir,int flags,char *data) struct inode *dir_i; int retval; - retval = namei(NAM_FOLLOW_LINK, dir, &dir_i); + retval = namei(dir, &dir_i); if (retval) return retval; - if (dir_i != dir_i->i_sb->s_mounted) { + if (dir_i != dir_i->i_sb->s_root->d_inode) { iput(dir_i); return -EINVAL; } @@ -916,7 +910,7 @@ asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type, t = fstype->name; fops = NULL; if ((fstype->fs_flags & FS_REQUIRES_DEV)) { - retval = namei(NAM_FOLLOW_LINK, dev_name, &inode); + retval = namei(dev_name, &inode); if (retval) goto out; retval = -ENOTBLK; @@ -986,7 +980,7 @@ __initfunc(static void do_mount_root(void)) struct file_system_type * fs_type; struct super_block * sb; struct vfsmount *vfsmnt; - struct inode * inode, * d_inode = NULL; + struct inode * d_inode = NULL; struct file filp; int retval; @@ -1005,15 +999,11 @@ __initfunc(static void do_mount_root(void)) sb->s_dev = get_unnamed_dev(); sb->s_flags = root_mountflags & ~MS_RDONLY; if (nfs_root_mount(sb) >= 0) { - inode = sb->s_mounted; - atomic_add(3, &inode->i_count); - sb->s_covered = inode; sb->s_rd_only = 0; sb->s_dirt = 0; sb->s_type = fs_type; - current->fs->pwd = inode; - current->fs->root = inode; - (void)d_alloc_root(inode); + current->fs->root = dget(sb->s_root); + current->fs->pwd = dget(sb->s_root); ROOT_DEV = sb->s_dev; printk (KERN_NOTICE "VFS: Mounted root (nfs filesystem).\n"); vfsmnt = add_vfsmnt(ROOT_DEV, "/dev/root", "/"); @@ -1070,15 +1060,9 @@ __initfunc(static void do_mount_root(void)) continue; sb = read_super(ROOT_DEV,fs_type->name,root_mountflags,NULL,1); if (sb) { - inode = sb->s_mounted; - - /* NOTE! it is logically used 4 times, not 1 */ - atomic_add(3, &inode->i_count); - sb->s_covered = inode; sb->s_flags = root_mountflags; - current->fs->pwd = inode; - current->fs->root = inode; - (void)d_alloc_root(inode); + current->fs->root = dget(sb->s_root); + current->fs->pwd = dget(sb->s_root); printk ("VFS: Mounted root (%s filesystem)%s.\n", fs_type->name, (sb->s_flags & MS_RDONLY) ? " readonly" : ""); @@ -1125,7 +1109,7 @@ __initfunc(static int do_change_root(kdev_t new_root_dev,const char *put_old)) do_mount_root(); old_fs = get_fs(); set_fs(get_ds()); - error = namei(NAM_FOLLOW_LINK, put_old, &inode); + error = namei(put_old, &inode); if (error) inode = NULL; set_fs(old_fs); if (!error && (atomic_read(&inode->i_count) != 1 || inode->i_mount)) diff --git a/include/asm-alpha/pgtable.h b/include/asm-alpha/pgtable.h index 2f40ad67322d..7d80c32f82b8 100644 --- a/include/asm-alpha/pgtable.h +++ b/include/asm-alpha/pgtable.h @@ -517,4 +517,7 @@ extern inline pte_t mk_swap_pte(unsigned long type, unsigned long offset) #define SWP_OFFSET(entry) ((entry) >> 40) #define SWP_ENTRY(type,offset) pte_val(mk_swap_pte((type),(offset))) +#define module_map vmalloc +#define module_unmap vfree + #endif /* _ALPHA_PGTABLE_H */ diff --git a/include/asm-i386/pgtable.h b/include/asm-i386/pgtable.h index 7d973530c47a..347611f1bd10 100644 --- a/include/asm-i386/pgtable.h +++ b/include/asm-i386/pgtable.h @@ -491,4 +491,7 @@ extern inline void update_mmu_cache(struct vm_area_struct * vma, #define SWP_OFFSET(entry) ((entry) >> 8) #define SWP_ENTRY(type,offset) (((type) << 1) | ((offset) << 8)) +#define module_map vmalloc +#define module_unmap vfree + #endif /* _I386_PAGE_H */ diff --git a/include/asm-m68k/hardirq.h b/include/asm-m68k/hardirq.h index 512e0b054230..ab3ca802e761 100644 --- a/include/asm-m68k/hardirq.h +++ b/include/asm-m68k/hardirq.h @@ -12,4 +12,6 @@ extern unsigned int local_irq_count[NR_CPUS]; #define hardirq_enter(cpu) (local_irq_count[cpu]++) #define hardirq_exit(cpu) (local_irq_count[cpu]--) +#define synchronize_irq() do { } while (0) + #endif diff --git a/include/asm-m68k/pgtable.h b/include/asm-m68k/pgtable.h index 9463700a3e47..589dfe956cb3 100644 --- a/include/asm-m68k/pgtable.h +++ b/include/asm-m68k/pgtable.h @@ -95,38 +95,23 @@ extern void cache_push_v (unsigned long vaddr, int len); extern inline void flush_cache_mm(struct mm_struct *mm) { -#if FLUSH_VIRTUAL_CACHE_040 - if (mm == current->mm) __flush_cache_all(); -#else - if (mm == current->mm) __flush_cache_030(); -#endif + if (mm == current->mm) + __flush_cache_030(); } extern inline void flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end) { - if (mm == current->mm){ -#if FLUSH_VIRTUAL_CACHE_040 - if (CPU_IS_040_OR_060) - cache_push_v(start, end-start); - else -#endif + if (mm == current->mm) __flush_cache_030(); - } } extern inline void flush_cache_page(struct vm_area_struct *vma, unsigned long vmaddr) { - if (vma->vm_mm == current->mm){ -#if FLUSH_VIRTUAL_CACHE_040 - if (CPU_IS_040_OR_060) - cache_push_v(vmaddr, PAGE_SIZE); - else -#endif + if (vma->vm_mm == current->mm) __flush_cache_030(); - } } /* Push the page at kernel virtual address and clear the icache */ @@ -783,4 +768,7 @@ extern inline void update_mmu_cache(struct vm_area_struct * vma, #endif /* __ASSEMBLY__ */ +#define module_map vmalloc +#define module_unmap vfree + #endif /* _M68K_PGTABLE_H */ diff --git a/include/asm-mips/atomic.h b/include/asm-mips/atomic.h index 7b11877686fb..cd8cb1dc5635 100644 --- a/include/asm-mips/atomic.h +++ b/include/asm-mips/atomic.h @@ -10,25 +10,21 @@ * for more details. * * Copyright (C) 1996 by Ralf Baechle + * + * $Id: atomic.h,v 1.2 1997/06/25 19:10:33 ralf Exp $ */ #ifndef __ASM_MIPS_ATOMIC_H #define __ASM_MIPS_ATOMIC_H #include -/* - * Make sure gcc doesn't try to be clever and move things around - * on us. We need to use _exactly_ the address the user gave us, - * not some alias that contains the same information. - */ -#define __atomic_fool_gcc(x) (*(struct { int a[100]; } *)x) - #ifdef __SMP__ typedef struct { volatile int counter; } atomic_t; #else typedef struct { int counter; } atomic_t; #endif +#ifdef __KERNEL__ #define ATOMIC_INIT(i) { (i) } #define atomic_read(v) ((v)->counter) @@ -97,6 +93,14 @@ extern __inline__ int atomic_sub_return(int i, atomic_t * v) * ... while for MIPS II and better we can use ll/sc instruction. This * implementation is SMP safe ... */ + +/* + * Make sure gcc doesn't try to be clever and move things around + * on us. We need to use _exactly_ the address the user gave us, + * not some alias that contains the same information. + */ +#define __atomic_fool_gcc(x) (*(struct { int a[100]; } *)x) + extern __inline__ void atomic_add(int i, volatile atomic_t * v) { unsigned long temp; @@ -181,5 +185,6 @@ extern __inline__ int atomic_sub_return(int i, atomic_t * v) #define atomic_inc(v) atomic_add(1,(v)) #define atomic_dec(v) atomic_sub(1,(v)) +#endif /* defined(__KERNEL__) */ #endif /* __ASM_MIPS_ATOMIC_H */ diff --git a/include/asm-mips/byteorder.h b/include/asm-mips/byteorder.h index 92e3b8863f50..d7356d91a850 100644 --- a/include/asm-mips/byteorder.h +++ b/include/asm-mips/byteorder.h @@ -6,15 +6,12 @@ * for more details. * * Copyright (C) 1995, 1996, 1997 by Ralf Baechle + * + * $Id: byteorder.h,v 1.5 1997/06/25 19:10:18 ralf Exp $ */ #ifndef __ASM_MIPS_BYTEORDER_H #define __ASM_MIPS_BYTEORDER_H -extern unsigned long int ntohl(unsigned long int __x); -extern unsigned short int ntohs(unsigned short int __x); -extern unsigned short int htons(unsigned short int __x); -extern unsigned long int htonl(unsigned long int __x); - #define __swap32(x) \ ((unsigned long int)((((unsigned long int)(x) & 0x000000ffU) << 24) | \ (((unsigned long int)(x) & 0x0000ff00U) << 8) | \ @@ -94,6 +91,66 @@ extern unsigned long int htonl(unsigned long int __x); #error "MIPS but neither __MIPSEL__ nor __MIPSEB__?" #endif +/* The same, but returns converted value from the location pointer by addr. */ +extern __inline__ __u16 cpu_to_le16p(__u16 *addr) +{ + return cpu_to_le16(*addr); +} + +extern __inline__ __u32 cpu_to_le32p(__u32 *addr) +{ + return cpu_to_le32(*addr); +} + +extern __inline__ __u16 cpu_to_be16p(__u16 *addr) +{ + return cpu_to_be16(*addr); +} + +extern __inline__ __u32 cpu_to_be32p(__u32 *addr) +{ + return cpu_to_be32(*addr); +} + +#define le16_to_cpup(x) cpu_to_le16p(x) +#define le32_to_cpup(x) cpu_to_le32p(x) +#define be16_to_cpup(x) cpu_to_be16p(x) +#define be32_to_cpup(x) cpu_to_be32p(x) + + +/* The same, but do the conversion in situ, ie. put the value back to addr. */ +extern __inline__ void cpu_to_le16s(__u16 *addr) +{ + *addr = cpu_to_le16(*addr); +} + +extern __inline__ void cpu_to_le32s(__u32 *addr) +{ + *addr = cpu_to_le32(*addr); +} + +extern __inline__ void cpu_to_be16s(__u16 *addr) +{ + *addr = cpu_to_be16(*addr); +} + +extern __inline__ void cpu_to_be32s(__u32 *addr) +{ + *addr = cpu_to_be32(*addr); +} + +#define le16_to_cpus(x) cpu_to_le16s(x) +#define le32_to_cpus(x) cpu_to_le32s(x) +#define be16_to_cpus(x) cpu_to_be16s(x) +#define be32_to_cpus(x) cpu_to_be32s(x) + +#ifdef __KERNEL__ +extern unsigned long int ntohl(unsigned long int __x); +extern unsigned short int ntohs(unsigned short int __x); +extern unsigned short int htons(unsigned short int __x); +extern unsigned long int htonl(unsigned long int __x); + + extern __inline__ unsigned long int ntohl(unsigned long int __x) { return __constant_ntohl(__x); @@ -113,5 +170,6 @@ extern __inline__ unsigned short int htons(unsigned short int __x) { return __constant_htons(__x); } +#endif /* __KERNEL__ */ #endif /* __ASM_MIPS_BYTEORDER_H */ diff --git a/include/asm-mips/checksum.h b/include/asm-mips/checksum.h index c5f9d20c7ee3..776c658d48e7 100644 --- a/include/asm-mips/checksum.h +++ b/include/asm-mips/checksum.h @@ -52,23 +52,20 @@ unsigned int csum_partial_copy_from_user(const char *src, char *dst, int len, un */ static inline unsigned short int csum_fold(unsigned int sum) { - unsigned int __res; - - __asm__(" - .set noat - srl $1,%0,16 - andi %0,0xffff - addu $1,%0 - srl %0,$1,16 # addup halfword carry - andi $1,0xffff - addu $1,%0 - nor %0,$0,$1 + __asm__(" + .set noat + sll $1,%0,16 + addu %0,$1 + sltu $1,%0,$1 + srl %0,%0,16 + addu %0,$1 + xori %0,0xffff .set at" - : "=r"(__res) + : "=r" (sum) : "0" (sum) : "$1"); - return __res; + return sum; } /* @@ -140,20 +137,14 @@ static inline unsigned short int csum_tcpudp_magic(unsigned long saddr, addu %0,%2 sltu $1,%0,%2 addu %0,$1 + addu %0,%3 sltu $1,%0,%3 addu %0,$1 + addu %0,%4 sltu $1,%0,%4 addu %0,$1 - - srl $1,%0,16 - andi %0,0xffff - addu %0,$1 - srl $1,%0,16 # addup halfword carry - andi %0,0xffff - addu %0,$1 - nor %0,$0,%0 .set at" : "=r" (sum) : "0" (daddr), "r"(saddr), @@ -165,7 +156,7 @@ static inline unsigned short int csum_tcpudp_magic(unsigned long saddr, "r"(sum) : "$1"); - return (unsigned short)sum; + return csum_fold(sum); } /* @@ -174,22 +165,10 @@ static inline unsigned short int csum_tcpudp_magic(unsigned long saddr, */ static inline unsigned short ip_compute_csum(unsigned char * buff, int len) { - unsigned short int sum; - - __asm__(" - .set noat - srl $1,%0,16 - andi %0,0xffff - addu %0,$1 - sltu $1,%0,$1 - addu %0,$1 - nor %0,$0,%0 - .set at" - : "=r"(sum) - : "0" (csum_partial(buff, len, 0)) - : "$1"); + unsigned int sum; - return sum; + sum = csum_partial(buff, len, 0); + return csum_fold(sum); } #define _HAVE_ARCH_IPV6_CSUM diff --git a/include/asm-mips/offset.h b/include/asm-mips/offset.h index 0f6e63a257cf..3f5430d97771 100644 --- a/include/asm-mips/offset.h +++ b/include/asm-mips/offset.h @@ -85,4 +85,20 @@ #define MM_PGD 8 #define MM_CONTEXT 28 +/* Linux sigcontext offsets. */ +#define SC_REGMASK 0 +#define SC_STATUS 4 +#define SC_PC 8 +#define SC_REGS 16 +#define SC_FPREGS 272 +#define SC_OWNEDFP 528 +#define SC_FPC_CSR 532 +#define SC_FPC_EIR 536 +#define SC_SSFLAGS 540 +#define SC_MDHI 544 +#define SC_MDLO 552 +#define SC_CAUSE 560 +#define SC_BADVADDR 564 +#define SC_SIGSET 568 + #endif /* !(_MIPS_OFFSET_H) */ diff --git a/include/asm-mips/pgtable.h b/include/asm-mips/pgtable.h index c6bb9b2de2d6..5d080f822676 100644 --- a/include/asm-mips/pgtable.h +++ b/include/asm-mips/pgtable.h @@ -765,4 +765,7 @@ extern inline void set_context(unsigned long val) #endif /* !defined (__LANGUAGE_ASSEMBLY__) */ +#define module_map vmalloc +#define module_unmap vfree + #endif /* __ASM_MIPS_PGTABLE_H */ diff --git a/include/asm-mips/r4kcache.h b/include/asm-mips/r4kcache.h index ebacf77603a0..c08923223f8c 100644 --- a/include/asm-mips/r4kcache.h +++ b/include/asm-mips/r4kcache.h @@ -1,4 +1,4 @@ -/* $Id: r4kcache.h,v 1.1 1997/06/06 09:39:42 ralf Exp $ +/* $Id: r4kcache.h,v 1.2 1997/06/25 17:04:19 ralf Exp $ * r4kcache.h: Inline assembly cache operations. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) @@ -339,6 +339,7 @@ extern inline void blast_dcache32_page(unsigned long page) unsigned long start = page; unsigned long end = (start + PAGE_SIZE); + __asm__ __volatile__("nop;nop;nop;nop"); while(start < end) { __asm__ __volatile__(" .set noreorder @@ -429,7 +430,7 @@ extern inline void blast_dcache32_page_indexed(unsigned long page) .set reorder" : : "r" (start), - "i" (Index_Invalidate_I)); + "i" (Index_Writeback_Inv_D)); start += 0x800; } } diff --git a/include/asm-mips/sigcontext.h b/include/asm-mips/sigcontext.h index 3e7346dee102..6b25821bb5b8 100644 --- a/include/asm-mips/sigcontext.h +++ b/include/asm-mips/sigcontext.h @@ -1,30 +1,20 @@ +/* + * include/asm-mips/uaccess.h + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1996, 1997 by Ralf Baechle + * + * $Id: sigcontext.h,v 1.3 1997/06/25 16:57:31 ralf Exp $ + */ #ifndef __ASM_MIPS_SIGCONTEXT_H #define __ASM_MIPS_SIGCONTEXT_H -#ifdef __LANGUAGE_ASSEMBLY__ - -#define SC_REGMASK 0 -#define SC_STATUS 4 -#define SC_PC 8 -#define SC_REGS 16 -#define SC_FPREGS 272 -#define SC_OWNEDFP 528 -#define SC_FPC_CSR 532 -#define SC_FPC_EIR 536 -#define SC_SSFLAGS 540 -#define SC_MDHI 544 -#define SC_MDLO 552 - -#endif - -#if defined(__LANGUAGE_C__) || \ - defined(_LANGUAGE_C) || \ - defined(__LANGUAGE_C_PLUS_PLUS__) || \ - defined(__LANGUAGE_OBJECTIVE_C__) - /* - * Whenever this structure is changed you must update the offsets in - * arch/mips/mips/fp-context.S. + * Keep this struct definition in sync with the sigcontext fragment + * in arch/mips/tools/offset.c */ struct sigcontext { unsigned int sc_regmask; /* Unused */ @@ -45,6 +35,5 @@ struct sigcontext { sigset_t sc_sigset; unsigned long __pad0[3]; /* pad for constant size */ }; -#endif #endif /* __ASM_MIPS_SIGCONTEXT_H */ diff --git a/include/asm-mips/uaccess.h b/include/asm-mips/uaccess.h index b650209dd3c6..b7ab2d8540d0 100644 --- a/include/asm-mips/uaccess.h +++ b/include/asm-mips/uaccess.h @@ -5,7 +5,9 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 1996 by Ralf Baechle + * Copyright (C) 1996, 1997 by Ralf Baechle + * + * $Id: uaccess.h,v 1.4 1997/07/01 08:23:56 ralf Exp $ */ #ifndef __ASM_MIPS_UACCESS_H #define __ASM_MIPS_UACCESS_H @@ -124,57 +126,48 @@ extern size_t __copy_user(void *__to, const void *__from, size_t __n); __cu_len; \ }) +/* + * Yuck. We need two variants, one for 64bit operation and one + * for 32 bit mode and old iron. + */ +#ifdef __mips64 +#define __GET_USER_DW __get_user_asm("ld") +#else +#define __GET_USER_DW __get_user_asm_ll32 +#endif + #define __get_user_nocheck(x,ptr,size) ({ \ long __gu_err; \ -long __gu_val; \ +__typeof(*(ptr)) __gu_val; \ long __gu_addr; \ __asm__("":"=r" (__gu_val)); \ __gu_addr = (long) (ptr); \ -__gu_err = 0; \ +__asm__("":"=r" (__gu_err)); \ switch (size) { \ -case 1: __get_user_nocheck_asm("lb"); break; \ -case 2: __get_user_nocheck_asm("lh"); break; \ -case 4: __get_user_nocheck_asm("lw"); break; \ -case 8: __get_user_nocheck_asm("ld"); break; \ +case 1: __get_user_asm("lb"); break; \ +case 2: __get_user_asm("lh"); break; \ +case 4: __get_user_asm("lw"); break; \ +case 8: __GET_USER_DW; break; \ default: __get_user_unknown(); break; \ } x = (__typeof__(*(ptr))) __gu_val; __gu_err; }) -#define __get_user_nocheck_asm(insn) \ -({ \ -__asm__ __volatile__( \ - "1:\t" insn "\t%1,%3\n" \ - "2:\n\t" \ - ".section\t.fixup,\"ax\"\n" \ - "3:\t.set\tnoat\n\t" \ - "la\t$1,2b\n\t" \ - "li\t%0,%4\n\t" \ - "jr\t$1\n\t" \ - ".set\tat\n\t" \ - ".previous\n\t" \ - ".section\t__ex_table,\"a\"\n\t" \ - STR(PTR)"\t1b,3b\n\t" \ - ".previous" \ - :"=r" (__gu_err), "=r" (__gu_val) \ - :"0" (__gu_err), "o" (__m(__gu_addr)), "i" (-EFAULT) \ - :"$1"); }) - #define __get_user_check(x,ptr,size,mask) ({ \ long __gu_err; \ -long __gu_val; \ +__typeof__(*(ptr)) __gu_val; \ long __gu_addr; \ __asm__("":"=r" (__gu_val)); \ __gu_addr = (long) (ptr); \ -__gu_err = -EFAULT; \ +__asm__("":"=r" (__gu_err)); \ if (__access_ok(__gu_addr,size,mask)) { \ switch (size) { \ -case 1: __get_user_check_asm("lb"); break; \ -case 2: __get_user_check_asm("lh"); break; \ -case 4: __get_user_check_asm("lw"); break; \ -case 8: __get_user_check_asm("ld"); break; \ +case 1: __get_user_asm("lb"); break; \ +case 2: __get_user_asm("lh"); break; \ +case 4: __get_user_asm("lw"); break; \ +case 8: __GET_USER_DW; break; \ default: __get_user_unknown(); break; \ } } x = (__typeof__(*(ptr))) __gu_val; __gu_err; }) -#define __get_user_check_asm(insn) \ +#define __get_user_asm(insn) \ ({ \ __asm__ __volatile__( \ "1:\t" insn "\t%1,%2\n\t" \ @@ -183,6 +176,7 @@ __asm__ __volatile__( \ ".section\t.fixup,\"ax\"\n" \ "3:\t.set\tnoat\n\t" \ "la\t$1,2b\n\t" \ + "li\t%0,%3\n\t" \ "jr\t$1\n\t" \ ".set\tat\n\t" \ ".previous\n\t" \ @@ -190,30 +184,82 @@ __asm__ __volatile__( \ STR(PTR)"\t1b,3b\n\t" \ ".previous" \ :"=r" (__gu_err), "=r" (__gu_val) \ - :"o" (__m(__gu_addr)) \ + :"o" (__m(__gu_addr)), "i" (-EFAULT) \ + :"$1"); }) + +/* + * Get a long long 64 using 32 bit registers. + */ +#define __get_user_asm_ll32 \ +({ \ +__asm__ __volatile__( \ + "1:\tlw\t%1,%2\n" \ + "2:\tlw\t%D1,%3\n\t" \ + "move\t%0,$0\n" \ + "3:\t.section\t.fixup,\"ax\"\n" \ + "4:\t.set\tnoat\n\t" \ + "la\t$1,3b\n\t" \ + "li\t%0,%4\n\t" \ + "jr\t$1\n\t" \ + ".set\tat\n\t" \ + ".previous\n\t" \ + ".section\t__ex_table,\"a\"\n\t" \ + STR(PTR)"\t1b,4b\n\t" \ + STR(PTR)"\t2b,4b\n\t" \ + ".previous" \ + :"=r" (__gu_err), "=&r" (__gu_val) \ + :"o" (__m(__gu_addr)), "o" (__m(__gu_addr + 4)), \ + "i" (-EFAULT) \ :"$1"); }) extern void __get_user_unknown(void); +/* + * Yuck. We need two variants, one for 64bit operation and one + * for 32 bit mode and old iron. + */ +#ifdef __mips64 +#define __PUT_USER_DW __put_user_asm("sd") +#else +#define __PUT_USER_DW __put_user_asm_ll32 +#endif + #define __put_user_nocheck(x,ptr,size) ({ \ long __pu_err; \ __typeof__(*(ptr)) __pu_val; \ long __pu_addr; \ __pu_val = (x); \ __pu_addr = (long) (ptr); \ -__pu_err = 0; \ +__asm__("":"=r" (__pu_err)); \ switch (size) { \ -case 1: __put_user_nocheck_asm("sb"); break; \ -case 2: __put_user_nocheck_asm("sh"); break; \ -case 4: __put_user_nocheck_asm("sw"); break; \ -case 8: __put_user_nocheck_asm("sd"); break; \ +case 1: __put_user_asm("sb"); break; \ +case 2: __put_user_asm("sh"); break; \ +case 4: __put_user_asm("sw"); break; \ +case 8: __PUT_USER_DW; break; \ default: __put_user_unknown(); break; \ } __pu_err; }) -#define __put_user_nocheck_asm(insn) \ +#define __put_user_check(x,ptr,size,mask) ({ \ +long __pu_err; \ +__typeof__(*(ptr)) __pu_val; \ +long __pu_addr; \ +__pu_val = (x); \ +__pu_addr = (long) (ptr); \ +__asm__("":"=r" (__pu_err)); \ +if (__access_ok(__pu_addr,size,mask)) { \ +switch (size) { \ +case 1: __put_user_asm("sb"); break; \ +case 2: __put_user_asm("sh"); break; \ +case 4: __put_user_asm("sw"); break; \ +case 8: __PUT_USER_DW; break; \ +default: __put_user_unknown(); break; \ +} } __pu_err; }) + +#define __put_user_asm(insn) \ ({ \ __asm__ __volatile__( \ - "1:\t" insn "\t%1,%2\n" \ + "1:\t" insn "\t%1,%2\n\t" \ + "move\t%0,$0\n" \ "2:\n\t" \ ".section\t.fixup,\"ax\"\n" \ "3:\t.set\tnoat\n\t" \ @@ -229,39 +275,27 @@ __asm__ __volatile__( \ :"r" (__pu_val), "o" (__m(__pu_addr)), "i" (-EFAULT) \ :"$1"); }) -#define __put_user_check(x,ptr,size,mask) ({ \ -long __pu_err; \ -__typeof__(*(ptr)) __pu_val; \ -long __pu_addr; \ -__pu_val = (x); \ -__pu_addr = (long) (ptr); \ -__pu_err = -EFAULT; \ -if (__access_ok(__pu_addr,size,mask)) { \ -switch (size) { \ -case 1: __put_user_check_asm("sb"); break; \ -case 2: __put_user_check_asm("sh"); break; \ -case 4: __put_user_check_asm("sw"); break; \ -case 8: __put_user_check_asm("sd"); break; \ -default: __put_user_unknown(); break; \ -} } __pu_err; }) - -#define __put_user_check_asm(insn) \ +#define __put_user_asm_ll32 \ ({ \ __asm__ __volatile__( \ - "1:\t" insn "\t%1,%2\n\t" \ + "1:\tsw\t%1,%2\n\t" \ + "2:\tsw\t%D1,%3\n" \ "move\t%0,$0\n" \ - "2:\n\t" \ + "3:\n\t" \ ".section\t.fixup,\"ax\"\n" \ - "3:\t.set\tnoat\n\t" \ - "la\t$1,2b\n\t" \ + "4:\t.set\tnoat\n\t" \ + "la\t$1,3b\n\t" \ + "li\t%0,%4\n\t" \ "jr\t$1\n\t" \ ".set\tat\n\t" \ ".previous\n\t" \ ".section\t__ex_table,\"a\"\n\t" \ - STR(PTR)"\t1b,3b\n\t" \ + STR(PTR)"\t1b,4b\n\t" \ + STR(PTR)"\t2b,4b\n\t" \ ".previous" \ :"=r" (__pu_err) \ - :"r" (__pu_val), "o" (__m(__pu_addr)) \ + :"r" (__pu_val), "o" (__m(__pu_addr)), "o" (__m(__pu_addr + 4)), \ + "i" (-EFAULT) \ :"$1"); }) extern void __put_user_unknown(void); diff --git a/include/asm-ppc/pgtable.h b/include/asm-ppc/pgtable.h index 243a0b115223..e9c400345491 100644 --- a/include/asm-ppc/pgtable.h +++ b/include/asm-ppc/pgtable.h @@ -361,4 +361,7 @@ extern inline void update_mmu_cache(struct vm_area_struct * vma, #define SWP_OFFSET(entry) ((entry) >> 8) #define SWP_ENTRY(type,offset) (((type) << 1) | ((offset) << 8)) +#define module_map vmalloc +#define module_unmap vfree + #endif /* _PPC_PAGE_H */ diff --git a/include/asm-sparc/asi.h b/include/asm-sparc/asi.h index f81ab33b9cb0..59fcd4337e7d 100644 --- a/include/asm-sparc/asi.h +++ b/include/asm-sparc/asi.h @@ -1,4 +1,4 @@ -/* $Id: asi.h,v 1.16 1996/04/25 06:12:43 davem Exp $ */ +/* $Id: asi.h,v 1.17 1997/06/24 15:48:10 jj Exp $ */ #ifndef _SPARC_ASI_H #define _SPARC_ASI_H @@ -69,7 +69,7 @@ /* Block-copy operations are available only on certain V8 cpus. */ #define ASI_M_BCOPY 0x17 /* Block copy */ -/* These affect only the ICACHE and are Ross HyperSparc specific. */ +/* These affect only the ICACHE and are Ross HyperSparc and TurboSparc specific. */ #define ASI_M_IFLUSH_PAGE 0x18 /* Flush I Cache Line (page); wo, ss */ #define ASI_M_IFLUSH_SEG 0x19 /* Flush I Cache Line (seg); wo, ss */ #define ASI_M_IFLUSH_REGION 0x1A /* Flush I Cache Line (region); wo, ss */ @@ -97,7 +97,7 @@ /* This is ROSS HyperSparc only. */ #define ASI_M_FLUSH_IWHOLE 0x31 /* Flush entire ICACHE; wo, ss */ -/* Tsunami/Viking i/d cache flash clear. */ +/* Tsunami/Viking/TurboSparc i/d cache flash clear. */ #define ASI_M_IC_FLCLEAR 0x36 #define ASI_M_DC_FLCLEAR 0x37 diff --git a/include/asm-sparc/ioctls.h b/include/asm-sparc/ioctls.h index 80eff02ea5b8..ccc5e7fced50 100644 --- a/include/asm-sparc/ioctls.h +++ b/include/asm-sparc/ioctls.h @@ -63,8 +63,8 @@ /* 119 is the non-posix getpgrp tty ioctl */ #define __TIOCCDTR _IO('t', 120) /* SunOS Specific */ #define __TIOCSDTR _IO('t', 121) /* SunOS Specific */ -#define __TIOCCBRK _IO('t', 122) /* SunOS Specific */ -#define __TIOCSBRK _IO('t', 123) /* SunOS Specific */ +#define TIOCCBRK _IO('t', 122) +#define TIOCSBRK _IO('t', 123) #define __TIOCLGET _IOW('t', 124, int) /* SunOS Specific */ #define __TIOCLSET _IOW('t', 125, int) /* SunOS Specific */ #define __TIOCLBIC _IOW('t', 126, int) /* SunOS Specific */ diff --git a/include/asm-sparc/mbus.h b/include/asm-sparc/mbus.h index e5e5a18c86c1..5f2749015342 100644 --- a/include/asm-sparc/mbus.h +++ b/include/asm-sparc/mbus.h @@ -1,4 +1,4 @@ -/* $Id: mbus.h,v 1.8 1996/08/29 09:48:21 davem Exp $ +/* $Id: mbus.h,v 1.9 1997/06/24 15:48:12 jj Exp $ * mbus.h: Various defines for MBUS modules. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -25,7 +25,8 @@ enum mbus_module { Viking_30 = 10, Viking_35 = 11, Viking_new = 12, - SRMMU_INVAL_MOD = 13, + TurboSparc = 13, + SRMMU_INVAL_MOD = 14, }; extern enum mbus_module srmmu_modtype; @@ -71,6 +72,7 @@ extern unsigned int hwbug_bitmask; /* Fujitsu */ #define FMI_AURORA 0x4 /* MB8690x, a Swift module... */ +#define FMI_TURBO 0x5 /* MB86907, a TurboSparc module... */ /* For multiprocessor support we need to be able to obtain the CPU id and * the MBUS Module id. diff --git a/include/asm-sparc/oplib.h b/include/asm-sparc/oplib.h index 40c6de10b419..bb404745f3d6 100644 --- a/include/asm-sparc/oplib.h +++ b/include/asm-sparc/oplib.h @@ -1,4 +1,4 @@ -/* $Id: oplib.h,v 1.15 1997/03/18 18:00:18 jj Exp $ +/* $Id: oplib.h,v 1.16 1997/06/27 14:55:04 jj Exp $ * oplib.h: Describes the interface and available routines in the * Linux Prom library. * @@ -272,12 +272,12 @@ extern int prom_searchsiblings(int node_start, char *name); /* Return the first property type, as a string, for the given node. * Returns a null string on error. */ -extern char *prom_firstprop(int node); +extern char *prom_firstprop(int node, char *buffer); /* Returns the next property after the passed property for the given * node. Returns null string on failure. */ -extern char *prom_nextprop(int node, char *prev_property); +extern char *prom_nextprop(int node, char *prev_property, char *buffer); /* Returns 1 if the specified node has given property. */ extern int prom_node_has_property(int node, char *property); diff --git a/include/asm-sparc/pgtable.h b/include/asm-sparc/pgtable.h index 5b54f967ca63..de8ce568720f 100644 --- a/include/asm-sparc/pgtable.h +++ b/include/asm-sparc/pgtable.h @@ -1,4 +1,4 @@ -/* $Id: pgtable.h,v 1.61 1997/06/06 10:56:34 jj Exp $ */ +/* $Id: pgtable.h,v 1.62 1997/06/27 14:55:00 jj Exp $ */ #ifndef _SPARC_PGTABLE_H #define _SPARC_PGTABLE_H @@ -394,4 +394,7 @@ __get_iospace (unsigned long addr) } } +#define module_map vmalloc +#define module_unmap vfree + #endif /* !(_SPARC_PGTABLE_H) */ diff --git a/include/asm-sparc/turbosparc.h b/include/asm-sparc/turbosparc.h new file mode 100644 index 000000000000..b0adb815820a --- /dev/null +++ b/include/asm-sparc/turbosparc.h @@ -0,0 +1,114 @@ +/* $Id: turbosparc.h,v 1.3 1997/06/26 12:59:27 jj Exp $ + * turbosparc.h: Defines specific to the TurboSparc module. + * This is SRMMU stuff. + * + * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + */ +#ifndef _SPARC_TURBOSPARC_H +#define _SPARC_TURBOSPARC_H + +#include +#include + +/* Bits in the SRMMU control register for TurboSparc modules. + * + * ------------------------------------------------------------------- + * |impl-vers| RSV| PMC |PE|PC| RSV |BM| RFR |IC|DC|PSO|RSV|ICS|NF|ME| + * ------------------------------------------------------------------- + * 31 24 23-21 20-19 18 17 16-15 14 13-10 9 8 7 6-3 2 1 0 + * + * BM: Boot Mode -- 0 = not in boot mode, 1 = in boot mode + * + * This indicates whether the TurboSparc is in boot-mode or not. + * + * IC: Instruction Cache -- 0 = off, 1 = on + * DC: Data Cache -- 0 = off, 1 = 0n + * + * These bits enable the on-cpu TurboSparc split I/D caches. + * + * ICS: ICache Snooping -- 0 = disable, 1 = enable snooping of icache + * NF: No Fault -- 0 = faults generate traps, 1 = faults don't trap + * ME: MMU enable -- 0 = mmu not translating, 1 = mmu translating + * + */ + +#define TURBOSPARC_MMUENABLE 0x00000001 +#define TURBOSPARC_NOFAULT 0x00000002 +#define TURBOSPARC_ICSNOOP 0x00000004 +#define TURBOSPARC_PSO 0x00000080 +#define TURBOSPARC_DCENABLE 0x00000100 /* Enable data cache */ +#define TURBOSPARC_ICENABLE 0x00000200 /* Enable instruction cache */ +#define TURBOSPARC_BMODE 0x00004000 +#define TURBOSPARC_PARITYODD 0x00020000 /* Parity odd, if enabled */ +#define TURBOSPARC_PCENABLE 0x00040000 /* Enable parity checking */ + +/* Bits in the CPU configuration register for TurboSparc modules. + * + * ------------------------------------------------------- + * |IOClk|SNP|AXClk| RAH | WS | RSV |SBC|WT|uS2|SE|SCC| + * ------------------------------------------------------- + * 31 30 29-28 27-26 25-23 22-8 7-6 5 4 3 2-0 + * + */ + +#define TURBOSPARC_SCENABLE 0x00000008 /* Secondary cache enable */ +#define TURBOSPARC_uS2 0x00000010 /* Swift compatibility mode */ +#define TURBOSPARC_WTENABLE 0x00000020 /* Write thru for dcache */ +#define TURBOSPARC_SNENABLE 0x40000000 /* DVMA snoop enable */ + +#ifndef __ASSEMBLY__ + +/* Bits [13:5] select one of 512 instruction cache tags */ +extern __inline__ void turbosparc_inv_insn_tag(unsigned long addr) +{ + __asm__ __volatile__("sta %%g0, [%0] %1\n\t" : : + "r" (addr), "i" (ASI_M_TXTC_TAG)); +} + +/* Bits [13:5] select one of 512 data cache tags */ +extern __inline__ void turbosparc_inv_data_tag(unsigned long addr) +{ + __asm__ __volatile__("sta %%g0, [%0] %1\n\t" : : + "r" (addr), "i" (ASI_M_DATAC_TAG)); +} + +extern __inline__ void turbosparc_flush_icache(void) +{ + __asm__ __volatile__("sta %%g0, [%%g0] %0\n\t" : : + "i" (ASI_M_IC_FLCLEAR)); +} + +extern __inline__ void turbosparc_flush_dcache(void) +{ + unsigned long addr; + + for(addr = 0; addr < 0x4000; addr += 0x20) + turbosparc_inv_data_tag(addr); +} + +extern __inline__ void turbosparc_idflash_clear(void) +{ + turbosparc_flush_icache(); turbosparc_flush_dcache(); +} + +extern __inline__ void turbosparc_set_ccreg(unsigned long regval) +{ + __asm__ __volatile__("sta %0, [%1] %2\n\t" : : + "r" (regval), "r" (0x600), + "i" (ASI_M_MMUREGS)); +} + +extern __inline__ unsigned long turbosparc_get_ccreg(void) +{ + unsigned long regval; + + __asm__ __volatile__("lda [%1] %2, %0\n\t" : + "=r" (regval) : + "r" (0x600), + "i" (ASI_M_MMUREGS)); + return regval; +} + +#endif /* !__ASSEMBLY__ */ + +#endif /* !(_SPARC_TURBOSPARC_H) */ diff --git a/include/asm-sparc64/asm_offsets.h b/include/asm-sparc64/asm_offsets.h index e1fa6f88afd4..18cf7c541582 100644 --- a/include/asm-sparc64/asm_offsets.h +++ b/include/asm-sparc64/asm_offsets.h @@ -145,24 +145,24 @@ #define AOFF_task_ldt 0x00000370 #define ASIZ_task_ldt 0x00000008 #define AOFF_task_tss 0x00000380 -#define ASIZ_task_tss 0x00000600 -#define AOFF_task_fs 0x00000980 +#define ASIZ_task_tss 0x000004c0 +#define AOFF_task_fs 0x00000840 #define ASIZ_task_fs 0x00000008 -#define AOFF_task_files 0x00000988 +#define AOFF_task_files 0x00000848 #define ASIZ_task_files 0x00000008 -#define AOFF_task_mm 0x00000990 +#define AOFF_task_mm 0x00000850 #define ASIZ_task_mm 0x00000008 -#define AOFF_task_sig 0x00000998 +#define AOFF_task_sig 0x00000858 #define ASIZ_task_sig 0x00000008 -#define AOFF_task_has_cpu 0x000009a0 +#define AOFF_task_has_cpu 0x00000860 #define ASIZ_task_has_cpu 0x00000004 -#define AOFF_task_processor 0x000009a4 +#define AOFF_task_processor 0x00000864 #define ASIZ_task_processor 0x00000004 -#define AOFF_task_last_processor 0x000009a8 +#define AOFF_task_last_processor 0x00000868 #define ASIZ_task_last_processor 0x00000004 -#define AOFF_task_lock_depth 0x000009ac +#define AOFF_task_lock_depth 0x0000086c #define ASIZ_task_lock_depth 0x00000004 -#define AOFF_task_sigmask_lock 0x000009b0 +#define AOFF_task_sigmask_lock 0x00000870 #define ASIZ_task_sigmask_lock 0x00000000 #define AOFF_mm_mmap 0x00000000 #define ASIZ_mm_mmap 0x00000008 @@ -210,41 +210,37 @@ #define ASIZ_mm_def_flags 0x00000008 #define AOFF_mm_cpu_vm_mask 0x000000b8 #define ASIZ_mm_cpu_vm_mask 0x00000008 -#define AOFF_thread_float_regs 0x00000000 -#define ASIZ_thread_float_regs 0x00000100 -#define AOFF_thread_fsr 0x00000100 -#define ASIZ_thread_fsr 0x00000008 -#define AOFF_thread_ksp 0x00000108 +#define AOFF_thread_ksp 0x00000000 #define ASIZ_thread_ksp 0x00000008 -#define AOFF_thread_kpc 0x00000110 +#define AOFF_thread_kpc 0x00000008 #define ASIZ_thread_kpc 0x00000008 -#define AOFF_thread_wstate 0x00000118 +#define AOFF_thread_wstate 0x00000010 #define ASIZ_thread_wstate 0x00000008 -#define AOFF_thread_cwp 0x00000120 -#define ASIZ_thread_cwp 0x00000008 -#define AOFF_thread_ctx 0x00000128 -#define ASIZ_thread_ctx 0x00000008 -#define AOFF_thread_reg_window 0x00000130 +#define AOFF_thread_cwp 0x00000018 +#define ASIZ_thread_cwp 0x00000004 +#define AOFF_thread_ctx 0x0000001c +#define ASIZ_thread_ctx 0x00000004 +#define AOFF_thread_flags 0x00000020 +#define ASIZ_thread_flags 0x00000004 +#define AOFF_thread_new_signal 0x00000024 +#define ASIZ_thread_new_signal 0x00000004 +#define AOFF_thread_current_ds 0x00000028 +#define ASIZ_thread_current_ds 0x00000008 +#define AOFF_thread_w_saved 0x00000030 +#define ASIZ_thread_w_saved 0x00000008 +#define AOFF_thread_kregs 0x00000038 +#define ASIZ_thread_kregs 0x00000008 +#define AOFF_thread_reg_window 0x00000040 #define ASIZ_thread_reg_window 0x00000400 -#define AOFF_thread_rwbuf_stkptrs 0x00000530 +#define AOFF_thread_rwbuf_stkptrs 0x00000440 #define ASIZ_thread_rwbuf_stkptrs 0x00000040 -#define AOFF_thread_w_saved 0x00000570 -#define ASIZ_thread_w_saved 0x00000008 -#define AOFF_thread_flags 0x00000578 -#define ASIZ_thread_flags 0x00000008 -#define AOFF_thread_sig_address 0x00000580 +#define AOFF_thread_sig_address 0x00000480 #define ASIZ_thread_sig_address 0x00000008 -#define AOFF_thread_sig_desc 0x00000588 +#define AOFF_thread_sig_desc 0x00000488 #define ASIZ_thread_sig_desc 0x00000008 -#define AOFF_thread_sstk_info 0x00000590 +#define AOFF_thread_sstk_info 0x00000490 #define ASIZ_thread_sstk_info 0x00000010 -#define AOFF_thread_current_ds 0x000005a0 -#define ASIZ_thread_current_ds 0x00000008 -#define AOFF_thread_new_signal 0x000005a8 -#define ASIZ_thread_new_signal 0x00000008 -#define AOFF_thread_kregs 0x000005b0 -#define ASIZ_thread_kregs 0x00000008 -#define AOFF_thread_core_exec 0x000005b8 +#define AOFF_thread_core_exec 0x000004a0 #define ASIZ_thread_core_exec 0x00000020 #endif /* __ASM_OFFSETS_H__ */ diff --git a/include/asm-sparc64/atomic.h b/include/asm-sparc64/atomic.h index ec496fa17db5..12baf0222d97 100644 --- a/include/asm-sparc64/atomic.h +++ b/include/asm-sparc64/atomic.h @@ -1,8 +1,8 @@ -/* $Id: atomic.h,v 1.14 1997/04/16 05:57:06 davem Exp $ +/* $Id: atomic.h,v 1.15 1997/07/03 09:18:09 davem Exp $ * atomic.h: Thankfully the V9 is at least reasonable for this * stuff. * - * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1996, 1997 David S. Miller (davem@caip.rutgers.edu) */ #ifndef __ARCH_SPARC64_ATOMIC__ @@ -22,73 +22,63 @@ typedef struct { int counter; } atomic_t; extern __inline__ void atomic_add(int i, atomic_t *v) { - unsigned long temp0, temp1; __asm__ __volatile__(" - lduw [%3], %0 -1: - add %0, %2, %1 - cas [%3], %0, %1 - cmp %0, %1 - bne,a,pn %%icc, 1b - lduw [%3], %0 -2: -" : "=&r" (temp0), "=&r" (temp1) +1: lduw [%1], %%g1 + add %%g1, %0, %%g2 + cas [%1], %%g1, %%g2 + sub %%g1, %%g2, %%g1 + brnz,pn %%g1, 1b + nop" + : /* No outputs */ : "HIr" (i), "r" (__atomic_fool_gcc(v)) - : "cc"); + : "g1", "g2"); } extern __inline__ void atomic_sub(int i, atomic_t *v) { - unsigned long temp0, temp1; __asm__ __volatile__(" - lduw [%3], %0 -1: - sub %0, %2, %1 - cas [%3], %0, %1 - cmp %0, %1 - bne,a,pn %%icc, 1b - lduw [%3], %0 -2: -" : "=&r" (temp0), "=&r" (temp1) +1: lduw [%1], %%g1 + sub %%g1, %0, %%g2 + cas [%1], %%g1, %%g2 + sub %%g1, %%g2, %%g1 + brnz,pn %%g1, 1b + nop" + : /* No outputs */ : "HIr" (i), "r" (__atomic_fool_gcc(v)) - : "cc"); + : "g1", "g2"); } /* Same as above, but return the result value. */ extern __inline__ int atomic_add_return(int i, atomic_t *v) { - unsigned long temp0, oldval; + unsigned long oldval; __asm__ __volatile__(" - lduw [%3], %0 -1: - add %0, %2, %1 - cas [%3], %0, %1 - cmp %0, %1 - bne,a,pn %%icc, 1b - lduw [%3], %0 -2: -" : "=&r" (temp0), "=&r" (oldval) +1: lduw [%2], %%g1 + add %%g1, %1, %%g2 + cas [%2], %%g1, %%g2 + sub %%g1, %%g2, %%g1 + brnz,pn %%g1, 1b + add %%g2, %1, %0" + : "=&r" (oldval) : "HIr" (i), "r" (__atomic_fool_gcc(v)) - : "cc"); - return (((int)oldval) + 1); + : "g1", "g2"); + return (int)oldval; } extern __inline__ int atomic_sub_return(int i, atomic_t *v) { - unsigned long temp0, oldval; + unsigned long oldval; __asm__ __volatile__(" - lduw [%3], %0 -1: - sub %0, %2, %1 - cas [%3], %0, %1 - cmp %0, %1 - bne,a,pn %%icc, 1b - lduw [%3], %0 -2: -" : "=&r" (temp0), "=&r" (oldval) +1: lduw [%2], %%g1 + sub %%g1, %1, %%g2 + cas [%2], %%g1, %%g2 + sub %%g1, %%g2, %%g1 + brnz,pn %%g1, 1b + sub %%g2, %1, %0" + : "=&r" (oldval) : "HIr" (i), "r" (__atomic_fool_gcc(v)) - : "cc"); - return (((int)oldval) - 1); + : "g1", "g2"); + return (int)oldval; } #define atomic_dec_return(v) atomic_sub_return(1,(v)) diff --git a/include/asm-sparc64/bitops.h b/include/asm-sparc64/bitops.h index f71f7e0e5c9a..dabc83297a94 100644 --- a/include/asm-sparc64/bitops.h +++ b/include/asm-sparc64/bitops.h @@ -1,4 +1,4 @@ -/* $Id: bitops.h,v 1.17 1997/06/14 17:35:05 davem Exp $ +/* $Id: bitops.h,v 1.18 1997/06/30 12:36:18 davem Exp $ * bitops.h: Bit string operations on the V9. * * Copyright 1996 David S. Miller (davem@caip.rutgers.edu) @@ -121,7 +121,7 @@ extern __inline__ unsigned long ffz(unsigned long word) : "0" (word) : "g1", "g2"); #else -#ifdef EASY_CHEESE_VERSION +#if 1 /* def EASY_CHEESE_VERSION */ result = 0; while(word & 1) { result++; @@ -177,13 +177,11 @@ extern __inline__ unsigned long find_next_zero_bit(void *addr, unsigned long siz size -= 64; result += 64; } - offset = size >> 6; - size &= 63UL; - while (offset) { + while (size & ~63UL) { if (~(tmp = *(p++))) goto found_middle; result += 64; - offset--; + size -= 64; } if (!size) return result; @@ -289,13 +287,11 @@ extern __inline__ unsigned long find_next_zero_le_bit(void *addr, unsigned long size -= 64; result += 64; } - offset = size >> 6; - size &= 63UL; - while(offset) { + while(size & ~63) { if(~(tmp = __swab64p(p++))) goto found_middle; result += 64; - offset--; + size -= 64; } if(!size) return result; diff --git a/include/asm-sparc64/checksum.h b/include/asm-sparc64/checksum.h index d04abac7e435..b1ff474c32b0 100644 --- a/include/asm-sparc64/checksum.h +++ b/include/asm-sparc64/checksum.h @@ -1,4 +1,4 @@ -/* $Id: checksum.h,v 1.8 1997/05/29 12:45:03 jj Exp $ */ +/* $Id: checksum.h,v 1.9 1997/06/26 04:05:17 davem Exp $ */ #ifndef __SPARC64_CHECKSUM_H #define __SPARC64_CHECKSUM_H @@ -108,31 +108,30 @@ extern __inline__ unsigned short ip_fast_csum(__const__ unsigned char *iph, * both operands. */ __asm__ __volatile__(" - sub %2, 4, %%g7 - lduw [%1 + 0x00], %0 - lduw [%1 + 0x04], %%g2 - lduw [%1 + 0x08], %%g3 - addcc %%g2, %0, %0 - addccc %%g3, %0, %0 - lduw [%1 + 0x0c], %%g2 - lduw [%1 + 0x10], %%g3 - addccc %%g2, %0, %0 - addc %0, %%g0, %0 -1: - addcc %%g3, %0, %0 - add %1, 4, %1 - addccc %0, %%g0, %0 - subcc %%g7, 1, %%g7 - be,a,pt %%icc, 2f - sll %0, 16, %%g2 - ba,pt %%xcc, 1b - lduw [%1 + 0x10], %%g3 -2: - addcc %0, %%g2, %%g2 - srl %%g2, 16, %0 - addc %0, %%g0, %0 - xnor %%g0, %0, %0 - srl %0, 0, %0 + sub %2, 4, %%g7 ! IEU0 + lduw [%1 + 0x00], %0 ! Load Group + lduw [%1 + 0x04], %%g2 ! Load Group + lduw [%1 + 0x08], %%g3 ! Load Group + addcc %%g2, %0, %0 ! IEU1 1 Load Bubble + Group + lduw [%1 + 0x0c], %%g2 ! Load + addccc %%g3, %0, %0 ! Sngle Group no Bubble + lduw [%1 + 0x10], %%g3 ! Load Group + addccc %%g2, %0, %0 ! Sngle Group no Bubble + addc %0, %%g0, %0 ! Sngle Group +1: addcc %%g3, %0, %0 ! IEU1 Group no Bubble + add %1, 4, %1 ! IEU0 + addccc %0, %%g0, %0 ! Sngle Group no Bubble + subcc %%g7, 1, %%g7 ! IEU1 Group + be,a,pt %%icc, 2f ! CTI + sll %0, 16, %%g2 ! IEU0 + lduw [%1 + 0x10], %%g3 ! Load Group + ba,pt %%xcc, 1b ! CTI + nop ! IEU0 +2: addcc %0, %%g2, %%g2 ! IEU1 Group + srl %%g2, 16, %0 ! IEU0 Group regdep XXX Scheisse! + addc %0, %%g0, %0 ! Sngle Group + xnor %%g0, %0, %0 ! IEU0 Group + srl %0, 0, %0 ! IEU0 Group XXX Scheisse! " : "=r" (sum), "=&r" (iph) : "r" (ihl), "1" (iph) : "g2", "g3", "g7", "cc"); diff --git a/include/asm-sparc64/delay.h b/include/asm-sparc64/delay.h index 7923b50143be..f70d99b685c1 100644 --- a/include/asm-sparc64/delay.h +++ b/include/asm-sparc64/delay.h @@ -1,4 +1,4 @@ -/* $Id: delay.h,v 1.4 1997/04/10 23:32:44 davem Exp $ +/* $Id: delay.h,v 1.5 1997/06/18 12:36:23 jj Exp $ * delay.h: Linux delay routines on the V9. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu). @@ -12,7 +12,9 @@ extern unsigned long loops_per_sec; extern __inline__ void __delay(unsigned long loops) { __asm__ __volatile__(" - cmp %0, 0 + b,pt %%xcc, 1f + cmp %0, 0 + .align 32 1: bne,pt %%xcc, 1b subcc %0, 1, %0 diff --git a/include/asm-sparc64/fpumacro.h b/include/asm-sparc64/fpumacro.h index f6323254d259..dab13447254f 100644 --- a/include/asm-sparc64/fpumacro.h +++ b/include/asm-sparc64/fpumacro.h @@ -21,68 +21,44 @@ extern __inline__ void fprs_write(unsigned long val) __asm__ __volatile__("wr %0, 0x0, %%fprs" : : "r" (val)); } -extern __inline__ void fpsave32(unsigned int *fpregs, unsigned long *fsr) +extern __inline__ void fpsave(unsigned long *fpregs, + unsigned long *fsr, + unsigned long *gsr) { __asm__ __volatile__ (" - wr %%g0, %2, %%asi - stx %%fsr, [%1] - stda %%f0, [%0] %%asi - stda %%f16, [%0 + 64] %%asi - " : : "r" (fpregs), "r" (fsr), "i" (ASI_BLK_P)); -} - -extern __inline__ void fpload32(unsigned int *fpregs, unsigned long *fsr) -{ - __asm__ __volatile__ (" - wr %%g0, %2, %%asi - ldda [%0] %%asi, %%f0 - ldda [%0 + 64] %%asi, %%f16 - ldx [%1], %%fsr - " : : "r" (fpregs), "r" (fsr), "i" (ASI_BLK_P)); -} - -extern __inline__ void fpsave64hi(unsigned int *fpregs, unsigned long *fsr) -{ - __asm__ __volatile__ (" - wr %%g0, %2, %%asi - stx %%fsr, [%1] - stda %%f32, [%0 + 128] %%asi - stda %%f48, [%0 + 192] %%asi - " : : "r" (fpregs), "r" (fsr), "i" (ASI_BLK_P)); -} - -extern __inline__ void fpload64hi(unsigned int *fpregs, unsigned long *fsr) -{ - __asm__ __volatile__ (" - wr %%g0, %2, %%asi - ldda [%0 + 128] %%asi, %%f32 - ldda [%0 + 192] %%asi, %%f48 - ldx [%1], %%fsr - " : : "r" (fpregs), "r" (fsr), "i" (ASI_BLK_P)); -} - -extern __inline__ void fpsave(unsigned int *fpregs, unsigned long *fsr) -{ - __asm__ __volatile__ (" - wr %%g0, %2, %%asi + wr %%g0, %3, %%asi + rd %%gsr, %%g1 + membar #LoadStore | #StoreStore stx %%fsr, [%1] + stx %%g1, [%2] stda %%f0, [%0] %%asi stda %%f16, [%0 + 64] %%asi stda %%f32, [%0 + 128] %%asi stda %%f48, [%0 + 192] %%asi - " : : "r" (fpregs), "r" (fsr), "i" (ASI_BLK_P)); + membar #Sync +" : /* No outputs */ + : "r" (fpregs), "r" (fsr), "r" (gsr), "i" (ASI_BLK_P) + : "g1"); } -extern __inline__ void fpload(unsigned int *fpregs, unsigned long *fsr) +extern __inline__ void fpload(unsigned long *fpregs, + unsigned long *fsr, + unsigned long *gsr) { __asm__ __volatile__ (" - wr %%g0, %2, %%asi + wr %%g0, %3, %%asi + membar #StoreLoad | #LoadLoad ldda [%0] %%asi, %%f0 ldda [%0 + 64] %%asi, %%f16 ldda [%0 + 128] %%asi, %%f32 ldda [%0 + 192] %%asi, %%f48 ldx [%1], %%fsr - " : : "r" (fpregs), "r" (fsr), "i" (ASI_BLK_P)); + ldx [%2], %%g1 + wr %%g1, 0, %%gsr + membar #Sync +" : /* No outputs */ + : "r" (fpregs), "r" (fsr), "r" (gsr), "i" (ASI_BLK_P) + : "g1"); } #endif /* !(_SPARC64_FPUMACRO_H) */ diff --git a/include/asm-sparc64/head.h b/include/asm-sparc64/head.h index 7c6be4849643..73c4e1acf346 100644 --- a/include/asm-sparc64/head.h +++ b/include/asm-sparc64/head.h @@ -1,10 +1,10 @@ -/* $Id: head.h,v 1.23 1997/06/14 13:25:50 davem Exp $ */ +/* $Id: head.h,v 1.26 1997/07/07 03:05:23 davem Exp $ */ #ifndef _SPARC64_HEAD_H #define _SPARC64_HEAD_H #include -#define KERNBASE 0xFFFFF80000000000 +#define KERNBASE 0x400000 #define BOOT_KERNEL b sparc64_boot; nop; nop; nop; nop; nop; nop; nop; /* We need a "cleaned" instruction... */ @@ -43,17 +43,6 @@ nop; \ nop; -/* Just for testing */ -#define PROM_TRAP \ - rd %pc, %g1; \ - sethi %uhi(KERNBASE), %g4; \ - sethi %hi(0xf0000000-0x8000), %g2; \ - sllx %g4, 32, %g4; \ - add %g1, %g2, %g1; \ - sub %g1, %g4, %g1; \ - jmpl %g1 + %g0, %g0; \ - nop; - #define TRAP_ARG(routine, arg) \ ba,pt %xcc, etrap; \ rd %pc, %g7; \ @@ -105,12 +94,12 @@ #define SUNOS_SYSCALL_TRAP SYSCALL_TRAP(linux_sparc_syscall, sunos_sys_table) #define LINUX_32BIT_SYSCALL_TRAP SYSCALL_TRAP(linux_sparc_syscall, sys_call_table32) #define LINUX_64BIT_SYSCALL_TRAP SYSCALL_TRAP(linux_sparc_syscall, sys_call_table64) +#define GETCC_TRAP TRAP(getcc) +#define SETCC_TRAP TRAP(setcc) /* FIXME: Write these actually */ #define NETBSD_SYSCALL_TRAP TRAP(netbsd_syscall) #define SOLARIS_SYSCALL_TRAP TRAP(solaris_syscall) #define BREAKPOINT_TRAP TRAP(breakpoint_trap) -#define GETCC_TRAP TRAP(getcc) -#define SETCC_TRAP TRAP(setcc) #define INDIRECT_SOLARIS_SYSCALL(tlvl) TRAP_ARG(indirect_syscall, tlvl) #define TRAP_IRQ(routine, level) \ diff --git a/include/asm-sparc64/ioctls.h b/include/asm-sparc64/ioctls.h index c0348eef1c52..1d6c1cace33c 100644 --- a/include/asm-sparc64/ioctls.h +++ b/include/asm-sparc64/ioctls.h @@ -1,4 +1,4 @@ -/* $Id: ioctls.h,v 1.3 1997/06/14 17:35:08 davem Exp $ */ +/* $Id: ioctls.h,v 1.4 1997/06/23 07:26:03 davem Exp $ */ #ifndef _ASM_SPARC64_IOCTLS_H #define _ASM_SPARC64_IOCTLS_H @@ -64,8 +64,8 @@ /* 119 is the non-posix getpgrp tty ioctl */ #define __TIOCCDTR _IO('t', 120) /* SunOS Specific */ #define __TIOCSDTR _IO('t', 121) /* SunOS Specific */ -#define __TIOCCBRK _IO('t', 122) /* SunOS Specific */ -#define __TIOCSBRK _IO('t', 123) /* SunOS Specific */ +#define TIOCCBRK _IO('t', 122) +#define TIOCSBRK _IO('t', 123) #define __TIOCLGET _IOW('t', 124, int) /* SunOS Specific */ #define __TIOCLSET _IOW('t', 125, int) /* SunOS Specific */ #define __TIOCLBIC _IOW('t', 126, int) /* SunOS Specific */ diff --git a/include/asm-sparc64/mmu_context.h b/include/asm-sparc64/mmu_context.h index 008f68bc1edc..1c2a92c4b8dc 100644 --- a/include/asm-sparc64/mmu_context.h +++ b/include/asm-sparc64/mmu_context.h @@ -1,4 +1,4 @@ -/* $Id: mmu_context.h,v 1.11 1997/06/13 14:03:04 davem Exp $ */ +/* $Id: mmu_context.h,v 1.16 1997/07/05 09:54:46 davem Exp $ */ #ifndef __SPARC64_MMU_CONTEXT_H #define __SPARC64_MMU_CONTEXT_H @@ -24,34 +24,7 @@ extern unsigned long tlb_context_cache; #define CTX_VERSION_MASK ((~0UL) << CTX_VERSION_SHIFT) #define CTX_FIRST_VERSION ((1UL << CTX_VERSION_SHIFT) + 1UL) -extern __inline__ void get_new_mmu_context(struct mm_struct *mm, - unsigned long ctx) -{ - if((ctx & ~(CTX_VERSION_MASK)) == 0) { - unsigned long flags; - int entry; - - save_and_cli(flags); - __asm__ __volatile__("stxa %%g0, [%0] %1\n\t" - "stxa %%g0, [%0] %2" - : /* No outputs */ - : "r" (TLB_TAG_ACCESS), "i" (ASI_IMMU), - "i" (ASI_DMMU)); - for(entry = 0; entry < 62; entry++) { - spitfire_put_dtlb_data(entry, 0x0UL); - spitfire_put_itlb_data(entry, 0x0UL); - } - membar("#Sync"); - __asm__ __volatile__("flush %g4"); - restore_flags(flags); - - ctx = (ctx & CTX_VERSION_MASK) + CTX_FIRST_VERSION; - if(ctx == 1) /* _not_ zero! */ - ctx = CTX_FIRST_VERSION; - } - tlb_context_cache = ctx + 1; - mm->context = ctx; -} +extern void get_new_mmu_context(struct mm_struct *mm, unsigned long ctx); extern __inline__ void get_mmu_context(struct task_struct *tsk) { @@ -71,7 +44,7 @@ extern __inline__ void get_mmu_context(struct task_struct *tsk) } else tsk->tss.ctx = 0; spitfire_set_secondary_context(tsk->tss.ctx); - __asm__ __volatile__("flush %g4"); + __asm__ __volatile__("flush %g6"); paddr = __pa(mm->pgd); __asm__ __volatile__(" rdpr %%pstate, %%o4 diff --git a/include/asm-sparc64/page.h b/include/asm-sparc64/page.h index 17a6ff611ed9..d39d3d4942de 100644 --- a/include/asm-sparc64/page.h +++ b/include/asm-sparc64/page.h @@ -1,4 +1,4 @@ -/* $Id: page.h,v 1.9 1997/06/14 21:28:09 davem Exp $ */ +/* $Id: page.h,v 1.14 1997/06/26 22:32:03 davem Exp $ */ #ifndef _SPARC64_PAGE_H #define _SPARC64_PAGE_H @@ -18,17 +18,15 @@ #ifndef __ASSEMBLY__ -#define clear_page(page) \ - __asm__ __volatile__( "mov %%o7, %%g3\n\t" \ - "call __bzero_1page\n\t" \ - " mov %0, %%g2\n\t" \ - : /* No outputs */ \ - : "r" (page) \ - : "g1", "g2", "g3") +#define clear_page(page) memset((void *)(page), 0, PAGE_SIZE) -#define copy_page(to,from) memcpy((void *)(to), (void *)(from), PAGE_SIZE) +extern void copy_page(unsigned long to, unsigned long from); -#define STRICT_MM_TYPECHECKS +/* GROSS, defining this makes gcc pass these types as aggregates, + * and thus on the stack, turn this crap off... -DaveM + */ + +/* #define STRICT_MM_TYPECHECKS */ #ifdef STRICT_MM_TYPECHECKS /* These are used to make use of C type-checking.. */ @@ -96,7 +94,9 @@ typedef unsigned long iopgprot_t; #define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) #ifndef __ASSEMBLY__ -#define PAGE_OFFSET 0xFFFFF80000000000UL +/* Do prdele, look what happens to be in %g4... */ +register unsigned long page_offset asm("g4"); +#define PAGE_OFFSET page_offset #else #define PAGE_OFFSET 0xFFFFF80000000000 #endif diff --git a/include/asm-sparc64/pgtable.h b/include/asm-sparc64/pgtable.h index 772121a26437..5cbd9a3c54e8 100644 --- a/include/asm-sparc64/pgtable.h +++ b/include/asm-sparc64/pgtable.h @@ -1,4 +1,4 @@ -/* $Id: pgtable.h,v 1.37 1997/06/13 14:03:06 davem Exp $ +/* $Id: pgtable.h,v 1.49 1997/06/30 09:24:12 jj Exp $ * pgtable.h: SpitFire page table operations. * * Copyright 1996,1997 David S. Miller (davem@caip.rutgers.edu) @@ -51,7 +51,7 @@ #define PTRS_PER_PAGE (1UL << (PAGE_SHIFT-3)) /* NOTE: TLB miss handlers depend heavily upon where this is. */ -#define VMALLOC_START 0xFFFFFc0000000000UL +#define VMALLOC_START 0x0000000800000000UL #define VMALLOC_VMADDR(x) ((unsigned long)(x)) #endif /* !(__ASSEMBLY__) */ @@ -78,11 +78,11 @@ #define _PAGE_G 0x0000000000000001 /* Global */ /* Here are the SpitFire software bits we use in the TTE's. */ -#define _PAGE_PRESENT 0x0000000000001000 /* Present Page (ie. not swapped out) */ #define _PAGE_MODIFIED 0x0000000000000800 /* Modified Page (ie. dirty) */ #define _PAGE_ACCESSED 0x0000000000000400 /* Accessed Page (ie. referenced) */ #define _PAGE_READ 0x0000000000000200 /* Readable SW Bit */ #define _PAGE_WRITE 0x0000000000000100 /* Writable SW Bit */ +#define _PAGE_PRESENT 0x0000000000000080 /* Present Page (ie. not swapped out) */ #define _PAGE_CACHE (_PAGE_CP | _PAGE_CV) @@ -146,8 +146,7 @@ extern pte_t *__bad_pte(void); * hit for all __pa()/__va() operations. */ extern unsigned long phys_base; - -#define ZERO_PAGE (PAGE_OFFSET + phys_base) +#define ZERO_PAGE ((unsigned long)__va(phys_base)) /* This is for making TLB miss faster to process. */ extern unsigned long null_pmd_table; @@ -159,156 +158,47 @@ extern void *sparc_init_alloc(unsigned long *kbrk, unsigned long size); /* Cache and TLB flush operations. */ -extern __inline__ void flush_cache_all(void) -{ - unsigned long addr; - - flushw_all(); - for(addr = 0; addr < (PAGE_SIZE << 1); addr += 32) - spitfire_put_icache_tag(addr, 0x0UL); -} - -extern __inline__ void flush_cache_mm(struct mm_struct *mm) -{ - if(mm->context != NO_CONTEXT) { - unsigned long addr; - - flushw_user(); - for(addr = 0; addr < (PAGE_SIZE << 1); addr += 32) - spitfire_put_icache_tag(addr, 0x0UL); - } -} - -extern __inline__ void flush_cache_range(struct mm_struct *mm, unsigned long start, - unsigned long end) -{ - if(mm->context != NO_CONTEXT) { - unsigned long addr; +#define flush_cache_all() \ +do { unsigned long va; \ + flushw_all(); \ + for(va = 0; \ + va<(PAGE_SIZE<<1); \ + va += 32) \ +spitfire_put_icache_tag(va,0x0);\ +} while(0) - flushw_user(); - for(addr = 0; addr < (PAGE_SIZE << 1); addr += 32) - spitfire_put_icache_tag(addr, 0x0UL); - } -} - -extern __inline__ void flush_cache_page(struct vm_area_struct *vma, unsigned long page) -{ - struct mm_struct *mm = vma->vm_mm; - - if(mm->context != NO_CONTEXT) { - unsigned long addr; - - flushw_user(); - for(addr = 0; addr < (PAGE_SIZE << 1); addr += 32) - spitfire_put_icache_tag(addr, 0x0UL); - } -} +#define flush_cache_mm(mm) do { } while(0) +#define flush_cache_range(mm, start, end) do { } while(0) +#define flush_cache_page(vma, page) do { } while(0) /* This operation in unnecessary on the SpitFire since D-CACHE is write-through. */ #define flush_page_to_ram(page) do { } while (0) -extern __inline__ void flush_tlb_all(void) -{ - unsigned long flags; - int entry; - - /* Invalidate all non-locked TTE's in both the dtlb and itlb. */ - save_and_cli(flags); - __asm__ __volatile__("stxa %%g0, [%0] %1\n\t" - "stxa %%g0, [%0] %2" - : /* No outputs */ - : "r" (TLB_TAG_ACCESS), "i" (ASI_IMMU), "i" (ASI_DMMU)); - for(entry = 0; entry < 62; entry++) { - spitfire_put_dtlb_data(entry, 0x0UL); - spitfire_put_itlb_data(entry, 0x0UL); - } - membar("#Sync"); - flushi(PAGE_OFFSET); - restore_flags(flags); -} +extern void flush_tlb_all(void); +extern void __flush_tlb_mm(unsigned long context); extern __inline__ void flush_tlb_mm(struct mm_struct *mm) { - if(mm->context != NO_CONTEXT) { - __asm__ __volatile__(" - /* flush_tlb_mm() */ - rdpr %%pil, %%g1 - mov %1, %%g7 - wrpr %%g0, 15, %%pil - ldxa [%%g7] %2, %%g2 - cmp %%g2, %0 - be,pt %%icc, 1f - mov 0x50, %%g3 - stxa %0, [%%g7] %2 -1: - stxa %%g0, [%%g3] %3 - stxa %%g0, [%%g3] %4 - be,pt %%icc, 1f - nop - stxa %%g2, [%%g7] %2 -1: - flush %%g4 - wrpr %%g1, 0x0, %%pil -" : /* no outputs */ - : "r" (mm->context & 0x1fff), "i" (SECONDARY_CONTEXT), "i" (ASI_DMMU), - "i" (ASI_DMMU_DEMAP), "i" (ASI_IMMU_DEMAP) - : "g1", "g2", "g3", "g7", "cc"); - } + if(mm->context != NO_CONTEXT) + __flush_tlb_mm(mm->context & 0x1fff); } +extern void __flush_tlb_range(unsigned long context, unsigned long start, + unsigned long end); extern __inline__ void flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end) { - if(mm->context != NO_CONTEXT) { - unsigned long old_ctx = spitfire_get_secondary_context(); - unsigned long new_ctx = (mm->context & 0x1fff); - unsigned long flags; - - start &= PAGE_MASK; - save_and_cli(flags); - if(new_ctx != old_ctx) - spitfire_set_secondary_context(new_ctx); - while(start < end) { - spitfire_flush_dtlb_secondary_page(start); - spitfire_flush_itlb_secondary_page(start); - start += PAGE_SIZE; - } - if(new_ctx != old_ctx) - spitfire_set_secondary_context(old_ctx); - __asm__ __volatile__("flush %g4"); - restore_flags(flags); - } + if(mm->context != NO_CONTEXT) + __flush_tlb_range(mm->context & 0x1fff, start, end); } +extern void __flush_tlb_page(unsigned long context, unsigned long page); extern __inline__ void flush_tlb_page(struct vm_area_struct *vma, unsigned long page) { struct mm_struct *mm = vma->vm_mm; - if(mm->context != NO_CONTEXT) { - __asm__ __volatile__(" - /* flush_tlb_page() */ - rdpr %%pil, %%g1 - mov %1, %%g7 - wrpr %%g0, 15, %%pil - ldxa [%%g7] %2, %%g2 - cmp %%g2, %0 - be,pt %%icc, 1f - or %5, 0x10, %%g3 - stxa %0, [%%g7] %2 -1: - stxa %%g0, [%%g3] %3 - stxa %%g0, [%%g3] %4 - be,pt %%icc, 1f - nop - stxa %%g2, [%%g7] %2 -1: - flush %%g4 - wrpr %%g1, 0x0, %%pil -" : /* no outputs */ - : "r" (mm->context & 0x1fff), "i" (SECONDARY_CONTEXT), "i" (ASI_DMMU), - "i" (ASI_DMMU_DEMAP), "i" (ASI_IMMU_DEMAP), "r" (page & PAGE_MASK) - : "g1", "g2", "g3", "g7", "cc"); - } + if(mm->context != NO_CONTEXT) + __flush_tlb_page(mm->context & 0x1fff, page & PAGE_MASK); } extern inline pte_t mk_pte(unsigned long page, pgprot_t pgprot) @@ -393,24 +283,6 @@ extern inline pte_t pte_mkyoung(pte_t pte) return __pte(pte_val(pte) | (_PAGE_ACCESSED)); } -extern inline void SET_PAGE_DIR(struct task_struct *tsk, pgd_t *pgdir) -{ - register unsigned long paddr asm("o5"); - - paddr = __pa(pgdir); - - if(tsk == current) { - __asm__ __volatile__ (" - rdpr %%pstate, %%o4 - wrpr %%o4, %1, %%pstate - mov %0, %%g7 - wrpr %%o4, 0x0, %%pstate - " : /* No outputs */ - : "r" (paddr), "i" (PSTATE_MG|PSTATE_IE) - : "o4"); - } -} - /* to find an entry in a page-table-directory. */ extern inline pgd_t *pgd_offset(struct mm_struct *mm, unsigned long address) { return mm->pgd + ((address >> PGDIR_SHIFT) & (PTRS_PER_PAGE - 1)); } @@ -428,11 +300,16 @@ extern inline pte_t *pte_offset(pmd_t *dir, unsigned long address) extern __inline__ void __init_pmd(pmd_t *pmdp) { - extern void __bfill64(void *, unsigned long); + extern void __bfill64(void *, unsigned long *); - __bfill64((void *)pmdp, null_pte_table); + __bfill64((void *)pmdp, &null_pte_table); } +/* Turning this off makes things much faster, but eliminates some + * sanity checking as well. + */ +/* #define PGTABLE_SANITY_CHECKS */ + /* Allocate and free page tables. The xxx_kernel() versions are * used to allocate a kernel page table - this turns on supervisor * bits if any. @@ -455,11 +332,13 @@ extern inline pte_t * pte_alloc_kernel(pmd_t *pmd, unsigned long address) } free_page((unsigned long) page); } +#ifdef PGTABLE_SANITY_CHECKS if (pmd_bad(*pmd)) { printk("Bad pmd in pte_alloc_kernel: %08lx\n", pmd_val(*pmd)); pmd_set(pmd, BAD_PTE); return NULL; } +#endif return (pte_t *) pmd_page(*pmd) + address; } @@ -482,11 +361,13 @@ extern inline pmd_t * pmd_alloc_kernel(pgd_t *pgd, unsigned long address) } free_page((unsigned long) page); } +#ifdef PGTABLE_SANITY_CHECKS if (pgd_bad(*pgd)) { printk("Bad pgd in pmd_alloc_kernel: %08lx\n", pgd_val(*pgd)); pgd_set(pgd, BAD_PMD); return NULL; } +#endif return (pmd_t *) pgd_page(*pgd) + address; } @@ -508,11 +389,13 @@ extern inline pte_t * pte_alloc(pmd_t *pmd, unsigned long address) } free_page((unsigned long) page); } +#ifdef PGTABLE_SANITY_CHECKS if (pmd_bad(*pmd)) { printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd)); pmd_set(pmd, BAD_PTE); return NULL; } +#endif return (pte_t *) pmd_page(*pmd) + address; } @@ -535,11 +418,13 @@ extern inline pmd_t * pmd_alloc(pgd_t *pgd, unsigned long address) } free_page((unsigned long) page); } +#ifdef PGTABLE_SANITY_CHECKS if (pgd_bad(*pgd)) { printk("Bad pgd in pmd_alloc: %08lx\n", pgd_val(*pgd)); pgd_set(pgd, BAD_PMD); return NULL; } +#endif return (pmd_t *) pgd_page(*pgd) + address; } @@ -548,16 +433,33 @@ extern inline void pgd_free(pgd_t * pgd) extern inline pgd_t * pgd_alloc(void) { - extern void __bfill64(void *, unsigned long); + extern void __bfill64(void *, unsigned long *); pgd_t *pgd = (pgd_t *) __get_free_page(GFP_KERNEL); if (pgd) - __bfill64((void *)pgd, null_pmd_table); + __bfill64((void *)pgd, &null_pmd_table); return pgd; } extern pgd_t swapper_pg_dir[1024]; +extern inline void SET_PAGE_DIR(struct task_struct *tsk, pgd_t *pgdir) +{ + if(pgdir != swapper_pg_dir && tsk == current) { + register unsigned long paddr asm("o5"); + + paddr = __pa(pgdir); + __asm__ __volatile__ (" + rdpr %%pstate, %%o4 + wrpr %%o4, %1, %%pstate + mov %0, %%g7 + wrpr %%o4, 0x0, %%pstate + " : /* No outputs */ + : "r" (paddr), "i" (PSTATE_MG|PSTATE_IE) + : "o4"); + } +} + /* Routines for getting a dvma scsi buffer. */ struct mmu_sglist { char *addr; @@ -575,61 +477,15 @@ extern void mmu_get_scsi_sgl(struct mmu_sglist *, int, struct linux_sbus *sbus) #define mmu_lockarea(vaddr, len) (vaddr) #define mmu_unlockarea(vaddr, len) do { } while(0) +extern void fixup_dcache_alias(struct vm_area_struct *vma, unsigned long address, + pte_t pte); + extern inline void update_mmu_cache(struct vm_area_struct * vma, unsigned long address, pte_t pte) { /* Find and fix bad virutal cache aliases. */ - if((vma->vm_flags & (VM_WRITE|VM_SHARED)) == (VM_WRITE|VM_SHARED)) { - struct vm_area_struct *vmaring; - struct inode *inode; - unsigned long vaddr, offset, start; - pgd_t *pgdp; - pmd_t *pmdp; - pte_t *ptep; - int alias_found = 0; - - inode = vma->vm_inode; - if(!inode) - return; - - offset = (address & PAGE_MASK) - vma->vm_start; - vmaring = inode->i_mmap; - do { - vaddr = vmaring->vm_start + offset; - - /* This conditional is misleading... */ - if((vaddr ^ address) & PAGE_SIZE) { - alias_found++; - start = vmaring->vm_start; - while(start < vmaring->vm_end) { - pgdp = pgd_offset(vmaring->vm_mm, start); - if(!pgdp) goto next; - pmdp = pmd_offset(pgdp, start); - if(!pmdp) goto next; - ptep = pte_offset(pmdp, start); - if(!ptep) goto next; - - if(pte_val(*ptep) & _PAGE_PRESENT) { - flush_cache_page(vmaring, start); - *ptep = __pte(pte_val(*ptep) & - ~(_PAGE_CV)); - flush_tlb_page(vmaring, start); - } - next: - start += PAGE_SIZE; - } - } - } while((vmaring = vmaring->vm_next_share) != NULL); - - if(alias_found && (pte_val(pte) & _PAGE_CV)) { - pgdp = pgd_offset(vma->vm_mm, address); - pmdp = pmd_offset(pgdp, address); - ptep = pte_offset(pmdp, address); - flush_cache_page(vma, address); - *ptep = __pte(pte_val(*ptep) & ~(_PAGE_CV)); - flush_tlb_page(vma, address); - } - } + if((vma->vm_flags & (VM_WRITE|VM_SHARED)) == (VM_WRITE|VM_SHARED)) + fixup_dcache_alias(vma, address, pte); } /* Make a non-present pseudo-TTE. */ @@ -649,7 +505,9 @@ sun4u_get_pte (unsigned long addr) pgd_t *pgdp; pmd_t *pmdp; pte_t *ptep; - + + if (addr >= PAGE_OFFSET) + return addr & _PAGE_PADDR; pgdp = pgd_offset_k (addr); pmdp = pmd_offset (pgdp, addr); ptep = pte_offset (pmdp, addr); @@ -668,6 +526,9 @@ __get_iospace (unsigned long addr) return ((sun4u_get_pte (addr) & 0xf0000000) >> 28); } +extern void * module_map (unsigned long size); +extern void module_unmap (void *addr); + #endif /* !(__ASSEMBLY__) */ #endif /* !(_SPARC64_PGTABLE_H) */ diff --git a/include/asm-sparc64/processor.h b/include/asm-sparc64/processor.h index 3cd62b8afd5e..019bbf600723 100644 --- a/include/asm-sparc64/processor.h +++ b/include/asm-sparc64/processor.h @@ -1,4 +1,4 @@ -/* $Id: processor.h,v 1.29 1997/06/16 04:45:05 davem Exp $ +/* $Id: processor.h,v 1.32 1997/07/01 21:59:38 davem Exp $ * include/asm-sparc64/processor.h * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -32,35 +32,24 @@ /* The Sparc processor specific thread struct. */ struct thread_struct { - /* Floating point regs */ - /* Please check asm_offsets, so that not to much precious space - is wasted by this alignment and move the float_regs wherever - is better in this structure. Remember every byte of alignment - is multiplied by 512 to get the amount of wasted kernel memory. */ - unsigned int float_regs[64] __attribute__ ((aligned (64))); - unsigned long fsr; - - /* Context switch saved kernel state. */ - unsigned long ksp, kpc, wstate, cwp, ctx; +/*DC1*/ unsigned long ksp __attribute__ ((aligned(16))); + unsigned long kpc; +/*DC2*/ unsigned long wstate; + unsigned int cwp; + unsigned int ctx; + +/*DC3*/ unsigned int flags; + unsigned int new_signal; + unsigned long current_ds; +/*DC4*/ unsigned long w_saved; + struct pt_regs *kregs; - /* Storage for windows when user stack is bogus. */ struct reg_window reg_window[NSWINS] __attribute__ ((aligned (16))); unsigned long rwbuf_stkptrs[NSWINS] __attribute__ ((aligned (8))); - unsigned long w_saved; - /* Arch-specific task state flags, see below. */ - unsigned long flags; - - /* For signal handling */ unsigned long sig_address __attribute__ ((aligned (8))); unsigned long sig_desc; - struct sigstack sstk_info; - unsigned long current_ds; - unsigned long new_signal; - - struct pt_regs *kregs; - struct exec core_exec; /* just what it says. */ }; @@ -75,30 +64,18 @@ struct thread_struct { PAGE_SHARED , VM_READ | VM_WRITE | VM_EXEC, NULL, &init_mm.mmap } #define INIT_TSS { \ -/* FPU regs */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, \ -/* FPU status */ \ - 0, \ /* ksp, kpc, wstate, cwp, secctx */ \ 0, 0, 0, 0, 0, \ +/* flags, new_signal, current_ds, */ \ + SPARC_FLAG_KTHREAD, 0, USER_DS, \ +/* w_saved, kregs, */ \ + 0, 0, \ /* reg_window */ \ -{ { { 0, }, { 0, } }, }, \ + { { { 0, }, { 0, } }, }, \ /* rwbuf_stkptrs */ \ -{ 0, 0, 0, 0, 0, 0, 0, 0, }, \ -/* w_saved */ \ - 0, \ -/* flags */ \ - SPARC_FLAG_KTHREAD, \ -/* sig_address, sig_desc */ \ - 0, 0, \ -/* ex, sstk_info, current_ds, */ \ - { 0, 0, }, USER_DS, \ -/* new_signal, kregs */ \ - 0, 0, \ -/* core_exec */ \ -{ 0, }, \ + { 0, 0, 0, 0, 0, 0, 0, 0, }, \ +/* sig_address, sig_desc, sstk_info, core_exec */ \ + 0, 0, { 0, 0, }, { 0, }, \ } #ifndef __ASSEMBLY__ @@ -112,11 +89,12 @@ extern __inline__ unsigned long thread_saved_pc(struct thread_struct *t) /* Do necessary setup to start up a newly executed thread. */ #define start_thread(regs, pc, sp) \ do { \ - regs->tstate = (regs->tstate & (TSTATE_CWP)) | TSTATE_IE; \ + regs->tstate = (regs->tstate & (TSTATE_CWP)) | (TSTATE_IE|TSTATE_PEF); \ regs->tpc = ((pc & (~3)) - 4); \ regs->tnpc = regs->tpc + 4; \ regs->y = 0; \ current->tss.flags &= ~SPARC_FLAG_32BIT; \ + current->tss.wstate = (1 << 3); \ __asm__ __volatile__( \ "stx %%g0, [%0 + %2 + 0x00]\n\t" \ "stx %%g0, [%0 + %2 + 0x08]\n\t" \ @@ -147,11 +125,12 @@ do { \ pc &= 0x00000000ffffffffUL; \ sp &= 0x00000000ffffffffUL; \ \ - regs->tstate = (regs->tstate & (TSTATE_CWP)) | (TSTATE_IE | TSTATE_AM); \ + regs->tstate = (regs->tstate & (TSTATE_CWP))|(TSTATE_IE|TSTATE_AM|TSTATE_PEF); \ regs->tpc = ((pc & (~3)) - 4); \ regs->tnpc = regs->tpc + 4; \ regs->y = 0; \ current->tss.flags |= SPARC_FLAG_32BIT; \ + current->tss.wstate = (2 << 3); \ zero = 0; \ __asm__ __volatile__( \ "stx %%g0, [%0 + %2 + 0x00]\n\t" \ diff --git a/include/asm-sparc64/psrcompat.h b/include/asm-sparc64/psrcompat.h index b971514d6b59..22e9da3d6091 100644 --- a/include/asm-sparc64/psrcompat.h +++ b/include/asm-sparc64/psrcompat.h @@ -1,4 +1,4 @@ -/* $Id: psrcompat.h,v 1.3 1997/06/05 06:22:54 davem Exp $ */ +/* $Id: psrcompat.h,v 1.4 1997/06/20 11:54:39 davem Exp $ */ #ifndef _SPARC64_PSRCOMPAT_H #define _SPARC64_PSRCOMPAT_H @@ -23,33 +23,19 @@ extern inline unsigned int tstate_to_psr(unsigned long tstate) { - unsigned int psr; unsigned long vers; - /* These fields are in the same place. */ - psr = (tstate & (TSTATE_CWP | TSTATE_PEF)); - - /* This is what the user would have always seen. */ - psr |= PSR_S; - - /* Slam in the 32-bit condition codes. */ - psr |= ((tstate & TSTATE_ICC) >> 12); - - /* This is completely arbitrary. */ __asm__ __volatile__("rdpr %%ver, %0" : "=r" (vers)); - psr |= ((vers << 8) >> 32) & PSR_IMPL; - psr |= ((vers << 24) >> 36) & PSR_VERS; - - return psr; + return ((tstate & TSTATE_CWP) | + PSR_S | + ((tstate & TSTATE_ICC) >> 12) | + (((vers << 8) >> 32) & PSR_IMPL) | + (((vers << 24) >> 36) & PSR_VERS)); } extern inline unsigned long psr_to_tstate_icc(unsigned int psr) { - unsigned long tstate; - - tstate = ((unsigned long)(psr & PSR_ICC)) << 12; - - return tstate; + return ((unsigned long)(psr & PSR_ICC)) << 12; } #endif /* !(_SPARC64_PSRCOMPAT_H) */ diff --git a/include/asm-sparc64/pstate.h b/include/asm-sparc64/pstate.h index 2233ee7f069b..a1e1414d62d5 100644 --- a/include/asm-sparc64/pstate.h +++ b/include/asm-sparc64/pstate.h @@ -1,4 +1,4 @@ -/* $Id: pstate.h,v 1.4 1997/05/29 12:45:02 jj Exp $ */ +/* $Id: pstate.h,v 1.6 1997/06/25 07:39:45 jj Exp $ */ #ifndef _SPARC64_PSTATE_H #define _SPARC64_PSTATE_H @@ -14,6 +14,9 @@ #define PSTATE_CLE 0x0000000000000200 /* Current Little Endian. */ #define PSTATE_TLE 0x0000000000000100 /* Trap Little Endian. */ #define PSTATE_MM 0x00000000000000c0 /* Memory Model. */ +#define PSTATE_TSO 0x0000000000000000 /* MM: Total Store Order */ +#define PSTATE_PSO 0x0000000000000040 /* MM: Partial Store Order */ +#define PSTATE_RMO 0x0000000000000080 /* MM: Relaxed Memory Order */ #define PSTATE_RED 0x0000000000000020 /* Reset Error Debug State. */ #define PSTATE_PEF 0x0000000000000010 /* Floating Point Enable. */ #define PSTATE_AM 0x0000000000000008 /* Address Mask. */ @@ -47,6 +50,9 @@ #define TSTATE_CLE 0x0000000000020000 /* Current Little Endian. */ #define TSTATE_TLE 0x0000000000010000 /* Trap Little Endian. */ #define TSTATE_MM 0x000000000000c000 /* Memory Model. */ +#define TSTATE_TSO 0x0000000000000000 /* MM: Total Store Order */ +#define TSTATE_PSO 0x0000000000004000 /* MM: Partial Store Order */ +#define TSTATE_RMO 0x0000000000008000 /* MM: Relaxed Memory Order */ #define TSTATE_RED 0x0000000000002000 /* Reset Error Debug State. */ #define TSTATE_PEF 0x0000000000001000 /* Floating Point Enable. */ #define TSTATE_AM 0x0000000000000800 /* Address Mask. */ diff --git a/include/asm-sparc64/ptrace.h b/include/asm-sparc64/ptrace.h index 5da6f6dd1793..a4784d41e512 100644 --- a/include/asm-sparc64/ptrace.h +++ b/include/asm-sparc64/ptrace.h @@ -1,4 +1,4 @@ -/* $Id: ptrace.h,v 1.8 1997/05/27 19:30:27 jj Exp $ */ +/* $Id: ptrace.h,v 1.12 1997/06/24 16:30:35 davem Exp $ */ #ifndef _SPARC64_PTRACE_H #define _SPARC64_PTRACE_H @@ -15,7 +15,8 @@ struct pt_regs { unsigned long tstate; unsigned long tpc; unsigned long tnpc; - unsigned long y; + unsigned int y; + unsigned int fprs; }; struct pt_regs32 { @@ -137,6 +138,7 @@ extern void show_regs(struct pt_regs *); #define PT_V9_TPC 0x88 #define PT_V9_TNPC 0x90 #define PT_V9_Y 0x98 +#define PT_V9_FPRS 0x9c #define PT_TSTATE PT_V9_TSTATE #define PT_TPC PT_V9_TPC #define PT_TNPC PT_V9_TNPC @@ -265,6 +267,28 @@ extern void show_regs(struct pt_regs *); #define PTRACE_GETFPAREGS 20 #define PTRACE_SETFPAREGS 21 +/* There are for debugging 64-bit processes, either from a 32 or 64 bit + * parent. Thus their compliments are for debugging 32-bit processes only. + */ + +#define PTRACE_GETREGS64 22 +#define PTRACE_SETREGS64 23 +/* PTRACE_SYSCALL is 24 */ +#define PTRACE_GETFPREGS64 25 +#define PTRACE_SETFPREGS64 26 + #define PTRACE_GETUCODE 29 /* stupid bsd-ism */ +/* These are for 32-bit processes debugging 64-bit ones. + * Here addr and addr2 are passed in %g2 and %g3 respectively. + */ +#define PTRACE_PEEKTEXT64 (30 + PTRACE_PEEKTEXT) +#define PTRACE_POKETEXT64 (30 + PTRACE_POKETEXT) +#define PTRACE_PEEKDATA64 (30 + PTRACE_PEEKDATA) +#define PTRACE_POKEDATA64 (30 + PTRACE_POKEDATA) +#define PTRACE_READDATA64 (30 + PTRACE_READDATA) +#define PTRACE_WRITEDATA64 (30 + PTRACE_WRITEDATA) +#define PTRACE_READTEXT64 (30 + PTRACE_READTEXT) +#define PTRACE_WRITETEXT64 (30 + PTRACE_WRITETEXT) + #endif /* !(_SPARC64_PTRACE_H) */ diff --git a/include/asm-sparc64/reg.h b/include/asm-sparc64/reg.h index 716b8f8c6887..ea3fc6e9c43d 100644 --- a/include/asm-sparc64/reg.h +++ b/include/asm-sparc64/reg.h @@ -1,4 +1,4 @@ -/* $Id: reg.h,v 1.1 1996/12/26 14:22:34 davem Exp $ +/* $Id: reg.h,v 1.2 1997/06/24 23:19:55 davem Exp $ * linux/asm-sparc64/reg.h * Layout of the registers as expected by gdb on the Sparc * we should replace the user.h definitions with those in @@ -76,4 +76,33 @@ struct fpu { struct fp_status f_fpstatus; }; +struct regs64 { + unsigned long r_g1; + unsigned long r_g2; + unsigned long r_g3; + unsigned long r_g4; + unsigned long r_g5; + unsigned long r_g6; + unsigned long r_g7; + unsigned long r_o0; + unsigned long r_o1; + unsigned long r_o2; + unsigned long r_o3; + unsigned long r_o4; + unsigned long r_o5; + unsigned long r_o6; + unsigned long r_o7; + unsigned long tstate; + unsigned long tpc; + unsigned long tnpc; + unsigned int y; + unsigned int fprs; +}; + +struct fp_status64 { + unsigned long regs[32]; + unsigned long fsr; +}; + + #endif /* __SPARC64_REG_H */ diff --git a/include/asm-sparc64/sigcontext.h b/include/asm-sparc64/sigcontext.h index 2e072c0c8809..3fba2f834cce 100644 --- a/include/asm-sparc64/sigcontext.h +++ b/include/asm-sparc64/sigcontext.h @@ -1,4 +1,4 @@ -/* $Id: sigcontext.h,v 1.6 1997/06/16 00:29:25 richard Exp $ */ +/* $Id: sigcontext.h,v 1.8 1997/06/20 11:54:41 davem Exp $ */ #ifndef __SPARC64_SIGCONTEXT_H #define __SPARC64_SIGCONTEXT_H @@ -66,17 +66,6 @@ typedef struct { int si_mask; } __siginfo32_t; -typedef struct { - unsigned int si_float_regs [32]; - unsigned int si_fsr; - unsigned int si_fpqdepth; - struct { - unsigned int *insn_addr; - unsigned int insn; - } si_fpqueue [16]; -} __siginfo_fpu32_t; - - typedef struct { struct pt_regs si_regs; int si_mask; @@ -85,6 +74,7 @@ typedef struct { typedef struct { unsigned int si_float_regs [64]; unsigned long si_fsr; + unsigned long si_gsr; unsigned int si_fpqdepth; struct { unsigned int *insn_addr; diff --git a/include/asm-sparc64/string.h b/include/asm-sparc64/string.h index b420d80bb9f7..fd45c2e06554 100644 --- a/include/asm-sparc64/string.h +++ b/include/asm-sparc64/string.h @@ -1,4 +1,4 @@ -/* $Id: string.h,v 1.5 1997/05/18 04:16:57 davem Exp $ +/* $Id: string.h,v 1.6 1997/06/24 17:29:14 jj Exp $ * string.h: External definitions for optimized assembly string * routines for the Linux Kernel. * @@ -13,8 +13,14 @@ #ifdef __KERNEL__ +#include + extern void __memmove(void *,const void *,__kernel_size_t); extern __kernel_size_t __memcpy(void *,const void *,__kernel_size_t); +extern __kernel_size_t __memcpy_short(void *,const void *,__kernel_size_t,long,long); +extern __kernel_size_t __memcpy_entry(void *,const void *,__kernel_size_t,long,long); +extern __kernel_size_t __memcpy_16plus(void *,const void *,__kernel_size_t,long,long); +extern __kernel_size_t __memcpy_384plus(void *,const void *,__kernel_size_t,long,long); extern __kernel_size_t __memset(void *,int,__kernel_size_t); #ifndef EXPORT_SYMTAB @@ -35,32 +41,31 @@ extern __kernel_size_t __memset(void *,int,__kernel_size_t); extern inline void *__constant_memcpy(void *to, const void *from, __kernel_size_t n) { - extern void __copy_1page(void *, const void *); - if(n) { if(n <= 32) { __builtin_memcpy(to, from, n); +#if 0 + } else if (n < 384) { + __memcpy_16plus(to, from, n, ASI_BLK_P, ASI_BLK_P); } else { -#if 0 - switch(n) { - case 8192: - __copy_1page(to, from); - break; - default: -#endif - __memcpy(to, from, n); -#if 0 - break; - } -#endif + __memcpy_384plus(to, from, n, ASI_BLK_P, ASI_BLK_P); } +#else + } else { + __memcpy(to, from, n); + } +#endif } return to; } extern inline void *__nonconstant_memcpy(void *to, const void *from, __kernel_size_t n) { +#if 0 + __memcpy_entry(to, from, n, ASI_BLK_P, ASI_BLK_P); +#else __memcpy(to, from, n); +#endif return to; } @@ -74,15 +79,13 @@ extern inline void *__nonconstant_memcpy(void *to, const void *from, __kernel_si extern inline void *__constant_c_and_count_memset(void *s, char c, __kernel_size_t count) { - extern void *bzero_1page(void *); + extern void *__bzero_1page(void *); extern __kernel_size_t __bzero(void *, __kernel_size_t); if(!c) { -#if 0 - if(count == 8192) - bzero_1page(s); + if (count == 8192) + __bzero_1page(s); else -#endif __bzero(s, count); } else { __memset(s, c, count); diff --git a/include/asm-sparc64/system.h b/include/asm-sparc64/system.h index c428fdcd9c46..6e7c42e55e34 100644 --- a/include/asm-sparc64/system.h +++ b/include/asm-sparc64/system.h @@ -1,4 +1,4 @@ -/* $Id: system.h,v 1.23 1997/06/16 06:17:06 davem Exp $ */ +/* $Id: system.h,v 1.26 1997/06/28 10:04:03 davem Exp $ */ #ifndef __SPARC64_SYSTEM_H #define __SPARC64_SYSTEM_H @@ -95,45 +95,15 @@ extern __inline__ void flushw_user(void) { __asm__ __volatile__(" rdpr %%otherwin, %%g1 - brz,pt %%g1, 2f + brz,pt %%g1, 1f + mov %%o7, %%g3 + call __flushw_user clr %%g2 -1: - save %%sp, %0, %%sp - rdpr %%otherwin, %%g1 - brnz,pt %%g1, 1b - add %%g2, 1, %%g2 -1: - subcc %%g2, 1, %%g2 - bne,pt %%xcc, 1b - restore %%g0, %%g0, %%g0 -2: - " : : "i" (-REGWIN_SZ) - : "g1", "g2", "cc"); +1:" : : : "g1", "g2", "g3"); } #define flush_user_windows flushw_user -#ifdef __SMP__ - -#include - -#define SWITCH_ENTER(prev) \ - if((prev)->flags & PF_USEDFPU) { \ - fprs_write(FPRS_FEF); \ - fpsave((unsigned long *) &(prev)->tss.float_regs[0], \ - &(prev)->tss.fsr); \ - (prev)->flags &= ~PF_USEDFPU; \ - (prev)->tss.kregs->tstate &= ~TSTATE_PEF; \ - } - -#define SWITCH_DO_LAZY_FPU(next) -#else -#define SWITCH_ENTER(prev) -#define SWITCH_DO_LAZY_FPU(next) \ - if(last_task_used_math != (next)) \ - (next)->tss.kregs->tstate &= ~TSTATE_PEF -#endif - /* See what happens when you design the chip correctly? * NOTE NOTE NOTE this is extremely non-trivial what I * am doing here. GCC needs only one register to stuff @@ -146,13 +116,13 @@ extern __inline__ void flushw_user(void) do { \ __label__ switch_continue; \ register unsigned long task_pc asm("o7"); \ - SWITCH_ENTER(prev) \ - SWITCH_DO_LAZY_FPU(next); \ + (prev)->tss.kregs->fprs = 0; \ task_pc = ((unsigned long) &&switch_continue) - 0x8; \ __asm__ __volatile__( \ "rdpr %%pstate, %%g2\n\t" \ - "wrpr %%g2, 0x2, %%pstate\n\t" \ + "wrpr %%g2, 0x3, %%pstate\n\t" \ "flushw\n\t" \ +/*XXX*/ "wr %%g0, 0, %%fprs\n\t" \ "stx %%i6, [%%sp + 2047 + 0x70]\n\t" \ "stx %%i7, [%%sp + 2047 + 0x78]\n\t" \ "rdpr %%wstate, %%o5\n\t" \ @@ -160,19 +130,20 @@ do { \ "stx %%o5, [%%g6 + %2]\n\t" \ "rdpr %%cwp, %%o5\n\t" \ "stx %%o7, [%%g6 + %4]\n\t" \ - "stx %%o5, [%%g6 + %5]\n\t" \ + "st %%o5, [%%g6 + %5]\n\t" \ "mov %0, %%g6\n\t" \ - "ldx [%0 + %5], %%g1\n\t" \ - "wr %0, 0x0, %%pic\n\t" \ + "ld [%0 + %5], %%g1\n\t" \ "wrpr %%g1, %%cwp\n\t" \ "ldx [%%g6 + %2], %%o5\n\t" \ "ldx [%%g6 + %3], %%o6\n\t" \ "ldx [%%g6 + %4], %%o7\n\t" \ + "mov %%g6, %0\n\t" \ "wrpr %%o5, 0x0, %%wstate\n\t" \ "ldx [%%sp + 2047 + 0x70], %%i6\n\t" \ "ldx [%%sp + 2047 + 0x78], %%i7\n\t" \ + "wrpr %%g0, 0x96, %%pstate\n\t" \ "jmpl %%o7 + 0x8, %%g0\n\t" \ - " wrpr %%g2, 0x0, %%pstate\n\t" \ + " mov %0, %%g6\n\t" \ : /* No outputs */ \ : "r" (next), "r" (task_pc), \ "i" ((const unsigned long)(&((struct task_struct *)0)->tss.wstate)), \ diff --git a/include/asm-sparc64/uaccess.h b/include/asm-sparc64/uaccess.h index 460ccc11c1d4..54dee9bc60e0 100644 --- a/include/asm-sparc64/uaccess.h +++ b/include/asm-sparc64/uaccess.h @@ -1,4 +1,4 @@ -/* $Id: uaccess.h,v 1.14 1997/06/13 14:03:11 davem Exp $ */ +/* $Id: uaccess.h,v 1.19 1997/06/30 10:31:46 jj Exp $ */ #ifndef _ASM_UACCESS_H #define _ASM_UACCESS_H @@ -40,7 +40,7 @@ do { \ current->tss.ctx = (current->mm->context & 0x1fff); \ } \ spitfire_set_secondary_context(current->tss.ctx); \ - __asm__ __volatile__("flush %g4"); \ + __asm__ __volatile__("flush %g6"); \ } while(0) #define __user_ok(addr,size) 1 @@ -255,8 +255,47 @@ __asm__ __volatile__( \ extern int __get_user_bad(void); -extern __kernel_size_t __copy_to_user(void *to, void *from, __kernel_size_t size); -extern __kernel_size_t __copy_from_user(void *to, void *from, __kernel_size_t size); +extern __kernel_size_t __memcpy_short(void *to, const void *from, __kernel_size_t size, long asi_src, long asi_dst); +extern __kernel_size_t __memcpy_entry(void *to, const void *from, __kernel_size_t size, long asi_src, long asi_dst); +extern __kernel_size_t __memcpy_16plus(void *to, const void *from, __kernel_size_t size, long asi_src, long asi_dst); +extern __kernel_size_t __memcpy_386plus(void *to, const void *from, __kernel_size_t size, long asi_src, long asi_dst); + +#if 0 +extern __inline__ __kernel_size_t __copy_to_user(void *to, void *from, __kernel_size_t size) +{ + if (__builtin_constant_p(size)) { + if (!size) return 0; + if (size < 16) + return __memcpy_short(to,(const void *)from,size,ASI_BLK_P,ASI_BLK_S); + else if (size < 384) + return __memcpy_16plus(to,(const void *)from,size,ASI_BLK_P,ASI_BLK_S); + else + return __memcpy_386plus(to,(const void *)from,size,ASI_BLK_P,ASI_BLK_S); + } else { + if (!size) return 0; + return __memcpy_entry(to,(const void *)from,size,ASI_BLK_P,ASI_BLK_S); + } +} + +extern __inline__ __kernel_size_t __copy_from_user(void *to, void *from, __kernel_size_t size) +{ + if (__builtin_constant_p(size)) { + if (!size) return 0; + if (size < 16) + return __memcpy_short(to,(const void *)from,size,ASI_BLK_S,ASI_BLK_P); + else if (size < 384) + return __memcpy_16plus(to,(const void *)from,size,ASI_BLK_S,ASI_BLK_P); + else + return __memcpy_386plus(to,(const void *)from,size,ASI_BLK_S,ASI_BLK_P); + } else { + if (!size) return 0; + return __memcpy_entry(to,(const void *)from,size,ASI_BLK_S,ASI_BLK_P); + } +} +#else +extern __kernel_size_t __copy_from_user(void *to, const void *from, __kernel_size_t size); +extern __kernel_size_t __copy_to_user(void *to, const void *from, __kernel_size_t size); +#endif #define copy_to_user(to,from,n) \ __copy_to_user((void *)(to), \ @@ -288,21 +327,11 @@ if (__copy_from_user(to,from,n)) \ extern __inline__ __kernel_size_t __clear_user(void *addr, __kernel_size_t size) { - __kernel_size_t ret; - __asm__ __volatile__ (" - .section __ex_table,#alloc - .align 8 - .xword 1f,3 - .previous -1: - wr %%g0, %3, %%asi - mov %2, %%o1 - call __bzero_noasi - mov %1, %%o0 - mov %%o0, %0 - " : "=r" (ret) : "r" (addr), "r" (size), "i" (ASI_S) : - "cc", "o0", "o1", "o2", "o3", "o4", "o5", "o7", "g1", "g2", "g3", "g5", "g7"); - return ret; + extern __kernel_size_t __bzero_noasi(void *addr, __kernel_size_t size); + + + __asm__ __volatile__ ("wr %%g0, %0, %%asi" : : "i" (ASI_S)); + return __bzero_noasi(addr, size); } #define clear_user(addr,n) \ diff --git a/include/asm-sparc64/uctx.h b/include/asm-sparc64/uctx.h new file mode 100644 index 000000000000..6eaf16ef23f6 --- /dev/null +++ b/include/asm-sparc64/uctx.h @@ -0,0 +1,71 @@ +/* $Id: uctx.h,v 1.1 1997/06/18 16:51:58 davem Exp $ + * uctx.h: Sparc64 {set,get}context() register state layouts. + * + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + */ + +#ifndef __SPARC64_UCTX_H +#define __SPARC64_UCTX_H + +#define MC_TSTATE 0 +#define MC_PC 1 +#define MC_NPC 2 +#define MC_Y 3 +#define MC_G1 4 +#define MC_G2 5 +#define MC_G3 6 +#define MC_G4 7 +#define MC_G5 8 +#define MC_G6 9 +#define MC_G7 10 +#define MC_O0 11 +#define MC_O1 12 +#define MC_O2 13 +#define MC_O3 14 +#define MC_O4 15 +#define MC_O5 16 +#define MC_O6 17 +#define MC_O7 18 +#define MC_NGREG 19 + +typedef unsigned long mc_greg_t; +typedef mc_greg_t mc_gregset_t[MC_NGREG]; + +#define MC_MAXFPQ 16 +struct mc_fq { + unsigned long *mcfq_addr; + unsigned int mcfq_insn; +}; + +struct mc_fpu { + union { + unsigned int sregs[32]; + unsigned long dregs[32]; + long double qregs[16]; + } mcfpu_fregs; + unsigned long mcfpu_fsr; + unsigned long mcfpu_fprs; + unsigned long mcfpu_gsr; + struct mc_fq *mcfpu_fq; + unsigned char mcfpu_qcnt; + unsigned char mcfpu_qentsz; + unsigned char mcfpu_enab; +}; +typedef struct mc_fpu mc_fpu_t; + +typedef struct { + mc_gregset_t mc_gregs; + mc_greg_t mc_fp; + mc_greg_t mc_i7; + mc_fpu_t mc_fpregs; +} mcontext_t; + +struct ucontext { + struct ucontext *uc_link; + unsigned long uc_flags; + sigset_t uc_sigmask; + mcontext_t uc_mcontext; +}; +typedef struct ucontext ucontext_t; + +#endif /* __SPARC64_UCTX_H */ diff --git a/include/asm-sparc64/vaddrs.h b/include/asm-sparc64/vaddrs.h index cd82abb060f9..b88085668a1d 100644 --- a/include/asm-sparc64/vaddrs.h +++ b/include/asm-sparc64/vaddrs.h @@ -1,4 +1,4 @@ -/* $Id: vaddrs.h,v 1.6 1997/04/04 00:50:31 davem Exp $ */ +/* $Id: vaddrs.h,v 1.8 1997/06/27 14:55:13 jj Exp $ */ #ifndef _SPARC64_VADDRS_H #define _SPARC64_VADDRS_H @@ -14,12 +14,15 @@ * mappings for devices and is the speedup improvements of not loading * a pointer and then the value in the assembly code */ -#define IOBASE_VADDR 0xfffffd0000000000ULL /* Base for mapping pages */ -#define IOBASE_LEN 0x0000008000000000ULL /* Length of the IO area */ -#define IOBASE_END 0xfffffd8000000000ULL -#define DVMA_VADDR 0xfffffd8000000000ULL /* Base area of the DVMA on suns */ -#define DVMA_LEN 0x0000004000000000ULL /* Size of the DVMA address space */ -#define DVMA_END 0xfffffdc000000000ULL +#define IOBASE_VADDR 0x0000006000000000ULL /* Base for mapping pages */ +#define IOBASE_LEN 0x0000001000000000ULL /* Length of the IO area */ +#define IOBASE_END 0x0000007000000000ULL +#define DVMA_VADDR 0x0000007000000000ULL /* Base area of the DVMA on suns */ +#define DVMA_LEN 0x0000001000000000ULL /* Size of the DVMA address space */ +#define DVMA_END 0x0000008000000000ULL +#define MODULES_VADDR 0x0000000001000000ULL /* Where to map modules */ +#define MODULES_LEN 0x000000007f000000ULL +#define MODULES_END 0x0000000080000000ULL #endif /* !(_SPARC_VADDRS_H) */ diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h index ee593013be29..1a8e027cc2fa 100644 --- a/include/linux/binfmts.h +++ b/include/linux/binfmts.h @@ -50,6 +50,7 @@ extern int open_inode(struct inode * inode, int mode); extern int init_elf_binfmt(void); extern int init_elf32_binfmt(void); extern int init_aout_binfmt(void); +extern int init_aout32_binfmt(void); extern int init_script_binfmt(void); extern int init_java_binfmt(void); extern int init_em86_binfmt(void); diff --git a/include/linux/console_struct.h b/include/linux/console_struct.h index e9ef418f7d51..99ed0e347ee3 100644 --- a/include/linux/console_struct.h +++ b/include/linux/console_struct.h @@ -4,6 +4,8 @@ * Data structure and defines shared between console.c, vga.c and tga.c */ +#include + #define NPAR 16 struct vc_data { @@ -17,12 +19,17 @@ struct vc_data { unsigned char vc_halfcolor; /* Colour for half intensity mode */ unsigned long vc_origin; /* Used for EGA/VGA fast scroll */ unsigned long vc_scr_end; /* Used for EGA/VGA fast scroll */ - unsigned long vc_pos; unsigned long vc_x,vc_y; unsigned long vc_top,vc_bottom; unsigned long vc_state; unsigned long vc_npar,vc_par[NPAR]; +#ifdef CONFIG_FB_CONSOLE + unsigned short *vc_video_mem_start; /* Start of video RAM */ + unsigned short *vc_pos; +#else + unsigned long vc_pos; unsigned long vc_video_mem_start; /* Start of video RAM */ +#endif unsigned long vc_video_mem_end; /* End of video RAM (sort of) */ unsigned long vc_saved_x; unsigned long vc_saved_y; diff --git a/include/linux/dalloc.h b/include/linux/dalloc.h index 64f78b233aa7..a180bb94d1f9 100644 --- a/include/linux/dalloc.h +++ b/include/linux/dalloc.h @@ -12,13 +12,16 @@ #define D_MAXLEN 1024 /* public flags for d_add() */ -#define D_NORMAL 0 -#define D_BASKET 1 /* put into basket (deleted/unref'd files) */ -#define D_DUPLICATE 2 /* allow duplicate entries */ -#define D_NOCHECKDUP 4 /* no not check for duplicates */ - -/* public flags for d_flag */ -#define D_PRELOADED 8 +#define D_NORMAL 0 +#define D_BASKET 1 /* put into basket (deleted/unref'd files) */ +#define D_DUPLICATE 2 /* allow duplicate entries */ +#define D_NOCHECKDUP 4 /* no not check for duplicates */ +#define D_NEGATIVE 8 /* negative entry */ +#define D_PRELOADED 16 +#define D_DIR 32 /* directory entry - look out for allocation issues */ +#define D_HASHED 64 +#define D_ZOMBIE 128 +#define D_INC_DDIR 512 /* public flags for d_del() */ #define D_REMOVE 0 @@ -27,51 +30,77 @@ #define IS_ROOT(x) ((x) == (x)->d_parent) /* "quick string" -- I introduced this to shorten the parameter list - * of many routines. Think of it as a (str,stlen) pair. + * of many routines. Think of it as a (str,stlen,hash) pair. * Storing the len instead of doing strlen() very often is performance * critical. */ struct qstr { - char * name; - int len; + const unsigned char * name; + int len, hash; }; +/* Name hashing routines. Initial hash value */ +#define init_name_hash() 0 + +/* partial hash update function. Assume roughly 4 bits per character */ +static inline unsigned long partial_name_hash(unsigned char c, unsigned long prevhash) +{ + prevhash = (prevhash << 4) | (prevhash >> (8*sizeof(unsigned long)-4)); + return prevhash ^ c; +} + +/* Finally: cut down the number of bits to a int value (and try to avoid losing bits) */ +static inline unsigned long end_name_hash(unsigned long hash) +{ + if (sizeof(hash) > sizeof(unsigned int)) + hash += hash >> 4*sizeof(hash); + return (unsigned int) hash; +} + struct dentry { - union { - struct inode * d_inode; /* Where the name belongs to */ - unsigned long d_ino; /* for preliminary entries */ - } u; - struct dentry * d_parent; /* parent directory */ - struct dentry * d_next; /* hardlink aliasname / empty list */ - struct dentry * d_prev; /* hardlink aliasname */ + unsigned int d_flag; + unsigned int d_count; + struct inode * d_inode; /* Where the name belongs to */ + struct dentry * d_parent; /* parent directory */ + struct dentry * d_mounts; /* mount information */ + struct dentry * d_covers; + struct dentry * d_next; /* hardlink aliasname / empty list */ + struct dentry * d_prev; /* hardlink aliasname */ struct dentry * d_hash_next; struct dentry * d_hash_prev; struct dentry * d_basket_next; struct dentry * d_basket_prev; struct qstr d_name; - unsigned int d_flag; }; extern struct dentry * the_root; +/* + * These are the low-level FS interfaces to the dcache.. + */ +extern void d_instantiate(struct dentry *, struct inode *, int); +extern void d_delete(struct dentry *); + + /* Note that all these routines must be called with vfs_lock() held */ /* get inode, if necessary retrieve it with iget() */ extern blocking struct inode * d_inode(struct dentry ** changing_entry); -/* allocate proper space for the len */ -extern struct dentry * d_alloc(struct dentry * parent, int len, int isdir); +/* allocate/de-allocate */ +extern void d_free(struct dentry *); +extern struct dentry * d_alloc(struct dentry * parent, struct qstr *name, int isdir); -/* only used once at mount_root() */ +/* only used at mount-time */ extern blocking -struct dentry * d_alloc_root(struct inode * root_inode); +struct dentry * d_alloc_root(struct inode * root_inode, struct dentry * old_root); -/* d_inode is connected with inode, and d_name is copied from ininame. - * either of them may be NULL, but when ininame is NULL, dname must be - * set by the caller prior to calling this. */ +/* + * This adds the entry to the hash queues and initializes "d_inode". + * The entry was actually filled in earlier during "d_alloc()" + */ extern blocking -void d_add(struct dentry * entry, struct inode * inode, - struct qstr * ininame, int flags); +void d_add(struct dentry * entry, struct inode * inode, int flags); /* combination of d_alloc() and d_add(), less lookup overhead */ extern blocking @@ -85,17 +114,32 @@ void d_del(struct dentry * entry, int flags); /* used for rename() and baskets */ extern blocking -void d_move(struct dentry * entry, struct inode * newdir, - struct qstr * newname, struct qstr * newapp); +void d_move(struct dentry * entry, struct dentry * newparent, struct qstr * newname); /* appendix may either be NULL or be used for transname suffixes */ -extern struct dentry * d_lookup(struct inode * dir, struct qstr * name, - struct qstr * appendix); +extern struct dentry * d_lookup(struct dentry * dir, struct qstr * name); /* write full pathname into buffer and return length */ -extern int d_path(struct dentry * entry, struct inode * chroot, char * buf); +extern int d_path(struct dentry * entry, struct dentry * chroot, char * buf); extern struct dentry * d_basket(struct dentry * dir_entry); extern int d_isbasket(struct dentry * entry); + +/* + * Whee.. + */ +static inline void dput(struct dentry *dentry) +{ + if (dentry) + dentry->d_count--; +} + +static inline struct dentry * dget(struct dentry *dentry) +{ + if (dentry) + dentry->d_count++; + return dentry; +} + #endif diff --git a/include/linux/ext2_fs.h b/include/linux/ext2_fs.h index 9c66cfc29470..f3087fb44aa2 100644 --- a/include/linux/ext2_fs.h +++ b/include/linux/ext2_fs.h @@ -492,17 +492,15 @@ extern int ext2_ioctl (struct inode *, struct file *, unsigned int, /* namei.c */ extern void ext2_release (struct inode *, struct file *); -extern int ext2_lookup (struct inode *,const char *, int, struct inode **); -extern int ext2_create (struct inode *,const char *, int, int, - struct inode **); -extern int ext2_mkdir (struct inode *, const char *, int, int); -extern int ext2_rmdir (struct inode *, const char *, int); -extern int ext2_unlink (struct inode *, const char *, int); -extern int ext2_symlink (struct inode *, const char *, int, const char *); -extern int ext2_link (struct inode *, struct inode *, const char *, int); -extern int ext2_mknod (struct inode *, const char *, int, int, int); -extern int ext2_rename (struct inode *, const char *, int, - struct inode *, const char *, int); +extern int ext2_lookup (struct inode *,struct qstr *, struct inode **); +extern int ext2_create (struct inode *,struct dentry *,int); +extern int ext2_mkdir (struct inode *,struct dentry *,int); +extern int ext2_rmdir (struct inode *,struct dentry *); +extern int ext2_unlink (struct inode *,struct dentry *); +extern int ext2_symlink (struct inode *,struct dentry *,const char *); +extern int ext2_link (struct inode *, struct inode *, struct dentry *); +extern int ext2_mknod (struct inode *, struct dentry *, int, int); +extern int ext2_rename (struct inode *, struct dentry *,struct inode *, struct dentry *); /* super.c */ extern void ext2_error (struct super_block *, const char *, const char *, ...) diff --git a/include/linux/fs.h b/include/linux/fs.h index ddc349d3ea1d..724b62b7ace0 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -24,6 +24,8 @@ */ #define blocking /*routine may schedule()*/ +#include + /* * It's silly to have NR_OPEN bigger than NR_FILE, but I'll fix * that later. Anyway, now the file code is no longer dependent @@ -105,13 +107,6 @@ extern int max_files, nr_files; #define MS_MGC_VAL 0xC0ED0000 /* magic flag number to indicate "new" flags */ #define MS_MGC_MSK 0xffff0000 /* magic flag number mask */ -/* - * Public flags for namei() - */ -#define NAM_PLAIN 0 /* Retrieve last component of pathname as is. */ -#define NAM_FOLLOW_LINK 2 /* If last component of path is a symlink, follow it */ -#define NAM_FOLLOW_TRAILSLASH 4 /* Follow last symlink only if trailed by slash. */ - /* * Note that read-only etc flags are inode-specific: setting some file-system * flags just means all the inodes inherit those flags by default. It might be @@ -342,12 +337,9 @@ struct inode { struct inode *i_basket_prev; struct dentry *i_dentry; - short i_ddir_count; - short i_dent_count; unsigned short i_status; unsigned short i_reuse_count; - struct inode *i_mount; unsigned int i_flags; unsigned char i_lock; unsigned char i_dirt; @@ -503,8 +495,7 @@ struct super_block { unsigned long s_flags; unsigned long s_magic; unsigned long s_time; - struct inode *s_covered; - struct inode *s_mounted; + struct dentry *s_root; struct wait_queue *s_wait; struct inode *s_ibasket; @@ -553,16 +544,17 @@ struct file_operations { struct inode_operations { struct file_operations * default_file_ops; - int (*create) (struct inode *,const char *,int,int,struct inode **); - int (*lookup) (struct inode *,const char *,int,struct inode **); - int (*link) (struct inode *,struct inode *,const char *,int); - int (*unlink) (struct inode *,const char *,int); - int (*symlink) (struct inode *,const char *,int,const char *); - int (*mkdir) (struct inode *,const char *,int,int); - int (*rmdir) (struct inode *,const char *,int); - int (*mknod) (struct inode *,const char *,int,int,int); - int (*rename) (struct inode *,const char *,int,struct inode *,const char *,int); + int (*create) (struct inode *,struct dentry *,int); + int (*lookup) (struct inode *,struct qstr *name,struct inode **); + int (*link) (struct inode *,struct inode *,struct dentry *); + int (*unlink) (struct inode *,struct dentry *); + int (*symlink) (struct inode *,struct dentry *,const char *); + int (*mkdir) (struct inode *,struct dentry *,int); + int (*rmdir) (struct inode *,struct dentry *); + int (*mknod) (struct inode *,struct dentry *,int,int); + int (*rename) (struct inode *,struct dentry *,struct inode *,struct dentry *); int (*readlink) (struct inode *,char *,int); + struct dentry * (*follow_link) (struct inode *, struct dentry *); int (*readpage) (struct inode *, struct page *); int (*writepage) (struct inode *, struct page *); int (*bmap) (struct inode *,int); @@ -640,7 +632,7 @@ extern struct file_operations rdwr_pipe_fops; extern struct file_system_type *get_fs_type(const char *name); extern int fs_may_mount(kdev_t dev); -extern int fs_may_umount(kdev_t dev, struct inode * mount_root); +extern int fs_may_umount(kdev_t dev, struct dentry * root); extern int fs_may_remount_ro(kdev_t dev); extern struct file *inuse_filps; @@ -689,15 +681,32 @@ extern int fsync_dev(kdev_t dev); extern void sync_supers(kdev_t dev); extern int bmap(struct inode * inode,int block); extern int notify_change(struct inode *, struct iattr *); -extern int namei(int retr_mode, const char *pathname, struct inode **res_inode); extern int permission(struct inode * inode,int mask); extern int get_write_access(struct inode *inode); extern void put_write_access(struct inode *inode); extern int open_namei(const char * pathname, int flag, int mode, - struct inode ** res_inode, struct inode * base); + struct inode ** res_inode, struct dentry * base); extern int do_mknod(const char * filename, int mode, dev_t dev); extern int do_pipe(int *); +/* + * Kernel pointers have redundant information, so we can use a + * scheme where we can return either an error code or a dentry + * pointer with the same return value. + * + * This should be a per-architecture thing, to allow different + * error and pointer decisions. + */ +#define ERR_PTR(err) ((void *)((long)(err))) +#define PTR_ERR(ptr) ((long)(ptr)) +#define IS_ERR(ptr) ((unsigned long)(ptr) > (unsigned long)(-1000)) + +extern struct dentry * lookup_dentry(const char *, struct dentry *, int); +extern int __namei(const char *, struct inode **, int); + +#define namei(pathname, inode_p) __namei(pathname, inode_p, 1) +#define lnamei(pathname, inode_p) __namei(pathname, inode_p, 0) + #include /* Intended for short locks of the global data structures in inode.c. @@ -728,51 +737,24 @@ extern inline void vfs_unlock(void) extern void _get_inode(struct inode * inode); extern blocking void __iput(struct inode * inode); -/* This must not be called if the inode is not in use (i.e. given - * back with iput(). The atomic inc assumes that the inode is - * already in use, and just has to be incremented higher. - * Please do not directly manipulate i_count any more. - * Use iget, iinc and iput. - * You may test i_count for zero if you are aware that it - * might change under you. - */ -extern inline void iinc(struct inode * inode) -{ - atomic_inc(&inode->i_count); -} - -/* The same, but the inode may not be in use. This must be called - * with vfslock() held, and be asure that the inode argument is - * valid (i.e. not out of cache). So the vfs_lock() must span the - * retrieval method of the inode. - */ -extern inline void iinc_zero(struct inode * inode) -{ - if(!atomic_read(&inode->i_count)) { - atomic_inc(&inode->i_count); - _get_inode(inode); - } else - atomic_inc(&inode->i_count); -} - extern blocking void _iput(struct inode * inode); extern inline blocking void iput(struct inode * inode) { - if(inode) { + if (inode) { extern void wake_up_interruptible(struct wait_queue **q); - if(inode->i_pipe) + if (inode->i_pipe) wake_up_interruptible(&inode->u.pipe_i.wait); /* It does not matter if somebody re-increments it in between, * only the _last_ user needs to call _iput(). */ - if(atomic_dec_and_test(&inode->i_count) && inode->i_ddir_count <= 0) + if (atomic_dec_and_test(&inode->i_count)) _iput(inode); } } -extern blocking struct inode * __iget(struct super_block * sb, unsigned long nr, int crsmnt); +extern blocking struct inode * iget(struct super_block * sb, unsigned long nr); extern blocking void _clear_inode(struct inode * inode, int external, int verbose); extern blocking inline void clear_inode(struct inode * inode) { @@ -795,16 +777,6 @@ extern inline blocking struct inode * get_empty_inode(void) */ blocking struct inode * get_empty_inode_hashed(dev_t i_dev, unsigned long i_ino); -extern blocking int _free_ibasket(struct super_block * sb); -extern inline blocking int free_ibasket(struct super_block * sb) -{ - int res; - vfs_lock(); - res = _free_ibasket(sb); - vfs_unlock(); - return res; -} - extern void insert_inode_hash(struct inode *); extern blocking struct inode * get_pipe_inode(void); extern int get_unused_fd(void); @@ -872,12 +844,6 @@ extern int dcache_lookup(struct inode *, const char *, int, unsigned long *); extern int inode_change_ok(struct inode *, struct iattr *); extern void inode_setattr(struct inode *, struct iattr *); -extern inline blocking -struct inode * iget(struct super_block * sb, unsigned long nr) -{ - return __iget(sb, nr, 1); -} - /* kludge to get SCSI modules working */ #include #include diff --git a/include/linux/ghash.h b/include/linux/ghash.h new file mode 100644 index 000000000000..278f6c2f630d --- /dev/null +++ b/include/linux/ghash.h @@ -0,0 +1,218 @@ +/* + * include/linux/ghash.h -- generic hashing with fuzzy retrieval + * + * (C) 1997 Thomas Schoebel-Theuer + * + * The algorithms implemented here seem to be a completely new invention, + * and I'll publish the fundamentals in a paper. + */ + +#ifndef _GHASH_H +#define _GHASH_H +/* HASHSIZE _must_ be a power of two!!! */ + + +#define DEF_HASH_FUZZY_STRUCTS(NAME,HASHSIZE,TYPE) \ +\ +struct NAME##_table {\ + TYPE * hashtable[HASHSIZE];\ + TYPE * sorted_list;\ + int nr_entries;\ +};\ +\ +struct NAME##_ptrs {\ + TYPE * next_hash;\ + TYPE * prev_hash;\ + TYPE * next_sorted;\ + TYPE * prev_sorted;\ +}; + +#define DEF_HASH_FUZZY(LINKAGE,NAME,HASHSIZE,TYPE,PTRS,KEYTYPE,KEY,KEYCMP,KEYEQ,HASHFN)\ +\ +LINKAGE void insert_##NAME##_hash(struct NAME##_table * tbl, TYPE * elem)\ +{\ + int ix = HASHFN(elem->KEY);\ + TYPE ** base = &tbl->hashtable[ix];\ + TYPE * ptr = *base;\ + TYPE * prev = NULL;\ +\ + tbl->nr_entries++;\ + while(ptr && KEYCMP(ptr->KEY, elem->KEY)) {\ + base = &ptr->PTRS.next_hash;\ + prev = ptr;\ + ptr = *base;\ + }\ + elem->PTRS.next_hash = ptr;\ + elem->PTRS.prev_hash = prev;\ + if(ptr) {\ + ptr->PTRS.prev_hash = elem;\ + }\ + *base = elem;\ +\ + ptr = prev;\ + if(!ptr) {\ + ptr = tbl->sorted_list;\ + prev = NULL;\ + } else {\ + prev = ptr->PTRS.prev_sorted;\ + }\ + while(ptr) {\ + TYPE * next = ptr->PTRS.next_hash;\ + if(next && KEYCMP(next->KEY, elem->KEY)) {\ + prev = ptr;\ + ptr = next;\ + } else if(KEYCMP(ptr->KEY, elem->KEY)) {\ + prev = ptr;\ + ptr = ptr->PTRS.next_sorted;\ + } else\ + break;\ + }\ + elem->PTRS.next_sorted = ptr;\ + elem->PTRS.prev_sorted = prev;\ + if(ptr) {\ + ptr->PTRS.prev_sorted = elem;\ + }\ + if(prev) {\ + prev->PTRS.next_sorted = elem;\ + } else {\ + tbl->sorted_list = elem;\ + }\ +}\ +\ +LINKAGE void remove_##NAME##_hash(struct NAME##_table * tbl, TYPE * elem)\ +{\ + TYPE * next = elem->PTRS.next_hash;\ + TYPE * prev = elem->PTRS.prev_hash;\ +\ + tbl->nr_entries--;\ + if(next)\ + next->PTRS.prev_hash = prev;\ + if(prev)\ + prev->PTRS.next_hash = next;\ + else {\ + int ix = HASHFN(elem->KEY);\ + tbl->hashtable[ix] = next;\ + }\ +\ + next = elem->PTRS.next_sorted;\ + prev = elem->PTRS.prev_sorted;\ + if(next)\ + next->PTRS.prev_sorted = prev;\ + if(prev)\ + prev->PTRS.next_sorted = next;\ + else\ + tbl->sorted_list = next;\ +}\ +\ +LINKAGE TYPE * find_##NAME##_hash(struct NAME##_table * tbl, KEYTYPE pos)\ +{\ + int ix = hashfn(pos);\ + TYPE * ptr = tbl->hashtable[ix];\ + while(ptr && KEYCMP(ptr->KEY, pos))\ + ptr = ptr->PTRS.next_hash;\ + if(ptr && !KEYEQ(ptr->KEY, pos))\ + ptr = NULL;\ + return ptr;\ +}\ +\ +LINKAGE TYPE * find_##NAME##_hash_fuzzy(struct NAME##_table * tbl, KEYTYPE pos)\ +{\ + int ix;\ + int offset;\ + TYPE * ptr;\ + TYPE * next;\ +\ + ptr = tbl->sorted_list;\ + if(!ptr || KEYCMP(pos, ptr->KEY))\ + return NULL;\ + ix = HASHFN(pos);\ + offset = HASHSIZE;\ + do {\ + offset >>= 1;\ + next = tbl->hashtable[(ix+offset) & ((HASHSIZE)-1)];\ + if(next && (KEYCMP(next->KEY, pos) || KEYEQ(next->KEY, pos))\ + && KEYCMP(ptr->KEY, next->KEY))\ + ptr = next;\ + } while(offset);\ +\ + for(;;) {\ + next = ptr->PTRS.next_hash;\ + if(next) {\ + if(KEYCMP(next->KEY, pos)) {\ + ptr = next;\ + continue;\ + }\ + }\ + next = ptr->PTRS.next_sorted;\ + if(next && KEYCMP(next->KEY, pos)) {\ + ptr = next;\ + continue;\ + }\ + return ptr;\ + }\ + return NULL;\ +} + +#define DEF_HASH_STRUCTS(NAME,HASHSIZE,TYPE) \ +\ +struct NAME##_table {\ + TYPE * hashtable[HASHSIZE];\ + int nr_entries;\ +};\ +\ +struct NAME##_ptrs {\ + TYPE * next_hash;\ + TYPE * prev_hash;\ +}; + +#define DEF_HASH(LINKAGE,NAME,HASHSIZE,TYPE,PTRS,KEYTYPE,KEY,KEYCMP,KEYEQ,HASHFN)\ +\ +LINKAGE void insert_##NAME##_hash(struct NAME##_table * tbl, TYPE * elem)\ +{\ + int ix = HASHFN(elem->KEY);\ + TYPE ** base = &tbl->hashtable[ix];\ + TYPE * ptr = *base;\ + TYPE * prev = NULL;\ +\ + tbl->nr_entries++;\ + while(ptr && KEYCMP(ptr->KEY, elem->KEY)) {\ + base = &ptr->PTRS.next_hash;\ + prev = ptr;\ + ptr = *base;\ + }\ + elem->PTRS.next_hash = ptr;\ + elem->PTRS.prev_hash = prev;\ + if(ptr) {\ + ptr->PTRS.prev_hash = elem;\ + }\ + *base = elem;\ +}\ +\ +LINKAGE void remove_##NAME##_hash(struct NAME##_table * tbl, TYPE * elem)\ +{\ + TYPE * next = elem->PTRS.next_hash;\ + TYPE * prev = elem->PTRS.prev_hash;\ +\ + tbl->nr_entries--;\ + if(next)\ + next->PTRS.prev_hash = prev;\ + if(prev)\ + prev->PTRS.next_hash = next;\ + else {\ + int ix = HASHFN(elem->KEY);\ + tbl->hashtable[ix] = next;\ + }\ +}\ +\ +LINKAGE TYPE * find_##NAME##_hash(struct NAME##_table * tbl, KEYTYPE pos)\ +{\ + int ix = hashfn(pos);\ + TYPE * ptr = tbl->hashtable[ix];\ + while(ptr && KEYCMP(ptr->KEY, pos))\ + ptr = ptr->PTRS.next_hash;\ + if(ptr && !KEYEQ(ptr->KEY, pos))\ + ptr = NULL;\ + return ptr;\ +} + +#endif diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index 14762045e8fe..d95e99211da8 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h @@ -312,7 +312,8 @@ extern struct inode * proc_get_inode(struct super_block *, int, struct proc_dir_ extern void proc_statfs(struct super_block *, struct statfs *, int); extern void proc_read_inode(struct inode *); extern void proc_write_inode(struct inode *); -extern int proc_match(int, const char *, struct proc_dir_entry *); + +extern int proc_match(int, const char *,struct proc_dir_entry *); /* * These are generic /proc routines that use the internal @@ -322,7 +323,7 @@ extern int proc_match(int, const char *, struct proc_dir_entry *); * of the /proc/ subdirectories. */ extern int proc_readdir(struct inode *, struct file *, void *, filldir_t); -extern int proc_lookup(struct inode *, const char *, int, struct inode **); +extern int proc_lookup(struct inode *, struct qstr *, struct inode **); struct openpromfs_dev { struct openpromfs_dev *next; @@ -334,7 +335,7 @@ struct openpromfs_dev { }; extern struct inode_operations * proc_openprom_register(int (*readdir)(struct inode *, struct file *, void *, filldir_t), - int (*lookup)(struct inode *, const char *, int, struct inode **), + int (*lookup)(struct inode *, struct qstr *, struct inode **), void (*use)(struct inode *, int), struct openpromfs_dev ***); extern void proc_openprom_deregister(void); @@ -362,9 +363,6 @@ extern struct inode_operations proc_ringbuf_inode_operations; #endif extern struct inode_operations proc_omirr_inode_operations; -/* Not sure whether this belongs here */ -int proc_arbitrary_lookup(struct inode * dir, const char * name, - int len, struct inode ** result); #endif /* diff --git a/include/linux/rose.h b/include/linux/rose.h index 2ca68dbaa3a5..cd82c95c3ac8 100644 --- a/include/linux/rose.h +++ b/include/linux/rose.h @@ -8,8 +8,9 @@ #define ROSE_KERNEL_H #define PF_ROSE AF_ROSE -#define ROSE_MTU 128 +#define ROSE_MTU 251 +#define ROSE_DEFER 1 #define ROSE_T1 2 #define ROSE_T2 3 #define ROSE_T3 4 @@ -17,7 +18,22 @@ #define ROSE_QBITINCL 6 #define ROSE_HOLDBACK 7 +#define SIOCRSGCAUSE (SIOCPROTOPRIVATE+0) +#define SIOCRSSCAUSE (SIOCPROTOPRIVATE+1) #define SIOCRSL2CALL (SIOCPROTOPRIVATE+2) +#define SIOCRSACCEPT (SIOCPROTOPRIVATE+3) +#define SIOCRSCLRRT (SIOCPROTOPRIVATE+4) + +#define ROSE_DTE_ORIGINATED 0x00 +#define ROSE_NUMBER_BUSY 0x01 +#define ROSE_INVALID_FACILITY 0x03 +#define ROSE_NETWORK_CONGESTION 0x05 +#define ROSE_OUT_OF_ORDER 0x09 +#define ROSE_ACCESS_BARRED 0x0B +#define ROSE_NOT_OBTAINABLE 0x0D +#define ROSE_REMOTE_PROCEDURE 0x11 +#define ROSE_LOCAL_PROCEDURE 0x13 +#define ROSE_SHIP_ABSENT 0x39 typedef struct { char rose_addr[5]; @@ -40,4 +56,9 @@ struct rose_route_struct { ax25_address digipeaters[AX25_MAX_DIGIS]; }; +struct rose_cause_struct { + unsigned char cause; + unsigned char diagnostic; +}; + #endif diff --git a/include/linux/sched.h b/include/linux/sched.h index 9346ab2a77fe..361498eadb4f 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -130,7 +130,7 @@ struct files_struct { struct fs_struct { int count; int umask; - struct inode * root, * pwd; + struct dentry * root, * pwd; }; #define INIT_FS { \ diff --git a/include/linux/selection.h b/include/linux/selection.h index 058e35588ccd..3779429b45c8 100644 --- a/include/linux/selection.h +++ b/include/linux/selection.h @@ -23,11 +23,11 @@ extern unsigned long get_video_size_row(unsigned int console); #define get_video_num_columns(dummy) video_num_columns #define get_video_num_lines(dummy) video_num_lines #define get_video_size_row(dummy) video_size_row -#endif - extern unsigned long video_num_columns; extern unsigned long video_num_lines; extern unsigned long video_size_row; +#endif + extern unsigned char video_type; extern unsigned long video_mem_base; extern unsigned long video_mem_term; @@ -73,6 +73,8 @@ extern void putconsxy(int currcons, char *p); /* how to access screen memory */ +#include + #if defined(CONFIG_TGA_CONSOLE) extern int tga_blitc(unsigned int, unsigned long); diff --git a/include/linux/simp.h b/include/linux/simp.h new file mode 100644 index 000000000000..e9cb6e52b169 --- /dev/null +++ b/include/linux/simp.h @@ -0,0 +1,39 @@ +/* + * include/linux/simp.h -- simple allocator for cached objects + * + * This is meant as a faster and simpler (not full-featured) replacement + * for SLAB, thus the name "simp" :-) + * + * (C) 1997 Thomas Schoebel-Theuer + */ + +#ifndef SIMP_H +#define SIMP_H + +/* used for constructors / destructors */ +typedef void (*structor)(void *); + +/* create an object cache */ +/* positive clearable_offset means the next two pointers at that offset + * can be internally used for freelist pointers when the object is + * deallocated / not in use; + * if there is no space inside the element that can be reused for + * this purpose, supply -1. Using positive offsets is essential for + * saving space with very small-sized objects. + * + * Note for big-sized objects in the range of whole pages, use + * the fast Linux page allocator instead, directly. + */ +extern struct simp * simp_create(char * name, long size, long clearable_offset, + structor first_ctor, + structor again_ctor, + structor dtor); + +/* alloc / dealloc routines */ +extern void * simp_alloc(struct simp * simp); +extern void simp_free(void * objp); + +/* garbage collection */ +extern long simp_garbage(void); + +#endif diff --git a/include/linux/slab.h b/include/linux/slab.h index 21a01f013c2e..0e3c0831d076 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -56,7 +56,7 @@ extern void *kmem_cache_alloc(kmem_cache_t *, int); extern void kmem_cache_free(kmem_cache_t *, void *); extern void *kmalloc(size_t, int); -extern void kfree(void *); +extern void kfree(const void *); extern void kfree_s(void *, size_t); extern int kmem_cache_reap(int, int, int); diff --git a/include/linux/tty.h b/include/linux/tty.h index cab84d21299b..2d9bb6e196c5 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -90,13 +90,19 @@ extern struct screen_info screen_info; struct tty_flip_buffer { struct tq_struct tqueue; - unsigned char char_buf[2*TTY_FLIPBUF_SIZE]; - char flag_buf[2*TTY_FLIPBUF_SIZE]; + struct semaphore pty_sem; char *char_buf_ptr; unsigned char *flag_buf_ptr; int count; int buf_num; + unsigned char char_buf[2*TTY_FLIPBUF_SIZE]; + char flag_buf[2*TTY_FLIPBUF_SIZE]; + unsigned char slop[4]; /* N.B. bug overwrites buffer by 1 */ }; +/* + * The pty uses char_buf and flag_buf as a contiguous buffer + */ +#define PTY_BUF_SIZE 4*TTY_FLIPBUF_SIZE /* * When a break, frame error, or parity error happens, these codes are @@ -198,7 +204,7 @@ struct tty_flip_buffer { * most often used by a windowing system, which will set the correct * size each time the window is created or resized anyway. * IMPORTANT: since this structure is dynamically allocated, it must - * be no larger than 4096 bytes. Changing TTY_BUF_SIZE will change + * be no larger than 4096 bytes. Changing TTY_FLIPBUF_SIZE will change * the size of this structure, and it needs to be done with care. * - TYT, 9/14/92 */ diff --git a/include/linux/x25.h b/include/linux/x25.h index 6af8e9a5bc6d..0435e9701061 100644 --- a/include/linux/x25.h +++ b/include/linux/x25.h @@ -13,6 +13,7 @@ #define SIOCX25SFACILITIES (SIOCPROTOPRIVATE + 3) #define SIOCX25GCALLUSERDATA (SIOCPROTOPRIVATE + 4) #define SIOCX25SCALLUSERDATA (SIOCPROTOPRIVATE + 5) +#define SIOCX25GCAUSEDIAG (SIOCPROTOPRIVATE + 6) /* * Values for {get,set}sockopt. @@ -32,43 +33,12 @@ #define X25_PS2048 11 #define X25_PS4096 12 -/* - * X.25 Reset error and diagnostic codes. - */ -#define X25_ERR_RESET 100 /* Call Reset */ -#define X25_ERR_ROUT 101 /* Out of Order */ -#define X25_ERR_RRPE 102 /* Remote Procedure Error */ -#define X25_ERR_RLPE 103 /* Local Procedure Error */ -#define X25_ERR_RNCG 104 /* Network Congestion */ -#define X25_ERR_RRDO 105 /* Remote DTE Operational */ -#define X25_ERR_RNOP 106 /* Network Operational */ -#define X25_ERR_RINV 107 /* Invalid Call */ -#define X25_ERR_RNOO 108 /* Network Out of Order */ - -/* - * X.25 Clear error and diagnostic codes. - */ -#define X25_ERR_CLEAR 110 /* Call Cleared */ -#define X25_ERR_CBUSY 111 /* Number Busy */ -#define X25_ERR_COUT 112 /* Out of Order */ -#define X25_ERR_CRPE 113 /* Remote Procedure Error */ -#define X25_ERR_CRRC 114 /* Collect Call Refused */ -#define X25_ERR_CINV 115 /* Invalid Call */ -#define X25_ERR_CNFS 116 /* Invalid Fast Select */ -#define X25_ERR_CSA 117 /* Ship Absent */ -#define X25_ERR_CIFR 118 /* Invalid Facility Request */ -#define X25_ERR_CAB 119 /* Access Barred */ -#define X25_ERR_CLPE 120 /* Local Procedure Error */ -#define X25_ERR_CNCG 121 /* Network Congestion */ -#define X25_ERR_CNOB 122 /* Not Obtainable */ -#define X25_ERR_CROO 123 /* RPOA Out of Order */ - /* * An X.121 address, it is held as ASCII text, null terminated, up to 15 * digits and a null terminator. */ typedef struct { - char x25_addr[16]; + char x25_addr[16]; } x25_address; /* @@ -114,4 +84,12 @@ struct x25_calluserdata { unsigned char cuddata[128]; }; +/* + * Call clearing Cause and Diagnostic structure. + */ +struct x25_causediag { + unsigned char cause; + unsigned char diagnostic; +}; + #endif diff --git a/include/net/ax25.h b/include/net/ax25.h index 9e6f0df1118c..fd25e9f7f70c 100644 --- a/include/net/ax25.h +++ b/include/net/ax25.h @@ -9,10 +9,8 @@ #include #include -#define AX25_SLOWHZ 10 /* Run timing at 1/10 second - gives us better resolution for 56kbit links */ - -#define AX25_T1CLAMPLO (1 * AX25_SLOWHZ) /* If defined, clamp at 1 second **/ -#define AX25_T1CLAMPHI (30 * AX25_SLOWHZ) /* If defined, clamp at 30 seconds **/ +#define AX25_T1CLAMPLO 1 +#define AX25_T1CLAMPHI (30 * HZ) #define AX25_BPQ_HEADER_LEN 16 #define AX25_KISS_HEADER_LEN 1 @@ -125,14 +123,14 @@ enum { #define AX25_DEF_CONMODE 2 /* Connected mode allowed */ #define AX25_DEF_WINDOW 2 /* Window=2 */ #define AX25_DEF_EWINDOW 32 /* Module-128 Window=32 */ -#define AX25_DEF_T1 (10 * AX25_SLOWHZ) /* T1=10s */ -#define AX25_DEF_T2 (3 * AX25_SLOWHZ) /* T2=3s */ -#define AX25_DEF_T3 (300 * AX25_SLOWHZ) /* T3=300s */ +#define AX25_DEF_T1 (10 * HZ) /* T1=10s */ +#define AX25_DEF_T2 (3 * HZ) /* T2=3s */ +#define AX25_DEF_T3 (300 * HZ) /* T3=300s */ #define AX25_DEF_N2 10 /* N2=10 */ -#define AX25_DEF_IDLE (0 * 60 * AX25_SLOWHZ) /* Idle=None */ +#define AX25_DEF_IDLE (0 * 60 * HZ) /* Idle=None */ #define AX25_DEF_PACLEN 256 /* Paclen=256 */ #define AX25_DEF_PROTOCOL AX25_PROTO_STD_SIMPLEX /* Standard AX.25 */ -#define AX25_DEF_DS_TIMEOUT (3 * 60 * AX25_SLOWHZ) /* DAMA timeout 3 minutes */ +#define AX25_DEF_DS_TIMEOUT (3 * 60 * HZ) /* DAMA timeout 3 minutes */ typedef struct ax25_uid_assoc { struct ax25_uid_assoc *next; @@ -186,8 +184,8 @@ typedef struct ax25_cb { unsigned short vs, vr, va; unsigned char condition, backoff; unsigned char n2, n2count; - unsigned short t1, t2, t3, idle, rtt; - unsigned short t1timer, t2timer, t3timer, idletimer; + struct timer_list t1timer, t2timer, t3timer, idletimer; + unsigned long t1, t2, t3, idle, rtt; unsigned short paclen, fragno, fraglen; struct sk_buff_head write_queue; struct sk_buff_head reseq_queue; @@ -251,20 +249,22 @@ extern void ax25_ds_set_timer(ax25_dev *); extern void ax25_ds_del_timer(ax25_dev *); extern void ax25_ds_timer(ax25_cb *); extern void ax25_ds_t1_timeout(ax25_cb *); +extern void ax25_ds_heartbeat_expiry(ax25_cb *); +extern void ax25_ds_t3timer_expiry(ax25_cb *); +extern void ax25_ds_idletimer_expiry(ax25_cb *); #include /* ax25_iface.c */ extern int ax25_protocol_register(unsigned int, int (*)(struct sk_buff *, ax25_cb *)); extern void ax25_protocol_release(unsigned int); -extern int ax25_linkfail_register(void (*)(ax25_address *, struct device *)); -extern void ax25_linkfail_release(void (*)(ax25_address *, struct device *)); +extern int ax25_linkfail_register(void (*)(ax25_cb *, int)); +extern void ax25_linkfail_release(void (*)(ax25_cb *, int)); extern int ax25_listen_register(ax25_address *, struct device *); extern void ax25_listen_release(ax25_address *, struct device *); extern int (*ax25_protocol_function(unsigned int))(struct sk_buff *, ax25_cb *); extern int ax25_listen_mine(ax25_address *, struct device *); -extern void ax25_link_failed(ax25_address *, struct device *); -extern int ax25_link_up(ax25_address *, ax25_address *, ax25_digi *, struct device *); +extern void ax25_link_failed(ax25_cb *, int); extern int ax25_protocol_is_registered(unsigned int); /* ax25_in.c */ @@ -276,7 +276,7 @@ extern int ax25_encapsulate(struct sk_buff *, struct device *, unsigned short, extern int ax25_rebuild_header(struct sk_buff *); /* ax25_out.c */ -extern int ax25_send_frame(struct sk_buff *, int, ax25_address *, ax25_address *, ax25_digi *, struct device *); +extern ax25_cb *ax25_send_frame(struct sk_buff *, int, ax25_address *, ax25_address *, ax25_digi *, struct device *); extern void ax25_output(ax25_cb *, int, struct sk_buff *); extern void ax25_kick(ax25_cb *); extern void ax25_transmit_buffer(ax25_cb *, struct sk_buff *, int); @@ -288,9 +288,8 @@ extern void ax25_rt_device_down(struct device *); extern int ax25_rt_ioctl(unsigned int, void *); extern int ax25_rt_get_info(char *, char **, off_t, int, int); extern int ax25_rt_autobind(ax25_cb *, ax25_address *); -extern void ax25_rt_build_path(ax25_cb *, ax25_address *, struct device *); -extern struct sk_buff *ax25_dg_build_path(struct sk_buff *, ax25_address *, struct device *); -extern char ax25_ip_mode_get(ax25_address *, struct device *); +extern ax25_route *ax25_rt_find_route(ax25_address *, struct device *); +extern struct sk_buff *ax25_rt_build_path(struct sk_buff *, ax25_address *, ax25_address *, ax25_digi *); extern void ax25_rt_free(void); /* ax25_std_in.c */ @@ -304,7 +303,11 @@ extern void ax25_std_enquiry_response(ax25_cb *); extern void ax25_std_timeout_response(ax25_cb *); /* ax25_std_timer.c */ -extern void ax25_std_timer(ax25_cb *); +extern void ax25_std_heartbeat_expiry(ax25_cb *); +extern void ax25_std_t1timer_expiry(ax25_cb *); +extern void ax25_std_t2timer_expiry(ax25_cb *); +extern void ax25_std_t3timer_expiry(ax25_cb *); +extern void ax25_std_idletimer_expiry(ax25_cb *); /* ax25_subr.c */ extern void ax25_clear_queues(ax25_cb *); @@ -314,11 +317,23 @@ extern int ax25_validate_nr(ax25_cb *, unsigned short); extern int ax25_decode(ax25_cb *, struct sk_buff *, int *, int *, int *); extern void ax25_send_control(ax25_cb *, int, int, int); extern void ax25_return_dm(struct device *, ax25_address *, ax25_address *, ax25_digi *); -extern unsigned short ax25_calculate_t1(ax25_cb *); +extern void ax25_calculate_t1(ax25_cb *); extern void ax25_calculate_rtt(ax25_cb *); +extern void ax25_disconnect(ax25_cb *, int); /* ax25_timer.c */ -extern void ax25_set_timer(ax25_cb *); +extern void ax25_start_heartbeat(ax25_cb *); +extern void ax25_start_t1timer(ax25_cb *); +extern void ax25_start_t2timer(ax25_cb *); +extern void ax25_start_t3timer(ax25_cb *); +extern void ax25_start_idletimer(ax25_cb *); +extern void ax25_stop_heartbeat(ax25_cb *); +extern void ax25_stop_t1timer(ax25_cb *); +extern void ax25_stop_t2timer(ax25_cb *); +extern void ax25_stop_t3timer(ax25_cb *); +extern void ax25_stop_idletimer(ax25_cb *); +extern int ax25_t1timer_running(ax25_cb *); +extern unsigned long ax25_display_timer(struct timer_list *); /* ax25_uid.c */ extern int ax25_uid_policy; diff --git a/include/net/lapb.h b/include/net/lapb.h index f06583cee778..7cc9b3452abb 100644 --- a/include/net/lapb.h +++ b/include/net/lapb.h @@ -2,8 +2,6 @@ #define _LAPB_H #include -#define LAPB_SLOWHZ 10 /* Run timing at 1/10 second */ - #define LAPB_HEADER_LEN 20 /* LAPB over Ethernet + a bit more */ #define LAPB_ACK_PENDING_CONDITION 0x01 @@ -58,10 +56,10 @@ enum { }; #define LAPB_DEFAULT_MODE (LAPB_STANDARD | LAPB_SLP | LAPB_DTE) -#define LAPB_DEFAULT_WINDOW 7 /* Window=7 */ -#define LAPB_DEFAULT_T1 (5 * LAPB_SLOWHZ) /* T1=5s */ -#define LAPB_DEFAULT_T2 (1 * LAPB_SLOWHZ) /* T2=1s */ -#define LAPB_DEFAULT_N2 20 /* N2=20 */ +#define LAPB_DEFAULT_WINDOW 7 /* Window=7 */ +#define LAPB_DEFAULT_T1 (5 * HZ) /* T1=5s */ +#define LAPB_DEFAULT_T2 (1 * HZ) /* T2=1s */ +#define LAPB_DEFAULT_N2 20 /* N2=20 */ #define LAPB_SMODULUS 8 #define LAPB_EMODULUS 128 @@ -91,14 +89,12 @@ typedef struct lapb_cb { unsigned char condition; unsigned short n2, n2count; unsigned short t1, t2; - unsigned short t1timer, t2timer; + struct timer_list t1timer, t2timer; /* Internal control information */ - struct sk_buff_head input_queue; struct sk_buff_head write_queue; struct sk_buff_head ack_queue; unsigned char window; - struct timer_list timer; struct lapb_register_struct callbacks; /* FRMR control information */ @@ -136,7 +132,11 @@ extern void lapb_send_control(lapb_cb *, int, int, int); extern void lapb_transmit_frmr(lapb_cb *); /* lapb_timer.c */ -extern void lapb_set_timer(lapb_cb *); +extern void lapb_start_t1timer(lapb_cb *); +extern void lapb_start_t2timer(lapb_cb *); +extern void lapb_stop_t1timer(lapb_cb *); +extern void lapb_stop_t2timer(lapb_cb *); +extern int lapb_t1timer_running(lapb_cb *); /* * Debug levels. diff --git a/include/net/netrom.h b/include/net/netrom.h index 8a255660aab2..cc9fc842c0ac 100644 --- a/include/net/netrom.h +++ b/include/net/netrom.h @@ -8,8 +8,6 @@ #define _NETROM_H #include -#define NR_SLOWHZ 10 /* Run timing at 1/10 second */ - #define NR_NETWORK_LEN 15 #define NR_TRANSPORT_LEN 5 @@ -40,17 +38,17 @@ enum { #define NR_COND_PEER_RX_BUSY 0x04 #define NR_COND_OWN_RX_BUSY 0x08 -#define NR_DEFAULT_T1 (120 * NR_SLOWHZ) /* Outstanding frames - 120 seconds */ -#define NR_DEFAULT_T2 (5 * NR_SLOWHZ) /* Response delay - 5 seconds */ -#define NR_DEFAULT_N2 3 /* Number of Retries - 3 */ -#define NR_DEFAULT_T4 (180 * NR_SLOWHZ) /* Busy Delay - 180 seconds */ -#define NR_DEFAULT_IDLE (20* 60 * NR_SLOWHZ) /* No Activuty Timeout - 900 seconds*/ -#define NR_DEFAULT_WINDOW 4 /* Default Window Size - 4 */ -#define NR_DEFAULT_OBS 6 /* Default Obsolescence Count - 6 */ -#define NR_DEFAULT_QUAL 10 /* Default Neighbour Quality - 10 */ -#define NR_DEFAULT_TTL 16 /* Default Time To Live - 16 */ -#define NR_DEFAULT_ROUTING 1 /* Is routing enabled ? */ -#define NR_DEFAULT_FAILS 2 /* Link fails until route fails */ +#define NR_DEFAULT_T1 (120 * HZ) /* Outstanding frames - 120 seconds */ +#define NR_DEFAULT_T2 (5 * HZ) /* Response delay - 5 seconds */ +#define NR_DEFAULT_N2 3 /* Number of Retries - 3 */ +#define NR_DEFAULT_T4 (180 * HZ) /* Busy Delay - 180 seconds */ +#define NR_DEFAULT_IDLE (0 * 60 * HZ) /* No Activity Timeout - none */ +#define NR_DEFAULT_WINDOW 4 /* Default Window Size - 4 */ +#define NR_DEFAULT_OBS 6 /* Default Obsolescence Count - 6 */ +#define NR_DEFAULT_QUAL 10 /* Default Neighbour Quality - 10 */ +#define NR_DEFAULT_TTL 16 /* Default Time To Live - 16 */ +#define NR_DEFAULT_ROUTING 1 /* Is routing enabled ? */ +#define NR_DEFAULT_FAILS 2 /* Link fails until route fails */ #define NR_MODULUS 256 #define NR_MAX_WINDOW_SIZE 127 /* Maximum Window Allowable - 127 */ @@ -64,9 +62,12 @@ typedef struct { unsigned char state, condition, bpqext, window; unsigned short vs, vr, va, vl; unsigned char n2, n2count; - unsigned short t1, t2, t4, idle; - unsigned short t1timer, t2timer, t4timer, idletimer; + unsigned long t1, t2, t4, idle; unsigned short fraglen; + struct timer_list t1timer; + struct timer_list t2timer; + struct timer_list t4timer; + struct timer_list idletimer; struct sk_buff_head ack_queue; struct sk_buff_head reseq_queue; struct sk_buff_head frag_queue; @@ -77,6 +78,7 @@ struct nr_neigh { struct nr_neigh *next; ax25_address callsign; ax25_digi *digipeat; + ax25_cb *ax25; struct device *dev; unsigned char quality; unsigned char locked; @@ -138,7 +140,7 @@ extern void nr_rt_device_down(struct device *); extern struct device *nr_dev_first(void); extern struct device *nr_dev_get(ax25_address *); extern int nr_rt_ioctl(unsigned int, void *); -extern void nr_link_failed(ax25_address *, struct device *); +extern void nr_link_failed(ax25_cb *, int); extern int nr_route_frame(struct sk_buff *, ax25_cb *); extern int nr_nodes_get_info(char *, char **, off_t, int, int); extern int nr_neigh_get_info(char *, char **, off_t, int, int); @@ -152,9 +154,20 @@ extern int nr_validate_nr(struct sock *, unsigned short); extern int nr_in_rx_window(struct sock *, unsigned short); extern void nr_write_internal(struct sock *, int); extern void nr_transmit_dm(struct sk_buff *); +extern void nr_disconnect(struct sock *, int); /* nr_timer.c */ -extern void nr_set_timer(struct sock *); +extern void nr_start_heartbeat(struct sock *); +extern void nr_start_t1timer(struct sock *); +extern void nr_start_t2timer(struct sock *); +extern void nr_start_t4timer(struct sock *); +extern void nr_start_idletimer(struct sock *); +extern void nr_stop_heartbeat(struct sock *); +extern void nr_stop_t1timer(struct sock *); +extern void nr_stop_t2timer(struct sock *); +extern void nr_stop_t4timer(struct sock *); +extern void nr_stop_idletimer(struct sock *); +extern int nr_t1timer_running(struct sock *); /* sysctl_net_netrom.c */ extern void nr_register_sysctl(void); diff --git a/include/net/rose.h b/include/net/rose.h index 1e60cfa8fc61..86f6a6721f72 100644 --- a/include/net/rose.h +++ b/include/net/rose.h @@ -8,8 +8,6 @@ #define _ROSE_H #include -#define ROSE_SLOWHZ 10 /* Run timing at 1/10 second */ - #define ROSE_ADDR_LEN 5 #define ROSE_MIN_LEN 3 @@ -45,22 +43,23 @@ enum { ROSE_STATE_1, /* Awaiting Call Accepted */ ROSE_STATE_2, /* Awaiting Clear Confirmation */ ROSE_STATE_3, /* Data Transfer */ - ROSE_STATE_4 /* Awaiting Reset Confirmation */ + ROSE_STATE_4, /* Awaiting Reset Confirmation */ + ROSE_STATE_5 /* Deferred Call Acceptance */ }; -#define ROSE_DEFAULT_T0 (180 * ROSE_SLOWHZ) /* Default T10 T20 value */ -#define ROSE_DEFAULT_T1 (200 * ROSE_SLOWHZ) /* Default T11 T21 value */ -#define ROSE_DEFAULT_T2 (180 * ROSE_SLOWHZ) /* Default T12 T22 value */ -#define ROSE_DEFAULT_T3 (180 * ROSE_SLOWHZ) /* Default T13 T23 value */ -#define ROSE_DEFAULT_HB (5 * ROSE_SLOWHZ) /* Default Holdback value */ -#define ROSE_DEFAULT_IDLE (20 * 60 * ROSE_SLOWHZ) /* Default No Activity value */ -#define ROSE_DEFAULT_ROUTING 1 /* Default routing flag */ -#define ROSE_DEFAULT_FAIL_TIMEOUT (120 * ROSE_SLOWHZ) /* Time until link considered usable */ -#define ROSE_DEFAULT_MAXVC 50 /* Maximum number of VCs per neighbour */ -#define ROSE_DEFAULT_WINDOW_SIZE 3 /* Default window size */ +#define ROSE_DEFAULT_T0 (180 * HZ) /* Default T10 T20 value */ +#define ROSE_DEFAULT_T1 (200 * HZ) /* Default T11 T21 value */ +#define ROSE_DEFAULT_T2 (180 * HZ) /* Default T12 T22 value */ +#define ROSE_DEFAULT_T3 (180 * HZ) /* Default T13 T23 value */ +#define ROSE_DEFAULT_HB (5 * HZ) /* Default Holdback value */ +#define ROSE_DEFAULT_IDLE (0 * 60 * HZ) /* No Activity Timeout - none */ +#define ROSE_DEFAULT_ROUTING 1 /* Default routing flag */ +#define ROSE_DEFAULT_FAIL_TIMEOUT (120 * HZ) /* Time until link considered usable */ +#define ROSE_DEFAULT_MAXVC 50 /* Maximum number of VCs per neighbour */ +#define ROSE_DEFAULT_WINDOW_SIZE 3 /* Default window size */ #define ROSE_MODULUS 8 -#define ROSE_MAX_PACKET_SIZE 256 /* Maximum packet size */ +#define ROSE_MAX_PACKET_SIZE 251 /* Maximum packet size */ #define ROSE_COND_ACK_PENDING 0x01 #define ROSE_COND_PEER_RX_BUSY 0x02 @@ -81,14 +80,16 @@ struct rose_neigh { struct rose_neigh *next; ax25_address callsign; ax25_digi *digipeat; + ax25_cb *ax25; struct device *dev; unsigned short count; + unsigned short use; unsigned int number; char restarted; char dce_mode; struct sk_buff_head queue; - unsigned short t0timer, ftimer; - struct timer_list timer; + struct timer_list t0timer; + struct timer_list ftimer; }; struct rose_node { @@ -124,11 +125,13 @@ typedef struct { struct rose_neigh *neighbour; struct device *device; unsigned int lci, rand; - unsigned char state, condition, qbitincl; + unsigned char state, condition, qbitincl, defer; + unsigned char cause, diagnostic; unsigned short vs, vr, va, vl; - unsigned short timer; - unsigned short t1, t2, t3, hb, idle; + unsigned long t1, t2, t3, hb, idle; unsigned short fraglen; + struct timer_list timer; + struct timer_list idletimer; struct sk_buff_head frag_queue; struct sock *sk; /* Backlink to socket */ } rose_cb; @@ -164,12 +167,17 @@ extern int rose_init(struct device *); extern int rose_process_rx_frame(struct sock *, struct sk_buff *); /* rose_link.c */ -extern void rose_link_set_timer(struct rose_neigh *); +extern void rose_start_ftimer(struct rose_neigh *); +extern void rose_start_t0timer(struct rose_neigh *); +extern void rose_stop_ftimer(struct rose_neigh *); +extern void rose_stop_t0timer(struct rose_neigh *); +extern int rose_ftimer_running(struct rose_neigh *); +extern int rose_t0timer_running(struct rose_neigh *); extern void rose_link_rx_restart(struct sk_buff *, struct rose_neigh *, unsigned short); extern void rose_transmit_restart_request(struct rose_neigh *); extern void rose_transmit_restart_confirmation(struct rose_neigh *); extern void rose_transmit_diagnostic(struct rose_neigh *, unsigned char); -extern void rose_transmit_clear_request(struct rose_neigh *, unsigned int, unsigned char); +extern void rose_transmit_clear_request(struct rose_neigh *, unsigned int, unsigned char, unsigned char); extern void rose_transmit_link(struct sk_buff *, struct rose_neigh *); /* rose_out.c */ @@ -185,9 +193,9 @@ extern struct device *rose_dev_first(void); extern struct device *rose_dev_get(rose_address *); extern struct rose_route *rose_route_free_lci(unsigned int, struct rose_neigh *); extern struct device *rose_ax25_dev_get(char *); -extern struct rose_neigh *rose_get_neigh(rose_address *); +extern struct rose_neigh *rose_get_neigh(rose_address *, unsigned char *, unsigned char *); extern int rose_rt_ioctl(unsigned int, void *); -extern void rose_link_failed(ax25_address *, struct device *); +extern void rose_link_failed(ax25_cb *, int); extern int rose_route_frame(struct sk_buff *, ax25_cb *); extern int rose_nodes_get_info(char *, char **, off_t, int, int); extern int rose_neigh_get_info(char *, char **, off_t, int, int); @@ -201,9 +209,18 @@ extern void rose_write_internal(struct sock *, int); extern int rose_decode(struct sk_buff *, int *, int *, int *, int *, int *); extern int rose_parse_facilities(struct sk_buff *, struct rose_facilities *); extern int rose_create_facilities(unsigned char *, rose_cb *); +extern void rose_disconnect(struct sock *, int, int, int); /* rose_timer.c */ -extern void rose_set_timer(struct sock *); +extern void rose_start_heartbeat(struct sock *); +extern void rose_start_t1timer(struct sock *); +extern void rose_start_t2timer(struct sock *); +extern void rose_start_t3timer(struct sock *); +extern void rose_start_hbtimer(struct sock *); +extern void rose_start_idletimer(struct sock *); +extern void rose_stop_heartbeat(struct sock *); +extern void rose_stop_timer(struct sock *); +extern void rose_stop_idletimer(struct sock *); /* sysctl_net_rose.c */ extern void rose_register_sysctl(void); diff --git a/include/net/x25.h b/include/net/x25.h index cb1ae8bee6c0..7b58ad4e30e8 100644 --- a/include/net/x25.h +++ b/include/net/x25.h @@ -8,8 +8,6 @@ #define _X25_H #include -#define X25_SLOWHZ 1 /* Run timing at 1 Hz */ - #define X25_ADDR_LEN 16 #define X25_MAX_L2_LEN 18 /* 802.2 LLC */ @@ -67,11 +65,11 @@ enum { X25_LINK_STATE_3 }; -#define X25_DEFAULT_T20 (180 * X25_SLOWHZ) /* Default T20 value */ -#define X25_DEFAULT_T21 (200 * X25_SLOWHZ) /* Default T21 value */ -#define X25_DEFAULT_T22 (180 * X25_SLOWHZ) /* Default T22 value */ -#define X25_DEFAULT_T23 (180 * X25_SLOWHZ) /* Default T23 value */ -#define X25_DEFAULT_T2 (3 * X25_SLOWHZ) /* Default ack holdback value */ +#define X25_DEFAULT_T20 (180 * HZ) /* Default T20 value */ +#define X25_DEFAULT_T21 (200 * HZ) /* Default T21 value */ +#define X25_DEFAULT_T22 (180 * HZ) /* Default T22 value */ +#define X25_DEFAULT_T23 (180 * HZ) /* Default T23 value */ +#define X25_DEFAULT_T2 (3 * HZ) /* Default ack holdback value */ #define X25_DEFAULT_WINDOW_SIZE 2 /* Default Window Size */ #define X25_DEFAULT_PACKET_SIZE X25_PS128 /* Default Packet Size */ @@ -113,8 +111,8 @@ struct x25_neigh { unsigned int state; unsigned int extended; struct sk_buff_head queue; - unsigned short t20, t20timer; - struct timer_list timer; + unsigned long t20; + struct timer_list t20timer; }; typedef struct { @@ -123,13 +121,14 @@ typedef struct { unsigned int lci; unsigned char state, condition, qbitincl, intflag; unsigned short vs, vr, va, vl; - unsigned short timer; - unsigned short t2, t21, t22, t23; + unsigned long t2, t21, t22, t23; unsigned short fraglen; struct sk_buff_head fragment_queue; struct sk_buff_head interrupt_in_queue; struct sk_buff_head interrupt_out_queue; struct sock *sk; /* Backlink to socket */ + struct timer_list timer; + struct x25_causediag causediag; struct x25_facilities facilities; struct x25_calluserdata calluserdata; } x25_cb; @@ -143,8 +142,8 @@ extern int sysctl_x25_ack_holdback_timeout; extern int x25_addr_ntoa(unsigned char *, x25_address *, x25_address *); extern int x25_addr_aton(unsigned char *, x25_address *, x25_address *); -extern unsigned int x25_new_lci(void); -extern struct sock *x25_find_socket(unsigned int); +extern unsigned int x25_new_lci(struct x25_neigh *); +extern struct sock *x25_find_socket(unsigned int, struct x25_neigh *); extern void x25_destroy_socket(struct sock *); extern int x25_rx_call_request(struct sk_buff *, struct x25_neigh *, unsigned int); @@ -199,9 +198,17 @@ extern void x25_clear_queues(struct sock *); extern int x25_validate_nr(struct sock *, unsigned short); extern void x25_write_internal(struct sock *, int); extern int x25_decode(struct sock *, struct sk_buff *, int *, int *, int *, int *, int *); +extern void x25_disconnect(struct sock *, int, unsigned char, unsigned char); /* x25_timer.c */ -extern void x25_set_timer(struct sock *); +extern void x25_start_heartbeat(struct sock *); +extern void x25_start_t2timer(struct sock *); +extern void x25_start_t21timer(struct sock *); +extern void x25_start_t22timer(struct sock *); +extern void x25_start_t23timer(struct sock *); +extern void x25_stop_heartbeat(struct sock *); +extern void x25_stop_timer(struct sock *); +extern unsigned long x25_display_timer(struct sock *); /* sysctl_net_x25.c */ extern void x25_register_sysctl(void); diff --git a/kernel/exit.c b/kernel/exit.c index 18dd86698d47..bb1f0391d01c 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -404,8 +404,8 @@ static inline void __exit_fs(struct task_struct *tsk) if (fs) { tsk->fs = NULL; if (!--fs->count) { - iput(fs->root); - iput(fs->pwd); + dput(fs->root); + dput(fs->pwd); kfree(fs); } } diff --git a/kernel/fork.c b/kernel/fork.c index c3bcf7cca5af..97837db65714 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -302,10 +302,8 @@ static inline int copy_fs(unsigned long clone_flags, struct task_struct * tsk) return -1; tsk->fs->count = 1; tsk->fs->umask = current->fs->umask; - if ((tsk->fs->root = current->fs->root)) - atomic_inc(&tsk->fs->root->i_count); - if ((tsk->fs->pwd = current->fs->pwd)) - atomic_inc(&tsk->fs->pwd->i_count); + tsk->fs->root = dget(current->fs->root); + tsk->fs->pwd = dget(current->fs->pwd); return 0; } diff --git a/kernel/ksyms.c b/kernel/ksyms.c index 8bd9d269dd65..b5c2b4d0abb9 100644 --- a/kernel/ksyms.c +++ b/kernel/ksyms.c @@ -141,9 +141,10 @@ EXPORT_SYMBOL(update_vm_cache); EXPORT_SYMBOL(getname); EXPORT_SYMBOL(putname); EXPORT_SYMBOL(__fput); -EXPORT_SYMBOL(__iget); +EXPORT_SYMBOL(iget); EXPORT_SYMBOL(_iput); -EXPORT_SYMBOL(namei); +EXPORT_SYMBOL(__namei); +EXPORT_SYMBOL(lookup_dentry); EXPORT_SYMBOL(open_namei); EXPORT_SYMBOL(sys_close); EXPORT_SYMBOL(close_fp); @@ -341,7 +342,6 @@ EXPORT_SYMBOL(set_writetime); EXPORT_SYMBOL(sys_tz); EXPORT_SYMBOL(__wait_on_super); EXPORT_SYMBOL(file_fsync); -EXPORT_SYMBOL(_free_ibasket); EXPORT_SYMBOL(_clear_inode); EXPORT_SYMBOL(refile_buffer); EXPORT_SYMBOL(nr_async_pages); diff --git a/kernel/module.c b/kernel/module.c index 885539b5c69c..c584eb3ae8e7 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -79,7 +79,8 @@ get_mod_name(const char *user_name, char **buf) unsigned long page; long retval; - if ((unsigned long)user_name >= TASK_SIZE) + if ((unsigned long)user_name >= TASK_SIZE + && get_fs () != KERNEL_DS) return -EFAULT; page = __get_free_page(GFP_KERNEL); @@ -134,7 +135,7 @@ sys_create_module(const char *name_user, size_t size) error = -EEXIST; goto err1; } - if ((mod = (struct module *)vmalloc(size)) == NULL) { + if ((mod = (struct module *)module_map(size)) == NULL) { error = -ENOMEM; goto err1; } @@ -685,6 +686,7 @@ sys_get_kernel_syms(struct kernel_sym *table) { struct module *mod; int i; + struct kernel_sym ksym; lock_kernel(); for (mod = module_list, i = 0; mod; mod = mod->next) { @@ -695,8 +697,10 @@ sys_get_kernel_syms(struct kernel_sym *table) if (table == NULL) goto out; + /* So that we don't give the user our stack content */ + memset (&ksym, 0, sizeof (ksym)); + for (mod = module_list, i = 0; mod; mod = mod->next) { - struct kernel_sym ksym; struct module_symbol *msym; unsigned int j; @@ -790,7 +794,7 @@ free_module(struct module *mod) /* And free the memory. */ - vfree(mod); + module_unmap(mod); } /* diff --git a/kernel/sysctl.c b/kernel/sysctl.c index e4bdcfc1ad33..a5f5c68f5467 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -103,6 +103,7 @@ struct inode_operations proc_sys_inode_operations = NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ diff --git a/mm/mmap.c b/mm/mmap.c index 761f7e4e661f..4386adcb8243 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -311,6 +311,8 @@ unsigned long do_mmap(struct file * file, unsigned long addr, unsigned long len, if (correct_wcount) file->f_inode->i_writecount++; merge_segments(mm, vma->vm_start, vma->vm_end); + + addr = vma->vm_start; /* merge_segments might have merged our vma, so we can't use it any more */ mm->total_vm += len >> PAGE_SHIFT; diff --git a/mm/slab.c b/mm/slab.c index b79c7165695c..7bd9755c7465 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -1615,7 +1615,7 @@ kmalloc(size_t size, int flags) } void -kfree(void *objp) +kfree(const void *objp) { struct page *page; int nr; diff --git a/mm/swapfile.c b/mm/swapfile.c index 4a6addce1b91..eae481821bec 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -334,7 +334,7 @@ asmlinkage int sys_swapoff(const char * specialfile) lock_kernel(); if (!suser()) goto out; - err = namei(NAM_FOLLOW_LINK, specialfile, &inode); + err = namei(specialfile, &inode); if (err) goto out; prev = -1; @@ -488,13 +488,10 @@ asmlinkage int sys_swapon(const char * specialfile, int swap_flags) } else { p->prio = --least_priority; } - error = namei(NAM_FOLLOW_LINK, specialfile, &swap_inode); + error = namei(specialfile, &swap_inode); if (error) goto bad_swap_2; p->swap_file = swap_inode; - error = -EBUSY; - if (atomic_read(&swap_inode->i_count) != 1) - goto bad_swap_2; error = -EINVAL; if (S_ISBLK(swap_inode->i_mode)) { diff --git a/mm/vmalloc.c b/mm/vmalloc.c index 71afe1aea134..d0270d58677a 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -68,7 +68,7 @@ static inline void free_area_pmd(pgd_t * dir, unsigned long address, unsigned lo } } -static void free_area_pages(unsigned long address, unsigned long size) +void vmfree_area_pages(unsigned long address, unsigned long size) { pgd_t * dir; unsigned long end = address + size; @@ -125,7 +125,7 @@ static inline int alloc_area_pmd(pmd_t * pmd, unsigned long address, unsigned lo return 0; } -static int alloc_area_pages(unsigned long address, unsigned long size) +int vmalloc_area_pages(unsigned long address, unsigned long size) { pgd_t * dir; unsigned long end = address + size; @@ -181,7 +181,7 @@ void vfree(void * addr) for (p = &vmlist ; (tmp = *p) ; p = &tmp->next) { if (tmp->addr == addr) { *p = tmp->next; - free_area_pages(VMALLOC_VMADDR(tmp->addr), tmp->size); + vmfree_area_pages(VMALLOC_VMADDR(tmp->addr), tmp->size); kfree(tmp); return; } @@ -201,7 +201,7 @@ void * vmalloc(unsigned long size) if (!area) return NULL; addr = area->addr; - if (alloc_area_pages(VMALLOC_VMADDR(addr), size)) { + if (vmalloc_area_pages(VMALLOC_VMADDR(addr), size)) { vfree(addr); return NULL; } diff --git a/net/Makefile b/net/Makefile index 09924ff8939e..53382aacb923 100644 --- a/net/Makefile +++ b/net/Makefile @@ -113,9 +113,6 @@ else endif endif -# We must attach netsyms.o to socket.o, as otherwise there is nothing -# to pull the object file from the archive. - SOCK := socket.o ifeq ($(CONFIG_NET),y) ifeq ($(CONFIG_MODULES),y) diff --git a/net/README b/net/README index a88ccfc5aa80..1cd7f5331561 100644 --- a/net/README +++ b/net/README @@ -6,19 +6,19 @@ Code Section Bug Report Contact 802 [other ] alan@lxorguk.ukuu.org.uk [token ring ] pnorton@cts.com appletalk alan@lxorguk.ukuu.org.uk and netatalk@umich.edu -ax25 jsn@cs.nott.ac.uk +ax25 g4klx@g4klx.demon.co.uk core alan@lxorguk.ukuu.org.uk decnet SteveW@ACM.org ethernet alan@lxorguk.ukuu.org.uk ipv4 davem@caip.rutgers.edu,Eric.Schenk@dna.lth.se ipv6 davem@caip.rutgers.edu,Eric.Schenk@dna.lth.se ipx alan@lxorguk.ukuu.org.uk,greg@caldera.com -lapb jsn@cs.nott.ac.uk -netrom jsn@cs.nott.ac.uk -rose jsn@cs.nott.ac.uk +lapb g4klx@g4klx.demon.co.uk +netrom g4klx@g4klx.demon.co.uk +rose g4klx@g4klx.demon.co.uk wanrouter genek@compuserve.com and dm@sangoma.com unix alan@lxorguk.ukuu.org.uk -x25 jsn@cs.nott.ac.uk +x25 g4klx@g4klx.demon.co.uk If in doubt contact me first. diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c index 37b679600f9b..8e5992747e10 100644 --- a/net/ax25/af_ax25.c +++ b/net/ax25/af_ax25.c @@ -1,5 +1,5 @@ /* - * AX.25 release 036 + * AX.25 release 037 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -92,6 +92,7 @@ * AX.25 036 Jonathan(G4KLX) Major restructuring. * Joerg(DL1BKE) Fixed DAMA Slave. * Jonathan(G4KLX) Fix widlcard listen parameter setting. + * AX.25 037 Jonathan(G4KLX) New timer architecture. */ #include @@ -161,8 +162,7 @@ static void ax25_remove_socket(ax25_cb *ax25) ax25_cb *s; unsigned long flags; - save_flags(flags); - cli(); + save_flags(flags); cli(); if ((s = ax25_list) == ax25) { ax25_list = s->next; @@ -196,16 +196,8 @@ static void ax25_kill_by_device(struct device *dev) for (s = ax25_list; s != NULL; s = s->next) { if (s->ax25_dev == ax25_dev) { - s->state = AX25_STATE_0; s->ax25_dev = NULL; - if (s->sk != NULL) { - s->sk->state = TCP_CLOSE; - s->sk->err = ENETUNREACH; - s->sk->shutdown |= SEND_SHUTDOWN; - if (!s->sk->dead) - s->sk->state_change(s->sk); - s->sk->dead = 1; - } + ax25_disconnect(s, ENETUNREACH); } } } @@ -308,7 +300,7 @@ struct sock *ax25_find_socket(ax25_address *my_addr, ax25_address *dest_addr, in * Find an AX.25 control block given both ends. It will only pick up * floating AX.25 control blocks or non Raw socket bound control blocks. */ -ax25_cb *ax25_find_cb(ax25_address *my_addr, ax25_address *dest_addr, ax25_digi *digi, struct device *dev) +ax25_cb *ax25_find_cb(ax25_address *src_addr, ax25_address *dest_addr, ax25_digi *digi, struct device *dev) { ax25_cb *s; unsigned long flags; @@ -319,11 +311,16 @@ ax25_cb *ax25_find_cb(ax25_address *my_addr, ax25_address *dest_addr, ax25_digi for (s = ax25_list; s != NULL; s = s->next) { if (s->sk != NULL && s->sk->type != SOCK_SEQPACKET) continue; - if (ax25cmp(&s->source_addr, my_addr) == 0 && ax25cmp(&s->dest_addr, dest_addr) == 0 && s->ax25_dev->dev == dev) { - if (digi != NULL) { - if (s->digipeat == NULL && digi->ndigi != 0) + if (s->ax25_dev == NULL) + continue; + if (ax25cmp(&s->source_addr, src_addr) == 0 && ax25cmp(&s->dest_addr, dest_addr) == 0 && s->ax25_dev->dev == dev) { + if (digi != NULL && digi->ndigi != 0) { + if (s->digipeat == NULL) continue; - if (s->digipeat != NULL && ax25digicmp(s->digipeat, digi) != 0) + if (ax25digicmp(s->digipeat, digi) != 0) + continue; + } else { + if (s->digipeat != NULL && s->digipeat->ndigi != 0) continue; } restore_flags(flags); @@ -402,10 +399,13 @@ void ax25_destroy_socket(ax25_cb *ax25) /* Not static as it's used by the timer struct sk_buff *skb; unsigned long flags; - save_flags(flags); - cli(); + save_flags(flags); cli(); - del_timer(&ax25->timer); + ax25_stop_heartbeat(ax25); + ax25_stop_t1timer(ax25); + ax25_stop_t2timer(ax25); + ax25_stop_t3timer(ax25); + ax25_stop_idletimer(ax25); ax25_remove_socket(ax25); ax25_clear_queues(ax25); /* Flush the queues */ @@ -414,7 +414,7 @@ void ax25_destroy_socket(ax25_cb *ax25) /* Not static as it's used by the timer while ((skb = skb_dequeue(&ax25->sk->receive_queue)) != NULL) { if (skb->sk != ax25->sk) { /* A pending connection */ skb->sk->dead = 1; /* Queue the unaccepted socket for death */ - ax25_set_timer(skb->sk->protinfo.ax25); + ax25_start_heartbeat(skb->sk->protinfo.ax25); skb->sk->protinfo.ax25->state = AX25_STATE_0; } @@ -451,13 +451,9 @@ static int ax25_ctl_ioctl(const unsigned int cmd, void *arg) struct ax25_ctl_struct ax25_ctl; ax25_dev *ax25_dev; ax25_cb *ax25; - unsigned long flags; - int err; - - if ((err = verify_area(VERIFY_READ, arg, sizeof(ax25_ctl))) != 0) - return err; - copy_from_user(&ax25_ctl, arg, sizeof(ax25_ctl)); + if (copy_from_user(&ax25_ctl, arg, sizeof(ax25_ctl))) + return -EFAULT; if ((ax25_dev = ax25_addr_ax25dev(&ax25_ctl.port_addr)) == NULL) return -ENODEV; @@ -467,22 +463,12 @@ static int ax25_ctl_ioctl(const unsigned int cmd, void *arg) switch (ax25_ctl.cmd) { case AX25_KILL: - ax25_clear_queues(ax25); ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - ax25->state = AX25_STATE_0; #ifdef CONFIG_AX25_DAMA_SLAVE if (ax25_dev->dama.slave && ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_DAMA_SLAVE) ax25_dama_off(ax25); #endif - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = ENETRESET; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } - ax25_set_timer(ax25); + ax25_disconnect(ax25, ENETRESET); break; case AX25_WINDOW: @@ -499,22 +485,14 @@ static int ax25_ctl_ioctl(const unsigned int cmd, void *arg) case AX25_T1: if (ax25_ctl.arg < 1) return -EINVAL; - ax25->rtt = (ax25_ctl.arg * AX25_SLOWHZ) / 2; - ax25->t1 = ax25_ctl.arg * AX25_SLOWHZ; - save_flags(flags); cli(); - if (ax25->t1timer > ax25->t1) - ax25->t1timer = ax25->t1; - restore_flags(flags); + ax25->rtt = (ax25_ctl.arg * HZ) / 2; + ax25->t1 = ax25_ctl.arg * HZ; break; case AX25_T2: if (ax25_ctl.arg < 1) return -EINVAL; - save_flags(flags); cli(); - ax25->t2 = ax25_ctl.arg * AX25_SLOWHZ; - if (ax25->t2timer > ax25->t2) - ax25->t2timer = ax25->t2; - restore_flags(flags); + ax25->t2 = ax25_ctl.arg * HZ; break; case AX25_N2: @@ -527,21 +505,13 @@ static int ax25_ctl_ioctl(const unsigned int cmd, void *arg) case AX25_T3: if (ax25_ctl.arg < 0) return -EINVAL; - save_flags(flags); cli(); - ax25->t3 = ax25_ctl.arg * AX25_SLOWHZ; - if (ax25->t3timer != 0) - ax25->t3timer = ax25->t3; - restore_flags(flags); + ax25->t3 = ax25_ctl.arg * HZ; break; case AX25_IDLE: if (ax25_ctl.arg < 0) return -EINVAL; - save_flags(flags); cli(); - ax25->idle = ax25_ctl.arg * AX25_SLOWHZ * 60; - if (ax25->idletimer != 0) - ax25->idletimer = ax25->idle; - restore_flags(flags); + ax25->idle = ax25_ctl.arg * 60 * HZ; break; case AX25_PACLEN: @@ -622,6 +592,10 @@ ax25_cb *ax25_create_cb(void) skb_queue_head_init(&ax25->reseq_queue); init_timer(&ax25->timer); + init_timer(&ax25->t1timer); + init_timer(&ax25->t2timer); + init_timer(&ax25->t3timer); + init_timer(&ax25->idletimer); ax25_fillin_cb(ax25, NULL); @@ -664,13 +638,14 @@ static int ax25_setsockopt(struct socket *sock, int level, int optname, char *op case AX25_T1: if (opt < 1) return -EINVAL; - sk->protinfo.ax25->rtt = (opt * AX25_SLOWHZ) / 2; + sk->protinfo.ax25->rtt = (opt * HZ) / 2; + sk->protinfo.ax25->t1 = opt * HZ; return 0; case AX25_T2: if (opt < 1) return -EINVAL; - sk->protinfo.ax25->t2 = opt * AX25_SLOWHZ; + sk->protinfo.ax25->t2 = opt * HZ; return 0; case AX25_N2: @@ -682,13 +657,13 @@ static int ax25_setsockopt(struct socket *sock, int level, int optname, char *op case AX25_T3: if (opt < 1) return -EINVAL; - sk->protinfo.ax25->t3 = opt * AX25_SLOWHZ; + sk->protinfo.ax25->t3 = opt * HZ; return 0; case AX25_IDLE: if (opt < 0) return -EINVAL; - sk->protinfo.ax25->idle = opt * AX25_SLOWHZ * 60; + sk->protinfo.ax25->idle = opt * 60 * HZ; return 0; case AX25_BACKOFF: @@ -738,11 +713,11 @@ static int ax25_getsockopt(struct socket *sock, int level, int optname, char *op break; case AX25_T1: - val = (sk->protinfo.ax25->t1 * 2) / AX25_SLOWHZ; + val = sk->protinfo.ax25->t1 / HZ; break; case AX25_T2: - val = sk->protinfo.ax25->t2 / AX25_SLOWHZ; + val = sk->protinfo.ax25->t2 / HZ; break; case AX25_N2: @@ -750,11 +725,11 @@ static int ax25_getsockopt(struct socket *sock, int level, int optname, char *op break; case AX25_T3: - val = sk->protinfo.ax25->t3 / AX25_SLOWHZ; + val = sk->protinfo.ax25->t3 / HZ; break; case AX25_IDLE: - val = sk->protinfo.ax25->idle / (AX25_SLOWHZ * 60); + val = sk->protinfo.ax25->idle / (60 * HZ); break; case AX25_BACKOFF: @@ -963,22 +938,14 @@ static int ax25_release(struct socket *sock, struct socket *peer) if (sk->type == SOCK_SEQPACKET) { switch (sk->protinfo.ax25->state) { case AX25_STATE_0: - sk->state = TCP_CLOSE; - sk->shutdown |= SEND_SHUTDOWN; - sk->state_change(sk); - sk->dead = 1; + ax25_disconnect(sk->protinfo.ax25, 0); ax25_destroy_socket(sk->protinfo.ax25); break; case AX25_STATE_1: case AX25_STATE_2: - ax25_clear_queues(sk->protinfo.ax25); ax25_send_control(sk->protinfo.ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - sk->protinfo.ax25->state = AX25_STATE_0; - sk->state = TCP_CLOSE; - sk->shutdown |= SEND_SHUTDOWN; - sk->state_change(sk); - sk->dead = 1; + ax25_disconnect(sk->protinfo.ax25, 0); ax25_destroy_socket(sk->protinfo.ax25); break; @@ -990,31 +957,34 @@ static int ax25_release(struct socket *sock, struct socket *peer) case AX25_PROTO_STD_SIMPLEX: case AX25_PROTO_STD_DUPLEX: ax25_send_control(sk->protinfo.ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - sk->protinfo.ax25->t3timer = 0; + ax25_stop_t2timer(sk->protinfo.ax25); + ax25_stop_t3timer(sk->protinfo.ax25); + ax25_stop_idletimer(sk->protinfo.ax25); break; #ifdef AX25_CONFIG_DAMA_SLAVE case AX25_PROTO_DAMA_SLAVE: - sk->protinfo.ax25->t3timer = 0; + ax25_stop_t3timer(sk->protinfo.ax25); break; #endif } - sk->protinfo.ax25->t1timer = sk->protinfo.ax25->t1 = ax25_calculate_t1(sk->protinfo.ax25); - sk->protinfo.ax25->state = AX25_STATE_2; - sk->state = TCP_CLOSE; - sk->shutdown |= SEND_SHUTDOWN; + ax25_calculate_t1(sk->protinfo.ax25); + ax25_start_t1timer(sk->protinfo.ax25); + sk->protinfo.ax25->state = AX25_STATE_2; + sk->state = TCP_CLOSE; + sk->shutdown |= SEND_SHUTDOWN; sk->state_change(sk); - sk->dead = 1; - sk->destroy = 1; + sk->dead = 1; + sk->destroy = 1; break; default: break; } } else { - sk->state = TCP_CLOSE; - sk->shutdown |= SEND_SHUTDOWN; + sk->state = TCP_CLOSE; + sk->shutdown |= SEND_SHUTDOWN; sk->state_change(sk); - sk->dead = 1; + sk->dead = 1; ax25_destroy_socket(sk->protinfo.ax25); } @@ -1092,8 +1062,9 @@ static int ax25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags) { struct sock *sk = sock->sk; - struct sockaddr_ax25 *addr = (struct sockaddr_ax25 *)uaddr; - int err; + struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)uaddr; + ax25_digi *digi = NULL; + int ct = 0, err; if (sk->state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) { sock->state = SS_CONNECTED; @@ -1114,36 +1085,36 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le if (addr_len != sizeof(struct sockaddr_ax25) && addr_len != sizeof(struct full_sockaddr_ax25)) return -EINVAL; - if (addr->sax25_family != AF_AX25) + if (fsa->fsa_ax25.sax25_family != AF_AX25) return -EINVAL; + if (sk->protinfo.ax25->digipeat != NULL) { + kfree(sk->protinfo.ax25->digipeat); + sk->protinfo.ax25->digipeat = NULL; + } + /* * Handle digi-peaters to be used. */ - if (addr_len == sizeof(struct full_sockaddr_ax25) && addr->sax25_ndigis != 0) { - int ct = 0; - struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)addr; - + if (addr_len == sizeof(struct full_sockaddr_ax25) && fsa->fsa_ax25.sax25_ndigis != 0) { /* Valid number of digipeaters ? */ - if (addr->sax25_ndigis < 1 || addr->sax25_ndigis > AX25_MAX_DIGIS) + if (fsa->fsa_ax25.sax25_ndigis < 1 || fsa->fsa_ax25.sax25_ndigis > AX25_MAX_DIGIS) return -EINVAL; - if (sk->protinfo.ax25->digipeat == NULL) { - if ((sk->protinfo.ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL) - return -ENOBUFS; - } + if ((digi = kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL) + return -ENOBUFS; - sk->protinfo.ax25->digipeat->ndigi = addr->sax25_ndigis; - sk->protinfo.ax25->digipeat->lastrepeat = -1; + digi->ndigi = fsa->fsa_ax25.sax25_ndigis; + digi->lastrepeat = -1; - while (ct < addr->sax25_ndigis) { + while (ct < fsa->fsa_ax25.sax25_ndigis) { if ((fsa->fsa_digipeater[ct].ax25_call[6] & AX25_HBIT) && sk->protinfo.ax25->iamdigi) { - sk->protinfo.ax25->digipeat->repeated[ct] = 1; - sk->protinfo.ax25->digipeat->lastrepeat = ct; + digi->repeated[ct] = 1; + digi->lastrepeat = ct; } else { - sk->protinfo.ax25->digipeat->repeated[ct] = 0; + digi->repeated[ct] = 0; } - sk->protinfo.ax25->digipeat->calls[ct] = fsa->fsa_digipeater[ct]; + digi->calls[ct] = fsa->fsa_digipeater[ct]; ct++; } } @@ -1154,7 +1125,7 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le * been filled in, error if it hasn't. */ if (sk->zapped) { - if ((err = ax25_rt_autobind(sk->protinfo.ax25, &addr->sax25_call)) < 0) + if ((err = ax25_rt_autobind(sk->protinfo.ax25, &fsa->fsa_ax25.sax25_call)) < 0) return err; ax25_fillin_cb(sk->protinfo.ax25, sk->protinfo.ax25->ax25_dev); ax25_insert_socket(sk->protinfo.ax25); @@ -1163,10 +1134,13 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le return -EHOSTUNREACH; } - if (sk->type == SOCK_SEQPACKET && ax25_find_cb(&sk->protinfo.ax25->source_addr, &addr->sax25_call, NULL, sk->protinfo.ax25->ax25_dev->dev) != NULL) + if (sk->type == SOCK_SEQPACKET && ax25_find_cb(&sk->protinfo.ax25->source_addr, &fsa->fsa_ax25.sax25_call, digi, sk->protinfo.ax25->ax25_dev->dev) != NULL) { + if (digi != NULL) kfree(digi); return -EADDRINUSE; /* Already such a connection */ + } - sk->protinfo.ax25->dest_addr = addr->sax25_call; + sk->protinfo.ax25->dest_addr = fsa->fsa_ax25.sax25_call; + sk->protinfo.ax25->digipeat = digi; /* First the easy one */ if (sk->type != SOCK_SEQPACKET) { @@ -1198,7 +1172,8 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le } sk->protinfo.ax25->state = AX25_STATE_1; - ax25_set_timer(sk->protinfo.ax25); /* Start going SABM SABM until a UA or a give up and DM */ + + ax25_start_heartbeat(sk->protinfo.ax25); /* Now the loop */ if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) @@ -1532,38 +1507,35 @@ static int ax25_shutdown(struct socket *sk, int how) static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { struct sock *sk = sock->sk; - struct ax25_info_struct ax25_info; - int err; - long amount = 0; switch (cmd) { - case TIOCOUTQ: - if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(int))) != 0) - return err; + case TIOCOUTQ: { + long amount; amount = sk->sndbuf - atomic_read(&sk->wmem_alloc); if (amount < 0) amount = 0; - put_user(amount, (int *)arg); + if (put_user(amount, (int *)arg)) + return -EFAULT; return 0; + } case TIOCINQ: { struct sk_buff *skb; + long amount = 0L; /* These two are safe on a single CPU system as only user tasks fiddle here */ if ((skb = skb_peek(&sk->receive_queue)) != NULL) amount = skb->len; - if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(int))) != 0) - return err; - put_user(amount, (int *)arg); + if (put_user(amount, (int *)arg)) + return -EFAULT; return 0; } case SIOCGSTAMP: if (sk != NULL) { - if (sk->stamp.tv_sec==0) + if (sk->stamp.tv_sec == 0) return -ENOENT; - if ((err = verify_area(VERIFY_WRITE,(void *)arg,sizeof(struct timeval))) != 0) - return err; - copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval)); + if (copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval))) + return -EFAULT; return 0; } return -EINVAL; @@ -1572,22 +1544,22 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) case SIOCAX25DELUID: /* Delete a uid from the uid/call map table */ case SIOCAX25GETUID: { struct sockaddr_ax25 sax25; - if ((err = verify_area(VERIFY_READ, (void *)arg, sizeof(struct sockaddr_ax25))) != 0) - return err; - copy_from_user(&sax25, (void *)arg, sizeof(sax25)); + if (copy_from_user(&sax25, (void *)arg, sizeof(sax25))) + return -EFAULT; return ax25_uid_ioctl(cmd, &sax25); } - case SIOCAX25NOUID: /* Set the default policy (default/bar) */ - if ((err = verify_area(VERIFY_READ, (void *)arg, sizeof(unsigned long))) != 0) - return err; + case SIOCAX25NOUID: { /* Set the default policy (default/bar) */ + long amount; if (!suser()) return -EPERM; - get_user(amount, (long *)arg); + if (get_user(amount, (long *)arg)) + return -EFAULT; if (amount > AX25_NOUID_BLOCK) return -EINVAL; ax25_uid_policy = amount; return 0; + } case SIOCADDRT: case SIOCDELRT: @@ -1601,33 +1573,33 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) return -EPERM; return ax25_ctl_ioctl(cmd, (void *)arg); - case SIOCAX25GETINFO: - if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(ax25_info))) != 0) - return err; - ax25_info.t1 = sk->protinfo.ax25->t1; - ax25_info.t2 = sk->protinfo.ax25->t2; - ax25_info.t3 = sk->protinfo.ax25->t3; - ax25_info.idle = sk->protinfo.ax25->idle; + case SIOCAX25GETINFO: { + struct ax25_info_struct ax25_info; + ax25_info.t1 = sk->protinfo.ax25->t1 / HZ; + ax25_info.t2 = sk->protinfo.ax25->t2 / HZ; + ax25_info.t3 = sk->protinfo.ax25->t3 / HZ; + ax25_info.idle = sk->protinfo.ax25->idle / (60 * HZ); ax25_info.n2 = sk->protinfo.ax25->n2; - ax25_info.t1timer = sk->protinfo.ax25->t1timer; - ax25_info.t2timer = sk->protinfo.ax25->t2timer; - ax25_info.t3timer = sk->protinfo.ax25->t3timer; - ax25_info.idletimer = sk->protinfo.ax25->idletimer; + ax25_info.t1timer = ax25_display_timer(&sk->protinfo.ax25->t1timer) / HZ; + ax25_info.t2timer = ax25_display_timer(&sk->protinfo.ax25->t2timer) / HZ; + ax25_info.t3timer = ax25_display_timer(&sk->protinfo.ax25->t3timer) / HZ; + ax25_info.idletimer = ax25_display_timer(&sk->protinfo.ax25->idletimer) / (60 * HZ); ax25_info.n2count = sk->protinfo.ax25->n2count; ax25_info.state = sk->protinfo.ax25->state; ax25_info.rcv_q = atomic_read(&sk->rmem_alloc); ax25_info.snd_q = atomic_read(&sk->wmem_alloc); - copy_to_user((void *)arg, &ax25_info, sizeof(ax25_info)); + if (copy_to_user((void *)arg, &ax25_info, sizeof(ax25_info))) + return -EFAULT; return 0; + } case SIOCAX25ADDFWD: case SIOCAX25DELFWD: { struct ax25_fwd_struct ax25_fwd; if (!suser()) return -EPERM; - if ((err = verify_area(VERIFY_READ, (void *)arg, sizeof(ax25_fwd))) != 0) - return err; - copy_from_user(&ax25_fwd, (void *)arg, sizeof(ax25_fwd)); + if (copy_from_user(&ax25_fwd, (void *)arg, sizeof(ax25_fwd))) + return -EFAULT; return ax25_fwd_ioctl(cmd, &ax25_fwd); } @@ -1655,13 +1627,14 @@ static int ax25_get_info(char *buffer, char **start, off_t offset, int length, i { ax25_cb *ax25; const char *devname; + char callbuf[15]; int len = 0; off_t pos = 0; off_t begin = 0; cli(); - len += sprintf(buffer, "dest_addr src_addr dev st vs vr va t1 t2 t3 idle n2 rtt wnd paclen Snd-Q Rcv-Q\n"); + len += sprintf(buffer, "dest_addr src_addr dev st vs vr va t1 t2 t3 idle n2 rtt wnd paclen Snd-Q Rcv-Q\n"); for (ax25 = ax25_list; ax25 != NULL; ax25 = ax25->next) { if (ax25->ax25_dev == NULL) @@ -1671,20 +1644,28 @@ static int ax25_get_info(char *buffer, char **start, off_t offset, int length, i len += sprintf(buffer + len, "%-9s ", ax2asc(&ax25->dest_addr)); - len += sprintf(buffer + len, "%-9s %-4s %2d %3d %3d %3d %3d/%03d %2d/%02d %3d/%03d %3d/%03d %2d/%02d %3d %3d %5d", - ax2asc(&ax25->source_addr), devname, + + sprintf(callbuf, "%s%c", ax2asc(&ax25->source_addr), + (ax25->iamdigi) ? '*' : ' '); + + len += sprintf(buffer + len, "%-10s %-4s %2d %3d %3d %3d %3lu/%03lu %2lu/%02lu %3lu/%03lu %3lu/%03lu %2d/%02d %3lu %3d %5d", + callbuf, + devname, ax25->state, - ax25->vs, ax25->vr, ax25->va, - ax25->t1timer / AX25_SLOWHZ, - ax25->t1 / AX25_SLOWHZ, - ax25->t2timer / AX25_SLOWHZ, - ax25->t2 / AX25_SLOWHZ, - ax25->t3timer / AX25_SLOWHZ, - ax25->t3 / AX25_SLOWHZ, - ax25->idletimer / (AX25_SLOWHZ * 60), - ax25->idle / (AX25_SLOWHZ * 60), - ax25->n2count, ax25->n2, - ax25->rtt / AX25_SLOWHZ, + ax25->vs, + ax25->vr, + ax25->va, + ax25_display_timer(&ax25->t1timer) / HZ, + ax25->t1 / HZ, + ax25_display_timer(&ax25->t2timer) / HZ, + ax25->t2 / HZ, + ax25_display_timer(&ax25->t3timer) / HZ, + ax25->t3 / HZ, + ax25_display_timer(&ax25->idletimer) / (60 * HZ), + ax25->idle / (60 * HZ), + ax25->n2count, + ax25->n2, + ax25->rtt / HZ, ax25->window, ax25->paclen); @@ -1764,7 +1745,7 @@ static struct notifier_block ax25_dev_notifier = { EXPORT_SYMBOL(ax25_encapsulate); EXPORT_SYMBOL(ax25_rebuild_header); EXPORT_SYMBOL(ax25_findbyuid); -EXPORT_SYMBOL(ax25_link_up); +EXPORT_SYMBOL(ax25_find_cb); EXPORT_SYMBOL(ax25_linkfail_register); EXPORT_SYMBOL(ax25_linkfail_release); EXPORT_SYMBOL(ax25_listen_register); @@ -1777,6 +1758,7 @@ EXPORT_SYMBOL(ax25cmp); EXPORT_SYMBOL(ax2asc); EXPORT_SYMBOL(asc2ax); EXPORT_SYMBOL(null_ax25_address); +EXPORT_SYMBOL(ax25_display_timer); #ifdef CONFIG_PROC_FS static struct proc_dir_entry proc_ax25_route = { @@ -1815,7 +1797,7 @@ __initfunc(void ax25_proto_init(struct net_proto *pro)) proc_net_register(&proc_ax25_calls); #endif - printk(KERN_INFO "G4KLX/GW4PTS AX.25 for Linux. Version 0.36 for Linux NET3.038 (Linux 2.1)\n"); + printk(KERN_INFO "G4KLX/GW4PTS AX.25 for Linux. Version 0.37 for Linux NET3.038 (Linux 2.1)\n"); } #ifdef MODULE diff --git a/net/ax25/ax25_addr.c b/net/ax25/ax25_addr.c index 77bfabe73257..5daf92fa5c16 100644 --- a/net/ax25/ax25_addr.c +++ b/net/ax25/ax25_addr.c @@ -1,5 +1,5 @@ /* - * AX.25 release 036 + * AX.25 release 037 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -165,27 +165,23 @@ unsigned char *ax25_addr_parse(unsigned char *buf, int len, ax25_address *src, a if (len < 14) return NULL; - if (flags != NULL) { - *flags = 0; + *flags = 0; - if (buf[6] & AX25_CBIT) - *flags = AX25_COMMAND; - if (buf[13] & AX25_CBIT) - *flags = AX25_RESPONSE; - } + if (buf[6] & AX25_CBIT) + *flags = AX25_COMMAND; + if (buf[13] & AX25_CBIT) + *flags = AX25_RESPONSE; if (dama != NULL) *dama = ~buf[13] & AX25_DAMA_FLAG; /* Copy to, from */ - if (dest != NULL) - memcpy(dest, buf + 0, AX25_ADDR_LEN); - - if (src != NULL) - memcpy(src, buf + 7, AX25_ADDR_LEN); + memcpy(dest, buf + 0, AX25_ADDR_LEN); + memcpy(src, buf + 7, AX25_ADDR_LEN); buf += 2 * AX25_ADDR_LEN; len -= 2 * AX25_ADDR_LEN; + digi->lastrepeat = -1; digi->ndigi = 0; @@ -193,15 +189,14 @@ unsigned char *ax25_addr_parse(unsigned char *buf, int len, ax25_address *src, a if (d >= AX25_MAX_DIGIS) return NULL; /* Max of 6 digis */ if (len < 7) return NULL; /* Short packet */ - if (digi != NULL) { - memcpy(&digi->calls[d], buf, AX25_ADDR_LEN); - digi->ndigi = d + 1; - if (buf[6] & AX25_HBIT) { - digi->repeated[d] = 1; - digi->lastrepeat = d; - } else { - digi->repeated[d] = 0; - } + memcpy(&digi->calls[d], buf, AX25_ADDR_LEN); + digi->ndigi = d + 1; + + if (buf[6] & AX25_HBIT) { + digi->repeated[d] = 1; + digi->lastrepeat = d; + } else { + digi->repeated[d] = 0; } buf += AX25_ADDR_LEN; @@ -285,15 +280,15 @@ int ax25_addr_size(ax25_digi *dp) */ void ax25_digi_invert(ax25_digi *in, ax25_digi *out) { - int ct = 0; + int ct; out->ndigi = in->ndigi; out->lastrepeat = in->ndigi - in->lastrepeat - 2; /* Invert the digipeaters */ + for (ct = 0; ct < in->ndigi; ct++) { + out->calls[ct] = in->calls[in->ndigi - ct - 1]; - while (ct < in->ndigi) { - out->calls[ct] = in->calls[in->ndigi - ct - 1]; if (ct <= out->lastrepeat) { out->calls[ct].ax25_call[6] |= AX25_HBIT; out->repeated[ct] = 1; @@ -301,7 +296,6 @@ void ax25_digi_invert(ax25_digi *in, ax25_digi *out) out->calls[ct].ax25_call[6] &= ~AX25_HBIT; out->repeated[ct] = 0; } - ct++; } } diff --git a/net/ax25/ax25_dev.c b/net/ax25/ax25_dev.c index 6468faf77348..3f4f46ad4dc3 100644 --- a/net/ax25/ax25_dev.c +++ b/net/ax25/ax25_dev.c @@ -1,5 +1,5 @@ /* - * AX.25 release 036 + * AX.25 release 037 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * diff --git a/net/ax25/ax25_ds_in.c b/net/ax25/ax25_ds_in.c index 1394c9ab78f2..6b5e68236add 100644 --- a/net/ax25/ax25_ds_in.c +++ b/net/ax25/ax25_ds_in.c @@ -1,5 +1,5 @@ /* - * AX.25 release 036 + * AX.25 release 037 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -18,6 +18,7 @@ * History * AX.25 036 Jonathan(G4KLX) Cloned from ax25_in.c * Joerg(DL1BKE) Fixed it. + * AX.25 037 Jonathan(G4KLX) New timer architecture. */ #include @@ -64,9 +65,9 @@ static int ax25_ds_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int framet case AX25_UA: ax25_calculate_rtt(ax25); - ax25->t1timer = 0; - ax25->t3timer = ax25->t3; - ax25->idletimer = ax25->idle; + ax25_stop_t1timer(ax25); + ax25_start_t3timer(ax25); + ax25_start_idletimer(ax25); ax25->vs = 0; ax25->va = 0; ax25->vr = 0; @@ -90,23 +91,11 @@ static int ax25_ds_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int framet break; case AX25_DM: - if (pf) { - ax25_clear_queues(ax25); - ax25->state = AX25_STATE_0; - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = ECONNREFUSED; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } - } + if (pf) ax25_disconnect(ax25, ECONNREFUSED); break; default: - if (pf) - ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND); + if (pf) ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND); break; } @@ -128,31 +117,15 @@ static int ax25_ds_state2_machine(ax25_cb *ax25, struct sk_buff *skb, int framet case AX25_DISC: ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25->state = AX25_STATE_0; ax25_dama_off(ax25); - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = 0; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } + ax25_disconnect(ax25, 0); break; case AX25_DM: case AX25_UA: if (pf) { - ax25->state = AX25_STATE_0; ax25_dama_off(ax25); - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = 0; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } + ax25_disconnect(ax25, 0); } break; @@ -187,10 +160,10 @@ static int ax25_ds_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int framet ax25->modulus = AX25_MODULUS; ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); + ax25_stop_t1timer(ax25); + ax25_start_t3timer(ax25); + ax25_start_idletimer(ax25); ax25->condition = 0x00; - ax25->t1timer = 0; - ax25->t3timer = ax25->t3; - ax25->idletimer = ax25->idle; ax25->vs = 0; ax25->va = 0; ax25->vr = 0; @@ -199,34 +172,14 @@ static int ax25_ds_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int framet break; case AX25_DISC: - ax25_clear_queues(ax25); ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25->t3timer = 0; - ax25->state = AX25_STATE_0; ax25_dama_off(ax25); - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = 0; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } + ax25_disconnect(ax25, 0); break; case AX25_DM: - ax25_clear_queues(ax25); - ax25->t3timer = 0; - ax25->state = AX25_STATE_0; ax25_dama_off(ax25); - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = ECONNRESET; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } + ax25_disconnect(ax25, ECONNRESET); break; case AX25_RR: @@ -250,9 +203,9 @@ static int ax25_ds_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int framet if (ax25_validate_nr(ax25, nr)) { ax25_frames_acked(ax25, nr); ax25_calculate_rtt(ax25); + ax25_stop_t1timer(ax25); + ax25_start_t3timer(ax25); ax25->n2count = 0; - ax25->t1timer = 0; - ax25->t3timer = ax25->t3; ax25_requeue_frames(ax25); if (type == AX25_COMMAND && pf) ax25_ds_enquiry_response(ax25); @@ -281,18 +234,15 @@ static int ax25_ds_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int framet if (ns == ax25->vr) { ax25->vr = (ax25->vr + 1) % AX25_MODULUS; queued = ax25_rx_iframe(ax25, skb); - if (ax25->condition & AX25_COND_OWN_RX_BUSY) { + if (ax25->condition & AX25_COND_OWN_RX_BUSY) ax25->vr = ns; /* ax25->vr - 1 */ - if (pf) ax25_ds_enquiry_response(ax25); - break; - } ax25->condition &= ~AX25_COND_REJECT; if (pf) { ax25_ds_enquiry_response(ax25); } else { if (!(ax25->condition & AX25_COND_ACK_PENDING)) { - ax25->t2timer = ax25->t2; ax25->condition |= AX25_COND_ACK_PENDING; + ax25_start_t2timer(ax25); } } } else { diff --git a/net/ax25/ax25_ds_subr.c b/net/ax25/ax25_ds_subr.c index 6037f16b48c7..89ca64f3fbce 100644 --- a/net/ax25/ax25_ds_subr.c +++ b/net/ax25/ax25_ds_subr.c @@ -1,5 +1,5 @@ /* - * AX.25 release 036 + * AX.25 release 037 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -19,6 +19,7 @@ * AX.25 036 Jonathan(G4KLX) Cloned from ax25_out.c and ax25_subr.c. * Joerg(DL1BKE) Changed ax25_ds_enquiry_response(), * fixed ax25_dama_on() and ax25_dama_off(). + * AX.25 037 Jonathan(G4KLX) New timer architecture. */ #include @@ -91,7 +92,7 @@ void ax25_ds_enquiry_response(ax25_cb *ax25) else ax25->n2count = 0; - ax25->t3timer = ax25->t3; + ax25_start_t3timer(ax25); ax25_ds_set_timer(ax25->ax25_dev); for (ax25o = ax25_list; ax25o != NULL; ax25o = ax25o->next) { @@ -114,7 +115,7 @@ void ax25_ds_enquiry_response(ax25_cb *ax25) if (ax25o->state == AX25_STATE_1 || ax25o->state == AX25_STATE_2 || skb_peek(&ax25o->ack_queue) != NULL) ax25_ds_t1_timeout(ax25o); - ax25o->t3timer = ax25o->t3; + ax25_start_t3timer(ax25o); } } @@ -122,9 +123,10 @@ void ax25_ds_establish_data_link(ax25_cb *ax25) { ax25->condition &= AX25_COND_DAMA_MODE; ax25->n2count = 0; - ax25->t3timer = ax25->t3; - ax25->t2timer = 0; - ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25); + ax25_calculate_t1(ax25); + ax25_start_t1timer(ax25); + ax25_stop_t2timer(ax25); + ax25_start_t3timer(ax25); } /* diff --git a/net/ax25/ax25_ds_timer.c b/net/ax25/ax25_ds_timer.c index 847be57908b9..841149996770 100644 --- a/net/ax25/ax25_ds_timer.c +++ b/net/ax25/ax25_ds_timer.c @@ -1,5 +1,5 @@ /* - * AX.25 release 036 + * AX.25 release 037 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -12,6 +12,7 @@ * History * AX.25 036 Jonathan(G4KLX) Cloned from ax25_timer.c. * Joerg(DL1BKE) Added DAMA Slave Timeout timer + * AX.25 037 Jonathan(G4KLX) New timer architecture. */ #include @@ -93,41 +94,21 @@ static void ax25_ds_timeout(unsigned long arg) if (ax25->ax25_dev != ax25_dev || !(ax25->condition & AX25_COND_DAMA_MODE)) continue; - ax25_link_failed(&ax25->dest_addr, ax25_dev->dev); - ax25_clear_queues(ax25); ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - ax25->state = AX25_STATE_0; - - if (ax25->sk != NULL) { - SOCK_DEBUG(ax25->sk, "AX.25 DAMA Slave Timeout\n"); - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = ETIMEDOUT; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } - - ax25_set_timer(ax25); /* notify socket... */ + ax25_disconnect(ax25, ETIMEDOUT); } ax25_dev_dama_off(ax25_dev); } -/* - * AX.25 TIMER - * - * This routine is called every 100ms. Decrement timer by this - * amount - if expired then process the event. - */ -void ax25_ds_timer(ax25_cb *ax25) +void ax25_ds_heartbeat_expiry(ax25_cb *ax25) { switch (ax25->state) { + case AX25_STATE_0: /* Magic here: If we listen() and a new link dies before it is accepted() it isn't 'dead' so doesn't get removed. */ if (ax25->sk == NULL || ax25->sk->destroy || (ax25->sk->state == TCP_LISTEN && ax25->sk->dead)) { - del_timer(&ax25->timer); ax25_destroy_socket(ax25); return; } @@ -144,71 +125,51 @@ void ax25_ds_timer(ax25_cb *ax25) } } break; - - default: - break; - } - - /* dl1bke 960114: T3 works much like the IDLE timeout, but - * gets reloaded with every frame for this - * connection. - */ - - if (ax25->t3timer > 0 && --ax25->t3timer == 0) { - ax25_link_failed(&ax25->dest_addr, ax25->ax25_dev->dev); - ax25_clear_queues(ax25); - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - - ax25->state = AX25_STATE_0; - ax25_dama_off(ax25); - - if (ax25->sk != NULL) { - SOCK_DEBUG(ax25->sk, "AX.25 T3 Timeout\n"); - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = ETIMEDOUT; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } - - ax25_set_timer(ax25); - - return; } - /* dl1bke 960228: close the connection when IDLE expires. - * unlike T3 this timer gets reloaded only on - * I frames. - */ - - if (ax25->idletimer > 0 && --ax25->idletimer == 0) { - ax25_clear_queues(ax25); - - ax25->n2count = 0; - ax25->t3timer = ax25->t3; + ax25_start_heartbeat(ax25); +} + +/* dl1bke 960114: T3 works much like the IDLE timeout, but + * gets reloaded with every frame for this + * connection. + */ +void ax25_ds_t3timer_expiry(ax25_cb *ax25) +{ + ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); + ax25_dama_off(ax25); + ax25_disconnect(ax25, ETIMEDOUT); +} - /* state 1 or 2 should not happen, but... */ +/* dl1bke 960228: close the connection when IDLE expires. + * unlike T3 this timer gets reloaded only on + * I frames. + */ +void ax25_ds_idletimer_expiry(ax25_cb *ax25) +{ + ax25_clear_queues(ax25); - if (ax25->state == AX25_STATE_1 || ax25->state == AX25_STATE_2) - ax25->state = AX25_STATE_0; - else - ax25->state = AX25_STATE_2; + ax25->n2count = 0; - ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25); + /* state 1 or 2 should not happen, but... */ - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = 0; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - ax25->sk->destroy = 1; - } + if (ax25->state == AX25_STATE_1 || ax25->state == AX25_STATE_2) + ax25->state = AX25_STATE_0; + else + ax25->state = AX25_STATE_2; + + ax25_calculate_t1(ax25); + ax25_start_t1timer(ax25); + ax25_start_t3timer(ax25); + + if (ax25->sk != NULL) { + ax25->sk->state = TCP_CLOSE; + ax25->sk->err = 0; + ax25->sk->shutdown |= SEND_SHUTDOWN; + if (!ax25->sk->dead) + ax25->sk->state_change(ax25->sk); + ax25->sk->dead = 1; } - - ax25_set_timer(ax25); } /* dl1bke 960114: The DAMA protocol requires to send data and SABM/DISC @@ -222,20 +183,12 @@ void ax25_ds_timer(ax25_cb *ax25) void ax25_ds_t1_timeout(ax25_cb *ax25) { switch (ax25->state) { + case AX25_STATE_1: if (ax25->n2count == ax25->n2) { if (ax25->modulus == AX25_MODULUS) { - ax25_link_failed(&ax25->dest_addr, ax25->ax25_dev->dev); - ax25_clear_queues(ax25); - ax25->state = AX25_STATE_0; - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = ETIMEDOUT; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } + ax25_disconnect(ax25, ETIMEDOUT); + return; } else { ax25->modulus = AX25_MODULUS; ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; @@ -253,19 +206,9 @@ void ax25_ds_t1_timeout(ax25_cb *ax25) case AX25_STATE_2: if (ax25->n2count == ax25->n2) { - ax25_link_failed(&ax25->dest_addr, ax25->ax25_dev->dev); - ax25_clear_queues(ax25); - ax25->state = AX25_STATE_0; ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = ETIMEDOUT; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } + ax25_disconnect(ax25, ETIMEDOUT); + return; } else { ax25->n2count++; } @@ -273,28 +216,17 @@ void ax25_ds_t1_timeout(ax25_cb *ax25) case AX25_STATE_3: if (ax25->n2count == ax25->n2) { - ax25_link_failed(&ax25->dest_addr, ax25->ax25_dev->dev); - ax25_clear_queues(ax25); ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE); - ax25->state = AX25_STATE_0; - if (ax25->sk != NULL) { - SOCK_DEBUG(ax25->sk, "AX.25 link Failure\n"); - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = ETIMEDOUT; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } + ax25_disconnect(ax25, ETIMEDOUT); + return; } else { ax25->n2count++; } break; } - ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25); - - ax25_set_timer(ax25); + ax25_calculate_t1(ax25); + ax25_start_t1timer(ax25); } #endif diff --git a/net/ax25/ax25_iface.c b/net/ax25/ax25_iface.c index d27a3af23671..7e6ad845c4e0 100644 --- a/net/ax25/ax25_iface.c +++ b/net/ax25/ax25_iface.c @@ -1,5 +1,5 @@ /* - * AX.25 release 036 + * AX.25 release 037 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -44,7 +44,7 @@ static struct protocol_struct { static struct linkfail_struct { struct linkfail_struct *next; - void (*func)(ax25_address *, struct device *); + void (*func)(ax25_cb *, int); } *linkfail_list = NULL; static struct listen_struct { @@ -114,7 +114,7 @@ void ax25_protocol_release(unsigned int pid) restore_flags(flags); } -int ax25_linkfail_register(void (*func)(ax25_address *, struct device *)) +int ax25_linkfail_register(void (*func)(ax25_cb *, int)) { struct linkfail_struct *linkfail; unsigned long flags; @@ -135,7 +135,7 @@ int ax25_linkfail_register(void (*func)(ax25_address *, struct device *)) return 1; } -void ax25_linkfail_release(void (*func)(ax25_address *, struct device *)) +void ax25_linkfail_release(void (*func)(ax25_cb *, int)) { struct linkfail_struct *s, *linkfail = linkfail_list; unsigned long flags; @@ -248,21 +248,12 @@ int ax25_listen_mine(ax25_address *callsign, struct device *dev) return 0; } -void ax25_link_failed(ax25_address *callsign, struct device *dev) +void ax25_link_failed(ax25_cb *ax25, int reason) { struct linkfail_struct *linkfail; for (linkfail = linkfail_list; linkfail != NULL; linkfail = linkfail->next) - (linkfail->func)(callsign, dev); -} - -/* - * Return the state of an AX.25 link given source, destination, and - * device. - */ -int ax25_link_up(ax25_address *src, ax25_address *dest, ax25_digi *digi, struct device *dev) -{ - return ax25_find_cb(src, dest, digi, dev) != NULL; + (linkfail->func)(ax25, reason); } int ax25_protocol_is_registered(unsigned int pid) diff --git a/net/ax25/ax25_in.c b/net/ax25/ax25_in.c index 2e6090d76443..a17109bff862 100644 --- a/net/ax25/ax25_in.c +++ b/net/ax25/ax25_in.c @@ -1,5 +1,5 @@ /* - * AX.25 release 036 + * AX.25 release 037 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -35,6 +35,7 @@ * AX.25 035 Hans(PE1AYX) Fixed interface to IP layer. * AX.25 036 Jonathan(G4KLX) Move DAMA code into own file. * Joerg(DL1BKE) Fixed DAMA Slave. + * AX.25 037 Jonathan(G4KLX) New timer architecture. */ #include @@ -136,7 +137,7 @@ int ax25_rx_iframe(ax25_cb *ax25, struct sk_buff *skb) if (skb == NULL) return 0; - ax25->idletimer = ax25->idle; + ax25_start_idletimer(ax25); pid = *skb->data; @@ -193,8 +194,6 @@ static int ax25_process_rx_frame(ax25_cb *ax25, struct sk_buff *skb, int type, i if (ax25->state == AX25_STATE_0) return 0; - del_timer(&ax25->timer); - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { case AX25_PROTO_STD_SIMPLEX: case AX25_PROTO_STD_DUPLEX: @@ -211,8 +210,6 @@ static int ax25_process_rx_frame(ax25_cb *ax25, struct sk_buff *skb, int type, i #endif } - ax25_set_timer(ax25); - return queued; } @@ -413,7 +410,6 @@ static int ax25_rcv(struct sk_buff *skb, struct device *dev, ax25_address *dev_a } ax25_fillin_cb(ax25, ax25_dev); - ax25->idletimer = ax25->idle; } ax25->source_addr = dest; @@ -453,12 +449,13 @@ static int ax25_rcv(struct sk_buff *skb, struct device *dev, ax25_address *dev_a ax25_dama_on(ax25); #endif - ax25->t3timer = ax25->t3; - ax25->state = AX25_STATE_3; + ax25->state = AX25_STATE_3; ax25_insert_socket(ax25); - ax25_set_timer(ax25); + ax25_start_heartbeat(ax25); + ax25_start_t3timer(ax25); + ax25_start_idletimer(ax25); if (sk != NULL) { if (!sk->dead) diff --git a/net/ax25/ax25_ip.c b/net/ax25/ax25_ip.c index 009428fa9a4d..d26f008dddb7 100644 --- a/net/ax25/ax25_ip.c +++ b/net/ax25/ax25_ip.c @@ -1,5 +1,5 @@ /* - * AX.25 release 036 + * AX.25 release 037 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -105,20 +105,25 @@ int ax25_encapsulate(struct sk_buff *skb, struct device *dev, unsigned short typ int ax25_rebuild_header(struct sk_buff *skb) { struct sk_buff *ourskb; - int mode; unsigned char *bp = skb->data; struct device *dev = skb->dev; + ax25_address *src, *dst; + ax25_route *route; ax25_dev *ax25_dev; + dst = (ax25_address *)(bp + 1); + src = (ax25_address *)(bp + 8); + if (arp_find(bp + 1, skb)) return 1; if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) return 1; + route = ax25_rt_find_route(dst, dev); + if (bp[16] == AX25_P_IP) { - mode = ax25_ip_mode_get((ax25_address *)(bp + 1), dev); - if (mode == 'V' || (mode == ' ' && ax25_dev->values[AX25_VALUES_IPDEFMODE])) { + if (route->ip_mode == 'V' || (route->ip_mode == ' ' && ax25_dev->values[AX25_VALUES_IPDEFMODE])) { /* * We copy the buffer and release the original thereby * keeping it straight @@ -146,7 +151,7 @@ int ax25_rebuild_header(struct sk_buff *skb) skb_pull(ourskb, AX25_HEADER_LEN - 1); /* Keep PID */ - ax25_send_frame(ourskb, ax25_dev->values[AX25_VALUES_PACLEN], (ax25_address *)(bp + 8), (ax25_address *)(bp + 1), NULL, dev); + ax25_send_frame(ourskb, ax25_dev->values[AX25_VALUES_PACLEN], src, dst, route->digipeat, dev); return 1; } @@ -160,15 +165,19 @@ int ax25_rebuild_header(struct sk_buff *skb) bp[14] |= AX25_EBIT; bp[14] |= AX25_SSSID_SPARE; - if ((ourskb = ax25_dg_build_path(skb, (ax25_address *)(bp + 1), dev)) == NULL) { - kfree_skb(skb, FREE_WRITE); - return 1; + if (route->digipeat != NULL) { + if ((ourskb = ax25_rt_build_path(skb, src, dst, route->digipeat)) == NULL) { + kfree_skb(skb, FREE_WRITE); + return 1; + } + + skb = ourskb; } - ourskb->dev = dev; - ourskb->priority = SOPRI_NORMAL; + skb->dev = dev; + skb->priority = SOPRI_NORMAL; - ax25_queue_xmit(ourskb); + ax25_queue_xmit(skb); return 1; } diff --git a/net/ax25/ax25_out.c b/net/ax25/ax25_out.c index db7207b28dd5..4550302d731b 100644 --- a/net/ax25/ax25_out.c +++ b/net/ax25/ax25_out.c @@ -1,5 +1,5 @@ /* - * AX.25 release 036 + * AX.25 release 037 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -26,6 +26,7 @@ * AX.25 I-Frames. Added PACLEN parameter. * Joerg(DL1BKE) Fixed a problem with buffer allocation * for fragments. + * AX.25 037 Jonathan(G4KLX) New timer architecture. */ #include @@ -52,7 +53,7 @@ #include #include -int ax25_send_frame(struct sk_buff *skb, int paclen, ax25_address *src, ax25_address *dest, ax25_digi *digi, struct device *dev) +ax25_cb *ax25_send_frame(struct sk_buff *skb, int paclen, ax25_address *src, ax25_address *dest, ax25_digi *digi, struct device *dev) { ax25_dev *ax25_dev; ax25_cb *ax25; @@ -65,15 +66,14 @@ int ax25_send_frame(struct sk_buff *skb, int paclen, ax25_address *src, ax25_add */ if ((ax25 = ax25_find_cb(src, dest, digi, dev)) != NULL) { ax25_output(ax25, paclen, skb); - ax25->idletimer = ax25->idle; - return 1; /* It already existed */ + return ax25; /* It already existed */ } if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) - return 0; + return NULL; if ((ax25 = ax25_create_cb()) == NULL) - return 0; + return NULL; ax25_fillin_cb(ax25, ax25_dev); @@ -83,11 +83,9 @@ int ax25_send_frame(struct sk_buff *skb, int paclen, ax25_address *src, ax25_add if (digi != NULL) { if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) { ax25_free_cb(ax25); - return 0; + return NULL; } *ax25->digipeat = *digi; - } else { - ax25_rt_build_path(ax25, dest, dev); } switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { @@ -106,19 +104,15 @@ int ax25_send_frame(struct sk_buff *skb, int paclen, ax25_address *src, ax25_add #endif } - /* idle timeouts only for mode vc connections */ - - ax25->idletimer = ax25->idle; - ax25_insert_socket(ax25); ax25->state = AX25_STATE_1; - ax25_set_timer(ax25); + ax25_start_heartbeat(ax25); ax25_output(ax25, paclen, skb); - return 1; /* We had to create it */ + return ax25; /* We had to create it */ } /* @@ -195,10 +189,8 @@ void ax25_output(ax25_cb *ax25, int paclen, struct sk_buff *skb) } if (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_STD_SIMPLEX || - ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_STD_DUPLEX) { - if (ax25->state == AX25_STATE_3 || ax25->state == AX25_STATE_4) - ax25_kick(ax25); - } + ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_STD_DUPLEX) + ax25_kick(ax25); } /* @@ -228,6 +220,8 @@ static void ax25_send_iframe(ax25_cb *ax25, struct sk_buff *skb, int poll_bit) frame[1] |= (ax25->vr << 1); } + ax25_start_idletimer(ax25); + ax25_transmit_buffer(ax25, skb, AX25_COMMAND); } @@ -237,76 +231,80 @@ void ax25_kick(ax25_cb *ax25) int last = 1; unsigned short start, end, next; - del_timer(&ax25->timer); + if (ax25->state != AX25_STATE_3 && ax25->state != AX25_STATE_4) + return; + + if (ax25->condition & AX25_COND_PEER_RX_BUSY) + return; + + if (skb_peek(&ax25->write_queue) == NULL) + return; start = (skb_peek(&ax25->ack_queue) == NULL) ? ax25->va : ax25->vs; end = (ax25->va + ax25->window) % ax25->modulus; - if (!(ax25->condition & AX25_COND_PEER_RX_BUSY) && - start != end && - skb_peek(&ax25->write_queue) != NULL) { + if (start == end) + return; - ax25->vs = start; + ax25->vs = start; - /* - * Transmit data until either we're out of data to send or - * the window is full. Send a poll on the final I frame if - * the window is filled. - */ + /* + * Transmit data until either we're out of data to send or + * the window is full. Send a poll on the final I frame if + * the window is filled. + */ - /* - * Dequeue the frame and copy it. - */ - skb = skb_dequeue(&ax25->write_queue); + /* + * Dequeue the frame and copy it. + */ + skb = skb_dequeue(&ax25->write_queue); - do { - if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) { - skb_queue_head(&ax25->write_queue, skb); - break; - } + do { + if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) { + skb_queue_head(&ax25->write_queue, skb); + break; + } - if (skb->sk != NULL) - skb_set_owner_w(skbn, skb->sk); + if (skb->sk != NULL) + skb_set_owner_w(skbn, skb->sk); - next = (ax25->vs + 1) % ax25->modulus; - last = (next == end); + next = (ax25->vs + 1) % ax25->modulus; + last = (next == end); - /* - * Transmit the frame copy. - * bke 960114: do not set the Poll bit on the last frame - * in DAMA mode. - */ - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_send_iframe(ax25, skbn, (last) ? AX25_POLLON : AX25_POLLOFF); - break; + /* + * Transmit the frame copy. + * bke 960114: do not set the Poll bit on the last frame + * in DAMA mode. + */ + switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { + case AX25_PROTO_STD_SIMPLEX: + case AX25_PROTO_STD_DUPLEX: + ax25_send_iframe(ax25, skbn, (last) ? AX25_POLLON : AX25_POLLOFF); + break; #ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - ax25_send_iframe(ax25, skbn, AX25_POLLOFF); - break; + case AX25_PROTO_DAMA_SLAVE: + ax25_send_iframe(ax25, skbn, AX25_POLLOFF); + break; #endif - } + } - ax25->vs = next; + ax25->vs = next; - /* - * Requeue the original data frame. - */ - skb_queue_tail(&ax25->ack_queue, skb); + /* + * Requeue the original data frame. + */ + skb_queue_tail(&ax25->ack_queue, skb); - } while (!last && (skb = skb_dequeue(&ax25->write_queue)) != NULL); + } while (!last && (skb = skb_dequeue(&ax25->write_queue)) != NULL); - ax25->condition &= ~AX25_COND_ACK_PENDING; + ax25->condition &= ~AX25_COND_ACK_PENDING; - if (ax25->t1timer == 0) { - ax25->t3timer = 0; - ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25); - } + if (!ax25_t1timer_running(ax25)) { + ax25_stop_t3timer(ax25); + ax25_calculate_t1(ax25); + ax25_start_t1timer(ax25); } - - ax25_set_timer(ax25); } void ax25_transmit_buffer(ax25_cb *ax25, struct sk_buff *skb, int type) @@ -316,14 +314,7 @@ void ax25_transmit_buffer(ax25_cb *ax25, struct sk_buff *skb, int type) int headroom; if (ax25->ax25_dev == NULL) { - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = ENETUNREACH; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } + ax25_disconnect(ax25, ENETUNREACH); return; } @@ -381,12 +372,13 @@ void ax25_check_iframes_acked(ax25_cb *ax25, unsigned short nr) if (ax25->vs == nr) { ax25_frames_acked(ax25, nr); ax25_calculate_rtt(ax25); - ax25->t1timer = 0; - ax25->t3timer = ax25->t3; + ax25_stop_t1timer(ax25); + ax25_start_t3timer(ax25); } else { if (ax25->va != nr) { ax25_frames_acked(ax25, nr); - ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25); + ax25_calculate_t1(ax25); + ax25_start_t1timer(ax25); } } } diff --git a/net/ax25/ax25_route.c b/net/ax25/ax25_route.c index 3cda48a173b1..2c7d082a94be 100644 --- a/net/ax25/ax25_route.c +++ b/net/ax25/ax25_route.c @@ -1,5 +1,5 @@ /* - * AX.25 release 036 + * AX.25 release 037 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -120,13 +120,12 @@ int ax25_rt_ioctl(unsigned int cmd, void *arg) struct ax25_routes_struct route; struct ax25_route_opt_struct rt_option; ax25_dev *ax25_dev; - int i, err; + int i; switch (cmd) { case SIOCADDRT: - if ((err = verify_area(VERIFY_READ, arg, sizeof(route))) != 0) - return err; - copy_from_user(&route, arg, sizeof(route)); + if (copy_from_user(&route, arg, sizeof(route))) + return -EFAULT; if ((ax25_dev = ax25_addr_ax25dev(&route.port_addr)) == NULL) return -EINVAL; if (route.digi_count > AX25_MAX_DIGIS) @@ -175,9 +174,8 @@ int ax25_rt_ioctl(unsigned int cmd, void *arg) break; case SIOCDELRT: - if ((err = verify_area(VERIFY_READ, arg, sizeof(route))) != 0) - return err; - copy_from_user(&route, arg, sizeof(route)); + if (copy_from_user(&route, arg, sizeof(route))) + return -EFAULT; if ((ax25_dev = ax25_addr_ax25dev(&route.port_addr)) == NULL) return -EINVAL; ax25_rt = ax25_route_list; @@ -206,9 +204,8 @@ int ax25_rt_ioctl(unsigned int cmd, void *arg) break; case SIOCAX25OPTRT: - if ((err = verify_area(VERIFY_READ, arg, sizeof(rt_option))) != 0) - return err; - copy_from_user(&rt_option, arg, sizeof(rt_option)); + if (copy_from_user(&rt_option, arg, sizeof(rt_option))) + return -EFAULT; if ((ax25_dev = ax25_addr_ax25dev(&rt_option.port_addr)) == NULL) return -EINVAL; for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) { @@ -390,48 +387,32 @@ int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr) * dl1bke 960117: build digipeater path * dl1bke 960301: use the default route if it exists */ -void ax25_rt_build_path(ax25_cb *ax25, ax25_address *addr, struct device *dev) +ax25_route *ax25_rt_find_route(ax25_address *addr, struct device *dev) { + static ax25_route route; ax25_route *ax25_rt; - if ((ax25_rt = ax25_find_route(addr, dev)) == NULL) - return; - - if (ax25_rt->digipeat == NULL) - return; - - if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) - return; - - if ((ax25->ax25_dev = ax25_dev_ax25dev(ax25_rt->dev)) == NULL) - return; + if ((ax25_rt = ax25_find_route(addr, dev)) == NULL) { + route.next = NULL; + route.callsign = *addr; + route.dev = dev; + route.digipeat = NULL; + route.ip_mode = ' '; + return &route; + } - *ax25->digipeat = *ax25_rt->digipeat; - ax25_adjust_path(addr, ax25->digipeat); + return ax25_rt; } -struct sk_buff *ax25_dg_build_path(struct sk_buff *skb, ax25_address *addr, struct device *dev) +struct sk_buff *ax25_rt_build_path(struct sk_buff *skb, ax25_address *src, ax25_address *dest, ax25_digi *digi) { struct sk_buff *skbn; - ax25_route *ax25_rt; - ax25_digi digipeat; - ax25_address src, dest; unsigned char *bp; int len; skb_pull(skb, 1); /* skip KISS command */ - if ((ax25_rt = ax25_find_route(addr, dev)) == NULL) - return skb; - - if (ax25_rt->digipeat == NULL) - return skb; - - digipeat = *ax25_rt->digipeat; - - ax25_adjust_path(addr, &digipeat); - - len = ax25_rt->digipeat->ndigi * AX25_ADDR_LEN; + len = digi->ndigi * AX25_ADDR_LEN; if (skb_headroom(skb) < len) { if ((skbn = skb_realloc_headroom(skb, len)) == NULL) { @@ -447,30 +428,13 @@ struct sk_buff *ax25_dg_build_path(struct sk_buff *skb, ax25_address *addr, stru skb = skbn; } - memcpy(&dest, skb->data + 0, AX25_ADDR_LEN); - memcpy(&src, skb->data + 7, AX25_ADDR_LEN); - bp = skb_push(skb, len); - ax25_addr_build(bp, &src, &dest, ax25_rt->digipeat, AX25_COMMAND, AX25_MODULUS); + ax25_addr_build(bp, src, dest, digi, AX25_COMMAND, AX25_MODULUS); return skb; } -/* - * Return the IP mode of a given callsign/device pair. - */ -char ax25_ip_mode_get(ax25_address *callsign, struct device *dev) -{ - ax25_route *ax25_rt; - - for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) - if (ax25cmp(&ax25_rt->callsign, callsign) == 0 && ax25_rt->dev == dev) - return ax25_rt->ip_mode; - - return ' '; -} - #ifdef MODULE /* diff --git a/net/ax25/ax25_std_in.c b/net/ax25/ax25_std_in.c index 1efe0836612f..7b7d437e885f 100644 --- a/net/ax25/ax25_std_in.c +++ b/net/ax25/ax25_std_in.c @@ -1,5 +1,5 @@ /* - * AX.25 release 036 + * AX.25 release 037 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -34,6 +34,7 @@ * Modularisation changes. * AX.25 035 Hans(PE1AYX) Fixed interface to IP layer. * AX.25 036 Jonathan(G4KLX) Cloned from ax25_in.c. + * AX.25 037 Jonathan(G4KLX) New timer architecture. */ #include @@ -87,9 +88,9 @@ static int ax25_std_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int frame case AX25_UA: if (pf) { ax25_calculate_rtt(ax25); - ax25->t1timer = 0; - ax25->t3timer = ax25->t3; - ax25->idletimer = ax25->idle; + ax25_stop_t1timer(ax25); + ax25_start_t3timer(ax25); + ax25_start_idletimer(ax25); ax25->vs = 0; ax25->va = 0; ax25->vr = 0; @@ -107,16 +108,7 @@ static int ax25_std_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int frame case AX25_DM: if (pf) { if (ax25->modulus == AX25_MODULUS) { - ax25_clear_queues(ax25); - ax25->state = AX25_STATE_0; - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = ECONNREFUSED; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } + ax25_disconnect(ax25, ECONNREFUSED); } else { ax25->modulus = AX25_MODULUS; ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; @@ -146,30 +138,12 @@ static int ax25_std_state2_machine(ax25_cb *ax25, struct sk_buff *skb, int frame case AX25_DISC: ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25->state = AX25_STATE_0; - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = 0; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } + ax25_disconnect(ax25, 0); break; case AX25_DM: case AX25_UA: - if (pf) { - ax25->state = AX25_STATE_0; - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = 0; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } - } + if (pf) ax25_disconnect(ax25, 0); break; case AX25_I: @@ -206,10 +180,11 @@ static int ax25_std_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int frame ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW]; } ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); + ax25_stop_t1timer(ax25); + ax25_stop_t2timer(ax25); + ax25_start_t3timer(ax25); + ax25_start_idletimer(ax25); ax25->condition = 0x00; - ax25->t1timer = 0; - ax25->t3timer = ax25->t3; - ax25->idletimer = ax25->idle; ax25->vs = 0; ax25->va = 0; ax25->vr = 0; @@ -217,32 +192,12 @@ static int ax25_std_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int frame break; case AX25_DISC: - ax25_clear_queues(ax25); ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25->t3timer = 0; - ax25->state = AX25_STATE_0; - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = 0; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } + ax25_disconnect(ax25, 0); break; case AX25_DM: - ax25_clear_queues(ax25); - ax25->t3timer = 0; - ax25->state = AX25_STATE_0; - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = ECONNRESET; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } + ax25_disconnect(ax25, ECONNRESET); break; case AX25_RR: @@ -268,8 +223,8 @@ static int ax25_std_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int frame if (ax25_validate_nr(ax25, nr)) { ax25_frames_acked(ax25, nr); ax25_calculate_rtt(ax25); - ax25->t1timer = 0; - ax25->t3timer = ax25->t3; + ax25_stop_t1timer(ax25); + ax25_start_t3timer(ax25); ax25_requeue_frames(ax25); } else { ax25_std_nr_error_recovery(ax25); @@ -295,18 +250,15 @@ static int ax25_std_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int frame if (ns == ax25->vr) { ax25->vr = (ax25->vr + 1) % ax25->modulus; queued = ax25_rx_iframe(ax25, skb); - if (ax25->condition & AX25_COND_OWN_RX_BUSY) { + if (ax25->condition & AX25_COND_OWN_RX_BUSY) ax25->vr = ns; /* ax25->vr - 1 */ - if (pf) ax25_std_enquiry_response(ax25); - break; - } ax25->condition &= ~AX25_COND_REJECT; if (pf) { ax25_std_enquiry_response(ax25); } else { if (!(ax25->condition & AX25_COND_ACK_PENDING)) { - ax25->t2timer = ax25->t2; ax25->condition |= AX25_COND_ACK_PENDING; + ax25_start_t2timer(ax25); } } } else { @@ -353,10 +305,11 @@ static int ax25_std_state4_machine(ax25_cb *ax25, struct sk_buff *skb, int frame ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW]; } ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); + ax25_stop_t1timer(ax25); + ax25_stop_t2timer(ax25); + ax25_start_t3timer(ax25); + ax25_start_idletimer(ax25); ax25->condition = 0x00; - ax25->t1timer = 0; - ax25->t3timer = ax25->t3; - ax25->idletimer = ax25->idle; ax25->vs = 0; ax25->va = 0; ax25->vr = 0; @@ -366,32 +319,12 @@ static int ax25_std_state4_machine(ax25_cb *ax25, struct sk_buff *skb, int frame break; case AX25_DISC: - ax25_clear_queues(ax25); ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25->t3timer = 0; - ax25->state = AX25_STATE_0; - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = 0; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } + ax25_disconnect(ax25, 0); break; case AX25_DM: - ax25_clear_queues(ax25); - ax25->t3timer = 0; - ax25->state = AX25_STATE_0; - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = ECONNRESET; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } + ax25_disconnect(ax25, ECONNRESET); break; case AX25_RR: @@ -401,11 +334,11 @@ static int ax25_std_state4_machine(ax25_cb *ax25, struct sk_buff *skb, int frame else ax25->condition |= AX25_COND_PEER_RX_BUSY; if (type == AX25_RESPONSE && pf) { - ax25->t1timer = 0; + ax25_stop_t1timer(ax25); if (ax25_validate_nr(ax25, nr)) { ax25_frames_acked(ax25, nr); if (ax25->vs == ax25->va) { - ax25->t3timer = ax25->t3; + ax25_start_t3timer(ax25); ax25->n2count = 0; ax25->state = AX25_STATE_3; } else { @@ -430,11 +363,11 @@ static int ax25_std_state4_machine(ax25_cb *ax25, struct sk_buff *skb, int frame case AX25_REJ: ax25->condition &= ~AX25_COND_PEER_RX_BUSY; if (pf && type == AX25_RESPONSE) { - ax25->t1timer = 0; + ax25_stop_t1timer(ax25); if (ax25_validate_nr(ax25, nr)) { ax25_frames_acked(ax25, nr); if (ax25->vs == ax25->va) { - ax25->t3timer = ax25->t3; + ax25_start_t3timer(ax25); ax25->n2count = 0; ax25->state = AX25_STATE_3; } else { @@ -471,18 +404,15 @@ static int ax25_std_state4_machine(ax25_cb *ax25, struct sk_buff *skb, int frame if (ns == ax25->vr) { ax25->vr = (ax25->vr + 1) % ax25->modulus; queued = ax25_rx_iframe(ax25, skb); - if (ax25->condition & AX25_COND_OWN_RX_BUSY) { + if (ax25->condition & AX25_COND_OWN_RX_BUSY) ax25->vr = ns; /* ax25->vr - 1 */ - if (pf) ax25_std_enquiry_response(ax25); - break; - } ax25->condition &= ~AX25_COND_REJECT; if (pf) { ax25_std_enquiry_response(ax25); } else { if (!(ax25->condition & AX25_COND_ACK_PENDING)) { - ax25->t2timer = ax25->t2; ax25->condition |= AX25_COND_ACK_PENDING; + ax25_start_t2timer(ax25); } } } else { @@ -533,6 +463,8 @@ int ax25_std_frame_in(ax25_cb *ax25, struct sk_buff *skb, int type) break; } + ax25_kick(ax25); + return queued; } diff --git a/net/ax25/ax25_std_subr.c b/net/ax25/ax25_std_subr.c index 3661a63fbea8..1b1d1c8bb7e8 100644 --- a/net/ax25/ax25_std_subr.c +++ b/net/ax25/ax25_std_subr.c @@ -1,5 +1,5 @@ /* - * AX.25 release 036 + * AX.25 release 037 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -17,6 +17,7 @@ * * History * AX.25 036 Jonathan(G4KLX) Split from ax25_out.c. + * AX.25 037 Jonathan(G4KLX) New timer architecture. */ #include @@ -62,9 +63,11 @@ void ax25_std_establish_data_link(ax25_cb *ax25) else ax25_send_control(ax25, AX25_SABME, AX25_POLLON, AX25_COMMAND); - ax25->t3timer = 0; - ax25->t2timer = 0; - ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25); + ax25_calculate_t1(ax25); + ax25_stop_idletimer(ax25); + ax25_stop_t3timer(ax25); + ax25_stop_t2timer(ax25); + ax25_start_t1timer(ax25); } void ax25_std_transmit_enquiry(ax25_cb *ax25) @@ -76,7 +79,8 @@ void ax25_std_transmit_enquiry(ax25_cb *ax25) ax25->condition &= ~AX25_COND_ACK_PENDING; - ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25); + ax25_calculate_t1(ax25); + ax25_start_t1timer(ax25); } void ax25_std_enquiry_response(ax25_cb *ax25) diff --git a/net/ax25/ax25_std_timer.c b/net/ax25/ax25_std_timer.c index 9a4a34c0f097..4e97c51b8566 100644 --- a/net/ax25/ax25_std_timer.c +++ b/net/ax25/ax25_std_timer.c @@ -1,5 +1,5 @@ /* - * AX.25 release 036 + * AX.25 release 037 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -19,6 +19,7 @@ * AX.25 033 Jonathan(G4KLX) Modularisation functions. * AX.25 035 Frederic(F1OAT) Support for pseudo-digipeating. * AX.25 036 Jonathan(G4KLX) Split from ax25_timer.c. + * AX.25 037 Jonathan(G4KLX) New timer architecture. */ #include @@ -44,14 +45,14 @@ #include #include -void ax25_std_timer(ax25_cb *ax25) +void ax25_std_heartbeat_expiry(ax25_cb *ax25) { switch (ax25->state) { + case AX25_STATE_0: /* Magic here: If we listen() and a new link dies before it is accepted() it isn't 'dead' so doesn't get removed. */ if (ax25->sk == NULL || ax25->sk->destroy || (ax25->sk->state == TCP_LISTEN && ax25->sk->dead)) { - del_timer(&ax25->timer); ax25_destroy_socket(ax25); return; } @@ -71,78 +72,57 @@ void ax25_std_timer(ax25_cb *ax25) break; } } - /* - * Check for frames to transmit. - */ - ax25_kick(ax25); - break; - - default: - break; } - if (ax25->t2timer > 0 && --ax25->t2timer == 0) { - if (ax25->state == AX25_STATE_3 || ax25->state == AX25_STATE_4) { - if (ax25->condition & AX25_COND_ACK_PENDING) { - ax25->condition &= ~AX25_COND_ACK_PENDING; - ax25_std_timeout_response(ax25); - } - } - } + ax25_start_heartbeat(ax25); +} - if (ax25->t3timer > 0 && --ax25->t3timer == 0) { - if (ax25->state == AX25_STATE_3) { - ax25->n2count = 0; - ax25_std_transmit_enquiry(ax25); - ax25->state = AX25_STATE_4; - } - ax25->t3timer = ax25->t3; +void ax25_std_t2timer_expiry(ax25_cb *ax25) +{ + if (ax25->condition & AX25_COND_ACK_PENDING) { + ax25->condition &= ~AX25_COND_ACK_PENDING; + ax25_std_timeout_response(ax25); } +} - if (ax25->idletimer > 0 && --ax25->idletimer == 0) { - /* dl1bke 960228: close the connection when IDLE expires */ - /* similar to DAMA T3 timeout but with */ - /* a "clean" disconnect of the connection */ - - ax25_clear_queues(ax25); - - ax25->n2count = 0; - ax25->t3timer = 0; - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - ax25->state = AX25_STATE_2; - ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25); - - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = 0; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - ax25->sk->destroy = 1; - } - } +void ax25_std_t3timer_expiry(ax25_cb *ax25) +{ + ax25->n2count = 0; + ax25_std_transmit_enquiry(ax25); + ax25->state = AX25_STATE_4; +} - if (ax25->t1timer == 0 || --ax25->t1timer > 0) { - ax25_set_timer(ax25); - return; +void ax25_std_idletimer_expiry(ax25_cb *ax25) +{ + ax25_clear_queues(ax25); + + ax25->n2count = 0; + ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); + ax25->state = AX25_STATE_2; + + ax25_calculate_t1(ax25); + ax25_start_t1timer(ax25); + ax25_stop_t2timer(ax25); + ax25_stop_t3timer(ax25); + + if (ax25->sk != NULL) { + ax25->sk->state = TCP_CLOSE; + ax25->sk->err = 0; + ax25->sk->shutdown |= SEND_SHUTDOWN; + if (!ax25->sk->dead) + ax25->sk->state_change(ax25->sk); + ax25->sk->dead = 1; } +} +void ax25_std_t1timer_expiry(ax25_cb *ax25) +{ switch (ax25->state) { case AX25_STATE_1: if (ax25->n2count == ax25->n2) { if (ax25->modulus == AX25_MODULUS) { - ax25_link_failed(&ax25->dest_addr, ax25->ax25_dev->dev); - ax25_clear_queues(ax25); - ax25->state = AX25_STATE_0; - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = ETIMEDOUT; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } + ax25_disconnect(ax25, ETIMEDOUT); + return; } else { ax25->modulus = AX25_MODULUS; ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; @@ -160,19 +140,9 @@ void ax25_std_timer(ax25_cb *ax25) case AX25_STATE_2: if (ax25->n2count == ax25->n2) { - ax25_link_failed(&ax25->dest_addr, ax25->ax25_dev->dev); - ax25_clear_queues(ax25); - ax25->state = AX25_STATE_0; ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = ETIMEDOUT; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } + ax25_disconnect(ax25, ETIMEDOUT); + return; } else { ax25->n2count++; ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); @@ -187,19 +157,9 @@ void ax25_std_timer(ax25_cb *ax25) case AX25_STATE_4: if (ax25->n2count == ax25->n2) { - ax25_link_failed(&ax25->dest_addr, ax25->ax25_dev->dev); - ax25_clear_queues(ax25); ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE); - ax25->state = AX25_STATE_0; - if (ax25->sk != NULL) { - SOCK_DEBUG(ax25->sk, "AX.25 link Failure\n"); - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = ETIMEDOUT; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } + ax25_disconnect(ax25, ETIMEDOUT); + return; } else { ax25->n2count++; ax25_std_transmit_enquiry(ax25); @@ -207,9 +167,8 @@ void ax25_std_timer(ax25_cb *ax25) break; } - ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25); - - ax25_set_timer(ax25); + ax25_calculate_t1(ax25); + ax25_start_t1timer(ax25); } #endif diff --git a/net/ax25/ax25_subr.c b/net/ax25/ax25_subr.c index ba2c72297e51..39dfd7d422f5 100644 --- a/net/ax25/ax25_subr.c +++ b/net/ax25/ax25_subr.c @@ -1,5 +1,5 @@ /* - * AX.25 release 036 + * AX.25 release 037 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -30,6 +30,7 @@ * AX.25 032 Joerg(DL1BKE) Added ax25_queue_length to count the number of * enqueued buffers of a socket.. * AX.25 035 Frederic(F1OAT) Support for pseudo-digipeating. + * AX.25 037 Jonathan(G4KLX) New timer architecture. */ #include @@ -259,7 +260,7 @@ void ax25_return_dm(struct device *dev, ax25_address *src, ax25_address *dest, a /* * Exponential backoff for AX.25 */ -unsigned short ax25_calculate_t1(ax25_cb *ax25) +void ax25_calculate_t1(ax25_cb *ax25) { int n, t = 2; @@ -278,7 +279,7 @@ unsigned short ax25_calculate_t1(ax25_cb *ax25) break; } - return t * ax25->rtt; + ax25->t1 = t * ax25->rtt; } /* @@ -289,8 +290,8 @@ void ax25_calculate_rtt(ax25_cb *ax25) if (ax25->backoff == 0) return; - if (ax25->t1timer > 0 && ax25->n2count == 0) - ax25->rtt = (9 * ax25->rtt + ax25->t1 - ax25->t1timer) / 10; + if (ax25_t1timer_running(ax25) && ax25->n2count == 0) + ax25->rtt = (9 * ax25->rtt + ax25->t1 - ax25_display_timer(&ax25->t1timer)) / 10; if (ax25->rtt < AX25_T1CLAMPLO) ax25->rtt = AX25_T1CLAMPLO; @@ -299,4 +300,27 @@ void ax25_calculate_rtt(ax25_cb *ax25) ax25->rtt = AX25_T1CLAMPHI; } +void ax25_disconnect(ax25_cb *ax25, int reason) +{ + ax25_clear_queues(ax25); + + ax25_stop_t1timer(ax25); + ax25_stop_t2timer(ax25); + ax25_stop_t3timer(ax25); + ax25_stop_idletimer(ax25); + + ax25->state = AX25_STATE_0; + + ax25_link_failed(ax25, reason); + + if (ax25->sk != NULL) { + ax25->sk->state = TCP_CLOSE; + ax25->sk->err = reason; + ax25->sk->shutdown |= SEND_SHUTDOWN; + if (!ax25->sk->dead) + ax25->sk->state_change(ax25->sk); + ax25->sk->dead = 1; + } +} + #endif diff --git a/net/ax25/ax25_timer.c b/net/ax25/ax25_timer.c index c0a54da116f0..8a384b58ba32 100644 --- a/net/ax25/ax25_timer.c +++ b/net/ax25/ax25_timer.c @@ -1,5 +1,5 @@ /* - * AX.25 release 036 + * AX.25 release 037 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -21,6 +21,7 @@ * AX.25 036 Jonathan(G4KLX) Split Standard and DAMA code into seperate files. * Joerg(DL1BKE) Fixed DAMA Slave. We are *required* to start with * standard AX.25 mode. + * AX.25 037 Jonathan(G4KLX) New timer architecture. */ #include @@ -46,48 +47,205 @@ #include #include -static void ax25_timer(unsigned long); +static void ax25_heartbeat_expiry(unsigned long); +static void ax25_t1timer_expiry(unsigned long); +static void ax25_t2timer_expiry(unsigned long); +static void ax25_t3timer_expiry(unsigned long); +static void ax25_idletimer_expiry(unsigned long); -/* - * Linux set timer - */ -void ax25_set_timer(ax25_cb *ax25) +void ax25_start_heartbeat(ax25_cb *ax25) { - unsigned long flags; - - save_flags(flags); cli(); del_timer(&ax25->timer); - restore_flags(flags); ax25->timer.data = (unsigned long)ax25; - ax25->timer.function = &ax25_timer; - ax25->timer.expires = jiffies + (HZ / 10); + ax25->timer.function = &ax25_heartbeat_expiry; + ax25->timer.expires = jiffies + 5 * HZ; add_timer(&ax25->timer); } -/* - * AX.25 TIMER - * - * This routine is called every 100ms. Decrement timer by this - * amount - if expired then process the event. - */ -static void ax25_timer(unsigned long param) +void ax25_start_t1timer(ax25_cb *ax25) +{ + del_timer(&ax25->t1timer); + + ax25->t1timer.data = (unsigned long)ax25; + ax25->t1timer.function = &ax25_t1timer_expiry; + ax25->t1timer.expires = jiffies + ax25->t1; + + add_timer(&ax25->t1timer); +} + +void ax25_start_t2timer(ax25_cb *ax25) +{ + del_timer(&ax25->t2timer); + + ax25->t2timer.data = (unsigned long)ax25; + ax25->t2timer.function = &ax25_t2timer_expiry; + ax25->t2timer.expires = jiffies + ax25->t2; + + add_timer(&ax25->t2timer); +} + +void ax25_start_t3timer(ax25_cb *ax25) +{ + del_timer(&ax25->t3timer); + + if (ax25->t3 > 0) { + ax25->t3timer.data = (unsigned long)ax25; + ax25->t3timer.function = &ax25_t3timer_expiry; + ax25->t3timer.expires = jiffies + ax25->t3; + + add_timer(&ax25->t3timer); + } +} + +void ax25_start_idletimer(ax25_cb *ax25) +{ + del_timer(&ax25->idletimer); + + if (ax25->idle > 0) { + ax25->idletimer.data = (unsigned long)ax25; + ax25->idletimer.function = &ax25_idletimer_expiry; + ax25->idletimer.expires = jiffies + ax25->idle; + + add_timer(&ax25->idletimer); + } +} + +void ax25_stop_heartbeat(ax25_cb *ax25) +{ + del_timer(&ax25->timer); +} + +void ax25_stop_t1timer(ax25_cb *ax25) +{ + del_timer(&ax25->t1timer); +} + +void ax25_stop_t2timer(ax25_cb *ax25) +{ + del_timer(&ax25->t2timer); +} + +void ax25_stop_t3timer(ax25_cb *ax25) +{ + del_timer(&ax25->t3timer); +} + +void ax25_stop_idletimer(ax25_cb *ax25) +{ + del_timer(&ax25->idletimer); +} + +int ax25_t1timer_running(ax25_cb *ax25) +{ + return (ax25->t1timer.prev != NULL || ax25->t1timer.next != NULL); +} + +unsigned long ax25_display_timer(struct timer_list *timer) +{ + if (timer->prev == NULL && timer->next == NULL) + return 0; + + return timer->expires - jiffies; +} + +static void ax25_heartbeat_expiry(unsigned long param) +{ + ax25_cb *ax25 = (ax25_cb *)param; + + switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { + case AX25_PROTO_STD_SIMPLEX: + case AX25_PROTO_STD_DUPLEX: + ax25_std_heartbeat_expiry(ax25); + break; + +#ifdef CONFIG_AX25_DAMA_SLAVE + case AX25_PROTO_DAMA_SLAVE: + if (ax25->ax25_dev->dama.slave) + ax25_ds_heartbeat_expiry(ax25); + else + ax25_std_heartbeat_expiry(ax25); + break; +#endif + } +} + +static void ax25_t1timer_expiry(unsigned long param) +{ + ax25_cb *ax25 = (ax25_cb *)param; + + switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { + case AX25_PROTO_STD_SIMPLEX: + case AX25_PROTO_STD_DUPLEX: + ax25_std_t1timer_expiry(ax25); + break; + +#ifdef CONFIG_AX25_DAMA_SLAVE + case AX25_PROTO_DAMA_SLAVE: + if (!ax25->ax25_dev->dama.slave) + ax25_std_t1timer_expiry(ax25); + break; +#endif + } +} + +static void ax25_t2timer_expiry(unsigned long param) +{ + ax25_cb *ax25 = (ax25_cb *)param; + + switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { + case AX25_PROTO_STD_SIMPLEX: + case AX25_PROTO_STD_DUPLEX: + ax25_std_t2timer_expiry(ax25); + break; + +#ifdef CONFIG_AX25_DAMA_SLAVE + case AX25_PROTO_DAMA_SLAVE: + if (!ax25->ax25_dev->dama.slave) + ax25_std_t2timer_expiry(ax25); + break; +#endif + } +} + +static void ax25_t3timer_expiry(unsigned long param) +{ + ax25_cb *ax25 = (ax25_cb *)param; + + switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { + case AX25_PROTO_STD_SIMPLEX: + case AX25_PROTO_STD_DUPLEX: + ax25_std_t3timer_expiry(ax25); + break; + +#ifdef CONFIG_AX25_DAMA_SLAVE + case AX25_PROTO_DAMA_SLAVE: + if (ax25->ax25_dev->dama.slave) + ax25_ds_t3timer_expiry(ax25); + else + ax25_std_t3timer_expiry(ax25); + break; +#endif + } +} + +static void ax25_idletimer_expiry(unsigned long param) { ax25_cb *ax25 = (ax25_cb *)param; switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { case AX25_PROTO_STD_SIMPLEX: case AX25_PROTO_STD_DUPLEX: - ax25_std_timer(ax25); + ax25_std_idletimer_expiry(ax25); break; #ifdef CONFIG_AX25_DAMA_SLAVE case AX25_PROTO_DAMA_SLAVE: if (ax25->ax25_dev->dama.slave) - ax25_ds_timer(ax25); + ax25_ds_idletimer_expiry(ax25); else - ax25_std_timer(ax25); + ax25_std_idletimer_expiry(ax25); break; #endif } diff --git a/net/ax25/ax25_uid.c b/net/ax25/ax25_uid.c index c5113fce8d4f..5f33147cf37c 100644 --- a/net/ax25/ax25_uid.c +++ b/net/ax25/ax25_uid.c @@ -1,5 +1,5 @@ /* - * AX.25 release 036 + * AX.25 release 037 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * diff --git a/net/ax25/sysctl_net_ax25.c b/net/ax25/sysctl_net_ax25.c index 1cf2bab65d52..000203aafe87 100644 --- a/net/ax25/sysctl_net_ax25.c +++ b/net/ax25/sysctl_net_ax25.c @@ -17,14 +17,14 @@ static int min_backoff[] = {0}, max_backoff[] = {2}; static int min_conmode[] = {0}, max_conmode[] = {2}; static int min_window[] = {1}, max_window[] = {7}; static int min_ewindow[] = {1}, max_ewindow[] = {63}; -static int min_t1[] = {1}, max_t1[] = {30 * AX25_SLOWHZ}; -static int min_t2[] = {1}, max_t2[] = {20 * AX25_SLOWHZ}; -static int min_t3[] = {0}, max_t3[] = {3600 * AX25_SLOWHZ}; -static int min_idle[] = {0}, max_idle[] = {65535 * AX25_SLOWHZ}; +static int min_t1[] = {1}, max_t1[] = {30 * HZ}; +static int min_t2[] = {1}, max_t2[] = {20 * HZ}; +static int min_t3[] = {0}, max_t3[] = {3600 * HZ}; +static int min_idle[] = {0}, max_idle[] = {65535 * HZ}; static int min_n2[] = {1}, max_n2[] = {31}; static int min_paclen[] = {1}, max_paclen[] = {512}; static int min_proto[] = {0}, max_proto[] = {3}; -static int min_ds_timeout[] = {0}, max_ds_timeout[] = {65535 * AX25_SLOWHZ}; +static int min_ds_timeout[] = {0}, max_ds_timeout[] = {65535 * HZ}; static struct ctl_table_header *ax25_table_header; diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c index c912e8b2ea83..b684fba334f2 100644 --- a/net/core/sysctl_net_core.c +++ b/net/core/sysctl_net_core.c @@ -7,6 +7,9 @@ #include #include +#include + +#ifdef CONFIG_SYSCTL extern __u32 sysctl_wmem_max; extern __u32 sysctl_rmem_max; @@ -33,3 +36,4 @@ ctl_table core_table[] = { &proc_dointvec_jiffies}, { 0 } }; +#endif diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 8115e819ae2c..d3b0c26c9403 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_ipv4.c,v 1.49 1997/06/09 13:27:35 freitag Exp $ + * Version: $Id: tcp_ipv4.c,v 1.51 1997/07/04 23:35:02 freitag Exp $ * * IPv4 specific functions * @@ -701,6 +701,12 @@ void tcp_v4_err(struct sk_buff *skb, unsigned char *dp) } /* FIXME: What about the IP layer options size here? */ + /* FIXME: add a timeout here, to cope with broken devices that + drop all DF=1 packets. Do some more sanity checking + here to prevent DOS attacks? + This code should kick the tcp_output routine to + retransmit a packet immediately because we know that + the last packet has been dropped. -AK */ if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) { if (sk->ip_pmtudisc != IP_PMTUDISC_DONT) { int new_mtu = sk->dst_cache->pmtu - sizeof(struct iphdr) - tp->tcp_header_len; diff --git a/net/lapb/lapb_iface.c b/net/lapb/lapb_iface.c index 2be6e565e71a..77382683c667 100644 --- a/net/lapb/lapb_iface.c +++ b/net/lapb/lapb_iface.c @@ -1,8 +1,5 @@ /* - * LAPB release 001 - * - * This is ALPHA test software. This code may break your machine, randomly fail to work with new - * releases, misbehave and/or generally screw up. It might even work. + * LAPB release 002 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -14,6 +11,7 @@ * * History * LAPB 001 Jonathan Naylor Started Coding + * LAPB 002 Jonathan Naylor New timer architecture. */ #include @@ -63,8 +61,7 @@ static void lapb_remove_cb(lapb_cb *lapb) lapb_cb *s; unsigned long flags; - save_flags(flags); - cli(); + save_flags(flags); cli(); if ((s = lapb_list) == lapb) { lapb_list = s->next; @@ -92,8 +89,7 @@ static void lapb_insert_cb(lapb_cb *lapb) { unsigned long flags; - save_flags(flags); - cli(); + save_flags(flags); cli(); lapb->next = lapb_list; lapb_list = lapb; @@ -130,11 +126,11 @@ static lapb_cb *lapb_create_cb(void) memset(lapb, 0x00, sizeof(*lapb)); - skb_queue_head_init(&lapb->input_queue); skb_queue_head_init(&lapb->write_queue); skb_queue_head_init(&lapb->ack_queue); - init_timer(&lapb->timer); + init_timer(&lapb->t1timer); + init_timer(&lapb->t2timer); lapb->t1 = LAPB_DEFAULT_T1; lapb->t2 = LAPB_DEFAULT_T2; @@ -161,9 +157,7 @@ int lapb_register(void *token, struct lapb_register_struct *callbacks) lapb_insert_cb(lapb); - lapb->t1timer = lapb->t1; - - lapb_set_timer(lapb); + lapb_start_t1timer(lapb); return LAPB_OK; } @@ -175,7 +169,8 @@ int lapb_unregister(void *token) if ((lapb = lapb_tokentostruct(token)) == NULL) return LAPB_BADTOKEN; - del_timer(&lapb->timer); + lapb_stop_t1timer(lapb); + lapb_stop_t2timer(lapb); lapb_clear_queues(lapb); @@ -193,16 +188,24 @@ int lapb_getparms(void *token, struct lapb_parms_struct *parms) if ((lapb = lapb_tokentostruct(token)) == NULL) return LAPB_BADTOKEN; - parms->t1 = lapb->t1; - parms->t1timer = lapb->t1timer; - parms->t2 = lapb->t2; - parms->t2timer = lapb->t2timer; + parms->t1 = lapb->t1 / HZ; + parms->t2 = lapb->t2 / HZ; parms->n2 = lapb->n2; parms->n2count = lapb->n2count; parms->state = lapb->state; parms->window = lapb->window; parms->mode = lapb->mode; + if (lapb->t1timer.prev == NULL && lapb->t1timer.next == NULL) + parms->t1timer = 0; + else + parms->t1timer = (lapb->t1timer.expires - jiffies) / HZ; + + if (lapb->t2timer.prev == NULL && lapb->t2timer.next == NULL) + parms->t2timer = 0; + else + parms->t2timer = (lapb->t2timer.expires - jiffies) / HZ; + return LAPB_OK; } @@ -235,8 +238,8 @@ int lapb_setparms(void *token, struct lapb_parms_struct *parms) lapb->window = parms->window; } - lapb->t1 = parms->t1; - lapb->t2 = parms->t2; + lapb->t1 = parms->t1 * HZ; + lapb->t2 = parms->t2 * HZ; lapb->n2 = parms->n2; return LAPB_OK; @@ -287,8 +290,8 @@ int lapb_disconnect_request(void *token) printk(KERN_DEBUG "lapb: (%p) S1 -> S0\n", lapb->token); #endif lapb_send_control(lapb, LAPB_DISC, LAPB_POLLON, LAPB_COMMAND); - lapb->state = LAPB_STATE_0; - lapb->t1timer = lapb->t1; + lapb->state = LAPB_STATE_0; + lapb_start_t1timer(lapb); return LAPB_NOTCONNECTED; case LAPB_STATE_2: @@ -298,9 +301,9 @@ int lapb_disconnect_request(void *token) lapb_clear_queues(lapb); lapb->n2count = 0; lapb_send_control(lapb, LAPB_DISC, LAPB_POLLON, LAPB_COMMAND); - lapb->t1timer = lapb->t1; - lapb->t2timer = 0; - lapb->state = LAPB_STATE_2; + lapb_start_t1timer(lapb); + lapb_stop_t2timer(lapb); + lapb->state = LAPB_STATE_2; #if LAPB_DEBUG > 1 printk(KERN_DEBUG "lapb: (%p) S3 DISC(1)\n", lapb->token); @@ -336,7 +339,7 @@ int lapb_data_received(void *token, struct sk_buff *skb) if ((lapb = lapb_tokentostruct(token)) == NULL) return LAPB_BADTOKEN; - skb_queue_tail(&lapb->input_queue, skb); + lapb_data_input(lapb, skb); return LAPB_OK; } diff --git a/net/lapb/lapb_in.c b/net/lapb/lapb_in.c index 9aa36711506e..126b9367351e 100644 --- a/net/lapb/lapb_in.c +++ b/net/lapb/lapb_in.c @@ -1,8 +1,5 @@ /* - * LAPB release 001 - * - * This is ALPHA test software. This code may break your machine, randomly fail to work with new - * releases, misbehave and/or generally screw up. It might even work. + * LAPB release 002 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -14,6 +11,7 @@ * * History * LAPB 001 Jonathan Naulor Started Coding + * LAPB 002 Jonathan Naylor New timer architecture. */ #include @@ -63,10 +61,10 @@ static void lapb_state0_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ printk(KERN_DEBUG "lapb: (%p) S0 -> S3\n", lapb->token); #endif lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE); + lapb_stop_t1timer(lapb); + lapb_stop_t2timer(lapb); lapb->state = LAPB_STATE_3; lapb->condition = 0x00; - lapb->t1timer = 0; - lapb->t2timer = 0; lapb->n2count = 0; lapb->vs = 0; lapb->vr = 0; @@ -87,10 +85,10 @@ static void lapb_state0_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ printk(KERN_DEBUG "lapb: (%p) S0 -> S3\n", lapb->token); #endif lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE); + lapb_stop_t1timer(lapb); + lapb_stop_t2timer(lapb); lapb->state = LAPB_STATE_3; lapb->condition = 0x00; - lapb->t1timer = 0; - lapb->t2timer = 0; lapb->n2count = 0; lapb->vs = 0; lapb->vr = 0; @@ -176,10 +174,10 @@ static void lapb_state1_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ #if LAPB_DEBUG > 0 printk(KERN_DEBUG "lapb: (%p) S1 -> S3\n", lapb->token); #endif + lapb_stop_t1timer(lapb); + lapb_stop_t2timer(lapb); lapb->state = LAPB_STATE_3; lapb->condition = 0x00; - lapb->t1timer = 0; - lapb->t2timer = 0; lapb->n2count = 0; lapb->vs = 0; lapb->vr = 0; @@ -197,9 +195,9 @@ static void lapb_state1_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ printk(KERN_DEBUG "lapb: (%p) S1 -> S0\n", lapb->token); #endif lapb_clear_queues(lapb); - lapb->state = LAPB_STATE_0; - lapb->t1timer = lapb->t1; - lapb->t2timer = 0; + lapb->state = LAPB_STATE_0; + lapb_start_t1timer(lapb); + lapb_stop_t2timer(lapb); lapb_disconnect_indication(lapb, LAPB_REFUSED); } break; @@ -243,9 +241,9 @@ static void lapb_state2_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ #if LAPB_DEBUG > 0 printk(KERN_DEBUG "lapb: (%p) S2 -> S0\n", lapb->token); #endif - lapb->state = LAPB_STATE_0; - lapb->t1timer = lapb->t1; - lapb->t2timer = 0; + lapb->state = LAPB_STATE_0; + lapb_start_t1timer(lapb); + lapb_stop_t2timer(lapb); lapb_disconnect_confirmation(lapb, LAPB_OK); } break; @@ -258,9 +256,9 @@ static void lapb_state2_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ #if LAPB_DEBUG > 0 printk(KERN_DEBUG "lapb: (%p) S2 -> S0\n", lapb->token); #endif - lapb->state = LAPB_STATE_0; - lapb->t1timer = lapb->t1; - lapb->t2timer = 0; + lapb->state = LAPB_STATE_0; + lapb_start_t1timer(lapb); + lapb_stop_t2timer(lapb); lapb_disconnect_confirmation(lapb, LAPB_NOTCONNECTED); } break; @@ -309,9 +307,9 @@ static void lapb_state3_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ printk(KERN_DEBUG "lapb: (%p) S3 TX UA(%d)\n", lapb->token, frame->pf); #endif lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE); + lapb_stop_t1timer(lapb); + lapb_stop_t2timer(lapb); lapb->condition = 0x00; - lapb->t1timer = 0; - lapb->t2timer = 0; lapb->n2count = 0; lapb->vs = 0; lapb->vr = 0; @@ -329,9 +327,9 @@ static void lapb_state3_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ printk(KERN_DEBUG "lapb: (%p) S3 TX UA(%d)\n", lapb->token, frame->pf); #endif lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE); + lapb_stop_t1timer(lapb); + lapb_stop_t2timer(lapb); lapb->condition = 0x00; - lapb->t1timer = 0; - lapb->t2timer = 0; lapb->n2count = 0; lapb->vs = 0; lapb->vr = 0; @@ -354,9 +352,9 @@ static void lapb_state3_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ #endif lapb_clear_queues(lapb); lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE); - lapb->state = LAPB_STATE_0; - lapb->t1timer = lapb->t1; - lapb->t2timer = 0; + lapb_start_t1timer(lapb); + lapb_stop_t2timer(lapb); + lapb->state = LAPB_STATE_0; lapb_disconnect_indication(lapb, LAPB_OK); break; @@ -368,9 +366,9 @@ static void lapb_state3_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ printk(KERN_DEBUG "lapb: (%p) S3 -> S0\n", lapb->token); #endif lapb_clear_queues(lapb); - lapb->state = LAPB_STATE_0; - lapb->t1timer = lapb->t1; - lapb->t2timer = 0; + lapb->state = LAPB_STATE_0; + lapb_start_t1timer(lapb); + lapb_stop_t2timer(lapb); lapb_disconnect_indication(lapb, LAPB_NOTCONNECTED); break; @@ -389,10 +387,10 @@ static void lapb_state3_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ #if LAPB_DEBUG > 0 printk(KERN_DEBUG "lapb: (%p) S3 -> S4\n", lapb->token); #endif - lapb->state = LAPB_STATE_4; - lapb->t1timer = lapb->t1; - lapb->t2timer = 0; - lapb->n2count = 0; + lapb_start_t1timer(lapb); + lapb_stop_t2timer(lapb); + lapb->state = LAPB_STATE_4; + lapb->n2count = 0; } break; @@ -411,10 +409,10 @@ static void lapb_state3_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ #if LAPB_DEBUG > 0 printk(KERN_DEBUG "lapb: (%p) S3 -> S4\n", lapb->token); #endif - lapb->state = LAPB_STATE_4; - lapb->t1timer = lapb->t1; - lapb->t2timer = 0; - lapb->n2count = 0; + lapb_start_t1timer(lapb); + lapb_stop_t2timer(lapb); + lapb->state = LAPB_STATE_4; + lapb->n2count = 0; } break; @@ -426,7 +424,7 @@ static void lapb_state3_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ lapb_check_need_response(lapb, frame->cr, frame->pf); if (lapb_validate_nr(lapb, frame->nr)) { lapb_frames_acked(lapb, frame->nr); - lapb->t1timer = 0; + lapb_stop_t1timer(lapb); lapb->n2count = 0; lapb_requeue_frames(lapb); } else { @@ -436,10 +434,10 @@ static void lapb_state3_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ #if LAPB_DEBUG > 0 printk(KERN_DEBUG "lapb: (%p) S3 -> S4\n", lapb->token); #endif - lapb->state = LAPB_STATE_4; - lapb->t1timer = lapb->t1; - lapb->t2timer = 0; - lapb->n2count = 0; + lapb_start_t1timer(lapb); + lapb_stop_t2timer(lapb); + lapb->state = LAPB_STATE_4; + lapb->n2count = 0; } break; @@ -454,10 +452,10 @@ static void lapb_state3_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ #if LAPB_DEBUG > 0 printk(KERN_DEBUG "lapb: (%p) S3 -> S4\n", lapb->token); #endif - lapb->state = LAPB_STATE_4; - lapb->t1timer = lapb->t1; - lapb->t2timer = 0; - lapb->n2count = 0; + lapb_start_t1timer(lapb); + lapb_stop_t2timer(lapb); + lapb->state = LAPB_STATE_4; + lapb->n2count = 0; break; } if (lapb->condition & LAPB_PEER_RX_BUSY_CONDITION) { @@ -473,8 +471,8 @@ static void lapb_state3_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ lapb_enquiry_response(lapb); } else { if (!(lapb->condition & LAPB_ACK_PENDING_CONDITION)) { - lapb->t2timer = lapb->t2; lapb->condition |= LAPB_ACK_PENDING_CONDITION; + lapb_start_t2timer(lapb); } } } else { @@ -514,10 +512,10 @@ static void lapb_state3_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ #if LAPB_DEBUG > 0 printk(KERN_DEBUG "lapb: (%p) S3 -> S4\n", lapb->token); #endif - lapb->state = LAPB_STATE_4; - lapb->t1timer = lapb->t1; - lapb->t2timer = 0; - lapb->n2count = 0; + lapb_start_t1timer(lapb); + lapb_stop_t2timer(lapb); + lapb->state = LAPB_STATE_4; + lapb->n2count = 0; break; default: @@ -552,10 +550,10 @@ static void lapb_state4_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ printk(KERN_DEBUG "lapb: (%p) S4 -> S3\n", lapb->token); #endif lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE); + lapb_stop_t1timer(lapb); + lapb_stop_t2timer(lapb); lapb->state = LAPB_STATE_3; lapb->condition = 0x00; - lapb->t1timer = 0; - lapb->t2timer = 0; lapb->n2count = 0; lapb->vs = 0; lapb->vr = 0; @@ -576,10 +574,10 @@ static void lapb_state4_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ printk(KERN_DEBUG "lapb: (%p) S4 -> S3\n", lapb->token); #endif lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE); + lapb_stop_t1timer(lapb); + lapb_stop_t2timer(lapb); lapb->state = LAPB_STATE_3; lapb->condition = 0x00; - lapb->t1timer = 0; - lapb->t2timer = 0; lapb->n2count = 0; lapb->vs = 0; lapb->vr = 0; @@ -626,6 +624,8 @@ void lapb_data_input(lapb_cb *lapb, struct sk_buff *skb) lapb_state4_machine(lapb, skb, &frame); break; } + + lapb_kick(lapb); } #endif diff --git a/net/lapb/lapb_out.c b/net/lapb/lapb_out.c index 1256e3a3c109..9e1cdf4758be 100644 --- a/net/lapb/lapb_out.c +++ b/net/lapb/lapb_out.c @@ -1,8 +1,5 @@ /* - * LAPB release 001 - * - * This is ALPHA test software. This code may break your machine, randomly fail to work with new - * releases, misbehave and/or generally screw up. It might even work. + * LAPB release 002 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -14,6 +11,7 @@ * * History * LAPB 001 Jonathan Naylor Started Coding + * LAPB 002 Jonathan Naylor New timer architecture. */ #include @@ -77,8 +75,6 @@ void lapb_kick(lapb_cb *lapb) struct sk_buff *skb, *skbn; unsigned short modulus, start, end; - del_timer(&lapb->timer); - modulus = (lapb->mode & LAPB_EXTENDED) ? LAPB_EMODULUS : LAPB_SMODULUS; start = (skb_peek(&lapb->ack_queue) == NULL) ? lapb->va : lapb->vs; @@ -120,11 +116,9 @@ void lapb_kick(lapb_cb *lapb) lapb->condition &= ~LAPB_ACK_PENDING_CONDITION; - if (lapb->t1timer == 0) - lapb->t1timer = lapb->t1; + if (!lapb_t1timer_running(lapb)) + lapb_start_t1timer(lapb); } - - lapb_set_timer(lapb); } void lapb_transmit_buffer(lapb_cb *lapb, struct sk_buff *skb, int type) @@ -184,8 +178,8 @@ void lapb_establish_data_link(lapb_cb *lapb) lapb_send_control(lapb, LAPB_SABM, LAPB_POLLON, LAPB_COMMAND); } - lapb->t2timer = 0; - lapb->t1timer = lapb->t1; + lapb_start_t1timer(lapb); + lapb_stop_t2timer(lapb); } void lapb_enquiry_response(lapb_cb *lapb) @@ -214,12 +208,12 @@ void lapb_check_iframes_acked(lapb_cb *lapb, unsigned short nr) { if (lapb->vs == nr) { lapb_frames_acked(lapb, nr); - lapb->t1timer = 0; + lapb_stop_t1timer(lapb); lapb->n2count = 0; } else { if (lapb->va != nr) { lapb_frames_acked(lapb, nr); - lapb->t1timer = lapb->t1; + lapb_start_t1timer(lapb); } } } diff --git a/net/lapb/lapb_subr.c b/net/lapb/lapb_subr.c index 626e08927475..3f7f0a84e904 100644 --- a/net/lapb/lapb_subr.c +++ b/net/lapb/lapb_subr.c @@ -1,8 +1,5 @@ /* - * LAPB release 001 - * - * This is ALPHA test software. This code may break your machine, randomly fail to work with new - * releases, misbehave and/or generally screw up. It might even work. + * LAPB release 002 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -45,9 +42,6 @@ void lapb_clear_queues(lapb_cb *lapb) { struct sk_buff *skb; - while ((skb = skb_dequeue(&lapb->input_queue)) != NULL) - kfree_skb(skb, FREE_READ); - while ((skb = skb_dequeue(&lapb->write_queue)) != NULL) kfree_skb(skb, FREE_WRITE); diff --git a/net/lapb/lapb_timer.c b/net/lapb/lapb_timer.c index 2679ff514dcf..757fd10d9409 100644 --- a/net/lapb/lapb_timer.c +++ b/net/lapb/lapb_timer.c @@ -1,8 +1,5 @@ /* - * LAPB release 001 - * - * This is ALPHA test software. This code may break your machine, randomly fail to work with new - * releases, misbehave and/or generally screw up. It might even work. + * LAPB release 002 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -14,6 +11,7 @@ * * History * LAPB 001 Jonathan Naylor Started Coding + * LAPB 002 Jonathan Naylor New timer architecture. */ #include @@ -38,72 +36,60 @@ #include #include -static void lapb_timer(unsigned long); +static void lapb_t1timer_expiry(unsigned long); +static void lapb_t2timer_expiry(unsigned long); -/* - * Linux set timer - */ -void lapb_set_timer(lapb_cb *lapb) +void lapb_start_t1timer(lapb_cb *lapb) { - unsigned long flags; + del_timer(&lapb->t1timer); + + lapb->t1timer.data = (unsigned long)lapb; + lapb->t1timer.function = &lapb_t1timer_expiry; + lapb->t1timer.expires = jiffies + lapb->t1; - save_flags(flags); cli(); - del_timer(&lapb->timer); - restore_flags(flags); + add_timer(&lapb->t1timer); +} + +void lapb_start_t2timer(lapb_cb *lapb) +{ + del_timer(&lapb->t2timer); - lapb->timer.data = (unsigned long)lapb; - lapb->timer.function = &lapb_timer; - lapb->timer.expires = jiffies + (HZ / 10); + lapb->t2timer.data = (unsigned long)lapb; + lapb->t2timer.function = &lapb_t2timer_expiry; + lapb->t2timer.expires = jiffies + lapb->t2; - add_timer(&lapb->timer); + add_timer(&lapb->t2timer); } -/* - * LAPB TIMER - * - * This routine is called every 100ms. Decrement timer by this - * amount - if expired then process the event. - */ -static void lapb_timer(unsigned long param) +void lapb_stop_t1timer(lapb_cb *lapb) +{ + del_timer(&lapb->t1timer); +} + +void lapb_stop_t2timer(lapb_cb *lapb) +{ + del_timer(&lapb->t2timer); +} + +int lapb_t1timer_running(lapb_cb *lapb) +{ + return (lapb->t1timer.prev != NULL || lapb->t1timer.next != NULL); +} + +static void lapb_t2timer_expiry(unsigned long param) { lapb_cb *lapb = (lapb_cb *)param; - struct sk_buff *skb; - - /* - * Process all packet received since the last clock tick. - */ - while ((skb = skb_dequeue(&lapb->input_queue)) != NULL) - lapb_data_input(lapb, skb); - - /* - * If in a data transfer state, transmit any data. - */ - if (lapb->state == LAPB_STATE_3) - lapb_kick(lapb); - - /* - * T2 expiry code. - */ - if (lapb->t2timer > 0 && --lapb->t2timer == 0) { - if (lapb->state == LAPB_STATE_3) { - if (lapb->condition & LAPB_ACK_PENDING_CONDITION) { - lapb->condition &= ~LAPB_ACK_PENDING_CONDITION; - lapb_timeout_response(lapb); - } - } - } - /* - * If T1 isn't running, or hasn't timed out yet, keep going. - */ - if (lapb->t1timer == 0 || --lapb->t1timer > 0) { - lapb_set_timer(lapb); - return; + if (lapb->condition & LAPB_ACK_PENDING_CONDITION) { + lapb->condition &= ~LAPB_ACK_PENDING_CONDITION; + lapb_timeout_response(lapb); } +} + +static void lapb_t1timer_expiry(unsigned long param) +{ + lapb_cb *lapb = (lapb_cb *)param; - /* - * T1 has expired. - */ switch (lapb->state) { /* @@ -120,12 +106,12 @@ static void lapb_timer(unsigned long param) case LAPB_STATE_1: if (lapb->n2count == lapb->n2) { lapb_clear_queues(lapb); - lapb->state = LAPB_STATE_0; - lapb->t2timer = 0; + lapb->state = LAPB_STATE_0; lapb_disconnect_indication(lapb, LAPB_TIMEDOUT); #if LAPB_DEBUG > 0 printk(KERN_DEBUG "lapb: (%p) S1 -> S0\n", lapb->token); #endif + return; } else { lapb->n2count++; if (lapb->mode & LAPB_EXTENDED) { @@ -148,12 +134,12 @@ static void lapb_timer(unsigned long param) case LAPB_STATE_2: if (lapb->n2count == lapb->n2) { lapb_clear_queues(lapb); - lapb->state = LAPB_STATE_0; - lapb->t2timer = 0; + lapb->state = LAPB_STATE_0; lapb_disconnect_confirmation(lapb, LAPB_TIMEDOUT); #if LAPB_DEBUG > 0 printk(KERN_DEBUG "lapb: (%p) S2 -> S0\n", lapb->token); #endif + return; } else { lapb->n2count++; #if LAPB_DEBUG > 1 @@ -169,12 +155,13 @@ static void lapb_timer(unsigned long param) case LAPB_STATE_3: if (lapb->n2count == lapb->n2) { lapb_clear_queues(lapb); - lapb->state = LAPB_STATE_0; - lapb->t2timer = 0; + lapb->state = LAPB_STATE_0; + lapb_stop_t2timer(lapb); lapb_disconnect_indication(lapb, LAPB_TIMEDOUT); #if LAPB_DEBUG > 0 printk(KERN_DEBUG "lapb: (%p) S3 -> S0\n", lapb->token); #endif + return; } else { lapb->n2count++; lapb_requeue_frames(lapb); @@ -187,12 +174,12 @@ static void lapb_timer(unsigned long param) case LAPB_STATE_4: if (lapb->n2count == lapb->n2) { lapb_clear_queues(lapb); - lapb->state = LAPB_STATE_0; - lapb->t2timer = 0; + lapb->state = LAPB_STATE_0; lapb_disconnect_indication(lapb, LAPB_TIMEDOUT); #if LAPB_DEBUG > 0 printk(KERN_DEBUG "lapb: (%p) S4 -> S0\n", lapb->token); #endif + return; } else { lapb->n2count++; lapb_transmit_frmr(lapb); @@ -200,9 +187,7 @@ static void lapb_timer(unsigned long param) break; } - lapb->t1timer = lapb->t1; - - lapb_set_timer(lapb); + lapb_start_t1timer(lapb); } #endif diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c index 22e1afbeeea7..dd80a211b29b 100644 --- a/net/netrom/af_netrom.c +++ b/net/netrom/af_netrom.c @@ -1,5 +1,5 @@ /* - * NET/ROM release 006 + * NET/ROM release 007 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -28,6 +28,8 @@ * Alan(GW4PTS) Started POSIXisms * NET/ROM 006 Alan(GW4PTS) Brought in line with the ANK changes * Jonathan(G4KLX) Removed hdrincl. + * NET/ROM 007 Jonathan(G4KLX) New timer architecture. + * Impmented Idle timer. */ #include @@ -122,8 +124,7 @@ static void nr_remove_socket(struct sock *sk) struct sock *s; unsigned long flags; - save_flags(flags); - cli(); + save_flags(flags); cli(); if ((s = nr_list) == sk) { nr_list = s->next; @@ -152,15 +153,8 @@ static void nr_kill_by_device(struct device *dev) struct sock *s; for (s = nr_list; s != NULL; s = s->next) { - if (s->protinfo.nr->device == dev) { - s->protinfo.nr->state = NR_STATE_0; - s->protinfo.nr->device = NULL; - s->state = TCP_CLOSE; - s->err = ENETUNREACH; - s->shutdown |= SEND_SHUTDOWN; - s->state_change(s); - s->dead = 1; - } + if (s->protinfo.nr->device == dev) + nr_disconnect(s, ENETUNREACH); } } @@ -187,8 +181,7 @@ static void nr_insert_socket(struct sock *sk) { unsigned long flags; - save_flags(flags); - cli(); + save_flags(flags); cli(); sk->next = nr_list; nr_list = sk; @@ -289,10 +282,13 @@ void nr_destroy_socket(struct sock *sk) /* Not static as it's used by the timer struct sk_buff *skb; unsigned long flags; - save_flags(flags); - cli(); + save_flags(flags); cli(); - del_timer(&sk->timer); + nr_stop_heartbeat(sk); + nr_stop_t1timer(sk); + nr_stop_t2timer(sk); + nr_stop_t4timer(sk); + nr_stop_idletimer(sk); nr_remove_socket(sk); nr_clear_queues(sk); /* Flush the queues */ @@ -300,7 +296,7 @@ void nr_destroy_socket(struct sock *sk) /* Not static as it's used by the timer while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) { if (skb->sk != sk) { /* A pending connection */ skb->sk->dead = 1; /* Queue the unaccepted socket for death */ - nr_set_timer(skb->sk); + nr_start_heartbeat(skb->sk); skb->sk->protinfo.nr->state = NR_STATE_0; } @@ -345,13 +341,13 @@ static int nr_setsockopt(struct socket *sock, int level, int optname, case NETROM_T1: if (opt < 1) return -EINVAL; - sk->protinfo.nr->t1 = opt * NR_SLOWHZ; + sk->protinfo.nr->t1 = opt * HZ; return 0; case NETROM_T2: if (opt < 1) return -EINVAL; - sk->protinfo.nr->t2 = opt * NR_SLOWHZ; + sk->protinfo.nr->t2 = opt * HZ; return 0; case NETROM_N2: @@ -363,13 +359,13 @@ static int nr_setsockopt(struct socket *sock, int level, int optname, case NETROM_T4: if (opt < 1) return -EINVAL; - sk->protinfo.nr->t4 = opt * NR_SLOWHZ; + sk->protinfo.nr->t4 = opt * HZ; return 0; case NETROM_IDLE: - if (opt < 1) + if (opt < 0) return -EINVAL; - sk->protinfo.nr->idle = opt * 60 * NR_SLOWHZ; + sk->protinfo.nr->idle = opt * 60 * HZ; return 0; default: @@ -392,11 +388,11 @@ static int nr_getsockopt(struct socket *sock, int level, int optname, switch (optname) { case NETROM_T1: - val = (sk->protinfo.nr->t1 * 2) / NR_SLOWHZ; + val = sk->protinfo.nr->t1 / HZ; break; case NETROM_T2: - val = sk->protinfo.nr->t2 / NR_SLOWHZ; + val = sk->protinfo.nr->t2 / HZ; break; case NETROM_N2: @@ -404,11 +400,11 @@ static int nr_getsockopt(struct socket *sock, int level, int optname, break; case NETROM_T4: - val = sk->protinfo.nr->t4 / NR_SLOWHZ; + val = sk->protinfo.nr->t4 / HZ; break; case NETROM_IDLE: - val = sk->protinfo.nr->idle / (NR_SLOWHZ * 60); + val = sk->protinfo.nr->idle / (60 * HZ); break; default: @@ -463,6 +459,11 @@ static int nr_create(struct socket *sock, int protocol) skb_queue_head_init(&nr->reseq_queue); skb_queue_head_init(&nr->frag_queue); + init_timer(&nr->t1timer); + init_timer(&nr->t2timer); + init_timer(&nr->t4timer); + init_timer(&nr->idletimer); + nr->t1 = sysctl_netrom_transport_timeout; nr->t2 = sysctl_netrom_transport_acknowledge_delay; nr->n2 = sysctl_netrom_transport_maximum_tries; @@ -507,6 +508,11 @@ static struct sock *nr_make_new(struct sock *osk) skb_queue_head_init(&nr->reseq_queue); skb_queue_head_init(&nr->frag_queue); + init_timer(&nr->t1timer); + init_timer(&nr->t2timer); + init_timer(&nr->t4timer); + init_timer(&nr->idletimer); + nr->t1 = osk->protinfo.nr->t1; nr->t2 = osk->protinfo.nr->t2; nr->n2 = osk->protinfo.nr->n2; @@ -539,39 +545,20 @@ static int nr_release(struct socket *sock, struct socket *peer) switch (sk->protinfo.nr->state) { case NR_STATE_0: - sk->state = TCP_CLOSE; - sk->shutdown |= SEND_SHUTDOWN; - sk->state_change(sk); - sk->dead = 1; + case NR_STATE_2: + nr_disconnect(sk, 0); nr_destroy_socket(sk); break; case NR_STATE_1: - sk->protinfo.nr->state = NR_STATE_0; - sk->state = TCP_CLOSE; - sk->shutdown |= SEND_SHUTDOWN; - sk->state_change(sk); - sk->dead = 1; - nr_destroy_socket(sk); - break; - - case NR_STATE_2: - nr_write_internal(sk, NR_DISCACK); - sk->protinfo.nr->state = NR_STATE_0; - sk->state = TCP_CLOSE; - sk->shutdown |= SEND_SHUTDOWN; - sk->state_change(sk); - sk->dead = 1; - nr_destroy_socket(sk); - break; - case NR_STATE_3: nr_clear_queues(sk); sk->protinfo.nr->n2count = 0; nr_write_internal(sk, NR_DISCREQ); - sk->protinfo.nr->t1timer = sk->protinfo.nr->t1; - sk->protinfo.nr->t2timer = 0; - sk->protinfo.nr->t4timer = 0; + nr_start_t1timer(sk); + nr_stop_t2timer(sk); + nr_stop_t4timer(sk); + nr_stop_idletimer(sk); sk->protinfo.nr->state = NR_STATE_2; sk->state = TCP_CLOSE; sk->shutdown |= SEND_SHUTDOWN; @@ -704,9 +691,12 @@ static int nr_connect(struct socket *sock, struct sockaddr *uaddr, /* Move to connecting socket, start sending Connect Requests */ sock->state = SS_CONNECTING; sk->state = TCP_SYN_SENT; + nr_establish_data_link(sk); + sk->protinfo.nr->state = NR_STATE_1; - nr_set_timer(sk); + + nr_start_heartbeat(sk); /* Now the loop */ if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) @@ -838,13 +828,13 @@ int nr_rx_frame(struct sk_buff *skb, struct device *dev) circuit_index = skb->data[15]; circuit_id = skb->data[16]; - frametype = skb->data[19]; + frametype = skb->data[19] & 0x0F; #ifdef CONFIG_INET /* * Check for an incoming IP over NET/ROM frame. */ - if ((frametype & 0x0F) == NR_PROTOEXT && circuit_index == NR_PROTO_IP && circuit_id == NR_PROTO_IP) { + if (frametype == NR_PROTOEXT && circuit_index == NR_PROTO_IP && circuit_id == NR_PROTO_IP) { skb_pull(skb, NR_NETWORK_LEN + NR_TRANSPORT_LEN); skb->h.raw = skb->data; @@ -856,11 +846,11 @@ int nr_rx_frame(struct sk_buff *skb, struct device *dev) * Find an existing socket connection, based on circuit ID, if it's * a Connect Request base it on their circuit ID. */ - if (((frametype & 0x0F) != NR_CONNREQ && (sk = nr_find_socket(circuit_index, circuit_id)) != NULL) || - ((frametype & 0x0F) == NR_CONNREQ && (sk = nr_find_peer(circuit_index, circuit_id)) != NULL)) { + if ((frametype != NR_CONNREQ && (sk = nr_find_socket(circuit_index, circuit_id)) != NULL) || + (frametype == NR_CONNREQ && (sk = nr_find_peer(circuit_index, circuit_id)) != NULL)) { skb->h.raw = skb->data; - if ((frametype & 0x0F) == NR_CONNACK && skb->len == 22) + if (frametype == NR_CONNACK && skb->len == 22) sk->protinfo.nr->bpqext = 1; else sk->protinfo.nr->bpqext = 0; @@ -868,8 +858,16 @@ int nr_rx_frame(struct sk_buff *skb, struct device *dev) return nr_process_rx_frame(sk, skb); } - if ((frametype & 0x0F) != NR_CONNREQ) - return 0; + switch (frametype) { + case NR_CONNREQ: + break; + case NR_DISCREQ: + case NR_DISCACK: + return 0; + default: + nr_transmit_dm(skb); + return 0; + } sk = nr_find_listener(dest); @@ -905,8 +903,8 @@ int nr_rx_frame(struct sk_buff *skb, struct device *dev) /* L4 timeout negotiation */ if (skb->len == 37) { timeout = skb->data[36] * 256 + skb->data[35]; - if (timeout * NR_SLOWHZ < make->protinfo.nr->t1) - make->protinfo.nr->t1 = timeout * NR_SLOWHZ; + if (timeout * HZ < make->protinfo.nr->t1) + make->protinfo.nr->t1 = timeout * HZ; make->protinfo.nr->bpqext = 1; } else { make->protinfo.nr->bpqext = 0; @@ -927,7 +925,8 @@ int nr_rx_frame(struct sk_buff *skb, struct device *dev) skb_queue_head(&sk->receive_queue, skb); - nr_set_timer(make); + nr_start_heartbeat(make); + nr_start_idletimer(make); if (!sk->dead) sk->data_ready(sk, skb->len); @@ -973,6 +972,7 @@ static int nr_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct s sax.sax25_family = AF_NETROM; sax.sax25_call = sk->protinfo.nr->dest_addr; } + SOCK_DEBUG(sk, "NET/ROM: sendto: Addresses built.\n"); /* Build a packet */ @@ -1074,37 +1074,35 @@ static int nr_shutdown(struct socket *sk, int how) static int nr_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { struct sock *sk = sock->sk; - int err; - long amount = 0; switch (cmd) { - case TIOCOUTQ: - if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(int))) != 0) - return err; + case TIOCOUTQ: { + long amount; amount = sk->sndbuf - atomic_read(&sk->wmem_alloc); if (amount < 0) amount = 0; - put_user(amount, (int *)arg); + if (put_user(amount, (int *)arg)) + return -EFAULT; return 0; + } case TIOCINQ: { struct sk_buff *skb; + long amount = 0L; /* These two are safe on a single CPU system as only user tasks fiddle here */ if ((skb = skb_peek(&sk->receive_queue)) != NULL) - amount = skb->len - 20; - if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(int))) != 0) - return err; - put_user(amount, (int *)arg); + amount = skb->len; + if (put_user(amount, (int *)arg)) + return -EFAULT; return 0; } case SIOCGSTAMP: if (sk != NULL) { - if (sk->stamp.tv_sec==0) + if (sk->stamp.tv_sec == 0) return -ENOENT; - if ((err = verify_area(VERIFY_WRITE,(void *)arg,sizeof(struct timeval))) != 0) - return err; - copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval)); + if (copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval))) + return -EFAULT; return 0; } return -EINVAL; @@ -1146,7 +1144,7 @@ static int nr_get_info(char *buffer, char **start, off_t offset, int length, int cli(); - len += sprintf(buffer, "user_addr dest_node src_node dev my your st vs vr va t1 t2 n2 wnd Snd-Q Rcv-Q\n"); + len += sprintf(buffer, "user_addr dest_node src_node dev my your st vs vr va t1 t2 t4 idle n2 wnd Snd-Q Rcv-Q\n"); for (s = nr_list; s != NULL; s = s->next) { if ((dev = s->protinfo.nr->device) == NULL) @@ -1158,20 +1156,30 @@ static int nr_get_info(char *buffer, char **start, off_t offset, int length, int ax2asc(&s->protinfo.nr->user_addr)); len += sprintf(buffer + len, "%-9s ", ax2asc(&s->protinfo.nr->dest_addr)); - len += sprintf(buffer + len, "%-9s %-3s %02X/%02X %02X/%02X %2d %3d %3d %3d %3d/%03d %2d/%02d %2d/%02d %3d %5d %5d\n", + len += sprintf(buffer + len, "%-9s %-3s %02X/%02X %02X/%02X %2d %3d %3d %3d %3lu/%03lu %2lu/%02lu %3lu/%03lu %3lu/%03lu %2d/%02d %3d %5d %5d\n", ax2asc(&s->protinfo.nr->source_addr), - devname, s->protinfo.nr->my_index, s->protinfo.nr->my_id, - s->protinfo.nr->your_index, s->protinfo.nr->your_id, + devname, + s->protinfo.nr->my_index, + s->protinfo.nr->my_id, + s->protinfo.nr->your_index, + s->protinfo.nr->your_id, s->protinfo.nr->state, - s->protinfo.nr->vs, s->protinfo.nr->vr, s->protinfo.nr->va, - s->protinfo.nr->t1timer / NR_SLOWHZ, - s->protinfo.nr->t1 / NR_SLOWHZ, - s->protinfo.nr->t2timer / NR_SLOWHZ, - s->protinfo.nr->t2 / NR_SLOWHZ, + s->protinfo.nr->vs, + s->protinfo.nr->vr, + s->protinfo.nr->va, + ax25_display_timer(&s->protinfo.nr->t1timer) / HZ, + s->protinfo.nr->t1 / HZ, + ax25_display_timer(&s->protinfo.nr->t2timer) / HZ, + s->protinfo.nr->t2 / HZ, + ax25_display_timer(&s->protinfo.nr->t4timer) / HZ, + s->protinfo.nr->t4 / HZ, + ax25_display_timer(&s->protinfo.nr->idletimer) / (60 * HZ), + s->protinfo.nr->idle / (60 * HZ), s->protinfo.nr->n2count, s->protinfo.nr->n2, s->protinfo.nr->window, - atomic_read(&s->wmem_alloc), atomic_read(&s->rmem_alloc)); + atomic_read(&s->wmem_alloc), + atomic_read(&s->rmem_alloc)); pos = begin + len; @@ -1269,7 +1277,7 @@ __initfunc(void nr_proto_init(struct net_proto *pro)) sock_register(&nr_family_ops); register_netdevice_notifier(&nr_dev_notifier); - printk(KERN_INFO "G4KLX NET/ROM for Linux. Version 0.6 for AX25.035 Linux 2.1\n"); + printk(KERN_INFO "G4KLX NET/ROM for Linux. Version 0.7 for AX25.037 Linux 2.1\n"); ax25_protocol_register(AX25_P_NETROM, nr_route_frame); ax25_linkfail_register(nr_link_failed); diff --git a/net/netrom/nr_dev.c b/net/netrom/nr_dev.c index 2d88bec2bafa..f7b617dccb14 100644 --- a/net/netrom/nr_dev.c +++ b/net/netrom/nr_dev.c @@ -1,5 +1,5 @@ /* - * NET/ROM release 006 + * NET/ROM release 007 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * diff --git a/net/netrom/nr_in.c b/net/netrom/nr_in.c index c13e92666174..a0d3148c2fde 100644 --- a/net/netrom/nr_in.c +++ b/net/netrom/nr_in.c @@ -1,5 +1,5 @@ /* - * NET/ROM release 006 + * NET/ROM release 007 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -21,6 +21,7 @@ * Darryl(G7LED) Added missing INFO with NAK case, optimized * INFOACK handling, removed reconnect on error. * NET/ROM 006 Jonathan(G4KLX) Hdrincl removal changes. + * NET/ROM 007 Jonathan(G4KLX) New timer architecture. */ #include @@ -54,6 +55,8 @@ static int nr_queue_rx_frame(struct sock *sk, struct sk_buff *skb, int more) skb_pull(skb, NR_NETWORK_LEN + NR_TRANSPORT_LEN); + nr_start_idletimer(sk); + if (more) { sk->protinfo.nr->fraglen += skb->len; skb_queue_tail(&sk->protinfo.nr->frag_queue, skb); @@ -90,11 +93,10 @@ static int nr_state1_machine(struct sock *sk, struct sk_buff *skb, int frametype switch (frametype) { case NR_CONNACK: + nr_stop_t1timer(sk); + nr_start_idletimer(sk); sk->protinfo.nr->your_index = skb->data[17]; sk->protinfo.nr->your_id = skb->data[18]; - sk->protinfo.nr->t1timer = 0; - sk->protinfo.nr->t2timer = 0; - sk->protinfo.nr->t4timer = 0; sk->protinfo.nr->vs = 0; sk->protinfo.nr->va = 0; sk->protinfo.nr->vr = 0; @@ -103,20 +105,12 @@ static int nr_state1_machine(struct sock *sk, struct sk_buff *skb, int frametype sk->protinfo.nr->n2count = 0; sk->protinfo.nr->window = skb->data[20]; sk->state = TCP_ESTABLISHED; - /* For WAIT_SABM connections we will produce an accept ready socket here */ if (!sk->dead) sk->state_change(sk); break; case NR_CONNACK | NR_CHOKE_FLAG: - nr_clear_queues(sk); - sk->protinfo.nr->state = NR_STATE_0; - sk->state = TCP_CLOSE; - sk->err = ECONNREFUSED; - sk->shutdown |= SEND_SHUTDOWN; - if (!sk->dead) - sk->state_change(sk); - sk->dead = 1; + nr_disconnect(sk, ECONNREFUSED); break; default: @@ -139,13 +133,7 @@ static int nr_state2_machine(struct sock *sk, struct sk_buff *skb, int frametype nr_write_internal(sk, NR_DISCACK); case NR_DISCACK: - sk->protinfo.nr->state = NR_STATE_0; - sk->state = TCP_CLOSE; - sk->err = 0; - sk->shutdown |= SEND_SHUTDOWN; - if (!sk->dead) - sk->state_change(sk); - sk->dead = 1; + nr_disconnect(sk, 0); break; default: @@ -178,26 +166,12 @@ static int nr_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype break; case NR_DISCREQ: - nr_clear_queues(sk); nr_write_internal(sk, NR_DISCACK); - sk->protinfo.nr->state = NR_STATE_0; - sk->state = TCP_CLOSE; - sk->err = 0; - sk->shutdown |= SEND_SHUTDOWN; - if (!sk->dead) - sk->state_change(sk); - sk->dead = 1; + nr_disconnect(sk, 0); break; case NR_DISCACK: - nr_clear_queues(sk); - sk->protinfo.nr->state = NR_STATE_0; - sk->state = TCP_CLOSE; - sk->err = ECONNRESET; - sk->shutdown |= SEND_SHUTDOWN; - if (!sk->dead) - sk->state_change(sk); - sk->dead = 1; + nr_disconnect(sk, ECONNRESET); break; case NR_INFOACK: @@ -206,10 +180,10 @@ static int nr_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype case NR_INFOACK | NR_NAK_FLAG | NR_CHOKE_FLAG: if (frametype & NR_CHOKE_FLAG) { sk->protinfo.nr->condition |= NR_COND_PEER_RX_BUSY; - sk->protinfo.nr->t4timer = sk->protinfo.nr->t4; + nr_start_t4timer(sk); } else { sk->protinfo.nr->condition &= ~NR_COND_PEER_RX_BUSY; - sk->protinfo.nr->t4timer = 0; + nr_stop_t4timer(sk); } if (!nr_validate_nr(sk, nr)) { break; @@ -236,10 +210,10 @@ static int nr_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype case NR_INFO | NR_NAK_FLAG | NR_CHOKE_FLAG | NR_MORE_FLAG: if (frametype & NR_CHOKE_FLAG) { sk->protinfo.nr->condition |= NR_COND_PEER_RX_BUSY; - sk->protinfo.nr->t4timer = sk->protinfo.nr->t4; + nr_start_t4timer(sk); } else { sk->protinfo.nr->condition &= ~NR_COND_PEER_RX_BUSY; - sk->protinfo.nr->t4timer = 0; + nr_stop_t4timer(sk); } if (nr_validate_nr(sk, nr)) { if (frametype & NR_NAK_FLAG) { @@ -286,8 +260,8 @@ static int nr_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype nr_enquiry_response(sk); } else { if (!(sk->protinfo.nr->condition & NR_COND_ACK_PENDING)) { - sk->protinfo.nr->t2timer = sk->protinfo.nr->t2; sk->protinfo.nr->condition |= NR_COND_ACK_PENDING; + nr_start_t2timer(sk); } } break; @@ -307,8 +281,6 @@ int nr_process_rx_frame(struct sock *sk, struct sk_buff *skb) if (sk->protinfo.nr->state == NR_STATE_0) return 0; - del_timer(&sk->timer); - frametype = skb->data[19]; switch (sk->protinfo.nr->state) { @@ -323,7 +295,7 @@ int nr_process_rx_frame(struct sock *sk, struct sk_buff *skb) break; } - nr_set_timer(sk); + nr_kick(sk); return queued; } diff --git a/net/netrom/nr_out.c b/net/netrom/nr_out.c index 7c053b482624..4c3eb61d8d41 100644 --- a/net/netrom/nr_out.c +++ b/net/netrom/nr_out.c @@ -1,5 +1,5 @@ /* - * NET/ROM release 006 + * NET/ROM release 007 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -13,6 +13,7 @@ * NET/ROM 001 Jonathan(G4KLX) Cloned from ax25_out.c * NET/ROM 003 Jonathan(G4KLX) Added NET/ROM fragmentation. * Darryl(G7LED) Fixed NAK, to give out correct reponse. + * NET/ROM 007 Jonathan(G4KLX) New timer architecture. */ #include @@ -83,8 +84,7 @@ void nr_output(struct sock *sk, struct sk_buff *skb) skb_queue_tail(&sk->write_queue, skb); /* Throw it on the queue */ } - if (sk->protinfo.nr->state == NR_STATE_3) - nr_kick(sk); + nr_kick(sk); } /* @@ -102,6 +102,8 @@ static void nr_send_iframe(struct sock *sk, struct sk_buff *skb) if (sk->protinfo.nr->condition & NR_COND_OWN_RX_BUSY) skb->data[4] |= NR_CHOKE_FLAG; + nr_start_idletimer(sk); + nr_transmit_buffer(sk, skb); } @@ -125,7 +127,8 @@ void nr_send_nak_frame(struct sock *sk) sk->protinfo.nr->condition &= ~NR_COND_ACK_PENDING; sk->protinfo.nr->vl = sk->protinfo.nr->vr; - sk->protinfo.nr->t1timer = 0; + + nr_stop_t1timer(sk); } void nr_kick(struct sock *sk) @@ -133,57 +136,60 @@ void nr_kick(struct sock *sk) struct sk_buff *skb, *skbn; unsigned short start, end; - del_timer(&sk->timer); + if (sk->protinfo.nr->state != NR_STATE_3) + return; + + if (sk->protinfo.nr->condition & NR_COND_PEER_RX_BUSY) + return; + + if (skb_peek(&sk->write_queue) == NULL) + return; start = (skb_peek(&sk->protinfo.nr->ack_queue) == NULL) ? sk->protinfo.nr->va : sk->protinfo.nr->vs; end = (sk->protinfo.nr->va + sk->protinfo.nr->window) % NR_MODULUS; - if (!(sk->protinfo.nr->condition & NR_COND_PEER_RX_BUSY) && - start != end && - skb_peek(&sk->write_queue) != NULL) { - - sk->protinfo.nr->vs = start; + if (start == end) + return; - /* - * Transmit data until either we're out of data to send or - * the window is full. - */ + sk->protinfo.nr->vs = start; - /* - * Dequeue the frame and copy it. - */ - skb = skb_dequeue(&sk->write_queue); + /* + * Transmit data until either we're out of data to send or + * the window is full. + */ - do { - if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) { - skb_queue_head(&sk->write_queue, skb); - break; - } + /* + * Dequeue the frame and copy it. + */ + skb = skb_dequeue(&sk->write_queue); - skb_set_owner_w(skbn, sk); + do { + if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) { + skb_queue_head(&sk->write_queue, skb); + break; + } - /* - * Transmit the frame copy. - */ - nr_send_iframe(sk, skbn); + skb_set_owner_w(skbn, sk); - sk->protinfo.nr->vs = (sk->protinfo.nr->vs + 1) % NR_MODULUS; + /* + * Transmit the frame copy. + */ + nr_send_iframe(sk, skbn); - /* - * Requeue the original data frame. - */ - skb_queue_tail(&sk->protinfo.nr->ack_queue, skb); + sk->protinfo.nr->vs = (sk->protinfo.nr->vs + 1) % NR_MODULUS; - } while (sk->protinfo.nr->vs != end && (skb = skb_dequeue(&sk->write_queue)) != NULL); + /* + * Requeue the original data frame. + */ + skb_queue_tail(&sk->protinfo.nr->ack_queue, skb); - sk->protinfo.nr->vl = sk->protinfo.nr->vr; - sk->protinfo.nr->condition &= ~NR_COND_ACK_PENDING; + } while (sk->protinfo.nr->vs != end && (skb = skb_dequeue(&sk->write_queue)) != NULL); - if (sk->protinfo.nr->t1timer == 0) - sk->protinfo.nr->t1timer = sk->protinfo.nr->t1; - } + sk->protinfo.nr->vl = sk->protinfo.nr->vr; + sk->protinfo.nr->condition &= ~NR_COND_ACK_PENDING; - nr_set_timer(sk); + if (!nr_t1timer_running(sk)) + nr_start_t1timer(sk); } void nr_transmit_buffer(struct sock *sk, struct sk_buff *skb) @@ -211,13 +217,7 @@ void nr_transmit_buffer(struct sock *sk, struct sk_buff *skb) if (!nr_route_frame(skb, NULL)) { kfree_skb(skb, FREE_WRITE); - - sk->state = TCP_CLOSE; - sk->err = ENETUNREACH; - sk->shutdown |= SEND_SHUTDOWN; - if (!sk->dead) - sk->state_change(sk); - sk->dead = 1; + nr_disconnect(sk, ENETUNREACH); } } @@ -233,8 +233,10 @@ void nr_establish_data_link(struct sock *sk) nr_write_internal(sk, NR_CONNREQ); - sk->protinfo.nr->t2timer = 0; - sk->protinfo.nr->t1timer = sk->protinfo.nr->t1; + nr_stop_t2timer(sk); + nr_stop_t4timer(sk); + nr_stop_idletimer(sk); + nr_start_t1timer(sk); } /* @@ -261,12 +263,12 @@ void nr_check_iframes_acked(struct sock *sk, unsigned short nr) { if (sk->protinfo.nr->vs == nr) { nr_frames_acked(sk, nr); - sk->protinfo.nr->t1timer = 0; + nr_stop_t1timer(sk); sk->protinfo.nr->n2count = 0; } else { if (sk->protinfo.nr->va != nr) { nr_frames_acked(sk, nr); - sk->protinfo.nr->t1timer = sk->protinfo.nr->t1; + nr_start_t1timer(sk); } } } diff --git a/net/netrom/nr_route.c b/net/netrom/nr_route.c index 41399a53caa3..ffbb240c411a 100644 --- a/net/netrom/nr_route.c +++ b/net/netrom/nr_route.c @@ -1,5 +1,5 @@ /* - * NET/ROM release 006 + * NET/ROM release 007 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -93,6 +93,7 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2 nr_neigh->callsign = *ax25; nr_neigh->digipeat = NULL; + nr_neigh->ax25 = NULL; nr_neigh->dev = dev; nr_neigh->quality = sysctl_netrom_default_path_quality; nr_neigh->locked = 0; @@ -372,6 +373,7 @@ static int nr_add_neigh(ax25_address *callsign, ax25_digi *ax25_digi, struct dev nr_neigh->callsign = *callsign; nr_neigh->digipeat = NULL; + nr_neigh->ax25 = NULL; nr_neigh->dev = dev; nr_neigh->quality = quality; nr_neigh->locked = 1; @@ -582,7 +584,7 @@ static ax25_digi *nr_call_to_digi(int ndigis, ax25_address *digipeaters) } ax25_digi.ndigi = ndigis; - ax25_digi.lastrepeat = 0; + ax25_digi.lastrepeat = -1; return &ax25_digi; } @@ -594,14 +596,12 @@ int nr_rt_ioctl(unsigned int cmd, void *arg) { struct nr_route_struct nr_route; struct device *dev; - int err; switch (cmd) { case SIOCADDRT: - if ((err = verify_area(VERIFY_READ, arg, sizeof(struct nr_route_struct))) != 0) - return err; - copy_from_user(&nr_route, arg, sizeof(struct nr_route_struct)); + if (copy_from_user(&nr_route, arg, sizeof(struct nr_route_struct))) + return -EFAULT; if ((dev = nr_ax25_dev_get(nr_route.device)) == NULL) return -EINVAL; if (nr_route.ndigis < 0 || nr_route.ndigis > AX25_MAX_DIGIS) @@ -623,9 +623,8 @@ int nr_rt_ioctl(unsigned int cmd, void *arg) } case SIOCDELRT: - if ((err = verify_area(VERIFY_READ, arg, sizeof(struct nr_route_struct))) != 0) - return err; - copy_from_user(&nr_route, arg, sizeof(struct nr_route_struct)); + if (copy_from_user(&nr_route, arg, sizeof(struct nr_route_struct))) + return -EFAULT; if ((dev = nr_ax25_dev_get(nr_route.device)) == NULL) return -EINVAL; switch (nr_route.type) { @@ -653,17 +652,19 @@ int nr_rt_ioctl(unsigned int cmd, void *arg) * A level 2 link has timed out, therefore it appears to be a poor link, * then don't use that neighbour until it is reset. */ -void nr_link_failed(ax25_address *callsign, struct device *dev) +void nr_link_failed(ax25_cb *ax25, int reason) { struct nr_neigh *nr_neigh; struct nr_node *nr_node; for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) - if (ax25cmp(&nr_neigh->callsign, callsign) == 0 && nr_neigh->dev == dev) + if (nr_neigh->ax25 == ax25) break; if (nr_neigh == NULL) return; + nr_neigh->ax25 = NULL; + if (++nr_neigh->failed < sysctl_netrom_link_fails_count) return; for (nr_node = nr_node_list; nr_node != NULL; nr_node = nr_node->next) @@ -724,7 +725,9 @@ int nr_route_frame(struct sk_buff *skb, ax25_cb *ax25) dptr = skb_push(skb, 1); *dptr = AX25_P_NETROM; - return ax25_send_frame(skb, 256, (ax25_address *)dev->dev_addr, &nr_neigh->callsign, nr_neigh->digipeat, nr_neigh->dev); + nr_neigh->ax25 = ax25_send_frame(skb, 256, (ax25_address *)dev->dev_addr, &nr_neigh->callsign, nr_neigh->digipeat, nr_neigh->dev); + + return (nr_neigh->ax25 != NULL); } int nr_nodes_get_info(char *buffer, char **start, off_t offset, diff --git a/net/netrom/nr_subr.c b/net/netrom/nr_subr.c index 5eae252796b9..d311418769f7 100644 --- a/net/netrom/nr_subr.c +++ b/net/netrom/nr_subr.c @@ -1,5 +1,5 @@ /* - * NET/ROM release 006 + * NET/ROM release 007 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -12,6 +12,7 @@ * History * NET/ROM 001 Jonathan(G4KLX) Cloned from ax25_subr.c * NET/ROM 003 Jonathan(G4KLX) Added G8BPQ NET/ROM extensions. + * NET/ROM 007 Jonathan(G4KLX) New timer architecture. */ #include @@ -172,7 +173,7 @@ void nr_write_internal(struct sock *sk, int frametype) switch (frametype & 0x0F) { case NR_CONNREQ: - timeout = sk->protinfo.nr->t1 / NR_SLOWHZ; + timeout = sk->protinfo.nr->t1 / HZ; *dptr++ = sk->protinfo.nr->my_index; *dptr++ = sk->protinfo.nr->my_id; *dptr++ = 0; @@ -268,4 +269,25 @@ void nr_transmit_dm(struct sk_buff *skb) kfree_skb(skbn, FREE_WRITE); } +void nr_disconnect(struct sock *sk, int reason) +{ + nr_stop_t1timer(sk); + nr_stop_t2timer(sk); + nr_stop_t4timer(sk); + nr_stop_idletimer(sk); + + nr_clear_queues(sk); + + sk->protinfo.nr->state = NR_STATE_0; + + sk->state = TCP_CLOSE; + sk->err = reason; + sk->shutdown |= SEND_SHUTDOWN; + + if (!sk->dead) + sk->state_change(sk); + + sk->dead = 1; +} + #endif diff --git a/net/netrom/nr_timer.c b/net/netrom/nr_timer.c index cc96f26dd12e..b3fbd012ebac 100644 --- a/net/netrom/nr_timer.c +++ b/net/netrom/nr_timer.c @@ -1,5 +1,5 @@ /* - * NET/ROM release 006 + * NET/ROM release 007 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -11,6 +11,8 @@ * * History * NET/ROM 001 Jonathan(G4KLX) Cloned from ax25_timer.c + * NET/ROM 007 Jonathan(G4KLX) New timer architecture. + * Implemented idle timer. */ #include @@ -37,42 +39,110 @@ #include #include -static void nr_timer(unsigned long); +static void nr_heartbeat_expiry(unsigned long); +static void nr_t1timer_expiry(unsigned long); +static void nr_t2timer_expiry(unsigned long); +static void nr_t4timer_expiry(unsigned long); +static void nr_idletimer_expiry(unsigned long); -/* - * Linux set timer - */ -void nr_set_timer(struct sock *sk) +void nr_start_t1timer(struct sock *sk) +{ + del_timer(&sk->protinfo.nr->t1timer); + + sk->protinfo.nr->t1timer.data = (unsigned long)sk; + sk->protinfo.nr->t1timer.function = &nr_t1timer_expiry; + sk->protinfo.nr->t1timer.expires = jiffies + sk->protinfo.nr->t1; + + add_timer(&sk->protinfo.nr->t1timer); +} + +void nr_start_t2timer(struct sock *sk) +{ + del_timer(&sk->protinfo.nr->t2timer); + + sk->protinfo.nr->t2timer.data = (unsigned long)sk; + sk->protinfo.nr->t2timer.function = &nr_t2timer_expiry; + sk->protinfo.nr->t2timer.expires = jiffies + sk->protinfo.nr->t2; + + add_timer(&sk->protinfo.nr->t2timer); +} + +void nr_start_t4timer(struct sock *sk) +{ + del_timer(&sk->protinfo.nr->t4timer); + + sk->protinfo.nr->t4timer.data = (unsigned long)sk; + sk->protinfo.nr->t4timer.function = &nr_t4timer_expiry; + sk->protinfo.nr->t4timer.expires = jiffies + sk->protinfo.nr->t4; + + add_timer(&sk->protinfo.nr->t4timer); +} + +void nr_start_idletimer(struct sock *sk) { - unsigned long flags; + del_timer(&sk->protinfo.nr->idletimer); + + if (sk->protinfo.nr->idle > 0) { + sk->protinfo.nr->idletimer.data = (unsigned long)sk; + sk->protinfo.nr->idletimer.function = &nr_idletimer_expiry; + sk->protinfo.nr->idletimer.expires = jiffies + sk->protinfo.nr->idle; + + add_timer(&sk->protinfo.nr->idletimer); + } +} - save_flags(flags); cli(); +void nr_start_heartbeat(struct sock *sk) +{ del_timer(&sk->timer); - restore_flags(flags); sk->timer.data = (unsigned long)sk; - sk->timer.function = &nr_timer; - sk->timer.expires = jiffies + (HZ / 10); + sk->timer.function = &nr_heartbeat_expiry; + sk->timer.expires = jiffies + 5 * HZ; add_timer(&sk->timer); } -/* - * NET/ROM TIMER - * - * This routine is called every 100ms. Decrement timer by this - * amount - if expired then process the event. - */ -static void nr_timer(unsigned long param) +void nr_stop_t1timer(struct sock *sk) +{ + del_timer(&sk->protinfo.nr->t1timer); +} + +void nr_stop_t2timer(struct sock *sk) +{ + del_timer(&sk->protinfo.nr->t2timer); +} + +void nr_stop_t4timer(struct sock *sk) +{ + del_timer(&sk->protinfo.nr->t4timer); +} + +void nr_stop_idletimer(struct sock *sk) +{ + del_timer(&sk->protinfo.nr->idletimer); +} + +void nr_stop_heartbeat(struct sock *sk) +{ + del_timer(&sk->timer); +} + +int nr_t1timer_running(struct sock *sk) +{ + return (sk->protinfo.nr->t1timer.prev != NULL || + sk->protinfo.nr->t1timer.next != NULL); +} + +static void nr_heartbeat_expiry(unsigned long param) { struct sock *sk = (struct sock *)param; switch (sk->protinfo.nr->state) { + case NR_STATE_0: /* Magic here: If we listen() and a new link dies before it is accepted() it isn't 'dead' so doesn't get removed. */ if (sk->destroy || (sk->state == TCP_LISTEN && sk->dead)) { - del_timer(&sk->timer); nr_destroy_socket(sk); return; } @@ -90,45 +160,63 @@ static void nr_timer(unsigned long param) nr_write_internal(sk, NR_INFOACK); break; } - /* - * Check for frames to transmit. - */ - nr_kick(sk); - break; - - default: break; } - if (sk->protinfo.nr->t2timer > 0 && --sk->protinfo.nr->t2timer == 0) { - if (sk->protinfo.nr->state == NR_STATE_3) { - if (sk->protinfo.nr->condition & NR_COND_ACK_PENDING) { - sk->protinfo.nr->condition &= ~NR_COND_ACK_PENDING; - nr_enquiry_response(sk); - } - } - } + nr_start_heartbeat(sk); +} - if (sk->protinfo.nr->t4timer > 0 && --sk->protinfo.nr->t4timer == 0) { - sk->protinfo.nr->condition &= ~NR_COND_PEER_RX_BUSY; - } +static void nr_t2timer_expiry(unsigned long param) +{ + struct sock *sk = (struct sock *)param; - if (sk->protinfo.nr->t1timer == 0 || --sk->protinfo.nr->t1timer > 0) { - nr_set_timer(sk); - return; + if (sk->protinfo.nr->condition & NR_COND_ACK_PENDING) { + sk->protinfo.nr->condition &= ~NR_COND_ACK_PENDING; + nr_enquiry_response(sk); } +} + +static void nr_t4timer_expiry(unsigned long param) +{ + struct sock *sk = (struct sock *)param; + + sk->protinfo.nr->condition &= ~NR_COND_PEER_RX_BUSY; +} + +static void nr_idletimer_expiry(unsigned long param) +{ + struct sock *sk = (struct sock *)param; + + nr_clear_queues(sk); + + sk->protinfo.nr->n2count = 0; + nr_write_internal(sk, NR_DISCREQ); + sk->protinfo.nr->state = NR_STATE_2; + + nr_start_t1timer(sk); + nr_stop_t2timer(sk); + nr_stop_t4timer(sk); + + sk->state = TCP_CLOSE; + sk->err = 0; + sk->shutdown |= SEND_SHUTDOWN; + + if (!sk->dead) + sk->state_change(sk); + + sk->dead = 1; +} + +static void nr_t1timer_expiry(unsigned long param) +{ + struct sock *sk = (struct sock *)param; switch (sk->protinfo.nr->state) { + case NR_STATE_1: if (sk->protinfo.nr->n2count == sk->protinfo.nr->n2) { - nr_clear_queues(sk); - sk->protinfo.nr->state = NR_STATE_0; - sk->state = TCP_CLOSE; - sk->err = ETIMEDOUT; - sk->shutdown |= SEND_SHUTDOWN; - if (!sk->dead) - sk->state_change(sk); - sk->dead = 1; + nr_disconnect(sk, ETIMEDOUT); + return; } else { sk->protinfo.nr->n2count++; nr_write_internal(sk, NR_CONNREQ); @@ -137,14 +225,8 @@ static void nr_timer(unsigned long param) case NR_STATE_2: if (sk->protinfo.nr->n2count == sk->protinfo.nr->n2) { - nr_clear_queues(sk); - sk->protinfo.nr->state = NR_STATE_0; - sk->state = TCP_CLOSE; - sk->err = ETIMEDOUT; - sk->shutdown |= SEND_SHUTDOWN; - if (!sk->dead) - sk->state_change(sk); - sk->dead = 1; + nr_disconnect(sk, ETIMEDOUT); + return; } else { sk->protinfo.nr->n2count++; nr_write_internal(sk, NR_DISCREQ); @@ -153,14 +235,8 @@ static void nr_timer(unsigned long param) case NR_STATE_3: if (sk->protinfo.nr->n2count == sk->protinfo.nr->n2) { - nr_clear_queues(sk); - sk->protinfo.nr->state = NR_STATE_0; - sk->state = TCP_CLOSE; - sk->err = ETIMEDOUT; - sk->shutdown |= SEND_SHUTDOWN; - if (!sk->dead) - sk->state_change(sk); - sk->dead = 1; + nr_disconnect(sk, ETIMEDOUT); + return; } else { sk->protinfo.nr->n2count++; nr_requeue_frames(sk); @@ -168,9 +244,7 @@ static void nr_timer(unsigned long param) break; } - sk->protinfo.nr->t1timer = sk->protinfo.nr->t1; - - nr_set_timer(sk); + nr_start_t1timer(sk); } #endif diff --git a/net/netrom/sysctl_net_netrom.c b/net/netrom/sysctl_net_netrom.c index c6a415ee6ae5..3ce3e71f2781 100644 --- a/net/netrom/sysctl_net_netrom.c +++ b/net/netrom/sysctl_net_netrom.c @@ -17,16 +17,16 @@ static int min_quality[] = {0}, max_quality[] = {255}; static int min_obs[] = {0}, max_obs[] = {255}; static int min_ttl[] = {0}, max_ttl[] = {255}; -static int min_t1[] = {5 * NR_SLOWHZ}; -static int max_t1[] = {600 * NR_SLOWHZ}; +static int min_t1[] = {5 * HZ}; +static int max_t1[] = {600 * HZ}; static int min_n2[] = {2}, max_n2[] = {127}; -static int min_t2[] = {1 * NR_SLOWHZ}; -static int max_t2[] = {60 * NR_SLOWHZ}; -static int min_t4[] = {1 * NR_SLOWHZ}; -static int max_t4[] = {1000 * NR_SLOWHZ}; +static int min_t2[] = {1 * HZ}; +static int max_t2[] = {60 * HZ}; +static int min_t4[] = {1 * HZ}; +static int max_t4[] = {1000 * HZ}; static int min_window[] = {1}, max_window[] = {127}; -static int min_idle[] = {0 * NR_SLOWHZ}; -static int max_idle[] = {65535 * NR_SLOWHZ}; +static int min_idle[] = {0 * HZ}; +static int max_idle[] = {65535 * HZ}; static int min_route[] = {0}, max_route[] = {1}; static int min_fails[] = {1}, max_fails[] = {10}; diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c index 9896de9cbd12..134eee17a27b 100644 --- a/net/rose/af_rose.c +++ b/net/rose/af_rose.c @@ -1,5 +1,5 @@ /* - * ROSE release 002 + * ROSE release 003 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -17,6 +17,9 @@ * ROSE 002 Jonathan(G4KLX) Changed hdrincl to qbitincl. * Added random number facilities entry. * Variable number of ROSE devices. + * ROSE 003 Jonathan(G4KLX) New timer architecture. + * Implemented idle timer. + * Added use count to neighbour. */ #include @@ -172,8 +175,7 @@ static void rose_remove_socket(struct sock *sk) struct sock *s; unsigned long flags; - save_flags(flags); - cli(); + save_flags(flags); cli(); if ((s = rose_list) == sk) { rose_list = s->next; @@ -204,13 +206,9 @@ void rose_kill_by_neigh(struct rose_neigh *neigh) for (s = rose_list; s != NULL; s = s->next) { if (s->protinfo.rose->neighbour == neigh) { - s->protinfo.rose->state = ROSE_STATE_0; + rose_disconnect(s, ENETUNREACH, ROSE_OUT_OF_ORDER, 0); + s->protinfo.rose->neighbour->use--; s->protinfo.rose->neighbour = NULL; - s->state = TCP_CLOSE; - s->err = ENETUNREACH; - s->shutdown |= SEND_SHUTDOWN; - s->state_change(s); - s->dead = 1; } } } @@ -224,13 +222,9 @@ static void rose_kill_by_device(struct device *dev) for (s = rose_list; s != NULL; s = s->next) { if (s->protinfo.rose->device == dev) { - s->protinfo.rose->state = ROSE_STATE_0; + rose_disconnect(s, ENETUNREACH, ROSE_OUT_OF_ORDER, 0); + s->protinfo.rose->neighbour->use--; s->protinfo.rose->device = NULL; - s->state = TCP_CLOSE; - s->err = ENETUNREACH; - s->shutdown |= SEND_SHUTDOWN; - s->state_change(s); - s->dead = 1; } } } @@ -265,8 +259,7 @@ static void rose_insert_socket(struct sock *sk) { unsigned long flags; - save_flags(flags); - cli(); + save_flags(flags); cli(); sk->next = rose_list; rose_list = sk; @@ -283,8 +276,7 @@ static struct sock *rose_find_listener(rose_address *addr, ax25_address *call) unsigned long flags; struct sock *s; - save_flags(flags); - cli(); + save_flags(flags); cli(); for (s = rose_list; s != NULL; s = s->next) { if (rosecmp(&s->protinfo.rose->source_addr, addr) == 0 && ax25cmp(&s->protinfo.rose->source_call, call) == 0 && s->protinfo.rose->source_ndigis == 0 && s->state == TCP_LISTEN) { @@ -312,8 +304,7 @@ struct sock *rose_find_socket(unsigned int lci, struct rose_neigh *neigh) struct sock *s; unsigned long flags; - save_flags(flags); - cli(); + save_flags(flags); cli(); for (s = rose_list; s != NULL; s = s->next) { if (s->protinfo.rose->lci == lci && s->protinfo.rose->neighbour == neigh) { @@ -371,10 +362,11 @@ void rose_destroy_socket(struct sock *sk) /* Not static as it's used by the time struct sk_buff *skb; unsigned long flags; - save_flags(flags); - cli(); + save_flags(flags); cli(); - del_timer(&sk->timer); + rose_stop_heartbeat(sk); + rose_stop_idletimer(sk); + rose_stop_timer(sk); rose_remove_socket(sk); rose_clear_queues(sk); /* Flush the queues */ @@ -382,7 +374,7 @@ void rose_destroy_socket(struct sock *sk) /* Not static as it's used by the time while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) { if (skb->sk != sk) { /* A pending connection */ skb->sk->dead = 1; /* Queue the unaccepted socket for death */ - rose_set_timer(skb->sk); + rose_start_heartbeat(skb->sk); skb->sk->protinfo.rose->state = ROSE_STATE_0; } @@ -424,34 +416,38 @@ static int rose_setsockopt(struct socket *sock, int level, int optname, return -EFAULT; switch (optname) { + case ROSE_DEFER: + sk->protinfo.rose->defer = opt ? 1 : 0; + return 0; + case ROSE_T1: if (opt < 1) return -EINVAL; - sk->protinfo.rose->t1 = opt * ROSE_SLOWHZ; + sk->protinfo.rose->t1 = opt * HZ; return 0; case ROSE_T2: if (opt < 1) return -EINVAL; - sk->protinfo.rose->t2 = opt * ROSE_SLOWHZ; + sk->protinfo.rose->t2 = opt * HZ; return 0; case ROSE_T3: if (opt < 1) return -EINVAL; - sk->protinfo.rose->t3 = opt * ROSE_SLOWHZ; + sk->protinfo.rose->t3 = opt * HZ; return 0; case ROSE_HOLDBACK: if (opt < 1) return -EINVAL; - sk->protinfo.rose->hb = opt * ROSE_SLOWHZ; + sk->protinfo.rose->hb = opt * HZ; return 0; case ROSE_IDLE: - if (opt < 1) + if (opt < 0) return -EINVAL; - sk->protinfo.rose->idle = opt * 60 * ROSE_SLOWHZ; + sk->protinfo.rose->idle = opt * 60 * HZ; return 0; case ROSE_QBITINCL: @@ -477,24 +473,28 @@ static int rose_getsockopt(struct socket *sock, int level, int optname, return -EFAULT; switch (optname) { + case ROSE_DEFER: + val = sk->protinfo.rose->defer; + break; + case ROSE_T1: - val = sk->protinfo.rose->t1 / ROSE_SLOWHZ; + val = sk->protinfo.rose->t1 / HZ; break; case ROSE_T2: - val = sk->protinfo.rose->t2 / ROSE_SLOWHZ; + val = sk->protinfo.rose->t2 / HZ; break; case ROSE_T3: - val = sk->protinfo.rose->t3 / ROSE_SLOWHZ; + val = sk->protinfo.rose->t3 / HZ; break; case ROSE_HOLDBACK: - val = sk->protinfo.rose->hb / ROSE_SLOWHZ; + val = sk->protinfo.rose->hb / HZ; break; case ROSE_IDLE: - val = sk->protinfo.rose->idle / (ROSE_SLOWHZ * 60); + val = sk->protinfo.rose->idle / (60 * HZ); break; case ROSE_QBITINCL: @@ -550,7 +550,10 @@ static int rose_create(struct socket *sock, int protocol) sock->ops = &rose_proto_ops; sk->protocol = protocol; - sk->mtu = ROSE_MTU; /* 128 */ + sk->mtu = ROSE_MTU; /* 253 */ + + init_timer(&rose->timer); + init_timer(&rose->idletimer); skb_queue_head_init(&rose->frag_queue); @@ -592,6 +595,9 @@ static struct sock *rose_make_new(struct sock *osk) sk->sleep = osk->sleep; sk->zapped = osk->zapped; + init_timer(&rose->timer); + init_timer(&rose->idletimer); + skb_queue_head_init(&rose->frag_queue); rose->t1 = osk->protinfo.rose->t1; @@ -600,6 +606,7 @@ static struct sock *rose_make_new(struct sock *osk) rose->hb = osk->protinfo.rose->hb; rose->idle = osk->protinfo.rose->idle; + rose->defer = osk->protinfo.rose->defer; rose->device = osk->protinfo.rose->device; rose->qbitincl = osk->protinfo.rose->qbitincl; @@ -625,28 +632,24 @@ static int rose_release(struct socket *sock, struct socket *peer) switch (sk->protinfo.rose->state) { case ROSE_STATE_0: - sk->state = TCP_CLOSE; - sk->shutdown |= SEND_SHUTDOWN; - sk->state_change(sk); - sk->dead = 1; + rose_disconnect(sk, 0, -1, -1); rose_destroy_socket(sk); break; case ROSE_STATE_2: - sk->protinfo.rose->state = ROSE_STATE_0; - sk->state = TCP_CLOSE; - sk->shutdown |= SEND_SHUTDOWN; - sk->state_change(sk); - sk->dead = 1; + sk->protinfo.rose->neighbour->use--; + rose_disconnect(sk, 0, -1, -1); rose_destroy_socket(sk); - break; + break; case ROSE_STATE_1: case ROSE_STATE_3: case ROSE_STATE_4: + case ROSE_STATE_5: rose_clear_queues(sk); + rose_stop_idletimer(sk); rose_write_internal(sk, ROSE_CLEAR_REQUEST); - sk->protinfo.rose->timer = sk->protinfo.rose->t3; + rose_start_t3timer(sk); sk->protinfo.rose->state = ROSE_STATE_2; sk->state = TCP_CLOSE; sk->shutdown |= SEND_SHUTDOWN; @@ -714,6 +717,7 @@ static int rose_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le { struct sock *sk = sock->sk; struct sockaddr_rose *addr = (struct sockaddr_rose *)uaddr; + unsigned char cause, diagnostic; ax25_address *user; struct device *dev; @@ -739,7 +743,7 @@ static int rose_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le if (addr->srose_family != AF_ROSE) return -EINVAL; - if ((sk->protinfo.rose->neighbour = rose_get_neigh(&addr->srose_addr)) == NULL) + if ((sk->protinfo.rose->neighbour = rose_get_neigh(&addr->srose_addr, &cause, &diagnostic)) == NULL) return -ENETUNREACH; if ((sk->protinfo.rose->lci = rose_new_lci(sk->protinfo.rose->neighbour)) == 0) @@ -775,10 +779,12 @@ static int rose_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le sk->state = TCP_SYN_SENT; sk->protinfo.rose->state = ROSE_STATE_1; - sk->protinfo.rose->timer = sk->protinfo.rose->t1; - rose_write_internal(sk, ROSE_CALL_REQUEST); - rose_set_timer(sk); + sk->protinfo.rose->neighbour->use++; + + rose_write_internal(sk, ROSE_CALL_REQUEST); + rose_start_heartbeat(sk); + rose_start_t1timer(sk); /* Now the loop */ if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) @@ -911,11 +917,8 @@ int rose_rx_call_request(struct sk_buff *skb, struct device *dev, struct rose_ne /* * skb->data points to the rose frame start */ - - /* - * XXX This is an error. - */ if (!rose_parse_facilities(skb, &facilities)) { + rose_transmit_clear_request(neigh, lci, ROSE_INVALID_FACILITY, 76); return 0; } @@ -925,7 +928,7 @@ int rose_rx_call_request(struct sk_buff *skb, struct device *dev, struct rose_ne * We can't accept the Call Request. */ if (sk == NULL || sk->ack_backlog == sk->max_ack_backlog || (make = rose_make_new(sk)) == NULL) { - rose_transmit_clear_request(neigh, lci, 0x01); + rose_transmit_clear_request(neigh, lci, ROSE_NETWORK_CONGESTION, 120); return 0; } @@ -944,14 +947,21 @@ int rose_rx_call_request(struct sk_buff *skb, struct device *dev, struct rose_ne make->protinfo.rose->neighbour = neigh; make->protinfo.rose->device = dev; - rose_write_internal(make, ROSE_CALL_ACCEPTED); + make->protinfo.rose->neighbour->use++; + + if (sk->protinfo.rose->defer) { + make->protinfo.rose->state = ROSE_STATE_5; + } else { + rose_write_internal(make, ROSE_CALL_ACCEPTED); + make->protinfo.rose->state = ROSE_STATE_3; + rose_start_idletimer(make); + } make->protinfo.rose->condition = 0x00; make->protinfo.rose->vs = 0; make->protinfo.rose->va = 0; make->protinfo.rose->vr = 0; make->protinfo.rose->vl = 0; - make->protinfo.rose->state = ROSE_STATE_3; sk->ack_backlog++; make->pair = sk; @@ -959,7 +969,7 @@ int rose_rx_call_request(struct sk_buff *skb, struct device *dev, struct rose_ne skb_queue_head(&sk->receive_queue, skb); - rose_set_timer(make); + rose_start_heartbeat(make); if (!sk->dead) sk->data_ready(sk, skb->len); @@ -1146,37 +1156,35 @@ static int rose_shutdown(struct socket *sk, int how) static int rose_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { struct sock *sk = sock->sk; - int err; - long amount = 0; switch (cmd) { - case TIOCOUTQ: - if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned int))) != 0) - return err; + case TIOCOUTQ: { + long amount; amount = sk->sndbuf - atomic_read(&sk->wmem_alloc); if (amount < 0) amount = 0; - put_user(amount, (unsigned int *)arg); + if (put_user(amount, (unsigned int *)arg)) + return -EFAULT; return 0; + } case TIOCINQ: { struct sk_buff *skb; + long amount = 0L; /* These two are safe on a single CPU system as only user tasks fiddle here */ if ((skb = skb_peek(&sk->receive_queue)) != NULL) - amount = skb->len - 20; - if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned int))) != 0) - return err; - put_user(amount, (unsigned int *)arg); + amount = skb->len; + if (put_user(amount, (unsigned int *)arg)) + return -EFAULT; return 0; } case SIOCGSTAMP: if (sk != NULL) { - if (sk->stamp.tv_sec==0) + if (sk->stamp.tv_sec == 0) return -ENOENT; - if ((err = verify_area(VERIFY_WRITE,(void *)arg,sizeof(struct timeval))) != 0) - return err; - copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval)); + if (copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval))) + return -EFAULT; return 0; } return -EINVAL; @@ -1195,20 +1203,51 @@ static int rose_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) case SIOCADDRT: case SIOCDELRT: + case SIOCRSCLRRT: if (!suser()) return -EPERM; return rose_rt_ioctl(cmd, (void *)arg); + case SIOCRSGCAUSE: { + struct rose_cause_struct rose_cause; + rose_cause.cause = sk->protinfo.rose->cause; + rose_cause.diagnostic = sk->protinfo.rose->diagnostic; + if (copy_to_user((void *)arg, &rose_cause, sizeof(struct rose_cause_struct))) + return -EFAULT; + return 0; + } + + case SIOCRSSCAUSE: { + struct rose_cause_struct rose_cause; + if (copy_from_user(&rose_cause, (void *)arg, sizeof(struct rose_cause_struct))) + return -EFAULT; + sk->protinfo.rose->cause = rose_cause.cause; + sk->protinfo.rose->diagnostic = rose_cause.diagnostic; + return 0; + } + case SIOCRSL2CALL: if (!suser()) return -EPERM; - if ((err = verify_area(VERIFY_READ, (void *)arg, sizeof(ax25_address))) != 0) - return err; if (ax25cmp(&rose_callsign, &null_ax25_address) != 0) ax25_listen_release(&rose_callsign, NULL); - copy_from_user(&rose_callsign, (void *)arg, sizeof(ax25_address)); + if (copy_from_user(&rose_callsign, (void *)arg, sizeof(ax25_address))) + return -EFAULT; if (ax25cmp(&rose_callsign, &null_ax25_address) != 0) ax25_listen_register(&rose_callsign, NULL); return 0; + case SIOCRSACCEPT: + if (sk->protinfo.rose->state == ROSE_STATE_5) { + rose_write_internal(sk, ROSE_CALL_ACCEPTED); + rose_start_idletimer(sk); + sk->protinfo.rose->condition = 0x00; + sk->protinfo.rose->vs = 0; + sk->protinfo.rose->va = 0; + sk->protinfo.rose->vr = 0; + sk->protinfo.rose->vl = 0; + sk->protinfo.rose->state = ROSE_STATE_3; + } + return 0; + default: return dev_ioctl(cmd, (void *)arg); } @@ -1228,7 +1267,7 @@ static int rose_get_info(char *buffer, char **start, off_t offset, int length, i cli(); - len += sprintf(buffer, "dest_addr dest_call src_addr src_call dev lci st vs vr va t t1 t2 t3 hb Snd-Q Rcv-Q\n"); + len += sprintf(buffer, "dest_addr dest_call src_addr src_call dev lci st vs vr va t t1 t2 t3 hb idle Snd-Q Rcv-Q\n"); for (s = rose_list; s != NULL; s = s->next) { if ((dev = s->protinfo.rose->device) == NULL) @@ -1245,17 +1284,24 @@ static int rose_get_info(char *buffer, char **start, off_t offset, int length, i else callsign = ax2asc(&s->protinfo.rose->source_call); - len += sprintf(buffer + len, "%-10s %-9s %-5s %3.3X %d %d %d %d %3d %3d %3d %3d %3d %5d %5d\n", - rose2asc(&s->protinfo.rose->source_addr), callsign, - devname, s->protinfo.rose->lci & 0x0FFF, + len += sprintf(buffer + len, "%-10s %-9s %-5s %3.3X %d %d %d %d %3lu %3lu %3lu %3lu %3lu %3lu/%03lu %5d %5d\n", + rose2asc(&s->protinfo.rose->source_addr), + callsign, + devname, + s->protinfo.rose->lci & 0x0FFF, s->protinfo.rose->state, - s->protinfo.rose->vs, s->protinfo.rose->vr, s->protinfo.rose->va, - s->protinfo.rose->timer / ROSE_SLOWHZ, - s->protinfo.rose->t1 / ROSE_SLOWHZ, - s->protinfo.rose->t2 / ROSE_SLOWHZ, - s->protinfo.rose->t3 / ROSE_SLOWHZ, - s->protinfo.rose->hb / ROSE_SLOWHZ, - atomic_read(&s->wmem_alloc), atomic_read(&s->rmem_alloc)); + s->protinfo.rose->vs, + s->protinfo.rose->vr, + s->protinfo.rose->va, + ax25_display_timer(&s->protinfo.rose->timer) / HZ, + s->protinfo.rose->t1 / HZ, + s->protinfo.rose->t2 / HZ, + s->protinfo.rose->t3 / HZ, + s->protinfo.rose->hb / HZ, + ax25_display_timer(&s->protinfo.rose->idletimer) / (60 * HZ), + s->protinfo.rose->idle / (60 * HZ), + atomic_read(&s->wmem_alloc), + atomic_read(&s->rmem_alloc)); pos = begin + len; @@ -1360,7 +1406,7 @@ __initfunc(void rose_proto_init(struct net_proto *pro)) sock_register(&rose_family_ops); register_netdevice_notifier(&rose_dev_notifier); - printk(KERN_INFO "G4KLX ROSE for Linux. Version 0.2 for AX25.035 Linux 2.1\n"); + printk(KERN_INFO "G4KLX ROSE for Linux. Version 0.3 for AX25.037 Linux 2.1\n"); ax25_protocol_register(AX25_P_ROSE, rose_route_frame); ax25_linkfail_register(rose_link_failed); diff --git a/net/rose/rose_dev.c b/net/rose/rose_dev.c index 73d0aa55293f..7861220eede8 100644 --- a/net/rose/rose_dev.c +++ b/net/rose/rose_dev.c @@ -1,5 +1,5 @@ /* - * ROSE release 002 + * ROSE release 003 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -101,7 +101,7 @@ static int rose_rebuild_header(struct sk_buff *skb) unsigned char *bp = (unsigned char *)skb->data; struct sk_buff *skbn; - if (!arp_find(bp + 7, skb)) { + if (arp_find(bp + 7, skb)) { kfree_skb(skb, FREE_WRITE); return 1; } diff --git a/net/rose/rose_in.c b/net/rose/rose_in.c index 3c3e17b2b8ae..1ac11528dc97 100644 --- a/net/rose/rose_in.c +++ b/net/rose/rose_in.c @@ -1,5 +1,5 @@ /* - * ROSE release 002 + * ROSE release 003 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -17,6 +17,8 @@ * * History * ROSE 001 Jonathan(G4KLX) Cloned from nr_in.c + * ROSE 002 Jonathan(G4KLX) Return cause and diagnostic codes from Clear Requests. + * ROSE 003 Jonathan(G4KLX) New timer architecture. */ #include @@ -48,6 +50,8 @@ static int rose_queue_rx_frame(struct sock *sk, struct sk_buff *skb, int more) { struct sk_buff *skbo, *skbn = skb; + rose_start_idletimer(sk); + if (more) { sk->protinfo.rose->fraglen += skb->len; skb_queue_tail(&sk->protinfo.rose->frag_queue, skb); @@ -89,8 +93,9 @@ static int rose_state1_machine(struct sock *sk, struct sk_buff *skb, int framety switch (frametype) { case ROSE_CALL_ACCEPTED: + rose_stop_timer(sk); + rose_start_idletimer(sk); sk->protinfo.rose->condition = 0x00; - sk->protinfo.rose->timer = 0; sk->protinfo.rose->vs = 0; sk->protinfo.rose->va = 0; sk->protinfo.rose->vr = 0; @@ -102,15 +107,9 @@ static int rose_state1_machine(struct sock *sk, struct sk_buff *skb, int framety break; case ROSE_CLEAR_REQUEST: - rose_clear_queues(sk); rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); - sk->protinfo.rose->state = ROSE_STATE_0; - sk->state = TCP_CLOSE; - sk->err = ECONNREFUSED; - sk->shutdown |= SEND_SHUTDOWN; - if (!sk->dead) - sk->state_change(sk); - sk->dead = 1; + rose_disconnect(sk, ECONNREFUSED, skb->data[3], skb->data[4]); + sk->protinfo.rose->neighbour->use--; break; default: @@ -131,15 +130,13 @@ static int rose_state2_machine(struct sock *sk, struct sk_buff *skb, int framety case ROSE_CLEAR_REQUEST: rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); + rose_disconnect(sk, 0, skb->data[3], skb->data[4]); + sk->protinfo.rose->neighbour->use--; + break; + case ROSE_CLEAR_CONFIRMATION: - rose_clear_queues(sk); - sk->protinfo.rose->state = ROSE_STATE_0; - sk->state = TCP_CLOSE; - sk->err = 0; - sk->shutdown |= SEND_SHUTDOWN; - if (!sk->dead) - sk->state_change(sk); - sk->dead = 1; + rose_disconnect(sk, 0, -1, -1); + sk->protinfo.rose->neighbour->use--; break; default: @@ -161,9 +158,10 @@ static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int framety switch (frametype) { case ROSE_RESET_REQUEST: + rose_stop_timer(sk); + rose_start_idletimer(sk); rose_write_internal(sk, ROSE_RESET_CONFIRMATION); sk->protinfo.rose->condition = 0x00; - sk->protinfo.rose->timer = 0; sk->protinfo.rose->vs = 0; sk->protinfo.rose->vr = 0; sk->protinfo.rose->va = 0; @@ -171,15 +169,9 @@ static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int framety break; case ROSE_CLEAR_REQUEST: - rose_clear_queues(sk); rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); - sk->protinfo.rose->state = ROSE_STATE_0; - sk->state = TCP_CLOSE; - sk->err = 0; - sk->shutdown |= SEND_SHUTDOWN; - if (!sk->dead) - sk->state_change(sk); - sk->dead = 1; + rose_disconnect(sk, 0, skb->data[3], skb->data[4]); + sk->protinfo.rose->neighbour->use--; break; case ROSE_RR: @@ -189,7 +181,6 @@ static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int framety else sk->protinfo.rose->condition &= ~ROSE_COND_PEER_RX_BUSY; if (!rose_validate_nr(sk, nr)) { - rose_clear_queues(sk); rose_write_internal(sk, ROSE_RESET_REQUEST); sk->protinfo.rose->condition = 0x00; sk->protinfo.rose->vs = 0; @@ -197,7 +188,8 @@ static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int framety sk->protinfo.rose->va = 0; sk->protinfo.rose->vl = 0; sk->protinfo.rose->state = ROSE_STATE_4; - sk->protinfo.rose->timer = sk->protinfo.rose->t2; + rose_start_t2timer(sk); + rose_stop_idletimer(sk); } else { if (sk->protinfo.rose->condition & ROSE_COND_PEER_RX_BUSY) { sk->protinfo.rose->va = nr; @@ -210,7 +202,6 @@ static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int framety case ROSE_DATA: /* XXX */ sk->protinfo.rose->condition &= ~ROSE_COND_PEER_RX_BUSY; if (!rose_validate_nr(sk, nr)) { - rose_clear_queues(sk); rose_write_internal(sk, ROSE_RESET_REQUEST); sk->protinfo.rose->condition = 0x00; sk->protinfo.rose->vs = 0; @@ -218,7 +209,8 @@ static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int framety sk->protinfo.rose->va = 0; sk->protinfo.rose->vl = 0; sk->protinfo.rose->state = ROSE_STATE_4; - sk->protinfo.rose->timer = sk->protinfo.rose->t2; + rose_start_t2timer(sk); + rose_stop_idletimer(sk); break; } if (sk->protinfo.rose->condition & ROSE_COND_PEER_RX_BUSY) { @@ -242,11 +234,11 @@ static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int framety */ if (((sk->protinfo.rose->vl + sysctl_rose_window_size) % ROSE_MODULUS) == sk->protinfo.rose->vr) { sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING; - sk->protinfo.rose->timer = 0; + rose_stop_timer(sk); rose_enquiry_response(sk); } else { sk->protinfo.rose->condition |= ROSE_COND_ACK_PENDING; - sk->protinfo.rose->timer = sk->protinfo.rose->hb; + rose_start_hbtimer(sk); } break; @@ -270,7 +262,8 @@ static int rose_state4_machine(struct sock *sk, struct sk_buff *skb, int framety case ROSE_RESET_REQUEST: rose_write_internal(sk, ROSE_RESET_CONFIRMATION); case ROSE_RESET_CONFIRMATION: - sk->protinfo.rose->timer = 0; + rose_stop_timer(sk); + rose_start_idletimer(sk); sk->protinfo.rose->condition = 0x00; sk->protinfo.rose->va = 0; sk->protinfo.rose->vr = 0; @@ -280,16 +273,9 @@ static int rose_state4_machine(struct sock *sk, struct sk_buff *skb, int framety break; case ROSE_CLEAR_REQUEST: - rose_clear_queues(sk); rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); - sk->protinfo.rose->timer = 0; - sk->protinfo.rose->state = ROSE_STATE_0; - sk->state = TCP_CLOSE; - sk->err = 0; - sk->shutdown |= SEND_SHUTDOWN; - if (!sk->dead) - sk->state_change(sk); - sk->dead = 1; + rose_disconnect(sk, 0, skb->data[3], skb->data[4]); + sk->protinfo.rose->neighbour->use--; break; default: @@ -299,6 +285,22 @@ static int rose_state4_machine(struct sock *sk, struct sk_buff *skb, int framety return 0; } +/* + * State machine for state 5, Awaiting Call Acceptance State. + * The handling of the timer(s) is in file rose_timer.c + * Handling of state 0 and connection release is in af_rose.c. + */ +static int rose_state5_machine(struct sock *sk, struct sk_buff *skb, int frametype) +{ + if (frametype == ROSE_CLEAR_REQUEST) { + rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); + rose_disconnect(sk, 0, skb->data[3], skb->data[4]); + sk->protinfo.rose->neighbour->use--; + } + + return 0; +} + /* Higher level upcall for a LAPB frame */ int rose_process_rx_frame(struct sock *sk, struct sk_buff *skb) { @@ -307,8 +309,6 @@ int rose_process_rx_frame(struct sock *sk, struct sk_buff *skb) if (sk->protinfo.rose->state == ROSE_STATE_0) return 0; - del_timer(&sk->timer); - frametype = rose_decode(skb, &ns, &nr, &q, &d, &m); switch (sk->protinfo.rose->state) { @@ -324,9 +324,12 @@ int rose_process_rx_frame(struct sock *sk, struct sk_buff *skb) case ROSE_STATE_4: queued = rose_state4_machine(sk, skb, frametype); break; + case ROSE_STATE_5: + queued = rose_state5_machine(sk, skb, frametype); + break; } - rose_set_timer(sk); + rose_kick(sk); return queued; } diff --git a/net/rose/rose_link.c b/net/rose/rose_link.c index 86626511e735..b481e485feaf 100644 --- a/net/rose/rose_link.c +++ b/net/rose/rose_link.c @@ -1,5 +1,5 @@ /* - * ROSE release 002 + * ROSE release 003 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -11,6 +11,7 @@ * * History * ROSE 001 Jonathan(G4KLX) Cloned from rose_timer.c + * ROSE 003 Jonathan(G4KLX) New timer architecture. */ #include @@ -38,53 +39,64 @@ #include #include -static void rose_link_timer(unsigned long); +static void rose_ftimer_expiry(unsigned long); +static void rose_t0timer_expiry(unsigned long); -/* - * Linux set timer - */ -void rose_link_set_timer(struct rose_neigh *neigh) +void rose_start_ftimer(struct rose_neigh *neigh) { - unsigned long flags; + del_timer(&neigh->ftimer); - save_flags(flags); cli(); - del_timer(&neigh->timer); - restore_flags(flags); + neigh->ftimer.data = (unsigned long)neigh; + neigh->ftimer.function = &rose_ftimer_expiry; + neigh->ftimer.expires = jiffies + sysctl_rose_link_fail_timeout; - neigh->timer.data = (unsigned long)neigh; - neigh->timer.function = &rose_link_timer; - neigh->timer.expires = jiffies + (HZ / 10); + add_timer(&neigh->ftimer); +} + +void rose_start_t0timer(struct rose_neigh *neigh) +{ + del_timer(&neigh->t0timer); - add_timer(&neigh->timer); + neigh->t0timer.data = (unsigned long)neigh; + neigh->t0timer.function = &rose_t0timer_expiry; + neigh->t0timer.expires = jiffies + sysctl_rose_restart_request_timeout; + + add_timer(&neigh->t0timer); } -/* - * ROSE Link Timer - * - * This routine is called every 100ms. Decrement timer by this - * amount - if expired then process the event. - */ -static void rose_link_timer(unsigned long param) +void rose_stop_ftimer(struct rose_neigh *neigh) { - struct rose_neigh *neigh = (struct rose_neigh *)param; + del_timer(&neigh->ftimer); +} - if (neigh->ftimer > 0) - neigh->ftimer--; +void rose_stop_t0timer(struct rose_neigh *neigh) +{ + del_timer(&neigh->t0timer); +} - if (neigh->t0timer > 0) { - neigh->t0timer--; +int rose_ftimer_running(struct rose_neigh *neigh) +{ + return (neigh->ftimer.prev != NULL || neigh->ftimer.next != NULL); +} - if (neigh->t0timer == 0) { - rose_transmit_restart_request(neigh); - neigh->dce_mode = 0; - neigh->t0timer = sysctl_rose_restart_request_timeout; - } - } +int rose_t0timer_running(struct rose_neigh *neigh) +{ + return (neigh->t0timer.prev != NULL || neigh->t0timer.next != NULL); +} - if (neigh->ftimer > 0 || neigh->t0timer > 0) - rose_link_set_timer(neigh); - else - del_timer(&neigh->timer); +static void rose_ftimer_expiry(unsigned long param) +{ +} + +static void rose_t0timer_expiry(unsigned long param) +{ + struct rose_neigh *neigh = (struct rose_neigh *)param; + + rose_transmit_restart_request(neigh); + + neigh->dce_mode = 0; + + rose_start_t0timer(neigh); } /* @@ -101,7 +113,9 @@ static int rose_send_frame(struct sk_buff *skb, struct rose_neigh *neigh) else rose_call = &rose_callsign; - return ax25_send_frame(skb, 256, rose_call, &neigh->callsign, neigh->digipeat, neigh->dev); + neigh->ax25 = ax25_send_frame(skb, 256, rose_call, &neigh->callsign, neigh->digipeat, neigh->dev); + + return (neigh->ax25 != NULL); } /* @@ -118,7 +132,9 @@ static int rose_link_up(struct rose_neigh *neigh) else rose_call = &rose_callsign; - return ax25_link_up(rose_call, &neigh->callsign, neigh->digipeat, neigh->dev); + neigh->ax25 = ax25_find_cb(rose_call, &neigh->callsign, neigh->digipeat, neigh->dev); + + return (neigh->ax25 != NULL); } /* @@ -130,17 +146,15 @@ void rose_link_rx_restart(struct sk_buff *skb, struct rose_neigh *neigh, unsigne switch (frametype) { case ROSE_RESTART_REQUEST: - neigh->t0timer = 0; + rose_stop_t0timer(neigh); neigh->restarted = 1; - neigh->dce_mode = (skb->data[3] == 0x00); - del_timer(&neigh->timer); + neigh->dce_mode = (skb->data[3] == ROSE_DTE_ORIGINATED); rose_transmit_restart_confirmation(neigh); break; case ROSE_RESTART_CONFIRMATION: - neigh->t0timer = 0; + rose_stop_t0timer(neigh); neigh->restarted = 1; - del_timer(&neigh->timer); break; case ROSE_DIAGNOSTIC: @@ -181,7 +195,7 @@ void rose_transmit_restart_request(struct rose_neigh *neigh) *dptr++ = ROSE_GFI; *dptr++ = 0x00; *dptr++ = ROSE_RESTART_REQUEST; - *dptr++ = 0x00; + *dptr++ = ROSE_DTE_ORIGINATED; *dptr++ = 0; if (!rose_send_frame(skb, neigh)) @@ -247,7 +261,7 @@ void rose_transmit_diagnostic(struct rose_neigh *neigh, unsigned char diag) * This routine is called when a Clear Request is needed outside of the context * of a connected socket. */ -void rose_transmit_clear_request(struct rose_neigh *neigh, unsigned int lci, unsigned char cause) +void rose_transmit_clear_request(struct rose_neigh *neigh, unsigned int lci, unsigned char cause, unsigned char diagnostic) { struct sk_buff *skb; unsigned char *dptr; @@ -267,7 +281,7 @@ void rose_transmit_clear_request(struct rose_neigh *neigh, unsigned int lci, uns *dptr++ = ((lci >> 0) & 0xFF); *dptr++ = ROSE_CLEAR_REQUEST; *dptr++ = cause; - *dptr++ = 0x00; + *dptr++ = diagnostic; if (!rose_send_frame(skb, neigh)) kfree_skb(skb, FREE_WRITE); @@ -294,11 +308,10 @@ void rose_transmit_link(struct sk_buff *skb, struct rose_neigh *neigh) } else { skb_queue_tail(&neigh->queue, skb); - if (neigh->t0timer == 0) { + if (!rose_t0timer_running(neigh)) { rose_transmit_restart_request(neigh); neigh->dce_mode = 0; - neigh->t0timer = sysctl_rose_restart_request_timeout; - rose_link_set_timer(neigh); + rose_start_t0timer(neigh); } } } diff --git a/net/rose/rose_out.c b/net/rose/rose_out.c index f0e212dc3fa8..0ed9f7480a23 100644 --- a/net/rose/rose_out.c +++ b/net/rose/rose_out.c @@ -1,5 +1,5 @@ /* - * ROSE release 002 + * ROSE release 003 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -11,6 +11,7 @@ * * History * ROSE 001 Jonathan(G4KLX) Cloned from nr_out.c + * ROSE 003 Jonathan(G4KLX) New timer architecture. */ #include @@ -80,8 +81,7 @@ void rose_output(struct sock *sk, struct sk_buff *skb) skb_queue_tail(&sk->write_queue, skb); /* Throw it on the queue */ } - if (sk->protinfo.rose->state == ROSE_STATE_3) - rose_kick(sk); + rose_kick(sk); } /* @@ -96,6 +96,8 @@ static void rose_send_iframe(struct sock *sk, struct sk_buff *skb) skb->data[2] |= (sk->protinfo.rose->vr << 5) & 0xE0; skb->data[2] |= (sk->protinfo.rose->vs << 1) & 0x0E; + rose_start_idletimer(sk); + rose_transmit_link(skb, sk->protinfo.rose->neighbour); } @@ -104,36 +106,41 @@ void rose_kick(struct sock *sk) struct sk_buff *skb; unsigned short end; - del_timer(&sk->timer); + if (sk->protinfo.rose->state != ROSE_STATE_3) + return; + + if (sk->protinfo.rose->condition & ROSE_COND_PEER_RX_BUSY) + return; + + if (skb_peek(&sk->write_queue) == NULL) + return; end = (sk->protinfo.rose->va + sysctl_rose_window_size) % ROSE_MODULUS; - if (!(sk->protinfo.rose->condition & ROSE_COND_PEER_RX_BUSY) && - sk->protinfo.rose->vs != end && - skb_peek(&sk->write_queue) != NULL) { - /* - * Transmit data until either we're out of data to send or - * the window is full. - */ + if (sk->protinfo.rose->vs == end) + return; - skb = skb_dequeue(&sk->write_queue); + /* + * Transmit data until either we're out of data to send or + * the window is full. + */ - do { - /* - * Transmit the frame. - */ - rose_send_iframe(sk, skb); + skb = skb_dequeue(&sk->write_queue); - sk->protinfo.rose->vs = (sk->protinfo.rose->vs + 1) % ROSE_MODULUS; + do { + /* + * Transmit the frame. + */ + rose_send_iframe(sk, skb); - } while (sk->protinfo.rose->vs != end && (skb = skb_dequeue(&sk->write_queue)) != NULL); + sk->protinfo.rose->vs = (sk->protinfo.rose->vs + 1) % ROSE_MODULUS; - sk->protinfo.rose->vl = sk->protinfo.rose->vr; - sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING; - sk->protinfo.rose->timer = 0; - } + } while (sk->protinfo.rose->vs != end && (skb = skb_dequeue(&sk->write_queue)) != NULL); - rose_set_timer(sk); + sk->protinfo.rose->vl = sk->protinfo.rose->vr; + sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING; + + rose_stop_timer(sk); } /* @@ -150,7 +157,8 @@ void rose_enquiry_response(struct sock *sk) sk->protinfo.rose->vl = sk->protinfo.rose->vr; sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING; - sk->protinfo.rose->timer = 0; + + rose_stop_timer(sk); } void rose_check_iframes_acked(struct sock *sk, unsigned short nr) diff --git a/net/rose/rose_route.c b/net/rose/rose_route.c index 5b1338609b5a..43358644c4aa 100644 --- a/net/rose/rose_route.c +++ b/net/rose/rose_route.c @@ -1,5 +1,5 @@ /* - * ROSE release 002 + * ROSE release 003 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -15,6 +15,8 @@ * address masks. * ROSE 002 Jonathan(G4KLX) Uprated through routing of packets. * Routing loop detection. + * ROSE 003 Jonathan(G4KLX) New timer architecture. + * Added use count to neighbours. */ #include @@ -80,24 +82,32 @@ static int rose_add_node(struct rose_route_struct *rose_route, struct device *de rose_neigh->callsign = rose_route->neighbour; rose_neigh->digipeat = NULL; + rose_neigh->ax25 = NULL; rose_neigh->dev = dev; rose_neigh->count = 0; + rose_neigh->use = 0; rose_neigh->dce_mode = 0; rose_neigh->number = rose_neigh_no++; rose_neigh->restarted = 0; + skb_queue_head_init(&rose_neigh->queue); - rose_neigh->t0timer = 0; - rose_neigh->ftimer = 0; - init_timer(&rose_neigh->timer); + + init_timer(&rose_neigh->ftimer); + init_timer(&rose_neigh->t0timer); if (rose_route->ndigis != 0) { if ((rose_neigh->digipeat = kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL) { kfree(rose_neigh); return -ENOMEM; } - rose_neigh->digipeat->ndigi = rose_route->ndigis; - for (i = 0; i < rose_route->ndigis; i++) - rose_neigh->digipeat->calls[i] = rose_route->digipeaters[i]; + + rose_neigh->digipeat->ndigi = rose_route->ndigis; + rose_neigh->digipeat->lastrepeat = -1; + + for (i = 0; i < rose_route->ndigis; i++) { + rose_neigh->digipeat->calls[i] = rose_route->digipeaters[i]; + rose_neigh->digipeat->repeated[i] = 0; + } } save_flags(flags); cli(); @@ -207,13 +217,13 @@ static void rose_remove_neigh(struct rose_neigh *rose_neigh) unsigned long flags; struct sk_buff *skb; - del_timer(&rose_neigh->timer); + rose_stop_ftimer(rose_neigh); + rose_stop_t0timer(rose_neigh); while ((skb = skb_dequeue(&rose_neigh->queue)) != NULL) kfree_skb(skb, FREE_WRITE); - save_flags(flags); - cli(); + save_flags(flags); cli(); if ((s = rose_neigh_list) == rose_neigh) { rose_neigh_list = rose_neigh->next; @@ -244,9 +254,14 @@ static void rose_remove_route(struct rose_route *rose_route) { struct rose_route *s; unsigned long flags; - - save_flags(flags); - cli(); + + if (rose_route->neigh1 != NULL) + rose_route->neigh1->use--; + + if (rose_route->neigh2 != NULL) + rose_route->neigh2->use--; + + save_flags(flags); cli(); if ((s = rose_route_list) == rose_route) { rose_route_list = rose_route->next; @@ -295,7 +310,7 @@ static int rose_del_node(struct rose_route_struct *rose_route, struct device *de if (rose_node->neighbour[i] == rose_neigh) { rose_neigh->count--; - if (rose_neigh->count == 0) + if (rose_neigh->count == 0 && rose_neigh->use == 0) rose_remove_neigh(rose_neigh); rose_node->count--; @@ -380,6 +395,35 @@ void rose_route_device_down(struct device *dev) } } +/* + * Clear all nodes and neighbours out, except for neighbours with + * active connections going through them. + */ +static int rose_clear_routes(void) +{ + struct rose_neigh *s, *rose_neigh = rose_neigh_list; + struct rose_node *t, *rose_node = rose_node_list; + + while (rose_node != NULL) { + t = rose_node; + rose_node = rose_node->next; + + rose_remove_node(t); + } + + while (rose_neigh != NULL) { + s = rose_neigh; + rose_neigh = rose_neigh->next; + + s->count = 0; + + if (s->use == 0) + rose_remove_neigh(s); + } + + return 0; +} + /* * Check that the device given is a valid AX.25 interface that is "up". */ @@ -440,20 +484,31 @@ struct rose_route *rose_route_free_lci(unsigned int lci, struct rose_neigh *neig /* * Find a neighbour given a ROSE address. */ -struct rose_neigh *rose_get_neigh(rose_address *addr) +struct rose_neigh *rose_get_neigh(rose_address *addr, unsigned char *cause, unsigned char *diagnostic) { struct rose_node *node; + int failed = 0; int i; for (node = rose_node_list; node != NULL; node = node->next) { if (rosecmpm(addr, &node->address, node->mask) == 0) { for (i = 0; i < node->count; i++) { - if (node->neighbour[i]->ftimer == 0) + if (!rose_ftimer_running(node->neighbour[i])) return node->neighbour[i]; + else + failed = 1; } } } + if (failed) { + *cause = ROSE_OUT_OF_ORDER; + *diagnostic = 0; + } else { + *cause = ROSE_NOT_OBTAINABLE; + *diagnostic = 0; + } + return NULL; } @@ -464,14 +519,12 @@ int rose_rt_ioctl(unsigned int cmd, void *arg) { struct rose_route_struct rose_route; struct device *dev; - int err; switch (cmd) { case SIOCADDRT: - if ((err = verify_area(VERIFY_READ, arg, sizeof(struct rose_route_struct))) != 0) - return err; - copy_from_user(&rose_route, arg, sizeof(struct rose_route_struct)); + if (copy_from_user(&rose_route, arg, sizeof(struct rose_route_struct))) + return -EFAULT; if ((dev = rose_ax25_dev_get(rose_route.device)) == NULL) return -EINVAL; if (rose_dev_get(&rose_route.address) != NULL) /* Can't add routes to ourself */ @@ -482,13 +535,15 @@ int rose_rt_ioctl(unsigned int cmd, void *arg) return rose_add_node(&rose_route, dev); case SIOCDELRT: - if ((err = verify_area(VERIFY_READ, arg, sizeof(struct rose_route_struct))) != 0) - return err; - copy_from_user(&rose_route, arg, sizeof(struct rose_route_struct)); + if (copy_from_user(&rose_route, arg, sizeof(struct rose_route_struct))) + return -EFAULT; if ((dev = rose_ax25_dev_get(rose_route.device)) == NULL) return -EINVAL; return rose_del_node(&rose_route, dev); + case SIOCRSCLRRT: + return rose_clear_routes(); + default: return -EINVAL; } @@ -502,10 +557,9 @@ static void rose_del_route_by_neigh(struct rose_neigh *rose_neigh) struct sk_buff *skb; rose_neigh->restarted = 0; - rose_neigh->t0timer = 0; - rose_neigh->ftimer = sysctl_rose_link_fail_timeout; - rose_link_set_timer(rose_neigh); + rose_stop_t0timer(rose_neigh); + rose_start_ftimer(rose_neigh); while ((skb = skb_dequeue(&rose_neigh->queue)) != NULL) kfree_skb(skb, FREE_WRITE); @@ -523,13 +577,15 @@ static void rose_del_route_by_neigh(struct rose_neigh *rose_neigh) } if (rose_route->neigh1 == rose_neigh) { + rose_route->neigh1->use--; rose_route->neigh1 = NULL; - rose_transmit_clear_request(rose_route->neigh2, rose_route->lci2, 0x0D); + rose_transmit_clear_request(rose_route->neigh2, rose_route->lci2, ROSE_OUT_OF_ORDER, 0); } if (rose_route->neigh2 == rose_neigh) { + rose_route->neigh2->use--; rose_route->neigh2 = NULL; - rose_transmit_clear_request(rose_route->neigh1, rose_route->lci1, 0x0D); + rose_transmit_clear_request(rose_route->neigh1, rose_route->lci1, ROSE_OUT_OF_ORDER, 0); } rose_route = rose_route->next; @@ -541,16 +597,18 @@ static void rose_del_route_by_neigh(struct rose_neigh *rose_neigh) * then don't use that neighbour until it is reset. Blow away all through * routes and connections using this route. */ -void rose_link_failed(ax25_address *callsign, struct device *dev) +void rose_link_failed(ax25_cb *ax25, int reason) { struct rose_neigh *rose_neigh; for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next) - if (ax25cmp(&rose_neigh->callsign, callsign) == 0 && rose_neigh->dev == dev) + if (rose_neigh->ax25 == ax25) break; if (rose_neigh == NULL) return; + rose_neigh->ax25 = NULL; + rose_del_route_by_neigh(rose_neigh); rose_kill_by_neigh(rose_neigh); } @@ -583,6 +641,7 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) struct sock *sk; unsigned short frametype; unsigned int lci, new_lci; + unsigned char cause, diagnostic; struct device *dev; unsigned long flags; @@ -604,7 +663,7 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) /* * Obviously the link is working, halt the ftimer. */ - rose_neigh->ftimer = 0; + rose_stop_ftimer(rose_neigh); /* * LCI of zero is always for us, and its always a restart @@ -631,7 +690,7 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) return rose_rx_call_request(skb, dev, rose_neigh, lci); if (!sysctl_rose_routing_control) { - rose_transmit_clear_request(rose_neigh, lci, 0x0D); + rose_transmit_clear_request(rose_neigh, lci, ROSE_NOT_OBTAINABLE, 0); return 0; } @@ -679,7 +738,10 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) if (frametype != ROSE_CALL_REQUEST) /* XXX */ return 0; - rose_parse_facilities(skb, &facilities); + if (!rose_parse_facilities(skb, &facilities)) { + rose_transmit_clear_request(rose_neigh, lci, ROSE_INVALID_FACILITY, 76); + return 0; + } /* * Check for routing loops. @@ -691,25 +753,25 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) ax25cmp(&facilities.source_call, &rose_route->dest_call) == 0) { printk(KERN_DEBUG "ROSE: routing loop from %s\n", rose2asc(src_addr)); printk(KERN_DEBUG "ROSE: to %s\n", rose2asc(dest_addr)); - rose_transmit_clear_request(rose_neigh, lci, 0x0D); + rose_transmit_clear_request(rose_neigh, lci, ROSE_NOT_OBTAINABLE, 120); return 0; } } - if ((new_neigh = rose_get_neigh(dest_addr)) == NULL) { - printk(KERN_DEBUG "ROSE: no route to %s\n", rose2asc(dest_addr)); - rose_transmit_clear_request(rose_neigh, lci, 0x0D); + if ((new_neigh = rose_get_neigh(dest_addr, &cause, &diagnostic)) == NULL) { + if (cause == ROSE_NOT_OBTAINABLE) + printk(KERN_DEBUG "ROSE: no route to %s\n", rose2asc(dest_addr)); + rose_transmit_clear_request(rose_neigh, lci, cause, diagnostic); return 0; } if ((new_lci = rose_new_lci(new_neigh)) == 0) { - printk(KERN_DEBUG "ROSE: no spare VCs to %s\n", rose2asc(dest_addr)); - rose_transmit_clear_request(rose_neigh, lci, 0x0D); + rose_transmit_clear_request(rose_neigh, lci, ROSE_NETWORK_CONGESTION, 71); return 0; } if ((rose_route = kmalloc(sizeof(*rose_route), GFP_ATOMIC)) == NULL) { - rose_transmit_clear_request(rose_neigh, lci, 0x0D); + rose_transmit_clear_request(rose_neigh, lci, ROSE_NETWORK_CONGESTION, 120); return 0; } @@ -723,6 +785,9 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) rose_route->lci2 = new_lci; rose_route->neigh2 = new_neigh; + rose_route->neigh1->use++; + rose_route->neigh2->use++; + save_flags(flags); cli(); rose_route->next = rose_route_list; rose_route_list = rose_route; @@ -790,21 +855,30 @@ int rose_neigh_get_info(char *buffer, char **start, off_t offset, int len = 0; off_t pos = 0; off_t begin = 0; + int i; cli(); - len += sprintf(buffer, "addr callsign dev count mode restart t0 tf\n"); + len += sprintf(buffer, "addr callsign dev count use mode restart t0 tf digipeaters\n"); for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next) { - len += sprintf(buffer + len, "%05d %-9s %-4s %3d %3s %3s %3d %3d\n", + len += sprintf(buffer + len, "%05d %-9s %-4s %3d %3d %3s %3s %3lu %3lu", rose_neigh->number, ax2asc(&rose_neigh->callsign), rose_neigh->dev ? rose_neigh->dev->name : "???", rose_neigh->count, + rose_neigh->use, (rose_neigh->dce_mode) ? "DCE" : "DTE", (rose_neigh->restarted) ? "yes" : "no", - rose_neigh->t0timer / ROSE_SLOWHZ, - rose_neigh->ftimer / ROSE_SLOWHZ); + ax25_display_timer(&rose_neigh->t0timer) / HZ, + ax25_display_timer(&rose_neigh->ftimer) / HZ); + + if (rose_neigh->digipeat != NULL) { + for (i = 0; i < rose_neigh->digipeat->ndigi; i++) + len += sprintf(buffer + len, " %s", ax2asc(&rose_neigh->digipeat->calls[i])); + } + + len += sprintf(buffer + len, "\n"); pos = begin + len; diff --git a/net/rose/rose_subr.c b/net/rose/rose_subr.c index 4e0530cb4962..ee710bd6e353 100644 --- a/net/rose/rose_subr.c +++ b/net/rose/rose_subr.c @@ -1,5 +1,5 @@ /* - * ROSE release 002 + * ROSE release 003 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -11,6 +11,8 @@ * * History * ROSE 001 Jonathan(G4KLX) Cloned from nr_subr.c + * ROSE 002 Jonathan(G4KLX) Centralised disconnect processing. + * ROSE 003 Jonathan(G4KLX) Added use count to neighbours. */ #include @@ -92,12 +94,8 @@ void rose_write_internal(struct sock *sk, int frametype) case ROSE_CALL_ACCEPTED: case ROSE_CLEAR_REQUEST: case ROSE_RESET_REQUEST: - case ROSE_DIAGNOSTIC: len += 2; break; - case ROSE_INTERRUPT: - len += 1; - break; } if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL) @@ -137,24 +135,23 @@ void rose_write_internal(struct sock *sk, int frametype) break; case ROSE_CLEAR_REQUEST: - case ROSE_RESET_REQUEST: *dptr++ = ROSE_GFI | lci1; *dptr++ = lci2; *dptr++ = frametype; - *dptr++ = 0x00; /* XXX */ - *dptr++ = 0x00; /* XXX */ + *dptr++ = sk->protinfo.rose->cause; + *dptr++ = sk->protinfo.rose->diagnostic; break; - case ROSE_INTERRUPT: + case ROSE_RESET_REQUEST: *dptr++ = ROSE_GFI | lci1; *dptr++ = lci2; *dptr++ = frametype; - *dptr++ = 0x00; /* XXX */ + *dptr++ = ROSE_DTE_ORIGINATED; + *dptr++ = 0; break; case ROSE_RR: case ROSE_RNR: - case ROSE_REJ: *dptr++ = ROSE_GFI | lci1; *dptr++ = lci2; *dptr = frametype; @@ -162,7 +159,6 @@ void rose_write_internal(struct sock *sk, int frametype) break; case ROSE_CLEAR_CONFIRMATION: - case ROSE_INTERRUPT_CONFIRMATION: case ROSE_RESET_CONFIRMATION: *dptr++ = ROSE_GFI | lci1; *dptr++ = lci2; @@ -191,23 +187,15 @@ int rose_decode(struct sk_buff *skb, int *ns, int *nr, int *q, int *d, int *m) case ROSE_CALL_ACCEPTED: case ROSE_CLEAR_REQUEST: case ROSE_CLEAR_CONFIRMATION: - case ROSE_INTERRUPT: - case ROSE_INTERRUPT_CONFIRMATION: case ROSE_RESET_REQUEST: case ROSE_RESET_CONFIRMATION: - case ROSE_RESTART_REQUEST: - case ROSE_RESTART_CONFIRMATION: - case ROSE_REGISTRATION_REQUEST: - case ROSE_REGISTRATION_CONFIRMATION: - case ROSE_DIAGNOSTIC: return frame[2]; default: break; } if ((frame[2] & 0x1F) == ROSE_RR || - (frame[2] & 0x1F) == ROSE_RNR || - (frame[2] & 0x1F) == ROSE_REJ) { + (frame[2] & 0x1F) == ROSE_RNR) { *nr = (frame[2] >> 5) & 0x07; return frame[2] & 0x1F; } @@ -437,4 +425,30 @@ int rose_create_facilities(unsigned char *buffer, rose_cb *rose) return len; } +void rose_disconnect(struct sock *sk, int reason, int cause, int diagnostic) +{ + rose_stop_timer(sk); + rose_stop_idletimer(sk); + + rose_clear_queues(sk); + + sk->protinfo.rose->lci = 0; + sk->protinfo.rose->state = ROSE_STATE_0; + + if (cause != -1) + sk->protinfo.rose->cause = cause; + + if (diagnostic != -1) + sk->protinfo.rose->diagnostic = diagnostic; + + sk->state = TCP_CLOSE; + sk->err = reason; + sk->shutdown |= SEND_SHUTDOWN; + + if (!sk->dead) + sk->state_change(sk); + + sk->dead = 1; +} + #endif diff --git a/net/rose/rose_timer.c b/net/rose/rose_timer.c index 572975e5d314..718a64ec04d2 100644 --- a/net/rose/rose_timer.c +++ b/net/rose/rose_timer.c @@ -1,5 +1,5 @@ /* - * ROSE release 002 + * ROSE release 003 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -11,6 +11,8 @@ * * History * ROSE 001 Jonathan(G4KLX) Cloned from nr_timer.c + * ROSE 003 Jonathan(G4KLX) New timer architecture. + * Implemented idle timer. */ #include @@ -37,42 +39,103 @@ #include #include -static void rose_timer(unsigned long); +static void rose_heartbeat_expiry(unsigned long); +static void rose_timer_expiry(unsigned long); +static void rose_idletimer_expiry(unsigned long); -/* - * Linux set timer - */ -void rose_set_timer(struct sock *sk) +void rose_start_heartbeat(struct sock *sk) { - unsigned long flags; - - save_flags(flags); cli(); del_timer(&sk->timer); - restore_flags(flags); sk->timer.data = (unsigned long)sk; - sk->timer.function = &rose_timer; - sk->timer.expires = jiffies + (HZ / 10); + sk->timer.function = &rose_heartbeat_expiry; + sk->timer.expires = jiffies + 5 * HZ; add_timer(&sk->timer); } -/* - * ROSE Timer - * - * This routine is called every 100ms. Decrement timer by this - * amount - if expired then process the event. - */ -static void rose_timer(unsigned long param) +void rose_start_t1timer(struct sock *sk) +{ + del_timer(&sk->protinfo.rose->timer); + + sk->protinfo.rose->timer.data = (unsigned long)sk; + sk->protinfo.rose->timer.function = &rose_timer_expiry; + sk->protinfo.rose->timer.expires = jiffies + sk->protinfo.rose->t1; + + add_timer(&sk->protinfo.rose->timer); +} + +void rose_start_t2timer(struct sock *sk) +{ + del_timer(&sk->protinfo.rose->timer); + + sk->protinfo.rose->timer.data = (unsigned long)sk; + sk->protinfo.rose->timer.function = &rose_timer_expiry; + sk->protinfo.rose->timer.expires = jiffies + sk->protinfo.rose->t2; + + add_timer(&sk->protinfo.rose->timer); +} + +void rose_start_t3timer(struct sock *sk) +{ + del_timer(&sk->protinfo.rose->timer); + + sk->protinfo.rose->timer.data = (unsigned long)sk; + sk->protinfo.rose->timer.function = &rose_timer_expiry; + sk->protinfo.rose->timer.expires = jiffies + sk->protinfo.rose->t3; + + add_timer(&sk->protinfo.rose->timer); +} + +void rose_start_hbtimer(struct sock *sk) +{ + del_timer(&sk->protinfo.rose->timer); + + sk->protinfo.rose->timer.data = (unsigned long)sk; + sk->protinfo.rose->timer.function = &rose_timer_expiry; + sk->protinfo.rose->timer.expires = jiffies + sk->protinfo.rose->hb; + + add_timer(&sk->protinfo.rose->timer); +} + +void rose_start_idletimer(struct sock *sk) +{ + del_timer(&sk->protinfo.rose->idletimer); + + if (sk->protinfo.rose->idle > 0) { + sk->protinfo.rose->idletimer.data = (unsigned long)sk; + sk->protinfo.rose->idletimer.function = &rose_idletimer_expiry; + sk->protinfo.rose->idletimer.expires = jiffies + sk->protinfo.rose->idle; + + add_timer(&sk->protinfo.rose->idletimer); + } +} + +void rose_stop_heartbeat(struct sock *sk) +{ + del_timer(&sk->timer); +} + +void rose_stop_timer(struct sock *sk) +{ + del_timer(&sk->protinfo.rose->timer); +} + +void rose_stop_idletimer(struct sock *sk) +{ + del_timer(&sk->protinfo.rose->idletimer); +} + +static void rose_heartbeat_expiry(unsigned long param) { struct sock *sk = (struct sock *)param; switch (sk->protinfo.rose->state) { + case ROSE_STATE_0: /* Magic here: If we listen() and a new link dies before it is accepted() it isn't 'dead' so doesn't get removed. */ if (sk->destroy || (sk->state == TCP_LISTEN && sk->dead)) { - del_timer(&sk->timer); rose_destroy_socket(sk); return; } @@ -87,57 +150,62 @@ static void rose_timer(unsigned long param) sk->protinfo.rose->condition &= ~ROSE_COND_OWN_RX_BUSY; sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING; sk->protinfo.rose->vl = sk->protinfo.rose->vr; - sk->protinfo.rose->timer = 0; rose_write_internal(sk, ROSE_RR); + rose_stop_timer(sk); /* HB */ break; } - /* - * Check for frames to transmit. - */ - rose_kick(sk); - break; - - default: break; } - if (sk->protinfo.rose->timer == 0 || --sk->protinfo.rose->timer > 0) { - rose_set_timer(sk); - return; - } + rose_start_heartbeat(sk); +} + +static void rose_timer_expiry(unsigned long param) +{ + struct sock *sk = (struct sock *)param; - /* - * Timer has expired, it may have been T1, T2, T3 or HB. We can tell - * by the socket state. - */ switch (sk->protinfo.rose->state) { - case ROSE_STATE_3: /* HB */ - if (sk->protinfo.rose->condition & ROSE_COND_ACK_PENDING) { - sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING; - rose_enquiry_response(sk); - } - break; case ROSE_STATE_1: /* T1 */ case ROSE_STATE_4: /* T2 */ rose_write_internal(sk, ROSE_CLEAR_REQUEST); sk->protinfo.rose->state = ROSE_STATE_2; - sk->protinfo.rose->timer = sk->protinfo.rose->t3; + rose_start_t3timer(sk); break; case ROSE_STATE_2: /* T3 */ - rose_clear_queues(sk); - sk->protinfo.rose->state = ROSE_STATE_0; - sk->state = TCP_CLOSE; - sk->err = ETIMEDOUT; - sk->shutdown |= SEND_SHUTDOWN; - if (!sk->dead) - sk->state_change(sk); - sk->dead = 1; + sk->protinfo.rose->neighbour->use--; + rose_disconnect(sk, ETIMEDOUT, -1, -1); + break; + + case ROSE_STATE_3: /* HB */ + if (sk->protinfo.rose->condition & ROSE_COND_ACK_PENDING) { + sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING; + rose_enquiry_response(sk); + } break; } +} + +static void rose_idletimer_expiry(unsigned long param) +{ + struct sock *sk = (struct sock *)param; + + rose_clear_queues(sk); + + rose_write_internal(sk, ROSE_CLEAR_REQUEST); + sk->protinfo.rose->state = ROSE_STATE_2; + + rose_start_t3timer(sk); + + sk->state = TCP_CLOSE; + sk->err = 0; + sk->shutdown |= SEND_SHUTDOWN; + + if (!sk->dead) + sk->state_change(sk); - rose_set_timer(sk); + sk->dead = 1; } #endif diff --git a/net/rose/sysctl_net_rose.c b/net/rose/sysctl_net_rose.c index 409f79b524b3..ad45e3b4783b 100644 --- a/net/rose/sysctl_net_rose.c +++ b/net/rose/sysctl_net_rose.c @@ -11,13 +11,13 @@ #include #include -static int min_timer[] = {1 * ROSE_SLOWHZ}; -static int max_timer[] = {300 * ROSE_SLOWHZ}; -static int min_idle[] = {0 * ROSE_SLOWHZ}; -static int max_idle[] = {65535 * ROSE_SLOWHZ}; +static int min_timer[] = {1 * HZ}; +static int max_timer[] = {300 * HZ}; +static int min_idle[] = {0 * HZ}; +static int max_idle[] = {65535 * HZ}; static int min_route[] = {0}, max_route[] = {1}; -static int min_ftimer[] = {60 * ROSE_SLOWHZ}; -static int max_ftimer[] = {600 * ROSE_SLOWHZ}; +static int min_ftimer[] = {60 * HZ}; +static int max_ftimer[] = {600 * HZ}; static int min_maxvcs[] = {1}, max_maxvcs[] = {254}; static int min_window[] = {1}, max_window[] = {7}; diff --git a/net/socket.c b/net/socket.c index 2587083bcfa4..ba5f5057ed4d 100644 --- a/net/socket.c +++ b/net/socket.c @@ -67,6 +67,7 @@ #include #include #include +#include #include #include #include @@ -266,6 +267,7 @@ struct socket *sock_alloc(void) inode = get_empty_inode(); if (!inode) return NULL; + sock = socki_lookup(inode); inode->i_mode = S_IFSOCK; diff --git a/net/unix/sysctl_net_unix.c b/net/unix/sysctl_net_unix.c index 18ce57684d29..f487ae95adbd 100644 --- a/net/unix/sysctl_net_unix.c +++ b/net/unix/sysctl_net_unix.c @@ -13,6 +13,9 @@ #include #include +#include + +#ifdef CONFIG_SYSCTL extern int sysctl_unix_destroy_delay; extern int sysctl_unix_delete_delay; @@ -26,3 +29,4 @@ ctl_table unix_table[] = { &proc_dointvec_jiffies}, {0} }; +#endif diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c index a77380648477..f59dd3a5140b 100644 --- a/net/x25/af_x25.c +++ b/net/x25/af_x25.c @@ -1,5 +1,5 @@ /* - * X.25 Packet Layer release 001 + * X.25 Packet Layer release 002 * * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. @@ -14,6 +14,8 @@ * * History * X.25 001 Jonathan Naylor Started coding. + * X.25 002 Jonathan Naylor Centralised disconnect handling. + * New timer architecture. */ #include @@ -54,8 +56,6 @@ int sysctl_x25_reset_request_timeout = X25_DEFAULT_T22; int sysctl_x25_clear_request_timeout = X25_DEFAULT_T23; int sysctl_x25_ack_holdback_timeout = X25_DEFAULT_T2; -static unsigned int lci = 1; - static struct sock *volatile x25_list = NULL; static struct proto_ops x25_proto_ops; @@ -173,16 +173,9 @@ static void x25_kill_by_device(struct device *dev) { struct sock *s; - for (s = x25_list; s != NULL; s = s->next) { - if (s->protinfo.x25->neighbour->dev == dev) { - s->protinfo.x25->state = X25_STATE_0; - s->state = TCP_CLOSE; - s->err = ENETUNREACH; - s->shutdown |= SEND_SHUTDOWN; - s->state_change(s); - s->dead = 1; - } - } + for (s = x25_list; s != NULL; s = s->next) + if (s->protinfo.x25->neighbour->dev == dev) + x25_disconnect(s, ENETUNREACH, 0, 0); } /* @@ -254,9 +247,9 @@ static struct sock *x25_find_listener(x25_address *addr) } /* - * Find a connected X.25 socket given my LCI. + * Find a connected X.25 socket given my LCI and neighbour. */ -struct sock *x25_find_socket(unsigned int lci) +struct sock *x25_find_socket(unsigned int lci, struct x25_neigh *neigh) { struct sock *s; unsigned long flags; @@ -265,7 +258,7 @@ struct sock *x25_find_socket(unsigned int lci) cli(); for (s = x25_list; s != NULL; s = s->next) { - if (s->protinfo.x25->lci == lci) { + if (s->protinfo.x25->lci == lci && s->protinfo.x25->neighbour == neigh) { restore_flags(flags); return s; } @@ -278,14 +271,13 @@ struct sock *x25_find_socket(unsigned int lci) /* * Find a unique LCI for a given device. */ -unsigned int x25_new_lci(void) +unsigned int x25_new_lci(struct x25_neigh *neigh) { - lci++; - if (lci > 4095) lci = 1; + unsigned int lci = 1; - while (x25_find_socket(lci) != NULL) { + while (x25_find_socket(lci, neigh) != NULL) { lci++; - if (lci > 4095) lci = 1; + if (lci == 4096) return 0; } return lci; @@ -318,7 +310,8 @@ void x25_destroy_socket(struct sock *sk) /* Not static as it's used by the timer save_flags(flags); cli(); - del_timer(&sk->timer); + x25_stop_heartbeat(sk); + x25_stop_timer(sk); x25_remove_socket(sk); x25_clear_queues(sk); /* Flush the queues */ @@ -326,7 +319,7 @@ void x25_destroy_socket(struct sock *sk) /* Not static as it's used by the timer while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) { if (skb->sk != sk) { /* A pending connection */ skb->sk->dead = 1; /* Queue the unaccepted socket for death */ - x25_set_timer(skb->sk); + x25_start_heartbeat(skb->sk); skb->sk->protinfo.x25->state = X25_STATE_0; } @@ -469,6 +462,8 @@ static int x25_create(struct socket *sock, int protocol) sock_init_data(sock, sk); + init_timer(&x25->timer); + sock->ops = &x25_proto_ops; sk->protocol = protocol; sk->mtu = X25_DEFAULT_PACKET_SIZE; /* X25_PS128 */ @@ -523,6 +518,8 @@ static struct sock *x25_make_new(struct sock *osk) x25->qbitincl = osk->protinfo.x25->qbitincl; + init_timer(&x25->timer); + return sk; } @@ -545,28 +542,17 @@ static int x25_release(struct socket *sock, struct socket *peer) switch (sk->protinfo.x25->state) { case X25_STATE_0: - sk->state = TCP_CLOSE; - sk->shutdown |= SEND_SHUTDOWN; - sk->state_change(sk); - sk->dead = 1; - x25_destroy_socket(sk); - break; - case X25_STATE_2: - sk->protinfo.x25->state = X25_STATE_0; - sk->state = TCP_CLOSE; - sk->shutdown |= SEND_SHUTDOWN; - sk->state_change(sk); - sk->dead = 1; + x25_disconnect(sk, 0, 0, 0); x25_destroy_socket(sk); - break; + break; case X25_STATE_1: case X25_STATE_3: case X25_STATE_4: x25_clear_queues(sk); x25_write_internal(sk, X25_CLEAR_REQUEST); - sk->protinfo.x25->timer = sk->protinfo.x25->t23; + x25_start_t23timer(sk); sk->protinfo.x25->state = X25_STATE_2; sk->state = TCP_CLOSE; sk->shutdown |= SEND_SHUTDOWN; @@ -644,6 +630,9 @@ static int x25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len if ((sk->protinfo.x25->neighbour = x25_get_neigh(dev)) == NULL) return -ENETUNREACH; + if ((sk->protinfo.x25->lci = x25_new_lci(sk->protinfo.x25->neighbour)) == 0) + return -ENETUNREACH; + if (sk->zapped) /* Must bind first - autobinding does not work */ return -EINVAL; @@ -651,17 +640,17 @@ static int x25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len memset(&sk->protinfo.x25->source_addr, '\0', X25_ADDR_LEN); sk->protinfo.x25->dest_addr = addr->sx25_addr; - sk->protinfo.x25->lci = x25_new_lci(); /* Move to connecting socket, start sending Connect Requests */ sock->state = SS_CONNECTING; sk->state = TCP_SYN_SENT; sk->protinfo.x25->state = X25_STATE_1; - sk->protinfo.x25->timer = sk->protinfo.x25->t21; + x25_write_internal(sk, X25_CALL_REQUEST); - x25_set_timer(sk); + x25_start_heartbeat(sk); + x25_start_t21timer(sk); /* Now the loop */ if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) @@ -850,7 +839,7 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *neigh, unsigned i skb_queue_head(&sk->receive_queue, skb); - x25_set_timer(make); + x25_start_heartbeat(make); if (!sk->dead) sk->data_ready(sk, skb->len); @@ -987,8 +976,7 @@ static int x25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct x25_output(sk, skb); } - if (sk->protinfo.x25->state == X25_STATE_3) - x25_kick(sk); + x25_kick(sk); return len; } @@ -1072,30 +1060,27 @@ static int x25_shutdown(struct socket *sk, int how) static int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { - struct x25_facilities facilities; - struct x25_calluserdata calluserdata; struct sock *sk = sock->sk; - int err; - long amount = 0; switch (cmd) { - case TIOCOUTQ: - if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned long))) != 0) - return err; + case TIOCOUTQ: { + long amount; amount = sk->sndbuf - atomic_read(&sk->wmem_alloc); if (amount < 0) amount = 0; - put_user(amount, (unsigned long *)arg); + if (put_user(amount, (unsigned long *)arg)) + return -EFAULT; return 0; + } case TIOCINQ: { struct sk_buff *skb; + long amount = 0L; /* These two are safe on a single CPU system as only user tasks fiddle here */ if ((skb = skb_peek(&sk->receive_queue)) != NULL) - amount = skb->len - 20; - if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned long))) != 0) - return err; - put_user(amount, (unsigned long *)arg); + amount = skb->len; + if (put_user(amount, (unsigned long *)arg)) + return -EFAULT; return 0; } @@ -1103,9 +1088,8 @@ static int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) if (sk != NULL) { if (sk->stamp.tv_sec == 0) return -ENOENT; - if ((err = verify_area(VERIFY_WRITE,(void *)arg,sizeof(struct timeval))) != 0) - return err; - copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval)); + if (copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval))) + return -EFAULT; return 0; } return -EINVAL; @@ -1134,17 +1118,18 @@ static int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) if (!suser()) return -EPERM; return x25_subscr_ioctl(cmd, (void *)arg); - case SIOCX25GFACILITIES: - if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(facilities))) != 0) - return err; + case SIOCX25GFACILITIES: { + struct x25_facilities facilities; facilities = sk->protinfo.x25->facilities; - copy_to_user((void *)arg, &facilities, sizeof(facilities)); + if (copy_to_user((void *)arg, &facilities, sizeof(facilities))) + return -EFAULT; return 0; + } - case SIOCX25SFACILITIES: - if ((err = verify_area(VERIFY_READ, (void *)arg, sizeof(facilities))) != 0) - return err; - copy_from_user(&facilities, (void *)arg, sizeof(facilities)); + case SIOCX25SFACILITIES: { + struct x25_facilities facilities; + if (copy_from_user(&facilities, (void *)arg, sizeof(facilities))) + return -EFAULT; if (sk->state != TCP_LISTEN) return -EINVAL; if (facilities.pacsize_in < X25_PS16 || facilities.pacsize_in > X25_PS4096) @@ -1168,22 +1153,33 @@ static int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) return -EINVAL; sk->protinfo.x25->facilities = facilities; return 0; + } - case SIOCX25GCALLUSERDATA: - if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(calluserdata))) != 0) - return err; + case SIOCX25GCALLUSERDATA: { + struct x25_calluserdata calluserdata; calluserdata = sk->protinfo.x25->calluserdata; - copy_to_user((void *)arg, &calluserdata, sizeof(calluserdata)); + if (copy_to_user((void *)arg, &calluserdata, sizeof(calluserdata))) + return -EFAULT; return 0; + } - case SIOCX25SCALLUSERDATA: - if ((err = verify_area(VERIFY_READ, (void *)arg, sizeof(calluserdata))) != 0) - return err; - copy_from_user(&calluserdata, (void *)arg, sizeof(calluserdata)); + case SIOCX25SCALLUSERDATA: { + struct x25_calluserdata calluserdata; + if (copy_from_user(&calluserdata, (void *)arg, sizeof(calluserdata))) + return -EFAULT; if (calluserdata.cudlength > X25_MAX_CUD_LEN) return -EINVAL; sk->protinfo.x25->calluserdata = calluserdata; return 0; + } + + case SIOCX25GCAUSEDIAG: { + struct x25_causediag causediag; + causediag = sk->protinfo.x25->causediag; + if (copy_to_user((void *)arg, &causediag, sizeof(causediag))) + return -EFAULT; + return 0; + } default: return dev_ioctl(cmd, (void *)arg); @@ -1212,18 +1208,22 @@ static int x25_get_info(char *buffer, char **start, off_t offset, int length, in else devname = s->protinfo.x25->neighbour->dev->name; - len += sprintf(buffer + len, "%-10s %-10s %-5s %3.3X %d %d %d %d %3d %3d %3d %3d %3d %5d %5d\n", + len += sprintf(buffer + len, "%-10s %-10s %-5s %3.3X %d %d %d %d %3lu %3lu %3lu %3lu %3lu %5d %5d\n", (s->protinfo.x25->dest_addr.x25_addr[0] == '\0') ? "*" : s->protinfo.x25->dest_addr.x25_addr, (s->protinfo.x25->source_addr.x25_addr[0] == '\0') ? "*" : s->protinfo.x25->source_addr.x25_addr, - devname, s->protinfo.x25->lci & 0x0FFF, + devname, + s->protinfo.x25->lci & 0x0FFF, s->protinfo.x25->state, - s->protinfo.x25->vs, s->protinfo.x25->vr, s->protinfo.x25->va, - s->protinfo.x25->timer / X25_SLOWHZ, - s->protinfo.x25->t2 / X25_SLOWHZ, - s->protinfo.x25->t21 / X25_SLOWHZ, - s->protinfo.x25->t22 / X25_SLOWHZ, - s->protinfo.x25->t23 / X25_SLOWHZ, - atomic_read(&s->wmem_alloc), atomic_read(&s->rmem_alloc)); + s->protinfo.x25->vs, + s->protinfo.x25->vr, + s->protinfo.x25->va, + x25_display_timer(s) / HZ, + s->protinfo.x25->t2 / HZ, + s->protinfo.x25->t21 / HZ, + s->protinfo.x25->t22 / HZ, + s->protinfo.x25->t23 / HZ, + atomic_read(&s->wmem_alloc), + atomic_read(&s->rmem_alloc)); pos = begin + len; @@ -1310,7 +1310,7 @@ __initfunc(void x25_proto_init(struct net_proto *pro)) register_netdevice_notifier(&x25_dev_notifier); - printk(KERN_INFO "X.25 for Linux. Version 0.1 for Linux 2.1.15\n"); + printk(KERN_INFO "X.25 for Linux. Version 0.2 for Linux 2.1.15\n"); #ifdef CONFIG_SYSCTL x25_register_sysctl(); diff --git a/net/x25/sysctl_net_x25.c b/net/x25/sysctl_net_x25.c index 8454ac9d962c..42893df320cf 100644 --- a/net/x25/sysctl_net_x25.c +++ b/net/x25/sysctl_net_x25.c @@ -13,8 +13,8 @@ #include #include -static int min_timer[] = {1 * X25_SLOWHZ}; -static int max_timer[] = {300 * X25_SLOWHZ}; +static int min_timer[] = {1 * HZ}; +static int max_timer[] = {300 * HZ}; static struct ctl_table_header *x25_table_header; diff --git a/net/x25/x25_dev.c b/net/x25/x25_dev.c index 6b02a944175f..e4cd99ae7ba4 100644 --- a/net/x25/x25_dev.c +++ b/net/x25/x25_dev.c @@ -1,5 +1,5 @@ /* - * X.25 Packet Layer release 001 + * X.25 Packet Layer release 002 * * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. @@ -73,7 +73,7 @@ static int x25_receive_data(struct sk_buff *skb, struct x25_neigh *neigh) /* * Find an existing socket. */ - if ((sk = x25_find_socket(lci)) != NULL) { + if ((sk = x25_find_socket(lci, neigh)) != NULL) { skb->h.raw = skb->data; return x25_process_rx_frame(sk, skb); } diff --git a/net/x25/x25_facilities.c b/net/x25/x25_facilities.c index 13759d1eaa88..af072ce22dbe 100644 --- a/net/x25/x25_facilities.c +++ b/net/x25/x25_facilities.c @@ -1,5 +1,5 @@ /* - * X.25 Packet Layer release 001 + * X.25 Packet Layer release 002 * * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. diff --git a/net/x25/x25_in.c b/net/x25/x25_in.c index 82e5c0817abd..96b459a4ee19 100644 --- a/net/x25/x25_in.c +++ b/net/x25/x25_in.c @@ -1,5 +1,5 @@ /* - * X.25 Packet Layer release 001 + * X.25 Packet Layer release 002 * * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. @@ -14,6 +14,8 @@ * * History * X.25 001 Jonathan Naylor Started coding. + * X.25 002 Jonathan Naylor Centralised disconnection code. + * New timer architecture. */ #include @@ -88,8 +90,8 @@ static int x25_state1_machine(struct sock *sk, struct sk_buff *skb, int frametyp switch (frametype) { case X25_CALL_ACCEPTED: + x25_stop_timer(sk); sk->protinfo.x25->condition = 0x00; - sk->protinfo.x25->timer = 0; sk->protinfo.x25->vs = 0; sk->protinfo.x25->va = 0; sk->protinfo.x25->vr = 0; @@ -114,15 +116,8 @@ static int x25_state1_machine(struct sock *sk, struct sk_buff *skb, int frametyp break; case X25_CLEAR_REQUEST: - x25_clear_queues(sk); x25_write_internal(sk, X25_CLEAR_CONFIRMATION); - sk->protinfo.x25->state = X25_STATE_0; - sk->state = TCP_CLOSE; - sk->err = ECONNREFUSED; - sk->shutdown |= SEND_SHUTDOWN; - if (!sk->dead) - sk->state_change(sk); - sk->dead = 1; + x25_disconnect(sk, ECONNREFUSED, skb->data[3], skb->data[4]); break; default: @@ -143,15 +138,11 @@ static int x25_state2_machine(struct sock *sk, struct sk_buff *skb, int frametyp case X25_CLEAR_REQUEST: x25_write_internal(sk, X25_CLEAR_CONFIRMATION); + x25_disconnect(sk, 0, skb->data[3], skb->data[4]); + break; + case X25_CLEAR_CONFIRMATION: - x25_clear_queues(sk); - sk->protinfo.x25->state = X25_STATE_0; - sk->state = TCP_CLOSE; - sk->err = 0; - sk->shutdown |= SEND_SHUTDOWN; - if (!sk->dead) - sk->state_change(sk); - sk->dead = 1; + x25_disconnect(sk, 0, 0, 0); break; default: @@ -177,8 +168,8 @@ static int x25_state3_machine(struct sock *sk, struct sk_buff *skb, int frametyp case X25_RESET_REQUEST: x25_write_internal(sk, X25_RESET_CONFIRMATION); + x25_stop_timer(sk); sk->protinfo.x25->condition = 0x00; - sk->protinfo.x25->timer = 0; sk->protinfo.x25->vs = 0; sk->protinfo.x25->vr = 0; sk->protinfo.x25->va = 0; @@ -186,15 +177,8 @@ static int x25_state3_machine(struct sock *sk, struct sk_buff *skb, int frametyp break; case X25_CLEAR_REQUEST: - x25_clear_queues(sk); x25_write_internal(sk, X25_CLEAR_CONFIRMATION); - sk->protinfo.x25->state = X25_STATE_0; - sk->state = TCP_CLOSE; - sk->err = 0; - sk->shutdown |= SEND_SHUTDOWN; - if (!sk->dead) - sk->state_change(sk); - sk->dead = 1; + x25_disconnect(sk, 0, skb->data[3], skb->data[4]); break; case X25_RR: @@ -207,13 +191,13 @@ static int x25_state3_machine(struct sock *sk, struct sk_buff *skb, int frametyp if (!x25_validate_nr(sk, nr)) { x25_clear_queues(sk); x25_write_internal(sk, X25_RESET_REQUEST); + x25_start_t22timer(sk); sk->protinfo.x25->condition = 0x00; sk->protinfo.x25->vs = 0; sk->protinfo.x25->vr = 0; sk->protinfo.x25->va = 0; sk->protinfo.x25->vl = 0; sk->protinfo.x25->state = X25_STATE_4; - sk->protinfo.x25->timer = sk->protinfo.x25->t22; } else { if (sk->protinfo.x25->condition & X25_COND_PEER_RX_BUSY) { sk->protinfo.x25->va = nr; @@ -228,13 +212,13 @@ static int x25_state3_machine(struct sock *sk, struct sk_buff *skb, int frametyp if (!x25_validate_nr(sk, nr)) { x25_clear_queues(sk); x25_write_internal(sk, X25_RESET_REQUEST); + x25_start_t22timer(sk); sk->protinfo.x25->condition = 0x00; sk->protinfo.x25->vs = 0; sk->protinfo.x25->vr = 0; sk->protinfo.x25->va = 0; sk->protinfo.x25->vl = 0; sk->protinfo.x25->state = X25_STATE_4; - sk->protinfo.x25->timer = sk->protinfo.x25->t22; break; } if (sk->protinfo.x25->condition & X25_COND_PEER_RX_BUSY) { @@ -258,11 +242,11 @@ static int x25_state3_machine(struct sock *sk, struct sk_buff *skb, int frametyp */ if (((sk->protinfo.x25->vl + sk->protinfo.x25->facilities.winsize_in) % modulus) == sk->protinfo.x25->vr) { sk->protinfo.x25->condition &= ~X25_COND_ACK_PENDING; - sk->protinfo.x25->timer = 0; + x25_stop_timer(sk); x25_enquiry_response(sk); } else { sk->protinfo.x25->condition |= X25_COND_ACK_PENDING; - sk->protinfo.x25->timer = sk->protinfo.x25->t2; + x25_start_t2timer(sk); } break; @@ -307,7 +291,7 @@ static int x25_state4_machine(struct sock *sk, struct sk_buff *skb, int frametyp case X25_RESET_REQUEST: x25_write_internal(sk, X25_RESET_CONFIRMATION); case X25_RESET_CONFIRMATION: - sk->protinfo.x25->timer = 0; + x25_stop_timer(sk); sk->protinfo.x25->condition = 0x00; sk->protinfo.x25->va = 0; sk->protinfo.x25->vr = 0; @@ -317,16 +301,8 @@ static int x25_state4_machine(struct sock *sk, struct sk_buff *skb, int frametyp break; case X25_CLEAR_REQUEST: - x25_clear_queues(sk); x25_write_internal(sk, X25_CLEAR_CONFIRMATION); - sk->protinfo.x25->timer = 0; - sk->protinfo.x25->state = X25_STATE_0; - sk->state = TCP_CLOSE; - sk->err = 0; - sk->shutdown |= SEND_SHUTDOWN; - if (!sk->dead) - sk->state_change(sk); - sk->dead = 1; + x25_disconnect(sk, 0, skb->data[3], skb->data[4]); break; default: @@ -344,8 +320,6 @@ int x25_process_rx_frame(struct sock *sk, struct sk_buff *skb) if (sk->protinfo.x25->state == X25_STATE_0) return 0; - del_timer(&sk->timer); - frametype = x25_decode(sk, skb, &ns, &nr, &q, &d, &m); switch (sk->protinfo.x25->state) { @@ -363,7 +337,7 @@ int x25_process_rx_frame(struct sock *sk, struct sk_buff *skb) break; } - x25_set_timer(sk); + x25_kick(sk); return queued; } diff --git a/net/x25/x25_link.c b/net/x25/x25_link.c index f44c0a2c5b14..1742d802f557 100644 --- a/net/x25/x25_link.c +++ b/net/x25/x25_link.c @@ -1,5 +1,5 @@ /* - * X.25 Packet Layer release 001 + * X.25 Packet Layer release 002 * * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. @@ -14,6 +14,7 @@ * * History * X.25 001 Jonathan Naylor Started coding. + * X.25 002 Jonathan Naylor New timer architecture. */ #include @@ -43,49 +44,34 @@ static struct x25_neigh *x25_neigh_list = NULL; -static void x25_link_timer(unsigned long); +static void x25_t20timer_expiry(unsigned long); /* * Linux set/reset timer routines */ -static void x25_link_set_timer(struct x25_neigh *neigh) +static void x25_start_t20timer(struct x25_neigh *neigh) { - unsigned long flags; - - save_flags(flags); cli(); - del_timer(&neigh->timer); - restore_flags(flags); + del_timer(&neigh->t20timer); - neigh->timer.data = (unsigned long)neigh; - neigh->timer.function = &x25_link_timer; - neigh->timer.expires = jiffies + (HZ / 1); + neigh->t20timer.data = (unsigned long)neigh; + neigh->t20timer.function = &x25_t20timer_expiry; + neigh->t20timer.expires = jiffies + neigh->t20; - add_timer(&neigh->timer); + add_timer(&neigh->t20timer); } -/* - * X.25 Link TIMER - * - * This routine is called every second. Decrement timer by this - * amount - if expired then process the event. - */ -static void x25_link_timer(unsigned long param) +static void x25_t20timer_expiry(unsigned long param) { struct x25_neigh *neigh = (struct x25_neigh *)param; - if (neigh->t20timer == 0 || --neigh->t20timer > 0) { - x25_link_set_timer(neigh); - return; - } - - /* - * T20 for a link has expired. - */ x25_transmit_restart_request(neigh); - neigh->t20timer = neigh->t20; + x25_start_t20timer(neigh); +} - x25_link_set_timer(neigh); +static void x25_stop_t20timer(struct x25_neigh *neigh) +{ + del_timer(&neigh->t20timer); } /* @@ -97,16 +83,14 @@ void x25_link_control(struct sk_buff *skb, struct x25_neigh *neigh, unsigned sho switch (frametype) { case X25_RESTART_REQUEST: - neigh->t20timer = 0; - neigh->state = X25_LINK_STATE_3; - del_timer(&neigh->timer); + x25_stop_t20timer(neigh); + neigh->state = X25_LINK_STATE_3; x25_transmit_restart_confirmation(neigh); break; case X25_RESTART_CONFIRMATION: - neigh->t20timer = 0; - neigh->state = X25_LINK_STATE_3; - del_timer(&neigh->timer); + x25_stop_t20timer(neigh); + neigh->state = X25_LINK_STATE_3; break; case X25_DIAGNOSTIC: @@ -272,9 +256,8 @@ void x25_link_established(struct x25_neigh *neigh) break; case X25_LINK_STATE_1: x25_transmit_restart_request(neigh); - neigh->state = X25_LINK_STATE_2; - neigh->t20timer = neigh->t20; - x25_link_set_timer(neigh); + neigh->state = X25_LINK_STATE_2; + x25_start_t20timer(neigh); break; } } @@ -300,12 +283,12 @@ void x25_link_device_up(struct device *dev) return; skb_queue_head_init(&x25_neigh->queue); - init_timer(&x25_neigh->timer); + + init_timer(&x25_neigh->t20timer); x25_neigh->dev = dev; x25_neigh->state = X25_LINK_STATE_0; x25_neigh->extended = 0; - x25_neigh->t20timer = 0; x25_neigh->t20 = sysctl_x25_restart_request_timeout; save_flags(flags); cli(); @@ -323,10 +306,9 @@ static void x25_remove_neigh(struct x25_neigh *x25_neigh) while ((skb = skb_dequeue(&x25_neigh->queue)) != NULL) kfree_skb(skb, FREE_WRITE); - del_timer(&x25_neigh->timer); + x25_stop_t20timer(x25_neigh); - save_flags(flags); - cli(); + save_flags(flags); cli(); if ((s = x25_neigh_list) == x25_neigh) { x25_neigh_list = x25_neigh->next; @@ -387,25 +369,22 @@ int x25_subscr_ioctl(unsigned int cmd, void *arg) struct x25_subscrip_struct x25_subscr; struct x25_neigh *x25_neigh; struct device *dev; - int err; switch (cmd) { case SIOCX25GSUBSCRIP: - if ((err = verify_area(VERIFY_WRITE, arg, sizeof(struct x25_subscrip_struct))) != 0) - return err; if ((dev = x25_dev_get(x25_subscr.device)) == NULL) return -EINVAL; if ((x25_neigh = x25_get_neigh(dev)) == NULL) return -EINVAL; x25_subscr.extended = x25_neigh->extended; - copy_to_user(arg, &x25_subscr, sizeof(struct x25_subscrip_struct)); + if (copy_to_user(arg, &x25_subscr, sizeof(struct x25_subscrip_struct))) + return -EFAULT; break; case SIOCX25SSUBSCRIP: - if ((err = verify_area(VERIFY_READ, arg, sizeof(struct x25_subscrip_struct))) != 0) - return err; - copy_from_user(&x25_subscr, arg, sizeof(struct x25_subscrip_struct)); + if (copy_from_user(&x25_subscr, arg, sizeof(struct x25_subscrip_struct))) + return -EFAULT; if ((dev = x25_dev_get(x25_subscr.device)) == NULL) return -EINVAL; if ((x25_neigh = x25_get_neigh(dev)) == NULL) diff --git a/net/x25/x25_out.c b/net/x25/x25_out.c index 321baa5d6cf6..aa8fc2c1bf4c 100644 --- a/net/x25/x25_out.c +++ b/net/x25/x25_out.c @@ -1,5 +1,5 @@ /* - * X.25 Packet Layer release 001 + * X.25 Packet Layer release 002 * * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. @@ -14,6 +14,7 @@ * * History * X.25 001 Jonathan Naylor Started coding. + * X.25 002 Jonathan Naylor New timer architecture. */ #include @@ -129,7 +130,8 @@ void x25_kick(struct sock *sk) unsigned short end; int modulus; - del_timer(&sk->timer); + if (sk->protinfo.x25->state != X25_STATE_3) + return; /* * Transmit interrupt data. @@ -140,38 +142,39 @@ void x25_kick(struct sock *sk) x25_transmit_link(skb, sk->protinfo.x25->neighbour); } + if (sk->protinfo.x25->condition & X25_COND_PEER_RX_BUSY) + return; + + if (skb_peek(&sk->write_queue) == NULL) + return; + modulus = (sk->protinfo.x25->neighbour->extended) ? X25_EMODULUS : X25_SMODULUS; end = (sk->protinfo.x25->va + sk->protinfo.x25->facilities.winsize_out) % modulus; + if (sk->protinfo.x25->vs == end) + return; + /* - * Transmit normal stream data. + * Transmit data until either we're out of data to send or + * the window is full. */ - if (!(sk->protinfo.x25->condition & X25_COND_PEER_RX_BUSY) && - sk->protinfo.x25->vs != end && - skb_peek(&sk->write_queue) != NULL) { - /* - * Transmit data until either we're out of data to send or - * the window is full. - */ - skb = skb_dequeue(&sk->write_queue); + skb = skb_dequeue(&sk->write_queue); - do { - /* - * Transmit the frame. - */ - x25_send_iframe(sk, skb); + do { + /* + * Transmit the frame. + */ + x25_send_iframe(sk, skb); - sk->protinfo.x25->vs = (sk->protinfo.x25->vs + 1) % modulus; + sk->protinfo.x25->vs = (sk->protinfo.x25->vs + 1) % modulus; - } while (sk->protinfo.x25->vs != end && (skb = skb_dequeue(&sk->write_queue)) != NULL); + } while (sk->protinfo.x25->vs != end && (skb = skb_dequeue(&sk->write_queue)) != NULL); - sk->protinfo.x25->vl = sk->protinfo.x25->vr; - sk->protinfo.x25->condition &= ~X25_COND_ACK_PENDING; - sk->protinfo.x25->timer = 0; - } + sk->protinfo.x25->vl = sk->protinfo.x25->vr; + sk->protinfo.x25->condition &= ~X25_COND_ACK_PENDING; - x25_set_timer(sk); + x25_stop_timer(sk); } /* @@ -188,7 +191,8 @@ void x25_enquiry_response(struct sock *sk) sk->protinfo.x25->vl = sk->protinfo.x25->vr; sk->protinfo.x25->condition &= ~X25_COND_ACK_PENDING; - sk->protinfo.x25->timer = 0; + + x25_stop_timer(sk); } void x25_check_iframes_acked(struct sock *sk, unsigned short nr) diff --git a/net/x25/x25_route.c b/net/x25/x25_route.c index 820c11cd4014..9c3204537f70 100644 --- a/net/x25/x25_route.c +++ b/net/x25/x25_route.c @@ -1,5 +1,5 @@ /* - * X.25 Packet Layer release 001 + * X.25 Packet Layer release 002 * * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. @@ -183,14 +183,12 @@ int x25_route_ioctl(unsigned int cmd, void *arg) { struct x25_route_struct x25_route; struct device *dev; - int err; switch (cmd) { case SIOCADDRT: - if ((err = verify_area(VERIFY_READ, arg, sizeof(struct x25_route_struct))) != 0) - return err; - copy_from_user(&x25_route, arg, sizeof(struct x25_route_struct)); + if (copy_from_user(&x25_route, arg, sizeof(struct x25_route_struct))) + return -EFAULT; if (x25_route.sigdigits < 0 || x25_route.sigdigits > 15) return -EINVAL; if ((dev = x25_dev_get(x25_route.device)) == NULL) @@ -198,9 +196,8 @@ int x25_route_ioctl(unsigned int cmd, void *arg) return x25_add_route(&x25_route.address, x25_route.sigdigits, dev); case SIOCDELRT: - if ((err = verify_area(VERIFY_READ, arg, sizeof(struct x25_route_struct))) != 0) - return err; - copy_from_user(&x25_route, arg, sizeof(struct x25_route_struct)); + if (copy_from_user(&x25_route, arg, sizeof(struct x25_route_struct))) + return -EFAULT; if (x25_route.sigdigits < 0 || x25_route.sigdigits > 15) return -EINVAL; if ((dev = x25_dev_get(x25_route.device)) == NULL) @@ -227,7 +224,8 @@ int x25_routes_get_info(char *buffer, char **start, off_t offset, int length, in for (x25_route = x25_route_list; x25_route != NULL; x25_route = x25_route->next) { len += sprintf(buffer + len, "%-15s %-6d %-5s\n", - x25_route->address.x25_addr, x25_route->sigdigits, + x25_route->address.x25_addr, + x25_route->sigdigits, (x25_route->dev != NULL) ? x25_route->dev->name : "???"); pos = begin + len; diff --git a/net/x25/x25_subr.c b/net/x25/x25_subr.c index 75e58af98cc2..f2aff6d12b25 100644 --- a/net/x25/x25_subr.c +++ b/net/x25/x25_subr.c @@ -1,5 +1,5 @@ /* - * X.25 Packet Layer release 001 + * X.25 Packet Layer release 002 * * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. @@ -14,6 +14,7 @@ * * History * X.25 001 Jonathan Naylor Started coding. + * X.25 002 Jonathan Naylor Centralised disconnection processing. */ #include @@ -281,4 +282,25 @@ int x25_decode(struct sock *sk, struct sk_buff *skb, int *ns, int *nr, int *q, i return X25_ILLEGAL; } +void x25_disconnect(struct sock *sk, int reason, unsigned char cause, unsigned char diagnostic) +{ + x25_clear_queues(sk); + x25_stop_timer(sk); + + sk->protinfo.x25->lci = 0; + sk->protinfo.x25->state = X25_STATE_0; + + sk->protinfo.x25->causediag.cause = cause; + sk->protinfo.x25->causediag.diagnostic = diagnostic; + + sk->state = TCP_CLOSE; + sk->err = reason; + sk->shutdown |= SEND_SHUTDOWN; + + if (!sk->dead) + sk->state_change(sk); + + sk->dead = 1; +} + #endif diff --git a/net/x25/x25_timer.c b/net/x25/x25_timer.c index e625c41edb58..90b6513c93c2 100644 --- a/net/x25/x25_timer.c +++ b/net/x25/x25_timer.c @@ -1,5 +1,5 @@ /* - * X.25 Packet Layer release 001 + * X.25 Packet Layer release 002 * * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. @@ -14,6 +14,8 @@ * * History * X.25 001 Jonathan Naylor Started coding. + * X.25 002 Jonathan Naylor New timer architecture. + * Centralised disconnection processing. */ #include @@ -39,42 +41,93 @@ #include #include -static void x25_timer(unsigned long); +static void x25_heartbeat_expiry(unsigned long); +static void x25_timer_expiry(unsigned long); -/* - * Linux set timer - */ -void x25_set_timer(struct sock *sk) +void x25_start_heartbeat(struct sock *sk) { - unsigned long flags; - - save_flags(flags); cli(); del_timer(&sk->timer); - restore_flags(flags); sk->timer.data = (unsigned long)sk; - sk->timer.function = &x25_timer; - sk->timer.expires = jiffies + (HZ / 1); + sk->timer.function = &x25_heartbeat_expiry; + sk->timer.expires = jiffies + 5 * HZ; add_timer(&sk->timer); } -/* - * X.25 TIMER - * - * This routine is called every second. Decrement timer by this - * amount - if expired then process the event. - */ -static void x25_timer(unsigned long param) +void x25_stop_heartbeat(struct sock *sk) +{ + del_timer(&sk->timer); +} + +void x25_start_t2timer(struct sock *sk) +{ + del_timer(&sk->protinfo.x25->timer); + + sk->protinfo.x25->timer.data = (unsigned long)sk; + sk->protinfo.x25->timer.function = &x25_timer_expiry; + sk->protinfo.x25->timer.expires = jiffies + sk->protinfo.x25->t2; + + add_timer(&sk->protinfo.x25->timer); +} + +void x25_start_t21timer(struct sock *sk) +{ + del_timer(&sk->protinfo.x25->timer); + + sk->protinfo.x25->timer.data = (unsigned long)sk; + sk->protinfo.x25->timer.function = &x25_timer_expiry; + sk->protinfo.x25->timer.expires = jiffies + sk->protinfo.x25->t21; + + add_timer(&sk->protinfo.x25->timer); +} + +void x25_start_t22timer(struct sock *sk) +{ + del_timer(&sk->protinfo.x25->timer); + + sk->protinfo.x25->timer.data = (unsigned long)sk; + sk->protinfo.x25->timer.function = &x25_timer_expiry; + sk->protinfo.x25->timer.expires = jiffies + sk->protinfo.x25->t22; + + add_timer(&sk->protinfo.x25->timer); +} + +void x25_start_t23timer(struct sock *sk) +{ + del_timer(&sk->protinfo.x25->timer); + + sk->protinfo.x25->timer.data = (unsigned long)sk; + sk->protinfo.x25->timer.function = &x25_timer_expiry; + sk->protinfo.x25->timer.expires = jiffies + sk->protinfo.x25->t23; + + add_timer(&sk->protinfo.x25->timer); +} + +void x25_stop_timer(struct sock *sk) +{ + del_timer(&sk->protinfo.x25->timer); +} + +unsigned long x25_display_timer(struct sock *sk) +{ + if (sk->protinfo.x25->timer.prev == NULL && + sk->protinfo.x25->timer.next == NULL) + return 0; + + return sk->protinfo.x25->timer.expires - jiffies; +} + +static void x25_heartbeat_expiry(unsigned long param) { struct sock *sk = (struct sock *)param; switch (sk->protinfo.x25->state) { + case X25_STATE_0: /* Magic here: If we listen() and a new link dies before it is accepted() it isn't 'dead' so doesn't get removed. */ if (sk->destroy || (sk->state == TCP_LISTEN && sk->dead)) { - del_timer(&sk->timer); x25_destroy_socket(sk); return; } @@ -89,30 +142,26 @@ static void x25_timer(unsigned long param) sk->protinfo.x25->condition &= ~X25_COND_OWN_RX_BUSY; sk->protinfo.x25->condition &= ~X25_COND_ACK_PENDING; sk->protinfo.x25->vl = sk->protinfo.x25->vr; - sk->protinfo.x25->timer = 0; x25_write_internal(sk, X25_RR); + x25_stop_timer(sk); break; } - /* - * Check for frames to transmit. - */ - x25_kick(sk); - break; - - default: break; } - if (sk->protinfo.x25->timer == 0 || --sk->protinfo.x25->timer > 0) { - x25_set_timer(sk); - return; - } + x25_start_heartbeat(sk); +} + +/* + * Timer has expired, it may have been T2, T21, T22, or T23. We can tell + * by the state machine state. + */ +static void x25_timer_expiry(unsigned long param) +{ + struct sock *sk = (struct sock *)param; - /* - * Timer has expired, it may have been T2, T21, T22, or T23. We can tell - * by the state machine state. - */ switch (sk->protinfo.x25->state) { + case X25_STATE_3: /* T2 */ if (sk->protinfo.x25->condition & X25_COND_ACK_PENDING) { sk->protinfo.x25->condition &= ~X25_COND_ACK_PENDING; @@ -124,22 +173,13 @@ static void x25_timer(unsigned long param) case X25_STATE_4: /* T22 */ x25_write_internal(sk, X25_CLEAR_REQUEST); sk->protinfo.x25->state = X25_STATE_2; - sk->protinfo.x25->timer = sk->protinfo.x25->t23; + x25_start_t23timer(sk); break; case X25_STATE_2: /* T23 */ - x25_clear_queues(sk); - sk->protinfo.x25->state = X25_STATE_0; - sk->state = TCP_CLOSE; - sk->err = ETIMEDOUT; - sk->shutdown |= SEND_SHUTDOWN; - if (!sk->dead) - sk->state_change(sk); - sk->dead = 1; + x25_disconnect(sk, ETIMEDOUT, 0, 0); break; } - - x25_set_timer(sk); } #endif -- 2.39.5