From 770b8afb15810f52bd8022ed5323302e56ab8bf4 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 23 Nov 2007 15:10:22 -0500 Subject: [PATCH] Import 1.3.42 --- CREDITS | 11 +- Documentation/Configure.help | 13 +- Documentation/SMP.txt | 6 +- Documentation/networking/net-modules.txt | 85 +- Makefile | 27 +- arch/alpha/kernel/irq.c | 34 +- arch/i386/Makefile | 4 + arch/i386/boot/Makefile | 4 + arch/i386/boot/bootsect.S | 2 +- arch/i386/boot/compressed/Makefile | 13 + arch/i386/boot/setup.S | 1040 +++++---- arch/i386/defconfig | 3 +- arch/i386/kernel/Makefile | 20 +- arch/i386/kernel/bios32.c | 6 + arch/i386/kernel/setup.c | 4 +- drivers/block/floppy.c | 34 +- drivers/block/ll_rw_blk.c | 4 +- drivers/cdrom/mcd.c | 13 +- drivers/cdrom/mcdx.c | 16 - drivers/char/cyclades.c | 26 +- drivers/net/3c503.c | 125 +- drivers/net/8390.c | 22 +- drivers/net/8390.h | 3 +- drivers/net/Config.in | 6 +- drivers/net/Makefile | 2 +- drivers/net/Space.c | 18 +- drivers/net/ac3200.c | 100 +- drivers/net/e2100.c | 110 +- drivers/net/hp-plus.c | 114 +- drivers/net/hp.c | 120 +- drivers/net/ne.c | 145 +- drivers/net/net_init.c | 6 +- drivers/net/smc-ultra.c | 108 +- drivers/net/tunnel.c | 3 +- drivers/net/wd.c | 98 +- drivers/pci/pci.c | 16 - drivers/scsi/53c7,8xx.c | 2 +- drivers/scsi/BusLogic.c | 2577 ++++++++++++++++++++++ drivers/scsi/BusLogic.h | 967 ++++++++ drivers/scsi/ChangeLog | 4 + drivers/scsi/Config.in | 2 +- drivers/scsi/Makefile | 4 +- drivers/scsi/README.BusLogic | 262 +++ drivers/scsi/buslogic.c | 1574 ------------- drivers/scsi/buslogic.h | 190 -- drivers/scsi/hosts.c | 4 +- drivers/scsi/scsi_syms.c | 2 + drivers/scsi/seagate.c | 27 + drivers/scsi/seagate.h | 3 +- fs/Config.in | 3 + fs/binfmt_elf.c | 85 +- fs/buffer.c | 12 +- fs/dcache.c | 2 +- fs/exec.c | 18 +- fs/ext/inode.c | 2 +- fs/fcntl.c | 2 +- fs/file_table.c | 6 +- fs/filesystems.c | 14 + fs/inode.c | 10 +- fs/nfs/Makefile | 5 + fs/nfs/nfsroot.c | 849 +++++++ fs/nfs/proc.c | 35 +- fs/super.c | 35 + include/asm-i386/ioctl.h | 4 +- include/asm-i386/smp.h | 2 +- include/linux/bios32.h | 2 +- include/linux/cyclades.h | 55 +- include/linux/etherdevice.h | 4 +- include/linux/fs.h | 14 +- include/linux/if_arp.h | 10 + include/linux/if_ppp.h | 4 +- include/linux/ioport.h | 2 +- include/linux/ip_fw.h | 17 + include/linux/major.h | 2 + include/linux/mm.h | 36 +- include/linux/netdevice.h | 23 +- include/linux/nfs_fs.h | 3 + include/linux/nfs_mount.h | 14 +- include/linux/pci.h | 1 - include/linux/proc_fs.h | 1 + include/linux/sockios.h | 9 +- include/linux/tcp.h | 4 - include/net/arp.h | 12 +- include/net/ip.h | 5 +- include/net/netlink.h | 4 +- include/net/route.h | 198 +- include/net/sock.h | 16 +- init/main.c | 35 +- kernel/exit.c | 45 +- kernel/fork.c | 12 +- kernel/ksyms.c | 6 +- kernel/signal.c | 2 +- mm/mmap.c | 89 +- net/Changes | 26 +- net/ax25/af_ax25.c | 4 +- net/core/dev.c | 2 + net/ethernet/eth.c | 64 +- net/ipv4/Config.in | 2 +- net/ipv4/af_inet.c | 11 + net/ipv4/arp.c | 1548 ++++++++----- net/ipv4/devinet.c | 13 + net/ipv4/icmp.c | 55 +- net/ipv4/igmp.c | 2 +- net/ipv4/ip_forward.c | 47 +- net/ipv4/ip_fw.c | 55 +- net/ipv4/ip_options.c | 8 +- net/ipv4/ip_output.c | 216 +- net/ipv4/ip_sockglue.c | 12 +- net/ipv4/rarp.c | 10 +- net/ipv4/raw.c | 8 +- net/ipv4/route.c | 1843 ++++++++++++---- net/ipv4/tcp.c | 214 +- net/ipv4/timer.c | 2 - net/ipv4/udp.c | 14 +- net/ipx/af_ipx.c | 2 +- net/netlink.c | 4 +- net/netrom/nr_dev.c | 2 +- net/netrom/nr_route.c | 2 +- net/socket.c | 4 +- 119 files changed, 9759 insertions(+), 4099 deletions(-) create mode 100644 drivers/scsi/BusLogic.c create mode 100644 drivers/scsi/BusLogic.h create mode 100644 drivers/scsi/README.BusLogic delete mode 100644 drivers/scsi/buslogic.c delete mode 100644 drivers/scsi/buslogic.h create mode 100644 fs/nfs/nfsroot.c diff --git a/CREDITS b/CREDITS index 216eea069a77..b3fa273ec86e 100644 --- a/CREDITS +++ b/CREDITS @@ -336,11 +336,11 @@ S: Laval, Quebec S: Canada H7Y 1V9 N: David Gentzel -E: gentzel@nova.enet.dec.com -D: BusLogic driver and original UltraStor driver +E: gentzel@telerama.lm.com +D: Original BusLogic driver and original UltraStor driver S: Whitfield Software Services -S: 631 Idlewood Avenue -S: Carnegie, Pennsylvania 15106-1126 +S: 600 North Bell Avenue, Suite 160 +S: Carnegie, Pennsylvania 15106-4304 S: USA N: Philip Gladstone @@ -1077,7 +1077,8 @@ S: Germany N: Leonard N. Zubkoff E: lnz@dandelion.com -D: XFree86 and BusLogic driver additions +D: BusLogic SCSI driver +D: Miscellaneous kernel fixes S: 3078 Sulphur Spring Court S: San Jose, California 95148 S: USA diff --git a/Documentation/Configure.help b/Documentation/Configure.help index 10682ed64506..f872fbba2533 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -748,14 +748,11 @@ CONFIG_SCSI_AIC7XXX BusLogic SCSI support CONFIG_SCSI_BUSLOGIC - This is support for the BusLogic family of SCSI host adaptors. If - it doesn't work out of the box, you may have to change some settings - in drivers/scsi/buslogic.h. Please read the SCSI-HOWTO, available - via ftp (user: anonymous) at - sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available - as a module ( = code which can be inserted in and removed from the - running kernel whenever you want). If you want to compile it as a - module, say M here and read Documentation/modules.txt. + This is support for BusLogic MultiMaster SCSI Host Adaptors. Consult + the documentation in drivers/scsi/README.BusLogic for more information. + BusLogic FlashPoint SCSI Host Adapters are not supported by this driver. + If this driver does not work correctly without modification, please + consult the author. This driver is not currently available as a module. EATA-DMA (DPT,NEC&ATT for ISA,EISA,PCI) support CONFIG_SCSI_EATA_DMA diff --git a/Documentation/SMP.txt b/Documentation/SMP.txt index 6b537da8bd28..1bb9a49e0091 100644 --- a/Documentation/SMP.txt +++ b/Documentation/SMP.txt @@ -3,9 +3,7 @@ SMP support for Linux with up to 32 processors using the Intel MP specification. WARNING: - This is experimental. Back up your disks first. Build only -with gcc2.5.8. - + This is experimental. Back up your disks first. To fix: @@ -27,4 +25,4 @@ o Distribute irq's (locking present just needs the 82489 to be asked nicely). o 486 startup code. o How to handle mixed FPU/non FPU processors. -o Support 4Mb page mode again +o Support 4Mb page mode again [TESTING] diff --git a/Documentation/networking/net-modules.txt b/Documentation/networking/net-modules.txt index 496108358702..5f0e5930321b 100644 --- a/Documentation/networking/net-modules.txt +++ b/Documentation/networking/net-modules.txt @@ -38,14 +38,54 @@ Wed 2-Aug-95 -------------------------- + 8390 based Network Modules (Paul Gortmaker, Nov 12, 1995) + -------------------------- + +(Includes: smc-ultra, ne, wd, 3c503, hp, hp-plus, e2100 and ac3200) + +The 8390 series of network drivers now support multiple card systems without +reloading the same module multiple times (memory efficient!) This is done by +specifying multiple comma separated values, such as: + + insmod 3c503.o io=0x280,0x300,0x330,0x350 xcvr=0,1,0,1 + +The above would have the one module controlling four 3c503 cards, with card 2 +and 4 using external transcievers. The "insmod" manual describes the usage +of comma separated value lists. + +It is *STRONGLY RECOMMENDED* that you supply "io=" instead of autoprobing. +If an "io=" argument is not supplied, then the ISA drivers will complain +about autoprobing being not recommended, and begrudgingly autoprobe for +a *SINGLE CARD ONLY* -- if you want to use multiple cards you *have* to +supply an "io=0xNNN,0xQQQ,..." argument. + +The ne module is an exception to the above. A NE2000 is essentially an +8390 chip, some bus glue and some RAM. Because of this, the ne probe is +more invasive than the rest, and so at boot we make sure the ne probe is +done last of all the 8390 cards (so that it won't trip over other 8390 based +cards) With modules we can't ensure that all other non-ne 8390 cards have +already been found. Because of this, the ne module REQUIRES an "io=0xNNN" +argument passed in via insmod. It will refuse to autoprobe. + +It is also worth noting that auto-IRQ probably isn't as reliable during +the flurry of interrupt activity on a running machine. Cards such as the +ne2000 that can't get the IRQ setting from an EEPROM or configuration +register are probably best supplied with an "irq=M" argument as well. + + +---------------------------------------------------------------------- +Card/Module List - Configurable Parameters and Default Values +---------------------------------------------------------------------- + 3c501.c: io = 0x280 IO base address irq = 5 IRQ (Probes ports: 0x280, 0x300) 3c503.c: - io = 0x300 - irq = 0 + io = 0 (It will complain if you don't supply an "io=0xNNN") + irq = 0 (IRQ software selected by driver using autoIRQ) + xcvr = 0 (Use xcvr=1 to select external transceiver.) (Probes ports: 0x300, 0x310, 0x330, 0x350, 0x250, 0x280, 0x2A0, 0x2E0) 3c505.c: @@ -69,8 +109,8 @@ Wed 2-Aug-95 (No public options, several other modules need this one) ac3200.c: - io = 0 - irq = 0 + io = 0 (Checks 0x1000 to 0x8fff in 0x1000 intervals) + irq = 0 (Read from config register) (EISA probing..) apricot.c: @@ -101,14 +141,10 @@ atp.c: *Not modularized* (Probes ports: 0x378, 0x278, 0x3BC; fixed IRQs: 5 and 7 ) - -auto_irq.c: *Static kernel component* - - de4x5.c: io = 0x000b irq = 10 - is_not_dec = 0 -- For SMC card using DEC 21140 set this to 1 + is_not_dec = 0 -- For non-DEC card using DEC 21040/21041/21140 chip, set this to 1 (EISA, and PCI probing) de600.c: @@ -130,8 +166,10 @@ dummy.c: No options e2100.c: - io = 0x300 - irq = 0 + io = 0 (It will complain if you don't supply an "io=0xNNN") + irq = 0 (IRQ software selected by driver) + mem = 0 (Override default shared memory start of 0xd0000) + xcvr = 0 (Use xcvr=1 to select external transceiver.) (Probes ports: 0x300, 0x280, 0x380, 0x220) eepro.c: @@ -158,13 +196,13 @@ ewrk3.c: 0x300, 0x340, 0x360, 0x380, 0x3A0, 0x3C0) hp-plus.c: - io = 0x200 - irq = 0 + io = 0 (It will complain if you don't supply an "io=0xNNN") + irq = 0 (IRQ read from configuration register) (Probes ports: 0x200, 0x240, 0x280, 0x2C0, 0x300, 0x320, 0x340) hp.c: - io = 0x300 - irq = 0 + io = 0 (It will complain if you don't supply an "io=0xNNN") + irq = 0 (IRQ software selected by driver using autoIRQ) (Probes ports: 0x300, 0x320, 0x340, 0x280, 0x2C0, 0x200, 0x240) hp100.c: @@ -184,8 +222,8 @@ lance.c: *Not modularized* loopback.c: *Static kernel component* ne.c: - io = 0x300 - irq = 0 + io = 0 (Explicitly *requires* an "io=0xNNN" value) + irq = 0 (Tries to determine configured IRQ via autoIRQ) (Probes ports: 0x300, 0x280, 0x320, 0x340, 0x360) net_init.c: *Static kernel component* @@ -225,8 +263,8 @@ slip.c: smc-ultra.c: - io = 0x200 - irq = 0 + io = 0 (It will complain if you don't supply an "io=0xNNN") + irq = 0 (IRQ val. read from EEPROM) (Probes ports: 0x200, 0x220, 0x240, 0x280, 0x300, 0x340, 0x380) tulip.c: *Partial modularization* @@ -240,11 +278,12 @@ wavelan.c: irq = 0 (Not honoured, if changed..) wd.c: - io = 0x300 - irq = 0 + io = 0 (It will complain if you don't supply an "io=0xNNN") + irq = 0 (IRQ val. read from EEPROM, ancient cards use autoIRQ) mem = 0 (Force shared-memory on address 0xC8000, or whatever..) - (Probes ports: 0x300, 0x280, 0x380, 0x240, - uses AUTOIRQ) + mem_end = 0 (Force non-std. mem. size via supplying mem_end val.) + (eg. for 32k WD8003EBT, use mem=0xd0000 mem_end=0xd8000) + (Probes ports: 0x300, 0x280, 0x380, 0x240) znet.c: *Not modularized* (Only one device on Zenith Z-Note (notebook?) systems, diff --git a/Makefile b/Makefile index 381b7e207778..3413150b37d5 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 1 PATCHLEVEL = 3 -SUBLEVEL = 41 +SUBLEVEL = 42 ARCH = i386 @@ -9,8 +9,7 @@ ARCH = i386 # because it makes re-config very ugly and too many fundamental files depend # on "CONFIG_SMP" # -# NOTE! SMP is experimental, and gcc-2.5.8 is recommended. See the file -# Documentation/SMP. +# NOTE! SMP is experimental. See the file Documentation/SMP.txt # # SMP = 1 @@ -61,6 +60,15 @@ endif ROOT_DEV = CURRENT +# +# NFS_ROOT_NAME specifies the default name of the directory to mount +# as root via NFS, if the kernel does not get the "root=" option from +# the boot loader. The "%s" will be replaced by the IP-number of the +# local system. +# + +NFS_ROOT = -DNFS_ROOT="\"/tftpboot/%s\"" + # # INSTALL_PATH specifies where to place the updated kernel and system map # images. Uncomment if you want to place them anywhere other than root. @@ -126,11 +134,22 @@ endif include arch/$(ARCH)/Makefile +ifdef SMP + +.S.s: + $(CC) -D__ASSEMBLY__ -D__SMP__ -traditional -E -o $*.s $< +.S.o: + $(CC) -D__ASSEMBLY__ -D__SMP__ -traditional -c -o $*.o $< + +else + .S.s: $(CC) -D__ASSEMBLY__ -traditional -E -o $*.s $< .S.o: $(CC) -D__ASSEMBLY__ -traditional -c -o $*.o $< +endif + Version: dummy @rm -f include/linux/compile.h @@ -200,7 +219,7 @@ init/version.o: init/version.c include/linux/compile.h $(CC) $(CFLAGS) -DUTS_MACHINE='"$(ARCH)"' -c -o init/version.o init/version.c init/main.o: init/main.c - $(CC) $(CFLAGS) $(PROFILING) -c -o $*.o $< + $(CC) $(CFLAGS) $(PROFILING) $(NFS_ROOT) -c -o $*.o $< fs: dummy $(MAKE) linuxsubdirs SUBDIRS=fs diff --git a/arch/alpha/kernel/irq.c b/arch/alpha/kernel/irq.c index 6c797791a710..e8f8302287d9 100644 --- a/arch/alpha/kernel/irq.c +++ b/arch/alpha/kernel/irq.c @@ -352,8 +352,39 @@ static inline void device_interrupt(int irq, int ack, struct pt_regs * regs) static inline void isa_device_interrupt(unsigned long vector, struct pt_regs * regs) { - unsigned long pic; +#if defined(CONFIG_ALPHA_APECS) +# define IACK_SC APECS_IACK_SC +#elif defined(CONFIG_ALPHA_LCA) +# define IACK_SC LCA_IACK_SC +#endif int j; + + /* + * Generate a PCI interrupt acknowledge cycle. The PIC will + * respond with the interrupt vector of the highest priority + * interrupt that is pending. The PALcode sets up the + * interrupts vectors such that irq level L generates vector + * L. + */ + j = *(volatile int *) IACK_SC; + j &= 0xff; + if (j == 7) { + if (!(inb(0x20) & 0x80)) { + /* it's only a passive release... */ + return; + } + } + device_interrupt(j, j, regs); +#if 0 + unsigned long pic; + + /* + * It seems to me that the probability of two or more *device* + * interrupts occuring at almost exactly the same time is + * pretty low. So why pay the price of checking for + * additional interrupts here if the common case can be + * handled so much easier? + */ /* * The first read of gives you *all* interrupting lines. * Therefore, read the mask register and and out those lines @@ -369,6 +400,7 @@ static inline void isa_device_interrupt(unsigned long vector, pic &= pic - 1; device_interrupt(j, j, regs); } +#endif } static inline void cabriolet_and_eb66p_device_interrupt(unsigned long vector, diff --git a/arch/i386/Makefile b/arch/i386/Makefile index 0b6d7f378ad0..b90dc0bea4a3 100644 --- a/arch/i386/Makefile +++ b/arch/i386/Makefile @@ -56,6 +56,10 @@ CFLAGS := $(CFLAGS) -m386 endif endif +ifdef SMP +CFLAGS := $(CFLAGS) -D__SMP__ +endif + HEAD := arch/i386/kernel/head.o SUBDIRS := $(SUBDIRS) arch/i386/kernel arch/i386/mm arch/i386/lib diff --git a/arch/i386/boot/Makefile b/arch/i386/boot/Makefile index c0a77a028eef..2af754e371ae 100644 --- a/arch/i386/boot/Makefile +++ b/arch/i386/boot/Makefile @@ -15,6 +15,10 @@ ifdef CONFIG_KERNEL_ELF CFLAGS := $(CFLAGS) -D__BFD__ endif +ifdef SMP +CFLAGS := $(CFLAGS) -D__SMP__ +endif + zImage: $(CONFIGURE) bootsect setup compressed/vmlinux tools/build ifdef CONFIG_KERNEL_ELF $(OBJDUMP) $(OBJDUMP_FLAGS) -o $(ZIMAGE_OFFSET) compressed/vmlinux > compressed/vmlinux.out diff --git a/arch/i386/boot/bootsect.S b/arch/i386/boot/bootsect.S index f6a0d315881e..6ed9a6607452 100644 --- a/arch/i386/boot/bootsect.S +++ b/arch/i386/boot/bootsect.S @@ -47,7 +47,7 @@ SWAP_DEV = 0 #define RAMDISK 0 #endif #ifndef CONFIG_ROOT_RDONLY -#define CONFIG_ROOT_RDONLY 0 +#define CONFIG_ROOT_RDONLY 1 #endif ! ld86 requires an entry symbol. This may as well be the usual one. diff --git a/arch/i386/boot/compressed/Makefile b/arch/i386/boot/compressed/Makefile index 326f5443cce4..91407065c974 100644 --- a/arch/i386/boot/compressed/Makefile +++ b/arch/i386/boot/compressed/Makefile @@ -11,6 +11,10 @@ OBJECTS = $(HEAD) inflate.o unzip.o misc.o CFLAGS = -O2 -DSTDC_HEADERS +ifdef SMP +CFLAGS := $(CFLAGS) -D__SMP__ +endif + ifdef CONFIG_KERNEL_ELF TARGET=--target elf32-i386 INPUT_DATA=input_data @@ -22,9 +26,18 @@ all: vmlinux vmlinux: piggy.o $(OBJECTS) $(LD) $(ZLINKFLAGS) -o vmlinux $(OBJECTS) piggy.o +ifdef SMP + +head.o: head.S $(TOPDIR)/include/linux/tasks.h + $(CC) -D__SMP__ -traditional -c head.S + +else + head.o: head.S $(TOPDIR)/include/linux/tasks.h $(CC) -traditional -c head.S +endif + ifdef CONFIG_KERNEL_ELF # You cannot compress a file and have the kernel uncompress it, it must diff --git a/arch/i386/boot/setup.S b/arch/i386/boot/setup.S index d12951a79e50..c1a327c9a43a 100644 --- a/arch/i386/boot/setup.S +++ b/arch/i386/boot/setup.S @@ -1,31 +1,32 @@ ! ! setup.S Copyright (C) 1991, 1992 Linus Torvalds ! -! setup.s is responsible for getting the system data from the BIOS, -! and putting them into the appropriate places in system memory. -! both setup.s and system has been loaded by the bootblock. -! -! This code asks the bios for memory/disk/other parameters, and -! puts them in a "safe" place: 0x90000-0x901FF, ie where the -! boot-block used to be. It is then up to the protected mode +! This code performs all initialization procedures that should be done +! before entering the protected mode. It's responsible for getting of all +! system data offered by BIOS and for detection / selection of video +! mode. All information is put in a "safe" place: 0x90000-0x901FF, i. e. +! where the boot-block used to be. It is then up to the protected mode ! system to read them from there before the area is overwritten ! for buffer-blocks. ! ! Move PS/2 aux init code to psaux.c ! (troyer@saifr00.cfsat.Honeywell.COM) 03Oct92 ! -! some changes and additional features by Christoph Niemann, +! Some changes and additional features by Christoph Niemann, ! March 1993/June 1994 (Christoph.Niemann@linux.org) ! +! Completely new video-mode handling code, VESA mode detection, support +! for new Cirrus Logic cards and some additional changes +! by Martin Mares , October 1995. +! -! NOTE! These had better be the same as in bootsect.s! +! NOTE! These had better be the same as in bootsect.S! #define __ASSEMBLY__ #include #include -#ifndef SVGA_MODE -#define SVGA_MODE ASK_VGA -#endif +! Uncomment this if you want the BIOS mode numbers to be listed +!#define SHOW_BIOS_MODES ! Signature words to ensure LILO loaded us right #define SIG1 0xAA55 @@ -46,6 +47,7 @@ begbss: entry start start: + ! Bootlin depends on this being done early mov ax,#0x01500 mov dl,#0x81 @@ -72,12 +74,14 @@ fin: ret ! Part of above routine, this one just prints ascii al prnt1: push ax + push bx push cx xor bh,bh mov cx,#0x01 mov ah,#0x0e int 0x10 pop cx + pop bx pop ax ret @@ -145,43 +149,10 @@ good_sig: xor bx,bx ! clear bx int 0x16 -! check for EGA/VGA and some config parameters +! Check for video adapter and its parameters +! Video mode selection is also handled here - mov ah,#0x12 - mov bl,#0x10 - int 0x10 - mov [8],ax - mov [10],bx - mov [12],cx - mov ax,#0x5019 - cmp bl,#0x10 - je novga - mov ax,#0x1a00 ! Added check for EGA/VGA discrimination - int 0x10 - mov bx,ax - mov ax,#0x5019 - movb [15],#0 ! by default, no VGA - cmp bl,#0x1a ! 1a means VGA, anything else EGA or lower - jne novga - movb [15],#1 ! we've detected a VGA - call chsvga -novga: mov [14],al - mov ah,#0x03 ! read cursor pos - xor bh,bh ! clear bh - int 0x10 ! save it in known place, con_init fetches - mov [0],dx ! it from 0x90000. - -! Get video-card data: - - mov ah,#0x0f - int 0x10 - mov [4],bx ! bh = display page - mov [6],ax ! al = video mode, ah = window width - xor ax,ax - mov es,ax ! Access low memory - seg es - mov ax,[0x485] ! POINTS - Height of character matrix - mov [16],ax + call video ! Get hd0 data @@ -334,17 +305,16 @@ end_move: ! Well, now's the time to actually move into protected mode. To make ! things as simple as possible, we do no register set-up or anything, ! we let the gnu-compiled 32-bit programs do that. We just jump to -! absolute address 0x00000, in 32-bit protected mode. +! absolute address 0x10000, in 32-bit protected mode. ! ! Note that the short jump isn't strictly needed, although there are ! reasons why it might be a good idea. It won't hurt in any case. ! - xor ax,ax - inc ax ! protected mode (PE) bit + mov ax,#1 ! protected mode (PE) bit lmsw ax ! This is it! jmp flush_instr flush_instr: - mov bx,#0 ! Flag to indicate a boot + xor bx,bx ! Flag to indicate a boot jmpi 0x1000,KERNEL_CS ! jmp offset 1000 of segment 0x10 (cs) ! This routine checks that the keyboard command queue is empty @@ -364,61 +334,6 @@ no_output: test al,#2 ! is input buffer full? jnz empty_8042 ! yes - loop ret -! -! Read a key and return the (US-)ascii code in al, scan code in ah -! -getkey: - xor ah,ah - int 0x16 - ret - -! -! Read a key with a timeout of 30 seconds. The cmos clock is used to get -! the time. -! -getkt: - call gettime - add al,#30 ! wait 30 seconds - cmp al,#60 - jl lminute - sub al,#60 -lminute: - mov cl,al -again: mov ah,#0x01 - int 0x16 - jnz getkey ! key pressed, so get it - call gettime - cmp al,cl - jne again - mov al,#0x20 ! timeout, return default char `space' - ret - -! -! Flush the keyboard buffer -! -flush: mov ah,#0x01 - int 0x16 - jz empty - xor ah,ah - int 0x16 - jmp flush -empty: ret - -! -! Read the cmos clock. Return the seconds in al -! -gettime: - push cx - mov ah,#0x02 - int 0x1a - mov al,dh ! dh contains the seconds - and al,#0x0f - mov ah,dh - mov cl,#0x04 - shr ah,cl - aad - pop cx - ret ! ! Delay is needed after doing i/o @@ -427,43 +342,199 @@ delay: .word 0x00eb ! jmp $+2 ret -! Routine trying to recognize type of SVGA-board present (if any) -! and if it recognize one gives the choices of resolution it offers. -! If one is found the resolution chosen is given by al,ah (rows,cols). +! +! Video card / mode detection. We do some hardware testing and build +! a video mode list (placed directly after our code and data). Then we +! choose the right mode given in the configuration or ask the user if +! we are requested to do so. After all, all video parameters are stored +! for later perusal by the kernel. +! -chsvga: cld +video: movb [15],#0 ! Default is no VGA + mov ax,[0x01fa] push ds + push ds + pop fs ! In this routine: FS=orig. DS push cs - mov ax,[0x01fa] - pop ds + pop ds ! ES=DS=CS + push cs + pop es mov modesave,ax - mov ax,#0xc000 - mov es,ax - mov ax,modesave - cmp ax,#NORMAL_VGA - je defvga - cmp ax,#EXTENDED_VGA - je vga50 + lea di,modelist ! ES:DI points to current item in our mode list + mov eax,#0x50190000 ! Store current mode: 80x25 + cld + stosd + + mov ah,#0x12 ! Check EGA/VGA + mov bl,#0x10 + int 0x10 + seg fs + mov [10],bx ! Used for identification of VGA in the kernel + cmp bl,#0x10 ! Not EGA nor VGA -> 80x25 only + je selmd1 + + mov eax,#0x5032FFFF ! EGA or VGA: 80x50 supported + stosd + + mov ax,#0x1a00 ! Added check for EGA/VGA discrimination + int 0x10 + cmp al,#0x1a ! 1a means VGA, anything else EGA + jne selmd1 + seg fs + movb [15],#1 ! We've detected a VGA + + mov eax,#0x501cFFFE ! VGA: 80x28 supported + stosd + + lea si,vgatable ! Test all known SVGA adapters +dosvga: lodsw + mov bp,ax ! Default mode table + or ax,ax + jz didsv + lodsw ! Pointer to test routine + push si + push di + push es + mov bx,#0xc000 + mov es,bx + call ax ! Call test routine + pop es + pop di + pop si + or bp,bp + jz dosvga + mov si,bp ! Found, copy the modes + mov ah,#0 +cpsvga: lodsb + or al,al + jz didsv + stosw + movsw + jmp cpsvga + +selmd1: jmp selmd + +didsv: mov ax,#0x4f00 ! Fetch VESA information to ES:DI+0x400 + add di,#0x400 + int 0x10 + sub di,#0x400 + cmp al,#0x4f + jne selmd + lgs bx,(di+0x40e) + cmp (di+0x400),#0x4556 + jne selmd + cmp (di+0x402),#0x4153 + jne selmd + +vesa1: seg gs + mov cx,(bx) + add bx,#2 + cmp cx,#0xffff + je selmd + mov ax,#0x4f01 + add di,#0xc00 + int 0x10 + sub di,#0xc00 + cmp al,#0x4f + jne selmd + testb (di+0xc00),#0x10 ! Is it a text mode? + jne vesa1 + testb (di+0xc00),#0x08 ! Has it colors? + je vesa1 + mov dh,(di+0xc12) ! DX=dimensions, CX=mode + mov dl,(di+0xc14) + + lea si,modelist ! Check if it's already on the list +vesa2: lodsw + lodsw + cmp ax,dx + je vesa1 + cmp si,di + jc vesa2 + + mov ax,cx ! New mode, store it + stosw + mov ax,dx + stosw + jmp vesa1 + +! +! Video mode table built. Determine the mode we should use and set it. +! +selmd: mov ax,modesave + cmp ax,#NORMAL_VGA ! Current mode (80x25) + je defmd1 + cmp ax,#EXTENDED_VGA ! 80x50 mode + je try50 cmp ax,#ASK_VGA - jne svga - lea si,msg1 + jne usemd +banner: lea si,keymsg call prtstr call flush nokey: call getkt - cmp al,#0x0d ! enter ? - je svga ! yes - svga selection - cmp al,#0x20 ! space ? - je defvga ! no - repeat + cmp al,#0x0d ! ENTER ? + je listm ! yes - manual mode selection + cmp al,#0x20 ! SPACE ? + je defmd1 ! no - repeat call beep jmp nokey -defvga: mov ax,#0x5019 - pop ds - ret -/* extended vga mode: 80x50 */ -vga50: - mov ax,#0x1112 + +defmd1: br defmd + +listm: call listmodes ! List all available modes +keymd: call getkey ! Get key representing mode ID + xor ah,ah + sub al,#0x30 + jc keymd + cmp al,#10 + jc usemd + sub al,#39 + cmp al,#10 + jc keymd + cmp al,#26 + jnc keymd + jmp usemd + +try50: mov ax,#1 ! 80x50 is mode #1 +usemd: shl ax,#2 ! We're requested to set mode in AX + lea si,modelist + add si,ax + cmp si,di + jc mdok + cmp modesave,#ASK_VGA + je keymd + lea si,undefd + call prtstr + jmp banner + +mdok: lodsw ! AX=mode number + cmp ah,#0xff + jz mdspec + or ax,ax + jz mdsetd + or ah,ah + jz mdset + mov bx,ax + mov ax,#0x4f02 +mdset: int 0x10 +mdsetd: lodsb ! AL=number of lines + jmp getpar + +mdspec: inc ax ! Special modes + jz m80x50 + +m80x28: mov ax,#0x1111 ! Setting 80x28 (VGA with EGA font) + xor bl,bl + int 0x10 ! use 9x14 fontset (28 lines on VGA) + mov ah,#0x01 + mov cx,#0x0b0c + int 0x10 ! turn on cursor (scan lines 11 to 12) + mov al,#28 + jmp getpar + +m80x50: mov ax,#0x1112 ! Setting 80x50 (EGA/VGA) xor bl,bl - int 0x10 ! use 8x8 font set (50 lines on VGA) + int 0x10 ! use 8x8 font set mov ax,#0x1200 mov bl,#0x20 int 0x10 ! use alternate print screen @@ -473,28 +544,65 @@ vga50: mov ah,#0x01 mov cx,#0x0607 int 0x10 ! turn on cursor (scan lines 6 to 7) - pop ds - mov ax,#0x5032 ! return 80x50 - ret -/* extended vga mode: 80x28 */ -vga28: - pop ax ! clean the stack - mov ax,#0x1111 - xor bl,bl - int 0x10 ! use 9x14 fontset (28 lines on VGA) - mov ah, #0x01 - mov cx,#0x0b0c - int 0x10 ! turn on cursor (scan lines 11 to 12) - pop ds - mov ax,#0x501c ! return 80x28 - ret -/* svga modes */ + mov al,#50 + jmp getpar + +defmd: mov al,#25 ! Default is 25 lines + +! +! Correct video mode set. Determine the remaining parameters. +! + +getpar: pop ds ! Restore original DS + mov [14],al ! Number of lines + + mov ah,#0x03 ! read cursor pos + xor bh,bh ! clear bh + int 0x10 ! save it in known place, con_init fetches + mov [0],dx ! it from 0x90000. + + mov ah,#0x0f + int 0x10 + mov [4],bx ! bh = display page + mov [6],ax ! al = video mode, ah = window width + xor ax,ax + mov es,ax ! Access low memory + seg es + mov ax,[0x485] ! POINTS - Height of character matrix + mov [16],ax + + ret ! Well done... + +! +! Table of all known SVGA cards. For each card, we store a pointer to +! a table of video modes supported by the card and a pointer to a routine +! used for testing of presence of the card. ! -! test for presence of an S3 VGA chip. The algorithm was taken -! from the SuperProbe package of XFree86 1.2.1 -! report bugs to Christoph.Niemann@linux.org + +vgatable: + .word s3_md, s3_test + .word ati_md, ati_test + .word ahead_md, ahead_test + .word chips_md, chips_test + .word cirrus2_md, cirrus2_test + .word cirrus1_md, cirrus1_test + .word everex_md, everex_test + .word genoa_md, genoa_test + .word oak_md, oak_test + .word paradise_md, paradise_test + .word trident_md, trident_test + .word tseng_md, tseng_test + .word video7_md, video7_test + .word 0 + ! -svga: cld +! Test routines and mode tables: +! + +! S3 - The test algorithm was taken from the SuperProbe package +! for XFree86 1.2.1. Report bugs to Christoph.Niemann@linux.org + +s3_test: mov cx,#0x0f35 ! we store some constants in cl/ch mov dx,#0x03d4 movb al,#0x38 @@ -559,29 +667,64 @@ s3_1: mov ax,#0x4838 ! allow writing to special regs by putting repne scasb je no_s31 - lea si,dsc_S3 ! table of descriptions of video modes for BIOS - lea di,mo_S3 ! table of sizes of video modes for my BIOS movb ah,bh movb al,#0x38 - call outidx ! restore old value of CRT register 0x38 - br selmod ! go ask for video mode + jmp s3rest no_s3: movb al,#0x35 ! restore CRT register 0x35 movb ah,bl call outidx -no_s31: movb ah,bh - movb al,#0x38 - call outidx ! restore old value of CRT register 0x38 +no_s31: xor bp,bp ! Detection failed +s3rest: movb ah,bh + movb al,#0x38 ! restore old value of CRT register 0x38 +outidx: out dx,al ! Write to indexed VGA register + push ax ! AL=index, AH=data, DX=index reg port + mov al,ah + inc dx + out dx,al + dec dx + pop ax + ret - lea si,idati ! Check ATI 'clues' +tstidx: out dx,ax ! OUT DX,AX and inidx +inidx: out dx,al ! Read from indexed VGA register + inc dx ! AL=index, DX=index reg port -> AL=data + in al,dx + dec dx + ret + +idS3: .byte 0x81, 0x82, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95 + .byte 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa8, 0xb0 + +s3_md: .byte 0x54, 0x2b, 0x84 + .byte 0x55, 0x19, 0x84 + .byte 0 + +! ATI cards. + +ati_test: + lea si,idati mov di,#0x31 mov cx,#0x09 repe cmpsb - jne noati - lea si,dscati - lea di,moati - br selmod -noati: mov ax,#0x200f ! Check Ahead 'clues' + je atiok + xor bp,bp +atiok: ret + +idati: .ascii "761295520" + +ati_md: .byte 0x23, 0x19, 0x84 + .byte 0x33, 0x2c, 0x84 + .byte 0x22, 0x1e, 0x64 + .byte 0x21, 0x19, 0x64 + .byte 0x58, 0x21, 0x50 + .byte 0x5b, 0x1e, 0x50 + .byte 0 + +! AHEAD + +ahead_test: + mov ax,#0x200f mov dx,#0x3ce out dx,ax inc dx @@ -589,11 +732,23 @@ noati: mov ax,#0x200f ! Check Ahead 'clues' cmp al,#0x20 je isahed cmp al,#0x21 - jne noahed -isahed: lea si,dscahead - lea di,moahead - br selmod -noahed: mov dx,#0x3c3 ! Check Chips & Tech. 'clues' + je isahed + xor bp,bp +isahed: ret + +ahead_md: + .byte 0x22, 0x2c, 0x84 + .byte 0x23, 0x19, 0x84 + .byte 0x24, 0x1c, 0x84 + .byte 0x2f, 0x32, 0xa0 + .byte 0x32, 0x22, 0x50 + .byte 0x34, 0x42, 0x50 + .byte 0 + +! Chips & Tech. + +chips_test: + mov dx,#0x3c3 in al,dx or al,#0x10 out dx,al @@ -604,12 +759,20 @@ noahed: mov dx,#0x3c3 ! Check Chips & Tech. 'clues' in al,dx and al,#0xef out dx,al - cmp bl,[idcandt] - jne nocant - lea si,dsccandt - lea di,mocandt - br selmod -nocant: mov dx,#0x3d4 ! Check Cirrus 'clues' + cmp bl,#0xa5 + je cantok + xor bp,bp +cantok: ret + +chips_md: + .byte 0x60, 0x19, 0x84 + .byte 0x61, 0x32, 0x84 + .byte 0 + +! Cirrus Logic 5X0 + +cirrus1_test: + mov dx,#0x3d4 mov al,#0x0c out dx,al inc dx @@ -642,19 +805,76 @@ nocant: mov dx,#0x3d4 ! Check Cirrus 'clues' out dx,al in al,dx cmp al,#0x01 - jne nocirr - call rst3d4 - lea si,dsccirrus - lea di,mocirrus - br selmod -rst3d4: mov dx,#0x3d4 + je iscirr +nocirr: xor bp,bp +iscirr: mov dx,#0x3d4 mov al,bl xor ah,ah shl ax,#8 add ax,#0x0c out dx,ax - ret -nocirr: call rst3d4 ! Check Everex 'clues' + ret + +cirrus1_md: + .byte 0x1f, 0x19, 0x84 + .byte 0x20, 0x2c, 0x84 + .byte 0x22, 0x1e, 0x84 + .byte 0x31, 0x25, 0x64 + .byte 0 + +! Cirrus Logic 54XX + +cirrus2_test: + mov dx,#0x3c4 + mov al,#6 + call inidx + mov bl,al ! BL=backup + mov al,#6 + xor ah,ah + call tstidx + cmp al,#0x0f + jne c2fail + mov ax,#0x1206 + call tstidx + cmp al,#0x12 + jne c2fail + mov al,#0x1e + call inidx + mov bh,al + and bh,#0xc0 + mov ah,bh + mov al,#0x1e + call tstidx + xor al,bh + and al,#0x3f + jne c2xx + mov al,#0x1e + mov ah,bh + or ah,#0x3f + call tstidx + xor al,bh + xor al,#0x3f + and al,#0x3f +c2xx: pushf + mov al,#0x1e + mov ah,bh + out dx,ax + popf + je c2done +c2fail: xor bp,bp +c2done: mov al,#6 + mov ah,bl + out dx,ax + ret + +cirrus2_md: + .byte 0x14, 0x19, 0x84 + .byte 0x54, 0x2b, 0x84 + .byte 0 + +! Everex / Trident + +everex_test: mov ax,#0x7000 xor bx,bx int 0x10 @@ -662,15 +882,32 @@ nocirr: call rst3d4 ! Check Everex 'clues' jne noevrx shr dx,#4 cmp dx,#0x678 - je istrid + je evtrid cmp dx,#0x236 - je istrid - lea si,dsceverex - lea di,moeverex - br selmod -istrid: lea cx,ev2tri - jmp cx -noevrx: lea si,idgenoa ! Check Genoa 'clues' + jne evrxok +evtrid: lea bp,trident_md +evrxok: ret + +noevrx: xor bp,bp + ret + +everex_md: + .byte 0x03, 0x22, 0x50 + .byte 0x04, 0x3c, 0x50 + .byte 0x07, 0x2b, 0x64 + .byte 0x08, 0x4b, 0x64 + .byte 0x0a, 0x19, 0x84 + .byte 0x0b, 0x2c, 0x84 + .byte 0x16, 0x1e, 0x50 + .byte 0x18, 0x1b, 0x64 + .byte 0x21, 0x40, 0xa0 + .byte 0x40, 0x1e, 0x84 + .byte 0 + +! Genoa. + +genoa_test: + lea si,idgenoa ! Check Genoa 'clues' xor ax,ax seg es mov al,[0x37] @@ -686,32 +923,72 @@ l1: inc si seg es cmp al,(di) l2: loope l1 - cmp cx,#0x00 - jne nogen - lea si,dscgenoa - lea di,mogenoa - br selmod -nogen: cld + or cx,cx + je isgen + xor bp,bp +isgen: ret + +idgenoa: .byte 0x77, 0x00, 0x99, 0x66 + +genoa_md: + .byte 0x58, 0x20, 0x50 + .byte 0x5a, 0x2a, 0x64 + .byte 0x60, 0x19, 0x84 + .byte 0x61, 0x1d, 0x84 + .byte 0x62, 0x20, 0x84 + .byte 0x63, 0x2c, 0x84 + .byte 0x64, 0x3c, 0x84 + .byte 0x6b, 0x4f, 0x64 + .byte 0x72, 0x3c, 0x50 + .byte 0x74, 0x42, 0x50 + .byte 0x78, 0x4b, 0x64 + .byte 0 + +! OAK + +oak_test: lea si,idoakvga mov di,#0x08 mov cx,#0x08 repe cmpsb - jne nooak - lea si,dscoakvga - lea di,mooakvga - br selmod -nooak: cld - lea si,idparadise ! Check Paradise 'clues' + je isoak + xor bp,bp +isoak: ret + +idoakvga: .ascii "OAK VGA " + +oak_md: .byte 0x4e, 0x3c, 0x50 + .byte 0x4f, 0x3c, 0x84 + .byte 0x50, 0x19, 0x84 + .byte 0x51, 0x2b, 0x84 + .byte 0 + +! WD Paradise. + +paradise_test: + lea si,idparadise mov di,#0x7d mov cx,#0x04 repe cmpsb - jne nopara - lea si,dscparadise - lea di,moparadise - br selmod -nopara: mov dx,#0x3c4 ! Check Trident 'clues' + je ispara + xor bp,bp +ispara: ret + +idparadise: .ascii "VGA=" + +paradise_md: + .byte 0x41, 0x22, 0x50 + .byte 0x47, 0x1c, 0x84 + .byte 0x55, 0x19, 0x84 + .byte 0x54, 0x2c, 0x84 + .byte 0 + +! Trident. + +trident_test: + mov dx,#0x3c4 mov al,#0x0e out dx,al inc dx @@ -730,11 +1007,24 @@ setb2: or al,#0x02 ! clrb2: out dx,al and ah,#0x0f cmp ah,#0x02 - jne notrid -ev2tri: lea si,dsctrident - lea di,motrident - jmp selmod -notrid: mov dx,#0x3cd ! Check Tseng 'clues' + je istrid + xor bp,bp +istrid: ret + +trident_md: + .byte 0x50, 0x1e, 0x50 + .byte 0x51, 0x2b, 0x50 + .byte 0x52, 0x3c, 0x50 + .byte 0x57, 0x19, 0x84 + .byte 0x58, 0x1e, 0x84 + .byte 0x59, 0x2b, 0x84 + .byte 0x5a, 0x3c, 0x84 + .byte 0 + +! Tseng. + +tseng_test: + mov dx,#0x3cd in al,dx ! Could things be this simple ! :-) mov bl,al mov al,#0x55 @@ -744,11 +1034,22 @@ notrid: mov dx,#0x3cd ! Check Tseng 'clues' mov al,bl out dx,al cmp ah,#0x55 - jne notsen - lea si,dsctseng - lea di,motseng - jmp selmod -notsen: mov dx,#0x3cc ! Check Video7 'clues' + je istsen + xor bp,bp +istsen: ret + +tseng_md: + .byte 0x26, 0x3c, 0x50 + .byte 0x2a, 0x28, 0x64 + .byte 0x23, 0x19, 0x84 + .byte 0x24, 0x1c, 0x84 + .byte 0x22, 0x2c, 0x84 + .byte 0 + +! Video7. + +video7_test: + mov dx,#0x3cc in al,dx mov dx,#0x3b4 and al,#0x01 @@ -777,115 +1078,78 @@ even7: mov al,#0x0c mov al,#0x55 xor al,#0xea cmp al,bh - jne novid7 - lea si,dscvideo7 - lea di,movideo7 - jmp selmod -novid7: lea si,dsunknown - lea di,mounknown -selmod: xor cx,cx - mov cl,(di) - mov ax,modesave - cmp ax,#ASK_VGA - je askmod - cmp ax,#NORMAL_VGA - je askmod - cmp al,cl - jl gotmode - push si - lea si,msg4 + je isvid7 + xor bp,bp +isvid7: ret + +video7_md: + .byte 0x40, 0x2b, 0x50 + .byte 0x43, 0x3c, 0x50 + .byte 0x44, 0x3c, 0x64 + .byte 0x41, 0x19, 0x84 + .byte 0x42, 0x2c, 0x84 + .byte 0x45, 0x1c, 0x84 + .byte 0 + +! +! Displaying of the mode list. +! + +listmodes: + lea si,listhdr call prtstr - pop si -askmod: push si - lea si,msg2 + lea bx,modelist + mov cl,#0x30 +listm1: mov modenr,cl + lea si,modestring call prtstr - pop si - push si - push cx -tbl: pop bx - push bx - mov al,bl - sub al,cl - call modepr - lodsw - xchg al,ah + mov al,(bx+3) call dprnt - xchg ah,al - push ax mov al,#0x78 call prnt1 - pop ax + mov al,(bx+2) call dprnt - push si - lea si,crlf ! print CR+LF - call prtstr - pop si - loop tbl - pop cx - lea si,msg3 +#ifdef SHOW_BIOS_MODES + mov al,#0x20 + call prnt1 + mov al,#0x28 + call prnt1 + mov al,(bx+1) + call prthex + mov al,(bx) + call prthex + mov al,#0x29 + call prnt1 +#endif + lea si,crlf call prtstr - pop si - add cl,#0x30 - jmp nonum -nonumb: call beep -nonum: call getkey - cmp al,#0x30 ! ascii `0' - jb nonumb - cmp al,#0x3a ! ascii `9' - jbe number - cmp al,#0x61 ! ascii `a' - jb nonumb - cmp al,#0x7a ! ascii `z' - ja nonumb - sub al,#0x27 - cmp al,cl - jae nonumb - sub al,#0x30 - jmp gotmode -number: cmp al,cl - jae nonumb - sub al,#0x30 -gotmode: xor ah,ah - or al,al - beq vga50 - push ax - dec ax - beq vga28 - add di,ax - mov al,(di) - int 0x10 - pop ax - shl ax,#1 - add si,ax - lodsw - pop ds - ret - -! Routine to write al into a VGA-register that is -! accessed via an index register -! -! dx contains the address of the index register -! al contains the index -! ah contains the value to write to the data register (dx + 1) -! -! no registers are changed - -outidx: out dx,al - push ax - mov al,ah - inc dx - out dx,al - dec dx + add bx,#4 + inc cl + cmp cl,#0x3a + jnz listm2 + mov cl,#0x61 +listm2: cmp bx,di + jc listm1 + lea si,prompt + br prtstr + +! Routine to print a hexadecimal byte (AL) on screen. + +#ifdef SHOW_BIOS_MODES +prthex: push ax + shr al,#4 + call prth1 pop ax - ret -inidx: out dx,al - inc dx - in al,dx - dec dx - ret +prth1: and al,#15 + cmp al,#10 + jc prth2 + add al,#7 +prth2: add al,#0x30 + br prnt1 +#endif ! Routine to print a decimal value on screen, the value to be -! printed is put in al (i.e 0-255). +! printed is put in AL (i.e 0-255). dprnt: push ax push cx @@ -906,25 +1170,65 @@ skip10: mov al,ah ret ! -! Routine to print the mode number key on screen. Mode numbers -! 0-9 print the ascii values `0' to '9', 10-35 are represented by -! the letters `a' to `z'. This routine prints some spaces around the -! mode no. +! Read a key and return the (US-)ascii code in al, scan code in ah ! +getkey: + xor ah,ah + int 0x16 + ret -modepr: push ax - cmp al,#0x0a - jb digit ! Here is no check for number > 35 - add al,#0x27 -digit: add al,#0x30 - mov modenr, al - push si - lea si, modestring - call prtstr - pop si - pop ax +! +! Read a key with a timeout of 30 seconds. The cmos clock is used to get +! the time. +! +getkt: + call gettime + add al,#30 ! wait 30 seconds + cmp al,#60 + jl lminute + sub al,#60 +lminute: + mov cl,al +again: mov ah,#0x01 + int 0x16 + jnz getkey ! key pressed, so get it + call gettime + cmp al,cl + jne again + mov al,#0x20 ! timeout, return default char `space' ret +! +! Flush the keyboard buffer +! +flush: mov ah,#0x01 + int 0x16 + jz empty + xor ah,ah + int 0x16 + jmp flush +empty: ret + +! +! Read the cmos clock. Return the seconds in al +! +gettime: + push cx + mov ah,#0x02 + int 0x1a + mov al,dh ! dh contains the seconds + and al,#0x0f + mov ah,dh + mov cl,#0x04 + shr ah,cl + aad + pop cx + ret + +! +! Descriptor table for our protected mode transition. +! + gdt: .word 0,0,0,0 ! dummy @@ -948,69 +1252,37 @@ gdt_48: .word 0x800 ! gdt limit=2048, 256 GDT entries .word 512+gdt,0x9 ! gdt base = 0X9xxxx -msg1: .ascii "Press to see SVGA-modes available, to continue or wait 30 secs." - db 0x0d, 0x0a, 0x0a, 0x00 -msg2: .ascii "Mode: COLSxROWS:" +! +! Assorted messages. +! + +keymsg: .ascii "Press to see video modes available, to continue or wait 30 secs" + db 0x0d, 0x0a, 0x00 +listhdr: .ascii "Mode: COLSxROWS:" db 0x0d, 0x0a, 0x0a, 0x00 -msg3: db 0x0d, 0x0a +prompt: db 0x0d, 0x0a .ascii "Choose mode by pressing the corresponding number or letter." crlf: db 0x0d, 0x0a, 0x00 -msg4: .ascii "You passed an undefined mode number to setup. Please choose a new mode." +undefd: .ascii "You passed an undefined mode number to setup. Please choose a new mode." db 0x0d, 0x0a, 0x0a, 0x07, 0x00 modestring: .ascii " " modenr: db 0x00 ! mode number .ascii ": " db 0x00 - -idati: .ascii "761295520" -idcandt: .byte 0xa5 -idgenoa: .byte 0x77, 0x00, 0x99, 0x66 -idparadise: .ascii "VGA=" -idoakvga: .ascii "OAK VGA " -idS3: .byte 0x81, 0x82, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95 - .byte 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa8, 0xb0 - -! Manufacturer: Numofmodes+2: Mode: -! Number of modes is the number of chip-specific svga modes plus the extended -! modes available on any vga (currently 2) - -moati: .byte 0x06, 0x23, 0x33, 0x22, 0x21 -moahead: .byte 0x07, 0x22, 0x23, 0x24, 0x2f, 0x34 -mocandt: .byte 0x04, 0x60, 0x61 -mocirrus: .byte 0x06, 0x1f, 0x20, 0x22, 0x31 -moeverex: .byte 0x0c, 0x03, 0x04, 0x07, 0x08, 0x0a, 0x0b, 0x16, 0x18, 0x21, 0x40 -mogenoa: .byte 0x0c, 0x58, 0x5a, 0x60, 0x61, 0x62, 0x63, 0x64, 0x72, 0x74, 0x78 -moparadise: .byte 0x04, 0x55, 0x54 -motrident: .byte 0x09, 0x50, 0x51, 0x52, 0x57, 0x58, 0x59, 0x5a -motseng: .byte 0x07, 0x26, 0x2a, 0x23, 0x24, 0x22 -movideo7: .byte 0x08, 0x40, 0x43, 0x44, 0x41, 0x42, 0x45 -mooakvga: .byte 0x08, 0x00, 0x07, 0x4e, 0x4f, 0x50, 0x51 -mo_S3: .byte 0x04, 0x54, 0x55 -mounknown: .byte 0x02 - -! msb = Cols lsb = Rows: -! The first two modes are standard vga modes available on any vga. -! mode 0 is 80x50 and mode 1 is 80x28 - -dscati: .word 0x5032, 0x501c, 0x8419, 0x842c, 0x641e, 0x6419 -dscahead: .word 0x5032, 0x501c, 0x842c, 0x8419, 0x841c, 0xa032, 0x5042 -dsccandt: .word 0x5032, 0x501c, 0x8419, 0x8432 -dsccirrus: .word 0x5032, 0x501c, 0x8419, 0x842c, 0x841e, 0x6425 -dsceverex: .word 0x5032, 0x501c, 0x5022, 0x503c, 0x642b, 0x644b, 0x8419, 0x842c, 0x501e, 0x641b, 0xa040, 0x841e -dscgenoa: .word 0x5032, 0x501c, 0x5020, 0x642a, 0x8419, 0x841d, 0x8420, 0x842c, 0x843c, 0x503c, 0x5042, 0x644b -dscparadise: .word 0x5032, 0x501c, 0x8419, 0x842c -dsctrident: .word 0x5032, 0x501c, 0x501e, 0x502b, 0x503c, 0x8419, 0x841e, 0x842b, 0x843c -dsctseng: .word 0x5032, 0x501c, 0x503c, 0x6428, 0x8419, 0x841c, 0x842c -dscvideo7: .word 0x5032, 0x501c, 0x502b, 0x503c, 0x643c, 0x8419, 0x842c, 0x841c -dscoakvga: .word 0x5032, 0x501c, 0x2819, 0x5019, 0x503c, 0x843c, 0x8419, 0x842b -dsc_S3: .word 0x5032, 0x501c, 0x842b, 0x8419 -dsunknown: .word 0x5032, 0x501c -modesave: .word SVGA_MODE + +modesave: .word 0 ! Requsted mode ID. ! This must be last setup_sig1: .word SIG1 setup_sig2: .word SIG2 +! After our code and data, we'll store the mode list. +! Mode record: .word modenr +! .byte lines +! .byte columns +! Mode numbers used: 0=current, >=0x100=VESA, -1=80x50, -2=80x28 +modelist: + .text endtext: .data diff --git a/arch/i386/defconfig b/arch/i386/defconfig index 60b7eb290606..c3e4cff75670 100644 --- a/arch/i386/defconfig +++ b/arch/i386/defconfig @@ -49,7 +49,7 @@ CONFIG_INET=y # # CONFIG_INET_PCTCP is not set # CONFIG_INET_RARP is not set -CONFIG_INET_SNARL=y +# CONFIG_NO_PATH_MTU_DISCOVERY is not set # CONFIG_TCP_NAGLE_OFF is not set CONFIG_IP_NOSR=y CONFIG_SKB_LARGE=y @@ -102,6 +102,7 @@ CONFIG_MSDOS_FS=y # CONFIG_UMSDOS_FS is not set CONFIG_PROC_FS=y CONFIG_NFS_FS=y +# CONFIG_ROOT_NFS is not set CONFIG_ISO9660_FS=y # CONFIG_HPFS_FS is not set # CONFIG_SYSV_FS is not set diff --git a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile index 521ac5994b4c..1186c23064d7 100644 --- a/arch/i386/kernel/Makefile +++ b/arch/i386/kernel/Makefile @@ -14,8 +14,15 @@ LD86 =ld86 -0 #.S.s: # $(CPP) -D__ASSEMBLY__ -traditional $< -o $*.s + +ifdef SMP + +.S.o: + $(CC) -D__ASSEMBLY__ -D__SMP__ -traditional -c $< -o $*.o +else .S.o: $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o +endif all: kernel.o head.o @@ -24,12 +31,19 @@ O_OBJS := process.o signal.o entry.o traps.o irq.o vm86.o bios32.o \ ptrace.o ioport.o ldt.o setup.o time.o sys_i386.o ifdef SMP + O_OBJS += smp.o -endif + +head.o: head.S $(TOPDIR)/include/linux/tasks.h + $(CC) -D__ASSEMBLY__ -D__SMP__ -traditional -c $*.S -o $*.o + +else head.o: head.S $(TOPDIR)/include/linux/tasks.h $(CC) -D__ASSEMBLY__ -traditional -c $*.S -o $*.o +endif + hexify: $(HOSTCC) hexify.c -o hexify @@ -48,10 +62,10 @@ trampoline32.o: trampoline32.s $(AS386) -o $@ $< trampoline.s: trampoline.S $(CONFIGURE) $(TOPDIR)/include/linux/config.h Makefile - $(CPP) -traditional $< -o $@ + $(CPP) -D__SMP__ -traditional $< -o $@ trampoline32.s: trampoline32.S $(CONFIGURE) $(TOPDIR)/include/linux/config.h Makefile - $(CPP) -traditional $< -o $@ + $(CPP) -D__SMP__ -traditional $< -o $@ clean: rm -f trampoline hexify diff --git a/arch/i386/kernel/bios32.c b/arch/i386/kernel/bios32.c index 85d40575063b..e10fdab30549 100644 --- a/arch/i386/kernel/bios32.c +++ b/arch/i386/kernel/bios32.c @@ -383,6 +383,12 @@ const char *pcibios_strerror (int error) case PCIBIOS_BAD_REGISTER_NUMBER: return "BAD_REGISTER_NUMBER"; + case PCIBIOS_SET_FAILED: + return "SET_FAILED"; + + case PCIBIOS_BUFFER_TOO_SMALL: + return "BUFFER_TOO_SMALL"; + default: sprintf (buf, "UNKNOWN RETURN 0x%x", error); return buf; diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index 2ed851457113..9c8a19c21c7c 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -103,8 +103,8 @@ void setup_arch(char **cmdline_p, if (memory_end > 16*1024*1024) memory_end = 16*1024*1024; #endif - if (MOUNT_ROOT_RDONLY) - root_mountflags |= MS_RDONLY; + if (!MOUNT_ROOT_RDONLY) + root_mountflags &= ~MS_RDONLY; memory_start = (unsigned long) &_end; init_task.mm->start_code = TASK_SIZE; init_task.mm->end_code = TASK_SIZE + (unsigned long) &_etext; diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index 86d855d9e990..000a55a73545 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -2440,14 +2440,21 @@ static int make_raw_rw_request(void) raw_cmd->flags |= FD_RAW_READ; COMMAND = FM_MODE(floppy,FD_READ); } else if ((unsigned long)CURRENT->buffer < MAX_DMA_ADDRESS) { + unsigned long dma_limit; int direct, indirect; indirect= transfer_size(ssize,max_sector,max_buffer_sectors*2) - sector_t; - max_size = minimum(buffer_chain_size(), - (MAX_DMA_ADDRESS - - ((unsigned long) CURRENT->buffer))>>9); + /* + * Do NOT use minimum() here---MAX_DMA_ADDRESS is 64 bits wide + * on a 64 bit machine! + */ + max_size = buffer_chain_size(); + dma_limit = (MAX_DMA_ADDRESS - ((unsigned long) CURRENT->buffer)) >> 9; + if ((unsigned long) max_size > dma_limit) { + max_size = dma_limit; + } /* 64 kb boundaries */ if (CROSS_64KB(CURRENT->buffer, max_size << 9)) max_size = (K_64 - ((long) CURRENT->buffer) % K_64)>>9; @@ -3033,8 +3040,7 @@ static inline int set_geometry(unsigned int cmd, struct floppy_struct *g, return -EPERM; LOCK_FDC(drive,1); for (cnt = 0; cnt < N_DRIVE; cnt++){ - if (TYPE(drive_state[cnt].fd_device) == - type && + if (TYPE(drive_state[cnt].fd_device) == type && drive_state[cnt].fd_ref) set_bit(drive, &fake_change); } @@ -3047,7 +3053,9 @@ static inline int set_geometry(unsigned int cmd, struct floppy_struct *g, for (cnt = 0; cnt < N_DRIVE; cnt++){ if (TYPE(drive_state[cnt].fd_device) == type && drive_state[cnt].fd_ref) - check_disk_change(drive_state[cnt].fd_device); + check_disk_change( + MKDEV(FLOPPY_MAJOR, + drive_state[cnt].fd_device)); } } else { LOCK_FDC(drive,1); @@ -3125,7 +3133,7 @@ static inline int normalize_0x02xx_ioctl(int *cmd, int *size) DPRINT1("warning: obsolete ioctl 0x%x\n",ocmd); DPRINT("please recompile your program\n"); /* these ioctls only existed - * in two (development) + * in six (development) * kernels anyways. That's why we * complain about these, and not about * the much older 0x00xx ioctl's @@ -3206,12 +3214,12 @@ static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, return -EPERM; /* verify writability of result, and fail early */ - if (_IOC_DIR(cmd) & _IOC_WRITE) + if (_IOC_DIR(cmd) & _IOC_READ) ECALL(verify_area(VERIFY_WRITE,(void *) param, size)); /* copyin */ CLEARSTRUCT(&inparam); - if (_IOC_DIR(cmd) & _IOC_READ) + if (_IOC_DIR(cmd) & _IOC_WRITE) ECALL(fd_copyin((void *)param, &inparam, size)) switch (cmd) { @@ -3304,7 +3312,7 @@ static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, return -EINVAL; } - if (_IOC_DIR(cmd) & _IOC_WRITE) + if (_IOC_DIR(cmd) & _IOC_READ) return fd_copyout((void *)param, outparam, size); else return 0; @@ -3479,11 +3487,10 @@ static int floppy_open(struct inode * inode, struct file * filp) } UDRS->fd_device = MINOR(inode->i_rdev); - - if (old_dev && old_dev != inode->i_rdev) { + if (old_dev != -1 && old_dev != MINOR(inode->i_rdev)) { if (buffer_drive == drive) buffer_track = -1; - invalidate_buffers(old_dev); + invalidate_buffers(MKDEV(FLOPPY_MAJOR,old_dev)); } /* Allow ioctls if we have write-permissions even if read-only open */ @@ -3831,6 +3838,7 @@ int floppy_init(void) CLEARSTRUCT(UDRS); CLEARSTRUCT(UDRWE); UDRS->flags = FD_VERIFY | FD_DISK_NEWCHANGE | FD_DISK_CHANGED; + UDRS->fd_device = -1; floppy_track_buffer = NULL; max_buffer_sectors = 0; } diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index d5554b7f9399..6b5a4813e5a1 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -104,7 +104,7 @@ int * hardsect_size[MAX_BLKDEV] = { NULL, NULL, }; * force the transfer to start only after we have put all the requests * on the list. */ -static void plug_device(struct blk_dev_struct * dev, struct request * plug) +static inline void plug_device(struct blk_dev_struct * dev, struct request * plug) { unsigned long flags; @@ -121,7 +121,7 @@ static void plug_device(struct blk_dev_struct * dev, struct request * plug) /* * remove the plug and let it rip.. */ -static void unplug_device(struct blk_dev_struct * dev) +static inline void unplug_device(struct blk_dev_struct * dev) { struct request * req; unsigned long flags; diff --git a/drivers/cdrom/mcd.c b/drivers/cdrom/mcd.c index d89753a1f8b1..8207cf810f3a 100644 --- a/drivers/cdrom/mcd.c +++ b/drivers/cdrom/mcd.c @@ -62,22 +62,11 @@ */ -#include -#ifdef MODULE -# include -#endif - -#include +#include #ifdef MODULE -# ifndef CONFIG_MODVERSIONS - char kernel_version[]= UTS_RELEASE; -# endif #define mcd_init init_module -#else -# define MOD_INC_USE_COUNT -# define MOD_DEC_USE_COUNT #endif #include diff --git a/drivers/cdrom/mcdx.c b/drivers/cdrom/mcdx.c index 592abf3a94cf..915649805343 100644 --- a/drivers/cdrom/mcdx.c +++ b/drivers/cdrom/mcdx.c @@ -37,23 +37,7 @@ static const char *mcdx_c_version = "mcdx.c,v 1.17 1995/11/06 01:07:57 heiko Exp"; #endif -#include - -#ifdef MODULE #include -#endif - -#include - -#ifdef MODULE -#ifndef CONFIG_MODVERSIONS -char kernel_version[] = UTS_RELEASE; -#endif -#else -#define MOD_INC_USE_COUNT -#define MOD_DEC_USE_COUNT -#define MOD_IN_USE 1 -#endif MODULE #include #include diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index a49344ffe2df..6c253b3a2e15 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -1,5 +1,5 @@ static char rcsid[] = -"$Revision: 1.36.3.2 $$Date: 1995/09/08 22:07:14 $"; +"$Revision: 1.36.3.4 $$Date: 1995/11/13 20:45:10 $"; /* * linux/drivers/char/cyclades.c * @@ -24,6 +24,19 @@ static char rcsid[] = * int cy_open(struct tty_struct *tty, struct file *filp); * * $Log: cyclades.c,v $ + * Revision 1.36.3.4 1995/11/13 20:45:10 bentson + * Changes by Corey Minyard distributed + * in 1.3.41 kernel to remove a possible race condition, extend + * some error messages, and let the driver run as a loadable module + * Change by Alan Wendt to remove a + * possible race condition. + * Change by Marcio Saito to fix PCI addressing. + * + * Revision 1.36.3.3 1995/11/13 19:44:48 bentson + * Changes by Linus Torvalds in 1.3.33 kernel distribution + * required due to reordering of driver initialization. + * Drivers are now initialized *after* memory management. + * * Revision 1.36.3.2 1995/09/08 22:07:14 bentson * remove printk from ISR; fix typo * @@ -452,12 +465,13 @@ SP(char *data){ console_print(data); restore_flags(flags); } -char scrn[2]; void CP(char data){ unsigned long flags; + char scrn[2]; save_flags(flags); cli(); scrn[0] = data; + scrn[1] = '\0'; console_print(scrn); restore_flags(flags); }/* CP */ @@ -1296,8 +1310,10 @@ shutdown(struct cyclades_port * info) */ save_flags(flags); cli(); if (info->xmit_buf){ - free_page((unsigned long) info->xmit_buf); - info->xmit_buf = 0; + unsigned long temp; + temp = info->xmit_buf; + info->xmit_buf = 0; + free_page((unsigned long) temp); } base_addr[CyCAR<base_addr = ioaddr; - ethdev_init(dev); - printk("%s: 3c503 at i/o base %#3x, node address", dev->name, ioaddr); + /* Allocate dev->priv and fill in 8390 specific dev fields. */ + if (ethdev_init(dev)) { + printk ("3c503: unable to allocate memory for dev->priv.\n"); + return -ENOMEM; + } + + printk("%s: 3c503 at i/o base %#3x, node ", dev->name, ioaddr); /* Retrieve and print the ethernet address. */ for (i = 0; i < 6; i++) @@ -220,6 +229,7 @@ el2_probe1(struct device *dev, int ioaddr) #else ei_status.interface_num = dev->mem_end & 0xf; #endif + printk(", using %sternal xcvr.\n", ei_status.interface_num == 0 ? "in" : "ex"); if ((membase_reg & 0xf0) == 0) { dev->mem_start = 0; @@ -241,7 +251,7 @@ el2_probe1(struct device *dev, int ioaddr) writel(test_val, mem_base + i); if (readl(mem_base) != 0xba5eba5e || readl(mem_base + i) != test_val) { - printk(" memory failure or memory address conflict.\n"); + printk("3c503.c: memory failure or memory address conflict.\n"); dev->mem_start = 0; ei_status.name = "3c503-PIO"; break; @@ -292,7 +302,7 @@ el2_probe1(struct device *dev, int ioaddr) if (dev->irq == 2) dev->irq = 9; else if (dev->irq > 5 && dev->irq != 9) { - printk("\n3c503: configured interrupt %d invalid, will use autoIRQ.\n", + printk("3c503: configured interrupt %d invalid, will use autoIRQ.\n", dev->irq); dev->irq = 0; } @@ -304,7 +314,7 @@ el2_probe1(struct device *dev, int ioaddr) dev->stop = &el2_close; if (dev->mem_start) - printk("\n%s: %s - %dkB RAM, 8kB shared mem window at %#6lx-%#6lx.\n", + printk("%s: %s - %dkB RAM, 8kB shared mem window at %#6lx-%#6lx.\n", dev->name, ei_status.name, (wordlength+1)<<3, dev->mem_start, dev->mem_end-1); @@ -344,8 +354,11 @@ el2_open(struct device *dev) return -EAGAIN; } } + el2_init_card(dev); - return ei_open(dev); + ei_open(dev); + MOD_INC_USE_COUNT; + return 0; } static int @@ -356,9 +369,8 @@ el2_close(struct device *dev) irq2dev_map[dev->irq] = NULL; outb(EGACFR_IRQOFF, E33G_GACFR); /* disable interrupts. */ - NS8390_init(dev, 0); - dev->start = 0; - + ei_close(dev); + MOD_DEC_USE_COUNT; return 0; } @@ -526,56 +538,69 @@ el2_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_off } outb_p(ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL); } -#ifdef MODULE -static struct device el2_drv = -{"3c503", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, el2_probe }; - -static struct device el2pio_drv = -{"3c503pio", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, el2_pio_probe }; -static int io = 0x300; -static int irq = 0; - -static int no_pio = 1; -int init_module(void) + +#ifdef MODULE +#define MAX_EL2_CARDS 4 /* Max number of EL2 cards per module */ +#define NAMELEN 8 /* # of chars for storing dev->name */ +static char namelist[NAMELEN * MAX_EL2_CARDS] = { 0, }; +static struct device dev_el2[MAX_EL2_CARDS] = { + { + NULL, /* assign a chunk of namelist[] below */ + 0, 0, 0, 0, + 0, 0, + 0, 0, 0, NULL, NULL + }, +}; + +static int io[MAX_EL2_CARDS] = { 0, }; +static int irq[MAX_EL2_CARDS] = { 0, }; +static int xcvr[MAX_EL2_CARDS] = { 0, }; /* choose int. or ext. xcvr */ + +/* This is set up so that only a single autoprobe takes place per call. +ISA device autoprobes on a running machine are not recommended. */ +int +init_module(void) { - int rc1, rc2; - el2_drv.base_addr = io; - el2_drv.irq = irq; - el2pio_drv.base_addr = io; - el2pio_drv.irq = irq; - - if (io == 0) - printk("3c503: You should not use auto-probing with insmod!\n"); - - rc2 = 0; - no_pio = 1; - rc1 = register_netdev(&el2_drv); - if (rc1 != 0) { - rc2 = register_netdev(&el2pio_drv); - no_pio = 0; + int this_dev, found = 0; + + for (this_dev = 0; this_dev < MAX_EL2_CARDS; this_dev++) { + struct device *dev = &dev_el2[this_dev]; + dev->name = namelist+(NAMELEN*this_dev); + dev->irq = irq[this_dev]; + dev->base_addr = io[this_dev]; + dev->mem_end = xcvr[this_dev]; /* low 4bits = xcvr sel. */ + dev->init = el2_probe; + if (io[this_dev] == 0) { + if (this_dev != 0) break; /* only autoprobe 1st one */ + printk(KERN_NOTICE "3c503.c: Presently autoprobing (not recommended) for a single card.\n"); + } + if (register_netdev(dev) != 0) { + printk(KERN_WARNING "3c503.c: No 3c503 card found (i/o = 0x%x).\n", io[this_dev]); + if (found != 0) return 0; /* Got at least one. */ + return -ENXIO; + } + found++; } - if (rc1 != 0 && rc2 != 0) - return -EIO; return 0; } void cleanup_module(void) { - int ioaddr; - - if (no_pio) { - ioaddr = el2_drv.base_addr; - unregister_netdev(&el2_drv); - } else { - ioaddr = el2pio_drv.base_addr; - unregister_netdev(&el2pio_drv); + int this_dev; + + for (this_dev = 0; this_dev < MAX_EL2_CARDS; this_dev++) { + struct device *dev = &dev_el2[this_dev]; + if (dev->priv != NULL) { + /* NB: el2_close() handles free_irq + irq2dev map */ + kfree(dev->priv); + dev->priv = NULL; + release_region(dev->base_addr, EL2_IO_EXTENT); + unregister_netdev(dev); + } } - - /* If we don't do this, we can't re-insmod it later. */ - release_region(ioaddr, EL2_IO_EXTENT); } #endif /* MODULE */ diff --git a/drivers/net/8390.c b/drivers/net/8390.c index f21462bc9b4b..dcd1ff814d05 100644 --- a/drivers/net/8390.c +++ b/drivers/net/8390.c @@ -55,7 +55,6 @@ static const char *version = #include #include -#include #include "8390.h" @@ -118,10 +117,11 @@ static void set_multicast_list(struct device *dev, int num_addrs, void *addrs); int ei_open(struct device *dev) { struct ei_device *ei_local = (struct ei_device *) dev->priv; - - if ( ! ei_local) { - printk("%s: Opening a non-existent physical device\n", dev->name); - return ENXIO; + + /* This can't happen unless somebody forgot to call ethdev_init(). */ + if (ei_local == NULL) { + printk(KERN_EMERG "%s: ei_open passed a non-existent device!\n", dev->name); + return -ENXIO; } irq2dev_map[dev->irq] = dev; @@ -131,6 +131,14 @@ int ei_open(struct device *dev) return 0; } +/* Opposite of above. Only used when "ifconfig down" is done. */ +int ei_close(struct device *dev) +{ + NS8390_init(dev, 0); + dev->start = 0; + return 0; +} + static int ei_start_xmit(struct sk_buff *skb, struct device *dev) { int e8390_base = dev->base_addr; @@ -603,10 +611,6 @@ int ethdev_init(struct device *dev) ei_local->pingpong = ei_pingpong; } - /* The open call may be overridden by the card-specific code. */ - if (dev->open == NULL) - dev->open = &ei_open; - /* We should have a dev->stop entry also. */ dev->hard_start_xmit = &ei_start_xmit; dev->get_stats = get_stats; #ifdef HAVE_MULTICAST diff --git a/drivers/net/8390.h b/drivers/net/8390.h index fce3b00d70d8..17b8cdb588a6 100644 --- a/drivers/net/8390.h +++ b/drivers/net/8390.h @@ -32,12 +32,13 @@ extern int ethif_init(struct device *dev); extern int ethdev_init(struct device *dev); extern void NS8390_init(struct device *dev, int startp); extern int ei_open(struct device *dev); +extern int ei_close(struct device *dev); extern void ei_interrupt(int irq, struct pt_regs *regs); #ifndef HAVE_AUTOIRQ /* From auto_irq.c */ extern struct device *irq2dev_map[16]; -extern void autoirq_setup(int waittime); +extern int autoirq_setup(int waittime); extern int autoirq_report(int waittime); #endif diff --git a/drivers/net/Config.in b/drivers/net/Config.in index 1a78940fede8..3752df5c858b 100644 --- a/drivers/net/Config.in +++ b/drivers/net/Config.in @@ -10,11 +10,7 @@ tristate 'PPP (point-to-point) support' CONFIG_PPP if [ ! "$CONFIG_PPP" = "n" ]; then comment 'CCP compressors for PPP are only built as modules.' fi -if [ "$CONFIG_AX25" = "y" ]; then - bool 'Z8530 SCC kiss emulation driver for AX.25' CONFIG_SCC -else - bool 'Z8530 SCC kiss emulation driver for AX.25' CONFIG_SCC -fi +bool 'Z8530 SCC kiss emulation driver for AX.25' CONFIG_SCC tristate 'PLIP (parallel port) support' CONFIG_PLIP tristate 'EQL (serial line load balancing) support' CONFIG_EQUALIZER bool 'Do you want to be offered ALPHA test drivers' CONFIG_NET_ALPHA diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 6cb6a82d6c29..3bf3176da77c 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -139,7 +139,7 @@ else endif endif -ifneq ($(CONFIG_PPP),n) +ifdef CONFIG_PPP M_OBJS += bsd_comp.o endif diff --git a/drivers/net/Space.c b/drivers/net/Space.c index dac48c2f990c..65bd3e1cbf33 100644 --- a/drivers/net/Space.c +++ b/drivers/net/Space.c @@ -97,15 +97,21 @@ ethif_probe(struct device *dev) #if defined(CONFIG_EL2) || defined(EL2) /* 3c503 */ && el2_probe(dev) #endif -#if defined(CONFIG_NE2000) || defined(NE2000) - && ne_probe(dev) -#endif #if defined(CONFIG_HPLAN) || defined(HPLAN) && hp_probe(dev) #endif #if defined(CONFIG_HPLAN_PLUS) && hp_plus_probe(dev) #endif +#ifdef CONFIG_AC3200 /* Ansel Communications EISA 3200. */ + && ac3200_probe(dev) +#endif +#ifdef CONFIG_E2100 /* Cabletron E21xx series. */ + && e2100_probe(dev) +#endif +#if defined(CONFIG_NE2000) || defined(NE2000) + && ne_probe(dev) +#endif #ifdef CONFIG_AT1500 && at1500_probe(dev) #endif @@ -148,12 +154,6 @@ ethif_probe(struct device *dev) #ifdef CONFIG_ELPLUS /* 3c505 */ && elplus_probe(dev) #endif -#ifdef CONFIG_AC3200 /* Ansel Communications EISA 3200. */ - && ac3200_probe(dev) -#endif -#ifdef CONFIG_E2100 /* Cabletron E21xx series. */ - && e2100_probe(dev) -#endif #ifdef CONFIG_DE600 /* D-Link DE-600 adapter */ && de600_probe(dev) #endif diff --git a/drivers/net/ac3200.c b/drivers/net/ac3200.c index 86eb9a6dd4b3..bbb5332f33f0 100644 --- a/drivers/net/ac3200.c +++ b/drivers/net/ac3200.c @@ -97,13 +97,16 @@ int ac3200_probe(struct device *dev) else if (ioaddr > 0) /* Don't probe at all. */ return ENXIO; - /* If you have a pre-pl15 machine you should delete this line. */ + /* If you have a pre 0.99pl15 machine you should delete this line. */ if ( ! EISA_bus) return ENXIO; - for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) + for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) { + if (check_region(ioaddr, AC_IO_EXTENT)) + continue; if (ac_probe1(ioaddr, dev) == 0) return 0; + } return ENODEV; } @@ -138,6 +141,13 @@ static int ac_probe1(int ioaddr, struct device *dev) return ENODEV; } + + /* We should have a "dev" from Space.c or the static module table. */ + if (dev == NULL) { + printk("ac3200.c: Passed a NULL device.\n"); + dev = init_etherdev(0, 0); + } + for(i = 0; i < ETHER_ADDR_LEN; i++) dev->dev_addr[i] = inb(ioaddr + AC_SA_PROM + i); @@ -159,6 +169,13 @@ static int ac_probe1(int ioaddr, struct device *dev) return EAGAIN; } + /* Allocate dev->priv and fill in 8390 specific dev fields. */ + if (ethdev_init(dev)) { + printk (" unable to allocate memory for dev->priv.\n"); + free_irq(dev->irq); + return -ENOMEM; + } + request_region(ioaddr, AC_IO_EXTENT, "ac3200"); dev->base_addr = ioaddr; @@ -180,8 +197,6 @@ static int ac_probe1(int ioaddr, struct device *dev) dev->mem_end = dev->rmem_end = dev->mem_start + (AC_STOP_PG - AC_START_PG)*256; - ethdev_init(dev); - ei_status.name = "AC3200"; ei_status.tx_start_page = AC_START_PG; ei_status.rx_start_page = AC_START_PG + TX_PAGES; @@ -208,7 +223,6 @@ static int ac_probe1(int ioaddr, struct device *dev) static int ac_open(struct device *dev) { - int rc; #ifdef notyet /* Someday we may enable the IRQ and shared memory here. */ int ioaddr = dev->base_addr; @@ -217,8 +231,7 @@ static int ac_open(struct device *dev) return -EAGAIN; #endif - rc = ei_open(dev); - if (rc != 0) return rc; + ei_open(dev); MOD_INC_USE_COUNT; @@ -293,7 +306,7 @@ static int ac_close_card(struct device *dev) irq2dev_map[dev->irq] = 0; #endif - NS8390_init(dev, 0); + ei_close(dev); MOD_DEC_USE_COUNT; @@ -301,35 +314,64 @@ static int ac_close_card(struct device *dev) } #ifdef MODULE -static char devicename[9] = { 0, }; -static struct device dev_ac3200 = { - devicename, /* device name is inserted by linux/drivers/net/net_init.c */ - 0, 0, 0, 0, - 0, 0, - 0, 0, 0, NULL, ac3200_probe }; - -static int io = 0; -static int irq = 0; - -int init_module(void) +#define MAX_AC32_CARDS 4 /* Max number of AC32 cards per module */ +#define NAMELEN 8 /* # of chars for storing dev->name */ +static char namelist[NAMELEN * MAX_AC32_CARDS] = { 0, }; +static struct device dev_ac32[MAX_AC32_CARDS] = { + { + NULL, /* assign a chunk of namelist[] below */ + 0, 0, 0, 0, + 0, 0, + 0, 0, 0, NULL, NULL + }, +}; + +static int io[MAX_AC32_CARDS] = { 0, }; +static int irq[MAX_AC32_CARDS] = { 0, }; +static int mem[MAX_AC32_CARDS] = { 0, }; + +int +init_module(void) { - dev_ac3200.base_addr = io; - dev_ac3200.irq = irq; - if (register_netdev(&dev_ac3200) != 0) { - printk("ac3200: register_netdev() returned non-zero.\n"); - return -EIO; + int this_dev, found = 0; + + for (this_dev = 0; this_dev < MAX_AC32_CARDS; this_dev++) { + struct device *dev = &dev_ac32[this_dev]; + dev->name = namelist+(NAMELEN*this_dev); + dev->irq = irq[this_dev]; + dev->base_addr = io[this_dev]; + dev->mem_start = mem[this_dev]; /* Currently ignored by driver */ + dev->init = ac3200_probe; + /* Default is to only install one card. */ + if (io[this_dev] == 0 && this_dev != 0) break; + if (register_netdev(dev) != 0) { + printk(KERN_WARNING "ac3200.c: No ac3200 card found (i/o = 0x%x).\n", io[this_dev]); + if (found != 0) return 0; /* Got at least one. */ + return -ENXIO; + } + found++; } + return 0; } void cleanup_module(void) { - unregister_netdev(&dev_ac3200); - - /* If we don't do this, we can't re-insmod it later. */ - free_irq(dev_ac3200.irq); - release_region(dev_ac3200.base_addr, AC_IO_EXTENT); + int this_dev; + + for (this_dev = 0; this_dev < MAX_AC32_CARDS; this_dev++) { + struct device *dev = &dev_ac32[this_dev]; + if (dev->priv != NULL) { + kfree(dev->priv); + dev->priv = NULL; + /* Someday free_irq + irq2dev may be in ac_close_card() */ + free_irq(dev->irq); + irq2dev_map[dev->irq] = NULL; + release_region(dev->base_addr, AC_IO_EXTENT); + unregister_netdev(dev); + } + } } #endif /* MODULE */ diff --git a/drivers/net/e2100.c b/drivers/net/e2100.c index 756a72bfe336..ac10e9041d8f 100644 --- a/drivers/net/e2100.c +++ b/drivers/net/e2100.c @@ -139,6 +139,7 @@ int e21_probe1(struct device *dev, int ioaddr) { int i, status; unsigned char *station_addr = dev->dev_addr; + static unsigned version_printed = 0; /* First check the station address for the Ctron prefix. */ if (inb(ioaddr + E21_SAPROM + 0) != 0x00 @@ -160,6 +161,15 @@ int e21_probe1(struct device *dev, int ioaddr) inb(ioaddr + E21_MEDIA); /* Point to media selection. */ outb(0, ioaddr + E21_ASIC); /* and disable the secondary interface. */ + if (ei_debug && version_printed++ == 0) + printk(version); + + /* We should have a "dev" from Space.c or the static module table. */ + if (dev == NULL) { + printk("e2100.c: Passed a NULL device.\n"); + dev = init_etherdev(0, 0); + } + printk("%s: E21** at %#3x,", dev->name, ioaddr); for (i = 0; i < 6; i++) printk(" %02X", station_addr[i]); @@ -178,14 +188,18 @@ int e21_probe1(struct device *dev, int ioaddr) } else if (dev->irq == 2) /* Fixup luser bogosity: IRQ2 is really IRQ9 */ dev->irq = 9; + /* Allocate dev->priv and fill in 8390 specific dev fields. */ + if (ethdev_init(dev)) { + printk (" unable to get memory for dev->priv.\n"); + return -ENOMEM; + } + /* Grab the region so we can find a different board if IRQ select fails. */ request_region(ioaddr, E21_IO_EXTENT, "e2100"); /* The 8390 is at the base address. */ dev->base_addr = ioaddr; - ethdev_init(dev); - ei_status.name = "E2100"; ei_status.word16 = 1; ei_status.tx_start_page = E21_TX_START_PG; @@ -223,9 +237,6 @@ int e21_probe1(struct device *dev, int ioaddr) printk(", IRQ %d, %s media, memory @ %#lx.\n", dev->irq, dev->if_port ? "secondary" : "primary", dev->mem_start); - if (ei_debug > 0) - printk(version); - ei_status.reset_8390 = &e21_reset_8390; ei_status.block_input = &e21_block_input; ei_status.block_output = &e21_block_output; @@ -241,7 +252,6 @@ static int e21_open(struct device *dev) { short ioaddr = dev->base_addr; - int rc; if (request_irq(dev->irq, ei_interrupt, 0, "e2100")) { return EBUSY; @@ -257,8 +267,7 @@ e21_open(struct device *dev) inb(ioaddr + E21_MEM_BASE); outb(0, ioaddr + E21_ASIC + ((dev->mem_start >> 17) & 7)); - rc = ei_open(dev); - if (rc != 0) return rc; + ei_open(dev); MOD_INC_USE_COUNT; return 0; } @@ -338,8 +347,8 @@ e21_close(struct device *dev) if (ei_debug > 1) printk("%s: Shutting down ethercard.\n", dev->name); - free_irq(dev->irq); - dev->irq = ei_status.saved_irq; + free_irq(dev->irq); + dev->irq = ei_status.saved_irq; /* Shut off the interrupt line and secondary interface. */ inb(ioaddr + E21_IRQ_LOW); @@ -347,9 +356,9 @@ e21_close(struct device *dev) inb(ioaddr + E21_IRQ_HIGH); /* High IRQ bit, and if_port. */ outb(0, ioaddr + E21_ASIC); - irq2dev_map[dev->irq] = NULL; + irq2dev_map[dev->irq] = NULL; - NS8390_init(dev, 0); + ei_close(dev); /* Double-check that the memory has been turned off, because really really bad things happen if it isn't. */ @@ -365,37 +374,70 @@ struct netdev_entry e21_drv = {"e21", e21_probe1, E21_IO_EXTENT, e21_probe_list}; #endif + #ifdef MODULE -static char devicename[9] = { 0, }; -static struct device dev_e2100 = { - devicename, /* device name is inserted by linux/drivers/net/net_init.c */ - 0, 0, 0, 0, - 0, 0, - 0, 0, 0, NULL, e2100_probe }; - -static int io = 0x300; -static int irq = 0; - -int init_module(void) +#define MAX_E21_CARDS 4 /* Max number of E21 cards per module */ +#define NAMELEN 8 /* # of chars for storing dev->name */ +static char namelist[NAMELEN * MAX_E21_CARDS] = { 0, }; +static struct device dev_e21[MAX_E21_CARDS] = { + { + NULL, /* assign a chunk of namelist[] below */ + 0, 0, 0, 0, + 0, 0, + 0, 0, 0, NULL, NULL + }, +}; + +static int io[MAX_E21_CARDS] = { 0, }; +static int irq[MAX_E21_CARDS] = { 0, }; +static int mem[MAX_E21_CARDS] = { 0, }; +static int xcvr[MAX_E21_CARDS] = { 0, }; /* choose int. or ext. xcvr */ + +/* This is set up so that only a single autoprobe takes place per call. +ISA device autoprobes on a running machine are not recommended. */ +int +init_module(void) { - if (io == 0) - printk("e2100: You should not use auto-probing with insmod!\n"); - dev_e2100.base_addr = io; - dev_e2100.irq = irq; - if (register_netdev(&dev_e2100) != 0) { - printk("e2100: register_netdev() returned non-zero.\n"); - return -EIO; + int this_dev, found = 0; + + for (this_dev = 0; this_dev < MAX_E21_CARDS; this_dev++) { + struct device *dev = &dev_e21[this_dev]; + dev->name = namelist+(NAMELEN*this_dev); + dev->irq = irq[this_dev]; + dev->base_addr = io[this_dev]; + dev->mem_start = mem[this_dev]; + dev->mem_end = xcvr[this_dev]; /* low 4bits = xcvr sel. */ + dev->init = e2100_probe; + if (io[this_dev] == 0) { + if (this_dev != 0) break; /* only autoprobe 1st one */ + printk(KERN_NOTICE "e2100.c: Presently autoprobing (not recommended) for a single card.\n"); + } + if (register_netdev(dev) != 0) { + printk(KERN_WARNING "e2100.c: No E2100 card found (i/o = 0x%x).\n", io[this_dev]); + if (found != 0) return 0; /* Got at least one. */ + return -ENXIO; + } + found++; } + return 0; } void cleanup_module(void) { - unregister_netdev(&dev_e2100); - - /* If we don't do this, we can't re-insmod it later. */ - release_region(dev_e2100.base_addr, E21_IO_EXTENT); + int this_dev; + + for (this_dev = 0; this_dev < MAX_E21_CARDS; this_dev++) { + struct device *dev = &dev_e21[this_dev]; + if (dev->priv != NULL) { + /* NB: e21_close() handles free_irq + irq2dev map */ + kfree(dev->priv); + dev->priv = NULL; + release_region(dev->base_addr, E21_IO_EXTENT); + unregister_netdev(dev); + } + } } #endif /* MODULE */ diff --git a/drivers/net/hp-plus.c b/drivers/net/hp-plus.c index 57e39498047c..aed7ee01e7db 100644 --- a/drivers/net/hp-plus.c +++ b/drivers/net/hp-plus.c @@ -150,14 +150,21 @@ int hpp_probe1(struct device *dev, int ioaddr) unsigned char checksum = 0; const char *name = "HP-PC-LAN+"; int mem_start; + static unsigned version_printed = 0; /* Check for the HP+ signature, 50 48 0x 53. */ if (inw(ioaddr + HP_ID) != 0x4850 || (inw(ioaddr + HP_PAGING) & 0xfff0) != 0x5300) return ENODEV; - if (dev == NULL) - dev = init_etherdev(0, sizeof(struct ei_device)); + /* We should have a "dev" from Space.c or the static module table. */ + if (dev == NULL) { + printk("hp-plus.c: Passed a NULL device.\n"); + dev = init_etherdev(0, 0); + } + + if (ei_debug && version_printed++ == 0) + printk(version); printk("%s: %s at %#3x,", dev->name, name, ioaddr); @@ -181,6 +188,12 @@ int hpp_probe1(struct device *dev, int ioaddr) printk(" ID %4.4x", inw(ioaddr + 12)); } + /* Allocate dev->priv and fill in 8390 specific dev fields. */ + if (ethdev_init(dev)) { + printk ("hp-plus.c: unable to allocate memory for dev->priv.\n"); + return -ENOMEM; + } + /* Grab the region so we can find another board if something fails. */ request_region(ioaddr, HP_IO_EXTENT,"hp-plus"); @@ -200,18 +213,14 @@ int hpp_probe1(struct device *dev, int ioaddr) } } - printk( "%s%s", KERN_INFO, version); - /* Set the wrap registers for string I/O reads. */ outw((HP_START_PG + TX_2X_PAGES) | ((HP_STOP_PG - 1) << 8), ioaddr + 14); /* Set the base address to point to the NIC, not the "real" base! */ dev->base_addr = ioaddr + NIC_OFFSET; - ethdev_init(dev); - - dev->open = &hpp_open; - dev->stop = &hpp_close; + dev->open = &hpp_open; + dev->stop = &hpp_close; ei_status.name = name; ei_status.word16 = 0; /* Agggghhhhh! Debug time: 2 days! */ @@ -267,7 +276,9 @@ hpp_open(struct device *dev) /* Select the operational page. */ outw(Perf_Page, ioaddr + HP_PAGING); - return ei_open(dev); + ei_open(dev); + MOD_INC_USE_COUNT; + return 0; } static int @@ -276,13 +287,14 @@ hpp_close(struct device *dev) int ioaddr = dev->base_addr - NIC_OFFSET; int option_reg = inw(ioaddr + HPP_OPTION); - free_irq(dev->irq); - irq2dev_map[dev->irq] = NULL; - NS8390_init(dev, 0); + free_irq(dev->irq); + irq2dev_map[dev->irq] = NULL; + ei_close(dev); outw((option_reg & ~EnableIRQ) | MemDisable | NICReset | ChipReset, ioaddr + HPP_OPTION); - return 0; + MOD_DEC_USE_COUNT; + return 0; } static void @@ -396,39 +408,67 @@ hpp_mem_block_output(struct device *dev, int count, return; } + #ifdef MODULE -static char devicename[9] = { 0, }; -static struct device dev_hp = { - devicename, /* device name is inserted by linux/drivers/net/net_init.c */ - 0, 0, 0, 0, - 0, 0, - 0, 0, 0, NULL, hp_plus_probe }; - -static int io = 0x200; -static int irq = 0; - -int init_module(void) +#define MAX_HPP_CARDS 4 /* Max number of HPP cards per module */ +#define NAMELEN 8 /* # of chars for storing dev->name */ +static char namelist[NAMELEN * MAX_HPP_CARDS] = { 0, }; +static struct device dev_hpp[MAX_HPP_CARDS] = { + { + NULL, /* assign a chunk of namelist[] below */ + 0, 0, 0, 0, + 0, 0, + 0, 0, 0, NULL, NULL + }, +}; + +static int io[MAX_HPP_CARDS] = { 0, }; +static int irq[MAX_HPP_CARDS] = { 0, }; + +/* This is set up so that only a single autoprobe takes place per call. +ISA device autoprobes on a running machine are not recommended. */ +int +init_module(void) { - if (io == 0) - printk("HP-plus: You should not use auto-probing with insmod!\n"); - dev_hp.base_addr = io; - dev_hp.irq = irq; - if (register_netdev(&dev_hp) != 0) { - printk("HP-plus: register_netdev() returned non-zero.\n"); - return -EIO; + int this_dev, found = 0; + + for (this_dev = 0; this_dev < MAX_HPP_CARDS; this_dev++) { + struct device *dev = &dev_hpp[this_dev]; + dev->name = namelist+(NAMELEN*this_dev); + dev->irq = irq[this_dev]; + dev->base_addr = io[this_dev]; + dev->init = hp_plus_probe; + if (io[this_dev] == 0) { + if (this_dev != 0) break; /* only autoprobe 1st one */ + printk(KERN_NOTICE "hp-plus.c: Presently autoprobing (not recommended) for a single card.\n"); + } + if (register_netdev(dev) != 0) { + printk(KERN_WARNING "hp-plus.c: No HP-Plus card found (i/o = 0x%x).\n", io[this_dev]); + if (found != 0) return 0; /* Got at least one. */ + return -ENXIO; + } + found++; } + return 0; } void cleanup_module(void) { - int ioaddr = dev_hp.base_addr - NIC_OFFSET; - - unregister_netdev(&dev_hp); - - /* If we don't do this, we can't re-insmod it later. */ - release_region(ioaddr, HP_IO_EXTENT); + int this_dev; + + for (this_dev = 0; this_dev < MAX_HPP_CARDS; this_dev++) { + struct device *dev = &dev_hpp[this_dev]; + if (dev->priv != NULL) { + /* NB: hpp_close() handles free_irq + irq2dev map */ + int ioaddr = dev->base_addr - NIC_OFFSET; + kfree(dev->priv); + dev->priv = NULL; + release_region(ioaddr, HP_IO_EXTENT); + unregister_netdev(dev); + } + } } #endif /* MODULE */ diff --git a/drivers/net/hp.c b/drivers/net/hp.c index a3631a803768..d0443a7dd374 100644 --- a/drivers/net/hp.c +++ b/drivers/net/hp.c @@ -57,6 +57,8 @@ static unsigned int hppclan_portlist[] = int hp_probe(struct device *dev); int hp_probe1(struct device *dev, int ioaddr); +static int hp_open(struct device *dev); +static int hp_close(struct device *dev); static void hp_reset_8390(struct device *dev); static void hp_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page); @@ -106,6 +108,7 @@ int hp_probe1(struct device *dev, int ioaddr) { int i, board_id, wordmode; const char *name; + static unsigned version_printed = 0; /* Check for the HP physical address, 08 00 09 xx xx xx. */ /* This really isn't good enough: we may pick up HP LANCE boards @@ -126,9 +129,15 @@ int hp_probe1(struct device *dev, int ioaddr) wordmode = 0; } - if (dev == NULL) - dev = init_etherdev(0, sizeof(struct ei_device)); + /* We should have a "dev" from Space.c or the static module table. */ + if (dev == NULL) { + printk("hp.c: Passed a NULL device.\n"); + dev = init_etherdev(0, 0); + } + if (ei_debug && version_printed++ == 0) + printk(version); + printk("%s: %s (ID %02x) at %#3x,", dev->name, name, board_id, ioaddr); for(i = 0; i < ETHER_ADDR_LEN; i++) @@ -167,16 +176,20 @@ int hp_probe1(struct device *dev, int ioaddr) } } + /* Allocate dev->priv and fill in 8390 specific dev fields. */ + if (ethdev_init(dev)) { + printk (" unable to get memory for dev->priv.\n"); + free_irq(dev->irq); + return -ENOMEM; + } + /* Grab the region so we can find another board if something fails. */ request_region(ioaddr, HP_IO_EXTENT,"hp"); - if (ei_debug > 1) - printk(version); - /* Set the base address to point to the NIC, not the "real" base! */ dev->base_addr = ioaddr + NIC_OFFSET; - - ethdev_init(dev); + dev->open = &hp_open; + dev->stop = &hp_close; ei_status.name = name; ei_status.word16 = wordmode; @@ -193,6 +206,22 @@ int hp_probe1(struct device *dev, int ioaddr) return 0; } +static int +hp_open(struct device *dev) +{ + ei_open(dev); + MOD_INC_USE_COUNT; + return 0; +} + +static int +hp_close(struct device *dev) +{ + ei_close(dev); + MOD_DEC_USE_COUNT; + return 0; +} + static void hp_reset_8390(struct device *dev) { @@ -348,39 +377,66 @@ hp_init_card(struct device *dev) } #ifdef MODULE -static char devicename[9] = { 0, }; -static struct device dev_hp = { - devicename, /* device name is inserted by linux/drivers/net/net_init.c */ - 0, 0, 0, 0, - 0, 0, - 0, 0, 0, NULL, hp_probe }; - -static int io = 300; -static int irq = 0; - -int init_module(void) +#define MAX_HP_CARDS 4 /* Max number of HP cards per module */ +#define NAMELEN 8 /* # of chars for storing dev->name */ +static char namelist[NAMELEN * MAX_HP_CARDS] = { 0, }; +static struct device dev_hp[MAX_HP_CARDS] = { + { + NULL, /* assign a chunk of namelist[] below */ + 0, 0, 0, 0, + 0, 0, + 0, 0, 0, NULL, NULL + }, +}; + +static int io[MAX_HP_CARDS] = { 0, }; +static int irq[MAX_HP_CARDS] = { 0, }; + +/* This is set up so that only a single autoprobe takes place per call. +ISA device autoprobes on a running machine are not recommended. */ +int +init_module(void) { - if (io == 0) - printk("hp: You should not use auto-probing with insmod!\n"); - dev_hp.base_addr = io; - dev_hp.irq = irq; - if (register_netdev(&dev_hp) != 0) { - printk("hp: register_netdev() returned non-zero.\n"); - return -EIO; + int this_dev, found = 0; + + for (this_dev = 0; this_dev < MAX_HP_CARDS; this_dev++) { + struct device *dev = &dev_hp[this_dev]; + dev->name = namelist+(NAMELEN*this_dev); + dev->irq = irq[this_dev]; + dev->base_addr = io[this_dev]; + dev->init = hp_probe; + if (io[this_dev] == 0) { + if (this_dev != 0) break; /* only autoprobe 1st one */ + printk(KERN_NOTICE "hp.c: Presently autoprobing (not recommended) for a single card.\n"); + } + if (register_netdev(dev) != 0) { + printk(KERN_WARNING "hp.c: No HP card found (i/o = 0x%x).\n", io[this_dev]); + if (found != 0) return 0; /* Got at least one. */ + return -ENXIO; + } + found++; } + return 0; } void cleanup_module(void) { - int ioaddr = dev_hp.base_addr - NIC_OFFSET; - - unregister_netdev(&dev_hp); - - /* If we don't do this, we can't re-insmod it later. */ - free_irq(dev_hp.irq); - release_region(ioaddr, HP_IO_EXTENT); + int this_dev; + + for (this_dev = 0; this_dev < MAX_HP_CARDS; this_dev++) { + struct device *dev = &dev_hp[this_dev]; + if (dev->priv != NULL) { + int ioaddr = dev->base_addr - NIC_OFFSET; + kfree(dev->priv); + dev->priv = NULL; + free_irq(dev->irq); + irq2dev_map[dev->irq] = NULL; + release_region(ioaddr, HP_IO_EXTENT); + unregister_netdev(dev); + } + } } #endif /* MODULE */ diff --git a/drivers/net/ne.c b/drivers/net/ne.c index 7f565aa312c6..ae22a6a4843d 100644 --- a/drivers/net/ne.c +++ b/drivers/net/ne.c @@ -21,6 +21,7 @@ Paul Gortmaker : use ENISR_RDC to monitor Tx PIO uploads, made sanity checks and bad clone support optional. Paul Gortmaker : new reset code, reset card after probe at boot. + Paul Gortmaker : multiple card support for module users. */ @@ -53,6 +54,9 @@ static const char *version = /* Do we implement the read before write bugfix ? */ /* #define NE_RW_BUGFIX */ +/* Do we have a non std. amount of memory? (in units of 256 byte pages) */ +/* #define PACKETBUF_MEMSIZE 0x40 */ + /* ---- No user-serviceable parts below ---- */ /* A zero-terminated list of I/O addresses to be probed. */ @@ -89,6 +93,9 @@ bad_clone_list[] = { int ne_probe(struct device *dev); static int ne_probe1(struct device *dev, int ioaddr); +static int ne_open(struct device *dev); +static int ne_close(struct device *dev); + static void ne_reset_8390(struct device *dev); static void ne_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page); @@ -155,6 +162,7 @@ static int ne_probe1(struct device *dev, int ioaddr) int start_page, stop_page; int neX000, ctron; int reg0 = inb_p(ioaddr); + static unsigned version_printed = 0; if (reg0 == 0xFF) return ENODEV; @@ -173,6 +181,9 @@ static int ne_probe1(struct device *dev, int ioaddr) } } + if (ei_debug && version_printed++ == 0) + printk(version); + printk("NE*000 ethercard probe at %#3x:", ioaddr); /* Reset card. Who knows what dain-bramaged state it was left in. */ @@ -236,11 +247,6 @@ static int ne_probe1(struct device *dev, int ioaddr) stop_page = NE1SM_STOP_PG; } - for(i = 0; i < ETHER_ADDR_LEN; i++) { - dev->dev_addr[i] = SA_prom[i]; - printk(" %2.2x", SA_prom[i]); - } - neX000 = (SA_prom[14] == 0x57 && SA_prom[15] == 0x57); ctron = (SA_prom[0] == 0x00 && SA_prom[1] == 0x00 && SA_prom[2] == 0x1d); @@ -248,7 +254,7 @@ static int ne_probe1(struct device *dev, int ioaddr) if (neX000) { name = (wordlength == 2) ? "NE2000" : "NE1000"; } else if (ctron) { - name = "Cabletron"; + name = (wordlength == 2) ? "Ctron-8" : "Ctron-16"; start_page = 0x01; stop_page = (wordlength == 2) ? 0x40 : 0x20; } else { @@ -279,9 +285,11 @@ static int ne_probe1(struct device *dev, int ioaddr) } - - if (dev == NULL) - dev = init_etherdev(0, sizeof(struct ei_device)); + /* We should have a "dev" from Space.c or the static module table. */ + if (dev == NULL) { + printk("ne.c: Passed a NULL device.\n"); + dev = init_etherdev(0, 0); + } if (dev->irq < 2) { autoirq_setup(0); @@ -297,11 +305,16 @@ static int ne_probe1(struct device *dev, int ioaddr) /* 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; + + if (! dev->irq) { + printk(" failed to detect IRQ line.\n"); + return EAGAIN; + } /* Snarf the interrupt now. There's no point in waiting since we cannot share and the board will usually be enabled. */ { - int irqval = request_irq (dev->irq, ei_interrupt, 0, wordlength==2 ? "ne2000":"ne1000"); + int irqval = request_irq(dev->irq, ei_interrupt, 0, name); if (irqval) { printk (" unable to get IRQ %d (irqval=%d).\n", dev->irq, irqval); return EAGAIN; @@ -310,18 +323,23 @@ static int ne_probe1(struct device *dev, int ioaddr) dev->base_addr = ioaddr; - request_region(ioaddr, NE_IO_EXTENT, wordlength==2 ? "ne2000":"ne1000"); + /* Allocate dev->priv and fill in 8390 specific dev fields. */ + if (ethdev_init(dev)) { + printk (" unable to get memory for dev->priv.\n"); + free_irq(dev->irq); + return -ENOMEM; + } + + request_region(ioaddr, NE_IO_EXTENT, name); - for(i = 0; i < ETHER_ADDR_LEN; i++) + for(i = 0; i < ETHER_ADDR_LEN; i++) { + printk(" %2.2x", SA_prom[i]); dev->dev_addr[i] = SA_prom[i]; + } - ethdev_init(dev); printk("\n%s: %s found at %#x, using IRQ %d.\n", dev->name, name, ioaddr, dev->irq); - if (ei_debug > 0) - printk(version); - ei_status.name = name; ei_status.tx_start_page = start_page; ei_status.stop_page = stop_page; @@ -337,10 +355,30 @@ static int ne_probe1(struct device *dev, int ioaddr) ei_status.block_input = &ne_block_input; ei_status.block_output = &ne_block_output; ei_status.get_8390_hdr = &ne_get_8390_hdr; + dev->open = &ne_open; + dev->stop = &ne_close; NS8390_init(dev, 0); return 0; } +static int +ne_open(struct device *dev) +{ + ei_open(dev); + MOD_INC_USE_COUNT; + return 0; +} + +static int +ne_close(struct device *dev) +{ + if (ei_debug > 1) + printk("%s: Shutting down ethercard.\n", dev->name); + ei_close(dev); + MOD_DEC_USE_COUNT; + return 0; +} + /* Hard reset the card. This used to pause for the same period that a 8390 reset command required, but that shouldn't be necessary. */ static void @@ -568,36 +606,71 @@ ne_block_output(struct device *dev, int count, return; } + #ifdef MODULE -static char devicename[9] = { 0, }; -static struct device dev_ne2000 = { - devicename, /* device name is inserted by linux/drivers/net/net_init.c */ - 0, 0, 0, 0, - 0, 0, - 0, 0, 0, NULL, ne_probe }; +#define MAX_NE_CARDS 4 /* Max number of NE cards per module */ +#define NAMELEN 8 /* # of chars for storing dev->name */ +static char namelist[NAMELEN * MAX_NE_CARDS] = { 0, }; +static struct device dev_ne[MAX_NE_CARDS] = { + { + NULL, /* assign a chunk of namelist[] below */ + 0, 0, 0, 0, + 0, 0, + 0, 0, 0, NULL, NULL + }, +}; + +static int io[MAX_NE_CARDS] = { 0, }; +static int irq[MAX_NE_CARDS] = { 0, }; -static int io = 0x300; -static int irq = 0; +/* This is set up so that no autoprobe takes place. We can't guarantee +that the ne2k probe is the last 8390 based probe to take place (as it +is at boot) and so the probe will get confused by any other 8390 cards. +ISA device autoprobes on a running machine are not recommended anyway. */ -int init_module(void) +int +init_module(void) { - if (io == 0) - printk("ne: You should not use auto-probing with insmod!\n"); - dev_ne2000.base_addr = io; - dev_ne2000.irq = irq; - if (register_netdev(&dev_ne2000) != 0) - return -EIO; + int this_dev, found = 0; + + for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) { + struct device *dev = &dev_ne[this_dev]; + dev->name = namelist+(NAMELEN*this_dev); + dev->irq = irq[this_dev]; + dev->base_addr = io[this_dev]; + dev->init = ne_probe; + if (io[this_dev] == 0) { + if (this_dev != 0) break; /* only complain once */ + printk(KERN_NOTICE "ne.c: Module autoprobing not allowed. Append \"io=0xNNN\" value(s).\n"); + return -EPERM; + } + if (register_netdev(dev) != 0) { + printk(KERN_WARNING "ne.c: No NE*000 card found (i/o = 0x%x).\n", io[this_dev]); + if (found != 0) return 0; /* Got at least one. */ + return -ENXIO; + } + found++; + } + return 0; } void cleanup_module(void) { - unregister_netdev(&dev_ne2000); - - /* If we don't do this, we can't re-insmod it later. */ - free_irq(dev_ne2000.irq); - release_region(dev_ne2000.base_addr, NE_IO_EXTENT); + int this_dev; + + for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) { + struct device *dev = &dev_ne[this_dev]; + if (dev->priv != NULL) { + kfree(dev->priv); + dev->priv = NULL; + free_irq(dev->irq); + irq2dev_map[dev->irq] = NULL; + release_region(dev->base_addr, NE_IO_EXTENT); + unregister_netdev(dev); + } + } } #endif /* MODULE */ diff --git a/drivers/net/net_init.c b/drivers/net/net_init.c index c9eea60cb93e..921f20348f7e 100644 --- a/drivers/net/net_init.c +++ b/drivers/net/net_init.c @@ -132,8 +132,9 @@ init_etherdev(struct device *dev, int sizeof_priv) } -static int eth_mac_addr(struct device *dev, struct sockaddr *addr) +static int eth_mac_addr(struct device *dev, void *p) { + struct sockaddr *addr=p; if(dev->start) return -EBUSY; memcpy(dev->dev_addr, addr->sa_data,dev->addr_len); @@ -164,7 +165,8 @@ void ether_setup(struct device *dev) dev->hard_header = eth_header; dev->rebuild_header = eth_rebuild_header; dev->set_mac_address = eth_mac_addr; - dev->header_cache = eth_header_cache; + dev->header_cache_bind = eth_header_cache_bind; + dev->header_cache_update = eth_header_cache_update; dev->type = ARPHRD_ETHER; dev->hard_header_len = ETH_HLEN; diff --git a/drivers/net/smc-ultra.c b/drivers/net/smc-ultra.c index 36a301ca6feb..20537baf4c84 100644 --- a/drivers/net/smc-ultra.c +++ b/drivers/net/smc-ultra.c @@ -35,6 +35,10 @@ This driver does not support the programmed-I/O data transfer mode of the EtherEZ. That support (if available) is smc-ez.c. Nor does it use the non-8390-compatible "Altego" mode. (No support currently planned.) + + Changelog: + + Paul Gortmaker : multiple card support for module users. */ static const char *version = @@ -117,6 +121,7 @@ int ultra_probe1(struct device *dev, int ioaddr) int checksum = 0; const char *model_name; unsigned char eeprom_irq = 0; + static unsigned version_printed = 0; /* Values from various config regs. */ unsigned char num_pages, irqreg, addr; unsigned char idreg = inb(ioaddr + 7); @@ -135,10 +140,14 @@ int ultra_probe1(struct device *dev, int ioaddr) if ((checksum & 0xff) != 0xFF) return ENODEV; - if (dev == NULL) - dev = init_etherdev(0, sizeof(struct ei_device)); - if (dev == NULL) /* Still.. */ - return ENOMEM; /* Out of memory ?? */ + /* We should have a "dev" from Space.c or the static module table. */ + if (dev == NULL) { + printk("smc-ultra.c: Passed a NULL device.\n"); + dev = init_etherdev(0, 0); + } + + if (ei_debug && version_printed++ == 0) + printk(version); model_name = (idreg & 0xF0) == 0x20 ? "SMC Ultra" : "SMC EtherEZ"; @@ -175,7 +184,12 @@ int ultra_probe1(struct device *dev, int ioaddr) eeprom_irq = 1; } - + /* Allocate dev->priv and fill in 8390 specific dev fields. */ + if (ethdev_init(dev)) { + printk (", no memory for dev->priv.\n"); + return -ENOMEM; + } + /* OK, we are certain this is going to work. Setup the device. */ request_region(ioaddr, ULTRA_IO_EXTENT, model_name); @@ -190,8 +204,6 @@ int ultra_probe1(struct device *dev, int ioaddr) num_pages = num_pages_tbl[(addr >> 4) & 3]; } - ethdev_init(dev); - ei_status.name = model_name; ei_status.word16 = 1; ei_status.tx_start_page = START_PG; @@ -204,8 +216,6 @@ int ultra_probe1(struct device *dev, int ioaddr) printk(",%s IRQ %d memory %#lx-%#lx.\n", eeprom_irq ? "" : "assigned ", dev->irq, dev->mem_start, dev->mem_end-1); - if (ei_debug > 0) - printk(version); ei_status.reset_8390 = &ultra_reset_8390; ei_status.block_input = &ultra_block_input; @@ -222,7 +232,6 @@ static int ultra_open(struct device *dev) { int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */ - int rc; if (request_irq(dev->irq, ei_interrupt, 0, ei_status.name)) return -EAGAIN; @@ -230,8 +239,7 @@ ultra_open(struct device *dev) outb(ULTRA_MEMENB, ioaddr); /* Enable memory, 16 bit mode. */ outb(0x80, ioaddr + 5); outb(0x01, ioaddr + 6); /* Enable interrupts and memory. */ - rc = ei_open(dev); - if (rc != 0) return rc; + ei_open(dev); MOD_INC_USE_COUNT; return 0; } @@ -330,39 +338,67 @@ ultra_close_card(struct device *dev) return 0; } + #ifdef MODULE -static char devicename[9] = { 0, }; -static struct device dev_ultra = { - devicename, /* device name is inserted by linux/drivers/net/net_init.c */ - 0, 0, 0, 0, - 0, 0, - 0, 0, 0, NULL, ultra_probe }; - -static int io = 0x200; -static int irq = 0; - -int init_module(void) +#define MAX_ULTRA_CARDS 4 /* Max number of Ultra cards per module */ +#define NAMELEN 8 /* # of chars for storing dev->name */ +static char namelist[NAMELEN * MAX_ULTRA_CARDS] = { 0, }; +static struct device dev_ultra[MAX_ULTRA_CARDS] = { + { + NULL, /* assign a chunk of namelist[] below */ + 0, 0, 0, 0, + 0, 0, + 0, 0, 0, NULL, NULL + }, +}; + +static int io[MAX_ULTRA_CARDS] = { 0, }; +static int irq[MAX_ULTRA_CARDS] = { 0, }; + +/* This is set up so that only a single autoprobe takes place per call. +ISA device autoprobes on a running machine are not recommended. */ +int +init_module(void) { - if (io == 0) - printk("smc-ultra: You should not use auto-probing with insmod!\n"); - dev_ultra.base_addr = io; - dev_ultra.irq = irq; - if (register_netdev(&dev_ultra) != 0) { - printk("smc-ultra: register_netdev() returned non-zero.\n"); - return -EIO; + int this_dev, found = 0; + + for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) { + struct device *dev = &dev_ultra[this_dev]; + dev->name = namelist+(NAMELEN*this_dev); + dev->irq = irq[this_dev]; + dev->base_addr = io[this_dev]; + dev->init = ultra_probe; + if (io[this_dev] == 0) { + if (this_dev != 0) break; /* only autoprobe 1st one */ + printk(KERN_NOTICE "smc-ultra.c: Presently autoprobing (not recommended) for a single card.\n"); + } + if (register_netdev(dev) != 0) { + printk(KERN_WARNING "smc-ultra.c: No SMC Ultra card found (i/o = 0x%x).\n", io[this_dev]); + if (found != 0) return 0; /* Got at least one. */ + return -ENXIO; + } + found++; } + return 0; } void cleanup_module(void) { - int ioaddr = dev_ultra.base_addr - ULTRA_NIC_OFFSET; - - unregister_netdev(&dev_ultra); - - /* If we don't do this, we can't re-insmod it later. */ - release_region(ioaddr, ULTRA_IO_EXTENT); + int this_dev; + + for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) { + struct device *dev = &dev_ultra[this_dev]; + if (dev->priv != NULL) { + /* NB: ultra_close_card() does free_irq + irq2dev */ + int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; + kfree(dev->priv); + dev->priv = NULL; + release_region(ioaddr, ULTRA_IO_EXTENT); + unregister_netdev(dev); + } + } } #endif /* MODULE */ diff --git a/drivers/net/tunnel.c b/drivers/net/tunnel.c index dcd54df4840c..bac60259a355 100644 --- a/drivers/net/tunnel.c +++ b/drivers/net/tunnel.c @@ -88,7 +88,7 @@ int tunnel_init(struct device *dev) dev->addr_len=0; dev->hard_header_len=0; dev->hard_header=NULL; - dev->header_cache=NULL; + dev->header_cache_bind=NULL; dev->rebuild_header=NULL; /* End of stomp 8) */ return 0; @@ -271,7 +271,6 @@ tunnel_get_stats(struct device *dev) } #ifdef MODULE -char kernel_version[] = UTS_RELEASE; static int tunnel_probe(struct device *dev) { diff --git a/drivers/net/wd.c b/drivers/net/wd.c index eacf953521a3..84a6129bca1d 100644 --- a/drivers/net/wd.c +++ b/drivers/net/wd.c @@ -18,7 +18,9 @@ Changelog: - Paul Gortmaker : multiple card support for module users + Paul Gortmaker : multiple card support for module users, support + for non-standard memory sizes. + */ @@ -121,8 +123,11 @@ int wd_probe1(struct device *dev, int ioaddr) || (checksum & 0xff) != 0xFF) return ENODEV; - if (dev == NULL) - dev = init_etherdev(0, sizeof(struct ei_device)); + /* We should have a "dev" from Space.c or the static module table. */ + if (dev == NULL) { + printk("wd.c: Passed a NULL device.\n"); + dev = init_etherdev(0, 0); + } if (ei_debug && version_printed++ == 0) printk(version); @@ -246,20 +251,32 @@ int wd_probe1(struct device *dev, int ioaddr) return EAGAIN; } + /* Allocate dev->priv and fill in 8390 specific dev fields. */ + if (ethdev_init(dev)) { + printk (" unable to get memory for dev->priv.\n"); + free_irq(dev->irq); + return -ENOMEM; + } + /* OK, were are certain this is going to work. Setup the device. */ request_region(ioaddr, WD_IO_EXTENT,"wd"); - ethdev_init(dev); ei_status.name = model_name; ei_status.word16 = word16; ei_status.tx_start_page = WD_START_PG; ei_status.rx_start_page = WD_START_PG + TX_PAGES; - ei_status.stop_page = word16 ? WD13_STOP_PG : WD03_STOP_PG; /* Don't map in the shared memory until the board is actually opened. */ dev->rmem_start = dev->mem_start + TX_PAGES*256; - dev->mem_end = dev->rmem_end - = dev->mem_start + (ei_status.stop_page - WD_START_PG)*256; + + /* Some cards (eg WD8003EBT) can be jumpered for more (32k!) memory. */ + if (dev->mem_end != 0) { + ei_status.stop_page = (dev->mem_end - dev->mem_start)/256; + } else { + ei_status.stop_page = word16 ? WD13_STOP_PG : WD03_STOP_PG; + dev->mem_end = dev->mem_start + (ei_status.stop_page - WD_START_PG)*256; + } + dev->rmem_end = dev->mem_end; printk(" %s, IRQ %d, shared memory at %#lx-%#lx.\n", model_name, dev->irq, dev->mem_start, dev->mem_end-1); @@ -286,7 +303,6 @@ static int wd_open(struct device *dev) { int ioaddr = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ - int rc; /* Map in the shared memory. Always set register 0 last to remain compatible with very old boards. */ @@ -297,8 +313,7 @@ wd_open(struct device *dev) outb(ei_status.reg5, ioaddr+WD_CMDREG5); outb(ei_status.reg0, ioaddr); /* WD_CMDREG */ - rc = ei_open(dev); - if (rc != 0) return rc; + ei_open(dev); MOD_INC_USE_COUNT; return 0; } @@ -392,11 +407,11 @@ wd_close_card(struct device *dev) if (ei_debug > 1) printk("%s: Shutting down ethercard.\n", dev->name); - NS8390_init(dev, 0); - dev->start = 0; + ei_close(dev); /* Change from 16-bit to 8-bit shared memory so reboot works. */ - outb(ei_status.reg5, wd_cmdreg + WD_CMDREG5 ); + if (ei_status.word16) + outb(ei_status.reg5, wd_cmdreg + WD_CMDREG5 ); /* And disable the shared memory. */ outb(ei_status.reg0 & ~WD_MEMENB, wd_cmdreg); @@ -406,12 +421,12 @@ wd_close_card(struct device *dev) return 0; } - + #ifdef MODULE -#define MAX_WD_MODS 4 /* Max number of wd modules allowed */ -#define NAMELEN 9 /* # of chars for storing dev->name */ -static char namelist[NAMELEN * MAX_WD_MODS] = { 0, }; -static struct device dev_wd80x3[MAX_WD_MODS] = { +#define MAX_WD_CARDS 4 /* Max number of wd cards per module */ +#define NAMELEN 8 /* # of chars for storing dev->name */ +static char namelist[NAMELEN * MAX_WD_CARDS] = { 0, }; +static struct device dev_wd[MAX_WD_CARDS] = { { NULL, /* assign a chunk of namelist[] below */ 0, 0, 0, 0, @@ -420,31 +435,36 @@ static struct device dev_wd80x3[MAX_WD_MODS] = { }, }; -static int io[MAX_WD_MODS] = { 0, }; -static int irq[MAX_WD_MODS] = { 0, }; -static int mem[MAX_WD_MODS] = { 0, }; +static int io[MAX_WD_CARDS] = { 0, }; +static int irq[MAX_WD_CARDS] = { 0, }; +static int mem[MAX_WD_CARDS] = { 0, }; +static int mem_end[MAX_WD_CARDS] = { 0, }; /* for non std. mem size */ /* This is set up so that only a single autoprobe takes place per call. ISA device autoprobes on a running machine are not recommended. */ int init_module(void) { - int this_dev; - - for (this_dev = 0; this_dev < MAX_WD_MODS; this_dev++) { - dev_wd80x3[this_dev].name = namelist+(NAMELEN*this_dev); - dev_wd80x3[this_dev].irq = irq[this_dev]; - dev_wd80x3[this_dev].base_addr = io[this_dev]; - dev_wd80x3[this_dev].mem_start = mem[this_dev]; - dev_wd80x3[this_dev].init = wd_probe; + int this_dev, found = 0; + + for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) { + struct device *dev = &dev_wd[this_dev]; + dev->name = namelist+(NAMELEN*this_dev); + dev->irq = irq[this_dev]; + dev->base_addr = io[this_dev]; + dev->mem_start = mem[this_dev]; + dev->mem_end = mem_end[this_dev]; + dev->init = wd_probe; if (io[this_dev] == 0) { if (this_dev != 0) break; /* only autoprobe 1st one */ printk(KERN_NOTICE "wd.c: Presently autoprobing (not recommended) for a single card.\n"); } - if (register_netdev(&dev_wd80x3[this_dev]) != 0) { - printk(KERN_WARNING "modules: No wd80x3 card found (i/o = 0x%x).\n", io[this_dev]); - return -EIO; + if (register_netdev(dev) != 0) { + printk(KERN_WARNING "wd.c: No wd80x3 card found (i/o = 0x%x).\n", io[this_dev]); + if (found != 0) return 0; /* Got at least one. */ + return -ENXIO; } + found++; } return 0; @@ -455,12 +475,16 @@ cleanup_module(void) { int this_dev; - for (this_dev = 0; this_dev < MAX_WD_MODS; this_dev++) { - if (dev_wd80x3[this_dev].priv != NULL) { - int ioaddr = dev_wd80x3[this_dev].base_addr - WD_NIC_OFFSET; - unregister_netdev(&dev_wd80x3[this_dev]); - free_irq(dev_wd80x3[this_dev].irq); + for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) { + struct device *dev = &dev_wd[this_dev]; + if (dev->priv != NULL) { + int ioaddr = dev->base_addr - WD_NIC_OFFSET; + kfree(dev->priv); + dev->priv = NULL; + free_irq(dev->irq); + irq2dev_map[dev->irq] = NULL; release_region(ioaddr, WD_IO_EXTENT); + unregister_netdev(dev); } } } diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index cab5ddec6315..8b586894d1f3 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -289,22 +289,6 @@ struct pci_dev_info *pci_lookup_dev(unsigned int vendor, unsigned int dev) } } - -const char *pci_strbioserr (int error) -{ - switch (error) { - case PCIBIOS_SUCCESSFUL: return "SUCCESSFUL"; - case PCIBIOS_FUNC_NOT_SUPPORTED: return "FUNC_NOT_SUPPORTED"; - case PCIBIOS_BAD_VENDOR_ID: return "SUCCESSFUL"; - case PCIBIOS_DEVICE_NOT_FOUND: return "DEVICE_NOT_FOUND"; - case PCIBIOS_BAD_REGISTER_NUMBER: return "BAD_REGISTER_NUMBER"; - case PCIBIOS_SET_FAILED: return "SET_FAILED"; - case PCIBIOS_BUFFER_TOO_SMALL: return "BUFFER_TOO_SMALL"; - default: return "Unknown error status"; - } -} - - const char *pci_strclass (unsigned int class) { switch (class >> 8) { diff --git a/drivers/scsi/53c7,8xx.c b/drivers/scsi/53c7,8xx.c index 4874477c1e43..375649258564 100644 --- a/drivers/scsi/53c7,8xx.c +++ b/drivers/scsi/53c7,8xx.c @@ -894,7 +894,7 @@ static int ncr_pci_init (Scsi_Host_Template *tpnt, int board, int chip, &irq))) { printk ("scsi-ncr53c7,8xx : error %s not initializing due to error reading configuration space\n" " perhaps you specified an incorrect PCI bus, device, or function.\n" - , pci_strbioserr(error)); + , pcibios_strerror(error)); return -1; } diff --git a/drivers/scsi/BusLogic.c b/drivers/scsi/BusLogic.c new file mode 100644 index 000000000000..dfa10eb4725e --- /dev/null +++ b/drivers/scsi/BusLogic.c @@ -0,0 +1,2577 @@ +/* + + Linux Driver for BusLogic SCSI Host Adapters + + Copyright 1995 by Leonard N. Zubkoff + + This program is free software; you may redistribute and/or modify it under + the terms of the GNU General Public License Version 2 as published by the + Free Software Foundation, provided that none of the source code or runtime + copyright notices are removed or modified. + + 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 complete details. + + The author respectfully requests that all modifications to this software be + sent directly to him for evaluation and testing. + + Special thanks to Alex T. Win of BusLogic, whose advice has been invaluable, + and to David B. Gentzel, for writing the original Linux BusLogic driver. + +*/ + + +#define BusLogic_DriverVersion "1.3.0" +#define BusLogic_DriverDate "13 November 1995" + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "scsi.h" +#include "hosts.h" +#include "sd.h" +#include "BusLogic.h" + + +/* + BusLogic_CommandLineEntryCount is a count of the number of "BusLogic=" + entries provided on the Linux Kernel Command Line. +*/ + +static int + BusLogic_CommandLineEntryCount = 0; + + +/* + BusLogic_CommandLineEntries is an array of Command Line Entry structures + representing the "BusLogic=" entries provided on the Linux Kernel Command + Line. +*/ + +static BusLogic_CommandLineEntry_T + BusLogic_CommandLineEntries[BusLogic_MaxHostAdapters]; + + +/* + BusLogic_TracingOptions is a bit mask of Tracing Options to be applied + across all Host Adapters. +*/ + +static int + BusLogic_TracingOptions = 0; + + +/* + BusLogic_RegisteredHostAdapters is a linked list of all the registered + BusLogic Host Adapters. +*/ + +static BusLogic_HostAdapter_T + *BusLogic_RegisteredHostAdapters = NULL; + + +/* + BusLogic_Standard_IO_Addresses is the list of standard I/O Addresses at which + BusLogic Host Adapters may potentially be found. +*/ + +static unsigned short + BusLogic_IO_StandardAddresses[] = + { 0x330, 0x334, 0x230, 0x234, 0x130, 0x134, 0 }; + + +/* + BusLogic_IO_AddressProbeList is the list of I/O Addresses to be probed for + potential BusLogic Host Adapters. It is initialized by interrogating the + PCI Configuration Space on PCI machines as well as from the list of + standard BusLogic I/O Addresses. +*/ + +static unsigned short + BusLogic_IO_AddressProbeList[BusLogic_IO_MaxProbeAddresses+1] = { 0 }; + + +/* + BusLogic_IRQ_UsageCount stores a count of the number of Host Adapters using + a given IRQ Channel, which is necessary to support PCI, EISA, or MCA shared + interrupts. Only IRQ Channels 9, 10, 11, 12, 14, and 15 are supported by + BusLogic Host Adapters. +*/ + +static short + BusLogic_IRQ_UsageCount[7] = { 0 }; + + +/* + BusLogic_CommandFailureReason holds a string identifying the reason why a + call to BusLogic_Command failed. It is only valid when BusLogic_Command + returns a failure code. +*/ + +static char + *BusLogic_CommandFailureReason; + + +/* + BusLogic_ProcDirectoryEntry is the BusLogic /proc/scsi directory entry. +*/ + +static struct proc_dir_entry + BusLogic_ProcDirectoryEntry = + { PROC_SCSI_BUSLOGIC, 8, "BusLogic", S_IFDIR | S_IRUGO | S_IXUGO, 2 }; + + +/* + BusLogic_AnnounceDriver announces the Driver Version and Date, Author's + Name, Copyright Notice, and Contact Address. +*/ + +static void BusLogic_AnnounceDriver(void) +{ + static boolean DriverAnnouncementPrinted = false; + if (DriverAnnouncementPrinted) return; + printk("scsi: ***** BusLogic SCSI Driver Version " + BusLogic_DriverVersion " of " BusLogic_DriverDate " *****\n"); + printk("scsi: Copyright 1995 by Leonard N. Zubkoff " + "\n"); + DriverAnnouncementPrinted = true; +} + + +/* + BusLogic_DriverInfo returns the Board Name to identify this SCSI Driver + and Host Adapter. +*/ + +const char *BusLogic_DriverInfo(SCSI_Host_T *Host) +{ + BusLogic_HostAdapter_T *HostAdapter = + (BusLogic_HostAdapter_T *) Host->hostdata; + return HostAdapter->BoardName; +} + + +/* + BusLogic_InitializeAddressProbeList initializes the list of I/O Addresses + to be probed for potential BusLogic SCSI Host Adapters by interrogating the + PCI Configuration Space on PCI machines as well as from the list of standard + BusLogic I/O Addresses. +*/ + +static void BusLogic_InitializeAddressProbeList(void) +{ + int DestinationIndex = 0, SourceIndex = 0; + /* + If BusLogic_Setup has been called, do not override the Kernel Command + Line specifications. + */ + if (BusLogic_IO_AddressProbeList[0] != 0) return; +#ifdef CONFIG_PCI + /* + Interrogate PCI Configuration Space for any BusLogic SCSI Host Adapters. + */ + if (pcibios_present()) + { + unsigned short Index = 0, VendorID; + unsigned char Bus, DeviceAndFunction; + unsigned int BaseAddress0; + while (pcibios_find_class(PCI_CLASS_STORAGE_SCSI<<8, Index++, + &Bus, &DeviceAndFunction) == 0) + if (pcibios_read_config_word(Bus, DeviceAndFunction, + PCI_VENDOR_ID, &VendorID) == 0 && + VendorID == PCI_VENDOR_ID_BUSLOGIC && + pcibios_read_config_dword(Bus, DeviceAndFunction, + PCI_BASE_ADDRESS_0, &BaseAddress0) == 0 && + (BaseAddress0 & PCI_BASE_ADDRESS_SPACE) == + PCI_BASE_ADDRESS_SPACE_IO) + { + BusLogic_IO_AddressProbeList[DestinationIndex++] = + BaseAddress0 & PCI_BASE_ADDRESS_IO_MASK; + } + } +#endif + /* + Append the list of standard BusLogic I/O Addresses. + */ + while (DestinationIndex < BusLogic_IO_MaxProbeAddresses && + BusLogic_IO_StandardAddresses[SourceIndex] > 0) + BusLogic_IO_AddressProbeList[DestinationIndex++] = + BusLogic_IO_StandardAddresses[SourceIndex++]; + BusLogic_IO_AddressProbeList[DestinationIndex] = 0; +} + + +/* + BusLogic_RegisterHostAdapter adds Host Adapter to the list of registered + BusLogic Host Adapters. +*/ + +static void BusLogic_RegisterHostAdapter(BusLogic_HostAdapter_T *HostAdapter) +{ + HostAdapter->Next = NULL; + if (BusLogic_RegisteredHostAdapters != NULL) + { + BusLogic_HostAdapter_T *LastHostAdapter = BusLogic_RegisteredHostAdapters; + BusLogic_HostAdapter_T *NextHostAdapter; + while ((NextHostAdapter = LastHostAdapter->Next) != NULL) + LastHostAdapter = NextHostAdapter; + LastHostAdapter->Next = HostAdapter; + } + else BusLogic_RegisteredHostAdapters = HostAdapter; +} + + +/* + BusLogic_UnregisterHostAdapter removes Host Adapter from the list of + registered BusLogic Host Adapters. +*/ + +static void BusLogic_UnregisterHostAdapter(BusLogic_HostAdapter_T *HostAdapter) +{ + if (BusLogic_RegisteredHostAdapters != HostAdapter) + { + BusLogic_HostAdapter_T *LastHostAdapter = BusLogic_RegisteredHostAdapters; + while (LastHostAdapter != NULL && LastHostAdapter->Next != HostAdapter) + LastHostAdapter = LastHostAdapter->Next; + if (LastHostAdapter != NULL) + LastHostAdapter->Next = HostAdapter->Next; + } + else BusLogic_RegisteredHostAdapters = HostAdapter->Next; + HostAdapter->Next = NULL; +} + + +/* + BusLogic_CreateCCBs allocates the initial Command Control Blocks (CCBs) + for Host Adapter. +*/ + +static boolean BusLogic_CreateCCBs(BusLogic_HostAdapter_T *HostAdapter) +{ + int i; + for (i = 0; i < BusLogic_InitialCCBs; i++) + { + BusLogic_CCB_T *CCB = (BusLogic_CCB_T *) + scsi_init_malloc(sizeof(BusLogic_CCB_T), GFP_ATOMIC | GFP_DMA); + if (CCB == NULL) + { + printk("scsi%d: UNABLE TO ALLOCATE CCB %d - DETACHING\n", + HostAdapter->HostNumber, i); + return false; + } + memset(CCB, 0, sizeof(BusLogic_CCB_T)); + CCB->HostAdapter = HostAdapter; + CCB->Status = BusLogic_CCB_Free; + CCB->Next = HostAdapter->Free_CCBs; + CCB->NextAll = HostAdapter->All_CCBs; + HostAdapter->Free_CCBs = CCB; + HostAdapter->All_CCBs = CCB; + } + return true; +} + + +/* + BusLogic_DestroyCCBs deallocates the CCBs for Host Adapter. +*/ + +static void BusLogic_DestroyCCBs(BusLogic_HostAdapter_T *HostAdapter) +{ + BusLogic_CCB_T *NextCCB = HostAdapter->All_CCBs, *CCB; + HostAdapter->All_CCBs = NULL; + HostAdapter->Free_CCBs = NULL; + while ((CCB = NextCCB) != NULL) + { + NextCCB = CCB->NextAll; + scsi_init_free((char *) CCB, sizeof(BusLogic_CCB_T)); + } +} + + +/* + BusLogic_AllocateCCB allocates a CCB from the Host Adapter's free list, + allocating more memory from the Kernel if necessary. +*/ + +static BusLogic_CCB_T *BusLogic_AllocateCCB(BusLogic_HostAdapter_T *HostAdapter) +{ + static unsigned int SerialNumber = 0; + BusLogic_CCB_T *CCB; + BusLogic_LockHostAdapter(HostAdapter); + CCB = HostAdapter->Free_CCBs; + if (CCB != NULL) + { + CCB->SerialNumber = SerialNumber++; + HostAdapter->Free_CCBs = CCB->Next; + BusLogic_UnlockHostAdapter(HostAdapter); + return CCB; + } + BusLogic_UnlockHostAdapter(HostAdapter); + CCB = (BusLogic_CCB_T *) scsi_init_malloc(sizeof(BusLogic_CCB_T), + GFP_ATOMIC | GFP_DMA); + if (CCB == NULL) + { + printk("scsi%d: Failed to allocate an additional CCB\n", + HostAdapter->HostNumber); + return NULL; + } + printk("scsi%d: Allocated an additional CCB\n", HostAdapter->HostNumber); + memset(CCB, 0, sizeof(BusLogic_CCB_T)); + CCB->HostAdapter = HostAdapter; + CCB->Status = BusLogic_CCB_Free; + BusLogic_LockHostAdapter(HostAdapter); + CCB->SerialNumber = SerialNumber++; + CCB->NextAll = HostAdapter->All_CCBs; + HostAdapter->All_CCBs = CCB; + BusLogic_UnlockHostAdapter(HostAdapter); + return CCB; +} + + +/* + BusLogic_DeallocateCCB deallocates a CCB, returning it to the Host Adapter's + free list. +*/ + +static void BusLogic_DeallocateCCB(BusLogic_CCB_T *CCB) +{ + BusLogic_HostAdapter_T *HostAdapter = CCB->HostAdapter; + BusLogic_LockHostAdapter(HostAdapter); + CCB->Command = NULL; + CCB->Status = BusLogic_CCB_Free; + CCB->SerialNumber = 0; + CCB->Next = HostAdapter->Free_CCBs; + HostAdapter->Free_CCBs = CCB; + BusLogic_UnlockHostAdapter(HostAdapter); +} + + +/* + BusLogic_Command sends the command OperationCode to HostAdapter, optionally + providing ParameterLength bytes of ParameterData and receiving at most + ReplyLength bytes of ReplyData; any excess reply data is received but + discarded. + + On success, this function returns the number of reply bytes read from + the Host Adapter (including any discarded data); on failure, it returns + -1 if the command was invalid, or -2 if a timeout occurred. + + This function is only called during board detection and initialization, so + performance and latency are not critical, and exclusive access to the Host + Adapter hardware is assumed. Once the board and driver are initialized, the + only Host Adapter command that is issued is the single byte Start Mailbox + Scan command, which does not require waiting for the Host Adapter Ready bit + to be set in the Status Register. +*/ + +static int BusLogic_Command(BusLogic_HostAdapter_T *HostAdapter, + BusLogic_OperationCode_T OperationCode, + void *ParameterData, + int ParameterLength, + void *ReplyData, + int ReplyLength) +{ + unsigned char *ParameterPointer = (unsigned char *) ParameterData; + unsigned char *ReplyPointer = (unsigned char *) ReplyData; + unsigned char StatusRegister = 0, InterruptRegister; + long TimeoutCounter; + int ReplyBytes = 0; + /* + Clear out the Reply Data if provided. + */ + if (ReplyLength > 0) + memset(ReplyData, 0, ReplyLength); + /* + Select an appropriate timeout value. + */ + switch (OperationCode) + { + case BusLogic_InquireInstalledDevicesID0to7: + case BusLogic_InquireInstalledDevicesID8to15: + /* Approximately 60 seconds. */ + TimeoutCounter = loops_per_sec << 2; + break; + default: + /* Approximately 1 second. */ + TimeoutCounter = loops_per_sec >> 4; + break; + } + /* + Wait for the Host Adapter Ready bit to be set and the Command/Parameter + Register Busy bit to be reset in the Status Register. + */ + while (--TimeoutCounter >= 0) + { + StatusRegister = BusLogic_ReadStatusRegister(HostAdapter); + if ((StatusRegister & BusLogic_HostAdapterReady) && + !(StatusRegister & BusLogic_CommandParameterRegisterBusy)) + break; + } + BusLogic_CommandFailureReason = "Timeout waiting for Host Adapter Ready"; + if (TimeoutCounter < 0) return -2; + /* + Write the OperationCode to the Command/Parameter Register. + */ + BusLogic_WriteCommandParameterRegister(HostAdapter, OperationCode); + /* + Write any additional Parameter Bytes. + */ + HostAdapter->HostAdapterCommandCompleted = false; + while (--ParameterLength >= 0) + { + InterruptRegister = BusLogic_ReadInterruptRegister(HostAdapter); + if (InterruptRegister & BusLogic_CommandComplete) break; + if (HostAdapter->HostAdapterCommandCompleted) break; + while (--TimeoutCounter >= 0) + { + StatusRegister = BusLogic_ReadStatusRegister(HostAdapter); + if (!(StatusRegister & BusLogic_CommandParameterRegisterBusy)) break; + } + BusLogic_CommandFailureReason = + "Timeout waiting for Parameter Acceptance"; + if (TimeoutCounter < 0) return -2; + BusLogic_WriteCommandParameterRegister(HostAdapter, *ParameterPointer++); + } + BusLogic_CommandFailureReason = "Excess Parameters Supplied"; + if (ParameterLength >= 0) return -1; + /* + The Modify I/O Address command does not cause a Command Complete Interrupt. + */ + if (OperationCode == BusLogic_ModifyIOAddress) + { + StatusRegister = BusLogic_ReadStatusRegister(HostAdapter); + BusLogic_CommandFailureReason = "Modify I/O Address Invalid"; + return ((StatusRegister & BusLogic_CommandInvalid) ? -1 : 0); + } + /* + Receive any Reply Bytes, waiting for either the Command Complete bit to + be set in the Interrupt Register, or for the Interrupt Handler to set the + HostAdapterCommandCompleted bit in the Host Adapter structure. + */ + HostAdapter->HostAdapterCommandCompleted = false; + while (--TimeoutCounter >= 0) + { + InterruptRegister = BusLogic_ReadInterruptRegister(HostAdapter); + StatusRegister = BusLogic_ReadStatusRegister(HostAdapter); + if (InterruptRegister & BusLogic_CommandComplete) break; + if (HostAdapter->HostAdapterCommandCompleted) break; + if (StatusRegister & BusLogic_DataInRegisterReady) + if (++ReplyBytes <= ReplyLength) + *ReplyPointer++ = BusLogic_ReadDataInRegister(HostAdapter); + else BusLogic_ReadDataInRegister(HostAdapter); + } + BusLogic_CommandFailureReason = "Timeout waiting for Command Complete"; + if (TimeoutCounter < 0) return -2; + /* + Clear any pending Command Complete Interrupt, unless this is a + Test Command Complete Interrupt command. + */ + if (OperationCode != BusLogic_TestCommandCompleteInterrupt) + BusLogic_WriteControlRegister(HostAdapter, BusLogic_InterruptReset); + if (BusLogic_TracingOptions & BusLogic_TraceConfiguration) + if (OperationCode != BusLogic_TestCommandCompleteInterrupt) + { + int i; + printk("BusLogic_Command(%02X) Status = %02X: %2d ==> %2d:", + OperationCode, StatusRegister, ReplyLength, ReplyBytes); + for (i = 0; i < ReplyBytes; i++) + printk(" %02X", ((unsigned char *) ReplyData)[i]); + printk("\n"); + } + /* + Return count of Reply Bytes, or -1 if the command was invalid. + */ + BusLogic_CommandFailureReason = "Command Invalid"; + return ((StatusRegister & BusLogic_CommandInvalid) ? -1 : ReplyBytes); +} + + +/* + BusLogic_Failure prints a standardized error message for tests that are + executed before the SCSI Host is registered, and then returns false. +*/ + +static boolean BusLogic_Failure(BusLogic_HostAdapter_T *HostAdapter, + char *ErrorMessage) +{ + BusLogic_AnnounceDriver(); + printk("While configuring BusLogic Host Adapter at I/O Address 0x%X:\n", + HostAdapter->IO_Address); + printk("%s FAILED - DETACHING\n", ErrorMessage); + if (BusLogic_CommandFailureReason != NULL) + printk("ADDITIONAL FAILURE INFO - %s\n", BusLogic_CommandFailureReason); + return false; +} + + +/* + BusLogic_ProbeHostAdapter probes for a BusLogic Host Adapter. +*/ + +static boolean BusLogic_ProbeHostAdapter(BusLogic_HostAdapter_T *HostAdapter) +{ + boolean TraceProbe = (BusLogic_TracingOptions & BusLogic_TraceProbe); + unsigned char StatusRegister, GeometryRegister; + /* + Read the Status Register to test if there is an I/O port that responds. A + nonexistent I/O port will return 0xFF, in which case there is definitely no + BusLogic Host Adapter at this base I/O Address. + */ + StatusRegister = BusLogic_ReadStatusRegister(HostAdapter); + if (TraceProbe) + printk("BusLogic_Probe(0x%X): Status 0x%02X\n", + HostAdapter->IO_Address, StatusRegister); + if (StatusRegister == 0xFF) return false; + /* + Read the undocumented BusLogic Geometry Register to test if there is an I/O + port that responds. Adaptec Host Adapters do not implement the Geometry + Register, so this test helps serve to avoid incorrectly recognizing an + Adaptec 1542A or 1542B as a BusLogic. Unfortunately, the Adaptec 1542C + series does respond to the Geometry Register I/O port, but it will be + rejected later when the Inquire Extended Setup Information command is + issued in BusLogic_CheckHostAdapter. The AMI FastDisk Host Adapter is a + BusLogic clone that implements the same interface as earlier BusLogic + boards, including the undocumented commands, and is therefore supported by + this driver. However, the AMI FastDisk always returns 0x00 upon reading + the Geometry Register, so the extended translation option should always be + left disabled on the AMI FastDisk. + */ + GeometryRegister = BusLogic_ReadGeometryRegister(HostAdapter); + if (TraceProbe) + printk("BusLogic_Probe(0x%X): Geometry 0x%02X\n", + HostAdapter->IO_Address, GeometryRegister); + if (GeometryRegister == 0xFF) return false; + /* + Indicate the Host Adapter Probe completed successfully. + */ + return true; +} + + +/* + BusLogic_HardResetHostAdapter issues a Hard Reset to the Host Adapter, + and waits for Host Adapter Diagnostics to complete. +*/ + +static boolean BusLogic_HardResetHostAdapter(BusLogic_HostAdapter_T + *HostAdapter) +{ + boolean TraceHardReset = (BusLogic_TracingOptions & BusLogic_TraceHardReset); + long TimeoutCounter = loops_per_sec >> 2; + unsigned char StatusRegister = 0; + /* + Issue a Hard Reset Command to the Host Adapter. The Host Adapter should + respond by setting Diagnostic Active in the Status Register. + */ + BusLogic_WriteControlRegister(HostAdapter, BusLogic_HardReset); + /* + Wait until Diagnostic Active is set in the Status Register. + */ + while (--TimeoutCounter >= 0) + { + StatusRegister = BusLogic_ReadStatusRegister(HostAdapter); + if ((StatusRegister & BusLogic_DiagnosticActive)) break; + } + if (TraceHardReset) + printk("BusLogic_HardReset(0x%X): Diagnostic Active, Status 0x%02X\n", + HostAdapter->IO_Address, StatusRegister); + if (TimeoutCounter < 0) return false; + /* + Wait until Diagnostic Active is reset in the Status Register. + */ + while (--TimeoutCounter >= 0) + { + StatusRegister = BusLogic_ReadStatusRegister(HostAdapter); + if (!(StatusRegister & BusLogic_DiagnosticActive)) break; + } + if (TraceHardReset) + printk("BusLogic_HardReset(0x%X): Diagnostic Completed, Status 0x%02X\n", + HostAdapter->IO_Address, StatusRegister); + if (TimeoutCounter < 0) return false; + /* + Wait until at least one of the Diagnostic Failure, Host Adapter Ready, + or Data In Register Ready bits is set in the Status Register. + */ + while (--TimeoutCounter >= 0) + { + StatusRegister = BusLogic_ReadStatusRegister(HostAdapter); + if (StatusRegister & (BusLogic_DiagnosticFailure | + BusLogic_HostAdapterReady | + BusLogic_DataInRegisterReady)) + break; + } + if (TraceHardReset) + printk("BusLogic_HardReset(0x%X): Host Adapter Ready, Status 0x%02X\n", + HostAdapter->IO_Address, StatusRegister); + if (TimeoutCounter < 0) return false; + /* + If Diagnostic Failure is set or Host Adapter Ready is reset, then an + error occurred during the Host Adapter diagnostics. If Data In Register + Ready is set, then there is an Error Code available. + */ + if ((StatusRegister & BusLogic_DiagnosticFailure) || + !(StatusRegister & BusLogic_HostAdapterReady)) + { + BusLogic_CommandFailureReason = NULL; + BusLogic_Failure(HostAdapter, "HARD RESET DIAGNOSTICS"); + printk("HOST ADAPTER STATUS REGISTER = %02X\n", StatusRegister); + if (StatusRegister & BusLogic_DataInRegisterReady) + { + unsigned char ErrorCode = BusLogic_ReadDataInRegister(HostAdapter); + printk("HOST ADAPTER ERROR CODE = %d\n", ErrorCode); + } + return false; + } + /* + Indicate the Host Adapter Hard Reset completed successfully. + */ + return true; +} + + +/* + BusLogic_CheckHostAdapter checks to be sure this really is a BusLogic + Host Adapter. +*/ + +static boolean BusLogic_CheckHostAdapter(BusLogic_HostAdapter_T *HostAdapter) +{ + BusLogic_ExtendedSetupInformation_T ExtendedSetupInformation; + BusLogic_RequestedReplyLength_T RequestedReplyLength; + unsigned long ProcessorFlags; + int Result; + /* + Issue the Inquire Setup Information command. Only genuine BusLogic Host + Adapters and true clones support this command. Adaptec 1542C series Host + Adapters that respond to the Geometry Register I/O port will fail this + command. Interrupts must be disabled around the call to BusLogic_Command + since a Command Complete interrupt could occur if the IRQ Channel was + previously enabled for another BusLogic Host Adapter sharing the same IRQ + Channel. + */ + save_flags(ProcessorFlags); + cli(); + RequestedReplyLength = sizeof(ExtendedSetupInformation); + Result = BusLogic_Command(HostAdapter, + BusLogic_InquireExtendedSetupInformation, + &RequestedReplyLength, sizeof(RequestedReplyLength), + &ExtendedSetupInformation, + sizeof(ExtendedSetupInformation)); + restore_flags(ProcessorFlags); + if (BusLogic_TracingOptions & BusLogic_TraceProbe) + printk("BusLogic_Check(0x%X): Result %d\n", + HostAdapter->IO_Address, Result); + return (Result == sizeof(ExtendedSetupInformation)); +} + + +/* + BusLogic_ReadHostAdapterConfiguration reads the Configuration Information + from Host Adapter. +*/ + +static boolean BusLogic_ReadHostAdapterConfiguration(BusLogic_HostAdapter_T + *HostAdapter) +{ + BusLogic_BoardID_T BoardID; + BusLogic_Configuration_T Configuration; + BusLogic_SetupInformation_T SetupInformation; + BusLogic_ExtendedSetupInformation_T ExtendedSetupInformation; + BusLogic_ModelAndRevision_T ModelAndRevision; + BusLogic_FirmwareVersion3rdDigit_T FirmwareVersion3rdDigit; + BusLogic_FirmwareVersionLetter_T FirmwareVersionLetter; + BusLogic_RequestedReplyLength_T RequestedReplyLength; + unsigned char GeometryRegister, *TargetPointer, Character; + unsigned short AllTargetsMask, DisconnectPermitted; + unsigned short TaggedQueuingPermitted, TaggedQueuingPermittedDefault; + boolean CommonErrorRecovery; + int TargetID, i; + /* + Issue the Inquire Board ID command. + */ + if (BusLogic_Command(HostAdapter, BusLogic_InquireBoardID, NULL, 0, + &BoardID, sizeof(BoardID)) != sizeof(BoardID)) + return BusLogic_Failure(HostAdapter, "INQUIRE BOARD ID"); + /* + Issue the Inquire Configuration command. + */ + if (BusLogic_Command(HostAdapter, BusLogic_InquireConfiguration, NULL, 0, + &Configuration, sizeof(Configuration)) + != sizeof(Configuration)) + return BusLogic_Failure(HostAdapter, "INQUIRE CONFIGURATION"); + /* + Issue the Inquire Setup Information command. + */ + RequestedReplyLength = sizeof(SetupInformation); + if (BusLogic_Command(HostAdapter, BusLogic_InquireSetupInformation, + &RequestedReplyLength, sizeof(RequestedReplyLength), + &SetupInformation, sizeof(SetupInformation)) + != sizeof(SetupInformation)) + return BusLogic_Failure(HostAdapter, "INQUIRE SETUP INFORMATION"); + /* + Issue the Inquire Extended Setup Information command. + */ + RequestedReplyLength = sizeof(ExtendedSetupInformation); + if (BusLogic_Command(HostAdapter, BusLogic_InquireExtendedSetupInformation, + &RequestedReplyLength, sizeof(RequestedReplyLength), + &ExtendedSetupInformation, + sizeof(ExtendedSetupInformation)) + != sizeof(ExtendedSetupInformation)) + return BusLogic_Failure(HostAdapter, "INQUIRE EXTENDED SETUP INFORMATION"); + /* + Issue the Inquire Board Model and Revision command. + */ + RequestedReplyLength = sizeof(ModelAndRevision); + if (BusLogic_Command(HostAdapter, BusLogic_InquireBoardModelAndRevision, + &RequestedReplyLength, sizeof(RequestedReplyLength), + &ModelAndRevision, sizeof(ModelAndRevision)) + != sizeof(ModelAndRevision)) + return BusLogic_Failure(HostAdapter, "INQUIRE BOARD MODEL AND REVISION"); + /* + Issue the Inquire Firmware Version 3rd Digit command. + */ + if (BusLogic_Command(HostAdapter, BusLogic_InquireFirmwareVersion3rdDigit, + NULL, 0, &FirmwareVersion3rdDigit, + sizeof(FirmwareVersion3rdDigit)) + != sizeof(FirmwareVersion3rdDigit)) + return BusLogic_Failure(HostAdapter, "INQUIRE FIRMWARE 3RD DIGIT"); + /* + Issue the Inquire Firmware Version Letter command. + */ + FirmwareVersionLetter = '\0'; + if (BoardID.FirmwareVersion1stDigit >= '3') + if (BusLogic_Command(HostAdapter, BusLogic_InquireFirmwareVersionLetter, + NULL, 0, &FirmwareVersionLetter, + sizeof(FirmwareVersionLetter)) + != sizeof(FirmwareVersionLetter)) + return BusLogic_Failure(HostAdapter, "INQUIRE FIRMWARE VERSION LETTER"); + /* + BusLogic Host Adapters can be identified by their model number and + the major version number of their firmware as follows: + + 4.xx BusLogic "C" Series Host Adapters: + 946C/956C/956CD/747C/757C/757CD/445C/545C/540CF + 3.xx BusLogic "S" Series Host Adapters: + 747S/747D/757S/757D/445S/545S/542D + 542B/742A (revision H) + 2.xx BusLogic "A" Series Host Adapters: + 542B/742A (revision G and below) + 0.xx AMI FastDisk VLB BusLogic Clone Host Adapter + */ + /* + Save the Model Name and Board Name in the Host Adapter structure. + */ + TargetPointer = HostAdapter->ModelName; + for (i = 0; i < sizeof(ModelAndRevision.Model); i++) + { + Character = ModelAndRevision.Model[i]; + if (Character == ' ' || Character == '\0') break; + *TargetPointer++ = Character; + } + *TargetPointer++ = '\0'; + strcpy(HostAdapter->BoardName, "BusLogic "); + strcat(HostAdapter->BoardName, HostAdapter->ModelName); + strcpy(HostAdapter->InterruptLabel, HostAdapter->BoardName); + /* + Save the Firmware Version in the Host Adapter structure. + */ + TargetPointer = HostAdapter->FirmwareVersion; + *TargetPointer++ = BoardID.FirmwareVersion1stDigit; + *TargetPointer++ = '.'; + *TargetPointer++ = BoardID.FirmwareVersion2ndDigit; + if (FirmwareVersion3rdDigit != ' ' && FirmwareVersion3rdDigit != '\0') + *TargetPointer++ = FirmwareVersion3rdDigit; + if (FirmwareVersionLetter != ' ' && FirmwareVersionLetter != '\0') + *TargetPointer++ = FirmwareVersionLetter; + *TargetPointer++ = '\0'; + /* + Determine the IRQ Channel and save it in the Host Adapter structure. + */ + if (Configuration.IRQ_Channel9) + HostAdapter->IRQ_Channel = 9; + else if (Configuration.IRQ_Channel10) + HostAdapter->IRQ_Channel = 10; + else if (Configuration.IRQ_Channel11) + HostAdapter->IRQ_Channel = 11; + else if (Configuration.IRQ_Channel12) + HostAdapter->IRQ_Channel = 12; + else if (Configuration.IRQ_Channel14) + HostAdapter->IRQ_Channel = 14; + else if (Configuration.IRQ_Channel15) + HostAdapter->IRQ_Channel = 15; + /* + Determine the DMA Channel and save it in the Host Adapter structure. + */ + if (Configuration.DMA_Channel5) + HostAdapter->DMA_Channel = 5; + else if (Configuration.DMA_Channel6) + HostAdapter->DMA_Channel = 6; + else if (Configuration.DMA_Channel7) + HostAdapter->DMA_Channel = 7; + /* + Save the Host Adapter SCSI ID in the Host Adapter structure. + */ + HostAdapter->SCSI_ID = Configuration.HostAdapterID; + /* + Save the Synchronous Initiation flag and SCSI Parity Checking flag + in the Host Adapter structure. + */ + HostAdapter->SynchronousInitiation = + SetupInformation.SynchronousInitiationEnabled; + HostAdapter->ParityChecking = SetupInformation.ParityCheckEnabled; + /* + Determine the Bus Type and save it in the Host Adapter structure, + overriding the DMA Channel if it is inappropriate for the bus type. + */ + if (ExtendedSetupInformation.BusType == 'A') + HostAdapter->BusType = BusLogic_ISA_Bus; + else + switch (HostAdapter->ModelName[0]) + { + case '4': + HostAdapter->BusType = BusLogic_VESA_Bus; + HostAdapter->DMA_Channel = 0; + break; + case '5': + HostAdapter->BusType = BusLogic_ISA_Bus; + break; + case '6': + HostAdapter->BusType = BusLogic_MCA_Bus; + HostAdapter->DMA_Channel = 0; + break; + case '7': + HostAdapter->BusType = BusLogic_EISA_Bus; + HostAdapter->DMA_Channel = 0; + break; + case '9': + HostAdapter->BusType = BusLogic_PCI_Bus; + HostAdapter->DMA_Channel = 0; + break; + } + /* + Determine whether Extended Translation is enabled and save it in + the Host Adapter structure. + */ + GeometryRegister = BusLogic_ReadGeometryRegister(HostAdapter); + if (GeometryRegister & BusLogic_ExtendedTranslationEnabled) + HostAdapter->ExtendedTranslation = true; + /* + Save the Disconnect/Reconnect Permitted flag bits in the Host Adapter + structure. The Disconnect Permitted information is only valid on "C" + Series boards, but Disconnect/Reconnect is always permitted on "S" and + "A" Series boards. + */ + if (HostAdapter->FirmwareVersion[0] >= '4') + HostAdapter->DisconnectPermitted = + (SetupInformation.DisconnectPermittedID8to15 << 8) + | SetupInformation.DisconnectPermittedID0to7; + else HostAdapter->DisconnectPermitted = 0xFF; + /* + Save the Scatter Gather Limits, Level Triggered Interrupts flag, + Wide SCSI flag, and Differential SCSI flag in the Host Adapter structure. + */ + HostAdapter->HostAdapterScatterGatherLimit = + ExtendedSetupInformation.ScatterGatherLimit; + HostAdapter->DriverScatterGatherLimit = + HostAdapter->HostAdapterScatterGatherLimit; + if (HostAdapter->HostAdapterScatterGatherLimit > BusLogic_ScatterGatherLimit) + HostAdapter->DriverScatterGatherLimit = BusLogic_ScatterGatherLimit; + if (ExtendedSetupInformation.Misc.LevelTriggeredInterrupts) + HostAdapter->LevelTriggeredInterrupts = true; + if (ExtendedSetupInformation.HostWideSCSI) + { + HostAdapter->HostWideSCSI = true; + HostAdapter->MaxTargetIDs = 16; + HostAdapter->MaxLogicalUnits = 64; + } + else + { + HostAdapter->HostWideSCSI = false; + HostAdapter->MaxTargetIDs = 8; + HostAdapter->MaxLogicalUnits = 8; + } + HostAdapter->HostDifferentialSCSI = + ExtendedSetupInformation.HostDifferentialSCSI; + /* + Determine the Host Adapter BIOS Address if the BIOS is enabled and + save it in the Host Adapter structure. The BIOS is disabled if the + BIOS_Address is 0. + */ + HostAdapter->BIOS_Address = ExtendedSetupInformation.BIOS_Address << 12; + /* + Select an appropriate value for Concurrency (Commands per Logical Unit) + either from a Command Line Entry, or based on whether this is an ISA + or non-ISA Host Adapter. + */ + if (HostAdapter->CommandLineEntry != NULL && + HostAdapter->CommandLineEntry->Concurrency > 0) + HostAdapter->Concurrency = HostAdapter->CommandLineEntry->Concurrency; + else if (HostAdapter->BusType == BusLogic_ISA_Bus) + HostAdapter->Concurrency = BusLogic_Concurrency_ISA; + else HostAdapter->Concurrency = BusLogic_Concurrency; + /* + Select an appropriate value for Bus Settle Time either from a Command + Line Entry, or from BusLogic_DefaultBusSettleTime. + */ + if (HostAdapter->CommandLineEntry != NULL && + HostAdapter->CommandLineEntry->BusSettleTime > 0) + HostAdapter->BusSettleTime = HostAdapter->CommandLineEntry->BusSettleTime; + else HostAdapter->BusSettleTime = BusLogic_DefaultBusSettleTime; + /* + Select appropriate values for the Error Recovery Option array either from + a Command Line Entry, or using BusLogic_ErrorRecoveryDefault. + */ + if (HostAdapter->CommandLineEntry != NULL) + memcpy(HostAdapter->ErrorRecoveryOption, + HostAdapter->CommandLineEntry->ErrorRecoveryOption, + sizeof(HostAdapter->ErrorRecoveryOption)); + else memset(HostAdapter->ErrorRecoveryOption, + BusLogic_ErrorRecoveryDefault, + sizeof(HostAdapter->ErrorRecoveryOption)); + /* + Tagged Queuing support is available and operates properly only on "C" + Series boards with firmware version 4.22 and above and on "S" Series + boards with firmware version 3.35 and above. Tagged Queuing is disabled + by default when the Concurrency value is 1 since queuing multiple commands + is not possible. + */ + TaggedQueuingPermittedDefault = 0; + if (HostAdapter->Concurrency > 1) + switch (HostAdapter->FirmwareVersion[0]) + { + case '4': + if (HostAdapter->FirmwareVersion[2] > '2' || + (HostAdapter->FirmwareVersion[2] == '2' && + HostAdapter->FirmwareVersion[3] >= '2')) + TaggedQueuingPermittedDefault = 0xFFFF; + break; + case '3': + if (HostAdapter->FirmwareVersion[2] > '3' || + (HostAdapter->FirmwareVersion[2] == '3' && + HostAdapter->FirmwareVersion[3] >= '5')) + TaggedQueuingPermittedDefault = 0xFFFF; + break; + } + /* + Combine the default Tagged Queuing permission based on the Host Adapter + firmware version and Concurrency with any Command Line Entry Tagged + Queuing specification. + */ + if (HostAdapter->CommandLineEntry != NULL) + HostAdapter->TaggedQueuingPermitted = + (HostAdapter->CommandLineEntry->TaggedQueuingPermitted & + HostAdapter->CommandLineEntry->TaggedQueuingPermittedMask) | + (TaggedQueuingPermittedDefault & + ~HostAdapter->CommandLineEntry->TaggedQueuingPermittedMask); + else HostAdapter->TaggedQueuingPermitted = TaggedQueuingPermittedDefault; + /* + Announce the Host Adapter Configuration. + */ + printk("scsi%d: Configuring BusLogic Model %s %s%s%s SCSI Host Adapter\n", + HostAdapter->HostNumber, HostAdapter->ModelName, + BusLogic_BusNames[HostAdapter->BusType], + (HostAdapter->HostWideSCSI ? " Wide" : ""), + (HostAdapter->HostDifferentialSCSI ? " Differential" : "")); + printk("scsi%d: Firmware Version: %s, I/O Address: 0x%X, " + "IRQ Channel: %d/%s\n", + HostAdapter->HostNumber, HostAdapter->FirmwareVersion, + HostAdapter->IO_Address, HostAdapter->IRQ_Channel, + (HostAdapter->LevelTriggeredInterrupts ? "Level" : "Edge")); + printk("scsi%d: DMA Channel: ", HostAdapter->HostNumber); + if (HostAdapter->DMA_Channel > 0) + printk("%d, ", HostAdapter->DMA_Channel); + else printk("None, "); + if (HostAdapter->BIOS_Address > 0) + printk("BIOS Address: 0x%lX, ", HostAdapter->BIOS_Address); + else printk("BIOS Address: None, "); + printk("Host Adapter SCSI ID: %d\n", HostAdapter->SCSI_ID); + printk("scsi%d: Scatter/Gather Limit: %d segments, " + "Synchronous Initiation: %s\n", HostAdapter->HostNumber, + HostAdapter->HostAdapterScatterGatherLimit, + (HostAdapter->SynchronousInitiation ? "Enabled" : "Disabled")); + printk("scsi%d: SCSI Parity Checking: %s, " + "Extended Disk Translation: %s\n", HostAdapter->HostNumber, + (HostAdapter->ParityChecking ? "Enabled" : "Disabled"), + (HostAdapter->ExtendedTranslation ? "Enabled" : "Disabled")); + AllTargetsMask = (1 << HostAdapter->MaxTargetIDs) - 1; + DisconnectPermitted = HostAdapter->DisconnectPermitted & AllTargetsMask; + printk("scsi%d: Disconnect/Reconnect: ", HostAdapter->HostNumber); + if (DisconnectPermitted == 0) + printk("Disabled"); + else if (DisconnectPermitted == AllTargetsMask) + printk("Enabled"); + else + for (TargetID = 0; TargetID < HostAdapter->MaxTargetIDs; TargetID++) + printk("%c", (DisconnectPermitted & (1 << TargetID)) ? 'Y' : 'N'); + printk(", Tagged Queuing: "); + TaggedQueuingPermitted = + HostAdapter->TaggedQueuingPermitted & AllTargetsMask; + if (TaggedQueuingPermitted == 0) + printk("Disabled"); + else if (TaggedQueuingPermitted == AllTargetsMask) + printk("Enabled"); + else + for (TargetID = 0; TargetID < HostAdapter->MaxTargetIDs; TargetID++) + printk("%c", (TaggedQueuingPermitted & (1 << TargetID)) ? 'Y' : 'N'); + printk("\n"); + CommonErrorRecovery = true; + for (TargetID = 1; TargetID < HostAdapter->MaxTargetIDs; TargetID++) + if (HostAdapter->ErrorRecoveryOption[TargetID] != + HostAdapter->ErrorRecoveryOption[0]) + { + CommonErrorRecovery = false; + break; + } + printk("scsi%d: Error Recovery: ", HostAdapter->HostNumber); + if (CommonErrorRecovery) + printk("%s", BusLogic_ErrorRecoveryOptions[ + HostAdapter->ErrorRecoveryOption[0]]); + else + for (TargetID = 0; TargetID < HostAdapter->MaxTargetIDs; TargetID++) + printk("%s", BusLogic_ErrorRecoveryOptions2[ + HostAdapter->ErrorRecoveryOption[TargetID]]); + printk(", Mailboxes: %d, Initial CCBs: %d\n", + BusLogic_MailboxCount, BusLogic_InitialCCBs); + printk("scsi%d: Driver Scatter/Gather Limit: %d segments, " + "Concurrency: %d\n", HostAdapter->HostNumber, + HostAdapter->DriverScatterGatherLimit, HostAdapter->Concurrency); + /* + Indicate reading the Host Adapter Configuration completed successfully. + */ + return true; +} + + +/* + BusLogic_AcquireResources acquires the system resources necessary to use Host + Adapter, and initializes the fields in the SCSI Host structure. The base, + io_port, n_io_ports, irq, and dma_channel fields in the SCSI Host structure + are intentionally left uninitialized, as this driver handles acquisition and + release of these resources explicitly, as well as ensuring exclusive access + to the Host Adapter hardware and data structures through explicit locking. +*/ + +static boolean BusLogic_AcquireResources(BusLogic_HostAdapter_T *HostAdapter, + SCSI_Host_T *Host) +{ + /* + Acquire exclusive or shared access to the IRQ Channel. A usage count is + maintained so that PCI, EISA, or MCA shared Interrupts can be supported. + */ + if (BusLogic_IRQ_UsageCount[HostAdapter->IRQ_Channel - 9]++ == 0) + { + if (request_irq(HostAdapter->IRQ_Channel, BusLogic_InterruptHandler, + SA_INTERRUPT, HostAdapter->InterruptLabel) < 0) + { + BusLogic_IRQ_UsageCount[HostAdapter->IRQ_Channel - 9]--; + printk("scsi%d: UNABLE TO ACQUIRE IRQ CHANNEL %d - DETACHING\n", + HostAdapter->HostNumber, HostAdapter->IRQ_Channel); + return false; + } + } + else + { + BusLogic_HostAdapter_T *FirstHostAdapter = + BusLogic_RegisteredHostAdapters; + while (FirstHostAdapter != NULL) + { + if (FirstHostAdapter->IRQ_Channel == HostAdapter->IRQ_Channel) + { + if (strlen(FirstHostAdapter->InterruptLabel) + 8 + < sizeof(FirstHostAdapter->InterruptLabel)) + { + strcat(FirstHostAdapter->InterruptLabel, " + "); + strcat(FirstHostAdapter->InterruptLabel, + HostAdapter->ModelName); + } + break; + } + FirstHostAdapter = FirstHostAdapter->Next; + } + } + HostAdapter->IRQ_ChannelAcquired = true; + /* + Acquire exclusive access to the DMA Channel. + */ + if (HostAdapter->DMA_Channel > 0) + { + if (request_dma(HostAdapter->DMA_Channel, HostAdapter->BoardName) < 0) + { + printk("scsi%d: UNABLE TO ACQUIRE DMA CHANNEL %d - DETACHING\n", + HostAdapter->HostNumber, HostAdapter->DMA_Channel); + return false; + } + set_dma_mode(HostAdapter->DMA_Channel, DMA_MODE_CASCADE); + enable_dma(HostAdapter->DMA_Channel); + HostAdapter->DMA_ChannelAcquired = true; + } + /* + Initialize necessary fields in the SCSI Host structure. + */ + Host->max_id = HostAdapter->MaxTargetIDs; + Host->max_lun = HostAdapter->MaxLogicalUnits; + Host->max_channel = 0; + Host->this_id = HostAdapter->SCSI_ID; + Host->can_queue = BusLogic_MailboxCount; + Host->cmd_per_lun = HostAdapter->Concurrency; + Host->sg_tablesize = HostAdapter->DriverScatterGatherLimit; + Host->unchecked_isa_dma = (HostAdapter->BusType == BusLogic_ISA_Bus); + /* + BusLogic 445S Host Adapters prior to board revision D have a hardware bug + whereby when the BIOS is enabled, transfers to/from the same address range + the BIOS occupies modulo 16MB are handled incorrectly. Since 16KB out of + each 16MB after the first is such a small amount of memory, this memory + can be marked as reserved without a significant loss of performance; this + is a much cheaper solution than requiring that ISA bounce buffers be used. + */ + if (HostAdapter->BIOS_Address > 0 && + strcmp(HostAdapter->ModelName, "445S") == 0) + { + Host->forbidden_addr = HostAdapter->BIOS_Address; + Host->forbidden_size = 16*1024; + } + /* + Indicate the System Resource Acquisition completed successfully, + */ + return true; +} + + +/* + BusLogic_ReleaseResources releases any system resources previously acquired + by BusLogic_AcquireResources. +*/ + +static void BusLogic_ReleaseResources(BusLogic_HostAdapter_T *HostAdapter) +{ + /* + Release exclusive or shared access to the IRQ Channel. + */ + if (HostAdapter->IRQ_ChannelAcquired) + if (--BusLogic_IRQ_UsageCount[HostAdapter->IRQ_Channel - 9] == 0) + free_irq(HostAdapter->IRQ_Channel); + /* + Release exclusive access to the DMA Channel. + */ + if (HostAdapter->DMA_ChannelAcquired) + free_dma(HostAdapter->DMA_Channel); +} + + +/* + BusLogic_TestInterrupts tests for proper functioning of the Host Adapter + Interrupt Register and that interrupts generated by the Host Adapter are + getting through to the Interrupt Handler. A large proportion of initial + problems with installing PCI Host Adapters are due to configuration problems + where either the Host Adapter or Motherboard is configured incorrectly, and + interrupts do not get through as a result. +*/ + +static boolean BusLogic_TestInterrupts(BusLogic_HostAdapter_T *HostAdapter) +{ + unsigned int InitialInterruptCount, FinalInterruptCount; + int TestCount = 5, i; + InitialInterruptCount = kstat.interrupts[HostAdapter->IRQ_Channel]; + /* + Issue the Test Command Complete Interrupt commands. + */ + for (i = 0; i < TestCount; i++) + BusLogic_Command(HostAdapter, BusLogic_TestCommandCompleteInterrupt, + NULL, 0, NULL, 0); + /* + Verify that BusLogic_InterruptHandler was called at least TestCount times. + Shared IRQ Channels could cause more than TestCount interrupts to occur, + but there should never be fewer than TestCount. + */ + FinalInterruptCount = kstat.interrupts[HostAdapter->IRQ_Channel]; + if (FinalInterruptCount < InitialInterruptCount + TestCount) + { + printk("scsi%d: HOST ADAPTER INTERRUPT TEST FAILED - DETACHING\n", + HostAdapter->HostNumber); + printk("scsi%d: Interrupts are not getting through " + "from the Host Adapter to the\n", HostAdapter->HostNumber); + printk("scsi%d: BusLogic Driver Interrupt Handler. " + "The most likely cause is that\n", HostAdapter->HostNumber); + printk("scsi%d: either the Host Adapter or Motherboard " + "is configured incorrectly.\n", HostAdapter->HostNumber); + printk("scsi%d: Please check the Host Adapter configuration " + "with AutoSCSI or by\n", HostAdapter->HostNumber); + printk("scsi%d: examining any dip switch and jumper settings " + "on the Host Adapter, and\n", HostAdapter->HostNumber); + printk("scsi%d: verify that no other device is attempting to " + "use the same IRQ Channel.\n", HostAdapter->HostNumber); + printk("scsi%d: For PCI Host Adapters, it may also be necessary " + "to investigate and\n", HostAdapter->HostNumber); + printk("scsi%d: manually set the PCI interrupt assignments " + "and edge/level interrupt\n", HostAdapter->HostNumber); + printk("scsi%d: type selection in the BIOS Setup Program or " + "with Motherboard jumpers.\n", HostAdapter->HostNumber); + return false; + } + /* + Indicate the Host Adapter Interrupt Test completed successfully. + */ + return true; +} + + +/* + BusLogic_InitializeHostAdapter initializes Host Adapter. This is the only + function called during SCSI Host Adapter detection which modifies the state + of the Host Adapter from its initial power on or hard reset state. +*/ + +static boolean BusLogic_InitializeHostAdapter(BusLogic_HostAdapter_T + *HostAdapter) +{ + BusLogic_ExtendedMailboxRequest_T ExtendedMailboxRequest; + BusLogic_RoundRobinModeRequest_T RoundRobinModeRequest; + BusLogic_WideModeCCBRequest_T WideModeCCBRequest; + BusLogic_ModifyIOAddressRequest_T ModifyIOAddressRequest; + /* + Initialize Read/Write Operation Count and Command Successful Flag + for each Target. + */ + memset(HostAdapter->ReadWriteOperationCount, 0, + sizeof(HostAdapter->ReadWriteOperationCount)); + memset(HostAdapter->CommandSuccessfulFlag, false, + sizeof(HostAdapter->CommandSuccessfulFlag)); + /* + Initialize the Outgoing and Incoming Mailbox structures. + */ + memset(HostAdapter->OutgoingMailboxes, 0, + sizeof(HostAdapter->OutgoingMailboxes)); + memset(HostAdapter->IncomingMailboxes, 0, + sizeof(HostAdapter->IncomingMailboxes)); + /* + Initialize the pointers to the First, Last, and Next Mailboxes. + */ + HostAdapter->FirstOutgoingMailbox = &HostAdapter->OutgoingMailboxes[0]; + HostAdapter->LastOutgoingMailbox = + &HostAdapter->OutgoingMailboxes[BusLogic_MailboxCount-1]; + HostAdapter->NextOutgoingMailbox = HostAdapter->FirstOutgoingMailbox; + HostAdapter->FirstIncomingMailbox = &HostAdapter->IncomingMailboxes[0]; + HostAdapter->LastIncomingMailbox = + &HostAdapter->IncomingMailboxes[BusLogic_MailboxCount-1]; + HostAdapter->NextIncomingMailbox = HostAdapter->FirstIncomingMailbox; + /* + Initialize the Host Adapter's Pointer to the Outgoing/Incoming Mailboxes. + */ + ExtendedMailboxRequest.MailboxCount = BusLogic_MailboxCount; + ExtendedMailboxRequest.BaseMailboxAddress = HostAdapter->OutgoingMailboxes; + if (BusLogic_Command(HostAdapter, BusLogic_InitializeExtendedMailbox, + &ExtendedMailboxRequest, + sizeof(ExtendedMailboxRequest), NULL, 0) < 0) + { + printk("scsi%d: MAILBOX INITIALIZATION FAILED - DETACHING\n", + HostAdapter->HostNumber); + return false; + } + /* + Enable Strict Round Robin Mode if supported by the Host Adapter. In Strict + Round Robin Mode, the Host Adapter only looks at the next Outgoing Mailbox + for each new command, rather than scanning through all the Outgoing + Mailboxes to find any that have new commands in them. BusLogic indicates + that Strict Round Robin Mode is significantly more efficient. + */ + RoundRobinModeRequest = BusLogic_StrictRoundRobinMode; + BusLogic_Command(HostAdapter, BusLogic_EnableStrictRoundRobinMode, + &RoundRobinModeRequest, + sizeof(RoundRobinModeRequest), NULL, 0); + /* + For Wide SCSI Host Adapters, issue the Enable Wide Mode CCB command to + allow more than 8 Logical Units per Target to be supported. + */ + if (HostAdapter->HostWideSCSI) + { + WideModeCCBRequest = BusLogic_WideModeCCB; + if (BusLogic_Command(HostAdapter, BusLogic_EnableWideModeCCB, + &WideModeCCBRequest, + sizeof(WideModeCCBRequest), NULL, 0) < 0) + { + printk("scsi%d: ENABLE WIDE MODE CCB FAILED - DETACHING\n", + HostAdapter->HostNumber); + return false; + } + } + /* + For PCI Host Adapters being accessed through the PCI compliant I/O + Address, disable the ISA compatible I/O Address to avoid detecting the + same Host Adapter at both I/O Addresses. + */ + if (HostAdapter->BusType == BusLogic_PCI_Bus) + { + int Index; + for (Index = 0; BusLogic_IO_StandardAddresses[Index] > 0; Index++) + if (HostAdapter->IO_Address == BusLogic_IO_StandardAddresses[Index]) + break; + if (BusLogic_IO_StandardAddresses[Index] == 0) + { + ModifyIOAddressRequest = BusLogic_ModifyIO_Disable; + if (BusLogic_Command(HostAdapter, BusLogic_ModifyIOAddress, + &ModifyIOAddressRequest, + sizeof(ModifyIOAddressRequest), NULL, 0) < 0) + { + printk("scsi%d: MODIFY I/O ADDRESS FAILED - DETACHING\n", + HostAdapter->HostNumber); + return false; + } + } + } + /* + Announce Successful Initialization. + */ + printk("scsi%d: *** %s Initialized Successfully ***\n", + HostAdapter->HostNumber, HostAdapter->BoardName); + /* + Indicate the Host Adapter Initialization completed successfully. + */ + return true; +} + + +/* + BusLogic_InquireTargetDevices inquires about the Target Devices accessible + through Host Adapter and reports on the results. +*/ + +static boolean BusLogic_InquireTargetDevices(BusLogic_HostAdapter_T + *HostAdapter) +{ + BusLogic_InstalledDevices8_T InstalledDevicesID0to7; + BusLogic_InstalledDevices8_T InstalledDevicesID8to15; + BusLogic_SetupInformation_T SetupInformation; + BusLogic_SynchronousPeriod_T SynchronousPeriod; + BusLogic_RequestedReplyLength_T RequestedReplyLength; + int TargetDevicesFound = 0, TargetID; + /* + Wait a few seconds between the Host Adapter Hard Reset which initiates + a SCSI Bus Reset and issuing any SCSI commands. Some SCSI devices get + confused if they receive SCSI commands too soon after a SCSI Bus Reset. + */ + BusLogic_Delay(HostAdapter->BusSettleTime); + /* + Issue the Inquire Installed Devices ID 0 to 7 command, and for Wide SCSI + Host Adapters the Inquire Installed Devices ID 8 to 15 command. This is + necessary to force Synchronous Transfer Negotiation so that the Inquire + Setup Information and Inquire Synchronous Period commands will return + valid data. + */ + if (BusLogic_Command(HostAdapter, BusLogic_InquireInstalledDevicesID0to7, + NULL, 0, &InstalledDevicesID0to7, + sizeof(InstalledDevicesID0to7)) + != sizeof(InstalledDevicesID0to7)) + return BusLogic_Failure(HostAdapter, "INQUIRE INSTALLED DEVICES ID 0 TO 7"); + if (HostAdapter->HostWideSCSI) + if (BusLogic_Command(HostAdapter, BusLogic_InquireInstalledDevicesID8to15, + NULL, 0, &InstalledDevicesID8to15, + sizeof(InstalledDevicesID8to15)) + != sizeof(InstalledDevicesID8to15)) + return BusLogic_Failure(HostAdapter, + "INQUIRE INSTALLED DEVICES ID 8 TO 15"); + /* + Issue the Inquire Setup Information command. + */ + RequestedReplyLength = sizeof(SetupInformation); + if (BusLogic_Command(HostAdapter, BusLogic_InquireSetupInformation, + &RequestedReplyLength, sizeof(RequestedReplyLength), + &SetupInformation, sizeof(SetupInformation)) + != sizeof(SetupInformation)) + return BusLogic_Failure(HostAdapter, "INQUIRE SETUP INFORMATION"); + /* + Issue the Inquire Synchronous Period command. + */ + RequestedReplyLength = sizeof(SynchronousPeriod); + if (BusLogic_Command(HostAdapter, BusLogic_InquireSynchronousPeriod, + &RequestedReplyLength, 1, + &SynchronousPeriod, sizeof(SynchronousPeriod)) + != sizeof(SynchronousPeriod)) + return BusLogic_Failure(HostAdapter, "INQUIRE SYNCHRONOUS PERIOD"); + /* + Save the Installed Devices, Synchronous Values, and Synchronous Period + information in the Host Adapter structure. + */ + memcpy(HostAdapter->InstalledDevices, InstalledDevicesID0to7, + sizeof(BusLogic_InstalledDevices8_T)); + memcpy(HostAdapter->SynchronousValues, + SetupInformation.SynchronousValuesID0to7, + sizeof(BusLogic_SynchronousValues8_T)); + if (HostAdapter->HostWideSCSI) + { + memcpy(&HostAdapter->InstalledDevices[8], InstalledDevicesID8to15, + sizeof(BusLogic_InstalledDevices8_T)); + memcpy(&HostAdapter->SynchronousValues[8], + SetupInformation.SynchronousValuesID8to15, + sizeof(BusLogic_SynchronousValues8_T)); + } + memcpy(HostAdapter->SynchronousPeriod, SynchronousPeriod, + sizeof(BusLogic_SynchronousPeriod_T)); + for (TargetID = 0; TargetID < HostAdapter->MaxTargetIDs; TargetID++) + if (HostAdapter->InstalledDevices[TargetID] != 0) + { + int SynchronousPeriod = HostAdapter->SynchronousPeriod[TargetID]; + if (SynchronousPeriod > 10) + { + int SynchronousTransferRate = 100000000 / SynchronousPeriod; + int RoundedSynchronousTransferRate = + (SynchronousTransferRate + 5000) / 10000; + printk("scsi%d: Target %d: Synchronous at " + "%d.%02d mega-transfers/sec, offset %d\n", + HostAdapter->HostNumber, TargetID, + RoundedSynchronousTransferRate / 100, + RoundedSynchronousTransferRate % 100, + HostAdapter->SynchronousValues[TargetID].Offset); + } + else if (SynchronousPeriod > 0) + { + int SynchronousTransferRate = 100000000 / SynchronousPeriod; + int RoundedSynchronousTransferRate = + (SynchronousTransferRate + 50000) / 100000; + printk("scsi%d: Target %d: Synchronous at " + "%d.%01d mega-transfers/sec, offset %d\n", + HostAdapter->HostNumber, TargetID, + RoundedSynchronousTransferRate / 10, + RoundedSynchronousTransferRate % 10, + HostAdapter->SynchronousValues[TargetID].Offset); + } + else printk("scsi%d: Target %d: Asynchronous\n", + HostAdapter->HostNumber, TargetID); + TargetDevicesFound++; + } + if (TargetDevicesFound == 0) + printk("scsi%d: No Target Devices Found\n", HostAdapter->HostNumber); + /* + Indicate the Target Device Inquiry completed successfully. + */ + return true; +} + + +/* + BusLogic_DetectHostAdapter probes for BusLogic Host Adapters at the standard + I/O Addresses where they may be located, initializing, registering, and + reporting the configuration of each BusLogic Host Adapter it finds. It + returns the number of BusLogic Host Adapters successfully initialized and + registered. +*/ + +int BusLogic_DetectHostAdapter(SCSI_Host_Template_T *HostTemplate) +{ + int BusLogicHostAdapterCount = 0, CommandLineEntryIndex = 0; + int AddressProbeIndex = 0; + BusLogic_InitializeAddressProbeList(); + while (BusLogic_IO_AddressProbeList[AddressProbeIndex] > 0) + { + BusLogic_HostAdapter_T HostAdapterPrototype; + BusLogic_HostAdapter_T *HostAdapter = &HostAdapterPrototype; + SCSI_Host_T *Host; + memset(HostAdapter, 0, sizeof(BusLogic_HostAdapter_T)); + HostAdapter->IO_Address = + BusLogic_IO_AddressProbeList[AddressProbeIndex++]; + /* + Initialize the Command Line Entry field if an explicit I/O Address + was specified. + */ + if (CommandLineEntryIndex < BusLogic_CommandLineEntryCount && + BusLogic_CommandLineEntries[CommandLineEntryIndex].IO_Address == + HostAdapter->IO_Address) + HostAdapter->CommandLineEntry = + &BusLogic_CommandLineEntries[CommandLineEntryIndex++]; + /* + Check whether the I/O Address range is already in use. + */ + if (check_region(HostAdapter->IO_Address, BusLogic_IO_PortCount) < 0) + continue; + /* + Probe the Host Adapter. If unsuccessful, abort further initialization. + */ + if (!BusLogic_ProbeHostAdapter(HostAdapter)) continue; + /* + Hard Reset the Host Adapter. If unsuccessful, abort further + initialization. + */ + if (!BusLogic_HardResetHostAdapter(HostAdapter)) continue; + /* + Check the Host Adapter. If unsuccessful, abort further initialization. + */ + if (!BusLogic_CheckHostAdapter(HostAdapter)) continue; + /* + Initialize the Command Line Entry field if an explicit I/O Address + was not specified. + */ + if (CommandLineEntryIndex < BusLogic_CommandLineEntryCount && + BusLogic_CommandLineEntries[CommandLineEntryIndex].IO_Address == 0) + HostAdapter->CommandLineEntry = + &BusLogic_CommandLineEntries[CommandLineEntryIndex++]; + /* + Announce the Driver Version and Date, Author's Name, Copyright Notice, + and Contact Address. + */ + BusLogic_AnnounceDriver(); + /* + Register usage of the I/O Address range. From this point onward, any + failure will be assumed to be due to a problem with the Host Adapter, + rather than due to having mistakenly identified this port as belonging + to a BusLogic Host Adapter. The I/O Address range will not be + released, thereby preventing it from being incorrectly identified as + any other type of Host Adapter. + */ + request_region(HostAdapter->IO_Address, BusLogic_IO_PortCount, + "BusLogic"); + /* + Register the SCSI Host structure. + */ + HostTemplate->proc_dir = &BusLogic_ProcDirectoryEntry; + Host = scsi_register(HostTemplate, sizeof(BusLogic_HostAdapter_T)); + HostAdapter = (BusLogic_HostAdapter_T *) Host->hostdata; + memcpy(HostAdapter, &HostAdapterPrototype, + sizeof(BusLogic_HostAdapter_T)); + HostAdapter->SCSI_Host = Host; + HostAdapter->HostNumber = Host->host_no; + /* + Add Host Adapter to the end of the list of registered BusLogic + Host Adapters. In order for Command Complete Interrupts to be + properly dismissed by BusLogic_InterruptHandler, the Host Adapter + must be registered. This must be done before the IRQ Channel is + acquired, and in a shared IRQ Channel environment, must be done + before any Command Complete Interrupts occur, since the IRQ Channel + may have already been acquired by a previous BusLogic Host Adapter. + */ + BusLogic_RegisterHostAdapter(HostAdapter); + /* + Read the Host Adapter Configuration, Acquire the System Resources + necessary to use Host Adapter and initialize the fields in the SCSI + Host structure, then Test Interrupts, Create the CCBs, and finally + Initialize the Host Adapter. + */ + if (BusLogic_ReadHostAdapterConfiguration(HostAdapter) && + BusLogic_AcquireResources(HostAdapter, Host) && + BusLogic_TestInterrupts(HostAdapter) && + BusLogic_CreateCCBs(HostAdapter) && + BusLogic_InitializeHostAdapter(HostAdapter) && + BusLogic_InquireTargetDevices(HostAdapter)) + { + /* + Initialization has been completed successfully. Release and + re-register usage of the I/O Address range so that the Model + Name of the Host Adapter will appear. + */ + release_region(HostAdapter->IO_Address, BusLogic_IO_PortCount); + request_region(HostAdapter->IO_Address, BusLogic_IO_PortCount, + HostAdapter->BoardName); + BusLogicHostAdapterCount++; + } + else + { + /* + An error occurred during Host Adapter Configuration Querying, + Resource Acquisition, Interrupt Testing, CCB Creation, or Host + Adapter Initialization, so remove Host Adapter from the list of + registered BusLogic Host Adapters, destroy the CCBs, Release + the System Resources, and Unregister the SCSI Host. + */ + BusLogic_DestroyCCBs(HostAdapter); + BusLogic_ReleaseResources(HostAdapter); + BusLogic_UnregisterHostAdapter(HostAdapter); + scsi_unregister(Host); + } + } + return BusLogicHostAdapterCount; +} + + +/* + BusLogic_ReleaseHostAdapter releases all resources previously acquired to + support a specific Host Adapter, including the I/O Address range, and + unregisters the BusLogic Host Adapter. +*/ + +int BusLogic_ReleaseHostAdapter(SCSI_Host_T *Host) +{ + BusLogic_HostAdapter_T *HostAdapter = + (BusLogic_HostAdapter_T *) Host->hostdata; + /* + Destroy the CCBs and release any system resources acquired to use + Host Adapter. + */ + BusLogic_DestroyCCBs(HostAdapter); + BusLogic_ReleaseResources(HostAdapter); + /* + Release usage of the I/O Address range. + */ + release_region(HostAdapter->IO_Address, BusLogic_IO_PortCount); + /* + Remove Host Adapter from the list of registered BusLogic Host Adapters. + */ + BusLogic_UnregisterHostAdapter(HostAdapter); + return 0; +} + + +/* + BusLogic_ComputeResultCode computes a SCSI Subsystem Result Code from + the Host Adapter Status and Target Device Status. +*/ + +static int BusLogic_ComputeResultCode(BusLogic_HostAdapterStatus_T + HostAdapterStatus, + BusLogic_TargetDeviceStatus_T + TargetDeviceStatus) +{ + int HostStatus; + switch (HostAdapterStatus) + { + case BusLogic_CommandCompletedNormally: + case BusLogic_LinkedCommandCompleted: + case BusLogic_LinkedCommandCompletedWithFlag: + HostStatus = DID_OK; + break; + case BusLogic_SCSISelectionTimeout: + HostStatus = DID_TIME_OUT; + break; + case BusLogic_InvalidOutgoingMailboxActionCode: + case BusLogic_InvalidCommandOperationCode: + case BusLogic_InvalidCommandParameter: + printk("BusLogic: BusLogic Driver Protocol Error 0x%02X\n", + HostAdapterStatus); + case BusLogic_DataOverUnderRun: + case BusLogic_UnexpectedBusFree: + case BusLogic_LinkedCCBhasInvalidLUN: + case BusLogic_AutoRequestSenseFailed: + case BusLogic_TaggedQueuingMessageRejected: + case BusLogic_UnsupportedMessageReceived: + case BusLogic_HostAdapterHardwareFailed: + case BusLogic_TargetDeviceReconnectedImproperly: + case BusLogic_AbortQueueGenerated: + case BusLogic_HostAdapterSoftwareError: + case BusLogic_HostAdapterHardwareTimeoutError: + case BusLogic_SCSIParityErrorDetected: + HostStatus = DID_ERROR; + break; + case BusLogic_InvalidBusPhaseRequested: + case BusLogic_TargetFailedResponseToATN: + case BusLogic_HostAdapterAssertedRST: + case BusLogic_OtherDeviceAssertedRST: + case BusLogic_HostAdapterAssertedBusDeviceReset: + HostStatus = DID_RESET; + break; + default: + printk("BusLogic: unknown Host Adapter Status 0x%02X\n", + HostAdapterStatus); + HostStatus = DID_ERROR; + break; + } + return (HostStatus << 16) | TargetDeviceStatus; +} + + +/* + BusLogic_InterruptHandler handles hardware interrupts from BusLogic Host + Adapters. To simplify handling shared IRQ Channels, all installed BusLogic + Host Adapters are scanned whenever any one of them signals a hardware + interrupt. +*/ + +static void BusLogic_InterruptHandler(int IRQ_Channel, + Registers_T *InterruptRegisters) +{ + BusLogic_CCB_T *FirstCompletedCCB = NULL, *LastCompletedCCB = NULL; + BusLogic_HostAdapter_T *HostAdapter; + int HostAdapterResetPendingCount = 0; + /* + Iterate over the installed BusLogic Host Adapters accepting any Incoming + Mailbox entries and saving the completed CCBs for processing. This + interrupt handler is installed with SA_INTERRUPT, so interrupts are + disabled when the interrupt handler is entered. + */ + for (HostAdapter = BusLogic_RegisteredHostAdapters; + HostAdapter != NULL; + HostAdapter = HostAdapter->Next) + { + unsigned char InterruptRegister; + /* + Acquire exclusive access to Host Adapter. + */ + BusLogic_LockHostAdapterID(HostAdapter); + /* + Read the Host Adapter Interrupt Register. + */ + InterruptRegister = BusLogic_ReadInterruptRegister(HostAdapter); + if (InterruptRegister & BusLogic_InterruptValid) + { + /* + Acknowledge the interrupt and reset the Host Adapter + Interrupt Register. + */ + BusLogic_WriteControlRegister(HostAdapter, BusLogic_InterruptReset); + /* + Process valid SCSI Reset State and Incoming Mailbox Loaded + interrupts. Command Complete interrupts are noted, and + Outgoing Mailbox Available interrupts are ignored, as they + are never enabled. + */ + if (InterruptRegister & BusLogic_SCSIResetState) + { + HostAdapter->HostAdapterResetPending = true; + HostAdapterResetPendingCount++; + } + else if (InterruptRegister & BusLogic_IncomingMailboxLoaded) + { + /* + Scan through the Incoming Mailboxes in Strict Round Robin + fashion, saving any completed CCBs for further processing. + It is essential that for each CCB and SCSI Command issued, + completion processing is performed exactly once. Therefore, + only Incoming Mailbox entries with completion code Command + Completed Without Error, Command Completed With Error, or + Command Aborted At Host Request are saved for completion + processing. When an Incoming Mailbox entry has a completion + code of Aborted Command Not Found, the CCB had already + completed or been aborted before the current Abort request + was processed, and so completion processing has already + occurred and no further action should be taken. + */ + BusLogic_IncomingMailbox_T *NextIncomingMailbox = + HostAdapter->NextIncomingMailbox; + while (NextIncomingMailbox->CompletionCode != + BusLogic_IncomingMailboxFree) + { + BusLogic_CCB_T *CCB = NextIncomingMailbox->CCB; + BusLogic_CompletionCode_T MailboxCompletionCode = + NextIncomingMailbox->CompletionCode; + if (MailboxCompletionCode != BusLogic_AbortedCommandNotFound) + { + /* + Mark this CCB as completed and add it to the end + of the list of completed CCBs. + */ + CCB->Status = BusLogic_CCB_Completed; + CCB->MailboxCompletionCode = MailboxCompletionCode; + CCB->Next = NULL; + if (FirstCompletedCCB == NULL) + { + FirstCompletedCCB = CCB; + LastCompletedCCB = CCB; + } + else + { + LastCompletedCCB->Next = CCB; + LastCompletedCCB = CCB; + } + } + else printk("scsi%d: Aborted CCB #%d Not Found\n", + HostAdapter->HostNumber, CCB->SerialNumber); + NextIncomingMailbox->CompletionCode = + BusLogic_IncomingMailboxFree; + if (++NextIncomingMailbox > HostAdapter->LastIncomingMailbox) + NextIncomingMailbox = HostAdapter->FirstIncomingMailbox; + } + HostAdapter->NextIncomingMailbox = NextIncomingMailbox; + } + else if (InterruptRegister & BusLogic_CommandComplete) + HostAdapter->HostAdapterCommandCompleted = true; + } + /* + Release exclusive access to Host Adapter. + */ + BusLogic_UnlockHostAdapterID(HostAdapter); + } + /* + Enable interrupts while the completed CCBs are processed. + */ + sti(); + /* + Iterate over the Host Adapters performing any pending Host Adapter Resets. + */ + if (HostAdapterResetPendingCount > 0) + for (HostAdapter = BusLogic_RegisteredHostAdapters; + HostAdapter != NULL; + HostAdapter = HostAdapter->Next) + if (HostAdapter->HostAdapterResetPending) + { + BusLogic_ResetHostAdapter(HostAdapter, NULL); + HostAdapter->HostAdapterResetPending = false; + scsi_mark_host_bus_reset(HostAdapter->SCSI_Host); + } + /* + Iterate over the completed CCBs setting the SCSI Command Result Codes, + deallocating the CCBs, and calling the Completion Routines. + */ + while (FirstCompletedCCB != NULL) + { + BusLogic_CCB_T *CCB = FirstCompletedCCB; + SCSI_Command_T *Command = CCB->Command; + FirstCompletedCCB = FirstCompletedCCB->Next; + HostAdapter = CCB->HostAdapter; + /* + Bus Device Reset CCBs have the Command field non-NULL only when a Bus + Device Reset was requested for a command that was not currently active + in the Host Adapter, and hence would not have its Completion Routine + called otherwise. + */ + if (CCB->Opcode == BusLogic_SCSIBusDeviceReset) + { + printk("scsi%d: Bus Device Reset CCB #%d to Target %d Completed\n", + HostAdapter->HostNumber, CCB->SerialNumber, CCB->TargetID); + if (Command != NULL) Command->result = DID_RESET << 16; + } + else + /* + Translate the Mailbox Completion Code, Host Adapter Status, and + Target Device Status into a SCSI Subsystem Result Code. + */ + switch (CCB->MailboxCompletionCode) + { + case BusLogic_IncomingMailboxFree: + case BusLogic_AbortedCommandNotFound: + printk("scsi%d: CCB #%d Impossible State\n", + HostAdapter->HostNumber, CCB->SerialNumber); + break; + case BusLogic_CommandCompletedWithoutError: + HostAdapter->CommandSuccessfulFlag[CCB->TargetID] = true; + Command->result = DID_OK << 16; + break; + case BusLogic_CommandAbortedAtHostRequest: + printk("scsi%d: CCB #%d Aborted\n", + HostAdapter->HostNumber, CCB->SerialNumber); + Command->result = DID_ABORT << 16; + break; + case BusLogic_CommandCompletedWithError: + Command->result = + BusLogic_ComputeResultCode(CCB->HostAdapterStatus, + CCB->TargetDeviceStatus); + if (BusLogic_TracingOptions & BusLogic_TraceErrors) + if (CCB->HostAdapterStatus != BusLogic_SCSISelectionTimeout) + { + int i; + printk("scsi%d: CCB #%d Target %d: Result %X " + "Host Adapter Status %02X Target Status %02X\n", + HostAdapter->HostNumber, CCB->SerialNumber, + CCB->TargetID, Command->result, + CCB->HostAdapterStatus, CCB->TargetDeviceStatus); + printk("scsi%d: CDB ", HostAdapter->HostNumber); + for (i = 0; i < CCB->CDB_Length; i++) + printk(" %02X", CCB->CDB[i]); + printk("\n"); + printk("scsi%d: Sense ", HostAdapter->HostNumber); + for (i = 0; i < CCB->SenseDataLength; i++) + printk(" %02X", (*CCB->SenseDataPointer)[i]); + printk("\n"); + } + break; + } + /* + Place CCB back on the Host Adapter's free list. + */ + BusLogic_DeallocateCCB(CCB); + /* + Call the SCSI Command Completion Routine if appropriate. + */ + if (Command != NULL) Command->scsi_done(Command); + } +} + + +/* + BusLogic_WriteOutgoingMailboxEntry writes an Outgoing Mailbox entry + for Host Adapter with Action Code and CCB. +*/ + +static boolean BusLogic_WriteOutgoingMailboxEntry(BusLogic_HostAdapter_T + *HostAdapter, + BusLogic_ActionCode_T + ActionCode, + BusLogic_CCB_T *CCB) +{ + BusLogic_OutgoingMailbox_T *NextOutgoingMailbox; + boolean Result = false; + BusLogic_LockHostAdapter(HostAdapter); + NextOutgoingMailbox = HostAdapter->NextOutgoingMailbox; + if (NextOutgoingMailbox->ActionCode == BusLogic_OutgoingMailboxFree) + { + NextOutgoingMailbox->ActionCode = ActionCode; + NextOutgoingMailbox->CCB = CCB; + CCB->Status = BusLogic_CCB_Active; + BusLogic_StartMailboxScan(HostAdapter); + if (++NextOutgoingMailbox > HostAdapter->LastOutgoingMailbox) + NextOutgoingMailbox = HostAdapter->FirstOutgoingMailbox; + HostAdapter->NextOutgoingMailbox = NextOutgoingMailbox; + Result = true; + } + BusLogic_UnlockHostAdapter(HostAdapter); + return Result; +} + + +/* + BusLogic_QueueCommand creates a CCB for Command and places it into an + Outgoing Mailbox for execution by the associated Host Adapter. +*/ + +int BusLogic_QueueCommand(SCSI_Command_T *Command, + void (*CompletionRoutine)(SCSI_Command_T *)) +{ + BusLogic_HostAdapter_T *HostAdapter = + (BusLogic_HostAdapter_T *) Command->host->hostdata; + unsigned char *CDB = Command->cmnd; + unsigned char CDB_Length = Command->cmd_len; + unsigned char TargetID = Command->target; + unsigned char LogicalUnit = Command->lun; + void *BufferPointer = Command->request_buffer; + int BufferLength = Command->request_bufflen; + int SegmentCount = Command->use_sg; + BusLogic_CCB_T *CCB; + long EnableTQ; + /* + SCSI REQUEST_SENSE commands will be executed automatically by the Host + Adapter for any errors, so they should not be executed explicitly unless + the Sense Data is zero indicating that no error occurred. + */ + if (CDB[0] == REQUEST_SENSE && Command->sense_buffer[0] != 0) + { + Command->result = DID_OK << 16; + CompletionRoutine(Command); + return 0; + } + /* + Allocate a CCB from the Host Adapter's free list, aborting the command + with an error if there are none available and memory allocation fails. + */ + CCB = BusLogic_AllocateCCB(HostAdapter); + if (CCB == NULL) + { + Command->result = DID_ERROR << 16; + CompletionRoutine(Command); + return 0; + } + /* + Initialize the fields in the BusLogic Command Control Block (CCB). + */ + if (SegmentCount == 0) + { + CCB->Opcode = BusLogic_InitiatorCCB; + CCB->DataLength = BufferLength; + CCB->DataPointer = BufferPointer; + } + else + { + SCSI_ScatterList_T *ScatterList = (SCSI_ScatterList_T *) BufferPointer; + int Segment; + CCB->Opcode = BusLogic_InitiatorCCB_ScatterGather; + CCB->DataLength = SegmentCount * sizeof(BusLogic_ScatterGatherSegment_T); + CCB->DataPointer = CCB->ScatterGatherList; + for (Segment = 0; Segment < SegmentCount; Segment++) + { + CCB->ScatterGatherList[Segment].SegmentByteCount = + ScatterList[Segment].length; + CCB->ScatterGatherList[Segment].SegmentDataPointer = + ScatterList[Segment].address; + } + } + switch (CDB[0]) + { + case READ_6: + case READ_10: + CCB->DataDirection = BusLogic_DataInLengthChecked; + HostAdapter->ReadWriteOperationCount[TargetID]++; + break; + case WRITE_6: + case WRITE_10: + CCB->DataDirection = BusLogic_DataOutLengthChecked; + HostAdapter->ReadWriteOperationCount[TargetID]++; + break; + default: + CCB->DataDirection = BusLogic_UncheckedDataTransfer; + break; + } + CCB->CDB_Length = CDB_Length; + CCB->SenseDataLength = sizeof(Command->sense_buffer); + CCB->TargetID = TargetID; + CCB->LogicalUnit = LogicalUnit; + /* + For Wide SCSI Host Adapters, Wide Mode CCBs are used to support more than + 8 Logical Units per Target, and this requires setting the overloaded + TagEnable field to Logical Unit bit 5. + */ + if (HostAdapter->HostWideSCSI) + { + CCB->TagEnable = LogicalUnit >> 5; + CCB->WideModeTagEnable = false; + } + else CCB->TagEnable = false; + /* + BusLogic recommends that after a Reset the first couple of commands that + are sent to a Target be sent in a non Tagged Queue fashion so that the Host + Adapter and Target can establish Synchronous Transfer before Queue Tag + messages can interfere with the Synchronous Negotiation message. + */ + if ((HostAdapter->TaggedQueuingPermitted & (1 << TargetID)) && + Command->device->tagged_supported && + (EnableTQ = HostAdapter->ReadWriteOperationCount[TargetID] - 5) >= 0) + { + if (EnableTQ == 0) + printk("scsi%d: Tagged Queuing now active for Target %d\n", + HostAdapter->HostNumber, TargetID); + if (HostAdapter->HostWideSCSI) + { + CCB->WideModeTagEnable = true; + CCB->WideModeQueueTag = BusLogic_SimpleQueueTag; + } + else + { + CCB->TagEnable = true; + CCB->QueueTag = BusLogic_SimpleQueueTag; + } + } + memcpy(CCB->CDB, CDB, CDB_Length); + CCB->SenseDataPointer = (SCSI_SenseData_T *) &Command->sense_buffer; + CCB->Command = Command; + Command->scsi_done = CompletionRoutine; + /* + Place the CCB in an Outgoing Mailbox, aborting the command with an + error if there are none available. + */ + if (!(BusLogic_WriteOutgoingMailboxEntry( + HostAdapter, BusLogic_MailboxStartCommand, CCB))) + { + printk("scsi%d: cannot write Outgoing Mailbox Entry\n", + HostAdapter->HostNumber); + BusLogic_DeallocateCCB(CCB); + Command->result = DID_ERROR << 16; + CompletionRoutine(Command); + } + return 0; +} + + +/* + BusLogic_AbortCommand aborts Command if possible. +*/ + +int BusLogic_AbortCommand(SCSI_Command_T *Command) +{ + BusLogic_HostAdapter_T *HostAdapter = + (BusLogic_HostAdapter_T *) Command->host->hostdata; + unsigned char InterruptRegister; + BusLogic_CCB_T *CCB; + int Result; + /* + If the Host Adapter has posted an interrupt but the Interrupt Handler + has not been called for some reason (i.e. the interrupt was lost), try + calling the Interrupt Handler directly to process the commands that + have been completed. + */ + InterruptRegister = BusLogic_ReadInterruptRegister(HostAdapter); + if (InterruptRegister & BusLogic_InterruptValid) + { + unsigned long ProcessorFlags; + printk("scsi%d: Recovering Lost Interrupt for IRQ Channel %d\n", + HostAdapter->HostNumber, HostAdapter->IRQ_Channel); + save_flags(ProcessorFlags); + cli(); + BusLogic_InterruptHandler(HostAdapter->IRQ_Channel, NULL); + restore_flags(ProcessorFlags); + return SCSI_ABORT_SNOOZE; + } + /* + Find the CCB to be aborted and determine how to proceed. + */ + BusLogic_LockHostAdapter(HostAdapter); + Result = SCSI_ABORT_NOT_RUNNING; + for (CCB = HostAdapter->All_CCBs; CCB != NULL; CCB = CCB->NextAll) + if (CCB->Command == Command) + { + if (CCB->Status == BusLogic_CCB_Active) + if ((HostAdapter->HostWideSCSI && CCB->WideModeTagEnable && + CCB->WideModeQueueTag != BusLogic_SimpleQueueTag) || + (!HostAdapter->HostWideSCSI && CCB->TagEnable && + CCB->QueueTag != BusLogic_SimpleQueueTag)) + { + /* + CCBs using Tagged Queuing with other than Simple Queue Tag + should not be aborted. + */ + Result = SCSI_ABORT_BUSY; + } + else + { + /* + Attempt to abort the CCB. + */ + if (BusLogic_WriteOutgoingMailboxEntry( + HostAdapter, BusLogic_MailboxAbortCommand, CCB)) + { + printk("scsi%d: Aborting CCB #%d\n", + HostAdapter->HostNumber, CCB->SerialNumber); + Result = SCSI_ABORT_PENDING; + } + else Result = SCSI_ABORT_BUSY; + } + break; + } + BusLogic_UnlockHostAdapter(HostAdapter); + return Result; +} + + +/* + BusLogic_ResetHostAdapter resets Host Adapter if possible, marking all + currently executing SCSI commands as having been reset, as well as + the specified Command if non-NULL. +*/ + +static int BusLogic_ResetHostAdapter(BusLogic_HostAdapter_T *HostAdapter, + SCSI_Command_T *Command) +{ + BusLogic_CCB_T *CCB; + if (Command == NULL) + printk("scsi%d: Resetting %s due to SCSI Reset State Interrupt\n", + HostAdapter->HostNumber, HostAdapter->BoardName); + else printk("scsi%d: Resetting %s due to Target %d\n", + HostAdapter->HostNumber, HostAdapter->BoardName, Command->target); + /* + Attempt to Reset and Reinitialize the Host Adapter. + */ + BusLogic_LockHostAdapter(HostAdapter); + if (!(BusLogic_HardResetHostAdapter(HostAdapter) && + BusLogic_InitializeHostAdapter(HostAdapter))) + { + printk("scsi%d: Resetting %s Failed\n", + HostAdapter->HostNumber, HostAdapter->BoardName); + BusLogic_UnlockHostAdapter(HostAdapter); + return SCSI_RESET_ERROR; + } + BusLogic_UnlockHostAdapter(HostAdapter); + /* + Wait a few seconds between the Host Adapter Hard Reset which initiates + a SCSI Bus Reset and issuing any SCSI commands. Some SCSI devices get + confused if they receive SCSI commands too soon after a SCSI Bus Reset. + */ + BusLogic_Delay(HostAdapter->BusSettleTime); + /* + Mark all currently executing CCBs as having been reset. + */ + for (CCB = HostAdapter->All_CCBs; CCB != NULL; CCB = CCB->NextAll) + if (CCB->Status == BusLogic_CCB_Active) + { + SCSI_Command_T *ActiveCommand = CCB->Command; + if (ActiveCommand == Command) Command = NULL; + BusLogic_DeallocateCCB(CCB); + if (ActiveCommand != NULL) + { + ActiveCommand->result = DID_RESET << 16; + ActiveCommand->scsi_done(ActiveCommand); + } + } + if (Command != NULL) + { + Command->result = DID_RESET << 16; + Command->scsi_done(Command); + } + return SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET; +} + + +/* + BusLogic_BusDeviceReset sends a Bus Device Reset to the Target + associated with Command. +*/ + +static int BusLogic_BusDeviceReset(BusLogic_HostAdapter_T *HostAdapter, + SCSI_Command_T *Command) +{ + BusLogic_CCB_T *CCB = BusLogic_AllocateCCB(HostAdapter), *XCCB; + unsigned char TargetID = Command->target; + /* + If sending a Bus Device Reset is impossible, attempt a full Host + Adapter Hard Reset and SCSI Bus Reset. + */ + if (CCB == NULL) + return BusLogic_ResetHostAdapter(HostAdapter, Command); + printk("scsi%d: Sending Bus Device Reset CCB #%d to Target %d\n", + HostAdapter->HostNumber, CCB->SerialNumber, TargetID); + CCB->Opcode = BusLogic_SCSIBusDeviceReset; + CCB->TargetID = TargetID; + CCB->Command = Command; + /* + If there is a currently executing CCB in the Host Adapter for this Command, + then an Incoming Mailbox entry will be made with a completion code of + BusLogic_HostAdapterAssertedBusDeviceReset. Otherwise, the CCB Command + field will be left pointing to the Command so that the interrupt for the + completion of the Bus Device Reset can call the Completion Routine for the + Command. + */ + BusLogic_LockHostAdapter(HostAdapter); + for (XCCB = HostAdapter->All_CCBs; XCCB != NULL; XCCB = XCCB->NextAll) + if (XCCB->Status == BusLogic_CCB_Active && XCCB->Command == Command) + { + CCB->Command = NULL; + break; + } + BusLogic_UnlockHostAdapter(HostAdapter); + /* + Attempt to write an Outgoing Mailbox with the Bus Device Reset CCB. + If sending a Bus Device Reset is impossible, attempt a full Host + Adapter Hard Reset and SCSI Bus Reset. + */ + if (!(BusLogic_WriteOutgoingMailboxEntry( + HostAdapter, BusLogic_MailboxStartCommand, CCB))) + { + printk("scsi%d: cannot write Outgoing Mailbox Entry for " + "Bus Device Reset\n", HostAdapter->HostNumber); + BusLogic_DeallocateCCB(CCB); + return BusLogic_ResetHostAdapter(HostAdapter, Command); + } + HostAdapter->ReadWriteOperationCount[TargetID] = 0; + return SCSI_RESET_PENDING; +} + + +/* + BusLogic_ResetCommand takes appropriate action to reset Command. +*/ + +int BusLogic_ResetCommand(SCSI_Command_T *Command) +{ + BusLogic_HostAdapter_T *HostAdapter = + (BusLogic_HostAdapter_T *) Command->host->hostdata; + unsigned char TargetID = Command->target; + unsigned char ErrorRecoveryOption = + HostAdapter->ErrorRecoveryOption[TargetID]; + if (ErrorRecoveryOption == BusLogic_ErrorRecoveryDefault) + if (Command->host->suggest_bus_reset) + ErrorRecoveryOption = BusLogic_ErrorRecoveryHardReset; + else ErrorRecoveryOption = BusLogic_ErrorRecoveryBusDeviceReset; + switch (ErrorRecoveryOption) + { + case BusLogic_ErrorRecoveryHardReset: + return BusLogic_ResetHostAdapter(HostAdapter, Command); + case BusLogic_ErrorRecoveryBusDeviceReset: + if (HostAdapter->CommandSuccessfulFlag[TargetID]) + { + HostAdapter->CommandSuccessfulFlag[TargetID] = false; + return BusLogic_BusDeviceReset(HostAdapter, Command); + } + else return BusLogic_ResetHostAdapter(HostAdapter, Command); + } + printk("scsi%d: Error Recovery Suppressed\n", HostAdapter->HostNumber); + return SCSI_RESET_PUNT; +} + + +/* + BusLogic_BIOSDiskParameters returns the Heads/Sectors/Cylinders BIOS Disk + Parameters for Disk. The default disk geometry is 64 heads, 32 sectors, and + the appropriate number of cylinders so as not to exceed drive capacity. In + order for disks equal to or larger than 1 GB to be addressable by the BIOS + without exceeding the BIOS limitation of 1024 cylinders, Extended Translation + may be enabled in AutoSCSI on "C" Series boards or by a dip switch setting + on older boards. With Extended Translation enabled, drives between 1 GB + inclusive and 2 GB exclusive are given a disk geometry of 128 heads and 32 + sectors, and drives between 2 GB inclusive and 8 GB exclusive are given a + disk geometry of 255 heads and 63 sectors. On "C" Series boards the firmware + can be queried for the precise translation in effect for each drive + individually, but there is really no need to do so since we know the total + capacity of the drive and whether Extended Translation is enabled, hence we + can deduce the BIOS disk geometry that must be in effect. +*/ + +int BusLogic_BIOSDiskParameters(SCSI_Disk_T *Disk, KernelDevice_T Device, + int *Parameters) +{ + BusLogic_HostAdapter_T *HostAdapter = + (BusLogic_HostAdapter_T *) Disk->device->host->hostdata; + BIOS_DiskParameters_T *DiskParameters = (BIOS_DiskParameters_T *) Parameters; + if (HostAdapter->ExtendedTranslation && + Disk->capacity >= 2*1024*1024 /* 1 GB in 512 byte sectors */) + if (Disk->capacity >= 4*1024*1024 /* 2 GB in 512 byte sectors */) + { + DiskParameters->Heads = 255; + DiskParameters->Sectors = 63; + } + else + { + DiskParameters->Heads = 128; + DiskParameters->Sectors = 32; + } + else + { + DiskParameters->Heads = 64; + DiskParameters->Sectors = 32; + } + DiskParameters->Cylinders = + Disk->capacity / (DiskParameters->Heads * DiskParameters->Sectors); + return 0; +} + + +/* + BusLogic_Setup handles processing of Kernel Command Line Arguments. + + For the BusLogic driver, a kernel command line entry comprises the driver + identifier "BusLogic=" optionally followed by a comma-separated sequence of + integers and then optionally followed by a comma-separated sequence of + strings. Each command line entry applies to one BusLogic Host Adapter. + Multiple command line entries may be used in systems which contain multiple + BusLogic Host Adapters. + + The first integer specified is the I/O Address at which the Host Adapter is + located. If unspecified, it defaults to 0 which means to apply this entry to + the first BusLogic Host Adapter found during the default probe sequence. If + any I/O Address parameters are provided on the command line, then the default + probe sequence is omitted. + + The second integer specified is the number of Concurrent Commands per Logical + Unit to allow for Target Devices on the Host Adapter. If unspecified, it + defaults to 0 which means to use the value of BusLogic_Concurrency for + non-ISA Host Adapters, or BusLogic_Concurrency_ISA for ISA Host Adapters. + + The third integer specified is the Bus Settle Time in seconds. This is + the amount of time to wait between a Host Adapter Hard Reset which initiates + a SCSI Bus Reset and issuing any SCSI commands. If unspecified, it defaults + to 0 which means to use the value of BusLogic_DefaultBusSettleTime. + + The fourth integer specified is the Tracing Options. If unspecified, it + defaults to 0 which means that no special tracing information is to be + printed. Note that Tracing Options are applied across all Host Adapters. + + The string options are used to provide control over Tagged Queuing and Error + Recovery. If both Tagged Queuing and Error Recovery strings are provided, the + Tagged Queuing specification string must come first. + + The Tagged Queuing specification begins with "TQ:" and allows for explicitly + specifying whether Tagged Queuing is permitted on Target Devices that support + it. The following specification options are available: + + TQ:Default Tagged Queuing will be permitted based on the firmware + version of the BusLogic Host Adapter and based on + whether the Concurrency value allows queuing multiple + commands. + + TQ:Enable Tagged Queuing will be enabled for all Target Devices + on this Host Adapter overriding any limitation that + would otherwise be imposed based on the Host Adapter + firmware version. + + TQ:Disable Tagged Queuing will be disabled for all Target Devices + on this Host Adapter. + + TQ: Tagged Queuing will be controlled individually for each + Target Device. is a sequence of "Y", + "N", and "X" characters. "Y" enabled Tagged Queuing, + "N" disables Tagged Queuing, and "X" accepts the + default based on the firmware version. The first + character refers to Target 0, the second to Target 1, + and so on; if the sequence of "Y", "N", and "X" + characters does not cover all the Target Devices, + unspecified characters are assumed to be "X". + + Note that explicitly requesting Tagged Queuing may lead to problems; this + facility is provided primarily to allow disabling Tagged Queuing on Target + Devices that do not implement it correctly. + + The Error Recovery specification begins with "ER:" and allows for explicitly + specifying the Error Recovery action to be performed when ResetCommand is + called due to a SCSI Command failing to complete successfully. The following + specification options are available: + + ER:Default Error Recovery will select between the Hard Reset and + Bus Device Reset options based on the recommendation + of the SCSI Subsystem. + + ER:HardReset Error Recovery will initiate a Host Adapter Hard Reset + which also causes a SCSI Bus Reset. + + ER:BusDeviceReset Error Recovery will send a Bus Device Reset message to + the individual Target Device causing the error. If + Error Recovery is again initiated for this Target + Device and no SCSI Command to this Target Device has + completed successfully since the Bus Device Reset + message was sent, then a Hard Reset will be attempted. + + ER:None Error Recovery will be suppressed. This option should + only be selected if a SCSI Bus Reset or Bus Device + Reset will cause the Target Device to fail completely + and unrecoverably. + + ER: Error Recovery will be controlled individually for each + Target Device. is a sequence of "D", + "H", "B", and "N" characters. "D" selects Default, "H" + selects Hard Reset, "B" selects Bus Device Reset, and + "N" selects None. The first character refers to Target + 0, the second to Target 1, and so on; if the sequence + of "D", "H", "B", and "N" characters does not cover all + the Target Devices, unspecified characters are assumed + to be "D". +*/ + +void BusLogic_Setup(char *Strings, int *Integers) +{ + BusLogic_CommandLineEntry_T *CommandLineEntry = + &BusLogic_CommandLineEntries[BusLogic_CommandLineEntryCount++]; + static int ProbeListIndex = 0; + int IntegerCount = Integers[0], TargetID, i; + CommandLineEntry->IO_Address = 0; + CommandLineEntry->Concurrency = 0; + CommandLineEntry->BusSettleTime = 0; + CommandLineEntry->TaggedQueuingPermitted = 0; + CommandLineEntry->TaggedQueuingPermittedMask = 0; + memset(CommandLineEntry->ErrorRecoveryOption, + BusLogic_ErrorRecoveryDefault, + sizeof(CommandLineEntry->ErrorRecoveryOption)); + if (IntegerCount > 4) + printk("BusLogic: Unexpected Command Line Integers ignored\n"); + if (IntegerCount >= 1) + { + unsigned short IO_Address = Integers[1]; + if (IO_Address > 0) + { + for (i = 0; ; i++) + if (BusLogic_IO_StandardAddresses[i] == 0) + { + printk("BusLogic: Invalid Command Line Entry " + "(illegal I/O Address 0x%X)\n", IO_Address); + return; + } + else if (i < ProbeListIndex && + IO_Address == BusLogic_IO_AddressProbeList[i]) + { + printk("BusLogic: Invalid Command Line Entry " + "(duplicate I/O Address 0x%X)\n", IO_Address); + return; + } + else if (IO_Address == BusLogic_IO_StandardAddresses[i]) break; + BusLogic_IO_AddressProbeList[ProbeListIndex++] = IO_Address; + BusLogic_IO_AddressProbeList[ProbeListIndex] = 0; + } + CommandLineEntry->IO_Address = IO_Address; + } + if (IntegerCount >= 2) + { + unsigned short Concurrency = Integers[2]; + if (Concurrency > BusLogic_MailboxCount) + { + printk("BusLogic: Invalid Command Line Entry " + "(illegal Concurrency %d)\n", Concurrency); + return; + } + CommandLineEntry->Concurrency = Concurrency; + } + if (IntegerCount >= 3) + CommandLineEntry->BusSettleTime = Integers[3]; + if (IntegerCount >= 4) + BusLogic_TracingOptions |= Integers[4]; + if (!(BusLogic_CommandLineEntryCount == 0 || ProbeListIndex == 0 || + BusLogic_CommandLineEntryCount == ProbeListIndex)) + { + printk("BusLogic: Invalid Command Line Entry " + "(all or no I/O Addresses must be specified)\n"); + return; + } + if (Strings == NULL) return; + if (strncmp(Strings, "TQ:", 3) == 0) + { + Strings += 3; + if (strncmp(Strings, "Default", 7) == 0) + Strings += 7; + else if (strncmp(Strings, "Enable", 6) == 0) + { + Strings += 6; + CommandLineEntry->TaggedQueuingPermitted = 0xFFFF; + CommandLineEntry->TaggedQueuingPermittedMask = 0xFFFF; + } + else if (strncmp(Strings, "Disable", 7) == 0) + { + Strings += 7; + CommandLineEntry->TaggedQueuingPermitted = 0x0000; + CommandLineEntry->TaggedQueuingPermittedMask = 0xFFFF; + } + else + for (TargetID = 0; TargetID < BusLogic_MaxTargetIDs; TargetID++) + switch (*Strings++) + { + case 'Y': + CommandLineEntry->TaggedQueuingPermitted |= 1 << TargetID; + CommandLineEntry->TaggedQueuingPermittedMask |= 1 << TargetID; + break; + case 'N': + CommandLineEntry->TaggedQueuingPermittedMask |= 1 << TargetID; + break; + case 'X': + break; + default: + Strings--; + TargetID = BusLogic_MaxTargetIDs; + break; + } + } + if (*Strings == ',') Strings++; + if (strncmp(Strings, "ER:", 3) == 0) + { + Strings += 3; + if (strncmp(Strings, "Default", 7) == 0) + Strings += 7; + else if (strncmp(Strings, "HardReset", 9) == 0) + { + Strings += 9; + memset(CommandLineEntry->ErrorRecoveryOption, + BusLogic_ErrorRecoveryHardReset, + sizeof(CommandLineEntry->ErrorRecoveryOption)); + } + else if (strncmp(Strings, "BusDeviceReset", 14) == 0) + { + Strings += 14; + memset(CommandLineEntry->ErrorRecoveryOption, + BusLogic_ErrorRecoveryBusDeviceReset, + sizeof(CommandLineEntry->ErrorRecoveryOption)); + } + else if (strncmp(Strings, "None", 4) == 0) + { + Strings += 4; + memset(CommandLineEntry->ErrorRecoveryOption, + BusLogic_ErrorRecoveryNone, + sizeof(CommandLineEntry->ErrorRecoveryOption)); + } + else + for (TargetID = 0; TargetID < BusLogic_MaxTargetIDs; TargetID++) + switch (*Strings++) + { + case 'D': + CommandLineEntry->ErrorRecoveryOption[TargetID] = + BusLogic_ErrorRecoveryDefault; + break; + case 'H': + CommandLineEntry->ErrorRecoveryOption[TargetID] = + BusLogic_ErrorRecoveryHardReset; + break; + case 'B': + CommandLineEntry->ErrorRecoveryOption[TargetID] = + BusLogic_ErrorRecoveryBusDeviceReset; + break; + case 'N': + CommandLineEntry->ErrorRecoveryOption[TargetID] = + BusLogic_ErrorRecoveryNone; + break; + default: + Strings--; + TargetID = BusLogic_MaxTargetIDs; + break; + } + } + if (*Strings != '\0') + printk("BusLogic: Unexpected Command Line String '%s' ignored\n", Strings); +} diff --git a/drivers/scsi/BusLogic.h b/drivers/scsi/BusLogic.h new file mode 100644 index 000000000000..a513ce2db376 --- /dev/null +++ b/drivers/scsi/BusLogic.h @@ -0,0 +1,967 @@ +/* + + Linux Driver for BusLogic SCSI Host Adapters + + Copyright 1995 by Leonard N. Zubkoff + + See BusLogic.c for licensing information. + +*/ + + +/* + Define types for some of the structures that interface with the rest + of the Linux Kernel and SCSI Subsystem. +*/ + +typedef struct pt_regs Registers_T; +typedef Scsi_Host_Template SCSI_Host_Template_T; +typedef struct Scsi_Host SCSI_Host_T; +typedef struct scsi_disk SCSI_Disk_T; +typedef struct scsi_cmnd SCSI_Command_T; +typedef struct scatterlist SCSI_ScatterList_T; +typedef kdev_t KernelDevice_T; + + +/* + Define prototypes for the BusLogic Driver Interface Functions. +*/ + +const char *BusLogic_DriverInfo(SCSI_Host_T *); +int BusLogic_DetectHostAdapter(SCSI_Host_Template_T *); +int BusLogic_ReleaseHostAdapter(SCSI_Host_T *); +int BusLogic_QueueCommand(SCSI_Command_T *, + void (*CompletionRoutine)(SCSI_Command_T *)); +int BusLogic_AbortCommand(SCSI_Command_T *); +int BusLogic_ResetCommand(SCSI_Command_T *); +int BusLogic_BIOSDiskParameters(SCSI_Disk_T *, KernelDevice_T, int *); + + +/* + Define the BusLogic SCSI Host Template structure. +*/ + +#define BUSLOGIC \ + { NULL, /* Next */ \ + NULL, /* Usage Count Pointer */ \ + NULL, /* /proc Directory Entry */ \ + NULL, /* /proc Info Function */ \ + "BusLogic", /* Driver Name */ \ + BusLogic_DetectHostAdapter, /* Detect Host Adapter */ \ + BusLogic_ReleaseHostAdapter, /* Release Host Adapter */ \ + BusLogic_DriverInfo, /* Driver Info Function */ \ + NULL, /* Command Function */ \ + BusLogic_QueueCommand, /* Queue Command Function */ \ + BusLogic_AbortCommand, /* Abort Command Function */ \ + BusLogic_ResetCommand, /* Reset Command Function */ \ + NULL, /* Slave Attach Function */ \ + BusLogic_BIOSDiskParameters, /* Disk BIOS Parameters */ \ + 0, /* Can Queue */ \ + 0, /* This ID */ \ + 0, /* Scatter/Gather Table Size */ \ + 0, /* SCSI Commands per LUN */ \ + 0, /* Present */ \ + 1, /* Default Unchecked ISA DMA */ \ + ENABLE_CLUSTERING } /* Enable Clustering */ + + +/* + BusLogic_DriverVersion protects the private portion of this file. +*/ + +#ifdef BusLogic_DriverVersion + + +/* + Define the maximum number of BusLogic Host Adapters that are supported. +*/ + +#define BusLogic_MaxHostAdapters 10 + + +/* + Define the maximum number of I/O Addresses that may be probed. +*/ + +#define BusLogic_IO_MaxProbeAddresses 16 + + +/* + Define the maximum number of Target IDs supported by this driver. +*/ + +#define BusLogic_MaxTargetIDs 16 + + +/* + Define the number of Incoming and Outgoing Mailboxes used by this driver. + Since BusLogic Host Adapters have room to buffer 32 commands internally, + there is significant virtue in setting BusLogic_MailboxCount to 32 or above. + The maximum possible value is 255, since the MailboxCount parameter to the + Initialize Extended Mailbox command is limited to a single byte. +*/ + +#define BusLogic_MailboxCount 32 + + +/* + Define the number of Command Control Blocks (CCBs) to create during + initialization for each Host Adapter. Additional CCBs will be allocated + if necessary as commands are queued. +*/ + +#define BusLogic_InitialCCBs 32 + + +/* + Define the maximum number of Scatter/Gather Segments used by this driver. + For maximum performance, it is important that this limit be at least as + large as the maximum single request generated by the routine make_request. +*/ + +#define BusLogic_ScatterGatherLimit 128 + + +/* + Define the default number of Concurrent Commands per Logical Unit to allow + for Target Devices on non-ISA and ISA Host Adapters. +*/ + +#define BusLogic_Concurrency 7 +#define BusLogic_Concurrency_ISA 1 + + +/* + Define the default amount of time in seconds to wait between a Host Adapter + Hard Reset which initiates a SCSI Bus Reset and issuing any SCSI commands. + Some SCSI devices get confused if they receive SCSI commands too soon after + a SCSI Bus Reset. +*/ + +#define BusLogic_DefaultBusSettleTime 2 + + +/* + Define the possible Tracing Options. +*/ + +#define BusLogic_TraceProbe 1 +#define BusLogic_TraceHardReset 2 +#define BusLogic_TraceConfiguration 4 +#define BusLogic_TraceErrors 8 + + +/* + Define the possible Error Recovery Options. +*/ + +#define BusLogic_ErrorRecoveryDefault 0 +#define BusLogic_ErrorRecoveryHardReset 1 +#define BusLogic_ErrorRecoveryBusDeviceReset 2 +#define BusLogic_ErrorRecoveryNone 3 + +static char + *BusLogic_ErrorRecoveryOptions[] = + { "Default", "Hard Reset", "Bus Device Reset", "None" }, + *BusLogic_ErrorRecoveryOptions2[] = + { "D", "H", "B", "N" }; + + +/* + Define a boolean data type. +*/ + +#define false 0 +#define true 1 +typedef unsigned char boolean; + + +/* + Define the BusLogic SCSI Host Adapter I/O Register Offsets. +*/ + +#define BusLogic_IO_PortCount 4 /* I/O Registers */ +#define BusLogic_ControlRegister 0 /* WO register */ +#define BusLogic_StatusRegister 0 /* RO register */ +#define BusLogic_CommandParameterRegister 1 /* WO register */ +#define BusLogic_DataInRegister 1 /* RO register */ +#define BusLogic_InterruptRegister 2 /* RO register */ +#define BusLogic_GeometryRegister 3 /* RO, undocumented */ + + +/* + Define the bits in the write-only Control Register. +*/ + +#define BusLogic_ReservedCR 0x0F +#define BusLogic_SCSIBusReset 0x10 +#define BusLogic_InterruptReset 0x20 +#define BusLogic_SoftReset 0x40 +#define BusLogic_HardReset 0x80 + + +/* + Define the bits in the read-only Status Register. +*/ + +#define BusLogic_CommandInvalid 0x01 +#define BusLogic_ReservedSR 0x02 +#define BusLogic_DataInRegisterReady 0x04 +#define BusLogic_CommandParameterRegisterBusy 0x08 +#define BusLogic_HostAdapterReady 0x10 +#define BusLogic_InitializationRequired 0x20 +#define BusLogic_DiagnosticFailure 0x40 +#define BusLogic_DiagnosticActive 0x80 + + +/* + Define the bits in the read-only Interrupt Register. +*/ + +#define BusLogic_IncomingMailboxLoaded 0x01 +#define BusLogic_OutgoingMailboxAvailable 0x02 +#define BusLogic_CommandComplete 0x04 +#define BusLogic_SCSIResetState 0x08 +#define BusLogic_ReservedIR 0x70 +#define BusLogic_InterruptValid 0x80 + + +/* + Define the bits in the undocumented read-only Geometry Register. +*/ + +#define BusLogic_Drive0Geometry 0x03 +#define BusLogic_Drive1Geometry 0x0C +#define BusLogic_ReservedGR 0x70 +#define BusLogic_ExtendedTranslationEnabled 0x80 + + +/* + Define the BusLogic SCSI Host Adapter Command Register Operation Codes. +*/ + +typedef enum +{ + BusLogic_TestCommandCompleteInterrupt = 0x00, /* documented */ + BusLogic_InitializeMailbox = 0x01, /* documented */ + BusLogic_StartMailboxCommand = 0x02, /* documented */ + BusLogic_StartBIOSCommand = 0x03, /* documented */ + BusLogic_InquireBoardID = 0x04, /* documented */ + BusLogic_EnableOutgoingMailboxAvailableIRQ = 0x05, /* documented */ + BusLogic_SetSCSISelectionTimeout = 0x06, /* documented */ + BusLogic_SetPreemptTimeOnBus = 0x07, /* documented */ + BusLogic_SetTimeOffBus = 0x08, /* ISA Bus only */ + BusLogic_SetBusTransferRate = 0x09, /* ISA Bus only */ + BusLogic_InquireInstalledDevicesID0to7 = 0x0A, /* documented */ + BusLogic_InquireConfiguration = 0x0B, /* documented */ + BusLogic_SetTargetMode = 0x0C, /* now undocumented */ + BusLogic_InquireSetupInformation = 0x0D, /* documented */ + BusLogic_WriteAdapterLocalRAM = 0x1A, /* documented */ + BusLogic_ReadAdapterLocalRAM = 0x1B, /* documented */ + BusLogic_WriteBusMasterChipFIFO = 0x1C, /* documented */ + BusLogic_ReadBusMasterChipFIFO = 0x1D, /* documented */ + BusLogic_EchoCommandData = 0x1F, /* documented */ + BusLogic_HostAdapterDiagnostic = 0x20, /* documented */ + BusLogic_SetAdapterOptions = 0x21, /* documented */ + BusLogic_InquireInstalledDevicesID8to15 = 0x23, /* Wide only */ + BusLogic_InitializeExtendedMailbox = 0x81, /* documented */ + BusLogic_InquireFirmwareVersion3rdDigit = 0x84, /* undocumented */ + BusLogic_InquireFirmwareVersionLetter = 0x85, /* undocumented */ + BusLogic_InquireBoardModelAndRevision = 0x8B, /* undocumented */ + BusLogic_InquireSynchronousPeriod = 0x8C, /* undocumented */ + BusLogic_InquireExtendedSetupInformation = 0x8D, /* documented */ + BusLogic_EnableStrictRoundRobinMode = 0x8F, /* documented */ + BusLogic_ModifyIOAddress = 0x95, /* PCI only */ + BusLogic_EnableWideModeCCB = 0x96 /* Wide only */ +} +BusLogic_OperationCode_T; + + +/* + Define the Inquire Board ID reply structure. +*/ + +typedef struct BusLogic_BoardID +{ + unsigned char BoardType; + unsigned char CustomFeatures; + unsigned char FirmwareVersion1stDigit; + unsigned char FirmwareVersion2ndDigit; +} +BusLogic_BoardID_T; + + +/* + Define the Inquire Installed Devices ID 0 to 7 and Inquire Installed + Devices ID 8 to 15 reply type. For each Target ID, a byte is returned + where bit 0 set indicates that Logical Unit 0 exists, bit 1 set indicates + that Logical Unit 1 exists, and so on. +*/ + +typedef unsigned char BusLogic_InstalledDevices8_T[8]; + +typedef unsigned char BusLogic_InstalledDevices_T[BusLogic_MaxTargetIDs]; + + +/* + Define the Inquire Configuration reply structure. +*/ + +typedef struct BusLogic_Configuration +{ + unsigned char :5; /* Byte 0: DMA Channel */ + boolean DMA_Channel5:1; + boolean DMA_Channel6:1; + boolean DMA_Channel7:1; + boolean IRQ_Channel9:1; /* Byte 1: IRQ Channel */ + boolean IRQ_Channel10:1; + boolean IRQ_Channel11:1; + boolean IRQ_Channel12:1; + unsigned char :1; + boolean IRQ_Channel14:1; + boolean IRQ_Channel15:1; + unsigned char :1; + unsigned char HostAdapterID:4; /* Byte 2: Host Adapter ID */ + unsigned char :4; +} +BusLogic_Configuration_T; + + +/* + Define the Inquire Setup Information reply structure. +*/ + +typedef struct BusLogic_SynchronousValue +{ + unsigned char Offset:4; + unsigned char TransferPeriod:3; + boolean Synchronous:1; +} +BusLogic_SynchronousValue_T; + +typedef BusLogic_SynchronousValue_T + BusLogic_SynchronousValues8_T[8]; + +typedef BusLogic_SynchronousValue_T + BusLogic_SynchronousValues_T[BusLogic_MaxTargetIDs]; + +typedef struct BusLogic_SetupInformation +{ + boolean SynchronousInitiationEnabled:1; /* Byte 0 */ + boolean ParityCheckEnabled:1; + unsigned char :6; + unsigned char BusTransferRate; /* Byte 1 */ + unsigned char PreemptTimeOnBus; /* Byte 2 */ + unsigned char TimeOffBus; /* Byte 3 */ + unsigned char MailboxCount; /* Byte 4 */ + unsigned char MailboxAddress[3]; /* Bytes 5-7 */ + BusLogic_SynchronousValues8_T SynchronousValuesID0to7; /* Bytes 8-15 */ + unsigned char DisconnectPermittedID0to7; /* Byte 16 */ + unsigned char Signature; /* Byte 17 */ + unsigned char CharacterD; /* Byte 18 */ + unsigned char BusLetter; /* Byte 19 */ + unsigned char :8; /* Byte 20 */ + unsigned char :8; /* Byte 21 */ + BusLogic_SynchronousValues8_T SynchronousValuesID8to15; /* Bytes 22-29 */ + unsigned char DisconnectPermittedID8to15; /* Byte 30 */ +} +BusLogic_SetupInformation_T; + + +/* + Define the Initialize Extended Mailbox request structure. +*/ + +typedef struct BusLogic_ExtendedMailboxRequest +{ + unsigned char MailboxCount; + void *BaseMailboxAddress __attribute__ ((packed)); +} +BusLogic_ExtendedMailboxRequest_T; + + +/* + Define the Inquire Firmware Version 3rd Digit reply type. +*/ + +typedef unsigned char BusLogic_FirmwareVersion3rdDigit_T; + + +/* + Define the Inquire Firmware Version Letter reply type. +*/ + +typedef unsigned char BusLogic_FirmwareVersionLetter_T; + + +/* + Define the Inquire Board Model and Revision reply structure. +*/ + +typedef struct BusLogic_ModelAndRevision +{ + unsigned char Model[5]; + unsigned char Revision; +} +BusLogic_ModelAndRevision_T; + + +/* + Define the Inquire Synchronous Period reply type. For each Target ID, a byte + is returned which represents the Synchronous Transfer Period in units of 10 + nanoseconds. +*/ + +typedef unsigned char BusLogic_SynchronousPeriod_T[BusLogic_MaxTargetIDs]; + + +/* + Define the Inquire Extended Setup Information reply structure. +*/ + +typedef struct BusLogic_ExtendedSetupInformation +{ + unsigned char BusType; /* Byte 0 */ + unsigned char BIOS_Address; /* Byte 1 */ + unsigned short ScatterGatherLimit; /* Bytes 2-3 */ + unsigned char MailboxCount; /* Byte 4 */ + void *BaseMailboxAddress __attribute__ ((packed)); /* Bytes 5-8 */ + struct { unsigned char :6; /* Byte 9 */ + boolean LevelTriggeredInterrupts:1; + unsigned char :1; } Misc; + unsigned char FirmwareRevision[3]; /* Bytes 10-12 */ + boolean HostWideSCSI:1; /* Byte 13 Bit 0 */ + boolean HostDifferentialSCSI:1; /* Byte 13 Bit 1 */ + unsigned char :6; +} +BusLogic_ExtendedSetupInformation_T; + + +/* + Define the Enable Strict Round Robin Mode request type. +*/ + +#define BusLogic_AggressiveRoundRobinMode 0x00 +#define BusLogic_StrictRoundRobinMode 0x01 + +typedef unsigned char BusLogic_RoundRobinModeRequest_T; + + +/* + Define the Modify I/O Address request type. On PCI Host Adapters, the + Modify I/O Address command allows modification of the ISA compatible I/O + Address that the Host Adapter responds to; it does not affect the PCI + compliant I/O Address assigned at system initialization. +*/ + +#define BusLogic_ModifyIO_330 0x00 +#define BusLogic_ModifyIO_334 0x01 +#define BusLogic_ModifyIO_230 0x02 +#define BusLogic_ModifyIO_234 0x03 +#define BusLogic_ModifyIO_130 0x04 +#define BusLogic_ModifyIO_134 0x05 +#define BusLogic_ModifyIO_Disable 0x06 +#define BusLogic_ModifyIO_Disable2 0x07 + +typedef unsigned char BusLogic_ModifyIOAddressRequest_T; + + +/* + Define the Enable Wide Mode SCSI CCB request type. Wide Mode CCBs are + necessary to support more than 8 Logical Units per Target. +*/ + +#define BusLogic_NormalModeCCB 0x00 +#define BusLogic_WideModeCCB 0x01 + +typedef unsigned char BusLogic_WideModeCCBRequest_T; + + +/* + Define the Requested Reply Length type used by the Inquire Setup Information, + Inquire Board Model and Revision, Inquire Synchronous Period, and Inquire + Extended Setup Information commands. +*/ + +typedef unsigned char BusLogic_RequestedReplyLength_T; + + +/* + Define a Lock data structure. Until a true symmetric multiprocessing kernel + is available, locking is implemented as saving the processor flags and + disabling interrupts, and unlocking restores the saved processor flags. +*/ + +typedef unsigned long BusLogic_Lock_T; + + +/* + Define the Outgoing Mailbox Action Codes. +*/ + +typedef enum +{ + BusLogic_OutgoingMailboxFree = 0, + BusLogic_MailboxStartCommand = 1, + BusLogic_MailboxAbortCommand = 2 +} +BusLogic_ActionCode_T; + + +/* + Define the Incoming Mailbox Completion Codes. +*/ + +typedef enum +{ + BusLogic_IncomingMailboxFree = 0, + BusLogic_CommandCompletedWithoutError = 1, + BusLogic_CommandAbortedAtHostRequest = 2, + BusLogic_AbortedCommandNotFound = 3, + BusLogic_CommandCompletedWithError = 4 +} +BusLogic_CompletionCode_T; + + +/* + Define the Command Control Block (CCB) Opcodes. +*/ + +typedef enum +{ + BusLogic_InitiatorCCB = 0x00, + BusLogic_TargetCCB = 0x01, + BusLogic_InitiatorCCB_ScatterGather = 0x02, + BusLogic_InitiatorCCB_ResidualDataLength = 0x03, + BusLogic_InitiatorCCB_ScatterGatherResidual = 0x04, + BusLogic_SCSIBusDeviceReset = 0x81 +} +BusLogic_CCB_Opcode_T; + + +/* + Define the CCB Data Direction Codes. +*/ + +typedef enum +{ + BusLogic_UncheckedDataTransfer = 0x00, + BusLogic_DataInLengthChecked = 0x01, + BusLogic_DataOutLengthChecked = 0x02, + BusLogic_NoDataTransfer = 0x03 +} +BusLogic_DataDirection_T; + + +/* + Define the Host Adapter Status Codes. +*/ + +typedef enum +{ + BusLogic_CommandCompletedNormally = 0x00, + BusLogic_LinkedCommandCompleted = 0x0A, + BusLogic_LinkedCommandCompletedWithFlag = 0x0B, + BusLogic_SCSISelectionTimeout = 0x11, + BusLogic_DataOverUnderRun = 0x12, + BusLogic_UnexpectedBusFree = 0x13, + BusLogic_InvalidBusPhaseRequested = 0x14, + BusLogic_InvalidOutgoingMailboxActionCode = 0x15, + BusLogic_InvalidCommandOperationCode = 0x16, + BusLogic_LinkedCCBhasInvalidLUN = 0x17, + BusLogic_InvalidCommandParameter = 0x1A, + BusLogic_AutoRequestSenseFailed = 0x1B, + BusLogic_TaggedQueuingMessageRejected = 0x1C, + BusLogic_UnsupportedMessageReceived = 0x1D, + BusLogic_HostAdapterHardwareFailed = 0x20, + BusLogic_TargetFailedResponseToATN = 0x21, + BusLogic_HostAdapterAssertedRST = 0x22, + BusLogic_OtherDeviceAssertedRST = 0x23, + BusLogic_TargetDeviceReconnectedImproperly = 0x24, + BusLogic_HostAdapterAssertedBusDeviceReset = 0x25, + BusLogic_AbortQueueGenerated = 0x26, + BusLogic_HostAdapterSoftwareError = 0x27, + BusLogic_HostAdapterHardwareTimeoutError = 0x30, + BusLogic_SCSIParityErrorDetected = 0x34 +} +BusLogic_HostAdapterStatus_T; + + +/* + Define the SCSI Target Device Status Codes. +*/ + +typedef enum +{ + BusLogic_OperationGood = 0x00, + BusLogic_CheckCondition = 0x02, + BusLogic_DeviceBusy = 0x08 +} +BusLogic_TargetDeviceStatus_T; + + +/* + Define the Queue Tag Codes. +*/ + +typedef enum +{ + BusLogic_SimpleQueueTag = 0x00, + BusLogic_HeadOfQueueTag = 0x01, + BusLogic_OrderedQueueTag = 0x02, + BusLogic_ReservedQT = 0x03 +} +BusLogic_QueueTag_T; + + +/* + Define the SCSI Command Descriptor Block (CDB). +*/ + +#define BusLogic_CDB_MaxLength 12 + +typedef unsigned char SCSI_CDB_T[BusLogic_CDB_MaxLength]; + + +/* + Define the SCSI Sense Data. +*/ + +#define BusLogic_SenseDataMaxLength 255 + +typedef unsigned char SCSI_SenseData_T[BusLogic_SenseDataMaxLength]; + + +/* + Define the Scatter/Gather Segment structure required by the Host Adapter + Firmware Interface. +*/ + +typedef struct BusLogic_ScatterGatherSegment +{ + unsigned long SegmentByteCount; + void *SegmentDataPointer; +} +BusLogic_ScatterGatherSegment_T; + + +/* + Define the 32 Bit Mode Command Control Block (CCB) structure. The first 40 + bytes are defined by the Host Adapter Firmware Interface. The remaining + components are defined by the Linux BusLogic Driver. Wide Mode CCBs differ + from standard 32 Bit Mode CCBs only in having the TagEnable and QueueTag + fields moved from byte 17 to byte 1, and the Logical Unit field in byte 17 + expanded to 6 bits; unfortunately, using a union of structs containing + enumeration type bitfields to provide both definitions leads to packing + problems, so the following definition is used which requires setting + TagEnable to Logical Unit bit 5 in Wide Mode CCBs. +*/ + +typedef struct BusLogic_CCB +{ + /* + BusLogic Host Adapter Firmware Portion. + */ + BusLogic_CCB_Opcode_T Opcode:8; /* Byte 0 */ + unsigned char :3; /* Byte 1 Bits 0-2 */ + BusLogic_DataDirection_T DataDirection:2; /* Byte 1 Bits 3-4 */ + boolean WideModeTagEnable:1; /* Byte 1 Bit 5 */ + BusLogic_QueueTag_T WideModeQueueTag:2; /* Byte 1 Bits 6-7 */ + unsigned char CDB_Length; /* Byte 2 */ + unsigned char SenseDataLength; /* Byte 3 */ + unsigned long DataLength; /* Bytes 4-7 */ + void *DataPointer; /* Bytes 8-11 */ + unsigned char :8; /* Byte 12 */ + unsigned char :8; /* Byte 13 */ + BusLogic_HostAdapterStatus_T HostAdapterStatus:8; /* Byte 14 */ + BusLogic_TargetDeviceStatus_T TargetDeviceStatus:8; /* Byte 15 */ + unsigned char TargetID; /* Byte 16 */ + unsigned char LogicalUnit:5; /* Byte 17 Bits 0-2 */ + boolean TagEnable:1; /* Byte 17 Bit 5 */ + BusLogic_QueueTag_T QueueTag:2; /* Byte 17 Bits 6-7 */ + SCSI_CDB_T CDB; /* Bytes 18-29 */ + unsigned char :8; /* Byte 30 */ + unsigned char :8; /* Byte 31 */ + unsigned long :32; /* Bytes 32-35 */ + SCSI_SenseData_T *SenseDataPointer; /* Bytes 36-39 */ + /* + BusLogic Linux Driver Portion. + */ + struct BusLogic_HostAdapter *HostAdapter; + SCSI_Command_T *Command; + enum { BusLogic_CCB_Free = 0, + BusLogic_CCB_Active = 1, + BusLogic_CCB_Completed = 2 } Status; + BusLogic_CompletionCode_T MailboxCompletionCode; + unsigned int SerialNumber; + struct BusLogic_CCB *Next; + struct BusLogic_CCB *NextAll; + BusLogic_ScatterGatherSegment_T + ScatterGatherList[BusLogic_ScatterGatherLimit]; +} +BusLogic_CCB_T; + + +/* + Define the 32 Bit Mode Outgoing Mailbox structure. +*/ + +typedef struct BusLogic_OutgoingMailbox +{ + BusLogic_CCB_T *CCB; + unsigned long :24; + BusLogic_ActionCode_T ActionCode:8; +} +BusLogic_OutgoingMailbox_T; + + +/* + Define the 32 Bit Mode Incoming Mailbox structure. +*/ + +typedef struct BusLogic_IncomingMailbox +{ + BusLogic_CCB_T *CCB; + BusLogic_HostAdapterStatus_T HostAdapterStatus:8; + BusLogic_TargetDeviceStatus_T TargetDeviceStatus:8; + unsigned char :8; + BusLogic_CompletionCode_T CompletionCode:8; +} +BusLogic_IncomingMailbox_T; + + +/* + Define the possible Bus Types. +*/ + +typedef enum +{ + BusLogic_Unknown_Bus = 0, + BusLogic_ISA_Bus = 1, + BusLogic_MCA_Bus = 2, + BusLogic_EISA_Bus = 3, + BusLogic_VESA_Bus = 4, + BusLogic_PCI_Bus = 5 +} +BusLogic_BusType_T; + +static char + *BusLogic_BusNames[] = + { "Unknown", "ISA", "MCA", "EISA", "VESA", "PCI" }; + + +/* + Define the Linux BusLogic Driver Command Line Entry structure. +*/ + +typedef struct BusLogic_CommandLineEntry +{ + unsigned short IO_Address; + unsigned short Concurrency; + unsigned short BusSettleTime; + unsigned short TaggedQueuingPermitted; + unsigned short TaggedQueuingPermittedMask; + unsigned char ErrorRecoveryOption[BusLogic_MaxTargetIDs]; +} +BusLogic_CommandLineEntry_T; + + +/* + Define the Linux BusLogic Driver Host Adapter structure. +*/ + +typedef struct BusLogic_HostAdapter +{ + SCSI_Host_T *SCSI_Host; + unsigned char HostNumber; + unsigned char ModelName[6]; + unsigned char FirmwareVersion[6]; + unsigned char BoardName[15]; + unsigned char InterruptLabel[63]; + unsigned short IO_Address; + unsigned char IRQ_Channel; + unsigned char DMA_Channel; + unsigned char SCSI_ID; + BusLogic_BusType_T BusType:3; + boolean IRQ_ChannelAcquired:1; + boolean DMA_ChannelAcquired:1; + boolean SynchronousInitiation:1; + boolean ParityChecking:1; + boolean ExtendedTranslation:1; + boolean LevelTriggeredInterrupts:1; + boolean HostWideSCSI:1; + boolean HostDifferentialSCSI:1; + boolean HostAdapterResetPending:1; + volatile boolean HostAdapterCommandCompleted:1; + unsigned short HostAdapterScatterGatherLimit; + unsigned short DriverScatterGatherLimit; + unsigned short MaxTargetIDs; + unsigned short MaxLogicalUnits; + unsigned short Concurrency; + unsigned short BusSettleTime; + unsigned short DisconnectPermitted; + unsigned short TaggedQueuingPermitted; + unsigned long BIOS_Address; + BusLogic_InstalledDevices_T InstalledDevices; + BusLogic_SynchronousValues_T SynchronousValues; + BusLogic_SynchronousPeriod_T SynchronousPeriod; + BusLogic_Lock_T Lock; + struct BusLogic_HostAdapter *Next; + BusLogic_CommandLineEntry_T *CommandLineEntry; + BusLogic_CCB_T *All_CCBs; + BusLogic_CCB_T *Free_CCBs; + unsigned char ErrorRecoveryOption[BusLogic_MaxTargetIDs]; + unsigned char CommandSuccessfulFlag[BusLogic_MaxTargetIDs]; + unsigned long ReadWriteOperationCount[BusLogic_MaxTargetIDs]; + BusLogic_OutgoingMailbox_T *FirstOutgoingMailbox; + BusLogic_OutgoingMailbox_T *LastOutgoingMailbox; + BusLogic_OutgoingMailbox_T *NextOutgoingMailbox; + BusLogic_IncomingMailbox_T *FirstIncomingMailbox; + BusLogic_IncomingMailbox_T *LastIncomingMailbox; + BusLogic_IncomingMailbox_T *NextIncomingMailbox; + BusLogic_OutgoingMailbox_T OutgoingMailboxes[BusLogic_MailboxCount]; + BusLogic_IncomingMailbox_T IncomingMailboxes[BusLogic_MailboxCount]; +} +BusLogic_HostAdapter_T; + + +/* + Define a symbolic structure for the BIOS Disk Parameters. +*/ + +typedef struct BIOS_DiskParameters +{ + int Heads; + int Sectors; + int Cylinders; +} +BIOS_DiskParameters_T; + + +/* + BusLogic_LockHostAdapter acquires exclusive access to Host Adapter. +*/ + +static inline +void BusLogic_LockHostAdapter(BusLogic_HostAdapter_T *HostAdapter) +{ + save_flags(HostAdapter->Lock); + cli(); +} + + +/* + BusLogic_UnlockHostAdapter releases exclusive access to Host Adapter. +*/ + +static inline +void BusLogic_UnlockHostAdapter(BusLogic_HostAdapter_T *HostAdapter) +{ + restore_flags(HostAdapter->Lock); +} + + +/* + BusLogic_LockHostAdapterID acquires exclusive access to Host Adapter, + but is only called when interrupts are disabled. +*/ + +static inline +void BusLogic_LockHostAdapterID(BusLogic_HostAdapter_T *HostAdapter) +{ +} + + +/* + BusLogic_UnlockHostAdapterID releases exclusive access to Host Adapter, + but is only called when interrupts are disabled. +*/ + +static inline +void BusLogic_UnlockHostAdapterID(BusLogic_HostAdapter_T *HostAdapter) +{ +} + + +/* + Define functions to provide an abstraction for reading and writing the + Host Adapter I/O Registers. +*/ + +static inline +void BusLogic_WriteControlRegister(BusLogic_HostAdapter_T *HostAdapter, + unsigned char Value) +{ + outb(Value, HostAdapter->IO_Address + BusLogic_ControlRegister); +} + +static inline +unsigned char BusLogic_ReadStatusRegister(BusLogic_HostAdapter_T *HostAdapter) +{ + return inb(HostAdapter->IO_Address + BusLogic_StatusRegister); +} + +static inline +void BusLogic_WriteCommandParameterRegister(BusLogic_HostAdapter_T *HostAdapter, + unsigned char Value) +{ + outb(Value, HostAdapter->IO_Address + BusLogic_CommandParameterRegister); +} + +static inline +unsigned char BusLogic_ReadDataInRegister(BusLogic_HostAdapter_T *HostAdapter) +{ + return inb(HostAdapter->IO_Address + BusLogic_DataInRegister); +} + +static inline +unsigned char BusLogic_ReadInterruptRegister(BusLogic_HostAdapter_T + *HostAdapter) +{ + return inb(HostAdapter->IO_Address + BusLogic_InterruptRegister); +} + +static inline +unsigned char BusLogic_ReadGeometryRegister(BusLogic_HostAdapter_T *HostAdapter) +{ + return inb(HostAdapter->IO_Address + BusLogic_GeometryRegister); +} + + +/* + BusLogic_StartMailboxScan issues a Start Mailbox Scan command, which + notifies the Host Adapter that an entry has been made in an Outgoing + Mailbox. +*/ + +static inline +void BusLogic_StartMailboxScan(BusLogic_HostAdapter_T *HostAdapter) +{ + BusLogic_WriteCommandParameterRegister(HostAdapter, + BusLogic_StartMailboxCommand); +} + + +/* + BusLogic_Delay waits for Seconds to elapse. It must be called with + interrupts enabled so that jiffies is updated. +*/ + +static inline void BusLogic_Delay(int Seconds) +{ + unsigned long TimeoutJiffies = jiffies + Seconds * HZ; + while (jiffies < TimeoutJiffies) ; +} + + +/* + Define prototypes for the forward referenced BusLogic Driver + Internal Functions. +*/ + +static void BusLogic_InterruptHandler(int, Registers_T *); +static int BusLogic_ResetHostAdapter(BusLogic_HostAdapter_T *, + SCSI_Command_T *); + + +#endif /* BusLogic_DriverVersion */ diff --git a/drivers/scsi/ChangeLog b/drivers/scsi/ChangeLog index 97ea292162cd..20c185a8e813 100644 --- a/drivers/scsi/ChangeLog +++ b/drivers/scsi/ChangeLog @@ -1,3 +1,7 @@ +Fri Nov 10 15:29:49 1995 Leonard N. Zubkoff (lnz@dandelion.com) + + * Released new BusLogic driver. + Wed Aug 9 22:37:04 1995 Andries Brouwer As a preparation for new device code, separated the various diff --git a/drivers/scsi/Config.in b/drivers/scsi/Config.in index 68842058a731..feb9306c8f50 100644 --- a/drivers/scsi/Config.in +++ b/drivers/scsi/Config.in @@ -18,7 +18,7 @@ dep_tristate 'Adaptec AHA152X support' CONFIG_SCSI_AHA152X $CONFIG_SCSI dep_tristate 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 $CONFIG_SCSI dep_tristate 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 $CONFIG_SCSI dep_tristate 'Adaptec AHA274X/284X/294X support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI -dep_tristate 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC $CONFIG_SCSI +bool 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC dep_tristate 'EATA-DMA (DPT, NEC, ATT, Olivetti) support' CONFIG_SCSI_EATA_DMA $CONFIG_SCSI dep_tristate 'EATA-PIO (old DPT PM2001, PM2012A) support' CONFIG_SCSI_EATA_PIO $CONFIG_SCSI dep_tristate 'UltraStor 14F/34F support' CONFIG_SCSI_U14_34F $CONFIG_SCSI diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 9b03da7aeee3..166f0c2cf0dc 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -113,10 +113,10 @@ else endif ifeq ($(CONFIG_SCSI_BUSLOGIC),y) -L_OBJS += buslogic.o +L_OBJS += BusLogic.o else ifeq ($(CONFIG_SCSI_BUSLOGIC),m) - M_OBJS += buslogic.o + M_OBJS += BusLogic.o endif endif diff --git a/drivers/scsi/README.BusLogic b/drivers/scsi/README.BusLogic new file mode 100644 index 000000000000..a4a2aa63a849 --- /dev/null +++ b/drivers/scsi/README.BusLogic @@ -0,0 +1,262 @@ + BusLogic SCSI Driver for Linux 1.3.41 + Version 1.3.0 ~ 13 November 1995 + + Leonard N. Zubkoff + Dandelion Digital + lnz@dandelion.com + + Copyright 1995 by Leonard N. Zubkoff + + + INTRODUCTION + +BusLogic, Inc. designs and manufactures a variety of high performance SCSI host +adapters which share a common programming interface across a diverse collection +of bus architectures by virtue of their MultiMaster ASIC technology. This +driver supports all present BusLogic MultiMaster Host Adapters, and should +support any future MultiMaster designs with little or no modification. Host +adapters based on the new FlashPoint technology are not supported by this +driver. + +My primary goals in writing this completely new BusLogic driver for Linux are +to achieve the full performance that BusLogic SCSI Host Adapters and modern +SCSI peripherals are capable of, and to provide a highly robust driver that can +be depended upon for high performance mission critical applications. All of +the major performance and error recovery features can be configured from the +Linux kernel command line, allowing individual installations to tune driver +performance and error recovery to their particular needs. + +The most recent versions of this driver will always be available by anonymous +FTP from ftp.dandelion.com. While directory listings are not permitted, the +introductory banner displayed on anonymous FTP login will provide a list of the +driver versions and any other files available for retrieval. + +Bug reports should be sent via electronic mail to "lnz@dandelion.com". Please +include with the bug report the complete configuration messages reported by the +driver and SCSI subsystem at startup, along with any subsequent system messages +relevant to SCSI operations, and a detailed description of your system's +hardware configuration. + +I have recently had conversations with the Senior Product Marketing Manager at +BusLogic regarding the needs of free software developers, and he has reaffirmed +BusLogic's committment to providing the technical information and support we +need to take full advantage of their products. BusLogic has also been very +accomodating in providing technical documentation, as well as access to their +engineering staff for technical questions and advice. In addition, they have +loaned me ISA cards for configuration testing, and even allowed me use of their +technical support lab to test EISA configurations, since I don't have an EISA +system. Their interest and support is greatly appreciated. + +BusLogic, Inc. is located at 4151 Burton Drive, Santa Clara, California, 95054, +USA and can be reached by Voice at 408/492-9090 or by FAX at 408/492-1542. +BusLogic maintains a World Wide Web site at http://www.buslogic.com, an +anonymous FTP site at ftp.buslogic.com, and a BBS at 408/492-1984. BusLogic +Technical Support can be reached by electronic mail at techsup@buslogic.com, by +Voice at 408/654-0760, or by FAX at 408/492-1542. Contact information for +offices in Europe and Japan is available on the Web site. + + + DRIVER FEATURES + +o Configuration Reporting and Testing + + During system initialization, the driver reports extensively on the host + adapter hardware configuration, including the synchronous transfer parameters + negotiated with each target device. In addition, the driver tests the + hardware interrupt configuration to verify that interrupts are actually + delivered correctly to the interrupt handler. This should catch a high + percentage of PCI motherboard configuration errors early, because when the + host adapter is probed successfully, most of the remaining problems appear to + be related to interrupts. Most often, any remaining hardware problems are + related to the specific configuration of devices on the SCSI bus, and the + quality of cabling and termination used. Finally, this BusLogic driver + should never incorrectly attempt to support an Adaptec 154x Host Adapter. + +o Performance Features + + BusLogic SCSI Host Adapters directly implement SCSI-2 Tagged Queuing, and so + support has been included in the driver to utilize tagged queuing with any + target devices that report having the tagged queuing capability. Tagged + queuing allows for multiple outstanding commands to be issued to each target + device or logical unit, and can improve I/O performance substantially. In + addition, BusLogic's Strict Round Robin Mode is used to optimize host adapter + performance, and scatter/gather I/O can support as many segments as can be + effectively utilized by the Linux I/O subsystem. Control over the use of + tagged queuing for each target device as well as selection of the maximum + number of concurrent commands per logical unit is available from the kernel + command line. In addition, tagged queuing is automatically disabled whenever + the host adapter firmware version is known not to implement it correctly, or + whenever a concurrency value of 1 is selected. In performance testing, + sustained disk writes of 7.3MB per second have been observed to a /dev/sd + device. + +o Robustness Features + + The driver implements extensive error recovery procedures. By default, when + the higher level parts of the SCSI subsystem request that a command be reset, + a selection is made between a full host adapter hard reset and SCSI bus reset + versus sending a bus device reset message to the individual device based on + the recommendation of the SCSI subsystem. Error recovery options are + selectable from the kernel command line individually for each target device, + and also include forcing a full host adapter hard reset and SCSI bus reset, + sending a bus device reset to the specific target device associated with the + command being reset, as well as suppressing error recovery entirely to avoid + perturbing an improperly functioning device. If the bus device reset error + recovery option is selected and sending a bus device reset does not restore + correct operation, the next command that is reset will force a full host + adapter hard reset and SCSI bus reset. SCSI bus resets caused by other + devices and detected by the host adapter are also handled by issuing a hard + reset to the host adapter and full reinitialization. These error recovery + options should improve overall system robustness by preventing individual + errant devices from causing the system as a whole to lock up or crash, and + thereby allowing a clean shutdown and restart after the offending component + is removed. + +o Extensive Testing + + This driver has undergone extensive testing and improvement over a period of + several months, and is routinely being used on heavily loaded systems. Over + 300 people retrieved the driver during the beta test period. In addition to + testing in normal system operation, error recovery tests have been performed + to verify proper system recovery in the case of simulated dropped interrupts, + external SCSI bus resets, and SCSI command errors due to bad CD-ROM media. + +o PCI Configuration Support + + On PCI systems running kernels compiled with PCI BIOS support enabled, this + driver will interrogate the PCI configuration space and use the I/O port + addresses assigned by the system BIOS, rather than the ISA compatible I/O + port addresses. + +o Shared Interrupts Support + + On systems that support shared interrupts, any number of BusLogic Host + Adapters may share the same interrupt request channel, and in fact it is more + efficient if they do so. The driver scans all known BusLogic Host Adapters + whenever an interrupt is handled on an interrupt channel assigned to any + BusLogic Host Adapter. + +o Wide SCSI Support + + All BusLogic MultiMaster SCSI Host Adapters share a common programming + interface, except for the inevitable improvements and extensions as new + models are released, so support for Wide SCSI data transfer has automatically + been available without explicit driver support. When used with Linux 1.3.x, + this driver adds explicit support for up to 15 target devices and 64 logical + units per target device, to fully exploit the capabilities of the newest + BusLogic Wide SCSI Host Adapters. + + + SUPPORTED HOST ADAPTERS + +The following list comprises the supported BusLogic SCSI Host Adapters as of +the date of this document. It is recommended that anyone purchasing a BusLogic +Host Adapter not in the following table contact the author beforehand to verify +that it is or will be supported. + +"C" Series Host Adapters: + +946C PCI Fast Single-ended SCSI-2 +956C PCI Fast/Wide Single-ended SCSI-2 +956CD PCI Fast/Wide Differential SCSI-2 +445C VLB Fast Single-ended SCSI-2 +747C EISA Fast Single-ended SCSI-2 +757C EISA Fast/Wide Single-ended SCSI-2 +757CD EISA Fast/Wide Differential SCSI-2 +545C ISA Fast Single-ended SCSI-2 +540CF ISA Fast Single-ended SCSI-2 + +"S" Series Host Adapters: + +445S VLB Fast Single-ended SCSI-2 +747S EISA Fast Single-ended SCSI-2 +747D EISA Fast Differential SCSI-2 +757S EISA Fast/Wide Single-ended SCSI-2 +757D EISA Fast/Wide Differential SCSI-2 +545S ISA Fast Single-ended SCSI-2 +542D ISA Fast Differential SCSI-2 +742A EISA Single-ended SCSI-2 (742A revision H) +542B ISA Single-ended SCSI-2 (542B revision H) + +"A" Series Host Adapters: + +742A EISA Single-ended SCSI-2 (742A revisions A - G) +542B ISA Single-ended SCSI-2 (542B revisions A - G) + +The FlashPoint LT, also known as the 930 Ultra, implements a different host +interface and is not supported by this driver. + +AMI FastDisk Host Adapters are true BusLogic clones and are supported by this +driver. + + + COMMAND LINE OPTIONS + +Many features of this driver are configurable by specification of appropriate +kernel command line options. A full description of the command line options +may be found in the comments before BusLogic_Setup in the kernel source code +file "BusLogic.c". The following examples may be useful as a starting point: + + "BusLogic=0x330" + + This command line limits probing to the single I/O port at 0x330. + + "BusLogic=0,1" + + This command line selects default probing and a concurrency of 1 which also + disables tagged queuing. It may be useful if problems arise during + installation on a system with a flakey SCSI configuration. In cases of a + marginal SCSI configuration it may also be beneficial to disable fast + transfers and/or synchronous negotiation using AutoSCSI on "C" series + boards. Disconnect/reconnect may also be disabled for fast devices such as + disk drives, but should not be disabled for tape drives or other devices + where a single command may take over a second to execute. + + "BusLogic=0,0,10" + + This command line selects default probing and concurrency but changes the + bus settle time to 10 seconds. It may be useful with SCSI devices that + take an unusually long time to become ready to accept commands after a SCSI + bus reset. + + "BusLogic=TQ:Disable" + + This command line selects default probing and disables tagged queuing, + while keeping the default concurrency. + + "BusLogic=0,15,TQ:N" + + This command line selects a concurrency of 15 and disables tagged queuing + for target 0, while allowing tagged queuing for all other target devices. + +Note that limiting the concurrency to 1 or disabling tagged queuing can +substantially impact performance. + + + INSTALLATION + +This distribution was prepared for Linux kernel version 1.3.41. Installation +in later versions will probably be successful as well, though BusLogic.patch +may not be required once this driver becomes part of the standard development +kernel; installation in earlier versions should not be attempted as 1.3.41 +contains changes I made to the common code that are essential for correct error +recovery. + +To install the BusLogic SCSI driver, you may use the following commands, +replacing "/usr/src" with wherever you keep your Linux kernel source tree: + + cd /usr/src + tar -xvzf BusLogic-1.3.0.tar.gz + mv README.BusLogic BusLogic.[ch] linux/drivers/scsi + patch -p < BusLogic.patch + cd linux + make config + make depend + make zImage + +Then install "arch/i386/boot/zImage" as your standard kernel, run lilo if +appropriate, and reboot. + +Be sure to answer "y" to the "BusLogic SCSI support" query during the "make +config" step. If your system was already configured for the old BusLogic +driver, you may omit the "make config" step above. diff --git a/drivers/scsi/buslogic.c b/drivers/scsi/buslogic.c deleted file mode 100644 index 36027823f36d..000000000000 --- a/drivers/scsi/buslogic.c +++ /dev/null @@ -1,1574 +0,0 @@ -/* - * buslogic.c Copyright (C) 1993, 1994 David B. Gentzel - * Low-level scsi driver for BusLogic adapters - * by David B. Gentzel, Whitfield Software Services, Carnegie, PA - * (gentzel@nova.enet.dec.com) - * Thanks to BusLogic for providing the necessary documentation - * - * The original version of this driver was derived from aha1542.[ch], - * which is Copyright (C) 1992 Tommy Thorn. Much has been reworked, but - * most of basic structure and substantial chunks of code still remain. - * - * Furthermore, many subsequent fixes and improvements to the aha1542 - * driver have been folded back into this driver. These changes to - * aha1542.[ch] are Copyright (C) 1993, 1994 Eric Youngdale. - * - * Thanks to the following individuals who have made contributions (of - * (code, information, support, or testing) to this driver: - * Eric Youngdale Leonard Zubkoff - * Tomas Hurka Andrew Walker - */ - -/* - * TODO: - * 1. Clean up error handling & reporting. - * 2. Find out why scatter/gather is limited to 16 requests per command. - * 3. Test/improve/fix abort & reset functions. - * 4. Look at command linking. - * 5. Allow multiple boards to share an IRQ if the bus allows (EISA, MCA, - * and PCI). - * 6. Avoid using the 445S workaround for board revs >= D. - */ - -/* - * NOTES: - * BusLogic (formerly BusTek) manufactures an extensive family of - * intelligent, high performance SCSI-2 host adapters. They all support - * command queueing and scatter/gather I/O. Most importantly, they all - * support identical programming interfaces, so a single driver can be used - * for all boards. - * - * Actually, they all support TWO identical programming interfaces! They - * have an Adaptec 154x compatible interface (complete with 24 bit - * addresses) as well as a "native" 32 bit interface. As such, the Linux - * aha1542 driver can be used to drive them, but with less than optimal - * performance (at least for the EISA, VESA, and MCA boards). - * - * Here is the scoop on the various models: - * BT-542B - ISA first-party DMA with floppy support. - * BT-545S - 542B + FAST SCSI and active termination. - * BT-545D - 545S + differential termination. - * BT-640A - MCA bus-master with floppy support. - * BT-646S - 640A + FAST SCSI and active termination. - * BT-646D - 646S + differential termination. - * BT-742A - EISA bus-master with floppy support. - * BT-747S - 742A + FAST SCSI, active termination, and 2.88M floppy. - * BT-747D - 747S + differential termination. - * BT-757S - 747S + WIDE SCSI. - * BT-757D - 747D + WIDE SCSI. - * BT-445S - VESA bus-master FAST SCSI with active termination - * and floppy support. - * BT-445C - 445S + enhanced BIOS & firmware options. - * BT-946C - PCI bus-master FAST SCSI. - * BT-956C - PCI bus-master FAST/WIDE SCSI. - * - * ??? I believe other boards besides the 445 now have a "C" model, but I - * have no facts on them. - * - * This driver SHOULD support all of these boards. It has only been tested - * with a 747S, 445S, 946C, and 956C; there is no PCI-specific support as - * yet. - * - * Should you require further information on any of these boards, BusLogic - * can be reached at (408)492-9090. Their BBS # is (408)492-1984 (maybe BBS - * stands for "Big Brother System"?). - * - * Places flagged with a triple question-mark are things which are either - * unfinished, questionable, or wrong. - */ - -#ifdef MODULE -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include "scsi.h" -#include "hosts.h" -#include "sd.h" -#define BUSLOGIC_PRIVATE_H /* Get the "private" stuff */ -#include "buslogic.h" - -#ifndef BUSLOGIC_DEBUG -# define BUSLOGIC_DEBUG 0 -#endif - -#include - -struct proc_dir_entry proc_scsi_buslogic = { - PROC_SCSI_BUSLOGIC, 8, "buslogic", - S_IFDIR | S_IRUGO | S_IXUGO, 2 -}; - -/* ??? Until kmalloc actually implements GFP_DMA, we can't depend on it... */ -#undef GFP_DMA - -/* If different port addresses are needed (e.g. to install more than two - cards), you must define BUSLOGIC_PORT_OVERRIDE to be a comma-separated list - of the addresses which will be checked. This can also be used to resolve a - conflict if the port-probing at a standard port causes problems with - another board. */ -/* #define BUSLOGIC_PORT_OVERRIDE 0x330, 0x334, 0x130, 0x134, 0x230, 0x234 */ - -/* Define this to be either BIOS_TRANSLATION_DEFAULT or BIOS_TRANSLATION_BIG - if you wish to bypass the test for this, which uses an undocumented port. - The test is believed to fail on at least some AMI BusLogic clones. */ -/* #define BIOS_TRANSLATION_OVERRIDE BIOS_TRANSLATION_BIG */ - -#define BUSLOGIC_VERSION "1.15" - -/* Not a random value - if this is too large, the system hangs for a long time - waiting for something to happen if a board is not installed. */ -/* ??? I don't really like this as it will wait longer on slow machines. - Perhaps we should base this on the loops_per_second "Bogomips" value? */ -#define WAITNEXTTIMEOUT 3000000 - -/* This is for the scsi_malloc call in buslogic_queuecommand. */ -/* ??? I'd up this to 4096, but would we be in danger of using up the - scsi_malloc memory pool? */ -/* This could be a concern, I guess. It may be possible to fix things so that - the table generated in sd.c is compatible with the low-level code, but - don't hold your breath. -ERY */ -#define BUSLOGIC_SG_MALLOC 512 - -/* Since the SG list is malloced, we have to limit the length. */ -#define BUSLOGIC_MAX_SG (BUSLOGIC_SG_MALLOC / sizeof (struct chain)) - -/* Since the host adapters have room to buffer 32 commands internally, there - is some virtue in setting BUSLOGIC_MAILBOXES to 32. The maximum value - appears to be 255, since the Count parameter to the Initialize Extended - Mailbox command is limited to one byte. */ -#define BUSLOGIC_MAILBOXES 32 - -#define BUSLOGIC_CMDLUN 4 /* Arbitrary, but seems to work well. */ - -/* BusLogic boards can be configured for quite a number of port addresses (six - to be exact), but I generally do not want the driver poking around at - random. We allow two port addresses - this allows people to use a BusLogic - with a MIDI card, which frequently also uses 0x330. - - This can also be overridden on the command line to the kernel, via LILO or - LOADLIN. */ -static unsigned short bases[7] = { -#ifdef BUSLOGIC_PORT_OVERRIDE - BUSLOGIC_PORT_OVERRIDE, -#else - 0x330, 0x334, /* 0x130, 0x134, 0x230, 0x234, */ -#endif - 0 -}; - -#define BIOS_TRANSLATION_DEFAULT 0 /* Default case */ -#define BIOS_TRANSLATION_BIG 1 /* Big disk (> 1G) case */ - -struct hostdata { - unsigned int bus_type; - unsigned int bios_translation: 1; /* BIOS mapping (for compatibility) */ - int last_mbi_used; - int last_mbo_used; - char model[7]; - char firmware_rev[6]; - Scsi_Cmnd *sc[BUSLOGIC_MAILBOXES]; - struct mailbox mb[2 * BUSLOGIC_MAILBOXES]; - struct ccb ccbs[BUSLOGIC_MAILBOXES]; -}; - -#define HOSTDATA(host) ((struct hostdata *)&(host)->hostdata) - -/* One for each IRQ level (9-15), although 13 will never be used. */ -static struct Scsi_Host *host[7] = { NULL, }; - -static int setup_mailboxes(unsigned int base, struct Scsi_Host *shpnt); -static int restart(struct Scsi_Host *shpnt); - -#define INTR_RESET(base) outb(RINT, CONTROL(base)) - -#define buslogic_printk buslogic_prefix(__PRETTY_FUNCTION__),printk - -#if defined(MODULE) && !defined(GFP_DMA) -# define CHECK_DMA_ADDR(isa, addr, badstmt) \ - do { if ((isa) && ((const void *)addr) > (const void *)ISA_DMA_THRESHOLD) badstmt; } while (0) -#else -# define CHECK_DMA_ADDR(isa, addr, badstmt) -#endif - -#define CHECK(cond) if (cond) ; else goto fail - -#define WAIT(port, allof, noneof) \ - CHECK(wait(port, allof, noneof, WAITNEXTTIMEOUT, FALSE)) -#define WAIT_WHILE(port, mask) WAIT(port, 0, mask) -#define WAIT_UNTIL(port, mask) WAIT(port, mask, 0) -#define WAIT_FAST(port, allof, noneof) \ - CHECK(wait(port, allof, noneof, 100, TRUE)) -#define WAIT_WHILE_FAST(port, mask) WAIT_FAST(port, 0, mask) -#define WAIT_UNTIL_FAST(port, mask) WAIT_FAST(port, mask, 0) - -/* If delay != 0, we use the udelay call to regulate the amount of time we - wait. - - This is inline as it is always called with constant arguments and hence - will be very well optimized. */ -static __inline__ int wait(unsigned short port, - unsigned char allof, unsigned char noneof, - unsigned int timeout, int delay) -{ - int bits; - - for (;;) { - bits = inb(port); - if ((bits & allof) == allof && (bits & noneof) == 0) - return TRUE; - if (delay) - udelay(1000); - if (--timeout == 0) - return FALSE; - } -} - -static void buslogic_prefix(const char *func) -{ - printk("BusLogic SCSI: %s: ", func); -} - -static void buslogic_stat(unsigned int base) -{ - int s = inb(STATUS(base)), i = inb(INTERRUPT(base)); - - buslogic_printk("status=%02X intrflags=%02X\n", s, i); -} - -/* This is a bit complicated, but we need to make sure that an interrupt - routine does not send something out while we are in the middle of this. - Fortunately, it is only at boot time that multi-byte messages are ever - sent. */ -static int buslogic_out(unsigned int base, const unsigned char *cmdp, - size_t len) -{ - unsigned long flags = 0; - - if (len == 1) { - for (;;) { - WAIT_WHILE(STATUS(base), CPRBSY); - save_flags(flags); - cli(); - if (!(inb(STATUS(base)) & CPRBSY)) { - outb(*cmdp, COMMAND_PARAMETER(base)); - restore_flags(flags); - return FALSE; - } - restore_flags(flags); - } - } else { - save_flags(flags); - cli(); - while (len--) { - WAIT_WHILE(STATUS(base), CPRBSY); - outb(*cmdp++, COMMAND_PARAMETER(base)); - } - restore_flags(flags); - } - return FALSE; - fail: - restore_flags(flags); - buslogic_printk("failed(%u): ", len + 1); - buslogic_stat(base); - return TRUE; -} - -/* Only used at boot time, so we do not need to worry about latency as much - here. This waits a very short period of time. We use this if we are not - sure whether the board will respond to the command we just sent. */ -static int buslogic_in(unsigned int base, unsigned char *cmdp, size_t len) -{ - unsigned long flags; - - save_flags(flags); - cli(); - while (len--) { - WAIT_UNTIL_FAST(STATUS(base), DIRRDY); - *cmdp++ = inb(DATA_IN(base)); - } - restore_flags(flags); - return FALSE; - fail: - restore_flags(flags); -#if (BUSLOGIC_DEBUG & BD_IO) - buslogic_printk("failed(%u): ", len + 1); - buslogic_stat(base); -#endif - return TRUE; -} - -static unsigned int makecode(unsigned int haerr, unsigned int scsierr) -{ - unsigned int hosterr; - const char *errstr = NULL; -#if (BUSLOGIC_DEBUG & BD_ERRORS) && defined(CONFIG_SCSI_CONSTANTS) - static const char *const buslogic_status[] = { - /* 00 */ "Command completed normally", - /* 01-07 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, - /* 08-09 */ NULL, NULL, - /* 0A */ "Linked command completed normally", - /* 0B */ "Linked command completed normally, interrupt generated", - /* 0C-0F */ NULL, NULL, NULL, NULL, - /* 10 */ NULL, - /* 11 */ "Selection timed out", - /* 12 */ "Data overrun/underrun", - /* 13 */ "Unexpected bus free", - /* 14 */ "Target bus phase sequence failure", - /* 15 */ "First byte of outgoing MB was invalid", - /* 16 */ "Invalid CCB Operation Code", - /* 17 */ "Linked CCB does not have the same LUN", - /* 18 */ "Invalid Target Direction received from Host", - /* 19 */ "Duplicate CCB Received in Target Mode", - /* 1A */ "Invalid CCB or Segment List Parameter", - /* 1B */ "Auto request sense failed", - /* 1C */ "SCSI-2 tagged queueing message was rejected by the target", - /* 1D-1F */ NULL, NULL, NULL, - /* 20 */ "Host adapter hardware failure", - /* 21 */ "Target did not respond to SCSI ATN and the HA SCSI bus reset", - /* 22 */ "Host adapter asserted a SCSI bus reset", - /* 23 */ "Other SCSI devices asserted a SCSI bus reset", - }; -#endif - - switch (haerr) { - case 0x00: /* Normal completion. */ - case 0x0A: /* Linked command complete without error and linked - normally. */ - case 0x0B: /* Linked command complete without error, interrupt - generated. */ - hosterr = DID_OK; - break; - - case 0x11: /* Selection time out: the initiator selection or - target reselection was not complete within the SCSI - time out period. */ - hosterr = DID_TIME_OUT; - break; - - case 0x14: /* Target bus phase sequence failure - An invalid bus - phase or bus phase sequence was requested by the - target. The host adapter will generate a SCSI - Reset Condition, notifying the host with a RSTS - interrupt. */ - case 0x21: /* The target did not respond to SCSI ATN and the host - adapter consequently issued a SCSI bus reset to - clear up the failure. */ - case 0x22: /* The host adapter asserted a SCSI bus reset. */ - hosterr = DID_RESET; - break; - - case 0x12: /* Data overrun/underrun: the target attempted to - transfer more data than was allocated by the Data - Length field or the sum of the Scatter/Gather Data - Length fields. */ - case 0x13: /* Unexpected bus free - The target dropped the SCSI - BSY at an unexpected time. */ - case 0x15: /* MBO command was not 00, 01, or 02 - The first byte - of the MB was invalid. This usually indicates a - software failure. */ - case 0x16: /* Invalid CCB Operation Code - The first byte of the - CCB was invalid. This usually indicates a software - failure. */ - case 0x17: /* Linked CCB does not have the same LUN - A - subsequent CCB of a set of linked CCB's does not - specify the same logical unit number as the - first. */ - case 0x18: /* Invalid Target Direction received from Host - The - direction of a Target Mode CCB was invalid. */ - case 0x19: /* Duplicate CCB Received in Target Mode - More than - once CCB was received to service data transfer - between the same target LUN and initiator SCSI ID - in the same direction. */ - case 0x1A: /* Invalid CCB or Segment List Parameter - A segment - list with a zero length segment or invalid segment - list boundaries was received. A CCB parameter was - invalid. */ - case 0x1B: /* Auto request sense failed. */ - case 0x1C: /* SCSI-2 tagged queueing message was rejected by the - target. */ - case 0x20: /* The host adapter hardware failed. */ - case 0x23: /* Other SCSI devices asserted a SCSI bus reset. */ - hosterr = DID_ERROR; /* ??? Couldn't find any better. */ - break; - - default: -#ifndef CONFIG_SCSI_CONSTANTS - errstr = "unknown hoststatus"; -#endif - hosterr = DID_ERROR; - break; - } -#if (BUSLOGIC_DEBUG & BD_ERRORS) -# ifdef CONFIG_SCSI_CONSTANTS - if (hosterr != DID_OK) { - if (haerr < ARRAY_SIZE(buslogic_status)) - errstr = buslogic_status[haerr]; - if (errstr == NULL) - errstr = "unknown hoststatus"; - } -# else - if (hosterr == DID_ERROR) - errstr = ""; -# endif -#endif - if (errstr != NULL) - buslogic_printk("%s (%02X)\n", errstr, haerr); - return (hosterr << 16) | scsierr; -} - -/* ??? this should really be "const struct Scsi_Host *" */ -const char *buslogic_info(struct Scsi_Host *shpnt) -{ - return "BusLogic SCSI driver " BUSLOGIC_VERSION; -} - -/* - This is a major rewrite of the interrupt handler to support the newer - and faster PCI cards. While the previous interrupt handler was supposed - to handle multiple incoming becoming available mailboxes during the same - interrupt, my testing showed that in practice only a single mailbox was - ever made available. With the 946C and 956C, multiple incoming mailboxes - being ready for processing during a single interrupt occurs much more - frequently, and so care must be taken to avoid race conditions managing - the Host Adapter Interrupt Register, which can lead to lost interrupts. - - Leonard N. Zubkoff, 23-Mar-95 -*/ - -static void buslogic_interrupt(int irq, struct pt_regs * regs) -{ - int mbi, saved_mbo[BUSLOGIC_MAILBOXES]; - int base, interrupt_flags, found, i; - struct Scsi_Host *shpnt; - Scsi_Cmnd *sctmp; - struct mailbox *mb; - struct ccb *ccb; - - shpnt = host[irq - 9]; - if (shpnt == NULL) - panic("buslogic_interrupt: NULL SCSI host entry"); - - mb = HOSTDATA(shpnt)->mb; - ccb = HOSTDATA(shpnt)->ccbs; - base = shpnt->io_port; - - /* - This interrupt handler is now specified to use the SA_INTERRUPT - protocol, so interrupts are inhibited on entry until explicitly - allowed again. Read the Host Adapter Interrupt Register, and - complain if there is no pending interrupt being signaled. - */ - - interrupt_flags = inb(INTERRUPT(base)); - - if (!(interrupt_flags & INTV)) - buslogic_printk("interrupt received, but INTV not set\n"); - - /* - Reset the Host Adapter Interrupt Register. It appears to be - important that this is only done once per interrupt to avoid - losing interrupts under heavy loads. - */ - - INTR_RESET(base); - - if (interrupt_flags & RSTS) - { - restart(shpnt); - return; - } - - /* - With interrupts still inhibited, scan through the incoming mailboxes - in strict round robin fashion saving the status information and - then freeing the mailbox. A second pass over the completed commands - will be made separately to complete their processing. - */ - - mbi = HOSTDATA(shpnt)->last_mbi_used + 1; - if (mbi >= 2*BUSLOGIC_MAILBOXES) - mbi = BUSLOGIC_MAILBOXES; - - found = 0; - - while (mb[mbi].status != MBX_NOT_IN_USE && found < BUSLOGIC_MAILBOXES) - { - int mbo = (struct ccb *)mb[mbi].ccbptr - ccb; - - sctmp = HOSTDATA(shpnt)->sc[mbo]; - - /* - If sctmp has become NULL, higher level code must have aborted - this operation and called the necessary completion routine. - */ - - if (sctmp != NULL && mb[mbi].status != MBX_COMPLETION_NOT_FOUND) - { - int result = 0; - - saved_mbo[found++] = mbo; - - if (mb[mbi].status != MBX_COMPLETION_OK) - result = makecode(ccb[mbo].hastat, ccb[mbo].tarstat); - - sctmp->result = result; - - mb[mbi].status = MBX_NOT_IN_USE; - } - - HOSTDATA(shpnt)->last_mbi_used = mbi; - - if (++mbi >= 2*BUSLOGIC_MAILBOXES) - mbi = BUSLOGIC_MAILBOXES; - } - - /* - With interrupts no longer inhibited, iterate over the completed - commands freeing resources and calling the completion routines. - Since we exit upon completion of this loop, there is no need to - inhibit interrupts before exit, as this will be handled by the - fast interrupt assembly code we return to. - */ - - sti(); - - for (i = 0; i < found; i++) - { - int mbo = saved_mbo[i]; - sctmp = HOSTDATA(shpnt)->sc[mbo]; - if (sctmp == NULL) continue; - /* - First, free any storage allocated for a scatter/gather - data segment list. - */ - if (sctmp->host_scribble) - scsi_free(sctmp->host_scribble, BUSLOGIC_SG_MALLOC); - /* - Next, mark the SCSI Command as completed so it may be reused - for another command by buslogic_queuecommand. This also signals - to buslogic_reset that the command is no longer active. - */ - HOSTDATA(shpnt)->sc[mbo] = NULL; - /* - Finally, call the SCSI command completion handler. - */ - sctmp->scsi_done(sctmp); - } -} - - -/* ??? Why does queuecommand return a value? scsi.c never looks at it... */ -int buslogic_queuecommand(Scsi_Cmnd *scpnt, void (*done)(Scsi_Cmnd *)) -{ - static const unsigned char buscmd[] = { CMD_START_SCSI }; - unsigned char direction; - unsigned char *cmd = (unsigned char *)scpnt->cmnd; - unsigned char target = scpnt->target; - unsigned char lun = scpnt->lun; - void *buff = scpnt->request_buffer; - int bufflen = scpnt->request_bufflen; - int mbo; - unsigned long flags; - struct Scsi_Host *shpnt = scpnt->host; - struct mailbox *mb = HOSTDATA(shpnt)->mb; - struct ccb *ccb; - - -#if (BUSLOGIC_DEBUG & BD_COMMAND) - if (target > 1) { - scpnt->result = DID_TIME_OUT << 16; - done(scpnt); - return 0; - } -#endif - - if (*cmd == REQUEST_SENSE) { -#if (BUSLOGIC_DEBUG & (BD_COMMAND | BD_ERRORS)) - if (bufflen != sizeof scpnt->sense_buffer) { - buslogic_printk("wrong buffer length supplied for request sense" - " (%d).\n", - bufflen); - } -#endif - scpnt->result = 0; - done(scpnt); - return 0; - } - -#if (BUSLOGIC_DEBUG & BD_COMMAND) - { - int i; - - if (*cmd == READ_10 || *cmd == WRITE_10 - || *cmd == READ_6 || *cmd == WRITE_6) - i = *(int *)(cmd + 2); - else - i = -1; - buslogic_printk("dev %d cmd %02X pos %d len %d ", - target, *cmd, i, bufflen); - buslogic_stat(shpnt->io_port); - buslogic_printk("dumping scsi cmd:"); - for (i = 0; i < scpnt->cmd_len; i++) - printk(" %02X", cmd[i]); - printk("\n"); - if (*cmd == WRITE_10 || *cmd == WRITE_6) - return 0; /* we are still testing, so *don't* write */ - } -#endif - - /* Use the outgoing mailboxes in a round-robin fashion, because this - is how the host adapter will scan for them. */ - - save_flags(flags); - cli(); - - mbo = HOSTDATA(shpnt)->last_mbo_used + 1; - if (mbo >= BUSLOGIC_MAILBOXES) - mbo = 0; - - do { - if (mb[mbo].status == MBX_NOT_IN_USE - && HOSTDATA(shpnt)->sc[mbo] == NULL) - break; - mbo++; - if (mbo >= BUSLOGIC_MAILBOXES) - mbo = 0; - } while (mbo != HOSTDATA(shpnt)->last_mbo_used); - - if (mb[mbo].status != MBX_NOT_IN_USE || HOSTDATA(shpnt)->sc[mbo]) { - /* ??? Instead of failing, should we enable OMBR interrupts and sleep - until we get one? */ - restore_flags(flags); - buslogic_printk("unable to find empty mailbox.\n"); - goto fail; - } - - HOSTDATA(shpnt)->sc[mbo] = scpnt; /* This will effectively - prevent someone else from - screwing with this cdb. */ - - HOSTDATA(shpnt)->last_mbo_used = mbo; - - restore_flags(flags); - -#if (BUSLOGIC_DEBUG & BD_COMMAND) - buslogic_printk("sending command (%d %08X)...", mbo, done); -#endif - - ccb = &HOSTDATA(shpnt)->ccbs[mbo]; - - /* This gets trashed for some reason */ - mb[mbo].ccbptr = ccb; - - memset(ccb, 0, sizeof (struct ccb)); - - ccb->cdblen = scpnt->cmd_len; /* SCSI Command Descriptor - Block Length */ - - direction = 0; - if (*cmd == READ_10 || *cmd == READ_6) - direction = 8; - else if (*cmd == WRITE_10 || *cmd == WRITE_6) - direction = 16; - - memcpy(ccb->cdb, cmd, ccb->cdblen); - - if (scpnt->use_sg) { - struct scatterlist *sgpnt; - struct chain *cptr; - size_t i; - - ccb->op = CCB_OP_INIT_SG; /* SCSI Initiator Command - w/scatter-gather */ - scpnt->host_scribble - = (unsigned char *)scsi_malloc(BUSLOGIC_SG_MALLOC); - if (scpnt->host_scribble == NULL) { - buslogic_printk("unable to allocate DMA memory.\n"); - goto fail; - } - sgpnt = (struct scatterlist *)scpnt->request_buffer; - cptr = (struct chain *)scpnt->host_scribble; - if (scpnt->use_sg > shpnt->sg_tablesize) { - buslogic_printk("bad segment list, %d > %d.\n", - scpnt->use_sg, shpnt->sg_tablesize); - goto fail; - } - for (i = 0; i < scpnt->use_sg; i++) { - CHECK_DMA_ADDR(shpnt->unchecked_isa_dma, sgpnt[i].address, - goto baddma); - cptr[i].dataptr = sgpnt[i].address; - cptr[i].datalen = sgpnt[i].length; - } - ccb->datalen = scpnt->use_sg * sizeof (struct chain); - ccb->dataptr = cptr; -#if (BUSLOGIC_DEBUG & BD_COMMAND) - { - unsigned char *ptr; - - buslogic_printk("cptr %08X:", cptr); - ptr = (unsigned char *)cptr; - for (i = 0; i < 18; i++) - printk(" %02X", ptr[i]); - printk("\n"); - } -#endif - } else { - ccb->op = CCB_OP_INIT; /* SCSI Initiator Command */ - scpnt->host_scribble = NULL; - CHECK_DMA_ADDR(shpnt->unchecked_isa_dma, buff, goto baddma); - ccb->datalen = bufflen; - ccb->dataptr = buff; - } - ccb->id = target; - ccb->lun = lun; - ccb->dir = direction; - ccb->rsalen = sizeof scpnt->sense_buffer; - ccb->senseptr = scpnt->sense_buffer; - /* ccbcontrol, commlinkid, and linkptr are 0 due to above memset. */ - -#if (BUSLOGIC_DEBUG & BD_COMMAND) - { - size_t i; - - buslogic_printk("sending..."); - for (i = 0; i < sizeof(struct ccb) - 10; i++) - printk(" %02X", ((unsigned char *)ccb)[i]); - printk("\n"); - } -#endif - - if (done) { -#if (BUSLOGIC_DEBUG & BD_COMMAND) - buslogic_printk("now waiting for interrupt: "); - buslogic_stat(shpnt->io_port); -#endif - scpnt->scsi_done = done; - mb[mbo].status = MBX_ACTION_START; - /* start scsi command */ - buslogic_out(shpnt->io_port, buscmd, sizeof buscmd); -#if (BUSLOGIC_DEBUG & BD_COMMAND) - buslogic_stat(shpnt->io_port); -#endif - } else - buslogic_printk("done can't be NULL.\n"); - - while (0) { -#if defined(MODULE) && !defined(GFP_DMA) - baddma: - buslogic_printk("address > 16MB used for ISA HA.\n"); -#endif - fail: - scpnt->result = DID_ERROR << 16; - done(scpnt); - } - - return 0; -} - -#if 0 -static void internal_done(Scsi_Cmnd *scpnt) -{ - scpnt->SCp.Status++; -} - -int buslogic_command(Scsi_Cmnd *scpnt) -{ -#if (BUSLOGIC_DEBUG & BD_COMMAND) - buslogic_printk("calling buslogic_queuecommand.\n"); -#endif - - buslogic_queuecommand(scpnt, internal_done); - - scpnt->SCp.Status = 0; - while (!scpnt->SCp.Status) - barrier(); - return scpnt->result; -} -#endif - -/* Initialize mailboxes. */ -static int setup_mailboxes(unsigned int base, struct Scsi_Host *shpnt) -{ - size_t i; - int ok = FALSE; /* Innocent until proven guilty... */ - struct mailbox *mb = HOSTDATA(shpnt)->mb; - struct ccb *ccb = HOSTDATA(shpnt)->ccbs; - struct { - unsigned char cmd, count; - void *base PACKED; - } cmd = { CMD_INITEXTMB, BUSLOGIC_MAILBOXES, mb }; - - for (i = 0; i < BUSLOGIC_MAILBOXES; i++) { - mb[i].status = mb[BUSLOGIC_MAILBOXES + i].status = MBX_NOT_IN_USE; - mb[i].ccbptr = &ccb[i]; - } - INTR_RESET(base); /* reset interrupts, so they don't block */ - - if (buslogic_out(base, (unsigned char *)&cmd, sizeof cmd)) - goto fail; - WAIT_UNTIL(INTERRUPT(base), CMDC); - - ok = TRUE; - - while (0) { - fail: - buslogic_printk("failed setting up mailboxes.\n"); - } - - INTR_RESET(base); - - return !ok; -} - -static int getconfig(unsigned int base, unsigned char *irq, - unsigned char *dma, unsigned char *id, - char *bus_type, unsigned short *max_sg, - const unsigned char **bios) -{ - unsigned char inquiry_cmd[2]; - unsigned char inquiry_result[4]; - int i; - -#if (BUSLOGIC_DEBUG & BD_DETECT) - buslogic_printk("called\n"); -#endif - - i = inb(STATUS(base)); - if (i & DIRRDY) - i = inb(DATA_IN(base)); - inquiry_cmd[0] = CMD_RETCONF; - buslogic_out(base, inquiry_cmd, 1); - if (buslogic_in(base, inquiry_result, 3)) - goto fail; - WAIT_UNTIL_FAST(INTERRUPT(base), CMDC); - INTR_RESET(base); - /* Defer using the DMA value until we know the bus type. */ - *dma = inquiry_result[0]; - switch (inquiry_result[1]) { - case 0x01: - *irq = 9; - break; - case 0x02: - *irq = 10; - break; - case 0x04: - *irq = 11; - break; - case 0x08: - *irq = 12; - break; - case 0x20: - *irq = 14; - break; - case 0x40: - *irq = 15; - break; - default: - buslogic_printk("unable to determine BusLogic IRQ level, " - " disabling board.\n"); - goto fail; - } - *id = inquiry_result[2] & 0x7; - - /* I expected Adaptec boards to fail on this, but it doesn't happen... */ - inquiry_cmd[0] = CMD_INQEXTSETUP; - inquiry_cmd[1] = 4; - if (buslogic_out(base, inquiry_cmd, 2)) - goto fail; - if (buslogic_in(base, inquiry_result, inquiry_cmd[1])) - goto fail; - WAIT_UNTIL_FAST(INTERRUPT(base), CMDC); - if (inb(STATUS(base)) & CMDINV) - goto fail; - INTR_RESET(base); - - *bus_type = inquiry_result[0]; - CHECK(*bus_type == 'A' || *bus_type == 'E' || *bus_type == 'M'); - - *bios = (const unsigned char *)((unsigned int)inquiry_result[1] << 12); - - *max_sg = (inquiry_result[3] << 8) | inquiry_result[2]; - - /* We only need a DMA channel for ISA boards. Some other types of boards - (such as the 747S) have an option to report a DMA channel even though - none is used (for compatibility with Adaptec drivers which require a - DMA channel). We ignore this. */ - if (*bus_type == 'A') - switch (*dma) { - case 0: /* This indicates that no DMA channel is used. */ - *dma = 0; - break; - case 0x20: - *dma = 5; - break; - case 0x40: - *dma = 6; - break; - case 0x80: - *dma = 7; - break; - default: - buslogic_printk("unable to determine BusLogic DMA channel," - " disabling board.\n"); - goto fail; - } - else - *dma = 0; - - while (0) { - fail: -#if (BUSLOGIC_DEBUG & BD_DETECT) - buslogic_printk("query board settings\n"); -#endif - return TRUE; - } - - return FALSE; -} - -/* Query the board. This acts both as part of the detection sequence and as a - means to get necessary configuration information. */ -static int buslogic_query(unsigned int base, unsigned char *trans, - unsigned char *irq, unsigned char *dma, - unsigned char *id, char *bus_type, - unsigned short *max_sg, const unsigned char **bios, - char *model, char *firmware_rev) -{ - unsigned char inquiry_cmd[2]; - unsigned char inquiry_result[6]; - unsigned char geo; - unsigned int i; - -#if (BUSLOGIC_DEBUG & BD_DETECT) - buslogic_printk("called\n"); -#endif - - /* Quick and dirty test for presence of the card. */ - if (inb(STATUS(base)) == 0xFF) - goto fail; - - /* Check the GEOMETRY port early for quick bailout on Adaptec boards. */ - geo = inb(GEOMETRY(base)); -#if (BUSLOGIC_DEBUG & BD_DETECT) - buslogic_printk("geometry bits: %02X\n", geo); -#endif - /* Here is where we tell the men from the boys (i.e. Adaptec's don't - support the GEOMETRY port, the men do :-) */ - if (geo == 0xFF) - goto fail; - - /* In case some other card was probing here, reset interrupts. */ - INTR_RESET(base); - - /* Reset the adapter. I ought to make a hard reset, but it's not really - necessary. */ - outb(RSOFT | RINT/* | RSBUS*/, CONTROL(base)); - - /* Wait a little bit for things to settle down. */ - i = jiffies + 2; - while (i > jiffies); - - /* Expect INREQ and HARDY, any of the others are bad. */ - WAIT(STATUS(base), INREQ | HARDY, DACT | DFAIL | CMDINV | DIRRDY | CPRBSY); - - /* Shouldn't have generated any interrupts during reset. */ - if (inb(INTERRUPT(base)) & INTRMASK) - goto fail; - - /* Getting the BusLogic firmware revision level is a bit tricky. We get - the first two digits (d.d) from CMD_INQUIRY and then use two undocumented - commands to get the remaining digit and letter (d.ddl as in 3.31C). */ - - inquiry_cmd[0] = CMD_INQUIRY; - buslogic_out(base, inquiry_cmd, 1); - if (buslogic_in(base, inquiry_result, 4)) - goto fail; - /* Reading port should reset DIRRDY. */ - if (inb(STATUS(base)) & DIRRDY) - goto fail; - WAIT_UNTIL_FAST(INTERRUPT(base), CMDC); - INTR_RESET(base); - firmware_rev[0] = inquiry_result[2]; - firmware_rev[1] = '.'; - firmware_rev[2] = inquiry_result[3]; - firmware_rev[3] = '\0'; -#if 0 - buslogic_printk("inquiry bytes: %02X(%c) %02X(%c)\n", - inquiry_result[0], inquiry_result[0], - inquiry_result[1], inquiry_result[1]); -#endif - - if (getconfig(base, irq, dma, id, bus_type, max_sg, bios)) - goto fail; - - /* Set up defaults */ -#ifdef BIOS_TRANSLATION_OVERRIDE - *trans = BIOS_TRANSLATION_OVERRIDE; -#else - *trans = BIOS_TRANSLATION_DEFAULT; -#endif - model[0] = '\0'; - model[6] = 0; - - /* ??? Begin undocumented command use. - These may not be supported by clones. */ - - do { - /* ??? It appears as though AMI BusLogic clones don't implement this - feature. As an experiment, if we read a 00 we ignore the GEO_GT_1GB - bit and skip all further undocumented commands. */ - if (geo == 0x00) - break; -#ifndef BIOS_TRANSLATION_OVERRIDE - *trans = ((geo & GEO_GT_1GB) - ? BIOS_TRANSLATION_BIG : BIOS_TRANSLATION_DEFAULT); -#endif - - inquiry_cmd[0] = CMD_VER_NO_LAST; - buslogic_out(base, inquiry_cmd, 1); - if (buslogic_in(base, inquiry_result, 1)) - break; - WAIT_UNTIL_FAST(INTERRUPT(base), CMDC); - INTR_RESET(base); - firmware_rev[3] = inquiry_result[0]; - firmware_rev[4] = '\0'; - - inquiry_cmd[0] = CMD_VER_NO_LETTER; - buslogic_out(base, inquiry_cmd, 1); - if (buslogic_in(base, inquiry_result, 1)) - break; - WAIT_UNTIL_FAST(INTERRUPT(base), CMDC); - INTR_RESET(base); - firmware_rev[4] = inquiry_result[0]; - firmware_rev[5] = '\0'; - - /* Use undocumented command to get model number and revision. */ - - inquiry_cmd[0] = CMD_RET_MODEL_NO; - inquiry_cmd[1] = 6; - buslogic_out(base, inquiry_cmd, 2); - if (buslogic_in(base, inquiry_result, inquiry_cmd[1])) - break; - WAIT_UNTIL_FAST(INTERRUPT(base), CMDC); - INTR_RESET(base); - memcpy(model, inquiry_result, 5); - model[5] = '\0'; - model[6] = inquiry_result[5]; - } while (0); - - /* ??? End undocumented command use. */ - - /* bus_type from getconfig doesn't differentiate between EISA/VESA. We - override using the model number here. */ - switch (*bus_type) { - case 'E': - switch (model[0]) { - case '4': - *bus_type = 'V'; - break; - case '9': - *bus_type = 'P'; - break; - case '7': - break; - default: - *bus_type = 'X'; - break; - } - break; - default: - break; - } - - while (0) { - fail: -#if (BUSLOGIC_DEBUG & BD_DETECT) - buslogic_printk("query board settings\n"); -#endif - return TRUE; - } - - return FALSE; -} - -/* return non-zero on detection */ -int buslogic_detect(Scsi_Host_Template *tpnt) -{ - unsigned char dma; - unsigned char irq; - unsigned int base; - unsigned char id; - char bus_type; - unsigned short max_sg; - unsigned char bios_translation; - unsigned long flags; - const unsigned char *bios; - char *model; - char *firmware_rev; - struct Scsi_Host *shpnt; - size_t indx; - int unchecked_isa_dma; - int count = 0; - -#if (BUSLOGIC_DEBUG & BD_DETECT) - buslogic_printk("called\n"); -#endif - - tpnt->proc_dir = &proc_scsi_buslogic; - tpnt->can_queue = BUSLOGIC_MAILBOXES; - for (indx = 0; bases[indx] != 0; indx++) - if (!check_region(bases[indx], 4)) { - shpnt = scsi_register(tpnt, sizeof (struct hostdata)); - - base = bases[indx]; - - model = HOSTDATA(shpnt)->model; - firmware_rev = HOSTDATA(shpnt)->firmware_rev; - if (buslogic_query(base, &bios_translation, &irq, &dma, &id, - &bus_type, &max_sg, &bios, model, firmware_rev)) - goto unregister; - -#if (BUSLOGIC_DEBUG & BD_DETECT) - buslogic_stat(base); -#endif - - /* Only type 'A' (AT/ISA) bus adapters use unchecked DMA. */ - unchecked_isa_dma = (bus_type == 'A'); -#ifndef CONFIG_NO_BUGGY_BUSLOGIC - /* There is a hardware bug in the BT-445S prior to revision D. - When the BIOS is enabled and you have more than 16MB of memory, - the card mishandles memory transfers over 16MB which (if viewed - as a 24-bit address) overlap with the BIOS address space. For - example if you have the BIOS located at physical address - 0xDC000 and a DMA transfer from the card to RAM starts at - physical address 0x10DC000 then the transfer is messed up. To - be more precise every fourth byte of the transfer is messed up. - (This analysis courtesy of Tomas Hurka, author of the NeXTSTEP - BusLogic driver.) */ - - if (bus_type == 'V' /* 445 */ - && firmware_rev[0] <= '3' /* S */ - && bios != NULL) { /* BIOS enabled */ -#if 1 - /* Now that LNZ's forbidden_addr stuff is in the higher level - scsi code, we can use this instead. */ - /* Avoid addresses which "mirror" the BIOS for DMA. */ - shpnt->forbidden_addr = (unsigned long)bios; - shpnt->forbidden_size = 16 * 1024; -#else - /* Use double-buffering. */ - unchecked_isa_dma = TRUE; -#endif - } -#endif - - CHECK_DMA_ADDR(unchecked_isa_dma, shpnt, goto unregister); - - if (setup_mailboxes(base, shpnt)) - goto unregister; - - /* Set the Bus on/off-times as not to ruin floppy performance. - CMD_BUSOFF_TIME is a noop for EISA boards (and possibly - others???). */ - if (bus_type != 'E' && bus_type != 'P') { - /* The default ON/OFF times for BusLogic adapters is 7/4. */ - static const unsigned char oncmd[] = { CMD_BUSON_TIME, 7 }; - static const unsigned char offcmd[] = { CMD_BUSOFF_TIME, 5 }; - - INTR_RESET(base); - buslogic_out(base, oncmd, sizeof oncmd); - WAIT_UNTIL(INTERRUPT(base), CMDC); - INTR_RESET(base); - buslogic_out(base, offcmd, sizeof offcmd); - WAIT_UNTIL(INTERRUPT(base), CMDC); - while (0) { - fail: - buslogic_printk("setting bus on/off-time failed.\n"); - } - INTR_RESET(base); - } - - buslogic_printk("configuring %s HA at port 0x%03X, IRQ %u", - (bus_type == 'A' ? "ISA" - : (bus_type == 'E' ? "EISA" - : (bus_type == 'M' ? "MCA" - : (bus_type == 'P' ? "PCI" - : (bus_type == 'V' ? "VESA" - : (bus_type == 'X' ? "EISA/VESA/PCI" - : "Unknown")))))), - base, irq); - if (bios != NULL) - printk(", BIOS 0x%05X", (unsigned int)bios); - if (dma != 0) - printk(", DMA %u", dma); - printk(", ID %u\n", id); - buslogic_printk("Model Number: %s", - (model[0] ? model : "Unknown")); - if (model[0]) - printk(" (revision %d)", model[6]); - printk("\n"); - buslogic_printk("firmware revision: %s\n", firmware_rev); - -#if (BUSLOGIC_DEBUG & BD_DETECT) - buslogic_stat(base); -#endif - -#if (BUSLOGIC_DEBUG & BD_DETECT) - buslogic_printk("enable interrupt channel %d.\n", irq); -#endif - - save_flags(flags); - cli(); - if (request_irq(irq, buslogic_interrupt, - SA_INTERRUPT, "buslogic")) { - buslogic_printk("unable to allocate IRQ for " - "BusLogic controller.\n"); - restore_flags(flags); - goto unregister; - } - - if (dma) { - if (request_dma(dma, "buslogic")) { - buslogic_printk("unable to allocate DMA channel for " - "BusLogic controller.\n"); - free_irq(irq); - restore_flags(flags); - goto unregister; - } - - /* The DMA-Controller. We need to fool with this because we - want to be able to use an ISA BusLogic without having to - have the BIOS enabled. */ - set_dma_mode(dma, DMA_MODE_CASCADE); - enable_dma(dma); - } - - host[irq - 9] = shpnt; - shpnt->this_id = id; - shpnt->unchecked_isa_dma = unchecked_isa_dma; - /* Have to keep cmd_per_lun at 1 for ISA machines otherwise lots - of memory gets sucked up for bounce buffers. */ - shpnt->cmd_per_lun = (unchecked_isa_dma ? 1 : BUSLOGIC_CMDLUN); - shpnt->sg_tablesize = max_sg; - if (shpnt->sg_tablesize > BUSLOGIC_MAX_SG) - shpnt->sg_tablesize = BUSLOGIC_MAX_SG; - /* ??? shpnt->base should really be "const unsigned char *"... */ - shpnt->base = (unsigned char *)bios; - shpnt->io_port = base; - shpnt->n_io_port = 4; /* Number of bytes of I/O space used */ - shpnt->dma_channel = dma; - shpnt->irq = irq; - HOSTDATA(shpnt)->bios_translation = bios_translation; - if (bios_translation == BIOS_TRANSLATION_BIG) - buslogic_printk("using extended bios translation.\n"); - HOSTDATA(shpnt)->last_mbi_used = 2 * BUSLOGIC_MAILBOXES - 1; - HOSTDATA(shpnt)->last_mbo_used = BUSLOGIC_MAILBOXES - 1; - memset(HOSTDATA(shpnt)->sc, 0, sizeof HOSTDATA(shpnt)->sc); - restore_flags(flags); - -#if 0 - { - unsigned char buf[8]; - unsigned char cmd[] - = { READ_CAPACITY, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - size_t i; - -#if (BUSLOGIC_DEBUG & BD_DETECT) - buslogic_printk("*** READ CAPACITY ***\n"); -#endif - for (i = 0; i < sizeof buf; i++) - buf[i] = 0x87; - for (i = 0; i < 2; i++) - if (!buslogic_command(i, cmd, buf, sizeof buf)) { - buslogic_printk("LU %u sector_size %d device_size %d\n", - i, *(int *)(buf + 4), *(int *)buf); - } - -#if (BUSLOGIC_DEBUG & BD_DETECT) - buslogic_printk("*** NOW RUNNING MY OWN TEST ***\n"); -#endif - for (i = 0; i < 4; i++) { - static buffer[512]; - - cmd[0] = READ_10; - cmd[1] = 0; - xany2scsi(cmd + 2, i); - cmd[6] = 0; - cmd[7] = 0; - cmd[8] = 1; - cmd[9] = 0; - buslogic_command(0, cmd, buffer, sizeof buffer); - } - } -#endif - - request_region(bases[indx], 4,"buslogic"); - /* Register the IO ports that we use */ - count++; - continue; - unregister: - scsi_unregister(shpnt); - } - return count; -} - -static int restart(struct Scsi_Host *shpnt) -{ - unsigned int i; - unsigned int count = 0; -#if 0 - static const unsigned char buscmd[] = { CMD_START_SCSI }; -#endif - - for (i = 0; i < BUSLOGIC_MAILBOXES; i++) - if (HOSTDATA(shpnt)->sc[i] - && !HOSTDATA(shpnt)->sc[i]->device->soft_reset) { -#if 0 - HOSTDATA(shpnt)->mb[i].status - = MBX_ACTION_START; /* Indicate ready to restart... */ -#endif - count++; - } - - buslogic_printk("potential to restart %d stalled commands...\n", count); -#if 0 - /* start scsi command */ - if (count) - buslogic_out(shpnt->host->io_port, buscmd, sizeof buscmd); -#endif - return 0; -} - -/* ??? The abort command for the aha1542 does not leave the device in a clean - state where it is available to be used again. As it is not clear whether - the same problem exists with BusLogic boards, we will enable this and see - if it works. */ -int buslogic_abort(Scsi_Cmnd *scpnt) -{ -#if 1 - static const unsigned char buscmd[] = { CMD_START_SCSI }; - struct mailbox *mb; - int mbi, mbo, last_mbi; - unsigned long flags; - unsigned int i; - - buslogic_printk("%X %X\n", - inb(STATUS(scpnt->host->io_port)), - inb(INTERRUPT(scpnt->host->io_port))); - - save_flags(flags); - cli(); - mb = HOSTDATA(scpnt->host)->mb; - last_mbi = HOSTDATA(scpnt->host)->last_mbi_used; - mbi = last_mbi + 1; - if (mbi >= 2 * BUSLOGIC_MAILBOXES) - mbi = BUSLOGIC_MAILBOXES; - - do { - if (mb[mbi].status != MBX_NOT_IN_USE) - break; - last_mbi = mbi; - mbi++; - if (mbi >= 2 * BUSLOGIC_MAILBOXES) - mbi = BUSLOGIC_MAILBOXES; - } while (mbi != HOSTDATA(scpnt->host)->last_mbi_used); - - if (mb[mbi].status != MBX_NOT_IN_USE) { - buslogic_printk("lost interrupt discovered on irq %d, " - " - attempting to recover...\n", - scpnt->host->irq); - HOSTDATA(scpnt->host)->last_mbi_used = last_mbi; - buslogic_interrupt(scpnt->host->irq, NULL); - restore_flags(flags); - return SCSI_ABORT_SUCCESS; - } - restore_flags(flags); - - /* OK, no lost interrupt. Try looking to see how many pending commands we - think we have. */ - for (i = 0; i < BUSLOGIC_MAILBOXES; i++) - if (HOSTDATA(scpnt->host)->sc[i]) { - if (HOSTDATA(scpnt->host)->sc[i] == scpnt) { - buslogic_printk("timed out command pending for %s.\n", - kdevname(scpnt->request.rq_dev)); - if (HOSTDATA(scpnt->host)->mb[i].status != MBX_NOT_IN_USE) { - buslogic_printk("OGMB still full - restarting...\n"); - buslogic_out(scpnt->host->io_port, buscmd, sizeof buscmd); - } - } else - buslogic_printk("other pending command: %s\n", - kdevname(scpnt->request.rq_dev)); - } -#endif - -#if (BUSLOGIC_DEBUG & BD_ABORT) - buslogic_printk("called\n"); -#endif - -#if 1 - /* This section of code should be used carefully - some devices cannot - abort a command, and this merely makes it worse. */ - save_flags(flags); - cli(); - for (mbo = 0; mbo < BUSLOGIC_MAILBOXES; mbo++) - if (scpnt == HOSTDATA(scpnt->host)->sc[mbo]) { - mb[mbo].status = MBX_ACTION_ABORT; - buslogic_out(scpnt->host->io_port, buscmd, sizeof buscmd); - break; - } - restore_flags(flags); -#endif - - return SCSI_ABORT_SNOOZE; -} - -/* We do not implement a reset function here, but the upper level code assumes - that it will get some kind of response for the command in scpnt. We must - oblige, or the command will hang the SCSI system. For a first go, we assume - that the BusLogic notifies us with all of the pending commands (it does - implement soft reset, after all). */ -int buslogic_reset(Scsi_Cmnd *scpnt) -{ - static const unsigned char buscmd[] = { CMD_START_SCSI }; - unsigned int i; - -#if (BUSLOGIC_DEBUG & BD_RESET) - buslogic_printk("called\n"); -#endif -#if 0 - /* This does a scsi reset for all devices on the bus. */ - outb(RSBUS, CONTROL(scpnt->host->io_port)); -#else - /* This does a selective reset of just the one device. */ - /* First locate the ccb for this command. */ - for (i = 0; i < BUSLOGIC_MAILBOXES; i++) - if (HOSTDATA(scpnt->host)->sc[i] == scpnt) { - HOSTDATA(scpnt->host)->ccbs[i].op = CCB_OP_BUS_RESET; - - /* Now tell the BusLogic to flush all pending commands for this - target. */ - buslogic_out(scpnt->host->io_port, buscmd, sizeof buscmd); - - /* Here is the tricky part. What to do next. Do we get an - interrupt for the commands that we aborted with the specified - target, or do we generate this on our own? Try it without first - and see what happens. */ - buslogic_printk("sent BUS DEVICE RESET to target %d.\n", - scpnt->target); - - /* If the first does not work, then try the second. I think the - first option is more likely to be correct. Free the command - block for all commands running on this target... */ -#if 1 - for (i = 0; i < BUSLOGIC_MAILBOXES; i++) - if (HOSTDATA(scpnt->host)->sc[i] - && HOSTDATA(scpnt->host)->sc[i]->target == scpnt->target) { - Scsi_Cmnd *sctmp = HOSTDATA(scpnt->host)->sc[i]; - - sctmp->result = DID_RESET << 16; - if (sctmp->host_scribble) - scsi_free(sctmp->host_scribble, BUSLOGIC_SG_MALLOC); - buslogic_printk("sending DID_RESET for target %d.\n", - scpnt->target); - sctmp->scsi_done(scpnt); - - HOSTDATA(scpnt->host)->sc[i] = NULL; - HOSTDATA(scpnt->host)->mb[i].status = MBX_NOT_IN_USE; - } - return SCSI_RESET_SUCCESS; -#else - return SCSI_RESET_PENDING; -#endif - } -#endif - /* No active command at this time, so this means that each time we got some - kind of response the last time through. Tell the mid-level code to - request sense information in order to decide what to do next. */ - return SCSI_RESET_PUNT; -} - -/* ??? This is probably not correct for series "C" boards. I believe these - support separate mappings for each disk. We would need to issue a - CMD_READ_FW_LOCAL_RAM command to check for the particular drive being - queried. Note that series "C" boards can be differentiated by having - HOSTDATA(disk->device->host)->firmware_rev[0] >= '4'. */ -int buslogic_biosparam(Disk *disk, kdev_t dev, int *ip) -{ - unsigned int size = disk->capacity; - - /* ip[0] == heads, ip[1] == sectors, ip[2] == cylinders */ - if (HOSTDATA(disk->device->host)->bios_translation == BIOS_TRANSLATION_BIG - && size >= 0x200000) { /* 1GB */ - if (size >= 0x400000) { /* 2GB */ -#if 0 /* ??? Used in earlier kernels, but disagrees with BusLogic info. */ - if (mb >= 0x800000) { /* 4GB */ - ip[0] = 256; - ip[1] = 64; - } else { - ip[0] = 256; - ip[1] = 32; - } -#else - ip[0] = 255; - ip[1] = 63; -#endif - } else { - ip[0] = 128; - ip[1] = 32; - } - } else { - ip[0] = 64; - ip[1] = 32; - } - ip[2] = size / (ip[0] * ip[1]); -/* if (ip[2] > 1024) - ip[2] = 1024; */ - return 0; -} - -/* called from init/main.c */ -void buslogic_setup(char *str, int *ints) -{ - static const unsigned short valid_bases[] - = { 0x130, 0x134, 0x230, 0x234, 0x330, 0x334 }; - static size_t setup_idx = 0; - size_t i; - - if (setup_idx >= ARRAY_SIZE(bases) - 1) { - buslogic_printk("called too many times. Bad LILO params?\n"); - return; - } - if (ints[0] != 1) { - buslogic_printk("malformed command line.\n"); - buslogic_printk("usage: buslogic=\n"); - return; - } - for (i = 0; i < ARRAY_SIZE(valid_bases); i++) - if (valid_bases[i] == ints[1]) { - bases[setup_idx++] = ints[1]; - bases[setup_idx] = 0; - return; - } - buslogic_printk("invalid base 0x%X specified.\n", ints[1]); -} - -#ifdef MODULE -/* Eventually this will go into an include file, but that's later... */ -Scsi_Host_Template driver_template = BUSLOGIC; - -# include "scsi_module.c" -#endif diff --git a/drivers/scsi/buslogic.h b/drivers/scsi/buslogic.h deleted file mode 100644 index a47c4903101a..000000000000 --- a/drivers/scsi/buslogic.h +++ /dev/null @@ -1,190 +0,0 @@ -/* - * buslogic.h Copyright (C) 1993, 1994 David B. Gentzel - * See buslogic.c for more information. - */ - -#ifndef _BUSLOGIC_H - -int buslogic_detect(Scsi_Host_Template *); -int buslogic_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); -int buslogic_abort(Scsi_Cmnd *); -const char *buslogic_info(struct Scsi_Host *); -int buslogic_reset(Scsi_Cmnd *); -int buslogic_biosparam(Disk *, kdev_t, int *); - - -#define BUSLOGIC { NULL, NULL, \ - NULL, \ - NULL, \ - "BusLogic", \ - buslogic_detect, \ - 0, /* no release func */ \ - buslogic_info, \ - 0, /* no command func */ \ - buslogic_queuecommand, \ - buslogic_abort, \ - buslogic_reset, \ - 0, /* slave_attach NYI */ \ - buslogic_biosparam, \ - 0, /* set by driver */ \ - 0, /* set by driver */ \ - 0, /* set by driver */ \ - 0, /* set by driver */ \ - 0, \ - 0, /* set by driver */ \ - ENABLE_CLUSTERING \ - } - -#ifdef BUSLOGIC_PRIVATE_H - -/* ??? These don't really belong here */ -#ifndef TRUE -# define TRUE 1 -#endif -#ifndef FALSE -# define FALSE 0 -#endif - -#define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr)[0]) - -#define PACKED __attribute__((packed)) - -#define BD_ABORT 0x0001 -#define BD_COMMAND 0x0002 -#define BD_DETECT 0x0004 -#define BD_ERRORS 0x0008 -#define BD_INTERRUPT 0x0010 -#define BD_IO 0x0020 -#define BD_RESET 0x0040 -#define BD_UNDOCUMENTED 0x0080 - -/* I/O Port interface */ -/* READ */ -#define STATUS(base) (base) -#define DACT 0x80 /* Diagnostic Active */ -#define DFAIL 0x40 /* Diagnostic Failure */ -#define INREQ 0x20 /* Initialization Required */ -#define HARDY 0x10 /* Host Adapter Ready */ -#define CPRBSY 0x08 /* Command/Parameter Register Busy */ -#define DIRRDY 0x04 /* Data In Register Ready */ - /* 0x02 is reserved */ -#define CMDINV 0x01 /* Command Invalid */ - -#define DATA_IN(base) (STATUS(base) + 1) - -#define INTERRUPT(base) (STATUS(base) + 2) -#define INTV 0x80 /* Interrupt Valid */ - /* 0x70 are reserved */ -#define RSTS 0x08 /* SCSI Reset State */ -#define CMDC 0x04 /* Command Complete */ -#define MBOR 0x02 /* Mailbox Out Ready */ -#define IMBL 0x01 /* Incoming Mailbox Loaded */ -#define INTRMASK 0x8F - -/* This undocumented port returns a bitmask indicating geometry translation. */ -#define GEOMETRY(base) (STATUS(base) + 3) -#define GEO_GT_1GB 0x80 /* > 1GB under DOS geometry mapping */ - /* 0x70 are unknown */ -#define GEO_XLATION_S_D1 0x0C /* Disk 1 geometry ("S" models only) */ -#define GEO_XLATION_S_D0 0x03 /* Disk 0 geometry ("S" models only) */ - - -/* WRITE */ -#define CONTROL(base) STATUS(base) -#define RHARD 0x80 /* Hard Reset */ -#define RSOFT 0x40 /* Soft Reset */ -#define RINT 0x20 /* Interrupt Reset */ -#define RSBUS 0x10 /* SCSI Bus Reset */ - /* 0x0F are reserved */ - -#define COMMAND_PARAMETER(base) (STATUS(base) + 1) -#define CMD_TSTCMDCINT 0x00 /* Test CMDC Interrupt */ -#define CMD_INITMB 0x01 /* Initialize Mailbox */ -#define CMD_START_SCSI 0x02 /* Start Mailbox */ -#define CMD_START_BIOS 0x03 /* Start BIOS */ -#define CMD_INQUIRY 0x04 /* Inquire Board ID */ -#define CMD_ENBOMBRINT 0x05 /* Enable OMBR Interrupt */ -#define CMD_SETSELTIMOUT 0x06 /* Set SCSI Selection Time-Out */ -#define CMD_BUSON_TIME 0x07 /* Set Bus-On Time */ -#define CMD_BUSOFF_TIME 0x08 /* Set Bus-Off Time */ -#define CMD_BUSXFR_RATE 0x09 /* Set Bus Transfer Rate */ -#define CMD_INQ_DEVICES 0x0A /* Inquire Installed Devices */ -#define CMD_RETCONF 0x0B /* Return Configuration */ -#define CMD_TARGET_MODE 0x0C /* Set Target Mode */ -#define CMD_INQ_SETUP_INFO 0x0D /* Inquire Set-up Information */ -#define CMD_WRITE_LCL_RAM 0x1A /* Write Adapter Local RAM */ -#define CMD_READ_LCL_RAM 0x1B /* Read Adapter Local RAM */ -#define CMD_WRITE_BM_FIFO 0x1C /* Write Bus Master Chip FIFO */ -#define CMD_READ_BM_FIFO 0x1D /* Read Bus Master Chip FIFO */ -#define CMD_ECHO 0x1F /* Echo Data Byte */ -#define CMD_HA_DIAG 0x20 /* Host Adapter Diagnostic */ -#define CMD_HA_OPTIONS 0x21 /* Host Adapter Options */ -#define CMD_INITEXTMB 0x81 /* Initialize Extended Mailbox */ -#define CMD_VER_NO_LAST 0x84 /* Version Number Last Byte (undocumented) */ -#define CMD_VER_NO_LETTER 0x85 /* Version Number One Letter (undocumented) */ -#define CMD_RET_MODEL_NO 0x8B /* Return Model Number (undocumented) */ -#define CMD_INQEXTSETUP 0x8D /* Inquire Extended Set-up Information */ -#define CMD_ROUND_ROBIN 0x8F /* Enable strict vs. half-assed round-robin - mailbox filling (undocumented) */ -#define CMD_READ_FW_LCL_RAM 0x91/* Read Firmware Local RAM (undocumented) */ -#define CMD_WRITE_INQ_BUF 0x9A /* Write Inquiry Data Buffer - (Target Mode Only) */ -#define CMD_READ_INQ_BUF 0x9B /* Read Inquiry Data Buffer - (Target Mode Only) */ - -#define MBX_NOT_IN_USE 0x00 -#define MBX_ACTION_START 0x01 -#define MBX_ACTION_ABORT 0x02 -#define MBX_COMPLETION_OK 0x01 -#define MBX_COMPLETION_ABORTED 0x02 -#define MBX_COMPLETION_NOT_FOUND 0x03 -#define MBX_COMPLETION_ERROR 0x04 - -/* Mailbox Definition */ -struct mailbox { - void *ccbptr; /* lsb, ..., msb */ - unsigned char btstat; - unsigned char sdstat; - unsigned char reserved; - unsigned char status; /* Command/Status */ -}; - -/* This is used with scatter-gather */ -struct chain { - unsigned long datalen; /* Size of this part of chain */ - void *dataptr; /* Location of data */ -}; - -#define MAX_CDB 12 - -struct ccb { /* Command Control Block */ - unsigned char op; /* Command Control Block Operation Code */ - unsigned char dir; - unsigned char cdblen; /* SCSI Command Length */ - unsigned char rsalen; /* Request Sense Allocation Length/Disable */ - unsigned long datalen; /* Data Length (msb, ..., lsb) */ - void *dataptr; /* Data Pointer */ - unsigned char reserved[2]; - unsigned char hastat; /* Host Adapter Status (HASTAT) */ - unsigned char tarstat; /* Target Device Status */ - unsigned char id; - unsigned char lun; - unsigned char cdb[MAX_CDB]; - unsigned char ccbcontrol; - unsigned char commlinkid; /* Command Linking Identifier */ - void *linkptr; /* Link Pointer */ - void *senseptr; -}; - -#define CCB_OP_INIT 0x00 /* Initiator CCB */ -#define CCB_OP_TARG 0x01 /* Target CCB */ -#define CCB_OP_INIT_SG 0x02 /* Initiator CCB with scatter-gather */ -#define CCB_OP_INIT_R 0x03 /* Initiator CCB with residual data length - returned */ -#define CCB_OP_INIT_SG_R 0x04 /* Initiator CCB with scatter-gather and - residual data length returned */ -#define CCB_OP_BUS_RESET 0x81 /* SCSI bus device reset */ - -#endif - -#endif diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 2c726f57fef7..ee6d8e9b7107 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -58,7 +58,7 @@ #endif #ifdef CONFIG_SCSI_BUSLOGIC -#include "buslogic.h" +#include "BusLogic.h" #endif #ifdef CONFIG_SCSI_EATA_DMA @@ -167,7 +167,7 @@ static Scsi_Host_Template builtin_scsi_hosts[] = #ifdef CONFIG_SCSI_AHA152X AHA152X, #endif -/* Buslogic must come before aha1542.c */ +/* BusLogic must come before aha1542.c */ #ifdef CONFIG_SCSI_BUSLOGIC BUSLOGIC, #endif diff --git a/drivers/scsi/scsi_syms.c b/drivers/scsi/scsi_syms.c index 600708bdde9b..db0a0b808d63 100644 --- a/drivers/scsi/scsi_syms.c +++ b/drivers/scsi/scsi_syms.c @@ -53,6 +53,8 @@ struct symbol_table scsi_symbol_table = { X(scsi_ioctl), X(print_command), X(print_sense), + X(print_msg), + X(print_status), X(dma_free_sectors), X(kernel_scsi_ioctl), X(need_isa_buffer), diff --git a/drivers/scsi/seagate.c b/drivers/scsi/seagate.c index 10337adbfc39..5363e538dfb1 100644 --- a/drivers/scsi/seagate.c +++ b/drivers/scsi/seagate.c @@ -406,6 +406,33 @@ const char *seagate_st0x_info(struct Scsi_Host * shpnt) { return buffer; } +int seagate_st0x_proc_info(char *buffer, char **start, off_t offset, + int length, int hostno, int inout) +{ + const char *info = seagate_st0x_info(NULL); + int len; + int pos; + int begin; + + if (inout) return(-ENOSYS); + + begin = 0; + strcpy(buffer,info); + strcat(buffer,"\n"); + + pos = len = strlen(buffer); + + if (pos length ) len = length; + return(len); +} + /* * These are our saved pointers for the outstanding command that is * waiting for a reconnect diff --git a/drivers/scsi/seagate.h b/drivers/scsi/seagate.h index 7210cb3dc174..8d9e1a42fcb1 100644 --- a/drivers/scsi/seagate.h +++ b/drivers/scsi/seagate.h @@ -19,6 +19,7 @@ int seagate_st0x_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); int seagate_st0x_abort(Scsi_Cmnd *); const char *seagate_st0x_info(struct Scsi_Host *); int seagate_st0x_reset(Scsi_Cmnd *); +int seagate_st0x_proc_info(char *,char **,off_t,int,int,int); #ifndef NULL #define NULL 0 @@ -27,7 +28,7 @@ int seagate_st0x_reset(Scsi_Cmnd *); #include int seagate_st0x_biosparam(Disk *, kdev_t, int*); -#define SEAGATE_ST0X { NULL, NULL, NULL, NULL, \ +#define SEAGATE_ST0X { NULL, NULL, NULL, seagate_st0x_proc_info, \ NULL, seagate_st0x_detect, \ NULL, \ seagate_st0x_info, seagate_st0x_command, \ diff --git a/fs/Config.in b/fs/Config.in index df6af9db03d6..bb61dcd6ad54 100644 --- a/fs/Config.in +++ b/fs/Config.in @@ -15,6 +15,9 @@ fi bool '/proc filesystem support' CONFIG_PROC_FS if [ "$CONFIG_INET" = "y" ]; then tristate 'NFS filesystem support' CONFIG_NFS_FS + if [ "$CONFIG_NFS_FS" = "y" ]; then + bool 'Root file system on NFS' CONFIG_ROOT_NFS + fi fi tristate 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS tristate 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 9990615ea1bc..4722731c8e57 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -345,8 +345,8 @@ static unsigned int load_aout_interp(struct exec * interp_ex, #define INTERPRETER_ELF 2 -static int -load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) +static inline int +do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) { struct elfhdr elf_ex; struct elfhdr interp_elf_ex; @@ -370,8 +370,6 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) unsigned int elf_stack; char passed_fileno[6]; - MOD_INC_USE_COUNT; - ibcs2_interpreter = 0; status = 0; load_addr = 0; @@ -379,7 +377,6 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) if (elf_ex.e_ident[0] != 0x7f || strncmp(&elf_ex.e_ident[1], "ELF",3) != 0) { - MOD_DEC_USE_COUNT; return -ENOEXEC; } @@ -390,7 +387,6 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) (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)){ - MOD_DEC_USE_COUNT; return -ENOEXEC; } @@ -399,7 +395,6 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) elf_phdata = (struct elf_phdr *) kmalloc(elf_ex.e_phentsize * elf_ex.e_phnum, GFP_KERNEL); if (elf_phdata == NULL) { - MOD_DEC_USE_COUNT; return -ENOMEM; } @@ -407,7 +402,6 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) elf_ex.e_phentsize * elf_ex.e_phnum, 1); if (retval < 0) { kfree (elf_phdata); - MOD_DEC_USE_COUNT; return retval; } @@ -420,7 +414,6 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) if (elf_exec_fileno < 0) { kfree (elf_phdata); - MOD_DEC_USE_COUNT; return elf_exec_fileno; } @@ -438,7 +431,6 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) { kfree (elf_phdata); kfree(elf_interpreter); - MOD_DEC_USE_COUNT; return -EINVAL; } @@ -451,7 +443,6 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) GFP_KERNEL); if (elf_interpreter == NULL) { kfree (elf_phdata); - MOD_DEC_USE_COUNT; return -ENOMEM; } @@ -484,7 +475,6 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) if(retval < 0) { kfree (elf_phdata); kfree(elf_interpreter); - MOD_DEC_USE_COUNT; return retval; } } @@ -509,7 +499,6 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) { kfree(elf_interpreter); kfree(elf_phdata); - MOD_DEC_USE_COUNT; return -ELIBBAD; } } @@ -534,7 +523,6 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) kfree(elf_interpreter); } kfree (elf_phdata); - MOD_DEC_USE_COUNT; return -E2BIG; } } @@ -587,7 +575,6 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) printk("Unable to load interpreter\n"); kfree(elf_phdata); send_sig(SIGSEGV, current, 0); - MOD_DEC_USE_COUNT; return 0; } } @@ -709,15 +696,25 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) start_thread(regs, elf_entry, bprm->p); if (current->flags & PF_PTRACED) send_sig(SIGTRAP, current, 0); - MOD_DEC_USE_COUNT; return 0; } +static int +load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) +{ + int retval; + + MOD_INC_USE_COUNT; + retval = do_load_elf_binary(bprm, regs); + MOD_DEC_USE_COUNT; + return retval; +} + /* This is really simpleminded and specialized - we are loading an a.out library that is given an ELF header. */ -static int -load_elf_library(int fd){ +static inline int +do_load_elf_library(int fd){ struct file * file; struct elfhdr elf_ex; struct elf_phdr *elf_phdata = NULL; @@ -729,47 +726,46 @@ load_elf_library(int fd){ int error; int i,j, k; - MOD_INC_USE_COUNT; len = 0; file = current->files->fd[fd]; inode = file->f_inode; elf_bss = 0; - set_fs(KERNEL_DS); - if (file->f_op->read(inode, file, (char *) &elf_ex, sizeof(elf_ex)) != sizeof(elf_ex)) { - SYS(close)(fd); - MOD_DEC_USE_COUNT; + if (!file || !file->f_op) return -EACCES; - } + + /* seek to the beginning of the file */ + if (file->f_op->lseek) { + if ((error = file->f_op->lseek(inode, file, 0, 0)) != 0) + return -ENOEXEC; + } else + file->f_pos = 0; + + set_fs(KERNEL_DS); + error = file->f_op->read(inode, file, (char *) &elf_ex, sizeof(elf_ex)); set_fs(USER_DS); - + if (error != sizeof(elf_ex)) + return -ENOEXEC; + if (elf_ex.e_ident[0] != 0x7f || - strncmp(&elf_ex.e_ident[1], "ELF",3) != 0) { - MOD_DEC_USE_COUNT; + strncmp(&elf_ex.e_ident[1], "ELF",3) != 0) return -ENOEXEC; - } - + /* First of all, some simple consistency checks */ if(elf_ex.e_type != ET_EXEC || elf_ex.e_phnum > 2 || (elf_ex.e_machine != EM_386 && elf_ex.e_machine != EM_486) || - (!inode->i_op || !inode->i_op->default_file_ops->mmap)){ - MOD_DEC_USE_COUNT; + (!inode->i_op || !inode->i_op->default_file_ops->mmap)) return -ENOEXEC; - } /* Now read in all of the header information */ - if(sizeof(struct elf_phdr) * elf_ex.e_phnum > PAGE_SIZE) { - MOD_DEC_USE_COUNT; + if(sizeof(struct elf_phdr) * elf_ex.e_phnum > PAGE_SIZE) return -ENOEXEC; - } elf_phdata = (struct elf_phdr *) kmalloc(sizeof(struct elf_phdr) * elf_ex.e_phnum, GFP_KERNEL); - if (elf_phdata == NULL) { - MOD_DEC_USE_COUNT; + if (elf_phdata == NULL) return -ENOMEM; - } retval = read_exec(inode, elf_ex.e_phoff, (char *) elf_phdata, sizeof(struct elf_phdr) * elf_ex.e_phnum, 1); @@ -780,7 +776,6 @@ load_elf_library(int fd){ if(j != 1) { kfree(elf_phdata); - MOD_DEC_USE_COUNT; return -ENOEXEC; } @@ -800,7 +795,6 @@ load_elf_library(int fd){ SYS(close)(fd); if (error != (elf_phdata->p_vaddr & 0xfffff000)) { kfree(elf_phdata); - MOD_DEC_USE_COUNT; return error; } @@ -813,10 +807,19 @@ load_elf_library(int fd){ PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_PRIVATE, 0); kfree(elf_phdata); - MOD_DEC_USE_COUNT; return 0; } +static int load_elf_library(int fd) +{ + int retval; + + MOD_INC_USE_COUNT; + retval = do_load_elf_library(fd); + MOD_DEC_USE_COUNT; + return retval; +} + /* * ELF core dumper * diff --git a/fs/buffer.c b/fs/buffer.c index 0dde5edfc27c..05a60ed4c565 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -428,7 +428,7 @@ static inline void insert_into_queues(struct buffer_head * bh) bh->b_next->b_prev = bh; } -static struct buffer_head * find_buffer(kdev_t dev, int block, int size) +static inline struct buffer_head * find_buffer(kdev_t dev, int block, int size) { struct buffer_head * tmp; @@ -807,10 +807,8 @@ void refile_buffer(struct buffer_head * buf) /* * Release a buffer head */ -void brelse(struct buffer_head * buf) +void __brelse(struct buffer_head * buf) { - if (!buf) - return; wait_on_buffer(buf); /* If dirty, mark the time this buffer should be written back */ @@ -828,10 +826,8 @@ void brelse(struct buffer_head * buf) /* * bforget() is like brelse(), except is throws the buffer away */ -void bforget(struct buffer_head * buf) +void __bforget(struct buffer_head * buf) { - if (!buf) - return; wait_on_buffer(buf); if (buf->b_count != 1) { printk("Aieee... bforget(): count = %d\n", buf->b_count); @@ -1044,7 +1040,7 @@ static void read_buffers(struct buffer_head * bh[], int nrbuf) * "address" points to the new page we can use to move things * around.. */ -static unsigned long try_to_align(struct buffer_head ** bh, int nrbuf, +static inline unsigned long try_to_align(struct buffer_head ** bh, int nrbuf, unsigned long address) { while (nrbuf-- > 0) diff --git a/fs/dcache.c b/fs/dcache.c index 3a232088a0dd..ec1629ea5246 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -130,7 +130,7 @@ static inline void add_hash(struct dir_cache_entry * de, struct hash_list * hash /* * Find a directory cache entry given all the necessary info. */ -static struct dir_cache_entry * find_entry(struct inode * dir, const char * name, int len, struct hash_list * hash) +static inline struct dir_cache_entry * find_entry(struct inode * dir, const char * name, int len, struct hash_list * hash) { struct dir_cache_entry * de = hash->next; diff --git a/fs/exec.c b/fs/exec.c index 7c5919cccaf0..72fc3f45bbf4 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -953,12 +953,22 @@ static int load_aout_library(int fd) file = current->files->fd[fd]; inode = file->f_inode; - set_fs(KERNEL_DS); - if (file->f_op->read(inode, file, (char *) &ex, sizeof(ex)) != sizeof(ex)) { + if (!file || !file->f_op) return -EACCES; - } + + /* Seek into the file */ + if (file->f_op->lseek) { + if ((error = file->f_op->lseek(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) || diff --git a/fs/ext/inode.c b/fs/ext/inode.c index aef0ed55a8eb..63e2123a7156 100644 --- a/fs/ext/inode.c +++ b/fs/ext/inode.c @@ -171,7 +171,7 @@ void ext_statfs (struct super_block *sb, struct statfs *buf, int bufsiz) #define inode_bmap(inode,nr) ((inode)->u.ext_i.i_data[(nr)]) -static int block_bmap(struct buffer_head * bh, int nr) +static inline int block_bmap(struct buffer_head * bh, int nr) { int tmp; diff --git a/fs/fcntl.c b/fs/fcntl.c index 12f7cf4893da..166f79675c2c 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -17,7 +17,7 @@ extern int fcntl_getlk(unsigned int, struct flock *); extern int fcntl_setlk(unsigned int, unsigned int, struct flock *); extern int sock_fcntl (struct file *, unsigned int cmd, unsigned long arg); -static int dupfd(unsigned int fd, unsigned int arg) +static inline int dupfd(unsigned int fd, unsigned int arg) { if (fd >= NR_OPEN || !current->files->fd[fd]) return -EBADF; diff --git a/fs/file_table.c b/fs/file_table.c index 14ba6fdd9e69..a088e48ec73c 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -19,7 +19,7 @@ int nr_files = 0; /* * Insert a new file structure at the head of the list of available ones. */ -static void insert_file_free(struct file *file) +static inline void insert_file_free(struct file *file) { file->f_count = 0; file->f_next = first_file; @@ -32,7 +32,7 @@ static void insert_file_free(struct file *file) /* * Remove a file structure from the list of available ones. */ -static void remove_file_free(struct file *file) +static inline void remove_file_free(struct file *file) { if (first_file == file) first_file = first_file->f_next; @@ -44,7 +44,7 @@ static void remove_file_free(struct file *file) /* * Insert a file structure at the end of the list of available ones. */ -static void put_last_free(struct file *file) +static inline void put_last_free(struct file *file) { file->f_prev = first_file->f_prev; file->f_prev->f_next = file; diff --git a/fs/filesystems.c b/fs/filesystems.c index b884e7e189c9..3f273e59bbde 100644 --- a/fs/filesystems.c +++ b/fs/filesystems.c @@ -21,9 +21,15 @@ #include #include #include +#include extern void device_setup(void); +#ifdef CONFIG_ROOT_NFS +extern int nfs_root_init(char *nfsname); +extern char nfs_root_name []; +#endif + /* This may be used only once, enforced by 'static int callable' */ asmlinkage int sys_setup(void) { @@ -100,6 +106,14 @@ asmlinkage int sys_setup(void) {hpfs_read_super, "hpfs", 1, NULL}); #endif +#ifdef CONFIG_ROOT_NFS + if (nfs_root_name [0]){ + if (nfs_root_init(nfs_root_name) < 0) { + printk(KERN_ERR "Root-NFS: Unable to mount NFS filesystem as /, using /dev/fd0 instead\n"); + ROOT_DEV = MKDEV(FLOPPY_MAJOR, 0); + } + } +#endif mount_root(); return 0; } diff --git a/fs/inode.c b/fs/inode.c index 6992b68d207a..14e9770b1f75 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -31,7 +31,7 @@ static inline struct inode_hash_entry * const hash(kdev_t dev, int i) return hash_table + hashfn(dev, i); } -static void insert_inode_free(struct inode *inode) +static inline void insert_inode_free(struct inode *inode) { inode->i_next = first_inode; inode->i_prev = first_inode->i_prev; @@ -40,7 +40,7 @@ static void insert_inode_free(struct inode *inode) first_inode = inode; } -static void remove_inode_free(struct inode *inode) +static inline void remove_inode_free(struct inode *inode) { if (first_inode == inode) first_inode = first_inode->i_next; @@ -63,7 +63,7 @@ void insert_inode_hash(struct inode *inode) h->inode = inode; } -static void remove_inode_hash(struct inode *inode) +static inline void remove_inode_hash(struct inode *inode) { struct inode_hash_entry *h; h = hash(inode->i_dev, inode->i_ino); @@ -77,7 +77,7 @@ static void remove_inode_hash(struct inode *inode) inode->i_hash_prev = inode->i_hash_next = NULL; } -static void put_last_free(struct inode *inode) +static inline void put_last_free(struct inode *inode) { remove_inode_free(inode); inode->i_prev = first_inode->i_prev; @@ -225,7 +225,7 @@ static void write_inode(struct inode * inode) unlock_inode(inode); } -static void read_inode(struct inode * inode) +static inline void read_inode(struct inode * inode) { lock_inode(inode); if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->read_inode) diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile index 61466a09dfb7..8afce01fb8ec 100644 --- a/fs/nfs/Makefile +++ b/fs/nfs/Makefile @@ -9,6 +9,11 @@ O_TARGET := nfs.o O_OBJS := proc.o sock.o rpcsock.o inode.o file.o dir.o symlink.o mmap.o + +ifdef CONFIG_ROOT_NFS +O_OBJS += nfsroot.o +endif + M_OBJS := $(O_TARGET) include $(TOPDIR)/Rules.make diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c new file mode 100644 index 000000000000..35b4674ce1bc --- /dev/null +++ b/fs/nfs/nfsroot.c @@ -0,0 +1,849 @@ +/* + * linux/fs/nfs/nfsroot.c + * + * Copyright (C) 1995 Gero Kuhlmann + * + * Allow an NFS filesystem to be mounted as root. The way this works + * is to first determine the local IP address via RARP. Then handle + * the RPC negotiation with the system which replied to the RARP. The + * actual mounting is done later, when init() is running. + */ + + +/* Define this to allow debugging output */ +#define NFSROOT_DEBUG 1 + +/* Define the timeout for waiting for a RARP reply */ +#define RARP_TIMEOUT 30 /* 30 seconds */ +#define RARP_RETRIES 5 /* 5 retries */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_AX25 +#include /* For AX25_P_IP */ +#endif +#include +#include +#include +#include +#include +#include +#include +/* #include */ + +#define IPPORT_RESERVED 1024 + +/* Range of privileged ports */ +#define STARTPORT 600 +#define ENDPORT (IPPORT_RESERVED - 1) +#define NPORTS (ENDPORT - STARTPORT + 1) + + + +struct open_dev +{ + struct device *dev; + unsigned short old_flags; + struct open_dev *next; +}; + +static struct open_dev *open_base = NULL; +static struct device *root_dev = NULL; +static struct sockaddr_in myaddr; /* My IP address */ +static struct sockaddr_in server; /* Server IP address */ +static struct nfs_mount_data nfs_data; /* NFS mount info */ +static char nfs_path[NFS_MAXPATHLEN]; /* Name of directory to mount */ +static int nfs_port; /* Port to connect to for NFS service */ + + + +/*************************************************************************** + + RARP Subroutines + + ***************************************************************************/ + +extern void arp_send(int type, int ptype, unsigned long target_ip, + struct device *dev, unsigned long src_ip, + unsigned char *dest_hw, unsigned char *src_hw, + unsigned char *target_hw); + +static int root_rarp_recv(struct sk_buff *skb, struct device *dev, + struct packet_type *pt); + + +static struct packet_type rarp_packet_type = +{ + 0, /* Should be: __constant_htons(ETH_P_RARP) - but this _doesn't_ come out constant! */ + NULL, /* Listen to all devices */ + root_rarp_recv, + NULL, + NULL +}; + + +/* + * For receiving rarp packets a packet type has to be registered. Also + * initialize all devices for usage by RARP. + */ +static int root_rarp_open(void) +{ + struct open_dev *openp; + struct device *dev; + unsigned short old_flags; + int num; + + /* Register the packet type */ + rarp_packet_type.type=htons(ETH_P_RARP); + dev_add_pack(&rarp_packet_type); + + /* Open all devices which allow RARP */ + for (dev = dev_base, num = 0; dev != NULL; dev = dev->next) { + if (dev->type < ARPHRD_SLIP && + dev->family == AF_INET && + !(dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT | IFF_NOARP))) { + /* First up the interface */ + old_flags = dev->flags; + dev->flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING; + if (!(old_flags & IFF_UP) && dev_open(dev)) { + dev->flags = old_flags; + continue; + } + openp = (struct open_dev *) kmalloc(sizeof(struct open_dev), + GFP_ATOMIC); + if (openp == NULL) + continue; + openp->dev = dev; + openp->old_flags = old_flags; + openp->next = open_base; + open_base = openp; + num++; + } + } + return num; +} + + +/* + * Remove the packet type again when all rarp packets have been received + * and restore the state of the device. However, keep the root device + * open for the upcoming mount. + */ +static void root_rarp_close(void) +{ + struct open_dev *openp; + struct open_dev *nextp; + + /* Deregister the packet type */ + rarp_packet_type.type=htons(ETH_P_RARP); + dev_remove_pack(&rarp_packet_type); + + /* Deactivate all previously opened devices except that one which is + * able to connect to a suitable server + */ + openp = open_base; + while (openp != NULL) { + nextp = openp->next; + openp->next = NULL; + if (openp->dev != root_dev) { + if (!(openp->old_flags & IFF_UP)) + dev_close(openp->dev); + openp->dev->flags = openp->old_flags; + } + kfree_s(openp, sizeof(struct open_dev)); + openp = nextp; + } +} + + +/* + * Receive RARP packets. + */ +static int root_rarp_recv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) +{ + struct arphdr *rarp = (struct arphdr *)skb->h.raw; + unsigned char *rarp_ptr = (unsigned char *)(rarp+1); + unsigned long sip, tip; + unsigned char *sha, *tha; /* s for "source", t for "target" */ + + /* If this test doesn't pass, its not IP, or we should ignore it anyway */ + if (rarp->ar_hln != dev->addr_len || dev->type != ntohs(rarp->ar_hrd)) { + kfree_skb(skb, FREE_READ); + return 0; + } + + /* If it's not a RARP reply, delete it. */ + if (rarp->ar_op != htons(ARPOP_RREPLY)) { + kfree_skb(skb, FREE_READ); + return 0; + } + + /* If it's not ethernet or AX25, delete it. */ + if ((rarp->ar_pro != htons(ETH_P_IP) && dev->type != ARPHRD_AX25) || +#ifdef CONFIG_AX25 + (rarp->ar_pro != htons(AX25_P_IP) && dev->type == ARPHRD_AX25) || +#endif + rarp->ar_pln != 4) { + kfree_skb(skb, FREE_READ); + return 0; + } + + /* Extract variable width fields */ + sha = rarp_ptr; + rarp_ptr += dev->addr_len; + memcpy(&sip, rarp_ptr, 4); + rarp_ptr += 4; + tha = rarp_ptr; + rarp_ptr += dev->addr_len; + memcpy(&tip, rarp_ptr, 4); + + /* Discard packets which are not meant for us. */ + if (memcmp(tha, dev->dev_addr, dev->addr_len)) { + kfree_skb(skb, FREE_READ); + return 0; + } + + /* The packet is what we were looking for. Setup the global variables. */ + cli(); + if (root_dev != NULL) { + sti(); + kfree_skb(skb, FREE_READ); + return 0; + } + root_dev = dev; + sti(); + + myaddr.sin_family = dev->family; + myaddr.sin_addr.s_addr = tip; + server.sin_family = dev->family; + if (!server.sin_addr.s_addr) + server.sin_addr.s_addr = sip; + + kfree_skb(skb, FREE_READ); + return 0; +} + + +/* + * Send RARP request packet over all devices which allow RARP. + */ +static void root_rarp_send(void) +{ + struct open_dev *openp; + struct device *dev; + +#ifdef NFSROOT_DEBUG + printk(KERN_NOTICE "NFS: Sending RARP request...\n"); +#endif + + for (openp = open_base; openp != NULL; openp = openp->next) { + dev = openp->dev; + arp_send(ARPOP_RREQUEST, ETH_P_RARP, 0, dev, 0, NULL, + dev->dev_addr, dev->dev_addr); + } +} + + +/* + * Determine client and server IP numbers and appropriate device by using + * the RARP protocol. + */ +static int do_rarp(void) +{ + int retries = 0; + unsigned long timeout; + + /* Open all devices and setup RARP protocol */ + if (!root_rarp_open()) { + printk(KERN_ERR "NFS: No network device found to send RARP request to\n"); + return -1; + } + + /* Send RARP request and wait, until we get an answer. This loop seems + * to be a terrible waste of cpu time, but actually there is no process + * running at all, so we don't need to use any scheduler functions. + */ + for (retries = 0; retries < RARP_RETRIES && root_dev == NULL; retries++) { + root_rarp_send(); + timeout = jiffies + (RARP_TIMEOUT * HZ); + while (jiffies < timeout && root_dev == NULL) + ;; + } + + if (root_dev == NULL) { + printk(KERN_ERR "NFS: Timed out while waiting for RARP answer\n"); + return -1; + } + + root_rarp_close(); + + printk(KERN_NOTICE "NFS: "); + printk("Got RARP answer from %s, ", in_ntoa(server.sin_addr.s_addr)); + printk("my address is %s\n", in_ntoa(myaddr.sin_addr.s_addr)); + + return 0; +} + + + + +/*************************************************************************** + + Routines to setup NFS + + ***************************************************************************/ + +extern void ip_rt_add(short flags, unsigned long addr, unsigned long mask, + unsigned long gw, struct device *dev, + unsigned short mss, unsigned long window); + + +/* The following integer options are recognized */ +static struct nfs_int_opts +{ + char *name; + int *val; +} root_int_opts[] = { + { "port", &nfs_port }, + { "rsize", &nfs_data.rsize }, + { "wsize", &nfs_data.wsize }, + { "timeo", &nfs_data.timeo }, + { "retrans", &nfs_data.retrans }, + { "acregmin", &nfs_data.acregmin }, + { "acregmax", &nfs_data.acregmax }, + { "acdirmin", &nfs_data.acdirmin }, + { "acdirmax", &nfs_data.acdirmax }, + { NULL, NULL }}; + + +/* And now the flag options */ +static struct nfs_bool_opts +{ + char *name; + int and_mask; + int or_mask; +} root_bool_opts[] = { + { "soft", ~NFS_MOUNT_SOFT, NFS_MOUNT_SOFT }, + { "hard", ~NFS_MOUNT_SOFT, 0 }, + { "intr", ~NFS_MOUNT_INTR, NFS_MOUNT_INTR }, + { "nointr", ~NFS_MOUNT_INTR, 0 }, + { "posix", ~NFS_MOUNT_POSIX, NFS_MOUNT_POSIX }, + { "noposix", ~NFS_MOUNT_POSIX, 0 }, + { "cto", ~NFS_MOUNT_NOCTO, 0 }, + { "nocto", ~NFS_MOUNT_NOCTO, NFS_MOUNT_NOCTO }, + { "ac", ~NFS_MOUNT_NOAC, 0 }, + { "noac", ~NFS_MOUNT_NOAC, NFS_MOUNT_NOAC }, + { NULL, 0, 0 }}; + + +unsigned long get_address (char **str) +{ + unsigned long l; + unsigned int val; + int i; + + l = 0; + for (i = 0; i < 4; i++) + { + + l <<= 8; + if (**str != '\0') + { + + val = 0; + while (**str != '\0' && **str != '.' && **str != ':') + { + + val *= 10; + val += **str - '0'; + (*str)++; + } + + l |= val; + if (**str != '\0') + (*str)++; + } + + } + + return(htonl(l)); +} + +/* + * Prepare the NFS data structure and parse any options + */ +static int root_nfs_parse(char *name) +{ + char buf[NFS_MAXPATHLEN]; + char *cp, *options, *val; + + /* get the host ip number */ + if (*name >= '0' && *name <= '9'){ + server.sin_addr.s_addr = get_address (&name); + } + /* Setup the server hostname */ + cp = in_ntoa(server.sin_addr.s_addr); + strncpy(nfs_data.hostname, cp, 255); + nfs_data.addr = server; + + /* Set the name of the directory to mount */ + cp = in_ntoa(myaddr.sin_addr.s_addr); + strncpy(buf, name, 255); + if ((options = strchr(buf, ','))) + *options++ = '\0'; + if (strlen(buf) + strlen(cp) > NFS_MAXPATHLEN) { + printk(KERN_ERR "NFS: Pathname for remote directory too long\n"); + return -1; + } + sprintf(nfs_path, buf, cp); + + /* Set some default values */ + nfs_port = -1; + nfs_data.version = NFS_MOUNT_VERSION; + nfs_data.flags = 0; + nfs_data.rsize = NFS_DEF_FILE_IO_BUFFER_SIZE; + nfs_data.wsize = NFS_DEF_FILE_IO_BUFFER_SIZE; + nfs_data.timeo = 7; + nfs_data.retrans = 3; + nfs_data.acregmin = 3; + nfs_data.acregmax = 60; + nfs_data.acdirmin = 30; + nfs_data.acdirmax = 60; + + /* Process any options */ + if (options) { + cp = strtok(options, ","); + while (cp) { + if ((val = strchr(cp, '='))) { + struct nfs_int_opts *opts = root_int_opts; + *val++ = '\0'; + while (opts->name && strcmp(opts->name, cp)) + opts++; + if (opts->name) + *(opts->val) = (int) simple_strtoul(val, NULL, 10); + } else { + struct nfs_bool_opts *opts = root_bool_opts; + while (opts->name && strcmp(opts->name, cp)) + opts++; + if (opts->name) { + nfs_data.flags &= opts->and_mask; + nfs_data.flags |= opts->or_mask; + } + } + cp = strtok(NULL, ","); + } + } + + return 0; +} + + +/* + * Tell the user what's going on. + */ +static void root_nfs_print(void) +{ +#ifdef NFSROOT_DEBUG + printk(KERN_NOTICE "NFS: Mounting %s on server %s as root\n", + nfs_path, nfs_data.hostname); + printk(KERN_NOTICE "NFS: rsize = %d, wsize = %d, timeo = %d, retrans = %d\n", + nfs_data.rsize, nfs_data.wsize, nfs_data.timeo, nfs_data.retrans); + printk(KERN_NOTICE "NFS: acreg (min,max) = (%d,%d), acdir (min,max) = (%d,%d)\n", + nfs_data.acregmin, nfs_data.acregmax, + nfs_data.acdirmin, nfs_data.acdirmax); + printk(KERN_NOTICE "NFS: port = %d, flags = %08x\n", + nfs_port, nfs_data.flags); +#endif +} + + +/* + * Set the interface address and configure a route to the server. + */ +static void root_nfs_setup(void) +{ + struct rtentry server_route; + struct sockaddr_in *sin; + + /* Setup the device correctly */ + root_dev->family = myaddr.sin_family; + root_dev->pa_addr = myaddr.sin_addr.s_addr; + root_dev->pa_mask = ip_get_mask(myaddr.sin_addr.s_addr); + root_dev->pa_brdaddr = root_dev->pa_addr | ~root_dev->pa_mask; + root_dev->pa_dstaddr = 0; + + sin=(struct sockaddr_in *)&server_route.rt_dst; + *sin=server; + sin=(struct sockaddr_in *)&server_route.rt_genmask; + sin->sin_family=AF_INET; + sin->sin_addr.s_addr= root_dev->pa_mask; + server_route.rt_dev[0]=0; + server_route.rt_flags=RTF_HOST|RTF_UP; + + /* Now add a route to the server */ + if(ip_rt_new(&server_route)==-1) + printk("Unable to add NFS server route.\n"); +} + + +/* + * Get the necessary IP addresses and prepare for mounting the required + * NFS filesystem. + */ +int nfs_root_init(char *nfsname) +{ + /* Initialize network device and get local and server IP address */ + if (do_rarp() < 0) + return -1; + + /* Initialize the global variables necessary for NFS. The server + * directory is actually mounted after init() has been started. + */ + if (root_nfs_parse(nfsname) < 0) + return -1; + root_nfs_print(); + root_nfs_setup(); + return 0; +} + + + + +/*************************************************************************** + + Routines to actually mount the root directory + + ***************************************************************************/ + +static struct file nfs_file; /* File descriptor containing socket */ +static struct inode nfs_inode; /* Inode containing socket */ +static int *rpc_packet = NULL; /* RPC packet */ + +extern asmlinkage int sys_socketcall(int call, unsigned long *args); +extern struct socket *socki_lookup(struct inode *inode); + + +/* + * Open a UDP socket. + */ +static int root_nfs_open(void) +{ + struct file *filp; + unsigned long opt[] = { AF_INET, SOCK_DGRAM, IPPROTO_UDP }; + + /* Open the socket */ + if ((nfs_data.fd = sys_socketcall(SYS_SOCKET, opt)) < 0) { + printk(KERN_ERR "NFS: Cannot open UDP socket\n"); + return -1; + } + + /* Copy the file and inode data area so that we can remove the + * file lateron without killing the socket. After all this the + * closing routine just needs to remove the file pointer from + * the init-task descriptor. + */ + filp = current->files->fd[nfs_data.fd]; + memcpy(&nfs_file, filp, sizeof(struct file)); + nfs_file.f_next = nfs_file.f_prev = NULL; + current->files->fd[nfs_data.fd] = &nfs_file; + filp->f_count = 0; /* Free the file descriptor */ + + memcpy(&nfs_inode, nfs_file.f_inode, sizeof(struct inode)); + nfs_inode.i_hash_next = nfs_inode.i_hash_prev = NULL; + nfs_inode.i_next = nfs_inode.i_prev = NULL; + clear_inode(nfs_file.f_inode); + nfs_file.f_inode = &nfs_inode; + nfs_inode.u.socket_i.inode = &nfs_inode; + nfs_file.private_data = NULL; + + return 0; +} + + +/* + * Close the UDP file descriptor. The main part of preserving the socket + * has already been done after opening it. Now we have to remove the + * file descriptor from the init task. + */ +static void root_nfs_close(int close_all) +{ + /* Remove the file from the list of open files */ + current->files->fd[nfs_data.fd] = NULL; + if (current->files->count > 0) + current->files->count--; + + /* Clear memory use by the RPC packet */ + if (rpc_packet != NULL) + kfree_s(rpc_packet, nfs_data.wsize + 1024); + + /* In case of an error we also have to close the socket again (sigh) */ + if (close_all) { + nfs_inode.u.socket_i.inode = NULL; /* The inode is already cleared */ + if (nfs_file.f_op->release) + nfs_file.f_op->release(&nfs_inode, &nfs_file); + } +} + + +/* + * Find a suitable listening port and bind to it + */ +static int root_nfs_bind(void) +{ + int res = -1; + short port = STARTPORT; + struct sockaddr_in *sin = &myaddr; + int i; + + if (nfs_inode.u.socket_i.ops->bind) { + for (i = 0; i < NPORTS && res < 0; i++) { + sin->sin_port = htons(port++); + if (port > ENDPORT) { + port = STARTPORT; + } + res = nfs_inode.u.socket_i.ops->bind(&nfs_inode.u.socket_i, + (struct sockaddr *) sin, sizeof(struct sockaddr_in)); + } + } + if (res < 0) { + printk(KERN_ERR "NFS: Cannot find a suitable listening port\n"); + root_nfs_close(1); + return -1; + } + +#ifdef NFSROOT_DEBUG + printk(KERN_NOTICE "NFS: Binding to listening port %d\n", port); +#endif + return 0; +} + + +/* + * Send an RPC request and wait for the answer + */ +static int *root_nfs_call(int *end) +{ + struct file *filp; + struct socket *sock; + int dummylen; + static struct nfs_server s = + { &nfs_file, /* struct file * */ + 0, /* struct rsock * */ + { + 0, "", + }, /* toaddr */ + 0, /* lock */ + NULL, /* wait queue */ + NFS_MOUNT_SOFT, /* flags */ + 0, 0, /* rsize, wsize */ + 0, /* timeo */ + 0, /* retrans */ + 3*HZ, 60*HZ, 30*HZ, 60*HZ, "\0" }; + + filp = &nfs_file; + sock = &((filp->f_inode)->u.socket_i); + + /* extract the other end of the socket into server->toaddr */ + sock->ops->getname(sock, &(s.toaddr), &dummylen, 1) ; + ((struct sockaddr_in *) &s.toaddr)->sin_port = server.sin_port; + ((struct sockaddr_in *) &s.toaddr)->sin_family = server.sin_family; + ((struct sockaddr_in *) &s.toaddr)->sin_addr.s_addr = server.sin_addr.s_addr; + + s.rsock = rpc_makesock(filp); + s.flags = nfs_data.flags; + s.rsize = nfs_data.rsize; + s.wsize = nfs_data.wsize; + s.timeo = nfs_data.timeo * HZ / 10; + s.retrans = nfs_data.retrans; + strcpy(s.hostname, nfs_data.hostname); + + /* First connect the UDP socket to a server port, then send the packet + * out, and finally check wether the answer is OK. + */ + if (nfs_inode.u.socket_i.ops->connect && + nfs_inode.u.socket_i.ops->connect(&nfs_inode.u.socket_i, + (struct sockaddr *) &server, sizeof(struct sockaddr_in), + nfs_file.f_flags) < 0) + return NULL; + if (nfs_rpc_call(&s, rpc_packet, end, nfs_data.wsize) < 0) + return NULL; + return rpc_verify(rpc_packet); +} + + +/* + * Create an RPC packet header + */ +static int *root_nfs_header(int proc, int program, int version) +{ + int groups[] = { 0, NOGROUP }; + + if (rpc_packet == NULL) { + if (!(rpc_packet = kmalloc(nfs_data.wsize + 1024, GFP_NFS))) { + printk(KERN_ERR "NFS: Cannot allocate UDP buffer\n"); + return NULL; + } + } + strcpy(system_utsname.nodename, in_ntoa(myaddr.sin_addr.s_addr)); + return rpc_header(rpc_packet, proc, program, version, 0, 0, groups); +} + + +/* + * Query server portmapper for the port of a daemon program + */ +static int root_nfs_get_port(int program, int version) +{ + int *p; + + /* Prepare header for portmap request */ + server.sin_port = htons(NFS_PMAP_PORT); + p = root_nfs_header(NFS_PMAP_PROC, NFS_PMAP_PROGRAM, NFS_PMAP_VERSION); + if (!p) + return -1; + + /* Set arguments for portmapper */ + *p++ = htonl(program); + *p++ = htonl(version); + *p++ = htonl(IPPROTO_UDP); + *p++ = 0; + + /* Send request to server portmapper */ + if ((p = root_nfs_call(p)) == NULL) + return -1; + + return ntohl(*p); +} + + +/* + * Get portnumbers for mountd and nfsd from server + */ +static int root_nfs_ports(void) +{ + int port; + + if (nfs_port < 0) { + if ((port = root_nfs_get_port(NFS_NFS_PROGRAM, NFS_NFS_VERSION)) < 0) { + printk(KERN_ERR "NFS: Unable to get nfsd port number from server, using default\n"); + port = NFS_NFS_PORT; + } + nfs_port = port; +#ifdef NFSROOT_DEBUG + printk(KERN_NOTICE "NFS: Portmapper on server returned %d as nfsd port\n", port); +#endif + } + + if ((port = root_nfs_get_port(NFS_MOUNT_PROGRAM, NFS_MOUNT_VERSION)) < 0) { + printk(KERN_ERR "NFS: Unable to get mountd port number from server, using default\n"); + port = NFS_MOUNT_PORT; + } + server.sin_port = htons(port); +#ifdef NFSROOT_DEBUG + printk(KERN_NOTICE "NFS: Portmapper on server returned %d as mountd port\n", port); +#endif + + return 0; +} + + +/* + * Get a file handle from the server for the directory which is to be mounted + */ +static int root_nfs_get_handle(void) +{ + int len, status, *p; + + /* Prepare header for mountd request */ + p = root_nfs_header(NFS_MOUNT_PROC, NFS_MOUNT_PROGRAM, NFS_MOUNT_VERSION); + if (!p) { + root_nfs_close(1); + return -1; + } + + /* Set arguments for mountd */ + len = strlen(nfs_path); + *p++ = htonl(len); + memcpy(p, nfs_path, len); + len = (len + 3) >> 2; + p[len] = 0; + p += len; + + /* Send request to server portmapper */ + if ((p = root_nfs_call(p)) == NULL) { + root_nfs_close(1); + return -1; + } + + status = ntohl(*p++); + if (status == 0) { + nfs_data.root = *((struct nfs_fh *) p); + } else { + printk(KERN_ERR "NFS: Server returned error %d while mounting %s\n", + status, nfs_path); + root_nfs_close(1); + return -1; + } + + return 0; +} + + +/* + * Now actually mount the given directory + */ +static int root_nfs_do_mount(struct super_block *sb) +{ + /* First connect to the nfsd port on the server */ + server.sin_port = htons(nfs_port); + nfs_data.addr = server; + if (nfs_inode.u.socket_i.ops->connect && + nfs_inode.u.socket_i.ops->connect(&nfs_inode.u.socket_i, + (struct sockaddr *) &server, sizeof(struct sockaddr_in), + nfs_file.f_flags) < 0) { + root_nfs_close(1); + return -1; + } + + /* Now (finally ;-)) read the super block for mounting */ + if (nfs_read_super(sb, &nfs_data, 1) == NULL) { + root_nfs_close(1); + return -1; + } + + return 0; +} + + +/* + * Get the NFS port numbers and file handle, and then read the super- + * block for mounting. + */ +int nfs_root_mount(struct super_block *sb) +{ + if (root_nfs_open() < 0) + return -1; + if (root_nfs_bind() < 0) + return -1; + if (root_nfs_ports() < 0) + return -1; + if (root_nfs_get_handle() < 0) + return -1; + if (root_nfs_do_mount(sb) < 0) + return -1; + root_nfs_close(0); + return 0; +} diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index ce593760bd6a..72f7b05f5f71 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -818,7 +818,8 @@ retry: * Here are a few RPC-assist functions. */ -static int *nfs_rpc_header(int *p, int procedure, int ruid) +int *rpc_header(int *p, int procedure, int program, int version, + int uid, int gid, int *groups) { int *p1, *p2; int i; @@ -832,18 +833,18 @@ static int *nfs_rpc_header(int *p, int procedure, int ruid) *p++ = htonl(++xid); *p++ = htonl(RPC_CALL); *p++ = htonl(RPC_VERSION); - *p++ = htonl(NFS_PROGRAM); - *p++ = htonl(NFS_VERSION); + *p++ = htonl(program); + *p++ = htonl(version); *p++ = htonl(procedure); *p++ = htonl(RPC_AUTH_UNIX); p1 = p++; *p++ = htonl(CURRENT_TIME); /* traditional, could be anything */ p = xdr_encode_string(p, (char *) sys); - *p++ = htonl(ruid ? current->uid : current->fsuid); - *p++ = htonl(current->egid); + *p++ = htonl(uid); + *p++ = htonl(gid); p2 = p++; - for (i = 0; i < 16 && i < NGROUPS && current->groups[i] != NOGROUP; i++) - *p++ = htonl(current->groups[i]); + for (i = 0; i < 16 && i < NGROUPS && groups[i] != NOGROUP; i++) + *p++ = htonl(groups[i]); *p2 = htonl(i); *p1 = htonl((p - (p1 + 1)) << 2); *p++ = htonl(RPC_AUTH_NULL); @@ -851,7 +852,16 @@ static int *nfs_rpc_header(int *p, int procedure, int ruid) return p; } -static int *nfs_rpc_verify(int *p) + +static int *nfs_rpc_header(int *p, int procedure, int ruid) +{ + return rpc_header(p, procedure, NFS_PROGRAM, NFS_VERSION, + (ruid ? current->uid : current->fsuid), + current->egid, current->groups); +} + + +int *rpc_verify(int *p) { unsigned int n; @@ -882,7 +892,14 @@ static int *nfs_rpc_verify(int *p) } return p; } - + + +static int *nfs_rpc_verify(int *p) +{ + return rpc_verify(p); +} + + /* * We need to translate between nfs status return values and * the local errno values which may not be the same. diff --git a/fs/super.c b/fs/super.c index e6ae81ec451d..245a15cda327 100644 --- a/fs/super.c +++ b/fs/super.c @@ -6,6 +6,8 @@ /* * super.c contains code to handle the super-block tables. + * + * GK 2/5/95 - Changed to support mounting the root fs via NFS */ #include @@ -34,6 +36,10 @@ struct super_block super_blocks[NR_SUPER]; static int do_remount_sb(struct super_block *sb, int flags, char * data); +#ifdef CONFIG_ROOT_NFS +extern int nfs_root_mount(struct super_block *sb); +#endif + /* this is initialized in init/main.c */ kdev_t ROOT_DEV; @@ -677,6 +683,35 @@ void mount_root(void) int retval; memset(super_blocks, 0, sizeof(super_blocks)); +#ifdef CONFIG_ROOT_NFS + if (MAJOR(ROOT_DEV) == UNNAMED_MAJOR) { + ROOT_DEV = 0; + if ((fs_type = get_fs_type("nfs"))) { + sb = &super_blocks[0]; + sb->s_dev = get_unnamed_dev(); + sb->s_flags = root_mountflags & ~MS_RDONLY; + if (nfs_root_mount(sb) >= 0) { + inode = sb->s_mounted; + inode->i_count += 3 ; + 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; + ROOT_DEV = sb->s_dev; + printk (KERN_NOTICE "VFS: Mounted root (nfs filesystem).\n"); + return; + } + sb->s_dev = 0; + } + if (!ROOT_DEV) { + printk(KERN_ERR "VFS: Unable to mount root fs via NFS, trying floppy.\n"); + ROOT_DEV = MKDEV(FLOPPY_MAJOR, 0); + } + } +#endif + #ifdef CONFIG_BLK_DEV_FD if (MAJOR(ROOT_DEV) == FLOPPY_MAJOR) { printk(KERN_NOTICE "VFS: Insert root floppy and press ENTER\n"); diff --git a/include/asm-i386/ioctl.h b/include/asm-i386/ioctl.h index 22f4a20bb225..c75f20ade6b9 100644 --- a/include/asm-i386/ioctl.h +++ b/include/asm-i386/ioctl.h @@ -43,8 +43,8 @@ * Direction bits. */ #define _IOC_NONE 0U -#define _IOC_READ 1U -#define _IOC_WRITE 2U +#define _IOC_WRITE 1U +#define _IOC_READ 2U #define _IOC(dir,type,nr,size) \ (((dir) << _IOC_DIRSHIFT) | \ diff --git a/include/asm-i386/smp.h b/include/asm-i386/smp.h index 32b599b75201..bd8a1f73c8ad 100644 --- a/include/asm-i386/smp.h +++ b/include/asm-i386/smp.h @@ -239,7 +239,7 @@ extern __inline int smp_processor_id(void) * processes are run. */ -#define PROC_CHANGE_PENALTY 5 /* Schedule penalty */ +#define PROC_CHANGE_PENALTY 20 /* Schedule penalty */ #endif #endif diff --git a/include/linux/bios32.h b/include/linux/bios32.h index 263ded786699..f57398e53323 100644 --- a/include/linux/bios32.h +++ b/include/linux/bios32.h @@ -24,7 +24,7 @@ /* * Error values that may be returned by the PCI bios. Use - * pci_strbioserr() to convert to a printable string. + * pcibios_strerror() to convert to a printable string. */ #define PCIBIOS_SUCCESSFUL 0x00 #define PCIBIOS_FUNC_NOT_SUPPORTED 0x81 diff --git a/include/linux/cyclades.h b/include/linux/cyclades.h index 2d12b8dc3c21..6b01e096d939 100644 --- a/include/linux/cyclades.h +++ b/include/linux/cyclades.h @@ -5,8 +5,17 @@ * Randolph Bentson . * * This file contains the general definitions for the cyclades.c driver + *$Log: cyclades.h,v $ + * Revision 1.5 1995/11/13 21:13:31 bentson + * changes suggested by Michael Chastain + * to support use of this file in non-kernel applications + * + * */ +#ifndef _LINUX_CYCLADES_H +#define _LINUX_CYCLADES_H + /* PCI vendor and device ID's */ #ifndef PCI_VENDOR_ID_CYCLADES @@ -17,6 +26,27 @@ #define PCI_DEVICE_ID_CYCLOMY 0x0100 #endif +struct cyclades_monitor { + unsigned long int_count; + unsigned long char_count; + unsigned long char_max; + unsigned long char_last; +}; + +#define CYCLADES_MAGIC 0x4359 + +#define CYGETMON 0x435901 +#define CYGETTHRESH 0x435902 +#define CYSETTHRESH 0x435903 +#define CYGETDEFTHRESH 0x435904 +#define CYSETDEFTHRESH 0x435905 +#define CYGETTIMEOUT 0x435906 +#define CYSETTIMEOUT 0x435907 +#define CYGETDEFTIMEOUT 0x435908 +#define CYSETDEFTIMEOUT 0x435909 + +#ifdef __KERNEL__ + /* Per card data structure */ struct cyclades_card { @@ -31,13 +61,6 @@ struct cyclades_chip { int filler; }; -struct cyclades_monitor { - unsigned long int_count; - unsigned long char_count; - unsigned long char_max; - unsigned long char_last; -}; - /* * This is our internal structure for each serial port's state. * @@ -47,9 +70,6 @@ struct cyclades_monitor { * For definitions of the flags field, see tty.h */ -#include -#include - struct cyclades_port { int magic; int type; @@ -87,18 +107,6 @@ struct cyclades_port { struct cyclades_monitor mon; }; -#define CYCLADES_MAGIC 0x4359 - -#define CYGETMON 0x435901 -#define CYGETTHRESH 0x435902 -#define CYSETTHRESH 0x435903 -#define CYGETDEFTHRESH 0x435904 -#define CYSETDEFTHRESH 0x435905 -#define CYGETTIMEOUT 0x435906 -#define CYSETTIMEOUT 0x435907 -#define CYGETDEFTIMEOUT 0x435908 -#define CYSETDEFTIMEOUT 0x435909 - /* * Events are used to schedule things to happen at timer-interrupt * time, instead of at cy interrupt time. @@ -281,3 +289,6 @@ struct cyclades_port { #define CyMAX_CHAR_FIFO 12 /***************************************************************************/ + +#endif /* __KERNEL__ */ +#endif /* _LINUX_CYCLADES_H */ diff --git a/include/linux/etherdevice.h b/include/linux/etherdevice.h index c5f0d489d23b..9f8b97ccfc66 100644 --- a/include/linux/etherdevice.h +++ b/include/linux/etherdevice.h @@ -34,7 +34,9 @@ extern int eth_header(struct sk_buff *skb, struct device *dev, extern int eth_rebuild_header(void *buff, struct device *dev, unsigned long dst, struct sk_buff *skb); extern unsigned short eth_type_trans(struct sk_buff *skb, struct device *dev); -extern void eth_header_cache(struct device *dev, struct sock *sk, unsigned long saddr, unsigned long daddr); +extern void eth_header_cache_bind(struct hh_cache ** hhp, struct device *dev, + unsigned short htype, __u32 daddr); +extern void eth_header_cache_update(struct hh_cache *hh, struct device *dev, unsigned char * haddr); extern void eth_copy_and_sum(struct sk_buff *dest, unsigned char *src, int length, int base); extern struct device * init_etherdev(struct device *, int); diff --git a/include/linux/fs.h b/include/linux/fs.h index 57af1a48f8a2..1536f6c645db 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -496,8 +496,18 @@ extern void ll_rw_block(int rw, int nr, struct buffer_head * bh[]); extern void ll_rw_page(int rw, kdev_t dev, unsigned long nr, char * buffer); extern void ll_rw_swap_file(int rw, kdev_t dev, unsigned int *b, int nb, char *buffer); extern int is_read_only(kdev_t dev); -extern void brelse(struct buffer_head * buf); -extern void bforget(struct buffer_head * buf); +extern void __brelse(struct buffer_head *buf); +extern inline void brelse(struct buffer_head *buf) +{ + if (buf) + __brelse(buf); +} +extern void __bforget(struct buffer_head *buf); +extern inline void bforget(struct buffer_head *buf) +{ + if (buf) + __bforget(buf); +} extern void set_blocksize(kdev_t dev, int size); extern struct buffer_head * bread(kdev_t dev, int block, int size); extern unsigned long bread_page(unsigned long addr,kdev_t dev,int b[],int size,int no_share); diff --git a/include/linux/if_arp.h b/include/linux/if_arp.h index 0b7dd484214d..34e4c78c8f7b 100644 --- a/include/linux/if_arp.h +++ b/include/linux/if_arp.h @@ -41,6 +41,8 @@ #define ARPHRD_PPP 512 #define ARPHRD_TUNNEL 768 /* IPIP tunnel */ #define ARPHRD_TUNNEL6 769 /* IPIP6 tunnel */ +#define ARPHRD_FRAD 770 /* Frame Relay */ +#define ARPHRD_SKIP 771 /* SKIP vif */ /* ARP protocol opcodes. */ #define ARPOP_REQUEST 1 /* ARP request */ @@ -55,6 +57,14 @@ struct arpreq { struct sockaddr arp_ha; /* hardware address */ int arp_flags; /* flags */ struct sockaddr arp_netmask; /* netmask (only for proxy arps) */ + char arp_dev[16]; +}; + +struct arpreq_old { + struct sockaddr arp_pa; /* protocol address */ + struct sockaddr arp_ha; /* hardware address */ + int arp_flags; /* flags */ + struct sockaddr arp_netmask; /* netmask (only for proxy arps) */ }; /* ARP Flag values. */ diff --git a/include/linux/if_ppp.h b/include/linux/if_ppp.h index 2823b3f607cd..922a2b98370a 100644 --- a/include/linux/if_ppp.h +++ b/include/linux/if_ppp.h @@ -21,7 +21,7 @@ */ /* - * ==FILEVERSION 4== + * ==FILEVERSION 5== * * NOTE TO MAINTAINERS: * If you modify this file at all, increment the number above. @@ -50,6 +50,8 @@ #define PPP_VERSION "2.2.0" #define PPP_MAGIC 0x5002 /* Magic value for the ppp structure */ #define PROTO_IPX 0x002b /* protocol numbers */ +#define PROTO_DNA_RT 0x0027 /* DNA Routing */ + /* * Bit definitions for flags. diff --git a/include/linux/ioport.h b/include/linux/ioport.h index 8a019adb6a16..bbeb596e1077 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h @@ -26,7 +26,7 @@ extern int get_ioport_list(char *); #define HAVE_AUTOIRQ extern void *irq2dev_map[16]; /* Use only if you own the IRQ. */ -extern void autoirq_setup(int waittime); +extern int autoirq_setup(int waittime); extern int autoirq_report(int waittime); #endif /* _LINUX_PORTIO_H */ diff --git a/include/linux/ip_fw.h b/include/linux/ip_fw.h index c96d34f58986..210d86867a2c 100644 --- a/include/linux/ip_fw.h +++ b/include/linux/ip_fw.h @@ -46,6 +46,23 @@ #define _IP_FW_H struct ip_fw +{ + struct ip_fw *fw_next; /* Next firewall on chain */ + struct in_addr fw_src, fw_dst; /* Source and destination IP addr */ + struct in_addr fw_smsk, fw_dmsk; /* Mask for src and dest IP addr */ + struct in_addr fw_via; /* IP address of interface "via" */ + unsigned short fw_flg; /* Flags word */ + unsigned short fw_nsp, fw_ndp; /* N'of src ports and # of dst ports */ + /* in ports array (dst ports follow */ + /* src ports; max of 10 ports in all; */ + /* count of 0 means match all ports) */ +#define IP_FW_MAX_PORTS 10 /* A reasonable maximum */ + unsigned short fw_pts[IP_FW_MAX_PORTS]; /* Array of port numbers to match */ + unsigned long fw_pcnt,fw_bcnt; /* Packet and byte counters */ + unsigned short fw_priority; /* Revised packet priority */ +}; + +struct ip_fw_old { struct ip_fw *fw_next; /* Next firewall on chain */ struct in_addr fw_src, fw_dst; /* Source and destination IP addr */ diff --git a/include/linux/major.h b/include/linux/major.h index a5d9ccb9d824..bc68a82e13c1 100644 --- a/include/linux/major.h +++ b/include/linux/major.h @@ -50,6 +50,7 @@ * 32 - philips/lms cm206 cdrom * 33 - ide2 * 34 - z8530 driver ide3 + * 36 - netlink */ #define UNNAMED_MAJOR 0 @@ -97,6 +98,7 @@ #define CM206_CDROM_MAJOR 32 #define IDE2_MAJOR 33 #define IDE3_MAJOR 34 +#define NETLINK_MAJOR 36 /* * Tests for SCSI devices. diff --git a/include/linux/mm.h b/include/linux/mm.h index a9a9f0b708ce..3c4e3c2f9f02 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -215,8 +215,6 @@ extern void rw_swap_page(int rw, unsigned long nr, char * buf); /* mmap.c */ extern unsigned long do_mmap(struct file * file, unsigned long addr, unsigned long len, unsigned long prot, unsigned long flags, unsigned long off); -extern struct vm_area_struct * find_vma (struct task_struct *, unsigned long); -extern struct vm_area_struct * find_vma_intersection (struct task_struct *, unsigned long, unsigned long); extern void merge_segments(struct task_struct *, unsigned long, unsigned long); extern void insert_vm_struct(struct task_struct *, struct vm_area_struct *); extern void remove_shared_vm_struct(struct vm_area_struct *); @@ -244,6 +242,40 @@ extern unsigned long get_unmapped_area(unsigned long, unsigned long); #define GFP_LEVEL_MASK 0xf +#define avl_empty (struct vm_area_struct *) NULL + +/* Look up the first VMA which satisfies addr < vm_end, NULL if none. */ +static inline struct vm_area_struct * find_vma (struct task_struct * task, unsigned long addr) +{ + struct vm_area_struct * result = NULL; + struct vm_area_struct * tree; + + if (!task->mm) + return NULL; + for (tree = task->mm->mmap_avl ; ; ) { + if (tree == avl_empty) + return result; + if (tree->vm_end > addr) { + if (tree->vm_start <= addr) + return tree; + result = tree; + tree = tree->vm_avl_left; + } else + tree = tree->vm_avl_right; + } +} + +/* Look up the first VMA which intersects the interval start_addr..end_addr-1, + NULL if none. Assume start_addr < end_addr. */ +static inline struct vm_area_struct * find_vma_intersection (struct task_struct * task, unsigned long start_addr, unsigned long end_addr) +{ + struct vm_area_struct * vma; + + vma = find_vma(task,start_addr); + if (!vma || end_addr <= vma->vm_start) + return NULL; + return vma; +} /* * vm_ops not present page codes for shared memory. diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index f0cbcf70dab2..8fbd37658c47 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -24,12 +24,11 @@ #ifndef _LINUX_NETDEVICE_H #define _LINUX_NETDEVICE_H +#include #include #include #include -#include - /* for future expansion when we will have different priorities. */ #define DEV_NUMBUFFS 3 #define MAX_ADDR_LEN 7 @@ -61,6 +60,19 @@ struct dev_mc_list unsigned short dmi_users; }; +struct hh_cache +{ + struct hh_cache *hh_next; + unsigned long hh_refcnt; /* number of users */ + void *hh_arp; /* Opaque pointer, used by + * any address resolution module, + * not only ARP. + */ + unsigned short hh_type; /* protocol identifier, f.e ETH_P_IP */ + char hh_uptodate; /* hh_data is valid */ + char hh_data[16]; /* cached hardware header */ +}; + /* * The DEVICE structure. * Actually, this whole structure is a big mistake. It mixes I/O @@ -162,12 +174,14 @@ struct device void (*set_multicast_list)(struct device *dev, int num_addrs, void *addrs); #define HAVE_SET_MAC_ADDR - int (*set_mac_address)(struct device *dev, struct sockaddr *addr); + int (*set_mac_address)(struct device *dev, void *addr); #define HAVE_PRIVATE_IOCTL int (*do_ioctl)(struct device *dev, struct ifreq *ifr, int cmd); #define HAVE_SET_CONFIG int (*set_config)(struct device *dev, struct ifmap *map); - void (*header_cache)(struct device *dev, struct sock *sk, unsigned long saddr, unsigned long daddr); +#define HAVE_HEADER_CACHE + void (*header_cache_bind)(struct hh_cache **hhp, struct device *dev, unsigned short htype, __u32 daddr); + void (*header_cache_update)(struct hh_cache *hh, struct device *dev, unsigned char * haddr); }; @@ -201,6 +215,7 @@ extern struct device *ip_dev_check(unsigned long daddr); extern unsigned long ip_my_addr(void); extern unsigned long ip_get_mask(unsigned long addr); extern struct device *ip_dev_find(unsigned long addr); +extern struct device *dev_getbytype(unsigned short type); extern void dev_add_pack(struct packet_type *pt); extern void dev_remove_pack(struct packet_type *pt); diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 2ff70c025ac3..b30246c2ac1d 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -86,6 +86,9 @@ extern int nfs_proc_readdir(struct nfs_server *server, struct nfs_fh *fhandle, int cookie, int count, struct nfs_entry *entry); extern int nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsinfo *res); +extern int *rpc_header(int *p, int procedure, int program, int version, + int uid, int gid, int *groups); +extern int *rpc_verify(int *p); /* linux/fs/nfs/sock.c */ diff --git a/include/linux/nfs_mount.h b/include/linux/nfs_mount.h index beb5f1dd845f..b122b43dc0b7 100644 --- a/include/linux/nfs_mount.h +++ b/include/linux/nfs_mount.h @@ -17,7 +17,19 @@ * but here they are anyway. */ -#define NFS_MOUNT_VERSION 1 /* current version */ +#define NFS_NFS_PROGRAM 100003 /* nfsd program number */ +#define NFS_NFS_VERSION 2 /* nfsd version */ +#define NFS_NFS_PORT 2049 /* portnumber on server for nfsd */ + +#define NFS_MOUNT_PROGRAM 100005 /* mountd program number */ +#define NFS_MOUNT_VERSION 1 /* mountd version */ +#define NFS_MOUNT_PROC 1 /* mount process id */ +#define NFS_MOUNT_PORT 627 /* portnumber on server for mountd */ + +#define NFS_PMAP_PROGRAM 100000 /* portmap program number */ +#define NFS_PMAP_VERSION 2 /* portmap version */ +#define NFS_PMAP_PROC 3 /* portmap getport id */ +#define NFS_PMAP_PORT 111 /* portnumber on server for portmap */ struct nfs_mount_data { int version; /* 1 */ diff --git a/include/linux/pci.h b/include/linux/pci.h index 06df6d4a545d..0c6c8063d35e 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -541,7 +541,6 @@ extern unsigned long pci_init (unsigned long mem_start, unsigned long mem_end); extern struct pci_dev_info *pci_lookup_dev (unsigned int vendor, unsigned int dev); -extern const char *pci_strbioserr (int error); extern const char *pci_strclass (unsigned int class); extern const char *pci_strvendor (unsigned int vendor); extern const char *pci_strdev (unsigned int vendor, unsigned int device); diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index bcc0b96c5c17..9716ac786c14 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h @@ -86,6 +86,7 @@ enum net_directory_inos { PROC_NET_NR_NEIGH, PROC_NET_NR, PROC_NET_SOCKSTAT, + PROC_NET_RTCACHE, PROC_NET_LAST }; diff --git a/include/linux/sockios.h b/include/linux/sockios.h index d067fe1734a6..a93fa3068fd8 100644 --- a/include/linux/sockios.h +++ b/include/linux/sockios.h @@ -54,9 +54,12 @@ /* end multicast support change */ /* ARP cache control calls. */ -#define SIOCDARP 0x8950 /* delete ARP table entry */ -#define SIOCGARP 0x8951 /* get ARP table entry */ -#define SIOCSARP 0x8952 /* set ARP table entry */ +#define OLD_SIOCDARP 0x8950 /* old delete ARP table entry */ +#define OLD_SIOCGARP 0x8951 /* old get ARP table entry */ +#define OLD_SIOCSARP 0x8952 /* old set ARP table entry */ +#define SIOCDARP 0x8953 /* delete ARP table entry */ +#define SIOCGARP 0x8954 /* get ARP table entry */ +#define SIOCSARP 0x8955 /* set ARP table entry */ /* RARP cache control calls. */ #define SIOCDRARP 0x8960 /* delete RARP table entry */ diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 521d20e923d5..5805203ea30b 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -17,10 +17,6 @@ #ifndef _LINUX_TCP_H #define _LINUX_TCP_H - -#define HEADER_SIZE 128 /* maximum header size */ - - struct tcphdr { __u16 source; __u16 dest; diff --git a/include/net/arp.h b/include/net/arp.h index 293e7c6a5d32..db7a29c36f5b 100644 --- a/include/net/arp.h +++ b/include/net/arp.h @@ -3,19 +3,15 @@ #define _ARP_H extern void arp_init(void); -extern void arp_destroy(u32 paddr, int force); -extern void arp_device_down(struct device *dev); extern int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt); -extern int arp_query(unsigned char *haddr, u32 paddr, unsigned short type); +extern int arp_query(unsigned char *haddr, u32 paddr, struct device *dev); extern int arp_find(unsigned char *haddr, u32 paddr, struct device *dev, u32 saddr, struct sk_buff *skb); -extern int arp_get_info(char *buffer, char **start, off_t origin, int length, int dummy); extern int arp_ioctl(unsigned int cmd, void *arg); extern void arp_send(int type, int ptype, u32 dest_ip, struct device *dev, u32 src_ip, - unsigned char *dest_hw, unsigned char *src_hw); -extern int arp_find_cache(unsigned char *dp, u32 daddr, struct device *dev); - -extern unsigned long arp_cache_stamp; + unsigned char *dest_hw, unsigned char *src_hw, unsigned char *th); +extern int arp_bind_cache(struct hh_cache ** hhp, struct device *dev, unsigned short type, __u32 daddr); +extern int arp_update_cache(struct hh_cache * hh); #endif /* _ARP_H */ diff --git a/include/net/ip.h b/include/net/ip.h index e23afaad310a..88dbe05ad97d 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -25,6 +25,7 @@ #include #include #include +#include #ifndef _SNMP_H #include @@ -84,13 +85,13 @@ struct ipq extern void ip_print(const struct iphdr *ip); extern int ip_ioctl(struct sock *sk, int cmd, unsigned long arg); extern void ip_route_check(__u32 daddr); -extern int ip_send(struct sk_buff *skb, __u32 daddr, int len, struct device *dev, __u32 saddr); +extern int ip_send(struct rtable *rt, struct sk_buff *skb, __u32 daddr, int len, struct device *dev, __u32 saddr); extern int ip_build_header(struct sk_buff *skb, __u32 saddr, __u32 daddr, struct device **dev, int type, struct options *opt, int len, - int tos,int ttl); + int tos,int ttl,struct rtable **rp); extern int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt); extern int ip_options_echo(struct options * dopt, struct options * sopt, diff --git a/include/net/netlink.h b/include/net/netlink.h index a785321a6017..27db2d1a5f0d 100644 --- a/include/net/netlink.h +++ b/include/net/netlink.h @@ -1,12 +1,12 @@ #ifndef __NET_NETLINK_H #define __NET_NETLINK_H -#include - #define NET_MAJOR 18 /* Major 18 is reserved for networking */ #define MAX_LINKS 3 /* 18,0 for route updates, 18,1 for SKIP */ #define MAX_QBYTES 32768 /* Maximum bytes in the queue */ +#include + extern int netlink_attach(int unit, int (*function)(struct sk_buff *skb)); extern int netlink_donothing(struct sk_buff *skb); extern void netlink_detach(int unit); diff --git a/include/net/route.h b/include/net/route.h index 0ca393b962dc..48001b0dc8d7 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -12,6 +12,11 @@ * Fixes: * Alan Cox : Reformatted. Added ip_rt_local() * Alan Cox : Support for TCP parameters. + * Alexey Kuznetsov: Major changes for new routing code. + * + * FIXME: + * Modules stuff is broken at the moment. + * Make atomic ops more generic and hide them in asm/... * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -21,36 +26,203 @@ #ifndef _ROUTE_H #define _ROUTE_H +/* + * 0 - no debugging messages + * 1 - rare events and bugs situations (default) + * 2 - trace mode. + */ +#define RT_CACHE_DEBUG 1 + +#define RT_HASH_DIVISOR 256 +#define RT_CACHE_SIZE_MAX 256 + +#define RTZ_HASH_DIVISOR 256 + +#if RT_CACHE_DEBUG >= 2 +#define RTZ_HASHING_LIMIT 0 +#else +#define RTZ_HASHING_LIMIT 16 +#endif + +/* + * Maximal time to live for unused entry. + */ +#define RT_CACHE_TIMEOUT (HZ*300) + +/* + * Prevents LRU trashing, entries considered equivalent, + * if the difference between last use times is less then this number. + */ +#define RT_CACHE_BUBBLE_THRESHOULD (HZ*5) #include +#ifdef __KERNEL__ +#define RTF_LOCAL 0x8000 +#endif + +/* + * Semaphores. + */ + +#ifdef __i386__ +#include + +extern __inline__ void ATOMIC_INCR(void * addr) +{ + __asm__ __volatile__( + "incl %0" + :"=m" (ADDR)); +} + +extern __inline__ void ATOMIC_DECR(void * addr) +{ + __asm__ __volatile__( + "decl %0" + :"=m" (ADDR)); +} + +/* + * It is DECR that is ATOMIC, not CHECK! + * If you want to do atomic checks, use cli()/sti(). --ANK + */ + +extern __inline__ unsigned long ATOMIC_DECR_AND_CHECK(void * addr) +{ + unsigned long retval; + __asm__ __volatile__( + "decl %0\nmovl %0,%1" + : "=m" (ADDR), "=r"(retval)); + return retval; +} + + +#else + +static __inline__ void ATOMIC_INCR(void * addr) +{ + (*(__volatile__ unsigned long*)addr)++; +} + +static __inline__ void ATOMIC_DECR(void * addr) +{ + (*(__volatile__ unsigned long*)addr)--; +} + +static __inline__ int ATOMIC_DECR_AND_CHECK (void * addr) +{ + ATOMIC_DECR(addr); + return *(volatile unsigned long*)addr; +} + +#endif + + -/* This is an entry in the IP routing table. */ struct rtable { struct rtable *rt_next; __u32 rt_dst; - __u32 rt_mask; + __u32 rt_src; __u32 rt_gateway; - unsigned short rt_flags; - short rt_metric; - unsigned int rt_refcnt; + unsigned long rt_refcnt; unsigned long rt_use; - unsigned short rt_mss; - unsigned short rt_irtt; unsigned long rt_window; + unsigned long rt_lastuse; + struct hh_cache *rt_hh; struct device *rt_dev; + unsigned short rt_flags; + unsigned short rt_mtu; + unsigned short rt_irtt; + unsigned char rt_tos; }; - extern void ip_rt_flush(struct device *dev); -extern void ip_rt_add(short flags, __u32 addr, __u32 mask, - __u32 gw, struct device *dev, unsigned short mss, unsigned long window, unsigned short irtt, short metric); -extern struct rtable *ip_rt_route(__u32 daddr, struct options *opt, __u32 *src_addr); -extern struct rtable *ip_rt_local(__u32 daddr, struct options *opt, __u32 *src_addr); +extern void ip_rt_redirect(__u32 src, __u32 dst, __u32 gw, struct device *dev); +extern struct rtable *ip_rt_slow_route(__u32 daddr, int local); extern int rt_get_info(char * buffer, char **start, off_t offset, int length, int dummy); +extern int rt_cache_get_info(char *buffer, char **start, off_t offset, int length, int dummy); extern int ip_rt_ioctl(unsigned int cmd, void *arg); +extern int ip_rt_new(struct rtentry *rt); +extern void ip_rt_check_expire(void); +extern void ip_rt_advice(struct rtable **rp, int advice); + +extern void ip_rt_run_bh(void); +extern int ip_rt_lock; +extern unsigned ip_rt_bh_mask; +extern struct rtable *ip_rt_hash_table[RT_HASH_DIVISOR]; + +extern __inline__ void ip_rt_fast_lock(void) +{ + ATOMIC_INCR(&ip_rt_lock); +} + +extern __inline__ void ip_rt_fast_unlock(void) +{ + ATOMIC_DECR(&ip_rt_lock); +} + +extern __inline__ void ip_rt_unlock(void) +{ + if (!ATOMIC_DECR_AND_CHECK(&ip_rt_lock) && ip_rt_bh_mask) + ip_rt_run_bh(); +} + +extern __inline__ unsigned ip_rt_hash_code(__u32 addr) +{ + unsigned tmp = addr + (addr>>16); + return (tmp + (tmp>>8)) & 0xFF; +} + + +extern __inline__ void ip_rt_put(struct rtable * rt) +#ifndef MODULE +{ + if (rt) + ATOMIC_DECR(&rt->rt_refcnt); +} +#else +; +#endif + +extern __inline__ struct rtable * ip_rt_route(__u32 daddr, int local) +#ifndef MODULE +{ + struct rtable * rth; + + ip_rt_fast_lock(); + + for (rth=ip_rt_hash_table[ip_rt_hash_code(daddr)^local]; rth; rth=rth->rt_next) + { + if (rth->rt_dst == daddr) + { + rth->rt_lastuse = jiffies; + ATOMIC_INCR(&rth->rt_use); + ATOMIC_INCR(&rth->rt_refcnt); + ip_rt_unlock(); + return rth; + } + } + return ip_rt_slow_route (daddr, local); +} +#else +; +#endif + +extern __inline__ struct rtable * ip_check_route(struct rtable ** rp, + __u32 daddr, int local) +{ + struct rtable * rt = *rp; + + if (!rt || rt->rt_dst != daddr || !(rt->rt_flags&RTF_UP) + || ((local==1)^((rt->rt_flags&RTF_LOCAL) != 0))) + { + ip_rt_put(rt); + rt = ip_rt_route(daddr, local); + *rp = rt; + } + return rt; +} -extern unsigned long rt_stamp; #endif /* _ROUTE_H */ diff --git a/include/net/sock.h b/include/net/sock.h index 5964051d684b..cb3d5f1e2634 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -37,6 +37,7 @@ #include /* struct tcphdr */ #include +#include #include /* struct sk_buff */ #include /* struct inet_protocol */ #ifdef CONFIG_AX25 @@ -226,19 +227,6 @@ struct sock struct timer_list ack_timer; /* TCP delayed ack timer */ int ip_xmit_timeout; /* Why the timeout is running */ struct rtable *ip_route_cache; /* Cached output route */ - unsigned long ip_route_stamp; /* Route cache stamp */ - unsigned long ip_route_daddr; /* Target address */ - unsigned long ip_route_saddr; /* Source address */ - int ip_route_local; /* State of locality flag */ - unsigned long ip_hcache_stamp; /* Header cache stamp */ - unsigned long *ip_hcache_ver; /* Pointer to version of cache */ - char ip_hcache_data[16]; /* Cached header */ - int ip_hcache_state; /* Have we a cached header */ - unsigned char ip_option_len; /* Length of IP options */ - unsigned char ip_option_flen; /* Second fragment option length */ - unsigned char ip_opt_next_strict; /* Next hop is strict route */ - unsigned long ip_opt_next_hop; /* Next hop if forced */ - unsigned char *ip_opt_ptr[2]; /* IP option pointers */ unsigned char ip_hdrincl; /* Include headers ? */ #ifdef CONFIG_IP_MULTICAST int ip_mc_ttl; /* Multicasting TTL */ @@ -286,7 +274,7 @@ struct proto __u32 daddr, struct device **dev, int type, struct options *opt, int len, - int tos, int ttl); + int tos, int ttl, struct rtable ** rp); int (*connect)(struct sock *sk, struct sockaddr_in *usin, int addr_len); struct sock * (*accept) (struct sock *sk, int flags); diff --git a/init/main.c b/init/main.c index 1b6c90be4438..8ef1fb3a5500 100644 --- a/init/main.c +++ b/init/main.c @@ -2,6 +2,8 @@ * linux/init/main.c * * Copyright (C) 1991, 1992 Linus Torvalds + * + * GK 2/5/95 - Changed to support mounting root fs via NFS */ #define __KERNEL_SYSCALLS__ @@ -26,6 +28,7 @@ #include #include #include +#include #include @@ -61,7 +64,7 @@ extern void generic_NCR5380_setup(char *str, int *intr); extern void aha152x_setup(char *str, int *ints); extern void aha1542_setup(char *str, int *ints); extern void aic7xxx_setup(char *str, int *ints); -extern void buslogic_setup(char *str, int *ints); +extern void BusLogic_Setup(char *str, int *ints); extern void fdomain_setup(char *str, int *ints); extern void NCR53c406a_setup(char *str, int *ints); extern void scsi_luns_setup(char *str, int *ints); @@ -118,6 +121,11 @@ int rows, cols; int ramdisk_size; int root_mountflags = MS_RDONLY; +#ifdef CONFIG_ROOT_NFS +char nfs_root_name[256] = { 0, }; +extern int nfs_root_init(char *name); +#endif + static char * argv_init[MAX_INIT_ARGS+2] = { "init", NULL, }; static char * envp_init[MAX_INIT_ENVS+2] = { "HOME=/", "TERM=linux", NULL, }; @@ -204,7 +212,7 @@ struct { { "aic7xxx=", aic7xxx_setup}, #endif #ifdef CONFIG_SCSI_BUSLOGIC - { "buslogic=", buslogic_setup}, + { "BusLogic=", BusLogic_Setup}, #endif #ifdef CONFIG_SCSI_NCR53C406A { "ncr53c406a=", NCR53c406a_setup}, @@ -297,9 +305,8 @@ void calibrate_delay(void) int ticks; int loopbit; int lps_precision = LPS_PREC; -#ifdef __SMP__ + loops_per_sec = (1<<12); -#endif printk("Calibrating delay loop.. "); while (loops_per_sec <<= 1) { @@ -350,10 +357,14 @@ void calibrate_delay(void) static void parse_options(char *line) { char *next; +#ifdef CONFIG_ROOT_NFS + char *devnames[] = { "nfs", "hda", "hdb", "hdc", "hdd", "sda", "sdb", "sdc", "sdd", "sde", "fd", "xda", "xdb", NULL }; + int devnums[] = { 0x0FF, 0x300, 0x340, 0x1600, 0x1640, 0x800, 0x810, 0x820, 0x830, 0x840, 0x200, 0xD00, 0xD40, 0}; +#else static const char *devnames[] = { "hda", "hdb", "hdc", "hdd", "sda", "sdb", "sdc", "sdd", "sde", "fd", "xda", "xdb", NULL }; static int devnums[] = { 0x300, 0x340, 0x1600, 0x1640, 0x800, 0x810, 0x820, 0x830, 0x840, 0x200, 0xD00, 0xD40, 0}; +#endif int args, envs; - if (!*line) return; args = 0; @@ -384,6 +395,20 @@ static void parse_options(char *line) } continue; } +#ifdef CONFIG_ROOT_NFS + if (!strncmp(line,"nfsroot=",8)) { + int n = 255 - strlen (NFS_ROOT); + line += 8; + if (line [0] == '/' || (line [0] >= '0' && line [0] <= '9')){ + strncpy (nfs_root_name, line, 255); + continue; + } + if (strlen (line) >= n) + line [n] = 0; + sprintf (nfs_root_name, NFS_ROOT, line); + continue; + } +#endif if (!strcmp(line,"ro")) { root_mountflags |= MS_RDONLY; continue; diff --git a/kernel/exit.c b/kernel/exit.c index 73c950c79efd..2f39057ff624 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -332,7 +332,7 @@ int is_orphaned_pgrp(int pgrp) return(1); /* (sighing) "Often!" */ } -static int has_stopped_jobs(int pgrp) +static inline int has_stopped_jobs(int pgrp) { struct task_struct * p; @@ -345,7 +345,7 @@ static int has_stopped_jobs(int pgrp) return(0); } -static void forget_original_parent(struct task_struct * father) +static inline void forget_original_parent(struct task_struct * father) { struct task_struct * p; @@ -358,7 +358,7 @@ static void forget_original_parent(struct task_struct * father) } } -void exit_files(struct task_struct *tsk) +static inline void __exit_files(struct task_struct *tsk) { struct files_struct * files = tsk->files; @@ -378,7 +378,12 @@ void exit_files(struct task_struct *tsk) } } -void exit_fs(struct task_struct *tsk) +void exit_files(struct task_struct *tsk) +{ + __exit_files(tsk); +} + +static inline void __exit_fs(struct task_struct *tsk) { struct fs_struct * fs = tsk->fs; @@ -392,7 +397,12 @@ void exit_fs(struct task_struct *tsk) } } -void exit_sighand(struct task_struct *tsk) +void exit_fs(struct task_struct *tsk) +{ + __exit_fs(tsk); +} + +static inline void __exit_sighand(struct task_struct *tsk) { struct signal_struct * sig = tsk->sig; @@ -404,7 +414,12 @@ void exit_sighand(struct task_struct *tsk) } } -static void exit_mm(void) +void exit_sighand(struct task_struct *tsk) +{ + __exit_sighand(tsk); +} + +static inline void exit_mm(void) { struct mm_struct * mm = current->mm; @@ -497,9 +512,9 @@ fake_volatile: del_timer(¤t->real_timer); sem_exit(); exit_mm(); - exit_files(current); - exit_fs(current); - exit_sighand(current); + __exit_files(current); + __exit_fs(current); + __exit_sighand(current); exit_thread(); exit_notify(); current->state = TASK_ZOMBIE; @@ -544,6 +559,11 @@ asmlinkage int sys_wait4(pid_t pid,unsigned int * stat_addr, int options, struct if (flag) return flag; } + if (ru) { + flag = verify_area(VERIFY_WRITE, ru, sizeof(*ru)); + if (flag) + return flag; + } add_wait_queue(¤t->wait_chldexit,&wait); repeat: flag=0; @@ -568,12 +588,12 @@ repeat: continue; if (!(options & WUNTRACED) && !(p->flags & PF_PTRACED)) continue; + if (ru != NULL) + getrusage(p, RUSAGE_BOTH, ru); if (stat_addr) put_user((p->exit_code << 8) | 0x7f, stat_addr); p->exit_code = 0; - if (ru != NULL) - getrusage(p, RUSAGE_BOTH, ru); retval = p->pid; goto end_wait4; case TASK_ZOMBIE: @@ -581,9 +601,9 @@ repeat: current->cstime += p->stime + p->cstime; if (ru != NULL) getrusage(p, RUSAGE_BOTH, ru); - flag = p->pid; if (stat_addr) put_user(p->exit_code, stat_addr); + retval = p->pid; if (p->p_opptr != p->p_pptr) { REMOVE_LINKS(p); p->p_pptr = p->p_opptr; @@ -594,7 +614,6 @@ repeat: #ifdef DEBUG_PROC_TREE audit_ptree(); #endif - retval = flag; goto end_wait4; default: continue; diff --git a/kernel/fork.c b/kernel/fork.c index 22f5aba0b09f..9388432d7dcf 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -29,7 +29,7 @@ int nr_tasks=1; int nr_running=1; -static int find_empty_process(void) +static inline int find_empty_process(void) { int i; struct task_struct *p; @@ -75,7 +75,7 @@ repeat: return last_pid; } -static int dup_mmap(struct mm_struct * mm) +static inline int dup_mmap(struct mm_struct * mm) { struct vm_area_struct * mpnt, **p, *tmp; @@ -110,7 +110,7 @@ static int dup_mmap(struct mm_struct * mm) return 0; } -static int copy_mm(unsigned long clone_flags, struct task_struct * tsk) +static inline int copy_mm(unsigned long clone_flags, struct task_struct * tsk) { if (clone_flags & CLONE_VM) { SET_PAGE_DIR(tsk, current->mm->pgd); @@ -134,7 +134,7 @@ static int copy_mm(unsigned long clone_flags, struct task_struct * tsk) return 0; } -static int copy_fs(unsigned long clone_flags, struct task_struct * tsk) +static inline int copy_fs(unsigned long clone_flags, struct task_struct * tsk) { if (clone_flags & CLONE_FS) { current->fs->count++; @@ -152,7 +152,7 @@ static int copy_fs(unsigned long clone_flags, struct task_struct * tsk) return 0; } -static int copy_files(unsigned long clone_flags, struct task_struct * tsk) +static inline int copy_files(unsigned long clone_flags, struct task_struct * tsk) { int i; @@ -175,7 +175,7 @@ static int copy_files(unsigned long clone_flags, struct task_struct * tsk) return 0; } -static int copy_sighand(unsigned long clone_flags, struct task_struct * tsk) +static inline int copy_sighand(unsigned long clone_flags, struct task_struct * tsk) { if (clone_flags & CLONE_SIGHAND) { current->sig->count++; diff --git a/kernel/ksyms.c b/kernel/ksyms.c index 8d12332c8e94..0fb500d77783 100644 --- a/kernel/ksyms.c +++ b/kernel/ksyms.c @@ -126,7 +126,7 @@ extern void (* iABI_hook)(struct pt_regs * regs); struct symbol_table symbol_table = { #include -#ifdef CONFIG_MODVERSIONS +#ifdef MODVERSIONS { (void *)1 /* Version version :-) */, SYMBOL_NAME_STR (Using_Versions) }, #endif @@ -226,7 +226,8 @@ struct symbol_table symbol_table = { X(getblk), X(bread), X(breada), - X(brelse), + X(__brelse), + X(__bforget), X(ll_rw_block), X(__wait_on_buffer), X(dcache_lookup), @@ -387,6 +388,7 @@ struct symbol_table symbol_table = { defined(CONFIG_HPLAN) || defined(CONFIG_AC3200) /* If 8390 NIC support is built in, we will need these. */ X(ei_open), + X(ei_close), X(ei_debug), X(ei_interrupt), X(ethdev_init), diff --git a/kernel/signal.c b/kernel/signal.c index 5bcae597f500..53f7d09f9e4a 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -90,7 +90,7 @@ asmlinkage int sys_sigpending(sigset_t *set) * isn't actually ignored, but does automatic child reaping, while * SIG_DFL is explicitly said by POSIX to force the signal to be ignored.. */ -static void check_pending(int signum) +static inline void check_pending(int signum) { struct sigaction *p; diff --git a/mm/mmap.c b/mm/mmap.c index 11a6c1f49e87..c7605b1aab6a 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -17,7 +17,16 @@ #include #include -static int anon_map(struct inode *, struct file *, struct vm_area_struct *); +/* + * Map memory not associated with any file into a process + * address space. Adjacent memory is merged. + */ +static inline int anon_map(struct inode *ino, struct file * file, struct vm_area_struct * vma) +{ + if (zeromap_page_range(vma->vm_start, vma->vm_end - vma->vm_start, vma->vm_page_prot)) + return -ENOMEM; + return 0; +} /* * description of effects of mapping type and prot in current implementation. @@ -212,7 +221,6 @@ unsigned long get_unmapped_area(unsigned long addr, unsigned long len) * vm_avl_height 1+max(heightof(left),heightof(right)) * The empty tree is represented as NULL. */ -#define avl_empty (struct vm_area_struct *) NULL /* Since the trees are balanced, their height will never be large. */ #define avl_maxheight 41 /* why this? a small exercise */ @@ -225,64 +233,8 @@ unsigned long get_unmapped_area(unsigned long addr, unsigned long len) * foreach node in tree->vm_avl_right: node->vm_avl_key >= tree->vm_avl_key. */ -/* Look up the first VMA which satisfies addr < vm_end, NULL if none. */ -struct vm_area_struct * find_vma (struct task_struct * task, unsigned long addr) -{ -#if 0 /* equivalent, but slow */ - struct vm_area_struct * vma; - - if (!task->mm) - return NULL; - for (vma = task->mm->mmap ; ; vma = vma->vm_next) { - if (!vma) - return NULL; - if (vma->vm_end > addr) - return vma; - } -#else - struct vm_area_struct * result = NULL; - struct vm_area_struct * tree; - - if (!task->mm) - return NULL; - for (tree = task->mm->mmap_avl ; ; ) { - if (tree == avl_empty) - return result; - if (tree->vm_end > addr) { - if (tree->vm_start <= addr) - return tree; - result = tree; - tree = tree->vm_avl_left; - } else - tree = tree->vm_avl_right; - } -#endif -} - -/* Look up the first VMA which intersects the interval start_addr..end_addr-1, - NULL if none. Assume start_addr < end_addr. */ -struct vm_area_struct * find_vma_intersection (struct task_struct * task, unsigned long start_addr, unsigned long end_addr) -{ - struct vm_area_struct * vma; - -#if 0 /* equivalent, but slow */ - for (vma = task->mm->mmap; vma; vma = vma->vm_next) { - if (end_addr <= vma->vm_start) - break; - if (start_addr < vma->vm_end) - return vma; - } - return NULL; -#else - vma = find_vma(task,start_addr); - if (!vma || end_addr <= vma->vm_start) - return NULL; - return vma; -#endif -} - /* Look up the nodes at the left and at the right of a given node. */ -static void avl_neighbours (struct vm_area_struct * node, struct vm_area_struct * tree, struct vm_area_struct ** to_the_left, struct vm_area_struct ** to_the_right) +static inline void avl_neighbours (struct vm_area_struct * node, struct vm_area_struct * tree, struct vm_area_struct ** to_the_left, struct vm_area_struct ** to_the_right) { vm_avl_key_t key = node->vm_avl_key; @@ -328,7 +280,7 @@ static void avl_neighbours (struct vm_area_struct * node, struct vm_area_struct * nodes[0]..nodes[k-1] such that * nodes[0] is the root and nodes[i+1] = nodes[i]->{vm_avl_left|vm_avl_right}. */ -static void avl_rebalance (struct vm_area_struct *** nodeplaces_ptr, int count) +static inline void avl_rebalance (struct vm_area_struct *** nodeplaces_ptr, int count) { for ( ; count > 0 ; count--) { struct vm_area_struct ** nodeplace = *--nodeplaces_ptr; @@ -405,7 +357,7 @@ static void avl_rebalance (struct vm_area_struct *** nodeplaces_ptr, int count) } /* Insert a node into a tree. */ -static void avl_insert (struct vm_area_struct * new_node, struct vm_area_struct ** ptree) +static inline void avl_insert (struct vm_area_struct * new_node, struct vm_area_struct ** ptree) { vm_avl_key_t key = new_node->vm_avl_key; struct vm_area_struct ** nodeplace = ptree; @@ -432,7 +384,7 @@ static void avl_insert (struct vm_area_struct * new_node, struct vm_area_struct /* Insert a node into a tree, and * return the node to the left of it and the node to the right of it. */ -static void avl_insert_neighbours (struct vm_area_struct * new_node, struct vm_area_struct ** ptree, +static inline void avl_insert_neighbours (struct vm_area_struct * new_node, struct vm_area_struct ** ptree, struct vm_area_struct ** to_the_left, struct vm_area_struct ** to_the_right) { vm_avl_key_t key = new_node->vm_avl_key; @@ -462,7 +414,7 @@ static void avl_insert_neighbours (struct vm_area_struct * new_node, struct vm_a } /* Removes a node out of a tree. */ -static void avl_remove (struct vm_area_struct * node_to_delete, struct vm_area_struct ** ptree) +static inline void avl_remove (struct vm_area_struct * node_to_delete, struct vm_area_struct ** ptree) { vm_avl_key_t key = node_to_delete->vm_avl_key; struct vm_area_struct ** nodeplace = ptree; @@ -958,14 +910,3 @@ void merge_segments (struct task_struct * task, unsigned long start_addr, unsign mpnt = prev; } } - -/* - * Map memory not associated with any file into a process - * address space. Adjacent memory is merged. - */ -static int anon_map(struct inode *ino, struct file * file, struct vm_area_struct * vma) -{ - if (zeromap_page_range(vma->vm_start, vma->vm_end - vma->vm_start, vma->vm_page_prot)) - return -ENOMEM; - return 0; -} diff --git a/net/Changes b/net/Changes index b184ba4c2a77..d0c4b9f5a605 100644 --- a/net/Changes +++ b/net/Changes @@ -262,7 +262,7 @@ o IPX uses sock_alloc_send_skb [TESTED] o Recvmsg for all IP, sendmsg for TCP [IN] (nearly ready to go all *msg()) --------->>>>> 1.3.38 <<<<<<-------- +-------->>>>> 1.3.41 <<<<<<-------- o ip udp/raw nonblock bug fixed [TESTED] o ICMP lockup fix [IN] @@ -271,7 +271,15 @@ o bind() for SOCK_PACKET [IN] o set_mac_addr fixed up [IN] o BSD SIOCSIFADDR, AF_UNSPEC behaviour [IN] o Updated this list [IN] - +o Massive ARP/cache/routing rewrite [ANK] [IN] +o AX.25 connect return fixed in using sock_error [IN] +o Proper netlink device major(36) [IN] +o First parts of the SKIP support [IN, not useful] +o TCP ICMP (SOSS should work again) [IN] +o IPFW support for TOS changing (Al Longyear) [IN] +o DECNET PPP test code [IN] +o NFS root [IN] +o Path MTU discovery [ANK] [IN] ---------- Things I thought Linus had for a while and not merged ---------------- @@ -280,33 +288,29 @@ o Updated this list [IN] o Improved IPX support for lwared. o Decnet pre pre pre pre pre Alpha 0.0. -o Faster routing/caching support. ---------- Things pending for me to merge -------------- -o IPFW support for TOS changing (Al Longyear) o AF_UNIX garbage collect code o Faster closedown option for heavy use sites (me) o Tom May's insw_and_checksum() o IPX PPP support -o DECNET PPP test code -o SPARC patches [Dave] +o SPARC patches [Dave] [partly in] +o Path MTU and other goodies [ANK] --------------- Things That Need Doing Before 1.4 ------------------ -o inet_error for other layers o Finish merging the bridge code o Fast checksum/copy on outgoing TCP o Fast dev_grab_next() transmit reload function - and dev_push_failed() ?? -o Faster ip_forward last hit cache [PENDING(GuruA0)] -o L2 ip routing cache [PENDING(btv)] + and dev_push_failed() ?? [Causes deadlock + cases with slow box, heavy networking] o Forwarding queue control (+ fairness algorithms ??) o IP forward flow control. o Clean up RAW AX.25 sockets. o Finish 802.2 Class I code to be compliant to the oddities of 802.2 o Tidy BPQ support to use a bpqip tunnel device -o Strange eth0-eth3 bug +o Strange eth0-eth3 bug [Matti fixed ??] o Finish IPIP bug fixes [Done hopefully] o Why doesnt the PROTO_UNREACH get sent ? [Should now work] o Kill off old ip_queue_xmit/ip_send stuff. diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c index 97df026923ae..2d8abf6957a0 100644 --- a/net/ax25/af_ax25.c +++ b/net/ax25/af_ax25.c @@ -661,7 +661,7 @@ struct device *ax25rtr_get_dev(ax25_address *addr) return dev; break; case ARPHRD_ETHER: - if (arp_query((unsigned char *)&dev_addr, dev->pa_addr, ARPHRD_AX25)) + if (arp_query((unsigned char *)&dev_addr, dev->pa_addr, dev)) if (ax25cmp(addr, &dev_addr) == 0) return dev; break; @@ -1669,7 +1669,7 @@ static int bpq_rcv(struct sk_buff *skb, struct device *dev, struct packet_type * skb->sk = NULL; /* Initially we don't know who its for */ - if (!arp_query((unsigned char *)&port_call, dev->pa_addr, ARPHRD_AX25)) { + if (!arp_query((unsigned char *)&port_call, dev->pa_addr, dev)) { kfree_skb(skb, FREE_READ); /* We have no port callsign */ return 0; } diff --git a/net/core/dev.c b/net/core/dev.c index 844063fc4d64..3c5c670b2132 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -39,6 +39,8 @@ * Alan Cox : Device lock protection. * Alan Cox : Fixed nasty side effect of device close changes. * Rudi Cilibrasi : Pass the right thing to set_mac_address() + * Dave Miller : 32bit quantity for the device lock to make it work out + * on a Sparc. * */ diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c index 96f6e2b72919..518ac9edd0fb 100644 --- a/net/ethernet/eth.c +++ b/net/ethernet/eth.c @@ -89,7 +89,7 @@ void eth_setup(char *str, int *ints) int eth_header(struct sk_buff *skb, struct device *dev, unsigned short type, void *daddr, void *saddr, unsigned len) { - struct ethhdr *eth = (struct ethhdr *)skb_push(skb,14); + struct ethhdr *eth = (struct ethhdr *)skb_push(skb,ETH_HLEN); /* * Set the protocol type. For a packet of type ETH_P_802_3 we put the length @@ -175,7 +175,7 @@ unsigned short eth_type_trans(struct sk_buff *skb, struct device *dev) unsigned char *rawp; skb->mac.raw=skb->data; - skb_pull(skb,14); + skb_pull(skb,dev->hard_header_len); eth= skb->mac.ethernet; if(*eth->h_dest&1) @@ -218,28 +218,48 @@ unsigned short eth_type_trans(struct sk_buff *skb, struct device *dev) } /* - * Header caching for ethernet. Try to find and cache a header to avoid arp overhead. + * Upper level calls this function to bind hardware header cache entry. + * If the call is successful, then corresponding Address Resolution Protocol + * (maybe, not ARP) takes responsibility for updating cache content. */ - -void eth_header_cache(struct device *dev, struct sock *sk, unsigned long saddr, unsigned long daddr) + +void eth_header_cache_bind(struct hh_cache ** hhp, struct device *dev, + unsigned short htype, __u32 daddr) { - int v=arp_find_cache(sk->ip_hcache_data, daddr, dev); - if(v!=1) - sk->ip_hcache_state=0; /* Try when arp resolves */ - else + struct hh_cache *hh; + + if (htype != ETH_P_IP) + { + printk("eth_header_cache_bind: %04x cache is not implemented\n", htype); + return; + } + if (arp_bind_cache(hhp, dev, htype, daddr)) + return; + if ((hh=*hhp) != NULL) + { + memcpy(hh->hh_data+6, dev->dev_addr, ETH_ALEN); + hh->hh_data[12] = htype>>8; + hh->hh_data[13] = htype&0xFF; + } +} + +/* + * Called by Address Resolution module to notify changes in address. + */ + +void eth_header_cache_update(struct hh_cache *hh, struct device *dev, unsigned char * haddr) +{ + if (hh->hh_type != ETH_P_IP) { - memcpy(sk->ip_hcache_data+6, dev->dev_addr, ETH_ALEN); - sk->ip_hcache_data[12]=ETH_P_IP>>8; - sk->ip_hcache_data[13]=ETH_P_IP&0xFF; - sk->ip_hcache_state=1; - sk->ip_hcache_stamp=arp_cache_stamp; - sk->ip_hcache_ver=&arp_cache_stamp; + printk("eth_header_cache_update: %04x cache is not implemented\n", hh->hh_type); + return; } + memcpy(hh->hh_data, haddr, ETH_ALEN); + hh->hh_uptodate = 1; } /* * Copy from an ethernet device memory space to an sk_buff while checksumming if IP - * The magic "34" is Rx_addr+Tx_addr+type_field+sizeof(struct iphdr) == 6+6+2+20. */ void eth_copy_and_sum(struct sk_buff *dest, unsigned char *src, int length, int base) @@ -261,13 +281,15 @@ void eth_copy_and_sum(struct sk_buff *dest, unsigned char *src, int length, int * We have to use the smaller of length and ip_length because it * can happen that ip_length > length. */ - memcpy(dest->data,src,34); /* ethernet is always >= 34 */ - length -= 34; - iph=(struct iphdr*)(src+14); /* 14 = Rx_addr+Tx_addr+type_field */ + memcpy(dest->data,src,sizeof(struct iphdr)+ETH_HLEN); /* ethernet is always >= 34 */ + length -= sizeof(struct iphdr) + ETH_HLEN; + iph=(struct iphdr*)(src+ETH_HLEN); ip_length = ntohs(iph->tot_len) - sizeof(struct iphdr); - if (ip_length <= length) + + /* Also watch out for bogons - min IP size is 8 (rfc-1042) */ + if ((ip_length <= length) && (ip_length > 7)) length=ip_length; - dest->csum=csum_partial_copy(src+34,dest->data+34,length,base); + dest->csum=csum_partial_copy(src+sizeof(struct iphdr)+ETH_HLEN,dest->data+sizeof(struct iphdr)+ETH_HLEN,length,base); dest->ip_summed=1; } diff --git a/net/ipv4/Config.in b/net/ipv4/Config.in index ddb75a41810e..d9930d0e467a 100644 --- a/net/ipv4/Config.in +++ b/net/ipv4/Config.in @@ -20,7 +20,7 @@ fi comment '(it is safe to leave these untouched)' bool 'IP: PC/TCP compatibility mode' CONFIG_INET_PCTCP tristate 'IP: Reverse ARP' CONFIG_INET_RARP -bool 'IP: Assume subnets are local' CONFIG_INET_SNARL +bool 'IP: Disable Path MTU Discovery (normally enabled)' CONFIG_NO_PATH_MTU_DISCOVERY bool 'IP: Disable NAGLE algorithm (normally enabled)' CONFIG_TCP_NAGLE_OFF bool 'IP: Drop source routed frames' CONFIG_IP_NOSR bool 'IP: Allow large windows (not recommended if <16Mb of memory)' CONFIG_SKB_LARGE diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index e8dac8258a2d..9c9648ece50a 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -402,6 +402,7 @@ void destroy_sock(struct sock *sk) { if(sk->opt) kfree(sk->opt); + ip_rt_put(sk->ip_route_cache); /* * This one is pure paranoia. I'll take it out * later once I know the bug is buried. @@ -915,6 +916,7 @@ static int inet_bind(struct socket *sock, struct sockaddr *uaddr, sk->daddr = 0; sk->dummy_th.dest = 0; } + ip_rt_put(sk->ip_route_cache); sk->ip_route_cache=NULL; return(0); } @@ -1236,6 +1238,9 @@ static int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) case SIOCDARP: case SIOCGARP: case SIOCSARP: + case OLD_SIOCDARP: + case OLD_SIOCGARP: + case OLD_SIOCSARP: return(arp_ioctl(cmd,(void *) arg)); case SIOCDRARP: case SIOCGRARP: @@ -1570,4 +1575,10 @@ void inet_proto_init(struct net_proto *pro) 0, &proc_net_inode_operations, rt_get_info }); + proc_net_register(&(struct proc_dir_entry) { + PROC_NET_RTCACHE, 8, "rt_cache", + S_IFREG | S_IRUGO, 1, 0, 0, + 0, &proc_net_inode_operations, + rt_cache_get_info + }); } diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index d75b4298e137..9414f0772338 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -105,12 +105,11 @@ struct arp_table { struct arp_table *next; /* Linked entry list */ unsigned long last_used; /* For expiry */ + unsigned long last_updated; /* For expiry */ unsigned int flags; /* Control status */ u32 ip; /* ip address of entry */ u32 mask; /* netmask - used for generalised proxy arps (tridge) */ unsigned char ha[MAX_ADDR_LEN]; /* Hardware address */ - unsigned char hlen; /* Length of hardware address */ - unsigned short htype; /* Type of hardware in use */ struct device *dev; /* Device the entry is tied to */ /* @@ -120,6 +119,7 @@ struct arp_table struct timer_list timer; /* expire timer */ int retries; /* remaining retries */ struct sk_buff_head skb; /* list of queued packets */ + struct hh_cache *hh; }; @@ -130,17 +130,18 @@ struct arp_table /* * If an arp request is send, ARP_RES_TIME is the timeout value until the * next request is send. + * RFC1122: OK. Throttles ARPing, as per 2.3.2.1. (MUST) + * The recommended minimum timeout is 1 second per destination. + * This timeout is prolongated to ARP_DEAD_RES_TIME, if + * destination does not respond. */ -/* RFC1122: OK. Throttles ARPing, as per 2.3.2.1. (MUST) */ -/* The recommended minimum timeout is 1 second per destination. */ -/* Is this a per-destination timeout? -- MS [YES AC]*/ - -#define ARP_RES_TIME (250*(HZ/10)) +#define ARP_RES_TIME (5*HZ) +#define ARP_DEAD_RES_TIME (60*HZ) /* * The number of times an arp request is send, until the host is - * considered unreachable. + * considered temporarily unreachable. */ #define ARP_MAX_TRIES 3 @@ -153,22 +154,30 @@ struct arp_table /* * How often is the function 'arp_check_retries' called. - * An entry is invalidated in the time between ARP_TIMEOUT and + * An unused entry is invalidated in the time between ARP_TIMEOUT and * (ARP_TIMEOUT+ARP_CHECK_INTERVAL). */ -#define ARP_CHECK_INTERVAL (60 * HZ) +#define ARP_CHECK_INTERVAL (60*HZ) -enum proxy { - PROXY_EXACT=0, - PROXY_ANY, - PROXY_NONE, -}; +/* + * The entry is reconfirmed by sending point-to-point ARP + * request after ARP_CONFIRM_INTERVAL. If destinations does not respond + * for ARP_CONFIRM_TIMEOUT, normal broadcast resolution scheme is started. + */ -/* Forward declarations. */ -static void arp_check_expire (unsigned long); -static struct arp_table *arp_lookup(u32 paddr, enum proxy proxy, unsigned short type); +#define ARP_CONFIRM_INTERVAL (300*HZ) +#define ARP_CONFIRM_TIMEOUT ARP_RES_TIME + +static unsigned long arp_lock; +static unsigned long arp_bh_mask; + +#define ARP_BH_BACKLOG 1 + +static struct arp_table *arp_backlog; +static void arp_run_bh(void); +static void arp_check_expire (unsigned long); static struct timer_list arp_timer = { NULL, NULL, ARP_CHECK_INTERVAL, 0L, &arp_check_expire }; @@ -176,26 +185,17 @@ static struct timer_list arp_timer = /* * The default arp netmask is just 255.255.255.255 which means it's * a single machine entry. Only proxy entries can have other netmasks - * -*/ + */ #define DEF_ARP_NETMASK (~0) - /* * The size of the hash table. Must be a power of two. * Maybe we should remove hashing in the future for arp and concentrate * on Patrick Schaaf's Host-Cache-Lookup... */ - #define ARP_TABLE_SIZE 16 - -/* The ugly +1 here is to cater for proxy entries. They are put in their - own list for efficiency of lookup. If you don't want to find a proxy - entry then don't look in the last entry, otherwise do -*/ - #define FULL_ARP_TABLE_SIZE (ARP_TABLE_SIZE+1) struct arp_table *arp_tables[FULL_ARP_TABLE_SIZE] = @@ -203,8 +203,7 @@ struct arp_table *arp_tables[FULL_ARP_TABLE_SIZE] = NULL, }; -unsigned long arp_cache_stamp; - +#define arp_proxy_list arp_tables[ARP_TABLE_SIZE] /* * The last bits in the IP address are used for the cache lookup. @@ -212,82 +211,356 @@ unsigned long arp_cache_stamp; */ #define HASH(paddr) (htonl(paddr) & (ARP_TABLE_SIZE - 1)) -#define PROXY_HASH ARP_TABLE_SIZE + +/* + * Lock/unlock arp_table chains. + */ + +static __inline__ void arp_fast_lock(void) +{ + ATOMIC_INCR(&arp_lock); +} + +static __inline__ void arp_fast_unlock(void) +{ + ATOMIC_DECR(&arp_lock); +} + +static __inline__ void arp_unlock(void) +{ + if (!ATOMIC_DECR_AND_CHECK(&arp_lock) && arp_bh_mask) + arp_run_bh(); +} + +/* + * Enqueue to FIFO list. + */ + +static void arp_enqueue(struct arp_table **q, struct arp_table *entry) +{ + unsigned long flags; + struct arp_table * tail; + + save_flags(flags); + cli(); + tail = *q; + if (!tail) + entry->next = entry; + else + { + entry->next = tail->next; + tail->next = entry; + } + *q = entry; + restore_flags(flags); + return; +} + +/* + * Dequeue from FIFO list, + * caller should mask interrupts. + */ + +static struct arp_table * arp_dequeue(struct arp_table **q) +{ + struct arp_table * entry; + + if (*q) + { + entry = (*q)->next; + (*q)->next = entry->next; + if (entry->next == entry) + *q = NULL; + entry->next = NULL; + return entry; + } + return NULL; +} + +/* + * Purge all linked skb's of the entry. + */ + +static void arp_release_entry(struct arp_table *entry) +{ + struct sk_buff *skb; + unsigned long flags; + + save_flags(flags); + cli(); + /* Release the list of `skb' pointers. */ + while ((skb = skb_dequeue(&entry->skb)) != NULL) + { + skb_device_lock(skb); + restore_flags(flags); + dev_kfree_skb(skb, FREE_WRITE); + cli(); + } + restore_flags(flags); + return; +} + +/* + * Release the entry and all resources linked to it: skb's, hh's, timer + * and certainly memory. + */ + +static void arp_free_entry(struct arp_table *entry) +{ + unsigned long flags; + struct hh_cache *hh, *next; + + del_timer(&entry->timer); + + save_flags(flags); + cli(); + arp_release_entry(entry); + + for (hh = entry->hh; hh; hh = next) + { + next = hh->hh_next; + hh->hh_arp = NULL; + if (!--hh->hh_refcnt) + kfree_s(hh, sizeof(struct(struct hh_cache))); + } + restore_flags(flags); + + kfree_s(entry, sizeof(struct arp_table)); + return; +} + +/* + * How many users has this entry? + */ + +static __inline__ int arp_count_hhs(struct arp_table * entry) +{ + struct hh_cache *hh, **hhp; + int count = 0; + + hhp = &entry->hh; + while ((hh=*hhp) != NULL) + { + if (hh->hh_refcnt == 1) + { + *hhp = hh->hh_next; + kfree_s(hh, sizeof(struct hh_cache)); + continue; + } + count += hh->hh_refcnt-1; + hhp = &hh->hh_next; + } + + return count; +} + +/* + * Invalidate all hh's, so that higher level will not try to use it. + */ + +static __inline__ void arp_invalidate_hhs(struct arp_table * entry) +{ + struct hh_cache *hh; + + for (hh=entry->hh; hh; hh=hh->hh_next) + hh->hh_uptodate = 0; +} + +/* + * Signal to device layer, that hardware address may be changed. + */ + +static __inline__ void arp_update_hhs(struct arp_table * entry) +{ + struct hh_cache *hh; + + for (hh=entry->hh; hh; hh=hh->hh_next) + entry->dev->header_cache_update(hh, entry->dev, entry->ha); +} /* * Check if there are too old entries and remove them. If the ATF_PERM * flag is set, they are always left in the arp cache (permanent entry). - * Note: Only fully resolved entries, which don't have any packets in - * the queue, can be deleted, since ARP_TIMEOUT is much greater than - * ARP_MAX_TRIES*ARP_RES_TIME. + * If an entry was not be confirmed for ARP_CONFIRM_INTERVAL, + * declare it invalid and send point-to-point ARP request. + * If it will not be confirmed for ARP_CONFIRM_TIMEOUT, + * give it to shred by arp_expire_entry. */ -/* RFC1122: Looks good. Prevents stale ARP entries, as per 2.3.2.1. (MUST) */ - static void arp_check_expire(unsigned long dummy) { int i; unsigned long now = jiffies; - unsigned long flags; - save_flags(flags); - cli(); - for (i = 0; i < FULL_ARP_TABLE_SIZE; i++) + del_timer(&arp_timer); + + if (!arp_lock) { - struct arp_table *entry; - struct arp_table **pentry = &arp_tables[i]; + arp_fast_lock(); - while ((entry = *pentry) != NULL) + for (i = 0; i < ARP_TABLE_SIZE; i++) { - if ((now - entry->last_used) > ARP_TIMEOUT - && !(entry->flags & ATF_PERM)) + struct arp_table *entry; + struct arp_table **pentry; + + pentry = &arp_tables[i]; + + while ((entry = *pentry) != NULL) { - *pentry = entry->next; /* remove from list */ - arp_cache_stamp++; - del_timer(&entry->timer); /* Paranoia */ - kfree_s(entry, sizeof(struct arp_table)); - /* Don't have to remove packets in entry->skb. */ - /* See comments above. */ + cli(); + if (now - entry->last_used > ARP_TIMEOUT + && !(entry->flags & ATF_PERM) + && !arp_count_hhs(entry)) + { + *pentry = entry->next; + sti(); +#if RT_CACHE_DEBUG >= 2 + printk("arp_expire: %08x expired\n", entry->ip); +#endif + arp_free_entry(entry); + } + else if (entry->last_updated + && now - entry->last_updated > ARP_CONFIRM_INTERVAL + && !(entry->flags & ATF_PERM)) + { + struct device * dev = entry->dev; + pentry = &entry->next; + entry->flags &= ~ATF_COM; + arp_invalidate_hhs(entry); + sti(); + entry->retries = ARP_MAX_TRIES+1; + del_timer(&entry->timer); + entry->timer.expires = jiffies + ARP_CONFIRM_TIMEOUT; + add_timer(&entry->timer); + arp_send(ARPOP_REQUEST, ETH_P_ARP, entry->ip, + dev, dev->pa_addr, entry->ha, + dev->dev_addr, NULL); +#if RT_CACHE_DEBUG >= 2 + printk("arp_expire: %08x requires confirmation\n", entry->ip); +#endif + } + else + pentry = &entry->next; /* go to next entry */ } - else - pentry = &entry->next; /* go to next entry */ } + arp_unlock(); } - restore_flags(flags); + + ip_rt_check_expire(); /* * Set the timer again. */ - del_timer(&arp_timer); arp_timer.expires = jiffies + ARP_CHECK_INTERVAL; add_timer(&arp_timer); } - /* - * Release all linked skb's and the memory for this entry. + * This function is called, if an entry is not resolved in ARP_RES_TIME. + * When more than MAX_ARP_TRIES retries was done, release queued skb's, + * but not discard entry itself if it is in use. */ -static void arp_release_entry(struct arp_table *entry) +static void arp_expire_request (unsigned long arg) { - struct sk_buff *skb; + struct arp_table *entry = (struct arp_table *) arg; + struct arp_table **pentry; + unsigned long hash; unsigned long flags; save_flags(flags); cli(); - /* Release the list of `skb' pointers. */ - while ((skb = skb_dequeue(&entry->skb)) != NULL) + + /* + * Since all timeouts are handled with interrupts enabled, there is a + * small chance, that this entry has just been resolved by an incoming + * packet. This is the only race condition, but it is handled... + */ + + if (entry->flags & ATF_COM) { - skb_device_lock(skb); restore_flags(flags); - dev_kfree_skb(skb, FREE_WRITE); - cli(); + return; + } + + if (arp_lock) + { +#if RT_CACHE_DEBUG >= 1 + printk("arp_expire_request: %08x postponed\n", entry->ip); +#endif + del_timer(&entry->timer); + entry->timer.expires = jiffies + HZ/10; + add_timer(&entry->timer); + restore_flags(flags); + return; } + + arp_fast_lock(); restore_flags(flags); - del_timer(&entry->timer); - kfree_s(entry, sizeof(struct arp_table)); - return; + + if (entry->last_updated && --entry->retries > 0) + { + struct device *dev = entry->dev; + +#if RT_CACHE_DEBUG >= 2 + printk("arp_expire_request: %08x timed out\n", entry->ip); +#endif + /* Set new timer. */ + del_timer(&entry->timer); + entry->timer.expires = jiffies + ARP_RES_TIME; + add_timer(&entry->timer); + arp_send(ARPOP_REQUEST, ETH_P_ARP, entry->ip, dev, dev->pa_addr, + NULL, dev->dev_addr, NULL); + arp_unlock(); + return; + } + + arp_release_entry(entry); + + cli(); + if (arp_count_hhs(entry)) + { + struct device *dev = entry->dev; +#if RT_CACHE_DEBUG >= 2 + printk("arp_expire_request: %08x is dead\n", entry->ip); +#endif + arp_release_entry(entry); + entry->retries = ARP_MAX_TRIES; + restore_flags(flags); + entry->last_updated = 0; + del_timer(&entry->timer); + entry->timer.expires = jiffies + ARP_DEAD_RES_TIME; + add_timer(&entry->timer); + arp_send(ARPOP_REQUEST, ETH_P_ARP, entry->ip, dev, dev->pa_addr, + NULL, dev->dev_addr, NULL); + arp_unlock(); + return; + } + restore_flags(flags); + + hash = HASH(entry->ip); + + pentry = &arp_tables[hash]; + + while (*pentry != NULL) + { + if (*pentry == entry) + { + cli(); + *pentry = entry->next; + restore_flags(flags); +#if RT_CACHE_DEBUG >= 2 + printk("arp_expire_request: %08x is killed\n", entry->ip); +#endif + arp_free_entry(entry); + arp_unlock(); + return; + } + pentry = &(*pentry)->next; + } + printk("arp_expire_request: bug: ARP entry is lost!\n"); + arp_unlock(); } /* @@ -298,16 +571,19 @@ int arp_device_event(struct notifier_block *this, unsigned long event, void *ptr { struct device *dev=ptr; int i; - unsigned long flags; - if(event!=NETDEV_DOWN) + if (event != NETDEV_DOWN) return NOTIFY_DONE; /* * This is a bit OTT - maybe we need some arp semaphores instead. */ - - save_flags(flags); - cli(); + +#if RT_CACHE_DEBUG >= 1 + if (arp_lock) + printk("arp_device_event: bug\n"); +#endif + arp_fast_lock(); + for (i = 0; i < FULL_ARP_TABLE_SIZE; i++) { struct arp_table *entry; @@ -315,18 +591,15 @@ int arp_device_event(struct notifier_block *this, unsigned long event, void *ptr while ((entry = *pentry) != NULL) { - if(entry->dev==dev) + if (entry->dev == dev) { *pentry = entry->next; /* remove from list */ - del_timer(&entry->timer); /* Paranoia */ - kfree_s(entry, sizeof(struct arp_table)); + arp_free_entry(entry); } else pentry = &entry->next; /* go to next entry */ } } - arp_cache_stamp++; - restore_flags(flags); return NOTIFY_DONE; } @@ -338,7 +611,8 @@ int arp_device_event(struct notifier_block *this, unsigned long event, void *ptr void arp_send(int type, int ptype, u32 dest_ip, struct device *dev, u32 src_ip, - unsigned char *dest_hw, unsigned char *src_hw) + unsigned char *dest_hw, unsigned char *src_hw, + unsigned char *target_hw) { struct sk_buff *skb; struct arphdr *arp; @@ -348,7 +622,7 @@ void arp_send(int type, int ptype, u32 dest_ip, * No arp on this interface. */ - if(dev->flags&IFF_NOARP) + if (dev->flags&IFF_NOARP) return; /* @@ -380,7 +654,7 @@ void arp_send(int type, int ptype, u32 dest_ip, #ifdef CONFIG_NETROM arp->ar_pro = (dev->type == ARPHRD_AX25 || dev->type == ARPHRD_NETROM) ? htons(AX25_P_IP) : htons(ETH_P_IP); #else - arp->ar_pro = (dev->type != ARPHRD_AX25)? htons(ETH_P_IP) : htons(AX25_P_IP); + arp->ar_pro = (dev->type != ARPHRD_AX25) ? htons(ETH_P_IP) : htons(AX25_P_IP); #endif #else arp->ar_pro = htons(ETH_P_IP); @@ -395,8 +669,8 @@ void arp_send(int type, int ptype, u32 dest_ip, arp_ptr+=dev->addr_len; memcpy(arp_ptr, &src_ip,4); arp_ptr+=4; - if (dest_hw != NULL) - memcpy(arp_ptr, dest_hw, dev->addr_len); + if (target_hw != NULL) + memcpy(arp_ptr, target_hw, dev->addr_len); else memset(arp_ptr, 0, dev->addr_len); arp_ptr+=dev->addr_len; @@ -405,124 +679,35 @@ void arp_send(int type, int ptype, u32 dest_ip, dev_queue_xmit(skb, dev, 0); } - /* - * This function is called, if an entry is not resolved in ARP_RES_TIME. - * Either resend a request, or give it up and free the entry. + * This will try to retransmit everything on the queue. */ -static void arp_expire_request (unsigned long arg) +static void arp_send_q(struct arp_table *entry) { - struct arp_table *entry = (struct arp_table *) arg; - struct arp_table **pentry; - unsigned long hash; - unsigned long flags; + struct sk_buff *skb; - save_flags(flags); - cli(); + unsigned long flags; /* - * Since all timeouts are handled with interrupts enabled, there is a - * small chance, that this entry has just been resolved by an incoming - * packet. This is the only race condition, but it is handled... + * Empty the entire queue, building its data up ready to send */ - if (entry->flags & ATF_COM) + if(!(entry->flags&ATF_COM)) { - restore_flags(flags); + printk("arp_send_q: incomplete entry for %s\n", + in_ntoa(entry->ip)); + /* Can't flush the skb, because RFC1122 says to hang on to */ + /* at least one from any unresolved entry. --MS */ + /* Whats happened is that someone has 'unresolved' the entry + as we got to use it - this 'can't happen' -- AC */ return; } - if (--entry->retries > 0) - { - u32 ip = entry->ip; - struct device *dev = entry->dev; - - /* Set new timer. */ - del_timer(&entry->timer); - entry->timer.expires = jiffies + ARP_RES_TIME; - add_timer(&entry->timer); - restore_flags(flags); - arp_send(ARPOP_REQUEST, ETH_P_ARP, ip, dev, dev->pa_addr, - NULL, dev->dev_addr); - return; - } - - /* - * Arp request timed out. Delete entry and all waiting packets. - * If we give each entry a pointer to itself, we don't have to - * loop through everything again. Maybe hash is good enough, but - * I will look at it later. - */ - - hash = HASH(entry->ip); - - /* proxy entries shouldn't really time out so this is really - only here for completeness - */ - - /* RFC1122: They *can* be timed out, according to 2.3.2.1. */ - /* They recommend a minute. -- MS */ - /* The world doesn't work this way -- AC */ - - if (entry->flags & ATF_PUBL) - pentry = &arp_tables[PROXY_HASH]; - else - pentry = &arp_tables[hash]; - while (*pentry != NULL) - { - if (*pentry == entry) - { - *pentry = entry->next; /* delete from linked list */ - del_timer(&entry->timer); - restore_flags(flags); - arp_release_entry(entry); - arp_cache_stamp++; - return; - } - pentry = &(*pentry)->next; - } - restore_flags(flags); - printk("Possible ARP queue corruption.\n"); - /* - * We should never arrive here. - */ - - /* Should we perhaps flush the ARP table (except the ones we're */ - /* publishing, if we can trust the queue that much) at this */ - /* point? -- MS */ -} - - -/* - * This will try to retransmit everything on the queue. - */ - -static void arp_send_q(struct arp_table *entry, unsigned char *hw_dest) -{ - struct sk_buff *skb; - - unsigned long flags; - - /* - * Empty the entire queue, building its data up ready to send - */ - - if(!(entry->flags&ATF_COM)) - { - printk("arp_send_q: incomplete entry for %s\n", - in_ntoa(entry->ip)); - /* Can't flush the skb, because RFC1122 says to hang on to */ - /* at least one from any unresolved entry. --MS */ - /* Whats happened is that someone has 'unresolved' the entry - as we got to use it - this 'can't happen' -- AC */ - return; - } - - save_flags(flags); - - cli(); - while((skb = skb_dequeue(&entry->skb)) != NULL) + save_flags(flags); + + cli(); + while((skb = skb_dequeue(&entry->skb)) != NULL) { IS_SKB(skb); skb_device_lock(skb); @@ -544,50 +729,29 @@ static void arp_send_q(struct arp_table *entry, unsigned char *hw_dest) * Delete an ARP mapping entry in the cache. */ -void arp_destroy(u32 ip_addr, int force) +static void arp_destroy(struct arp_table * entry) { - int checked_proxies = 0; - struct arp_table *entry; + struct arp_table *entry1; struct arp_table **pentry; - unsigned long hash = HASH(ip_addr); -ugly: - cli(); - pentry = &arp_tables[hash]; - if (! *pentry) /* also check proxy entries */ - pentry = &arp_tables[PROXY_HASH]; + if (entry->flags & ATF_PUBL) + pentry = &arp_proxy_list; + else + pentry = &arp_tables[HASH(entry->ip)]; - while ((entry = *pentry) != NULL) + while ((entry1 = *pentry) != NULL) { - if (entry->ip == ip_addr) + if (entry1 == entry) { - if ((entry->flags & ATF_PERM) && !force) { - sti(); - return; - } - *pentry = entry->next; + *pentry = entry1->next; del_timer(&entry->timer); - sti(); - arp_release_entry(entry); - /* this would have to be cleaned up */ - goto ugly; - /* perhaps like this ? - cli(); - entry = *pentry; - */ + arp_free_entry(entry); + return; } - pentry = &entry->next; - if (!checked_proxies && ! *pentry) - { /* ugly. we have to make sure we check proxy - entries as well */ - checked_proxies = 1; - pentry = &arp_tables[PROXY_HASH]; - } + pentry = &entry1->next; } - sti(); } - /* * Receive an arp request by the device layer. Maybe I rewrite it, to * use the incoming packet for the reply. The time for the current @@ -604,7 +768,6 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) unsigned char *arp_ptr= (unsigned char *)(arp+1); struct arp_table *entry; struct arp_table *proxy_entry; - int hlen,htype; unsigned long hash; unsigned char ha[MAX_ADDR_LEN]; /* So we can enable ints again. */ unsigned char *sha,*tha; @@ -636,7 +799,7 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) */ /* Again, should this be an error/printk? -- MS */ - switch(dev->type) + switch (dev->type) { #ifdef CONFIG_AX25 case ARPHRD_AX25: @@ -683,16 +846,13 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) * Extract fields */ - hlen = dev->addr_len; - htype = dev->type; - sha=arp_ptr; - arp_ptr+=hlen; - memcpy(&sip,arp_ptr,4); - arp_ptr+=4; + arp_ptr += dev->addr_len; + memcpy(&sip, arp_ptr, 4); + arp_ptr += 4; tha=arp_ptr; - arp_ptr+=hlen; - memcpy(&tip,arp_ptr,4); + arp_ptr += dev->addr_len; + memcpy(&tip, arp_ptr, 4); /* * Check for bad requests for 127.x.x.x and requests for multicast @@ -721,47 +881,48 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) * cache. */ - if(arp->ar_op == htons(ARPOP_REQUEST)) + if (arp->ar_op == htons(ARPOP_REQUEST)) { /* * Only reply for the real device address or when it's in our proxy tables */ - if(tip!=dev->pa_addr) + if (tip != dev->pa_addr) { /* * To get in here, it is a request for someone else. We need to * check if that someone else is one of our proxies. If it isn't, * we can toss it. */ - cli(); - for(proxy_entry=arp_tables[PROXY_HASH]; - proxy_entry; - proxy_entry = proxy_entry->next) + arp_fast_lock(); + + for (proxy_entry=arp_proxy_list; + proxy_entry; + proxy_entry = proxy_entry->next) { - /* we will respond to a proxy arp request - if the masked arp table ip matches the masked - tip. This allows a single proxy arp table - entry to be used on a gateway machine to handle - all requests for a whole network, rather than - having to use a huge number of proxy arp entries - and having to keep them uptodate. - */ - if (proxy_entry->dev != dev && proxy_entry->htype == htype && - !((proxy_entry->ip^tip)&proxy_entry->mask)) - break; + /* we will respond to a proxy arp request + if the masked arp table ip matches the masked + tip. This allows a single proxy arp table + entry to be used on a gateway machine to handle + all requests for a whole network, rather than + having to use a huge number of proxy arp entries + and having to keep them uptodate. + */ + if (proxy_entry->dev == dev && + !((proxy_entry->ip^tip)&proxy_entry->mask)) + break; } if (proxy_entry) { - memcpy(ha, proxy_entry->ha, hlen); - sti(); - arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,ha); + memcpy(ha, proxy_entry->ha, dev->addr_len); + arp_unlock(); + arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,ha, sha); kfree_skb(skb, FREE_READ); return 0; } else { - sti(); + arp_unlock(); kfree_skb(skb, FREE_READ); return 0; } @@ -771,7 +932,7 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) /* * To get here, it must be an arp request for us. We need to reply. */ - arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr); + arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha); } } /* @@ -791,21 +952,22 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) * there. */ + arp_fast_lock(); + hash = HASH(sip); - cli(); - for(entry=arp_tables[hash];entry;entry=entry->next) - if(entry->ip==sip && entry->htype==htype) + + for (entry=arp_tables[hash]; entry; entry=entry->next) + if (entry->ip == sip && entry->dev == dev) break; - if(entry) + if (entry) { /* * Entry found; update it only if it is not a permanent entry. */ if (!(entry->flags & ATF_PERM)) { - memcpy(entry->ha, sha, hlen); - entry->hlen = hlen; - entry->last_used = jiffies; + memcpy(entry->ha, sha, dev->addr_len); + entry->last_updated = jiffies; } if (!(entry->flags & ATF_COM)) { @@ -815,17 +977,13 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) */ del_timer(&entry->timer); entry->flags |= ATF_COM; - sti(); + arp_update_hhs(entry); /* * Send out waiting packets. We might have problems, if someone is * manually removing entries right now -- entry might become invalid * underneath us. */ - arp_send_q(entry, sha); - } - else - { - sti(); + arp_send_q(entry); } } else @@ -836,94 +994,121 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) entry = (struct arp_table *)kmalloc(sizeof(struct arp_table),GFP_ATOMIC); if(entry == NULL) { - sti(); + arp_unlock(); printk("ARP: no memory for new arp entry\n"); - kfree_skb(skb, FREE_READ); return 0; } entry->mask = DEF_ARP_NETMASK; entry->ip = sip; - entry->hlen = hlen; - entry->htype = htype; entry->flags = ATF_COM; + entry->hh = NULL; init_timer(&entry->timer); - memcpy(entry->ha, sha, hlen); - entry->last_used = jiffies; + entry->timer.function = arp_expire_request; + entry->timer.data = (unsigned long)entry; + memcpy(entry->ha, sha, dev->addr_len); + entry->last_updated = entry->last_used = jiffies; entry->dev = skb->dev; skb_queue_head_init(&entry->skb); - entry->next = arp_tables[hash]; - arp_tables[hash] = entry; - sti(); + if (arp_lock == 1) + { + entry->next = arp_tables[hash]; + arp_tables[hash] = entry; + } + else + { +#if RT_CACHE_DEBUG >= 1 + printk("arp_rcv: %08x backlogged\n", entry->ip); +#endif + arp_enqueue(&arp_backlog, entry); + arp_bh_mask |= ARP_BH_BACKLOG; + } } /* * Replies have been sent, and entries have been added. All done. */ kfree_skb(skb, FREE_READ); + arp_unlock(); return 0; } - /* - * Find an arp mapping in the cache. If not found, return false. + * Lookup ARP entry by (addr, dev) pair. + * Flags: ATF_PUBL - search for proxy entries + * ATF_NETMASK - search for proxy network entry. + * NOTE: should be called with locked ARP tables. */ -int arp_query(unsigned char *haddr, u32 paddr, unsigned short type) +static struct arp_table *arp_lookup(u32 paddr, unsigned short flags, struct device * dev) { struct arp_table *entry; - unsigned long hash = HASH(paddr); - /* - * Find an entry - */ - cli(); - - for (entry = arp_tables[hash]; entry != NULL; entry = entry->next) - if (entry->ip == paddr && entry->htype == type) - break; + if (!(flags & ATF_PUBL)) + { + for (entry = arp_tables[HASH(paddr)]; + entry != NULL; entry = entry->next) + if (entry->ip == paddr && entry->dev == dev) + break; + return entry; + } - if (entry != NULL) { - /* - * Update the record - */ - - entry->last_used = jiffies; - memcpy(haddr, entry->ha, entry->hlen); - sti(); - return 1; + if (!(flags & ATF_NETMASK)) + { + for (entry = arp_proxy_list; + entry != NULL; entry = entry->next) + if (entry->ip == paddr && entry->dev == dev) + break; + return entry; } - sti(); - return 0; + for (entry=arp_proxy_list; entry != NULL; entry = entry->next) + if (!((entry->ip^paddr)&entry->mask) && entry->dev == dev) + break; + return entry; } /* - * Find an arp mapping in the cache. If not found, post a request. + * Find an arp mapping in the cache. If not found, return false. */ -int arp_find(unsigned char *haddr, u32 paddr, struct device *dev, - u32 saddr, struct sk_buff *skb) +int arp_query(unsigned char *haddr, u32 paddr, struct device * dev) { struct arp_table *entry; - unsigned long hash; -#ifdef CONFIG_IP_MULTICAST - u32 taddr; -#endif - switch (ip_chk_addr(paddr)) + arp_fast_lock(); + + entry = arp_lookup(paddr, 0, dev); + + if (entry != NULL) + { + entry->last_used = jiffies; + if (entry->flags & ATF_COM) + { + memcpy(haddr, entry->ha, dev->addr_len); + arp_unlock(); + return 1; + } + } + arp_unlock(); + return 0; +} + + +static int arp_set_predefined(int addr_hint, unsigned char * haddr, __u32 paddr, struct device * dev) +{ + switch (addr_hint) { case IS_MYADDR: printk("ARP: arp called for own IP address\n"); memcpy(haddr, dev->dev_addr, dev->addr_len); - skb->arp = 1; - return 0; + return 1; #ifdef CONFIG_IP_MULTICAST case IS_MULTICAST: if(dev->type==ARPHRD_ETHER || dev->type==ARPHRD_IEEE802) { - /* What exactly does this do? -- MS */ + u32 taddr; haddr[0]=0x01; haddr[1]=0x00; haddr[2]=0x5e; @@ -933,7 +1118,7 @@ int arp_find(unsigned char *haddr, u32 paddr, struct device *dev, haddr[4]=taddr&0xff; taddr=taddr>>8; haddr[3]=taddr&0x7f; - return 0; + return 1; } /* * If a device does not support multicast broadcast the stuff (eg AX.25 for now) @@ -942,17 +1127,35 @@ int arp_find(unsigned char *haddr, u32 paddr, struct device *dev, case IS_BROADCAST: memcpy(haddr, dev->broadcast, dev->addr_len); + return 1; + } + return 0; +} + +/* + * Find an arp mapping in the cache. If not found, post a request. + */ + +int arp_find(unsigned char *haddr, u32 paddr, struct device *dev, + u32 saddr, struct sk_buff *skb) +{ + struct arp_table *entry; + unsigned long hash; + + if (arp_set_predefined(ip_chk_addr(paddr), haddr, paddr, dev)) + { + if (skb) skb->arp = 1; - return 0; + return 0; } hash = HASH(paddr); - cli(); + arp_fast_lock(); /* * Find an entry */ - entry = arp_lookup(paddr, PROXY_NONE, dev->type); + entry = arp_lookup(paddr, 0, dev); if (entry != NULL) /* It exists */ { @@ -965,10 +1168,30 @@ int arp_find(unsigned char *haddr, u32 paddr, struct device *dev, if (skb != NULL) { - skb_queue_tail(&entry->skb, skb); - skb_device_unlock(skb); + if (entry->last_updated) + { + skb_queue_tail(&entry->skb, skb); + skb_device_unlock(skb); + } + /* + * If last_updated==0 host is dead, so + * drop skb's and set socket error. + */ + else + { + /* + * FIXME: ICMP HOST UNREACHABLE should be + * sent in this situation. --ANK + */ + if (skb->sk) + { + skb->sk->err = EHOSTDOWN; + skb->sk->error_report(skb->sk); + } + dev_kfree_skb(skb, FREE_WRITE); + } } - sti(); + arp_unlock(); return 1; } @@ -980,7 +1203,7 @@ int arp_find(unsigned char *haddr, u32 paddr, struct device *dev, memcpy(haddr, entry->ha, dev->addr_len); if (skb) skb->arp = 1; - sti(); + arp_unlock(); return 0; } @@ -992,42 +1215,49 @@ int arp_find(unsigned char *haddr, u32 paddr, struct device *dev, GFP_ATOMIC); if (entry != NULL) { - entry->next = arp_tables[hash]; - entry->last_used = jiffies; + entry->last_updated = entry->last_used = jiffies; entry->flags = 0; entry->ip = paddr; entry->mask = DEF_ARP_NETMASK; memset(entry->ha, 0, dev->addr_len); - entry->hlen = dev->addr_len; - entry->htype = dev->type; entry->dev = dev; + entry->hh = NULL; init_timer(&entry->timer); entry->timer.function = arp_expire_request; entry->timer.data = (unsigned long)entry; entry->timer.expires = jiffies + ARP_RES_TIME; - arp_tables[hash] = entry; - add_timer(&entry->timer); - entry->retries = ARP_MAX_TRIES; skb_queue_head_init(&entry->skb); if (skb != NULL) { skb_queue_tail(&entry->skb, skb); skb_device_unlock(skb); } + if (arp_lock == 1) + { + entry->next = arp_tables[hash]; + arp_tables[hash] = entry; + add_timer(&entry->timer); + entry->retries = ARP_MAX_TRIES; + } + else + { +#if RT_CACHE_DEBUG >= 1 + printk("arp_find: %08x backlogged\n", entry->ip); +#endif + arp_enqueue(&arp_backlog, entry); + arp_bh_mask |= ARP_BH_BACKLOG; + } } - else - { - if (skb != NULL && skb->free) - kfree_skb(skb, FREE_WRITE); - } - sti(); + else if (skb != NULL) + dev_kfree_skb(skb, FREE_WRITE); + arp_unlock(); /* * If we didn't find an entry, we will try to send an ARP packet. */ arp_send(ARPOP_REQUEST, ETH_P_ARP, paddr, dev, saddr, NULL, - dev->dev_addr); + dev->dev_addr, NULL); return 1; } @@ -1042,7 +1272,6 @@ int arp_find(unsigned char *haddr, u32 paddr, struct device *dev, int arp_get_info(char *buffer, char **start, off_t offset, int length, int dummy) { int len=0; - off_t begin=0; off_t pos=0; int size; struct arp_table *entry; @@ -1050,12 +1279,13 @@ int arp_get_info(char *buffer, char **start, off_t offset, int length, int dummy int i,j,k; const char hexbuf[] = "0123456789ABCDEF"; - size = sprintf(buffer,"IP address HW type Flags HW address Mask\n"); + size = sprintf(buffer,"IP address HW type Flags HW address Mask Device\n"); pos+=size; len+=size; - - cli(); + + arp_fast_lock(); + for(i=0; inext) @@ -1065,17 +1295,17 @@ int arp_get_info(char *buffer, char **start, off_t offset, int length, int dummy */ #ifdef CONFIG_AX25 #ifdef CONFIG_NETROM - if (entry->htype == ARPHRD_AX25 || entry->htype == ARPHRD_NETROM) + if (entry->dev->type == ARPHRD_AX25 || entry->dev->type == ARPHRD_NETROM) strcpy(hbuffer,ax2asc((ax25_address *)entry->ha)); else { #else - if(entry->htype==ARPHRD_AX25) + if(entry->dev->type==ARPHRD_AX25) strcpy(hbuffer,ax2asc((ax25_address *)entry->ha)); else { #endif #endif - for(k=0,j=0;khlen;j++) + for(k=0,j=0;kdev->addr_len;j++) { hbuffer[k++]=hexbuf[ (entry->ha[j]>>4)&15 ]; hbuffer[k++]=hexbuf[ entry->ha[j]&15 ]; @@ -1089,200 +1319,330 @@ int arp_get_info(char *buffer, char **start, off_t offset, int length, int dummy size = sprintf(buffer+len, "%-17s0x%-10x0x%-10x%s", in_ntoa(entry->ip), - (unsigned int)entry->htype, + (unsigned int)entry->dev->type, entry->flags, hbuffer); +#if RT_CACHE_DEBUG < 2 size += sprintf(buffer+len+size, - " %-17s\n", - entry->mask==DEF_ARP_NETMASK? - "*":in_ntoa(entry->mask)); + " %-17s %s\n", + entry->mask==DEF_ARP_NETMASK ? + "*" : in_ntoa(entry->mask), entry->dev->name); +#else + size += sprintf(buffer+len+size, + " %-17s %s\t%ld\t%1d\n", + entry->mask==DEF_ARP_NETMASK ? + "*" : in_ntoa(entry->mask), entry->dev->name, + entry->hh ? entry->hh->hh_refcnt : -1, + entry->hh ? entry->hh->hh_uptodate : 0); +#endif - len+=size; - pos=begin+len; + len += size; + pos += size; - if(posoffset+length) + if (pos >= offset+length) break; } } - sti(); + arp_unlock(); - *start=buffer+(offset-begin); /* Start of wanted data */ - len-=(offset-begin); /* Start slop */ - if(len>length) - len=length; /* Ending slop */ + *start = buffer+len-(pos-offset); /* Start of wanted data */ + len = pos-offset; /* Start slop */ + if (len>length) + len = length; /* Ending slop */ return len; } -/* - * This will find an entry in the ARP table by looking at the IP address. - * If proxy is PROXY_EXACT then only exact IP matches will be allowed - * for proxy entries, otherwise the netmask will be used - */ -static struct arp_table *arp_lookup(u32 paddr, enum proxy proxy, unsigned short type) +int arp_bind_cache(struct hh_cache ** hhp, struct device *dev, unsigned short htype, u32 paddr) { struct arp_table *entry; - unsigned long hash = HASH(paddr); - - for (entry = arp_tables[hash]; entry != NULL; entry = entry->next) - if (entry->ip == paddr && entry->htype == type) - break; + struct hh_cache *hh = *hhp; + int addr_hint; + unsigned long flags; - /* it's possibly a proxy entry (with a netmask) */ - if (!entry && proxy != PROXY_NONE) - for (entry=arp_tables[PROXY_HASH]; entry != NULL; entry = entry->next) - if ((proxy==PROXY_EXACT) ? (entry->ip==paddr) - : !((entry->ip^paddr)&entry->mask)) - break; + if (hh) + return 1; - return entry; -} + if ((addr_hint = ip_chk_addr(paddr)) != 0) + { + unsigned char haddr[MAX_ADDR_LEN]; + if (hh) + return 1; + hh = kmalloc(sizeof(struct hh_cache), GFP_ATOMIC); + if (!hh) + return 1; + arp_set_predefined(addr_hint, haddr, paddr, dev); + hh->hh_uptodate = 0; + hh->hh_refcnt = 1; + hh->hh_arp = NULL; + hh->hh_next = NULL; + hh->hh_type = htype; + *hhp = hh; + dev->header_cache_update(hh, dev, haddr); + return 0; + } + save_flags(flags); -int arp_find_cache(unsigned char *dp, u32 daddr, struct device *dev) -{ - /* - * We need the broadcast/multicast awareness here and the find routine split up. - */ - struct arp_table *entry; -#ifdef CONFIG_IP_MULTICAST - u32 taddr; -#endif + arp_fast_lock(); - switch (ip_chk_addr(daddr)) + entry = arp_lookup(paddr, 0, dev); + + if (entry) { - case IS_MYADDR: - printk("ARP: arp called for own IP address\n"); - memcpy(dp, dev->dev_addr, dev->addr_len); + cli(); + for (hh = entry->hh; hh; hh=hh->hh_next) + if (hh->hh_type == htype) + break; + if (hh) + { + hh->hh_refcnt++; + *hhp = hh; + restore_flags(flags); + arp_unlock(); return 1; -#ifdef CONFIG_IP_MULTICAST - case IS_MULTICAST: - if(dev->type==ARPHRD_ETHER || dev->type==ARPHRD_IEEE802) + } + restore_flags(flags); + } + + hh = kmalloc(sizeof(struct hh_cache), GFP_ATOMIC); + if (!hh) + { + arp_unlock(); + return 1; + } + + hh->hh_uptodate = 0; + hh->hh_refcnt = 1; + hh->hh_arp = NULL; + hh->hh_next = NULL; + hh->hh_type = htype; + + if (entry) + { + dev->header_cache_update(hh, dev, entry->ha); + *hhp = hh; + cli(); + hh->hh_arp = (void*)entry; + entry->hh = hh; + hh->hh_refcnt++; + restore_flags(flags); + entry->last_used = jiffies; + arp_unlock(); + return 0; + } + + + /* + * Create a new unresolved entry. + */ + + entry = (struct arp_table *) kmalloc(sizeof(struct arp_table), + GFP_ATOMIC); + if (entry == NULL) + { + kfree_s(hh, sizeof(struct hh_cache)); + arp_unlock(); + return 1; + } + + entry->last_updated = entry->last_used = jiffies; + entry->flags = 0; + entry->ip = paddr; + entry->mask = DEF_ARP_NETMASK; + memset(entry->ha, 0, dev->addr_len); + entry->dev = dev; + entry->hh = hh; + ATOMIC_INCR(&hh->hh_refcnt); + init_timer(&entry->timer); + entry->timer.function = arp_expire_request; + entry->timer.data = (unsigned long)entry; + entry->timer.expires = jiffies + ARP_RES_TIME; + skb_queue_head_init(&entry->skb); + + if (arp_lock == 1) + { + unsigned long hash = HASH(paddr); + cli(); + entry->next = arp_tables[hash]; + arp_tables[hash] = entry; + hh->hh_arp = (void*)entry; + entry->retries = ARP_MAX_TRIES; + restore_flags(flags); + + add_timer(&entry->timer); + arp_send(ARPOP_REQUEST, ETH_P_ARP, paddr, dev, dev->pa_addr, NULL, dev->dev_addr, NULL); + } + else + { +#if RT_CACHE_DEBUG >= 1 + printk("arp_cache_bind: %08x backlogged\n", entry->ip); +#endif + arp_enqueue(&arp_backlog, entry); + arp_bh_mask |= ARP_BH_BACKLOG; + } + *hhp = hh; + arp_unlock(); + return 0; +} + +static void arp_run_bh() +{ + unsigned long flags; + struct arp_table *entry, *entry1; + struct hh_cache *hh; + __u32 sip; + + save_flags(flags); + cli(); + if (!arp_lock) + { + arp_fast_lock(); + + while ((entry = arp_dequeue(&arp_backlog)) != NULL) + { + unsigned long hash; + sti(); + sip = entry->ip; + hash = HASH(sip); + + /* It's possible, that an entry with the same pair + * (addr,type) was already created. Our entry is older, + * so it should be discarded. + */ + for (entry1=arp_tables[hash]; entry1; entry1=entry1->next) + if (entry1->ip==sip && entry1->dev == entry->dev) + break; + + if (!entry1) { - dp[0]=0x01; - dp[1]=0x00; - dp[2]=0x5e; - taddr=ntohl(daddr); - dp[5]=taddr&0xff; - taddr=taddr>>8; - dp[4]=taddr&0xff; - taddr=taddr>>8; - dp[3]=taddr&0x7f; - return 1; - } - /* - * If a device does not support multicast broadcast the stuff (eg AX.25 for now) - */ + struct device * dev = entry->dev; + cli(); + entry->next = arp_tables[hash]; + arp_tables[hash] = entry; + for (hh=entry->hh; hh; hh=hh->hh_next) + hh->hh_arp = (void*)entry; + sti(); + del_timer(&entry->timer); + entry->timer.expires = jiffies + ARP_RES_TIME; + add_timer(&entry->timer); + entry->retries = ARP_MAX_TRIES; + arp_send(ARPOP_REQUEST, ETH_P_ARP, entry->ip, dev, dev->pa_addr, NULL, dev->dev_addr, NULL); +#if RT_CACHE_DEBUG >= 1 + printk("arp_run_bh: %08x reinstalled\n", sip); #endif - - case IS_BROADCAST: - memcpy(dp, dev->broadcast, dev->addr_len); - return 1; - - default: - entry=arp_lookup(daddr, PROXY_NONE, dev->type); - if(entry) + } + else { - memcpy(dp,entry->ha, ETH_ALEN); - return 1; + struct sk_buff * skb; + struct hh_cache * next; + + /* Discard entry, but preserve its hh's and + * skb's. + */ + cli(); + for (hh=entry->hh; hh; hh=next) + { + next = hh->hh_next; + hh->hh_next = entry1->hh; + entry1->hh = hh; + hh->hh_arp = (void*)entry1; + } + entry->hh = NULL; + + /* Prune skb list from entry + * and graft it to entry1. + */ + while ((skb = skb_dequeue(&entry->skb)) != NULL) + { + skb_device_lock(skb); + sti(); + skb_queue_tail(&entry1->skb, skb); + skb_device_unlock(skb); + cli(); + } + sti(); + +#if RT_CACHE_DEBUG >= 1 + printk("arp_run_bh: entry %08x was born dead\n", entry->ip); +#endif + arp_free_entry(entry); + + if (entry1->flags & ATF_COM) + { + arp_update_hhs(entry1); + arp_send_q(entry1); + } } + cli(); + } + arp_bh_mask &= ~ARP_BH_BACKLOG; + arp_unlock(); } - return 0; + restore_flags(flags); } + /* * Set (create) an ARP cache entry. */ -static int arp_req_set(struct arpreq *req) +static int arp_req_set(struct arpreq *r, struct device * dev) { - struct arpreq r; struct arp_table *entry; struct sockaddr_in *si; - int htype, hlen; struct rtable *rt; + struct device * dev1; u32 ip; - memcpy_fromfs(&r, req, sizeof(r)); - - /* We only understand about IP addresses... */ - if (r.arp_pa.sa_family != AF_INET) - return -EPFNOSUPPORT; - /* * Find out about the hardware type. * We have to be compatible with BSD UNIX, so we have to * assume that a "not set" value (i.e. 0) means Ethernet. + * + * ANK: Hey, who wrote it? Do you really mean that BSD considers + * ARPHRD_NETROM as ARPHRD_ETHER, or somthing another? */ - switch (r.arp_ha.sa_family) { - case ARPHRD_ETHER: - htype = ARPHRD_ETHER; - hlen = ETH_ALEN; - break; - - case ARPHRD_ARCNET: - htype = ARPHRD_ARCNET; - hlen = 1; /* length of arcnet addresses */ - break; - -#ifdef CONFIG_AX25 - case ARPHRD_AX25: - htype = ARPHRD_AX25; - hlen = AX25_ADDR_LEN; - break; -#endif -#ifdef CONFIG_NETROM - case ARPHRD_NETROM: - htype = ARPHRD_NETROM; - hlen = AX25_ADDR_LEN; - break; -#endif - case ARPHRD_IEEE802: - htype = ARPHRD_IEEE802; - hlen = TR_ALEN; - break; - default: - return -EPFNOSUPPORT; - } - - si = (struct sockaddr_in *) &r.arp_pa; + si = (struct sockaddr_in *) &r->arp_pa; ip = si->sin_addr.s_addr; - if (ip == 0) - { - printk("ARP: SETARP: requested PA is 0.0.0.0 !\n"); - return -EINVAL; - } /* - * Is it reachable directly ? + * Is it reachable ? */ - rt = ip_rt_route(ip, NULL, NULL); - if (rt == NULL) + rt = ip_rt_route(ip, 0); + if (!rt) return -ENETUNREACH; + dev1 = rt->rt_dev; + ip_rt_put(rt); + + if (((r->arp_flags & ATF_PUBL) && dev == dev1) || + (!(r->arp_flags & ATF_PUBL) && dev != dev1)) + return -EINVAL; + +#if RT_CACHE_DEBUG >= 1 + if (arp_lock) + printk("arp_req_set: bug\n"); +#endif + arp_fast_lock(); /* * Is there an existing entry for this address? */ - - cli(); /* * Find the entry */ - entry = arp_lookup(ip, PROXY_EXACT, htype); - if (entry && (entry->flags & ATF_PUBL) != (r.arp_flags & ATF_PUBL)) + + entry = arp_lookup(ip, r->arp_flags & ~ATF_NETMASK, dev); + + if (entry) { - sti(); - arp_destroy(ip,1); - cli(); + arp_destroy(entry); entry = NULL; } @@ -1292,77 +1652,83 @@ static int arp_req_set(struct arpreq *req) if (entry == NULL) { - unsigned long hash = HASH(ip); - if (r.arp_flags & ATF_PUBL) - hash = PROXY_HASH; - entry = (struct arp_table *) kmalloc(sizeof(struct arp_table), GFP_ATOMIC); if (entry == NULL) { - sti(); + arp_unlock(); return -ENOMEM; } entry->ip = ip; - entry->hlen = hlen; - entry->htype = htype; + entry->hh = NULL; init_timer(&entry->timer); - entry->next = arp_tables[hash]; - arp_tables[hash] = entry; + entry->timer.function = arp_expire_request; + entry->timer.data = (unsigned long)entry; + + if (r->arp_flags & ATF_PUBL) + { + cli(); + entry->next = arp_proxy_list; + arp_proxy_list = entry; + sti(); + } + else + { + unsigned long hash = HASH(ip); + cli(); + entry->next = arp_tables[hash]; + arp_tables[hash] = entry; + sti(); + } skb_queue_head_init(&entry->skb); } /* * We now have a pointer to an ARP entry. Update it! */ - memcpy(&entry->ha, &r.arp_ha.sa_data, hlen); - entry->last_used = jiffies; - entry->flags = r.arp_flags | ATF_COM; + if ((r->arp_flags & ATF_COM) && !r->arp_ha.sa_data[0]) + memcpy(&entry->ha, dev->dev_addr, dev->addr_len); + else + memcpy(&entry->ha, &r->arp_ha.sa_data, dev->addr_len); + entry->last_updated = entry->last_used = jiffies; + entry->flags = r->arp_flags | ATF_COM; if ((entry->flags & ATF_PUBL) && (entry->flags & ATF_NETMASK)) - { - si = (struct sockaddr_in *) &r.arp_netmask; - entry->mask = si->sin_addr.s_addr; - } + { + si = (struct sockaddr_in *) &r->arp_netmask; + entry->mask = si->sin_addr.s_addr; + } else - entry->mask = DEF_ARP_NETMASK; - entry->dev = rt->rt_dev; - arp_cache_stamp++; - sti(); - + entry->mask = DEF_ARP_NETMASK; + entry->dev = dev; + arp_update_hhs(entry); + arp_unlock(); return 0; } + /* * Get an ARP cache entry. */ -static int arp_req_get(struct arpreq *req) +static int arp_req_get(struct arpreq *r, struct device *dev) { - struct arpreq r; struct arp_table *entry; struct sockaddr_in *si; - /* - * We only understand about IP addresses... - */ - - memcpy_fromfs(&r, req, sizeof(r)); + si = (struct sockaddr_in *) &r->arp_pa; - if (r.arp_pa.sa_family != AF_INET) - return -EPFNOSUPPORT; +#if RT_CACHE_DEBUG >= 1 + if (arp_lock) + printk("arp_req_set: bug\n"); +#endif + arp_fast_lock(); - /* - * Is there an existing entry for this address? - */ - - si = (struct sockaddr_in *) &r.arp_pa; - cli(); - entry = arp_lookup(si->sin_addr.s_addr, PROXY_ANY, r.arp_ha.sa_family); + entry = arp_lookup(si->sin_addr.s_addr, r->arp_flags|ATF_NETMASK, dev); if (entry == NULL) { - sti(); + arp_unlock(); return -ENXIO; } @@ -1370,19 +1736,54 @@ static int arp_req_get(struct arpreq *req) * We found it; copy into structure. */ - memcpy(r.arp_ha.sa_data, &entry->ha, entry->hlen); - r.arp_ha.sa_family = entry->htype; - r.arp_flags = entry->flags; - sti(); - - /* - * Copy the information back - */ - - memcpy_tofs(req, &r, sizeof(r)); + memcpy(r->arp_ha.sa_data, &entry->ha, entry->dev->addr_len); + r->arp_ha.sa_family = entry->dev->type; + r->arp_flags = entry->flags; + strncpy(r->arp_dev, entry->dev->name, 16); + arp_unlock(); return 0; } +static int arp_req_delete(struct arpreq *r, struct device * dev) +{ + struct arp_table *entry; + struct sockaddr_in *si; + + si = (struct sockaddr_in *) &r->arp_pa; +#if RT_CACHE_DEBUG >= 1 + if (arp_lock) + printk("arp_req_delete: bug\n"); +#endif + arp_fast_lock(); + + if (!(r->arp_flags & ATF_PUBL)) + { + for (entry = arp_tables[HASH(si->sin_addr.s_addr)]; + entry != NULL; entry = entry->next) + if (entry->ip == si->sin_addr.s_addr + && entry->dev == dev) + { + arp_destroy(entry); + arp_unlock(); + return 0; + } + } + else + { + for (entry = arp_proxy_list; + entry != NULL; entry = entry->next) + if (entry->ip == si->sin_addr.s_addr + && entry->dev == dev) + { + arp_destroy(entry); + arp_unlock(); + return 0; + } + } + + arp_unlock(); + return -ENXIO; +} /* * Handle an ARP layer I/O control request. @@ -1390,39 +1791,116 @@ static int arp_req_get(struct arpreq *req) int arp_ioctl(unsigned int cmd, void *arg) { - struct arpreq r; - struct sockaddr_in *si; int err; + struct arpreq r; + + struct device * dev = NULL; switch(cmd) { case SIOCDARP: + case SIOCSARP: if (!suser()) return -EPERM; - err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq)); - if(err) - return err; - memcpy_fromfs(&r, arg, sizeof(r)); - if (r.arp_pa.sa_family != AF_INET) - return -EPFNOSUPPORT; - si = (struct sockaddr_in *) &r.arp_pa; - arp_destroy(si->sin_addr.s_addr, 1); - return 0; case SIOCGARP: - err = verify_area(VERIFY_WRITE, arg, sizeof(struct arpreq)); - if(err) + err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq)); + if (err) return err; - return arp_req_get((struct arpreq *)arg); - case SIOCSARP: + memcpy_fromfs(&r, arg, sizeof(struct arpreq)); + break; + case OLD_SIOCDARP: + case OLD_SIOCSARP: if (!suser()) return -EPERM; - err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq)); - if(err) + case OLD_SIOCGARP: + err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq_old)); + if (err) return err; - return arp_req_set((struct arpreq *)arg); + memcpy_fromfs(&r, arg, sizeof(struct arpreq_old)); + memset(&r.arp_dev, 0, sizeof(r.arp_dev)); + break; default: return -EINVAL; } + + if (r.arp_pa.sa_family != AF_INET) + return -EPFNOSUPPORT; + if (((struct sockaddr_in *)&r.arp_pa)->sin_addr.s_addr == 0) + return -EINVAL; + + if (r.arp_dev[0]) + { + if ((dev = dev_get(r.arp_dev)) == NULL) + return -ENODEV; + + if (!r.arp_ha.sa_family) + r.arp_ha.sa_family = dev->type; + else if (r.arp_ha.sa_family != dev->type) + return -EINVAL; + } + else + { + /* + * Device was not specified. Take the first suitable one. + */ + if ((dev = dev_getbytype(r.arp_ha.sa_family)) == NULL) + return -ENODEV; + } + + switch(cmd) + { + case SIOCDARP: + return arp_req_delete(&r, dev); + case SIOCSARP: + return arp_req_set(&r, dev); + case OLD_SIOCDARP: + /* old SIOCDARP destoyes both + * normal and proxy mappings + */ + r.arp_flags &= ~ATF_PUBL; + err = arp_req_delete(&r, dev); + r.arp_flags |= ATF_PUBL; + if (!err) + arp_req_delete(&r, dev); + else + err = arp_req_delete(&r, dev); + return err; + case OLD_SIOCSARP: + err = arp_req_set(&r, dev); + /* old SIOCSARP works so funny, + * that its behaviour can be emulated + * only approximately 8). + * It should work. --ANK + */ + if (r.arp_flags & ATF_PUBL) + { + r.arp_flags &= ~ATF_PUBL; + arp_req_delete(&r, dev); + } + return err; + case SIOCGARP: + err = verify_area(VERIFY_WRITE, arg, sizeof(struct arpreq)); + if (err) + return err; + err = arp_req_get(&r, dev); + if (!err) + memcpy_tofs(arg, &r, sizeof(r)); + return err; + case OLD_SIOCGARP: + err = verify_area(VERIFY_WRITE, arg, sizeof(struct arpreq_old)); + if (err) + return err; + r.arp_flags &= ~ATF_PUBL; + err = arp_req_get(&r, dev); + if (err < 0) + { + r.arp_flags |= ATF_PUBL; + err = arp_req_get(&r, dev); + } + if (!err) + memcpy_tofs(arg, &r, sizeof(struct arpreq_old)); + return err; + } /*NOTREACHED*/ return 0; } diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 1c91d9a7c911..b761ba93f3c1 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -227,3 +227,16 @@ struct device *ip_dev_find(unsigned long addr) } return NULL; } + +struct device *dev_getbytype(unsigned short type) +{ + struct device *dev; + + for (dev = dev_base; dev != NULL; dev = dev->next) + { + if (dev->type == type && !(dev->flags&(IFF_LOOPBACK|IFF_NOARP))) + return(dev); + } + return(NULL); +} + diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 7e815223e677..a6d0b43ec481 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -362,9 +362,51 @@ static void icmp_unreach(struct icmphdr *icmph, struct sk_buff *skb, struct devi case ICMP_PORT_UNREACH: break; case ICMP_FRAG_NEEDED: +#ifdef CONFIG_NO_PATH_MTU_DISCOVERY printk("ICMP: %s: fragmentation needed and DF set.\n", in_ntoa(iph->daddr)); break; +#else + { + unsigned short old_mtu = ntohs(iph->tot_len); + unsigned short new_mtu = ntohs(icmph->un.echo.sequence); + + if (new_mtu < 68 || new_mtu >= old_mtu) + { + /* + * It is either dumb router, which does not + * understand Path MTU Disc. protocol + * or broken (f.e. Linux<=1.3.37 8) router. + * Try to guess... + * The table is taken from RFC-1191. + */ + if (old_mtu > 32000) + new_mtu = 32000; + else if (old_mtu > 17914) + new_mtu = 17914; + else if (old_mtu > 8166) + new_mtu = 8166; + else if (old_mtu > 4352) + new_mtu = 4352; + else if (old_mtu > 2002) + new_mtu = 2002; + else if (old_mtu > 1492) + new_mtu = 1492; + else if (old_mtu > 576) + new_mtu = 576; + else if (old_mtu > 296) + new_mtu = 296; + else + new_mtu = 68; + } + /* + * Ugly trick to pass MTU to protocol layer. + * Really we should add argument "info" to error handler. + */ + iph->id = htons(new_mtu); + break; + } +#endif case ICMP_SR_FAILED: printk("ICMP: %s: Source Route Failed.\n", in_ntoa(iph->daddr)); break; @@ -427,9 +469,6 @@ static void icmp_unreach(struct icmphdr *icmph, struct sk_buff *skb, struct devi static void icmp_redirect(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev, __u32 source, __u32 daddr, int len) { -#ifndef CONFIG_IP_FORWARD - struct rtable *rt; -#endif struct iphdr *iph; unsigned long ip; @@ -472,16 +511,8 @@ static void icmp_redirect(struct icmphdr *icmph, struct sk_buff *skb, struct dev * (not some confused thing sending our * address) */ - rt = ip_rt_route(ip, NULL, NULL); - if (!rt) - break; - if (rt->rt_gateway != source || - ((icmph->un.gateway^dev->pa_addr)&dev->pa_mask) || - ip_chk_addr(icmph->un.gateway)) - break; printk("ICMP redirect from %s\n", in_ntoa(source)); - ip_rt_add((RTF_DYNAMIC | RTF_MODIFIED | RTF_HOST | RTF_GATEWAY), - ip, 0, icmph->un.gateway, dev,0, 0, 0, 0); + ip_rt_redirect(source, ip, icmph->un.gateway, dev); break; case ICMP_REDIR_NETTOS: case ICMP_REDIR_HOSTTOS: diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index b61d3c6bbb65..56e0a673fbe8 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -103,7 +103,7 @@ static void igmp_send_report(struct device *dev, unsigned long address, int type if(skb==NULL) return; tmp=ip_build_header(skb, INADDR_ANY, address, &dev, IPPROTO_IGMP, NULL, - 28 , 0, 1); + 28 , 0, 1, NULL); if(tmp<0) { kfree_skb(skb, FREE_WRITE); diff --git a/net/ipv4/ip_forward.c b/net/ipv4/ip_forward.c index 3e1ef8bef9da..b6eec53f540c 100644 --- a/net/ipv4/ip_forward.c +++ b/net/ipv4/ip_forward.c @@ -90,6 +90,7 @@ int ip_forward(struct sk_buff *skb, struct device *dev, int is_frag, unsigned char *ptr; /* Data pointer */ unsigned long raddr; /* Router IP address */ struct options * opt = (struct options*)skb->proto_priv; + struct hh_cache *hh = NULL; int encap = 0; /* Encap length */ #ifdef CONFIG_FIREWALL int fw_res = 0; /* Forwarding result */ @@ -159,7 +160,8 @@ int ip_forward(struct sk_buff *skb, struct device *dev, int is_frag, * and give it to the IP sender for further processing. */ - rt = ip_rt_route(target_addr, NULL, NULL); + rt = ip_rt_route(target_addr, 0); + if (rt == NULL) { /* @@ -181,31 +183,22 @@ int ip_forward(struct sk_buff *skb, struct device *dev, int is_frag, raddr = rt->rt_gateway; - if (raddr != 0) - { + if (opt->is_strictroute && (rt->rt_flags & RTF_GATEWAY)) { /* * Strict routing permits no gatewaying */ - if (opt->is_strictroute) - { - icmp_send(skb, ICMP_DEST_UNREACH, ICMP_SR_FAILED, 0, dev); - return -1; - } - - /* - * There is a gateway so find the correct route for it. - * Gateways cannot in turn be gatewayed. - */ + ip_rt_put(rt); + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_SR_FAILED, 0, dev); + return -1; } - else - raddr = target_addr; /* * Having picked a route we can now send the frame out. */ dev2 = rt->rt_dev; + hh = rt->rt_hh; /* * In IP you never have to forward a frame on the interface that it * arrived upon. We now generate an ICMP HOST REDIRECT giving the route @@ -227,6 +220,7 @@ int ip_forward(struct sk_buff *skb, struct device *dev, int is_frag, raddr=skb->raddr; if(is_frag&16) /* VIFF_TUNNEL mode */ encap=20; + rt=NULL; } #endif @@ -250,7 +244,8 @@ int ip_forward(struct sk_buff *skb, struct device *dev, int is_frag, if (skb->len+encap > dev2->mtu && (ntohs(iph->frag_off) & IP_DF)) { ip_statistics.IpFragFails++; - icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, dev2->mtu, dev); + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(dev2->mtu), dev); + ip_rt_put(rt); return -1; } @@ -271,6 +266,7 @@ int ip_forward(struct sk_buff *skb, struct device *dev, int is_frag, if (skb2 == NULL) { NETDEBUG(printk("\nIP: No memory available for IP forward\n")); + ip_rt_put(rt); return -1; } @@ -286,7 +282,7 @@ int ip_forward(struct sk_buff *skb, struct device *dev, int is_frag, } else #endif - ip_send(skb2,raddr,skb->len,dev2,dev2->pa_addr); + ip_send(rt,skb2,raddr,skb->len,dev2,dev2->pa_addr); /* * We have to copy the bytes over as the new header wouldn't fit @@ -320,7 +316,18 @@ int ip_forward(struct sk_buff *skb, struct device *dev, int is_frag, #endif skb->arp=1; skb->raddr=raddr; - if(dev2->hard_header) + if (hh) + { + memcpy(skb_push(skb, dev2->hard_header_len), hh->hh_data, dev2->hard_header_len); + if (!hh->hh_uptodate) + { +#if RT_CACHE_DEBUG >= 2 + printk("ip_forward: hh miss %08x via %08x\n", target_addr, rt->rt_gateway); +#endif + skb->arp = 0; + } + } + else if (dev2->hard_header) { if(dev2->hard_header(skb, dev2, ETH_P_IP, NULL, NULL, skb->len)<0) skb->arp=0; @@ -418,7 +425,11 @@ int ip_forward(struct sk_buff *skb, struct device *dev, int is_frag, } } else + { + ip_rt_put(rt); return -1; + } + ip_rt_put(rt); /* * Tell the caller if their buffer is free. diff --git a/net/ipv4/ip_fw.c b/net/ipv4/ip_fw.c index 2f9c485c6d81..4753a2653fdb 100644 --- a/net/ipv4/ip_fw.c +++ b/net/ipv4/ip_fw.c @@ -196,8 +196,8 @@ int ip_fw_chk(struct iphdr *ip, struct device *rif, struct ip_fw *chain, int pol __u16 src_port=0, dst_port=0, icmp_type=0; unsigned short f_prt=0, prt; char notcpsyn=1, notcpack=1, match; - unsigned short f_flag; unsigned short offset; + int answer, priority; /* * If the chain is empty follow policy. The BSD one @@ -465,24 +465,39 @@ int ip_fw_chk(struct iphdr *ip, struct device *rif, struct ip_fw *chain, int pol break; } /* Loop */ - if(opt == 1) - return 0; - + answer = FW_BLOCK; + /* * We rely on policy defined in the rejecting entry or, if no match * was found, we rely on the general policy variable for this type * of firewall. */ - if(f!=NULL) /* A match was found */ - f_flag=f->fw_flg; + if(f!=NULL) + { + policy=f->fw_flg; + priority=f->fw_priority; + } else - f_flag=policy; - if(f_flag&IP_FW_F_ACCEPT) - return ((f_flag&IP_FW_F_MASQ)?FW_MASQUERADE:FW_ACCEPT); - if(f_flag&IP_FW_F_ICMPRPL) - return FW_REJECT; - return FW_BLOCK; + priority=0xFF00; + + if(opt != 1) + { + if(policy&IP_FW_F_ACCEPT) + answer=(policy&IP_FW_F_MASQ)?FW_MASQUERADE:FW_ACCEPT; + else + if(policy&IP_FW_F_ICMPRPL) + answer = FW_REJECT; + } + + if (answer == 0) { /* Adjust priority and recompute checksum */ + __u8 old_tos = ip->tos; + ip->tos = (old_tos & (priority>>8)) ^ priority; + if (ip->tos != old_tos) + ip_send_check(ip); + } + + return answer; } #ifdef CONFIG_IP_MASQUERADE @@ -1022,7 +1037,7 @@ static void free_fw_chain(struct ip_fw *volatile* chainptr) /* Volatiles to keep some of the compiler versions amused */ -static int add_to_chain(struct ip_fw *volatile* chainptr, struct ip_fw *frwl) +static int add_to_chain(struct ip_fw *volatile* chainptr, struct ip_fw *frwl,int len) { struct ip_fw *ftmp; struct ip_fw *chtmp=NULL; @@ -1046,8 +1061,11 @@ static int add_to_chain(struct ip_fw *volatile* chainptr, struct ip_fw *frwl) return( ENOMEM ); } - memcpy(ftmp, frwl, sizeof( struct ip_fw ) ); + memcpy(ftmp, frwl, len); + if (len == sizeof (struct ip_fw_old)) + ftmp->fw_priority = 0xFF00; /* and_mask, xor_mask */ + ftmp->fw_priority = (ftmp->fw_priority & 0xFFFC) | 0x0300; ftmp->fw_pcnt=0L; ftmp->fw_bcnt=0L; @@ -1286,7 +1304,7 @@ static int del_from_chain(struct ip_fw *volatile*chainptr, struct ip_fw *frwl) struct ip_fw *check_ipfw_struct(struct ip_fw *frwl, int len) { - if ( len != sizeof(struct ip_fw) ) + if ( len != sizeof(struct ip_fw) && len != sizeof(struct ip_fw_old)) { #ifdef DEBUG_CONFIG_IP_FIREWALL printk("ip_fw_ctl: len=%d, want %d\n",len, sizeof(struct ip_fw)); @@ -1370,7 +1388,7 @@ int ip_acct_ctl(int stage, void *m, int len) switch (stage) { case IP_ACCT_ADD: - return( add_to_chain(&ip_acct_chain,frwl)); + return( add_to_chain(&ip_acct_chain,frwl,len)); case IP_ACCT_DEL: return( del_from_chain(&ip_acct_chain,frwl)); default: @@ -1489,9 +1507,9 @@ int ip_fw_ctl(int stage, void *m, int len) switch (stage) { case IP_FW_ADD_BLK: - return(add_to_chain(&ip_fw_blk_chain,frwl)); + return(add_to_chain(&ip_fw_blk_chain,frwl,len)); case IP_FW_ADD_FWD: - return(add_to_chain(&ip_fw_fwd_chain,frwl)); + return(add_to_chain(&ip_fw_fwd_chain,frwl,len)); case IP_FW_DEL_BLK: return(del_from_chain(&ip_fw_blk_chain,frwl)); case IP_FW_DEL_FWD: @@ -1565,6 +1583,7 @@ static int ip_chain_procinfo(int stage, char *buffer, char **start, i->fw_nsp,i->fw_ndp, i->fw_pcnt,i->fw_bcnt); for (p = 0; p < IP_FW_MAX_PORTS; p++) len+=sprintf(buffer+len, " %u", i->fw_pts[p]); + len+=sprintf(buffer+len, " M%04X", i->fw_priority); buffer[len++]='\n'; buffer[len]='\0'; pos=begin+len; diff --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c index 5a320c00b551..8162e1949ea4 100644 --- a/net/ipv4/ip_options.c +++ b/net/ipv4/ip_options.c @@ -118,14 +118,14 @@ int ip_options_echo(struct options * dopt, struct options * sopt, memcpy(dptr, sptr+sopt->ts, optlen); if (soffset <= optlen) { - if (dopt->ts_needaddr) + if (sopt->ts_needaddr) { if (soffset + 3 > optlen) return -EINVAL; dopt->ts_needaddr = 1; soffset += 4; } - if (dopt->ts_needtime) + if (sopt->ts_needtime) { if (soffset + 3 > optlen) return -EINVAL; @@ -376,10 +376,8 @@ int ip_options_compile(struct options * opt, struct sk_buff * skb) case IPOPT_TS_TSONLY: opt->ts = optptr - iph; if (skb) - { timeptr = (__u32*)&optptr[ts->ptr-1]; - opt->is_changed = 1; - } + opt->ts_needtime = 1; ts->ptr += 4; break; case IPOPT_TS_TSANDADDR: diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 1d05e2f7e632..5340b9acf218 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -86,7 +86,7 @@ static void ip_loopback(struct device *old_dev, struct sk_buff *skb) /* * Put a MAC header on the packet */ - ip_send(newskb, skb->ip_hdr->daddr, len, dev, skb->ip_hdr->saddr); + ip_send(NULL,newskb, skb->ip_hdr->daddr, len, dev, skb->ip_hdr->saddr); /* * Add the rest of the data space. */ @@ -110,7 +110,7 @@ static void ip_loopback(struct device *old_dev, struct sk_buff *skb) * Take an skb, and fill in the MAC header. */ -int ip_send(struct sk_buff *skb, __u32 daddr, int len, struct device *dev, __u32 saddr) +int ip_send(struct rtable * rt, struct sk_buff *skb, __u32 daddr, int len, struct device *dev, __u32 saddr) { int mac = 0; @@ -123,6 +123,18 @@ int ip_send(struct sk_buff *skb, __u32 daddr, int len, struct device *dev, __u32 * (rebuild header will sort this out) */ skb_reserve(skb,(dev->hard_header_len+15)&~15); /* 16 byte aligned IP headers are good */ + if (rt && dev == rt->rt_dev && rt->rt_hh) + { + memcpy(skb_push(skb,dev->hard_header_len),rt->rt_hh->hh_data,dev->hard_header_len); + if (rt->rt_hh->hh_uptodate) + return dev->hard_header_len; +#if RT_CACHE_DEBUG >= 2 + printk("ip_send: hh miss %08x via %08x\n", daddr, rt->rt_gateway); +#endif + skb->arp = 0; + skb->raddr = daddr; + return -dev->hard_header_len; + } mac = dev->hard_header(skb, dev, ETH_P_IP, NULL, NULL, len); if (mac < 0) { @@ -134,7 +146,7 @@ int ip_send(struct sk_buff *skb, __u32 daddr, int len, struct device *dev, __u32 return mac; } -static int ip_send_room(struct sk_buff *skb, __u32 daddr, int len, struct device *dev, __u32 saddr) +static int ip_send_room(struct rtable * rt, struct sk_buff *skb, __u32 daddr, int len, struct device *dev, __u32 saddr) { int mac = 0; @@ -143,6 +155,18 @@ static int ip_send_room(struct sk_buff *skb, __u32 daddr, int len, struct device if (dev->hard_header) { skb_reserve(skb,MAX_HEADER); + if (rt && dev == rt->rt_dev && rt->rt_hh) + { + memcpy(skb_push(skb,dev->hard_header_len),rt->rt_hh->hh_data,dev->hard_header_len); + if (rt->rt_hh->hh_uptodate) + return dev->hard_header_len; +#if RT_CACHE_DEBUG >= 2 + printk("ip_send_room: hh miss %08x via %08x\n", daddr, rt->rt_gateway); +#endif + skb->arp = 0; + skb->raddr = daddr; + return -dev->hard_header_len; + } mac = dev->hard_header(skb, dev, ETH_P_IP, NULL, NULL, len); if (mac < 0) { @@ -163,15 +187,16 @@ int ip_id_count = 0; * routing/ARP tables to select a device struct. */ int ip_build_header(struct sk_buff *skb, __u32 saddr, __u32 daddr, - struct device **dev, int type, struct options *opt, int len, int tos, int ttl) + struct device **dev, int type, struct options *opt, + int len, int tos, int ttl, struct rtable ** rp) { struct rtable *rt; __u32 raddr; int tmp; - __u32 src; struct iphdr *iph; __u32 final_daddr = daddr; + if (opt && opt->srr) daddr = opt->faddr; @@ -183,12 +208,22 @@ int ip_build_header(struct sk_buff *skb, __u32 saddr, __u32 daddr, if(MULTICAST(daddr) && *dev==NULL && skb->sk && *skb->sk->ip_mc_name) *dev=dev_get(skb->sk->ip_mc_name); #endif + if (rp) + { + rt = ip_check_route(rp, daddr, skb->localroute); + /* + * If rp != NULL rt_put following below should not + * release route, so that... + */ + if (rt) + ATOMIC_INCR(&rt->rt_refcnt); + } + else + rt = ip_rt_route(daddr, skb->localroute); + + if (*dev == NULL) { - if(skb->localroute) - rt = ip_rt_local(daddr, NULL, &src); - else - rt = ip_rt_route(daddr, NULL, &src); if (rt == NULL) { ip_statistics.IpOutNoRoutes++; @@ -196,43 +231,24 @@ int ip_build_header(struct sk_buff *skb, __u32 saddr, __u32 daddr, } *dev = rt->rt_dev; - /* - * If the frame is from us and going off machine it MUST MUST MUST - * have the output device ip address and never the loopback - */ - if (LOOPBACK(saddr) && !LOOPBACK(daddr)) - saddr = src;/*rt->rt_dev->pa_addr;*/ - raddr = rt->rt_gateway; - } - else - { - /* - * We still need the address of the first hop. - */ - if(skb->localroute) - rt = ip_rt_local(daddr, NULL, &src); - else - rt = ip_rt_route(daddr, NULL, &src); - /* - * If the frame is from us and going off machine it MUST MUST MUST - * have the output device ip address and never the loopback - */ - if (LOOPBACK(saddr) && !LOOPBACK(daddr)) - saddr = src;/*rt->rt_dev->pa_addr;*/ - raddr = (rt == NULL) ? 0 : rt->rt_gateway; - } + if ((LOOPBACK(saddr) && !LOOPBACK(daddr)) || !saddr) + saddr = rt ? rt->rt_src : (*dev)->pa_addr; - /* - * No source addr so make it our addr - */ - if (saddr == 0) - saddr = src; + raddr = rt ? rt->rt_gateway : 0; + + if (opt && opt->is_strictroute && rt && (rt->rt_flags & RTF_GATEWAY)) + { + ip_rt_put(rt); + ip_statistics.IpOutNoRoutes++; + return -ENETUNREACH; + } /* * No gateway so aim at the real destination */ + if (raddr == 0) raddr = daddr; @@ -240,10 +256,12 @@ int ip_build_header(struct sk_buff *skb, __u32 saddr, __u32 daddr, * Now build the MAC header. */ - if(type==IPPROTO_TCP) - tmp = ip_send_room(skb, raddr, len, *dev, saddr); + if (type==IPPROTO_TCP) + tmp = ip_send_room(rt, skb, raddr, len, *dev, saddr); else - tmp = ip_send(skb, raddr, len, *dev, saddr); + tmp = ip_send(rt, skb, raddr, len, *dev, saddr); + + ip_rt_put(rt); /* * Book keeping @@ -285,11 +303,6 @@ int ip_build_header(struct sk_buff *skb, __u32 saddr, __u32 daddr, if (!opt || !opt->optlen) return sizeof(struct iphdr) + tmp; - if (opt->is_strictroute && rt && rt->rt_gateway) - { - ip_statistics.IpOutNoRoutes++; - return -ENETUNREACH; - } iph->ihl += opt->optlen>>2; ip_options_build(skb, opt, final_daddr, (*dev)->pa_addr, 0); return iph->ihl*4 + tmp; @@ -563,8 +576,9 @@ int ip_build_xmit(struct sock *sk, __u32 saddr; unsigned short id; struct iphdr *iph; - int local=0; - struct device *dev; + __u32 raddr; + struct device *dev = NULL; + struct hh_cache * hh=NULL; int nfrags=0; __u32 true_daddr = daddr; @@ -588,60 +602,17 @@ int ip_build_xmit(struct sock *sk, else { #endif - /* - * Perform the IP routing decisions - */ - - if(sk->localroute || flags&MSG_DONTROUTE) - local=1; - - rt = sk->ip_route_cache; - - /* - * See if the routing cache is outdated. We need to clean this up once we are happy it is reliable - * by doing the invalidation actively in the route change and header change. - */ - - saddr=sk->ip_route_saddr; - if(!rt || sk->ip_route_stamp != rt_stamp || - daddr!=sk->ip_route_daddr || sk->ip_route_local!=local || - (sk->saddr && sk->saddr != saddr)) - { - if(local) - rt = ip_rt_local(daddr, NULL, &saddr); - else - rt = ip_rt_route(daddr, NULL, &saddr); - sk->ip_route_local=local; - sk->ip_route_daddr=daddr; - sk->ip_route_saddr=saddr; - sk->ip_route_stamp=rt_stamp; - sk->ip_route_cache=rt; - sk->ip_hcache_ver=NULL; - sk->ip_hcache_state= 0; - } - else if(rt) - { - /* - * Attempt header caches only if the cached route is being reused. Header cache - * is not ultra cheap to set up. This means we only set it up on the second packet, - * so one shot communications are not slowed. We assume (seems reasonable) that 2 is - * probably going to be a stream of data. - */ - if(rt->rt_dev->header_cache && sk->ip_hcache_state!= -1) - { - if(sk->ip_hcache_ver==NULL || sk->ip_hcache_stamp!=*sk->ip_hcache_ver) - rt->rt_dev->header_cache(rt->rt_dev,sk,saddr,daddr); - else - /* Can't cache. Remember this */ - sk->ip_hcache_state= -1; - } - } - + rt = ip_check_route(&sk->ip_route_cache, daddr, + sk->localroute || (flags&MSG_DONTROUTE) || + (opt && opt->is_strictroute)); if (rt == NULL) { - ip_statistics.IpOutNoRoutes++; + ip_statistics.IpOutNoRoutes++; return(-ENETUNREACH); } + saddr = rt->rt_src; + + hh = rt->rt_hh; if (sk->saddr && (!LOOPBACK(sk->saddr) || LOOPBACK(daddr))) saddr = sk->saddr; @@ -649,10 +620,13 @@ int ip_build_xmit(struct sock *sk, dev=rt->rt_dev; #ifdef CONFIG_IP_MULTICAST } + if (rt && !dev) + dev = rt->rt_dev; #endif if (user_saddr) saddr = user_saddr; + raddr = rt ? rt->rt_gateway : daddr; /* * Now compute the buffer space we require */ @@ -662,16 +636,10 @@ int ip_build_xmit(struct sock *sk, * choice RAW frames within 20 bytes of maximum size(rare) to the long path */ - length += 20; + length += sizeof(struct iphdr); if (!sk->ip_hdrincl && opt) - { length += opt->optlen; - if (opt->is_strictroute && rt && rt->rt_gateway) - { - ip_statistics.IpOutNoRoutes++; - return -ENETUNREACH; - } - } + if(length <= dev->mtu && !MULTICAST(daddr) && daddr!=0xFFFFFFFF && daddr!=dev->pa_brdaddr) { int error; @@ -687,12 +655,19 @@ int ip_build_xmit(struct sock *sk, skb->sk=sk; skb->arp=0; skb->saddr=saddr; - skb->raddr=(rt&&rt->rt_gateway)?rt->rt_gateway:daddr; + skb->raddr = raddr; skb_reserve(skb,(dev->hard_header_len+15)&~15); - if(sk->ip_hcache_state>0) + if (hh) { - memcpy(skb_push(skb,dev->hard_header_len),sk->ip_hcache_data,dev->hard_header_len); skb->arp=1; + memcpy(skb_push(skb,dev->hard_header_len),hh->hh_data,dev->hard_header_len); + if (!hh->hh_uptodate) + { + skb->arp = 0; +#if RT_CACHE_DEBUG >= 2 + printk("ip_build_xmit: hh miss %08x via %08x\n", rt->rt_dst, rt->rt_gateway); +#endif + } } else if(dev->hard_header) { @@ -747,7 +722,7 @@ int ip_build_xmit(struct sock *sk, } return 0; } - length-=20; + length -= sizeof(struct iphdr); if (sk && !sk->ip_hdrincl && opt) { length -= opt->optlen; @@ -847,7 +822,7 @@ int ip_build_xmit(struct sock *sk, skb->sk = sk; skb->arp = 0; skb->saddr = saddr; - skb->raddr = (rt&&rt->rt_gateway) ? rt->rt_gateway : daddr; + skb->raddr = raddr; skb_reserve(skb,(dev->hard_header_len+15)&~15); data = skb_put(skb, fraglen-dev->hard_header_len); @@ -858,10 +833,17 @@ int ip_build_xmit(struct sock *sk, * pointer to speed header cache builds for identical targets. */ - if(sk->ip_hcache_state>0) + if (hh) { - memcpy(skb_push(skb,dev->hard_header_len),sk->ip_hcache_data, dev->hard_header_len); skb->arp=1; + memcpy(skb_push(skb,dev->hard_header_len),hh->hh_data,dev->hard_header_len); + if (!hh->hh_uptodate) + { + skb->arp = 0; +#if RT_CACHE_DEBUG >= 2 + printk("ip_build_xmit: hh miss %08x via %08x\n", rt->rt_dst, rt->rt_gateway); +#endif + } } else if (dev->hard_header) { @@ -958,15 +940,15 @@ int ip_build_xmit(struct sock *sk, if(sk==NULL || sk->ip_mc_loop) { if(skb->daddr==IGMP_ALL_HOSTS || (dev->flags&IFF_ALLMULTI)) - ip_loopback(rt?rt->rt_dev:dev,skb); + ip_loopback(dev,skb); else { - struct ip_mc_list *imc=rt?rt->rt_dev->ip_mc_list:dev->ip_mc_list; + struct ip_mc_list *imc=dev->ip_mc_list; while(imc!=NULL) { if(imc->multiaddr==daddr) { - ip_loopback(rt?rt->rt_dev:dev,skb); + ip_loopback(dev,skb); break; } imc=imc->next; diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 9e70be68754d..818b7269d7c4 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -283,10 +283,12 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt /* * Not set so scan. */ - if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,NULL, &route_src))!=NULL) + if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,0))!=NULL) { dev=rt->rt_dev; - rt->rt_use--; + route_src = rt->rt_src; + ATOMIC_DECR(&rt->rt_use); + ip_rt_put(rt); } } else @@ -335,10 +337,12 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt if(mreq.imr_interface.s_addr==INADDR_ANY) { - if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,NULL, &route_src))!=NULL) + if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,0))!=NULL) { dev=rt->rt_dev; - rt->rt_use--; + ATOMIC_DECR(&rt->rt_use); + route_src = rt->rt_src; + ip_rt_put(rt); } } else diff --git a/net/ipv4/rarp.c b/net/ipv4/rarp.c index e4b2140e5842..650f02daabcc 100644 --- a/net/ipv4/rarp.c +++ b/net/ipv4/rarp.c @@ -266,7 +266,7 @@ static int rarp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type sti(); arp_send(ARPOP_RREPLY, ETH_P_RARP, sip, dev, dev->pa_addr, sha, - dev->dev_addr); + dev->dev_addr, sha); } else sti(); @@ -288,6 +288,7 @@ static int rarp_req_set(struct arpreq *req) int htype, hlen; unsigned long ip; struct rtable *rt; + struct device * dev; memcpy_fromfs(&r, req, sizeof(r)); @@ -326,9 +327,11 @@ static int rarp_req_set(struct arpreq *req) * Is it reachable directly ? */ - rt = ip_rt_route(ip, NULL, NULL); + rt = ip_rt_route(ip, 0); if (rt == NULL) return -ENETUNREACH; + dev = rt->rt_dev; + ip_rt_put(rt); /* * Is there an existing entry for this address? Find out... @@ -366,7 +369,7 @@ static int rarp_req_set(struct arpreq *req) entry->hlen = hlen; entry->htype = htype; memcpy(&entry->ha, &r.arp_ha.sa_data, hlen); - entry->dev = rt->rt_dev; + entry->dev = dev; sti(); @@ -574,5 +577,4 @@ void cleanup_module(void) rarp_release_entry(rt); } } - #endif diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 495ec9adbee7..b7f9a6a54a47 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -35,6 +35,8 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ + +#include #include #include #include @@ -57,8 +59,6 @@ #include #include -#include - #ifdef CONFIG_IP_MROUTE struct sock *mroute_socket=NULL; #endif @@ -240,8 +240,8 @@ static int raw_sendto(struct sock *sk, const unsigned char *from, * Temporary */ -static int raw_sendmsg(struct sock *sk, struct msghdr *msg, - int len, int noblock, int flags) +static int raw_sendmsg(struct sock *sk, struct msghdr *msg, int len, int noblock, + int flags) { if(msg->msg_iovlen==1) return raw_sendto(sk,msg->msg_iov[0].iov_base,len, noblock, flags, msg->msg_name, msg->msg_namelen); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 6483db017a2f..d14fead2ea25 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -35,6 +35,8 @@ * Alan Cox : Aligned routing errors more closely with BSD * our system is still very different. * Alan Cox : Faster /proc handling + * Alexey Kuznetsov : Massive rework to support tree based routing, + * routing caches and better behaviour. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -42,8 +44,10 @@ * 2 of the License, or (at your option) any later version. */ +#include #include #include +#include #include #include #include @@ -65,102 +69,246 @@ #include /* - * The routing table list + * Forwarding Information Base definitions. */ -static struct rtable *rt_base = NULL; -unsigned long rt_stamp = 1; /* Routing table version stamp for caches ( 0 is 'unset' ) */ +struct fib_node +{ + struct fib_node *fib_next; + __u32 fib_dst; + unsigned long fib_use; + struct fib_info *fib_info; + short fib_metric; + unsigned char fib_tos; +}; + +/* + * This structure contains data shared by many of routes. + */ + +struct fib_info +{ + struct fib_info *fib_next; + struct fib_info *fib_prev; + __u32 fib_gateway; + struct device *fib_dev; + int fib_refcnt; + unsigned long fib_window; + unsigned short fib_flags; + unsigned short fib_mtu; + unsigned short fib_irtt; +}; + +struct fib_zone +{ + struct fib_zone *fz_next; + struct fib_node **fz_hash_table; + struct fib_node *fz_list; + int fz_nent; + int fz_logmask; + __u32 fz_mask; +}; + +static struct fib_zone *fib_zones[33]; +static struct fib_zone *fib_zone_list; +static struct fib_node *fib_loopback = NULL; +static struct fib_info *fib_info_list; /* - * Pointer to the loopback route + * Backlogging. */ - -static struct rtable *rt_loopback = NULL; + +#define RT_BH_REDIRECT 0 +#define RT_BH_GARBAGE_COLLECT 1 +#define RT_BH_FREE 2 + +struct rt_req +{ + struct rt_req * rtr_next; + struct device *dev; + __u32 dst; + __u32 gw; + unsigned char tos; +}; + +int ip_rt_lock; +unsigned ip_rt_bh_mask; +static struct rt_req *rt_backlog; /* - * Remove a routing table entry. + * Route cache. */ -static int rt_del(__u32 dst, __u32 mask, - char *devname, __u32 gtw, short rt_flags, short metric) +struct rtable *ip_rt_hash_table[RT_HASH_DIVISOR]; +static int rt_cache_size; +static struct rtable *rt_free_queue; +struct wait_queue *rt_wait; + +static void rt_kick_backlog(void); +static void rt_cache_add(unsigned hash, struct rtable * rth); +static void rt_cache_flush(void); +static void rt_garbage_collect_1(void); + +/* + * Evaluate mask length. + */ + +static __inline__ int rt_logmask(__u32 mask) { - struct rtable *r, **rp; - unsigned long flags; - int found=0; + if (!(mask = ntohl(mask))) + return 32; + return ffz(~mask); +} - rp = &rt_base; - - /* - * This must be done with interrupts off because we could take - * an ICMP_REDIRECT. - */ - - save_flags(flags); - cli(); - while((r = *rp) != NULL) +/* + * Create mask from length. + */ + +static __inline__ __u32 rt_mask(int logmask) +{ + if (logmask >= 32) + return 0; + return htonl(~((1<>logmask); +} + +/* + * Free FIB node. + */ + +static void fib_free_node(struct fib_node * f) +{ + struct fib_info * fi = f->fib_info; + if (!--fi->fib_refcnt) { - /* - * Make sure the destination and netmask match. - * metric, gateway and device are also checked - * if they were specified. - */ - if (r->rt_dst != dst || - (mask && r->rt_mask != mask) || - (gtw && r->rt_gateway != gtw) || - (metric >= 0 && r->rt_metric != metric) || - (devname && strcmp((r->rt_dev)->name,devname) != 0) ) +#if RT_CACHE_DEBUG >= 2 + printk("fib_free_node: fi %08x/%s is free\n", fi->fib_gateway, fi->fib_dev->name); +#endif + if (fi->fib_next) + fi->fib_next->fib_prev = fi->fib_prev; + if (fi->fib_prev) + fi->fib_prev->fib_next = fi->fib_next; + if (fi == fib_info_list) + fib_info_list = fi->fib_next; + } + kfree_s(f, sizeof(struct fib_node)); +} + +/* + * Find gateway route by address. + */ + +static struct fib_node * fib_lookup_gateway(__u32 dst) +{ + struct fib_zone * fz; + struct fib_node * f; + + for (fz = fib_zone_list; fz; fz = fz->fz_next) + { + if (fz->fz_hash_table) + f = fz->fz_hash_table[fz_hash_code(dst, fz->fz_logmask)]; + else + f = fz->fz_list; + + for ( ; f; f = f->fib_next) { - rp = &r->rt_next; - continue; + if ((dst ^ f->fib_dst) & fz->fz_mask) + continue; + if (f->fib_info->fib_flags & RTF_GATEWAY) + return NULL; + return f; } - *rp = r->rt_next; - - /* - * If we delete the loopback route update its pointer. - */ - - if (rt_loopback == r) - rt_loopback = NULL; - ip_netlink_msg(RTMSG_DELROUTE, dst, gtw, mask, rt_flags, metric, r->rt_dev->name); - kfree_s(r, sizeof(struct rtable)); - found=1; - } - rt_stamp++; /* New table revision */ - - restore_flags(flags); - - if(found) - return 0; - return -ESRCH; + } + return NULL; } +/* + * Find local route by address. + * FIXME: I use "longest match" principle. If destination + * has some non-local route, I'll not search shorter matches. + * It's possible, I'm wrong, but I wanted to prevent following + * situation: + * route add 193.233.7.128 netmask 255.255.255.192 gw xxxxxx + * route add 193.233.7.0 netmask 255.255.255.0 eth1 + * (Two ethernets connected by serial line, one is small and other is large) + * Host 193.233.7.129 is locally unreachable, + * but old (<=1.3.37) code will send packets destined for it to eth1. + * + */ + +static struct fib_node * fib_lookup_local(__u32 dst) +{ + struct fib_zone * fz; + struct fib_node * f; + + for (fz = fib_zone_list; fz; fz = fz->fz_next) + { + int longest_match_found = 0; + + if (fz->fz_hash_table) + f = fz->fz_hash_table[fz_hash_code(dst, fz->fz_logmask)]; + else + f = fz->fz_list; + + for ( ; f; f = f->fib_next) + { + if ((dst ^ f->fib_dst) & fz->fz_mask) + continue; + if (!(f->fib_info->fib_flags & RTF_GATEWAY)) + return f; + longest_match_found = 1; + } + if (longest_match_found) + return NULL; + } + return NULL; +} /* - * Remove all routing table entries for a device. This is called when - * a device is downed. + * Main lookup routine. + * IMPORTANT NOTE: this algorithm has small difference from <=1.3.37 visible + * by user. It doesn't route non-CIDR broadcasts by default. + * + * F.e. + * ifconfig eth0 193.233.7.65 netmask 255.255.255.192 broadcast 193.233.7.255 + * is valid, but if you really are not able (not allowed, do not want) to + * use CIDR compliant broadcast 193.233.7.127, you should add host route: + * route add -host 193.233.7.255 eth0 */ - -void ip_rt_flush(struct device *dev) + +static struct fib_node * fib_lookup(__u32 dst) { - struct rtable *r; - struct rtable **rp; - unsigned long flags; + struct fib_zone * fz; + struct fib_node * f; - rp = &rt_base; - save_flags(flags); - cli(); - while ((r = *rp) != NULL) { - if (r->rt_dev != dev) { - rp = &r->rt_next; - continue; + for (fz = fib_zone_list; fz; fz = fz->fz_next) + { + if (fz->fz_hash_table) + f = fz->fz_hash_table[fz_hash_code(dst, fz->fz_logmask)]; + else + f = fz->fz_list; + + for ( ; f; f = f->fib_next) + { + if ((dst ^ f->fib_dst) & fz->fz_mask) + continue; + return f; } - *rp = r->rt_next; - if (rt_loopback == r) - rt_loopback = NULL; - kfree_s(r, sizeof(struct rtable)); - } - rt_stamp++; /* New table revision */ - restore_flags(flags); + } + return NULL; +} + +static __inline__ struct device * get_gw_dev(__u32 gw) +{ + struct fib_node * f; + f = fib_lookup_gateway(gw); + if (f) + return f->fib_info->fib_dev; + return NULL; } /* @@ -199,228 +347,1244 @@ static __u32 guess_mask(__u32 dst, struct device * dev) } -/* - * Find the route entry through which our gateway will be reached - */ - -static inline struct device * get_gw_dev(__u32 gw) +/* + * Check if a mask is acceptable. + */ + +static inline int bad_mask(__u32 mask, __u32 addr) +{ + if (addr & (mask = ~mask)) + return 1; + mask = ntohl(mask); + if (mask & (mask+1)) + return 1; + return 0; +} + + +static int fib_del_list(struct fib_node **fp, __u32 dst, + struct device * dev, __u32 gtw, short flags, short metric, __u32 mask) +{ + struct fib_node *f; + int found=0; + + while((f = *fp) != NULL) + { + struct fib_info * fi = f->fib_info; + + /* + * Make sure the destination and netmask match. + * metric, gateway and device are also checked + * if they were specified. + */ + if (f->fib_dst != dst || + (gtw && fi->fib_gateway != gtw) || + (metric >= 0 && f->fib_metric != metric) || + (dev && fi->fib_dev != dev) ) + { + fp = &f->fib_next; + continue; + } + cli(); + *fp = f->fib_next; + if (fib_loopback == f) + fib_loopback = NULL; + sti(); + ip_netlink_msg(RTMSG_DELROUTE, dst, gtw, mask, flags, metric, fi->fib_dev->name); + fib_free_node(f); + found++; + } + return found; +} + +static __inline__ int fib_del_1(__u32 dst, __u32 mask, + struct device * dev, __u32 gtw, short flags, short metric) +{ + struct fib_node **fp; + struct fib_zone *fz; + int found=0; + + if (!mask) + { + for (fz=fib_zone_list; fz; fz = fz->fz_next) + { + int tmp; + if (fz->fz_hash_table) + fp = &fz->fz_hash_table[fz_hash_code(dst, fz->fz_logmask)]; + else + fp = &fz->fz_list; + + tmp = fib_del_list(fp, dst, dev, gtw, flags, metric, mask); + fz->fz_nent -= tmp; + found += tmp; + } + } + else + { + if ((fz = fib_zones[rt_logmask(mask)]) != NULL) + { + if (fz->fz_hash_table) + fp = &fz->fz_hash_table[fz_hash_code(dst, fz->fz_logmask)]; + else + fp = &fz->fz_list; + + found = fib_del_list(fp, dst, dev, gtw, flags, metric, mask); + fz->fz_nent -= found; + } + } + + if (found) + { + rt_cache_flush(); + return 0; + } + return -ESRCH; +} + + +static struct fib_info * fib_create_info(__u32 gw, struct device * dev, + unsigned short flags, unsigned short mss, + unsigned long window, unsigned short irtt) +{ + struct fib_info * fi; + + if (!(flags & RTF_MSS)) + { + mss = dev->mtu; +#ifdef CONFIG_NO_PATH_MTU_DISCOVERY + /* + * If MTU was not specified, use default. + * If you want to increase MTU for some net (local subnet) + * use "route add .... mss xxx". + * + * The MTU isnt currently always used and computed as it + * should be as far as I can tell. [Still verifying this is right] + */ + if ((flags & RTF_GATEWAY) && mss > 576) + mss = 576; +#endif + } + if (!(flags & RTF_WINDOW)) + window = 0; + if (!(flags & RTF_IRTT)) + irtt = 0; + + for (fi=fib_info_list; fi; fi = fi->fib_next) + { + if (fi->fib_gateway != gw || + fi->fib_dev != dev || + fi->fib_flags != flags || + fi->fib_mtu != mss || + fi->fib_window != window || + fi->fib_irtt != irtt) + continue; + fi->fib_refcnt++; +#if RT_CACHE_DEBUG >= 2 + printk("fib_create_info: fi %08x/%s is duplicate\n", fi->fib_gateway, fi->fib_dev->name); +#endif + return fi; + } + fi = (struct fib_info*)kmalloc(sizeof(struct fib_info), GFP_KERNEL); + if (!fi) + return NULL; + memset(fi, 0, sizeof(struct fib_info)); + fi->fib_flags = flags; + fi->fib_dev = dev; + fi->fib_gateway = gw; + fi->fib_mtu = mss; + fi->fib_window = window; + fi->fib_refcnt++; + fi->fib_next = fib_info_list; + fi->fib_prev = NULL; + if (fib_info_list) + fib_info_list->fib_prev = fi; + fib_info_list = fi; +#if RT_CACHE_DEBUG >= 2 + printk("fib_create_info: fi %08x/%s is created\n", fi->fib_gateway, fi->fib_dev->name); +#endif + return fi; +} + + +static __inline__ void fib_add_1(short flags, __u32 dst, __u32 mask, + __u32 gw, struct device *dev, unsigned short mss, + unsigned long window, unsigned short irtt, short metric) +{ + struct fib_node *f, *f1; + struct fib_node **fp; + struct fib_node **dup_fp = NULL; + struct fib_zone * fz; + struct fib_info * fi; + int logmask; + + if (flags & RTF_HOST) + mask = 0xffffffff; + /* + * If mask is not specified, try to guess it. + */ + else if (!mask) + { + if (!((dst ^ dev->pa_addr) & dev->pa_mask)) + { + mask = dev->pa_mask; + flags &= ~RTF_GATEWAY; + if (flags & RTF_DYNAMIC) + { + printk("Dynamic route to my own net rejected\n"); + return; + } + } + else + mask = guess_mask(dst, dev); + dst &= mask; + } + + /* + * A gateway must be reachable and not a local address + */ + + if (gw == dev->pa_addr) + flags &= ~RTF_GATEWAY; + + if (flags & RTF_GATEWAY) + { + /* + * Don't try to add a gateway we can't reach.. + */ + + if (dev != get_gw_dev(gw)) + return; + + flags |= RTF_GATEWAY; + } + else + gw = 0; + + /* + * Allocate an entry and fill it in. + */ + + f = (struct fib_node *) kmalloc(sizeof(struct fib_node), GFP_KERNEL); + if (f == NULL) + return; + + memset(f, 0, sizeof(struct fib_node)); + f->fib_dst = dst; + f->fib_metric = metric; + f->fib_tos = 0; + + if ((fi = fib_create_info(gw, dev, flags, mss, window, irtt)) == NULL) + { + kfree_s(f, sizeof(struct fib_node)); + return; + } + f->fib_info = fi; + + logmask = rt_logmask(mask); + fz = fib_zones[logmask]; + + + if (!fz) + { + int i; + fz = kmalloc(sizeof(struct fib_zone), GFP_KERNEL); + if (!fz) + { + fib_free_node(f); + return; + } + memset(fz, 0, sizeof(struct fib_zone)); + fz->fz_logmask = logmask; + fz->fz_mask = mask; + for (i=logmask-1; i>=0; i--) + if (fib_zones[i]) + break; + cli(); + if (i<0) + { + fz->fz_next = fib_zone_list; + fib_zone_list = fz; + } + else + { + fz->fz_next = fib_zones[i]->fz_next; + fib_zones[i]->fz_next = fz; + } + fib_zones[logmask] = fz; + sti(); + } + + /* + * If zone overgrows RTZ_HASHING_LIMIT, create hash table. + */ + + if (fz->fz_nent >= RTZ_HASHING_LIMIT && !fz->fz_hash_table && logmask<32) + { + struct fib_node ** ht; +#if RT_CACHE_DEBUG + printk("fib_add_1: hashing for zone %d started\n", logmask); +#endif + ht = kmalloc(RTZ_HASH_DIVISOR*sizeof(struct rtable*), GFP_KERNEL); + + if (ht) + { + memset(ht, 0, RTZ_HASH_DIVISOR*sizeof(struct fib_node*)); + cli(); + f1 = fz->fz_list; + while (f1) + { + struct fib_node * next; + unsigned hash = fz_hash_code(f1->fib_dst, logmask); + next = f1->fib_next; + f1->fib_next = ht[hash]; + ht[hash] = f1; + f1 = next; + } + fz->fz_list = NULL; + fz->fz_hash_table = ht; + sti(); + } + } + + if (fz->fz_hash_table) + fp = &fz->fz_hash_table[fz_hash_code(dst, logmask)]; + else + fp = &fz->fz_list; + + /* + * Scan list to find the first route with the same destination + */ + while ((f1 = *fp) != NULL) + { + if (f1->fib_dst == dst) + break; + fp = &f1->fib_next; + } + + /* + * Find route with the same destination and less (or equal) metric. + */ + while ((f1 = *fp) != NULL && f1->fib_dst == dst) + { + if (f1->fib_metric >= metric) + break; + /* + * Record route with the same destination and gateway, + * but less metric. We'll delete it + * after instantiation of new route. + */ + if (f1->fib_info->fib_gateway == gw) + dup_fp = fp; + fp = &f1->fib_next; + } + + /* + * Is it already present? + */ + + if (f1 && f1->fib_metric == metric && f1->fib_info == fi) + { + fib_free_node(f); + return; + } + + /* + * Insert new entry to the list. + */ + + cli(); + f->fib_next = f1; + *fp = f; + if (!fib_loopback && (fi->fib_dev->flags & IFF_LOOPBACK)) + fib_loopback = f; + sti(); + fz->fz_nent++; + ip_netlink_msg(RTMSG_NEWROUTE, dst, gw, mask, flags, metric, fi->fib_dev->name); + + /* + * Delete route with the same destination and gateway. + * Note that we should have at most one such route. + */ + if (dup_fp) + fp = dup_fp; + else + fp = &f->fib_next; + + while ((f1 = *fp) != NULL && f1->fib_dst == dst) + { + if (f1->fib_info->fib_gateway == gw) + { + cli(); + *fp = f1->fib_next; + if (fib_loopback == f1) + fib_loopback = NULL; + sti(); + ip_netlink_msg(RTMSG_DELROUTE, dst, gw, mask, flags, metric, f1->fib_info->fib_dev->name); + fib_free_node(f1); + fz->fz_nent--; + break; + } + fp = &f1->fib_next; + } + rt_cache_flush(); + return; +} + +static int rt_flush_list(struct fib_node ** fp, struct device *dev) +{ + int found = 0; + struct fib_node *f; + + while ((f = *fp) != NULL) { + if (f->fib_info->fib_dev != dev) { + fp = &f->fib_next; + continue; + } + cli(); + *fp = f->fib_next; + if (fib_loopback == f) + fib_loopback = NULL; + sti(); + fib_free_node(f); + found++; + } + return found; +} + +static __inline__ void fib_flush_1(struct device *dev) +{ + struct fib_zone *fz; + int found = 0; + + for (fz = fib_zone_list; fz; fz = fz->fz_next) + { + if (fz->fz_hash_table) + { + int i; + int tmp = 0; + for (i=0; ifz_hash_table[i], dev); + fz->fz_nent -= tmp; + found += tmp; + } + else + { + int tmp; + tmp = rt_flush_list(&fz->fz_list, dev); + fz->fz_nent -= tmp; + found += tmp; + } + } + + if (found) + rt_cache_flush(); +} + + +/* + * Called from the PROCfs module. This outputs /proc/net/route. + * + * We preserve the old format but pad the buffers out. This means that + * we can spin over the other entries as we read them. Remember the + * gated BGP4 code could need to read 60,000+ routes on occasion (thats + * about 7Mb of data). To do that ok we will need to also cache the + * last route we got to (reads will generally be following on from + * one another without gaps). + */ + +int rt_get_info(char *buffer, char **start, off_t offset, int length, int dummy) +{ + struct fib_zone *fz; + struct fib_node *f; + int len=0; + off_t pos=0; + char temp[129]; + int i; + + pos = 128; + + if (offset<128) + { + sprintf(buffer,"%-127s\n","Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU\tWindow\tIRTT"); + len = 128; + } + + while (ip_rt_lock) + sleep_on(&rt_wait); + ip_rt_fast_lock(); + + for (fz=fib_zone_list; fz; fz = fz->fz_next) + { + int maxslot; + struct fib_node ** fp; + + if (fz->fz_nent == 0) + continue; + + if (pos + 128*fz->fz_nent <= offset) + { + pos += 128*fz->fz_nent; + len = 0; + continue; + } + + if (fz->fz_hash_table) + { + maxslot = RTZ_HASH_DIVISOR; + fp = fz->fz_hash_table; + } + else + { + maxslot = 1; + fp = &fz->fz_list; + } + + for (i=0; i < maxslot; i++, fp++) + { + + for (f = *fp; f; f = f->fib_next) + { + struct fib_info * fi; + /* + * Spin through entries until we are ready + */ + pos += 128; + + if (pos <= offset) + { + len=0; + continue; + } + + fi = f->fib_info; + sprintf(temp, "%s\t%08lX\t%08lX\t%02X\t%d\t%lu\t%d\t%08lX\t%d\t%lu\t%u", + fi->fib_dev->name, (unsigned long)f->fib_dst, (unsigned long)fi->fib_gateway, + fi->fib_flags, 0, f->fib_use, f->fib_metric, + (unsigned long)fz->fz_mask, (int)fi->fib_mtu, fi->fib_window, (int)fi->fib_irtt); + sprintf(buffer+len,"%-127s\n",temp); + + len += 128; + if (pos >= offset+length) + goto done; + } + } + } + +done: + ip_rt_unlock(); + wake_up(&rt_wait); + + *start = buffer+len-(pos-offset); + len = pos - offset; + if (len>length) + len = length; + return len; +} + +int rt_cache_get_info(char *buffer, char **start, off_t offset, int length, int dummy) +{ + int len=0; + off_t pos=0; + char temp[129]; + struct rtable *r; + int i; + + pos = 128; + + if (offset<128) + { + sprintf(buffer,"%-127s\n","Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tSource\t\tMTU\tWindow\tIRTT\tHH\tARP\n"); + len = 128; + } + + + while (ip_rt_lock) + sleep_on(&rt_wait); + ip_rt_fast_lock(); + + for (i = 0; irt_next) + { + /* + * Spin through entries until we are ready + */ + pos += 128; + + if (pos <= offset) + { + len = 0; + continue; + } + + sprintf(temp, "%s\t%08lX\t%08lX\t%02X\t%ld\t%lu\t%d\t%08lX\t%d\t%lu\t%u\t%ld\t%1d", + r->rt_dev->name, (unsigned long)r->rt_dst, (unsigned long)r->rt_gateway, + r->rt_flags, r->rt_refcnt, r->rt_use, 0, + (unsigned long)r->rt_src, (int)r->rt_mtu, r->rt_window, (int)r->rt_irtt, r->rt_hh ? r->rt_hh->hh_refcnt : -1, r->rt_hh ? r->rt_hh->hh_uptodate : 0); + sprintf(buffer+len,"%-127s\n",temp); + len += 128; + if (pos >= offset+length) + goto done; + } + } + +done: + ip_rt_unlock(); + wake_up(&rt_wait); + + *start = buffer+len-(pos-offset); + len = pos-offset; + if (len>length) + len = length; + return len; +} + + +static void rt_free(struct rtable * rt) +{ + unsigned long flags; + + save_flags(flags); + cli(); + if (!rt->rt_refcnt) + { + struct hh_cache * hh = rt->rt_hh; + rt->rt_hh = NULL; + if (hh && !--hh->hh_refcnt) + { + restore_flags(flags); + kfree_s(hh, sizeof(struct hh_cache)); + } + restore_flags(flags); + kfree_s(rt, sizeof(struct rt_table)); + return; + } + rt->rt_next = rt_free_queue; + rt->rt_flags &= ~RTF_UP; + rt_free_queue = rt; + ip_rt_bh_mask |= RT_BH_FREE; +#if RT_CACHE_DEBUG >= 2 + printk("rt_free: %08x\n", rt->rt_dst); +#endif + restore_flags(flags); +} + +/* + * RT "bottom half" handlers. Called with masked inetrrupts. + */ + +static __inline__ void rt_kick_free_queue(void) +{ + struct rtable *rt, **rtp; + + rtp = &rt_free_queue; + + while ((rt = *rtp) != NULL) + { + if (!rt->rt_refcnt) + { + struct hh_cache * hh = rt->rt_hh; +#if RT_CACHE_DEBUG >= 2 + __u32 daddr = rt->rt_dst; +#endif + *rtp = rt->rt_next; + rt->rt_hh = NULL; + if (hh && !--hh->hh_refcnt) + { + sti(); + kfree_s(hh, sizeof(struct hh_cache)); + } + sti(); + kfree_s(rt, sizeof(struct rt_table)); +#if RT_CACHE_DEBUG >= 2 + printk("rt_kick_free_queue: %08x is free\n", daddr); +#endif + cli(); + continue; + } + rtp = &rt->rt_next; + } +} + +void ip_rt_run_bh() { + unsigned long flags; + save_flags(flags); + cli(); + if (ip_rt_bh_mask && !ip_rt_lock) + { + if (ip_rt_bh_mask & RT_BH_REDIRECT) + rt_kick_backlog(); + + if (ip_rt_bh_mask & RT_BH_GARBAGE_COLLECT) + { + ip_rt_fast_lock(); + ip_rt_bh_mask &= ~RT_BH_GARBAGE_COLLECT; + sti(); + rt_garbage_collect_1(); + cli(); + ip_rt_fast_unlock(); + } + + if (ip_rt_bh_mask & RT_BH_FREE) + rt_kick_free_queue(); + } + restore_flags(flags); +} + + +void ip_rt_check_expire() +{ + ip_rt_fast_lock(); + if (ip_rt_lock == 1) + { + int i; + struct rtable *rth, **rthp; + unsigned long flags; + unsigned long now = jiffies; + + save_flags(flags); + for (i=0; irt_next; + + /* + * Cleanup aged off entries. + */ + + cli(); + if (!rth->rt_refcnt && rth->rt_lastuse + RT_CACHE_TIMEOUT < now) + { + *rthp = rth_next; + sti(); + rt_cache_size--; +#if RT_CACHE_DEBUG >= 2 + printk("rt_check_expire clean %02x@%08x\n", i, rth->rt_dst); +#endif + rt_free(rth); + continue; + } + sti(); + + if (!rth_next) + break; + + /* + * LRU ordering. + */ + + if (rth->rt_lastuse + RT_CACHE_BUBBLE_THRESHOULD < rth_next->rt_lastuse || + (rth->rt_lastuse < rth_next->rt_lastuse && + rth->rt_use < rth_next->rt_use)) + { +#if RT_CACHE_DEBUG >= 2 + printk("rt_check_expire bubbled %02x@%08x<->%08x\n", i, rth->rt_dst, rth_next->rt_dst); +#endif + cli(); + *rthp = rth_next; + rth->rt_next = rth_next->rt_next; + rth_next->rt_next = rth; + sti(); + rthp = &rth_next->rt_next; + continue; + } + rthp = &rth->rt_next; + } + } + restore_flags(flags); + rt_kick_free_queue(); + } + ip_rt_unlock(); +} + +static void rt_redirect_1(__u32 dst, __u32 gw, struct device *dev) +{ + struct rtable *rt; + unsigned long hash = ip_rt_hash_code(dst); + + if (gw == dev->pa_addr) + return; + if (dev != get_gw_dev(gw)) + return; + rt = (struct rtable *) kmalloc(sizeof(struct rtable), GFP_ATOMIC); + if (rt == NULL) + return; + memset(rt, 0, sizeof(struct rtable)); + rt->rt_flags = RTF_DYNAMIC | RTF_MODIFIED | RTF_HOST | RTF_GATEWAY | RTF_UP; + rt->rt_dst = dst; + rt->rt_dev = dev; + rt->rt_gateway = gw; + rt->rt_src = dev->pa_addr; + rt->rt_mtu = dev->mtu; +#ifdef CONFIG_NO_PATH_MTU_DISCOVERY + if (dev->mtu > 576) + rt->rt_mtu = 576; +#endif + rt->rt_lastuse = jiffies; + rt->rt_refcnt = 1; + rt_cache_add(hash, rt); + ip_rt_put(rt); + return; +} + +static void rt_cache_flush(void) +{ + int i; + struct rtable * rth, * next; + + for (i=0; irt_next; + rt_cache_size--; + nr++; + rth->rt_next = NULL; + rt_free(rth); + } +#if RT_CACHE_DEBUG >= 2 + if (nr > 0) + printk("rt_cache_flush: %d@%02x\n", nr, i); +#endif + } +#if RT_CACHE_DEBUG >= 1 + if (rt_cache_size) + { + printk("rt_cache_flush: bug rt_cache_size=%d\n", rt_cache_size); + rt_cache_size = 0; + } +#endif +} + +static void rt_garbage_collect_1(void) +{ + int i; + unsigned expire = RT_CACHE_TIMEOUT>>1; + struct rtable * rth, **rthp; + unsigned long now = jiffies; + + for (;;) + { + for (i=0; irt_next) + { + if (rth->rt_lastuse + expire*(rth->rt_refcnt+1) > now) + continue; + rt_cache_size--; + cli(); + *rthp=rth->rt_next; + rth->rt_next = NULL; + sti(); + rt_free(rth); + break; + } + } + if (rt_cache_size < RT_CACHE_SIZE_MAX) + return; + expire >>= 1; + } +} + +static __inline__ void rt_req_enqueue(struct rt_req **q, struct rt_req *rtr) +{ + unsigned long flags; + struct rt_req * tail; + + save_flags(flags); + cli(); + tail = *q; + if (!tail) + rtr->rtr_next = rtr; + else + { + rtr->rtr_next = tail->rtr_next; + tail->rtr_next = rtr; + } + *q = rtr; + restore_flags(flags); + return; +} + +/* + * Caller should mask interrupts. + */ + +static __inline__ struct rt_req * rt_req_dequeue(struct rt_req **q) +{ + struct rt_req * rtr; + + if (*q) + { + rtr = (*q)->rtr_next; + (*q)->rtr_next = rtr->rtr_next; + if (rtr->rtr_next == rtr) + *q = NULL; + rtr->rtr_next = NULL; + return rtr; + } + return NULL; +} + +/* + Called with masked interrupts + */ + +static void rt_kick_backlog() +{ + if (!ip_rt_lock) + { + struct rt_req * rtr; + + ip_rt_fast_lock(); + + while ((rtr = rt_req_dequeue(&rt_backlog)) != NULL) + { + sti(); + rt_redirect_1(rtr->dst, rtr->gw, rtr->dev); + kfree_s(rtr, sizeof(struct rt_req)); + cli(); + } + + ip_rt_bh_mask &= ~RT_BH_REDIRECT; + + ip_rt_fast_unlock(); + } +} + +/* + * rt_{del|add|flush} called only from USER process. Waiting is OK. + */ + +static int rt_del(__u32 dst, __u32 mask, + struct device * dev, __u32 gtw, short rt_flags, short metric) +{ + int retval; + + while (ip_rt_lock) + sleep_on(&rt_wait); + ip_rt_fast_lock(); + retval = fib_del_1(dst, mask, dev, gtw, rt_flags, metric); + ip_rt_unlock(); + wake_up(&rt_wait); + return retval; +} + +static void rt_add(short flags, __u32 dst, __u32 mask, + __u32 gw, struct device *dev, unsigned short mss, + unsigned long window, unsigned short irtt, short metric) +{ + while (ip_rt_lock) + sleep_on(&rt_wait); + ip_rt_fast_lock(); + fib_add_1(flags, dst, mask, gw, dev, mss, window, irtt, metric); + ip_rt_unlock(); + wake_up(&rt_wait); +} + +void ip_rt_flush(struct device *dev) +{ + while (ip_rt_lock) + sleep_on(&rt_wait); + ip_rt_fast_lock(); + fib_flush_1(dev); + ip_rt_unlock(); + wake_up(&rt_wait); +} + +/* + Called by ICMP module. + */ + +void ip_rt_redirect(__u32 src, __u32 dst, __u32 gw, struct device *dev) +{ + struct rt_req * rtr; + struct rtable * rt; + + rt = ip_rt_route(dst, 0); + if (!rt) + return; + + if (rt->rt_gateway != src || + rt->rt_dev != dev || + ((gw^dev->pa_addr)&dev->pa_mask) || + ip_chk_addr(gw)) + { + ip_rt_put(rt); + return; + } + ip_rt_put(rt); + + ip_rt_fast_lock(); + if (ip_rt_lock == 1) + { + rt_redirect_1(dst, gw, dev); + ip_rt_unlock(); + return; + } + + rtr = kmalloc(sizeof(struct rt_req), GFP_ATOMIC); + if (rtr) + { + rtr->dst = dst; + rtr->gw = gw; + rtr->dev = dev; + rt_req_enqueue(&rt_backlog, rtr); + ip_rt_bh_mask |= RT_BH_REDIRECT; + } + ip_rt_unlock(); +} + + +static __inline__ void rt_garbage_collect(void) { - struct rtable * rt; - - for (rt = rt_base ; ; rt = rt->rt_next) + if (ip_rt_lock == 1) { - if (!rt) - return NULL; - if ((gw ^ rt->rt_dst) & rt->rt_mask) - continue; - /* - * Gateways behind gateways are a no-no - */ - - if (rt->rt_flags & RTF_GATEWAY) - return NULL; - return rt->rt_dev; + rt_garbage_collect_1(); + return; } + ip_rt_bh_mask |= RT_BH_GARBAGE_COLLECT; } -/* - * Rewrote rt_add(), as the old one was weird - Linus - * - * This routine is used to update the IP routing table, either - * from the kernel (ICMP_REDIRECT) or via an ioctl call issued - * by the superuser. - */ - -void ip_rt_add(short flags, __u32 dst, __u32 mask, - __u32 gw, struct device *dev, unsigned short mtu, - unsigned long window, unsigned short irtt, short metric) +static void rt_cache_add(unsigned hash, struct rtable * rth) { - struct rtable *r, *rt; - struct rtable **rp; - unsigned long cpuflags; - int duplicate = 0; + unsigned long flags; + struct rtable **rthp; + __u32 daddr = rth->rt_dst; + unsigned long now = jiffies; - /* - * A host is a unique machine and has no network bits. - */ - - if (flags & RTF_HOST) +#if RT_CACHE_DEBUG >= 2 + if (ip_rt_lock != 1) { - mask = 0xffffffff; - } - - /* - * Calculate the network mask - */ - - else if (!mask) + printk("rt_cache_add: ip_rt_lock==%d\n", ip_rt_lock); + return; + } +#endif + + save_flags(flags); + + if (rth->rt_dev->header_cache_bind) { - if (!((dst ^ dev->pa_addr) & dev->pa_mask)) + struct rtable * rtg = rth; + + if (rth->rt_gateway != daddr) { - mask = dev->pa_mask; - flags &= ~RTF_GATEWAY; - if (flags & RTF_DYNAMIC) + ip_rt_fast_unlock(); + rtg = ip_rt_route(rth->rt_gateway, 0); + ip_rt_fast_lock(); + } + + if (rtg) + { + if (rtg == rth) + rtg->rt_dev->header_cache_bind(&rtg->rt_hh, rtg->rt_dev, ETH_P_IP, rtg->rt_dst); + else { - /*printk("Dynamic route to my own net rejected\n");*/ - return; + if (rtg->rt_hh) + ATOMIC_INCR(&rtg->rt_hh->hh_refcnt); + rth->rt_hh = rtg->rt_hh; + ip_rt_put(rtg); } - } - else - mask = guess_mask(dst, dev); - dst &= mask; + } } - - /* - * A gateway must be reachable and not a local address - */ - - if (gw == dev->pa_addr) - flags &= ~RTF_GATEWAY; - - if (flags & RTF_GATEWAY) + + if (rt_cache_size >= RT_CACHE_SIZE_MAX) + rt_garbage_collect(); + + cli(); + rth->rt_next = ip_rt_hash_table[hash]; +#if RT_CACHE_DEBUG >= 2 + if (rth->rt_next) { - /* - * Don't try to add a gateway we can't reach.. - */ - - if (dev != get_gw_dev(gw)) - return; - - flags |= RTF_GATEWAY; - } - else - gw = 0; - + struct rtable * trth; + printk("rt_cache @%02x: %08x", hash, daddr); + for (trth=rth->rt_next; trth; trth=trth->rt_next) + printk(" . %08x", trth->rt_dst); + printk("\n"); + } +#endif + ip_rt_hash_table[hash] = rth; + rthp = &rth->rt_next; + sti(); + rt_cache_size++; + /* - * Allocate an entry and fill it in. + * Cleanup duplicate (and aged off) entries. */ - - rt = (struct rtable *) kmalloc(sizeof(struct rtable), GFP_ATOMIC); - if (rt == NULL) + + while ((rth = *rthp) != NULL) { - return; + + cli(); + if ((!rth->rt_refcnt && rth->rt_lastuse + RT_CACHE_TIMEOUT < now) + || rth->rt_dst == daddr) + { + *rthp = rth->rt_next; + rt_cache_size--; + sti(); +#if RT_CACHE_DEBUG >= 2 + printk("rt_cache clean %02x@%08x\n", hash, rth->rt_dst); +#endif + rt_free(rth); + continue; + } + sti(); + rthp = &rth->rt_next; } - memset(rt, 0, sizeof(struct rtable)); - rt->rt_flags = flags | RTF_UP; - rt->rt_dst = dst; - rt->rt_dev = dev; - rt->rt_gateway = gw; - rt->rt_mask = mask; - rt->rt_mss = dev->mtu - HEADER_SIZE; - rt->rt_metric = metric; - rt->rt_window = 0; /* Default is no clamping */ + restore_flags(flags); +} - /* Are the MSS/Window valid ? */ +/* + RT should be already locked. + + We could improve this by keeping a chain of say 32 struct rtable's + last freed for fast recycling. + + */ - if(rt->rt_flags & RTF_MSS) - rt->rt_mss = mtu; - - if(rt->rt_flags & RTF_WINDOW) - rt->rt_window = window; - if(rt->rt_flags & RTF_IRTT) - rt->rt_irtt = irtt; +struct rtable * ip_rt_slow_route (__u32 daddr, int local) +{ + unsigned hash = ip_rt_hash_code(daddr)^local; + struct rtable * rth; + struct fib_node * f; + struct fib_info * fi; + __u32 saddr; - /* - * What we have to do is loop though this until we have - * found the first address which has a higher generality than - * the one in rt. Then we can put rt in right before it. - * The interrupts must be off for this process. - */ +#if RT_CACHE_DEBUG >= 2 + printk("rt_cache miss @%08x\n", daddr); +#endif - save_flags(cpuflags); - cli(); + rth = kmalloc(sizeof(struct rtable), GFP_ATOMIC); + if (!rth) + { + ip_rt_unlock(); + return NULL; + } - /* - * Remove old route if we are getting a duplicate. - */ - - rp = &rt_base; - while ((r = *rp) != NULL) + if (local) + f = fib_lookup_local(daddr); + else + f = fib_lookup (daddr); + + if (f) { - if (r->rt_dst != dst || - r->rt_mask != mask) - { - rp = &r->rt_next; - continue; - } - if (r->rt_metric != metric && r->rt_gateway != gw) + fi = f->fib_info; + f->fib_use++; + } + + if (!f || (fi->fib_flags & RTF_REJECT)) + { +#if RT_CACHE_DEBUG >= 2 + printk("rt_route failed @%08x\n", daddr); +#endif + ip_rt_unlock(); + kfree_s(rth, sizeof(struct rtable)); + return NULL; + } + + saddr = fi->fib_dev->pa_addr; + + if (daddr == fi->fib_dev->pa_addr) + { + f->fib_use--; + if ((f = fib_loopback) != NULL) { - duplicate = 1; - rp = &r->rt_next; - continue; + f->fib_use++; + fi = f->fib_info; } - *rp = r->rt_next; - if (rt_loopback == r) - rt_loopback = NULL; - ip_netlink_msg(RTMSG_DELROUTE, dst,gw, mask, flags, metric, rt->rt_dev->name); - kfree_s(r, sizeof(struct rtable)); } - - /* - * Add the new route - */ - - rp = &rt_base; - while ((r = *rp) != NULL) { - /* - * When adding a duplicate route, add it before - * the route with a higher metric. - */ - if (duplicate && - r->rt_dst == dst && - r->rt_mask == mask && - r->rt_metric > metric) - break; - else - /* - * Otherwise, just add it before the - * route with a higher generality. - */ - if ((r->rt_mask & mask) != mask) - break; - rp = &r->rt_next; + + if (!f) + { + ip_rt_unlock(); + kfree_s(rth, sizeof(struct rtable)); + return NULL; } - rt->rt_next = r; - *rp = rt; - - /* - * Update the loopback route - */ - - if ((rt->rt_dev->flags & IFF_LOOPBACK) && !rt_loopback) - rt_loopback = rt; - rt_stamp++; /* New table revision */ - - /* - * Restore the interrupts and return - */ + rth->rt_dst = daddr; + rth->rt_src = saddr; + rth->rt_lastuse = jiffies; + rth->rt_refcnt = 1; + rth->rt_use = 1; + rth->rt_next = NULL; + rth->rt_hh = NULL; + rth->rt_gateway = fi->fib_gateway; + rth->rt_dev = fi->fib_dev; + rth->rt_mtu = fi->fib_mtu; + rth->rt_window = fi->fib_window; + rth->rt_irtt = fi->fib_irtt; + rth->rt_tos = f->fib_tos; + rth->rt_flags = fi->fib_flags | RTF_HOST; + if (local) + rth->rt_flags |= RTF_LOCAL; - restore_flags(cpuflags); - ip_netlink_msg(RTMSG_NEWROUTE, dst,gw, mask, flags, metric, rt->rt_dev->name); - return; + if (!(rth->rt_flags & RTF_GATEWAY)) + rth->rt_gateway = rth->rt_dst; + + if (ip_rt_lock == 1) + rt_cache_add(hash, rth); + else + { + rt_free(rth); +#if RT_CACHE_DEBUG >= 1 + printk("rt_cache: route to %08x was born dead\n", daddr); +#endif + } + + ip_rt_unlock(); + return rth; } +void ip_rt_put(struct rtable * rt) +{ + if (rt) + ATOMIC_DECR(&rt->rt_refcnt); +} -/* - * Check if a mask is acceptable. - */ - -static inline int bad_mask(__u32 mask, __u32 addr) +struct rtable * ip_rt_route(__u32 daddr, int local) { - if (addr & (mask = ~mask)) - return 1; - mask = ntohl(mask); - if (mask & (mask+1)) - return 1; - return 0; + struct rtable * rth; + + ip_rt_fast_lock(); + + for (rth=ip_rt_hash_table[ip_rt_hash_code(daddr)^local]; rth; rth=rth->rt_next) + { + if (rth->rt_dst == daddr) + { + rth->rt_lastuse = jiffies; + ATOMIC_INCR(&rth->rt_use); + ATOMIC_INCR(&rth->rt_refcnt); + ip_rt_unlock(); + return rth; + } + } + return ip_rt_slow_route (daddr, local); } + /* - * Process a route add request from the user + * Process a route add request from the user, or from a kernel + * task. */ -static int rt_new(struct rtentry *r) +int ip_rt_new(struct rtentry *r) { int err; char * devname; @@ -465,7 +1629,7 @@ static int rt_new(struct rtentry *r) /* * BSD emulation: Permits route add someroute gw one-of-my-addresses * to indicate which iface. Not as clean as the nice Linux dev technique - * but people keep using it... + * but people keep using it... (and gated likes it ;)) */ if (!dev && (flags & RTF_GATEWAY)) @@ -522,8 +1686,8 @@ static int rt_new(struct rtentry *r) /* * Add the route */ - - ip_rt_add(flags, daddr, mask, gw, dev, r->rt_mss, r->rt_window, r->rt_irtt, metric); + + rt_add(flags, daddr, mask, gw, dev, r->rt_mss, r->rt_window, r->rt_irtt, metric); return 0; } @@ -539,6 +1703,7 @@ static int rt_kill(struct rtentry *r) struct sockaddr_in *gtw; char *devname; int err; + struct device * dev = NULL; trg = (struct sockaddr_in *) &r->rt_dst; msk = (struct sockaddr_in *) &r->rt_genmask; @@ -548,159 +1713,20 @@ static int rt_kill(struct rtentry *r) err = getname(devname, &devname); if (err) return err; + dev = dev_get(devname); + putname(devname); + if (!dev) + return -ENODEV; } /* * metric can become negative here if it wasn't filled in * but that's a fortunate accident; we really use that in rt_del. */ - err=rt_del((__u32)trg->sin_addr.s_addr, (__u32)msk->sin_addr.s_addr, devname, + err=rt_del((__u32)trg->sin_addr.s_addr, (__u32)msk->sin_addr.s_addr, dev, (__u32)gtw->sin_addr.s_addr, r->rt_flags, r->rt_metric - 1); - if ( devname != NULL ) - putname(devname); return err; } - -/* - * Called from the PROCfs module. This outputs /proc/net/route. - * - * We preserve the old format but pad the buffers out. This means that - * we can spin over the other entries as we read them. Remember the - * gated BGP4 code could need to read 60,000+ routes on occasion (thats - * about 7Mb of data). To do that ok we will need to also cache the - * last route we got to (reads will generally be following on from - * one another without gaps). - */ - -int rt_get_info(char *buffer, char **start, off_t offset, int length, int dummy) -{ - struct rtable *r; - int len=128; - off_t pos=0; - off_t begin=0; - char temp[129]; - - if(offset<128) - sprintf(buffer,"%-127s\n","Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU\tWindow\tIRTT"); - pos=128; - - for (r = rt_base; r != NULL; r = r->rt_next) - { - /* - * Spin through entries until we are ready - */ - if(pos+128rt_dev->name, (unsigned long)r->rt_dst, (unsigned long)r->rt_gateway, - r->rt_flags, r->rt_refcnt, r->rt_use, r->rt_metric, - (unsigned long)r->rt_mask, (int)r->rt_mss, r->rt_window, (int)r->rt_irtt); - sprintf(buffer+len,"%-127s\n",temp); - len+=128; - pos+=128; - if(posoffset+length) - break; - } - - *start=buffer+(offset-begin); - len-=(offset-begin); - if(len>length) - len=length; - return len; -} - -/* - * This is hackish, but results in better code. Use "-S" to see why. - */ - -#define early_out ({ goto no_route; 1; }) - -/* - * Route a packet. This needs to be fairly quick. Florian & Co. - * suggested a unified ARP and IP routing cache. Done right its - * probably a brilliant idea. I'd actually suggest a unified - * ARP/IP routing/Socket pointer cache. Volunteers welcome - */ - -struct rtable * ip_rt_route(__u32 daddr, struct options *opt, __u32 *src_addr) -{ - struct rtable *rt; - - for (rt = rt_base; rt != NULL || early_out ; rt = rt->rt_next) - { - if (!((rt->rt_dst ^ daddr) & rt->rt_mask)) - break; - /* - * broadcast addresses can be special cases.. - */ - if (rt->rt_flags & RTF_GATEWAY) - continue; - if ((rt->rt_dev->flags & IFF_BROADCAST) && - (rt->rt_dev->pa_brdaddr == daddr)) - break; - } - - if(rt->rt_flags&RTF_REJECT) - return NULL; - - if(src_addr!=NULL) - *src_addr= rt->rt_dev->pa_addr; - - if (daddr == rt->rt_dev->pa_addr) { - if ((rt = rt_loopback) == NULL) - goto no_route; - } - rt->rt_use++; - return rt; -no_route: - return NULL; -} - -struct rtable * ip_rt_local(__u32 daddr, struct options *opt, __u32 *src_addr) -{ - struct rtable *rt; - - for (rt = rt_base; rt != NULL || early_out ; rt = rt->rt_next) - { - /* - * No routed addressing. - */ - if (rt->rt_flags&RTF_GATEWAY) - continue; - - if (!((rt->rt_dst ^ daddr) & rt->rt_mask)) - break; - /* - * broadcast addresses can be special cases.. - */ - - if ((rt->rt_dev->flags & IFF_BROADCAST) && - rt->rt_dev->pa_brdaddr == daddr) - break; - } - - if(src_addr!=NULL) - *src_addr= rt->rt_dev->pa_addr; - - if (daddr == rt->rt_dev->pa_addr) { - if ((rt = rt_loopback) == NULL) - goto no_route; - } - rt->rt_use++; - return rt; -no_route: - return NULL; -} - /* * Handle IP routing ioctl calls. These are used to manipulate the routing tables */ @@ -720,8 +1746,15 @@ int ip_rt_ioctl(unsigned int cmd, void *arg) if (err) return err; memcpy_fromfs(&rt, arg, sizeof(struct rtentry)); - return (cmd == SIOCDELRT) ? rt_kill(&rt) : rt_new(&rt); + return (cmd == SIOCDELRT) ? rt_kill(&rt) : ip_rt_new(&rt); } return -EINVAL; } + +void ip_rt_advice(struct rtable **rp, int advice) +{ + /* Thanks! */ + return; +} + diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 6281566d9d0a..86a1075e3ff0 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -176,7 +176,9 @@ * Marc Tamsky : Closing in closing fixes. * Mike Shaver : RFC1122 verifications. * Alan Cox : rcv_saddr errors. - * Alan Cox : Block double connect() + * Alan Cox : Block double connect(). + * Alan Cox : Small hooks for enSKIP. + * Alexey Kuznetsov: Path MTU discovery. * * * To Fix: @@ -184,8 +186,6 @@ * so it doesn't iterate over the queue, also spot packets with no funny * options arriving in order and process directly. * - * Implement RFC 1191 [Path MTU discovery] - * Look at the effect of implementing RFC 1337 suggestions and their impact. * Rewrite output state machine to use a single queue and do low window * situations as per the spec (RFC 1122) * Speed up input assembly algorithm. @@ -674,19 +674,25 @@ void tcp_do_retransmit(struct sock *sk, int all) * currently this is done (less efficiently) elsewhere. */ - iph->id = htons(ip_id_count++); - ip_send_check(iph); - /* * Put a MAC header back on (may cause ARPing) */ - if(skb->localroute) - rt=ip_rt_local(iph->daddr,NULL,NULL); - else - rt=ip_rt_route(iph->daddr,NULL,NULL); + { + /* ANK: UGLY, but the bug, that was here, should be fixed. + */ + struct options * opt = (struct options*)skb->proto_priv; + rt = ip_check_route(&sk->ip_route_cache, opt->srr?opt->faddr:iph->daddr, skb->localroute); + } + + iph->id = htons(ip_id_count++); +#ifndef CONFIG_NO_PATH_MTU_DISCOVERY + if (rt && ntohs(iph->tot_len) > rt->rt_mtu) + iph->frag_off &= ~htons(IP_DF); +#endif + ip_send_check(iph); - if(rt==NULL) /* Deep poo */ + if (rt==NULL) /* Deep poo */ { if(skb->sk) { @@ -698,11 +704,20 @@ void tcp_do_retransmit(struct sock *sk, int all) { dev=rt->rt_dev; skb->raddr=rt->rt_gateway; - if(skb->raddr==0) - skb->raddr=iph->daddr; skb->dev=dev; skb->arp=1; - if(dev->hard_header) + if (rt->rt_hh) + { + memcpy(skb_push(skb,dev->hard_header_len),rt->rt_hh->hh_data,dev->hard_header_len); + if (!rt->rt_hh->hh_uptodate) + { + skb->arp = 0; +#if RT_CACHE_DEBUG >= 2 + printk("tcp_do_retransmit: hh miss %08x via %08x\n", iph->daddr, rt->rt_gateway); +#endif + } + } + else if (dev->hard_header) { if(dev->hard_header(skb, dev, ETH_P_IP, NULL, NULL, skb->len)<0) skb->arp=0; @@ -869,8 +884,7 @@ static int tcp_write_timeout(struct sock *sk) * Attempt to recover if arp has changed (unlikely!) or * a route has shifted (not supported prior to 1.3). */ - arp_destroy (sk->daddr, 0); - /*ip_route_check (sk->daddr);*/ + ip_rt_advice(&sk->ip_route_cache, 0); } /* @@ -1040,13 +1054,15 @@ static void retransmit_timer(unsigned long data) void tcp_err(int type, int code, unsigned char *header, __u32 daddr, __u32 saddr, struct inet_protocol *protocol) { - struct tcphdr *th; + struct tcphdr *th = (struct tcphdr *)header; struct sock *sk; - struct iphdr *iph=(struct iphdr *)header; - - header+=4*iph->ihl; - - + + /* + * This one is _WRONG_. FIXME urgently. + */ +#ifndef CONFIG_NO_PATH_MTU_DISCOVERY + struct iphdr *iph=(struct iphdr *)(header-sizeof(struct iphdr)); +#endif th =(struct tcphdr *)header; sk = get_sock(&tcp_prot, th->source, daddr, th->dest, saddr); @@ -1071,6 +1087,27 @@ void tcp_err(int type, int code, unsigned char *header, __u32 daddr, sk->error_report(sk); } +#ifndef CONFIG_NO_PATH_MTU_DISCOVERY + if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) + { + struct rtable * rt; + /* + * Ugly trick to pass MTU to protocol layer. + * Really we should add argument "info" to error handler. + */ + unsigned short new_mtu = ntohs(iph->id); + + if ((rt = sk->ip_route_cache) != NULL) + if (rt->rt_mtu > new_mtu) + rt->rt_mtu = new_mtu; + + if (sk->mtu > new_mtu - sizeof(struct iphdr) - sizeof(struct tcphdr)) + sk->mtu = new_mtu - sizeof(struct iphdr) - sizeof(struct tcphdr); + + return; + } +#endif + /* * If we've already connected we will keep trying * until we time out, or the user gives up. @@ -1553,7 +1590,7 @@ static void tcp_send_ack(u32 sequence, u32 ack, */ tmp = sk->prot->build_header(buff, sk->saddr, daddr, &dev, - IPPROTO_TCP, sk->opt, MAX_ACK_SIZE,sk->ip_tos,sk->ip_ttl); + IPPROTO_TCP, sk->opt, MAX_ACK_SIZE,sk->ip_tos,sk->ip_ttl,&sk->ip_route_cache); if (tmp < 0) { buff->free = 1; @@ -1787,6 +1824,24 @@ static int tcp_sendmsg(struct sock *sk, struct msghdr *msg, /* * Now we need to check if we have a half built packet. */ +#ifndef CONFIG_NO_PATH_MTU_DISCOVERY + /* + * FIXME: I'm almost sure that this fragment is BUG, + * but it works... I do not know why 8) --ANK + * + * Really, we should rebuild all the queues... + * It's difficult. Temprorary hack is to send all + * queued segments with allowed fragmentation. + */ + { + int new_mss = min(sk->mtu, sk->max_window); + if (new_mss < sk->mss) + { + tcp_send_partial(sk); + sk->mss = new_mss; + } + } +#endif if ((skb = tcp_dequeue_partial(sk)) != NULL) { @@ -1800,11 +1855,10 @@ static int tcp_sendmsg(struct sock *sk, struct msghdr *msg, if (!(flags & MSG_OOB)) { copy = min(sk->mss - (skb->len - hdrlen), seglen); - /* FIXME: this is really a bug. */ if (copy <= 0) { - printk("TCP: **bug**: \"copy\" <= 0: %d - (%ld - %d) <= %d\n", sk->mss, skb->len, hdrlen, seglen); - copy = 0; + printk("TCP: **bug**: \"copy\" <= 0\n"); + return -EFAULT; } memcpy_fromfs(skb_put(skb,copy), from, copy); from += copy; @@ -1922,7 +1976,7 @@ static int tcp_sendmsg(struct sock *sk, struct msghdr *msg, */ tmp = prot->build_header(skb, sk->saddr, sk->daddr, &dev, - IPPROTO_TCP, sk->opt, skb->truesize,sk->ip_tos,sk->ip_ttl); + IPPROTO_TCP, sk->opt, skb->truesize,sk->ip_tos,sk->ip_ttl,&sk->ip_route_cache); if (tmp < 0 ) { sock_wfree(sk, skb); @@ -1931,6 +1985,9 @@ static int tcp_sendmsg(struct sock *sk, struct msghdr *msg, return(copied); return(tmp); } +#ifndef CONFIG_NO_PATH_MTU_DISCOVERY + skb->ip_hdr->frag_off |= htons(IP_DF); +#endif skb->dev = dev; skb->h.th =(struct tcphdr *)skb_put(skb,sizeof(struct tcphdr)); tmp = tcp_build_header(skb->h.th, sk, seglen-copy); @@ -2038,7 +2095,7 @@ static void tcp_read_wakeup(struct sock *sk) */ tmp = sk->prot->build_header(buff, sk->saddr, sk->daddr, &dev, - IPPROTO_TCP, sk->opt, MAX_ACK_SIZE,sk->ip_tos,sk->ip_ttl); + IPPROTO_TCP, sk->opt, MAX_ACK_SIZE,sk->ip_tos,sk->ip_ttl,&sk->ip_route_cache); if (tmp < 0) { buff->free = 1; @@ -2560,7 +2617,7 @@ static void tcp_send_fin(struct sock *sk) tmp = prot->build_header(buff,sk->saddr, sk->daddr, &dev, IPPROTO_TCP, sk->opt, - sizeof(struct tcphdr),sk->ip_tos,sk->ip_ttl); + sizeof(struct tcphdr),sk->ip_tos,sk->ip_ttl,&sk->ip_route_cache); if (tmp < 0) { int t; @@ -2715,7 +2772,7 @@ static void tcp_reset(unsigned long saddr, unsigned long daddr, struct tcphdr *t */ tmp = prot->build_header(buff, saddr, daddr, &ndev, IPPROTO_TCP, opt, - sizeof(struct tcphdr),tos,ttl); + sizeof(struct tcphdr),tos,ttl,NULL); if (tmp < 0) { buff->free = 1; @@ -2919,6 +2976,7 @@ static void tcp_conn_request(struct sock *sk, struct sk_buff *skb, memcpy(newsk, sk, sizeof(*newsk)); newsk->opt = NULL; + newsk->ip_route_cache = NULL; if (opt && opt->optlen) { sk->opt = (struct options*)kmalloc(sizeof(struct options)+opt->optlen, GFP_ATOMIC); if (!sk->opt) { @@ -3022,7 +3080,8 @@ static void tcp_conn_request(struct sock *sk, struct sk_buff *skb, * Note use of sk->user_mss, since user has no direct access to newsk */ - rt=ip_rt_route(saddr, NULL,NULL); + rt = ip_rt_route(newsk->opt && newsk->opt->srr ? newsk->opt->faddr : saddr, 0); + newsk->ip_route_cache = rt; if(rt!=NULL && (rt->rt_flags&RTF_WINDOW)) newsk->window_clamp = rt->rt_window; @@ -3031,19 +3090,10 @@ static void tcp_conn_request(struct sock *sk, struct sk_buff *skb, if (sk->user_mss) newsk->mtu = sk->user_mss; - else if(rt!=NULL && (rt->rt_flags&RTF_MSS)) - newsk->mtu = rt->rt_mss - sizeof(struct iphdr) - sizeof(struct tcphdr); + else if (rt) + newsk->mtu = rt->rt_mtu - sizeof(struct iphdr) - sizeof(struct tcphdr); else - { -#ifdef CONFIG_INET_SNARL /* Sub Nets Are Local */ - if ((saddr ^ daddr) & default_mask(saddr)) -#else - if ((saddr ^ daddr) & dev->pa_mask) -#endif - newsk->mtu = 576 - sizeof(struct iphdr) - sizeof(struct tcphdr); - else - newsk->mtu = MAX_WINDOW; - } + newsk->mtu = 576 - sizeof(struct iphdr) - sizeof(struct tcphdr); /* * But not bigger than device MTU @@ -3051,6 +3101,20 @@ static void tcp_conn_request(struct sock *sk, struct sk_buff *skb, newsk->mtu = min(newsk->mtu, dev->mtu - sizeof(struct iphdr) - sizeof(struct tcphdr)); +#ifdef CONFIG_SKIP + + /* + * SKIP devices set their MTU to 65535. This is so they can take packets + * unfragmented to security process then fragment. They could lie to the + * TCP layer about a suitable MTU, but its easier to let skip sort it out + * simply because the final package we want unfragmented is going to be + * + * [IPHDR][IPSP][Security data][Modified TCP data][Security data] + */ + + if(skip_pick_mtu!=NULL) /* If SKIP is loaded.. */ + sk->mtu=skip_pick_mtu(sk->mtu,dev); +#endif /* * This will min with what arrived in the packet */ @@ -3080,7 +3144,7 @@ static void tcp_conn_request(struct sock *sk, struct sk_buff *skb, */ tmp = sk->prot->build_header(buff, newsk->saddr, newsk->daddr, &ndev, - IPPROTO_TCP, NULL, MAX_SYN_SIZE,sk->ip_tos,sk->ip_ttl); + IPPROTO_TCP, NULL, MAX_SYN_SIZE,sk->ip_tos,sk->ip_ttl,&newsk->ip_route_cache); /* * Something went wrong. @@ -3281,6 +3345,13 @@ static void tcp_write_xmit(struct sock *sk) iph = skb->ip_hdr; th = (struct tcphdr *)(((char *)iph) +(iph->ihl << 2)); size = skb->len - (((unsigned char *) th) - skb->data); +#ifndef CONFIG_NO_PATH_MTU_DISCOVERY + if (size > sk->mtu - sizeof(struct iphdr)) + { + iph->frag_off &= ~htons(IP_DF); + ip_send_check(iph); + } +#endif th->ack_seq = ntohl(sk->acked_seq); th->window = ntohs(tcp_select_window(sk)); @@ -4522,29 +4593,17 @@ static int tcp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len) * Put in the IP header and routing stuff. */ - if (sk->localroute) - rt=ip_rt_local(sk->daddr, NULL, sk->saddr ? NULL : &sk->saddr); - else - rt=ip_rt_route(sk->daddr, NULL, sk->saddr ? NULL : &sk->saddr); - - /* - * When we connect we enforce receive requirements too. - */ - - sk->rcv_saddr=sk->saddr; - - /* - * We need to build the routing stuff from the things saved in skb. - */ - tmp = sk->prot->build_header(buff, sk->saddr, sk->daddr, &dev, - IPPROTO_TCP, NULL, MAX_SYN_SIZE,sk->ip_tos,sk->ip_ttl); + IPPROTO_TCP, NULL, MAX_SYN_SIZE,sk->ip_tos,sk->ip_ttl,&sk->ip_route_cache); if (tmp < 0) { sock_wfree(sk, buff); release_sock(sk); return(-ENETUNREACH); } + if ((rt = sk->ip_route_cache) != NULL && !sk->saddr) + sk->saddr = rt->rt_src; + sk->rcv_saddr = sk->saddr; t1 = (struct tcphdr *) skb_put(buff,sizeof(struct tcphdr)); @@ -4571,19 +4630,11 @@ static int tcp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len) if (sk->user_mss) sk->mtu = sk->user_mss; - else if(rt!=NULL && (rt->rt_flags&RTF_MSS)) - sk->mtu = rt->rt_mss; + else if (rt) + sk->mtu = rt->rt_mtu - sizeof(struct iphdr) - sizeof(struct tcphdr); else - { -#ifdef CONFIG_INET_SNARL - if ((sk->saddr ^ sk->daddr) & default_mask(sk->saddr)) -#else - if ((sk->saddr ^ sk->daddr) & dev->pa_mask) -#endif - sk->mtu = 576 - sizeof(struct iphdr) - sizeof(struct tcphdr); - else - sk->mtu = MAX_WINDOW; - } + sk->mtu = 576 - sizeof(struct iphdr) - sizeof(struct tcphdr); + /* * but not bigger than device MTU */ @@ -4592,6 +4643,21 @@ static int tcp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len) sk->mtu = 32; /* Sanity limit */ sk->mtu = min(sk->mtu, dev->mtu - sizeof(struct iphdr) - sizeof(struct tcphdr)); + +#ifdef CONFIG_SKIP + + /* + * SKIP devices set their MTU to 65535. This is so they can take packets + * unfragmented to security process then fragment. They could lie to the + * TCP layer about a suitable MTU, but its easier to let skip sort it out + * simply because the final package we want unfragmented is going to be + * + * [IPHDR][IPSP][Security data][Modified TCP data][Security data] + */ + + if(skip_pick_mtu!=NULL) /* If SKIP is loaded.. */ + sk->mtu=skip_pick_mtu(sk->mtu,dev); +#endif /* * Put in the TCP options to say MTU. @@ -5222,7 +5288,7 @@ static void tcp_write_wakeup(struct sock *sk) tmp = sk->prot->build_header(buff, sk->saddr, sk->daddr, &dev, IPPROTO_TCP, sk->opt, buff->truesize, - sk->ip_tos,sk->ip_ttl); + sk->ip_tos,sk->ip_ttl,&sk->ip_route_cache); if (tmp < 0) { sock_wfree(sk, buff); @@ -5321,7 +5387,7 @@ static void tcp_write_wakeup(struct sock *sk) */ tmp = sk->prot->build_header(buff, sk->saddr, sk->daddr, &dev, - IPPROTO_TCP, sk->opt, MAX_ACK_SIZE,sk->ip_tos,sk->ip_ttl); + IPPROTO_TCP, sk->opt, MAX_ACK_SIZE,sk->ip_tos,sk->ip_ttl,&sk->ip_route_cache); if (tmp < 0) { sock_wfree(sk, buff); diff --git a/net/ipv4/timer.c b/net/ipv4/timer.c index aa1c5afc2def..e62cf1486f3f 100644 --- a/net/ipv4/timer.c +++ b/net/ipv4/timer.c @@ -146,8 +146,6 @@ void net_timer (unsigned long data) /* We've waited long enough, close the socket. */ sk->state = TCP_CLOSE; delete_timer (sk); - /* Kill the ARP entry in case the hardware has changed. */ - arp_destroy (sk->daddr, 0); if (!sk->dead) sk->state_change(sk); sk->shutdown = SHUTDOWN_MASK; diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 88528625549c..e33880ed9a55 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -400,8 +400,8 @@ static int udp_sendto(struct sock *sk, const unsigned char *from, int len, int n * Temporary */ -static int udp_sendmsg(struct sock *sk, struct msghdr *msg, - int len, int noblock, int flags) +static int udp_sendmsg(struct sock *sk, struct msghdr *msg, int len, int noblock, + int flags) { if(msg->msg_iovlen==1) return udp_sendto(sk,msg->msg_iov[0].iov_base,len, noblock, flags, msg->msg_name, msg->msg_namelen); @@ -541,7 +541,6 @@ int udp_recvmsg(struct sock *sk, struct msghdr *msg, int len, int udp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len) { struct rtable *rt; - __u32 sa; if (addr_len < sizeof(*usin)) return(-EINVAL); @@ -553,19 +552,18 @@ int udp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len) if(!sk->broadcast && ip_chk_addr(usin->sin_addr.s_addr)==IS_BROADCAST) return -EACCES; /* Must turn broadcast on first */ - rt=(sk->localroute?ip_rt_local:ip_rt_route)((__u32)usin->sin_addr.s_addr, NULL, &sa); - if(rt==NULL) + rt=ip_rt_route((__u32)usin->sin_addr.s_addr, sk->localroute); + if (rt==NULL) return -ENETUNREACH; if(!sk->saddr) - sk->saddr = sa; /* Update source address */ + sk->saddr = rt->rt_src; /* Update source address */ if(!sk->rcv_saddr) - sk->rcv_saddr = sa; + sk->rcv_saddr = rt->rt_src; sk->daddr = usin->sin_addr.s_addr; sk->dummy_th.dest = usin->sin_port; sk->state = TCP_ESTABLISHED; udp_cache_zap(); sk->ip_route_cache = rt; - sk->ip_route_stamp = rt_stamp; return(0); } diff --git a/net/ipx/af_ipx.c b/net/ipx/af_ipx.c index a10b8cf029f3..e5fbec680eb3 100644 --- a/net/ipx/af_ipx.c +++ b/net/ipx/af_ipx.c @@ -304,7 +304,7 @@ ipxitf_down(ipx_interface *intrfc) } static int -ipxitf_device_event(unsigned long event, void *ptr) +ipxitf_device_event(struct notifier_block *notifier, unsigned long event, void *ptr) { struct device *dev = ptr; ipx_interface *i, *tmp; diff --git a/net/netlink.c b/net/netlink.c index c72abe4342fd..3b7d7fb00a21 100644 --- a/net/netlink.c +++ b/net/netlink.c @@ -216,8 +216,8 @@ int init_netlink(void) { int ct; - if(register_chrdev(NET_MAJOR,"netlink", &netlink_fops)) { - printk("netlink: unable to get major %d\n", NET_MAJOR); + if(register_chrdev(NETLINK_MAJOR,"netlink", &netlink_fops)) { + printk("netlink: unable to get major %d\n", NETLINK_MAJOR); return -EIO; } for(ct=0;ctfree = 1; kfree_skb(skb, FREE_WRITE); return 1; diff --git a/net/netrom/nr_route.c b/net/netrom/nr_route.c index ff4bb73b44f8..2181e60e9d29 100644 --- a/net/netrom/nr_route.c +++ b/net/netrom/nr_route.c @@ -524,7 +524,7 @@ static struct device *nr_ax25_dev_get(char *devname) return dev; if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ETHER) - if (arp_query((unsigned char *)&callsign, dev->pa_addr, ARPHRD_AX25)) + if (arp_query((unsigned char *)&callsign, dev->pa_addr, dev)) return dev; return NULL; diff --git a/net/socket.c b/net/socket.c index 66765bcd07a1..98baa2c59ef1 100644 --- a/net/socket.c +++ b/net/socket.c @@ -878,12 +878,12 @@ asmlinkage int sys_send(int fd, void * buff, int len, unsigned flags) if(err) return err; + iov.iov_base=buff; + iov.iov_len=len; msg.msg_name=NULL; msg.msg_iov=&iov; msg.msg_iovlen=1; msg.msg_accrights=NULL; - iov.iov_base=buff; - iov.iov_len=1; return(sock->ops->sendmsg(sock, &msg, len, (file->f_flags & O_NONBLOCK), flags)); } -- 2.39.5