summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CREDITS37
-rw-r--r--Documentation/Configure.help59
-rw-r--r--Documentation/binfmt_misc.txt79
-rw-r--r--Documentation/ioctl-number.txt1
-rw-r--r--Documentation/m68k/amiboot.txt6
-rw-r--r--Documentation/networking/README.cops39
-rw-r--r--Documentation/networking/ax25.txt4
-rw-r--r--Documentation/networking/net-modules.txt7
-rw-r--r--Documentation/networking/wan-router.txt31
-rw-r--r--Documentation/networking/x25.txt1
-rw-r--r--MAINTAINERS36
-rw-r--r--Makefile2
-rw-r--r--arch/alpha/defconfig8
-rw-r--r--arch/alpha/kernel/osf_sys.c190
-rw-r--r--arch/alpha/kernel/process.c5
-rw-r--r--arch/alpha/kernel/traps.c20
-rw-r--r--arch/alpha/mm/fault.c20
-rw-r--r--arch/i386/boot/compressed/Makefile2
-rw-r--r--arch/i386/defconfig14
-rw-r--r--arch/i386/kernel/bios32.c28
-rw-r--r--arch/i386/kernel/entry.S4
-rw-r--r--arch/i386/kernel/irq.c5
-rw-r--r--arch/i386/kernel/process.c5
-rw-r--r--arch/i386/kernel/setup.c19
-rw-r--r--arch/i386/mm/fault.c20
-rw-r--r--arch/m68k/amiga/amifb.c6
-rw-r--r--arch/m68k/amiga/config.c21
-rw-r--r--arch/m68k/amiga/cyberfb.c2
-rw-r--r--arch/m68k/atari/atakeyb.c2
-rw-r--r--arch/m68k/boot/amiga/bootstrap.c4
-rw-r--r--arch/m68k/boot/amiga/linuxboot.c53
-rw-r--r--arch/m68k/config.in6
-rw-r--r--arch/m68k/console/fbcon.c24
-rw-r--r--arch/m68k/defconfig3
-rw-r--r--arch/m68k/ifpsp060/iskeleton.S24
-rw-r--r--arch/m68k/kernel/console.c4
-rw-r--r--arch/m68k/mm/memory.c22
-rw-r--r--arch/mips/defconfig1
-rw-r--r--arch/mips/kernel/irixelf.c99
-rw-r--r--arch/mips/kernel/syscall.c13
-rw-r--r--arch/mips/kernel/syscalls.h4
-rw-r--r--arch/mips/kernel/sysirix.c287
-rw-r--r--arch/mips/kernel/sysmips.c15
-rw-r--r--arch/mips/mm/fault.c23
-rw-r--r--arch/mips/mm/init.c1
-rw-r--r--arch/sparc/Makefile5
-rw-r--r--arch/sparc/boot/Makefile15
-rw-r--r--arch/sparc/boot/piggyback.c100
-rw-r--r--arch/sparc/config.in2
-rw-r--r--arch/sparc/defconfig4
-rw-r--r--arch/sparc/kernel/cpu.c1
-rw-r--r--arch/sparc/kernel/process.c7
-rw-r--r--arch/sparc/kernel/sparc_ksyms.c5
-rw-r--r--arch/sparc/kernel/sys_sunos.c67
-rw-r--r--arch/sparc/mm/Makefile11
-rw-r--r--arch/sparc/mm/srmmu.c100
-rw-r--r--arch/sparc/mm/turbosparc.S46
-rw-r--r--arch/sparc/prom/bootstr.c11
-rw-r--r--arch/sparc/prom/tree.c8
-rw-r--r--arch/sparc64/Makefile9
-rw-r--r--arch/sparc64/boot/Makefile23
-rw-r--r--arch/sparc64/boot/piggyback.c109
-rw-r--r--arch/sparc64/config.in5
-rw-r--r--arch/sparc64/defconfig97
-rw-r--r--arch/sparc64/kernel/Makefile18
-rw-r--r--arch/sparc64/kernel/binfmt_aout32.c482
-rw-r--r--arch/sparc64/kernel/binfmt_elf32.c2
-rw-r--r--arch/sparc64/kernel/cpu.c19
-rw-r--r--arch/sparc64/kernel/devices.c5
-rw-r--r--arch/sparc64/kernel/dtlb_miss.S33
-rw-r--r--arch/sparc64/kernel/entry.S697
-rw-r--r--arch/sparc64/kernel/etrap.S189
-rw-r--r--arch/sparc64/kernel/hack.S170
-rw-r--r--arch/sparc64/kernel/head.S255
-rw-r--r--arch/sparc64/kernel/ioctl32.c205
-rw-r--r--arch/sparc64/kernel/ioport.c13
-rw-r--r--arch/sparc64/kernel/irq.c161
-rw-r--r--arch/sparc64/kernel/process.c354
-rw-r--r--arch/sparc64/kernel/ptrace.c370
-rw-r--r--arch/sparc64/kernel/rtrap.S217
-rw-r--r--arch/sparc64/kernel/setup.c53
-rw-r--r--arch/sparc64/kernel/signal.c276
-rw-r--r--arch/sparc64/kernel/signal32.c200
-rw-r--r--arch/sparc64/kernel/smp.c347
-rw-r--r--arch/sparc64/kernel/sparc64_ksyms.c35
-rw-r--r--arch/sparc64/kernel/sunos_ioctl32.c281
-rw-r--r--arch/sparc64/kernel/sys32.S427
-rw-r--r--arch/sparc64/kernel/sys_sparc.c3
-rw-r--r--arch/sparc64/kernel/sys_sparc32.c1090
-rw-r--r--arch/sparc64/kernel/sys_sunos32.c1511
-rw-r--r--arch/sparc64/kernel/systbls.S120
-rw-r--r--arch/sparc64/kernel/time.c9
-rw-r--r--arch/sparc64/kernel/traps.c396
-rw-r--r--arch/sparc64/kernel/ttable.S16
-rw-r--r--arch/sparc64/kernel/unaligned.c517
-rw-r--r--arch/sparc64/kernel/winfixup.S123
-rw-r--r--arch/sparc64/lib/Makefile48
-rw-r--r--arch/sparc64/lib/VIS.h113
-rw-r--r--arch/sparc64/lib/VISbzero.S246
-rw-r--r--arch/sparc64/lib/VIScopy.S1060
-rw-r--r--arch/sparc64/lib/VIScsum.S436
-rw-r--r--arch/sparc64/lib/VISmemset.S228
-rw-r--r--arch/sparc64/lib/blockops.S178
-rw-r--r--arch/sparc64/lib/checksum.S871
-rw-r--r--arch/sparc64/lib/copy_from_user.S469
-rw-r--r--arch/sparc64/lib/copy_to_user.S469
-rw-r--r--arch/sparc64/lib/memcpy.S526
-rw-r--r--arch/sparc64/lib/memset.S196
-rw-r--r--arch/sparc64/lib/strlen_user.S47
-rw-r--r--arch/sparc64/mm/Makefile10
-rw-r--r--arch/sparc64/mm/fault.c161
-rw-r--r--arch/sparc64/mm/generic.c36
-rw-r--r--arch/sparc64/mm/init.c235
-rw-r--r--arch/sparc64/mm/modutil.c66
-rw-r--r--arch/sparc64/mm/ultra.S226
-rw-r--r--arch/sparc64/prom/bootstr.c20
-rw-r--r--arch/sparc64/prom/misc.c14
-rw-r--r--arch/sparc64/prom/p1275.c84
-rw-r--r--arch/sparc64/vmlinux.lds12
-rw-r--r--drivers/block/floppy.c2
-rw-r--r--drivers/block/ide.c1
-rw-r--r--drivers/block/ide.h2
-rw-r--r--drivers/block/loop.c4
-rw-r--r--drivers/block/md.c4
-rw-r--r--drivers/block/rd.c29
-rw-r--r--drivers/block/triton.c114
-rw-r--r--drivers/char/ChangeLog24
-rw-r--r--drivers/char/Config.in2
-rw-r--r--drivers/char/Makefile18
-rw-r--r--drivers/char/console.c58
-rw-r--r--drivers/char/cyclades.c353
-rw-r--r--drivers/char/joystick.c376
-rw-r--r--drivers/char/lp.c4
-rw-r--r--drivers/char/mem.c11
-rw-r--r--drivers/char/misc.c3
-rw-r--r--drivers/char/n_tty.c93
-rw-r--r--drivers/char/pc110pad.c690
-rw-r--r--drivers/char/pc110pad.h31
-rw-r--r--drivers/char/psaux.c32
-rw-r--r--drivers/char/pty.c97
-rw-r--r--drivers/char/random.c150
-rw-r--r--drivers/char/rtc.c10
-rw-r--r--drivers/char/serial.c40
-rw-r--r--drivers/char/sysrq.c4
-rw-r--r--drivers/char/tty_io.c594
-rw-r--r--drivers/char/vc_screen.c5
-rw-r--r--drivers/isdn/avmb1/capiutil.c6
-rw-r--r--drivers/net/8390.c1
-rw-r--r--drivers/net/Config.in22
-rw-r--r--drivers/net/Makefile18
-rw-r--r--drivers/net/README.wanpipe12
-rw-r--r--drivers/net/Space.c15
-rw-r--r--drivers/net/cops.c1018
-rw-r--r--drivers/net/cops.h60
-rw-r--r--drivers/net/cops_ffdrv.h533
-rw-r--r--drivers/net/cops_ltdrv.h242
-rw-r--r--drivers/net/ibmtr.c11
-rw-r--r--drivers/net/lapbether.c2
-rw-r--r--drivers/net/sdla_fr.c339
-rw-r--r--drivers/net/sdla_ppp.c142
-rw-r--r--drivers/net/sdla_x25.c174
-rw-r--r--drivers/net/sdlamain.c45
-rw-r--r--drivers/net/shaper.c22
-rw-r--r--drivers/net/strip.c1940
-rw-r--r--drivers/net/sunhme.c17
-rw-r--r--drivers/net/sunqe.c14
-rw-r--r--drivers/net/tlan.c2309
-rw-r--r--drivers/net/tlan.h485
-rw-r--r--drivers/net/tulip.c1
-rw-r--r--drivers/pci/pci.c2
-rw-r--r--drivers/pnp/parport_procfs.c3
-rw-r--r--drivers/sbus/char/bwtwo.c12
-rw-r--r--drivers/sbus/char/cgfourteen.c11
-rw-r--r--drivers/sbus/char/cgsix.c65
-rw-r--r--drivers/sbus/char/cgthree.c11
-rw-r--r--drivers/sbus/char/creator.c604
-rw-r--r--drivers/sbus/char/fb.h15
-rw-r--r--drivers/sbus/char/leo.c41
-rw-r--r--drivers/sbus/char/openprom.c10
-rw-r--r--drivers/sbus/char/suncons.c89
-rw-r--r--drivers/sbus/char/sunfb.c20
-rw-r--r--drivers/sbus/char/sunkbd.c20
-rw-r--r--drivers/sbus/char/sunmouse.c2
-rw-r--r--drivers/sbus/char/sunserial.c11
-rw-r--r--drivers/sbus/char/sunserial.h2
-rw-r--r--drivers/sbus/char/tcx.c12
-rw-r--r--drivers/sbus/char/vfc_dev.c11
-rw-r--r--drivers/sbus/char/weitek.c11
-rw-r--r--drivers/scsi/BusLogic.h3
-rw-r--r--drivers/scsi/README.in200025
-rw-r--r--drivers/scsi/advansys.h2
-rw-r--r--drivers/scsi/amiga7xx.c13
-rw-r--r--drivers/scsi/in2000.c631
-rw-r--r--drivers/scsi/in2000.h359
-rw-r--r--drivers/scsi/ppa.c2
-rw-r--r--drivers/scsi/qlogicpti.c12
-rw-r--r--drivers/scsi/scsi.c4
-rw-r--r--drivers/scsi/sg.c2
-rw-r--r--drivers/scsi/sr_ioctl.c1
-rw-r--r--drivers/scsi/tmscsim.c49
-rw-r--r--drivers/scsi/wd33c93.c201
-rw-r--r--drivers/scsi/wd33c93.h4
-rw-r--r--drivers/sound/Config.in284
-rw-r--r--drivers/sound/dev_table.h1
-rw-r--r--drivers/sound/soundcard.c5
-rw-r--r--fs/Config.in20
-rw-r--r--fs/affs/Changes20
-rw-r--r--fs/affs/amigaffs.c21
-rw-r--r--fs/affs/file.c80
-rw-r--r--fs/affs/inode.c10
-rw-r--r--fs/affs/namei.c10
-rw-r--r--fs/attr.c2
-rw-r--r--fs/autofs/autofs_i.h11
-rw-r--r--fs/autofs/dir.c24
-rw-r--r--fs/autofs/dirhash.c28
-rw-r--r--fs/autofs/init.c2
-rw-r--r--fs/autofs/inode.c251
-rw-r--r--fs/autofs/root.c270
-rw-r--r--fs/autofs/symlink.c14
-rw-r--r--fs/autofs/waitq.c20
-rw-r--r--fs/binfmt_aout.c45
-rw-r--r--fs/binfmt_elf.c116
-rw-r--r--fs/binfmt_em86.c26
-rw-r--r--fs/binfmt_java.c40
-rw-r--r--fs/binfmt_misc.c38
-rw-r--r--fs/binfmt_script.c21
-rw-r--r--fs/buffer.c89
-rw-r--r--fs/dcache.c1168
-rw-r--r--fs/devices.c4
-rw-r--r--fs/dquot.c87
-rw-r--r--fs/exec.c108
-rw-r--r--fs/ext2/balloc.c6
-rw-r--r--fs/ext2/dir.c6
-rw-r--r--fs/ext2/file.c5
-rw-r--r--fs/ext2/ialloc.c56
-rw-r--r--fs/ext2/inode.c21
-rw-r--r--fs/ext2/ioctl.c4
-rw-r--r--fs/ext2/namei.c416
-rw-r--r--fs/ext2/super.c10
-rw-r--r--fs/ext2/symlink.c37
-rw-r--r--fs/ext2/truncate.c209
-rw-r--r--fs/fat/cache.c2
-rw-r--r--fs/fat/file.c8
-rw-r--r--fs/fat/inode.c13
-rw-r--r--fs/fat/misc.c29
-rw-r--r--fs/fat/mmap.c7
-rw-r--r--fs/fcntl.c55
-rw-r--r--fs/fifo.c1
-rw-r--r--fs/file_table.c42
-rw-r--r--fs/filesystems.c5
-rw-r--r--fs/inode.c992
-rw-r--r--fs/ioctl.c21
-rw-r--r--fs/isofs/dir.c19
-rw-r--r--fs/isofs/file.c1
-rw-r--r--fs/isofs/inode.c15
-rw-r--r--fs/isofs/namei.c82
-rw-r--r--fs/lockd/clntlock.c2
-rw-r--r--fs/lockd/clntproc.c2
-rw-r--r--fs/lockd/svclock.c16
-rw-r--r--fs/locks.c68
-rw-r--r--fs/minix/bitmap.c8
-rw-r--r--fs/minix/dir.c1
-rw-r--r--fs/minix/file.c3
-rw-r--r--fs/minix/inode.c39
-rw-r--r--fs/minix/namei.c293
-rw-r--r--fs/minix/symlink.c18
-rw-r--r--fs/minix/truncate.c14
-rw-r--r--fs/msdos/namei.c31
-rw-r--r--fs/namei.c1615
-rw-r--r--fs/ncpfs/inode.c2
-rw-r--r--fs/ncpfs/mmap.c4
-rw-r--r--fs/nfs/dir.c546
-rw-r--r--fs/nfs/file.c7
-rw-r--r--fs/nfs/inode.c31
-rw-r--r--fs/nfs/nfsroot.c15
-rw-r--r--fs/nfs/read.c2
-rw-r--r--fs/nfs/symlink.c36
-rw-r--r--fs/nfs/write.c5
-rw-r--r--fs/nfsd/export.c2
-rw-r--r--fs/nfsd/vfs.c34
-rw-r--r--fs/open.c425
-rw-r--r--fs/pipe.c73
-rw-r--r--fs/proc/Makefile2
-rw-r--r--fs/proc/arbitrary.c58
-rw-r--r--fs/proc/array.c15
-rw-r--r--fs/proc/base.c1
-rw-r--r--fs/proc/fd.c49
-rw-r--r--fs/proc/generic.c11
-rw-r--r--fs/proc/inode.c23
-rw-r--r--fs/proc/kmsg.c1
-rw-r--r--fs/proc/link.c115
-rw-r--r--fs/proc/mem.c1
-rw-r--r--fs/proc/net.c1
-rw-r--r--fs/proc/omirr.c2
-rw-r--r--fs/proc/openpromfs.c115
-rw-r--r--fs/proc/procfs_syms.c2
-rw-r--r--fs/proc/root.c149
-rw-r--r--fs/proc/scsi.c1
-rw-r--r--fs/read_write.c68
-rw-r--r--fs/readdir.c232
-rw-r--r--fs/romfs/inode.c2
-rw-r--r--fs/smbfs/inode.c2
-rw-r--r--fs/smbfs/mmap.c4
-rw-r--r--fs/stat.c155
-rw-r--r--fs/super.c382
-rw-r--r--fs/sysv/dir.c1
-rw-r--r--fs/sysv/file.c7
-rw-r--r--fs/sysv/ialloc.c11
-rw-r--r--fs/sysv/inode.c15
-rw-r--r--fs/sysv/namei.c81
-rw-r--r--fs/sysv/symlink.c18
-rw-r--r--fs/sysv/truncate.c12
-rw-r--r--fs/ufs/ufs_file.c3
-rw-r--r--fs/ufs/ufs_inode.c5
-rw-r--r--fs/ufs/ufs_namei.c6
-rw-r--r--fs/ufs/ufs_super.c4
-rw-r--r--fs/vfat/namei.c20
-rw-r--r--include/asm-alpha/ioctls.h2
-rw-r--r--include/asm-alpha/pgtable.h3
-rw-r--r--include/asm-i386/ioctls.h3
-rw-r--r--include/asm-i386/pgtable.h3
-rw-r--r--include/asm-i386/unistd.h2
-rw-r--r--include/asm-m68k/hardirq.h2
-rw-r--r--include/asm-m68k/ioctls.h3
-rw-r--r--include/asm-m68k/pgtable.h26
-rw-r--r--include/asm-mips/ioctls.h2
-rw-r--r--include/asm-mips/namei.h4
-rw-r--r--include/asm-mips/pgtable.h3
-rw-r--r--include/asm-mips/system.h14
-rw-r--r--include/asm-mips/unistd.h4
-rw-r--r--include/asm-ppc/ioctls.h2
-rw-r--r--include/asm-ppc/pgtable.h3
-rw-r--r--include/asm-sparc/asi.h6
-rw-r--r--include/asm-sparc/ioctls.h4
-rw-r--r--include/asm-sparc/mbus.h6
-rw-r--r--include/asm-sparc/namei.h6
-rw-r--r--include/asm-sparc/oplib.h6
-rw-r--r--include/asm-sparc/pgtable.h7
-rw-r--r--include/asm-sparc/turbosparc.h114
-rw-r--r--include/asm-sparc64/asm_offsets.h80
-rw-r--r--include/asm-sparc64/atomic.h86
-rw-r--r--include/asm-sparc64/bitops.h74
-rw-r--r--include/asm-sparc64/byteorder.h14
-rw-r--r--include/asm-sparc64/checksum.h51
-rw-r--r--include/asm-sparc64/delay.h6
-rw-r--r--include/asm-sparc64/elf.h4
-rw-r--r--include/asm-sparc64/fbio.h49
-rw-r--r--include/asm-sparc64/floppy.h4
-rw-r--r--include/asm-sparc64/fpumacro.h68
-rw-r--r--include/asm-sparc64/fs_mount.h44
-rw-r--r--include/asm-sparc64/hardirq.h4
-rw-r--r--include/asm-sparc64/head.h71
-rw-r--r--include/asm-sparc64/ioctls.h13
-rw-r--r--include/asm-sparc64/mmu_context.h77
-rw-r--r--include/asm-sparc64/namei.h5
-rw-r--r--include/asm-sparc64/page.h17
-rw-r--r--include/asm-sparc64/pgtable.h294
-rw-r--r--include/asm-sparc64/processor.h70
-rw-r--r--include/asm-sparc64/psrcompat.h28
-rw-r--r--include/asm-sparc64/pstate.h8
-rw-r--r--include/asm-sparc64/ptrace.h28
-rw-r--r--include/asm-sparc64/reg.h31
-rw-r--r--include/asm-sparc64/resource.h3
-rw-r--r--include/asm-sparc64/sigcontext.h31
-rw-r--r--include/asm-sparc64/softirq.h2
-rw-r--r--include/asm-sparc64/spinlock.h62
-rw-r--r--include/asm-sparc64/string.h31
-rw-r--r--include/asm-sparc64/system.h63
-rw-r--r--include/asm-sparc64/uaccess.h104
-rw-r--r--include/asm-sparc64/uctx.h71
-rw-r--r--include/asm-sparc64/unistd.h38
-rw-r--r--include/asm-sparc64/vaddrs.h17
-rw-r--r--include/linux/binfmts.h8
-rw-r--r--include/linux/console_struct.h9
-rw-r--r--include/linux/cyclades.h22
-rw-r--r--include/linux/dcache.h122
-rw-r--r--include/linux/ext2_fs.h23
-rw-r--r--include/linux/file.h6
-rw-r--r--include/linux/fs.h217
-rw-r--r--include/linux/ghash.h218
-rw-r--r--include/linux/if_arp.h2
-rw-r--r--include/linux/if_shaper.h (renamed from drivers/net/shaper.h)0
-rw-r--r--include/linux/iso_fs.h5
-rw-r--r--include/linux/iso_fs_i.h1
-rw-r--r--include/linux/joystick.h61
-rw-r--r--include/linux/list.h63
-rw-r--r--include/linux/lockd/lockd.h2
-rw-r--r--include/linux/minix_fs.h25
-rw-r--r--include/linux/miscdevice.h1
-rw-r--r--include/linux/mm.h4
-rw-r--r--include/linux/msdos_fs.h2
-rw-r--r--include/linux/net.h3
-rw-r--r--include/linux/nfsd/nfsfh.h26
-rw-r--r--include/linux/omirr.h17
-rw-r--r--include/linux/pci.h2
-rw-r--r--include/linux/pipe_fs_i.h3
-rw-r--r--include/linux/proc_fs.h12
-rw-r--r--include/linux/random.h2
-rw-r--r--include/linux/rose.h23
-rw-r--r--include/linux/sched.h10
-rw-r--r--include/linux/sdla_fr.h26
-rw-r--r--include/linux/sdla_x25.h2
-rw-r--r--include/linux/selection.h6
-rw-r--r--include/linux/simp.h39
-rw-r--r--include/linux/slab.h6
-rw-r--r--include/linux/swap.h3
-rw-r--r--include/linux/sysctl.h1
-rw-r--r--include/linux/sysv_fs.h6
-rw-r--r--include/linux/tqueue.h2
-rw-r--r--include/linux/tty.h12
-rw-r--r--include/linux/ufs_fs.h4
-rw-r--r--include/linux/vmalloc.h3
-rw-r--r--include/linux/wanpipe.h24
-rw-r--r--include/linux/wanrouter.h31
-rw-r--r--include/linux/wrapper.h6
-rw-r--r--include/linux/x25.h42
-rw-r--r--include/net/ax25.h59
-rw-r--r--include/net/lapb.h20
-rw-r--r--include/net/netrom.h47
-rw-r--r--include/net/rose.h65
-rw-r--r--include/net/tcp.h21
-rw-r--r--include/net/x25.h35
-rw-r--r--init/main.c15
-rw-r--r--ipc/shm.c2
-rw-r--r--kernel/exit.c11
-rw-r--r--kernel/fork.c16
-rw-r--r--kernel/ksyms.c20
-rw-r--r--kernel/module.c12
-rw-r--r--kernel/sys.c114
-rw-r--r--kernel/sysctl.c24
-rw-r--r--mm/Makefile2
-rw-r--r--mm/filemap.c26
-rw-r--r--mm/mlock.c11
-rw-r--r--mm/mmap.c63
-rw-r--r--mm/mprotect.c10
-rw-r--r--mm/mremap.c3
-rw-r--r--mm/page_io.c2
-rw-r--r--mm/simp.c434
-rw-r--r--mm/slab.c72
-rw-r--r--mm/swapfile.c78
-rw-r--r--mm/vmalloc.c8
-rw-r--r--mm/vmscan.c19
-rw-r--r--net/Config.in2
-rw-r--r--net/README10
-rw-r--r--net/appletalk/ddp.c32
-rw-r--r--net/appletalk/sysctl_net_atalk.c16
-rw-r--r--net/ax25/af_ax25.c308
-rw-r--r--net/ax25/ax25_addr.c46
-rw-r--r--net/ax25/ax25_dev.c2
-rw-r--r--net/ax25/ax25_ds_in.c86
-rw-r--r--net/ax25/ax25_ds_subr.c14
-rw-r--r--net/ax25/ax25_ds_timer.c172
-rw-r--r--net/ax25/ax25_iface.c21
-rw-r--r--net/ax25/ax25_in.c17
-rw-r--r--net/ax25/ax25_ip.c31
-rw-r--r--net/ax25/ax25_out.c152
-rw-r--r--net/ax25/ax25_route.c80
-rw-r--r--net/ax25/ax25_std_in.c132
-rw-r--r--net/ax25/ax25_std_subr.c14
-rw-r--r--net/ax25/ax25_std_timer.c139
-rw-r--r--net/ax25/ax25_subr.c34
-rw-r--r--net/ax25/ax25_timer.c202
-rw-r--r--net/ax25/ax25_uid.c2
-rw-r--r--net/ax25/sysctl_net_ax25.c10
-rw-r--r--net/core/scm.c4
-rw-r--r--net/core/sysctl_net_core.c4
-rw-r--r--net/ipv4/Config.in1
-rw-r--r--net/ipv4/Makefile5
-rw-r--r--net/ipv4/raw.c2
-rw-r--r--net/ipv4/route.c9
-rw-r--r--net/ipv4/syncookies.c218
-rw-r--r--net/ipv4/sysctl_net_ipv4.c8
-rw-r--r--net/ipv4/tcp_input.c44
-rw-r--r--net/ipv4/tcp_ipv4.c196
-rw-r--r--net/ipv4/utils.c23
-rw-r--r--net/ipv6/icmp.c20
-rw-r--r--net/ipv6/tcp_ipv6.c54
-rw-r--r--net/lapb/lapb_iface.c55
-rw-r--r--net/lapb/lapb_in.c110
-rw-r--r--net/lapb/lapb_out.c22
-rw-r--r--net/lapb/lapb_subr.c8
-rw-r--r--net/lapb/lapb_timer.c123
-rw-r--r--net/netlink.c2
-rw-r--r--net/netrom/af_netrom.c184
-rw-r--r--net/netrom/nr_dev.c2
-rw-r--r--net/netrom/nr_in.c60
-rw-r--r--net/netrom/nr_out.c104
-rw-r--r--net/netrom/nr_route.c27
-rw-r--r--net/netrom/nr_subr.c26
-rw-r--r--net/netrom/nr_timer.c208
-rw-r--r--net/netrom/sysctl_net_netrom.c16
-rw-r--r--net/rose/af_rose.c224
-rw-r--r--net/rose/rose_dev.c4
-rw-r--r--net/rose/rose_in.c95
-rw-r--r--net/rose/rose_link.c111
-rw-r--r--net/rose/rose_out.c58
-rw-r--r--net/rose/rose_route.c162
-rw-r--r--net/rose/rose_subr.c56
-rw-r--r--net/rose/rose_timer.c172
-rw-r--r--net/rose/sysctl_net_rose.c12
-rw-r--r--net/socket.c33
-rw-r--r--net/unix/af_unix.c53
-rw-r--r--net/unix/garbage.c2
-rw-r--r--net/unix/sysctl_net_unix.c4
-rw-r--r--net/wanrouter/patchlevel3
-rw-r--r--net/wanrouter/wanmain.c20
-rw-r--r--net/wanrouter/wanproc.c190
-rw-r--r--net/x25/af_x25.c168
-rw-r--r--net/x25/sysctl_net_x25.c4
-rw-r--r--net/x25/x25_dev.c4
-rw-r--r--net/x25/x25_facilities.c2
-rw-r--r--net/x25/x25_in.c62
-rw-r--r--net/x25/x25_link.c79
-rw-r--r--net/x25/x25_out.c52
-rw-r--r--net/x25/x25_route.c16
-rw-r--r--net/x25/x25_subr.c24
-rw-r--r--net/x25/x25_timer.c134
-rw-r--r--scripts/ksymoops.cc34
518 files changed, 30304 insertions, 17003 deletions
diff --git a/CREDITS b/CREDITS
index 33d2c8b89..c903f985a 100644
--- a/CREDITS
+++ b/CREDITS
@@ -81,6 +81,14 @@ S: University of Notre Dame
S: Notre Dame, Indiana
S: USA
+N: James Banks
+E: james.banks@caldera.com
+D: TLAN network driver
+S: Caldera, Inc.
+S: 633 South 550 East
+S: Provo, UT 84606
+S: USA
+
N: Peter Bauer
E: 100136.3530@compuserve.com
D: Driver for depca-ethernet-board
@@ -107,7 +115,9 @@ S: USA
N: Randolph Bentson
E: bentson@grieg.seaslug.org
-D: author of driver for Cyclades Cyclom-Y async mux
+D: author of driver for Cyclom-Y and Cyclades-Z async mux
+P: 1024/39ED5729 5C A8 7A F4 B2 7A D1 3E B5 3B 81 CF 47 30 11 71
+W: http://www.aa.net/~bentson/
S: 2322 37th Ave SW
S: Seattle, Washington 98126-2010
S: USA
@@ -793,6 +803,12 @@ S: Schlehenweg 9
S: D-91080 Uttenreuth
S: Germany
+N: Jaroslav Kysela
+E: perex@jcu.cz
+D: Original Author and Maintainer for HP 10/100 Mbit Network Adapters
+W: http://www.pf.jcu.cz/~perex
+S: Unix Centre of Pedagogical Faculty, University of South Bohemia
+
N: Bas Laarhoven
E: bas@vimec.nl
D: Loadable modules and ftape driver
@@ -842,6 +858,13 @@ S: PO Box 371
S: North Little Rock, Arkansas 72115
S: US
+N: Siegfried "Frieder" Loeffler (dg1sek)
+E: floeff@tunix.mathematik.uni-stuttgart.de, fl@LF.net
+W: http://www.mathematik.uni-stuttgart.de/~floeff
+D: Busmaster driver for HP 10/100 Mbit Network Adapters
+S: University of Stuttgart, Germany and
+S: Ecole Nationale Superieure des Telecommunications, Paris
+
N: Martin von Loewis
E: loewis@informatik.hu-berlin.de
D: script binary format
@@ -956,12 +979,13 @@ S: D-91056 Erlangen
S: Germany
N: Michael Meskes
-E: meskes@informatik.rwth-aachen.de
+E: meskes@topsystem.de
+P: 1024/04B6E8F5 6C 77 33 CA CC D6 22 03 AB AB 15 A3 AE AD 39 7D
D: Kernel hacker. Software watchdog daemon.
D: Maintainer of several Debian packages
-S: Lehrstuhl fuer angewandte Mathematik insb. Informatik
-S: RWTH-Aachen
-S: D-52056 Aachen
+S: topsystem Systemhaus GmbH
+S: Europark A2, Adenauerstr. 20
+S: D-52146 Wuerselen
S: Germany
N: Nigel Metheringham
@@ -1055,8 +1079,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/Configure.help b/Documentation/Configure.help
index 4e9e6678d..a9274b91a 100644
--- a/Documentation/Configure.help
+++ b/Documentation/Configure.help
@@ -287,17 +287,19 @@ CONFIG_BLK_DEV_RZ1000
Linux. This may slow disk throughput by a few percent, but at least
things will operate 100% reliably. If unsure, say Y.
-Intel 82371 PIIX (Triton I/II) DMA support
+Intel 82371 PIIX (Triton I/II), VIA VP-1 DMA support
CONFIG_BLK_DEV_TRITON
If your PCI system uses an IDE harddrive (as opposed to SCSI, say)
and includes the Intel Triton I/II IDE interface chipset (i82371FB,
- i82371SB or i82371AB), you will want to enable this option to allow
- use of bus-mastering DMA data transfers. Read the comments at the
+ i82371SB or i82371AB), or the VIA VP-1 IDE interface chipset
+ (VT82C586), you will want to enable this option to allow use of
+ bus-mastering DMA data transfers. Read the comments at the
beginning of drivers/block/triton.c and Documentation/ide.txt.
You can get the latest version of the hdparm utility via
ftp (user: anonymous) from
sunsite.unc.edu/pub/Linux/kernel/patches/diskdrives/; it is
- used to tune your harddisk. It is safe to say Y to this question.
+ used to tune your harddisk.
+ It is safe to say Y to this question.
Other IDE chipset support
CONFIG_IDE_CHIPSETS
@@ -541,6 +543,19 @@ CONFIG_FIREWALL
proxy server). Chances are that you should use this on every machine
being run as a router and not on any regular host. If unsure, say N.
+SYN flood protection
+CONFIG_SYN_COOKIES
+ Normal TCP/IP networking is open to an attack known as SYN flooding.
+ This attack prevents legitimate users from being able to connect to
+ your computer and requires very little work for the attacker.
+ SYN cookies provide protection against this type of attack. With
+ this option turned on the TCP/IP stack will use a cryptographic
+ challenge protocol known as SYN cookies to enable legitimate users
+ to continue to connect, even when your machine is under attack.
+ Note that SYN cookies aren't enabled per default, you need to add
+ echo 1 >/proc/sys/net/ipv4/tcp_syncookies to one of your startup scripts
+ (e.g. /etc/rc.local or /etc/rc.d/rc.local).
+
Socket Security API Support (EXPERIMENTAL)
CONFIG_NET_SECURITY
Enable use of the socket security API. Note that Linux does not include
@@ -1331,7 +1346,7 @@ CONFIG_IPDDP
networking available. This feature is experimental. Please see
http://www.maths.unm.edu/~bradford/ltpc.html for support software.
-LocalTalk PC card support
+Apple/Farallon LocalTalk PC card support
CONFIG_LTPC
This allows you to use the AppleTalk PC card to connect to LocalTalk
networks. The card is also known as the Farallon PhoneNet PC card.
@@ -1343,6 +1358,26 @@ CONFIG_LTPC
See README.ltpc in the drivers/net directory, and the web site
http://www.math.unm.edu/~bradford/ltpc.html
+COPS LocalTalk PC card support
+CONFIG_COPS
+ This allows you to use the COPS AppleTalk card to connect to LocalTalk
+ networks. You also need version 1.3.3 or later of the netatalk package.
+ This driver is experimental, which means that it may not work.
+ In particular the module support is not yet working for the 2.1.xx
+ kernels, so choose Y or N, but not M for now.
+ See the web site http://www.math.unm.edu/~bradford/ltpc.html for localtalk
+ IP tools.
+
+Dayna firmware support
+CONFIG_COPS_DAYNA
+ Support COPS compatible cards with Dayna style firmware (Dayna DL2000/
+ Daynatalk/PC (half length), COPS LT-95, Farallon PhoneNET PC III)
+
+Tangent firmware support
+CONFIG_COPS_TANGENT
+ Support COPS compatible cards with Tangent style firmware (Tangent ATB_II,
+ Novell NL-1000, Daystar Digital LT-200
+
Amateur Radio AX.25 Level 2
CONFIG_AX25
This is the protocol used for computer communication over amateur
@@ -3280,6 +3315,14 @@ CONFIG_ETH16I
Multiple-Ethernet-mini-HOWTO, available from
sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+TI ThunderLAN support (EXPERIMENTAL)
+CONFIG_TLAN
+ If you have a TLAN based network card which is supported by this
+ driver, say Y and read the Ethernet-HOWTO. Devices currently supported
+ are the Compaq Netelligent 10, Netelligent 10/100, and Internal
+ NetFlex 3. This driver is also available as a module. Please email
+ feedback to james.banks@caldera.com.
+
Zenith Z-Note support
CONFIG_ZNET
The Zenith Z-Note notebook computer has a built-in network
@@ -4357,6 +4400,12 @@ CONFIG_82C710_MOUSE
doesn't work. Read the Busmouse-HOWTO, available via ftp (user:
anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO.
+PC110 digitizer pad support
+CONFIG_PC110_PAD
+ This drives the digitizer pad on the IBM PC110 palmtop (see
+ http://toy.cabi.net). It can turn the digitizer pad into a
+ mouse emulation with tap gestures or into an absolute pad.
+
Microsoft busmouse support
CONFIG_MS_BUSMOUSE
These animals (also called Inport mice) are connected to an
diff --git a/Documentation/binfmt_misc.txt b/Documentation/binfmt_misc.txt
index 81d49b2bc..aef3b271a 100644
--- a/Documentation/binfmt_misc.txt
+++ b/Documentation/binfmt_misc.txt
@@ -1,26 +1,26 @@
- Kernel Support for miscellaneous (your favourite) Binary Formats v1.1
- ====================================================================
+ 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.
+program by simply typing its name in the shell.
This includes for example compiled Java(TM), Python or Emacs programs.
-To achieve this you must tell binfmt_misc which interpreter has to be invoked with
-which binary. Binfmt_misc recognises the binary-type by matching some bytes at the
-beginning of the file with a magic byte sequence (masking out specified bits) you
-have supplied. Binfmt_misc can also recognise a filename extension (aka .com) and
-optionally strip it off.
+To achieve this you must tell binfmt_misc which interpreter has to be invoked
+with which binary. Binfmt_misc recognises the binary-type by matching some bytes
+at the beginning of the file with a magic byte sequence (masking out specified
+bits) you have supplied. Binfmt_misc can also recognise a filename extension
+(aka .com) and optionally strip it off.
To actually register a new binary type, you have to set up a string looking like
-:name:type:offset:magic:mask:interpreter: (where you can choose the ':' upon your
-needs) and echo it to /proc/sys/fs/binfmt_misc/register.
+:name:type:offset:magic:mask:interpreter: (where you can choose the ':' upon
+your needs) and echo it to /proc/sys/fs/binfmt_misc/register.
Here is what the fields mean:
- 'name' is an identifier string. A new /proc file will be created with this
- this name below /proc/sys/fs/binfmt_misc
+ name below /proc/sys/fs/binfmt_misc
- 'type' is the type of recognition. Give 'M' for magic and 'E' for extension.
- Give the corresponding lowercase letter to let binfmt_misc strip of the
+ Give the corresponding lowercase letter to let binfmt_misc strip off the
filename extension.
- - 'offset' is the offset of the magic/mask in the file counted in bytes. This
+ - 'offset' is the offset of the magic/mask in the file, counted in bytes. This
defaults to 0 if you omit it (i.e. you write ':name:type::magic...')
- 'magic' is the byte sequence binfmt_misc is matching for. The magic string
may contain hex-encoded characters like \x0a or \xA4. In a shell environment
@@ -28,33 +28,34 @@ Here is what the fields mean:
If you chose filename extension matching, this is the extension to be
recognised (the \x0a specials are not allowed). Extension matching is case
sensitive!
- - 'mask' is an (optional, defaults to all 0xff) mask. You can mask out some bits
- from matching by supplying a string like magic and as long as magic. The
- mask is anded with the byte sequence of the file.
+ - 'mask' is an (optional, defaults to all 0xff) mask. You can mask out some
+ bits from matching by supplying a string like magic and as long as magic.
+ The mask is anded with the byte sequence of the file.
- 'interpreter' is the program that should be invoked with the binary as first
argument (specify the full path)
There are some restrictions:
- the whole register string may not exceed 255 characters
- - the magic must resist in the first 128 bytes of the file, i.e. offset+size(magic)
- has to be less than 128
+ - the magic must resist in the first 128 bytes of the file, i.e.
+ offset+size(magic) has to be less than 128
- the interpreter string may not exceed 127 characters
-You may want to add the binary formats in one of your /etc/rc scripts during boot-up.
-Read the manual of your init program to figure out how to do this right.
+You may want to add the binary formats in one of your /etc/rc scripts during
+boot-up. Read the manual of your init program to figure out how to do this
+right.
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 ":DOScom:E::com::/usr/sbin/mzloader:" > register
+ echo ":DOSexe:E::exe::/usr/sbin/mzloader:" > register
You can enable/disable binfmt_misc or one binary type by echoing 0 (to disable)
@@ -65,16 +66,34 @@ You can remove one entry or all entries by echoing -1 to /proc/.../the_name
or /proc/sys/fs/binfmt_misc/status.
+Emulating binfmt_java:
+======================
+
+To emulate binfmt_java the following register-strings are necessary
+(the first two for byte-compiled Java binaries, the third for applets
+contained in a html-file). Register exactly in this order!
+ ":Java:M::\xca\xfe\xba\xbe::/usr/local/java/bin/java:"
+ ":JavaC:e::class::/usr/local/java/bin/java:"
+ ":Applet:E::html::/usr/local/java/bin/appletviewer:"
+
+To add a Java-executable to your path you can either make a symbolic
+link to the .class file elsewhere in your path (cut the .class-extension
+in the destination name for convenience) or add the directory of your
+.class files to your PATH environment. In both cases, ensure that the
+.class files are in your CLASSPATH environment!
+
+This is sort of ugly - Javas filename handling is just broken.
+
+
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/ioctl-number.txt b/Documentation/ioctl-number.txt
index 7d86d3168..790bb18c7 100644
--- a/Documentation/ioctl-number.txt
+++ b/Documentation/ioctl-number.txt
@@ -91,6 +91,7 @@ Code Seq# Include File Comments
<mailto:natalia@nikhefk.nikhef.nl>
'c' all linux/comstats.h
'f' all linux/ext2_fs.h
+'k' all asm-sparc/kbio.h, asm-sparc64/kbio.h
'l' 00-3F linux/tcfs_fs.h in development:
<http://mikonos.dia.unisa.it/tcfs>
'm' all linux/mtio.h conflict!
diff --git a/Documentation/m68k/amiboot.txt b/Documentation/m68k/amiboot.txt
index c119c6357..c6b369e28 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/README.cops b/Documentation/networking/README.cops
new file mode 100644
index 000000000..7956fd213
--- /dev/null
+++ b/Documentation/networking/README.cops
@@ -0,0 +1,39 @@
+README for the COPS LocalTalk Linux driver (cops.c).
+ By Jay Schulist <Jay.Schulist@spacs.k12.wi.us>
+
+This driver compiles well against 2.1.29 - 2.1.41.
+
+Building the driver from the cops-0.0*.tar.gz file.
+1. Untar the cops-0.0*.tar.gz it will make a directory called cops.
+2. Copy the cops-kernel.diff to /usr/src/ and then type patch -p0 < cops-kernel.diff
+3. In the cops driver directory type make install. It will copy the driver to
+ the linux/drivers/net directory.
+4. When you configure your kernel select Y or M for COPS LocalTalk PC support.
+ Also make sure you have choosen Appletalk support.
+5. Compile like usual and you should bet set.
+
+This driver has 2 modes and they are: Dayna mode and Tangent mode.
+Each mode corresponds with the type of card. It has been found
+that there are 2 main types of cards and all other cards are
+the same and just have different names or only have minor differences
+such as more IO ports. As this driver is tested it will
+become more clear on exactly what cards are supported. The driver
+defaults to using Dayna mode. To change the drivers mode if you build
+a driver with dual support use board_type=1 or board_type=2 for
+dayna and tangent in the insmod.
+
+Operation/loading of the driver.
+Use modprobe like this: /sbin/modprobe cops.o (IO #) (IRQ #)
+If you do not specify any options the driver will try and use the IO = 0x240,
+IRQ = 5. As of right now I would only use IRQ 5 for the card, if autoprobing.
+
+Use ifconfig like this: /sbin/ifconfig lt0 127.0.0.34 up
+
+You will need to configure atalkd with something like the following to make
+it work with the cops.c driver.
+
+dummy -seed -phase 2 -net 2000 -addr 2000.10 -zone "1033"
+lt0 -seed -phase 1 -net 1000 -addr 1000.50 -zone "1033"
+- Or -
+eth0 -seed -phase 2 -net 3000 -addr 3000.20 -zone "1033"
+lt0 -seed -phase 1 -net 1000 -addr 1000.50 -zone "1033"
diff --git a/Documentation/networking/ax25.txt b/Documentation/networking/ax25.txt
index 937b9efe6..7572cf733 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 <terry@perf.no.itg.telstra.com.au>
@@ -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/net-modules.txt b/Documentation/networking/net-modules.txt
index bdf5a34a1..f0f3ab470 100644
--- a/Documentation/networking/net-modules.txt
+++ b/Documentation/networking/net-modules.txt
@@ -159,6 +159,13 @@ atp.c: *Not modularized*
(Probes ports: 0x378, 0x278, 0x3BC;
fixed IRQs: 5 and 7 )
+cops.c:
+ io = 0x240
+ irq = 5
+ nodeid = 0 (AutoSelect = 0, NodeID 1-254 is hand selected.)
+ (Probes ports: 0x240, 0x340, 0x200, 0x210, 0x220, 0x230, 0x260,
+ 0x2A0, 0x300, 0x310, 0x320, 0x330, 0x350, 0x360)
+
de4x5.c:
io = 0x000b
irq = 10
diff --git a/Documentation/networking/wan-router.txt b/Documentation/networking/wan-router.txt
index f95bc9f14..3062f43d5 100644
--- a/Documentation/networking/wan-router.txt
+++ b/Documentation/networking/wan-router.txt
@@ -1,10 +1,11 @@
------------------------------------------------------------------------------
WAN Router for Linux Operating System
------------------------------------------------------------------------------
-Version 1.0.0
-December 31, 1996
-Author: Gene Kozin <genek@compuserve.com>
-Copyright (c) 1995-1996 Sangoma Technologies Inc.
+Version 1.0.3 - June 3, 1997
+Version 1.0.1 - January 30, 1997
+Author: Jaspreet Singh <jaspreet@sangoma.com>
+ Gene Kozin <genek@compuserve.com>
+Copyright (c) 1995-1997 Sangoma Technologies Inc.
------------------------------------------------------------------------------
INTRODUCTION
@@ -95,12 +96,6 @@ Ave, Cambridge, MA 02139, USA.
-KNOWN BUGS AND LIMITATIONS
-
-/proc user interface is not complete yet.
-
-
-
ACKNOLEGEMENTS
This product is based on the WANPIPE(tm) Multiprotocol WAN Router developed
@@ -122,9 +117,19 @@ product.
REVISION HISTORY
+1.0.3 June 3, 1997
+ o UDP port for multiple boards (Frame relay, PPP)
+ o Continuous Transmission of Configure Request Packet for PPP (this
+ support is only added for 508 cards)
+ o Connection Timeout for PPP changed from 900 to 0
+ o Flow Control for multiple boards and multiple channels (Frame Relay)
+
+1.0.1 January 30, 1997
+
+ o Implemented user-readable status and statistics via /proc filesystem
+
+1.0.0 December 31, 1996
-1.0.0 December 31, 1996
---------------------------
- o Initial version.
+ o Initial version
>>>>>> END <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
diff --git a/Documentation/networking/x25.txt b/Documentation/networking/x25.txt
index 09681d565..67f17e644 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/MAINTAINERS b/MAINTAINERS
index 9012dede9..0b32ffe38 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -95,6 +95,12 @@ M: phil@tazenda.demon.co.uk
L: linux-net@vger.rutgers.edu
S: Maintained
+TLAN NETWORK DRIVER
+P: James Banks
+M: james.banks@caldera.com
+L: linux-net@vger.rutgers.edu
+S: Supported
+
DIGI RIGHTSWITCH NETWORK DRIVER
P: Rick Richardson
M: rick@dgii.com
@@ -107,6 +113,11 @@ P: Jean Tourrilhes
M: jt@hplb.hpl.hp.com
S: Maintained
+HP100: Driver for HP 10/100 Mbit/s Network Adapter Series
+P: Jarsolav Kysela
+M: perex@jcu.cz
+S: Maintained
+
APM DRIVER
P: Rik Faith & Stephen Rothwell
M: faith@cs.unc.edu, Stephen.Rothwell@canb.auug.org.au
@@ -217,10 +228,10 @@ L: linux-tape@vger.rutgers.edu
S: Maintained
IPX NETWORK LAYER
-P: Alan Cox [for the moment]
-M: net-patches@lxorguk.ukuu.org.uk
-L: linux-ipx@vger.rutgers.edu [will change]
-S: Maintained
+P:
+M:
+L:
+S: Orphan
IDE DRIVER [GENERAL]
P: Mark Lord
@@ -267,6 +278,13 @@ W: http://lena.fnet.fr/
L: linux-mips@fnet.fr
S: Maintained
+MIPS:
+P: Ralf Baechle
+M: ralf@gnu.ai.mit.edu
+W: http://lena.fnet.fr/
+L: linux-mips@fnet.fr
+S: Maintained
+
NCP FILESYSTEM:
P: Volker Lendecke
M: lendecke@Math.Uni-Goettingen.de
@@ -280,10 +298,10 @@ L: linux-hams@vger.rutgers.edu
S: Maintained
NETWORKING [GENERAL]:
-P: Alan Cox
-M: net-patches@lxorguk.ukuu.org.uk
+P: Networking Teak
+M: netdev@nuclecu.unam.mx
L: linux-net@vger.rutgers.edu
-W: http://www.uk.linux.org/NetNews.html
+W: http://www.uk.linux.org/NetNews.html (2.0 only)
S: Maintained
NETWORKING [IPv4/IPv6]:
@@ -326,8 +344,8 @@ L: samba@listproc.anu.edu.au
S: Maintained
SMP: (except SPARC)
-P: Alan Cox
-M: smp-patches@lxorguk.ukuu.org.uk
+P: Linus Torvalds
+M: torvalds@transmeta.com
L: linux-smp@vger.rutgers.edu
S: Maintained
diff --git a/Makefile b/Makefile
index a59812c75..c0d5de7f9 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
VERSION = 2
PATCHLEVEL = 1
-SUBLEVEL = 43
+SUBLEVEL = 46
ARCH = mips
diff --git a/arch/alpha/defconfig b/arch/alpha/defconfig
index 0c3c65d28..a36ec98a9 100644
--- a/arch/alpha/defconfig
+++ b/arch/alpha/defconfig
@@ -66,7 +66,7 @@ CONFIG_BLK_DEV_FD=y
#
# CONFIG_BLK_DEV_LOOP is not set
# CONFIG_BLK_DEV_MD is not set
-CONFIG_BLK_DEV_RAM=y
+# CONFIG_BLK_DEV_RAM is not set
# CONFIG_BLK_DEV_INITRD is not set
# CONFIG_BLK_DEV_XD is not set
# CONFIG_BLK_DEV_EZ is not set
@@ -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)
@@ -196,8 +197,8 @@ CONFIG_DE4X5=y
# CONFIG_QUOTA 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
@@ -245,3 +246,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 3b3d8574b..f725e2aba 100644
--- a/arch/alpha/kernel/osf_sys.c
+++ b/arch/alpha/kernel/osf_sys.c
@@ -135,30 +135,46 @@ asmlinkage int osf_getdirentries(unsigned int fd, struct osf_dirent *dirent,
{
int error;
struct file *file;
+ struct dentry *dentry;
+ struct inode *inode;
struct osf_dirent_callback buf;
- if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
- return -EBADF;
- if (!file->f_op || !file->f_op->readdir)
- return -ENOTDIR;
- error = verify_area(VERIFY_WRITE, dirent, count);
- if (error)
- return error;
- if (basep) {
- error = verify_area(VERIFY_WRITE, basep, sizeof(long));
- if (error)
- return error;
- }
+ error = -EBADF;
+ if (fd >= NR_OPEN)
+ goto out;
+
+ file = current->files->fd[fd];
+ if (!file)
+ goto out;
+
+ dentry = file->f_dentry;
+ if (!dentry)
+ goto out;
+
+ inode = dentry->d_inode;
+ if (!inode)
+ goto out;
+
buf.dirent = dirent;
buf.basep = basep;
buf.count = count;
buf.error = 0;
- error = file->f_op->readdir(file->f_inode, file, &buf, osf_filldir);
+
+ error = -ENOTDIR;
+ if (!file->f_op || !file->f_op->readdir)
+ goto out;
+
+ error = file->f_op->readdir(inode, file, &buf, osf_filldir);
if (error < 0)
- return error;
+ goto out;
+
+ error = buf.error;
if (count == buf.count)
- return buf.error;
- return count - buf.count;
+ goto out;
+
+ error = count - buf.count;
+out:
+ return error;
}
/*
@@ -267,76 +283,73 @@ struct osf_statfs {
__kernel_fsid_t f_fsid;
} *osf_stat;
-static void linux_to_osf_statfs(struct statfs *linux_stat, struct osf_statfs *osf_stat)
+static int linux_to_osf_statfs(struct statfs *linux_stat, struct osf_statfs *osf_stat, unsigned long bufsiz)
{
- osf_stat->f_type = linux_stat->f_type;
- osf_stat->f_flags = 0; /* mount flags */
+ struct osf_statfs tmp_stat;
+
+ tmp_stat.f_type = linux_stat->f_type;
+ tmp_stat.f_flags = 0; /* mount flags */
/* Linux doesn't provide a "fundamental filesystem block size": */
- osf_stat->f_fsize = linux_stat->f_bsize;
- osf_stat->f_bsize = linux_stat->f_bsize;
- osf_stat->f_blocks = linux_stat->f_blocks;
- osf_stat->f_bfree = linux_stat->f_bfree;
- osf_stat->f_bavail = linux_stat->f_bavail;
- osf_stat->f_files = linux_stat->f_files;
- osf_stat->f_ffree = linux_stat->f_ffree;
- osf_stat->f_fsid = linux_stat->f_fsid;
+ tmp_stat.f_fsize = linux_stat->f_bsize;
+ tmp_stat.f_bsize = linux_stat->f_bsize;
+ tmp_stat.f_blocks = linux_stat->f_blocks;
+ tmp_stat.f_bfree = linux_stat->f_bfree;
+ tmp_stat.f_bavail = linux_stat->f_bavail;
+ tmp_stat.f_files = linux_stat->f_files;
+ tmp_stat.f_ffree = linux_stat->f_ffree;
+ tmp_stat.f_fsid = linux_stat->f_fsid;
+ if (bufsiz > sizeof(tmp_stat))
+ bufsiz = sizeof(tmp_stat);
+ return copy_to_user(osf_stat, &tmp_stat, bufsiz) ? -EFAULT : 0;
}
+static int do_osf_statfs(struct dentry * dentry, struct osf_statfs *buffer, unsigned long bufsiz)
+{
+ struct statfs linux_stat;
+ struct inode * inode = dentry->d_inode;
+ struct super_block * sb = inode->i_sb;
+ int error;
+
+ error = -ENOSYS;
+ if (sb->s_op->statfs) {
+ set_fs(KERNEL_DS);
+ error = sb->s_op->statfs(sb, &linux_stat, sizeof(linux_stat));
+ set_fs(USER_DS);
+ if (!error)
+ error = linux_to_osf_statfs(&linux_stat, buffer, bufsiz);
+ }
+ return error;
+}
asmlinkage int osf_statfs(char *path, struct osf_statfs *buffer, unsigned long bufsiz)
{
- struct statfs linux_stat;
- struct inode *inode;
+ struct dentry *dentry;
int retval;
lock_kernel();
- if (bufsiz > sizeof(struct osf_statfs))
- bufsiz = sizeof(struct osf_statfs);
- retval = verify_area(VERIFY_WRITE, buffer, bufsiz);
- if (retval)
- goto out;
- retval = namei(NAM_FOLLOW_LINK, path, &inode);
- if (retval)
- goto out;
- retval = -ENOSYS;
- if (!inode->i_sb->s_op->statfs) {
- iput(inode);
- goto out;
+ dentry = namei(path);
+ retval = PTR_ERR(dentry);
+ if (!IS_ERR(dentry)) {
+ retval = do_osf_statfs(dentry, buffer, bufsiz);
+ dput(dentry);
}
- inode->i_sb->s_op->statfs(inode->i_sb, &linux_stat, sizeof(linux_stat));
- linux_to_osf_statfs(&linux_stat, buffer);
- iput(inode);
- retval = 0;
-out:
unlock_kernel();
return retval;
}
asmlinkage int osf_fstatfs(unsigned long fd, struct osf_statfs *buffer, unsigned long bufsiz)
{
- struct statfs linux_stat;
struct file *file;
- struct inode *inode;
+ struct dentry *dentry;
int retval;
lock_kernel();
- retval = verify_area(VERIFY_WRITE, buffer, bufsiz);
- if (retval)
- goto out;
- if (bufsiz > sizeof(struct osf_statfs))
- bufsiz = sizeof(struct osf_statfs);
retval = -EBADF;
if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
goto out;
- retval = -ENOENT;
- if (!(inode = file->f_inode))
- goto out;
- retval = -ENOSYS;
- if (!inode->i_sb->s_op->statfs)
- goto out;
- inode->i_sb->s_op->statfs(inode->i_sb, &linux_stat, sizeof(linux_stat));
- linux_to_osf_statfs(&linux_stat, buffer);
- retval = 0;
+ dentry = file->f_dentry;
+ if (dentry)
+ retval = do_osf_statfs(dentry, buffer, bufsiz);
out:
unlock_kernel();
return retval;
@@ -369,56 +382,60 @@ struct procfs_args {
uid_t exroot;
};
-static int getdev(const char *name, int rdonly, struct inode **ino)
+static int getdev(const char *name, int rdonly, struct dentry **dp)
{
kdev_t dev;
+ struct dentry *dentry;
struct inode *inode;
struct file_operations *fops;
int retval;
- retval = namei(NAM_FOLLOW_LINK, name, &inode);
- if (retval)
+ dentry = namei(name);
+ retval = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
return retval;
+
+ inode = dentry->d_inode;
if (!S_ISBLK(inode->i_mode)) {
- iput(inode);
+ dput(dentry);
return -ENOTBLK;
}
if (IS_NODEV(inode)) {
- iput(inode);
+ dput(dentry);
return -EACCES;
}
dev = inode->i_rdev;
if (MAJOR(dev) >= MAX_BLKDEV) {
- iput(inode);
+ dput(dentry);
return -ENXIO;
}
fops = get_blkfops(MAJOR(dev));
if (!fops) {
- iput(inode);
+ dput(dentry);
return -ENODEV;
}
if (fops->open) {
struct file dummy;
memset(&dummy, 0, sizeof(dummy));
- dummy.f_inode = inode;
+ dummy.f_dentry = dentry;
dummy.f_mode = rdonly ? 1 : 3;
retval = fops->open(inode, &dummy);
if (retval) {
- iput(inode);
+ dput(dentry);
return retval;
}
}
- *ino = inode;
+ *dp = dentry;
return 0;
}
-static void putdev(struct inode *inode)
+static void putdev(struct dentry *dentry)
{
struct file_operations *fops;
- fops = get_blkfops(MAJOR(inode->i_rdev));
+ fops = get_blkfops(MAJOR(dentry->d_inode->i_rdev));
if (fops->release)
- fops->release(inode, NULL);
+ fops->release(dentry->d_inode, NULL);
}
/*
@@ -429,40 +446,40 @@ static void putdev(struct inode *inode)
static int osf_ufs_mount(char *dirname, struct ufs_args *args, int flags)
{
int retval;
- struct inode *inode;
+ struct dentry *dentry;
struct cdfs_args tmp;
retval = verify_area(VERIFY_READ, args, sizeof(*args));
if (retval)
return retval;
copy_from_user(&tmp, args, sizeof(tmp));
- retval = getdev(tmp.devname, 0, &inode);
+ retval = getdev(tmp.devname, 0, &dentry);
if (retval)
return retval;
- retval = do_mount(inode->i_rdev, tmp.devname, dirname, "ext2", flags, NULL);
+ retval = do_mount(dentry->d_inode->i_rdev, tmp.devname, dirname, "ext2", flags, NULL);
if (retval)
- putdev(inode);
- iput(inode);
+ putdev(dentry);
+ dput(dentry);
return retval;
}
static int osf_cdfs_mount(char *dirname, struct cdfs_args *args, int flags)
{
int retval;
- struct inode *inode;
+ struct dentry * dentry;
struct cdfs_args tmp;
retval = verify_area(VERIFY_READ, args, sizeof(*args));
if (retval)
return retval;
copy_from_user(&tmp, args, sizeof(tmp));
- retval = getdev(tmp.devname, 1, &inode);
+ retval = getdev(tmp.devname, 1, &dentry);
if (retval)
return retval;
- retval = do_mount(inode->i_rdev, tmp.devname, dirname, "iso9660", flags, NULL);
+ retval = do_mount(dentry->d_inode->i_rdev, tmp.devname, dirname, "iso9660", flags, NULL);
if (retval)
- putdev(inode);
- iput(inode);
+ putdev(dentry);
+ dput(dentry);
return retval;
}
@@ -876,6 +893,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/alpha/kernel/process.c b/arch/alpha/kernel/process.c
index b6c97e726..81744663d 100644
--- a/arch/alpha/kernel/process.c
+++ b/arch/alpha/kernel/process.c
@@ -308,8 +308,9 @@ asmlinkage int sys_execve(unsigned long a0, unsigned long a1, unsigned long a2,
char * filename;
lock_kernel();
- error = getname((char *) a0, &filename);
- if (error)
+ filename = getname((char *) a0);
+ error = PTR_ERR(filename);
+ if (IS_ERR(filename))
goto out;
error = do_execve(filename, (char **) a1, (char **) a2, &regs);
putname(filename);
diff --git a/arch/alpha/kernel/traps.c b/arch/alpha/kernel/traps.c
index 02fa5a35d..c3838a8bb 100644
--- a/arch/alpha/kernel/traps.c
+++ b/arch/alpha/kernel/traps.c
@@ -24,13 +24,14 @@ void die_if_kernel(char * str, struct pt_regs * regs, long err,
unsigned long *r9_15)
{
long i;
- unsigned long sp, ra;
+ unsigned long ra;
unsigned int * pc;
+ unsigned long * sp;
if (regs->ps & 8)
return;
printk("%s(%d): %s %ld\n", current->comm, current->pid, str, err);
- sp = (unsigned long) (regs+1);
+ sp = (unsigned long *) (regs+1);
__get_user(ra, (unsigned long *)sp);
printk("pc = [<%016lx>] ps = %04lx\n", regs->pc, regs->ps);
printk("rp = [<%016lx>] ra = [<%016lx>]\n", regs->r26, ra);
@@ -54,7 +55,7 @@ void die_if_kernel(char * str, struct pt_regs * regs, long err,
printk("r22= %016lx r23= %016lx\n", regs->r22, regs->r23);
printk("r24= %016lx r25= %016lx\n", regs->r24, regs->r25);
printk("r27= %016lx r28= %016lx\n", regs->r27, regs->r28);
- printk("gp = %016lx sp = %016lx\n", regs->gp, sp);
+ printk("gp = %016lx sp = %p\n", regs->gp, sp);
printk("Code:");
pc = (unsigned int *) regs->pc;
@@ -65,6 +66,19 @@ void die_if_kernel(char * str, struct pt_regs * regs, long err,
printk("%c%08x%c",i?' ':'<',insn,i?' ':'>');
}
printk("\n");
+ printk("Trace:");
+ while (0x1ff8 & (unsigned long) sp) {
+ extern unsigned long _stext, _etext;
+ unsigned long tmp = *sp;
+ sp++;
+ if (tmp < (unsigned long) &_stext)
+ continue;
+ if (tmp >= (unsigned long) &_etext)
+ continue;
+ printk(" [<%lx>]", tmp);
+ }
+ printk("\n");
+
do_exit(SIGSEGV);
}
diff --git a/arch/alpha/mm/fault.c b/arch/alpha/mm/fault.c
index a8bc34108..72d4224f7 100644
--- a/arch/alpha/mm/fault.c
+++ b/arch/alpha/mm/fault.c
@@ -67,8 +67,7 @@ asmlinkage void do_page_fault(unsigned long address, unsigned long mmcsr,
long cause, struct pt_regs *regs)
{
struct vm_area_struct * vma;
- struct task_struct *tsk = current;
- struct mm_struct *mm = tsk->mm;
+ struct mm_struct *mm = current->mm;
unsigned fixup;
down(&mm->mmap_sem);
@@ -97,7 +96,7 @@ good_area:
if (!(vma->vm_flags & VM_WRITE))
goto bad_area;
}
- handle_mm_fault(tsk, vma, address, cause > 0);
+ handle_mm_fault(current, vma, address, cause > 0);
up(&mm->mmap_sem);
return;
@@ -108,23 +107,20 @@ good_area:
bad_area:
up(&mm->mmap_sem);
+ if (user_mode(regs)) {
+ force_sig(SIGSEGV, current);
+ return;
+ }
+
/* Are we prepared to handle this fault as an exception? */
if ((fixup = search_exception_table(regs->pc)) != 0) {
unsigned long newpc;
newpc = fixup_exception(dpf_reg, fixup, regs->pc);
- printk("Taking exception at [<%lx>] (%lx)\n", regs->pc, newpc);
+ printk("%s: Exception at [<%lx>] (%lx)\n", current->comm, regs->pc, newpc);
regs->pc = newpc;
return;
}
- if (user_mode(regs)) {
- printk("%s: memory violation at pc=%08lx ra=%08lx "
- "(bad address = %08lx)\n",
- tsk->comm, regs->pc, regs->r26, address);
- die_if_kernel("oops", regs, cause, (unsigned long*)regs - 16);
- force_sig(SIGSEGV, tsk);
- return;
- }
/*
* Oops. The kernel tried to access some bad page. We'll have to
* terminate things with extreme prejudice.
diff --git a/arch/i386/boot/compressed/Makefile b/arch/i386/boot/compressed/Makefile
index 9a78bca69..653c12eba 100644
--- a/arch/i386/boot/compressed/Makefile
+++ b/arch/i386/boot/compressed/Makefile
@@ -48,7 +48,7 @@ endif
piggy.o: $(SYSTEM)
- tmppiggy=/tmp/$$$$piggy; \
+ tmppiggy=_tmp_$$$$piggy; \
rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk; \
$(OBJCOPY) $(SYSTEM) $$tmppiggy; \
gzip -f -9 < $$tmppiggy > $$tmppiggy.gz; \
diff --git a/arch/i386/defconfig b/arch/i386/defconfig
index ab30a25c8..6e7700c91 100644
--- a/arch/i386/defconfig
+++ b/arch/i386/defconfig
@@ -80,6 +80,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)
@@ -191,14 +192,11 @@ CONFIG_EEXPRESS_PRO100=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_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
@@ -229,12 +227,14 @@ CONFIG_MOUSE=y
# CONFIG_MS_BUSMOUSE is not set
CONFIG_PSMOUSE=y
CONFIG_82C710_MOUSE=y
+# CONFIG_PC110_PAD is not set
# CONFIG_UMISC is not set
# CONFIG_QIC02_TAPE is not set
# CONFIG_FTAPE is not set
# CONFIG_APM is not set
# CONFIG_WATCHDOG is not set
# CONFIG_RTC is not set
+# CONFIG_JOYSTICK is not set
#
# Sound
diff --git a/arch/i386/kernel/bios32.c b/arch/i386/kernel/bios32.c
index 157e62b2d..8f7a796fe 100644
--- a/arch/i386/kernel/bios32.c
+++ b/arch/i386/kernel/bios32.c
@@ -1,7 +1,7 @@
/*
* bios32.c - BIOS32, PCI BIOS functions.
*
- * $Id: bios32.c,v 1.11 1997/05/07 13:35:21 mj Exp $
+ * $Id: bios32.c,v 1.1.1.1 1997/06/01 03:16:32 ralf Exp $
*
* Sponsored by
* iX Multiuser Multitasking Magazine
@@ -59,6 +59,8 @@
*
* May 7, 1997 : Added some missing cli()'s. [mj]
*
+ * Jun 20, 1997 : Corrected problems in "conf1" type accesses.
+ * (paubert@iram.es)
*/
#include <linux/config.h>
@@ -512,16 +514,7 @@ static int pci_conf1_read_config_byte(unsigned char bus, unsigned char device_fn
save_flags(flags); cli();
outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
- switch (where & 3) {
- case 0: *value = inb(0xCFC);
- break;
- case 1: *value = inb(0xCFD);
- break;
- case 2: *value = inb(0xCFE);
- break;
- case 3: *value = inb(0xCFF);
- break;
- }
+ *value = inb(0xCFC + (where&3));
restore_flags(flags);
return PCIBIOS_SUCCESSFUL;
}
@@ -531,12 +524,10 @@ static int pci_conf1_read_config_word (unsigned char bus,
{
unsigned long flags;
+ if (where&1) return PCIBIOS_BAD_REGISTER_NUMBER;
save_flags(flags); cli();
outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
- if (where & 2)
- *value = inw(0xCFE);
- else
- *value = inw(0xCFC);
+ *value = inw(0xCFC + (where&2));
restore_flags(flags);
return PCIBIOS_SUCCESSFUL;
}
@@ -546,6 +537,7 @@ static int pci_conf1_read_config_dword (unsigned char bus, unsigned char device_
{
unsigned long flags;
+ if (where&3) return PCIBIOS_BAD_REGISTER_NUMBER;
save_flags(flags); cli();
outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
*value = inl(0xCFC);
@@ -560,7 +552,7 @@ static int pci_conf1_write_config_byte (unsigned char bus, unsigned char device_
save_flags(flags); cli();
outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
- outb(value, 0xCFC);
+ outb(value, 0xCFC + (where&3));
restore_flags(flags);
return PCIBIOS_SUCCESSFUL;
}
@@ -570,9 +562,10 @@ static int pci_conf1_write_config_word (unsigned char bus, unsigned char device_
{
unsigned long flags;
+ if (where&1) return PCIBIOS_BAD_REGISTER_NUMBER;
save_flags(flags); cli();
outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
- outw(value, 0xCFC);
+ outw(value, 0xCFC + (where&2));
restore_flags(flags);
return PCIBIOS_SUCCESSFUL;
}
@@ -582,6 +575,7 @@ static int pci_conf1_write_config_dword (unsigned char bus, unsigned char device
{
unsigned long flags;
+ if (where&3) return PCIBIOS_BAD_REGISTER_NUMBER;
save_flags(flags); cli();
outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
outl(value, 0xCFC);
diff --git a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S
index ac67da797..e7b9e0779 100644
--- a/arch/i386/kernel/entry.S
+++ b/arch/i386/kernel/entry.S
@@ -526,6 +526,8 @@ ENTRY(sys_call_table)
.long SYMBOL_NAME(sys_query_module)
.long SYMBOL_NAME(sys_poll)
.long SYMBOL_NAME(sys_nfsservctl)
- .rept NR_syscalls-169
+ .long SYMBOL_NAME(sys_setresgid) /* 170 */
+ .long SYMBOL_NAME(sys_getresgid)
+ .rept NR_syscalls-171
.long SYMBOL_NAME(sys_ni_syscall)
.endr
diff --git a/arch/i386/kernel/irq.c b/arch/i386/kernel/irq.c
index eedb1d0fe..2e0f3e084 100644
--- a/arch/i386/kernel/irq.c
+++ b/arch/i386/kernel/irq.c
@@ -82,12 +82,13 @@ static inline void mask_and_ack_irq(int irq_nr)
if (irq_nr & 8) {
inb(0xA1); /* DUMMY */
outb(cached_A1,0xA1);
+ outb(0x62,0x20); /* Specific EOI to cascade */
outb(0x20,0xA0);
} else {
inb(0x21); /* DUMMY */
outb(cached_21,0x21);
+ outb(0x20,0x20);
}
- outb(0x20,0x20);
spin_unlock(&irq_controller_lock);
}
@@ -207,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/process.c b/arch/i386/kernel/process.c
index 33842a21f..a8fe0315b 100644
--- a/arch/i386/kernel/process.c
+++ b/arch/i386/kernel/process.c
@@ -623,8 +623,9 @@ asmlinkage int sys_execve(struct pt_regs regs)
char * filename;
lock_kernel();
- error = getname((char *) regs.ebx, &filename);
- if (error)
+ filename = getname((char *) regs.ebx);
+ error = PTR_ERR(filename);
+ if (IS_ERR(filename))
goto out;
error = do_execve(filename, (char **) regs.ecx, (char **) regs.edx, &regs);
putname(filename);
diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c
index f62744d11..4dd8edf76 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)
@@ -244,6 +244,17 @@ static const char * i586model(unsigned int nr)
return NULL;
}
+static const char * k5model(unsigned int nr)
+{
+ static const char *model[] = {
+ "SSA5 (PR-75, PR-90, PR-100)", "5k86 (PR-120, PR-133)",
+ "5k86 (PR-166)", "5k86 (PR-200)", "", "", "K6"
+ };
+ if (nr < sizeof(model)/sizeof(char *))
+ return model[nr];
+ return NULL;
+}
+
static const char * i686model(unsigned int nr)
{
static const char *model[] = {
@@ -263,7 +274,11 @@ static const char * getmodel(int x86, int model)
p = i486model(model);
break;
case 5:
- p = i586model(model);
+ if(strcmp(x86_vendor_id, "AuthenticAMD") == 0){
+ p = k5model(model);
+ } else {
+ p = i586model(model);
+ }
break;
case 6:
p = i686model(model);
diff --git a/arch/i386/mm/fault.c b/arch/i386/mm/fault.c
index b0404a6a9..e4847c070 100644
--- a/arch/i386/mm/fault.c
+++ b/arch/i386/mm/fault.c
@@ -161,23 +161,25 @@ good_area:
bad_area:
up(&mm->mmap_sem);
- /* Are we prepared to handle this fault? */
+ /* User mode accesses just cause a SIGSEGV */
+ if (error_code & 4) {
+ tsk->tss.cr2 = address;
+ tsk->tss.error_code = error_code;
+ tsk->tss.trap_no = 14;
+ force_sig(SIGSEGV, tsk);
+ goto out;
+ }
+
+ /* Are we prepared to handle this kernel fault? */
if ((fixup = search_exception_table(regs->eip)) != 0) {
printk(KERN_DEBUG "%s: Exception at [<%lx>] (%lx)\n",
- current->comm,
+ tsk->comm,
regs->eip,
fixup);
regs->eip = fixup;
goto out;
}
- if (error_code & 4) {
- tsk->tss.cr2 = address;
- tsk->tss.error_code = error_code;
- tsk->tss.trap_no = 14;
- force_sig(SIGSEGV, tsk);
- goto out;
- }
/*
* Oops. The kernel tried to access some bad page. We'll have to
* terminate things with extreme prejudice.
diff --git a/arch/m68k/amiga/amifb.c b/arch/m68k/amiga/amifb.c
index 15e424ea7..eb72970d7 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
@@ -1811,11 +1810,6 @@ __initfunc(struct fb_info *amiga_fb_init(long *mem_start))
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 e36016306..1571e1a0f 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 31e2a4608..b7802a67c 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 d5d45be01..e09743e79 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 b109466ed..41ac75ada 100644
--- a/arch/m68k/boot/amiga/bootstrap.c
+++ b/arch/m68k/boot/amiga/bootstrap.c
@@ -36,10 +36,12 @@
#include <stdarg.h>
#include <string.h>
#include <sys/file.h>
-#include <sys/types.h>
#include <unistd.h>
/* required Linux/m68k include files */
+#define __KERNEL_STRICT_NAMES /* This is ugly, I know */
+#define _LINUX_POSIX_TYPES_H
+#include <asm/posix_types.h>
#include <linux/a.out.h>
#include <linux/elf.h>
#include <asm/amigahw.h>
diff --git a/arch/m68k/boot/amiga/linuxboot.c b/arch/m68k/boot/amiga/linuxboot.c
index 23b7fa9d0..74b873bf2 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 <stddef.h>
#include <string.h>
#include <errno.h>
-#include <sys/types.h>
#include <linux/a.out.h>
#include <linux/elf.h>
@@ -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 df6a45a9d..b963687ae 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 62457c0cb..5065e5769 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 65f71d5a9..18763c58b 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/ifpsp060/iskeleton.S b/arch/m68k/ifpsp060/iskeleton.S
index 3c310c237..35542d4f1 100644
--- a/arch/m68k/ifpsp060/iskeleton.S
+++ b/arch/m68k/ifpsp060/iskeleton.S
@@ -35,24 +35,7 @@
|
#include <linux/linkage.h>
-
-/*
- * This has to match entry.S
- */
-LOFF_ORIG_D0 = 0x24
-
-#define curptr a2
-
-#define SAVE_ALL \
- clrl %sp@-; /* stk_adj */ \
- movel %d0,%sp@-; /* orig d0 */ \
- movel %d0,%sp@-; /* d0 */ \
- moveml %d1-%d5/%a0-%a1/%curptr,%sp@-;
-
-#define GET_CURRENT(tmp) \
- movel %sp,tmp; \
- andw &-8192,tmp; \
- movel tmp,%curptr;
+#include <asm/entry.h>
|################################
| (1) EXAMPLE CALL-OUTS #
@@ -92,9 +75,8 @@ Lnotkern:
bne Lmustsched
rte
Lmustsched:
- SAVE_ALL
- moveq #-1,%d0
- movel %d0,%sp@(LOFF_ORIG_D0) | indicate stack frame not for syscall
+ SAVE_ALL_INT
+
GET_CURRENT(%d0)
bral SYMBOL_NAME(ret_from_exception) | deliver signals, reschedule etc..
diff --git a/arch/m68k/kernel/console.c b/arch/m68k/kernel/console.c
index 31608e904..204f4d3cc 100644
--- a/arch/m68k/kernel/console.c
+++ b/arch/m68k/kernel/console.c
@@ -109,6 +109,7 @@
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
+#include <linux/console.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
@@ -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 dea7695e8..a135143b2 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/defconfig b/arch/mips/defconfig
index 9c6fb9073..099cb8406 100644
--- a/arch/mips/defconfig
+++ b/arch/mips/defconfig
@@ -86,6 +86,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)
diff --git a/arch/mips/kernel/irixelf.c b/arch/mips/kernel/irixelf.c
index fc0d542fc..dabd91762 100644
--- a/arch/mips/kernel/irixelf.c
+++ b/arch/mips/kernel/irixelf.c
@@ -228,7 +228,7 @@ unsigned long * create_irix_tables(char * p, int argc, int envc,
* an ELF header.
*/
static unsigned int load_irix_interp(struct elfhdr * interp_elf_ex,
- struct inode * interpreter_inode,
+ struct dentry * interpreter_dentry,
unsigned int *interp_load_addr)
{
struct file * file;
@@ -256,8 +256,8 @@ static unsigned int load_irix_interp(struct elfhdr * interp_elf_ex,
if((interp_elf_ex->e_type != ET_EXEC &&
interp_elf_ex->e_type != ET_DYN) ||
!elf_check_arch(interp_elf_ex->e_machine) ||
- (!interpreter_inode->i_op ||
- !interpreter_inode->i_op->default_file_ops->mmap)){
+ (!interpreter_dentry->d_inode->i_op ||
+ !interpreter_dentry->d_inode->i_op->default_file_ops->mmap)) {
printk("IRIX interp has bad e_type %d\n", interp_elf_ex->e_type);
return 0xffffffff;
}
@@ -288,7 +288,7 @@ static unsigned int load_irix_interp(struct elfhdr * interp_elf_ex,
return 0xffffffff;
}
- retval = read_exec(interpreter_inode, interp_elf_ex->e_phoff,
+ retval = read_exec(interpreter_dentry, interp_elf_ex->e_phoff,
(char *) elf_phdata,
sizeof(struct elf_phdr) * interp_elf_ex->e_phnum, 1);
@@ -296,7 +296,7 @@ static unsigned int load_irix_interp(struct elfhdr * interp_elf_ex,
dump_phdrs(elf_phdata, interp_elf_ex->e_phnum);
#endif
- elf_exec_fileno = open_inode(interpreter_inode, O_RDONLY);
+ elf_exec_fileno = open_dentry(interpreter_dentry, O_RDONLY);
if (elf_exec_fileno < 0) {
printk("Could not open IRIX interp inode.\n");
kfree(elf_phdata);
@@ -408,8 +408,9 @@ static int verify_binary(struct elfhdr *ehp, struct linux_binprm *bprm)
/* First of all, some simple consistency checks */
if((ehp->e_type != ET_EXEC && ehp->e_type != ET_DYN) ||
!elf_check_arch(ehp->e_machine) ||
- (!bprm->inode->i_op || !bprm->inode->i_op->default_file_ops ||
- !bprm->inode->i_op->default_file_ops->mmap)) {
+ (!bprm->dentry->d_inode->i_op ||
+ !bprm->dentry->d_inode->i_op->default_file_ops ||
+ !bprm->dentry->d_inode->i_op->default_file_ops->mmap)) {
return -ENOEXEC;
}
@@ -435,13 +436,14 @@ static int verify_binary(struct elfhdr *ehp, struct linux_binprm *bprm)
/* Look for an IRIX ELF interpreter. */
static inline int look_for_irix_interpreter(char **name,
- struct inode **interpreter_inode,
+ struct dentry **interpreter_dentry,
struct elfhdr *interp_elf_ex,
struct elf_phdr *epp,
struct linux_binprm *bprm, int pnum)
{
int i, old_fs;
int retval = -EINVAL;
+ struct dentry *dentry = NULL;
*name = NULL;
for(i = 0; i < pnum; i++, epp++) {
@@ -450,7 +452,7 @@ static inline int look_for_irix_interpreter(char **name,
/* It is illegal to have two interpreters for one executable. */
if(*name != NULL)
- goto losing;
+ goto out;
*name = (char *) kmalloc((epp->p_filesz +
strlen(IRIX_INTERP_PREFIX)),
@@ -459,26 +461,31 @@ static inline int look_for_irix_interpreter(char **name,
return -ENOMEM;
strcpy(*name, IRIX_INTERP_PREFIX);
- retval = read_exec(bprm->inode, epp->p_offset, (*name + 16),
+ retval = read_exec(bprm->dentry, epp->p_offset, (*name + 16),
epp->p_filesz, 1);
if(retval < 0)
- goto losing;
+ goto out;
old_fs = get_fs(); set_fs(get_ds());
- retval = namei(NAM_FOLLOW_LINK, *name, interpreter_inode);
+ dentry = namei(*name);
set_fs(old_fs);
- if(retval < 0)
- goto losing;
+ if(IS_ERR(dentry)) {
+ retval = PTR_ERR(dentry);
+ goto out;
+ }
- retval = read_exec(*interpreter_inode, 0, bprm->buf, 128, 1);
- if(retval < 0)
- goto losing;
+ retval = read_exec(dentry, 0, bprm->buf, 128, 1);
+ if(retval)
+ goto dput_and_out;
*interp_elf_ex = *((struct elfhdr *) bprm->buf);
}
+ *interpreter_dentry = dentry;
return 0;
-losing:
+dput_and_out:
+ dput(dentry);
+out:
kfree(*name);
return retval;
}
@@ -538,7 +545,7 @@ static inline void map_executable(struct file *fp, struct elf_phdr *epp, int pnu
}
static inline int map_interpreter(struct elf_phdr *epp, struct elfhdr *ihp,
- struct inode *iino, unsigned int *iladdr,
+ struct dentry *identry, unsigned int *iladdr,
int pnum, int old_fs,
unsigned int *eentry)
{
@@ -554,11 +561,11 @@ static inline int map_interpreter(struct elf_phdr *epp, struct elfhdr *ihp,
return -1;
set_fs(old_fs);
- *eentry = load_irix_interp(ihp, iino, iladdr);
+ *eentry = load_irix_interp(ihp, identry, iladdr);
old_fs = get_fs();
set_fs(get_ds());
- iput(iino);
+ dput(identry);
if(*eentry == 0xffffffff)
return -1;
@@ -573,7 +580,7 @@ static inline int do_load_irix_binary(struct linux_binprm * bprm,
struct pt_regs * regs)
{
struct elfhdr elf_ex, interp_elf_ex;
- struct inode *interpreter_inode;
+ struct dentry *interpreter_dentry;
struct elf_phdr *elf_phdata, *elf_ihdr, *elf_ephdr;
unsigned int load_addr, elf_bss, elf_brk;
unsigned int elf_entry, interp_load_addr = 0;
@@ -599,7 +606,7 @@ static inline int do_load_irix_binary(struct linux_binprm * bprm,
if (elf_phdata == NULL)
return -ENOMEM;
- retval = read_exec(bprm->inode, elf_ex.e_phoff, (char *) elf_phdata,
+ retval = read_exec(bprm->dentry, elf_ex.e_phoff, (char *) elf_phdata,
elf_ex.e_phentsize * elf_ex.e_phnum, 1);
if (retval < 0) {
kfree (elf_phdata);
@@ -629,7 +636,7 @@ static inline int do_load_irix_binary(struct linux_binprm * bprm,
elf_bss = 0;
elf_brk = 0;
- elf_exec_fileno = open_inode(bprm->inode, O_RDONLY);
+ elf_exec_fileno = open_dentry(bprm->dentry, O_RDONLY);
if (elf_exec_fileno < 0) {
kfree (elf_phdata);
@@ -642,7 +649,8 @@ static inline int do_load_irix_binary(struct linux_binprm * bprm,
end_code = 0;
end_data = 0;
- retval = look_for_irix_interpreter(&elf_interpreter, &interpreter_inode,
+ retval = look_for_irix_interpreter(&elf_interpreter,
+ &interpreter_dentry,
&interp_elf_ex, elf_phdata, bprm,
elf_ex.e_phnum);
if(retval) {
@@ -703,7 +711,7 @@ static inline int do_load_irix_binary(struct linux_binprm * bprm,
if(elf_interpreter) {
retval = map_interpreter(elf_phdata, &interp_elf_ex,
- interpreter_inode, &interp_load_addr,
+ interpreter_dentry, &interp_load_addr,
elf_ex.e_phnum, old_fs, &elf_entry);
kfree(elf_interpreter);
if(retval) {
@@ -795,7 +803,8 @@ static inline int do_load_irix_library(int fd)
struct file * file;
struct elfhdr elf_ex;
struct elf_phdr *elf_phdata = NULL;
- struct inode * inode;
+ struct dentry *dentry;
+ struct inode *inode;
unsigned int len;
int elf_bss;
int retval;
@@ -805,7 +814,8 @@ static inline int do_load_irix_library(int fd)
len = 0;
file = current->files->fd[fd];
- inode = file->f_inode;
+ dentry = file->f_dentry;
+ inode = dentry->d_inode;
elf_bss = 0;
if (!file || !file->f_op)
@@ -831,7 +841,8 @@ static inline int do_load_irix_library(int fd)
/* First of all, some simple consistency checks. */
if(elf_ex.e_type != ET_EXEC || elf_ex.e_phnum > 2 ||
!elf_check_arch(elf_ex.e_machine) ||
- (!inode->i_op || !inode->i_op->default_file_ops->mmap))
+ (!dentry->d_inode->i_op ||
+ !dentry->d_inode->i_op->default_file_ops->mmap))
return -ENOEXEC;
/* Now read in all of the header information. */
@@ -843,7 +854,7 @@ static inline int do_load_irix_library(int fd)
if (elf_phdata == NULL)
return -ENOMEM;
- retval = read_exec(inode, elf_ex.e_phoff, (char *) elf_phdata,
+ retval = read_exec(dentry, elf_ex.e_phoff, (char *) elf_phdata,
sizeof(struct elf_phdr) * elf_ex.e_phnum, 1);
j = 0;
@@ -973,14 +984,13 @@ 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;
+ return file->f_op->write(file->f_dentry->d_inode, file, addr, nr) == nr;
}
static int dump_seek(struct file *file, off_t off)
{
if (file->f_op->llseek) {
- if (file->f_op->llseek(file->f_inode, file, off, 0) != off)
+ if (file->f_op->llseek(file->f_dentry->d_inode, file, off, 0) != off)
return 0;
} else
file->f_pos = off;
@@ -1071,6 +1081,7 @@ static int irix_core_dump(long signr, struct pt_regs * regs)
{
int has_dumped = 0;
struct file file;
+ struct dentry *dentry;
struct inode *inode;
unsigned short fs;
char corefile[6+sizeof(current->comm)];
@@ -1138,30 +1149,24 @@ static int irix_core_dump(long signr, struct pt_regs * regs)
fs = get_fs();
set_fs(KERNEL_DS);
- memcpy(corefile,"core.",5);
+ 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)) {
+ dentry = open_namei(corefile, O_CREAT | 2 | O_TRUNC, 0600);
+ if (IS_ERR(dentry)) {
inode = NULL;
goto end_coredump;
}
+ inode = dentry->d_inode;
if (!S_ISREG(inode->i_mode))
goto end_coredump;
if (!inode->i_op || !inode->i_op->default_file_ops)
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 end_coredump;
+ if (init_private_file(&file, dentry, 3))
+ goto end_coredump;
if (!file.f_op->write)
goto close_coredump;
has_dumped = 1;
@@ -1330,11 +1335,11 @@ static int irix_core_dump(long signr, struct pt_regs * regs)
close_coredump:
if (file.f_op->release)
- file.f_op->release(inode,&file);
+ file.f_op->release(inode, &file);
end_coredump:
set_fs(fs);
- iput(inode);
+ dput(dentry);
#ifndef CONFIG_BINFMT_ELF
MOD_DEC_USE_COUNT;
#endif
diff --git a/arch/mips/kernel/syscall.c b/arch/mips/kernel/syscall.c
index 2faf604c7..f1b117a63 100644
--- a/arch/mips/kernel/syscall.c
+++ b/arch/mips/kernel/syscall.c
@@ -131,20 +131,21 @@ asmlinkage int sys_clone(struct pt_regs *regs)
*/
asmlinkage int sys_execve(struct pt_regs *regs)
{
- int res;
+ int error;
char * filename;
lock_kernel();
- res = getname((char *) (long)regs->regs[4], &filename);
- if (res)
+ filename = getname((char *) (long)regs->regs[4]);
+ error = PTR_ERR(filename);
+ if (IS_ERR(filename))
goto out;
- res = do_execve(filename, (char **) (long)regs->regs[5],
- (char **) (long)regs->regs[6], regs);
+ error = do_execve(filename, (char **) (long)regs->regs[5],
+ (char **) (long)regs->regs[6], regs);
putname(filename);
out:
unlock_kernel();
- return res;
+ return error;
}
/*
diff --git a/arch/mips/kernel/syscalls.h b/arch/mips/kernel/syscalls.h
index b8fc556fa..66d66ce5b 100644
--- a/arch/mips/kernel/syscalls.h
+++ b/arch/mips/kernel/syscalls.h
@@ -7,7 +7,7 @@
*
* Copyright (C) 1995, 1996, 1997 by Ralf Baechle
*
- * $Id: syscalls.h,v 1.3 1997/06/20 09:48:49 ralf Exp $
+ * $Id: syscalls.h,v 1.4 1997/06/25 20:07:40 ralf Exp $
*/
/*
@@ -208,3 +208,5 @@ SYS(sys_getresuid, 3)
SYS(sys_query_module, 5)
SYS(sys_poll, 3)
SYS(sys_nfsservctl, 3)
+SYS(sys_setresgid, 3) /* 4190 */
+SYS(sys_getresgid, 3)
diff --git a/arch/mips/kernel/sysirix.c b/arch/mips/kernel/sysirix.c
index 95a0dc1c6..e87e5084b 100644
--- a/arch/mips/kernel/sysirix.c
+++ b/arch/mips/kernel/sysirix.c
@@ -1,4 +1,4 @@
-/* $Id: sysirix.c,v 1.14 1996/07/14 01:59:51 dm Exp $
+/* $Id: sysirix.c,v 1.4 1997/07/19 19:03:18 root Exp $
* sysirix.c: IRIX system call emulation.
*
* Copyright (C) 1996 David S. Miller
@@ -652,6 +652,7 @@ struct irix_statfs {
asmlinkage int irix_statfs(const char *path, struct irix_statfs *buf,
int len, int fs_type)
{
+ struct dentry *dentry;
struct inode *inode;
struct statfs kbuf;
int error, old_fs, i;
@@ -664,20 +665,19 @@ 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(NAM_FOLLOW_LINK, path, &inode);
- if (error)
- goto out;
- if (!inode->i_sb->s_op->statfs) {
- iput(inode);
- error = -ENOSYS;
+ dentry = namei(path);
+ error = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
goto out;
- }
+ inode = dentry->d_inode;
old_fs = get_fs(); set_fs(get_ds());
- inode->i_sb->s_op->statfs(inode->i_sb, &kbuf, sizeof(struct statfs));
+ error = inode->i_sb->s_op->statfs(inode->i_sb, &kbuf,
+ sizeof(struct statfs));
set_fs(old_fs);
+ if (error)
+ goto dput_and_out;
- iput(inode);
__put_user(kbuf.f_type, &buf->f_type);
__put_user(kbuf.f_bsize, &buf->f_bsize);
__put_user(kbuf.f_frsize, &buf->f_frsize);
@@ -691,6 +691,8 @@ asmlinkage int irix_statfs(const char *path, struct irix_statfs *buf,
}
error = 0;
+dput_and_out:
+ dput(dentry);
out:
unlock_kernel();
return error;
@@ -698,7 +700,8 @@ out:
asmlinkage int irix_fstatfs(unsigned int fd, struct irix_statfs *buf)
{
- struct inode * inode;
+ struct dentry *dentry;
+ struct inode *inode;
struct statfs kbuf;
struct file *file;
int error, old_fs, i;
@@ -711,18 +714,29 @@ asmlinkage int irix_fstatfs(unsigned int fd, struct irix_statfs *buf)
error = -EBADF;
goto out;
}
- if (!(inode = file->f_inode)) {
+ if (!(dentry = file->f_dentry)) {
+ error = -ENOENT;
+ goto out;
+ }
+ if (!(inode = dentry->d_inode)) {
error = -ENOENT;
goto out;
}
+ if (!inode->i_sb) {
+ error = -ENODEV;
+ goto out;
+ }
if (!inode->i_sb->s_op->statfs) {
error = -ENOSYS;
goto out;
}
old_fs = get_fs(); set_fs(get_ds());
- inode->i_sb->s_op->statfs(inode->i_sb, &kbuf, sizeof(struct statfs));
+ error = inode->i_sb->s_op->statfs(inode->i_sb, &kbuf,
+ sizeof(struct statfs));
set_fs(old_fs);
+ if (error)
+ goto out;
__put_user(kbuf.f_type, &buf->f_type);
__put_user(kbuf.f_bsize, &buf->f_bsize);
@@ -789,13 +803,14 @@ out:
asmlinkage int irix_exec(struct pt_regs *regs)
{
int error, base = 0;
- char * filename;
+ char *filename;
lock_kernel();
if(regs->regs[2] == 1000)
base = 1;
- error = getname((char *) (long)regs->regs[base + 4], &filename);
- if (error)
+ filename = getname((char *) (long)regs->regs[base + 4]);
+ error = PTR_ERR(filename);
+ if (IS_ERR(filename))
goto out;
error = do_execve(filename, (char **) (long)regs->regs[base + 5],
(char **) 0, regs);
@@ -809,13 +824,14 @@ out:
asmlinkage int irix_exece(struct pt_regs *regs)
{
int error, base = 0;
- char * filename;
+ char *filename;
lock_kernel();
if(regs->regs[2] == 1000)
base = 1;
- error = getname((char *) (long)regs->regs[base + 4], &filename);
- if (error)
+ filename = getname((char *) (long)regs->regs[base + 4]);
+ error = PTR_ERR(filename);
+ if (IS_ERR(filename))
goto out;
error = do_execve(filename, (char **) (long)regs->regs[base + 5],
(char **) (long)regs->regs[base + 6], regs);
@@ -1380,6 +1396,7 @@ struct irix_statvfs {
asmlinkage int irix_statvfs(char *fname, struct irix_statvfs *buf)
{
+ struct dentry *dentry;
struct inode *inode;
struct statfs kbuf;
int error, old_fs, i;
@@ -1390,20 +1407,23 @@ 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(NAM_FOLLOW_LINK, fname, &inode);
- if(error)
- goto out;
- if(!inode->i_sb->s_op->statfs) {
- iput(inode);
- error = -ENOSYS;
+ dentry = namei(fname);
+ error = PTR_ERR(dentry);
+ if(!IS_ERR(dentry))
goto out;
- }
+ inode = dentry->d_inode;
+
+ error = -ENOSYS;
+ if(!inode->i_sb->s_op->statfs)
+ goto dput_and_out;
old_fs = get_fs(); set_fs(get_ds());
- inode->i_sb->s_op->statfs(inode->i_sb, &kbuf, sizeof(struct statfs));
+ error = inode->i_sb->s_op->statfs(inode->i_sb, &kbuf,
+ sizeof(struct statfs));
set_fs(old_fs);
+ if (error)
+ goto dput_and_out;
- iput(inode);
__put_user(kbuf.f_bsize, &buf->f_bsize);
__put_user(kbuf.f_frsize, &buf->f_frsize);
__put_user(kbuf.f_blocks, &buf->f_blocks);
@@ -1426,6 +1446,8 @@ asmlinkage int irix_statvfs(char *fname, struct irix_statvfs *buf)
error = 0;
+dput_and_out:
+ dput(dentry);
out:
unlock_kernel();
return error;
@@ -1433,7 +1455,8 @@ out:
asmlinkage int irix_fstatvfs(int fd, struct irix_statvfs *buf)
{
- struct inode * inode;
+ struct dentry *dentry;
+ struct inode *inode;
struct statfs kbuf;
struct file *file;
int error, old_fs, i;
@@ -1449,7 +1472,11 @@ asmlinkage int irix_fstatvfs(int fd, struct irix_statvfs *buf)
error = -EBADF;
goto out;
}
- if (!(inode = file->f_inode)) {
+ if (!(dentry = file->f_dentry)) {
+ error = -ENOENT;
+ goto out;
+ }
+ if (!(inode = dentry->d_inode)) {
error = -ENOENT;
goto out;
}
@@ -1459,8 +1486,11 @@ asmlinkage int irix_fstatvfs(int fd, struct irix_statvfs *buf)
}
old_fs = get_fs(); set_fs(get_ds());
- inode->i_sb->s_op->statfs(inode->i_sb, &kbuf, sizeof(struct statfs));
+ error = inode->i_sb->s_op->statfs(inode->i_sb, &kbuf,
+ sizeof(struct statfs));
set_fs(old_fs);
+ if (error)
+ goto out;
__put_user(kbuf.f_bsize, &buf->f_bsize);
__put_user(kbuf.f_frsize, &buf->f_frsize);
@@ -1489,26 +1519,28 @@ out:
return error;
}
-#define NOFOLLOW_LINKS NAM_FOLLOW_TRAILSLASH
-#define FOLLOW_LINKS NAM_FOLLOW_LINK
+#define NOFOLLOW_LINKS 0
+#define FOLLOW_LINKS 1
-static inline int chown_common(char *filename, uid_t user, gid_t group, int follow)
+static inline int chown_common(uid_t user, gid_t group, struct dentry *dentry)
{
struct inode * inode;
int error;
struct iattr newattrs;
- error = namei(follow, filename,&inode);
- if (error)
- return error;
- if (IS_RDONLY(inode)) {
- iput(inode);
- return -EROFS;
- }
- if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) {
- iput(inode);
- return -EPERM;
- }
+ error = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
+ goto out;
+ inode = dentry->d_inode;
+
+ error = -EROFS;
+ if (IS_RDONLY(inode))
+ goto dput_and_out;
+
+ error = -EPERM;
+ if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
+ goto dput_and_out;
+
if (user == (uid_t) -1)
user = inode->i_uid;
if (group == (gid_t) -1)
@@ -1534,38 +1566,45 @@ static inline int chown_common(char *filename, uid_t user, gid_t group, int foll
newattrs.ia_mode &= ~S_ISGID;
newattrs.ia_valid |= ATTR_MODE;
}
- inode->i_dirt = 1;
if (inode->i_sb->dq_op) {
inode->i_sb->dq_op->initialize(inode, -1);
+ error = -EDQUOT;
if (inode->i_sb->dq_op->transfer(inode, &newattrs, 0))
- return -EDQUOT;
+ goto dput_and_out;
error = notify_change(inode, &newattrs);
if (error)
inode->i_sb->dq_op->transfer(inode, &newattrs, 1);
} else
error = notify_change(inode, &newattrs);
- iput(inode);
- return(error);
+
+dput_and_out:
+ dput(dentry);
+out:
+ return error;
}
-asmlinkage int irix_chown(char *fname, int uid, int gid)
+asmlinkage int irix_chown(const char *filename, int uid, int gid)
{
int retval;
+ struct dentry *dentry;
lock_kernel();
/* Do follow any and all links... */
- retval = chown_common(fname, uid, gid, FOLLOW_LINKS);
+ dentry = namei(filename);
+ retval = chown_common(uid, gid, dentry);
unlock_kernel();
return retval;
}
-asmlinkage int irix_lchown(char *fname, int uid, int gid)
+asmlinkage int irix_lchown(const char *filename, int uid, int gid)
{
int retval;
+ struct dentry *dentry;
lock_kernel();
/* Do _not_ follow any links... */
- retval = chown_common(fname, uid, gid, NOFOLLOW_LINKS);
+ dentry = lnamei(filename);
+ retval = chown_common(uid, gid, dentry);
unlock_kernel();
return retval;
}
@@ -1722,6 +1761,7 @@ struct irix_statvfs64 {
asmlinkage int irix_statvfs64(char *fname, struct irix_statvfs64 *buf)
{
+ struct dentry *dentry;
struct inode *inode;
struct statfs kbuf;
int error, old_fs, i;
@@ -1731,20 +1771,22 @@ 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(NAM_FOLLOW_LINK, fname, &inode);
- if(error)
- goto out;
- if(!inode->i_sb->s_op->statfs) {
- iput(inode);
- error = -ENOSYS;
+ dentry = namei(fname);
+ error = PTR_ERR(dentry);
+ if(IS_ERR(dentry))
goto out;
- }
+ error = -ENOSYS;
+ inode = dentry->d_inode;
+ if(!inode->i_sb->s_op->statfs)
+ goto dput_and_out;
old_fs = get_fs(); set_fs(get_ds());
- inode->i_sb->s_op->statfs(inode->i_sb, &kbuf, sizeof(struct statfs));
+ error = inode->i_sb->s_op->statfs(inode->i_sb, &kbuf,
+ sizeof(struct statfs));
set_fs(old_fs);
+ if (error)
+ goto dput_and_out;
- iput(inode);
__put_user(kbuf.f_bsize, &buf->f_bsize);
__put_user(kbuf.f_frsize, &buf->f_frsize);
__put_user(kbuf.f_blocks, &buf->f_blocks);
@@ -1767,6 +1809,8 @@ asmlinkage int irix_statvfs64(char *fname, struct irix_statvfs64 *buf)
error = 0;
+dput_and_out:
+ dput(dentry);
out:
unlock_kernel();
return error;
@@ -1774,7 +1818,8 @@ out:
asmlinkage int irix_fstatvfs64(int fd, struct irix_statvfs *buf)
{
- struct inode * inode;
+ struct dentry *dentry;
+ struct inode *inode;
struct statfs kbuf;
struct file *file;
int error, old_fs, i;
@@ -1790,7 +1835,11 @@ asmlinkage int irix_fstatvfs64(int fd, struct irix_statvfs *buf)
error = -EBADF;
goto out;
}
- if (!(inode = file->f_inode)) {
+ if (!(dentry = file->f_dentry)) {
+ error = -ENOENT;
+ goto out;
+ }
+ if (!(inode = dentry->d_inode)) {
error = -ENOENT;
goto out;
}
@@ -1800,8 +1849,11 @@ asmlinkage int irix_fstatvfs64(int fd, struct irix_statvfs *buf)
}
old_fs = get_fs(); set_fs(get_ds());
- inode->i_sb->s_op->statfs(inode->i_sb, &kbuf, sizeof(struct statfs));
+ error = inode->i_sb->s_op->statfs(inode->i_sb, &kbuf,
+ sizeof(struct statfs));
set_fs(old_fs);
+ if (error)
+ goto out;
__put_user(kbuf.f_bsize, &buf->f_bsize);
__put_user(kbuf.f_frsize, &buf->f_frsize);
@@ -1927,6 +1979,8 @@ out:
asmlinkage int irix_ngetdents(unsigned int fd, void * dirent, unsigned int count, int *eob)
{
struct file *file;
+ struct dentry *dentry;
+ struct inode *inode;
struct irix_dirent32 *lastdirent;
struct irix_dirent32_callback buf;
int error;
@@ -1936,25 +1990,34 @@ asmlinkage int irix_ngetdents(unsigned int fd, void * dirent, unsigned int count
printk("[%s:%d] ngetdents(%d, %p, %d, %p) ", current->comm,
current->pid, fd, dirent, count, eob);
#endif
- if (fd >= NR_OPEN || !(file = current->files->fd[fd])) {
- error = -EBADF;
+ error = -EBADF;
+ if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
goto out;
- }
- if (!file->f_op || !file->f_op->readdir) {
- error = -ENOTDIR;
+
+ dentry = file->f_dentry;
+ if (!dentry)
goto out;
- }
- if(verify_area(VERIFY_WRITE, dirent, count) ||
- verify_area(VERIFY_WRITE, eob, sizeof(*eob))) {
- error = -EFAULT;
+
+ inode = dentry->d_inode;
+ if (!inode)
goto out;
- }
+
+ error = -ENOTDIR;
+ if (!file->f_op || !file->f_op->readdir)
+ goto out;
+
+ error = -EFAULT;
+ if(!access_ok(VERIFY_WRITE, dirent, count) ||
+ !access_ok(VERIFY_WRITE, eob, sizeof(*eob)))
+ goto out;
+
__put_user(0, eob);
buf.current_dir = (struct irix_dirent32 *) dirent;
buf.previous = NULL;
buf.count = count;
buf.error = 0;
- error = file->f_op->readdir(file->f_inode, file, &buf, irix_filldir32);
+
+ error = file->f_op->readdir(inode, file, &buf, irix_filldir32);
if (error < 0)
goto out;
lastdirent = buf.previous;
@@ -2027,6 +2090,8 @@ out:
asmlinkage int irix_getdents64(int fd, void *dirent, int cnt)
{
struct file *file;
+ struct dentry *dentry;
+ struct inode *inode;
struct irix_dirent64 *lastdirent;
struct irix_dirent64_callback buf;
int error;
@@ -2036,28 +2101,35 @@ asmlinkage int irix_getdents64(int fd, void *dirent, int cnt)
printk("[%s:%d] getdents64(%d, %p, %d) ", current->comm,
current->pid, fd, dirent, cnt);
#endif
- if (fd >= NR_OPEN || !(file = current->files->fd[fd])) {
- error = -EBADF;
+ error = -EBADF;
+ if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
goto out;
- }
- if (!file->f_op || !file->f_op->readdir) {
- error = -ENOTDIR;
+
+ dentry = file->f_dentry;
+ if (!dentry)
goto out;
- }
- if(verify_area(VERIFY_WRITE, dirent, cnt)) {
- error = -EFAULT;
+
+ inode = dentry->d_inode;
+ if (!inode)
goto out;
- }
- if(cnt < (sizeof(struct irix_dirent64) + 255)) {
- error = -EINVAL;
+
+ error = -ENOTDIR;
+ if (!file->f_op || !file->f_op->readdir)
+ goto out;
+
+ error = -EFAULT;
+ if(!access_ok(VERIFY_WRITE, dirent, cnt))
+ goto out;
+
+ error = -EINVAL;
+ if(cnt < (sizeof(struct irix_dirent64) + 255))
goto out;
- }
buf.curr = (struct irix_dirent64 *) dirent;
buf.previous = NULL;
buf.count = cnt;
buf.error = 0;
- error = file->f_op->readdir(file->f_inode, file, &buf, irix_filldir64);
+ error = file->f_op->readdir(inode, file, &buf, irix_filldir64);
if (error < 0)
goto out;
lastdirent = buf.previous;
@@ -2079,6 +2151,8 @@ out:
asmlinkage int irix_ngetdents64(int fd, void *dirent, int cnt, int *eob)
{
struct file *file;
+ struct dentry *dentry;
+ struct inode *inode;
struct irix_dirent64 *lastdirent;
struct irix_dirent64_callback buf;
int error;
@@ -2088,30 +2162,37 @@ asmlinkage int irix_ngetdents64(int fd, void *dirent, int cnt, int *eob)
printk("[%s:%d] ngetdents64(%d, %p, %d) ", current->comm,
current->pid, fd, dirent, cnt);
#endif
- if (fd >= NR_OPEN || !(file = current->files->fd[fd])) {
- error = -EBADF;
+ error = -EBADF;
+ if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
goto out;
- }
- if (!file->f_op || !file->f_op->readdir) {
- error = -ENOTDIR;
+
+ dentry = file->f_dentry;
+ if (!dentry)
goto out;
- }
- if(verify_area(VERIFY_WRITE, dirent, cnt) ||
- verify_area(VERIFY_WRITE, eob, sizeof(*eob))) {
- error = -EFAULT;
+
+ inode = dentry->d_inode;
+ if (!inode)
goto out;
- }
- if(cnt < (sizeof(struct irix_dirent64) + 255)) {
- error = -EINVAL;
+
+ error = -ENOTDIR;
+ if (!file->f_op || !file->f_op->readdir)
+ goto out;
+
+ error = -EFAULT;
+ if(!access_ok(VERIFY_WRITE, dirent, cnt) ||
+ !access_ok(VERIFY_WRITE, eob, sizeof(*eob)))
+ goto out;
+
+ error = -EINVAL;
+ if(cnt < (sizeof(struct irix_dirent64) + 255))
goto out;
- }
*eob = 0;
buf.curr = (struct irix_dirent64 *) dirent;
buf.previous = NULL;
buf.count = cnt;
buf.error = 0;
- error = file->f_op->readdir(file->f_inode, file, &buf, irix_filldir64);
+ error = file->f_op->readdir(inode, file, &buf, irix_filldir64);
if (error < 0)
goto out;
lastdirent = buf.previous;
diff --git a/arch/mips/kernel/sysmips.c b/arch/mips/kernel/sysmips.c
index ae99466e4..e73598345 100644
--- a/arch/mips/kernel/sysmips.c
+++ b/arch/mips/kernel/sysmips.c
@@ -7,7 +7,7 @@
*
* Copyright (C) 1995, 1996, 1997 by Ralf Baechle
*
- * $Id: sysmips.c,v 1.2 1997/06/28 23:26:25 ralf Exp $
+ * $Id: sysmips.c,v 1.2 1997/07/01 08:59:08 ralf Exp $
*/
#include <linux/errno.h>
#include <linux/linkage.h>
@@ -57,18 +57,21 @@ sys_sysmips(int cmd, int arg1, int arg2, int arg3)
switch(cmd)
{
case SETNAME:
- if (!suser()) {
- retval = -EPERM;
+ retval = -EPERM;
+ if (!suser())
goto out;
- }
+
name = (char *) arg1;
len = strlen_user(name);
+
+ retval = len;
if (len < 0)
- retval = len;
goto out;
+
+ retval = -EINVAL;
if (len == 0 || len > __NEW_UTS_LEN)
- retval = -EINVAL;
goto out;
+
copy_from_user(system_utsname.nodename, name, len);
system_utsname.nodename[len] = '\0';
retval = 0;
diff --git a/arch/mips/mm/fault.c b/arch/mips/mm/fault.c
index 3ad703b1f..f1ebab648 100644
--- a/arch/mips/mm/fault.c
+++ b/arch/mips/mm/fault.c
@@ -81,17 +81,6 @@ good_area:
*/
bad_area:
up(&mm->mmap_sem);
- /* Did we have an exception handler installed? */
-
- fixup = search_exception_table(regs->cp0_epc);
- if (fixup) {
- long new_epc;
- new_epc = fixup_exception(dpf_reg, fixup, regs->cp0_epc);
- printk(KERN_DEBUG "Exception at [<%lx>] (%lx)\n",
- regs->cp0_epc, new_epc);
- regs->cp0_epc = new_epc;
- goto out;
- }
if (user_mode(regs)) {
tsk->tss.cp0_badvaddr = address;
@@ -111,6 +100,18 @@ bad_area:
force_sig(SIGSEGV, tsk);
goto out;
}
+
+ /* Did we have an exception handler installed? */
+ fixup = search_exception_table(regs->cp0_epc);
+ if (fixup) {
+ long new_epc;
+ new_epc = fixup_exception(dpf_reg, fixup, regs->cp0_epc);
+ printk(KERN_DEBUG "%s: Exception at [<%lx>] (%lx)\n",
+ tsk->comm, regs->cp0_epc, new_epc);
+ regs->cp0_epc = new_epc;
+ goto out;
+ }
+
/*
* Oops. The kernel tried to access some bad page. We'll have to
* terminate things with extreme prejudice.
diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c
index 7a4b6df4b..b62e571d6 100644
--- a/arch/mips/mm/init.c
+++ b/arch/mips/mm/init.c
@@ -156,7 +156,6 @@ static inline void
zeropage(unsigned long page)
{
flush_page_to_ram(page);
- sync_mem();
__zeropage(page);
}
diff --git a/arch/sparc/Makefile b/arch/sparc/Makefile
index 92c73de32..7a8d46a07 100644
--- a/arch/sparc/Makefile
+++ b/arch/sparc/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.28 1997/05/01 01:41:21 davem Exp $
+# $Id: Makefile,v 1.29 1997/07/11 11:05:23 jj Exp $
# sparc/Makefile
#
# Makefile for the architecture dependent flags and dependencies on the
@@ -45,3 +45,6 @@ archdep:
check_asm:
$(MAKE) -C arch/sparc/kernel check_asm
+
+tftpboot.img:
+ $(MAKE) -C arch/sparc/boot tftpboot.img
diff --git a/arch/sparc/boot/Makefile b/arch/sparc/boot/Makefile
index b9d54e652..af462db3a 100644
--- a/arch/sparc/boot/Makefile
+++ b/arch/sparc/boot/Makefile
@@ -1,12 +1,23 @@
-# $Id: Makefile,v 1.3 1996/08/04 08:40:58 ecd Exp $
-# Makefile for the Sparc low level /boot module.
+# $Id: Makefile,v 1.4 1997/07/11 11:05:18 jj Exp $
+# Makefile for the Sparc boot stuff.
#
# Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+# Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+
+ROOT_IMG =/usr/src/root.img
+ELFTOAOUT =elftoaout
all: boot
boot:
@echo "Nothing special to be done for 'boot' on Linux/SPARC."
+tftpboot.img: piggyback
+ $(ELFTOAOUT) $(TOPDIR)/vmlinux -o tftpboot.img
+ ./piggyback tftpboot.img $(TOPDIR)/System.map $(ROOT_IMG)
+
+piggyback: piggyback.c
+ $(HOSTCC) $(HOSTCFLAGS) -o piggyback piggyback.c
+
dep:
diff --git a/arch/sparc/boot/piggyback.c b/arch/sparc/boot/piggyback.c
new file mode 100644
index 000000000..21a32932a
--- /dev/null
+++ b/arch/sparc/boot/piggyback.c
@@ -0,0 +1,100 @@
+/* $Id: piggyback.c,v 1.1 1997/07/18 06:26:14 ralf Exp $
+ Simple utility to make a single-image install kernel with initial ramdisk
+ for Sparc tftpbooting without need to set up nfs.
+
+ Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+
+ This program is free software; you can redistribute it and/or 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.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+/* Note: run this on an a.out kernel (use elftoaout for it), as PROM looks for a.out image onlly
+ usage: piggyback vmlinux System.map tail, where tail is gzipped fs of the initial ramdisk */
+
+void die(char *str)
+{
+ perror (str);
+ exit(1);
+}
+
+int main(int argc,char **argv)
+{
+ char buffer [1024], *q, *r;
+ unsigned int i, j, k, start, end, offset;
+ FILE *map;
+ struct stat s;
+ int image, tail;
+
+ if (stat (argv[3], &s) < 0) die (argv[3]);
+ map = fopen (argv[2], "r");
+ if (!map) die(argv[2]);
+ while (fgets (buffer, 1024, map)) {
+ if (!strcmp (buffer + 11, "start\n"))
+ start = strtoul (buffer, NULL, 16);
+ else if (!strcmp (buffer + 11, "end\n"))
+ end = strtoul (buffer, NULL, 16);
+ }
+ fclose (map);
+ if ((image = open(argv[1],O_RDWR)) < 0) die(argv[1]);
+ if (read(image,buffer,512) != 512) die(argv[1]);
+ if (!memcmp (buffer, "\177ELF", 4)) {
+ unsigned int *p = (unsigned int *)(buffer + *(unsigned int *)(buffer + 28));
+
+ i = p[1] + *(unsigned int *)(buffer + 24) - p[2];
+ if (lseek(image,i,0) < 0) die("lseek");
+ if (read(image,buffer,512) != 512) die(argv[1]);
+ j = 0;
+ } else if (*(unsigned int *)buffer == 0x01030107) {
+ i = j = 32;
+ } else {
+ fprintf (stderr, "Not ELF nor a.out. Don't blame me.\n");
+ exit(1);
+ }
+ k = i;
+ i += ((*(unsigned short *)(buffer + j + 2))<<2) - 512;
+ if (lseek(image,i,0) < 0) die("lseek");
+ if (read(image,buffer,1024) != 1024) die(argv[1]);
+ for (q = buffer, r = q + 512; q < r; q += 4) {
+ if (*q == 'H' && q[1] == 'd' && q[2] == 'r' && q[3] == 'S')
+ break;
+ }
+ if (q == r) {
+ fprintf (stderr, "Couldn't find headers signature in the kernel.\n");
+ exit(1);
+ }
+ offset = i + (q - buffer) + 10;
+ if (lseek(image, offset, 0) < 0) die ("lseek");
+ *(unsigned *)buffer = 0;
+ *(unsigned *)(buffer + 4) = 0x01000000;
+ *(unsigned *)(buffer + 8) = ((end + 32 + 4095) & ~4095);
+ *(unsigned *)(buffer + 12) = s.st_size;
+ if (write(image,buffer+2,14) != 14) die (argv[1]);
+ if (lseek(image, k - start + ((end + 32 + 4095) & ~4095), 0) < 0) die ("lseek");
+ if ((tail = open(argv[3],O_RDONLY)) < 0) die(argv[3]);
+ while ((i = read (tail,buffer,1024)) > 0)
+ if (write(image,buffer,i) != i) die (argv[1]);
+ if (close(image) < 0) die("close");
+ if (close(tail) < 0) die("close");
+ return 0;
+}
diff --git a/arch/sparc/config.in b/arch/sparc/config.in
index 85566667f..69ed62422 100644
--- a/arch/sparc/config.in
+++ b/arch/sparc/config.in
@@ -1,4 +1,4 @@
-# $Id: config.in,v 1.35 1997/04/07 06:54:09 davem Exp $
+# $Id: config.in,v 1.36 1997/06/17 03:54:47 davem Exp $
# For a description of the syntax of this configuration file,
# see the Configure script.
#
diff --git a/arch/sparc/defconfig b/arch/sparc/defconfig
index a6df2dcc0..9d64ee85d 100644
--- a/arch/sparc/defconfig
+++ b/arch/sparc/defconfig
@@ -42,7 +42,7 @@ SUN_FB_CGFOURTEEN=y
SUN_FB_BWTWO=y
SUN_FB_LEO=y
TADPOLE_FB_WEITEK=y
-SUN_FB_CREATOR=y
+#SUN_FB_CREATOR is not set
#
# Misc Linux/SPARC drivers
@@ -64,6 +64,7 @@ CONFIG_SYSVIPC=y
CONFIG_SYSCTL=y
CONFIG_BINFMT_AOUT=y
CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_MISC=y
CONFIG_BINFMT_JAVA=m
#
@@ -103,6 +104,7 @@ CONFIG_IP_MASQUERADE=y
# CONFIG_IP_MROUTE is not set
CONFIG_IP_ALIAS=m
# CONFIG_ARPD is not set
+# CONFIG_SYN_COOKIES is not set
#
# (it is safe to leave these untouched)
diff --git a/arch/sparc/kernel/cpu.c b/arch/sparc/kernel/cpu.c
index f94ef8b91..aeb5a46c8 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/process.c b/arch/sparc/kernel/process.c
index 1adc9e817..95eed6287 100644
--- a/arch/sparc/kernel/process.c
+++ b/arch/sparc/kernel/process.c
@@ -1,4 +1,4 @@
-/* $Id: process.c,v 1.98 1997/05/14 20:44:54 davem Exp $
+/* $Id: process.c,v 1.99 1997/07/17 02:20:13 davem Exp $
* linux/arch/sparc/kernel/process.c
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -594,8 +594,9 @@ asmlinkage int sparc_execve(struct pt_regs *regs)
base = 1;
lock_kernel();
- error = getname((char *) regs->u_regs[base + UREG_I0], &filename);
- if(error)
+ filename = getname((char *)regs->u_regs[base + UREG_I0]);
+ error = PTR_ERR(filename);
+ if(IS_ERR(filename))
goto out;
error = do_execve(filename, (char **) regs->u_regs[base + UREG_I1],
(char **) regs->u_regs[base + UREG_I2], regs);
diff --git a/arch/sparc/kernel/sparc_ksyms.c b/arch/sparc/kernel/sparc_ksyms.c
index 38896ab22..cf4146c4a 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/kernel/sys_sunos.c b/arch/sparc/kernel/sys_sunos.c
index 3fafe7f9c..5d0ea0840 100644
--- a/arch/sparc/kernel/sys_sunos.c
+++ b/arch/sparc/kernel/sys_sunos.c
@@ -1,4 +1,4 @@
-/* $Id: sys_sunos.c,v 1.79 1997/04/23 23:01:15 ecd Exp $
+/* $Id: sys_sunos.c,v 1.80 1997/07/17 02:20:22 davem Exp $
* sys_sunos.c: SunOS specific syscall compatibility support.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -90,10 +90,12 @@ asmlinkage unsigned long sunos_mmap(unsigned long addr, unsigned long len,
* of /dev/zero, transform it into an anonymous mapping.
* SunOS is so stupid some times... hmph!
*/
- if(MAJOR(file->f_inode->i_rdev) == MEM_MAJOR &&
- MINOR(file->f_inode->i_rdev) == 5) {
- flags |= MAP_ANONYMOUS;
- file = 0;
+ if(file->f_dentry && file->f_dentry->d_inode) {
+ if(MAJOR(file->f_dentry->d_inode->i_rdev) == MEM_MAJOR &&
+ MINOR(file->f_dentry->d_inode->i_rdev) == 5) {
+ flags |= MAP_ANONYMOUS;
+ file = 0;
+ }
}
if(!(flags & MAP_FIXED))
addr = 0;
@@ -428,16 +430,32 @@ static int sunos_filldir(void * __buf, const char * name, int namlen,
asmlinkage int sunos_getdents(unsigned int fd, void * dirent, int cnt)
{
struct file * file;
+ struct dentry * dentry;
+ struct inode * inode;
struct sunos_dirent * lastdirent;
struct sunos_dirent_callback buf;
int error = -EBADF;
lock_kernel();
- if (fd >= SUNOS_NR_OPEN || !(file = current->files->fd[fd]))
+ if(fd >= SUNOS_NR_OPEN)
+ goto out;
+
+ file = current->files->fd[fd];
+ if(!file)
+ goto out;
+
+ dentry = file->f_dentry;
+ if(!dentry)
goto out;
+
+ inode = dentry->d_inode;
+ if(!inode)
+ 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;
@@ -446,13 +464,12 @@ asmlinkage int sunos_getdents(unsigned int fd, void * dirent, int cnt)
buf.previous = NULL;
buf.count = cnt;
buf.error = 0;
- error = file->f_op->readdir(file->f_inode, file, &buf, sunos_filldir);
+ error = file->f_op->readdir(inode, file, &buf, sunos_filldir);
if (error < 0)
goto out;
lastdirent = buf.previous;
- if (!lastdirent) {
- error = buf.error;
- } else {
+ error = buf.error;
+ if (lastdirent) {
put_user(file->f_pos, &lastdirent->d_off);
error = cnt - buf.count;
}
@@ -503,16 +520,32 @@ static int sunos_filldirentry(void * __buf, const char * name, int namlen,
asmlinkage int sunos_getdirentries(unsigned int fd, void * dirent, int cnt, unsigned int *basep)
{
struct file * file;
+ struct dentry * dentry;
+ struct inode * inode;
struct sunos_direntry * lastdirent;
struct sunos_direntry_callback buf;
int error = -EBADF;
lock_kernel();
- if (fd >= SUNOS_NR_OPEN || !(file = current->files->fd[fd]))
+ if(fd >= SUNOS_NR_OPEN)
goto out;
+
+ file = current->files->fd[fd];
+ if(!file)
+ goto out;
+
+ dentry = file->f_dentry;
+ if(!dentry)
+ goto out;
+
+ inode = dentry->d_inode;
+ if(!inode)
+ 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;
@@ -521,13 +554,12 @@ asmlinkage int sunos_getdirentries(unsigned int fd, void * dirent, int cnt, unsi
buf.previous = NULL;
buf.count = cnt;
buf.error = 0;
- error = file->f_op->readdir(file->f_inode, file, &buf, sunos_filldirentry);
+ error = file->f_op->readdir(inode, file, &buf, sunos_filldirentry);
if (error < 0)
goto out;
lastdirent = buf.previous;
- if (!lastdirent) {
- error = buf.error;
- } else {
+ error = buf.error;
+ if (lastdirent) {
put_user(file->f_pos, basep);
error = cnt - buf.count;
}
@@ -725,12 +757,13 @@ sunos_nfs_get_server_fd (int fd, struct sockaddr_in *addr)
int try_port;
int ret;
struct socket *socket;
+ struct dentry *dentry;
struct inode *inode;
struct file *file;
file = current->files->fd [fd];
- inode = file->f_inode;
- if (!inode || !inode->i_sock)
+ dentry = file->f_dentry;
+ if(!dentry || !(inode = dentry->d_inode))
return 0;
socket = &inode->u.socket_i;
diff --git a/arch/sparc/mm/Makefile b/arch/sparc/mm/Makefile
index f7b9b367c..84cd1d2e2 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 7420b98cb..3b9970551 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 <linux/config.h>
@@ -36,6 +37,7 @@
#include <asm/ross.h>
#include <asm/tsunami.h>
#include <asm/swift.h>
+#include <asm/turbosparc.h>
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 000000000..5660d4f84
--- /dev/null
+++ b/arch/sparc/mm/turbosparc.S
@@ -0,0 +1,46 @@
+/* $Id: turbosparc.S,v 1.1 1997/07/18 06:26:22 ralf 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 <asm/ptrace.h>
+#include <asm/psr.h>
+#include <asm/asi.h>
+#include <asm/page.h>
+#include <asm/pgtsrmmu.h>
+
+#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 1722d9fdc..e7bd9b06d 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 <linux/config.h>
#include <linux/string.h>
#include <asm/oplib.h>
+#include <linux/init.h>
#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 251b2ee6a..295ce5355 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 a70f9ebf8..b8cf06878 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.20 1997/07/11 11:05:29 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
@@ -49,3 +49,6 @@ archdep:
check_asm:
$(MAKE) -C arch/sparc64/kernel check_asm
+
+tftpboot.img:
+ $(MAKE) -C arch/sparc64/boot tftpboot.img
diff --git a/arch/sparc64/boot/Makefile b/arch/sparc64/boot/Makefile
new file mode 100644
index 000000000..ed3fc6cdb
--- /dev/null
+++ b/arch/sparc64/boot/Makefile
@@ -0,0 +1,23 @@
+# $Id: Makefile,v 1.1 1997/07/18 06:26:30 ralf Exp $
+# Makefile for the Sparc64 boot stuff.
+#
+# Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+# Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+
+ROOT_IMG =/usr/src/root.img
+ELFTOAOUT =elftoaout
+
+all: boot
+
+boot:
+ @echo "Nothing special to be done for 'boot' on Linux/UltraSPARC."
+
+tftpboot.img: piggyback
+ $(ELFTOAOUT) $(TOPDIR)/vmlinux -o tftpboot.img
+ ./piggyback tftpboot.img $(TOPDIR)/System.map $(ROOT_IMG)
+
+piggyback: piggyback.c
+ $(HOSTCC) $(HOSTCFLAGS) -o piggyback piggyback.c
+
+dep:
+
diff --git a/arch/sparc64/boot/piggyback.c b/arch/sparc64/boot/piggyback.c
new file mode 100644
index 000000000..869b3492c
--- /dev/null
+++ b/arch/sparc64/boot/piggyback.c
@@ -0,0 +1,109 @@
+/* $Id: piggyback.c,v 1.1 1997/07/18 06:26:30 ralf Exp $
+ Simple utility to make a single-image install kernel with initial ramdisk
+ for Sparc64 tftpbooting without need to set up nfs.
+
+ Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+
+ This program is free software; you can redistribute it and/or 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.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+/* Note: run this on an a.out kernel (use elftoaout for it), as PROM looks for a.out image onlly
+ usage: piggyback vmlinux System.map tail, where tail is gzipped fs of the initial ramdisk */
+
+void die(char *str)
+{
+ perror (str);
+ exit(1);
+}
+
+int main(int argc,char **argv)
+{
+ char buffer [1024], *q, *r;
+ unsigned int i, j, k, start, end, offset;
+ FILE *map;
+ struct stat s;
+ int image, tail;
+
+ if (stat (argv[3], &s) < 0) die (argv[3]);
+ map = fopen (argv[2], "r");
+ if (!map) die(argv[2]);
+ while (fgets (buffer, 1024, map)) {
+ if (!strcmp (buffer + 19, "start\n"))
+ start = strtoul (buffer + 8, NULL, 16);
+ else if (!strcmp (buffer + 19, "end\n"))
+ end = strtoul (buffer + 8, NULL, 16);
+ }
+ fclose (map);
+ if ((image = open(argv[1],O_RDWR)) < 0) die(argv[1]);
+ if (read(image,buffer,512) != 512) die(argv[1]);
+ if (!memcmp (buffer, "\177ELF", 4)) {
+ unsigned int *p = (unsigned int *)(buffer + *(unsigned int *)(buffer + 28));
+
+ i = p[1] + *(unsigned int *)(buffer + 24) - p[2];
+ if (lseek(image,i,0) < 0) die("lseek");
+ if (read(image,buffer,512) != 512) die(argv[1]);
+ j = 0;
+ } else if (*(unsigned int *)buffer == 0x01030107) {
+ i = j = 32;
+ } else {
+ fprintf (stderr, "Not ELF nor a.out. Don't blame me.\n");
+ exit(1);
+ }
+ k = i;
+ if (j == 32 && buffer[40] == 'H' && buffer[41] == 'd' && buffer[42] == 'r' && buffer[43] == 'S') {
+ offset = 40 + 10;
+ } else {
+ i += ((*(unsigned short *)(buffer + j + 2))<<2) - 512;
+ if (lseek(image,i,0) < 0) die("lseek");
+ if (read(image,buffer,1024) != 1024) die(argv[1]);
+ for (q = buffer, r = q + 512; q < r; q += 4) {
+ if (*q == 'H' && q[1] == 'd' && q[2] == 'r' && q[3] == 'S')
+ break;
+ }
+ if (q == r) {
+ fprintf (stderr, "Couldn't find headers signature in the kernel.\n");
+ exit(1);
+ }
+ offset = i + (q - buffer) + 10;
+ }
+ if (lseek(image, offset, 0) < 0) die ("lseek");
+ *(unsigned *)buffer = 0;
+ *(unsigned *)(buffer + 4) = 0x01000000;
+ *(unsigned *)(buffer + 8) = ((end + 32 + 8191) & ~8191);
+ *(unsigned *)(buffer + 12) = s.st_size;
+ if (write(image,buffer+2,14) != 14) die (argv[1]);
+ if (lseek(image, 4, 0) < 0) die ("lseek");
+ *(unsigned *)buffer = ((end + 32 + 8191) & ~8191) - (start & ~0x3fffffUL) + s.st_size;
+ *(unsigned *)(buffer + 4) = 0;
+ *(unsigned *)(buffer + 8) = 0;
+ if (write(image,buffer,12) != 12) die (argv[1]);
+ if (lseek(image, k - start + ((end + 32 + 8191) & ~8191), 0) < 0) die ("lseek");
+ if ((tail = open(argv[3],O_RDONLY)) < 0) die(argv[3]);
+ while ((i = read (tail,buffer,1024)) > 0)
+ if (write(image,buffer,i) != i) die (argv[1]);
+ if (close(image) < 0) die("close");
+ if (close(tail) < 0) die("close");
+ return 0;
+}
diff --git a/arch/sparc64/config.in b/arch/sparc64/config.in
index 6354edded..fcbac5c1a 100644
--- a/arch/sparc64/config.in
+++ b/arch/sparc64/config.in
@@ -1,4 +1,4 @@
-# $Id: config.in,v 1.6 1997/04/17 20:35:42 jj 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 fbc4a5073..92a003853 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,24 +47,31 @@ SUN_FB_CREATOR=y
#
# Misc Linux/SPARC drivers
#
-# CONFIG_SUN_OPENPROMIO is not set
-# CONFIG_SUN_MOSTEK_RTC is not set
-# CONFIG_SUN_OPENPROMFS is not set
+CONFIG_SUN_OPENPROMIO=m
+CONFIG_SUN_MOSTEK_RTC=y
+# 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_RAM is not set
-# CONFIG_BLK_DEV_LOOP 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=y
+CONFIG_BLK_DEV_LOOP=m
#
# Networking options
@@ -75,6 +84,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)
@@ -84,13 +94,22 @@ CONFIG_INET=y
CONFIG_PATH_MTU_DISCOVERY=y
CONFIG_IP_NOSR=y
CONFIG_SKB_LARGE=y
+CONFIG_IPV6=m
#
#
#
-# CONFIG_IPX is not set
-# CONFIG_ATALK is not set
+CONFIG_IPX=m
+# CONFIG_IPX_INTERN is not set
+# 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
@@ -101,10 +120,10 @@ CONFIG_SCSI=y
# SCSI support type (disk, tape, CDrom)
#
CONFIG_BLK_DEV_SD=y
-# CONFIG_CHR_DEV_ST is not set
+CONFIG_CHR_DEV_ST=y
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
@@ -116,46 +135,59 @@ 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
#
CONFIG_NETDEVICES=y
-CONFIG_DUMMY=y
-# CONFIG_PPP is not set
-# CONFIG_SLIP is not set
+CONFIG_DUMMY=m
+CONFIG_PPP=m
+
+#
+# CCP compressors for PPP are only built as modules.
+#
+CONFIG_SLIP=m
+CONFIG_SLIP_COMPRESSED=y
+CONFIG_SLIP_SMART=y
+# CONFIG_SLIP_MODE_SLIP6 is not set
CONFIG_SUNLANCE=y
-# CONFIG_HAPPYMEAL is not set
-# CONFIG_SUNQE is not set
-# CONFIG_MYRI_SBUS is not set
+CONFIG_HAPPYMEAL=y
+CONFIG_SUNQE=m
+CONFIG_MYRI_SBUS=m
#
# Filesystems
#
# CONFIG_QUOTA is not set
-# CONFIG_MINIX_FS is not set
+# CONFIG_DCACHE_PRELOAD is not set
+# CONFIG_OMIRR is not set
+# CONFIG_TRANS_NAMES is not set
+CONFIG_MINIX_FS=m
CONFIG_EXT2_FS=y
-# CONFIG_FAT_FS is not set
-# CONFIG_MSDOS_FS is not set
-# CONFIG_VFAT_FS is not set
+CONFIG_FAT_FS=m
+CONFIG_MSDOS_FS=m
+CONFIG_VFAT_FS=m
# CONFIG_UMSDOS_FS is not set
CONFIG_PROC_FS=y
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
+CONFIG_SMB_FS=m
+CONFIG_SMB_WIN95=y
+CONFIG_NCP_FS=m
CONFIG_ISO9660_FS=y
-# CONFIG_HPFS_FS is not set
-# 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_UFS_FS=y
+CONFIG_HPFS_FS=m
+CONFIG_SYSV_FS=m
+CONFIG_AFFS_FS=m
+CONFIG_ROMFS_FS=m
+CONFIG_AUTOFS_FS=m
+CONFIG_AMIGA_PARTITION=y
+CONFIG_UFS_FS=m
CONFIG_BSD_DISKLABEL=y
CONFIG_SMD_DISKLABEL=y
@@ -163,3 +195,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 199360a5f..9e9013735 100644
--- a/arch/sparc64/kernel/Makefile
+++ b/arch/sparc64/kernel/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.22 1997/05/27 19:30:17 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,20 +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
+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 000000000..215aaf06f
--- /dev/null
+++ b/arch/sparc64/kernel/binfmt_aout32.c
@@ -0,0 +1,482 @@
+/*
+ * 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 <linux/module.h>
+
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/a.out.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/user.h>
+#include <linux/malloc.h>
+#include <linux/binfmts.h>
+#include <linux/personality.h>
+#include <linux/init.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+
+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 dentry * dentry = NULL;
+ 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
+ dentry = open_namei(corefile,O_CREAT | 2 | O_TRUNC, 0600);
+ if (IS_ERR(dentry)) {
+ dentry = NULL;
+ goto end_coredump;
+ }
+ inode = dentry->d_inode;
+ 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_dentry = dentry;
+ 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));
+close_coredump:
+ if (file.f_op->release)
+ file.f_op->release(inode,&file);
+done_coredump:
+ put_write_access(inode);
+end_coredump:
+ set_fs(fs);
+ dput(dentry);
+ 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->dentry->d_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(&current->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->dentry, 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->dentry, 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->dentry, 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_dentry(bprm->dentry, 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->dentry, 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 dentry * dentry;
+ struct inode * inode;
+ unsigned int len;
+ unsigned int bss;
+ unsigned int start_addr;
+ unsigned long error;
+
+ file = current->files->fd[fd];
+
+ if (!file || !file->f_op)
+ return -EACCES;
+
+ dentry = file->f_dentry;
+ inode = dentry->d_inode;
+
+ /* 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/binfmt_elf32.c b/arch/sparc64/kernel/binfmt_elf32.c
index 05d50fe56..9ab2b7aca 100644
--- a/arch/sparc64/kernel/binfmt_elf32.c
+++ b/arch/sparc64/kernel/binfmt_elf32.c
@@ -6,6 +6,8 @@
#define ELF_CLASS ELFCLASS32
#define ELF_DATA ELFDATA2MSB;
+#define elf_check_arch(x) (((x) == EM_SPARC) || ((x) == EM_SPARC32PLUS))
+
#include <asm/processor.h>
#include <linux/module.h>
#include <linux/config.h>
diff --git a/arch/sparc64/kernel/cpu.c b/arch/sparc64/kernel/cpu.c
index 695ad680e..d6cdf9162 100644
--- a/arch/sparc64/kernel/cpu.c
+++ b/arch/sparc64/kernel/cpu.c
@@ -6,7 +6,9 @@
#include <linux/kernel.h>
#include <linux/init.h>
+#include <asm/asi.h>
#include <asm/system.h>
+#include <asm/fpumacro.h>
struct cpu_iu_info {
short manuf;
@@ -26,6 +28,7 @@ struct cpu_fp_info {
*/
struct cpu_fp_info linux_sparc_fpu[] = {
{ 0x17, 0x10, 0, "UltraSparc I integrated FPU"},
+ { 0x22, 0x10, 0, "UltraSparc II integrated FPU"},
{ 0x17, 0x11, 0, "UltraSparc II integrated FPU"},
{ 0x17, 0x12, 0, "UltraSparc III integrated FPU"},
};
@@ -34,6 +37,7 @@ struct cpu_fp_info linux_sparc_fpu[] = {
struct cpu_iu_info linux_sparc_chips[] = {
{ 0x17, 0x10, "TI UltraSparc I (SpitFire)"},
+ { 0x22, 0x10, "TI UltraSparc II (BlackBird)"},
{ 0x17, 0x11, "TI UltraSparc II (BlackBird)"},
{ 0x17, 0x12, "TI UltraSparc III (Cheetah)"}, /* A guess... */
};
@@ -50,11 +54,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 6aadd14e0..5e6705896 100644
--- a/arch/sparc64/kernel/devices.c
+++ b/arch/sparc64/kernel/devices.c
@@ -6,7 +6,6 @@
#include <linux/kernel.h>
#include <linux/tasks.h>
-#include <linux/config.h>
#include <linux/init.h>
#include <asm/page.h>
@@ -15,7 +14,7 @@
#include <asm/smp.h>
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 31b87f3de..b034ef407 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 0d95e1b75..a410cfe80 100644
--- a/arch/sparc64/kernel/entry.S
+++ b/arch/sparc64/kernel/entry.S
@@ -1,7 +1,7 @@
-/* $Id: entry.S,v 1.31 1997/06/02 06:33:25 davem Exp $
+/* $Id: entry.S,v 1.50 1997/07/15 16:53:00 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)
@@ -25,7 +25,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 +37,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 +66,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,47 +93,119 @@ sparc64_itlb_refbit_catch:
mov 1, %g4
rdpr %pstate, %g1
wrpr %g1, PSTATE_AG|PSTATE_MG, %pstate
- ba,pt %xcc, etrap
- rd %pc, %g7
+ rdpr %tpc, %g5
- 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
-
-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
+
+#ifdef __SMP__
/* 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:
*
- * DATA 0: Address of function to call
- * DATA 1: Argument 1, place in %g6
- * DATA 2: Argument 2, place in %g7
+ * DATA 0: [low 32-bits] Address of function to call, jmp to this
+ * [high 32-bits] MMU Context Argument 0, place in %g5
+ * DATA 1: Address Argument 1, place in %g6
+ * DATA 2: Address Argument 2, place in %g7
*
* With this method we can do most of the cross-call tlb/cache
- * flushing in very quickly.
+ * flushing very quickly.
*/
- .align 4
+ .align 32
.globl do_ivec
do_ivec:
ldxa [%g0] ASI_INTR_RECEIVE, %g1
@@ -139,16 +214,14 @@ do_ivec:
mov 0x40, %g2
/* Load up Interrupt Vector Data 0 register. */
- sethi %uhi(ivector_to_mask), %g4
+ sethi %hi(KERNBASE), %g4
ldxa [%g2] ASI_UDB_INTR_R, %g3
- or %g4, %ulo(ivector_to_mask), %g4
+ cmp %g3, %g4
+ bgeu,pn %xcc, do_ivec_xcall
+ nop
and %g3, 0x7ff, %g3
- sllx %g4, 32, %g4
- sethi %hi(ivector_to_mask), %g5
sllx %g3, 3, %g3
- or %g5, %lo(ivector_to_mask), %g5
- add %g5, %g4, %g4
- ldx [%g4 + %g3], %g2
+ ldx [%g1 + %g3], %g2
brz,pn %g2, do_ivec_spurious
nop
@@ -163,9 +236,17 @@ do_ivec_return:
stxa %g0, [%g0] ASI_INTR_RECEIVE
membar #Sync
retry
-
+do_ivec_xcall:
+ srlx %g3, 32, %g5
+ add %g2, 0x10, %g2
+ sra %g3, 0, %g3
+ ldxa [%g2] ASI_UDB_INTR_R, %g6
+ add %g2, 0x10, %g2
+ jmpl %g3, %g0
+ ldxa [%g2] ASI_UDB_INTR_R, %g7
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
@@ -174,8 +255,132 @@ do_ivec_spurious:
add %sp, STACK_BIAS + REGWIN_SZ, %o0
ba,pt %xcc, rtrap
clr %l6
+#endif /* __SMP__ */
+
+ .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]
+
+#ifdef CONFIG_BLK_DEV_FD
+ .globl floppy_hardint
+floppy_hardint:
+ sethi %hi(doing_pdma), %g1
+ ld [%g1 + %lo(doing_pdma)], %g2
+ brz,pn %g2, floppy_dosoftint
+ sethi %hi(fdc_status), %g3
+ ldx [%g3 + %lo(fdc_status)], %g3
+ sethi %hi(pdma_vaddr), %g5
+ ldx [%g5 + %lo(pdma_vaddr)], %g4
+ sethi %hi(pdma_size), %g5
+ ldx [%g5 + %lo(pdma_size)], %g5
+
+next_byte:
+ ldub [%g3], %g7
+ andcc %g7, 0x80, %g0
+ be,pn %icc, floppy_fifo_emptied
+ andcc %g7, 0x20, %g0
+ be,pn %icc, floppy_overrun
+ andcc %g7, 0x40, %g0
+ be,pn %icc, floppy_write
+ sub %g5, 1, %g5
+
+ ldub [%g3 + 1], %g7
+ orcc %g0, %g5, %g0
+ stb %g7, [%g4]
+ bne,pn %xcc, next_byte
+ add %g4, 1, %g4
+
+ b,pt %xcc, floppy_tdone
+ nop
+
+floppy_write:
+ ldub [%g4], %g7
+ orcc %g0, %g5, %g0
+ stb %g7, [%g3 + 1]
+ bne,pn %xcc, next_byte
+ add %g4, 1, %g4
+
+floppy_tdone:
+ sethi %hi(pdma_vaddr), %g1
+ stx %g4, [%g1 + %lo(pdma_vaddr)]
+ sethi %hi(pdma_size), %g1
+ stx %g5, [%g1 + %lo(pdma_size)]
+ sethi %hi(auxio_register), %g1
+ ldx [%g1 + %lo(auxio_register)], %g7
+ ldub [%g7], %g5
+ or %g5, 0xc2, %g5
+ stb %g5, [%g7]
+ andn %g5, 0x02, %g5
+
+ nop; nop; nop; nop; nop; nop;
+ nop; nop; nop; nop; nop; nop;
+
+ stb %g5, [%g7]
+ sethi %hi(doing_pdma), %g1
+ b,pt %xcc, floppy_dosoftint
+ st %g0, [%g1 + %lo(doing_pdma)]
+
+floppy_fifo_emptied:
+ sethi %hi(pdma_vaddr), %g1
+ stx %g4, [%g1 + %lo(pdma_vaddr)]
+ sethi %hi(pdma_size), %g1
+ stx %g5, [%g1 + %lo(pdma_size)]
+ sethi %hi(irq_action), %g1
+ or %g1, %lo(irq_action), %g1
+ ldx [%g1 + (11 << 3)], %g3 ! irqaction[floppy_irq]
+ ldx [%g3 + 0x10], %g4 ! action->mask
+ st %g0, [%g4] ! SYSIO_ICLR_IDLE
+ membar #Sync ! probably not needed...
+ retry
+
+floppy_overrun:
+ sethi %hi(pdma_vaddr), %g1
+ stx %g4, [%g1 + %lo(pdma_vaddr)]
+ sethi %hi(pdma_size), %g1
+ stx %g5, [%g1 + %lo(pdma_size)]
+ sethi %hi(doing_pdma), %g1
+ st %g0, [%g1 + %lo(doing_pdma)]
+
+floppy_dosoftint:
+ rdpr %pil, %g2
+ wrpr %g0, 15, %pil
+ b,pt %xcc, etrap_irq
+ rd %pc, %g7
+
+ mov 11, %o0
+ mov 0, %o1
+ call sparc_floppy_irq
+ add %sp, STACK_BIAS + REGWIN_SZ, %o2
- .globl do_mna
+ b,pt %xcc, rtrap
+ clr %l6
+
+#endif /* CONFIG_BLK_DEV_FD */
+
+ /* XXX Here is stuff we still need to write... -DaveM XXX */
+ .globl indirect_syscall, netbsd_syscall, solaris_syscall
+indirect_syscall:
+netbsd_syscall:
+solaris_syscall:
+ retl
+ nop
+
+ .globl do_mna
do_mna:
rdpr %tl, %g3
cmp %g3, 1
@@ -195,187 +400,239 @@ 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 */
+ /* This is how fork() was meant to be done, 12 instruction entry.
+ *
+ * I questioned the following code briefly, let me clear things
+ * up so you must not reason on it like I did.
+ *
+ * Know the fork_kpsr etc. we use in the sparc32 port? We don't
+ * need it here because the only piece of window state we copy to
+ * the child is the CWP register. Even if the parent sleeps,
+ * we are safe because we stuck it into pt_regs of the parent
+ * so it will not change.
+ *
+ * XXX This raises the question, whether we can do the same on
+ * XXX sparc32 to get rid of fork_kpsr _and_ fork_kwim. The
+ * XXX answer is yes. We stick fork_kpsr in UREG_G0 and
+ * XXX fork_kwim in UREG_G1 (global registers are considered
+ * XXX volatile across a system call in the sparc ABI I think
+ * XXX if it isn't we can use regs->y instead, anyone who depends
+ * XXX upon the Y register being preserved across a fork deserves
+ * XXX to lose).
+ *
+ * In fact we should take advantage of that fact for other things
+ * during system calls...
+ */
.globl sys_fork, sys_vfork, sys_clone
+ .globl ret_from_syscall, ret_from_smpfork
+ .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
+#ifdef __SMP__
+ret_from_smpfork:
+ sethi %hi(scheduler_lock), %o4
+ membar #StoreStore | #LoadStore
+ stb %g0, [%o4 + %lo(scheduler_lock)]
+#endif
+ret_from_syscall:
+ b,pt %xcc, ret_sys_call
+ ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0], %o0
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 +640,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 +659,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 +670,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 efb1b48fc..4daf30e21 100644
--- a/arch/sparc64/kernel/etrap.S
+++ b/arch/sparc64/kernel/etrap.S
@@ -1,4 +1,4 @@
-/* $Id: etrap.S,v 1.21 1997/06/02 06:33:28 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,88 +12,121 @@
#include <asm/spitfire.h>
#include <asm/head.h>
- /* 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
- stxa %g0, [%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 843221395..000000000
--- a/arch/sparc64/kernel/hack.S
+++ /dev/null
@@ -1,170 +0,0 @@
-/* <hack>
- 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 3844c24c3..0ed975aff 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 <linux/version.h>
+#include <linux/errno.h>
+#include <asm/asm_offsets.h>
+#include <asm/asi.h>
#include <asm/pstate.h>
#include <asm/ptrace.h>
#include <asm/spitfire.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/errno.h>
+#include <asm/signal.h>
+#include <asm/processor.h>
#include <asm/lsu.h>
#include <asm/head.h>
/* 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/ioctl32.c b/arch/sparc64/kernel/ioctl32.c
index d3792dec6..b7a0f312d 100644
--- a/arch/sparc64/kernel/ioctl32.c
+++ b/arch/sparc64/kernel/ioctl32.c
@@ -1,4 +1,4 @@
-/* $Id: ioctl32.c,v 1.8 1997/06/04 13:05:15 jj Exp $
+/* $Id: ioctl32.c,v 1.13 1997/07/17 02:20:38 davem Exp $
* ioctl32.c: Conversion between 32bit and 64bit native ioctls.
*
* Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -20,9 +20,16 @@
#include <linux/kd.h>
#include <linux/route.h>
#include <linux/netlink.h>
+#include <linux/vt.h>
+#include <linux/fs.h>
+#include <linux/fd.h>
#include <asm/types.h>
#include <asm/uaccess.h>
+#include <asm/fbio.h>
+#include <asm/kbio.h>
+#include <asm/vuid_event.h>
+#include <asm/rtc.h>
/* 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.
@@ -370,14 +377,122 @@ static inline int hdio_getgeo(unsigned int fd, u32 arg)
return err;
}
+struct fbcmap32 {
+ int index; /* first element (0 origin) */
+ int count;
+ u32 red;
+ u32 green;
+ u32 blue;
+};
+
+#define FBIOPUTCMAP32 _IOW('F', 3, struct fbcmap32)
+#define FBIOGETCMAP32 _IOW('F', 4, struct fbcmap32)
+
+static inline int fbiogetputcmap(unsigned int fd, unsigned int cmd, u32 arg)
+{
+ struct fbcmap f;
+ int ret;
+ char red[256], green[256], blue[256];
+ u32 r, g, b;
+ unsigned long old_fs = get_fs();
+
+ if (get_user(f.index, &(((struct fbcmap32 *)A(arg))->index)) ||
+ __get_user(f.count, &(((struct fbcmap32 *)A(arg))->count)) ||
+ __get_user(r, &(((struct fbcmap32 *)A(arg))->red)) ||
+ __get_user(g, &(((struct fbcmap32 *)A(arg))->green)) ||
+ __get_user(b, &(((struct fbcmap32 *)A(arg))->blue)))
+ return -EFAULT;
+ if ((f.index < 0) || (f.index > 255)) return -EINVAL;
+ if (f.index + f.count > 256)
+ f.count = 256 - f.index;
+ if (cmd == FBIOPUTCMAP32) {
+ if (copy_from_user (red, (char *)A(r), f.count) ||
+ copy_from_user (green, (char *)A(g), f.count) ||
+ copy_from_user (blue, (char *)A(b), f.count))
+ return -EFAULT;
+ }
+ f.red = red; f.green = green; f.blue = blue;
+ set_fs (KERNEL_DS);
+ ret = sys_ioctl (fd, (cmd == FBIOPUTCMAP32) ? FBIOPUTCMAP : FBIOGETCMAP, (long)&f);
+ set_fs (old_fs);
+ if (!ret && cmd == FBIOGETCMAP32) {
+ if (copy_to_user ((char *)A(r), red, f.count) ||
+ copy_to_user ((char *)A(g), green, f.count) ||
+ copy_to_user ((char *)A(b), blue, f.count))
+ return -EFAULT;
+ }
+ return ret;
+}
+
+struct fbcursor32 {
+ short set; /* what to set, choose from the list above */
+ short enable; /* cursor on/off */
+ struct fbcurpos pos; /* cursor position */
+ struct fbcurpos hot; /* cursor hot spot */
+ struct fbcmap32 cmap; /* color map info */
+ struct fbcurpos size; /* cursor bit map size */
+ u32 image; /* cursor image bits */
+ u32 mask; /* cursor mask bits */
+};
+
+#define FBIOSCURSOR32 _IOW('F', 24, struct fbcursor32)
+#define FBIOGCURSOR32 _IOW('F', 25, struct fbcursor32)
+
+static inline int fbiogscursor(unsigned int fd, unsigned int cmd, u32 arg)
+{
+ struct fbcursor f;
+ int ret;
+ char red[2], green[2], blue[2];
+ char image[128], mask[128];
+ u32 r, g, b;
+ u32 m, i;
+ unsigned long old_fs = get_fs();
+
+ if (copy_from_user (&f, (struct fbcursor32 *)A(arg), 2 * sizeof (short) + 2 * sizeof(struct fbcurpos)) ||
+ __get_user(f.size.fbx, &(((struct fbcursor32 *)A(arg))->size.fbx)) ||
+ __get_user(f.size.fby, &(((struct fbcursor32 *)A(arg))->size.fby)) ||
+ __get_user(f.cmap.index, &(((struct fbcursor32 *)A(arg))->cmap.index)) ||
+ __get_user(f.cmap.count, &(((struct fbcursor32 *)A(arg))->cmap.count)) ||
+ __get_user(r, &(((struct fbcursor32 *)A(arg))->cmap.red)) ||
+ __get_user(g, &(((struct fbcursor32 *)A(arg))->cmap.green)) ||
+ __get_user(b, &(((struct fbcursor32 *)A(arg))->cmap.blue)) ||
+ __get_user(m, &(((struct fbcursor32 *)A(arg))->mask)) ||
+ __get_user(i, &(((struct fbcursor32 *)A(arg))->image)))
+ return -EFAULT;
+ if (f.set & FB_CUR_SETCMAP) {
+ if ((uint) f.size.fby > 32)
+ return -EINVAL;
+ if (copy_from_user (mask, (char *)A(m), f.size.fby * 4) ||
+ copy_from_user (image, (char *)A(i), f.size.fby * 4))
+ return -EFAULT;
+ f.image = image; f.mask = mask;
+ }
+ if (f.set & FB_CUR_SETCMAP) {
+ if (copy_from_user (red, (char *)A(r), 2) ||
+ copy_from_user (green, (char *)A(g), 2) ||
+ copy_from_user (blue, (char *)A(b), 2))
+ return -EFAULT;
+ f.cmap.red = red; f.cmap.green = green; f.cmap.blue = blue;
+ }
+ set_fs (KERNEL_DS);
+ ret = sys_ioctl (fd, FBIOSCURSOR, (long)&f);
+ set_fs (old_fs);
+ return ret;
+}
+
asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg)
{
struct file * filp;
int error = -EBADF;
lock_kernel();
- if (fd >= NR_OPEN || !(filp = current->files->fd[fd]))
+ if(fd >= NR_OPEN)
+ goto out;
+
+ filp = current->files->fd[fd];
+ if(!filp)
goto out;
+
if (!filp->f_op || !filp->f_op->ioctl) {
error = sys_ioctl (fd, cmd, (unsigned long)arg);
goto out;
@@ -431,6 +546,15 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg)
case BLKGETSIZE:
error = w_long(fd, cmd, arg);
goto out;
+
+ case FBIOPUTCMAP32:
+ case FBIOGETCMAP32:
+ error = fbiogetputcmap(fd, cmd, arg);
+ goto out;
+
+ case FBIOSCURSOR32:
+ error = fbiogscursor(fd, cmd, arg);
+ goto out;
/* List here exlicitly which ioctl's are known to have
* compatable types passed or none at all...
@@ -471,6 +595,17 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg)
case TIOCSPGRP:
case TIOCGPGRP:
case TIOCSCTTY:
+
+ /* Big F */
+ case FBIOGTYPE:
+ case FBIOSATTR:
+ case FBIOGATTR:
+ case FBIOSVIDEO:
+ case FBIOGVIDEO:
+ case FBIOGCURSOR32: /* This is not implemented yet. Later it should be converted... */
+ case FBIOSCURPOS:
+ case FBIOGCURPOS:
+ case FBIOGCURMAX:
/* Little f */
case FIOCLEX:
@@ -479,6 +614,19 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg)
case FIONBIO:
case FIONREAD: /* This is also TIOCINQ */
+ /* 0x00 */
+ case FIBMAP:
+ case FIGETBSZ:
+
+ /* 0x02 -- Floppy ioctls */
+ case FDSETEMSGTRESH:
+ case FDFLUSH:
+ case FDSETMAXERRS:
+ case FDGETMAXERRS:
+ case FDGETDRVTYP:
+ case FDEJECT:
+ /* XXX The rest need struct floppy_* translations. */
+
/* 0x12 */
case BLKRRPART:
case BLKFLSBUF:
@@ -495,6 +643,59 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg)
case KDSIGACCEPT:
case KDGETKEYCODE:
case KDSETKEYCODE:
+ case KIOCSOUND:
+ case KDMKTONE:
+ case KDGKBTYPE:
+ case KDSETMODE:
+ case KDGETMODE:
+ case KDSKBMODE:
+ case KDGKBMODE:
+ case KDSKBMETA:
+ case KDGKBMETA:
+ case KDGKBENT:
+ case KDSKBENT:
+ case KDGKBSENT:
+ case KDSKBSENT:
+ case KDGKBDIACR:
+ case KDSKBDIACR:
+ case KDGKBLED:
+ case KDSKBLED:
+ case KDGETLED:
+ case KDSETLED:
+
+ /* Little k */
+ case KIOCTYPE:
+ case KIOCLAYOUT:
+ case KIOCGTRANS:
+ case KIOCTRANS:
+ case KIOCCMD:
+ case KIOCSDIRECT:
+ case KIOCSLED:
+ case KIOCGLED:
+ case KIOCSRATE:
+ case KIOCGRATE:
+
+ /* Big V */
+ case VT_SETMODE:
+ case VT_GETMODE:
+ case VT_GETSTATE:
+ case VT_OPENQRY:
+ case VT_ACTIVATE:
+ case VT_WAITACTIVE:
+ case VT_RELDISP:
+ case VT_DISALLOCATE:
+ case VT_RESIZE:
+ case VT_RESIZEX:
+ case VT_LOCKSWITCH:
+ case VT_UNLOCKSWITCH:
+
+ /* Little v */
+ case VUIDSFORMAT:
+ case VUIDGFORMAT:
+
+ /* Little p (/dev/rtc etc.) */
+ case RTCGET:
+ case RTCSET:
/* Socket level stuff */
case FIOSETOWN:
diff --git a/arch/sparc64/kernel/ioport.c b/arch/sparc64/kernel/ioport.c
index 2f94e9102..390c33517 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 3c9b1a89e..c4e7f0e74 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.16 1997/07/11 03:03:08 davem Exp $
* irq.c: UltraSparc IRQ handling/init/registry.
*
* Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -47,7 +47,8 @@ unsigned long ivector_to_mask[NUM_IVECS];
static struct irqaction static_irqaction[MAX_STATIC_ALLOC];
static int static_irq_count = 0;
-static struct irqaction *irq_action[NR_IRQS+1] = {
+/* XXX Must be exported so that fast IRQ handlers can get at it... -DaveM */
+struct irqaction *irq_action[NR_IRQS+1] = {
NULL, NULL, NULL, NULL, NULL, NULL , NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL , NULL, NULL
};
@@ -279,13 +280,13 @@ int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *)
/* If this is flagged as statically allocated then we use our
* private struct which is never freed.
*/
- if(irqflags & SA_STATIC_ALLOC)
+ if(irqflags & SA_STATIC_ALLOC) {
if(static_irq_count < MAX_STATIC_ALLOC)
action = &static_irqaction[static_irq_count++];
else
printk("Request for IRQ%d (%s) SA_STATIC_ALLOC failed "
"using kmalloc\n", irq, name);
-
+ }
if(action == NULL)
action = (struct irqaction *)kmalloc(sizeof(struct irqaction),
GFP_KERNEL);
@@ -464,14 +465,101 @@ void sparc_floppy_irq(int irq, void *dev_cookie, struct pt_regs *regs)
}
#endif
-/* XXX This needs to be written for floppy driver, and soon will be necessary
- * XXX for serial driver as well.
+/* The following assumes that the branch lies before the place we
+ * are branching to. This is the case for a trap vector...
+ * You have been warned.
*/
+#define SPARC_BRANCH(dest_addr, inst_addr) \
+ (0x10800000 | ((((dest_addr)-(inst_addr))>>2)&0x3fffff))
+
+#define SPARC_NOP (0x01000000)
+
+static void install_fast_irq(unsigned int cpu_irq,
+ void (*handler)(int, void *, struct pt_regs *))
+{
+ extern unsigned long sparc64_ttable_tl0;
+ unsigned long ttent = (unsigned long) &sparc64_ttable_tl0;
+ unsigned int *insns;
+
+ ttent += 0x820;
+ ttent += (cpu_irq - 1) << 5;
+ insns = (unsigned int *) ttent;
+ insns[0] = SPARC_BRANCH(((unsigned long) handler),
+ ((unsigned long)&insns[0]));
+ insns[1] = SPARC_NOP;
+ __asm__ __volatile__("flush %0" : : "r" (ttent));
+}
+
int request_fast_irq(unsigned int irq,
void (*handler)(int, void *, struct pt_regs *),
unsigned long irqflags, const char *name)
{
- return -1;
+ struct irqaction *action;
+ unsigned long flags;
+ unsigned int cpu_irq, *imap, *iclr;
+
+ /* XXX This really is not the way to do it, the "right way"
+ * XXX is to have drivers set SA_SBUS or something like that
+ * XXX in irqflags and we base our decision here on whether
+ * XXX that flag bit is set or not.
+ *
+ * In this case nobody can have a fast interrupt at the level
+ * where TICK interrupts live.
+ */
+ if(irq == 14)
+ return -EINVAL;
+ cpu_irq = ino_to_pil[irq];
+
+ if(!handler)
+ return -EINVAL;
+ imap = irq_to_imap(irq);
+ action = *(cpu_irq + irq_action);
+ if(action) {
+ if(action->flags & SA_SHIRQ)
+ panic("Trying to register fast irq when already shared.\n");
+ if(irqflags & SA_SHIRQ)
+ panic("Trying to register fast irq as shared.\n");
+ printk("request_fast_irq: Trying to register yet already owned.\n");
+ return -EBUSY;
+ }
+ save_and_cli(flags);
+ if(irqflags & SA_STATIC_ALLOC) {
+ if(static_irq_count < MAX_STATIC_ALLOC)
+ action = &static_irqaction[static_irq_count++];
+ else
+ printk("Request for IRQ%d (%s) SA_STATIC_ALLOC failed "
+ "using kmalloc\n", irq, name);
+ }
+ if(action == NULL)
+ action = (struct irqaction *)kmalloc(sizeof(struct irqaction),
+ GFP_KERNEL);
+ if(!action) {
+ restore_flags(flags);
+ return -ENOMEM;
+ }
+ install_fast_irq(cpu_irq, handler);
+
+ if(imap) {
+ int ivindex = (*imap & (SYSIO_IMAP_IGN | SYSIO_IMAP_INO));
+
+ ivector_to_mask[ivindex] = (1 << cpu_irq);
+ iclr = imap_to_iclr(imap);
+ action->mask = (unsigned long) iclr;
+ irqflags |= SA_SYSIO_MASKED;
+ } else
+ action->mask = 0;
+
+ action->handler = handler;
+ action->flags = irqflags;
+ action->dev_id = NULL;
+ action->name = name;
+ action->next = NULL;
+
+ *(cpu_irq + irq_action) = action;
+
+ enable_irq(irq);
+ restore_flags(flags);
+ return 0;
}
/* We really don't need these at all on the Sparc. We only have
@@ -496,27 +584,31 @@ static unsigned long tick_offset;
/* XXX This doesn't belong here, just do this cruft in the timer.c handler code. */
static void timer_handler(int irq, void *dev_id, struct pt_regs *regs)
{
- extern void timer_interrupt(int, void *, struct pt_regs *);
- unsigned long compare;
-
if (!(get_softint () & 1)) {
/* Just to be sure... */
clear_softint(1 << 14);
printk("Spurious level14 at %016lx\n", regs->tpc);
return;
- }
+ } else {
+ unsigned long compare, tick;
+
+ do {
+ extern void timer_interrupt(int, void *, struct pt_regs *);
- timer_interrupt(irq, dev_id, regs);
+ timer_interrupt(irq, dev_id, regs);
- /* Acknowledge INT_TIMER */
- clear_softint(1 << 0);
+ /* Acknowledge INT_TIMER */
+ clear_softint(1 << 0);
- /* Set up for next timer tick. */
- __asm__ __volatile__("rd %%tick_cmpr, %0\n\t"
- "add %0, %1, %0\n\t"
- "wr %0, 0x0, %%tick_cmpr"
- : "=r" (compare)
- : "r" (tick_offset));
+ /* Set up for next timer tick. */
+ __asm__ __volatile__("rd %%tick_cmpr, %0\n\t"
+ "add %0, %2, %0\n\t"
+ "wr %0, 0x0, %%tick_cmpr\n\t"
+ "rd %%tick, %1"
+ : "=&r" (compare), "=r" (tick)
+ : "r" (tick_offset));
+ } while(tick >= compare);
+ }
}
/* This is called from time_init() to get the jiffies timer going. */
@@ -558,6 +650,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];
@@ -582,7 +676,7 @@ static void map_prom_timers(void)
prom_timers = (struct sun5_timer *) 0;
return;
}
- prom_timers = (struct sun5_timer *) addr[0];
+ prom_timers = (struct sun5_timer *) ((unsigned long)addr[0]);
}
static void kill_prom_timer(void)
@@ -590,24 +684,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 cc8183618..89f63f78f 100644
--- a/arch/sparc64/kernel/process.c
+++ b/arch/sparc64/kernel/process.c
@@ -1,4 +1,4 @@
-/* $Id: process.c,v 1.17 1997/06/02 06:33:32 davem Exp $
+/* $Id: process.c,v 1.29 1997/07/17 02:20:40 davem Exp $
* arch/sparc64/kernel/process.c
*
* Copyright (C) 1995, 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -17,6 +17,8 @@
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
#include <linux/stddef.h>
#include <linux/unistd.h>
#include <linux/ptrace.h>
@@ -137,7 +139,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 +148,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 +207,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;
@@ -228,10 +265,7 @@ void show_stackframe32(struct sparc_stackf32 *sf)
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,16 +279,11 @@ 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)
{
-#if __MPP__
- printk("CID: %d\n",mpp_cid());
-#endif
printk("PSR: %08x PC: %08x NPC: %08x Y: %08x\n", regs->psr,
regs->pc, regs->npc, regs->y);
printk("g0: %08x g1: %08x g2: %08x g3: %08x\n",
@@ -269,7 +298,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 +318,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 *)&current->tss.float_regs[0],
- &current->tss.fsr);
- else
- fpsave((unsigned long *)&current->tss.float_regs[0],
- &current->tss.fsr);
-#ifndef __SMP__
- last_task_used_math = NULL;
-#else
- current->flags &= ~PF_USEDFPU;
-#endif
- }
}
void flush_thread(void)
@@ -338,28 +342,12 @@ 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 *)&current->tss.float_regs[0],
- &current->tss.fsr);
- else
- fpsave((unsigned long *)&current->tss.float_regs[0],
- &current->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;
if(current->tss.flags & SPARC_FLAG_KTHREAD) {
current->tss.flags &= ~SPARC_FLAG_KTHREAD;
@@ -368,78 +356,38 @@ void flush_thread(void)
*/
get_mmu_context(current);
}
- current->tss.current_ds = USER_DS;
- spitfire_set_secondary_context (current->mm->context);
+ current->tss.ctx = current->mm->context & 0x1fff;
+ spitfire_set_secondary_context (current->tss.ctx);
+ __asm__ __volatile__("flush %g6");
}
-static __inline__ void copy_regs(struct pt_regs *dst, struct pt_regs *src)
+/* It's a bit more tricky when 64-bit tasks are involved... */
+static unsigned long clone_stackframe(unsigned long csp, unsigned long psp)
{
- __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");
-}
-
-static __inline__ struct sparc_stackf *
-clone_stackframe(struct sparc_stackf *dst, struct sparc_stackf *src)
-{
- struct sparc_stackf *sp;
-
-#if 0
- unsigned long size;
- size = ((unsigned long)src->fp) - ((unsigned long)src);
- sp = (struct sparc_stackf *)(((unsigned long)dst) - size);
-
- if (copy_to_user(sp, src, size))
- return 0;
- if (put_user(dst, &sp->fp))
+ unsigned long fp, distance, rval;
+
+ if(!(current->tss.flags & SPARC_FLAG_32BIT)) {
+ csp += STACK_BIAS;
+ psp += STACK_BIAS;
+ __get_user(fp, &(((struct reg_window *)psp)->ins[6]));
+ } else
+ __get_user(fp, &(((struct reg_window32 *)psp)->ins[6]));
+ distance = fp - psp;
+ rval = (csp - distance);
+ if(copy_in_user(rval, psp, distance))
return 0;
-#endif
- return sp;
+ if(current->tss.flags & SPARC_FLAG_32BIT) {
+ if(put_user(((u32)csp), &(((struct reg_window32 *)rval)->ins[6])))
+ return 0;
+ return rval;
+ } else {
+ if(put_user(((u64)csp - STACK_BIAS),
+ &(((struct reg_window *)rval)->ins[6])))
+ return 0;
+ return rval - STACK_BIAS;
+ }
}
-/* #define DEBUG_WINFIXUPS */
-
/* Standard stuff. */
static inline void shift_window_buffer(int first_win, int last_win,
struct thread_struct *tp)
@@ -461,16 +409,16 @@ 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)) {
@@ -478,9 +426,6 @@ void synchronize_user_stack(void)
tp->w_saved--;
}
} while(window--);
-#ifdef DEBUG_WINFIXUPS
- printk(")");
-#endif
}
}
@@ -489,18 +434,18 @@ void fault_in_user_windows(struct pt_regs *regs)
struct thread_struct *tp = &current->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
- printk("fiuw(%d", (int)window);
-#endif
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))
@@ -508,9 +453,6 @@ void fault_in_user_windows(struct pt_regs *regs)
} while(window--);
}
current->tss.w_saved = 0;
-#ifdef DEBUG_WINFIXUPS
- printk(")");
-#endif
}
/* Copy a Sparc thread. The fork() return value conventions
@@ -530,97 +472,49 @@ 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;
-
-#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;
-#endif
- }
+ char *child_trap_frame;
+ int tframe_size;
/* 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;
- childregs->u_regs[UREG_G6] = (unsigned long) p;
+ p->tss.ctx = 0;
+ 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;
-
-#if 0
+ p->tss.ctx = (p->mm->context & 0x1fff);
if (sp != regs->u_regs[UREG_FP]) {
- struct sparc_stackf *childstack;
- struct sparc_stackf *parentstack;
-
- /*
- * This is a clone() call with supplied user stack.
- * Set some valid stack frames to give to the child.
- */
- childstack = (struct sparc_stackf *)sp;
- parentstack = (struct sparc_stackf *)regs->u_regs[UREG_FP];
+ unsigned long csp;
-#if 0
- printk("clone: parent stack:\n");
- show_stackframe(parentstack);
-#endif
-
- childstack = clone_stackframe(childstack, parentstack);
- if (!childstack)
+ csp = clone_stackframe(sp, regs->u_regs[UREG_FP]);
+ if(!csp)
return -EFAULT;
-
-#if 0
- printk("clone: child stack:\n");
- show_stackframe(childstack);
-#endif
-
- childregs->u_regs[UREG_FP] = (unsigned long)childstack;
+ p->tss.kregs->u_regs[UREG_FP] = csp;
}
-#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;
}
@@ -676,11 +570,19 @@ asmlinkage int sparc_execve(struct pt_regs *regs)
if(regs->u_regs[UREG_G1] == 0)
base = 1;
- error = getname((char *) regs->u_regs[base + UREG_I0], &filename);
- if(error)
- return error;
+ lock_kernel();
+ filename = getname((char *)regs->u_regs[base + UREG_I0]);
+ error = PTR_ERR(filename);
+ if(IS_ERR(filename))
+ goto out;
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;
+ }
+out:
+ unlock_kernel();
return error;
}
diff --git a/arch/sparc64/kernel/ptrace.c b/arch/sparc64/kernel/ptrace.c
index 24fe052cd..ac91df894 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->tnpc = 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, &current->signal);
+ /* spin_lock_irq(&current->sigmask_lock); */
+ current->signal |= (1 << (current->exit_code - 1));
+ /* spin_unlock_irq(&current->sigmask_lock); */
}
+
current->exit_code = 0;
}
diff --git a/arch/sparc64/kernel/rtrap.S b/arch/sparc64/kernel/rtrap.S
index 165b17ef0..9f087a969 100644
--- a/arch/sparc64/kernel/rtrap.S
+++ b/arch/sparc64/kernel/rtrap.S
@@ -1,4 +1,4 @@
-/* $Id: rtrap.S,v 1.21 1997/06/02 07:26:54 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 <asm/spitfire.h>
#include <asm/head.h>
- /* 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], %l3
- ldx [%l1 + %g4], %l4
+ 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 %l3, %l4, %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 832d3b97f..680baf7de 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.10 1997/07/08 11:07:47 jj Exp $
* linux/arch/sparc64/kernel/setup.c
*
* Copyright (C) 1995,1996 David S. Miller (davem@caip.rutgers.edu)
@@ -35,6 +35,7 @@
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/idprom.h>
+#include <asm/head.h>
struct screen_info screen_info = {
0, 0, /* orig-x, orig-y */
@@ -62,7 +63,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 +108,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 +138,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 +256,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();
@@ -313,7 +289,7 @@ __initfunc(void setup_arch(char **cmdline_p,
*memory_start_p = PAGE_ALIGN(((unsigned long) &end));
*memory_end_p = (end_of_phys_memory + PAGE_OFFSET);
-#ifndef NO_DAVEM_DEBUGGING
+#ifdef DAVEM_DEBUGGING
prom_printf("phys_base[%016lx] memory_start[%016lx] memory_end[%016lx]\n",
phys_base, *memory_start_p, *memory_end_p);
#endif
@@ -328,8 +304,11 @@ __initfunc(void setup_arch(char **cmdline_p,
#endif
#ifdef CONFIG_BLK_DEV_INITRD
if (ramdisk_image) {
- initrd_start = ramdisk_image;
- if (initrd_start < PAGE_OFFSET) initrd_start += PAGE_OFFSET;
+ unsigned long start = 0;
+
+ if (ramdisk_image >= (unsigned long)&end - 2 * PAGE_SIZE)
+ ramdisk_image -= KERNBASE;
+ initrd_start = ramdisk_image + phys_base + PAGE_OFFSET;
initrd_end = initrd_start + ramdisk_size;
if (initrd_end > *memory_end_p) {
printk(KERN_CRIT "initrd extends beyond end of memory "
@@ -337,9 +316,11 @@ __initfunc(void setup_arch(char **cmdline_p,
initrd_end,*memory_end_p);
initrd_start = 0;
}
- if (initrd_start >= *memory_start_p && initrd_start < *memory_start_p + 2 * PAGE_SIZE) {
+ if (initrd_start)
+ start = ramdisk_image + KERNBASE;
+ if (start >= *memory_start_p && start < *memory_start_p + 2 * PAGE_SIZE) {
initrd_below_start_ok = 1;
- *memory_start_p = PAGE_ALIGN (initrd_end);
+ *memory_start_p = PAGE_ALIGN (start + ramdisk_size);
}
}
#endif
@@ -424,7 +405,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 fe4615a6b..cfc55fc2e 100644
--- a/arch/sparc64/kernel/signal.c
+++ b/arch/sparc64/kernel/signal.c
@@ -1,4 +1,4 @@
-/* $Id: signal.c,v 1.6 1997/05/29 12:44:48 jj Exp $
+/* $Id: signal.c,v 1.20 1997/07/14 03:10:28 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 <linux/config.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/signal.h>
@@ -23,6 +24,7 @@
#include <asm/svr4.h>
#include <asm/pgtable.h>
#include <asm/fpumacro.h>
+#include <asm/uctx.h>
#include <asm/smp_lock.h>
#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 = &current->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 = &current->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(&current->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,24 +250,25 @@ 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 ();
- sf = (struct new_signal_frame *) regs->u_regs [UREG_FP];
+ sf = (struct new_signal_frame *)
+ (regs->u_regs [UREG_FP] + STACK_BIAS);
+
/* 1. Make sure we are not getting garbage from the user */
- if (verify_area (VERIFY_READ, sf, sizeof (*sf))){
+ if (verify_area (VERIFY_READ, sf, sizeof (*sf)))
goto segv;
- }
- if (((unsigned long) sf) & 3){
+
+ if (((unsigned long) sf) & 3)
goto segv;
- }
+
get_user(tpc, &sf->info.si_regs.tpc);
__get_user(tnpc, &sf->info.si_regs.tnpc);
- if ((tpc | tnpc) & 3){
+ if ((tpc | tnpc) & 3)
goto segv;
- }
+
regs->tpc = tpc;
regs->tnpc = tnpc;
@@ -165,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)
@@ -191,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 *)&current->tss.float_regs[0],
- &current->tss.fsr);
- regs->tstate &= ~(TSTATE_PEF);
- current->flags &= ~(PF_USEDFPU);
- }
-#else
- if (current == last_task_used_math) {
- fprs_write(FPRS_FEF);
- fpsave((unsigned long *)&current->tss.float_regs[0],
- &current->tss.fsr);
- last_task_used_math = 0;
- regs->tstate &= ~(TSTATE_PEF);
- }
-#endif
- copy_to_user(&fpu->si_float_regs[0], &current->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
@@ -220,33 +317,29 @@ new_setup_frame(struct sigaction *sa, struct pt_regs *regs,
{
struct new_signal_frame *sf;
int sigframe_size;
- unsigned long tmp;
- int i;
/* 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 *)(regs->u_regs[UREG_FP] - sigframe_size);
+ sf = (struct new_signal_frame *)
+ (regs->u_regs[UREG_FP] + STACK_BIAS - sigframe_size);
- if (invalid_frame_pointer (sf, sigframe_size)){
- lock_kernel ();
- do_exit(SIGILL);
- }
+ if (invalid_frame_pointer (sf, sigframe_size))
+ goto sigill;
- if (current->tss.w_saved != 0){
+ if (current->tss.w_saved != 0) {
printk ("%s[%d]: Invalid user stack frame for "
"signal delivery.\n", current->comm, current->pid);
- lock_kernel ();
- do_exit (SIGILL);
+ goto sigill;
}
/* 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 {
@@ -254,17 +347,17 @@ new_setup_frame(struct sigaction *sa, struct pt_regs *regs,
}
__put_user(oldmask, &sf->info.si_mask);
- for (i = 0; i < sizeof(struct reg_window)/8; i++) {
- __get_user(tmp, (((u64 *)regs->u_regs[UREG_FP])+i));
- __put_user(tmp, (((u64 *)sf)+i));
- }
+
+ copy_in_user((u64 *)sf,
+ (u64 *)(regs->u_regs[UREG_FP]+STACK_BIAS),
+ sizeof(struct reg_window));
/* 3. return to kernel instructions */
__put_user(0x821020d8, &sf->insns[0]); /* mov __NR_sigreturn, %g1 */
__put_user(0x91d02011, &sf->insns[1]); /* t 0x11 */
/* 4. signal handler back-trampoline and parameters */
- regs->u_regs[UREG_FP] = (unsigned long) sf;
+ regs->u_regs[UREG_FP] = ((unsigned long) sf) - STACK_BIAS;
regs->u_regs[UREG_I0] = signo;
regs->u_regs[UREG_I1] = (unsigned long) &sf->info;
regs->u_regs[UREG_I7] = (unsigned long) (&(sf->insns[0]) - 2);
@@ -274,15 +367,27 @@ 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");
+ }
+ }
+ return;
+
+sigill:
+ lock_kernel();
+ do_exit(SIGILL);
}
static inline void handle_signal(unsigned long signr, struct sigaction *sa,
@@ -291,8 +396,11 @@ static inline void handle_signal(unsigned long signr, struct sigaction *sa,
new_setup_frame(sa, regs, signr, oldmask);
if(sa->sa_flags & SA_ONESHOT)
sa->sa_handler = NULL;
- if(!(sa->sa_flags & SA_NOMASK))
+ if(!(sa->sa_flags & SA_NOMASK)) {
+ spin_lock_irq(&current->sigmask_lock);
current->blocked |= (sa->sa_mask | _S(signr)) & _BLOCKABLE;
+ spin_unlock_irq(&current->sigmask_lock);
+ }
}
static inline void syscall_restart(unsigned long orig_i0, struct pt_regs *regs,
@@ -334,7 +442,11 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs,
#endif
while ((signr = current->signal & mask) != 0) {
signr = ffz(~signr);
- clear_bit(signr + 32, &current->signal);
+
+ spin_lock_irq(&current->sigmask_lock);
+ current->signal &= ~(1 << signr);
+ spin_unlock_irq(&current->sigmask_lock);
+
sa = current->sig->action + signr;
signr++;
if ((current->flags & PF_PTRACED) && signr != SIGKILL) {
@@ -348,7 +460,9 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs,
if (signr == SIGSTOP)
continue;
if (_S(signr) & current->blocked) {
+ spin_lock_irq(&current->sigmask_lock);
current->signal |= _S(signr);
+ spin_unlock_irq(&current->sigmask_lock);
continue;
}
sa = current->sig->action + signr - 1;
@@ -391,8 +505,10 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs,
case SIGQUIT: case SIGILL: case SIGTRAP:
case SIGABRT: case SIGFPE: case SIGSEGV: case SIGBUS:
if(current->binfmt && current->binfmt->core_dump) {
+ lock_kernel();
if(current->binfmt->core_dump(signr, regs))
signr |= 0x80;
+ unlock_kernel();
}
#ifdef DEBUG_SIGNALS
/* Very useful to debug dynamic linker problems */
@@ -401,9 +517,15 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs,
#endif
/* fall through */
default:
+ spin_lock_irq(&current->sigmask_lock);
current->signal |= _S(signr & 0x7f);
+ spin_unlock_irq(&current->sigmask_lock);
+
current->flags |= PF_SIGNALED;
+
+ lock_kernel();
do_exit(signr);
+ unlock_kernel();
}
}
if(restart_syscall)
diff --git a/arch/sparc64/kernel/signal32.c b/arch/sparc64/kernel/signal32.c
index c0454658b..5135c2ae5 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.26 1997/07/14 03:10:31 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(&current->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))
@@ -257,9 +245,9 @@ setup_frame32(struct sigaction *sa, unsigned long pc, unsigned long npc,
#endif
int old_status = current->tss.sstk_info.cur_status;
unsigned psr;
- 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 +273,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);
@@ -301,13 +291,9 @@ setup_frame32(struct sigaction *sa, unsigned long pc, unsigned long npc,
}
else
#endif
- /* XXX Perhaps we need a copy_in_user()? -DaveM */
- for (i = 0; i < 16; i++) {
- u32 temp;
-
- get_user (temp, (((u32 *)(regs->u_regs[UREG_FP]))+i));
- put_user (temp, (((u32 *)sframep)+i));
- }
+ copy_in_user((u32 *)sframep,
+ (u32 *)(regs->u_regs[UREG_FP]),
+ sizeof(struct reg_window32));
current->tss.w_saved = 0; /* So process is allowed to execute. */
__put_user(signr, &sframep->sig_num);
@@ -329,35 +315,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 *)&current->tss.float_regs[0],
- &current->tss.fsr);
- regs->tstate &= ~(TSTATE_PEF);
- current->flags &= ~(PF_USEDFPU);
- }
-#else
- if (current == last_task_used_math) {
- fprs_write(FPRS_FEF);
- fpsave32((unsigned long *)&current->tss.float_regs[0],
- &current->tss.fsr);
- last_task_used_math = 0;
- regs->tstate &= ~(TSTATE_PEF);
- }
-#endif
- copy_to_user(&fpu->si_float_regs[0], &current->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,21 +335,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)) {
- lock_kernel ();
- do_exit(SIGILL);
+#ifdef DEBUG_SIGNALS
+ printk("new_setup_frame32(%s:%d): invalid_frame_pointer(%p, %d)\n",
+ current->comm, current->pid, sf, sigframe_size);
+#endif
+ goto 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);
- lock_kernel ();
- do_exit (SIGILL);
+#endif
+ goto sigill;
}
/* 2. Save the current process state */
@@ -389,11 +362,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 {
@@ -402,13 +377,9 @@ new_setup_frame32(struct sigaction *sa, struct pt_regs *regs,
__put_user(oldmask, &sf->info.si_mask);
- /* XXX Perhaps we need a copy_in_user()? -DaveM */
- for (i = 0; i < sizeof(struct reg_window32)/4; i++) {
- u32 tmp;
-
- __get_user(tmp, (((u32 *)regs->u_regs[UREG_FP])+i));
- __put_user(tmp, (((u32 *)sf)+i));
- }
+ copy_in_user((u32 *)sf,
+ (u32 *)(regs->u_regs[UREG_FP]),
+ sizeof(struct reg_window32));
/* 3. return to kernel instructions */
__put_user(0x821020d8, &sf->insns[0]); /* mov __NR_sigreturn, %g1 */
@@ -425,15 +396,27 @@ 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");
+ }
+ }
+ return;
+
+sigill:
+ lock_kernel();
+ do_exit(SIGILL);
}
/* Setup a Solaris stack frame */
@@ -454,6 +437,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 +469,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 +532,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 +570,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 */
@@ -619,16 +608,16 @@ asmlinkage int svr4_setcontext32(svr4_ucontext_t *c, struct pt_regs *regs)
if (tp->w_saved){
printk ("Uh oh, w_saved is: 0x%lx\n", tp->w_saved);
- do_exit(SIGSEGV);
+ goto sigsegv;
}
if (((unsigned long) c) & 3){
printk ("Unaligned structure passed\n");
- do_exit (SIGSEGV);
+ goto sigsegv;
}
if(!__access_ok((unsigned long)c, sizeof(*c))) {
/* Miguel, add nice debugging msg _here_. ;-) */
- do_exit(SIGSEGV);
+ goto sigsegv;
}
/* Check for valid PC and nPC */
@@ -637,7 +626,7 @@ asmlinkage int svr4_setcontext32(svr4_ucontext_t *c, struct pt_regs *regs)
__get_user(npc, &((*gr)[SVR4_NPC]));
if((pc | npc) & 3) {
printk ("setcontext, PC or nPC were bogus\n");
- do_exit (SIGSEGV);
+ goto sigsegv;
}
/* Retrieve information from passed ucontext */
/* note that nPC is ored a 1, this is used to inform entry.S */
@@ -650,14 +639,19 @@ 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;
+sigsegv:
+ lock_kernel();
+ do_exit(SIGSEGV);
}
static inline void handle_signal32(unsigned long signr, struct sigaction *sa,
@@ -674,8 +668,11 @@ static inline void handle_signal32(unsigned long signr, struct sigaction *sa,
}
if(sa->sa_flags & SA_ONESHOT)
sa->sa_handler = NULL;
- if(!(sa->sa_flags & SA_NOMASK))
+ if(!(sa->sa_flags & SA_NOMASK)) {
+ spin_lock_irq(&current->sigmask_lock);
current->blocked |= (sa->sa_mask | _S(signr)) & _BLOCKABLE;
+ spin_unlock_irq(&current->sigmask_lock);
+ }
}
static inline void syscall_restart32(unsigned long orig_i0, struct pt_regs *regs,
@@ -711,7 +708,11 @@ asmlinkage int do_signal32(unsigned long oldmask, struct pt_regs * regs,
while ((signr = current->signal & mask) != 0) {
signr = ffz(~signr);
- clear_bit(signr + 32, &current->signal);
+
+ spin_lock_irq(&current->sigmask_lock);
+ current->signal &= ~(1 << signr);
+ spin_unlock_irq(&current->sigmask_lock);
+
sa = current->sig->action + signr;
signr++;
if ((current->flags & PF_PTRACED) && signr != SIGKILL) {
@@ -725,7 +726,9 @@ asmlinkage int do_signal32(unsigned long oldmask, struct pt_regs * regs,
if (signr == SIGSTOP)
continue;
if (_S(signr) & current->blocked) {
+ spin_lock_irq(&current->sigmask_lock);
current->signal |= _S(signr);
+ spin_unlock_irq(&current->sigmask_lock);
continue;
}
sa = current->sig->action + signr - 1;
@@ -768,8 +771,10 @@ asmlinkage int do_signal32(unsigned long oldmask, struct pt_regs * regs,
case SIGQUIT: case SIGILL: case SIGTRAP:
case SIGABRT: case SIGFPE: case SIGSEGV: case SIGBUS:
if(current->binfmt && current->binfmt->core_dump) {
+ lock_kernel();
if(current->binfmt->core_dump(signr, regs))
signr |= 0x80;
+ unlock_kernel();
}
#ifdef DEBUG_SIGNALS
/* Very useful to debug dynamic linker problems */
@@ -778,9 +783,15 @@ asmlinkage int do_signal32(unsigned long oldmask, struct pt_regs * regs,
#endif
/* fall through */
default:
+ spin_lock_irq(&current->sigmask_lock);
current->signal |= _S(signr & 0x7f);
+ spin_unlock_irq(&current->sigmask_lock);
+
current->flags |= PF_SIGNALED;
+
+ lock_kernel();
do_exit(signr);
+ unlock_kernel();
}
}
if(restart_syscall)
@@ -805,9 +816,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/smp.c b/arch/sparc64/kernel/smp.c
new file mode 100644
index 000000000..88d7a8ecf
--- /dev/null
+++ b/arch/sparc64/kernel/smp.c
@@ -0,0 +1,347 @@
+/* smp.c: Sparc64 SMP support.
+ *
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+
+extern int linux_num_cpus;
+extern void calibrate_delay(void);
+
+volatile int smp_processors_ready = 0;
+unsigned long cpu_present_map = 0;
+int smp_num_cpus = 1;
+int smp_threads_ready = 0;
+
+struct cpuinfo_sparc64 cpu_data[NR_CPUS];
+static unsigned char boot_cpu_id = 0;
+static int smp_activated = 0;
+
+volatile int cpu_number_map[NR_CPUS];
+volatile int cpu_logical_map[NR_CPUS];
+
+struct klock_info klock_info = { KLOCK_CLEAR, 0 };
+
+static volatile int smp_commenced = 0;
+
+void smp_setup(char *str, int *ints)
+{
+ /* XXX implement me XXX */
+}
+
+static char smp_buf[512];
+
+char *smp_info(void)
+{
+ /* XXX not SMP safe and need to support up to 64 penguins */
+ sprintf(smp_buf,
+" CPU0\t\tCPU1\t\tCPU2\t\tCPU3\n"
+"State: %s\t\t%s\t\t%s\t\t%s\n",
+(cpu_present_map & 1) ? ((klock_info.akp == 0) ? "akp" : "online") : "offline",
+(cpu_present_map & 2) ? ((klock_info.akp == 1) ? "akp" : "online") : "offline",
+(cpu_present_map & 4) ? ((klock_info.akp == 2) ? "akp" : "online") : "offline",
+(cpu_present_map & 8) ? ((klock_info.akp == 3) ? "akp" : "online") : "offline");
+ return smp_buf;
+}
+
+void smp_store_cpu_info(int id)
+{
+ cpu_data[id].udelay_val = loops_per_sec;
+}
+
+void smp_commence(void)
+{
+ local_flush_cache_all();
+ local_flush_tlb_all();
+ smp_commenced = 1;
+ local_flush_cache_all();
+ local_flush_tlb_all();
+}
+
+static void smp_setup_percpu_timer(void);
+
+static volatile unsigned long callin_flag = 0;
+
+void smp_callin(void)
+{
+ int cpuid = hard_smp_processor_id();
+
+ local_flush_cache_all();
+ local_flush_tlb_all();
+
+ smp_setup_percpu_timer();
+
+ calibrate_delay();
+ smp_store_cpu_info(cpuid);
+ callin_flag = 1;
+ __asm__ __volatile__("membar #Sync\n\t"
+ "flush %g6" : : : "memory");
+
+ while(!task[cpuid])
+ barrier();
+ current = task[cpuid];
+
+ while(!smp_commenced)
+ barrier();
+
+ __sti();
+}
+
+extern int cpu_idle(void *unused);
+extern void init_IRQ(void);
+
+void initialize_secondary(void)
+{
+}
+
+int start_secondary(void *unused)
+{
+ trap_init();
+ init_IRQ();
+ smp_callin();
+ return cpu_idle(NULL);
+}
+
+extern struct prom_cpuinfo linux_cpus[NR_CPUS];
+
+void smp_boot_cpus(void)
+{
+ int cpucount = 0, i, first, prev;
+
+ printk("Entering UltraSMPenguin Mode...\n");
+ __sti();
+ cpu_present_map = 0;
+ for(i = 0; i < linux_num_cpus; i++)
+ cpu_present_map |= (1 << i);
+ for(i = 0; i < NR_CPUS; i++) {
+ cpu_number_map[i] = -1;
+ cpu_logical_map[i] = -1;
+ }
+ cpu_number_map[boot_cpu_id] = 0;
+ cpu_logical_map[0] = boot_cpu_id;
+ klock_info.akp = boot_cpu_id;
+ current->processor = boot_cpu_id;
+ smp_store_cpu_info(boot_cpu_id);
+ smp_setup_percpu_timer();
+
+ if(linux_num_cpus == 1)
+ return;
+
+ for(i = 0; i < NR_CPUS; i++) {
+ if(i == boot_cpu_id)
+ continue;
+
+ if(cpu_present_map & (1 << i)) {
+ extern unsigned long sparc64_cpu_startup;
+ unsigned long entry = (unsigned long)&sparc_cpu_startup;
+ struct task_struct *p;
+ int timeout;
+
+ kernel_thread(start_secondary, NULL, CLONE_PID);
+ p = task[++cpucount];
+ p->processor = i;
+ prom_startcpu(linux_cpus[i].prom_node, entry, i);
+ for(timeout = 0; timeout < 5000000; timeout++) {
+ if(cpu_callin_map[i])
+ break;
+ udelay(100);
+ }
+ if(cpu_callin_map[i]) {
+ /* XXX fix this */
+ cpu_number_map[i] = i;
+ cpu_logical_map[i] = i;
+ } else {
+ cpucount--;
+ printk("Processor %d is stuck.\n", i);
+ }
+ }
+ if(!(cpu_callin_map[i])) {
+ cpu_present_map &= ~(1 << i);
+ cpu_number_map[i] = -1;
+ }
+ }
+ if(cpucount == 0) {
+ printk("Error: only one processor found.\n");
+ cpu_present_map = (1 << smp_processor_id());
+ } else {
+ unsigned long bogosum = 0;
+
+ for(i = 0; i < NR_CPUS; i++) {
+ if(cpu_present_map & (1 << i))
+ bogosum += cpu_data[i].udelay_val;
+ }
+ printk("Total of %d processors activated (%lu.%02lu BogoMIPS).\n",
+ cpucount + 1,
+ (bogosum + 2500)/500000,
+ ((bogosum + 2500)/5000)%100);
+ smp_activated = 1;
+ smp_num_cpus = cpucount + 1;
+ }
+ smp_processors_ready = 1;
+}
+
+/* XXX deprecated interface... */
+void smp_message_pass(int target, int msg, unsigned long data, int wait)
+{
+ printk("smp_message_pass() called, this is bad, spinning.\n");
+ __sti();
+ while(1)
+ barrier();
+}
+
+/* XXX Make it fast later. */
+void smp_cross_call(unsigned long *func, u32 ctx, u64 data1, u64 data2)
+{
+ if(smp_processors_ready) {
+ unsigned long mask;
+ u64 data0 = (((unsigned long)ctx)<<32 |
+ (((unsigned long)func) & 0xffffffff));
+ u64 pstate;
+ int i, ncpus = smp_num_cpus;
+
+ __asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate));
+ mask = (cpu_present_map & ~(1 << smp_processor_id()));
+ for(i = 0; i < ncpus; i++) {
+ if(mask & (1 << i)) {
+ u64 target = mid<<14 | 0x70;
+ u64 result;
+
+ __asm__ __volatile__("
+ wrpr %0, %1, %%pstate
+ wrpr %%g0, %2, %%asi
+ stxa %3, [0x40] %%asi
+ stxa %4, [0x50] %%asi
+ stxa %5, [0x60] %%asi
+ stxa %%g0, [%6] %7
+ membar #Sync"
+ : /* No outputs */
+ : "r" (pstate), "i" (PSTATE_IE), "i" (ASI_UDB_INTR_W),
+ "r" (data0), "r" (data1), "r" (data2),
+ "r" (target), "i" (ASI_UDB_INTR_W));
+
+ /* NOTE: PSTATE_IE is still clear. */
+ do {
+ __asm__ __volatile__("ldxa [%%g0] %1, %0",
+ : "=r" (result)
+ : "i" (ASI_INTR_DISPATCH_STAT));
+ } while(result & 0x1);
+ __asm__ __volatile__("wrpr %0, 0x0, %%pstate"
+ : : "r" (pstate));
+ if(result & 0x2)
+ panic("Penguin NACK's master!");
+ }
+ }
+
+ /* NOTE: Caller runs local copy on master. */
+ }
+}
+
+extern unsigned long xcall_flush_tlb_page;
+extern unsigned long xcall_flush_tlb_mm;
+extern unsigned long xcall_flush_tlb_range;
+extern unsigned long xcall_flush_tlb_all;
+extern unsigned long xcall_flush_cache_all;
+
+void smp_flush_cache_all(void)
+{
+ smp_cross_call(&xcall_flush_cache_all, 0, 0, 0);
+}
+
+void smp_flush_tlb_all(void)
+{
+ smp_cross_call(&xcall_flush_tlb_all, 0, 0, 0);
+}
+
+void smp_flush_tlb_mm(struct mm_struct *mm)
+{
+ u32 ctx = mm->context & 0x1fff;
+ if(mm->cpu_vm_mask != (1 << smp_processor_id()))
+ smp_cross_call(&xcall_flush_tlb_mm, ctx, 0, 0);
+ __flush_tlb_mm(ctx);
+}
+
+void smp_flush_tlb_range(struct mm_struct *mm, unsigned long start,
+ unsigned long end)
+{
+ u32 ctx = mm->context & 0x1fff;
+ if(mm->cpu_vm_mask != (1 << smp_processor_id()))
+ smp_cross_call(&xcall_flush_tlb_range, ctx, start, end);
+ __flush_tlb_range(ctx, start, end);
+}
+
+void smp_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
+{
+ struct mm_struct *mm = vma->vm_mm;
+ u32 ctx = mm->context & 0x1fff;
+
+ if(mm->cpu_vm_mask != (1 << smp_processor_id()))
+ smp_cross_call(&xcall_flush_tlb_page, ctx, page, 0);
+ __flush_tlb_page(ctx, page);
+}
+
+static spinlock_t ticker_lock = SPIN_LOCK_UNLOCKED;
+
+static inline void sparc64_do_profile(unsigned long pc)
+{
+ if(prof_buffer && current->pid) {
+ extern int _stext;
+
+ pc -= (unsigned long) &_stext;
+ pc >>= prof_shift;
+
+ spin_lock(&ticker_lock);
+ if(pc < prof_len)
+ prof_buffer[pc]++;
+ else
+ prof_buffer[prof_len - 1]++;
+ spin_unlock(&ticker_lock);
+ }
+}
+
+unsigned int prof_multiplier[NR_CPUS];
+unsigned int prof_counter[NR_CPUS];
+
+extern void update_one_process(struct task_struct *p, unsigned long ticks,
+ unsigned long user, unsigned long system);
+
+void smp_percpu_timer_interrupt(struct pt_regs *regs)
+{
+ int cpu = smp_processor_id();
+
+ clear_profile_irq(cpu);
+ if(!user_mode(regs))
+ sparc_do_profile(regs->pc);
+ if(!--prof_counter[cpu]) {
+ int user = user_mode(regs);
+ if(current->pid) {
+ update_one_process(current, 1, user, !user);
+ if(--current->counter < 0) {
+ current->counter = 0;
+ need_resched = 1;
+ }
+
+ spin_lock(&ticker_lock);
+ if(user) {
+ if(current->priority < DEF_PRIORITY)
+ kstat.cpu_nice++;
+ else
+ kstat.cpu_user++;
+ } else {
+ kstat.cpu_system++;
+ }
+ spin_unlock(&ticker_lock);
+ }
+ prof_counter[cpu] = prof_multiplier[cpu];
+ }
+}
+
+static void smp_setup_percpu_timer(void)
+{
+ /* XXX implement me */
+}
+
+int setup_profiling_timer(unsigned int multiplier)
+{
+ /* XXX implement me */
+}
diff --git a/arch/sparc64/kernel/sparc64_ksyms.c b/arch/sparc64/kernel/sparc64_ksyms.c
index 91426c814..990202bac 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.11 1997/07/14 23:58:20 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)
@@ -18,6 +18,8 @@
#include <asm/pgtable.h>
#include <asm/io.h>
#include <asm/irq.h>
+#include <asm/softirq.h>
+#include <asm/hardirq.h>
#include <asm/idprom.h>
#include <asm/svr4.h>
#include <asm/head.h>
@@ -38,19 +40,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,32 +75,24 @@ 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);
EXPORT_SYMBOL(sparc_free_io);
-#if 0
-EXPORT_SYMBOL(io_remap_page_range);
-EXPORT_SYMBOL(mmu_unlockarea);
-EXPORT_SYMBOL(mmu_lockarea);
+EXPORT_SYMBOL(local_irq_count);
+EXPORT_SYMBOL(__sparc64_bh_counter);
+EXPORT_SYMBOL(sparc_ultra_unmapioaddr);
EXPORT_SYMBOL(mmu_get_scsi_sgl);
EXPORT_SYMBOL(mmu_get_scsi_one);
-EXPORT_SYMBOL(mmu_release_scsi_sgl);
-EXPORT_SYMBOL(mmu_release_scsi_one);
-#endif
EXPORT_SYMBOL(sparc_dvma_malloc);
-#if 0
-EXPORT_SYMBOL(sun4c_unmapioaddr);
-EXPORT_SYMBOL(srmmu_unmapioaddr);
-#endif
#if CONFIG_SBUS
EXPORT_SYMBOL(SBus_chain);
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 +111,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 +140,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 000000000..311110d3c
--- /dev/null
+++ b/arch/sparc64/kernel/sunos_ioctl32.c
@@ -0,0 +1,281 @@
+/* $Id: sunos_ioctl32.c,v 1.1 1997/07/18 06:26:42 ralf 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 <asm/uaccess.h>
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/termios.h>
+#include <linux/ioctl.h>
+#include <linux/route.h>
+#include <linux/sockios.h>
+#include <linux/if.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <asm/kbio.h>
+
+#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, u32 cmd, u32 arg)
+{
+ struct file *filp;
+ int ret = -EBADF;
+
+ lock_kernel();
+ if(fd >= SUNOS_NR_OPEN)
+ goto out;
+
+ filp = current->files->fd[fd];
+ if(!filp)
+ 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 000000000..ddd726069
--- /dev/null
+++ b/arch/sparc64/kernel/sys32.S
@@ -0,0 +1,427 @@
+/* $Id: sys32.S,v 1.1 1997/07/18 06:26:42 ralf 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 851d1550c..c827df7a1 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 <linux/errno.h>
#include <linux/types.h>
#include <linux/sched.h>
-#include <linux/config.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/sem.h>
diff --git a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc64/kernel/sys_sparc32.c
index 59815b7a8..1f607da98 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.26 1997/06/04 13:05:21 jj Exp $
+/* $Id: sys_sparc32.c,v 1.43 1997/07/17 02:20:45 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 <linux/config.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/signal.h>
@@ -30,11 +31,13 @@
#include <linux/ncp_fs.h>
#include <linux/quota.h>
#include <linux/file.h>
+#include <linux/module.h>
#include <asm/types.h>
#include <asm/poll.h>
#include <asm/ipc.h>
#include <asm/uaccess.h>
+#include <asm/fpumacro.h>
/* 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);
@@ -934,14 +759,29 @@ static long do_readv_writev32(int type, struct inode *inode, struct file *file,
asmlinkage long sys32_readv(int fd, u32 vector, u32 count)
{
struct file *file;
+ struct dentry *dentry;
struct inode *inode;
long err = -EBADF;
lock_kernel();
- if (fd >= NR_OPEN || !(file = current->files->fd[fd]) || !(inode=file->f_inode))
+ if(fd >= NR_OPEN)
+ goto out;
+
+ file = current->files->fd[fd];
+ if(!file)
+ goto out;
+
+ if(!(file->f_mode & 1))
+ goto out;
+
+ dentry = file->f_dentry;
+ if(!dentry)
goto out;
- if (!(file->f_mode & 1))
+
+ inode = dentry->d_inode;
+ if(!inode)
goto out;
+
err = do_readv_writev32(VERIFY_WRITE, inode, file,
(struct iovec32 *)A(vector), count);
out:
@@ -953,13 +793,28 @@ asmlinkage long sys32_writev(int fd, u32 vector, u32 count)
{
int error = -EBADF;
struct file *file;
+ struct dentry *dentry;
struct inode *inode;
lock_kernel();
- if (fd >= NR_OPEN || !(file = current->files->fd[fd]) || !(inode=file->f_inode))
+ if(fd >= NR_OPEN)
+ goto out;
+
+ file = current->files->fd[fd];
+ if(!file)
+ goto out;
+
+ if(!(file->f_mode & 2))
+ goto out;
+
+ dentry = file->f_dentry;
+ if(!dentry)
goto out;
- if (!(file->f_mode & 2))
+
+ inode = dentry->d_inode;
+ if(!inode)
goto out;
+
down(&inode->i_sem);
error = do_readv_writev32(VERIFY_READ, inode, file,
(struct iovec32 *)A(vector), count);
@@ -1008,21 +863,34 @@ asmlinkage int old32_readdir(unsigned int fd, u32 dirent, unsigned int count)
{
int error = -EBADF;
struct file * file;
+ struct dentry * dentry;
+ struct inode * inode;
struct readdir_callback32 buf;
lock_kernel();
- if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
+ if(fd >= NR_OPEN)
goto out;
- error = -ENOTDIR;
- if (!file->f_op || !file->f_op->readdir)
+
+ file = current->files->fd[fd];
+ if(!file)
goto out;
- error = verify_area(VERIFY_WRITE, (void *)A(dirent),
- sizeof(struct old_linux_dirent32));
- if (error)
+
+ dentry = file->f_dentry;
+ if(!dentry)
+ goto out;
+
+ inode = dentry->d_inode;
+ if(!inode)
goto out;
+
buf.count = 0;
buf.dirent = (struct old_linux_dirent32 *)A(dirent);
- error = file->f_op->readdir(file->f_inode, file, &buf, fillonedir);
+
+ error = -ENOTDIR;
+ if (!file->f_op || !file->f_op->readdir)
+ goto out;
+
+ error = file->f_op->readdir(inode, file, &buf, fillonedir);
if (error < 0)
goto out;
error = buf.count;
@@ -1072,30 +940,43 @@ static int filldir(void * __buf, const char * name, int namlen, off_t offset, in
asmlinkage int sys32_getdents(unsigned int fd, u32 dirent, unsigned int count)
{
struct file * file;
+ struct dentry * dentry;
+ struct inode *inode;
struct linux_dirent32 * lastdirent;
struct getdents_callback32 buf;
int error = -EBADF;
lock_kernel();
- if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
+ if(fd >= NR_OPEN)
goto out;
- error = -ENOTDIR;
- if (!file->f_op || !file->f_op->readdir)
+
+ file = current->files->fd[fd];
+ if(!file)
+ goto out;
+
+ dentry = file->f_dentry;
+ if(!dentry)
goto out;
- error = verify_area(VERIFY_WRITE, (void *)A(dirent), count);
- if (error)
+
+ inode = dentry->d_inode;
+ if(!inode)
goto out;
+
buf.current_dir = (struct linux_dirent32 *) A(dirent);
buf.previous = NULL;
buf.count = count;
buf.error = 0;
- error = file->f_op->readdir(file->f_inode, file, &buf, filldir);
+
+ error = -ENOTDIR;
+ if (!file->f_op || !file->f_op->readdir)
+ goto out;
+
+ error = file->f_op->readdir(inode, file, &buf, filldir);
if (error < 0)
goto out;
lastdirent = buf.previous;
- if (!lastdirent) {
- error = buf.error;
- } else {
+ error = buf.error;
+ if(lastdirent) {
put_user(file->f_pos, &lastdirent->d_off);
error = count - buf.count;
}
@@ -1196,13 +1077,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 +1154,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,42 +1179,162 @@ asmlinkage int sys32_sysfs(int option, ...)
return ret;
}
-extern asmlinkage int sys_ustat(dev_t dev, struct ustat * ubuf);
+struct ncp_mount_data32 {
+ int version;
+ unsigned int ncp_fd;
+ __kernel_uid_t32 mounted_uid;
+ __kernel_pid_t32 wdog_pid;
+ unsigned char mounted_vol[NCP_VOLNAME_LEN + 1];
+ unsigned int time_out;
+ unsigned int retry_count;
+ unsigned int flags;
+ __kernel_uid_t32 uid;
+ __kernel_gid_t32 gid;
+ __kernel_mode_t32 file_mode;
+ __kernel_mode_t32 dir_mode;
+};
-asmlinkage int sys32_ustat(dev_t dev, u32 ubuf)
+static void *do_ncp_super_data_conv(void *raw_data)
{
- /* ustat is the same :)) */
- return sys_ustat(dev, (struct ustat *)A(ubuf));
+ struct ncp_mount_data *n = (struct ncp_mount_data *)raw_data;
+ struct ncp_mount_data32 *n32 = (struct ncp_mount_data32 *)raw_data;
+
+ n->dir_mode = n32->dir_mode;
+ n->file_mode = n32->file_mode;
+ n->gid = n32->gid;
+ n->uid = n32->uid;
+ memmove (n->mounted_vol, n32->mounted_vol, (sizeof (n32->mounted_vol) + 3 * sizeof (unsigned int)));
+ n->wdog_pid = n32->wdog_pid;
+ n->mounted_uid = n32->mounted_uid;
+ return raw_data;
}
-extern asmlinkage int sys_umount(char * name);
+struct smb_mount_data32 {
+ int version;
+ unsigned int fd;
+ __kernel_uid_t32 mounted_uid;
+ struct sockaddr_in addr;
+ char server_name[17];
+ char client_name[17];
+ char service[64];
+ char root_path[64];
+ char username[64];
+ char password[64];
+ char domain[64];
+ unsigned short max_xmit;
+ __kernel_uid_t32 uid;
+ __kernel_gid_t32 gid;
+ __kernel_mode_t32 file_mode;
+ __kernel_mode_t32 dir_mode;
+};
-asmlinkage int sys32_umount(u32 name)
+static void *do_smb_super_data_conv(void *raw_data)
{
- return sys_umount((char *)A(name));
-}
+ struct smb_mount_data *s = (struct smb_mount_data *)raw_data;
+ struct smb_mount_data32 *s32 = (struct smb_mount_data32 *)raw_data;
-extern asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type,
- unsigned long new_flags, void *data);
+ s->dir_mode = s32->dir_mode;
+ s->file_mode = s32->file_mode;
+ s->gid = s32->gid;
+ s->uid = s32->uid;
+ memmove (&s->addr, &s32->addr, (((long)&s->uid) - ((long)&s->addr)));
+ s->mounted_uid = s32->mounted_uid;
+ return raw_data;
+}
-asmlinkage int sys32_mount(u32 dev_name, u32 dir_name, u32 type, u32 new_flags, u32 data)
+static int copy_mount_stuff_to_kernel(const void *user, unsigned long *kernel)
{
- return sys_mount((char *)A(dev_name), (char *)A(dir_name), (char *)A(type),
- (unsigned long)new_flags, (void *)A(data));
+ int i;
+ unsigned long page;
+ struct vm_area_struct *vma;
+
+ *kernel = 0;
+ if(!user)
+ return 0;
+ vma = find_vma(current->mm, (unsigned long)user);
+ if(!vma || (unsigned long)user < vma->vm_start)
+ return -EFAULT;
+ if(!(vma->vm_flags & VM_READ))
+ return -EFAULT;
+ i = vma->vm_end - (unsigned long) user;
+ if(PAGE_SIZE <= (unsigned long) i)
+ i = PAGE_SIZE - 1;
+ if(!(page = __get_free_page(GFP_KERNEL)))
+ return -ENOMEM;
+ if(copy_from_user((void *) page, user, i)) {
+ free_page(page);
+ return -EFAULT;
+ }
+ *kernel = page;
+ return 0;
}
-extern asmlinkage int sys_syslog(int type, char * bug, int count);
+extern asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type,
+ unsigned long new_flags, void *data);
+
+#define SMBFS_NAME "smbfs"
+#define NCPFS_NAME "ncpfs"
-asmlinkage int sys32_syslog(int type, u32 bug, int count)
+asmlinkage int sys32_mount(u32 dev_name, u32 dir_name, u32 type, u32 new_flags, u32 data)
{
- return sys_syslog(type, (char *)A(bug), count);
-}
+ unsigned long type_page;
+ int err, is_smb, is_ncp;
-extern asmlinkage int sys_personality(unsigned long personality);
+ if(!suser())
+ return -EPERM;
+ is_smb = is_ncp = 0;
+ err = copy_mount_stuff_to_kernel((const void *)A(type), &type_page);
+ if(err)
+ return err;
+ if(type_page) {
+ is_smb = !strcmp((char *)type_page, SMBFS_NAME);
+ is_ncp = !strcmp((char *)type_page, NCPFS_NAME);
+ }
+ if(!is_smb && !is_ncp) {
+ if(type_page)
+ free_page(type_page);
+ return sys_mount((char *)A(dev_name), (char *)A(dir_name),
+ (char *)A(type), (unsigned long)new_flags,
+ (void *)A(data));
+ } else {
+ unsigned long dev_page, dir_page, data_page;
+ int old_fs;
-asmlinkage int sys32_personality(u32 personality)
-{
- return sys_personality((unsigned long)personality);
+ err = copy_mount_stuff_to_kernel((const void *)A(dev_name), &dev_page);
+ if(err)
+ goto out;
+ err = copy_mount_stuff_to_kernel((const void *)A(dir_name), &dir_page);
+ if(err)
+ goto dev_out;
+ err = copy_mount_stuff_to_kernel((const void *)A(data), &data_page);
+ if(err)
+ goto dir_out;
+ if(is_ncp)
+ do_ncp_super_data_conv((void *)data_page);
+ else if(is_smb)
+ do_smb_super_data_conv((void *)data_page);
+ else
+ panic("Tell DaveM he fucked up...");
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ err = sys_mount((char *)dev_page, (char *)dir_page,
+ (char *)type_page, (unsigned long)new_flags,
+ (void *)data_page);
+ set_fs(old_fs);
+
+ if(data_page)
+ free_page(data_page);
+ dir_out:
+ if(dir_page)
+ free_page(dir_page);
+ dev_out:
+ if(dev_page)
+ free_page(dev_page);
+ out:
+ if(type_page)
+ free_page(type_page);
+ return err;
+ }
}
struct rusage32 {
@@ -1416,13 +1403,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];
@@ -1462,46 +1442,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;
@@ -1577,25 +1517,29 @@ asmlinkage int sys32_sigpending(u32 set)
return ret;
}
-extern asmlinkage unsigned long sys_signal(int signum, __sighandler_t handler);
+extern asmlinkage int sys_setreuid(uid_t ruid, uid_t euid);
-asmlinkage unsigned long sys32_signal(int signum, u32 handler)
+asmlinkage int sys32_setreuid(__kernel_uid_t32 ruid, __kernel_uid_t32 euid)
{
- return sys_signal(signum, (__sighandler_t)A(handler));
-}
-
-extern asmlinkage int sys_reboot(int magic1, int magic2, int cmd, void * arg);
+ uid_t sruid, seuid;
-asmlinkage int sys32_reboot(int magic1, int magic2, int cmd, u32 arg)
-{
- return sys_reboot(magic1, magic2, cmd, (void *)A(arg));
+ sruid = (ruid == (__kernel_uid_t32)-1) ? ((uid_t)-1) : ((uid_t)ruid);
+ seuid = (euid == (__kernel_uid_t32)-1) ? ((uid_t)-1) : ((uid_t)euid);
+ return sys_setreuid(sruid, seuid);
}
-extern asmlinkage int sys_acct(const char *name);
+extern asmlinkage int sys_setresuid(uid_t ruid, uid_t euid, uid_t suid);
-asmlinkage int sys32_acct(u32 name)
+asmlinkage int sys32_setresuid(__kernel_uid_t32 ruid,
+ __kernel_uid_t32 euid,
+ __kernel_uid_t32 suid)
{
- return sys_acct((const char *)A(name));
+ uid_t sruid, seuid, ssuid;
+
+ sruid = (ruid == (__kernel_uid_t32)-1) ? ((uid_t)-1) : ((uid_t)ruid);
+ seuid = (euid == (__kernel_uid_t32)-1) ? ((uid_t)-1) : ((uid_t)euid);
+ ssuid = (suid == (__kernel_uid_t32)-1) ? ((uid_t)-1) : ((uid_t)suid);
+ return sys_setresuid(sruid, seuid, ssuid);
}
extern asmlinkage int sys_getresuid(uid_t *ruid, uid_t *euid, uid_t *suid);
@@ -1654,7 +1598,7 @@ asmlinkage int sys32_getgroups(int gidsetsize, u32 grouplist)
set_fs (KERNEL_DS);
ret = sys_getgroups(gidsetsize, gl);
set_fs (old_fs);
- if (ret > 0 && ret <= NGROUPS)
+ if (gidsetsize && ret > 0 && ret <= NGROUPS)
for (i = 0; i < ret; i++, grouplist += sizeof(__kernel_gid_t32))
if (__put_user (gl[i], (__kernel_gid_t32 *)A(grouplist)))
return -EFAULT;
@@ -1680,41 +1624,8 @@ 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)
struct rlimit32 {
s32 rlim_cur;
@@ -1733,8 +1644,8 @@ asmlinkage int sys32_getrlimit(unsigned int resource, u32 rlim)
ret = sys_getrlimit(resource, &r);
set_fs (old_fs);
if (!ret && (
- put_user (r.rlim_cur, &(((struct rlimit32 *)A(rlim))->rlim_cur)) ||
- __put_user (r.rlim_max, &(((struct rlimit32 *)A(rlim))->rlim_max))))
+ put_user (RESOURCE32(r.rlim_cur), &(((struct rlimit32 *)A(rlim))->rlim_cur)) ||
+ __put_user (RESOURCE32(r.rlim_max), &(((struct rlimit32 *)A(rlim))->rlim_max))))
return -EFAULT;
return ret;
}
@@ -1751,6 +1662,10 @@ asmlinkage int sys32_setrlimit(unsigned int resource, u32 rlim)
if (get_user (r.rlim_cur, &(((struct rlimit32 *)A(rlim))->rlim_cur)) ||
__get_user (r.rlim_max, &(((struct rlimit32 *)A(rlim))->rlim_max)))
return -EFAULT;
+ if (r.rlim_cur == RLIM_INFINITY32)
+ r.rlim_cur = RLIM_INFINITY;
+ if (r.rlim_max == RLIM_INFINITY32)
+ r.rlim_max = RLIM_INFINITY;
set_fs (KERNEL_DS);
ret = sys_setrlimit(resource, &r);
set_fs (old_fs);
@@ -1772,28 +1687,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;
@@ -1865,170 +1758,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,
@@ -2052,11 +1781,11 @@ extern __inline__ struct socket *sockfd_lookup(int fd, int *err)
return NULL;
}
- inode = file->f_inode;
+ inode = file->f_dentry->d_inode;
if (!inode || !inode->i_sock || !socki_lookup(inode))
{
*err = -ENOTSOCK;
- fput(file,inode);
+ fput(file);
return NULL;
}
@@ -2065,7 +1794,7 @@ extern __inline__ struct socket *sockfd_lookup(int fd, int *err)
extern __inline__ void sockfd_put(struct socket *sock)
{
- fput(sock->file,sock->inode);
+ fput(sock->file);
}
struct msghdr32 {
@@ -2293,6 +2022,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]);
@@ -2389,7 +2136,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;
}
@@ -2407,14 +2154,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
*/
@@ -2485,25 +2224,33 @@ static inline int
do_execve32(char * filename, u32 * argv, u32 * envp, struct pt_regs * regs)
{
struct linux_binprm bprm;
+ struct dentry * dentry;
int retval;
int i;
bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *);
for (i=0 ; i<MAX_ARG_PAGES ; i++) /* clear page-table */
bprm.page[i] = 0;
- retval = open_namei(filename, 0, 0, &bprm.inode, NULL);
- if (retval)
+
+ dentry = open_namei(filename, 0, 0);
+ retval = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
return retval;
+
+ bprm.dentry = dentry;
bprm.filename = filename;
bprm.sh_bang = 0;
bprm.java = 0;
bprm.loader = 0;
bprm.exec = 0;
- bprm.dont_iput = 0;
- if ((bprm.argc = count32(argv)) < 0)
+ if ((bprm.argc = count32(argv)) < 0) {
+ dput(dentry);
return bprm.argc;
- if ((bprm.envc = count32(envp)) < 0)
+ }
+ if ((bprm.envc = count32(envp)) < 0) {
+ dput(dentry);
return bprm.envc;
+ }
retval = prepare_binprm(&bprm);
@@ -2523,8 +2270,9 @@ do_execve32(char * filename, u32 * argv, u32 * envp, struct pt_regs * regs)
return retval;
/* Something went wrong, return the inode and free the argument pages*/
- if(!bprm.dont_iput)
- iput(bprm.inode);
+ if(bprm.dentry)
+ dput(bprm.dentry);
+
for (i=0 ; i<MAX_ARG_PAGES ; i++)
free_page(bprm.page[i]);
return(retval);
@@ -2543,81 +2291,231 @@ asmlinkage int sparc32_execve(struct pt_regs *regs)
if((u32)regs->u_regs[UREG_G1] == 0)
base = 1;
- error = getname((char *)(unsigned long)(u32)regs->u_regs[base + UREG_I0], &filename);
- if(error)
- return error;
+ lock_kernel();
+ filename = getname((char *)(unsigned long)(u32)regs->u_regs[base + UREG_I0]);
+ error = PTR_ERR(filename);
+ if(IS_ERR(filename))
+ goto out;
error = do_execve32(filename,
(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;
+ }
+out:
+ unlock_kernel();
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 -ENOSYS;
+ return sys_create_module((const char *)A(name_user), (size_t)size);
}
-struct ncp_mount_data32 {
- int version;
- unsigned int ncp_fd;
- __kernel_uid_t32 mounted_uid;
- __kernel_pid_t32 wdog_pid;
- unsigned char mounted_vol[NCP_VOLNAME_LEN + 1];
- unsigned int time_out;
- unsigned int retry_count;
- unsigned int flags;
- __kernel_uid_t32 uid;
- __kernel_gid_t32 gid;
- __kernel_mode_t32 file_mode;
- __kernel_mode_t32 dir_mode;
-};
+extern asmlinkage int sys_init_module(const char *name_user, struct module *mod_user);
-void *do_ncp_super_data_conv(void *raw_data)
+/* 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)
{
- struct ncp_mount_data *n = (struct ncp_mount_data *)raw_data;
- struct ncp_mount_data32 *n32 = (struct ncp_mount_data32 *)raw_data;
+ return sys_init_module((const char *)A(nameuser), (struct module *)A(mod_user));
+}
- n->dir_mode = n32->dir_mode;
- n->file_mode = n32->file_mode;
- n->gid = n32->gid;
- n->uid = n32->uid;
- memmove (n->mounted_vol, n32->mounted_vol, (sizeof (n32->mounted_vol) + 3 * sizeof (unsigned int)));
- n->wdog_pid = n32->wdog_pid;
- n->mounted_uid = n32->mounted_uid;
- return raw_data;
+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 smb_mount_data32 {
- int version;
- unsigned int fd;
- __kernel_uid_t32 mounted_uid;
- struct sockaddr_in addr;
- char server_name[17];
- char client_name[17];
- char service[64];
- char root_path[64];
- char username[64];
- char password[64];
- char domain[64];
- unsigned short max_xmit;
- __kernel_uid_t32 uid;
- __kernel_gid_t32 gid;
- __kernel_mode_t32 file_mode;
- __kernel_mode_t32 dir_mode;
+struct module_info32 {
+ u32 addr;
+ u32 size;
+ u32 flags;
+ s32 usecount;
};
-void *do_smb_super_data_conv(void *raw_data)
+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)
{
- struct smb_mount_data *s = (struct smb_mount_data *)raw_data;
- struct smb_mount_data32 *s32 = (struct smb_mount_data32 *)raw_data;
+ 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;
+ }
+}
- s->dir_mode = s32->dir_mode;
- s->file_mode = s32->file_mode;
- s->gid = s32->gid;
- s->uid = s32->uid;
- memmove (&s->addr, &s32->addr, (((long)&s->uid) - ((long)&s->addr)));
- s->mounted_uid = s32->mounted_uid;
- return raw_data;
+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 000000000..3dfdad5a7
--- /dev/null
+++ b/arch/sparc64/kernel/sys_sunos32.c
@@ -0,0 +1,1511 @@
+/* $Id: sys_sunos32.c,v 1.1 1997/07/18 06:26:43 ralf 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 <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/mman.h>
+#include <linux/mm.h>
+#include <linux/swap.h>
+#include <linux/fs.h>
+#include <linux/resource.h>
+#include <linux/ipc.h>
+#include <linux/shm.h>
+#include <linux/msg.h>
+#include <linux/sem.h>
+#include <linux/signal.h>
+#include <linux/uio.h>
+#include <linux/utsname.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <linux/stat.h>
+#include <linux/malloc.h>
+#include <linux/pagemap.h>
+#include <linux/errno.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+
+#include <asm/uaccess.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/pconf.h>
+#include <asm/idprom.h> /* for gethostid() */
+#include <asm/unistd.h>
+#include <asm/system.h>
+
+/* For the nfs mount emulation */
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/nfs.h>
+#include <linux/nfs_mount.h>
+
+/* for sunos_select */
+#include <linux/time.h>
+#include <linux/personality.h>
+
+#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(file->f_dentry && file->f_dentry->d_inode) {
+ if(MAJOR(file->f_dentry->d_inode->i_rdev) == MEM_MAJOR &&
+ MINOR(file->f_dentry->d_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 dentry * dentry;
+ struct inode * inode;
+ 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)
+ goto out;
+
+ file = current->files->fd[fd];
+ if(!file)
+ goto out;
+
+ dentry = file->f_dentry;
+ if(!dentry)
+ goto out;
+
+ inode = dentry->d_inode;
+ if(!inode)
+ 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(inode, file, &buf, sunos_filldir);
+ if (error < 0)
+ goto out;
+ lastdirent = buf.previous;
+ error = buf.error;
+ if (lastdirent) {
+ 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 dentry * dentry;
+ struct inode * inode;
+ 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)
+ goto out;
+
+ file = current->files->fd[fd];
+ if(!file)
+ goto out;
+
+ dentry = file->f_dentry;
+ if(!dentry)
+ goto out;
+
+ inode = dentry->d_inode;
+ if(!inode)
+ 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(inode, file, &buf, sunos_filldirentry);
+ if (error < 0)
+ goto out;
+ lastdirent = buf.previous;
+ error = buf.error;
+ if (lastdirent) {
+ 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 dentry *dentry;
+ struct inode *inode;
+ struct file *file;
+
+ file = current->files->fd [fd];
+ if(!file)
+ return 0;
+
+ dentry = file->f_dentry;
+ if(!dentry)
+ return 0;
+
+ inode = dentry->d_inode;
+ if(!inode)
+ 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;
+
+ the_name = getname(sunos_mount->hostname);
+ if(IS_ERR(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(&current->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(&current->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 a74d0ffbd..eda0ff326 100644
--- a/arch/sparc64/kernel/systbls.S
+++ b/arch/sparc64/kernel/systbls.S
@@ -1,4 +1,4 @@
-/* $Id: systbls.S,v 1.13 1997/06/04 13:05:29 jj 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.
*
@@ -30,7 +30,7 @@ sys_call_table32:
/*50*/ .xword sys_getegid, sys32_acct, sys_nis_syscall, sys_nis_syscall, sys32_ioctl
.xword sys32_reboot, sys_nis_syscall, sys32_symlink, sys32_readlink, sys32_execve
/*60*/ .xword sys_umask, sys32_chroot, sys32_newfstat, sys_nis_syscall, sys_getpagesize
- .xword sys_nis_syscall, sys_vfork, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
+ .xword sys32_msync, sys_vfork, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
/*70*/ .xword sys_nis_syscall, sys32_mmap, sys_nis_syscall, sys32_munmap, sys32_mprotect
.xword sys_nis_syscall, sys_vhangup, sys_nis_syscall, sys_nis_syscall, sys32_getgroups
/*80*/ .xword sys32_setgroups, sys_getpgrp, sys_nis_syscall, sys32_setitimer, sys_nis_syscall
@@ -42,7 +42,7 @@ sys_call_table32:
/*110*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
.xword sys_nis_syscall, sys32_gettimeofday, sys32_getrusage, sys_nis_syscall, sys_nis_syscall
/*120*/ .xword sys32_readv, sys32_writev, sys32_settimeofday, sys_fchown, sys_fchmod
- .xword sys_nis_syscall, sys_setreuid, sys_setregid, sys32_rename, sys32_truncate
+ .xword sys_nis_syscall, sys32_setreuid, sys_setregid, sys32_rename, sys32_truncate
/*130*/ .xword sys32_ftruncate, sys_flock, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
.xword sys_nis_syscall, sys32_mkdir, sys32_rmdir, sys_nis_syscall, sys_nis_syscall
/*140*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_getrlimit
@@ -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
@@ -94,29 +94,29 @@ sys_call_table:
/*80*/ .xword sys_setgroups, sys_getpgrp, sys_nis_syscall, sys_setitimer, sys_nis_syscall
.xword sys_swapon, sys_getitimer, sys_nis_syscall, sys_sethostname, sys_nis_syscall
/*90*/ .xword sys_dup2, sys_nis_syscall, sys_fcntl, sys_select, sys_nis_syscall
- .xword sys_fsync, sys_setpriority, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
-/*100*/ .xword sys_getpriority, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
- .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
-/*110*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
- .xword sys_nis_syscall, sys_gettimeofday, sys_getrusage, sys_nis_syscall, sys_nis_syscall
+ .xword sys_fsync, sys_setpriority, sys_socket, sys_connect, sys_accept
+/*100*/ .xword sys_getpriority, sys_send, sys_recv, sys_nis_syscall, sys_bind
+ .xword sys_setsockopt, sys_listen, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
+/*110*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_recvmsg, sys_sendmsg
+ .xword sys_nis_syscall, sys_gettimeofday, sys_getrusage, sys_getsockopt, sys_nis_syscall
/*120*/ .xword sys_readv, sys_writev, sys_settimeofday, sys_fchown, sys_fchmod
- .xword sys_nis_syscall, sys_setreuid, sys_setregid, sys_rename, sys_truncate
-/*130*/ .xword sys_ftruncate, sys_flock, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
- .xword sys_nis_syscall, sys_mkdir, sys_rmdir, sys_nis_syscall, sys_nis_syscall
-/*140*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_getrlimit
+ .xword sys_recvfrom, sys_setreuid, sys_setregid, sys_rename, sys_truncate
+/*130*/ .xword sys_ftruncate, sys_flock, sys_nis_syscall, sys_sendto, sys_shutdown
+ .xword sys_socketpair, sys_mkdir, sys_rmdir, sys_nis_syscall, sys_nis_syscall
+/*140*/ .xword sys_nis_syscall, sys_getpeername, sys_nis_syscall, sys_nis_syscall, sys_getrlimit
.xword sys_setrlimit, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
-/*150*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
+/*150*/ .xword sys_getsockname, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
.xword sys_nis_syscall, sys_nis_syscall, sys_statfs, sys_fstatfs, sys_umount
/*160*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_setdomainname, sys_nis_syscall
.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
/*200*/ .xword sys_ssetmask, sys_sigsuspend, sys_newlstat, sys_uselib, sys_nis_syscall
- .xword sys_nis_syscall, sys_socketcall, sys_syslog, sys_nis_syscall, sys_nis_syscall
+ .xword sys_nis_syscall, sys_nis_syscall, sys_syslog, sys_nis_syscall, sys_nis_syscall
/*210*/ .xword sys_idle, sys_nis_syscall, sys_waitpid, sys_swapoff, sys_sysinfo
.xword sys_ipc, sys_sigreturn, sys_clone, sys_nis_syscall, sys_adjtimex
/*220*/ .xword sys_sigprocmask, sys_create_module, sys_delete_module, sys_get_kernel_syms, sys_getpgid
@@ -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 3f15fcb54..ad40a5fb5 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 824a3ddb4..ac3e79958 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 <linux/config.h>
#include <linux/sched.h> /* for jiffies */
#include <linux/kernel.h>
#include <linux/signal.h>
@@ -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(&current->tss.float_regs[0],
- &current->tss.fsr);
- else
- fpload(&current->tss.float_regs[0],
- &current->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(&current->tss.float_regs[0],
- &current->tss.fsr);
- else
- fpload(&current->tss.float_regs[0],
- &current->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 8db708f07..73bda96d9 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 <linux/config.h>
+
.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
new file mode 100644
index 000000000..f66889195
--- /dev/null
+++ b/arch/sparc64/kernel/unaligned.c
@@ -0,0 +1,517 @@
+/* $Id: unaligned.c,v 1.1 1997/07/18 06:26:45 ralf Exp $
+ * unaligned.c: Unaligned load/store trap handling with special
+ * cases for the kernel to do them more quickly.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <asm/asi.h>
+#include <asm/ptrace.h>
+#include <asm/processor.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+
+/* #define DEBUG_MNA */
+
+enum direction {
+ load, /* ld, ldd, ldh, ldsh */
+ store, /* st, std, sth, stsh */
+ both, /* Swap, ldstub, cas, ... */
+ fpload,
+ fpstore,
+ invalid,
+};
+
+#ifdef DEBUG_MNA
+static char *dirstrings[] = {
+ "load", "store", "both", "fpload", "fpstore", "invalid"
+};
+#endif
+
+static inline enum direction decode_direction(unsigned int insn)
+{
+ unsigned long tmp = (insn >> 21) & 1;
+
+ if(!tmp)
+ return load;
+ else {
+ switch ((insn>>19)&0xf) {
+ case 15: /* swap* */
+ return both;
+ default:
+ return store;
+ }
+ }
+}
+
+/* 16 = double-word, 8 = extra-word, 4 = word, 2 = half-word */
+static inline int decode_access_size(unsigned int insn)
+{
+ unsigned int tmp;
+
+ if (((insn >> 19) & 0xf) == 14)
+ return 8; /* stx* */
+ tmp = (insn >> 19) & 3;
+ if(!tmp)
+ return 4;
+ else if(tmp == 3)
+ return 16; /* ldd/std - Although it is actually 8 */
+ else if(tmp == 2)
+ return 2;
+ else {
+ printk("Impossible unaligned trap. insn=%08x\n", insn);
+ die_if_kernel("Byte sized unaligned access?!?!", current->tss.kregs);
+ }
+}
+
+static inline int decode_asi(unsigned int insn, struct pt_regs *regs)
+{
+ if (insn & 0x800000) {
+ if (insn & 0x2000)
+ return (unsigned char)(regs->tstate >> 24); /* %asi */
+ else
+ return (unsigned char)(insn >> 5); /* imm_asi */
+ } else
+ return ASI_P;
+}
+
+/* 0x400000 = signed, 0 = unsigned */
+static inline int decode_signedness(unsigned int insn)
+{
+ return (insn & 0x400000);
+}
+
+static inline void maybe_flush_windows(unsigned int rs1, unsigned int rs2,
+ unsigned int rd)
+{
+ if(rs2 >= 16 || rs1 >= 16 || rd >= 16) {
+ flushw_user();
+ }
+}
+
+static inline long sign_extend_imm13(long imm)
+{
+ return imm << 51 >> 51;
+}
+
+static inline unsigned long fetch_reg(unsigned int reg, struct pt_regs *regs)
+{
+ struct reg_window *win;
+
+ if(reg < 16)
+ return (!reg ? 0 : regs->u_regs[reg]);
+
+ /* Ho hum, the slightly complicated case. */
+ win = (struct reg_window *) regs->u_regs[UREG_FP];
+ return win->locals[reg - 16]; /* yes, I know what this does... */
+}
+
+static inline unsigned long *fetch_reg_addr(unsigned int reg, struct pt_regs *regs)
+{
+ struct reg_window *win;
+
+ if(reg < 16)
+ return &regs->u_regs[reg];
+ win = (struct reg_window *) regs->u_regs[UREG_FP];
+ return &win->locals[reg - 16];
+}
+
+static inline unsigned long compute_effective_address(struct pt_regs *regs,
+ unsigned int insn)
+{
+ unsigned int rs1 = (insn >> 14) & 0x1f;
+ unsigned int rs2 = insn & 0x1f;
+ unsigned int rd = (insn >> 25) & 0x1f;
+
+ if(insn & 0x2000) {
+ maybe_flush_windows(rs1, 0, rd);
+ return (fetch_reg(rs1, regs) + sign_extend_imm13(insn));
+ } else {
+ maybe_flush_windows(rs1, rs2, rd);
+ return (fetch_reg(rs1, regs) + fetch_reg(rs2, regs));
+ }
+}
+
+/* This is just to make gcc think panic does return... */
+static void unaligned_panic(char *str)
+{
+ panic(str);
+}
+
+#define do_integer_load(dest_reg, size, saddr, is_signed, asi, errh) ({ \
+__asm__ __volatile__ ( \
+ "wr %4, 0, %%asi\n\t" \
+ "cmp %1, 8\n\t" \
+ "bge,pn %%icc, 9f\n\t" \
+ " cmp %1, 4\n\t" \
+ "be,pt %%icc, 6f\n" \
+"4:\t" " lduba [%2] %%asi, %%l1\n" \
+"5:\t" "lduba [%2 + 1] %%asi, %%l2\n\t" \
+ "sll %%l1, 8, %%l1\n\t" \
+ "brz,pt %3, 3f\n\t" \
+ " add %%l1, %%l2, %%l1\n\t" \
+ "sllx %%l1, 48, %%l1\n\t" \
+ "srax %%l1, 48, %%l1\n" \
+"3:\t" "ba,pt %%xcc, 0f\n\t" \
+ " stx %%l1, [%0]\n" \
+"6:\t" "lduba [%2 + 1] %%asi, %%l2\n\t" \
+ "sll %%l1, 24, %%l1\n" \
+"7:\t" "lduba [%2 + 2] %%asi, %%g7\n\t" \
+ "sll %%l2, 16, %%l2\n" \
+"8:\t" "lduba [%2 + 3] %%asi, %%g1\n\t" \
+ "sll %%g7, 8, %%g7\n\t" \
+ "or %%l1, %%l2, %%l1\n\t" \
+ "or %%g7, %%g1, %%g7\n\t" \
+ "or %%l1, %%g7, %%l1\n\t" \
+ "brnz,a,pt %3, 3f\n\t" \
+ " sra %%l1, 0, %%l1\n" \
+"3:\t" "ba,pt %%xcc, 0f\n\t" \
+ " stx %%l1, [%0]\n" \
+"9:\t" "lduba [%2] %%asi, %%l1\n" \
+"10:\t" "lduba [%2 + 1] %%asi, %%l2\n\t" \
+ "sllx %%l1, 56, %%l1\n" \
+"11:\t" "lduba [%2 + 2] %%asi, %%g7\n\t" \
+ "sllx %%l2, 48, %%l2\n" \
+"12:\t" "lduba [%2 + 3] %%asi, %%g1\n\t" \
+ "sllx %%g7, 40, %%g7\n\t" \
+ "sllx %%g1, 32, %%g1\n\t" \
+ "or %%l1, %%l2, %%l1\n\t" \
+ "or %%g7, %%g1, %%g7\n" \
+"13:\t" "lduba [%2 + 4] %%asi, %%l2\n\t" \
+ "or %%l1, %%g7, %%g7\n" \
+"14:\t" "lduba [%2 + 5] %%asi, %%g1\n\t" \
+ "sllx %%l2, 24, %%l2\n" \
+"15:\t" "lduba [%2 + 6] %%asi, %%l1\n\t" \
+ "sllx %%g1, 16, %%g1\n\t" \
+ "or %%g7, %%l2, %%g7\n" \
+"16:\t" "lduba [%2 + 7] %%asi, %%l2\n\t" \
+ "sllx %%l1, 8, %%l1\n\t" \
+ "or %%g7, %%g1, %%g7\n\t" \
+ "or %%l1, %%l2, %%l1\n\t" \
+ "or %%g7, %%l1, %%g7\n\t" \
+ "cmp %1, 8\n\t" \
+ "be,a,pt %%icc, 0f\n\t" \
+ " stx %%g7, [%0]\n\t" \
+ "srlx %%g7, 32, %%l1\n\t" \
+ "sra %%g7, 0, %%g7\n\t" \
+ "stx %%l1, [%0]\n\t" \
+ "stx %%g7, [%0 + 8]\n" \
+"0:\n\n\t" \
+ ".section __ex_table\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"); \
+})
+
+#define store_common(dst_addr, size, src_val, asi, errh) ({ \
+__asm__ __volatile__ ( \
+ "wr %3, 0, %%asi\n\t" \
+ "ldx [%2], %%l1\n" \
+ "cmp %1, 2\n\t" \
+ "be,pn %%icc, 2f\n\t" \
+ " cmp %1, 4\n\t" \
+ "be,pt %%icc, 1f\n\t" \
+ " srlx %%l1, 24, %%l2\n\t" \
+ "srlx %%l1, 56, %%g1\n\t" \
+ "srlx %%l1, 48, %%g7\n" \
+"4:\t" "stba %%g1, [%0] %%asi\n\t" \
+ "srlx %%l1, 40, %%g1\n" \
+"5:\t" "stba %%g7, [%0 + 1] %%asi\n\t" \
+ "srlx %%l1, 32, %%g7\n" \
+"6:\t" "stba %%g1, [%0 + 2] %%asi\n" \
+"7:\t" "stba %%g7, [%0 + 3] %%asi\n\t" \
+ "srlx %%l1, 16, %%g1\n" \
+"8:\t" "stba %%l2, [%0 + 4] %%asi\n\t" \
+ "srlx %%l1, 8, %%g7\n" \
+"9:\t" "stba %%g1, [%0 + 5] %%asi\n" \
+"10:\t" "stba %%g7, [%0 + 6] %%asi\n\t" \
+ "ba,pt %%xcc, 0f\n" \
+"11:\t" " stba %%l1, [%0 + 7] %%asi\n" \
+"1:\t" "srl %%l1, 16, %%g7\n" \
+"12:\t" "stba %%l2, [%0] %%asi\n\t" \
+ "srl %%l1, 8, %%l2\n" \
+"13:\t" "stba %%g7, [%0 + 1] %%asi\n" \
+"14:\t" "stba %%l2, [%0 + 2] %%asi\n\t" \
+ "ba,pt %%xcc, 0f\n" \
+"15:\t" " stba %%l1, [%0 + 3] %%asi\n" \
+"2:\t" "srl %%l1, 8, %%l2\n" \
+"16:\t" "stba %%l2, [%0] %%asi\n" \
+"17:\t" "stba %%l1, [%0 + 1] %%asi\n" \
+"0:\n\n\t" \
+ ".section __ex_table\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"); \
+})
+
+#define do_integer_store(reg_num, size, dst_addr, regs, asi, errh) ({ \
+ unsigned long zero = 0; \
+ unsigned long *src_val = &zero; \
+ \
+ if (size == 16) { \
+ size = 8; \
+ zero = (((long)(reg_num ? \
+ (unsigned)fetch_reg(reg_num, regs) : 0)) << 32) | \
+ (unsigned)fetch_reg(reg_num + 1, regs); \
+ } else if (reg_num) src_val = fetch_reg_addr(reg_num, regs); \
+ store_common(dst_addr, size, src_val, asi, errh); \
+})
+
+/* XXX Need to capture/release other cpu's for SMP around this. */
+#define do_atomic(srcdest_reg, mem, errh) ({ \
+ unsigned long flags, tmp; \
+ \
+ save_and_cli(flags); \
+ tmp = *srcdest_reg; \
+ do_integer_load(srcdest_reg, 4, mem, 0, errh); \
+ store_common(mem, 4, &tmp, errh); \
+ restore_flags(flags); \
+})
+
+static inline void advance(struct pt_regs *regs)
+{
+ regs->tpc = regs->tnpc;
+ regs->tnpc += 4;
+}
+
+static inline int floating_point_load_or_store_p(unsigned int insn)
+{
+ return (insn >> 24) & 1;
+}
+
+static inline int ok_for_kernel(unsigned int insn)
+{
+ return !floating_point_load_or_store_p(insn);
+}
+
+void kernel_mna_trap_fault(struct pt_regs *regs, unsigned int insn) __asm__ ("kernel_mna_trap_fault");
+
+void kernel_mna_trap_fault(struct pt_regs *regs, unsigned int insn)
+{
+ unsigned long g2 = regs->u_regs [UREG_G2];
+ unsigned long fixup = search_exception_table (regs->tpc, &g2);
+
+ if (!fixup) {
+ unsigned long address = compute_effective_address(regs, insn);
+ if(address < PAGE_SIZE) {
+ printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference in mna handler");
+ } else
+ printk(KERN_ALERT "Unable to handle kernel paging request in mna handler");
+ printk(KERN_ALERT " at virtual address %016lx\n",address);
+ printk(KERN_ALERT "current->mm->context = %016lx\n",
+ (unsigned long) current->mm->context);
+ printk(KERN_ALERT "current->mm->pgd = %016lx\n",
+ (unsigned long) current->mm->pgd);
+ die_if_kernel("Oops", regs);
+ /* Not reached */
+ }
+ regs->tpc = fixup;
+ regs->tnpc = regs->tpc + 4;
+ regs->u_regs [UREG_G2] = g2;
+}
+
+asmlinkage void kernel_unaligned_trap(struct pt_regs *regs, unsigned int insn)
+{
+ enum direction dir = decode_direction(insn);
+ int size = decode_access_size(insn);
+
+ lock_kernel();
+ if(!ok_for_kernel(insn) || dir == both) {
+ printk("Unsupported unaligned load/store trap for kernel at <%016lx>.\n",
+ regs->tpc);
+ unaligned_panic("Wheee. Kernel does fpu/atomic unaligned load/store.");
+
+ __asm__ __volatile__ ("\n"
+"kernel_unaligned_trap_fault:\n\t"
+ "mov %0, %%o0\n\t"
+ "call kernel_mna_trap_fault\n\t"
+ " mov %1, %%o1\n\t"
+ :
+ : "r" (regs), "r" (insn)
+ : "o0", "o1", "o2", "o3", "o4", "o5", "o7",
+ "g1", "g2", "g3", "g4", "g5", "g7", "cc");
+ } else {
+ unsigned long addr = compute_effective_address(regs, insn);
+
+#ifdef DEBUG_MNA
+ printk("KMNA: pc=%016lx [dir=%s addr=%016lx size=%d] retpc[%016lx]\n",
+ regs->tpc, dirstrings[dir], addr, size, regs->u_regs[UREG_RETPC]);
+#endif
+ switch(dir) {
+ case load:
+ do_integer_load(fetch_reg_addr(((insn>>25)&0x1f), regs),
+ size, (unsigned long *) addr,
+ decode_signedness(insn), decode_asi(insn, regs),
+ kernel_unaligned_trap_fault);
+ break;
+
+ case store:
+ do_integer_store(((insn>>25)&0x1f), size,
+ (unsigned long *) addr, regs,
+ decode_asi(insn, regs),
+ kernel_unaligned_trap_fault);
+ break;
+#if 0 /* unsupported */
+ case both:
+ do_atomic(fetch_reg_addr(((insn>>25)&0x1f), regs),
+ (unsigned long *) addr,
+ kernel_unaligned_trap_fault);
+ break;
+#endif
+ default:
+ panic("Impossible kernel unaligned trap.");
+ /* Not reached... */
+ }
+ advance(regs);
+ }
+ unlock_kernel();
+}
+
+#if 0 /* XXX: Implement user mna some day */
+static inline int ok_for_user(struct pt_regs *regs, unsigned int insn,
+ enum direction dir)
+{
+ unsigned int reg;
+ int retval, check = (dir == load) ? VERIFY_READ : VERIFY_WRITE;
+ int size = ((insn >> 19) & 3) == 3 ? 8 : 4;
+
+ if((regs->pc | regs->npc) & 3)
+ return 0;
+
+ /* Must verify_area() in all the necessary places. */
+#define WINREG_ADDR(regnum) ((void *)(((unsigned long *)regs->u_regs[UREG_FP])+(regnum)))
+ retval = 0;
+ reg = (insn >> 25) & 0x1f;
+ if(reg >= 16) {
+ retval = verify_area(check, WINREG_ADDR(reg - 16), size);
+ if(retval)
+ return retval;
+ }
+ reg = (insn >> 14) & 0x1f;
+ if(reg >= 16) {
+ retval = verify_area(check, WINREG_ADDR(reg - 16), size);
+ if(retval)
+ return retval;
+ }
+ if(!(insn & 0x2000)) {
+ reg = (insn & 0x1f);
+ if(reg >= 16) {
+ retval = verify_area(check, WINREG_ADDR(reg - 16), size);
+ if(retval)
+ return retval;
+ }
+ }
+ return retval;
+#undef WINREG_ADDR
+}
+
+void user_mna_trap_fault(struct pt_regs *regs, unsigned int insn) __asm__ ("user_mna_trap_fault");
+
+void user_mna_trap_fault(struct pt_regs *regs, unsigned int insn)
+{
+ current->tss.sig_address = regs->pc;
+ current->tss.sig_desc = SUBSIG_PRIVINST;
+ send_sig(SIGBUS, current, 1);
+}
+
+asmlinkage void user_unaligned_trap(struct pt_regs *regs, unsigned int insn)
+{
+ enum direction dir;
+
+ lock_kernel();
+ if(!(current->tss.flags & SPARC_FLAG_UNALIGNED) ||
+ (((insn >> 30) & 3) != 3))
+ goto kill_user;
+ dir = decode_direction(insn);
+ if(!ok_for_user(regs, insn, dir)) {
+ goto kill_user;
+ } else {
+ int size = decode_access_size(insn);
+ unsigned long addr;
+
+ if(floating_point_load_or_store_p(insn)) {
+ printk("User FPU load/store unaligned unsupported.\n");
+ goto kill_user;
+ }
+
+ addr = compute_effective_address(regs, insn);
+ switch(dir) {
+ case load:
+ do_integer_load(fetch_reg_addr(((insn>>25)&0x1f), regs),
+ size, (unsigned long *) addr,
+ decode_signedness(insn),
+ user_unaligned_trap_fault);
+ break;
+
+ case store:
+ do_integer_store(((insn>>25)&0x1f), size,
+ (unsigned long *) addr, regs,
+ user_unaligned_trap_fault);
+ break;
+
+ case both:
+ do_atomic(fetch_reg_addr(((insn>>25)&0x1f), regs),
+ (unsigned long *) addr,
+ user_unaligned_trap_fault);
+ break;
+
+ default:
+ unaligned_panic("Impossible user unaligned trap.");
+
+ __asm__ __volatile__ ("\n"
+"user_unaligned_trap_fault:\n\t"
+ "mov %0, %%o0\n\t"
+ "call user_mna_trap_fault\n\t"
+ " mov %1, %%o1\n\t"
+ :
+ : "r" (regs), "r" (insn)
+ : "o0", "o1", "o2", "o3", "o4", "o5", "o7",
+ "g1", "g2", "g3", "g4", "g5", "g7", "cc");
+ goto out;
+ }
+ advance(regs);
+ goto out;
+ }
+
+kill_user:
+ current->tss.sig_address = regs->pc;
+ current->tss.sig_desc = SUBSIG_PRIVINST;
+ send_sig(SIGBUS, current, 1);
+out:
+ unlock_kernel();
+}
+#endif
diff --git a/arch/sparc64/kernel/winfixup.S b/arch/sparc64/kernel/winfixup.S
index 2ac19a440..f2c714eae 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.16 1997/07/13 20:02:42 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.
+ wrpr %l1, (PSTATE_IE | PSTATE_AG | PSTATE_RMO), %pstate
+ 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.
+ wrpr %l1, (PSTATE_IE | PSTATE_AG | PSTATE_RMO), %pstate
+ 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 56c506507..3da21c606 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.13 1997/07/16 10:12:03 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 VIScsum.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 000000000..45bc870a4
--- /dev/null
+++ b/arch/sparc64/lib/VIS.h
@@ -0,0 +1,113 @@
+/* $Id: VIS.h,v 1.1 1997/07/18 06:26:48 ralf 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 <asm/head.h>
+#include <asm/asi.h>
+#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 000000000..3c86861fd
--- /dev/null
+++ b/arch/sparc64/lib/VISbzero.S
@@ -0,0 +1,246 @@
+/* $Id: VISbzero.S,v 1.1 1997/07/18 06:26:48 ralf 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 000000000..1429f1658
--- /dev/null
+++ b/arch/sparc64/lib/VIScopy.S
@@ -0,0 +1,1060 @@
+/* $Id: VIScopy.S,v 1.1 1997/07/18 06:26:48 ralf 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
+
+ .globl __copy_in_user
+ .type __copy_in_user,@function
+__copy_in_user: mov ASI_BLK_S, 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/VIScsum.S b/arch/sparc64/lib/VIScsum.S
new file mode 100644
index 000000000..1ccb98759
--- /dev/null
+++ b/arch/sparc64/lib/VIScsum.S
@@ -0,0 +1,436 @@
+/* $Id: VIScsum.S,v 1.1 1997/07/18 06:26:49 ralf Exp $
+ * VIScsum.S: High bandwidth IP checksumming utilizing the UltraSparc
+ * Visual Instruction Set.
+ *
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ *
+ * Based on older sparc32/sparc64 checksum.S, which is:
+ *
+ * Copyright(C) 1995 Linus Torvalds
+ * Copyright(C) 1995 Miguel de Icaza
+ * Copyright(C) 1996,1997 David S. Miller
+ * derived from:
+ * Linux/Alpha checksum c-code
+ * Linux/ix86 inline checksum assembly
+ * RFC1071 Computing the Internet Checksum (esp. Jacobsons m68k code)
+ * David Mosberger-Tang for optimized reference c-code
+ * BSD4.4 portable checksum routine
+ */
+
+#ifdef __sparc_v9__
+#define STACKOFF 2175
+#else
+#define STACKOFF 64
+#endif
+
+#ifdef __KERNEL__
+#include <asm/head.h>
+#include <asm/asi.h>
+#else
+#define ASI_BLK_P 0xf0
+#define FRPS_FEF 0x04
+#endif
+
+/* Dobrou noc, SunSoft engineers. Spete sladce.
+ * This has a couple of tricks in and those
+ * tricks are UltraLinux trade secrets :))
+ */
+
+#define START_THE_TRICK(fz,f0,f2,f4,f6,f8,f10) \
+ fcmpgt32 %fz, %f0, %g1 /* FPM Group */; \
+ fcmpgt32 %fz, %f2, %g2 /* FPM Group */; \
+ fcmpgt32 %fz, %f4, %g3 /* FPM Group */; \
+ fcmpgt32 %fz, %f6, %g5 /* FPM Group */; \
+ inc %g1 /* IEU0 */; \
+ fcmpgt32 %fz, %f8, %g7 /* FPM Group */; \
+ srl %g1, 1, %g1 /* IEU0 */; \
+ inc %g2 /* IEU1 */; \
+ fcmpgt32 %fz, %f10, %o3 /* FPM Group */; \
+ srl %g2, 1, %g2 /* IEU0 */; \
+ add %o2, %g1, %o2 /* IEU1 */; \
+ add %g3, 1, %g3 /* IEU0 Group */; \
+ srl %g3, 1, %g3 /* IEU0 Group */; \
+ add %o2, %g2, %o2 /* IEU1 */; \
+ inc %g5 /* IEU0 Group */; \
+ add %o2, %g3, %o2 /* IEU1 */;
+
+#define DO_THE_TRICK(O12,O14,f0,f2,f4,f6,f8,f10,f12,f14,F0,F2,F4,F6,F8,F10,F12,F14) \
+ fcmpgt32 %O12, %f12, %o4 /* FPM Group */; \
+ srl %g5, 1, %g5 /* IEU0 */; \
+ inc %g7 /* IEU1 */; \
+ fpadd32 %F0, %f0, %F0 /* FPA */; \
+ fcmpgt32 %O14, %f14, %o5 /* FPM Group */; \
+ srl %g7, 1, %g7 /* IEU0 */; \
+ add %o2, %g5, %o2 /* IEU1 */; \
+ fpadd32 %F2, %f2, %F2 /* FPA */; \
+ inc %o3 /* IEU0 Group */; \
+ add %o2, %g7, %o2 /* IEU1 */; \
+ fcmpgt32 %f0, %F0, %g1 /* FPM Group */; \
+ srl %o3, 1, %o3 /* IEU0 */; \
+ inc %o4 /* IEU1 */; \
+ fpadd32 %F4, %f4, %F4 /* FPA */; \
+ fcmpgt32 %f2, %F2, %g2 /* FPM Group */; \
+ srl %o4, 1, %o4 /* IEU0 */; \
+ add %o2, %o3, %o2 /* IEU1 */; \
+ fpadd32 %F6, %f6, %F6 /* FPA */; \
+ inc %o5 /* IEU0 Group */; \
+ add %o2, %o4, %o2 /* IEU1 */; \
+ fcmpgt32 %f4, %F4, %g3 /* FPM Group */; \
+ srl %o5, 1, %o5 /* IEU0 */; \
+ inc %g1 /* IEU1 */; \
+ fpadd32 %F8, %f8, %F8 /* FPA */; \
+ fcmpgt32 %f6, %F6, %g5 /* FPM Group */; \
+ srl %g1, 1, %g1 /* IEU0 */; \
+ add %o2, %o5, %o2 /* IEU1 */; \
+ fpadd32 %F10, %f10, %F10 /* FPA */; \
+ inc %g2 /* IEU0 Group */; \
+ add %o2, %g1, %o2 /* IEU1 */; \
+ fcmpgt32 %f8, %F8, %g7 /* FPM Group */; \
+ srl %g2, 1, %g2 /* IEU0 */; \
+ inc %g3 /* IEU1 */; \
+ fpadd32 %F12, %f12, %F12 /* FPA */; \
+ fcmpgt32 %f10, %F10, %o3 /* FPM Group */; \
+ srl %g3, 1, %g3 /* IEU0 */; \
+ add %o2, %g2, %o2 /* IEU1 */; \
+ fpadd32 %F14, %f14, %F14 /* FPA */; \
+ inc %g5 /* IEU0 Group */; \
+ add %o2, %g3, %o2 /* IEU1 */;
+
+#define END_THE_TRICK(O12,O14,f0,f2,f4,f6,f8,f10,f12,f14,S0,S1,S2,S3,T0,T1,U0,fz) \
+ fcmpgt32 %O12, %f12, %o4 /* FPM Group */; \
+ srl %g5, 1, %g5 /* IEU0 */; \
+ inc %g7 /* IEU1 */; \
+ fpadd32 %f2, %f0, %S0 /* FPA */; \
+ fcmpgt32 %O14, %f14, %o5 /* FPM Group */; \
+ srl %g7, 1, %g7 /* IEU0 */; \
+ add %o2, %g5, %o2 /* IEU1 */; \
+ fpadd32 %f6, %f4, %S1 /* FPA */; \
+ inc %o3 /* IEU0 Group */; \
+ add %o2, %g7, %o2 /* IEU1 */; \
+ fcmpgt32 %f0, %S0, %g1 /* FPM Group */; \
+ srl %o3, 1, %o3 /* IEU0 */; \
+ inc %o4 /* IEU1 */; \
+ fpadd32 %f10, %f8, %S2 /* FPA */; \
+ fcmpgt32 %f4, %S1, %g2 /* FPM Group */; \
+ srl %o4, 1, %o4 /* IEU0 */; \
+ add %o2, %o3, %o2 /* IEU1 */; \
+ fpadd32 %f14, %f12, %S3 /* FPA */; \
+ inc %o5 /* IEU0 Group */; \
+ add %o2, %o4, %o2 /* IEU1 */; \
+ fzero %fz /* FPA */; \
+ fcmpgt32 %f8, %S2, %g3 /* FPM Group */; \
+ srl %o5, 1, %o5 /* IEU0 */; \
+ inc %g1 /* IEU1 */; \
+ fpadd32 %S0, %S1, %T0 /* FPA */; \
+ fcmpgt32 %f12, %S3, %g5 /* FPM Group */; \
+ srl %g1, 1, %g1 /* IEU0 */; \
+ add %o2, %o5, %o2 /* IEU1 */; \
+ fpadd32 %S2, %S3, %T1 /* FPA */; \
+ inc %g2 /* IEU0 Group */; \
+ add %o2, %g1, %o2 /* IEU1 */; \
+ fcmpgt32 %S0, %T0, %g7 /* FPM Group */; \
+ srl %g2, 1, %g2 /* IEU0 */; \
+ inc %g3 /* IEU1 */; \
+ fcmpgt32 %S2, %T1, %o3 /* FPM Group */; \
+ srl %g3, 1, %g3 /* IEU0 */; \
+ add %o2, %g2, %o2 /* IEU1 */; \
+ inc %g5 /* IEU0 Group */; \
+ add %o2, %g3, %o2 /* IEU1 */; \
+ fcmpgt32 %fz, %f2, %o4 /* FPM Group */; \
+ srl %g5, 1, %g5 /* IEU0 */; \
+ inc %g7 /* IEU1 */; \
+ fpadd32 %T0, %T1, %U0 /* FPA */; \
+ fcmpgt32 %fz, %f6, %o5 /* FPM Group */; \
+ srl %g7, 1, %g7 /* IEU0 */; \
+ add %o2, %g5, %o2 /* IEU1 */; \
+ inc %o3 /* IEU0 Group */; \
+ add %o2, %g7, %o2 /* IEU1 */; \
+ fcmpgt32 %fz, %f10, %g1 /* FPM Group */; \
+ srl %o3, 1, %o3 /* IEU0 */; \
+ inc %o4 /* IEU1 */; \
+ fcmpgt32 %fz, %f14, %g2 /* FPM Group */; \
+ srl %o4, 1, %o4 /* IEU0 */; \
+ add %o2, %o3, %o2 /* IEU1 */; \
+ std %U0, [%sp + STACKOFF] /* Store Group */; \
+ inc %o5 /* IEU0 */; \
+ sub %o2, %o4, %o2 /* IEU1 */; \
+ fcmpgt32 %fz, %S1, %g3 /* FPM Group */; \
+ srl %o5, 1, %o5 /* IEU0 */; \
+ inc %g1 /* IEU1 */; \
+ fcmpgt32 %fz, %S3, %g5 /* FPM Group */; \
+ srl %g1, 1, %g1 /* IEU0 */; \
+ sub %o2, %o5, %o2 /* IEU1 */; \
+ ldx [%sp + STACKOFF], %o5 /* Load Group */; \
+ inc %g2 /* IEU0 */; \
+ sub %o2, %g1, %o2 /* IEU1 */; \
+ fcmpgt32 %fz, %T1, %g7 /* FPM Group */; \
+ srl %g2, 1, %g2 /* IEU0 */; \
+ inc %g3 /* IEU1 */; \
+ fcmpgt32 %T0, %U0, %o3 /* FPM Group */; \
+ srl %g3, 1, %g3 /* IEU0 */; \
+ sub %o2, %g2, %o2 /* IEU1 */; \
+ inc %g5 /* IEU0 Group */; \
+ sub %o2, %g3, %o2 /* IEU1 */; \
+ fcmpgt32 %fz, %U0, %o4 /* FPM Group */; \
+ srl %g5, 1, %g5 /* IEU0 */; \
+ inc %g7 /* IEU1 */; \
+ srl %g7, 1, %g7 /* IEU0 Group */; \
+ sub %o2, %g5, %o2 /* IEU1 */; \
+ inc %o3 /* IEU0 Group */; \
+ sub %o2, %g7, %o2 /* IEU1 */; \
+ srl %o3, 1, %o3 /* IEU0 Group */; \
+ inc %o4 /* IEU1 */; \
+ srl %o4, 1, %o4 /* IEU0 Group */; \
+ add %o2, %o3, %o2 /* IEU1 */; \
+ sub %o2, %o4, %o2 /* IEU0 Group */; \
+ addcc %o2, %o5, %o2 /* IEU1 Group */; \
+ bcs,a,pn %xcc, 33f /* CTI */; \
+ add %o2, 1, %o2 /* IEU0 */; \
+33: /* That's it */;
+
+#define CSUM_LASTCHUNK(offset) \
+ ldx [%o0 - offset - 0x10], %g2; \
+ ldx [%o0 - offset - 0x08], %g3; \
+ addcc %g2, %o2, %o2; \
+ bcs,a,pn %xcc, 31f; \
+ add %o2, 1, %o2; \
+31: addcc %g3, %o2, %o2; \
+ bcs,a,pn %xcc, 32f; \
+ add %o2, 1, %o2; \
+32:
+
+ .text
+ .globl csum_partial
+ .align 32
+csum_partial:
+ andcc %o0, 7, %g0 /* IEU1 Group */
+ be,pt %icc, 4f /* CTI */
+ andcc %o0, 0x38, %g3 /* IEU1 */
+ mov 1, %g5 /* IEU0 Group */
+ cmp %o1, 6 /* IEU1 */
+ bl,pn %icc, 21f /* CTI */
+ andcc %o0, 2, %g0 /* IEU1 Group */
+ be,pt %icc, 1f /* CTI */
+ and %o0, 4, %g7 /* IEU0 */
+ lduh [%o0], %g2 /* Load */
+ sub %o1, 2, %o1 /* IEU0 Group */
+ add %o0, 2, %o0 /* IEU1 */
+ andcc %o0, 4, %g7 /* IEU1 Group */
+ sll %g5, 16, %g5 /* IEU0 */
+ sll %g2, 16, %g2 /* IEU0 Group */
+ addcc %g2, %o2, %o2 /* IEU1 Group (regdep) */
+ bcs,a,pn %icc, 1f /* CTI */
+ add %o2, %g5, %o2 /* IEU0 */
+1: ld [%o0], %g2 /* Load */
+ brz,a,pn %g7, 4f /* CTI+IEU1 Group */
+ and %o0, 0x38, %g3 /* IEU0 */
+ add %o0, 4, %o0 /* IEU0 Group */
+ sub %o1, 4, %o1 /* IEU1 */
+ addcc %g2, %o2, %o2 /* IEU1 Group */
+ bcs,a,pn %icc, 1f /* CTI */
+ add %o2, 1, %o2 /* IEU0 */
+1: and %o0, 0x38, %g3 /* IEU1 Group */
+4: srl %o2, 0, %o2 /* IEU0 Group */
+ mov 0x40, %g1 /* IEU1 */
+ brz,pn %g3, 3f /* CTI+IEU1 Group */
+ sub %g1, %g3, %g1 /* IEU0 */
+ cmp %o1, 56 /* IEU1 Group */
+ blu,pn %icc, 20f /* CTI */
+ andcc %o0, 8, %g0 /* IEU1 Group */
+ be,pn %icc, 1f /* CTI */
+ ldx [%o0], %g2 /* Load */
+ add %o0, 8, %o0 /* IEU0 Group */
+ sub %o1, 8, %o1 /* IEU1 */
+ addcc %g2, %o2, %o2 /* IEU1 Group */
+ bcs,a,pn %xcc, 1f /* CTI */
+ add %o2, 1, %o2 /* IEU0 */
+1: andcc %g1, 0x10, %g0 /* IEU1 Group */
+ be,pn %icc, 2f /* CTI */
+ and %g1, 0x20, %g1 /* IEU0 */
+ ldx [%o0], %g2 /* Load */
+ ldx [%o0+8], %g3 /* Load Group */
+ add %o0, 16, %o0 /* IEU0 */
+ sub %o1, 16, %o1 /* IEU1 */
+ addcc %g2, %o2, %o2 /* IEU1 Group */
+ bcs,a,pn %xcc, 1f /* CTI */
+ add %o2, 1, %o2 /* IEU0 */
+1: addcc %g3, %o2, %o2 /* IEU1 Group */
+ bcs,a,pn %xcc, 2f /* CTI */
+ add %o2, 1, %o2 /* IEU0 */
+2: brz,pn %g1, 3f /* CTI+IEU1 Group */
+ ldx [%o0], %g2 /* Load */
+ ldx [%o0+8], %g3 /* Load Group */
+ ldx [%o0+16], %g5 /* Load Group */
+ ldx [%o0+24], %g7 /* Load Group */
+ add %o0, 32, %o0 /* IEU0 */
+ sub %o1, 32, %o1 /* IEU1 */
+ addcc %g2, %o2, %o2 /* IEU1 Group */
+ bcs,a,pn %xcc, 1f /* CTI */
+ add %o2, 1, %o2 /* IEU0 */
+1: addcc %g3, %o2, %o2 /* IEU1 Group */
+ bcs,a,pn %xcc, 1f /* CTI */
+ add %o2, 1, %o2 /* IEU0 */
+1: addcc %g5, %o2, %o2 /* IEU1 Group */
+ bcs,a,pn %xcc, 1f /* CTI */
+ add %o2, 1, %o2 /* IEU0 */
+1: addcc %g7, %o2, %o2 /* IEU1 Group */
+ bcs,a,pn %xcc, 3f /* CTI */
+ add %o2, 1, %o2 /* IEU0 */
+3: cmp %o1, 0xc0 /* IEU1 Group */
+ blu,pn %icc, 20f /* CTI */
+ sllx %o2, 32, %g1 /* IEU0 */
+ addcc %o2, %g1, %o2 /* IEU1 Group */
+ sub %o1, 0xc0, %o1 /* IEU0 */
+ wr %g0, ASI_BLK_P, %asi /* LSU Group */
+#ifdef __KERNEL__
+ wr %g0, FPRS_FEF, %fprs /* LSU Group */
+#endif
+ membar #StoreLoad /* LSU Group */
+ srlx %o2, 32, %o2 /* IEU0 Group */
+ bcs,a,pn %xcc, 1f /* CTI */
+ add %o2, 1, %o2 /* IEU1 */
+1: andcc %o1, 0x80, %g0 /* IEU1 Group */
+ bne,pn %icc, 7f /* CTI */
+ andcc %o1, 0x40, %g0 /* IEU1 Group */
+ be,pn %icc, 6f /* CTI */
+ fzero %f12 /* FPA */
+ fzero %f14 /* FPA Group */
+ ldda [%o0 + 0x000] %asi, %f16
+ ldda [%o0 + 0x040] %asi, %f32
+ ldda [%o0 + 0x080] %asi, %f48
+ START_THE_TRICK(f12,f16,f18,f20,f22,f24,f26)
+ ba,a,pt %xcc, 3f
+6: sub %o0, 0x40, %o0 /* IEU0 Group */
+ fzero %f28 /* FPA */
+ fzero %f30 /* FPA Group */
+ ldda [%o0 + 0x040] %asi, %f32
+ ldda [%o0 + 0x080] %asi, %f48
+ ldda [%o0 + 0x0c0] %asi, %f0
+ START_THE_TRICK(f28,f32,f34,f36,f38,f40,f42)
+ ba,a,pt %xcc, 4f
+7: bne,pt %icc, 8f /* CTI */
+ fzero %f44 /* FPA */
+ add %o0, 0x40, %o0 /* IEU0 Group */
+ fzero %f60 /* FPA */
+ fzero %f62 /* FPA Group */
+ ldda [%o0 - 0x040] %asi, %f0
+ ldda [%o0 + 0x000] %asi, %f16
+ ldda [%o0 + 0x040] %asi, %f32
+ START_THE_TRICK(f60,f0,f2,f4,f6,f8,f10)
+ ba,a,pt %xcc, 2f
+8: add %o0, 0x80, %o0 /* IEU0 Group */
+ fzero %f46 /* FPA */
+ ldda [%o0 - 0x080] %asi, %f48
+ ldda [%o0 - 0x040] %asi, %f0
+ ldda [%o0 + 0x000] %asi, %f16
+ START_THE_TRICK(f44,f48,f50,f52,f54,f56,f58)
+1: DO_THE_TRICK(f44,f46,f48,f50,f52,f54,f56,f58,f60,f62,f0,f2,f4,f6,f8,f10,f12,f14)
+ ldda [%o0 + 0x040] %asi, %f32
+2: DO_THE_TRICK(f60,f62,f0,f2,f4,f6,f8,f10,f12,f14,f16,f18,f20,f22,f24,f26,f28,f30)
+ ldda [%o0 + 0x080] %asi, %f48
+3: DO_THE_TRICK(f12,f14,f16,f18,f20,f22,f24,f26,f28,f30,f32,f34,f36,f38,f40,f42,f44,f46)
+ ldda [%o0 + 0x0c0] %asi, %f0
+4: DO_THE_TRICK(f28,f30,f32,f34,f36,f38,f40,f42,f44,f46,f48,f50,f52,f54,f56,f58,f60,f62)
+ add %o0, 0x100, %o0 /* IEU0 Group */
+ subcc %o1, 0x100, %o1 /* IEU1 */
+ bgeu,a,pt %icc, 1b /* CTI */
+ ldda [%o0 + 0x000] %asi, %f16
+ membar #Sync /* LSU Group */
+ DO_THE_TRICK(f44,f46,f48,f50,f52,f54,f56,f58,f60,f62,f0,f2,f4,f6,f8,f10,f12,f14)
+ END_THE_TRICK(f60,f62,f0,f2,f4,f6,f8,f10,f12,f14,f16,f18,f20,f22,f24,f26,f28,f30)
+ and %o1, 0x3f, %o1 /* IEU0 Group */
+#ifdef __KERNEL__
+ wr %g0, 0, %fprs /* LSU Group */
+#endif
+20: andcc %o1, 0xf0, %g1 /* IEU1 Group */
+ be,pn %icc, 23f /* CTI */
+ and %o1, 0xf, %o3 /* IEU0 */
+22: rd %pc, %g7 /* LSU Group */
+ sll %g1, 1, %o4 /* IEU0 Group */
+ sub %g7, %o4, %g7 /* IEU0 Group (regdep) */
+ jmpl %g7 + (23f - 22b), %g0 /* CTI Group brk forced */
+ add %o0, %g1, %o0 /* IEU0 */
+ CSUM_LASTCHUNK(0xe0)
+ CSUM_LASTCHUNK(0xd0)
+ CSUM_LASTCHUNK(0xc0)
+ CSUM_LASTCHUNK(0xb0)
+ CSUM_LASTCHUNK(0xa0)
+ CSUM_LASTCHUNK(0x90)
+ CSUM_LASTCHUNK(0x80)
+ CSUM_LASTCHUNK(0x70)
+ CSUM_LASTCHUNK(0x60)
+ CSUM_LASTCHUNK(0x50)
+ CSUM_LASTCHUNK(0x40)
+ CSUM_LASTCHUNK(0x30)
+ CSUM_LASTCHUNK(0x20)
+ CSUM_LASTCHUNK(0x10)
+ CSUM_LASTCHUNK(0x00)
+23: brnz,pn %o3, 26f /* CTI+IEU1 Group */
+24: sllx %o2, 32, %g1 /* IEU0 */
+25: addcc %o2, %g1, %o0 /* IEU1 Group */
+ srlx %o0, 32, %o0 /* IEU0 Group (regdep) */
+ bcs,a,pn %xcc, 1f /* CTI */
+ add %o0, 1, %o0 /* IEU1 */
+1: retl /* CTI Group brk forced */
+ srl %o0, 0, %o0 /* IEU0 */
+26: andcc %o1, 8, %g0 /* IEU1 Group */
+ be,pn %icc, 1f /* CTI */
+ ldx [%o0], %g3 /* Load */
+ add %o0, 8, %o0 /* IEU0 Group */
+ addcc %g3, %o2, %o2 /* IEU1 Group */
+ bcs,a,pn %xcc, 1f /* CTI */
+ add %o2, 1, %o2 /* IEU0 */
+1: andcc %o1, 4, %g0 /* IEU1 Group */
+ be,a,pn %icc, 1f /* CTI */
+ clr %g2 /* IEU0 */
+ ld [%o0], %g2 /* Load */
+ add %o0, 4, %o0 /* IEU0 Group */
+ sllx %g2, 32, %g2 /* IEU0 Group */
+1: andcc %o1, 2, %g0 /* IEU1 */
+ be,a,pn %icc, 1f /* CTI */
+ clr %o4 /* IEU0 Group */
+ lduh [%o0], %o4 /* Load */
+ add %o0, 2, %o0 /* IEU1 */
+ sll %o4, 16, %o4 /* IEU0 Group */
+1: andcc %o1, 1, %g0 /* IEU1 */
+ be,a,pn %icc, 1f /* CTI */
+ clr %o5 /* IEU0 Group */
+ ldub [%o0], %o5 /* Load */
+ sll %o5, 8, %o5 /* IEU0 Group */
+1: or %g2, %o4, %o4 /* IEU1 */
+ or %o5, %o4, %o4 /* IEU0 Group (regdep) */
+ addcc %o4, %o2, %o2 /* IEU1 Group (regdep) */
+ bcs,a,pn %xcc, 1f /* CTI */
+ add %o2, 1, %o2 /* IEU0 */
+1: ba,pt %xcc, 25b /* CTI Group */
+ sllx %o2, 32, %g1 /* IEU0 */
+21: srl %o2, 0, %o2 /* IEU0 Group */
+ cmp %o1, 0 /* IEU1 */
+ be,pn %icc, 24b /* CTI */
+ andcc %o1, 4, %g0 /* IEU1 Group */
+ be,a,pn %icc, 1f /* CTI */
+ clr %g2 /* IEU0 */
+ lduh [%o0], %g3 /* Load */
+ lduh [%o0+2], %g2 /* Load Group */
+ add %o0, 4, %o0 /* IEU0 Group */
+ sllx %g3, 48, %g3 /* IEU0 Group */
+ sllx %g2, 32, %g2 /* IEU0 Group */
+ or %g3, %g2, %g2 /* IEU0 Group */
+1: andcc %o1, 2, %g0 /* IEU1 */
+ be,a,pn %icc, 1f /* CTI */
+ clr %o4 /* IEU0 Group */
+ lduh [%o0], %o4 /* Load */
+ add %o0, 2, %o0 /* IEU1 */
+ sll %o4, 16, %o4 /* IEU0 Group */
+1: andcc %o1, 1, %g0 /* IEU1 */
+ be,a,pn %icc, 1f /* CTI */
+ clr %o5 /* IEU0 Group */
+ ldub [%o0], %o5 /* Load */
+ sll %o5, 8, %o5 /* IEU0 Group */
+1: or %g2, %o4, %o4 /* IEU1 */
+ or %o5, %o4, %o4 /* IEU0 Group (regdep) */
+ addcc %o4, %o2, %o2 /* IEU1 Group (regdep) */
+ bcs,a,pn %xcc, 1f /* CTI */
+ add %o2, 1, %o2 /* IEU0 */
+1: ba,pt %xcc, 25b /* CTI Group */
+ sllx %o2, 32, %g1 /* IEU0 */
diff --git a/arch/sparc64/lib/VISmemset.S b/arch/sparc64/lib/VISmemset.S
new file mode 100644
index 000000000..d674f2a6e
--- /dev/null
+++ b/arch/sparc64/lib/VISmemset.S
@@ -0,0 +1,228 @@
+/* $Id: VISmemset.S,v 1.1 1997/07/18 06:26:49 ralf 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 d0f023d1b..59083aa02 100644
--- a/arch/sparc64/lib/blockops.S
+++ b/arch/sparc64/lib/blockops.S
@@ -1,138 +1,70 @@
-/* $Id: blockops.S,v 1.6 1997/05/18 04:16:49 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 <asm/asi.h>
-
- /* 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;
-
- /* Copy 256 bytes of memory at (src + offset) to
- * (dst + offset).
- */
-#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;
+#include "VIS.h"
.text
- .align 4
-
-#if 0
- .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
-
- membar #Sync|#LoadStore|#StoreStore
-
- retl
- mov %o1, %o0
-#endif
+ .align 32
.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];
+__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
+
+ /* Cannot perform real arithmatic on the pattern, that can
+ * lead to fp_exception_other ;-)
+ */
+ 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
- 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
+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
-#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
+ jmpl %o7 + 0x8, %g0 ! CTI Group brk forced
+ wr %g0, 0, %fprs ! FPU Group
- retl
- membar #Sync|#LoadStore|#StoreStore
-#endif
+ .align 32
+ .globl __bzero_1page
+__bzero_1page:
+ 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 10eebb8df..703370fc6 100644
--- a/arch/sparc64/lib/checksum.S
+++ b/arch/sparc64/lib/checksum.S
@@ -17,383 +17,398 @@
#include <asm/head.h>
#include <asm/ptrace.h>
#include <asm/asi.h>
+#include <asm/page.h>
-#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;
-
-#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;
-
- /* Do end cruft out of band to get better cache patterns. */
-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
-
- /* Also do alignment out of band to get better cache patterns. */
-csum_partial_fix_alignment:
-
- /* The common case is to get called with a nicely aligned
- * buffer of size 0x20. Follow the code path for that case.
+ /* 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. This is already
+ * done for csum_partial, needs to be written for the copy stuff
+ * still.
*/
- .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
+ .text
.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 +416,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 +426,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 +460,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 +489,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 196435aed..000000000
--- 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 <asm/ptrace.h>
-#include <asm/asi.h>
-#include <asm/head.h>
-
-#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 cc6db141f..000000000
--- 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 <asm/ptrace.h>
-#include <asm/head.h>
-#include <asm/asi.h>
-
-#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 e9462345a..000000000
--- 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 <asm/asi.h>
-#include <asm/head.h>
-
-#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 713c78ca8..000000000
--- a/arch/sparc64/lib/memset.S
+++ /dev/null
@@ -1,196 +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 <asm/asi.h>
-#include <asm/ptrace.h>
-
-#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/lib/strlen_user.S b/arch/sparc64/lib/strlen_user.S
index 4d57aed64..ef6cee5a6 100644
--- a/arch/sparc64/lib/strlen_user.S
+++ b/arch/sparc64/lib/strlen_user.S
@@ -20,36 +20,27 @@ __strlen_user:
andcc %o0, 3, %g0
be,pt %icc, 9f
sethi %hi(HI_MAGIC), %o4
-10:
- lduba [%o0] ASI_S, %o5
+10: lduba [%o0] ASI_S, %o5
brz,pn %o5, 21f
add %o0, 1, %o0
andcc %o0, 3, %g0
be,pn %icc, 4f
or %o4, %lo(HI_MAGIC), %o3
-11:
- lduba [%o0] ASI_S, %o5
+11: lduba [%o0] ASI_S, %o5
brz,pn %o5, 22f
add %o0, 1, %o0
andcc %o0, 3, %g0
- be,pt %icc, 5f
- sethi %hi(LO_MAGIC), %o4
-12:
- lduba [%o0] ASI_S, %o5
+ be,pt %icc, 13f
+ srl %o3, 7, %o2
+12: lduba [%o0] ASI_S, %o5
brz,pn %o5, 23f
add %o0, 1, %o0
- ba,pt %icc, 13f
- or %o4, %lo(LO_MAGIC), %o2
-9:
- or %o4, %lo(HI_MAGIC), %o3
-4:
- sethi %hi(LO_MAGIC), %o4
-5:
- or %o4, %lo(LO_MAGIC), %o2
-13:
- lda [%o0] ASI_S, %o5
-2:
- sub %o5, %o2, %o4
+ ba,pt %icc, 2f
+15: lda [%o0] ASI_S, %o5
+9: or %o4, %lo(HI_MAGIC), %o3
+4: srl %o3, 7, %o2
+13: lda [%o0] ASI_S, %o5
+2: sub %o5, %o2, %o4
andcc %o4, %o3, %g0
be,pt %icc, 13b
add %o0, 4, %o0
@@ -69,20 +60,15 @@ __strlen_user:
add %o4, 1, %o4
andcc %o5, 0xff, %g0
bne,a,pt %icc, 2b
-14:
- lda [%o0] ASI_S, %o5
+14: lda [%o0] ASI_S, %o5
add %o4, 1, %o4
-1:
- retl
+1: retl
sub %o4, %o1, %o0
-21:
- retl
+21: retl
mov 1, %o0
-22:
- retl
+22: retl
mov 2, %o0
-23:
- retl
+23: retl
mov 3, %o0
.section .fixup,#alloc,#execinstr
@@ -97,5 +83,6 @@ __strlen_user:
.xword 10b, 30b
.xword 11b, 30b
.xword 12b, 30b
+ .xword 15b, 30b
.xword 13b, 30b
.xword 14b, 30b
diff --git a/arch/sparc64/mm/Makefile b/arch/sparc64/mm/Makefile
index c41c7a938..dddf3153b 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 6df923a4b..5e16e1218 100644
--- a/arch/sparc64/mm/fault.c
+++ b/arch/sparc64/mm/fault.c
@@ -1,4 +1,4 @@
-/* $Id: fault.c,v 1.11 1997/06/01 05:46:15 davem Exp $
+/* $Id: fault.c,v 1.18 1997/07/17 02:20:56 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,44 +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 */
-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;
-
-#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");
- __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)
@@ -204,40 +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
-#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_dentry->d_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 289ddd411..730e8cb32 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 960b3cbbd..a8d903b25 100644
--- a/arch/sparc64/mm/init.c
+++ b/arch/sparc64/mm/init.c
@@ -1,15 +1,17 @@
-/* $Id: init.c,v 1.29 1997/05/27 06:28:13 davem 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 <linux/config.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/blk.h>
#include <linux/swap.h>
+#include <asm/head.h>
#include <asm/system.h>
#include <asm/page.h>
#include <asm/pgtable.h>
@@ -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))
{
@@ -440,9 +512,9 @@ void sparc_ultra_mapioaddr(unsigned long physaddr, unsigned long virt_addr,
physaddr &= PAGE_MASK;
if(rdonly)
- pte = mk_pte_phys(physaddr, __pgprot(pg_iobits));
+ pte = mk_pte_phys(physaddr, __pgprot(pg_iobits | __PRIV_BITS));
else
- pte = mk_pte_phys(physaddr, __pgprot(pg_iobits | __DIRTY_BITS));
+ pte = mk_pte_phys(physaddr, __pgprot(pg_iobits | __DIRTY_BITS | __PRIV_BITS));
set_pte(ptep, pte);
}
@@ -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 000000000..a0eba8019
--- /dev/null
+++ b/arch/sparc64/mm/modutil.c
@@ -0,0 +1,66 @@
+/* $Id: modutil.c,v 1.1 1997/07/18 06:26:54 ralf 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 <linux/malloc.h>
+#include <linux/vmalloc.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/vaddrs.h>
+
+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 000000000..b11903a25
--- /dev/null
+++ b/arch/sparc64/mm/ultra.S
@@ -0,0 +1,226 @@
+/* $Id: ultra.S,v 1.1 1997/07/18 06:26:55 ralf Exp $
+ * ultra.S: Don't expand these all over the place...
+ *
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <asm/asi.h>
+#include <asm/spitfire.h>
+
+ /* 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
+
+#ifdef __SMP__
+ /* These are all called by the slaves of a cross call, at
+ * trap level 1, with interrupts fully disabled.
+ *
+ * Register usage:
+ * %g5 mm->context (all tlb flushes)
+ * %g6 address arg 1 (tlb page and range flushes)
+ * %g7 address arg 2 (tlb range flush only)
+ *
+ * %g1 ivector table, don't touch
+ * %g2 scratch 1
+ * %g3 scratch 2
+ * %g4 scratch 3
+ *
+ * NOTE: We do not acknowledge the UPA until we are done
+ * with the service. This is what tells the master
+ * that he can consider the effects of the flush
+ * "complete" on this cpu.
+ */
+ .align 32
+ .globl xcall_flush_tlb_page
+xcall_flush_tlb_page:
+ mov SECONDARY_CONTEXT, %g2
+ nop
+ ldxa [%g2] ASI_DMMU, %g3
+ cmp %g3, %g5
+ be,pt %icc, 1f
+ or %g6, 0x10, %g4
+ stxa %g5, [%g2] ASI_DMMU
+1: stxa %g0, [%g4] ASI_DMMU_DEMAP
+
+ be,pt %icc, 1f
+ stxa %g0, [%g4] ASI_IMMU_DEMAP
+ stxa %g3, [%g2] ASI_DMMU
+1: b,pt %xcc, do_ivec_return
+ flush %g1
+
+ .align 32
+ .globl xcall_flush_tlb_mm
+xcall_flush_tlb_mm:
+ mov SECONDARY_CONTEXT, %g2
+ nop
+ ldxa [%g2] ASI_DMMU, %g3
+ cmp %g3, %g5
+ be,pt %icc, 1f
+ mov 0x50, %g4
+ stxa %g5, [%g2] ASI_DMMU
+1: stxa %g0, [%g4] ASI_DMMU_DEMAP
+
+ be,pt %icc, 1f
+ stxa %g0, [%g4] ASI_IMMU_DEMAP
+ stxa %g3, [%g2] ASI_DMMU
+1: b,pt %xcc, do_ivec_return
+ flush %g1
+
+ .align 32
+ .globl xcall_flush_tlb_range
+xcall_flush_tlb_range:
+ sethi %hi(8192 - 1), %g2
+ or %g2, %lo(8192 - 1), %g2
+ andn %g6, %g2, %g6
+ andn %g7, %g2, %g7
+ sub %g7, %g6, %g3
+ add %g2, 1, %g2
+ orcc %g6, 0x50, %g6
+ srlx %g3, 13, %g4
+
+ cmp %g4, 96
+ bgu,pn %icc, xcall_flush_tlb_mm
+ mov SECONDARY_CONTEXT, %g4
+ ldxa [%g4] ASI_DMMU, %g7
+ cmp %g7, %g5
+ be,pt %icc, 1f
+ sub %g3, %g2, %g3
+ stxa %g5, [%g4] ASI_DMMU
+
+1: stxa %g0, [%g6 + %g3] ASI_DMMU_DEMAP
+ stxa %g0, [%g6 + %g3] ASI_IMMU_DEMAP
+ brnz,pt %g3, 1b
+ sub %g3, %g2, %g3
+ bne,a,pn %icc, 1f
+ stxa %g7, [%g4] ASI_DMMU
+1: b,pt %xcc, do_ivec_return
+ flush %g1
+
+ /* These two are not performance critical... */
+ .globl xcall_flush_tlb_all
+xcall_flush_tlb_all:
+ clr %g2
+ clr %g3
+1: ldxa [%g3] ASI_DTLB_DATA_ACCESS, %g4
+ and %g4, _PAGE_L, %g5
+ brnz,pn %g5, 2f
+ mov TLB_TAG_ACCESS, %g7
+ stxa %g0, [%g7] ASI_DMMU
+ membar #Sync
+
+ stxa %g0, [%g3] ASI_DTLB_DATA_ACCESS
+ membar #Sync
+2: ldxa [%g3] ASI_ITLB_DATA_ACCESS, %g4
+ and %g4, _PAGE_L, %g5
+ brnz,pn %g5, 2f
+ mov TLB_TAG_ACCESS, %g7
+ stxa %g0, [%g7] ASI_IMMU
+ membar #Sync
+
+ stxa %g0, [%g3] ASI_ITLB_DATA_ACCESS
+2: add %g2, 1, %g2
+ cmp %g2, 63
+ ble,pt %icc, 1b
+ sll %g2, 3, %g3
+ b,pt %xcc, do_ivec_return
+ flush %g1
+
+ .globl xcall_flush_cache_all
+xcall_flush_cache_all:
+ sethi %hi(16383), %g2
+ or %g2, %lo(16383), %g2
+ clr %g3
+1: stxa %g0, [%g3] ASI_IC_TAG
+ add %g3, 32, %g3
+ cmp %g3, %g2
+ bleu,pt %xcc, 1b
+ nop
+ b,pt %xcc, do_ivec_return
+ flush %g1
+#endif /* __SMP__ */
diff --git a/arch/sparc64/prom/bootstr.c b/arch/sparc64/prom/bootstr.c
index e226c6e95..7ef17159d 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 <linux/string.h>
+#include <linux/init.h>
#include <asm/oplib.h>
#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 fe9bf9c6b..8b738fd41 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.8 1997/07/14 23:45:28 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 <linux/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
@@ -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(); */
@@ -132,3 +133,10 @@ void prom_set_trap_table(unsigned long tba)
{
p1275_cmd("SUNW,set-trap-table", P1275_INOUT(1, 0), tba);
}
+
+#ifdef __SMP__
+void prom_start_cpu(int cpunode, unsigned long pc, unsigned long o0)
+{
+ p1275_cmd("SUNW,start-cpu", P1275_INOUT(3, 0), cpunode, pc, o0);
+}
+#endif
diff --git a/arch/sparc64/prom/p1275.c b/arch/sparc64/prom/p1275.c
index 3eb0311df..18de40deb 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 <asm/spitfire.h>
#include <asm/pstate.h>
-/* 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 d2d0cac34..eac8314ca 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 3f90981fd..d46336e34 100644
--- a/drivers/block/floppy.c
+++ b/drivers/block/floppy.c
@@ -2897,6 +2897,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/block/ide.c b/drivers/block/ide.c
index 1b293c539..61b792f74 100644
--- a/drivers/block/ide.c
+++ b/drivers/block/ide.c
@@ -2602,6 +2602,7 @@ __initfunc(static void probe_for_hwifs (void))
ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371_0, &ide_init_triton, 1);
ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_1, &ide_init_triton, 0);
ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB, &ide_init_triton, 0);
+ ide_probe_pci (PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_1, &ide_init_triton, 0);
#endif /* CONFIG_BLK_DEV_TRITON */
#ifdef CONFIG_BLK_DEV_OPTI621
ide_probe_pci (PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C621, &ide_init_opti621, 0);
diff --git a/drivers/block/ide.h b/drivers/block/ide.h
index 95724e6e9..ecfdc12ea 100644
--- a/drivers/block/ide.h
+++ b/drivers/block/ide.h
@@ -302,7 +302,7 @@ typedef void (ide_selectproc_t) (ide_drive_t *);
typedef enum { ide_unknown, ide_generic, ide_triton,
ide_cmd640, ide_dtc2278, ide_ali14xx,
ide_qd6580, ide_umc8672, ide_ht6560b,
- ide_promise }
+ ide_promise, ide_via }
hwif_chipset_t;
typedef struct hwif_s {
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 53fef9b74..9bb78d63e 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -292,7 +292,7 @@ static int loop_set_fd(struct loop_device *lo, kdev_t dev, unsigned int arg)
return -EBADF;
if (lo->lo_inode)
return -EBUSY;
- inode = file->f_inode;
+ inode = file->f_dentry->d_inode;
if (!inode) {
printk("loop_set_fd: NULL inode?!?\n");
return -EINVAL;
@@ -318,7 +318,7 @@ static int loop_set_fd(struct loop_device *lo, kdev_t dev, unsigned int arg)
}
lo->lo_inode = inode;
- atomic_inc(&lo->lo_inode->i_count);
+ lo->lo_inode->i_count++;
lo->transfer = NULL;
figure_loop_size(lo);
MOD_INC_USE_COUNT;
diff --git a/drivers/block/md.c b/drivers/block/md.c
index 12cb6dcf0..8f8ba19d5 100644
--- a/drivers/block/md.c
+++ b/drivers/block/md.c
@@ -202,10 +202,10 @@ static int do_md_stop (int minor, struct inode *inode)
{
int i;
- if (atomic_read(&inode->i_count)>1 || md_dev[minor].busy>1) /* ioctl : one open channel */
+ if (inode->i_count > 1 || md_dev[minor].busy>1) /* ioctl : one open channel */
{
printk ("STOP_MD md%x failed : i_count=%d, busy=%d\n", minor,
- atomic_read(&inode->i_count), md_dev[minor].busy);
+ inode->i_count, md_dev[minor].busy);
return -EBUSY;
}
diff --git a/drivers/block/rd.c b/drivers/block/rd.c
index 4ac6c0c41..7d7a3931c 100644
--- a/drivers/block/rd.c
+++ b/drivers/block/rd.c
@@ -360,10 +360,10 @@ identify_ramdisk_image(kdev_t device, struct file *fp, int start_block))
* Read block 0 to test for gzipped kernel
*/
if (fp->f_op->llseek)
- fp->f_op->llseek(fp->f_inode, fp, start_block * BLOCK_SIZE, 0);
+ fp->f_op->llseek(fp->f_dentry->d_inode, fp, start_block * BLOCK_SIZE, 0);
fp->f_pos = start_block * BLOCK_SIZE;
- fp->f_op->read(fp->f_inode, fp, buf, size);
+ fp->f_op->read(fp->f_dentry->d_inode, fp, buf, size);
/*
* If it matches the gzip magic numbers, return -1
@@ -390,11 +390,11 @@ identify_ramdisk_image(kdev_t device, struct file *fp, int start_block))
* Read block 1 to test for minix and ext2 superblock
*/
if (fp->f_op->llseek)
- fp->f_op->llseek(fp->f_inode, fp,
+ fp->f_op->llseek(fp->f_dentry->d_inode, fp,
(start_block+1) * BLOCK_SIZE, 0);
fp->f_pos = (start_block+1) * BLOCK_SIZE;
- fp->f_op->read(fp->f_inode, fp, buf, size);
+ fp->f_op->read(fp->f_dentry->d_inode, fp, buf, size);
/* Try minix */
if (minixsb->s_magic == MINIX_SUPER_MAGIC ||
@@ -421,7 +421,7 @@ identify_ramdisk_image(kdev_t device, struct file *fp, int start_block))
done:
if (fp->f_op->llseek)
- fp->f_op->llseek(fp->f_inode, fp, start_block * BLOCK_SIZE, 0);
+ fp->f_op->llseek(fp->f_dentry->d_inode, fp, start_block * BLOCK_SIZE, 0);
fp->f_pos = start_block * BLOCK_SIZE;
if ((nblocks > 0) && blk_size[MAJOR(device)]) {
@@ -444,8 +444,9 @@ done:
*/
__initfunc(static void rd_load_image(kdev_t device,int offset))
{
- struct inode inode, out_inode;
+ struct inode inode, out_inode;
struct file infile, outfile;
+ struct dentry in_dentry, out_dentry;
unsigned short fs;
kdev_t ram_device;
int nblocks, i;
@@ -457,15 +458,19 @@ __initfunc(static void rd_load_image(kdev_t device,int offset))
memset(&infile, 0, sizeof(infile));
memset(&inode, 0, sizeof(inode));
+ memset(&in_dentry, 0, sizeof(in_dentry));
inode.i_rdev = device;
infile.f_mode = 1; /* read only */
- infile.f_inode = &inode;
+ infile.f_dentry = &in_dentry;
+ in_dentry.d_inode = &inode;
memset(&outfile, 0, sizeof(outfile));
memset(&out_inode, 0, sizeof(out_inode));
+ memset(&out_dentry, 0, sizeof(out_dentry));
out_inode.i_rdev = ram_device;
outfile.f_mode = 3; /* read/write */
- outfile.f_inode = &out_inode;
+ outfile.f_dentry = &out_dentry;
+ out_dentry.d_inode = &out_inode;
if (blkdev_open(&inode, &infile) != 0) return;
if (blkdev_open(&out_inode, &outfile) != 0) return;
@@ -506,9 +511,9 @@ __initfunc(static void rd_load_image(kdev_t device,int offset))
printk(KERN_NOTICE "RAMDISK: Loading %d blocks into ram disk... ", nblocks);
for (i=0; i < nblocks; i++) {
- infile.f_op->read(infile.f_inode, &infile, buf,
+ infile.f_op->read(infile.f_dentry->d_inode, &infile, buf,
BLOCK_SIZE);
- outfile.f_op->write(outfile.f_inode, &outfile, buf,
+ outfile.f_op->write(outfile.f_dentry->d_inode, &outfile, buf,
BLOCK_SIZE);
if (!(i % 16)) {
printk("%c\b", rotator[rotate & 0x3]);
@@ -637,7 +642,7 @@ __initfunc(static int fill_inbuf(void))
{
if (exit_code) return -1;
- insize = crd_infp->f_op->read(crd_infp->f_inode, crd_infp,
+ insize = crd_infp->f_op->read(crd_infp->f_dentry->d_inode, crd_infp,
inbuf, INBUFSIZ);
if (insize == 0) return -1;
@@ -656,7 +661,7 @@ __initfunc(static void flush_window(void))
unsigned n;
uch *in, ch;
- crd_outfp->f_op->write(crd_outfp->f_inode, crd_outfp, window,
+ crd_outfp->f_op->write(crd_outfp->f_dentry->d_inode, crd_outfp, window,
outcnt);
in = window;
for (n = 0; n < outcnt; n++) {
diff --git a/drivers/block/triton.c b/drivers/block/triton.c
index 7956307c8..006051af6 100644
--- a/drivers/block/triton.c
+++ b/drivers/block/triton.c
@@ -122,15 +122,25 @@ static unsigned int piix_key;
#define PIIX_FLAGS_PREFETCH 4
#define PIIX_FLAGS_FAST_DMA 8
-typedef struct {
- unsigned d0_flags :4;
- unsigned d1_flags :4;
- unsigned recovery :2;
- unsigned reserved :2;
- unsigned sample :2;
- unsigned sidetim_enabled:1;
- unsigned ports_enabled :1;
-} piix_timing_t;
+
+union chip_en_reg_u {
+ struct {
+ unsigned d0_flags :4;
+ unsigned d1_flags :4;
+ unsigned recovery :2;
+ unsigned reserved :2;
+ unsigned sample :2;
+ unsigned sidetim_enabled:1;
+ unsigned ports_enabled :1;
+ } piix_s;
+ struct {
+ unsigned sec_en :1;
+ unsigned pri_en :1;
+ unsigned reserved :14;
+ } via_s;
+};
+
+typedef union chip_en_reg_u piix_timing_t;
typedef struct {
unsigned pri_recovery :2;
@@ -269,16 +279,16 @@ static int piix_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
printk("%s: pcibios read failed\n", HWIF(drive)->name);
return 1;
}
- dflags = drive->select.b.unit ? timing.d1_flags : timing.d0_flags;
+ dflags = drive->select.b.unit ? timing.piix_s.d1_flags : timing.piix_s.d0_flags;
if (dflags & PIIX_FLAGS_FAST_PIO) {
if (func == ide_dma_on && drive->media == ide_disk)
dflags |= PIIX_FLAGS_FAST_DMA;
else
dflags &= ~PIIX_FLAGS_FAST_DMA;
if (drive->select.b.unit == 0)
- timing.d0_flags = dflags;
+ timing.piix_s.d0_flags = dflags;
else
- timing.d1_flags = dflags;
+ timing.piix_s.d1_flags = dflags;
if (pcibios_write_config_word(piix_pci_bus, piix_pci_fn, reg, *(short *)&timing)) {
printk("%s: pcibios write failed\n", HWIF(drive)->name);
return 1;
@@ -456,8 +466,14 @@ void ide_init_triton (byte bus, byte fn)
chipset = "PIIX4";
else if (devid == PCI_DEVICE_ID_INTEL_82371SB_1)
chipset = "PIIX3";
- else
+ else if (devid == PCI_DEVICE_ID_INTEL_82371_1)
chipset = "PIIX";
+ else if (devid == PCI_DEVICE_ID_VIA_82C586_1)
+ chipset = "VP1";
+ else {
+ printk("Unknown PCI IDE interface 0x%x\n", devid);
+ goto quit;
+ }
printk("%s: bus-master IDE device on PCI bus %d function %d\n", chipset, bus, fn);
@@ -470,13 +486,24 @@ void ide_init_triton (byte bus, byte fn)
printk("%s: IDE ports are not enabled (BIOS)\n", chipset);
goto quit;
}
- if ((rc = pcibios_read_config_word(bus, fn, 0x40, (short *)&timings[0])))
- goto quit;
- if ((rc = pcibios_read_config_word(bus, fn, 0x42, (short *)&timings[1])))
- goto quit;
- if ((!timings[0].ports_enabled) && (!timings[1].ports_enabled)) {
- printk("%s: neither IDE port is enabled\n", chipset);
- goto quit;
+ if (devid == PCI_DEVICE_ID_VIA_82C586_1) {
+ /* pri and sec channel enables are in port 0x40 */
+ if ((rc = pcibios_read_config_word(bus, fn, 0x40, (short *)&timings[0])))
+ goto quit;
+ if ((!timings[0].via_s.pri_en && (!timings[0].via_s.sec_en))) {
+ printk("%s: neither IDE port is enabled\n", chipset);
+ goto quit;
+ }
+ }
+ else { /* INTEL piix */
+ if ((rc = pcibios_read_config_word(bus, fn, 0x40, (short *)&timings[0])))
+ goto quit;
+ if ((rc = pcibios_read_config_word(bus, fn, 0x42, (short *)&timings[1])))
+ goto quit;
+ if ((!timings[0].piix_s.ports_enabled) && (!timings[1].piix_s.ports_enabled)) {
+ printk("%s: neither IDE port is enabled\n", chipset);
+ goto quit;
+ }
}
/*
@@ -526,10 +553,30 @@ void ide_init_triton (byte bus, byte fn)
case 0x170: pri_sec = 1; break;
default: continue;
}
+
+ if (devid == PCI_DEVICE_ID_VIA_82C586_1) {
+ timing = timings[0];
+ switch (h) {
+ case 0:
+ if (!timing.piix_s.ports_enabled) {
+ printk("port 0 DMA not enabled\n");
+ continue;
+ }
+ case 1:
+ if (!timing.piix_s.sidetim_enabled) {
+ printk("port 1 DMA not enabled\n");
+ continue;
+ }
+ }
+ hwif->chipset = ide_via;
+ }
+ else { /* PIIX */
+
timing = timings[pri_sec];
- if (!timing.ports_enabled) /* interface disabled? */
+ if (!timing.piix_s.ports_enabled) /* interface disabled? */
continue;
hwif->chipset = ide_triton;
+ }
if (dma_enabled)
init_piix_dma(hwif, bmiba + (pri_sec ? 8 : 0));
#ifdef DISPLAY_PIIX_TIMINGS
@@ -539,17 +586,32 @@ void ide_init_triton (byte bus, byte fn)
{
const char *slave;
piix_sidetim_t sidetim;
- byte sample = 5 - timing.sample;
- byte recovery = 4 - timing.recovery;
+ byte sample = 5 - timing.piix_s.sample;
+ byte recovery = 4 - timing.piix_s.recovery;
+ unsigned int drvtim;
+
+ if (devid == PCI_DEVICE_ID_VIA_82C586_1) {
+ pcibios_read_config_dword(bus, fn, 0x48, &drvtim);
+ if (pri_sec == 0) {
+ printk(" %s master: active_pulse_CLKs=%d, recovery_CLKs=%d\n", hwif->name, 1+(drvtim>>28), 1+((drvtim & 0x0f000000)>>24));
+ printk(" %s slave: active_pulse_CLKs=%d, recovery_CLKs=%d\n", hwif->name, 1+((drvtim & 0xf00000)>>20), 1+((drvtim & 0x0f0000)>>16));
+ continue;
+ } else {
+ printk(" %s master: active_pulse_CLKs=%d, recovery_CLKs=%d\n", hwif->name, 1+((drvtim & 0xf000)>>12), 1+((drvtim & 0x0f00)>>8));
+ printk(" %s slave: active_pulse_CLKs=%d, recovery_CLKs=%d\n", hwif->name, 1+((drvtim & 0xf0)>>4), 1+(drvtim & 0x0f));
+ continue;
+ }
+ }
+
if ((devid == PCI_DEVICE_ID_INTEL_82371SB_1
|| devid == PCI_DEVICE_ID_INTEL_82371AB)
- && timing.sidetim_enabled
+ && timing.piix_s.sidetim_enabled
&& !pcibios_read_config_byte(bus, fn, 0x44, (byte *) &sidetim))
slave = ""; /* PIIX3 and later */
else
slave = "/slave"; /* PIIX, or PIIX3 in compatibility mode */
printk(" %s master%s: sample_CLKs=%d, recovery_CLKs=%d\n", hwif->name, slave, sample, recovery);
- print_piix_drive_flags ("master:", timing.d0_flags);
+ print_piix_drive_flags ("master:", timing.piix_s.d0_flags);
if (!*slave) {
if (pri_sec == 0) {
sample = 5 - sidetim.pri_sample;
@@ -560,7 +622,7 @@ void ide_init_triton (byte bus, byte fn)
}
printk(" slave : sample_CLKs=%d, recovery_CLKs=%d\n", sample, recovery);
}
- print_piix_drive_flags ("slave :", timing.d1_flags);
+ print_piix_drive_flags ("slave :", timing.piix_s.d1_flags);
}
#endif /* DISPLAY_PIIX_TIMINGS */
}
diff --git a/drivers/char/ChangeLog b/drivers/char/ChangeLog
index 6a0009511..3083c0c54 100644
--- a/drivers/char/ChangeLog
+++ b/drivers/char/ChangeLog
@@ -1,3 +1,27 @@
+Thu Jun 19 20:05:58 1997 Theodore Ts'o <tytso@rsts-11.mit.edu>
+
+ * serial.c (begin_break, end_break, rs_ioctl): Applied patch
+ to support BSD ioctls to set and clear the break
+ condition explicitly.
+
+ * console.c (scrup, scrdown, insert_line, delete_line): Applied
+ fix suggested by Aaron Tiensivu to speed up block scrolls
+ up and down.
+
+ * n_tty.c (opost_block, write_chan): Added a modified "fast
+ console" patch which processes a block of text via
+ "cooking" efficiently.
+
+Wed Jun 18 15:25:50 1997 Theodore Ts'o <tytso@rsts-11.mit.edu>
+
+ * tty_io.c (init_dev, release_dev): Applied fix suggested by Bill
+ Hawes to prevent race conditions in the tty code.
+
+ * n_tty.c (n_tty_chars_in_buffer): Applied fix suggested by Bill
+ Hawes so that n_tty_chars_in_buffer returns the correct
+ value in the case when the tty is in cannonical mode. (To
+ avoid a pty deadlock with telnetd.)
+
Thu Feb 27 01:53:08 1997 Theodore Ts'o <tytso@rsts-11.mit.edu>
* serial.c (change_speed): Add support for the termios flag
diff --git a/drivers/char/Config.in b/drivers/char/Config.in
index 4216b3154..c02b376e8 100644
--- a/drivers/char/Config.in
+++ b/drivers/char/Config.in
@@ -52,6 +52,7 @@ if [ "$CONFIG_MOUSE" = "y" ]; then
if [ "$CONFIG_PSMOUSE" != "n" ]; then
bool 'C&T 82C710 mouse port support (as on TI Travelmate)' CONFIG_82C710_MOUSE
fi
+ tristate 'PC110 digitizer pad support' CONFIG_PC110_PAD
fi
if [ "$CONFIG_MODULES" = "y" ]; then
@@ -97,4 +98,5 @@ if [ "$CONFIG_WATCHDOG" != "n" ]; then
tristate ' Berkshire Products PC Watchdog' CONFIG_PCWATCHDOG
fi
bool 'Enhanced Real Time Clock Support' CONFIG_RTC
+tristate 'PC joystick support' CONFIG_JOYSTICK
endmenu
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 14efcf554..73c1ac599 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -145,6 +145,14 @@ else
endif
endif
+ifeq ($(CONFIG_JOYSTICK),y)
+L_OBJS += joystick.o
+else
+ ifeq ($(CONFIG_JOYSTICK),m)
+ M_OBJS += joystick.o
+ endif
+endif
+
ifeq ($(CONFIG_MS_BUSMOUSE),y)
M = y
L_OBJS += msbusmouse.o
@@ -218,6 +226,16 @@ ifdef CONFIG_SUN_MOUSE
M = y
endif
+ifeq ($(CONFIG_PC110_PAD),y)
+M = y
+L_OBJS += pc110pad.o
+else
+ ifeq ($(CONFIG_PC110_PAD),m)
+ M_OBJS += pc110pad.o
+ MM = m
+ endif
+endif
+
ifeq ($(CONFIG_SUN_OPENPROMIO),y)
M = y
else
diff --git a/drivers/char/console.c b/drivers/char/console.c
index 61ab64aac..b24def4a2 100644
--- a/drivers/char/console.c
+++ b/drivers/char/console.c
@@ -514,13 +514,15 @@ static void set_origin(int currcons)
__set_origin(__real_origin);
}
-static void scrup(int currcons, unsigned int t, unsigned int b)
+static void scrup(int currcons, unsigned int t, unsigned int b, unsigned int nr)
{
int hardscroll = hardscroll_enabled;
- if (b > video_num_lines || t >= b)
+ if (t+nr >= b)
+ nr = b - t - 1;
+ if (b > video_num_lines || t >= b || nr < 1)
return;
- if (t || b != video_num_lines)
+ if (t || b != video_num_lines || nr > 1)
hardscroll = 0;
if (hardscroll) {
origin += video_size_row;
@@ -575,31 +577,35 @@ static void scrup(int currcons, unsigned int t, unsigned int b)
set_origin(currcons);
} else {
unsigned short * d = (unsigned short *) (origin+video_size_row*t);
- unsigned short * s = (unsigned short *) (origin+video_size_row*(t+1));
+ unsigned short * s = (unsigned short *) (origin+video_size_row*(t+nr));
- memcpyw(d, s, (b-t-1) * video_size_row);
- memsetw(d + (b-t-1) * video_num_columns, video_erase_char, video_size_row);
+ memcpyw(d, s, (b-t-nr) * video_size_row);
+ memsetw(d + (b-t-nr) * video_num_columns, video_erase_char, video_size_row*nr);
}
}
static void
-scrdown(int currcons, unsigned int t, unsigned int b)
+scrdown(int currcons, unsigned int t, unsigned int b, unsigned int nr)
{
unsigned short *s;
unsigned int count;
+ unsigned int step;
- if (b > video_num_lines || t >= b)
+ if (t+nr >= b)
+ nr = b - t - 1;
+ if (b > video_num_lines || t >= b || nr < 1)
return;
- s = (unsigned short *) (origin+video_size_row*(b-2));
- if (b >= t + 1) {
- count = b - t - 1;
- while (count) {
- count--;
- memcpyw(s + video_num_columns, s, video_size_row);
- s -= video_num_columns;
- }
+ s = (unsigned short *) (origin+video_size_row*(b-nr-1));
+ step = video_num_columns * nr;
+ count = b - t - nr;
+ while (count--) {
+ memcpyw(s + step, s, video_size_row);
+ s -= video_num_columns;
+ }
+ while (nr--) {
+ s += video_num_columns;
+ memsetw(s, video_erase_char, video_size_row);
}
- memsetw(s + video_num_columns, video_erase_char, video_size_row);
has_scrolled = 1;
}
@@ -609,7 +615,7 @@ static void lf(int currcons)
* if below scrolling region
*/
if (y+1 == bottom)
- scrup(currcons,top,bottom);
+ scrup(currcons,top,bottom, 1);
else if (y < video_num_lines-1) {
y++;
pos += video_size_row;
@@ -623,7 +629,7 @@ static void ri(int currcons)
* if above scrolling region
*/
if (y == top)
- scrdown(currcons,top,bottom);
+ scrdown(currcons,top,bottom,1);
else if (y > 0) {
y--;
pos -= video_size_row;
@@ -1148,9 +1154,9 @@ static void insert_char(int currcons)
need_wrap = 0;
}
-static void insert_line(int currcons)
+static void insert_line(int currcons, unsigned int nr)
{
- scrdown(currcons,y,bottom);
+ scrdown(currcons,y,bottom,nr);
need_wrap = 0;
}
@@ -1167,9 +1173,9 @@ static void delete_char(int currcons)
need_wrap = 0;
}
-static void delete_line(int currcons)
+static void delete_line(int currcons, unsigned int nr)
{
- scrup(currcons,y,bottom);
+ scrup(currcons,y,bottom,nr);
need_wrap = 0;
}
@@ -1189,8 +1195,7 @@ static void csi_L(int currcons, unsigned int nr)
nr = video_num_lines;
else if (!nr)
nr = 1;
- while (nr--)
- insert_line(currcons);
+ insert_line(currcons, nr);
}
static void csi_P(int currcons, unsigned int nr)
@@ -1209,8 +1214,7 @@ static void csi_M(int currcons, unsigned int nr)
nr = video_num_lines;
else if (!nr)
nr=1;
- while (nr--)
- delete_line(currcons);
+ delete_line(currcons, nr);
}
static void save_cur(int currcons)
diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c
index efa895a69..94969bf28 100644
--- a/drivers/char/cyclades.c
+++ b/drivers/char/cyclades.c
@@ -1,6 +1,6 @@
#define BLOCKMOVE
static char rcsid[] =
-"$Revision: 1.36.4.27 $$Date: 1997/03/26 10:30:00 $";
+"$Revision: 1.1.1.1 $$Date: 1997/06/01 03:17:29 $";
/*
* linux/drivers/char/cyclades.c
@@ -29,6 +29,9 @@ static char rcsid[] =
* void cleanup_module(void);
*
* $Log: cyclades.c,v $
+ * Revision 1.1.1.1 1997/06/01 03:17:29 ralf
+ * Initial import of Linux/MIPS pre-2.1.40.
+ *
* Revision 1.36.4.27 1997/03/26 10:30:00 daniel
* Changed for suport linux versions 2.1.X.
* Backward compatible with linux versions 2.0.X.
@@ -98,7 +101,7 @@ static char rcsid[] =
* after -Y stuff (to make changes clearer)
*
* Revision 1.36.4.12 1996/07/11 15:40:55 bentson
- * Add code to poll Cyclom-Z. Add code to get & set RS-232 control.
+ * Add code to poll Cyclades-Z. Add code to get & set RS-232 control.
* Add code to send break. Clear firmware ID word at startup (so
* that other code won't talk to inactive board).
*
@@ -391,7 +394,13 @@ static char rcsid[] =
constant in the definition below. No other change is necessary to
support more boards/ports. */
-#define NR_PORTS 64
+//#define NR_PORTS 64
+#define NR_PORTS 128
+
+#define ZE_V1_NPORTS 64
+#define ZO_V1 0
+#define ZO_V2 1
+#define ZE_V1 2
#define SERIAL_PARANOIA_CHECK
#undef SERIAL_DEBUG_OPEN
@@ -453,35 +462,51 @@ static char rcsid[] =
#include <linux/kernel.h>
#include <linux/bios32.h>
#include <linux/pci.h>
-#include <linux/init.h>
#include <linux/version.h>
-#if LINUX_VERSION_CODE >= 131328
+#if (LINUX_VERSION_CODE >= 0x020100)
#include <asm/uaccess.h>
+#include <linux/init.h>
-#define memcpy_fromfs copy_from_user
-#define memcpy_tofs copy_to_user
-#define put_fs_long put_user
-#define vremap ioremap
+#define cy_put_user put_user
-static unsigned long get_fs_long(unsigned long *addr)
+static unsigned long cy_get_user(unsigned long *addr)
{
unsigned long result = 0;
int error = get_user (result, addr);
if (error)
- printk ("cyclades: get_fs_long: error == %d\n", error);
+ printk ("cyclades: cy_get_user: error == %d\n", error);
return result;
}
+#else
+
+#define __initfunc(__arginit) __arginit
+#define copy_from_user memcpy_fromfs
+#define copy_to_user memcpy_tofs
+#define cy_get_user get_fs_long
+#define cy_put_user put_fs_long
+#define ioremap vremap
+
#endif
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
+
#define IS_CYC_Z(card) ((card).num_chips == 1)
+#define Z_FPGA_CHECK(card) \
+ ((((struct RUNTIME_9060 *)((card).ctl_addr))->init_ctrl&(1<<17))!=0)
+
+#define ISZLOADED(card) (((ZO_V1==((struct RUNTIME_9060 *) \
+ ((card).ctl_addr))->mail_box_0) || \
+ Z_FPGA_CHECK(card)) && \
+ (ZFIRM_ID==((struct FIRM_ID *) \
+ ((card).base_addr+ID_ADDRESS))->signature))
+
#define WAKEUP_CHARS (SERIAL_XMIT_SIZE-256)
#define STD_COM_FLAGS (0)
@@ -548,7 +573,7 @@ static struct cyclades_card *IRQ_cards[16];
/*
* tmp_buf is used as a temporary buffer by serial_write. We need to
- * lock it in case the memcpy_fromfs blocks while swapping in a page,
+ * lock it in case the copy_from_user blocks while swapping in a page,
* and some other program tries to do a serial 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
@@ -698,10 +723,12 @@ static void CP4(int data)
{ (data<10)? CP(data+'0'): CP(data+'A'-10); }/* CP4 */
static void CP8(int data)
{ CP4((data>>4) & 0x0f); CP4( data & 0x0f); }/* CP8 */
+#if 0
static void CP16(int data)
{ CP8((data>>8) & 0xff); CP8(data & 0xff); }/* CP16 */
static void CP32(long data)
{ CP16((data>>16) & 0xffff); CP16(data & 0xffff); }/* CP32 */
+#endif
/*
@@ -758,6 +785,7 @@ do_softint(void *private_)
if (!tty)
return;
+#if (LINUX_VERSION_CODE >= 0x020125)
if (test_and_clear_bit(Cy_EVENT_HANGUP, &info->event)) {
tty_hangup(info->tty);
wake_up_interruptible(&info->open_wait);
@@ -774,6 +802,24 @@ do_softint(void *private_)
}
wake_up_interruptible(&tty->write_wait);
}
+#else
+ if (clear_bit(Cy_EVENT_HANGUP, &info->event)) {
+ tty_hangup(info->tty);
+ wake_up_interruptible(&info->open_wait);
+ info->flags &= ~(ASYNC_NORMAL_ACTIVE|
+ ASYNC_CALLOUT_ACTIVE);
+ }
+ if (clear_bit(Cy_EVENT_OPEN_WAKEUP, &info->event)) {
+ wake_up_interruptible(&info->open_wait);
+ }
+ if (clear_bit(Cy_EVENT_WRITE_WAKEUP, &info->event)) {
+ if((tty->flags & (1<< TTY_DO_WRITE_WAKEUP))
+ && tty->ldisc.write_wakeup){
+ (tty->ldisc.write_wakeup)(tty);
+ }
+ wake_up_interruptible(&tty->write_wait);
+ }
+#endif
} /* do_softint */
@@ -1351,7 +1397,7 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
/***********************************************************/
/********* End of block of Cyclom-Y specific code **********/
-/******** Start of block of Cyclom-Z specific code *********/
+/******** Start of block of Cyclades-Z specific code *********/
/***********************************************************/
@@ -1365,7 +1411,7 @@ cyz_fetch_msg( struct cyclades_card *cinfo,
unsigned long loc_doorbell;
firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS);
- if (firm_id->signature != ZFIRM_ID){
+ if (!ISZLOADED(*cinfo)){
return (-1);
}
zfw_ctrl = (struct ZFW_CTRL *)
@@ -1397,7 +1443,7 @@ cyz_issue_cmd( struct cyclades_card *cinfo,
int index;
firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS);
- if (firm_id->signature != ZFIRM_ID){
+ if (!ISZLOADED(*cinfo)){
return (-1);
}
zfw_ctrl = (struct ZFW_CTRL *)
@@ -1408,7 +1454,7 @@ cyz_issue_cmd( struct cyclades_card *cinfo,
pci_doorbell = &((struct RUNTIME_9060 *)
(cinfo->ctl_addr))->pci_doorbell;
while( (*pci_doorbell & 0xff) != 0){
- if (index++ == 100){
+ if (index++ == 1000){
return(-1);
}
udelay(50L);
@@ -1421,6 +1467,7 @@ cyz_issue_cmd( struct cyclades_card *cinfo,
} /* cyz_issue_cmd */
+#if 0
static int
cyz_update_channel( struct cyclades_card *cinfo,
u_long channel, u_char mode, u_char cmd)
@@ -1430,7 +1477,7 @@ cyz_update_channel( struct cyclades_card *cinfo,
struct ZFW_CTRL *zfw_ctrl;
struct CH_CTRL *ch_ctrl;
- if (firm_id->signature != ZFIRM_ID){
+ if (!ISZLOADED(*cinfo)){
return (-1);
}
zfw_ctrl =
@@ -1442,6 +1489,7 @@ cyz_update_channel( struct cyclades_card *cinfo,
return cyz_issue_cmd(cinfo, channel, cmd, 0L);
} /* cyz_update_channel */
+#endif
static void
@@ -1467,6 +1515,7 @@ cyz_poll(unsigned long arg)
u_long channel;
u_char cmd;
u_long *param;
+ u_long hw_ver, fw_ver;
cyz_timerlist.expires = jiffies + 100;
@@ -1476,7 +1525,7 @@ cyz_poll(unsigned long arg)
firm_id = (struct FIRM_ID *)
(cinfo->base_addr + ID_ADDRESS);
- if (firm_id->signature != ZFIRM_ID){
+ if (!ISZLOADED(*cinfo)){
continue;
}
@@ -1484,6 +1533,8 @@ cyz_poll(unsigned long arg)
(struct ZFW_CTRL *)
(cinfo->base_addr + firm_id->zfwctrl_addr);
board_ctrl = &zfw_ctrl->board_ctrl;
+ fw_ver = board_ctrl->fw_version;
+ hw_ver = ((struct RUNTIME_9060 *)(cinfo->ctl_addr))->mail_box_0;
while( cyz_fetch_msg( cinfo, &channel, &cmd, &param) == 1){
char_count = 0;
@@ -1514,7 +1565,9 @@ cyz_poll(unsigned long arg)
break;
case C_CM_MDCD:
if (info->flags & ASYNC_CHECK_CD){
- if( ch_ctrl[channel].rs_status & C_RS_DCD){
+ if (((hw_ver != 0 || fw_ver > 241)
+ ? ((u_long)param)
+ : ch_ctrl[channel].rs_status) & C_RS_DCD) {
/* SP("Open Wakeup\n"); */
cy_sched_event(info,
Cy_EVENT_OPEN_WAKEUP);
@@ -1709,7 +1762,7 @@ cyz_poll(unsigned long arg)
} /* cyz_poll */
-/********** End of block of Cyclom-Z specific code *********/
+/********** End of block of Cyclades-Z specific code *********/
/***********************************************************/
@@ -1794,7 +1847,7 @@ startup(struct cyclades_port * info)
base_addr = (unsigned char*) (cy_card[card].base_addr);
firm_id = (struct FIRM_ID *) (base_addr + ID_ADDRESS);
- if (firm_id->signature != ZFIRM_ID){
+ if (!ISZLOADED(cy_card[card])){
return -ENODEV;
}
@@ -1951,7 +2004,7 @@ shutdown(struct cyclades_port * info)
#endif
firm_id = (struct FIRM_ID *) (base_addr + ID_ADDRESS);
- if (firm_id->signature != ZFIRM_ID){
+ if (!ISZLOADED(cy_card[card])) {
return;
}
@@ -2143,7 +2196,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp,
base_addr = (char *)(cinfo->base_addr);
firm_id = (struct FIRM_ID *)
(base_addr + ID_ADDRESS);
- if (firm_id->signature != ZFIRM_ID){
+ if (!ISZLOADED(*cinfo)){
return -EINVAL;
}
@@ -2237,11 +2290,17 @@ cy_open(struct tty_struct *tty, struct file * filp)
will make the user pay attention.
*/
if (IS_CYC_Z(cy_card[info->card])) {
- struct FIRM_ID *firm_id =
- (struct FIRM_ID *)
- (cy_card[info->card].base_addr + ID_ADDRESS);
- if (firm_id->signature != ZFIRM_ID){
- printk("Cyclom-Z firmware not yet loaded\n");
+ if (!ISZLOADED(cy_card[info->card])) {
+ if (((ZE_V1 ==((struct RUNTIME_9060 *)
+ ((cy_card[info->card]).ctl_addr))->mail_box_0) &&
+ Z_FPGA_CHECK(cy_card[info->card])) &&
+ (ZFIRM_HLT==((struct FIRM_ID *)
+ ((cy_card[info->card]).base_addr+ID_ADDRESS))->signature))
+ {
+ printk ("Cyclades-Z Error: you need an external power supply for this number of ports.\n\rFirmware halted.\r\n");
+ } else {
+ printk("Cyclades-Z firmware not yet loaded\n");
+ }
return -ENODEV;
}
}
@@ -2448,7 +2507,7 @@ cy_write(struct tty_struct * tty, int from_user,
}
if (from_user) {
- memcpy_fromfs(tmp_buf, buf, c);
+ copy_from_user(tmp_buf, buf, c);
c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
SERIAL_XMIT_SIZE - info->xmit_head));
memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
@@ -2816,7 +2875,7 @@ set_line_char(struct cyclades_port * info)
firm_id = (struct FIRM_ID *)
(cy_card[card].base_addr + ID_ADDRESS);
- if (firm_id->signature != ZFIRM_ID){
+ if (!ISZLOADED(cy_card[card])) {
return;
}
@@ -2956,7 +3015,7 @@ get_serial_info(struct cyclades_port * info,
tmp.baud_base = info->baud;
tmp.custom_divisor = 0; /*!!!*/
tmp.hub6 = 0; /*!!!*/
- memcpy_tofs(retinfo,&tmp,sizeof(*retinfo));
+ copy_to_user(retinfo,&tmp,sizeof(*retinfo));
return 0;
} /* get_serial_info */
@@ -2970,7 +3029,7 @@ set_serial_info(struct cyclades_port * info,
if (!new_info)
return -EFAULT;
- memcpy_fromfs(&new_serial,new_info,sizeof(new_serial));
+ copy_from_user(&new_serial,new_info,sizeof(new_serial));
old_info = *info;
if (!suser()) {
@@ -3051,7 +3110,7 @@ get_modem_info(struct cyclades_port * info, unsigned int *value)
firm_id = (struct FIRM_ID *)
(cy_card[card].base_addr + ID_ADDRESS);
- if (firm_id->signature == ZFIRM_ID){
+ if (ISZLOADED(cy_card[card])) {
zfw_ctrl =
(struct ZFW_CTRL *)
(cy_card[card].base_addr + firm_id->zfwctrl_addr);
@@ -3070,7 +3129,7 @@ get_modem_info(struct cyclades_port * info, unsigned int *value)
}
}
- put_fs_long(result,(unsigned long *) value);
+ cy_put_user(result,(unsigned long *) value);
return 0;
} /* get_modem_info */
@@ -3082,7 +3141,7 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd,
int card,chip,channel,index;
unsigned char *base_addr;
unsigned long flags;
- unsigned int arg = get_fs_long((unsigned long *) value);
+ unsigned int arg = cy_get_user((unsigned long *) value);
struct FIRM_ID *firm_id;
struct ZFW_CTRL *zfw_ctrl;
struct BOARD_CTRL *board_ctrl;
@@ -3180,7 +3239,7 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd,
firm_id = (struct FIRM_ID *)
(cy_card[card].base_addr + ID_ADDRESS);
- if (firm_id->signature == ZFIRM_ID){
+ if (ISZLOADED(cy_card[card])) {
zfw_ctrl =
(struct ZFW_CTRL *)
(cy_card[card].base_addr + firm_id->zfwctrl_addr);
@@ -3281,7 +3340,7 @@ static int
get_mon_info(struct cyclades_port * info, struct cyclades_monitor * mon)
{
- memcpy_tofs(mon, &info->mon, sizeof(struct cyclades_monitor));
+ copy_to_user(mon, &info->mon, sizeof(struct cyclades_monitor));
info->mon.int_count = 0;
info->mon.char_count = 0;
info->mon.char_max = 0;
@@ -3335,7 +3394,7 @@ get_threshold(struct cyclades_port * info, unsigned long *value)
+ (cy_chip_offset[chip]<<index));
tmp = base_addr[CyCOR3<<index] & CyREC_FIFO;
- put_fs_long(tmp,value);
+ cy_put_user(tmp,value);
} else {
// Nothing to do!
}
@@ -3354,7 +3413,7 @@ set_default_threshold(struct cyclades_port * info, unsigned long value)
static int
get_default_threshold(struct cyclades_port * info, unsigned long *value)
{
- put_fs_long(info->default_threshold,value);
+ cy_put_user(info->default_threshold,value);
return 0;
}/* get_default_threshold */
@@ -3401,7 +3460,7 @@ get_timeout(struct cyclades_port * info, unsigned long *value)
+ (cy_chip_offset[chip]<<index));
tmp = base_addr[CyRTPR<<index];
- put_fs_long(tmp,value);
+ cy_put_user(tmp,value);
} else {
// Nothing to do!
}
@@ -3420,7 +3479,7 @@ set_default_timeout(struct cyclades_port * info, unsigned long value)
static int
get_default_timeout(struct cyclades_port * info, unsigned long *value)
{
- put_fs_long(info->default_timeout,value);
+ cy_put_user(info->default_timeout,value);
return 0;
}/* get_default_timeout */
@@ -3530,7 +3589,7 @@ cy_ioctl(struct tty_struct *tty, struct file * file,
ret_val = error;
break;
}
- put_fs_long(C_CLOCAL(tty) ? 1 : 0,
+ cy_put_user(C_CLOCAL(tty) ? 1 : 0,
(unsigned long *) arg);
break;
case TIOCSSOFTCAR:
@@ -3541,7 +3600,7 @@ cy_ioctl(struct tty_struct *tty, struct file * file,
break;
}
- arg = get_fs_long((unsigned long *) arg);
+ arg = cy_get_user((unsigned long *) arg);
tty->termios->c_cflag =
((tty->termios->c_cflag & ~CLOCAL) |
(arg ? CLOCAL : 0));
@@ -3967,8 +4026,8 @@ cy_detect_isa(void))
}
/* probe for CD1400... */
-#if LINUX_VERSION_CODE >= 131328
- cy_isa_address = vremap((unsigned int)cy_isa_address,0x2000);
+#if (LINUX_VERSION_CODE >= 0x020100)
+ cy_isa_address = ioremap((unsigned int)cy_isa_address,0x2000);
#endif
cy_isa_nchan = 4 * cyy_init_card(cy_isa_address,0);
if (cy_isa_nchan == 0) {
@@ -3988,6 +4047,7 @@ cy_detect_isa(void))
printk("Cyclom-Y/ISA found at 0x%x ",
(unsigned int) cy_isa_address);
printk("but no more channels are available.\n");
+ printk("Change NR_PORTS in cyclades.c and recompile kernel.\n");
return(nboard);
}
/* fill the next cy_card structure available */
@@ -3998,6 +4058,7 @@ cy_detect_isa(void))
printk("Cyclom-Y/ISA found at 0x%x ",
(unsigned int) cy_isa_address);
printk("but no more cards can be used .\n");
+ printk("Change NR_CARDS in cyclades.c and recompile kernel.\n");
return(nboard);
}
@@ -4051,6 +4112,8 @@ cy_detect_pci(void))
unsigned int cy_pci_addr0, cy_pci_addr1, cy_pci_addr2;
unsigned short i,j,cy_pci_nchan;
unsigned short device_id,dev_index = 0,board_index = 0;
+ unsigned long mailbox;
+ unsigned int Ze_addr0[NR_CARDS], Ze_addr2[NR_CARDS], ZeIndex = 0;
if(pcibios_present() == 0) { /* PCI bus not present */
return(0);
@@ -4070,6 +4133,9 @@ cy_detect_pci(void))
}
}
+ if (device_id == 0)
+ break;
+
/* read PCI configuration area */
pcibios_read_config_byte(cyy_bus, cyy_dev_fn,
PCI_INTERRUPT_LINE, &cy_pci_irq);
@@ -4081,9 +4147,7 @@ cy_detect_pci(void))
PCI_BASE_ADDRESS_2, &cy_pci_addr2);
pcibios_read_config_byte(cyy_bus, cyy_dev_fn,
PCI_REVISION_ID, &cyy_rev_id);
- if (device_id == 0){
- break;
- }else if ((device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo)
+ if ((device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo)
|| (device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi)){
#ifdef CY_PCI_DEBUG
printk("Cyclom-Y/PCI (bus=0x0%x, pci_id=0x%x, ",
@@ -4096,11 +4160,11 @@ cy_detect_pci(void))
cy_pci_addr1 &= 0xfffffffc;
cy_pci_addr2 &= 0xfffffff0;
-#if LINUX_VERSION_CODE < 131328
+#if (LINUX_VERSION_CODE < 0x020100)
if ((ulong)cy_pci_addr2 >= 0x100000) /* above 1M? */
#endif
cy_pci_addr2 =
- (unsigned int) vremap(cy_pci_addr2,CyPCI_Ywin);
+ (unsigned int) ioremap(cy_pci_addr2,CyPCI_Ywin);
#ifdef CY_PCI_DEBUG
printk("Cyclom-Y/PCI: relocate winaddr=0x%x ioaddr=0x%x\n",
@@ -4112,12 +4176,14 @@ cy_detect_pci(void))
printk("Cyclom-Y PCI host card with ");
printk("no Serial-Modules at 0x%x.\n",
(unsigned int) cy_pci_addr2);
+ i--;
continue;
}
if((cy_next_channel+cy_pci_nchan) > NR_PORTS) {
printk("Cyclom-Y/PCI found at 0x%x ",
(unsigned int) cy_pci_addr2);
printk("but no channels are available.\n");
+ printk("Change NR_PORTS in cyclades.c and recompile kernel.\n");
return(i);
}
/* fill the next cy_card structure available */
@@ -4128,6 +4194,7 @@ cy_detect_pci(void))
printk("Cyclom-Y/PCI found at 0x%x ",
(unsigned int) cy_pci_addr2);
printk("but no more cards can be used.\n");
+ printk("Change NR_CARDS in cyclades.c and recompile kernel.\n");
return(i);
}
@@ -4167,65 +4234,101 @@ cy_detect_pci(void))
cy_next_channel += cy_pci_nchan;
}else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Lo){
/* print message */
- printk("Cyclom-Z/PCI (bus=0x0%x, pci_id=0x%x, ",
+ printk("Cyclades-Z/PCI (bus=0x0%x, pci_id=0x%x, ",
cyy_bus, cyy_dev_fn);
printk("rev_id=%d) IRQ%d\n",
cyy_rev_id, (int)cy_pci_irq);
- printk("Cyclom-Z/PCI: found winaddr=0x%x ctladdr=0x%x\n",
+ printk("Cyclades-Z/PCI: found winaddr=0x%x ctladdr=0x%x\n",
cy_pci_addr2, cy_pci_addr0);
- printk("Cyclom-Z/PCI not supported for low addresses\n");
+ printk("Cyclades-Z/PCI not supported for low addresses\n");
break;
}else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Hi){
#ifdef CY_PCI_DEBUG
- printk("Cyclom-Z/PCI (bus=0x0%x, pci_id=0x%x, ",
+ printk("Cyclades-Z/PCI (bus=0x0%x, pci_id=0x%x, ",
cyy_bus, cyy_dev_fn);
printk("rev_id=%d) IRQ%d\n",
cyy_rev_id, (int)cy_pci_irq);
- printk("Cyclom-Z/PCI: found winaddr=0x%x ctladdr=0x%x\n",
+ printk("Cyclades-Z/PCI: found winaddr=0x%x ctladdr=0x%x\n",
cy_pci_addr2, cy_pci_addr0);
#endif
- cy_pci_addr2 &= 0xfffffff0;
- cy_pci_addr2 = (unsigned int) vremap(
- cy_pci_addr2 & PAGE_MASK,
- PAGE_ALIGN(CyPCI_Zwin))
- + (cy_pci_addr2 & (PAGE_SIZE-1));
cy_pci_addr0 &= 0xfffffff0;
- cy_pci_addr0 = (unsigned int) vremap(
+ cy_pci_addr0 = (unsigned int) ioremap(
cy_pci_addr0 & PAGE_MASK,
PAGE_ALIGN(CyPCI_Zctl))
+ (cy_pci_addr0 & (PAGE_SIZE-1));
+
+ mailbox = ((struct RUNTIME_9060 *) cy_pci_addr0)->mail_box_0;
+ cy_pci_addr2 &= 0xfffffff0;
+ if (mailbox == ZE_V1) {
+ cy_pci_addr2 = (unsigned int) ioremap(
+ cy_pci_addr2 & PAGE_MASK,
+ PAGE_ALIGN(CyPCI_Ze_win))
+ + (cy_pci_addr2 & (PAGE_SIZE-1));
+ if (ZeIndex == NR_CARDS) {
+ printk("Cyclades-Z/PCI found at 0x%x ",
+ (unsigned int) cy_pci_addr2);
+ printk("but no more cards can be used.\n");
+ printk("Change NR_CARDS in cyclades.c and recompile kernel.\n");
+ } else {
+ Ze_addr0[ZeIndex] = cy_pci_addr0;
+ Ze_addr2[ZeIndex] = cy_pci_addr2;
+ ZeIndex++;
+ }
+ i--;
+ continue;
+ } else {
+ cy_pci_addr2 = (unsigned int) ioremap(
+ cy_pci_addr2 & PAGE_MASK,
+ PAGE_ALIGN(CyPCI_Zwin))
+ + (cy_pci_addr2 & (PAGE_SIZE-1));
+ }
+
#ifdef CY_PCI_DEBUG
- printk("Cyclom-Z/PCI: relocate winaddr=0x%x ctladdr=0x%x\n",
+ printk("Cyclades-Z/PCI: relocate winaddr=0x%x ctladdr=0x%x\n",
cy_pci_addr2, cy_pci_addr0);
- ((struct RUNTIME_9060 *)(cy_pci_addr0))
+ if (mailbox == ZO_V1) {
+ ((struct RUNTIME_9060 *)(cy_pci_addr0))
->loc_addr_base = WIN_CREG;
- PAUSE
- printk("Cyclom-Z/PCI: FPGA id %lx, ver %lx\n",
- 0xff & ((struct CUSTOM_REG *)(cy_pci_addr2))->fpga_id,
- 0xff & ((struct CUSTOM_REG *)(cy_pci_addr2))->fpga_version);
- ((struct RUNTIME_9060 *)(cy_pci_addr0))
- ->loc_addr_base = WIN_RAM;
+ PAUSE
+ printk("Cyclades-Z/PCI: FPGA id %lx, ver %lx\n",
+ 0xff & ((struct CUSTOM_REG *)(cy_pci_addr2))->fpga_id,
+ 0xff & ((struct CUSTOM_REG *)(cy_pci_addr2))->fpga_version);
+ ((struct RUNTIME_9060 *)(cy_pci_addr0))
+ ->loc_addr_base = WIN_RAM;
+ } else {
+ printk("Cyclades-Z/PCI: New Cyclades-Z board. FPGA not loaded\n");
+ }
#endif
/* The following clears the firmware id word. This ensures
that the driver will not attempt to talk to the board
until it has been properly initialized.
*/
PAUSE
- *(unsigned long *)(cy_pci_addr2+ID_ADDRESS) = 0L;
+ if (mailbox == ZO_V1)
+ *(unsigned long *)(cy_pci_addr2+ID_ADDRESS) = 0L;
- /* This must be a Cyclom-8Zo/PCI. The extendable
+ /* This must be a Cyclades-8Zo/PCI. The extendable
version will have a different device_id and will
be allocated its maximum number of ports. */
cy_pci_nchan = 8;
+ if((cy_next_channel+cy_pci_nchan) > NR_PORTS) {
+ printk("Cyclades-Z/PCI found at 0x%x ",
+ (unsigned int) cy_pci_addr2);
+ printk("but no channels are available.\n");
+ printk("Change NR_PORTS in cyclades.c and recompile kernel.\n");
+ return(i);
+ }
+
/* fill the next cy_card structure available */
for (j = 0 ; j < NR_CARDS ; j++) {
if (cy_card[j].base_addr == 0) break;
}
if (j == NR_CARDS) { /* no more cy_cards available */
- printk("Cyclom-Z/PCI found at 0x%x ",
+ printk("Cyclades-Z/PCI found at 0x%x ",
(unsigned int) cy_pci_addr2);
printk("but no more cards can be used.\n");
+ printk("Change NR_CARDS in cyclades.c and recompile kernel.\n");
return(i);
}
@@ -4235,9 +4338,9 @@ cy_detect_pci(void))
SA_INTERRUPT,"cyclomZ",NULL))
{
printk("Could not allocate IRQ%d ",
- (unsigned int) cy_pci_addr2);
- printk("for Cyclom-Z/PCI at 0x%x.\n",
cy_pci_irq);
+ printk("for Cyclades-Z/PCI at 0x%x.\n",
+ (unsigned int) cy_pci_addr2);
return(i);
}
}
@@ -4254,12 +4357,12 @@ cy_detect_pci(void))
/* print message */
/* don't report IRQ if board is no IRQ */
if( (cy_pci_irq < 15) && (cy_pci_irq > 1) ) {
- printk("Cyclom-Z/PCI #%d: 0x%x-0x%x, IRQ%d, ",
+ printk("Cyclades-Z/PCI #%d: 0x%x-0x%x, IRQ%d, ",
j+1,cy_pci_addr2,
(cy_pci_addr2 + CyPCI_Zwin - 1),
(int)cy_pci_irq);
}else{
- printk("Cyclom-Z/PCI #%d: 0x%x-0x%x, ",
+ printk("Cyclades-Z/PCI #%d: 0x%x-0x%x, ",
j+1,cy_pci_addr2,
(cy_pci_addr2 + CyPCI_Zwin - 1));
}
@@ -4268,6 +4371,93 @@ cy_detect_pci(void))
cy_next_channel += cy_pci_nchan;
}
}
+
+ for (; ZeIndex != 0 && i < NR_CARDS; i++) {
+ cy_pci_addr0 = Ze_addr0[0];
+ cy_pci_addr2 = Ze_addr2[0];
+ for (j = 0 ; j < ZeIndex-1 ; j++) {
+ Ze_addr0[j] = Ze_addr0[j+1];
+ Ze_addr2[j] = Ze_addr2[j+1];
+ }
+ ZeIndex--;
+ mailbox = ((struct RUNTIME_9060 *) cy_pci_addr0)->mail_box_0;
+#ifdef CY_PCI_DEBUG
+ printk("Cyclades-Z/PCI: relocate winaddr=0x%x ctladdr=0x%x\n",
+ cy_pci_addr2, cy_pci_addr0);
+ printk("Cyclades-Z/PCI: New Cyclades-Z board. FPGA not loaded\n");
+#endif
+ /* The following clears the firmware id word. This ensures
+ that the driver will not attempt to talk to the board
+ until it has been properly initialized.
+ */
+ PAUSE
+ /* This must be the new Cyclades-Ze/PCI. */
+ cy_pci_nchan = ZE_V1_NPORTS;
+
+ if((cy_next_channel+cy_pci_nchan) > NR_PORTS) {
+ printk("Cyclades-Z/PCI found at 0x%x ",
+ (unsigned int) cy_pci_addr2);
+ printk("but no channels are available.\n");
+ printk("Change NR_PORTS in cyclades.c and recompile kernel.\n");
+ return(i);
+ }
+
+ /* fill the next cy_card structure available */
+ for (j = 0 ; j < NR_CARDS ; j++) {
+ if (cy_card[j].base_addr == 0) break;
+ }
+ if (j == NR_CARDS) { /* no more cy_cards available */
+ printk("Cyclades-Z/PCI found at 0x%x ",
+ (unsigned int) cy_pci_addr2);
+ printk("but no more cards can be used.\n");
+ printk("Change NR_CARDS in cyclades.c and recompile kernel.\n");
+ return(i);
+ }
+
+ /* allocate IRQ only if board has an IRQ */
+ if( (1 < cy_pci_irq) && (cy_pci_irq < 15) ) {
+ if(request_irq(cy_pci_irq,cyz_interrupt,
+ SA_INTERRUPT,"cyclomZ",NULL))
+ {
+ printk("Could not allocate IRQ%d ",
+ cy_pci_irq);
+ printk("for Cyclades-Z/PCI at 0x%x.\n",
+ (unsigned int) cy_pci_addr2);
+ return(i);
+ }
+ }
+
+ /* set cy_card */
+ cy_card[j].base_addr = cy_pci_addr2;
+ cy_card[j].ctl_addr = cy_pci_addr0;
+ cy_card[j].irq = (int) cy_pci_irq;
+ cy_card[j].bus_index = 1;
+ cy_card[j].first_line = cy_next_channel;
+ cy_card[j].num_chips = 1;
+ IRQ_cards[cy_pci_irq] = &cy_card[j];
+
+ /* print message */
+ /* don't report IRQ if board is no IRQ */
+ if( (cy_pci_irq < 15) && (cy_pci_irq > 1) ) {
+ printk("Cyclades-Z/PCI #%d: 0x%x-0x%x, IRQ%d, ",
+ j+1,cy_pci_addr2,
+ (cy_pci_addr2 + CyPCI_Ze_win - 1),
+ (int)cy_pci_irq);
+ }else{
+ printk("Cyclades-Z/PCI #%d: 0x%x-0x%x, ",
+ j+1,cy_pci_addr2,
+ (cy_pci_addr2 + CyPCI_Ze_win - 1));
+ }
+ printk("%d channels starting from port %d.\n",
+ cy_pci_nchan,cy_next_channel);
+ cy_next_channel += cy_pci_nchan;
+ }
+ if (ZeIndex != 0) {
+ printk("Cyclades-Z/PCI found at 0x%x ",
+ (unsigned int) Ze_addr2[0]);
+ printk("but no more cards can be used.\n");
+ printk("Change NR_CARDS in cyclades.c and recompile kernel.\n");
+ }
return(i);
#else
return(0);
@@ -4319,6 +4509,8 @@ cy_init(void))
struct cyclades_card *cinfo;
int number_z_boards = 0;
int board,port,i;
+ unsigned long mailbox;
+ int nports;
show_version();
@@ -4416,10 +4608,13 @@ cy_init(void))
/* initialize per-port data structures for each valid board found */
for (board = 0 ; board < cy_nboard ; board++) {
cinfo = &cy_card[board];
- if (cinfo->num_chips == 1){ /* Cyclom-8Zo/PCI */
+ if (cinfo->num_chips == 1){ /* Cyclades-8Zo/PCI */
number_z_boards++;
+ mailbox = ((struct RUNTIME_9060 *)
+ cy_card[board].ctl_addr)->mail_box_0;
+ nports = (mailbox == ZE_V1) ? ZE_V1_NPORTS : 8;
for (port = cinfo->first_line ;
- port < cinfo->first_line + 8;
+ port < cinfo->first_line + nports;
port++)
{
info = &cy_port[port];
@@ -4523,7 +4718,7 @@ cy_init(void))
cyz_timerlist.expires = jiffies + 1;
add_timer(&cyz_timerlist);
#ifdef CY_PCI_DEBUG
- printk("Cyclom-Z polling initialized\n");
+ printk("Cyclades-Z polling initialized\n");
#endif
}
diff --git a/drivers/char/joystick.c b/drivers/char/joystick.c
new file mode 100644
index 000000000..e85d36a98
--- /dev/null
+++ b/drivers/char/joystick.c
@@ -0,0 +1,376 @@
+/*
+
+ linux/drivers/char/joystick.c
+ Copyright (C) 1992, 1993 Arthur C. Smith
+ Joystick driver for Linux running on an IBM compatible computer.
+
+VERSION INFO:
+01/08/93 ACS 0.1: Works but needs multi-joystick support
+01/13/93 ACS 0.2: Added multi-joystick support (minor 0 and 1)
+ Added delay between measuring joystick axis
+ Added scaling ioctl
+02/16/93 ACS 0.3: Modified scaling to use ints to prevent kernel
+ panics 8-)
+02/28/93 ACS 0.4: Linux99.6 and fixed race condition in js_read.
+ After looking at a schematic of a joystick card
+ it became apparent that any write to the joystick
+ port started ALL the joystick one shots. If the
+ one that we are reading is short enough and the
+ first one to be read, the second one will return
+ bad data if it's one shot has not expired when
+ the joystick port is written for the second time.
+ Thus solves the mystery delay problem in 0.2!
+05/05/93 ACS/Eyal 0.5: Upgraded the driver to the 99.9 kernel, added
+ joystick support to the make config options,
+ updated the driver to return the buttons as
+ positive logic, and read both axis at once
+ (thanks Eyal!), and added some new ioctls.
+02/12/94 Jeff Tranter 0.6: Made necessary changes to work with 0.99pl15
+ kernel (and hopefully 1.0). Also did some
+ cleanup: indented code, fixed some typos, wrote
+ man page, etc...
+05/17/95 Dan Fandrich 0.7.3: Added I/O port registration, cleaned up code
+04/03/96 Matt Rhoten 0.8: many minor changes:
+ new read loop from Hal Maney <maney@norden.com>
+ cleaned up #includes to allow #include of
+ joystick.h with gcc -Wall and from g++
+ made js_init fail if it finds zero joysticks
+ general source/comment cleanup
+ use of MOD_(INC|DEC)_USE_COUNT
+ changes from Bernd Schmidt <crux@Pool.Informatik.RWTH-Aachen.DE>
+ to compile correctly under 1.3 in kernel or as module
+06/30/97 Alan Cox 0.9: Ported to 2.1.x
+ Reformatted to resemble Linux coding standard
+ Removed semaphore bug (we can dump the lot I think)
+ Fixed xntp timer adjust during joystick timer0 bug
+ Changed variable names to lower case. Kept binary
+ compatibility.
+ Better ioctl names. Kept binary compatibility.
+ Removed 'save_busy'. Just set busy to 1.
+*/
+
+#include <linux/module.h>
+#include <linux/joystick.h>
+#include <linux/mm.h>
+#include <linux/major.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+static struct js_config js_data[JS_MAX]; /* misc data */
+static int js_exist; /* which joysticks' axis exist? */
+static int js_read_semaphore; /* to prevent two processes from trying
+ to read different joysticks at the
+ same time */
+
+/*
+ * get_timer0():
+ * returns the current value of timer 0. This is a 16 bit counter that starts
+ * at LATCH and counts down to 0
+ */
+
+extern inline int get_timer0(void)
+{
+ unsigned long flags;
+ int t0, t1;
+ save_flags(flags);
+ cli();
+ outb (0, PIT_MODE);
+ t0 = (int) inb (PIT_COUNTER_0);
+ t1 = ((int) inb (PIT_COUNTER_0) << 8) + t0;
+ restore_flags(flags);
+ return (t1);
+}
+
+/*
+ * find_axes():
+ *
+ * returns which axes are hooked up, in a bitfield. 2^n is set if
+ * axis n is hooked up, for 0 <= n < 4.
+ *
+ * REVIEW: should update this to handle eight-axis (four-stick) game port
+ * cards. anyone have one of these to test on? mattrh 3/23/96
+ */
+
+extern inline int find_axes(void)
+{
+ int j;
+ outb (0xff, JS_PORT); /* trigger oneshots */
+ /* and see what happens */
+ for (j = JS_DEF_TIMEOUT; (0x0f & inb (JS_PORT)) && j; j--);
+ /* do nothing; wait for the timeout */
+ js_exist = inb (JS_PORT) & 0x0f; /* get joystick status byte */
+ js_exist = (~js_exist) & 0x0f;
+/* printk("find_axes: js_exist is %d (0x%04X)\n", js_exist, js_exist);*/
+ return js_exist;
+}
+
+static int js_ioctl (struct inode *inode,
+ struct file *file,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ unsigned int minor = MINOR (inode->i_rdev);
+ if (minor >= JS_MAX)
+ return -ENODEV;
+
+ if ((((inb (JS_PORT) & 0x0f) >> (minor << 1)) & 0x03) == 0x03) /*js minor exists?*/
+ return -ENODEV;
+ switch (cmd)
+ {
+
+ case JSIOCSCAL: /*from struct *arg to js_data[minor]*/
+ if(copy_from_user(&js_data[minor].js_corr,
+ (void *)arg, sizeof(struct js_status)))
+ return -EFAULT;
+ break;
+ case JSIOCGCAL: /*to struct *arg from js_data[minor]*/
+ if(copy_to_user((void *) arg, &js_data[minor].js_corr,
+ sizeof(struct js_status)))
+ return -EFAULT;
+ break;
+ case JSIOCSTIMEOUT:
+ if(copy_from_user(&js_data[minor].js_timeout,
+ (void *)arg, sizeof(js_data[0].js_timeout)))
+ return -EFAULT;
+ break;
+ case JSIOCGTIMEOUT:
+ if(copy_to_user((void *)arg, &js_data[minor].js_timeout,
+ sizeof(js_data[0].js_timeout)))
+ return -EFAULT;
+ break;
+ case JSIOCSTIMELIMIT:
+ if(copy_from_user(&js_data[minor].js_timelimit,
+ (void *)arg, sizeof(js_data[0].js_timelimit)))
+ return -EFAULT;
+ break;
+ case JSIOCGTIMELIMIT:
+ if(copy_to_user((void *)arg, &js_data[minor].js_timelimit,
+ sizeof(js_data[0].js_timelimit)))
+ return -EFAULT;
+ break;
+ case JSIOCGCONFIG:
+ if(copy_to_user((void *)arg, &js_data[minor],
+ sizeof(struct js_config)))
+ return -EFAULT;
+ break;
+ case JSIOCSCONFIG:
+ if(copy_from_user(&js_data[minor], (void *)arg,
+ sizeof(struct js_config)))
+ return -EFAULT;
+ /* Must be busy to do this ioctl! */
+ js_data[minor].busy = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * js_open():
+ * device open routine. increments module usage count, initializes
+ * data for that joystick.
+ *
+ * returns: 0 or
+ * -ENODEV: asked for joystick other than #0 or #1
+ * -ENODEV: asked for joystick on axis where there is none
+ * -EBUSY: attempt to open joystick already open
+ */
+
+static int js_open (struct inode *inode, struct file *file)
+{
+ unsigned int minor = MINOR (inode->i_rdev);
+ int j;
+
+ if (minor >= JS_MAX)
+ return -ENODEV; /*check for joysticks*/
+
+ for (j = JS_DEF_TIMEOUT; (js_exist & inb (JS_PORT)) && j; j--);
+ cli(); /*block js_read while js_exist is being modified*/
+ /*js minor exists?*/
+ if ((((js_exist = inb (JS_PORT)) >> (minor << 1)) & 0x03) == 0x03) {
+ js_exist = (~js_exist) & 0x0f;
+ sti();
+ return -ENODEV;
+ }
+ js_exist = (~js_exist) & 0x0f;
+ sti();
+
+ if (js_data[minor].busy)
+ return -EBUSY;
+ js_data[minor].busy = JS_TRUE;
+ js_data[minor].js_corr.x = JS_DEF_CORR; /*default scale*/
+ js_data[minor].js_corr.y = JS_DEF_CORR;
+ js_data[minor].js_timeout = JS_DEF_TIMEOUT;
+ js_data[minor].js_timelimit = JS_DEF_TIMELIMIT;
+ js_data[minor].js_expiretime = jiffies;
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int js_release (struct inode *inode, struct file *file)
+{
+ unsigned int minor = MINOR (inode->i_rdev);
+ inode->i_atime = CURRENT_TIME;
+ js_data[minor].busy = JS_FALSE;
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * js_read() reads the buttons x, and y axis from both joysticks if a
+ * given interval has expired since the last read or is equal to
+ * -1l. The buttons are in port 0x201 in the high nibble. The axis are
+ * read by writing to 0x201 and then measuring the time it takes the
+ * one shots to clear.
+ */
+
+static long js_read (struct inode *inode, struct file *file, char *buf, unsigned long count)
+{
+ int j, chk, jsmask;
+ int t0, t_x0, t_y0, t_x1, t_y1;
+ unsigned int minor, minor2;
+ int buttons;
+
+ if (count != JS_RETURN)
+ return -EINVAL;
+ minor = MINOR (inode->i_rdev);
+ inode->i_atime = CURRENT_TIME;
+ if (jiffies >= js_data[minor].js_expiretime)
+ {
+ minor2 = minor << 1;
+ j = js_data[minor].js_timeout;
+ for (; (js_exist & inb (JS_PORT)) && j; j--);
+ if (j == 0)
+ return -ENODEV; /*no joystick here*/
+ /*Make sure no other proc is using port*/
+
+ cli();
+ js_read_semaphore++;
+ sti();
+
+ buttons = ~(inb (JS_PORT) >> 4);
+ js_data[0].js_save.buttons = buttons & 0x03;
+ js_data[1].js_save.buttons = (buttons >> 2) & 0x03;
+ j = js_data[minor].js_timeout;
+ jsmask = 0;
+
+ cli(); /*no interrupts!*/
+ outb (0xff, JS_PORT); /*trigger one-shots*/
+ /*get init timestamp*/
+ t_x0 = t_y0 = t_x1 = t_y1 = t0 = get_timer0 ();
+ /*wait for an axis' bit to clear or timeout*/
+ while (j-- && (chk = (inb (JS_PORT) & js_exist ) | jsmask))
+ {
+ if (!(chk & JS_X_0)) {
+ t_x0 = get_timer0();
+ jsmask |= JS_X_0;
+ }
+ if (!(chk & JS_Y_0)) {
+ t_y0 = get_timer0();
+ jsmask |= JS_Y_0;
+ }
+ if (!(chk & JS_X_1)) {
+ t_x1 = get_timer0();
+ jsmask |= JS_X_1;
+ }
+ if (!(chk & JS_Y_1)) {
+ t_y1 = get_timer0();
+ jsmask |= JS_Y_1;
+ }
+ }
+ sti(); /* allow interrupts */
+
+ js_read_semaphore = 0; /* allow other reads to progress */
+ if (j == 0)
+ return -ENODEV; /*read timed out*/
+ js_data[0].js_expiretime = jiffies +
+ js_data[0].js_timelimit; /*update data*/
+ js_data[1].js_expiretime = jiffies +
+ js_data[1].js_timelimit;
+ js_data[0].js_save.x = DELTA_TIME (t0, t_x0) >>
+ js_data[0].js_corr.x;
+ js_data[0].js_save.y = DELTA_TIME (t0, t_y0) >>
+ js_data[0].js_corr.y;
+ js_data[1].js_save.x = DELTA_TIME (t0, t_x1) >>
+ js_data[1].js_corr.x;
+ js_data[1].js_save.y = DELTA_TIME (t0, t_y1) >>
+ js_data[1].js_corr.y;
+ }
+
+ if(copy_to_user(buf, &js_data[minor].js_save, JS_RETURN))
+ return -EFAULT;
+ return JS_RETURN;
+}
+
+
+static struct file_operations js_fops =
+{
+ NULL, /* js_lseek*/
+ js_read, /* js_read */
+ NULL, /* js_write*/
+ NULL, /* js_readaddr*/
+ NULL, /* js_select */
+ js_ioctl, /* js_ioctl*/
+ NULL, /* js_mmap */
+ js_open, /* js_open*/
+ js_release, /* js_release*/
+ NULL /* js_fsync */
+};
+
+#ifdef MODULE
+
+#define joystick_init init_module
+
+void cleanup_module (void)
+{
+ if (unregister_chrdev (JOYSTICK_MAJOR, "joystick"))
+ printk ("joystick: cleanup_module failed\n");
+ release_region(JS_PORT, 1);
+}
+
+#endif /* MODULE */
+
+int joystick_init(void)
+{
+ int js_num;
+ int js_count;
+
+ if (check_region(JS_PORT, 1)) {
+ printk("js_init: port already in use\n");
+ return -EBUSY;
+ }
+
+ js_num = find_axes();
+ js_count = !!(js_num & 0x3) + !!(js_num & 0xC);
+
+
+ if (js_count == 0)
+ {
+ printk("No joysticks found.\n");
+ return -ENODEV;
+ /* if the user boots the machine, which runs insmod, and THEN
+ decides to hook up the joystick, well, then we do the wrong
+ thing. But it's a good idea to avoid giving out a false sense
+ of security by letting the module load otherwise. */
+ }
+
+ if (register_chrdev (JOYSTICK_MAJOR, "joystick", &js_fops)) {
+ printk ("Unable to get major=%d for joystick\n",
+ JOYSTICK_MAJOR);
+ return -EBUSY;
+ }
+ request_region(JS_PORT, 1, "joystick");
+
+ for (js_num = 0; js_num < JS_MAX; js_num++)
+ js_data[js_num].busy = JS_FALSE;
+ js_read_semaphore = 0;
+
+ printk (KERN_INFO "Found %d joystick%c.\n",
+ js_count,
+ (js_num == 1) ? ' ' : 's');
+ return 0;
+}
+
diff --git a/drivers/char/lp.c b/drivers/char/lp.c
index 5eb52752d..9f9e5a992 100644
--- a/drivers/char/lp.c
+++ b/drivers/char/lp.c
@@ -724,7 +724,11 @@ __initfunc(int lp_init(void))
return 0;
printk(KERN_INFO "lp: driver loaded but no devices found\n");
+#ifdef MODULE
+ return 0;
+#else
return 1;
+#endif
}
#ifdef MODULE
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 499132bf8..063503595 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -20,6 +20,7 @@
#include <linux/mm.h>
#include <linux/random.h>
#include <linux/init.h>
+#include <linux/joystick.h>
#include <asm/uaccess.h>
#include <asm/io.h>
@@ -134,8 +135,7 @@ static int mmap_mem(struct inode * inode, struct file * file, struct vm_area_str
#endif
if (remap_page_range(vma->vm_start, offset, vma->vm_end - vma->vm_start, vma->vm_page_prot))
return -EAGAIN;
- vma->vm_inode = inode;
- atomic_inc(&inode->i_count);
+ vma->vm_dentry = dget(file->f_dentry);
return 0;
}
@@ -532,6 +532,13 @@ __initfunc(int chr_dev_init(void))
#ifdef CONFIG_SOUND
soundcard_init();
#endif
+#ifdef CONFIG_JOYSTICK
+ /*
+ * Some joysticks only appear when the soundcard they are
+ * connected too is confgured. Keep the sound/joystick ordering.
+ */
+ joystick_init();
+#endif
#if CONFIG_QIC02_TAPE
qic02_tape_init();
#endif
diff --git a/drivers/char/misc.c b/drivers/char/misc.c
index 90ff2026f..6262792b6 100644
--- a/drivers/char/misc.c
+++ b/drivers/char/misc.c
@@ -217,6 +217,9 @@ __initfunc(int misc_init(void))
#ifdef CONFIG_SUN_MOUSE
sun_mouse_init();
#endif
+#ifdef CONFIG_PC110_PAD
+ pc110pad_init();
+#endif
/*
* Only one watchdog can succeed. We probe the pcwatchdog first,
* then the wdt cards and finally the software watchdog which always
diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c
index 8db4e1443..32dc3a51d 100644
--- a/drivers/char/n_tty.c
+++ b/drivers/char/n_tty.c
@@ -88,9 +88,17 @@ void n_tty_flush_buffer(struct tty_struct * tty)
/*
* Return number of characters buffered to be delivered to user
+ *
*/
int n_tty_chars_in_buffer(struct tty_struct *tty)
{
+ if (tty->icanon) {
+ if (!tty->canon_data) return 0;
+
+ return (tty->canon_head > tty->read_tail) ?
+ tty->canon_head - tty->read_tail :
+ tty->canon_head + (N_TTY_BUF_SIZE - tty->read_tail);
+ }
return tty->read_cnt;
}
@@ -157,6 +165,72 @@ static int opost(unsigned char c, struct tty_struct *tty)
return 0;
}
+/*
+ * opost_block --- to speed up block console writes, among other
+ * things.
+ */
+static int opost_block(struct tty_struct * tty,
+ const unsigned char * inbuf, unsigned int nr)
+{
+ char buf[80];
+ int space;
+ int i;
+ char *cp;
+
+ space = tty->driver.write_room(tty);
+ if (!space)
+ return 0;
+ if (nr > space)
+ nr = space;
+ if (nr > sizeof(buf))
+ nr = sizeof(buf);
+ nr -= copy_from_user(buf, inbuf, nr);
+ if (!nr)
+ return 0;
+
+ for (i = 0, cp = buf; i < nr; i++, cp++) {
+ switch (*cp) {
+ case '\n':
+ if (O_ONLRET(tty))
+ tty->column = 0;
+ if (O_ONLCR(tty))
+ goto break_out;
+ tty->canon_column = tty->column;
+ break;
+ case '\r':
+ if (O_ONOCR(tty) && tty->column == 0)
+ goto break_out;
+ if (O_OCRNL(tty)) {
+ *cp = '\n';
+ if (O_ONLRET(tty))
+ tty->canon_column = tty->column = 0;
+ break;
+ }
+ tty->canon_column = tty->column = 0;
+ break;
+ case '\t':
+ goto break_out;
+ case '\b':
+ if (tty->column > 0)
+ tty->column--;
+ break;
+ default:
+ if (O_OLCUC(tty))
+ *cp = toupper(*cp);
+ if (!iscntrl(*cp))
+ tty->column++;
+ break;
+ }
+ }
+break_out:
+ if (tty->driver.flush_chars)
+ tty->driver.flush_chars(tty);
+ i = tty->driver.write(tty, 0, buf, i);
+ return i;
+}
+
+
+
static inline void put_char(unsigned char c, struct tty_struct *tty)
{
tty->driver.put_char(tty, c);
@@ -632,7 +706,7 @@ static void n_tty_set_termios(struct tty_struct *tty, struct termios * old)
return;
tty->icanon = (L_ICANON(tty) != 0);
- if (tty->flags & (1<<TTY_HW_COOK_IN)) {
+ if (test_bit(TTY_HW_COOK_IN, &tty->flags)) {
tty->raw = 1;
tty->real_raw = 1;
return;
@@ -780,7 +854,7 @@ do_it_again:
/* NOTE: not yet done after every sleep pending a thorough
check of the logic of this change. -- jlc */
/* don't stop on /dev/console */
- if (file->f_inode->i_rdev != CONSOLE_DEV &&
+ if (file->f_dentry->d_inode->i_rdev != CONSOLE_DEV &&
current->tty == tty) {
if (tty->pgrp <= 0)
printk("read_chan: tty->pgrp <= 0!\n");
@@ -838,7 +912,7 @@ do_it_again:
tty->minimum_to_wake = (minimum - (b - buf));
if (!input_available_p(tty, 0)) {
- if (tty->flags & (1 << TTY_OTHER_CLOSED)) {
+ if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) {
retval = -EIO;
break;
}
@@ -934,12 +1008,12 @@ static int write_chan(struct tty_struct * tty, struct file * file,
const unsigned char * buf, unsigned int nr)
{
struct wait_queue wait = { current, NULL };
- int c;
+ int c, num;
const unsigned char *b = buf;
int retval = 0;
/* Job control check -- must be done at start (POSIX.1 7.1.1.4). */
- if (L_TOSTOP(tty) && file->f_inode->i_rdev != CONSOLE_DEV) {
+ if (L_TOSTOP(tty) && file->f_dentry->d_inode->i_rdev != CONSOLE_DEV) {
retval = tty_check_change(tty);
if (retval)
return retval;
@@ -956,8 +1030,13 @@ static int write_chan(struct tty_struct * tty, struct file * file,
retval = -EIO;
break;
}
- if (O_OPOST(tty) && !(tty->flags & (1<<TTY_HW_COOK_OUT))) {
+ if (O_OPOST(tty) && !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) {
while (nr > 0) {
+ num = opost_block(tty, b, nr);
+ b += num;
+ nr -= num;
+ if (nr == 0)
+ break;
get_user(c, b);
if (opost(c, tty) < 0)
break;
@@ -993,7 +1072,7 @@ static unsigned int normal_poll(struct tty_struct * tty, struct file * file, pol
mask |= POLLIN | POLLRDNORM;
if (tty->packet && tty->link->ctrl_status)
mask |= POLLPRI | POLLIN | POLLRDNORM;
- if (tty->flags & (1 << TTY_OTHER_CLOSED))
+ if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
mask |= POLLHUP;
if (tty_hung_up_p(file))
mask |= POLLHUP;
diff --git a/drivers/char/pc110pad.c b/drivers/char/pc110pad.c
new file mode 100644
index 000000000..7a6b8cde0
--- /dev/null
+++ b/drivers/char/pc110pad.c
@@ -0,0 +1,690 @@
+/*
+ * Linux driver for the PC110 pad
+ *
+ * The pad provides triples of data. The first byte has
+ * 0x80=bit 8 X, 0x01=bit 7 X, 0x08=bit 8 Y, 0x01=still down
+ * The second byte is bits 0-6 X
+ * The third is bits 0-6 Y
+ *
+ * This is read internally and used to synthesize a stream of
+ * triples in the form expected from a PS/2 device.
+ *
+ * 0.0 1997-05-16 Alan Cox <alan@cymru.net> - Pad reader
+ * 0.1 1997-05-19 Robin O'Leary <robin@acm.org> - PS/2 emulation
+ * 0.2 1997-06-03 Robin O'Leary <robin@acm.org> - tap gesture
+ * 0.3 1997-06-27 Alan Cox <alan@cymru.net> - 2.1 commit
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/busmouse.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/miscdevice.h>
+#include <linux/ptrace.h>
+#include <linux/poll.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+
+#include <asm/signal.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+#include "pc110pad.h"
+
+
+static struct pc110pad_params default_params = {
+ PC110PAD_PS2, /* read mode */
+ 50 MS, /* bounce interval */
+ 200 MS, /* tap interval */
+ 10, /* IRQ */
+ 0x15E0, /* I/O port */
+};
+
+
+static struct pc110pad_params current_params;
+
+
+/* driver/filesystem interface management */
+static struct wait_queue *queue;
+static struct fasync_struct *asyncptr;
+static int active=0; /* number of concurrent open()s */
+
+
+/*
+ * Utility to reset a timer to go off some time in the future.
+ */
+
+static void set_timer_callback(struct timer_list *timer, int ticks)
+{
+ del_timer(timer);
+ timer->expires = jiffies+ticks;
+ add_timer(timer);
+}
+
+
+/*
+ * Take care of letting any waiting processes know that
+ * now would be a good time to do a read(). Called
+ * whenever a state transition occurs, real or synthetic.
+ */
+
+static void wake_readers(void)
+{
+ wake_up_interruptible(&queue);
+ if(asyncptr)
+ kill_fasync(asyncptr, SIGIO);
+}
+
+
+/*****************************************************************************/
+/*
+ * Deal with the messy business of synthesizing button tap and drag
+ * events.
+ *
+ * Exports:
+ * notify_pad_up_down()
+ * Must be called whenever debounced pad up/down state changes.
+ * button_pending
+ * Flag is set whenever read_button() has new values
+ * to return.
+ * read_button()
+ * Obtains the current synthetic mouse button state.
+ */
+
+/*
+ * These keep track of up/down transitions needed to generate the
+ * synthetic mouse button events. While recent_transition is set,
+ * up/down events cause transition_count to increment. tap_timer
+ * turns off the recent_transition flag and may cause some synthetic
+ * up/down mouse events to be created by incrementing synthesize_tap.
+ */
+
+static int button_pending=0;
+static int recent_transition=0;
+static int transition_count=0;
+static int synthesize_tap=0;
+static void tap_timeout(unsigned long data);
+static struct timer_list tap_timer = { NULL, NULL, 0, 0, tap_timeout };
+
+
+/*
+ * This callback goes off a short time after an up/down transition;
+ * before it goes off, transitions will be considered part of a
+ * single PS/2 event and counted in transition_count. Once the
+ * timeout occurs the recent_transition flag is cleared and
+ * any synthetic mouse up/down events are generated.
+ */
+
+static void tap_timeout(unsigned long data)
+{
+ if(!recent_transition)
+ {
+ printk("pc110pad: tap_timeout but no recent transition!\n");
+ }
+ if( transition_count==2 || transition_count==4 || transition_count==6 )
+ {
+ synthesize_tap+=transition_count;
+ button_pending = 1;
+ wake_readers();
+ }
+ recent_transition=0;
+}
+
+
+/*
+ * Called by the raw pad read routines when a (debounced) up/down
+ * transition is detected.
+ */
+
+void notify_pad_up_down(void)
+{
+ if(recent_transition)
+ {
+ transition_count++;
+ }
+ else
+ {
+ transition_count=1;
+ recent_transition=1;
+ }
+ set_timer_callback(&tap_timer, current_params.tap_interval);
+
+ /* changes to transition_count can cause reported button to change */
+ button_pending = 1;
+ wake_readers();
+}
+
+
+static void read_button(int *b)
+{
+ if(synthesize_tap)
+ {
+ *b=--synthesize_tap & 1;
+ }
+ else
+ {
+ *b=(!recent_transition && transition_count==3); /* drag */
+ }
+ button_pending=(synthesize_tap>0);
+}
+
+
+/*****************************************************************************/
+/*
+ * Read pad absolute co-ordinates and debounced up/down state.
+ *
+ * Exports:
+ * pad_irq()
+ * Function to be called whenever the pad signals
+ * that it has new data available.
+ * read_raw_pad()
+ * Returns the most current pad state.
+ * xy_pending
+ * Flag is set whenever read_raw_pad() has new values
+ * to return.
+ * Imports:
+ * wake_readers()
+ * Called when movement occurs.
+ * notify_pad_up_down()
+ * Called when debounced up/down status changes.
+ */
+
+/*
+ * These are up/down state and absolute co-ords read directly from pad
+ */
+
+static int raw_data[3];
+static int raw_data_count=0;
+static int raw_x=0, raw_y=0; /* most recent absolute co-ords read */
+static int raw_down=0; /* raw up/down state */
+static int debounced_down=0; /* up/down state after debounce processing */
+static enum { NO_BOUNCE, JUST_GONE_UP, JUST_GONE_DOWN } bounce=NO_BOUNCE;
+ /* set just after an up/down transition */
+static int xy_pending=0; /* set if new data have not yet been read */
+
+/*
+ * Timer goes off a short while after an up/down transition and copies
+ * the value of raw_down to debounced_down.
+ */
+
+static void bounce_timeout(unsigned long data);
+static struct timer_list bounce_timer = { NULL, NULL, 0, 0, bounce_timeout };
+
+
+static void bounce_timeout(unsigned long data)
+{
+ /*
+ * No further up/down transitions happened within the
+ * bounce period, so treat this as a genuine transition.
+ */
+ switch(bounce)
+ {
+ case NO_BOUNCE:
+ {
+ /*
+ * Strange; the timer callback should only go off if
+ * we were expecting to do bounce processing!
+ */
+ printk("pc110pad, bounce_timeout: bounce flag not set!\n");
+ break;
+ }
+ case JUST_GONE_UP:
+ {
+ /*
+ * The last up we spotted really was an up, so set
+ * debounced state the same as raw state.
+ */
+ bounce=NO_BOUNCE;
+ if(debounced_down==raw_down)
+ {
+ printk("pc110pad, bounce_timeout: raw already debounced!\n");
+ }
+ debounced_down=raw_down;
+
+ notify_pad_up_down();
+ break;
+ }
+ case JUST_GONE_DOWN:
+ {
+ /*
+ * We don't debounce down events, but we still time
+ * out soon after one occurs so we can avoid the (x,y)
+ * skittering that sometimes happens.
+ */
+ bounce=NO_BOUNCE;
+ break;
+ }
+ }
+}
+
+
+/*
+ * Callback when pad's irq goes off; copies values in to raw_* globals;
+ * initiates debounce processing.
+ */
+static void pad_irq(int irq, void *ptr, struct pt_regs *regs)
+{
+
+ /* Obtain byte from pad and prime for next byte */
+ {
+ int value=inb_p(current_params.io);
+ int handshake=inb_p(current_params.io+2);
+ outb_p(handshake | 1, current_params.io+2);
+ outb_p(handshake &~1, current_params.io+2);
+ inb_p(0x64);
+
+ raw_data[raw_data_count++]=value;
+ }
+
+ if(raw_data_count==3)
+ {
+ int new_down=raw_data[0]&0x01;
+ int new_x=raw_data[1];
+ int new_y=raw_data[2];
+ if(raw_data[0]&0x10) new_x+=128;
+ if(raw_data[0]&0x80) new_x+=256;
+ if(raw_data[0]&0x08) new_y+=128;
+
+ if( (raw_x!=new_x) || (raw_y!=new_y) )
+ {
+ raw_x=new_x;
+ raw_y=new_y;
+ xy_pending=1;
+ }
+
+ if(new_down != raw_down)
+ {
+ /* Down state has changed. raw_down always holds
+ * the most recently observed state.
+ */
+ raw_down=new_down;
+
+ /* Forget any earlier bounce processing */
+ if(bounce)
+ {
+ del_timer(&bounce_timer);
+ bounce=NO_BOUNCE;
+ }
+
+ if(new_down)
+ {
+ if(debounced_down)
+ {
+ /* pad gone down, but we were reporting
+ * it down anyway because we suspected
+ * (correctly) that the last up was just
+ * a bounce
+ */
+ }
+ else
+ {
+ bounce=JUST_GONE_DOWN;
+ set_timer_callback(&bounce_timer,
+ current_params.bounce_interval);
+ /* start new stroke/tap */
+ debounced_down=new_down;
+ notify_pad_up_down();
+ }
+ }
+ else /* just gone up */
+ {
+ if(recent_transition)
+ {
+ /* early bounces are probably part of
+ * a multi-tap gesture, so process
+ * immediately
+ */
+ debounced_down=new_down;
+ notify_pad_up_down();
+ }
+ else
+ {
+ /* don't trust it yet */
+ bounce=JUST_GONE_UP;
+ set_timer_callback(&bounce_timer,
+ current_params.bounce_interval);
+ }
+ }
+ }
+ wake_readers();
+ raw_data_count=0;
+ }
+}
+
+
+static void read_raw_pad(int *down, int *debounced, int *x, int *y)
+{
+ disable_irq(current_params.irq);
+ {
+ *down=raw_down;
+ *debounced=debounced_down;
+ *x=raw_x;
+ *y=raw_y;
+ xy_pending = 0;
+ }
+ enable_irq(current_params.irq);
+}
+
+/*****************************************************************************/
+/*
+ * Filesystem interface
+ */
+
+/*
+ * Read returns byte triples, so we need to keep track of
+ * how much of a triple has been read. This is shared across
+ * all processes which have this device open---not that anything
+ * will make much sense in that case.
+ */
+static int read_bytes[3];
+static int read_byte_count=0;
+
+
+
+static void sample_raw(int d[3])
+{
+ d[0]=raw_data[0];
+ d[1]=raw_data[1];
+ d[2]=raw_data[2];
+}
+
+
+static void sample_rare(int d[3])
+{
+ int thisd, thisdd, thisx, thisy;
+
+ read_raw_pad(&thisd, &thisdd, &thisx, &thisy);
+
+ d[0]=(thisd?0x80:0)
+ | (thisx/256)<<4
+ | (thisdd?0x08:0)
+ | (thisy/256)
+ ;
+ d[1]=thisx%256;
+ d[2]=thisy%256;
+}
+
+
+static void sample_debug(int d[3])
+{
+ int thisd, thisdd, thisx, thisy;
+ int b;
+ cli();
+ read_raw_pad(&thisd, &thisdd, &thisx, &thisy);
+ d[0]=(thisd?0x80:0) | (thisdd?0x40:0) | bounce;
+ d[1]=(recent_transition?0x80:0)+transition_count;
+ read_button(&b);
+ d[2]=(synthesize_tap<<4) | (b?0x01:0);
+ sti();
+}
+
+
+static void sample_ps2(int d[3])
+{
+ static int lastx, lasty, lastd;
+
+ int thisd, thisdd, thisx, thisy;
+ int dx, dy, b;
+
+ /*
+ * Obtain the current mouse parameters and limit as appropriate for
+ * the return data format. Interrupts are only disabled while
+ * obtaining the parameters, NOT during the puts_fs_byte() calls,
+ * so paging in put_user() does not affect mouse tracking.
+ */
+ read_raw_pad(&thisd, &thisdd, &thisx, &thisy);
+ read_button(&b);
+
+ /* Now compare with previous readings. Note that we use the
+ * raw down flag rather than the debounced one.
+ */
+ if( (thisd && !lastd) /* new stroke */
+ || (bounce!=NO_BOUNCE) )
+ {
+ dx=0;
+ dy=0;
+ }
+ else
+ {
+ dx = (thisx-lastx);
+ dy = -(thisy-lasty);
+ }
+ lastx=thisx;
+ lasty=thisy;
+ lastd=thisd;
+
+/*
+ d[0]= ((dy<0)?0x20:0)
+ | ((dx<0)?0x10:0)
+ | 0x08
+ | (b? 0x01:0x00)
+ ;
+*/
+ d[0]= ((dy<0)?0x20:0)
+ | ((dx<0)?0x10:0)
+ | (b? 0x00:0x08)
+ ;
+ d[1]=dx;
+ d[2]=dy;
+}
+
+
+
+static int fasync_pad(struct inode *inode, struct file *filp, int on)
+{
+ int retval;
+
+ retval = fasync_helper(inode, filp, on, &asyncptr);
+ if (retval < 0)
+ return retval;
+ return 0;
+}
+
+
+/*
+ * close access to the pad
+ */
+static int close_pad(struct inode * inode, struct file * file)
+{
+ fasync_pad(inode, file, 0);
+ if (--active)
+ return;
+ outb(0x30, current_params.io+2); /* switch off digitiser */
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+
+/*
+ * open access to the pad
+ */
+static int open_pad(struct inode * inode, struct file * file)
+{
+ if (active++)
+ return 0;
+ MOD_INC_USE_COUNT;
+
+ cli();
+ outb(0x30, current_params.io+2); /* switch off digitiser */
+ pad_irq(0,0,0); /* read to flush any pending bytes */
+ pad_irq(0,0,0); /* read to flush any pending bytes */
+ pad_irq(0,0,0); /* read to flush any pending bytes */
+ outb(0x38, current_params.io+2); /* switch on digitiser */
+ current_params = default_params;
+ raw_data_count=0; /* re-sync input byte counter */
+ read_byte_count=0; /* re-sync output byte counter */
+ button_pending=0;
+ recent_transition=0;
+ transition_count=0;
+ synthesize_tap=0;
+ del_timer(&bounce_timer);
+ del_timer(&tap_timer);
+ sti();
+
+ return 0;
+}
+
+
+/*
+ * writes are disallowed
+ */
+static long write_pad(struct inode * inode, struct file * file, const char * buffer, unsigned long count)
+{
+ return -EINVAL;
+}
+
+
+void new_sample(int d[3])
+{
+ switch(current_params.mode)
+ {
+ case PC110PAD_RAW: sample_raw(d); break;
+ case PC110PAD_RARE: sample_rare(d); break;
+ case PC110PAD_DEBUG: sample_debug(d); break;
+ case PC110PAD_PS2: sample_ps2(d); break;
+ }
+}
+
+
+/*
+ * Read pad data. Currently never blocks.
+ */
+static long read_pad(struct inode * inode, struct file * file, char * buffer, unsigned long count)
+{
+ int r;
+
+ for(r=0; r<count; r++)
+ {
+ if(!read_byte_count)
+ new_sample(read_bytes);
+ if(put_user(read_bytes[read_byte_count], buffer+r))
+ return -EFAULT;
+ read_byte_count = (read_byte_count+1)%3;
+ }
+ return r;
+}
+
+
+/*
+ * select for pad input
+ */
+
+static unsigned int pad_poll(struct file *file, poll_table * wait)
+{
+ poll_wait(&queue, wait);
+ if(button_pending || xy_pending)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+
+
+static int pad_ioctl(struct inode *inode, struct file * file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct pc110pad_params new;
+
+ if (!inode)
+ return -EINVAL;
+
+ switch (cmd) {
+ case PC110PADIOCGETP:
+ new = current_params;
+ if(copy_to_user((void *)arg, &new, sizeof(new)))
+ return -EFAULT;
+ return 0;
+
+ case PC110PADIOCSETP:
+ if(copy_from_user(&new, (void *)arg, sizeof(new)))
+ return -EFAULT;
+
+ if( (new.mode<PC110PAD_RAW)
+ || (new.mode>PC110PAD_PS2)
+ || (new.bounce_interval<0)
+ || (new.tap_interval<0)
+ )
+ return -EINVAL;
+
+ current_params.mode = new.mode;
+ current_params.bounce_interval = new.bounce_interval;
+ current_params.tap_interval = new.tap_interval;
+ return 0;
+ }
+ return -ENOIOCTLCMD;
+}
+
+
+static struct file_operations pad_fops = {
+ NULL, /* pad_seek */
+ read_pad,
+ write_pad,
+ NULL, /* pad_readdir */
+ pad_poll,
+ pad_ioctl,
+ NULL, /* pad_mmap */
+ open_pad,
+ close_pad,
+ NULL, /* fsync */
+ fasync_pad,
+ NULL, /* check_media_change */
+ NULL, /* revalidate */
+ NULL /* lock */
+};
+
+
+static struct miscdevice pc110_pad = {
+ PC110PAD_MINOR, "pc110 pad", &pad_fops
+};
+
+
+static int pc110pad_init(void)
+{
+ current_params = default_params;
+
+ if(request_irq(current_params.irq, pad_irq, 0, "pc110pad", 0))
+ {
+ printk("pc110pad: Unable to get IRQ.\n");
+ return -EBUSY;
+ }
+ if(check_region(current_params.io, 4))
+ {
+ printk("pc110pad: I/O area in use.\n");
+ free_irq(current_params.irq,0);
+ return -EBUSY;
+ }
+ request_region(current_params.io, 4, "pc110pad");
+ printk("PC110 digitizer pad at 0x%X, irq %d.\n",
+ current_params.io,current_params.irq);
+ misc_register(&pc110_pad);
+ outb(0x30, current_params.io+2); /* switch off digitiser */
+
+ return 0;
+}
+
+#ifdef MODULE
+
+static void pc110pad_unload(void)
+{
+ outb(0x30, current_params.io+2); /* switch off digitiser */
+ if(current_params.irq)
+ free_irq(current_params.irq, 0);
+ current_params.irq=0;
+ release_region(current_params.io, 4);
+ misc_deregister(&pc110_pad);
+}
+
+
+
+int init_module(void)
+{
+ return pc110pad_init();
+}
+
+void cleanup_module(void)
+{
+ pc110pad_unload();
+}
+#endif
diff --git a/drivers/char/pc110pad.h b/drivers/char/pc110pad.h
new file mode 100644
index 000000000..56d8d82e0
--- /dev/null
+++ b/drivers/char/pc110pad.h
@@ -0,0 +1,31 @@
+#ifndef _PC110PAD_H
+#define _PC110PAD_H
+
+#include <linux/ioctl.h>
+
+enum pc110pad_mode {
+ PC110PAD_RAW, /* bytes as they come out of the hardware */
+ PC110PAD_RARE, /* debounced up/down and absolute x,y */
+ PC110PAD_DEBUG, /* up/down, debounced, transitions, button */
+ PC110PAD_PS2, /* ps2 relative (default) */
+};
+
+
+struct pc110pad_params {
+ enum pc110pad_mode mode;
+ int bounce_interval;
+ int tap_interval;
+ int irq;
+ int io;
+};
+
+#define MS *HZ/1000
+
+/* Appears as device major=10 (MISC), minor=PC110_PAD */
+
+#define PC110PAD_IOCTL_TYPE 0x9a
+
+#define PC110PADIOCGETP _IOR(PC110PAD_IOCTL_TYPE, 0, struct pc110pad_params)
+#define PC110PADIOCSETP _IOR(PC110PAD_IOCTL_TYPE, 1, struct pc110pad_params)
+
+#endif /* _PC110PAD_H */
diff --git a/drivers/char/psaux.c b/drivers/char/psaux.c
index 006c60ddf..eea857b8a 100644
--- a/drivers/char/psaux.c
+++ b/drivers/char/psaux.c
@@ -53,6 +53,8 @@
#include <asm/uaccess.h>
#include <asm/system.h>
+#include "pc_keyb.h"
+
#ifdef CONFIG_SGI
#include <asm/segment.h>
#include <asm/sgihpc.h>
@@ -80,17 +82,6 @@
#define AUX_DISABLE 0xa7 /* disable aux */
#define AUX_ENABLE 0xa8 /* enable aux */
-/* aux device commands */
-#define AUX_SET_RES 0xe8 /* set resolution */
-#define AUX_SET_SCALE11 0xe6 /* set 1:1 scaling */
-#define AUX_SET_SCALE21 0xe7 /* set 2:1 scaling */
-#define AUX_GET_SCALE 0xe9 /* get scaling factor */
-#define AUX_SET_STREAM 0xea /* set stream mode */
-#define AUX_SET_SAMPLE 0xf3 /* set sample rate */
-#define AUX_ENABLE_DEV 0xf4 /* enable aux device */
-#define AUX_DISABLE_DEV 0xf5 /* disable aux device */
-#define AUX_RESET 0xff /* reset aux device */
-
#define MAX_RETRIES 60 /* some aux operations take long time*/
#if defined(__alpha__) && !defined(CONFIG_PCI)
# define AUX_IRQ 9 /* Jensen is odd indeed */
@@ -212,7 +203,16 @@ static void aux_write_dev(int val)
/*
* Write to device & handle returned ack
*/
-#if defined INITIALIZE_DEVICE
+
+#ifdef INITIALIZE_DEVICE
+__initfunc(static void aux_write_dev_nosleep(int val))
+{
+ poll_aux_status_nosleep();
+ ps2_outb_p(KBD_CCMD_WRITE_MOUSE, KBD_CNTL_REG);
+ poll_aux_status_nosleep();
+ ps2_outb_p(val, KBD_DATA_REG);
+}
+
static int aux_write_ack(int val)
{
int retries = 0;
@@ -663,11 +663,11 @@ __initfunc(int psaux_init(void))
aux_write_ack(AUX_SET_SCALE21); /* 2:1 scaling */
poll_aux_status_nosleep();
#endif /* INITIALIZE_DEVICE */
- ps2_outb_p(AUX_DISABLE,AUX_COMMAND); /* Disable Aux device */
+ ps2_outb_p(KBD_CCMD_MOUSE_DISABLE, AUX_COMMAND); /* Disable Aux device */
+ poll_aux_status_nosleep();
+ ps2_outb_p(KBD_CCMD_WRITE_MODE, AUX_COMMAND);
poll_aux_status_nosleep();
- ps2_outb_p(AUX_CMD_WRITE,AUX_COMMAND);
- poll_aux_status_nosleep(); /* Disable interrupts */
- ps2_outb_p(AUX_INTS_OFF, AUX_OUTPUT_PORT); /* on the controller */
+ ps2_outb_p(AUX_INTS_OFF, AUX_OUTPUT_PORT);
}
return 0;
}
diff --git a/drivers/char/pty.c b/drivers/char/pty.c
index 8f1015397..929bb2f85 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 5f7619391..527ac8609 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -1,7 +1,7 @@
/*
* random.c -- A strong random number generator
*
- * Version 1.02, last modified 15-Apr-97
+ * Version 1.03, last modified 26-Apr-97
*
* Copyright Theodore Ts'o, 1994, 1995, 1996, 1997. All rights reserved.
*
@@ -227,6 +227,7 @@
*/
#include <linux/utsname.h>
+#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/string.h>
@@ -1124,8 +1125,7 @@ random_read(struct inode * inode, struct file * file, char * buf, unsigned long
* update the access time.
*/
if (inode && count != 0) {
- inode->i_atime = CURRENT_TIME;
- inode->i_dirt = 1;
+ UPDATE_ATIME(inode);
}
return (count ? count : retval);
@@ -1181,7 +1181,7 @@ random_write(struct inode * inode, struct file * file,
}
if ((ret > 0) && inode) {
inode->i_mtime = CURRENT_TIME;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
}
return ret;
}
@@ -1335,11 +1335,15 @@ struct file_operations urandom_fops = {
* starting point for each pair of TCP endpoints. This defeats
* attacks which rely on guessing the initial TCP sequence number.
* This algorithm was suggested by Steve Bellovin.
+ *
+ * Using a very strong hash was taking an appreciable amount of the total
+ * TCP connection establishment time, so this is a weaker hash,
+ * compensated for by changing the secret periodically.
*/
/* F, G and H are basic MD4 functions: selection, majority, parity */
-#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
-#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
+#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
+#define G(x, y, z) (((x) & (y)) + (((x) ^ (y)) & (z)))
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define ROTL(n,X) ( ( ( X ) << n ) | ( ( X ) >> ( 32 - n ) ) )
@@ -1357,9 +1361,9 @@ struct file_operations urandom_fops = {
(a) = ROTL ((s), (a));}
/*
- * Basic cut-down MD4 transform
+ * Basic cut-down MD4 transform. Returns only 32 bits of result.
*/
-static void halfMD4Transform (__u32 buf[4], __u32 in[8])
+static __u32 halfMD4Transform (__u32 const buf[4], __u32 const in[8])
{
__u32 a = buf[0], b = buf[1], c = buf[2], d = buf[3];
@@ -1376,77 +1380,141 @@ static void halfMD4Transform (__u32 buf[4], __u32 in[8])
/* Round 2 */
GG (a, b, c, d, in[ 0], 3);
GG (d, a, b, c, in[ 4], 5);
- GG (a, b, c, d, in[ 1], 9);
- GG (d, a, b, c, in[ 5], 13);
+ GG (c, d, a, b, in[ 1], 9);
+ GG (b, c, d, a, in[ 5], 13);
GG (a, b, c, d, in[ 2], 3);
GG (d, a, b, c, in[ 6], 5);
- GG (a, b, c, d, in[ 3], 9);
- GG (d, a, b, c, in[ 7], 13);
+ GG (c, d, a, b, in[ 3], 9);
+ GG (b, c, d, a, in[ 7], 13);
/* Round 3 */
HH (a, b, c, d, in[ 0], 3);
- HH (c, d, a, b, in[ 4], 9);
- HH (a, b, c, d, in[ 2], 11);
- HH (c, d, a, b, in[ 6], 15);
+ HH (d, a, b, c, in[ 4], 9);
+ HH (c, d, a, b, in[ 2], 11);
+ HH (b, c, d, a, in[ 6], 15);
HH (a, b, c, d, in[ 1], 3);
- HH (c, d, a, b, in[ 5], 9);
- HH (a, b, c, d, in[ 3], 11);
- HH (c, d, a, b, in[ 7], 15);
+ HH (d, a, b, c, in[ 5], 9);
+ HH (c, d, a, b, in[ 3], 11);
+ HH (b, c, d, a, in[ 7], 15);
- buf[0] += a;
- buf[1] += b;
- buf[2] += c;
- buf[3] += d;
+ return buf[1] + b; /* "most hashed" word */
+ /* Alternative: return sum of all words? */
}
+/* This should not be decreased so low that ISNs wrap too fast. */
#define REKEY_INTERVAL 300
+#define HASH_BITS 24
__u32 secure_tcp_sequence_number(__u32 saddr, __u32 daddr,
__u16 sport, __u16 dport)
{
static __u32 rekey_time = 0;
+ static __u32 count = 0;
static __u32 secret[12];
- static char count = 0;
struct timeval tv;
- __u32 tmp[12];
__u32 seq;
/*
- * Pick a random secret every REKEY_INTERVAL seconds
+ * Pick a random secret every REKEY_INTERVAL seconds.
*/
- do_gettimeofday(&tv);
+ do_gettimeofday(&tv); /* We need the usecs below... */
+
if (!rekey_time ||
(tv.tv_sec - rekey_time) > REKEY_INTERVAL) {
- get_random_bytes(&secret, sizeof(secret));
rekey_time = tv.tv_sec;
- count++;
+ /* First three words are overwritten below. */
+ get_random_bytes(&secret+3, sizeof(secret)-12);
+ count = (tv.tv_sec/REKEY_INTERVAL) << HASH_BITS;
}
- memcpy(tmp, secret, sizeof(tmp));
/*
- * Pick a unique starting offset for each
- * TCP connection endpoints (saddr, daddr, sport, dport)
+ * Pick a unique starting offset for each TCP connection endpoints
+ * (saddr, daddr, sport, dport).
+ * Note that the words are placed into the first words to be
+ * mixed in with the halfMD4. This is because the starting
+ * vector is also a random secret (at secret+8), and further
+ * hashing fixed data into it isn't going to improve anything,
+ * so we should get started with the variable data.
*/
- tmp[8]=saddr;
- tmp[9]=daddr;
- tmp[10]=(sport << 16) + dport;
- halfMD4Transform(tmp, tmp+4);
-
+ secret[0]=saddr;
+ secret[1]=daddr;
+ secret[2]=(sport << 16) + dport;
+
+ seq = (halfMD4Transform(secret+8, secret) &
+ ((1<<HASH_BITS)-1)) + (count << HASH_BITS);
+
/*
* As close as possible to RFC 793, which
* suggests using a 250kHz clock.
- * Further reading shows this assumes 2MB/s networks.
- * For 10MB/s ethernet, a 1MHz clock is appropriate.
+ * Further reading shows this assumes 2Mb/s networks.
+ * For 10Mb/s ethernet, a 1MHz clock is appropriate.
* That's funny, Linux has one built in! Use it!
+ * (Networks are faster now - should this be increased?)
*/
- seq = (tmp[1]&0xFFFFFF) + (tv.tv_usec+tv.tv_sec*1000000) +
- (count << 24);
+ seq += tv.tv_usec + tv.tv_sec*1000000;
#if 0
printk("init_seq(%lx, %lx, %d, %d) = %d\n",
saddr, daddr, sport, dport, seq);
#endif
- return (seq);
+ return seq;
+}
+
+#ifdef CONFIG_SYN_COOKIES
+/*
+ * Secure SYN cookie computation. This is the algorithm worked out by
+ * Dan Bernstein and Eric Schenk.
+ *
+ * For linux I implement the 1 minute counter by looking at the jiffies clock.
+ * The count is passed in as a parameter;
+ *
+ */
+__u32 secure_tcp_syn_cookie(__u32 saddr, __u32 daddr,
+ __u16 sport, __u16 dport, __u32 sseq, __u32 count)
+{
+ static int is_init = 0;
+ static __u32 secret[2][16];
+ __u32 tmp[16];
+ __u32 seq;
+
+ /*
+ * Pick two random secret the first time we open a TCP connection.
+ */
+ if (is_init == 0) {
+ get_random_bytes(&secret[0], sizeof(secret[0]));
+ get_random_bytes(&secret[1], sizeof(secret[1]));
+ is_init = 1;
+ }
+
+ /*
+ * Compute the secure sequence number.
+ * The output should be:
+ * MD5(sec1,saddr,sport,daddr,dport,sec1) + their sequence number
+ * + (count * 2^24)
+ * + (MD5(sec2,saddr,sport,daddr,dport,count,sec2) % 2^24).
+ * Where count increases every minute by 1.
+ */
+
+ memcpy(tmp, secret[0], sizeof(tmp));
+ tmp[8]=saddr;
+ tmp[9]=daddr;
+ tmp[10]=(sport << 16) + dport;
+ HASH_TRANSFORM(tmp, tmp);
+ seq = tmp[1];
+
+ memcpy(tmp, secret[1], sizeof(tmp));
+ tmp[8]=saddr;
+ tmp[9]=daddr;
+ tmp[10]=(sport << 16) + dport;
+ tmp[11]=count; /* minute counter */
+ HASH_TRANSFORM(tmp, tmp);
+
+ seq += sseq + (count << 24) + (tmp[1] & 0x00ffffff);
+
+ /* Zap lower 3 bits to leave room for the MSS representation */
+ return (seq & 0xfffff8);
}
+#endif
+
#ifdef RANDOM_BENCHMARK
/*
diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c
index a8614999d..5f03f8887 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;
@@ -180,7 +180,9 @@ static long rtc_read(struct inode *inode, struct file *file, char *buf,
data = rtc_irq_data;
rtc_irq_data = 0;
restore_flags(flags);
- retval = put_user(data, (unsigned long *)buf)) ?: sizeof(unsigned long);
+ retval = put_user(data, (unsigned long *)buf);
+ if (!retval)
+ retval = sizeof(unsigned long);
}
current->state = TASK_RUNNING;
@@ -262,7 +264,6 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
* "don't care" or "match all". Only the tm_hour,
* tm_min and tm_sec are used.
*/
- int retval;
unsigned char hrs, min, sec;
struct rtc_time alm_tm;
@@ -305,7 +306,6 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
}
case RTC_SET_TIME: /* Set the RTC */
{
- int retval;
struct rtc_time rtc_tm;
unsigned char mon, day, hrs, min, sec, leap_yr;
unsigned char save_control, save_freq_select;
@@ -418,7 +418,7 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
default:
return -EINVAL;
}
- return copy_to_user(arg, &wtime, sizeof wtime) ? -EFAULT : 0;
+ return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0;
}
/*
diff --git a/drivers/char/serial.c b/drivers/char/serial.c
index e2a93a0c3..ca561c4e9 100644
--- a/drivers/char/serial.c
+++ b/drivers/char/serial.c
@@ -1421,6 +1421,9 @@ static void shutdown(struct async_struct * info)
info->MCR |= UART_MCR_OUT1 | UART_MCR_OUT2;
#endif
+ /* disable break condition */
+ serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC);
+
if (!info->tty || (info->tty->termios->c_cflag & HUPCL))
info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS);
serial_outp(info, UART_MCR, info->MCR);
@@ -2089,6 +2092,30 @@ static void send_break( struct async_struct * info, int duration)
}
/*
+ * This routine sets the break condition on the serial port.
+ */
+static void begin_break(struct async_struct * info)
+{
+ if (!info->port)
+ return;
+ cli();
+ serial_out(info, UART_LCR, serial_inp(info, UART_LCR) | UART_LCR_SBC);
+ sti();
+}
+
+/*
+ * This routine clears the break condition on the serial port.
+ */
+static void end_break(struct async_struct * info)
+{
+ if (!info->port)
+ return;
+ cli();
+ serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC);
+ sti();
+}
+
+/*
* This routine returns a bitfield of "wild interrupts". Basically,
* any unclaimed interrupts which is flapping around.
*/
@@ -2292,6 +2319,19 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file,
if (current->signal & ~current->blocked)
return -EINTR;
return 0;
+ case TIOCSBRK:
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ tty_wait_until_sent(tty, 0);
+ begin_break(info);
+ return 0;
+ case TIOCCBRK:
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ end_break(info);
+ return 0;
case TIOCGSOFTCAR:
return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg);
case TIOCSSOFTCAR:
diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c
index e2044c086..1e0e1a2a0 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.1 1997/06/17 13:24:07 ralf 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 c08e44a27..7a31e162d 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -45,9 +45,12 @@
* Restrict vt switching via ioctl()
* -- grif@cs.ucr.edu, 5-Dec-95
*
- * Move console and virtual terminal code to more apropriate files,
+ * Move console and virtual terminal code to more appropriate files,
* implement CONFIG_VT and generalize console device interface.
* -- Marko Kohtala <Marko.Kohtala@hut.fi>, March 97
+ *
+ * Rewrote init_dev and release_dev to eliminate races.
+ * -- Bill Hawes <whawes@star.net>, June 97
*/
#include <linux/config.h>
@@ -90,8 +93,8 @@
#undef TTY_DEBUG_HANGUP
-#define TTY_PARANOIA_CHECK
-#define CHECK_TTY_COUNT
+#define TTY_PARANOIA_CHECK 1
+#define CHECK_TTY_COUNT 1
struct termios tty_std_termios; /* for the benefit of tty drivers */
struct tty_driver *tty_drivers = NULL; /* linked list of tty drivers */
@@ -370,13 +373,15 @@ void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops)
for (filp = inuse_filps; filp; filp = filp->f_next) {
if (filp->private_data != tty)
continue;
- if (!filp->f_inode)
+ if (!filp->f_dentry)
+ continue;
+ if (!filp->f_dentry->d_inode)
continue;
- if (filp->f_inode->i_rdev == CONSOLE_DEV)
+ if (filp->f_dentry->d_inode->i_rdev == CONSOLE_DEV)
continue;
if (filp->f_op != &tty_fops)
continue;
- tty_fasync(filp->f_inode, filp, 0);
+ tty_fasync(filp->f_dentry->d_inode, filp, 0);
filp->f_op = fops;
}
@@ -384,7 +389,7 @@ void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops)
tty->ldisc.flush_buffer(tty);
if (tty->driver.flush_buffer)
tty->driver.flush_buffer(tty);
- if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) &&
tty->ldisc.write_wakeup)
(tty->ldisc.write_wakeup)(tty);
wake_up_interruptible(&tty->write_wait);
@@ -533,7 +538,7 @@ void start_tty(struct tty_struct *tty)
}
if (tty->driver.start)
(tty->driver.start)(tty);
- if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) &&
tty->ldisc.write_wakeup)
(tty->ldisc.write_wakeup)(tty);
wake_up_interruptible(&tty->write_wait);
@@ -548,7 +553,7 @@ static long tty_read(struct inode * inode, struct file * file,
tty = (struct tty_struct *)file->private_data;
if (tty_paranoia_check(tty, inode->i_rdev, "tty_read"))
return -EIO;
- if (!tty || (tty->flags & (1 << TTY_IO_ERROR)))
+ if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags)))
return -EIO;
/* This check not only needs to be done before reading, but also
@@ -630,7 +635,7 @@ static long tty_write(struct inode * inode, struct file * file,
tty = (struct tty_struct *)file->private_data;
if (tty_paranoia_check(tty, inode->i_rdev, "tty_write"))
return -EIO;
- if (!tty || !tty->driver.write || (tty->flags & (1 << TTY_IO_ERROR)))
+ if (!tty || !tty->driver.write || (test_bit(TTY_IO_ERROR, &tty->flags)))
return -EIO;
#if 0
if (!is_console && L_TOSTOP(tty) && (tty->pgrp > 0) &&
@@ -651,18 +656,31 @@ static long tty_write(struct inode * inode, struct file * file,
(unsigned int)count);
}
+/* Semaphore to protect creating and releasing a tty */
+static struct semaphore tty_sem = MUTEX;
+static void down_tty_sem(int index)
+{
+ down(&tty_sem);
+}
+static void up_tty_sem(int index)
+{
+ up(&tty_sem);
+}
+static void release_mem(struct tty_struct *tty, int idx);
+
/*
- * This is so ripe with races that you should *really* not touch this
- * unless you know exactly what you are doing. All the changes have to be
- * made atomically, or there may be incorrect pointers all over the place.
+ * WSH 06/09/97: Rewritten to remove races and properly clean up after a
+ * failed open. The new code protects the open with a semaphore, so it's
+ * really quite straightforward. The semaphore locking can probably be
+ * relaxed for the (most common) case of reopening a tty.
*/
static int init_dev(kdev_t device, struct tty_struct **ret_tty)
{
- struct tty_struct *tty, **tty_loc, *o_tty, **o_tty_loc;
+ struct tty_struct *tty, *o_tty;
struct termios *tp, **tp_loc, *o_tp, **o_tp_loc;
struct termios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc;
struct tty_driver *driver;
- int retval;
+ int retval=0;
int idx;
driver = get_tty_driver(device);
@@ -670,189 +688,251 @@ static int init_dev(kdev_t device, struct tty_struct **ret_tty)
return -ENODEV;
idx = MINOR(device) - driver->minor_start;
- tty = o_tty = NULL;
+ tty = driver->table[idx];
+
+ /*
+ * Check whether we need to acquire the tty semaphore to avoid
+ * race conditions. For now, play it safe.
+ */
+ down_tty_sem(idx);
+
+ /* check whether we're reopening an existing tty */
+ if(tty) goto fast_track;
+
+ /*
+ * First time open is complex, especially for PTY devices.
+ * This code guarantees that either everything succeeds and the
+ * TTY is ready for operation, or else the table slots are vacated
+ * and the allocated memory released. (Except that the termios
+ * and locked termios may be retained.)
+ */
+
+ o_tty = NULL;
tp = o_tp = NULL;
ltp = o_ltp = NULL;
- o_tty_loc = NULL;
- o_tp_loc = o_ltp_loc = NULL;
- tty_loc = &driver->table[idx];
- tp_loc = &driver->termios[idx];
- ltp_loc = &driver->termios_locked[idx];
+ tty = (struct tty_struct*) get_free_page(GFP_KERNEL);
+ if(!tty)
+ goto fail_no_mem;
+ initialize_tty_struct(tty);
+ tty->device = device;
+ tty->driver = *driver;
-repeat:
- retval = -EIO;
- if (driver->type == TTY_DRIVER_TYPE_PTY &&
- driver->subtype == PTY_TYPE_MASTER &&
- *tty_loc && (*tty_loc)->count)
- goto end_init;
- retval = -ENOMEM;
- if (!*tty_loc && !tty) {
- if (!(tty = (struct tty_struct*) get_free_page(GFP_KERNEL)))
- goto end_init;
- initialize_tty_struct(tty);
- tty->device = device;
- tty->driver = *driver;
- goto repeat;
- }
- if (!*tp_loc && !tp) {
+ tp_loc = &driver->termios[idx];
+ if (!*tp_loc) {
tp = (struct termios *) kmalloc(sizeof(struct termios),
GFP_KERNEL);
if (!tp)
- goto end_init;
+ goto free_mem_out;
*tp = driver->init_termios;
- goto repeat;
}
- if (!*ltp_loc && !ltp) {
+
+ ltp_loc = &driver->termios_locked[idx];
+ if (!*ltp_loc) {
ltp = (struct termios *) kmalloc(sizeof(struct termios),
GFP_KERNEL);
if (!ltp)
- goto end_init;
+ goto free_mem_out;
memset(ltp, 0, sizeof(struct termios));
- goto repeat;
}
- if (driver->type == TTY_DRIVER_TYPE_PTY) {
- o_tty_loc = &driver->other->table[idx];
- o_tp_loc = &driver->other->termios[idx];
- o_ltp_loc = &driver->other->termios_locked[idx];
- if (!*o_tty_loc && !o_tty) {
- kdev_t o_device;
-
- o_tty = (struct tty_struct *)
- get_free_page(GFP_KERNEL);
- if (!o_tty)
- goto end_init;
- o_device = MKDEV(driver->other->major,
- driver->other->minor_start + idx);
- initialize_tty_struct(o_tty);
- o_tty->device = o_device;
- o_tty->driver = *driver->other;
- goto repeat;
- }
- if (!*o_tp_loc && !o_tp) {
+ if (driver->type == TTY_DRIVER_TYPE_PTY) {
+ o_tty = (struct tty_struct *) get_free_page(GFP_KERNEL);
+ if (!o_tty)
+ goto free_mem_out;
+ initialize_tty_struct(o_tty);
+ o_tty->device = (kdev_t) MKDEV(driver->other->major,
+ driver->other->minor_start + idx);
+ o_tty->driver = *driver->other;
+
+ o_tp_loc = &driver->other->termios[idx];
+ if (!*o_tp_loc) {
o_tp = (struct termios *)
kmalloc(sizeof(struct termios), GFP_KERNEL);
if (!o_tp)
- goto end_init;
+ goto free_mem_out;
*o_tp = driver->other->init_termios;
- goto repeat;
}
- if (!*o_ltp_loc && !o_ltp) {
+
+ o_ltp_loc = &driver->other->termios_locked[idx];
+ if (!*o_ltp_loc) {
o_ltp = (struct termios *)
kmalloc(sizeof(struct termios), GFP_KERNEL);
if (!o_ltp)
- goto end_init;
+ goto free_mem_out;
memset(o_ltp, 0, sizeof(struct termios));
- goto repeat;
}
-
+
+ /*
+ * Everything allocated ... set up the o_tty structure.
+ */
+ driver->other->table[idx] = o_tty;
+ if (!*o_tp_loc)
+ *o_tp_loc = o_tp;
+ if (!*o_ltp_loc)
+ *o_ltp_loc = o_ltp;
+ o_tty->termios = *o_tp_loc;
+ o_tty->termios_locked = *o_ltp_loc;
+ (*driver->other->refcount)++;
+ if (driver->subtype == PTY_TYPE_MASTER)
+ o_tty->count++;
+
+ /* Establish the links in both directions */
+ tty->link = o_tty;
+ o_tty->link = tty;
}
- /* Now we have allocated all the structures: update all the pointers.. */
- if (!*tp_loc) {
+
+ /*
+ * All structures have been allocated, so now we install them.
+ * Failures after this point use release_mem to clean up, so
+ * there's no need to null out the local pointers.
+ */
+ driver->table[idx] = tty;
+ if (!*tp_loc)
*tp_loc = tp;
- tp = NULL;
- }
- if (!*ltp_loc) {
+ if (!*ltp_loc)
*ltp_loc = ltp;
- ltp = NULL;
+ tty->termios = *tp_loc;
+ tty->termios_locked = *ltp_loc;
+ (*driver->refcount)++;
+ tty->count++;
+
+ /*
+ * Structures all installed ... call the ldisc open routines.
+ * If we fail here just call release_mem to clean up. No need
+ * to decrement the use counts, as release_mem doesn't care.
+ */
+ if (tty->ldisc.open) {
+ retval = (tty->ldisc.open)(tty);
+ if (retval)
+ goto release_mem_out;
}
- if (!*tty_loc) {
- tty->termios = *tp_loc;
- tty->termios_locked = *ltp_loc;
- *tty_loc = tty;
- (*driver->refcount)++;
- (*tty_loc)->count++;
- if (tty->ldisc.open) {
- retval = (tty->ldisc.open)(tty);
- if (retval < 0) {
- (*tty_loc)->count--;
- tty = NULL;
- goto end_init;
- }
- }
- tty = NULL;
- } else {
- if ((*tty_loc)->flags & (1 << TTY_CLOSING)) {
- printk("Attempt to open closing tty %s.\n",
- tty_name(*tty_loc));
- printk("Ack!!!! This should never happen!!\n");
- return -EINVAL;
+ if (o_tty && o_tty->ldisc.open) {
+ retval = (o_tty->ldisc.open)(o_tty);
+ if (retval) {
+ if (tty->ldisc.close)
+ (tty->ldisc.close)(tty);
+ goto release_mem_out;
}
- (*tty_loc)->count++;
}
- if (driver->type == TTY_DRIVER_TYPE_PTY) {
- if (!*o_tp_loc) {
- *o_tp_loc = o_tp;
- o_tp = NULL;
- }
- if (!*o_ltp_loc) {
- *o_ltp_loc = o_ltp;
- o_ltp = NULL;
- }
- if (!*o_tty_loc) {
- o_tty->termios = *o_tp_loc;
- o_tty->termios_locked = *o_ltp_loc;
- *o_tty_loc = o_tty;
- (*driver->other->refcount)++;
- if (o_tty->ldisc.open) {
- retval = (o_tty->ldisc.open)(o_tty);
- if (retval < 0) {
- (*tty_loc)->count--;
- o_tty = NULL;
- goto end_init;
- }
- }
- o_tty = NULL;
+ goto success;
+
+ /*
+ * This fast open can be used if the tty is already open.
+ * No memory is allocated, and the only failures are from
+ * attempting to open a closing tty or attempting multiple
+ * opens on a pty master.
+ */
+fast_track:
+ if (test_bit(TTY_CLOSING, &tty->flags)) {
+ retval = -EIO;
+ goto end_init;
+ }
+ if (driver->type == TTY_DRIVER_TYPE_PTY &&
+ driver->subtype == PTY_TYPE_MASTER) {
+ /*
+ * special case for PTY masters: only one open permitted,
+ * and the slave side open count is incremented as well.
+ */
+ if (tty->count) {
+ retval = -EIO;
+ goto end_init;
}
- (*tty_loc)->link = *o_tty_loc;
- (*o_tty_loc)->link = *tty_loc;
- if (driver->subtype == PTY_TYPE_MASTER)
- (*o_tty_loc)->count++;
+ tty->link->count++;
}
- (*tty_loc)->driver = *driver;
- *ret_tty = *tty_loc;
- retval = 0;
+ tty->count++;
+ tty->driver = *driver; /* N.B. why do this every time?? */
+
+success:
+ *ret_tty = tty;
+
+ /* All paths come through here to release the semaphore */
end_init:
- if (tty)
- free_page((unsigned long) tty);
- if (o_tty)
- free_page((unsigned long) o_tty);
- if (tp)
- kfree_s(tp, sizeof(struct termios));
+ up_tty_sem(idx);
+ return retval;
+
+ /* Release locally allocated memory ... nothing placed in slots */
+free_mem_out:
if (o_tp)
kfree_s(o_tp, sizeof(struct termios));
+ if (o_tty)
+ free_page((unsigned long) o_tty);
if (ltp)
kfree_s(ltp, sizeof(struct termios));
- if (o_ltp)
- kfree_s(o_ltp, sizeof(struct termios));
- return retval;
+ if (tp)
+ kfree_s(tp, sizeof(struct termios));
+ free_page((unsigned long) tty);
+
+fail_no_mem:
+ retval = -ENOMEM;
+ goto end_init;
+
+ /* call the tty release_mem routine to clean out this slot */
+release_mem_out:
+ printk("init_dev: ldisc open failed, clearing slot %d\n", idx);
+ release_mem(tty, idx);
+ goto end_init;
+}
+
+/*
+ * Releases memory associated with a tty structure, and clears out the
+ * driver table slots.
+ */
+static void release_mem(struct tty_struct *tty, int idx)
+{
+ struct tty_struct *o_tty;
+ struct termios *tp;
+
+ if ((o_tty = tty->link) != NULL) {
+ o_tty->driver.table[idx] = NULL;
+ if (o_tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) {
+ tp = o_tty->driver.termios[idx];
+ o_tty->driver.termios[idx] = NULL;
+ kfree_s(tp, sizeof(struct termios));
+ }
+ o_tty->magic = 0;
+ (*o_tty->driver.refcount)--;
+ free_page((unsigned long) o_tty);
+ }
+
+ tty->driver.table[idx] = NULL;
+ if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) {
+ tp = tty->driver.termios[idx];
+ tty->driver.termios[idx] = NULL;
+ kfree_s(tp, sizeof(struct termios));
+ }
+ tty->magic = 0;
+ (*tty->driver.refcount)--;
+ free_page((unsigned long) tty);
}
/*
* Even releasing the tty structures is a tricky business.. We have
* to be very careful that the structures are all released at the
* same time, as interrupts might otherwise get the wrong pointers.
+ *
+ * WSH 09/09/97: rewritten to avoid some nasty race conditions that could
+ * lead to double frees or releasing memory still in use.
*/
static void release_dev(struct file * filp)
{
struct tty_struct *tty, *o_tty;
- struct termios *tp, *o_tp, *ltp, *o_ltp;
- struct task_struct *p;
+ int pty_master, tty_closing, o_tty_closing, do_sleep;
int idx;
tty = (struct tty_struct *)filp->private_data;
- if (tty_paranoia_check(tty, filp->f_inode->i_rdev, "release_dev"))
+ if (tty_paranoia_check(tty, filp->f_dentry->d_inode->i_rdev, "release_dev"))
return;
check_tty_count(tty, "release_dev");
- tty_fasync(filp->f_inode, filp, 0);
-
- tp = tty->termios;
- ltp = tty->termios_locked;
+ tty_fasync(filp->f_dentry->d_inode, filp, 0);
idx = MINOR(tty->device) - tty->driver.minor_start;
+ pty_master = (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
+ tty->driver.subtype == PTY_TYPE_MASTER);
+ o_tty = tty->link;
+
#ifdef TTY_PARANOIA_CHECK
if (idx < 0 || idx >= tty->driver.num) {
printk("release_dev: bad idx when trying to free (%s)\n",
@@ -864,15 +944,15 @@ static void release_dev(struct file * filp)
idx, kdevname(tty->device));
return;
}
- if (tp != tty->driver.termios[idx]) {
- printk("release_dev: driver.termios[%d] not termios for ("
- "%s)\n",
+ if (tty->termios != tty->driver.termios[idx]) {
+ printk("release_dev: driver.termios[%d] not termios "
+ "for (%s)\n",
idx, kdevname(tty->device));
return;
}
- if (ltp != tty->driver.termios_locked[idx]) {
- printk("release_dev: driver.termios_locked[%d] not termios_locked for ("
- "%s)\n",
+ if (tty->termios_locked != tty->driver.termios_locked[idx]) {
+ printk("release_dev: driver.termios_locked[%d] not "
+ "termios_locked for (%s)\n",
idx, kdevname(tty->device));
return;
}
@@ -883,10 +963,6 @@ static void release_dev(struct file * filp)
tty->count);
#endif
- o_tty = tty->link;
- o_tp = (o_tty) ? o_tty->termios : NULL;
- o_ltp = (o_tty) ? o_tty->termios_locked : NULL;
-
#ifdef TTY_PARANOIA_CHECK
if (tty->driver.other) {
if (o_tty != tty->driver.other->table[idx]) {
@@ -895,34 +971,90 @@ static void release_dev(struct file * filp)
idx, kdevname(tty->device));
return;
}
- if (o_tp != tty->driver.other->termios[idx]) {
- printk("release_dev: other->termios[%d] not o_termios for ("
- "%s)\n",
+ if (o_tty->termios != tty->driver.other->termios[idx]) {
+ printk("release_dev: other->termios[%d] not o_termios "
+ "for (%s)\n",
idx, kdevname(tty->device));
return;
}
- if (o_ltp != tty->driver.other->termios_locked[idx]) {
- printk("release_dev: other->termios_locked[%d] not o_termios_locked for ("
- "%s)\n",
+ if (o_tty->termios_locked !=
+ tty->driver.other->termios_locked[idx]) {
+ printk("release_dev: other->termios_locked[%d] not "
+ "o_termios_locked for (%s)\n",
idx, kdevname(tty->device));
return;
}
-
if (o_tty->link != tty) {
printk("release_dev: bad pty pointers\n");
return;
}
}
#endif
-
+ /*
+ * Sanity check: if tty->count is going to zero, there shouldn't be
+ * any waiters on tty->read_wait or tty->write_wait. We test the
+ * wait queues and kick everyone out _before_ actually starting to
+ * close. This ensures that we won't block while releasing the tty
+ * structure.
+ *
+ * The test for the o_tty closing is necessary, since the master and
+ * slave sides may close in any order. If the slave side closes out
+ * first, its count will be one, since the master side holds an open.
+ * Thus this test wouldn't be triggered at the time the slave closes,
+ * so we do it now.
+ *
+ * Note that it's possible for the tty to be opened again while we're
+ * flushing out waiters. By recalculating the closing flags before
+ * each iteration we avoid any problems.
+ */
+ while (1) {
+ tty_closing = tty->count <= 1;
+ o_tty_closing = o_tty &&
+ (o_tty->count <= (pty_master ? 1 : 0));
+ do_sleep = 0;
+
+ if (tty_closing) {
+ if (waitqueue_active(&tty->read_wait)) {
+ wake_up(&tty->read_wait);
+ do_sleep++;
+ }
+ if (waitqueue_active(&tty->write_wait)) {
+ wake_up(&tty->write_wait);
+ do_sleep++;
+ }
+ }
+ if (o_tty_closing) {
+ if (waitqueue_active(&o_tty->read_wait)) {
+ wake_up(&o_tty->read_wait);
+ do_sleep++;
+ }
+ if (waitqueue_active(&o_tty->write_wait)) {
+ wake_up(&o_tty->write_wait);
+ do_sleep++;
+ }
+ }
+ if (!do_sleep)
+ break;
+
+ printk("release_dev: %s: read/write wait queue active!\n",
+ tty_name(tty));
+ schedule();
+ }
+
+ /*
+ * The closing flags are now consistent with the open counts on
+ * both sides, and we've completed the last operation that could
+ * block, so it's safe to proceed with closing.
+ */
+
if (tty->driver.close)
tty->driver.close(tty, filp);
- if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
- tty->driver.subtype == PTY_TYPE_MASTER) {
- if (--tty->link->count < 0) {
+
+ if (pty_master) {
+ if (--o_tty->count < 0) {
printk("release_dev: bad pty slave count (%d) for %s\n",
- tty->count, tty_name(tty));
- tty->link->count = 0;
+ o_tty->count, tty_name(o_tty));
+ o_tty->count = 0;
}
}
if (--tty->count < 0) {
@@ -930,60 +1062,50 @@ static void release_dev(struct file * filp)
tty->count, tty_name(tty));
tty->count = 0;
}
- if (tty->count)
- return;
/*
- * Sanity check --- if tty->count is zero, there shouldn't be
- * any waiters on tty->read_wait or tty->write_wait. But just
- * in case....
+ * Perform some housekeeping before deciding whether to return.
+ *
+ * Set the TTY_CLOSING flag if this was the last open. In the
+ * case of a pty we may have to wait around for the other side
+ * to close, and TTY_CLOSING makes sure we can't be reopened.
*/
- while (1) {
- if (waitqueue_active(&tty->read_wait)) {
- printk("release_dev: %s: read_wait active?!?\n",
- tty_name(tty));
- wake_up(&tty->read_wait);
- } else if (waitqueue_active(&tty->write_wait)) {
- printk("release_dev: %s: write_wait active?!?\n",
- tty_name(tty));
- wake_up(&tty->write_wait);
- } else
- break;
- schedule();
- }
-
+ if(tty_closing)
+ set_bit(TTY_CLOSING, &tty->flags);
+ if(o_tty_closing)
+ set_bit(TTY_CLOSING, &o_tty->flags);
+
/*
- * We're committed; at this point, we must not block!
+ * If _either_ side is closing, make sure there aren't any
+ * processes that still think tty or o_tty is their controlling
+ * tty. Also, clear redirect if it points to either tty.
*/
- if (o_tty) {
- if (o_tty->count)
- return;
- tty->driver.other->table[idx] = NULL;
- tty->driver.other->termios[idx] = NULL;
- kfree_s(o_tp, sizeof(struct termios));
+ if (tty_closing || o_tty_closing) {
+ struct task_struct *p;
+
+ read_lock(&tasklist_lock);
+ for_each_task(p) {
+ if (p->tty == tty || (o_tty && p->tty == o_tty))
+ p->tty = NULL;
+ }
+ read_unlock(&tasklist_lock);
+
+ if (redirect == tty || (o_tty && redirect == o_tty))
+ redirect = NULL;
}
+
+ /* check whether both sides are closing ... */
+ if (!tty_closing || (o_tty && !o_tty_closing))
+ return;
+ filp->private_data = 0;
#ifdef TTY_DEBUG_HANGUP
printk("freeing tty structure...");
#endif
- tty->flags |= (1 << TTY_CLOSING);
-
- /*
- * Make sure there aren't any processes that still think this
- * tty is their controlling tty.
- */
- read_lock(&tasklist_lock);
- for_each_task(p) {
- if (p->tty == tty)
- p->tty = NULL;
- if (o_tty && p->tty == o_tty)
- p->tty = NULL;
- }
- read_unlock(&tasklist_lock);
/*
- * Shutdown the current line discipline, and reset it to
- * N_TTY.
+ * Shutdown the current line discipline, and reset it to N_TTY.
+ * N.B. why reset ldisc when we're releasing the memory??
*/
if (tty->ldisc.close)
(tty->ldisc.close)(tty);
@@ -995,41 +1117,34 @@ static void release_dev(struct file * filp)
o_tty->ldisc = ldiscs[N_TTY];
}
- tty->driver.table[idx] = NULL;
- if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) {
- tty->driver.termios[idx] = NULL;
- kfree_s(tp, sizeof(struct termios));
- }
- if (tty == redirect || o_tty == redirect)
- redirect = NULL;
/*
* Make sure that the tty's task queue isn't activated. If it
- * is, take it out of the linked list.
+ * is, take it out of the linked list. The tqueue isn't used by
+ * pty's, so skip the test for them.
*/
- spin_lock_irq(&tqueue_lock);
- if (tty->flip.tqueue.sync) {
- struct tq_struct *tq, *prev;
-
- for (tq=tq_timer, prev=0; tq; prev=tq, tq=tq->next) {
- if (tq == &tty->flip.tqueue) {
- if (prev)
- prev->next = tq->next;
- else
- tq_timer = tq->next;
- break;
+ if (tty->driver.type != TTY_DRIVER_TYPE_PTY) {
+ spin_lock_irq(&tqueue_lock);
+ if (tty->flip.tqueue.sync) {
+ struct tq_struct *tq, *prev;
+
+ for (tq=tq_timer, prev=0; tq; prev=tq, tq=tq->next) {
+ if (tq == &tty->flip.tqueue) {
+ if (prev)
+ prev->next = tq->next;
+ else
+ tq_timer = tq->next;
+ break;
+ }
}
}
+ spin_unlock_irq(&tqueue_lock);
}
- spin_unlock_irq(&tqueue_lock);
- tty->magic = 0;
- (*tty->driver.refcount)--;
- free_page((unsigned long) tty);
- filp->private_data = 0;
- if (o_tty) {
- o_tty->magic = 0;
- (*o_tty->driver.refcount)--;
- free_page((unsigned long) o_tty);
- }
+
+ /*
+ * The release_mem function takes care of the details of clearing
+ * the slots and preserving the termios structure.
+ */
+ release_mem(tty, idx);
}
/*
@@ -1077,6 +1192,7 @@ retry_open:
retval = init_dev(device, &tty);
if (retval)
return retval;
+ /* N.B. this error exit may leave filp->f_flags with O_NONBLOCK set */
filp->private_data = tty;
check_tty_count(tty, "tty_open");
if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
@@ -1123,11 +1239,6 @@ retry_open:
return 0;
}
-/*
- * Note that releasing a pty master also releases the child, so
- * we have to make the redirection checks after that and on both
- * sides of a pty.
- */
static int tty_release(struct inode * inode, struct file * filp)
{
release_dev(filp);
@@ -1139,7 +1250,7 @@ static unsigned int tty_poll(struct file * filp, poll_table * wait)
struct tty_struct * tty;
tty = (struct tty_struct *)filp->private_data;
- if (tty_paranoia_check(tty, filp->f_inode->i_rdev, "tty_poll"))
+ if (tty_paranoia_check(tty, filp->f_dentry->d_inode->i_rdev, "tty_poll"))
return 0;
if (tty->ldisc.poll)
@@ -1545,6 +1656,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 603250b81..c0d7440c3 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 b3c25cd2a..51d57fe9c 100644
--- a/drivers/isdn/avmb1/capiutil.c
+++ b/drivers/isdn/avmb1/capiutil.c
@@ -1,5 +1,5 @@
/*
- * $Id: capiutil.c,v 1.3 1997/05/18 09:24:18 calle Exp $
+ * $Id: capiutil.c,v 1.1 1997/06/08 14:58:41 ralf Exp $
*
* CAPI 2.0 convert capi message to capi message struct
*
@@ -7,6 +7,9 @@
* Rewritten for Linux 1996 by Carsten Paeth (calle@calle.in-berlin.de)
*
* $Log: capiutil.c,v $
+ * Revision 1.1 1997/06/08 14:58:41 ralf
+ * These files were missing in the 2.1.42 merge.
+ *
* Revision 1.3 1997/05/18 09:24:18 calle
* added verbose disconnect reason reporting to avmb1.
* some fixes in capi20 interface.
@@ -26,6 +29,7 @@
*
*/
#include <linux/module.h>
+#include <linux/config.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/stddef.h>
diff --git a/drivers/net/8390.c b/drivers/net/8390.c
index fbf67cb42..1150ddcf4 100644
--- a/drivers/net/8390.c
+++ b/drivers/net/8390.c
@@ -186,6 +186,7 @@ static int ei_start_xmit(struct sk_buff *skb, struct device *dev)
/* Mask interrupts from the ethercard. */
outb_p(0x00, e8390_base + EN0_IMR);
+ synchronize_irq();
if (dev->interrupt) {
printk("%s: Tx request while isr active.\n",dev->name);
outb_p(ENISR_ALL, e8390_base + EN0_IMR);
diff --git a/drivers/net/Config.in b/drivers/net/Config.in
index 23befe03c..bba3e43b8 100644
--- a/drivers/net/Config.in
+++ b/drivers/net/Config.in
@@ -84,6 +84,7 @@ if [ "$CONFIG_NET_ETHERNET" = "y" ]; then
tristate 'Digi Intl. RightSwitch SE-X support' CONFIG_DGRS
tristate 'EtherExpressPro/100 support' CONFIG_EEXPRESS_PRO100
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ tristate 'TI ThunderLAN support (EXPERIMENTAL)' CONFIG_TLAN
tristate 'Racal-Interlan EISA ES3210 support (EXPERIMENTAL)' CONFIG_ES3210
bool 'Zenith Z-Note support (EXPERIMENTAL)' CONFIG_ZNET
fi
@@ -113,7 +114,12 @@ fi
#
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
if [ "$CONFIG_ATALK" != "n" ]; then
- tristate 'LocalTalk PC support' CONFIG_LTPC
+ tristate 'Apple/Farallon LocalTalk PC support' CONFIG_LTPC
+ tristate 'COPS LocalTalk PC support' CONFIG_COPS
+ if [ "$CONFIG_COPS" != "n" ]; then
+ bool 'Dayna firmware support' CONFIG_COPS_DAYNA
+ bool 'Tangent firmware support' CONFIG_COPS_TANGENT
+ fi
fi
fi
@@ -146,12 +152,14 @@ 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
- 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
+# if [ -f doesn't work with xconfig. Enable it again when the drivers are really
+# included.
+# 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/Makefile b/drivers/net/Makefile
index 3a8721933..b149eac58 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -391,6 +391,14 @@ else
endif
endif
+ifeq ($(CONFIG_TLAN),y)
+L_OBJS += tlan.o
+else
+ ifeq ($(CONFIG_TLAN),m)
+ M_OBJS += tlan.o
+ endif
+endif
+
ifeq ($(CONFIG_ZNET),y)
L_OBJS += znet.o
endif
@@ -663,6 +671,14 @@ else
endif
endif
+ifeq ($(CONFIG_COPS),y)
+L_OBJS += cops.o
+else
+ ifeq ($(CONFIG_COPS),m)
+ M_OBJS += cops.o
+ endif
+endif
+
ifeq ($(CONFIG_BAYCOM),y)
L_OBJS += baycom.o
CONFIG_HDLCDRV_BUILTIN = y
@@ -785,7 +801,7 @@ dlci.o: dlci.c CONFIG
sdladrv.o: sdladrv.c CONFIG
wanpipe.o: $(WANPIPE_OBJS)
- ld -r -o $@ $^
+ ld -r -o $@ $(WANPIPE_OBJS)
sdlamain.o: sdlamain.c CONFIG
diff --git a/drivers/net/README.wanpipe b/drivers/net/README.wanpipe
index bcfed26fe..9650edb73 100644
--- a/drivers/net/README.wanpipe
+++ b/drivers/net/README.wanpipe
@@ -1,10 +1,10 @@
------------------------------------------------------------------------------
WANPIPE(tm) Multiprotocol WAN Driver for Linux WAN Router
------------------------------------------------------------------------------
-Release 3.0.0
-December 31, 1996
+Release 3.1.0
+January 30, 1997
Author: Gene Kozin <genek@compuserve.com>
-Copyright (c) 1995-1996 Sangoma Technologies Inc.
+Copyright (c) 1995-1997 Sangoma Technologies Inc.
------------------------------------------------------------------------------
INTRODUCTION
@@ -81,6 +81,12 @@ include/linux:
REVISION HISTORY
+3.1.0 January 30, 1997
+
+ o Implemented IOCTL for executing adapter commands.
+ o Fixed a bug in frame relay code causing driver configured as a FR
+ switch to be stuck in WAN_DISCONNECTED mode.
+
3.0.0 December 31, 1996
o Uses Linux WAN Router interface
diff --git a/drivers/net/Space.c b/drivers/net/Space.c
index ce820df60..64bc62c41 100644
--- a/drivers/net/Space.c
+++ b/drivers/net/Space.c
@@ -89,6 +89,7 @@ extern int atarilance_probe(struct device *);
extern int a2065_probe(struct device *);
extern int ariadne_probe(struct device *);
extern int hydra_probe(struct device *);
+extern int tlan_probe(struct device *);
extern int cs89x0_probe(struct device *dev);
/* Detachable devices ("pocket adaptors") */
@@ -242,6 +243,9 @@ __initfunc(static int ethif_probe(struct device *dev))
#ifdef CONFIG_SUNLANCE
&& sparc_lance_probe(dev)
#endif
+#ifdef CONFIG_TLAN
+ && tlan_probe(dev)
+#endif
#ifdef CONFIG_HAPPYMEAL
&& happy_meal_probe(dev)
#endif
@@ -301,6 +305,17 @@ static struct device atp_dev = {
# define NEXT_DEV (&dev_ltpc)
#endif /* LTPC */
+#if defined(CONFIG_COPS)
+ extern int cops_probe(struct device *);
+ static struct device dev_cops = {
+ "lt0",
+ 0, 0, 0, 0,
+ 0x0, 0,
+ 0, 0, 0, NEXT_DEV, cops_probe };
+# undef NEXT_DEV
+# define NEXT_DEV (&dev_cops)
+#endif /* COPS */
+
/* The first device defaults to I/O base '0', which means autoprobe. */
#ifndef ETH0_ADDR
# define ETH0_ADDR 0
diff --git a/drivers/net/cops.c b/drivers/net/cops.c
new file mode 100644
index 000000000..66e3c5fea
--- /dev/null
+++ b/drivers/net/cops.c
@@ -0,0 +1,1018 @@
+/* cops.c: LocalTalk driver for Linux.
+ *
+ * Authors:
+ * - Jay Schulist <Jay.Schulist@spacs.k12.wi.us>
+ *
+ * With more than a little help from;
+ * - Alan Cox <Alan.Cox@linux.org>
+ *
+ * Derived from:
+ * - skeleton.c: A network driver outline for linux.
+ * Written 1993-94 by Donald Becker.
+ * - ltpc.c: A driver for the LocalTalk PC card.
+ * Written by Bradford W. Johnson.
+ *
+ * Copyright 1993 United States Government as represented by the
+ * Director, National Security Agency.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU Public License, incorporated herein by reference.
+ *
+ * Changes:
+ * 19970608 Alan Cox Allowed dual card type support
+ * Can set board type in insmod
+ * Hooks for cops_setup routine
+ * (not yet implemented).
+ */
+
+static const char *version =
+ "cops.c:v0.01 3/17/97 Jay Schulist <Jay.Schulist@spacs.k12.wi.us>\n";
+/*
+ * Sources:
+ * COPS Localtalk SDK. This provides almost all of the information
+ * needed.
+ */
+
+/*
+ * insmod/modprobe configurable stuff.
+ * - IO Port, choose one your card supports or 0 if you dare.
+ * - IRQ, also choose one your card supports or nothing and let
+ * the driver figure it out.
+ */
+
+#include <linux/config.h>
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/errno.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <linux/if_arp.h>
+#include <linux/if_ltalk.h> /* For ltalk_setup() */
+#include <linux/delay.h> /* For udelay() */
+#include <linux/atalk.h>
+
+#include "cops.h" /* Our Stuff */
+#include "cops_ltdrv.h" /* Firmware code for Tangent type cards. */
+#include "cops_ffdrv.h" /* Firmware code for Dayna type cards. */
+
+/*
+ * The name of the card. Is used for messages and in the requests for
+ * io regions, irqs and dma channels
+ */
+
+static const char *cardname = "cops";
+
+#ifdef CONFIG_COPS_DAYNA
+static int board_type = DAYNA; /* Module exported */
+#else
+static int board_type = TANGENT;
+#endif
+
+#ifdef MODULE
+static int io = 0x240; /* Default IO for Dayna */
+static int irq = 5; /* Default IRQ */
+#else
+static int io = 0; /* Default IO for Dayna */
+static int irq = 0; /* Default IRQ */
+#endif
+
+/*
+ * COPS Autoprobe information.
+ * Right now if port address is right but IRQ is not 5 this will
+ * return a 5 no matter what since we will still get a status response.
+ * Need one more additional check to narrow down after we have gotten
+ * the ioaddr. But since only other possible IRQs is 3 and 4 so no real
+ * hurry on this. I *STRONGLY* recommend using IRQ 5 for your card with
+ * this driver.
+ *
+ * This driver has 2 modes and they are: Dayna mode and Tangent mode.
+ * Each mode corresponds with the type of card. It has been found
+ * that there are 2 main types of cards and all other cards are
+ * the same and just have different names or only have minor differences
+ * such as more IO ports. As this driver is tested it will
+ * become more clear on exactly what cards are supported. The driver
+ * defaults to using Dayna mode. To change the drivers mode adjust
+ * drivers/net/CONFIG, and the line COPS_OPTS = -DDAYNA to -DTANGENT.
+ *
+ * This driver should support:
+ * TANGENT driver mode:
+ * Tangent ATB-II, Novell NL-1000, Daystar Digital LT-200
+ * DAYNA driver mode:
+ * Dayna DL2000/DaynaTalk PC (Half Length), COPS LT-95, Farallon PhoneNET PC III
+ * Other cards possibly supported mode unkown though:
+ * Farallon PhoneNET PC II
+ * Dayna DL2000 (Full length)
+ *
+ * Cards NOT supported by this driver but supported by the ltpc.c
+ * driver written by Bradford W. Johnson <johns393@maroon.tc.umn.edu>
+ * Farallon PhoneNET PC
+ * Original Apple LocalTalk PC card
+ */
+
+/*
+ * Zero terminated list of IO ports to probe.
+ */
+
+static unsigned int cops_portlist[] = {
+ 0x240, 0x340, 0x200, 0x210, 0x220, 0x230, 0x260,
+ 0x2A0, 0x300, 0x310, 0x320, 0x330, 0x350, 0x360,
+ 0
+};
+
+/*
+ * Zero terminated list of IRQ ports to probe.
+ */
+
+static int cops_irqlist[] = {
+ 5, 4, 3, 0
+};
+
+/* use 0 for production, 1 for verification, 2 for debug, 3 for very verbose debug */
+#ifndef COPS_DEBUG
+#define COPS_DEBUG 1
+#endif
+static unsigned int cops_debug = COPS_DEBUG;
+
+/* The number of low I/O ports used by the card. */
+#define COPS_IO_EXTENT 8
+
+/* Information that needs to be kept for each board. */
+
+struct cops_local
+{
+ struct enet_statistics stats;
+ int board; /* Holds what board type is. */
+ int nodeid; /* Set to 1 once have nodeid. */
+ unsigned char node_acquire; /* Node ID when acquired. */
+};
+
+/* Allocate a new device with the form of lt0, lt1, lt2, etc. */
+struct device *cops_dev_alloc(char *name)
+{
+ int i=0;
+ struct device *d=kmalloc(sizeof(struct device)+8, GFP_KERNEL);
+
+ memset(d,0,sizeof(*d)); /* Clear the structure */
+ if(d==NULL)
+ return NULL;
+ d->name=(char *)(d+1); /* Name string space */
+
+ /* Get next free device name */
+ for(i=0;i<100;i++)
+ {
+ sprintf(d->name,name,i);
+ if(dev_get(d->name)==NULL)
+ return d;
+ }
+ return NULL; /* Over 100 of the things .. bail out! */
+}
+
+/* Index to functions, as function prototypes. */
+extern int cops_probe (struct device *dev);
+static int cops_probe1 (struct device *dev, int ioaddr);
+static int cops_irq (int ioaddr, int board);
+
+static int cops_open (struct device *dev);
+static int cops_jumpstart (struct device *dev);
+static void cops_reset (struct device *dev, int sleep);
+static void cops_load (struct device *dev);
+static int cops_nodeid (struct device *dev, int nodeid);
+
+static void cops_interrupt (int irq, void *dev_id, struct pt_regs *regs);
+static void cops_rx (struct device *dev);
+static int cops_send_packet (struct sk_buff *skb, struct device *dev);
+static void set_multicast_list (struct device *dev);
+static int cops_hard_header (struct sk_buff *skb, struct device *dev,
+ unsigned short type, void *daddr, void *saddr, unsigned len);
+
+static int cops_ioctl (struct device *dev, struct ifreq *rq, int cmd);
+static int cops_close (struct device *dev);
+static struct enet_statistics *cops_get_stats (struct device *dev);
+
+
+/*
+ * Check for a network adaptor of this type, and return '0' iff one exists.
+ * If dev->base_addr == 0, probe all likely locations.
+ * If dev->base_addr == 1, always return failure.
+ * If dev->base_addr == 2, allocate space for the device and return success
+ * (detachable devices only).
+ */
+int cops_probe(struct device *dev)
+{
+ int i;
+ int base_addr = dev ? dev->base_addr : 0;
+
+ if (base_addr == 0 && io)
+ base_addr=io;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return cops_probe1(dev, base_addr);
+ else if (base_addr != 0) /* Don't probe at all. */
+ return -ENXIO;
+
+ for (i=0; cops_portlist[i]; i++) {
+ int ioaddr = cops_portlist[i];
+ if (check_region(ioaddr, COPS_IO_EXTENT))
+ continue;
+ if (cops_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+
+ /* No "lt" devices found. */
+ printk(KERN_WARNING "%s: No COPS localtalk devices found!\n", dev->name);
+ return -ENODEV;
+}
+
+/*
+ * This is the real probe routine. Linux has a history of friendly device
+ * probes on the ISA bus. A good device probes avoids doing writes, and
+ * verifies that the correct device exists and functions.
+ */
+static int cops_probe1(struct device *dev, int ioaddr)
+{
+ struct cops_local *lp;
+ static unsigned version_printed = 0;
+ int irqaddr = 0;
+ int irqval;
+
+ int board = board_type;
+
+/* Defined here to save some trouble */
+
+ /* Allocate a new 'dev' if needed. */
+ if (dev == NULL)
+ {
+ dev=cops_dev_alloc(dev->name); /* New "lt" device; beyond lt0. */
+ if(dev==NULL)
+ return -ENOMEM;
+ }
+
+ if (cops_debug && version_printed++ == 0)
+ printk("%s", version);
+
+ /* Fill in the 'dev' fields. */
+ dev->base_addr = ioaddr;
+
+ /*
+ * Since this board has jumpered interrupts, allocate the interrupt
+ * vector now. There is no point in waiting since no other device
+ * can use the interrupt, and this marks the irq as busy. Jumpered
+ * interrupts are typically not reported by the boards, and we must
+ * used AutoIRQ to find them.
+ *
+ */
+
+ if (dev->irq < 2 && irq)
+ dev->irq = irq;
+
+ if (dev->irq < 2)
+ {
+ irqaddr = cops_irq(ioaddr, board); /* COPS AutoIRQ routine */
+ if (irqaddr == 0)
+ return -EAGAIN; /* No IRQ found on this port */
+ else
+ dev->irq = irqaddr;
+ }
+ else if (dev->irq == 2)
+ /*
+ * Fixup for users that don't know that IRQ 2 is really
+ * IRQ 9, or don't know which one to set.
+ */
+ dev->irq = 9;
+
+ /* Snarf the interrupt now. */
+ irqval = request_irq(dev->irq, &cops_interrupt, 0, cardname, NULL);
+ if (irqval)
+ {
+ printk(KERN_WARNING "%s: Unable to get IRQ %d (irqval=%d).\n", dev->name, dev->irq, irqval);
+ return -EAGAIN;
+ }
+
+ dev->hard_start_xmit = &cops_send_packet;
+
+ /* Initialize the device structure. */
+ dev->priv = kmalloc(sizeof(struct cops_local), GFP_KERNEL);
+ if (dev->priv == NULL)
+ return -ENOMEM;
+
+ lp = (struct cops_local *)dev->priv;
+ memset(lp, 0, sizeof(struct cops_local));
+
+ /* Copy local board variable to lp struct. */
+ lp->board = board;
+
+ /* Tell the user where the card is and what mode were in. */
+ if(board==DAYNA)
+ printk("%s: %s found at %#3x, using IRQ %d, in Dayna mode.\n",
+ dev->name, cardname, ioaddr, dev->irq);
+ if(board==TANGENT)
+ printk("%s: %s found at %#3x, using IRQ %d, in Tangent mode.\n",
+ dev->name, cardname, ioaddr, dev->irq);
+
+ /* Grab the region so no one else tries to probe our ioports. */
+ request_region(ioaddr, COPS_IO_EXTENT, cardname);
+
+ /* Fill in the fields of the device structure with LocalTalk values. */
+ ltalk_setup(dev);
+
+ dev->hard_header = cops_hard_header;
+ dev->get_stats = cops_get_stats;
+ dev->open = cops_open;
+ dev->stop = cops_close;
+ dev->do_ioctl = &cops_ioctl;
+ dev->set_multicast_list = &set_multicast_list;
+ dev->mc_list = NULL;
+
+ return 0;
+}
+
+static int cops_irq (int ioaddr, int board)
+{ /*
+ * This does not use the IRQ to determine where the IRQ is. We just
+ * assume that when we get a correct status response that is the IRQ then.
+ * This really just verifies the IO port but since we only have access
+ * to such a small number of IRQs (5, 4, 3) this is not bad.
+ * This will probably not work for more than one card.
+ */
+ int irqaddr=0;
+ int i, x, status;
+
+ if(board==DAYNA)
+ {
+ outb(0, ioaddr+DAYNA_RESET);
+ inb(ioaddr+DAYNA_RESET);
+ udelay(333333);
+ }
+ if(board==TANGENT)
+ {
+ inb(ioaddr);
+ outb(0, ioaddr);
+ outb(0, ioaddr+TANG_RESET);
+ }
+
+ for(i=0; cops_irqlist[i] !=0; i++)
+ {
+ irqaddr = cops_irqlist[i];
+ for(x = 0xFFFF; x>0; x --) /* wait for response */
+ {
+ if(board==DAYNA)
+ {
+ status = (inb(ioaddr+DAYNA_CARD_STATUS)&3);
+ if (status == 1)
+ return irqaddr;
+ }
+ if(board==TANGENT)
+ {
+ if((inb(ioaddr+TANG_CARD_STATUS)& TANG_TX_READY) !=0)
+ return irqaddr;
+ }
+ }
+ }
+ return 0; /* no IRQ found */
+}
+
+/*
+ * Open/initialize the board. This is called (in the current kernel)
+ * sometime after booting when the 'ifconfig' program is run.
+ */
+static int cops_open(struct device *dev)
+{
+ irq2dev_map[dev->irq] = dev;
+
+ cops_jumpstart(dev); /* Start the card up. */
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif
+
+ return 0;
+}
+
+/*
+ * This allows for a dynamic start/restart of the entire card.
+ */
+static int cops_jumpstart(struct device *dev)
+{
+ struct cops_local *lp = (struct cops_local *)dev->priv;
+
+ /*
+ * Once the card has the firmware loaded and has acquired
+ * the nodeid, if it is reset it will lose it all.
+ */
+ cops_reset(dev,1); /* Need to reset card before load firmware. */
+ cops_load(dev); /* Load the firmware. */
+
+ /*
+ * If atalkd already gave us a nodeid we will use that
+ * one again, else we wait for atalkd to give us a nodeid
+ * in cops_ioctl. This may cause a problem if someone steals
+ * our nodeid while we are resetting.
+ */
+ if(lp->nodeid == 1)
+ cops_nodeid(dev,lp->node_acquire);
+
+ return 0;
+}
+
+static int tangent_wait_reset(int ioaddr)
+{
+ int timeout=0;
+
+ while(timeout < 5000 && (inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0)
+ udelay(1000); /* Wait 1000 useconds */
+
+ return 0;
+}
+
+/*
+ * Reset the LocalTalk board.
+ */
+static void cops_reset(struct device *dev, int sleep)
+{
+ struct cops_local *lp = (struct cops_local *)dev->priv;
+ int ioaddr=dev->base_addr;
+
+ if(lp->board==TANGENT)
+ {
+ inb(ioaddr); /* Clear request latch. */
+ outb(0,ioaddr); /* Clear the TANG_TX_READY flop. */
+ outb(0, ioaddr+TANG_RESET); /* Reset the adapter. */
+
+ /* Can take 5 seconds max - youch! */
+ if(sleep)
+ {
+ long snapt=jiffies;
+ while(jiffies-snapt<5*HZ)
+ {
+ if(inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)
+ break;
+ schedule();
+ }
+ }
+ else
+ tangent_wait_reset(ioaddr);
+ outb(0, ioaddr+TANG_CLEAR_INT);
+ }
+ if(lp->board==DAYNA)
+ {
+ outb(0, ioaddr+DAYNA_RESET); /* Assert the reset port */
+ inb(ioaddr+DAYNA_RESET); /* Clear the reset */
+ if(sleep)
+ {
+ long snap=jiffies;
+
+ /* Let card finish initializing, about 1/3 second */
+ while(jiffies-snap<HZ/3)
+ schedule();
+ }
+ else
+ udelay(333333);
+ }
+ dev->tbusy=0;
+
+ return;
+}
+
+static void cops_load (struct device *dev)
+{
+ struct ifreq ifr;
+ struct ltfirmware *ltf= (struct ltfirmware *)&ifr.ifr_data;
+ struct cops_local *lp=(struct cops_local *)dev->priv;
+ int ioaddr=dev->base_addr;
+ int length, i = 0;
+
+ strcpy(ifr.ifr_name,"lt0");
+
+ /* Get card's firmware code and do some checks on it. */
+#ifdef CONFIG_COPS_DAYNA
+ if (lp->board==DAYNA)
+ {
+ ltf->length=sizeof(ffdrv_code);
+ ltf->data=ffdrv_code;
+ }
+ else
+#endif
+#ifdef CONFIG_COPS_TANGENT
+ if (lp->board==TANGENT)
+ {
+ ltf->length=sizeof(ltdrv_code);
+ ltf->data=ltdrv_code;
+ }
+ else
+#endif
+ {
+ printk(KERN_INFO "%s; unsupported board type.\n", dev->name);
+ return;
+ }
+
+ /* Check to make sure firmware is correct length. */
+ if(lp->board==DAYNA && ltf->length!=5983)
+ {
+ printk(KERN_WARNING "%s: Firmware is not length of FFDRV.BIN.\n", dev->name);
+ return;
+ }
+ if(lp->board==TANGENT && ltf->length!=2501)
+ {
+ printk(KERN_WARNING "%s: Firmware is not length of DRVCODE.BIN.\n", dev->name);
+ return;
+ }
+
+ if(lp->board==DAYNA)
+ {
+ /*
+ * We must wait for a status response
+ * with the DAYNA board.
+ */
+ while(++i<65536)
+ {
+ if((inb(ioaddr+DAYNA_CARD_STATUS)&3)==1)
+ break;
+ }
+
+ if(i==65536)
+ return;
+ }
+
+ /*
+ * Upload the firmware and kick. Byte-by-byte works nicely here.
+ */
+ i=0;
+ length = ltf->length;
+ while(length--)
+ {
+ outb(ltf->data[i], ioaddr);
+ i++;
+ }
+
+ if(cops_debug > 1)
+ printk(KERN_DEBUG "%s: Uploaded firmware - %d bytes of %d bytes.\n", dev->name, i, ltf->length);
+
+ if(lp->board==DAYNA)
+ outb(1, ioaddr+DAYNA_INT_CARD); /* Tell Dayna to run the firmware code. */
+ else
+ inb(ioaddr); /* Tell Tang to run the firmware code. */
+
+ if(lp->board==TANGENT)
+ {
+ tangent_wait_reset(ioaddr);
+ inb(ioaddr); /* Clear initial ready signal. */
+ }
+
+ return;
+}
+
+/*
+ * Get the LocalTalk Nodeid from the card. We can suggest
+ * any nodeid 1-254. The card will try and get that exact
+ * address else we can specify 0 as the nodeid and the card
+ * will autoprobe for a nodeid.
+ */
+static int cops_nodeid (struct device *dev, int nodeid)
+{
+ struct cops_local *lp = (struct cops_local *) dev->priv;
+ int ioaddr = dev->base_addr;
+
+ if (lp->board == DAYNA)
+ {
+ /* Empty any pending adapter responses. */
+ while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0)
+ {
+ outb(0, ioaddr+COPS_CLEAR_INT); /* Clear any interrupt. */
+ if((inb(ioaddr+DAYNA_CARD_STATUS)&0x03)==DAYNA_RX_REQUEST)
+ cops_rx(dev); /* Kick out any packet waiting. */
+ schedule();
+ }
+
+ outb(2, ioaddr); /* Output command packet length as 2. */
+ outb(0, ioaddr);
+ outb(LAP_INIT, ioaddr); /* Send LAP_INIT command byte. */
+ outb(nodeid, ioaddr); /* Suggest node address. */
+ }
+
+ if (lp->board == TANGENT)
+ {
+ /* Empty any pending adapter responses. */
+ while(inb(ioaddr+TANG_CARD_STATUS)&TANG_RX_READY)
+ {
+ outb(0, ioaddr+COPS_CLEAR_INT); /* Clear any interrupt. */
+ cops_rx(dev); /* Kick out any packet waiting. */
+ schedule();
+ }
+
+ /* Not sure what Tangent does if random nodeid we picked is already used. */
+ if(nodeid == 0) /* Seed. */
+ nodeid = jiffies&0xFF; /* Get a random try .*/
+ outb(2, ioaddr); /* Command length LSB. */
+ outb(0, ioaddr); /* Command length MSB. */
+ outb(LAP_INIT, ioaddr); /* Send LAP_INIT command byte. */
+ outb(nodeid, ioaddr); /* LAP address hint. */
+ outb(0xFF, ioaddr); /* Interrupt level to use (NONE). */
+ }
+
+ lp->node_acquire=0; /* Set nodeid holder to 0. */
+ while(lp->node_acquire==0) /* Get *True* nodeid finally. */
+ {
+ outb(0, ioaddr+COPS_CLEAR_INT); /* Clear any interrupt. */
+
+ if(lp->board == DAYNA)
+ {
+ if((inb(ioaddr+DAYNA_CARD_STATUS)&0x03)==DAYNA_RX_REQUEST)
+ cops_rx(dev); /* Grab the nodeid put in lp->node_acquire. */
+ }
+ if(lp->board == TANGENT)
+ {
+ if(inb(ioaddr+TANG_CARD_STATUS)&TANG_RX_READY)
+ cops_rx(dev); /* Grab the nodeid put in lp->node_acquire. */
+ }
+ schedule();
+ }
+
+ if(cops_debug > 1)
+ printk(KERN_DEBUG "%s: Node ID %d has been acquired.\n", dev->name, lp->node_acquire);
+
+ lp->nodeid=1; /* Set got nodeid to 1. */
+
+ return 0;
+}
+
+/*
+ * The typical workload of the driver:
+ * Handle the network interface interrupts.
+ */
+static void cops_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+ struct device *dev = (struct device *) irq2dev_map[irq];
+ struct cops_local *lp;
+ int ioaddr, status;
+ int boguscount = 0;
+
+ if (dev == NULL)
+ {
+ printk(KERN_WARNING "%s: irq %d for unknown device.\n", cardname, irq);
+ return;
+ }
+ dev->interrupt = 1;
+
+ ioaddr = dev->base_addr;
+ lp = (struct cops_local *)dev->priv;
+
+ do
+ {
+ /* Clear any interrupt. */
+ outb(0, ioaddr + COPS_CLEAR_INT);
+
+ if(lp->board==DAYNA)
+ {
+ status=inb(ioaddr+DAYNA_CARD_STATUS);
+ if((status&0x03)==DAYNA_RX_REQUEST)
+ cops_rx(dev);
+ }
+ else
+ {
+ status=inb(ioaddr+TANG_CARD_STATUS);
+ if (status&TANG_RX_READY)
+ cops_rx(dev);
+ }
+
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ } while (++boguscount < 20 );
+ dev->interrupt = 0;
+
+ return;
+}
+
+/*
+ * We have a good packet(s), get it/them out of the buffers.
+ */
+static void cops_rx(struct device *dev)
+{
+ int pkt_len = 0;
+ int rsp_type = 0;
+ struct sk_buff *skb;
+ struct cops_local *lp = (struct cops_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int boguscount = 0;
+
+ cli(); /* Disable interrupts. */
+
+ if(lp->board==DAYNA)
+ {
+ outb(0, ioaddr); /* Send out Zero length. */
+ outb(0, ioaddr);
+ outb(DATA_READ, ioaddr); /* Send read command out. */
+
+ /* Wait for DMA to turn around. */
+ while(++boguscount<1000000)
+ {
+ if((inb(ioaddr+DAYNA_CARD_STATUS)&0x03)==DAYNA_RX_READY)
+ break;
+ }
+
+ if(boguscount==1000000)
+ {
+ printk(KERN_WARNING "%s: DMA timed out.\n",dev->name);
+ return;
+ }
+ }
+
+ /* Get response length. */
+ pkt_len = inb(ioaddr) & 0xFF;
+ pkt_len |= (inb(ioaddr) << 8);
+ /* Input IO code. */
+ rsp_type=inb(ioaddr);
+
+ /* Malloc up new buffer. */
+ skb = dev_alloc_skb(pkt_len);
+ if (skb == NULL)
+ {
+ printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name);
+ lp->stats.rx_dropped++;
+ while(pkt_len--) /* Discard packet */
+ inb(ioaddr);
+ return;
+ }
+ skb->dev = dev;
+ skb_put(skb, pkt_len);
+ skb->protocol = htons(ETH_P_LOCALTALK);
+
+ insb(ioaddr, skb->data, pkt_len); /* Eat the Data */
+
+ if(lp->board==DAYNA)
+ outb(1, ioaddr+DAYNA_INT_CARD); /* Interrupt the card. */
+
+ sti(); /* Restore interrupts. */
+
+ /* Check for bad response length */
+ if (pkt_len < 0 || pkt_len > MAX_LLAP_SIZE)
+ {
+ printk(KERN_NOTICE "%s: Bad packet length of %d bytes.\n", dev->name, pkt_len);
+ lp->stats.tx_errors++;
+ kfree_skb(skb, FREE_READ);
+ return;
+ }
+
+ /* Set nodeid and then get out. */
+ if(rsp_type == LAP_INIT_RSP)
+ {
+ lp->node_acquire = skb->data[0]; /* Nodeid taken from received packet. */
+ kfree_skb(skb, FREE_READ);
+ return;
+ }
+
+ /* One last check to make sure we have a good packet. */
+ if(rsp_type != LAP_RESPONSE)
+ {
+ printk("%s: Bad packet type %d.\n", dev->name, rsp_type);
+ lp->stats.tx_errors++;
+ kfree_skb(skb, FREE_READ);
+ return;
+ }
+
+ skb->mac.raw = skb->data; /* Point to entire packet. */
+ skb_pull(skb,3);
+ skb->h.raw = skb->data; /* Point to just the data (Skip header). */
+
+ /* Update the counters. */
+ lp->stats.rx_packets++;
+ lp->stats.rx_bytes += skb->len;
+
+ /* Send packet to a higher place. */
+ netif_rx(skb);
+
+ return;
+}
+
+/*
+ * Make the card transmit a LocalTalk packet.
+ */
+static int cops_send_packet(struct sk_buff *skb, struct device *dev)
+{
+ struct cops_local *lp = (struct cops_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+
+ if (dev->tbusy)
+ {
+ /*
+ * If we get here, some higher level has decided we are broken.
+ * There should really be a "kick me" function call instead.
+ */
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 5)
+ return 1;
+ lp->stats.tx_errors++;
+ if(lp->board==TANGENT)
+ {
+ if((inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0)
+ printk(KERN_WARNING "%s: No TX complete interrupt.\n", dev->name);
+ }
+ printk(KERN_WARNING "%s: Transmit timed out.\n", dev->name);
+ cops_jumpstart(dev); /* Restart the card. */
+ dev->tbusy=0;
+ dev->trans_start = jiffies;
+ }
+
+ /*
+ * Block a timer-based transmit from overlapping. This could better be
+ * done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
+ */
+ if (test_and_set_bit(0, (void*) &dev->tbusy) != 0)
+ printk(KERN_WARNING "%s: Transmitter access conflict.\n", dev->name);
+ else
+ {
+ cli(); /* Disable interrupts. */
+ if(lp->board == DAYNA) /* Wait for adapter transmit buffer. */
+ while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0);
+ if(lp->board == TANGENT) /* Wait for adapter transmit buffer. */
+ while((inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0);
+
+ /* Output IO length. */
+ if(lp->board == DAYNA)
+ {
+ outb(skb->len, ioaddr);
+ outb(skb->len >> 8, ioaddr);
+ }
+ else
+ {
+ outb(skb->len&0x0FF, ioaddr);
+ outb((skb->len >> 8)&0x0FF, ioaddr);
+ }
+
+ /* Output IO code. */
+ outb(LAP_WRITE, ioaddr);
+
+ if(lp->board == DAYNA) /* Check the transmit buffer again. */
+ while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0);
+
+ outsb(ioaddr, skb->data, skb->len); /* Send out the data. */
+
+ if(lp->board==DAYNA) /* The Dayna requires you kick the card. */
+ outb(1, ioaddr+DAYNA_INT_CARD);
+
+ sti(); /* Restore interrupts. */
+
+ /* Done sending packet, update counters and cleanup. */
+ lp->stats.tx_packets++;
+ lp->stats.tx_bytes += skb->len;
+ dev->trans_start = jiffies;
+ }
+
+ dev_kfree_skb (skb, FREE_WRITE);
+ dev->tbusy = 0;
+
+ return 0;
+}
+
+/*
+ * Dummy function to keep the Appletalk layer happy.
+ */
+
+static void set_multicast_list(struct device *dev)
+{
+ if(cops_debug >= 3)
+ printk("%s: set_mulicast_list executed. NeatO.\n", dev->name);
+}
+
+/*
+ * Another Dummy function to keep the Appletalk layer happy.
+ */
+
+static int cops_hard_header(struct sk_buff *skb, struct device *dev,
+ unsigned short type, void *daddr, void *saddr, unsigned len)
+{
+ if(cops_debug >= 3)
+ printk("%s: cops_hard_header executed. Wow!\n", dev->name);
+ return 0;
+}
+
+/*
+ * System ioctls for the COPS LocalTalk card.
+ */
+
+static int cops_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+ struct cops_local *lp = (struct cops_local *)dev->priv;
+ struct sockaddr_at *sa=(struct sockaddr_at *)&ifr->ifr_addr;
+ struct at_addr *aa=(struct at_addr *)&dev->pa_addr;
+
+ switch(cmd)
+ {
+ case SIOCSIFADDR:
+ /* Get and set the nodeid and network # atalkd wants. */
+ cops_nodeid(dev, sa->sat_addr.s_node);
+ aa->s_net = sa->sat_addr.s_net;
+ aa->s_node = lp->node_acquire;
+
+ /* Set broardcast address. */
+ dev->broadcast[0] = 0xFF;
+
+ /* Set hardware address. */
+ dev->dev_addr[0] = aa->s_node;
+ dev->addr_len = 1;
+ return 0;
+
+ case SIOCGIFADDR:
+ sa->sat_addr.s_net = aa->s_net;
+ sa->sat_addr.s_node = aa->s_node;
+ return 0;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+/*
+ * The inverse routine to cops_open().
+ */
+
+static int cops_close(struct device *dev)
+{
+ dev->tbusy = 1;
+ dev->start = 0;
+ irq2dev_map[dev->irq] = 0;
+
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif
+
+ return 0;
+}
+
+/*
+ * Get the current statistics.
+ * This may be called with the card open or closed.
+ */
+static struct enet_statistics *cops_get_stats(struct device *dev)
+{
+ struct cops_local *lp = (struct cops_local *)dev->priv;
+ return &lp->stats;
+}
+
+#ifdef MODULE
+static struct device dev_cops =
+{
+ "lt0", /* device name */
+ 0, 0, 0, 0,
+ 0x0, 0, /* I/O address, IRQ */
+ 0, 0, 0, NULL, cops_probe
+};
+
+
+MODULE_PARM(io, "i");
+MODULE_PARM(irq, "i");
+MODULE_PARM(board_type, "i");
+
+int init_module(void)
+{
+ int result;
+
+ if (io == 0)
+ printk(KERN_WARNING "%s: You shouldn't use auto-probing with insmod!\n", cardname);
+
+ /* Copy the parameters from insmod into the device structure. */
+ dev_cops.base_addr = io;
+ dev_cops.irq = irq;
+
+ if ((result = register_netdev(&dev_cops)) != 0)
+ return result;
+
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ /* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+
+ free_irq(dev_cops.irq, NULL);
+ release_region(dev_cops.base_addr, COPS_IO_EXTENT);
+ unregister_netdev(&dev_cops);
+
+ if (dev_cops.priv)
+ kfree_s(dev_cops.priv, sizeof(struct cops_local));
+}
+#endif /* MODULE */
diff --git a/drivers/net/cops.h b/drivers/net/cops.h
new file mode 100644
index 000000000..a064bc12b
--- /dev/null
+++ b/drivers/net/cops.h
@@ -0,0 +1,60 @@
+/* cops.h: LocalTalk driver for Linux.
+ *
+ * Authors:
+ * - Jay Schulist <Jay.Schulist@spacs.k12.wi.us>
+ */
+
+#ifndef __LINUX_COPSLTALK_H
+#define __LINUX_COPSLTALK_H
+
+#ifdef __KERNEL__
+
+/* Max LLAP size we will accept. */
+#define MAX_LLAP_SIZE 603
+
+/* Tangent */
+#define TANG_CARD_STATUS 1
+#define TANG_CLEAR_INT 1
+#define TANG_RESET 3
+
+#define TANG_TX_READY 1
+#define TANG_RX_READY 2
+
+/* Dayna */
+#define DAYNA_CMD_DATA 0
+#define DAYNA_CLEAR_INT 1
+#define DAYNA_CARD_STATUS 2
+#define DAYNA_INT_CARD 3
+#define DAYNA_RESET 4
+
+#define DAYNA_RX_READY 0
+#define DAYNA_TX_READY 1
+#define DAYNA_RX_REQUEST 3
+
+/* Same on both card types */
+#define COPS_CLEAR_INT 1
+
+/* LAP response codes recieved from the cards. */
+#define LAP_INIT 1 /* Init cmd */
+#define LAP_INIT_RSP 2 /* Init response */
+#define LAP_WRITE 3 /* Write cmd */
+#define DATA_READ 4 /* Data read */
+#define LAP_RESPONSE 4 /* Received ALAP frame response */
+#define LAP_GETSTAT 5 /* Get LAP and HW status */
+#define LAP_RSPSTAT 6 /* Status response */
+
+#endif
+
+/*
+ * Structure to hold the firmware information.
+ */
+struct ltfirmware
+{
+ unsigned int length;
+ unsigned char * data;
+};
+
+#define DAYNA 1
+#define TANGENT 2
+
+#endif
diff --git a/drivers/net/cops_ffdrv.h b/drivers/net/cops_ffdrv.h
new file mode 100644
index 000000000..d3e337afc
--- /dev/null
+++ b/drivers/net/cops_ffdrv.h
@@ -0,0 +1,533 @@
+
+/*
+ * The firmware this driver downloads into the Localtalk card is a
+ * seperate program and is not GPL'd source code, even though the Linux
+ * side driver and the routine that loads this data into the card are.
+ *
+ * It is taken from the COPS SDK and is under the following license
+ *
+ * This material is licensed to you strictly for use in conjunction with
+ * the use of COPS LocalTalk adapters.
+ * There is no charge for this SDK. And no waranty express or implied
+ * about its fitness for any purpose. However, we will cheerefully
+ * refund every penny you paid for this SDK...
+ * Regards,
+ *
+ * Thomas F. Divine
+ * Chief Scientist
+ */
+
+
+/* cops_ffdrv.h: LocalTalk driver firmware dump for Linux.
+ *
+ * Authors:
+ * - Jay Schulist <Jay.Schulist@spacs.k12.wi.us>
+ */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_COPS_DAYNA
+
+unsigned char ffdrv_code[] = {
+ 58,3,0,50,228,149,33,255,255,34,226,149,
+ 249,17,40,152,33,202,154,183,237,82,77,68,
+ 11,107,98,19,54,0,237,176,175,50,80,0,
+ 62,128,237,71,62,32,237,57,51,62,12,237,
+ 57,50,237,57,54,62,6,237,57,52,62,12,
+ 237,57,49,33,107,137,34,32,128,33,83,130,
+ 34,40,128,33,86,130,34,42,128,33,112,130,
+ 34,36,128,33,211,130,34,38,128,62,0,237,
+ 57,16,33,63,148,34,34,128,237,94,205,15,
+ 130,251,205,168,145,24,141,67,111,112,121,114,
+ 105,103,104,116,32,40,67,41,32,49,57,56,
+ 56,32,45,32,68,97,121,110,97,32,67,111,
+ 109,109,117,110,105,99,97,116,105,111,110,115,
+ 32,32,32,65,108,108,32,114,105,103,104,116,
+ 115,32,114,101,115,101,114,118,101,100,46,32,
+ 32,40,68,40,68,7,16,8,34,7,22,6,
+ 16,5,12,4,8,3,6,140,0,16,39,128,
+ 0,4,96,10,224,6,0,7,126,2,64,11,
+ 118,12,6,13,0,14,193,15,0,5,96,3,
+ 192,1,64,9,8,62,9,211,66,62,192,211,
+ 66,62,100,61,32,253,6,28,33,205,129,14,
+ 66,237,163,194,253,129,6,28,33,205,129,14,
+ 64,237,163,194,9,130,201,62,47,50,71,152,
+ 62,47,211,68,58,203,129,237,57,20,58,204,
+ 129,237,57,21,33,77,152,54,132,205,233,129,
+ 58,228,149,254,209,40,6,56,4,62,0,24,
+ 2,219,96,33,233,149,119,230,62,33,232,149,
+ 119,213,33,8,152,17,7,0,25,119,19,25,
+ 119,209,201,251,237,77,245,197,213,229,221,229,
+ 205,233,129,62,1,50,106,137,205,158,139,221,
+ 225,225,209,193,241,251,237,77,245,197,213,219,
+ 72,237,56,16,230,46,237,57,16,237,56,12,
+ 58,72,152,183,32,26,6,20,17,128,2,237,
+ 56,46,187,32,35,237,56,47,186,32,29,219,
+ 72,230,1,32,3,5,32,232,175,50,72,152,
+ 229,221,229,62,1,50,106,137,205,158,139,221,
+ 225,225,24,25,62,1,50,72,152,58,201,129,
+ 237,57,12,58,202,129,237,57,13,237,56,16,
+ 246,17,237,57,16,209,193,241,251,237,77,245,
+ 197,229,213,221,229,237,56,16,230,17,237,57,
+ 16,237,56,20,58,34,152,246,16,246,8,211,
+ 68,62,6,61,32,253,58,34,152,246,8,211,
+ 68,58,203,129,237,57,20,58,204,129,237,57,
+ 21,237,56,16,246,34,237,57,16,221,225,209,
+ 225,193,241,251,237,77,33,2,0,57,126,230,
+ 3,237,100,1,40,2,246,128,230,130,245,62,
+ 5,211,64,241,211,64,201,229,213,243,237,56,
+ 16,230,46,237,57,16,237,56,12,251,70,35,
+ 35,126,254,175,202,77,133,254,129,202,15,133,
+ 230,128,194,191,132,43,58,44,152,119,33,76,
+ 152,119,35,62,132,119,120,254,255,40,4,58,
+ 49,152,119,219,72,43,43,112,17,3,0,237,
+ 56,52,230,248,237,57,52,219,72,230,1,194,
+ 141,131,209,225,237,56,52,246,6,237,57,52,
+ 62,1,55,251,201,62,3,211,66,62,192,211,
+ 66,62,48,211,66,0,0,219,66,230,1,40,
+ 4,219,67,24,240,205,203,135,58,75,152,254,
+ 255,202,128,132,58,49,152,254,161,250,207,131,
+ 58,34,152,211,68,62,10,211,66,62,128,211,
+ 66,62,11,211,66,62,6,211,66,24,0,62,
+ 14,211,66,62,33,211,66,62,1,211,66,62,
+ 64,211,66,62,3,211,66,62,209,211,66,62,
+ 100,71,219,66,230,1,32,6,5,32,247,195,
+ 248,132,219,67,71,58,44,152,184,194,248,132,
+ 62,100,71,219,66,230,1,32,6,5,32,247,
+ 195,248,132,219,67,62,100,71,219,66,230,1,
+ 32,6,5,32,247,195,248,132,219,67,254,133,
+ 32,7,62,0,50,74,152,24,17,254,173,32,
+ 7,62,1,50,74,152,24,6,254,141,194,248,
+ 132,71,209,225,58,49,152,254,132,32,10,62,
+ 50,205,2,134,205,144,135,24,27,254,140,32,
+ 15,62,110,205,2,134,62,141,184,32,5,205,
+ 144,135,24,8,62,10,205,2,134,205,8,134,
+ 62,1,50,106,137,205,158,139,237,56,52,246,
+ 6,237,57,52,175,183,251,201,62,20,135,237,
+ 57,20,175,237,57,21,237,56,16,246,2,237,
+ 57,16,237,56,20,95,237,56,21,123,254,10,
+ 48,244,237,56,16,230,17,237,57,16,209,225,
+ 205,144,135,62,1,50,106,137,205,158,139,237,
+ 56,52,246,6,237,57,52,175,183,251,201,209,
+ 225,243,219,72,230,1,40,13,62,10,211,66,
+ 0,0,219,66,230,192,202,226,132,237,56,52,
+ 246,6,237,57,52,62,1,55,251,201,205,203,
+ 135,62,1,50,106,137,205,158,139,237,56,52,
+ 246,6,237,57,52,183,251,201,209,225,62,1,
+ 50,106,137,205,158,139,237,56,52,246,6,237,
+ 57,52,62,2,55,251,201,209,225,243,219,72,
+ 230,1,202,213,132,62,10,211,66,0,0,219,
+ 66,230,192,194,213,132,229,62,1,50,106,137,
+ 42,40,152,205,65,143,225,17,3,0,205,111,
+ 136,62,6,211,66,58,44,152,211,66,237,56,
+ 52,246,6,237,57,52,183,251,201,209,197,237,
+ 56,52,230,248,237,57,52,219,72,230,1,32,
+ 15,193,225,237,56,52,246,6,237,57,52,62,
+ 1,55,251,201,14,23,58,37,152,254,0,40,
+ 14,14,2,254,1,32,5,62,140,119,24,3,
+ 62,132,119,43,43,197,205,203,135,193,62,1,
+ 211,66,62,64,211,66,62,3,211,66,62,193,
+ 211,66,62,100,203,39,71,219,66,230,1,32,
+ 6,5,32,247,195,229,133,33,238,151,219,67,
+ 71,58,44,152,184,194,229,133,119,62,100,71,
+ 219,66,230,1,32,6,5,32,247,195,229,133,
+ 219,67,35,119,13,32,234,193,225,62,1,50,
+ 106,137,205,158,139,237,56,52,246,6,237,57,
+ 52,175,183,251,201,33,234,151,35,35,62,255,
+ 119,193,225,62,1,50,106,137,205,158,139,237,
+ 56,52,246,6,237,57,52,175,251,201,243,61,
+ 32,253,251,201,62,3,211,66,62,192,211,66,
+ 58,49,152,254,140,32,19,197,229,213,17,181,
+ 129,33,185,129,1,2,0,237,176,209,225,193,
+ 24,27,229,213,33,187,129,58,49,152,230,15,
+ 87,30,2,237,92,25,17,181,129,126,18,19,
+ 35,126,18,209,225,58,34,152,246,8,211,68,
+ 58,49,152,254,165,40,14,254,164,40,10,62,
+ 10,211,66,62,224,211,66,24,25,58,74,152,
+ 254,0,40,10,62,10,211,66,62,160,211,66,
+ 24,8,62,10,211,66,62,128,211,66,62,11,
+ 211,66,62,6,211,66,205,147,143,62,5,211,
+ 66,62,224,211,66,62,5,211,66,62,96,211,
+ 66,62,5,61,32,253,62,5,211,66,62,224,
+ 211,66,62,14,61,32,253,62,5,211,66,62,
+ 233,211,66,62,128,211,66,58,181,129,61,32,
+ 253,62,1,211,66,62,192,211,66,1,254,19,
+ 237,56,46,187,32,6,13,32,247,195,226,134,
+ 62,192,211,66,0,0,219,66,203,119,40,250,
+ 219,66,203,87,40,250,243,237,56,16,230,17,
+ 237,57,16,237,56,20,251,62,5,211,66,62,
+ 224,211,66,58,182,129,61,32,253,229,33,181,
+ 129,58,183,129,203,63,119,35,58,184,129,119,
+ 225,62,10,211,66,62,224,211,66,62,11,211,
+ 66,62,118,211,66,62,47,211,68,62,5,211,
+ 66,62,233,211,66,58,181,129,61,32,253,62,
+ 5,211,66,62,224,211,66,58,182,129,61,32,
+ 253,62,5,211,66,62,96,211,66,201,229,213,
+ 58,50,152,230,15,87,30,2,237,92,33,187,
+ 129,25,17,181,129,126,18,35,19,126,18,209,
+ 225,58,71,152,246,8,211,68,58,50,152,254,
+ 165,40,14,254,164,40,10,62,10,211,66,62,
+ 224,211,66,24,8,62,10,211,66,62,128,211,
+ 66,62,11,211,66,62,6,211,66,195,248,135,
+ 62,3,211,66,62,192,211,66,197,229,213,17,
+ 181,129,33,183,129,1,2,0,237,176,209,225,
+ 193,62,47,211,68,62,10,211,66,62,224,211,
+ 66,62,11,211,66,62,118,211,66,62,1,211,
+ 66,62,0,211,66,205,147,143,195,16,136,62,
+ 3,211,66,62,192,211,66,197,229,213,17,181,
+ 129,33,183,129,1,2,0,237,176,209,225,193,
+ 62,47,211,68,62,10,211,66,62,224,211,66,
+ 62,11,211,66,62,118,211,66,205,147,143,62,
+ 5,211,66,62,224,211,66,62,5,211,66,62,
+ 96,211,66,62,5,61,32,253,62,5,211,66,
+ 62,224,211,66,62,14,61,32,253,62,5,211,
+ 66,62,233,211,66,62,128,211,66,58,181,129,
+ 61,32,253,62,1,211,66,62,192,211,66,1,
+ 254,19,237,56,46,187,32,6,13,32,247,195,
+ 88,136,62,192,211,66,0,0,219,66,203,119,
+ 40,250,219,66,203,87,40,250,62,5,211,66,
+ 62,224,211,66,58,182,129,61,32,253,62,5,
+ 211,66,62,96,211,66,201,197,14,67,6,0,
+ 62,3,211,66,62,192,211,66,62,48,211,66,
+ 0,0,219,66,230,1,40,4,219,67,24,240,
+ 62,5,211,66,62,233,211,66,62,128,211,66,
+ 58,181,129,61,32,253,237,163,29,62,192,211,
+ 66,219,66,230,4,40,250,237,163,29,32,245,
+ 219,66,230,4,40,250,62,255,71,219,66,230,
+ 4,40,3,5,32,247,219,66,230,4,40,250,
+ 62,5,211,66,62,224,211,66,58,182,129,61,
+ 32,253,62,5,211,66,62,96,211,66,58,71,
+ 152,254,1,202,18,137,62,16,211,66,62,56,
+ 211,66,62,14,211,66,62,33,211,66,62,1,
+ 211,66,62,248,211,66,237,56,48,246,153,230,
+ 207,237,57,48,62,3,211,66,62,221,211,66,
+ 193,201,58,71,152,211,68,62,10,211,66,62,
+ 128,211,66,62,11,211,66,62,6,211,66,62,
+ 6,211,66,58,44,152,211,66,62,16,211,66,
+ 62,56,211,66,62,48,211,66,0,0,62,14,
+ 211,66,62,33,211,66,62,1,211,66,62,248,
+ 211,66,237,56,48,246,145,246,8,230,207,237,
+ 57,48,62,3,211,66,62,221,211,66,193,201,
+ 44,3,1,0,70,69,1,245,197,213,229,175,
+ 50,72,152,237,56,16,230,46,237,57,16,237,
+ 56,12,62,1,211,66,0,0,219,66,95,230,
+ 160,32,3,195,20,139,123,230,96,194,72,139,
+ 62,48,211,66,62,1,211,66,62,64,211,66,
+ 237,91,40,152,205,207,143,25,43,55,237,82,
+ 218,70,139,34,42,152,98,107,58,44,152,190,
+ 194,210,138,35,35,62,130,190,194,200,137,62,
+ 1,50,48,152,62,175,190,202,82,139,62,132,
+ 190,32,44,50,50,152,62,47,50,71,152,229,
+ 175,50,106,137,42,40,152,205,65,143,225,54,
+ 133,43,70,58,44,152,119,43,112,17,3,0,
+ 62,10,205,2,134,205,111,136,195,158,138,62,
+ 140,190,32,19,50,50,152,58,233,149,230,4,
+ 202,222,138,62,1,50,71,152,195,219,137,126,
+ 254,160,250,185,138,254,166,242,185,138,50,50,
+ 152,43,126,35,229,213,33,234,149,95,22,0,
+ 25,126,254,132,40,18,254,140,40,14,58,50,
+ 152,230,15,87,126,31,21,242,65,138,56,2,
+ 175,119,58,50,152,230,15,87,58,233,149,230,
+ 62,31,21,242,85,138,218,98,138,209,225,195,
+ 20,139,58,50,152,33,100,137,230,15,95,22,
+ 0,25,126,50,71,152,209,225,58,50,152,254,
+ 164,250,135,138,58,73,152,254,0,40,4,54,
+ 173,24,2,54,133,43,70,58,44,152,119,43,
+ 112,17,3,0,205,70,135,175,50,106,137,205,
+ 208,139,58,199,129,237,57,12,58,200,129,237,
+ 57,13,237,56,16,246,17,237,57,16,225,209,
+ 193,241,251,237,77,62,129,190,194,227,138,54,
+ 130,43,70,58,44,152,119,43,112,17,3,0,
+ 205,144,135,195,20,139,35,35,126,254,132,194,
+ 227,138,175,50,106,137,205,158,139,24,42,58,
+ 201,154,254,1,40,7,62,1,50,106,137,24,
+ 237,58,106,137,254,1,202,222,138,62,128,166,
+ 194,222,138,221,229,221,33,67,152,205,127,142,
+ 205,109,144,221,225,225,209,193,241,251,237,77,
+ 58,106,137,254,1,202,44,139,58,50,152,254,
+ 164,250,44,139,58,73,152,238,1,50,73,152,
+ 221,229,221,33,51,152,205,127,142,221,225,62,
+ 1,50,106,137,205,158,139,195,13,139,24,208,
+ 24,206,24,204,230,64,40,3,195,20,139,195,
+ 20,139,43,126,33,8,152,119,35,58,44,152,
+ 119,43,237,91,35,152,205,203,135,205,158,139,
+ 195,13,139,175,50,78,152,62,3,211,66,62,
+ 192,211,66,201,197,33,4,0,57,126,35,102,
+ 111,62,1,50,106,137,219,72,205,141,139,193,
+ 201,62,1,50,78,152,34,40,152,54,0,35,
+ 35,54,0,195,163,139,58,78,152,183,200,229,
+ 33,181,129,58,183,129,119,35,58,184,129,119,
+ 225,62,47,211,68,62,14,211,66,62,193,211,
+ 66,62,10,211,66,62,224,211,66,62,11,211,
+ 66,62,118,211,66,195,3,140,58,78,152,183,
+ 200,58,71,152,211,68,254,69,40,4,254,70,
+ 32,17,58,73,152,254,0,40,10,62,10,211,
+ 66,62,160,211,66,24,8,62,10,211,66,62,
+ 128,211,66,62,11,211,66,62,6,211,66,62,
+ 6,211,66,58,44,152,211,66,62,16,211,66,
+ 62,56,211,66,62,48,211,66,0,0,219,66,
+ 230,1,40,4,219,67,24,240,62,14,211,66,
+ 62,33,211,66,42,40,152,205,65,143,62,1,
+ 211,66,62,248,211,66,237,56,48,246,145,246,
+ 8,230,207,237,57,48,62,3,211,66,62,221,
+ 211,66,201,62,16,211,66,62,56,211,66,62,
+ 48,211,66,0,0,219,66,230,1,40,4,219,
+ 67,24,240,62,14,211,66,62,33,211,66,62,
+ 1,211,66,62,248,211,66,237,56,48,246,153,
+ 230,207,237,57,48,62,3,211,66,62,221,211,
+ 66,201,229,213,33,234,149,95,22,0,25,126,
+ 254,132,40,4,254,140,32,2,175,119,123,209,
+ 225,201,6,8,14,0,31,48,1,12,16,250,
+ 121,201,33,4,0,57,94,35,86,33,2,0,
+ 57,126,35,102,111,221,229,34,89,152,237,83,
+ 91,152,221,33,63,152,205,127,142,58,81,152,
+ 50,82,152,58,80,152,135,50,80,152,205,162,
+ 140,254,3,56,16,58,81,152,135,60,230,15,
+ 50,81,152,175,50,80,152,24,23,58,79,152,
+ 205,162,140,254,3,48,13,58,81,152,203,63,
+ 50,81,152,62,255,50,79,152,58,81,152,50,
+ 82,152,58,79,152,135,50,79,152,62,32,50,
+ 83,152,50,84,152,237,56,16,230,17,237,57,
+ 16,219,72,62,192,50,93,152,62,93,50,94,
+ 152,58,93,152,61,50,93,152,32,9,58,94,
+ 152,61,50,94,152,40,44,62,170,237,57,20,
+ 175,237,57,21,237,56,16,246,2,237,57,16,
+ 219,72,230,1,202,29,141,237,56,20,71,237,
+ 56,21,120,254,10,48,237,237,56,16,230,17,
+ 237,57,16,243,62,14,211,66,62,65,211,66,
+ 251,58,39,152,23,23,60,50,39,152,71,58,
+ 82,152,160,230,15,40,22,71,14,10,219,66,
+ 230,16,202,186,141,219,72,230,1,202,186,141,
+ 13,32,239,16,235,42,89,152,237,91,91,152,
+ 205,47,131,48,7,61,202,186,141,195,227,141,
+ 221,225,33,0,0,201,221,33,55,152,205,127,
+ 142,58,84,152,61,50,84,152,40,19,58,82,
+ 152,246,1,50,82,152,58,79,152,246,1,50,
+ 79,152,195,29,141,221,225,33,1,0,201,221,
+ 33,59,152,205,127,142,58,80,152,246,1,50,
+ 80,152,58,82,152,135,246,1,50,82,152,58,
+ 83,152,61,50,83,152,194,29,141,221,225,33,
+ 2,0,201,221,229,33,0,0,57,17,4,0,
+ 25,126,50,44,152,230,128,50,85,152,58,85,
+ 152,183,40,6,221,33,88,2,24,4,221,33,
+ 150,0,58,44,152,183,40,53,60,40,50,60,
+ 40,47,61,61,33,86,152,119,35,119,35,54,
+ 129,175,50,48,152,221,43,221,229,225,124,181,
+ 40,42,33,86,152,17,3,0,205,189,140,17,
+ 232,3,27,123,178,32,251,58,48,152,183,40,
+ 224,58,44,152,71,62,7,128,230,127,71,58,
+ 85,152,176,50,44,152,24,162,221,225,201,183,
+ 221,52,0,192,221,52,1,192,221,52,2,192,
+ 221,52,3,192,55,201,245,62,1,211,100,241,
+ 201,245,62,1,211,96,241,201,33,2,0,57,
+ 126,35,102,111,237,56,48,230,175,237,57,48,
+ 62,48,237,57,49,125,237,57,32,124,237,57,
+ 33,62,0,237,57,34,62,88,237,57,35,62,
+ 0,237,57,36,237,57,37,33,128,2,125,237,
+ 57,38,124,237,57,39,237,56,48,246,97,230,
+ 207,237,57,48,62,0,237,57,0,62,0,211,
+ 96,211,100,201,33,2,0,57,126,35,102,111,
+ 237,56,48,230,175,237,57,48,62,12,237,57,
+ 49,62,76,237,57,32,62,0,237,57,33,237,
+ 57,34,125,237,57,35,124,237,57,36,62,0,
+ 237,57,37,33,128,2,125,237,57,38,124,237,
+ 57,39,237,56,48,246,97,230,207,237,57,48,
+ 62,1,211,96,201,33,2,0,57,126,35,102,
+ 111,229,237,56,48,230,87,237,57,48,125,237,
+ 57,40,124,237,57,41,62,0,237,57,42,62,
+ 67,237,57,43,62,0,237,57,44,58,106,137,
+ 254,1,32,5,33,6,0,24,3,33,128,2,
+ 125,237,57,46,124,237,57,47,237,56,50,230,
+ 252,246,2,237,57,50,225,201,33,4,0,57,
+ 94,35,86,33,2,0,57,126,35,102,111,237,
+ 56,48,230,87,237,57,48,125,237,57,40,124,
+ 237,57,41,62,0,237,57,42,62,67,237,57,
+ 43,62,0,237,57,44,123,237,57,46,122,237,
+ 57,47,237,56,50,230,244,246,0,237,57,50,
+ 237,56,48,246,145,230,207,237,57,48,201,213,
+ 237,56,46,95,237,56,47,87,237,56,46,111,
+ 237,56,47,103,183,237,82,32,235,33,128,2,
+ 183,237,82,209,201,213,237,56,38,95,237,56,
+ 39,87,237,56,38,111,237,56,39,103,183,237,
+ 82,32,235,33,128,2,183,237,82,209,201,245,
+ 197,1,52,0,237,120,230,253,237,121,193,241,
+ 201,245,197,1,52,0,237,120,246,2,237,121,
+ 193,241,201,33,2,0,57,126,35,102,111,126,
+ 35,110,103,201,33,0,0,34,102,152,34,96,
+ 152,34,98,152,33,202,154,34,104,152,237,91,
+ 104,152,42,226,149,183,237,82,17,0,255,25,
+ 34,100,152,203,124,40,6,33,0,125,34,100,
+ 152,42,104,152,35,35,35,229,205,120,139,193,
+ 201,205,186,149,229,42,40,152,35,35,35,229,
+ 205,39,144,193,124,230,3,103,221,117,254,221,
+ 116,255,237,91,42,152,35,35,35,183,237,82,
+ 32,12,17,5,0,42,42,152,205,171,149,242,
+ 169,144,42,40,152,229,205,120,139,193,195,198,
+ 149,237,91,42,152,42,98,152,25,34,98,152,
+ 19,19,19,42,102,152,25,34,102,152,237,91,
+ 100,152,33,158,253,25,237,91,102,152,205,171,
+ 149,242,214,144,33,0,0,34,102,152,62,1,
+ 50,95,152,205,225,144,195,198,149,58,95,152,
+ 183,200,237,91,96,152,42,102,152,205,171,149,
+ 242,5,145,237,91,102,152,33,98,2,25,237,
+ 91,96,152,205,171,149,250,37,145,237,91,96,
+ 152,42,102,152,183,237,82,32,7,42,98,152,
+ 125,180,40,13,237,91,102,152,42,96,152,205,
+ 171,149,242,58,145,237,91,104,152,42,102,152,
+ 25,35,35,35,229,205,120,139,193,175,50,95,
+ 152,201,195,107,139,205,206,149,250,255,243,205,
+ 225,144,251,58,230,149,183,194,198,149,17,1,
+ 0,42,98,152,205,171,149,250,198,149,62,1,
+ 50,230,149,237,91,96,152,42,104,152,25,221,
+ 117,252,221,116,253,237,91,104,152,42,96,152,
+ 25,35,35,35,221,117,254,221,116,255,35,35,
+ 35,229,205,39,144,124,230,3,103,35,35,35,
+ 221,117,250,221,116,251,235,221,110,252,221,102,
+ 253,115,35,114,35,54,4,62,1,211,100,211,
+ 84,195,198,149,33,0,0,34,102,152,34,96,
+ 152,34,98,152,33,202,154,34,104,152,237,91,
+ 104,152,42,226,149,183,237,82,17,0,255,25,
+ 34,100,152,33,109,152,54,0,33,107,152,229,
+ 205,240,142,193,62,47,50,34,152,62,132,50,
+ 49,152,205,241,145,205,61,145,58,39,152,60,
+ 50,39,152,24,241,205,206,149,251,255,33,109,
+ 152,126,183,202,198,149,110,221,117,251,33,109,
+ 152,54,0,221,126,251,254,1,40,28,254,3,
+ 40,101,254,4,202,190,147,254,5,202,147,147,
+ 254,8,40,87,33,107,152,229,205,240,142,195,
+ 198,149,58,201,154,183,32,21,33,111,152,126,
+ 50,229,149,205,52,144,33,110,152,110,38,0,
+ 229,205,11,142,193,237,91,96,152,42,104,152,
+ 25,221,117,254,221,116,255,35,35,54,2,17,
+ 2,0,43,43,115,35,114,58,44,152,35,35,
+ 119,58,228,149,35,119,62,1,211,100,211,84,
+ 62,1,50,201,154,24,169,205,153,142,58,231,
+ 149,183,40,250,175,50,231,149,33,110,152,126,
+ 254,255,40,91,58,233,149,230,63,183,40,83,
+ 94,22,0,33,234,149,25,126,183,40,13,33,
+ 110,152,94,33,234,150,25,126,254,3,32,36,
+ 205,81,148,125,180,33,110,152,94,22,0,40,
+ 17,33,234,149,25,54,0,33,107,152,229,205,
+ 240,142,193,195,198,149,33,234,150,25,54,0,
+ 33,110,152,94,22,0,33,234,149,25,126,50,
+ 49,152,254,132,32,37,62,47,50,34,152,42,
+ 107,152,229,33,110,152,229,205,174,140,193,193,
+ 125,180,33,110,152,94,22,0,33,234,150,202,
+ 117,147,25,52,195,120,147,58,49,152,254,140,
+ 32,7,62,1,50,34,152,24,210,62,32,50,
+ 106,152,24,19,58,49,152,95,58,106,152,163,
+ 183,58,106,152,32,11,203,63,50,106,152,58,
+ 106,152,183,32,231,254,2,40,51,254,4,40,
+ 38,254,8,40,26,254,16,40,13,254,32,32,
+ 158,62,165,50,49,152,62,69,24,190,62,164,
+ 50,49,152,62,70,24,181,62,163,50,49,152,
+ 175,24,173,62,162,50,49,152,62,1,24,164,
+ 62,161,50,49,152,62,3,24,155,25,54,0,
+ 221,126,251,254,8,40,7,58,230,149,183,202,
+ 32,146,33,107,152,229,205,240,142,193,211,84,
+ 195,198,149,237,91,96,152,42,104,152,25,221,
+ 117,254,221,116,255,35,35,54,6,17,2,0,
+ 43,43,115,35,114,58,228,149,35,35,119,58,
+ 233,149,35,119,205,146,142,195,32,146,237,91,
+ 96,152,42,104,152,25,229,205,160,142,193,58,
+ 231,149,183,40,250,175,50,231,149,243,237,91,
+ 96,152,42,104,152,25,221,117,254,221,116,255,
+ 78,35,70,221,113,252,221,112,253,89,80,42,
+ 98,152,183,237,82,34,98,152,203,124,40,19,
+ 33,0,0,34,98,152,34,102,152,34,96,152,
+ 62,1,50,95,152,24,40,221,94,252,221,86,
+ 253,19,19,19,42,96,152,25,34,96,152,237,
+ 91,100,152,33,158,253,25,237,91,96,152,205,
+ 171,149,242,55,148,33,0,0,34,96,152,175,
+ 50,230,149,251,195,32,146,245,62,1,50,231,
+ 149,62,16,237,57,0,211,80,241,251,237,77,
+ 201,205,186,149,229,229,33,0,0,34,37,152,
+ 33,110,152,126,50,234,151,58,44,152,33,235,
+ 151,119,221,54,253,0,221,54,254,0,195,230,
+ 148,33,236,151,54,175,33,3,0,229,33,234,
+ 151,229,205,174,140,193,193,33,236,151,126,254,
+ 255,40,74,33,245,151,110,221,117,255,33,249,
+ 151,126,221,166,255,221,119,255,33,253,151,126,
+ 221,166,255,221,119,255,58,232,149,95,221,126,
+ 255,163,221,119,255,183,40,15,230,191,33,110,
+ 152,94,22,0,33,234,149,25,119,24,12,33,
+ 110,152,94,22,0,33,234,149,25,54,132,33,
+ 0,0,195,198,149,221,110,253,221,102,254,35,
+ 221,117,253,221,116,254,17,32,0,221,110,253,
+ 221,102,254,205,171,149,250,117,148,58,233,149,
+ 203,87,40,84,33,1,0,34,37,152,221,54,
+ 253,0,221,54,254,0,24,53,33,236,151,54,
+ 175,33,3,0,229,33,234,151,229,205,174,140,
+ 193,193,33,236,151,126,254,255,40,14,33,110,
+ 152,94,22,0,33,234,149,25,54,140,24,159,
+ 221,110,253,221,102,254,35,221,117,253,221,116,
+ 254,17,32,0,221,110,253,221,102,254,205,171,
+ 149,250,12,149,33,2,0,34,37,152,221,54,
+ 253,0,221,54,254,0,24,54,33,236,151,54,
+ 175,33,3,0,229,33,234,151,229,205,174,140,
+ 193,193,33,236,151,126,254,255,40,15,33,110,
+ 152,94,22,0,33,234,149,25,54,132,195,211,
+ 148,221,110,253,221,102,254,35,221,117,253,221,
+ 116,254,17,32,0,221,110,253,221,102,254,205,
+ 171,149,250,96,149,33,1,0,195,198,149,124,
+ 170,250,179,149,237,82,201,124,230,128,237,82,
+ 60,201,225,253,229,221,229,221,33,0,0,221,
+ 57,233,221,249,221,225,253,225,201,233,225,253,
+ 229,221,229,221,33,0,0,221,57,94,35,86,
+ 35,235,57,249,235,233,0,0,0,0,0,0,
+ 62,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,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,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,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,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,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,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,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,0,0,0,
+ 175,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,133,1,0,0,0,63,
+ 255,255,255,255,0,0,0,63,0,0,0,0,
+ 0,0,0,0,0,0,0,24,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
+ } ;
+
+#endif
diff --git a/drivers/net/cops_ltdrv.h b/drivers/net/cops_ltdrv.h
new file mode 100644
index 000000000..33f3d9a06
--- /dev/null
+++ b/drivers/net/cops_ltdrv.h
@@ -0,0 +1,242 @@
+/*
+ * The firmware this driver downloads into the Localtalk card is a
+ * seperate program and is not GPL'd source code, even though the Linux
+ * side driver and the routine that loads this data into the card are.
+ *
+ * It is taken from the COPS SDK and is under the following license
+ *
+ * This material is licensed to you strictly for use in conjunction with
+ * the use of COPS LocalTalk adapters.
+ * There is no charge for this SDK. And no waranty express or implied
+ * about its fitness for any purpose. However, we will cheerefully
+ * refund every penny you paid for this SDK...
+ * Regards,
+ *
+ * Thomas F. Divine
+ * Chief Scientist
+ */
+
+
+/* cops_ltdrv.h: LocalTalk driver firmware dump for Linux.
+ *
+ * Authors:
+ * - Jay Schulist <Jay.Schulist@spacs.k12.wi.us>
+ */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_COPS_TANGENT
+
+unsigned char ltdrv_code[] = {
+ 58,3,0,50,148,10,33,143,15,62,85,119,
+ 190,32,9,62,170,119,190,32,3,35,24,241,
+ 34,146,10,249,17,150,10,33,143,15,183,237,
+ 82,77,68,11,107,98,19,54,0,237,176,62,
+ 16,237,57,51,62,0,237,57,50,237,57,54,
+ 62,12,237,57,49,62,195,33,39,2,50,56,
+ 0,34,57,0,237,86,205,30,2,251,205,60,
+ 10,24,169,67,111,112,121,114,105,103,104,116,
+ 32,40,99,41,32,49,57,56,56,45,49,57,
+ 57,50,44,32,80,114,105,110,116,105,110,103,
+ 32,67,111,109,109,117,110,105,99,97,116,105,
+ 111,110,115,32,65,115,115,111,99,105,97,116,
+ 101,115,44,32,73,110,99,46,65,108,108,32,
+ 114,105,103,104,116,115,32,114,101,115,101,114,
+ 118,101,100,46,32,32,4,4,22,40,255,60,
+ 4,96,10,224,6,0,7,126,2,64,11,246,
+ 12,6,13,0,14,193,15,0,5,96,3,192,
+ 1,0,9,8,62,3,211,82,62,192,211,82,
+ 201,62,3,211,82,62,213,211,82,201,62,5,
+ 211,82,62,224,211,82,201,62,5,211,82,62,
+ 224,211,82,201,62,5,211,82,62,96,211,82,
+ 201,6,28,33,180,1,14,82,237,163,194,4,
+ 2,33,39,2,34,64,0,58,3,0,230,1,
+ 192,62,11,237,121,62,118,237,121,201,33,182,
+ 10,54,132,205,253,1,201,245,197,213,229,42,
+ 150,10,14,83,17,98,2,67,20,237,162,58,
+ 179,1,95,219,82,230,1,32,6,29,32,247,
+ 195,17,3,62,1,211,82,219,82,95,230,160,
+ 32,10,237,162,32,225,21,32,222,195,15,3,
+ 237,162,123,230,96,194,21,3,62,48,211,82,
+ 62,1,211,82,175,211,82,237,91,150,10,43,
+ 55,237,82,218,19,3,34,152,10,98,107,58,
+ 154,10,190,32,81,62,1,50,158,10,35,35,
+ 62,132,190,32,44,54,133,43,70,58,154,10,
+ 119,43,112,17,3,0,205,137,3,62,16,211,
+ 82,62,56,211,82,205,217,1,42,150,10,14,
+ 83,17,98,2,67,20,58,178,1,95,195,59,
+ 2,62,129,190,194,227,2,54,130,43,70,58,
+ 154,10,119,43,112,17,3,0,205,137,3,195,
+ 254,2,35,35,126,254,132,194,227,2,205,61,
+ 3,24,20,62,128,166,194,222,2,221,229,221,
+ 33,175,10,205,93,6,205,144,7,221,225,225,
+ 209,193,241,251,237,77,221,229,221,33,159,10,
+ 205,93,6,221,225,205,61,3,195,247,2,24,
+ 237,24,235,24,233,230,64,40,2,24,227,24,
+ 225,175,50,179,10,205,208,1,201,197,33,4,
+ 0,57,126,35,102,111,205,51,3,193,201,62,
+ 1,50,179,10,34,150,10,54,0,58,179,10,
+ 183,200,62,14,211,82,62,193,211,82,62,10,
+ 211,82,62,224,211,82,62,6,211,82,58,154,
+ 10,211,82,62,16,211,82,62,56,211,82,62,
+ 48,211,82,219,82,230,1,40,4,219,83,24,
+ 242,62,14,211,82,62,33,211,82,62,1,211,
+ 82,62,9,211,82,62,32,211,82,205,217,1,
+ 201,14,83,205,208,1,24,23,14,83,205,208,
+ 1,205,226,1,58,174,1,61,32,253,205,244,
+ 1,58,174,1,61,32,253,205,226,1,58,175,
+ 1,61,32,253,62,5,211,82,62,233,211,82,
+ 62,128,211,82,58,176,1,61,32,253,237,163,
+ 27,62,192,211,82,219,82,230,4,40,250,237,
+ 163,27,122,179,32,243,219,82,230,4,40,250,
+ 58,178,1,71,219,82,230,4,40,3,5,32,
+ 247,219,82,230,4,40,250,205,235,1,58,177,
+ 1,61,32,253,205,244,1,201,229,213,35,35,
+ 126,230,128,194,145,4,43,58,154,10,119,43,
+ 70,33,181,10,119,43,112,17,3,0,243,62,
+ 10,211,82,219,82,230,128,202,41,4,209,225,
+ 62,1,55,251,201,205,144,3,58,180,10,254,
+ 255,202,127,4,205,217,1,58,178,1,71,219,
+ 82,230,1,32,6,5,32,247,195,173,4,219,
+ 83,71,58,154,10,184,194,173,4,58,178,1,
+ 71,219,82,230,1,32,6,5,32,247,195,173,
+ 4,219,83,58,178,1,71,219,82,230,1,32,
+ 6,5,32,247,195,173,4,219,83,254,133,194,
+ 173,4,58,179,1,24,4,58,179,1,135,61,
+ 32,253,209,225,205,137,3,205,61,3,183,251,
+ 201,209,225,243,62,10,211,82,219,82,230,128,
+ 202,164,4,62,1,55,251,201,205,144,3,205,
+ 61,3,183,251,201,209,225,62,2,55,251,201,
+ 243,62,14,211,82,62,33,211,82,251,201,33,
+ 4,0,57,94,35,86,33,2,0,57,126,35,
+ 102,111,221,229,34,193,10,237,83,195,10,221,
+ 33,171,10,205,93,6,58,185,10,50,186,10,
+ 58,184,10,135,50,184,10,205,112,6,254,3,
+ 56,16,58,185,10,135,60,230,15,50,185,10,
+ 175,50,184,10,24,23,58,183,10,205,112,6,
+ 254,3,48,13,58,185,10,203,63,50,185,10,
+ 62,255,50,183,10,58,185,10,50,186,10,58,
+ 183,10,135,50,183,10,62,32,50,187,10,50,
+ 188,10,6,255,219,82,230,16,32,3,5,32,
+ 247,205,180,4,6,40,219,82,230,16,40,3,
+ 5,32,247,62,10,211,82,219,82,230,128,194,
+ 46,5,219,82,230,16,40,214,237,95,71,58,
+ 186,10,160,230,15,40,32,71,14,10,62,10,
+ 211,82,219,82,230,128,202,119,5,205,180,4,
+ 195,156,5,219,82,230,16,202,156,5,13,32,
+ 229,16,225,42,193,10,237,91,195,10,205,252,
+ 3,48,7,61,202,156,5,195,197,5,221,225,
+ 33,0,0,201,221,33,163,10,205,93,6,58,
+ 188,10,61,50,188,10,40,19,58,186,10,246,
+ 1,50,186,10,58,183,10,246,1,50,183,10,
+ 195,46,5,221,225,33,1,0,201,221,33,167,
+ 10,205,93,6,58,184,10,246,1,50,184,10,
+ 58,186,10,135,246,1,50,186,10,58,187,10,
+ 61,50,187,10,194,46,5,221,225,33,2,0,
+ 201,221,229,33,0,0,57,17,4,0,25,126,
+ 50,154,10,230,128,50,189,10,58,189,10,183,
+ 40,6,221,33,88,2,24,4,221,33,150,0,
+ 58,154,10,183,40,49,60,40,46,61,33,190,
+ 10,119,35,119,35,54,129,175,50,158,10,221,
+ 43,221,229,225,124,181,40,42,33,190,10,17,
+ 3,0,205,206,4,17,232,3,27,123,178,32,
+ 251,58,158,10,183,40,224,58,154,10,71,62,
+ 7,128,230,127,71,58,189,10,176,50,154,10,
+ 24,166,221,225,201,183,221,52,0,192,221,52,
+ 1,192,221,52,2,192,221,52,3,192,55,201,
+ 6,8,14,0,31,48,1,12,16,250,121,201,
+ 33,2,0,57,94,35,86,35,78,35,70,35,
+ 126,35,102,105,79,120,68,103,237,176,201,33,
+ 2,0,57,126,35,102,111,62,17,237,57,48,
+ 125,237,57,40,124,237,57,41,62,0,237,57,
+ 42,62,64,237,57,43,62,0,237,57,44,33,
+ 128,2,125,237,57,46,124,237,57,47,62,145,
+ 237,57,48,211,68,58,149,10,211,66,201,33,
+ 2,0,57,126,35,102,111,62,33,237,57,48,
+ 62,64,237,57,32,62,0,237,57,33,237,57,
+ 34,125,237,57,35,124,237,57,36,62,0,237,
+ 57,37,33,128,2,125,237,57,38,124,237,57,
+ 39,62,97,237,57,48,211,67,58,149,10,211,
+ 66,201,237,56,46,95,237,56,47,87,237,56,
+ 46,111,237,56,47,103,183,237,82,32,235,33,
+ 128,2,183,237,82,201,237,56,38,95,237,56,
+ 39,87,237,56,38,111,237,56,39,103,183,237,
+ 82,32,235,33,128,2,183,237,82,201,205,106,
+ 10,221,110,6,221,102,7,126,35,110,103,195,
+ 118,10,205,106,10,33,0,0,34,205,10,34,
+ 198,10,34,200,10,33,143,15,34,207,10,237,
+ 91,207,10,42,146,10,183,237,82,17,0,255,
+ 25,34,203,10,203,124,40,6,33,0,125,34,
+ 203,10,42,207,10,229,205,37,3,195,118,10,
+ 205,106,10,229,42,150,10,35,35,35,229,205,
+ 70,7,193,124,230,3,103,221,117,254,221,116,
+ 255,237,91,152,10,35,35,35,183,237,82,32,
+ 12,17,5,0,42,152,10,205,91,10,242,203,
+ 7,42,150,10,229,205,37,3,195,118,10,237,
+ 91,152,10,42,200,10,25,34,200,10,42,205,
+ 10,25,34,205,10,237,91,203,10,33,158,253,
+ 25,237,91,205,10,205,91,10,242,245,7,33,
+ 0,0,34,205,10,62,1,50,197,10,205,5,
+ 8,33,0,0,57,249,195,118,10,205,106,10,
+ 58,197,10,183,202,118,10,237,91,198,10,42,
+ 205,10,205,91,10,242,46,8,237,91,205,10,
+ 33,98,2,25,237,91,198,10,205,91,10,250,
+ 78,8,237,91,198,10,42,205,10,183,237,82,
+ 32,7,42,200,10,125,180,40,13,237,91,205,
+ 10,42,198,10,205,91,10,242,97,8,237,91,
+ 207,10,42,205,10,25,229,205,37,3,175,50,
+ 197,10,195,118,10,205,29,3,33,0,0,57,
+ 249,195,118,10,205,106,10,58,202,10,183,40,
+ 22,205,14,7,237,91,209,10,19,19,19,205,
+ 91,10,242,139,8,33,1,0,195,118,10,33,
+ 0,0,195,118,10,205,126,10,252,255,205,108,
+ 8,125,180,194,118,10,237,91,200,10,33,0,
+ 0,205,91,10,242,118,10,237,91,207,10,42,
+ 198,10,25,221,117,254,221,116,255,35,35,35,
+ 229,205,70,7,193,124,230,3,103,35,35,35,
+ 221,117,252,221,116,253,229,221,110,254,221,102,
+ 255,229,33,212,10,229,205,124,6,193,193,221,
+ 110,252,221,102,253,34,209,10,33,211,10,54,
+ 4,33,209,10,227,205,147,6,193,62,1,50,
+ 202,10,243,221,94,252,221,86,253,42,200,10,
+ 183,237,82,34,200,10,203,124,40,17,33,0,
+ 0,34,200,10,34,205,10,34,198,10,50,197,
+ 10,24,37,221,94,252,221,86,253,42,198,10,
+ 25,34,198,10,237,91,203,10,33,158,253,25,
+ 237,91,198,10,205,91,10,242,68,9,33,0,
+ 0,34,198,10,205,5,8,33,0,0,57,249,
+ 251,195,118,10,205,106,10,33,49,13,126,183,
+ 40,16,205,42,7,237,91,47,13,19,19,19,
+ 205,91,10,242,117,9,58,142,15,198,1,50,
+ 142,15,195,118,10,33,49,13,126,254,1,40,
+ 25,254,3,202,7,10,254,5,202,21,10,33,
+ 49,13,54,0,33,47,13,229,205,207,6,195,
+ 118,10,58,141,15,183,32,72,33,51,13,126,
+ 50,149,10,205,86,7,33,50,13,126,230,127,
+ 183,32,40,58,142,15,230,127,50,142,15,183,
+ 32,5,198,1,50,142,15,33,50,13,126,111,
+ 23,159,103,203,125,58,142,15,40,5,198,128,
+ 50,142,15,33,50,13,119,33,50,13,126,111,
+ 23,159,103,229,205,237,5,193,33,211,10,54,
+ 2,33,2,0,34,209,10,58,154,10,33,212,
+ 10,119,58,148,10,33,213,10,119,33,209,10,
+ 229,205,147,6,193,24,128,42,47,13,229,33,
+ 50,13,229,205,191,4,193,24,239,33,211,10,
+ 54,6,33,3,0,34,209,10,58,154,10,33,
+ 212,10,119,58,148,10,33,213,10,119,33,214,
+ 10,54,5,33,209,10,229,205,147,6,24,200,
+ 205,106,10,33,49,13,54,0,33,47,13,229,
+ 205,207,6,33,209,10,227,205,147,6,193,205,
+ 80,9,205,145,8,24,248,124,170,250,99,10,
+ 237,82,201,124,230,128,237,82,60,201,225,253,
+ 229,221,229,221,33,0,0,221,57,233,221,249,
+ 221,225,253,225,201,233,225,253,229,221,229,221,
+ 33,0,0,221,57,94,35,86,35,235,57,249,
+ 235,233,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
+ } ;
+
+#endif
diff --git a/drivers/net/ibmtr.c b/drivers/net/ibmtr.c
index e0d71b202..d803e953e 100644
--- a/drivers/net/ibmtr.c
+++ b/drivers/net/ibmtr.c
@@ -55,7 +55,10 @@
* + completed multiple adapter support. (November 20 1996)
* + implemented csum_partial_copy in tr_rx and increased receive
* buffer size and count. Minor fixes. (March 15, 1997)
-*/
+ *
+ * Changes by Christopher Turcksin <wabbit@rtfc.demon.co.uk>
+ * + Now compiles ok as a module again.
+ */
#ifdef PCMCIA
#define MODULE
@@ -163,9 +166,9 @@ unsigned char ibmtr_debug_trace=0;
int ibmtr_probe(struct device *dev);
static int ibmtr_probe1(struct device *dev, int ioaddr);
-unsigned char get_sram_size(struct tok_info *adapt_info);
+static unsigned char get_sram_size(struct tok_info *adapt_info);
static int tok_init_card(struct device *dev);
-int trdev_init(struct device *dev);
+static int trdev_init(struct device *dev);
void tok_interrupt(int irq, void *dev_id, struct pt_regs *regs);
static void initial_tok_int(struct device *dev);
static void open_sap(unsigned char type,struct device *dev);
@@ -1583,7 +1586,7 @@ int init_module(void)
dev_ibmtr[i]->init = &ibmtr_probe;
if (register_trdev(dev_ibmtr[i]) != 0) {
- kfree_s(dev_ibmtr[i], sizeof(struct dev));
+ kfree_s(dev_ibmtr[i], sizeof(struct device));
dev_ibmtr[i] = NULL;
if (i == 0) {
printk("ibmtr: register_trdev() returned non-zero.\n");
diff --git a/drivers/net/lapbether.c b/drivers/net/lapbether.c
index e282847a9..5f8cb5cca 100644
--- a/drivers/net/lapbether.c
+++ b/drivers/net/lapbether.c
@@ -407,7 +407,7 @@ static int lapbeth_close(struct device *dev)
return 0;
}
-__initfunc(static int lapbeth_dev_init(struct device *dev))
+static int lapbeth_dev_init(struct device *dev)
{
return 0;
}
diff --git a/drivers/net/sdla_fr.c b/drivers/net/sdla_fr.c
index d2dc0eb90..95f1ae739 100644
--- a/drivers/net/sdla_fr.c
+++ b/drivers/net/sdla_fr.c
@@ -10,6 +10,49 @@
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
* ============================================================================
+*
+* Jun 29, 1997 Alan Cox o Hacked up vendor source 1.0.3 to remove
+* C++ style comments, and a massive security
+* hole (the UDP management junk).
+*
+* May 29, 1997 Jaspreet Singh o Fixed major Flow Control Problem
+* With multiple boards a problem was seen where
+* the second board always stopped transmitting
+* packet after running for a while. The code
+* got into a stage where the interrupts were
+* disabled and dev->tbusy was set to 1.
+* This caused the If_send() routine to get into
+* the if clause for set_bit(0,dev->tbusy)
+* forever.
+* The code got into this stage due to an
+* interrupt occuring within the if clause for
+* set_bit(0,dev->tbusy). Since an interrupt
+* disables furhter transmit interrupt and
+* makes dev->tbusy = 0, this effect was undone
+* by making dev->tbusy = 1 in the if clause.
+* The Fix checks to see if Transmit interrupts
+* are disabled then do not make dev->tbusy = 1
+* Introduced a global variable: int_occur and
+* added tx_int_enabled in the wan_device
+* structure.
+* May 21, 1997 Jaspreet Singh o Fixed UDP Management for multiple
+* boards.
+*
+* Apr 25, 1997 Farhan Thawar o added UDP Management stuff
+* o fixed bug in if_send() and tx_intr() to
+* sleep and wakeup all devices
+* Mar 11, 1997 Farhan Thawar Version 3.1.1
+* o fixed (+1) bug in fr508_rx_intr()
+* o changed if_send() to return 0 if
+* wandev.critical() is true
+* o free socket buffer in if_send() if
+* returning 0
+* o added tx_intr() routine
+* Jan 30, 1997 Gene Kozin Version 3.1.0
+* o implemented exec() entry point
+* o fixed a bug causing driver configured as
+* a FR switch to be stuck in WAN_DISCONNECTED
+* mode
* Jan 02, 1997 Gene Kozin Initial version.
*****************************************************************************/
@@ -22,12 +65,13 @@
#include <linux/errno.h> /* return codes */
#include <linux/string.h> /* inline memset(), etc. */
#include <linux/malloc.h> /* kmalloc(), kfree() */
-#include <linux/router.h> /* WAN router definitions */
+#include <linux/wanrouter.h> /* WAN router definitions */
#include <linux/wanpipe.h> /* WANPIPE common user API definitions */
#include <linux/if_arp.h> /* ARPHRD_* defines */
#include <linux/init.h> /* __initfunc et al. */
#include <asm/byteorder.h> /* htons(), etc. */
#include <asm/io.h> /* for inb(), outb(), etc. */
+#include <asm/uaccess.h>
#define _GNUC_
#include <linux/sdla_fr.h> /* frame relay firmware API definitions */
@@ -67,6 +111,9 @@ typedef struct dlci_status
unsigned char state PACKED;
} dlci_status_t;
+static char TracingEnabled;
+/* variable for checking interrupts within the ISR routine */
+static int int_occur = 0;
/****** Function Prototypes *************************************************/
/* WAN link driver entry points. These are called by the WAN router module. */
@@ -75,14 +122,16 @@ static int new_if (wan_device_t* wandev, struct device* dev,
wanif_conf_t* conf);
static int del_if (wan_device_t* wandev, struct device* dev);
+/* WANPIPE-specific entry points */
+static int wpf_exec (struct sdla* card, void* u_cmd, void* u_data);
+
/* Network device interface */
static int if_init (struct device* dev);
static int if_open (struct device* dev);
static int if_close (struct device* dev);
static int if_header (struct sk_buff* skb, struct device* dev,
unsigned short type, void* daddr, void* saddr, unsigned len);
-static int if_rebuild_hdr (void* hdr, struct device* dev, unsigned long raddr,
- struct sk_buff* skb);
+static int if_rebuild_hdr (struct sk_buff* skb);
static int if_send (struct sk_buff* skb, struct device* dev);
static struct enet_statistics* if_stats (struct device* dev);
@@ -103,6 +152,8 @@ static int fr_configure (sdla_t* card, fr_conf_t *conf);
static int fr_set_intr_mode (sdla_t* card, unsigned mode, unsigned mtu);
static int fr_comm_enable (sdla_t* card);
static int fr_comm_disable (sdla_t* card);
+static int fr_get_err_stats (sdla_t* card);
+static int fr_get_stats (sdla_t* card);
static int fr_add_dlci (sdla_t* card, int dlci, int num);
static int fr_activate_dlci (sdla_t* card, int dlci, int num);
static int fr_issue_isf (sdla_t* card, int isf);
@@ -120,7 +171,6 @@ static void set_chan_state (struct device* dev, int state);
static struct device* find_channel (sdla_t* card, unsigned dlci);
static int is_tx_ready (sdla_t* card);
static unsigned int dec_to_uint (unsigned char* str, int len);
-static unsigned int hex_to_uint (unsigned char* str, int len);
/****** Public Functions ****************************************************/
@@ -198,7 +248,7 @@ __initfunc(int wpf_init (sdla_t* card, wandev_conf_t* conf))
u.cfg.n392 = 3;
u.cfg.n393 = 4;
u.cfg.kbps = conf->bps / 1000;
- u.cfg.cir_fwd = max(min(u.cfg.kbps, 512), 1);
+ u.cfg.cir_fwd = 16;
u.cfg.cir_bwd = u.cfg.bc_fwd = u.cfg.bc_bwd = u.cfg.cir_fwd;
u.cfg.options = 0x0081; /* direct Rx, no CIR check */
switch (conf->u.fr.signalling)
@@ -214,7 +264,7 @@ __initfunc(int wpf_init (sdla_t* card, wandev_conf_t* conf))
{
u.cfg.station = 1; /* switch emulation mode */
card->u.f.node_dlci = conf->u.fr.dlci ? conf->u.fr.dlci : 16;
- card->u.f.dlci_num = max(min(conf->u.fr.dlci_num, 1), 100);
+ card->u.f.dlci_num = min(max(conf->u.fr.dlci_num, 1), 100);
}
if (conf->clocking == WANOPT_INTERNAL)
u.cfg.port |= 0x0001
@@ -270,11 +320,14 @@ __initfunc(int wpf_init (sdla_t* card, wandev_conf_t* conf))
card->wandev.clocking = conf->clocking;
card->wandev.station = conf->station;
card->poll = &wpf_poll;
+ card->exec = &wpf_exec;
card->wandev.update = &update;
card->wandev.new_if = &new_if;
card->wandev.del_if = &del_if;
card->wandev.state = WAN_DISCONNECTED;
- return 0;
+ card->wandev.udp_port = conf->udp_port;
+ TracingEnabled = '0';
+ return 0;
}
/******* WAN Device Driver Entry Points *************************************/
@@ -284,9 +337,22 @@ __initfunc(int wpf_init (sdla_t* card, wandev_conf_t* conf))
*/
static int update (wan_device_t* wandev)
{
-/*
- sdla_t* card = wandev->private;
-*/
+ sdla_t* card;
+
+ /* sanity checks */
+ if ((wandev == NULL) || (wandev->private == NULL))
+ return -EFAULT
+ ;
+ if (wandev->state == WAN_UNCONFIGURED)
+ return -ENODEV
+ ;
+ if (test_and_set_bit(0, (void*)&wandev->critical))
+ return -EAGAIN
+ ;
+ card = wandev->private;
+ fr_get_err_stats(card);
+ fr_get_stats(card);
+ wandev->critical = 0;
return 0;
}
@@ -377,6 +443,43 @@ static int del_if (wan_device_t* wandev, struct device* dev)
return 0;
}
+/****** WANPIPE-specific entry points ***************************************/
+
+/*============================================================================
+ * Execute adapter interface command.
+ */
+static int wpf_exec (struct sdla* card, void* u_cmd, void* u_data)
+{
+ fr_mbox_t* mbox = card->mbox;
+ int retry = MAX_CMD_RETRY;
+ int err, len;
+ fr_cmd_t cmd;
+
+ if(copy_from_user((void*)&cmd, u_cmd, sizeof(cmd)))
+ return -EFAULT;
+ /* execute command */
+ do
+ {
+ memcpy(&mbox->cmd, &cmd, sizeof(cmd));
+ if (cmd.length)
+ if(copy_from_user((void*)&mbox->data, u_data, cmd.length))
+ return -EFAULT;
+ if (sdla_exec(mbox))
+ err = mbox->cmd.result;
+ else
+ return -EIO;
+ }
+ while (err && retry-- && fr_event(card, err, mbox));
+
+ /* return result */
+ if(copy_to_user(u_cmd, (void*)&mbox->cmd, sizeof(fr_cmd_t)))
+ return -EFAULT;
+ len = mbox->cmd.length;
+ if (len && u_data && copy_to_user(u_data, (void*)&mbox->data, len))
+ return -EFAULT;
+ return 0;
+}
+
/****** Network Device Interface ********************************************/
/*============================================================================
@@ -416,6 +519,9 @@ static int if_init (struct device* dev)
dev->mem_start = wandev->maddr;
dev->mem_end = wandev->maddr + wandev->msize - 1;
+ /* Set transmit buffer queue length */
+ dev->tx_queue_len = 30;
+
/* Initialize socket buffers */
for (i = 0; i < DEV_NUMBUFFS; ++i)
skb_queue_head_init(&dev->buffs[i])
@@ -451,12 +557,14 @@ static int if_open (struct device* dev)
err = -EIO;
goto done;
}
+ wanpipe_set_state(card, WAN_CONNECTED);
+
if (card->wandev.station == WANOPT_CPE)
{
/* CPE: issue full status enquiry */
fr_issue_isf(card, FR_ISF_FSE);
}
- else /* Switch: activate DLCI(s) */
+ else /* FR switch: activate DLCI(s) */
{
fr_add_dlci(card,
card->u.f.node_dlci, card->u.f.dlci_num)
@@ -465,7 +573,6 @@ static int if_open (struct device* dev)
card->u.f.node_dlci, card->u.f.dlci_num)
;
}
- wanpipe_set_state(card, WAN_CONNECTED);
}
dev->mtu = min(dev->mtu, card->wandev.mtu - FR_HEADER_LEN);
dev->interrupt = 0;
@@ -538,14 +645,13 @@ static int if_header (struct sk_buff* skb, struct device* dev,
* Return: 1 physical address resolved.
* 0 physical address not resolved
*/
-static int if_rebuild_hdr (void* hdr, struct device* dev, unsigned long raddr,
- struct sk_buff* skb)
+static int if_rebuild_hdr (struct sk_buff* skb)
{
- fr_channel_t* chan = dev->priv;
+ fr_channel_t* chan = skb->dev->priv;
sdla_t* card = chan->card;
printk(KERN_INFO "%s: rebuild_header() called for interface %s!\n",
- card->devname, dev->name)
+ card->devname, skb->dev->name)
;
return 1;
}
@@ -570,10 +676,11 @@ static int if_rebuild_hdr (void* hdr, struct device* dev, unsigned long raddr,
*/
static int if_send (struct sk_buff* skb, struct device* dev)
{
- fr_channel_t* chan = dev->priv;
- sdla_t* card = chan->card;
- int retry = 0;
-
+ fr_channel_t *chan = dev->priv;
+ sdla_t *card = chan->card;
+ int retry=0, err;
+ struct device *dev2;
+
if (test_and_set_bit(0, (void*)&card->wandev.critical))
{
#ifdef _DEBUG_
@@ -581,7 +688,8 @@ static int if_send (struct sk_buff* skb, struct device* dev)
card->devname)
;
#endif
- return 1;
+ dev_kfree_skb(skb, FREE_WRITE);
+ return 0;
}
if (test_and_set_bit(0, (void*)&dev->tbusy))
@@ -592,23 +700,55 @@ static int if_send (struct sk_buff* skb, struct device* dev)
;
#endif
++chan->ifstats.collisions;
+ ++card->wandev.stats.collisions;
+
retry = 1;
+ if(card->wandev.tx_int_enabled)
+ {
+ for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave)
+ {
+ dev2->tbusy = 1;
+ }
+ }
+ }
+ else if (card->wandev.state != WAN_CONNECTED)
+ {
+ ++chan->ifstats.tx_dropped;
+ ++card->wandev.stats.tx_dropped;
+ }
+ else if (chan->state != WAN_CONNECTED)
+ {
+ update_chan_state(dev);
+ ++chan->ifstats.tx_dropped;
+ ++card->wandev.stats.tx_dropped;
+ }
+ else if (!is_tx_ready(card))
+ {
+ retry = 1;
+ if(card->wandev.tx_int_enabled)
+ {
+ for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave)
+ {
+ dev2->tbusy = 1;
+ }
+ }
}
- else if ((card->wandev.state != WAN_CONNECTED) ||
- (chan->state != WAN_CONNECTED))
- ++chan->ifstats.tx_dropped
- ;
- else if (!is_tx_ready(card))
- retry = 1
- ;
else
{
- int err = (card->hw.fwid == SFID_FR508) ?
+ err = (card->hw.fwid == SFID_FR508) ?
fr508_send(card, chan->dlci, 0, skb->len, skb->data) :
fr502_send(card, chan->dlci, 0, skb->len, skb->data)
;
- if (err) ++chan->ifstats.tx_errors;
- else ++chan->ifstats.tx_packets;
+ if (err)
+ {
+ ++chan->ifstats.tx_errors;
+ ++card->wandev.stats.tx_errors;
+ }
+ else
+ {
+ ++chan->ifstats.tx_packets;
+ ++card->wandev.stats.tx_packets;
+ }
}
if (!retry)
{
@@ -619,6 +759,7 @@ static int if_send (struct sk_buff* skb, struct device* dev)
return retry;
}
+
/*============================================================================
* Get ethernet-style interface statistics.
* Return a pointer to struct enet_statistics.
@@ -663,7 +804,14 @@ static void fr508_isr (sdla_t* card)
{
fr508_flags_t* flags = card->flags;
fr_buf_ctl_t* bctl;
-
+
+ if(int_occur){
+#ifdef _DEBUG_
+ printk(KERN_INFO "%s:Interrupt Occurred within an ISR\n",card->devname);
+#endif
+ return;
+ }
+ int_occur=1;
switch (flags->iflag)
{
case 0x01: /* receive interrupt */
@@ -681,6 +829,7 @@ static void fr508_isr (sdla_t* card)
default:
spur_intr(card);
}
+ int_occur = 0;
flags->iflag = 0;
}
@@ -690,13 +839,14 @@ static void fr508_isr (sdla_t* card)
static void fr502_rx_intr (sdla_t* card)
{
fr_mbox_t* mbox = card->rxmb;
- struct sk_buff* skb;
- struct device* dev;
- fr_channel_t* chan;
+ struct sk_buff *skb;
+ struct device *dev;
+ fr_channel_t *chan;
unsigned dlci, len;
void* buf;
-
+
sdla_mapmem(&card->hw, FR502_RX_VECTOR);
+
dlci = mbox->cmd.dlci;
len = mbox->cmd.length;
@@ -741,13 +891,14 @@ static void fr502_rx_intr (sdla_t* card)
/* can't decapsulate packet */
dev_kfree_skb(skb, FREE_READ);
++chan->ifstats.rx_errors;
+ ++card->wandev.stats.rx_errors;
}
else
{
netif_rx(skb);
++chan->ifstats.rx_packets;
+ ++card->wandev.stats.rx_packets;
}
-
rx_done:
sdla_mapmem(&card->hw, FR_MB_VECTOR);
}
@@ -763,7 +914,7 @@ static void fr508_rx_intr (sdla_t* card)
fr_channel_t* chan;
unsigned dlci, len, offs;
void* buf;
-
+
if (frbuf->flag != 0x01)
{
printk(KERN_INFO "%s: corrupted Rx buffer @ 0x%X!\n",
@@ -804,9 +955,9 @@ static void fr508_rx_intr (sdla_t* card)
}
/* Copy data to the socket buffer */
- if ((offs + len) > card->u.f.rx_top)
+ if ((offs + len) > card->u.f.rx_top + 1)
{
- unsigned tmp = card->u.f.rx_top - offs;
+ unsigned tmp = card->u.f.rx_top - offs + 1;
buf = skb_put(skb, tmp);
sdla_peek(&card->hw, offs, buf, tmp);
@@ -814,8 +965,7 @@ static void fr508_rx_intr (sdla_t* card)
len -= tmp;
}
buf = skb_put(skb, len);
- sdla_peek(&card->hw, offs, buf, len);
-
+ sdla_peek(&card->hw, offs, buf, len);
/* Decapsulate packet and pass it up the protocol stack */
skb->dev = dev;
buf = skb_pull(skb, 1); /* remove hardware header */
@@ -824,13 +974,14 @@ static void fr508_rx_intr (sdla_t* card)
/* can't decapsulate packet */
dev_kfree_skb(skb, FREE_READ);
++chan->ifstats.rx_errors;
+ ++card->wandev.stats.rx_errors;
}
else
{
netif_rx(skb);
++chan->ifstats.rx_packets;
+ ++card->wandev.stats.rx_packets;
}
-
rx_done:
/* Release buffer element and calculate a pointer to the next one */
frbuf->flag = 0;
@@ -848,7 +999,17 @@ rx_done:
*/
static void tx_intr (sdla_t* card)
{
+ struct device* dev = card->wandev.dev;
+
+ for (; dev; dev = dev->slave) {
+ if( !dev || !dev->start ) continue;
+ dev->tbusy = 0;
+ dev_tint(dev);
+ }
+ card->wandev.tx_int_enabled = 0;
+/*
printk(KERN_INFO "%s: transmit interrupt!\n", card->devname);
+*/
}
/*============================================================================
@@ -877,8 +1038,14 @@ static void spur_intr (sdla_t* card)
*/
static void wpf_poll (sdla_t* card)
{
- fr502_flags_t* flags = card->flags;
+ static unsigned long last_poll;
+ fr502_flags_t* flags;
+ if ((jiffies - last_poll) < HZ)
+ return
+ ;
+
+ flags = card->flags;
if (flags->event)
{
fr_mbox_t* mbox = card->mbox;
@@ -889,6 +1056,7 @@ static void wpf_poll (sdla_t* card)
err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
if (err) fr_event(card, err, mbox);
}
+ last_poll = jiffies;
}
/****** Frame Relay Firmware-Specific Functions *****************************/
@@ -1025,6 +1193,65 @@ static int fr_comm_disable (sdla_t* card)
}
/*============================================================================
+ * Get communications error statistics.
+ */
+static int fr_get_err_stats (sdla_t* card)
+{
+ fr_mbox_t* mbox = card->mbox;
+ int retry = MAX_CMD_RETRY;
+ int err;
+
+ do
+ {
+ memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
+ mbox->cmd.command = FR_READ_ERROR_STATS;
+ err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
+ }
+ while (err && retry-- && fr_event(card, err, mbox));
+
+ if (!err)
+ {
+ fr_comm_stat_t* stats = (void*)mbox->data;
+
+ card->wandev.stats.rx_over_errors = stats->rx_overruns;
+ card->wandev.stats.rx_crc_errors = stats->rx_bad_crc;
+ card->wandev.stats.rx_missed_errors = stats->rx_aborts;
+ card->wandev.stats.rx_length_errors = stats->rx_too_long;
+ card->wandev.stats.tx_aborted_errors = stats->tx_aborts;
+ }
+ return err;
+}
+
+/*============================================================================
+ * Get statistics.
+ */
+static int fr_get_stats (sdla_t* card)
+{
+ fr_mbox_t* mbox = card->mbox;
+ int retry = MAX_CMD_RETRY;
+ int err;
+
+ do
+ {
+ memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
+ mbox->cmd.command = FR_READ_STATISTICS;
+ err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
+ }
+ while (err && retry-- && fr_event(card, err, mbox));
+
+ if (!err)
+ {
+ fr_link_stat_t* stats = (void*)mbox->data;
+
+ card->wandev.stats.rx_frame_errors = stats->rx_bad_format;
+ card->wandev.stats.rx_dropped =
+ stats->rx_dropped + stats->rx_dropped2
+ ;
+ }
+ return err;
+}
+
+/*============================================================================
* Add DLCI(s) (Access Node only!).
*/
static int fr_add_dlci (sdla_t* card, int dlci, int num)
@@ -1363,6 +1590,7 @@ static int is_tx_ready (sdla_t* card)
if (sb & 0x02) return 1;
flags->imask |= 0x02;
+ card->wandev.tx_int_enabled = 1;
}
else
{
@@ -1389,27 +1617,4 @@ static unsigned int dec_to_uint (unsigned char* str, int len)
return val;
}
-/*============================================================================
- * Convert hex string to unsigned integer.
- * If len != 0 then only 'len' characters of the string are conferted.
- */
-static unsigned int hex_to_uint (unsigned char* str, int len)
-{
- unsigned val, ch;
-
- if (!len) len = strlen(str);
- for (val = 0; len; ++str, --len)
- {
- ch = *str;
- if (is_digit(ch))
- val = (val << 4) + (ch - (unsigned)'0')
- ;
- else if (is_hex_digit(ch))
- val = (val << 4) + ((ch & 0xDF) - (unsigned)'A' + 10)
- ;
- else break;
- }
- return val;
-}
-
/****** End *****************************************************************/
diff --git a/drivers/net/sdla_ppp.c b/drivers/net/sdla_ppp.c
index c5c5ecc55..fa636040b 100644
--- a/drivers/net/sdla_ppp.c
+++ b/drivers/net/sdla_ppp.c
@@ -10,6 +10,25 @@
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
* ============================================================================
+* Jun 29, 1997 Alan Cox o Dumped the idiot UDP management system.
+*
+* May 22, 1997 Jaspreet Singh o Added change in the PPP_SET_CONFIG command for
+* 508 card to reflect changes in the new
+* ppp508.sfm for supporting:continous transmission
+* of Configure-Request packets without receiving a
+* reply
+* OR-ed 0x300 to conf_flags
+* o Changed connect_tmout from 900 to 0
+* May 21, 1997 Jaspreet Singh o Fixed UDP Management for multiple boards
+* Apr 25, 1997 Farhan Thawar o added UDP Management stuff
+* Mar 11, 1997 Farhan Thawar Version 3.1.1
+* o fixed (+1) bug in rx_intr()
+* o changed if_send() to return 0 if
+* wandev.critical() is true
+* o free socket buffer in if_send() if
+* returning 0
+* Jan 15, 1997 Gene Kozin Version 3.1.0
+* o implemented exec() entry point
* Jan 06, 1997 Gene Kozin Initial version.
*****************************************************************************/
@@ -22,11 +41,12 @@
#include <linux/errno.h> /* return codes */
#include <linux/string.h> /* inline memset(), etc. */
#include <linux/malloc.h> /* kmalloc(), kfree() */
-#include <linux/router.h> /* WAN router definitions */
+#include <linux/wanrouter.h> /* WAN router definitions */
#include <linux/wanpipe.h> /* WANPIPE common user API definitions */
#include <linux/if_arp.h> /* ARPHRD_* defines */
#include <linux/init.h> /* __initfunc et al. */
#include <asm/byteorder.h> /* htons(), etc. */
+#include <asm/uaccess.h>
#define _GNUC_
#include <linux/sdla_ppp.h> /* PPP firmware API definitions */
@@ -57,14 +77,16 @@ static int new_if (wan_device_t* wandev, struct device* dev,
wanif_conf_t* conf);
static int del_if (wan_device_t* wandev, struct device* dev);
+/* WANPIPE-specific entry points */
+static int wpp_exec (struct sdla* card, void* u_cmd, void* u_data);
+
/* Network device interface */
static int if_init (struct device* dev);
static int if_open (struct device* dev);
static int if_close (struct device* dev);
static int if_header (struct sk_buff* skb, struct device* dev,
unsigned short type, void* daddr, void* saddr, unsigned len);
-static int if_rebuild_hdr (void* hdr, struct device* dev, unsigned long raddr,
- struct sk_buff* skb);
+static int if_rebuild_hdr (struct sk_buff* skb);
static int if_send (struct sk_buff* skb, struct device* dev);
static struct enet_statistics* if_stats (struct device* dev);
@@ -74,6 +96,7 @@ static int ppp_configure (sdla_t* card, void* data);
static int ppp_set_intr_mode (sdla_t* card, unsigned mode);
static int ppp_comm_enable (sdla_t* card);
static int ppp_comm_disable (sdla_t* card);
+static int ppp_get_err_stats (sdla_t* card);
static int ppp_send (sdla_t* card, void* data, unsigned len, unsigned proto);
static int ppp_error (sdla_t *card, int err, ppp_mbox_t* mb);
@@ -94,6 +117,7 @@ static int config508 (sdla_t* card);
static void show_disc_cause (sdla_t* card, unsigned cause);
static unsigned char bps_to_speed_code (unsigned long bps);
+static char TracingEnabled;
/****** Public Functions ****************************************************/
/*============================================================================
@@ -163,10 +187,13 @@ __initfunc(int wpp_init (sdla_t* card, wandev_conf_t* conf))
card->wandev.station = conf->station;
card->isr = &wpp_isr;
card->poll = &wpp_poll;
+ card->exec = &wpp_exec;
card->wandev.update = &update;
card->wandev.new_if = &new_if;
card->wandev.del_if = &del_if;
card->wandev.state = WAN_DISCONNECTED;
+ card->wandev.udp_port = conf->udp_port;
+ TracingEnabled = '0';
return 0;
}
@@ -177,9 +204,22 @@ __initfunc(int wpp_init (sdla_t* card, wandev_conf_t* conf))
*/
static int update (wan_device_t* wandev)
{
-/*
- sdla_t* card = wandev->private;
-*/
+ sdla_t* card;
+
+ /* sanity checks */
+ if ((wandev == NULL) || (wandev->private == NULL))
+ return -EFAULT
+ ;
+ if (wandev->state == WAN_UNCONFIGURED)
+ return -ENODEV
+ ;
+ if (test_and_set_bit(0, (void*)&wandev->critical))
+ return -EAGAIN
+ ;
+ card = wandev->private;
+
+ ppp_get_err_stats(card);
+ wandev->critical = 0;
return 0;
}
@@ -228,6 +268,38 @@ static int del_if (wan_device_t* wandev, struct device* dev)
return 0;
}
+/****** WANPIPE-specific entry points ***************************************/
+
+/*============================================================================
+ * Execute adapter interface command.
+ */
+static int wpp_exec (struct sdla* card, void* u_cmd, void* u_data)
+{
+ ppp_mbox_t* mbox = card->mbox;
+ int len;
+
+ if(copy_from_user((void*)&mbox->cmd, u_cmd, sizeof(ppp_cmd_t)))
+ return -EFAULT;
+ len = mbox->cmd.length;
+ if (len)
+ {
+ if(copy_from_user((void*)&mbox->data, u_data, len))
+ return -EFAULT;
+ }
+
+ /* execute command */
+ if (!sdla_exec(mbox))
+ return -EIO;
+
+ /* return result */
+ if(copy_to_user(u_cmd, (void*)&mbox->cmd, sizeof(ppp_cmd_t)))
+ return -EFAULT;
+ len = mbox->cmd.length;
+ if (len && u_data && copy_to_user(u_data, (void*)&mbox->data, len))
+ return -EFAULT;
+ return 0;
+}
+
/****** Network Device Interface ********************************************/
/*============================================================================
@@ -264,6 +336,9 @@ static int if_init (struct device* dev)
dev->mem_start = wandev->maddr;
dev->mem_end = wandev->maddr + wandev->msize - 1;
+ /* Set transmit buffer queue length */
+ dev->tx_queue_len = 30;
+
/* Initialize socket buffers */
for (i = 0; i < DEV_NUMBUFFS; ++i)
skb_queue_head_init(&dev->buffs[i])
@@ -408,13 +483,12 @@ static int if_header (struct sk_buff* skb, struct device* dev,
* Return: 1 physical address resolved.
* 0 physical address not resolved
*/
-static int if_rebuild_hdr (void* hdr, struct device* dev, unsigned long raddr,
- struct sk_buff* skb)
+static int if_rebuild_hdr (struct sk_buff* skb)
{
- sdla_t* card = dev->priv;
+ sdla_t* card = skb->dev->priv;
printk(KERN_INFO "%s: rebuild_header() called for interface %s!\n",
- card->devname, dev->name)
+ card->devname, skb->dev->name)
;
return 1;
}
@@ -460,8 +534,7 @@ static int if_send (struct sk_buff* skb, struct device* dev)
#endif
++card->wandev.stats.collisions;
retry = 1;
- }
- else if (card->wandev.state != WAN_CONNECTED)
+ } else if (card->wandev.state != WAN_CONNECTED)
++card->wandev.stats.tx_dropped
;
else if (!skb->protocol)
@@ -489,6 +562,7 @@ static int if_send (struct sk_buff* skb, struct device* dev)
* Get ethernet-style interface statistics.
* Return a pointer to struct enet_statistics.
*/
+
static struct enet_statistics* if_stats (struct device* dev)
{
sdla_t* card = dev->priv;
@@ -599,6 +673,31 @@ static int ppp_comm_disable (sdla_t* card)
}
/*============================================================================
+ * Get communications error statistics.
+ */
+static int ppp_get_err_stats (sdla_t* card)
+{
+ ppp_mbox_t* mb = card->mbox;
+ int err;
+
+ memset(&mb->cmd, 0, sizeof(ppp_cmd_t));
+ mb->cmd.command = PPP_READ_ERROR_STATS;
+ err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT;
+ if (err == CMD_OK)
+ {
+ ppp_err_stats_t* stats = (void*)mb->data;
+
+ card->wandev.stats.rx_over_errors = stats->rx_overrun;
+ card->wandev.stats.rx_crc_errors = stats->rx_bad_crc;
+ card->wandev.stats.rx_missed_errors = stats->rx_abort;
+ card->wandev.stats.rx_length_errors = stats->rx_lost;
+ card->wandev.stats.tx_aborted_errors = stats->tx_abort;
+ }
+ else ppp_error(card, err, mb);
+ return err;
+}
+
+/*============================================================================
* Send packet.
* Return: 0 - o.k.
* 1 - no transmit buffers available
@@ -701,7 +800,7 @@ static void rx_intr (sdla_t* card)
struct sk_buff* skb;
unsigned len;
void* buf;
-
+
if (rxbuf->flag != 0x01)
{
printk(KERN_INFO "%s: corrupted Rx buffer @ 0x%X!\n",
@@ -738,9 +837,9 @@ static void rx_intr (sdla_t* card)
{
unsigned addr = rxbuf->buf.ptr;
- if ((addr + len) > card->u.p.rx_top)
+ if ((addr + len) > card->u.p.rx_top + 1)
{
- unsigned tmp = card->u.p.rx_top - addr;
+ unsigned tmp = card->u.p.rx_top - addr + 1;
buf = skb_put(skb, tmp);
sdla_peek(&card->hw, addr, buf, tmp);
@@ -751,8 +850,8 @@ static void rx_intr (sdla_t* card)
sdla_peek(&card->hw, addr, buf, len);
}
- /* Decapsulate packet and pass it up the protocol stack */
- switch (rxbuf->proto)
+ /* Decapsulate packet */
+ switch (rxbuf->proto)
{
case 0x00:
skb->protocol = htons(ETH_P_IP);
@@ -762,10 +861,11 @@ static void rx_intr (sdla_t* card)
skb->protocol = htons(ETH_P_IPX);
break;
}
+
+ /* Pass it up the protocol stack */
skb->dev = dev;
netif_rx(skb);
++card->wandev.stats.rx_packets;
-
rx_done:
/* Release buffer element and calculate a pointer to the next one */
rxbuf->flag = (card->hw.fwid == SFID_PPP502) ? 0xFF : 0x00;
@@ -891,13 +991,14 @@ static int config502 (sdla_t* card)
cfg.auth_wait_tmr = 300;
cfg.mdm_fail_tmr = 5;
cfg.dtr_drop_tmr = 1;
- cfg.connect_tmout = 900;
+ cfg.connect_tmout = 0; /* changed it from 900 */
cfg.conf_retry = 10;
cfg.term_retry = 2;
cfg.fail_retry = 5;
cfg.auth_retry = 10;
cfg.ip_options = 0x80;
cfg.ipx_options = 0xA0;
+ cfg.conf_flags |= 0x0E;
/*
cfg.ip_local = dev->pa_addr;
cfg.ip_remote = dev->pa_dstaddr;
@@ -921,6 +1022,7 @@ static int config508 (sdla_t* card)
if (card->wandev.interface == WANOPT_RS232)
cfg.conf_flags |= 0x0020;
;
+ cfg.conf_flags |= 0x300; /*send Configure-Request packets forever*/
cfg.txbuf_percent = 60; /* % of Tx bufs */
cfg.mtu_local = card->wandev.mtu;
cfg.mtu_remote = card->wandev.mtu;
@@ -929,7 +1031,7 @@ static int config508 (sdla_t* card)
cfg.auth_wait_tmr = 300;
cfg.mdm_fail_tmr = 5;
cfg.dtr_drop_tmr = 1;
- cfg.connect_tmout = 900;
+ cfg.connect_tmout = 0; /* changed it from 900 */
cfg.conf_retry = 10;
cfg.term_retry = 2;
cfg.fail_retry = 5;
diff --git a/drivers/net/sdla_x25.c b/drivers/net/sdla_x25.c
index 3296c2819..77dfc43b0 100644
--- a/drivers/net/sdla_x25.c
+++ b/drivers/net/sdla_x25.c
@@ -10,6 +10,17 @@
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
* ============================================================================
+* Mar 11, 1997 Farhan Thawar Version 3.1.1
+* o added support for V35
+* o changed if_send() to return 0 if
+* wandev.critical() is true
+* o free socket buffer in if_send() if
+* returning 0
+* o added support for single '@' address to
+* accept all incoming calls
+* o fixed bug in set_chan_state() to disconnect
+* Jan 15, 1997 Gene Kozin Version 3.1.0
+* o implemented exec() entry point
* Jan 07, 1997 Gene Kozin Initial version.
*****************************************************************************/
@@ -22,10 +33,11 @@
#include <linux/errno.h> /* return codes */
#include <linux/string.h> /* inline memset(), etc. */
#include <linux/malloc.h> /* kmalloc(), kfree() */
-#include <linux/router.h> /* WAN router definitions */
+#include <linux/wanrouter.h> /* WAN router definitions */
#include <linux/wanpipe.h> /* WANPIPE common user API definitions */
#include <linux/init.h> /* __initfunc et al. */
#include <asm/byteorder.h> /* htons(), etc. */
+#include <asm/uaccess.h> /* copy_from_user, etc */
#define _GNUC_
#include <linux/sdla_x25.h> /* X.25 firmware API definitions */
@@ -90,14 +102,16 @@ static int new_if (wan_device_t* wandev, struct device* dev,
wanif_conf_t* conf);
static int del_if (wan_device_t* wandev, struct device* dev);
+/* WANPIPE-specific entry points */
+static int wpx_exec (struct sdla* card, void* u_cmd, void* u_data);
+
/* Network device interface */
static int if_init (struct device* dev);
static int if_open (struct device* dev);
static int if_close (struct device* dev);
static int if_header (struct sk_buff* skb, struct device* dev,
unsigned short type, void* daddr, void* saddr, unsigned len);
-static int if_rebuild_hdr (void* hdr, struct device* dev, unsigned long raddr,
- struct sk_buff* skb);
+static int if_rebuild_hdr (struct sk_buff* skb);
static int if_send (struct sk_buff* skb, struct device* dev);
static struct enet_statistics* if_stats (struct device* dev);
@@ -118,6 +132,8 @@ static void poll_active (sdla_t* card);
/* X.25 firmware interface functions */
static int x25_get_version (sdla_t* card, char* str);
static int x25_configure (sdla_t* card, TX25Config* conf);
+static int x25_get_err_stats (sdla_t* card);
+static int x25_get_stats (sdla_t* card);
static int x25_set_intr_mode (sdla_t* card, int mode);
static int x25_close_hdlc (sdla_t* card);
static int x25_open_hdlc (sdla_t* card);
@@ -235,7 +251,9 @@ __initfunc(int wpx_init (sdla_t* card, wandev_conf_t* conf))
{
u.cfg.station = 0; /* DCE mode */
}
-
+ if (conf->interface != WANOPT_RS232 ) {
+ u.cfg.hdlcOptions |= 0x80; /* V35 mode */
+ }
/* adjust MTU */
if (!conf->mtu || (conf->mtu >= 1024))
card->wandev.mtu = 1024
@@ -299,6 +317,7 @@ __initfunc(int wpx_init (sdla_t* card, wandev_conf_t* conf))
card->wandev.station = conf->station;
card->isr = &wpx_isr;
card->poll = &wpx_poll;
+ card->exec = &wpx_exec;
card->wandev.update = &update;
card->wandev.new_if = &new_if;
card->wandev.del_if = &del_if;
@@ -313,9 +332,23 @@ __initfunc(int wpx_init (sdla_t* card, wandev_conf_t* conf))
*/
static int update (wan_device_t* wandev)
{
-/*
- sdla_t* card = wandev->private;
-*/
+ sdla_t* card;
+
+ /* sanity checks */
+ if ((wandev == NULL) || (wandev->private == NULL))
+ return -EFAULT
+ ;
+ if (wandev->state == WAN_UNCONFIGURED)
+ return -ENODEV
+ ;
+ if (test_and_set_bit(0, (void*)&wandev->critical))
+ return -EAGAIN
+ ;
+ card = wandev->private;
+
+ x25_get_err_stats(card);
+ x25_get_stats(card);
+ wandev->critical = 0;
return 0;
}
@@ -412,6 +445,45 @@ static int del_if (wan_device_t* wandev, struct device* dev)
return 0;
}
+/****** WANPIPE-specific entry points ***************************************/
+
+/*============================================================================
+ * Execute adapter interface command.
+ */
+static int wpx_exec (struct sdla* card, void* u_cmd, void* u_data)
+{
+ TX25Mbox* mbox = card->mbox;
+ int retry = MAX_CMD_RETRY;
+ int err, len;
+ TX25Cmd cmd;
+
+ if(copy_from_user((void*)&cmd, u_cmd, sizeof(cmd)))
+ return -EFAULT;
+
+ /* execute command */
+ do
+ {
+ memcpy(&mbox->cmd, &cmd, sizeof(cmd));
+ if (cmd.length)
+ if(copy_from_user((void*)&mbox->data, u_data, cmd.length))
+ return -EFAULT;
+ if (sdla_exec(mbox))
+ err = mbox->cmd.result;
+ else
+ return -EIO;
+ }
+ while (err && retry-- && x25_error(card, err, cmd.command, cmd.lcn));
+
+ /* return result */
+ if(copy_to_user(u_cmd, (void*)&mbox->cmd, sizeof(TX25Cmd)))
+ return -EFAULT;
+ len = mbox->cmd.length;
+ if (len && u_data)
+ if(copy_to_user(u_data, (void*)&mbox->data, len))
+ return -EFAULT;
+ return 0;
+}
+
/****** Network Device Interface ********************************************/
/*============================================================================
@@ -555,14 +627,13 @@ static int if_header (struct sk_buff* skb, struct device* dev,
* Return: 1 physical address resolved.
* 0 physical address not resolved
*/
-static int if_rebuild_hdr (void* hdr, struct device* dev, unsigned long raddr,
- struct sk_buff* skb)
+static int if_rebuild_hdr (struct sk_buff* skb)
{
- x25_channel_t* chan = dev->priv;
+ x25_channel_t* chan = skb->dev->priv;
sdla_t* card = chan->card;
printk(KERN_INFO "%s: rebuild_header() called for interface %s!\n",
- card->devname, dev->name)
+ card->devname, skb->dev->name)
;
return 1;
}
@@ -588,7 +659,7 @@ static int if_send (struct sk_buff* skb, struct device* dev)
{
x25_channel_t* chan = dev->priv;
sdla_t* card = chan->card;
- int retry = 0, queued = 0;
+ int queued = 0;
if (test_and_set_bit(0, (void*)&card->wandev.critical))
{
@@ -597,7 +668,8 @@ static int if_send (struct sk_buff* skb, struct device* dev)
card->devname)
;
#endif
- return 1;
+ dev_kfree_skb(skb, FREE_WRITE);
+ return 0;
}
if (test_and_set_bit(0, (void*)&dev->tbusy))
@@ -608,7 +680,10 @@ static int if_send (struct sk_buff* skb, struct device* dev)
;
#endif
++chan->ifstats.collisions;
- retry = 1;
+ ++card->wandev.stats.collisions;
+ dev_kfree_skb(skb, FREE_WRITE);
+ card->wandev.critical = 0;
+ return 0;
}
else if (chan->protocol && (chan->protocol != skb->protocol))
{
@@ -646,13 +721,13 @@ static int if_send (struct sk_buff* skb, struct device* dev)
++chan->ifstats.tx_dropped;
}
- if (!retry && !queued)
+ if (!queued)
{
dev_kfree_skb(skb, FREE_WRITE);
dev->tbusy = 0;
}
card->wandev.critical = 0;
- return retry;
+ return 0;
}
/*============================================================================
@@ -996,6 +1071,62 @@ static int x25_configure (sdla_t* card, TX25Config* conf)
}
/*============================================================================
+ * Get communications error statistics.
+ */
+static int x25_get_err_stats (sdla_t* card)
+{
+ TX25Mbox* mbox = card->mbox;
+ int retry = MAX_CMD_RETRY;
+ int err;
+
+ do
+ {
+ memset(&mbox->cmd, 0, sizeof(TX25Cmd));
+ mbox->cmd.command = X25_HDLC_READ_COMM_ERR;
+ err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
+ } while (err && retry-- &&
+ x25_error(card, err, X25_HDLC_READ_COMM_ERR, 0))
+ ;
+ if (!err)
+ {
+ THdlcCommErr* stats = (void*)mbox->data;
+
+ card->wandev.stats.rx_over_errors = stats->rxOverrun;
+ card->wandev.stats.rx_crc_errors = stats->rxBadCrc;
+ card->wandev.stats.rx_missed_errors = stats->rxAborted;
+ card->wandev.stats.tx_aborted_errors = stats->txAborted;
+ }
+ return err;
+}
+
+/*============================================================================
+ * Get protocol statistics.
+ */
+static int x25_get_stats (sdla_t* card)
+{
+ TX25Mbox* mbox = card->mbox;
+ int retry = MAX_CMD_RETRY;
+ int err;
+
+ do
+ {
+ memset(&mbox->cmd, 0, sizeof(TX25Cmd));
+ mbox->cmd.command = X25_READ_STATISTICS;
+ err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
+ } while (err && retry-- &&
+ x25_error(card, err, X25_READ_STATISTICS, 0))
+ ;
+ if (!err)
+ {
+ TX25Stats* stats = (void*)mbox->data;
+
+ card->wandev.stats.rx_packets = stats->rxData;
+ card->wandev.stats.tx_packets = stats->rxData;
+ }
+ return err;
+}
+
+/*============================================================================
* Close HDLC link.
*/
static int x25_close_hdlc (sdla_t* card)
@@ -1478,6 +1609,10 @@ static int incomming_call (sdla_t* card, int cmd, int lcn, TX25Mbox* mb)
if (strcmp(info->src, chan->addr) == 0)
break
;
+ // If just an '@' is specified, accept all incomming calls
+ if (strcmp(chan->addr, "") == 0)
+ break
+ ;
}
if (dev == NULL)
@@ -1755,9 +1890,10 @@ static void set_chan_state (struct device* dev, int state)
printk (KERN_INFO "%s: interface %s disconnected!\n",
card->devname, dev->name)
;
- if (chan->svc)
- *(unsigned short*)dev->dev_addr = 0
- ;
+ if (chan->svc) {
+ *(unsigned short*)dev->dev_addr = 0;
+ chan->lcn = 0;
+ }
break;
}
chan->state = state;
diff --git a/drivers/net/sdlamain.c b/drivers/net/sdlamain.c
index 75d7df944..eeb0b40f3 100644
--- a/drivers/net/sdlamain.c
+++ b/drivers/net/sdlamain.c
@@ -26,9 +26,10 @@
#include <linux/module.h> /* support for loadable modules */
#include <linux/ioport.h> /* request_region(), release_region() */
#include <linux/tqueue.h> /* for kernel task queues */
-#include <linux/router.h> /* WAN router definitions */
+#include <linux/wanrouter.h> /* WAN router definitions */
#include <linux/wanpipe.h> /* WANPIPE common user API definitions */
-#include <asm/segment.h> /* kernel <-> user copy */
+#include <asm/uaccess.h> /* kernel <-> user copy */
+
/****** Defines & Macros ****************************************************/
@@ -404,20 +405,13 @@ static int ioctl_dump (sdla_t* card, sdla_dump_t* u_dump)
unsigned long oldvec; /* DPM window vector */
int err = 0;
- if ((u_dump == NULL) ||
- verify_area(VERIFY_READ, u_dump, sizeof(sdla_dump_t)))
- return -EFAULT
- ;
- memcpy_fromfs((void*)&dump, (void*)u_dump, sizeof(sdla_dump_t));
+ if(copy_from_user((void*)&dump, (void*)u_dump, sizeof(sdla_dump_t)))
+ return -EFAULT;
+
if ((dump.magic != WANPIPE_MAGIC) ||
(dump.offset + dump.length > card->hw.memory))
- return -EINVAL
- ;
- if ((dump.ptr == NULL) ||
- verify_area(VERIFY_WRITE, dump.ptr, dump.length))
- return -EFAULT
- ;
-
+ return -EINVAL;
+
winsize = card->hw.dpmsize;
cli(); /* >>> critical section start <<< */
oldvec = card->hw.vector;
@@ -433,9 +427,12 @@ static int ioctl_dump (sdla_t* card, sdla_dump_t* u_dump)
err = -EIO;
break;
}
- memcpy_tofs((void*)(dump.ptr),
- (void*)(card->hw.dpmbase + pos), len)
- ;
+ /* FIXME::: COPY TO KERNEL BUFFER FIRST ?? */
+ sti(); /* Not ideal but tough we have to do this */
+ if(copy_to_user((void*)(dump.ptr),
+ (void*)(card->hw.dpmbase + pos), len))
+ return -EFAULT;
+ cli();
dump.length -= len;
dump.offset += len;
(char*)dump.ptr += len;
@@ -456,16 +453,12 @@ static int ioctl_exec (sdla_t* card, sdla_exec_t* u_exec)
sdla_exec_t exec;
if (card->exec == NULL)
- return -ENODEV
- ;
- if ((u_exec == NULL) ||
- verify_area(VERIFY_READ, u_exec, sizeof(sdla_exec_t)))
- return -EFAULT
- ;
- memcpy_fromfs((void*)&exec, (void*)u_exec, sizeof(sdla_exec_t));
+ return -ENODEV;
+
+ if(copy_from_user((void*)&exec, (void*)u_exec, sizeof(sdla_exec_t)))
+ return -EFAULT;
if ((exec.magic != WANPIPE_MAGIC) || (exec.cmd == NULL))
- return -EINVAL
- ;
+ return -EINVAL;
return card->exec(card, exec.cmd, exec.data);
}
diff --git a/drivers/net/shaper.c b/drivers/net/shaper.c
index a40f16599..fff408c42 100644
--- a/drivers/net/shaper.c
+++ b/drivers/net/shaper.c
@@ -70,11 +70,11 @@
#include <linux/if_arp.h>
#include <linux/init.h>
#include <net/dst.h>
-#include "shaper.h"
+#include <linux/if_shaper.h>
int sh_debug; /* Debug flag */
-#define SHAPER_BANNER "CymruNet Traffic Shaper BETA 0.03 for Linux 2.1\n"
+#define SHAPER_BANNER "CymruNet Traffic Shaper BETA 0.04 for Linux 2.1\n"
/*
* Locking
@@ -426,17 +426,26 @@ static int shaper_header(struct sk_buff *skb, struct device *dev,
unsigned short type, void *daddr, void *saddr, unsigned len)
{
struct shaper *sh=dev->priv;
+ int v;
if(sh_debug)
printk("Shaper header\n");
- return sh->hard_header(skb,sh->dev,type,daddr,saddr,len);
+ skb->dev=sh->dev;
+ v=sh->hard_header(skb,sh->dev,type,daddr,saddr,len);
+ skb->dev=dev;
+ return v;
}
static int shaper_rebuild_header(struct sk_buff *skb)
{
struct shaper *sh=skb->dev->priv;
+ struct device *dev=skb->dev;
+ int v;
if(sh_debug)
printk("Shaper rebuild header\n");
- return sh->rebuild_header(skb);
+ skb->dev=sh->dev;
+ v=sh->rebuild_header(skb);
+ skb->dev=dev;
+ return v;
}
static int shaper_cache(struct dst_entry *dst, struct neighbour *neigh, struct hh_cache *hh)
@@ -483,6 +492,7 @@ static int shaper_attach(struct device *shdev, struct shaper *sh, struct device
else
shdev->rebuild_header = NULL;
+#if 0
if(dev->hard_header_cache)
{
sh->hard_header_cache = dev->hard_header_cache;
@@ -500,6 +510,10 @@ static int shaper_attach(struct device *shdev, struct shaper *sh, struct device
}
else
shdev->header_cache_update= NULL;
+#else
+ shdev->header_cache_update = NULL;
+ shdev->hard_header_cache = NULL;
+#endif
shdev->hard_header_len=dev->hard_header_len;
shdev->type=dev->type;
diff --git a/drivers/net/strip.c b/drivers/net/strip.c
index 32c02b4f0..0104f6dc9 100644
--- a/drivers/net/strip.c
+++ b/drivers/net/strip.c
@@ -14,53 +14,62 @@
* for kernel-based devices like TTY. It interfaces between a
* raw TTY, and the kernel's INET protocol layers (via DDI).
*
- * Version: @(#)strip.c 0.9.8 June 1996
+ * Version: @(#)strip.c 1.2 February 1997
*
* Author: Stuart Cheshire <cheshire@cs.stanford.edu>
*
- * Fixes: v0.9 12th Feb 1996.
+ * Fixes: v0.9 12th Feb 1996 (SC)
* New byte stuffing (2+6 run-length encoding)
* New watchdog timer task
* New Protocol key (SIP0)
*
- * v0.9.1 3rd March 1996
+ * v0.9.1 3rd March 1996 (SC)
* Changed to dynamic device allocation -- no more compile
* time (or boot time) limit on the number of STRIP devices.
*
- * v0.9.2 13th March 1996
+ * v0.9.2 13th March 1996 (SC)
* Uses arp cache lookups (but doesn't send arp packets yet)
*
- * v0.9.3 17th April 1996
+ * v0.9.3 17th April 1996 (SC)
* Fixed bug where STR_ERROR flag was getting set unneccessarily
+ * (causing otherwise good packets to be unneccessarily dropped)
*
- * v0.9.4 27th April 1996
+ * v0.9.4 27th April 1996 (SC)
* First attempt at using "&COMMAND" Starmode AT commands
*
- * v0.9.5 29th May 1996
+ * v0.9.5 29th May 1996 (SC)
* First attempt at sending (unicast) ARP packets
*
- * v0.9.6 5th June 1996
- * Elliot put "message level" tags in every "printk" statement
+ * v0.9.6 5th June 1996 (Elliot)
+ * Put "message level" tags in every "printk" statement
*
- * v0.9.7 13th June 1996
- * Added support for the /proc fs (laik)
+ * v0.9.7 13th June 1996 (laik)
+ * Added support for the /proc fs
*
- * v0.9.8 July 1996
- * Added packet logging (Mema)
- */
-
-/*
- * Undefine this symbol if you don't have PROC_NET_STRIP_STATUS
- * defined in include/linux/proc_fs.h
+ * v0.9.8 July 1996 (Mema)
+ * Added packet logging
+ *
+ * v1.0 November 1996 (SC)
+ * Fixed (severe) memory leaks in the /proc fs code
+ * Fixed race conditions in the logging code
+ *
+ * v1.1 January 1997 (SC)
+ * Deleted packet logging (use tcpdump instead)
+ * Added support for Metricom Firmware v204 features
+ * (like message checksums)
+ *
+ * v1.2 January 1997 (SC)
+ * Put portables list back in
*/
-#define DO_PROC_NET_STRIP_STATUS 1
-
-/*
- * Define this symbol if you want to enable STRIP packet tracing.
- */
+#ifdef MODULE
+static const char StripVersion[] = "1.2-STUART.CHESHIRE-MODULAR";
+#else
+static const char StripVersion[] = "1.2-STUART.CHESHIRE";
+#endif
-#define DO_PROC_NET_STRIP_TRACE 0
+#define TICKLE_TIMERS 0
+#define EXT_COUNTERS 1
/************************************************************************/
@@ -146,13 +155,19 @@ typedef struct
__u8 c[24];
} MetricomAddressString;
+/* Encapsulation can expand packet of size x to 65/64x + 1
+ * Sent packet looks like "<CR>*<address>*<key><encaps payload><CR>"
+ * 1 1 1-18 1 4 ? 1
+ * eg. <CR>*0000-1234*SIP0<encaps payload><CR>
+ * We allow 31 bytes for the stars, the key, the address and the <CR>s
+ */
+#define STRIP_ENCAP_SIZE(X) (32 + (X)*65L/64L)
+
/*
- * Note: A Metricom packet looks like this: *<address>*<key><payload><CR>
- * eg. *0000-1234*SIP0<payload><CR>
- * A STRIP_Header is never really sent over the radio, but making a dummy header
- * for internal use within the kernel that looks like an Ethernet header makes
- * certain other software happier. For example, tcpdump already understands
- * Ethernet headers.
+ * A STRIP_Header is never really sent over the radio, but making a dummy
+ * header for internal use within the kernel that looks like an Ethernet
+ * header makes certain other software happier. For example, tcpdump
+ * already understands Ethernet headers.
*/
typedef struct
@@ -162,83 +177,20 @@ typedef struct
unsigned short protocol; /* The protocol type, using Ethernet codes */
} STRIP_Header;
-typedef struct GeographicLocation
-{
- char s[18];
-} GeographicLocation;
-
-typedef enum {
- NodeValid = 0x1,
- NodeHasWAN = 0x2,
- NodeIsRouter = 0x4
-} NodeType;
-
-typedef struct MetricomNode
-{
- NodeType type; /* Some flags about the type of node */
- GeographicLocation gl; /* The location of the node. */
- MetricomAddress addr; /* The metricom address of this node */
- int poll_latency; /* The latency to poll that node ? */
- int rssi; /* The Receiver Signal Strength Indicator */
- struct MetricomNode *next; /* The next node */
-} MetricomNode;
-
-enum { FALSE = 0, TRUE = 1 };
-
-/*
- * Holds the packet signature for an IP packet.
- */
typedef struct
{
- IPaddr src;
- /* Data is stored in the following field in network byte order. */
- __u16 id;
-} IPSignature;
+ char c[60];
+} MetricomNode;
-/*
- * Holds the packet signature for an ARP packet.
- */
+#define NODE_TABLE_SIZE 32
typedef struct
{
- IPaddr src;
- /* Data is stored in the following field in network byte order. */
- __u16 op;
-} ARPSignature;
-
-/*
- * Holds the signature of a packet.
- */
-typedef union
-{
- IPSignature ip_sig;
- ARPSignature arp_sig;
- __u8 print_sig[6];
-} PacketSignature;
-
-typedef enum {
- EntrySend = 0,
- EntryReceive = 1
-} LogEntry;
-
-/* Structure for Packet Logging */
-typedef struct stripLog
-{
- LogEntry entry_type;
- u_long seqNum;
- int packet_type;
- PacketSignature sig;
- MetricomAddress src;
- MetricomAddress dest;
- struct timeval timeStamp;
- u_long rawSize;
- u_long stripSize;
- u_long slipSize;
- u_long valid;
-} StripLog;
+ struct timeval timestamp;
+ int num_nodes;
+ MetricomNode node[NODE_TABLE_SIZE];
+} MetricomNodeTable;
-#define ENTRY_TYPE_TO_STRING(X) ((X) ? "r" : "s")
-
-#define BOOLEAN_TO_STRING(X) ((X) ? "true" : "false")
+enum { FALSE = 0, TRUE = 1 };
/*
* Holds the radio's firmware version.
@@ -246,7 +198,7 @@ typedef struct stripLog
typedef struct
{
char c[50];
-} MetricomFirmwareVersion;
+} FirmwareVersion;
/*
* Holds the radio's serial number.
@@ -254,7 +206,7 @@ typedef struct
typedef struct
{
char c[18];
-} MetricomSerialNumber;
+} SerialNumber;
/*
* Holds the radio's battery voltage.
@@ -262,7 +214,19 @@ typedef struct
typedef struct
{
char c[11];
-} MetricomBatteryVoltage;
+} BatteryVoltage;
+
+typedef struct
+{
+ char c[8];
+} char8;
+
+enum
+{
+ NoStructure = 0, /* Really old firmware */
+ StructuredMessages = 1, /* Parsable AT response msgs */
+ ChecksummedMessages = 2 /* Parsable AT response msgs with checksums */
+} FirmwareLevel;
struct strip
{
@@ -292,6 +256,25 @@ struct strip
unsigned long tx_dropped; /* When MTU change */
unsigned long rx_over_errors; /* Frame bigger then STRIP buf. */
+ unsigned long pps_timer; /* Timer to determine pps */
+ unsigned long rx_pps_count; /* Counter to determine pps */
+ unsigned long tx_pps_count; /* Counter to determine pps */
+ unsigned long sx_pps_count; /* Counter to determine pps */
+ unsigned long rx_average_pps; /* rx packets per second * 8 */
+ unsigned long tx_average_pps; /* tx packets per second * 8 */
+ unsigned long sx_average_pps; /* sent packets per second * 8 */
+
+#ifdef EXT_COUNTERS
+ unsigned long rx_bytes; /* total received bytes */
+ unsigned long tx_bytes; /* total received bytes */
+ unsigned long rx_rbytes; /* bytes thru radio i/f */
+ unsigned long tx_rbytes; /* bytes thru radio i/f */
+ unsigned long rx_sbytes; /* tot bytes thru serial i/f */
+ unsigned long tx_sbytes; /* tot bytes thru serial i/f */
+ unsigned long rx_ebytes; /* tot stat/err bytes */
+ unsigned long tx_ebytes; /* tot stat/err bytes */
+#endif
+
/*
* Internal variables.
*/
@@ -300,52 +283,142 @@ struct strip
struct strip **referrer; /* The pointer that points to us*/
int discard; /* Set if serial error */
int working; /* Is radio working correctly? */
- int structured_messages; /* Parsable AT response msgs? */
+ int firmware_level; /* Message structuring level */
+ int next_command; /* Next periodic command */
int mtu; /* Our mtu (to spot changes!) */
long watchdog_doprobe; /* Next time to test the radio */
long watchdog_doreset; /* Time to do next reset */
long gratuitous_arp; /* Time to send next ARP refresh*/
long arp_interval; /* Next ARP interval */
struct timer_list idle_timer; /* For periodic wakeup calls */
- MetricomNode *neighbor_list; /* The list of neighbor nodes */
- int neighbor_list_locked; /* Indicates the list is locked */
- MetricomFirmwareVersion firmware_version; /* The radio's firmware version */
- MetricomSerialNumber serial_number; /* The radio's serial number */
- MetricomBatteryVoltage battery_voltage; /* The radio's battery voltage */
+ MetricomAddress true_dev_addr; /* True address of radio */
+ int manual_dev_addr; /* Hack: See note below */
+
+ FirmwareVersion firmware_version; /* The radio's firmware version */
+ SerialNumber serial_number; /* The radio's serial number */
+ BatteryVoltage battery_voltage; /* The radio's battery voltage */
/*
* Other useful structures.
*/
struct tty_struct *tty; /* ptr to TTY structure */
- char if_name[8]; /* Dynamically generated name */
+ char8 if_name; /* Dynamically generated name */
struct device dev; /* Our device structure */
/*
- * Packet Logging Structures.
+ * Neighbour radio records
*/
- u_long num_sent;
- u_long num_received;
-
- int next_entry; /* The index of the oldest packet; */
- /* Also the next to be logged. */
- StripLog packetLog[610];
+ MetricomNodeTable portables;
+ MetricomNodeTable poletops;
};
+/*
+ * Note: manual_dev_addr hack
+ *
+ * It is not possible to change the hardware address of a Metricom radio,
+ * or to send packets with a user-specified hardware source address, thus
+ * trying to manually set a hardware source address is a questionable
+ * thing to do. However, if the user *does* manually set the hardware
+ * source address of a STRIP interface, then the kernel will believe it,
+ * and use it in certain places. For example, the hardware address listed
+ * by ifconfig will be the manual address, not the true one.
+ * (Both addresses are listed in /proc/net/strip.)
+ * Also, ARP packets will be sent out giving the user-specified address as
+ * the source address, not the real address. This is dangerous, because
+ * it means you won't receive any replies -- the ARP replies will go to
+ * the specified address, which will be some other radio. The case where
+ * this is useful is when that other radio is also connected to the same
+ * machine. This allows you to connect a pair of radios to one machine,
+ * and to use one exclusively for inbound traffic, and the other
+ * exclusively for outbound traffic. Pretty neat, huh?
+ *
+ * Here's the full procedure to set this up:
+ *
+ * 1. "slattach" two interfaces, e.g. st0 for outgoing packets,
+ * and st1 for incoming packets
+ *
+ * 2. "ifconfig" st0 (outbound radio) to have the hardware address
+ * which is the real hardware address of st1 (inbound radio).
+ * Now when it sends out packets, it will masquerade as st1, and
+ * replies will be sent to that radio, which is exactly what we want.
+ *
+ * 3. Set the route table entry ("route add default ..." or
+ * "route add -net ...", as appropriate) to send packets via the st0
+ * interface (outbound radio). Do not add any route which sends packets
+ * out via the st1 interface -- that radio is for inbound traffic only.
+ *
+ * 4. "ifconfig" st1 (inbound radio) to have hardware address zero.
+ * This tells the STRIP driver to "shut down" that interface and not
+ * send any packets through it. In particular, it stops sending the
+ * periodic gratuitous ARP packets that a STRIP interface normally sends.
+ * Also, when packets arrive on that interface, it will search the
+ * interface list to see if there is another interface who's manual
+ * hardware address matches its own real address (i.e. st0 in this
+ * example) and if so it will transfer ownership of the skbuff to
+ * that interface, so that it looks to the kernel as if the packet
+ * arrived on that interface. This is necessary because when the
+ * kernel sends an ARP packet on st0, it expects to get a reply on
+ * st0, and if it sees the reply come from st1 then it will ignore
+ * it (to be accurate, it puts the entry in the ARP table, but
+ * labelled in such a way that st0 can't use it).
+ *
+ * Thanks to Petros Maniatis for coming up with the idea of splitting
+ * inbound and outbound traffic between two interfaces, which turned
+ * out to be really easy to implement, even if it is a bit of a hack.
+ *
+ * Having set a manual address on an interface, you can restore it
+ * to automatic operation (where the address is automatically kept
+ * consistent with the real address of the radio) by setting a manual
+ * address of all ones, e.g. "ifconfig st0 hw strip FFFFFFFFFFFF"
+ * This 'turns off' manual override mode for the device address.
+ *
+ * Note: The IEEE 802 headers reported in tcpdump will show the *real*
+ * radio addresses the packets were sent and received from, so that you
+ * can see what is really going on with packets, and which interfaces
+ * they are really going through.
+ */
+
/************************************************************************/
/* Constants */
-#ifdef MODULE
-static const char StripVersion[] = "0.9.8-STUART.CHESHIRE-MODULAR";
-#else
-static const char StripVersion[] = "0.9.8-STUART.CHESHIRE";
-#endif
+/*
+ * CommandString1 works on all radios
+ * Other CommandStrings are only used with firmware that provides structured responses.
+ *
+ * ats319=1 Enables Info message for node additions and deletions
+ * ats319=2 Enables Info message for a new best node
+ * ats319=4 Enables checksums
+ * ats319=8 Enables ACK messages
+ */
+
+static const int MaxCommandStringLength = 32;
+static const int CompatibilityCommand = 1;
-static const char TickleString1[] = "***&COMMAND*ATS305?\r";
-static const char TickleString2[] = "***&COMMAND*ATS305?\r\r"
- "*&COMMAND*ATS300?\r\r*&COMMAND*ATS325?\r\r*&COMMAND*AT~I2 nn\r\r";
+static const char CommandString0[] = "*&COMMAND*ATS319=7"; /* Turn on checksums & info messages */
+static const char CommandString1[] = "*&COMMAND*ATS305?"; /* Query radio name */
+static const char CommandString2[] = "*&COMMAND*ATS325?"; /* Query battery voltage */
+static const char CommandString3[] = "*&COMMAND*ATS300?"; /* Query version information */
+static const char CommandString4[] = "*&COMMAND*ATS311?"; /* Query poletop list */
+static const char CommandString5[] = "*&COMMAND*AT~LA"; /* Query portables list */
+typedef struct { const char *string; long length; } StringDescriptor;
+
+static const StringDescriptor CommandString[] =
+ {
+ { CommandString0, sizeof(CommandString0)-1 },
+ { CommandString1, sizeof(CommandString1)-1 },
+ { CommandString2, sizeof(CommandString2)-1 },
+ { CommandString3, sizeof(CommandString3)-1 },
+ { CommandString4, sizeof(CommandString4)-1 },
+ { CommandString5, sizeof(CommandString5)-1 }
+ };
+
+#define GOT_ALL_RADIO_INFO(S) \
+ ((S)->firmware_version.c[0] && \
+ (S)->battery_voltage.c[0] && \
+ memcmp(&(S)->true_dev_addr, zero_address.c, sizeof(zero_address)))
static const char hextable[16] = "0123456789ABCDEF";
@@ -354,26 +427,27 @@ static const MetricomAddress broadcast_address = { { 0xFF,0xFF,0xFF,0xFF,0xFF,0x
static const MetricomKey SIP0Key = { { "SIP0" } };
static const MetricomKey ARP0Key = { { "ARP0" } };
-static const MetricomKey ERR_Key = { { "ERR_" } };
static const MetricomKey ATR_Key = { { "ATR " } };
+static const MetricomKey ACK_Key = { { "ACK_" } };
+static const MetricomKey INF_Key = { { "INF_" } };
+static const MetricomKey ERR_Key = { { "ERR_" } };
static const long MaxARPInterval = 60 * HZ; /* One minute */
/*
- * Maximum Starmode packet length (including starmode address) is 1183 bytes.
- * Allowing 32 bytes for header, and 65/64 expansion for STRIP encoding,
- * that translates to a maximum payload MTU of 1132.
+ * Maximum Starmode packet length is 1183 bytes. Allowing 4 bytes for
+ * protocol key, 4 bytes for checksum, one byte for CR, and 65/64 expansion
+ * for STRIP encoding, that translates to a maximum payload MTU of 1155.
+ * Note: A standard NFS 1K data packet is a total of 0x480 (1152) bytes
+ * long, including IP header, UDP header, and NFS header. Setting the STRIP
+ * MTU to 1152 allows us to send default sized NFS packets without fragmentation.
*/
-static const unsigned short MAX_STRIP_MTU = 1132;
-static const unsigned short DEFAULT_STRIP_MTU = 1024;
+static const unsigned short MAX_SEND_MTU = 1152;
+static const unsigned short MAX_RECV_MTU = 1500; /* Hoping for Ethernet sized packets in the future! */
+static const unsigned short DEFAULT_STRIP_MTU = 1152;
static const int STRIP_MAGIC = 0x5303;
static const long LongTime = 0x7FFFFFFF;
-static const int STRIP_NODE_LEN = 64;
-static const char STRIP_PORTABLE_CHAR = 'P';
-static const char STRIP_ROUTER_CHAR = 'r';
-static const int STRIP_PROC_BUFFER_SIZE = 4096;
-static const int STRIP_LOG_INT_SIZE = 10;
/************************************************************************/
/* Global variables */
@@ -384,10 +458,18 @@ static struct strip *struct_strip_list = NULL;
/************************************************************************/
/* Macros */
+/* Returns TRUE if text T begins with prefix P */
+#define has_prefix(T,P) (!strncmp((T), (P), sizeof(P)-1))
+
+/* Returns TRUE if text T of length L is equal to string S */
+#define text_equal(T,L,S) (((L) == sizeof(S)-1) && !strncmp((T), (S), sizeof(S)-1))
+
#define READHEX(X) ((X)>='0' && (X)<='9' ? (X)-'0' : \
(X)>='a' && (X)<='f' ? (X)-'a'+10 : \
(X)>='A' && (X)<='F' ? (X)-'A'+10 : 0 )
+#define READHEX16(X) ((__u16)(READHEX(X)))
+
#define READDEC(X) ((X)>='0' && (X)<='9' ? (X)-'0' : 0)
#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
@@ -395,17 +477,6 @@ static struct strip *struct_strip_list = NULL;
#define ELEMENTS_OF(X) (sizeof(X) / sizeof((X)[0]))
#define ARRAY_END(X) (&((X)[ELEMENTS_OF(X)]))
-/* Encapsulation can expand packet of size x to 65/64x + 1 */
-/* Sent packet looks like "*<address>*<key><encaps payload><CR>" */
-/* 1 1-18 1 4 ? 1 */
-/* We allow 31 bytes for the stars, the key, the address and the <CR> */
-#define STRIP_ENCAP_SIZE(X) (32 + (X)*65L/64L)
-
-#define IS_RADIO_ADDRESS(p) ( \
- isdigit((p)[0]) && isdigit((p)[1]) && isdigit((p)[2]) && isdigit((p)[3]) && \
- (p)[4] == '-' && \
- isdigit((p)[5]) && isdigit((p)[6]) && isdigit((p)[7]) && isdigit((p)[8]) )
-
#define JIFFIE_TO_SEC(X) ((X) / HZ)
@@ -605,7 +676,15 @@ static __u8 *StuffData(__u8 *src, __u32 length, __u8 *dst, __u8 **code_ptr_ptr)
}
/* else, we only have one so far, so switch to Stuff_Diff code */
code = Stuff_Diff;
- /* and fall through to Stuff_Diff case below */
+ /* and fall through to Stuff_Diff case below
+ * Note cunning cleverness here: case Stuff_Diff compares
+ * the current character with the previous two to see if it
+ * has a run of three the same. Won't this be an error if
+ * there aren't two previous characters stored to compare with?
+ * No. Because we know the current character is *not* the same
+ * as the previous one, the first test below will necessarily
+ * fail and the send half of the "if" won't be executed.
+ */
/* Stuff_Diff: We have at least two *different* bytes encoded */
case Stuff_Diff:
@@ -639,10 +718,10 @@ static __u8 *StuffData(__u8 *src, __u32 length, __u8 *dst, __u8 **code_ptr_ptr)
src++; /* Consume the byte */
break;
}
- if (count == Stuff_MaxCount)
- {
- StuffData_FinishBlock(code + count);
- }
+ if (count == Stuff_MaxCount)
+ {
+ StuffData_FinishBlock(code + count);
+ }
}
if (code == Stuff_NoCode)
{
@@ -758,14 +837,21 @@ static __u8 *UnStuffData(__u8 *src, __u8 *end, __u8 *dst, __u32 dst_length)
* Convert a string to a Metricom Address.
*/
-static void string_to_radio_address(MetricomAddress *addr, __u8 *p)
+#define IS_RADIO_ADDRESS(p) ( \
+ isdigit((p)[0]) && isdigit((p)[1]) && isdigit((p)[2]) && isdigit((p)[3]) && \
+ (p)[4] == '-' && \
+ isdigit((p)[5]) && isdigit((p)[6]) && isdigit((p)[7]) && isdigit((p)[8]) )
+
+static int string_to_radio_address(MetricomAddress *addr, __u8 *p)
{
+ if (!IS_RADIO_ADDRESS(p)) return(1);
addr->c[0] = 0;
addr->c[1] = 0;
addr->c[2] = READHEX(p[0]) << 4 | READHEX(p[1]);
addr->c[3] = READHEX(p[2]) << 4 | READHEX(p[3]);
addr->c[4] = READHEX(p[5]) << 4 | READHEX(p[6]);
addr->c[5] = READHEX(p[7]) << 4 | READHEX(p[8]);
+ return(0);
}
/*
@@ -780,19 +866,18 @@ static __u8 *radio_address_to_string(const MetricomAddress *addr, MetricomAddres
/*
* Note: Must make sure sx_size is big enough to receive a stuffed
- * MAX_STRIP_MTU packet. Additionally, we also want to ensure that it's
+ * MAX_RECV_MTU packet. Additionally, we also want to ensure that it's
* big enough to receive a large radio neighbour list (currently 4K).
*/
static int allocate_buffers(struct strip *strip_info)
{
struct device *dev = &strip_info->dev;
- int stuffedlen = STRIP_ENCAP_SIZE(dev->mtu);
- int sx_size = MAX(stuffedlen, 4096);
- int tx_size = stuffedlen + sizeof(TickleString2);
- __u8 *r = kmalloc(MAX_STRIP_MTU, GFP_ATOMIC);
- __u8 *s = kmalloc(sx_size, GFP_ATOMIC);
- __u8 *t = kmalloc(tx_size, GFP_ATOMIC);
+ int sx_size = MAX(STRIP_ENCAP_SIZE(MAX_RECV_MTU), 4096);
+ int tx_size = STRIP_ENCAP_SIZE(dev->mtu) + MaxCommandStringLength;
+ __u8 *r = kmalloc(MAX_RECV_MTU, GFP_ATOMIC);
+ __u8 *s = kmalloc(sx_size, GFP_ATOMIC);
+ __u8 *t = kmalloc(tx_size, GFP_ATOMIC);
if (r && s && t)
{
strip_info->rx_buff = r;
@@ -824,10 +909,10 @@ static void strip_changedmtu(struct strip *strip_info)
unsigned char *otbuff = strip_info->tx_buff;
InterruptStatus intstat;
- if (dev->mtu > MAX_STRIP_MTU)
+ if (dev->mtu > MAX_SEND_MTU)
{
printk(KERN_ERR "%s: MTU exceeds maximum allowable (%d), MTU change cancelled.\n",
- strip_info->dev.name, MAX_STRIP_MTU);
+ strip_info->dev.name, MAX_SEND_MTU);
dev->mtu = old_mtu;
return;
}
@@ -856,9 +941,8 @@ static void strip_changedmtu(struct strip *strip_info)
memcpy(strip_info->sx_buff, osbuff, strip_info->sx_count);
else
{
- strip_info->sx_count = 0;
+ strip_info->discard = strip_info->sx_count;
strip_info->rx_over_errors++;
- strip_info->discard = 1;
}
}
@@ -889,7 +973,7 @@ static void strip_unlock(struct strip *strip_info)
/*
* Set the time to go off in one second.
*/
- strip_info->idle_timer.expires = jiffies + HZ;
+ strip_info->idle_timer.expires = jiffies + 1*HZ;
add_timer(&strip_info->idle_timer);
if (!test_and_clear_bit(0, (void *)&strip_info->dev.tbusy))
printk(KERN_ERR "%s: trying to unlock already unlocked device!\n",
@@ -900,8 +984,6 @@ static void strip_unlock(struct strip *strip_info)
/************************************************************************/
/* Callback routines for exporting information through /proc */
-#if DO_PROC_NET_STRIP_STATUS | DO_PROC_NET_STRIP_TRACE
-
/*
* This function updates the total amount of data printed so far. It then
* determines if the amount of data printed into a buffer has reached the
@@ -912,29 +994,29 @@ static void strip_unlock(struct strip *strip_info)
*/
static int
shift_buffer(char *buffer, int requested_offset, int requested_len,
- int *total, int *slop, char **buf)
+ int *total, int *slop, char **buf)
{
int printed;
/* printk(KERN_DEBUG "shift: buffer: %d o: %d l: %d t: %d buf: %d\n",
- (int) buffer, requested_offset, requested_len, *total,
- (int) *buf); */
+ (int) buffer, requested_offset, requested_len, *total,
+ (int) *buf); */
printed = *buf - buffer;
if (*total + printed <= requested_offset) {
- *total += printed;
- *buf = buffer;
+ *total += printed;
+ *buf = buffer;
}
else {
- if (*total < requested_offset) {
- *slop = requested_offset - *total;
- }
- *total = requested_offset + printed - *slop;
+ if (*total < requested_offset) {
+ *slop = requested_offset - *total;
+ }
+ *total = requested_offset + printed - *slop;
}
if (*total > requested_offset + requested_len) {
- return 1;
+ return 1;
}
else {
- return 0;
+ return 0;
}
}
@@ -945,12 +1027,12 @@ shift_buffer(char *buffer, int requested_offset, int requested_len,
*/
static int
calc_start_len(char *buffer, char **start, int requested_offset,
- int requested_len, int total, char *buf)
+ int requested_len, int total, char *buf)
{
int return_len, buffer_len;
buffer_len = buf - buffer;
- if (buffer_len >= STRIP_PROC_BUFFER_SIZE - 1) {
+ if (buffer_len >= 4095) {
printk(KERN_ERR "STRIP: exceeded /proc buffer size\n");
}
@@ -960,20 +1042,16 @@ calc_start_len(char *buffer, char **start, int requested_offset,
*/
return_len = total - requested_offset;
if (return_len < 0) {
- return_len = 0;
+ return_len = 0;
}
*start = buf - return_len;
if (return_len > requested_len) {
- return_len = requested_len;
+ return_len = requested_len;
}
/* printk(KERN_DEBUG "return_len: %d\n", return_len); */
return return_len;
}
-#endif DO_PROC_NET_STRIP_STATUS | DO_PROC_NET_STRIP_TRACE
-
-#if DO_PROC_NET_STRIP_STATUS
-
/*
* If the time is in the near future, time_delta prints the number of
* seconds to go into the buffer and returns the address of the buffer.
@@ -991,537 +1069,171 @@ static char *time_delta(char buffer[], long time)
return(buffer);
}
-/*
- * This function prints radio status information into the specified
- * buffer.
- */
-static int
-sprintf_status_info(char *buffer, struct strip *strip_info)
-{
- char temp_buffer[32];
- MetricomAddressString addr_string;
- char *buf;
-
- buf = buffer;
- buf += sprintf(buf, "Interface name\t\t%s\n", strip_info->if_name);
- buf += sprintf(buf, " Radio working:\t\t%s\n",
- strip_info->working &&
- (long)jiffies - strip_info->watchdog_doreset < 0 ? "Yes" : "No");
- (void) radio_address_to_string((MetricomAddress *)
- &strip_info->dev.dev_addr,
- &addr_string);
- buf += sprintf(buf, " Device address:\t%s\n", addr_string.c);
- buf += sprintf(buf, " Firmware version:\t%s\n",
- !strip_info->working ? "Unknown" :
- !strip_info->structured_messages ? "Should be upgraded" :
- strip_info->firmware_version.c);
- buf += sprintf(buf, " Serial number:\t\t%s\n", strip_info->serial_number.c);
- buf += sprintf(buf, " Battery voltage:\t%s\n", strip_info->battery_voltage.c);
- buf += sprintf(buf, " Transmit queue (bytes):%d\n", strip_info->tx_left);
- buf += sprintf(buf, " Next watchdog probe:\t%s\n",
- time_delta(temp_buffer, strip_info->watchdog_doprobe));
- buf += sprintf(buf, " Next watchdog reset:\t%s\n",
- time_delta(temp_buffer, strip_info->watchdog_doreset));
- buf += sprintf(buf, " Next gratuitous ARP:\t%s\n",
- time_delta(temp_buffer, strip_info->gratuitous_arp));
- buf += sprintf(buf, " Next ARP interval:\t%ld seconds\n",
- JIFFIE_TO_SEC(strip_info->arp_interval));
- return buf - buffer;
-}
-
-static int
-sprintf_portables(char *buffer, struct strip *strip_info)
-{
-
- MetricomAddressString addr_string;
- MetricomNode *node;
- char *buf;
-
- buf = buffer;
- buf += sprintf(buf, " portables: name\t\tpoll_latency\tsignal strength\n");
- for (node = strip_info->neighbor_list; node != NULL;
- node = node->next) {
- if (!(node->type & NodeValid)) {
- break;
- }
- if (node->type & NodeHasWAN) {
- continue;
- }
- (void) radio_address_to_string(&node->addr, &addr_string);
- buf += sprintf(buf, " %s\t\t\t\t%d\t\t%d\n",
- addr_string.c, node->poll_latency, node->rssi);
- }
- return buf - buffer;
-}
-
-static int
-sprintf_poletops(char *buffer, struct strip *strip_info)
-{
- MetricomNode *node;
- char *buf;
-
- buf = buffer;
- buf += sprintf(buf, " poletops: GPS\t\t\tpoll_latency\tsignal strength\n");
- for (node = strip_info->neighbor_list;
- node != NULL; node = node->next) {
- if (!(node->type & NodeValid)) {
- break;
- }
- if (!(node->type & NodeHasWAN)) {
- continue;
- }
- buf += sprintf(buf, " %s\t\t\t%d\t\t%d\n",
- node->gl.s, node->poll_latency, node->rssi);
- }
- return buf - buffer;
-}
-
-/*
- * This function is exports status information from the STRIP driver through
- * the /proc file system. /proc filesystem should be fixed:
- * 1) slow (sprintfs here, a memory copy in the proc that calls this one)
- * 2) length of buffer not passed
- * 3) dummy isn't client data set when the callback was registered
- * 4) poorly documented (this function is called until the requested amount
- * of data is returned, buffer is only 4K long, dummy is the permissions
- * of the file (?), the proc_dir_entry passed to proc_net_register must
- * be kmalloc-ed)
- */
-
-static int
-strip_get_status_info(char *buffer, char **start, off_t requested_offset,
- int requested_len, int dummy)
+static int sprintf_neighbours(char *buffer, MetricomNodeTable *table, char *title)
{
- char *buf;
- int total = 0, slop = 0, len_exceeded;
- InterruptStatus i_status;
- struct strip *strip_info;
-
- buf = buffer;
- buf += sprintf(buf, "strip_version: %s\n", StripVersion);
-
- i_status = DisableInterrupts();
- strip_info = struct_strip_list;
- RestoreInterrupts(i_status);
-
- while (strip_info != NULL) {
- i_status = DisableInterrupts();
- buf += sprintf_status_info(buf, strip_info);
- RestoreInterrupts(i_status);
- len_exceeded = shift_buffer(buffer, requested_offset, requested_len,
- &total, &slop, &buf);
- if (len_exceeded) {
- goto done;
- }
- strip_info->neighbor_list_locked = TRUE;
- buf += sprintf_portables(buf, strip_info);
- strip_info->neighbor_list_locked = FALSE;
- len_exceeded = shift_buffer(buffer, requested_offset, requested_len,
- &total, &slop, &buf);
- if (len_exceeded) {
- goto done;
- }
- strip_info->neighbor_list_locked = TRUE;
- buf += sprintf_poletops(buf, strip_info);
- strip_info->neighbor_list_locked = FALSE;
- len_exceeded = shift_buffer(buffer, requested_offset, requested_len,
- &total, &slop, &buf);
- if (len_exceeded) {
- goto done;
- }
- strip_info = strip_info->next;
- }
-done:
- return calc_start_len(buffer, start, requested_offset, requested_len,
- total, buf);
+ /* We wrap this in a do/while loop, so if the table changes */
+ /* while we're reading it, we just go around and try again. */
+ struct timeval t;
+ char *ptr;
+ do
+ {
+ int i;
+ t = table->timestamp;
+ ptr = buffer;
+ if (table->num_nodes) ptr += sprintf(ptr, "\n %s\n", title);
+ for (i=0; i<table->num_nodes; i++)
+ {
+ InterruptStatus intstat = DisableInterrupts();
+ MetricomNode node = table->node[i];
+ RestoreInterrupts(intstat);
+ ptr += sprintf(ptr, " %s\n", node.c);
+ }
+ } while (table->timestamp.tv_sec != t.tv_sec || table->timestamp.tv_usec != t.tv_usec);
+ return ptr - buffer;
}
-#endif DO_PROC_NET_STRIP_STATUS
-
-#if DO_PROC_NET_STRIP_TRACE
-
/*
- * Convert an Ethernet protocol to a string
- * Returns the number of characters printed.
+ * This function prints radio status information into the specified buffer.
+ * I think the buffer size is 4K, so this routine should never print more
+ * than 4K of data into it. With the maximum of 32 portables and 32 poletops
+ * reported, the routine outputs 3107 bytes into the buffer.
*/
-
-static int protocol_to_string(int protocol, __u8 *p)
-{
- int printed;
-
- switch (protocol) {
- case ETH_P_IP:
- printed = sprintf(p, "IP");
- break;
- case ETH_P_ARP:
- printed = sprintf(p, "ARP");
- break;
- default:
- printed = sprintf(p, "%d", protocol);
- }
- return printed;
-}
-
static int
-sprintf_log_entry(char *buffer, struct strip *strip_info, int packet_index)
+sprintf_status_info(char *buffer, struct strip *strip_info)
{
- StripLog *entry;
+ char temp[32];
+ char *p = buffer;
MetricomAddressString addr_string;
- __u8 sig_buf[24], *s;
- char *buf, proto_buf[10];
-
- entry = &strip_info->packetLog[packet_index];
- if (!entry->valid) {
- return 0;
- }
- buf = buffer;
- buf += sprintf(buf, "%-4s %s %7lu ", strip_info->if_name,
- ENTRY_TYPE_TO_STRING(entry->entry_type), entry->seqNum);
- (void) protocol_to_string(entry->packet_type, proto_buf);
- buf += sprintf(buf, "%-4s", proto_buf);
- s = entry->sig.print_sig;
- sprintf(sig_buf, "%d.%d.%d.%d.%d.%d", s[0], s[1], s[2], s[3], s[4], s[5]);
- buf += sprintf(buf, "%-24s", sig_buf);
- (void) radio_address_to_string((MetricomAddress *) &entry->src,
- &addr_string);
- buf += sprintf(buf, "%-10s", addr_string.c);
- (void) radio_address_to_string((MetricomAddress *) &entry->dest,
- &addr_string);
- buf += sprintf(buf, "%-10s", addr_string.c);
- buf += sprintf(buf, "%8d %6d %5lu %6lu %5lu\n", entry->timeStamp.tv_sec,
- entry->timeStamp.tv_usec, entry->rawSize,
- entry->stripSize, entry->slipSize);
- return buf - buffer;
-}
-
-/*
- * This function exports trace information from the STRIP driver through the
- * /proc file system.
- */
-static int
-strip_get_trace_info(char *buffer, char **start, off_t requested_offset,
- int requested_len, int dummy)
-{
- char *buf;
- int len_exceeded, total = 0, slop = 0, packet_index, oldest;
- InterruptStatus i_status;
- struct strip *strip_info;
-
- buf = buffer;
- buf += sprintf(buf, "if s/r seqnum t signature ");
- buf += sprintf(buf,
- "src dest sec usec raw strip slip\n");
-
- i_status = DisableInterrupts();
- strip_info = struct_strip_list;
- oldest = strip_info->next_entry;
- RestoreInterrupts(i_status);
-
- /*
- * If we disable interrupts for this entire loop,
- * characters from the serial port could be lost,
- * so we only disable interrupts when accessing
- * a log entry. If more than STRIP_LOG_INT_SIZE
- * packets are logged before the first entry is
- * printed, then some of the entries could be
- * printed out of order.
- */
- while (strip_info != NULL) {
- for (packet_index = oldest + STRIP_LOG_INT_SIZE;
- packet_index != oldest;
- packet_index = (packet_index + 1) %
- ELEMENTS_OF(strip_info->packetLog)) {
- i_status = DisableInterrupts();
- buf += sprintf_log_entry(buf, strip_info, packet_index);
- RestoreInterrupts(i_status);
- len_exceeded = shift_buffer(buffer, requested_offset,
- requested_len, &total, &slop, &buf);
- if (len_exceeded) {
- goto done;
- }
- }
- strip_info = strip_info->next;
- }
-done:
- return calc_start_len(buffer, start, requested_offset, requested_len,
- total, buf);
-}
+ /* First, we must copy all of our data to a safe place, */
+ /* in case a serial interrupt comes in and changes it. */
+ InterruptStatus intstat = DisableInterrupts();
+ int tx_left = strip_info->tx_left;
+ unsigned long rx_average_pps = strip_info->rx_average_pps;
+ unsigned long tx_average_pps = strip_info->tx_average_pps;
+ unsigned long sx_average_pps = strip_info->sx_average_pps;
+ int working = strip_info->working;
+ int firmware_level = strip_info->firmware_level;
+ long watchdog_doprobe = strip_info->watchdog_doprobe;
+ long watchdog_doreset = strip_info->watchdog_doreset;
+ long gratuitous_arp = strip_info->gratuitous_arp;
+ long arp_interval = strip_info->arp_interval;
+ FirmwareVersion firmware_version = strip_info->firmware_version;
+ SerialNumber serial_number = strip_info->serial_number;
+ BatteryVoltage battery_voltage = strip_info->battery_voltage;
+ char8 if_name = strip_info->if_name;
+ MetricomAddress true_dev_addr = strip_info->true_dev_addr;
+ MetricomAddress dev_dev_addr = *(MetricomAddress*)strip_info->dev.dev_addr;
+ int manual_dev_addr = strip_info->manual_dev_addr;
+#ifdef EXT_COUNTERS
+ unsigned long rx_bytes = strip_info->rx_bytes;
+ unsigned long tx_bytes = strip_info->tx_bytes;
+ unsigned long rx_rbytes = strip_info->rx_rbytes;
+ unsigned long tx_rbytes = strip_info->tx_rbytes;
+ unsigned long rx_sbytes = strip_info->rx_sbytes;
+ unsigned long tx_sbytes = strip_info->tx_sbytes;
+ unsigned long rx_ebytes = strip_info->rx_ebytes;
+ unsigned long tx_ebytes = strip_info->tx_ebytes;
+#endif
+ RestoreInterrupts(intstat);
-static int slip_len(unsigned char *data, int len)
-{
- static const unsigned char SLIP_END=0300; /* indicates end of SLIP frame */
- static const unsigned char SLIP_ESC=0333; /* indicates SLIP byte stuffing */
- int count = len;
- while (--len >= 0)
+ p += sprintf(p, "\nInterface name\t\t%s\n", if_name.c);
+ p += sprintf(p, " Radio working:\t\t%s\n", working ? "Yes" : "No");
+ radio_address_to_string(&true_dev_addr, &addr_string);
+ p += sprintf(p, " Radio address:\t\t%s\n", addr_string.c);
+ if (manual_dev_addr)
{
- if (*data == SLIP_END || *data == SLIP_ESC) count++;
- data++;
- }
- return(count);
-}
-
-/* Copied from kernel/sched.c */
-static void jiffiestotimeval(unsigned long jiffies, struct timeval *value)
-{
- value->tv_usec = (jiffies % HZ) * (1000000.0 / HZ);
- value->tv_sec = jiffies / HZ;
- return;
-}
-
-/*
- * This function logs a packet.
- * A pointer to the packet itself is passed so that some of the data can be
- * used to compute a signature. The pointer should point the the
- * part of the packet following the STRIP_header.
- */
-
-static void packet_log(struct strip *strip_info, __u8 *packet,
- LogEntry entry_type, STRIP_Header *hdr,
- int raw_size, int strip_size, int slip_size)
-{
- StripLog *entry;
- struct iphdr *iphdr;
- struct arphdr *arphdr;
-
- entry = &strip_info->packetLog[strip_info->next_entry];
- if (entry_type == EntrySend) {
- entry->seqNum = strip_info->num_sent++;
+ radio_address_to_string(&dev_dev_addr, &addr_string);
+ p += sprintf(p, " Device address:\t%s\n", addr_string.c);
+ }
+ p += sprintf(p, " Firmware version:\t%s", !working ? "Unknown" :
+ !firmware_level ? "Should be upgraded" :
+ firmware_version.c);
+ if (firmware_level >= ChecksummedMessages) p += sprintf(p, " (Checksums Enabled)");
+ p += sprintf(p, "\n");
+ p += sprintf(p, " Serial number:\t\t%s\n", serial_number.c);
+ p += sprintf(p, " Battery voltage:\t%s\n", battery_voltage.c);
+ p += sprintf(p, " Transmit queue (bytes):%d\n", tx_left);
+ p += sprintf(p, " Receive packet rate: %ld packets per second\n", rx_average_pps / 8);
+ p += sprintf(p, " Transmit packet rate: %ld packets per second\n", tx_average_pps / 8);
+ p += sprintf(p, " Sent packet rate: %ld packets per second\n", sx_average_pps / 8);
+ p += sprintf(p, " Next watchdog probe:\t%s\n", time_delta(temp, watchdog_doprobe));
+ p += sprintf(p, " Next watchdog reset:\t%s\n", time_delta(temp, watchdog_doreset));
+ p += sprintf(p, " Next gratuitous ARP:\t");
+
+ if (!memcmp(strip_info->dev.dev_addr, zero_address.c, sizeof(zero_address)))
+ p += sprintf(p, "Disabled\n");
+ else
+ {
+ p += sprintf(p, "%s\n", time_delta(temp, gratuitous_arp));
+ p += sprintf(p, " Next ARP interval:\t%ld seconds\n", JIFFIE_TO_SEC(arp_interval));
}
- else {
- entry->seqNum = strip_info->num_received++;
- }
- entry->entry_type = entry_type;
- entry->packet_type = ntohs(hdr->protocol);
- switch (entry->packet_type) {
- case ETH_P_IP:
- /*
- * The signature for IP is the sender's ip address and
- * the identification field.
- */
- iphdr = (struct iphdr *) packet;
- entry->sig.ip_sig.id = iphdr->id;
- entry->sig.ip_sig.src.l = iphdr->saddr;
- break;
- case ETH_P_ARP:
- /*
- * The signature for ARP is the sender's ip address and
- * the operation.
- */
- arphdr = (struct arphdr *) packet;
- entry->sig.arp_sig.op = arphdr->ar_op;
- memcpy(&entry->sig.arp_sig.src.l, packet + 8 + arphdr->ar_hln,
- sizeof(entry->sig.arp_sig.src.l));
- entry->sig.arp_sig.src.l = entry->sig.arp_sig.src.l;
- break;
- default:
- printk(KERN_DEBUG "STRIP: packet_log: unknown packet type: %d\n",
- entry->packet_type);
- break;
- }
- memcpy(&entry->src, &hdr->src_addr, sizeof(MetricomAddress));
- memcpy(&entry->dest, &hdr->dst_addr, sizeof(MetricomAddress));
-
- jiffiestotimeval(jiffies, &(entry->timeStamp));
- entry->rawSize = raw_size;
- entry->stripSize = strip_size;
- entry->slipSize = slip_size;
- entry->valid = 1;
-
- strip_info->next_entry = (strip_info->next_entry + 1) %
- ELEMENTS_OF(strip_info->packetLog);
-}
-#endif DO_PROC_NET_STRIP_TRACE
+ if (working)
+ {
+#ifdef EXT_COUNTERS
+ p += sprintf(p, "\n");
+ p += sprintf(p, " Total bytes: \trx:\t%lu\ttx:\t%lu\n", rx_bytes, tx_bytes);
+ p += sprintf(p, " thru radio: \trx:\t%lu\ttx:\t%lu\n", rx_rbytes, tx_rbytes);
+ p += sprintf(p, " thru serial port: \trx:\t%lu\ttx:\t%lu\n", rx_sbytes, tx_sbytes);
+ p += sprintf(p, " Total stat/err bytes:\trx:\t%lu\ttx:\t%lu\n", rx_ebytes, tx_ebytes);
+#endif
+ p += sprintf_neighbours(p, &strip_info->poletops, "Poletops:");
+ p += sprintf_neighbours(p, &strip_info->portables, "Portables:");
+ }
-/*
- * This function parses the response to the ATS300? command,
- * extracting the radio version and serial number.
- */
-static void get_radio_version(struct strip *strip_info, __u8 *ptr, __u8 *end)
-{
- __u8 *p, *value_begin, *value_end;
- int len;
-
- /* Determine the beginning of the second line of the payload */
- p = ptr;
- while (p < end && *p != 10) p++;
- if (p >= end) return;
- p++;
- value_begin = p;
-
- /* Determine the end of line */
- while (p < end && *p != 10) p++;
- if (p >= end) return;
- value_end = p;
- p++;
-
- len = value_end - value_begin;
- len = MIN(len, sizeof(MetricomFirmwareVersion) - 1);
- sprintf(strip_info->firmware_version.c, "%.*s", len, value_begin);
-
- /* Look for the first colon */
- while (p < end && *p != ':') p++;
- if (p >= end) return;
- /* Skip over the space */
- p += 2;
- len = sizeof(MetricomSerialNumber) - 1;
- if (p + len <= end) {
- sprintf(strip_info->serial_number.c, "%.*s", len, p);
- }
- else {
- printk(KERN_ERR "STRIP: radio serial number shorter (%d) than expected (%d)\n",
- end - p, len);
- }
+ return p - buffer;
}
/*
- * This function parses the response to the ATS325? command,
- * extracting the radio battery voltage.
+ * This function is exports status information from the STRIP driver through
+ * the /proc file system.
*/
-static void get_radio_voltage(struct strip *strip_info, __u8 *ptr, __u8 *end)
-{
- int len;
- len = sizeof(MetricomBatteryVoltage) - 1;
- if (ptr + len <= end) {
- sprintf(strip_info->battery_voltage.c, "%.*s", len, ptr);
- }
- else {
- printk(KERN_ERR "STRIP: radio voltage string shorter (%d) than expected (%d)\n",
- end - ptr, len);
- }
-}
-
-/*
- * This function parses the response to the AT~I2 command,
- * which gives the names of the radio's nearest neighbors.
- * It relies on the format of the response.
- */
-static void get_radio_neighbors(struct strip *strip_info, __u8 *ptr, __u8 *end)
+static int get_status_info(char *buffer, char **start, off_t req_offset, int req_len, int dummy)
{
- __u8 *p, *line_begin;
- int num_nodes_reported, num_nodes_counted;
- MetricomNode *node, *last;
-
- /* Check if someone is reading the list */
- if (strip_info->neighbor_list_locked) {
- return;
- }
-
- /* Determine the number of Nodes */
- p = ptr;
- num_nodes_reported = simple_strtoul(p, NULL, 10);
- /* printk(KERN_DEBUG "num_nodes: %d\n", num_nodes_reported); */
-
- /* Determine the beginning of the next line */
- while (p < end && *p != 10) p++;
- if (p >= end) return;
- p++;
+ int total = 0, slop = 0;
+ struct strip *strip_info = struct_strip_list;
+ char *buf = buffer;
- /*
- * The node list should never be empty because we allocate one empty
- * node when the strip_info is allocated. The nodes which were allocated
- * when the number of neighbors was high but are no longer needed because
- * there aren't as many neighbors any more are marked invalid. Invalid nodes
- * are kept at the end of the list.
- */
- node = strip_info->neighbor_list;
- last = node;
- if (node == NULL) {
- DumpData("Neighbor list is NULL:", strip_info, p, end);
- return;
- }
- line_begin = p;
- num_nodes_counted = 0;
- while (line_begin < end) {
- /* Check to see if the format is what we expect. */
- if ((line_begin + STRIP_NODE_LEN) > end) {
- printk(KERN_ERR "STRIP: radio neighbor node string shorter (%d) than expected (%d)\n",
- end - line_begin, STRIP_NODE_LEN);
- break;
- }
-
- /* Get a node */
- if (node == NULL) {
- node = kmalloc(sizeof(MetricomNode), GFP_ATOMIC);
- node->next = NULL;
- }
- node->type = NodeValid;
-
- /* Fill the node in */
-
- /* Determine if it has a GPS location and fill it in if it does. */
- p = line_begin;
- /* printk(KERN_DEBUG "node: %64s\n", p); */
- if (p[0] != STRIP_PORTABLE_CHAR) {
- node->type |= NodeHasWAN;
- sprintf(node->gl.s, "%.*s", (int) sizeof(GeographicLocation) - 1, p);
- }
-
- /* Determine if it is a router */
- p = line_begin + 18;
- if (p[0] == STRIP_ROUTER_CHAR) {
- node->type |= NodeIsRouter;
- }
-
- /* Could be a radio address or some weird poletop address. */
- p = line_begin + 20;
- /* printk(KERN_DEBUG "before addr: %6s\n", p); */
- string_to_radio_address(&node->addr, p);
- /* radio_address_to_string(&node->addr, addr_string);
- printk(KERN_DEBUG "after addr: %s\n", addr_string); */
-
- if (IS_RADIO_ADDRESS(p)) {
- string_to_radio_address(&node->addr, p);
- }
- else {
- memset(&node->addr, 0, sizeof(MetricomAddress));
- }
-
- /* Get the poll latency. %$#!@ simple_strtoul can't skip white space */
- p = line_begin + 41;
- while (isspace(*p) && (p < end)) {
- p++;
- }
- node->poll_latency = simple_strtoul(p, NULL, 10);
-
- /* Get the signal strength. simple_strtoul doesn't do minus signs */
- p = line_begin + 60;
- node->rssi = -simple_strtoul(p, NULL, 10);
-
- if (last != node) {
- last->next = node;
- last = node;
- }
- node = node->next;
- line_begin += STRIP_NODE_LEN;
- num_nodes_counted++;
- }
-
- /* invalidate all remaining nodes */
- for (;node != NULL; node = node->next) {
- node->type &= ~NodeValid;
- }
+ buf += sprintf(buf, "strip_version: %s\n", StripVersion);
+ if (shift_buffer(buffer, req_offset, req_len, &total, &slop, &buf)) goto exit;
- /*
- * If the number of nodes reported is different
- * from the number counted, might need to up the number
- * requested.
- */
- if (num_nodes_reported != num_nodes_counted) {
- printk(KERN_DEBUG "nodes reported: %d \tnodes counted: %d\n",
- num_nodes_reported, num_nodes_counted);
- }
+ while (strip_info != NULL)
+ {
+ buf += sprintf_status_info(buf, strip_info);
+ if (shift_buffer(buffer, req_offset, req_len, &total, &slop, &buf)) break;
+ strip_info = strip_info->next;
+ }
+ exit:
+ return(calc_start_len(buffer, start, req_offset, req_len, total, buf));
}
+static const char proc_strip_status_name[] = "strip";
+static struct proc_dir_entry proc_strip_get_status_info =
+{
+ PROC_NET_STRIP_STATUS, /* unsigned short low_ino */
+ sizeof(proc_strip_status_name)-1, /* unsigned short namelen */
+ proc_strip_status_name, /* const char *name */
+ S_IFREG | S_IRUGO, /* mode_t mode */
+ 1, /* nlink_t nlink */
+ 0, 0, 0, /* uid_t uid, gid_t gid, unsigned long size */
+ &proc_net_inode_operations, /* struct inode_operations * ops */
+ &get_status_info, /* int (*get_info)(...) */
+ NULL, /* void (*fill_inode)(struct inode *); */
+ NULL, NULL, NULL, /* struct proc_dir_entry *next, *parent, *subdir; */
+ NULL /* void *data; */
+};
+
/************************************************************************/
/* Sending routines */
+#define InitString "ate0q1dt**starmode"
+
static void ResetRadio(struct strip *strip_info)
-{
- static const char InitString[] = "\rat\r\rate0q1dt**starmode\r\r**";
+{
+ static const char s[] = "\r" InitString "\r**";
/* If the radio isn't working anymore, we should clear the old status information. */
if (strip_info->working)
@@ -1530,14 +1242,32 @@ static void ResetRadio(struct strip *strip_info)
strip_info->firmware_version.c[0] = '\0';
strip_info->serial_number.c[0] = '\0';
strip_info->battery_voltage.c[0] = '\0';
+ strip_info->portables.num_nodes = 0;
+ do_gettimeofday(&strip_info->portables.timestamp);
+ strip_info->poletops.num_nodes = 0;
+ do_gettimeofday(&strip_info->poletops.timestamp);
}
+
+ strip_info->pps_timer = jiffies;
+ strip_info->rx_pps_count = 0;
+ strip_info->tx_pps_count = 0;
+ strip_info->sx_pps_count = 0;
+ strip_info->rx_average_pps = 0;
+ strip_info->tx_average_pps = 0;
+ strip_info->sx_average_pps = 0;
+
/* Mark radio address as unknown */
- *(MetricomAddress*)&strip_info->dev.dev_addr = zero_address;
+ *(MetricomAddress*)&strip_info->true_dev_addr = zero_address;
+ if (!strip_info->manual_dev_addr) *(MetricomAddress*)strip_info->dev.dev_addr = zero_address;
strip_info->working = FALSE;
- strip_info->structured_messages = FALSE;
+ strip_info->firmware_level = NoStructure;
+ strip_info->next_command = CompatibilityCommand;
strip_info->watchdog_doprobe = jiffies + 10 * HZ;
strip_info->watchdog_doreset = jiffies + 1 * HZ;
- strip_info->tty->driver.write(strip_info->tty, 0, (char *)InitString, sizeof(InitString)-1);
+ strip_info->tty->driver.write(strip_info->tty, 0, (char *)s, sizeof(s)-1);
+#ifdef EXT_COUNTERS
+ strip_info->tx_ebytes += sizeof(s) - 1;
+#endif
}
/*
@@ -1573,6 +1303,9 @@ static void strip_write_some_more(struct tty_struct *tty)
int num_written = tty->driver.write(tty, 0, strip_info->tx_head, strip_info->tx_left);
strip_info->tx_left -= num_written;
strip_info->tx_head += num_written;
+#ifdef EXT_COUNTERS
+ strip_info->tx_sbytes += num_written;
+#endif
RestoreInterrupts(intstat);
}
else /* Else start transmission of another packet */
@@ -1583,12 +1316,21 @@ static void strip_write_some_more(struct tty_struct *tty)
}
}
-static unsigned char *strip_make_packet(unsigned char *ptr, struct strip *strip_info, struct sk_buff *skb)
+static __u8 *add_checksum(__u8 *buffer, __u8 *end)
{
-#if DO_PROC_NET_STRIP_TRACE
- unsigned char *start_ptr;
-#endif DO_PROC_NET_STRIP_TRACE
+ __u16 sum = 0;
+ __u8 *p = buffer;
+ while (p < end) sum += *p++;
+ end[3] = hextable[sum & 0xF]; sum >>= 4;
+ end[2] = hextable[sum & 0xF]; sum >>= 4;
+ end[1] = hextable[sum & 0xF]; sum >>= 4;
+ end[0] = hextable[sum & 0xF];
+ return(end+4);
+}
+static unsigned char *strip_make_packet(unsigned char *buffer, struct strip *strip_info, struct sk_buff *skb)
+{
+ __u8 *ptr = buffer;
__u8 *stuffstate = NULL;
STRIP_Header *header = (STRIP_Header *)skb->data;
MetricomAddress haddr = header->dst_addr;
@@ -1603,7 +1345,6 @@ static unsigned char *strip_make_packet(unsigned char *ptr, struct strip *strip_
{
printk(KERN_ERR "%s: strip_make_packet: Unknown packet type 0x%04X\n",
strip_info->dev.name, ntohs(header->protocol));
- strip_info->tx_dropped++;
return(NULL);
}
@@ -1611,7 +1352,6 @@ static unsigned char *strip_make_packet(unsigned char *ptr, struct strip *strip_
{
printk(KERN_ERR "%s: Dropping oversized transmit packet: %d bytes\n",
strip_info->dev.name, len);
- strip_info->tx_dropped++;
return(NULL);
}
@@ -1629,6 +1369,12 @@ static unsigned char *strip_make_packet(unsigned char *ptr, struct strip *strip_
}
}
+ /*
+ * If we're sending to ourselves, discard the packet.
+ * (Metricom radios choke if they try to send a packet to their own address.)
+ */
+ if (!memcmp(haddr.c, strip_info->true_dev_addr.c, sizeof(haddr)))
+ return(NULL);
*ptr++ = '*';
*ptr++ = hextable[haddr.c[2] >> 4];
*ptr++ = hextable[haddr.c[2] & 0xF];
@@ -1645,17 +1391,9 @@ static unsigned char *strip_make_packet(unsigned char *ptr, struct strip *strip_
*ptr++ = key.c[2];
*ptr++ = key.c[3];
-#if DO_PROC_NET_STRIP_TRACE
- start_ptr = ptr;
-#endif DO_PROC_NET_STRIP_TRACE
-
ptr = StuffData(skb->data + sizeof(STRIP_Header), len, ptr, &stuffstate);
-#if DO_PROC_NET_STRIP_TRACE
- packet_log(strip_info, skb->data + sizeof(STRIP_Header), EntrySend,
- header, len, ptr-start_ptr,
- slip_len(skb->data + sizeof(STRIP_Header), len));
-#endif DO_PROC_NET_STRIP_TRACE
+ if (strip_info->firmware_level >= ChecksummedMessages) ptr = add_checksum(buffer+1, ptr);
*ptr++ = 0x0D;
return(ptr);
@@ -1664,57 +1402,108 @@ static unsigned char *strip_make_packet(unsigned char *ptr, struct strip *strip_
static void strip_send(struct strip *strip_info, struct sk_buff *skb)
{
unsigned char *ptr = strip_info->tx_buff;
+ int doreset = (long)jiffies - strip_info->watchdog_doreset >= 0;
+ int doprobe = (long)jiffies - strip_info->watchdog_doprobe >= 0 && !doreset;
- /* If we have a packet, encapsulate it and put it in the buffer */
+ /*
+ * 1. If we have a packet, encapsulate it and put it in the buffer
+ */
if (skb)
{
- ptr = strip_make_packet(ptr, strip_info, skb);
- /* If error, unlock and return */
- if (!ptr) { strip_unlock(strip_info); return; }
- strip_info->tx_packets++; /* Count another successful packet */
- /*DumpData("Sending:", strip_info, strip_info->tx_buff, ptr);*/
- /*HexDump("Sending", strip_info, strip_info->tx_buff, ptr);*/
- }
-
- /* Set up the strip_info ready to send the data */
- strip_info->tx_head = strip_info->tx_buff;
- strip_info->tx_left = ptr - strip_info->tx_buff;
- strip_info->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
-
- /* If watchdog has expired, reset the radio */
- if ((long)jiffies - strip_info->watchdog_doreset >= 0)
- {
- ResetRadio(strip_info);
- return;
- /* Note: if there's a packet to send, strip_write_some_more
- will do it after the reset has finished */
+ char *newptr = strip_make_packet(ptr, strip_info, skb);
+ strip_info->tx_pps_count++;
+ if (!newptr) strip_info->tx_dropped++;
+ else
+ {
+ ptr = newptr;
+ strip_info->sx_pps_count++;
+ strip_info->tx_packets++; /* Count another successful packet */
+#ifdef EXT_COUNTERS
+ strip_info->tx_bytes += skb->len;
+ strip_info->tx_rbytes += ptr - strip_info->tx_buff;
+#endif
+ /*DumpData("Sending:", strip_info, strip_info->tx_buff, ptr);*/
+ /*HexDump("Sending", strip_info, strip_info->tx_buff, ptr);*/
+ }
}
- /* No reset.
- * If it is time for another tickle, tack it on the end of the packet
+ /*
+ * 2. If it is time for another tickle, tack it on, after the packet
*/
- if ((long)jiffies - strip_info->watchdog_doprobe >= 0)
+ if (doprobe)
{
- /* Send tickle to make radio protest */
- /*printk(KERN_INFO "%s: Routine radio test.\n", strip_info->dev.name);*/
- const char *TickleString = TickleString1;
- int length = sizeof(TickleString1)-1;
- if (strip_info->structured_messages)
+ StringDescriptor ts = CommandString[strip_info->next_command];
+#if TICKLE_TIMERS
{
- TickleString = TickleString2;
- length = sizeof(TickleString2)-1;
+ struct timeval tv;
+ do_gettimeofday(&tv);
+ printk(KERN_INFO "**** Sending tickle string %d at %02d.%06d\n",
+ strip_info->next_command, tv.tv_sec % 100, tv.tv_usec);
}
- memcpy(ptr, TickleString, length);
- strip_info->tx_left += length;
+#endif
+ if (ptr == strip_info->tx_buff) *ptr++ = 0x0D;
+
+ *ptr++ = '*'; /* First send "**" to provoke an error message */
+ *ptr++ = '*';
+
+ /* Then add the command */
+ memcpy(ptr, ts.string, ts.length);
+
+ /* Add a checksum ? */
+ if (strip_info->firmware_level < ChecksummedMessages) ptr += ts.length;
+ else ptr = add_checksum(ptr, ptr + ts.length);
+
+ *ptr++ = 0x0D; /* Terminate the command with a <CR> */
+
+ /* Cycle to next periodic command? */
+ if (strip_info->firmware_level >= StructuredMessages)
+ if (++strip_info->next_command >= ELEMENTS_OF(CommandString))
+ strip_info->next_command = 0;
+#ifdef EXT_COUNTERS
+ strip_info->tx_ebytes += ts.length;
+#endif
strip_info->watchdog_doprobe = jiffies + 10 * HZ;
strip_info->watchdog_doreset = jiffies + 1 * HZ;
+ /*printk(KERN_INFO "%s: Routine radio test.\n", strip_info->dev.name);*/
}
/*
- * If it is time for a periodic ARP, queue one up to be sent
+ * 3. Set up the strip_info ready to send the data (if any).
+ */
+ strip_info->tx_head = strip_info->tx_buff;
+ strip_info->tx_left = ptr - strip_info->tx_buff;
+ strip_info->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+
+ /*
+ * 4. Debugging check to make sure we're not overflowing the buffer.
+ */
+ if (strip_info->tx_size - strip_info->tx_left < 20)
+ printk(KERN_ERR "%s: Sending%5d bytes;%5d bytes free.\n", strip_info->dev.name,
+ strip_info->tx_left, strip_info->tx_size - strip_info->tx_left);
+
+ /*
+ * 5. If watchdog has expired, reset the radio. Note: if there's data waiting in
+ * the buffer, strip_write_some_more will send it after the reset has finished
+ */
+ if (doreset) { ResetRadio(strip_info); return; }
+
+ /*
+ * 6. If it is time for a periodic ARP, queue one up to be sent.
+ * We only do this if:
+ * 1. The radio is working
+ * 2. It's time to send another periodic ARP
+ * 3. We really know what our address is (and it is not manually set to zero)
+ * 4. We have a designated broadcast address configured
+ * If we queue up an ARP packet when we don't have a designated broadcast
+ * address configured, then the packet will just have to be discarded in
+ * strip_make_packet. This is not fatal, but it causes misleading information
+ * to be displayed in tcpdump. tcpdump will report that periodic APRs are
+ * being sent, when in fact they are not, because they are all being dropped
+ * in the strip_make_packet routine.
*/
if (strip_info->working && (long)jiffies - strip_info->gratuitous_arp >= 0 &&
- memcmp(strip_info->dev.dev_addr, zero_address.c, sizeof(zero_address)))
+ memcmp(strip_info->dev.dev_addr, zero_address.c, sizeof(zero_address)) &&
+ *strip_info->dev.broadcast!=0xFF)
{
/*printk(KERN_INFO "%s: Sending gratuitous ARP with interval %ld\n",
strip_info->dev.name, strip_info->arp_interval / HZ);*/
@@ -1722,16 +1511,18 @@ static void strip_send(struct strip *strip_info, struct sk_buff *skb)
strip_info->arp_interval *= 2;
if (strip_info->arp_interval > MaxARPInterval)
strip_info->arp_interval = MaxARPInterval;
- arp_send(ARPOP_REPLY, ETH_P_ARP, strip_info->dev.pa_addr,
- &strip_info->dev, strip_info->dev.pa_addr,
- NULL, strip_info->dev.dev_addr, NULL);
+ arp_send(ARPOP_REPLY, ETH_P_ARP,
+ strip_info->dev.pa_addr, /* Target address of ARP packet is our address */
+ &strip_info->dev, /* Device to send packet on */
+ strip_info->dev.pa_addr, /* Source IP address this ARP packet comes from */
+ NULL, /* Destination HW address is NULL (broadcast it) */
+ strip_info->dev.dev_addr, /* Source HW address is our HW address */
+ strip_info->dev.dev_addr); /* Target HW address is our HW address (redundant) */
}
- if (strip_info->tx_size - strip_info->tx_left < 20)
- printk(KERN_ERR "%s: Sending%5d bytes;%5d bytes free.\n", strip_info->dev.name,
- strip_info->tx_left, strip_info->tx_size - strip_info->tx_left);
-
- /* All ready. Start the transmission */
+ /*
+ * 7. All ready. Start the transmission
+ */
strip_write_some_more(strip_info->tty);
}
@@ -1752,6 +1543,33 @@ static int strip_xmit(struct sk_buff *skb, struct device *dev)
if (strip_info->mtu != strip_info->dev.mtu)
strip_changedmtu(strip_info);
+ if (jiffies - strip_info->pps_timer > HZ)
+ {
+ unsigned long t = jiffies - strip_info->pps_timer;
+ unsigned long rx_pps_count = (strip_info->rx_pps_count * HZ * 8 + t/2) / t;
+ unsigned long tx_pps_count = (strip_info->tx_pps_count * HZ * 8 + t/2) / t;
+ unsigned long sx_pps_count = (strip_info->sx_pps_count * HZ * 8 + t/2) / t;
+
+ strip_info->pps_timer = jiffies;
+ strip_info->rx_pps_count = 0;
+ strip_info->tx_pps_count = 0;
+ strip_info->sx_pps_count = 0;
+
+ strip_info->rx_average_pps = (strip_info->rx_average_pps + rx_pps_count + 1) / 2;
+ strip_info->tx_average_pps = (strip_info->tx_average_pps + tx_pps_count + 1) / 2;
+ strip_info->sx_average_pps = (strip_info->sx_average_pps + sx_pps_count + 1) / 2;
+
+ if (rx_pps_count / 8 >= 10)
+ printk(KERN_INFO "%s: WARNING: Receiving %ld packets per second.\n",
+ strip_info->dev.name, rx_pps_count / 8);
+ if (tx_pps_count / 8 >= 10)
+ printk(KERN_INFO "%s: WARNING: Tx %ld packets per second.\n",
+ strip_info->dev.name, tx_pps_count / 8);
+ if (sx_pps_count / 8 >= 10)
+ printk(KERN_INFO "%s: WARNING: Sending %ld packets per second.\n",
+ strip_info->dev.name, sx_pps_count / 8);
+ }
+
strip_send(strip_info, skb);
if (skb) dev_kfree_skb(skb, FREE_WRITE);
@@ -1759,6 +1577,17 @@ static int strip_xmit(struct sk_buff *skb, struct device *dev)
}
/*
+ * IdleTask periodically calls strip_xmit, so even when we have no IP packets
+ * to send for an extended period of time, the watchdog processing still gets
+ * done to ensure that the radio stays in Starmode
+ */
+
+static void strip_IdleTask(unsigned long parameter)
+{
+ strip_xmit(NULL, (struct device *)parameter);
+}
+
+/*
* Create the MAC header for an arbitrary protocol layer
*
* saddr!=NULL means use this specific address (n/a for Metricom)
@@ -1772,19 +1601,20 @@ static int strip_xmit(struct sk_buff *skb, struct device *dev)
static int strip_header(struct sk_buff *skb, struct device *dev,
unsigned short type, void *daddr, void *saddr, unsigned len)
{
+ struct strip *strip_info = (struct strip *)(dev->priv);
STRIP_Header *header = (STRIP_Header *)skb_push(skb, sizeof(STRIP_Header));
/*printk(KERN_INFO "%s: strip_header 0x%04X %s\n", dev->name, type,
type == ETH_P_IP ? "IP" : type == ETH_P_ARP ? "ARP" : "");*/
- memcpy(header->src_addr.c, dev->dev_addr, dev->addr_len);
+ header->src_addr = strip_info->true_dev_addr;
header->protocol = htons(type);
/*HexDump("strip_header", (struct strip *)(dev->priv), skb->data, skb->data + skb->len);*/
if (!daddr) return(-dev->hard_header_len);
- memcpy(header->dst_addr.c, daddr, dev->addr_len);
+ header->dst_addr = *(MetricomAddress*)daddr;
return(dev->hard_header_len);
}
@@ -1811,43 +1641,130 @@ static int strip_rebuild_header(struct sk_buff *skb)
#endif
}
+
+/************************************************************************/
+/* Receiving routines */
+
+static int strip_receive_room(struct tty_struct *tty)
+{
+ return 0x10000; /* We can handle an infinite amount of data. :-) */
+}
+
/*
- * IdleTask periodically calls strip_xmit, so even when we have no IP packets
- * to send for an extended period of time, the watchdog processing still gets
- * done to ensure that the radio stays in Starmode
+ * This function parses the response to the ATS300? command,
+ * extracting the radio version and serial number.
*/
-
-static void strip_IdleTask(unsigned long parameter)
+static void get_radio_version(struct strip *strip_info, __u8 *ptr, __u8 *end)
{
- strip_xmit(NULL, (struct device *)parameter);
+ __u8 *p, *value_begin, *value_end;
+ int len;
+
+ /* Determine the beginning of the second line of the payload */
+ p = ptr;
+ while (p < end && *p != 10) p++;
+ if (p >= end) return;
+ p++;
+ value_begin = p;
+
+ /* Determine the end of line */
+ while (p < end && *p != 10) p++;
+ if (p >= end) return;
+ value_end = p;
+ p++;
+
+ len = value_end - value_begin;
+ len = MIN(len, sizeof(FirmwareVersion) - 1);
+ if (strip_info->firmware_version.c[0] == 0)
+ printk(KERN_INFO "%s: Radio Firmware: %.*s\n",
+ strip_info->dev.name, len, value_begin);
+ sprintf(strip_info->firmware_version.c, "%.*s", len, value_begin);
+
+ /* Look for the first colon */
+ while (p < end && *p != ':') p++;
+ if (p >= end) return;
+ /* Skip over the space */
+ p += 2;
+ len = sizeof(SerialNumber) - 1;
+ if (p + len <= end) {
+ sprintf(strip_info->serial_number.c, "%.*s", len, p);
+ }
+ else {
+ printk(KERN_DEBUG "STRIP: radio serial number shorter (%d) than expected (%d)\n",
+ end - p, len);
+ }
}
+/*
+ * This function parses the response to the ATS325? command,
+ * extracting the radio battery voltage.
+ */
+static void get_radio_voltage(struct strip *strip_info, __u8 *ptr, __u8 *end)
+{
+ int len;
-/************************************************************************/
-/* Receiving routines */
+ len = sizeof(BatteryVoltage) - 1;
+ if (ptr + len <= end) {
+ sprintf(strip_info->battery_voltage.c, "%.*s", len, ptr);
+ }
+ else {
+ printk(KERN_DEBUG "STRIP: radio voltage string shorter (%d) than expected (%d)\n",
+ end - ptr, len);
+ }
+}
-static int strip_receive_room(struct tty_struct *tty)
+/*
+ * This function parses the responses to the AT~LA and ATS311 commands,
+ * which list the radio's neighbours.
+ */
+static void get_radio_neighbours(MetricomNodeTable *table, __u8 *ptr, __u8 *end)
{
- return 0x10000; /* We can handle an infinite amount of data. :-) */
+ table->num_nodes = 0;
+ while (ptr < end && table->num_nodes < NODE_TABLE_SIZE)
+ {
+ MetricomNode *node = &table->node[table->num_nodes++];
+ char *dst = node->c, *limit = dst + sizeof(*node) - 1;
+ while (ptr < end && *ptr <= 32) ptr++;
+ while (ptr < end && dst < limit && *ptr != 10) *dst++ = *ptr++;
+ *dst++ = 0;
+ while (ptr < end && ptr[-1] != 10) ptr++;
+ }
+ do_gettimeofday(&table->timestamp);
}
-static void get_radio_address(struct strip *strip_info, __u8 *p)
+static int get_radio_address(struct strip *strip_info, __u8 *p)
{
MetricomAddress addr;
- string_to_radio_address(&addr, p);
+ if (string_to_radio_address(&addr, p)) return(1);
/* See if our radio address has changed */
- if (memcmp(strip_info->dev.dev_addr, addr.c, sizeof(addr)))
+ if (memcmp(strip_info->true_dev_addr.c, addr.c, sizeof(addr)))
{
MetricomAddressString addr_string;
radio_address_to_string(&addr, &addr_string);
- printk(KERN_INFO "%s: My radio address = %s\n", strip_info->dev.name, addr_string.c);
- memcpy(strip_info->dev.dev_addr, addr.c, sizeof(addr));
+ printk(KERN_INFO "%s: Radio address = %s\n", strip_info->dev.name, addr_string.c);
+ strip_info->true_dev_addr = addr;
+ if (!strip_info->manual_dev_addr) *(MetricomAddress*)strip_info->dev.dev_addr = addr;
/* Give the radio a few seconds to get its head straight, then send an arp */
- strip_info->gratuitous_arp = jiffies + 6 * HZ;
+ strip_info->gratuitous_arp = jiffies + 15 * HZ;
strip_info->arp_interval = 1 * HZ;
}
+ return(0);
+}
+
+static int verify_checksum(struct strip *strip_info)
+{
+ __u8 *p = strip_info->sx_buff;
+ __u8 *end = strip_info->sx_buff + strip_info->sx_count - 4;
+ u_short sum = (READHEX16(end[0]) << 12) | (READHEX16(end[1]) << 8) |
+ (READHEX16(end[2]) << 4) | (READHEX16(end[3]));
+ while (p < end) sum -= *p++;
+ if (sum == 0 && strip_info->firmware_level == StructuredMessages)
+ {
+ strip_info->firmware_level = ChecksummedMessages;
+ printk(KERN_INFO "%s: Radio provides message checksums\n", strip_info->dev.name);
+ }
+ return(sum == 0);
}
static void RecvErr(char *msg, struct strip *strip_info)
@@ -1860,114 +1777,156 @@ static void RecvErr(char *msg, struct strip *strip_info)
static void RecvErr_Message(struct strip *strip_info, __u8 *sendername, const __u8 *msg)
{
- static const char ERR_001[] = "001"; /* Not in StarMode! */
- static const char ERR_002[] = "002"; /* Remap handle */
- static const char ERR_003[] = "003"; /* Can't resolve name */
- static const char ERR_004[] = "004"; /* Name too small or missing */
- static const char ERR_005[] = "005"; /* Bad count specification */
- static const char ERR_006[] = "006"; /* Header too big */
- static const char ERR_007[] = "007"; /* Body too big */
- static const char ERR_008[] = "008"; /* Bad character in name */
- static const char ERR_009[] = "009"; /* No count or line terminator */
-
- if (!strncmp(msg, ERR_001, sizeof(ERR_001)-1))
+ if (has_prefix(msg, "001")) /* Not in StarMode! */
{
RecvErr("Error Msg:", strip_info);
printk(KERN_INFO "%s: Radio %s is not in StarMode\n",
strip_info->dev.name, sendername);
}
- else if (!strncmp(msg, ERR_002, sizeof(ERR_002)-1))
+
+ else if (has_prefix(msg, "002")) /* Remap handle */
{
- RecvErr("Error Msg:", strip_info);
-#ifdef notyet /*Kernel doesn't have scanf!*/
- int handle;
- __u8 newname[64];
- sscanf(msg, "ERR_002 Remap handle &%d to name %s", &handle, newname);
- printk(KERN_INFO "%s: Radio name %s is handle %d\n",
- strip_info->dev.name, newname, handle);
-#endif
+ /* We ignore "Remap handle" messages for now */
}
- else if (!strncmp(msg, ERR_003, sizeof(ERR_003)-1))
+
+ else if (has_prefix(msg, "003")) /* Can't resolve name */
{
RecvErr("Error Msg:", strip_info);
printk(KERN_INFO "%s: Destination radio name is unknown\n",
strip_info->dev.name);
}
- else if (!strncmp(msg, ERR_004, sizeof(ERR_004)-1))
+
+ else if (has_prefix(msg, "004")) /* Name too small or missing */
{
strip_info->watchdog_doreset = jiffies + LongTime;
+#if TICKLE_TIMERS
+ {
+ struct timeval tv;
+ do_gettimeofday(&tv);
+ printk(KERN_INFO "**** Got ERR_004 response at %02d.%06d\n",
+ tv.tv_sec % 100, tv.tv_usec);
+ }
+#endif
if (!strip_info->working)
{
strip_info->working = TRUE;
- printk(KERN_INFO "%s: Radio now in starmode\n",
- strip_info->dev.name);
+ printk(KERN_INFO "%s: Radio now in starmode\n", strip_info->dev.name);
/*
* If the radio has just entered a working state, we should do our first
* probe ASAP, so that we find out our radio address etc. without delay.
*/
strip_info->watchdog_doprobe = jiffies;
}
- if (!strip_info->structured_messages && sendername)
+ if (strip_info->firmware_level == NoStructure && sendername)
+ {
+ strip_info->firmware_level = StructuredMessages;
+ strip_info->next_command = 0; /* Try to enable checksums ASAP */
+ printk(KERN_INFO "%s: Radio provides structured messages\n", strip_info->dev.name);
+ }
+ if (strip_info->firmware_level >= StructuredMessages)
{
- strip_info->structured_messages = TRUE;
- printk(KERN_INFO "%s: Radio provides structured messages\n",
- strip_info->dev.name);
+ verify_checksum(strip_info);
+ /*
+ * If the radio has structured messages but we don't yet have all our information about it, we should do
+ * probes without delay, until we have gathered all the information
+ */
+ if (!GOT_ALL_RADIO_INFO(strip_info)) strip_info->watchdog_doprobe = jiffies;
}
}
- else if (!strncmp(msg, ERR_005, sizeof(ERR_005)-1))
+
+ else if (has_prefix(msg, "005")) /* Bad count specification */
RecvErr("Error Msg:", strip_info);
- else if (!strncmp(msg, ERR_006, sizeof(ERR_006)-1))
+
+ else if (has_prefix(msg, "006")) /* Header too big */
RecvErr("Error Msg:", strip_info);
- else if (!strncmp(msg, ERR_007, sizeof(ERR_007)-1))
+
+ else if (has_prefix(msg, "007")) /* Body too big */
{
- /*
- * Note: This error knocks the radio back into
- * command mode.
- */
RecvErr("Error Msg:", strip_info);
- printk(KERN_ERR "%s: Error! Packet size too big for radio.",
+ printk(KERN_ERR "%s: Error! Packet size too big for radio.\n",
strip_info->dev.name);
- strip_info->watchdog_doreset = jiffies; /* Do reset ASAP */
}
- else if (!strncmp(msg, ERR_008, sizeof(ERR_008)-1))
+
+ else if (has_prefix(msg, "008")) /* Bad character in name */
{
RecvErr("Error Msg:", strip_info);
printk(KERN_ERR "%s: Radio name contains illegal character\n",
strip_info->dev.name);
}
- else if (!strncmp(msg, ERR_009, sizeof(ERR_009)-1))
+
+ else if (has_prefix(msg, "009")) /* No count or line terminator */
+ RecvErr("Error Msg:", strip_info);
+
+ else if (has_prefix(msg, "010")) /* Invalid checksum */
RecvErr("Error Msg:", strip_info);
+
+ else if (has_prefix(msg, "011")) /* Checksum didn't match */
+ RecvErr("Error Msg:", strip_info);
+
+ else if (has_prefix(msg, "012")) /* Failed to transmit packet */
+ RecvErr("Error Msg:", strip_info);
+
else
RecvErr("Error Msg:", strip_info);
}
static void process_AT_response(struct strip *strip_info, __u8 *ptr, __u8 *end)
{
- static const char ATS305[] = "ATS305?";
- static const char ATS300[] = "ATS300?";
- static const char ATS325[] = "ATS325?";
- static const char ATI2[] = "AT~I2 nn";
-
- /* Skip to the first newline character */
__u8 *p = ptr;
- while (p < end && *p != 10) p++;
- if (p >= end) return;
- p++;
+ while (p < end && p[-1] != 10) p++; /* Skip past first newline character */
+ /* Now ptr points to the AT command, and p points to the text of the response. */
- if (!strncmp(ptr, ATS305, sizeof(ATS305)-1))
+#if TICKLE_TIMERS
{
- if (IS_RADIO_ADDRESS(p)) get_radio_address(strip_info, p);
- }
- else if (!strncmp(ptr, ATS300, sizeof(ATS300)-1)) {
- get_radio_version(strip_info, p, end);
- }
- else if (!strncmp(ptr, ATS325, sizeof(ATS325)-1)) {
- get_radio_voltage(strip_info, p, end);
+ struct timeval tv;
+ do_gettimeofday(&tv);
+ printk(KERN_INFO "**** Got AT response %.7s at %02d.%06d\n",
+ ptr, tv.tv_sec % 100, tv.tv_usec);
}
- else if (!strncmp(ptr, ATI2, sizeof(ATI2)-1)) {
- get_radio_neighbors(strip_info, p, end);
+#endif
+
+ if (has_prefix(ptr, "ATS300?" )) get_radio_version(strip_info, p, end);
+ else if (has_prefix(ptr, "ATS305?" )) get_radio_address(strip_info, p);
+ else if (has_prefix(ptr, "ATS311?" )) get_radio_neighbours(&strip_info->poletops, p, end);
+ else if (has_prefix(ptr, "ATS319=7")) verify_checksum(strip_info);
+ else if (has_prefix(ptr, "ATS325?" )) get_radio_voltage(strip_info, p, end);
+ else if (has_prefix(ptr, "AT~LA" )) get_radio_neighbours(&strip_info->portables, p, end);
+ else RecvErr("Unknown AT Response:", strip_info);
+}
+
+static void process_ACK(struct strip *strip_info, __u8 *ptr, __u8 *end)
+{
+ /* Currently we don't do anything with ACKs from the radio */
+}
+
+static void process_Info(struct strip *strip_info, __u8 *ptr, __u8 *end)
+{
+ if (ptr+16 > end) RecvErr("Bad Info Msg:", strip_info);
+}
+
+static struct device *get_strip_dev(struct strip *strip_info)
+{
+ /* If our hardware address is *manually set* to zero, and we know our */
+ /* real radio hardware address, try to find another strip device that has been */
+ /* manually set to that address that we can 'transfer ownership' of this packet to */
+ if (strip_info->manual_dev_addr &&
+ !memcmp(strip_info->dev.dev_addr, zero_address.c, sizeof(zero_address)) &&
+ memcmp(&strip_info->true_dev_addr, zero_address.c, sizeof(zero_address)))
+ {
+ struct device *dev = dev_base;
+ while (dev)
+ {
+ if (dev->type == strip_info->dev.type &&
+ !memcmp(dev->dev_addr, &strip_info->true_dev_addr, sizeof(MetricomAddress)))
+ {
+ printk(KERN_INFO "%s: Transferred packet ownership to %s.\n",
+ strip_info->dev.name, dev->name);
+ return(dev);
+ }
+ dev = dev->next;
+ }
}
- else RecvErr("Unknown AT Response:", strip_info);
+ return(&strip_info->dev);
}
/*
@@ -1979,14 +1938,14 @@ static void deliver_packet(struct strip *strip_info, STRIP_Header *header, __u16
struct sk_buff *skb = dev_alloc_skb(sizeof(STRIP_Header) + packetlen);
if (!skb)
{
- printk(KERN_INFO "%s: memory squeeze, dropping packet.\n", strip_info->dev.name);
+ printk(KERN_ERR "%s: memory squeeze, dropping packet.\n", strip_info->dev.name);
strip_info->rx_dropped++;
}
else
{
memcpy(skb_put(skb, sizeof(STRIP_Header)), header, sizeof(STRIP_Header));
memcpy(skb_put(skb, packetlen), strip_info->rx_buff, packetlen);
- skb->dev = &strip_info->dev;
+ skb->dev = get_strip_dev(strip_info);
skb->protocol = header->protocol;
skb->mac.raw = skb->data;
@@ -1997,6 +1956,10 @@ static void deliver_packet(struct strip *strip_info, STRIP_Header *header, __u16
/* Finally, hand the packet up to the next layer (e.g. IP or ARP, etc.) */
strip_info->rx_packets++;
+ strip_info->rx_pps_count++;
+#ifdef EXT_COUNTERS
+ strip_info->rx_bytes += packetlen;
+#endif
netif_rx(skb);
}
}
@@ -2005,10 +1968,6 @@ static void process_IP_packet(struct strip *strip_info, STRIP_Header *header, __
{
__u16 packetlen;
-#if DO_PROC_NET_STRIP_TRACE
- __u8 *start_ptr = ptr;
-#endif DO_PROC_NET_STRIP_TRACE
-
/* Decode start of the IP packet header */
ptr = UnStuffData(ptr, end, strip_info->rx_buff, 4);
if (!ptr)
@@ -2019,9 +1978,9 @@ static void process_IP_packet(struct strip *strip_info, STRIP_Header *header, __
packetlen = ((__u16)strip_info->rx_buff[2] << 8) | strip_info->rx_buff[3];
- if (packetlen > MAX_STRIP_MTU)
+ if (packetlen > MAX_RECV_MTU)
{
- printk(KERN_ERR "%s: Dropping oversized receive packet: %d bytes\n",
+ printk(KERN_INFO "%s: Dropping oversized received IP packet: %d bytes\n",
strip_info->dev.name, packetlen);
strip_info->rx_dropped++;
return;
@@ -2045,11 +2004,6 @@ static void process_IP_packet(struct strip *strip_info, STRIP_Header *header, __
header->protocol = htons(ETH_P_IP);
-#if DO_PROC_NET_STRIP_TRACE
- packet_log(strip_info, strip_info->rx_buff, EntryReceive, header,
- packetlen, end-start_ptr, slip_len(strip_info->rx_buff, packetlen));
-#endif DO_PROC_NET_STRIP_TRACE
-
deliver_packet(strip_info, header, packetlen);
}
@@ -2058,10 +2012,6 @@ static void process_ARP_packet(struct strip *strip_info, STRIP_Header *header, _
__u16 packetlen;
struct arphdr *arphdr = (struct arphdr *)strip_info->rx_buff;
-#if DO_PROC_NET_STRIP_TRACE
- __u8 *start_ptr = ptr;
-#endif DO_PROC_NET_STRIP_TRACE
-
/* Decode start of the ARP packet */
ptr = UnStuffData(ptr, end, strip_info->rx_buff, 8);
if (!ptr)
@@ -2072,9 +2022,9 @@ static void process_ARP_packet(struct strip *strip_info, STRIP_Header *header, _
packetlen = 8 + (arphdr->ar_hln + arphdr->ar_pln) * 2;
- if (packetlen > MAX_STRIP_MTU)
+ if (packetlen > MAX_RECV_MTU)
{
- printk(KERN_ERR "%s: Dropping oversized receive packet: %d bytes\n",
+ printk(KERN_INFO "%s: Dropping oversized received ARP packet: %d bytes\n",
strip_info->dev.name, packetlen);
strip_info->rx_dropped++;
return;
@@ -2100,15 +2050,47 @@ static void process_ARP_packet(struct strip *strip_info, STRIP_Header *header, _
header->protocol = htons(ETH_P_ARP);
-#if DO_PROC_NET_STRIP_TRACE
- packet_log(strip_info, strip_info->rx_buff, EntryReceive, header,
- packetlen, end-start_ptr, slip_len(strip_info->rx_buff, packetlen));
-#endif DO_PROC_NET_STRIP_TRACE
-
deliver_packet(strip_info, header, packetlen);
}
-static void process_packet(struct strip *strip_info)
+/*
+ * process_text_message processes a <CR>-terminated block of data received
+ * from the radio that doesn't begin with a '*' character. All normal
+ * Starmode communication messages with the radio begin with a '*',
+ * so any text that does not indicates a serial port error, a radio that
+ * is in Hayes command mode instead of Starmode, or a radio with really
+ * old firmware that doesn't frame its Starmode responses properly.
+ */
+static void process_text_message(struct strip *strip_info)
+{
+ __u8 *msg = strip_info->sx_buff;
+ int len = strip_info->sx_count;
+
+ /* Check for anything that looks like it might be our radio name */
+ /* (This is here for backwards compatibility with old firmware) */
+ if (len == 9 && get_radio_address(strip_info, msg) == 0) return;
+
+ if (text_equal(msg, len, "OK" )) return; /* Ignore 'OK' responses from prior commands */
+ if (text_equal(msg, len, "ERROR" )) return; /* Ignore 'ERROR' messages */
+ if (text_equal(msg, len, InitString)) return; /* Ignore character echo back from the radio */
+
+ /* Catch other error messages */
+ /* (This is here for backwards compatibility with old firmware) */
+ if (has_prefix(msg, "ERR_")) { RecvErr_Message(strip_info, NULL, &msg[4]); return; }
+
+ RecvErr("No initial *", strip_info);
+}
+
+/*
+ * process_message processes a <CR>-terminated block of data received
+ * from the radio. If the radio is not in Starmode or has old firmware,
+ * it may be a line of text in response to an AT command. Ideally, with
+ * a current radio that's properly in Starmode, all data received should
+ * be properly framed and checksummed radio message blocks, containing
+ * either a starmode packet, or a other communication from the radio
+ * firmware, like "INF_" Info messages and &COMMAND responses.
+ */
+static void process_message(struct strip *strip_info)
{
STRIP_Header header = { zero_address, zero_address, 0 };
__u8 *ptr = strip_info->sx_buff;
@@ -2116,29 +2098,11 @@ static void process_packet(struct strip *strip_info)
__u8 sendername[32], *sptr = sendername;
MetricomKey key;
- /* Ignore 'OK' responses from prior commands */
- if (strip_info->sx_count == 2 && ptr[0] == 'O' && ptr[1] == 'K') return;
-
- /* Check for anything that looks like it might be our radio name: dddd-dddd */
- /* (This is here for backwards compatibility with old firmware) */
- if (strip_info->sx_count == 9 && IS_RADIO_ADDRESS(ptr))
- {
- get_radio_address(strip_info, ptr);
- return;
- }
-
/*HexDump("Receiving", strip_info, ptr, end);*/
/* Check for start of address marker, and then skip over it */
- if (*ptr != '*')
- {
- /* Catch other error messages */
- if (ptr[0] == 'E' && ptr[1] == 'R' && ptr[2] == 'R' && ptr[3] == '_')
- RecvErr_Message(strip_info, NULL, &ptr[4]);
- else RecvErr("No initial *", strip_info);
- return;
- }
- ptr++; /* Skip the initial '*' */
+ if (*ptr == '*') ptr++;
+ else { process_text_message(strip_info); return; }
/* Copy out the return address */
while (ptr < end && *ptr != '*' && sptr < ARRAY_END(sendername)-1) *sptr++ = *ptr++;
@@ -2156,55 +2120,85 @@ static void process_packet(struct strip *strip_info)
/* (This is here for backwards compatibility with old firmware) */
if (!strcmp(sendername, "&COMMAND"))
{
- strip_info->structured_messages = FALSE;
+ strip_info->firmware_level = NoStructure;
+ strip_info->next_command = CompatibilityCommand;
return;
}
- if (ptr+4 >= end)
+ if (ptr+4 > end)
{
RecvErr("No proto key", strip_info);
return;
}
- /*printk(KERN_INFO "%s: Got packet from \"%s\".\n", strip_info->dev.name, sendername);*/
-
- /*
- * Fill in (pseudo) source and destination addresses in the packet.
- * We assume that the destination address was our address (the radio does not
- * tell us this). If the radio supplies a source address, then we use it.
- */
- memcpy(&header.dst_addr, strip_info->dev.dev_addr, sizeof(MetricomAddress));
- if (IS_RADIO_ADDRESS(sendername)) string_to_radio_address(&header.src_addr, sendername);
-
/* Get the protocol key out of the buffer */
key.c[0] = *ptr++;
key.c[1] = *ptr++;
key.c[2] = *ptr++;
key.c[3] = *ptr++;
- if (key.l == SIP0Key.l) process_IP_packet(strip_info, &header, ptr, end);
- else if (key.l == ARP0Key.l) process_ARP_packet(strip_info, &header, ptr, end);
- else if (key.l == ATR_Key.l) process_AT_response(strip_info, ptr, end);
- else if (key.l == ERR_Key.l) RecvErr_Message(strip_info, sendername, ptr);
- else /* RecvErr("Unrecognized protocol key", strip_info); */
-
- /* Note, this "else" block is temporary, until Metricom fix their */
- /* packet corruption bug */
+ /* If we're using checksums, verify the checksum at the end of the packet */
+ if (strip_info->firmware_level >= ChecksummedMessages)
{
- RecvErr("Unrecognized protocol key (retrying)", strip_info);
- ptr -= 3; /* Back up and try again */
- key.c[0] = *ptr++;
- key.c[1] = *ptr++;
- key.c[2] = *ptr++;
- key.c[3] = *ptr++;
- if (key.l == SIP0Key.l) process_IP_packet(strip_info, &header, ptr, end);
- else if (key.l == ARP0Key.l) process_ARP_packet(strip_info, &header, ptr, end);
- else if (key.l == ATR_Key.l) process_AT_response(strip_info, ptr, end);
- else if (key.l == ERR_Key.l) RecvErr_Message(strip_info, sendername, ptr);
- else RecvErr("Unrecognized protocol key", strip_info);
+ end -= 4; /* Chop the last four bytes off the packet (they're the checksum) */
+ if (ptr > end)
+ {
+ RecvErr("Missing Checksum", strip_info);
+ return;
+ }
+ if (!verify_checksum(strip_info))
+ {
+ RecvErr("Bad Checksum", strip_info);
+ return;
+ }
}
+
+ /*printk(KERN_INFO "%s: Got packet from \"%s\".\n", strip_info->dev.name, sendername);*/
+
+ /*
+ * Fill in (pseudo) source and destination addresses in the packet.
+ * We assume that the destination address was our address (the radio does not
+ * tell us this). If the radio supplies a source address, then we use it.
+ */
+ header.dst_addr = strip_info->true_dev_addr;
+ string_to_radio_address(&header.src_addr, sendername);
+
+#ifdef EXT_COUNTERS
+ if (key.l == SIP0Key.l) {
+ strip_info->rx_rbytes += (end - ptr);
+ process_IP_packet(strip_info, &header, ptr, end);
+ } else if (key.l == ARP0Key.l) {
+ strip_info->rx_rbytes += (end - ptr);
+ process_ARP_packet(strip_info, &header, ptr, end);
+ } else if (key.l == ATR_Key.l) {
+ strip_info->rx_ebytes += (end - ptr);
+ process_AT_response(strip_info, ptr, end);
+ } else if (key.l == ACK_Key.l) {
+ strip_info->rx_ebytes += (end - ptr);
+ process_ACK(strip_info, ptr, end);
+ } else if (key.l == INF_Key.l) {
+ strip_info->rx_ebytes += (end - ptr);
+ process_Info(strip_info, ptr, end);
+ } else if (key.l == ERR_Key.l) {
+ strip_info->rx_ebytes += (end - ptr);
+ RecvErr_Message(strip_info, sendername, ptr);
+ } else RecvErr("Unrecognized protocol key", strip_info);
+#else
+ if (key.l == SIP0Key.l) process_IP_packet (strip_info, &header, ptr, end);
+ else if (key.l == ARP0Key.l) process_ARP_packet (strip_info, &header, ptr, end);
+ else if (key.l == ATR_Key.l) process_AT_response(strip_info, ptr, end);
+ else if (key.l == ACK_Key.l) process_ACK (strip_info, ptr, end);
+ else if (key.l == INF_Key.l) process_Info (strip_info, ptr, end);
+ else if (key.l == ERR_Key.l) RecvErr_Message (strip_info, sendername, ptr);
+ else RecvErr("Unrecognized protocol key", strip_info);
+#endif
}
+#define TTYERROR(X) ((X) == TTY_BREAK ? "Break" : \
+ (X) == TTY_FRAME ? "Framing Error" : \
+ (X) == TTY_PARITY ? "Parity Error" : \
+ (X) == TTY_OVERRUN ? "Hardware Overrun" : "Unknown Error")
+
/*
* Handle the 'receiver data ready' interrupt.
* This function is called by the 'tty_io' module in the kernel when
@@ -2229,17 +2223,23 @@ strip_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int
{
struct timeval tv;
do_gettimeofday(&tv);
- printk(KERN_INFO "**** strip_receive_buf: %3d bytes at %d.%06d\n",
+ printk(KERN_INFO "**** strip_receive_buf: %3d bytes at %02d.%06d\n",
count, tv.tv_sec % 100, tv.tv_usec);
}
#endif
+#ifdef EXT_COUNTERS
+ strip_info->rx_sbytes += count;
+#endif
+
/* Read the characters out of the buffer */
while (cp < end)
{
+ if (fp && *fp) printk(KERN_INFO "%s: %s on serial port\n", strip_info->dev.name, TTYERROR(*fp));
if (fp && *fp++ && !strip_info->discard) /* If there's a serial error, record it */
{
- strip_info->discard = 1;
+ /* If we have some characters in the buffer, discard them */
+ strip_info->discard = strip_info->sx_count;
strip_info->rx_errors++;
}
@@ -2249,21 +2249,23 @@ strip_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int
if (*cp == 0x0D) /* If end of packet, decide what to do with it */
{
if (strip_info->sx_count > 3000)
- printk(KERN_INFO "Cut a %d byte packet (%d bytes remaining)%s\n",
- strip_info->sx_count, end-cp-1,
+ printk(KERN_INFO "%s: Cut a %d byte packet (%d bytes remaining)%s\n",
+ strip_info->dev.name, strip_info->sx_count, end-cp-1,
strip_info->discard ? " (discarded)" : "");
if (strip_info->sx_count > strip_info->sx_size)
{
- strip_info->discard = 1;
strip_info->rx_over_errors++;
printk(KERN_INFO "%s: sx_buff overflow (%d bytes total)\n",
strip_info->dev.name, strip_info->sx_count);
}
- if (!strip_info->discard) process_packet(strip_info);
+ else if (strip_info->discard)
+ printk(KERN_INFO "%s: Discarding bad packet (%d/%d)\n",
+ strip_info->dev.name, strip_info->discard, strip_info->sx_count);
+ else process_message(strip_info);
strip_info->discard = 0;
strip_info->sx_count = 0;
}
- else if (!strip_info->discard) /* If we're not discarding, store the character */
+ else
{
/* Make sure we have space in the buffer */
if (strip_info->sx_count < strip_info->sx_size)
@@ -2279,9 +2281,28 @@ strip_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int
/************************************************************************/
/* General control routines */
-static int strip_set_dev_mac_address(struct device *dev, void *addr)
+static int set_mac_address(struct strip *strip_info, MetricomAddress *addr)
{
- return -1; /* You cannot override a Metricom radio's address */
+ /*
+ * We're using a manually specified address if the address is set
+ * to anything other than all ones. Setting the address to all ones
+ * disables manual mode and goes back to automatic address determination
+ * (tracking the true address that the radio has).
+ */
+ strip_info->manual_dev_addr = memcmp(addr->c, broadcast_address.c, sizeof(broadcast_address));
+ if (strip_info->manual_dev_addr)
+ *(MetricomAddress*)strip_info->dev.dev_addr = *addr;
+ else *(MetricomAddress*)strip_info->dev.dev_addr = strip_info->true_dev_addr;
+ return 0;
+}
+
+static int dev_set_mac_address(struct device *dev, void *addr)
+{
+ struct strip *strip_info = (struct strip *)(dev->priv);
+ struct sockaddr *sa = addr;
+ printk(KERN_INFO "%s: strip_set_dev_mac_address called\n", dev->name);
+ set_mac_address(strip_info, (MetricomAddress *)sa->sa_data);
+ return 0;
}
static struct net_device_stats *strip_get_stats(struct device *dev)
@@ -2341,7 +2362,8 @@ static int strip_open_low(struct device *dev)
strip_info->discard = 0;
strip_info->working = FALSE;
- strip_info->structured_messages = FALSE;
+ strip_info->firmware_level = NoStructure;
+ strip_info->next_command = CompatibilityCommand;
strip_info->sx_count = 0;
strip_info->tx_left = 0;
@@ -2442,7 +2464,7 @@ static int strip_dev_init(struct device *dev)
dev->rebuild_header = strip_rebuild_header;
/* dev->type_trans unused */
/* dev->set_multicast_list unused */
- dev->set_mac_address = strip_set_dev_mac_address;
+ dev->set_mac_address = dev_set_mac_address;
/* dev->do_ioctl unused */
/* dev->set_config unused */
dev->get_stats = strip_get_stats;
@@ -2455,19 +2477,10 @@ static int strip_dev_init(struct device *dev)
static void strip_free(struct strip *strip_info)
{
- MetricomNode *node, *free;
-
*(strip_info->referrer) = strip_info->next;
if (strip_info->next)
strip_info->next->referrer = strip_info->referrer;
strip_info->magic = 0;
-
- for (node = strip_info->neighbor_list; node != NULL; )
- {
- free = node;
- node = node->next;
- kfree(free);
- }
kfree(strip_info);
}
@@ -2522,13 +2535,9 @@ static struct strip *strip_alloc(void)
strip_info->idle_timer.data = (long)&strip_info->dev;
strip_info->idle_timer.function = strip_IdleTask;
- strip_info->neighbor_list = kmalloc(sizeof(MetricomNode), GFP_KERNEL);
- strip_info->neighbor_list->type = 0;
- strip_info->neighbor_list->next = NULL;
-
/* Note: strip_info->if_name is currently 8 characters long */
- sprintf(strip_info->if_name, "st%d", channel_id);
- strip_info->dev.name = strip_info->if_name;
+ sprintf(strip_info->if_name.c, "st%d", channel_id);
+ strip_info->dev.name = strip_info->if_name.c;
strip_info->dev.base_addr = channel_id;
strip_info->dev.priv = (void*)strip_info;
strip_info->dev.next = NULL;
@@ -2598,6 +2607,9 @@ static int strip_open(struct tty_struct *tty)
#ifdef MODULE
MOD_INC_USE_COUNT;
#endif
+
+ printk(KERN_INFO "STRIP: device \"%s\" activated\n", strip_info->if_name.c);
+
/*
* Done. We have linked the TTY line to a channel.
*/
@@ -2627,6 +2639,7 @@ static void strip_close(struct tty_struct *tty)
tty->disc_data = 0;
strip_info->tty = NULL;
+ printk(KERN_INFO "STRIP: device \"%s\" closed down\n", strip_info->if_name.c);
strip_free(strip_info);
tty->disc_data = NULL;
#ifdef MODULE
@@ -2657,12 +2670,17 @@ static int strip_ioctl(struct tty_struct *tty, struct file *file,
err = verify_area(VERIFY_WRITE, (void*)arg, 16);
if (err)
return -err;
- copy_to_user((void*)arg, strip_info->dev.name,
- strlen(strip_info->dev.name) + 1);
- return 0;
+ return copy_to_user((void*)arg, strip_info->dev.name,
+ strlen(strip_info->dev.name) + 1)?-EFAULT:0;
case SIOCSIFHWADDR:
- return -EINVAL;
+ {
+ MetricomAddress addr;
+ printk(KERN_INFO "%s: SIOCSIFHWADDR\n", strip_info->dev.name);
+ if(copy_from_user(&addr, (void*)arg, sizeof(MetricomAddress)))
+ return -EFAULT;
+ return(set_mac_address(strip_info, &addr));
+ }
/*
* Allow stty to read, but not set, the serial port
@@ -2683,32 +2701,6 @@ static int strip_ioctl(struct tty_struct *tty, struct file *file,
/* Initialization */
/*
- * Registers with the /proc file system to create different /proc/net files.
- */
-
-static int strip_proc_net_register(unsigned short type, char *file_name,
- int (*get_info)(char *, char **, off_t, int, int))
-{
- struct proc_dir_entry *strip_entry;
-
- strip_entry = kmalloc(sizeof(struct proc_dir_entry), GFP_ATOMIC);
-
- memset(strip_entry, 0, sizeof(struct proc_dir_entry));
- strip_entry->low_ino = type;
- strip_entry->namelen = strlen(file_name);
- strip_entry->name = file_name;
- strip_entry->mode = S_IFREG | S_IRUGO;
- strip_entry->nlink = 1;
- strip_entry->uid = 0;
- strip_entry->gid = 0;
- strip_entry->size = 0;
- strip_entry->ops = &proc_net_inode_operations;
- strip_entry->get_info = get_info;
-
- return proc_net_register(strip_entry);
-}
-
-/*
* Initialize the STRIP driver.
* This routine is called at boot time, to bootstrap the multi-channel
* STRIP driver
@@ -2722,7 +2714,7 @@ int strip_init_ctrl_dev(struct device *dummy)
static struct tty_ldisc strip_ldisc;
int status;
- printk("STRIP: version %s (unlimited channels)\n", StripVersion);
+ printk(KERN_INFO "STRIP: Version %s (unlimited channels)\n", StripVersion);
/*
* Fill in our line protocol discipline, and register it
@@ -2747,24 +2739,12 @@ int strip_init_ctrl_dev(struct device *dummy)
}
/*
- * Register the status and trace files with /proc
+ * Register the status file with /proc
*/
-
-#if DO_PROC_NET_STRIP_STATUS
- if (strip_proc_net_register(PROC_NET_STRIP_STATUS, "strip_status",
- &strip_get_status_info) != 0)
+ if (proc_net_register(&proc_strip_get_status_info) != 0)
{
- printk(KERN_ERR "strip: status strip_proc_net_register() failed.\n");
+ printk(KERN_ERR "strip: status proc_net_register() failed.\n");
}
-#endif
-
-#if DO_PROC_NET_STRIP_TRACE
- if (strip_proc_net_register(PROC_NET_STRIP_TRACE, "strip_trace",
- &strip_get_trace_info) != 0)
- {
- printk(KERN_ERR "strip: trace strip_proc_net_register() failed.\n");
- }
-#endif
#ifdef MODULE
return status;
@@ -2794,16 +2774,12 @@ void cleanup_module(void)
while (struct_strip_list)
strip_free(struct_strip_list);
- /* Unregister with the /proc/net files here. */
-
-#if DO_PROC_NET_STRIP_TRACE
- proc_net_unregister(PROC_NET_STRIP_TRACE);
-#endif
-#if DO_PROC_NET_STRIP_STATUS
+ /* Unregister with the /proc/net file here. */
proc_net_unregister(PROC_NET_STRIP_STATUS);
-#endif
if ((i = tty_register_ldisc(N_STRIP, NULL)))
printk(KERN_ERR "STRIP: can't unregister line discipline (err = %d)\n", i);
+
+ printk(KERN_INFO "STRIP: Module Unloaded\n");
}
#endif /* MODULE */
diff --git a/drivers/net/sunhme.c b/drivers/net/sunhme.c
index 7299b0f4b..dca243c4f 100644
--- a/drivers/net/sunhme.c
+++ b/drivers/net/sunhme.c
@@ -946,7 +946,8 @@ static void happy_meal_init_rings(struct happy_meal *hp, int from_irq)
/* Because we reserve afterwards. */
skb_put(skb, (ETH_FRAME_LEN + RX_OFFSET));
- hb->happy_meal_rxd[i].rx_addr = (unsigned int) skb->data;
+ hb->happy_meal_rxd[i].rx_addr =
+ (u32) ((unsigned long)skb->data);
skb_reserve(skb, RX_OFFSET);
hb->happy_meal_rxd[i].rx_flags =
(RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16));
@@ -1615,7 +1616,8 @@ static inline void happy_meal_rx(struct happy_meal *hp, struct device *dev,
/* Return it to the Happy meal. */
drop_it:
hp->net_stats.rx_dropped++;
- this->rx_addr = (unsigned int) hp->rx_skbs[elem]->data;
+ this->rx_addr =
+ (u32) ((unsigned long)hp->rx_skbs[elem]->data);
this->rx_flags =
(RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16));
goto next;
@@ -1634,7 +1636,8 @@ static inline void happy_meal_rx(struct happy_meal *hp, struct device *dev,
hp->rx_skbs[elem] = new_skb;
new_skb->dev = dev;
skb_put(new_skb, (ETH_FRAME_LEN + RX_OFFSET));
- rxbase[elem].rx_addr = (unsigned int) new_skb->data;
+ rxbase[elem].rx_addr =
+ (u32) ((unsigned long)new_skb->data);
skb_reserve(new_skb, RX_OFFSET);
rxbase[elem].rx_flags =
(RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16));
@@ -1655,7 +1658,8 @@ static inline void happy_meal_rx(struct happy_meal *hp, struct device *dev,
memcpy(copy_skb->data, skb->data, len);
/* Reuse original ring buffer. */
- rxbase[elem].rx_addr = (unsigned int) skb->data;
+ rxbase[elem].rx_addr =
+ (u32) ((unsigned long)skb->data);
rxbase[elem].rx_flags =
(RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16));
@@ -1913,7 +1917,8 @@ static int happy_meal_start_xmit(struct sk_buff *skb, struct device *dev)
SXD(("SX<l[%d]e[%d]>", len, entry));
hp->tx_skbs[entry] = skb;
- hp->happy_block->happy_meal_txd[entry].tx_addr = (unsigned int) skb->data;
+ hp->happy_block->happy_meal_txd[entry].tx_addr =
+ (u32) ((unsigned long)skb->data);
hp->happy_block->happy_meal_txd[entry].tx_flags =
(TXFLAG_OWN | TXFLAG_SOP | TXFLAG_EOP | (len & TXFLAG_SIZE));
hp->tx_new = NEXT_TX(entry);
@@ -2087,6 +2092,8 @@ static inline int happy_meal_ether_init(struct device *dev, struct linux_sbus_de
printk("\n");
hp = (struct happy_meal *) dev->priv;
+ memset(hp, 0, sizeof(*hp));
+
hp->happy_sbus_dev = sdev;
if(sdev->num_registers != 5) {
diff --git a/drivers/net/sunqe.c b/drivers/net/sunqe.c
index 18e758c80..6b417e34b 100644
--- a/drivers/net/sunqe.c
+++ b/drivers/net/sunqe.c
@@ -152,8 +152,8 @@ static void qe_init_rings(struct sunqe *qep, int from_irq)
skb_put(skb, ETH_FRAME_LEN);
skb_reserve(skb, 34);
- /* FIX FOR ULTRA */
- qb->qe_rxd[i].rx_addr = (unsigned int) skb->data;
+ qb->qe_rxd[i].rx_addr =
+ (unsigned int) ((unsigned long)skb->data);
qb->qe_rxd[i].rx_flags =
(RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH));
}
@@ -491,7 +491,8 @@ static inline void qe_rx(struct sunqe *qep)
drop_it:
/* Return it to the QE. */
qep->net_stats.rx_dropped++;
- this->rx_addr = (unsigned int) qep->rx_skbs[elem]->data;
+ this->rx_addr =
+ (unsigned int) ((unsigned long)qep->rx_skbs[elem]->data);
this->rx_flags =
(RXD_OWN | (RX_BUF_ALLOC_SIZE & RXD_LENGTH));
goto next;
@@ -512,8 +513,8 @@ static inline void qe_rx(struct sunqe *qep)
skb_put(new_skb, ETH_FRAME_LEN);
skb_reserve(new_skb, 34);
- /* FIX FOR ULTRA */
- rxbase[elem].rx_addr = (unsigned int) new_skb->data;
+ rxbase[elem].rx_addr =
+ (unsigned int) ((unsigned long)new_skb->data);
rxbase[elem].rx_flags =
(RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH));
@@ -533,7 +534,8 @@ static inline void qe_rx(struct sunqe *qep)
eth_copy_and_sum(copy_skb, (unsigned char *)skb->data, len, 0);
/* Reuse original ring buffer. */
- rxbase[elem].rx_addr = (unsigned int) skb->data;
+ rxbase[elem].rx_addr =
+ (unsigned int) ((unsigned long)skb->data);
rxbase[elem].rx_flags =
(RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH));
diff --git a/drivers/net/tlan.c b/drivers/net/tlan.c
new file mode 100644
index 000000000..d3d9d0e1c
--- /dev/null
+++ b/drivers/net/tlan.c
@@ -0,0 +1,2309 @@
+/********************************************************************
+ *
+ * Linux ThunderLAN Driver
+ *
+ * tlan.c
+ * by James Banks, james.banks@caldera.com
+ *
+ * (C) 1997 Caldera, Inc.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU Public License, incorporated herein by reference.
+ *
+ ** This file is best viewed/edited with tabstop=4 and colums>=132.
+ *
+ ** Useful (if not required) reading:
+ *
+ * Texas Instruments, ThunderLAN Programmer's Guide,
+ * TI Literature Number SPWU013A
+ * available in PDF format from www.ti.com
+ * National Semiconductor, DP83840A Data Sheet
+ * available in PDF format from www.national.com
+ * Microchip Technology, 24C01A/02A/04A Data Sheet
+ * available in PDF format from www.microchip.com
+ *
+ ********************************************************************/
+
+
+#include <linux/module.h>
+
+
+#include "tlan.h"
+
+
+#include <linux/bios32.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/etherdevice.h>
+
+
+
+
+#ifdef MODULE
+ static struct device *TLanDevices = NULL;
+ static int TLanDevicesInstalled = 0;
+#endif
+ static int debug = 0;
+ static u8 *TLanPadBuffer;
+ static char TLanSignature[] = "TLAN";
+ static int TLanVersionMajor = 0;
+ static int TLanVersionMinor = 27;
+
+ static TLanPciId TLanDeviceList[] = {
+ { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETELLIGENT_10, "Compaq Netelligent 10" },
+ { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETELLIGENT_10_100, "Compaq Netelligent 10/100" },
+ { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETFLEX_3_INTEGRATED, "Compaq Integrated NetFlex-3" },
+ { 0, 0, NULL } /* End of List */
+ };
+
+
+ static int TLan_MiiReadReg(u16, u16, u16, u16 *);
+ static void TLan_MiiSendData( u16, u32, unsigned );
+ static void TLan_MiiSync(u16);
+ static void TLan_MiiWriteReg(u16, u16, u16, u16);
+
+
+
+/*****************************************************************************
+******************************************************************************
+
+ ThunderLAN Driver MII Routines
+
+ These routines are based on the information in Chap. 2 of the
+ "ThunderLAN Programmer's Guide", pp. 15-24.
+
+******************************************************************************
+*****************************************************************************/
+
+
+ /*************************************************************************
+ * TLan_MiiReadReg
+ *
+ * Returns: 0 if ack received ok, 1 otherwise.
+ * Parms: base_port The base IO port of the adapter in question.
+ * dev The address of the PHY to be queried.
+ * reg The register whose contents are to be
+ * retreived.
+ * val A pointer to a variable to store the retrieved
+ * value.
+ *
+ * This function uses the TLAN's MII bus to retreive the contents of a
+ * given register on a PHY. It sends the appropriate info and then
+ * reads the 16-bit register value from the MII bus via the TLAN SIO
+ * register.
+ *
+ ************************************************************************/
+
+ int TLan_MiiReadReg(u16 base_port, u16 dev, u16 reg, u16 *val)
+ {
+ u8 nack;
+ u16 sio, tmp;
+ u32 i;
+ int err;
+
+ err = FALSE;
+ outw(TLAN_NET_SIO, base_port + TLAN_DIO_ADR);
+ sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO;
+
+ cli();
+
+ TLan_MiiSync(base_port);
+
+ TLan_ClearBit(TLAN_NET_SIO_MINTEN, sio); /* Disable PHY ints */
+
+ TLan_MiiSendData( base_port, 0x1, 2 ); /* Start ( 01b ) */
+ TLan_MiiSendData( base_port, 0x2, 2 ); /* Read ( 10b ) */
+ TLan_MiiSendData( base_port, dev, 5 ); /* Device # */
+ TLan_MiiSendData( base_port, reg, 5 ); /* Register # */
+
+
+ TLan_ClearBit(TLAN_NET_SIO_MTXEN, sio); /* Change direction */
+
+ TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Clock through Idle bit */
+ TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
+ TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Use this to wait 300ns */
+
+ nack = TLan_GetBit(TLAN_NET_SIO_MDATA, sio); /* Check for ACK */
+ TLan_SetBit(TLAN_NET_SIO_MCLK, sio); /* Finish ACK clock cycle */
+ if (nack) { /* No ACK, so fake it */
+ for (i = 0; i < 16; i++) {
+ TLan_ClearBit(TLAN_NET_SIO_MCLK, sio);
+ TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
+ }
+ tmp = 0xffff;
+ err = TRUE;
+ } else { /* ACKed, so read data */
+ for (tmp = 0, i = 0x8000; i; i >>= 1) {
+ TLan_ClearBit(TLAN_NET_SIO_MCLK, sio);
+ if (TLan_GetBit(TLAN_NET_SIO_MDATA, sio))
+ tmp |= i;
+ TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
+ }
+ }
+
+
+ TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Idle cycle */
+ TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
+
+ TLan_SetBit(TLAN_NET_SIO_MINTEN, sio); /* Enable PHY ints */
+
+ *val = tmp;
+
+ sti();
+
+ return err;
+
+ } /* TLan_MiiReadReg */
+
+
+
+
+ /*************************************************************************
+ * TLan_MiiSendData
+ *
+ * Returns: Nothing
+ * Parms: base_port The base IO port of the adapter in question.
+ * dev The address of the PHY to be queried.
+ * data The value to be placed on the MII bus.
+ * num_bits The number of bits in data that are to be
+ * placed on the MII bus.
+ *
+ * This function sends on sequence of bits on the MII configuration
+ * bus.
+ *
+ ************************************************************************/
+
+ void TLan_MiiSendData( u16 base_port, u32 data, unsigned num_bits )
+ {
+ u16 sio;
+ u32 i;
+
+ if ( num_bits == 0 )
+ return;
+
+ outw( TLAN_NET_SIO, base_port + TLAN_DIO_ADR );
+ sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO;
+ TLan_SetBit( TLAN_NET_SIO_MTXEN, sio );
+
+ for ( i = ( 0x1 << ( num_bits - 1 ) ); i; i >>= 1 ) {
+ TLan_ClearBit( TLAN_NET_SIO_MCLK, sio );
+ TLan_GetBit( TLAN_NET_SIO_MCLK, sio );
+ if ( data & i )
+ TLan_SetBit( TLAN_NET_SIO_MDATA, sio );
+ else
+ TLan_ClearBit( TLAN_NET_SIO_MDATA, sio );
+ TLan_SetBit( TLAN_NET_SIO_MCLK, sio );
+ TLan_GetBit( TLAN_NET_SIO_MCLK, sio );
+ }
+
+ } /* TLan_MiiSendData */
+
+
+
+
+ /*************************************************************************
+ * TLan_MiiSync
+ *
+ * Returns: Nothing
+ * Parms: base_port The base IO port of the adapter in question.
+ *
+ * This functions syncs all PHYs in terms of the MII configuration bus.
+ *
+ ************************************************************************/
+
+ void TLan_MiiSync( u16 base_port )
+ {
+ int i;
+ u16 sio;
+
+ outw( TLAN_NET_SIO, base_port + TLAN_DIO_ADR );
+ sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO;
+
+ TLan_ClearBit( TLAN_NET_SIO_MTXEN, sio );
+ for ( i = 0; i < 32; i++ ) {
+ TLan_ClearBit( TLAN_NET_SIO_MCLK, sio );
+ TLan_SetBit( TLAN_NET_SIO_MCLK, sio );
+ }
+
+ } /* TLan_MiiSync */
+
+
+
+
+ /*************************************************************************
+ * TLan_MiiWriteReg
+ *
+ * Returns: Nothing
+ * Parms: base_port The base IO port of the adapter in question.
+ * dev The address of the PHY to be written to.
+ * reg The register whose contents are to be
+ * written.
+ * val The value to be written to the register.
+ *
+ * This function uses the TLAN's MII bus to write the contents of a
+ * given register on a PHY. It sends the appropriate info and then
+ * writes the 16-bit register value from the MII configuration bus
+ * via the TLAN SIO register.
+ *
+ ************************************************************************/
+
+ void TLan_MiiWriteReg(u16 base_port, u16 dev, u16 reg, u16 val)
+ {
+ u16 sio;
+
+ outw(TLAN_NET_SIO, base_port + TLAN_DIO_ADR);
+ sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO;
+
+ cli();
+
+ TLan_MiiSync( base_port );
+
+ TLan_ClearBit( TLAN_NET_SIO_MINTEN, sio );
+
+ TLan_MiiSendData( base_port, 0x1, 2 ); /* Start ( 01b ) */
+ TLan_MiiSendData( base_port, 0x1, 2 ); /* Write ( 01b ) */
+ TLan_MiiSendData( base_port, dev, 5 ); /* Device # */
+ TLan_MiiSendData( base_port, reg, 5 ); /* Register # */
+
+ TLan_MiiSendData( base_port, 0x2, 2 ); /* Send ACK */
+ TLan_MiiSendData( base_port, val, 16 ); /* Send Data */
+
+ TLan_ClearBit( TLAN_NET_SIO_MCLK, sio ); /* Idle cycle */
+ TLan_SetBit( TLAN_NET_SIO_MCLK, sio );
+
+ TLan_SetBit( TLAN_NET_SIO_MINTEN, sio );
+
+ sti();
+
+ } /* TLan_MiiWriteReg */
+
+
+
+
+/*****************************************************************************
+******************************************************************************
+
+ ThunderLAN Driver PHY Layer Routines
+
+ The TLAN chip can drive any number of PHYs (physical devices). Rather
+ than having lots of 'if' or '#ifdef' statements, I have created a
+ second driver layer for the PHYs. Each PHY can be identified from its
+ id in registers 2 and 3, and can be given a Check and Service routine
+ that will be called when the adapter is reset and when the adapter
+ receives a Network Status interrupt, respectively.
+
+******************************************************************************
+*****************************************************************************/
+
+ static int TLan_PhyNop( struct device * );
+ static void TLan_PhyPrint( struct device * );
+ static void TLan_PhySelect( struct device * );
+ static int TLan_PhyInternalCheck( struct device * );
+ static int TLan_PhyInternalService( struct device * );
+ static int TLan_PhyDp83840aCheck( struct device * );
+
+
+
+
+ static TLanPhyIdEntry TLanPhyIdTable[] = {
+ { 0x4000, 0x5014, &TLan_PhyInternalCheck, &TLan_PhyInternalService, TLAN_PHY_ACTIVITY },
+ { 0x4000, 0x5015, &TLan_PhyInternalCheck, &TLan_PhyInternalService, TLAN_PHY_ACTIVITY },
+ { 0x2000, 0x5C01, &TLan_PhyDp83840aCheck, &TLan_PhyNop, TLAN_PHY_ACTIVITY | TLAN_PHY_AUTONEG },
+ { 0x0000, 0x0000, NULL, NULL, 0 }
+ };
+
+
+
+
+ /*************************************************************************
+ * TLan_PhyPrint
+ *
+ * Returns: Nothing
+ * Parms: dev A pointer to the device structure of the adapter
+ * which the desired PHY is located.
+ *
+ * This function prints the registers a PHY.
+ *
+ ************************************************************************/
+
+ void TLan_PhyPrint( struct device *dev )
+ {
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ u16 i, data0, data1, data2, data3, phy;
+ u32 io;
+
+ phy = priv->phyAddr;
+ io = dev->base_addr;
+
+ if ( ( phy > 0 ) || ( phy <= TLAN_PHY_MAX_ADDR ) ) {
+ printk( "TLAN: Device %s, PHY 0x%02x.\n", dev->name, phy );
+ printk( "TLAN: Off. +0 +1 +2 +3 \n" );
+ for ( i = 0; i < 0x20; i+= 4 ) {
+ TLan_MiiReadReg( io, phy, i, &data0 );
+ TLan_MiiReadReg( io, phy, i + 1, &data1 );
+ TLan_MiiReadReg( io, phy, i + 2, &data2 );
+ TLan_MiiReadReg( io, phy, i + 3, &data3 );
+ printk( "TLAN: 0x%02x 0x%04hx 0x%04hx 0x%04hx 0x%04hx\n", i, data0, data1, data2, data3 );
+ }
+ } else {
+ printk( "TLAN: Device %s, PHY 0x%02x (Invalid).\n", dev->name, phy );
+ }
+
+ } /* TLan_PhyPrint */
+
+
+
+
+ /*************************************************************************
+ * TLan_PhySelect
+ *
+ * Returns: Nothing
+ * Parms: dev A pointer to the device structure of the adapter
+ * for which the PHY needs determined.
+ *
+ * This function decides which PHY amoung those attached to the TLAN chip
+ * is to be used. The TLAN chip can be attached to multiple PHYs, and
+ * the driver needs to decide which one to talk to. Currently this
+ * routine picks the PHY with the lowest address as the internal PHY
+ * address is 0x1F, the highest possible. This strategy assumes that
+ * there can be only one other PHY, and, if it exists, it is the one to
+ * be used. If token ring PHYs are ever supported, this routine will
+ * become a little more interesting...
+ *
+ ************************************************************************/
+
+ void TLan_PhySelect( struct device *dev )
+ {
+ int err;
+ int phy;
+ int entry;
+ u16 id_hi;
+ u16 id_lo;
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ u16 val;
+
+ priv->phyCheck = &TLan_PhyNop; // Make absolutely sure these aren't NULL
+ priv->phyService = &TLan_PhyNop;
+
+ for ( phy = 0; phy <= TLAN_PHY_MAX_ADDR; phy++ ) {
+ err = TLan_MiiReadReg( dev->base_addr, phy, 0, &val );
+ if ( ! err ) {
+ TLan_MiiReadReg( dev->base_addr, phy, MII_GEN_ID_HI, &id_hi );
+ TLan_MiiReadReg( dev->base_addr, phy, MII_GEN_ID_LO, &id_lo );
+ entry = 0;
+ while ( ( TLanPhyIdTable[entry].idHi != 0 ) && ( TLanPhyIdTable[entry].idLo != 0 ) ) {
+ if ( ( TLanPhyIdTable[entry].idHi == id_hi ) && ( TLanPhyIdTable[entry].idLo == id_lo ) ) {
+ priv->phyAddr = phy;
+ priv->phyEntry = entry;
+ priv->phyCheck = TLanPhyIdTable[entry].check;
+ priv->phyService = TLanPhyIdTable[entry].service;
+ priv->phyFlags = TLanPhyIdTable[entry].flags;
+ break;
+ }
+ entry++;
+ }
+ break;
+ }
+ }
+
+ } /* TLan_PhySelect */
+
+
+
+
+ /*************************************************************************
+ * TLan_PhyNop
+ *
+ * Returns: Nothing
+ * Parms: dev A pointer to a device structure.
+ *
+ * This function does nothing and is meant as a stand-in for when
+ * a Check or Service function would be meaningless.
+ *
+ ************************************************************************/
+
+ int TLan_PhyNop( struct device *dev )
+ {
+ dev = NULL;
+ return 0;
+
+ } /* TLan_PhyNop */
+
+
+
+
+ /*************************************************************************
+ * TLan_PhyInternalCheck
+ *
+ * Returns: Nothing
+ * Parms: dev A pointer to a device structure of the adapter
+ * holding the PHY to be checked.
+ *
+ * This function resets the internal PHY on a TLAN chip. See Chap. 7,
+ * "Physical Interface (PHY)" of "ThunderLAN Programmer's Guide"
+ *
+ ************************************************************************/
+
+ int TLan_PhyInternalCheck( struct device *dev )
+ {
+ u16 gen_ctl;
+ int i;
+ u32 io;
+ u16 phy;
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ u16 value;
+
+ io = dev->base_addr;
+ phy = priv->phyAddr;
+
+ TLan_MiiReadReg( io, phy, MII_GEN_CTL, &gen_ctl );
+ if ( gen_ctl & MII_GC_PDOWN ) {
+ TLan_MiiSync( io );
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_ISOLATE );
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK );
+ for ( i = 0; i < 500000; i++ )
+ SLOW_DOWN_IO;
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_RESET | MII_GC_LOOPBK );
+ TLan_MiiSync( io );
+ }
+
+ TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value );
+ while ( value & MII_GC_RESET )
+ TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value );
+
+ // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK | MII_GC_DUPLEX );
+ // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_DUPLEX );
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0 );
+
+ for ( i = 0; i < 500000; i++ )
+ SLOW_DOWN_IO;
+
+ // Read Possible Latched Link Status
+ TLan_MiiReadReg( io, phy, MII_GEN_STS, &value );
+ // Read Real Link Status
+ TLan_MiiReadReg( io, phy, MII_GEN_STS, &value );
+ if ( value & MII_GS_LINK ) {
+ priv->phyOnline = 1;
+ TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK );
+ } else {
+ priv->phyOnline = 0;
+ TLan_DioWrite8( io, TLAN_LED_REG, 0 );
+ }
+
+ // Enable Interrupts
+ TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value );
+ value |= TLAN_TC_INTEN;
+ TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value );
+
+ return 0;
+
+ } /* TLanPhyInternalCheck */
+
+
+
+
+ /*************************************************************************
+ * TLan_PhyInternalService
+ *
+ * Returns: Nothing
+ * Parms: dev A pointer to a device structure of the adapter
+ * holding the PHY to be serviced.
+ *
+ * This function services an interrupt generated by the internal PHY.
+ * It can turn on/off the link LED. See Chap. 7,
+ * "Physical Interface (PHY)" of "ThunderLAN Programmer's Guide".
+ *
+ ************************************************************************/
+
+ int TLan_PhyInternalService( struct device *dev )
+ {
+ u16 tlphy_sts;
+ u16 gen_sts;
+ u16 an_exp;
+ u32 io;
+ u16 phy;
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+
+ io = dev->base_addr;
+ phy = priv->phyAddr;
+
+ TLan_MiiReadReg( io, phy, TLAN_TLPHY_STS, &tlphy_sts );
+ TLan_MiiReadReg( io, phy, MII_GEN_STS, &gen_sts );
+ TLan_MiiReadReg( io, phy, MII_AN_EXP, &an_exp );
+ if ( gen_sts & MII_GS_LINK ) {
+ priv->phyOnline = 1;
+ TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK );
+ } else {
+ priv->phyOnline = 0;
+ TLan_DioWrite8( io, TLAN_LED_REG, 0 );
+ }
+
+ return 0;
+
+ } /* TLan_PhyInternalService */
+
+
+
+
+ /*************************************************************************
+ * TLan_PhyDp83840aCheck
+ *
+ * Returns: Nothing
+ * Parms: dev A pointer to a device structure of the adapter
+ * holding the PHY to be reset.
+ *
+ * This function resets a National Semiconductor DP83840A 10/100 Mb/s
+ * PHY device. See National Semiconductor's data sheet for more info.
+ * This PHY is used on Compaq Netelligent 10/100 cards.
+ *
+ ************************************************************************/
+
+ static int TLan_PhyDp83840aCheck( struct device *dev )
+ {
+ u16 gen_ctl;
+ int i;
+ u32 io;
+ u16 phy;
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ u16 value;
+
+ io = dev->base_addr;
+ phy = priv->phyAddr;
+
+ TLan_MiiReadReg( io, phy, MII_GEN_CTL, &gen_ctl );
+ if ( gen_ctl & MII_GC_PDOWN ) {
+ TLan_MiiSync( io );
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_ISOLATE );
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK );
+ for ( i = 0; i < 500000; i++ )
+ SLOW_DOWN_IO;
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_RESET | MII_GC_LOOPBK );
+ TLan_MiiSync( io );
+ }
+
+ TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value );
+ while ( value & MII_GC_RESET )
+ TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value );
+
+ // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK | MII_GC_DUPLEX );
+ // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_DUPLEX );
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0 );
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0x1000 );
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0x1200 );
+
+ for ( i = 0; i < 50000; i++ )
+ SLOW_DOWN_IO;
+
+/*
+ // Read Possible Latched Link Status
+ TLan_MiiReadReg( io, phy, MII_GEN_STS, &value );
+ // Read Real Link Status
+ TLan_MiiReadReg( io, phy, MII_GEN_STS, &value );
+ if ( value & MII_GS_LINK ) {
+ priv->phyOnline = 1;
+ TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK );
+ } else {
+ priv->phyOnline = 0;
+ TLan_DioWrite8( io, TLAN_LED_REG, 0 );
+ }
+
+ // Enable Interrupts
+ TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value );
+ value |= TLAN_TC_INTEN;
+ TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value );
+*/
+ priv->phyOnline = 1;
+
+ return 0;
+
+ } /* TLan_PhyDp83840aCheck */
+
+
+
+
+/*****************************************************************************
+******************************************************************************
+
+ ThunderLAN Driver Adapter Related Routines
+
+******************************************************************************
+*****************************************************************************/
+
+
+
+ static void TLan_ResetLists( struct device * );
+ static void TLan_PrintDio( u16 );
+ static void TLan_PrintList( TLanList *, char *, int );
+ static void TLan_ReadAndClearStats( struct device *, int );
+ static int TLan_Reset( struct device * );
+ static void TLan_SetMac( struct device *, int areg, char *mac );
+
+
+
+
+ /*************************************************************************
+ * TLan_ResetLists
+ *
+ * Returns: Nothing
+ * Parms: dev The device structure with the list stuctures to
+ * be reset.
+ *
+ * This routine sets the variables associated with managing the TLAN
+ * lists to their initial values.
+ *
+ ************************************************************************/
+
+ void TLan_ResetLists( struct device *dev )
+ {
+ int i;
+ TLanList *list;
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+
+ priv->txHead = 0;
+ priv->txTail = 0;
+ for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ ) {
+ list = priv->txList + i;
+ list->cStat = TLAN_CSTAT_UNUSED;
+ list->buffer[0].address = virt_to_bus( priv->txBuffer + ( i * TLAN_MAX_FRAME_SIZE ) );
+ list->buffer[2].count = 0;
+ list->buffer[2].address = 0;
+ }
+
+ priv->rxHead = 0;
+ priv->rxTail = TLAN_NUM_RX_LISTS - 1;
+ for ( i = 0; i < TLAN_NUM_RX_LISTS; i++ ) {
+ list = priv->rxList + i;
+ list->cStat = TLAN_CSTAT_READY;
+ list->frameSize = TLAN_MAX_FRAME_SIZE;
+ list->buffer[0].count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER;
+ list->buffer[0].address = virt_to_bus( priv->rxBuffer + ( i * TLAN_MAX_FRAME_SIZE ) );
+ list->buffer[1].count = 0;
+ list->buffer[1].address = 0;
+ if ( i < TLAN_NUM_RX_LISTS - 1 )
+ list->forward = virt_to_bus( list + 1 );
+ else
+ list->forward = 0;
+ }
+
+ } /* TLan_ResetLists */
+
+
+
+
+ /*************************************************************************
+ * TLan_PrintDio
+ *
+ * Returns: Nothing
+ * Parms: io_base Base IO port of the device of which to print
+ * DIO registers.
+ *
+ * This function prints out all the the internal (DIO) registers of a
+ * TLAN chip.
+ *
+ ************************************************************************/
+
+ void TLan_PrintDio( u16 io_base )
+ {
+ u32 data0, data1;
+ int i;
+
+ printk( "TLAN: Contents of internal registers for io base 0x%04hx.\n", io_base );
+ printk( "TLAN: Off. +0 +4\n" );
+ for ( i = 0; i < 0x4C; i+= 8 ) {
+ data0 = TLan_DioRead32( io_base, i );
+ data1 = TLan_DioRead32( io_base, i + 0x4 );
+ printk( "TLAN: 0x%02x 0x%08x 0x%08x\n", i, data0, data1 );
+ }
+
+ } /* TLan_PrintDio */
+
+
+
+
+ /*************************************************************************
+ * TLan_PrintList
+ *
+ * Returns: Nothing
+ * Parms: list A pointer to the TLanList structure to be printed.
+ * type A string to designate type of list, "Rx" or "Tx".
+ * num The index of the list.
+ *
+ * This function prints out the contents of the list pointed to by the
+ * list parameter.
+ *
+ ************************************************************************/
+
+ void TLan_PrintList( TLanList *list, char *type, int num)
+ {
+ int i;
+
+ printk( "TLAN: %s List %d at 0x%08x\n", type, num, (u32) list );
+ printk( "TLAN: Forward = 0x%08x\n", list->forward );
+ printk( "TLAN: CSTAT = 0x%04hx\n", list->cStat );
+ printk( "TLAN: Frame Size = 0x%04hx\n", list->frameSize );
+ for ( i = 0; i < 10; i++ ) {
+ printk( "TLAN: Buffer[%d].count, addr = 0x%08x, 0x%08x\n", i, list->buffer[i].count, list->buffer[i].address );
+ }
+
+ } /* TLan_PrintList */
+
+
+
+
+ /*************************************************************************
+ * TLan_ReadAndClearStats
+ *
+ * Returns: Nothing
+ * Parms: dev Pointer to device structure of adapter to which
+ * to read stats.
+ * record Flag indicating whether to add
+ *
+ * This functions reads all the internal status registers of the TLAN
+ * chip, which clears them as a side effect. It then either adds the
+ * values to the device's status struct, or discards them, depending
+ * on whether record is TLAN_RECORD (!=0) or TLAN_IGNORE (==0).
+ *
+ ************************************************************************/
+
+ void TLan_ReadAndClearStats( struct device *dev, int record )
+ {
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ u32 tx_good, tx_under;
+ u32 rx_good, rx_over;
+ u32 def_tx, crc, code;
+ u32 multi_col, single_col;
+ u32 excess_col, late_col, loss;
+
+ outw( TLAN_GOOD_TX_FRMS, dev->base_addr + TLAN_DIO_ADR );
+ tx_good = inb( dev->base_addr + TLAN_DIO_DATA );
+ tx_good += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8;
+ tx_good += inb( dev->base_addr + TLAN_DIO_DATA + 2 ) << 16;
+ tx_under = inb( dev->base_addr + TLAN_DIO_DATA + 3 );
+
+ outw( TLAN_GOOD_RX_FRMS, dev->base_addr + TLAN_DIO_ADR );
+ rx_good = inb( dev->base_addr + TLAN_DIO_DATA );
+ rx_good += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8;
+ rx_good += inb( dev->base_addr + TLAN_DIO_DATA + 2 ) << 16;
+ rx_over = inb( dev->base_addr + TLAN_DIO_DATA + 3 );
+
+ outw( TLAN_DEFERRED_TX, dev->base_addr + TLAN_DIO_ADR );
+ def_tx = inb( dev->base_addr + TLAN_DIO_DATA );
+ def_tx += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8;
+ crc = inb( dev->base_addr + TLAN_DIO_DATA + 2 );
+ code = inb( dev->base_addr + TLAN_DIO_DATA + 3 );
+
+ outw( TLAN_MULTICOL_FRMS, dev->base_addr + TLAN_DIO_ADR );
+ multi_col = inb( dev->base_addr + TLAN_DIO_DATA );
+ multi_col += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8;
+ single_col = inb( dev->base_addr + TLAN_DIO_DATA + 2 );
+ single_col += inb( dev->base_addr + TLAN_DIO_DATA + 3 ) << 8;
+
+ outw( TLAN_EXCESSCOL_FRMS, dev->base_addr + TLAN_DIO_ADR );
+ excess_col = inb( dev->base_addr + TLAN_DIO_DATA );
+ late_col = inb( dev->base_addr + TLAN_DIO_DATA + 1 );
+ loss = inb( dev->base_addr + TLAN_DIO_DATA + 2 );
+
+ if ( record ) {
+ priv->stats.rx_packets += rx_good;
+ priv->stats.rx_errors += rx_over + crc + code;
+ priv->stats.tx_packets += tx_good;
+ priv->stats.tx_errors += tx_under + loss;
+ priv->stats.collisions += multi_col + single_col + excess_col + late_col;
+
+ priv->stats.rx_over_errors += rx_over;
+ priv->stats.rx_crc_errors += crc;
+ priv->stats.rx_frame_errors += code;
+
+ priv->stats.tx_aborted_errors += tx_under;
+ priv->stats.tx_carrier_errors += loss;
+ }
+
+ } /* TLan_ReadAndClearStats */
+
+
+
+
+ /*************************************************************************
+ * TLan_Reset
+ *
+ * Returns: 0
+ * Parms: dev Pointer to device structure of adapter to be
+ * reset.
+ *
+ * This function resets the adapter and it's physical device. See
+ * Chap. 3, pp. 9-10 of the "ThunderLAN Programmer's Guide" for details.
+ * The routine tries to implement what is detailed there, though
+ * adjustments have been made.
+ *
+ ************************************************************************/
+
+ int TLan_Reset( struct device *dev )
+ {
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ int i;
+ u32 data;
+ u8 data8;
+
+ // 1. Assume Stat registers have been dealt with.
+ // 2. Assert reset bit.
+ data = inl(dev->base_addr + TLAN_HOST_CMD);
+ data |= TLAN_HC_AD_RST;
+ outl(data, dev->base_addr + TLAN_HOST_CMD);
+ // 3. Turn off interrupts.
+ data = inl(dev->base_addr + TLAN_HOST_CMD);
+ data |= TLAN_HC_INT_OFF;
+ outl(data, dev->base_addr + TLAN_HOST_CMD);
+ // 4. Setup AREGs and HASHs.
+ for ( i = TLAN_AREG_0; i <= TLAN_HASH_2; i += 4 )
+ TLan_DioWrite32( dev->base_addr, (u16) i, 0 );
+ // 5. Setup NetConfig register.
+ outw(TLAN_NET_CONFIG, dev->base_addr + TLAN_DIO_ADR);
+ outw(TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN, dev->base_addr + TLAN_DIO_DATA);
+ // 6. Setup BSIZE register.
+ // Accept defaults, 0x22, for now.
+ // 7. Setup TX commit in Acommit.
+ // Allow it to manage itself.
+ // 8. Load Ld_Tmr in HOST_CMD.
+ // I don't know what this value should be. I'll try 3.
+ outl( TLAN_HC_LD_TMR | 0x0, dev->base_addr + TLAN_HOST_CMD );
+ // 9. Load Ld_Thr in HOST_CMD.
+ // Try 1 for now.
+ outl( TLAN_HC_LD_THR | 0x1, dev->base_addr + TLAN_HOST_CMD );
+ // 10. Unreset the MII by setting NMRST (in NetSio) to 1.
+ outw( TLAN_NET_SIO, dev->base_addr + TLAN_DIO_ADR );
+ TLan_SetBit( TLAN_NET_SIO_NMRST, dev->base_addr + TLAN_DIO_DATA + TLAN_NET_SIO );
+ // 10a. Other.
+ // 12. Setup the NetMask register.
+ TLan_DioWrite8( dev->base_addr, TLAN_INT_DIS, TLAN_ID_TX_EOC | TLAN_ID_RX_EOC );
+ //TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, TLAN_NET_CMD_NRESET | TLAN_NET_CMD_NWRAP | TLAN_NET_CMD_DUPLEX | TLAN_NET_CMD_CAF );
+ TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, TLAN_NET_CMD_NRESET | TLAN_NET_CMD_NWRAP );
+ // 11. Initialize PHYs.
+ TLan_PhySelect( dev );
+ (*priv->phyCheck)( dev );
+ if ( debug >= 1 )
+ TLan_PhyPrint( dev );
+
+ //data8 = TLAN_NET_MASK_MASK4 | TLAN_NET_MASK_MASK5 | TLAN_NET_MASK_MASK6 | TLAN_NET_MASK_MASK7;
+ data8 = TLAN_NET_MASK_MASK4 | TLAN_NET_MASK_MASK5 | TLAN_NET_MASK_MASK7;
+ TLan_DioWrite8( dev->base_addr, TLAN_NET_MASK, data8 );
+ TLan_DioWrite16( dev->base_addr, TLAN_MAX_RX, 1550 );
+ /*
+ * 13. Turn on interrupts to host.
+ * I don't want any interrupts, yet.
+ */
+ /*
+ data = inl(base_port + TLAN_HOST_CMD);
+ data |= TLAN_HC_INT_ON;
+ outl(data, base_port + TLAN_HOST_CMD);
+ */
+
+ return 0;
+
+ } /* TLan_Reset */
+
+
+
+
+ /*************************************************************************
+ * TLan_SetMac
+ *
+ * Returns: Nothing
+ * Parms: dev Pointer to device structure of adapter on which to
+ * change the AREG.
+ * areg The AREG to set the address in (0 - 3).
+ * mac A pointer to an array of chars. Each element
+ * stores one byte of the address. IE, it isn't
+ * in ascii.
+ *
+ * This function transfers a MAC address to one of the TLAN AREGs
+ * (address registers). The TLAN chip locks the register on writing to
+ * offset 0 and unlocks the register after writing to offset 5. If NULL
+ * is passed in mac, then the AREG is filled with 0's.
+ *
+ ************************************************************************/
+
+ void TLan_SetMac( struct device *dev, int areg, char *mac )
+ {
+ int i;
+
+ areg *= 6;
+
+ if ( mac != NULL ) {
+ for ( i = 0; i < 6; i++ )
+ TLan_DioWrite8( dev->base_addr, TLAN_AREG_0 + areg + i, mac[i] );
+ } else {
+ for ( i = 0; i < 6; i++ )
+ TLan_DioWrite8( dev->base_addr, TLAN_AREG_0 + areg + i, 0 );
+ }
+
+ } /* TLan_SetMac */
+
+
+
+
+/*****************************************************************************
+******************************************************************************
+
+ ThunderLAN Driver Eeprom routines
+
+ The Compaq Netelligent 10 and 10/100 cards use a Microchip 24C02A EEPROM.
+ These functions are based on information in Microchip's data sheet. I
+ don't know how well this functions will work with other EEPROMs.
+
+******************************************************************************
+*****************************************************************************/
+
+ static void TLan_EeSendStart( u16 );
+ static int TLan_EeSendByte( u16, u8, int );
+ static void TLan_EeReceiveByte( u16, u8 *, int );
+ static int TLan_EeReadByte( u16, u8, u8 * );
+
+
+
+
+ /*************************************************************************
+ * TLan_EeSendStart
+ *
+ * Returns: Nothing
+ * Parms: io_base The IO port base address for the TLAN device
+ * with the EEPROM to use.
+ *
+ * This function sends a start cycle to an EEPROM attached to a TLAN
+ * chip.
+ *
+ ************************************************************************/
+
+ void TLan_EeSendStart( u16 io_base )
+ {
+ u16 sio;
+
+ outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR );
+ sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO;
+
+ TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+ TLan_SetBit( TLAN_NET_SIO_EDATA, sio );
+ TLan_SetBit( TLAN_NET_SIO_ETXEN, sio );
+ //
+ // FIXME: Do I really need these SLOW_DOWN_IOs?
+ //
+ SLOW_DOWN_IO;
+ TLan_ClearBit( TLAN_NET_SIO_EDATA, sio );
+ SLOW_DOWN_IO;
+ TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
+ SLOW_DOWN_IO;
+
+ } /* TLan_EeSendStart */
+
+
+
+
+ /*************************************************************************
+ * TLan_EeSendByte
+ *
+ * Returns: If the correct ack was received, 0, otherwise 1
+ * Parms: io_base The IO port base address for the TLAN device
+ * with the EEPROM to use.
+ * data The 8 bits of information to send to the
+ * EEPROM.
+ * stop If TLAN_EEPROM_STOP is passed, a stop cycle is
+ * sent after the byte is sent after the ack is
+ * read.
+ *
+ * This function sends a byte on the serial EEPROM line, driving the
+ * clock to send each bit. The function then reverses transmission
+ * direction and reads an acknowledge bit.
+ *
+ ************************************************************************/
+
+ int TLan_EeSendByte( u16 io_base, u8 data, int stop )
+ {
+ int err;
+ u8 place;
+ u16 sio;
+
+ outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR );
+ sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO;
+
+ // Assume clock is low, tx is enabled;
+ for ( place = 0x80; place; place >>= 1 ) {
+ if ( place & data )
+ TLan_SetBit( TLAN_NET_SIO_EDATA, sio );
+ else
+ TLan_ClearBit( TLAN_NET_SIO_EDATA, sio );
+ TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+ TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
+ }
+ TLan_ClearBit( TLAN_NET_SIO_ETXEN, sio );
+ TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+ err = TLan_GetBit( TLAN_NET_SIO_EDATA, sio );
+ TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
+ TLan_SetBit( TLAN_NET_SIO_ETXEN, sio );
+
+ if ( ( ! err ) && stop ) {
+ TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); // STOP, raise data while clock is high
+ TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+ TLan_SetBit( TLAN_NET_SIO_EDATA, sio );
+ }
+
+ return ( err );
+
+ } /* TLan_EeSendByte */
+
+
+
+
+ /*************************************************************************
+ * TLan_EeReceiveByte
+ *
+ * Returns: Nothing
+ * Parms: io_base The IO port base address for the TLAN device
+ * with the EEPROM to use.
+ * data An address to a char to hold the data sent
+ * from the EEPROM.
+ * stop If TLAN_EEPROM_STOP is passed, a stop cycle is
+ * sent after the byte is received, and no ack is
+ * sent.
+ *
+ * This function receives 8 bits of data from the EEPROM over the serial
+ * link. It then sends and ack bit, or no ack and a stop bit. This
+ * function is used to retrieve data after the address of a byte in the
+ * EEPROM has been sent.
+ *
+ ************************************************************************/
+
+ void TLan_EeReceiveByte( u16 io_base, u8 *data, int stop )
+ {
+ u8 place;
+ u16 sio;
+
+ outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR );
+ sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO;
+ *data = 0;
+
+ // Assume clock is low, tx is enabled;
+ TLan_ClearBit( TLAN_NET_SIO_ETXEN, sio );
+ for ( place = 0x80; place; place >>= 1 ) {
+ TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+ if ( TLan_GetBit( TLAN_NET_SIO_EDATA, sio ) )
+ *data |= place;
+ TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
+ }
+
+ TLan_SetBit( TLAN_NET_SIO_ETXEN, sio );
+ if ( ! stop ) {
+ TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); // Ack = 0
+ TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+ TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
+ } else {
+ TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); // No ack = 1 (?)
+ TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+ TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
+ TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); // STOP, raise data while clock is high
+ TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+ TLan_SetBit( TLAN_NET_SIO_EDATA, sio );
+ }
+
+ } /* TLan_EeReceiveByte */
+
+
+
+
+ /*************************************************************************
+ * TLan_EeReadByte
+ *
+ * Returns: No error = 0, else, the stage at which the error occured.
+ * Parms: io_base The IO port base address for the TLAN device
+ * with the EEPROM to use.
+ * ee_addr The address of the byte in the EEPROM whose
+ * contents are to be retrieved.
+ * data An address to a char to hold the data obtained
+ * from the EEPROM.
+ *
+ * This function reads a byte of information from an byte cell in the
+ * EEPROM.
+ *
+ ************************************************************************/
+
+ int TLan_EeReadByte( u16 io_base, u8 ee_addr, u8 *data )
+ {
+ int err;
+
+ cli();
+
+ TLan_EeSendStart( io_base );
+ err = TLan_EeSendByte( io_base, 0xA0, TLAN_EEPROM_ACK );
+ if (err)
+ return 1;
+ err = TLan_EeSendByte( io_base, ee_addr, TLAN_EEPROM_ACK );
+ if (err)
+ return 2;
+ TLan_EeSendStart( io_base );
+ err = TLan_EeSendByte( io_base, 0xA1, TLAN_EEPROM_ACK );
+ if (err)
+ return 3;
+ TLan_EeReceiveByte( io_base, data, TLAN_EEPROM_STOP );
+
+ sti();
+
+ return 0;
+
+ } /* TLan_EeReadByte */
+
+
+
+
+/*****************************************************************************
+******************************************************************************
+
+ ThunderLAN Driver Interrupt Vectors and Table
+
+ Please see Chap. 4, "Interrupt Handling" of the
+ "ThunderLAN Programmer's Guide" for more informations on handling
+ interrupts generated by TLAN based adapters.
+
+******************************************************************************
+*****************************************************************************/
+
+ static u32 TLan_HandleInvalid( struct device *, u16 );
+ static u32 TLan_HandleTxEOF( struct device *, u16 );
+ static u32 TLan_HandleStatOverflow( struct device *, u16 );
+ static u32 TLan_HandleRxEOF( struct device *, u16 );
+ static u32 TLan_HandleDummy( struct device *, u16 );
+ static u32 TLan_HandleTxEOC( struct device *, u16 );
+ static u32 TLan_HandleStatusCheck( struct device *, u16 );
+ static u32 TLan_HandleRxEOC( struct device *, u16 );
+
+
+
+
+ typedef u32 (TLanIntVectorFunc)( struct device *, u16 );
+
+
+
+
+ /*************************************************************************
+ * TLan_HandleInvalid
+ *
+ * Returns: 0
+ * Parms: dev Device assigned the IRQ that was raised.
+ * host_int The contents of the HOST_INT port.
+ *
+ * This function handles invalid interrupts. This should never happen
+ * unless some other adapter is trying to use the IRQ line assigned to
+ * the device.
+ *
+ ************************************************************************/
+
+ u32 TLan_HandleInvalid( struct device *dev, u16 host_int )
+ {
+ host_int = 0;
+ // printk( "TLAN: Invalid interrupt on %s.\n", dev->name );
+ return 0;
+
+ } /* TLan_HandleInvalid */
+
+
+ /*************************************************************************
+ * TLan_HandleTxEOF
+ *
+ * Returns: 1
+ * Parms: dev Device assigned the IRQ that was raised.
+ * host_int The contents of the HOST_INT port.
+ *
+ * This function handles Tx EOF interrupts which are raised by the
+ * adapter when it has completed sending the contents of a buffer. If
+ * detemines which list/buffer was completed and resets it. If the
+ * buffer was the last in the channel (EOC), then the function checks
+ * to see if another buffer is ready to send, and if so, sends a Tx Go
+ * command. Finally, the driver activates/continues the activity LED.
+ *
+ ************************************************************************/
+
+ u32 TLan_HandleTxEOF( struct device *dev, u16 host_int )
+ {
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ int eoc = 0;
+ TLanList *head_list;
+ u32 ack = 1;
+
+ // printk( "TLAN: Handling Tx EOF\n" );
+ host_int = 0;
+ head_list = priv->txList + priv->txHead;
+ if ( head_list->cStat & TLAN_CSTAT_EOC )
+ eoc = 1;
+ if ( ! head_list->cStat & TLAN_CSTAT_FRM_CMP ) {
+ printk( "TLAN: Received interrupt for uncompleted TX frame.\n" );
+ }
+ // printk( "Ack %d CSTAT=%hx\n", priv->txHead, head_list->cStat );
+ head_list->cStat = TLAN_CSTAT_UNUSED;
+ dev->tbusy = 0;
+ priv->txHead++;
+ if ( priv->txHead >= TLAN_NUM_TX_LISTS )
+ priv->txHead = 0;
+ if ( eoc ) {
+ // printk( "TLAN: Handling Tx EOC\n" );
+ head_list = priv->txList + priv->txHead;
+ if ( ( head_list->cStat & TLAN_CSTAT_READY ) == TLAN_CSTAT_READY ) {
+ outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM );
+ ack |= TLAN_HC_GO;
+ } else {
+ priv->txInProgress = 0;
+ }
+ }
+ TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT );
+ if ( priv->phyFlags & TLAN_PHY_ACTIVITY ) {
+ if ( priv->timerSetAt == 0 ) {
+ // printk("TxEOF Starting timer...\n");
+ priv->timerSetAt = jiffies;
+ priv->timer.expires = jiffies + TLAN_TIMER_ACT_DELAY;
+ priv->timerType = TLAN_TIMER_ACT;
+ add_timer( &priv->timer );
+ } else if ( priv->timerType == TLAN_TIMER_ACT ) {
+ priv->timerSetAt = jiffies;
+ // printk("TxEOF continuing timer...\n");
+ }
+ }
+
+ return ack;
+
+ } /* TLan_HandleTxEOF */
+
+
+
+
+ /*************************************************************************
+ * TLan_HandleStatOverflow
+ *
+ * Returns: 1
+ * Parms: dev Device assigned the IRQ that was raised.
+ * host_int The contents of the HOST_INT port.
+ *
+ * This function handles the Statistics Overflow interrupt which means
+ * that one or more of the TLAN statistics registers has reached 1/2
+ * capacity and needs to be read.
+ *
+ ************************************************************************/
+
+ u32 TLan_HandleStatOverflow( struct device *dev, u16 host_int )
+ {
+ host_int = 0;
+ TLan_ReadAndClearStats( dev, TLAN_RECORD );
+
+ return 1;
+
+ } /* TLan_HandleStatOverflow */
+
+
+
+
+ /*************************************************************************
+ * TLan_HandleRxEOF
+ *
+ * Returns: 1
+ * Parms: dev Device assigned the IRQ that was raised.
+ * host_int The contents of the HOST_INT port.
+ *
+ * This function handles the Rx EOF interrupt which indicates a frame
+ * has been received by the adapter from the net and the frame has been
+ * transferred to memory. The function determines the bounce buffer
+ * the frame has been loaded into, creates a new sk_buff big enough to
+ * hold the frame, and sends it to protocol stack. It then resets the
+ * used buffer and appends it to the end of the list. If the frame was
+ * the last in the Rx channel (EOC), the function restarts the receive
+ * channel by sending an Rx Go command to the adapter. Then it activates/
+ * continues the the activity LED.
+ *
+ ************************************************************************/
+
+ u32 TLan_HandleRxEOF( struct device *dev, u16 host_int )
+ {
+ u32 ack = 1;
+ int eoc = 0;
+ u8 *head_buffer;
+ TLanList *head_list;
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ struct sk_buff *skb;
+ TLanList *tail_list;
+ void *t;
+
+ // printk( "TLAN: Handling Rx EOF Head=%d Tail=%d\n", priv->rxHead, priv->rxTail );
+ host_int = 0;
+ head_list = priv->rxList + priv->rxHead;
+ tail_list = priv->rxList + priv->rxTail;
+ if ( head_list->cStat & TLAN_CSTAT_EOC )
+ eoc = 1;
+ if ( ! head_list->cStat & TLAN_CSTAT_FRM_CMP ) {
+ printk( "TLAN: Received interrupt for uncompleted RX frame.\n" );
+ } else {
+ skb = dev_alloc_skb( head_list->frameSize + 7 );
+ if ( skb == NULL ) {
+ printk( "TLAN: Couldn't allocate memory for received data.\n" );
+ } else {
+ head_buffer = priv->rxBuffer + ( priv->rxHead * TLAN_MAX_FRAME_SIZE );
+ skb->dev = dev;
+ skb_reserve( skb, 2 );
+ t = (void *) skb_put( skb, head_list->frameSize );
+ // printk( " %hd %p %p\n", head_list->frameSize, skb->data, t );
+ memcpy( t, head_buffer, head_list->frameSize );
+ skb->protocol = eth_type_trans( skb, dev );
+ netif_rx( skb );
+ }
+ }
+ head_list->forward = 0;
+ head_list->frameSize = TLAN_MAX_FRAME_SIZE;
+ head_list->buffer[0].count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER;
+ tail_list->forward = virt_to_bus( head_list );
+ priv->rxHead++;
+ if ( priv->rxHead >= TLAN_NUM_RX_LISTS )
+ priv->rxHead = 0;
+ priv->rxTail++;
+ if ( priv->rxTail >= TLAN_NUM_RX_LISTS )
+ priv->rxTail = 0;
+ if ( eoc ) {
+ // printk( "TLAN: Handling Rx EOC Head=%d Tail=%d\n", priv->rxHead, priv->rxTail );
+ head_list = priv->rxList + priv->rxHead;
+ outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM );
+ ack |= TLAN_HC_GO | TLAN_HC_RT;
+ priv->rxEocCount++;
+ }
+ TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT );
+ if ( priv->phyFlags & TLAN_PHY_ACTIVITY ) {
+ if ( priv->timerSetAt == 0 ) {
+ // printk("RxEOF Starting timer...\n");
+ priv->timerSetAt = jiffies;
+ priv->timer.expires = jiffies + TLAN_TIMER_ACT_DELAY;
+ priv->timerType = TLAN_TIMER_ACT;
+ add_timer( &priv->timer );
+ } else if ( priv->timerType == TLAN_TIMER_ACT ) {
+ // printk("RxEOF tarting continuing timer...\n");
+ priv->timerSetAt = jiffies;
+ }
+ }
+ dev->last_rx = jiffies;
+
+ return ack;
+
+ } /* TLan_HandleRxEOF */
+
+
+
+
+ /*************************************************************************
+ * TLan_HandleDummy
+ *
+ * Returns: 1
+ * Parms: dev Device assigned the IRQ that was raised.
+ * host_int The contents of the HOST_INT port.
+ *
+ * This function handles the Dummy interrupt, which is raised whenever
+ * a test interrupt is generated by setting the Req_Int bit of HOST_CMD
+ * to 1.
+ *
+ ************************************************************************/
+
+ u32 TLan_HandleDummy( struct device *dev, u16 host_int )
+ {
+ host_int = 0;
+ printk( "TLAN: Dummy interrupt on %s.\n", dev->name );
+ return 1;
+
+ } /* TLan_HandleDummy */
+
+
+
+
+ /*************************************************************************
+ * TLan_HandleTxEOC
+ *
+ * Returns: 1
+ * Parms: dev Device assigned the IRQ that was raised.
+ * host_int The contents of the HOST_INT port.
+ *
+ * This driver is structured to determine EOC occurances by reading the
+ * CSTAT member of the list structure. Tx EOC interrupts are disabled
+ * via the DIO INTDIS register.
+ *
+ ************************************************************************/
+
+ u32 TLan_HandleTxEOC( struct device *dev, u16 host_int )
+ {
+ host_int = 0;
+ printk( "TLAN: Tx EOC interrupt on %s.\n", dev->name );
+ return 1;
+
+ } /* TLan_HandleTxEOC */
+
+
+
+
+ /*************************************************************************
+ * TLan_HandleStatusCheck
+ *
+ * Returns: 0 if Adapter check, 1 if Network Status check.
+ * Parms: dev Device assigned the IRQ that was raised.
+ * host_int The contents of the HOST_INT port.
+ *
+ * This function handles Adapter Check/Network Status interrupts
+ * generated by the adapter. It checks the vector in the HOST_INT
+ * register to determine if it is an Adapter Check interrupt. If so,
+ * it resets the adapter. Otherwise it clears the status registers
+ * and services the PHY.
+ *
+ ************************************************************************/
+
+ u32 TLan_HandleStatusCheck( struct device *dev, u16 host_int )
+ {
+ u32 ack;
+ u32 error;
+ u8 net_sts;
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+
+ ack = 1;
+ if ( host_int & TLAN_HI_IV_MASK ) {
+ error = inl( dev->base_addr + TLAN_CH_PARM );
+ printk( "TLAN: Adaptor Check on device %s err = 0x%x\n", dev->name, error );
+ TLan_ReadAndClearStats( dev, TLAN_RECORD );
+ outl( TLAN_HC_AD_RST, dev->base_addr + TLAN_HOST_CMD );
+ TLan_ResetLists( dev );
+ TLan_Reset( dev );
+ dev->tbusy = 0;
+ TLan_SetMac( dev, 0, dev->dev_addr );
+ if ( priv->timerType == 0 ) {
+ if ( priv->phyFlags & TLAN_PHY_AUTONEG ) {
+ priv->timer.expires = jiffies + TLAN_TIMER_LINK_DELAY;
+ priv->timerSetAt = jiffies;
+ priv->timerType = TLAN_TIMER_LINK;
+ add_timer( &priv->timer );
+ } else {
+ //printk( " RX GO---->\n" );
+ outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM );
+ outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD );
+ }
+ }
+ ack = 0;
+ } else {
+ net_sts = TLan_DioRead8( dev->base_addr, TLAN_NET_STS );
+ if ( net_sts )
+ TLan_DioWrite8( dev->base_addr, TLAN_NET_STS, net_sts );
+ if ( net_sts & TLAN_NET_STS_MIRQ )
+ (*priv->phyService)( dev );
+
+ TLAN_DBG( 1, "TLAN: Status Check! %s Net_Sts=%x\n", dev->name, (unsigned) net_sts );
+ }
+
+ return ack;
+
+ } /* TLan_HandleStatusCheck */
+
+
+
+
+ /*************************************************************************
+ * TLan_HandleRxEOC
+ *
+ * Returns: 1
+ * Parms: dev Device assigned the IRQ that was raised.
+ * host_int The contents of the HOST_INT port.
+ *
+ * This driver is structured to determine EOC occurances by reading the
+ * CSTAT member of the list structure. Rx EOC interrupts are disabled
+ * via the DIO INTDIS register.
+ *
+ ************************************************************************/
+
+ u32 TLan_HandleRxEOC( struct device *dev, u16 host_int )
+ {
+ host_int = 0;
+ printk( "TLAN: Rx EOC interrupt on %s.\n", dev->name );
+ return 1;
+
+ } /* TLan_HandleRxEOC */
+
+
+
+ static TLanIntVectorFunc *TLanIntVector[TLAN_INT_NUMBER_OF_INTS] = {
+ TLan_HandleInvalid,
+ TLan_HandleTxEOF,
+ TLan_HandleStatOverflow,
+ TLan_HandleRxEOF,
+ TLan_HandleDummy,
+ TLan_HandleTxEOC,
+ TLan_HandleStatusCheck,
+ TLan_HandleRxEOC
+ };
+
+
+
+
+/*****************************************************************************
+******************************************************************************
+
+ ThunderLAN Driver Timer Function
+
+******************************************************************************
+*****************************************************************************/
+
+ static void TLan_Timer( unsigned long );
+
+
+
+
+ /*************************************************************************
+ * TLan_Timer
+ *
+ * Returns: Nothing
+ * Parms: data A value given to add timer when add_timer was
+ * called.
+ *
+ * This function handles timed functionality for the TLAN driver. The
+ * two current timer uses are for delaying for autonegotionation and
+ * driving the ACT LED.
+ * - Autonegotiation requires being allowed about 2 1/2 seconds before
+ * attempting to transmit a packet. It would be a very bad thing
+ * to hang the kernel this long, so the driver doesn't allow
+ * transmission 'til after this time, for certain PHYs. It would
+ * be much nicer if all PHYs were interrupt-capable like the
+ * internal PHY.
+ * - The ACT LED, which shows adapter activity, is driven by the driver,
+ * and so must be left on for a short period to power up the LED so
+ * it can be seen. This delay can be changed by changing the
+ * TLAN_TIMER_ACT_DELAY in tlan.h, if desired. 10 jiffies produces a
+ * slightly sluggish response.
+ *
+ ************************************************************************/
+
+ void TLan_Timer( unsigned long data )
+ {
+ struct device *dev = (struct device *) data;
+ u16 gen_sts;
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+
+ // printk( "TLAN: %s Entered Timer, type = %d\n", dev->name, priv->timerType );
+
+ switch ( priv->timerType ) {
+ case TLAN_TIMER_LINK:
+ TLan_MiiReadReg( dev->base_addr, priv->phyAddr, MII_GEN_STS, &gen_sts );
+ if ( gen_sts & MII_GS_LINK ) {
+ priv->phyOnline = 1;
+ outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM );
+ outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD );
+ priv->timerSetAt = 0;
+ priv->timerType = 0;
+ } else {
+ priv->timer.expires = jiffies + ( TLAN_TIMER_LINK_DELAY * 2 );
+ add_timer( &priv->timer );
+ }
+ break;
+ case TLAN_TIMER_ACT:
+ if ( jiffies - priv->timerSetAt >= TLAN_TIMER_ACT_DELAY ) {
+ TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK );
+ priv->timerSetAt = 0;
+ priv->timerType = 0;
+ } else {
+ priv->timer.expires = priv->timerSetAt + TLAN_TIMER_ACT_DELAY;
+ add_timer( &priv->timer );
+ }
+ break;
+ default:
+ break;
+ }
+
+ } /* TLan_Timer */
+
+
+
+
+/*****************************************************************************
+******************************************************************************
+
+ ThunderLAN Driver Primary Functions
+
+ These functions are more or less common to all Linux network drivers.
+
+******************************************************************************
+*****************************************************************************/
+
+ static int TLan_PciProbe( u8 *, u8 *, u8 *, u8 *, u32 *, u32 * );
+ static int TLan_Init( struct device * );
+ static int TLan_Open(struct device *dev);
+ static int TLan_StartTx(struct sk_buff *, struct device *);
+ static void TLan_HandleInterrupt(int, void *, struct pt_regs *);
+ static int TLan_Close(struct device *);
+ static struct enet_statistics *TLan_GetStats( struct device * );
+ static void TLan_SetMulticastList( struct device * );
+
+
+#ifdef MODULE
+
+ /*************************************************************************
+ * init_module
+ *
+ * Returns: 0 if module installed ok, non-zero if not.
+ * Parms: None
+ *
+ * This function begins the setup of the driver creating a pad buffer,
+ * finding all TLAN devices (matching TLanDeviceList entries), and
+ * creating and initializing a device structure for each adapter.
+ *
+ ************************************************************************/
+
+ extern int init_module(void)
+ {
+ int failed, found;
+ size_t dev_size;
+ struct device *dev;
+ TLanPrivateInfo *priv;
+ u8 bus, dfn, irq, rev;
+ u32 io_base, dl_ix;
+
+ printk("TLAN driver, v%d.%d, (C) 1997 Caldera, Inc.\n",
+ TLanVersionMajor,
+ TLanVersionMinor
+ );
+
+ TLanPadBuffer = (u8 *) kmalloc( TLAN_MIN_FRAME_SIZE, GFP_KERNEL | GFP_DMA );
+ if ( TLanPadBuffer == NULL ) {
+ printk( "TLAN: Could not allocate memory for pad buffer.\n" );
+ return -ENOMEM;
+ }
+ memset( TLanPadBuffer, 0, TLAN_MIN_FRAME_SIZE );
+
+ dev_size = sizeof(struct device) + sizeof(TLanPrivateInfo);
+ while ( ( found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &dl_ix ) ) ) {
+ dev = (struct device *) kmalloc( dev_size, GFP_KERNEL );
+ if ( dev == NULL ) {
+ printk( "TLAN: Could not allocate memory for device.\n" );
+ continue;
+ }
+ memset( dev, 0, dev_size );
+
+ dev->priv = priv = ( (void *) dev ) + sizeof(struct device);
+ dev->name = priv->devName;
+ strcpy( priv->devName, " " );
+ dev->base_addr = io_base;
+ dev->irq = irq;
+ dev->init = TLan_Init;
+
+ priv->pciBus = bus;
+ priv->pciDeviceFn = dfn;
+ priv->pciRevision = rev;
+ priv->pciEntry = dl_ix;
+
+ ether_setup( dev );
+
+ failed = register_netdev( dev );
+
+ if ( failed ) {
+ printk( "TLAN: Could not register network device. Freeing struct.\n" );
+ kfree( dev );
+ } else {
+ priv->nextDevice = TLanDevices;
+ TLanDevices = dev;
+ TLanDevicesInstalled++;
+ printk("TLAN: %s irq=%2d io=%04x, %s\n", dev->name, (int) irq, io_base, TLanDeviceList[dl_ix].deviceName );
+ }
+ }
+
+ // printk( "TLAN: Found %d device(s).\n", TLanDevicesInstalled );
+
+ return ( ( TLanDevicesInstalled >= 0 ) ? 0 : -ENODEV );
+
+ } /* init_module */
+
+
+
+
+ /*************************************************************************
+ * cleanup_module
+ *
+ * Returns: Nothing
+ * Parms: None
+ *
+ * Goes through the TLanDevices list and frees the device structs and
+ * memory associated with each device (lists and buffers). It also
+ * ureserves the IO port regions associated with this device.
+ *
+ ************************************************************************/
+
+ extern void cleanup_module(void)
+ {
+ struct device *dev;
+ TLanPrivateInfo *priv;
+
+ while (TLanDevicesInstalled) {
+ dev = TLanDevices;
+ priv = (TLanPrivateInfo *) dev->priv;
+ if ( priv->dmaStorage )
+ kfree( priv->dmaStorage );
+ release_region( dev->base_addr, 0x10 );
+ unregister_netdev( dev );
+ TLanDevices = priv->nextDevice;
+ kfree( dev );
+ TLanDevicesInstalled--;
+ }
+ kfree( TLanPadBuffer );
+
+ } /* cleanup_module */
+
+
+#else /* MODULE */
+
+
+
+
+ /*************************************************************************
+ * tlan_probe
+ *
+ * Returns: 0 on success, error code on error
+ * Parms: dev device struct to use if adapter is found.
+ *
+ * The name is lower case to fit in with all the rest of the
+ * netcard_probe names. This function looks for a/another TLan based
+ * adapter, setting it up with the provided device struct if one is
+ * found.
+ *
+ ************************************************************************/
+
+ extern int tlan_probe( struct device *dev )
+ {
+ static int pad_allocated = 0;
+ int found;
+ TLanPrivateInfo *priv;
+ u8 bus, dfn, irq, rev;
+ u32 io_base, dl_ix;
+
+ found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &dl_ix );
+ TLAN_DBG( 1, "TLAN: Probing...\n" );
+ if ( found ) {
+ dev->priv = kmalloc( sizeof(TLanPrivateInfo), GFP_KERNEL );
+ if ( dev->priv == NULL ) {
+ printk( "TLAN: Could not allocate memory for device.\n" );
+ }
+ memset( dev->priv, 0, sizeof(TLanPrivateInfo) );
+ priv = (TLanPrivateInfo *) dev->priv;
+
+ dev->name = priv->devName;
+ strcpy( priv->devName, " " );
+
+ dev = init_etherdev( dev, sizeof(TLanPrivateInfo) );
+
+ dev->base_addr = io_base;
+ dev->irq = irq;
+
+ priv->pciBus = bus;
+ priv->pciDeviceFn = dfn;
+ priv->pciRevision = rev;
+ priv->pciEntry = dl_ix;
+
+ if ( ! pad_allocated ) {
+ TLanPadBuffer = (u8 *) kmalloc( TLAN_MIN_FRAME_SIZE, GFP_KERNEL | GFP_DMA );
+ if ( TLanPadBuffer == NULL ) {
+ printk( "TLAN: Could not allocate memory for pad buffer.\n" );
+ } else {
+ pad_allocated = 1;
+ memset( TLanPadBuffer, 0, TLAN_MIN_FRAME_SIZE );
+ }
+ }
+ printk("TLAN %d.%d: %s irq=%2d io=%04x, %s\n", TLanVersionMajor,
+ TLanVersionMinor,
+ dev->name,
+ (int) irq,
+ io_base,
+ TLanDeviceList[dl_ix].deviceName );
+ TLan_Init( dev );
+ }
+
+ return ( ( found ) ? 0 : -ENODEV );
+
+ } /* tlan_probe */
+
+
+#endif /* MODULE */
+
+
+
+
+ /*************************************************************************
+ * TLan_PciProbe
+ *
+ * Returns: 1 if another TLAN card was found, 0 if not.
+ * Parms: pci_bus The PCI bus the card was found on.
+ * pci_dfn The PCI whatever the card was found at.
+ * pci_irq The IRQ of the found adapter.
+ * pci_rev The revision of the adapter.
+ * pci_io_base The first IO port used by the adapter.
+ * dl_ix The index in the device list of the adapter.
+ *
+ * This function searches for an adapter with PCI vendor and device
+ * IDs matching those in the TLanDeviceList. The function 'remembers'
+ * the last device it found, and so finds a new device (if anymore are
+ * to be found) each time the function is called. It then looks up
+ * pertinent PCI info and returns it to the caller.
+ *
+ ************************************************************************/
+
+ int TLan_PciProbe( u8 *pci_bus, u8 *pci_dfn, u8 *pci_irq, u8 *pci_rev, u32 *pci_io_base, u32 *dl_ix )
+ {
+ static int dl_index = 0;
+ static int pci_index = 0;
+
+ int not_found;
+ u8 pci_latency;
+ u16 pci_command;
+
+
+ if ( ! pcibios_present() ) {
+ printk( "TLAN: PCI Bios not present.\n" );
+ return 0;
+ }
+
+ for (; TLanDeviceList[dl_index].vendorId != 0; dl_index++) {
+ not_found = pcibios_find_device( TLanDeviceList[dl_index].vendorId,
+ TLanDeviceList[dl_index].deviceId,
+ pci_index,
+ pci_bus,
+ pci_dfn
+ );
+ if ( ! not_found ) {
+ TLAN_DBG( 1, "TLAN: found: Vendor Id = 0x%hx, Device Id = 0x%hx\n",
+ TLanDeviceList[dl_index].vendorId,
+ TLanDeviceList[dl_index].deviceId
+ );
+
+ pcibios_read_config_byte ( *pci_bus, *pci_dfn, PCI_REVISION_ID, pci_rev);
+ pcibios_read_config_byte ( *pci_bus, *pci_dfn, PCI_INTERRUPT_LINE, pci_irq);
+ pcibios_read_config_word ( *pci_bus, *pci_dfn, PCI_COMMAND, &pci_command);
+ pcibios_read_config_dword( *pci_bus, *pci_dfn, PCI_BASE_ADDRESS_0, pci_io_base);
+ pcibios_read_config_byte ( *pci_bus, *pci_dfn, PCI_LATENCY_TIMER, &pci_latency);
+
+ if (pci_latency < 0x10) {
+ pcibios_write_config_byte( *pci_bus, *pci_dfn, PCI_LATENCY_TIMER, 0xff);
+ TLAN_DBG( 1, "TLAN: Setting latency timer to max.\n");
+ }
+
+ if ((pci_command & PCI_COMMAND_IO) && (*pci_io_base & 0x3)) {
+ *pci_io_base &= PCI_BASE_ADDRESS_IO_MASK;
+ TLAN_DBG( 1, "TLAN: IO mapping is available at %x.\n", *pci_io_base);
+ } else {
+ *pci_io_base = 0;
+ printk("TLAN: IO mapping not available, ignoring device.\n");
+ }
+
+ if (pci_command & PCI_COMMAND_MASTER) {
+ TLAN_DBG( 1, "TLAN: Bus mastering is active.\n");
+ }
+
+ pci_index++;
+
+ if ( *pci_io_base ) {
+ *dl_ix = dl_index;
+ return 1;
+ }
+
+ } else {
+ pci_index = 0;
+ }
+ }
+
+ return 0;
+
+ } /* TLan_PciProbe */
+
+
+
+
+ /*************************************************************************
+ * TLan_Init
+ *
+ * Returns: 0 on success, error code otherwise.
+ * Parms: dev The structure of the device to be init'ed.
+ *
+ * This function completes the initialization of the device structure
+ * and driver. It reserves the IO addresses, allocates memory for the
+ * lists and bounce buffers, retrieves the MAC address from the eeprom
+ * and assignes the device's methods.
+ *
+ ************************************************************************/
+
+ int TLan_Init( struct device *dev )
+ {
+ int dma_size;
+ int err;
+ int i;
+ TLanPrivateInfo *priv;
+
+ priv = (TLanPrivateInfo *) dev->priv;
+
+ err = check_region( dev->base_addr, 0x10 );
+ if ( err ) {
+ printk( "TLAN: %s: Io port region 0x%lx size 0x%x in use.\n", dev->name, dev->base_addr, 0x10 );
+ return -EIO;
+ }
+ request_region( dev->base_addr, 0x10, TLanSignature );
+
+ dma_size = ( TLAN_NUM_RX_LISTS + TLAN_NUM_TX_LISTS ) * ( sizeof(TLanList) + TLAN_MAX_FRAME_SIZE );
+ priv->dmaStorage = kmalloc( dma_size, GFP_KERNEL | GFP_DMA );
+ if ( priv->dmaStorage == NULL ) {
+ printk( "TLAN: Could not allocate lists and buffers for %s.\n", dev->name );
+ return -ENOMEM;
+ }
+ memset( priv->dmaStorage, 0, dma_size );
+ priv->rxList = (TLanList *) ( ( ( (u32) priv->dmaStorage ) + 7 ) & 0xFFFFFFF8 );
+ priv->txList = priv->rxList + TLAN_NUM_RX_LISTS;
+ priv->rxBuffer = (u8 *) ( priv->txList + TLAN_NUM_TX_LISTS );
+ priv->txBuffer = priv->rxBuffer + ( TLAN_NUM_RX_LISTS * TLAN_MAX_FRAME_SIZE );
+
+ err = 0;
+ for ( i = 0; i < 6 ; i++ )
+ err |= TLan_EeReadByte( dev->base_addr, (u8) 0x83 + i, (u8 *) &dev->dev_addr[i] );
+ if ( err )
+ printk( "TLAN: %s: Error reading MAC Address from eeprom: %d\n", dev->name, err );
+ dev->addr_len = 6;
+
+ dev->open = &TLan_Open;
+ dev->hard_start_xmit = &TLan_StartTx;
+ dev->stop = &TLan_Close;
+ dev->get_stats = &TLan_GetStats;
+ dev->set_multicast_list = &TLan_SetMulticastList;
+
+ return 0;
+
+ } /* TLan_Init */
+
+
+
+
+ /*************************************************************************
+ * TLan_Open
+ *
+ * Returns: 0 on success, error code otherwise.
+ * Parms: dev Structure of device to be opened.
+ *
+ * This routine puts the driver and TLAN adapter in a state where it is
+ * ready to send and receive packets. It allocates the IRQ, resets and
+ * brings the adapter out of reset, and allows interrupts. It also
+ * delays the startup for autonegotiation or sends a Rx GO command to
+ * the adapter, as appropriate.
+ *
+ ************************************************************************/
+
+ int TLan_Open( struct device *dev )
+ {
+ int err;
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+
+ err = request_irq( dev->irq, TLan_HandleInterrupt, SA_SHIRQ, TLanSignature, dev );
+ if ( err ) {
+ printk( "TLAN: %s: IRQ %d already in use.\n", dev->name, dev->irq );
+ return -EAGAIN;
+ }
+
+ MOD_INC_USE_COUNT;
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+
+ /* NOTE: It might not be necessary to read the stats before a
+ reset if you don't care what the values are.
+ */
+ TLan_ResetLists( dev );
+ TLan_ReadAndClearStats( dev, TLAN_IGNORE );
+ TLan_Reset( dev );
+ TLan_SetMac( dev, 0, dev->dev_addr );
+ outb( ( TLAN_HC_INT_ON >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 );
+ if ( debug >= 1 )
+ outb( ( TLAN_HC_REQ_INT >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 );
+
+ init_timer( &priv->timer );
+ priv->timer.data = (unsigned long) dev;
+ priv->timer.function = &TLan_Timer;
+ if ( priv->phyFlags & TLAN_PHY_AUTONEG ) {
+ priv->timer.expires = jiffies + TLAN_TIMER_LINK_DELAY;
+ priv->timerSetAt = jiffies;
+ priv->timerType = TLAN_TIMER_LINK;
+ add_timer( &priv->timer );
+ } else {
+ TLAN_DBG( 1, "TLAN: RX GO\n" );
+ outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM );
+ outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD );
+ }
+
+ TLAN_DBG( 1, "TLAN: Device %s opened.\n", dev->name );
+
+ return 0;
+
+ } /* TLan_Open */
+
+
+
+
+ /*************************************************************************
+ * TLan_StartTx
+ *
+ * Returns: 0 on success, non-zero on failure.
+ * Parms: skb A pointer to the sk_buff containing the frame to
+ * be sent.
+ * dev The device to send the data on.
+ *
+ * This function adds a frame to the Tx list to be sent ASAP. First it
+ * verifies that the adapter is ready and there is room in the queue.
+ * Then it sets up the next available list, copies the frame to the
+ * corresponding buffer. If the adapter Tx channel is idle, it gives
+ * adapter a Tx Go command on the list, otherwise it sets the forward
+ * address of the previous list to point to this one. Then it frees
+ * the sk_buff.
+ *
+ ************************************************************************/
+
+ int TLan_StartTx( struct sk_buff *skb, struct device *dev )
+ {
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ TLanList *tail_list;
+ u8 *tail_buffer;
+ int pad;
+
+ // printk( "Entering StartTx\n" );
+ if ( ! priv->phyOnline ) {
+ dev_kfree_skb( skb, FREE_WRITE );
+ return 0;
+ }
+
+ tail_list = priv->txList + priv->txTail;
+ if ( tail_list->cStat != TLAN_CSTAT_UNUSED ) {
+ // printk( "TLAN: %s TX is busy, Head=%d Tail=%d\n", dev->name, priv->txHead, priv->txTail );
+ dev->tbusy = 1;
+ // printk( "TLAN: Tx is busy.\n");
+ priv->txBusyCount++;
+ return 1;
+ }
+ tail_list->forward = 0;
+ tail_buffer = priv->txBuffer + ( priv->txTail * TLAN_MAX_FRAME_SIZE );
+ memcpy( tail_buffer, skb->data, skb->len );
+ pad = TLAN_MIN_FRAME_SIZE - skb->len;
+ if ( pad > 0 ) {
+ tail_list->frameSize = (u16) skb->len + pad;
+ tail_list->buffer[0].count = (u32) skb->len;
+ tail_list->buffer[1].count = TLAN_LAST_BUFFER | (u32) pad;
+ tail_list->buffer[1].address = virt_to_bus( TLanPadBuffer );
+ } else {
+ tail_list->frameSize = (u16) skb->len;
+ tail_list->buffer[0].count = TLAN_LAST_BUFFER | (u32) skb->len;
+ tail_list->buffer[1].count = 0;
+ tail_list->buffer[1].address = 0;
+ }
+ // are we transferring?
+ cli();
+ tail_list->cStat = TLAN_CSTAT_READY;
+ if ( ! priv->txInProgress ) {
+ priv->txInProgress = 1;
+ outw( 0x4, dev->base_addr + TLAN_HOST_INT );
+ // printk("TLAN: Sending GO for 0%d\n", priv->txTail );
+ outl( virt_to_bus( tail_list ), dev->base_addr + TLAN_CH_PARM );
+ outl( TLAN_HC_GO | TLAN_HC_ACK, dev->base_addr + TLAN_HOST_CMD );
+ } else {
+ // printk("TLAN: Adding 0%d\n", priv->txTail );
+ // Assign previous list to point to this one. If previous has
+ // already been read, the EOC check in the TX EOF interrupt handler
+ // will start another transfer.
+ if ( priv->txTail == 0 )
+ ( priv->txList + ( TLAN_NUM_TX_LISTS - 1 ) )->forward = virt_to_bus( tail_list );
+ else
+ ( priv->txList + ( priv->txTail - 1 ) )->forward = virt_to_bus( tail_list );
+ }
+ sti();
+ priv->txTail++;
+ if ( priv->txTail >= TLAN_NUM_TX_LISTS )
+ priv->txTail = 0;
+
+ dev_kfree_skb( skb, FREE_WRITE );
+
+ dev->trans_start = jiffies;
+ // printk( "Leaving StartTx\n" );
+ return 0;
+
+ } /* TLan_StartTx */
+
+
+
+
+ /*************************************************************************
+ * TLan_HandleInterrupt
+ *
+ * Returns: Nothing
+ * Parms: irq The line on which the interrupt occurred.
+ * dev_id A pointer to the device assigned to this irq line.
+ * regs ???
+ *
+ * This function handles an interrupt generated by its assigned TLAN
+ * adapter. The function deactivates interrupts on its adapter, records
+ * the type of interrupt, executes the appropriate subhandler, and
+ * acknowdges the interrupt to the adapter (thus re-enabling adapter
+ * interrupts.
+ *
+ ************************************************************************/
+
+ void TLan_HandleInterrupt(int irq, void *dev_id, struct pt_regs *regs)
+ {
+ u32 ack;
+ struct device *dev;
+ u32 host_cmd;
+ u16 host_int;
+ int type;
+
+ dev = (struct device *) dev_id;
+
+ if ( dev->interrupt )
+ TLAN_DBG( 1, "TLAN: Re-entering interrupt handler for %s: %d.\n" , dev->name, dev->interrupt );
+ dev->interrupt++;
+
+ cli();
+
+ host_int = inw( dev->base_addr + TLAN_HOST_INT );
+ outw( host_int, dev->base_addr + TLAN_HOST_INT ); // Deactivate Ints
+
+ type = ( host_int & TLAN_HI_IT_MASK ) >> 2;
+
+ ack = TLanIntVector[type]( dev, host_int );
+
+ sti();
+
+ if ( ack ) {
+ host_cmd = TLAN_HC_ACK | ack | ( type << 18 );
+ outl( host_cmd, dev->base_addr + TLAN_HOST_CMD );
+ }
+
+ dev->interrupt--;
+
+ } /* TLan_HandleInterrupts */
+
+
+
+
+ /*************************************************************************
+ * TLan_Close
+ *
+ * Returns: An error code.
+ * Parms: dev The device structure of the device to close.
+ *
+ * This function shuts down the adapter. It records any stats, puts
+ * the adapter into reset state, deactivates its time as needed, and
+ * frees the irq it is using.
+ *
+ ************************************************************************/
+
+ int TLan_Close(struct device *dev)
+ {
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+
+ dev->start = 0;
+ dev->tbusy = 1;
+
+ TLan_ReadAndClearStats( dev, TLAN_RECORD );
+ outl( TLAN_HC_AD_RST, dev->base_addr + TLAN_HOST_CMD );
+ if ( priv->timerSetAt != 0 )
+ del_timer( &priv->timer );
+ free_irq( dev->irq, dev );
+ TLAN_DBG( 1, "TLAN: Device %s closed.\n", dev->name );
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+
+ } /* TLan_Close */
+
+
+
+
+ /*************************************************************************
+ * TLan_GetStats
+ *
+ * Returns: A pointer to the device's statistics structure.
+ * Parms: dev The device structure to return the stats for.
+ *
+ * This function updates the devices statistics by reading the TLAN
+ * chip's onboard registers. Then it returns the address of the
+ * statistics structure.
+ *
+ ************************************************************************/
+
+ struct enet_statistics *TLan_GetStats( struct device *dev )
+ {
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ int i;
+
+ /* Should only read stats if open ? */
+ TLan_ReadAndClearStats( dev, TLAN_RECORD );
+ printk( "TLAN: %s Rx EOC Count: %d\n", dev->name, priv->rxEocCount );
+ printk( "TLAN: %s Tx Busy Count: %d\n", dev->name, priv->txBusyCount );
+ // printk( "TLAN: Got stats for %s.\n", dev->name );
+ TLan_PrintDio( dev->base_addr );
+ TLan_PhyPrint( dev );
+ for ( i = 0; i < TLAN_NUM_RX_LISTS; i++ )
+ TLan_PrintList( priv->rxList + i, "RX", i );
+ for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ )
+ TLan_PrintList( priv->txList + i, "TX", i );
+
+ return ( &( (TLanPrivateInfo *) dev->priv )->stats );
+
+ } /* TLan_GetStats */
+
+
+
+
+ /*************************************************************************
+ * TLan_SetMulticastList
+ *
+ * Returns: Nothing
+ * Parms: dev The device structure to set the multicast list for.
+ *
+ * This function sets the TLAN adaptor to various receive modes. If the
+ * IFF_PROMISC flag is set, promiscuous mode is acitviated. Otherwise,
+ * promiscuous mode is turned off. If the IFF_ALLMULTI flag is set, then
+ * the hash table is set to receive all group addresses. Otherwise, the
+ * first three multicast addresses are stored in AREG_1-3, and the rest
+ * are selected via the hash table, as necessary.
+ *
+ ************************************************************************/
+
+ void TLan_SetMulticastList( struct device *dev )
+ {
+ struct dev_mc_list *dmi = dev->mc_list;
+ u32 hash1 = 0;
+ u32 hash2 = 0;
+ int i;
+ u32 offset;
+ u8 tmp;
+
+ if ( dev->flags & IFF_PROMISC ) {
+ tmp = TLan_DioRead8( dev->base_addr, TLAN_NET_CMD );
+ TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, tmp | TLAN_NET_CMD_CAF );
+ } else {
+ tmp = TLan_DioRead8( dev->base_addr, TLAN_NET_CMD );
+ TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, tmp & ~TLAN_NET_CMD_CAF );
+ if ( dev->flags & IFF_ALLMULTI ) {
+ for ( i = 0; i < 3; i++ )
+ TLan_SetMac( dev, i + 1, NULL );
+ TLan_DioWrite32( dev->base_addr, TLAN_HASH_1, 0xFFFFFFFF );
+ TLan_DioWrite32( dev->base_addr, TLAN_HASH_2, 0xFFFFFFFF );
+ } else {
+ for ( i = 0; i < dev->mc_count; i++ ) {
+ if ( i < 3 ) {
+ TLan_SetMac( dev, i + 1, (char *) &dmi->dmi_addr );
+ } else {
+ offset = TLan_HashFunc( (u8 *) &dmi->dmi_addr );
+ if ( offset < 32 )
+ hash1 |= ( 1 << offset );
+ else
+ hash2 |= ( 1 << ( offset - 32 ) );
+ }
+ dmi = dmi->next;
+ }
+ for ( ; i < 3; i++ )
+ TLan_SetMac( dev, i + 1, NULL );
+ TLan_DioWrite32( dev->base_addr, TLAN_HASH_1, hash1 );
+ TLan_DioWrite32( dev->base_addr, TLAN_HASH_2, hash2 );
+ }
+ }
+
+ } /* TLan_SetRxMode */
diff --git a/drivers/net/tlan.h b/drivers/net/tlan.h
new file mode 100644
index 000000000..9919fd275
--- /dev/null
+++ b/drivers/net/tlan.h
@@ -0,0 +1,485 @@
+/********************************************************************
+ *
+ * Linux ThunderLAN Driver
+ *
+ * tlan.h
+ * by James Banks, james.banks@caldera.com
+ *
+ * (C) 1997 Caldera, Inc.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU Public License, incorporated herein by reference.
+ *
+ ** This file is best viewed/edited with tabstop=4, colums>=132
+ *
+ ********************************************************************/
+
+#include <asm/io.h>
+#include <asm/types.h>
+#include <linux/netdevice.h>
+
+
+
+ /*****************************************************************
+ * TLan Definitions
+ *
+ ****************************************************************/
+
+ #define FALSE 0
+ #define TRUE 1
+
+ #define TLAN_MIN_FRAME_SIZE 64
+ #define TLAN_MAX_FRAME_SIZE 1600
+
+ #define TLAN_NUM_RX_LISTS 4
+ #define TLAN_NUM_TX_LISTS 8
+
+ #define TLAN_IGNORE 0
+ #define TLAN_RECORD 1
+
+ #define TLAN_DBG(lvl, format, args...) if ( debug >= lvl ) printk( format, ##args );
+
+
+
+
+ /*****************************************************************
+ * Device Identification Definitions
+ *
+ ****************************************************************/
+
+ /* NOTE: These should be moved to pci.h someday */
+ #define PCI_DEVICE_ID_NETELLIGENT_10 0xAE34
+ #define PCI_DEVICE_ID_NETELLIGENT_10_100 0xAE32
+ #define PCI_DEVICE_ID_NETFLEX_3_INTEGRATED 0xAE35
+
+
+ typedef struct tlan_pci_id {
+ u16 vendorId;
+ u16 deviceId;
+ char *deviceName;
+ } TLanPciId;
+
+
+
+
+ /*****************************************************************
+ * Rx/Tx List Definitions
+ *
+ ****************************************************************/
+
+ #define TLAN_BUFFERS_PER_LIST 10
+ #define TLAN_LAST_BUFFER 0x80000000
+ #define TLAN_CSTAT_UNUSED 0x8000
+ #define TLAN_CSTAT_FRM_CMP 0x4000
+ #define TLAN_CSTAT_READY 0x3000
+ #define TLAN_CSTAT_EOC 0x0800
+ #define TLAN_CSTAT_RX_ERROR 0x0400
+ #define TLAN_CSTAT_PASS_CRC 0x0200
+ #define TLAN_CSTAT_DP_PR 0x0100
+
+
+ typedef struct tlan_buffer_ref_tag {
+ u32 count;
+ u32 address;
+ } TLanBufferRef;
+
+
+ typedef struct tlan_list_tag {
+ u32 forward;
+ u16 cStat;
+ u16 frameSize;
+ TLanBufferRef buffer[TLAN_BUFFERS_PER_LIST];
+ } TLanList;
+
+
+ typedef u8 TLanBuffer[TLAN_MAX_FRAME_SIZE];
+
+
+
+
+ /*****************************************************************
+ * PHY definitions
+ *
+ ****************************************************************/
+
+ #define TLAN_PHY_MAX_ADDR 0x1F
+ #define TLAN_PHY_INTERNAL 0x1F
+
+ #define TLAN_PHY_ACTIVITY 0x00000001
+ #define TLAN_PHY_AUTONEG 0x00000002
+
+
+ typedef int (TLanPhyFunc)( struct device * );
+
+
+ typedef struct tlan_phy_id_entry_tag {
+ u16 idHi;
+ u16 idLo;
+ TLanPhyFunc *check;
+ TLanPhyFunc *service;
+ u32 flags;
+ } TLanPhyIdEntry;
+
+
+
+
+ /*****************************************************************
+ * TLAN Private Information Structure
+ *
+ ****************************************************************/
+
+ typedef struct tlan_private_tag {
+ struct device *nextDevice;
+ void *dmaStorage;
+ u8 *padBuffer;
+ TLanList *rxList;
+ u8 *rxBuffer;
+ u32 rxHead;
+ u32 rxTail;
+ u32 rxEocCount;
+ TLanList *txList;
+ u8 *txBuffer;
+ u32 txHead;
+ u32 txInProgress;
+ u32 txTail;
+ u32 txBusyCount;
+ u32 phyAddr;
+ u32 phyEntry;
+ u32 phyOnline;
+ u32 phyFlags;
+ TLanPhyFunc *phyCheck;
+ TLanPhyFunc *phyService;
+ u32 timerSetAt;
+ u32 timerType;
+ struct timer_list timer;
+ struct enet_statistics stats;
+ u32 pciEntry;
+ u8 pciRevision;
+ u8 pciBus;
+ u8 pciDeviceFn;
+ char devName[8];
+ } TLanPrivateInfo;
+
+
+
+
+ /*****************************************************************
+ * TLan Driver Timer Definitions
+ *
+ ****************************************************************/
+
+ #define TLAN_TIMER_LINK 1
+ #define TLAN_TIMER_ACT 2
+
+ #define TLAN_TIMER_LINK_DELAY 230
+ #define TLAN_TIMER_ACT_DELAY 10
+
+
+
+
+ /*****************************************************************
+ * TLan Driver Eeprom Definitions
+ *
+ ****************************************************************/
+
+ #define TLAN_EEPROM_ACK 0
+ #define TLAN_EEPROM_STOP 1
+
+
+
+
+ /*****************************************************************
+ * Host Register Offsets and Contents
+ *
+ ****************************************************************/
+
+ #define TLAN_HOST_CMD 0x00
+ #define TLAN_HC_GO 0x80000000
+ #define TLAN_HC_STOP 0x40000000
+ #define TLAN_HC_ACK 0x20000000
+ #define TLAN_HC_CS_MASK 0x1FE00000
+ #define TLAN_HC_EOC 0x00100000
+ #define TLAN_HC_RT 0x00080000
+ #define TLAN_HC_NES 0x00040000
+ #define TLAN_HC_AD_RST 0x00008000
+ #define TLAN_HC_LD_TMR 0x00004000
+ #define TLAN_HC_LD_THR 0x00002000
+ #define TLAN_HC_REQ_INT 0x00001000
+ #define TLAN_HC_INT_OFF 0x00000800
+ #define TLAN_HC_INT_ON 0x00000400
+ #define TLAN_HC_AC_MASK 0x000000FF
+ #define TLAN_CH_PARM 0x04
+ #define TLAN_DIO_ADR 0x08
+ #define TLAN_DA_ADR_INC 0x8000
+ #define TLAN_DA_RAM_ADR 0x4000
+ #define TLAN_HOST_INT 0x0A
+ #define TLAN_HI_IV_MASK 0x1FE0
+ #define TLAN_HI_IT_MASK 0x001C
+ #define TLAN_DIO_DATA 0x0C
+
+
+/* ThunderLAN Internal Register DIO Offsets */
+
+#define TLAN_NET_CMD 0x00
+#define TLAN_NET_CMD_NRESET 0x80
+#define TLAN_NET_CMD_NWRAP 0x40
+#define TLAN_NET_CMD_CSF 0x20
+#define TLAN_NET_CMD_CAF 0x10
+#define TLAN_NET_CMD_NOBRX 0x08
+#define TLAN_NET_CMD_DUPLEX 0x04
+#define TLAN_NET_CMD_TRFRAM 0x02
+#define TLAN_NET_CMD_TXPACE 0x01
+#define TLAN_NET_SIO 0x01
+#define TLAN_NET_SIO_MINTEN 0x80
+#define TLAN_NET_SIO_ECLOK 0x40
+#define TLAN_NET_SIO_ETXEN 0x20
+#define TLAN_NET_SIO_EDATA 0x10
+#define TLAN_NET_SIO_NMRST 0x08
+#define TLAN_NET_SIO_MCLK 0x04
+#define TLAN_NET_SIO_MTXEN 0x02
+#define TLAN_NET_SIO_MDATA 0x01
+#define TLAN_NET_STS 0x02
+#define TLAN_NET_STS_MIRQ 0x80
+#define TLAN_NET_STS_HBEAT 0x40
+#define TLAN_NET_STS_TXSTOP 0x20
+#define TLAN_NET_STS_RXSTOP 0x10
+#define TLAN_NET_STS_RSRVD 0x0F
+#define TLAN_NET_MASK 0x03
+#define TLAN_NET_MASK_MASK7 0x80
+#define TLAN_NET_MASK_MASK6 0x40
+#define TLAN_NET_MASK_MASK5 0x20
+#define TLAN_NET_MASK_MASK4 0x10
+#define TLAN_NET_MASK_RSRVD 0x0F
+#define TLAN_NET_CONFIG 0x04
+#define TLAN_NET_CFG_RCLK 0x8000
+#define TLAN_NET_CFG_TCLK 0x4000
+#define TLAN_NET_CFG_BIT 0x2000
+#define TLAN_NET_CFG_RXCRC 0x1000
+#define TLAN_NET_CFG_PEF 0x0800
+#define TLAN_NET_CFG_1FRAG 0x0400
+#define TLAN_NET_CFG_1CHAN 0x0200
+#define TLAN_NET_CFG_MTEST 0x0100
+#define TLAN_NET_CFG_PHY_EN 0x0080
+#define TLAN_NET_CFG_MSMASK 0x007F
+#define TLAN_MAN_TEST 0x06
+#define TLAN_DEF_VENDOR_ID 0x08
+#define TLAN_DEF_DEVICE_ID 0x0A
+#define TLAN_DEF_REVISION 0x0C
+#define TLAN_DEF_SUBCLASS 0x0D
+#define TLAN_DEF_MIN_LAT 0x0E
+#define TLAN_DEF_MAX_LAT 0x0F
+#define TLAN_AREG_0 0x10
+#define TLAN_AREG_1 0x16
+#define TLAN_AREG_2 0x1C
+#define TLAN_AREG_3 0x22
+#define TLAN_HASH_1 0x28
+#define TLAN_HASH_2 0x2C
+#define TLAN_GOOD_TX_FRMS 0x30
+#define TLAN_TX_UNDERUNS 0x33
+#define TLAN_GOOD_RX_FRMS 0x34
+#define TLAN_RX_OVERRUNS 0x37
+#define TLAN_DEFERRED_TX 0x38
+#define TLAN_CRC_ERRORS 0x3A
+#define TLAN_CODE_ERRORS 0x3B
+#define TLAN_MULTICOL_FRMS 0x3C
+#define TLAN_SINGLECOL_FRMS 0x3E
+#define TLAN_EXCESSCOL_FRMS 0x40
+#define TLAN_LATE_COLS 0x41
+#define TLAN_CARRIER_LOSS 0x42
+#define TLAN_ACOMMIT 0x43
+#define TLAN_LED_REG 0x44
+#define TLAN_LED_ACT 0x10
+#define TLAN_LED_LINK 0x01
+#define TLAN_BSIZE_REG 0x45
+#define TLAN_MAX_RX 0x46
+#define TLAN_INT_DIS 0x48
+#define TLAN_ID_TX_EOC 0x04
+#define TLAN_ID_RX_EOF 0x02
+#define TLAN_ID_RX_EOC 0x01
+
+
+
+/* ThunderLAN Interrupt Codes */
+
+#define TLAN_INT_NUMBER_OF_INTS 8
+
+#define TLAN_INT_NONE 0x0000
+#define TLAN_INT_TX_EOF 0x0001
+#define TLAN_INT_STAT_OVERFLOW 0x0002
+#define TLAN_INT_RX_EOF 0x0003
+#define TLAN_INT_DUMMY 0x0004
+#define TLAN_INT_TX_EOC 0x0005
+#define TLAN_INT_STATUS_CHECK 0x0006
+#define TLAN_INT_RX_EOC 0x0007
+
+
+
+/* ThunderLAN MII Registers */
+
+/* Generic MII/PHY Registers */
+
+#define MII_GEN_CTL 0x00
+#define MII_GC_RESET 0x8000
+#define MII_GC_LOOPBK 0x4000
+#define MII_GC_SPEEDSEL 0x2000
+#define MII_GC_AUTOENB 0x1000
+#define MII_GC_PDOWN 0x0800
+#define MII_GC_ISOLATE 0x0400
+#define MII_GC_AUTORSRT 0x0200
+#define MII_GC_DUPLEX 0x0100
+#define MII_GC_COLTEST 0x0080
+#define MII_GC_RESERVED 0x007F
+#define MII_GEN_STS 0x01
+#define MII_GS_100BT4 0x8000
+#define MII_GS_100BTXFD 0x4000
+#define MII_GS_100BTXHD 0x2000
+#define MII_GS_10BTFD 0x1000
+#define MII_GS_10BTHD 0x0800
+#define MII_GS_RESERVED 0x07C0
+#define MII_GS_AUTOCMPLT 0x0020
+#define MII_GS_RFLT 0x0010
+#define MII_GS_AUTONEG 0x0008
+#define MII_GS_LINK 0x0004
+#define MII_GS_JABBER 0x0002
+#define MII_GS_EXTCAP 0x0001
+#define MII_GEN_ID_HI 0x02
+#define MII_GEN_ID_LO 0x03
+#define MII_GIL_OUI 0xFC00
+#define MII_GIL_MODEL 0x03F0
+#define MII_GIL_REVISION 0x000F
+#define MII_AN_ADV 0x04
+#define MII_AN_LPA 0x05
+#define MII_AN_EXP 0x06
+
+/* ThunderLAN Specific MII/PHY Registers */
+
+#define TLAN_TLPHY_ID 0x10
+#define TLAN_TLPHY_CTL 0x11
+#define TLAN_TC_IGLINK 0x8000
+#define TLAN_TC_SWAPOL 0x4000
+#define TLAN_TC_AUISEL 0x2000
+#define TLAN_TC_SQEEN 0x1000
+#define TLAN_TC_MTEST 0x0800
+#define TLAN_TC_RESERVED 0x07F8
+#define TLAN_TC_NFEW 0x0004
+#define TLAN_TC_INTEN 0x0002
+#define TLAN_TC_TINT 0x0001
+#define TLAN_TLPHY_STS 0x12
+#define TLAN_TS_MINT 0x8000
+#define TLAN_TS_PHOK 0x4000
+#define TLAN_TS_POLOK 0x2000
+#define TLAN_TS_TPENERGY 0x1000
+#define TLAN_TS_RESERVED 0x0FFF
+
+
+
+/* Routines to access internal registers. */
+
+inline u8 TLan_DioRead8(u16 base_addr, u16 internal_addr)
+{
+ outw(internal_addr, base_addr + TLAN_DIO_ADR);
+ return (inb((base_addr + TLAN_DIO_DATA) + (internal_addr & 0x3)));
+
+} /* TLan_DioRead8 */
+
+
+
+
+inline u16 TLan_DioRead16(u16 base_addr, u16 internal_addr)
+{
+ outw(internal_addr, base_addr + TLAN_DIO_ADR);
+ return (inw((base_addr + TLAN_DIO_DATA) + (internal_addr & 0x2)));
+
+} /* TLan_DioRead16 */
+
+
+
+
+inline u32 TLan_DioRead32(u16 base_addr, u16 internal_addr)
+{
+ outw(internal_addr, base_addr + TLAN_DIO_ADR);
+ return (inl(base_addr + TLAN_DIO_DATA));
+
+} /* TLan_DioRead32 */
+
+
+
+
+inline void TLan_DioWrite8(u16 base_addr, u16 internal_addr, u8 data)
+{
+ outw(internal_addr, base_addr + TLAN_DIO_ADR);
+ outb(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x3));
+
+}
+
+
+
+
+inline void TLan_DioWrite16(u16 base_addr, u16 internal_addr, u16 data)
+{
+ outw(internal_addr, base_addr + TLAN_DIO_ADR);
+ outw(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x2));
+
+}
+
+
+
+
+inline void TLan_DioWrite32(u16 base_addr, u16 internal_addr, u32 data)
+{
+ outw(internal_addr, base_addr + TLAN_DIO_ADR);
+ outl(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x2));
+
+}
+
+
+
+
+inline void TLan_ClearBit(u8 bit, u16 port)
+{
+ outb_p(inb_p(port) & ~bit, port);
+}
+
+
+
+
+inline int TLan_GetBit(u8 bit, u16 port)
+{
+ return ((int) (inb_p(port) & bit));
+}
+
+
+
+
+inline void TLan_SetBit(u8 bit, u16 port)
+{
+ outb_p(inb_p(port) | bit, port);
+}
+
+
+inline u32 xor( u32 a, u32 b )
+{
+ return ( ( a && ! b ) || ( ! a && b ) );
+}
+#define XOR8( a, b, c, d, e, f, g, h ) xor( a, xor( b, xor( c, xor( d, xor( e, xor( f, xor( g, h ) ) ) ) ) ) )
+#define DA( a, bit ) ( ( (u8) a[bit/8] ) & ( (u8) ( 1 << bit%8 ) ) )
+
+inline u32 TLan_HashFunc( u8 *a )
+{
+ u32 hash;
+
+ hash = XOR8( DA(a,0), DA(a, 6), DA(a,12), DA(a,18), DA(a,24), DA(a,30), DA(a,36), DA(a,42) );
+ hash |= XOR8( DA(a,1), DA(a, 7), DA(a,13), DA(a,19), DA(a,25), DA(a,31), DA(a,37), DA(a,43) ) << 1;
+ hash |= XOR8( DA(a,2), DA(a, 8), DA(a,14), DA(a,20), DA(a,26), DA(a,32), DA(a,38), DA(a,44) ) << 2;
+ hash |= XOR8( DA(a,3), DA(a, 9), DA(a,15), DA(a,21), DA(a,27), DA(a,33), DA(a,39), DA(a,45) ) << 3;
+ hash |= XOR8( DA(a,4), DA(a,10), DA(a,16), DA(a,22), DA(a,28), DA(a,34), DA(a,40), DA(a,46) ) << 4;
+ hash |= XOR8( DA(a,5), DA(a,11), DA(a,17), DA(a,23), DA(a,29), DA(a,35), DA(a,41), DA(a,47) ) << 5;
+
+ return hash;
+
+}
+
+
+
+
+#endif
diff --git a/drivers/net/tulip.c b/drivers/net/tulip.c
index f87beba00..03b7fc10c 100644
--- a/drivers/net/tulip.c
+++ b/drivers/net/tulip.c
@@ -1078,6 +1078,7 @@ tulip_rx(struct device *dev)
skb->protocol = eth_type_trans(skb,dev);
netif_rx(skb);
lp->stats.rx_packets++;
+ lp->stats.rx_bytes+=skb->len;
}
lp->rx_ring[entry].status = TRING_OWN;
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 7118164b6..16dcfafa0 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -204,7 +204,7 @@ struct pci_dev_info dev_info[] = {
DEVICE( VIA, VIA_82C586_1, "VT 82C586 Apollo VP-1"),
DEVICE( VIA, VIA_82C576, "VT 82C576 3V"),
DEVICE( VIA, VIA_82C585, "VT 82C585VP Apollo VP-1"),
- DEVICE( VIA, VIA_82C586_0, "VT 82C586 Apollo VP-1"),
+ DEVICE( VIA, VIA_82C586, "VT 82C586 Apollo VP-1"),
DEVICE( VIA, VIA_82C416, "VT 82C416MV"),
DEVICE( VORTEX, VORTEX_GDT60x0, "GDT 60x0"),
DEVICE( VORTEX, VORTEX_GDT6000B,"GDT 6000b"),
diff --git a/drivers/pnp/parport_procfs.c b/drivers/pnp/parport_procfs.c
index 77574991b..7adfac2da 100644
--- a/drivers/pnp/parport_procfs.c
+++ b/drivers/pnp/parport_procfs.c
@@ -1,4 +1,4 @@
-/* $Id: parport_procfs.c,v 1.3.2.6 1997/04/16 21:30:38 phil Exp $
+/* $Id: parport_procfs.c,v 1.2 1997/06/03 07:28:11 ralf Exp $
* Parallel port /proc interface code.
*
* Authors: David Campbell <campbell@tirian.che.curtin.edu.au>
@@ -13,7 +13,6 @@
#include <asm/io.h>
#include <asm/dma.h>
-#include <linux/config.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/ioport.h>
diff --git a/drivers/sbus/char/bwtwo.c b/drivers/sbus/char/bwtwo.c
index ae81e1260..615fef33d 100644
--- a/drivers/sbus/char/bwtwo.c
+++ b/drivers/sbus/char/bwtwo.c
@@ -1,4 +1,4 @@
-/* $Id: bwtwo.c,v 1.16 1997/06/04 08:27:26 davem Exp $
+/* $Id: bwtwo.c,v 1.18 1997/07/17 02:21:43 davem Exp $
* bwtwo.c: bwtwo console driver
*
* Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
@@ -74,7 +74,8 @@ static int
bwtwo_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma,
long base, fbinfo_t *fb)
{
- uint size, map_offset, r;
+ uint size, r;
+ unsigned long map_offset;
int map_size;
map_size = size = vma->vm_end - vma->vm_start;
@@ -91,9 +92,10 @@ bwtwo_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma,
map_offset = get_phys ((unsigned long) fb->base);
r = io_remap_page_range (vma->vm_start, map_offset, map_size,
vma->vm_page_prot, fb->space);
- if (r) return -EAGAIN;
- vma->vm_inode = inode;
- atomic_inc(&inode->i_count);
+ if (r)
+ return -EAGAIN;
+
+ vma->vm_dentry = dget(file->f_dentry);
return 0;
}
diff --git a/drivers/sbus/char/cgfourteen.c b/drivers/sbus/char/cgfourteen.c
index 2cb4c21c9..40fb9fd70 100644
--- a/drivers/sbus/char/cgfourteen.c
+++ b/drivers/sbus/char/cgfourteen.c
@@ -1,4 +1,4 @@
-/* $Id: cgfourteen.c,v 1.22 1997/06/04 08:27:27 davem Exp $
+/* $Id: cgfourteen.c,v 1.24 1997/07/17 02:21:44 davem Exp $
* cgfourteen.c: Sun SparcStation console support.
*
* Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
@@ -166,7 +166,7 @@ cg14_mmap (struct inode *inode, struct file *file,
struct vm_area_struct *vma, long base, fbinfo_t *fb)
{
uint size, page, r, map_size;
- uint map_offset = 0;
+ unsigned long map_offset = 0;
uint ram_size = fb->info.cg14.ramsize;
printk ("RAMSIZE=%d\n", ram_size);
@@ -269,11 +269,12 @@ cg14_mmap (struct inode *inode, struct file *file,
map_offset,
map_size, vma->vm_page_prot,
fb->space);
- if (r) return -EAGAIN;
+ if (r)
+ return -EAGAIN;
page += map_size;
}
- vma->vm_inode = inode;
- atomic_inc(&inode->i_count);
+
+ vma->vm_dentry = dget(file->f_dentry);
return 0;
}
diff --git a/drivers/sbus/char/cgsix.c b/drivers/sbus/char/cgsix.c
index e53fcf09e..825d49b4a 100644
--- a/drivers/sbus/char/cgsix.c
+++ b/drivers/sbus/char/cgsix.c
@@ -1,4 +1,4 @@
-/* $Id: cgsix.c,v 1.30 1997/06/04 08:27:28 davem Exp $
+/* $Id: cgsix.c,v 1.35 1997/07/17 02:21:45 davem Exp $
* cgsix.c: cgsix frame buffer driver
*
* Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
@@ -233,12 +233,33 @@ cg6_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma,
long base, fbinfo_t *fb)
{
uint size, page, r, map_size;
- uint map_offset = 0;
-
+ 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;
@@ -247,7 +268,7 @@ cg6_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma,
switch (vma->vm_offset+page){
case CG6_TEC:
map_size = PAGE_SIZE;
- map_offset = get_phys ((unsigned long)fb->info.cg6.tec);
+ map_offset = get_phys ((unsigned long)fb->info.cg6.tec) & PAGE_MASK;
break;
case CG6_FBC:
map_size = PAGE_SIZE;
@@ -259,18 +280,25 @@ cg6_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma,
break;
case CG6_THC:
map_size = PAGE_SIZE;
- map_offset = get_phys ((unsigned long)fb->info.cg6.thc);
+ map_offset = get_phys ((unsigned long)fb->info.cg6.thc) & PAGE_MASK;
break;
case CG6_BTREGS:
map_size = PAGE_SIZE;
map_offset = get_phys ((unsigned long)fb->info.cg6.bt);
break;
+
+ /* For Ultra, make sure the following two are right.
+ * The above two happen to work out (for example FBC and
+ * TEC will get mapped by one I/O page mapping because
+ * of the 8192 byte page size, same for FHC/THC. -DaveM
+ */
+
case CG6_DHC:
- map_size = PAGE_SIZE * 40;
+ map_size = /* PAGE_SIZE * 40 */ (4096 * 40);
map_offset = get_phys ((unsigned long)fb->info.cg6.dhc);
break;
case CG6_ROM:
- map_size = PAGE_SIZE * 16;
+ map_size = /* PAGE_SIZE * 16 */ (4096 * 16);
map_offset = get_phys ((unsigned long)fb->info.cg6.rom);
break;
case CG6_RAM:
@@ -293,11 +321,12 @@ cg6_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma,
map_offset,
map_size, vma->vm_page_prot,
fb->space);
- if (r) return -EAGAIN;
+ if (r)
+ return -EAGAIN;
page += map_size;
}
- vma->vm_inode = inode;
- atomic_inc(&inode->i_count);
+
+ vma->vm_dentry = dget(file->f_dentry);
return 0;
}
@@ -449,14 +478,22 @@ __initfunc(void cg6_setup (fbinfo_t *fb, int slot, u32 cg6, int cg6_io))
sizeof (struct bt_regs), "cgsix_dac", cg6_io, 0);
cg6info->fhc = sparc_alloc_io (cg6+CG6_FHC_OFFSET, 0,
sizeof (int), "cgsix_fhc", cg6_io, 0);
+#if PAGE_SHIFT <= 12
cg6info->thc = sparc_alloc_io (cg6+CG6_THC_OFFSET, 0,
sizeof (struct cg6_thc), "cgsix_thc", cg6_io, 0);
+#else
+ cg6info->thc = (struct cg6_thc *)(((char *)cg6info->fhc)+0x1000);
+#endif
+ cg6info->fbc = sparc_alloc_io (cg6+CG6_FBC_OFFSET, 0,
+ 0x1000, "cgsix_fbc", cg6_io, 0);
+#if PAGE_SHIFT <= 12
cg6info->tec = sparc_alloc_io (cg6+CG6_TEC_OFFSET, 0,
sizeof (struct cg6_tec), "cgsix_tec", cg6_io, 0);
+#else
+ cg6info->tec = (struct cg6_tec *)(((char *)cg6info->fbc)+0x1000);
+#endif
cg6info->dhc = sparc_alloc_io (cg6+CG6_DHC_OFFSET, 0,
0x40000, "cgsix_dhc", cg6_io, 0);
- cg6info->fbc = sparc_alloc_io (cg6+CG6_FBC_OFFSET, 0,
- 0x1000, "cgsix_fbc", cg6_io, 0);
cg6info->rom = sparc_alloc_io (cg6+CG6_ROM_OFFSET, 0,
0x10000, "cgsix_rom", cg6_io, 0);
if (!fb->base) {
@@ -477,6 +514,10 @@ __initfunc(void cg6_setup (fbinfo_t *fb, int slot, u32 cg6, int cg6_io))
cg6info->bt->addr = 0x07 << 24;
cg6info->bt->control = 0x00 << 24;
+#ifdef __sparc_v9__
+ printk("VA %016lx ", fb->base);
+#endif
+
printk("TEC Rev %x ",
(cg6info->thc->thc_misc >> CG6_THC_MISC_REV_SHIFT) &
CG6_THC_MISC_REV_MASK);
diff --git a/drivers/sbus/char/cgthree.c b/drivers/sbus/char/cgthree.c
index 0e1446c0e..aea1bff32 100644
--- a/drivers/sbus/char/cgthree.c
+++ b/drivers/sbus/char/cgthree.c
@@ -1,4 +1,4 @@
-/* $Id: cgthree.c,v 1.21 1997/06/04 08:27:29 davem Exp $
+/* $Id: cgthree.c,v 1.23 1997/07/17 02:21:46 davem Exp $
* cgtree.c: cg3 frame buffer driver
*
* Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
@@ -96,7 +96,7 @@ cg3_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma,
long base, fbinfo_t *fb)
{
uint size, page, r, map_size;
- uint map_offset = 0;
+ unsigned long map_offset = 0;
size = vma->vm_end - vma->vm_start;
if (vma->vm_offset & ~PAGE_MASK)
@@ -128,11 +128,12 @@ cg3_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma,
map_offset,
map_size, vma->vm_page_prot,
fb->space);
- if (r) return -EAGAIN;
+ if (r)
+ return -EAGAIN;
page += map_size;
}
- vma->vm_inode = inode;
- atomic_inc(&inode->i_count);
+
+ vma->vm_dentry = dget(file->f_dentry);
return 0;
}
diff --git a/drivers/sbus/char/creator.c b/drivers/sbus/char/creator.c
index 4ff2caf31..086f9b40e 100644
--- a/drivers/sbus/char/creator.c
+++ b/drivers/sbus/char/creator.c
@@ -1,13 +1,13 @@
-/*
- * creator.c: Linux/Sun Ultra Creator console support.
- *
- * Copyright (C) 1997 MIguel de Icaza (miguel@nuclecu.unam.mx)
+/* $Id: creator.c,v 1.7 1997/07/17 02:21:47 davem Exp $
+ * creator.c: Creator/Creator3D frame buffer driver
*
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
*/
#include <linux/kd.h>
#include <linux/tty.h>
#include <linux/malloc.h>
#include <linux/proc_fs.h>
+#include <linux/config.h>
#include <asm/sbus.h>
#include <asm/io.h>
@@ -20,31 +20,575 @@
#include <linux/selection.h>
#include <linux/console_struct.h>
#include "fb.h"
+#include "cg_common.h"
+
+#define FFB_SFB8R_VOFF 0x00000000
+#define FFB_SFB8G_VOFF 0x00400000
+#define FFB_SFB8B_VOFF 0x00800000
+#define FFB_SFB8X_VOFF 0x00c00000
+#define FFB_SFB32_VOFF 0x01000000
+#define FFB_SFB64_VOFF 0x02000000
+#define FFB_FBC_REGS_VOFF 0x04000000
+#define FFB_BM_FBC_REGS_VOFF 0x04002000
+#define FFB_DFB8R_VOFF 0x04004000
+#define FFB_DFB8G_VOFF 0x04404000
+#define FFB_DFB8B_VOFF 0x04804000
+#define FFB_DFB8X_VOFF 0x04c04000
+#define FFB_DFB24_VOFF 0x05004000
+#define FFB_DFB32_VOFF 0x06004000
+#define FFB_DFB422A_VOFF 0x07004000 /* DFB 422 mode write to A */
+#define FFB_DFB422AD_VOFF 0x07804000 /* DFB 422 mode with line doubling */
+#define FFB_DFB24B_VOFF 0x08004000 /* DFB 24bit mode write to B */
+#define FFB_DFB422B_VOFF 0x09004000 /* DFB 422 mode write to B */
+#define FFB_DFB422BD_VOFF 0x09804000 /* DFB 422 mode with line doubling */
+#define FFB_SFB16Z_VOFF 0x0a004000 /* 16bit mode Z planes */
+#define FFB_SFB8Z_VOFF 0x0a404000 /* 8bit mode Z planes */
+#define FFB_SFB422_VOFF 0x0ac04000 /* SFB 422 mode write to A/B */
+#define FFB_SFB422D_VOFF 0x0b404000 /* SFB 422 mode with line doubling */
+#define FFB_FBC_KREGS_VOFF 0x0bc04000
+#define FFB_DAC_VOFF 0x0bc06000
+#define FFB_PROM_VOFF 0x0bc08000
+#define FFB_EXP_VOFF 0x0bc18000
+
+#define FFB_SFB8R_POFF 0x04000000
+#define FFB_SFB8G_POFF 0x04400000
+#define FFB_SFB8B_POFF 0x04800000
+#define FFB_SFB8X_POFF 0x04c00000
+#define FFB_SFB32_POFF 0x05000000
+#define FFB_SFB64_POFF 0x06000000
+#define FFB_FBC_REGS_POFF 0x00600000
+#define FFB_BM_FBC_REGS_POFF 0x00600000
+#define FFB_DFB8R_POFF 0x01000000
+#define FFB_DFB8G_POFF 0x01400000
+#define FFB_DFB8B_POFF 0x01800000
+#define FFB_DFB8X_POFF 0x01c00000
+#define FFB_DFB24_POFF 0x02000000
+#define FFB_DFB32_POFF 0x03000000
+#define FFB_FBC_KREGS_POFF 0x00610000
+#define FFB_DAC_POFF 0x00400000
+#define FFB_PROM_POFF 0x00000000
+#define FFB_EXP_POFF 0x00200000
+
+#define FFB_Y_BYTE_ADDR_SHIFT 11
+#define FFB_Y_ADDR_SHIFT 13
+
+#define FFB_PPC_ACE_DISABLE 1
+#define FFB_PPC_ACE_AUX_ADD 3
+#define FFB_PPC_ACE_SHIFT 18
+#define FFB_PPC_DCE_DISABLE 2
+#define FFB_PPC_DCE_SHIFT 16
+#define FFB_PPC_ABE_DISABLE 2
+#define FFB_PPC_ABE_SHIFT 14
+#define FFB_PPC_VCE_DISABLE 1
+#define FFB_PPC_VCE_2D 2
+#define FFB_PPC_VCE_SHIFT 12
+#define FFB_PPC_APE_DISABLE 2
+#define FFB_PPC_APE_SHIFT 10
+#define FFB_PPC_CS_VARIABLE 2
+#define FFB_PPC_CS_SHIFT 0
+
+#define FFB_FBC_WB_A 1
+#define FFB_FBC_WB_SHIFT 29
+#define FFB_FBC_PGE_MASK 3
+#define FFB_FBC_BE_SHIFT 4
+#define FFB_FBC_GE_SHIFT 2
+#define FFB_FBC_RE_SHIFT 0
+
+#define FFB_ROP_NEW 0x83
+#define FFB_ROP_RGB_SHIFT 0
+
+#define FFB_UCSR_FIFO_MASK 0x00000fff
+#define FFB_UCSR_RP_BUSY 0x02000000
+
+struct ffb_fbc {
+ u8 xxx1[0x200];
+ volatile u32 ppc;
+ u8 xxx2[0x50];
+ volatile u32 fbc;
+ volatile u32 rop;
+ u8 xxx3[0x34];
+ volatile u32 pmask;
+ u8 xxx4[12];
+ volatile u32 clip0min;
+ volatile u32 clip0max;
+ volatile u32 clip1min;
+ volatile u32 clip1max;
+ volatile u32 clip2min;
+ volatile u32 clip2max;
+ volatile u32 clip3min;
+ volatile u32 clip3max;
+ u8 xxx5[0x3c];
+ volatile u32 unk1;
+ u8 xxx6[0x500];
+ volatile u32 unk2;
+ u8 xxx7[0xfc];
+ volatile u32 ucsr;
+};
+
+struct ffb_dac {
+ volatile u32 type;
+ volatile u32 value;
+ volatile u32 type2;
+ volatile u32 value2;
+};
+
+static void
+ffb_restore_palette (fbinfo_t *fbinfo)
+{
+}
+
+static void ffb_blitc(unsigned short, int, int);
+static void ffb_setw(int, int, unsigned short, int);
+static void ffb_cpyw(int, int, unsigned short *, int);
+static void ffb_fill(int, int, int *);
+
+static struct {
+ unsigned long voff;
+ unsigned long poff;
+ unsigned long size;
+} ffbmmap [] = {
+ { FFB_SFB8R_VOFF, FFB_SFB8R_POFF, 0x0400000 },
+ { FFB_SFB8G_VOFF, FFB_SFB8G_POFF, 0x0400000 },
+ { FFB_SFB8B_VOFF, FFB_SFB8B_POFF, 0x0400000 },
+ { FFB_SFB8X_VOFF, FFB_SFB8X_POFF, 0x0400000 },
+ { FFB_SFB32_VOFF, FFB_SFB32_POFF, 0x1000000 },
+ { FFB_SFB64_VOFF, FFB_SFB64_POFF, 0x2000000 },
+ { FFB_FBC_REGS_VOFF, FFB_FBC_REGS_POFF, 0x0002000 },
+ { FFB_BM_FBC_REGS_VOFF, FFB_BM_FBC_REGS_POFF, 0x0002000 },
+ { FFB_DFB8R_VOFF, FFB_DFB8R_POFF, 0x0400000 },
+ { FFB_DFB8G_VOFF, FFB_DFB8G_POFF, 0x0400000 },
+ { FFB_DFB8B_VOFF, FFB_DFB8B_POFF, 0x0400000 },
+ { FFB_DFB8X_VOFF, FFB_DFB8X_POFF, 0x0400000 },
+ { FFB_DFB24_VOFF, FFB_DFB24_POFF, 0x1000000 },
+ { FFB_DFB32_VOFF, FFB_DFB32_POFF, 0x1000000 },
+ { FFB_FBC_KREGS_VOFF, FFB_FBC_KREGS_POFF, 0x0002000 },
+ { FFB_DAC_VOFF, FFB_DAC_POFF, 0x0002000 },
+ { FFB_PROM_VOFF, FFB_PROM_POFF, 0x0010000 },
+ { FFB_EXP_VOFF, FFB_EXP_POFF, 0x0002000 }
+};
+
+/* Ugh: X wants to mmap a bunch of cute stuff at the same time :-( */
+/* So, we just mmap the things that are being asked for */
+static int
+ffb_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma,
+ long base, fbinfo_t *fb)
+{
+ uint size, page, r, map_size;
+ unsigned long map_offset = 0;
+ int i;
+
+ size = vma->vm_end - vma->vm_start;
+ if (vma->vm_offset & ~PAGE_MASK)
+ return -ENXIO;
+
+ /* Try to align RAM */
+#define ALIGNMENT 0x400000
+ map_offset = vma->vm_offset + size;
+ if (vma->vm_offset < FFB_FBC_REGS_VOFF) {
+ struct vm_area_struct *vmm = find_vma(current->mm, vma->vm_start);
+ int alignment = ALIGNMENT - ((vma->vm_start - vma->vm_offset) & (ALIGNMENT - 1));
+
+ if (alignment == ALIGNMENT) alignment = 0;
+ if (alignment && (!vmm || vmm->vm_start >= vma->vm_end + alignment)) {
+ vma->vm_start += alignment;
+ vma->vm_end += alignment;
+ }
+ }
+#undef ALIGNMENT
+
+ /* To stop the swapper from even considering these pages */
+ vma->vm_flags |= FB_MMAP_VM_FLAGS;
+
+ /* Each page, see which map applies */
+ for (page = 0; page < size; ){
+ map_size = 0;
+ for (i = 0; i < sizeof (ffbmmap) / sizeof (ffbmmap[0]); i++)
+ if (ffbmmap[i].voff == vma->vm_offset+page) {
+ map_size = ffbmmap[i].size;
+ map_offset = fb->info.ffb.physbase + ffbmmap[i].poff;
+ }
+
+ if (!map_size){
+ page += PAGE_SIZE;
+ continue;
+ }
+ if (page + map_size > size)
+ map_size = size - page;
+ r = io_remap_page_range (vma->vm_start+page,
+ map_offset,
+ map_size, vma->vm_page_prot, 0);
+ if (r)
+ return -EAGAIN;
+ page += map_size;
+ }
+
+ vma->vm_dentry = dget(file->f_dentry);
+ return 0;
+}
+
+/* XXX write body of these two... */
+static inline int
+ffb_wid_get (fbinfo_t *fb, struct fb_wid_list *wl)
+{
+ struct fb_wid_item *wi;
+ struct fb_wid_list wlt;
+ struct fb_wid_item wit[30];
+ char *km = NULL;
+ int i, j;
+ u32 l;
+ int err;
+
+#ifdef CONFIG_SPARC32_COMPAT
+ if (current->tss.flags & SPARC_FLAG_32BIT) {
+ if (copy_from_user (&wlt, wl, 2 * sizeof (__u32)) ||
+ __get_user ((long)wlt.wl_list, (((__u32 *)wl)+2)))
+ return -EFAULT;
+ } else
+#endif
+ if (copy_from_user (&wlt, wl, sizeof (wlt)))
+ return -EFAULT;
+ if (wlt.wl_count <= 30) {
+ if (copy_from_user (wit, wlt.wl_list, wlt.wl_count * sizeof(*wi)))
+ return -EFAULT;
+ wi = wit;
+ } else if (wlt.wl_count > 120)
+ return -EINVAL;
+ else {
+ wi = (struct fb_wid_item *) km = kmalloc (wlt.wl_count * sizeof (*wi), GFP_KERNEL);
+ if (!wi) return -ENOMEM;
+ if (copy_from_user (wi, wlt.wl_list, wlt.wl_count * sizeof(*wi))) {
+ kfree (wi);
+ return -EFAULT;
+ }
+ }
+ for (i = 0; i < wlt.wl_count; i++, wi++) {
+ switch (wi->wi_type) {
+ case FB_WID_DBL_8: j = (wi->wi_index & 0xf) + 0x40; break;
+ case FB_WID_DBL_24: j = wi->wi_index & 0x3f; break;
+ default: return -EINVAL;
+ }
+ wi->wi_attrs = 0xffff;
+ for (j = 0; j < 32; j++)
+ wi->wi_values [j] = 0;
+ }
+ err = 0;
+ if (copy_to_user (wlt.wl_list, km ? km : (char *)wit, wlt.wl_count * sizeof (*wi)))
+ err = -EFAULT;
+ if (km)
+ kfree (km);
+ return err;
+}
+
+static inline int
+ffb_wid_put (fbinfo_t *fb, struct fb_wid_list *wl)
+{
+ struct fb_wid_item *wi;
+ struct fb_wid_list wlt;
+ struct fb_wid_item wit[30];
+ char *km = NULL;
+ int i, j;
+ u32 l;
+
+#ifdef CONFIG_SPARC32_COMPAT
+ if (current->tss.flags & SPARC_FLAG_32BIT) {
+ if (copy_from_user (&wlt, wl, 2 * sizeof (__u32)) ||
+ __get_user ((long)wlt.wl_list, (((__u32 *)wl)+2)))
+ return -EFAULT;
+ } else
+#endif
+ if (copy_from_user (&wlt, wl, sizeof (wlt)))
+ return -EFAULT;
+ if (wlt.wl_count <= 30) {
+ if (copy_from_user (wit, wlt.wl_list, wlt.wl_count * sizeof(*wi)))
+ return -EFAULT;
+ wi = wit;
+ } else if (wlt.wl_count > 120)
+ return -EINVAL;
+ else {
+ wi = (struct fb_wid_item *) km = kmalloc (wlt.wl_count * sizeof (*wi), GFP_KERNEL);
+ if (!wi) return -ENOMEM;
+ if (copy_from_user (wi, wlt.wl_list, wlt.wl_count * sizeof(*wi))) {
+ kfree (wi);
+ return -EFAULT;
+ }
+ }
+ for (i = 0; i < wlt.wl_count; i++, wi++) {
+ switch (wi->wi_type) {
+ case FB_WID_DBL_8: j = (wi->wi_index & 0xf) + 0x40; break;
+ case FB_WID_DBL_24: j = wi->wi_index & 0x3f; break;
+ default: return -EINVAL;
+ }
+ /* x = wi->wi_values [j] */;
+ }
+ if (km)
+ kfree (km);
+ return 0;
+}
+
+static inline void
+ffb_curs_enable (fbinfo_t *fb, int enable)
+{
+ struct ffb_dac *dac = fb->info.ffb.dac;
+
+ dac->type2 = 0x100;
+ if (fb->info.ffb.dac_rev <= 2)
+ dac->value2 = enable ? 3 : 0;
+ else
+ dac->value2 = enable ? 0 : 3;
+}
-__initfunc(void creator_setup (fbinfo_t *fb, int slot, int con_node, unsigned long creator, int creator_io))
-{
- uint bases [2];
- unsigned long *p;
-
- if (!creator) {
- prom_getproperty (con_node, "address", (char *) &bases[0], 4);
- prom_printf ("Bases: %x %x\n", bases [0], bases [1]);
- p = (unsigned long *) creator = bases[0];
- fb->base = creator;
- fb->base = 0xff168000;
- }
-
- fb->type.fb_cmsize = 256;
- fb->mmap = 0;
- fb->loadcmap = 0;
- fb->setcursor = 0;
- fb->setcursormap = 0;
- fb->setcurshape = 0;
- fb->ioctl = 0;
- fb->switch_from_graph = 0;
- fb->postsetup = sun_cg_postsetup;
- fb->reset = 0;
- fb->blank = 0;
- fb->unblank = 0;
- fb->type.fb_depth = 8;
+static void
+ffb_setcursormap (fbinfo_t *fb, unsigned char *red,
+ unsigned char *green,
+ unsigned char *blue)
+{
+ struct ffb_dac *dac = fb->info.ffb.dac;
+
+ ffb_curs_enable (fb, 0);
+ dac->type2 = 0x102;
+ dac->value2 = (red[0] | (green[0]<<8) | (blue[0]<<16));
+ dac->value2 = (red[1] | (green[1]<<8) | (blue[1]<<16));
+}
+
+/* Set cursor shape */
+static void
+ffb_setcurshape (fbinfo_t *fb)
+{
+ struct ffb_dac *dac = fb->info.ffb.dac;
+ int i, j;
+
+ ffb_curs_enable (fb, 0);
+ for (j = 0; j < 2; j++) {
+ dac->type2 = j ? 0 : 0x80;
+ for (i = 0; i < 0x40; i++) {
+ if (fb->cursor.size.fbx <= 32) {
+ dac->value2 = fb->cursor.bits [j][i];
+ dac->value2 = 0;
+ } else {
+ dac->value2 = fb->cursor.bits [j][2*i];
+ dac->value2 = fb->cursor.bits [j][2*i+1];
+ }
+ }
+ }
+}
+
+/* Load cursor information */
+static void
+ffb_setcursor (fbinfo_t *fb)
+{
+ struct ffb_dac *dac = fb->info.ffb.dac;
+ struct cg_cursor *c = &fb->cursor;
+
+ dac->type2 = 0x104;
+/* Should this be just 0x7ff?? Should I do some margin handling and setcurshape
+ in that case? */
+ dac->value2 = (((c->cpos.fbx - c->chot.fbx) & 0xffff) << 16)
+ |((c->cpos.fby - c->chot.fby) & 0xffff);
+ ffb_curs_enable (fb, fb->cursor.enable);
+}
+
+static void
+ffb_blank (fbinfo_t *fb)
+{
+/* XXX Write this */
+}
+
+static void
+ffb_unblank (fbinfo_t *fb)
+{
+/* XXX Write this */
+}
+
+static int ffb_clutstore (fbinfo_t *fb, int offset, int count)
+{
+ int i;
+ u32 *clut = fb->info.ffb.clut + offset;
+ struct ffb_dac *dac = fb->info.ffb.dac;
+
+ dac->type = 0x2000 | offset;
+ for (i = 0; i < count; i++)
+ dac->value = *clut++; /* Feed the colors in :)) */
+ return 0;
+}
+
+static int ffb_clutpost (fbinfo_t *fb, struct fb_clut *fc)
+{
+ int i;
+ u32 *clut;
+ struct fb_clut fct;
+ u8 red[256], green[256], blue[256];
+
+#ifdef CONFIG_SPARC32_COMPAT
+ if (current->tss.flags & SPARC_FLAG_32BIT) {
+ if (copy_from_user (&fct, fc, 3 * sizeof (__u32)) ||
+ __get_user ((long)fct.red, &(((struct fb_clut32 *)fc)->red)) ||
+ __get_user ((long)fct.green, &(((struct fb_clut32 *)fc)->green)) ||
+ __get_user ((long)fct.blue, &(((struct fb_clut32 *)fc)->blue)))
+ return -EFAULT;
+ } else
+#endif
+ if (copy_from_user (&fct, fc, sizeof (struct fb_clut)))
+ return -EFAULT;
+ i = fct.offset + fct.count;
+ if (fct.clutid || i <= 0 || i > 256) return -EINVAL;
+ if (copy_from_user (red, fct.red, fct.count) ||
+ copy_from_user (green, fct.green, fct.count) ||
+ copy_from_user (blue, fct.blue, fct.count))
+ return -EFAULT;
+ clut = fb->info.ffb.clut + fct.offset;
+ for (i = 0; i < fct.count; i++)
+ *clut++ = ((red[i])|(green[i]<<8)|(blue[i]<<16));
+ return ffb_clutstore (fb, fct.offset, fct.count);
+}
+
+static int ffb_clutread (fbinfo_t *fb, struct fb_clut *fc)
+{
+/* XXX write this */
+ return 0;
+}
+
+static void
+ffb_loadcmap (fbinfo_t *fb, int index, int count)
+{
+ u32 *clut = fb->info.ffb.clut + index;
+ int i, j = count;
+
+ for (i = index; j--; i++)
+ *clut++ = ((fb->color_map CM(i,0))) |
+ ((fb->color_map CM(i,1)) << 8) |
+ ((fb->color_map CM(i,2)) << 16);
+ ffb_clutstore (fb, index, count);
+}
+
+/* Handle ffb-specific ioctls */
+static int
+ffb_ioctl (struct inode *inode, struct file *file, unsigned cmd, unsigned long arg, fbinfo_t *fb)
+{
+ int i;
+
+ switch (cmd) {
+ case FBIO_WID_GET:
+ return ffb_wid_get (fb, (struct fb_wid_list *)arg);
+ case FBIO_WID_PUT:
+ return ffb_wid_put (fb, (struct fb_wid_list *)arg);
+ case FFB_CLUTPOST:
+ return ffb_clutpost (fb, (struct fb_clut *)arg);
+ case FFB_CLUTREAD:
+ return ffb_clutread (fb, (struct fb_clut *)arg);
+
+ default:
+ return -ENOSYS;
+ }
+}
+
+void
+ffb_reset (fbinfo_t *fb)
+{
+ struct ffb_info *ffb = &(fb->info.ffb);
+ int fifo;
+
+ if (fb->setcursor)
+ sun_hw_hide_cursor ();
+
+ while ((fifo = (ffb->fbc->ucsr & FFB_UCSR_FIFO_MASK)) < 8);
+ ffb->fbc->ppc = (FFB_PPC_ACE_DISABLE << FFB_PPC_ACE_SHIFT) |
+ (FFB_PPC_DCE_DISABLE << FFB_PPC_DCE_SHIFT) |
+ (FFB_PPC_ABE_DISABLE << FFB_PPC_ABE_SHIFT) |
+ (FFB_PPC_VCE_DISABLE << FFB_PPC_VCE_SHIFT) |
+ (FFB_PPC_APE_DISABLE << FFB_PPC_APE_SHIFT) |
+ (FFB_PPC_CS_VARIABLE << FFB_PPC_CS_SHIFT);
+ ffb->fbc->fbc = (FFB_FBC_WB_A << FFB_FBC_WB_SHIFT) |
+ (FFB_FBC_PGE_MASK << FFB_FBC_BE_SHIFT) |
+ (FFB_FBC_PGE_MASK << FFB_FBC_GE_SHIFT) |
+ (FFB_FBC_PGE_MASK << FFB_FBC_RE_SHIFT);
+ ffb->fbc->rop = (FFB_ROP_NEW << FFB_ROP_RGB_SHIFT);
+ ffb->fbc->pmask = 0x00ffffff;
+ while (ffb->fbc->ucsr & FFB_UCSR_RP_BUSY);
+}
+
+__initfunc(static unsigned long ffb_postsetup (fbinfo_t *fb, unsigned long memory_start))
+{
+ fb->info.ffb.clut = (u32 *)(memory_start);
+ fb->color_map = (u8 *)(memory_start+256*4+256);
+ return memory_start + 256*4 + 256*3;
+}
+
+__initfunc(void creator_setup (fbinfo_t *fb, int slot, int ffb_node, unsigned long ffb, int ffb_io))
+{
+ struct ffb_info *ffbinfo;
+ struct linux_prom64_registers regs[2*PROMREG_MAX];
+
+ if (prom_getproperty(ffb_node, "reg", (void *) regs, sizeof(regs)) <= 0)
+ return;
+ ffb = regs[0].phys_addr;
+ printk ("creator%d at 0x%016lx ", slot, ffb);
+
+ fb->base = ffb; /* ??? */
+
+ /* Fill in parameters we left out */
+ fb->type.fb_cmsize = 256;
+ fb->mmap = ffb_mmap;
+ fb->loadcmap = ffb_loadcmap;
+ fb->reset = ffb_reset;
+ fb->blank = ffb_blank;
+ fb->unblank = ffb_unblank;
+ fb->setcursor = ffb_setcursor;
+ fb->setcursormap = ffb_setcursormap;
+ fb->setcurshape = ffb_setcurshape;
+ fb->postsetup = ffb_postsetup;
+ fb->blitc = ffb_blitc;
+ fb->setw = ffb_setw;
+ fb->cpyw = ffb_cpyw;
+ fb->fill = ffb_fill;
+ fb->ioctl = ffb_ioctl;
+ fb->cursor.hwsize.fbx = 64;
+ fb->cursor.hwsize.fby = 64;
+
+ ffbinfo = (struct ffb_info *) &fb->info.ffb;
+
+ ffbinfo->physbase = ffb;
+
+ ffbinfo->fbc = (struct ffb_fbc *)(ffb + FFB_FBC_REGS_POFF);
+ ffbinfo->dac = (struct ffb_dac *)(ffb + FFB_DAC_POFF);
+
+ ffbinfo->dac->type = 0x8000;
+ ffbinfo->dac_rev = (ffbinfo->dac->value >> 0x1c);
+
+ if (slot == sun_prom_console_id)
+ fb_restore_palette = ffb_restore_palette;
+
+ /* Initialize Brooktree DAC */
+
+ printk("DAC %d\n", ffbinfo->dac_rev);
+
+ if (slot && sun_prom_console_id == slot)
+ return;
+
+ /* Reset the ffb */
+ ffb_reset(fb);
+
+ if (!slot) {
+ /* Enable Video */
+ ffb_unblank(fb);
+ } else {
+ ffb_blank(fb);
+ }
+}
+
+extern unsigned char vga_font[];
+
+static void ffb_blitc(unsigned short charattr, int xoff, int yoff)
+{
+}
+
+static void ffb_setw(int xoff, int yoff, unsigned short c, int count)
+{
+}
+
+static void ffb_cpyw(int xoff, int yoff, unsigned short *p, int count)
+{
+}
+
+static void ffb_fill(int attrib, int count, int *boxes)
+{
}
diff --git a/drivers/sbus/char/fb.h b/drivers/sbus/char/fb.h
index 029eac81b..0aa9f2b48 100644
--- a/drivers/sbus/char/fb.h
+++ b/drivers/sbus/char/fb.h
@@ -1,4 +1,4 @@
-/* $Id: fb.h,v 1.26 1997/04/17 02:29:33 miguel Exp $
+/* $Id: fb.h,v 1.29 1997/07/15 09:48:48 jj Exp $
* fb.h: contains the definitions of the structures that various sun
* frame buffer can use to do console driver stuff.
*
@@ -33,7 +33,7 @@ struct cg_cursor {
struct fbcurpos chot; /* hot-spot */
struct fbcurpos size; /* size of mask & image fields */
struct fbcurpos hwsize; /* hw max size */
- int bits[2][32]; /* space for mask & image bits */
+ int bits[2][128]; /* space for mask & image bits */
char color [6]; /* cursor colors */
};
@@ -57,6 +57,14 @@ struct tcx_info {
int lowdepth;
};
+struct ffb_info {
+ unsigned long physbase;
+ struct ffb_fbc *fbc;
+ struct ffb_dac *dac;
+ int dac_rev;
+ u32 *clut;
+};
+
struct leo_info {
struct leo_cursor *cursor;
struct leo_lc_ss0_krn *lc_ss0_krn;
@@ -113,6 +121,7 @@ typedef struct fbinfo {
struct cg14_info cg14;
struct tcx_info tcx;
struct leo_info leo;
+ struct ffb_info ffb;
} info; /* per frame information */
int space; /* I/O space this card resides in */
int blanked; /* true if video blanked */
@@ -183,7 +192,7 @@ extern int con_height, con_linebytes;
extern int ints_per_line;
/* used in the mmap routines */
-extern unsigned int get_phys (unsigned long addr);
+extern unsigned long get_phys (unsigned long addr);
extern int get_iospace (unsigned long addr);
extern void render_screen(void);
diff --git a/drivers/sbus/char/leo.c b/drivers/sbus/char/leo.c
index 61e646e9f..1b5d06e4b 100644
--- a/drivers/sbus/char/leo.c
+++ b/drivers/sbus/char/leo.c
@@ -1,7 +1,7 @@
-/* $Id: leo.c,v 1.18 1997/06/04 08:27:30 davem Exp $
+/* $Id: leo.c,v 1.21 1997/07/17 02:21:48 davem Exp $
* leo.c: SUNW,leo 24/8bit frame buffer driver
*
- * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
* Copyright (C) 1997 Michal Rehacek (Michal.Rehacek@st.mff.cuni.cz)
*/
@@ -143,7 +143,7 @@ leo_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma,
long base, fbinfo_t *fb)
{
uint size, page, r, map_size = 0;
- uint map_offset = 0;
+ unsigned long map_offset = 0;
size = vma->vm_end - vma->vm_start;
if (vma->vm_offset & ~PAGE_MASK)
@@ -218,11 +218,12 @@ leo_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma,
map_offset,
map_size, vma->vm_page_prot,
fb->space);
- if (r) return -EAGAIN;
+ if (r)
+ return -EAGAIN;
page += map_size;
}
- vma->vm_inode = inode;
- atomic_inc(&inode->i_count);
+
+ vma->vm_dentry = dget(file->f_dentry);
return 0;
}
@@ -376,7 +377,7 @@ static int leo_clutstore (fbinfo_t *fb, int clutid)
return 0;
}
-static int leo_clutpost (fbinfo_t *fb, struct leo_clut *lc)
+static int leo_clutpost (fbinfo_t *fb, struct fb_clut *lc)
{
int xlate = 0, i;
u32 *clut;
@@ -398,7 +399,7 @@ static int leo_clutpost (fbinfo_t *fb, struct leo_clut *lc)
return leo_clutstore (fb, lc->clutid);
}
-static int leo_clutread (fbinfo_t *fb, struct leo_clut *lc)
+static int leo_clutread (fbinfo_t *fb, struct fb_clut *lc)
{
int i;
u32 u;
@@ -463,29 +464,29 @@ leo_ioctl (struct inode *inode, struct file *file, unsigned cmd, unsigned long a
if (i) return i;
return leo_wid_put (fb, (struct fb_wid_list *)arg);
case LEO_CLUTPOST:
- i = verify_area (VERIFY_READ, (void *)arg, sizeof (struct leo_clut));
+ i = verify_area (VERIFY_READ, (void *)arg, sizeof (struct fb_clut));
if (i) return i;
- i = ((struct leo_clut *)arg)->offset + ((struct leo_clut *)arg)->count;
+ i = ((struct fb_clut *)arg)->offset + ((struct fb_clut *)arg)->count;
if (i <= 0 || i > 256) return -EINVAL;
- i = verify_area (VERIFY_READ, ((struct leo_clut *)arg)->red, ((struct leo_clut *)arg)->count);
+ i = verify_area (VERIFY_READ, ((struct fb_clut *)arg)->red, ((struct fb_clut *)arg)->count);
if (i) return i;
- i = verify_area (VERIFY_READ, ((struct leo_clut *)arg)->green, ((struct leo_clut *)arg)->count);
+ i = verify_area (VERIFY_READ, ((struct fb_clut *)arg)->green, ((struct fb_clut *)arg)->count);
if (i) return i;
- i = verify_area (VERIFY_READ, ((struct leo_clut *)arg)->blue, ((struct leo_clut *)arg)->count);
+ i = verify_area (VERIFY_READ, ((struct fb_clut *)arg)->blue, ((struct fb_clut *)arg)->count);
if (i) return i;
- return leo_clutpost (fb, (struct leo_clut *)arg);
+ return leo_clutpost (fb, (struct fb_clut *)arg);
case LEO_CLUTREAD:
- i = verify_area (VERIFY_READ, (void *)arg, sizeof (struct leo_clut));
+ i = verify_area (VERIFY_READ, (void *)arg, sizeof (struct fb_clut));
if (i) return i;
- i = ((struct leo_clut *)arg)->offset + ((struct leo_clut *)arg)->count;
+ i = ((struct fb_clut *)arg)->offset + ((struct fb_clut *)arg)->count;
if (i <= 0 || i > 256) return -EINVAL;
- i = verify_area (VERIFY_WRITE, ((struct leo_clut *)arg)->red, ((struct leo_clut *)arg)->count);
+ i = verify_area (VERIFY_WRITE, ((struct fb_clut *)arg)->red, ((struct fb_clut *)arg)->count);
if (i) return i;
- i = verify_area (VERIFY_WRITE, ((struct leo_clut *)arg)->green, ((struct leo_clut *)arg)->count);
+ i = verify_area (VERIFY_WRITE, ((struct fb_clut *)arg)->green, ((struct fb_clut *)arg)->count);
if (i) return i;
- i = verify_area (VERIFY_WRITE, ((struct leo_clut *)arg)->blue, ((struct leo_clut *)arg)->count);
+ i = verify_area (VERIFY_WRITE, ((struct fb_clut *)arg)->blue, ((struct fb_clut *)arg)->count);
if (i) return i;
- return leo_clutread (fb, (struct leo_clut *)arg);
+ return leo_clutread (fb, (struct fb_clut *)arg);
default:
return -ENOSYS;
diff --git a/drivers/sbus/char/openprom.c b/drivers/sbus/char/openprom.c
index 548abb601..75b950071 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/suncons.c b/drivers/sbus/char/suncons.c
index 1d3815dd3..cad446904 100644
--- a/drivers/sbus/char/suncons.c
+++ b/drivers/sbus/char/suncons.c
@@ -1,4 +1,4 @@
-/* $Id: suncons.c,v 1.63 1997/05/31 18:33:25 mj Exp $
+/* $Id: suncons.c,v 1.66 1997/07/15 09:48:47 jj Exp $
*
* suncons.c: Sun SparcStation console support.
*
@@ -759,7 +759,7 @@ console_restore_palette (void)
(*fb_restore_palette) (&fbinfo[0]);
}
-unsigned int
+unsigned long
get_phys (unsigned long addr)
{
return __get_phys(addr);
@@ -828,10 +828,14 @@ __initfunc(static int creator_present (void))
{
int root, n;
+#ifdef __sparc_v9__
root = prom_getchild (prom_root_node);
if ((n = prom_searchsiblings (root, "SUNW,ffb")) == 0)
return 0;
return n;
+#else
+ return 0;
+#endif
}
__initfunc(static void
@@ -1108,7 +1112,6 @@ __initfunc(static int sparc_console_probe(void))
if (!card_found)
card_found = cg14 = cg14_present ();
if (!card_found){
- prom_printf ("Searching for a creator\n");
card_found = creator = creator_present ();
}
if (!card_found){
@@ -1172,7 +1175,7 @@ __initfunc(static int sparc_console_probe(void))
if (creator){
sparc_framebuffer_setup (!sbdprom, creator, FBTYPE_CREATOR,
0, 0, 0, prom_console_node == creator,
- prom_getchild (prom_root_node));
+ prom_root_node);
}
break;
default:
@@ -1641,57 +1644,67 @@ void memcpyw(unsigned short *to, unsigned short *from, unsigned int count)
int
sun_hw_scursor (struct fbcursor *cursor, fbinfo_t *fb)
{
- int op = cursor->set;
+ int op;
int i, bytes = 0;
+ struct fbcursor f;
+ char red[2], green[2], blue[2];
+ if (copy_from_user (&f, cursor, sizeof(struct fbcursor)))
+ return -EFAULT;
+ op = f.set;
if (op & FB_CUR_SETSHAPE){
- if ((uint) cursor->size.fbx > fb->cursor.hwsize.fbx)
+ if ((uint) f.size.fbx > fb->cursor.hwsize.fbx)
return -EINVAL;
- if ((uint) cursor->size.fby > fb->cursor.hwsize.fby)
+ if ((uint) f.size.fby > fb->cursor.hwsize.fby)
return -EINVAL;
- bytes = (cursor->size.fby * 32)/8;
- i = verify_area (VERIFY_READ, cursor->image, bytes);
- if (i) return i;
- i = verify_area (VERIFY_READ, cursor->mask, bytes);
- if (i) return i;
+ if (f.size.fbx > 32)
+ bytes = f.size.fby << 3;
+ else
+ bytes = f.size.fby << 2;
}
if (op & FB_CUR_SETCMAP){
- if (cursor->cmap.index && cursor->cmap.count != 2)
+ if (f.cmap.index || f.cmap.count != 2)
return -EINVAL;
- i = verify_area (VERIFY_READ, cursor->cmap.red, 2);
- if (i) return i;
- i = verify_area (VERIFY_READ, cursor->cmap.green, 2);
- if (i) return i;
- i = verify_area (VERIFY_READ, cursor->cmap.blue, 2);
- if (i) return i;
- }
- if (op & (FB_CUR_SETCUR | FB_CUR_SETPOS | FB_CUR_SETHOT)){
- if (op & FB_CUR_SETCUR)
- fb->cursor.enable = cursor->enable;
- if (op & FB_CUR_SETPOS)
- fb->cursor.cpos = cursor->pos;
- if (op & FB_CUR_SETHOT)
- fb->cursor.chot = cursor->hot;
- (*fb->setcursor) (fb);
+ if (copy_from_user (red, f.cmap.red, 2) ||
+ copy_from_user (green, f.cmap.green, 2) ||
+ copy_from_user (blue, f.cmap.blue, 2))
+ return -EFAULT;
}
if (op & FB_CUR_SETCMAP)
- (*fb->setcursormap) (fb, cursor->cmap.red, cursor->cmap.green, cursor->cmap.blue);
+ (*fb->setcursormap) (fb, red, green, blue);
if (op & FB_CUR_SETSHAPE){
uint u;
- fb->cursor.size = cursor->size;
+ fb->cursor.size = f.size;
memset ((void *)&fb->cursor.bits, 0, sizeof (fb->cursor.bits));
- memcpy (fb->cursor.bits [0], cursor->mask, bytes);
- memcpy (fb->cursor.bits [1], cursor->image, bytes);
- u = ~0;
- if (cursor->size.fbx < fb->cursor.hwsize.fbx)
- u = ~(u >> cursor->size.fbx);
- for (i = fb->cursor.size.fby - 1; i >= 0; i--) {
- fb->cursor.bits [0][i] &= u;
- fb->cursor.bits [1][i] &= fb->cursor.bits [0][i];
+ if (copy_from_user (fb->cursor.bits [0], f.mask, bytes) ||
+ copy_from_user (fb->cursor.bits [1], f.image, bytes))
+ return -EFAULT;
+ if (f.size.fbx <= 32) {
+ u = ~(0xffffffff >> f.size.fbx);
+ for (i = fb->cursor.size.fby - 1; i >= 0; i--) {
+ fb->cursor.bits [0][i] &= u;
+ fb->cursor.bits [1][i] &= fb->cursor.bits [0][i];
+ }
+ } else {
+ u = ~(0xffffffff >> (f.size.fbx - 32));
+ for (i = fb->cursor.size.fby - 1; i >= 0; i--) {
+ fb->cursor.bits [0][2*i+1] &= u;
+ fb->cursor.bits [1][2*i] &= fb->cursor.bits [0][2*i];
+ fb->cursor.bits [1][2*i+1] &= fb->cursor.bits [0][2*i+1];
+ }
}
(*fb->setcurshape) (fb);
}
+ if (op & (FB_CUR_SETCUR | FB_CUR_SETPOS | FB_CUR_SETHOT)){
+ if (op & FB_CUR_SETCUR)
+ fb->cursor.enable = f.enable;
+ if (op & FB_CUR_SETPOS)
+ fb->cursor.cpos = f.pos;
+ if (op & FB_CUR_SETHOT)
+ fb->cursor.chot = f.hot;
+ (*fb->setcursor) (fb);
+ }
return 0;
}
diff --git a/drivers/sbus/char/sunfb.c b/drivers/sbus/char/sunfb.c
index 68856c9ee..885581975 100644
--- a/drivers/sbus/char/sunfb.c
+++ b/drivers/sbus/char/sunfb.c
@@ -1,4 +1,4 @@
-/* $Id: sunfb.c,v 1.23 1997/05/31 18:33:26 mj Exp $
+/* $Id: sunfb.c,v 1.26 1997/07/17 02:21:48 davem Exp $
* sunfb.c: Sun generic frame buffer support.
*
* Copyright (C) 1995, 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
@@ -132,7 +132,7 @@ fb_ioctl (struct inode *inode, struct file *file, uint cmd, unsigned long arg)
if ((index < 0) || (index > 255))
return -EINVAL;
if (index + count > 256)
- count = 256 - cmap->index;
+ count = 256 - index;
__get_user_ret(rp, &cmap->red, -EFAULT);
__get_user_ret(gp, &cmap->green, -EFAULT);
__get_user_ret(bp, &cmap->blue, -EFAULT);
@@ -146,7 +146,7 @@ fb_ioctl (struct inode *inode, struct file *file, uint cmd, unsigned long arg)
__put_user_ret(fb->color_map CM(i,2), bp, -EFAULT);
rp++; gp++; bp++;
}
- (*fb->loadcmap)(fb, cmap->index, count);
+ (*fb->loadcmap)(fb, index, count);
break;
}
@@ -164,13 +164,13 @@ fb_ioctl (struct inode *inode, struct file *file, uint cmd, unsigned long arg)
if ((index < 0) || (index > 255))
return -EINVAL;
if (index + count > 256)
- count = 256 - cmap->index;
+ count = 256 - index;
__get_user_ret(rp, &cmap->red, -EFAULT);
__get_user_ret(gp, &cmap->green, -EFAULT);
__get_user_ret(bp, &cmap->blue, -EFAULT);
- if(verify_area (VERIFY_READ, rp, cmap->count)) return -EFAULT;
- if(verify_area (VERIFY_READ, gp, cmap->count)) return -EFAULT;
- if(verify_area (VERIFY_READ, bp, cmap->count)) return -EFAULT;
+ if(verify_area (VERIFY_READ, rp, count)) return -EFAULT;
+ if(verify_area (VERIFY_READ, gp, count)) return -EFAULT;
+ if(verify_area (VERIFY_READ, bp, count)) return -EFAULT;
end = index + count;
for (i = index; i < end; i++){
@@ -179,7 +179,7 @@ fb_ioctl (struct inode *inode, struct file *file, uint cmd, unsigned long arg)
__get_user_ret(fb->color_map CM(i,2), bp, -EFAULT);
rp++; gp++; bp++;
}
- (*fb->loadcmap)(fb, cmap->index, count);
+ (*fb->loadcmap)(fb, index, count);
break;
}
@@ -275,7 +275,9 @@ fb_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma)
int v;
v = (*fb->mmap)(inode, file, vma, fb->base, fb);
- if (v) return v;
+ 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 1ddaaecd4..54b09cd76 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 371fb3004..789a332c0 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 e89384a3a..c0e6ab71d 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.2 $";
char *version, *p;
version = strchr(revision, ' ');
diff --git a/drivers/sbus/char/sunserial.h b/drivers/sbus/char/sunserial.h
index b8ae23305..ae4260cad 100644
--- a/drivers/sbus/char/sunserial.h
+++ b/drivers/sbus/char/sunserial.h
@@ -1,4 +1,4 @@
-/* $Id: sunserial.h,v 1.9 1997/04/12 23:33:12 ecd Exp $
+/* $Id: sunserial.h,v 1.11 1997/07/08 10:17:23 davem Exp $
* serial.h: Definitions for the Sparc Zilog serial driver.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
diff --git a/drivers/sbus/char/tcx.c b/drivers/sbus/char/tcx.c
index db66383ac..20c14a687 100644
--- a/drivers/sbus/char/tcx.c
+++ b/drivers/sbus/char/tcx.c
@@ -1,4 +1,4 @@
-/* $Id: tcx.c,v 1.15 1997/06/04 08:27:32 davem Exp $
+/* $Id: tcx.c,v 1.17 1997/07/17 02:21:50 davem Exp $
* tcx.c: SUNW,tcx 24/8bit frame buffer driver
*
* Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -110,7 +110,8 @@ tcx_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma,
long base, fbinfo_t *fb)
{
uint size, page, r, map_size;
- uint map_offset = 0, i;
+ unsigned long map_offset = 0;
+ uint i;
long offsets[13] = { -1, TCX_RAM24BIT, TCX_UNK3, TCX_UNK4,
-1, TCX_UNK6, TCX_UNK7,
-1, -1, -1, TCX_UNK2, TCX_DHC, TCX_ALT };
@@ -168,11 +169,12 @@ tcx_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma,
map_offset,
map_size, vma->vm_page_prot,
fb->space);
- if (r) return -EAGAIN;
+ if (r)
+ return -EAGAIN;
page += map_size;
}
- vma->vm_inode = inode;
- atomic_inc(&inode->i_count);
+
+ vma->vm_dentry = dget(file->f_dentry);
return 0;
}
diff --git a/drivers/sbus/char/vfc_dev.c b/drivers/sbus/char/vfc_dev.c
index 00cdfe35c..007174a82 100644
--- a/drivers/sbus/char/vfc_dev.c
+++ b/drivers/sbus/char/vfc_dev.c
@@ -577,11 +577,12 @@ static int vfc_mmap(struct inode *inode, struct file *file,
if(vma->vm_offset & ~PAGE_MASK) return -ENXIO;
vma->vm_flags |= VM_SHM | VM_LOCKED | VM_IO | VM_MAYREAD | VM_MAYWRITE | VM_MAYSHARE;
map_offset=(unsigned int)dev->phys_regs;
- ret=io_remap_page_range(vma->vm_start,map_offset,map_size,
- vma->vm_page_prot, dev->which_io);
- if(ret) return -EAGAIN;
- vma->vm_inode=inode;
- atomic_inc(&inode->i_count);
+ ret = io_remap_page_range(vma->vm_start,map_offset,map_size,
+ vma->vm_page_prot, dev->which_io);
+ if(ret)
+ return -EAGAIN;
+
+ vma->vm_dentry = dget(file->f_dentry);
return 0;
}
diff --git a/drivers/sbus/char/weitek.c b/drivers/sbus/char/weitek.c
index d2ac4d135..7b7b1bd72 100644
--- a/drivers/sbus/char/weitek.c
+++ b/drivers/sbus/char/weitek.c
@@ -1,4 +1,4 @@
-/* $Id: weitek.c,v 1.12 1997/06/04 08:27:34 davem Exp $
+/* $Id: weitek.c,v 1.14 1997/07/17 02:21:53 davem Exp $
* weitek.c: Tadpole P9100/P9000 console driver
*
* Copyright (C) 1996 David Redman (djhr@tadpole.co.uk)
@@ -41,7 +41,7 @@ weitek_mmap(struct inode *inode, struct file *file, struct vm_area_struct *vma,
long base, fbinfo_t *fb)
{
unsigned int size, page, r, map_size;
- unsigned int map_offset = 0;
+ unsigned long map_offset = 0;
size = vma->vm_end - vma->vm_start;
if (vma->vm_offset & ~PAGE_MASK)
@@ -79,11 +79,12 @@ weitek_mmap(struct inode *inode, struct file *file, struct vm_area_struct *vma,
map_offset,
map_size, vma->vm_page_prot,
fb->space);
- if (r) return -EAGAIN;
+ if (r)
+ return -EAGAIN;
page += map_size;
}
- vma->vm_inode = inode;
- atomic_inc(&inode->i_count);
+
+ vma->vm_dentry = dget(file->f_dentry);
return 0;
}
#endif
diff --git a/drivers/scsi/BusLogic.h b/drivers/scsi/BusLogic.h
index 7cd6ba436..515e84b64 100644
--- a/drivers/scsi/BusLogic.h
+++ b/drivers/scsi/BusLogic.h
@@ -27,6 +27,9 @@
*/
+#include <linux/config.h>
+
+
/*
Define types for some of the structures that interface with the rest
of the Linux Kernel and SCSI Subsystem.
diff --git a/drivers/scsi/README.in2000 b/drivers/scsi/README.in2000
index 06d0fee22..861d6efb2 100644
--- a/drivers/scsi/README.in2000
+++ b/drivers/scsi/README.in2000
@@ -1,4 +1,16 @@
+UPDATE NEWS: version 1.31 - 6 Jul 97
+
+ Fixed a bug that caused incorrect SCSI status bytes to be
+ returned from commands sent to LUN's greater than 0. This
+ means that CDROM changers work now! Fixed a bug in the
+ handling of command-line arguments when loaded as a module.
+ Also put all the header data in in2000.h where it belongs.
+ There are no longer any differences between this driver in
+ the 2.1.xx source tree and the 2.0.xx tree, as of 2.0.31
+ and 2.1.45 (or is it .46?) - this makes things much easier
+ for me...
+
UPDATE NEWS: version 1.30 - 14 Oct 96
Fixed a bug in the code that sets the transfer direction
@@ -105,15 +117,10 @@ to see what happens: my tests showed little difference either way.
There's also a define called 'DEFAULT_SX_PER'; this sets the data
transfer speed for the asynchronous mode. I've put it at 500 ns
despite the fact that the card could handle settings of 376 or
-252, because I'm not really sure if certain devices or maybe bad
-cables might have trouble at higher speeds. I couldn't find any
-info in my various SCSI references that talk about this in language
-I could understand, so decided to compromise with 500. This is still
-faster than the old driver was set at (I think). Can someone explain
-the significance of the bus transfer speed setting? Do devices on
-the bus ever care what it is? Is cable quality a factor here?
-Regardless, you can choose your own default through the command-
-line with the 'period' keyword.
+252, because higher speeds may be a problem with poor quality
+cables or improper termination; 500 ns is a compromise. You can
+choose your own default through the command-line with the
+'period' keyword.
------------------------------------------------
diff --git a/drivers/scsi/advansys.h b/drivers/scsi/advansys.h
index 6cf629921..eb3d65e65 100644
--- a/drivers/scsi/advansys.h
+++ b/drivers/scsi/advansys.h
@@ -1,4 +1,4 @@
-/* $Id: advansys.h,v 1997/05/28 00:23:06 bobf Exp bobf $ */
+/* $Id: advansys.h,v 1.6 1997/05/30 19:25:12 davem Exp $ */
/*
* advansys.h - Linux Host Driver for AdvanSys SCSI Adapters
diff --git a/drivers/scsi/amiga7xx.c b/drivers/scsi/amiga7xx.c
index 6edd01320..f07d5e2cd 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/in2000.c b/drivers/scsi/in2000.c
index 6f24da81c..d493fa167 100644
--- a/drivers/scsi/in2000.c
+++ b/drivers/scsi/in2000.c
@@ -104,7 +104,7 @@
*
*/
-
+#include <linux/module.h>
#include <asm/system.h>
#include <linux/sched.h>
@@ -115,46 +115,44 @@
#include <linux/ioport.h>
#include <linux/blkdev.h>
+#include <linux/blk.h>
+#include <linux/stat.h>
+
#include "scsi.h"
#include "sd.h"
#include "hosts.h"
-#include "in2000.h"
-
-#include <linux/blk.h>
-#include <linux/stat.h>
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-#define uchar unsigned char
+#define IN2000_VERSION "1.31"
+#define IN2000_DATE "06/July/1997"
-#define IN2000_VERSION "1.30"
-#define IN2000_DATE "14/Oct/1996"
+/*
+ * Note - the following defines have been moved to 'in2000.h':
+ *
+ * PROC_INTERFACE
+ * PROC_STATISTICS
+ * SYNC_DEBUG
+ * DEBUGGING_ON
+ * DEBUG_DEFAULTS
+ * FAST_READ_IO
+ * FAST_WRITE_IO
+ *
+ */
-#define PROC_INTERFACE /* add code for /proc/scsi/in2000/xxx interface */
-#define SYNC_DEBUG /* extra info on sync negotiation printed */
-#define DEBUGGING_ON /* enable command-line debugging bitmask */
-#define DEBUG_DEFAULTS 0 /* default bitmask - change from command-line */
-#define FAST_READ_IO /* No problems with these on my machine */
-#define FAST_WRITE_IO
+#include "in2000.h"
-#ifdef DEBUGGING_ON
-#define DB(f,a) if (hostdata->args & (f)) a;
-#define CHECK_NULL(p,s) /* if (!(p)) {printk("\n"); while (1) printk("NP:%s\r",(s));} */
-#else
-#define DB(f,a)
-#define CHECK_NULL(p,s)
-#endif
/*
- * setup_strings is an array of strings that define some of the operating
- * parameters and settings for this driver. It is used unless a LILO
- * or insmod command line has been specified, in which case those settings
- * are combined with the ones here. The driver recognizes the following
- * keywords (lower case required) and arguments:
+ * 'setup_strings' is a single string used to pass operating parameters and
+ * settings from the kernel/module command-line to the driver. 'setup_args[]'
+ * is an array of strings that define the compile-time default values for
+ * these settings. If Linux boots with a LILO or insmod command-line, those
+ * settings are combined with 'setup_args[]'. Note that LILO command-lines
+ * are prefixed with "in2000=" while insmod uses a "setup_strings=" prefix.
+ * The driver recognizes the following keywords (lower case required) and
+ * arguments:
*
* - ioport:addr -Where addr is IO address of a (usually ROM-less) card.
* - noreset -No optional args. Prevents SCSI bus reset at boot time.
@@ -179,13 +177,11 @@
* _must_ be a colon between a keyword and its numeric argument, with no
* spaces.
* - Keywords are separated by commas, no spaces, in the standard kernel
- * command-line manner, except in the case of 'setup_strings[]' (see
- * below), which is simply a C array of pointers to char. Each element
- * in the array is a string comprising one keyword & argument.
+ * command-line manner.
* - A keyword in the 'nth' comma-separated command-line member will overwrite
- * the 'nth' element of setup_strings[]. A blank command-line member (in
+ * the 'nth' element of setup_args[]. A blank command-line member (in
* other words, a comma with no preceding keyword) will _not_ overwrite
- * the corresponding setup_strings[] element.
+ * the corresponding setup_args[] element.
*
* A few LILO examples (for insmod, use 'setup_strings' instead of 'in2000'):
* - in2000=ioport:0x220,noreset
@@ -194,334 +190,20 @@
* - in2000=proc:3
*/
-static char *setup_strings[] =
- {"","","","","","","","","","","",""};
+/* Normally, no defaults are specified... */
+static char *setup_args[] =
+ {"","","","","","","","",""};
-static struct Scsi_Host *instance_list = 0;
+/* filled in by 'insmod' */
+static char *setup_strings = 0;
-#ifdef PROC_INTERFACE
-static unsigned long disc_allowed_total;
-static unsigned long disc_taken_total;
+#ifdef MODULE_PARM
+MODULE_PARM(setup_strings, "s");
#endif
-/* IN2000 io_port offsets */
-#define IO_WD_ASR 0x00 /* R - 3393 auxstat reg */
-#define ASR_INT 0x80
-#define ASR_LCI 0x40
-#define ASR_BSY 0x20
-#define ASR_CIP 0x10
-#define ASR_PE 0x02
-#define ASR_DBR 0x01
-#define IO_WD_ADDR 0x00 /* W - 3393 address reg */
-#define IO_WD_DATA 0x01 /* R/W - rest of 3393 regs */
-#define IO_FIFO 0x02 /* R/W - in2000 dual-port fifo (16 bits) */
-#define IN2000_FIFO_SIZE 2048 /* fifo capacity in bytes */
-#define IO_CARD_RESET 0x03 /* W - in2000 start master reset */
-#define IO_FIFO_COUNT 0x04 /* R - in2000 fifo counter */
-#define IO_FIFO_WRITE 0x05 /* W - clear fifo counter, start write */
-#define IO_FIFO_READ 0x07 /* W - start fifo read */
-#define IO_LED_OFF 0x08 /* W - turn off in2000 activity LED */
-#define IO_SWITCHES 0x08 /* R - read in2000 dip switch */
-#define SW_ADDR0 0x01 /* bit 0 = bit 0 of index to io addr */
-#define SW_ADDR1 0x02 /* bit 1 = bit 1 of index io addr */
-#define SW_DISINT 0x04 /* bit 2 true if ints disabled */
-#define SW_INT0 0x08 /* bit 3 = bit 0 of index to interrupt */
-#define SW_INT1 0x10 /* bit 4 = bit 1 of index to interrupt */
-#define SW_INT_SHIFT 3 /* shift right this amount to right justify int bits */
-#define SW_SYNC_DOS5 0x20 /* bit 5 used by Always BIOS */
-#define SW_FLOPPY 0x40 /* bit 6 true if floppy enabled */
-#define SW_BIT7 0x80 /* bit 7 hardwired true (ground) */
-#define IO_LED_ON 0x09 /* W - turn on in2000 activity LED */
-#define IO_HARDWARE 0x0a /* R - read in2000 hardware rev, stop reset */
-#define IO_INTR_MASK 0x0c /* W - in2000 interrupt mask reg */
-#define IMASK_WD 0x01 /* WD33c93 interrupt mask */
-#define IMASK_FIFO 0x02 /* FIFO interrupt mask */
-
-/* wd register names */
-#define WD_OWN_ID 0x00
-#define WD_CONTROL 0x01
-#define WD_TIMEOUT_PERIOD 0x02
-#define WD_CDB_1 0x03
-#define WD_CDB_2 0x04
-#define WD_CDB_3 0x05
-#define WD_CDB_4 0x06
-#define WD_CDB_5 0x07
-#define WD_CDB_6 0x08
-#define WD_CDB_7 0x09
-#define WD_CDB_8 0x0a
-#define WD_CDB_9 0x0b
-#define WD_CDB_10 0x0c
-#define WD_CDB_11 0x0d
-#define WD_CDB_12 0x0e
-#define WD_TARGET_LUN 0x0f
-#define WD_COMMAND_PHASE 0x10
-#define WD_SYNCHRONOUS_TRANSFER 0x11
-#define WD_TRANSFER_COUNT_MSB 0x12
-#define WD_TRANSFER_COUNT 0x13
-#define WD_TRANSFER_COUNT_LSB 0x14
-#define WD_DESTINATION_ID 0x15
-#define WD_SOURCE_ID 0x16
-#define WD_SCSI_STATUS 0x17
-#define WD_COMMAND 0x18
-#define WD_DATA 0x19
-#define WD_QUEUE_TAG 0x1a
-#define WD_AUXILIARY_STATUS 0x1f
-
-/* WD commands */
-#define WD_CMD_RESET 0x00
-#define WD_CMD_ABORT 0x01
-#define WD_CMD_ASSERT_ATN 0x02
-#define WD_CMD_NEGATE_ACK 0x03
-#define WD_CMD_DISCONNECT 0x04
-#define WD_CMD_RESELECT 0x05
-#define WD_CMD_SEL_ATN 0x06
-#define WD_CMD_SEL 0x07
-#define WD_CMD_SEL_ATN_XFER 0x08
-#define WD_CMD_SEL_XFER 0x09
-#define WD_CMD_RESEL_RECEIVE 0x0a
-#define WD_CMD_RESEL_SEND 0x0b
-#define WD_CMD_WAIT_SEL_RECEIVE 0x0c
-#define WD_CMD_TRANS_ADDR 0x18
-#define WD_CMD_TRANS_INFO 0x20
-#define WD_CMD_TRANSFER_PAD 0x21
-#define WD_CMD_SBT_MODE 0x80
-
-/* SCSI Bus Phases */
-#define PHS_DATA_OUT 0x00
-#define PHS_DATA_IN 0x01
-#define PHS_COMMAND 0x02
-#define PHS_STATUS 0x03
-#define PHS_MESS_OUT 0x06
-#define PHS_MESS_IN 0x07
-
-/* Command Status Register definitions */
-
- /* reset state interrupts */
-#define CSR_RESET 0x00
-#define CSR_RESET_AF 0x01
-
- /* successful completion interrupts */
-#define CSR_RESELECT 0x10
-#define CSR_SELECT 0x11
-#define CSR_SEL_XFER_DONE 0x16
-#define CSR_XFER_DONE 0x18
-
- /* paused or aborted interrupts */
-#define CSR_MSGIN 0x20
-#define CSR_SDP 0x21
-#define CSR_SEL_ABORT 0x22
-#define CSR_RESEL_ABORT 0x25
-#define CSR_RESEL_ABORT_AM 0x27
-#define CSR_ABORT 0x28
-
- /* terminated interrupts */
-#define CSR_INVALID 0x40
-#define CSR_UNEXP_DISC 0x41
-#define CSR_TIMEOUT 0x42
-#define CSR_PARITY 0x43
-#define CSR_PARITY_ATN 0x44
-#define CSR_BAD_STATUS 0x45
-#define CSR_UNEXP 0x48
-
- /* service required interrupts */
-#define CSR_RESEL 0x80
-#define CSR_RESEL_AM 0x81
-#define CSR_DISC 0x85
-#define CSR_SRV_REQ 0x88
-
- /* Own ID/CDB Size register */
-#define OWNID_EAF 0x08
-#define OWNID_EHP 0x10
-#define OWNID_RAF 0x20
-#define OWNID_FS_8 0x00
-#define OWNID_FS_12 0x40
-#define OWNID_FS_16 0x80
-
- /* Control register */
-#define CTRL_HSP 0x01
-#define CTRL_HA 0x02
-#define CTRL_IDI 0x04
-#define CTRL_EDI 0x08
-#define CTRL_HHP 0x10
-#define CTRL_POLLED 0x00
-#define CTRL_BURST 0x20
-#define CTRL_BUS 0x40
-#define CTRL_DMA 0x80
-
- /* Timeout Period register */
-#define TIMEOUT_PERIOD_VALUE 20 /* results in 200 ms. */
-
- /* Synchronous Transfer Register */
-#define STR_FSS 0x80
-
- /* Destination ID register */
-#define DSTID_DPD 0x40
-#define DATA_OUT_DIR 0
-#define DATA_IN_DIR 1
-#define DSTID_SCC 0x80
-
- /* Source ID register */
-#define SRCID_MASK 0x07
-#define SRCID_SIV 0x08
-#define SRCID_DSP 0x20
-#define SRCID_ES 0x40
-#define SRCID_ER 0x80
-
-
-
-#define DEFAULT_SX_PER 500 /* (ns) fairly safe */
-#define DEFAULT_SX_OFF 0 /* aka async */
-
-#define OPTIMUM_SX_PER 252 /* (ns) best we can do (mult-of-4) */
-#define OPTIMUM_SX_OFF 12 /* size of in2000 fifo */
-
-
-/* defines for hostdata->chip */
-
-#define C_WD33C93 0
-#define C_WD33C93A 1
-#define C_WD33C93B 2
-#define C_UNKNOWN_CHIP 100
-
-/* defines for hostdata->state */
-
-#define S_UNCONNECTED 0
-#define S_SELECTING 1
-#define S_RUNNING_LEVEL2 2
-#define S_CONNECTED 3
-#define S_PRE_TMP_DISC 4
-#define S_PRE_CMP_DISC 5
-
-/* defines for hostdata->fifo */
-
-#define FI_FIFO_UNUSED 0
-#define FI_FIFO_READING 1
-#define FI_FIFO_WRITING 2
-
-/* defines for hostdata->level2 */
-/* NOTE: only the first 3 are trustworthy at this point -
- * having trouble when more than 1 device is reading/writing
- * at the same time...
- */
-
-#define L2_NONE 0 /* no combination commands - we get lots of ints */
-#define L2_SELECT 1 /* start with SEL_ATN_XFER, but never resume it */
-#define L2_BASIC 2 /* resume after STATUS ints & RDP messages */
-#define L2_DATA 3 /* resume after DATA_IN/OUT ints */
-#define L2_MOST 4 /* resume after anything except a RESELECT int */
-#define L2_RESELECT 5 /* resume after everything, including RESELECT ints */
-#define L2_ALL 6 /* always resume */
-
-/* defines for hostdata->disconnect */
-
-#define DIS_NEVER 0
-#define DIS_ADAPTIVE 1
-#define DIS_ALWAYS 2
-
-/* defines for hostdata->args */
-
-#define DB_TEST 1<<0
-#define DB_FIFO 1<<1
-#define DB_QUEUE_COMMAND 1<<2
-#define DB_EXECUTE 1<<3
-#define DB_INTR 1<<4
-#define DB_TRANSFER 1<<5
-#define DB_MASK 0x3f
-#define A_NO_SCSI_RESET 1<<15
-
-
-/* defines for hostdata->sync_xfer[] */
-
-#define SS_UNSET 0
-#define SS_FIRST 1
-#define SS_WAITING 2
-#define SS_SET 3
-
-/* defines for hostdata->proc */
-
-#define PR_VERSION 1<<0
-#define PR_INFO 1<<1
-#define PR_TOTALS 1<<2
-#define PR_CONNECTED 1<<3
-#define PR_INPUTQ 1<<4
-#define PR_DISCQ 1<<5
-#define PR_TEST 1<<6
-#define PR_STOP 1<<7
-
-
-#define read1_io(a) (inb(hostdata->io_base+(a)))
-#define read2_io(a) (inw(hostdata->io_base+(a)))
-#define write1_io(b,a) (outb((b),hostdata->io_base+(a)))
-#define write2_io(w,a) (outw((w),hostdata->io_base+(a)))
-
-
-struct sx_period {
- unsigned int period_ns;
- uchar reg_value;
- };
-
-
-struct IN2000_hostdata {
- struct Scsi_Host *next;
- uchar chip; /* what kind of wd33c93 chip? */
- uchar microcode; /* microcode rev if 'B' */
- unsigned short io_base; /* IO port base */
- unsigned int dip_switch; /* dip switch settings */
- unsigned int hrev; /* hardware revision of card */
- volatile uchar busy[8]; /* index = target, bit = lun */
- volatile Scsi_Cmnd *input_Q; /* commands waiting to be started */
- volatile Scsi_Cmnd *selecting; /* trying to select this command */
- volatile Scsi_Cmnd *connected; /* currently connected command */
- volatile Scsi_Cmnd *disconnected_Q;/* commands waiting for reconnect */
- uchar state; /* what we are currently doing */
- uchar fifo; /* what the FIFO is up to */
- uchar level2; /* extent to which Level-2 commands are used */
- uchar disconnect; /* disconnect/reselect policy */
- unsigned int args; /* set from command-line argument */
- uchar incoming_msg[8]; /* filled during message_in phase */
- int incoming_ptr; /* mainly used with EXTENDED messages */
- uchar outgoing_msg[8]; /* send this during next message_out */
- int outgoing_len; /* length of outgoing message */
- unsigned int default_sx_per; /* default transfer period for SCSI bus */
- uchar sync_xfer[8]; /* sync_xfer reg settings per target */
- uchar sync_stat[8]; /* status of sync negotiation per target */
- uchar sync_off; /* bit mask: don't use sync with these targets */
- uchar proc; /* bit mask: what's in proc output */
- };
-
-/* These inline assembly defines are derived from a patch
- * sent to me by Bill Earnest. He's done a lot of very
- * valuable thinking, testing, and coding during his effort
- * to squeeze more speed out of this driver. I really think
- * that we are doing IO at close to the maximum now with
- * the fifo. (And yes, insw uses 'edi' while outsw uses
- * 'esi'. Thanks Bill!)
- */
+static struct Scsi_Host *instance_list = 0;
-#define FAST_READ2_IO() \
- __asm__ __volatile__ ("\n \
- cld \n \
- orl %%ecx, %%ecx \n \
- jz 1f \n \
- rep \n \
- insw %%dx \n \
-1: " \
- : "=D" (sp) /* output */ \
- : "d" (f), "D" (sp), "c" (i) /* input */ \
- : "edx", "ecx", "edi" ) /* trashed */
-
-#define FAST_WRITE2_IO() \
- __asm__ __volatile__ ("\n \
- cld \n \
- orl %%ecx, %%ecx \n \
- jz 1f \n \
- rep \n \
- outsw %%dx \n \
-1: " \
- : "=S" (sp) /* output */ \
- : "d" (f), "S" (sp), "c" (i) /* input */ \
- : "edx", "ecx", "esi" ) /* trashed */
static inline uchar read_3393(struct IN2000_hostdata *hostdata, uchar reg_num)
@@ -596,10 +278,10 @@ static int is_dir_out(Scsi_Cmnd *cmd)
switch (cmd->cmnd[0]) {
case WRITE_6: case WRITE_10: case WRITE_12:
case WRITE_LONG: case WRITE_SAME: case WRITE_BUFFER:
- case WRITE_VERIFY: case WRITE_VERIFY_12:
+ case WRITE_VERIFY: case WRITE_VERIFY_12:
case COMPARE: case COPY: case COPY_VERIFY:
case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW:
- case SEARCH_EQUAL_12: case SEARCH_HIGH_12: case SEARCH_LOW_12:
+ case SEARCH_EQUAL_12: case SEARCH_HIGH_12: case SEARCH_LOW_12:
case FORMAT_UNIT: case REASSIGN_BLOCKS: case RESERVE:
case MODE_SELECT: case MODE_SELECT_10: case LOG_SELECT:
case SEND_DIAGNOSTIC: case CHANGE_DEFINITION: case UPDATE_BLOCK:
@@ -624,7 +306,7 @@ static struct sx_period sx_table[] = {
{1000,0x00},
{0, 0} };
-int round_period(unsigned int period)
+static int round_period(unsigned int period)
{
int x;
@@ -657,7 +339,6 @@ struct IN2000_hostdata *hostdata;
Scsi_Cmnd *tmp;
unsigned long flags;
-
hostdata = (struct IN2000_hostdata *)cmd->host->hostdata;
DB(DB_QUEUE_COMMAND,printk("Q-%d-%02x-%ld(",cmd->target,cmd->cmnd[0],cmd->pid))
@@ -675,7 +356,7 @@ DB(DB_QUEUE_COMMAND,printk("Q-%d-%02x-%ld(",cmd->target,cmd->cmnd[0],cmd->pid))
/* We use the Scsi_Pointer structure that's included with each command
* as a scratchpad (as it's intended to be used!). The handy thing about
* the SCp.xxx fields is that they're always associated with a given
- * cmd, and are preserved across disconnect-reconnect. This means we
+ * cmd, and are preserved across disconnect-reselect. This means we
* can pretty much ignore SAVE_POINTERS and RESTORE_POINTERS messages
* if we keep all the critical pointers and counters in SCp:
* - SCp.ptr is the pointer into the RAM buffer
@@ -703,9 +384,24 @@ DB(DB_QUEUE_COMMAND,printk("Q-%d-%02x-%ld(",cmd->target,cmd->cmnd[0],cmd->pid))
/* We don't set SCp.phase here - that's done in in2000_execute() */
-/* Preset the command status to GOOD, since that's the normal case */
+/* WD docs state that at the conclusion of a "LEVEL2" command, the
+ * status byte can be retrieved from the LUN register. Apparently,
+ * this is the case only for *uninterrupted* LEVEL2 commands! If
+ * there are any unexpected phases entered, even if they are 100%
+ * legal (different devices may choose to do things differently),
+ * the LEVEL2 command sequence is exited. This often occurs prior
+ * to receiving the status byte, in which case the driver does a
+ * status phase interrupt and gets the status byte on its own.
+ * While such a command can then be "resumed" (ie restarted to
+ * finish up as a LEVEL2 command), the LUN register will NOT be
+ * a valid status byte at the command's conclusion, and we must
+ * use the byte obtained during the earlier interrupt. Here, we
+ * preset SCp.Status to an illegal value (0xff) so that when
+ * this command finally completes, we can tell where the actual
+ * status byte is stored.
+ */
- cmd->SCp.Status = GOOD;
+ cmd->SCp.Status = ILLEGAL_STATUS_BYTE;
save_flags(flags);
cli();
@@ -803,6 +499,10 @@ DB(DB_EXECUTE,printk(")EX-1 "))
else
hostdata->input_Q = (Scsi_Cmnd *)cmd->host_scribble;
+#ifdef PROC_STATISTICS
+ hostdata->cmd_cnt[cmd->target]++;
+#endif
+
/*
* Start the selection process
*/
@@ -860,8 +560,8 @@ DB(DB_EXECUTE,printk(")EX-1 "))
yes:
cmd->SCp.phase = 1;
-#ifdef PROC_INTERFACE
- disc_allowed_total++;
+#ifdef PROC_STATISTICS
+ hostdata->disc_allowed_cnt[cmd->target]++;
#endif
no:
@@ -1094,7 +794,8 @@ int i;
if (data_in_dir) {
write1_io(0,IO_FIFO_READ);
- if ((hostdata->level2 >= L2_DATA) || (cmd->SCp.phase == 0)) {
+ if ((hostdata->level2 >= L2_DATA) ||
+ (hostdata->level2 == L2_BASIC && cmd->SCp.phase == 0)) {
write_3393(hostdata,WD_COMMAND_PHASE,0x45);
write_3393_cmd(hostdata,WD_CMD_SEL_ATN_XFER);
hostdata->state = S_RUNNING_LEVEL2;
@@ -1111,7 +812,8 @@ int i;
* write any bytes that don't make it at this stage.
*/
- if ((hostdata->level2 >= L2_DATA) || (cmd->SCp.phase == 0)) {
+ if ((hostdata->level2 >= L2_DATA) ||
+ (hostdata->level2 == L2_BASIC && cmd->SCp.phase == 0)) {
write_3393(hostdata,WD_COMMAND_PHASE,0x45);
write_3393_cmd(hostdata,WD_CMD_SEL_ATN_XFER);
hostdata->state = S_RUNNING_LEVEL2;
@@ -1177,6 +879,10 @@ unsigned short f;
save_flags(flags);
sti();
+#ifdef PROC_STATISTICS
+ hostdata->int_cnt++;
+#endif
+
/* The IN2000 card has 2 interrupt sources OR'ed onto its IRQ line - the
* WD3393 chip and the 2k fifo (which is actually a dual-port RAM combined
* with a big logic array, so it's a little different than what you might
@@ -1484,9 +1190,10 @@ DB(DB_INTR,printk("CMND-%02x,%ld",cmd->cmnd[0],cmd->pid))
case CSR_XFER_DONE|PHS_STATUS:
case CSR_UNEXP |PHS_STATUS:
case CSR_SRV_REQ |PHS_STATUS:
-DB(DB_INTR,printk("STATUS"))
+DB(DB_INTR,printk("STATUS="))
cmd->SCp.Status = read_1_byte(hostdata);
+DB(DB_INTR,printk("%02x",cmd->SCp.Status))
if (hostdata->level2 >= L2_BASIC) {
sr = read_3393(hostdata,WD_SCSI_STATUS); /* clear interrupt */
hostdata->state = S_RUNNING_LEVEL2;
@@ -1494,7 +1201,6 @@ DB(DB_INTR,printk("STATUS"))
write_3393_cmd(hostdata,WD_CMD_SEL_ATN_XFER);
}
else {
-DB(DB_INTR,printk("=%02x",cmd->SCp.Status))
hostdata->state = S_CONNECTED;
}
break;
@@ -1665,15 +1371,16 @@ printk("sync_xfer=%02x",hostdata->sync_xfer[cmd->target]);
DB(DB_INTR,printk("SX-DONE-%ld",cmd->pid))
cmd->SCp.Message = COMMAND_COMPLETE;
lun = read_3393(hostdata,WD_TARGET_LUN);
- if (cmd->SCp.Status == GOOD)
- cmd->SCp.Status = lun;
+DB(DB_INTR,printk(":%d.%d",cmd->SCp.Status,lun))
hostdata->connected = NULL;
- if (cmd->cmnd[0] != REQUEST_SENSE)
- cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
- else if (cmd->SCp.Status != GOOD)
- cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
hostdata->state = S_UNCONNECTED;
+ if (cmd->SCp.Status == ILLEGAL_STATUS_BYTE)
+ cmd->SCp.Status = lun;
+ if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD)
+ cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
+ else
+ cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
cmd->scsi_done(cmd);
/* We are no longer connected to a target - check to see if
@@ -1755,10 +1462,10 @@ DB(DB_INTR,printk("UNEXP_DISC-%ld",cmd->pid))
hostdata->connected = NULL;
hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
hostdata->state = S_UNCONNECTED;
- if (cmd->cmnd[0] != REQUEST_SENSE)
- cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
- else if (cmd->SCp.Status != GOOD)
+ if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD)
cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
+ else
+ cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
cmd->scsi_done(cmd);
/* We are no longer connected to a target - check to see if
@@ -1788,10 +1495,11 @@ DB(DB_INTR,printk("DISC-%ld",cmd->pid))
hostdata->connected = NULL;
hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
hostdata->state = S_UNCONNECTED;
- if (cmd->cmnd[0] != REQUEST_SENSE)
- cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
- else if (cmd->SCp.Status != GOOD)
+DB(DB_INTR,printk(":%d",cmd->SCp.Status))
+ if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD)
cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
+ else
+ cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
cmd->scsi_done(cmd);
break;
case S_PRE_TMP_DISC:
@@ -1801,8 +1509,8 @@ DB(DB_INTR,printk("DISC-%ld",cmd->pid))
hostdata->connected = NULL;
hostdata->state = S_UNCONNECTED;
-#ifdef PROC_INTERFACE
- disc_taken_total++;
+#ifdef PROC_STATISTICS
+ hostdata->disc_done_cnt[cmd->target]++;
#endif
break;
@@ -2158,10 +1866,11 @@ unsigned long timeout;
#define MAX_IN2000_HOSTS 3
-#define MAX_SETUP_STRINGS (sizeof(setup_strings) / sizeof(char *))
+#define MAX_SETUP_ARGS (sizeof(setup_args) / sizeof(char *))
#define SETUP_BUFFER_SIZE 200
static char setup_buffer[SETUP_BUFFER_SIZE];
-static char setup_used[MAX_SETUP_STRINGS];
+static char setup_used[MAX_SETUP_ARGS];
+static int done_setup = 0;
void in2000_setup (char *str, int *ints)
{
@@ -2172,43 +1881,44 @@ char *p1,*p2;
setup_buffer[SETUP_BUFFER_SIZE - 1] = '\0';
p1 = setup_buffer;
i = 0;
- while (*p1 && (i < MAX_SETUP_STRINGS)) {
+ while (*p1 && (i < MAX_SETUP_ARGS)) {
p2 = strchr(p1, ',');
if (p2) {
*p2 = '\0';
if (p1 != p2)
- setup_strings[i] = p1;
+ setup_args[i] = p1;
p1 = p2 + 1;
i++;
}
else {
- setup_strings[i] = p1;
+ setup_args[i] = p1;
break;
}
}
- for (i=0; i<MAX_SETUP_STRINGS; i++)
+ for (i=0; i<MAX_SETUP_ARGS; i++)
setup_used[i] = 0;
+ done_setup = 1;
}
-/* check_setup_strings() returns index if key found, 0 if not
+/* check_setup_args() returns index if key found, 0 if not
*/
-static int check_setup_strings(char *key, int *flags, int *val, char *buf)
+static int check_setup_args(char *key, int *flags, int *val, char *buf)
{
int x;
char *cp;
- for (x=0; x<MAX_SETUP_STRINGS; x++) {
+ for (x=0; x<MAX_SETUP_ARGS; x++) {
if (setup_used[x])
continue;
- if (!strncmp(setup_strings[x], key, strlen(key)))
+ if (!strncmp(setup_args[x], key, strlen(key)))
break;
}
- if (x == MAX_SETUP_STRINGS)
+ if (x == MAX_SETUP_ARGS)
return 0;
setup_used[x] = 1;
- cp = setup_strings[x] + strlen(key);
+ cp = setup_args[x] + strlen(key);
*val = -1;
if (*cp != ':')
return ++x;
@@ -2221,16 +1931,10 @@ char *cp;
-struct proc_dir_entry proc_scsi_in2000 = {
- PROC_SCSI_IN2000, 6, "in2000",
- S_IFDIR | S_IRUGO | S_IXUGO, 2
- };
-
-
-/* As of the 2.1.x kernel series, memory-mapped hardware such
- * as the IN2000 EPROM and dip switch must be accessed through
- * special macros declared in 'asm/io.h'. We use readb() and
- * readl() when reading from the card's BIOS area in in2000_detect().
+/* The "correct" (ie portable) way to access memory-mapped hardware
+ * such as the IN2000 EPROM and dip switch is through the use of
+ * special macros declared in 'asm/io.h'. We use readb() and readl()
+ * when reading from the card's BIOS area in in2000_detect().
*/
static const unsigned int *bios_tab[] = {
(unsigned int *)0xc8000,
@@ -2253,6 +1957,7 @@ static const int int_tab[] = {
10
};
+
int in2000_detect(Scsi_Host_Template * tpnt)
{
struct Scsi_Host *instance;
@@ -2268,29 +1973,33 @@ int val;
char buf[32];
/* Thanks to help from Bill Earnest, probing for IN2000 cards is a
- * pretty straightforward and fool-proof operation. We do require
- * that cards have their BIOS enabled, although I hope to be able
- * to detect and use BIOS-less cards in the future. There are 3
+ * pretty straightforward and fool-proof operation. There are 3
* possible locations for the IN2000 EPROM in memory space - if we
* find a BIOS signature, we can read the dip switch settings from
* the byte at BIOS+32 (shadowed in by logic on the card). From 2
* of the switch bits we get the card's address in IO space. There's
* an image of the dip switch there, also, so we have a way to back-
- * check that this really is an IN2000 card. Very nifty.
- *
- * There have been a couple of BIOS versions with different layouts
- * for the obvious ID strings. We look for the 2 most common ones and
- * hope that they cover all the cases...
+ * check that this really is an IN2000 card. Very nifty. Use the
+ * 'ioport:xx' command-line parameter if your BIOS EPROM is absent
+ * or disabled.
*/
+ if (!done_setup && setup_strings)
+ in2000_setup(setup_strings,0);
+
detect_count = 0;
for (bios = 0; bios_tab[bios]; bios++) {
- if (check_setup_strings("ioport",&flags,&val,buf)) {
+ if (check_setup_args("ioport",&flags,&val,buf)) {
base = val;
switches = ~inb(base + IO_SWITCHES) & 0xff;
printk("Forcing IN2000 detection at IOport 0x%x ",base);
bios = 2;
}
+/*
+ * There have been a couple of BIOS versions with different layouts
+ * for the obvious ID strings. We look for the 2 most common ones and
+ * hope that they cover all the cases...
+ */
else if (readl(bios_tab[bios]+0x04) == 0x41564f4e ||
readl(bios_tab[bios]+0x0c) == 0x61776c41) {
printk("Found IN2000 BIOS at 0x%x ",(unsigned int)bios_tab[bios]);
@@ -2374,6 +2083,11 @@ char buf[32];
hostdata->busy[x] = 0;
hostdata->sync_xfer[x] = calc_sync_xfer(DEFAULT_SX_PER/4,DEFAULT_SX_OFF);
hostdata->sync_stat[x] = SS_UNSET; /* using default sync values */
+#ifdef PROC_STATISTICS
+ hostdata->cmd_cnt[x] = 0;
+ hostdata->disc_allowed_cnt[x] = 0;
+ hostdata->disc_done_cnt[x] = 0;
+#endif
}
hostdata->input_Q = NULL;
hostdata->selecting = NULL;
@@ -2395,36 +2109,42 @@ char buf[32];
else
hostdata->sync_off = 0xff; /* sync defaults to off */
- hostdata->proc = PR_VERSION|PR_INFO|PR_TOTALS|
+#ifdef PROC_INTERFACE
+ hostdata->proc = PR_VERSION|PR_INFO|PR_STATISTICS|
PR_CONNECTED|PR_INPUTQ|PR_DISCQ|
PR_STOP;
-
-#ifdef PROC_INTERFACE
- disc_allowed_total = 0;
- disc_taken_total = 0;
+#ifdef PROC_STATISTICS
+ hostdata->int_cnt = 0;
+#endif
#endif
- if (check_setup_strings("nosync",&flags,&val,buf))
+ if (check_setup_args("nosync",&flags,&val,buf))
hostdata->sync_off = val;
- if (check_setup_strings("period",&flags,&val,buf))
+ if (check_setup_args("period",&flags,&val,buf))
hostdata->default_sx_per = sx_table[round_period((unsigned int)val)].period_ns;
- if (check_setup_strings("disconnect",&flags,&val,buf)) {
+ if (check_setup_args("disconnect",&flags,&val,buf)) {
if ((val >= DIS_NEVER) && (val <= DIS_ALWAYS))
hostdata->disconnect = val;
else
hostdata->disconnect = DIS_ADAPTIVE;
}
- if (check_setup_strings("noreset",&flags,&val,buf))
+ if (check_setup_args("noreset",&flags,&val,buf))
hostdata->args ^= A_NO_SCSI_RESET;
- if (check_setup_strings("debug",&flags,&val,buf))
+ if (check_setup_args("level2",&flags,&val,buf))
+ hostdata->level2 = val;
+
+ if (check_setup_args("debug",&flags,&val,buf))
hostdata->args = (val & DB_MASK);
- if (check_setup_strings("proc",&flags,&val,buf))
+#ifdef PROC_INTERFACE
+ if (check_setup_args("proc",&flags,&val,buf))
hostdata->proc = val;
+#endif
+
x = reset_hardware(instance,(hostdata->args & A_NO_SCSI_RESET)?RESET_CARD:RESET_CARD_AND_BUS);
@@ -2450,9 +2170,9 @@ char buf[32];
(hostdata->chip==C_WD33C93B)?"WD33c93B":"unknown",
hostdata->microcode);
#ifdef DEBUGGING_ON
- printk("setup_strings = ");
- for (x=0; x<8; x++)
- printk("%s,",setup_strings[x]);
+ printk("setup_args = ");
+ for (x=0; x<MAX_SETUP_ARGS; x++)
+ printk("%s,",setup_args[x]);
printk("\n");
#endif
if (hostdata->sync_off == 0xff)
@@ -2496,23 +2216,18 @@ int size;
iinfo[0] = 255;
iinfo[1] = 63;
iinfo[2] = disk->capacity / (iinfo[0] * iinfo[1]);
-
-/* This next little bit of code was intended to prevent the number of
- * tracks from exceeding 1023. As Andries Brouwer (aeb@cwi.nl) pointed
- * out in his "Large Disk HOWTO" (June 1996), this kind of DOS
- * compatibility is pointless. And wasteful on disks larger than 8 gigs.
- */
-
-#if 0
- if (iinfo[2] > 1023)
- iinfo[2] = 1023;
-#endif
-
}
return 0;
}
+
+struct proc_dir_entry proc_scsi_in2000 = {
+ PROC_SCSI_IN2000, 6, "in2000",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+ };
+
+
int in2000_proc_info(char *buf, char **start, off_t off, int len, int hn, int in)
{
@@ -2576,6 +2291,10 @@ static int stop = 0;
bp += 5;
hd->proc = simple_strtoul(bp,NULL,0);
}
+ else if (!strncmp(bp,"level2:",7)) {
+ bp += 7;
+ hd->level2 = simple_strtoul(bp,NULL,0);
+ }
return len;
}
@@ -2594,12 +2313,38 @@ static int stop = 0;
(hd->dip_switch & 0x40)?"Yes":"No",
(hd->dip_switch & 0x20)?"Yes":"No");
strcat(bp,tbuf);
+ strcat(bp,"\nsync_xfer[] = ");
+ for (x=0; x<7; x++) {
+ sprintf(tbuf,"\t%02x",hd->sync_xfer[x]);
+ strcat(bp,tbuf);
+ }
+ strcat(bp,"\nsync_stat[] = ");
+ for (x=0; x<7; x++) {
+ sprintf(tbuf,"\t%02x",hd->sync_stat[x]);
+ strcat(bp,tbuf);
+ }
}
- if (hd->proc & PR_TOTALS) {
- sprintf(tbuf,"\n%ld disc_allowed, %ld disc_taken",
- disc_allowed_total,disc_taken_total);
+#ifdef PROC_STATISTICS
+ if (hd->proc & PR_STATISTICS) {
+ strcat(bp,"\ncommands issued: ");
+ for (x=0; x<7; x++) {
+ sprintf(tbuf,"\t%ld",hd->cmd_cnt[x]);
+ strcat(bp,tbuf);
+ }
+ strcat(bp,"\ndisconnects allowed:");
+ for (x=0; x<7; x++) {
+ sprintf(tbuf,"\t%ld",hd->disc_allowed_cnt[x]);
+ strcat(bp,tbuf);
+ }
+ strcat(bp,"\ndisconnects done: ");
+ for (x=0; x<7; x++) {
+ sprintf(tbuf,"\t%ld",hd->disc_done_cnt[x]);
+ strcat(bp,tbuf);
+ }
+ sprintf(tbuf,"\ninterrupts: \t%ld",hd->int_cnt);
strcat(bp,tbuf);
}
+#endif
if (hd->proc & PR_CONNECTED) {
strcat(bp,"\nconnected: ");
if (hd->connected) {
diff --git a/drivers/scsi/in2000.h b/drivers/scsi/in2000.h
index 4bae99bc1..bb75a08c5 100644
--- a/drivers/scsi/in2000.h
+++ b/drivers/scsi/in2000.h
@@ -2,7 +2,7 @@
* in2000.h - Linux device driver definitions for the
* Always IN2000 ISA SCSI card.
*
- * IMPORTANT: This file is for version 1.30 - 14/Oct/1996
+ * IMPORTANT: This file is for version 1.31 - 06/Jul/1997
*
* Copyright (c) 1996 John Shifflett, GeoLog Consulting
* john@geolog.com
@@ -23,13 +23,366 @@
#ifndef IN2000_H
#define IN2000_H
-extern struct proc_dir_entry proc_scsi_in2000;
+#include <asm/io.h>
+
+#define PROC_INTERFACE /* add code for /proc/scsi/in2000/xxx interface */
+#ifdef PROC_INTERFACE
+#define PROC_STATISTICS /* add code for keeping various real time stats */
+#endif
+
+#define SYNC_DEBUG /* extra info on sync negotiation printed */
+#define DEBUGGING_ON /* enable command-line debugging bitmask */
+#define DEBUG_DEFAULTS 0 /* default bitmask - change from command-line */
+
+#define FAST_READ_IO /* No problems with these on my machine */
+#define FAST_WRITE_IO
+
+#ifdef DEBUGGING_ON
+#define DB(f,a) if (hostdata->args & (f)) a;
+#define CHECK_NULL(p,s) /* if (!(p)) {printk("\n"); while (1) printk("NP:%s\r",(s));} */
+#else
+#define DB(f,a)
+#define CHECK_NULL(p,s)
+#endif
+
+#define uchar unsigned char
+
+#define read1_io(a) (inb(hostdata->io_base+(a)))
+#define read2_io(a) (inw(hostdata->io_base+(a)))
+#define write1_io(b,a) (outb((b),hostdata->io_base+(a)))
+#define write2_io(w,a) (outw((w),hostdata->io_base+(a)))
+
+/* These inline assembly defines are derived from a patch
+ * sent to me by Bill Earnest. He's done a lot of very
+ * valuable thinking, testing, and coding during his effort
+ * to squeeze more speed out of this driver. I really think
+ * that we are doing IO at close to the maximum now with
+ * the fifo. (And yes, insw uses 'edi' while outsw uses
+ * 'esi'. Thanks Bill!)
+ */
+
+#define FAST_READ2_IO() \
+ __asm__ __volatile__ ("\n \
+ cld \n \
+ orl %%ecx, %%ecx \n \
+ jz 1f \n \
+ rep \n \
+ insw %%dx \n \
+1: " \
+ : "=D" (sp) /* output */ \
+ : "d" (f), "D" (sp), "c" (i) /* input */ \
+ : "edx", "ecx", "edi" ) /* trashed */
+
+#define FAST_WRITE2_IO() \
+ __asm__ __volatile__ ("\n \
+ cld \n \
+ orl %%ecx, %%ecx \n \
+ jz 1f \n \
+ rep \n \
+ outsw %%dx \n \
+1: " \
+ : "=S" (sp) /* output */ \
+ : "d" (f), "S" (sp), "c" (i) /* input */ \
+ : "edx", "ecx", "esi" ) /* trashed */
+
+
+/* IN2000 io_port offsets */
+#define IO_WD_ASR 0x00 /* R - 3393 auxstat reg */
+#define ASR_INT 0x80
+#define ASR_LCI 0x40
+#define ASR_BSY 0x20
+#define ASR_CIP 0x10
+#define ASR_PE 0x02
+#define ASR_DBR 0x01
+#define IO_WD_ADDR 0x00 /* W - 3393 address reg */
+#define IO_WD_DATA 0x01 /* R/W - rest of 3393 regs */
+#define IO_FIFO 0x02 /* R/W - in2000 dual-port fifo (16 bits) */
+#define IN2000_FIFO_SIZE 2048 /* fifo capacity in bytes */
+#define IO_CARD_RESET 0x03 /* W - in2000 start master reset */
+#define IO_FIFO_COUNT 0x04 /* R - in2000 fifo counter */
+#define IO_FIFO_WRITE 0x05 /* W - clear fifo counter, start write */
+#define IO_FIFO_READ 0x07 /* W - start fifo read */
+#define IO_LED_OFF 0x08 /* W - turn off in2000 activity LED */
+#define IO_SWITCHES 0x08 /* R - read in2000 dip switch */
+#define SW_ADDR0 0x01 /* bit 0 = bit 0 of index to io addr */
+#define SW_ADDR1 0x02 /* bit 1 = bit 1 of index io addr */
+#define SW_DISINT 0x04 /* bit 2 true if ints disabled */
+#define SW_INT0 0x08 /* bit 3 = bit 0 of index to interrupt */
+#define SW_INT1 0x10 /* bit 4 = bit 1 of index to interrupt */
+#define SW_INT_SHIFT 3 /* shift right this amount to right justify int bits */
+#define SW_SYNC_DOS5 0x20 /* bit 5 used by Always BIOS */
+#define SW_FLOPPY 0x40 /* bit 6 true if floppy enabled */
+#define SW_BIT7 0x80 /* bit 7 hardwired true (ground) */
+#define IO_LED_ON 0x09 /* W - turn on in2000 activity LED */
+#define IO_HARDWARE 0x0a /* R - read in2000 hardware rev, stop reset */
+#define IO_INTR_MASK 0x0c /* W - in2000 interrupt mask reg */
+#define IMASK_WD 0x01 /* WD33c93 interrupt mask */
+#define IMASK_FIFO 0x02 /* FIFO interrupt mask */
+
+/* wd register names */
+#define WD_OWN_ID 0x00
+#define WD_CONTROL 0x01
+#define WD_TIMEOUT_PERIOD 0x02
+#define WD_CDB_1 0x03
+#define WD_CDB_2 0x04
+#define WD_CDB_3 0x05
+#define WD_CDB_4 0x06
+#define WD_CDB_5 0x07
+#define WD_CDB_6 0x08
+#define WD_CDB_7 0x09
+#define WD_CDB_8 0x0a
+#define WD_CDB_9 0x0b
+#define WD_CDB_10 0x0c
+#define WD_CDB_11 0x0d
+#define WD_CDB_12 0x0e
+#define WD_TARGET_LUN 0x0f
+#define WD_COMMAND_PHASE 0x10
+#define WD_SYNCHRONOUS_TRANSFER 0x11
+#define WD_TRANSFER_COUNT_MSB 0x12
+#define WD_TRANSFER_COUNT 0x13
+#define WD_TRANSFER_COUNT_LSB 0x14
+#define WD_DESTINATION_ID 0x15
+#define WD_SOURCE_ID 0x16
+#define WD_SCSI_STATUS 0x17
+#define WD_COMMAND 0x18
+#define WD_DATA 0x19
+#define WD_QUEUE_TAG 0x1a
+#define WD_AUXILIARY_STATUS 0x1f
+
+/* WD commands */
+#define WD_CMD_RESET 0x00
+#define WD_CMD_ABORT 0x01
+#define WD_CMD_ASSERT_ATN 0x02
+#define WD_CMD_NEGATE_ACK 0x03
+#define WD_CMD_DISCONNECT 0x04
+#define WD_CMD_RESELECT 0x05
+#define WD_CMD_SEL_ATN 0x06
+#define WD_CMD_SEL 0x07
+#define WD_CMD_SEL_ATN_XFER 0x08
+#define WD_CMD_SEL_XFER 0x09
+#define WD_CMD_RESEL_RECEIVE 0x0a
+#define WD_CMD_RESEL_SEND 0x0b
+#define WD_CMD_WAIT_SEL_RECEIVE 0x0c
+#define WD_CMD_TRANS_ADDR 0x18
+#define WD_CMD_TRANS_INFO 0x20
+#define WD_CMD_TRANSFER_PAD 0x21
+#define WD_CMD_SBT_MODE 0x80
+
+/* SCSI Bus Phases */
+#define PHS_DATA_OUT 0x00
+#define PHS_DATA_IN 0x01
+#define PHS_COMMAND 0x02
+#define PHS_STATUS 0x03
+#define PHS_MESS_OUT 0x06
+#define PHS_MESS_IN 0x07
+
+/* Command Status Register definitions */
+
+ /* reset state interrupts */
+#define CSR_RESET 0x00
+#define CSR_RESET_AF 0x01
+
+ /* successful completion interrupts */
+#define CSR_RESELECT 0x10
+#define CSR_SELECT 0x11
+#define CSR_SEL_XFER_DONE 0x16
+#define CSR_XFER_DONE 0x18
+
+ /* paused or aborted interrupts */
+#define CSR_MSGIN 0x20
+#define CSR_SDP 0x21
+#define CSR_SEL_ABORT 0x22
+#define CSR_RESEL_ABORT 0x25
+#define CSR_RESEL_ABORT_AM 0x27
+#define CSR_ABORT 0x28
+
+ /* terminated interrupts */
+#define CSR_INVALID 0x40
+#define CSR_UNEXP_DISC 0x41
+#define CSR_TIMEOUT 0x42
+#define CSR_PARITY 0x43
+#define CSR_PARITY_ATN 0x44
+#define CSR_BAD_STATUS 0x45
+#define CSR_UNEXP 0x48
+
+ /* service required interrupts */
+#define CSR_RESEL 0x80
+#define CSR_RESEL_AM 0x81
+#define CSR_DISC 0x85
+#define CSR_SRV_REQ 0x88
+
+ /* Own ID/CDB Size register */
+#define OWNID_EAF 0x08
+#define OWNID_EHP 0x10
+#define OWNID_RAF 0x20
+#define OWNID_FS_8 0x00
+#define OWNID_FS_12 0x40
+#define OWNID_FS_16 0x80
+
+ /* Control register */
+#define CTRL_HSP 0x01
+#define CTRL_HA 0x02
+#define CTRL_IDI 0x04
+#define CTRL_EDI 0x08
+#define CTRL_HHP 0x10
+#define CTRL_POLLED 0x00
+#define CTRL_BURST 0x20
+#define CTRL_BUS 0x40
+#define CTRL_DMA 0x80
+
+ /* Timeout Period register */
+#define TIMEOUT_PERIOD_VALUE 20 /* results in 200 ms. */
+
+ /* Synchronous Transfer Register */
+#define STR_FSS 0x80
+
+ /* Destination ID register */
+#define DSTID_DPD 0x40
+#define DATA_OUT_DIR 0
+#define DATA_IN_DIR 1
+#define DSTID_SCC 0x80
+
+ /* Source ID register */
+#define SRCID_MASK 0x07
+#define SRCID_SIV 0x08
+#define SRCID_DSP 0x20
+#define SRCID_ES 0x40
+#define SRCID_ER 0x80
+
+
+
+#define ILLEGAL_STATUS_BYTE 0xff
+
+
+#define DEFAULT_SX_PER 500 /* (ns) fairly safe */
+#define DEFAULT_SX_OFF 0 /* aka async */
+
+#define OPTIMUM_SX_PER 252 /* (ns) best we can do (mult-of-4) */
+#define OPTIMUM_SX_OFF 12 /* size of in2000 fifo */
+
+struct sx_period {
+ unsigned int period_ns;
+ uchar reg_value;
+ };
+
+
+struct IN2000_hostdata {
+ struct Scsi_Host *next;
+ uchar chip; /* what kind of wd33c93 chip? */
+ uchar microcode; /* microcode rev if 'B' */
+ unsigned short io_base; /* IO port base */
+ unsigned int dip_switch; /* dip switch settings */
+ unsigned int hrev; /* hardware revision of card */
+ volatile uchar busy[8]; /* index = target, bit = lun */
+ volatile Scsi_Cmnd *input_Q; /* commands waiting to be started */
+ volatile Scsi_Cmnd *selecting; /* trying to select this command */
+ volatile Scsi_Cmnd *connected; /* currently connected command */
+ volatile Scsi_Cmnd *disconnected_Q;/* commands waiting for reconnect */
+ uchar state; /* what we are currently doing */
+ uchar fifo; /* what the FIFO is up to */
+ uchar level2; /* extent to which Level-2 commands are used */
+ uchar disconnect; /* disconnect/reselect policy */
+ unsigned int args; /* set from command-line argument */
+ uchar incoming_msg[8]; /* filled during message_in phase */
+ int incoming_ptr; /* mainly used with EXTENDED messages */
+ uchar outgoing_msg[8]; /* send this during next message_out */
+ int outgoing_len; /* length of outgoing message */
+ unsigned int default_sx_per; /* default transfer period for SCSI bus */
+ uchar sync_xfer[8]; /* sync_xfer reg settings per target */
+ uchar sync_stat[8]; /* status of sync negotiation per target */
+ uchar sync_off; /* bit mask: don't use sync with these targets */
+#ifdef PROC_INTERFACE
+ uchar proc; /* bit mask: what's in proc output */
+#ifdef PROC_STATISTICS
+ unsigned long cmd_cnt[8]; /* # of commands issued per target */
+ unsigned long int_cnt; /* # of interrupts serviced */
+ unsigned long disc_allowed_cnt[8]; /* # of disconnects allowed per target */
+ unsigned long disc_done_cnt[8]; /* # of disconnects done per target*/
+#endif
+#endif
+ };
+
+
+/* defines for hostdata->chip */
+
+#define C_WD33C93 0
+#define C_WD33C93A 1
+#define C_WD33C93B 2
+#define C_UNKNOWN_CHIP 100
+
+/* defines for hostdata->state */
+
+#define S_UNCONNECTED 0
+#define S_SELECTING 1
+#define S_RUNNING_LEVEL2 2
+#define S_CONNECTED 3
+#define S_PRE_TMP_DISC 4
+#define S_PRE_CMP_DISC 5
+
+/* defines for hostdata->fifo */
+
+#define FI_FIFO_UNUSED 0
+#define FI_FIFO_READING 1
+#define FI_FIFO_WRITING 2
+
+/* defines for hostdata->level2 */
+/* NOTE: only the first 3 are trustworthy at this point -
+ * having trouble when more than 1 device is reading/writing
+ * at the same time...
+ */
+
+#define L2_NONE 0 /* no combination commands - we get lots of ints */
+#define L2_SELECT 1 /* start with SEL_ATN_XFER, but never resume it */
+#define L2_BASIC 2 /* resume after STATUS ints & RDP messages */
+#define L2_DATA 3 /* resume after DATA_IN/OUT ints */
+#define L2_MOST 4 /* resume after anything except a RESELECT int */
+#define L2_RESELECT 5 /* resume after everything, including RESELECT ints */
+#define L2_ALL 6 /* always resume */
+
+/* defines for hostdata->disconnect */
+
+#define DIS_NEVER 0
+#define DIS_ADAPTIVE 1
+#define DIS_ALWAYS 2
+
+/* defines for hostdata->args */
+
+#define DB_TEST 1<<0
+#define DB_FIFO 1<<1
+#define DB_QUEUE_COMMAND 1<<2
+#define DB_EXECUTE 1<<3
+#define DB_INTR 1<<4
+#define DB_TRANSFER 1<<5
+#define DB_MASK 0x3f
+
+#define A_NO_SCSI_RESET 1<<15
+
+
+/* defines for hostdata->sync_xfer[] */
+
+#define SS_UNSET 0
+#define SS_FIRST 1
+#define SS_WAITING 2
+#define SS_SET 3
+
+/* defines for hostdata->proc */
+
+#define PR_VERSION 1<<0
+#define PR_INFO 1<<1
+#define PR_STATISTICS 1<<2
+#define PR_CONNECTED 1<<3
+#define PR_INPUTQ 1<<4
+#define PR_DISCQ 1<<5
+#define PR_TEST 1<<6
+#define PR_STOP 1<<7
+
int in2000_detect(Scsi_Host_Template *);
int in2000_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
int in2000_abort(Scsi_Cmnd *);
void in2000_setup(char *, int *);
int in2000_proc_info(char *, char **, off_t, int, int, int);
+struct proc_dir_entry proc_scsi_in2000;
int in2000_biosparam(struct scsi_disk *, kdev_t, int *);
int in2000_reset(Scsi_Cmnd *, unsigned int);
@@ -40,7 +393,7 @@ int in2000_reset(Scsi_Cmnd *, unsigned int);
#define IN2000_HOST_ID 7
#define IN2000 { NULL, /* link pointer for modules */ \
- NULL, /* module pointer for modules */ \
+ NULL, /* usage_count for modules */ \
&proc_scsi_in2000, /* pointer to /proc/scsi directory entry */ \
in2000_proc_info, /* pointer to proc info function */ \
"Always IN2000", /* device name */ \
diff --git a/drivers/scsi/ppa.c b/drivers/scsi/ppa.c
index 581f280e8..e9bdcee9b 100644
--- a/drivers/scsi/ppa.c
+++ b/drivers/scsi/ppa.c
@@ -115,6 +115,8 @@
* [Curtin-1-08-STABLE]
*/
+#include <linux/config.h>
+
/* The following #define is to avoid a clash with hosts.c */
#define PPA_CODE 1
#include "ppa.h"
diff --git a/drivers/scsi/qlogicpti.c b/drivers/scsi/qlogicpti.c
index 5e14ea594..b8620dfdc 100644
--- a/drivers/scsi/qlogicpti.c
+++ b/drivers/scsi/qlogicpti.c
@@ -653,7 +653,7 @@ __initfunc(int qlogicpti_detect(Scsi_Host_Template *tpnt))
}
qpti_host->base = (unsigned char *)qregs;
- qpti_host->io_port = (unsigned int) qregs;
+ qpti_host->io_port = (unsigned int) ((unsigned long)qregs);
qpti_host->n_io_port = (unsigned char)
qpti->qdev->reg_addrs[0].reg_size;
@@ -805,7 +805,7 @@ static inline void cmd_frob(struct Command_Entry *cmd, Scsi_Cmnd *Cmnd,
memset(cmd, 0, sizeof(struct Command_Entry));
cmd->hdr.entry_cnt = 1;
cmd->hdr.entry_type = ENTRY_COMMAND;
- cmd->handle = (u_int) Cmnd; /* magic mushroom */
+ cmd->handle = (u_int) ((unsigned long)Cmnd); /* magic mushroom */
cmd->target_id = Cmnd->target;
cmd->target_lun = Cmnd->lun;
cmd->cdb_length = Cmnd->cmd_len;
@@ -890,7 +890,7 @@ static inline u_int load_cmd(Scsi_Cmnd *Cmnd, struct Command_Entry *cmd,
Cmnd->request_bufflen,
qpti->qdev->my_bus));
- cmd->dataseg[0].d_base = (u_int) Cmnd->SCp.ptr;
+ cmd->dataseg[0].d_base = (u_int) ((unsigned long)Cmnd->SCp.ptr);
cmd->dataseg[0].d_count = Cmnd->request_bufflen;
cmd->segment_cnt = 1;
}
@@ -1062,7 +1062,7 @@ repeat:
while(out_ptr != in_ptr) {
sts = (struct Status_Entry *) &qpti->res_cpu[out_ptr];
out_ptr = NEXT_RES_PTR(out_ptr);
- Cmnd = (Scsi_Cmnd *) sts->handle; /* but_to_virt?!?! */
+ Cmnd = (Scsi_Cmnd *) ((unsigned long)sts->handle);
if(sts->completion_status == CS_RESET_OCCURRED ||
sts->completion_status == CS_ABORTED ||
(sts->status_flags & STF_BUS_RESET))
@@ -1111,8 +1111,8 @@ int qlogicpti_abort(Scsi_Cmnd *Cmnd)
qlogicpti_disable_irqs(qpti->qregs);
param[0] = MBOX_ABORT;
param[1] = (((u_short) Cmnd->target) << 8) | Cmnd->lun;
- param[2] = ((unsigned int)Cmnd) >> 16;
- param[3] = ((unsigned int)Cmnd) & 0xffff;
+ param[2] = ((unsigned int)((unsigned long)Cmnd)) >> 16;
+ param[3] = ((unsigned int)((unsigned long)Cmnd)) & 0xffff;
if(qlogicpti_mbox_command(qpti, param, 0) ||
(param[0] != MBOX_COMMAND_COMPLETE)) {
printk(KERN_EMERG "qlogicpti : scsi abort failure: %x\n", param[0]);
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index c9b8064b3..1f0080bd9 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -12,6 +12,7 @@
* Rik Faith <faith@cs.unc.edu>
* Tommy Thorn <tthorn>
* Thomas Wuensche <tw@fgb1.fgb.mw.tu-muenchen.de>
+ * Andrea Arcangeli <arcangeli@mbox.queen.it>
*
* Modified by Eric Youngdale eric@aib.com to
* add scatter-gather, multiple outstanding request, and other
@@ -65,7 +66,7 @@
#undef USE_STATIC_SCSI_MEMORY
/*
-static const char RCSid[] = "$Header: /vger/u4/cvs/linux/drivers/scsi/scsi.c,v 1.38 1997/01/19 23:07:18 davem Exp $";
+static const char RCSid[] = "$Header: /src/cvs/linux/drivers/scsi/scsi.c,v 1.1.1.1 1997/06/01 03:17:37 ralf Exp $";
*/
@@ -3512,6 +3513,7 @@ int init_module(void) {
void cleanup_module( void)
{
+ timer_active &= ~(1 << SCSI_TIMER);
#if CONFIG_PROC_FS
proc_scsi_unregister(0, PROC_SCSI_SCSI);
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index eb9e7fec3..d83b94e26 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -488,7 +488,7 @@ static long sg_write(struct inode *inode,struct file *filp,const char *buf,unsig
static unsigned int sg_poll(struct file *file, poll_table * wait)
{
- int dev = MINOR(file->f_inode->i_rdev);
+ int dev = MINOR(file->f_dentry->d_inode->i_rdev);
struct scsi_generic *device = &scsi_generics[dev];
unsigned int mask = 0;
diff --git a/drivers/scsi/sr_ioctl.c b/drivers/scsi/sr_ioctl.c
index a10e9545d..bc870dfea 100644
--- a/drivers/scsi/sr_ioctl.c
+++ b/drivers/scsi/sr_ioctl.c
@@ -1,3 +1,4 @@
+#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
diff --git a/drivers/scsi/tmscsim.c b/drivers/scsi/tmscsim.c
index f5ceb1371..3008f1b21 100644
--- a/drivers/scsi/tmscsim.c
+++ b/drivers/scsi/tmscsim.c
@@ -50,13 +50,8 @@
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/config.h>
-
-#include <linux/version.h>
-#if LINUX_VERSION_CODE < 66354 /* 1.3.50 */
-#include "../block/blk.h"
-#else
+#include <linux/init.h>
#include <linux/blk.h>
-#endif
#include "scsi.h"
#include "hosts.h"
@@ -69,13 +64,10 @@
#define PCI_DEVICE_ID_AMD53C974 PCI_DEVICE_ID_AMD_SCSI
-
-#ifndef VERSION_ELF_1_2_13
struct proc_dir_entry proc_scsi_tmscsim ={
PROC_SCSI_DC390T, 7 ,"tmscsim",
S_IFDIR | S_IRUGO | S_IXUGO, 2
};
-#endif
static USHORT DC390_StartSCSI( PACB pACB, PDCB pDCB, PSRB pSRB );
static void DC390_DataOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
@@ -675,11 +667,7 @@ DoNextCmd( PACB pACB, PDCB pDCB )
* Description:
* Return the disk geometry for the given SCSI device.
***********************************************************************/
-#ifdef VERSION_ELF_1_2_13
-int DC390_bios_param(Disk *disk, int devno, int geom[])
-#else
int DC390_bios_param(Disk *disk, kdev_t devno, int geom[])
-#endif
{
int heads, sectors, cylinders;
PACB pACB;
@@ -1046,14 +1034,10 @@ void DC390_initDCB( PACB pACB, PDCB pDCB, PSCSICMD cmd )
***********************************************************************/
void DC390_initSRB( PSRB psrb )
{
-#ifndef VERSION_ELF_1_2_13
#ifdef DC390_DEBUG0
printk("DC390 init: %08lx %08lx,",(ULONG)psrb,(ULONG)virt_to_bus(psrb));
#endif
psrb->PhysSRB = virt_to_bus( psrb );
-#else
- psrb->PhysSRB = (ULONG) psrb;
-#endif
}
@@ -1084,7 +1068,7 @@ void DC390_linkSRB( PACB pACB )
* Inputs : psh - pointer to this host adapter's structure
*
***********************************************************************/
-void DC390_initACB( PSH psh, ULONG io_port, UCHAR Irq, USHORT index )
+__initfunc(void DC390_initACB( PSH psh, ULONG io_port, UCHAR Irq, USHORT index ))
{
PACB pACB;
USHORT i;
@@ -1098,7 +1082,6 @@ void DC390_initACB( PSH psh, ULONG io_port, UCHAR Irq, USHORT index )
pACB = (PACB) psh->hostdata;
-#ifndef VERSION_ELF_1_2_13
psh->max_id = 8;
#ifdef CONFIG_SCSI_MULTI_LUN
if( eepromBuf[index][EE_MODE2] & LUN_CHECK )
@@ -1106,7 +1089,6 @@ void DC390_initACB( PSH psh, ULONG io_port, UCHAR Irq, USHORT index )
else
#endif
psh->max_lun = 1;
-#endif
pACB->max_id = 7;
if( pACB->max_id == eepromBuf[index][EE_ADAPT_SCSI_ID] )
@@ -1155,7 +1137,7 @@ void DC390_initACB( PSH psh, ULONG io_port, UCHAR Irq, USHORT index )
* Inputs : psh - pointer to this host adapter's structure
*
***********************************************************************/
-int DC390_initAdapter( PSH psh, ULONG io_port, UCHAR Irq, USHORT index )
+__initfunc(int DC390_initAdapter( PSH psh, ULONG io_port, UCHAR Irq, USHORT index))
{
USHORT ioport;
UCHAR bval;
@@ -1179,11 +1161,7 @@ int DC390_initAdapter( PSH psh, ULONG io_port, UCHAR Irq, USHORT index )
if( !used_irq )
{
-#ifdef VERSION_ELF_1_2_13
- if( request_irq(Irq, DC390_Interrupt, SA_INTERRUPT, "tmscsim"))
-#else
if( request_irq(Irq, DC390_Interrupt, SA_INTERRUPT, "tmscsim", NULL))
-#endif
{
printk("DC390: register IRQ error!\n");
return( -1 );
@@ -1533,8 +1511,8 @@ DC390_ToMech( USHORT Mechnum, USHORT BusDevFunNum )
* field of the pACB structure MUST have been set.
***********************************************************************/
-static int
-DC390_init (PSHT psht, ULONG io_port, UCHAR Irq, USHORT index, USHORT MechNum)
+__initfunc(static int
+DC390_init (PSHT psht, ULONG io_port, UCHAR Irq, USHORT index, USHORT MechNum))
{
PSH psh;
PACB pACB;
@@ -1614,8 +1592,8 @@ DC390_init (PSHT psht, ULONG io_port, UCHAR Irq, USHORT index, USHORT MechNum)
*
***********************************************************************/
-int
-DC390_detect(Scsi_Host_Template *psht)
+__initfunc(int
+DC390_detect(Scsi_Host_Template *psht))
{
#ifdef FOR_PCI_OK
UCHAR pci_bus, pci_device_fn;
@@ -1626,19 +1604,13 @@ DC390_detect(Scsi_Host_Template *psht)
UCHAR irq;
UCHAR istatus;
-#ifndef VERSION_ELF_1_2_13
UINT io_port;
-#else
- ULONG io_port;
-#endif
USHORT adaptCnt = 0; /* Number of boards detected */
USHORT pci_index = 0; /* Device index to PCI BIOS calls */
USHORT MechNum, BusDevFunNum;
ULONG wlval;
-#ifndef VERSION_ELF_1_2_13
psht->proc_dir = &proc_scsi_tmscsim;
-#endif
InitialTime = 1;
pSHT_start = psht;
@@ -1726,8 +1698,6 @@ DC390_detect(Scsi_Host_Template *psht)
}
-#ifndef VERSION_ELF_1_2_13
-
/********************************************************************
* Function: tmscsim_set_info()
*
@@ -1848,7 +1818,6 @@ int tmscsim_proc_info(char *buffer, char **start,
else
return length;
}
-#endif /* VERSION_ELF_1_2_13 */
#ifdef MODULE
@@ -1909,11 +1878,7 @@ int DC390_release(struct Scsi_Host *host)
#ifdef DC390_DEBUG0
printk("DC390: Free IRQ %i.",host->irq);
#endif
-#ifndef VERSION_ELF_1_2_13
free_irq(host->irq,NULL);
-#else
- free_irq(host->irq);
-#endif
}
}
diff --git a/drivers/scsi/wd33c93.c b/drivers/scsi/wd33c93.c
index 14cb0f37a..6d60d12ae 100644
--- a/drivers/scsi/wd33c93.c
+++ b/drivers/scsi/wd33c93.c
@@ -84,8 +84,9 @@
#include "scsi.h"
#include "hosts.h"
-#define WD33C93_VERSION "1.24"
-#define WD33C93_DATE "29/Jan/1997"
+#define WD33C93_VERSION "1.25"
+#define WD33C93_DATE "09/Jul/1997"
+/* NOTE: 1.25 for m68k is related to in2000-1.31 for x86 */
/*
* Note - the following defines have been moved to 'wd33c93.h':
@@ -103,11 +104,15 @@
-/* setup_strings is an array of strings that define some of the operating
- * parameters and settings for this driver. It is used unless an amiboot
- * or insmod command line has been specified, in which case those settings
- * are combined with the ones here. The driver recognizes the following
- * keywords (lower case required) and arguments:
+/*
+ * 'setup_strings' is a single string used to pass operating parameters and
+ * settings from the kernel/module command-line to the driver. 'setup_args[]'
+ * is an array of strings that define the compile-time default values for
+ * these settings. If Linux boots with an amiboot or insmod command-line,
+ * those settings are combined with 'setup_args[]'. Note that amiboot
+ * command-lines are prefixed with "wd33c93=" while insmod uses a
+ * "setup_strings=" prefix. The driver recognizes the following keywords
+ * (lower case required) and arguments:
*
* - nosync:bitmask -bitmask is a byte where the 1st 7 bits correspond with
* the 7 possible SCSI devices. Set a bit to negotiate for
@@ -135,13 +140,11 @@
* _must_ be a colon between a keyword and its numeric argument, with no
* spaces.
* - Keywords are separated by commas, no spaces, in the standard kernel
- * command-line manner, except in the case of 'setup_strings[]' (see
- * below), which is simply a C array of pointers to char. Each element
- * in the array is a string comprising one keyword & argument.
+ * command-line manner.
* - A keyword in the 'nth' comma-separated command-line member will overwrite
- * the 'nth' element of setup_strings[]. A blank command-line member (in
+ * the 'nth' element of setup_args[]. A blank command-line member (in
* other words, a comma with no preceding keyword) will _not_ overwrite
- * the corresponding setup_strings[] element.
+ * the corresponding setup_args[] element.
* - If a keyword is used more than once, the first one applies to the first
* SCSI host found, the second to the second card, etc, unless the 'next'
* keyword is used to change the order.
@@ -154,8 +157,16 @@
* - wd33c93=debug:0x1c
*/
-static char *setup_strings[] =
- {"","","","","","","","","","","",""};
+/* Normally, no defaults are specified */
+static char *setup_args[] =
+ {"","","","","","","","",""};
+
+/* filled in by 'insmod' */
+static char *setup_strings = 0;
+
+#ifdef MODULE_PARM
+MODULE_PARM(setup_strings, "s");
+#endif
static inline uchar read_wd33c93(wd33c93_regs *regp,uchar reg_num)
@@ -319,8 +330,24 @@ int wd33c93_queuecommand (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
cmd->SCp.this_residual = cmd->request_bufflen;
}
- /* Preset the command status to GOOD, since that's the normal case */
- cmd->SCp.Status = GOOD;
+ /* WD docs state that at the conclusion of a "LEVEL2" command, the
+ * status byte can be retrieved from the LUN register. Apparently,
+ * this is the case only for *uninterrupted* LEVEL2 commands! If
+ * there are any unexpected phases entered, even if they are 100%
+ * legal (different devices may choose to do things differently),
+ * the LEVEL2 command sequence is exited. This often occurs prior
+ * to receiving the status byte, in which case the driver does a
+ * status phase interrupt and gets the status byte on its own.
+ * While such a command can then be "resumed" (ie restarted to
+ * finish up as a LEVEL2 command), the LUN register will NOT be
+ * a valid status byte at the command's conclusion, and we must
+ * use the byte obtained during the earlier interrupt. Here, we
+ * preset SCp.Status to an illegal value (0xff) so that when
+ * this command finally completes, we can tell where the actual
+ * status byte is stored.
+ */
+
+ cmd->SCp.Status = ILLEGAL_STATUS_BYTE;
/* Add the cmd to the end of 'input_Q'. Note that REQUEST SENSE
* commands are added to the head of the queue so that the desired
@@ -641,7 +668,8 @@ use_transfer_pio:
write_wd33c93(regp, WD_CONTROL, (CTRL_IDI | CTRL_EDI | CTRL_DMA));
/* write_wd33c93_count(regp, cmd->SCp.this_residual); */
- if ((hostdata->level2 >= L2_DATA) || (cmd->SCp.phase == 0)) {
+ if ((hostdata->level2 >= L2_DATA) ||
+ (hostdata->level2 == L2_BASIC && cmd->SCp.phase == 0)) {
write_wd33c93(regp, WD_COMMAND_PHASE, 0x45);
write_wd33c93_cmd(regp, WD_CMD_SEL_ATN_XFER);
hostdata->state = S_RUNNING_LEVEL2;
@@ -808,8 +836,10 @@ void wd33c93_intr (struct Scsi_Host *instance)
case CSR_XFER_DONE|PHS_STATUS:
case CSR_UNEXP |PHS_STATUS:
case CSR_SRV_REQ |PHS_STATUS:
- DB(DB_INTR,printk("STATUS"));
+ DB(DB_INTR,printk("STATUS="));
+
cmd->SCp.Status = read_1_byte(regp);
+ DB(DB_INTR,printk("%02x",cmd->SCp.Status));
if (hostdata->level2 >= L2_BASIC) {
/* clear interrupt */
sr = read_wd33c93(regp, WD_SCSI_STATUS);
@@ -817,7 +847,6 @@ void wd33c93_intr (struct Scsi_Host *instance)
write_wd33c93(regp, WD_COMMAND_PHASE, 0x50);
write_wd33c93_cmd(regp, WD_CMD_SEL_ATN_XFER);
} else {
- DB(DB_INTR, printk("=%02x", cmd->SCp.Status));
hostdata->state = S_CONNECTED;
}
break;
@@ -999,21 +1028,22 @@ void wd33c93_intr (struct Scsi_Host *instance)
DB(DB_INTR, printk("SX-DONE-%ld", cmd->pid));
cmd->SCp.Message = COMMAND_COMPLETE;
lun = read_wd33c93(regp, WD_TARGET_LUN);
- if (cmd->SCp.Status == GOOD)
- cmd->SCp.Status = lun;
+ DB(DB_INTR,printk(":%d.%d",cmd->SCp.Status,lun));
hostdata->connected = NULL;
- if (cmd->cmnd[0] != REQUEST_SENSE)
- cmd->result = (cmd->SCp.Status |
- (cmd->SCp.Message << 8));
- else if (cmd->SCp.Status != GOOD)
- cmd->result = ((cmd->result & 0x00ffff) |
- (DID_ERROR << 16));
hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
hostdata->state = S_UNCONNECTED;
+ if (cmd->SCp.Status == ILLEGAL_STATUS_BYTE)
+ cmd->SCp.Status = lun;
+ if (cmd->cmnd[0] == REQUEST_SENSE
+ && cmd->SCp.Status != GOOD)
+ cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
+ else
+ cmd->result = cmd->SCp.Status |
+ (cmd->SCp.Message << 8);
cmd->scsi_done(cmd);
- /* We are no longer connected to a target - check to see if
- * there are commands waiting to be executed.
+ /* We are no longer connected to a target - check to
+ * see if there are commands waiting to be executed.
*/
restore_flags(flags);
wd33c93_execute(instance);
@@ -1081,10 +1111,13 @@ void wd33c93_intr (struct Scsi_Host *instance)
hostdata->connected = NULL;
hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
hostdata->state = S_UNCONNECTED;
- if (cmd->cmnd[0] != REQUEST_SENSE)
- cmd->result = (cmd->SCp.Status | (cmd->SCp.Message << 8));
- else if (cmd->SCp.Status != GOOD)
- cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
+ if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD)
+ cmd->result = (cmd->result & 0x00ffff) |
+ (DID_ERROR << 16);
+ else
+ cmd->result = cmd->SCp.Status |
+ (cmd->SCp.Message << 8);
+
cmd->scsi_done(cmd);
/* We are no longer connected to a target - check to see if
@@ -1110,12 +1143,14 @@ void wd33c93_intr (struct Scsi_Host *instance)
hostdata->connected = NULL;
hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
hostdata->state = S_UNCONNECTED;
- if (cmd->cmnd[0] != REQUEST_SENSE)
- cmd->result = (cmd->SCp.Status |
- (cmd->SCp.Message << 8));
- else if (cmd->SCp.Status != GOOD)
- cmd->result = ((cmd->result & 0x00ffff) |
- (DID_ERROR << 16));
+ DB(DB_INTR,printk(":%d",cmd->SCp.Status))
+ if (cmd->cmnd[0] == REQUEST_SENSE &&
+ cmd->SCp.Status != GOOD)
+ cmd->result = (cmd->result & 0x00ffff) |
+ (DID_ERROR << 16);
+ else
+ cmd->result = cmd->SCp.Status |
+ (cmd->SCp.Message << 8);
cmd->scsi_done(cmd);
restore_flags(flags);
break;
@@ -1455,10 +1490,11 @@ int wd33c93_abort (Scsi_Cmnd *cmd)
}
#define MAX_WD33C93_HOSTS 4
-#define MAX_SETUP_STRINGS (sizeof(setup_strings) / sizeof(char *))
+#define MAX_SETUP_ARGS (sizeof(setup_args) / sizeof(char *))
#define SETUP_BUFFER_SIZE 200
static char setup_buffer[SETUP_BUFFER_SIZE];
-static char setup_used[MAX_SETUP_STRINGS];
+static char setup_used[MAX_SETUP_ARGS];
+static int done_setup = 0;
void wd33c93_setup (char *str, int *ints)
{
@@ -1486,41 +1522,42 @@ void wd33c93_setup (char *str, int *ints)
setup_buffer[SETUP_BUFFER_SIZE - 1] = '\0';
p1 = setup_buffer;
i = 0;
- while (*p1 && (i < MAX_SETUP_STRINGS)) {
+ while (*p1 && (i < MAX_SETUP_ARGS)) {
p2 = strchr(p1, ',');
if (p2) {
*p2 = '\0';
if (p1 != p2)
- setup_strings[i] = p1;
+ setup_args[i] = p1;
p1 = p2 + 1;
i++;
} else {
- setup_strings[i] = p1;
+ setup_args[i] = p1;
break;
}
}
- for (i = 0; i < MAX_SETUP_STRINGS; i++)
+ for (i = 0; i < MAX_SETUP_ARGS; i++)
setup_used[i] = 0;
+ done_setup = 1;
}
-/* check_setup_strings() returns index if key found, 0 if not */
-static int check_setup_strings(char *key, int *flags, int *val, char *buf)
+/* check_setup_args() returns index if key found, 0 if not */
+static int check_setup_args(char *key, int *flags, int *val, char *buf)
{
int x;
char *cp;
- for (x = 0; x < MAX_SETUP_STRINGS; x++) {
+ for (x = 0; x < MAX_SETUP_ARGS; x++) {
if (setup_used[x])
continue;
- if (!strncmp(setup_strings[x], key, strlen(key)))
+ if (!strncmp(setup_args[x], key, strlen(key)))
break;
- if (!strncmp(setup_strings[x], "next", strlen("next")))
+ if (!strncmp(setup_args[x], "next", strlen("next")))
return 0;
}
- if (x == MAX_SETUP_STRINGS)
+ if (x == MAX_SETUP_ARGS)
return 0;
setup_used[x] = 1;
- cp = setup_strings[x] + strlen(key);
+ cp = setup_args[x] + strlen(key);
*val = -1;
if (*cp != ':')
return ++x;
@@ -1535,12 +1572,16 @@ void wd33c93_init (struct Scsi_Host *instance, wd33c93_regs *regs,
dma_setup_t setup, dma_stop_t stop, int clock_freq)
{
static int shown = 0;
- struct WD33C93_hostdata *hostdata = INSTHOSTDATA(instance);
+ struct WD33C93_hostdata *hostdata;
int i;
int flags;
int val;
char buf[32];
+ if (!done_setup && setup_strings)
+ wd33c93_setup(setup_strings,0);
+ hostdata = INSTHOSTDATA(instance);
+
hostdata->regp = regs;
hostdata->clock_freq = clock_freq;
hostdata->dma_setup = setup;
@@ -1580,27 +1621,30 @@ void wd33c93_init (struct Scsi_Host *instance, wd33c93_regs *regs,
#endif
- if (check_setup_strings("nosync", &flags, &val, buf))
+ if (check_setup_args("nosync",&flags,&val,buf))
hostdata->no_sync = val;
- if (check_setup_strings("nodma",&flags,&val,buf))
+ if (check_setup_args("nodma",&flags,&val,buf))
hostdata->no_dma = (val == -1) ? 1 : val;
- if (check_setup_strings("period", &flags, &val, buf))
+ if (check_setup_args("period",&flags,&val,buf))
hostdata->default_sx_per =
sx_table[round_period((unsigned int)val)].period_ns;
- if (check_setup_strings("disconnect", &flags, &val, buf)) {
+ if (check_setup_args("disconnect",&flags,&val,buf)) {
if ((val >= DIS_NEVER) && (val <= DIS_ALWAYS))
hostdata->disconnect = val;
else
hostdata->disconnect = DIS_ADAPTIVE;
}
- if (check_setup_strings("debug", &flags, &val, buf))
+ if (check_setup_args("level2",&flags,&val,buf))
+ hostdata->level2 = val;
+
+ if (check_setup_args("debug",&flags,&val,buf))
hostdata->args = val & DB_MASK;
- if (check_setup_strings("clock", &flags, &val, buf)) {
+ if (check_setup_args("clock",&flags,&val,buf)) {
if ((val > 7) && (val < 11))
val = WD33C93_FS_8_10;
else if ((val > 11) && (val < 16))
@@ -1612,13 +1656,13 @@ void wd33c93_init (struct Scsi_Host *instance, wd33c93_regs *regs,
hostdata->clock_freq = val;
}
- if ((i = check_setup_strings("next", &flags, &val, buf))) {
+ if ((i = check_setup_args("next",&flags,&val,buf))) {
while (i)
setup_used[--i] = 1;
}
#ifdef PROC_INTERFACE
- if (check_setup_strings("proc", &flags, &val, buf))
+ if (check_setup_args("proc",&flags,&val,buf))
hostdata->proc = val;
#endif
@@ -1635,9 +1679,9 @@ void wd33c93_init (struct Scsi_Host *instance, wd33c93_regs *regs,
printk(" debugging=OFF\n");
#endif
#if 0
- printk("wd33c93-%d: setup_strings=", instance->host_no);
- for (i = 0; i < MAX_SETUP_STRINGS; i++)
- printk("%s,", setup_strings[i]);
+ printk("wd33c93-%d: setup_args=", instance->host_no);
+ for (i = 0; i < MAX_SETUP_ARGS; i++)
+ printk("%s,", setup_args[i]);
printk("\n");
printk("wd33c93-%d: debug_flags = %04x\n",
instance->host_no, hostdata->args);
@@ -1715,6 +1759,11 @@ int wd33c93_proc_info(char *buf, char **start, off_t off, int len, int hn, int i
bp += 6;
hd->no_dma = simple_strtoul(bp,NULL,0);
}
+ else if (!strncmp(bp,"level2:",7)) {
+ bp += 7;
+ hd->level2 = simple_strtoul(bp,NULL,0);
+ }
+
return len;
}
@@ -1731,32 +1780,32 @@ int wd33c93_proc_info(char *buf, char **start, off_t off, int len, int hn, int i
sprintf(tbuf,"\nclock_freq=%02x no_sync=%02x no_dma=%d",
hd->clock_freq,hd->no_sync,hd->no_dma);
strcat(bp,tbuf);
- strcat(bp,"\nsync_xfer[] =");
- for (x=0; x<8; x++) {
- sprintf(tbuf," %02x",hd->sync_xfer[x]);
+ strcat(bp,"\nsync_xfer[] = ");
+ for (x=0; x<7; x++) {
+ sprintf(tbuf,"\t%02x",hd->sync_xfer[x]);
strcat(bp,tbuf);
}
- strcat(bp,"\nsync_stat[] =");
- for (x=0; x<8; x++) {
- sprintf(tbuf," %02x",hd->sync_stat[x]);
+ strcat(bp,"\nsync_stat[] = ");
+ for (x=0; x<7; x++) {
+ sprintf(tbuf,"\t%02x",hd->sync_stat[x]);
strcat(bp,tbuf);
}
}
#ifdef PROC_STATISTICS
if (hd->proc & PR_STATISTICS) {
strcat(bp,"\ncommands issued: ");
- for (x=0; x<8; x++) {
- sprintf(tbuf," %ld",hd->cmd_cnt[x]);
+ for (x=0; x<7; x++) {
+ sprintf(tbuf,"\t%ld",hd->cmd_cnt[x]);
strcat(bp,tbuf);
}
strcat(bp,"\ndisconnects allowed:");
- for (x=0; x<8; x++) {
- sprintf(tbuf," %ld",hd->disc_allowed_cnt[x]);
+ for (x=0; x<7; x++) {
+ sprintf(tbuf,"\t%ld",hd->disc_allowed_cnt[x]);
strcat(bp,tbuf);
}
strcat(bp,"\ndisconnects done: ");
- for (x=0; x<8; x++) {
- sprintf(tbuf," %ld",hd->disc_done_cnt[x]);
+ for (x=0; x<7; x++) {
+ sprintf(tbuf,"\t%ld",hd->disc_done_cnt[x]);
strcat(bp,tbuf);
}
sprintf(tbuf,
diff --git a/drivers/scsi/wd33c93.h b/drivers/scsi/wd33c93.h
index 3b8b4cf3a..c0584037b 100644
--- a/drivers/scsi/wd33c93.h
+++ b/drivers/scsi/wd33c93.h
@@ -2,7 +2,7 @@
* wd33c93.h - Linux device driver definitions for the
* Commodore Amiga A2091/590 SCSI controller card
*
- * IMPORTANT: This file is for version 1.24 - 29/Jan/1997
+ * IMPORTANT: This file is for version 1.25 - 09/Jul/1997
*
* Copyright (c) 1996 John Shifflett, GeoLog Consulting
* john@geolog.com
@@ -34,6 +34,8 @@
#define DEBUG_DEFAULTS 0 /* default debugging bitmask */
+#define ILLEGAL_STATUS_BYTE 0xff
+
#ifdef DEBUGGING_ON
#define DB(f,a) if (hostdata->args & (f)) a;
#else
diff --git a/drivers/sound/Config.in b/drivers/sound/Config.in
index 310bf01bf..d14653d45 100644
--- a/drivers/sound/Config.in
+++ b/drivers/sound/Config.in
@@ -1,15 +1,277 @@
-#
-# Sound driver configuration
-#
-#--------
-# There is another confic script which is compatible with rest of
-# the kernel. It can be activated by running 'make mkscript' in this
-# directory. Please note that this is an _experimental_ feature which
-# doesn't work with all cards (PSS, SM Wave, AudioTriX Pro, Maui).
-#--------
-#
-$MAKE -C drivers/sound config || exit 1
+bool 'ProAudioSpectrum 16 support' CONFIG_PAS
+bool '100%% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support' CONFIG_SB
+bool 'Generic OPL2/OPL3 FM synthesizer support' CONFIG_ADLIB
+bool 'Gravis Ultrasound support' CONFIG_GUS
+bool 'MPU-401 support (NOT for SB16)' CONFIG_MPU401
+bool 'PSS (ECHO-ADI2111) support' CONFIG_PSS
+bool '16 bit sampling option of GUS (_NOT_ GUS MAX)' CONFIG_GUS16
+bool 'GUS MAX support' CONFIG_GUSMAX
+bool 'Microsoft Sound System support' CONFIG_MSS
+bool 'Ensoniq SoundScape support' CONFIG_SSCAPE
+bool 'MediaTrix AudioTrix Pro support' CONFIG_TRIX
+bool 'Support for MAD16 and/or Mozart based cards' CONFIG_MAD16
+bool 'Support for Crystal CS4232 based (PnP) cards' CONFIG_CS4232
+bool 'Support for Turtle Beach Wave Front (Maui, Tropez) synthesizers' CONFIG_MAUI
+bool 'FM synthesizer (YM3812/OPL-3) support' CONFIG_YM3812
+
+if [ "$CONFIG_AEDSP16" = "y" ]; then
+hex 'I/O base for Audio Excel DSP 16 220 or 240' AEDSP16_BASE 220
+fi
+
+if [ "$CONFIG_SB" = "y" ]; then
+hex 'I/O base for SB Check from manual of the card' SBC_BASE 220
+fi
+
+if [ "$CONFIG_SB" = "y" ]; then
+int 'Sound Blaster IRQ Check from manual of the card' SBC_IRQ 7
+fi
+
+if [ "$CONFIG_SB" = "y" ]; then
+int 'Sound Blaster DMA 0, 1 or 3' SBC_DMA 1
+fi
+
+if [ "$CONFIG_SB" = "y" ]; then
+int 'Sound Blaster 16 bit DMA (SB16, Jazz16, SMW) 5, 6 or 7 (use 1 for 8 bit cards)' SB_DMA2 5
+fi
+
+if [ "$CONFIG_SB" = "y" ]; then
+hex 'MPU401 I/O base of SB16, Jazz16 and ES1688 Check from manual of the card' SB_MPU_BASE 330
+fi
+
+
+if [ "$CONFIG_SB" = "y" ]; then
+comment 'MPU401 IRQ is only required with Jazz16, SM Wave and ESS1688.'
+fi
+
+
+if [ "$CONFIG_SB" = "y" ]; then
+comment 'Enter -1 to the following question if you have something else such as SB16/32.'
+fi
+
+if [ "$CONFIG_SB" = "y" ]; then
+int 'SB MPU401 IRQ (Jazz16, SM Wave and ES1688) Check from manual of the card' SB_MPU_IRQ -1
+fi
+
+if [ "$CONFIG_PAS" = "y" ]; then
+int 'PAS16 IRQ 3, 4, 5, 7, 9, 10, 11, 12, 14 or 15' PAS_IRQ 10
+fi
+
+if [ "$CONFIG_PAS" = "y" ]; then
+int 'PAS16 DMA 0, 1, 3, 5, 6 or 7' PAS_DMA 3
+fi
+
+if [ "$CONFIG_GUS" = "y" ]; then
+hex 'I/O base for GUS 210, 220, 230, 240, 250 or 260' GUS_BASE 220
+fi
+
+if [ "$CONFIG_GUS" = "y" ]; then
+int 'GUS IRQ 3, 5, 7, 9, 11, 12 or 15' GUS_IRQ 15
+fi
+
+if [ "$CONFIG_GUS" = "y" ]; then
+int 'GUS DMA 1, 3, 5, 6 or 7' GUS_DMA 6
+fi
+
+if [ "$CONFIG_GUS" = "y" ]; then
+int 'Second DMA channel for GUS 1, 3, 5, 6 or 7' GUS_DMA2 -1
+fi
+
+if [ "$CONFIG_GUS16" = "y" ]; then
+hex 'I/O base for the 16 bit daughtercard of GUS 530, 604, E80 or F40' GUS16_BASE 530
+fi
+
+if [ "$CONFIG_GUS16" = "y" ]; then
+int 'GUS 16 bit daughtercard IRQ 3, 4, 5, 7, or 9' GUS16_IRQ 7
+fi
+
+if [ "$CONFIG_GUS16" = "y" ]; then
+int 'GUS DMA 0, 1 or 3' GUS16_DMA 3
+fi
+
+if [ "$CONFIG_MPU401" = "y" ]; then
+hex 'I/O base for MPU401 Check from manual of the card' MPU_BASE 330
+fi
+
+if [ "$CONFIG_MPU401" = "y" ]; then
+int 'MPU401 IRQ Check from manual of the card' MPU_IRQ 9
+fi
+
+
+if [ "$CONFIG_MAUI" = "y" ]; then
+comment 'ERROR! You have to use old sound configuration method with Maui.'
+fi
+
+if [ "$CONFIG_MAUI" = "y" ]; then
+hex 'I/O base for Maui 210, 230, 260, 290, 300, 320, 338 or 330' MAUI_BASE 330
+fi
+
+if [ "$CONFIG_MAUI" = "y" ]; then
+int 'Maui IRQ 5, 9, 12 or 15' MAUI_IRQ 9
+fi
+
+if [ "$CONFIG_UART6850" = "y" ]; then
+hex 'I/O base for UART 6850 MIDI port (Unknown)' U6850_BASE 0
+fi
+
+if [ "$CONFIG_UART6850" = "y" ]; then
+int 'UART6850 IRQ (Unknown)' U6850_IRQ -1
+fi
+
+
+if [ "$CONFIG_PSS" = "y" ]; then
+comment 'ERROR! You have to use old sound configuration method with PSS cards.'
+fi
+
+if [ "$CONFIG_PSS" = "y" ]; then
+hex 'PSS I/O base 220 or 240' PSS_BASE 220
+fi
+
+if [ "$CONFIG_PSS" = "y" ]; then
+hex 'PSS audio I/O base 530, 604, E80 or F40' PSS_MSS_BASE 530
+fi
+
+if [ "$CONFIG_PSS" = "y" ]; then
+int 'PSS audio IRQ 7, 9, 10 or 11' PSS_MSS_IRQ 11
+fi
+
+if [ "$CONFIG_PSS" = "y" ]; then
+int 'PSS audio DMA 0, 1 or 3' PSS_MSS_DMA 3
+fi
+if [ "$CONFIG_PSS" = "y" ]; then
+hex 'PSS MIDI I/O base ' PSS_MPU_BASE 330
+fi
+
+if [ "$CONFIG_PSS" = "y" ]; then
+int 'PSS MIDI IRQ 3, 4, 5, 7 or 9' PSS_MPU_IRQ 9
+fi
+
+if [ "$CONFIG_MSS" = "y" ]; then
+hex 'MSS/WSS I/O base 530, 604, E80 or F40' MSS_BASE 530
+fi
+
+if [ "$CONFIG_MSS" = "y" ]; then
+int 'MSS/WSS IRQ 7, 9, 10 or 11' MSS_IRQ 11
+fi
+
+if [ "$CONFIG_MSS" = "y" ]; then
+int 'MSS/WSS DMA 0, 1 or 3' MSS_DMA 3
+fi
+
+if [ "$CONFIG_MSS" = "y" ]; then
+int 'MSS/WSS second DMA (if possible) 0, 1 or 3' MSS_DMA2 -1
+fi
+
+if [ "$CONFIG_SSCAPE" = "y" ]; then
+hex 'SoundScape MIDI I/O base 320, 330, 340 or 350' SSCAPE_BASE 330
+fi
+
+if [ "$CONFIG_SSCAPE" = "y" ]; then
+int 'SoundScape MIDI IRQ ' SSCAPE_IRQ 9
+fi
+
+if [ "$CONFIG_SSCAPE" = "y" ]; then
+int 'SoundScape initialization DMA 0, 1 or 3' SSCAPE_DMA 3
+fi
+
+if [ "$CONFIG_SSCAPE" = "y" ]; then
+hex 'SoundScape audio I/O base 534, 608, E84 or F44' SSCAPE_MSS_BASE 534
+fi
+
+if [ "$CONFIG_SSCAPE" = "y" ]; then
+int 'SoundScape audio IRQ 7, 9, 10 or 11' SSCAPE_MSS_IRQ 11
+fi
+
+
+if [ "$CONFIG_TRIX" = "y" ]; then
+comment 'ERROR! You have to use old sound configuration method with AudioTrix.'
+fi
+
+if [ "$CONFIG_TRIX" = "y" ]; then
+hex 'AudioTrix audio I/O base 530, 604, E80 or F40' TRIX_BASE 530
+fi
+
+if [ "$CONFIG_TRIX" = "y" ]; then
+int 'AudioTrix audio IRQ 7, 9, 10 or 11' TRIX_IRQ 11
+fi
+
+if [ "$CONFIG_TRIX" = "y" ]; then
+int 'AudioTrix audio DMA 0, 1 or 3' TRIX_DMA 0
+fi
+
+if [ "$CONFIG_TRIX" = "y" ]; then
+int 'AudioTrix second (duplex) DMA 0, 1 or 3' TRIX_DMA2 3
+fi
+
+if [ "$CONFIG_TRIX" = "y" ]; then
+hex 'AudioTrix MIDI I/O base 330, 370, 3B0 or 3F0' TRIX_MPU_BASE 330
+fi
+
+if [ "$CONFIG_TRIX" = "y" ]; then
+int 'AudioTrix MIDI IRQ 3, 4, 5, 7 or 9' TRIX_MPU_IRQ 9
+fi
+
+if [ "$CONFIG_TRIX" = "y" ]; then
+hex 'AudioTrix SB I/O base 220, 210, 230, 240, 250, 260 or 270' TRIX_SB_BASE 220
+fi
+
+if [ "$CONFIG_TRIX" = "y" ]; then
+int 'AudioTrix SB IRQ 3, 4, 5 or 7' TRIX_SB_IRQ 7
+fi
+
+if [ "$CONFIG_TRIX" = "y" ]; then
+int 'AudioTrix SB DMA 1 or 3' TRIX_SB_DMA 1
+fi
+
+if [ "$CONFIG_CS4232" = "y" ]; then
+hex 'CS4232 audio I/O base 530, 604, E80 or F40' CS4232_BASE 530
+fi
+
+if [ "$CONFIG_CS4232" = "y" ]; then
+int 'CS4232 audio IRQ 5, 7, 9, 11, 12 or 15' CS4232_IRQ 11
+fi
+
+if [ "$CONFIG_CS4232" = "y" ]; then
+int 'CS4232 audio DMA 0, 1 or 3' CS4232_DMA 0
+fi
+
+if [ "$CONFIG_CS4232" = "y" ]; then
+int 'CS4232 second (duplex) DMA 0, 1 or 3' CS4232_DMA2 3
+fi
+
+if [ "$CONFIG_CS4232" = "y" ]; then
+hex 'CS4232 MIDI I/O base 330, 370, 3B0 or 3F0' CS4232_MPU_BASE 330
+fi
+
+if [ "$CONFIG_CS4232" = "y" ]; then
+int 'CS4232 MIDI IRQ 5, 7, 9, 11, 12 or 15' CS4232_MPU_IRQ 9
+fi
+
+if [ "$CONFIG_MAD16" = "y" ]; then
+hex 'MAD16 audio I/O base 530, 604, E80 or F40' MAD16_BASE 530
+fi
+
+if [ "$CONFIG_MAD16" = "y" ]; then
+int 'MAD16 audio IRQ 7, 9, 10 or 11' MAD16_IRQ 11
+fi
+
+if [ "$CONFIG_MAD16" = "y" ]; then
+int 'MAD16 audio DMA 0, 1 or 3' MAD16_DMA 3
+fi
+
+if [ "$CONFIG_MAD16" = "y" ]; then
+int 'MAD16 second (duplex) DMA 0, 1 or 3' MAD16_DMA2 0
+fi
+
+if [ "$CONFIG_MAD16" = "y" ]; then
+hex 'MAD16 MIDI I/O base 300, 310, 320 or 330 (0 disables)' MAD16_MPU_BASE 330
+fi
+
+if [ "$CONFIG_MAD16" = "y" ]; then
+int 'MAD16 MIDI IRQ 5, 7, 9 or 10' MAD16_MPU_IRQ 9
+fi
+#
+$MAKE -C drivers/sound kernelconfig || exit 1
bool 'Additional low level drivers' CONFIG_LOWLEVEL_SOUND
if [ "$CONFIG_LOWLEVEL_SOUND" = "y" ]; then
diff --git a/drivers/sound/dev_table.h b/drivers/sound/dev_table.h
index 7013c3c8d..a7e4026e8 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 <linux/config.h>
/*
* Sound card numbers 27 to 999. (1 to 26 are defined in soundcard.h)
diff --git a/drivers/sound/soundcard.c b/drivers/sound/soundcard.c
index 957150f7b..a9af5956b 100644
--- a/drivers/sound/soundcard.c
+++ b/drivers/sound/soundcard.c
@@ -246,7 +246,7 @@ sound_poll (struct file *file, poll_table * wait)
struct inode *inode;
int ret = 0;
- inode = file->f_inode;
+ inode = file->f_dentry->d_inode;
if (sound_select (inode, file, SEL_IN, wait))
ret |= POLLIN;
@@ -326,8 +326,7 @@ sound_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma)
vma->vm_page_prot))
return -EAGAIN;
- vma->vm_inode = inode;
- atomic_inc(&inode->i_count);
+ vma->vm_dentry = dget(file->f_dentry);
dmap->mapping_flags |= DMA_MAP_MAPPED;
diff --git a/fs/Config.in b/fs/Config.in
index a9f922d8a..cfc601630 100644
--- a/fs/Config.in
+++ b/fs/Config.in
@@ -5,26 +5,6 @@ mainmenu_option next_comment
comment 'Filesystems'
bool 'Quota support' CONFIG_QUOTA
-bool 'Preload dcache entries in readdir() [ALPHA, currently dangerous!]' CONFIG_DCACHE_PRELOAD
-bool 'Include support for omirr online mirror' CONFIG_OMIRR
-bool 'Translate filename suffixes' CONFIG_TRANS_NAMES
-if [ "$CONFIG_TRANS_NAMES" = "y" ]; then
- bool ' Restrict translation to specific gid' CONFIG_TRANS_RESTRICT
- if [ "$CONFIG_TRANS_RESTRICT" = "y" ]; then
- int ' Enter gid to compile in' CONFIG_TRANS_GID 4
- fi
- bool ' Translate nodename' CONFIG_TR_NODENAME
- bool ' Translate compiled-in kernelname' CONFIG_TR_KERNNAME
- if [ "$CONFIG_TR_KERNNAME" = "y" ]; then
- string ' Enter kernelname string to compile in' CONFIG_KERNNAME banana
- fi
- bool ' Translate compiled-in kerneltype' CONFIG_TR_KERNTYPE
- if [ "$CONFIG_TR_KERNTYPE" = "y" ]; then
- string ' Enter kerneltype string to compile in' CONFIG_KERNTYPE default
- fi
- bool ' Translate machine type' CONFIG_TR_MACHINE
- bool ' Translate sysname' CONFIG_TR_SYSNAME
-fi
tristate 'Minix fs support' CONFIG_MINIX_FS
tristate 'Second extended fs support' CONFIG_EXT2_FS
diff --git a/fs/affs/Changes b/fs/affs/Changes
index a65dc326a..b19c24ae6 100644
--- a/fs/affs/Changes
+++ b/fs/affs/Changes
@@ -13,11 +13,25 @@ Known bugs:
reads basically work (but all files are of size 0).
Alas, I've got no alpha to debug. :-(
- If an affs mounted filesystem is exported via
- nfs, it cannot be written to. No networking to
- test that, either. :-(
+ nfs, it cannot be written to.
+ As soon as I have my network up and running, I'll
+ try to fix this.
+- The partition checker (drivers/block/genhd.c)
+ doesn't work with devices which have 256 byte
+ blocks (some very old SCSI drives).
Please direct bug reports to: hjw@zvw.de
+Version 3.5
+-----------
+
+- Extension block caches are now allocated on
+ demand instead of when a file is opened, as
+ files can be read and written without opening
+ them (e. g. the loopback device does this).
+
+- Removed an unused function.
+
Version 3.4
-----------
@@ -80,7 +94,7 @@ Version 3.0
interface in Linux 1.3.
- Write support.
- Support for hard and symbolic links.
-- Lots of things I remeber even less ...
+- Lots of things I remember even less ...
Version 2.0
-----------
diff --git a/fs/affs/amigaffs.c b/fs/affs/amigaffs.c
index 4afd66e49..7ddb54a62 100644
--- a/fs/affs/amigaffs.c
+++ b/fs/affs/amigaffs.c
@@ -26,27 +26,6 @@ static char ErrorBuffer[256];
*
*/
-/* Find the next used hash entry at or after *HASH_POS in a directory's hash
- table. *HASH_POS is assigned that entry's number. DIR_DATA points to
- the directory header block in memory. If there are no more entries,
- 0 is returned. Otherwise, the key number in the next used hash slot
- is returned. */
-
-static int
-affs_find_next_hash_entry(int hsize, void *dir_data, int *hash_pos)
-{
- struct dir_front *dir_front = dir_data;
- int i;
-
- for (i = *hash_pos; i < hsize; i++)
- if (dir_front->hashtable[i] != 0)
- break;
- if (i >= hsize)
- return 0;
- *hash_pos = i;
- return htonl(dir_front->hashtable[i]);
-}
-
/* Set *NAME to point to the file name in a file header block in memory
pointed to by FH_DATA. The length of the name is returned. */
diff --git a/fs/affs/file.c b/fs/affs/file.c
index 46b10bcb1..7777e4763 100644
--- a/fs/affs/file.c
+++ b/fs/affs/file.c
@@ -43,8 +43,8 @@ static long affs_file_write(struct inode *inode, struct file *filp, const char *
unsigned long count);
static long affs_file_write_ofs(struct inode *inode, struct file *filp, const char *buf,
unsigned long count);
-static int affs_open_file(struct inode *inode, struct file *filp);
static int affs_release_file(struct inode *inode, struct file *filp);
+static int alloc_ext_cache(struct inode *inode);
static struct file_operations affs_file_operations = {
NULL, /* lseek - default */
@@ -54,7 +54,7 @@ static struct file_operations affs_file_operations = {
NULL, /* poll - default */
NULL, /* ioctl - default */
generic_file_mmap, /* mmap */
- affs_open_file, /* special open is needed */
+ NULL, /* no special open */
affs_release_file, /* release */
file_fsync /* brute force, but works */
};
@@ -87,7 +87,7 @@ static struct file_operations affs_file_operations_ofs = {
NULL, /* poll - default */
NULL, /* ioctl - default */
NULL, /* mmap */
- affs_open_file, /* special open is needed */
+ NULL, /* no special open */
affs_release_file, /* release */
file_fsync /* brute force, but works */
};
@@ -248,9 +248,9 @@ affs_bmap(struct inode *inode, int block)
return 0;
}
if (!inode->u.affs_i.i_ec) {
- affs_error(inode->i_sb,"bmap","No extension cache for open file (inode=%lu)",
- inode->i_ino);
- return 0;
+ if (alloc_ext_cache(inode)) {
+ return 0;
+ }
}
/* Try to find the requested key in the cache.
@@ -582,6 +582,12 @@ affs_file_write(struct inode *inode, struct file *filp, const char *buf, unsigne
iput(inode);
return -EINVAL;
}
+ if (!inode->u.affs_i.i_ec) {
+ if (alloc_ext_cache(inode)) {
+ iput(inode);
+ return -ENOMEM;
+ }
+ }
if (filp->f_flags & O_APPEND) {
pos = inode->i_size;
} else
@@ -861,40 +867,6 @@ affs_truncate(struct inode *inode)
}
static int
-affs_open_file(struct inode *inode, struct file *filp)
-{
- int error;
- u32 key;
- int i;
-
- pr_debug("AFFS: open_file(ino=%lu)\n",inode->i_ino);
-
- error = 0;
- inode->u.affs_i.i_cache_users++;
- lock_super(inode->i_sb);
- if (!inode->u.affs_i.i_ec) {
- inode->u.affs_i.i_ec = (struct ext_cache *)get_free_page(GFP_KERNEL);
- if (!inode->u.affs_i.i_ec) {
- affs_error(inode->i_sb,"open_file","Cache allocation failed");
- error = ENOMEM;
- } else {
- /* We only have to initialize non-zero values.
- * get_free_page() zeroed the page already.
- */
- key = inode->u.affs_i.i_original ? inode->u.affs_i.i_original : inode->i_ino;
- inode->u.affs_i.i_ec->ec[0] = key;
- for (i = 0; i < 4; i++) {
- inode->u.affs_i.i_ec->kc[i].kc_this_key = key;
- inode->u.affs_i.i_ec->kc[i].kc_last = -1;
- }
- }
- }
- unlock_super(inode->i_sb);
-
- return error;
-}
-
-static int
affs_release_file(struct inode *inode, struct file *filp)
{
struct affs_zone *zone;
@@ -916,13 +888,35 @@ affs_release_file(struct inode *inode, struct file *filp)
unlock_super(inode->i_sb);
}
}
+ return 0;
+}
+
+static int
+alloc_ext_cache(struct inode *inode)
+{
+ s32 key;
+ int i;
+
lock_super(inode->i_sb);
- if (--inode->u.affs_i.i_cache_users == 0) {
+ if (!inode->u.affs_i.i_ec) {
+ inode->u.affs_i.i_ec = (struct ext_cache *)get_free_page(GFP_KERNEL);
if (inode->u.affs_i.i_ec) {
- free_page((unsigned long)inode->u.affs_i.i_ec);
- inode->u.affs_i.i_ec = NULL;
+ /* We only have to initialize non-zero values.
+ * get_free_page() zeroed the page already.
+ */
+ key = inode->u.affs_i.i_original ? inode->u.affs_i.i_original : inode->i_ino;
+ inode->u.affs_i.i_ec->ec[0] = key;
+ for (i = 0; i < 4; i++) {
+ inode->u.affs_i.i_ec->kc[i].kc_this_key = key;
+ inode->u.affs_i.i_ec->kc[i].kc_last = -1;
+ }
}
}
unlock_super(inode->i_sb);
+
+ if (!inode->u.affs_i.i_ec) {
+ affs_error(inode->i_sb,"alloc_ext_cache","Cache allocation failed");
+ return -ENOMEM;
+ }
return 0;
}
diff --git a/fs/affs/inode.c b/fs/affs/inode.c
index 2805f1ccf..a1e380cce 100644
--- a/fs/affs/inode.c
+++ b/fs/affs/inode.c
@@ -691,7 +691,6 @@ affs_read_inode(struct inode *inode)
inode->u.affs_i.i_pa_next = 0;
inode->u.affs_i.i_pa_last = 0;
inode->u.affs_i.i_ec = NULL;
- inode->u.affs_i.i_cache_users = 0;
inode->u.affs_i.i_lastblock = -1;
inode->i_nlink = 1;
inode->i_mode = 0;
@@ -870,6 +869,12 @@ static void
affs_put_inode(struct inode *inode)
{
pr_debug("AFFS: put_inode(ino=%lu, nlink=%u)\n",inode->i_ino,inode->i_nlink);
+ lock_super(inode->i_sb);
+ if (inode->u.affs_i.i_ec) {
+ free_page((unsigned long)inode->u.affs_i.i_ec);
+ inode->u.affs_i.i_ec = NULL;
+ }
+ unlock_super(inode->i_sb);
if (inode->i_nlink) {
return;
}
@@ -899,7 +904,7 @@ affs_new_inode(const struct inode *dir)
return NULL;
}
- atomic_set(&inode->i_count, 1);
+ inode->i_count = 1;
inode->i_nlink = 1;
inode->i_dev = sb->s_dev;
inode->i_uid = current->fsuid;
@@ -921,7 +926,6 @@ affs_new_inode(const struct inode *dir)
inode->u.affs_i.i_pa_next = 0;
inode->u.affs_i.i_pa_last = 0;
inode->u.affs_i.i_ec = NULL;
- inode->u.affs_i.i_cache_users = 0;
inode->u.affs_i.i_lastblock = -1;
insert_inode_hash(inode);
diff --git a/fs/affs/namei.c b/fs/affs/namei.c
index 5ea649425..ce04df073 100644
--- a/fs/affs/namei.c
+++ b/fs/affs/namei.c
@@ -343,7 +343,7 @@ affs_rmdir(struct inode *dir, const char *name, int len)
retval = -ENOTEMPTY;
goto rmdir_done;
}
- if (atomic_read(&inode->i_count) > 1) {
+ if (inode->i_count > 1) {
retval = -EBUSY;
goto rmdir_done;
}
@@ -512,7 +512,7 @@ subdir(struct inode *new_inode, struct inode *old_inode)
int ino;
int result;
- atomic_inc(&new_inode->i_count);
+ new_inode->i_count++;
result = 0;
for (;;) {
if (new_inode == old_inode) {
@@ -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;
@@ -592,7 +592,7 @@ start_up:
if (!empty_dir(new_bh,AFFS_I2HSIZE(new_inode)))
goto end_rename;
retval = -EBUSY;
- if (atomic_read(&new_inode->i_count) > 1)
+ if (new_inode->i_count > 1)
goto end_rename;
}
if (S_ISDIR(old_inode->i_mode)) {
diff --git a/fs/attr.c b/fs/attr.c
index be824dd4a..7a6b9f814 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -74,7 +74,7 @@ void inode_setattr(struct inode * inode, struct iattr * attr)
if (!fsuser() && !in_group_p(inode->i_gid))
inode->i_mode &= ~S_ISGID;
}
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
}
}
diff --git a/fs/autofs/autofs_i.h b/fs/autofs/autofs_i.h
index cc38577aa..f2bb6c339 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 0f529c900..61900c481 100644
--- a/fs/autofs/dir.c
+++ b/fs/autofs/dir.c
@@ -34,25 +34,13 @@ 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 dentry * dentry)
{
- *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 */
+ d_add(dentry, NULL);
+ return 0;
}
static struct file_operations autofs_dir_operations = {
diff --git a/fs/autofs/dirhash.c b/fs/autofs/dirhash.c
index 90c18695a..60a3c6933 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/init.c b/fs/autofs/init.c
index 4dbb76c85..ed4401ce5 100644
--- a/fs/autofs/init.c
+++ b/fs/autofs/init.c
@@ -21,7 +21,7 @@
static struct file_system_type autofs_fs_type = {
"autofs",
- FS_NO_DCACHE,
+ 0 /* FS_NO_DCACHE doesn't work correctly */,
autofs_read_super,
NULL
};
diff --git a/fs/autofs/inode.c b/fs/autofs/inode.c
index 20ca0907a..870ff120d 100644
--- a/fs/autofs/inode.c
+++ b/fs/autofs/inode.c
@@ -19,11 +19,17 @@
#define __NO_VERSION__
#include <linux/module.h>
+/*
+ * Dummy functions - do we ever actually want to do
+ * something here?
+ */
static void autofs_put_inode(struct inode *inode)
{
- if (inode->i_nlink)
- return;
- inode->i_size = 0;
+}
+
+static void autofs_delete_inode(struct inode *inode)
+{
+ inode->i_size = 0;
}
static void autofs_put_super(struct super_block *sb)
@@ -35,16 +41,16 @@ static void autofs_put_super(struct super_block *sb)
if ( !sbi->catatonic )
autofs_catatonic_mode(sbi); /* Free wait queues, close pipe */
- lock_super(sb);
+ lock_super(sb);
autofs_hash_nuke(&sbi->dirhash);
for ( n = 0 ; n < AUTOFS_MAX_SYMLINKS ; n++ ) {
if ( test_bit(n, sbi->symlink_bitmap) )
kfree(sbi->symlink[n].data);
}
- sb->s_dev = 0;
+ sb->s_dev = 0;
kfree(sb->u.generic_sbp);
- unlock_super(sb);
+ unlock_super(sb);
DPRINTK(("autofs: shutting down\n"));
@@ -53,95 +59,96 @@ static void autofs_put_super(struct super_block *sb)
#endif
}
-static void autofs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz);
+static int autofs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz);
static void autofs_read_inode(struct inode *inode);
static void autofs_write_inode(struct inode *inode);
static struct super_operations autofs_sops = {
- autofs_read_inode,
- NULL,
- autofs_write_inode,
- autofs_put_inode,
- autofs_put_super,
- NULL,
- autofs_statfs,
- NULL
+ autofs_read_inode,
+ autofs_write_inode,
+ autofs_put_inode,
+ autofs_delete_inode,
+ NULL, /* notify_change */
+ autofs_put_super,
+ NULL, /* write_super */
+ autofs_statfs,
+ NULL
};
static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid, pid_t *pgrp, int *minproto, int *maxproto)
{
- char *this_char, *value;
-
- *uid = current->uid;
- *gid = current->gid;
+ char *this_char, *value;
+
+ *uid = current->uid;
+ *gid = current->gid;
*pgrp = current->pgrp;
*minproto = *maxproto = AUTOFS_PROTO_VERSION;
- *pipefd = -1;
+ *pipefd = -1;
- if ( !options ) return 1;
- for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) {
- if ((value = strchr(this_char,'=')) != NULL)
- *value++ = 0;
- if (!strcmp(this_char,"fd")) {
- if (!value || !*value)
- return 1;
- *pipefd = simple_strtoul(value,&value,0);
- if (*value)
- return 1;
- }
- else if (!strcmp(this_char,"uid")) {
- if (!value || !*value)
- return 1;
- *uid = simple_strtoul(value,&value,0);
- if (*value)
- return 1;
- }
- else if (!strcmp(this_char,"gid")) {
- if (!value || !*value)
- return 1;
- *gid = simple_strtoul(value,&value,0);
- if (*value)
- return 1;
- }
- else if (!strcmp(this_char,"pgrp")) {
- if (!value || !*value)
- return 1;
- *pgrp = simple_strtoul(value,&value,0);
- if (*value)
- return 1;
- }
- else if (!strcmp(this_char,"minproto")) {
- if (!value || !*value)
- return 1;
- *minproto = simple_strtoul(value,&value,0);
- if (*value)
- return 1;
- }
- else if (!strcmp(this_char,"maxproto")) {
- if (!value || !*value)
- return 1;
- *maxproto = simple_strtoul(value,&value,0);
- if (*value)
- return 1;
- }
- else break;
- }
- return (*pipefd < 0);
+ if ( !options ) return 1;
+ for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) {
+ if ((value = strchr(this_char,'=')) != NULL)
+ *value++ = 0;
+ if (!strcmp(this_char,"fd")) {
+ if (!value || !*value)
+ return 1;
+ *pipefd = simple_strtoul(value,&value,0);
+ if (*value)
+ return 1;
+ }
+ else if (!strcmp(this_char,"uid")) {
+ if (!value || !*value)
+ return 1;
+ *uid = simple_strtoul(value,&value,0);
+ if (*value)
+ return 1;
+ }
+ else if (!strcmp(this_char,"gid")) {
+ if (!value || !*value)
+ return 1;
+ *gid = simple_strtoul(value,&value,0);
+ if (*value)
+ return 1;
+ }
+ else if (!strcmp(this_char,"pgrp")) {
+ if (!value || !*value)
+ return 1;
+ *pgrp = simple_strtoul(value,&value,0);
+ if (*value)
+ return 1;
+ }
+ else if (!strcmp(this_char,"minproto")) {
+ if (!value || !*value)
+ return 1;
+ *minproto = simple_strtoul(value,&value,0);
+ if (*value)
+ return 1;
+ }
+ else if (!strcmp(this_char,"maxproto")) {
+ if (!value || !*value)
+ return 1;
+ *maxproto = simple_strtoul(value,&value,0);
+ if (*value)
+ return 1;
+ }
+ else break;
+ }
+ return (*pipefd < 0);
}
struct super_block *autofs_read_super(struct super_block *s, void *data,
- int silent)
+ int silent)
{
- int pipefd;
+ int pipefd;
struct autofs_sb_info *sbi;
int minproto, maxproto;
MOD_INC_USE_COUNT;
- lock_super(s);
- sbi = (struct autofs_sb_info *) kmalloc(sizeof(struct autofs_sb_info), GFP_KERNEL);
+ lock_super(s);
+ sbi = (struct autofs_sb_info *) kmalloc(sizeof(struct autofs_sb_info), GFP_KERNEL);
if ( !sbi ) {
s->s_dev = 0;
MOD_DEC_USE_COUNT;
@@ -158,30 +165,31 @@ struct super_block *autofs_read_super(struct super_block *s, void *data,
sbi->queues = NULL;
memset(sbi->symlink_bitmap, 0, sizeof(u32)*AUTOFS_SYMLINK_BITMAP_LEN);
sbi->next_dir_ino = AUTOFS_FIRST_DIR_INO;
- s->s_blocksize = 1024;
- s->s_blocksize_bits = 10;
- 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_dev = 0;
+ s->s_blocksize = 1024;
+ s->s_blocksize_bits = 10;
+ s->s_magic = AUTOFS_SUPER_MAGIC;
+ s->s_op = &autofs_sops;
+ unlock_super(s);
+ 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");
+ printk("autofs: get root inode failed\n");
MOD_DEC_USE_COUNT;
- return NULL;
- }
+ 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);
- s->s_dev = 0;
+ if ( parse_options(data,&pipefd,&s->s_root->d_inode->i_uid,&s->s_root->d_inode->i_gid,&sbi->oz_pgrp,&minproto,&maxproto) ) {
+ dput(s->s_root);
+ s->s_dev = 0;
kfree(sbi);
- printk("autofs: called with bogus options\n");
+ printk("autofs: called with bogus options\n");
MOD_DEC_USE_COUNT;
- return NULL;
- }
+ return NULL;
+ }
if ( minproto > AUTOFS_PROTO_VERSION || maxproto < AUTOFS_PROTO_VERSION ) {
- iput(s->s_mounted);
+ dput(s->s_root);
s->s_dev = 0;
kfree(sbi);
printk("autofs: kernel does not match daemon version\n");
@@ -193,60 +201,60 @@ struct super_block *autofs_read_super(struct super_block *s, void *data,
sbi->pipe = fget(pipefd);
if ( !sbi->pipe || !sbi->pipe->f_op || !sbi->pipe->f_op->write ) {
if ( sbi->pipe ) {
- fput(sbi->pipe, sbi->pipe->f_inode);
+ fput(sbi->pipe);
printk("autofs: pipe file descriptor does not contain proper ops\n");
} else {
printk("autofs: could not open pipe file descriptor\n");
}
- iput(s->s_mounted);
+ dput(s->s_root);
s->s_dev = 0;
kfree(sbi);
MOD_DEC_USE_COUNT;
return NULL;
}
- return s;
+ return s;
}
-static void autofs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
+static int autofs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
{
- struct statfs tmp;
+ struct statfs tmp;
- tmp.f_type = AUTOFS_SUPER_MAGIC;
- tmp.f_bsize = 1024;
- tmp.f_blocks = 0;
- tmp.f_bfree = 0;
- tmp.f_bavail = 0;
- tmp.f_files = 0;
- tmp.f_ffree = 0;
- tmp.f_namelen = NAME_MAX;
- copy_to_user(buf, &tmp, bufsiz);
+ tmp.f_type = AUTOFS_SUPER_MAGIC;
+ tmp.f_bsize = 1024;
+ tmp.f_blocks = 0;
+ tmp.f_bfree = 0;
+ tmp.f_bavail = 0;
+ tmp.f_files = 0;
+ tmp.f_ffree = 0;
+ tmp.f_namelen = NAME_MAX;
+ return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
}
static void autofs_read_inode(struct inode *inode)
{
- ino_t ino = inode->i_ino;
+ ino_t ino = inode->i_ino;
unsigned int n;
- struct autofs_sb_info *sbi =
+ struct autofs_sb_info *sbi =
(struct autofs_sb_info *) inode->i_sb->u.generic_sbp;
- inode->i_op = NULL;
- inode->i_mode = 0;
- inode->i_nlink = 2;
- inode->i_size = 0;
- inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
- inode->i_blocks = 0;
- inode->i_blksize = 1024;
+ inode->i_op = NULL;
+ inode->i_mode = 0;
+ inode->i_nlink = 2;
+ inode->i_size = 0;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+ inode->i_blocks = 0;
+ inode->i_blksize = 1024;
- if ( ino == AUTOFS_ROOT_INO ) {
- inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR;
- inode->i_op = &autofs_root_inode_operations;
+ if ( ino == AUTOFS_ROOT_INO ) {
+ inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR;
+ inode->i_op = &autofs_root_inode_operations;
inode->i_uid = inode->i_gid = 0; /* Changed in read_super */
- return;
- }
+ return;
+ }
+
+ inode->i_uid = inode->i_sb->s_root->d_inode->i_uid;
+ inode->i_gid = inode->i_sb->s_root->d_inode->i_gid;
- inode->i_uid = inode->i_sb->s_mounted->i_uid;
- inode->i_gid = inode->i_sb->s_mounted->i_gid;
-
if ( ino >= AUTOFS_FIRST_SYMLINK && ino < AUTOFS_FIRST_DIR_INO ) {
/* Symlink inode - should be in symlink list */
struct autofs_symlink *sl;
@@ -273,5 +281,4 @@ static void autofs_read_inode(struct inode *inode)
static void autofs_write_inode(struct inode *inode)
{
- inode->i_dirt = 0;
}
diff --git a/fs/autofs/root.c b/fs/autofs/root.c
index a615ede29..4ecc6520e 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 dentry *);
+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,167 +93,189 @@ 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 try_to_fill_dentry(struct dentry * dentry, struct super_block * sb, struct autofs_sb_info *sbi)
{
- struct autofs_sb_info *sbi;
+ struct inode * inode;
struct autofs_dir_ent *ent;
- struct inode *res;
- autofs_hash_t hash;
- int status, oz_mode;
+
+ while (!(ent = autofs_hash_lookup(&sbi->dirhash, &dentry->d_name))) {
+ int status = autofs_wait(sbi, &dentry->d_name);
- DPRINTK(("autofs_root_lookup: name = "));
- autofs_say(name,len);
+ /* Turn this into a real negative dentry? */
+ if (status == -ENOENT) {
+ dentry->d_flags = 0;
+ return 0;
+ }
+ if (status)
+ return status;
+ }
- *result = NULL;
- if (!dir)
- return -ENOENT;
- if (!S_ISDIR(dir->i_mode)) {
- iput(dir);
- return -ENOTDIR;
+ if (!dentry->d_inode) {
+ inode = iget(sb, ent->ino);
+ if (!inode)
+ return -EACCES;
+
+ dentry->d_inode = inode;
}
- /* 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;
+ if (S_ISDIR(dentry->d_inode->i_mode)) {
+ while (dentry == dentry->d_mounts)
+ schedule();
}
+ dentry->d_flags = 0;
+ return 0;
+}
+
+
+/*
+ * Revalidate is called on every cache lookup. Some of those
+ * cache lookups may actually happen while the dentry is not
+ * yet completely filled in, and revalidate has to delay such
+ * lookups..
+ */
+static struct dentry * autofs_revalidate(struct dentry * dentry)
+{
+ struct autofs_sb_info *sbi;
+ struct inode * dir = dentry->d_parent->d_inode;
- *result = res = NULL;
sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp;
- hash = autofs_hash(name,len);
+ /* Incomplete dentry? */
+ if (dentry->d_flags) {
+ if (autofs_oz_mode(sbi))
+ return dentry;
- 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));
+ try_to_fill_dentry(dentry, dir->i_sb, sbi);
+ return dentry;
+ }
- do {
- while ( !(ent = autofs_hash_lookup(&sbi->dirhash,hash,name,len)) ) {
- DPRINTK(("lookup failed, pid = %u, pgrp = %u\n", current->pid, current->pgrp));
-
- if ( oz_mode ) {
- iput(dir);
- return -ENOENT;
- } else {
- status = autofs_wait(sbi,hash,name,len);
- DPRINTK(("autofs_wait returned %d\n", status));
- if ( status ) {
- iput(dir);
- return status;
- }
- }
- }
+ /* Negative dentry.. Should we time these out? */
+ if (!dentry->d_inode)
+ return dentry;
- DPRINTK(("lookup successful, inode = %08x\n", (unsigned int)ent->ino));
+ /* We should update the usage stuff here.. */
+ return dentry;
+}
- 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);
- return status;
- }
- }
- } while(!res);
- autofs_update_usage(&sbi->dirhash,ent);
-
- *result = res;
- iput(dir);
+static int autofs_root_lookup(struct inode *dir, struct dentry * dentry)
+{
+ struct autofs_sb_info *sbi;
+ struct inode *res;
+ int oz_mode;
+
+ DPRINTK(("autofs_root_lookup: name = "));
+ autofs_say(dentry->d_name.name,dentry->d_name.len);
+
+ if (!S_ISDIR(dir->i_mode))
+ return -ENOTDIR;
+
+ res = NULL;
+ sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp;
+
+ 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));
+
+ /*
+ * Mark the dentry incomplete, but add it. This is needed so
+ * that the VFS layer knows about the dentry, and we can count
+ * on catching any lookups through the revalidate.
+ *
+ * Let all the hard work be done by the revalidate function that
+ * needs to be able to do this anyway..
+ *
+ * We need to do this before we release the directory semaphore.
+ */
+ dentry->d_revalidate = autofs_revalidate;
+ dentry->d_flags = 1;
+ d_add(dentry, NULL);
+
+ up(&dir->i_sem);
+ autofs_revalidate(dentry);
+ down(&dir->i_sem);
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;
struct autofs_symlink *sl;
DPRINTK(("autofs_root_symlink: %s <- ", symname));
- autofs_say(name,len);
+ autofs_say(dentry->d_name.name,dentry->d_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));
return 0;
}
-static int autofs_root_unlink(struct inode *dir, const char *name, int len)
+/*
+ * NOTE!
+ *
+ * Normal filesystems would do a "d_delete()" to tell the VFS dcache
+ * that the file no longer exists. However, doing that means that the
+ * VFS layer can turn the dentry into a negative dentry, which we
+ * obviously do not want (we're dropping the entry not because it
+ * doesn't exist, but because it has timed out).
+ *
+ * Also see autofs_root_rmdir()..
+ */
+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 +286,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_drop(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_drop(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));
return 0;
}
diff --git a/fs/autofs/symlink.c b/fs/autofs/symlink.c
index d6ac82ed4..e3ff3d31b 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 cbe270a53..39beed699 100644
--- a/fs/autofs/waitq.c
+++ b/fs/autofs/waitq.c
@@ -37,7 +37,7 @@ void autofs_catatonic_mode(struct autofs_sb_info *sbi)
wake_up(&wq->queue);
wq = nwq;
}
- fput(sbi->pipe, sbi->pipe->f_inode); /* Close the pipe */
+ fput(sbi->pipe); /* Close the pipe */
}
static int autofs_write(struct file *file, const void *addr, int bytes)
@@ -55,7 +55,7 @@ static int autofs_write(struct file *file, const void *addr, int bytes)
old_signal = current->signal;
- while ( bytes && (written = file->f_op->write(file->f_inode,file,data,bytes)) > 0 ) {
+ while ( bytes && (written = file->f_op->write(file->f_dentry->d_inode,file,data,bytes)) > 0 ) {
data += written;
bytes -= written;
}
@@ -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_aout.c b/fs/binfmt_aout.c
index 394f41eb1..c5c7998cf 100644
--- a/fs/binfmt_aout.c
+++ b/fs/binfmt_aout.c
@@ -79,6 +79,7 @@ if (file.f_op->llseek) { \
static inline int
do_aout_core_dump(long signr, struct pt_regs * regs)
{
+ struct dentry * dentry = NULL;
struct inode * inode = NULL;
struct file file;
unsigned short fs;
@@ -114,26 +115,20 @@ do_aout_core_dump(long signr, struct pt_regs * regs)
#else
corefile[4] = '\0';
#endif
- if (open_namei(corefile,O_CREAT | 2 | O_TRUNC,0600,&inode,NULL)) {
- inode = NULL;
+ dentry = open_namei(corefile,O_CREAT | 2 | O_TRUNC, 0600);
+ if (IS_ERR(dentry)) {
+ dentry = NULL;
goto end_coredump;
}
+ inode = dentry->d_inode;
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 (init_private_file(&file, dentry, 3))
+ goto end_coredump;
if (!file.f_op->write)
goto close_coredump;
has_dumped = 1;
@@ -214,7 +209,6 @@ do_aout_core_dump(long signr, struct pt_regs * regs)
/* 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);
@@ -222,7 +216,7 @@ done_coredump:
put_write_access(inode);
end_coredump:
set_fs(fs);
- iput(inode);
+ dput(dentry);
return has_dumped;
}
@@ -318,7 +312,7 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs
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)) {
+ bprm->dentry->d_inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) {
return -ENOEXEC;
}
@@ -332,7 +326,7 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs
}
if (N_MAGIC(ex) == ZMAGIC && ex.a_text &&
- (fd_offset < bprm->inode->i_sb->s_blocksize)) {
+ (fd_offset < bprm->dentry->d_inode->i_sb->s_blocksize)) {
printk(KERN_NOTICE "N_TXTOFF < BLOCK_SIZE. Please convert binary.\n");
return -ENOEXEC;
}
@@ -372,12 +366,12 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs
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),
+ read_exec(bprm->dentry, 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),
+ read_exec(bprm->dentry, fd_offset + ex.a_text, (char *) N_DATADDR(ex),
ex.a_data, 0);
goto beyond_if;
}
@@ -389,20 +383,20 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs
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),
+ read_exec(bprm->dentry, fd_offset, (char *) N_TXTADDR(ex),
ex.a_text+ex.a_data, 0);
#else
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, 32, (char *) 0, ex.a_text+ex.a_data, 0);
+ read_exec(bprm->dentry, 32, (char *) 0, ex.a_text+ex.a_data, 0);
#endif
} 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);
+ fd = open_dentry(bprm->dentry, O_RDONLY);
if (fd < 0)
return fd;
@@ -412,7 +406,7 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs
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,
+ read_exec(bprm->dentry, fd_offset,
(char *) N_TXTADDR(ex), ex.a_text+ex.a_data, 0);
goto beyond_if;
}
@@ -482,18 +476,21 @@ do_load_aout_library(int fd)
{
struct file * file;
struct exec ex;
- struct inode * inode;
+ struct dentry * dentry;
+ 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;
+ dentry = file->f_dentry;
+ inode = dentry->d_inode;
+
/* Seek into the file */
if (file->f_op->llseek) {
if ((error = file->f_op->llseek(inode, file, 0, 0)) != 0)
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index ff987e0e8..7629fd0d8 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -134,23 +134,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);
-#if defined(__mips__) || defined(__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
@@ -165,17 +162,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
@@ -212,7 +209,7 @@ create_elf_tables(char *p, int argc, int envc,
an ELF header */
static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex,
- struct inode * interpreter_inode,
+ struct dentry * interpreter_dentry,
unsigned long *interp_load_addr)
{
struct file * file;
@@ -234,8 +231,9 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex,
if ((interp_elf_ex->e_type != ET_EXEC &&
interp_elf_ex->e_type != ET_DYN) ||
!elf_check_arch(interp_elf_ex->e_machine) ||
- (!interpreter_inode->i_op ||
- !interpreter_inode->i_op->default_file_ops->mmap)){
+ (!interpreter_dentry->d_inode->i_op ||
+ !interpreter_dentry->d_inode->i_op->default_file_ops->mmap)){
+
#ifdef DEBUG_ELF
printk("bad e_type %d ", interp_elf_ex->e_type);
#endif
@@ -265,7 +263,7 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex,
return ~0UL;
}
- retval = read_exec(interpreter_inode, interp_elf_ex->e_phoff,
+ retval = read_exec(interpreter_dentry, interp_elf_ex->e_phoff,
(char *) elf_phdata,
sizeof(struct elf_phdr) * interp_elf_ex->e_phnum, 1);
@@ -274,7 +272,7 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex,
return retval;
}
- elf_exec_fileno = open_inode(interpreter_inode, O_RDONLY);
+ elf_exec_fileno = open_dentry(interpreter_dentry, O_RDONLY);
if (elf_exec_fileno < 0) {
kfree(elf_phdata);
return ~0UL;
@@ -366,7 +364,7 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex,
}
static unsigned long load_aout_interp(struct exec * interp_ex,
- struct inode * interpreter_inode)
+ struct dentry * interpreter_dentry)
{
int retval;
unsigned long elf_entry;
@@ -381,13 +379,13 @@ static unsigned long load_aout_interp(struct exec * interp_ex,
do_mmap(NULL, 0, interp_ex->a_text+interp_ex->a_data,
PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_FIXED|MAP_PRIVATE, 0);
- retval = read_exec(interpreter_inode, 32, (char *) 0,
+ retval = read_exec(interpreter_dentry, 32, (char *) 0,
interp_ex->a_text+interp_ex->a_data, 0);
} else if (N_MAGIC(*interp_ex) == ZMAGIC || N_MAGIC(*interp_ex) == QMAGIC) {
do_mmap(NULL, 0, interp_ex->a_text+interp_ex->a_data,
PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_FIXED|MAP_PRIVATE, 0);
- retval = read_exec(interpreter_inode,
+ retval = read_exec(interpreter_dentry,
N_TXTOFF(*interp_ex) ,
(char *) N_TXTADDR(*interp_ex),
interp_ex->a_text+interp_ex->a_data, 0);
@@ -422,7 +420,7 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
struct elfhdr interp_elf_ex;
struct file * file;
struct exec interp_ex;
- struct inode *interpreter_inode;
+ struct dentry *interpreter_dentry;
unsigned long load_addr;
int load_addr_set = 0;
unsigned int interpreter_type = INTERPRETER_NONE;
@@ -456,8 +454,8 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
if ((elf_ex.e_type != ET_EXEC &&
elf_ex.e_type != ET_DYN) ||
(! elf_check_arch(elf_ex.e_machine)) ||
- (!bprm->inode->i_op || !bprm->inode->i_op->default_file_ops ||
- !bprm->inode->i_op->default_file_ops->mmap)){
+ (!bprm->dentry->d_inode->i_op || !bprm->dentry->d_inode->i_op->default_file_ops ||
+ !bprm->dentry->d_inode->i_op->default_file_ops->mmap)){
return -ENOEXEC;
}
@@ -475,7 +473,7 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
return -ENOMEM;
}
- retval = read_exec(bprm->inode, elf_ex.e_phoff, (char *) elf_phdata,
+ retval = read_exec(bprm->dentry, elf_ex.e_phoff, (char *) elf_phdata,
elf_ex.e_phentsize * elf_ex.e_phnum, 1);
if (retval < 0) {
kfree (elf_phdata);
@@ -487,7 +485,7 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
elf_bss = 0;
elf_brk = 0;
- elf_exec_fileno = open_inode(bprm->inode, O_RDONLY);
+ elf_exec_fileno = open_dentry(bprm->dentry, O_RDONLY);
if (elf_exec_fileno < 0) {
kfree (elf_phdata);
@@ -525,7 +523,7 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
return -ENOMEM;
}
- retval = read_exec(bprm->inode,elf_ppnt->p_offset,
+ retval = read_exec(bprm->dentry,elf_ppnt->p_offset,
elf_interpreter,
elf_ppnt->p_filesz, 1);
/* If the program interpreter is one of these two,
@@ -540,13 +538,14 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
if (retval >= 0) {
old_fs = get_fs(); /* This could probably be optimized */
set_fs(get_ds());
- retval = open_namei(elf_interpreter, 0, 0,
- &interpreter_inode, NULL);
+ interpreter_dentry = open_namei(elf_interpreter, 0, 0);
set_fs(old_fs);
+ if (IS_ERR(interpreter_dentry))
+ retval = PTR_ERR(interpreter_dentry);
}
if (retval >= 0)
- retval = read_exec(interpreter_inode,0,bprm->buf,128, 1);
+ retval = read_exec(interpreter_dentry,0,bprm->buf,128, 1);
if (retval >= 0) {
interp_ex = *((struct exec *) bprm->buf); /* exec-header */
@@ -682,13 +681,13 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
if (elf_interpreter) {
if (interpreter_type & 1)
elf_entry = load_aout_interp(&interp_ex,
- interpreter_inode);
+ interpreter_dentry);
else if (interpreter_type & 2)
elf_entry = load_elf_interp(&interp_elf_ex,
- interpreter_inode,
+ interpreter_dentry,
&interp_load_addr);
- iput(interpreter_inode);
+ dput(interpreter_dentry);
kfree(elf_interpreter);
if (elf_entry == ~0UL) {
@@ -716,8 +715,7 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
__MOD_INC_USE_COUNT(current->binfmt->module);
#ifndef VM_STACK_FLAGS
- current->executable = bprm->inode;
- atomic_inc(&bprm->inode->i_count);
+ current->executable = dget(bprm->dentry);
#endif
#ifdef LOW_ELF_STACK
current->start_stack = bprm->p = elf_stack - 4;
@@ -802,7 +800,8 @@ do_load_elf_library(int fd){
struct file * file;
struct elfhdr elf_ex;
struct elf_phdr *elf_phdata = NULL;
- struct inode * inode;
+ struct dentry * dentry;
+ struct inode * inode;
unsigned long len;
int elf_bss;
int retval;
@@ -812,7 +811,8 @@ do_load_elf_library(int fd){
len = 0;
file = current->files->fd[fd];
- inode = file->f_inode;
+ dentry = file->f_dentry;
+ inode = dentry->d_inode;
elf_bss = 0;
if (!file || !file->f_op)
@@ -851,7 +851,7 @@ do_load_elf_library(int fd){
if (elf_phdata == NULL)
return -ENOMEM;
- retval = read_exec(inode, elf_ex.e_phoff, (char *) elf_phdata,
+ retval = read_exec(dentry, elf_ex.e_phoff, (char *) elf_phdata,
sizeof(struct elf_phdr) * elf_ex.e_phnum, 1);
j = 0;
@@ -923,14 +923,13 @@ static int load_elf_library(int fd)
*/
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;
+ return file->f_op->write(file->f_dentry->d_inode, file, addr, nr) == nr;
}
static int dump_seek(struct file *file, off_t off)
{
if (file->f_op->llseek) {
- if (file->f_op->llseek(file->f_inode, file, off, 0) != off)
+ if (file->f_op->llseek(file->f_dentry->d_inode, file, off, 0) != off)
return 0;
} else
file->f_pos = off;
@@ -1045,6 +1044,7 @@ static int elf_core_dump(long signr, struct pt_regs * regs)
{
int has_dumped = 0;
struct file file;
+ struct dentry *dentry;
struct inode *inode;
unsigned short fs;
char corefile[6+sizeof(current->comm)];
@@ -1118,24 +1118,18 @@ static int elf_core_dump(long signr, struct pt_regs * regs)
#else
corefile[4] = '\0';
#endif
- if (open_namei(corefile,O_CREAT | 2 | O_TRUNC,0600,&inode,NULL)) {
- inode = NULL;
+ dentry = open_namei(corefile, O_CREAT | 2 | O_TRUNC, 0600);
+ if (IS_ERR(dentry)) {
+ dentry = NULL;
goto end_coredump;
}
+ inode = dentry->d_inode;
if (!S_ISREG(inode->i_mode))
goto end_coredump;
if (!inode->i_op || !inode->i_op->default_file_ops)
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 end_coredump;
+ if (init_private_file(&file, dentry, 3))
+ goto end_coredump;
if (!file.f_op->write)
goto close_coredump;
has_dumped = 1;
@@ -1326,7 +1320,7 @@ static int elf_core_dump(long signr, struct pt_regs * regs)
end_coredump:
set_fs(fs);
- iput(inode);
+ dput(dentry);
#ifndef CONFIG_BINFMT_ELF
MOD_DEC_USE_COUNT;
#endif
diff --git a/fs/binfmt_em86.c b/fs/binfmt_em86.c
index 7bd71f212..133586e69 100644
--- a/fs/binfmt_em86.c
+++ b/fs/binfmt_em86.c
@@ -23,6 +23,7 @@
static int do_load_em86(struct linux_binprm *bprm,struct pt_regs *regs)
{
char *interp, *i_name, *i_arg;
+ struct dentry * dentry;
int retval;
struct elfhdr elf_ex;
@@ -39,14 +40,14 @@ static int do_load_em86(struct linux_binprm *bprm,struct pt_regs *regs)
if ((elf_ex.e_type != ET_EXEC &&
elf_ex.e_type != ET_DYN) ||
(!((elf_ex.e_machine == EM_386) || (elf_ex.e_machine == EM_486))) ||
- (!bprm->inode->i_op || !bprm->inode->i_op->default_file_ops ||
- !bprm->inode->i_op->default_file_ops->mmap)){
+ (!bprm->dentry->d_inode->i_op || !bprm->dentry->d_inode->i_op->default_file_ops ||
+ !bprm->dentry->d_inode->i_op->default_file_ops->mmap)){
return -ENOEXEC;
}
bprm->sh_bang++; /* Well, the bang-shell is implicit... */
- iput(bprm->inode);
- bprm->dont_iput = 1;
+ dput(bprm->dentry);
+ bprm->dentry = NULL;
/* Unlike in the script case, we don't have to do any hairy
* parsing to find our interpreter... it's hardcoded!
@@ -79,14 +80,17 @@ static int do_load_em86(struct linux_binprm *bprm,struct pt_regs *regs)
* Note that we use open_namei() as the name is now in kernel
* space, and we don't need to copy it.
*/
- retval = open_namei(interp, 0, 0, &bprm->inode, NULL);
- if (retval)
- return retval;
- bprm->dont_iput=0;
- retval=prepare_binprm(bprm);
- if(retval<0)
+ dentry = open_namei(interp, 0, 0);
+ if (IS_ERR(dentry))
+ return PTR_ERR(dentry);
+
+ bprm->dentry = dentry;
+
+ retval = prepare_binprm(bprm);
+ if (retval < 0)
return retval;
- return search_binary_handler(bprm,regs);
+
+ return search_binary_handler(bprm, regs);
}
static int load_em86(struct linux_binprm *bprm,struct pt_regs *regs)
diff --git a/fs/binfmt_java.c b/fs/binfmt_java.c
index fcf664c5d..41e66a21a 100644
--- a/fs/binfmt_java.c
+++ b/fs/binfmt_java.c
@@ -28,7 +28,9 @@ static int do_load_java(struct linux_binprm *bprm,struct pt_regs *regs)
char *i_name;
int len;
int retval;
+ struct dentry * dentry;
unsigned char *ucp = (unsigned char *) bprm->buf;
+
if ((ucp[0] != 0xca) || (ucp[1] != 0xfe) || (ucp[2] != 0xba) || (ucp[3] != 0xbe))
return -ENOEXEC;
@@ -42,8 +44,8 @@ static int do_load_java(struct linux_binprm *bprm,struct pt_regs *regs)
bprm->java = 1;
- iput(bprm->inode);
- bprm->dont_iput=1;
+ dput(bprm->dentry);
+ bprm->dentry = NULL;
/*
* Set args: [0] the name of the java interpreter
@@ -75,15 +77,17 @@ static int do_load_java(struct linux_binprm *bprm,struct pt_regs *regs)
if (!bprm->p)
return -E2BIG;
/*
- * OK, now restart the process with the interpreter's inode.
+ * OK, now restart the process with the interpreter's dentry.
*/
bprm->filename = binfmt_java_interpreter;
- retval = open_namei(binfmt_java_interpreter, 0, 0, &bprm->inode, NULL);
- if (retval)
+ dentry = open_namei(binfmt_java_interpreter, 0, 0);
+ retval = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
return retval;
- bprm->dont_iput=0;
- retval=prepare_binprm(bprm);
- if(retval<0)
+
+ bprm->dentry = dentry;
+ retval = prepare_binprm(bprm);
+ if (retval < 0)
return retval;
return search_binary_handler(bprm,regs);
@@ -92,12 +96,14 @@ static int do_load_java(struct linux_binprm *bprm,struct pt_regs *regs)
static int do_load_applet(struct linux_binprm *bprm,struct pt_regs *regs)
{
char *i_name;
+ struct dentry * dentry;
int retval;
+
if (strncmp (bprm->buf, "<!--applet", 10))
return -ENOEXEC;
- iput(bprm->inode);
- bprm->dont_iput=1;
+ dput(bprm->dentry);
+ bprm->dentry = NULL;
/*
* Set args: [0] the name of the appletviewer
@@ -118,15 +124,17 @@ static int do_load_applet(struct linux_binprm *bprm,struct pt_regs *regs)
if (!bprm->p)
return -E2BIG;
/*
- * OK, now restart the process with the interpreter's inode.
+ * OK, now restart the process with the interpreter's dentry.
*/
bprm->filename = binfmt_java_appletviewer;
- retval = open_namei(binfmt_java_appletviewer, 0, 0, &bprm->inode, NULL);
- if (retval)
+ dentry = open_namei(binfmt_java_appletviewer, 0, 0);
+ retval = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
return retval;
- bprm->dont_iput=0;
- retval=prepare_binprm(bprm);
- if(retval<0)
+
+ bprm->dentry = dentry;
+ retval = prepare_binprm(bprm);
+ if (retval < 0)
return retval;
return search_binary_handler(bprm,regs);
diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c
index 28dced394..ffca300d9 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 <linux/module.h>
@@ -48,7 +50,7 @@ struct binfmt_entry {
#define ENTRY_ENABLED 1 /* the old binfmt_entry.enabled */
#define ENTRY_MAGIC 8 /* not filename detection */
-#define ENTRY_STRIP_EXT 32 /* strip of last filename extension */
+#define ENTRY_STRIP_EXT 32 /* strip off last filename extension */
static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs);
static void entry_proc_cleanup(struct binfmt_entry *e);
@@ -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);
}
@@ -157,6 +157,7 @@ static struct binfmt_entry *check_file(struct linux_binprm *bprm)
static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs)
{
struct binfmt_entry *fmt;
+ struct dentry * dentry;
char iname[128];
char *iname_addr = iname, *p;
int retval, fmt_flags = 0;
@@ -180,17 +181,16 @@ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs)
goto _ret;
}
- iput(bprm->inode);
- bprm->dont_iput = 1;
+ dput(bprm->dentry);
+ bprm->dentry = NULL;
/* 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) {
@@ -199,11 +199,14 @@ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs)
}
bprm->filename = iname; /* for binfmt_script */
- if ((retval = open_namei(iname, 0, 0, &bprm->inode, NULL)))
+ dentry = open_namei(iname, 0, 0);
+ retval = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
goto _ret;
- bprm->dont_iput = 0;
+ bprm->dentry = dentry;
- if ((retval = prepare_binprm(bprm)) >= 0)
+ retval = prepare_binprm(bprm);
+ if (retval >= 0)
retval = search_binary_handler(bprm, regs);
_ret:
MOD_DEC_USE_COUNT;
@@ -322,7 +325,7 @@ static int proc_write_register(struct file *file, const char *buffer,
entries = e;
write_unlock(&entries_lock);
- return count;
+ err = count;
_err:
MOD_DEC_USE_COUNT;
return err;
@@ -499,6 +502,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/binfmt_script.c b/fs/binfmt_script.c
index 1bd2f0d10..5a38cf545 100644
--- a/fs/binfmt_script.c
+++ b/fs/binfmt_script.c
@@ -15,8 +15,10 @@
static int do_load_script(struct linux_binprm *bprm,struct pt_regs *regs)
{
char *cp, *i_name, *i_name_start, *i_arg;
+ struct dentry * dentry;
char interp[128];
int retval;
+
if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!') || (bprm->sh_bang))
return -ENOEXEC;
/*
@@ -25,8 +27,8 @@ static int do_load_script(struct linux_binprm *bprm,struct pt_regs *regs)
*/
bprm->sh_bang++;
- iput(bprm->inode);
- bprm->dont_iput=1;
+ dput(bprm->dentry);
+ bprm->dentry = NULL;
bprm->buf[127] = '\0';
if ((cp = strchr(bprm->buf, '\n')) == NULL)
@@ -75,14 +77,15 @@ static int do_load_script(struct linux_binprm *bprm,struct pt_regs *regs)
if (!bprm->p)
return -E2BIG;
/*
- * OK, now restart the process with the interpreter's inode.
+ * OK, now restart the process with the interpreter's dentry.
*/
- retval = open_namei(interp, 0, 0, &bprm->inode, NULL);
- if (retval)
- return retval;
- bprm->dont_iput=0;
- retval=prepare_binprm(bprm);
- if(retval<0)
+ dentry = open_namei(interp, 0, 0);
+ if (IS_ERR(dentry))
+ return PTR_ERR(dentry);
+
+ bprm->dentry = dentry;
+ retval = prepare_binprm(bprm);
+ if (retval < 0)
return retval;
return search_binary_handler(bprm,regs);
}
diff --git a/fs/buffer.c b/fs/buffer.c
index bd06972f3..3e1b5cc35 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -289,16 +289,35 @@ int file_fsync (struct inode *inode, struct file *filp)
asmlinkage int sys_fsync(unsigned int fd)
{
struct file * file;
+ struct dentry * dentry;
struct inode * inode;
- int err = 0;
+ int err;
lock_kernel();
- if (fd>=NR_OPEN || !(file=current->files->fd[fd]) || !(inode=file->f_inode))
- err = -EBADF;
- else if (!file->f_op || !file->f_op->fsync)
- err = -EINVAL;
- else if (file->f_op->fsync(inode,file))
- err = -EIO;
+ err = -EBADF;
+
+ if (fd >= NR_OPEN)
+ goto out;
+
+ file = current->files->fd[fd];
+ if (!file)
+ goto out;
+
+ dentry = file->f_dentry;
+ if (!dentry)
+ goto out;
+
+ inode = dentry->d_inode;
+ if (!inode)
+ goto out;
+
+ err = -EINVAL;
+ if (!file->f_op || !file->f_op->fsync)
+ goto out;
+
+ err = file->f_op->fsync(inode,file);
+
+out:
unlock_kernel();
return err;
}
@@ -306,20 +325,35 @@ asmlinkage int sys_fsync(unsigned int fd)
asmlinkage int sys_fdatasync(unsigned int fd)
{
struct file * file;
+ struct dentry * dentry;
struct inode * inode;
- int err = -EBADF;
+ int err;
lock_kernel();
- if (fd>=NR_OPEN || !(file=current->files->fd[fd]) || !(inode=file->f_inode))
+ err = -EBADF;
+
+ if (fd >= NR_OPEN)
+ goto out;
+
+ file = current->files->fd[fd];
+ if (!file)
+ goto out;
+
+ dentry = file->f_dentry;
+ if (!dentry)
goto out;
+
+ inode = dentry->d_inode;
+ if (!inode)
+ goto out;
+
err = -EINVAL;
if (!file->f_op || !file->f_op->fsync)
goto out;
+
/* this needs further work, at the moment it is identical to fsync() */
- if (file->f_op->fsync(inode,file))
- err = -EIO;
- else
- err = 0;
+ err = file->f_op->fsync(inode,file);
+
out:
unlock_kernel();
return err;
@@ -495,17 +529,18 @@ static inline void insert_into_queues(struct buffer_head * bh)
static inline struct buffer_head * find_buffer(kdev_t dev, int block, int size)
{
- struct buffer_head * tmp;
-
- for (tmp = hash(dev,block) ; tmp != NULL ; tmp = tmp->b_next)
- if (tmp->b_blocknr == block && tmp->b_dev == dev) {
- if (tmp->b_size == size)
- return tmp;
+ struct buffer_head * next;
- printk("VFS: Wrong blocksize on device %s\n",
- kdevname(dev));
- return NULL;
- }
+ next = hash(dev,block);
+ for (;;) {
+ struct buffer_head *tmp = next;
+ if (!next)
+ break;
+ next = tmp->b_next;
+ if (tmp->b_blocknr != block || tmp->b_size != size || tmp->b_dev != dev)
+ continue;
+ return tmp;
+ }
return NULL;
}
@@ -518,10 +553,11 @@ static inline struct buffer_head * find_buffer(kdev_t dev, int block, int size)
*/
struct buffer_head * get_hash_table(kdev_t dev, int block, int size)
{
- struct buffer_head * bh;
-
for (;;) {
- if (!(bh=find_buffer(dev,block,size)))
+ struct buffer_head * bh;
+
+ bh=find_buffer(dev,block,size);
+ if (!bh)
return NULL;
bh->b_count++;
wait_on_buffer(bh);
@@ -1610,6 +1646,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 0472487e0..395aed829 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -5,1035 +5,341 @@
* (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 <linux/config.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/fs.h>
-#include <linux/dalloc.h>
-#include <linux/dlists.h>
-
-/* this should be removed after the beta phase */
-/* #define DEBUG */
-/*#undef DEBUG*/
-/* #define DEBUG_DDIR_COUNT */
-
-#define D_HASHSIZE 64
-
-/* 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
-
-/* adjust these constants if you know a probability distribution ... */
-#define D_SMALL 16
-#define D_MEDIUM 64
-#define D_LARGE 256
-#define D_HUGE D_MAXLEN
+#include <linux/malloc.h>
-#define BASE_DHEADER(x) (struct dheader*)((unsigned long)(x) & ~(PAGE_SIZE-1))
-#define BYTE_ADD(x,n) (void*)((char*)(x) + (n))
-#define BYTE_SUB(x,n) (void*)((char*)(x) - (n))
-
-/* This is for global allocation of dentries. Remove this when
- * converting to SLAB.
- */
-struct dheader {
- struct dentry * emptylist;
- short free, maxfree;
- struct dheader * next;
- struct dheader * prev;
-};
-
-struct anchors {
- struct dheader * free; /* each contains at least 1 empty dentry */
- struct dheader * full; /* all the used up ones */
- struct dheader * dir_free;
- struct dheader * dir_full;
-};
-
-/* 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.
+/*
+ * 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.
*/
-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 */
-};
-
-DEF_INSERT(header,struct dheader,next,prev)
-DEF_REMOVE(header,struct dheader,next,prev)
+#define D_HASHBITS 10
+#define D_HASHSIZE (1UL << D_HASHBITS)
+#define D_HASHMASK (D_HASHSIZE-1)
-DEF_INSERT(alias,struct dentry,d_next,d_prev)
-DEF_REMOVE(alias,struct dentry,d_next,d_prev)
+static struct list_head dentry_hashtable[D_HASHSIZE];
+static LIST_HEAD(dentry_unused);
-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)
-
-static struct anchors anchors[4];
-
-struct dentry * the_root = NULL;
-
-unsigned long name_cache_init(unsigned long mem_start, unsigned long mem_end)
+void d_free(struct dentry *dentry)
{
- memset(anchors, 0, sizeof(anchors));
- return mem_start;
+ kfree(dentry->d_name.name);
+ kfree(dentry);
}
-#ifdef DEBUG
-/* throw this away after the beta phase */
-/*************************************************************************/
-extern void xcheck(char * txt, struct inode * p);
-
-static int x_alloc = 0;
-static int x_freed = 0;
-static int x_free = 0;
-
-static void * tst[20000];
-static int cnt = 0;
-
-static void ins(void* ptr)
-{
- extern int inodes_stat;
- tst[cnt++] = 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 0
-static inline int search(void* ptr)
-{
- int i;
- for(i = cnt-1; i>=0; i--)
- if(tst[i] == ptr)
- return i;
- return -1;
-}
-
-#define TST(n,x) if(search(x)<0) printk("%s bad ptr %p line %d\n", n, x, __LINE__)
-#else
-#define TST(n,x) /*nothing*/
-#endif
-
-void LOG(char * txt, struct dentry * entry)
-{
- static int count = 0;
- if(entry) {
- TST(txt,entry);
- }
- 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");
+/*
+ * dput()
+ *
+ * This is complicated by the fact that we do not want to put
+ * dentries that are no longer on any hash chain on the unused
+ * list: we'd much rather just get rid of them immediately.
+ *
+ * However, that implies that we have to traverse the dentry
+ * tree upwards to the parents which might _also_ now be
+ * scheduled for deletion (it may have been only waiting for
+ * its last child to go away).
+ *
+ * This tail recursion is done by hand as we don't want to depend
+ * on the compiler to always get this right (gcc generally doesn't).
+ * Real recursion would eat up our stack space.
+ */
+void dput(struct dentry *dentry)
+{
+ if (dentry) {
+ int count;
+repeat:
+ count = dentry->d_count-1;
+ if (count < 0) {
+ printk("Negative d_count (%d) for %s/%s\n",
+ count,
+ dentry->d_parent->d_name.name,
+ dentry->d_name.name);
+ *(int *)0 = 0;
+ }
+ dentry->d_count = count;
+ if (!count) {
+ list_del(&dentry->d_lru);
+ if (list_empty(&dentry->d_hash)) {
+ struct inode *inode = dentry->d_inode;
+ struct dentry * parent;
+ if (inode) {
+ list_del(&dentry->d_alias);
+ iput(inode);
+ }
+ parent = dentry->d_parent;
+ d_free(dentry);
+ if (dentry == parent)
+ return;
+ dentry = parent;
+ goto repeat;
}
+ list_add(&dentry->d_lru, &dentry_unused);
}
}
}
-#else
-#define recursive_test(e) /*nothing*/
-#endif
-#else
-#define TST(n,x) /*nothing*/
-#define LOG(n,x) /*nothing*/
-#define xcheck(t,i) /*nothing*/
-#define recursive_test(e) /*nothing*/
-/*****************************************************************************/
-#endif
-void printpath(struct dentry * entry)
-{
- if(!IS_ROOT(entry))
- printpath(entry->d_parent);
- printk("/%s", entry->d_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.
+/*
+ * Shrink the dcache. This is done when we need
+ * more memory, or simply when we need to unmount
+ * something (at which point we need to unuse
+ * all dentries).
*/
-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);
+void shrink_dcache(void)
+{
+ for (;;) {
+ struct dentry *dentry;
+ struct list_head *tmp = dentry_unused.prev;
+
+ if (tmp == &dentry_unused)
+ break;
+ list_del(tmp);
+ INIT_LIST_HEAD(tmp);
+ dentry = list_entry(tmp, struct dentry, d_lru);
+ if (!dentry->d_count) {
+ struct dentry * parent;
+
+ list_del(&dentry->d_hash);
+ if (dentry->d_inode) {
+ struct inode * inode = dentry->d_inode;
+
+ list_del(&dentry->d_alias);
+ dentry->d_inode = NULL;
+ iput(inode);
+ }
+ parent = dentry->d_parent;
+ d_free(dentry);
+ dput(parent);
}
-#endif
- inode->i_ddir_count++;
- _get_inode(inode);
- }
-}
-
-static inline blocking void dec_ddir(struct dentry * entry, struct inode * inode)
-{
- if(entry->d_flag & D_INC_DDIR) {
- entry->d_flag &= ~D_INC_DDIR;
- inode->i_ddir_count--;
- if(!inode->i_ddir_count)
- __iput(inode);
}
}
-/* Do not inline this many times. */
-static void d_panic(void)
-{
- panic("VFS: dcache directory corruption");
-}
+#define NAME_ALLOC_LEN(len) ((len+16) & ~15)
-static inline struct ddir * d_dir(struct dentry * entry)
+struct dentry * d_alloc(struct dentry * parent, const struct qstr *name)
{
- struct ddir * res = BYTE_SUB(entry, sizeof(struct ddir));
-
- if(!(entry->d_flag & D_DIR))
- d_panic();
-#ifdef DEBUG
- if(!entry)
- panic("entry NULL!");
- if(BASE_DHEADER(res) != BASE_DHEADER(entry))
- printk("Scheisse!!!\n");
-#endif
- return res;
-}
+ char * str;
+ struct dentry *dentry;
-static /*inline*/ struct dheader * dinit(int isdir, int size)
-{
- struct dheader * res = (struct dheader*)__get_free_page(GFP_KERNEL);
- int restlen = PAGE_SIZE - sizeof(struct dheader);
- struct dentry * ptr = BYTE_ADD(res, sizeof(struct dheader));
+ dentry = kmalloc(sizeof(struct dentry), GFP_KERNEL);
+ if (!dentry)
+ return NULL;
- if(!res)
+ str = kmalloc(NAME_ALLOC_LEN(name->len), GFP_KERNEL);
+ if (!str) {
+ kfree(dentry);
return NULL;
- memset(res, 0, sizeof(struct dheader));
- if(isdir) {
- ptr = BYTE_ADD(ptr, sizeof(struct ddir));
- size += sizeof(struct ddir);
}
- if(BASE_DHEADER(ptr) != res)
- panic("Bad kernel page alignment");
- size += sizeof(struct dentry) - D_MAXLEN;
- res->emptylist = NULL;
- res->free = 0;
- while(restlen >= size) {
-#ifdef DEBUG
- ins(ptr);
- if(BASE_DHEADER(ptr) != res)
- panic("Wrong dinit!");
-#endif
- ptr->d_next = res->emptylist;
- res->emptylist = ptr;
- ptr = BYTE_ADD(ptr, size);
- res->free++;
- restlen -= size;
- }
- res->maxfree = res->free;
- return res;
-}
-static /*inline*/ struct dentry * __dalloc(struct anchors * anchor,
- struct dentry * parent, int isdir,
- int len, int size)
-{
- struct dheader ** free = isdir ? &anchor->dir_free : &anchor->free;
- struct dheader ** full = isdir ? &anchor->dir_full : &anchor->full;
- struct dheader * base = *free;
- struct dentry * res;
+ memcpy(str, name->name, name->len);
+ str[name->len] = 0;
- if(!base) {
- base = dinit(isdir, size);
- if(!base)
- return NULL;
- insert_header(free, base);
- }
- base->free--;
- res = base->emptylist;
- if(!(base->emptylist = res->d_next)) {
- remove_header(free, base);
- insert_header(full, base);
- }
- memset(res, 0, sizeof(struct dentry) - D_MAXLEN);
- if(isdir) {
- res->d_flag = D_DIR;
- memset(d_dir(res), 0, sizeof(struct ddir));
- }
- res->d_len = len;
- 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++;
- }
-#ifdef DEBUG
- x_alloc++;
-#endif
- return res;
+ dentry->d_count = 0;
+ dentry->d_flags = 0;
+ dentry->d_inode = NULL;
+ dentry->d_parent = parent;
+ dentry->d_mounts = dentry;
+ dentry->d_covers = dentry;
+ INIT_LIST_HEAD(&dentry->d_hash);
+ INIT_LIST_HEAD(&dentry->d_alias);
+ INIT_LIST_HEAD(&dentry->d_lru);
+
+ dentry->d_name.name = str;
+ dentry->d_name.len = name->len;
+ dentry->d_name.hash = name->hash;
+ dentry->d_revalidate = NULL;
+ return dentry;
}
-struct dentry * d_alloc(struct dentry * parent, int len, int isdir)
+/*
+ * 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 i, size;
+ if (inode)
+ list_add(&entry->d_alias, &inode->i_dentry);
-#ifdef DEBUG
- if(the_root)
- recursive_test(the_root);
- LOG("d_alloc", parent);
-#endif
- if(len >= D_MEDIUM) {
- if(len >= D_LARGE) {
- i = 3;
- size = D_HUGE;
- } else {
- i = 2;
- size = D_LARGE;
- }
- } else if(len >= D_SMALL) {
- i = 1;
- size = D_MEDIUM;
- } else {
- i = 0;
- size = D_SMALL;
- }
- return __dalloc(&anchors[i], parent, isdir, len, size);
+ entry->d_inode = inode;
}
-extern blocking struct dentry * d_alloc_root(struct inode * root_inode)
+struct dentry * d_alloc_root(struct inode * root_inode, struct dentry *old_root)
{
- struct dentry * res = the_root;
-
- if(res) {
- d_del(res, D_NO_CLEAR_INODE); /* invalidate everything beyond */
- } else {
- struct ddir * ddir;
+ struct dentry *res = NULL;
- the_root = res = d_alloc(NULL, 0, 1);
- LOG("d_alloc_root", res);
+ if (root_inode) {
+ res = d_alloc(NULL, &(const struct qstr) { "/", 1, 0 });
res->d_parent = res;
- res->d_name[0]='\0';
- ddir = d_dir(res);
- ddir->dd_alloced = 999; /* protect from deletion */
+ res->d_count = 1;
+ d_instantiate(res, root_inode);
}
- insert_alias(&root_inode->i_dentry, res);
- root_inode->i_dent_count++;
- root_inode->i_ddir_count++;
- res->u.d_inode = root_inode;
return res;
}
-static inline unsigned long d_hash(char first, char last)
+static inline struct list_head * d_hash(struct dentry * parent, unsigned long hash)
{
- return ((unsigned long)first ^ ((unsigned long)last << 4)) & (D_HASHSIZE-1);
+ hash += (unsigned long) parent;
+ hash = hash ^ (hash >> D_HASHBITS) ^ (hash >> D_HASHBITS*2);
+ return dentry_hashtable + (hash & D_HASHMASK);
}
-static inline struct dentry ** d_base_entry(struct ddir * pdir, struct dentry * entry)
+static inline struct dentry * __dlookup(struct list_head *head, struct dentry * parent, struct qstr * name)
{
- return &pdir->dd_hashtable[d_hash(entry->d_name[0],
- entry->d_name[entry->d_len-1])];
-}
+ struct list_head *tmp = head->next;
+ int len = name->len;
+ int hash = name->hash;
+ const unsigned char *str = name->name;
-static inline struct dentry ** d_base_qstr(struct ddir * pdir,
- struct qstr * s1,
- struct qstr * s2)
-{
- unsigned long hash;
+ while (tmp != head) {
+ struct dentry * dentry = list_entry(tmp, struct dentry, d_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]);
+ tmp = tmp->next;
+ if (dentry->d_name.hash != hash)
+ continue;
+ if (dentry->d_name.len != len)
+ continue;
+ if (dentry->d_parent != parent)
+ continue;
+ if (memcmp(dentry->d_name.name, str, len))
+ continue;
+ return dentry;
}
- return &pdir->dd_hashtable[hash];
-}
-
-
-static /*inline*/ blocking void _d_remove_from_parent(struct dentry * entry,
- struct ddir * pdir,
- struct inode * inode,
- int flags)
-{
- if(entry->d_flag & D_HASHED) {
- struct dentry ** base = d_base_entry(pdir, 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,
- struct anchors * anchor,
- int flags)
-{
- struct dheader ** free;
- struct dheader ** full;
- struct dheader * base = BASE_DHEADER(entry);
- 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;
- }
- if(entry->d_flag & D_DIR) {
- free = &anchor->dir_free;
- full = &anchor->dir_full;
- } else {
- free = &anchor->free;
- full = &anchor->full;
- }
- 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)) {
- base->free++;
- if(base->free == base->maxfree) {
-#ifndef DEBUG
- remove_header(free, base);
- free_page((unsigned long)base);
- goto done;
-#endif
- }
- entry->d_next = base->emptylist;
- base->emptylist = entry;
- if(!entry->d_next) {
- remove_header(full, base);
- insert_header(free, base);
- }
-#ifdef DEBUG
- x_freed++;
-#endif
- }
-#ifndef DEBUG
-done:
-#else
- x_free++;
-#endif
+ return NULL;
}
-blocking void d_del(struct dentry * entry, int flags)
+struct dentry * d_lookup(struct dentry * dir, struct qstr * name)
{
- int i;
-
- if(!entry)
- return;
- LOG("d_clear", entry);
- if(entry->d_len >= D_MEDIUM) {
- if(entry->d_len >= D_LARGE) {
- i = 3;
- } else {
- i = 2;
- }
- } else if(entry->d_len >= D_SMALL) {
- i = 1;
- } else {
- i = 0;
- }
- _d_del(entry, &anchors[i], flags);
+ return __dlookup(d_hash(dir, name->hash), dir, name);
}
-static inline struct dentry * __dlookup(struct dentry ** base,
- struct qstr * name,
- struct qstr * appendix)
+static inline void d_insert_to_parent(struct dentry * entry, struct dentry * parent)
{
- struct dentry * tmp = *base;
-
- if(tmp && name->len) {
- int totallen = name->len;
-
- if(appendix)
- totallen += appendix->len;
- do {
- if(tmp->d_len == totallen &&
- !(tmp->d_flag & D_DUPLICATE) &&
- !strncmp(tmp->d_name, name->name, name->len) &&
- (!appendix || !strncmp(tmp->d_name+name->len,
- appendix->name, appendix->len)))
- return tmp;
- tmp = tmp->d_hash_next;
- } while(tmp != *base);
- }
- return NULL;
+ list_add(&entry->d_hash, d_hash(dget(parent), entry->d_name.hash));
}
-struct dentry * d_lookup(struct inode * dir,
- struct qstr * name,
- struct qstr * appendix)
+static inline void d_remove_from_parent(struct dentry * dentry, struct dentry * parent)
{
- if(dir->i_dentry) {
- struct ddir * ddir = d_dir(dir->i_dentry);
- struct dentry ** base = d_base_qstr(ddir, name, appendix);
-
- return __dlookup(base, name, appendix);
- }
- return NULL;
+ list_del(&dentry->d_hash);
+ dput(parent);
}
-static /*inline*/ blocking void _d_insert_to_parent(struct dentry * entry,
- struct ddir * pdir,
- 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) {
- 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);
- 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");
- }
-#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);
- }
-}
-
-blocking void d_add(struct dentry * entry, struct inode * inode,
- struct qstr * ininame, int flags)
+/*
+ * When a file is deleted, we have two options:
+ * - turn this dentry into a negative dentry
+ * - unhash this dentry and free it.
+ *
+ * Usually, we want to just turn this into
+ * a negative dentry, but if anybody else is
+ * currently using the dentry or the inode
+ * we can't do that and we fall back on removing
+ * it from the hash queues and waiting for
+ * it to be deleted later when it has no users
+ */
+void d_delete(struct dentry * dentry)
{
- struct dentry * parent = entry->d_parent;
- struct qstr dummy;
- struct ddir * pdir;
+ /*
+ * Are we the only user?
+ */
+ if (dentry->d_count == 1) {
+ struct inode * inode = dentry->d_inode;
-#ifdef DEBUG
- if(inode)
- xcheck("d_add", inode);
- if(IS_ROOT(entry)) {
- printk("VFS: d_add for root dentry ");
- printpath(entry);
- printk(" -> ");
- if(ininame)
- printk("%s", ininame->name);
- printk("\n");
+ dentry->d_inode = NULL;
+ list_del(&dentry->d_alias);
+ iput(inode);
return;
}
- if(!parent)
- panic("d_add with parent==NULL");
- LOG("d_add", entry);
-#endif
- if(ininame) {
- if(ininame->len != entry->d_len) {
- printk("VFS: d_add with wrong string length");
- entry->d_len = ininame->len; /* kludge */
- }
- memcpy(entry->d_name, ininame->name, ininame->len);
- entry->d_name[ininame->len] = '\0';
- } else {
- dummy.name = entry->d_name;
- dummy.len = entry->d_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;
+ /*
+ * If not, just drop the dentry and let dput
+ * pick up the tab..
+ */
+ d_drop(dentry);
}
-blocking struct dentry * d_entry(struct dentry * parent,
- struct qstr * name,
- struct inode * inode)
+void d_add(struct dentry * entry, struct inode * inode)
{
- 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;
+ d_insert_to_parent(entry, entry->d_parent);
+ d_instantiate(entry, inode);
}
-blocking void d_entry_preliminary(struct dentry * parent,
- struct qstr * name,
- unsigned long ino)
+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);
+ int len = newname->len;
+ int hash = newname->hash;
+ char *name = (char *) entry->d_name.name;
- 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;
}
+ memcpy(name, newname->name, len);
+ name[len] = 0;
+ entry->d_name.len = len;
+ entry->d_name.hash = hash;
}
-blocking void d_move(struct dentry * entry, struct inode * newdir,
- struct qstr * newname, struct qstr * newapp)
+void d_move(struct dentry * dentry, struct dentry * newdir, struct qstr * newname)
{
- struct ddir tmp;
- struct dentry * new;
- struct inode * inode;
- int len;
- int flags;
-
- if(!entry)
+ if (!dentry)
return;
- inode = entry->u.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 0
-printk("d_move %p '%s' -> '%s%s' dent_count=%d\n", inode, entry->d_name,
- newname->name, newapp ? newapp->name : "", inode->i_dent_count);
-#endif
- if(flags & D_ZOMBIE) {
- printk("VFS: moving zombie entry\n");
- }
- if(flags & D_DIR) {
- struct ddir * ddir = d_dir(entry);
-
- memcpy(&tmp, ddir, sizeof(struct ddir));
- /* Simulate empty dir for d_del(). */
- memset(ddir, 0, sizeof(struct ddir));
- }
- len = newname->len;
- if(newapp) {
- len += newapp->len;
- flags |= D_BASKET;
- } else
- flags &= ~D_BASKET;
- new = d_alloc(newdir->i_dentry, len, flags & D_DIR);
- memcpy(new->d_name, newname->name, newname->len);
- if(newapp)
- memcpy(new->d_name+newname->len, newapp->name, newapp->len);
- new->d_name[len] = '\0';
- d_del(entry, D_NO_CLEAR_INODE);
- d_add(new, inode, NULL, flags & (D_DIR|D_BASKET));
- if(flags & D_DIR) {
- struct ddir * ddir = d_dir(new);
+ if (!dentry->d_inode)
+ printk("VFS: moving negative dcache entry\n");
- memcpy(ddir, &tmp, sizeof(struct ddir));
- }
+ d_remove_from_parent(dentry, dentry->d_parent);
+ alloc_new_name(dentry, newname);
+ dentry->d_parent = newdir;
+ d_insert_to_parent(dentry, 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++;
}
- memcpy(buf, entry->d_name, entry->d_len);
- return len + entry->d_len;
+ memcpy(buf, entry->d_name.name, entry->d_name.len);
+ return len + entry->d_name.len;
}
}
-struct dentry * d_basket(struct dentry * dir_entry)
+void dcache_init(void)
{
- 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, entry->d_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_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;
+ int i;
+ struct list_head *d = dentry_hashtable;
+
+ i = D_HASHSIZE;
+ do {
+ INIT_LIST_HEAD(d);
+ d++;
+ i--;
+ } while (i);
}
diff --git a/fs/devices.c b/fs/devices.c
index d3b1d6846..26f668e7f 100644
--- a/fs/devices.c
+++ b/fs/devices.c
@@ -209,9 +209,7 @@ int check_disk_change(kdev_t dev)
printk(KERN_DEBUG "VFS: Disk change detected on device %s\n",
kdevname(dev));
- for (i=0 ; i<NR_SUPER ; i++)
- if (super_blocks[i].s_dev == dev)
- put_super(super_blocks[i].s_dev);
+
invalidate_inodes(dev);
invalidate_buffers(dev);
diff --git a/fs/dquot.c b/fs/dquot.c
index 59d2112d9..b19c939be 100644
--- a/fs/dquot.c
+++ b/fs/dquot.c
@@ -13,7 +13,7 @@
* diskquota system. This implementation is not based on any BSD
* kernel sourcecode.
*
- * Version: $Id: dquot.c,v 1.11 1997/01/06 06:53:02 davem Exp $
+ * Version: $Id: dquot.c,v 1.2 1997/06/17 13:25:58 ralf Exp $
*
* Author: Marco van Wieringen <mvw@mcs.ow.nl> <mvw@tnix.net>
*
@@ -227,7 +227,7 @@ static void write_dquot(struct dquot *dquot)
lock_dquot(dquot);
down(&dquot->dq_mnt->mnt_sem);
if (filp->f_op->llseek) {
- if (filp->f_op->llseek(filp->f_inode, filp,
+ if (filp->f_op->llseek(filp->f_dentry->d_inode, filp,
dqoff(dquot->dq_id), 0) != dqoff(dquot->dq_id)) {
up(&dquot->dq_mnt->mnt_sem);
unlock_dquot(dquot);
@@ -238,10 +238,9 @@ static void write_dquot(struct dquot *dquot)
fs = get_fs();
set_fs(KERNEL_DS);
- if (filp->f_op->write(filp->f_inode, filp,
+ if (filp->f_op->write(filp->f_dentry->d_inode, filp,
(char *)&dquot->dq_dqb, sizeof(struct dqblk)) == sizeof(struct dqblk))
dquot->dq_flags &= ~DQ_MOD;
- /* inode->i_status |= ST_MODIFIED is willingly *not* done here */
up(&dquot->dq_mnt->mnt_sem);
set_fs(fs);
@@ -260,7 +259,7 @@ static void read_dquot(struct dquot *dquot)
lock_dquot(dquot);
down(&dquot->dq_mnt->mnt_sem);
if (filp->f_op->llseek) {
- if (filp->f_op->llseek(filp->f_inode, filp,
+ if (filp->f_op->llseek(filp->f_dentry->d_inode, filp,
dqoff(dquot->dq_id), 0) != dqoff(dquot->dq_id)) {
up(&dquot->dq_mnt->mnt_sem);
unlock_dquot(dquot);
@@ -270,7 +269,7 @@ static void read_dquot(struct dquot *dquot)
filp->f_pos = dqoff(dquot->dq_id);
fs = get_fs();
set_fs(KERNEL_DS);
- filp->f_op->read(filp->f_inode, filp, (char *)&dquot->dq_dqb, sizeof(struct dqblk));
+ filp->f_op->read(filp->f_dentry->d_inode, filp, (char *)&dquot->dq_dqb, sizeof(struct dqblk));
up(&dquot->dq_mnt->mnt_sem);
set_fs(fs);
if (dquot->dq_bhardlimit == 0 && dquot->dq_bsoftlimit == 0 &&
@@ -947,39 +946,53 @@ int quota_off(kdev_t dev, short type)
int quota_on(kdev_t dev, short type, char *path)
{
- struct file *filp = (struct file *)NULL;
+ struct file *filp = NULL;
+ struct dentry *dentry;
struct vfsmount *vfsmnt;
struct inode *inode;
struct dquot *dquot;
char *tmp;
int error;
- if ((vfsmnt = lookup_vfsmnt(dev)) == (struct vfsmount *)NULL)
- return(-ENODEV);
- if (vfsmnt->mnt_quotas[type] != (struct file *)NULL)
- return(-EBUSY);
- if ((error = getname(path, &tmp)) != 0)
- return(error);
- error = open_namei(tmp, O_RDWR, 0600, &inode, 0);
+ vfsmnt = lookup_vfsmnt(dev);
+ if (vfsmnt == NULL)
+ return -ENODEV;
+
+ if (vfsmnt->mnt_quotas[type] != NULL)
+ return -EBUSY;
+
+ tmp = getname(path);
+ error = PTR_ERR(tmp);
+ if (IS_ERR(tmp))
+ return error;
+
+ dentry = open_namei(tmp, O_RDWR, 0600);
putname(tmp);
- if (error)
- return(error);
+
+ error = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
+ return error;
+ inode = dentry->d_inode;
+
if (!S_ISREG(inode->i_mode)) {
- iput(inode);
- return(-EACCES);
+ dput(dentry);
+ return -EACCES;
}
- if ((filp = get_empty_filp()) != (struct file *)NULL) {
+
+ filp = get_empty_filp();
+ if (filp != NULL) {
filp->f_mode = (O_RDWR + 1) & O_ACCMODE;
filp->f_flags = O_RDWR;
- filp->f_inode = inode;
+ filp->f_dentry = dentry;
filp->f_pos = 0;
filp->f_reada = 0;
filp->f_op = inode->i_op->default_file_ops;
if (filp->f_op->read || filp->f_op->write) {
- if ((error = get_write_access(inode)) == 0) {
+ error = get_write_access(inode);
+ if (!error) {
if (filp->f_op && filp->f_op->open)
error = filp->f_op->open(inode, filp);
- if (error == 0) {
+ if (!error) {
vfsmnt->mnt_quotas[type] = filp;
dquot = dqget(dev, 0, type);
vfsmnt->mnt_iexp[type] = (dquot) ? dquot->dq_itime : MAX_IQ_TIME;
@@ -987,7 +1000,7 @@ int quota_on(kdev_t dev, short type, char *path)
dqput(dquot);
vfsmnt->mnt_sb->dq_op = &dquot_operations;
add_dquot_ref(dev, type);
- return(0);
+ return 0;
}
put_write_access(inode);
}
@@ -996,8 +1009,8 @@ int quota_on(kdev_t dev, short type, char *path)
put_filp(filp);
} else
error = -EMFILE;
- iput(inode);
- return(error);
+ dput(dentry);
+ return error;
}
/*
@@ -1009,7 +1022,6 @@ int quota_on(kdev_t dev, short type, char *path)
asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr)
{
int cmds = 0, type = 0, flags = 0;
- struct inode *ino;
kdev_t dev;
int ret = -EINVAL;
@@ -1035,19 +1047,22 @@ asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr)
}
ret = -EINVAL;
- if (special == (char *)NULL && (cmds == Q_SYNC || cmds == Q_GETSTATS))
- dev = 0;
- else {
- int error = namei(NAM_FOLLOW_LINK, special, &ino);
- if(error)
+ dev = 0;
+ if (special != NULL || (cmds != Q_SYNC && cmds != Q_GETSTATS)) {
+ mode_t mode;
+ struct dentry * dentry;
+
+ dentry = namei(special);
+ if (IS_ERR(dentry))
goto out;
- dev = ino->i_rdev;
+
+ dev = dentry->d_inode->i_rdev;
+ mode = dentry->d_inode->i_mode;
+ dput(dentry);
+
ret = -ENOTBLK;
- if (!S_ISBLK(ino->i_mode)) {
- iput(ino);
+ if (!S_ISBLK(mode))
goto out;
- }
- iput(ino);
}
ret = -EINVAL;
diff --git a/fs/exec.c b/fs/exec.c
index 74d228019..6facaf283 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -88,6 +88,10 @@ __initfunc(void binfmt_setup(void))
init_aout_binfmt();
#endif
+#ifdef CONFIG_BINFMT_AOUT32
+ init_aout32_binfmt();
+#endif
+
#ifdef CONFIG_BINFMT_JAVA
init_java_binfmt();
#endif
@@ -134,22 +138,24 @@ int unregister_binfmt(struct linux_binfmt * fmt)
}
#endif /* CONFIG_MODULES */
-int open_inode(struct inode * inode, int mode)
+int open_dentry(struct dentry * dentry, int mode)
{
int fd;
+ struct inode * inode = dentry->d_inode;
if (!inode->i_op || !inode->i_op->default_file_ops)
return -EINVAL;
fd = get_unused_fd();
if (fd >= 0) {
struct file * f = get_empty_filp();
+
if (!f) {
put_unused_fd(fd);
return -ENFILE;
}
f->f_flags = mode;
f->f_mode = (mode+1) & O_ACCMODE;
- f->f_inode = inode;
+ f->f_dentry = dentry;
f->f_pos = 0;
f->f_reada = 0;
f->f_op = inode->i_op->default_file_ops;
@@ -162,7 +168,7 @@ int open_inode(struct inode * inode, int mode)
}
}
current->files->fd[fd] = f;
- atomic_inc(&inode->i_count);
+ dget(dentry);
}
return fd;
}
@@ -186,7 +192,7 @@ asmlinkage int sys_uselib(const char * library)
goto out;
file = current->files->fd[fd];
retval = -ENOEXEC;
- if (file && file->f_inode && file->f_op && file->f_op->read) {
+ if (file && file->f_dentry && file->f_op && file->f_op->read) {
for (fmt = formats ; fmt ; fmt = fmt->next) {
int (*fn)(int) = fmt->load_shlib;
if (!fn)
@@ -320,7 +326,7 @@ unsigned long setup_arg_pages(unsigned long p, struct linux_binprm * bprm)
mpnt->vm_flags = VM_STACK_FLAGS;
mpnt->vm_ops = NULL;
mpnt->vm_offset = 0;
- mpnt->vm_inode = NULL;
+ mpnt->vm_dentry = NULL;
mpnt->vm_pte = 0;
insert_vm_struct(current->mm, mpnt);
current->mm->total_vm = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT;
@@ -341,25 +347,18 @@ unsigned long setup_arg_pages(unsigned long p, struct linux_binprm * bprm)
* that aren't on a block boundary, and for files on filesystems
* without bmap support.
*/
-int read_exec(struct inode *inode, unsigned long offset,
+int read_exec(struct dentry *dentry, unsigned long offset,
char * addr, unsigned long count, int to_kmem)
{
struct file file;
+ struct inode * inode = dentry->d_inode;
int result = -ENOEXEC;
if (!inode->i_op || !inode->i_op->default_file_ops)
goto end_readexec;
- file.f_mode = 1;
- 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 end_readexec;
- if (!file.f_op || !file.f_op->read)
+ if (init_private_file(&file, dentry, 1))
+ goto end_readexec;
+ if (!file.f_op->read)
goto close_readexec;
if (file.f_op->llseek) {
if (file.f_op->llseek(inode,&file,offset,0) != offset)
@@ -481,7 +480,7 @@ void flush_old_exec(struct linux_binprm * bprm)
flush_thread();
if (bprm->e_uid != current->euid || bprm->e_gid != current->egid ||
- permission(bprm->inode,MAY_READ))
+ permission(bprm->dentry->d_inode,MAY_READ))
current->dumpable = 0;
flush_old_signals(current->sig);
@@ -496,20 +495,21 @@ int prepare_binprm(struct linux_binprm *bprm)
{
int mode;
int retval,id_change;
+ struct inode * inode = bprm->dentry->d_inode;
- mode = bprm->inode->i_mode;
+ mode = inode->i_mode;
if (!S_ISREG(mode)) /* must be regular file */
return -EACCES;
if (!(mode & 0111)) /* with at least _one_ execute bit set */
return -EACCES;
- if (IS_NOEXEC(bprm->inode)) /* FS mustn't be mounted noexec */
+ if (IS_NOEXEC(inode)) /* FS mustn't be mounted noexec */
return -EACCES;
- if (!bprm->inode->i_sb)
+ if (!inode->i_sb)
return -EACCES;
- if ((retval = permission(bprm->inode, MAY_EXEC)) != 0)
+ if ((retval = permission(inode, MAY_EXEC)) != 0)
return retval;
/* better not execute files which are being written to */
- if (bprm->inode->i_writecount > 0)
+ if (inode->i_writecount > 0)
return -ETXTBSY;
bprm->e_uid = current->euid;
@@ -518,7 +518,7 @@ int prepare_binprm(struct linux_binprm *bprm)
/* Set-uid? */
if (mode & S_ISUID) {
- bprm->e_uid = bprm->inode->i_uid;
+ bprm->e_uid = inode->i_uid;
if (bprm->e_uid != current->euid)
id_change = 1;
}
@@ -530,7 +530,7 @@ int prepare_binprm(struct linux_binprm *bprm)
* executable.
*/
if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
- bprm->e_gid = bprm->inode->i_gid;
+ bprm->e_gid = inode->i_gid;
if (!in_group_p(bprm->e_gid))
id_change = 1;
}
@@ -539,7 +539,7 @@ int prepare_binprm(struct linux_binprm *bprm)
/* We can't suid-execute if we're sharing parts of the executable */
/* or if we're being traced (or if suid execs are not allowed) */
/* (current->mm->count > 1 is ok, as we'll get a new mm anyway) */
- if (IS_NOSUID(bprm->inode)
+ if (IS_NOSUID(inode)
|| (current->flags & PF_PTRACED)
|| (current->fs->count > 1)
|| (atomic_read(&current->sig->count) > 1)
@@ -550,7 +550,7 @@ int prepare_binprm(struct linux_binprm *bprm)
}
memset(bprm->buf,0,sizeof(bprm->buf));
- return read_exec(bprm->inode,0,bprm->buf,128,1);
+ return read_exec(bprm->dentry,0,bprm->buf,128,1);
}
void remove_arg_zero(struct linux_binprm *bprm)
@@ -585,16 +585,19 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs)
(eh->fh.f_flags & 0x3000) == 0x3000)
{
char * dynloader[] = { "/sbin/loader" };
- iput(bprm->inode);
- bprm->dont_iput = 1;
+ struct dentry * dentry;
+
+ dput(bprm->dentry);
+ bprm->dentry = NULL;
remove_arg_zero(bprm);
bprm->p = copy_strings(1, dynloader, bprm->page, bprm->p, 2);
bprm->argc++;
bprm->loader = bprm->p;
- retval = open_namei(dynloader[0], 0, 0, &bprm->inode, NULL);
- if (retval)
+ dentry = open_namei(dynloader[0], 0, 0);
+ retval = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
return retval;
- bprm->dont_iput = 0;
+ bprm->dentry = dentry;
retval = prepare_binprm(bprm);
if (retval<0)
return retval;
@@ -610,15 +613,15 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs)
continue;
retval = fn(bprm, regs);
if (retval >= 0) {
- if(!bprm->dont_iput)
- iput(bprm->inode);
- bprm->dont_iput=1;
+ if (bprm->dentry)
+ dput(bprm->dentry);
+ bprm->dentry = NULL;
current->did_exec = 1;
return retval;
}
if (retval != -ENOEXEC)
break;
- if (bprm->dont_iput) /* We don't have the inode anymore*/
+ if (!bprm->dentry) /* We don't have the dentry anymore */
return retval;
}
if (retval != -ENOEXEC) {
@@ -647,29 +650,38 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs)
int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs)
{
struct linux_binprm bprm;
+ struct dentry * dentry;
int retval;
int i;
bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *);
for (i=0 ; i<MAX_ARG_PAGES ; i++) /* clear page-table */
bprm.page[i] = 0;
- retval = open_namei(filename, 0, 0, &bprm.inode, NULL);
- if (retval)
+
+ dentry = open_namei(filename, 0, 0);
+ retval = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
return retval;
+
+ bprm.dentry = dentry;
bprm.filename = filename;
bprm.sh_bang = 0;
bprm.java = 0;
bprm.loader = 0;
bprm.exec = 0;
- bprm.dont_iput = 0;
- if ((bprm.argc = count(argv)) < 0)
+ if ((bprm.argc = count(argv)) < 0) {
+ dput(dentry);
return bprm.argc;
- if ((bprm.envc = count(envp)) < 0)
+ }
+
+ if ((bprm.envc = count(envp)) < 0) {
+ dput(dentry);
return bprm.envc;
+ }
retval = prepare_binprm(&bprm);
- if(retval>=0) {
+ if (retval >= 0) {
bprm.p = copy_strings(1, &bprm.filename, bprm.page, bprm.p, 2);
bprm.exec = bprm.p;
bprm.p = copy_strings(bprm.envc,envp,bprm.page,bprm.p,0);
@@ -678,16 +690,18 @@ int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs
retval = -E2BIG;
}
- if(retval>=0)
+ if (retval >= 0)
retval = search_binary_handler(&bprm,regs);
- if(retval>=0)
+ if (retval >= 0)
/* execve success */
return retval;
/* Something went wrong, return the inode and free the argument pages*/
- if(!bprm.dont_iput)
- iput(bprm.inode);
+ if (bprm.dentry)
+ dput(bprm.dentry);
+
for (i=0 ; i<MAX_ARG_PAGES ; i++)
free_page(bprm.page[i]);
- return(retval);
+
+ return retval;
}
diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c
index 4d2b561ee..7e159e7d2 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 d9b1957e3..b75acdef5 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 */
@@ -211,9 +212,6 @@ revalidate:
offset = 0;
brelse (bh);
}
- if (DO_UPDATE_ATIME(inode)) {
- inode->i_atime = CURRENT_TIME;
- inode->i_dirt = 1;
- }
+ UPDATE_ATIME(inode);
return 0;
}
diff --git a/fs/ext2/file.c b/fs/ext2/file.c
index 1627f5cee..d632133f1 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 */
@@ -121,7 +122,7 @@ static inline void remove_suid(struct inode *inode)
mode &= inode->i_mode;
if (mode && !suser()) {
inode->i_mode &= ~mode;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
}
}
@@ -250,7 +251,7 @@ static long ext2_file_write (struct inode * inode, struct file * filp,
inode->u.ext2_i.i_osync--;
inode->i_ctime = inode->i_mtime = CURRENT_TIME;
filp->f_pos = pos;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
return written;
}
diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c
index a486679f9..bc16722e4 100644
--- a/fs/ext2/ialloc.c
+++ b/fs/ext2/ialloc.c
@@ -154,8 +154,26 @@ static int load_inode_bitmap (struct super_block * sb,
return 0;
}
+/*
+ * NOTE! When we get the inode, we're the only people
+ * that have access to it, and as such there are no
+ * race conditions we have to worry about. The inode
+ * is not on the hash-lists, and it cannot be reached
+ * through the filesystem because the directory entry
+ * has been deleted earlier.
+ *
+ * HOWEVER: we must make sure that we get no aliases,
+ * which means that we have to call "clear_inode()"
+ * _before_ we mark the inode not in use in the inode
+ * bitmaps. Otherwise a newly created file might use
+ * the same inode number (not actually the same pointer
+ * though), and then we'd have two inodes sharing the
+ * same inode number and space on the harddisk.
+ */
void ext2_free_inode (struct inode * inode)
{
+ int is_directory;
+ unsigned long ino;
struct super_block * sb;
struct buffer_head * bh;
struct buffer_head * bh2;
@@ -171,9 +189,8 @@ void ext2_free_inode (struct inode * inode)
printk ("ext2_free_inode: inode has no device\n");
return;
}
- if (atomic_read(&inode->i_count) > 1) {
- printk ("ext2_free_inode: inode has count=%d\n",
- atomic_read(&inode->i_count));
+ if (inode->i_count > 1) {
+ printk ("ext2_free_inode: inode has count=%d\n", inode->i_count);
return;
}
if (inode->i_nlink) {
@@ -186,47 +203,53 @@ void ext2_free_inode (struct inode * inode)
return;
}
- ext2_debug ("freeing inode %lu\n", inode->i_ino);
+ ino = inode->i_ino;
+ ext2_debug ("freeing inode %lu\n", ino);
sb = inode->i_sb;
lock_super (sb);
- if (inode->i_ino < EXT2_FIRST_INO(sb) ||
- inode->i_ino > le32_to_cpu(sb->u.ext2_sb.s_es->s_inodes_count)) {
+ if (ino < EXT2_FIRST_INO(sb) ||
+ ino > le32_to_cpu(sb->u.ext2_sb.s_es->s_inodes_count)) {
ext2_error (sb, "free_inode",
"reserved inode or nonexistent inode");
unlock_super (sb);
return;
}
es = sb->u.ext2_sb.s_es;
- block_group = (inode->i_ino - 1) / EXT2_INODES_PER_GROUP(sb);
- bit = (inode->i_ino - 1) % EXT2_INODES_PER_GROUP(sb);
+ block_group = (ino - 1) / EXT2_INODES_PER_GROUP(sb);
+ bit = (ino - 1) % EXT2_INODES_PER_GROUP(sb);
bitmap_nr = load_inode_bitmap (sb, block_group);
bh = sb->u.ext2_sb.s_inode_bitmap[bitmap_nr];
+
+ is_directory = S_ISDIR(inode->i_mode);
+
+ /* Do this BEFORE marking the inode not in use */
+ if (sb->dq_op)
+ sb->dq_op->free_inode (inode, 1);
+ clear_inode (inode);
+
+ /* Ok, now we can actually update the inode bitmaps.. */
if (!ext2_clear_bit (bit, bh->b_data))
ext2_warning (sb, "ext2_free_inode",
- "bit already cleared for inode %lu", inode->i_ino);
+ "bit already cleared for inode %lu", ino);
else {
gdp = get_group_desc (sb, block_group, &bh2);
gdp->bg_free_inodes_count =
cpu_to_le16(le16_to_cpu(gdp->bg_free_inodes_count) + 1);
- if (S_ISDIR(inode->i_mode))
+ if (is_directory)
gdp->bg_used_dirs_count =
cpu_to_le16(le16_to_cpu(gdp->bg_used_dirs_count) - 1);
mark_buffer_dirty(bh2, 1);
es->s_free_inodes_count =
cpu_to_le32(le32_to_cpu(es->s_free_inodes_count) + 1);
mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
- inode->i_dirt = 0;
}
mark_buffer_dirty(bh, 1);
if (sb->s_flags & MS_SYNCHRONOUS) {
ll_rw_block (WRITE, 1, &bh);
wait_on_buffer (bh);
}
- if (sb->dq_op)
- sb->dq_op->free_inode (inode, 1);
sb->s_dirt = 1;
- clear_inode (inode);
unlock_super (sb);
}
@@ -240,7 +263,7 @@ static void inc_inode_version (struct inode * inode,
int mode)
{
inode->u.ext2_i.i_version++;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
return;
}
@@ -404,7 +427,6 @@ repeat:
sb->s_dirt = 1;
inode->i_mode = mode;
inode->i_sb = sb;
- atomic_set(&inode->i_count, 1);
inode->i_nlink = 1;
inode->i_dev = sb->s_dev;
inode->i_uid = current->fsuid;
@@ -416,7 +438,7 @@ repeat:
mode |= S_ISGID;
} else
inode->i_gid = current->fsgid;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
inode->i_ino = j;
inode->i_blksize = PAGE_SIZE; /* This is the optimal IO size (for stat), not the fs block size */
inode->i_blocks = 0;
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
index f2dbff2d1..0fa14cfe1 100644
--- a/fs/ext2/inode.c
+++ b/fs/ext2/inode.c
@@ -31,14 +31,24 @@
static int ext2_update_inode(struct inode * inode, int do_sync);
+/*
+ * Called at each iput()
+ */
void ext2_put_inode (struct inode * inode)
{
ext2_discard_prealloc (inode);
- if (inode->i_nlink || inode->i_ino == EXT2_ACL_IDX_INO ||
+}
+
+/*
+ * Called at the last iput() if i_nlink is zero.
+ */
+void ext2_delete_inode (struct inode * inode)
+{
+ if (inode->i_ino == EXT2_ACL_IDX_INO ||
inode->i_ino == EXT2_ACL_DATA_INO)
return;
inode->u.ext2_i.i_dtime = CURRENT_TIME;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
ext2_update_inode(inode, IS_SYNC(inode));
inode->i_size = 0;
if (inode->i_blocks)
@@ -248,7 +258,7 @@ repeat:
if (IS_SYNC(inode) || inode->u.ext2_i.i_osync)
ext2_sync_inode (inode);
else
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
return result;
}
@@ -322,7 +332,7 @@ repeat:
}
inode->i_ctime = CURRENT_TIME;
inode->i_blocks += blocks;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
inode->u.ext2_i.i_next_alloc_block = new_block;
inode->u.ext2_i.i_next_alloc_goal = tmp;
brelse (bh);
@@ -591,7 +601,6 @@ static int ext2_update_inode(struct inode * inode, int do_sync)
else for (block = 0; block < EXT2_N_BLOCKS; block++)
raw_inode->i_block[block] = cpu_to_le32(inode->u.ext2_i.i_data[block]);
mark_buffer_dirty(bh, 1);
- inode->i_dirt = 0;
if (do_sync) {
ll_rw_block (WRITE, 1, &bh);
wait_on_buffer (bh);
@@ -671,7 +680,7 @@ int ext2_notify_change(struct inode *inode, struct iattr *iattr)
inode->i_flags &= ~S_IMMUTABLE;
inode->u.ext2_i.i_flags &= ~EXT2_IMMUTABLE_FL;
}
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
return 0;
}
diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c
index 387600bbf..c0514c01e 100644
--- a/fs/ext2/ioctl.c
+++ b/fs/ext2/ioctl.c
@@ -62,7 +62,7 @@ int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
else
inode->i_flags &= ~MS_NOATIME;
inode->i_ctime = CURRENT_TIME;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
return 0;
case EXT2_IOC_GETVERSION:
return put_user(inode->u.ext2_i.i_version, (int *) arg);
@@ -74,7 +74,7 @@ int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
if (get_user(inode->u.ext2_i.i_version, (int *) arg))
return -EFAULT;
inode->i_ctime = CURRENT_TIME;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
return 0;
default:
return -ENOTTY;
diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c
index 421393581..2a7c42bff 100644
--- a/fs/ext2/namei.c
+++ b/fs/ext2/namei.c
@@ -27,6 +27,7 @@
#include <linux/string.h>
#include <linux/locks.h>
+
/*
* define how far ahead to read directories while searching them.
*/
@@ -154,36 +155,26 @@ failure:
return NULL;
}
-int ext2_lookup (struct inode * dir, const char * name, int len,
- struct inode ** result)
+int ext2_lookup(struct inode * dir, struct dentry *dentry)
{
- unsigned long ino;
+ struct inode * inode;
struct ext2_dir_entry * de;
struct buffer_head * bh;
- *result = NULL;
- if (!dir)
- return -ENOENT;
- if (!S_ISDIR(dir->i_mode)) {
- iput (dir);
- return -ENOTDIR;
- }
- if (len > EXT2_NAME_LEN) {
- iput (dir);
+ if (dentry->d_name.len > EXT2_NAME_LEN)
return -ENAMETOOLONG;
+
+ bh = ext2_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de);
+ inode = NULL;
+ if (bh) {
+ unsigned long ino = le32_to_cpu(de->inode);
+ brelse (bh);
+ inode = iget(dir->i_sb, ino);
+
+ if (!inode)
+ return -EACCES;
}
- ino = dir->i_version;
- if (!(bh = ext2_find_entry (dir, name, len, &de))) {
- iput (dir);
- return -ENOENT;
- }
- ino = le32_to_cpu(de->inode);
- brelse (bh);
- if (!(*result = iget (dir->i_sb, ino))) {
- iput (dir);
- return -EACCES;
- }
- iput (dir);
+ d_add(dentry, inode);
return 0;
}
@@ -256,7 +247,7 @@ static struct buffer_head * ext2_add_entry (struct inode * dir,
de->inode = le32_to_cpu(0);
de->rec_len = le16_to_cpu(sb->s_blocksize);
dir->i_size = offset + sb->s_blocksize;
- dir->i_dirt = 1;
+ mark_inode_dirty(dir);
} else {
ext2_debug ("skipping to next block\n");
@@ -301,7 +292,7 @@ static struct buffer_head * ext2_add_entry (struct inode * dir,
* and/or different from the directory change time.
*/
dir->i_mtime = dir->i_ctime = CURRENT_TIME;
- dir->i_dirt = 1;
+ mark_inode_dirty(dir);
dir->i_version = ++event;
mark_buffer_dirty(bh, 1);
*res_dir = de;
@@ -347,31 +338,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 negative - it has no inode.
+ *
+ * If the create succeeds, we 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);
+ mark_inode_dirty(inode);
+ bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
if (!bh) {
inode->i_nlink--;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
iput (inode);
- iput (dir);
return err;
}
de->inode = cpu_to_le32(inode->i_ino);
@@ -382,13 +377,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);
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 +391,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;
@@ -433,13 +418,12 @@ int ext2_mknod (struct inode * dir, const char * name, int len, int mode,
init_fifo(inode);
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);
+ mark_inode_dirty(inode);
+ 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);
+ mark_inode_dirty(inode);
+ iput(inode);
return err;
}
de->inode = cpu_to_le32(inode->i_ino);
@@ -449,47 +433,34 @@ 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);
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;
+ mark_inode_dirty(inode);
iput (inode);
return err;
}
@@ -510,12 +481,11 @@ int ext2_mkdir (struct inode * dir, const char * name, int len, int mode)
inode->i_mode = S_IFDIR | (mode & (S_IRWXUGO|S_ISVTX) & ~current->fs->umask);
if (dir->i_mode & S_ISGID)
inode->i_mode |= S_ISGID;
- inode->i_dirt = 1;
- bh = ext2_add_entry (dir, name, len, &de, &err);
+ mark_inode_dirty(inode);
+ 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;
+ mark_inode_dirty(inode);
iput (inode);
return err;
}
@@ -527,9 +497,8 @@ int ext2_mkdir (struct inode * dir, const char * name, int len, int mode)
wait_on_buffer (bh);
}
dir->i_nlink++;
- dir->i_dirt = 1;
- iput (dir);
- iput (inode);
+ mark_inode_dirty(dir);
+ d_instantiate(dentry, inode);
brelse (bh);
return 0;
}
@@ -593,58 +562,53 @@ 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;
else if (le32_to_cpu(de->inode) != inode->i_ino)
retval = -ENOENT;
else {
- if (atomic_read(&inode->i_count) > 1) {
+ if (inode->i_count > 1) {
/*
* Are we deleting the last instance of a busy directory?
* Better clean up if so.
@@ -671,56 +635,51 @@ repeat:
(int) inode->i_nlink);
inode->i_version = ++event;
inode->i_nlink = 0;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
dir->i_nlink--;
inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
- dir->i_dirt = 1;
+ mark_inode_dirty(dir);
+ 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",
@@ -737,20 +696,19 @@ repeat:
wait_on_buffer (bh);
}
dir->i_ctime = dir->i_mtime = CURRENT_TIME;
- dir->i_dirt = 1;
+ mark_inode_dirty(dir);
inode->i_nlink--;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
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 +719,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,9 +732,8 @@ 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;
+ mark_inode_dirty(inode);
iput (inode);
return err;
}
@@ -797,23 +753,13 @@ int ext2_symlink (struct inode * dir, const char * name, int len,
brelse (name_block);
}
inode->i_size = i;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
- 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;
+ mark_inode_dirty(inode);
iput (inode);
- iput (dir);
return err;
}
de->inode = cpu_to_le32(inode->i_ino);
@@ -824,47 +770,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);
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,35 +801,33 @@ 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;
+ mark_inode_dirty(inode);
+ inode->i_count++;
+ d_instantiate(dentry, inode);
return 0;
}
-static int subdir (struct inode * new_inode, struct inode * old_inode)
+/*
+ * Trivially implemented using the dcache structure
+ */
+static int subdir (struct dentry * new_dentry, struct dentry * old_dentry)
{
- int ino;
int result;
- atomic_inc(&new_inode->i_count);
result = 0;
for (;;) {
- if (new_inode == old_inode) {
- result = 1;
- break;
+ if (new_dentry != old_dentry) {
+ struct dentry * parent = new_dentry->d_parent;
+ if (parent == new_dentry)
+ break;
+ new_dentry = parent;
+ continue;
}
- if (new_inode->i_dev != old_inode->i_dev)
- break;
- ino = new_inode->i_ino;
- if (ext2_lookup (new_inode, "..", 2, &new_inode))
- break;
- if (new_inode->i_ino == ino)
- break;
+ result = 1;
+ break;
}
- iput (new_inode);
return result;
}
@@ -908,10 +835,6 @@ static int subdir (struct inode * new_inode, struct inode * old_inode)
((struct ext2_dir_entry *) ((char *) buffer + \
le16_to_cpu(((struct ext2_dir_entry *) buffer)->rec_len)))->inode
-#define PARENT_NAME(buffer) \
- ((struct ext2_dir_entry *) ((char *) buffer + \
- le16_to_cpu(((struct ext2_dir_entry *) buffer)->rec_len)))->name
-
/*
* rename uses retrying to avoid race-conditions: at least they should be
* minimal.
@@ -923,43 +846,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 +874,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;
@@ -987,13 +895,13 @@ start_up:
if (!S_ISDIR(old_inode->i_mode))
goto end_rename;
retval = -EINVAL;
- if (subdir (new_dir, old_inode))
+ if (subdir(new_dentry, old_dentry))
goto end_rename;
retval = -ENOTEMPTY;
if (!empty_dir (new_inode))
goto end_rename;
retval = -EBUSY;
- if (atomic_read(&new_inode->i_count) > 1)
+ if (new_inode->i_count > 1)
goto end_rename;
}
retval = -EPERM;
@@ -1006,7 +914,7 @@ start_up:
if (new_inode && !S_ISDIR(new_inode->i_mode))
goto end_rename;
retval = -EINVAL;
- if (subdir (new_dir, old_inode))
+ if (subdir(new_dentry, old_dentry))
goto end_rename;
dir_bh = ext2_bread (old_inode, 0, 0, &retval);
if (!dir_bh)
@@ -1018,48 +926,37 @@ 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--;
new_inode->i_ctime = CURRENT_TIME;
- new_inode->i_dirt = 1;
+ mark_inode_dirty(new_inode);
}
old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
- old_dir->i_dirt = 1;
+ mark_inode_dirty(old_dir);
if (dir_bh) {
PARENT_INO(dir_bh->b_data) = le32_to_cpu(new_dir->i_ino);
mark_buffer_dirty(dir_bh, 1);
old_dir->i_nlink--;
- old_dir->i_dirt = 1;
+ mark_inode_dirty(old_dir);
if (new_inode) {
new_inode->i_nlink--;
- new_inode->i_dirt = 1;
+ mark_inode_dirty(new_inode);
} else {
new_dir->i_nlink++;
- new_dir->i_dirt = 1;
+ mark_inode_dirty(new_dir);
}
}
mark_buffer_dirty(old_bh, 1);
@@ -1072,15 +969,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 +994,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 635a45692..1adc82185 100644
--- a/fs/ext2/super.c
+++ b/fs/ext2/super.c
@@ -133,9 +133,10 @@ void ext2_put_super (struct super_block * sb)
static struct super_operations ext2_sops = {
ext2_read_inode,
- NULL,
ext2_write_inode,
ext2_put_inode,
+ ext2_delete_inode,
+ NULL,
ext2_put_super,
ext2_write_super,
ext2_statfs,
@@ -632,7 +633,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])
@@ -761,7 +763,7 @@ void cleanup_module(void)
#endif
-void ext2_statfs (struct super_block * sb, struct statfs * buf, int bufsiz)
+int ext2_statfs (struct super_block * sb, struct statfs * buf, int bufsiz)
{
unsigned long overhead;
unsigned long overhead_per_group;
@@ -792,5 +794,5 @@ void ext2_statfs (struct super_block * sb, struct statfs * buf, int bufsiz)
tmp.f_files = le32_to_cpu(sb->u.ext2_sb.s_es->s_inodes_count);
tmp.f_ffree = ext2_count_free_inodes (sb);
tmp.f_namelen = EXT2_NAME_LEN;
- copy_to_user(buf, &tmp, bufsiz);
+ return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
}
diff --git a/fs/ext2/symlink.c b/fs/ext2/symlink.c
index 4d5a5cada..781f9165d 100644
--- a/fs/ext2/symlink.c
+++ b/fs/ext2/symlink.c
@@ -25,6 +25,7 @@
#include <linux/stat.h>
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,37 +51,54 @@ 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;
+ }
+ UPDATE_ATIME(inode);
+ 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])
i++;
if (copy_to_user(buffer, link, i))
i = -EFAULT;
- if (DO_UPDATE_ATIME(inode)) {
- inode->i_atime = CURRENT_TIME;
- inode->i_dirt = 1;
- }
- iput (inode);
+ UPDATE_ATIME(inode);
if (bh)
brelse (bh);
return i;
diff --git a/fs/ext2/truncate.c b/fs/ext2/truncate.c
index a9e59ca00..5933ff77c 100644
--- a/fs/ext2/truncate.c
+++ b/fs/ext2/truncate.c
@@ -91,7 +91,7 @@ repeat:
}
*p = 0;
inode->i_blocks -= blocks;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
bforget(bh);
if (free_count == 0) {
block_to_free = tmp;
@@ -110,7 +110,8 @@ repeat:
return retry;
}
-static int trunc_indirect (struct inode * inode, int offset, u32 * p)
+static int trunc_indirect (struct inode * inode, int offset, u32 * p,
+ int in_inode)
{
int i, tmp;
struct buffer_head * bh;
@@ -124,103 +125,16 @@ static int trunc_indirect (struct inode * inode, int offset, u32 * p)
#define INDIRECT_BLOCK ((int)DIRECT_BLOCK - offset)
int indirect_block = INDIRECT_BLOCK;
- tmp = *p;
+ tmp = in_inode ? *p : le32_to_cpu(*p);
if (!tmp)
return 0;
ind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize);
- if (tmp != *p) {
- brelse (ind_bh);
- return 1;
- }
- if (!ind_bh) {
- *p = 0;
- return 0;
- }
-repeat:
- for (i = indirect_block ; i < addr_per_block ; i++) {
- if (i < 0)
- i = 0;
- if (i < indirect_block)
- goto repeat;
- ind = i + (u32 *) ind_bh->b_data;
- tmp = le32_to_cpu(*ind);
- if (!tmp)
- continue;
- bh = get_hash_table (inode->i_dev, tmp,
- inode->i_sb->s_blocksize);
- if (i < indirect_block) {
- brelse (bh);
- goto repeat;
- }
- if ((bh && bh->b_count != 1) || tmp != le32_to_cpu(*ind)) {
- retry = 1;
- brelse (bh);
- continue;
- }
- *ind = cpu_to_le32(0);
- mark_buffer_dirty(ind_bh, 1);
- bforget(bh);
- if (free_count == 0) {
- block_to_free = tmp;
- free_count++;
- } else if (free_count > 0 && block_to_free == tmp - free_count)
- free_count++;
- else {
- ext2_free_blocks (inode, block_to_free, free_count);
- block_to_free = tmp;
- free_count = 1;
- }
-/* ext2_free_blocks (inode, tmp, 1); */
- inode->i_blocks -= blocks;
- inode->i_dirt = 1;
- }
- if (free_count > 0)
- ext2_free_blocks (inode, block_to_free, free_count);
- ind = (u32 *) ind_bh->b_data;
- for (i = 0; i < addr_per_block; i++)
- if (le32_to_cpu(*(ind++)))
- break;
- if (i >= addr_per_block)
- if (ind_bh->b_count != 1)
- retry = 1;
- else {
- tmp = *p;
- *p = 0;
- inode->i_blocks -= blocks;
- inode->i_dirt = 1;
- ext2_free_blocks (inode, tmp, 1);
- }
- if (IS_SYNC(inode) && buffer_dirty(ind_bh)) {
- ll_rw_block (WRITE, 1, &ind_bh);
- wait_on_buffer (ind_bh);
- }
- brelse (ind_bh);
- return retry;
-}
-
-static int trunc_indirect_swab32 (struct inode * inode, int offset, u32 * p)
-{
- int i, tmp;
- struct buffer_head * bh;
- struct buffer_head * ind_bh;
- u32 * ind;
- unsigned long block_to_free = 0;
- unsigned long free_count = 0;
- int retry = 0;
- int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
- int blocks = inode->i_sb->s_blocksize / 512;
- int indirect_block = INDIRECT_BLOCK;
-
- tmp = le32_to_cpu(*p);
- if (!tmp)
- return 0;
- ind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize);
- if (tmp != le32_to_cpu(*p)) {
+ if (tmp != (in_inode ? *p : le32_to_cpu(*p))) {
brelse (ind_bh);
return 1;
}
if (!ind_bh) {
- *p = cpu_to_le32(0);
+ *p = in_inode ? 0 : cpu_to_le32(0);
return 0;
}
repeat:
@@ -259,7 +173,7 @@ repeat:
}
/* ext2_free_blocks (inode, tmp, 1); */
inode->i_blocks -= blocks;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
}
if (free_count > 0)
ext2_free_blocks (inode, block_to_free, free_count);
@@ -271,13 +185,15 @@ repeat:
if (ind_bh->b_count != 1)
retry = 1;
else {
- tmp = le32_to_cpu(*p);
- *p = cpu_to_le32(0);
+ tmp = in_inode ? *p : le32_to_cpu(*p);
+ *p = in_inode ? 0 : cpu_to_le32(0);
inode->i_blocks -= blocks;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
ext2_free_blocks (inode, tmp, 1);
+ bforget(ind_bh);
+ ind_bh = NULL;
}
- if (IS_SYNC(inode) && buffer_dirty(ind_bh)) {
+ if (IS_SYNC(inode) && ind_bh && buffer_dirty(ind_bh)) {
ll_rw_block (WRITE, 1, &ind_bh);
wait_on_buffer (ind_bh);
}
@@ -286,7 +202,7 @@ repeat:
}
static int trunc_dindirect (struct inode * inode, int offset,
- u32 * p)
+ u32 * p, int in_inode)
{
int i, tmp;
struct buffer_head * dind_bh;
@@ -297,75 +213,16 @@ static int trunc_dindirect (struct inode * inode, int offset,
#define DINDIRECT_BLOCK (((int)DIRECT_BLOCK - offset) / addr_per_block)
int dindirect_block = DINDIRECT_BLOCK;
- tmp = *p;
- if (!tmp)
- return 0;
- dind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize);
- if (tmp != *p) {
- brelse (dind_bh);
- return 1;
- }
- if (!dind_bh) {
- *p = 0;
- return 0;
- }
-repeat:
- for (i = dindirect_block ; i < addr_per_block ; i++) {
- if (i < 0)
- i = 0;
- if (i < dindirect_block)
- goto repeat;
- dind = i + (u32 *) dind_bh->b_data;
- tmp = le32_to_cpu(*dind);
- if (!tmp)
- continue;
- retry |= trunc_indirect_swab32 (inode, offset + (i * addr_per_block),
- dind);
- mark_buffer_dirty(dind_bh, 1);
- }
- dind = (u32 *) dind_bh->b_data;
- for (i = 0; i < addr_per_block; i++)
- if (le32_to_cpu(*(dind++)))
- break;
- if (i >= addr_per_block)
- if (dind_bh->b_count != 1)
- retry = 1;
- else {
- tmp = *p;
- *p = 0;
- inode->i_blocks -= blocks;
- inode->i_dirt = 1;
- ext2_free_blocks (inode, tmp, 1);
- }
- if (IS_SYNC(inode) && buffer_dirty(dind_bh)) {
- ll_rw_block (WRITE, 1, &dind_bh);
- wait_on_buffer (dind_bh);
- }
- brelse (dind_bh);
- return retry;
-}
-
-static int trunc_dindirect_swab32 (struct inode * inode, int offset,
- u32 * p)
-{
- int i, tmp;
- struct buffer_head * dind_bh;
- u32 * dind;
- int retry = 0;
- int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
- int blocks = inode->i_sb->s_blocksize / 512;
- int dindirect_block = DINDIRECT_BLOCK;
-
- tmp = le32_to_cpu(*p);
+ tmp = in_inode ? *p : le32_to_cpu(*p);
if (!tmp)
return 0;
dind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize);
- if (tmp != le32_to_cpu(*p)) {
+ if (tmp != (in_inode ? *p : le32_to_cpu(*p))) {
brelse (dind_bh);
return 1;
}
if (!dind_bh) {
- *p = cpu_to_le32(0);
+ *p = in_inode ? 0 : cpu_to_le32(0);
return 0;
}
repeat:
@@ -378,8 +235,8 @@ repeat:
tmp = le32_to_cpu(*dind);
if (!tmp)
continue;
- retry |= trunc_indirect_swab32 (inode, offset + (i * addr_per_block),
- dind);
+ retry |= trunc_indirect(inode, offset + (i * addr_per_block),
+ dind, 0);
mark_buffer_dirty(dind_bh, 1);
}
dind = (u32 *) dind_bh->b_data;
@@ -390,13 +247,15 @@ repeat:
if (dind_bh->b_count != 1)
retry = 1;
else {
- tmp = le32_to_cpu(*p);
- *p = cpu_to_le32(0);
+ tmp = in_inode ? *p : le32_to_cpu(*p);
+ *p = in_inode ? 0 : cpu_to_le32(0);
inode->i_blocks -= blocks;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
ext2_free_blocks (inode, tmp, 1);
+ bforget(dind_bh);
+ dind_bh = 0;
}
- if (IS_SYNC(inode) && buffer_dirty(dind_bh)) {
+ if (IS_SYNC(inode) && dind_bh && buffer_dirty(dind_bh)) {
ll_rw_block (WRITE, 1, &dind_bh);
wait_on_buffer (dind_bh);
}
@@ -436,9 +295,9 @@ repeat:
if (i < tindirect_block)
goto repeat;
tind = i + (u32 *) tind_bh->b_data;
- retry |= trunc_dindirect_swab32(inode, EXT2_NDIR_BLOCKS +
+ retry |= trunc_dindirect(inode, EXT2_NDIR_BLOCKS +
addr_per_block + (i + 1) * addr_per_block * addr_per_block,
- tind);
+ tind, 0);
mark_buffer_dirty(tind_bh, 1);
}
tind = (u32 *) tind_bh->b_data;
@@ -452,10 +311,12 @@ repeat:
tmp = *p;
*p = 0;
inode->i_blocks -= blocks;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
ext2_free_blocks (inode, tmp, 1);
+ bforget(tind_bh);
+ tind_bh = 0;
}
- if (IS_SYNC(inode) && buffer_dirty(tind_bh)) {
+ if (IS_SYNC(inode) && tind_bh && buffer_dirty(tind_bh)) {
ll_rw_block (WRITE, 1, &tind_bh);
wait_on_buffer (tind_bh);
}
@@ -479,14 +340,14 @@ void ext2_truncate (struct inode * inode)
while (1) {
retry = trunc_direct(inode);
retry |= trunc_indirect (inode, EXT2_IND_BLOCK,
- (u32 *) &inode->u.ext2_i.i_data[EXT2_IND_BLOCK]);
+ (u32 *) &inode->u.ext2_i.i_data[EXT2_IND_BLOCK], 1);
retry |= trunc_dindirect (inode, EXT2_IND_BLOCK +
EXT2_ADDR_PER_BLOCK(inode->i_sb),
- (u32 *) &inode->u.ext2_i.i_data[EXT2_DIND_BLOCK]);
+ (u32 *) &inode->u.ext2_i.i_data[EXT2_DIND_BLOCK], 1);
retry |= trunc_tindirect (inode);
if (!retry)
break;
- if (IS_SYNC(inode) && inode->i_dirt)
+ if (IS_SYNC(inode) && test_bit(I_DIRTY, &inode->i_state))
ext2_sync_inode (inode);
current->counter = 0;
schedule ();
@@ -510,5 +371,5 @@ void ext2_truncate (struct inode * inode)
}
}
inode->i_mtime = inode->i_ctime = CURRENT_TIME;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
}
diff --git a/fs/fat/cache.c b/fs/fat/cache.c
index 62ff8af1e..6223c1c48 100644
--- a/fs/fat/cache.c
+++ b/fs/fat/cache.c
@@ -280,7 +280,7 @@ int fat_free(struct inode *inode,int skip)
12 ? EOF_FAT12 : EOF_FAT16);
else {
MSDOS_I(inode)->i_start = 0;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
}
lock_fat(inode->i_sb);
while (nr != -1) {
diff --git a/fs/fat/file.c b/fs/fat/file.c
index 82787075a..ca35cc28f 100644
--- a/fs/fat/file.c
+++ b/fs/fat/file.c
@@ -52,6 +52,7 @@ struct inode_operations fat_file_inode_operations = {
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
generic_readpage, /* readpage */
NULL, /* writepage */
fat_bmap, /* bmap */
@@ -99,6 +100,7 @@ struct inode_operations fat_file_inode_operations_1024 = {
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
@@ -355,7 +357,7 @@ long fat_file_write(
filp->f_pos += written;
if (filp->f_pos > inode->i_size) {
inode->i_size = filp->f_pos;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
}
fat_set_uptodate(sb, bh, 1);
fat_mark_buffer_dirty(sb, bh, 0);
@@ -365,7 +367,7 @@ long fat_file_write(
return error;
inode->i_mtime = inode->i_ctime = CURRENT_TIME;
MSDOS_I(inode)->i_attrs |= ATTR_ARCH;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
return buf-start;
}
@@ -379,5 +381,5 @@ void fat_truncate(struct inode *inode)
cluster = SECTOR_SIZE*MSDOS_SB(inode->i_sb)->cluster_size;
(void) fat_free(inode,(inode->i_size+(cluster-1))/cluster);
MSDOS_I(inode)->i_attrs |= ATTR_ARCH;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
}
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index cf14856d1..e35722aff 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -196,6 +196,7 @@ struct super_block *fat_read_super(struct super_block *sb,void *data, int silent
int debug,error,fat;
int blksize = 512;
struct fat_mount_options opts;
+ struct inode *root_inode;
MOD_INC_USE_COUNT;
if (hardsect_size[MAJOR(sb->s_dev)] != NULL){
@@ -329,7 +330,10 @@ struct super_block *fat_read_super(struct super_block *sb,void *data, int silent
MSDOS_SB(sb)->fat_lock = 0;
MSDOS_SB(sb)->prev_free = 0;
memcpy(&(MSDOS_SB(sb)->options), &opts, sizeof(struct fat_mount_options));
- if (!(sb->s_mounted = iget(sb,MSDOS_ROOT_INO))) {
+
+ root_inode = iget(sb,MSDOS_ROOT_INO);
+ sb->s_root = d_alloc_root(root_inode, NULL);
+ if (!sb->s_root) {
sb->s_dev = 0;
printk("get root inode failed\n");
MOD_DEC_USE_COUNT;
@@ -339,7 +343,7 @@ struct super_block *fat_read_super(struct super_block *sb,void *data, int silent
}
-void fat_statfs(struct super_block *sb,struct statfs *buf, int bufsiz)
+int fat_statfs(struct super_block *sb,struct statfs *buf, int bufsiz)
{
int free,nr;
struct statfs tmp;
@@ -362,7 +366,7 @@ void fat_statfs(struct super_block *sb,struct statfs *buf, int bufsiz)
tmp.f_files = 0;
tmp.f_ffree = 0;
tmp.f_namelen = 12;
- copy_to_user(buf, &tmp, bufsiz);
+ return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
}
@@ -514,10 +518,9 @@ void fat_write_inode(struct inode *inode)
linked->i_blocks = inode->i_blocks;
linked->i_atime = inode->i_atime;
MSDOS_I(linked)->i_attrs = MSDOS_I(inode)->i_attrs;
- linked->i_dirt = 1;
+ mark_inode_dirty(linked);
}
- inode->i_dirt = 0;
if (inode->i_ino == MSDOS_ROOT_INO || !inode->i_nlink) return;
if (!(bh = fat_bread(sb, inode->i_ino >> MSDOS_DPB_BITS))) {
printk("dev = %s, ino = %ld\n",
diff --git a/fs/fat/misc.c b/fs/fat/misc.c
index d1e9bc6ca..034f62c1f 100644
--- a/fs/fat/misc.c
+++ b/fs/fat/misc.c
@@ -19,13 +19,14 @@
/* Well-known binary file extensions - of course there are many more */
-static char bin_extensions[] =
- "EXE" "COM" "BIN" "APP" "SYS" "DRV" "OVL" "OVR" "OBJ" "LIB" "DLL" "PIF" /* program code */
- "ARC" "ZIP" "LHA" "LZH" "ZOO" "TAR" "Z " "ARJ" /* common archivers */
- "TZ " "TAZ" "TZP" "TPZ" /* abbreviations of tar.Z and tar.zip */
- "GZ " "TGZ" "DEB" /* .gz, .tar.gz and Debian packages */
- "GIF" "BMP" "TIF" "GL " "JPG" "PCX" /* graphics */
- "TFM" "VF " "GF " "PK " "PXL" "DVI"; /* TeX */
+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 */
+ "PBM" "PGM" "DXF" /* graphics */
+ "TEX"; /* TeX */
/*
@@ -39,9 +40,7 @@ void fat_fs_panic(struct super_block *s,const char *msg)
not_ro = !(s->s_flags & MS_RDONLY);
if (not_ro) s->s_flags |= MS_RDONLY;
- printk("Filesystem panic (dev %s, ", kdevname(s->s_dev));
- printk("mounted on %s:%ld)\n %s\n", /* note: kdevname returns & static char[] */
- kdevname(s->s_covered->i_dev), s->s_covered->i_ino, msg);
+ printk("Filesystem panic (dev %s).", kdevname(s->s_dev));
if (not_ro)
printk(" File system has been set read-only\n");
}
@@ -62,9 +61,9 @@ int is_binary(char conversion,char *extension)
case 't':
return 0;
case 'a':
- for (walk = bin_extensions; *walk; walk += 3)
- if (!strncmp(extension,walk,3)) return 1;
- return 0;
+ for (walk = ascii_extensions; *walk; walk += 3)
+ if (!strncmp(extension,walk,3)) return 0;
+ return 1; /* default binary conversion */
default:
printk("Invalid conversion mode - defaulting to "
"binary.\n");
@@ -179,7 +178,7 @@ printk("last = %d\n",last);
if (last) fat_access(sb,last,nr);
else {
MSDOS_I(inode)->i_start = nr;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
}
#ifdef DEBUG
if (last) printk("next set to %d\n",fat_access(sb,last,-1));
@@ -216,7 +215,7 @@ if (last) printk("next set to %d\n",fat_access(sb,last,-1));
#ifdef DEBUG
printk("size is %d now (%x)\n",inode->i_size,inode);
#endif
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
}
return 0;
}
diff --git a/fs/fat/mmap.c b/fs/fat/mmap.c
index 6a3515eef..a858cd2cc 100644
--- a/fs/fat/mmap.c
+++ b/fs/fat/mmap.c
@@ -29,7 +29,7 @@ static unsigned long fat_file_mmap_nopage(
unsigned long address,
int error_code)
{
- struct inode * inode = area->vm_inode;
+ struct inode * inode = area->vm_dentry->d_inode;
unsigned long page;
unsigned int clear;
int pos;
@@ -101,11 +101,10 @@ int fat_mmap(struct inode * inode, struct file * file, struct vm_area_struct * v
return -EACCES;
if (!IS_RDONLY(inode)) {
inode->i_atime = CURRENT_TIME;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
}
- vma->vm_inode = inode;
- atomic_inc(&inode->i_count);
+ vma->vm_dentry = dget(file->f_dentry);
vma->vm_ops = &fat_file_mmap;
return 0;
}
diff --git a/fs/fcntl.c b/fs/fcntl.c
index 6418a5d83..57eef2530 100644
--- a/fs/fcntl.c
+++ b/fs/fcntl.c
@@ -67,6 +67,34 @@ asmlinkage int sys_dup(unsigned int fildes)
return ret;
}
+#define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | FASYNC)
+
+static int setfl(struct file * filp, unsigned long arg)
+{
+ struct inode * inode = filp->f_dentry->d_inode;
+
+ /*
+ * In the case of an append-only file, O_APPEND
+ * cannot be cleared
+ */
+ if (!(arg & O_APPEND) && IS_APPEND(inode))
+ return -EPERM;
+
+ /* Did FASYNC state change? */
+ if ((arg ^ filp->f_flags) & FASYNC) {
+ if (filp->f_op->fasync)
+ filp->f_op->fasync(inode, filp, (arg & FASYNC) != 0);
+ }
+
+ /* required for strict SunOS emulation */
+ if (O_NONBLOCK != O_NDELAY)
+ if (arg & O_NDELAY)
+ arg |= O_NONBLOCK;
+
+ filp->f_flags = (arg & SETFL_MASK) | (filp->f_flags & ~SETFL_MASK);
+ return 0;
+}
+
asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg)
{
struct file * filp;
@@ -95,28 +123,7 @@ asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg)
err = filp->f_flags;
break;
case F_SETFL:
- /*
- * In the case of an append-only file, O_APPEND
- * cannot be cleared
- */
- err = -EPERM;
- if (IS_APPEND(filp->f_inode) && !(arg & O_APPEND))
- break;
- err = 0;
- if ((arg & FASYNC) && !(filp->f_flags & FASYNC) &&
- filp->f_op->fasync)
- filp->f_op->fasync(filp->f_inode, filp, 1);
- if (!(arg & FASYNC) && (filp->f_flags & FASYNC) &&
- filp->f_op->fasync)
- filp->f_op->fasync(filp->f_inode, filp, 0);
- /* required for strict SunOS emulation */
- if (O_NONBLOCK != O_NDELAY)
- if (arg & O_NDELAY)
- arg |= O_NONBLOCK;
- filp->f_flags &= ~(O_APPEND | O_NONBLOCK |
- O_NDELAY | FASYNC);
- filp->f_flags |= arg & (O_APPEND | O_NONBLOCK |
- O_NDELAY | FASYNC);
+ err = setfl(filp, arg);
break;
case F_GETLK:
err = fcntl_getlk(fd, (struct flock *) arg);
@@ -186,12 +193,12 @@ asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg)
fasync_ok:
err = 0;
filp->f_owner = arg;
- if (S_ISSOCK (filp->f_inode->i_mode))
+ if (S_ISSOCK (filp->f_dentry->d_inode->i_mode))
err = sock_fcntl (filp, F_SETOWN, arg);
break;
default:
/* sockets need a few special fcntls. */
- if (S_ISSOCK (filp->f_inode->i_mode))
+ if (S_ISSOCK (filp->f_dentry->d_inode->i_mode))
err = sock_fcntl (filp, cmd, arg);
else
err = -EINVAL;
diff --git a/fs/fifo.c b/fs/fifo.c
index 16f4f7f61..d2eb7a80e 100644
--- a/fs/fifo.c
+++ b/fs/fifo.c
@@ -153,7 +153,6 @@ struct inode_operations fifo_inode_operations = {
void init_fifo(struct inode * inode)
{
inode->i_op = &fifo_inode_operations;
- inode->i_pipe = 1;
PIPE_LOCK(*inode) = 0;
PIPE_BASE(*inode) = NULL;
PIPE_START(*inode) = PIPE_LEN(*inode) = 0;
diff --git a/fs/file_table.c b/fs/file_table.c
index b8c8d4155..6413218ff 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -102,6 +102,24 @@ again:
return f;
}
+/*
+ * Clear and initialize a (private) struct file for the given dentry,
+ * and call the open function (if any). The caller must verify that
+ * inode->i_op and inode->i_op->default_file_ops are not NULL.
+ */
+int init_private_file(struct file *filp, struct dentry *dentry, int mode)
+{
+ memset(filp, 0, sizeof(*filp));
+ filp->f_mode = mode;
+ filp->f_count = 1;
+ filp->f_dentry = dentry;
+ filp->f_op = dentry->d_inode->i_op->default_file_ops;
+ if (filp->f_op->open)
+ return filp->f_op->open(dentry->d_inode, filp);
+ else
+ return 0;
+}
+
#ifdef CONFIG_QUOTA
void add_dquot_ref(kdev_t dev, short type)
@@ -109,11 +127,15 @@ void add_dquot_ref(kdev_t dev, short type)
struct file *filp;
for (filp = inuse_filps; filp; filp = filp->f_next) {
- if (!filp->f_inode || filp->f_inode->i_dev != dev)
+ struct inode * inode;
+ if (!filp->f_dentry)
+ continue;
+ inode = filp->f_dentry->d_inode;
+ if (!inode || inode->i_dev != dev)
continue;
- if (filp->f_mode & FMODE_WRITE && filp->f_inode->i_sb->dq_op) {
- filp->f_inode->i_sb->dq_op->initialize(filp->f_inode, type);
- filp->f_inode->i_flags |= S_WRITE;
+ if (filp->f_mode & FMODE_WRITE && inode->i_sb->dq_op) {
+ inode->i_sb->dq_op->initialize(inode, type);
+ inode->i_flags |= S_WRITE;
}
}
}
@@ -123,11 +145,15 @@ void reset_dquot_ptrs(kdev_t dev, short type)
struct file *filp;
for (filp = inuse_filps; filp; filp = filp->f_next) {
- if (!filp->f_inode || filp->f_inode->i_dev != dev)
+ struct inode * inode;
+ if (!filp->f_dentry)
+ continue;
+ inode = filp->f_dentry->d_inode;
+ if (!inode || inode->i_dev != dev)
continue;
- if (IS_WRITABLE(filp->f_inode)) {
- filp->f_inode->i_dquot[type] = NODQUOT;
- filp->f_inode->i_flags &= ~S_WRITE;
+ if (IS_WRITABLE(inode)) {
+ inode->i_dquot[type] = NODQUOT;
+ inode->i_flags &= ~S_WRITE;
}
}
}
diff --git a/fs/filesystems.c b/fs/filesystems.c
index 004ee0aff..74016aa67 100644
--- a/fs/filesystems.c
+++ b/fs/filesystems.c
@@ -8,7 +8,6 @@
#include <linux/config.h>
#include <linux/fs.h>
-#include <linux/nametrans.h>
#include <linux/minix_fs.h>
#include <linux/ext2_fs.h>
@@ -45,10 +44,6 @@ __initfunc(static void do_sys_setup(void))
binfmt_setup();
-#ifdef CONFIG_TRANS_NAMES
- init_nametrans();
-#endif
-
#ifdef CONFIG_EXT2_FS
init_ext2_fs();
#endif
diff --git a/fs/inode.c b/fs/inode.c
index 7215e1204..8813bbd45 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -1,708 +1,500 @@
/*
- * fs/inode.c
+ * linux/fs/inode.c
*
- * Complete reimplementation
- * (C) 1997 Thomas Schoebel-Theuer
+ * (C) 1997 Linus Torvalds
*/
-/* Everything here is intended to be MP-safe. However, other parts
- * of the kernel are not yet MP-safe, in particular the inode->i_count++
- * that are spread over everywhere. These should be replaced by
- * iinc() as soon as possible. Since I have no MP machine, I could
- * not test it.
- */
-#include <linux/config.h>
-#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/mm.h>
-#include <linux/kernel.h>
-#include <linux/dlists.h>
-#include <linux/dalloc.h>
-#include <linux/omirr.h>
-
-/* #define DEBUG */
-
-#define HASH_SIZE 1024 /* must be a power of 2 */
-#define NR_LEVELS 4
-
-#define ST_AGED 1
-#define ST_HASHED 2
-#define ST_EMPTY 4
-#define ST_TO_READ 8
-#define ST_TO_WRITE 16
-#define ST_TO_PUT 32
-#define ST_TO_DROP 64
-#define ST_IO (ST_TO_READ|ST_TO_WRITE|ST_TO_PUT|ST_TO_DROP)
-#define ST_WAITING 128
-#define ST_FREEING 256
-#define ST_IBASKET 512
-
-/* The idea is to keep empty inodes in a separate list, so no search
- * is required as long as empty inodes exit.
- * All reusable inodes occurring in the hash table with i_count==0
- * are also registered in the ringlist aged_i[level], but in LRU order.
- * Used inodes with i_count>0 are kept solely in the hashtable and in
- * all_i, but in no other list.
- * The level is used for multilevel aging to avoid thrashing; each
- * time i_count decreases to 0, the inode is inserted into the next level
- * ringlist. Cache reusage is simply by taking the _last_ element from the
- * lowest-level ringlist that contains inodes.
- * In contrast to the old code, there isn't any O(n) search overhead now
- * in iget/iput (if you make HASH_SIZE large enough).
+
+/*
+ * New inode.c implementation.
+ *
+ * This implementation has the basic premise of trying
+ * to be extremely low-overhead and SMP-safe, yet be
+ * simple enough to be "obviously correct".
+ *
+ * Famous last words.
+ */
+
+/*
+ * Inode lookup is no longer as critical as it used to be:
+ * most of the lookups are going to be through the dcache.
+ */
+#define HASH_BITS 8
+#define HASH_SIZE (1UL << HASH_BITS)
+#define HASH_MASK (HASH_SIZE-1)
+
+/*
+ * Each inode can be on two separate lists. One is
+ * the hash list of the inode, used for lookups. The
+ * other linked list is the "type" list:
+ * "in_use" - valid inode, hashed
+ * "dirty" - valid inode, hashed, dirty.
+ * "unused" - ready to be re-used. Not hashed.
+ *
+ * The two first versions also have a dirty list, allowing
+ * for low-overhead inode sync() operations.
+ */
+
+static LIST_HEAD(inode_in_use);
+static LIST_HEAD(inode_dirty);
+static LIST_HEAD(inode_unused);
+static struct list_head inode_hashtable[HASH_SIZE];
+
+/*
+ * A simple spinlock to protect the list manipulations
+ */
+spinlock_t inode_lock = SPIN_LOCK_UNLOCKED;
+
+/*
+ * Statistics gathering.. Not actually done yet.
*/
-static struct inode * hashtable[HASH_SIZE];/* linked with i_hash_{next,prev} */
-static struct inode * all_i = NULL; /* linked with i_{next,prev} */
-static struct inode * empty_i = NULL; /* linked with i_{next,prev} */
-static struct inode * aged_i[NR_LEVELS+1]; /* linked with i_lru_{next,prev} */
-static int aged_reused[NR_LEVELS+1]; /* # removals from aged_i[level] */
-static int age_table[NR_LEVELS+1] = { /* You may tune this. */
- 1, 4, 10, 100, 1000
-}; /* after which # of uses to increase to the next level */
-
-/* This is for kernel/sysctl.c */
-
-/* Just aligning plain ints and arrays thereof doesn't work reliably.. */
struct {
int nr_inodes;
int nr_free_inodes;
- int aged_count[NR_LEVELS+1]; /* # in each level */
+ int dummy[10];
} inodes_stat;
int max_inodes = NR_INODE;
-unsigned long last_inode = 0;
-void inode_init(void)
+void __mark_inode_dirty(struct inode *inode)
{
- memset(hashtable, 0, sizeof(hashtable));
- memset(aged_i, 0, sizeof(aged_i));
- memset(aged_reused, 0, sizeof(aged_reused));
- memset(&inodes_stat, 0, sizeof(inodes_stat));
+ spin_lock(&inode_lock);
+ list_del(&inode->i_list);
+ list_add(&inode->i_list, &inode_dirty);
+ spin_unlock(&inode_lock);
}
-/* Intended for short locks of the above global data structures.
- * Could be replaced with spinlocks completely, since there is
- * no blocking during manipulation of the static data; however the
- * lock in invalidate_inodes() may last relatively long.
- */
-#ifdef __SMP__
-struct semaphore vfs_sem = MUTEX;
-#endif
-
-DEF_INSERT(all,struct inode,i_next,i_prev)
-DEF_REMOVE(all,struct inode,i_next,i_prev)
-
-DEF_INSERT(lru,struct inode,i_lru_next,i_lru_prev)
-DEF_REMOVE(lru,struct inode,i_lru_next,i_lru_prev)
-
-DEF_INSERT(hash,struct inode,i_hash_next,i_hash_prev)
-DEF_REMOVE(hash,struct inode,i_hash_next,i_hash_prev)
-
-DEF_INSERT(ibasket,struct inode,i_basket_next,i_basket_prev)
-DEF_REMOVE(ibasket,struct inode,i_basket_next,i_basket_prev)
-
-#ifdef DEBUG
-extern void printpath(struct dentry * entry);
-struct inode * xtst[15000];
-int xcnt = 0;
-
-void xcheck(char * txt, struct inode * p)
+static inline void unlock_inode(struct inode *inode)
{
- int i;
- for(i=xcnt-1; i>=0; i--)
- if(xtst[i] == p)
- return;
- printk("Bogus inode %p in %s\n", p, txt);
+ clear_bit(I_LOCK, &inode->i_state);
+ wake_up(&inode->i_wait);
}
-#else
-#define xcheck(t,p) /*nothing*/
-#endif
-static inline struct inode * grow_inodes(void)
+static void __wait_on_inode(struct inode * inode)
{
- struct inode * res;
- struct inode * inode = res = (struct inode*)__get_free_page(GFP_KERNEL);
- int size = PAGE_SIZE;
- if(!inode)
- return NULL;
-
- size -= sizeof(struct inode);
- inode++;
- inodes_stat.nr_inodes++;
-#ifdef DEBUG
-xtst[xcnt++]=res;
-#endif
- while(size >= sizeof(struct inode)) {
-#ifdef DEBUG
-xtst[xcnt++]=inode;
-#endif
- inodes_stat.nr_inodes++;
- inodes_stat.nr_free_inodes++;
- insert_all(&empty_i, inode);
- inode->i_status = ST_EMPTY;
- inode++;
- size -= sizeof(struct inode);
+ struct wait_queue wait = { current, NULL };
+
+ add_wait_queue(&inode->i_wait, &wait);
+repeat:
+ current->state = TASK_UNINTERRUPTIBLE;
+ if (test_bit(I_LOCK, &inode->i_state)) {
+ schedule();
+ goto repeat;
}
- return res;
+ remove_wait_queue(&inode->i_wait, &wait);
+ current->state = TASK_RUNNING;
}
-static inline int hash(dev_t i_dev, unsigned long i_ino)
+static inline void wait_on_inode(struct inode *inode)
{
- return ((int)i_ino ^ ((int)i_dev << 6)) & (HASH_SIZE-1);
+ if (test_bit(I_LOCK, &inode->i_state))
+ __wait_on_inode(inode);
}
-static inline blocking void wait_io(struct inode * inode, unsigned short flags)
+/*
+ * These are initializations that only need to be done
+ * once, because the fields are idempotent across use
+ * of the inode..
+ */
+static inline void init_once(struct inode * inode)
{
- while(inode->i_status & flags) {
- struct wait_queue wait = {current, NULL};
- inode->i_status |= ST_WAITING;
- vfs_unlock();
- add_wait_queue(&inode->i_wait, &wait);
- sleep_on(&inode->i_wait);
- remove_wait_queue(&inode->i_wait, &wait);
- vfs_lock();
- }
+ memset(inode, 0, sizeof(*inode));
+ init_waitqueue(&inode->i_wait);
+ INIT_LIST_HEAD(&inode->i_dentry);
+ INIT_LIST_HEAD(&inode->i_hash);
+ sema_init(&inode->i_sem, 1);
}
-static inline blocking void set_io(struct inode * inode,
- unsigned short waitflags,
- unsigned short setflags)
+
+/*
+ * Look out! This returns with the inode lock held if
+ * it got an inode..
+ */
+static struct inode * grow_inodes(void)
{
- wait_io(inode, waitflags);
- inode->i_status |= setflags;
- vfs_unlock();
+ struct inode * inode = (struct inode *)__get_free_page(GFP_KERNEL);
+
+ if (inode) {
+ int size;
+ struct inode * tmp;
+
+ spin_lock(&inode_lock);
+ size = PAGE_SIZE - 2*sizeof(struct inode);
+ tmp = inode;
+ do {
+ tmp++;
+ init_once(tmp);
+ list_add(&tmp->i_list, &inode_unused);
+ size -= sizeof(struct inode);
+ } while (size >= 0);
+ init_once(inode);
+ }
+ return inode;
}
-static inline blocking int release_io(struct inode * inode, unsigned short flags)
+static inline void write_inode(struct inode *inode)
{
- int res = 0;
- vfs_lock();
- inode->i_status &= ~flags;
- if(inode->i_status & ST_WAITING) {
- inode->i_status &= ~ST_WAITING;
- vfs_unlock();
- wake_up(&inode->i_wait);
- res = 1;
- }
- return res;
+ if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->write_inode)
+ inode->i_sb->s_op->write_inode(inode);
}
-static inline blocking void _io(void (*op)(struct inode*), struct inode * inode,
- unsigned short waitflags, unsigned short setflags)
+static inline void sync_list(struct list_head *head, struct list_head *clean)
{
- /* Do nothing if the same op is already in progress. */
- if(op && !(inode->i_status & setflags)) {
- set_io(inode, waitflags, setflags);
- op(inode);
- if(release_io(inode, setflags)) {
- /* Somebody grabbed my inode from under me. */
-#ifdef DEBUG
- printk("_io grab!\n");
-#endif
- vfs_lock();
+ struct list_head * tmp;
+
+ while ((tmp = head->prev) != head) {
+ struct inode *inode = list_entry(tmp, struct inode, i_list);
+ list_del(tmp);
+
+ /*
+ * If the inode is locked, it's already being written out.
+ * We have to wait for it, though.
+ */
+ if (test_bit(I_LOCK, &inode->i_state)) {
+ list_add(tmp, head);
+ spin_unlock(&inode_lock);
+ __wait_on_inode(inode);
+ } else {
+ list_add(tmp, clean);
+ clear_bit(I_DIRTY, &inode->i_state);
+ set_bit(I_LOCK, &inode->i_state);
+ spin_unlock(&inode_lock);
+ write_inode(inode);
+ unlock_inode(inode);
}
- }
+ spin_lock(&inode_lock);
+ }
}
-blocking int _free_ibasket(struct super_block * sb)
+/*
+ * "sync_inodes()" goes through the dirty list
+ * and writes them out and puts them back on
+ * the normal list.
+ */
+void sync_inodes(kdev_t dev)
{
- 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;
+ spin_lock(&inode_lock);
+ sync_list(&inode_dirty, &inode_in_use);
+ spin_unlock(&inode_lock);
}
-static /*inline*/ void _put_ibasket(struct inode * inode)
+/*
+ * This is called by the filesystem to tell us
+ * that the inode is no longer useful. We just
+ * terminate it with extreme predjudice.
+ */
+void clear_inode(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);
- }
+ truncate_inode_pages(inode, 0);
+ wait_on_inode(inode);
+ if (IS_WRITABLE(inode) && inode->i_sb && inode->i_sb->dq_op)
+ inode->i_sb->dq_op->drop(inode);
+
+ inode->i_state = 0;
}
-blocking void _clear_inode(struct inode * inode, int external, int verbose)
+#define CAN_UNUSE(inode) \
+ (((inode)->i_count == 0) && \
+ ((inode)->i_nrpages == 0) && \
+ (!(inode)->i_state))
+
+static void invalidate_list(struct list_head *head, kdev_t dev)
{
-xcheck("_clear_inode",inode);
- if(inode->i_status & ST_IBASKET) {
- struct super_block * sb = inode->i_sb;
- remove_ibasket(&sb->s_ibasket, inode);
- sb->s_ibasket_count--;
- inode->i_status &= ~ST_IBASKET;
-#if 0
-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)
- return;
+ struct list_head *next;
+
+ next = head->next;
+ for (;;) {
+ struct list_head * tmp = next;
+ struct inode * inode;
+
+ next = next->next;
+ if (tmp == head)
+ break;
+ inode = list_entry(tmp, struct inode, i_list);
+ if (inode->i_dev != dev)
+ continue;
+ if (!CAN_UNUSE(inode))
+ continue;
+ list_del(&inode->i_hash);
+ INIT_LIST_HEAD(&inode->i_hash);
+ list_del(&inode->i_list);
+ list_add(&inode->i_list, &inode_unused);
}
- if(inode->i_status & ST_HASHED)
- remove_hash(&hashtable[hash(inode->i_dev, inode->i_ino)], inode);
- 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)
- printk("VFS: clearing aged inode\n");
- 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) {
- printk("VFS: clearing inode during IO operation\n");
- }
- 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) {
- vfs_unlock(); /* may block, can that be revised? */
- truncate_inode_pages(inode, 0);
- vfs_lock();
- }
- insert_all(&empty_i, inode);
- inodes_stat.nr_free_inodes++;
- } else if(external)
- printk("VFS: empty inode is unnecessarily cleared multiple "
- "times by an fs\n");
- else
- printk("VFS: clearing empty inode\n");
- inode->i_status = ST_EMPTY;
- /* The inode is not really cleared any more here, but only once
- * when taken from empty_i. This saves instructions and processor
- * cache pollution.
- */
}
-void insert_inode_hash(struct inode * inode)
+void invalidate_inodes(kdev_t dev)
{
-xcheck("insert_inode_hash",inode);
- vfs_lock();
- if(!(inode->i_status & ST_HASHED)) {
- insert_hash(&hashtable[hash(inode->i_dev, inode->i_ino)], inode);
- inode->i_status |= ST_HASHED;
- } else
- printk("VFS: trying to hash an inode again\n");
- vfs_unlock();
+ spin_lock(&inode_lock);
+ invalidate_list(&inode_in_use, dev);
+ invalidate_list(&inode_dirty, dev);
+ spin_unlock(&inode_lock);
}
-blocking struct inode * _get_empty_inode(void)
+/*
+ * This is called with the inode lock held. It just looks at the last
+ * inode on the in-use list, and if the inode is trivially freeable
+ * we just move it to the unused list.
+ *
+ * Otherwise we just move the inode to be the first inode and expect to
+ * get back to the problem later..
+ */
+static void try_to_free_inodes(void)
{
- struct inode * inode;
- int retry = 0;
-
-retry:
- inode = empty_i;
- 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) {
- int level;
- int usable = 0;
- for(level = 0; level <= NR_LEVELS; level++)
- if(aged_i[level]) {
- inode = aged_i[level]->i_lru_prev;
- /* Here is the picking strategy, tune this */
- if(aged_reused[level] < (usable++ ?
- inodes_stat.aged_count[level] :
- 2))
- break;
- aged_reused[level] = 0;
- }
- if(inode) {
- if(!(inode->i_status & ST_AGED))
- printk("VFS: inode aging inconsistency\n");
- if(atomic_read(&inode->i_count) + inode->i_ddir_count)
- printk("VFS: i_count of aged inode is not zero\n");
- if(inode->i_dirt)
- printk("VFS: Hey, somebody made my aged inode dirty\n");
- _clear_inode(inode, 0, 0);
- goto retry;
+ struct list_head * tmp;
+ struct list_head *head = &inode_in_use;
+
+ tmp = head->prev;
+ if (tmp != head) {
+ struct inode * inode;
+
+ list_del(tmp);
+ inode = list_entry(tmp, struct inode, i_list);
+ if (CAN_UNUSE(inode)) {
+ list_del(&inode->i_hash);
+ INIT_LIST_HEAD(&inode->i_hash);
+ head = &inode_unused;
}
+ list_add(tmp, head);
}
- if(!inode) {
- vfs_unlock();
- schedule();
- if(retry > 10)
- panic("VFS: cannot repair inode shortage");
- if(retry > 2)
- printk("VFS: no free inodes\n");
- retry++;
- vfs_lock();
- goto retry;
- }
-xcheck("get_empty_inode",inode);
- memset(inode, 0, sizeof(struct inode));
- atomic_set(&inode->i_count, 1);
- inode->i_nlink = 1;
- sema_init(&inode->i_sem, 1);
- inode->i_ino = ++last_inode;
- inode->i_version = ++event;
- insert_all(&all_i, inode);
- return inode;
}
+
-static inline blocking struct inode * _get_empty_inode_hashed(dev_t i_dev,
- unsigned long i_ino)
+static struct inode * find_inode(struct super_block * sb, unsigned long ino, struct list_head *head)
{
- 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) {
- atomic_inc(&inode->i_count);
- printk("VFS: inode %lx is already in use\n", i_ino);
- return inode;
- }
- inode = inode->i_hash_next;
- } while(inode != *base);
- inode = _get_empty_inode();
- inode->i_dev = i_dev;
- inode->i_ino = i_ino;
- insert_hash(base, inode);
- inode->i_status |= ST_HASHED;
+ struct list_head *tmp;
+ struct inode * inode;
+
+ tmp = head;
+ for (;;) {
+ tmp = tmp->next;
+ inode = NULL;
+ if (tmp == head)
+ break;
+ inode = list_entry(tmp, struct inode, i_hash);
+ if (inode->i_sb != sb)
+ continue;
+ if (inode->i_ino != ino)
+ continue;
+ inode->i_count++;
+ break;
+ }
return inode;
}
-blocking struct inode * get_empty_inode_hashed(dev_t i_dev, unsigned long i_ino)
+/*
+ * This just initializes the inode fields
+ * to known values before returning the inode..
+ *
+ * i_sb, i_ino, i_count, i_state and the lists have
+ * been initialized elsewhere..
+ */
+void clean_inode(struct inode *inode)
{
- struct inode * inode;
-
- vfs_lock();
- inode = _get_empty_inode_hashed(i_dev, i_ino);
- vfs_unlock();
- return inode;
+ memset(&inode->u, 0, sizeof(inode->u));
+ inode->i_sock = 0;
+ inode->i_op = NULL;
+ inode->i_nlink = 1;
+ inode->i_writecount = 0;
+ inode->i_size = 0;
+ memset(&inode->i_dquot, 0, sizeof(inode->i_dquot));
+ sema_init(&inode->i_sem, 1);
}
-void _get_inode(struct inode * inode)
+/*
+ * This gets called with I_LOCK held: it needs
+ * to read the inode and then unlock it
+ */
+static inline void read_inode(struct inode *inode, struct super_block *sb)
{
- 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) {
- 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))
- /* make dirs less thrashable */
- inode->i_level = NR_LEVELS-1;
- else if(inode->i_nlink > 1)
- /* keep hardlinks totally separate */
- inode->i_level = NR_LEVELS;
- 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");
- } else if(inode->i_status & ST_EMPTY)
- printk("VFS: invalid reuse of empty inode\n");
+ sb->s_op->read_inode(inode);
+ unlock_inode(inode);
}
-blocking struct inode * __iget(struct super_block * sb,
- unsigned long i_ino,
- int crossmntp)
+struct inode * get_empty_inode(void)
{
- struct inode ** base;
+ static unsigned long last_ino = 0;
struct inode * inode;
- dev_t i_dev;
-
- if(!sb)
- panic("VFS: iget with sb == NULL");
- i_dev = sb->s_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) {
- atomic_inc(&inode->i_count);
- _get_inode(inode);
-
- /* Allow concurrent writes/puts. This is in particular
- * useful e.g. when syncing large chunks.
- * I hope the i_dirty flag is everywhere set as soon
- * as _any_ modifcation is made and _before_
- * giving up control, so no harm should occur if data
- * is modified during writes, because it will be
- * rewritten again (does a short inconsistency on the
- * disk harm?)
- */
- wait_io(inode, ST_TO_READ);
- vfs_unlock();
- goto done;
- }
- inode = inode->i_hash_next;
- } while(inode != *base);
- 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) {
- 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))
- goto done;
- }
- vfs_unlock();
-done:
- while(crossmntp && inode->i_mount) {
- struct inode * tmp = inode->i_mount;
- iinc(tmp);
- iput(inode);
- inode = tmp;
+ struct list_head * tmp;
+
+ spin_lock(&inode_lock);
+ try_to_free_inodes();
+ tmp = inode_unused.next;
+ if (tmp != &inode_unused) {
+ list_del(tmp);
+ inode = list_entry(tmp, struct inode, i_list);
+add_new_inode:
+ inode->i_sb = NULL;
+ inode->i_ino = ++last_ino;
+ inode->i_count = 1;
+ list_add(&inode->i_list, &inode_in_use);
+ inode->i_state = 0;
+ spin_unlock(&inode_lock);
+ clean_inode(inode);
+ return inode;
}
-xcheck("_iget",inode);
+
+ /*
+ * Warning: if this succeeded, we will now
+ * return with the inode lock.
+ */
+ spin_unlock(&inode_lock);
+ inode = grow_inodes();
+ if (inode)
+ goto add_new_inode;
+
return inode;
}
-blocking void __iput(struct inode * inode)
+/*
+ * This is called with the inode lock held.. Be careful.
+ */
+static struct inode * get_new_inode(struct super_block *sb, unsigned long ino, struct list_head *head)
{
- struct super_block * sb;
-xcheck("_iput",inode);
- if(atomic_read(&inode->i_count) + inode->i_ddir_count < 0)
- printk("VFS: i_count is negative\n");
- if((atomic_read(&inode->i_count) + inode->i_ddir_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) {
- 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);
- 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) {
- 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)
- goto done;
- }
- }
- 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)
- goto done;
- }
- }
- if(inode->i_mmap)
- printk("VFS: inode has mappings\n");
- if(inode->i_status & ST_AGED) {
- printk("VFS: reaging inode\n");
-#if defined(DEBUG)
-printpath(inode->i_dentry);
-printk("\n");
-#endif
- goto done;
- }
- if(!(inode->i_status & (ST_HASHED|ST_EMPTY))) {
- _clear_inode(inode, 0, 1);
- goto done;
+ struct inode * inode;
+ struct list_head * tmp = inode_unused.next;
+
+ if (tmp != &inode_unused) {
+ list_del(tmp);
+ inode = list_entry(tmp, struct inode, i_list);
+add_new_inode:
+ list_add(&inode->i_list, &inode_in_use);
+ list_add(&inode->i_hash, head);
+ inode->i_sb = sb;
+ inode->i_dev = sb->s_dev;
+ inode->i_ino = ino;
+ inode->i_flags = sb->s_flags;
+ inode->i_count = 1;
+ inode->i_state = 1 << I_LOCK;
+ spin_unlock(&inode_lock);
+ clean_inode(inode);
+ read_inode(inode, sb);
+ return inode;
}
- if(inode->i_status & ST_EMPTY) {
- printk("VFS: aging an empty inode\n");
- goto done;
+
+ /*
+ * Uhhuh.. We need to expand. Unlock for the allocation,
+ * but note that "grow_inodes()" will return with the
+ * lock held again if the allocation succeeded.
+ */
+ spin_unlock(&inode_lock);
+ inode = grow_inodes();
+ if (inode) {
+ /* We released the lock, so.. */
+ struct inode * old = find_inode(sb, ino, head);
+ if (!old)
+ goto add_new_inode;
+ list_add(&inode->i_list, &inode_unused);
+ spin_unlock(&inode_lock);
+ wait_on_inode(old);
+ return old;
}
- insert_lru(&aged_i[inode->i_level], inode);
- inodes_stat.aged_count[inode->i_level]++;
- inode->i_status |= ST_AGED;
-done:
- inode->i_status &= ~ST_FREEING;
+ return inode;
}
-blocking void _iput(struct inode * inode)
+static inline unsigned long hash(struct super_block *sb, unsigned long i_ino)
{
- vfs_lock();
- __iput(inode);
- vfs_unlock();
+ unsigned long tmp = i_ino | (unsigned long) sb;
+ tmp = tmp + (tmp >> HASH_BITS) + (tmp >> HASH_BITS*2);
+ return tmp & HASH_MASK;
}
-blocking void sync_inodes(kdev_t dev)
+struct inode *iget(struct super_block *sb, unsigned long ino)
{
+ struct list_head * head = inode_hashtable + hash(sb,ino);
struct inode * inode;
- vfs_lock();
- inode = all_i;
- 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 &&
- !(inode->i_status & ST_FREEING)) {
- inode->i_dirt = 0;
- _io(inode->i_sb->s_op->write_inode, inode,
- ST_IO, ST_TO_WRITE);
- }
- }
- inode = inode->i_next;
- } while(inode != all_i);
- vfs_unlock();
+
+ spin_lock(&inode_lock);
+ inode = find_inode(sb, ino, head);
+ if (!inode) {
+ try_to_free_inodes();
+ return get_new_inode(sb, ino, head);
+ }
+ spin_unlock(&inode_lock);
+ wait_on_inode(inode);
+ return inode;
}
-blocking int _check_inodes(kdev_t dev, int complain)
+void insert_inode_hash(struct inode *inode)
{
- struct inode * inode;
- int bad = 0;
-
- vfs_lock();
-startover:
- inode = all_i;
- 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)) {
- bad++;
- } else {
- _clear_inode(inode, 0, 0);
-
- /* _clear_inode() may recursively clear other
- * inodes, probably also the next one.
- */
- if(next->i_status & ST_EMPTY)
- goto startover;
- }
- }
- inode = next;
- } while(inode != all_i);
- vfs_unlock();
- if(complain && bad)
- printk("VFS: %d inode(s) busy on removed device `%s'\n",
- bad, kdevname(dev));
- return (bad == 0);
+ struct list_head *head = inode_hashtable + hash(inode->i_sb, inode->i_ino);
+ list_add(&inode->i_hash, head);
}
-/*inline*/ void invalidate_inodes(kdev_t dev)
+void iput(struct inode *inode)
{
- /* Requires two passes, because of the new dcache holding
- * directories with i_count > 1.
- */
- (void)_check_inodes(dev, 0);
- (void)_check_inodes(dev, 1);
+ if (inode) {
+ struct super_operations *op = NULL;
+
+ if (inode->i_sb && inode->i_sb->s_op)
+ op = inode->i_sb->s_op;
+ if (op && op->put_inode)
+ op->put_inode(inode);
+
+ spin_lock(&inode_lock);
+ if (!--inode->i_count) {
+ if (!inode->i_nlink) {
+ list_del(&inode->i_hash);
+ INIT_LIST_HEAD(&inode->i_hash);
+ list_del(&inode->i_list);
+ INIT_LIST_HEAD(&inode->i_list);
+ if (op && op->delete_inode) {
+ void (*delete)(struct inode *) = op->delete_inode;
+ spin_unlock(&inode_lock);
+ delete(inode);
+ spin_lock(&inode_lock);
+ }
+ }
+ if (list_empty(&inode->i_hash)) {
+ list_del(&inode->i_list);
+ list_add(&inode->i_list, &inode_unused);
+ }
+ }
+ spin_unlock(&inode_lock);
+ }
}
-/*inline*/ int fs_may_mount(kdev_t dev)
+int bmap(struct inode * inode, int block)
{
- return _check_inodes(dev, 0);
+ if (inode->i_op && inode->i_op->bmap)
+ return inode->i_op->bmap(inode, block);
+ return 0;
}
-int fs_may_remount_ro(kdev_t dev)
+/*
+ * Initialize the hash tables
+ */
+void inode_init(void)
{
- (void)dev;
- return 1; /* not checked any more */
+ int i;
+ struct list_head *head = inode_hashtable;
+
+ i = HASH_SIZE;
+ do {
+ INIT_LIST_HEAD(head);
+ head++;
+ i--;
+ } while (i);
}
-int fs_may_umount(kdev_t dev, struct inode * mount_root)
+/*
+ * FIXME! These need to go through the in-use inodes to
+ * check whether we can mount/umount/remount.
+ */
+int fs_may_mount(kdev_t dev)
{
- 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)) {
- vfs_unlock();
- return 0;
- }
- inode = inode->i_next;
- } while(inode != all_i);
- vfs_unlock();
return 1;
}
-extern struct inode_operations pipe_inode_operations;
-
-blocking struct inode * get_pipe_inode(void)
+int fs_may_umount(struct super_block *sb, struct dentry * root)
{
- struct inode * inode = get_empty_inode();
-
- PIPE_BASE(*inode) = (char*)__get_free_page(GFP_USER);
- if(!(PIPE_BASE(*inode))) {
- iput(inode);
- return NULL;
- }
- inode->i_blksize = PAGE_SIZE;
- inode->i_pipe = 1;
- inode->i_mode = S_IFIFO | S_IRUSR | S_IWUSR;
- atomic_inc(&inode->i_count);
- inode->i_uid = current->fsuid;
- inode->i_gid = current->fsgid;
- inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
- 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;
+ shrink_dcache();
+ return root->d_count == 1;
}
-int bmap(struct inode * inode, int block)
+int fs_may_remount_ro(struct super_block *sb)
{
- if (inode->i_op && inode->i_op->bmap)
- return inode->i_op->bmap(inode, block);
- return 0;
+ return 1;
}
diff --git a/fs/ioctl.c b/fs/ioctl.c
index 6766506a8..11798a238 100644
--- a/fs/ioctl.c
+++ b/fs/ioctl.c
@@ -20,28 +20,27 @@ static int file_ioctl(struct file *filp,unsigned int cmd,unsigned long arg)
{
int error;
int block;
+ struct inode * inode = filp->f_dentry->d_inode;
switch (cmd) {
case FIBMAP:
- if (filp->f_inode->i_op == NULL)
+ if (inode->i_op == NULL)
return -EBADF;
- if (filp->f_inode->i_op->bmap == NULL)
+ if (inode->i_op->bmap == NULL)
return -EINVAL;
if ((error = get_user(block, (int *) arg)) != 0)
return error;
- block = filp->f_inode->i_op->bmap(filp->f_inode,block);
+ block = inode->i_op->bmap(inode,block);
return put_user(block, (int *) arg);
case FIGETBSZ:
- if (filp->f_inode->i_sb == NULL)
+ if (inode->i_sb == NULL)
return -EBADF;
- return put_user(filp->f_inode->i_sb->s_blocksize,
- (int *) arg);
+ return put_user(inode->i_sb->s_blocksize, (int *) arg);
case FIONREAD:
- return put_user(filp->f_inode->i_size - filp->f_pos,
- (int *) arg);
+ return put_user(inode->i_size - filp->f_pos, (int *) arg);
}
if (filp->f_op && filp->f_op->ioctl)
- return filp->f_op->ioctl(filp->f_inode, filp, cmd, arg);
+ return filp->f_op->ioctl(inode, filp, cmd, arg);
return -ENOTTY;
}
@@ -91,10 +90,10 @@ asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
break;
default:
- if (filp->f_inode && S_ISREG(filp->f_inode->i_mode))
+ if (filp->f_dentry && filp->f_dentry->d_inode && S_ISREG(filp->f_dentry->d_inode->i_mode))
error = file_ioctl(filp, cmd, arg);
else if (filp->f_op && filp->f_op->ioctl)
- error = filp->f_op->ioctl(filp->f_inode, filp, cmd, arg);
+ error = filp->f_op->ioctl(filp->f_dentry->d_inode, filp, cmd, arg);
else
error = -ENOTTY;
}
diff --git a/fs/isofs/dir.c b/fs/isofs/dir.c
index e22c3ca3b..48321d356 100644
--- a/fs/isofs/dir.c
+++ b/fs/isofs/dir.c
@@ -54,6 +54,7 @@ struct inode_operations isofs_dir_inode_operations =
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
isofs_bmap, /* bmap */
@@ -61,20 +62,6 @@ struct inode_operations isofs_dir_inode_operations =
NULL /* permission */
};
-static int parent_inode_number(struct inode * inode, struct iso_directory_record * de)
-{
- int inode_number = inode->i_ino;
-
- if ((inode->i_sb->u.isofs_sb.s_firstdatazone) != inode->i_ino)
- inode_number = inode->u.isofs_i.i_backlink;
-
- if (inode_number != -1)
- return inode_number;
-
- /* This should never happen, but who knows. Try to be forgiving */
- return isofs_lookup_grandparent(inode, find_rock_ridge_relocation(de, inode));
-}
-
static int isofs_name_translate(char * old, int len, char * new)
{
int i, c;
@@ -196,9 +183,7 @@ static int do_isofs_readdir(struct inode *inode, struct file *filp,
/* Handle the case of the '..' directory */
if (de->name_len[0] == 1 && de->name[0] == 1) {
- inode_number = parent_inode_number(inode, de);
- if (inode_number == -1)
- break;
+ inode_number = filp->f_dentry->d_parent->d_inode->i_ino;
if (filldir(dirent, "..", 2, filp->f_pos, inode_number) < 0)
break;
filp->f_pos += de_len;
diff --git a/fs/isofs/file.c b/fs/isofs/file.c
index 2742283f7..d14a558a0 100644
--- a/fs/isofs/file.c
+++ b/fs/isofs/file.c
@@ -47,6 +47,7 @@ struct inode_operations isofs_file_inode_operations = {
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
generic_readpage, /* readpage */
NULL, /* writepage */
isofs_bmap, /* bmap */
diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c
index d081a4cdd..436c22140 100644
--- a/fs/isofs/inode.c
+++ b/fs/isofs/inode.c
@@ -54,9 +54,10 @@ void isofs_put_super(struct super_block *sb)
static struct super_operations isofs_sops = {
isofs_read_inode,
- NULL, /* notify_change */
NULL, /* write_inode */
NULL, /* put_inode */
+ NULL, /* delete_inode */
+ NULL, /* notify_change */
isofs_put_super,
NULL, /* write_super */
isofs_statfs,
@@ -481,12 +482,12 @@ struct super_block *isofs_read_super(struct super_block *s,void *data,
s->u.isofs_sb.s_mode = opt.mode & 0777;
s->s_blocksize = opt.blocksize;
s->s_blocksize_bits = blocksize_bits;
- s->s_mounted = iget(s, (isonum_733(rootp->extent) +
+ s->s_root = d_alloc_root(iget(s, (isonum_733(rootp->extent) +
isonum_711(rootp->ext_attr_length))
- << s -> u.isofs_sb.s_log_zone_size);
+ << s -> u.isofs_sb.s_log_zone_size), NULL);
unlock_super(s);
- if (!(s->s_mounted)) {
+ if (!(s->s_root)) {
s->s_dev = 0;
printk("get root inode failed\n");
MOD_DEC_USE_COUNT;
@@ -504,7 +505,7 @@ struct super_block *isofs_read_super(struct super_block *s,void *data,
return NULL;
}
-void isofs_statfs (struct super_block *sb, struct statfs *buf, int bufsiz)
+int isofs_statfs (struct super_block *sb, struct statfs *buf, int bufsiz)
{
struct statfs tmp;
@@ -517,7 +518,7 @@ void isofs_statfs (struct super_block *sb, struct statfs *buf, int bufsiz)
tmp.f_files = sb->u.isofs_sb.s_ninodes;
tmp.f_ffree = 0;
tmp.f_namelen = NAME_MAX;
- copy_to_user(buf, &tmp, bufsiz);
+ return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
}
int isofs_bmap(struct inode * inode,int block)
@@ -663,7 +664,6 @@ void isofs_read_inode(struct inode * inode)
isonum_711 (raw_inode->ext_attr_length))
<< inode -> i_sb -> u.isofs_sb.s_log_zone_size;
- inode->u.isofs_i.i_backlink = 0xffffffff; /* Will be used for previous directory */
switch (inode->i_sb->u.isofs_sb.s_conversion){
case 'a':
inode->u.isofs_i.i_file_format = ISOFS_FILE_UNKNOWN; /* File type */
@@ -735,7 +735,6 @@ void isofs_read_inode(struct inode * inode)
/* With a data error we return this information */
inode->i_mtime = inode->i_atime = inode->i_ctime = 0;
inode->u.isofs_i.i_first_extent = 0;
- inode->u.isofs_i.i_backlink = 0xffffffff;
inode->i_size = 0;
inode->i_nlink = 1;
inode->i_uid = inode->i_gid = 0;
diff --git a/fs/isofs/namei.c b/fs/isofs/namei.c
index 155f4ae43..1c0be7fde 100644
--- a/fs/isofs/namei.c
+++ b/fs/isofs/namei.c
@@ -59,14 +59,13 @@ static int isofs_match(int len,const char * name, const char * compare, int dlen
* entry - you'll have to do that yourself if you want to.
*/
static struct buffer_head * isofs_find_entry(struct inode * dir,
- const char * name, int namelen, unsigned long * ino, unsigned long * ino_back)
+ const char * name, int namelen, unsigned long * ino)
{
unsigned long bufsize = ISOFS_BUFFER_SIZE(dir);
unsigned char bufbits = ISOFS_BUFFER_BITS(dir);
unsigned int block, i, f_pos, offset, inode_number;
struct buffer_head * bh;
unsigned int old_offset;
- unsigned int backlink;
int dlen, rrflag, match;
char * dpnt;
struct iso_directory_record * de;
@@ -86,7 +85,6 @@ static struct buffer_head * isofs_find_entry(struct inode * dir,
while (f_pos < dir->i_size) {
de = (struct iso_directory_record *) (bh->b_data + offset);
- backlink = dir->i_ino;
inode_number = (block << bufbits) + (offset & (bufsize - 1));
/* If byte is zero, this is the end of file, or time to move to
@@ -120,28 +118,6 @@ static struct buffer_head * isofs_find_entry(struct inode * dir,
return 0;
}
- /* Handle the '.' case */
-
- if (de->name[0]==0 && de->name_len[0]==1) {
- inode_number = dir->i_ino;
- backlink = 0;
- }
-
- /* Handle the '..' case */
-
- if (de->name[0]==1 && de->name_len[0]==1) {
-#if 0
- printk("Doing .. (%d %d)",
- dir->i_sb->s_firstdatazone,
- dir->i_ino);
-#endif
- if((dir->i_sb->u.isofs_sb.s_firstdatazone) != dir->i_ino)
- inode_number = dir->u.isofs_i.i_backlink;
- else
- inode_number = dir->i_ino;
- backlink = 0;
- }
-
dlen = de->name_len[0];
dpnt = de->name;
/* Now convert the filename in the buffer to lower case */
@@ -183,16 +159,8 @@ static struct buffer_head * isofs_find_entry(struct inode * dir,
inode_number =
isofs_lookup_grandparent(dir,
find_rock_ridge_relocation(de,dir));
- if(inode_number == -1){
- /* Should never happen */
- printk("Backlink not properly set %x %lx.\n",
- isonum_733(de->extent),
- dir->i_ino);
- goto out;
- }
}
*ino = inode_number;
- *ino_back = backlink;
return bh;
}
}
@@ -201,62 +169,48 @@ static struct buffer_head * isofs_find_entry(struct inode * dir,
return NULL;
}
-int isofs_lookup(struct inode * dir,const char * name, int len,
- struct inode ** result)
+int isofs_lookup(struct inode * dir, struct dentry * dentry)
{
- unsigned long ino, ino_back;
+ unsigned long ino;
struct buffer_head * bh;
char *lcname;
+ struct inode *inode;
#ifdef DEBUG
- printk("lookup: %x %d\n",dir->i_ino, len);
+ printk("lookup: %x %d\n",dir->i_ino, dentry->d_name.len);
#endif
- *result = NULL;
if (!dir)
return -ENOENT;
- if (!S_ISDIR(dir->i_mode)) {
- iput(dir);
+ if (!S_ISDIR(dir->i_mode))
return -ENOENT;
- }
/* If mounted with check=relaxed (and most likely norock),
* then first convert this name to lower case.
*/
if (dir->i_sb->u.isofs_sb.s_name_check == 'r' &&
- (lcname = kmalloc(len, GFP_KERNEL)) != NULL) {
+ (lcname = kmalloc(dentry->d_name.len, GFP_KERNEL)) != NULL) {
int i;
char c;
- for (i=0; i<len; i++) {
- c = name[i];
+ for (i=0; i<dentry->d_name.len; i++) {
+ c = dentry->d_name.name[i];
if (c >= 'A' && c <= 'Z') c |= 0x20;
lcname[i] = c;
}
- bh = isofs_find_entry(dir,lcname,len, &ino, &ino_back);
+ bh = isofs_find_entry(dir, lcname, dentry->d_name.len, &ino);
kfree(lcname);
} else
- bh = isofs_find_entry(dir,name,len, &ino, &ino_back);
+ bh = isofs_find_entry(dir, dentry->d_name.name, dentry->d_name.len, &ino);
- if (!bh) {
- iput(dir);
- return -ENOENT;
- }
- brelse(bh);
+ inode = NULL;
+ if (bh) {
+ brelse(bh);
- if (!(*result = iget(dir->i_sb,ino))) {
- iput(dir);
- return -EACCES;
+ inode = iget(dir->i_sb,ino);
+ if (!inode)
+ return -EACCES;
}
-
- /* We need this backlink for the ".." entry unless the name that we
- * are looking up traversed a mount point (in which case the inode
- * may not even be on an iso9660 filesystem, and writing to
- * u.isofs_i would only cause memory corruption).
- */
- if (ino_back && !(*result)->i_pipe && (*result)->i_sb == dir->i_sb)
- (*result)->u.isofs_i.i_backlink = ino_back;
-
- iput(dir);
+ d_add(dentry, inode);
return 0;
}
diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c
index 8c41d2f39..98814d220 100644
--- a/fs/lockd/clntlock.c
+++ b/fs/lockd/clntlock.c
@@ -172,7 +172,7 @@ reclaimer(void *ptr)
/* First, reclaim all locks that have been granted previously. */
do {
for (fl = file_lock_table; fl; fl = fl->fl_next) {
- inode = fl->fl_file->f_inode;
+ inode = fl->fl_file->f_dentry->d_inode;
if (inode->i_sb->s_magic == NFS_SUPER_MAGIC
&& nlm_cmp_addr(NFS_ADDR(inode), &host->h_addr)
&& fl->fl_u.nfs_fl.state != host->h_state
diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c
index 1506a2ba6..d219de5ea 100644
--- a/fs/lockd/clntproc.c
+++ b/fs/lockd/clntproc.c
@@ -42,7 +42,7 @@ nlmclnt_setlockargs(struct nlm_rqst *req, struct file_lock *fl)
memset(argp, 0, sizeof(*argp));
argp->cookie = nlm_cookie++;
argp->state = nsm_local_state;
- lock->fh = *NFS_FH(fl->fl_file->f_inode);
+ lock->fh = *NFS_FH(fl->fl_file->f_dentry->d_inode);
lock->caller = system_utsname.nodename;
lock->oh.data = req->a_owner;
lock->oh.len = sprintf(req->a_owner, "%d@%s",
diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c
index b4d74f745..69a9eeb21 100644
--- a/fs/lockd/svclock.c
+++ b/fs/lockd/svclock.c
@@ -274,8 +274,8 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
int error;
dprintk("lockd: nlmsvc_lock(%04x/%ld, ty=%d, pi=%d, %ld-%ld, bl=%d)\n",
- file->f_file.f_inode->i_dev,
- file->f_file.f_inode->i_ino,
+ file->f_file.f_dentry->d_inode->i_dev,
+ file->f_file.f_dentry->d_inode->i_ino,
lock->fl.fl_type, lock->fl.fl_pid,
lock->fl.fl_start,
lock->fl.fl_end,
@@ -344,8 +344,8 @@ nlmsvc_testlock(struct nlm_file *file, struct nlm_lock *lock,
struct file_lock *fl;
dprintk("lockd: nlmsvc_testlock(%04x/%ld, ty=%d, %ld-%ld)\n",
- file->f_file.f_inode->i_dev,
- file->f_file.f_inode->i_ino,
+ file->f_file.f_dentry->d_inode->i_dev,
+ file->f_file.f_dentry->d_inode->i_ino,
lock->fl.fl_type,
lock->fl.fl_start,
lock->fl.fl_end);
@@ -375,8 +375,8 @@ nlmsvc_unlock(struct nlm_file *file, struct nlm_lock *lock)
int error;
dprintk("lockd: nlmsvc_unlock(%04x/%ld, pi=%d, %ld-%ld)\n",
- file->f_file.f_inode->i_dev,
- file->f_file.f_inode->i_ino,
+ file->f_file.f_dentry->d_inode->i_dev,
+ file->f_file.f_dentry->d_inode->i_ino,
lock->fl.fl_pid,
lock->fl.fl_start,
lock->fl.fl_end);
@@ -403,8 +403,8 @@ nlmsvc_cancel_blocked(struct nlm_file *file, struct nlm_lock *lock)
struct nlm_block *block;
dprintk("lockd: nlmsvc_cancel(%04x/%ld, pi=%d, %ld-%ld)\n",
- file->f_file.f_inode->i_dev,
- file->f_file.f_inode->i_ino,
+ file->f_file.f_dentry->d_inode->i_dev,
+ file->f_file.f_dentry->d_inode->i_ino,
lock->fl.fl_pid,
lock->fl.fl_start,
lock->fl.fl_end);
diff --git a/fs/locks.c b/fs/locks.c
index 8ca8aa183..909c2eacb 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -297,25 +297,34 @@ int fcntl_getlk(unsigned int fd, struct flock *l)
{
struct flock flock;
struct file *filp;
+ struct dentry *dentry;
+ struct inode *inode;
struct file_lock *fl,file_lock;
int error;
if ((fd >= NR_OPEN) || !(filp = current->files->fd[fd]))
- return (-EBADF);
+ return -EBADF;
if (copy_from_user(&flock, l, sizeof(flock)))
- return (-EFAULT);
+ return -EFAULT;
if ((flock.l_type != F_RDLCK) && (flock.l_type != F_WRLCK))
- return (-EINVAL);
+ return -EINVAL;
- if (!filp->f_inode || !posix_make_lock(filp, &file_lock, &flock))
- return (-EINVAL);
+ dentry = filp->f_dentry;
+ if (!dentry)
+ return -EINVAL;
+
+ inode = dentry->d_inode;
+ if (!inode)
+ return -EINVAL;
+
+ if (!posix_make_lock(filp, &file_lock, &flock))
+ return -EINVAL;
if (filp->f_op->lock) {
- error = filp->f_op->lock(filp->f_inode, filp,
- F_GETLK, &file_lock);
+ error = filp->f_op->lock(inode, filp, F_GETLK, &file_lock);
if (error < 0)
- return (error);
+ return error;
fl = &file_lock;
} else {
fl = posix_test_lock(filp, &file_lock);
@@ -344,6 +353,7 @@ int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l)
struct file *filp;
struct file_lock file_lock;
struct flock flock;
+ struct dentry * dentry;
struct inode *inode;
int error;
@@ -351,10 +361,13 @@ int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l)
*/
if ((fd >= NR_OPEN) || !(filp = current->files->fd[fd]))
- return (-EBADF);
-
- if (!(inode = filp->f_inode))
- return (-EINVAL);
+ return -EBADF;
+
+ if (!(dentry = filp->f_dentry))
+ return -EINVAL;
+
+ if (!(inode = dentry->d_inode))
+ return -EINVAL;
/* Don't allow mandatory locks on files that may be memory mapped
* and shared.
@@ -407,7 +420,7 @@ int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l)
}
if (filp->f_op->lock != NULL) {
- error = filp->f_op->lock(filp->f_inode, filp, cmd, &file_lock);
+ error = filp->f_op->lock(inode, filp, cmd, &file_lock);
if (error < 0)
return (error);
}
@@ -421,12 +434,14 @@ void locks_remove_locks(struct task_struct *task, struct file *filp)
{
struct file_lock file_lock, *fl;
struct file_lock **before;
+ struct inode * inode;
/* For POSIX locks we free all locks on this file for the given task.
* For FLOCK we only free locks on this *open* file if it is the last
* close on that file.
*/
- before = &filp->f_inode->i_flock;
+ inode = filp->f_dentry->d_inode;
+ before = &inode->i_flock;
while ((fl = *before) != NULL) {
if (((fl->fl_flags & FL_POSIX) && (fl->fl_owner == task)) ||
@@ -436,10 +451,9 @@ void locks_remove_locks(struct task_struct *task, struct file *filp)
locks_delete_lock(before, 0);
if (filp->f_op->lock) {
file_lock.fl_type = F_UNLCK;
- filp->f_op->lock(filp->f_inode, filp,
- F_SETLK, &file_lock);
+ filp->f_op->lock(inode, filp, F_SETLK, &file_lock);
/* List may have changed: */
- before = &filp->f_inode->i_flock;
+ before = &inode->i_flock;
}
} else {
before = &fl->fl_next;
@@ -454,7 +468,7 @@ posix_test_lock(struct file *filp, struct file_lock *fl)
{
struct file_lock *cfl;
- for (cfl = filp->f_inode->i_flock; cfl; cfl = cfl->fl_next) {
+ for (cfl = filp->f_dentry->d_inode->i_flock; cfl; cfl = cfl->fl_next) {
if (!(cfl->fl_flags & FL_POSIX))
continue;
if (posix_locks_conflict(cfl, fl))
@@ -585,7 +599,7 @@ static int posix_make_lock(struct file *filp, struct file_lock *fl,
start = filp->f_pos;
break;
case 2: /*SEEK_END*/
- start = filp->f_inode->i_size;
+ start = filp->f_dentry->d_inode->i_size;
break;
default:
return (0);
@@ -612,7 +626,7 @@ static int flock_make_lock(struct file *filp, struct file_lock *fl,
{
memset(fl, 0, sizeof(*fl));
- if (!filp->f_inode) /* just in case */
+ if (!filp->f_dentry) /* just in case */
return (0);
switch (cmd & ~LOCK_NB) {
@@ -750,9 +764,10 @@ static int flock_lock_file(struct file *filp, struct file_lock *caller,
struct file_lock *fl;
struct file_lock *new_fl;
struct file_lock **before;
+ struct inode * inode = filp->f_dentry->d_inode;
int change = 0;
- before = &filp->f_inode->i_flock;
+ before = &inode->i_flock;
while (((fl = *before) != NULL) && (fl->fl_flags & FL_FLOCK)) {
if (caller->fl_file == fl->fl_file) {
if (caller->fl_type == fl->fl_type)
@@ -772,7 +787,7 @@ static int flock_lock_file(struct file *filp, struct file_lock *caller,
if ((new_fl = locks_alloc_lock(caller)) == NULL)
return (-ENOLCK);
repeat:
- for (fl = filp->f_inode->i_flock; (fl != NULL) && (fl->fl_flags & FL_FLOCK);
+ for (fl = inode->i_flock; (fl != NULL) && (fl->fl_flags & FL_FLOCK);
fl = fl->fl_next) {
if (!flock_locks_conflict(new_fl, fl))
continue;
@@ -801,7 +816,7 @@ repeat:
}
goto repeat;
}
- locks_insert_lock(&filp->f_inode->i_flock, new_fl);
+ locks_insert_lock(&inode->i_flock, new_fl);
return (0);
}
@@ -825,11 +840,12 @@ int posix_lock_file(struct file *filp, struct file_lock *caller,
struct file_lock *left = NULL;
struct file_lock *right = NULL;
struct file_lock **before;
+ struct inode * inode = filp->f_dentry->d_inode;
int added = 0;
if (caller->fl_type != F_UNLCK) {
repeat:
- for (fl = filp->f_inode->i_flock; fl != NULL; fl = fl->fl_next) {
+ for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
if (!(fl->fl_flags & FL_POSIX))
continue;
if (!posix_locks_conflict(caller, fl))
@@ -852,7 +868,7 @@ int posix_lock_file(struct file *filp, struct file_lock *caller,
/* Find the first old lock with the same owner as the new lock.
*/
- before = &filp->f_inode->i_flock;
+ before = &inode->i_flock;
/* First skip locks owned by other processes.
*/
@@ -1054,7 +1070,7 @@ static char *lock_get_status(struct file_lock *fl, int id, char *pfx)
char *p = temp;
struct inode *inode;
- inode = fl->fl_file->f_inode;
+ inode = fl->fl_file->f_dentry->d_inode;
p += sprintf(p, "%d:%s ", id, pfx);
if (fl->fl_flags & FL_POSIX) {
diff --git a/fs/minix/bitmap.c b/fs/minix/bitmap.c
index 81ac9b047..f47f779d5 100644
--- a/fs/minix/bitmap.c
+++ b/fs/minix/bitmap.c
@@ -191,8 +191,8 @@ void minix_free_inode(struct inode * inode)
printk("free_inode: inode has no device\n");
return;
}
- if (atomic_read(&inode->i_count) != 1) {
- printk("free_inode: inode has count=%d\n",atomic_read(&inode->i_count));
+ if (inode->i_count != 1) {
+ printk("free_inode: inode has count=%d\n",inode->i_count);
return;
}
if (inode->i_nlink) {
@@ -251,12 +251,12 @@ struct inode * minix_new_inode(const struct inode * dir)
iput(inode);
return NULL;
}
- atomic_set(&inode->i_count, 1);
+ inode->i_count = 1;
inode->i_nlink = 1;
inode->i_dev = sb->s_dev;
inode->i_uid = current->fsuid;
inode->i_gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
inode->i_ino = j;
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
inode->i_op = NULL;
diff --git a/fs/minix/dir.c b/fs/minix/dir.c
index 439005f4e..ec5113c4a 100644
--- a/fs/minix/dir.c
+++ b/fs/minix/dir.c
@@ -50,6 +50,7 @@ struct inode_operations minix_dir_inode_operations = {
minix_mknod, /* mknod */
minix_rename, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
diff --git a/fs/minix/file.c b/fs/minix/file.c
index 86cbca2b2..7ca7cb075 100644
--- a/fs/minix/file.c
+++ b/fs/minix/file.c
@@ -58,6 +58,7 @@ struct inode_operations minix_file_inode_operations = {
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
generic_readpage, /* readpage */
NULL, /* writepage */
minix_bmap, /* bmap */
@@ -120,6 +121,6 @@ static long minix_file_write(struct inode * inode, struct file * filp,
inode->i_size = pos;
inode->i_mtime = inode->i_ctime = CURRENT_TIME;
filp->f_pos = pos;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
return written;
}
diff --git a/fs/minix/inode.c b/fs/minix/inode.c
index cbd735ef1..898f56f19 100644
--- a/fs/minix/inode.c
+++ b/fs/minix/inode.c
@@ -24,10 +24,8 @@
#include <asm/uaccess.h>
#include <asm/bitops.h>
-void minix_put_inode(struct inode *inode)
+static void minix_delete_inode(struct inode *inode)
{
- if (inode->i_nlink)
- return;
inode->i_size = 0;
minix_truncate(inode);
minix_free_inode(inode);
@@ -77,9 +75,10 @@ void minix_put_super(struct super_block *sb)
static struct super_operations minix_sops = {
minix_read_inode,
- NULL,
minix_write_inode,
- minix_put_inode,
+ NULL, /* put_inode */
+ minix_delete_inode,
+ NULL, /* notify_change */
minix_put_super,
minix_write_super,
minix_statfs,
@@ -125,15 +124,13 @@ int minix_remount (struct super_block * sb, int * flags, char * data)
* it really _is_ a minix filesystem, and to check the size
* of the directory entry.
*/
-static const char * minix_checkroot(struct super_block *s)
+static const char * minix_checkroot(struct super_block *s, struct inode *dir)
{
- struct inode * dir;
struct buffer_head *bh;
struct minix_dir_entry *de;
const char * errmsg;
int dirsize;
- dir = s->s_mounted;
if (!S_ISDIR(dir->i_mode))
return "root directory is not a directory";
@@ -172,7 +169,8 @@ struct super_block *minix_read_super(struct super_block *s,void *data,
int i, block;
kdev_t dev = s->s_dev;
const char * errmsg;
-
+ struct inode *root_inode;
+
if (32 != sizeof (struct minix_inode))
panic("bad V1 i-node size");
if (64 != sizeof(struct minix2_inode))
@@ -272,8 +270,9 @@ struct super_block *minix_read_super(struct super_block *s,void *data,
/* set up enough so that it can read an inode */
s->s_dev = dev;
s->s_op = &minix_sops;
- s->s_mounted = iget(s,MINIX_ROOT_INO);
- if (!s->s_mounted) {
+ root_inode = iget(s,MINIX_ROOT_INO);
+ s->s_root = d_alloc_root(root_inode, NULL);
+ if (!s->s_root) {
s->s_dev = 0;
brelse(bh);
if (!silent)
@@ -282,11 +281,11 @@ struct super_block *minix_read_super(struct super_block *s,void *data,
return NULL;
}
- errmsg = minix_checkroot(s);
+ errmsg = minix_checkroot(s, root_inode);
if (errmsg) {
if (!silent)
printk("MINIX-fs: %s\n", errmsg);
- iput (s->s_mounted);
+ d_delete(s->s_root); /* XXX Is this enough? */
s->s_dev = 0;
brelse (bh);
MOD_DEC_USE_COUNT;
@@ -307,7 +306,7 @@ struct super_block *minix_read_super(struct super_block *s,void *data,
return s;
}
-void minix_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
+int minix_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
{
struct statfs tmp;
@@ -319,7 +318,7 @@ void minix_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
tmp.f_files = sb->u.minix_sb.s_ninodes;
tmp.f_ffree = minix_count_free_inodes(sb);
tmp.f_namelen = sb->u.minix_sb.s_namelen;
- copy_to_user(buf, &tmp, bufsiz);
+ return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
}
/*
@@ -472,7 +471,7 @@ repeat:
}
*p = tmp;
inode->i_ctime = CURRENT_TIME;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
return result;
}
@@ -585,7 +584,7 @@ repeat:
}
*p = tmp;
inode->i_ctime = CURRENT_TIME;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
return result;
}
@@ -833,14 +832,12 @@ static struct buffer_head * V1_minix_update_inode(struct inode * inode)
printk("Bad inode number on dev %s"
": %d is out of range\n",
kdevname(inode->i_dev), ino);
- inode->i_dirt = 0;
return 0;
}
block = 2 + inode->i_sb->u.minix_sb.s_imap_blocks + inode->i_sb->u.minix_sb.s_zmap_blocks +
(ino-1)/MINIX_INODES_PER_BLOCK;
if (!(bh=bread(inode->i_dev, block, BLOCK_SIZE))) {
printk("unable to read i-node block\n");
- inode->i_dirt = 0;
return 0;
}
raw_inode = ((struct minix_inode *)bh->b_data) +
@@ -855,7 +852,6 @@ static struct buffer_head * V1_minix_update_inode(struct inode * inode)
raw_inode->i_zone[0] = kdev_t_to_nr(inode->i_rdev);
else for (block = 0; block < 9; block++)
raw_inode->i_zone[block] = inode->u.minix_i.u.i1_data[block];
- inode->i_dirt=0;
mark_buffer_dirty(bh, 1);
return bh;
}
@@ -874,14 +870,12 @@ static struct buffer_head * V2_minix_update_inode(struct inode * inode)
printk("Bad inode number on dev %s"
": %d is out of range\n",
kdevname(inode->i_dev), ino);
- inode->i_dirt = 0;
return 0;
}
block = 2 + inode->i_sb->u.minix_sb.s_imap_blocks + inode->i_sb->u.minix_sb.s_zmap_blocks +
(ino-1)/MINIX2_INODES_PER_BLOCK;
if (!(bh=bread(inode->i_dev, block, BLOCK_SIZE))) {
printk("unable to read i-node block\n");
- inode->i_dirt = 0;
return 0;
}
raw_inode = ((struct minix2_inode *)bh->b_data) +
@@ -898,7 +892,6 @@ static struct buffer_head * V2_minix_update_inode(struct inode * inode)
raw_inode->i_zone[0] = kdev_t_to_nr(inode->i_rdev);
else for (block = 0; block < 10; block++)
raw_inode->i_zone[block] = inode->u.minix_i.u.i2_data[block];
- inode->i_dirt=0;
mark_buffer_dirty(bh, 1);
return bh;
}
diff --git a/fs/minix/namei.c b/fs/minix/namei.c
index b6041ad92..718d3dd07 100644
--- a/fs/minix/namei.c
+++ b/fs/minix/namei.c
@@ -47,9 +47,6 @@ static int minix_match(int len, const char * name,
*offset += info->s_dirsize;
if (!de->inode || len > info->s_namelen)
return 0;
- /* "" means "." ---> so paths like "/usr/lib//libc.a" work */
- if (!len && (de->name[0]=='.') && (de->name[1]=='\0'))
- return 1;
return namecompare(len,info->s_namelen,name,de->name);
}
@@ -104,31 +101,22 @@ static struct buffer_head * minix_find_entry(struct inode * dir,
return NULL;
}
-int minix_lookup(struct inode * dir,const char * name, int len,
- struct inode ** result)
+int minix_lookup(struct inode * dir, struct dentry *dentry)
{
- int ino;
+ struct inode * inode = NULL;
struct minix_dir_entry * de;
struct buffer_head * bh;
- *result = NULL;
- if (!dir)
- return -ENOENT;
- if (!S_ISDIR(dir->i_mode)) {
- iput(dir);
- return -ENOENT;
- }
- if (!(bh = minix_find_entry(dir,name,len,&de))) {
- iput(dir);
- return -ENOENT;
- }
- ino = de->inode;
- brelse(bh);
- if (!(*result = iget(dir->i_sb,ino))) {
- iput(dir);
- return -EACCES;
- }
- iput(dir);
+ bh = minix_find_entry(dir, dentry->d_name.name, dentry->d_name.len, &de);
+ if (bh) {
+ unsigned long ino = le32_to_cpu(de->inode);
+ brelse (bh);
+ inode = iget(dir->i_sb, ino);
+
+ if (!inode)
+ return -EACCES;
+ }
+ d_add(dentry, inode);
return 0;
}
@@ -180,7 +168,7 @@ static int minix_add_entry(struct inode * dir,
if (block*bh->b_size + offset > dir->i_size) {
de->inode = 0;
dir->i_size = block*bh->b_size + offset;
- dir->i_dirt = 1;
+ mark_inode_dirty(dir);
}
if (de->inode) {
if (namecompare(namelen, info->s_namelen, name, de->name)) {
@@ -189,7 +177,7 @@ static int minix_add_entry(struct inode * dir,
}
} else {
dir->i_mtime = dir->i_ctime = CURRENT_TIME;
- dir->i_dirt = 1;
+ mark_inode_dirty(dir);
for (i = 0; i < info->s_namelen ; i++)
de->name[i] = (i < namelen) ? name[i] : 0;
dir->i_version = ++event;
@@ -208,42 +196,37 @@ static int minix_add_entry(struct inode * dir,
return 0;
}
-int minix_create(struct inode * dir,const char * name, int len, int mode,
- struct inode ** result)
+int minix_create(struct inode * dir, struct dentry *dentry, int mode)
{
int error;
struct inode * inode;
struct buffer_head * bh;
struct minix_dir_entry * de;
- *result = NULL;
if (!dir)
return -ENOENT;
inode = minix_new_inode(dir);
- if (!inode) {
- iput(dir);
+ if (!inode)
return -ENOSPC;
- }
inode->i_op = &minix_file_inode_operations;
inode->i_mode = mode;
- inode->i_dirt = 1;
- error = minix_add_entry(dir,name,len, &bh ,&de);
+ mark_inode_dirty(inode);
+ error = minix_add_entry(dir, dentry->d_name.name,
+ dentry->d_name.len, &bh ,&de);
if (error) {
inode->i_nlink--;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
iput(inode);
- iput(dir);
return error;
}
de->inode = inode->i_ino;
mark_buffer_dirty(bh, 1);
brelse(bh);
- iput(dir);
- *result = inode;
+ d_instantiate(dentry, inode);
return 0;
}
-int minix_mknod(struct inode * dir, const char * name, int len, int mode, int rdev)
+int minix_mknod(struct inode * dir, struct dentry *dentry, int mode, int rdev)
{
int error;
struct inode * inode;
@@ -252,17 +235,15 @@ int minix_mknod(struct inode * dir, const char * name, int len, int mode, int rd
if (!dir)
return -ENOENT;
- bh = minix_find_entry(dir,name,len,&de);
+ bh = minix_find_entry(dir, dentry->d_name.name,
+ dentry->d_name.len, &de);
if (bh) {
brelse(bh);
- iput(dir);
return -EEXIST;
}
inode = minix_new_inode(dir);
- if (!inode) {
- iput(dir);
+ if (!inode)
return -ENOSPC;
- }
inode->i_uid = current->fsuid;
inode->i_mode = mode;
inode->i_op = NULL;
@@ -283,24 +264,22 @@ int minix_mknod(struct inode * dir, const char * name, int len, int mode, int rd
init_fifo(inode);
if (S_ISBLK(mode) || S_ISCHR(mode))
inode->i_rdev = to_kdev_t(rdev);
- inode->i_dirt = 1;
- error = minix_add_entry(dir, name, len, &bh, &de);
+ mark_inode_dirty(inode);
+ error = minix_add_entry(dir, dentry->d_name.name, dentry->d_name.len, &bh, &de);
if (error) {
inode->i_nlink--;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
iput(inode);
- iput(dir);
return error;
}
de->inode = inode->i_ino;
mark_buffer_dirty(bh, 1);
brelse(bh);
- iput(dir);
- iput(inode);
+ d_instantiate(dentry, inode);
return 0;
}
-int minix_mkdir(struct inode * dir, const char * name, int len, int mode)
+int minix_mkdir(struct inode * dir, struct dentry *dentry, int mode)
{
int error;
struct inode * inode;
@@ -308,33 +287,26 @@ int minix_mkdir(struct inode * dir, const char * name, int len, int mode)
struct minix_dir_entry * de;
struct minix_sb_info * info;
- if (!dir || !dir->i_sb) {
- iput(dir);
+ if (!dir || !dir->i_sb)
return -EINVAL;
- }
info = &dir->i_sb->u.minix_sb;
- bh = minix_find_entry(dir,name,len,&de);
+ bh = minix_find_entry(dir, dentry->d_name.name,
+ dentry->d_name.len, &de);
if (bh) {
brelse(bh);
- iput(dir);
return -EEXIST;
}
- if (dir->i_nlink >= MINIX_LINK_MAX) {
- iput(dir);
+ if (dir->i_nlink >= MINIX_LINK_MAX)
return -EMLINK;
- }
inode = minix_new_inode(dir);
- if (!inode) {
- iput(dir);
+ if (!inode)
return -ENOSPC;
- }
inode->i_op = &minix_dir_inode_operations;
inode->i_size = 2 * info->s_dirsize;
dir_block = minix_bread(inode,0,1);
if (!dir_block) {
- iput(dir);
inode->i_nlink--;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
iput(inode);
return -ENOSPC;
}
@@ -350,10 +322,10 @@ int minix_mkdir(struct inode * dir, const char * name, int len, int mode)
inode->i_mode = S_IFDIR | (mode & 0777 & ~current->fs->umask);
if (dir->i_mode & S_ISGID)
inode->i_mode |= S_ISGID;
- inode->i_dirt = 1;
- error = minix_add_entry(dir, name, len, &bh, &de);
+ mark_inode_dirty(inode);
+ error = minix_add_entry(dir, dentry->d_name.name,
+ dentry->d_name.len, &bh, &de);
if (error) {
- iput(dir);
inode->i_nlink=0;
iput(inode);
return error;
@@ -361,10 +333,9 @@ int minix_mkdir(struct inode * dir, const char * name, int len, int mode)
de->inode = inode->i_ino;
mark_buffer_dirty(bh, 1);
dir->i_nlink++;
- dir->i_dirt = 1;
- iput(dir);
- iput(inode);
+ mark_inode_dirty(dir);
brelse(bh);
+ d_instantiate(dentry, inode);
return 0;
}
@@ -427,7 +398,7 @@ bad_dir:
return 1;
}
-int minix_rmdir(struct inode * dir, const char * name, int len)
+int minix_rmdir(struct inode * dir, struct dentry *dentry)
{
int retval;
struct inode * inode;
@@ -435,13 +406,14 @@ int minix_rmdir(struct inode * dir, const char * name, int len)
struct minix_dir_entry * de;
inode = NULL;
- bh = minix_find_entry(dir,name,len,&de);
+ bh = minix_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, de->inode)))
- goto end_rmdir;
+ inode = dentry->d_inode;
+
if ((dir->i_mode & S_ISVTX) && !fsuser() &&
current->fsuid != inode->i_uid &&
current->fsuid != dir->i_uid)
@@ -462,7 +434,7 @@ int minix_rmdir(struct inode * dir, const char * name, int len)
retval = -ENOENT;
goto end_rmdir;
}
- if (atomic_read(&inode->i_count) > 1) {
+ if (inode->i_count > 1) {
retval = -EBUSY;
goto end_rmdir;
}
@@ -472,19 +444,18 @@ int minix_rmdir(struct inode * dir, const char * name, int len)
dir->i_version = ++event;
mark_buffer_dirty(bh, 1);
inode->i_nlink=0;
- inode->i_dirt=1;
+ mark_inode_dirty(inode);
inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
dir->i_nlink--;
- dir->i_dirt=1;
+ mark_inode_dirty(dir);
+ d_delete(dentry);
retval = 0;
end_rmdir:
- iput(dir);
- iput(inode);
brelse(bh);
return retval;
}
-int minix_unlink(struct inode * dir, const char * name, int len)
+int minix_unlink(struct inode * dir, struct dentry *dentry)
{
int retval;
struct inode * inode;
@@ -494,16 +465,16 @@ int minix_unlink(struct inode * dir, const char * name, int len)
repeat:
retval = -ENOENT;
inode = NULL;
- bh = minix_find_entry(dir,name,len,&de);
+ bh = minix_find_entry(dir, dentry->d_name.name,
+ dentry->d_name.len, &de);
if (!bh)
goto end_unlink;
- if (!(inode = iget(dir->i_sb, de->inode)))
- goto end_unlink;
+ inode = dentry->d_inode;
+
retval = -EPERM;
if (S_ISDIR(inode->i_mode))
goto end_unlink;
if (de->inode != inode->i_ino) {
- iput(inode);
brelse(bh);
current->counter = 0;
schedule();
@@ -527,19 +498,19 @@ repeat:
dir->i_version = ++event;
mark_buffer_dirty(bh, 1);
dir->i_ctime = dir->i_mtime = CURRENT_TIME;
- dir->i_dirt = 1;
+ mark_inode_dirty(dir);
inode->i_nlink--;
inode->i_ctime = dir->i_ctime;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
+ d_delete(dentry); /* This also frees the inode */
retval = 0;
end_unlink:
brelse(bh);
- iput(inode);
- iput(dir);
return retval;
}
-int minix_symlink(struct inode * dir, const char * name, int len, const char * symname)
+int minix_symlink(struct inode * dir, struct dentry *dentry,
+ const char * symname)
{
struct minix_dir_entry * de;
struct inode * inode = NULL;
@@ -547,17 +518,15 @@ int minix_symlink(struct inode * dir, const char * name, int len, const char * s
int i;
char c;
- if (!(inode = minix_new_inode(dir))) {
- iput(dir);
+ if (!(inode = minix_new_inode(dir)))
return -ENOSPC;
- }
+
inode->i_mode = S_IFLNK | 0777;
inode->i_op = &minix_symlink_inode_operations;
name_block = minix_bread(inode,0,1);
if (!name_block) {
- iput(dir);
inode->i_nlink--;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
iput(inode);
return -ENOSPC;
}
@@ -568,93 +537,81 @@ int minix_symlink(struct inode * dir, const char * name, int len, const char * s
mark_buffer_dirty(name_block, 1);
brelse(name_block);
inode->i_size = i;
- inode->i_dirt = 1;
- bh = minix_find_entry(dir,name,len,&de);
+ mark_inode_dirty(inode);
+ bh = minix_find_entry(dir, dentry->d_name.name,
+ dentry->d_name.len, &de);
if (bh) {
inode->i_nlink--;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
iput(inode);
brelse(bh);
- iput(dir);
return -EEXIST;
}
- i = minix_add_entry(dir, name, len, &bh, &de);
+ i = minix_add_entry(dir, dentry->d_name.name,
+ dentry->d_name.len, &bh, &de);
if (i) {
inode->i_nlink--;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
iput(inode);
- iput(dir);
return i;
}
de->inode = inode->i_ino;
mark_buffer_dirty(bh, 1);
brelse(bh);
- iput(dir);
- iput(inode);
+ d_instantiate(dentry, inode);
return 0;
}
-int minix_link(struct inode * oldinode, struct inode * dir, const char * name, int len)
+int minix_link(struct inode * inode, struct inode * dir,
+ struct dentry *dentry)
{
int error;
struct minix_dir_entry * de;
struct buffer_head * bh;
- if (S_ISDIR(oldinode->i_mode)) {
- iput(oldinode);
- iput(dir);
+ if (S_ISDIR(inode->i_mode))
return -EPERM;
- }
- if (oldinode->i_nlink >= MINIX_LINK_MAX) {
- iput(oldinode);
- iput(dir);
+
+ if (inode->i_nlink >= MINIX_LINK_MAX)
return -EMLINK;
- }
- bh = minix_find_entry(dir,name,len,&de);
+
+ bh = minix_find_entry(dir, dentry->d_name.name,
+ dentry->d_name.len, &de);
if (bh) {
brelse(bh);
- iput(dir);
- iput(oldinode);
return -EEXIST;
}
- error = minix_add_entry(dir, name, len, &bh, &de);
+ error = minix_add_entry(dir, dentry->d_name.name,
+ dentry->d_name.len, &bh, &de);
if (error) {
- iput(dir);
- iput(oldinode);
+ brelse(bh);
return error;
}
- de->inode = oldinode->i_ino;
+ de->inode = inode->i_ino;
mark_buffer_dirty(bh, 1);
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;
+ mark_inode_dirty(inode);
+ d_instantiate(dentry, inode);
return 0;
}
-static int subdir(struct inode * new_inode, struct inode * old_inode)
+static int subdir(struct dentry * new_dentry, struct dentry * old_dentry)
{
- int ino;
- int result;
+ int result = 0;
- atomic_inc(&new_inode->i_count);
- result = 0;
for (;;) {
- if (new_inode == old_inode) {
- result = 1;
- break;
+ if (new_dentry != old_dentry) {
+ struct dentry * parent = new_dentry->d_parent;
+ if (parent == new_dentry)
+ break;
+ new_dentry = parent;
+ continue;
}
- if (new_inode->i_dev != old_inode->i_dev)
- break;
- ino = new_inode->i_ino;
- if (minix_lookup(new_inode,"..",2,&new_inode))
- break;
- if (new_inode->i_ino == ino)
- break;
+ result = 1;
+ break;
}
- iput(new_inode);
return result;
}
@@ -671,8 +628,8 @@ 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_minix_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_minix_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;
@@ -686,28 +643,26 @@ try_again:
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;
- old_bh = minix_find_entry(old_dir,old_name,old_len,&old_de);
+ old_bh = minix_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, 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 &&
current->fsuid != old_dir->i_uid && !fsuser())
goto end_rename;
- new_bh = minix_find_entry(new_dir,new_name,new_len,&new_de);
+ new_inode = new_dentry->d_inode;
+ new_bh = minix_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, new_de->inode, 0);
if (!new_inode) {
brelse(new_bh);
new_bh = NULL;
@@ -722,13 +677,13 @@ start_up:
if (!S_ISDIR(old_inode->i_mode))
goto end_rename;
retval = -EINVAL;
- if (subdir(new_dir, old_inode))
+ if (subdir(new_dentry, old_dentry))
goto end_rename;
retval = -ENOTEMPTY;
if (!empty_dir(new_inode))
goto end_rename;
retval = -EBUSY;
- if (atomic_read(&new_inode->i_count) > 1)
+ if (new_inode->i_count > 1)
goto end_rename;
}
retval = -EPERM;
@@ -741,7 +696,7 @@ start_up:
if (new_inode && !S_ISDIR(new_inode->i_mode))
goto end_rename;
retval = -EINVAL;
- if (subdir(new_dir, old_inode))
+ if (subdir(new_dentry, old_dentry))
goto end_rename;
retval = -EIO;
dir_bh = minix_bread(old_inode,0,0);
@@ -754,7 +709,10 @@ start_up:
goto end_rename;
}
if (!new_bh) {
- retval = minix_add_entry(new_dir,new_name,new_len,&new_bh,&new_de);
+ retval = minix_add_entry(new_dir,
+ new_dentry->d_name.name,
+ new_dentry->d_name.len,
+ &new_bh, &new_de);
if (retval)
goto end_rename;
}
@@ -769,15 +727,15 @@ start_up:
old_de->inode = 0;
new_de->inode = old_inode->i_ino;
old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
- old_dir->i_dirt = 1;
+ mark_inode_dirty(old_dir);
old_dir->i_version = ++event;
new_dir->i_ctime = new_dir->i_mtime = CURRENT_TIME;
- new_dir->i_dirt = 1;
+ mark_inode_dirty(new_dir);
new_dir->i_version = ++event;
if (new_inode) {
new_inode->i_nlink--;
new_inode->i_ctime = CURRENT_TIME;
- new_inode->i_dirt = 1;
+ mark_inode_dirty(new_inode);
}
mark_buffer_dirty(old_bh, 1);
mark_buffer_dirty(new_bh, 1);
@@ -785,24 +743,23 @@ start_up:
PARENT_INO(dir_bh->b_data) = new_dir->i_ino;
mark_buffer_dirty(dir_bh, 1);
old_dir->i_nlink--;
- old_dir->i_dirt = 1;
+ mark_inode_dirty(old_dir);
if (new_inode) {
new_inode->i_nlink--;
- new_inode->i_dirt = 1;
+ mark_inode_dirty(new_inode);
} else {
new_dir->i_nlink++;
- new_dir->i_dirt = 1;
+ mark_inode_dirty(new_dir);
}
}
+ /* 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;
}
@@ -815,8 +772,8 @@ end_rename:
* the same device that races occur: many renames can happen at once, as long
* as they are on different partitions.
*/
-int minix_rename(struct inode * old_dir, const char * old_name, int old_len,
- struct inode * new_dir, const char * new_name, int new_len)
+int minix_rename(struct inode * old_dir, struct dentry *old_dentry,
+ struct inode * new_dir, struct dentry *new_dentry)
{
static struct wait_queue * wait = NULL;
static int lock = 0;
@@ -825,8 +782,8 @@ int minix_rename(struct inode * old_dir, const char * old_name, int old_len,
while (lock)
sleep_on(&wait);
lock = 1;
- result = do_minix_rename(old_dir, old_name, old_len,
- new_dir, new_name, new_len);
+ result = do_minix_rename(old_dir, old_dentry,
+ new_dir, new_dentry);
lock = 0;
wake_up(&wait);
return result;
diff --git a/fs/minix/symlink.c b/fs/minix/symlink.c
index 92539cded..9f759ecc9 100644
--- a/fs/minix/symlink.c
+++ b/fs/minix/symlink.c
@@ -15,6 +15,7 @@
#include <asm/uaccess.h>
static int minix_readlink(struct inode *, char *, int);
+static struct dentry *minix_follow_link(struct inode *, struct dentry *);
/*
* symlinks can't do much...
@@ -31,6 +32,7 @@ struct inode_operations minix_symlink_inode_operations = {
NULL, /* mknod */
NULL, /* rename */
minix_readlink, /* readlink */
+ minix_follow_link, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
@@ -38,6 +40,21 @@ struct inode_operations minix_symlink_inode_operations = {
NULL /* permission */
};
+static struct dentry * minix_follow_link(struct inode * inode, struct dentry * base)
+{
+ struct buffer_head * bh;
+
+ bh = minix_bread(inode, 0, 0);
+ if (!bh) {
+ dput(base);
+ return ERR_PTR(-EIO);
+ }
+ UPDATE_ATIME(inode);
+ base = lookup_dentry(bh->b_data, base, 1);
+ brelse(bh);
+ return base;
+}
+
static int minix_readlink(struct inode * inode, char * buffer, int buflen)
{
struct buffer_head * bh;
@@ -47,7 +64,6 @@ static int minix_readlink(struct inode * inode, char * buffer, int buflen)
if (buflen > 1023)
buflen = 1023;
bh = minix_bread(inode, 0, 0);
- iput(inode);
if (!bh)
return 0;
i = 0;
diff --git a/fs/minix/truncate.c b/fs/minix/truncate.c
index a6d3d4b5e..298d6c155 100644
--- a/fs/minix/truncate.c
+++ b/fs/minix/truncate.c
@@ -58,7 +58,7 @@ repeat:
continue;
}
*p = 0;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
if (bh) {
mark_buffer_clean(bh);
brelse(bh);
@@ -167,7 +167,7 @@ repeat:
else {
tmp = *p;
*p = 0;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
minix_free_block(inode->i_sb,tmp);
}
brelse(dind_bh);
@@ -191,7 +191,7 @@ void V1_minix_truncate(struct inode * inode)
schedule();
}
inode->i_mtime = inode->i_ctime = CURRENT_TIME;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
}
/*
@@ -220,7 +220,7 @@ repeat:
continue;
}
*p = 0;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
if (bh) {
mark_buffer_clean(bh);
brelse(bh);
@@ -329,7 +329,7 @@ repeat:
else {
tmp = *p;
*p = 0;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
minix_free_block(inode->i_sb,tmp);
}
brelse(dind_bh);
@@ -374,7 +374,7 @@ repeat:
else {
tmp = *p;
*p = 0;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
minix_free_block(inode->i_sb,tmp);
}
brelse(tind_bh);
@@ -402,7 +402,7 @@ static void V2_minix_truncate(struct inode * inode)
schedule();
}
inode->i_mtime = inode->i_ctime = CURRENT_TIME;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
}
/*
diff --git a/fs/msdos/namei.c b/fs/msdos/namei.c
index bcf6782d0..9481a763c 100644
--- a/fs/msdos/namei.c
+++ b/fs/msdos/namei.c
@@ -280,7 +280,7 @@ static int msdos_create_entry(struct inode *dir, const char *name,int len,
* XXX all times should be set by caller upon successful completion.
*/
dir->i_ctime = dir->i_mtime = CURRENT_TIME;
- dir->i_dirt = 1;
+ mark_inode_dirty(dir);
memcpy(de->name,name,MSDOS_NAME);
memset(de->unused, 0, sizeof(de->unused));
de->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
@@ -295,7 +295,7 @@ static int msdos_create_entry(struct inode *dir, const char *name,int len,
if (!*result) return -EIO;
(*result)->i_mtime = (*result)->i_atime = (*result)->i_ctime =
CURRENT_TIME;
- (*result)->i_dirt = 1;
+ mark_inode_dirty(*result);
return 0;
}
@@ -369,7 +369,7 @@ static int msdos_empty(struct inode *dir)
struct buffer_head *bh;
struct msdos_dir_entry *de;
- if (atomic_read(&dir->i_count) > 1)
+ if (dir->i_count > 1)
return -EBUSY;
if (MSDOS_I(dir)->i_start) { /* may be zero in mkdir */
pos = 0;
@@ -415,7 +415,8 @@ int msdos_rmdir(struct inode *dir,const char *name,int len)
inode->i_nlink = 0;
inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
dir->i_nlink--;
- inode->i_dirt = dir->i_dirt = 1;
+ mark_inode_dirty(inode);
+ mark_inode_dirty(dir);
de->name[0] = DELETED_FLAG;
fat_mark_buffer_dirty(sb, bh, 1);
res = 0;
@@ -465,7 +466,7 @@ int msdos_mkdir(struct inode *dir,const char *name,int len,int mode)
dot->i_size = inode->i_size; /* doesn't grow in the 2nd create_entry */
MSDOS_I(dot)->i_start = MSDOS_I(inode)->i_start;
dot->i_nlink = inode->i_nlink;
- dot->i_dirt = 1;
+ mark_inode_dirty(dot);
iput(dot);
if ((res = msdos_create_entry(inode,MSDOS_DOTDOT,2,1,0,&dot)) < 0)
goto mkdir_error;
@@ -473,7 +474,7 @@ int msdos_mkdir(struct inode *dir,const char *name,int len,int mode)
dot->i_size = dir->i_size;
MSDOS_I(dot)->i_start = MSDOS_I(dir)->i_start;
dot->i_nlink = dir->i_nlink;
- dot->i_dirt = 1;
+ mark_inode_dirty(dot);
MSDOS_I(inode)->i_busy = 0;
iput(dot);
iput(inode);
@@ -519,7 +520,8 @@ static int msdos_unlinkx(
inode->i_nlink = 0;
inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
MSDOS_I(inode)->i_busy = 1;
- inode->i_dirt = dir->i_dirt = 1;
+ mark_inode_dirty(inode);
+ mark_inode_dirty(dir);
de->name[0] = DELETED_FLAG;
fat_mark_buffer_dirty(sb, bh, 1);
unlink_done:
@@ -580,11 +582,11 @@ static int rename_same_dir(struct inode *old_dir,char *old_name,int old_len,
}
if (S_ISDIR(new_inode->i_mode)) {
new_dir->i_nlink--;
- new_dir->i_dirt = 1;
+ mark_inode_dirty(new_dir);
}
new_inode->i_nlink = 0;
MSDOS_I(new_inode)->i_busy = 1;
- new_inode->i_dirt = 1;
+ mark_inode_dirty(new_inode);
new_de->name[0] = DELETED_FLAG;
fat_mark_buffer_dirty(sb, new_bh, 1);
iput(new_inode);
@@ -675,7 +677,7 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name,int old_len,
}
new_inode->i_nlink = 0;
MSDOS_I(new_inode)->i_busy = 1;
- new_inode->i_dirt = 1;
+ mark_inode_dirty(new_inode);
new_de->name[0] = DELETED_FLAG;
fat_mark_buffer_dirty(sb, new_bh, 1);
}
@@ -696,14 +698,14 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name,int old_len,
}
if (exists && S_ISDIR(new_inode->i_mode)) {
new_dir->i_nlink--;
- new_dir->i_dirt = 1;
+ mark_inode_dirty(new_dir);
}
msdos_read_inode(free_inode);
MSDOS_I(old_inode)->i_busy = 1;
MSDOS_I(old_inode)->i_linked = free_inode;
MSDOS_I(free_inode)->i_oldlink = old_inode;
fat_cache_inval_inode(old_inode);
- old_inode->i_dirt = 1;
+ mark_inode_dirty(old_inode);
old_de->name[0] = DELETED_FLAG;
fat_mark_buffer_dirty(sb, old_bh, 1);
fat_mark_buffer_dirty(sb, free_bh, 1);
@@ -711,7 +713,7 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name,int old_len,
MSDOS_I(new_inode)->i_depend = free_inode;
MSDOS_I(free_inode)->i_old = new_inode;
/* Two references now exist to free_inode so increase count */
- atomic_inc(&free_inode->i_count);
+ free_inode->i_count++;
/* free_inode is put after putting new_inode and old_inode */
iput(new_inode);
fat_brelse(sb, new_bh);
@@ -726,7 +728,7 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name,int old_len,
}
dotdot_de->start = MSDOS_I(dotdot_inode)->i_start =
MSDOS_I(new_dir)->i_start;
- dotdot_inode->i_dirt = 1;
+ mark_inode_dirty(dotdot_inode);
fat_mark_buffer_dirty(sb, dotdot_bh, 1);
old_dir->i_nlink--;
new_dir->i_nlink++;
@@ -793,6 +795,7 @@ struct inode_operations msdos_dir_inode_operations = {
NULL, /* mknod */
msdos_rename, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
fat_bmap, /* bmap */
diff --git a/fs/namei.c b/fs/namei.c
index 198179b98..2ec173f0d 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -12,7 +12,6 @@
* lookup logic.
*/
-#include <linux/config.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
@@ -20,10 +19,7 @@
#include <linux/fcntl.h>
#include <linux/stat.h>
#include <linux/mm.h>
-#include <linux/dalloc.h>
-#include <linux/nametrans.h>
#include <linux/proc_fs.h>
-#include <linux/omirr.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
@@ -37,10 +33,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 +48,7 @@
* is done solely in the VFS level, such that <fs>_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 +87,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<quickcount; i++)
tmp = *(char**)tmp;
- if(tmp)
+ if (tmp)
printk("bad quicklist %x\n", (int)tmp);
#endif
quicklist = *(char**)res;
@@ -113,9 +105,21 @@ static inline char * get_page(void)
return res;
}
+/*
+ * 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))
+
inline void putname(char * name)
{
- if(name) {
+ if (name) {
down(&quicklock);
*(char**)name = quicklist;
quicklist = name;
@@ -154,20 +158,22 @@ static inline int do_getname(const char *filename, char *page)
return retval;
}
-int getname(const char * filename, char **result)
+char * getname(const char * filename)
{
- char *tmp;
- int retval;
+ char *tmp, *result;
+ result = ERR_PTR(-ENOMEM);
tmp = get_page();
- if(!tmp)
- return -ENOMEM;
- retval = do_getname(filename, tmp);
- if (retval < 0)
- putname(tmp);
- else
- *result = tmp;
- return retval;
+ if (tmp) {
+ int retval = do_getname(filename, tmp);
+
+ result = tmp;
+ if (retval < 0) {
+ putname(tmp);
+ result = ERR_PTR(retval);
+ }
+ }
+ return result;
}
/*
@@ -222,417 +228,207 @@ void put_write_access(struct inode * inode)
inode->i_writecount--;
}
-static /*inline */ int concat(struct qstr * name, struct qstr * appendix, char * buf)
+/*
+ * 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 struct dentry * real_lookup(struct dentry * parent, struct qstr * name)
{
- int totallen = name->len;
- if(name->len > MAX_TRANS_FILELEN ||
- appendix->len > MAX_TRANS_SUFFIX) {
- return -ENAMETOOLONG;
+ struct dentry * result;
+ struct inode *dir = parent->d_inode;
+
+ result = ERR_PTR(-ENOTDIR);
+ if (dir->i_op && dir->i_op->lookup) {
+ down(&dir->i_sem);
+ result = d_lookup(parent, name);
+ if (!result) {
+ int error;
+ result = d_alloc(parent, name);
+ error = dir->i_op->lookup(dir, result);
+ if (error) {
+ d_free(result);
+ result = ERR_PTR(error);
+ }
+ }
+ up(&dir->i_sem);
}
- memcpy(buf, name->name, name->len);
- memcpy(buf + name->len, appendix->name, appendix->len);
- totallen += appendix->len;
- buf[totallen] = '\0';
- return totallen;
+ return result;
}
-/* Internal lookup() using the new generic dcache.
- * buf must only be supplied if appendix!=NULL.
- */
-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)
+/* Internal lookup() using the new generic dcache. */
+static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name)
{
- struct qstr tmp = { name->name, name->len };
- int error;
- struct dentry * cached;
-
- *result = NULL;
- if(name->len >= D_MAXLEN)
- return -ENAMETOOLONG;
- vfs_lock();
- cached = d_lookup(dir, name, appendix);
- if(cached) {
- struct inode *inode = NULL;
+ struct dentry * dentry = d_lookup(parent, name);
- 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;
+ if (dentry) {
+ if (dentry->d_revalidate) {
+ /* spin_unlock(&dentry_lock); */
+ dentry = dentry->d_revalidate(dentry);
+ /* spin_lock(&dentry_lock); */
}
- vfs_unlock();
- if(res_entry)
- *res_entry = cached;
- /* Since we are bypassing the iget() mechanism, we have to
- * fabricate the act of crossing any mount points.
+ /*
+ * The parent d_count _should_ be at least 2: one for the
+ * dentry we found, and one for the fact that we are using
+ * it.
*/
- if(!error && inode && inode->i_mount) {
- do {
- struct inode *mnti = inode->i_mount;
- iinc(mnti);
- iput(inode);
- inode = mnti;
- } while(inode->i_mount);
+ if (parent->d_count <= 1) {
+ printk("lookup of %s success in %s, but parent count is %d\n",
+ dentry->d_name.name, parent->d_name.name, parent->d_count);
}
- *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;
+ return dentry;
}
-#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 * parent, 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 (parent != current->fs->root)
+ parent = parent->d_covers->d_parent;
+ /* fallthrough */
+ case 1:
+ result = parent;
}
}
- if(env)
- free_page((unsigned long)trans);
- return error;
-}
-
-#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)
-{
- 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(!error)
- atomic_inc(&(*result)->i_count);
- }
- return error;
+ return result;
}
/* 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)
- 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;
- }
- goto done;
- }
- perm = -ENOTDIR;
- if (!dir->i_op || !dir->i_op->lookup)
- 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);
- }
+ err = permission(dir->d_inode, MAY_EXEC);
+ result = ERR_PTR(err);
+ if (err)
+ goto done_error;
+
+ result = reserved_lookup(dir, name);
+ if (result)
+ goto done_noerror;
+
+ result = cached_lookup(dir, name);
+ if (result)
+ goto done_noerror;
+
+ result = real_lookup(dir, name);
+
+ if (!IS_ERR(result)) {
+done_noerror:
+ result = dget(result->d_mounts);
}
-#endif
-done:
- return perm;
+done_error:
+ return result;
}
-/* [8-Feb-97 T. Schoebel-Theuer] follow_link() modified for generic operation
- * on the VFS layer: first call <fs>_readlink() and then open_namei().
- * All <fs>_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 && 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)
+ goto 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, follow;
- for(len = 0; (c = *tmp++) && (c != '/'); len++) ;
- this.len = len;
- if(!c)
+ dentry = ERR_PTR(-ENOENT);
+ if (!base->d_inode)
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;
- }
- lastdone:
- if(last_error) {
- *last_error = error;
- error = 0;
+ this.name = name;
+ hash = init_name_hash();
+ len = 0;
+ c = *name;
+ do {
+ len++; name++;
+ hash = partial_name_hash(c, hash);
+ c = *name;
+ } while (c && (c != '/'));
+
+ this.len = len;
+ this.hash = end_name_hash(hash);
+
+ /* remove trailing slashes? */
+ follow = follow_link;
+ if (c) {
+ follow |= c;
+ do {
+ c = *++name;
+ } while (c == '/');
}
+
+ dentry = lookup(base, &this);
+ if (IS_ERR(dentry))
+ break;
+
+ if (!follow)
+ break;
+
+ base = do_follow_link(base, dentry);
+ if (c && !IS_ERR(base))
+ continue;
+
+return_base:
+ return base;
}
-alldone:
- if(!error && res_dir)
- *res_dir = base;
- else
- iput(base);
- putname(linkname);
- putname(oldlinkname);
- return error;
+ dput(base);
+ return dentry;
}
/*
@@ -641,24 +437,41 @@ 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.
*/
+struct dentry * __namei(const char *pathname, int follow_link)
+{
+ char *name;
+ struct dentry *dentry;
+
+ name = getname(pathname);
+ dentry = (struct dentry *) name;
+ if (!IS_ERR(name)) {
+ dentry = lookup_dentry(name, NULL, follow_link);
+ putname(name);
+ if (!IS_ERR(dentry)) {
+ if (!dentry->d_inode) {
+ dput(dentry);
+ dentry = ERR_PTR(-ENOENT);
+ }
+ }
+ }
+ return dentry;
+}
-/* [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)
+static inline struct inode *get_parent(struct dentry *dentry)
{
- int error;
- char * tmp;
+ return dentry->d_parent->d_inode;
+}
- error = getname(pathname, &tmp);
- 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);
- }
- return error;
+static inline struct inode *lock_parent(struct dentry *dentry)
+{
+ struct inode *dir = dentry->d_parent->d_inode;
+
+ down(&dir->i_sem);
+ return dir;
}
/*
@@ -674,175 +487,157 @@ int namei(int retrieve_mode, const char *pathname, struct inode **res_inode)
* which is a lot more logical, and also allows the "no perm" needed
* 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 dentry * open_namei(const char * pathname, int flag, int mode)
{
- char buf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2];
- struct qstr last;
- int error;
- int lasterror;
- struct inode * dir, * inode;
- int namei_mode;
+ int acc_mode, error;
+ 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, NULL, 1);
+ if (IS_ERR(dentry))
+ return dentry;
+
+ acc_mode = ACC_MODE(flag);
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_inode) {
+ 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);
+ /* Don't check for write permission */
+ acc_mode = 0;
}
up(&dir->i_sem);
+ if (error)
+ goto exit;
}
+
+ error = -ENOENT;
+ inode = dentry->d_inode;
+ if (!inode)
+ goto exit;
+
+ error = -EISDIR;
+ if (S_ISDIR(inode->i_mode) && (flag & FMODE_WRITE))
+ goto exit;
+
+ error = permission(inode,acc_mode);
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 <Bruce@Pixar.com>
- * 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);
+
+ return dentry;
+
exit:
- return error;
+ dput(dentry);
+ return ERR_PTR(error);
}
-int do_mknod(const char * filename, int mode, dev_t dev)
+struct dentry * 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, *retval;
mode &= ~current->fs->umask;
- error = __namei(NAM_FOLLOW_LINK|NAM_TRANSCREATE|NAM_NO_TRAILSLASH,
- filename, NULL, buf,
- &dir, &inode, &last, NULL, &lasterror);
+ dentry = lookup_dentry(filename, NULL, 1);
+ if (IS_ERR(dentry))
+ return dentry;
+
+ dir = lock_parent(dentry);
+
+ retval = ERR_PTR(-EEXIST);
+ if (dentry->d_inode)
+ goto exit_lock;
+
+ retval = ERR_PTR(-EROFS);
+ if (IS_RDONLY(dir))
+ goto exit_lock;
+
+ error = permission(dir,MAY_WRITE | MAY_EXEC);
+ retval = ERR_PTR(error);
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->mknod) {
- error = -ENOSYS; /* instead of EPERM, what does Posix say? */
- goto exit_inode;
- }
- atomic_inc(&dir->i_count);
+ goto exit_lock;
+
+ retval = ERR_PTR(-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);
+ retval = ERR_PTR(error);
+ if (!error)
+ retval = dget(dentry);
+
+exit_lock:
up(&dir->i_sem);
-exit_inode:
- if(!lasterror)
- iput(inode);
- iput(dir);
-exit:
- return error;
+ dput(dentry);
+ return retval;
}
asmlinkage int sys_mknod(const char * filename, int mode, dev_t dev)
@@ -864,68 +659,63 @@ asmlinkage int sys_mknod(const char * filename, int mode, dev_t dev)
default:
goto out;
}
- error = getname(filename,&tmp);
- if (!error) {
- error = do_mknod(tmp,mode,dev);
+ tmp = getname(filename);
+ error = PTR_ERR(tmp);
+ if (!IS_ERR(tmp)) {
+ struct dentry * dentry = do_mknod(tmp,mode,dev);
putname(tmp);
+ error = PTR_ERR(dentry);
+ if (!IS_ERR(dentry)) {
+ dput(dentry);
+ error = 0;
+ }
}
out:
unlock_kernel();
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;
+
+ dir = lock_parent(dentry);
+
+ error = -EEXIST;
+ if (dentry->d_inode)
+ goto exit_lock;
+
+ error = -EROFS;
+ if (IS_RDONLY(dir))
+ goto exit_lock;
- error = __namei(NAM_FOLLOW_LINK|NAM_TRANSCREATE, pathname, NULL, buf,
- &dir, &inode, &last, NULL, &lasterror);
+ 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;
}
@@ -936,8 +726,9 @@ asmlinkage int sys_mkdir(const char * pathname, int mode)
char * tmp;
lock_kernel();
- error = getname(pathname,&tmp);
- if (!error) {
+ tmp = getname(pathname);
+ error = PTR_ERR(tmp);
+ if (!IS_ERR(tmp)) {
error = do_mkdir(tmp,mode);
putname(tmp);
}
@@ -945,124 +736,54 @@ 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, entry->d_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, 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 = -ENOENT;
+ if (!dentry->d_inode)
+ 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);
+ dput(dentry);
exit:
return error;
}
@@ -1073,8 +794,9 @@ asmlinkage int sys_rmdir(const char * pathname)
char * tmp;
lock_kernel();
- error = getname(pathname,&tmp);
- if (!error) {
+ tmp = getname(pathname);
+ error = PTR_ERR(tmp);
+ if (!IS_ERR(tmp)) {
error = do_rmdir(tmp);
putname(tmp);
}
@@ -1084,90 +806,44 @@ 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);
+ dput(dentry);
exit:
return error;
}
@@ -1178,8 +854,9 @@ asmlinkage int sys_unlink(const char * pathname)
char * tmp;
lock_kernel();
- error = getname(pathname,&tmp);
- if (!error) {
+ tmp = getname(pathname);
+ error = PTR_ERR(tmp);
+ if (!IS_ERR(tmp)) {
error = do_unlink(tmp);
putname(tmp);
}
@@ -1189,62 +866,41 @@ 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_inode)
+ 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;
}
@@ -1252,13 +908,16 @@ exit:
asmlinkage int sys_symlink(const char * oldname, const char * newname)
{
int error;
- char * from, * to;
+ char * from;
lock_kernel();
- error = getname(oldname,&from);
- if (!error) {
- error = getname(newname,&to);
- if (!error) {
+ from = getname(oldname);
+ error = PTR_ERR(from);
+ if (!IS_ERR(from)) {
+ char * to;
+ to = getname(newname);
+ error = PTR_ERR(to);
+ if (!IS_ERR(to)) {
error = do_symlink(from,to);
putname(to);
}
@@ -1270,73 +929,63 @@ 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;
+ inode = old_dentry->d_inode;
+ if (!inode)
+ goto exit_lock;
+
+ error = -EEXIST;
+ if (new_dentry->d_inode)
+ goto exit_lock;
+
+ error = -EROFS;
+ if (IS_RDONLY(dir))
+ goto exit_lock;
+
+ 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);
+ dput(new_dentry);
+exit_old:
+ dput(old_dentry);
exit:
return error;
}
@@ -1344,13 +993,16 @@ exit:
asmlinkage int sys_link(const char * oldname, const char * newname)
{
int error;
- char * from, * to;
+ char * from;
lock_kernel();
- error = getname(oldname,&from);
- if (!error) {
- error = getname(newname,&to);
- if (!error) {
+ from = getname(oldname);
+ error = PTR_ERR(from);
+ if (!IS_ERR(from)) {
+ char * to;
+ to = getname(newname);
+ error = PTR_ERR(to);
+ if (!IS_ERR(to)) {
error = do_link(from,to);
putname(to);
}
@@ -1360,105 +1012,111 @@ asmlinkage int sys_link(const char * oldname, const char * newname)
return error;
}
-static inline int do_rename(const char * oldname, const char * newname)
+/*
+ * Whee.. Deadlock country. Happily there is only one VFS
+ * operation that does this..
+ */
+static inline void double_down(struct semaphore *s1, struct semaphore *s2)
{
- 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)
- 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;
+ 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);
}
- /* Disallow moves of mountpoints. */
- if(oldinode->i_mount) {
- error = -EBUSY;
- goto old_exit;
+}
+
+static inline int is_reserved(struct dentry *dentry)
+{
+ 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;
+ }
}
+ return 0;
+}
+
+static inline int do_rename(const char * oldname, const char * newname)
+{
+ int error;
+ struct inode * old_dir, * new_dir;
+ struct dentry * old_dentry, *new_dentry;
+
+ old_dentry = lookup_dentry(oldname, NULL, 0);
+
+ error = PTR_ERR(old_dentry);
+ if (IS_ERR(old_dentry))
+ goto exit;
+
+ new_dentry = lookup_dentry(newname, NULL, 0);
+
+ 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 = __namei(NAM_FOLLOW_LINK|NAM_TRANSCREATE, newname, NULL, newbuf,
- &newdir, &newinode, &newlast, NULL, &newlasterror);
+ error = -ENOENT;
+ if (!old_dentry->d_inode)
+ 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);
+ dput(new_dentry);
+exit_old:
+ dput(old_dentry);
exit:
return error;
}
@@ -1466,13 +1124,16 @@ exit:
asmlinkage int sys_rename(const char * oldname, const char * newname)
{
int error;
- char * from, * to;
+ char * from;
lock_kernel();
- error = getname(oldname,&from);
- if (!error) {
- error = getname(newname,&to);
- if (!error) {
+ from = getname(oldname);
+ error = PTR_ERR(from);
+ if (!IS_ERR(from)) {
+ char * to;
+ to = getname(newname);
+ error = PTR_ERR(to);
+ if (!IS_ERR(to)) {
error = do_rename(from,to);
putname(to);
}
diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c
index 72ca3e6dd..83309f3a6 100644
--- a/fs/ncpfs/inode.c
+++ b/fs/ncpfs/inode.c
@@ -408,7 +408,7 @@ int ncp_current_malloced;
static struct file_system_type ncp_fs_type = {
"ncpfs",
- FS_NO_DCACHE,
+ 0 /* FS_NO_DCACHE doesn't work correctly */,
ncp_read_super,
NULL
};
diff --git a/fs/ncpfs/mmap.c b/fs/ncpfs/mmap.c
index 8e814d153..937f40cec 100644
--- a/fs/ncpfs/mmap.c
+++ b/fs/ncpfs/mmap.c
@@ -132,8 +132,8 @@ int ncp_mmap(struct inode *inode, struct file *file, struct vm_area_struct *vma)
inode->i_atime = CURRENT_TIME;
inode->i_dirt = 1;
}
- vma->vm_inode = inode;
- atomic_inc(&inode->i_count);
+
+ vma->vm_dentry = dget(file->f_dentry);
vma->vm_ops = &ncp_file_mmap;
return 0;
}
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 71835c255..b10331c6a 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 dentry *);
+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,267 @@ 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,
- struct inode **result)
+static int nfs_lookup(struct inode *dir, struct dentry * dentry)
{
+ struct inode *inode;
struct nfs_fh fhandle;
struct nfs_fattr fattr;
- char name[len > NFS_MAXNAMLEN? 1 : len+1];
+ int len = dentry->d_name.len;
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, dentry->d_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);
- 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);
- return -EACCES;
- }
- iput(dir);
+
+ error = nfs_proc_lookup(NFS_SERVER(dir), NFS_FH(dir), dentry->d_name.name, &fhandle, &fattr);
+
+ inode = NULL;
+ if (!error) {
+ error = -ENOENT;
+ inode = nfs_fhget(dir->i_sb, &fhandle, &fattr);
+ if (!inode)
+ return -EACCES;
+ } else if (error != -ENOENT)
+ return error;
+
+ d_add(dentry, inode);
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);
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);
+ 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);
+ 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);
+ 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;
+
+ inode->i_count++;
+ d_instantiate(dentry, inode);
+ return 0;
}
/*
@@ -821,45 +600,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;
}
/*
@@ -873,8 +646,7 @@ void nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
int was_empty;
dfprintk(VFS, "NFS: refresh_inode(%x/%ld ct=%d)\n",
- inode->i_dev, inode->i_ino,
- atomic_read(&inode->i_count));
+ inode->i_dev, inode->i_ino, inode->i_count);
if (!inode || !fattr) {
printk("nfs_refresh_inode: inode or fattr is NULL\n");
@@ -925,6 +697,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 56540bbdc..eb4735a6d 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 */
@@ -142,7 +143,7 @@ nfs_file_write(struct inode *inode, struct file *file,
int result;
dfprintk(VFS, "nfs: write(%x/%ld (%d), %lu@%lu)\n",
- inode->i_dev, inode->i_ino, atomic_read(&inode->i_count),
+ inode->i_dev, inode->i_ino, inode->i_count,
count, (unsigned long) file->f_pos);
if (!inode) {
@@ -179,11 +180,11 @@ nfs_lock(struct inode *inode, struct file *filp, int cmd, struct file_lock *fl)
int status;
dprintk("NFS: nfs_lock(f=%4x/%ld, t=%x, fl=%x, r=%ld:%ld)\n",
- filp->f_inode->i_dev, filp->f_inode->i_ino,
+ filp->f_dentry->d_inode->i_dev, filp->f_dentry->d_inode->i_ino,
fl->fl_type, fl->fl_flags,
fl->fl_start, fl->fl_end);
- if (!(inode = filp->f_inode))
+ if (!(inode = filp->f_dentry->d_inode))
return -EINVAL;
/* No mandatory locks over NFS */
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 5ab9600e9..cf52a0d56 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -36,15 +36,17 @@
static int nfs_notify_change(struct inode *, struct iattr *);
static void nfs_put_inode(struct inode *);
+static void nfs_delete_inode(struct inode *);
static void nfs_put_super(struct super_block *);
static void nfs_read_inode(struct inode *);
-static void nfs_statfs(struct super_block *, struct statfs *, int bufsiz);
+static int nfs_statfs(struct super_block *, struct statfs *, int bufsiz);
static struct super_operations nfs_sops = {
nfs_read_inode, /* read inode */
- nfs_notify_change, /* notify change */
NULL, /* write inode */
nfs_put_inode, /* put inode */
+ nfs_delete_inode, /* delete inode */
+ nfs_notify_change, /* notify change */
nfs_put_super, /* put superblock */
NULL, /* write superblock */
nfs_statfs, /* stat filesystem */
@@ -73,11 +75,16 @@ static void
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);
+/*
+ * This should do any silly-rename cleanups once we
+ * get silly-renaming working again..
+ */
+static void
+nfs_delete_inode(struct inode * inode)
+{
+ dprintk("NFS: delete_inode(%x/%ld)\n", inode->i_dev, inode->i_ino);
}
void
@@ -230,7 +237,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();
@@ -250,7 +258,7 @@ failure:
return NULL;
}
-static void
+static int
nfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
{
int error;
@@ -271,7 +279,7 @@ nfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
tmp.f_files = 0;
tmp.f_ffree = 0;
tmp.f_namelen = NAME_MAX;
- copy_to_user(buf, &tmp, bufsiz);
+ return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
}
/*
@@ -317,7 +325,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fhandle,
}
dprintk("NFS: fhget(%x/%ld ct=%d)\n",
inode->i_dev, inode->i_ino,
- atomic_read(&inode->i_count));
+ inode->i_count);
return inode;
}
@@ -364,7 +372,6 @@ nfs_notify_change(struct inode *inode, struct iattr *attr)
nfs_truncate_dirty_pages(inode, sattr.size);
nfs_refresh_inode(inode, &fattr);
}
- inode->i_dirt = 0;
return error;
}
@@ -435,7 +442,7 @@ done:
*/
static struct file_system_type nfs_fs_type = {
"nfs",
- FS_NO_DCACHE,
+ 0 /* FS_NO_DCACHE - this doesn't work right now*/,
nfs_read_super,
NULL
};
diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c
index add3309f3..51cca7c48 100644
--- a/fs/nfs/nfsroot.c
+++ b/fs/nfs/nfsroot.c
@@ -1,5 +1,5 @@
/*
- * $Id: nfsroot.c,v 1.37 1997/06/04 08:28:10 davem Exp $
+ * $Id: nfsroot.c,v 1.3 1997/06/17 13:26:56 ralf Exp $
*
* Copyright (C) 1995, 1996 Gero Kuhlmann <gero@gkminix.han.de>
*
@@ -78,7 +78,6 @@
#include <asm/param.h>
#include <linux/utsname.h>
-#include <linux/nametrans.h>
#include <linux/in.h>
#include <linux/if.h>
#include <linux/inet.h>
@@ -833,9 +832,6 @@ __initfunc(static void root_do_bootp_ext(u8 *ext))
root_bootp_string(nfs_path, ext+1, *ext, NFS_MAXPATHLEN);
break;
}
-#ifdef CONFIG_TRANS_NAMES
- translations_dirty = 1;
-#endif
}
@@ -1258,9 +1254,6 @@ __initfunc(static void root_nfs_addrs(char *addrs))
system_utsname.domainname[0] = '\0';
user_dev_name[0] = '\0';
bootp_flag = rarp_flag = 1;
-#ifdef CONFIG_TRANS_NAMES
- translations_dirty = 1;
-#endif
/* The following is just a shortcut for automatic IP configuration */
if (!strcmp(addrs, "bootp")) {
@@ -1306,9 +1299,6 @@ __initfunc(static void root_nfs_addrs(char *addrs))
}
strncpy(system_utsname.nodename, ip, __NEW_UTS_LEN);
system_utsname.nodename[__NEW_UTS_LEN] = '\0';
-#ifdef CONFIG_TRANS_NAMES
- translations_dirty = 1;
-#endif
break;
case 5:
strncpy(user_dev_name, ip, IFNAMSIZ);
@@ -1342,9 +1332,6 @@ __initfunc(static int root_nfs_setup(void))
if (!system_utsname.nodename[0]) {
strncpy(system_utsname.nodename, in_ntoa(myaddr), __NEW_UTS_LEN);
system_utsname.nodename[__NEW_UTS_LEN] = '\0';
-#ifdef CONFIG_TRANS_NAMES
- translations_dirty = 1;
-#endif
}
/* Set the correct netmask */
diff --git a/fs/nfs/read.c b/fs/nfs/read.c
index 2c3b59036..4ce61f731 100644
--- a/fs/nfs/read.c
+++ b/fs/nfs/read.c
@@ -150,7 +150,6 @@ nfs_readpage_result(struct rpc_task *task)
fail++;
dprintk("NFS: %d successful reads, %d failures\n", succ, fail);
}
- iput(req->ra_inode);
clear_bit(PG_locked, &page->flags);
wake_up(&page->wait);
@@ -188,7 +187,6 @@ nfs_readpage_async(struct inode *inode, struct page *page)
nfs_readpage_result, req);
if (result >= 0) {
- atomic_inc(&inode->i_count);
atomic_inc(&page->count);
return 0;
}
diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c
index a22f96239..3d545f7d8 100644
--- a/fs/nfs/symlink.c
+++ b/fs/nfs/symlink.c
@@ -19,6 +19,7 @@
#include <asm/uaccess.h>
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/nfs/write.c b/fs/nfs/write.c
index f27d083e4..9241c679e 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -338,7 +338,7 @@ create_write_request(struct inode *inode, struct page *page,
wreq->wb_page = page;
wreq->wb_offset = offset;
wreq->wb_bytes = bytes;
- atomic_inc(&inode->i_count);
+
atomic_inc(&page->count);
append_write_request(&NFS_WRITEBACK(inode), wreq);
@@ -695,7 +695,6 @@ nfs_check_error(struct inode *inode)
status = req->wb_task.tk_status;
remove_write_request(&nfs_failed_requests, req);
- iput(req->wb_inode);
kfree(req);
return status;
}
@@ -788,7 +787,6 @@ nfs_wback_result(struct rpc_task *task)
dprintk("NFS: %4d saving write failure code\n",
task->tk_pid);
append_write_request(&nfs_failed_requests, req);
- atomic_inc(&inode->i_count);
}
clear_bit(PG_uptodate, &page->flags);
} else if (!WB_CANCELLED(req)) {
@@ -818,6 +816,5 @@ nfs_wback_result(struct rpc_task *task)
kfree(req);
free_page(page_address(page));
- iput(inode);
nr_write_requests--;
}
diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c
index a3b29313a..c83150b5f 100644
--- a/fs/nfsd/export.c
+++ b/fs/nfsd/export.c
@@ -331,7 +331,7 @@ exp_rootfh(struct svc_client *clp, dev_t dev, ino_t ino, struct knfs_fh *f)
if (!(exp = exp_get(clp, dev, ino)))
return -EPERM;
- atomic_inc(&exp->ex_inode->i_count);
+ exp->ex_inode->i_count++;
fh_compose(&fh, exp, exp->ex_inode);
memcpy(f, &fh.fh_handle, sizeof(struct knfs_fh));
fh_put(&fh);
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index a68fca997..b6fdb460d 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -120,13 +120,13 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
dotdot = (len == 2 && name[0] == '.' && name[1] == '.');
if (dotdot) {
if (dirp == current->fs->root) {
- atomic_inc(&dirp->i_count);
+ dirp->i_count++;
*resfh = *fhp;
return 0;
}
if (dirp->i_dev == exp->ex_dev && dirp->i_ino == exp->ex_ino) {
- atomic_inc(&dirp->i_count);
+ dirp->i_count++;
*resfh = *fhp;
return 0;
}
@@ -144,12 +144,12 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
if (perm != 0)
return perm;
if (!len) {
- atomic_inc(&dirp->i_count);
+ dirp->i_count++;
*resfh = *fhp;
return 0;
}
- atomic_inc(&dirp->i_count); /* lookup eats the dirp inode */
+ dirp->i_count++; /* lookup eats the dirp inode */
err = dirp->i_op->lookup(dirp, name, len, &inode);
if (err)
@@ -162,7 +162,7 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
if (!dotdot && (sb = inode->i_sb) && (inode == sb->s_mounted)) {
iput(inode);
inode = sb->s_covered;
- atomic_inc(&inode->i_count);
+ inode->i_count++;
}
fh_compose(resfh, exp, inode);
@@ -291,7 +291,7 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
}
}
- atomic_inc(&inode->i_count);
+ inode->i_count++;
return 0;
}
@@ -304,7 +304,7 @@ nfsd_close(struct file *filp)
struct inode *inode;
inode = filp->f_inode;
- if (!atomic_read(&inode->i_count))
+ if (!inode->i_count)
printk(KERN_WARNING "nfsd: inode count == 0!\n");
if (filp->f_op && filp->f_op->release)
filp->f_op->release(inode, filp);
@@ -533,7 +533,7 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
fh_lock(fhp); /* lock directory */
dirp = fhp->fh_inode;
- atomic_inc(&dirp->i_count); /* dirop eats the inode */
+ dirp->i_count++; /* dirop eats the inode */
switch (type) {
case S_IFREG:
@@ -568,7 +568,7 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
* If the VFS call doesn't return the inode, look it up now.
*/
if (inode == NULL) {
- atomic_inc(&dirp->i_count);
+ dirp->i_count++;
err = dirp->i_op->lookup(dirp, fname, flen, &inode);
if (err < 0)
return -nfserrno(err); /* Huh?! */
@@ -643,7 +643,7 @@ nfsd_readlink(struct svc_rqst *rqstp, struct svc_fh *fhp, char *buf, int *lenp)
if (!inode->i_op || !inode->i_op->readlink)
return nfserr_io;
- atomic_inc(&inode->i_count);
+ inode->i_count++;
oldfs = get_fs(); set_fs(KERNEL_DS);
err = inode->i_op->readlink(inode, buf, *lenp);
set_fs(oldfs);
@@ -680,7 +680,7 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
return nfserr_perm;
fh_lock(fhp); /* lock inode */
- atomic_inc(&dirp->i_count);
+ dirp->i_count++;
err = dirp->i_op->symlink(dirp, fname, flen, path);
fh_unlock(fhp); /* unlock inode */
@@ -693,7 +693,7 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
/*
* Okay, now look up the inode of the new symlink.
*/
- atomic_inc(&dirp->i_count); /* lookup eats the dirp inode */
+ dirp->i_count++; /* lookup eats the dirp inode */
err = dirp->i_op->lookup(dirp, fname, flen, &inode);
if (err)
return nfserrno(-err);
@@ -730,7 +730,7 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
return nfserr_perm;
fh_lock(ffhp); /* lock directory inode */
- atomic_inc(&dirp->i_count);
+ dirp->i_count++;
err = dirp->i_op->link(dest, dirp, fname, len);
fh_unlock(ffhp); /* unlock inode */
@@ -770,8 +770,8 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
return nfserr_perm;
fh_lock(tfhp); /* lock destination directory */
- atomic_inc(&tdir->i_count);
- atomic_inc(&fdir->i_count);
+ tdir->i_count++;
+ fdir->i_count++;
err = fdir->i_op->rename(fdir, fname, flen, tdir, tname, tlen);
fh_unlock(tfhp); /* unlock inode */
@@ -805,12 +805,12 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
if (type == S_IFDIR) {
if (!dirp->i_op || !dirp->i_op->rmdir)
return nfserr_notdir;
- atomic_inc(&dirp->i_count);
+ dirp->i_count++;
err = dirp->i_op->rmdir(dirp, fname, flen);
} else { /* other than S_IFDIR */
if (!dirp->i_op || !dirp->i_op->unlink)
return nfserr_perm;
- atomic_inc(&dirp->i_count);
+ dirp->i_count++;
err = dirp->i_op->unlink(dirp, fname, flen);
}
diff --git a/fs/open.c b/fs/open.c
index 70b8a8a58..3408fb2a6 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -4,7 +4,6 @@
* Copyright (C) 1991, 1992 Linus Torvalds
*/
-#include <linux/config.h>
#include <linux/vfs.h>
#include <linux/types.h>
#include <linux/utime.h>
@@ -21,32 +20,27 @@
#include <linux/file.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
-#include <linux/omirr.h>
#include <asm/uaccess.h>
#include <asm/bitops.h>
asmlinkage int sys_statfs(const char * path, struct statfs * buf)
{
- struct inode * inode;
+ struct dentry * dentry;
int error;
lock_kernel();
- error = verify_area(VERIFY_WRITE, buf, sizeof(struct statfs));
- if (error)
- goto out;
- error = namei(NAM_FOLLOW_LINK, path, &inode);
- if (error)
- goto out;
- error = -ENOSYS;
- if (!inode->i_sb->s_op->statfs) {
- iput(inode);
- goto out;
+ dentry = namei(path);
+ error = PTR_ERR(dentry);
+ if (!IS_ERR(dentry)) {
+ struct inode * inode = dentry->d_inode;
+
+ error = -ENOSYS;
+ if (inode->i_sb->s_op->statfs)
+ error = inode->i_sb->s_op->statfs(inode->i_sb, buf, sizeof(struct statfs));
+
+ dput(dentry);
}
- inode->i_sb->s_op->statfs(inode->i_sb, buf, sizeof(struct statfs));
- iput(inode);
- error = 0;
-out:
unlock_kernel();
return error;
}
@@ -54,6 +48,7 @@ out:
asmlinkage int sys_fstatfs(unsigned int fd, struct statfs * buf)
{
struct inode * inode;
+ struct dentry * dentry;
struct file * file;
int error;
@@ -63,7 +58,9 @@ asmlinkage int sys_fstatfs(unsigned int fd, struct statfs * buf)
goto out;
if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
error = -EBADF;
- else if (!(inode = file->f_inode))
+ else if (!(dentry = file->f_dentry))
+ error = -ENOENT;
+ else if (!(inode = dentry->d_inode))
error = -ENOENT;
else if (!inode->i_sb)
error = -ENODEV;
@@ -90,7 +87,6 @@ int do_truncate(struct inode *inode, unsigned long length)
vmtruncate(inode, length);
if (inode->i_op && inode->i_op->truncate)
inode->i_op->truncate(inode);
- inode->i_status |= ST_MODIFIED;
}
up(&inode->i_sem);
return error;
@@ -98,33 +94,37 @@ int do_truncate(struct inode *inode, unsigned long length)
asmlinkage int sys_truncate(const char * path, unsigned long length)
{
+ struct dentry * dentry;
struct inode * inode;
int error;
lock_kernel();
- error = namei(NAM_FOLLOW_LINK, path, &inode);
- if (error)
+ dentry = namei(path);
+
+ error = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
goto out;
+ inode = dentry->d_inode;
error = -EACCES;
if (S_ISDIR(inode->i_mode))
- goto iput_and_out;
+ goto dput_and_out;
error = permission(inode,MAY_WRITE);
if (error)
- goto iput_and_out;
+ goto dput_and_out;
error = -EROFS;
if (IS_RDONLY(inode))
- goto iput_and_out;
+ goto dput_and_out;
error = -EPERM;
if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
- goto iput_and_out;
+ goto dput_and_out;
error = get_write_access(inode);
if (error)
- goto iput_and_out;
+ goto dput_and_out;
error = locks_verify_area(FLOCK_VERIFY_WRITE, inode, NULL,
length < inode->i_size ? length : inode->i_size,
@@ -135,8 +135,8 @@ asmlinkage int sys_truncate(const char * path, unsigned long length)
error = do_truncate(inode, length);
}
put_write_access(inode);
-iput_and_out:
- iput(inode);
+dput_and_out:
+ dput(dentry);
out:
unlock_kernel();
return error;
@@ -145,13 +145,16 @@ out:
asmlinkage int sys_ftruncate(unsigned int fd, unsigned long length)
{
struct inode * inode;
+ struct dentry *dentry;
struct file * file;
int error;
lock_kernel();
if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
error = -EBADF;
- else if (!(inode = file->f_inode))
+ else if (!(dentry = file->f_dentry))
+ error = -ENOENT;
+ else if (!(inode = dentry->d_inode))
error = -ENOENT;
else if (S_ISDIR(inode->i_mode) || !(file->f_mode & FMODE_WRITE))
error = -EACCES;
@@ -184,17 +187,21 @@ asmlinkage int sys_ftruncate(unsigned int fd, unsigned long length)
asmlinkage int sys_utime(char * filename, struct utimbuf * times)
{
int error;
+ struct dentry * dentry;
struct inode * inode;
struct iattr newattrs;
lock_kernel();
- /* Hmm, should I always follow symlinks or not ? */
- error = namei(NAM_FOLLOW_LINK, filename, &inode);
- if (error)
+ dentry = namei(filename);
+
+ error = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
goto out;
+ inode = dentry->d_inode;
+
error = -EROFS;
if (IS_RDONLY(inode))
- goto iput_and_out;
+ goto dput_and_out;
/* Don't worry, the checks are done in inode_change_ok() */
newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME;
@@ -203,22 +210,17 @@ asmlinkage int sys_utime(char * filename, struct utimbuf * times)
if (!error)
error = get_user(newattrs.ia_mtime, &times->modtime);
if (error)
- goto iput_and_out;
+ goto dput_and_out;
newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET;
} else {
if (current->fsuid != inode->i_uid &&
(error = permission(inode,MAY_WRITE)) != 0)
- goto iput_and_out;
+ goto dput_and_out;
}
error = notify_change(inode, &newattrs);
-#ifdef CONFIG_OMIRR
- if(!error)
- omirr_printall(inode, " U %ld %ld %ld ", CURRENT_TIME,
- newattrs.ia_atime, newattrs.ia_mtime);
-#endif
-iput_and_out:
- iput(inode);
+dput_and_out:
+ dput(dentry);
out:
unlock_kernel();
return error;
@@ -233,38 +235,39 @@ out:
asmlinkage int sys_utimes(char * filename, struct timeval * utimes)
{
int error;
+ struct dentry * dentry;
struct inode * inode;
struct iattr newattrs;
lock_kernel();
- error = namei(NAM_FOLLOW_LINK, filename, &inode);
- if (error)
+ dentry = namei(filename);
+
+ error = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
goto out;
+ inode = dentry->d_inode;
+
error = -EROFS;
if (IS_RDONLY(inode))
- goto iput_and_out;
+ goto dput_and_out;
+
/* Don't worry, the checks are done in inode_change_ok() */
newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME;
if (utimes) {
struct timeval times[2];
error = -EFAULT;
if (copy_from_user(&times, utimes, sizeof(times)))
- goto iput_and_out;
+ goto dput_and_out;
newattrs.ia_atime = times[0].tv_sec;
newattrs.ia_mtime = times[1].tv_sec;
newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET;
} else {
if ((error = permission(inode,MAY_WRITE)) != 0)
- goto iput_and_out;
+ goto dput_and_out;
}
error = notify_change(inode, &newattrs);
-#ifdef CONFIG_OMIRR
- if(!error)
- omirr_printall(inode, " U %ld %ld %ld ", CURRENT_TIME,
- newattrs.ia_atime, newattrs.ia_mtime);
-#endif
-iput_and_out:
- iput(inode);
+dput_and_out:
+ dput(dentry);
out:
unlock_kernel();
return error;
@@ -276,7 +279,7 @@ out:
*/
asmlinkage int sys_access(const char * filename, int mode)
{
- struct inode * inode;
+ struct dentry * dentry;
int old_fsuid, old_fsgid;
int res = -EINVAL;
@@ -287,11 +290,14 @@ 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);
- if (!res) {
- res = permission(inode, mode);
- iput(inode);
+
+ dentry = namei(filename);
+ res = PTR_ERR(dentry);
+ if (!IS_ERR(dentry)) {
+ res = permission(dentry->d_inode, mode);
+ dput(dentry);
}
+
current->fsuid = old_fsuid;
current->fsgid = old_fsgid;
out:
@@ -301,24 +307,34 @@ 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 = namei(filename);
+ error = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
goto out;
+
+ inode = dentry->d_inode;
+
error = -ENOTDIR;
if (!S_ISDIR(inode->i_mode))
- goto iput_and_out;
- if ((error = permission(inode,MAY_EXEC)) != 0)
- goto iput_and_out;
-
- /* exchange inodes */
- tmpi = current->fs->pwd; current->fs->pwd = inode; inode = tmpi;
-iput_and_out:
- iput(inode);
+ goto dput_and_out;
+
+ 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 +342,38 @@ out:
asmlinkage int sys_fchdir(unsigned int fd)
{
- struct inode * inode;
- struct file * file;
- int error = -EBADF;
+ struct file *file;
+ struct dentry *dentry;
+ struct inode *inode;
+ int error;
lock_kernel();
+
+ error = -EBADF;
if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
goto out;
+
error = -ENOENT;
- if (!(inode = file->f_inode))
+ if (!(dentry = file->f_dentry))
goto out;
+ if (!(inode = dentry->d_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);
+
+ {
+ struct dentry *tmp;
+
+ tmp = current->fs->pwd;
+ current->fs->pwd = dget(dentry);
+ dput(tmp);
+ }
out:
unlock_kernel();
return error;
@@ -351,24 +381,39 @@ 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 = namei(filename);
+ error = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
goto out;
+
+ inode = dentry->d_inode;
+
error = -ENOTDIR;
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;
+ goto dput_and_out;
+
+ /* exchange dentries */
+ tmp = current->fs->root;
+ current->fs->root = dentry;
+ dentry = tmp;
error = 0;
-iput_and_out:
- iput(inode);
+
+dput_and_out:
+ dput(dentry);
out:
unlock_kernel();
return error;
@@ -377,6 +422,7 @@ out:
asmlinkage int sys_fchmod(unsigned int fd, mode_t mode)
{
struct inode * inode;
+ struct dentry * dentry;
struct file * file;
struct iattr newattrs;
int err = -EBADF;
@@ -385,7 +431,9 @@ asmlinkage int sys_fchmod(unsigned int fd, mode_t mode)
if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
goto out;
err = -ENOENT;
- if (!(inode = file->f_inode))
+ if (!(dentry = file->f_dentry))
+ goto out;
+ if (!(inode = dentry->d_inode))
goto out;
err = -EROFS;
if (IS_RDONLY(inode))
@@ -397,12 +445,7 @@ asmlinkage int sys_fchmod(unsigned int fd, mode_t mode)
mode = inode->i_mode;
newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
- inode->i_dirt = 1;
err = notify_change(inode, &newattrs);
-#ifdef CONFIG_OMIRR
- if(!err)
- omirr_printall(inode, " M %ld %ld ", CURRENT_TIME, newattrs.ia_mode);
-#endif
out:
unlock_kernel();
return err;
@@ -410,54 +453,51 @@ out:
asmlinkage int sys_chmod(const char * filename, mode_t mode)
{
+ struct dentry * dentry;
struct inode * inode;
int error;
struct iattr newattrs;
lock_kernel();
- /* I'm not sure whether to use NAM_FOLLOW_TRAILSLASH instead,
- * 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);
- if (error)
+ dentry = namei(filename);
+
+ error = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
goto out;
+ inode = dentry->d_inode;
+
error = -EROFS;
if (IS_RDONLY(inode))
- goto iput_and_out;
+ goto dput_and_out;
+
error = -EPERM;
if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
- goto iput_and_out;
+ goto dput_and_out;
+
if (mode == (mode_t) -1)
mode = inode->i_mode;
newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
- inode->i_dirt = 1;
error = notify_change(inode, &newattrs);
-#ifdef CONFIG_OMIRR
- if(!error)
- omirr_printall(inode, " M %ld %ld ", CURRENT_TIME, newattrs.ia_mode);
-#endif
-iput_and_out:
- iput(inode);
+
+dput_and_out:
+ dput(dentry);
out:
unlock_kernel();
return error;
}
-asmlinkage int sys_fchown(unsigned int fd, uid_t user, gid_t group)
+static int chown_common(struct dentry * dentry, uid_t user, gid_t group)
{
struct inode * inode;
- struct file * file;
struct iattr newattrs;
- int error = -EBADF;
+ int error;
- lock_kernel();
- if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
- goto out;
error = -ENOENT;
- if (!(inode = file->f_inode))
+ if (!(inode = dentry->d_inode)) {
+ printk("chown_common: NULL inode\n");
goto out;
+ }
error = -EROFS;
if (IS_RDONLY(inode))
goto out;
@@ -489,7 +529,6 @@ asmlinkage int sys_fchown(unsigned int fd, uid_t user, gid_t group)
newattrs.ia_mode &= ~S_ISGID;
newattrs.ia_valid |= ATTR_MODE;
}
- inode->i_dirt = 1;
if (inode->i_sb && inode->i_sb->dq_op) {
inode->i_sb->dq_op->initialize(inode, -1);
error = -EDQUOT;
@@ -500,80 +539,70 @@ asmlinkage int sys_fchown(unsigned int fd, uid_t user, gid_t group)
inode->i_sb->dq_op->transfer(inode, &newattrs, 1);
} else
error = notify_change(inode, &newattrs);
-#ifdef CONFIG_OMIRR
- if(!error)
- omirr_printall(inode, " O %d %d ", CURRENT_TIME,
- newattrs.ia_uid, newattrs.ia_gid);
-#endif
out:
- unlock_kernel();
return error;
}
+asmlinkage int sys_lchown(const char * filename, uid_t user, gid_t group)
+{
+ struct dentry * dentry;
+ int error;
+
+ lock_kernel();
+ dentry = lnamei(filename);
+
+ error = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
+ goto out;
+
+ error = chown_common(dentry, user, group);
+
+ dput(dentry);
+out:
+ unlock_kernel();
+ return(error);
+}
+
asmlinkage int sys_chown(const char * filename, uid_t user, gid_t group)
{
- struct inode * inode;
+ struct dentry * dentry;
int error;
- struct iattr newattrs;
lock_kernel();
- error = namei(NAM_FOLLOW_TRAILSLASH, filename, &inode);
- if (error)
+ dentry = namei(filename);
+
+ error = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
goto out;
- error = -EROFS;
- if (IS_RDONLY(inode))
- goto iput_and_out;
- error = -EPERM;
- if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
- goto iput_and_out;
- if (user == (uid_t) -1)
- user = inode->i_uid;
- if (group == (gid_t) -1)
- group = inode->i_gid;
- newattrs.ia_mode = inode->i_mode;
- newattrs.ia_uid = user;
- newattrs.ia_gid = group;
- newattrs.ia_valid = ATTR_UID | ATTR_GID | ATTR_CTIME;
- /*
- * If the owner has been changed, remove the setuid bit
- */
- if (inode->i_mode & S_ISUID) {
- newattrs.ia_mode &= ~S_ISUID;
- newattrs.ia_valid |= ATTR_MODE;
- }
- /*
- * If the group has been changed, remove the setgid bit
- *
- * Don't remove the setgid bit if no group execute bit.
- * This is a file marked for mandatory locking.
- */
- if (((inode->i_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))) {
- newattrs.ia_mode &= ~S_ISGID;
- newattrs.ia_valid |= ATTR_MODE;
- }
- inode->i_dirt = 1;
- if (inode->i_sb->dq_op) {
- inode->i_sb->dq_op->initialize(inode, -1);
- error = -EDQUOT;
- if (inode->i_sb->dq_op->transfer(inode, &newattrs, 0))
- goto iput_and_out;
- error = notify_change(inode, &newattrs);
- if (error)
- inode->i_sb->dq_op->transfer(inode, &newattrs, 1);
- } else
- error = notify_change(inode, &newattrs);
-#ifdef CONFIG_OMIRR
- if(!error)
- omirr_printall(inode, " O %d %d ", CURRENT_TIME,
- newattrs.ia_uid, newattrs.ia_gid);
-#endif
-iput_and_out:
- iput(inode);
+
+ error = chown_common(dentry, user, group);
+
+ dput(dentry);
out:
unlock_kernel();
return(error);
}
+asmlinkage int sys_fchown(unsigned int fd, uid_t user, gid_t group)
+{
+ struct dentry * dentry;
+ struct file * file;
+ int error = -EBADF;
+
+ lock_kernel();
+ if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
+ goto out;
+ error = -ENOENT;
+ if (!(dentry = file->f_dentry))
+ goto out;
+
+ error = chown_common(dentry, user, group);
+
+out:
+ unlock_kernel();
+ return error;
+}
+
/*
* Note that while the flag value (low two bits) for sys_open means:
* 00 - read-only
@@ -591,6 +620,7 @@ out:
static int do_open(const char * filename,int flags,int mode, int fd)
{
struct inode * inode;
+ struct dentry * dentry;
struct file * f;
int flag,error;
@@ -603,16 +633,18 @@ static int do_open(const char * filename,int flags,int mode, int fd)
flag++;
if (flag & O_TRUNC)
flag |= 2;
- error = open_namei(filename,flag,mode,&inode,NULL);
- if (error)
+ dentry = open_namei(filename,flag,mode);
+ error = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
goto cleanup_file;
+ inode = dentry->d_inode;
if (f->f_mode & FMODE_WRITE) {
error = get_write_access(inode);
if (error)
- goto cleanup_inode;
+ goto cleanup_dentry;
}
- f->f_inode = inode;
+ f->f_dentry = dentry;
f->f_pos = 0;
f->f_reada = 0;
f->f_op = NULL;
@@ -631,8 +663,8 @@ static int do_open(const char * filename,int flags,int mode, int fd)
cleanup_all:
if (f->f_mode & FMODE_WRITE)
put_write_access(inode);
-cleanup_inode:
- iput(inode);
+cleanup_dentry:
+ dput(dentry);
cleanup_file:
put_filp(f);
return error;
@@ -666,14 +698,15 @@ asmlinkage int sys_open(const char * filename,int flags,int mode)
int fd, error;
lock_kernel();
- fd = get_unused_fd();
- if (fd < 0) {
- error = fd;
+ error = get_unused_fd();
+ if (error < 0)
goto out;
- }
- error = getname(filename, &tmp);
- if (!error) {
- error = do_open(tmp,flags,mode, fd);
+
+ fd = error;
+ tmp = getname(filename);
+ error = PTR_ERR(tmp);
+ if (!IS_ERR(tmp)) {
+ error = do_open(tmp,flags,mode,fd);
putname(tmp);
if (!error) {
error = fd;
@@ -704,31 +737,35 @@ asmlinkage int sys_creat(const char * pathname, int mode)
#endif
-int __fput(struct file *filp, struct inode *inode)
+int __fput(struct file *filp)
{
int error = 0;
+ struct dentry * dentry = filp->f_dentry;
+ struct inode * inode = dentry->d_inode;
if (filp->f_op && filp->f_op->release)
error = filp->f_op->release(inode,filp);
- filp->f_inode = NULL;
+ filp->f_dentry = NULL;
if (filp->f_mode & FMODE_WRITE)
put_write_access(inode);
- iput(inode);
+ dput(dentry);
return error;
}
int close_fp(struct file *filp)
{
+ struct dentry *dentry;
struct inode *inode;
if (filp->f_count == 0) {
printk("VFS: Close: file count is 0\n");
return 0;
}
- inode = filp->f_inode;
+ dentry = filp->f_dentry;
+ inode = dentry->d_inode;
if (inode)
locks_remove_locks(current, filp);
- return fput(filp, inode);
+ return fput(filp);
}
asmlinkage int sys_close(unsigned int fd)
diff --git a/fs/pipe.c b/fs/pipe.c
index 732d37af5..0188409e8 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -75,10 +75,7 @@ static long pipe_read(struct inode * inode, struct file * filp,
PIPE_LOCK(*inode)--;
wake_up_interruptible(&PIPE_WAIT(*inode));
if (read) {
- if (DO_UPDATE_ATIME(inode)) {
- inode->i_atime = CURRENT_TIME;
- inode->i_dirt = 1;
- }
+ UPDATE_ATIME(inode);
return read;
}
if (PIPE_WRITERS(*inode))
@@ -132,7 +129,7 @@ static long pipe_write(struct inode * inode, struct file * filp,
free = 1;
}
inode->i_ctime = inode->i_mtime = CURRENT_TIME;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
return written;
}
@@ -168,7 +165,7 @@ static int pipe_ioctl(struct inode *pino, struct file * filp,
static unsigned int pipe_poll(struct file * filp, poll_table * wait)
{
unsigned int mask;
- struct inode * inode = filp->f_inode;
+ struct inode * inode = filp->f_dentry->d_inode;
poll_wait(&PIPE_WAIT(*inode), wait);
mask = POLLIN | POLLRDNORM;
@@ -189,7 +186,7 @@ static unsigned int pipe_poll(struct file * filp, poll_table * wait)
static unsigned int fifo_poll(struct file * filp, poll_table * wait)
{
unsigned int mask;
- struct inode * inode = filp->f_inode;
+ struct inode * inode = filp->f_dentry->d_inode;
poll_wait(&PIPE_WAIT(*inode), wait);
mask = POLLIN | POLLRDNORM;
@@ -221,7 +218,7 @@ static long connect_read(struct inode * inode, struct file * filp,
static unsigned int connect_poll(struct file * filp, poll_table * wait)
{
- struct inode * inode = filp->f_inode;
+ struct inode * inode = filp->f_dentry->d_inode;
poll_wait(&PIPE_WAIT(*inode), wait);
if (!PIPE_EMPTY(*inode)) {
@@ -233,18 +230,26 @@ static unsigned int connect_poll(struct file * filp, poll_table * wait)
return POLLOUT | POLLWRNORM;
}
-static int pipe_read_release(struct inode * inode, struct file * filp)
+static int pipe_release(struct inode * inode)
{
- PIPE_READERS(*inode)--;
+ if (!PIPE_READERS(*inode) && !PIPE_WRITERS(*inode)) {
+ free_page((unsigned long) PIPE_BASE(*inode));
+ PIPE_BASE(*inode) = NULL;
+ }
wake_up_interruptible(&PIPE_WAIT(*inode));
return 0;
}
+static int pipe_read_release(struct inode * inode, struct file * filp)
+{
+ PIPE_READERS(*inode)--;
+ return pipe_release(inode);
+}
+
static int pipe_write_release(struct inode * inode, struct file * filp)
{
PIPE_WRITERS(*inode)--;
- wake_up_interruptible(&PIPE_WAIT(*inode));
- return 0;
+ return pipe_release(inode);
}
static int pipe_rdwr_release(struct inode * inode, struct file * filp)
@@ -253,8 +258,7 @@ static int pipe_rdwr_release(struct inode * inode, struct file * filp)
PIPE_READERS(*inode)--;
if (filp->f_mode & FMODE_WRITE)
PIPE_WRITERS(*inode)--;
- wake_up_interruptible(&PIPE_WAIT(*inode));
- return 0;
+ return pipe_release(inode);
}
static int pipe_read_open(struct inode * inode, struct file * filp)
@@ -373,6 +377,42 @@ struct file_operations rdwr_pipe_fops = {
NULL
};
+static struct inode * get_pipe_inode(void)
+{
+ extern struct inode_operations pipe_inode_operations;
+ struct inode *inode = get_empty_inode();
+
+ if (inode) {
+ unsigned long page = __get_free_page(GFP_USER);
+
+ if (!page) {
+ iput(inode);
+ inode = NULL;
+ } else {
+ PIPE_BASE(*inode) = (char *) page;
+ inode->i_op = &pipe_inode_operations;
+ PIPE_WAIT(*inode) = NULL;
+ PIPE_START(*inode) = PIPE_LEN(*inode) = 0;
+ PIPE_RD_OPENERS(*inode) = PIPE_WR_OPENERS(*inode) = 0;
+ PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 1;
+ PIPE_LOCK(*inode) = 0;
+ /*
+ * Mark the inode dirty from the very beginning,
+ * that way it will never be moved to the dirty
+ * list because "make_inode_dirty()" will think
+ * that it already _is_ on the dirty list.
+ */
+ inode->i_state = 1 << I_DIRTY;
+ inode->i_mode = S_IFIFO | S_IRUSR | S_IWUSR;
+ inode->i_uid = current->fsuid;
+ inode->i_gid = current->fsgid;
+ inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ inode->i_blksize = PAGE_SIZE;
+ }
+ }
+ return inode;
+}
+
struct inode_operations pipe_inode_operations = {
&rdwr_pipe_fops,
NULL, /* create */
@@ -422,12 +462,14 @@ int do_pipe(int *fd)
goto close_f12_inode_i;
j = error;
- f1->f_inode = f2->f_inode = inode;
+ f1->f_dentry = f2->f_dentry = dget(d_alloc_root(inode, NULL));
+
/* read file */
f1->f_pos = f2->f_pos = 0;
f1->f_flags = O_RDONLY;
f1->f_op = &read_pipe_fops;
f1->f_mode = 1;
+
/* write file */
f2->f_flags = O_WRONLY;
f2->f_op = &write_pipe_fops;
@@ -441,7 +483,6 @@ int do_pipe(int *fd)
close_f12_inode_i:
put_unused_fd(i);
close_f12_inode:
- atomic_dec(&inode->i_count);
iput(inode);
close_f12:
put_filp(f2);
diff --git a/fs/proc/Makefile b/fs/proc/Makefile
index 6f336245d..68e2a3485 100644
--- a/fs/proc/Makefile
+++ b/fs/proc/Makefile
@@ -8,7 +8,7 @@
# Note 2! The CFLAGS definitions are now in the main makefile...
O_TARGET := proc.o
-O_OBJS := inode.o root.o base.o generic.o mem.o link.o arbitrary.o fd.o array.o \
+O_OBJS := inode.o root.o base.o generic.o mem.o link.o fd.o array.o \
kmsg.o scsi.o proc_tty.o
ifdef CONFIG_OMIRR
O_OBJS := $(O_OBJS) omirr.o
diff --git a/fs/proc/arbitrary.c b/fs/proc/arbitrary.c
deleted file mode 100644
index 1e18e594e..000000000
--- a/fs/proc/arbitrary.c
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * $Id: arbitrary.c,v 1.2 1997/06/05 01:27:47 davem Exp $
- *
- * linux/fs/proc/arbitrary.c - lookup() for arbitrary inodes.
- * Copyright (C) 1997, Thomas Schoebel-Theuer,
- * <schoebel@informatik.uni-stuttgart.de>.
- */
-
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/kdev_t.h>
-#include <linux/fs.h>
-
-/* Format of dev/inode pairs that can be used as file names:
- * [<dev_number_in_hex]:<inode_number_in_decimal>
- * (the same format that is already in use in /proc/<pid>/exe,
- * /proc/<pid>/cwd and /proc/<pid>/root).
- */
-/* Note that readdir does not supply such names, so they must be used
- * either "blind" or must be queried another way, for example
- * as result of a virtual symlink (see linux/proc/link.c).
- */
-int proc_arbitrary_lookup(struct inode * dir, const char * name,
- int len, struct inode ** result)
-{
- int dev, ino;
- char * ptr = (char*)name;
- kdev_t kdev;
- int i;
- int error = -EINVAL;
-
- if(*ptr++ != '[')
- goto done;
- dev = simple_strtoul(ptr, &ptr, 16);
- if(*ptr++ != ']')
- goto done;
- if(*ptr++ != ':')
- goto done;
- ino = simple_strtoul(ptr, &ptr, 0);
- if((long)ptr - (long)name != len)
- goto done;
-
- error = -ENOENT;
- kdev = to_kdev_t(dev);
- if(!kdev)
- goto done;
- for(i = 0; i < NR_SUPER; i++)
- if(super_blocks[i].s_dev == kdev)
- break;
- if(i < NR_SUPER) {
- *result = iget(&super_blocks[i], ino);
- if(*result)
- error = 0;
- }
-done:
- iput(dir);
- return error;
-}
diff --git a/fs/proc/array.c b/fs/proc/array.c
index 518ef1b4c..773b96873 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -612,7 +612,7 @@ static inline char * task_mem(struct task_struct *p, char *buffer)
for (vma = mm->mmap; vma; vma = vma->vm_next) {
unsigned long len = (vma->vm_end - vma->vm_start) >> 10;
- if (!vma->vm_inode) {
+ if (!vma->vm_dentry) {
data += len;
if (vma->vm_flags & VM_GROWSDOWN)
stack += len;
@@ -970,12 +970,11 @@ static long read_maps (int pid, struct file * file,
*cp++ = flags & VM_MAYSHARE ? 's' : 'p';
*cp++ = 0;
- if (map->vm_inode != NULL) {
- dev = map->vm_inode->i_dev;
- ino = map->vm_inode->i_ino;
- } else {
- dev = 0;
- ino = 0;
+ dev = 0;
+ ino = 0;
+ if (map->vm_dentry != NULL) {
+ dev = map->vm_dentry->d_inode->i_dev;
+ ino = map->vm_dentry->d_inode->i_ino;
}
len = sprintf(line,
@@ -1237,6 +1236,7 @@ struct inode_operations proc_array_inode_operations = {
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
@@ -1282,6 +1282,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 b983e73f6..7e9a65e08 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 884631db8..1e1fb494f 100644
--- a/fs/proc/fd.c
+++ b/fs/proc/fd.c
@@ -14,7 +14,7 @@
#include <linux/stat.h>
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 dentry *);
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,39 +52,36 @@ struct inode_operations proc_fd_inode_operations = {
NULL /* permission */
};
-static int proc_lookupfd(struct inode * dir, const char * name, int len,
- struct inode ** result)
+/*
+ * NOTE! Normally we'd indicate that a file does not
+ * exist by creating a negative dentry and returning
+ * a successful return code. However, for this case
+ * we do not want to create negative dentries, because
+ * the state of the world can change behind our backs.
+ *
+ * Thus just return -ENOENT instead.
+ */
+static int proc_lookupfd(struct inode * dir, struct dentry * dentry)
{
unsigned int ino, pid, fd, c;
struct task_struct * p;
struct super_block * sb;
+ struct inode *inode;
+ const char *name;
+ int len;
- *result = NULL;
ino = dir->i_ino;
pid = ino >> 16;
ino &= 0x0000ffff;
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 = dentry->d_name.len;
+ name = dentry->d_name.name;
while (len-- > 0) {
c = *name - '0';
name++;
@@ -111,13 +109,16 @@ static int proc_lookupfd(struct inode * dir, const char * name, int len,
if (fd >= NR_OPEN ||
!p->files ||
!p->files->fd[fd] ||
- !p->files->fd[fd]->f_inode)
+ !p->files->fd[fd]->f_dentry)
return -ENOENT;
ino = (pid << 16) + (PROC_PID_FD_DIR << 8) + fd;
- if (!(*result = proc_get_inode(sb, ino, NULL)))
+ inode = proc_get_inode(sb, ino, NULL);
+ if (!inode)
return -ENOENT;
+
+ d_add(dentry, inode);
return 0;
}
@@ -155,7 +156,7 @@ static int proc_readfd(struct inode * inode, struct file * filp,
for (fd -= 2 ; fd < NR_OPEN; fd++, filp->f_pos++) {
if (!p->files)
break;
- if (!p->files->fd[fd] || !p->files->fd[fd]->f_inode)
+ if (!p->files->fd[fd] || !p->files->fd[fd]->f_dentry)
continue;
j = NUMBUF;
diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index 1424dd1ef..358060020 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 943137bf4..4bc4a4f17 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -25,8 +25,14 @@ static void proc_put_inode(struct inode *inode)
&& proc_openprom_use)
(*proc_openprom_use)(inode, 0);
#endif
- if (inode->i_nlink)
- return;
+}
+
+/*
+ * Does this ever happen?
+ */
+static void proc_delete_inode(struct inode *inode)
+{
+ printk("proc_delete_inode()?\n");
inode->i_size = 0;
}
@@ -39,9 +45,10 @@ static void proc_put_super(struct super_block *sb)
static struct super_operations proc_sops = {
proc_read_inode,
- NULL,
proc_write_inode,
proc_put_inode,
+ proc_delete_inode,
+ NULL,
proc_put_super,
NULL,
proc_statfs,
@@ -131,16 +138,17 @@ 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;
}
-void proc_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
+int proc_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
{
struct statfs tmp;
@@ -152,7 +160,7 @@ void proc_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
tmp.f_files = 0;
tmp.f_ffree = 0;
tmp.f_namelen = NAME_MAX;
- copy_to_user(buf, &tmp, bufsiz);
+ return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
}
void proc_read_inode(struct inode * inode)
@@ -200,5 +208,4 @@ void proc_read_inode(struct inode * inode)
void proc_write_inode(struct inode * inode)
{
- inode->i_dirt=0;
}
diff --git a/fs/proc/kmsg.c b/fs/proc/kmsg.c
index 6ef386ffa..1cc6a9c83 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 695ed9bba..c25fd702b 100644
--- a/fs/proc/link.c
+++ b/fs/proc/link.c
@@ -14,9 +14,9 @@
#include <linux/mm.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
-#include <linux/dalloc.h>
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 +52,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 +60,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_dentry);
+
vma = vma->vm_next;
}
break;
@@ -122,45 +116,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_dentry)
+ break;
+ result = dget(p->files->fd[ino]->f_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 = buflen<PAGE_SIZE ? buflen : PAGE_SIZE;
- if(len <= min)
- min = len+1;
- copy_to_user(buffer, tmp, min);
- free_page((unsigned long)tmp);
- error = len;
- } else {
- error= -ENOENT;
+ error = PTR_ERR(dentry);
+ if (!IS_ERR(dentry)) {
+ error = -ENOENT;
+ if (dentry) {
+ char * tmp = (char*)__get_free_page(GFP_KERNEL);
+ int len = d_path(dentry, current->fs->root, tmp);
+ int min = buflen<PAGE_SIZE ? buflen : PAGE_SIZE;
+ if(len <= min)
+ min = len+1;
+ dput(dentry);
+ copy_to_user(buffer, tmp, min);
+ free_page((unsigned long)tmp);
+ error = len;
+ }
}
- iput(inode);
return error;
}
diff --git a/fs/proc/mem.c b/fs/proc/mem.c
index a64ead624..97acb5ee8 100644
--- a/fs/proc/mem.c
+++ b/fs/proc/mem.c
@@ -328,6 +328,7 @@ struct inode_operations proc_mem_inode_operations = {
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
diff --git a/fs/proc/net.c b/fs/proc/net.c
index 3bc5c339c..257487569 100644
--- a/fs/proc/net.c
+++ b/fs/proc/net.c
@@ -111,6 +111,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/omirr.c b/fs/proc/omirr.c
index 0e6377fb2..041e493d2 100644
--- a/fs/proc/omirr.c
+++ b/fs/proc/omirr.c
@@ -7,7 +7,6 @@
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/fs.h>
-#include <linux/dalloc.h>
#include <linux/omirr.h>
#include <asm/uaccess.h>
@@ -288,6 +287,7 @@ struct inode_operations proc_omirr_inode_operations = {
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
diff --git a/fs/proc/openpromfs.c b/fs/proc/openpromfs.c
index 7d741cfaf..346e72ed1 100644
--- a/fs/proc/openpromfs.c
+++ b/fs/proc/openpromfs.c
@@ -1,7 +1,7 @@
-/* $Id: openpromfs.c,v 1.15 1997/06/05 01:28:11 davem Exp $
+/* $Id: openpromfs.c,v 1.18 1997/07/17 02:24:01 davem Exp $
* openpromfs.c: /proc/openprom handling routines
*
- * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
*/
#include <linux/module.h>
@@ -68,7 +68,7 @@ static long nodenum_read(struct inode *inode, struct file *file,
if (count < 0 || !inode->u.generic_ip)
return -EINVAL;
- sprintf (buffer, "%8.8x\n", (u32)(inode->u.generic_ip));
+ sprintf (buffer, "%8.8x\n", (u32)(long)(inode->u.generic_ip));
if (file->f_pos >= 9)
return 0;
if (count > 9 - file->f_pos)
@@ -86,27 +86,28 @@ static long property_read(struct inode *inode, struct file *filp,
char *p;
u32 *q;
openprom_property *op;
+ char buffer[64];
if (filp->f_pos >= 0xffffff)
return -EINVAL;
if (!filp->private_data) {
- node = nodes[(u16)((uint)inode->u.generic_ip)].node;
- i = ((u32)inode->u.generic_ip) >> 16;
- if ((u16)((uint)inode->u.generic_ip) == aliases) {
+ node = nodes[(u16)((long)inode->u.generic_ip)].node;
+ i = ((u32)(long)inode->u.generic_ip) >> 16;
+ if ((u16)((long)inode->u.generic_ip) == aliases) {
if (i >= aliases_nodes)
p = 0;
else
p = alias_names [i];
} else
- for (p = prom_firstprop (node);
+ for (p = prom_firstprop (node, buffer);
i && p && *p;
- p = prom_nextprop (node, p), i--)
+ p = prom_nextprop (node, p, buffer), i--)
/* nothing */ ;
if (!p || !*p)
return -EIO;
i = prom_getproplen (node, p);
if (i < 0) {
- if ((u16)((uint)inode->u.generic_ip) == aliases)
+ if ((u16)((long)inode->u.generic_ip) == aliases)
i = 0;
else
return -EIO;
@@ -155,7 +156,7 @@ static long property_read(struct inode *inode, struct file *filp,
if (count > i - k) count = i - k;
if (op->flag & OPP_STRING) {
if (!k) {
- *buf = '\'';
+ __put_user('\'', buf);
k++;
count--;
}
@@ -170,9 +171,9 @@ static long property_read(struct inode *inode, struct file *filp,
k += j;
}
if (count)
- buf [k++ - filp->f_pos] = '\'';
+ __put_user('\'', &buf [k++ - filp->f_pos]);
if (count > 1)
- buf [k++ - filp->f_pos] = '\n';
+ __put_user('\n', &buf [k++ - filp->f_pos]);
} else if (op->flag & OPP_BINARY) {
char buffer[10];
u32 *first, *last;
@@ -186,26 +187,26 @@ static long property_read(struct inode *inode, struct file *filp,
if (first == last) {
sprintf (buffer, "%08x.", *first);
- memcpy (buf, buffer + first_off, last_cnt - first_off);
+ copy_to_user (buf, buffer + first_off, last_cnt - first_off);
buf += last_cnt - first_off;
} else {
for (q = first; q <= last; q++) {
sprintf (buffer, "%08x.", *q);
if (q == first) {
- memcpy (buf, buffer + first_off,
- 9 - first_off);
+ copy_to_user (buf, buffer + first_off,
+ 9 - first_off);
buf += 9 - first_off;
} else if (q == last) {
- memcpy (buf, buffer, last_cnt);
+ copy_to_user (buf, buffer, last_cnt);
buf += last_cnt;
} else {
- memcpy (buf, buffer, 9);
+ copy_to_user (buf, buffer, 9);
buf += 9;
}
}
}
if (last == (u32 *)(op->value + op->len - 4) && last_cnt == 9)
- *(buf - 1) = '\n';
+ __put_user('\n', (buf - 1));
k += count;
}
count = k - filp->f_pos;
@@ -242,8 +243,10 @@ static long property_write(struct inode *inode, struct file *filp,
for (i = 0; i < count; i++, j++) {
if (j == 9) j = 0;
if (!j) {
- if (buf [i] != '.') {
- if (buf [i] != '\n') {
+ char ctmp;
+ __get_user(ctmp, &buf[i]);
+ if (ctmp != '.') {
+ if (ctmp != '\n') {
if (op->flag & OPP_BINARY)
return -EINVAL;
else
@@ -255,10 +258,12 @@ static long property_write(struct inode *inode, struct file *filp,
}
}
} else {
- if (buf [i] < '0' ||
- (buf [i] > '9' && buf [i] < 'A') ||
- (buf [i] > 'F' && buf [i] < 'a') ||
- buf [i] > 'f') {
+ char ctmp;
+ __get_user(ctmp, &buf[i]);
+ if (ctmp < '0' ||
+ (ctmp > '9' && ctmp < 'A') ||
+ (ctmp > 'F' && ctmp < 'a') ||
+ ctmp > 'f') {
if (op->flag & OPP_BINARY)
return -EINVAL;
else
@@ -292,8 +297,8 @@ static long property_write(struct inode *inode, struct file *filp,
last_cnt = (k + count) % 9;
if (first + 1 == last) {
memset (tmp, '0', 8);
- memcpy (tmp + first_off, buf, (count + first_off > 8) ?
- 8 - first_off : count);
+ copy_from_user (tmp + first_off, buf,
+ (count + first_off > 8) ? 8 - first_off : count);
mask = 0xffffffff;
mask2 = 0xffffffff;
for (j = 0; j < first_off; j++)
@@ -312,8 +317,8 @@ static long property_write(struct inode *inode, struct file *filp,
if (q == first) {
if (first_off < 8) {
memset (tmp, '0', 8);
- memcpy (tmp + first_off, buf,
- 8 - first_off);
+ copy_from_user (tmp + first_off, buf,
+ 8 - first_off);
mask = 0xffffffff;
for (j = 0; j < first_off; j++)
mask >>= 1;
@@ -324,7 +329,7 @@ static long property_write(struct inode *inode, struct file *filp,
} else if ((q == last - 1) && last_cnt
&& (last_cnt < 8)) {
memset (tmp, '0', 8);
- memcpy (tmp, buf, last_cnt);
+ copy_from_user (tmp, buf, last_cnt);
mask = 0xffffffff;
for (j = 0; j < 8 - last_cnt; j++)
mask <<= 1;
@@ -332,7 +337,10 @@ static long property_write(struct inode *inode, struct file *filp,
*q |= simple_strtoul (tmp, 0, 16);
buf += last_cnt;
} else {
- *q = simple_strtoul (buf, 0, 16);
+ char tchars[17]; /* XXX yuck... */
+
+ copy_from_user(tchars, buf, 16);
+ *q = simple_strtoul (tchars, 0, 16);
buf += 9;
}
}
@@ -347,12 +355,15 @@ static long property_write(struct inode *inode, struct file *filp,
write_try_string:
if (!(op->flag & OPP_BINARY)) {
if (!(op->flag & (OPP_QUOTED | OPP_NOTQUOTED))) {
+ char ctmp;
+
/* No way, if somebody starts writing from the middle,
* we don't know whether he uses quotes around or not
*/
if (k > 0)
return -EINVAL;
- if (*buf == '\'') {
+ __get_user(ctmp, buf);
+ if (ctmp == '\'') {
op->flag |= OPP_QUOTED;
buf++;
count--;
@@ -383,7 +394,7 @@ write_try_string:
kfree (b);
}
p = op->value + filp->f_pos - ((op->flag & OPP_QUOTED) ? 1 : 0);
- memcpy (p, buf, count);
+ copy_from_user (p, buf, count);
op->flag |= OPP_DIRTY;
for (i = 0; i < count; i++, p++)
if (*p == '\n') {
@@ -414,8 +425,8 @@ int property_release (struct inode *inode, struct file *filp)
if (!op)
return 0;
- node = nodes[(u16)((uint)inode->u.generic_ip)].node;
- if ((u16)((uint)inode->u.generic_ip) == aliases) {
+ node = nodes[(u16)((long)inode->u.generic_ip)].node;
+ if ((u16)((long)inode->u.generic_ip) == aliases) {
if ((op->flag & OPP_DIRTY) && (op->flag & OPP_STRING)) {
char *p = op->name;
int i = (op->value - op->name) - strlen (op->name) - 1;
@@ -484,6 +495,7 @@ static struct inode_operations openpromfs_prop_inode_ops = {
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
@@ -516,6 +528,7 @@ static struct inode_operations openpromfs_nodenum_inode_ops = {
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
@@ -548,6 +561,7 @@ static struct inode_operations openprom_alias_inode_operations = {
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
@@ -604,6 +618,7 @@ static int openpromfs_lookup(struct inode * dir, const char * name, int len,
int i;
struct inode *inode;
struct openpromfs_dev *d = NULL;
+ char buffer2[64];
*result = NULL;
if (!dir || !S_ISDIR(dir->i_mode)) {
@@ -659,9 +674,9 @@ static int openpromfs_lookup(struct inode * dir, const char * name, int len,
if (!ino) {
int j = NODEP2INO(NODE(dir->i_ino).first_prop);
if (dirnode != aliases) {
- for (p = prom_firstprop (n);
+ for (p = prom_firstprop (n, buffer2);
p && *p;
- p = prom_nextprop (n, p)) {
+ p = prom_nextprop (n, p, buffer2)) {
j++;
if ((len == strlen (p))
&& !strncmp (p, name, len)) {
@@ -720,7 +735,7 @@ static int openpromfs_lookup(struct inode * dir, const char * name, int len,
inode->i_mode = S_IFREG | S_IRUGO;
inode->i_op = &openpromfs_nodenum_inode_ops;
inode->i_nlink = 1;
- inode->u.generic_ip = (void *)(n);
+ inode->u.generic_ip = (void *)(long)(n);
break;
case OPFSL_PROPERTY:
if ((dirnode == options) && (len == 17)
@@ -737,7 +752,7 @@ static int openpromfs_lookup(struct inode * dir, const char * name, int len,
inode->i_nlink = 1;
if (inode->i_size < 0)
inode->i_size = 0;
- inode->u.generic_ip = (void *)(((u16)dirnode) |
+ inode->u.generic_ip = (void *)(long)(((u16)dirnode) |
(((u16)(ino - NODEP2INO(NODE(dir->i_ino).first_prop) - 1)) << 16));
break;
case OPFSL_DEVICE:
@@ -761,6 +776,7 @@ static int openpromfs_readdir(struct inode * inode, struct file * filp,
u16 node;
char *p;
struct openpromfs_dev *d;
+ char buffer2[64];
if (!inode || !S_ISDIR (inode->i_mode)) return -ENOTDIR;
ino = inode->i_ino;
@@ -813,9 +829,9 @@ static int openpromfs_readdir(struct inode * inode, struct file * filp,
}
}
} else {
- for (p = prom_firstprop (n);
+ for (p = prom_firstprop (n, buffer2);
p && *p;
- p = prom_nextprop (n, p)) {
+ p = prom_nextprop (n, p, buffer2)) {
j++;
if (i) i--;
else {
@@ -877,7 +893,7 @@ static int openpromfs_create (struct inode *dir, const char *name, int len,
inode->i_op = &openpromfs_prop_inode_ops;
inode->i_nlink = 1;
if (inode->i_size < 0) inode->i_size = 0;
- inode->u.generic_ip = (void *)(((u16)aliases) |
+ inode->u.generic_ip = (void *)(long)(((u16)aliases) |
(((u16)(aliases_nodes - 1)) << 16));
*result = inode;
return 0;
@@ -940,6 +956,7 @@ static u16 get_nodes (u16 parent, u32 node)
{
char *p;
u16 n = last_node++, i;
+ char buffer[64];
if (check_space (n) < 0)
return 0xffff;
@@ -961,15 +978,15 @@ static u16 get_nodes (u16 parent, u32 node)
}
}
if (n != aliases)
- for (p = prom_firstprop (node);
+ for (p = prom_firstprop (node, buffer);
p && p != (char *)-1 && *p;
- p = prom_nextprop (node, p))
+ p = prom_nextprop (node, p, buffer))
first_prop++;
else {
char *q;
- for (p = prom_firstprop (node);
+ for (p = prom_firstprop (node, buffer);
p && p != (char *)-1 && *p;
- p = prom_nextprop (node, p)) {
+ p = prom_nextprop (node, p, buffer)) {
if (aliases_nodes == ALIASES_NNODES)
break;
for (i = 0; i < aliases_nodes; i++)
@@ -1012,7 +1029,7 @@ void openpromfs_use (struct inode *inode, int inc)
static int usec = 0;
if (inc) {
- if (atomic_read(&inode->i_count) == 1)
+ if (inode->i_count == 1)
usec++;
else if (root_fresh && inode->i_ino == PROC_OPENPROM_FIRST) {
root_fresh = 0;
@@ -1025,10 +1042,10 @@ void openpromfs_use (struct inode *inode, int inc)
usec--;
}
printk ("openpromfs_use: %d %d %d %d\n",
- inode->i_ino, inc, usec, atomic_read(&inode->i_count));
+ inode->i_ino, inc, usec, inode->i_count);
#else
if (inc) {
- if (atomic_read(&inode->i_count) == 1)
+ if (inode->i_count == 1)
MOD_INC_USE_COUNT;
else if (root_fresh && inode->i_ino == PROC_OPENPROM_FIRST) {
root_fresh = 0;
@@ -1059,8 +1076,10 @@ EXPORT_NO_SYMBOLS;
int init_module (void)
#endif
{
+#ifndef __sparc_v9__
if (!romvec->pv_romvers)
return RET(ENODEV);
+#endif
nodes = (openpromfs_node *)__get_free_pages(GFP_KERNEL, 0, 0);
if (!nodes) {
printk (KERN_WARNING "/proc/openprom: can't get free page\n");
diff --git a/fs/proc/procfs_syms.c b/fs/proc/procfs_syms.c
index 71c29dd75..d3077ea79 100644
--- a/fs/proc/procfs_syms.c
+++ b/fs/proc/procfs_syms.c
@@ -38,7 +38,7 @@ EXPORT_SYMBOL(proc_openprom_deregister);
static struct file_system_type proc_fs_type = {
"proc",
- FS_NO_DCACHE,
+ 0 /* FS_NO_DCACHE doesn't work correctly */,
proc_read_super,
NULL
};
diff --git a/fs/proc/root.c b/fs/proc/root.c
index f42557d2c..2b456ca57 100644
--- a/fs/proc/root.c
+++ b/fs/proc/root.c
@@ -24,7 +24,7 @@
#define FIRST_PROCESS_ENTRY 256
static int proc_root_readdir(struct inode *, struct file *, void *, filldir_t);
-static int proc_root_lookup(struct inode *,const char *,int,struct inode **);
+static int proc_root_lookup(struct inode *,struct dentry *);
static unsigned char proc_alloc_map[PROC_NDYNAMIC / 8] = {0};
@@ -64,6 +64,7 @@ struct inode_operations proc_dir_inode_operations = {
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
@@ -104,6 +105,7 @@ static struct inode_operations proc_root_inode_operations = {
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
@@ -149,14 +151,14 @@ struct proc_dir_entry proc_sys_root = {
#if defined(CONFIG_SUN_OPENPROMFS) || defined(CONFIG_SUN_OPENPROMFS_MODULE)
static int (*proc_openprom_defreaddir_ptr)(struct inode *, struct file *, void *, filldir_t);
-static int (*proc_openprom_deflookup_ptr)(struct inode *, const char *, int, struct inode **);
+static int (*proc_openprom_deflookup_ptr)(struct inode *, struct qstr *, struct inode **);
void (*proc_openprom_use)(struct inode *, int) = 0;
static struct openpromfs_dev *proc_openprom_devices = NULL;
static ino_t proc_openpromdev_ino = PROC_OPENPROMD_FIRST;
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 ***devices)
{
@@ -218,15 +220,13 @@ proc_openprom_defreaddir(struct inode * inode, struct file * filp,
}
static int
-proc_openprom_deflookup(struct inode * dir,const char * name, int len,
- struct inode ** result)
+proc_openprom_deflookup(struct inode * dir, struct qstr *str, struct inode ** result)
{
request_module("openpromfs");
if (proc_openprom_inode_operations.lookup !=
proc_openprom_deflookup)
return proc_openprom_inode_operations.lookup
- (dir, name, len, result);
- iput(dir);
+ (dir, str, result);
return -ENOENT;
}
#endif
@@ -264,6 +264,7 @@ struct inode_operations proc_openprom_inode_operations = {
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
@@ -350,7 +351,6 @@ static int proc_self_readlink(struct inode * inode, char * buffer, int buflen)
int len;
char tmp[30];
- iput(inode);
len = sprintf(tmp, "%d", current->pid);
if (buflen < len)
len = buflen;
@@ -358,6 +358,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 +379,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 */
@@ -611,93 +621,49 @@ void proc_root_init(void)
proc_tty_init();
}
-
-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)
+/*
+ * Don't create negative dentries here, return -ENOENT by hand
+ * instead.
+ */
+int proc_lookup(struct inode * dir, struct dentry *dentry)
{
+ struct inode *inode;
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);
- 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)
- return -EINVAL;
- *result = inode;
- return 0;
+ inode = NULL;
+ if (de) {
+ for (de = de->subdir; de ; de = de->next) {
+ if (!de || !de->low_ino)
+ continue;
+ if (de->namelen != dentry->d_name.len)
+ continue;
+ if (!memcmp(dentry->d_name.name, de->name, de->namelen)) {
+ int ino = de->low_ino | (dir->i_ino & ~(0xffff));
+ inode = proc_get_inode(dir->i_sb, ino, de);
+ if (!inode)
+ return -EINVAL;
+ break;
+ }
}
}
-
- *result = NULL;
- for (de = de->subdir; de ; de = de->next) {
- if (proc_match(len, name, de))
- break;
- }
- if (!de) {
- iput(dir);
+ if (!inode)
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);
+ d_add(dentry, inode);
return 0;
}
-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 dentry * dentry)
{
unsigned int pid, c;
- int ino, retval;
struct task_struct *p;
-
- atomic_inc(&dir->i_count);
+ const char *name;
+ struct inode *inode;
+ int len;
if (dir->i_ino == PROC_ROOT_INO) { /* check for safety... */
dir->i_nlink = proc_root.nlink;
@@ -710,13 +676,12 @@ 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);
- return retval;
- }
+ if (!proc_lookup(dir, dentry))
+ return 0;
pid = 0;
+ name = dentry->d_name.name;
+ len = dentry->d_name.len;
while (len-- > 0) {
c = *name - '0';
name++;
@@ -732,16 +697,14 @@ static int proc_root_lookup(struct inode * dir,const char * name, int len,
}
}
p = find_task_by_pid(pid);
- if (!pid || !p) {
- iput(dir);
- return -ENOENT;
- }
- ino = (pid << 16) + PROC_PID_INO;
- if (!(*result = proc_get_inode(dir->i_sb, ino, &proc_pid))) {
- iput(dir);
- return -EINVAL;
+ inode = NULL;
+ if (pid && p) {
+ unsigned long ino = (pid << 16) + PROC_PID_INO;
+ inode = proc_get_inode(dir->i_sb, ino, &proc_pid);
+ if (!inode)
+ return -EINVAL;
}
- iput(dir);
+ d_add(dentry, inode);
return 0;
}
diff --git a/fs/proc/scsi.c b/fs/proc/scsi.c
index fd629a75c..b1e77398c 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/read_write.c b/fs/read_write.c
index 81b19ac30..6c422b7f4 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -60,13 +60,15 @@ asmlinkage long sys_lseek(unsigned int fd, off_t offset, unsigned int origin)
{
long retval;
struct file * file;
+ struct dentry * dentry;
struct inode * inode;
lock_kernel();
retval = -EBADF;
if (fd >= NR_OPEN ||
!(file = current->files->fd[fd]) ||
- !(inode = file->f_inode))
+ !(dentry = file->f_dentry) ||
+ !(inode = dentry->d_inode))
goto bad;
retval = -EINVAL;
if (origin > 2)
@@ -83,6 +85,7 @@ asmlinkage int sys_llseek(unsigned int fd, unsigned long offset_high,
{
long retval;
struct file * file;
+ struct dentry * dentry;
struct inode * inode;
long long offset;
@@ -90,7 +93,8 @@ asmlinkage int sys_llseek(unsigned int fd, unsigned long offset_high,
retval = -EBADF;
if (fd >= NR_OPEN ||
!(file = current->files->fd[fd]) ||
- !(inode = file->f_inode))
+ !(dentry = file->f_dentry) ||
+ !(inode = dentry->d_inode))
goto bad;
retval = -EINVAL;
if (origin > 2)
@@ -115,6 +119,7 @@ asmlinkage long sys_read(unsigned int fd, char * buf, unsigned long count)
{
int error;
struct file * file;
+ struct dentry * dentry;
struct inode * inode;
long (*read)(struct inode *, struct file *, char *, unsigned long);
@@ -123,7 +128,10 @@ asmlinkage long sys_read(unsigned int fd, char * buf, unsigned long count)
file = fget(fd);
if (!file)
goto bad_file;
- inode = file->f_inode;
+ dentry = file->f_dentry;
+ if (!dentry)
+ goto bad_file;
+ inode = dentry->d_inode;
if (!inode)
goto out;
error = -EBADF;
@@ -137,7 +145,7 @@ asmlinkage long sys_read(unsigned int fd, char * buf, unsigned long count)
goto out;
error = read(inode,file,buf,count);
out:
- fput(file, inode);
+ fput(file);
bad_file:
unlock_kernel();
return error;
@@ -147,6 +155,7 @@ asmlinkage long sys_write(unsigned int fd, const char * buf, unsigned long count
{
int error;
struct file * file;
+ struct dentry * dentry;
struct inode * inode;
long (*write)(struct inode *, struct file *, const char *, unsigned long);
@@ -155,7 +164,10 @@ asmlinkage long sys_write(unsigned int fd, const char * buf, unsigned long count
file = fget(fd);
if (!file)
goto bad_file;
- inode = file->f_inode;
+ dentry = file->f_dentry;
+ if (!dentry)
+ goto out;
+ inode = dentry->d_inode;
if (!inode)
goto out;
if (!(file->f_mode & 2))
@@ -168,10 +180,9 @@ asmlinkage long sys_write(unsigned int fd, const char * buf, unsigned long count
goto out;
down(&inode->i_sem);
error = write(inode,file,buf,count);
- inode->i_status |= ST_MODIFIED;
up(&inode->i_sem);
out:
- fput(file, inode);
+ fput(file);
bad_file:
unlock_kernel();
return error;
@@ -264,8 +275,6 @@ static long do_readv_writev(int type, struct inode * inode, struct file * file,
if (nr != len)
break;
}
- if(fn == (IO_fn_t) file->f_op->write)
- inode->i_status |= ST_MODIFIED;
if (iov != iovstack)
kfree(iov);
return retval;
@@ -274,14 +283,30 @@ static long do_readv_writev(int type, struct inode * inode, struct file * file,
asmlinkage long sys_readv(unsigned long fd, const struct iovec * vector, unsigned long count)
{
struct file * file;
+ struct dentry * dentry;
struct inode * inode;
- long err = -EBADF;
+ long err;
lock_kernel();
- if (fd >= NR_OPEN || !(file = current->files->fd[fd]) || !(inode=file->f_inode))
+ err = -EBADF;
+ if (fd >= NR_OPEN)
+ goto out;
+
+ file = current->files->fd[fd];
+ if (!file)
goto out;
+
if (!(file->f_mode & 1))
goto out;
+
+ dentry = file->f_dentry;
+ if (!dentry)
+ goto out;
+
+ inode = dentry->d_inode;
+ if (!inode)
+ goto out;
+
err = do_readv_writev(VERIFY_WRITE, inode, file, vector, count);
out:
unlock_kernel();
@@ -290,15 +315,32 @@ out:
asmlinkage long sys_writev(unsigned long fd, const struct iovec * vector, unsigned long count)
{
- int error = -EBADF;
+ long error;
struct file * file;
+ struct dentry * dentry;
struct inode * inode;
lock_kernel();
- if (fd >= NR_OPEN || !(file = current->files->fd[fd]) || !(inode=file->f_inode))
+ error = -EBADF;
+
+ if (fd >= NR_OPEN)
+ goto out;
+
+ file = current->files->fd[fd];
+ if (!file)
goto out;
+
if (!(file->f_mode & 2))
goto out;
+
+ dentry = file->f_dentry;
+ if (!dentry)
+ goto out;
+
+ inode = dentry->d_inode;
+ if (!inode)
+ goto out;
+
down(&inode->i_sem);
error = do_readv_writev(VERIFY_READ, inode, file, vector, count);
up(&inode->i_sem);
diff --git a/fs/readdir.c b/fs/readdir.c
index a86398ac3..2a2b0a707 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 <linux/config.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/stat.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
-#ifdef CONFIG_TRANS_NAMES
-#include <linux/nametrans.h>
-#endif
-#include <linux/dalloc.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <asm/uaccess.h>
-/* [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,60 +47,49 @@ 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;
}
asmlinkage int old_readdir(unsigned int fd, void * dirent, unsigned int count)
{
- int error = -EBADF;
+ int error;
struct file * file;
+ struct dentry * dentry;
+ struct inode * inode;
struct readdir_callback buf;
- off_t oldpos;
lock_kernel();
- if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
+ error = -EBADF;
+ if (fd >= NR_OPEN)
goto out;
- error = -ENOTDIR;
- if (!file->f_op || !file->f_op->readdir)
+
+ file = current->files->fd[fd];
+ if (!file)
goto out;
- error = verify_area(VERIFY_WRITE, dirent, sizeof(struct old_linux_dirent));
- if (error)
+
+ dentry = file->f_dentry;
+ if (!dentry)
goto out;
- oldpos = file->f_pos;
- buf.file = file;
- buf.dirent = dirent;
+
+ inode = dentry->d_inode;
+ if (!inode)
+ goto out;
+
buf.count = 0;
- buf.translate = 0;
- if(file->f_pos & TRANS_BIT) {
- file->f_pos &= ~TRANS_BIT;
- buf.translate = 1;
- }
- error = file->f_op->readdir(file->f_inode, file, &buf, fillonedir);
+ buf.dirent = dirent;
+
+ error = -ENOTDIR;
+ if (!file->f_op || !file->f_op->readdir)
+ goto out;
+
+ error = file->f_op->readdir(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 +110,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 +120,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,84 +141,45 @@ 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 dentry * dentry;
+ struct inode * inode;
+ struct linux_dirent * lastdirent;
struct getdents_callback buf;
- int error = -EBADF;
+ int error;
lock_kernel();
- if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
+ error = -EBADF;
+ if (fd >= NR_OPEN)
goto out;
- error = -ENOTDIR;
- if (!file->f_op || !file->f_op->readdir)
+
+ file = current->files->fd[fd];
+ if (!file)
goto out;
- error = verify_area(VERIFY_WRITE, dirent, count);
- if (error)
+
+ dentry = file->f_dentry;
+ if (!dentry)
goto out;
- buf.file = file;
+
+ inode = dentry->d_inode;
+ if (!inode)
+ goto out;
+
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,
- ptr->d_len,
- file->f_pos,
- inode->i_ino);
- if(err)
- break;
- file->f_pos++;
- }
- iput(inode);
- }
- ptr = next;
- } while(ptr != list);
- }
- }
- if (!buf.previous) {
- error = buf.error;
- } else {
- put_user(file->f_pos, &buf.previous->d_off);
+
+ error = -ENOTDIR;
+ if (!file->f_op || !file->f_op->readdir)
+ goto out;
+
+ error = file->f_op->readdir(inode, file, &buf, filldir);
+ if (error < 0)
+ goto out;
+ lastdirent = buf.previous;
+ error = buf.error;
+ if (lastdirent) {
+ put_user(file->f_pos, &lastdirent->d_off);
error = count - buf.count;
}
out:
diff --git a/fs/romfs/inode.c b/fs/romfs/inode.c
index 0ddda855d..5d936ee59 100644
--- a/fs/romfs/inode.c
+++ b/fs/romfs/inode.c
@@ -584,7 +584,7 @@ static struct super_operations romfs_ops = {
static struct file_system_type romfs_fs_type = {
"romfs",
- (FS_REQUIRES_DEV | FS_NO_DCACHE), /* Can dcache be used? */
+ FS_REQUIRES_DEV,
romfs_read_super,
NULL
};
diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c
index 20738b0d2..75d3dbff7 100644
--- a/fs/smbfs/inode.c
+++ b/fs/smbfs/inode.c
@@ -431,7 +431,7 @@ int smb_current_vmalloced;
static struct file_system_type smb_fs_type = {
"smbfs",
- FS_NO_DCACHE,
+ 0 /* FS_NO_DCACHE doesn't work correctly */,
smb_read_super,
NULL
};
diff --git a/fs/smbfs/mmap.c b/fs/smbfs/mmap.c
index 472fad6de..20a2d0569 100644
--- a/fs/smbfs/mmap.c
+++ b/fs/smbfs/mmap.c
@@ -119,8 +119,8 @@ smb_mmap(struct inode *inode, struct file *file, struct vm_area_struct *vma)
inode->i_atime = CURRENT_TIME;
inode->i_dirt = 1;
}
- vma->vm_inode = inode;
- atomic_inc(&inode->i_count);
+
+ vma->vm_dentry = dget(file->f_dentry);
vma->vm_ops = &smb_file_mmap;
return 0;
}
diff --git a/fs/stat.c b/fs/stat.c
index 03f685552..b17c6bb13 100644
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -48,8 +48,6 @@ static int cp_old_stat(struct inode * inode, struct __old_kernel_stat * statbuf)
tmp.st_gid = inode->i_gid;
tmp.st_rdev = kdev_t_to_nr(inode->i_rdev);
tmp.st_size = inode->i_size;
- if (inode->i_pipe)
- tmp.st_size = PIPE_SIZE(*inode);
tmp.st_atime = inode->i_atime;
tmp.st_mtime = inode->i_mtime;
tmp.st_ctime = inode->i_ctime;
@@ -72,8 +70,6 @@ static int cp_new_stat(struct inode * inode, struct stat * statbuf)
tmp.st_gid = inode->i_gid;
tmp.st_rdev = kdev_t_to_nr(inode->i_rdev);
tmp.st_size = inode->i_size;
- if (inode->i_pipe)
- tmp.st_size = PIPE_SIZE(*inode);
tmp.st_atime = inode->i_atime;
tmp.st_mtime = inode->i_mtime;
tmp.st_ctime = inode->i_ctime;
@@ -116,6 +112,7 @@ static int cp_new_stat(struct inode * inode, struct stat * statbuf)
return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0;
}
+
#if !defined(__alpha__) && !defined(__sparc__)
/*
* For backward compatibility? Maybe this should be moved
@@ -123,17 +120,21 @@ static int cp_new_stat(struct inode * inode, struct stat * statbuf)
*/
asmlinkage int sys_stat(char * filename, struct __old_kernel_stat * statbuf)
{
- struct inode * inode;
+ struct dentry * dentry;
int error;
lock_kernel();
- error = namei(NAM_FOLLOW_LINK, filename, &inode);
- if (error)
- goto out;
- if ((error = do_revalidate(inode)) == 0)
- error = cp_old_stat(inode,statbuf);
- iput(inode);
-out:
+ dentry = namei(filename);
+
+ error = PTR_ERR(dentry);
+ if (!IS_ERR(dentry)) {
+ struct inode * inode = dentry->d_inode;
+ error = do_revalidate(inode);
+ if (!error)
+ error = cp_old_stat(inode, statbuf);
+
+ dput(dentry);
+ }
unlock_kernel();
return error;
}
@@ -141,17 +142,21 @@ out:
asmlinkage int sys_newstat(char * filename, struct stat * statbuf)
{
- struct inode * inode;
+ struct dentry * dentry;
int error;
lock_kernel();
- error = namei(NAM_FOLLOW_LINK, filename, &inode);
- if (error)
- goto out;
- if ((error = do_revalidate(inode)) == 0)
- error = cp_new_stat(inode,statbuf);
- iput(inode);
-out:
+ dentry = namei(filename);
+
+ error = PTR_ERR(dentry);
+ if (!IS_ERR(dentry)) {
+ struct inode * inode = dentry->d_inode;
+ error = do_revalidate(inode);
+ if (!error)
+ error = cp_new_stat(inode,statbuf);
+
+ dput(dentry);
+ }
unlock_kernel();
return error;
}
@@ -164,17 +169,21 @@ out:
*/
asmlinkage int sys_lstat(char * filename, struct __old_kernel_stat * statbuf)
{
- struct inode * inode;
+ struct dentry * dentry;
int error;
lock_kernel();
- error = namei(NAM_FOLLOW_TRAILSLASH, filename, &inode);
- if (error)
- goto out;
- if ((error = do_revalidate(inode)) == 0)
- error = cp_old_stat(inode,statbuf);
- iput(inode);
-out:
+ dentry = lnamei(filename);
+
+ error = PTR_ERR(dentry);
+ if (!IS_ERR(dentry)) {
+ struct inode * inode = dentry->d_inode;
+ error = do_revalidate(inode);
+ if (!error)
+ error = cp_old_stat(inode, statbuf);
+
+ dput(dentry);
+ }
unlock_kernel();
return error;
}
@@ -183,17 +192,21 @@ out:
asmlinkage int sys_newlstat(char * filename, struct stat * statbuf)
{
- struct inode * inode;
+ struct dentry * dentry;
int error;
lock_kernel();
- error = namei(NAM_FOLLOW_TRAILSLASH, filename, &inode);
- if (error)
- goto out;
- if ((error = do_revalidate(inode)) == 0)
- error = cp_new_stat(inode,statbuf);
- iput(inode);
-out:
+ dentry = lnamei(filename);
+
+ error = PTR_ERR(dentry);
+ if (!IS_ERR(dentry)) {
+ struct inode * inode = dentry->d_inode;
+ error = do_revalidate(inode);
+ if (!error)
+ error = cp_new_stat(inode,statbuf);
+
+ dput(dentry);
+ }
unlock_kernel();
return error;
}
@@ -207,17 +220,19 @@ out:
asmlinkage int sys_fstat(unsigned int fd, struct __old_kernel_stat * statbuf)
{
struct file * f;
- struct inode * inode;
- int ret = -EBADF;
+ int err = -EBADF;
lock_kernel();
- if (fd >= NR_OPEN || !(f=current->files->fd[fd]) || !(inode=f->f_inode))
- goto out;
- if ((ret = do_revalidate(inode)) == 0)
- ret = cp_old_stat(inode,statbuf);
-out:
+ if (fd < NR_OPEN && (f = current->files->fd[fd]) != NULL) {
+ struct dentry * dentry = f->f_dentry;
+ struct inode * inode = dentry->d_inode;
+
+ err = do_revalidate(inode);
+ if (!err)
+ err = cp_old_stat(inode,statbuf);
+ }
unlock_kernel();
- return ret;
+ return err;
}
#endif
@@ -225,45 +240,43 @@ out:
asmlinkage int sys_newfstat(unsigned int fd, struct stat * statbuf)
{
struct file * f;
- struct inode * inode;
int err = -EBADF;
lock_kernel();
- if (fd >= NR_OPEN || !(f=current->files->fd[fd]) || !(inode=f->f_inode))
- goto out;
- if ((err = do_revalidate(inode)) == 0)
- err = cp_new_stat(inode,statbuf);
-out:
+ if (fd < NR_OPEN && (f = current->files->fd[fd]) != NULL) {
+ struct dentry * dentry = f->f_dentry;
+ struct inode * inode = dentry->d_inode;
+
+ err = do_revalidate(inode);
+ if (!err)
+ err = cp_new_stat(inode,statbuf);
+ }
unlock_kernel();
return err;
}
asmlinkage int sys_readlink(const char * path, char * buf, int bufsiz)
{
- struct inode * inode;
- int error = -EINVAL;
+ struct dentry * dentry;
+ 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);
- if (error)
- goto out;
- error = -EINVAL;
- if (!inode->i_op || !inode->i_op->readlink ||
- !S_ISLNK(inode->i_mode) || (error = do_revalidate(inode)) < 0) {
- iput(inode);
- goto out;
- }
- if (!IS_RDONLY(inode)) {
- inode->i_atime = CURRENT_TIME;
- inode->i_dirt = 1;
+ return -EINVAL;
+
+ lock_kernel();
+ dentry = lnamei(path);
+
+ error = PTR_ERR(dentry);
+ if (!IS_ERR(dentry)) {
+ struct inode * inode = dentry->d_inode;
+
+ error = -EINVAL;
+ if (inode->i_op && inode->i_op->readlink && !(error = do_revalidate(inode))) {
+ UPDATE_ATIME(inode);
+ error = inode->i_op->readlink(inode,buf,bufsiz);
+ }
+ dput(dentry);
}
- error = inode->i_op->readlink(inode,buf,bufsiz);
-out:
unlock_kernel();
return error;
}
diff --git a/fs/super.c b/fs/super.c
index ec47301aa..f143f9348 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -33,7 +33,6 @@
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/fd.h>
-#include <linux/dalloc.h>
#include <linux/init.h>
#include <asm/system.h>
@@ -102,13 +101,13 @@ struct vfsmount *add_vfsmnt(kdev_t dev, const char *dev_name, const char *dir_na
lptr->mnt_dev = dev;
sema_init(&lptr->mnt_sem, 1);
- if (dev_name && !getname(dev_name, &tmp)) {
+ if (dev_name && !IS_ERR(tmp = getname(dev_name))) {
if ((lptr->mnt_devname =
(char *) kmalloc(strlen(tmp)+1, GFP_KERNEL)) != (char *)NULL)
strcpy(lptr->mnt_devname, tmp);
putname(tmp);
}
- if (dir_name && !getname(dir_name, &tmp)) {
+ if (dir_name && !IS_ERR(tmp = getname(dir_name))) {
if ((lptr->mnt_dirname =
(char *) kmalloc(strlen(tmp)+1, GFP_KERNEL)) != (char *)NULL)
strcpy(lptr->mnt_dirname, tmp);
@@ -198,9 +197,11 @@ static int fs_index(const char * __name)
char * name;
int err, index;
- err = getname(__name, &name);
- if (err)
+ name = getname(__name);
+ err = PTR_ERR(name);
+ if (IS_ERR(name))
return err;
+
index = 0;
for (tmp = file_systems ; tmp ; tmp = tmp->next) {
if (strcmp(tmp->name, name) == 0) {
@@ -454,26 +455,6 @@ struct super_block * get_super(kdev_t dev)
return NULL;
}
-void put_super(kdev_t dev)
-{
- struct super_block * sb;
-
- if (dev == ROOT_DEV) {
- printk("VFS: Root device %s: prepare for armageddon\n",
- kdevname(dev));
- return;
- }
- if (!(sb = get_super(dev)))
- return;
- if (sb->s_covered) {
- printk("VFS: Mounted device %s - tssk, tssk\n",
- kdevname(dev));
- return;
- }
- if (sb->s_op && sb->s_op->put_super)
- sb->s_op->put_super(sb);
-}
-
asmlinkage int sys_ustat(dev_t dev, struct ustat * ubuf)
{
struct super_block *s;
@@ -535,7 +516,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,18 +550,45 @@ 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) {
+ 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;
int retval;
+ sb = get_super(dev);
+ if (!sb)
+ return -ENOENT;
+
+ if (!sb->s_root)
+ return -ENOENT;
+
if (dev==ROOT_DEV && !unmount_root) {
/*
* Special case for "unmounting" root. We just try to remount
* it readonly, and sync() the device.
*/
- if (!(sb=get_super(dev)))
- return -ENOENT;
if (!(sb->s_flags & MS_RDONLY)) {
/*
* Make sure all quotas are turned off on this device we need to mount
@@ -597,35 +604,51 @@ static int do_umount(kdev_t dev,int unmount_root)
}
return 0;
}
- if (!(sb=get_super(dev)) || !(sb->s_covered))
- return -ENOENT;
- if (!sb->s_covered->i_mount)
- printk("VFS: umount(%s): mounted inode has i_mount=NULL\n",
- kdevname(dev));
- while(sb->s_ibasket)
- free_ibasket(sb);
- if(sb->s_mounted->i_dentry)
- d_del(sb->s_mounted->i_dentry, D_NO_CLEAR_INODE);
+
/*
* Before checking if the filesystem is still busy make sure the kernel
* doesn't hold any quotafiles open on that device. If the umount fails
* 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(sb, sb->s_root))
return -EBUSY;
- 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_op && sb->s_op->write_super && sb->s_dirt)
- sb->s_op->write_super(sb);
- put_super(dev);
+
+ /* clean up dcache .. */
+ d_umount(sb->s_root);
+ sb->s_root = NULL;
+
+ if (sb->s_op) {
+ if (sb->s_op->write_super && sb->s_dirt)
+ sb->s_op->write_super(sb);
+ if (sb->s_op->put_super)
+ sb->s_op->put_super(sb);
+ }
remove_vfsmnt(dev);
return 0;
}
+static int umount_dev(kdev_t dev)
+{
+ int retval;
+ struct inode * inode = get_empty_inode();
+
+ inode->i_rdev = dev;
+ if (MAJOR(dev) >= MAX_BLKDEV)
+ return -ENXIO;
+
+ retval = do_umount(dev,0);
+ if (!retval) {
+ fsync_dev(dev);
+ if (dev != ROOT_DEV) {
+ blkdev_release(inode);
+ put_unnamed_dev(dev);
+ }
+ }
+ iput(inode);
+ return retval;
+}
+
/*
* Now umount can handle mount points as well as block devices.
* This is important for filesystems which use unnamed block devices.
@@ -639,55 +662,36 @@ static int do_umount(kdev_t dev,int unmount_root)
asmlinkage int sys_umount(char * name)
{
- struct inode * inode;
- kdev_t dev;
- struct inode * dummy_inode = NULL;
- int retval = -EPERM;
+ struct dentry * dentry;
+ int retval;
- 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;
- }
- if (S_ISBLK(inode->i_mode)) {
- dev = inode->i_rdev;
- retval = -EACCES;
- if (IS_NODEV(inode)) {
- iput(inode);
- goto out;
- }
- } else {
- retval = -EINVAL;
- if (!inode->i_sb || inode != inode->i_sb->s_mounted) {
- iput(inode);
- goto out;
- }
- dev = inode->i_sb->s_dev;
- iput(inode);
- inode = dummy_inode = get_empty_inode();
- inode->i_rdev = dev;
- }
- retval = -ENXIO;
- if (MAJOR(dev) >= MAX_BLKDEV) {
- iput(inode);
- goto out;
- }
- retval = do_umount(dev,0);
- if (!retval) {
- fsync_dev(dev);
- if (dev != ROOT_DEV) {
- blkdev_release (inode);
- put_unnamed_dev(dev);
+ return -EPERM;
+
+ lock_kernel();
+ dentry = namei(name);
+ retval = PTR_ERR(dentry);
+ if (!IS_ERR(dentry)) {
+ struct inode * inode = dentry->d_inode;
+ kdev_t dev = inode->i_rdev;
+
+ retval = 0;
+ if (S_ISBLK(inode->i_mode)) {
+ if (IS_NODEV(inode))
+ retval = -EACCES;
+ } else {
+ struct super_block *sb = inode->i_sb;
+ retval = -EINVAL;
+ if (sb && inode == sb->s_root->d_inode) {
+ dev = sb->s_dev;
+ retval = 0;
+ }
}
+ dput(dentry);
+
+ if (!retval)
+ retval = umount_dev(dev);
}
- iput(inode);
- if (!retval)
- fsync_dev(dev);
-out:
unlock_kernel();
return retval;
}
@@ -715,45 +719,39 @@ 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 = namei(dir_name);
+ 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_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);
@@ -761,25 +759,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_len, 1);
- if(new) {
- struct qstr copy = { old->d_name, old->d_len };
- d_add(new, sb->s_mounted, &copy, 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 */
}
@@ -799,7 +780,7 @@ static int do_remount_sb(struct super_block *sb, int flags, char *data)
/*flags |= MS_RDONLY;*/
/* If we are remounting RDONLY, make sure there are no rw files open */
if ((flags & MS_RDONLY) && !(sb->s_flags & MS_RDONLY))
- if (!fs_may_remount_ro(sb->s_dev))
+ if (!fs_may_remount_ro(sb))
return -EBUSY;
sb->s_flags = (flags & ~MS_RDONLY) | (sb->s_flags & MS_RDONLY);
if (sb->s_op && sb->s_op->remount_fs) {
@@ -817,18 +798,19 @@ static int do_remount_sb(struct super_block *sb, int flags, char *data)
static int do_remount(const char *dir,int flags,char *data)
{
- struct inode *dir_i;
+ struct dentry *dentry;
int retval;
- retval = namei(NAM_FOLLOW_LINK, dir, &dir_i);
- if (retval)
- return retval;
- if (dir_i != dir_i->i_sb->s_mounted) {
- iput(dir_i);
- return -EINVAL;
+ dentry = namei(dir);
+ retval = PTR_ERR(dentry);
+ if (!IS_ERR(dentry)) {
+ struct super_block * sb = dentry->d_inode->i_sb;
+
+ retval = -EINVAL;
+ if (dentry == sb->s_root)
+ retval = do_remount_sb(sb, flags, data);
+ dput(dentry);
}
- retval = do_remount_sb(dir_i->i_sb, flags, data);
- iput(dir_i);
return retval;
}
@@ -879,7 +861,8 @@ asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type,
unsigned long new_flags, void * data)
{
struct file_system_type * fstype;
- struct inode * inode;
+ struct dentry * dentry = NULL;
+ struct inode * inode = NULL;
struct file_operations * fops;
kdev_t dev;
int retval = -EPERM;
@@ -911,58 +894,54 @@ asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type,
goto out;
t = fstype->name;
fops = NULL;
- if ((fstype->fs_flags & FS_REQUIRES_DEV)) {
- retval = namei(NAM_FOLLOW_LINK, dev_name, &inode);
- if (retval)
+ if (fstype->fs_flags & FS_REQUIRES_DEV) {
+ dentry = namei(dev_name);
+ retval = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
goto out;
+
+ inode = dentry->d_inode;
retval = -ENOTBLK;
- if (!S_ISBLK(inode->i_mode)) {
- iput(inode);
- goto out;
- }
+ if (!S_ISBLK(inode->i_mode))
+ goto dput_and_out;
+
retval = -EACCES;
- if (IS_NODEV(inode)) {
- iput(inode);
- goto out;
- }
+ if (IS_NODEV(inode))
+ goto dput_and_out;
+
dev = inode->i_rdev;
retval = -ENXIO;
- if (MAJOR(dev) >= MAX_BLKDEV) {
- iput(inode);
- goto out;
- }
+ if (MAJOR(dev) >= MAX_BLKDEV)
+ goto dput_and_out;
+
fops = get_blkfops(MAJOR(dev));
retval = -ENOTBLK;
- if (!fops) {
- iput(inode);
- goto out;
- }
+ if (!fops)
+ goto dput_and_out;
+
if (fops->open) {
struct file dummy; /* allows read-write or read-only flag */
memset(&dummy, 0, sizeof(dummy));
- dummy.f_inode = inode;
+ dummy.f_dentry = dentry;
dummy.f_mode = (new_flags & MS_RDONLY) ? 1 : 3;
retval = fops->open(inode, &dummy);
- if (retval) {
- iput(inode);
- goto out;
- }
+ if (retval)
+ goto dput_and_out;
}
} else {
retval = -EMFILE;
if (!(dev = get_unnamed_dev()))
goto out;
- inode = NULL;
}
+
page = 0;
if ((new_flags & MS_MGC_MSK) == MS_MGC_VAL) {
flags = new_flags & ~MS_MGC_MSK;
retval = copy_mount_options(data, &page);
if (retval < 0) {
put_unnamed_dev(dev);
- iput(inode);
- goto out;
+ goto dput_and_out;
}
}
retval = do_mount(dev,dev_name,dir_name,t,flags,(void *) page);
@@ -971,7 +950,8 @@ asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type,
fops->release(inode, NULL);
put_unnamed_dev(dev);
}
- iput(inode);
+dput_and_out:
+ dput(dentry);
out:
unlock_kernel();
return retval;
@@ -982,7 +962,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;
@@ -1001,15 +981,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", "/");
@@ -1042,7 +1018,7 @@ __initfunc(static void do_mount_root(void))
memset(&filp, 0, sizeof(filp));
d_inode = get_empty_inode();
d_inode->i_rdev = ROOT_DEV;
- filp.f_inode = d_inode;
+ filp.f_dentry = NULL;
if ( root_mountflags & MS_RDONLY)
filp.f_mode = 1; /* read only */
else
@@ -1066,15 +1042,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" : "");
@@ -1106,8 +1076,7 @@ __initfunc(static int do_change_root(kdev_t new_root_dev,const char *put_old))
{
kdev_t old_root_dev;
struct vfsmount *vfsmnt;
- struct inode *old_root,*old_pwd,*inode;
- unsigned long old_fs;
+ struct dentry *old_root,*old_pwd,*dir_d = NULL;
int error;
old_root = current->fs->root;
@@ -1119,24 +1088,29 @@ __initfunc(static int do_change_root(kdev_t new_root_dev,const char *put_old))
}
ROOT_DEV = new_root_dev;
do_mount_root();
- old_fs = get_fs();
- set_fs(get_ds());
- error = namei(NAM_FOLLOW_LINK, put_old, &inode);
- if (error) inode = NULL;
- set_fs(old_fs);
- if (!error && (atomic_read(&inode->i_count) != 1 || inode->i_mount))
+ dir_d = lookup_dentry(put_old, NULL, 1);
+ if (IS_ERR(dir_d)) {
+ error = PTR_ERR(dir_d);
+ } else if (!dir_d->d_inode) {
+ dput(dir_d);
+ error = -ENOENT;
+ } else {
+ error = 0;
+ }
+ if (!error && dir_d->d_covers != dir_d) {
+ dput(dir_d);
error = -EBUSY;
- if (!error && !S_ISDIR(inode->i_mode))
+ }
+ if (!error && !S_ISDIR(dir_d->d_inode->i_mode)) {
+ dput(dir_d);
error = -ENOTDIR;
- iput(old_root); /* current->fs->root */
- iput(old_pwd); /* current->fs->pwd */
+ }
+ dput(old_root);
+ dput(old_pwd);
if (error) {
int umount_error;
- if (inode) iput(inode);
printk(KERN_NOTICE "Trying to unmount old root ... ");
- old_root->i_mount = old_root;
- /* does this belong into do_mount_root ? */
umount_error = do_umount(old_root_dev,1);
if (umount_error) printk(KERN_ERR "error %d\n",umount_error);
else {
@@ -1145,16 +1119,16 @@ __initfunc(static int do_change_root(kdev_t new_root_dev,const char *put_old))
}
return umount_error ? error : 0;
}
- iput(old_root); /* sb->s_covered */
remove_vfsmnt(old_root_dev);
vfsmnt = add_vfsmnt(old_root_dev,"/dev/root.old",put_old);
if (!vfsmnt) printk(KERN_CRIT "Trouble: add_vfsmnt failed\n");
else {
- vfsmnt->mnt_sb = old_root->i_sb;
- vfsmnt->mnt_sb->s_covered = inode;
+ vfsmnt->mnt_sb = old_root->d_inode->i_sb;
+ d_mount(dir_d,vfsmnt->mnt_sb->s_root);
vfsmnt->mnt_flags = vfsmnt->mnt_sb->s_flags;
}
- inode->i_mount = old_root;
+ d_umount(old_root);
+ d_mount(dir_d,old_root);
return 0;
}
diff --git a/fs/sysv/dir.c b/fs/sysv/dir.c
index 8b942a5b1..3dd0931cf 100644
--- a/fs/sysv/dir.c
+++ b/fs/sysv/dir.c
@@ -57,6 +57,7 @@ struct inode_operations sysv_dir_inode_operations = {
sysv_mknod, /* mknod */
sysv_rename, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
diff --git a/fs/sysv/file.c b/fs/sysv/file.c
index da07ef7a8..1a28e4c2a 100644
--- a/fs/sysv/file.c
+++ b/fs/sysv/file.c
@@ -64,6 +64,7 @@ struct inode_operations sysv_file_inode_operations = {
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
generic_readpage, /* readpage */
NULL, /* writepage */
sysv_bmap, /* bmap */
@@ -194,7 +195,7 @@ long sysv_file_read(struct inode * inode, struct file * filp,
filp->f_reada = 1;
if (!IS_RDONLY(inode)) {
inode->i_atime = CURRENT_TIME;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
}
return read;
}
@@ -255,7 +256,7 @@ static long sysv_file_write(struct inode * inode, struct file * filp,
pos += c;
if (pos > inode->i_size) {
inode->i_size = pos;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
}
written += c;
buf += c;
@@ -265,6 +266,6 @@ static long sysv_file_write(struct inode * inode, struct file * filp,
}
inode->i_mtime = inode->i_ctime = CURRENT_TIME;
filp->f_pos = pos;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
return written;
}
diff --git a/fs/sysv/ialloc.c b/fs/sysv/ialloc.c
index 97bc7284f..fa0b3cf95 100644
--- a/fs/sysv/ialloc.c
+++ b/fs/sysv/ialloc.c
@@ -62,9 +62,8 @@ void sysv_free_inode(struct inode * inode)
printk("sysv_free_inode: inode has no device\n");
return;
}
- if (atomic_read(&inode->i_count) != 1) {
- printk("sysv_free_inode: inode has count=%d\n",
- atomic_read(&inode->i_count));
+ if (inode->i_count != 1) {
+ printk("sysv_free_inode: inode has count=%d\n", inode->i_count);
return;
}
if (inode->i_nlink) {
@@ -150,12 +149,12 @@ struct inode * sysv_new_inode(const struct inode * dir)
mark_buffer_dirty(sb->sv_bh1, 1); /* super-block has been modified */
if (sb->sv_bh1 != sb->sv_bh2) mark_buffer_dirty(sb->sv_bh2, 1);
sb->s_dirt = 1; /* and needs time stamp */
- atomic_set(&inode->i_count, 1);
+ inode->i_count = 1;
inode->i_nlink = 1;
inode->i_dev = sb->s_dev;
inode->i_uid = current->fsuid;
inode->i_gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
inode->i_ino = ino;
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
inode->i_op = NULL;
@@ -166,7 +165,7 @@ struct inode * sysv_new_inode(const struct inode * dir)
inode->i_size = 0; /* ditto */
sysv_write_inode(inode); /* ensure inode not allocated again */
/* FIXME: caller may call this too. */
- inode->i_dirt = 1; /* cleared by sysv_write_inode() */
+ mark_inode_dirty(inode); /* cleared by sysv_write_inode() */
/* That's it. */
(*sb->sv_sb_total_free_inodes)--;
mark_buffer_dirty(sb->sv_bh2, 1); /* super-block has been modified again */
diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c
index f8c6a1b38..b2d7edfbc 100644
--- a/fs/sysv/inode.c
+++ b/fs/sysv/inode.c
@@ -344,6 +344,7 @@ struct super_block *sysv_read_super(struct super_block *sb,void *data,
struct buffer_head *bh;
const char *found;
kdev_t dev = sb->s_dev;
+ struct inode *root_inode;
if (1024 != sizeof (struct xenix_super_block))
panic("Xenix FS: bad super-block size");
@@ -483,9 +484,10 @@ struct super_block *sysv_read_super(struct super_block *sb,void *data,
/* set up enough so that it can read an inode */
sb->s_dev = dev;
sb->s_op = &sysv_sops;
- sb->s_mounted = iget(sb,SYSV_ROOT_INO);
+ root_inode = iget(sb,SYSV_ROOT_INO);
+ sb->s_root = d_alloc_root(root_inode, NULL);
unlock_super(sb);
- if (!sb->s_mounted) {
+ if (!sb->s_root) {
printk("SysV FS: get root inode failed\n");
sysv_put_super(sb);
return NULL;
@@ -534,7 +536,7 @@ void sysv_put_super(struct super_block *sb)
MOD_DEC_USE_COUNT;
}
-void sysv_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
+int sysv_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
{
struct statfs tmp;
@@ -547,7 +549,7 @@ void sysv_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
tmp.f_ffree = sysv_count_free_inodes(sb); /* free file nodes in fs */
tmp.f_namelen = SYSV_NAMELEN;
/* Don't know what value to put in tmp.f_fsid */ /* file system id */
- copy_to_user(buf, &tmp, bufsiz);
+ return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
}
@@ -667,7 +669,7 @@ repeat:
}
*p = tmp;
inode->i_ctime = CURRENT_TIME;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
return result;
}
@@ -900,13 +902,11 @@ static struct buffer_head * sysv_update_inode(struct inode * inode)
printk("Bad inode number on dev %s"
": %d is out of range\n",
kdevname(inode->i_dev), ino);
- inode->i_dirt = 0;
return 0;
}
block = sb->sv_firstinodezone + ((ino-1) >> sb->sv_inodes_per_block_bits);
if (!(bh = sv_bread(sb,inode->i_dev,block))) {
printk("unable to read i-node block\n");
- inode->i_dirt = 0;
return 0;
}
raw_inode = (struct sysv_inode *) bh->b_data + ((ino-1) & sb->sv_inodes_per_block_1);
@@ -937,7 +937,6 @@ static struct buffer_head * sysv_update_inode(struct inode * inode)
else
for (block = 0; block < 10+1+1+1; block++)
write3byte(&raw_inode->i_a.i_addb[3*block],inode->u.sysv_i.i_data[block]);
- inode->i_dirt=0;
mark_buffer_dirty(bh, 1);
return bh;
}
diff --git a/fs/sysv/namei.c b/fs/sysv/namei.c
index d1b67ab5f..b3586b58f 100644
--- a/fs/sysv/namei.c
+++ b/fs/sysv/namei.c
@@ -175,7 +175,7 @@ static int sysv_add_entry(struct inode * dir,
if (pos > dir->i_size) {
de->inode = 0;
dir->i_size = pos;
- dir->i_dirt = 1;
+ mark_inode_dirty(dir);
}
if (de->inode) {
if (namecompare(namelen, SYSV_NAMELEN, name, de->name)) {
@@ -184,7 +184,7 @@ static int sysv_add_entry(struct inode * dir,
}
} else {
dir->i_mtime = dir->i_ctime = CURRENT_TIME;
- dir->i_dirt = 1;
+ mark_inode_dirty(dir);
for (i = 0; i < SYSV_NAMELEN ; i++)
de->name[i] = (i < namelen) ? name[i] : 0;
mark_buffer_dirty(bh, 1);
@@ -219,11 +219,11 @@ int sysv_create(struct inode * dir,const char * name, int len, int mode,
}
inode->i_op = &sysv_file_inode_operations;
inode->i_mode = mode;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
error = sysv_add_entry(dir,name,len, &bh ,&de);
if (error) {
inode->i_nlink--;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
iput(inode);
iput(dir);
return error;
@@ -276,11 +276,11 @@ int sysv_mknod(struct inode * dir, const char * name, int len, int mode, int rde
init_fifo(inode);
if (S_ISBLK(mode) || S_ISCHR(mode))
inode->i_rdev = to_kdev_t(rdev);
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
error = sysv_add_entry(dir, name, len, &bh, &de);
if (error) {
inode->i_nlink--;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
iput(inode);
iput(dir);
return error;
@@ -325,7 +325,7 @@ int sysv_mkdir(struct inode * dir, const char * name, int len, int mode)
if (!dir_block) {
iput(dir);
inode->i_nlink--;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
iput(inode);
return -ENOSPC;
}
@@ -341,7 +341,7 @@ int sysv_mkdir(struct inode * dir, const char * name, int len, int mode)
inode->i_mode = S_IFDIR | (mode & 0777 & ~current->fs->umask);
if (dir->i_mode & S_ISGID)
inode->i_mode |= S_ISGID;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
error = sysv_add_entry(dir, name, len, &bh, &de);
if (error) {
iput(dir);
@@ -352,7 +352,7 @@ int sysv_mkdir(struct inode * dir, const char * name, int len, int mode)
de->inode = inode->i_ino;
mark_buffer_dirty(bh, 1);
dir->i_nlink++;
- dir->i_dirt = 1;
+ mark_inode_dirty(dir);
iput(dir);
iput(inode);
brelse(bh);
@@ -454,7 +454,7 @@ int sysv_rmdir(struct inode * dir, const char * name, int len)
retval = -ENOENT;
goto end_rmdir;
}
- if (atomic_read(&inode->i_count) > 1) {
+ if (inode->i_count > 1) {
retval = -EBUSY;
goto end_rmdir;
}
@@ -463,10 +463,10 @@ int sysv_rmdir(struct inode * dir, const char * name, int len)
de->inode = 0;
mark_buffer_dirty(bh, 1);
inode->i_nlink=0;
- inode->i_dirt=1;
+ mark_inode_dirty(inode);
dir->i_nlink--;
inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
- dir->i_dirt=1;
+ mark_inode_dirty(dir);
retval = 0;
end_rmdir:
iput(dir);
@@ -517,10 +517,10 @@ repeat:
de->inode = 0;
mark_buffer_dirty(bh, 1);
dir->i_ctime = dir->i_mtime = CURRENT_TIME;
- dir->i_dirt = 1;
+ mark_inode_dirty(dir);
inode->i_nlink--;
inode->i_ctime = dir->i_ctime;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
retval = 0;
end_unlink:
brelse(bh);
@@ -550,7 +550,7 @@ int sysv_symlink(struct inode * dir, const char * name, int len, const char * sy
if (!name_block) {
iput(dir);
inode->i_nlink--;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
iput(inode);
return -ENOSPC;
}
@@ -563,11 +563,11 @@ int sysv_symlink(struct inode * dir, const char * name, int len, const char * sy
mark_buffer_dirty(name_block, 1);
brelse(name_block);
inode->i_size = i;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
bh = sysv_find_entry(dir,name,len,&de);
if (bh) {
inode->i_nlink--;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
iput(inode);
brelse(bh);
iput(dir);
@@ -576,7 +576,7 @@ int sysv_symlink(struct inode * dir, const char * name, int len, const char * sy
i = sysv_add_entry(dir, name, len, &bh, &de);
if (i) {
inode->i_nlink--;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
iput(inode);
iput(dir);
return i;
@@ -624,7 +624,7 @@ int sysv_link(struct inode * oldinode, struct inode * dir, const char * name, in
iput(dir);
oldinode->i_nlink++;
oldinode->i_ctime = CURRENT_TIME;
- oldinode->i_dirt = 1;
+ mark_inode_dirty(oldinode);
iput(oldinode);
return 0;
}
@@ -635,7 +635,7 @@ static int subdir(struct inode * new_inode, struct inode * old_inode)
int ino;
int result;
- atomic_inc(&new_inode->i_count);
+ new_inode->i_count++;
result = 0;
for (;;) {
if (new_inode == old_inode) {
@@ -667,8 +667,8 @@ 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_sysv_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_sysv_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;
@@ -687,21 +687,21 @@ try_again:
start_up:
old_inode = new_inode = NULL;
old_bh = new_bh = dir_bh = NULL;
- old_bh = sysv_find_entry(old_dir,old_name,old_len,&old_de);
+ old_bh = sysv_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, old_de->inode, 0); /* don't cross mnt-points */
- if (!old_inode)
- goto end_rename;
+ old_inode = old_dentry->d_inode;/* don't cross mnt-points */
retval = -EPERM;
if ((old_dir->i_mode & S_ISVTX) &&
current->fsuid != old_inode->i_uid &&
current->fsuid != old_dir->i_uid && !fsuser())
goto end_rename;
- new_bh = sysv_find_entry(new_dir,new_name,new_len,&new_de);
+ new_inode = new_dentry->d_inode;
+ new_bh = sysv_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, new_de->inode, 0);
if (!new_inode) {
brelse(new_bh);
new_bh = NULL;
@@ -722,7 +722,7 @@ start_up:
if (!empty_dir(new_inode))
goto end_rename;
retval = -EBUSY;
- if (atomic_read(&new_inode->i_count) > 1)
+ if (new_inode->i_count > 1)
goto end_rename;
}
retval = -EPERM;
@@ -748,7 +748,8 @@ start_up:
goto end_rename;
}
if (!new_bh) {
- retval = sysv_add_entry(new_dir,new_name,new_len,&new_bh,&new_de);
+ retval = sysv_add_entry(new_dir,new_dentry->d_name.name,
+ new_dentry->d_name.len,&new_bh,&new_de);
if (retval)
goto end_rename;
}
@@ -763,13 +764,13 @@ start_up:
old_de->inode = 0;
new_de->inode = old_inode->i_ino;
old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
- old_dir->i_dirt = 1;
+ mark_inode_dirty(old_dir);
new_dir->i_ctime = new_dir->i_mtime = CURRENT_TIME;
- new_dir->i_dirt = 1;
+ mark_inode_dirty(new_dir);
if (new_inode) {
new_inode->i_nlink--;
new_inode->i_ctime = CURRENT_TIME;
- new_inode->i_dirt = 1;
+ mark_inode_dirty(new_inode);
}
mark_buffer_dirty(old_bh, 1);
mark_buffer_dirty(new_bh, 1);
@@ -777,13 +778,13 @@ start_up:
PARENT_INO(dir_bh->b_data) = new_dir->i_ino;
mark_buffer_dirty(dir_bh, 1);
old_dir->i_nlink--;
- old_dir->i_dirt = 1;
+ mark_inode_dirty(old_dir);
if (new_inode) {
new_inode->i_nlink--;
- new_inode->i_dirt = 1;
+ mark_inode_dirty(new_inode);
} else {
new_dir->i_nlink++;
- new_dir->i_dirt = 1;
+ mark_inode_dirty(new_dir);
}
}
retval = 0;
@@ -807,8 +808,8 @@ end_rename:
* the same device that races occur: many renames can happen at once, as long
* as they are on different partitions.
*/
-int sysv_rename(struct inode * old_dir, const char * old_name, int old_len,
- struct inode * new_dir, const char * new_name, int new_len)
+int sysv_rename(struct inode * old_dir, struct dentry * old_dentry,
+ struct inode * new_dir, struct dentry * new_dentry)
{
static struct wait_queue * wait = NULL;
static int lock = 0;
@@ -817,8 +818,8 @@ int sysv_rename(struct inode * old_dir, const char * old_name, int old_len,
while (lock)
sleep_on(&wait);
lock = 1;
- result = do_sysv_rename(old_dir, old_name, old_len,
- new_dir, new_name, new_len);
+ result = do_sysv_rename(old_dir, old_dentry,
+ new_dir, new_dentry);
lock = 0;
wake_up(&wait);
return result;
diff --git a/fs/sysv/symlink.c b/fs/sysv/symlink.c
index 4e8a5e349..d76c3fa66 100644
--- a/fs/sysv/symlink.c
+++ b/fs/sysv/symlink.c
@@ -21,6 +21,7 @@
#include <asm/uaccess.h>
static int sysv_readlink(struct inode *, char *, int);
+static struct dentry *sysv_follow_link(struct inode *, struct dentry *);
/*
* symlinks can't do much...
@@ -37,6 +38,7 @@ struct inode_operations sysv_symlink_inode_operations = {
NULL, /* mknod */
NULL, /* rename */
sysv_readlink, /* readlink */
+ sysv_follow_link, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
@@ -44,6 +46,21 @@ struct inode_operations sysv_symlink_inode_operations = {
NULL /* permission */
};
+static struct dentry *sysv_follow_link(struct inode * inode, struct dentry * base)
+{
+ struct buffer_head * bh;
+
+ bh = sysv_file_bread(inode, 0, 0);
+ if (!bh) {
+ dput(base);
+ return ERR_PTR(-EIO);
+ }
+ UPDATE_ATIME(inode);
+ base = lookup_dentry(bh->b_data, base, 1);
+ brelse(bh);
+ return base;
+}
+
static int sysv_readlink(struct inode * inode, char * buffer, int buflen)
{
struct buffer_head * bh;
@@ -54,7 +71,6 @@ static int sysv_readlink(struct inode * inode, char * buffer, int buflen)
if (buflen > inode->i_sb->sv_block_size_1)
buflen = inode->i_sb->sv_block_size_1;
bh = sysv_file_bread(inode, 0, 0);
- iput(inode);
if (!bh)
return 0;
bh_data = bh->b_data;
diff --git a/fs/sysv/truncate.c b/fs/sysv/truncate.c
index 0eeb10d30..433c39cae 100644
--- a/fs/sysv/truncate.c
+++ b/fs/sysv/truncate.c
@@ -64,7 +64,7 @@ repeat:
continue;
}
*p = 0;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
brelse(bh);
sysv_free_block(sb,block);
}
@@ -257,12 +257,14 @@ done:
static int trunc_all(struct inode * inode)
{
struct super_block * sb;
+ char * res;
sb = inode->i_sb;
+ res = (char *)test_bit(I_DIRTY,&inode->i_state);
return trunc_direct(inode)
- | trunc_indirect(inode,sb->sv_ind0_size,&inode->u.sysv_i.i_data[10],0,&inode->i_dirt)
- | trunc_dindirect(inode,sb->sv_ind1_size,&inode->u.sysv_i.i_data[11],0,&inode->i_dirt)
- | trunc_tindirect(inode,sb->sv_ind2_size,&inode->u.sysv_i.i_data[12],0,&inode->i_dirt);
+ | trunc_indirect(inode,sb->sv_ind0_size,&inode->u.sysv_i.i_data[10],0,res)
+ | trunc_dindirect(inode,sb->sv_ind1_size,&inode->u.sysv_i.i_data[11],0,res)
+ | trunc_tindirect(inode,sb->sv_ind2_size,&inode->u.sysv_i.i_data[12],0,res);
}
@@ -285,5 +287,5 @@ void sysv_truncate(struct inode * inode)
schedule();
}
inode->i_mtime = inode->i_ctime = CURRENT_TIME;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
}
diff --git a/fs/ufs/ufs_file.c b/fs/ufs/ufs_file.c
index 74ae1a470..ef7858c8f 100644
--- a/fs/ufs/ufs_file.c
+++ b/fs/ufs/ufs_file.c
@@ -6,7 +6,7 @@
* Laboratory for Computer Science Research Computing Facility
* Rutgers, The State University of New Jersey
*
- * $Id: ufs_file.c,v 1.8 1997/06/05 01:29:09 davem Exp $
+ * $Id: ufs_file.c,v 1.2 1997/06/17 13:27:28 ralf Exp $
*
*/
@@ -41,6 +41,7 @@ struct inode_operations ufs_file_inode_operations = {
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
generic_readpage, /* readpage */
NULL, /* writepage */
ufs_bmap, /* bmap */
diff --git a/fs/ufs/ufs_inode.c b/fs/ufs/ufs_inode.c
index f0fdd5d5f..d8c8c6896 100644
--- a/fs/ufs/ufs_inode.c
+++ b/fs/ufs/ufs_inode.c
@@ -6,7 +6,7 @@
* Laboratory for Computer Science Research Computing Facility
* Rutgers, The State University of New Jersey
*
- * $Id: ufs_inode.c,v 1.8 1997/06/04 08:28:28 davem Exp $
+ * $Id: ufs_inode.c,v 1.2 1997/06/17 13:27:29 ralf Exp $
*
*/
@@ -19,8 +19,7 @@ void ufs_print_inode(struct inode * inode)
printk("ino %lu mode 0%6.6o lk %d uid %d gid %d"
" sz %lu blks %lu cnt %u\n",
inode->i_ino, inode->i_mode, inode->i_nlink, inode->i_uid,
- inode->i_gid, inode->i_size, inode->i_blocks,
- atomic_read(&inode->i_count));
+ inode->i_gid, inode->i_size, inode->i_blocks, inode->i_count);
printk(" db <0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x"
" 0x%x 0x%x 0x%x 0x%x>\n",
inode->u.ufs_i.i_data[0], inode->u.ufs_i.i_data[1],
diff --git a/fs/ufs/ufs_namei.c b/fs/ufs/ufs_namei.c
index 03ea2dde1..64ea3a866 100644
--- a/fs/ufs/ufs_namei.c
+++ b/fs/ufs/ufs_namei.c
@@ -6,7 +6,7 @@
* Laboratory for Computer Science Research Computing Facility
* Rutgers, The State University of New Jersey
*
- * $Id: ufs_namei.c,v 1.7 1996/06/01 14:56:49 ecd Exp $
+ * $Id: ufs_namei.c,v 1.1.1.1 1997/06/01 03:16:19 ralf Exp $
*
*/
@@ -35,12 +35,14 @@ static int ufs_match (int len, const char * const name, struct ufs_direct * d)
}
/* XXX - this is a mess, especially for endianity */
-int ufs_lookup (struct inode * dir, const char * name, int len,
+int ufs_lookup (struct inode * dir, struct qstr *qname,
struct inode ** result)
{
unsigned long int lfragno, fragno;
struct buffer_head * bh;
struct ufs_direct * d;
+ const char *name = qname->name;
+ int len = qname->len;
if (dir->i_sb->u.ufs_sb.s_flags & UFS_DEBUG)
printk("Passed name: %s\nPassed length: %d\n", name, len);
diff --git a/fs/ufs/ufs_super.c b/fs/ufs/ufs_super.c
index 342722237..f08292a23 100644
--- a/fs/ufs/ufs_super.c
+++ b/fs/ufs/ufs_super.c
@@ -8,7 +8,7 @@
*
* Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
*
- * $Id: ufs_super.c,v 1.24 1997/06/04 08:28:29 davem Exp $
+ * $Id: ufs_super.c,v 1.2 1997/06/17 13:27:29 ralf Exp $
*
*/
@@ -254,7 +254,7 @@ ufs_read_super(struct super_block * sb, void * data, int silent)
sb->u.ufs_sb.s_lmask = ~((ufs_swab32(usb->fs_fmask) - ufs_swab32(usb->fs_bmask))
>> ufs_swab32(usb->fs_fshift));
sb->u.ufs_sb.s_fsfrag = ufs_swab32(usb->fs_frag); /* XXX - rename this later */
- sb->s_mounted = iget(sb, UFS_ROOTINO);
+ sb->s_root = d_alloc_root(iget(sb, UFS_ROOTINO), NULL);
#ifdef DEBUG_UFS_SUPER
printk("ufs_read_super: inopb %u\n", sb->u.ufs_sb.s_inopb);
diff --git a/fs/vfat/namei.c b/fs/vfat/namei.c
index 161bc791e..b3263b42d 100644
--- a/fs/vfat/namei.c
+++ b/fs/vfat/namei.c
@@ -877,7 +877,7 @@ static int vfat_find(struct inode *dir,const char *name,int len,
PRINTK(("vfat_find: create file 4\n"));
dir->i_ctime = dir->i_mtime = dir->i_atime = CURRENT_TIME;
- dir->i_dirt = 1;
+ mark_inode_dirty(dir);
PRINTK(("vfat_find: create file 5\n"));
@@ -1010,7 +1010,7 @@ static int vfat_create_entry(struct inode *dir,const char *name,int len,
return -EIO;
(*result)->i_mtime = (*result)->i_atime = (*result)->i_ctime =
CURRENT_TIME;
- (*result)->i_dirt = 1;
+ mark_inode_dirty(*result);
(*result)->i_version = ++event;
dir->i_version = event;
@@ -1046,7 +1046,7 @@ static int vfat_create_a_dotdir(struct inode *dir,struct inode *parent,
* XXX all times should be set by caller upon successful completion.
*/
dir->i_atime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
- dir->i_dirt = 1;
+ mark_inode_dirty(dir);
memcpy(de->name,name,MSDOS_NAME);
memset(de->unused, 0, sizeof(de->unused));
de->lcase = 0;
@@ -1062,7 +1062,7 @@ static int vfat_create_a_dotdir(struct inode *dir,struct inode *parent,
vfat_read_inode(dot);
if (!dot) return -EIO;
dot->i_mtime = dot->i_atime = CURRENT_TIME;
- dot->i_dirt = 1;
+ mark_inode_dirty(dot);
if (isdot) {
dot->i_size = dir->i_size;
MSDOS_I(dot)->i_start = MSDOS_I(dir)->i_start;
@@ -1125,7 +1125,7 @@ static int vfat_empty(struct inode *dir)
struct buffer_head *bh;
struct msdos_dir_entry *de;
- if (atomic_read(&dir->i_count) > 1)
+ if (dir->i_count > 1)
return -EBUSY;
if (MSDOS_I(dir)->i_start) { /* may be zero in mkdir */
pos = 0;
@@ -1173,7 +1173,8 @@ static int vfat_rmdir_free_ino(struct inode *dir,struct buffer_head *bh,
inode->i_mtime = dir->i_mtime = CURRENT_TIME;
inode->i_atime = dir->i_atime = CURRENT_TIME;
dir->i_nlink--;
- inode->i_dirt = dir->i_dirt = 1;
+ mark_inode_dirty(dir);
+ mark_inode_dirty(inode);
de->name[0] = DELETED_FLAG;
fat_mark_buffer_dirty(sb, bh, 1);
iput(inode);
@@ -1196,7 +1197,8 @@ static int vfat_unlink_free_ino(struct inode *dir,struct buffer_head *bh,
inode->i_atime = dir->i_atime = CURRENT_TIME;
dir->i_version = ++event;
MSDOS_I(inode)->i_busy = 1;
- inode->i_dirt = dir->i_dirt = 1;
+ mark_inode_dirty(dir);
+ mark_inode_dirty(inode);
de->name[0] = DELETED_FLAG;
fat_mark_buffer_dirty(sb, bh, 1);
@@ -1478,7 +1480,7 @@ int vfat_rename(struct inode *old_dir,const char *old_name,int old_len,
MSDOS_I(new_inode)->i_oldlink = old_inode;
fat_cache_inval_inode(old_inode);
PRINTK(("vfat_rename 15: old_slots=%d\n",old_slots));
- old_inode->i_dirt = 1;
+ mark_inode_dirty(old_inode);
old_dir->i_version = ++event;
/* remove the old entry */
@@ -1511,7 +1513,7 @@ int vfat_rename(struct inode *old_dir,const char *old_name,int old_len,
}
dotdot_de->start = MSDOS_I(dotdot_inode)->i_start =
MSDOS_I(new_dir)->i_start;
- dotdot_inode->i_dirt = 1;
+ mark_inode_dirty(dotdot_inode);
fat_mark_buffer_dirty(sb, dotdot_bh, 1);
old_dir->i_nlink--;
new_dir->i_nlink++;
diff --git a/include/asm-alpha/ioctls.h b/include/asm-alpha/ioctls.h
index bb4f71de8..0337b92b3 100644
--- a/include/asm-alpha/ioctls.h
+++ b/include/asm-alpha/ioctls.h
@@ -83,6 +83,8 @@
#define TIOCGETD 0x5424
#define TCSBRKP 0x5425 /* Needed for POSIX tcsendbreak() */
#define TIOCTTYGSTRUCT 0x5426 /* For debugging only */
+#define TIOCSBRK 0x5427 /* BSD compatibility */
+#define TIOCCBRK 0x5428 /* BSD compatibility */
#define TIOCSERCONFIG 0x5453
#define TIOCSERGWILD 0x5454
diff --git a/include/asm-alpha/pgtable.h b/include/asm-alpha/pgtable.h
index 2f40ad673..7d80c32f8 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/ioctls.h b/include/asm-i386/ioctls.h
index 60e0806e5..ae04e1a55 100644
--- a/include/asm-i386/ioctls.h
+++ b/include/asm-i386/ioctls.h
@@ -44,6 +44,9 @@
#define TIOCGETD 0x5424
#define TCSBRKP 0x5425 /* Needed for POSIX tcsendbreak() */
#define TIOCTTYGSTRUCT 0x5426 /* For debugging only */
+#define TIOCSBRK 0x5427 /* BSD compatibility */
+#define TIOCCBRK 0x5428 /* BSD compatibility */
+
#define FIONCLEX 0x5450 /* these numbers need to be adjusted. */
#define FIOCLEX 0x5451
#define FIOASYNC 0x5452
diff --git a/include/asm-i386/pgtable.h b/include/asm-i386/pgtable.h
index 7d973530c..347611f1b 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-i386/unistd.h b/include/asm-i386/unistd.h
index 069c7a1a8..66efafee9 100644
--- a/include/asm-i386/unistd.h
+++ b/include/asm-i386/unistd.h
@@ -175,6 +175,8 @@
#define __NR_query_module 167
#define __NR_poll 168
#define __NR_nfsservctl 169
+#define __NR_setresgid 170
+#define __NR_getresgid 171
/* user-visible error numbers are in the range -1 - -122: see <asm-i386/errno.h> */
diff --git a/include/asm-m68k/hardirq.h b/include/asm-m68k/hardirq.h
index 512e0b054..ab3ca802e 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/ioctls.h b/include/asm-m68k/ioctls.h
index 77ad866b8..b4a95af61 100644
--- a/include/asm-m68k/ioctls.h
+++ b/include/asm-m68k/ioctls.h
@@ -44,6 +44,9 @@
#define TIOCGETD 0x5424
#define TCSBRKP 0x5425 /* Needed for POSIX tcsendbreak() */
#define TIOCTTYGSTRUCT 0x5426 /* For debugging only */
+#define TIOCSBRK 0x5427 /* BSD compatibility */
+#define TIOCCBRK 0x5428 /* BSD compatibility */
+
#define FIONCLEX 0x5450 /* these numbers need to be adjusted. */
#define FIOCLEX 0x5451
#define FIOASYNC 0x5452
diff --git a/include/asm-m68k/pgtable.h b/include/asm-m68k/pgtable.h
index 9463700a3..589dfe956 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/ioctls.h b/include/asm-mips/ioctls.h
index 9ec2c0215..5648eae7f 100644
--- a/include/asm-mips/ioctls.h
+++ b/include/asm-mips/ioctls.h
@@ -96,6 +96,8 @@
#define TCSBRKP 0x5486 /* Needed for POSIX tcsendbreak() */
#define TIOCTTYGSTRUCT 0x5487 /* For debugging only */
+#define TIOCSBRK 0x5427 /* BSD compatibility */
+#define TIOCCBRK 0x5428 /* BSD compatibility */
#define TIOCSERCONFIG 0x5488
#define TIOCSERGWILD 0x5489
diff --git a/include/asm-mips/namei.h b/include/asm-mips/namei.h
index 0ff96ac00..b9c0aea15 100644
--- a/include/asm-mips/namei.h
+++ b/include/asm-mips/namei.h
@@ -13,6 +13,8 @@
/* Only one at this time. */
#define IRIX32_EMUL "usr/gnemul/irix/"
+#if 0 /* XXX FIXME */
+
extern int __namei(int, const char *, struct inode *, char *, struct inode **,
struct inode **, struct qstr *, struct dentry **, int *);
@@ -44,6 +46,8 @@ __prefix_namei(int retrieve_mode, const char * name, struct inode * base,
return 0;
}
+#endif /* XXX FIXME */
+
#else /* !defined(CONFIG_BINFMT_IRIX) */
#define __prefix_namei(retrieve_mode, name, base, buf, res_dir, res_inode, \
diff --git a/include/asm-mips/pgtable.h b/include/asm-mips/pgtable.h
index ebeb6e67c..4f3876cdb 100644
--- a/include/asm-mips/pgtable.h
+++ b/include/asm-mips/pgtable.h
@@ -491,6 +491,9 @@ extern void (*update_mmu_cache)(struct vm_area_struct *vma,
#define SWP_OFFSET(entry) ((entry) >> 15)
#define SWP_ENTRY(type,offset) (((type) << 8) | ((offset) << 15))
+#define module_map vmalloc
+#define module_unmap vfree
+
/* TLB operations. */
extern inline void tlb_probe(void)
{
diff --git a/include/asm-mips/system.h b/include/asm-mips/system.h
index daf477459..2c752a0c0 100644
--- a/include/asm-mips/system.h
+++ b/include/asm-mips/system.h
@@ -108,13 +108,13 @@ __restore_flags(int flags)
#define save_and_cli(x) __save_and_cli(x)
#define restore_flags(x) __restore_flags(x)
-#define sync_mem() \
-__asm__ __volatile__( \
- ".set\tnoreorder\n\t" \
- "sync\n\t" \
- ".set\treorder" \
- : /* no output */ \
- : /* no input */ \
+#define mb() \
+__asm__ __volatile__( \
+ "# prevent instructions being moved around\n\t" \
+ ".set\tnoreorder\n\t" \
+ ".set\treorder" \
+ : /* no output */ \
+ : /* no input */ \
: "memory")
#if !defined (__LANGUAGE_ASSEMBLY__)
diff --git a/include/asm-mips/unistd.h b/include/asm-mips/unistd.h
index 4cf30a110..4517106ca 100644
--- a/include/asm-mips/unistd.h
+++ b/include/asm-mips/unistd.h
@@ -1177,11 +1177,13 @@
#define __NR_query_module (__NR_Linux + 187)
#define __NR_poll (__NR_Linux + 188)
#define __NR_nfsservctl (__NR_Linux + 189)
+#define __NR_setresgid (__NR_Linux + 190)
+#define __NR_getresgid (__NR_Linux + 191)
/*
* Offset of the last Linux flavoured syscall
*/
-#define __NR_Linux_syscalls 189
+#define __NR_Linux_syscalls 191
#ifndef __LANGUAGE_ASSEMBLY__
diff --git a/include/asm-ppc/ioctls.h b/include/asm-ppc/ioctls.h
index 52a3d45bd..f56e53db7 100644
--- a/include/asm-ppc/ioctls.h
+++ b/include/asm-ppc/ioctls.h
@@ -83,6 +83,8 @@
#define TIOCGETD 0x5424
#define TCSBRKP 0x5425 /* Needed for POSIX tcsendbreak() */
#define TIOCTTYGSTRUCT 0x5426 /* For debugging only */
+#define TIOCSBRK 0x5427 /* BSD compatibility */
+#define TIOCCBRK 0x5428 /* BSD compatibility */
#define TIOCSERCONFIG 0x5453
#define TIOCSERGWILD 0x5454
diff --git a/include/asm-ppc/pgtable.h b/include/asm-ppc/pgtable.h
index 243a0b115..e9c400345 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 f81ab33b9..59fcd4337 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 80eff02ea..ccc5e7fce 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 e5e5a18c8..5f2749015 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/namei.h b/include/asm-sparc/namei.h
index e74a76bce..8ce53ae35 100644
--- a/include/asm-sparc/namei.h
+++ b/include/asm-sparc/namei.h
@@ -1,4 +1,4 @@
-/* $Id: namei.h,v 1.5 1997/06/07 08:32:54 ecd Exp $
+/* $Id: namei.h,v 1.6 1997/07/17 02:24:25 davem Exp $
* linux/include/asm-sparc/namei.h
*
* Routines to handle famous /usr/gnemul/s*.
@@ -11,6 +11,8 @@
#define SPARC_BSD_EMUL "usr/gnemul/sunos/"
#define SPARC_SOL_EMUL "usr/gnemul/solaris/"
+#if 0 /* XXX FIXME */
+
extern int __namei(int, const char *, struct inode *, char *, struct inode **,
struct inode **, struct qstr *, struct dentry **, int *);
@@ -44,4 +46,6 @@ __prefix_namei(int retrieve_mode, const char * name, struct inode * base,
return 0;
}
+#endif /* XXX FIXME */
+
#endif /* __SPARC_NAMEI_H */
diff --git a/include/asm-sparc/oplib.h b/include/asm-sparc/oplib.h
index 40c6de10b..bb404745f 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 8b84e1dce..de8ce5687 100644
--- a/include/asm-sparc/pgtable.h
+++ b/include/asm-sparc/pgtable.h
@@ -1,4 +1,4 @@
-/* $Id: pgtable.h,v 1.60 1997/04/14 17:05:16 jj Exp $ */
+/* $Id: pgtable.h,v 1.62 1997/06/27 14:55:00 jj Exp $ */
#ifndef _SPARC_PGTABLE_H
#define _SPARC_PGTABLE_H
@@ -368,7 +368,7 @@ extern __inline__ void add_to_ctx_list(struct ctx_list *head, struct ctx_list *e
#define add_to_free_ctxlist(entry) add_to_ctx_list(&ctx_free, entry)
#define add_to_used_ctxlist(entry) add_to_ctx_list(&ctx_used, entry)
-extern __inline__ unsigned int
+extern __inline__ unsigned long
__get_phys (unsigned long addr)
{
switch (sparc_cpu_model){
@@ -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 000000000..b3cdc7d78
--- /dev/null
+++ b/include/asm-sparc/turbosparc.h
@@ -0,0 +1,114 @@
+/* $Id: turbosparc.h,v 1.1 1997/07/18 06:29:12 ralf 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 <asm/asi.h>
+#include <asm/pgtsrmmu.h>
+
+/* 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 70e6c37a4..18cf7c541 100644
--- a/include/asm-sparc64/asm_offsets.h
+++ b/include/asm-sparc64/asm_offsets.h
@@ -104,14 +104,8 @@
#define ASIZ_task_it_virt_incr 0x00000008
#define AOFF_task_real_timer 0x000001f8
#define ASIZ_task_real_timer 0x00000028
-#define AOFF_task_utime 0x00000220
-#define ASIZ_task_utime 0x00000008
-#define AOFF_task_stime 0x00000228
-#define ASIZ_task_stime 0x00000008
-#define AOFF_task_cutime 0x00000230
-#define ASIZ_task_cutime 0x00000008
-#define AOFF_task_cstime 0x00000238
-#define ASIZ_task_cstime 0x00000008
+#define AOFF_task_times 0x00000220
+#define ASIZ_task_times 0x00000020
#define AOFF_task_start_time 0x00000240
#define ASIZ_task_start_time 0x00000008
#define AOFF_task_min_flt 0x00000248
@@ -151,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
@@ -216,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 0x00000004
-#define AOFF_thread_new_signal 0x000005a4
-#define ASIZ_thread_new_signal 0x00000004
-#define AOFF_thread_kregs 0x000005a8
-#define ASIZ_thread_kregs 0x00000008
-#define AOFF_thread_core_exec 0x000005b0
+#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 ec496fa17..12baf0222 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 5060d88ae..f0d11e6ef 100644
--- a/include/asm-sparc64/bitops.h
+++ b/include/asm-sparc64/bitops.h
@@ -1,4 +1,4 @@
-/* $Id: bitops.h,v 1.16 1997/05/28 13:48:56 jj Exp $
+/* $Id: bitops.h,v 1.19 1997/07/08 10:17:37 davem Exp $
* bitops.h: Bit string operations on the V9.
*
* Copyright 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -23,21 +23,21 @@ extern __inline__ unsigned long test_and_set_bit(unsigned long nr, void *addr)
{
unsigned long oldbit;
unsigned long temp0, temp1;
- unsigned int * m = ((unsigned int *) addr) + (nr >> 5);
+ unsigned long * m = ((unsigned long *) addr) + (nr >> 6);
__asm__ __volatile__("
- lduw [%4], %0
+ ldx [%4], %0
1:
andcc %0, %3, %2
- bne,pn %%icc, 2f
+ bne,pn %%xcc, 2f
xor %0, %3, %1
- cas [%4], %0, %1
+ casx [%4], %0, %1
cmp %0, %1
- bne,a,pn %%icc, 1b
- lduw [%4], %0
+ bne,a,pn %%xcc, 1b
+ ldx [%4], %0
2:
" : "=&r" (temp0), "=&r" (temp1), "=&r" (oldbit)
- : "HIr" (1UL << (nr & 31)), "r" (m)
+ : "HIr" (1UL << (nr & 63)), "r" (m)
: "cc");
return oldbit != 0;
}
@@ -51,21 +51,21 @@ extern __inline__ unsigned long test_and_clear_bit(unsigned long nr, void *addr)
{
unsigned long oldbit;
unsigned long temp0, temp1;
- unsigned int * m = ((unsigned int *) addr) + (nr >> 5);
+ unsigned long * m = ((unsigned long *) addr) + (nr >> 6);
__asm__ __volatile__("
- lduw [%4], %0
+ ldx [%4], %0
1:
andcc %0, %3, %2
- be,pn %%icc, 2f
+ be,pn %%xcc, 2f
xor %0, %3, %1
- cas [%4], %0, %1
+ casx [%4], %0, %1
cmp %0, %1
- bne,a,pn %%icc, 1b
- lduw [%4], %0
+ bne,a,pn %%xcc, 1b
+ ldx [%4], %0
2:
" : "=&r" (temp0), "=&r" (temp1), "=&r" (oldbit)
- : "HIr" (1UL << (nr & 31)), "r" (m)
+ : "HIr" (1UL << (nr & 63)), "r" (m)
: "cc");
return oldbit != 0;
}
@@ -79,19 +79,19 @@ extern __inline__ unsigned long test_and_change_bit(unsigned long nr, void *addr
{
unsigned long oldbit;
unsigned long temp0, temp1;
- unsigned int * m = ((unsigned int *) addr) + (nr >> 5);
+ unsigned long * m = ((unsigned long *) addr) + (nr >> 6);
__asm__ __volatile__("
- lduw [%4], %0
+ ldx [%4], %0
1:
and %0, %3, %2
xor %0, %3, %1
- cas [%4], %0, %1
+ casx [%4], %0, %1
cmp %0, %1
- bne,a,pn %%icc, 1b
- lduw [%4], %0
+ bne,a,pn %%xcc, 1b
+ ldx [%4], %0
" : "=&r" (temp0), "=&r" (temp1), "=&r" (oldbit)
- : "HIr" (1UL << (nr & 31)), "r" (m)
+ : "HIr" (1UL << (nr & 63)), "r" (m)
: "cc");
return oldbit != 0;
}
@@ -103,7 +103,7 @@ extern __inline__ void change_bit(unsigned long nr, void *addr)
extern __inline__ unsigned long test_bit(int nr, __const__ void *addr)
{
- return 1UL & (((__const__ int *) addr)[nr >> 5] >> (nr & 31));
+ return 1UL & (((__const__ long *) addr)[nr >> 6] >> (nr & 63));
}
/* The easy/cheese version for now. */
@@ -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;
@@ -260,22 +258,12 @@ extern __inline__ int test_le_bit(int nr, __const__ void * addr)
#define find_first_zero_le_bit(addr, size) \
find_next_zero_le_bit((addr), (size), 0)
-extern __inline__ unsigned long __swab64(unsigned long value)
-{
- return (((value>>56) & 0x00000000000000ff) |
- ((value>>40) & 0x000000000000ff00) |
- ((value>>24) & 0x0000000000ff0000) |
- ((value>>8) & 0x00000000ff000000) |
- ((value<<8) & 0x000000ff00000000) |
- ((value<<24) & 0x0000ff0000000000) |
- ((value<<40) & 0x00ff000000000000) |
- ((value<<56) & 0xff00000000000000));
-}
-
extern __inline__ unsigned long __swab64p(unsigned long *addr)
{
unsigned long ret;
- __asm__ __volatile__ ("ldxa [%1] %2, %0" : "=r" (ret) : "r" (addr), "i" (ASI_PL));
+ __asm__ __volatile__ ("ldxa [%1] %2, %0"
+ : "=r" (ret)
+ : "r" (addr), "i" (ASI_PL));
return ret;
}
@@ -299,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/byteorder.h b/include/asm-sparc64/byteorder.h
index 2325ef29c..dce2db246 100644
--- a/include/asm-sparc64/byteorder.h
+++ b/include/asm-sparc64/byteorder.h
@@ -1,4 +1,4 @@
-/* $Id: byteorder.h,v 1.5 1997/05/28 11:35:41 jj Exp $ */
+/* $Id: byteorder.h,v 1.6 1997/06/14 17:35:07 davem Exp $ */
#ifndef _SPARC64_BYTEORDER_H
#define _SPARC64_BYTEORDER_H
@@ -56,21 +56,27 @@ extern __inline__ __u64 cpu_to_le64(__u64 value)
extern __inline__ __u16 cpu_to_le16p(__u16 *addr)
{
__u16 ret;
- __asm__ __volatile__ ("lduha [%1] %2, %0" : "=r" (ret) : "r" (addr), "i" (ASI_PL));
+ __asm__ __volatile__ ("lduha [%1] %2, %0"
+ : "=r" (ret)
+ : "r" (addr), "i" (ASI_PL));
return ret;
}
extern __inline__ __u32 cpu_to_le32p(__u32 *addr)
{
__u32 ret;
- __asm__ __volatile__ ("lduwa [%1] %2, %0" : "=r" (ret) : "r" (addr), "i" (ASI_PL));
+ __asm__ __volatile__ ("lduwa [%1] %2, %0"
+ : "=r" (ret)
+ : "r" (addr), "i" (ASI_PL));
return ret;
}
extern __inline__ __u64 cpu_to_le64p(__u64 *addr)
{
__u64 ret;
- __asm__ __volatile__ ("ldxa [%1] %2, %0" : "=r" (ret) : "r" (addr), "i" (ASI_PL));
+ __asm__ __volatile__ ("ldxa [%1] %2, %0"
+ : "=r" (ret)
+ : "r" (addr), "i" (ASI_PL));
return ret;
}
extern __inline__ __u16 cpu_to_be16p(__u16 *addr) { return *addr; }
diff --git a/include/asm-sparc64/checksum.h b/include/asm-sparc64/checksum.h
index d04abac7e..b1ff474c3 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 7923b5014..f70d99b68 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/elf.h b/include/asm-sparc64/elf.h
index 9a43b6c3f..1cef89ff1 100644
--- a/include/asm-sparc64/elf.h
+++ b/include/asm-sparc64/elf.h
@@ -1,4 +1,4 @@
-/* $Id: elf.h,v 1.6 1997/05/17 11:51:27 davem Exp $ */
+/* $Id: elf.h,v 1.7 1997/06/14 21:28:07 davem Exp $ */
#ifndef __ASM_SPARC64_ELF_H
#define __ASM_SPARC64_ELF_H
@@ -32,7 +32,9 @@ typedef unsigned long elf_fpregset_t;
/*
* This is used to ensure we don't load something for the wrong architecture.
*/
+#ifndef elf_check_arch
#define elf_check_arch(x) ((x) == ELF_ARCH) /* Might be EM_SPARC64 or EM_SPARC */
+#endif
#define USE_ELF_CORE_DUMP
#define ELF_EXEC_PAGESIZE 8192
diff --git a/include/asm-sparc64/fbio.h b/include/asm-sparc64/fbio.h
index 3d8713468..6d2f1e730 100644
--- a/include/asm-sparc64/fbio.h
+++ b/include/asm-sparc64/fbio.h
@@ -48,10 +48,6 @@ struct fbtype {
};
#define FBIOGTYPE _IOR('F', 0, struct fbtype)
-/* Used by FBIOPUTCMAP
- *
- * XXX 32-bit binary compatability item... -DaveM
- */
struct fbcmap {
int index; /* first element (0 origin) */
int count;
@@ -104,7 +100,6 @@ struct fbcurpos {
#define FB_CUR_SETSHAPE 0x10 /* set shape */
#define FB_CUR_SETALL 0x1F /* all of the above */
-/* XXX 32-bit binary compatability item... -DaveM */
struct fbcursor {
short set; /* what to set, choose from the list above */
short enable; /* cursor on/off */
@@ -143,7 +138,6 @@ struct fb_wid_item {
__u32 wi_attrs;
__u32 wi_values[32];
};
-/* XXX 32-bit binary compatability item... -DaveM */
struct fb_wid_list {
__u32 wl_flags;
__u32 wl_count;
@@ -155,6 +149,21 @@ struct fb_wid_list {
#define FBIO_WID_PUT _IOW('F', 32, struct fb_wid_list)
#define FBIO_WID_GET _IOWR('F', 33, struct fb_wid_list)
+/* Creator ioctls */
+#define FFB_IOCTL ('F'<<8)
+#define FFB_SYS_INFO (FFB_IOCTL|80)
+#define FFB_CLUTREAD (FFB_IOCTL|81)
+#define FFB_CLUTPOST (FFB_IOCTL|82)
+#define FFB_SETDIAGMODE (FFB_IOCTL|83)
+#define FFB_GETMONITORID (FFB_IOCTL|84)
+#define FFB_GETVIDEOMODE (FFB_IOCTL|85)
+#define FFB_SETVIDEOMODE (FFB_IOCTL|86)
+#define FFB_SETSERVER (FFB_IOCTL|87)
+#define FFB_SETOVCTL (FFB_IOCTL|88)
+#define FFB_GETOVCTL (FFB_IOCTL|89)
+#define FFB_GETSAXNUM (FFB_IOCTL|90)
+#define FFB_FBDEBUG (FFB_IOCTL|91)
+
/* Cg14 ioctls */
#define MDI_IOCTL ('M'<<8)
#define MDI_RESET (MDI_IOCTL|1)
@@ -179,16 +188,15 @@ struct mdi_cfginfo {
*/
#define MDI_CLEAR_XLUT (MDI_IOCTL|9)
-/* leo ioctls */
-struct leo_clut_alloc {
+/* leo & ffb ioctls */
+struct fb_clut_alloc {
__u32 clutid; /* Set on return */
__u32 flag;
__u32 index;
};
-/* XXX 32-bit binary compatability item... -DaveM */
-struct leo_clut {
-#define LEO_CLUT_WAIT 0x00000001 /* Not yet implemented */
+struct fb_clut {
+#define FB_CLUT_WAIT 0x00000001 /* Not yet implemented */
__u32 flag;
__u32 clutid;
__u32 offset;
@@ -197,10 +205,21 @@ struct leo_clut {
char * green;
char * blue;
};
-#define LEO_CLUTALLOC _IOWR('L', 53, struct leo_clut_alloc)
-#define LEO_CLUTFREE _IOW('L', 54, struct leo_clut_alloc)
-#define LEO_CLUTREAD _IOW('L', 55, struct leo_clut)
-#define LEO_CLUTPOST _IOW('L', 56, struct leo_clut)
+
+struct fb_clut32 {
+ __u32 flag;
+ __u32 clutid;
+ __u32 offset;
+ __u32 count;
+ __u32 red;
+ __u32 green;
+ __u32 blue;
+};
+
+#define LEO_CLUTALLOC _IOWR('L', 53, struct fb_clut_alloc)
+#define LEO_CLUTFREE _IOW('L', 54, struct fb_clut_alloc)
+#define LEO_CLUTREAD _IOW('L', 55, struct fb_clut)
+#define LEO_CLUTPOST _IOW('L', 56, struct fb_clut)
#define LEO_SETGAMMA _IOW('L', 68, int) /* Not yet implemented */
#define LEO_GETGAMMA _IOR('L', 69, int) /* Not yet implemented */
diff --git a/include/asm-sparc64/floppy.h b/include/asm-sparc64/floppy.h
index c7aa7cc81..bbef85483 100644
--- a/include/asm-sparc64/floppy.h
+++ b/include/asm-sparc64/floppy.h
@@ -1,4 +1,4 @@
-/* $Id: floppy.h,v 1.2 1997/03/14 21:05:25 jj Exp $
+/* $Id: floppy.h,v 1.3 1997/07/11 03:03:22 davem Exp $
* asm-sparc64/floppy.h: Sparc specific parts of the Floppy driver.
*
* Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -315,7 +315,7 @@ static int sun_floppy_init(void)
/* printk("DOR @0x%p\n", &sun_fdc->dor_82077); */ /* P3 */
/* Success... */
- return (int) sun_fdc;
+ return (int) ((unsigned long)sun_fdc);
}
static int sparc_eject(void)
diff --git a/include/asm-sparc64/fpumacro.h b/include/asm-sparc64/fpumacro.h
index f6323254d..dab134472 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/fs_mount.h b/include/asm-sparc64/fs_mount.h
deleted file mode 100644
index 3ad7ad698..000000000
--- a/include/asm-sparc64/fs_mount.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/* $Id: fs_mount.h,v 1.2 1997/04/18 14:34:46 jj Exp $
- * fs_mount.h: Definitions for mount structure conversions.
- *
- * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
- */
-
-#ifndef __ASM_FS_MOUNT_H
-#define __ASM_FS_MOUNT_H
-
-#if defined(CONFIG_SPARC32_COMPAT) || defined(CONFIG_SPARC32_COMPAT_MODULE)
-
-#include <linux/sched.h>
-
-/* We need this to convert 32bit mount structures to 64bit */
-
-extern void *do_ncp_super_data_conv(void *raw_data);
-extern void *do_smb_super_data_conv(void *raw_data);
-
-extern __inline__ void *ncp_super_data_conv(void *raw_data)
-{
- if (current->tss.flags & SPARC_FLAG_32BIT)
- return do_ncp_super_data_conv(raw_data);
- else
- return raw_data;
-}
-
-extern __inline__ void *smb_super_data_conv(void *raw_data)
-{
- if (current->tss.flags & SPARC_FLAG_32BIT)
- return do_smb_super_data_conv(raw_data);
- else
- return raw_data;
-}
-
-#else /* CONFIG_SPARC32_COMPAT* */
-
-#define ncp_super_data_conv(__x) __x
-#define smb_super_data_conv(__x) __x
-
-#endif /* CONFIG_SPARC32_COMPAT* */
-
-#define nfs_super_data_conv(__x) __x
-
-#endif /* __ASM_FS_MOUNT_H */
diff --git a/include/asm-sparc64/hardirq.h b/include/asm-sparc64/hardirq.h
index 4680a4095..03ee543b1 100644
--- a/include/asm-sparc64/hardirq.h
+++ b/include/asm-sparc64/hardirq.h
@@ -13,8 +13,8 @@ extern unsigned int local_irq_count[NR_CPUS];
#ifndef __SMP__
-#define hardirq_trylock(cpu) (++local_irq_count[cpu], (cpu)==0)
-#define hardirq_endlock(cpu) (--local_irq_count[cpu])
+#define hardirq_trylock(cpu) (local_irq_count[cpu] == 0)
+#define hardirq_endlock(cpu) do { } while(0)
#define hardirq_enter(cpu) (local_irq_count[cpu]++)
#define hardirq_exit(cpu) (local_irq_count[cpu]--)
diff --git a/include/asm-sparc64/head.h b/include/asm-sparc64/head.h
index 62fe9a08f..e3ff51686 100644
--- a/include/asm-sparc64/head.h
+++ b/include/asm-sparc64/head.h
@@ -1,10 +1,10 @@
-/* $Id: head.h,v 1.22 1997/06/02 06:33:40 davem Exp $ */
+/* $Id: head.h,v 1.27 1997/07/13 17:30:43 davem Exp $ */
#ifndef _SPARC64_HEAD_H
#define _SPARC64_HEAD_H
#include <asm/pstate.h>
-#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) \
@@ -126,7 +115,7 @@
/* On UP this is ok, and worth the effort, for SMP we need
* a different mechanism and thus cannot do it all in trap table. -DaveM
*/
-#if 0 /* ndef __SMP__ */
+#ifndef __SMP__
#define TRAP_IVEC \
ldxa [%g2] ASI_UDB_INTR_R, %g3; \
and %g3, 0x7ff, %g3; \
@@ -207,16 +196,23 @@
#define SPILL_2_GENERIC(xxx) \
wr %g0, xxx, %asi; \
srl %sp, 0, %sp; \
- stda %l0, [%sp + 0x00] %asi; \
- stda %l2, [%sp + 0x08] %asi; \
- stda %l4, [%sp + 0x10] %asi; \
- stda %l6, [%sp + 0x18] %asi; \
- stda %i0, [%sp + 0x20] %asi; \
- stda %i2, [%sp + 0x28] %asi; \
- stda %i4, [%sp + 0x30] %asi; \
- stda %i6, [%sp + 0x38] %asi; \
+ stwa %l0, [%sp + 0x00] %asi; \
+ stwa %l1, [%sp + 0x04] %asi; \
+ stwa %l2, [%sp + 0x08] %asi; \
+ stwa %l3, [%sp + 0x0c] %asi; \
+ stwa %l4, [%sp + 0x10] %asi; \
+ stwa %l5, [%sp + 0x14] %asi; \
+ stwa %l6, [%sp + 0x18] %asi; \
+ stwa %l7, [%sp + 0x1c] %asi; \
+ stwa %i0, [%sp + 0x20] %asi; \
+ stwa %i1, [%sp + 0x24] %asi; \
+ stwa %i2, [%sp + 0x28] %asi; \
+ stwa %i3, [%sp + 0x2c] %asi; \
+ stwa %i4, [%sp + 0x30] %asi; \
+ stwa %i5, [%sp + 0x34] %asi; \
+ stwa %i6, [%sp + 0x38] %asi; \
+ stwa %i7, [%sp + 0x3c] %asi; \
saved; retry; nop; nop; nop; nop; \
- nop; nop; nop; nop; nop; nop; nop; nop; \
nop; nop; nop; nop; nop; nop; \
b,a,pt %xcc, spill_fixup_mna; \
b,a,pt %xcc, spill_fixup;
@@ -287,16 +283,23 @@
#define FILL_2_GENERIC(xxx) \
wr %g0, xxx, %asi; \
srl %sp, 0, %sp; \
- ldda [%sp + 0x00] %asi, %l0; \
- ldda [%sp + 0x08] %asi, %l2; \
- ldda [%sp + 0x10] %asi, %l4; \
- ldda [%sp + 0x18] %asi, %l6; \
- ldda [%sp + 0x20] %asi, %i0; \
- ldda [%sp + 0x28] %asi, %i2; \
- ldda [%sp + 0x30] %asi, %i4; \
- ldda [%sp + 0x38] %asi, %i6; \
+ lduwa [%sp + 0x00] %asi, %l0; \
+ lduwa [%sp + 0x04] %asi, %l1; \
+ lduwa [%sp + 0x08] %asi, %l2; \
+ lduwa [%sp + 0x0c] %asi, %l3; \
+ lduwa [%sp + 0x10] %asi, %l4; \
+ lduwa [%sp + 0x14] %asi, %l5; \
+ lduwa [%sp + 0x18] %asi, %l6; \
+ lduwa [%sp + 0x1c] %asi, %l7; \
+ lduwa [%sp + 0x20] %asi, %i0; \
+ lduwa [%sp + 0x24] %asi, %i1; \
+ lduwa [%sp + 0x28] %asi, %i2; \
+ lduwa [%sp + 0x2c] %asi, %i3; \
+ lduwa [%sp + 0x30] %asi, %i4; \
+ lduwa [%sp + 0x34] %asi, %i5; \
+ lduwa [%sp + 0x38] %asi, %i6; \
+ lduwa [%sp + 0x3c] %asi, %i7; \
restored; retry; nop; nop; nop; nop; \
- nop; nop; nop; nop; nop; nop; nop; nop; \
nop; nop; nop; nop; nop; nop; \
b,a,pt %xcc, fill_fixup_mna; \
b,a,pt %xcc, fill_fixup;
diff --git a/include/asm-sparc64/ioctls.h b/include/asm-sparc64/ioctls.h
index 0432cb46f..1d6c1cace 100644
--- a/include/asm-sparc64/ioctls.h
+++ b/include/asm-sparc64/ioctls.h
@@ -1,14 +1,9 @@
-/* $Id: ioctls.h,v 1.2 1997/04/04 00:50:18 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
#include <asm/ioctl.h>
-/* XXX 32-bit binary compatability issues, I am sure that
- * XXX only IOCTL's which reference structures will be of
- * XXX concern and these are easily fabricated using wrappers.
- */
-
/* Big T */
#define TCGETA _IOR('T', 1, struct termio)
#define TCSETA _IOW('T', 2, struct termio)
@@ -24,7 +19,7 @@
/* Note that all the ioctls that are not available in Linux have a
* double underscore on the front to: a) avoid some programs to
- * thing we support some ioctls under Linux (autoconfiguration stuff)
+ * think we support some ioctls under Linux (autoconfiguration stuff)
*/
/* Little t */
#define TIOCGETD _IOR('t', 0, int)
@@ -69,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 9a5b10458..7e7aa0433 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.10 1997/05/23 09:35:55 jj Exp $ */
+/* $Id: mmu_context.h,v 1.17 1997/07/13 19:13:39 davem Exp $ */
#ifndef __SPARC64_MMU_CONTEXT_H
#define __SPARC64_MMU_CONTEXT_H
@@ -11,11 +11,6 @@
#ifndef __ASSEMBLY__
-/* Initialize the context related info for a new mm_struct
- * instance.
- */
-#define init_new_context(mm) ((mm)->context = NO_CONTEXT)
-
#define destroy_context(mm) do { } while(0)
extern unsigned long tlb_context_cache;
@@ -24,61 +19,41 @@ 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");
- flushi(PAGE_OFFSET);
- restore_flags(flags);
+extern void get_new_mmu_context(struct mm_struct *mm, unsigned long ctx);
- ctx = (ctx & CTX_VERSION_MASK) + CTX_FIRST_VERSION;
- if(!ctx)
- ctx = CTX_FIRST_VERSION;
- }
- tlb_context_cache = ctx + 1;
- mm->context = ctx;
-}
+/* Initialize the context related info for a new mm_struct
+ * instance.
+ */
+#define init_new_context(mm) get_new_mmu_context((mm), tlb_context_cache)
extern __inline__ void get_mmu_context(struct task_struct *tsk)
{
+ register unsigned long paddr asm("o5");
struct mm_struct *mm = tsk->mm;
- if(mm &&
- !(tsk->tss.flags & SPARC_FLAG_KTHREAD) &&
+ flushw_user();
+ if(!(tsk->tss.flags & SPARC_FLAG_KTHREAD) &&
!(tsk->flags & PF_EXITING)) {
unsigned long ctx = tlb_context_cache;
- register unsigned long paddr asm("o5");
-
- flushw_user();
if((mm->context ^ ctx) & CTX_VERSION_MASK)
get_new_mmu_context(mm, ctx);
- tsk->tss.ctx = (mm->context & 0x1fff);
- spitfire_set_secondary_context(tsk->tss.current_ds ?
- mm->context : 0);
- paddr = __pa(mm->pgd);
- __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");
- }
+
+ /* Don't worry, set_fs() will restore it... */
+ tsk->tss.ctx = (tsk->tss.current_ds ?
+ (mm->context & 0x1fff) : 0);
+ } else
+ tsk->tss.ctx = 0;
+ spitfire_set_secondary_context(tsk->tss.ctx);
+ __asm__ __volatile__("flush %g6");
+ paddr = __pa(mm->pgd);
+ __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");
}
#endif /* !(__ASSEMBLY__) */
diff --git a/include/asm-sparc64/namei.h b/include/asm-sparc64/namei.h
index af5afb721..e80c11979 100644
--- a/include/asm-sparc64/namei.h
+++ b/include/asm-sparc64/namei.h
@@ -1,4 +1,4 @@
-/* $Id: namei.h,v 1.4 1997/06/07 08:32:56 ecd Exp $
+/* $Id: namei.h,v 1.5 1997/07/17 02:24:28 davem Exp $
* linux/include/asm-sparc64/namei.h
*
* Routines to handle famous /usr/gnemul/s*.
@@ -11,6 +11,7 @@
#define SPARC_BSD_EMUL "usr/gnemul/sunos/"
#define SPARC_SOL_EMUL "usr/gnemul/solaris/"
+#if 0 /* XXX FIXME */
extern int __namei(int, const char *, struct inode *, char *, struct inode **,
struct inode **, struct qstr *, struct dentry **, int *);
@@ -44,4 +45,6 @@ __prefix_namei(int retrieve_mode, const char * name, struct inode * base,
return 0;
}
+#endif /* XXX FIXME */
+
#endif /* __SPARC64_NAMEI_H */
diff --git a/include/asm-sparc64/page.h b/include/asm-sparc64/page.h
index 71679e351..d39d3d494 100644
--- a/include/asm-sparc64/page.h
+++ b/include/asm-sparc64/page.h
@@ -1,4 +1,4 @@
-/* $Id: page.h,v 1.8 1997/03/26 12:24:21 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,10 +18,15 @@
#ifndef __ASSEMBLY__
-#define clear_page(page) memset((void *)(page), 0, PAGE_SIZE)
-#define copy_page(to,from) memcpy((void *)(to), (void *)(from), PAGE_SIZE)
+#define clear_page(page) memset((void *)(page), 0, PAGE_SIZE)
-#define STRICT_MM_TYPECHECKS
+extern void copy_page(unsigned long to, unsigned long from);
+
+/* 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.. */
@@ -89,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 e56a4024d..5cbd9a3c5 100644
--- a/include/asm-sparc64/pgtable.h
+++ b/include/asm-sparc64/pgtable.h
@@ -1,4 +1,4 @@
-/* $Id: pgtable.h,v 1.34 1997/06/02 06:33:41 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,18 +78,17 @@
#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_PRIV 0x0000000000000080 /* Software privilege bit */
+#define _PAGE_PRESENT 0x0000000000000080 /* Present Page (ie. not swapped out) */
#define _PAGE_CACHE (_PAGE_CP | _PAGE_CV)
#define __DIRTY_BITS (_PAGE_MODIFIED | _PAGE_WRITE | _PAGE_W)
#define __ACCESS_BITS (_PAGE_ACCESSED | _PAGE_READ | _PAGE_R)
-#define __PRIV_BITS (_PAGE_P | _PAGE_PRIV)
+#define __PRIV_BITS _PAGE_P
#define PAGE_NONE __pgprot (_PAGE_PRESENT | _PAGE_VALID | _PAGE_CACHE | \
__PRIV_BITS | __ACCESS_BITS)
@@ -112,7 +111,7 @@
#define _PAGE_CHG_MASK (_PFN_MASK | _PAGE_MODIFIED | _PAGE_ACCESSED | _PAGE_PRESENT)
-#define pg_iobits (_PAGE_VALID | __PRIV_BITS | __ACCESS_BITS | _PAGE_E)
+#define pg_iobits (_PAGE_VALID | _PAGE_PRESENT | __DIRTY_BITS | __ACCESS_BITS | _PAGE_E)
#define __P000 PAGE_NONE
#define __P001 PAGE_READONLY
@@ -147,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;
@@ -160,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,a,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(mm->context);
- 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,a,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)
@@ -394,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)); }
@@ -429,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.
@@ -456,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;
}
@@ -483,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;
}
@@ -509,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;
}
@@ -536,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;
}
@@ -549,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;
@@ -576,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. */
@@ -638,7 +493,7 @@ extern inline pte_t mk_swap_pte(unsigned long type, unsigned long offset)
{ pte_t pte; pte_val(pte) = (type<<PAGE_SHIFT)|(offset<<(PAGE_SHIFT+8)); return pte; }
extern inline pte_t mk_pte_io(unsigned long page, pgprot_t prot, int space)
-{ pte_t pte; pte_val(pte) = (page) | pgprot_val(prot); return pte; }
+{ pte_t pte; pte_val(pte) = ((page) | pgprot_val(prot) | _PAGE_E) & ~(unsigned long)_PAGE_CACHE; return pte; }
#define SWP_TYPE(entry) (((entry>>PAGE_SHIFT) & 0xff))
#define SWP_OFFSET(entry) ((entry) >> (PAGE_SHIFT+8))
@@ -650,17 +505,19 @@ sun4u_get_pte (unsigned long addr)
pgd_t *pgdp;
pmd_t *pmdp;
pte_t *ptep;
-
- pgdp = pgd_offset (current->mm, addr);
+
+ if (addr >= PAGE_OFFSET)
+ return addr & _PAGE_PADDR;
+ pgdp = pgd_offset_k (addr);
pmdp = pmd_offset (pgdp, addr);
ptep = pte_offset (pmdp, addr);
return pte_val (*ptep) & _PAGE_PADDR;
}
-extern __inline__ unsigned int
+extern __inline__ unsigned long
__get_phys (unsigned long addr)
{
- return (sun4u_get_pte (addr) & 0x0fffffff);
+ return sun4u_get_pte (addr);
}
extern __inline__ int
@@ -669,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 f58c9da70..019bbf600 100644
--- a/include/asm-sparc64/processor.h
+++ b/include/asm-sparc64/processor.h
@@ -1,4 +1,4 @@
-/* $Id: processor.h,v 1.27 1997/05/23 09:35:52 jj 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,34 +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;
- int current_ds, new_signal;
-
- struct pt_regs *kregs;
-
struct exec core_exec; /* just what it says. */
};
@@ -74,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__
@@ -111,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" \
@@ -135,7 +114,7 @@ do { \
"stx %%g0, [%0 + %2 + 0x78]\n\t" \
"wrpr %%g0, (1 << 3), %%wstate\n\t" \
: \
- : "r" (regs), "r" (sp - REGWIN_SZ), \
+ : "r" (regs), "r" (sp - REGWIN_SZ - STACK_BIAS), \
"i" ((const unsigned long)(&((struct pt_regs *)0)->u_regs[0]))); \
} while(0)
@@ -146,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 b971514d6..22e9da3d6 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 2233ee7f0..a1e1414d6 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 5da6f6dd1..a4784d41e 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 716b8f8c6..ea3fc6e9c 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/resource.h b/include/asm-sparc64/resource.h
index 5e7a7f8c1..b3aedd4ee 100644
--- a/include/asm-sparc64/resource.h
+++ b/include/asm-sparc64/resource.h
@@ -1,4 +1,4 @@
-/* $Id: resource.h,v 1.2 1997/04/04 00:50:27 davem Exp $
+/* $Id: resource.h,v 1.3 1997/06/14 17:35:09 davem Exp $
* resource.h: Resource definitions.
*
* Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -25,7 +25,6 @@
#define RLIM_NLIMITS 10
#ifdef __KERNEL__
-/* XXX 32-bit binary compatability... */
#define INIT_RLIMITS \
{ \
{LONG_MAX, LONG_MAX}, {LONG_MAX, LONG_MAX}, \
diff --git a/include/asm-sparc64/sigcontext.h b/include/asm-sparc64/sigcontext.h
index 9d35493d9..3fba2f834 100644
--- a/include/asm-sparc64/sigcontext.h
+++ b/include/asm-sparc64/sigcontext.h
@@ -1,14 +1,9 @@
-/* $Id: sigcontext.h,v 1.4 1997/04/04 00:50:28 davem Exp $ */
+/* $Id: sigcontext.h,v 1.8 1997/06/20 11:54:41 davem Exp $ */
#ifndef __SPARC64_SIGCONTEXT_H
#define __SPARC64_SIGCONTEXT_H
#include <asm/ptrace.h>
-/* XXX This gets exported to userland as well as kernel, it is probably
- * XXX riddled with many hard to find 32-bit binary compatability issues.
- * XXX Signals and this file need to be investigated heavily. -DaveM
- */
-
#define SUNOS_MAXWIN 31
#ifndef __ASSEMBLY__
@@ -47,12 +42,12 @@ struct sigcontext32 {
struct sigcontext {
int sigc_onstack; /* state to restore */
int sigc_mask; /* sigmask to restore */
- int sigc_sp; /* stack pointer */
- int sigc_pc; /* program counter */
- int sigc_npc; /* next program counter */
- int sigc_psr; /* for condition codes etc */
- int sigc_g1; /* User uses these two registers */
- int sigc_o0; /* within the trampoline code. */
+ unsigned long sigc_sp; /* stack pointer */
+ unsigned long sigc_pc; /* program counter */
+ unsigned long sigc_npc; /* next program counter */
+ unsigned long sigc_psr; /* for condition codes etc */
+ unsigned long sigc_g1; /* User uses these two registers */
+ unsigned long sigc_o0; /* within the trampoline code. */
/* Now comes information regarding the users window set
* at the time of the signal.
@@ -72,17 +67,6 @@ typedef struct {
} __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;
} __siginfo_t;
@@ -90,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/softirq.h b/include/asm-sparc64/softirq.h
index fa32f67e5..8386e4a15 100644
--- a/include/asm-sparc64/softirq.h
+++ b/include/asm-sparc64/softirq.h
@@ -43,10 +43,12 @@ do { int ent = nr; \
do { int ent = nr; \
bh_mask &= ~(1 << ent); \
bh_mask_count[ent]++; \
+ barrier(); \
} while(0)
#define enable_bh(nr) \
do { int ent = nr; \
+ barrier(); \
if (!--bh_mask_count[ent]) \
bh_mask |= 1 << ent; \
} while(0)
diff --git a/include/asm-sparc64/spinlock.h b/include/asm-sparc64/spinlock.h
index ec1ad2ea0..cefd43309 100644
--- a/include/asm-sparc64/spinlock.h
+++ b/include/asm-sparc64/spinlock.h
@@ -53,6 +53,11 @@ typedef struct { } rwlock_t;
#else /* !(__SMP__) */
+/* All of these locking primitives are expected to work properly
+ * even in an RMO memory model, which currently is what the kernel
+ * runs in.
+ */
+
typedef unsigned char spinlock_t;
#define SPIN_LOCK_UNLOCKED 0
#define spin_lock_init(lock) (*(lock) = 0)
@@ -64,6 +69,7 @@ extern __inline__ void spin_lock(spinlock_t *lock)
1: ldstub [%0], %%g2
brnz,a,pn %%g2, 2f
ldub [%0], %%g2
+ membar #LoadLoad | #LoadStore
.text 2
2: brnz,a,pt 2b
ldub [%0], %%g2
@@ -77,7 +83,8 @@ extern __inline__ void spin_lock(spinlock_t *lock)
extern __inline__ int spin_trylock(spinlock_t *lock)
{
unsigned int result;
- __asm__ __volatile__("ldstub [%1], %0"
+ __asm__ __volatile__("ldstub [%1], %0\n\t"
+ "membar #LoadLoad | #LoadStore"
: "=r" (result)
: "r" (lock)
: "memory");
@@ -86,7 +93,11 @@ extern __inline__ int spin_trylock(spinlock_t *lock)
extern __inline__ void spin_unlock(spinlock_t *lock)
{
- __asm__ __volatile__("stb %%g0, [%0]" : : "r" (lock) : "memory");
+ __asm__ __volatile__("membar #StoreStore | #LoadStore\n\t"
+ "stb %%g0, [%0]"
+ : /* No outputs */
+ : "r" (lock)
+ : "memory");
}
extern __inline__ void spin_lock_irq(spinlock_t *lock)
@@ -96,6 +107,7 @@ extern __inline__ void spin_lock_irq(spinlock_t *lock)
ldstub [%0], %%g2
brnz,a,pn %%g2, 2f
ldub [%0], %%g2
+ membar #LoadLoad | #LoadStore
.text 2
2: brnz,a,pt 2b
ldub [%0], %%g2
@@ -109,6 +121,7 @@ extern __inline__ void spin_lock_irq(spinlock_t *lock)
extern __inline__ void spin_unlock_irq(spinlock_t *lock)
{
__asm__ __volatile__("
+ membar #StoreStore | #LoadStore
stb %%g0, [%0]
wrpr %%g0, 0x0, %%pil
" : /* no outputs */
@@ -116,28 +129,30 @@ extern __inline__ void spin_unlock_irq(spinlock_t *lock)
: "memory");
}
-#define spin_lock_irqsave(lock, flags) \
-do { register spinlock_t *lp asm("g1"); \
- lp = lock; \
- __asm__ __volatile__( \
- " rdpr %%pil, %0\n\t" \
- " wrpr %%g0, 15, %%pil\n\t" \
- "1: ldstub [%1], %%g2\n\t" \
- " brnz,a,pnt %%g2, 2f\n\t" \
- " ldub [%1], %%g2\n\t" \
- " .text 2\n\t" \
- "2: brnz,a,pt %%g2, 2b\n\t" \
- " ldub [%1], %%g2\n\t" \
- " b,a,pt %%xcc, 1b\n\t" \
- " .previous\n" \
- : "=r" (flags) \
- : "r" (lp) \
- : "g2", "memory"); \
+#define spin_lock_irqsave(lock, flags) \
+do { register spinlock_t *lp asm("g1"); \
+ lp = lock; \
+ __asm__ __volatile__( \
+ " rdpr %%pil, %0\n\t" \
+ " wrpr %%g0, 15, %%pil\n\t" \
+ "1: ldstub [%1], %%g2\n\t" \
+ " brnz,a,pnt %%g2, 2f\n\t" \
+ " ldub [%1], %%g2\n\t" \
+ " membar #LoadLoad | #LoadStore\n\t" \
+ " .text 2\n\t" \
+ "2: brnz,a,pt %%g2, 2b\n\t" \
+ " ldub [%1], %%g2\n\t" \
+ " b,a,pt %%xcc, 1b\n\t" \
+ " .previous\n" \
+ : "=r" (flags) \
+ : "r" (lp) \
+ : "g2", "memory"); \
} while(0)
extern __inline__ void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags)
{
__asm__ __volatile__("
+ membar #StoreStore | #LoadStore
stb %%g0, [%0]
wrpr %1, 0x0, %%pil
" : /* no outputs */
@@ -161,6 +176,7 @@ extern __inline__ void read_lock(rwlock_t *rw)
cmp %%g2, %%g3
bne,a,pn %%xcc, 1b
ldx [%0],%%g2
+ membar #LoadLoad | #LoadStore
.text 2
2: ldx [%0], %%g2
3: brlz,pt %%g2, 3b
@@ -169,12 +185,13 @@ extern __inline__ void read_lock(rwlock_t *rw)
.previous
" : /* no outputs */
: "r" (rw)
- : "g2", "g3", "memory");
+ : "g2", "g3", "cc", "memory");
}
extern __inline__ void read_unlock(rwlock_t *rw)
{
__asm__ __volatile__("
+ membar #StoreStore | #LoadStore
ldx [%0], %%g2
1:
sub %%g2, 1, %%g3
@@ -184,7 +201,7 @@ extern __inline__ void read_unlock(rwlock_t *rw)
ldx [%0], %%g2
" : /* no outputs */
: "r" (rw)
- : "g2", "g3", "memory");
+ : "g2", "g3", "cc", "memory");
}
extern __inline__ void write_lock(rwlock_t *rw)
@@ -203,6 +220,7 @@ extern __inline__ void write_lock(rwlock_t *rw)
andncc %%g3, %%g5, %%g0
bne,a,pn %%xcc, 3f
ldx [%0], %%g2
+ membar #LoadLoad | #LoadStore
.text 2
3:
andn %%g2, %%g5, %%g3
@@ -210,6 +228,7 @@ extern __inline__ void write_lock(rwlock_t *rw)
cmp %%g2, %%g3
bne,a,pn %%xcc, 3b
ldx [%0], %%g2
+ membar #LoadLoad | #LoadStore
5: ldx [%0], %%g2
6: brlz,pt %%g2, 6b
ldx [%0], %%g2
@@ -222,6 +241,7 @@ extern __inline__ void write_lock(rwlock_t *rw)
extern __inline__ void write_unlock(rwlock_t *rw)
{
__asm__ __volatile__("
+ membar #StoreStore | #LoadStore
sethi %%uhi(0x8000000000000000), %%g5
ldx [%0], %%g2
sllx %%g5, 32, %%g5
diff --git a/include/asm-sparc64/string.h b/include/asm-sparc64/string.h
index b420d80bb..45b166c91 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.7 1997/07/13 18:23:44 davem Exp $
* string.h: External definitions for optimized assembly string
* routines for the Linux Kernel.
*
@@ -13,8 +13,14 @@
#ifdef __KERNEL__
+#include <asm/asi.h>
+
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,24 +41,11 @@ 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);
} else {
-#if 0
- switch(n) {
- case 8192:
- __copy_1page(to, from);
- break;
- default:
-#endif
- __memcpy(to, from, n);
-#if 0
- break;
- }
-#endif
+ __memcpy(to, from, n);
}
}
return to;
@@ -74,15 +67,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 d0d88fa5c..6e7c42e55 100644
--- a/include/asm-sparc64/system.h
+++ b/include/asm-sparc64/system.h
@@ -1,4 +1,4 @@
-/* $Id: system.h,v 1.22 1997/06/01 10:27:28 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 <asm/fpumacro.h>
-
-#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)), \
@@ -200,15 +171,15 @@ extern __inline__ unsigned long xchg_u64(__volatile__ unsigned long *m,
{
unsigned long temp;
__asm__ __volatile__("
- ldx [%3], %1
-1:
+ mov %0, %%g1
+1: ldx [%3], %1
casx [%3], %1, %0
cmp %1, %0
bne,a,pn %%xcc, 1b
- ldx [%3], %1
+ mov %%g1, %0
" : "=&r" (val), "=&r" (temp)
: "0" (val), "r" (m)
- : "cc");
+ : "g1", "cc");
return val;
}
diff --git a/include/asm-sparc64/uaccess.h b/include/asm-sparc64/uaccess.h
index 40ad3ee21..c0668e3f2 100644
--- a/include/asm-sparc64/uaccess.h
+++ b/include/asm-sparc64/uaccess.h
@@ -1,4 +1,4 @@
-/* $Id: uaccess.h,v 1.13 1997/05/29 12:45:04 jj Exp $ */
+/* $Id: uaccess.h,v 1.20 1997/07/13 18:23:45 davem Exp $ */
#ifndef _ASM_UACCESS_H
#define _ASM_UACCESS_H
@@ -22,26 +22,26 @@
*
* "For historical reasons, these macros are grossly misnamed." -Linus
*/
-#define KERNEL_DS 0
-#define USER_DS -1
+#define KERNEL_DS 0x00
+#define USER_DS 0x2B /* har har har */
#define VERIFY_READ 0
#define VERIFY_WRITE 1
#define get_fs() (current->tss.current_ds)
#define get_ds() (KERNEL_DS)
-extern __inline__ void set_fs(int val)
-{
- if (val != current->tss.current_ds) {
- if (val == KERNEL_DS) {
- flushw_user ();
- spitfire_set_secondary_context (0);
- } else {
- spitfire_set_secondary_context (current->mm->context);
- }
- current->tss.current_ds = val;
- }
-}
+#define set_fs(val) \
+do { \
+ current->tss.current_ds = (val); \
+ if ((val) == KERNEL_DS) { \
+ flushw_user (); \
+ current->tss.ctx = 0; \
+ } else { \
+ current->tss.ctx = (current->mm->context & 0x1fff); \
+ } \
+ spitfire_set_secondary_context(current->tss.ctx); \
+ __asm__ __volatile__("flush %g6"); \
+} while(0)
#define __user_ok(addr,size) 1
#define __kernel_ok (get_fs() == KERNEL_DS)
@@ -255,8 +255,44 @@ __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);
+
+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);
+
+extern __kernel_size_t __copy_in_user(void *to, const void *from,
+ __kernel_size_t size);
+
+#define copy_from_user(to,from,n) \
+ __copy_from_user((void *)(to), \
+ (void *)(from), (__kernel_size_t)(n))
+
+#define copy_from_user_ret(to,from,n,retval) ({ \
+if (copy_from_user(to,from,n)) \
+ return retval; \
+})
+
+#define __copy_from_user_ret(to,from,n,retval) ({ \
+if (__copy_from_user(to,from,n)) \
+ return retval; \
+})
#define copy_to_user(to,from,n) \
__copy_to_user((void *)(to), \
@@ -272,37 +308,27 @@ if (__copy_to_user(to,from,n)) \
return retval; \
})
-#define copy_from_user(to,from,n) \
- __copy_from_user((void *)(to), \
- (void *)(from), (__kernel_size_t)(n))
+#define copy_in_user(to,from,n) \
+ __copy_in_user((void *)(to), \
+ (void *) (from), (__kernel_size_t)(n))
-#define copy_from_user_ret(to,from,n,retval) ({ \
-if (copy_from_user(to,from,n)) \
+#define copy_in_user_ret(to,from,n,retval) ({ \
+if (copy_in_user(to,from,n)) \
return retval; \
})
-#define __copy_from_user_ret(to,from,n,retval) ({ \
-if (__copy_from_user(to,from,n)) \
+#define __copy_in_user_ret(to,from,n,retval) ({ \
+if (__copy_in_user(to,from,n)) \
return retval; \
})
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 000000000..1899ff971
--- /dev/null
+++ b/include/asm-sparc64/uctx.h
@@ -0,0 +1,71 @@
+/* $Id: uctx.h,v 1.1 1997/07/18 06:29:24 ralf 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/unistd.h b/include/asm-sparc64/unistd.h
index cb17f1888..27afe645e 100644
--- a/include/asm-sparc64/unistd.h
+++ b/include/asm-sparc64/unistd.h
@@ -1,4 +1,4 @@
-/* $Id: unistd.h,v 1.5 1997/05/21 10:21:57 jj Exp $ */
+/* $Id: unistd.h,v 1.7 1997/06/16 05:37:44 davem Exp $ */
#ifndef _SPARC64_UNISTD_H
#define _SPARC64_UNISTD_H
@@ -113,35 +113,35 @@
#define __NR_setdopt 94 /* SunOS Specific */
#define __NR_fsync 95 /* Common */
#define __NR_setpriority 96 /* Common */
-#define __NR_socket 97 /* SunOS Specific */
-#define __NR_connect 98 /* SunOS Specific */
-#define __NR_accept 99 /* SunOS Specific */
+#define __NR_socket 97 /* Common */
+#define __NR_connect 98 /* Common */
+#define __NR_accept 99 /* Common */
#define __NR_getpriority 100 /* Common */
-#define __NR_send 101 /* SunOS Specific */
-#define __NR_recv 102 /* SunOS Specific */
+#define __NR_send 101 /* Common */
+#define __NR_recv 102 /* Common */
/* #define __NR_ni_syscall 103 ENOSYS under SunOS */
-#define __NR_bind 104 /* SunOS Specific */
-#define __NR_setsockopt 105 /* SunOS Specific */
-#define __NR_listen 106 /* SunOS Specific */
+#define __NR_bind 104 /* Common */
+#define __NR_setsockopt 105 /* Common */
+#define __NR_listen 106 /* Common */
/* #define __NR_ni_syscall 107 ENOSYS under SunOS */
#define __NR_sigvec 108 /* SunOS Specific */
#define __NR_sigblock 109 /* SunOS Specific */
#define __NR_sigsetmask 110 /* SunOS Specific */
#define __NR_sigpause 111 /* SunOS Specific */
#define __NR_sigstack 112 /* SunOS Specific */
-#define __NR_recvmsg 113 /* SunOS Specific */
-#define __NR_sendmsg 114 /* SunOS Specific */
+#define __NR_recvmsg 113 /* Common */
+#define __NR_sendmsg 114 /* Common */
#define __NR_vtrace 115 /* SunOS Specific */
#define __NR_gettimeofday 116 /* Common */
#define __NR_getrusage 117 /* Common */
-#define __NR_getsockopt 118 /* SunOS Specific */
+#define __NR_getsockopt 118 /* Common */
/* #define __NR_ni_syscall 119 ENOSYS under SunOS */
#define __NR_readv 120 /* Common */
#define __NR_writev 121 /* Common */
#define __NR_settimeofday 122 /* Common */
#define __NR_fchown 123 /* Common */
#define __NR_fchmod 124 /* Common */
-#define __NR_recvfrom 125 /* SunOS Specific */
+#define __NR_recvfrom 125 /* Common */
#define __NR_setreuid 126 /* Common */
#define __NR_setregid 127 /* Common */
#define __NR_rename 128 /* Common */
@@ -149,15 +149,15 @@
#define __NR_ftruncate 130 /* Common */
#define __NR_flock 131 /* Common */
/* #define __NR_ni_syscall 132 ENOSYS under SunOS */
-#define __NR_sendto 133 /* SunOS Specific */
-#define __NR_shutdown 134 /* SunOS Specific */
-#define __NR_socketpair 135 /* SunOS Specific */
+#define __NR_sendto 133 /* Common */
+#define __NR_shutdown 134 /* Common */
+#define __NR_socketpair 135 /* Common */
#define __NR_mkdir 136 /* Common */
#define __NR_rmdir 137 /* Common */
#define __NR_utimes 138 /* SunOS Specific */
/* #define __NR_ni_syscall 139 ENOSYS under SunOS */
#define __NR_adjtime 140 /* SunOS Specific */
-#define __NR_getpeername 141 /* SunOS Specific */
+#define __NR_getpeername 141 /* Common */
#define __NR_gethostid 142 /* SunOS Specific */
/* #define __NR_ni_syscall 143 ENOSYS under SunOS */
#define __NR_getrlimit 144 /* Common */
@@ -166,7 +166,7 @@
/* #define __NR_ni_syscall 147 ENOSYS under SunOS */
/* #define __NR_ni_syscall 148 ENOSYS under SunOS */
/* #define __NR_ni_syscall 149 ENOSYS under SunOS */
-#define __NR_getsockname 150 /* SunOS Specific */
+#define __NR_getsockname 150 /* Common */
#define __NR_getmsg 151 /* SunOS Specific */
#define __NR_putmsg 152 /* SunOS Specific */
#define __NR_poll 153 /* SunOS Specific */
@@ -467,6 +467,7 @@ static __inline__ pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned lo
#endif /* __KERNEL_SYSCALLS__ */
+#ifdef __KERNEL__
/* sysconf options, for SunOS compatibility */
#define _SC_ARG_MAX 1
#define _SC_CHILD_MAX 2
@@ -476,5 +477,6 @@ static __inline__ pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned lo
#define _SC_JOB_CONTROL 6
#define _SC_SAVED_IDS 7
#define _SC_VERSION 8
+#endif
#endif /* _SPARC64_UNISTD_H */
diff --git a/include/asm-sparc64/vaddrs.h b/include/asm-sparc64/vaddrs.h
index cd82abb06..b88085668 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 eb91dd0fa..adf4278e6 100644
--- a/include/linux/binfmts.h
+++ b/include/linux/binfmts.h
@@ -19,12 +19,11 @@ struct linux_binprm{
unsigned long p;
int sh_bang;
int java; /* Java binary, prevent recursive invocation */
- struct inode * inode;
+ struct dentry * dentry;
int e_uid, e_gid;
int argc, envc;
char * filename; /* Name of binary */
unsigned long loader, exec;
- int dont_iput; /* binfmt handler has put inode */
};
/*
@@ -42,15 +41,16 @@ struct linux_binfmt {
extern int register_binfmt(struct linux_binfmt *);
extern int unregister_binfmt(struct linux_binfmt *);
-extern int read_exec(struct inode *inode, unsigned long offset,
+extern int read_exec(struct dentry *, unsigned long offset,
char * addr, unsigned long count, int to_kmem);
-extern int open_inode(struct inode * inode, int mode);
+extern int open_dentry(struct dentry *, int mode);
extern int init_elf_binfmt(void);
extern int init_elf32_binfmt(void);
extern int init_irix_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 e9ef418f7..99ed0e347 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 <linux/config.h>
+
#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/cyclades.h b/include/linux/cyclades.h
index 9386c17f0..0b22c2e30 100644
--- a/include/linux/cyclades.h
+++ b/include/linux/cyclades.h
@@ -1,4 +1,4 @@
-/* $Revision: 1.7 $$Date: 1997/03/26 10:30:00 $
+/* $Revision: 2.0 $$Date: 1997/06/30 10:30:00 $
* linux/include/linux/cyclades.h
*
* This file is maintained by Marcio Saito <marcio@cyclades.com> and
@@ -6,6 +6,9 @@
*
* This file contains the general definitions for the cyclades.c driver
*$Log: cyclades.h,v $
+ *Revision 1.1.1.1 1997/06/01 03:17:04 ralf
+ *Initial import of Linux/MIPS pre-2.1.40.
+ *
*Revision 1.7 1997/03/26 10:30:00 daniel
*new entries at the end of cyclades_port struct to reallocate
*variables illegally allocated within card memory.
@@ -86,6 +89,8 @@ typedef unsigned char ucchar; /* 8 bits, unsigned */
*/
#define DP_WINDOW_SIZE (0x00080000) /* window size 512 Kb */
+#define ZE_DP_WINDOW_SIZE (0x00100000) /* window size 1 Mb (Ze and
+ 8Zo V.2 */
#define CTRL_WINDOW_SIZE (0x00000100) /* runtime regs 256 bytes */
/*
@@ -183,6 +188,7 @@ struct RUNTIME_9060 {
#define ID_ADDRESS 0x00000180L /* signature/pointer address */
#define ZFIRM_ID 0x5557465AL /* ZFIRM/U signature */
+#define ZFIRM_HLT 0x59505B5CL /* ZFIRM needs external power supply */
struct FIRM_ID {
uclong signature; /* ZFIRM/U signature */
uclong zfwctrl_addr; /* pointer to ZFW_CTRL structure */
@@ -238,7 +244,10 @@ struct FIRM_ID {
#define C_IN_RXBRK 0x00001000 /* Break received */
#define C_IN_PR_ERROR 0x00002000 /* parity error */
#define C_IN_FR_ERROR 0x00004000 /* frame error */
-
+#define C_IN_OVR_ERROR 0x00008000 /* overrun error */
+#define C_IN_RXOFL 0x00010000 /* RX buffer overflow */
+#define C_IN_IOCTLW 0x00020000 /* I/O control w/ wait */
+
/* flow control */
#define C_FL_OXX 0x00000001 /* output Xon/Xoff flow control */
@@ -294,6 +303,8 @@ struct FIRM_ID {
#define C_CM_RXBRK 0x84 /* Break received */
#define C_CM_PR_ERROR 0x85 /* Parity error */
#define C_CM_FR_ERROR 0x86 /* Frame error */
+#define C_CM_OVR_ERROR 0x87 /* Overrun error */
+#define C_CM_RXOFL 0x88 /* RX buffer overflow */
#define C_CM_CMDERROR 0x90 /* command error */
#define C_CM_FATAL 0x91 /* fatal error */
#define C_CM_HW_RESET 0x92 /* reset board */
@@ -468,9 +479,10 @@ struct cyclades_port {
#define CyMaxChipsPerCard 8
-#define CyPCI_Ywin 0x4000
-#define CyPCI_Zctl 0x100
-#define CyPCI_Zwin 0x80000
+#define CyPCI_Ywin 0x4000
+#define CyPCI_Zctl 0x100
+#define CyPCI_Zwin 0x80000
+#define CyPCI_Ze_win (2 * CyPCI_Zwin)
/**** CD1400 registers ****/
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
new file mode 100644
index 000000000..1ca8af0d7
--- /dev/null
+++ b/include/linux/dcache.h
@@ -0,0 +1,122 @@
+#ifndef __LINUX_DCACHE_H
+#define __LINUX_DCACHE_H
+
+/*
+ * linux/include/linux/dcache.h
+ *
+ * Directory cache data structures
+ */
+
+#define D_MAXLEN 1024
+
+#define IS_ROOT(x) ((x) == (x)->d_parent)
+
+/*
+ * "quick string" -- eases parameter passing, but more importantly
+ * saves "metadata" about the string (ie length and the hash).
+ */
+struct qstr {
+ const unsigned char * name;
+ unsigned 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 {
+ int d_count;
+ unsigned int d_flags;
+ struct inode * d_inode; /* Where the name belongs to - NULL is negative */
+ struct dentry * d_parent; /* parent directory */
+ struct dentry * d_mounts; /* mount information */
+ struct dentry * d_covers;
+ struct list_head d_hash; /* lookup hash list */
+ struct list_head d_alias; /* inode alias list */
+ struct list_head d_lru; /* d_count = 0 LRU list */
+ struct qstr d_name;
+ struct dentry * (*d_revalidate)(struct dentry *);
+};
+
+/*
+ * d_drop() unhashes the entry from the parent
+ * dentry hashes, so that it won't be found through
+ * a VFS lookup any more. Note that this is different
+ * from deleting the dentry - d_delete will try to
+ * mark the dentry negative if possible, giving a
+ * successful _negative_ lookup, while d_drop will
+ * just make the cache lookup fail.
+ *
+ * d_drop() is used mainly for stuff that wants
+ * to invalidate a dentry for some reason (NFS
+ * timeouts or autofs deletes).
+ */
+static inline void d_drop(struct dentry * dentry)
+{
+ list_del(&dentry->d_hash);
+ INIT_LIST_HEAD(&dentry->d_hash);
+}
+
+/*
+ * These are the low-level FS interfaces to the dcache..
+ */
+extern void d_instantiate(struct dentry *, struct inode *);
+extern void d_delete(struct dentry *);
+
+
+/* allocate/de-allocate */
+extern void d_free(struct dentry *);
+extern struct dentry * d_alloc(struct dentry * parent, const struct qstr *name);
+extern void shrink_dcache(void);
+
+/* only used at mount-time */
+extern struct dentry * d_alloc_root(struct inode * root_inode, struct dentry * old_root);
+
+/*
+ * This adds the entry to the hash queues and initializes "d_inode".
+ * The entry was actually filled in earlier during "d_alloc()"
+ */
+extern void d_add(struct dentry * entry, struct inode * inode);
+
+/* used for rename() and baskets */
+extern 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 dentry * dir, struct qstr * name);
+
+/* write full pathname into buffer and return length */
+extern int d_path(struct dentry * entry, struct dentry * chroot, char * buf);
+
+/* Allocation counts.. */
+static inline struct dentry * dget(struct dentry *dentry)
+{
+ if (dentry)
+ dentry->d_count++;
+ return dentry;
+}
+
+extern void dput(struct dentry *);
+
+/*
+ * This is ugly. The inode:dentry relationship is a 1:n
+ * relationship, so we have to return one (random) dentry
+ * from the alias list. We select the first one..
+ */
+#define i_dentry(inode) \
+ list_entry((inode)->i_dentry.next, struct dentry, d_alias)
+
+#endif /* __LINUX_DCACHE_H */
diff --git a/include/linux/ext2_fs.h b/include/linux/ext2_fs.h
index 9c66cfc29..2831b1850 100644
--- a/include/linux/ext2_fs.h
+++ b/include/linux/ext2_fs.h
@@ -483,6 +483,7 @@ extern int ext2_getcluster (struct inode * inode, long block);
extern void ext2_read_inode (struct inode *);
extern void ext2_write_inode (struct inode *);
extern void ext2_put_inode (struct inode *);
+extern void ext2_delete_inode (struct inode *);
extern int ext2_sync_inode (struct inode *);
extern void ext2_discard_prealloc (struct inode *);
@@ -492,17 +493,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 dentry *);
+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 *, ...)
@@ -517,7 +516,7 @@ extern void ext2_write_super (struct super_block *);
extern int ext2_remount (struct super_block *, int *, char *);
extern struct super_block * ext2_read_super (struct super_block *,void *,int);
extern int init_ext2_fs(void);
-extern void ext2_statfs (struct super_block *, struct statfs *, int);
+extern int ext2_statfs (struct super_block *, struct statfs *, int);
/* truncate.c */
extern void ext2_truncate (struct inode *);
diff --git a/include/linux/file.h b/include/linux/file.h
index 0cb531c0c..b8cc01f0a 100644
--- a/include/linux/file.h
+++ b/include/linux/file.h
@@ -12,7 +12,7 @@ extern inline struct file * fget(unsigned long fd)
return file;
}
-extern int __fput(struct file *, struct inode *);
+extern int __fput(struct file *);
extern void insert_file_free(struct file *file);
/* It does not matter which list it is on. */
@@ -23,13 +23,13 @@ extern inline void remove_filp(struct file *file)
*file->f_pprev = file->f_next;
}
-extern inline int fput(struct file *file, struct inode *inode)
+extern inline int fput(struct file *file)
{
int count = file->f_count-1;
int error = 0;
if (!count) {
- error = __fput(file, inode);
+ error = __fput(file);
file->f_count = 0;
remove_filp(file);
insert_file_free(file);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 76fa53a7e..b459e966c 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -15,12 +15,13 @@
#include <linux/net.h>
#include <linux/kdev_t.h>
#include <linux/ioctl.h>
+#include <linux/list.h>
+#include <linux/dcache.h>
+
#include <asm/atomic.h>
+#include <asm/bitops.h>
+
-/* Prefixes for routines (having no effect), but indicate what
- * the routine may do. This can greatly ease reasoning about routines...
- */
-#define blocking /*routine may schedule()*/
/*
* It's silly to have NR_OPEN bigger than NR_FILE, but I'll fix
@@ -74,9 +75,6 @@ extern int max_files, nr_files;
*/
#define FS_IBASKET 8 /* FS does callback to free_ibasket() if space gets low. */
-/* public flags for i_status */
-#define ST_MODIFIED 1024
-
/*
* These are the fs-independent mount-flags: up to 16 flags are supported
*/
@@ -104,13 +102,6 @@ extern int max_files, nr_files;
#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
* possible to override it selectively if you really wanted to with some
@@ -129,7 +120,12 @@ extern int max_files, nr_files;
#define IS_APPEND(inode) ((inode)->i_flags & S_APPEND)
#define IS_IMMUTABLE(inode) ((inode)->i_flags & S_IMMUTABLE)
#define IS_NOATIME(inode) ((inode)->i_flags & MS_NOATIME)
-#define DO_UPDATE_ATIME(inode) (!IS_NOATIME(inode) && !IS_RDONLY(inode))
+
+#define UPDATE_ATIME(inode) \
+ if (!IS_NOATIME(inode) && !IS_RDONLY(inode)) { \
+ inode->i_atime = CURRENT_TIME; \
+ mark_inode_dirty(inode); \
+ }
/* the read-only stuff doesn't really belong here, but any other place is
probably as bad and I don't want to create yet another include file. */
@@ -155,7 +151,7 @@ extern int max_files, nr_files;
extern void buffer_init(void);
extern void inode_init(void);
extern void file_table_init(void);
-extern unsigned long name_cache_init(unsigned long start, unsigned long end);
+extern void dcache_init(void);
typedef char buffer_block[BLOCK_SIZE];
@@ -183,18 +179,16 @@ typedef char buffer_block[BLOCK_SIZE];
*/
struct buffer_head {
/* First cache line: */
+ struct buffer_head * b_next; /* Hash queue list */
unsigned long b_blocknr; /* block number */
+ unsigned long b_size; /* block size */
kdev_t b_dev; /* device (B_FREE = free) */
kdev_t b_rdev; /* Real device */
unsigned long b_rsector; /* Real buffer location on disk */
- struct buffer_head * b_next; /* Hash queue list */
struct buffer_head * b_this_page; /* circular list of buffers in one page */
-
- /* Second cache line: */
unsigned long b_state; /* buffer state bitmap (see above) */
struct buffer_head * b_next_free;
unsigned int b_count; /* users using this block */
- unsigned long b_size; /* block size */
/* Non-performance-critical data follows. */
char * b_data; /* pointer to data block (1024 bytes) */
@@ -305,14 +299,12 @@ struct iattr {
#include <linux/quota.h>
struct inode {
- struct inode *i_hash_next;
- struct inode *i_hash_prev;
- struct inode *i_next;
- struct inode *i_prev;
+ struct list_head i_hash;
+ struct list_head i_list;
unsigned long i_ino;
kdev_t i_dev;
- atomic_t i_count;
+ unsigned short i_count;
umode_t i_mode;
nlink_t i_nlink;
uid_t i_uid;
@@ -335,28 +327,14 @@ struct inode {
struct page *i_pages;
struct dquot *i_dquot[MAXQUOTAS];
- struct inode *i_lru_next;
- struct inode *i_lru_prev;
+ struct list_head i_dentry;
- struct inode *i_basket_next;
- struct inode *i_basket_prev;
- struct dentry *i_dentry;
+ unsigned long i_state;
- 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;
unsigned char i_pipe;
unsigned char i_sock;
- unsigned char i_level;
- unsigned short i_fill;
-
int i_writecount;
unsigned int i_attr_flags;
union {
@@ -377,9 +355,21 @@ struct inode {
} u;
};
+/* Inode state bits.. */
+#define I_DIRTY 0
+#define I_LOCK 1
+#define I_FREEING 2
+
+extern void __mark_inode_dirty(struct inode *);
+static inline void mark_inode_dirty(struct inode *inode)
+{
+ if (!test_and_set_bit(I_DIRTY, &inode->i_state))
+ __mark_inode_dirty(inode);
+}
+
struct file {
struct file *f_next, **f_pprev;
- struct inode *f_inode;
+ struct dentry *f_dentry;
struct file_operations *f_op;
mode_t f_mode;
loff_t f_pos;
@@ -395,6 +385,8 @@ struct file {
void *private_data;
};
+extern int init_private_file(struct file *, struct dentry *, int);
+
#define FL_POSIX 1
#define FL_FLOCK 2
#define FL_BROKEN 4 /* broken flock() emulation */
@@ -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 dentry *);
+ 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);
@@ -576,12 +568,13 @@ struct inode_operations {
struct super_operations {
void (*read_inode) (struct inode *);
- int (*notify_change) (struct inode *, struct iattr *);
void (*write_inode) (struct inode *);
void (*put_inode) (struct inode *);
+ void (*delete_inode) (struct inode *);
+ int (*notify_change) (struct inode *, struct iattr *);
void (*put_super) (struct super_block *);
void (*write_super) (struct super_block *);
- void (*statfs) (struct super_block *, struct statfs *, int);
+ int (*statfs) (struct super_block *, struct statfs *, int);
int (*remount_fs) (struct super_block *, int *, char *);
};
@@ -610,7 +603,7 @@ asmlinkage int sys_close(unsigned int); /* yes, it's really unsigned */
extern void kill_fasync(struct fasync_struct *fa, int sig);
-extern int getname(const char * filename, char **result);
+extern char * getname(const char * filename);
extern void putname(char * name);
extern int do_truncate(struct inode *, unsigned long);
extern int register_blkdev(unsigned int, const char *, struct file_operations *);
@@ -640,8 +633,8 @@ 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_remount_ro(kdev_t dev);
+extern int fs_may_umount(struct super_block *, struct dentry * root);
+extern int fs_may_remount_ro(struct super_block *);
extern struct file *inuse_filps;
extern struct super_block super_blocks[NR_SUPER];
@@ -689,15 +682,31 @@ 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);
-extern int do_mknod(const char * filename, int mode, dev_t dev);
+extern struct dentry * open_namei(const char * pathname, int flag, int mode);
+extern struct dentry * 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 struct dentry * __namei(const char *, int);
+
+#define namei(pathname) __namei(pathname, 1)
+#define lnamei(pathname) __namei(pathname, 0)
+
#include <asm/semaphore.h>
/* Intended for short locks of the global data structures in inode.c.
@@ -726,87 +735,19 @@ extern inline void vfs_unlock(void)
/* Not to be used by ordinary vfs users */
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) {
- extern void wake_up_interruptible(struct wait_queue **q);
+extern void iput(struct inode * inode);
- if(inode->i_pipe)
- wake_up_interruptible(&inode->u.pipe_i.wait);
+extern struct inode * iget(struct super_block * sb, unsigned long nr);
+extern void clear_inode(struct inode * inode);
+extern struct inode * get_empty_inode(void);
- /* 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)
- _iput(inode);
- }
-}
-
-extern blocking struct inode * __iget(struct super_block * sb, unsigned long nr, int crsmnt);
-extern blocking void _clear_inode(struct inode * inode, int external, int verbose);
-extern blocking inline void clear_inode(struct inode * inode)
-{
- vfs_lock();
- _clear_inode(inode, 1, 1);
- vfs_unlock();
-}
-extern blocking struct inode * _get_empty_inode(void);
-extern inline blocking struct inode * get_empty_inode(void)
-{
- struct inode * inode;
- vfs_lock();
- inode = _get_empty_inode();
- vfs_unlock();
- return inode;
-}
/* Please prefer to use this function in future, instead of using
* a get_empty_inode()/insert_inode_hash() combination.
* It allows for better checking and less race conditions.
*/
-blocking struct inode * get_empty_inode_hashed(dev_t i_dev, unsigned long i_ino);
-
-extern inline blocking int free_ibasket(struct super_block * sb)
-{
- extern blocking int _free_ibasket(struct super_block * sb);
- int res;
- vfs_lock();
- res = _free_ibasket(sb);
- vfs_unlock();
- return res;
-}
+extern struct inode * get_empty_inode_hashed(dev_t i_dev, unsigned long i_ino);
extern void insert_inode_hash(struct inode *);
-extern blocking struct inode * get_pipe_inode(void);
extern int get_unused_fd(void);
extern void put_unused_fd(int);
extern struct file * get_empty_filp(void);
@@ -872,12 +813,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 <linux/minix_fs.h>
#include <linux/minix_fs_sb.h>
diff --git a/include/linux/ghash.h b/include/linux/ghash.h
new file mode 100644
index 000000000..278f6c2f6
--- /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/if_arp.h b/include/linux/if_arp.h
index 5f18a049a..c93764cb7 100644
--- a/include/linux/if_arp.h
+++ b/include/linux/if_arp.h
@@ -47,6 +47,8 @@
#define ARPHRD_ROSE 270
#define ARPHRD_X25 271 /* CCITT X.25 */
#define ARPHRD_PPP 512
+#define ARPHRD_HDLC 513 /* (Cisco) HDLC */
+#define ARPHRD_LAPB 516 /* LAPB */
#define ARPHRD_TUNNEL 768 /* IPIP tunnel */
#define ARPHRD_TUNNEL6 769 /* IPIP6 tunnel */
diff --git a/drivers/net/shaper.h b/include/linux/if_shaper.h
index abb6198af..abb6198af 100644
--- a/drivers/net/shaper.h
+++ b/include/linux/if_shaper.h
diff --git a/include/linux/iso_fs.h b/include/linux/iso_fs.h
index 03239cfc3..011edfe6f 100644
--- a/include/linux/iso_fs.h
+++ b/include/linux/iso_fs.h
@@ -152,8 +152,7 @@ extern int find_rock_ridge_relocation(struct iso_directory_record *, struct inod
extern int isofs_open(struct inode * inode, struct file * filp);
extern void isofs_release(struct inode * inode, struct file * filp);
-extern int isofs_lookup(struct inode * dir,const char * name, int len,
- struct inode ** result);
+extern int isofs_lookup(struct inode * dir, struct dentry *);
extern unsigned long isofs_count_free_inodes(struct super_block *sb);
extern int isofs_new_block(int dev);
extern int isofs_free_block(int dev, int block);
@@ -164,7 +163,7 @@ extern struct super_block *isofs_read_super(struct super_block *,void *,int);
extern int init_iso9660_fs(void);
extern void isofs_read_inode(struct inode *);
extern void isofs_put_inode(struct inode *);
-extern void isofs_statfs(struct super_block *, struct statfs *, int);
+extern int isofs_statfs(struct super_block *, struct statfs *, int);
extern int isofs_lseek(struct inode *, struct file *, off_t, int);
extern int isofs_read(struct inode *, struct file *, char *, int);
diff --git a/include/linux/iso_fs_i.h b/include/linux/iso_fs_i.h
index d8065500f..a1343a636 100644
--- a/include/linux/iso_fs_i.h
+++ b/include/linux/iso_fs_i.h
@@ -6,7 +6,6 @@
*/
struct iso_inode_info {
unsigned int i_first_extent;
- unsigned int i_backlink;
unsigned char i_file_format;
};
diff --git a/include/linux/joystick.h b/include/linux/joystick.h
new file mode 100644
index 000000000..a9a56273d
--- /dev/null
+++ b/include/linux/joystick.h
@@ -0,0 +1,61 @@
+#ifndef _LINUX_JOYSTICK_H
+#define _LINUX_JOYSTICK_H
+
+#define JS_RETURN sizeof(struct js_status) /*number of bytes returned by js_read*/
+#define JS_TRUE 1
+#define JS_FALSE 0
+#define JS_PORT 0x201 /*io port for joystick operations*/
+#define JS_DEF_TIMEOUT 0x1300 /*default timeout value for js_read()*/
+#define JS_DEF_CORR 0 /*default correction factor*/
+#define JS_DEF_TIMELIMIT 10L /*default data valid time =10 jiffies == 100ms*/
+#define JS_X_0 0x01 /*bit mask for x-axis js0*/
+#define JS_Y_0 0x02 /*bit mask for y-axis js0*/
+#define JS_X_1 0x04 /*bit mask for x-axis js1*/
+#define JS_Y_1 0x08 /*bit mask for y-axis js1*/
+#define JS_MAX 2 /*Max number of joysticks*/
+#define PIT_MODE 0x43 /*io port for timer 0*/
+#define PIT_COUNTER_0 0x40 /*io port for timer 0*/
+#define JSIOCSCAL 0x01 /*ioctl cmd to set joystick correction factor*/
+#define JSIOCGCAL 0x02 /*ioctl cmd to get joystick correction factor*/
+#define JSIOCSTIMEOUT 0x03 /*ioctl cmd to set maximum number of iterations
+ to wait for a timeout*/
+#define JSIOCGTIMEOUT 0x04 /*as above, to get*/
+#define JSIOCSTIMELIMIT 0x05 /*set data retention time*/
+#define JSIOCGTIMELIMIT 0x06 /*get data retention time*/
+#define JSIOCGCONFIG 0x07 /*get the whole js_data[minor] struct*/
+#define JSIOCSCONFIG 0x08 /*set the whole js_data[minor] struct
+ except js_busy!*/
+
+/*
+ * This union is used for the ioctl to set the scaling factor and to
+ * return the current values for a joystick. 'buttons' is ignored on
+ * the ioctl call
+ */
+
+struct js_status
+{
+ int buttons;
+ int x;
+ int y;
+};
+
+/*
+ * This struct is used for misc data about the joystick
+ */
+
+struct js_config
+{
+ int js_timeout; /*timeout*/
+ int busy; /*joystick is in use*/
+ long js_expiretime; /*Time when stick after which stick must be re-read*/
+ long js_timelimit; /*Max time before data is invalid*/
+ struct js_status js_save; /*last read data*/
+ struct js_status js_corr; /*correction factor*/
+};
+
+#define LATCH (1193180L/HZ) /*initial timer 0 value*/
+#define DELTA_TIME(X,Y) ((X)-(Y)+(((X)>=(Y))?0:LATCH))
+
+extern int joystick_init(void);
+
+#endif /* _LINUX_JOYSTICK_H */
diff --git a/include/linux/list.h b/include/linux/list.h
new file mode 100644
index 000000000..84a0ecef6
--- /dev/null
+++ b/include/linux/list.h
@@ -0,0 +1,63 @@
+#ifndef _LINUX_LIST_H
+#define _LINUX_LIST_H
+
+/*
+ * Simple doubly linked list implementation.
+ */
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#define LIST_HEAD(name) \
+ struct list_head name = { &name, &name }
+
+#define INIT_LIST_HEAD(ptr) do { \
+ (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+ struct list_head *next = head->next;
+ next->prev = new;
+ new->next = next;
+ new->prev = head;
+ head->next = new;
+}
+
+static inline void list_del(struct list_head *entry)
+{
+ struct list_head *next, *prev;
+ next = entry->next;
+ prev = entry->prev;
+ next->prev = prev;
+ prev->next = next;
+}
+
+static inline int list_empty(struct list_head *head)
+{
+ return head->next == head;
+}
+
+/*
+ * Splice in "list" into "head"
+ */
+static inline void list_splice(struct list_head *list, struct list_head *head)
+{
+ struct list_head *first = list->next;
+
+ if (first != list) {
+ struct list_head *last = list->prev;
+ struct list_head *at = head->next;
+
+ first->prev = head;
+ head->next = first;
+
+ last->next = at;
+ at->prev = last;
+ }
+}
+
+#define list_entry(ptr, type, member) \
+ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
+
+#endif
diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h
index 72035cc5c..5c1d0e4cc 100644
--- a/include/linux/lockd/lockd.h
+++ b/include/linux/lockd/lockd.h
@@ -166,7 +166,7 @@ void nlmsvc_free_host_resources(struct nlm_host *);
extern __inline__ struct inode *
nlmsvc_file_inode(struct nlm_file *file)
{
- return file->f_file.f_inode;
+ return file->f_file.f_dentry->d_inode;
}
/*
diff --git a/include/linux/minix_fs.h b/include/linux/minix_fs.h
index 069686fff..8c75cc3c5 100644
--- a/include/linux/minix_fs.h
+++ b/include/linux/minix_fs.h
@@ -88,19 +88,17 @@ struct minix_dir_entry {
#ifdef __KERNEL__
-extern int minix_lookup(struct inode * dir,const char * name, int len,
- struct inode ** result);
-extern int minix_create(struct inode * dir,const char * name, int len, int mode,
- struct inode ** result);
-extern int minix_mkdir(struct inode * dir, const char * name, int len, int mode);
-extern int minix_rmdir(struct inode * dir, const char * name, int len);
-extern int minix_unlink(struct inode * dir, const char * name, int len);
-extern int minix_symlink(struct inode * inode, const char * name, int len,
+extern int minix_lookup(struct inode * dir, struct dentry *dentry);
+extern int minix_create(struct inode * dir, struct dentry *dentry, int mode);
+extern int minix_mkdir(struct inode * dir, struct dentry *dentry, int mode);
+extern int minix_rmdir(struct inode * dir, struct dentry *dentry);
+extern int minix_unlink(struct inode * dir, struct dentry *dentry);
+extern int minix_symlink(struct inode * inode, struct dentry *dentry,
const char * symname);
-extern int minix_link(struct inode * oldinode, struct inode * dir, const char * name, int len);
-extern int minix_mknod(struct inode * dir, const char * name, int len, int mode, int rdev);
-extern int minix_rename(struct inode * old_dir, const char * old_name, int old_len,
- struct inode * new_dir, const char * new_name, int new_len);
+extern int minix_link(struct inode * oldinode, struct inode * dir, struct dentry *dentry);
+extern int minix_mknod(struct inode * dir, struct dentry *dentry, int mode, int rdev);
+extern int minix_rename(struct inode * old_dir, struct dentry *old_dentry,
+ struct inode * new_dir, struct dentry *new_dentry);
extern struct inode * minix_new_inode(const struct inode * dir);
extern void minix_free_inode(struct inode * inode);
extern unsigned long minix_count_free_inodes(struct super_block *sb);
@@ -121,8 +119,7 @@ extern void minix_write_super(struct super_block *);
extern int minix_remount (struct super_block * sb, int * flags, char * data);
extern void minix_read_inode(struct inode *);
extern void minix_write_inode(struct inode *);
-extern void minix_put_inode(struct inode *);
-extern void minix_statfs(struct super_block *, struct statfs *, int);
+extern int minix_statfs(struct super_block *, struct statfs *, int);
extern int minix_sync_inode(struct inode *);
extern int minix_sync_file(struct inode *, struct file *);
diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h
index 471caa0b7..8f4bde9ab 100644
--- a/include/linux/miscdevice.h
+++ b/include/linux/miscdevice.h
@@ -8,6 +8,7 @@
#define AMIGAMOUSE_MINOR 4
#define ATARIMOUSE_MINOR 5
#define SUN_MOUSE_MINOR 6
+#define PC110PAD_MINOR 9
#define RTC_MINOR 135
#define SUN_OPENPROM_MINOR 139
#define MISC_DYNAMIC_MINOR 255
diff --git a/include/linux/mm.h b/include/linux/mm.h
index c8b9046a7..bfde668c7 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -48,7 +48,7 @@ struct vm_area_struct {
struct vm_operations_struct * vm_ops;
unsigned long vm_offset;
- struct inode * vm_inode;
+ struct dentry * vm_dentry;
unsigned long vm_pte; /* shared mem */
};
@@ -118,7 +118,7 @@ typedef struct page {
unsigned long offset;
struct page *next_hash;
atomic_t count;
- unsigned flags; /* atomic flags, some possibly updated asynchronously */
+ unsigned long flags; /* atomic flags, some possibly updated asynchronously */
unsigned dirty:16,
age:8;
struct wait_queue *wait;
diff --git a/include/linux/msdos_fs.h b/include/linux/msdos_fs.h
index 36ed2a890..0badd9ebe 100644
--- a/include/linux/msdos_fs.h
+++ b/include/linux/msdos_fs.h
@@ -215,7 +215,7 @@ extern void fat_put_super(struct super_block *sb);
extern void fat_read_inode(struct inode *inode, struct inode_operations *dir_ops);
extern struct super_block *fat_read_super(struct super_block *s, void *data, int silent);
extern void msdos_put_super(struct super_block *sb);
-extern void fat_statfs(struct super_block *sb,struct statfs *buf, int);
+extern int fat_statfs(struct super_block *sb,struct statfs *buf, int);
extern void fat_write_inode(struct inode *inode);
/* dir.c */
diff --git a/include/linux/net.h b/include/linux/net.h
index bd30d7804..82a4b7570 100644
--- a/include/linux/net.h
+++ b/include/linux/net.h
@@ -136,5 +136,8 @@ extern int sock_sendmsg(struct socket *, struct msghdr *m, int len);
extern int sock_recvmsg(struct socket *, struct msghdr *m, int len, int flags);
extern int sock_readv_writev(int type, struct inode * inode, struct file * file,
const struct iovec * iov, long count, long size);
+
+int net_ratelimit(void);
+
#endif /* __KERNEL__ */
#endif /* _LINUX_NET_H */
diff --git a/include/linux/nfsd/nfsfh.h b/include/linux/nfsd/nfsfh.h
index 489762a36..75c2c91b2 100644
--- a/include/linux/nfsd/nfsfh.h
+++ b/include/linux/nfsd/nfsfh.h
@@ -57,7 +57,7 @@ struct knfs_fh {
typedef struct svc_fh {
struct knfs_fh fh_handle; /* FH data */
struct svc_export * fh_export; /* export pointer */
- struct inode * fh_inode; /* inode */
+ struct dentry * fh_dentry; /* file */
size_t fh_pre_size; /* size before operation */
time_t fh_pre_mtime; /* mtime before oper */
time_t fh_pre_ctime; /* ctime before oper */
@@ -98,7 +98,7 @@ fh_init(struct svc_fh *fhp)
static inline void
fh_lock(struct svc_fh *fhp)
{
- struct inode *inode = fhp->fh_inode;
+ struct inode *inode = fhp->fh_dentry->d_inode;
/*
dfprintk(FILEOP, "nfsd: fh_lock(%x/%ld) locked = %d\n",
@@ -118,7 +118,7 @@ fh_lock(struct svc_fh *fhp)
static inline void
fh_unlock(struct svc_fh *fhp)
{
- struct inode *inode = fhp->fh_inode;
+ struct inode *inode = fhp->fh_dentry->d_inode;
if (fhp->fh_locked) {
if (!fhp->fh_post_version)
@@ -135,9 +135,9 @@ fh_unlock(struct svc_fh *fhp)
static inline void
fh_put(struct svc_fh *fhp)
{
- if (fhp->fh_inode) {
+ if (fhp->fh_dentry) {
fh_unlock(fhp);
- iput(fhp->fh_inode);
+ dput(fhp->fh_dentry);
}
}
#else
@@ -146,19 +146,19 @@ fh_put(struct svc_fh *fhp)
static inline void
__fh_put(struct svc_fh *fhp, char *file, int line)
{
- struct inode *inode;
+ struct dentry *dentry;
- if (!(inode = fhp->fh_inode))
+ if (!(dentry = fhp->fh_dentry))
return;
- if (!atomic_read(&inode->i_count)) {
- printk("nfsd: trying to free free inode in %s:%d\n"
- " dev %04x ino %ld, mode %07o\n",
- file, line, inode->i_dev,
- inode->i_ino, inode->i_mode);
+ if (!dentry->d_count) {
+ printk("nfsd: trying to free free dentry in %s:%d\n"
+ " file %s/%s\n",
+ file, line,
+ dentry->d_parent->d_name.name, dentry->d_name.name);
} else {
fh_unlock(fhp);
- iput(inode);
+ dput(dentry);
}
}
#endif
diff --git a/include/linux/omirr.h b/include/linux/omirr.h
deleted file mode 100644
index 379867423..000000000
--- a/include/linux/omirr.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * fs/proc/omirr.c - online mirror support
- *
- * (C) 1997 Thomas Schoebel-Theuer
- */
-
-#ifndef OMIRR_H
-#define OMIRR_H
-#include <linux/fs.h>
-#include <linux/dalloc.h>
-
-extern int omirr_print(struct dentry * ent1, struct dentry * ent2,
- struct qstr * suffix, const char * fmt, ...);
-
-extern int omirr_printall(struct inode * inode, const char * fmt, ...);
-
-#endif
diff --git a/include/linux/pci.h b/include/linux/pci.h
index b13929d6a..138e67bfe 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -497,7 +497,7 @@
#define PCI_DEVICE_ID_VIA_82C586_1 0x0571
#define PCI_DEVICE_ID_VIA_82C576 0x0576
#define PCI_DEVICE_ID_VIA_82C585 0x0585
-#define PCI_DEVICE_ID_VIA_82C586_0 0x0586
+#define PCI_DEVICE_ID_VIA_82C586 0x0586
#define PCI_DEVICE_ID_VIA_82C416 0x1571
#define PCI_VENDOR_ID_VORTEX 0x1119
diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h
index 21b39d628..3a847d72c 100644
--- a/include/linux/pipe_fs_i.h
+++ b/include/linux/pipe_fs_i.h
@@ -5,7 +5,6 @@ struct pipe_inode_info {
struct wait_queue * wait;
char * base;
unsigned int start;
- unsigned int len;
unsigned int lock;
unsigned int rd_openers;
unsigned int wr_openers;
@@ -16,7 +15,7 @@ struct pipe_inode_info {
#define PIPE_WAIT(inode) ((inode).u.pipe_i.wait)
#define PIPE_BASE(inode) ((inode).u.pipe_i.base)
#define PIPE_START(inode) ((inode).u.pipe_i.start)
-#define PIPE_LEN(inode) ((inode).u.pipe_i.len)
+#define PIPE_LEN(inode) ((inode).i_size)
#define PIPE_RD_OPENERS(inode) ((inode).u.pipe_i.rd_openers)
#define PIPE_WR_OPENERS(inode) ((inode).u.pipe_i.wr_openers)
#define PIPE_READERS(inode) ((inode).u.pipe_i.readers)
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h
index b0e33a0c6..80cdf71a7 100644
--- a/include/linux/proc_fs.h
+++ b/include/linux/proc_fs.h
@@ -310,10 +310,11 @@ static inline int proc_scsi_unregister(struct proc_dir_entry *driver, int x)
extern struct super_block *proc_read_super(struct super_block *,void *,int);
extern int init_proc_fs(void);
extern struct inode * proc_get_inode(struct super_block *, int, struct proc_dir_entry *);
-extern void proc_statfs(struct super_block *, struct statfs *, int);
+extern int 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
@@ -323,7 +324,7 @@ extern int proc_match(int, const char *, struct proc_dir_entry *);
* of the /proc/<pid> 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 dentry *);
struct openpromfs_dev {
struct openpromfs_dev *next;
@@ -335,7 +336,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);
@@ -363,9 +364,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/random.h b/include/linux/random.h
index b2706ce03..7be5e9898 100644
--- a/include/linux/random.h
+++ b/include/linux/random.h
@@ -55,6 +55,8 @@ extern void get_random_bytes(void *buf, int nbytes);
extern __u32 secure_tcp_sequence_number(__u32 saddr, __u32 daddr,
__u16 sport, __u16 dport);
+extern __u32 secure_tcp_syn_cookie(__u32 saddr, __u32 daddr,
+ __u16 sport, __u16 dport, __u32 sseq, __u32 count);
#ifndef MODULE
extern struct file_operations random_fops, urandom_fops;
diff --git a/include/linux/rose.h b/include/linux/rose.h
index 2ca68dbaa..cd82c95c3 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 189194a49..361498ead 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 { \
@@ -519,12 +519,10 @@ extern inline void __remove_wait_queue(struct wait_queue ** p, struct wait_queue
{
struct wait_queue * next = wait->next;
struct wait_queue * head = next;
+ struct wait_queue * tmp;
- for (;;) {
- struct wait_queue * nextlist = head->next;
- if (nextlist == wait)
- break;
- head = nextlist;
+ while ((tmp = head->next) != wait) {
+ head = tmp;
}
head->next = next;
}
diff --git a/include/linux/sdla_fr.h b/include/linux/sdla_fr.h
index bcca8d58d..c66ee0ec3 100644
--- a/include/linux/sdla_fr.h
+++ b/include/linux/sdla_fr.h
@@ -281,7 +281,7 @@ typedef struct fr_dlc_conf
} fr_dlc_conf_t;
/*----------------------------------------------------------------------------
- * S502 Interrupt mode control block.
+ * S502 interrupt mode control block.
* This structure is passed to the FR_SET_INTR_FLAGS and returned by the
* FR_READ_INTR_FLAGS commands.
*/
@@ -292,7 +292,7 @@ typedef struct fr502_intr_ctl
} fr502_intr_ctl_t;
/*----------------------------------------------------------------------------
- * S508 Interrupt mode control block.
+ * S508 interrupt mode control block.
* This structure is passed to the FR_SET_INTR_FLAGS and returned by the
* FR_READ_INTR_FLAGS commands.
*/
@@ -306,10 +306,10 @@ typedef struct fr508_intr_ctl
} fr508_intr_ctl_t;
/*----------------------------------------------------------------------------
- * Channel Status.
+ * Channel status.
* This structure is returned by the FR_READ_STATUS command.
*/
-typedef struct frDLCStatus
+typedef struct fr_dlc_Status
{
unsigned char status PACKED; /* 00h: link/DLCI status */
struct
@@ -317,7 +317,7 @@ typedef struct frDLCStatus
unsigned short dlci PACKED; /* 01h: DLCI number */
unsigned char status PACKED; /* 03h: DLCI status */
} circuit[1] PACKED;
-} frDLCStatus_t;
+} fr_dlc_status_t;
/* 'status' defines */
#define FR_LINK_INOPER 0x00 /* for global status (DLCI == 0) */
@@ -333,7 +333,7 @@ typedef struct frDLCStatus
* This structure is returned by the FR_READ_STATISTICS command when
* dcli == 0.
*/
-typedef struct frLinkStat
+typedef struct fr_link_stat
{
unsigned short rx_too_long PACKED; /* 00h: */
unsigned short rx_dropped PACKED; /* 02h: */
@@ -363,14 +363,14 @@ typedef struct frLinkStat
unsigned short current_T392 PACKED; /* 32h: */
unsigned short current_N392 PACKED; /* 34h: */
unsigned short current_N393 PACKED; /* 36h: */
-} frLinkStat_t;
+} fr_link_stat_t;
/*----------------------------------------------------------------------------
- * DLCI Statistics.
+ * DLCI statistics.
* This structure is returned by the FR_READ_STATISTICS command when
* dlci != 0.
*/
-typedef struct frDLCIStat
+typedef struct fr_dlci_stat
{
unsigned long tx_frames PACKED; /* 00h: */
unsigned long tx_bytes PACKED; /* 04h: */
@@ -384,13 +384,13 @@ typedef struct frDLCIStat
unsigned long tx_calc_timer PACKED; /* 24h: */
unsigned long rx_throughput PACKED; /* 28h: */
unsigned long rx_calc_timer PACKED; /* 2Ch: */
-} frDLCIStat_t;
+} fr_dlci_stat_t;
/*----------------------------------------------------------------------------
- * Communications Error Statistics.
+ * Communications error statistics.
* This structure is returned by the FR_READ_ERROR_STATS command.
*/
-typedef struct frCommStat
+typedef struct fr_comm_stat
{
unsigned char rx_overruns PACKED; /* 00h: */
unsigned char rx_bad_crc PACKED; /* 01h: */
@@ -401,7 +401,7 @@ typedef struct frCommStat
unsigned char tx_missed_undr PACKED; /* 06h: */
unsigned char dcd_dropped PACKED; /* 07h: */
unsigned char cts_dropped PACKED; /* 08h: */
-} frCommStat_t;
+} fr_comm_stat_t;
/*----------------------------------------------------------------------------
* Defines for the FR_ISSUE_IS_FRAME command.
diff --git a/include/linux/sdla_x25.h b/include/linux/sdla_x25.h
index 424e9fe6a..0cff58224 100644
--- a/include/linux/sdla_x25.h
+++ b/include/linux/sdla_x25.h
@@ -480,7 +480,7 @@ typedef struct X25Stats
unsigned short rxRR PACKED; /* 3Ah: RR */
unsigned short txRNR PACKED; /* 3Ch: RNR */
unsigned short rxRNR PACKED; /* 3Eh: RNR */
-} X25Stats;
+} TX25Stats;
/*----------------------------------------------------------------------------
* X25_READ_HISTORY_TABLE Command.
diff --git a/include/linux/selection.h b/include/linux/selection.h
index d6f1248f0..cad2a29b0 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 <linux/config.h>
+
#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 000000000..bf838f815
--- /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,
+ 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 08be13221..decb1e747 100644
--- a/include/linux/slab.h
+++ b/include/linux/slab.h
@@ -32,7 +32,7 @@ typedef struct kmem_cache_s kmem_cache_t;
#define SLAB_DEBUG_FREE 0x00000100UL /* Peform (expensive) checks on free */
#define SLAB_DEBUG_INITIAL 0x00000200UL /* Call constructor (as verifier) */
#define SLAB_RED_ZONE 0x00000400UL /* Red zone objs in a cache */
-#define SLAB_POISION 0x00000800UL /* Poision objects */
+#define SLAB_POISON 0x00000800UL /* Poison objects */
#define SLAB_NO_REAP 0x00001000UL /* never reap from the cache */
#define SLAB_HWCACHE_ALIGN 0x00002000UL /* align objs on a h/w cache lines */
#if 0
@@ -56,8 +56,8 @@ 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_s(void *, size_t);
+extern void kfree(const void *);
+extern void kfree_s(const void *, size_t);
extern int kmem_cache_reap(int, int, int);
extern int get_slabinfo(char *);
diff --git a/include/linux/swap.h b/include/linux/swap.h
index 56fef7a21..7faaccf75 100644
--- a/include/linux/swap.h
+++ b/include/linux/swap.h
@@ -19,8 +19,7 @@
struct swap_info_struct {
unsigned int flags;
kdev_t swap_device;
- char *swap_filename;
- struct inode * swap_file;
+ struct dentry * swap_file;
unsigned char * swap_map;
unsigned char * swap_lockmap;
unsigned int lowest_bit;
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index 2ec41aa05..75fdf6c3d 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -173,6 +173,7 @@ enum
NET_IPV4_IGMP_AGE_THRESHOLD,
NET_TCP_SYNCOOKIES,
NET_TCP_ALWAYS_SYNCOOKIE,
+ NET_TCP_STDURG,
};
diff --git a/include/linux/sysv_fs.h b/include/linux/sysv_fs.h
index e68f5d5f1..53ead0c99 100644
--- a/include/linux/sysv_fs.h
+++ b/include/linux/sysv_fs.h
@@ -372,8 +372,8 @@ extern int sysv_symlink(struct inode * inode, const char * name, int len,
const char * symname);
extern int sysv_link(struct inode * oldinode, struct inode * dir, const char * name, int len);
extern int sysv_mknod(struct inode * dir, const char * name, int len, int mode, int rdev);
-extern int sysv_rename(struct inode * old_dir, const char * old_name, int old_len,
- struct inode * new_dir, const char * new_name, int new_len);
+extern int sysv_rename(struct inode * old_dir, struct dentry * old_dentry,
+ struct inode * new_dir, struct dentry * new_dentry);
extern struct inode * sysv_new_inode(const struct inode * dir);
extern void sysv_free_inode(struct inode * inode);
extern unsigned long sysv_count_free_inodes(struct super_block *sb);
@@ -396,7 +396,7 @@ extern void sysv_read_inode(struct inode *);
extern int sysv_notify_change(struct inode *, struct iattr *);
extern void sysv_write_inode(struct inode *);
extern void sysv_put_inode(struct inode *);
-extern void sysv_statfs(struct super_block *, struct statfs *, int);
+extern int sysv_statfs(struct super_block *, struct statfs *, int);
extern int sysv_sync_inode(struct inode *);
extern int sysv_sync_file(struct inode *, struct file *);
extern int sysv_mmap(struct inode *, struct file *, struct vm_area_struct *);
diff --git a/include/linux/tqueue.h b/include/linux/tqueue.h
index de88d20d1..a2e1cb369 100644
--- a/include/linux/tqueue.h
+++ b/include/linux/tqueue.h
@@ -42,7 +42,7 @@
struct tq_struct {
struct tq_struct *next; /* linked list of active bh's */
- int sync; /* must be initialized to zero */
+ unsigned long sync; /* must be initialized to zero */
void (*routine)(void *); /* function to call */
void *data; /* argument to function */
};
diff --git a/include/linux/tty.h b/include/linux/tty.h
index 109955a8d..589fc6b88 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -96,13 +96,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
@@ -204,7 +210,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/ufs_fs.h b/include/linux/ufs_fs.h
index af856645d..3afea01ad 100644
--- a/include/linux/ufs_fs.h
+++ b/include/linux/ufs_fs.h
@@ -6,7 +6,7 @@
* Laboratory for Computer Science Research Computing Facility
* Rutgers, The State University of New Jersey
*
- * $Id: ufs_fs.h,v 1.7 1996/08/13 19:27:59 ecd Exp $
+ * $Id: ufs_fs.h,v 1.1.1.1 1997/06/01 03:17:06 ralf Exp $
*
*/
@@ -225,7 +225,7 @@ extern void ufs_put_inode(struct inode * inode);
extern void ufs_print_inode (struct inode *);
/* ufs_namei.c */
-extern int ufs_lookup (struct inode *, const char *, int, struct inode **);
+extern int ufs_lookup (struct inode *, struct qstr *, struct inode **);
/* ufs_super.c */
extern void ufs_warning (struct super_block *, const char *, const char *, ...)
diff --git a/include/linux/vmalloc.h b/include/linux/vmalloc.h
index fcc6ad09f..40072ab47 100644
--- a/include/linux/vmalloc.h
+++ b/include/linux/vmalloc.h
@@ -17,6 +17,8 @@ struct vm_struct * get_vm_area(unsigned long size);
void vfree(void * addr);
void * vmalloc(unsigned long size);
int vread(char *buf, char *addr, int count);
+void vmfree_area_pages(unsigned long address, unsigned long size);
+int vmalloc_area_pages(unsigned long address, unsigned long size);
extern inline void set_pgdir(unsigned long address, pgd_t entry)
{
@@ -34,3 +36,4 @@ extern inline void set_pgdir(unsigned long address, pgd_t entry)
}
#endif
+
diff --git a/include/linux/wanpipe.h b/include/linux/wanpipe.h
index f36642161..922a76961 100644
--- a/include/linux/wanpipe.h
+++ b/include/linux/wanpipe.h
@@ -11,6 +11,8 @@
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
* ============================================================================
+* Jan 15, 1997 Gene Kozin Version 3.1.0
+* o added UDP management stuff
* Jan 02, 1997 Gene Kozin Version 3.0.0
*****************************************************************************/
#ifndef _WANPIPE_H
@@ -19,6 +21,11 @@
#include <linux/wanrouter.h>
/* Defines */
+
+#ifndef PACKED
+#define PACKED __attribute__((packed))
+#endif
+
#define WANPIPE_MAGIC 0x414C4453L /* signatire: 'SDLA' reversed */
/* IOCTL numbers (up to 16) */
@@ -44,6 +51,22 @@ typedef struct sdla_exec /* WANPIPE_EXEC */
void* data; /* -> data buffer */
} sdla_exec_t;
+/* UDP management stuff */
+
+typedef struct wum_header
+{
+ unsigned char signature[8]; /* 00h: signature */
+ unsigned char type; /* 08h: request/reply */
+ unsigned char command; /* 09h: commnand */
+ unsigned char reserved[6]; /* 0Ah: reserved */
+} wum_header_t;
+
+#define WUM_SIGNATURE_L 0x50495046
+#define WUM_SIGNATURE_H 0x444E3845
+
+#define WUM_KILL 0x50
+#define WUM_EXEC 0x51
+
#ifdef __KERNEL__
/****** Kernel Interface ****************************************************/
@@ -77,6 +100,7 @@ typedef struct sdla
wan_device_t wandev; /* WAN device data space */
unsigned open_cnt; /* number of open interfaces */
unsigned long state_tick; /* link state timestamp */
+/* unsigned tx_int_enabled; */ /* tranmit interrupt enabled or not */
char in_isr; /* interrupt-in-service flag */
void* mbox; /* -> mailbox */
void* rxmb; /* -> receive mailbox */
diff --git a/include/linux/wanrouter.h b/include/linux/wanrouter.h
index 444608afb..accb9c9ee 100644
--- a/include/linux/wanrouter.h
+++ b/include/linux/wanrouter.h
@@ -1,5 +1,5 @@
/*****************************************************************************
-* wanrouter.h Definitions for the WAN Multiprotocol Router Module.
+* router.h Definitions for the WAN Multiprotocol Router Module.
* This module provides API and common services for WAN Link
* Drivers and is completely hardware-independent.
*
@@ -12,6 +12,10 @@
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
* ============================================================================
+* May 29, 1997 Jaspreet Singh Added 'tx_int_enabled' tp 'wan_device_t'
+* May 21, 1997 Jaspreet Singh Added 'udp_port' to 'wan_device_t'
+* Apr 25, 1997 Farhan Thawar Added 'udp_port' to 'wandev_conf_t'
+* Jan 16, 1997 Gene Kozin router_devlist made public
* Jan 02, 1997 Gene Kozin Initial version (based on wanpipe.h).
*****************************************************************************/
#ifndef _ROUTER_H
@@ -128,7 +132,8 @@ typedef struct wandev_conf
int dma; /* DMA request level */
unsigned bps; /* data transfer rate */
unsigned mtu; /* maximum transmit unit size */
- char interface; /* RS-232/V.35, etc. */
+ unsigned udp_port; /* UDP port for management */
+ char interface; /* RS-232/V.35, etc. */
char clocking; /* external/internal */
char line_coding; /* NRZ/NRZI/FM0/FM1, etc. */
char station; /* DTE/DCE, primary/secondary, etc. */
@@ -283,6 +288,8 @@ typedef struct wan_device
int dma; /* DMA request level */
unsigned bps; /* data transfer rate */
unsigned mtu; /* max physical transmit unit size */
+ unsigned udp_port; /* UDP port for management */
+ unsigned tx_int_enabled; /* Transmit Interrupt enabled or not */
char interface; /* RS-232/V.35, etc. */
char clocking; /* external/internal */
char line_coding; /* NRZ/NRZI/FM0/FM1, etc. */
@@ -311,22 +318,24 @@ typedef struct wan_device
struct proc_dir_entry dent; /* proc filesystem entry */
} wan_device_t;
-/* Init Point */
-extern void wanrouter_init(void);
-
/* Public functions available for device drivers */
-extern int register_wan_device (wan_device_t* wandev);
-extern int unregister_wan_device (char* name);
-unsigned short wanrouter_type_trans (struct sk_buff* skb, struct device* dev);
-int wanrouter_encapsulate (struct sk_buff* skb, struct device* dev);
+extern int register_wandev (wan_device_t* wandev);
+extern int unregister_wandev (char* name);
+unsigned short wan_type_trans (struct sk_buff* skb, struct device* dev);
+int wan_encapsulate (struct sk_buff* skb, struct device* dev);
/* Proc interface functions. These must not be called by the drivers! */
extern int wanrouter_proc_init (void);
extern void wanrouter_proc_cleanup (void);
extern int wanrouter_proc_add (wan_device_t* wandev);
extern int wanrouter_proc_delete (wan_device_t* wandev);
-extern int wanrouter_ioctl(struct inode* inode, struct file* file,
- unsigned int cmd, unsigned long arg);
+extern int wanrouter_ioctl(
+ struct inode* inode, struct file* file,
+ unsigned int cmd, unsigned long arg)
+;
+
+/* Public Data */
+extern wan_device_t* router_devlist; /* list of registered devices */
#endif /* __KERNEL__ */
#endif /* _ROUTER_H */
diff --git a/include/linux/wrapper.h b/include/linux/wrapper.h
index 750b97084..2d6d4719f 100644
--- a/include/linux/wrapper.h
+++ b/include/linux/wrapper.h
@@ -20,9 +20,9 @@
#define module_unregister_blkdev unregister_blkdev
#define inode_get_rdev(i) i->i_rdev
-#define inode_get_count(i) atomic_read(&((i)->i_count))
-#define inode_inc_count(i) atomic_inc(&((i)->i_count))
-#define inode_dec_count(i) atomic_dec(&((i)->i_count))
+#define inode_get_count(i) i->i_count
+#define inode_inc_count(i) i->i_count++
+#define inode_dec_count(i) i->i_count--
#define file_get_flags(f) f->f_flags
diff --git a/include/linux/x25.h b/include/linux/x25.h
index 6af8e9a5b..0435e9701 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.
@@ -33,42 +34,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 9e6f0df11..fd25e9f7f 100644
--- a/include/net/ax25.h
+++ b/include/net/ax25.h
@@ -9,10 +9,8 @@
#include <linux/config.h>
#include <linux/ax25.h>
-#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 <net/ax25call.h>
/* 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 f06583cee..7cc9b3452 100644
--- a/include/net/lapb.h
+++ b/include/net/lapb.h
@@ -2,8 +2,6 @@
#define _LAPB_H
#include <linux/lapb.h>
-#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 8a255660a..cc9fc842c 100644
--- a/include/net/netrom.h
+++ b/include/net/netrom.h
@@ -8,8 +8,6 @@
#define _NETROM_H
#include <linux/netrom.h>
-#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 1e60cfa8f..86f6a6721 100644
--- a/include/net/rose.h
+++ b/include/net/rose.h
@@ -8,8 +8,6 @@
#define _ROSE_H
#include <linux/rose.h>
-#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/tcp.h b/include/net/tcp.h
index cedc7f3b1..d0f812e4d 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -281,15 +281,17 @@ struct tcp_func {
int (*conn_request) (struct sock *sk,
struct sk_buff *skb,
- void *opt,
- __u32 isn);
+ void *opt, __u32 isn);
struct sock * (*syn_recv_sock) (struct sock *sk,
struct sk_buff *skb,
- struct open_request *req);
+ struct open_request *req,
+ struct dst_entry *dst);
+#if 0
__u32 (*init_sequence) (struct sock *sk,
struct sk_buff *skb);
+#endif
struct sock * (*get_sock) (struct sk_buff *skb,
struct tcphdr *th);
@@ -385,7 +387,8 @@ extern int tcp_recvmsg(struct sock *sk,
int len, int nonblock,
int flags, int *addr_len);
-extern void tcp_parse_options(struct tcphdr *th, struct tcp_opt *tp);
+extern void tcp_parse_options(struct tcphdr *th, struct tcp_opt *tp,
+ int no_fancy);
/*
* TCP v4 functions exported for the inet6 API
@@ -407,7 +410,8 @@ extern int tcp_v4_conn_request(struct sock *sk,
extern struct sock * tcp_v4_syn_recv_sock(struct sock *sk,
struct sk_buff *skb,
- struct open_request *req);
+ struct open_request *req,
+ struct dst_entry *dst);
extern int tcp_v4_do_rcv(struct sock *sk,
struct sk_buff *skb);
@@ -417,6 +421,12 @@ extern int tcp_v4_connect(struct sock *sk,
int addr_len);
+/* From syncookies.c */
+extern struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
+ struct ip_options *opt);
+extern __u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb,
+ __u16 *mss);
+
extern void tcp_read_wakeup(struct sock *);
extern void tcp_write_xmit(struct sock *);
extern void tcp_time_wait(struct sock *);
@@ -522,7 +532,6 @@ static __inline__ u16 tcp_v4_check(struct tcphdr *th, int len,
return csum_tcpudp_magic(saddr,daddr,len,IPPROTO_TCP,base);
}
-
#undef STATE_TRACE
#ifdef STATE_TRACE
diff --git a/include/net/x25.h b/include/net/x25.h
index cb1ae8bee..7b58ad4e3 100644
--- a/include/net/x25.h
+++ b/include/net/x25.h
@@ -8,8 +8,6 @@
#define _X25_H
#include <linux/x25.h>
-#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/init/main.c b/init/main.c
index a0d15926e..5366c2e06 100644
--- a/init/main.c
+++ b/init/main.c
@@ -32,7 +32,6 @@
#include <linux/slab.h>
#include <linux/major.h>
#include <linux/blk.h>
-#include <linux/nametrans.h>
#include <linux/init.h>
#ifdef CONFIG_ROOT_NFS
#include <linux/nfs_fs.h>
@@ -570,12 +569,6 @@ __initfunc(static int checksetup(char *line))
return 1;
}
#endif
-#ifdef CONFIG_TRANS_NAMES
- if(!strncmp(line,"nametrans=",10)) {
- nametrans_setup(line+10);
- return 1;
- }
-#endif
while (bootsetups[i].str) {
int n = strlen(bootsetups[i].str);
if (!strncmp(line,bootsetups[i].str,n)) {
@@ -769,6 +762,12 @@ __initfunc(static void parse_options(char *line))
if (!strncmp(line,"init=",5)) {
line += 5;
execute_command = line;
+ /* In case LILO is going to boot us with default command line,
+ * it prepends "auto" before the whole cmdline which makes
+ * the shell think it should execute a script with such name.
+ * So we ignore all arguments entered _before_ init=... [MJ]
+ */
+ args = 0;
continue;
}
if (checksetup(line))
@@ -901,7 +900,6 @@ __initfunc(asmlinkage void start_kernel(void))
memory_start = kmem_cache_init(memory_start, memory_end);
sti();
calibrate_delay();
- memory_start = name_cache_init(memory_start,memory_end);
#ifdef CONFIG_BLK_DEV_INITRD
if (initrd_start && !initrd_below_start_ok && initrd_start < memory_start) {
printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - "
@@ -916,6 +914,7 @@ __initfunc(asmlinkage void start_kernel(void))
#endif
uidcache_init();
filescache_init();
+ dcache_init();
vma_init();
buffer_init();
inode_init();
diff --git a/ipc/shm.c b/ipc/shm.c
index d2afb7d2f..b306922ec 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -555,7 +555,7 @@ asmlinkage int sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr)
shmd->vm_flags = VM_SHM | VM_MAYSHARE | VM_SHARED
| VM_MAYREAD | VM_MAYEXEC | VM_READ | VM_EXEC
| ((shmflg & SHM_RDONLY) ? 0 : VM_MAYWRITE | VM_WRITE);
- shmd->vm_inode = NULL;
+ shmd->vm_dentry = NULL;
shmd->vm_offset = 0;
shmd->vm_ops = &shm_vm_ops;
diff --git a/kernel/exit.c b/kernel/exit.c
index f6e8fb9b1..9a725e9c8 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -369,8 +369,11 @@ static inline void close_files(struct files_struct * files)
break;
while (set) {
if (set & 1) {
- close_fp(files->fd[i]);
- files->fd[i] = NULL;
+ struct file * file = files->fd[i];
+ if (file) {
+ files->fd[i] = NULL;
+ close_fp(file);
+ }
}
i++;
set >>= 1;
@@ -405,8 +408,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 c3bcf7cca..900d6015c 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -211,7 +211,7 @@ static inline int dup_mmap(struct mm_struct * mm)
flush_cache_mm(current->mm);
pprev = &mm->mmap;
for (mpnt = current->mm->mmap ; mpnt ; mpnt = mpnt->vm_next) {
- struct inode *inode;
+ struct dentry *dentry;
tmp = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
if (!tmp) {
@@ -223,11 +223,11 @@ static inline int dup_mmap(struct mm_struct * mm)
tmp->vm_flags &= ~VM_LOCKED;
tmp->vm_mm = mm;
tmp->vm_next = NULL;
- inode = tmp->vm_inode;
- if (inode) {
- atomic_inc(&inode->i_count);
+ dentry = tmp->vm_dentry;
+ if (dentry) {
+ dentry->d_count++;
if (tmp->vm_flags & VM_DENYWRITE)
- inode->i_writecount--;
+ dentry->d_inode->i_writecount--;
/* insert tmp into the share list, just after mpnt */
if((tmp->vm_next_share = mpnt->vm_next_share) != NULL)
@@ -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 8e5607e1e..fff97a75a 100644
--- a/kernel/ksyms.c
+++ b/kernel/ksyms.c
@@ -141,12 +141,19 @@ EXPORT_SYMBOL(update_vm_cache);
EXPORT_SYMBOL(getname);
EXPORT_SYMBOL(putname);
EXPORT_SYMBOL(__fput);
-EXPORT_SYMBOL(__iget);
-EXPORT_SYMBOL(_iput);
-EXPORT_SYMBOL(namei);
+EXPORT_SYMBOL(iget);
+EXPORT_SYMBOL(iput);
+EXPORT_SYMBOL(__namei);
+EXPORT_SYMBOL(lookup_dentry);
EXPORT_SYMBOL(open_namei);
EXPORT_SYMBOL(sys_close);
EXPORT_SYMBOL(close_fp);
+EXPORT_SYMBOL(d_alloc_root);
+EXPORT_SYMBOL(d_delete);
+EXPORT_SYMBOL(d_add);
+EXPORT_SYMBOL(d_move);
+EXPORT_SYMBOL(d_instantiate);
+EXPORT_SYMBOL(__mark_inode_dirty);
EXPORT_SYMBOL(insert_file_free);
EXPORT_SYMBOL(check_disk_change);
EXPORT_SYMBOL(invalidate_buffers);
@@ -177,6 +184,7 @@ EXPORT_SYMBOL(posix_lock_file);
EXPORT_SYMBOL(posix_test_lock);
EXPORT_SYMBOL(posix_block_lock);
EXPORT_SYMBOL(posix_unblock_lock);
+EXPORT_SYMBOL(dput);
#if !defined(CONFIG_NFSD) && defined(CONFIG_NFSD_MODULE)
EXPORT_SYMBOL(do_nfsservctl);
@@ -329,7 +337,7 @@ EXPORT_SYMBOL(setup_arg_pages);
EXPORT_SYMBOL(copy_strings);
EXPORT_SYMBOL(do_execve);
EXPORT_SYMBOL(flush_old_exec);
-EXPORT_SYMBOL(open_inode);
+EXPORT_SYMBOL(open_dentry);
EXPORT_SYMBOL(read_exec);
/* Miscellaneous access points */
@@ -341,7 +349,7 @@ EXPORT_SYMBOL(set_writetime);
EXPORT_SYMBOL(sys_tz);
EXPORT_SYMBOL(__wait_on_super);
EXPORT_SYMBOL(file_fsync);
-EXPORT_SYMBOL(_clear_inode);
+EXPORT_SYMBOL(clear_inode);
EXPORT_SYMBOL(refile_buffer);
EXPORT_SYMBOL(nr_async_pages);
EXPORT_SYMBOL(___strtok);
@@ -352,7 +360,7 @@ EXPORT_SYMBOL(chrdev_inode_operations);
EXPORT_SYMBOL(blkdev_inode_operations);
EXPORT_SYMBOL(read_ahead);
EXPORT_SYMBOL(get_hash_table);
-EXPORT_SYMBOL(_get_empty_inode);
+EXPORT_SYMBOL(get_empty_inode);
EXPORT_SYMBOL(insert_inode_hash);
EXPORT_SYMBOL(event);
EXPORT_SYMBOL(__down);
diff --git a/kernel/module.c b/kernel/module.c
index 885539b5c..c584eb3ae 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/sys.c b/kernel/sys.c
index ca3d17807..27d41eed1 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -21,7 +21,6 @@
#include <linux/fcntl.h>
#include <linux/acct.h>
#include <linux/tty.h>
-#include <linux/nametrans.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/notifier.h>
@@ -397,10 +396,8 @@ int acct_process(long exitcode)
fs = get_fs();
set_fs(KERNEL_DS);
- acct_file.f_op->write(acct_file.f_inode, &acct_file,
+ acct_file.f_op->write(acct_file.f_dentry->d_inode, &acct_file,
(char *)&ac, sizeof(struct acct));
- /* inode->i_status |= ST_MODIFIED is willingly *not* done here */
-
set_fs(fs);
}
return 0;
@@ -408,8 +405,6 @@ int acct_process(long exitcode)
asmlinkage int sys_acct(const char *name)
{
- struct inode *inode = (struct inode *)0;
- char *tmp;
int error = -EPERM;
lock_kernel();
@@ -419,10 +414,10 @@ asmlinkage int sys_acct(const char *name)
if (name == (char *)0) {
if (acct_active) {
if (acct_file.f_op->release)
- acct_file.f_op->release(acct_file.f_inode, &acct_file);
+ acct_file.f_op->release(acct_file.f_dentry->d_inode, &acct_file);
- if (acct_file.f_inode != (struct inode *) 0)
- iput(acct_file.f_inode);
+ if (acct_file.f_dentry != NULL)
+ dput(acct_file.f_dentry);
acct_active = 0;
}
@@ -430,40 +425,51 @@ asmlinkage int sys_acct(const char *name)
} else {
error = -EBUSY;
if (!acct_active) {
- if ((error = getname(name, &tmp)) != 0)
+ struct dentry *dentry;
+ struct inode *inode;
+ char *tmp;
+
+ tmp = getname(name);
+ error = PTR_ERR(tmp);
+ if (IS_ERR(tmp))
goto out;
- error = open_namei(tmp, O_RDWR, 0600, &inode, 0);
+ dentry = open_namei(tmp, O_RDWR, 0600);
putname(tmp);
- if (error)
+
+ error = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
goto out;
+ inode = dentry->d_inode;
error = -EACCES;
if (!S_ISREG(inode->i_mode)) {
- iput(inode);
+ dput(dentry);
goto out;
}
error = -EIO;
if (!inode->i_op || !inode->i_op->default_file_ops ||
!inode->i_op->default_file_ops->write) {
- iput(inode);
+ dput(dentry);
goto out;
}
acct_file.f_mode = 3;
acct_file.f_flags = 0;
acct_file.f_count = 1;
- acct_file.f_inode = inode;
+ acct_file.f_dentry = dentry;
acct_file.f_pos = inode->i_size;
acct_file.f_reada = 0;
acct_file.f_op = inode->i_op->default_file_ops;
- if(acct_file.f_op->open)
- if(acct_file.f_op->open(acct_file.f_inode, &acct_file)) {
- iput(inode);
+ if(acct_file.f_op->open) {
+ error = acct_file.f_op->open(inode, &acct_file);
+ if (error) {
+ dput(dentry);
goto out;
}
+ }
acct_active = 1;
error = 0;
@@ -612,21 +618,17 @@ asmlinkage int sys_setuid(uid_t uid)
*/
asmlinkage int sys_setresuid(uid_t ruid, uid_t euid, uid_t suid)
{
- uid_t old_ruid, old_euid, old_suid;
-
- old_ruid = current->uid;
- old_euid = current->euid;
- old_suid = current->suid;
-
- if ((ruid != (uid_t) -1) && (ruid != current->uid) &&
- (ruid != current->euid) && (ruid != current->suid))
- return -EPERM;
- if ((euid != (uid_t) -1) && (euid != current->uid) &&
- (euid != current->euid) && (euid != current->suid))
- return -EPERM;
- if ((suid != (uid_t) -1) && (suid != current->uid) &&
- (suid != current->euid) && (suid != current->suid))
- return -EPERM;
+ if (current->uid != 0 && current->euid != 0 && current->suid != 0) {
+ if ((ruid != (uid_t) -1) && (ruid != current->uid) &&
+ (ruid != current->euid) && (ruid != current->suid))
+ return -EPERM;
+ if ((euid != (uid_t) -1) && (euid != current->uid) &&
+ (euid != current->euid) && (euid != current->suid))
+ return -EPERM;
+ if ((suid != (uid_t) -1) && (suid != current->uid) &&
+ (suid != current->euid) && (suid != current->suid))
+ return -EPERM;
+ }
if (ruid != (uid_t) -1) {
/* See above commentary about NPROC rlimit issues here. */
charge_uid(current, -1);
@@ -634,8 +636,12 @@ asmlinkage int sys_setresuid(uid_t ruid, uid_t euid, uid_t suid)
if(ruid)
charge_uid(current, 1);
}
- if (euid != (uid_t) -1)
+ if (euid != (uid_t) -1) {
+ if (euid != current->euid)
+ current->dumpable = 0;
current->euid = euid;
+ current->fsuid = euid;
+ }
if (suid != (uid_t) -1)
current->suid = suid;
return 0;
@@ -652,6 +658,46 @@ asmlinkage int sys_getresuid(uid_t *ruid, uid_t *euid, uid_t *suid)
return retval;
}
+/*
+ * Same as above, but for rgid, egid, sgid.
+ */
+asmlinkage int sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid)
+{
+ if (current->uid != 0 && current->euid != 0 && current->suid != 0) {
+ if ((rgid != (gid_t) -1) && (rgid != current->gid) &&
+ (rgid != current->egid) && (rgid != current->sgid))
+ return -EPERM;
+ if ((egid != (gid_t) -1) && (egid != current->gid) &&
+ (egid != current->egid) && (egid != current->sgid))
+ return -EPERM;
+ if ((sgid != (gid_t) -1) && (sgid != current->gid) &&
+ (sgid != current->egid) && (sgid != current->sgid))
+ return -EPERM;
+ }
+ if (rgid != (gid_t) -1)
+ current->gid = rgid;
+ if (egid != (gid_t) -1) {
+ if (egid != current->egid)
+ current->dumpable = 0;
+ current->egid = egid;
+ current->fsgid = egid;
+ }
+ if (sgid != (gid_t) -1)
+ current->sgid = sgid;
+ return 0;
+}
+
+asmlinkage int sys_getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid)
+{
+ int retval;
+
+ if (!(retval = put_user(current->gid, rgid)) &&
+ !(retval = put_user(current->egid, egid)))
+ retval = put_user(current->sgid, sgid);
+
+ return retval;
+}
+
/*
* "setfsuid()" sets the fsuid - the uid used for filesystem checks. This
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index e4bdcfc1a..205190e9a 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -16,7 +16,6 @@
#include <linux/mm.h>
#include <linux/sysctl.h>
#include <linux/swapctl.h>
-#include <linux/nametrans.h>
#include <linux/proc_fs.h>
#include <linux/malloc.h>
#include <linux/stat.h>
@@ -103,6 +102,7 @@ struct inode_operations proc_sys_inode_operations =
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
@@ -171,10 +171,6 @@ static ctl_table kern_table[] = {
{KERN_JAVA_APPLETVIEWER, "java-appletviewer", binfmt_java_appletviewer,
64, 0644, NULL, &proc_dostring, &sysctl_string },
#endif
-#ifdef CONFIG_TRANS_NAMES
- {KERN_NAMETRANS, "nametrans", nametrans_txt, MAX_DEFAULT_TRANSLEN,
- 0644, NULL, &nametrans_dostring, &nametrans_string},
-#endif
#ifdef __sparc__
{KERN_SPARC_REBOOT, "reboot-cmd", reboot_command,
256, 0644, NULL, &proc_dostring, &sysctl_string },
@@ -822,6 +818,12 @@ int proc_dointvec_jiffies(ctl_table *table, int write, struct file *filp,
#else /* CONFIG_PROC_FS */
+int proc_dointvec_jiffies(ctl_table *table, int write, struct file *filp,
+ void *buffer, size_t *lenp)
+{
+ return -ENOSYS;
+}
+
int proc_dostring(ctl_table *table, int write, struct file *filp,
void *buffer, size_t *lenp)
{
@@ -840,6 +842,12 @@ int proc_dointvec_minmax(ctl_table *table, int write, struct file *filp,
return -ENOSYS;
}
+int proc_dointvec_jiffies(ctl_table *table, int write, struct file *filp,
+ void *buffer, size_t *lenp)
+{
+ return -ENOSYS;
+}
+
#endif /* CONFIG_PROC_FS */
@@ -1040,6 +1048,12 @@ int proc_dointvec_minmax(ctl_table *table, int write, struct file *filp,
return -ENOSYS;
}
+int proc_dointvec_jiffies(ctl_table *table, int write, struct file *filp,
+ void *buffer, size_t *lenp)
+{
+ return -ENOSYS;
+}
+
struct ctl_table_header * register_sysctl_table(ctl_table * table,
int insert_at_head)
{
diff --git a/mm/Makefile b/mm/Makefile
index c64eefbd2..ef3820d1c 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -9,7 +9,7 @@
O_TARGET := mm.o
O_OBJS := memory.o mmap.o filemap.o mprotect.o mlock.o mremap.o \
- vmalloc.o slab.o \
+ vmalloc.o slab.o simp.o\
swap.o vmscan.o page_io.o page_alloc.o swap_state.o swapfile.o
include $(TOPDIR)/Rules.make
diff --git a/mm/filemap.c b/mm/filemap.c
index 56aa1b486..8915c1096 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -753,10 +753,7 @@ page_read_error:
filp->f_reada = 1;
if (page_cache)
free_page(page_cache);
- if (DO_UPDATE_ATIME(inode)) {
- inode->i_atime = CURRENT_TIME;
- inode->i_dirt = 1;
- }
+ UPDATE_ATIME(inode)
if (!read)
read = error;
return read;
@@ -777,7 +774,7 @@ static unsigned long filemap_nopage(struct vm_area_struct * area, unsigned long
duplicate flushes. ... */
unsigned long offset;
struct page * page, **hash;
- struct inode * inode = area->vm_inode;
+ struct inode * inode = area->vm_dentry->d_inode;
unsigned long old_page, new_page;
new_page = 0;
@@ -921,7 +918,6 @@ static inline int do_write_page(struct inode * inode, struct file * file,
retval = -EIO;
if (size == file->f_op->write(inode, file, (const char *) page, size))
retval = 0;
- /* inode->i_status |= ST_MODIFIED is willingly *not* done here */
set_fs(old_fs);
return retval;
}
@@ -932,6 +928,7 @@ static int filemap_write_page(struct vm_area_struct * vma,
{
int result;
struct file file;
+ struct dentry * dentry;
struct inode * inode;
struct buffer_head * bh;
@@ -946,14 +943,15 @@ static int filemap_write_page(struct vm_area_struct * vma,
return 0;
}
- inode = vma->vm_inode;
+ dentry = vma->vm_dentry;
+ inode = dentry->d_inode;
file.f_op = inode->i_op->default_file_ops;
if (!file.f_op->write)
return -EIO;
file.f_mode = 3;
file.f_flags = 0;
file.f_count = 1;
- file.f_inode = inode;
+ file.f_dentry = dentry;
file.f_pos = offset;
file.f_reada = 0;
@@ -1191,12 +1189,8 @@ int generic_file_mmap(struct inode * inode, struct file * file, struct vm_area_s
return -EACCES;
if (!inode->i_op || !inode->i_op->readpage)
return -ENOEXEC;
- if (DO_UPDATE_ATIME(inode)) {
- inode->i_atime = CURRENT_TIME;
- inode->i_dirt = 1;
- }
- vma->vm_inode = inode;
- atomic_inc(&inode->i_count);
+ UPDATE_ATIME(inode);
+ vma->vm_dentry = dget(file->f_dentry);
vma->vm_ops = ops;
return 0;
}
@@ -1209,7 +1203,7 @@ int generic_file_mmap(struct inode * inode, struct file * file, struct vm_area_s
static int msync_interval(struct vm_area_struct * vma,
unsigned long start, unsigned long end, int flags)
{
- if (!vma->vm_inode)
+ if (!vma->vm_dentry)
return 0;
if (vma->vm_ops->sync) {
int error;
@@ -1217,7 +1211,7 @@ static int msync_interval(struct vm_area_struct * vma,
if (error)
return error;
if (flags & MS_SYNC)
- return file_fsync(vma->vm_inode, NULL);
+ return file_fsync(vma->vm_dentry->d_inode, NULL);
return 0;
}
return 0;
diff --git a/mm/mlock.c b/mm/mlock.c
index 5a69e4b55..eea100add 100644
--- a/mm/mlock.c
+++ b/mm/mlock.c
@@ -38,8 +38,7 @@ static inline int mlock_fixup_start(struct vm_area_struct * vma,
n->vm_end = end;
vma->vm_offset += vma->vm_start - n->vm_start;
n->vm_flags = newflags;
- if (n->vm_inode)
- atomic_inc(&n->vm_inode->i_count);
+ n->vm_dentry = dget(vma->vm_dentry);
if (n->vm_ops && n->vm_ops->open)
n->vm_ops->open(n);
insert_vm_struct(current->mm, n);
@@ -59,8 +58,7 @@ static inline int mlock_fixup_end(struct vm_area_struct * vma,
n->vm_start = start;
n->vm_offset += n->vm_start - vma->vm_start;
n->vm_flags = newflags;
- if (n->vm_inode)
- atomic_inc(&n->vm_inode->i_count);
+ n->vm_dentry = dget(vma->vm_dentry);
if (n->vm_ops && n->vm_ops->open)
n->vm_ops->open(n);
insert_vm_struct(current->mm, n);
@@ -89,8 +87,9 @@ static inline int mlock_fixup_middle(struct vm_area_struct * vma,
vma->vm_offset += vma->vm_start - left->vm_start;
right->vm_offset += right->vm_start - left->vm_start;
vma->vm_flags = newflags;
- if (vma->vm_inode)
- atomic_add(2, &vma->vm_inode->i_count);
+ if (vma->vm_dentry)
+ vma->vm_dentry->d_count += 2;
+
if (vma->vm_ops && vma->vm_ops->open) {
vma->vm_ops->open(left);
vma->vm_ops->open(right);
diff --git a/mm/mmap.c b/mm/mmap.c
index af8cd0a4a..be225e83b 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -74,11 +74,11 @@ int vm_enough_memory(long pages)
/* Remove one vm structure from the inode's i_mmap ring. */
static inline void remove_shared_vm_struct(struct vm_area_struct *vma)
{
- struct inode * inode = vma->vm_inode;
+ struct dentry * dentry = vma->vm_dentry;
- if (inode) {
+ if (dentry) {
if (vma->vm_flags & VM_DENYWRITE)
- inode->i_writecount++;
+ dentry->d_inode->i_writecount++;
if(vma->vm_next_share)
vma->vm_next_share->vm_pprev_share = vma->vm_pprev_share;
*vma->vm_pprev_share = vma->vm_next_share;
@@ -194,7 +194,7 @@ unsigned long do_mmap(struct file * file, unsigned long addr, unsigned long len,
return -EACCES;
/* make sure there are no mandatory locks on the file. */
- if (locks_verify_locked(file->f_inode))
+ if (locks_verify_locked(file->f_dentry->d_inode))
return -EAGAIN;
/* fall through */
case MAP_PRIVATE:
@@ -259,7 +259,7 @@ unsigned long do_mmap(struct file * file, unsigned long addr, unsigned long len,
vma->vm_page_prot = protection_map[vma->vm_flags & 0x0f];
vma->vm_ops = NULL;
vma->vm_offset = off;
- vma->vm_inode = NULL;
+ vma->vm_dentry = NULL;
vma->vm_pte = 0;
do_munmap(addr, len); /* Clear old maps */
@@ -283,7 +283,7 @@ unsigned long do_mmap(struct file * file, unsigned long addr, unsigned long len,
if (file) {
int error = 0;
if (vma->vm_flags & VM_DENYWRITE) {
- if (file->f_inode->i_writecount > 0)
+ if (file->f_dentry->d_inode->i_writecount > 0)
error = -ETXTBSY;
else {
/* f_op->mmap might possibly sleep
@@ -291,16 +291,16 @@ unsigned long do_mmap(struct file * file, unsigned long addr, unsigned long len,
* might). In any case, this takes care of any
* race that this might cause.
*/
- file->f_inode->i_writecount--;
+ file->f_dentry->d_inode->i_writecount--;
correct_wcount = 1;
}
}
if (!error)
- error = file->f_op->mmap(file->f_inode, file, vma);
+ error = file->f_op->mmap(file->f_dentry->d_inode, file, vma);
if (error) {
if (correct_wcount)
- file->f_inode->i_writecount++;
+ file->f_dentry->d_inode->i_writecount++;
kmem_cache_free(vm_area_cachep, vma);
return error;
}
@@ -309,8 +309,10 @@ unsigned long do_mmap(struct file * file, unsigned long addr, unsigned long len,
flags = vma->vm_flags;
insert_vm_struct(mm, vma);
if (correct_wcount)
- file->f_inode->i_writecount++;
+ file->f_dentry->d_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;
@@ -387,8 +389,8 @@ static void unmap_fixup(struct vm_area_struct *area,
if (addr == area->vm_start && end == area->vm_end) {
if (area->vm_ops && area->vm_ops->close)
area->vm_ops->close(area);
- if (area->vm_inode)
- iput(area->vm_inode);
+ if (area->vm_dentry)
+ dput(area->vm_dentry);
return;
}
@@ -405,11 +407,14 @@ static void unmap_fixup(struct vm_area_struct *area,
if (!mpnt)
return;
- *mpnt = *area;
- mpnt->vm_offset += (end - area->vm_start);
+ mpnt->vm_mm = area->vm_mm;
mpnt->vm_start = end;
- if (mpnt->vm_inode)
- atomic_inc(&mpnt->vm_inode->i_count);
+ mpnt->vm_end = area->vm_end;
+ mpnt->vm_page_prot = area->vm_page_prot;
+ mpnt->vm_flags = area->vm_flags;
+ mpnt->vm_ops = area->vm_ops;
+ mpnt->vm_offset += (end - area->vm_start);
+ mpnt->vm_dentry = dget(area->vm_dentry);
if (mpnt->vm_ops && mpnt->vm_ops->open)
mpnt->vm_ops->open(mpnt);
area->vm_end = addr; /* Truncate area */
@@ -542,8 +547,8 @@ void exit_mmap(struct mm_struct * mm)
}
remove_shared_vm_struct(mpnt);
zap_page_range(mm, start, size);
- if (mpnt->vm_inode)
- iput(mpnt->vm_inode);
+ if (mpnt->vm_dentry)
+ dput(mpnt->vm_dentry);
kmem_cache_free(vm_area_cachep, mpnt);
mpnt = next;
}
@@ -555,7 +560,7 @@ void exit_mmap(struct mm_struct * mm)
void insert_vm_struct(struct mm_struct *mm, struct vm_area_struct *vmp)
{
struct vm_area_struct **pprev = &mm->mmap;
- struct inode * inode;
+ struct dentry * dentry;
/* Find where to link it in. */
while(*pprev && (*pprev)->vm_start <= vmp->vm_start)
@@ -567,8 +572,9 @@ void insert_vm_struct(struct mm_struct *mm, struct vm_area_struct *vmp)
*pprev = vmp;
vmp->vm_pprev = pprev;
- inode = vmp->vm_inode;
- if (inode) {
+ dentry = vmp->vm_dentry;
+ if (dentry) {
+ struct inode * inode = dentry->d_inode;
if (vmp->vm_flags & VM_DENYWRITE)
inode->i_writecount--;
@@ -615,16 +621,19 @@ void merge_segments (struct mm_struct * mm, unsigned long start_addr, unsigned l
for ( ; mpnt && prev->vm_start < end_addr ; prev = mpnt, mpnt = next) {
next = mpnt->vm_next;
- /* To share, we must have the same inode, operations.. */
- if ((mpnt->vm_inode != prev->vm_inode) ||
+ /* To share, we must have the same dentry, operations.. */
+ if ((mpnt->vm_dentry != prev->vm_dentry)||
(mpnt->vm_pte != prev->vm_pte) ||
(mpnt->vm_ops != prev->vm_ops) ||
(mpnt->vm_flags != prev->vm_flags) ||
(prev->vm_end != mpnt->vm_start))
continue;
- /* and if we have an inode, the offsets must be contiguous.. */
- if ((mpnt->vm_inode != NULL) || (mpnt->vm_flags & VM_SHM)) {
+ /*
+ * If we have a dentry or it's a shared memory area
+ * the offsets must be contiguous..
+ */
+ if ((mpnt->vm_dentry != NULL) || (mpnt->vm_flags & VM_SHM)) {
unsigned long off = prev->vm_offset+prev->vm_end-prev->vm_start;
if (off != mpnt->vm_offset)
continue;
@@ -645,8 +654,8 @@ void merge_segments (struct mm_struct * mm, unsigned long start_addr, unsigned l
mpnt->vm_ops->close(mpnt);
}
remove_shared_vm_struct(mpnt);
- if (mpnt->vm_inode)
- atomic_dec(&mpnt->vm_inode->i_count);
+ if (mpnt->vm_dentry)
+ dput(mpnt->vm_dentry);
kmem_cache_free(vm_area_cachep, mpnt);
mpnt = prev;
}
diff --git a/mm/mprotect.c b/mm/mprotect.c
index 2e46ca142..ddf4f4ed6 100644
--- a/mm/mprotect.c
+++ b/mm/mprotect.c
@@ -110,8 +110,7 @@ static inline int mprotect_fixup_start(struct vm_area_struct * vma,
vma->vm_offset += vma->vm_start - n->vm_start;
n->vm_flags = newflags;
n->vm_page_prot = prot;
- if (n->vm_inode)
- atomic_inc(&n->vm_inode->i_count);
+ n->vm_dentry = dget(n->vm_dentry);
if (n->vm_ops && n->vm_ops->open)
n->vm_ops->open(n);
insert_vm_struct(current->mm, n);
@@ -133,8 +132,7 @@ static inline int mprotect_fixup_end(struct vm_area_struct * vma,
n->vm_offset += n->vm_start - vma->vm_start;
n->vm_flags = newflags;
n->vm_page_prot = prot;
- if (n->vm_inode)
- atomic_inc(&n->vm_inode->i_count);
+ n->vm_dentry = dget(n->vm_dentry);
if (n->vm_ops && n->vm_ops->open)
n->vm_ops->open(n);
insert_vm_struct(current->mm, n);
@@ -165,8 +163,8 @@ static inline int mprotect_fixup_middle(struct vm_area_struct * vma,
right->vm_offset += right->vm_start - left->vm_start;
vma->vm_flags = newflags;
vma->vm_page_prot = prot;
- if (vma->vm_inode)
- atomic_add(2, &vma->vm_inode->i_count);
+ if (vma->vm_dentry)
+ vma->vm_dentry->d_count += 2;
if (vma->vm_ops && vma->vm_ops->open) {
vma->vm_ops->open(left);
vma->vm_ops->open(right);
diff --git a/mm/mremap.c b/mm/mremap.c
index a52db58de..aaabde322 100644
--- a/mm/mremap.c
+++ b/mm/mremap.c
@@ -140,8 +140,7 @@ static inline unsigned long move_vma(struct vm_area_struct * vma,
new_vma->vm_start = new_addr;
new_vma->vm_end = new_addr+new_len;
new_vma->vm_offset = vma->vm_offset + (addr - vma->vm_start);
- if (new_vma->vm_inode)
- atomic_inc(&new_vma->vm_inode->i_count);
+ new_vma->vm_dentry = dget(vma->vm_dentry);
if (new_vma->vm_ops && new_vma->vm_ops->open)
new_vma->vm_ops->open(new_vma);
insert_vm_struct(current->mm, new_vma);
diff --git a/mm/page_io.c b/mm/page_io.c
index 30d0c882e..5ebea3f09 100644
--- a/mm/page_io.c
+++ b/mm/page_io.c
@@ -98,7 +98,7 @@ void rw_swap_page(int rw, unsigned long entry, char * buf, int wait)
return;
wait_on_page(page);
} else if (p->swap_file) {
- struct inode *swapf = p->swap_file;
+ struct inode *swapf = p->swap_file->d_inode;
unsigned int zones[PAGE_SIZE/512];
int i;
if (swapf->i_op->bmap == NULL
diff --git a/mm/simp.c b/mm/simp.c
new file mode 100644
index 000000000..7959d6a0e
--- /dev/null
+++ b/mm/simp.c
@@ -0,0 +1,434 @@
+#define NULL 0
+/*
+ * mm/simp.c -- simple allocator for cached objects
+ *
+ * (C) 1997 Thomas Schoebel-Theuer
+ */
+
+#include <linux/simp.h>
+#include <linux/smp.h>
+#include <linux/mm.h>
+#include <asm/spinlock.h>
+
+/* The next two defines can be independently enabled for debugging */
+/*#define DEBUG*/
+/*#define DEAD_BEEF*/
+
+#ifdef DEAD_BEEF
+#define DEBUG_BEEF 1
+#else
+#define DEBUG_BEEF 0
+#endif
+
+#ifdef __SMP__
+#define NR_PROCESSORS NR_CPUS
+#define GLOBAL_SIZE CHUNK_SIZE
+#else
+#define NR_PROCESSORS 1
+#define GLOBAL_SIZE PAGE_SIZE
+#endif
+
+#define POSTBUFFER_SIZE 63
+#define ORDER 2
+#define CHUNK_SIZE (PAGE_SIZE*(1<<ORDER))
+#define CHUNK_BASE(ptr) (struct header*)(((unsigned long)(ptr)) & ~(CHUNK_SIZE-1))
+#define CHUNK_END(hdr) (void**)((char*)(hdr) + CHUNK_SIZE)
+
+#define COLOR_INCREMENT (8*sizeof(void*)) /* should be 1 cache line */
+#define ALIGN_CACHE(adr) ((((((unsigned long)adr) - 1) / COLOR_INCREMENT) + 1) * COLOR_INCREMENT)
+#define HEADER_SIZE ALIGN_CACHE(sizeof(struct header))
+#define ELEM_SIZE ALIGN_CACHE(sizeof(struct elem))
+#define FILL_TYPE(name,wrongsize) char name[ALIGN_CACHE(wrongsize)-(wrongsize)]
+
+#define MAX_SIMPS ((GLOBAL_SIZE / sizeof(struct simp)) - 1)
+
+struct header { /* this is at the beginning of each memory region */
+ /* 1st cache line */
+ void ** index;
+ void ** fresh;
+ struct simp * father;
+ void ** emptypos;
+ struct header * next;
+ structor again_ctor;
+ structor first_ctor;
+ void * fill[1];
+#ifdef DEBUG
+ /* 2nd cache line */
+ char magic[32];
+#endif
+};
+
+struct per_processor {
+ void ** buffer_pos;
+ void * postbuffer[POSTBUFFER_SIZE];
+};
+
+struct simp {
+ /* 1st cache lines */
+ struct per_processor private[NR_PROCESSORS];
+ /* next cache line */
+ struct header * usable_list;
+ spinlock_t lock;
+ char fill[sizeof(void*) - sizeof(spinlock_t)];
+ long real_size;
+ long max_elems;
+ structor again_ctor;
+ structor first_ctor;
+ structor dtor;
+ long fill2;
+ /* next cache line */
+ long create_offset;
+ long color;
+ long max_color;
+ long size;
+ long fill3[4];
+ /* next cache line */
+ char name[32];
+};
+
+struct global_data {
+ /* 1st cache line */
+ long changed_flag;
+ long nr_simps;
+ spinlock_t lock;
+ char fill[(6+8)*sizeof(void*)+sizeof(void*)-sizeof(spinlock_t)];
+ /* rest */
+ struct simp simps[MAX_SIMPS];
+};
+
+static struct global_data * global = NULL;
+
+#ifdef DEBUG
+static char global_magic[32] = "SIMP header SdC581oi9rY20051962\n";
+#endif
+
+struct simp * simp_create(char * name, long size,
+ structor first_ctor,
+ structor again_ctor,
+ structor dtor)
+{
+ struct simp * simp;
+ long fraction;
+ long real_size;
+ int cpu;
+
+ if(!global) {
+#ifdef __SMP__
+ global = (struct global_data*)__get_free_pages(GFP_KERNEL, ORDER, 0);
+ memset(global, 0, CHUNK_SIZE);
+#else
+ global = (struct global_data*)get_free_page(GFP_KERNEL);
+#endif
+ spin_lock_init(&global->lock);
+ }
+
+ spin_lock(&global->lock);
+ simp = &global->simps[global->nr_simps++];
+ spin_unlock(&global->lock);
+
+ if(global->nr_simps >= MAX_SIMPS) {
+ printk("SIMP: too many simps allocated\n");
+ return NULL;
+ }
+ memset(simp, 0, sizeof(struct simp));
+ spin_lock_init(&simp->lock);
+ strncpy(simp->name, name, 15);
+ simp->size = size;
+ simp->real_size = real_size = ALIGN_CACHE(size);
+ /* allow aggregation of very small objects in 2-power fractions of
+ * cachelines */
+ fraction = COLOR_INCREMENT / 2;
+ while(size <= fraction && fraction >= sizeof(void*)) {
+ simp->real_size = fraction;
+ fraction >>= 1;
+ }
+ simp->first_ctor = first_ctor;
+ simp->again_ctor = again_ctor;
+ simp->dtor = dtor;
+
+ real_size += sizeof(void*);
+ simp->max_elems = (CHUNK_SIZE - HEADER_SIZE) / real_size;
+ simp->max_color = (CHUNK_SIZE - HEADER_SIZE) % real_size;
+ for(cpu = 0; cpu < NR_PROCESSORS; cpu++) {
+ struct per_processor * private = &simp->private[cpu];
+ private->buffer_pos = private->postbuffer;
+ }
+ return simp;
+}
+
+/* Do *not* inline this, it clobbers too many registers... */
+static void alloc_header(struct simp * simp)
+{
+ struct header * hdr;
+ char * ptr;
+ void ** index;
+ long count;
+
+ spin_unlock(&simp->lock);
+ for(;;) {
+ hdr = (struct header*)__get_free_pages(GFP_KERNEL, ORDER, 0);
+ if(hdr)
+ break;
+ if(!simp_garbage())
+ return;
+ }
+#ifdef DEBUG
+ if(CHUNK_BASE(hdr) != hdr)
+ panic("simp: bad kernel page alignment");
+#endif
+
+ memset(hdr, 0, HEADER_SIZE);
+#ifdef DEBUG
+ memcpy(hdr->magic, global_magic, sizeof(global_magic));
+#endif
+ hdr->father = simp;
+ hdr->again_ctor = simp->again_ctor;
+ hdr->first_ctor = simp->first_ctor;
+
+ /* note: races on simp->color don't produce any error :-) */
+ ptr = ((char*)hdr) + HEADER_SIZE + simp->color;
+ index = CHUNK_END(hdr);
+ for(count = 0; count < simp->max_elems; count++) {
+ *--index = ptr;
+ ptr += simp->real_size;
+ /* note: constructors are not called here in bunch but
+ * instead at each single simp_alloc(), in order
+ * to maximize chances that the cache will be
+ * polluted after a simp_alloc() anyway,
+ * and not here. */
+ }
+ hdr->index = hdr->fresh = hdr->emptypos = index;
+
+ spin_lock(&simp->lock);
+ simp->color += COLOR_INCREMENT;
+ if(simp->color >= simp->max_color)
+ simp->color = 0;
+ hdr->next = simp->usable_list;
+ simp->usable_list = hdr;
+}
+
+
+/* current x86 memcpy() is horribly moving around registers for nothing,
+ * is doing unnecessary work if the size is dividable by a power-of-two,
+ * and it clobbers way too many registers.
+ * This results in nearly any other register being transfered to stack.
+ * Fixing this would be a major win for the whole kernel!
+ */
+static void ** bunch_alloc(struct simp * simp, void ** buffer)
+{
+ struct header * hdr;
+ void ** index;
+ void ** to;
+ void ** end;
+ structor todo;
+ long length;
+
+ spin_lock(&simp->lock);
+ hdr = simp->usable_list;
+ if(!hdr) {
+ alloc_header(simp);
+ hdr = simp->usable_list;
+ if(!hdr) {
+ spin_unlock(&simp->lock);
+ *buffer = NULL;
+ return buffer+1;
+ }
+ }
+
+ index = hdr->index;
+ end = hdr->fresh;
+ todo = hdr->again_ctor;
+ if(index == end) {
+ end = CHUNK_END(hdr);
+ todo = hdr->first_ctor;
+ }
+ to = index + POSTBUFFER_SIZE/2;
+ if(to >= end) {
+ to = end;
+ if(to == CHUNK_END(hdr)) {
+ simp->usable_list = hdr->next;
+ hdr->next = NULL;
+ }
+ }
+ if(to > hdr->fresh)
+ hdr->fresh = to;
+ hdr->index = to;
+ length = ((unsigned long)to) - (unsigned long)index;
+ to = buffer + (length/sizeof(void**));
+
+ memcpy(buffer, index, length);
+
+ spin_unlock(&simp->lock);
+
+ if(todo) {
+ do {
+ todo(*buffer++);
+ } while(buffer < to);
+ }
+ return to;
+}
+
+void * simp_alloc(struct simp * simp)
+{
+#ifdef __SMP__
+ const long cpu = smp_processor_id();
+ struct per_processor * priv = &simp->private[cpu];
+#else
+#define priv (&simp->private[0]) /*fool gcc to use no extra register*/
+#endif
+ void ** buffer_pos = priv->buffer_pos;
+ void * res;
+
+ if(buffer_pos == priv->postbuffer) {
+ buffer_pos = bunch_alloc(simp, buffer_pos);
+ }
+ buffer_pos--;
+ res = *buffer_pos;
+ priv->buffer_pos = buffer_pos;
+ return res;
+}
+
+#ifdef DEBUG
+long check_header(struct header * hdr, void * ptr)
+{
+ void ** test;
+
+ if(!hdr) {
+ printk("SIMP: simp_free() with NULL pointer\n");
+ return 1;
+ }
+ if(strncmp(hdr->magic, global_magic, 32)) {
+ printk("SIMP: simpe_free() with bad ptr %p, or header corruption\n", ptr);
+ return 1;
+ }
+ /* This is brute force, but I don't want to pay for any
+ * overhead if debugging is not enabled, in particular
+ * no space overhead for keeping hashtables etc. */
+ test = hdr->index;
+ while(test < CHUNK_END(hdr)) {
+ if(*test++ == ptr) {
+ printk("SIMP: trying to simp_free(%p) again\n", ptr);
+ return 1;
+ }
+ }
+ return 0;
+}
+#endif
+
+static void ** bunch_free(struct simp * simp, void ** buffer)
+{
+ void ** stop;
+
+ stop = buffer - POSTBUFFER_SIZE/3;
+
+ spin_lock(&simp->lock);
+ while(buffer > stop) {
+ void * elem = buffer[-1];
+ struct header * hdr = CHUNK_BASE(elem);
+ void ** index = hdr->index;
+ index--;
+ hdr->index = index;
+ *index = elem;
+ if(!hdr->next) {
+ hdr->next = simp->usable_list;
+ simp->usable_list = hdr;
+ }
+
+ buffer -= 2;
+ elem = *buffer;
+ hdr = CHUNK_BASE(elem);
+ index = hdr->index;
+ index--;
+ hdr->index = index;
+ *index = elem;
+ if(!hdr->next) {
+ hdr->next = simp->usable_list;
+ simp->usable_list = hdr;
+ }
+ }
+ spin_unlock(&simp->lock);
+ global->changed_flag = 1;
+ return buffer;
+}
+
+void simp_free(void * objp)
+{
+ struct header * hdr;
+ void ** buffer_pos;
+ struct per_processor * private;
+#ifdef __SMP__
+ const long cpu = smp_processor_id();
+#else
+ const long cpu = 0;
+#endif
+
+ hdr = CHUNK_BASE(objp);
+#ifdef DEBUG
+ if(check_header(hdr, objp))
+ return;
+#endif
+
+ private = &hdr->father->private[cpu];
+ buffer_pos = private->buffer_pos;
+ if(buffer_pos >= private->postbuffer+POSTBUFFER_SIZE) {
+ buffer_pos = bunch_free(hdr->father, buffer_pos);
+ }
+
+ *buffer_pos++ = objp;
+ private->buffer_pos = buffer_pos;
+
+#ifdef DEAD_BEEF
+ {
+ unsigned int * ptr = (unsigned int*)objp;
+ int count = (hdr->father->real_size - ELEM_SIZE) / sizeof(unsigned int);
+ while(count--)
+ *ptr++ = 0xdeadbeef;
+ }
+#endif
+}
+
+long simp_garbage(void)
+{
+ int i;
+ int res;
+
+ if(!global->changed_flag)
+ return 0; /* shortcut */
+ /* Note: costs do not matter here. Any heavy thrashing of
+ * simp chunks that could be caused by pools stealing each
+ * other's memory has to be considered a BUG :-)
+ * Simply avoid memory shortages by conservative allocating
+ * policies.
+ */
+ global->changed_flag = 0;
+ res = 0;
+ for(i = 0; i < global->nr_simps; i++) {
+ struct simp * simp = &global->simps[i];
+ struct header ** base = &simp->usable_list;
+ struct header * del;
+
+ spin_lock(&simp->lock);
+ del = *base;
+ while(del) {
+ if(del->index == del->emptypos) {
+ if(simp->dtor) {
+ void ** ptr = del->index;
+ while(ptr < CHUNK_END(del)) {
+ simp->dtor(*ptr++);
+ }
+ }
+ *base = del->next;
+#ifdef DEBUG
+ memset(del, 0, CHUNK_SIZE);
+#endif
+ free_pages((unsigned long)del, ORDER);
+ res++;
+ } else
+ base = &del->next;
+ del = *base;
+ }
+ spin_unlock(&simp->lock);
+ }
+ return res;
+}
+
diff --git a/mm/slab.c b/mm/slab.c
index 6277739d4..f4793d271 100644
--- a/mm/slab.c
+++ b/mm/slab.c
@@ -123,7 +123,7 @@
* 0 if you wish to reduce memory usage.
*
* SLAB_DEBUG_SUPPORT - 1 for kmem_cache_create() to honour; SLAB_DEBUG_FREE,
- * SLAB_DEBUG_INITIAL, SLAB_RED_ZONE & SLAB_POISION.
+ * SLAB_DEBUG_INITIAL, SLAB_RED_ZONE & SLAB_POISON.
* 0 for faster, smaller, code (espically in the critical paths).
*
* SLAB_STATS - 1 to collect stats for /proc/slabinfo.
@@ -143,11 +143,11 @@
#if SLAB_DEBUG_SUPPORT
#if 0
#define SLAB_C_MASK (SLAB_DEBUG_FREE|SLAB_DEBUG_INITIAL|SLAB_RED_ZONE| \
- SLAB_POISION|SLAB_HWCACHE_ALIGN|SLAB_NO_REAP| \
+ SLAB_POISON|SLAB_HWCACHE_ALIGN|SLAB_NO_REAP| \
SLAB_HIGH_PACK)
#endif
#define SLAB_C_MASK (SLAB_DEBUG_FREE|SLAB_DEBUG_INITIAL|SLAB_RED_ZONE| \
- SLAB_POISION|SLAB_HWCACHE_ALIGN|SLAB_NO_REAP)
+ SLAB_POISON|SLAB_HWCACHE_ALIGN|SLAB_NO_REAP)
#else
#if 0
#define SLAB_C_MASK (SLAB_HWCACHE_ALIGN|SLAB_NO_REAP|SLAB_HIGH_PACK)
@@ -215,9 +215,9 @@ typedef struct kmem_bufctl_s {
#define SLAB_RED_MAGIC1 0x5A2CF071UL /* when obj is active */
#define SLAB_RED_MAGIC2 0x170FC2A5UL /* when obj is inactive */
-/* ...and for poisioning */
-#define SLAB_POISION_BYTE 0x5a /* byte value for poisioning */
-#define SLAB_POISION_END 0xa5 /* end-byte of poisioning */
+/* ...and for poisoning */
+#define SLAB_POISON_BYTE 0x5a /* byte value for poisoning */
+#define SLAB_POISON_END 0xa5 /* end-byte of poisoning */
#endif /* SLAB_DEBUG_SUPPORT */
@@ -546,17 +546,17 @@ kmem_freepages(kmem_cache_t *cachep, void *addr)
#if SLAB_DEBUG_SUPPORT
static inline void
-kmem_poision_obj(kmem_cache_t *cachep, void *addr)
+kmem_poison_obj(kmem_cache_t *cachep, void *addr)
{
- memset(addr, SLAB_POISION_BYTE, cachep->c_org_size);
- *(unsigned char *)(addr+cachep->c_org_size-1) = SLAB_POISION_END;
+ memset(addr, SLAB_POISON_BYTE, cachep->c_org_size);
+ *(unsigned char *)(addr+cachep->c_org_size-1) = SLAB_POISON_END;
}
static inline int
-kmem_check_poision_obj(kmem_cache_t *cachep, void *addr)
+kmem_check_poison_obj(kmem_cache_t *cachep, void *addr)
{
void *end;
- end = memchr(addr, SLAB_POISION_END, cachep->c_org_size);
+ end = memchr(addr, SLAB_POISON_END, cachep->c_org_size);
if (end != (addr+cachep->c_org_size-1))
return 1;
return 0;
@@ -605,7 +605,7 @@ kmem_slab_destroy(kmem_cache_t *cachep, kmem_slab_t *slabp)
{
if (cachep->c_dtor
#if SLAB_DEBUG_SUPPORT
- || cachep->c_flags & (SLAB_POISION || SLAB_RED_ZONE)
+ || cachep->c_flags & (SLAB_POISON || SLAB_RED_ZONE)
#endif /*SLAB_DEBUG_SUPPORT*/
) {
/* Doesn't use the bufctl ptrs to find objs. */
@@ -629,10 +629,10 @@ kmem_slab_destroy(kmem_cache_t *cachep, kmem_slab_t *slabp)
#endif /*SLAB_DEBUG_SUPPORT*/
(cachep->c_dtor)(objp, cachep, 0);
#if SLAB_DEBUG_SUPPORT
- else if (cachep->c_flags & SLAB_POISION) {
- if (kmem_check_poision_obj(cachep, objp))
+ else if (cachep->c_flags & SLAB_POISON) {
+ if (kmem_check_poison_obj(cachep, objp))
printk(KERN_ERR "kmem_slab_destory: "
- "Bad poision - %s\n", cachep->c_name);
+ "Bad poison - %s\n", cachep->c_name);
}
if (cachep->c_flags & SLAB_RED_ZONE)
objp -= BYTES_PER_WORD;
@@ -726,18 +726,18 @@ kmem_cache_create(const char *name, size_t size, size_t offset,
flags &= ~SLAB_DEBUG_INITIAL;
}
- if ((flags & SLAB_POISION) && ctor) {
- /* request for poisioning, but we can't do that with a constructor */
- printk("%sPoisioning requested, but con given - %s\n", func_nm, name);
- flags &= ~SLAB_POISION;
+ if ((flags & SLAB_POISON) && ctor) {
+ /* request for poisoning, but we can't do that with a constructor */
+ printk("%sPoisoning requested, but con given - %s\n", func_nm, name);
+ flags &= ~SLAB_POISON;
}
#if 0
if ((flags & SLAB_HIGH_PACK) && ctor) {
printk("%sHigh pack requested, but con given - %s\n", func_nm, name);
flags &= ~SLAB_HIGH_PACK;
}
- if ((flags & SLAB_HIGH_PACK) && (flags & (SLAB_POISION|SLAB_RED_ZONE))) {
- printk("%sHigh pack requested, but with poisioning/red-zoning - %s\n",
+ if ((flags & SLAB_HIGH_PACK) && (flags & (SLAB_POISON|SLAB_RED_ZONE))) {
+ printk("%sHigh pack requested, but with poisoning/red-zoning - %s\n",
func_nm, name);
flags &= ~SLAB_HIGH_PACK;
}
@@ -1094,9 +1094,9 @@ kmem_cache_init_objs(kmem_cache_t * cachep, kmem_slab_t * slabp, void *objp,
if (cachep->c_ctor)
cachep->c_ctor(objp, cachep, ctor_flags);
#if SLAB_DEBUG_SUPPORT
- else if (cachep->c_flags & SLAB_POISION) {
- /* need to poision the objs */
- kmem_poision_obj(cachep, objp);
+ else if (cachep->c_flags & SLAB_POISON) {
+ /* need to poison the objs */
+ kmem_poison_obj(cachep, objp);
}
if (cachep->c_flags & SLAB_RED_ZONE) {
@@ -1275,7 +1275,7 @@ kmem_report_alloc_err(const char *str, kmem_cache_t * cachep)
}
static void
-kmem_report_free_err(const char *str, void *objp, kmem_cache_t * cachep)
+kmem_report_free_err(const char *str, const void *objp, kmem_cache_t * cachep)
{
if (cachep)
SLAB_STATS_INC_ERR(cachep);
@@ -1386,7 +1386,7 @@ ret_obj:
bufp->buf_slabp = slabp;
objp = ((void*)bufp) - cachep->c_offset;
finished:
- /* The lock is not needed by the red-zone or poision ops, and the
+ /* The lock is not needed by the red-zone or poison ops, and the
* obj has been removed from the slab. Should be safe to drop
* the lock here.
*/
@@ -1395,8 +1395,8 @@ finished:
if (cachep->c_flags & SLAB_RED_ZONE)
goto red_zone;
ret_red:
- if ((cachep->c_flags & SLAB_POISION) && kmem_check_poision_obj(cachep, objp))
- kmem_report_alloc_err("Bad poision", cachep);
+ if ((cachep->c_flags & SLAB_POISON) && kmem_check_poison_obj(cachep, objp))
+ kmem_report_alloc_err("Bad poison", cachep);
#endif /* SLAB_DEBUG_SUPPORT */
return objp;
}
@@ -1456,7 +1456,7 @@ nul_ptr:
* it should be in this state _before_ it is released.
*/
static inline void
-__kmem_cache_free(kmem_cache_t *cachep, void *objp)
+__kmem_cache_free(kmem_cache_t *cachep, const void *objp)
{
kmem_slab_t *slabp;
kmem_bufctl_t *bufp;
@@ -1514,10 +1514,10 @@ passed_extra:
/* (hopefully) The most common case. */
finished:
#if SLAB_DEBUG_SUPPORT
- if (cachep->c_flags & SLAB_POISION) {
+ if (cachep->c_flags & SLAB_POISON) {
if (cachep->c_flags & SLAB_RED_ZONE)
objp += BYTES_PER_WORD;
- kmem_poision_obj(cachep, objp);
+ kmem_poison_obj(cachep, objp);
}
#endif /* SLAB_DEBUG_SUPPORT */
spin_unlock_irqrestore(&cachep->c_spinlock, save_flags);
@@ -1615,7 +1615,7 @@ kmalloc(size_t size, int flags)
}
void
-kfree(void *objp)
+kfree(const void *objp)
{
struct page *page;
int nr;
@@ -1654,7 +1654,7 @@ while(1);
}
void
-kfree_s(void *objp, size_t size)
+kfree_s(const void *objp, size_t size)
{
struct page *page;
int nr;
@@ -1861,7 +1861,7 @@ kmem_self_test(void)
kmem_cache_t *test_cachep;
printk(KERN_INFO "kmem_test() - start\n");
- test_cachep = kmem_cache_create("test-cachep", 16, 0, SLAB_RED_ZONE|SLAB_POISION, NULL, NULL);
+ test_cachep = kmem_cache_create("test-cachep", 16, 0, SLAB_RED_ZONE|SLAB_POISON, NULL, NULL);
if (test_cachep) {
char *objp = kmem_cache_alloc(test_cachep, SLAB_KERNEL);
if (objp) {
@@ -1870,12 +1870,12 @@ kmem_self_test(void)
*(objp+16) = 1;
kmem_cache_free(test_cachep, objp);
- /* Mess up poisioning. */
+ /* Mess up poisoning. */
*objp = 10;
objp = kmem_cache_alloc(test_cachep, SLAB_KERNEL);
kmem_cache_free(test_cachep, objp);
- /* Mess up poisioning (again). */
+ /* Mess up poisoning (again). */
*objp = 10;
kmem_cache_shrink(test_cachep);
}
diff --git a/mm/swapfile.c b/mm/swapfile.c
index 819ae7aa8..400274268 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -326,7 +326,7 @@ again:
asmlinkage int sys_swapoff(const char * specialfile)
{
struct swap_info_struct * p = NULL;
- struct inode * inode;
+ struct dentry * dentry;
struct file filp;
int i, type, prev;
int err = -EPERM;
@@ -334,19 +334,22 @@ asmlinkage int sys_swapoff(const char * specialfile)
lock_kernel();
if (!suser())
goto out;
- err = namei(NAM_FOLLOW_LINK, specialfile, &inode);
- if (err)
+
+ dentry = namei(specialfile);
+ err = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
goto out;
+
prev = -1;
for (type = swap_list.head; type >= 0; type = swap_info[type].next) {
p = swap_info + type;
if ((p->flags & SWP_WRITEOK) == SWP_WRITEOK) {
if (p->swap_file) {
- if (p->swap_file == inode)
+ if (p->swap_file == dentry)
break;
} else {
- if (S_ISBLK(inode->i_mode)
- && (p->swap_device == inode->i_rdev))
+ if (S_ISBLK(dentry->d_inode->i_mode)
+ && (p->swap_device == dentry->d_inode->i_rdev))
break;
}
}
@@ -354,7 +357,7 @@ asmlinkage int sys_swapoff(const char * specialfile)
}
err = -EINVAL;
if (type < 0){
- iput(inode);
+ dput(dentry);
goto out;
}
if (prev < 0) {
@@ -369,7 +372,7 @@ asmlinkage int sys_swapoff(const char * specialfile)
p->flags = SWP_USED;
err = try_to_unuse(type);
if (err) {
- iput(inode);
+ dput(dentry);
/* re-insert swap space back into swap_list */
for (prev = -1, i = swap_list.head; i >= 0; prev = i, i = swap_info[i].next)
if (p->prio >= swap_info[i].prio)
@@ -384,21 +387,19 @@ asmlinkage int sys_swapoff(const char * specialfile)
}
if(p->swap_device){
memset(&filp, 0, sizeof(filp));
- filp.f_inode = inode;
+ filp.f_dentry = dentry;
filp.f_mode = 3; /* read write */
/* open it again to get fops */
- if( !blkdev_open(inode, &filp) &&
+ if( !blkdev_open(dentry->d_inode, &filp) &&
filp.f_op && filp.f_op->release){
- filp.f_op->release(inode,&filp);
- filp.f_op->release(inode,&filp);
+ filp.f_op->release(dentry->d_inode,&filp);
+ filp.f_op->release(dentry->d_inode,&filp);
}
}
- iput(inode);
+ dput(dentry);
nr_swap_pages -= p->pages;
- iput(p->swap_file);
- if (p->swap_filename)
- kfree(p->swap_filename);
+ dput(p->swap_file);
p->swap_file = NULL;
p->swap_device = 0;
vfree(p->swap_map);
@@ -420,10 +421,8 @@ int get_swaparea_info(char *buf)
len += sprintf(buf, "Filename\t\t\tType\t\tSize\tUsed\tPriority\n");
for (i = 0 ; i < nr_swapfiles ; i++, ptr++)
if (ptr->flags & SWP_USED) {
- if (ptr->swap_filename)
- len += sprintf(buf + len, "%-31s ", ptr->swap_filename);
- else
- len += sprintf(buf + len, "(null)\t\t\t");
+ len += sprintf(buf + len, "%-31s ", ptr->swap_file->d_name.name);
+
if (ptr->swap_file)
len += sprintf(buf + len, "file\t\t");
else
@@ -451,11 +450,10 @@ int get_swaparea_info(char *buf)
asmlinkage int sys_swapon(const char * specialfile, int swap_flags)
{
struct swap_info_struct * p;
- struct inode * swap_inode;
+ struct dentry * swap_dentry;
unsigned int type;
int i, j, prev;
int error = -EPERM;
- char *tmp;
struct file filp;
static int least_priority = 0;
@@ -472,7 +470,6 @@ asmlinkage int sys_swapon(const char * specialfile, int swap_flags)
if (type >= nr_swapfiles)
nr_swapfiles = type+1;
p->flags = SWP_USED;
- p->swap_filename = NULL;
p->swap_file = NULL;
p->swap_device = 0;
p->swap_map = NULL;
@@ -488,25 +485,22 @@ asmlinkage int sys_swapon(const char * specialfile, int swap_flags)
} else {
p->prio = --least_priority;
}
- error = namei(NAM_FOLLOW_LINK, specialfile, &swap_inode);
- if (error)
- goto bad_swap_2;
- p->swap_file = swap_inode;
- error = -EBUSY;
- if (atomic_read(&swap_inode->i_count) != 1)
+ swap_dentry = namei(specialfile);
+ error = PTR_ERR(swap_dentry);
+ if (IS_ERR(swap_dentry))
goto bad_swap_2;
+
+ p->swap_file = swap_dentry;
error = -EINVAL;
- if (S_ISBLK(swap_inode->i_mode)) {
- p->swap_device = swap_inode->i_rdev;
+ if (S_ISBLK(swap_dentry->d_inode->i_mode)) {
+ p->swap_device = swap_dentry->d_inode->i_rdev;
set_blocksize(p->swap_device, PAGE_SIZE);
- filp.f_inode = swap_inode;
+ filp.f_dentry = swap_dentry;
filp.f_mode = 3; /* read write */
- error = blkdev_open(swap_inode, &filp);
- p->swap_file = NULL;
- iput(swap_inode);
- if(error)
+ error = blkdev_open(swap_dentry->d_inode, &filp);
+ if (error)
goto bad_swap_2;
error = -ENODEV;
if (!p->swap_device ||
@@ -520,7 +514,7 @@ asmlinkage int sys_swapon(const char * specialfile, int swap_flags)
if (p->swap_device == swap_info[i].swap_device)
goto bad_swap;
}
- } else if (!S_ISREG(swap_inode->i_mode))
+ } else if (!S_ISREG(swap_dentry->d_inode->i_mode))
goto bad_swap;
p->swap_lockmap = (unsigned char *) get_free_page(GFP_USER);
if (!p->swap_lockmap) {
@@ -580,12 +574,6 @@ asmlinkage int sys_swapon(const char * specialfile, int swap_flags)
prev = i;
}
p->next = i;
- if (!getname(specialfile, &tmp)) {
- if ((p->swap_filename =
- (char *) kmalloc(strlen(tmp)+1, GFP_KERNEL)) != (char *)NULL)
- strcpy(p->swap_filename, tmp);
- putname(tmp);
- }
if (prev < 0) {
swap_list.head = swap_list.next = p - swap_info;
} else {
@@ -595,11 +583,11 @@ asmlinkage int sys_swapon(const char * specialfile, int swap_flags)
goto out;
bad_swap:
if(filp.f_op && filp.f_op->release)
- filp.f_op->release(filp.f_inode,&filp);
+ filp.f_op->release(filp.f_dentry->d_inode,&filp);
bad_swap_2:
free_page((long) p->swap_lockmap);
vfree(p->swap_map);
- iput(p->swap_file);
+ dput(p->swap_file);
p->swap_device = 0;
p->swap_file = NULL;
p->swap_map = NULL;
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index 71afe1aea..d0270d586 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/mm/vmscan.c b/mm/vmscan.c
index 21c178159..eeadbaa4f 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -7,7 +7,7 @@
* kswapd added: 7.1.96 sct
* Removed kswapd_ctl limits, and swap out as many pages as needed
* to bring the system back to free_pages_high: 2.4.97, Rik van Riel.
- * Version: $Id: vmscan.c,v 1.23 1997/04/12 04:31:05 davem Exp $
+ * Version: $Id: vmscan.c,v 1.3 1997/06/17 13:31:02 ralf Exp $
*/
#include <linux/mm.h>
@@ -362,13 +362,22 @@ static inline int do_try_to_free_page(int priority, int dma, int wait)
return 1;
state = 1;
case 1:
- if (kmem_cache_reap(i, dma, wait))
- return 1;
+ shrink_dcache();
state = 2;
case 2:
- if (shm_swap(i, dma))
+ /*
+ * We shouldn't have a priority here:
+ * If we're low on memory we should
+ * unconditionally throw away _all_
+ * kmalloc caches!
+ */
+ if (kmem_cache_reap(0, dma, wait))
return 1;
state = 3;
+ case 3:
+ if (shm_swap(i, dma))
+ return 1;
+ state = 4;
default:
if (swap_out(i, dma, wait))
return 1;
@@ -403,7 +412,7 @@ int try_to_free_page(int priority, int dma, int wait)
int kswapd(void *unused)
{
int i;
- char *revision="$Revision: 1.23 $", *s, *e;
+ char *revision="$Revision: 1.3 $", *s, *e;
current->session = 1;
current->pgrp = 1;
diff --git a/net/Config.in b/net/Config.in
index 4cd3619c3..d9ec19354 100644
--- a/net/Config.in
+++ b/net/Config.in
@@ -53,6 +53,6 @@ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
# if [ "$CONFIG_LLC" = "y" ]; then
# bool 'Netbeui (EXPERIMENTAL)' CONFIG_NETBEUI
# fi
- tristate 'WAN router' CONFIG_WAN_ROUTER
+ bool 'WAN router' CONFIG_WAN_ROUTER
fi
endmenu
diff --git a/net/README b/net/README
index a88ccfc5a..1cd7f5331 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 <alan@lxorguk.ukuu.org.uk> first.
diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c
index de05f7047..a98ed27d3 100644
--- a/net/appletalk/ddp.c
+++ b/net/appletalk/ddp.c
@@ -1540,11 +1540,33 @@ static int atalk_rcv(struct sk_buff *skb, struct device *dev, struct packet_type
ddp_dl->header_length + ddp->deh_len));
*((__u16 *)ddp)=ntohs(*((__u16 *)ddp)); /* Mend the byte order */
+
/*
* Send the buffer onwards
*/
- skb=skb_unshare(skb, GFP_ATOMIC, FREE_READ);
+ /*
+ * Now we must always be careful. If it's come from
+ * localtalk to ethertalk it might not fit
+ *
+ * Order matters here: If a packet has to be copied
+ * to make a new headroom (rare hopefully) then it
+ * won't need unsharing.
+ *
+ * Note. ddp-> becomes invalid at the realloc.
+ */
+
+ if(skb_headroom(skb)<22)
+ /* 22 bytes - 12 ether, 2 len, 3 802.2 5 snap */
+ skb=skb_realloc_headroom(skb, 32);
+ else
+ skb=skb_unshare(skb, GFP_ATOMIC, FREE_READ);
+
+ /*
+ * If the buffer didnt vanish into the lack of
+ * space bitbucket we can send it.
+ */
+
if(skb)
{
skb->arp = 1; /* Resolved */
@@ -1702,10 +1724,14 @@ static int atalk_sendmsg(struct socket *sock, struct msghdr *msg, int len,
return(-EINVAL);
if(usat->sat_family != AF_APPLETALK)
return -EINVAL;
-#if 0 /* netatalk doesn't implement this check */
+ /* netatalk doesn't implement this check */
if(usat->sat_addr.s_node==ATADDR_BCAST && !sk->broadcast)
+ {
+ printk(KERN_INFO "SO_BROADCAST: Fix your netatalk as it will break before 2.2\n");
+#if 0
return -EPERM;
#endif
+ }
}
else
{
@@ -1806,7 +1832,7 @@ static int atalk_sendmsg(struct socket *sock, struct msghdr *msg, int len,
{
if((!(rt->flags&RTF_GATEWAY))&&(!(dev->flags&IFF_LOOPBACK)))
{
- struct sk_buff *skb2=skb_clone(skb, GFP_KERNEL);
+ struct sk_buff *skb2=skb_copy(skb, GFP_KERNEL);
if(skb2)
{
loopback=1;
diff --git a/net/appletalk/sysctl_net_atalk.c b/net/appletalk/sysctl_net_atalk.c
index 6d5159ddc..c2c1a8c64 100644
--- a/net/appletalk/sysctl_net_atalk.c
+++ b/net/appletalk/sysctl_net_atalk.c
@@ -6,6 +6,7 @@
* Dynamic registration, added aarp entries. (5/30/97 Chris Horn)
*/
+#include <linux/config.h>
#include <linux/mm.h>
#include <linux/sysctl.h>
@@ -14,7 +15,7 @@ extern int sysctl_aarp_tick_time;
extern int sysctl_aarp_retransmit_limit;
extern int sysctl_aarp_resolve_time;
-
+#ifdef CONFIG_SYSCTL
static ctl_table atalk_table[] = {
{NET_ATALK_AARP_EXPIRY_TIME, "aarp-expiry-time",
&sysctl_aarp_expiry_time, sizeof(int), 0644, NULL, &proc_dointvec_jiffies},
@@ -39,16 +40,25 @@ static ctl_table atalk_root_table[] = {
static struct ctl_table_header *atalk_table_header;
-inline void atalk_register_sysctl(void)
+void atalk_register_sysctl(void)
{
atalk_table_header = register_sysctl_table(atalk_root_table, 1);
}
-inline void atalk_unregister_sysctl(void)
+void atalk_unregister_sysctl(void)
{
unregister_sysctl_table(atalk_table_header);
}
+#else
+void atalk_register_sysctl(void)
+{
+}
+
+void atalk_unregister_sysctl(void)
+{
+}
+#endif
diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c
index 37b679600..8e5992747 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 <linux/config.h>
@@ -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 77bfabe73..5daf92fa5 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 6468faf77..3f4f46ad4 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 1394c9ab7..6b5e68236 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 <linux/config.h>
@@ -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 6037f16b4..89ca64f3f 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 <linux/config.h>
@@ -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 847be5790..841149996 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 <linux/config.h>
@@ -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 d27a3af23..7e6ad845c 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 2e6090d76..a17109bff 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 <linux/config.h>
@@ -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 009428fa9..d26f008dd 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 db7207b28..4550302d7 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 <linux/config.h>
@@ -52,7 +53,7 @@
#include <linux/mm.h>
#include <linux/interrupt.h>
-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 3cda48a17..2c7d082a9 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 1efe08366..7b7d437e8 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 <linux/config.h>
@@ -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 3661a63fb..1b1d1c8bb 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 <linux/config.h>
@@ -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 9a4a34c0f..4e97c51b8 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 <linux/config.h>
@@ -44,14 +45,14 @@
#include <linux/mm.h>
#include <linux/interrupt.h>
-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 ba2c72297..39dfd7d42 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 <linux/config.h>
@@ -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 c0a54da11..8a384b58b 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 <linux/config.h>
@@ -46,48 +47,205 @@
#include <linux/mm.h>
#include <linux/interrupt.h>
-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 c5113fce8..5f33147cf 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 1cf2bab65..000203aaf 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/scm.c b/net/core/scm.c
index d88ab0ae7..e5fa793a7 100644
--- a/net/core/scm.c
+++ b/net/core/scm.c
@@ -172,9 +172,9 @@ int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p)
if (acc_fd < 0 || acc_fd >= NR_OPEN ||
(file=current->files->fd[acc_fd])==NULL)
return -EBADF;
- if (!file->f_inode || !file->f_inode->i_sock)
+ if (!file->f_dentry->d_inode || !file->f_dentry->d_inode->i_sock)
return -ENOTSOCK;
- p->sock = &file->f_inode->u.socket_i;
+ p->sock = &file->f_dentry->d_inode->u.socket_i;
if (p->sock->state != SS_UNCONNECTED)
return -EINVAL;
}
diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c
index c912e8b2e..b684fba33 100644
--- a/net/core/sysctl_net_core.c
+++ b/net/core/sysctl_net_core.c
@@ -7,6 +7,9 @@
#include <linux/mm.h>
#include <linux/sysctl.h>
+#include <linux/config.h>
+
+#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/Config.in b/net/ipv4/Config.in
index 489598994..3a5ac3b04 100644
--- a/net/ipv4/Config.in
+++ b/net/ipv4/Config.in
@@ -31,6 +31,7 @@ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
bool 'IP: ARP daemon support (EXPERIMENTAL)' CONFIG_ARPD
fi
fi
+bool 'IP: TCP syncookie support (not enabled per default) ' CONFIG_SYN_COOKIES
comment '(it is safe to leave these untouched)'
bool 'IP: PC/TCP compatibility mode' CONFIG_INET_PCTCP
tristate 'IP: Reverse ARP' CONFIG_INET_RARP
diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile
index 9ce538dc4..2428ccc55 100644
--- a/net/ipv4/Makefile
+++ b/net/ipv4/Makefile
@@ -52,6 +52,11 @@ else
endif
endif
+ifeq ($(CONFIG_SYN_COOKIES),y)
+IPV4_OBJS += syncookies.o
+# module not supported, because it would be too messy.
+endif
+
ifdef CONFIG_INET
O_OBJS := $(IPV4_OBJS)
OX_OBJS := $(IPV4X_OBJS)
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index db54b567a..0d51af255 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -388,7 +388,7 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, int len)
err = memcpy_fromiovec(buf, msg->msg_iov, len);
if (!err)
{
- unsigned short fs;
+ unsigned long fs;
fs=get_fs();
set_fs(get_ds());
err=raw_sendto(sk,buf,len, msg);
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 4a4c5321c..b55fb7666 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -45,6 +45,7 @@
* Pavel Krauz : Limited broadcast fixed
* Alexey Kuznetsov : End of old history. Splitted to fib.c and
* route.c and rewritten from scratch.
+ * Andi Kleen : Load-limit warning messages.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -568,7 +569,7 @@ void ip_rt_redirect(u32 old_gw, u32 daddr, u32 new_gw,
return;
reject_redirect:
- if (ipv4_config.log_martians)
+ if (ipv4_config.log_martians && net_ratelimit())
printk(KERN_INFO "Redirect from %lX/%s to %lX ignored."
"Path = %lX -> %lX, tos %02x\n",
ntohl(old_gw), dev->name, ntohl(new_gw),
@@ -636,7 +637,7 @@ void ip_rt_send_redirect(struct sk_buff *skb)
if (jiffies - rt->last_error > (RT_REDIRECT_LOAD<<rt->errors)) {
icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST, rt->rt_gateway);
rt->last_error = jiffies;
- if (ipv4_config.log_martians && ++rt->errors == RT_REDIRECT_NUMBER)
+ if (ipv4_config.log_martians && ++rt->errors == RT_REDIRECT_NUMBER && net_ratelimit())
printk(KERN_WARNING "host %08x/%s ignores redirects for %08x to %08x.\n",
rt->rt_src, rt->rt_src_dev->name, rt->rt_dst, rt->rt_gateway);
}
@@ -1083,12 +1084,12 @@ no_route:
* Do not cache martian addresses: they should be logged (RFC1812)
*/
martian_destination:
- if (ipv4_config.log_martians)
+ if (ipv4_config.log_martians && net_ratelimit())
printk(KERN_WARNING "martian destination %08x from %08x, dev %s\n", daddr, saddr, dev->name);
return -EINVAL;
martian_source:
- if (ipv4_config.log_martians) {
+ if (ipv4_config.log_martians && net_ratelimit()) {
/*
* RFC1812 recommenadtion, if source is martian,
* the only hint is MAC header.
diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c
new file mode 100644
index 000000000..c18b209f0
--- /dev/null
+++ b/net/ipv4/syncookies.c
@@ -0,0 +1,218 @@
+/*
+ * Syncookies implementation for the Linux kernel
+ *
+ * Copyright (C) 1997 Andi Kleen
+ * Based on ideas by D.J.Bernstein and Eric Schenk.
+ *
+ * This program is free software; you can redistribute it and/or
+ * 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: syncookies.c,v 1.1 1997/07/18 06:30:06 ralf Exp $
+ *
+ * Missing: IPv6 support.
+ * Some counter so that the Administrator can see when the machine
+ * is under a syn flood attack.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_SYN_COOKIES)
+#include <linux/tcp.h>
+#include <linux/malloc.h>
+#include <linux/random.h>
+#include <net/tcp.h>
+
+extern int sysctl_tcp_syncookies;
+
+static unsigned long tcp_lastsynq_overflow;
+
+/*
+ * This table has to be sorted. Only 8 entries are allowed and the
+ * last entry has to be duplicated.
+ * XXX generate a better table.
+ * Unresolved Issues: HIPPI with a 64k MSS is not well supported.
+ */
+static __u16 const msstab[] = {
+ 64,
+ 256,
+ 512,
+ 536,
+ 1024,
+ 1440,
+ 1460,
+ 4312,
+ 4312
+};
+
+static __u32 make_syncookie(struct sk_buff *skb, __u32 counter, __u32 seq)
+{
+ __u32 z;
+
+ z = secure_tcp_syn_cookie(skb->nh.iph->saddr, skb->nh.iph->daddr,
+ skb->h.th->source, skb->h.th->dest,
+ seq,
+ counter);
+
+#if 0
+ printk(KERN_DEBUG
+ "msc: z=%u,cnt=%u,seq=%u,sadr=%u,dadr=%u,sp=%u,dp=%u\n",
+ z,counter,seq,
+ skb->nh.iph->saddr,skb->nh.iph->daddr,
+ ntohs(skb->h.th->source), ntohs(skb->h.th->dest));
+#endif
+
+ return z;
+}
+
+/*
+ * Generate a syncookie.
+ */
+__u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb,
+ __u16 *mssp)
+{
+ int i;
+ __u32 isn;
+ const __u16 mss = *mssp, *w;
+
+ tcp_lastsynq_overflow = jiffies;
+
+ isn = make_syncookie(skb, (jiffies/HZ) >> 6, ntohl(skb->h.th->seq));
+
+ /* XXX sort msstab[] by probability? */
+ w = msstab;
+ for (i = 0; i < 8; i++)
+ if (mss >= *w && mss < *++w)
+ goto found;
+ i--;
+found:
+ *mssp = w[-1];
+
+ isn |= i;
+ return isn;
+}
+
+/* This value should be dependant on TCP_TIMEOUT_INIT and
+ * sysctl_tcp_retries1. It's a rather complicated formula
+ * (exponential backoff) to compute at runtime so it's currently hardcoded
+ * here.
+ */
+#define COUNTER_TRIES 4
+
+/*
+ * Check if a ack sequence number is a valid syncookie.
+ */
+static inline int cookie_check(struct sk_buff *skb, __u32 cookie)
+{
+ int mssind;
+ int i;
+ __u32 counter;
+ __u32 seq;
+
+ if ((jiffies - tcp_lastsynq_overflow) > TCP_TIMEOUT_INIT
+ && tcp_lastsynq_overflow)
+ return 0;
+
+ mssind = cookie & 7;
+ cookie &= ~7;
+
+ counter = (jiffies/HZ)>>6;
+ seq = ntohl(skb->h.th->seq)-1;
+ for (i = 0; i < COUNTER_TRIES; i++)
+ if (make_syncookie(skb, counter-i, seq) == cookie)
+ return msstab[mssind];
+
+ return 0;
+}
+
+extern struct or_calltable or_ipv4;
+
+static inline struct sock *
+get_cookie_sock(struct sock *sk, struct sk_buff *skb, struct open_request *req,
+ struct dst_entry *dst)
+{
+ struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+
+ sk = tp->af_specific->syn_recv_sock(sk, skb, req, dst);
+ req->sk = sk;
+
+ /* Queue up for accept() */
+ tcp_synq_queue(tp, req);
+
+ return sk;
+}
+
+struct sock *
+cookie_v4_check(struct sock *sk, struct sk_buff *skb, struct ip_options *opt)
+{
+ __u32 cookie = ntohl(skb->h.th->ack_seq)-1;
+ struct open_request *req;
+ int mss;
+ struct rtable *rt;
+
+ if (!sysctl_tcp_syncookies)
+ return sk;
+ if (!skb->h.th->ack)
+ return sk;
+
+ mss = cookie_check(skb, cookie);
+ if (mss == 0)
+ return sk;
+
+ req = tcp_openreq_alloc();
+ if (req == NULL)
+ return NULL;
+
+ req->rcv_isn = htonl(skb->h.th->seq)-1;
+ req->snt_isn = cookie;
+ req->mss = mss;
+ req->rmt_port = skb->h.th->source;
+ req->af.v4_req.loc_addr = skb->nh.iph->daddr;
+ req->af.v4_req.rmt_addr = skb->nh.iph->saddr;
+ req->class = &or_ipv4; /* for savety */
+
+ /* We throwed the options of the initial SYN away, so we hope
+ * the ACK carries the same options again (see RFC1122 4.2.3.8)
+ */
+ if (opt && opt->optlen) {
+ int opt_size = sizeof(struct ip_options) + opt->optlen;
+
+ req->af.v4_req.opt = kmalloc(opt_size, GFP_ATOMIC);
+ if (req->af.v4_req.opt) {
+ if (ip_options_echo(req->af.v4_req.opt, skb)) {
+ kfree_s(req->af.v4_req.opt, opt_size);
+ req->af.v4_req.opt = NULL;
+ }
+ }
+ }
+
+ req->af.v4_req.opt = NULL;
+ req->snd_wscale = req->rcv_wscale = req->tstamp_ok = 0;
+ req->wscale_ok = 0;
+ req->expires = 0UL;
+ req->retrans = 0;
+
+ /*
+ * We need to lookup the route here to get at the correct
+ * window size. We should better make sure that the window size
+ * hasn't changed since we received the original syn, but I see
+ * no easy way to do this.
+ */
+ if (ip_route_output(&rt,
+ opt && opt->srr ? opt->faddr :
+ req->af.v4_req.rmt_addr,req->af.v4_req.loc_addr,
+ sk->ip_tos, NULL)) {
+ tcp_openreq_free(req);
+ return NULL;
+ }
+
+ /* Try to redo what tcp_v4_send_synack did. */
+ req->window_clamp = rt->u.dst.window;
+ tcp_select_initial_window(sock_rspace(sk)/2,req->mss,
+ &req->rcv_wnd, &req->window_clamp,
+ 0, &req->rcv_wscale);
+
+ return get_cookie_sock(sk, skb, req, &rt->u.dst);
+}
+
+#endif
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index 6d7ba591f..5f804f343 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -60,8 +60,8 @@ extern int sysctl_tcp_retries2;
extern int sysctl_tcp_max_delay_acks;
extern int sysctl_tcp_fin_timeout;
extern int sysctl_tcp_syncookies;
-extern int sysctl_tcp_always_syncookie;
extern int sysctl_tcp_syn_retries;
+extern int sysctl_tcp_stdurg;
extern int tcp_sysctl_congavoid(ctl_table *ctl, int write, struct file * filp,
void *buffer, size_t *lenp);
@@ -203,10 +203,12 @@ ctl_table ipv4_table[] = {
{NET_IPV4_IGMP_AGE_THRESHOLD, "igmp_age_threshold",
&sysctl_igmp_age_threshold, sizeof(int), 0644, NULL, &proc_dointvec},
#endif
+#ifdef CONFIG_SYN_COOKIES
{NET_TCP_SYNCOOKIES, "tcp_syncookies",
&sysctl_tcp_syncookies, sizeof(int), 0644, NULL, &proc_dointvec},
- {NET_TCP_ALWAYS_SYNCOOKIE, "tcp_always_syncookie",
- &sysctl_tcp_always_syncookie, sizeof(int), 0644, NULL, &proc_dointvec},
+#endif
+ {NET_TCP_STDURG, "tcp_stdurg", &sysctl_tcp_stdurg,
+ sizeof(int), 0644, NULL, &proc_dointvec},
{0}
};
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 604bd1c84..7a6b8f55f 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -5,7 +5,7 @@
*
* Implementation of the Transmission Control Protocol(TCP).
*
- * Version: $Id: tcp_input.c,v 1.52 1997/05/31 12:36:42 freitag Exp $
+ * Version: $Id: tcp_input.c,v 1.2 1997/06/17 13:31:29 ralf Exp $
*
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -56,15 +56,21 @@ static void tcp_cong_avoid_vanj(struct sock *sk, u32 seq, u32 ack,
static void tcp_cong_avoid_vegas(struct sock *sk, u32 seq, u32 ack,
u32 seq_rtt);
+#ifdef CONFIG_SYSCTL
+#define SYNC_INIT 0 /* let the user enable it */
+#else
+#define SYNC_INIT 1
+#endif
+
int sysctl_tcp_cong_avoidance;
int sysctl_tcp_hoe_retransmits;
int sysctl_tcp_sack;
int sysctl_tcp_tsack;
int sysctl_tcp_timestamps;
int sysctl_tcp_window_scaling;
-int sysctl_tcp_syncookies;
-int sysctl_tcp_always_syncookie;
+int sysctl_tcp_syncookies = SYNC_INIT;
int sysctl_tcp_max_delay_acks = MAX_DELAY_ACK;
+int sysctl_tcp_stdurg;
static tcp_sys_cong_ctl_t tcp_sys_cong_ctl_f = &tcp_cong_avoid_vanj;
@@ -288,7 +294,7 @@ static int tcp_reset(struct sock *sk, struct sk_buff *skb)
* FIXME: surely this can be more efficient. -- erics
*/
-void tcp_parse_options(struct tcphdr *th, struct tcp_opt *tp)
+void tcp_parse_options(struct tcphdr *th, struct tcp_opt *tp, int no_fancy)
{
unsigned char *ptr;
int length=(th->doff*4)-sizeof(struct tcphdr);
@@ -323,21 +329,21 @@ void tcp_parse_options(struct tcphdr *th, struct tcp_opt *tp)
break;
case TCPOPT_WINDOW:
if(opsize==TCPOLEN_WINDOW && th->syn)
- if (sysctl_tcp_window_scaling) {
+ if (!no_fancy && sysctl_tcp_window_scaling) {
tp->wscale_ok = 1;
tp->snd_wscale = *(__u8 *)ptr;
}
break;
case TCPOPT_SACK_PERM:
if(opsize==TCPOLEN_SACK_PERM && th->syn)
- if (sysctl_tcp_sack)
+ if (sysctl_tcp_sack && !no_fancy)
tp->sack_ok = 1;
case TCPOPT_TIMESTAMP:
if(opsize==TCPOLEN_TIMESTAMP) {
/* Cheaper to set again then to
* test syn. Optimize this?
*/
- if (sysctl_tcp_timestamps)
+ if (sysctl_tcp_timestamps && !no_fancy)
tp->tstamp_ok = 1;
tp->saw_tstamp = 1;
tp->rcv_tsval = ntohl(*(__u32 *)ptr);
@@ -345,6 +351,8 @@ void tcp_parse_options(struct tcphdr *th, struct tcp_opt *tp)
}
break;
case TCPOPT_SACK:
+ if (no_fancy)
+ break;
tp->sacks = (opsize-2)>>3;
if (tp->sacks<<3 == opsize-2) {
int i;
@@ -385,7 +393,7 @@ static __inline__ int tcp_fast_parse_options(struct tcphdr *th, struct tcp_opt *
return 1;
}
}
- tcp_parse_options(th,tp);
+ tcp_parse_options(th,tp,0);
return 1;
}
@@ -1233,7 +1241,7 @@ static __inline__ void tcp_ack_snd_check(struct sock *sk)
* place. We handle URGent data wrong. We have to - as
* BSD still doesn't use the correction from RFC961.
* For 1003.1g we should support a new option TCP_STDURG to permit
- * either form.
+ * either form (or just set the sysctl tcp_stdurg).
*/
static void tcp_check_urg(struct sock * sk, struct tcphdr * th)
@@ -1241,7 +1249,7 @@ static void tcp_check_urg(struct sock * sk, struct tcphdr * th)
struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
u32 ptr = ntohs(th->urg_ptr);
- if (ptr)
+ if (ptr && !sysctl_tcp_stdurg)
ptr--;
ptr += ntohl(th->seq);
@@ -1459,13 +1467,11 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
/* These use the socket TOS..
* might want to be the received TOS
*/
- if(th->ack)
+ if(th->ack)
return 1; /* send reset */
if(th->syn) {
- __u32 isn = tp->af_specific->init_sequence(sk, skb);
-
- if(tp->af_specific->conn_request(sk, skb, opt, isn) < 0)
+ if(tp->af_specific->conn_request(sk, skb, opt, 0) < 0)
return 1;
/* Now we have several options: In theory there is
@@ -1531,7 +1537,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
tp->fin_seq = skb->seq;
tcp_set_state(sk, TCP_ESTABLISHED);
- tcp_parse_options(th,tp);
+ tcp_parse_options(th,tp,0);
/* FIXME: need to make room for SACK still */
if (tp->wscale_ok == 0) {
tp->snd_wscale = tp->rcv_wscale = 0;
@@ -1574,7 +1580,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
* tcp_connect.
*/
tcp_set_state(sk, TCP_SYN_RECV);
- tcp_parse_options(th,tp);
+ tcp_parse_options(th,tp,0);
if (tp->saw_tstamp) {
tp->ts_recent = tp->rcv_tsval;
tp->ts_recent_stamp = jiffies;
@@ -1616,6 +1622,8 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
sk->shutdown = SHUTDOWN_MASK;
isn = tp->rcv_nxt + 128000;
+ if (isn == 0)
+ isn++;
sk = tp->af_specific->get_sock(skb, th);
@@ -1710,8 +1718,10 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
tp->snd_wl1 = skb->seq;
tp->snd_wl2 = skb->ack_seq;
- } else
+ } else {
+ SOCK_DEBUG(sk, "bad ack\n");
return 1;
+ }
break;
case TCP_FIN_WAIT1:
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index c4d12a54f..d89624175 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.43 1997/05/06 09:31:44 davem Exp $
+ * Version: $Id: tcp_ipv4.c,v 1.1.1.1 1997/06/01 03:16:26 ralf Exp $
*
* IPv4 specific functions
*
@@ -30,6 +30,9 @@
* David S. Miller : Change semantics of established hash,
* half is devoted to TIME_WAIT sockets
* and the rest go in the other half.
+ * Andi Kleen : Add support for syncookies and fixed
+ * some bugs: ip options weren't passed to
+ * the TCP layer, missed a check for an ACK bit.
*/
#include <linux/config.h>
@@ -48,6 +51,7 @@ extern int sysctl_tcp_sack;
extern int sysctl_tcp_tsack;
extern int sysctl_tcp_timestamps;
extern int sysctl_tcp_window_scaling;
+extern int sysctl_tcp_syncookies;
static void tcp_v4_send_reset(struct sk_buff *skb);
@@ -403,7 +407,7 @@ struct sock *tcp_v4_proxy_lookup(unsigned short num, unsigned long raddr,
#endif
-static __u32 tcp_v4_init_sequence(struct sock *sk, struct sk_buff *skb)
+static inline __u32 tcp_v4_init_sequence(struct sock *sk, struct sk_buff *skb)
{
return secure_tcp_sequence_number(sk->saddr, sk->daddr,
skb->h.th->dest,
@@ -697,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;
@@ -835,6 +845,8 @@ static void tcp_v4_send_synack(struct sock *sk, struct open_request *req)
/* Don't offer more than they did.
* This way we don't have to memorize who said what.
+ * FIXME: maybe this should be changed for better performance
+ * with syncookies.
*/
req->mss = min(mss, req->mss);
@@ -891,17 +903,13 @@ static void tcp_v4_or_free(struct open_request *req)
sizeof(struct ip_options) + req->af.v4_req.opt->optlen);
}
-static struct or_calltable or_ipv4 = {
+struct or_calltable or_ipv4 = {
tcp_v4_send_synack,
tcp_v4_or_free
};
-static int tcp_v4_syn_filter(struct sock *sk, struct sk_buff *skb, __u32 saddr)
-{
- return 0;
-}
-
-int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr, __u32 isn)
+int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr,
+ __u32 isn)
{
struct ip_options *opt = (struct ip_options *) ptr;
struct tcp_opt tp;
@@ -909,23 +917,39 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr, __u32 i
struct tcphdr *th = skb->h.th;
__u32 saddr = skb->nh.iph->saddr;
__u32 daddr = skb->nh.iph->daddr;
+#ifdef CONFIG_SYN_COOKIES
+ int want_cookie = 0;
+#else
+#define want_cookie 0 /* Argh, why doesn't gcc optimize this :( */
+#endif
/* If the socket is dead, don't accept the connection. */
- if (sk->dead) {
- SOCK_DEBUG(sk, "Reset on %p: Connect on dead socket.\n",sk);
- tcp_statistics.TcpAttemptFails++;
- return -ENOTCONN;
- }
-
- if (sk->ack_backlog >= sk->max_ack_backlog ||
- tcp_v4_syn_filter(sk, skb, saddr)) {
- SOCK_DEBUG(sk, "dropping syn ack:%d max:%d\n", sk->ack_backlog,
- sk->max_ack_backlog);
-#ifdef CONFIG_IP_TCPSF
- tcp_v4_random_drop(sk);
+ if (sk->dead)
+ goto dead;
+
+ if (sk->ack_backlog >= sk->max_ack_backlog) {
+#ifdef CONFIG_SYN_COOKIES
+ if (sysctl_tcp_syncookies) {
+ static unsigned long warntime;
+
+ if (jiffies - warntime > HZ*60) {
+ warntime = jiffies;
+ printk(KERN_INFO
+ "possible SYN flooding on port %d. Sending cookies.\n", ntohs(skb->h.th->dest));
+ }
+ want_cookie = 1;
+ } else
#endif
- tcp_statistics.TcpAttemptFails++;
- goto exit;
+ {
+ SOCK_DEBUG(sk, "dropping syn ack:%d max:%d\n", sk->ack_backlog,
+ sk->max_ack_backlog);
+ tcp_statistics.TcpAttemptFails++;
+ goto exit;
+ }
+ } else {
+ if (isn == 0)
+ isn = tcp_v4_init_sequence(sk, skb);
+ sk->ack_backlog++;
}
req = tcp_openreq_alloc();
@@ -934,15 +958,12 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr, __u32 i
goto exit;
}
- sk->ack_backlog++;
-
req->rcv_wnd = 0; /* So that tcp_send_synack() knows! */
req->rcv_isn = skb->seq;
- req->snt_isn = isn;
- tp.tstamp_ok = tp.sack_ok = tp.wscale_ok = tp.snd_wscale = 0;
+ tp.tstamp_ok = tp.sack_ok = tp.wscale_ok = tp.snd_wscale = 0;
tp.in_mss = 536;
- tcp_parse_options(th,&tp);
+ tcp_parse_options(th,&tp, want_cookie);
if (tp.saw_tstamp)
req->ts_recent = tp.rcv_tsval;
req->mss = tp.in_mss;
@@ -954,8 +975,17 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr, __u32 i
req->af.v4_req.loc_addr = daddr;
req->af.v4_req.rmt_addr = saddr;
+ /* Note that we ignore the isn passed from the TIME_WAIT
+ * state here. That's the price we pay for cookies.
+ */
+ if (want_cookie)
+ isn = cookie_v4_init_sequence(sk, skb, &req->mss);
+
+ req->snt_isn = isn;
+
/* IPv4 options */
req->af.v4_req.opt = NULL;
+
if (opt && opt->optlen) {
int opt_size = sizeof(struct ip_options) + opt->optlen;
@@ -973,36 +1003,50 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr, __u32 i
tcp_v4_send_synack(sk, req);
- req->expires = jiffies + TCP_TIMEOUT_INIT;
- tcp_inc_slow_timer(TCP_SLT_SYNACK);
- tcp_synq_queue(&sk->tp_pinfo.af_tcp, req);
+ if (want_cookie) {
+ if (req->af.v4_req.opt)
+ kfree(req->af.v4_req.opt);
+ tcp_openreq_free(req);
+ } else {
+ req->expires = jiffies + TCP_TIMEOUT_INIT;
+ tcp_inc_slow_timer(TCP_SLT_SYNACK);
+ tcp_synq_queue(&sk->tp_pinfo.af_tcp, req);
+ }
sk->data_ready(sk, 0);
exit:
kfree_skb(skb, FREE_READ);
return 0;
+
+dead:
+ SOCK_DEBUG(sk, "Reset on %p: Connect on dead socket.\n",sk);
+ tcp_statistics.TcpAttemptFails++;
+ return -ENOTCONN;
}
struct sock * tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
- struct open_request *req)
+ struct open_request *req,
+ struct dst_entry *dst)
{
struct tcp_opt *newtp;
struct sock *newsk;
- struct rtable *rt;
int snd_mss;
newsk = sk_alloc(GFP_ATOMIC);
- if (newsk == NULL)
+ if (newsk == NULL) {
+ if (dst)
+ dst_release(dst);
return NULL;
+ }
memcpy(newsk, sk, sizeof(*newsk));
/* Or else we die! -DaveM */
newsk->sklist_next = NULL;
- newsk->opt = NULL;
- newsk->dst_cache = NULL;
+ newsk->opt = req->af.v4_req.opt;
+
skb_queue_head_init(&newsk->write_queue);
skb_queue_head_init(&newsk->receive_queue);
skb_queue_head_init(&newsk->out_of_order_queue);
@@ -1072,17 +1116,21 @@ struct sock * tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
newsk->rcv_saddr = req->af.v4_req.loc_addr;
/* options / mss / route_cache */
- newsk->opt = req->af.v4_req.opt;
- if (ip_route_output(&rt,
- newsk->opt && newsk->opt->srr ? newsk->opt->faddr : newsk->daddr,
- newsk->saddr, newsk->ip_tos, NULL)) {
- kfree(newsk);
- return NULL;
- }
-
- newsk->dst_cache = &rt->u.dst;
-
- snd_mss = rt->u.dst.pmtu;
+ if (dst == NULL) {
+ struct rtable *rt;
+
+ if (ip_route_output(&rt,
+ newsk->opt && newsk->opt->srr ?
+ newsk->opt->faddr : newsk->daddr,
+ newsk->saddr, newsk->ip_tos, NULL)) {
+ kfree(newsk);
+ return NULL;
+ }
+ dst = &rt->u.dst;
+ }
+ newsk->dst_cache = dst;
+
+ snd_mss = dst->pmtu;
/* FIXME: is mtu really the same as snd_mss? */
newsk->mtu = snd_mss;
@@ -1124,7 +1172,7 @@ struct sock * tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
return newsk;
}
-struct sock *tcp_v4_check_req(struct sock *sk, struct sk_buff *skb)
+static inline struct sock *tcp_v4_check_req(struct sock *sk, struct sk_buff *skb, struct ip_options *opt)
{
struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
struct open_request *req = tp->syn_wait_queue;
@@ -1133,8 +1181,13 @@ struct sock *tcp_v4_check_req(struct sock *sk, struct sk_buff *skb)
* as we checked the user count on tcp_rcv and we're
* running from a soft interrupt.
*/
- if(!req)
+ if(!req) {
+#ifdef CONFIG_SYN_COOKIES
+ goto checkcookie;
+#else
return sk;
+#endif
+ }
while(req) {
if (req->af.v4_req.rmt_addr == skb->nh.iph->saddr &&
@@ -1147,7 +1200,7 @@ struct sock *tcp_v4_check_req(struct sock *sk, struct sk_buff *skb)
* yet accepted()...
*/
sk = req->sk;
- break;
+ goto ende;
}
/* Check for syn retransmission */
@@ -1161,20 +1214,28 @@ struct sock *tcp_v4_check_req(struct sock *sk, struct sk_buff *skb)
return NULL;
}
- sk = tp->af_specific->syn_recv_sock(sk, skb, req);
+ if (!skb->h.th->ack)
+ return sk;
+
+ sk = tp->af_specific->syn_recv_sock(sk, skb, req, NULL);
tcp_dec_slow_timer(TCP_SLT_SYNACK);
if (sk == NULL)
return NULL;
req->expires = 0UL;
req->sk = sk;
- break;
+ goto ende;
}
req = req->dl_next;
}
- skb_orphan(skb);
- skb_set_owner_r(skb, sk);
+#ifdef CONFIG_SYN_COOKIES
+checkcookie:
+ sk = cookie_v4_check(sk, skb, opt);
+#endif
+ende: skb_orphan(skb);
+ if (sk)
+ skb_set_owner_r(skb, sk);
return sk;
}
@@ -1195,20 +1256,28 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
goto ok;
}
- if (sk->state == TCP_LISTEN) {
- struct sock *nsk;
+ /*
+ * We check packets with only the SYN bit set against the
+ * open_request queue too: This increases connection latency a bit,
+ * but is required to detect retransmitted SYNs.
+ *
+ * The ACK/SYN bit check is probably not needed here because
+ * it is checked later again (we play save now).
+ */
+ if (sk->state == TCP_LISTEN && (skb->h.th->ack || skb->h.th->syn)) {
+ struct sock *nsk;
- /* Find possible connection requests. */
- nsk = tcp_v4_check_req(sk, skb);
- if (nsk == NULL)
+ /* Find possible connection requests. */
+ nsk = tcp_v4_check_req(sk, skb, &(IPCB(skb)->opt));
+ if (nsk == NULL)
goto discard_it;
-
- release_sock(sk);
- lock_sock(nsk);
+
+ release_sock(sk);
+ lock_sock(nsk);
sk = nsk;
}
- if (tcp_rcv_state_process(sk, skb, skb->h.th, NULL, skb->len) == 0)
+ if (tcp_rcv_state_process(sk, skb, skb->h.th, &(IPCB(skb)->opt), skb->len) == 0)
goto ok;
reset:
@@ -1352,7 +1421,6 @@ struct tcp_func ipv4_specific = {
tcp_v4_rebuild_header,
tcp_v4_conn_request,
tcp_v4_syn_recv_sock,
- tcp_v4_init_sequence,
tcp_v4_get_sock,
ip_setsockopt,
ip_getsockopt,
diff --git a/net/ipv4/utils.c b/net/ipv4/utils.c
index 4253c85db..d2b8e0089 100644
--- a/net/ipv4/utils.c
+++ b/net/ipv4/utils.c
@@ -13,7 +13,7 @@
* Fixes:
* Alan Cox : verify_area check.
* Alan Cox : removed old debugging.
- *
+ * Andi Kleen : add net_ratelimit()
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -89,3 +89,24 @@ __u32 in_aton(const char *str)
return(htonl(l));
}
+/*
+ * This enforces a rate limit: not more than one kernel message
+ * every 5secs to make a denial-of-service attack impossible.
+ *
+ * All warning printk()s should be guarded by this function.
+ */
+int net_ratelimit(void)
+{
+ static unsigned long last_msg;
+ static int missed;
+
+ if ((jiffies - last_msg) >= 5*HZ) {
+ if (missed)
+ printk(KERN_WARNING "ipv4: (%d messages suppressed. Flood?)\n", missed);
+ missed = 0;
+ last_msg = jiffies;
+ return 1;
+ }
+ missed++;
+ return 0;
+}
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
index 71ff84b4b..a5a884646 100644
--- a/net/ipv6/icmp.c
+++ b/net/ipv6/icmp.c
@@ -5,7 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: icmp.c,v 1.9 1997/04/29 09:38:42 mj Exp $
+ * $Id: icmp.c,v 1.1.1.1 1997/06/01 03:16:27 ralf Exp $
*
* Based on net/ipv4/icmp.c
*
@@ -405,7 +405,23 @@ int icmpv6_rcv(struct sk_buff *skb, struct device *dev,
case CHECKSUM_HW:
if (csum_ipv6_magic(saddr, daddr, len, IPPROTO_ICMPV6,
skb->csum)) {
- printk(KERN_DEBUG "icmpv6 checksum failed\n");
+ printk(KERN_DEBUG "ICMPv6 checksum failed [%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x > %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]\n",
+ ntohs(saddr->in6_u.u6_addr16[0]),
+ ntohs(saddr->in6_u.u6_addr16[1]),
+ ntohs(saddr->in6_u.u6_addr16[2]),
+ ntohs(saddr->in6_u.u6_addr16[3]),
+ ntohs(saddr->in6_u.u6_addr16[4]),
+ ntohs(saddr->in6_u.u6_addr16[5]),
+ ntohs(saddr->in6_u.u6_addr16[6]),
+ ntohs(saddr->in6_u.u6_addr16[7]),
+ ntohs(daddr->in6_u.u6_addr16[0]),
+ ntohs(daddr->in6_u.u6_addr16[1]),
+ ntohs(daddr->in6_u.u6_addr16[2]),
+ ntohs(daddr->in6_u.u6_addr16[3]),
+ ntohs(daddr->in6_u.u6_addr16[4]),
+ ntohs(daddr->in6_u.u6_addr16[5]),
+ ntohs(daddr->in6_u.u6_addr16[6]),
+ ntohs(daddr->in6_u.u6_addr16[7]));
goto discard_it;
}
default:
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 4bf0207d9..51b9eff4c 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -5,7 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: tcp_ipv6.c,v 1.32 1997/06/04 08:28:58 davem Exp $
+ * $Id: tcp_ipv6.c,v 1.2 1997/06/17 13:31:32 ralf Exp $
*
* Based on:
* linux/net/ipv4/tcp.c
@@ -695,7 +695,7 @@ static struct or_calltable or_ipv6 = {
* Can some kind of merge be done? -- erics
*/
static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr,
- __u32 isn)
+ __u32 isn)
{
struct tcp_opt tp;
struct open_request *req;
@@ -711,6 +711,9 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr,
if (skb->protocol == __constant_htons(ETH_P_IP))
return tcp_v4_conn_request(sk, skb, ptr, isn);
+ if (isn == 0)
+ isn = tcp_v6_init_sequence(sk,skb);
+
/*
* There are no SYN attacks on IPv6, yet...
*/
@@ -735,7 +738,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr,
req->snt_isn = isn;
tp.tstamp_ok = tp.sack_ok = tp.wscale_ok = tp.snd_wscale = 0;
tp.in_mss = 536;
- tcp_parse_options(skb->h.th,&tp);
+ tcp_parse_options(skb->h.th,&tp,0);
if (tp.saw_tstamp)
req->ts_recent = tp.rcv_tsval;
req->mss = tp.in_mss;
@@ -778,10 +781,10 @@ static void tcp_v6_send_check(struct sock *sk, struct tcphdr *th, int len,
}
static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
- struct open_request *req)
+ struct open_request *req,
+ struct dst_entry *dst)
{
struct ipv6_pinfo *np;
- struct dst_entry *dst;
struct flowi fl;
struct tcp_opt *newtp;
struct sock *newsk;
@@ -791,11 +794,11 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
* v6 mapped
*/
- newsk = tcp_v4_syn_recv_sock(sk, skb, req);
+ newsk = tcp_v4_syn_recv_sock(sk, skb, req, dst);
- if (newsk == NULL)
+ if (newsk == NULL)
return NULL;
-
+
np = &newsk->net_pinfo.af_inet6;
ipv6_addr_set(&np->daddr, 0, 0, __constant_htonl(0x0000FFFF),
@@ -813,8 +816,11 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
}
newsk = sk_alloc(GFP_ATOMIC);
- if (newsk == NULL)
+ if (newsk == NULL) {
+ if (dst)
+ dst_release(dst);
return NULL;
+ }
memcpy(newsk, sk, sizeof(*newsk));
@@ -902,18 +908,20 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
ipv6_addr_copy(&np->rcv_saddr, &req->af.v6_req.loc_addr);
np->oif = req->af.v6_req.dev;
- /*
- * options / mss / route cache
- */
-
- fl.proto = IPPROTO_TCP;
- fl.nl_u.ip6_u.daddr = &np->daddr;
- fl.nl_u.ip6_u.saddr = &np->saddr;
- fl.dev = np->oif;
- fl.uli_u.ports.dport = newsk->dummy_th.dest;
- fl.uli_u.ports.sport = newsk->dummy_th.source;
-
- dst = ip6_route_output(newsk, &fl);
+ if (dst == NULL) {
+ /*
+ * options / mss / route cache
+ */
+
+ fl.proto = IPPROTO_TCP;
+ fl.nl_u.ip6_u.daddr = &np->daddr;
+ fl.nl_u.ip6_u.saddr = &np->saddr;
+ fl.dev = np->oif;
+ fl.uli_u.ports.dport = newsk->dummy_th.dest;
+ fl.uli_u.ports.sport = newsk->dummy_th.source;
+
+ dst = ip6_route_output(newsk, &fl);
+ }
ip6_dst_store(newsk, dst);
@@ -1051,7 +1059,7 @@ struct sock *tcp_v6_check_req(struct sock *sk, struct sk_buff *skb)
}
skb_orphan(skb);
- sk = tp->af_specific->syn_recv_sock(sk, skb, req);
+ sk = tp->af_specific->syn_recv_sock(sk, skb, req, NULL);
tcp_dec_slow_timer(TCP_SLT_SYNACK);
@@ -1308,7 +1316,6 @@ static struct tcp_func ipv6_specific = {
tcp_v6_rebuild_header,
tcp_v6_conn_request,
tcp_v6_syn_recv_sock,
- tcp_v6_init_sequence,
tcp_v6_get_sock,
ipv6_setsockopt,
ipv6_getsockopt,
@@ -1328,7 +1335,6 @@ static struct tcp_func ipv6_mapped = {
tcp_v4_rebuild_header,
tcp_v6_conn_request,
tcp_v6_syn_recv_sock,
- tcp_v6_init_sequence,
tcp_v6_get_sock,
ipv6_setsockopt,
ipv6_getsockopt,
diff --git a/net/lapb/lapb_iface.c b/net/lapb/lapb_iface.c
index 2be6e565e..77382683c 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 <linux/config.h>
@@ -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 9aa367115..126b93673 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 <linux/config.h>
@@ -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 1256e3a3c..9e1cdf475 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 <linux/config.h>
@@ -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 626e08927..3f7f0a84e 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 2679ff514..757fd10d9 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 <linux/config.h>
@@ -38,72 +36,60 @@
#include <linux/interrupt.h>
#include <net/lapb.h>
-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/netlink.c b/net/netlink.c
index 539ec4295..2c7eb9dd0 100644
--- a/net/netlink.c
+++ b/net/netlink.c
@@ -68,7 +68,7 @@ int netlink_donothing(int minor, struct sk_buff *skb)
static unsigned int netlink_poll(struct file *file, poll_table * wait)
{
unsigned int mask;
- unsigned int minor = MINOR(file->f_inode->i_rdev);
+ unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
poll_wait(&read_space_wait[minor], wait);
mask = POLLOUT | POLLWRNORM;
diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c
index 22e1afbee..dd80a211b 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 <linux/config.h>
@@ -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 2d88bec2b..f7b617dcc 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 c13e92666..a0d3148c2 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 <linux/config.h>
@@ -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 7c053b482..4c3eb61d8 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 <linux/config.h>
@@ -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 41399a53c..ffbb240c4 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 5eae25279..d31141876 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 <linux/config.h>
@@ -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 cc96f26dd..b3fbd012e 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 <linux/config.h>
@@ -37,42 +39,110 @@
#include <linux/interrupt.h>
#include <net/netrom.h>
-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 c6a415ee6..3ce3e71f2 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 9896de9cb..134eee17a 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 <linux/config.h>
@@ -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 73d0aa552..7861220ee 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 3c3e17b2b..1ac11528d 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 <linux/config.h>
@@ -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 86626511e..b481e485f 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 <linux/config.h>
@@ -38,53 +39,64 @@
#include <linux/firewall.h>
#include <net/rose.h>
-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 f0e212dc3..0ed9f7480 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 <linux/config.h>
@@ -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 5b1338609..43358644c 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 <linux/config.h>
@@ -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--;
@@ -381,6 +396,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".
*/
struct device *rose_ax25_dev_get(char *devname)
@@ -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 4e0530cb4..ee710bd6e 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 <linux/config.h>
@@ -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 572975e5d..718a64ec0 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 <linux/config.h>
@@ -37,42 +39,103 @@
#include <linux/interrupt.h>
#include <net/rose.h>
-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 409f79b52..ad45e3b47 100644
--- a/net/rose/sysctl_net_rose.c
+++ b/net/rose/sysctl_net_rose.c
@@ -11,13 +11,13 @@
#include <net/ax25.h>
#include <net/rose.h>
-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 2587083bc..4b722e127 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -149,7 +149,11 @@ static int sockets_in_use = 0;
#define MAX_SOCK_ADDR 128 /* 108 for Unix domain -
16 for IP, 16 for IPX,
24 for IPv6,
- about 80 for AX.25 */
+ about 80 for AX.25
+ must be at least one bigger than
+ the AF_UNIX size (see net/unix/af_unix.c
+ :unix_mkname()).
+ */
int move_addr_to_kernel(void *uaddr, int ulen, void *kaddr)
{
@@ -206,13 +210,23 @@ static int get_fd(struct inode *inode)
return -ENFILE;
}
+ file->f_dentry = d_alloc_root(inode, NULL);
+ if (!file->f_dentry) {
+ put_filp(file);
+ put_unused_fd(fd);
+ return -ENOMEM;
+ }
+
+ /*
+ * The socket maintains a reference to the inode, so we
+ * have to increment the count.
+ */
+ inode->i_count++;
+
current->files->fd[fd] = file;
file->f_op = &socket_file_ops;
file->f_mode = 3;
file->f_flags = O_RDWR;
- file->f_inode = inode;
- if (inode)
- atomic_inc(&inode->i_count);
file->f_pos = 0;
}
return fd;
@@ -238,11 +252,11 @@ extern __inline__ struct socket *sockfd_lookup(int fd, int *err)
return NULL;
}
- inode = file->f_inode;
+ inode = file->f_dentry->d_inode;
if (!inode || !inode->i_sock || !socki_lookup(inode))
{
*err = -ENOTSOCK;
- fput(file,inode);
+ fput(file);
return NULL;
}
@@ -251,7 +265,7 @@ extern __inline__ struct socket *sockfd_lookup(int fd, int *err)
extern __inline__ void sockfd_put(struct socket *sock)
{
- fput(sock->file,sock->inode);
+ fput(sock->file);
}
/*
@@ -266,6 +280,7 @@ struct socket *sock_alloc(void)
inode = get_empty_inode();
if (!inode)
return NULL;
+
sock = socki_lookup(inode);
inode->i_mode = S_IFSOCK;
@@ -459,7 +474,7 @@ static unsigned int sock_poll(struct file *file, poll_table * wait)
{
struct socket *sock;
- sock = socki_lookup(file->f_inode);
+ sock = socki_lookup(file->f_dentry->d_inode);
/*
* We can't return errors to poll, so it's either yes or no.
@@ -1291,7 +1306,7 @@ int sock_fcntl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct socket *sock;
- sock = socki_lookup (filp->f_inode);
+ sock = socki_lookup (filp->f_dentry->d_inode);
if (sock && sock->ops && sock->ops->fcntl)
return sock->ops->fcntl(sock, cmd, arg);
return(-EINVAL);
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 1dfdf1832..f41213ad6 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -162,8 +162,16 @@ static int unix_mkname(struct sockaddr_un * sunaddr, int len, unsigned *hashp)
return -EINVAL;
if (sunaddr->sun_path[0])
{
- if (len >= sizeof(*sunaddr))
- len = sizeof(*sunaddr)-1;
+ /*
+ * This may look like an off by one error but it is
+ * a bit more subtle. 108 is the longest valid AF_UNIX
+ * path for a binding. sun_path[108] doesnt as such
+ * exist. However in kernel space we are guaranteed that
+ * it is a valid memory location in our kernel
+ * address buffer.
+ */
+ if (len > sizeof(*sunaddr))
+ len = sizeof(*sunaddr);
((char *)sunaddr)[len]=0;
len = strlen(sunaddr->sun_path)+1+sizeof(short);
return len;
@@ -450,24 +458,18 @@ retry:
static unix_socket *unix_find_other(struct sockaddr_un *sunname, int len,
int type, unsigned hash, int *error)
{
- int old_fs;
- int err;
- struct inode *inode;
unix_socket *u;
if (sunname->sun_path[0])
{
- old_fs=get_fs();
- set_fs(get_ds());
- err = open_namei(sunname->sun_path, 2, S_IFSOCK, &inode, NULL);
- set_fs(old_fs);
- if(err<0)
- {
- *error=err;
+ struct dentry *dentry;
+ dentry = open_namei(sunname->sun_path, 2, S_IFSOCK);
+ if (IS_ERR(dentry)) {
+ *error = PTR_ERR(dentry);
return NULL;
}
- u=unix_find_socket_byinode(inode);
- iput(inode);
+ u=unix_find_socket_byinode(dentry->d_inode);
+ dput(dentry);
if (u && u->type != type)
{
*error=-EPROTOTYPE;
@@ -491,8 +493,8 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
{
struct sock *sk = sock->sk;
struct sockaddr_un *sunaddr=(struct sockaddr_un *)uaddr;
- struct inode * inode;
- int old_fs;
+ struct dentry * dentry;
+ struct inode * inode = NULL;
int err;
unsigned hash;
struct unix_address *addr;
@@ -545,15 +547,16 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
addr->hash = UNIX_HASH_SIZE;
sk->protinfo.af_unix.addr = addr;
- old_fs=get_fs();
- set_fs(get_ds());
- err=do_mknod(sunaddr->sun_path, S_IFSOCK|S_IRWXUGO, 0);
- if (!err)
- err=open_namei(sunaddr->sun_path, 2, S_IFSOCK, &inode, NULL);
-
- set_fs(old_fs);
-
+ dentry = do_mknod(sunaddr->sun_path, S_IFSOCK|S_IRWXUGO, 0);
+ err = PTR_ERR(dentry);
+ if (!IS_ERR(dentry)) {
+ inode = dentry->d_inode;
+ inode->i_count++; /* HATEFUL - we should use the dentry */
+ dput(dentry);
+ err = 0;
+ }
+
if(err<0)
{
unix_release_addr(addr);
@@ -799,7 +802,7 @@ static int unix_accept(struct socket *sock, struct socket *newsock, int flags)
}
if (sk->protinfo.af_unix.inode)
{
- atomic_inc(&sk->protinfo.af_unix.inode->i_count);
+ sk->protinfo.af_unix.inode->i_count++; /* Should use dentry */
newsk->protinfo.af_unix.inode=sk->protinfo.af_unix.inode;
}
diff --git a/net/unix/garbage.c b/net/unix/garbage.c
index 2a10304df..cf0d634bc 100644
--- a/net/unix/garbage.c
+++ b/net/unix/garbage.c
@@ -68,7 +68,7 @@ static int in_stack = 0; /* first free entry in stack */
extern inline unix_socket *unix_get_socket(struct file *filp)
{
unix_socket * u_sock = NULL;
- struct inode *inode = filp->f_inode;
+ struct inode *inode = filp->f_dentry->d_inode;
/*
* Socket ?
diff --git a/net/unix/sysctl_net_unix.c b/net/unix/sysctl_net_unix.c
index 18ce57684..f487ae95a 100644
--- a/net/unix/sysctl_net_unix.c
+++ b/net/unix/sysctl_net_unix.c
@@ -13,6 +13,9 @@
#include <linux/mm.h>
#include <linux/sysctl.h>
+#include <linux/config.h>
+
+#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/wanrouter/patchlevel b/net/wanrouter/patchlevel
index 3eefcb9dd..b2292bed7 100644
--- a/net/wanrouter/patchlevel
+++ b/net/wanrouter/patchlevel
@@ -1 +1,2 @@
-1.0.0
+1.0.3-modified
+
diff --git a/net/wanrouter/wanmain.c b/net/wanrouter/wanmain.c
index 4c0042082..66b99dedc 100644
--- a/net/wanrouter/wanmain.c
+++ b/net/wanrouter/wanmain.c
@@ -11,15 +11,17 @@
*
* Author: Gene Kozin <genek@compuserve.com>
*
-* Copyright: (c) 1995-1996 Sangoma Technologies Inc.
+* Copyright: (c) 1995-1997 Sangoma Technologies Inc.
*
* This program is free software; you can redistribute it and/or
* 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.
* ============================================================================
-* Dec 27, 1996 Gene Kozin Initial version (based on Sangoma's WANPIPE)
+* Jun 27, 1997 Alan Cox realigned with vendor code
+* Jan 16, 1997 Gene Kozin router_devlist made public
* Jan 31, 1997 Alan Cox Hacked it about a bit for 2.1
+* Dec 27, 1996 Gene Kozin Initial version (based on Sangoma's WANPIPE)
*****************************************************************************/
#include <linux/stddef.h> /* offsetof(), etc. */
@@ -78,9 +80,9 @@ static int delete_interface (wan_device_t* wandev, char* name, int forse);
*/
static char fullname[] = "WAN Router";
-static char copyright[] = "(c) 1995-1996 Sangoma Technologies Inc.";
+static char copyright[] = "(c) 1995-1997 Sangoma Technologies Inc.";
static char modname[] = ROUTER_NAME; /* short module name */
-static wan_device_t* devlist = NULL; /* list of registered devices */
+wan_device_t * router_devlist = NULL; /* list of registered devices */
static int devcnt = 0;
/*
@@ -199,8 +201,8 @@ int register_wan_device(wan_device_t* wandev)
wandev->ndev = 0;
wandev->dev = NULL;
- wandev->next = devlist;
- devlist = wandev;
+ wandev->next = router_devlist;
+ router_devlist = wandev;
++devcnt;
MOD_INC_USE_COUNT; /* prevent module from unloading */
return 0;
@@ -225,7 +227,7 @@ int unregister_wan_device(char* name)
if (name == NULL)
return -EINVAL;
- for (wandev = devlist, prev = NULL;
+ for (wandev = router_devlist, prev = NULL;
wandev && strcmp(wandev->name, name);
prev = wandev, wandev = wandev->next)
;
@@ -246,7 +248,7 @@ int unregister_wan_device(char* name)
if (prev)
prev->next = wandev->next;
else
- devlist = wandev->next;
+ router_devlist = wandev->next;
--devcnt;
wanrouter_proc_delete(wandev);
MOD_DEC_USE_COUNT;
@@ -613,7 +615,7 @@ static wan_device_t* find_device (char* name)
{
wan_device_t* wandev;
- for (wandev = devlist;wandev && strcmp(wandev->name, name);
+ for (wandev = router_devlist;wandev && strcmp(wandev->name, name);
wandev = wandev->next);
return wandev;
}
diff --git a/net/wanrouter/wanproc.c b/net/wanrouter/wanproc.c
index 04a49d803..9c3fe9b2a 100644
--- a/net/wanrouter/wanproc.c
+++ b/net/wanrouter/wanproc.c
@@ -1,20 +1,22 @@
/*****************************************************************************
-* wanproc.c WAN Multiprotocol Router Module. proc filesystem interface.
+* wanproc.c WAN Router Module. /proc filesystem interface.
*
* This module is completely hardware-independent and provides
* access to the router using Linux /proc filesystem.
*
* Author: Gene Kozin <genek@compuserve.com>
*
-* Copyright: (c) 1995-1996 Sangoma Technologies Inc.
+* Copyright: (c) 1995-1997 Sangoma Technologies Inc.
*
* This program is free software; you can redistribute it and/or
* 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.
* ============================================================================
-* Dec 13, 1996 Gene Kozin Initial version (based on Sangoma's WANPIPE)
+* Jun 29, 1997 Alan Cox Merged with 1.0.3 vendor code
+* Jan 29, 1997 Gene Kozin v1.0.1. Implemented /proc read routines
* Jan 30, 1997 Alan Cox Hacked around for 2.1
+* Dec 13, 1996 Gene Kozin Initial version (based on Sangoma's WANPIPE)
*****************************************************************************/
#include <linux/stddef.h> /* offsetof(), etc. */
@@ -39,7 +41,7 @@
#define max(a,b) (((a)>(b))?(a):(b))
#endif
-#define ROUTER_PAGE_SZ 4000 /* buffer size for printing proc info */
+#define PROC_BUFSZ 4000 /* buffer size for printing proc info */
/****** Data Types **********************************************************/
@@ -60,7 +62,6 @@ static long router_proc_read(struct inode* inode, struct file* file, char* buf,
/* Methods for preparing data for reading proc entries */
-static int about_get_info(char* buf, char** start, off_t offs, int len, int dummy);
static int config_get_info(char* buf, char** start, off_t offs, int len, int dummy);
static int status_get_info(char* buf, char** start, off_t offs, int len, int dummy);
static int wandev_get_info(char* buf, char** start, off_t offs, int len, int dummy);
@@ -76,7 +77,6 @@ static int wandev_get_info(char* buf, char** start, off_t offs, int len, int dum
*/
static char name_root[] = ROUTER_NAME;
-static char name_info[] = "about";
static char name_conf[] = "config";
static char name_stat[] = "status";
@@ -84,9 +84,8 @@ static char name_stat[] = "status";
* Structures for interfacing with the /proc filesystem.
* Router creates its own directory /proc/net/router with the folowing
* entries:
- * About general information (version, copyright, etc.)
- * Conf device configuration
- * Stat global device statistics
+ * config device configuration
+ * status global device statistics
* <device> entry for each WAN device
*/
@@ -194,29 +193,6 @@ static struct proc_dir_entry proc_router =
};
/*
- * /proc/net/router/about
- */
-
-static struct proc_dir_entry proc_router_info =
-{
- 0, /* .low_ino */
- sizeof(name_info) - 1, /* .namelen */
- name_info, /* .name */
- 0444 | S_IFREG, /* .mode */
- 1, /* .nlink */
- 0, /* .uid */
- 0, /* .gid */
- 0, /* .size */
- &router_inode, /* .ops */
- &about_get_info, /* .get_info */
- NULL, /* .fill_node */
- NULL, /* .next */
- NULL, /* .parent */
- NULL, /* .subdir */
- NULL, /* .data */
-};
-
-/*
* /proc/net/router/config
*/
@@ -262,6 +238,16 @@ static struct proc_dir_entry proc_router_stat =
NULL, /* .data */
};
+/* Strings */
+static char conf_hdr[] =
+ "Device name | port |IRQ|DMA|mem.addr|mem.size|"
+ "option1|option2|option3|option4\n";
+
+static char stat_hdr[] =
+ "Device name |station|interface|clocking|baud rate| MTU |ndev"
+ "|link state\n";
+
+
/*
* Interface functions
*/
@@ -272,11 +258,10 @@ static struct proc_dir_entry proc_router_stat =
__initfunc(int wanrouter_proc_init (void))
{
- int err = proc_register(&proc_net, &proc_router);
+ int err = proc_register(proc_net, &proc_router);
if (!err)
{
- proc_register(&proc_router, &proc_router_info);
proc_register(&proc_router, &proc_router_conf);
proc_register(&proc_router, &proc_router_stat);
}
@@ -289,10 +274,9 @@ __initfunc(int wanrouter_proc_init (void))
void wanrouter_proc_cleanup (void)
{
- proc_unregister(&proc_router, proc_router_info.low_ino);
proc_unregister(&proc_router, proc_router_conf.low_ino);
proc_unregister(&proc_router, proc_router_stat.low_ino);
- proc_unregister(&proc_net, proc_router.low_ino);
+ proc_unregister(proc_net, proc_router.low_ino);
}
/*
@@ -367,7 +351,7 @@ static long router_proc_read(struct inode* inode, struct file* file,
if ((dent == NULL) || (dent->get_info == NULL))
return 0;
- page = kmalloc(ROUTER_PAGE_SZ, GFP_KERNEL);
+ page = kmalloc(PROC_BUFSZ, GFP_KERNEL);
if (page == NULL)
return -ENOBUFS;
@@ -387,49 +371,86 @@ static long router_proc_read(struct inode* inode, struct file* file,
}
/*
- * Prepare data for reading 'About' entry.
- * Return length of data.
- */
-
-static int about_get_info(char* buf, char** start, off_t offs, int len,
- int dummy)
-{
- int cnt = 0;
-
- cnt += sprintf(&buf[cnt], "%12s : %u.%u\n",
- "version", ROUTER_VERSION, ROUTER_RELEASE);
- return cnt;
-}
-
-/*
* Prepare data for reading 'Config' entry.
* Return length of data.
- * NOT YET IMPLEMENTED
*/
static int config_get_info(char* buf, char** start, off_t offs, int len,
int dummy)
{
- int cnt = 0;
+ int cnt = sizeof(conf_hdr) - 1;
+ wan_device_t* wandev;
+ strcpy(buf, conf_hdr);
+ for (wandev = router_devlist;
+ wandev && (cnt < (PROC_BUFSZ - 80));
+ wandev = wandev->next)
+ {
+ if (wandev->state) cnt += sprintf(&buf[cnt],
+ "%-15s|0x%-4X|%3u|%3u|0x%-6lX|0x%-6X|%7u|%7u|%7u|%7u\n",
+ wandev->name,
+ wandev->ioport,
+ wandev->irq,
+ wandev->dma,
+ wandev->maddr,
+ wandev->msize,
+ wandev->hw_opt[0],
+ wandev->hw_opt[1],
+ wandev->hw_opt[2],
+ wandev->hw_opt[3]);
+ }
- cnt += sprintf(&buf[cnt], "%12s : %u.%u\n",
- "version", ROUTER_VERSION, ROUTER_RELEASE);
return cnt;
}
/*
* Prepare data for reading 'Status' entry.
* Return length of data.
- * NOT YET IMPLEMENTED
*/
static int status_get_info(char* buf, char** start, off_t offs, int len,
int dummy)
{
- int cnt = 0;
-
- cnt += sprintf(&buf[cnt], "%12s : %u.%u\n",
- "version", ROUTER_VERSION, ROUTER_RELEASE);
+ int cnt = sizeof(stat_hdr) - 1;
+ wan_device_t* wandev;
+ strcpy(buf, stat_hdr);
+ for (wandev = router_devlist;
+ wandev && (cnt < (PROC_BUFSZ - 80));
+ wandev = wandev->next)
+ {
+ if (!wandev->state) continue;
+ cnt += sprintf(&buf[cnt],
+ "%-15s|%-7s|%-9s|%-8s|%9u|%5u|%3u |",
+ wandev->name,
+ wandev->station ? " DCE" : " DTE",
+ wandev->interface ? " V.35" : " RS-232",
+ wandev->clocking ? "internal" : "external",
+ wandev->bps,
+ wandev->mtu,
+ wandev->ndev)
+ ;
+ switch (wandev->state)
+ {
+ case WAN_UNCONFIGURED:
+ cnt += sprintf(&buf[cnt], "%-12s\n", "unconfigured");
+ break;
+
+ case WAN_DISCONNECTED:
+ cnt += sprintf(&buf[cnt], "%-12s\n", "disconnected");
+ break;
+
+ case WAN_CONNECTING:
+ cnt += sprintf(&buf[cnt], "%-12s\n", "connecting");
+ break;
+
+ case WAN_CONNECTED:
+ cnt += sprintf(&buf[cnt], "%-12s\n", "connected");
+ break;
+
+ default:
+ cnt += sprintf(&buf[cnt], "%-12s\n", "invalid");
+ break;
+ }
+ }
return cnt;
}
@@ -449,7 +470,52 @@ static int wandev_get_info(char* buf, char** start, off_t offs, int len,
if ((wandev == NULL) || (wandev->magic != ROUTER_MAGIC))
return 0;
- cnt += sprintf(&buf[cnt], "%12s : %s\n", "name", wandev->name);
+ if (!wandev->state)
+ return sprintf(&buf[cnt], "device is not configured!\n")
+ ;
+
+ /* Update device statistics */
+ if (wandev->update) wandev->update(wandev);
+
+ cnt += sprintf(&buf[cnt], "%30s: %12lu\n",
+ "total frames received", wandev->stats.rx_packets)
+ ;
+ cnt += sprintf(&buf[cnt], "%30s: %12lu\n",
+ "receiver overrun errors", wandev->stats.rx_over_errors)
+ ;
+ cnt += sprintf(&buf[cnt], "%30s: %12lu\n",
+ "CRC errors", wandev->stats.rx_crc_errors)
+ ;
+ cnt += sprintf(&buf[cnt], "%30s: %12lu\n",
+ "frame length errors", wandev->stats.rx_length_errors)
+ ;
+ cnt += sprintf(&buf[cnt], "%30s: %12lu\n",
+ "frame format errors", wandev->stats.rx_frame_errors)
+ ;
+ cnt += sprintf(&buf[cnt], "%30s: %12lu\n",
+ "aborted frames received", wandev->stats.rx_missed_errors)
+ ;
+ cnt += sprintf(&buf[cnt], "%30s: %12lu\n",
+ "reveived frames dropped", wandev->stats.rx_dropped)
+ ;
+ cnt += sprintf(&buf[cnt], "%30s: %12lu\n",
+ "other receive errors", wandev->stats.rx_errors)
+ ;
+ cnt += sprintf(&buf[cnt], "\n%30s: %12lu\n",
+ "total frames transmitted", wandev->stats.tx_packets)
+ ;
+ cnt += sprintf(&buf[cnt], "%30s: %12lu\n",
+ "aborted frames transmitted", wandev->stats.tx_aborted_errors)
+ ;
+ cnt += sprintf(&buf[cnt], "%30s: %12lu\n",
+ "transmit frames dropped", wandev->stats.tx_dropped)
+ ;
+ cnt += sprintf(&buf[cnt], "%30s: %12lu\n",
+ "transmit collisions", wandev->stats.collisions)
+ ;
+ cnt += sprintf(&buf[cnt], "%30s: %12lu\n",
+ "other transmit errors", wandev->stats.tx_errors)
+ ;
return cnt;
}
diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c
index a77380648..f59dd3a51 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 <linux/config.h>
@@ -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 8454ac9d9..42893df32 100644
--- a/net/x25/sysctl_net_x25.c
+++ b/net/x25/sysctl_net_x25.c
@@ -13,8 +13,8 @@
#include <linux/init.h>
#include <net/x25.h>
-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 6b02a9441..e4cd99ae7 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 13759d1ea..af072ce22 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 82e5c0817..96b459a4e 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 <linux/config.h>
@@ -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 f44c0a2c5..1742d802f 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 <linux/config.h>
@@ -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 321baa5d6..aa8fc2c1b 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 <linux/config.h>
@@ -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 820c11cd4..9c3204537 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 75e58af98..f2aff6d12 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 <linux/config.h>
@@ -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 e625c41ed..90b6513c9 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 <linux/config.h>
@@ -39,42 +41,93 @@
#include <linux/interrupt.h>
#include <net/x25.h>
-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
diff --git a/scripts/ksymoops.cc b/scripts/ksymoops.cc
index 28d93f56f..d1edca9c1 100644
--- a/scripts/ksymoops.cc
+++ b/scripts/ksymoops.cc
@@ -31,6 +31,7 @@
// * Only resolves operands of jump and call instructions.
#include <fstream.h>
+#include <strstream.h>
#include <iomanip.h>
#include <stdio.h>
#include <string.h>
@@ -184,9 +185,23 @@ NameList::decode(unsigned char* code, long eip_addr)
char buf[1024];
int lines = 0;
+ int eip_seen = 0;
+ long offset;
while (fgets(buf, sizeof(buf), objdump_FILE)) {
+ if (eip_seen && buf[4] == ':') {
+ // assume objdump from binutils 2.8..., reformat to old style
+ offset = strtol(buf, 0, 16);
+ char newbuf[sizeof(buf)];
+ memset(newbuf, '\0', sizeof(newbuf));
+ ostrstream ost(newbuf, sizeof(newbuf));
+ ost.width(8);
+ ost << offset;
+ ost << " <_EIP+" << offset << ">: " << &buf[6] << ends;
+ strcpy(buf, newbuf);
+ }
if (!strnequ(&buf[9], "<_EIP", 5))
continue;
+ eip_seen = 1;
if (strstr(buf, " is out of bounds"))
break;
lines++;
@@ -195,19 +210,28 @@ NameList::decode(unsigned char* code, long eip_addr)
cout << buf;
continue;
}
- long offset = strtol(buf, 0, 16);
- char* bp_0 = strchr(buf, '>') + 2;
+ offset = strtol(buf, 0, 16);
+ char* bp_0 = strchr(buf, '>');
KSym* ksym = find(eip_addr + offset);
+ if (bp_0)
+ bp_0 += 2;
+ else
+ bp_0 = strchr(buf, ':');
if (ksym)
cout << *ksym << ' ';
- char* bp = bp_0;
+ char *bp_1 = strstr(bp_0, "\t"); // objdump from binutils 2.8...
+ if (bp_1)
+ ++bp_1;
+ else
+ bp_1 = bp_0;
+ char *bp = bp_1;
while (!isspace(*bp))
bp++;
while (isspace(*bp))
bp++;
- if (*bp != '0') {
+ if (!isxdigit(*bp)) {
cout << bp_0;
- } else if (*bp_0 == 'j' || strnequ(bp_0, "call", 4)) { // a jump or call insn
+ } else if (*bp_1 == 'j' || strnequ(bp_1, "call", 4)) { // a jump or call insn
long rel_addr = strtol(bp, 0, 16);
ksym = find(eip_addr + rel_addr);
if (ksym) {