diff options
320 files changed, 10700 insertions, 9117 deletions
@@ -62,9 +62,10 @@ S: USA N: Andrea Arcangeli E: arcangeli@mbox.queen.it -W: http://www-linux.deis.unibo.it/~mirror/ +W: http://www.cs.unibo.it/~arcangel/ P: 1024/CB4660B9 CC A0 71 81 F4 A0 63 AC C0 4B 81 1D 8C 15 C8 E5 -D: parport sharing fix. Various other kernel hacks. +D: Parport sharing hacker. +D: Various other kernel hacks. S: Via Ciaclini 26 S: Imola 40026 S: Italy @@ -395,6 +396,11 @@ S: Virginia Tech S: Blacksburg, Virginia 24061 S: USA +N: Cyrus Durgin +E: cider@speakeasy.org +W: http://www.speakeasy.org/~cider/ +D: implemented kmod + N: Torsten Duwe E: Torsten.Duwe@informatik.uni-erlangen.de D: Part-time kernel hacker @@ -1337,6 +1343,7 @@ S: Russia N: Kirk Petersen E: kirk@speakeasy.org W: http://www.speakeasy.org/~kirk/ +D: implemented kmod D: modularized BSD Unix domain sockets N: Kai Petzke @@ -1413,6 +1420,15 @@ S: 6, rue Augustin Thierry S: 75019 Paris S: France +N: Rik van Riel +E: H.H.vanRiel@fys.ruu.nl +W: http://www.fys.ruu.nl/~riel/ +D: Maintainer of the mm-patches page (see www.linuxhq.com) +D: Documentation/sysctl/*, kswapd fixes, random kernel hacker +S: Vorenkampsweg 1 +S: NL-9488 TG Zeijerveld +S: The Netherlands + N: William E. Roadcap E: roadcapw@cfw.com W: http://www.cfw.com/~roadcapw diff --git a/Documentation/Changes b/Documentation/Changes index 6f778a20f..9b5846d92 100644 --- a/Documentation/Changes +++ b/Documentation/Changes @@ -32,7 +32,7 @@ http://cyberbuzz.gatech.edu/kaboom/linux/ as well. Also, don't forget http://www.linuxhq.com/ for all your Linux kernel needs. -Last updated: February 16. 1998 +Last updated: March 16. 1998 Current Author: Chris Ricker (kaboom@gatech.edu). Current Minimal Requirements @@ -45,11 +45,11 @@ running, the suggested command should tell you. - Kernel modules modutils-2.1.85 ; insmod -V - Gnu C 2.7.2.3 ; gcc --version - Binutils 2.8.1.0.1 ; ld -v -- Linux C Library 5.4.38 ; ls -l /lib/libc.so.* +- Linux C Library 5.4.44 ; ls -l /lib/libc.so.* - Dynamic Linker (ld.so) 1.9.5 ; ldd -v - Linux C++ Library 2.7.2.8 ; ls -l /usr/lib/libg++.so.* - Procps 1.2.5 ; ps --version -- Procinfo 0.11 ; procinfo -v +- Procinfo 0.13 ; procinfo -v - Mount 2.7l ; mount --version - Net-tools 1.41 ; hostname -V - Loadlin 1.6a @@ -58,7 +58,7 @@ running, the suggested command should tell you. - NFS 0.4.21 ; showmount --version - Bash 1.14.7 ; bash -version - Ncpfs 2.1.1 ; ncpmount -v -- Pcmcia-cs 2.9.12 +- Pcmcia-cs 3.0.0 - PPP 2.3.3 ; pppd -v Upgrade notes @@ -73,6 +73,10 @@ know it works on your hardware, add a "reboot=warm" command line option in Lilo. A small number of machines need "reboot=bios" to reboot via the BIOS. + Also, please remember that cua* devices are now obsolete. Switch to +the corresponding ttyS* device instead (e.g., cua0 -> ttyS0, cua1 -> +ttyS1, etc.). + Libc ==== @@ -109,6 +113,12 @@ Modules You need to upgrade to modutils-2.1.85 for kernels 2.1.85 and later. This version will also work with 2.0.x kernels. + As of 2.1.90-pre1, kerneld has been replaced by a kernel thread, +kmod. See Documentation/kmod.txt for more information. The main +user-level change this requires is modification to your init scripts to +check for the absence of /proc/sys/kernel/modprobe before starting +kerneld. + Binutils ======== @@ -148,7 +158,7 @@ in some standard tools. Check in /proc/net/rt_local to verify their presence. To turn on IP forwarding, issue the following command: echo 1 > -/proc/sys/net/ipv4/ip_forwarding +/proc/sys/net/ipv4/ip_forward To run bootpd, you'll need to issue the following command: echo 1 >/proc/sys/net/ipv4/ip_boot_agent @@ -266,12 +276,12 @@ ftp://sunsite.unc.edu/pub/Linux/GCC/release.gcc-2.7.2.3 Linux C Library =============== -The 5.4.38 release: -ftp://tsx-11.mit.edu/pub/linux/packages/GCC/libc-5.4.38.bin.tar.gz -ftp://sunsite.unc.edu/pub/Linux/GCC/libc-5.4.38.bin.tar.gz +The 5.4.44 release: +ftp://tsx-11.mit.edu/pub/linux/packages/GCC/libc-5.4.44.bin.tar.gz +ftp://sunsite.unc.edu/pub/Linux/GCC/libc-5.4.44.bin.tar.gz Installation notes for 5.4.38: -ftp://tsx-11.mit.edu/pub/linux/packages/GCC/release.libc-5.4.38 -ftp://sunsite.unc.edu/pub/Linux/GCC/release.libc-5.4.38 +ftp://tsx-11.mit.edu/pub/linux/packages/GCC/release.libc-5.4.44 +ftp://sunsite.unc.edu/pub/Linux/GCC/release.libc-5.4.44 Linux C++ Library ================= @@ -308,7 +318,7 @@ Procinfo utilities ================== The 0.11 release: -ftp://ftp.cistron.nl/pub/people/svm/procinfo-0.11.tar.gz +ftp://ftp.cistron.nl/pub/people/svm/procinfo-0.13.tar.gz RPM utilities ============= @@ -391,8 +401,8 @@ ftp://sunsite.unc.edu/pub/Linux/system/Filesystems/ncpfs/ncpfs-2.1.1.tgz Pcmcia-cs ========= -The 1.9.12 release: -ftp://hyper.stanford.edu/pub/pcmcia/pcmcia-cs-2.9.12.tar.gz +The 3.0.0 release: +ftp://hyper.stanford.edu/pub/pcmcia/pcmcia-cs-3.0.0.tar.gz PPP === @@ -415,11 +425,12 @@ distribution), most of these are available in RPM format. Check around your favorite Red Hat mirror site before installing the non-RPM version. Remember, you might need to use the -force option to get the upgrade to install. ftp://ftp.redhat.com/pub/contrib/ will have almost -everything you need. +everything you need, as does RedHat 5.0. Those of you running Debian (or a different distribution that supports .deb packages) can look in the "unstable" and -"project/experimental" directories of your favorite Debian mirror. +"project/experimental" directories of your favorite Debian mirror. The +Debian 2.0 release should have most packages you need as well. For others, David Bourgin has put together a package of everything necessary to quickly and easily upgrade to 2.1.x. See diff --git a/Documentation/Configure.help b/Documentation/Configure.help index daea952c7..eddc729b8 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -103,6 +103,17 @@ CONFIG_MATH_EMULATION you are not sure, say Y; apart from resulting in a 45kB bigger kernel, it won't hurt. +Max physical memory +CONFIG_MAX_MEMSIZE + Linux/x86 can use up to 3.8 gigabytes of physical memory. Default + is max 1 gigabyte physical memory (1024 MB), this is enough for + most systems. + A system with 2G physical memory should use a value of ~2400, a + system with 3.8G memory should use something like 3900. A bit of + experimentation with the limit wont hurt, the kernel needs a ~128M + window for vmalloc() plus PCI space uses up some memory too, thus + addresses above FD000000 should rather be kept free. + Normal floppy disk support CONFIG_BLK_DEV_FD If you want to use your floppy disk drive(s) under Linux, say @@ -1343,21 +1354,12 @@ CONFIG_MODVERSIONS non-kernel sources, you would benefit from this option. Otherwise it's not that important. So, N ought to be a safe bet. -Kernel daemon support -CONFIG_KERNELD - Normally when you have selected some drivers and/or filesystems to - be created as loadable modules, you also have the responsibility to - load the corresponding module (via insmod/modprobe) before you can - use it. If you select Y here, the kernel will take care of this all - by itself, together with the user level daemon "kerneld". Note that - "kerneld" will also automatically unload all unused modules, so you - don't have to use "rmmod" either. kerneld will also provide support - for different user-level beeper and screen blanker programs later - on. The "kerneld" daemon is included in the modutils package (check - Documentation/Changes for latest version and location). You will - probably want to read the kerneld mini-HOWTO, available via ftp - (user: anonymous) from - sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. If unsure, say Y. +Kernel module loader support +CONFIG_KMOD + This feature allows the kernel to load modules for itself. When + a part of the kernel needs a module, it runs modprobe with the + appropriate arguments. Say Y here and read about configuring it + in Documentation/kmod.txt. (this is a replacement of kerneld) ARP daemon support (EXPERIMENTAL) CONFIG_ARPD @@ -6135,9 +6137,9 @@ CONFIG_ZFTAPE loadable module called `zft-compressor.o' which contains code to support user transparent on-the-fly compression based on Ross William's lzrw3 algorithm will be produced. If you have enabled - auto-loading of kernel modules via `kerneld' (i.e. have said `Y' to - CONFIG_KERNELD) then `zft-compressor.o' will be loaded automatically - by zftape when needed. + the kernel module loader (i.e. have said `Y' to CONFIG_KMOD) then + `zft-compressor.o' will be loaded automatically by zftape when + needed. Despite of its name zftape does NOT use compression by default. The file Documentation/ftape.txt contains a short description of the most important changes in the file system interface compared to @@ -6595,12 +6597,6 @@ CONFIG_JOYSTICK be called joystick.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. -ARC console time -CONFIG_RTC_ARC - If you boot your Alpha using the ARC firmware, say Y here. This option - adjusts the RTC clock to take into account the different starting epoch - used by ARC. - Sound card support CONFIG_SOUND If you have a Sound Card in your Computer, i.e. if it can say more @@ -6636,6 +6632,16 @@ CONFIG_SB an unknown card you may answer Y if the card claims to be SoundBlaster compatible. +Are you using the IBM Mwave "emulation" of SB ? +CONFIG_SB_MWAVE + The IBM Mwave can do whats loosely describable as emulation of an 8bit + soundblaster if you load the right firmware from DOS warm boot and pray + and your machine happens to like you. Say Y if you are doing this as the + IRQ test normally fails on the mwave emulation. If you'd like real MWAVE + support phone IBM (425-556-8822) and ask them why they still haven't + released any documentation. + [http://204.200.238.31/cgi-bin/link.pl?co=i&cl=/ts/ibm/contact.html] + Generic OPL2/OPL3 FM synthesizer support CONFIG_ADLIB Answer Y if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4). diff --git a/Documentation/cdrom/ide-cd b/Documentation/cdrom/ide-cd index d6ceef8a5..ecd498158 100644 --- a/Documentation/cdrom/ide-cd +++ b/Documentation/cdrom/ide-cd @@ -224,10 +224,8 @@ a. Drive is not detected during booting. - If the autoprobing is not finding your drive, you can tell the driver to assume that one exists by using a lilo option of the form `hdX=cdrom', where X is the drive letter corresponding to - where your drive is installed (see section 2). This is required - for CDROM drives such as the Pioneer DR-A24X, which do not properly - identify themselves as ATAPI CDROM drives. Note that if you - do this and you see a boot message like + where your drive is installed. Note that if you do this and you + see a boot message like hdX: ATAPI cdrom (?) @@ -281,7 +279,16 @@ b. Timeout/IRQ errors. there are hardware problems with the interrupt setup; they apparently don't use interrupts. - + - If you own a Pioneer DR-A24X, you _will_ get nasty error messages + on boot such as "irq timeout: status=0x50 { DriveReady SeekComplete }" + The Pioneer DR-A24X cdrom drives are fairly popular these days. + Unfortunatly, these drives seem to become very confused when we perform + the standard Linux ATA disk drive probe. If you own one of these drives, + you can bypass the ATA probing which confuses these cdrom drives, by + adding `append="hdX=noprobe hdX=cdrom"' to your lilo.conf file and runing + lilo (again where X is the drive letter corresponding to where your drive + is installed.) + c. System hangups. - If the system locks up when you try to access the cdrom, the most diff --git a/Documentation/kmod.txt b/Documentation/kmod.txt new file mode 100644 index 000000000..3deeeaed8 --- /dev/null +++ b/Documentation/kmod.txt @@ -0,0 +1,47 @@ +Kmod: The Kernel Module Loader +Kirk Petersen + +Kmod is a simple replacement for kerneld. It consists of a +request_module() replacement and a kernel thread called kmod. When the +kernel requests a module, the kmod wakes up and execve()s modprobe, +passing it the name that was requested. After a configurable period of +time, kmod will have delete_module() remove any unused modules. + +Kmod is configurable through two entries in /proc/sys/kernel. You can +set the path of modprobe (where the kernel looks for it) by doing: + + echo "/sbin/modprobe" > /proc/sys/kernel/modprobe + +To tell kmod when to unload unused modules, do something like: + + echo "120" > /proc/sys/kernel/kmod_unload_delay + +Kmod only loads and unloads modules. Kerneld could do more (although +nothing in the standard kernel used its other features). If you +require features such as request_route, we suggest that you take +a similar approach. A simple request_route function could be called, +and a kroute kernel thread could be sent off to do the work. But +we should probably keep this to a minimum. + +Kerneld also had a mechanism for storing device driver settings. This +can easily be done with modprobe. When a module is unloaded, modprobe +could look at a per-driver-configurable location (/proc/sys/drivers/blah) +for device driver settings and save them to a file. When a module +is loaded, simply cat that file back to that location in the proc +filesystem. Or perhaps a script could be a setting in /etc/modules.conf. +There are many user-land methods that will work (I prefer using /proc, +myself). + +If kerneld worked, why replace it? + +- kerneld used sysv ipc, which can now be made into a module. Besides, + sysv ipc is ugly and should therefore be avoided (well, certainly for + kernel level stuff) + +- both kmod and kerneld end up doing the same thing (calling modprobe), + so why not skip the middle man? + +- removing kerneld related stuff from ipc/msg.c made it 40% smaller + +- kmod reports errors through the normal kernel mechanisms, which avoids + the chicken and egg problem of kerneld and modular unix domain sockets diff --git a/Documentation/modules.txt b/Documentation/modules.txt index b637da0a4..ca9c434f7 100644 --- a/Documentation/modules.txt +++ b/Documentation/modules.txt @@ -10,6 +10,12 @@ Some older modules packages aren't aware of some of the newer modular features that the kernel now supports. The current required version is listed in the file linux/Documentation/Changes. +* * * NOTE * * * +The kernel has been changed to remove kerneld support and use +the new kmod support. Keep this in mind when reading this file. Kmod +does the exact same thing as kerneld, but doesn't require an external +program (see Documentation/kmod.txt) + In the beginning... ------------------- diff --git a/Documentation/sysctl/vm.txt b/Documentation/sysctl/vm.txt index a9f5d9058..818d00f40 100644 --- a/Documentation/sysctl/vm.txt +++ b/Documentation/sysctl/vm.txt @@ -16,6 +16,7 @@ usage. Currently, these files are in /proc/sys/vm: - bdflush +- buffermem - freepages - overcommit_memory - swapctl @@ -88,11 +89,27 @@ number of jiffies per second is 100, except on Alpha machines age_super is for filesystem metadata. ============================================================== +buffermem: + +The three values in this file correspond to the values in +the struct buffer_mem. It controls how much memory should +be used for buffer and cache memory. Note that memorymapped +files are also counted as cache memory... + +The values are: +min_percent -- this is the minumum percentage of memory + that should be spent on buffer + page cache +borrow_percent -- when Linux is short on memory, and buffer + and cache use more than this percentage of + memory, free pages are stolen from them +max_percent -- this is the maximum amount of memory that + can be used for buffer and cache memory +============================================================== freepages: -This file contains three values: min_free_pages, free_pages_low -and free_pages_high in order. +This file contains the values in the struct freepages. That +struct contains three members: min, low and high. These numbers are used by the VM subsystem to keep a reasonable number of pages on the free page list, so that programs can @@ -100,25 +117,23 @@ allocate new pages without having to wait for the system to free used pages first. The actual freeing of pages is done by kswapd, a kernel daemon. -min_free_pages -- when the number of free pages reaches this - level, only the kernel can allocate memory - for _critical_ tasks only -free_pages_low -- when the number of free pages drops below - this level, kswapd is woken up immediately -free_pages_high -- this is kswapd's target, when more than - free_pages_high pages are free, kswapd will - stop swapping. +min -- when the number of free pages reaches this + level, only the kernel can allocate memory + for _critical_ tasks only +low -- when the number of free pages drops below + this level, kswapd is woken up immediately +high -- this is kswapd's target, when more than <high> + pages are free, kswapd will stop swapping. -When the number of free pages is between free_pages_low and -free_pages_high, and kswapd hasn't run for swapout_interval -jiffies, then kswapd is woken up too. See swapout_interval -for more info. +When the number of free pages is between low and high, +and kswapd hasn't run for swapout_interval jiffies, then +kswapd is woken up too. See swapout_interval for more info. When free memory is always low on your system, and kswapd has trouble keeping up with allocations, you might want to -increase these values, especially free_pages_high and perhaps -free_pages_low. I've found that a 1:2:4 relation for these -values tend to work rather well in a heavily loaded system. +increase these values, especially high and perhaps low. +I've found that a 1:2:4 relation for these values tend to work +rather well in a heavily loaded system. ============================================================== @@ -163,9 +178,7 @@ static inline int vm_enough_memory(long pages) swapctl: -This file contains no less than 16 variables, of which about -half is actually used :-) In the listing below, the unused -variables are marked as such. +This file contains no less than 8 variables. All of these values are used by kswapd, and the usage can be found in linux/mm/vmscan.c. @@ -177,18 +190,10 @@ typedef struct swap_control_v5 unsigned int sc_page_advance; unsigned int sc_page_decline; unsigned int sc_page_initial_age; - unsigned int sc_max_buff_age; /* unused */ - unsigned int sc_buff_advance; /* unused */ - unsigned int sc_buff_decline; /* unused */ - unsigned int sc_buff_initial_age; /* unused */ unsigned int sc_age_cluster_fract; unsigned int sc_age_cluster_min; unsigned int sc_pageout_weight; unsigned int sc_bufferout_weight; - unsigned int sc_buffer_grace; /* unused */ - unsigned int sc_nr_buffs_to_free; /* unused */ - unsigned int sc_nr_pages_to_free; /* unused */ - enum RCL_POLICY sc_policy; /* RCL_PERSIST hardcoded */ } swap_control_v5; -------------------------------------------------------------- @@ -207,9 +212,8 @@ is adjusted according to the following scheme: (default 1) And when a page reaches age 0, it's ready to be swapped out. -The variables sc_age_cluster_fract till sc_bufferout_weight -have to do with the amount of scanning kswapd is doing on -each call to try_to_swap_out(). +The next four variables can be used to control kswapd's +agressiveness in swapping out pages. sc_age_cluster_fract is used to calculate how many pages from a process are to be scanned by kswapd. The formula used is @@ -221,9 +225,12 @@ also scan small processes. The values of sc_pageout_weight and sc_bufferout_weight are used to control the how many tries kswapd will do in order -to swapout one page / buffer. As with sc_age_cluster_fract, -the actual value is calculated by several more or less complex -formulae and the default value is good for every purpose. +to swapout one page / buffer. These values can be used to +finetune the ratio between user pages and buffer/cache memory. +When you find that your Linux system is swapping out too much +process pages in order to satisfy buffer memory demands, you +might want to either increase sc_bufferout_weight, or decrease +the value of sc_pageout_weight. ============================================================== diff --git a/MAINTAINERS b/MAINTAINERS index 81bf2aeb4..b8198cbf3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -275,8 +275,8 @@ M: oe1kib@oe1xtu.ampr.org L: linux-hams@vger.rutgers.edu S: Maintained -HP100: Driver for HP 10/100 Mbit/s Network Adapter Series -P: Jarsolav Kysela +HP100: Driver for HP 10/100 Mbit/s Voice Grade Network Adapter Series +P: Jaroslav Kysela M: perex@jcu.cz S: Maintained @@ -604,6 +604,12 @@ P: Jean Tourrilhes M: jt@hplb.hpl.hp.com S: Maintained +WD7000 SCSI DRIVER +P: Miroslav Zagorac +M: zaga@fly.cc.fer.hr +L: linux-scsi@vger.rutgers.edu +S: Maintained + Z8530 DRIVER FOR AX.25 P: Joerg Reuter M: jreuter@poboxes.com @@ -1,6 +1,6 @@ VERSION = 2 PATCHLEVEL = 1 -SUBLEVEL = 89 +SUBLEVEL = 90 ARCH = mips @@ -9,7 +9,7 @@ ARCH = mips # because it makes re-config very ugly and too many fundamental files depend # on "CONFIG_SMP" # -# NOTE! SMP is experimental. See the file Documentation/SMP.txt +# For UP operations COMMENT THIS OUT, simply setting SMP = 0 won't work # # SMP = 1 # @@ -270,6 +270,7 @@ include/linux/compile.h: $(CONFIGURATION) include/linux/version.h newversion include/linux/version.h: ./Makefile @echo \#define UTS_RELEASE \"$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)\" > .ver @echo \#define LINUX_VERSION_CODE `expr $(VERSION) \\* 65536 + $(PATCHLEVEL) \\* 256 + $(SUBLEVEL)` >> .ver + @echo '#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))' >>.ver @mv -f .ver $@ init/version.o: init/version.c include/linux/compile.h diff --git a/arch/alpha/config.in b/arch/alpha/config.in index bda9ca130..1fefe0c62 100644 --- a/arch/alpha/config.in +++ b/arch/alpha/config.in @@ -23,7 +23,7 @@ bool 'Enable loadable module support' CONFIG_MODULES if [ "$CONFIG_MODULES" = "y" ]; then MODULES=y bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS - bool 'Kernel daemon support (e.g. autoload of modules)' CONFIG_KERNELD + bool 'Kernel module loader' CONFIG_KMOD fi endmenu diff --git a/arch/alpha/defconfig b/arch/alpha/defconfig index 8c1a4f696..1bdecf444 100644 --- a/arch/alpha/defconfig +++ b/arch/alpha/defconfig @@ -12,7 +12,7 @@ # CONFIG_MODULES=y # CONFIG_MODVERSIONS is not set -# CONFIG_KERNELD is not set +# CONFIG_KMOD is not set # # General setup diff --git a/arch/alpha/kernel/alpha_ksyms.c b/arch/alpha/kernel/alpha_ksyms.c index 53741095c..132175541 100644 --- a/arch/alpha/kernel/alpha_ksyms.c +++ b/arch/alpha/kernel/alpha_ksyms.c @@ -13,6 +13,7 @@ #include <linux/socket.h> #include <linux/in.h> #include <linux/in6.h> +#include <linux/pci.h> #include <asm/io.h> #include <asm/hwrpb.h> @@ -146,3 +147,7 @@ EXPORT_SYMBOL_NOVERS(__remq); EXPORT_SYMBOL_NOVERS(__remqu); EXPORT_SYMBOL_NOVERS(memcpy); EXPORT_SYMBOL_NOVERS(memset); + +#if CONFIG_PCI +EXPORT_SYMBOL(pci_devices); +#endif diff --git a/arch/alpha/kernel/process.c b/arch/alpha/kernel/process.c index 6a90ecb02..f8146c54f 100644 --- a/arch/alpha/kernel/process.c +++ b/arch/alpha/kernel/process.c @@ -39,6 +39,7 @@ #include <asm/io.h> #include <asm/pgtable.h> #include <asm/hwrpb.h> +#include <asm/fpu.h> /* * Initial task structure. Make this a per-architecture thing, @@ -50,6 +51,7 @@ unsigned long init_user_stack[1024] = { STACK_MAGIC, }; static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; +static struct file * init_fd_array[NR_OPEN] = { NULL, }; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; struct mm_struct init_mm = INIT_MM; @@ -198,6 +200,10 @@ void exit_thread(void) void flush_thread(void) { + /* Arrange for each exec'ed process to start off with a + clean slate wrt the fpu. */ + current->tss.flags &= ~IEEE_SW_MASK; + wrfpcr(FPCR_DYN_NORMAL); } void release_thread(struct task_struct *dead_task) diff --git a/arch/arm/config.in b/arch/arm/config.in index 2b5303196..b95c2f16e 100644 --- a/arch/arm/config.in +++ b/arch/arm/config.in @@ -16,7 +16,7 @@ comment 'Loadable module support' bool 'Enable loadable module support' CONFIG_MODULES if [ "$CONFIG_MODULES" = "y" ]; then bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS - bool 'Kernel daemon support (e.g. autoload of modules)' CONFIG_KERNELD + bool 'Kernel module loader' CONFIG_KMOD fi endmenu diff --git a/arch/arm/defconfig b/arch/arm/defconfig index c3a14ebcd..48358557b 100644 --- a/arch/arm/defconfig +++ b/arch/arm/defconfig @@ -13,7 +13,7 @@ CONFIG_EXPERIMENTAL=y # CONFIG_MODULES=y CONFIG_MODVERSIONS=y -CONFIG_KERNELD=y +CONFIG_KMOD=y # # General setup diff --git a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c index 19666ac1e..20c62e2e6 100644 --- a/arch/arm/kernel/armksyms.c +++ b/arch/arm/kernel/armksyms.c @@ -4,6 +4,7 @@ #include <linux/string.h> #include <linux/mm.h> #include <linux/mman.h> +#include <linux/pci.h> #include <asm/ecard.h> #include <asm/io.h> @@ -176,3 +177,7 @@ EXPORT_SYMBOL(change_bit); EXPORT_SYMBOL(test_and_change_bit); EXPORT_SYMBOL(find_first_zero_bit); EXPORT_SYMBOL(find_next_zero_bit); + +#if CONFIG_PCI +EXPORT_SYMBOL(pci_devices); +#endif diff --git a/arch/arm/kernel/init_task.c b/arch/arm/kernel/init_task.c index acc206942..90ae6952f 100644 --- a/arch/arm/kernel/init_task.c +++ b/arch/arm/kernel/init_task.c @@ -6,6 +6,7 @@ static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; +static struct files * init_fd_array[NR_OPEN] = { NULL, }; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; struct mm_struct init_mm = INIT_MM; diff --git a/arch/i386/.kernel_offset.lds b/arch/i386/.kernel_offset.lds new file mode 100644 index 000000000..9cfd927bf --- /dev/null +++ b/arch/i386/.kernel_offset.lds @@ -0,0 +1 @@ +__kernel_offset__ = (0x1000-1024)*1024*1024; diff --git a/arch/i386/Makefile b/arch/i386/Makefile index 98bde850f..359c20b9b 100644 --- a/arch/i386/Makefile +++ b/arch/i386/Makefile @@ -58,6 +58,9 @@ SUBDIRS := $(SUBDIRS) arch/i386/math-emu DRIVERS := $(DRIVERS) arch/i386/math-emu/math.a endif +memsize: dummy + @echo "__kernel_offset__ = (0x1000-$(CONFIG_MAX_MEMSIZE))*1024*1024;" > arch/i386/.kernel_offset.lds + arch/i386/kernel: dummy $(MAKE) linuxsubdirs SUBDIRS=arch/i386/kernel @@ -66,27 +69,27 @@ arch/i386/mm: dummy MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot -zImage: vmlinux +zImage: memsize vmlinux @$(MAKEBOOT) zImage -bzImage: vmlinux +bzImage: memsize vmlinux @$(MAKEBOOT) bzImage compressed: zImage -zlilo: vmlinux +zlilo: memsize vmlinux @$(MAKEBOOT) BOOTIMAGE=zImage zlilo -bzlilo: vmlinux +bzlilo: memsize vmlinux @$(MAKEBOOT) BOOTIMAGE=bzImage zlilo -zdisk: vmlinux +zdisk: memsize vmlinux @$(MAKEBOOT) BOOTIMAGE=zImage zdisk -bzdisk: vmlinux +bzdisk: memsize vmlinux @$(MAKEBOOT) BOOTIMAGE=bzImage zdisk -install: vmlinux +install: memsize vmlinux @$(MAKEBOOT) BOOTIMAGE=bzImage install archclean: diff --git a/arch/i386/config.in b/arch/i386/config.in index 3e52c2218..058f908e3 100644 --- a/arch/i386/config.in +++ b/arch/i386/config.in @@ -17,6 +17,7 @@ choice 'Processor family' \ Pentium/K5/5x86/6x86 CONFIG_M586 \ PPro/K6/6x86MX CONFIG_M686" Pentium bool 'Math emulation' CONFIG_MATH_EMULATION +int ' Max physical memory in MB' CONFIG_MAX_MEMSIZE 1024 endmenu mainmenu_option next_comment @@ -24,7 +25,7 @@ comment 'Loadable module support' bool 'Enable loadable module support' CONFIG_MODULES if [ "$CONFIG_MODULES" = "y" ]; then bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS - bool 'Kernel daemon support (e.g. autoload of modules)' CONFIG_KERNELD + bool 'Kernel module loader' CONFIG_KMOD fi endmenu diff --git a/arch/i386/defconfig b/arch/i386/defconfig index 2ca6da6bc..f3792ce05 100644 --- a/arch/i386/defconfig +++ b/arch/i386/defconfig @@ -15,13 +15,14 @@ CONFIG_M586=y # CONFIG_M686 is not set # CONFIG_MATH_EMULATION is not set +CONFIG_MAX_MEMSIZE=1024 # # Loadable module support # CONFIG_MODULES=y # CONFIG_MODVERSIONS is not set -# CONFIG_KERNELD is not set +# CONFIG_KMOD is not set # # General setup @@ -30,7 +31,6 @@ CONFIG_NET=y CONFIG_PCI=y CONFIG_PCI_BIOS=y CONFIG_PCI_DIRECT=y -# CONFIG_PCI_OPTIMIZE is not set CONFIG_PCI_OLD_PROC=y # CONFIG_MCA is not set CONFIG_SYSVIPC=y @@ -94,7 +94,6 @@ CONFIG_INET=y # CONFIG_IP_ADVANCED_ROUTER is not set # CONFIG_IP_PNP is not set # CONFIG_IP_ACCT is not set -# CONFIG_IP_MASQUERADE is not set # CONFIG_IP_ROUTER is not set # CONFIG_NET_IPIP is not set # CONFIG_NET_IPGRE is not set diff --git a/arch/i386/kernel/head.S b/arch/i386/kernel/head.S index 6722c4f7f..048921838 100644 --- a/arch/i386/kernel/head.S +++ b/arch/i386/kernel/head.S @@ -11,6 +11,9 @@ #include <linux/tasks.h> #include <linux/linkage.h> #include <asm/segment.h> +#include <asm/page.h> +#include <asm/pgtable.h> + #define CL_MAGIC_ADDR 0x90020 #define CL_MAGIC 0xA33F @@ -54,9 +57,9 @@ startup_32: * be using the global pages. * * NOTE! We have to correct for the fact that we're - * not yet offset 0xC0000000.. + * not yet offset PAGE_OFFSET.. */ -#define cr4_bits mmu_cr4_features-0xC0000000 +#define cr4_bits mmu_cr4_features-__PAGE_OFFSET movl %cr4,%eax # Turn on 4Mb pages orl cr4_bits,%eax movl %eax,%cr4 @@ -368,7 +371,7 @@ check_x87: * sets up a idt with 256 entries pointing to * ignore_int, interrupt gates. It doesn't actually load * idt - that can be done only after paging has been enabled - * and the kernel moved to 0xC0000000. Interrupts + * and the kernel moved to PAGE_OFFSET. Interrupts * are enabled elsewhere, when we can be relatively * sure everything is ok. */ @@ -444,14 +447,16 @@ SYMBOL_NAME(gdt): /* * This is initialized to create a identity-mapping at 0-4M (for bootup * purposes) and another mapping of the 0-4M area at virtual address - * 0xC0000000. + * PAGE_OFFSET. */ .org 0x1000 ENTRY(swapper_pg_dir) .long 0x00102007 - .fill 767,4,0 + .fill __USER_PGD_PTRS-1,4,0 + /* default: 767 entries */ .long 0x00102007 - .fill 255,4,0 + /* default: 255 entries */ + .fill __KERNEL_PGD_PTRS-1,4,0 /* * The page tables are initialized to only 4MB here - the final page @@ -614,8 +619,8 @@ ENTRY(idt_table) .fill 256,8,0 # idt is uninitialized /* - * This gdt setup gives the kernel a 1GB address space at virtual - * address 0xC0000000 - space enough for expansion, I hope. + * This gdt setup gives the kernel a CONFIG_MAX_MEMSIZE sized address space at + * virtual address PAGE_OFFSET. * * This contains up to 8192 quadwords depending on NR_TASKS - 64kB of * gdt entries. Ugh. diff --git a/arch/i386/kernel/i386_ksyms.c b/arch/i386/kernel/i386_ksyms.c index e5812400e..d2837d648 100644 --- a/arch/i386/kernel/i386_ksyms.c +++ b/arch/i386/kernel/i386_ksyms.c @@ -8,6 +8,7 @@ #include <linux/in6.h> #include <linux/interrupt.h> #include <linux/smp_lock.h> +#include <linux/pci.h> #include <asm/semaphore.h> #include <asm/processor.h> @@ -97,3 +98,6 @@ EXPORT_SYMBOL(mca_isenabled); EXPORT_SYMBOL(mca_isadapter); #endif +#if CONFIG_PCI +EXPORT_SYMBOL(pci_devices); +#endif diff --git a/arch/i386/kernel/init_task.c b/arch/i386/kernel/init_task.c index acc206942..c0571c769 100644 --- a/arch/i386/kernel/init_task.c +++ b/arch/i386/kernel/init_task.c @@ -6,6 +6,7 @@ static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; +static struct file * init_fd_array[NR_OPEN] = { NULL, }; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; struct mm_struct init_mm = INIT_MM; diff --git a/arch/i386/kernel/io_apic.c b/arch/i386/kernel/io_apic.c index be074cac6..219e7f853 100644 --- a/arch/i386/kernel/io_apic.c +++ b/arch/i386/kernel/io_apic.c @@ -271,7 +271,7 @@ void setup_IO_APIC_irqs (void) if (!IO_APIC_IRQ(irq)) continue; - entry.vector = IO_APIC_GATE_OFFSET + (irq<<3); + entry.vector = IO_APIC_VECTOR(irq); /* * Determine IRQ line polarity (high active or low active): @@ -383,7 +383,7 @@ void setup_IO_APIC_irq_ISA_default (unsigned int irq) entry.mask = 1; /* unmask IRQ now */ entry.dest.logical.logical_dest = 0xff; /* all CPUs */ - entry.vector = IO_APIC_GATE_OFFSET + (irq<<3); + entry.vector = IO_APIC_VECTOR(irq); entry.polarity=0; entry.trigger=0; @@ -513,7 +513,7 @@ void print_IO_APIC (void) return; } -void init_sym_mode (void) +static void init_sym_mode (void) { printk("enabling Symmetric IO mode ... "); outb (0x70, 0x22); @@ -521,6 +521,14 @@ void init_sym_mode (void) printk("...done.\n"); } +void init_pic_mode (void) +{ + printk("disabling Symmetric IO mode ... "); + outb (0x70, 0x22); + outb (0x00, 0x23); + printk("...done.\n"); +} + char ioapic_OEM_ID [16]; char ioapic_Product_ID [16]; diff --git a/arch/i386/kernel/ioport.c b/arch/i386/kernel/ioport.c index 9bb150075..44fd26530 100644 --- a/arch/i386/kernel/ioport.c +++ b/arch/i386/kernel/ioport.c @@ -13,6 +13,7 @@ #include <linux/mm.h> #include <linux/smp.h> #include <linux/smp_lock.h> +#include <linux/stddef.h> /* Set EXTENT bits starting at BASE in BITMAP to value TURN_ON. */ static void set_bitmap(unsigned long *bitmap, short base, short extent, int new_value) @@ -53,12 +54,25 @@ static void set_bitmap(unsigned long *bitmap, short base, short extent, int new_ */ asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int turn_on) { + struct thread_struct * t = ¤t->tss; + if ((from + num <= from) || (from + num > IO_BITMAP_SIZE*32)) return -EINVAL; if (!suser()) return -EPERM; + /* + * If it's the first ioperm() call in this thread's lifetime, set the + * IO bitmap up. ioperm() is much less timing critical than clone(), + * this is why we delay this operation until now: + */ +#define IO_BITMAP_OFFSET offsetof(struct thread_struct,io_bitmap) + + if (t->bitmap != IO_BITMAP_OFFSET) { + t->bitmap = IO_BITMAP_OFFSET; + memset(t->io_bitmap,0xff,(IO_BITMAP_SIZE+1)*4); + } - set_bitmap((unsigned long *)current->tss.io_bitmap, from, num, !turn_on); + set_bitmap((unsigned long *)t->io_bitmap, from, num, !turn_on); return 0; } diff --git a/arch/i386/kernel/irq.c b/arch/i386/kernel/irq.c index 24c33be65..4823c1546 100644 --- a/arch/i386/kernel/irq.c +++ b/arch/i386/kernel/irq.c @@ -70,6 +70,7 @@ spinlock_t irq_controller_lock; static unsigned int irq_events [NR_IRQS] = { -1, }; static int disabled_irq [NR_IRQS] = { 0, }; +static int ipi_pending [NR_IRQS] = { 0, }; /* * Not all IRQs can be routed through the IO-APIC, eg. on certain (older) @@ -489,12 +490,8 @@ static inline void wait_on_irq(int cpu) */ void synchronize_bh(void) { - if (atomic_read(&global_bh_count)) { - int cpu = smp_processor_id(); - if (!local_irq_count[cpu] && !local_bh_count[cpu]) { + if (atomic_read(&global_bh_count) && !in_interrupt()) wait_on_bh(); - } - } } /* @@ -672,8 +669,8 @@ void enable_8259A_irq (unsigned int irq) #ifdef __SMP__ void enable_ioapic_irq (unsigned int irq) { - unsigned long flags; - int cpu = smp_processor_id(), should_handle_irq; + unsigned long flags, should_handle_irq; + int cpu = smp_processor_id(); spin_lock_irqsave(&irq_controller_lock, flags); if (disabled_irq[irq]) @@ -682,18 +679,32 @@ void enable_ioapic_irq (unsigned int irq) spin_unlock_irqrestore(&irq_controller_lock, flags); return; } +#if 0 /* * In the SMP+IOAPIC case it might happen that there are an unspecified - * number of pending IRQ events unhandled. We protect against multiple - * enable_irq()'s executing them via disable_irq[irq]++ + * number of pending IRQ events unhandled. These cases are very rare, + * so we 'resend' these IRQs via IPIs, to the same CPU. It's much + * better to do it this way as thus we dont have to be aware of + * 'pending' interrupts in the IRQ path, except at this point. */ if (!disabled_irq[irq] && irq_events[irq]) { + if (!ipi_pending[irq]) { + ipi_pending[irq] = 1; + --irq_events[irq]; + send_IPI(cpu,IO_APIC_VECTOR(irq)); + } + } + spin_unlock_irqrestore(&irq_controller_lock, flags); +#else + if (!disabled_irq[irq] && irq_events[irq]) { struct pt_regs regs; /* FIXME: these are fake currently */ disabled_irq[irq]++; + hardirq_enter(cpu); spin_unlock(&irq_controller_lock); + release_irqlock(cpu); - irq_enter(cpu, irq); + while (test_bit(0,&global_irq_lock)) mb(); again: handle_IRQ_event(irq, ®s); @@ -713,6 +724,7 @@ again: __restore_flags(flags); } else spin_unlock_irqrestore(&irq_controller_lock, flags); +#endif } #endif @@ -775,15 +787,16 @@ static void do_ioapic_IRQ(unsigned int irq, int cpu, struct pt_regs * regs) ack_APIC_irq(); spin_lock(&irq_controller_lock); + if (ipi_pending[irq]) + ipi_pending[irq] = 0; if (!irq_events[irq]++ && !disabled_irq[irq]) should_handle_irq = 1; - + hardirq_enter(cpu); spin_unlock(&irq_controller_lock); - irq_enter(cpu, irq); - if (should_handle_irq) { + while (test_bit(0,&global_irq_lock)) mb(); again: handle_IRQ_event(irq, regs); @@ -797,7 +810,8 @@ again: goto again; } - irq_exit(cpu, irq); + hardirq_exit(cpu); + release_irqlock(cpu); } #endif @@ -1034,7 +1048,7 @@ void init_IO_APIC_traps(void) * 0x80, because int 0x80 is hm, kindof importantish ;) */ for (i = 0; i < NR_IRQS ; i++) - if (IO_APIC_GATE_OFFSET+(i<<3) <= 0xfe) /* HACK */ { + if (IO_APIC_VECTOR(i) <= 0xfe) /* HACK */ { if (IO_APIC_IRQ(i)) { irq_handles[i] = &ioapic_irq_type; /* @@ -1071,8 +1085,8 @@ __initfunc(void init_IRQ(void)) #ifdef __SMP__ for (i = 0; i < NR_IRQS ; i++) - if (IO_APIC_GATE_OFFSET+(i<<3) <= 0xfe) /* hack -- mingo */ - set_intr_gate(IO_APIC_GATE_OFFSET+(i<<3),interrupt[i]); + if (IO_APIC_VECTOR(i) <= 0xfe) /* hack -- mingo */ + set_intr_gate(IO_APIC_VECTOR(i),interrupt[i]); /* * The reschedule interrupt slowly changes it's functionality, diff --git a/arch/i386/kernel/irq.h b/arch/i386/kernel/irq.h index 6404bc9e3..9824026dc 100644 --- a/arch/i386/kernel/irq.h +++ b/arch/i386/kernel/irq.h @@ -2,13 +2,12 @@ #define __irq_h /* - * Various low-level irq details needed by irq.c and smp.c + * Various low-level irq details needed by irq.c, process.c, + * time.c, io_apic.c and smp.c * * Interrupt entry/exit code at both C and assembly level */ -#define IO_APIC_GATE_OFFSET 0x51 - void mask_irq(unsigned int irq); void unmask_irq(unsigned int irq); void enable_IO_APIC_irq (unsigned int irq); @@ -19,9 +18,16 @@ void setup_IO_APIC (void); void init_IO_APIC_traps(void); int IO_APIC_get_PCI_irq_vector (int bus, int slot, int fn); void make_8259A_irq (unsigned int irq); +void send_IPI (int dest, int vector); +void init_pic_mode (void); extern unsigned int io_apic_irqs; +extern inline int IO_APIC_VECTOR (int irq) +{ + return (0x51+(irq<<3)); +} + #define MAX_IRQ_SOURCES 128 #define MAX_MP_BUSSES 32 enum mp_bustype { @@ -38,6 +44,7 @@ extern spinlock_t irq_controller_lock; /* * IO-APIC */ + #ifdef __SMP__ #include <asm/atomic.h> diff --git a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c index 629e7ef12..6f2245790 100644 --- a/arch/i386/kernel/process.c +++ b/arch/i386/kernel/process.c @@ -44,6 +44,7 @@ #ifdef CONFIG_MATH_EMULATION #include <asm/math_emu.h> #endif +#include "irq.h" #ifdef __SMP__ asmlinkage void ret_from_smpfork(void) __asm__("ret_from_smpfork"); @@ -280,6 +281,12 @@ static inline void kb_wait(void) void machine_restart(char * __unused) { +#if __SMP__ + /* + * turn off the IO-APIC, so we can do a clean reboot + */ + init_pic_mode(); +#endif if(!reboot_thru_bios) { /* rebooting needs to touch the page at absolute addr 0 */ @@ -314,10 +321,10 @@ void machine_restart(char * __unused) /* Remap the kernel at virtual address zero, as well as offset zero from the kernel segment. This assumes the kernel segment starts at - virtual address 0xc0000000. */ + virtual address PAGE_OFFSET. */ - memcpy (swapper_pg_dir, swapper_pg_dir + 768, - sizeof (swapper_pg_dir [0]) * 256); + memcpy (swapper_pg_dir, swapper_pg_dir + USER_PGD_PTRS, + sizeof (swapper_pg_dir [0]) * KERNEL_PGD_PTRS); /* Make sure the first page is mapped to the start of physical memory. It is normally not mapped, to trap kernel NULL pointer dereferences. */ @@ -473,7 +480,6 @@ void release_thread(struct task_struct *dead_task) int copy_thread(int nr, unsigned long clone_flags, unsigned long esp, struct task_struct * p, struct pt_regs * regs) { - int i; struct pt_regs * childregs; p->tss.tr = _TSS(nr); @@ -510,9 +516,13 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long esp, set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,p->ldt, 512); else set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&default_ldt, 1); - p->tss.bitmap = offsetof(struct thread_struct,io_bitmap); - for (i = 0; i < IO_BITMAP_SIZE+1 ; i++) /* IO bitmap is actually SIZE+1 */ - p->tss.io_bitmap[i] = ~0; + /* + * a bitmap offset pointing outside of the TSS limit causes a nicely + * controllable SIGSEGV. The first sys_ioperm() call sets up the + * bitmap properly. + */ + p->tss.bitmap = sizeof(struct thread_struct); + if (last_task_used_math == current) __asm__("clts ; fnsave %0 ; frstor %0":"=m" (p->tss.i387)); diff --git a/arch/i386/kernel/ptrace.c b/arch/i386/kernel/ptrace.c index ca2147ee7..d05b54b63 100644 --- a/arch/i386/kernel/ptrace.c +++ b/arch/i386/kernel/ptrace.c @@ -371,7 +371,10 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) if (pid == 1) /* you may not mess with init */ goto out; ret = -ESRCH; - if (!(child = find_task_by_pid(pid))) + read_lock(&tasklist_lock); + child = find_task_by_pid(pid); + read_unlock(&tasklist_lock); /* FIXME!!! */ + if (!child) goto out; ret = -EPERM; if (request == PTRACE_ATTACH) { @@ -390,9 +393,13 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) goto out; child->flags |= PF_PTRACED; if (child->p_pptr != current) { + unsigned long flags; + + write_lock_irqsave(&tasklist_lock, flags); REMOVE_LINKS(child); child->p_pptr = current; SET_LINKS(child); + write_unlock_irqrestore(&tasklist_lock, flags); } send_sig(SIGSTOP, child, 1); ret = 0; @@ -545,6 +552,7 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) } case PTRACE_DETACH: { /* detach a process that was attached. */ + unsigned long flags; long tmp; ret = -EIO; @@ -553,9 +561,11 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) child->flags &= ~(PF_PTRACED|PF_TRACESYS); wake_up_process(child); child->exit_code = data; + write_lock_irqsave(&tasklist_lock, flags); REMOVE_LINKS(child); child->p_pptr = child->p_opptr; SET_LINKS(child); + write_unlock_irqrestore(&tasklist_lock, flags); /* make sure the single step bit is not set. */ tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG; put_stack_long(child, EFL_OFFSET, tmp); diff --git a/arch/i386/kernel/smp.c b/arch/i386/kernel/smp.c index 6f4bc60ec..9ca377128 100644 --- a/arch/i386/kernel/smp.c +++ b/arch/i386/kernel/smp.c @@ -1108,23 +1108,59 @@ __initfunc(void smp_boot_cpus(void)) setup_IO_APIC(); } + +void send_IPI (int dest, int vector) +{ + unsigned long cfg; + unsigned long flags; + + __save_flags(flags); + __cli(); + + /* + * prepare target chip field + */ + + cfg = apic_read(APIC_ICR2) & 0x00FFFFFF; + apic_write(APIC_ICR2, cfg|SET_APIC_DEST_FIELD(dest)); + + cfg = apic_read(APIC_ICR); + cfg &= ~0xFDFFF; + cfg |= APIC_DEST_FIELD|APIC_DEST_DM_FIXED|vector; + cfg |= dest; + + /* + * Send the IPI. The write to APIC_ICR fires this off. + */ + + apic_write(APIC_ICR, cfg); + __restore_flags(flags); +} + +void funny (void) +{ + send_IPI(APIC_DEST_ALLBUT,0x30 /*IO_APIC_VECTOR(11)*/); + for(;;)__cli(); +} + /* - * A non wait message cannot pass data or cpu source info. This current setup - * is only safe because the kernel lock owner is the only person who can send a message. + * A non wait message cannot pass data or cpu source info. This current setup + * is only safe because the kernel lock owner is the only person who can send + * a message. * - * Wrapping this whole block in a spinlock is not the safe answer either. A processor may - * get stuck with irq's off waiting to send a message and thus not replying to the person - * spinning for a reply.... + * Wrapping this whole block in a spinlock is not the safe answer either. A + * processor may get stuck with irq's off waiting to send a message and thus + * not replying to the person spinning for a reply.... * - * In the end flush tlb ought to be the NMI and a very very short function (to avoid the old - * IDE disk problems), and other messages sent with IRQ's enabled in a civilised fashion. That - * will also boost performance. + * In the end flush tlb ought to be the NMI and a very very short function + * (to avoid the old IDE disk problems), and other messages sent with IRQ's + * enabled in a civilised fashion. That will also boost performance. */ void smp_message_pass(int target, int msg, unsigned long data, int wait) { - unsigned long flags; unsigned long cfg; + unsigned long dest = 0; unsigned long target_map; int p=smp_processor_id(); int irq; @@ -1166,11 +1202,11 @@ void smp_message_pass(int target, int msg, unsigned long data, int wait) } /* - * Sanity check we don't re-enter this across CPU's. Only the kernel - * lock holder may send messages. For a STOP_CPU we are bringing the - * entire box to the fastest halt we can.. A reschedule carries - * no data and can occur during a flush.. guess what panic - * I got to notice this bug... + * Sanity check we don't re-enter this across CPU's. Only the kernel + * lock holder may send messages. For a STOP_CPU we are bringing the + * entire box to the fastest halt we can.. A reschedule carries + * no data and can occur during a flush.. guess what panic + * I got to notice this bug... */ /* @@ -1183,11 +1219,11 @@ void smp_message_pass(int target, int msg, unsigned long data, int wait) p, msg, target);*/ /* - * Wait for the APIC to become ready - this should never occur. Its - * a debugging check really. + * Wait for the APIC to become ready - this should never occur. Its + * a debugging check really. */ - while(ct<1000) + while (ct<1000) { cfg=apic_read(APIC_ICR); if(!(cfg&(1<<12))) @@ -1204,49 +1240,32 @@ void smp_message_pass(int target, int msg, unsigned long data, int wait) printk("CPU #%d: previous IPI still not cleared after 10mS\n", p); /* - * Program the APIC to deliver the IPI - */ - - __save_flags(flags); - __cli(); - cfg=apic_read(APIC_ICR2); - cfg&=0x00FFFFFF; - apic_write(APIC_ICR2, cfg|SET_APIC_DEST_FIELD(target)); /* Target chip */ - cfg=apic_read(APIC_ICR); - cfg&=~0xFDFFF; /* Clear bits */ - cfg|=APIC_DEST_FIELD|APIC_DEST_DM_FIXED|irq; /* Send an IRQ 13 */ - - /* * Set the target requirement */ if(target==MSG_ALL_BUT_SELF) { - cfg|=APIC_DEST_ALLBUT; + dest=APIC_DEST_ALLBUT; target_map=cpu_present_map; cpu_callin_map[0]=(1<<p); } else if(target==MSG_ALL) { - cfg|=APIC_DEST_ALLINC; + dest=APIC_DEST_ALLINC; target_map=cpu_present_map; cpu_callin_map[0]=0; } else - { - target_map=(1<<target); - cpu_callin_map[0]=0; - } + panic("huh?"); /* - * Send the IPI. The write to APIC_ICR fires this off. + * Program the APIC to deliver the IPI */ - - apic_write(APIC_ICR, cfg); - __restore_flags(flags); + + send_IPI(dest,irq); /* - * Spin waiting for completion + * Spin waiting for completion */ switch(wait) @@ -1443,6 +1462,7 @@ asmlinkage void smp_reschedule_interrupt(void) int cpu = smp_processor_id(); ack_APIC_irq(); + for (;;) __cli(); /* * This looks silly, but we actually do need to wait * for the global interrupt lock. @@ -1694,8 +1714,10 @@ __initfunc(void setup_APIC_clock (void)) /* * We ACK the APIC, just in case there is something pending. */ + ack_APIC_irq (); + restore_flags(flags); } diff --git a/arch/i386/kernel/trampoline.S b/arch/i386/kernel/trampoline.S index 9cee704f1..1f5303a9e 100644 --- a/arch/i386/kernel/trampoline.S +++ b/arch/i386/kernel/trampoline.S @@ -28,6 +28,7 @@ #include <linux/linkage.h> #include <asm/segment.h> +#include <asm/page.h> .data @@ -62,7 +63,7 @@ idt_48: gdt_48: .word 0x0800 # gdt limit = 2048, 256 GDT entries - .long gdt_table-0xc0000000 # gdt base = gdt (first SMP CPU) + .long gdt_table-__PAGE_OFFSET # gdt base = gdt (first SMP CPU) .globl SYMBOL_NAME(trampoline_end) SYMBOL_NAME_LABEL(trampoline_end) diff --git a/arch/i386/mm/init.c b/arch/i386/mm/init.c index f9172bdae..ef3ac57f4 100644 --- a/arch/i386/mm/init.c +++ b/arch/i386/mm/init.c @@ -204,8 +204,8 @@ __initfunc(unsigned long paging_init(unsigned long start_mem, unsigned long end_ /* unmap the original low memory mappings */ pgd_val(pg_dir[0]) = 0; - /* Map whole memory from 0xC0000000 */ - pg_dir += 768; + /* Map whole memory from PAGE_OFFSET */ + pg_dir += USER_PGD_PTRS; while (address < end_mem) { /* * If we're running on a Pentium CPU, we can use the 4MB diff --git a/arch/i386/vmlinux.lds b/arch/i386/vmlinux.lds index 7a1fd3d08..0284015ad 100644 --- a/arch/i386/vmlinux.lds +++ b/arch/i386/vmlinux.lds @@ -1,12 +1,13 @@ /* ld script to make i386 Linux kernel * Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz> */ +INCLUDE arch/i386/.kernel_offset.lds OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") OUTPUT_ARCH(i386) ENTRY(_start) SECTIONS { - . = 0xc0100000; + . = __kernel_offset__ + 0x100000; _text = .; /* Text and read-only data */ .text : { *(.text) diff --git a/arch/m68k/amiga/amiga_ksyms.c b/arch/m68k/amiga/amiga_ksyms.c index bc962f7d3..0eae9cfdb 100644 --- a/arch/m68k/amiga/amiga_ksyms.c +++ b/arch/m68k/amiga/amiga_ksyms.c @@ -2,6 +2,7 @@ #include <linux/types.h> #include <linux/zorro.h> #include <asm/amigahw.h> +#include <linux/pci.h> extern volatile u_short amiga_audio_min_period; extern u_short amiga_audio_period; @@ -24,3 +25,7 @@ EXPORT_SYMBOL(zorro_get_board); EXPORT_SYMBOL(zorro_config_board); EXPORT_SYMBOL(zorro_unconfig_board); EXPORT_SYMBOL(zorro_unused_z2ram); + +#if CONFIG_PCI +EXPORT_SYMBOL(pci_devices); +#endif diff --git a/arch/m68k/atari/atari_ksyms.c b/arch/m68k/atari/atari_ksyms.c index 0eb19e4a0..86e34fb7f 100644 --- a/arch/m68k/atari/atari_ksyms.c +++ b/arch/m68k/atari/atari_ksyms.c @@ -1,4 +1,6 @@ #include <linux/module.h> +#include <linux/pci.h> + #include <asm/ptrace.h> #include <asm/traps.h> #include <asm/atarihw.h> @@ -40,3 +42,7 @@ EXPORT_SYMBOL(ikbd_mouse_rel_pos); EXPORT_SYMBOL(ikbd_mouse_disable); EXPORT_SYMBOL(atari_microwire_cmd); + +#if CONFIG_PCI +EXPORT_SYMBOL(pci_devices); +#endif diff --git a/arch/m68k/config.in b/arch/m68k/config.in index 0b287bb9d..4b90b6b02 100644 --- a/arch/m68k/config.in +++ b/arch/m68k/config.in @@ -14,7 +14,7 @@ comment 'Loadable module support' bool 'Enable loadable module support' CONFIG_MODULES if [ "$CONFIG_MODULES" = "y" ]; then bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS - bool 'Kernel daemon support (e.g. autoload of modules)' CONFIG_KERNELD + bool 'Kernel module loader' CONFIG_KMOD fi endmenu diff --git a/arch/m68k/defconfig b/arch/m68k/defconfig index e9fe2175b..6ce3991d4 100644 --- a/arch/m68k/defconfig +++ b/arch/m68k/defconfig @@ -12,7 +12,7 @@ CONFIG_EXPERIMENTAL=y # # CONFIG_MODULES is not set # CONFIG_MODVERSIONS is not set -# CONFIG_KERNELD is not set +# CONFIG_KMOD is not set # # Platform-dependent setup diff --git a/arch/m68k/kernel/m68k_ksyms.c b/arch/m68k/kernel/m68k_ksyms.c index a5c83712b..3e6721d69 100644 --- a/arch/m68k/kernel/m68k_ksyms.c +++ b/arch/m68k/kernel/m68k_ksyms.c @@ -7,6 +7,7 @@ #include <linux/elfcore.h> #include <linux/in6.h> #include <linux/interrupt.h> +#include <linux/pci.h> #include <asm/setup.h> #include <asm/machdep.h> @@ -58,3 +59,7 @@ EXPORT_SYMBOL_NOVERS(memcmp); EXPORT_SYMBOL_NOVERS(__down_failed); EXPORT_SYMBOL_NOVERS(__down_failed_interruptible); EXPORT_SYMBOL_NOVERS(__up_wakeup); + +#if CONFIG_PCI +EXPORT_SYMBOL(pci_devices); +#endif diff --git a/arch/m68k/kernel/process.c b/arch/m68k/kernel/process.c index 2318caf81..a6fbd5718 100644 --- a/arch/m68k/kernel/process.c +++ b/arch/m68k/kernel/process.c @@ -40,6 +40,7 @@ */ static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; +static struct file * init_fd_array[NR_OPEN] = { NULL, }; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; struct mm_struct init_mm = INIT_MM; diff --git a/arch/m68k/mac/ksyms.c b/arch/m68k/mac/ksyms.c index 05373b04e..573e6f78e 100644 --- a/arch/m68k/mac/ksyms.c +++ b/arch/m68k/mac/ksyms.c @@ -1,7 +1,12 @@ #include <linux/module.h> +#include <linux/pci.h> #include <asm/ptrace.h> #include <asm/traps.h> /* Hook for mouse driver */ extern void (*mac_mouse_interrupt_hook) (char *); EXPORT_SYMBOL(mac_mouse_interrupt_hook); + +#if CONFIG_PCI +EXPORT_SYMBOL(pci_devices); +#endif diff --git a/arch/mips/config.in b/arch/mips/config.in index e33db8348..171170f3b 100644 --- a/arch/mips/config.in +++ b/arch/mips/config.in @@ -96,7 +96,7 @@ comment 'Loadable module support' bool 'Enable loadable module support' CONFIG_MODULES if [ "$CONFIG_MODULES" = "y" ]; then bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS - bool 'Kernel daemon support (e.g. autoload of modules)' CONFIG_KERNELD + bool 'Kernel module loader' CONFIG_KMOD fi endmenu diff --git a/arch/mips/kernel/init_task.c b/arch/mips/kernel/init_task.c index acc206942..90ae6952f 100644 --- a/arch/mips/kernel/init_task.c +++ b/arch/mips/kernel/init_task.c @@ -6,6 +6,7 @@ static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; +static struct files * init_fd_array[NR_OPEN] = { NULL, }; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; struct mm_struct init_mm = INIT_MM; diff --git a/arch/mips/kernel/mips_ksyms.c b/arch/mips/kernel/mips_ksyms.c index 47807c294..95bdbdc5c 100644 --- a/arch/mips/kernel/mips_ksyms.c +++ b/arch/mips/kernel/mips_ksyms.c @@ -7,7 +7,7 @@ * * Copyright (C) 1996, 1997 by Ralf Baechle * - * $Id: mips_ksyms.c,v 1.4 1997/12/01 17:57:28 ralf Exp $ + * $Id: mips_ksyms.c,v 1.5 1998/03/17 22:07:35 ralf Exp $ */ #include <linux/config.h> #include <linux/module.h> @@ -15,6 +15,7 @@ #include <linux/mm.h> #include <linux/interrupt.h> #include <linux/in6.h> +#include <linux/pci.h> #include <asm/checksum.h> #include <asm/dma.h> @@ -98,3 +99,7 @@ EXPORT_SYMBOL(__compute_return_epc); EXPORT_SYMBOL(register_fpe); EXPORT_SYMBOL(unregister_fpe); #endif + +#if CONFIG_PCI +EXPORT_SYMBOL(pci_devices); +#endif diff --git a/arch/mips/kernel/syscall.c b/arch/mips/kernel/syscall.c index e9d4340cb..ac5cd123b 100644 --- a/arch/mips/kernel/syscall.c +++ b/arch/mips/kernel/syscall.c @@ -11,7 +11,7 @@ * Don't waste that much memory for empty entries in the syscall * table. * - * $Id: syscall.c,v 1.5 1997/12/16 05:34:37 ralf Exp $ + * $Id: syscall.c,v 1.6 1998/03/17 22:07:36 ralf Exp $ */ #undef CONF_PRINT_SYSCALLS #undef CONF_DEBUG_IRIX @@ -23,6 +23,7 @@ #include <linux/smp_lock.h> #include <linux/mman.h> #include <linux/sched.h> +#include <linux/file.h> #include <linux/utsname.h> #include <linux/unistd.h> #include <asm/branch.h> @@ -59,19 +60,22 @@ asmlinkage unsigned long sys_mmap(unsigned long addr, size_t len, int prot, int flags, int fd, off_t offset) { struct file * file = NULL; - unsigned long res; + unsigned long error = -EFAULT; lock_kernel(); if (!(flags & MAP_ANONYMOUS)) { - if (fd >= NR_OPEN || !(file = current->files->fd[fd])) - return -EBADF; + error = -EBADF; + file = fget(fd); + if (!file) + goto out; } - flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); - - res = do_mmap(file, addr, len, prot, flags, offset); - + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + error = do_mmap(file, addr, len, prot, flags, offset); + if (file) + fput(file); +out: unlock_kernel(); - return res; + return error; } asmlinkage int sys_idle(void) diff --git a/arch/mips/mm/r4xx0.c b/arch/mips/mm/r4xx0.c index ab61c564a..e86f3e4eb 100644 --- a/arch/mips/mm/r4xx0.c +++ b/arch/mips/mm/r4xx0.c @@ -3,7 +3,7 @@ * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) * - * $Id: r4xx0.c,v 1.19 1998/01/18 02:49:41 ralf Exp $ + * $Id: r4xx0.c,v 1.12 1998/03/03 16:57:26 ralf Exp $ * * To do: * @@ -2379,7 +2379,7 @@ static void probe_icache(unsigned long config) ic_lsize = 16 << ((config >> 4) & 1); printk("Primary instruction cache %dkb, linesize %d bytes)\n", - icache_size, ic_lsize); + icache_size >> 10, ic_lsize); } static void probe_dcache(unsigned long config) @@ -2388,7 +2388,7 @@ static void probe_dcache(unsigned long config) dc_lsize = 16 << ((config >> 4) & 1); printk("Primary data cache %dkb, linesize %d bytes)\n", - dcache_size, dc_lsize); + dcache_size >> 10, dc_lsize); } diff --git a/arch/ppc/config.in b/arch/ppc/config.in index 6a244a72a..72bab8928 100644 --- a/arch/ppc/config.in +++ b/arch/ppc/config.in @@ -32,7 +32,7 @@ bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL bool 'Enable loadable module support' CONFIG_MODULES if [ "$CONFIG_MODULES" = "y" ]; then bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS - bool 'Kernel daemon support (e.g. autoload of modules)' CONFIG_KERNELD + bool 'Kernel module loader' CONFIG_KMOD fi define_bool CONFIG_PCI y diff --git a/arch/ppc/defconfig b/arch/ppc/defconfig index ae97abde6..7e5caa71d 100644 --- a/arch/ppc/defconfig +++ b/arch/ppc/defconfig @@ -19,7 +19,7 @@ CONFIG_COMMON=y CONFIG_EXPERIMENTAL=y CONFIG_MODULES=y CONFIG_MODVERSIONS=y -CONFIG_KERNELD=y +CONFIG_KMOD=y CONFIG_PCI=y # CONFIG_PCI_OPTIMIZE is not set CONFIG_PCI_OLD_PROC=y diff --git a/arch/ppc/kernel/ppc_ksyms.c b/arch/ppc/kernel/ppc_ksyms.c index 8f3225d59..6ec6258ab 100644 --- a/arch/ppc/kernel/ppc_ksyms.c +++ b/arch/ppc/kernel/ppc_ksyms.c @@ -6,6 +6,7 @@ #include <linux/string.h> #include <linux/bios32.h> #include <linux/interrupt.h> +#include <linux/pci.h> #include <asm/semaphore.h> #include <asm/processor.h> @@ -147,3 +148,7 @@ EXPORT_SYMBOL(get_property); EXPORT_SYMBOL(pci_io_base); EXPORT_SYMBOL(pci_device_loc); EXPORT_SYMBOL(note_scsi_host); + +#if CONFIG_PCI +EXPORT_SYMBOL(pci_devices); +#endif diff --git a/arch/ppc/kernel/process.c b/arch/ppc/kernel/process.c index b2a1478cb..7ffaf58c0 100644 --- a/arch/ppc/kernel/process.c +++ b/arch/ppc/kernel/process.c @@ -64,6 +64,7 @@ task_top(struct task_struct *tsk) static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; +static struct file * init_fd_array[NR_OPEN] = { NULL, }; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; diff --git a/arch/ppc/pmac_defconfig b/arch/ppc/pmac_defconfig index 8cd90e2c4..529b33138 100644 --- a/arch/ppc/pmac_defconfig +++ b/arch/ppc/pmac_defconfig @@ -19,7 +19,7 @@ CONFIG_COMMON=y CONFIG_EXPERIMENTAL=y CONFIG_MODULES=y # CONFIG_MODVERSIONS is not set -CONFIG_KERNELD=y +CONFIG_KMOD=y CONFIG_PCI=y CONFIG_NET=y CONFIG_SYSCTL=y diff --git a/arch/ppc/prep_defconfig b/arch/ppc/prep_defconfig index de686193d..147fa3005 100644 --- a/arch/ppc/prep_defconfig +++ b/arch/ppc/prep_defconfig @@ -19,7 +19,7 @@ CONFIG_COMMON=y CONFIG_EXPERIMENTAL=y CONFIG_MODULES=y CONFIG_MODVERSIONS=y -CONFIG_KERNELD=y +CONFIG_KMOD=y CONFIG_PCI=y # CONFIG_PCI_OPTIMIZE is not set CONFIG_NET=y diff --git a/arch/sparc/config.in b/arch/sparc/config.in index d7902e886..4a087fca2 100644 --- a/arch/sparc/config.in +++ b/arch/sparc/config.in @@ -14,7 +14,7 @@ comment 'Loadable module support' bool 'Enable loadable module support' CONFIG_MODULES if [ "$CONFIG_MODULES" = "y" ]; then bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS - bool 'Kernel daemon support (e.g. autoload of modules)' CONFIG_KERNELD + bool 'Kernel module loader' CONFIG_KMOD fi endmenu diff --git a/arch/sparc/defconfig b/arch/sparc/defconfig index 5641663e5..38b6096cc 100644 --- a/arch/sparc/defconfig +++ b/arch/sparc/defconfig @@ -12,7 +12,7 @@ CONFIG_EXPERIMENTAL=y # CONFIG_MODULES=y CONFIG_MODVERSIONS=y -CONFIG_KERNELD=y +CONFIG_KMOD=y # # General setup diff --git a/arch/sparc/kernel/init_task.c b/arch/sparc/kernel/init_task.c index d0fc09346..1829daeea 100644 --- a/arch/sparc/kernel/init_task.c +++ b/arch/sparc/kernel/init_task.c @@ -6,6 +6,7 @@ static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; +static struct files * init_fd_array[NR_OPEN] = { NULL, }; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; struct mm_struct init_mm = INIT_MM; diff --git a/arch/sparc/kernel/ptrace.c b/arch/sparc/kernel/ptrace.c index 635623fb3..e50f308c7 100644 --- a/arch/sparc/kernel/ptrace.c +++ b/arch/sparc/kernel/ptrace.c @@ -550,9 +550,12 @@ asmlinkage void do_ptrace(struct pt_regs *regs) } child->flags |= PF_PTRACED; if(child->p_pptr != current) { + unsigned long flags; + write_lock_irqsave(&tasklist_lock, flags); REMOVE_LINKS(child); child->p_pptr = current; SET_LINKS(child); + write_unlock_irqrestore(&tasklist_lock, flags); } send_sig(SIGSTOP, child, 1); pt_succ_return(regs, 0); @@ -851,6 +854,7 @@ asmlinkage void do_ptrace(struct pt_regs *regs) } case PTRACE_SUNDETACH: { /* detach a process that was attached. */ + unsigned long flags; if ((unsigned long) data > _NSIG) { pt_error_return(regs, EIO); goto out; @@ -858,9 +862,11 @@ asmlinkage void do_ptrace(struct pt_regs *regs) child->flags &= ~(PF_PTRACED|PF_TRACESYS); wake_up_process(child); child->exit_code = data; + write_lock_irqsave(&tasklist_lock, flags); REMOVE_LINKS(child); child->p_pptr = child->p_opptr; SET_LINKS(child); + write_unlock_irqrestore(&tasklist_lock, flags); pt_succ_return(regs, 0); goto out; } diff --git a/arch/sparc/kernel/sparc_ksyms.c b/arch/sparc/kernel/sparc_ksyms.c index df3eac300..92be6f74d 100644 --- a/arch/sparc/kernel/sparc_ksyms.c +++ b/arch/sparc/kernel/sparc_ksyms.c @@ -15,6 +15,7 @@ #include <linux/string.h> #include <linux/interrupt.h> #include <linux/in6.h> +#include <linux/pci.h> #include <asm/oplib.h> #include <asm/delay.h> @@ -271,3 +272,7 @@ EXPORT_SYMBOL_DOT(mul); EXPORT_SYMBOL_DOT(umul); EXPORT_SYMBOL_DOT(div); EXPORT_SYMBOL_DOT(udiv); + +#if CONFIG_PCI +EXPORT_SYMBOL(pci_devices); +#endif diff --git a/arch/sparc/mm/init.c b/arch/sparc/mm/init.c index 868fc2162..aa85666c6 100644 --- a/arch/sparc/mm/init.c +++ b/arch/sparc/mm/init.c @@ -17,6 +17,7 @@ #include <linux/mman.h> #include <linux/mm.h> #include <linux/swap.h> +#include <linux/swapctl.h> #ifdef CONFIG_BLK_DEV_INITRD #include <linux/blk.h> #endif @@ -172,9 +173,6 @@ paging_init(unsigned long start_mem, unsigned long end_mem)) struct cache_palias *sparc_aliases; -extern int min_free_pages; -extern int free_pages_low; -extern int free_pages_high; extern void srmmu_frob_mem_map(unsigned long); int physmem_mapped_contig = 1; @@ -265,11 +263,11 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) initpages << (PAGE_SHIFT-10), PAGE_OFFSET, end_mem); - min_free_pages = nr_free_pages >> 7; - if(min_free_pages < 16) - min_free_pages = 16; - free_pages_low = min_free_pages + (min_free_pages >> 1); - free_pages_high = min_free_pages + min_free_pages; + freepages.min = nr_free_pages >> 7; + if(freepages.min < 16) + freepages.min = 16; + freepages.low = freepages.min + (freepages.min >> 1); + freepages.high = freepages.min + freepages.min; } void free_initmem (void) diff --git a/arch/sparc64/config.in b/arch/sparc64/config.in index 0bb079f4e..4461cdea0 100644 --- a/arch/sparc64/config.in +++ b/arch/sparc64/config.in @@ -14,7 +14,7 @@ comment 'Loadable module support' bool 'Enable loadable module support' CONFIG_MODULES if [ "$CONFIG_MODULES" = "y" ]; then bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS - bool 'Kernel daemon support (e.g. autoload of modules)' CONFIG_KERNELD + bool 'Kernel module loader' CONFIG_KMOD fi endmenu diff --git a/arch/sparc64/defconfig b/arch/sparc64/defconfig index 7232deff2..f3c2e2ca4 100644 --- a/arch/sparc64/defconfig +++ b/arch/sparc64/defconfig @@ -12,7 +12,7 @@ CONFIG_EXPERIMENTAL=y # CONFIG_MODULES=y CONFIG_MODVERSIONS=y -CONFIG_KERNELD=y +CONFIG_KMOD=y # # General setup diff --git a/arch/sparc64/kernel/init_task.c b/arch/sparc64/kernel/init_task.c index d0fc09346..1829daeea 100644 --- a/arch/sparc64/kernel/init_task.c +++ b/arch/sparc64/kernel/init_task.c @@ -6,6 +6,7 @@ static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; +static struct files * init_fd_array[NR_OPEN] = { NULL, }; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; struct mm_struct init_mm = INIT_MM; diff --git a/arch/sparc64/kernel/traps.c b/arch/sparc64/kernel/traps.c index cde799d92..6e1d30990 100644 --- a/arch/sparc64/kernel/traps.c +++ b/arch/sparc64/kernel/traps.c @@ -26,8 +26,8 @@ #include <asm/uaccess.h> #include <asm/fpumacro.h> #include <asm/lsu.h> -#ifdef CONFIG_KERNELD -#include <linux/kerneld.h> +#ifdef CONFIG_KMOD +#include <linux/kmod.h> #endif /* #define SYSCALL_TRACING */ @@ -327,7 +327,7 @@ void do_fpother(struct pt_regs *regs) case (2 << 14): /* unfinished_FPop */ case (3 << 14): /* unimplemented_FPop */ #ifdef CONFIG_MATHEMU_MODULE -#ifdef CONFIG_KERNELD +#ifdef CONFIG_KMOD if (!handle_mathemu) request_module("math-emu"); #endif diff --git a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c index 54dc8f265..b564dc0dc 100644 --- a/arch/sparc64/mm/init.c +++ b/arch/sparc64/mm/init.c @@ -10,6 +10,7 @@ #include <linux/init.h> #include <linux/blk.h> #include <linux/swap.h> +#include <linux/swapctl.h> #include <asm/head.h> #include <asm/system.h> @@ -864,10 +865,6 @@ paging_init(unsigned long start_mem, unsigned long end_mem)) return device_scan (PAGE_ALIGN (start_mem)); } -extern int min_free_pages; -extern int free_pages_low; -extern int free_pages_high; - __initfunc(static void taint_real_pages(unsigned long start_mem, unsigned long end_mem)) { unsigned long addr, tmp2 = 0; @@ -946,11 +943,11 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) initpages << (PAGE_SHIFT-10), PAGE_OFFSET, end_mem); - min_free_pages = nr_free_pages >> 7; - if(min_free_pages < 16) - min_free_pages = 16; - free_pages_low = min_free_pages + (min_free_pages >> 1); - free_pages_high = min_free_pages + min_free_pages; + freepages.low = nr_free_pages >> 7; + if(freepages.low < 16) + freepages.low = 16; + freepages.low = freepages.low + (freepages.low >> 1); + freepages.high = freepages.low + freepages.low; } void free_initmem (void) diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index 8823e4c97..088980ead 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -534,6 +534,8 @@ static struct floppy_struct *_floppy = floppy_type; static unsigned char current_drive = 0; static long current_count_sectors = 0; static unsigned char sector_t; /* sector in track */ +static unsigned char in_sector_offset; /* offset within physical sector, + * expressed in units of 512 bytes */ #ifndef fd_eject #define fd_eject(x) -EINVAL @@ -2252,7 +2254,7 @@ static void request_done(int uptodate) /* Interrupt handler evaluating the result of the r/w operation */ static void rw_interrupt(void) { - int nr_sectors, ssize, eoc; + int nr_sectors, ssize, eoc, heads; if (!DRS->first_read_date) DRS->first_read_date = jiffies; @@ -2264,23 +2266,32 @@ static void rw_interrupt(void) eoc = 1; else eoc = 0; - nr_sectors = ((R_TRACK-TRACK)*_floppy->head+R_HEAD-HEAD) * - _floppy->sect + ((R_SECTOR-SECTOR+eoc) << SIZECODE >> 2) - - (sector_t % _floppy->sect) % ssize; + + if(COMMAND & 0x80) + heads = 2; + else + heads = 1; + + nr_sectors = (((R_TRACK-TRACK) * heads + + R_HEAD-HEAD) * SECT_PER_TRACK + + R_SECTOR-SECTOR + eoc) << SIZECODE >> 2; #ifdef FLOPPY_SANITY_CHECK - if (nr_sectors > current_count_sectors + ssize - - (current_count_sectors + sector_t) % ssize + - sector_t % ssize){ + if (nr_sectors / ssize > + (in_sector_offset + current_count_sectors + ssize - 1)/ssize) { DPRINT("long rw: %x instead of %lx\n", nr_sectors, current_count_sectors); printk("rs=%d s=%d\n", R_SECTOR, SECTOR); printk("rh=%d h=%d\n", R_HEAD, HEAD); printk("rt=%d t=%d\n", R_TRACK, TRACK); - printk("spt=%d st=%d ss=%d\n", SECT_PER_TRACK, - sector_t, ssize); + printk("heads=%d eoc=%d\n", heads, eoc); + printk("spt=%d st=%d ss=%d\n", SECT_PER_TRACK, + sector_t, ssize); + printk("in_sector_offset=%d\n", in_sector_offset); } #endif + + nr_sectors -= in_sector_offset; INFBOUND(nr_sectors,0); SUPBOUND(current_count_sectors, nr_sectors); @@ -2473,7 +2484,7 @@ static inline int check_dma_crossing(char *start, * does not work with MT, hence we can only transfer one head at * a time */ -static int virtualdmabug_workaround() { +static void virtualdmabug_workaround(void) { int hard_sectors, end_sector; if(CT(COMMAND) == FD_WRITE) { COMMAND &= ~0x80; /* switch off multiple track mode */ @@ -2484,7 +2495,7 @@ static int virtualdmabug_workaround() { if(end_sector > SECT_PER_TRACK) { printk("too many sectors %d > %d\n", end_sector, SECT_PER_TRACK); - return 0; + return; } #endif SECT_PER_TRACK = end_sector; /* make sure SECT_PER_TRACK points @@ -2595,7 +2606,8 @@ static int make_raw_rw_request(void) max_sector = _floppy->sect; } - aligned_sector_t = sector_t - (sector_t % _floppy->sect) % ssize; + in_sector_offset = (sector_t % _floppy->sect) % ssize; + aligned_sector_t = sector_t - in_sector_offset; max_size = CURRENT->nr_sectors; if ((raw_cmd->track == buffer_track) && (current_drive == buffer_drive) && @@ -2605,7 +2617,7 @@ static int make_raw_rw_request(void) copy_buffer(1, max_sector, buffer_max); return 1; } - } else if (aligned_sector_t != sector_t || CURRENT->nr_sectors < ssize){ + } else if (in_sector_offset || CURRENT->nr_sectors < ssize){ if (CT(COMMAND) == FD_WRITE){ if (sector_t + CURRENT->nr_sectors > ssize && sector_t + CURRENT->nr_sectors < ssize + ssize) @@ -2677,7 +2689,7 @@ static int make_raw_rw_request(void) sector_t > buffer_max || sector_t < buffer_min || ((CT(COMMAND) == FD_READ || - (aligned_sector_t == sector_t && CURRENT->nr_sectors >= ssize))&& + (!in_sector_offset && CURRENT->nr_sectors >= ssize))&& max_sector > 2 * max_buffer_sectors + buffer_min && max_size + sector_t > 2 * max_buffer_sectors + buffer_min) /* not enough space */){ @@ -2694,7 +2706,7 @@ static int make_raw_rw_request(void) * is either aligned or the data already in the buffer * (buffer will be overwritten) */ #ifdef FLOPPY_SANITY_CHECK - if (sector_t != aligned_sector_t && buffer_track == -1) + if (in_sector_offset && buffer_track == -1) DPRINT("internal error offset !=0 on write\n"); #endif buffer_track = raw_cmd->track; @@ -2705,7 +2717,7 @@ static int make_raw_rw_request(void) 2*max_buffer_sectors+buffer_min-aligned_sector_t); /* round up current_count_sectors to get dma xfer size */ - raw_cmd->length = sector_t+current_count_sectors-aligned_sector_t; + raw_cmd->length = in_sector_offset+current_count_sectors; raw_cmd->length = ((raw_cmd->length -1)|(ssize-1))+1; raw_cmd->length <<= 9; #ifdef FLOPPY_SANITY_CHECK diff --git a/drivers/block/ide-cd.c b/drivers/block/ide-cd.c index 24f00c950..27b7d4c70 100644 --- a/drivers/block/ide-cd.c +++ b/drivers/block/ide-cd.c @@ -26,7 +26,6 @@ * (If you are using a cd changer, you may get errors in the kernel * logs that are completly expected. Don't complain to me about this, * unless you have a patch to fix it. I am working on it...) - * -Implement ide_cdrom_select_speed using the generic cdrom interface * -Fix ide_cdrom_reset so that it works (it does nothing right now) * -Query the drive to find what features are available before trying to * use them (like trying to close the tray in drives that can't). @@ -189,10 +188,19 @@ * malloc'ed but never free'd when closing the device. * -- Cleaned up the global namespace a bit by making more * functions static that should already have been. + * 4.11 Mar 12, 1998 -- Added support for the CDROM_SELECT_SPEED ioctl + * based on a patch for 2.0.33 by Jelle Foks + * <jelle@scintilla.utwente.nl>, a patch for 2.0.33 + * by Toni Giorgino <toni@pcape2.pi.infn.it>, the SCSI + * version, and my own efforts. -erik + * -- Fixed a stupid bug which egcs was kind enough to + * inform me of where "Illegal mode for this track" + * was never returned due to a comparison on data + * types of limited range. * *************************************************************************/ -#define IDECD_VERSION "4.10" +#define IDECD_VERSION "4.11" #include <linux/module.h> #include <linux/types.h> @@ -271,8 +279,7 @@ void cdrom_analyze_sense_data (ide_drive_t *drive, printk (" Error code: 0x%02x\n", reqbuf->error_code); - if (reqbuf->sense_key >= 0 && - reqbuf->sense_key < ARY_LEN (sense_key_texts)) + if ( reqbuf->sense_key < ARY_LEN (sense_key_texts)) s = sense_key_texts[reqbuf->sense_key]; else s = "(bad sense key)"; @@ -285,7 +292,7 @@ void cdrom_analyze_sense_data (ide_drive_t *drive, s = buf; } else { int lo, hi; - int key = (reqbuf->asc << 8); + unsigned short key = (reqbuf->asc << 8); if ( ! (reqbuf->ascq >= 0x80 && reqbuf->ascq <= 0xdd) ) key |= reqbuf->ascq; @@ -496,7 +503,7 @@ static int cdrom_decode_status (ide_drive_t *drive, int good_stat, } else if (sense_key == UNIT_ATTENTION) { /* Check for media change. */ cdrom_saw_media_change (drive); - printk ("%s: media changed\n", drive->name); + /*printk("%s: media changed\n",drive->name);*/ return 0; } else { /* Otherwise, print an error. */ @@ -1518,7 +1525,6 @@ cdrom_startstop (ide_drive_t *drive, int startflag, return cdrom_queue_packet_command (drive, &pc); } - static int cdrom_read_capacity (ide_drive_t *drive, unsigned *capacity, struct atapi_request_sense *reqbuf) @@ -1721,7 +1727,6 @@ cdrom_mode_sense (ide_drive_t *drive, int pageno, int modeflag, return cdrom_queue_packet_command (drive, &pc); } - static int cdrom_mode_select (ide_drive_t *drive, int pageno, char *buf, int buflen, struct atapi_request_sense *reqbuf) @@ -1742,6 +1747,43 @@ cdrom_mode_select (ide_drive_t *drive, int pageno, char *buf, int buflen, } +/* Note that this takes speed in kbytes/second, so don't try requesting + silly speeds like 2 here. Common speeds include: + 176 kbytes/second -- 1x + 353 kbytes/second -- 2x + 387 kbytes/second -- 2.2x + 528 kbytes/second -- 3x + 706 kbytes/second -- 4x + 1400 kbytes/second -- 8x + 2800 kbytes/second -- 16x + ATAPI drives are free to select the speed you request or any slower + rate :-( Requesting too fast a speed will _not_ produce an error. */ +static int +cdrom_select_speed (ide_drive_t *drive, int speed, + struct atapi_request_sense *reqbuf) +{ + struct packet_command pc; + memset (&pc, 0, sizeof (pc)); + pc.sense_data = reqbuf; + + if (speed < 1) + speed = 0xffff; /* set to max */ + else + speed *= 177; /* Nx to kbytes/s */ + + pc.c[0] = SET_CD_SPEED; + /* Read Drive speed in kbytes/second MSB */ + pc.c[2] = (speed >> 8) & 0xff; + /* Read Drive speed in kbytes/second LSB */ + pc.c[3] = speed & 0xff; + /* Write Drive speed in kbytes/second MSB */ + //pc.c[4] = (speed >> 8) & 0xff; + /* Write Drive speed in kbytes/second LSB */ + //pc.c[5] = speed & 0xff; + + return cdrom_queue_packet_command (drive, &pc); +} + static int cdrom_play_lba_range_1 (ide_drive_t *drive, int lba_start, int lba_end, struct atapi_request_sense *reqbuf) @@ -2429,6 +2471,44 @@ int ide_cdrom_lock_door (struct cdrom_device_info *cdi, int lock) return cdrom_lockdoor (drive, lock, NULL); } +static +int ide_cdrom_select_speed (struct cdrom_device_info *cdi, int speed) +{ + int stat, attempts = 3; + struct { + char pad[8]; + struct atapi_capabilities_page cap; + } buf; + ide_drive_t *drive = (ide_drive_t*) cdi->handle; + struct atapi_request_sense reqbuf; + stat=cdrom_select_speed (drive, speed, &reqbuf); + if (stat<0) + return stat; + + /* Now that that is done, update the speed fields */ + do { /* we seem to get stat=0x01,err=0x00 the first time (??) */ + if (attempts-- <= 0) + return 0; + stat = cdrom_mode_sense (drive, PAGE_CAPABILITIES, 0, + (char *)&buf, sizeof (buf), NULL); + } while (stat); + + /* The ACER/AOpen 24X cdrom has the speed fields byte-swapped */ + if (drive->id && !drive->id->model[0] && !strncmp(drive->id->fw_rev, "241N", 4)) { + CDROM_STATE_FLAGS (drive)->current_speed = + (((unsigned int)buf.cap.curspeed) + (176/2)) / 176; + CDROM_CONFIG_FLAGS (drive)->max_speed = + (((unsigned int)buf.cap.maxspeed) + (176/2)) / 176; + } else { + CDROM_STATE_FLAGS (drive)->current_speed = + (ntohs(buf.cap.curspeed) + (176/2)) / 176; + CDROM_CONFIG_FLAGS (drive)->max_speed = + (ntohs(buf.cap.maxspeed) + (176/2)) / 176; + } + cdi->speed = CDROM_STATE_FLAGS (drive)->current_speed; + return 0; +} + static int ide_cdrom_select_disc (struct cdrom_device_info *cdi, int slot) @@ -2668,14 +2748,14 @@ struct cdrom_device_ops ide_cdrom_dops = { ide_cdrom_check_media_change_real, /* media_changed */ ide_cdrom_tray_move, /* tray_move */ ide_cdrom_lock_door, /* lock_door */ - NULL, /* select_speed */ + ide_cdrom_select_speed, /* select_speed */ ide_cdrom_select_disc, /* select_disc */ ide_cdrom_get_last_session, /* get_last_session */ ide_cdrom_get_mcn, /* get_mcn */ ide_cdrom_reset, /* reset */ ide_cdrom_audio_ioctl, /* audio_ioctl */ ide_cdrom_dev_ioctl, /* dev_ioctl */ - CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK + CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK | CDC_SELECT_SPEED | CDC_SELECT_DISC | CDC_MULTI_SESSION | CDC_MCN | CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO | CDC_RESET | CDC_IOCTLS | CDC_DRIVE_STATUS, /* capability */ @@ -2691,7 +2771,7 @@ static int ide_cdrom_register (ide_drive_t *drive, int nslots) devinfo->dev = MKDEV (HWIF(drive)->major, minor); devinfo->ops = &ide_cdrom_dops; devinfo->mask = 0; - *(int *)&devinfo->speed = CDROM_CONFIG_FLAGS (drive)->max_speed; + *(int *)&devinfo->speed = CDROM_STATE_FLAGS (drive)->current_speed; *(int *)&devinfo->capacity = nslots; devinfo->handle = (void *) drive; strcpy(devinfo->name, drive->name); @@ -2750,11 +2830,15 @@ int ide_cdrom_probe_capabilities (ide_drive_t *drive) /* The ACER/AOpen 24X cdrom has the speed fields byte-swapped */ if (drive->id && !drive->id->model[0] && !strncmp(drive->id->fw_rev, "241N", 4)) { - CDROM_STATE_FLAGS (drive)->current_speed = (((unsigned int)buf.cap.curspeed) + (176/2)) / 176; - CDROM_CONFIG_FLAGS (drive)->max_speed = (((unsigned int)buf.cap.maxspeed) + (176/2)) / 176; + CDROM_STATE_FLAGS (drive)->current_speed = + (((unsigned int)buf.cap.curspeed) + (176/2)) / 176; + CDROM_CONFIG_FLAGS (drive)->max_speed = + (((unsigned int)buf.cap.maxspeed) + (176/2)) / 176; } else { - CDROM_STATE_FLAGS (drive)->current_speed = (ntohs(buf.cap.curspeed) + (176/2)) / 176; - CDROM_CONFIG_FLAGS (drive)->max_speed = (ntohs(buf.cap.maxspeed) + (176/2)) / 176; + CDROM_STATE_FLAGS (drive)->current_speed = + (ntohs(buf.cap.curspeed) + (176/2)) / 176; + CDROM_CONFIG_FLAGS (drive)->max_speed = + (ntohs(buf.cap.maxspeed) + (176/2)) / 176; } printk ("%s: ATAPI %dX CDROM", diff --git a/drivers/block/ide-cd.h b/drivers/block/ide-cd.h index 53e38e1b1..140c4ccb6 100644 --- a/drivers/block/ide-cd.h +++ b/drivers/block/ide-cd.h @@ -71,7 +71,7 @@ #define MODE_SENSE_10 0x5a #define MODE_SELECT_10 0x55 #define READ_CD 0xbe - +#define SET_CD_SPEED 0xbb #define LOAD_UNLOAD 0xa6 #define MECHANISM_STATUS 0xbd @@ -431,7 +431,7 @@ char *sense_key_texts[16] = { with additions from Tables 141 and 142 of the ATAPI 2.6 draft standard. */ struct { - short asc_ascq; + unsigned short asc_ascq; char *text; } sense_data_texts[] = { { 0x0000, "No additional sense information" }, diff --git a/drivers/block/ide-disk.c b/drivers/block/ide-disk.c index 4ad8bf606..8c72c791d 100644 --- a/drivers/block/ide-disk.c +++ b/drivers/block/ide-disk.c @@ -16,9 +16,11 @@ * Version 1.02 remove ", LBA" from drive identification msgs * Version 1.03 fix display of id->buf_size for big-endian * Version 1.04 add /proc configurable settings and S.M.A.R.T support + * Version 1.05 add capacity support for ATA3 >= 8GB + * Version 1.06 get boot-up messages to show full cyl count */ -#define IDEDISK_VERSION "1.04" +#define IDEDISK_VERSION "1.06" #undef REALLY_SLOW_IO /* most systems can safely undef this */ @@ -83,6 +85,11 @@ static int lba_capacity_is_ok (struct hd_driveid *id) unsigned long chs_sects = id->cyls * id->heads * id->sectors; unsigned long _10_percent = chs_sects / 10; + /* very large drives (8GB+) may lie about the number of cylinders */ + if (id->cyls == 16383 && id->heads == 16 && id->sectors == 63 && lba_sects > chs_sects) { + id->cyls = lba_sects / (16 * 63); /* correct cyls */ + return 1; /* lba_capacity is our only option */ + } /* perform a rough sanity check on lba_sects: within 10% is "okay" */ if ((lba_sects - chs_sects) < _10_percent) return 1; /* lba_capacity is good */ @@ -439,6 +446,7 @@ static unsigned long idedisk_capacity (ide_drive_t *drive) /* Determine capacity, and use LBA if the drive properly supports it */ if (id != NULL && (id->capability & 2) && lba_capacity_is_ok(id)) { if (id->lba_capacity >= capacity) { + drive->cyl = id->lba_capacity / (drive->head * drive->sect); capacity = id->lba_capacity; drive->select.b.lba = 1; } @@ -698,6 +706,10 @@ static void idedisk_setup (ide_drive_t *drive) drive->head = id->heads; drive->sect = id->sectors; } + + /* calculate drive capacity, and select LBA if possible */ + (void) idedisk_capacity (drive); + /* Correct the number of cyls if the bios value is too small */ if (drive->sect == drive->bios_sect && drive->head == drive->bios_head) { if (drive->cyl > drive->bios_cyl) @@ -706,8 +718,6 @@ static void idedisk_setup (ide_drive_t *drive) /* fix byte-ordering of buffer size field */ id->buf_size = le16_to_cpu(id->buf_size); - (void) idedisk_capacity (drive); /* initialize LBA selection */ - printk (KERN_INFO "%s: %.40s, %ldMB w/%dkB Cache, CHS=%d/%d/%d", drive->name, id->model, idedisk_capacity(drive)/2048L, id->buf_size/2, drive->bios_cyl, drive->bios_head, drive->bios_sect); diff --git a/drivers/block/ide-pci.c b/drivers/block/ide-pci.c index d8fa6edf5..9e8fd5127 100644 --- a/drivers/block/ide-pci.c +++ b/drivers/block/ide-pci.c @@ -24,7 +24,8 @@ #include "ide.h" -#define DEVID_PIIX ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371FB_1}) +#define DEVID_PIIXa ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371FB_0}) +#define DEVID_PIIXb ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371FB_1}) #define DEVID_PIIX3 ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_1}) #define DEVID_PIIX4 ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB}) #define DEVID_VP_IDE ((ide_pci_devid_t){PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_1}) @@ -87,7 +88,8 @@ typedef struct ide_pci_device_s { } ide_pci_device_t; static ide_pci_device_t ide_pci_chipsets[] __initdata = { - {DEVID_PIIX, "PIIX", NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}} }, + {DEVID_PIIXa, "PIIX", NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}} }, + {DEVID_PIIXb, "PIIX", NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}} }, {DEVID_PIIX3, "PIIX3", NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}} }, {DEVID_PIIX4, "PIIX4", NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}} }, {DEVID_VP_IDE, "VP_IDE", NULL, {{0x40,0x02,0x02}, {0x40,0x01,0x01}} }, @@ -360,6 +362,13 @@ static inline void ide_scan_pci_device (unsigned int bus, unsigned int fn) || IDE_PCI_DEVID_EQ(devid, IDE_PCI_DEVID_NULL) || pcibios_read_config_dword(bus, fn, 0x08, &ccode)) return; + /* + * workaround Intel Advanced/ZP with bios <= 1.04; + * these appear in some Dell Dimension XPS's + */ + if (!hedt && IDE_PCI_DEVID_EQ(devid, DEVID_PIIXa)) + hedt = 0x80; + for (d = ide_pci_chipsets; d->devid.vid && !IDE_PCI_DEVID_EQ(d->devid, devid); ++d); if (d->init_hwif == IDE_IGNORE) printk("%s: ignored by ide_scan_pci_device() (uses own driver)\n", d->name); diff --git a/drivers/block/ide.c b/drivers/block/ide.c index 7ec27d45d..e10578508 100644 --- a/drivers/block/ide.c +++ b/drivers/block/ide.c @@ -134,9 +134,9 @@ #include "ide.h" #include "ide_modes.h" -#ifdef CONFIG_KERNELD -#include <linux/kerneld.h> -#endif /* CONFIG_KERNELD */ +#ifdef CONFIG_KMOD +#include <linux/kmod.h> +#endif /* CONFIG_KMOD */ static const byte ide_hwif_to_major[] = {IDE0_MAJOR, IDE1_MAJOR, IDE2_MAJOR, IDE3_MAJOR}; @@ -1505,10 +1505,10 @@ static void ide_init_module (int type) module = module->next; } revalidate_drives(); -#ifdef CONFIG_KERNELD +#ifdef CONFIG_KMOD if (!found && type == IDE_PROBE_MODULE) (void) request_module("ide-probe"); -#endif /* CONFIG_KERNELD */ +#endif /* CONFIG_KMOD */ } static int ide_open(struct inode * inode, struct file * filp) @@ -1521,7 +1521,7 @@ static int ide_open(struct inode * inode, struct file * filp) MOD_INC_USE_COUNT; if (drive->driver == NULL) ide_init_module(IDE_DRIVER_MODULE); -#ifdef CONFIG_KERNELD +#ifdef CONFIG_KMOD if (drive->driver == NULL) { if (drive->media == ide_disk) (void) request_module("ide-disk"); @@ -1532,7 +1532,7 @@ static int ide_open(struct inode * inode, struct file * filp) if (drive->media == ide_floppy) (void) request_module("ide-floppy"); } -#endif /* CONFIG_KERNELD */ +#endif /* CONFIG_KMOD */ while (drive->busy) sleep_on(&drive->wqueue); drive->usage++; diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index 01cf3923a..9f0db3b23 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -24,9 +24,6 @@ #include <linux/module.h> -#define ATOMIC_ON() do { } while (0) -#define ATOMIC_OFF() do { } while (0) - /* * The request-struct contains all necessary data * to load a nr of sectors into memory @@ -419,8 +416,6 @@ void make_request(int major,int rw, struct buffer_head * bh) * not to schedule or do something nonatomic */ spin_lock_irqsave(&io_request_lock,flags); - ATOMIC_ON(); - req = *get_queue(bh->b_rdev); if (!req) { /* MD and loop can't handle plugging without deadlocking */ @@ -480,7 +475,6 @@ void make_request(int major,int rw, struct buffer_head * bh) continue; mark_buffer_clean(bh); - ATOMIC_OFF(); spin_unlock_irqrestore(&io_request_lock,flags); return; @@ -490,7 +484,6 @@ void make_request(int major,int rw, struct buffer_head * bh) /* find an unused request. */ req = get_request(max_req, bh->b_rdev); - ATOMIC_OFF(); spin_unlock_irqrestore(&io_request_lock,flags); /* if no request available: if rw_ahead, forget it; otherwise try again blocking.. */ @@ -665,9 +658,7 @@ void ll_rw_swap_file(int rw, kdev_t dev, unsigned int *b, int nb, char *buf) } else { unsigned long flags; spin_lock_irqsave(&io_request_lock,flags); - ATOMIC_ON(); req[j] = get_request(max_req, rdev); - ATOMIC_OFF(); spin_unlock_irqrestore(&io_request_lock,flags); if (req[j] == NULL) break; diff --git a/drivers/block/md.c b/drivers/block/md.c index 4feeb0ce9..4d0ccf401 100644 --- a/drivers/block/md.c +++ b/drivers/block/md.c @@ -12,6 +12,9 @@ RAID-1/RAID-5 extensions by: Ingo Molnar, Miguel de Icaza, Gadi Oxman + + Changes for kmod by: + Cyrus Durgin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -43,8 +46,8 @@ #include <linux/blkdev.h> #include <linux/genhd.h> #include <linux/smp_lock.h> -#ifdef CONFIG_KERNELD -#include <linux/kerneld.h> +#ifdef CONFIG_KMOD +#include <linux/kmod.h> #endif #include <linux/errno.h> #include <linux/init.h> @@ -431,7 +434,7 @@ static int do_md_run (int minor, int repart) } if (!pers[pnum]) { -#ifdef CONFIG_KERNELD +#ifdef CONFIG_KMOD char module_name[80]; sprintf (module_name, "md-personality-%d", pnum); request_module (module_name); diff --git a/drivers/block/raid5.c b/drivers/block/raid5.c index 6fefac06d..2b0e97450 100644 --- a/drivers/block/raid5.c +++ b/drivers/block/raid5.c @@ -837,8 +837,11 @@ static void add_stripe_bh (struct stripe_head *sh, struct buffer_head *bh, int d struct raid5_data *raid_conf = sh->raid_conf; struct buffer_head *bh_req; - if (sh->bh_new[dd_idx]) + if (sh->bh_new[dd_idx]) { printk("raid5: bug: stripe->bh_new[%d], sector %lu exists\n", dd_idx, sh->sector); + printk("forcing oops.\n"); + *(int*)0=0; + } set_bit(BH_Lock, &bh->b_state); diff --git a/drivers/block/rd.c b/drivers/block/rd.c index de17158a7..a133b70cc 100644 --- a/drivers/block/rd.c +++ b/drivers/block/rd.c @@ -35,6 +35,9 @@ * * 4/25/96 : Made ramdisk size a parameter (default is now 4MB) * - Chad Page + * + * Add support for fs images split across >1 disk, Paul Gortmaker, Mar '98 + * */ #include <linux/config.h> @@ -344,7 +347,6 @@ identify_ramdisk_image(kdev_t device, struct file *fp, int start_block)) struct ext2_super_block *ext2sb; struct romfs_super_block *romfsb; int nblocks = -1; - int max_blocks; unsigned char *buf; buf = kmalloc(size, GFP_KERNEL); @@ -423,17 +425,6 @@ done: fp->f_op->llseek(fp, start_block * BLOCK_SIZE, 0); fp->f_pos = start_block * BLOCK_SIZE; - if ((nblocks > 0) && blk_size[MAJOR(device)]) { - max_blocks = blk_size[MAJOR(device)][MINOR(device)]; - max_blocks -= start_block; - if (nblocks > max_blocks) { - printk(KERN_NOTICE - "RAMDISK: Restricting filesystem size " - "from %d to %d blocks.\n", - nblocks, max_blocks); - nblocks = max_blocks; - } - } kfree(buf); return nblocks; } @@ -451,6 +442,7 @@ __initfunc(static void rd_load_image(kdev_t device,int offset)) int nblocks, i; char *buf; unsigned short rotate = 0; + unsigned short devblocks = 0; char rotator[4] = { '|' , '/' , '-' , '\\' }; ram_device = MKDEV(MAJOR_NR, 0); @@ -508,8 +500,31 @@ __initfunc(static void rd_load_image(kdev_t device,int offset)) goto done; } - printk(KERN_NOTICE "RAMDISK: Loading %d blocks into ram disk... ", nblocks); + if (blk_size[MAJOR(device)]) + devblocks = blk_size[MAJOR(device)][MINOR(device)]; + + if (devblocks == 0) { + printk(KERN_ERR "RAMDISK: could not determine device size\n"); + goto done; + } + + printk(KERN_NOTICE "RAMDISK: Loading %d blocks [%d disk(s)] into ram disk... ", nblocks, nblocks/devblocks+1); for (i=0; i < nblocks; i++) { + if (i && (i % devblocks == 0)) { + printk("done.\n"); + rotate = 0; + invalidate_buffers(device); + if (infile.f_op->release) + infile.f_op->release(&inode, &infile); + printk("Please insert disk #%d and press ENTER\n", i/devblocks+1); + wait_for_keypress(); + if (blkdev_open(&inode, &infile) != 0) { + printk("Error opening disk.\n"); + goto done; + } + infile.f_pos = 0; + printk("Loading disk #%d... ", i/devblocks+1); + } infile.f_op->read(&infile, buf, BLOCK_SIZE, &infile.f_pos); outfile.f_op->write(&outfile, buf, BLOCK_SIZE, &outfile.f_pos); if (!(i % 16)) { diff --git a/drivers/char/ChangeLog b/drivers/char/ChangeLog index 56e762e71..95d6741c5 100644 --- a/drivers/char/ChangeLog +++ b/drivers/char/ChangeLog @@ -1,3 +1,11 @@ +Thu Feb 19 14:24:08 1998 Theodore Ts'o <tytso@rsts-11.mit.edu> + + * tty_io.c (tty_name): Remove the non-reentrant (and non-SMP safe) + version of tty_name, and rename the reentrant _tty_name + function to be tty_name. + (tty_open): Add a warning message stating callout devices + are deprecated. + Mon Dec 1 08:24:15 1997 Theodore Ts'o <tytso@rsts-11.mit.edu> * tty_io.c (tty_get_baud_rate): Print a warning syslog if the diff --git a/drivers/char/Config.in b/drivers/char/Config.in index ffb6287a7..e17a220e6 100644 --- a/drivers/char/Config.in +++ b/drivers/char/Config.in @@ -110,11 +110,6 @@ if [ "$CONFIG_WATCHDOG" != "n" ]; then tristate ' Acquire SBC Watchdog Timer' CONFIG_ACQUIRE_WDT fi bool 'Enhanced Real Time Clock Support' CONFIG_RTC -if [ "$CONFIG_RTC" = "y" ]; then - if [ "$ARCH" = "alpha" ]; then - bool ' ARC console time' CONFIG_RTC_ARC y - fi -fi if [ "$CONFIG_ALPHA_BOOK1" = "y" ]; then bool 'Tadpole ANA H8 Support' CONFIG_H8 fi diff --git a/drivers/char/apm_bios.c b/drivers/char/apm_bios.c index b425ab694..94f13f8fd 100644 --- a/drivers/char/apm_bios.c +++ b/drivers/char/apm_bios.c @@ -1203,17 +1203,17 @@ __initfunc(void apm_bios_init(void)) * even though they are called in protected mode. */ set_base(gdt[APM_40 >> 3], - 0xc0000000 + ((unsigned long)0x40 << 4)); + __va((unsigned long)0x40 << 4)); set_limit(gdt[APM_40 >> 3], 4096 - (0x40 << 4)); apm_bios_entry.offset = apm_bios_info.offset; apm_bios_entry.segment = APM_CS; set_base(gdt[APM_CS >> 3], - 0xc0000000 + ((unsigned long)apm_bios_info.cseg << 4)); + __va((unsigned long)apm_bios_info.cseg << 4)); set_base(gdt[APM_CS_16 >> 3], - 0xc0000000 + ((unsigned long)apm_bios_info.cseg_16 << 4)); + __va((unsigned long)apm_bios_info.cseg_16 << 4)); set_base(gdt[APM_DS >> 3], - 0xc0000000 + ((unsigned long)apm_bios_info.dseg << 4)); + __va((unsigned long)apm_bios_info.dseg << 4)); if (apm_bios_info.version == 0x100) { set_limit(gdt[APM_CS >> 3], 64 * 1024); set_limit(gdt[APM_CS_16 >> 3], 64 * 1024); diff --git a/drivers/char/esp.c b/drivers/char/esp.c index 089ce7b2d..2ea9cb86f 100644 --- a/drivers/char/esp.c +++ b/drivers/char/esp.c @@ -1384,7 +1384,7 @@ static void rs_throttle(struct tty_struct * tty) #ifdef SERIAL_DEBUG_THROTTLE char buf[64]; - printk("throttle %s: %d....\n", _tty_name(tty, buf), + printk("throttle %s: %d....\n", tty_name(tty, buf), tty->ldisc.chars_in_buffer(tty)); #endif @@ -1406,7 +1406,7 @@ static void rs_unthrottle(struct tty_struct * tty) #ifdef SERIAL_DEBUG_THROTTLE char buf[64]; - printk("unthrottle %s: %d....\n", _tty_name(tty, buf), + printk("unthrottle %s: %d....\n", tty_name(tty, buf), tty->ldisc.chars_in_buffer(tty)); #endif diff --git a/drivers/char/ftape/zftape/zftape-ctl.c b/drivers/char/ftape/zftape/zftape-ctl.c index 19d10c95b..3b80c207f 100644 --- a/drivers/char/ftape/zftape/zftape-ctl.c +++ b/drivers/char/ftape/zftape/zftape-ctl.c @@ -29,9 +29,6 @@ #include <linux/mm.h> #define __NO_VERSION__ #include <linux/module.h> -#ifdef CONFIG_KERNELD -#include <linux/kerneld.h> -#endif #include <linux/fcntl.h> #include <linux/zftape.h> diff --git a/drivers/char/ftape/zftape/zftape-init.c b/drivers/char/ftape/zftape/zftape-init.c index 0c8a6f9eb..78a857ace 100644 --- a/drivers/char/ftape/zftape/zftape-init.c +++ b/drivers/char/ftape/zftape/zftape-init.c @@ -30,8 +30,8 @@ #include <linux/signal.h> #include <linux/major.h> #include <linux/malloc.h> -#ifdef CONFIG_KERNELD -#include <linux/kerneld.h> +#ifdef CONFIG_KMOD +#include <linux/kmod.h> #endif #include <linux/fcntl.h> #include <linux/wrapper.h> @@ -391,7 +391,7 @@ struct zft_cmpr_ops *zft_cmpr_unregister(void) int zft_cmpr_lock(int try_to_load) { if (zft_cmpr_ops == NULL) { -#ifdef CONFIG_KERNELD +#ifdef CONFIG_KMOD if (try_to_load) { request_module("zft-compressor"); if (zft_cmpr_ops == NULL) { diff --git a/drivers/char/ftape/zftape/zftape-read.c b/drivers/char/ftape/zftape/zftape-read.c index c7d319fac..91ed51423 100644 --- a/drivers/char/ftape/zftape/zftape-read.c +++ b/drivers/char/ftape/zftape/zftape-read.c @@ -24,12 +24,8 @@ * for the QIC-117 floppy-tape driver for Linux. */ -#include <linux/config.h> #include <linux/errno.h> #include <linux/mm.h> -#ifdef CONFIG_KERNELD -#include <linux/kerneld.h> -#endif #include <linux/zftape.h> diff --git a/drivers/char/ftape/zftape/zftape-write.c b/drivers/char/ftape/zftape/zftape-write.c index 46f1ecc09..03b126f49 100644 --- a/drivers/char/ftape/zftape/zftape-write.c +++ b/drivers/char/ftape/zftape/zftape-write.c @@ -24,12 +24,8 @@ * for the QIC-117 floppy-tape driver for Linux. */ -#include <linux/config.h> #include <linux/errno.h> #include <linux/mm.h> -#ifdef CONFIG_KERNELD -#include <linux/kerneld.h> -#endif #include <linux/zftape.h> diff --git a/drivers/char/lp_m68k.c b/drivers/char/lp_m68k.c index a36903c7a..4a000e9aa 100644 --- a/drivers/char/lp_m68k.c +++ b/drivers/char/lp_m68k.c @@ -43,8 +43,8 @@ #include <linux/string.h> #include <linux/init.h> #include <asm/irq.h> -#ifdef CONFIG_KERNELD -#include <linux/kerneld.h> +#ifdef CONFIG_KMOD +#include <linux/kmod.h> #endif #ifdef CONFIG_AMIGA @@ -365,7 +365,7 @@ static int lp_open(struct inode *inode, struct file *file) if (dev >= MAX_LP) return -ENODEV; -#ifdef CONFIG_KERNELD +#ifdef CONFIG_KMOD if (!lp_table[dev]) { char modname[30]; diff --git a/drivers/char/misc.c b/drivers/char/misc.c index 665b9ec9d..b3b283015 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -26,6 +26,9 @@ * Idea by Jacques Gelinas <jack@solucorp.qc.ca>, * adapted by Bjorn Ekwall <bj0rn@blox.se> * corrected by Alan Cox <alan@lxorguk.ukuu.org.uk> + * + * Changes for kmod (from kerneld): + Cyrus Durgin <cider@speakeasy.org> */ #include <linux/config.h> @@ -46,8 +49,8 @@ #include <linux/tty.h> #include <linux/selection.h> -#ifdef CONFIG_KERNELD -#include <linux/kerneld.h> +#ifdef CONFIG_KMOD +#include <linux/kmod.h> #endif /* @@ -109,7 +112,7 @@ static int misc_open(struct inode * inode, struct file * file) while ((c != &misc_list) && (c->minor != minor)) c = c->next; if (c == &misc_list) { -#ifdef CONFIG_KERNELD +#ifdef CONFIG_KMOD char modname[20]; sprintf(modname, "char-major-%d-%d", MISC_MAJOR, minor); request_module(modname); diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c index 3d2f2b18c..a434cf405 100644 --- a/drivers/char/n_tty.c +++ b/drivers/char/n_tty.c @@ -420,7 +420,7 @@ static inline void n_tty_receive_overrun(struct tty_struct *tty) tty->num_overrun++; if (tty->overrun_time < (jiffies - HZ)) { - printk("%s: %d input overrun(s)\n", _tty_name(tty, buf), + printk("%s: %d input overrun(s)\n", tty_name(tty, buf), tty->num_overrun); tty->overrun_time = jiffies; tty->num_overrun = 0; @@ -656,6 +656,7 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, const unsigned char *p; char *f, flags = TTY_NORMAL; int i; + char buf[64]; if (!tty->read_buf) return; @@ -693,8 +694,8 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, n_tty_receive_overrun(tty); break; default: - printk("%s: unknown flag %d\n", tty_name(tty), - flags); + printk("%s: unknown flag %d\n", + tty_name(tty, buf), flags); break; } } diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c index 26d15d182..9edca1683 100644 --- a/drivers/char/rtc.c +++ b/drivers/char/rtc.c @@ -48,7 +48,6 @@ * this driver.) */ -#include <linux/config.h> #include <linux/types.h> #include <linux/errno.h> #include <linux/miscdevice.h> @@ -63,13 +62,6 @@ #include <asm/uaccess.h> #include <asm/system.h> -/* Adjust starting epoch if ARC console time is being used */ -#ifdef CONFIG_RTC_ARC -#define ARCFUDGE 20 -#else -#define ARCFUDGE 0 -#endif - /* * We sponge a minor off of the misc major. No need slurping * up another valuable major dev number for this. If you add @@ -126,12 +118,8 @@ unsigned char days_in_mo[] = * so that there is no possibility of conflicting with the * set_rtc_mmss() call that happens during some timer interrupts. * (See ./arch/XXXX/kernel/time.c for the set_rtc_mmss() function.) - * - * On Alpha we won't get any interrupts anyway, as they all end up - * in the system timer code. */ -#ifndef __alpha__ static void rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs) { /* @@ -152,11 +140,9 @@ static void rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs) add_timer(&rtc_irq_timer); } } -#endif /* * Now all the various file operations that we export. - * They are all useless on Alpha... *sigh*. */ static long long rtc_llseek(struct file *file, loff_t offset, int origin) @@ -167,9 +153,6 @@ static long long rtc_llseek(struct file *file, loff_t offset, int origin) static ssize_t rtc_read(struct file *file, char *buf, size_t count, loff_t *ppos) { -#ifdef __alpha__ - return -EIO; -#else struct wait_queue wait = { current, NULL }; unsigned long data; ssize_t retval; @@ -201,7 +184,6 @@ static ssize_t rtc_read(struct file *file, char *buf, remove_wait_queue(&rtc_wait, &wait); return retval; -#endif } static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, @@ -212,7 +194,6 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, struct rtc_time wtime; switch (cmd) { -#ifndef __alpha__ case RTC_AIE_OFF: /* Mask alarm int. enab. bit */ { mask_rtc_irq_bit(RTC_AIE); @@ -260,7 +241,6 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, set_rtc_irq_bit(RTC_UIE); return 0; } -#endif case RTC_ALM_READ: /* Read the present alarm time */ { /* @@ -335,7 +315,7 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, sizeof(struct rtc_time))) return -EFAULT; - yrs = rtc_tm.tm_year + 1900 + ARCFUDGE; + yrs = rtc_tm.tm_year + 1900; mon = rtc_tm.tm_mon + 1; /* tm_mon starts at zero */ day = rtc_tm.tm_mday; hrs = rtc_tm.tm_hour; @@ -400,7 +380,6 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, { return put_user(rtc_freq, (unsigned long *)arg); } -#ifndef __alpha__ case RTC_IRQP_SET: /* Set periodic IRQ rate. */ { int tmp = 0; @@ -437,7 +416,7 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, restore_flags(flags); return 0; } -#else /* __alpha__ */ +#ifdef __alpha__ case RTC_EPOCH_READ: /* Read the epoch. */ { return put_user (epoch, (unsigned long *)arg); @@ -467,18 +446,15 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, * We enforce only one user at a time here with the open/close. * Also clear the previous interrupt data on an open, and clean * up things on a close. - * On Alpha we just open, for we don't mess with interrups anyway. */ static int rtc_open(struct inode *inode, struct file *file) { -#ifndef __alpha__ if(rtc_status & RTC_IS_OPEN) return -EBUSY; rtc_status |= RTC_IS_OPEN; rtc_irq_data = 0; -#endif return 0; } @@ -489,7 +465,6 @@ static int rtc_release(struct inode *inode, struct file *file) * in use, and clear the data. */ -#ifndef __alpha__ unsigned char tmp; unsigned long flags; @@ -510,11 +485,9 @@ static int rtc_release(struct inode *inode, struct file *file) rtc_irq_data = 0; rtc_status &= ~RTC_IS_OPEN; -#endif return 0; } -#ifndef __alpha__ static unsigned int rtc_poll(struct file *file, poll_table *wait) { poll_wait(file, &rtc_wait, wait); @@ -522,7 +495,6 @@ static unsigned int rtc_poll(struct file *file, poll_table *wait) return POLLIN | POLLRDNORM; return 0; } -#endif /* * The various file operations we support. @@ -533,11 +505,7 @@ static struct file_operations rtc_fops = { rtc_read, NULL, /* No write */ NULL, /* No readdir */ -#ifdef __alpha__ - NULL, /* No select on Alpha */ -#else rtc_poll, -#endif rtc_ioctl, NULL, /* No mmap */ rtc_open, @@ -560,14 +528,12 @@ __initfunc(int rtc_init(void)) char *guess = NULL; #endif printk(KERN_INFO "Real Time Clock Driver v%s\n", RTC_VERSION); -#ifndef __alpha__ if(request_irq(RTC_IRQ, rtc_interrupt, SA_INTERRUPT, "rtc", NULL)) { /* Yeah right, seeing as irq 8 doesn't even hit the bus. */ printk(KERN_ERR "rtc: IRQ %d is not free.\n", RTC_IRQ); return -EIO; } -#endif misc_register(&rtc_dev); /* Check region? Naaah! Just snarf it up. */ request_region(RTC_PORT(0), RTC_IO_EXTENT, "rtc"); @@ -599,8 +565,8 @@ __initfunc(int rtc_init(void)) guess = "Digital UNIX"; } if (guess) - printk("rtc: %s epoch (%ld) detected\n", guess, epoch); -#else + printk("rtc: %s epoch (%lu) detected\n", guess, epoch); +#endif init_timer(&rtc_irq_timer); rtc_irq_timer.function = rtc_dropped_irq; rtc_wait = NULL; @@ -610,7 +576,6 @@ __initfunc(int rtc_init(void)) CMOS_WRITE(((CMOS_READ(RTC_FREQ_SELECT) & 0xF0) | 0x06), RTC_FREQ_SELECT); restore_flags(flags); rtc_freq = 1024; -#endif return 0; } @@ -626,7 +591,6 @@ __initfunc(int rtc_init(void)) * for something that requires a steady > 1KHz signal anyways.) */ -#ifndef __alpha__ void rtc_dropped_irq(unsigned long data) { unsigned long flags; @@ -643,7 +607,6 @@ void rtc_dropped_irq(unsigned long data) rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & 0xF0); /* restart */ restore_flags(flags); } -#endif /* * Info exported via "/proc/rtc". @@ -672,9 +635,10 @@ int get_rtc_status(char *buf) */ p += sprintf(p, "rtc_time\t: %02d:%02d:%02d\n" - "rtc_date\t: %04d-%02d-%02d\n", + "rtc_date\t: %04d-%02d-%02d\n" + "rtc_epoch\t: %04lu\n", tm.tm_hour, tm.tm_min, tm.tm_sec, - tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, epoch); get_rtc_alm_time(&tm); @@ -788,12 +752,9 @@ void get_rtc_time(struct rtc_time *rtc_tm) * Account for differences between how the RTC uses the values * and how they are defined in a struct rtc_time; */ - if ((rtc_tm->tm_year += epoch - 1900) <= 69) + if ((rtc_tm->tm_year += (epoch - 1900)) <= 69) rtc_tm->tm_year += 100; - /* if ARCFUDGE == 0, the optimizer should do away with this */ - rtc_tm->tm_year -= ARCFUDGE; - rtc_tm->tm_mon--; } @@ -832,7 +793,6 @@ void get_rtc_alm_time(struct rtc_time *alm_tm) * meddles with the interrupt enable/disable bits. */ -#ifndef __alpha__ void mask_rtc_irq_bit(unsigned char bit) { unsigned char val; @@ -862,4 +822,3 @@ void set_rtc_irq_bit(unsigned char bit) rtc_irq_data = 0; restore_flags(flags); } -#endif diff --git a/drivers/char/serial.c b/drivers/char/serial.c index 7af575780..b81a8eb8f 100644 --- a/drivers/char/serial.c +++ b/drivers/char/serial.c @@ -1745,7 +1745,7 @@ static void rs_throttle(struct tty_struct * tty) #ifdef SERIAL_DEBUG_THROTTLE char buf[64]; - printk("throttle %s: %d....\n", _tty_name(tty, buf), + printk("throttle %s: %d....\n", tty_name(tty, buf), tty->ldisc.chars_in_buffer(tty)); #endif @@ -1769,7 +1769,7 @@ static void rs_unthrottle(struct tty_struct * tty) #ifdef SERIAL_DEBUG_THROTTLE char buf[64]; - printk("unthrottle %s: %d....\n", _tty_name(tty, buf), + printk("unthrottle %s: %d....\n", tty_name(tty, buf), tty->ldisc.chars_in_buffer(tty)); #endif diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index dcc38830f..5d100bba4 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -87,8 +87,8 @@ #include <linux/vt_kern.h> #include <linux/selection.h> -#ifdef CONFIG_KERNELD -#include <linux/kerneld.h> +#ifdef CONFIG_KMOD +#include <linux/kmod.h> #endif #define CONSOLE_DEV MKDEV(TTY_MAJOR,0) @@ -127,11 +127,9 @@ static int tty_fasync(struct file * filp, int on); #endif /* - * These two routines return the name of tty. tty_name() should NOT - * be used in interrupt drivers, since it's not re-entrant. Use - * _tty_name() instead. + * This routine returns the name of tty. */ -char *_tty_name(struct tty_struct *tty, char *buf) +char *tty_name(struct tty_struct *tty, char *buf) { if (tty) sprintf(buf, "%s%d", tty->driver.name, @@ -142,13 +140,6 @@ char *_tty_name(struct tty_struct *tty, char *buf) return buf; } -char *tty_name(struct tty_struct *tty) -{ - static char buf[64]; - - return(_tty_name(tty, buf)); -} - inline int tty_paranoia_check(struct tty_struct *tty, kdev_t device, const char *routine) { @@ -213,11 +204,13 @@ static int tty_set_ldisc(struct tty_struct *tty, int ldisc) { int retval = 0; struct tty_ldisc o_ldisc; + char buf[64]; if ((ldisc < N_TTY) || (ldisc >= NR_LDISCS)) return -EINVAL; -#ifdef CONFIG_KERNELD +#ifdef CONFIG_KMOD /* Eduardo Blanco <ejbs@cs.cs.com.uy> */ + /* Cyrus Durgin <cider@speakeasy.org> */ if (!(ldiscs[ldisc].flags & LDISC_FLAG_DEFINED)) { char modname [20]; sprintf(modname, "tty-ldisc-%d", ldisc); @@ -254,7 +247,7 @@ static int tty_set_ldisc(struct tty_struct *tty, int ldisc) if (r < 0) panic("Couldn't open N_TTY ldisc for " "%s --- error %d.", - tty_name(tty), r); + tty_name(tty, buf), r); } } } @@ -455,7 +448,9 @@ void do_tty_hangup(void *data) void tty_hangup(struct tty_struct * tty) { #ifdef TTY_DEBUG_HANGUP - printk("%s hangup...\n", tty_name(tty)); + char buf[64]; + + printk("%s hangup...\n", tty_name(tty, buf)); #endif queue_task(&tty->tq_hangup, &tq_timer); } @@ -463,7 +458,9 @@ void tty_hangup(struct tty_struct * tty) void tty_vhangup(struct tty_struct * tty) { #ifdef TTY_DEBUG_HANGUP - printk("%s vhangup...\n", tty_name(tty)); + char buf[64]; + + printk("%s vhangup...\n", tty_name(tty, buf)); #endif do_tty_hangup((void *) tty); } @@ -950,6 +947,7 @@ static void release_dev(struct file * filp) struct tty_struct *tty, *o_tty; int pty_master, tty_closing, o_tty_closing, do_sleep; int idx; + char buf[64]; tty = (struct tty_struct *)filp->private_data; if (tty_paranoia_check(tty, filp->f_dentry->d_inode->i_rdev, "release_dev")) @@ -990,7 +988,7 @@ static void release_dev(struct file * filp) #endif #ifdef TTY_DEBUG_HANGUP - printk("release_dev of %s (tty count=%d)...", tty_name(tty), + printk("release_dev of %s (tty count=%d)...", tty_name(tty, buf), tty->count); #endif @@ -1068,7 +1066,7 @@ static void release_dev(struct file * filp) break; printk("release_dev: %s: read/write wait queue active!\n", - tty_name(tty)); + tty_name(tty, buf)); schedule(); } @@ -1084,13 +1082,13 @@ static void release_dev(struct file * filp) if (pty_master) { if (--o_tty->count < 0) { printk("release_dev: bad pty slave count (%d) for %s\n", - o_tty->count, tty_name(o_tty)); + o_tty->count, tty_name(o_tty, buf)); o_tty->count = 0; } } if (--tty->count < 0) { printk("release_dev: bad tty->count (%d) for %s\n", - tty->count, tty_name(tty)); + tty->count, tty_name(tty, buf)); tty->count = 0; } @@ -1178,6 +1176,7 @@ static int tty_open(struct inode * inode, struct file * filp) int noctty, retval; kdev_t device; unsigned short saved_flags; + char buf[64]; saved_flags = filp->f_flags; retry_open: @@ -1245,7 +1244,7 @@ init_dev_done: tty->driver.subtype == PTY_TYPE_MASTER) noctty = 1; #ifdef TTY_DEBUG_HANGUP - printk("opening %s...", tty_name(tty)); + printk("opening %s...", tty_name(tty, buf)); #endif if (tty->driver.open) retval = tty->driver.open(tty, filp); @@ -1258,7 +1257,8 @@ init_dev_done: if (retval) { #ifdef TTY_DEBUG_HANGUP - printk("error %d in opening %s...", retval, tty_name(tty)); + printk("error %d in opening %s...", retval, + tty_name(tty, buf)); #endif release_dev(filp); @@ -1282,6 +1282,11 @@ init_dev_done: tty->session = current->session; tty->pgrp = current->pgrp; } + if ((tty->driver.type == TTY_DRIVER_TYPE_SERIAL) && + (tty->driver.subtype == SERIAL_TYPE_CALLOUT)) { + printk("Warning, %s opened, is a deprecated tty " + "callout device\n", tty_name(tty, buf)); + } return 0; } diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c index 21c09384e..735a955fc 100644 --- a/drivers/char/tty_ioctl.c +++ b/drivers/char/tty_ioctl.c @@ -45,7 +45,9 @@ void tty_wait_until_sent(struct tty_struct * tty, int timeout) struct wait_queue wait = { current, NULL }; #ifdef TTY_DEBUG_WAIT_UNTIL_SENT - printk("%s wait until sent...\n", tty_name(tty)); + char buf[64]; + + printk("%s wait until sent...\n", tty_name(tty, buf)); #endif if (!tty->driver.chars_in_buffer) return; @@ -57,7 +59,8 @@ void tty_wait_until_sent(struct tty_struct * tty, int timeout) current->timeout = (unsigned) -1; do { #ifdef TTY_DEBUG_WAIT_UNTIL_SENT - printk("waiting %s...(%d)\n", tty_name(tty), tty->driver.chars_in_buffer(tty)); + printk("waiting %s...(%d)\n", tty_name(tty, buf), + tty->driver.chars_in_buffer(tty)); #endif current->state = TASK_INTERRUPTIBLE; if (signal_pending(current)) diff --git a/drivers/misc/parport_init.c b/drivers/misc/parport_init.c index 3fc222ff9..31753dc1a 100644 --- a/drivers/misc/parport_init.c +++ b/drivers/misc/parport_init.c @@ -17,7 +17,6 @@ #include <linux/kernel.h> #include <linux/malloc.h> #include <linux/init.h> -#include <linux/kerneld.h> #ifndef MODULE static int io[PARPORT_MAX+1] __initdata = { [0 ... PARPORT_MAX] = 0 }; diff --git a/drivers/misc/parport_share.c b/drivers/misc/parport_share.c index 91fb8b0dc..bc39b351c 100644 --- a/drivers/misc/parport_share.c +++ b/drivers/misc/parport_share.c @@ -1,4 +1,4 @@ -/* $Id: parport_share.c,v 1.9 1998/03/16 23:44:45 ralf Exp $ +/* $Id: parport_share.c,v 1.10 1998/03/18 06:32:19 ralf Exp $ * Parallel-port resource manager code. * * Authors: David Campbell <campbell@tirian.che.curtin.edu.au> @@ -28,8 +28,8 @@ #include <asm/spinlock.h> -#ifdef CONFIG_KERNELD -#include <linux/kerneld.h> +#ifdef CONFIG_KMOD +#include <linux/kmod.h> #endif #undef PARPORT_PARANOID @@ -44,14 +44,14 @@ void (*parport_probe_hook)(struct parport *port) = NULL; /* Return a list of all the ports we know about. */ struct parport *parport_enumerate(void) { -#ifdef CONFIG_KERNELD +#ifdef CONFIG_KMOD if (portlist == NULL) { request_module("parport_lowlevel"); #ifdef CONFIG_PNP_PARPORT_MODULE request_module("parport_probe"); #endif /* CONFIG_PNP_PARPORT_MODULE */ } -#endif /* CONFIG_KERNELD */ +#endif /* CONFIG_KMOD */ return portlist; } diff --git a/drivers/net/Config.in b/drivers/net/Config.in index ea97a5bc1..c3bcd09c5 100644 --- a/drivers/net/Config.in +++ b/drivers/net/Config.in @@ -49,10 +49,9 @@ if [ "$CONFIG_NET_ETHERNET" = "y" ]; then if [ "$CONFIG_NET_VENDOR_SMC" = "y" ]; then tristate 'WD80*3 support' CONFIG_WD80x3 if [ "$CONFIG_MCA" = "y" ]; then - tristate 'SMC Ultra MCA support' CONFIG_ULTRA - else - tristate 'SMC Ultra support' CONFIG_ULTRA + tristate 'SMC Ultra MCA support' CONFIG_ULTRAMCA fi + tristate 'SMC Ultra support' CONFIG_ULTRA tristate 'SMC Ultra32 EISA support' CONFIG_ULTRA32 tristate 'SMC 9194 support' CONFIG_SMC9194 fi diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 5856b0f35..8d3f1f40b 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -158,9 +158,6 @@ endif ifeq ($(CONFIG_ULTRA),y) L_OBJS += smc-ultra.o CONFIG_8390_BUILTIN = y - ifeq ($(CONFIG_MCA),y) - L_OBJS += smc-mca.o - endif else ifeq ($(CONFIG_ULTRA),m) CONFIG_8390_MODULE = y @@ -168,6 +165,16 @@ else endif endif +ifeq ($(CONFIG_ULTRAMCA),y) +L_OBJS += smc-mca.o +CONFIG_8390_BUILTIN = y +else + ifeq ($(CONFIG_ULTRAMCA),m) + CONFIG_8390_MODULE = y + M_OBJS += smc-mca.o + endif +endif + ifeq ($(CONFIG_ULTRA32),y) L_OBJS += smc-ultra32.o CONFIG_8390_BUILTIN = y diff --git a/drivers/net/Space.c b/drivers/net/Space.c index e5a98378b..9d880aa4d 100644 --- a/drivers/net/Space.c +++ b/drivers/net/Space.c @@ -43,6 +43,7 @@ extern int tulip_probe(struct device *dev); extern int hp100_probe(struct device *dev); extern int ultra_probe(struct device *dev); +extern int ultra32_probe(struct device *dev); extern int ultramca_probe(struct device *dev); extern int wd_probe(struct device *dev); extern int el2_probe(struct device *dev); @@ -135,13 +136,13 @@ __initfunc(static int ethif_probe(struct device *dev)) #endif #if defined(CONFIG_ULTRA) && ultra_probe(dev) -#if defined(CONFIG_MCA) +#endif +#if defined(CONFIG_ULTRAMCA) && ultramca_probe(dev) #endif #if defined(CONFIG_ULTRA32) && ultra32_probe(dev) #endif -#endif #if defined(CONFIG_SMC9194) && smc_init(dev) #endif diff --git a/drivers/net/de4x5.c b/drivers/net/de4x5.c index ed2652b96..6bd5be8b7 100644 --- a/drivers/net/de4x5.c +++ b/drivers/net/de4x5.c @@ -213,7 +213,7 @@ insmod de4x5 args='eth1:fdx autosense=BNC eth0:autosense=100Mb'. - For a compiled in driver, somewhere in this file, place e.g. + For a compiled in driver, at or above line 526, place e.g. #define DE4X5_PARM "eth0:fdx autosense=AUI eth2:autosense=TP" Yes, I know full duplex isn't permissible on BNC or AUI; they're just @@ -371,11 +371,14 @@ 0.533 9-Jan-98 Fix more 64 bit bugs reported by <jal@cs.brown.edu>. 0.534 24-Jan-98 Fix last (?) endian bug from <Geert.Uytterhoeven@cs.kuleuven.ac.be> + 0.535 21-Feb-98 Fix Ethernet Address PROM reset bug for DC21040. + 0.536 21-Mar-98 Change pci_probe() to use the pci_dev structure. + **Incompatible with 2.0.x from here.** ========================================================================= */ -static const char *version = "de4x5.c:V0.534 1998/1/24 davies@maniac.ultranet.com\n"; +static const char *version = "de4x5.c:V0.536 1998/3/5 davies@maniac.ultranet.com\n"; #include <linux/module.h> @@ -390,11 +393,15 @@ static const char *version = "de4x5.c:V0.534 1998/1/24 davies@maniac.ultranet.co #include <linux/bios32.h> #include <linux/pci.h> #include <linux/delay.h> +#include <linux/init.h> +#include <linux/version.h> + #include <asm/bitops.h> #include <asm/io.h> #include <asm/dma.h> #include <asm/byteorder.h> #include <asm/unaligned.h> +#include <asm/uaccess.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> @@ -408,31 +415,6 @@ static const char *version = "de4x5.c:V0.534 1998/1/24 davies@maniac.ultranet.co #include "de4x5.h" #define c_char const char - -#include <linux/version.h> -#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,0) -# define __initfunc(__arginit) __arginit -# define test_and_set_bit set_bit -# define net_device_stats enet_statistics -# define copy_to_user(a,b,c) memcpy_tofs(a,b,c) -# define copy_from_user(a,b,c) memcpy_fromfs(a,b,c) -# define le16_to_cpu(a) cpu_to_le16(a) -# define le32_to_cpu(a) cpu_to_le32(a) -# ifdef __powerpc__ -# define cpu_to_le16(a) ((((a) & 0x00ffU) << 8) | (((a) & 0xff00U) >> 8)) -# define cpu_to_le32(a) ((((a) & 0x000000ffU) << 24) |\ - (((a) & 0x0000ff00U) << 8) |\ - (((a) & 0x00ff0000U) >> 8) |\ - (((a) & 0xff000000U) >> 24)) -# else -# define cpu_to_le16(a) (a) -# define cpu_to_le32(a) (a) -# endif /* __powerpc__ */ -# include <asm/segment.h> -#else -# include <asm/uaccess.h> -# include <linux/init.h> -#endif /* LINUX_VERSION_CODE */ #define TWIDDLE(a) (u_short)le16_to_cpu(get_unaligned((u_short *)(a))) /* @@ -538,8 +520,8 @@ static int de4x5_debug = (DEBUG_MEDIA | DEBUG_VERSION); ** insmod de4x5 args='eth1:fdx autosense=BNC eth0:autosense=100Mb'. ** ** For a compiled in driver, place e.g. -** #define DE4X5_PARM "eth0:fdx autosense=AUI eth2:autosense=TP" -** somewhere in this file above this point. +** #define DE4X5_PARM "eth0:fdx autosense=AUI eth2:autosense=TP" +** here */ #ifdef DE4X5_PARM static char *args = DE4X5_PARM; @@ -592,6 +574,12 @@ struct parameters { #define DE4X5_NAME_LENGTH 8 /* +** Ethernet PROM defines for DC21040 +*/ +#define PROBE_LENGTH 32 +#define ETH_PROM_SIG 0xAA5500FFUL + +/* ** PCI Bus defines */ #define PCI_MAX_BUS_NUM 8 @@ -912,6 +900,7 @@ static int test_tp(struct device *dev, s32 msec); static int EISA_signature(char *name, s32 eisa_id); static int PCI_signature(char *name, struct bus_type *lp); static void DevicePresent(u_long iobase); +static void enet_addr_rst(u_long aprom_addr); static int de4x5_bad_srom(struct bus_type *lp); static short srom_rd(u_long address, u_char offset); static void srom_latch(u_int command, u_long address); @@ -981,11 +970,9 @@ static struct device *insert_device(struct device *dev, u_long iobase, int (*init)(struct device *)); static int count_adapters(void); static int loading_module = 1; -#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0) MODULE_PARM(de4x5_debug, "i"); MODULE_PARM(dec_only, "i"); MODULE_PARM(args, "s"); -#endif /* LINUX_VERSION_CODE */ # else static int loading_module = 0; #endif /* MODULE */ @@ -1507,9 +1494,7 @@ de4x5_queue_pkt(struct sk_buff *skb, struct device *dev) cli(); test_and_set_bit(0, (void*)&dev->tbusy); load_packet(dev, skb->data, TD_IC | TD_LS | TD_FS | skb->len, skb); -#if LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8)) lp->stats.tx_bytes += skb->len; -#endif outl(POLL_DEMAND, DE4X5_TPD);/* Start the TX */ lp->tx_new = (++lp->tx_new) % lp->txRingSize; @@ -1559,9 +1544,7 @@ de4x5_interrupt(int irq, void *dev_id, struct pt_regs *regs) printk("%s: Re-entering the interrupt handler.\n", dev->name); DISABLE_IRQs; /* Ensure non re-entrancy */ -#if LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8)) synchronize_irq(); -#endif dev->interrupt = MASK_INTERRUPTS; for (limit=0; limit<8; limit++) { @@ -1659,9 +1642,7 @@ de4x5_rx(struct device *dev) /* Update stats */ lp->stats.rx_packets++; -#if LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8)) lp->stats.rx_bytes += pkt_len; -#endif de4x5_local_stats(dev, skb->data, pkt_len); } } @@ -2042,7 +2023,7 @@ eisa_probe(struct device *dev, u_long ioaddr)) outl(0x00006000, PCI_CFLT); outl(iobase, PCI_CBIO); - DevicePresent(DE4X5_APROM); + DevicePresent(EISA_APROM); if (check_region(iobase, DE4X5_EISA_TOTAL_SIZE) == 0) { dev->irq = irq; if ((status = de4x5_hw_init(dev, iobase)) == 0) { @@ -2081,11 +2062,12 @@ eisa_probe(struct device *dev, u_long ioaddr)) __initfunc(static void pci_probe(struct device *dev, u_long ioaddr)) { - u_char pb, pbus, dev_num, dnum, dev_fn, timer, tirq; - u_short dev_id, vendor, index, status; - u_int tmp, irq = 0, device, class = DE4X5_CLASS_CODE; + u_char pb, pbus, dev_num, dnum, dev_fn, timer; + u_short vendor, index, status; + u_int irq = 0, device, class = DE4X5_CLASS_CODE; u_long iobase = 0; /* Clear upper 32 bits in Alphas */ struct bus_type *lp = &bus; + struct pci_dev *pdev; if (lastPCI == NO_MORE_PCI) return; @@ -2109,96 +2091,77 @@ pci_probe(struct device *dev, u_long ioaddr)) (pcibios_find_class(class, index, &pb, &dev_fn)!= PCIBIOS_DEVICE_NOT_FOUND); index++) { dev_num = PCI_SLOT(dev_fn); - if ((!pbus && !dnum) || ((pbus == pb) && (dnum == dev_num))) { -#ifdef __sparc_v9__ - struct pci_dev *pdev; - for (pdev = pci_devices; pdev; pdev = pdev->next) { - if ((pdev->bus->number==pb) && (pdev->devfn==dev_fn)) break; - } -#endif - device = 0; - pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor); - pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &dev_id); - device = dev_id; - device <<= 8; - if (!(is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)) { - continue; - } - - /* Search for an SROM on this bus */ - if (lp->bus_num != pb) { - lp->bus_num = pb; - srom_search(index); - } + if ((pbus || dnum) && ((pbus != pb) || (dnum != dev_num))) continue; + for (pdev = pci_devices; pdev; pdev = pdev->next) { + if ((pdev->bus->number==pb) && (pdev->devfn==dev_fn)) break; + } - /* Get the chip configuration revision register */ - pcibios_read_config_dword(pb, PCI_DEVICE, PCI_REVISION_ID, &cfrv); + vendor = pdev->vendor; + device = pdev->device << 8; + if (!(is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)) continue; - /* Set the device number information */ - lp->device = dev_num; + /* Search for an SROM on this bus */ + if (lp->bus_num != pb) { lp->bus_num = pb; + srom_search(index); + } + + /* Get the chip configuration revision register */ + pcibios_read_config_dword(pb, PCI_DEVICE, PCI_REVISION_ID, &cfrv); + + /* Set the device number information */ + lp->device = dev_num; + lp->bus_num = pb; - /* Set the chipset information */ - if (is_DC2114x) device |= (cfrv & CFRV_RN); - lp->chipset = device; + /* Set the chipset information */ + if (is_DC2114x) device |= (cfrv & CFRV_RN); + lp->chipset = device; - /* Get the board I/O address (64 bits on sparc64) */ -#ifndef __sparc_v9__ - pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &tmp); - iobase = tmp; -#else - iobase = pdev->base_address[0]; -#endif - iobase &= CBIO_MASK; + /* Get the board I/O address (64 bits on sparc64) */ + iobase = pdev->base_address[0] & CBIO_MASK; - /* Fetch the IRQ to be used */ -#ifndef __sparc_v9__ - pcibios_read_config_byte(pb, PCI_DEVICE, PCI_INTERRUPT_LINE, &tirq); - irq = tirq; -#else - irq = pdev->irq; -#endif - if ((irq == 0) || (irq == 0xff) || ((int)irq == -1)) continue; + /* Fetch the IRQ to be used */ + irq = pdev->irq; + if ((irq == 0) || (irq == 0xff) || ((int)irq == -1)) continue; - /* Check if I/O accesses and Bus Mastering are enabled */ - pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status); + /* Check if I/O accesses and Bus Mastering are enabled */ + pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status); #ifdef __powerpc__ - if (!(status & PCI_COMMAND_IO)) { - status |= PCI_COMMAND_IO; - pcibios_write_config_word(pb, PCI_DEVICE, PCI_COMMAND, status); - pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status); - } + if (!(status & PCI_COMMAND_IO)) { + status |= PCI_COMMAND_IO; + pcibios_write_config_word(pb, PCI_DEVICE, PCI_COMMAND, status); + pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status); + } #endif /* __powerpc__ */ - if (!(status & PCI_COMMAND_IO)) continue; + if (!(status & PCI_COMMAND_IO)) continue; - if (!(status & PCI_COMMAND_MASTER)) { - status |= PCI_COMMAND_MASTER; - pcibios_write_config_word(pb, PCI_DEVICE, PCI_COMMAND, status); - pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status); - } - if (!(status & PCI_COMMAND_MASTER)) continue; + if (!(status & PCI_COMMAND_MASTER)) { + status |= PCI_COMMAND_MASTER; + pcibios_write_config_word(pb, PCI_DEVICE, PCI_COMMAND, status); + pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status); + } + if (!(status & PCI_COMMAND_MASTER)) continue; - /* Check the latency timer for values >= 0x60 */ - pcibios_read_config_byte(pb, PCI_DEVICE, PCI_LATENCY_TIMER, &timer); - if (timer < 0x60) { - pcibios_write_config_byte(pb, PCI_DEVICE, PCI_LATENCY_TIMER, 0x60); - } + /* Check the latency timer for values >= 0x60 */ + pcibios_read_config_byte(pb, PCI_DEVICE, PCI_LATENCY_TIMER, &timer); + if (timer < 0x60) { + pcibios_write_config_byte(pb, PCI_DEVICE, PCI_LATENCY_TIMER, 0x60); + } - DevicePresent(DE4X5_APROM); - if (check_region(iobase, DE4X5_PCI_TOTAL_SIZE) == 0) { - dev->irq = irq; - if ((status = de4x5_hw_init(dev, iobase)) == 0) { - num_de4x5s++; - if (loading_module) { - link_modules(lastModule, dev); - lastPCI = index; - } - return; + DevicePresent(DE4X5_APROM); + if (check_region(iobase, DE4X5_PCI_TOTAL_SIZE) == 0) { + dev->irq = irq; + if ((status = de4x5_hw_init(dev, iobase)) == 0) { + num_de4x5s++; + if (loading_module) { + link_modules(lastModule, dev); + lastPCI = index; } - } else if (ioaddr != 0) { - printk("%s: region already allocated at 0x%04lx.\n", dev->name, - iobase); + return; } + } else if (ioaddr != 0) { + printk("%s: region already allocated at 0x%04lx.\n", dev->name, + iobase); } } @@ -2216,12 +2179,13 @@ pci_probe(struct device *dev, u_long ioaddr)) __initfunc(static void srom_search(int index)) { - u_char pb, dev_fn, tirq; - u_short dev_id, dev_num, vendor, status; - u_int tmp, irq = 0, device, class = DE4X5_CLASS_CODE; + u_char pb, dev_fn; + u_short dev_num, vendor, status; + u_int irq = 0, device, class = DE4X5_CLASS_CODE; u_long iobase = 0; /* Clear upper 32 bits in Alphas */ int i, j; struct bus_type *lp = &bus; + struct pci_dev *pdev; for (; (pcibios_find_class(class, index, &pb, &dev_fn)!= PCIBIOS_DEVICE_NOT_FOUND); @@ -2229,20 +2193,13 @@ srom_search(int index)) if (lp->bus_num != pb) return; dev_num = PCI_SLOT(dev_fn); -#ifdef __sparc_v9__ - struct pci_dev *pdev; for (pdev = pci_devices; pdev; pdev = pdev->next) { if ((pdev->bus->number == pb) && (pdev->devfn == dev_fn)) break; } -#endif - device = 0; - pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor); - pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &dev_id); - device = dev_id; - device <<= 8; - if (!(is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)) { - continue; - } + + vendor = pdev->vendor; + device = pdev->device << 8; + if (!(is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)) continue; /* Get the chip configuration revision register */ pcibios_read_config_dword(pb, PCI_DEVICE, PCI_REVISION_ID, &cfrv); @@ -2256,21 +2213,10 @@ srom_search(int index)) lp->chipset = device; /* Get the board I/O address (64 bits on sparc64) */ -#ifndef __sparc_v9__ - pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &tmp); - iobase = tmp; -#else - iobase = pdev->base_address[0]; -#endif - iobase &= CBIO_MASK; + iobase = pdev->base_address[0] & CBIO_MASK; /* Fetch the IRQ to be used */ -#ifndef __sparc_v9__ - pcibios_read_config_byte(pb, PCI_DEVICE, PCI_INTERRUPT_LINE, &tirq); - irq = tirq; -#else irq = pdev->irq; -#endif if ((irq == 0) || (irq == 0xff) || ((int)irq == -1)) continue; /* Check if I/O accesses are enabled */ @@ -3982,7 +3928,11 @@ DevicePresent(u_long aprom_addr) struct bus_type *lp = &bus; if (lp->chipset == DC21040) { - outl(0, aprom_addr); /* Reset Ethernet Address ROM Pointer */ + if (lp->bus == EISA) { + enet_addr_rst(aprom_addr); /* Reset Ethernet Address ROM Pointer */ + } else { + outl(0, aprom_addr); /* Reset Ethernet Address ROM Pointer */ + } } else { /* Read new srom */ u_short tmp, *p = (short *)((char *)&lp->srom + SROM_HWADD); for (i=0; i<(ETH_ALEN>>1); i++) { @@ -4006,6 +3956,45 @@ DevicePresent(u_long aprom_addr) } /* +** Since the write on the Enet PROM register doesn't seem to reset the PROM +** pointer correctly (at least on my DE425 EISA card), this routine should do +** it...from depca.c. +*/ +static void +enet_addr_rst(u_long aprom_addr) +{ + union { + struct { + u32 a; + u32 b; + } llsig; + char Sig[sizeof(u32) << 1]; + } dev; + short sigLength=0; + s8 data; + int i, j; + + dev.llsig.a = ETH_PROM_SIG; + dev.llsig.b = ETH_PROM_SIG; + sigLength = sizeof(u32) << 1; + + for (i=0,j=0;j<sigLength && i<PROBE_LENGTH+sigLength-1;i++) { + data = inb(aprom_addr); + if (dev.Sig[j] == data) { /* track signature */ + j++; + } else { /* lost signature; begin search again */ + if (data == dev.Sig[0]) { /* rare case.... */ + j=1; + } else { + j=0; + } + } + } + + return; +} + +/* ** For the bad status case and no SROM, then add one to the previous ** address. However, need to add one backwards in case we have 0xff ** as one or more of the bytes. Only the last 3 bytes should be checked @@ -5665,9 +5654,7 @@ de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd) #define LP(a) ((struct de4x5_private *)(a)) static struct device *mdev = NULL; static int io=0x0;/* EDIT THIS LINE FOR YOUR CONFIGURATION IF NEEDED */ -#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0) MODULE_PARM(io, "i"); -#endif /* LINUX_VERSION_CODE */ int init_module(void) @@ -5730,16 +5717,18 @@ unlink_modules(struct device *p) static int count_adapters(void) { - int i, j; + int i, j=0; char name[DE4X5_STRLEN]; - u_char pb, dev_fn, dev_num; - u_short dev_id, vendor; + u_char pb, dev_fn; + u_short vendor; u_int class = DE4X5_CLASS_CODE; u_int device; + struct pci_dev *pdev; + #ifndef __sparc_v9__ u_long iobase = 0x1000; - for (j=0, i=1; i<MAX_EISA_SLOTS; i++, iobase+=EISA_SLOT_INC) { + for (i=1; i<MAX_EISA_SLOTS; i++, iobase+=EISA_SLOT_INC) { if (EISA_signature(name, EISA_ID)) j++; } #endif @@ -5748,12 +5737,12 @@ count_adapters(void) for (i=0; (pcibios_find_class(class, i, &pb, &dev_fn)!= PCIBIOS_DEVICE_NOT_FOUND); i++) { - dev_num = PCI_SLOT(dev_fn); - device = 0; - pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor); - pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &dev_id); - device = dev_id; - device <<= 8; + for (pdev = pci_devices; pdev; pdev = pdev->next) { + if ((pdev->bus->number==pb) && (pdev->devfn==dev_fn)) break; + } + + vendor = pdev->vendor; + device = pdev->device << 8; if (is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x) j++; } diff --git a/drivers/net/eepro100.c b/drivers/net/eepro100.c index 9a33ea3a4..156a07ba2 100644 --- a/drivers/net/eepro100.c +++ b/drivers/net/eepro100.c @@ -1348,7 +1348,7 @@ speedo_rx(struct device *dev) #if (LINUX_VERSION_CODE >= VERSION(1,3,44)) if (! rx_in_place) { skb_reserve(skb, 2); /* 16 byte align the data fields */ -#if defined(__i386) && notyet +#if defined(__i386__) && notyet /* Packet is in one chunk -- we can copy + cksum. */ eth_io_copy_and_sum(skb, bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr), pkt_len, 0); diff --git a/drivers/net/hp100.c b/drivers/net/hp100.c index cb6b1bb69..0f1bdd3c7 100644 --- a/drivers/net/hp100.c +++ b/drivers/net/hp100.c @@ -1,51 +1,77 @@ /* - ** hp100.c - ** HP CASCADE Architecture Driver for 100VG-AnyLan Network Adapters - ** - ** $Id: hp100.c,v 1.14 1997/11/16 13:57:28 alan Exp $ - ** - ** Based on the HP100 driver written by Jaroslav Kysela <perex@jcu.cz> - ** Extended for new busmaster capable chipsets by - ** Siegfried "Frieder" Loeffler (dg1sek) <floeff@mathematik.uni-stuttgart.de> - ** - ** Maintained by: Jaroslav Kysela <perex@jcu.cz> - ** - ** This driver has only been tested with - ** -- HP J2585B 10/100 Mbit/s PCI Busmaster - ** -- HP J2585A 10/100 Mbit/s PCI - ** -- HP J2970 10 Mbit/s PCI Combo 10base-T/BNC - ** -- HP J2973 10 Mbit/s PCI 10base-T - ** -- HP J2573 10/100 ISA - ** -- Compex ReadyLink ENET100-VG4 10/100 Mbit/s PCI / EISA - ** - ** but it should also work with the other CASCADE based adapters. - ** - ** TODO: - ** - J2573 seems to hang sometimes when in shared memory mode. - ** - Mode for Priority TX - ** - Check PCI registers, performance might be improved? - ** - To reduce interrupt load in busmaster, one could switch off - ** the interrupts that are used to refill the queues whenever the - ** queues are filled up to more than a certain threshold. - ** - ** - ** This source/code is public free; you can distribute it and/or modify - ** it under terms of the GNU General Public License (published by the - ** Free Software Foundation) either version two of this License, or any - ** later version. - ** - */ - -#define HP100_DEFAULT_PRIORITY_TX 0 +** hp100.c +** HP CASCADE Architecture Driver for 100VG-AnyLan Network Adapters +** +** $Id: hp100.c,v 1.56 1998/03/04 15:23:59 perex Exp perex $ +** +** Based on the HP100 driver written by Jaroslav Kysela <perex@jcu.cz> +** Extended for new busmaster capable chipsets by +** Siegfried "Frieder" Loeffler (dg1sek) <floeff@mathematik.uni-stuttgart.de> +** +** Maintained by: Jaroslav Kysela <perex@jcu.cz> +** +** This driver has only been tested with +** -- HP J2585B 10/100 Mbit/s PCI Busmaster +** -- HP J2585A 10/100 Mbit/s PCI +** -- HP J2970 10 Mbit/s PCI Combo 10base-T/BNC +** -- HP J2973 10 Mbit/s PCI 10base-T +** -- HP J2573 10/100 ISA +** -- Compex ReadyLink ENET100-VG4 10/100 Mbit/s PCI / EISA +** -- Compex FreedomLine 100/VG 10/100 Mbit/s ISA / EISA / PCI +** +** but it should also work with the other CASCADE based adapters. +** +** TODO: +** - J2573 seems to hang sometimes when in shared memory mode. +** - Mode for Priority TX +** - Check PCI registers, performance might be improved? +** - To reduce interrupt load in busmaster, one could switch off +** the interrupts that are used to refill the queues whenever the +** queues are filled up to more than a certain threshold. +** - some updates for EISA version of card +** +** +** This source/code is public free; you can distribute it and/or modify +** it under terms of the GNU General Public License (published by the +** Free Software Foundation) either version two of this License, or any +** later version. +** +** 1.55 -> 1.56 +** - removed printk in misc. interrupt and update statistics to allow +** monitoring of card status +** - timing changes in xmit routines, relogin to 100VG hub added when +** driver does reset +** - included fix for Compex FreedomLine PCI adapter +** +** 1.54 -> 1.55 +** - fixed bad initialization in init_module +** - added Compex FreedomLine adapter +** - some fixes in card initialization +** +** 1.53 -> 1.54 +** - added hardware multicast filter support (doesn't work) +** - little changes in hp100_sense_lan routine +** - added support for Coax and AUI (J2970) +** - fix for multiple cards and hp100_mode parameter (insmod) +** - fix for shared IRQ +** +** 1.52 -> 1.53 +** - fixed bug in multicast support +** +*/ + +#define HP100_DEFAULT_PRIORITY_TX 0 #undef HP100_DEBUG -#undef HP100_DEBUG_B /* Trace */ -#undef HP100_DEBUG_BM /* Debug busmaster code (PDL stuff) */ +#undef HP100_DEBUG_B /* Trace */ +#undef HP100_DEBUG_BM /* Debug busmaster code (PDL stuff) */ -#undef HP100_DEBUG_TRAINING /* Debug login-to-hub procedure */ -#undef HP100_DEBUG_TX -#undef HP100_DEBUG_IRQ -#undef HP100_DEBUG_RX +#undef HP100_DEBUG_TRAINING /* Debug login-to-hub procedure */ +#undef HP100_DEBUG_TX +#undef HP100_DEBUG_IRQ +#undef HP100_DEBUG_RX + +#undef HP100_MULTICAST_FILTER /* Need to be debugged... */ #include <linux/version.h> #include <linux/module.h> @@ -58,7 +84,6 @@ #include <linux/interrupt.h> #include <linux/pci.h> #include <linux/bios32.h> -#include <linux/init.h> #include <asm/bitops.h> #include <asm/io.h> @@ -67,7 +92,7 @@ #include <linux/skbuff.h> #include <linux/types.h> -#include <linux/config.h> /* for CONFIG_PCI */ +#include <linux/config.h> /* for CONFIG_PCI */ #include <linux/delay.h> #if LINUX_VERSION_CODE < 0x020100 @@ -79,6 +104,12 @@ typedef struct enet_statistics hp100_stats_t; typedef struct net_device_stats hp100_stats_t; #endif +#ifndef __initfunc +#define __initfunc(__initarg) __initarg +#else +#include <linux/init.h> +#endif + #include "hp100.h" /* @@ -98,8 +129,14 @@ typedef struct net_device_stats hp100_stats_t; #ifndef PCI_DEVICE_ID_COMPEX_ENET100VG4 #define PCI_DEVICE_ID_COMPEX_ENET100VG4 0x0112 #endif +#ifndef PCI_VENDOR_ID_COMPEX2 +#define PCI_VENDOR_ID_COMPEX2 0x101a +#endif +#ifndef PCI_DEVICE_ID_COMPEX2_100VG +#define PCI_DEVICE_ID_COMPEX2_100VG 0x0005 +#endif -#define HP100_REGION_SIZE 0x20 /* for ioports */ +#define HP100_REGION_SIZE 0x20 /* for ioports */ #define HP100_MAX_PACKET_SIZE (1536+4) #define HP100_MIN_PACKET_SIZE 60 @@ -119,135 +156,167 @@ typedef struct net_device_stats hp100_stats_t; */ struct hp100_eisa_id { - u_int id; - const char *name; - u_char bus; + u_int id; + const char *name; + u_char bus; +}; + +struct hp100_pci_id { + u_short vendor; + u_short device; }; struct hp100_private { - struct hp100_eisa_id *id; - u_short chip; - u_short soft_model; - u_int memory_size; - u_short rx_ratio; /* 1 - 99 */ - u_short priority_tx; /* != 0 - priority tx */ - u_short mode; /* PIO, Shared Mem or Busmaster */ - u_char bus; - u_char pci_bus; - u_char pci_device_fn; - short mem_mapped; /* memory mapped access */ - u_int *mem_ptr_virt; /* virtual memory mapped area, maybe NULL */ - u_int *mem_ptr_phys; /* physical memory mapped area */ - short lan_type; /* 10Mb/s, 100Mb/s or -1 (error) */ - int hub_status; /* was login to hub successful? */ - u_char mac1_mode; - u_char mac2_mode; - hp100_stats_t stats; - - /* Rings for busmaster mode: */ - hp100_ring_t *rxrhead; /* Head (oldest) index into rxring */ - hp100_ring_t *rxrtail; /* Tail (newest) index into rxring */ - hp100_ring_t *txrhead; /* Head (oldest) index into txring */ - hp100_ring_t *txrtail; /* Tail (newest) index into txring */ - - hp100_ring_t rxring[MAX_RX_PDL]; - hp100_ring_t txring[MAX_TX_PDL]; - - u_int *page_vaddr; /* Virtual address of allocated page */ - u_int *page_vaddr_algn; /* Aligned virtual address of allocated page */ - int rxrcommit; /* # Rx PDLs commited to adapter */ - int txrcommit; /* # Tx PDLs commited to adapter */ + struct hp100_eisa_id *id; + u_short chip; + u_short soft_model; + u_int memory_size; + u_int virt_memory_size; + u_short rx_ratio; /* 1 - 99 */ + u_short priority_tx; /* != 0 - priority tx */ + u_short mode; /* PIO, Shared Mem or Busmaster */ + u_char bus; + u_char pci_bus; + u_char pci_device_fn; + short mem_mapped; /* memory mapped access */ + u_int *mem_ptr_virt; /* virtual memory mapped area, maybe NULL */ + u_int *mem_ptr_phys; /* physical memory mapped area */ + short lan_type; /* 10Mb/s, 100Mb/s or -1 (error) */ + int hub_status; /* was login to hub successful? */ + u_char mac1_mode; + u_char mac2_mode; + u_char hash_bytes[ 8 ]; + hp100_stats_t stats; + + /* Rings for busmaster mode: */ + hp100_ring_t *rxrhead; /* Head (oldest) index into rxring */ + hp100_ring_t *rxrtail; /* Tail (newest) index into rxring */ + hp100_ring_t *txrhead; /* Head (oldest) index into txring */ + hp100_ring_t *txrtail; /* Tail (newest) index into txring */ + + hp100_ring_t rxring[ MAX_RX_PDL ]; + hp100_ring_t txring[ MAX_TX_PDL ]; + + u_int *page_vaddr; /* Virtual address of allocated page */ + u_int *page_vaddr_algn; /* Aligned virtual address of allocated page */ + int rxrcommit; /* # Rx PDLs commited to adapter */ + int txrcommit; /* # Tx PDLs commited to adapter */ }; /* * variables */ -static struct hp100_eisa_id hp100_eisa_ids[] = -{ +static struct hp100_eisa_id hp100_eisa_ids[] = { /* 10/100 EISA card with revision A Cascade chip */ - {0x80F1F022, "HP J2577 rev A", HP100_BUS_EISA}, + { 0x80F1F022, "HP J2577 rev A", HP100_BUS_EISA }, /* 10/100 ISA card with revision A Cascade chip */ - {0x50F1F022, "HP J2573 rev A", HP100_BUS_ISA}, + { 0x50F1F022, "HP J2573 rev A", HP100_BUS_ISA }, /* 10 only EISA card with Cascade chip */ - {0x2019F022, "HP 27248B", HP100_BUS_EISA}, + { 0x2019F022, "HP 27248B", HP100_BUS_EISA }, /* 10/100 EISA card with Cascade chip */ - {0x4019F022, "HP J2577", HP100_BUS_EISA}, + { 0x4019F022, "HP J2577", HP100_BUS_EISA }, /* 10/100 ISA card with Cascade chip */ - {0x5019F022, "HP J2573", HP100_BUS_ISA}, + { 0x5019F022, "HP J2573", HP100_BUS_ISA }, /* 10/100 PCI card - old J2585A */ - {0x1030103c, "HP J2585A", HP100_BUS_PCI}, + { 0x1030103c, "HP J2585A", HP100_BUS_PCI }, /* 10/100 PCI card - new J2585B - master capable */ - {0x1041103c, "HP J2585B", HP100_BUS_PCI}, + { 0x1041103c, "HP J2585B", HP100_BUS_PCI }, /* 10 Mbit Combo Adapter */ - {0x1042103c, "HP J2970", HP100_BUS_PCI}, + { 0x1042103c, "HP J2970", HP100_BUS_PCI }, /* 10 Mbit 10baseT Adapter */ - {0x1040103c, "HP J2973", HP100_BUS_PCI}, + { 0x1040103c, "HP J2973", HP100_BUS_PCI }, /* 10/100 EISA card from Compex */ - {0x0103180e, "ReadyLink ENET100-VG4", HP100_BUS_EISA}, - + { 0x0103180e, "ReadyLink ENET100-VG4", HP100_BUS_EISA }, + + /* 10/100 EISA card from Compex - FreedomLine (sq5bpf) */ + /* Note: plhbrod@mbox.vol.cz reported that same ID have ISA */ + /* version of adapter, too... */ + { 0x0104180e, "FreedomLine 100/VG", HP100_BUS_EISA }, + + /* 10/100 PCI card from Compex - FreedomLine + * + * I think this card doesn't like aic7178 scsi controller, but + * I haven't tested this much. It works fine on diskless machines. + * Jacek Lipkowski <sq5bpf@acid.ch.pw.edu.pl> + */ + { 0x021211f6, "FreedomLine 100/VG", HP100_BUS_PCI }, + /* 10/100 PCI card from Compex (J2585A compatible) */ - {0x011211f6, "ReadyLink ENET100-VG4", HP100_BUS_PCI} + { 0x011211f6, "ReadyLink ENET100-VG4", HP100_BUS_PCI } + }; +#define HP100_EISA_IDS_SIZE (sizeof(hp100_eisa_ids)/sizeof(struct hp100_eisa_id)) + +static struct hp100_pci_id hp100_pci_ids[] = { + { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_J2585A }, + { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_J2585B }, + { PCI_VENDOR_ID_COMPEX, PCI_DEVICE_ID_COMPEX_ENET100VG4 }, + { PCI_VENDOR_ID_COMPEX2, PCI_DEVICE_ID_COMPEX2_100VG } +}; + +#define HP100_PCI_IDS_SIZE (sizeof(hp100_pci_ids)/sizeof(struct hp100_pci_id)) + static int hp100_rx_ratio = HP100_DEFAULT_RX_RATIO; static int hp100_priority_tx = HP100_DEFAULT_PRIORITY_TX; static int hp100_mode = 1; #ifdef LINUX_2_1 -MODULE_PARM(hp100_rx_ratio, "1i"); -MODULE_PARM(hp100_priority_tx, "1i"); -MODULE_PARM(hp100_mode, "1i"); +MODULE_PARM( hp100_rx_ratio, "1i" ); +MODULE_PARM( hp100_priority_tx, "1i" ); +MODULE_PARM( hp100_mode, "1i" ); #endif /* * prototypes */ -static int hp100_probe1(struct device *dev, int ioaddr, u_char bus, u_char pci_bus, u_char pci_device_fn); -static int hp100_open(struct device *dev); -static int hp100_close(struct device *dev); -static int hp100_start_xmit(struct sk_buff *skb, struct device *dev); -static int hp100_start_xmit_bm(struct sk_buff *skb, struct device *dev); -static void hp100_rx(struct device *dev); -static hp100_stats_t *hp100_get_stats(struct device *dev); -static void hp100_update_stats(struct device *dev); -static void hp100_clear_stats(int ioaddr); -static void hp100_set_multicast_list(struct device *dev); -static void hp100_interrupt(int irq, void *dev_id, struct pt_regs *regs); -static void hp100_start_interface(struct device *dev); -static void hp100_stop_interface(struct device *dev); -static void hp100_load_eeprom(struct device *dev); -static int hp100_sense_lan(struct device *dev); -static int hp100_login_to_vg_hub(struct device *dev, u_short force_relogin); -static int hp100_down_vg_link(struct device *dev); -static void hp100_cascade_reset(struct device *dev, u_short enable); -static void hp100_BM_shutdown(struct device *dev); -static void hp100_mmuinit(struct device *dev); -static void hp100_init_pdls(struct device *dev); -static int hp100_init_rxpdl(register hp100_ring_t * ringptr, register u_int * pdlptr); -static int hp100_init_txpdl(register hp100_ring_t * ringptr, register u_int * pdlptr); -static void hp100_rxfill(struct device *dev); -static void hp100_hwinit(struct device *dev); -static void hp100_clean_txring(struct device *dev); +static int hp100_probe1( struct device *dev, int ioaddr, u_char bus, u_char pci_bus, u_char pci_device_fn ); +static int hp100_open( struct device *dev ); +static int hp100_close( struct device *dev ); +static int hp100_start_xmit( struct sk_buff *skb, struct device *dev ); +static int hp100_start_xmit_bm (struct sk_buff *skb, struct device *dev ); +static void hp100_rx( struct device *dev ); +static hp100_stats_t *hp100_get_stats( struct device *dev ); +static void hp100_misc_interrupt( struct device *dev ); +static void hp100_update_stats( struct device *dev ); +static void hp100_clear_stats( int ioaddr ); +static void hp100_set_multicast_list( struct device *dev); +static void hp100_interrupt( int irq, void *dev_id, struct pt_regs *regs ); +static void hp100_start_interface( struct device *dev ); +static void hp100_stop_interface( struct device *dev ); +static void hp100_load_eeprom( struct device *dev, u_short ioaddr ); +static int hp100_sense_lan( struct device *dev ); +static int hp100_login_to_vg_hub( struct device *dev, u_short force_relogin ); +static int hp100_down_vg_link( struct device *dev ); +static void hp100_cascade_reset( struct device *dev, u_short enable ); +static void hp100_BM_shutdown( struct device *dev ); +static void hp100_mmuinit( struct device *dev ); +static void hp100_init_pdls( struct device *dev ); +static int hp100_init_rxpdl( struct device *dev, register hp100_ring_t *ringptr, register u_int *pdlptr); +static int hp100_init_txpdl( struct device *dev, register hp100_ring_t *ringptr, register u_int *pdlptr); +static void hp100_rxfill( struct device *dev ); +static void hp100_hwinit( struct device *dev ); +static void hp100_clean_txring( struct device *dev ); #ifdef HP100_DEBUG -static void hp100_RegisterDump(struct device *dev); +static void hp100_RegisterDump( struct device *dev ); #endif /* TODO: This function should not really be needed in a good design... */ -static void wait(void) +static void wait( void ) { - udelay(1000); + udelay( 1000 ); } /* @@ -255,863 +324,984 @@ static void wait(void) * These functions should - if possible - avoid doing write operations * since this could cause problems when the card is not installed. */ - -__initfunc(int hp100_probe(struct device *dev)) + +__initfunc(int hp100_probe( struct device *dev )) { - int base_addr = dev ? dev->base_addr : 0; - int ioaddr = 0; + int base_addr = dev ? dev -> base_addr : 0; + int ioaddr = 0; #ifdef CONFIG_PCI - int pci_start_index = 0; + int pci_start_index = 0; #endif #ifdef HP100_DEBUG_B - hp100_outw(0x4200, TRACE); - printk("hp100: probe\n"); -#endif - - if (base_addr > 0xff) { /* Check a single specified location. */ - if (check_region(base_addr, HP100_REGION_SIZE)) - return -EINVAL; - if (base_addr < 0x400) - return hp100_probe1(dev, base_addr, HP100_BUS_ISA, 0, 0); - else - return hp100_probe1(dev, base_addr, HP100_BUS_EISA, 0, 0); - } else + hp100_outw( 0x4200, TRACE ); + printk( "hp100: %s: probe\n", dev->name ); +#endif + + if ( base_addr > 0xff ) /* Check a single specified location. */ + { + if ( check_region( base_addr, HP100_REGION_SIZE ) ) return -EINVAL; + if ( base_addr < 0x400 ) + return hp100_probe1( dev, base_addr, HP100_BUS_ISA, 0, 0 ); + if ( EISA_bus && base_addr >= 0x1c38 && ( (base_addr - 0x1c38) & 0x3ff ) == 0 ) + return hp100_probe1( dev, base_addr, HP100_BUS_EISA, 0, 0 ); +#ifdef CONFIG_PCI + printk( "hp100: %s: You may specify card # in i/o address parameter for PCI bus...", dev->name ); + return hp100_probe1( dev, base_addr, HP100_BUS_PCI, 0, 0 ); +#else + return -ENODEV; +#endif + } + else #ifdef CONFIG_PCI - if (base_addr > 0 && base_addr < 8 + 1) - pci_start_index = 0x100 | (base_addr - 1); - else + if ( base_addr > 0 && base_addr < 8 + 1 ) + pci_start_index = 0x100 | ( base_addr - 1 ); + else #endif - if (base_addr != 0) - return -ENXIO; + if ( base_addr != 0 ) return -ENXIO; - /* at first - scan PCI bus(es) */ + /* at first - scan PCI bus(es) */ #ifdef CONFIG_PCI - if (pcibios_present()) { - int pci_index; + if ( pcibios_present() ) + { + int pci_index; #ifdef HP100_DEBUG_PCI - printk("hp100: PCI BIOS is present, checking for devices..\n"); -#endif - for (pci_index = pci_start_index & 7; pci_index < 8; pci_index++) { - u_char pci_bus, pci_device_fn; - u_short pci_command; - - if ((pcibios_find_device(PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_J2585A, - pci_index, &pci_bus, - &pci_device_fn) != 0) && - (pcibios_find_device(PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_J2585B, - pci_index, &pci_bus, - &pci_device_fn) != 0) && - (pcibios_find_device(PCI_VENDOR_ID_COMPEX, PCI_DEVICE_ID_COMPEX_ENET100VG4, - pci_index, &pci_bus, - &pci_device_fn) != 0)) - break; - - pcibios_read_config_dword(pci_bus, pci_device_fn, - PCI_BASE_ADDRESS_0, &ioaddr); - - ioaddr &= ~3; /* remove I/O space marker in bit 0. */ - - if (check_region(ioaddr, HP100_REGION_SIZE)) - continue; - - pcibios_read_config_word(pci_bus, pci_device_fn, - PCI_COMMAND, &pci_command); - if (!(pci_command & PCI_COMMAND_MASTER)) { -#ifdef HP100_DEBUG - printk("hp100: PCI Master Bit has not been set. Setting...\n"); + printk( "hp100: %s: PCI BIOS is present, checking for devices..\n", dev->name ); #endif - pci_command |= PCI_COMMAND_MASTER; - pcibios_write_config_word(pci_bus, pci_device_fn, - PCI_COMMAND, pci_command); - } -#ifdef HP100_DEBUG - printk("hp100: PCI adapter found at 0x%x\n", ioaddr); -#endif - if (hp100_probe1(dev, ioaddr, HP100_BUS_PCI, pci_bus, pci_device_fn) == 0) - return 0; - } - } - if (pci_start_index > 0) - return -ENODEV; -#endif /* CONFIG_PCI */ - - /* Second: Probe all EISA possible port regions (if EISA bus present) */ - for (ioaddr = 0x1c38; EISA_bus && ioaddr < 0x10000; ioaddr += 0x400) { - if (check_region(ioaddr, HP100_REGION_SIZE)) - continue; - if (hp100_probe1(dev, ioaddr, HP100_BUS_EISA, 0, 0) == 0) - return 0; - } + for ( pci_index = pci_start_index & 7; pci_index < 8; pci_index++ ) + { + u_char pci_bus, pci_device_fn; + u_short pci_command; + int pci_id_index; - /* Third Probe all ISA possible port regions */ - for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x20) { - if (check_region(ioaddr, HP100_REGION_SIZE)) - continue; - if (hp100_probe1(dev, ioaddr, HP100_BUS_ISA, 0, 0) == 0) - return 0; - } + for ( pci_id_index = 0; pci_id_index < HP100_PCI_IDS_SIZE; pci_id_index++ ) + if ( pcibios_find_device( hp100_pci_ids[ pci_id_index ].vendor, + hp100_pci_ids[ pci_id_index ].device, + pci_index, &pci_bus, + &pci_device_fn ) == 0 ) goto __pci_found; + break; - return -ENODEV; + __pci_found: + pcibios_read_config_dword( pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_0, &ioaddr ); + + ioaddr &= ~3; /* remove I/O space marker in bit 0. */ + + if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue; + + pcibios_read_config_word( pci_bus, pci_device_fn, + PCI_COMMAND, &pci_command ); + if ( !( pci_command & PCI_COMMAND_IO ) ) + { +#ifdef HP100_DEBUG + printk( "hp100: %s: PCI I/O Bit has not been set. Setting...\n", dev->name ); +#endif + pci_command |= PCI_COMMAND_IO; + pcibios_write_config_word( pci_bus, pci_device_fn, + PCI_COMMAND, pci_command ); + } + if ( !( pci_command & PCI_COMMAND_MASTER ) ) + { +#ifdef HP100_DEBUG + printk( "hp100: %s: PCI Master Bit has not been set. Setting...\n", dev->name ); +#endif + pci_command |= PCI_COMMAND_MASTER; + pcibios_write_config_word( pci_bus, pci_device_fn, + PCI_COMMAND, pci_command ); + } +#ifdef HP100_DEBUG + printk( "hp100: %s: PCI adapter found at 0x%x\n", dev->name, ioaddr ); +#endif + if ( hp100_probe1( dev, ioaddr, HP100_BUS_PCI, pci_bus, pci_device_fn ) == 0 ) + return 0; + } + } + if ( pci_start_index > 0 ) return -ENODEV; +#endif /* CONFIG_PCI */ + + /* Second: Probe all EISA possible port regions (if EISA bus present) */ + for ( ioaddr = 0x1c38; EISA_bus && ioaddr < 0x10000; ioaddr += 0x400 ) + { + if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue; + if ( hp100_probe1( dev, ioaddr, HP100_BUS_EISA, 0, 0 ) == 0 ) return 0; + } + + /* Third Probe all ISA possible port regions */ + for ( ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x20 ) + { + if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue; + if ( hp100_probe1( dev, ioaddr, HP100_BUS_ISA, 0, 0 ) == 0 ) return 0; + } + + return -ENODEV; } - -__initfunc(static int hp100_probe1(struct device *dev, int ioaddr, u_char bus, u_char pci_bus, u_char pci_device_fn)) + +__initfunc(static int hp100_probe1( struct device *dev, int ioaddr, u_char bus, u_char pci_bus, u_char pci_device_fn )) { - int i; - - u_char uc, uc_1; - u_int eisa_id; - u_int chip; - u_int memory_size = 0; - short mem_mapped; - u_int *mem_ptr_phys, *mem_ptr_virt; - struct hp100_private *lp; - struct hp100_eisa_id *eid; + int i; + + u_char uc, uc_1; + u_int eisa_id; + u_int chip; + u_int memory_size = 0, virt_memory_size = 0; + u_short local_mode, lsw; + short mem_mapped; + u_int *mem_ptr_phys, *mem_ptr_virt; + struct hp100_private *lp; + struct hp100_eisa_id *eid; #ifdef HP100_DEBUG_B - hp100_outw(0x4201, TRACE); - printk("hp100: probe1\n"); + hp100_outw( 0x4201, TRACE ); + printk("hp100: %s: probe1\n",dev->name); #endif - if (dev == NULL) { + if ( dev == NULL ) + { #ifdef HP100_DEBUG - printk("hp100_probe1: dev == NULL ?\n"); -#endif - return EIO; + printk( "hp100_probe1: %s: dev == NULL ?\n", dev->name ); +#endif + return EIO; + } + + if ( hp100_inw( HW_ID ) != HP100_HW_ID_CASCADE ) + { + return -ENODEV; + } + else + { + chip = hp100_inw( PAGING ) & HP100_CHIPID_MASK; +#ifdef HP100_DEBUG + if ( chip == HP100_CHIPID_SHASTA ) + printk("hp100: %s: Shasta Chip detected. (This is a pre 802.12 chip)\n", dev->name); + else if ( chip == HP100_CHIPID_RAINIER ) + printk("hp100: %s: Rainier Chip detected. (This is a pre 802.12 chip)\n", dev->name); + else if ( chip == HP100_CHIPID_LASSEN ) + printk("hp100: %s: Lassen Chip detected.\n", dev->name); + else + printk("hp100: %s: Warning: Unknown CASCADE chip (id=0x%.4x).\n",dev->name,chip); +#endif + } + + dev->base_addr = ioaddr; + + hp100_page( ID_MAC_ADDR ); + for ( i = uc = eisa_id = 0; i < 4; i++ ) + { + eisa_id >>= 8; + uc_1 = hp100_inb( BOARD_ID + i ); + eisa_id |= uc_1 << 24; + uc += uc_1; + } + uc += hp100_inb( BOARD_ID + 4 ); + + if ( uc != 0xff ) /* bad checksum? */ + { + printk("hp100_probe: %s: bad EISA ID checksum at base port 0x%x\n", dev->name, ioaddr ); + return -ENODEV; + } + + for ( i=0; i < HP100_EISA_IDS_SIZE; i++) + if ( hp100_eisa_ids[ i ].id == eisa_id ) + break; + if ( i >= HP100_EISA_IDS_SIZE ) { + for ( i = 0; i < HP100_EISA_IDS_SIZE; i++) + if ( ( hp100_eisa_ids[ i ].id & 0xf0ffffff ) == ( eisa_id & 0xf0ffffff ) ) + break; + if ( i >= HP100_EISA_IDS_SIZE ) { + printk( "hp100_probe: %s: card at port 0x%x isn't known (id = 0x%x)\n", dev -> name, ioaddr, eisa_id ); + return -ENODEV; + } + } + eid = &hp100_eisa_ids[ i ]; + if ( ( eid->id & 0x0f000000 ) < ( eisa_id & 0x0f000000 ) ) + { + printk( "hp100_probe: %s: newer version of card %s at port 0x%x - unsupported\n", + dev->name, eid->name, ioaddr ); + return -ENODEV; + } + + for ( i = uc = 0; i < 7; i++ ) + uc += hp100_inb( LAN_ADDR + i ); + if ( uc != 0xff ) + { + printk("hp100_probe: %s: bad lan address checksum (card %s at port 0x%x)\n", + dev->name, eid->name, ioaddr ); + return -EIO; + } + + /* Make sure, that all registers are correctly updated... */ + + hp100_load_eeprom( dev, ioaddr ); + wait(); + + /* + * Determine driver operation mode + * + * Use the variable "hp100_mode" upon insmod or as kernel parameter to + * force driver modes: + * hp100_mode=1 -> default, use busmaster mode if configured. + * hp100_mode=2 -> enable shared memory mode + * hp100_mode=3 -> force use of i/o mapped mode. + * hp100_mode=4 -> same as 1, but re-set the enable bit on the card. + */ + + /* + * LSW values: + * 0x2278 -> J2585B, PnP shared memory mode + * 0x2270 -> J2585B, shared memory mode, 0xdc000 + * 0xa23c -> J2585B, I/O mapped mode + * 0x2240 -> EISA COMPEX, BusMaster (Shasta Chip) + * 0x2220 -> EISA HP, I/O (Shasta Chip) + * 0x2260 -> EISA HP, BusMaster (Shasta Chip) + */ + +#if 0 + local_mode = 0x2270; + hp100_outw(0xfefe,OPTION_LSW); + hp100_outw(local_mode|HP100_SET_LB|HP100_SET_HB,OPTION_LSW); +#endif + + /* hp100_mode value maybe used in future by another card */ + local_mode=hp100_mode; + if ( local_mode < 1 || local_mode > 4 ) + local_mode = 1; /* default */ +#ifdef HP100_DEBUG + printk( "hp100: %s: original LSW = 0x%x\n", dev->name, hp100_inw(OPTION_LSW) ); +#endif + + if(local_mode==3) + { + hp100_outw(HP100_MEM_EN|HP100_RESET_LB, OPTION_LSW); + hp100_outw(HP100_IO_EN|HP100_SET_LB, OPTION_LSW); + hp100_outw(HP100_BM_WRITE|HP100_BM_READ|HP100_RESET_HB, OPTION_LSW); + printk("hp100: %s: IO mapped mode forced.\n", dev->name); + } + else if(local_mode==2) + { + hp100_outw(HP100_MEM_EN|HP100_SET_LB, OPTION_LSW); + hp100_outw(HP100_IO_EN |HP100_SET_LB, OPTION_LSW); + hp100_outw(HP100_BM_WRITE|HP100_BM_READ|HP100_RESET_HB, OPTION_LSW); + printk("hp100: %s: Shared memory mode requested.\n", dev->name); + } + else if(local_mode==4) + { + if(chip==HP100_CHIPID_LASSEN) + { + hp100_outw(HP100_BM_WRITE| + HP100_BM_READ | HP100_SET_HB, OPTION_LSW); + hp100_outw(HP100_IO_EN | + HP100_MEM_EN | HP100_RESET_LB, OPTION_LSW); + printk("hp100: %s: Busmaster mode requested.\n",dev->name); } - if (hp100_inw(HW_ID) != HP100_HW_ID_CASCADE) { - return -ENODEV; - } else { - chip = hp100_inw(PAGING) & HP100_CHIPID_MASK; + local_mode=1; + } + + if(local_mode==1) /* default behaviour */ + { + lsw = hp100_inw(OPTION_LSW); + + if ( (lsw & HP100_IO_EN) && + (~lsw & HP100_MEM_EN) && + (~lsw & (HP100_BM_WRITE|HP100_BM_READ)) ) + { #ifdef HP100_DEBUG - if (chip == HP100_CHIPID_SHASTA) - printk("hp100: Shasta Chip detected. (This is a pre 802.12 chip)\n"); - else if (chip == HP100_CHIPID_RAINIER) - printk("hp100: Rainier Chip detected. (This is a pre 802.12 chip)\n"); - else if (chip == HP100_CHIPID_LASSEN) - printk("hp100: Lassen Chip detected.\n"); - else - printk("hp100: Warning: Unknown CASCADE chip (id=0x%.4x).\n", chip); + printk("hp100: %s: IO_EN bit is set on card.\n",dev->name); #endif + local_mode=3; } - - dev->base_addr = ioaddr; - - hp100_page(ID_MAC_ADDR); - for (i = uc = eisa_id = 0; i < 4; i++) { - eisa_id >>= 8; - uc_1 = hp100_inb(BOARD_ID + i); - eisa_id |= uc_1 << 24; - uc += uc_1; + else if ( chip == HP100_CHIPID_LASSEN && + ( lsw & (HP100_BM_WRITE|HP100_BM_READ) ) == + (HP100_BM_WRITE|HP100_BM_READ) ) + { + printk("hp100: %s: Busmaster mode enabled.\n",dev->name); + hp100_outw(HP100_MEM_EN|HP100_IO_EN|HP100_RESET_LB, OPTION_LSW); } - uc += hp100_inb(BOARD_ID + 4); - - if (uc != 0xff) { /* bad checksum? */ - printk("hp100_probe: bad EISA ID checksum at base port 0x%x\n", ioaddr); - return -ENODEV; - } - for (i = 0; i < sizeof(hp100_eisa_ids) / sizeof(struct hp100_eisa_id); i++) - if ((hp100_eisa_ids[i].id & 0xf0ffffff) == (eisa_id & 0xf0ffffff)) - break; - if (i >= sizeof(hp100_eisa_ids) / sizeof(struct hp100_eisa_id)) { - printk("hp100_probe1: card at port 0x%x isn't known (id = 0x%x)\n", ioaddr, eisa_id); - return -ENODEV; - } - eid = &hp100_eisa_ids[i]; - if ((eid->id & 0x0f000000) < (eisa_id & 0x0f000000)) { - printk("hp100_probe1: newer version of card %s at port 0x%x - unsupported\n", - eid->name, ioaddr); - return -ENODEV; - } - for (i = uc = 0; i < 7; i++) - uc += hp100_inb(LAN_ADDR + i); - if (uc != 0xff) { - printk("hp100_probe1: bad lan address checksum (card %s at port 0x%x)\n", - eid->name, ioaddr); - return -EIO; - } - /* Determine driver operation mode - - * Use the variable "hp100_mode" upon insmod or as kernel parameter to - * force driver modes: - * hp100_mode=1 -> default, use busmaster mode if configured. - * hp100_mode=2 -> enable shared memory mode - * hp100_mode=3 -> force use of i/o mapped mode. - * hp100_mode=4 -> same as 1, but re-set the enable bit on the card. - */ - - if (hp100_mode == 3) { - hp100_outw(HP100_MEM_EN | HP100_RESET_LB, OPTION_LSW); - hp100_outw(HP100_IO_EN | HP100_SET_LB, OPTION_LSW); - hp100_outw(HP100_BM_WRITE | HP100_BM_READ | HP100_RESET_HB, OPTION_LSW); - printk("hp100: IO mapped mode forced.\n"); - } else if (hp100_mode == 2) { - hp100_outw(HP100_MEM_EN | HP100_SET_LB, OPTION_LSW); - hp100_outw(HP100_IO_EN | HP100_SET_LB, OPTION_LSW); - hp100_outw(HP100_BM_WRITE | HP100_BM_READ | HP100_RESET_HB, OPTION_LSW); - printk("hp100: Shared memory mode requested.\n"); - } else if (hp100_mode == 4) { - if (chip == HP100_CHIPID_LASSEN) { - hp100_outw(HP100_BM_WRITE | - HP100_BM_READ | HP100_SET_HB, OPTION_LSW); - hp100_outw(HP100_IO_EN | - HP100_MEM_EN | HP100_RESET_LB, OPTION_LSW); - printk("hp100: Busmaster mode requested.\n"); - } - hp100_mode = 1; - } - if (hp100_mode == 1) { /* default behaviour */ - if ((hp100_inw(OPTION_LSW) & HP100_IO_EN) && - (~hp100_inw(OPTION_LSW) & HP100_MEM_EN) && - (~hp100_inw(OPTION_LSW) & (HP100_BM_WRITE | HP100_BM_READ)) - ) { -#ifdef HP100_DEBUG - printk("hp100: IO_EN bit is set on card.\n"); -#endif - hp100_mode = 3; - } else if ((chip == HP100_CHIPID_LASSEN) && - ((hp100_inw(OPTION_LSW) & (HP100_BM_WRITE | HP100_BM_READ)) == - (HP100_BM_WRITE | HP100_BM_READ))) { - printk("hp100: Busmaster mode enabled.\n"); - hp100_outw(HP100_MEM_EN | HP100_IO_EN | HP100_RESET_LB, OPTION_LSW); - } else { + else + { #ifdef HP100_DEBUG - printk("hp100: Card not configured for BM or BM not supported with this card. Trying shared memory mode.\n"); + printk("hp100: %s: Card not configured for BM or BM not supported with this card.\n", dev->name ); + printk("hp100: %s: Trying shared memory mode.\n", dev->name); #endif - /* In this case, try shared memory mode */ - hp100_mode = 2; - hp100_outw(HP100_MEM_EN | HP100_SET_LB, OPTION_LSW); - /* hp100_outw(HP100_IO_EN|HP100_RESET_LB, OPTION_LSW); */ - } + /* In this case, try shared memory mode */ + local_mode=2; + hp100_outw(HP100_MEM_EN|HP100_SET_LB, OPTION_LSW); + /* hp100_outw(HP100_IO_EN|HP100_RESET_LB, OPTION_LSW); */ } - /* Check for shared memory on the card, eventually remap it */ - hp100_page(HW_MAP); - mem_mapped = ((hp100_inw(OPTION_LSW) & (HP100_MEM_EN)) != 0); - mem_ptr_phys = mem_ptr_virt = NULL; - memory_size = (8192 << ((hp100_inb(SRAM) >> 5) & 0x07)); - - /* For memory mapped or busmaster mode, we want the memory address */ - if (mem_mapped || (hp100_mode == 1)) { - mem_ptr_phys = (u_int *) (hp100_inw(MEM_MAP_LSW) | - (hp100_inw(MEM_MAP_MSW) << 16)); - (u_int) mem_ptr_phys &= ~0x1fff; /* 8k alignment */ - - if (bus == HP100_BUS_ISA && ((u_long) mem_ptr_phys & ~0xfffff) != 0) { - printk("hp100: Can only use programmed i/o mode.\n"); - mem_ptr_phys = NULL; - mem_mapped = 0; - hp100_mode = 3; /* Use programmed i/o */ - } - /* We do not need access to shared memory in busmaster mode */ - /* However in slave mode we need to remap high (>1GB) card memory */ - if (hp100_mode != 1) { /* = not busmaster */ - if (bus == HP100_BUS_PCI) { - /* We try with smaller memory sizes, if ioremap fails */ - for (; memory_size > 16383; memory_size = memory_size / 2) { - if ((mem_ptr_virt = ioremap((u_long) mem_ptr_phys, memory_size)) == NULL) { + } + #ifdef HP100_DEBUG - printk("hp100: ioremap for 0x%x bytes high PCI memory at 0x%lx failed\n", memory_size, (u_long) mem_ptr_phys); + printk( "hp100: %s: new LSW = 0x%x\n", dev->name, hp100_inw(OPTION_LSW) ); +#endif + + /* Check for shared memory on the card, eventually remap it */ + hp100_page( HW_MAP ); + mem_mapped = (( hp100_inw( OPTION_LSW ) & ( HP100_MEM_EN ) ) != 0); + mem_ptr_phys = mem_ptr_virt = NULL; + memory_size = (8192<<( (hp100_inb(SRAM)>>5)&0x07)); + virt_memory_size = 0; + + /* For memory mapped or busmaster mode, we want the memory address */ + if ( mem_mapped || (local_mode==1)) + { + mem_ptr_phys = (u_int *)( hp100_inw( MEM_MAP_LSW ) | + ( hp100_inw( MEM_MAP_MSW ) << 16 ) ); + (u_int)mem_ptr_phys &= ~0x1fff; /* 8k alignment */ + + if ( bus == HP100_BUS_ISA && ( (u_long)mem_ptr_phys & ~0xfffff ) != 0 ) + { + printk("hp100: %s: Can only use programmed i/o mode.\n", dev->name); + mem_ptr_phys = NULL; + mem_mapped = 0; + local_mode=3; /* Use programmed i/o */ + } + + /* We do not need access to shared memory in busmaster mode */ + /* However in slave mode we need to remap high (>1GB) card memory */ + if(local_mode!=1) /* = not busmaster */ + { + if ( bus == HP100_BUS_PCI && mem_ptr_phys >= (u_int *)0x100000 ) + { + /* We try with smaller memory sizes, if ioremap fails */ + for(virt_memory_size = memory_size; virt_memory_size>16383; virt_memory_size>>=1) + { + if((mem_ptr_virt=ioremap((u_long)mem_ptr_phys,virt_memory_size))==NULL) + { +#ifdef HP100_DEBUG + printk( "hp100: %s: ioremap for 0x%x bytes high PCI memory at 0x%lx failed\n", dev->name, virt_memory_size, (u_long)mem_ptr_phys ); #endif - } else { + } + else + { #ifdef HP100_DEBUG - printk("hp100: remapped 0x%x bytes high PCI memory at 0x%lx to 0x%lx.\n", memory_size, (u_long) mem_ptr_phys, (u_long) mem_ptr_virt); -#endif - break; - } - } - - if (mem_ptr_virt == NULL) { /* all ioremap tries failed */ - printk("hp100: Failed to ioremap the PCI card memory. Will have to use i/o mapped mode.\n"); - hp100_mode = 3; - memory_size = (8192 << ((hp100_inb(SRAM) >> 5) & 0x07)); - } - } + printk( "hp100: %s: remapped 0x%x bytes high PCI memory at 0x%lx to 0x%lx.\n", dev->name, virt_memory_size, (u_long)mem_ptr_phys, (u_long)mem_ptr_virt); +#endif + break; + } } + + if(mem_ptr_virt==NULL) /* all ioremap tries failed */ + { + printk("hp100: %s: Failed to ioremap the PCI card memory. Will have to use i/o mapped mode.\n", dev->name); + local_mode=3; + virt_memory_size = 0; + } + } } - if (hp100_mode == 3) { /* io mapped forced */ - mem_mapped = 0; - mem_ptr_phys = mem_ptr_virt = NULL; - printk("hp100: Using (slow) programmed i/o mode.\n"); - } - /* Initialise the "private" data structure for this card. */ - if ((dev->priv = kmalloc(sizeof(struct hp100_private), GFP_KERNEL)) == NULL) - return -ENOMEM; - memset(dev->priv, 0, sizeof(struct hp100_private)); - - lp = (struct hp100_private *) dev->priv; - lp->id = eid; - lp->chip = chip; - lp->mode = hp100_mode; - lp->pci_bus = pci_bus; - lp->bus = bus; - lp->pci_device_fn = pci_device_fn; - lp->priority_tx = hp100_priority_tx; - lp->rx_ratio = hp100_rx_ratio; - lp->mem_ptr_phys = mem_ptr_phys; - lp->mem_ptr_virt = mem_ptr_virt; - hp100_page(ID_MAC_ADDR); - lp->soft_model = hp100_inb(SOFT_MODEL); - lp->mac1_mode = HP100_MAC1MODE3; - lp->mac2_mode = HP100_MAC2MODE3; - - dev->base_addr = ioaddr; - - lp->memory_size = memory_size; - lp->rx_ratio = hp100_rx_ratio; /* can be conf'd with insmod */ - - /* memory region for programmed i/o */ - request_region(dev->base_addr, HP100_REGION_SIZE, eid->name); - - dev->open = hp100_open; - dev->stop = hp100_close; - - if (lp->mode == 1) /* busmaster */ - dev->hard_start_xmit = hp100_start_xmit_bm; - else - dev->hard_start_xmit = hp100_start_xmit; - - dev->get_stats = hp100_get_stats; - dev->set_multicast_list = &hp100_set_multicast_list; - - /* Ask the card for which IRQ line it is configured */ - hp100_page(HW_MAP); - dev->irq = hp100_inb(IRQ_CHANNEL) & HP100_IRQMASK; - if (dev->irq == 2) - dev->irq = 9; - - if (lp->mode == 1) /* busmaster */ - dev->dma = 4; - - /* Ask the card for its MAC address and store it for later use. */ - hp100_page(ID_MAC_ADDR); - for (i = uc = 0; i < 6; i++) - dev->dev_addr[i] = hp100_inb(LAN_ADDR + i); - - /* Reset statistics (counters) */ - hp100_clear_stats(ioaddr); - - ether_setup(dev); - - /* If busmaster mode is wanted, a dma-capable memory area is needed for - * the rx and tx PDLs - * PCI cards can access the whole PC memory. Therefore GFP_DMA is not - * needed for the allocation of the memory area. - */ - - /* TODO: We do not need this with old cards, where PDLs are stored - * in the cards shared memory area. But currently, busmaster has been - * implemented/tested only with the lassen chip anyway... */ - if (lp->mode == 1) { /* busmaster */ - /* Get physically continous memory for TX & RX PDLs */ - if ((lp->page_vaddr = kmalloc(MAX_RINGSIZE + 0x0f, GFP_KERNEL)) == NULL) - return -ENOMEM; - lp->page_vaddr_algn = ((u_int *) (((u_int) (lp->page_vaddr) + 0x0f) & ~0x0f)); - memset(lp->page_vaddr, 0, MAX_RINGSIZE + 0x0f); + + } + + if(local_mode==3) /* io mapped forced */ + { + mem_mapped = 0; + mem_ptr_phys = mem_ptr_virt = NULL; + printk("hp100: %s: Using (slow) programmed i/o mode.\n", dev->name); + } + + /* Initialise the "private" data structure for this card. */ + if ( (dev->priv=kmalloc(sizeof(struct hp100_private), GFP_KERNEL)) == NULL) + return -ENOMEM; + memset( dev->priv, 0, sizeof(struct hp100_private) ); + + lp = (struct hp100_private *)dev->priv; + lp->id = eid; + lp->chip = chip; + lp->mode = local_mode; + lp->pci_bus = pci_bus; + lp->bus = bus; + lp->pci_device_fn = pci_device_fn; + lp->priority_tx = hp100_priority_tx; + lp->rx_ratio = hp100_rx_ratio; + lp->mem_ptr_phys = mem_ptr_phys; + lp->mem_ptr_virt = mem_ptr_virt; + hp100_page( ID_MAC_ADDR ); + lp->soft_model = hp100_inb( SOFT_MODEL ); + lp->mac1_mode = HP100_MAC1MODE3; + lp->mac2_mode = HP100_MAC2MODE3; + memset( &lp->hash_bytes, 0x00, 8 ); + + dev->base_addr = ioaddr; + + lp->memory_size = memory_size; + lp->virt_memory_size = virt_memory_size; + lp->rx_ratio = hp100_rx_ratio; /* can be conf'd with insmod */ + + /* memory region for programmed i/o */ + request_region( dev->base_addr, HP100_REGION_SIZE, eid->name ); + + dev->open = hp100_open; + dev->stop = hp100_close; + + if (lp->mode==1) /* busmaster */ + dev->hard_start_xmit = hp100_start_xmit_bm; + else + dev->hard_start_xmit = hp100_start_xmit; + + dev->get_stats = hp100_get_stats; + dev->set_multicast_list = &hp100_set_multicast_list; + + /* Ask the card for which IRQ line it is configured */ + hp100_page( HW_MAP ); + dev->irq = hp100_inb( IRQ_CHANNEL ) & HP100_IRQMASK; + if ( dev->irq == 2 ) + dev->irq = 9; + + if(lp->mode==1) /* busmaster */ + dev->dma=4; + + /* Ask the card for its MAC address and store it for later use. */ + hp100_page( ID_MAC_ADDR ); + for ( i = uc = 0; i < 6; i++ ) + dev->dev_addr[ i ] = hp100_inb( LAN_ADDR + i ); + + /* Reset statistics (counters) */ + hp100_clear_stats( ioaddr ); + + ether_setup( dev ); + + /* If busmaster mode is wanted, a dma-capable memory area is needed for + * the rx and tx PDLs + * PCI cards can access the whole PC memory. Therefore GFP_DMA is not + * needed for the allocation of the memory area. + */ + + /* TODO: We do not need this with old cards, where PDLs are stored + * in the cards shared memory area. But currently, busmaster has been + * implemented/tested only with the lassen chip anyway... */ + if(lp->mode==1) /* busmaster */ + { + /* Get physically continous memory for TX & RX PDLs */ + if ( (lp->page_vaddr=kmalloc(MAX_RINGSIZE+0x0f,GFP_KERNEL) ) == NULL) + return -ENOMEM; + lp->page_vaddr_algn=((u_int *) ( ((u_int)(lp->page_vaddr)+0x0f) &~0x0f)); + memset(lp->page_vaddr, 0, MAX_RINGSIZE+0x0f); #ifdef HP100_DEBUG_BM - printk("hp100: Reserved DMA memory from 0x%x to 0x%x\n", - (u_int) lp->page_vaddr_algn, - (u_int) lp->page_vaddr_algn + MAX_RINGSIZE); -#endif - lp->rxrcommit = lp->txrcommit = 0; - lp->rxrhead = lp->rxrtail = &(lp->rxring[0]); - lp->txrhead = lp->txrtail = &(lp->txring[0]); - } - /* Initialise the card. */ - /* (I'm not really sure if it's a good idea to do this during probing, but - * like this it's assured that the lan connection type can be sensed - * correctly) - */ - hp100_hwinit(dev); - - /* Try to find out which kind of LAN the card is connected to. */ - lp->lan_type = hp100_sense_lan(dev); - - /* Print out a message what about what we think we have probed. */ - printk("hp100: %s: %s at 0x%x, IRQ %d, ", - dev->name, lp->id->name, ioaddr, dev->irq); - switch (bus) { - case HP100_BUS_EISA: - printk("EISA"); - break; - case HP100_BUS_PCI: - printk("PCI"); - break; - default: - printk("ISA"); - break; - } - printk(" bus, %dk SRAM (rx/tx %d%%).\n", - lp->memory_size >> 10, lp->rx_ratio); - - if (lp->mode == 2) { /* memory mapped */ - printk("%s: Memory area at 0x%lx-0x%lx", - dev->name, (u_long) mem_ptr_phys, (u_long) mem_ptr_phys + (u_long) lp->memory_size); - if (mem_ptr_virt) - printk(" (virtual base 0x%lx)", (u_long) mem_ptr_virt); - printk(".\n"); - - /* Set for info when doing ifconfig */ - dev->mem_start = (u_long) mem_ptr_phys; - dev->mem_end = (u_long) mem_ptr_phys + (u_long) lp->memory_size; - } - printk("%s: ", dev->name); - if (lp->lan_type != HP100_LAN_ERR) - printk("Adapter is attached to "); - switch (lp->lan_type) { - case HP100_LAN_100: - printk("100Mb/s Voice Grade AnyLAN network.\n"); - break; - case HP100_LAN_10: - printk("10Mb/s network.\n"); - break; - default: - printk("Warning! Link down.\n"); - } - return 0; + printk("hp100: %s: Reserved DMA memory from 0x%x to 0x%x\n", + dev->name, + (u_int)lp->page_vaddr_algn, + (u_int)lp->page_vaddr_algn+MAX_RINGSIZE); +#endif + lp->rxrcommit = lp->txrcommit = 0; + lp->rxrhead = lp->rxrtail = &(lp->rxring[0]); + lp->txrhead = lp->txrtail = &(lp->txring[0]); + } + + /* Initialise the card. */ + /* (I'm not really sure if it's a good idea to do this during probing, but + * like this it's assured that the lan connection type can be sensed + * correctly) + */ + hp100_hwinit( dev ); + + /* Try to find out which kind of LAN the card is connected to. */ + lp->lan_type = hp100_sense_lan( dev ); + + /* Print out a message what about what we think we have probed. */ + printk( "hp100: %s: %s at 0x%x, IRQ %d, ", + dev->name, lp->id->name, ioaddr, dev->irq ); + switch ( bus ) { + case HP100_BUS_EISA: printk( "EISA" ); break; + case HP100_BUS_PCI: printk( "PCI" ); break; + default: printk( "ISA" ); break; + } + printk( " bus, %dk SRAM (rx/tx %d%%).\n", + lp->memory_size >> 10, lp->rx_ratio ); + + if ( lp->mode==2 ) /* memory mapped */ + { + printk( "hp100: %s: Memory area at 0x%lx-0x%lx", + dev->name,(u_long)mem_ptr_phys, + ((u_long)mem_ptr_phys+(mem_ptr_phys>(u_int *)0x100000?(u_long)lp->memory_size:16*1024))-1 ); + if ( mem_ptr_virt ) + printk( " (virtual base 0x%lx)", (u_long)mem_ptr_virt ); + printk( ".\n" ); + + /* Set for info when doing ifconfig */ + dev->mem_start = (u_long)mem_ptr_phys; + dev->mem_end = (u_long)mem_ptr_phys+(u_long)lp->memory_size; + } + printk( "hp100: %s: ", dev->name ); + if ( lp->lan_type != HP100_LAN_ERR ) + printk( "Adapter is attached to " ); + switch ( lp->lan_type ) { + case HP100_LAN_100: + printk( "100Mb/s Voice Grade AnyLAN network.\n" ); + break; + case HP100_LAN_10: + printk( "10Mb/s network.\n" ); + break; + default: + printk( "Warning! Link down.\n" ); + } + + return 0; } - + /* This procedure puts the card into a stable init state */ -static void hp100_hwinit(struct device *dev) +static void hp100_hwinit( struct device *dev ) { - int ioaddr = dev->base_addr; - struct hp100_private *lp = (struct hp100_private *) dev->priv; + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *)dev->priv; #ifdef HP100_DEBUG_B - hp100_outw(0x4202, TRACE); - printk("hp100: hwinit\n"); -#endif - - /* Initialise the card. -------------------------------------------- */ - - /* Clear all pending Ints and disable Ints */ - hp100_page(PERFORMANCE); - hp100_outw(0xfefe, IRQ_MASK); /* mask off all ints */ - hp100_outw(0xffff, IRQ_STATUS); /* clear all pending ints */ - - hp100_outw(HP100_INT_EN | HP100_RESET_LB, OPTION_LSW); - hp100_outw(HP100_TRI_INT | HP100_SET_HB, OPTION_LSW); - - if (lp->mode == 1) { - hp100_BM_shutdown(dev); /* disables BM, puts cascade in reset */ - wait(); - } else { - hp100_outw(HP100_INT_EN | HP100_RESET_LB, OPTION_LSW); - hp100_cascade_reset(dev, TRUE); - hp100_page(MAC_CTRL); - hp100_andb(~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1); - } - - /* Initiate EEPROM reload */ - hp100_load_eeprom(dev); - - wait(); - - /* Go into reset again. */ - hp100_cascade_reset(dev, TRUE); - - /* Set Option Registers to a safe state */ - hp100_outw(HP100_DEBUG_EN | - HP100_RX_HDR | - HP100_EE_EN | - HP100_BM_WRITE | - HP100_BM_READ | HP100_RESET_HB | - HP100_FAKE_INT | - HP100_INT_EN | - HP100_MEM_EN | - HP100_IO_EN | HP100_RESET_LB, OPTION_LSW); - - hp100_outw(HP100_TRI_INT | - HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW); - - hp100_outb(HP100_PRIORITY_TX | - HP100_ADV_NXT_PKT | - HP100_TX_CMD | HP100_RESET_LB, OPTION_MSW); - - /* TODO: Configure MMU for Ram Test. */ - /* TODO: Ram Test. */ - - /* Re-check if adapter is still at same i/o location */ - /* (If the base i/o in eeprom has been changed but the */ - /* registers had not been changed, a reload of the eeprom */ - /* would move the adapter to the address stored in eeprom */ - - /* TODO: Code to implement. */ - - /* Until here it was code from HWdiscover procedure. */ - /* Next comes code from mmuinit procedure of SCO BM driver which is - * called from HWconfigure in the SCO driver. */ - - /* Initialise MMU, eventually switch on Busmaster Mode, initialise - * multicast filter... - */ - hp100_mmuinit(dev); - - /* We don't turn the interrupts on here - this is done by start_interface. */ - wait(); /* TODO: Do we really need this? */ - - /* Enable Hardware (e.g. unreset) */ - hp100_cascade_reset(dev, FALSE); - - /* ------- initialisation complete ----------- */ - - /* Finally try to log in the Hub if there may be a VG connection. */ - if (lp->lan_type != HP100_LAN_10) - hp100_login_to_vg_hub(dev, FALSE); /* relogin */ + hp100_outw( 0x4202, TRACE ); + printk("hp100: %s: hwinit\n", dev->name); +#endif + + /* Initialise the card. -------------------------------------------- */ + + /* Clear all pending Ints and disable Ints */ + hp100_page( PERFORMANCE ); + hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all ints */ + hp100_outw( 0xffff, IRQ_STATUS ); /* clear all pending ints */ + + hp100_outw( HP100_INT_EN | HP100_RESET_LB, OPTION_LSW ); + hp100_outw( HP100_TRI_INT | HP100_SET_HB, OPTION_LSW ); + + if(lp->mode==1) + { + hp100_BM_shutdown( dev ); /* disables BM, puts cascade in reset */ + wait(); + } + else + { + hp100_outw( HP100_INT_EN | HP100_RESET_LB, OPTION_LSW ); + hp100_cascade_reset( dev, TRUE ); + hp100_page( MAC_CTRL ); + hp100_andb( ~(HP100_RX_EN|HP100_TX_EN), MAC_CFG_1); + } + + /* Initiate EEPROM reload */ + hp100_load_eeprom( dev, 0 ); + + wait(); + + /* Go into reset again. */ + hp100_cascade_reset( dev, TRUE ); + + /* Set Option Registers to a safe state */ + hp100_outw( HP100_DEBUG_EN | + HP100_RX_HDR | + HP100_EE_EN | + HP100_BM_WRITE | + HP100_BM_READ | HP100_RESET_HB | + HP100_FAKE_INT | + HP100_INT_EN | + HP100_MEM_EN | + HP100_IO_EN | HP100_RESET_LB, OPTION_LSW); + + hp100_outw( HP100_TRI_INT | + HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW ); + + hp100_outb( HP100_PRIORITY_TX | + HP100_ADV_NXT_PKT | + HP100_TX_CMD | HP100_RESET_LB, OPTION_MSW ); + + /* TODO: Configure MMU for Ram Test. */ + /* TODO: Ram Test. */ + + /* Re-check if adapter is still at same i/o location */ + /* (If the base i/o in eeprom has been changed but the */ + /* registers had not been changed, a reload of the eeprom */ + /* would move the adapter to the address stored in eeprom */ + + /* TODO: Code to implement. */ + + /* Until here it was code from HWdiscover procedure. */ + /* Next comes code from mmuinit procedure of SCO BM driver which is + * called from HWconfigure in the SCO driver. */ + + /* Initialise MMU, eventually switch on Busmaster Mode, initialise + * multicast filter... + */ + hp100_mmuinit( dev ); + + /* We don't turn the interrupts on here - this is done by start_interface. */ + wait(); /* TODO: Do we really need this? */ + + /* Enable Hardware (e.g. unreset) */ + hp100_cascade_reset( dev, FALSE ); + + /* ------- initialisation complete ----------- */ + + /* Finally try to log in the Hub if there may be a VG connection. */ + if( lp->lan_type != HP100_LAN_10 ) + hp100_login_to_vg_hub( dev, FALSE ); /* relogin */ } - + /* * mmuinit - Reinitialise Cascade MMU and MAC settings. * Note: Must already be in reset and leaves card in reset. */ -static void hp100_mmuinit(struct device *dev) +static void hp100_mmuinit( struct device *dev ) { - int ioaddr = dev->base_addr; - struct hp100_private *lp = (struct hp100_private *) dev->priv; - int i; + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *)dev->priv; + int i; #ifdef HP100_DEBUG_B - hp100_outw(0x4203, TRACE); - printk("hp100: mmuinit\n"); + hp100_outw( 0x4203, TRACE ); + printk("hp100: %s: mmuinit\n",dev->name); #endif #ifdef HP100_DEBUG - if (0 != (hp100_inw(OPTION_LSW) & HP100_HW_RST)) { - printk("hp100: Not in reset when entering mmuinit. Fix me.\n"); - return; - } -#endif - - /* Make sure IRQs are masked off and ack'ed. */ - hp100_page(PERFORMANCE); - hp100_outw(0xfefe, IRQ_MASK); /* mask off all ints */ - hp100_outw(0xffff, IRQ_STATUS); /* ack IRQ */ - - /* - * Enable Hardware - * - Clear Debug En, Rx Hdr Pipe, EE En, I/O En, Fake Int and Intr En - * - Set Tri-State Int, Bus Master Rd/Wr, and Mem Map Disable - * - Clear Priority, Advance Pkt and Xmit Cmd - */ - - hp100_outw(HP100_DEBUG_EN | - HP100_RX_HDR | - HP100_EE_EN | HP100_RESET_HB | - HP100_IO_EN | - HP100_FAKE_INT | - HP100_INT_EN | HP100_RESET_LB, OPTION_LSW); - - hp100_outw(HP100_TRI_INT | HP100_SET_HB, OPTION_LSW); - - if (lp->mode == 1) { /* busmaster */ - hp100_outw(HP100_BM_WRITE | - HP100_BM_READ | - HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW); - } else if (lp->mode == 2) { /* memory mapped */ - hp100_outw(HP100_BM_WRITE | - HP100_BM_READ | HP100_RESET_HB, OPTION_LSW); - hp100_outw(HP100_MMAP_DIS | HP100_RESET_HB, OPTION_LSW); - hp100_outw(HP100_MEM_EN | HP100_SET_LB, OPTION_LSW); - hp100_outw(HP100_IO_EN | HP100_SET_LB, OPTION_LSW); - } else if (lp->mode == 3) { /* i/o mapped mode */ - hp100_outw(HP100_MMAP_DIS | HP100_SET_HB | - HP100_IO_EN | HP100_SET_LB, OPTION_LSW); - } - hp100_page(HW_MAP); - hp100_outb(0, EARLYRXCFG); - hp100_outw(0, EARLYTXCFG); - - /* - * Enable Bus Master mode - */ - if (lp->mode == 1) { /* busmaster */ - /* Experimental: Set some PCI configuration bits */ - hp100_page(HW_MAP); - hp100_andb(~HP100_PDL_USE3, MODECTRL1); /* BM engine read maximum */ - hp100_andb(~HP100_TX_DUALQ, MODECTRL1); /* No Queue for Priority TX */ - - /* PCI Bus failures should result in a Misc. Interrupt */ - hp100_orb(HP100_EN_BUS_FAIL, MODECTRL2); - - hp100_outw(HP100_BM_READ | HP100_BM_WRITE | HP100_SET_HB, OPTION_LSW); - hp100_page(HW_MAP); - /* Use Burst Mode and switch on PAGE_CK */ - hp100_orb(HP100_BM_BURST_RD | - HP100_BM_BURST_WR, BM); - if ((lp->chip == HP100_CHIPID_RAINIER) || (lp->chip == HP100_CHIPID_SHASTA)) - hp100_orb(HP100_BM_PAGE_CK, BM); - hp100_orb(HP100_BM_MASTER, BM); - } else { /* not busmaster */ - hp100_page(HW_MAP); - hp100_andb(~HP100_BM_MASTER, BM); - } - - /* - * Divide card memory into regions for Rx, Tx and, if non-ETR chip, PDLs - */ - hp100_page(MMU_CFG); - if (lp->mode == 1) { /* only needed for Busmaster */ - int xmit_stop, recv_stop; - - if ((lp->chip == HP100_CHIPID_RAINIER) || (lp->chip == HP100_CHIPID_SHASTA)) { - int pdl_stop; - - /* - * Each pdl is 508 bytes long. (63 frags * 4 bytes for address and - * 4 bytes for for header). We will leave NUM_RXPDLS * 508 (rounded - * to the next higher 1k boundary) bytes for the rx-pdl's - * Note: For non-etr chips the transmit stop register must be - * programmed on a 1k boundary, i.e. bits 9:0 must be zero. - */ - pdl_stop = lp->memory_size; - xmit_stop = (pdl_stop - 508 * (MAX_RX_PDL) - 16) & ~(0x03ff); - recv_stop = (xmit_stop * (lp->rx_ratio) / 100) & ~(0x03ff); - hp100_outw((pdl_stop >> 4) - 1, PDL_MEM_STOP); + if( 0!=(hp100_inw(OPTION_LSW)&HP100_HW_RST) ) + { + printk("hp100: %s: Not in reset when entering mmuinit. Fix me.\n",dev->name); + return; + } +#endif + + /* Make sure IRQs are masked off and ack'ed. */ + hp100_page( PERFORMANCE ); + hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all ints */ + hp100_outw( 0xffff, IRQ_STATUS ); /* ack IRQ */ + + /* + * Enable Hardware + * - Clear Debug En, Rx Hdr Pipe, EE En, I/O En, Fake Int and Intr En + * - Set Tri-State Int, Bus Master Rd/Wr, and Mem Map Disable + * - Clear Priority, Advance Pkt and Xmit Cmd + */ + + hp100_outw( HP100_DEBUG_EN | + HP100_RX_HDR | + HP100_EE_EN | HP100_RESET_HB | + HP100_IO_EN | + HP100_FAKE_INT | + HP100_INT_EN | HP100_RESET_LB, OPTION_LSW ); + + hp100_outw( HP100_TRI_INT | HP100_SET_HB, OPTION_LSW); + + if(lp->mode==1) /* busmaster */ + { + hp100_outw( HP100_BM_WRITE | + HP100_BM_READ | + HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW ); + } + else if(lp->mode==2) /* memory mapped */ + { + hp100_outw( HP100_BM_WRITE | + HP100_BM_READ | HP100_RESET_HB, OPTION_LSW ); + hp100_outw( HP100_MMAP_DIS | HP100_RESET_HB, OPTION_LSW ); + hp100_outw( HP100_MEM_EN | HP100_SET_LB, OPTION_LSW ); + hp100_outw( HP100_IO_EN | HP100_SET_LB, OPTION_LSW ); + } + else if( lp->mode==3 ) /* i/o mapped mode */ + { + hp100_outw( HP100_MMAP_DIS | HP100_SET_HB | + HP100_IO_EN | HP100_SET_LB, OPTION_LSW ); + } + + hp100_page( HW_MAP ); + hp100_outb( 0, EARLYRXCFG ); + hp100_outw( 0, EARLYTXCFG ); + + /* + * Enable Bus Master mode + */ + if(lp->mode==1) /* busmaster */ + { + /* Experimental: Set some PCI configuration bits */ + hp100_page( HW_MAP ); + hp100_andb( ~HP100_PDL_USE3, MODECTRL1 ); /* BM engine read maximum */ + hp100_andb( ~HP100_TX_DUALQ, MODECTRL1 ); /* No Queue for Priority TX */ + + /* PCI Bus failures should result in a Misc. Interrupt */ + hp100_orb( HP100_EN_BUS_FAIL, MODECTRL2); + + hp100_outw( HP100_BM_READ | HP100_BM_WRITE | HP100_SET_HB, OPTION_LSW ); + hp100_page( HW_MAP ); + /* Use Burst Mode and switch on PAGE_CK */ + hp100_orb( HP100_BM_BURST_RD | + HP100_BM_BURST_WR, BM); + if((lp->chip==HP100_CHIPID_RAINIER)||(lp->chip==HP100_CHIPID_SHASTA)) + hp100_orb( HP100_BM_PAGE_CK, BM ); + hp100_orb( HP100_BM_MASTER, BM ); + } + else /* not busmaster */ + { + hp100_page(HW_MAP); + hp100_andb(~HP100_BM_MASTER, BM ); + } + + /* + * Divide card memory into regions for Rx, Tx and, if non-ETR chip, PDLs + */ + hp100_page( MMU_CFG ); + if(lp->mode==1) /* only needed for Busmaster */ + { + int xmit_stop, recv_stop; + + if((lp->chip==HP100_CHIPID_RAINIER)||(lp->chip==HP100_CHIPID_SHASTA)) + { + int pdl_stop; + + /* + * Each pdl is 508 bytes long. (63 frags * 4 bytes for address and + * 4 bytes for for header). We will leave NUM_RXPDLS * 508 (rounded + * to the next higher 1k boundary) bytes for the rx-pdl's + * Note: For non-etr chips the transmit stop register must be + * programmed on a 1k boundary, i.e. bits 9:0 must be zero. + */ + pdl_stop = lp->memory_size; + xmit_stop = ( pdl_stop-508*(MAX_RX_PDL)-16 )& ~(0x03ff); + recv_stop = ( xmit_stop * (lp->rx_ratio)/100 ) &~(0x03ff); + hp100_outw( (pdl_stop>>4)-1, PDL_MEM_STOP ); #ifdef HP100_DEBUG_BM - printk("hp100: PDL_STOP = 0x%x\n", pdl_stop); + printk("hp100: %s: PDL_STOP = 0x%x\n", dev->name, pdl_stop); #endif - } else { /* ETR chip (Lassen) in busmaster mode */ - xmit_stop = (lp->memory_size) - 1; - recv_stop = ((lp->memory_size * lp->rx_ratio) / 100) & ~(0x03ff); - } + } + else /* ETR chip (Lassen) in busmaster mode */ + { + xmit_stop = ( lp->memory_size ) - 1; + recv_stop = ( ( lp->memory_size * lp->rx_ratio ) / 100 ) & ~(0x03ff); + } - hp100_outw(xmit_stop >> 4, TX_MEM_STOP); - hp100_outw(recv_stop >> 4, RX_MEM_STOP); + hp100_outw( xmit_stop>>4 , TX_MEM_STOP ); + hp100_outw( recv_stop>>4 , RX_MEM_STOP ); #ifdef HP100_DEBUG_BM - printk("hp100: TX_STOP = 0x%x\n", xmit_stop >> 4); - printk("hp100: RX_STOP = 0x%x\n", recv_stop >> 4); -#endif - } else { /* Slave modes (memory mapped and programmed io) */ - hp100_outw((((lp->memory_size * lp->rx_ratio) / 100) >> 4), RX_MEM_STOP); - hp100_outw(((lp->memory_size - 1) >> 4), TX_MEM_STOP); + printk("hp100: %s: TX_STOP = 0x%x\n",dev->name,xmit_stop>>4); + printk("hp100: %s: RX_STOP = 0x%x\n",dev->name,recv_stop>>4); +#endif + } + else /* Slave modes (memory mapped and programmed io) */ + { + hp100_outw( (((lp->memory_size*lp->rx_ratio)/100)>>4), RX_MEM_STOP ); + hp100_outw( ((lp->memory_size - 1 )>>4), TX_MEM_STOP ); #ifdef HP100_DEBUG - printk("hp100: TX_MEM_STOP: 0x%x\n", hp100_inw(TX_MEM_STOP)); - printk("hp100: RX_MEM_STOP: 0x%x\n", hp100_inw(RX_MEM_STOP)); -#endif - } - - /* Write MAC address into page 1 */ - hp100_page(MAC_ADDRESS); - for (i = 0; i < 6; i++) - hp100_outb(dev->dev_addr[i], MAC_ADDR + i); - - /* Zero the multicast hash registers */ - for (i = 0; i < 8; i++) - hp100_outb(0x0, HASH_BYTE0 + i); - - /* Set up MAC defaults */ - hp100_page(MAC_CTRL); - - /* Go to LAN Page and zero all filter bits */ - /* Zero accept error, accept multicast, accept broadcast and accept */ - /* all directed packet bits */ - hp100_andb(~(HP100_RX_EN | - HP100_TX_EN | - HP100_ACC_ERRORED | - HP100_ACC_MC | - HP100_ACC_BC | - HP100_ACC_PHY), MAC_CFG_1); - - hp100_outb(0x00, MAC_CFG_2); - - /* Zero the frame format bit. This works around a training bug in the */ - /* new hubs. */ - hp100_outb(0x00, VG_LAN_CFG_2); /* (use 802.3) */ - - if (lp->priority_tx) - hp100_outb(HP100_PRIORITY_TX | HP100_SET_LB, OPTION_MSW); - else - hp100_outb(HP100_PRIORITY_TX | HP100_RESET_LB, OPTION_MSW); - - hp100_outb(HP100_ADV_NXT_PKT | - HP100_TX_CMD | HP100_RESET_LB, OPTION_MSW); - - /* If busmaster, initialize the PDLs */ - if (lp->mode == 1) - hp100_init_pdls(dev); - - /* Go to performance page and initalize isr and imr registers */ - hp100_page(PERFORMANCE); - hp100_outw(0xfefe, IRQ_MASK); /* mask off all ints */ - hp100_outw(0xffff, IRQ_STATUS); /* ack IRQ */ + printk("hp100: %s: TX_MEM_STOP: 0x%x\n", dev->name,hp100_inw(TX_MEM_STOP)); + printk("hp100: %s: RX_MEM_STOP: 0x%x\n", dev->name,hp100_inw(RX_MEM_STOP)); +#endif + } + + /* Write MAC address into page 1 */ + hp100_page( MAC_ADDRESS ); + for ( i = 0; i < 6; i++ ) + hp100_outb( dev->dev_addr[ i ], MAC_ADDR + i ); + + /* Zero the multicast hash registers */ + for ( i = 0; i < 8; i++ ) + hp100_outb( 0x0, HASH_BYTE0 + i ); + + /* Set up MAC defaults */ + hp100_page( MAC_CTRL ); + + /* Go to LAN Page and zero all filter bits */ + /* Zero accept error, accept multicast, accept broadcast and accept */ + /* all directed packet bits */ + hp100_andb( ~(HP100_RX_EN| + HP100_TX_EN| + HP100_ACC_ERRORED| + HP100_ACC_MC| + HP100_ACC_BC| + HP100_ACC_PHY), MAC_CFG_1 ); + + hp100_outb( 0x00, MAC_CFG_2 ); + + /* Zero the frame format bit. This works around a training bug in the */ + /* new hubs. */ + hp100_outb( 0x00, VG_LAN_CFG_2); /* (use 802.3) */ + + if(lp->priority_tx) + hp100_outb( HP100_PRIORITY_TX | HP100_SET_LB, OPTION_MSW ); + else + hp100_outb( HP100_PRIORITY_TX | HP100_RESET_LB, OPTION_MSW ); + + hp100_outb( HP100_ADV_NXT_PKT | + HP100_TX_CMD | HP100_RESET_LB, OPTION_MSW ); + + /* If busmaster, initialize the PDLs */ + if(lp->mode==1) + hp100_init_pdls( dev ); + + /* Go to performance page and initalize isr and imr registers */ + hp100_page( PERFORMANCE ); + hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all ints */ + hp100_outw( 0xffff, IRQ_STATUS ); /* ack IRQ */ } - + /* * open/close functions */ -static int hp100_open(struct device *dev) +static int hp100_open( struct device *dev ) { - struct hp100_private *lp = (struct hp100_private *) dev->priv; + struct hp100_private *lp = (struct hp100_private *)dev->priv; #ifdef HP100_DEBUG_B - int ioaddr = dev->base_addr; + int ioaddr=dev->base_addr; #endif #ifdef HP100_DEBUG_B - hp100_outw(0x4204, TRACE); - printk("hp100: open\n"); -#endif - - /* New: if bus is PCI or EISA, interrupts might be shared interrupts */ - if ((lp->bus == HP100_BUS_PCI) || (lp->bus == HP100_BUS_EISA)) { - if (request_irq(dev->irq, hp100_interrupt, SA_SHIRQ, lp->id->name, dev)) { - printk("%s: unable to get IRQ %d\n", dev->name, dev->irq); - return -EAGAIN; - } - } else if (request_irq(dev->irq, hp100_interrupt, SA_INTERRUPT, lp->id->name, dev)) { - printk("%s: unable to get IRQ %d\n", dev->name, dev->irq); - return -EAGAIN; - } - MOD_INC_USE_COUNT; - - dev->tbusy = 0; - dev->trans_start = jiffies; - dev->interrupt = 0; - dev->start = 1; - - lp->lan_type = hp100_sense_lan(dev); - lp->mac1_mode = HP100_MAC1MODE3; - lp->mac2_mode = HP100_MAC2MODE3; - - hp100_stop_interface(dev); - - hp100_hwinit(dev); - - hp100_start_interface(dev); /* sets mac modes, enables interrupts */ - - return 0; + hp100_outw( 0x4204, TRACE ); + printk("hp100: %s: open\n",dev->name); +#endif + + /* New: if bus is PCI or EISA, interrupts might be shared interrupts */ + if ( request_irq(dev->irq, hp100_interrupt, + lp->bus==HP100_BUS_PCI||lp->bus==HP100_BUS_EISA?SA_SHIRQ:SA_INTERRUPT, + lp->id->name, dev)) + { + printk( "hp100: %s: unable to get IRQ %d\n", dev->name, dev->irq ); + return -EAGAIN; + } + + MOD_INC_USE_COUNT; + + dev->tbusy = 0; + dev->trans_start = jiffies; + dev->interrupt = 0; + dev->start = 1; + + lp->lan_type = hp100_sense_lan( dev ); + lp->mac1_mode = HP100_MAC1MODE3; + lp->mac2_mode = HP100_MAC2MODE3; + memset( &lp->hash_bytes, 0x00, 8 ); + + hp100_stop_interface( dev ); + + hp100_hwinit( dev ); + + hp100_start_interface( dev ); /* sets mac modes, enables interrupts */ + + return 0; } - + /* The close function is called when the interface is to be brought down */ -static int hp100_close(struct device *dev) +static int hp100_close( struct device *dev ) { - int ioaddr = dev->base_addr; - struct hp100_private *lp = (struct hp100_private *) dev->priv; + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *)dev->priv; #ifdef HP100_DEBUG_B - hp100_outw(0x4205, TRACE); - printk("hp100:close\n"); + hp100_outw( 0x4205, TRACE ); + printk("hp100: %s: close\n", dev->name); #endif - hp100_page(PERFORMANCE); - hp100_outw(0xfefe, IRQ_MASK); /* mask off all IRQs */ + hp100_page( PERFORMANCE ); + hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all IRQs */ + + hp100_stop_interface( dev ); + + if ( lp->lan_type == HP100_LAN_100 ) + lp->hub_status=hp100_login_to_vg_hub( dev, FALSE ); - hp100_stop_interface(dev); + dev->tbusy = 1; + dev->start = 0; - if (lp->lan_type == HP100_LAN_100) - lp->hub_status = hp100_login_to_vg_hub(dev, FALSE); + free_irq( dev->irq, dev ); - dev->tbusy = 1; - dev->start = 0; +#ifdef HP100_DEBUG + printk( "hp100: %s: close LSW = 0x%x\n", dev->name, hp100_inw(OPTION_LSW) ); +#endif - if ((lp->bus == HP100_BUS_PCI) || (lp->bus == HP100_BUS_EISA)) - free_irq(dev->irq, dev); - else - free_irq(dev->irq, NULL); - MOD_DEC_USE_COUNT; - return 0; + MOD_DEC_USE_COUNT; + return 0; } - + /* * Configure the PDL Rx rings and LAN */ -static void hp100_init_pdls(struct device *dev) +static void hp100_init_pdls( struct device *dev ) { - struct hp100_private *lp = (struct hp100_private *) dev->priv; - hp100_ring_t *ringptr; - u_int *pageptr; - int i; + struct hp100_private *lp = (struct hp100_private *)dev->priv; + hp100_ring_t *ringptr; + u_int *pageptr; + int i; #ifdef HP100_DEBUG_B - int ioaddr = dev->base_addr; + int ioaddr = dev->base_addr; #endif #ifdef HP100_DEBUG_B - hp100_outw(0x4206, TRACE); - printk("hp100: init pdls\n"); -#endif - - if (0 == lp->page_vaddr_algn) - printk("hp100: Warning: lp->page_vaddr_algn not initialised!\n"); - else { - /* pageptr shall point into the DMA accessible memory region */ - /* we use this pointer to status the upper limit of allocated */ - /* memory in the allocated page. */ - /* note: align the pointers to the pci cache line size */ - memset(lp->page_vaddr_algn, 0, MAX_RINGSIZE); /* Zero Rx/Tx ring page */ - pageptr = lp->page_vaddr_algn; - - lp->rxrcommit = 0; - ringptr = lp->rxrhead = lp->rxrtail = &(lp->rxring[0]); - - /* Initialise Rx Ring */ - for (i = MAX_RX_PDL - 1; i >= 0; i--) { - lp->rxring[i].next = ringptr; - ringptr = &(lp->rxring[i]); - pageptr += hp100_init_rxpdl(ringptr, pageptr); - } - - /* Initialise Tx Ring */ - lp->txrcommit = 0; - ringptr = lp->txrhead = lp->txrtail = &(lp->txring[0]); - for (i = MAX_TX_PDL - 1; i >= 0; i--) { - lp->txring[i].next = ringptr; - ringptr = &(lp->txring[i]); - pageptr += hp100_init_txpdl(ringptr, pageptr); - } - } + hp100_outw( 0x4206, TRACE ); + printk("hp100: %s: init pdls\n", dev->name); +#endif + + if(0==lp->page_vaddr_algn) + printk("hp100: %s: Warning: lp->page_vaddr_algn not initialised!\n",dev->name); + else + { + /* pageptr shall point into the DMA accessible memory region */ + /* we use this pointer to status the upper limit of allocated */ + /* memory in the allocated page. */ + /* note: align the pointers to the pci cache line size */ + memset(lp->page_vaddr_algn, 0, MAX_RINGSIZE); /* Zero Rx/Tx ring page */ + pageptr=lp->page_vaddr_algn; + + lp->rxrcommit =0; + ringptr = lp->rxrhead = lp-> rxrtail = &(lp->rxring[0]); + + /* Initialise Rx Ring */ + for (i=MAX_RX_PDL-1; i>=0; i--) + { + lp->rxring[i].next = ringptr; + ringptr=&(lp->rxring[i]); + pageptr+=hp100_init_rxpdl(dev, ringptr, pageptr); + } + + /* Initialise Tx Ring */ + lp->txrcommit = 0; + ringptr = lp->txrhead = lp->txrtail = &(lp->txring[0]); + for (i=MAX_TX_PDL-1; i>=0; i--) + { + lp->txring[i].next = ringptr; + ringptr=&(lp->txring[i]); + pageptr+=hp100_init_txpdl(dev, ringptr, pageptr); + } + } } - + /* These functions "format" the entries in the pdl structure */ /* They return how much memory the fragments need. */ -static int hp100_init_rxpdl(register hp100_ring_t * ringptr, register u32 * pdlptr) +static int hp100_init_rxpdl( struct device *dev, register hp100_ring_t *ringptr, register u32 *pdlptr ) { - /* pdlptr is starting adress for this pdl */ + /* pdlptr is starting adress for this pdl */ - if (0 != (((unsigned) pdlptr) & 0xf)) - printk("hp100: Init rxpdl: Unaligned pdlptr 0x%x.\n", (unsigned) pdlptr); + if( 0!=( ((unsigned)pdlptr) & 0xf) ) + printk("hp100: %s: Init rxpdl: Unaligned pdlptr 0x%x.\n",dev->name,(unsigned)pdlptr); - ringptr->pdl = pdlptr + 1; - ringptr->pdl_paddr = virt_to_bus(pdlptr + 1); - ringptr->skb = (void *) NULL; + ringptr->pdl = pdlptr+1; + ringptr->pdl_paddr = virt_to_bus(pdlptr+1); + ringptr->skb = (void *) NULL; - /* - * Write address and length of first PDL Fragment (which is used for - * storing the RX-Header - * We use the 4 bytes _before_ the PDH in the pdl memory area to - * store this information. (PDH is at offset 0x04) - */ - /* Note that pdlptr+1 and not pdlptr is the pointer to the PDH */ + /* + * Write address and length of first PDL Fragment (which is used for + * storing the RX-Header + * We use the 4 bytes _before_ the PDH in the pdl memory area to + * store this information. (PDH is at offset 0x04) + */ + /* Note that pdlptr+1 and not pdlptr is the pointer to the PDH */ - *(pdlptr + 2) = (u_int) virt_to_bus(pdlptr); /* Address Frag 1 */ - *(pdlptr + 3) = 4; /* Length Frag 1 */ + *(pdlptr+2) =(u_int) virt_to_bus(pdlptr); /* Address Frag 1 */ + *(pdlptr+3) = 4; /* Length Frag 1 */ - return ((((MAX_RX_FRAG * 2 + 2) + 3) / 4) * 4); + return( ( ((MAX_RX_FRAG*2+2)+3) /4)*4 ); } -static int hp100_init_txpdl(register hp100_ring_t * ringptr, register u32 * pdlptr) +static int hp100_init_txpdl( struct device *dev, register hp100_ring_t *ringptr, register u32 *pdlptr ) { - if (0 != (((unsigned) pdlptr) & 0xf)) - printk("hp100: Init txpdl: Unaligned pdlptr 0x%x.\n", (unsigned) pdlptr); - - ringptr->pdl = pdlptr; /* +1; */ - ringptr->pdl_paddr = virt_to_bus(pdlptr); /* +1 */ - ringptr->skb = (void *) NULL; - - return ((((MAX_TX_FRAG * 2 + 2) + 3) / 4) * 4); + if( 0!=( ((unsigned)pdlptr) & 0xf) ) + printk("hp100: %s: Init txpdl: Unaligned pdlptr 0x%x.\n",dev->name,(unsigned) pdlptr); + + ringptr->pdl = pdlptr; /* +1; */ + ringptr->pdl_paddr = virt_to_bus(pdlptr); /* +1 */ + ringptr->skb = (void *) NULL; + + return((((MAX_TX_FRAG*2+2)+3)/4)*4); } - + /* * hp100_build_rx_pdl allocates an skb_buff of maximum size plus two bytes * for possible odd word alignment rounding up to next dword and set PDL @@ -1119,77 +1309,80 @@ static int hp100_init_txpdl(register hp100_ring_t * ringptr, register u32 * pdlp * Returns: 0 if unable to allocate skb_buff * 1 if successful */ -int hp100_build_rx_pdl(hp100_ring_t * ringptr, struct device *dev) +int hp100_build_rx_pdl( hp100_ring_t *ringptr, struct device *dev ) { #ifdef HP100_DEBUG_B - int ioaddr = dev->base_addr; + int ioaddr = dev->base_addr; #endif #ifdef HP100_DEBUG_BM - u_int *p; + u_int *p; #endif #ifdef HP100_DEBUG_B - hp100_outw(0x4207, TRACE); - printk("hp100: build rx pdl\n"); -#endif - - /* Allocate skb buffer of maximum size */ - /* Note: This depends on the alloc_skb functions allocating more - * space than requested, i.e. aligning to 16bytes */ - - ringptr->skb = dev_alloc_skb(((MAX_ETHER_SIZE + 2 + 3) / 4) * 4); - - if (NULL != ringptr->skb) { - /* - * Reserve 2 bytes at the head of the buffer to land the IP header - * on a long word boundary (According to the Network Driver section - * in the Linux KHG, this should help to increase performance.) - */ - skb_reserve(ringptr->skb, 2); - - ringptr->skb->dev = dev; - ringptr->skb->data = (u_char *) skb_put(ringptr->skb, MAX_ETHER_SIZE); - - /* ringptr->pdl points to the beginning of the PDL, i.e. the PDH */ - /* Note: 1st Fragment is used for the 4 byte packet status - * (receive header). Its PDL entries are set up by init_rxpdl. So - * here we only have to set up the PDL fragment entries for the data - * part. Those 4 bytes will be stored in the DMA memory region - * directly before the PDL. - */ + hp100_outw( 0x4207, TRACE ); + printk("hp100: %s: build rx pdl\n", dev->name); +#endif + + /* Allocate skb buffer of maximum size */ + /* Note: This depends on the alloc_skb functions allocating more + * space than requested, i.e. aligning to 16bytes */ + + ringptr->skb = dev_alloc_skb( ((MAX_ETHER_SIZE+2+3)/4)*4 ); + + if(NULL!=ringptr->skb) + { + /* + * Reserve 2 bytes at the head of the buffer to land the IP header + * on a long word boundary (According to the Network Driver section + * in the Linux KHG, this should help to increase performance.) + */ + skb_reserve(ringptr->skb, 2); + + ringptr->skb->dev=dev; + ringptr->skb->data=(u_char *)skb_put(ringptr->skb, MAX_ETHER_SIZE ); + + /* ringptr->pdl points to the beginning of the PDL, i.e. the PDH */ + /* Note: 1st Fragment is used for the 4 byte packet status + * (receive header). Its PDL entries are set up by init_rxpdl. So + * here we only have to set up the PDL fragment entries for the data + * part. Those 4 bytes will be stored in the DMA memory region + * directly before the PDL. + */ #ifdef HP100_DEBUG_BM - printk("hp100: build_rx_pdl: PDH@0x%x, skb->data (len %d) at 0x%x\n", - (u_int) ringptr->pdl, - ((MAX_ETHER_SIZE + 2 + 3) / 4) * 4, - (unsigned int) ringptr->skb->data); + printk("hp100: %s: build_rx_pdl: PDH@0x%x, skb->data (len %d) at 0x%x\n", + dev->name, + (u_int) ringptr->pdl, + ((MAX_ETHER_SIZE+2+3)/4)*4, + (unsigned int) ringptr->skb->data); #endif - ringptr->pdl[0] = 0x00020000; /* Write PDH */ - ringptr->pdl[3] = ((u_int) virt_to_bus(ringptr->skb->data)); - ringptr->pdl[4] = MAX_ETHER_SIZE; /* Length of Data */ - + ringptr->pdl[0] = 0x00020000; /* Write PDH */ + ringptr->pdl[3] = ((u_int)virt_to_bus(ringptr->skb->data)); + ringptr->pdl[4] = MAX_ETHER_SIZE; /* Length of Data */ + #ifdef HP100_DEBUG_BM - for (p = (ringptr->pdl); p < (ringptr->pdl + 5); p++) - printk("Adr 0x%.8x = 0x%.8x\n", (u_int) p, (u_int) * p); -#endif - return (1); - } - /* else: */ - /* alloc_skb failed (no memory) -> still can receive the header - * fragment into PDL memory. make PDL safe by clearing msgptr and - * making the PDL only 1 fragment (i.e. the 4 byte packet status) - */ + for(p=(ringptr->pdl); p<(ringptr->pdl+5); p++) + printk("hp100: %s: Adr 0x%.8x = 0x%.8x\n",dev->name,(u_int) p,(u_int) *p ); +#endif + return(1); + } + /* else: */ + /* alloc_skb failed (no memory) -> still can receive the header + * fragment into PDL memory. make PDL safe by clearing msgptr and + * making the PDL only 1 fragment (i.e. the 4 byte packet status) + */ #ifdef HP100_DEBUG_BM - printk("hp100: build_rx_pdl: PDH@0x%x, No space for skb.\n", - (u_int) ringptr->pdl); + printk("hp100: %s: build_rx_pdl: PDH@0x%x, No space for skb.\n", + dev->name, + (u_int) ringptr->pdl); #endif - ringptr->pdl[0] = 0x00010000; /* PDH: Count=1 Fragment */ + ringptr->pdl[0]=0x00010000; /* PDH: Count=1 Fragment */ - return (0); + return(0); } - + /* * hp100_rxfill - attempt to fill the Rx Ring will empty skb's * @@ -1200,246 +1393,279 @@ int hp100_build_rx_pdl(hp100_ring_t * ringptr, struct device *dev) * b. Put the physical address of the buffer into the PDL. * c. Output physical address of PDL to adapter. */ -static void hp100_rxfill(struct device *dev) +static void hp100_rxfill( struct device *dev ) { - int ioaddr = dev->base_addr; + int ioaddr=dev->base_addr; - struct hp100_private *lp = (struct hp100_private *) dev->priv; - hp100_ring_t *ringptr; + struct hp100_private *lp = (struct hp100_private *)dev->priv; + hp100_ring_t *ringptr; #ifdef HP100_DEBUG_B - hp100_outw(0x4208, TRACE); - printk("hp100: rxfill\n"); -#endif - - hp100_page(PERFORMANCE); - - while (lp->rxrcommit < MAX_RX_PDL) { - /* - ** Attempt to get a buffer and build a Rx PDL. - */ - ringptr = lp->rxrtail; - if (0 == hp100_build_rx_pdl(ringptr, dev)) { - return; /* None available, return */ - } - /* Hand this PDL over to the card */ - /* Note: This needs performance page selected! */ + hp100_outw( 0x4208, TRACE ); + printk("hp100: %s: rxfill\n",dev->name); +#endif + + hp100_page( PERFORMANCE ); + + while (lp->rxrcommit < MAX_RX_PDL) + { + /* + ** Attempt to get a buffer and build a Rx PDL. + */ + ringptr = lp->rxrtail; + if (0 == hp100_build_rx_pdl( ringptr, dev )) + { + return; /* None available, return */ + } + + /* Hand this PDL over to the card */ + /* Note: This needs performance page selected! */ #ifdef HP100_DEBUG_BM - printk("hp100: rxfill: Hand to card: pdl #%d @0x%x phys:0x%x, buffer: 0x%x\n", - lp->rxrcommit, - (u_int) ringptr->pdl, - (u_int) ringptr->pdl_paddr, - (u_int) ringptr->pdl[3]); -#endif - - hp100_outl((u32) ringptr->pdl_paddr, RX_PDA); - - lp->rxrcommit += 1; - lp->rxrtail = ringptr->next; - } + printk("hp100: %s: rxfill: Hand to card: pdl #%d @0x%x phys:0x%x, buffer: 0x%x\n", + dev->name, + lp->rxrcommit, + (u_int)ringptr->pdl, + (u_int)ringptr->pdl_paddr, + (u_int)ringptr->pdl[3]); +#endif + + hp100_outl( (u32)ringptr->pdl_paddr, RX_PDA); + + lp->rxrcommit += 1; + lp->rxrtail = ringptr->next; + } } - + /* * BM_shutdown - shutdown bus mastering and leave chip in reset state */ -static void hp100_BM_shutdown(struct device *dev) +static void hp100_BM_shutdown( struct device *dev ) { - int ioaddr = dev->base_addr; - struct hp100_private *lp = (struct hp100_private *) dev->priv; - unsigned long time; + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *)dev->priv; + unsigned long time; #ifdef HP100_DEBUG_B - hp100_outw(0x4209, TRACE); - printk("hp100: bm shutdown\n"); -#endif - - hp100_page(PERFORMANCE); - hp100_outw(0xfefe, IRQ_MASK); /* mask off all ints */ - hp100_outw(0xffff, IRQ_STATUS); /* Ack all ints */ - - /* Ensure Interrupts are off */ - hp100_outw(HP100_INT_EN | HP100_RESET_LB, OPTION_LSW); - - /* Disable all MAC activity */ - hp100_page(MAC_CTRL); - hp100_andb(~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1); /* stop rx/tx */ - - /* If cascade MMU is not already in reset */ - if (0 != (hp100_inw(OPTION_LSW) & HP100_HW_RST)) { - /* Wait 1.3ms (10Mb max packet time) to ensure MAC is idle so - * MMU pointers will not be reset out from underneath - */ - hp100_page(MAC_CTRL); - for (time = 0; time < 5000; time++) { - if ((hp100_inb(MAC_CFG_1) & (HP100_TX_IDLE | HP100_RX_IDLE)) == - (HP100_TX_IDLE | HP100_RX_IDLE)) - break; - } - - /* Shutdown algorithm depends on the generation of Cascade */ - if (lp->chip == HP100_CHIPID_LASSEN) { /* ETR shutdown/reset */ - /* Disable Busmaster mode and wait for bit to go to zero. */ - hp100_page(HW_MAP); - hp100_andb(~HP100_BM_MASTER, BM); - /* 100 ms timeout */ - for (time = 0; time < 32000; time++) { - if (0 == (hp100_inb(BM) & HP100_BM_MASTER)) - break; - } - } else { /* Shasta or Rainier Shutdown/Reset */ - /* To ensure all bus master inloading activity has ceased, - * wait for no Rx PDAs or no Rx packets on card. - */ - hp100_page(PERFORMANCE); - /* 100 ms timeout */ - for (time = 0; time < 10000; time++) { - /* RX_PDL: PDLs not executed. */ - /* RX_PKT_CNT: RX'd packets on card. */ - if ((hp100_inb(RX_PDL) == 0) && - (hp100_inb(RX_PKT_CNT) == 0)) - break; - } - - if (time >= 10000) - printk("hp100: BM shutdown error.\n"); - - /* To ensure all bus master outloading activity has ceased, - * wait until the Tx PDA count goes to zero or no more Tx space - * available in the Tx region of the card. - */ - /* 100 ms timeout */ - for (time = 0; time < 10000; time++) { - if ((0 == hp100_inb(TX_PKT_CNT)) && - (0 != (hp100_inb(TX_MEM_FREE) & HP100_AUTO_COMPARE))) - break; - } - - /* Disable Busmaster mode */ - hp100_page(HW_MAP); - hp100_andb(~HP100_BM_MASTER, BM); - } /* end of shutdown procedure for non-etr parts */ - - hp100_cascade_reset(dev, TRUE); - } - hp100_page(PERFORMANCE); - hp100_outw(HP100_BM_READ | HP100_BM_WRITE | HP100_RESET_HB, OPTION_LSW); - /* Busmaster mode should be shut down now. */ + hp100_outw( 0x4209, TRACE ); + printk("hp100: %s: bm shutdown\n",dev->name); +#endif + + hp100_page( PERFORMANCE ); + hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all ints */ + hp100_outw( 0xffff, IRQ_STATUS ); /* Ack all ints */ + + /* Ensure Interrupts are off */ + hp100_outw( HP100_INT_EN | HP100_RESET_LB , OPTION_LSW ); + + /* Disable all MAC activity */ + hp100_page( MAC_CTRL ); + hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 ); /* stop rx/tx */ + + /* If cascade MMU is not already in reset */ + if (0 != (hp100_inw(OPTION_LSW)&HP100_HW_RST) ) + { + /* Wait 1.3ms (10Mb max packet time) to ensure MAC is idle so + * MMU pointers will not be reset out from underneath + */ + hp100_page( MAC_CTRL ); + for(time=0; time<5000; time++) + { + if( (hp100_inb(MAC_CFG_1)&(HP100_TX_IDLE|HP100_RX_IDLE))== + (HP100_TX_IDLE|HP100_RX_IDLE) ) break; + } + + /* Shutdown algorithm depends on the generation of Cascade */ + if( lp->chip==HP100_CHIPID_LASSEN ) + { /* ETR shutdown/reset */ + /* Disable Busmaster mode and wait for bit to go to zero. */ + hp100_page(HW_MAP); + hp100_andb( ~HP100_BM_MASTER, BM ); + /* 100 ms timeout */ + for(time=0; time<32000; time++) + { + if ( 0 == (hp100_inb( BM ) & HP100_BM_MASTER) ) break; + } + } + else + { /* Shasta or Rainier Shutdown/Reset */ + /* To ensure all bus master inloading activity has ceased, + * wait for no Rx PDAs or no Rx packets on card. + */ + hp100_page( PERFORMANCE ); + /* 100 ms timeout */ + for(time=0; time<10000; time++) + { + /* RX_PDL: PDLs not executed. */ + /* RX_PKT_CNT: RX'd packets on card. */ + if ( (hp100_inb( RX_PDL ) == 0) && + (hp100_inb( RX_PKT_CNT ) == 0) ) break; + } + + if(time>=10000) + printk("hp100: %s: BM shutdown error.\n", dev->name); + + /* To ensure all bus master outloading activity has ceased, + * wait until the Tx PDA count goes to zero or no more Tx space + * available in the Tx region of the card. + */ + /* 100 ms timeout */ + for(time=0; time<10000; time++) { + if ( (0 == hp100_inb( TX_PKT_CNT )) && + (0 != (hp100_inb( TX_MEM_FREE )&HP100_AUTO_COMPARE))) break; + } + + /* Disable Busmaster mode */ + hp100_page(HW_MAP); + hp100_andb( ~HP100_BM_MASTER, BM ); + } /* end of shutdown procedure for non-etr parts */ + + hp100_cascade_reset( dev, TRUE ); + } + hp100_page( PERFORMANCE ); + /* hp100_outw( HP100_BM_READ | HP100_BM_WRITE | HP100_RESET_HB, OPTION_LSW ); */ + /* Busmaster mode should be shut down now. */ } - + /* * transmit functions */ /* tx function for busmaster mode */ -static int hp100_start_xmit_bm(struct sk_buff *skb, struct device *dev) +static int hp100_start_xmit_bm( struct sk_buff *skb, struct device *dev ) { - int i, ok_flag; - int ioaddr = dev->base_addr; - struct hp100_private *lp = (struct hp100_private *) dev->priv; - hp100_ring_t *ringptr; + unsigned long flags; + int i, ok_flag; + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *)dev->priv; + hp100_ring_t *ringptr; #ifdef HP100_DEBUG_B - hp100_outw(0x4210, TRACE); - printk("hp100: start_xmit_bm\n"); -#endif - - /* Get Tx ring tail pointer */ - if (lp->txrtail->next == lp->txrhead) { - /* No memory. */ + hp100_outw( 0x4210, TRACE ); + printk("hp100: %s: start_xmit_bm\n",dev->name); +#endif + + if ( skb==NULL ) + { +#ifndef LINUX_2_1 + dev_tint( dev ); +#endif + return 0; + } + + if ( skb->len <= 0 ) return 0; + + /* Get Tx ring tail pointer */ + if( lp->txrtail->next==lp->txrhead ) + { + /* No memory. */ #ifdef HP100_DEBUG - printk("hp100: start_xmit_bm: No TX PDL available.\n"); -#endif - /* not waited long enough since last tx? */ - if (jiffies - dev->trans_start < HZ / 10) - return -EAGAIN; - - if (lp->lan_type < 0) { /* no LAN type detected yet? */ - hp100_stop_interface(dev); - if ((lp->lan_type = hp100_sense_lan(dev)) < 0) { - printk("%s: no connection found - check wire\n", dev->name); - hp100_start_interface(dev); /* 10Mb/s RX pkts maybe handled */ - return -EIO; - } - if (lp->lan_type == HP100_LAN_100) - lp->hub_status = hp100_login_to_vg_hub(dev, FALSE); /* relogin */ - hp100_start_interface(dev); - } - if (lp->lan_type == HP100_LAN_100 && lp->hub_status < 0) - /* we have a 100Mb/s adapter but it isn't connected to hub */ - { - printk("%s: login to 100Mb/s hub retry\n", dev->name); - hp100_stop_interface(dev); - lp->hub_status = hp100_login_to_vg_hub(dev, FALSE); - hp100_start_interface(dev); - } else { - hp100_ints_off(); - i = hp100_sense_lan(dev); - hp100_page(PERFORMANCE); - hp100_ints_on(); - if (i == HP100_LAN_ERR) - printk("%s: link down detected\n", dev->name); - else if (lp->lan_type != i) { /* cable change! */ - /* it's very hard - all network setting must be changed!!! */ - printk("%s: cable change 10Mb/s <-> 100Mb/s detected\n", dev->name); - lp->lan_type = i; - hp100_stop_interface(dev); - if (lp->lan_type == HP100_LAN_100) - lp->hub_status = hp100_login_to_vg_hub(dev, FALSE); - hp100_start_interface(dev); - } else { - printk("%s: interface reset\n", dev->name); - hp100_stop_interface(dev); - hp100_start_interface(dev); - } - } - - dev->trans_start = jiffies; - return -EAGAIN; + printk("hp100: %s: start_xmit_bm: No TX PDL available.\n", dev->name); +#endif + /* not waited long enough since last tx? */ + if ( jiffies - dev->trans_start < HZ ) return -EAGAIN; + + if ( lp->lan_type < 0 ) /* no LAN type detected yet? */ + { + hp100_stop_interface( dev ); + if ( ( lp->lan_type = hp100_sense_lan( dev ) ) < 0 ) + { + printk( "hp100: %s: no connection found - check wire\n", dev->name ); + hp100_start_interface( dev ); /* 10Mb/s RX pkts maybe handled */ + return -EIO; + } + if ( lp->lan_type == HP100_LAN_100 ) + lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); /* relogin */ + hp100_start_interface( dev ); } - /* - * we have to turn int's off before modifying this, otherwise - * a tx_pdl_cleanup could occur at the same time - */ - cli(); - ringptr = lp->txrtail; - lp->txrtail = ringptr->next; - - /* Check whether packet has minimal packet size */ - ok_flag = skb->len >= HP100_MIN_PACKET_SIZE; - i = ok_flag ? skb->len : HP100_MIN_PACKET_SIZE; - - ringptr->skb = skb; - ringptr->pdl[0] = ((1 << 16) | i); /* PDH: 1 Fragment & length */ - ringptr->pdl[1] = (u32) virt_to_bus(skb->data); /* 1st Frag: Adr. of data */ - if (lp->chip == HP100_CHIPID_SHASTA) { - /* TODO:Could someone who has the EISA card please check if this works? */ - ringptr->pdl[2] = i; - } else { /* Lassen */ - /* In the PDL, don't use the padded size but the real packet size: */ - ringptr->pdl[2] = skb->len; /* 1st Frag: Length of frag */ + + if ( lp->lan_type == HP100_LAN_100 && lp->hub_status < 0 ) + /* we have a 100Mb/s adapter but it isn't connected to hub */ + { + printk( "hp100: %s: login to 100Mb/s hub retry\n", dev->name ); + hp100_stop_interface( dev ); + lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); + hp100_start_interface( dev ); + } + else + { + hp100_ints_off(); + i = hp100_sense_lan( dev ); + hp100_ints_on(); + if ( i == HP100_LAN_ERR ) + printk( "hp100: %s: link down detected\n", dev->name ); + else + if ( lp->lan_type != i ) /* cable change! */ + { + /* it's very hard - all network setting must be changed!!! */ + printk( "hp100: %s: cable change 10Mb/s <-> 100Mb/s detected\n", dev->name ); + lp->lan_type = i; + hp100_stop_interface( dev ); + if ( lp->lan_type == HP100_LAN_100 ) + lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); + hp100_start_interface( dev ); + } + else + { + printk( "hp100: %s: interface reset\n", dev->name ); + hp100_stop_interface( dev ); + if ( lp->lan_type == HP100_LAN_100 ) + lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); + hp100_start_interface( dev ); + } } - /* Hand this PDL to the card. */ - hp100_outl(ringptr->pdl_paddr, TX_PDA_L); /* Low Prio. Queue */ - - lp->txrcommit++; - sti(); - - /* Update statistics */ - lp->stats.tx_packets++; + dev->trans_start = jiffies; + return -EAGAIN; + } + + /* + * we have to turn int's off before modifying this, otherwise + * a tx_pdl_cleanup could occur at the same time + */ + save_flags( flags ); + cli(); + ringptr=lp->txrtail; + lp->txrtail=ringptr->next; + + /* Check whether packet has minimal packet size */ + ok_flag = skb->len >= HP100_MIN_PACKET_SIZE; + i = ok_flag ? skb->len : HP100_MIN_PACKET_SIZE; + + ringptr->skb=skb; + ringptr->pdl[0]=((1<<16) | i); /* PDH: 1 Fragment & length */ + ringptr->pdl[1]=(u32)virt_to_bus(skb->data); /* 1st Frag: Adr. of data */ + if(lp->chip==HP100_CHIPID_SHASTA) + { + /* TODO:Could someone who has the EISA card please check if this works? */ + ringptr->pdl[2]=i; + } + else /* Lassen */ + { + /* In the PDL, don't use the padded size but the real packet size: */ + ringptr->pdl[2]=skb->len; /* 1st Frag: Length of frag */ + } + + /* Hand this PDL to the card. */ + hp100_outl( ringptr->pdl_paddr, TX_PDA_L ); /* Low Prio. Queue */ + + lp->txrcommit++; + restore_flags( flags ); + + /* Update statistics */ + lp->stats.tx_packets++; #ifdef LINUX_2_1 - lp->stats.tx_bytes += skb->len; + lp->stats.tx_bytes += skb->len; #endif - dev->trans_start = jiffies; - - return 0; + dev->trans_start = jiffies; + + return 0; } - + /* clean_txring checks if packets have been sent by the card by reading * the TX_PDL register from the performance page and comparing it to the * number of commited packets. It then frees the skb's of the packets that @@ -1447,168 +1673,209 @@ static int hp100_start_xmit_bm(struct sk_buff *skb, struct device *dev) * * Needs the PERFORMANCE page selected. */ -static void hp100_clean_txring(struct device *dev) +static void hp100_clean_txring( struct device *dev ) { - struct hp100_private *lp = (struct hp100_private *) dev->priv; - int ioaddr = dev->base_addr; - int donecount; + struct hp100_private *lp = (struct hp100_private *)dev->priv; + int ioaddr = dev->base_addr; + int donecount; #ifdef HP100_DEBUG_B - hp100_outw(0x4211, TRACE); - printk("hp100: clean txring\n"); + hp100_outw( 0x4211, TRACE ); + printk("hp100: %s: clean txring\n", dev->name); #endif - /* How many PDLs have been transmitted? */ - donecount = (lp->txrcommit) - hp100_inb(TX_PDL); + /* How many PDLs have been transmitted? */ + donecount=(lp->txrcommit)-hp100_inb(TX_PDL); #ifdef HP100_DEBUG - if (donecount > MAX_TX_PDL) - printk("hp100: Warning: More PDLs transmitted than commited to card???\n"); + if(donecount>MAX_TX_PDL) + printk("hp100: %s: Warning: More PDLs transmitted than commited to card???\n",dev->name); #endif - for (; 0 != donecount; donecount--) { + for( ; 0!=donecount; donecount-- ) + { #ifdef HP100_DEBUG_BM - printk("hp100: Free skb: data @0x%.8x txrcommit=0x%x TXPDL=0x%x, done=0x%x\n", - (u_int) lp->txrhead->skb->data, - lp->txrcommit, - hp100_inb(TX_PDL), - donecount); -#endif - dev_kfree_skb(lp->txrhead->skb); - lp->txrhead->skb = (void *) NULL; - lp->txrhead = lp->txrhead->next; - lp->txrcommit--; - } + printk("hp100: %s: Free skb: data @0x%.8x txrcommit=0x%x TXPDL=0x%x, done=0x%x\n", + dev->name, + (u_int) lp->txrhead->skb->data, + lp->txrcommit, + hp100_inb(TX_PDL), + donecount); +#endif +#ifdef LINUX_2_1 + dev_kfree_skb( lp->txrhead->skb ); +#else + dev_kfree_skb( lp->txrhead->skb, FREE_WRITE ); +#endif + lp->txrhead->skb=(void *)NULL; + lp->txrhead=lp->txrhead->next; + lp->txrcommit--; + } } - + /* tx function for slave modes */ -static int hp100_start_xmit(struct sk_buff *skb, struct device *dev) +static int hp100_start_xmit( struct sk_buff *skb, struct device *dev ) { - int i, ok_flag; - int ioaddr = dev->base_addr; - u_short val; - struct hp100_private *lp = (struct hp100_private *) dev->priv; + int i, ok_flag; + int ioaddr = dev->base_addr; + u_short val; + struct hp100_private *lp = (struct hp100_private *)dev->priv; #ifdef HP100_DEBUG_B - hp100_outw(0x4212, TRACE); - printk("hp100: start_xmit\n"); -#endif - - if (lp->lan_type < 0) { /* no LAN type detected yet? */ - hp100_stop_interface(dev); - if ((lp->lan_type = hp100_sense_lan(dev)) < 0) { - printk("%s: no connection found - check wire\n", dev->name); - hp100_start_interface(dev); /* 10Mb/s RX packets maybe handled */ - return -EIO; - } - if (lp->lan_type == HP100_LAN_100) - lp->hub_status = hp100_login_to_vg_hub(dev, FALSE); /* relogin */ - hp100_start_interface(dev); - } - /* If there is not enough free memory on the card... */ - i = hp100_inl(TX_MEM_FREE) & 0x7fffffff; - if (!(((i / 2) - 539) > (skb->len + 16) && (hp100_inb(TX_PKT_CNT) < 255))) { + hp100_outw( 0x4212, TRACE ); + printk("hp100: %s: start_xmit\n", dev->name); +#endif + + if ( skb==NULL ) + { +#ifndef LINUX_2_1 + dev_tint( dev ); +#endif + return 0; + } + + if ( skb->len <= 0 ) return 0; + + if ( lp->lan_type < 0 ) /* no LAN type detected yet? */ + { + hp100_stop_interface( dev ); + if ( ( lp->lan_type = hp100_sense_lan( dev ) ) < 0 ) + { + printk( "hp100: %s: no connection found - check wire\n", dev->name ); + hp100_start_interface( dev ); /* 10Mb/s RX packets maybe handled */ + return -EIO; + } + if ( lp->lan_type == HP100_LAN_100 ) + lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); /* relogin */ + hp100_start_interface( dev ); + } + + /* If there is not enough free memory on the card... */ + i=hp100_inl(TX_MEM_FREE)&0x7fffffff; + if ( !(((i/2)-539)>(skb->len+16) && (hp100_inb(TX_PKT_CNT)<255)) ) + { #ifdef HP100_DEBUG - printk("hp100_start_xmit: tx free mem = 0x%x\n", i); + printk( "hp100: %s: start_xmit: tx free mem = 0x%x\n", dev->name, i ); #endif - /* not waited long enough since last failed tx try? */ - if (jiffies - dev->trans_start < HZ / 2) { + /* not waited long enough since last failed tx try? */ + if ( jiffies - dev->trans_start < HZ ) + { #ifdef HP100_DEBUG - printk("hp100: trans_start timing problem\n"); + printk("hp100: %s: trans_start timing problem\n", dev->name); #endif - return -EAGAIN; - } - if (lp->lan_type == HP100_LAN_100 && lp->hub_status < 0) - /* we have a 100Mb/s adapter but it isn't connected to hub */ - { - printk("%s: login to 100Mb/s hub retry\n", dev->name); - hp100_stop_interface(dev); - lp->hub_status = hp100_login_to_vg_hub(dev, FALSE); - hp100_start_interface(dev); - } else { - hp100_ints_off(); - i = hp100_sense_lan(dev); - hp100_page(PERFORMANCE); - hp100_ints_on(); - if (i == HP100_LAN_ERR) - printk("%s: link down detected\n", dev->name); - else if (lp->lan_type != i) { /* cable change! */ - /* it's very hard - all network setting must be changed!!! */ - printk("%s: cable change 10Mb/s <-> 100Mb/s detected\n", dev->name); - lp->lan_type = i; - hp100_stop_interface(dev); - if (lp->lan_type == HP100_LAN_100) - lp->hub_status = hp100_login_to_vg_hub(dev, FALSE); - hp100_start_interface(dev); - } else { - printk("%s: interface reset\n", dev->name); - hp100_stop_interface(dev); - hp100_start_interface(dev); - udelay(1000); - } - } - dev->trans_start = jiffies; - return -EAGAIN; + return -EAGAIN; } - for (i = 0; i < 6000 && (hp100_inb(OPTION_MSW) & HP100_TX_CMD); i++) { + if ( lp->lan_type == HP100_LAN_100 && lp->hub_status < 0 ) + /* we have a 100Mb/s adapter but it isn't connected to hub */ + { + printk( "hp100: %s: login to 100Mb/s hub retry\n", dev->name ); + hp100_stop_interface( dev ); + lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); + hp100_start_interface( dev ); + } + else + { + hp100_ints_off(); + i = hp100_sense_lan( dev ); + hp100_ints_on(); + if ( i == HP100_LAN_ERR ) + printk( "hp100: %s: link down detected\n", dev->name ); + else + if ( lp->lan_type != i ) /* cable change! */ + { + /* it's very hard - all network setting must be changed!!! */ + printk( "hp100: %s: cable change 10Mb/s <-> 100Mb/s detected\n", dev->name ); + lp->lan_type = i; + hp100_stop_interface( dev ); + if ( lp->lan_type == HP100_LAN_100 ) + lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); + hp100_start_interface( dev ); + } + else + { + printk( "hp100: %s: interface reset\n", dev->name ); + hp100_stop_interface( dev ); + if ( lp->lan_type == HP100_LAN_100 ) + lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); + hp100_start_interface( dev ); + udelay(1000); + } + } + dev->trans_start = jiffies; + return -EAGAIN; + } + + for ( i=0; i<6000 && ( hp100_inb( OPTION_MSW ) & HP100_TX_CMD ); i++ ) + { #ifdef HP100_DEBUG_TX - printk("hp100_start_xmit: busy\n"); -#endif - } - - hp100_ints_off(); - val = hp100_inw(IRQ_STATUS); - /* Ack / clear the interrupt TX_COMPLETE interrupt - this interrupt is set - * when the current packet being transmitted on the wire is completed. */ - hp100_outw(HP100_TX_COMPLETE, IRQ_STATUS); + printk( "hp100: %s: start_xmit: busy\n", dev->name ); +#endif + } + + hp100_ints_off(); + val = hp100_inw( IRQ_STATUS ); + /* Ack / clear the interrupt TX_COMPLETE interrupt - this interrupt is set + * when the current packet being transmitted on the wire is completed. */ + hp100_outw( HP100_TX_COMPLETE, IRQ_STATUS ); #ifdef HP100_DEBUG_TX - printk("hp100_start_xmit: irq_status=0x%.4x, irqmask=0x%.4x, len=%d\n", val, hp100_inw(IRQ_MASK), (int) skb->len); -#endif - - ok_flag = skb->len >= HP100_MIN_PACKET_SIZE; - i = ok_flag ? skb->len : HP100_MIN_PACKET_SIZE; - - hp100_outw(i, DATA32); /* tell card the total packet length */ - hp100_outw(i, FRAGMENT_LEN); /* and first/only fragment length */ - - if (lp->mode == 2) { /* memory mapped */ - if (lp->mem_ptr_virt) { /* high pci memory was remapped */ - /* Note: The J2585B needs alignment to 32bits here! */ - memcpy(lp->mem_ptr_virt, skb->data, (skb->len + 3) & ~3); - if (!ok_flag) - memset(lp->mem_ptr_virt, 0, HP100_MIN_PACKET_SIZE - skb->len); - } else { - memcpy_toio(lp->mem_ptr_phys, skb->data, skb->len); - if (!ok_flag) - memset_io(lp->mem_ptr_phys, 0, HP100_MIN_PACKET_SIZE - skb->len); - } - } else { /* programmed i/o */ - outsl(ioaddr + HP100_REG_DATA32, skb->data, (skb->len + 3) >> 2); - if (!ok_flag) - for (i = (skb->len + 3) & ~3; i < HP100_MIN_PACKET_SIZE; i += 4) - hp100_outl(0, DATA32); + printk("hp100: %s: start_xmit: irq_status=0x%.4x, irqmask=0x%.4x, len=%d\n",dev->name,val,hp100_inw(IRQ_MASK),(int)skb->len ); +#endif + + ok_flag = skb->len >= HP100_MIN_PACKET_SIZE; + i = ok_flag ? skb->len : HP100_MIN_PACKET_SIZE; + + hp100_outw( i, DATA32 ); /* tell card the total packet length */ + hp100_outw( i, FRAGMENT_LEN ); /* and first/only fragment length */ + + if ( lp->mode==2 ) /* memory mapped */ + { + if ( lp->mem_ptr_virt ) /* high pci memory was remapped */ + { + /* Note: The J2585B needs alignment to 32bits here! */ + memcpy( lp->mem_ptr_virt, skb->data, ( skb->len + 3 ) & ~3 ); + if ( !ok_flag ) + memset( lp->mem_ptr_virt, 0, HP100_MIN_PACKET_SIZE - skb->len ); } - - hp100_outb(HP100_TX_CMD | HP100_SET_LB, OPTION_MSW); /* send packet */ - - lp->stats.tx_packets++; + else + { + /* Note: The J2585B needs alignment to 32bits here! */ + memcpy_toio( lp->mem_ptr_phys, skb->data, (skb->len + 3) & ~3 ); + if ( !ok_flag ) + memset_io( lp->mem_ptr_phys, 0, HP100_MIN_PACKET_SIZE - skb->len ); + } + } + else /* programmed i/o */ + { + outsl( ioaddr + HP100_REG_DATA32, skb->data, ( skb->len + 3 ) >> 2 ); + if ( !ok_flag ) + for ( i = ( skb->len + 3 ) & ~3; i < HP100_MIN_PACKET_SIZE; i += 4 ) + hp100_outl( 0, DATA32 ); + } + + hp100_outb( HP100_TX_CMD | HP100_SET_LB, OPTION_MSW ); /* send packet */ + + lp->stats.tx_packets++; #ifdef LINUX_2_1 - lp->stats.tx_bytes += skb->len; + lp->stats.tx_bytes += skb->len; #endif - dev->trans_start = jiffies; - hp100_ints_on(); - - dev_kfree_skb(skb); - + dev->trans_start=jiffies; + hp100_ints_on(); + +#ifdef LINUX_2_1 + dev_kfree_skb( skb ); +#else + dev_kfree_skb( skb, FREE_WRITE ); +#endif + #ifdef HP100_DEBUG_TX - printk("hp100_start_xmit: end\n"); + printk( "hp100: %s: start_xmit: end\n", dev->name ); #endif - - return 0; + + return 0; } - + /* * Receive Function (Non-Busmaster mode) * Called when an "Receive Packet" interrupt occurs, i.e. the receive @@ -1618,981 +1885,1163 @@ static int hp100_start_xmit(struct sk_buff *skb, struct device *dev) * and netif_rx. */ -static void hp100_rx(struct device *dev) +static void hp100_rx( struct device *dev ) { - int packets, pkt_len; - int ioaddr = dev->base_addr; - struct hp100_private *lp = (struct hp100_private *) dev->priv; - u_int header; - struct sk_buff *skb; + int packets, pkt_len; + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *)dev->priv; + u_int header; + struct sk_buff *skb; #ifdef DEBUG_B - hp100_outw(0x4213, TRACE); - printk("hp100: rx\n"); + hp100_outw( 0x4213, TRACE ); + printk("hp100: %s: rx\n", dev->name); #endif - /* First get indication of received lan packet */ - /* RX_PKT_CND indicates the number of packets which have been fully */ - /* received onto the card but have not been fully transfered of the card */ - packets = hp100_inb(RX_PKT_CNT); + /* First get indication of received lan packet */ + /* RX_PKT_CND indicates the number of packets which have been fully */ + /* received onto the card but have not been fully transfered of the card */ + packets = hp100_inb( RX_PKT_CNT ); #ifdef HP100_DEBUG_RX - if (packets > 1) - printk("hp100_rx: waiting packets = %d\n", packets); + if ( packets > 1 ) + printk( "hp100: %s: rx: waiting packets = %d\n", dev->name,packets ); #endif - while (packets-- > 0) { - /* If ADV_NXT_PKT is still set, we have to wait until the card has */ - /* really advanced to the next packet. */ - for (pkt_len = 0; pkt_len < 6000 && (hp100_inb(OPTION_MSW) & HP100_ADV_NXT_PKT); - pkt_len++) { + while ( packets-- > 0 ) + { + /* If ADV_NXT_PKT is still set, we have to wait until the card has */ + /* really advanced to the next packet. */ + for (pkt_len=0; pkt_len<6000 &&(hp100_inb(OPTION_MSW)&HP100_ADV_NXT_PKT); + pkt_len++ ) + { #ifdef HP100_DEBUG_RX - printk("hp100_rx: busy, remaining packets = %d\n", packets); -#endif - } - - /* First we get the header, which contains information about the */ - /* actual length of the received packet. */ - if (lp->mode == 2) { /* memory mapped mode */ - if (lp->mem_ptr_virt) /* if memory was remapped */ - header = *(__u32 *) lp->mem_ptr_virt; - else - header = readl(lp->mem_ptr_phys); - } else /* programmed i/o */ - header = hp100_inl(DATA32); - - pkt_len = header & HP100_PKT_LEN_MASK; + printk( "hp100: %s: rx: busy, remaining packets = %d\n", dev->name, packets ); +#endif + } + + /* First we get the header, which contains information about the */ + /* actual length of the received packet. */ + if( lp->mode==2 ) /* memory mapped mode */ + { + if ( lp->mem_ptr_virt ) /* if memory was remapped */ + header = *(__u32 *)lp->mem_ptr_virt; + else + header = readl( lp->mem_ptr_phys ); + } + else /* programmed i/o */ + header = hp100_inl( DATA32 ); + + pkt_len = ((header & HP100_PKT_LEN_MASK) + 3) & ~3; #ifdef HP100_DEBUG_RX - printk("hp100_rx: new packet - length=%d, errors=0x%x, dest=0x%x\n", - header & HP100_PKT_LEN_MASK, (header >> 16) & 0xfff8, - (header >> 16) & 7); -#endif - - /* Now we allocate the skb and transfer the data into it. */ - /* NOTE! This (and the skb_put() below) depends on the skb-functions - * allocating more than asked (notably, aligning the request up to - * the next 16-byte length). - */ - skb = dev_alloc_skb(pkt_len); - if (skb == NULL) { /* Not enough memory->drop packet */ + printk( "hp100: %s: rx: new packet - length=%d, errors=0x%x, dest=0x%x\n", + dev->name, + header & HP100_PKT_LEN_MASK, (header>>16)&0xfff8, + (header>>16)&7); +#endif + + /* Now we allocate the skb and transfer the data into it. */ + skb = dev_alloc_skb( pkt_len ); + if ( skb == NULL ) /* Not enough memory->drop packet */ + { #ifdef HP100_DEBUG - printk("hp100_rx: couldn't allocate a sk_buff of size %d\n", pkt_len); + printk( "hp100: %s: rx: couldn't allocate a sk_buff of size %d\n", dev->name, pkt_len ); #endif - lp->stats.rx_dropped++; - } else { /* skb successfully allocated */ - u_char *ptr; - - skb->dev = dev; - - /* ptr to start of the sk_buff data area */ - ptr = (u_char *) skb_put(skb, pkt_len); - - /* Now transfer the data from the card into that area */ - if (lp->mode == 2) { - if (lp->mem_ptr_virt) - memcpy(ptr, lp->mem_ptr_virt, (pkt_len + 3) & ~3); - /* Note alignment to 32bit transfers */ - else - memcpy_fromio(ptr, lp->mem_ptr_phys, (pkt_len + 3) & ~3); - } else /* io mapped */ - insl(ioaddr + HP100_REG_DATA32, ptr, (pkt_len + 3) >> 2); - - skb->protocol = eth_type_trans(skb, dev); - - netif_rx(skb); - lp->stats.rx_packets++; + lp->stats.rx_dropped++; + } + else /* skb successfully allocated */ + { + u_char *ptr; + + skb->dev = dev; + + /* ptr to start of the sk_buff data area */ + ptr = (u_char *)skb_put( skb, pkt_len ); + + /* Now transfer the data from the card into that area */ + if ( lp->mode==2 ) + { + if ( lp->mem_ptr_virt ) + memcpy( ptr, lp->mem_ptr_virt, pkt_len ); + /* Note alignment to 32bit transfers */ + else + memcpy_fromio( ptr, lp->mem_ptr_phys, pkt_len ); + } + else /* io mapped */ + insl( ioaddr + HP100_REG_DATA32, ptr, pkt_len >> 2 ); + + skb->protocol = eth_type_trans( skb, dev ); + + netif_rx( skb ); + lp->stats.rx_packets++; #ifdef LINUX_2_1 - lp->stats.rx_bytes += skb->len; + lp->stats.rx_bytes += skb->len; #endif - + #ifdef HP100_DEBUG_RX - printk("rx: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", - ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5], - ptr[6], ptr[7], ptr[8], ptr[9], ptr[10], ptr[11]); + printk( "hp100: %s: rx: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + dev->name, + ptr[ 0 ], ptr[ 1 ], ptr[ 2 ], ptr[ 3 ], ptr[ 4 ], ptr[ 5 ], + ptr[ 6 ], ptr[ 7 ], ptr[ 8 ], ptr[ 9 ], ptr[ 10 ], ptr[ 11 ] ); #endif - } - - /* Indicate the card that we have got the packet */ - hp100_outb(HP100_ADV_NXT_PKT | HP100_SET_LB, OPTION_MSW); - - switch (header & 0x00070000) { - case (HP100_MULTI_ADDR_HASH << 16): - case (HP100_MULTI_ADDR_NO_HASH << 16): - lp->stats.multicast++; - break; - } - } /* end of while(there are packets) loop */ + } + + /* Indicate the card that we have got the packet */ + hp100_outb( HP100_ADV_NXT_PKT | HP100_SET_LB, OPTION_MSW ); + + switch ( header & 0x00070000 ) { + case (HP100_MULTI_ADDR_HASH<<16): + case (HP100_MULTI_ADDR_NO_HASH<<16): + lp->stats.multicast++; break; + } + } /* end of while(there are packets) loop */ #ifdef HP100_DEBUG_RX - printk("hp100_rx: end\n"); + printk( "hp100_rx: %s: end\n", dev->name ); #endif } - + /* * Receive Function for Busmaster Mode */ -static void hp100_rx_bm(struct device *dev) +static void hp100_rx_bm( struct device *dev ) { - int ioaddr = dev->base_addr; - struct hp100_private *lp = (struct hp100_private *) dev->priv; - hp100_ring_t *ptr; - u_int header; - int pkt_len; + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *)dev->priv; + hp100_ring_t *ptr; + u_int header; + int pkt_len; #ifdef HP100_DEBUG_B - hp100_outw(0x4214, TRACE); - printk("hp100: rx_bm\n"); + hp100_outw( 0x4214, TRACE ); + printk("hp100: %s: rx_bm\n", dev->name); #endif #ifdef HP100_DEBUG - if (0 == lp->rxrcommit) { - printk("hp100: rx_bm called although no PDLs were committed to adapter?\n"); - return; - } else - /* RX_PKT_CNT states how many PDLs are currently formatted and available to - * the cards BM engine */ - if ((hp100_inw(RX_PKT_CNT) & 0x00ff) >= lp->rxrcommit) { - printk("hp100: More packets received than commited? RX_PKT_CNT=0x%x, commit=0x%x\n", hp100_inw(RX_PKT_CNT) & 0x00ff, lp->rxrcommit); - return; - } -#endif - - while ((lp->rxrcommit > hp100_inb(RX_PDL))) { - /* - * The packet was received into the pdl pointed to by lp->rxrhead ( - * the oldest pdl in the ring - */ - - /* First we get the header, which contains information about the */ - /* actual length of the received packet. */ - - ptr = lp->rxrhead; - - header = *(ptr->pdl - 1); - pkt_len = (header & HP100_PKT_LEN_MASK); + if(0==lp->rxrcommit) + { + printk("hp100: %s: rx_bm called although no PDLs were committed to adapter?\n", dev->name); + return; + } + else + + /* RX_PKT_CNT states how many PDLs are currently formatted and available to + * the cards BM engine */ + if( (hp100_inw(RX_PKT_CNT)&0x00ff) >= lp->rxrcommit) + { + printk("hp100: %s: More packets received than commited? RX_PKT_CNT=0x%x, commit=0x%x\n", dev->name, hp100_inw(RX_PKT_CNT)&0x00ff, lp->rxrcommit); + return; + } +#endif + + while( (lp->rxrcommit > hp100_inb(RX_PDL)) ) + { + /* + * The packet was received into the pdl pointed to by lp->rxrhead ( + * the oldest pdl in the ring + */ + + /* First we get the header, which contains information about the */ + /* actual length of the received packet. */ + + ptr=lp->rxrhead; + + header = *(ptr->pdl-1); + pkt_len = (header & HP100_PKT_LEN_MASK); #ifdef HP100_DEBUG_BM - printk("hp100: rx_bm: header@0x%x=0x%x length=%d, errors=0x%x, dest=0x%x\n", - (u_int) (ptr->pdl - 1), (u_int) header, - pkt_len, - (header >> 16) & 0xfff8, - (header >> 16) & 7); - printk("hp100: RX_PDL_COUNT:0x%x TX_PDL_COUNT:0x%x, RX_PKT_CNT=0x%x PDH=0x%x, Data@0x%x len=0x%x\n", - hp100_inb(RX_PDL), - hp100_inb(TX_PDL), - hp100_inb(RX_PKT_CNT), - (u_int) * (ptr->pdl), - (u_int) * (ptr->pdl + 3), - (u_int) * (ptr->pdl + 4)); -#endif - - if ((pkt_len >= MIN_ETHER_SIZE) && - (pkt_len <= MAX_ETHER_SIZE)) { - if (ptr->skb == NULL) { - printk("hp100: rx_bm: skb null\n"); - /* can happen if we only allocated room for the pdh due to memory shortage. */ - lp->stats.rx_dropped++; - } else { - skb_trim(ptr->skb, pkt_len); /* Shorten it */ - ptr->skb->protocol = eth_type_trans(ptr->skb, dev); - - netif_rx(ptr->skb); /* Up and away... */ - - lp->stats.rx_packets++; + printk( "hp100: %s: rx_bm: header@0x%x=0x%x length=%d, errors=0x%x, dest=0x%x\n", + dev->name, + (u_int) (ptr->pdl-1),(u_int) header, + pkt_len, + (header>>16)&0xfff8, + (header>>16)&7); + printk( "hp100: %s: RX_PDL_COUNT:0x%x TX_PDL_COUNT:0x%x, RX_PKT_CNT=0x%x PDH=0x%x, Data@0x%x len=0x%x\n", + dev->name, + hp100_inb( RX_PDL ), + hp100_inb( TX_PDL ), + hp100_inb( RX_PKT_CNT ), + (u_int) *(ptr->pdl), + (u_int) *(ptr->pdl+3), + (u_int) *(ptr->pdl+4)); +#endif + + if( (pkt_len>=MIN_ETHER_SIZE) && + (pkt_len<=MAX_ETHER_SIZE) ) + { + if(ptr->skb==NULL) + { + printk("hp100: %s: rx_bm: skb null\n", dev->name); + /* can happen if we only allocated room for the pdh due to memory shortage. */ + lp->stats.rx_dropped++; + } + else + { + skb_trim( ptr->skb, pkt_len ); /* Shorten it */ + ptr->skb->protocol = eth_type_trans( ptr->skb, dev ); + + netif_rx( ptr->skb ); /* Up and away... */ + + lp->stats.rx_packets++; #ifdef LINUX_2_1 - lp->stats.rx_bytes += ptr->skb->len; -#endif - } - - switch (header & 0x00070000) { - case (HP100_MULTI_ADDR_HASH << 16): - case (HP100_MULTI_ADDR_NO_HASH << 16): - lp->stats.multicast++; - break; - } - } else { + lp->stats.rx_bytes += ptr->skb->len; +#endif + } + + switch ( header & 0x00070000 ) { + case (HP100_MULTI_ADDR_HASH<<16): + case (HP100_MULTI_ADDR_NO_HASH<<16): + lp->stats.multicast++; break; + } + } + else + { #ifdef HP100_DEBUG - printk("hp100: rx_bm: Received bad packet (length=%d)\n", pkt_len); + printk("hp100: %s: rx_bm: Received bad packet (length=%d)\n",dev->name,pkt_len); #endif - if (ptr->skb != NULL) - dev_kfree_skb(ptr->skb); - lp->stats.rx_errors++; - } - - lp->rxrhead = lp->rxrhead->next; - - /* Allocate a new rx PDL (so lp->rxrcommit stays the same) */ - if (0 == hp100_build_rx_pdl(lp->rxrtail, dev)) { - /* No space for skb, header can still be received. */ -#ifdef HP100_DEBUG - printk("hp100: rx_bm: No space for new PDL.\n"); + if(ptr->skb!=NULL) +#ifdef LINUX_2_1 + dev_kfree_skb( ptr->skb ); +#else + dev_kfree_skb( ptr->skb, FREE_READ ); #endif - return; - } else { /* successfully allocated new PDL - put it in ringlist at tail. */ - hp100_outl((u32) lp->rxrtail->pdl_paddr, RX_PDA); - lp->rxrtail = lp->rxrtail->next; - } + lp->stats.rx_errors++; + } + + lp->rxrhead=lp->rxrhead->next; + /* Allocate a new rx PDL (so lp->rxrcommit stays the same) */ + if (0 == hp100_build_rx_pdl( lp->rxrtail, dev )) + { + /* No space for skb, header can still be received. */ +#ifdef HP100_DEBUG + printk("hp100: %s: rx_bm: No space for new PDL.\n", dev->name); +#endif + return; + } + else + { /* successfully allocated new PDL - put it in ringlist at tail. */ + hp100_outl((u32)lp->rxrtail->pdl_paddr, RX_PDA); + lp->rxrtail=lp->rxrtail->next; } + + } } - + /* * statistics */ -static hp100_stats_t *hp100_get_stats(struct device *dev) +static hp100_stats_t *hp100_get_stats( struct device *dev ) { - int ioaddr = dev->base_addr; + int ioaddr = dev->base_addr; #ifdef HP100_DEBUG_B - hp100_outw(0x4215, TRACE); + hp100_outw( 0x4215, TRACE ); #endif - hp100_ints_off(); - hp100_update_stats(dev); - hp100_ints_on(); - return &((struct hp100_private *) dev->priv)->stats; + hp100_ints_off(); + hp100_update_stats( dev ); + hp100_ints_on(); + return &((struct hp100_private *)dev->priv)->stats; } -static void hp100_update_stats(struct device *dev) +static void hp100_update_stats( struct device *dev ) { - int ioaddr = dev->base_addr; - u_short val; - struct hp100_private *lp = (struct hp100_private *) dev->priv; + int ioaddr = dev->base_addr; + u_short val; + struct hp100_private *lp = (struct hp100_private *)dev->priv; #ifdef HP100_DEBUG_B - hp100_outw(0x4216, TRACE); - printk("hp100: update-stats\n"); -#endif - - /* Note: Statistics counters clear when read. */ - hp100_page(MAC_CTRL); - val = hp100_inw(DROPPED) & 0x0fff; - lp->stats.rx_errors += val; - lp->stats.rx_over_errors += val; - val = hp100_inb(CRC); - lp->stats.rx_errors += val; - lp->stats.rx_crc_errors += val; - val = hp100_inb(ABORT); - lp->stats.tx_errors += val; - lp->stats.tx_aborted_errors += val; - hp100_page(PERFORMANCE); + hp100_outw( 0x4216, TRACE ); + printk("hp100: %s: update-stats\n", dev->name); +#endif + + /* Note: Statistics counters clear when read. */ + hp100_page( MAC_CTRL ); + val = hp100_inw( DROPPED ) & 0x0fff; + lp->stats.rx_errors += val; + lp->stats.rx_over_errors += val; + val = hp100_inb( CRC ); + lp->stats.rx_errors += val; + lp->stats.rx_crc_errors += val; + val = hp100_inb( ABORT ); + lp->stats.tx_errors += val; + lp->stats.tx_aborted_errors += val; + hp100_page( PERFORMANCE ); } -static void hp100_clear_stats(int ioaddr) +static void hp100_misc_interrupt( struct device *dev ) { + struct hp100_private *lp = (struct hp100_private *)dev->priv; + #ifdef HP100_DEBUG_B - hp100_outw(0x4217, TRACE); - printk("hp100: clear_stats\n"); + hp100_outw( 0x4216, TRACE ); + printk("hp100: %s: misc_interrupt\n", dev->name); #endif - cli(); - hp100_page(MAC_CTRL); /* get all statistics bytes */ - hp100_inw(DROPPED); - hp100_inb(CRC); - hp100_inb(ABORT); - hp100_page(PERFORMANCE); - sti(); + /* Note: Statistics counters clear when read. */ + lp->stats.rx_errors++; + lp->stats.tx_errors++; +} + +static void hp100_clear_stats( int ioaddr ) +{ + unsigned long flags; + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4217, TRACE ); + printk("hp100: %s: clear_stats\n", dev->name); +#endif + + save_flags( flags ); + cli(); + hp100_page( MAC_CTRL ); /* get all statistics bytes */ + hp100_inw( DROPPED ); + hp100_inb( CRC ); + hp100_inb( ABORT ); + hp100_page( PERFORMANCE ); + restore_flags( flags ); } - + /* * multicast setup */ /* * Set or clear the multicast filter for this adapter. - * TODO: Currently when in multicast mode, card accepts all multicast packets - * for all MC addresses. Should better use the list on the card. */ - -static void hp100_set_multicast_list(struct device *dev) + +static void hp100_set_multicast_list( struct device *dev ) { - int ioaddr = dev->base_addr; - struct hp100_private *lp = (struct hp100_private *) dev->priv; + unsigned long flags; + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *)dev->priv; #ifdef HP100_DEBUG_B - hp100_outw(0x4218, TRACE); - printk("hp100: set_mc_list\n"); -#endif - - cli(); - hp100_ints_off(); - hp100_page(MAC_CTRL); - hp100_andb(~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1); /* stop rx/tx */ - - if (dev->flags & IFF_PROMISC) { - lp->mac2_mode = HP100_MAC2MODE6; /* promiscuous mode = get all good */ - lp->mac1_mode = HP100_MAC1MODE6; /* packets on the net */ - } else if (dev->mc_count || (dev->flags & IFF_ALLMULTI)) { - lp->mac2_mode = HP100_MAC2MODE5; /* multicast mode = get packets for */ - lp->mac1_mode = HP100_MAC1MODE5; /* me, broadcasts and all multicasts */ - } else { - lp->mac2_mode = HP100_MAC2MODE3; /* normal mode = get packets for me */ - lp->mac1_mode = HP100_MAC1MODE3; /* and broadcasts */ - } - - if (((hp100_inb(MAC_CFG_1) & 0x0f) != lp->mac1_mode) || - (hp100_inb(MAC_CFG_2) != lp->mac2_mode)) { - hp100_outb(lp->mac2_mode, MAC_CFG_2); - hp100_andb(HP100_MAC1MODEMASK, MAC_CFG_1); /* clear mac1 mode bits */ - hp100_orb(lp->mac1_mode, MAC_CFG_1); /* and set the new mode */ - - if (lp->lan_type == HP100_LAN_100) { + hp100_outw( 0x4218, TRACE ); + printk("hp100: %s: set_mc_list\n", dev->name); +#endif + + save_flags( flags ); + cli(); + hp100_ints_off(); + hp100_page( MAC_CTRL ); + hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 ); /* stop rx/tx */ + + if ( dev->flags & IFF_PROMISC ) + { + lp->mac2_mode = HP100_MAC2MODE6; /* promiscuous mode = get all good */ + lp->mac1_mode = HP100_MAC1MODE6; /* packets on the net */ + memset( &lp->hash_bytes, 0xff, 8 ); + } + else if ( dev->mc_count || (dev->flags&IFF_ALLMULTI) ) + { + lp->mac2_mode = HP100_MAC2MODE5; /* multicast mode = get packets for */ + lp->mac1_mode = HP100_MAC1MODE5; /* me, broadcasts and all multicasts */ +#ifdef HP100_MULTICAST_FILTER /* doesn't work!!! */ + if ( dev -> flags & IFF_ALLMULTI ) + { + /* set hash filter to receive all multicast packets */ + memset( &lp->hash_bytes, 0xff, 8 ); + } + else + { + int i, j, idx; + u_char *addrs; + struct dev_mc_list *dmi; + + memset( &lp->hash_bytes, 0x00, 8 ); +#ifdef HP100_DEBUG + printk("hp100: %s: computing hash filter - mc_count = %i\n", dev -> name, dev -> mc_count ); +#endif + for ( i = 0, dmi = dev -> mc_list; i < dev -> mc_count; i++, dmi = dmi -> next ) + { + addrs = dmi -> dmi_addr; + if ( ( *addrs & 0x01 ) == 0x01 ) /* multicast address? */ + { +#ifdef HP100_DEBUG + printk("hp100: %s: multicast = %02x:%02x:%02x:%02x:%02x:%02x, ", + dev -> name, + addrs[ 0 ], addrs[ 1 ], addrs[ 2 ], + addrs[ 3 ], addrs[ 4 ], addrs[ 5 ] ); +#endif + for ( j = idx = 0; j < 6; j++ ) + { + idx ^= *addrs++ & 0x3f; + printk( ":%02x:", idx ); + } #ifdef HP100_DEBUG - printk("hp100: 100VG MAC settings have changed - relogin.\n"); + printk("idx = %i\n", idx ); #endif - lp->hub_status = hp100_login_to_vg_hub(dev, TRUE); /* force a relogin to the hub */ - } - } - hp100_page(MAC_CTRL); - hp100_orb(HP100_RX_EN | HP100_RX_IDLE | /* enable rx */ - HP100_TX_EN | HP100_TX_IDLE, MAC_CFG_1); /* enable tx */ - - hp100_page(PERFORMANCE); - hp100_ints_on(); - sti(); + lp->hash_bytes[ idx >> 3 ] |= ( 1 << ( idx & 7 ) ); + } + } + } +#else + memset( &lp->hash_bytes, 0xff, 8 ); +#endif + } + else + { + lp->mac2_mode = HP100_MAC2MODE3; /* normal mode = get packets for me */ + lp->mac1_mode = HP100_MAC1MODE3; /* and broadcasts */ + memset( &lp->hash_bytes, 0x00, 8 ); + } + + if ( ( (hp100_inb(MAC_CFG_1) & 0x0f)!=lp->mac1_mode ) || + ( hp100_inb(MAC_CFG_2)!=lp->mac2_mode ) ) + { + int i; + + hp100_outb( lp->mac2_mode, MAC_CFG_2 ); + hp100_andb( HP100_MAC1MODEMASK, MAC_CFG_1 ); /* clear mac1 mode bits */ + hp100_orb( lp->mac1_mode, MAC_CFG_1 ); /* and set the new mode */ + + hp100_page( MAC_ADDRESS ); + for ( i = 0; i < 8; i++ ) + hp100_outb( lp->hash_bytes[ i ], HASH_BYTE0 + i ); +#ifdef HP100_DEBUG + printk("hp100: %s: mac1 = 0x%x, mac2 = 0x%x, multicast hash = %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", + dev->name, lp->mac1_mode, lp->mac2_mode, + lp->hash_bytes[ 0 ], lp->hash_bytes[ 1 ], + lp->hash_bytes[ 2 ], lp->hash_bytes[ 3 ], + lp->hash_bytes[ 4 ], lp->hash_bytes[ 5 ], + lp->hash_bytes[ 6 ], lp->hash_bytes[ 7 ] + ); +#endif + + if(lp->lan_type==HP100_LAN_100) + { +#ifdef HP100_DEBUG + printk("hp100: %s: 100VG MAC settings have changed - relogin.\n", dev->name); +#endif + lp->hub_status=hp100_login_to_vg_hub( dev, TRUE ); /* force a relogin to the hub */ + } + } + else + { + int i; + u_char old_hash_bytes[ 8 ]; + + hp100_page( MAC_ADDRESS ); + for ( i = 0; i < 8; i++ ) + old_hash_bytes[ i ] = hp100_inb( HASH_BYTE0 + i ); + if ( memcmp( old_hash_bytes, &lp->hash_bytes, 8 ) ) + { + for ( i = 0; i < 8; i++ ) + hp100_outb( lp->hash_bytes[ i ], HASH_BYTE0 + i ); +#ifdef HP100_DEBUG + printk("hp100: %s: multicast hash = %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", + dev->name, + lp->hash_bytes[ 0 ], lp->hash_bytes[ 1 ], + lp->hash_bytes[ 2 ], lp->hash_bytes[ 3 ], + lp->hash_bytes[ 4 ], lp->hash_bytes[ 5 ], + lp->hash_bytes[ 6 ], lp->hash_bytes[ 7 ] + ); +#endif + + if(lp->lan_type==HP100_LAN_100) + { +#ifdef HP100_DEBUG + printk("hp100: %s: 100VG MAC settings have changed - relogin.\n", dev->name); +#endif + lp->hub_status=hp100_login_to_vg_hub( dev, TRUE ); /* force a relogin to the hub */ + } + } + } + + hp100_page( MAC_CTRL ); + hp100_orb( HP100_RX_EN | HP100_RX_IDLE | /* enable rx */ + HP100_TX_EN | HP100_TX_IDLE, MAC_CFG_1 ); /* enable tx */ + + hp100_page( PERFORMANCE ); + hp100_ints_on(); + restore_flags( flags ); } - + /* * hardware interrupt handling */ -static void hp100_interrupt(int irq, void *dev_id, struct pt_regs *regs) +static void hp100_interrupt( int irq, void *dev_id, struct pt_regs *regs ) { - struct device *dev = dev_id; - struct hp100_private *lp = (struct hp100_private *) dev->priv; + struct device *dev = (struct device *)dev_id; + struct hp100_private *lp = (struct hp100_private *)dev->priv; - int ioaddr; - u_int val; + int ioaddr; + u_int val; - if (dev == NULL) - return; - ioaddr = dev->base_addr; + if ( dev == NULL ) return; + ioaddr = dev->base_addr; - if (dev->interrupt) - printk("%s: re-entering the interrupt handler\n", dev->name); - hp100_ints_off(); - dev->interrupt = 1; /* mark that we are inside the handler */ + if ( dev->interrupt ) + printk( "hp100: %s: re-entering the interrupt handler\n", dev->name ); + hp100_ints_off(); + dev->interrupt = 1; /* mark that we are inside the handler */ #ifdef HP100_DEBUG_B - hp100_outw(0x4219, TRACE); + hp100_outw( 0x4219, TRACE ); #endif - /* hp100_page( PERFORMANCE ); */ - val = hp100_inw(IRQ_STATUS); + /* hp100_page( PERFORMANCE ); */ + val = hp100_inw( IRQ_STATUS ); #ifdef HP100_DEBUG_IRQ - printk("hp100: mode=%x,IRQ_STAT=0x%.4x,RXPKTCNT=0x%.2x RXPDL=0x%.2x TXPKTCNT=0x%.2x TXPDL=0x%.2x\n", - lp->mode, - (u_int) val, - hp100_inb(RX_PKT_CNT), - hp100_inb(RX_PDL), - hp100_inb(TX_PKT_CNT), - hp100_inb(TX_PDL) - ); -#endif - - if (val == 0) { /* might be a shared interrupt */ - dev->interrupt = 0; - hp100_ints_on(); - return; + printk( "hp100: %s: mode=%x,IRQ_STAT=0x%.4x,RXPKTCNT=0x%.2x RXPDL=0x%.2x TXPKTCNT=0x%.2x TXPDL=0x%.2x\n", + dev->name, + lp->mode, + (u_int)val, + hp100_inb( RX_PKT_CNT ), + hp100_inb( RX_PDL ), + hp100_inb( TX_PKT_CNT ), + hp100_inb( TX_PDL ) + ); +#endif + + if(val==0) /* might be a shared interrupt */ + { + dev->interrupt=0; + hp100_ints_on(); + return; + } + /* We're only interested in those interrupts we really enabled. */ + /* val &= hp100_inw( IRQ_MASK ); */ + + /* + * RX_PDL_FILL_COMPL is set whenever a RX_PDL has been executed. A RX_PDL + * is considered executed whenever the RX_PDL data structure is no longer + * needed. + */ + if ( val & HP100_RX_PDL_FILL_COMPL ) + { + if(lp->mode==1) + hp100_rx_bm( dev ); + else + { + printk("hp100: %s: rx_pdl_fill_compl interrupt although not busmaster?\n", dev->name); } - /* We're only interested in those interrupts we really enabled. */ - /* val &= hp100_inw( IRQ_MASK ); */ - - /* - * RX_PDL_FILL_COMPL is set whenever a RX_PDL has been executed. A RX_PDL - * is considered executed whenever the RX_PDL data structure is no longer - * needed. - */ - if (val & HP100_RX_PDL_FILL_COMPL) { - if (lp->mode == 1) - hp100_rx_bm(dev); - else - printk("hp100: rx_pdl_fill_compl interrupt although not busmaster?\n"); + } + + /* + * The RX_PACKET interrupt is set, when the receive packet counter is + * non zero. We use this interrupt for receiving in slave mode. In + * busmaster mode, we use it to make sure we did not miss any rx_pdl_fill + * interrupts. If rx_pdl_fill_compl is not set and rx_packet is set, then + * we somehow have missed a rx_pdl_fill_compl interrupt. + */ + + if ( val & HP100_RX_PACKET ) /* Receive Packet Counter is non zero */ + { + if(lp->mode!=1) /* non busmaster */ + hp100_rx( dev ); + else if ( !(val & HP100_RX_PDL_FILL_COMPL )) + { + /* Shouldnt happen - maybe we missed a RX_PDL_FILL Interrupt? */ + hp100_rx_bm( dev ); } - /* - * The RX_PACKET interrupt is set, when the receive packet counter is - * non zero. We use this interrupt for receiving in slave mode. In - * busmaster mode, we use it to make sure we did not miss any rx_pdl_fill - * interrupts. If rx_pdl_fill_compl is not set and rx_packet is set, then - * we somehow have missed a rx_pdl_fill_compl interrupt. - */ - - if (val & HP100_RX_PACKET) { /* Receive Packet Counter is non zero */ - if (lp->mode != 1) /* non busmaster */ - hp100_rx(dev); - else if (!(val & HP100_RX_PDL_FILL_COMPL)) { - /* Shouldnt happen - maybe we missed a RX_PDL_FILL Interrupt? */ - hp100_rx_bm(dev); - } - } - /* - * Ack. that we have noticed the interrupt and thereby allow next one. - * Note that this is now done after the slave rx function, since first - * acknowledging and then setting ADV_NXT_PKT caused an extra interrupt - * on the J2573. - */ - hp100_outw(val, IRQ_STATUS); - - /* - * RX_ERROR is set when a packet is dropped due to no memory resources on - * the card or when a RCV_ERR occurs. - * TX_ERROR is set when a TX_ABORT condition occurs in the MAC->exists - * only in the 802.3 MAC and happens when 16 collisions occur during a TX - */ - if (val & (HP100_TX_ERROR | HP100_RX_ERROR)) { + } + + /* + * Ack. that we have noticed the interrupt and thereby allow next one. + * Note that this is now done after the slave rx function, since first + * acknowledging and then setting ADV_NXT_PKT caused an extra interrupt + * on the J2573. + */ + hp100_outw( val, IRQ_STATUS ); + + /* + * RX_ERROR is set when a packet is dropped due to no memory resources on + * the card or when a RCV_ERR occurs. + * TX_ERROR is set when a TX_ABORT condition occurs in the MAC->exists + * only in the 802.3 MAC and happens when 16 collisions occur during a TX + */ + if ( val & ( HP100_TX_ERROR | HP100_RX_ERROR ) ) + { #ifdef HP100_DEBUG_IRQ - printk("hp100: TX/RX Error IRQ\n"); + printk("hp100: %s: TX/RX Error IRQ\n", dev->name); #endif - hp100_update_stats(dev); - if (lp->mode == 1) { - hp100_rxfill(dev); - hp100_clean_txring(dev); - } + hp100_update_stats( dev ); + if(lp->mode==1) + { + hp100_rxfill( dev ); + hp100_clean_txring( dev ); } - /* - * RX_PDA_ZERO is set when the PDA count goes from non-zero to zero. - */ - if ((lp->mode == 1) && (val & (HP100_RX_PDA_ZERO))) - hp100_rxfill(dev); - - /* - * HP100_TX_COMPLETE interrupt occurs when packet transmitted on wire - * is completed - */ - if ((lp->mode == 1) && (val & (HP100_TX_COMPLETE))) - hp100_clean_txring(dev); - - /* - * MISC_ERROR is set when either the LAN link goes down or a detected - * bus error occurs. - */ - if (val & HP100_MISC_ERROR) { /* New for J2585B */ - printk("hp100: Misc. Error Interrupt - Check cabling.\n"); - if (lp->mode == 1) { - hp100_clean_txring(dev); - hp100_rxfill(dev); - } + } + + /* + * RX_PDA_ZERO is set when the PDA count goes from non-zero to zero. + */ + if ( (lp->mode==1)&&(val &(HP100_RX_PDA_ZERO)) ) + hp100_rxfill( dev ); + + /* + * HP100_TX_COMPLETE interrupt occurs when packet transmitted on wire + * is completed + */ + if ( (lp->mode==1) && ( val & ( HP100_TX_COMPLETE )) ) + hp100_clean_txring( dev ); + + /* + * MISC_ERROR is set when either the LAN link goes down or a detected + * bus error occurs. + */ + if ( val & HP100_MISC_ERROR ) /* New for J2585B */ + { +#ifdef HP100_DEBUG_IRQ + printk("hp100: %s: Misc. Error Interrupt - Check cabling.\n", dev->name); +#endif + if(lp->mode==1) + { + hp100_clean_txring( dev ); + hp100_rxfill( dev ); } - dev->interrupt = 0; - hp100_ints_on(); + hp100_misc_interrupt( dev ); + } + + dev->interrupt = 0; + hp100_ints_on(); } - + /* * some misc functions */ -static void hp100_start_interface(struct device *dev) +static void hp100_start_interface( struct device *dev ) { - int ioaddr = dev->base_addr; - struct hp100_private *lp = (struct hp100_private *) dev->priv; + unsigned long flags; + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *)dev->priv; #ifdef HP100_DEBUG_B - hp100_outw(0x4220, TRACE); - printk("hp100: hp100_start_interface %s\n", dev->name); -#endif - - cli(); - - /* Ensure the adapter does not want to request an interrupt when */ - /* enabling the IRQ line to be active on the bus (i.e. not tri-stated) */ - hp100_page(PERFORMANCE); - hp100_outw(0xfefe, IRQ_MASK); /* mask off all ints */ - hp100_outw(0xffff, IRQ_STATUS); /* ack all IRQs */ - hp100_outw(HP100_FAKE_INT | HP100_INT_EN | HP100_RESET_LB, OPTION_LSW); - /* Un Tri-state int. TODO: Check if shared interrupts can be realised? */ - hp100_outw(HP100_TRI_INT | HP100_RESET_HB, OPTION_LSW); - - if (lp->mode == 1) { - /* Make sure BM bit is set... */ - hp100_page(HW_MAP); - hp100_orb(HP100_BM_MASTER, BM); - hp100_rxfill(dev); - } else if (lp->mode == 2) { - /* Enable memory mapping. Note: Don't do this when busmaster. */ - hp100_outw(HP100_MMAP_DIS | HP100_RESET_HB, OPTION_LSW); - } - hp100_page(PERFORMANCE); - hp100_outw(0xfefe, IRQ_MASK); /* mask off all ints */ - hp100_outw(0xffff, IRQ_STATUS); /* ack IRQ */ - - /* enable a few interrupts: */ - if (lp->mode == 1) { /* busmaster mode */ - hp100_outw(HP100_RX_PDL_FILL_COMPL | - HP100_RX_PDA_ZERO | - HP100_RX_ERROR | - /* HP100_RX_PACKET | */ - /* HP100_RX_EARLY_INT | */ HP100_SET_HB | - /* HP100_TX_PDA_ZERO | */ - HP100_TX_COMPLETE | - /* HP100_MISC_ERROR | */ - HP100_TX_ERROR | HP100_SET_LB, IRQ_MASK); - } else { - hp100_outw(HP100_RX_PACKET | - HP100_RX_ERROR | HP100_SET_HB | - HP100_TX_ERROR | HP100_SET_LB, IRQ_MASK); - } - - /* Enable MAC Tx and RX, set MAC modes, ... */ - /* Note: This function also turns on the interrupts. */ - hp100_set_multicast_list(dev); + hp100_outw( 0x4220, TRACE ); + printk("hp100: %s: hp100_start_interface\n",dev->name); +#endif + + save_flags( flags ); + cli(); + + /* Ensure the adapter does not want to request an interrupt when */ + /* enabling the IRQ line to be active on the bus (i.e. not tri-stated) */ + hp100_page( PERFORMANCE ); + hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all ints */ + hp100_outw( 0xffff, IRQ_STATUS ); /* ack all IRQs */ + hp100_outw( HP100_FAKE_INT|HP100_INT_EN|HP100_RESET_LB, OPTION_LSW); + /* Un Tri-state int. TODO: Check if shared interrupts can be realised? */ + hp100_outw( HP100_TRI_INT | HP100_RESET_HB, OPTION_LSW ); + + if(lp->mode==1) + { + /* Make sure BM bit is set... */ + hp100_page(HW_MAP); + hp100_orb( HP100_BM_MASTER, BM ); + hp100_rxfill( dev ); + } + else if(lp->mode==2) + { + /* Enable memory mapping. Note: Don't do this when busmaster. */ + hp100_outw( HP100_MMAP_DIS | HP100_RESET_HB, OPTION_LSW ); + } + + hp100_page(PERFORMANCE); + hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all ints */ + hp100_outw( 0xffff, IRQ_STATUS ); /* ack IRQ */ + + /* enable a few interrupts: */ + if(lp->mode==1) /* busmaster mode */ + { + hp100_outw( HP100_RX_PDL_FILL_COMPL | + HP100_RX_PDA_ZERO | + HP100_RX_ERROR | + /* HP100_RX_PACKET | */ + /* HP100_RX_EARLY_INT | */ HP100_SET_HB | + /* HP100_TX_PDA_ZERO | */ + HP100_TX_COMPLETE | + /* HP100_MISC_ERROR | */ + HP100_TX_ERROR | HP100_SET_LB, IRQ_MASK ); + } + else + { + hp100_outw( HP100_RX_PACKET | + HP100_RX_ERROR | HP100_SET_HB | + HP100_TX_ERROR | HP100_SET_LB , IRQ_MASK ); + } + + /* Enable MAC Tx and RX, set MAC modes, ... */ + hp100_set_multicast_list( dev ); + + restore_flags( flags ); } - -static void hp100_stop_interface(struct device *dev) + +static void hp100_stop_interface( struct device *dev ) { - struct hp100_private *lp = (struct hp100_private *) dev->priv; - int ioaddr = dev->base_addr; - u_int val; + struct hp100_private *lp = (struct hp100_private *)dev->priv; + int ioaddr = dev->base_addr; + u_int val; #ifdef HP100_DEBUG_B - printk("hp100: hp100_stop_interface %s\n", dev->name); - hp100_outw(0x4221, TRACE); -#endif - - if (lp->mode == 1) - hp100_BM_shutdown(dev); - else { - /* Note: MMAP_DIS will be reenabled by start_interface */ - hp100_outw(HP100_INT_EN | HP100_RESET_LB | - HP100_TRI_INT | HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW); - val = hp100_inw(OPTION_LSW); - - hp100_page(MAC_CTRL); - hp100_andb(~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1); - - if (!(val & HP100_HW_RST)) - return; /* If reset, imm. return ... */ - /* ... else: busy wait until idle */ - for (val = 0; val < 6000; val++) - if ((hp100_inb(MAC_CFG_1) & (HP100_TX_IDLE | HP100_RX_IDLE)) == - (HP100_TX_IDLE | HP100_RX_IDLE)) { - hp100_page(PERFORMANCE); - return; - } - printk("%s: hp100_stop_interface - timeout\n", dev->name); - hp100_page(PERFORMANCE); - } + printk("hp100: %s: hp100_stop_interface\n",dev->name); + hp100_outw( 0x4221, TRACE ); +#endif + + if (lp->mode==1) + hp100_BM_shutdown( dev ); + else + { + /* Note: MMAP_DIS will be reenabled by start_interface */ + hp100_outw( HP100_INT_EN | HP100_RESET_LB | + HP100_TRI_INT | HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW ); + val = hp100_inw( OPTION_LSW ); + + hp100_page( MAC_CTRL ); + hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 ); + + if ( !(val & HP100_HW_RST) ) return; /* If reset, imm. return ... */ + /* ... else: busy wait until idle */ + for ( val = 0; val < 6000; val++ ) + if ( ( hp100_inb( MAC_CFG_1 ) & (HP100_TX_IDLE | HP100_RX_IDLE) ) == + (HP100_TX_IDLE | HP100_RX_IDLE) ) + { + hp100_page(PERFORMANCE); + return; + } + printk( "hp100: %s: hp100_stop_interface - timeout\n", dev->name ); + hp100_page(PERFORMANCE); + } } - -static void hp100_load_eeprom(struct device *dev) + +static void hp100_load_eeprom( struct device *dev, u_short probe_ioaddr ) { - int i; - int ioaddr = dev->base_addr; + int i; + int ioaddr = probe_ioaddr > 0 ? probe_ioaddr : dev->base_addr; #ifdef HP100_DEBUG_B - hp100_outw(0x4222, TRACE); + hp100_outw( 0x4222, TRACE ); #endif - hp100_page(EEPROM_CTRL); - hp100_andw(~HP100_EEPROM_LOAD, EEPROM_CTRL); - hp100_orw(HP100_EEPROM_LOAD, EEPROM_CTRL); - for (i = 0; i < 10000; i++) - if (!(hp100_inb(OPTION_MSW) & HP100_EE_LOAD)) - return; - printk("%s: hp100_load_eeprom - timeout\n", dev->name); + hp100_page( EEPROM_CTRL ); + hp100_andw( ~HP100_EEPROM_LOAD, EEPROM_CTRL ); + hp100_orw( HP100_EEPROM_LOAD, EEPROM_CTRL ); + for ( i = 0; i < 10000; i++ ) + if ( !( hp100_inb( OPTION_MSW ) & HP100_EE_LOAD ) ) return; + printk( "hp100: %s: hp100_load_eeprom - timeout\n", dev->name ); } - + /* Sense connection status. * return values: LAN_10 - Connected to 10Mbit/s network * LAN_100 - Connected to 100Mbit/s network * LAN_ERR - not connected or 100Mbit/s Hub down */ -static int hp100_sense_lan(struct device *dev) +static int hp100_sense_lan( struct device *dev ) { - int ioaddr = dev->base_addr; - u_short val_VG, val_10; - struct hp100_private *lp = (struct hp100_private *) dev->priv; + int ioaddr = dev->base_addr; + u_short val_VG, val_10; + struct hp100_private *lp = (struct hp100_private *)dev->priv; #ifdef HP100_DEBUG_B - hp100_outw(0x4223, TRACE); -#endif - - hp100_page(MAC_CTRL); - /* Enable Auto Selection */ - /* hp100_orb( HP100_VG_RESET|HP100_LINK_CMD|HP100_VG_SEL, VG_LAN_CFG_1 ); */ - /* hp100_orb( HP100_DOT3_MAC,10_LAN_CFG_2); */ - /* hp100_orb( HP100_AUTO_MODE,MAC_CFG_3); */ - /* Now we have to wait a while... */ - /* for(i=0; i<5000; i++) */ - /* { */ - val_10 = hp100_inb(10_LAN_CFG_1); - val_VG = hp100_inb(VG_LAN_CFG_1); - /* } */ + hp100_outw( 0x4223, TRACE ); +#endif + + hp100_page( MAC_CTRL ); + val_10 = hp100_inb( 10_LAN_CFG_1 ); + val_VG = hp100_inb( VG_LAN_CFG_1 ); + hp100_page( PERFORMANCE ); #ifdef HP100_DEBUG - printk("hp100_sense_lan: val_VG = 0x%04x, val_10 = 0x%04x\n", val_VG, val_10); -#endif - if (val_10 & HP100_LINK_BEAT_ST) - return HP100_LAN_10; - if ((lp->id->id == 0x02019F022) || - (lp->id->id == 0x01042103c) || - (lp->id->id == 0x01040103c)) { - hp100_page(PERFORMANCE); - return HP100_LAN_ERR; /* Those cards don't have a 100 Mbit connector */ - } - /* for ( i = 0; i < 2500; i++ ) */ - /* { */ - val_VG = hp100_inb(VG_LAN_CFG_1); - hp100_page(PERFORMANCE); - - if (val_VG & HP100_LINK_CABLE_ST) /* Can hear the HUBs tone. */ - return HP100_LAN_100; - /* } */ - return HP100_LAN_ERR; + printk( "hp100: %s: sense_lan: val_VG = 0x%04x, val_10 = 0x%04x\n", dev->name, val_VG, val_10 ); +#endif + + if ( val_10 & HP100_LINK_BEAT_ST ) /* 10Mb connection is active */ + return HP100_LAN_10; + + if ( val_10 & HP100_AUI_ST ) /* have we BNC or AUI onboard? */ + { + val_10 |= HP100_AUI_SEL | HP100_LOW_TH; + hp100_page( MAC_CTRL ); + hp100_outb( val_10, 10_LAN_CFG_1 ); + hp100_page( PERFORMANCE ); + return HP100_LAN_10; + } + + if ( (lp->id->id == 0x02019F022) || + (lp->id->id == 0x01042103c) || + (lp->id->id == 0x01040103c) ) + return HP100_LAN_ERR; /* Those cards don't have a 100 Mbit connector */ + + if ( val_VG & HP100_LINK_CABLE_ST ) /* Can hear the HUBs tone. */ + return HP100_LAN_100; + return HP100_LAN_ERR; } - -static int hp100_down_vg_link(struct device *dev) + +static int hp100_down_vg_link( struct device *dev ) { - struct hp100_private *lp = (struct hp100_private *) dev->priv; - int ioaddr = dev->base_addr; - unsigned long time; - long savelan, newlan; + struct hp100_private *lp = (struct hp100_private *)dev->priv; + int ioaddr = dev->base_addr; + unsigned long time; + long savelan, newlan; #ifdef HP100_DEBUG_B - hp100_outw(0x4224, TRACE); - printk("hp100: down_vg_link\n"); + hp100_outw( 0x4224, TRACE ); + printk("hp100: %s: down_vg_link\n", dev->name); #endif - hp100_page(MAC_CTRL); - time = jiffies + (HZ / 4); - do { - if (hp100_inb(VG_LAN_CFG_1) & HP100_LINK_CABLE_ST) - break; - } while (time > jiffies); + hp100_page( MAC_CTRL ); + time=jiffies+(HZ/4); + do{ + if ( hp100_inb( VG_LAN_CFG_1 ) & HP100_LINK_CABLE_ST ) break; + } while (time>jiffies); - if (jiffies >= time) /* no signal->no logout */ - return 0; + if ( jiffies >= time ) /* no signal->no logout */ + return 0; - /* Drop the VG Link by clearing the link up cmd and load addr. */ + /* Drop the VG Link by clearing the link up cmd and load addr.*/ - hp100_andb(~(HP100_LOAD_ADDR | HP100_LINK_CMD), VG_LAN_CFG_1); - hp100_orb(HP100_VG_SEL, VG_LAN_CFG_1); + hp100_andb( ~( HP100_LOAD_ADDR| HP100_LINK_CMD), VG_LAN_CFG_1); + hp100_orb( HP100_VG_SEL, VG_LAN_CFG_1); - /* Conditionally stall for >250ms on Link-Up Status (to go down) */ - time = jiffies + (HZ / 2); - do { - if (!(hp100_inb(VG_LAN_CFG_1) & HP100_LINK_UP_ST)) - break; - } while (time > jiffies); + /* Conditionally stall for >250ms on Link-Up Status (to go down) */ + time=jiffies+(HZ/2); + do{ + if ( !(hp100_inb( VG_LAN_CFG_1) & HP100_LINK_UP_ST) ) break; + } while(time>jiffies); #ifdef HP100_DEBUG - if (jiffies >= time) - printk("hp100_down_vg_link: Link does not go down?\n"); -#endif - - /* To prevent condition where Rev 1 VG MAC and old hubs do not complete */ - /* logout under traffic (even though all the status bits are cleared), */ - /* do this workaround to get the Rev 1 MAC in its idle state */ - if (lp->chip == HP100_CHIPID_LASSEN) { - /* Reset VG MAC to insure it leaves the logoff state even if */ - /* the Hub is still emitting tones */ - hp100_andb(~HP100_VG_RESET, VG_LAN_CFG_1); - udelay(1500); /* wait for >1ms */ - hp100_orb(HP100_VG_RESET, VG_LAN_CFG_1); /* Release Reset */ - udelay(1500); - } - /* New: For lassen, switch to 10 Mbps mac briefly to clear training ACK */ - /* to get the VG mac to full reset. This is not req.d with later chips */ - /* Note: It will take the between 1 and 2 seconds for the VG mac to be */ - /* selected again! This will be left to the connect hub function to */ - /* perform if desired. */ - if (lp->chip == HP100_CHIPID_LASSEN) { - /* Have to write to 10 and 100VG control registers simultaneously */ - savelan = newlan = hp100_inl(10_LAN_CFG_1); /* read 10+100 LAN_CFG regs */ - newlan &= ~(HP100_VG_SEL << 16); - newlan |= (HP100_DOT3_MAC) << 8; - hp100_andb(~HP100_AUTO_MODE, MAC_CFG_3); /* Autosel off */ - hp100_outl(newlan, 10_LAN_CFG_1); - - /* Conditionally stall for 5sec on VG selected. */ - time = jiffies + (HZ * 5); - do { - if (!(hp100_inb(MAC_CFG_4) & HP100_MAC_SEL_ST)) - break; - } while (time > jiffies); - - hp100_orb(HP100_AUTO_MODE, MAC_CFG_3); /* Autosel back on */ - hp100_outl(savelan, 10_LAN_CFG_1); - } - time = jiffies + (3 * HZ); /* Timeout 3s */ - do { - if ((hp100_inb(VG_LAN_CFG_1) & HP100_LINK_CABLE_ST) == 0) - break; - } while (time > jiffies); - - if (time <= jiffies) { + if (jiffies>=time) + printk("hp100: %s: down_vg_link: Link does not go down?\n", dev->name); +#endif + + /* To prevent condition where Rev 1 VG MAC and old hubs do not complete */ + /* logout under traffic (even though all the status bits are cleared), */ + /* do this workaround to get the Rev 1 MAC in its idle state */ + if ( lp->chip==HP100_CHIPID_LASSEN ) + { + /* Reset VG MAC to insure it leaves the logoff state even if */ + /* the Hub is still emitting tones */ + hp100_andb(~HP100_VG_RESET, VG_LAN_CFG_1); + udelay(1500); /* wait for >1ms */ + hp100_orb(HP100_VG_RESET, VG_LAN_CFG_1); /* Release Reset */ + udelay(1500); + } + + /* New: For lassen, switch to 10 Mbps mac briefly to clear training ACK */ + /* to get the VG mac to full reset. This is not req.d with later chips */ + /* Note: It will take the between 1 and 2 seconds for the VG mac to be */ + /* selected again! This will be left to the connect hub function to */ + /* perform if desired. */ + if (lp->chip==HP100_CHIPID_LASSEN) + { + /* Have to write to 10 and 100VG control registers simultaneously */ + savelan=newlan=hp100_inl(10_LAN_CFG_1); /* read 10+100 LAN_CFG regs */ + newlan &= ~(HP100_VG_SEL<<16); + newlan |= (HP100_DOT3_MAC)<<8; + hp100_andb( ~HP100_AUTO_MODE, MAC_CFG_3); /* Autosel off */ + hp100_outl(newlan, 10_LAN_CFG_1); + + /* Conditionally stall for 5sec on VG selected. */ + time=jiffies+(HZ*5); + do{ + if( !(hp100_inb(MAC_CFG_4) & HP100_MAC_SEL_ST) ) break; + } while(time>jiffies); + + hp100_orb( HP100_AUTO_MODE, MAC_CFG_3); /* Autosel back on */ + hp100_outl(savelan, 10_LAN_CFG_1); + } + + time=jiffies+(3*HZ); /* Timeout 3s */ + do { + if ( (hp100_inb( VG_LAN_CFG_1 )&HP100_LINK_CABLE_ST) == 0) break; + } while (time>jiffies); + + if(time<=jiffies) + { #ifdef HP100_DEBUG - printk("hp100_down_vg_link: timeout\n"); -#endif - return -EIO; - } - time = jiffies + (2 * HZ); /* This seems to take a while.... */ - do { - } while (time > jiffies); - - return 0; + printk( "hp100: %s: down_vg_link: timeout\n", dev->name ); +#endif + return -EIO; + } + + time=jiffies+(2*HZ); /* This seems to take a while.... */ + do {} while (time>jiffies); + + return 0; } - -static int hp100_login_to_vg_hub(struct device *dev, u_short force_relogin) + +static int hp100_login_to_vg_hub( struct device *dev, u_short force_relogin ) { - int ioaddr = dev->base_addr; - struct hp100_private *lp = (struct hp100_private *) dev->priv; - u_short val = 0; - unsigned long time; - int startst; + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *)dev->priv; + u_short val=0; + unsigned long time; + int startst; #ifdef HP100_DEBUG_B - hp100_outw(0x4225, TRACE); - printk("hp100: login_to_vg_hub\n"); -#endif - - /* Initiate a login sequence iff VG MAC is enabled and either Load Address - * bit is zero or the force relogin flag is set (e.g. due to MAC address or - * promiscuous mode change) - */ - hp100_page(MAC_CTRL); - startst = hp100_inb(VG_LAN_CFG_1); - if ((force_relogin == TRUE) || (hp100_inb(MAC_CFG_4) & HP100_MAC_SEL_ST)) { + hp100_outw( 0x4225, TRACE ); + printk("hp100: %s: login_to_vg_hub\n", dev->name); +#endif + + /* Initiate a login sequence iff VG MAC is enabled and either Load Address + * bit is zero or the force relogin flag is set (e.g. due to MAC address or + * promiscuous mode change) + */ + hp100_page( MAC_CTRL ); + startst=hp100_inb( VG_LAN_CFG_1 ); + if((force_relogin==TRUE)||(hp100_inb( MAC_CFG_4 )&HP100_MAC_SEL_ST)) + { #ifdef HP100_DEBUG_TRAINING - printk("hp100: Start training\n"); + printk("hp100: %s: Start training\n", dev->name); #endif - /* Ensure VG Reset bit is 1 (i.e., do not reset) */ - hp100_orb(HP100_VG_RESET, VG_LAN_CFG_1); + /* Ensure VG Reset bit is 1 (i.e., do not reset)*/ + hp100_orb( HP100_VG_RESET , VG_LAN_CFG_1 ); - /* If Lassen AND auto-select-mode AND VG tones were sensed on */ - /* entry then temporarily put them into force 100Mbit mode */ - if ((lp->chip == HP100_CHIPID_LASSEN) && (startst & HP100_LINK_CABLE_ST)) - hp100_andb(~HP100_DOT3_MAC, 10_LAN_CFG_2); - - /* Drop the VG link by zeroing Link Up Command and Load Address */ - hp100_andb(~(HP100_LINK_CMD /* |HP100_LOAD_ADDR */ ), VG_LAN_CFG_1); + /* If Lassen AND auto-select-mode AND VG tones were sensed on */ + /* entry then temporarily put them into force 100Mbit mode */ + if((lp->chip==HP100_CHIPID_LASSEN)&&( startst & HP100_LINK_CABLE_ST ) ) + hp100_andb( ~HP100_DOT3_MAC, 10_LAN_CFG_2 ); + + /* Drop the VG link by zeroing Link Up Command and Load Address */ + hp100_andb( ~(HP100_LINK_CMD/* |HP100_LOAD_ADDR */), VG_LAN_CFG_1); #ifdef HP100_DEBUG_TRAINING - printk("hp100: Bring down the link\n"); -#endif - - /* Wait for link to drop */ - time = jiffies + (HZ / 10); - do { - if (~(hp100_inb(VG_LAN_CFG_1) & HP100_LINK_UP_ST)) - break; - } while (time > jiffies); - - /* Start an addressed training and optionally request promiscuous port */ - if ((dev->flags) & IFF_PROMISC) { - hp100_orb(HP100_PROM_MODE, VG_LAN_CFG_2); - if (lp->chip == HP100_CHIPID_LASSEN) - hp100_orw(HP100_MACRQ_PROMSC, TRAIN_REQUEST); - } else { - hp100_andb(~HP100_PROM_MODE, VG_LAN_CFG_2); - /* For ETR parts we need to reset the prom. bit in the training - * register, otherwise promiscious mode won't be disabled. - */ - if (lp->chip == HP100_CHIPID_LASSEN) { - hp100_andw(~HP100_MACRQ_PROMSC, TRAIN_REQUEST); - } - } - - /* With ETR parts, frame format request bits can be set. */ - if (lp->chip == HP100_CHIPID_LASSEN) - hp100_orb(HP100_MACRQ_FRAMEFMT_EITHER, TRAIN_REQUEST); - - hp100_orb(HP100_LINK_CMD | HP100_LOAD_ADDR | HP100_VG_RESET, VG_LAN_CFG_1); + printk("hp100: %s: Bring down the link\n", dev->name); +#endif - /* Note: Next wait could be omitted for Hood and earlier chips under */ - /* certain circumstances */ - /* TODO: check if hood/earlier and skip wait. */ + /* Wait for link to drop */ + time = jiffies + (HZ/10); + do { + if (~(hp100_inb( VG_LAN_CFG_1 )& HP100_LINK_UP_ST) ) break; + } while (time>jiffies); - /* Wait for either short timeout for VG tones or long for login */ - /* Wait for the card hardware to signalise link cable status ok... */ - hp100_page(MAC_CTRL); - time = jiffies + (1 * HZ); /* 1 sec timeout for cable st */ - do { - if (hp100_inb(VG_LAN_CFG_1) & HP100_LINK_CABLE_ST) - break; - } while (jiffies < time); + /* Start an addressed training and optionally request promiscuous port */ + if ( (dev->flags) & IFF_PROMISC ) + { + hp100_orb( HP100_PROM_MODE, VG_LAN_CFG_2); + if(lp->chip==HP100_CHIPID_LASSEN) + hp100_orw( HP100_MACRQ_PROMSC, TRAIN_REQUEST ); + } + else + { + hp100_andb( ~HP100_PROM_MODE, VG_LAN_CFG_2); + /* For ETR parts we need to reset the prom. bit in the training + * register, otherwise promiscious mode won't be disabled. + */ + if(lp->chip==HP100_CHIPID_LASSEN) + { + hp100_andw( ~HP100_MACRQ_PROMSC, TRAIN_REQUEST ); + } + } - if (jiffies >= time) { + /* With ETR parts, frame format request bits can be set. */ + if(lp->chip==HP100_CHIPID_LASSEN) + hp100_orb( HP100_MACRQ_FRAMEFMT_EITHER, TRAIN_REQUEST); + + hp100_orb( HP100_LINK_CMD|HP100_LOAD_ADDR|HP100_VG_RESET, VG_LAN_CFG_1); + + /* Note: Next wait could be omitted for Hood and earlier chips under */ + /* certain circumstances */ + /* TODO: check if hood/earlier and skip wait. */ + + /* Wait for either short timeout for VG tones or long for login */ + /* Wait for the card hardware to signalise link cable status ok... */ + hp100_page( MAC_CTRL ); + time = jiffies + ( 1*HZ ); /* 1 sec timeout for cable st */ + do { + if ( hp100_inb( VG_LAN_CFG_1 ) & HP100_LINK_CABLE_ST ) break; + } while ( jiffies < time ); + + if ( jiffies >= time ) + { #ifdef HP100_DEBUG_TRAINING - printk("hp100: Link cable status not ok? Training aborted.\n"); -#endif - } else { + printk( "hp100: %s: Link cable status not ok? Training aborted.\n", dev->name ); +#endif + } + else + { #ifdef HP100_DEBUG_TRAINING - printk("hp100: HUB tones detected. Trying to train.\n"); + printk( "hp100: %s: HUB tones detected. Trying to train.\n", dev->name); #endif - time = jiffies + (2 * HZ); /* again a timeout */ - do { - val = hp100_inb(VG_LAN_CFG_1); - if ((val & (HP100_LINK_UP_ST))) { + time = jiffies + ( 2*HZ ); /* again a timeout */ + do { + val = hp100_inb( VG_LAN_CFG_1 ); + if ( (val & ( HP100_LINK_UP_ST )) ) + { #ifdef HP100_DEBUG_TRAINING - printk("hp100: Passed training.\n"); + printk( "hp100: %s: Passed training.\n", dev->name); #endif - break; - } - } while (time > jiffies); - } - - /* If LINK_UP_ST is set, then we are logged into the hub. */ - if ((jiffies <= time) && (val & HP100_LINK_UP_ST)) { + break; + } + } while ( time > jiffies ); + } + + /* If LINK_UP_ST is set, then we are logged into the hub. */ + if ( (jiffies<=time) && (val & HP100_LINK_UP_ST) ) + { #ifdef HP100_DEBUG_TRAINING - printk("hp100: Successfully logged into the HUB.\n"); - if (lp->chip == HP100_CHIPID_LASSEN) { - val = hp100_inw(TRAIN_ALLOW); - printk("hp100: Card supports 100VG MAC Version \"%s\" ", - (hp100_inw(TRAIN_REQUEST) & HP100_CARD_MACVER) ? "802.12" : "Pre"); - printk("Driver will use MAC Version \"%s\"\n", - (val & HP100_HUB_MACVER) ? "802.12" : "Pre"); - printk("hp100: Frame format is %s.\n", (val & HP100_MALLOW_FRAMEFMT) ? "802.5" : "802.3"); - } -#endif - } else { - /* If LINK_UP_ST is not set, login was not successful */ - printk("hp100/%s: Problem logging into the HUB.\n", dev->name); - if (lp->chip == HP100_CHIPID_LASSEN) { - /* Check allowed Register to find out why there is a problem. */ - val = hp100_inw(TRAIN_ALLOW); /* wont work on non-ETR card */ + printk( "hp100: %s: Successfully logged into the HUB.\n", dev->name); + if(lp->chip==HP100_CHIPID_LASSEN) + { + val = hp100_inw(TRAIN_ALLOW); + printk( "hp100: %s: Card supports 100VG MAC Version \"%s\" ", + dev->name,(hp100_inw(TRAIN_REQUEST)&HP100_CARD_MACVER) ? "802.12" : "Pre"); + printk( "Driver will use MAC Version \"%s\"\n", + ( val & HP100_HUB_MACVER) ? "802.12" : "Pre" ); + printk( "hp100: %s: Frame format is %s.\n",dev->name,(val&HP100_MALLOW_FRAMEFMT)?"802.5":"802.3"); + } +#endif + } + else + { + /* If LINK_UP_ST is not set, login was not successful */ + printk("hp100: %s: Problem logging into the HUB.\n",dev->name); + if(lp->chip==HP100_CHIPID_LASSEN) + { + /* Check allowed Register to find out why there is a problem. */ + val = hp100_inw( TRAIN_ALLOW ); /* wont work on non-ETR card */ #ifdef HP100_DEBUG_TRAINING - printk("hp100: MAC Configuration requested: 0x%04x, HUB allowed: 0x%04x\n", hp100_inw(TRAIN_REQUEST), val); -#endif - if (val & HP100_MALLOW_ACCDENIED) - printk("hp100: HUB access denied.\n"); - if (val & HP100_MALLOW_CONFIGURE) - printk("hp100: MAC Configuration is incompatible with the Network.\n"); - if (val & HP100_MALLOW_DUPADDR) - printk("hp100: Duplicate MAC Address on the Network.\n"); - } - } - - /* If we have put the chip into forced 100 Mbit mode earlier, go back */ - /* to auto-select mode */ - - if ((lp->chip == HP100_CHIPID_LASSEN) && (startst & HP100_LINK_CABLE_ST)) { - hp100_page(MAC_CTRL); - hp100_orb(HP100_DOT3_MAC, 10_LAN_CFG_2); - } - val = hp100_inb(VG_LAN_CFG_1); - - /* Clear the MISC_ERROR Interrupt, which might be generated when doing the relogin */ - hp100_page(PERFORMANCE); - hp100_outw(HP100_MISC_ERROR, IRQ_STATUS); - - if (val & HP100_LINK_UP_ST) - return (0); /* login was ok */ - else { - printk("hp100: Training failed.\n"); - hp100_down_vg_link(dev); - return -EIO; - } - } - /* no forced relogin & already link there->no training. */ - return -EIO; + printk("hp100: %s: MAC Configuration requested: 0x%04x, HUB allowed: 0x%04x\n", dev->name, hp100_inw(TRAIN_REQUEST), val); +#endif + if ( val & HP100_MALLOW_ACCDENIED ) + printk("hp100: %s: HUB access denied.\n", dev->name); + if ( val & HP100_MALLOW_CONFIGURE ) + printk("hp100: %s: MAC Configuration is incompatible with the Network.\n", dev->name); + if ( val & HP100_MALLOW_DUPADDR ) + printk("hp100: %s: Duplicate MAC Address on the Network.\n", dev->name); + } + } + + /* If we have put the chip into forced 100 Mbit mode earlier, go back */ + /* to auto-select mode */ + + if( (lp->chip==HP100_CHIPID_LASSEN)&&(startst & HP100_LINK_CABLE_ST) ) + { + hp100_page( MAC_CTRL ); + hp100_orb( HP100_DOT3_MAC, 10_LAN_CFG_2 ); + } + + val=hp100_inb(VG_LAN_CFG_1); + + /* Clear the MISC_ERROR Interrupt, which might be generated when doing the relogin */ + hp100_page(PERFORMANCE); + hp100_outw( HP100_MISC_ERROR, IRQ_STATUS); + + if (val&HP100_LINK_UP_ST) + return(0); /* login was ok */ + else + { + printk("hp100: %s: Training failed.\n", dev->name); + hp100_down_vg_link( dev ); + return -EIO; + } + } + /* no forced relogin & already link there->no training. */ + return -EIO; } - -static void hp100_cascade_reset(struct device *dev, u_short enable) + +static void hp100_cascade_reset( struct device *dev, u_short enable ) { - int ioaddr = dev->base_addr; - struct hp100_private *lp = (struct hp100_private *) dev->priv; - int i; + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *)dev->priv; + int i; #ifdef HP100_DEBUG_B - hp100_outw(0x4226, TRACE); - printk("hp100: cascade_reset\n"); -#endif - - if (enable == TRUE) { - hp100_outw(HP100_HW_RST | HP100_RESET_LB, OPTION_LSW); - if (lp->chip == HP100_CHIPID_LASSEN) { - /* Lassen requires a PCI transmit fifo reset */ - hp100_page(HW_MAP); - hp100_andb(~HP100_PCI_RESET, PCICTRL2); - hp100_orb(HP100_PCI_RESET, PCICTRL2); - /* Wait for min. 300 ns */ - /* we cant use jiffies here, because it may be */ - /* that we have disabled the timer... */ - for (i = 0; i < 0xffff; i++); - hp100_andb(~HP100_PCI_RESET, PCICTRL2); - hp100_page(PERFORMANCE); - } - } else { /* bring out of reset */ - hp100_outw(HP100_HW_RST | HP100_SET_LB, OPTION_LSW); - for (i = 0; i < 0xffff; i++); - hp100_page(PERFORMANCE); - } + hp100_outw( 0x4226, TRACE ); + printk("hp100: %s: cascade_reset\n", dev->name); +#endif + + if (enable==TRUE) + { + hp100_outw( HP100_HW_RST | HP100_RESET_LB, OPTION_LSW ); + if(lp->chip==HP100_CHIPID_LASSEN) + { + /* Lassen requires a PCI transmit fifo reset */ + hp100_page( HW_MAP ); + hp100_andb( ~HP100_PCI_RESET, PCICTRL2 ); + hp100_orb( HP100_PCI_RESET, PCICTRL2 ); + /* Wait for min. 300 ns */ + /* we cant use jiffies here, because it may be */ + /* that we have disabled the timer... */ + for (i=0; i<0xffff; i++); + hp100_andb( ~HP100_PCI_RESET, PCICTRL2 ); + hp100_page( PERFORMANCE ); + } + } + else + { /* bring out of reset */ + hp100_outw(HP100_HW_RST|HP100_SET_LB, OPTION_LSW); + for (i=0; i<0xffff; i++ ); + hp100_page(PERFORMANCE); + } } -#ifdef HP100_DEBUG -void hp100_RegisterDump(struct device *dev) +#ifdef HP100_DEBUG +void hp100_RegisterDump( struct device *dev ) { - int ioaddr = dev->base_addr; - int Page; - int Register; - - /* Dump common registers */ - printk("hp100: Cascade Register Dump\n"); - printk("hardware id #1: 0x%.2x\n", hp100_inb(HW_ID)); - printk("hardware id #2/paging: 0x%.2x\n", hp100_inb(PAGING)); - printk("option #1: 0x%.4x\n", hp100_inw(OPTION_LSW)); - printk("option #2: 0x%.4x\n", hp100_inw(OPTION_MSW)); - - /* Dump paged registers */ - for (Page = 0; Page < 8; Page++) { - /* Dump registers */ - printk("page: 0x%.2x\n", Page); - outw(Page, ioaddr + 0x02); - for (Register = 0x8; Register < 0x22; Register += 2) { - /* Display Register contents except data port */ - if (((Register != 0x10) && (Register != 0x12)) || (Page > 0)) { - printk("0x%.2x = 0x%.4x\n", Register, inw(ioaddr + Register)); - } - } + int ioaddr=dev->base_addr; + int Page; + int Register; + + /* Dump common registers */ + printk("hp100: %s: Cascade Register Dump\n", dev->name); + printk("hardware id #1: 0x%.2x\n",hp100_inb(HW_ID)); + printk("hardware id #2/paging: 0x%.2x\n",hp100_inb(PAGING)); + printk("option #1: 0x%.4x\n",hp100_inw(OPTION_LSW)); + printk("option #2: 0x%.4x\n",hp100_inw(OPTION_MSW)); + + /* Dump paged registers */ + for (Page = 0; Page < 8; Page++) + { + /* Dump registers */ + printk("page: 0x%.2x\n",Page); + outw( Page, ioaddr+0x02); + for (Register = 0x8; Register < 0x22; Register += 2) + { + /* Display Register contents except data port */ + if (((Register != 0x10) && (Register != 0x12)) || (Page > 0)) + { + printk("0x%.2x = 0x%.4x\n",Register,inw(ioaddr+Register)); + } } - hp100_page(PERFORMANCE); + } + hp100_page(PERFORMANCE); } #endif - + /* * module section */ - + #ifdef MODULE /* Parameters set by insmod */ -int hp100_port[5] = -{0, -1, -1, -1, -1}; +int hp100_port[5] = { 0, -1, -1, -1, -1 }; #ifdef LINUX_2_1 MODULE_PARM(hp100_port, "1-5i"); #endif #ifdef LINUX_2_1 -char hp100_name[5][IFNAMSIZ] = -{"", "", "", "", ""}; +char hp100_name[5][IFNAMSIZ] = { "", "", "", "", "" }; MODULE_PARM(hp100_name, "1-5c" __MODULE_STRING(IFNAMSIZ)); #else -static char devname[5][IFNAMSIZ] = -{"", "", "", "", ""}; -static char *hp100_name[5] = -{devname[0], devname[1], - devname[2], devname[3], - devname[4]}; +static char devname[5][IFNAMSIZ] = { "", "", "", "", "" }; +static char *hp100_name[5] = { devname[0], devname[1], + devname[2], devname[3], + devname[4] }; #endif /* List of devices */ -static struct device *hp100_devlist[5] = -{NULL, NULL, NULL, NULL, NULL}; +static struct device *hp100_devlist[5] = { NULL, NULL, NULL, NULL, NULL }; /* * Note: if you have more than five 100vg cards in your pc, feel free to @@ -2606,61 +3055,64 @@ static struct device *hp100_devlist[5] = * option hp100 hp100_port=0x280 hp100_name=eth239 */ -int init_module(void) +int init_module( void ) { - int i; - int ret = 0; - - if (hp100_port == 0 && !EISA_bus && !pcibios_present()) - printk("HP100: You should not use auto-probing with insmod!\n"); - - /* Loop on all possible base addresses */ - i = -1; - while ((hp100_port[++i] != -1) && (i < 5)) { - /* Create device and set basics args */ - hp100_devlist[i] = kmalloc(sizeof(struct device), GFP_KERNEL); - memset(hp100_devlist[i], 0x00, sizeof(struct device)); - hp100_devlist[i]->name = hp100_name[i]; - hp100_devlist[i]->base_addr = hp100_port[i]; - hp100_devlist[i]->init = &hp100_probe; - - /* Try to create the device */ - if (register_netdev(hp100_devlist[i]) != 0) { - /* DeAllocate everything */ - /* Note: if dev->priv is mallocated, there is no way to fail */ - kfree_s(hp100_devlist[i], sizeof(struct device)); - hp100_devlist[i] = (struct device *) NULL; - ret = -EIO; - } - } /* Loop over all devices */ + int i, cards; + + if (hp100_port == 0 && !EISA_bus && !pcibios_present()) + printk("hp100: You should not use auto-probing with insmod!\n"); + + /* Loop on all possible base addresses */ + i = -1; cards = 0; + while((hp100_port[++i] != -1) && (i < 5)) + { + /* Create device and set basics args */ + hp100_devlist[i] = kmalloc(sizeof(struct device), GFP_KERNEL); + memset(hp100_devlist[i], 0x00, sizeof(struct device)); + hp100_devlist[i]->name = hp100_name[i]; + hp100_devlist[i]->base_addr = hp100_port[i]; + hp100_devlist[i]->init = &hp100_probe; + + /* Try to create the device */ + if(register_netdev(hp100_devlist[i]) != 0) + { + /* DeAllocate everything */ + /* Note: if dev->priv is mallocated, there is no way to fail */ + kfree_s(hp100_devlist[i], sizeof(struct device)); + hp100_devlist[i] = (struct device *) NULL; + } + else + cards++; + } /* Loop over all devices */ - return ret; + return cards > 0 ? 0 : -ENODEV; } -void cleanup_module(void) +void cleanup_module( void ) { - int i; - - /* TODO: Check if all skb's are released/freed. */ - for (i = 0; i < 5; i++) - if (hp100_devlist[i] != (struct device *) NULL) { - unregister_netdev(hp100_devlist[i]); - release_region(hp100_devlist[i]->base_addr, HP100_REGION_SIZE); - if (((struct hp100_private *) hp100_devlist[i]->priv)->mode == 1) /* busmaster */ - kfree_s(((struct hp100_private *) hp100_devlist[i]->priv)->page_vaddr, MAX_RINGSIZE + 0x0f); - if (((struct hp100_private *) hp100_devlist[i]->priv)->mem_ptr_virt) - iounmap(((struct hp100_private *) hp100_devlist[i]->priv)->mem_ptr_virt); - kfree_s(hp100_devlist[i]->priv, sizeof(struct hp100_private)); - hp100_devlist[i]->priv = NULL; - kfree_s(hp100_devlist[i], sizeof(struct device)); - hp100_devlist[i] = (struct device *) NULL; - } + int i; + + /* TODO: Check if all skb's are released/freed. */ + for(i = 0; i < 5; i++) + if(hp100_devlist[i] != (struct device *) NULL) + { + unregister_netdev( hp100_devlist[i] ); + release_region( hp100_devlist[i]->base_addr, HP100_REGION_SIZE ); + if( ((struct hp100_private *)hp100_devlist[i]->priv)->mode==1 ) /* busmaster */ + kfree_s( ((struct hp100_private *)hp100_devlist[i]->priv)->page_vaddr, MAX_RINGSIZE+0x0f); + if ( ((struct hp100_private *)hp100_devlist[i]->priv) -> mem_ptr_virt ) + iounmap( ((struct hp100_private *)hp100_devlist[i]->priv) -> mem_ptr_virt ); + kfree_s( hp100_devlist[i]->priv, sizeof( struct hp100_private ) ); + hp100_devlist[i]->priv = NULL; + kfree_s(hp100_devlist[i], sizeof(struct device)); + hp100_devlist[i] = (struct device *) NULL; + } } -#endif /* MODULE */ - +#endif /* MODULE */ + /* * Local variables: * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c hp100.c" diff --git a/drivers/net/hp100.h b/drivers/net/hp100.h index c1c62e6bb..436dd3700 100644 --- a/drivers/net/hp100.h +++ b/drivers/net/hp100.h @@ -1,7 +1,7 @@ /* * hp100.h: Hewlett Packard HP10/100VG ANY LAN ethernet driver for Linux. * - * $Id: hp100.h,v 1.4 1997/05/26 21:09:19 davem Exp $ + * $Id: hp100.h,v 1.51 1997/04/08 14:26:42 floeff Exp floeff $ * * Authors: Jaroslav Kysela, <perex@pf.jcu.cz> * Siegfried Loeffler <floeff@tunix.mathematik.uni-stuttgart.de> diff --git a/drivers/net/ipddp.c b/drivers/net/ipddp.c index 141349576..16e52bd98 100644 --- a/drivers/net/ipddp.c +++ b/drivers/net/ipddp.c @@ -83,9 +83,6 @@ static unsigned int ipddp_debug = IPDDP_DEBUG; /* Index to functions, as function prototypes. */ static int ipddp_xmit(struct sk_buff *skb, struct device *dev); static struct net_device_stats *ipddp_get_stats(struct device *dev); -static int ipddp_rebuild_header(struct sk_buff *skb); -static int ipddp_hard_header(struct sk_buff *skb, struct device *dev, - unsigned short type, void *daddr, void *saddr, unsigned len); static int ipddp_create(struct ipddp_route *new_rt); static int ipddp_delete(struct ipddp_route *rt); static struct ipddp_route* ipddp_find_route(struct ipddp_route *rt); @@ -119,10 +116,10 @@ int ipddp_init(struct device *dev) /* Let the user now what mode we are in */ if(ipddp_mode == IPDDP_ENCAP) - printk("%s: Appletalk-IP Encapsulation mode by Bradford W. Johnson <johns393@maroon.tc.umn.edu>\n", + printk("%s: Appletalk-IP Encap. mode by Bradford W. Johnson <johns393@maroon.tc.umn.edu>\n", dev->name); if(ipddp_mode == IPDDP_DECAP) - printk("%s: Appletalk-IP Decapsulation mode by Jay Schulist <Jay.Schulist@spacs.k12.wi.us>\n", + printk("%s: Appletalk-IP Decap. mode by Jay Schulist <Jay.Schulist@spacs.k12.wi.us>\n", dev->name); /* Fill in the device structure with ethernet-generic values. */ @@ -140,8 +137,6 @@ int ipddp_init(struct device *dev) dev->stop = ipddp_close; dev->get_stats = ipddp_get_stats; dev->do_ioctl = ipddp_ioctl; - dev->hard_header = ipddp_hard_header; /* see ip_output.c */ - dev->rebuild_header = ipddp_rebuild_header; dev->type = ARPHRD_IPDDP; /* IP over DDP tunnel */ dev->mtu = 585; @@ -158,24 +153,6 @@ int ipddp_init(struct device *dev) } /* - * Transmit LLAP/ELAP frame using aarp_send_ddp. - */ -static int ipddp_xmit(struct sk_buff *skb, struct device *dev) -{ - /* Retrieve the saved address hint */ - struct at_addr *at = (struct at_addr *)skb->data; - skb_pull(skb,4); - - ((struct net_device_stats *) dev->priv)->tx_packets++; - ((struct net_device_stats *) dev->priv)->tx_bytes+=skb->len; - - if(aarp_send_ddp(skb->dev, skb, at, NULL) < 0) - dev_kfree_skb(skb); - - return 0; -} - -/* * Get the current statistics. This may be called with the card open or closed. */ static struct net_device_stats *ipddp_get_stats(struct device *dev) @@ -184,19 +161,15 @@ static struct net_device_stats *ipddp_get_stats(struct device *dev) } /* - * Now the packet really wants to go out. On entry skb->data points to the - * ddpehdr we reserved earlier. skb->h.raw will be the higher level header. + * Transmit LLAP/ELAP frame using aarp_send_ddp. */ -static int ipddp_rebuild_header(struct sk_buff *skb) +static int ipddp_xmit(struct sk_buff *skb, struct device *dev) { u32 paddr = ((struct rtable*)skb->dst)->rt_gateway; struct ddpehdr *ddp; - struct at_addr at; struct ipddp_route *rt; struct at_addr *our_addr; - /* Wow! I'll eat my hat if this routine is really called. --ANK */ - /* * Find appropriate route to use, based only on IP number. */ @@ -205,25 +178,21 @@ static int ipddp_rebuild_header(struct sk_buff *skb) if(rt->ip == paddr) break; } - if(rt == NULL) - { - printk("%s: unreachable dst %s\n", cardname, in_ntoa(paddr)); - return -ENETUNREACH; - } + return 0; our_addr = atalk_find_dev_addr(rt->dev); if(ipddp_mode == IPDDP_DECAP) /* * Pull off the excess room that should not be there. - * This is the case for Localtalk, this may not hold - * true for Ethertalk, etc. + * This is due to a hard-header problem. This is the + * quick fix for now though, till it breaks. */ - skb_pull(skb, 31-(sizeof(struct ddpehdr)+1)); + skb_pull(skb, 35-(sizeof(struct ddpehdr)+1)); /* Create the Extended DDP header */ - ddp = (struct ddpehdr *) (skb->data+4); + ddp = (struct ddpehdr *)skb->data; ddp->deh_len = skb->len; ddp->deh_hops = 1; ddp->deh_pad = 0; @@ -231,7 +200,7 @@ static int ipddp_rebuild_header(struct sk_buff *skb) /* * For Localtalk we need aarp_send_ddp to strip the - * Ext DDP header and place a Shrt DDP header on it. + * long DDP header and place a shot DDP header on it. */ if(rt->dev->type == ARPHRD_LOCALTLK) { @@ -248,24 +217,16 @@ static int ipddp_rebuild_header(struct sk_buff *skb) ddp->deh_dport = 72; ddp->deh_sport = 72; - *((__u8 *)(ddp+1)) = 22; /* ddp type = IP */ - *((__u16 *)ddp)=ntohs(*((__u16 *)ddp)); /* fix up length field */ + *((__u8 *)(ddp+1)) = 22; /* ddp type = IP */ + *((__u16 *)ddp)=ntohs(*((__u16 *)ddp)); /* fix up length field */ - /* Hide it at the start of the buffer, we pull it out in ipddp_xmit */ - at = rt->at; - memcpy(skb->data,(void *)&at,sizeof(at)); - - skb->dev = rt->dev; /* set skb->dev to appropriate device */ skb->protocol = htons(ETH_P_ATALK); /* Protocol has changed */ - return 0; -} + ((struct net_device_stats *) dev->priv)->tx_packets++; + ((struct net_device_stats *) dev->priv)->tx_bytes+=skb->len; -static int ipddp_hard_header(struct sk_buff *skb, struct device *dev, - unsigned short type, void *daddr, void *saddr, unsigned len) -{ - /* Push down the header space and the type byte */ - skb_push(skb, sizeof(struct ddpehdr)+1+4); + if(aarp_send_ddp(rt->dev, skb, &rt->at, NULL) < 0) + dev_kfree_skb(skb); return 0; } diff --git a/drivers/net/ipddp.h b/drivers/net/ipddp.h index 31178934b..076373b81 100644 --- a/drivers/net/ipddp.h +++ b/drivers/net/ipddp.h @@ -10,7 +10,6 @@ #define SIOCADDIPDDPRT (SIOCDEVPRIVATE) #define SIOCDELIPDDPRT (SIOCDEVPRIVATE+1) #define SIOCFINDIPDDPRT (SIOCDEVPRIVATE+2) -#define SIOCPRINTIPDDPRT (SIOCDEVPRIVATE+3) struct ipddp_route { diff --git a/drivers/net/ppp.c b/drivers/net/ppp.c index 9380f32f8..de70cd001 100644 --- a/drivers/net/ppp.c +++ b/drivers/net/ppp.c @@ -3,6 +3,7 @@ * Michael Callahan <callahan@maths.ox.ac.uk> * Al Longyear <longyear@netcom.com> * Paul Mackerras <Paul.Mackerras@cs.anu.edu.au> + * Cyrus Durgin <cider@speakeasy.org> (changes for kmod) * * Dynamic PPP devices by Jim Freeman <jfree@caldera.com>. * ppp_tty_receive ``noisy-raise-bug'' fixed by Ove Ewerlid <ewerlid@syscon.uu.se> @@ -50,7 +51,7 @@ /* $Id: ppp.c,v 1.14 1997/11/27 06:04:45 paulus Exp $ */ -#include <linux/config.h> /* for CONFIG_KERNELD */ +#include <linux/config.h> /* for CONFIG_KMOD */ #include <linux/module.h> #include <linux/kernel.h> #include <linux/sched.h> @@ -94,8 +95,8 @@ typedef struct sk_buff sk_buff; #include <linux/if_pppvar.h> #include <linux/ppp-comp.h> -#ifdef CONFIG_KERNELD -#include <linux/kerneld.h> +#ifdef CONFIG_KMOD +#include <linux/kmod.h> #endif #ifndef PPP_IPX @@ -2190,14 +2191,14 @@ ppp_set_compression (struct ppp *ppp, struct ppp_option_data *odp) restore_flags(flags); cp = find_compressor (ccp_option[0]); -#ifdef CONFIG_KERNELD +#ifdef CONFIG_KMOD if (cp == NULL) { char modname[32]; sprintf(modname, "ppp-compress-%d", ccp_option[0]); request_module(modname); cp = find_compressor(ccp_option[0]); } -#endif /* CONFIG_KERNELD */ +#endif /* CONFIG_KMOD */ if (cp == NULL) goto out_no_comp; diff --git a/drivers/net/seeq8005.c b/drivers/net/seeq8005.c index d036fabb3..6dbaec6d2 100644 --- a/drivers/net/seeq8005.c +++ b/drivers/net/seeq8005.c @@ -43,6 +43,7 @@ static const char *version = #include <linux/init.h> #include <asm/system.h> #include <asm/bitops.h> +#include <asm/delay.h> #include <asm/io.h> #include <asm/dma.h> #include <linux/errno.h> diff --git a/drivers/net/slip.c b/drivers/net/slip.c index fe464965e..489f0a39e 100644 --- a/drivers/net/slip.c +++ b/drivers/net/slip.c @@ -181,12 +181,14 @@ sl_alloc_bufs(struct slip *sl, int mtu) sl->xleft = 0; rbuff = xchg(&sl->rbuff, rbuff); xbuff = xchg(&sl->xbuff, xbuff); -#ifdef CONFIG_SLIP_MODE_SLIP6 +#ifdef SL_INCLUDE_CSLIP cbuff = xchg(&sl->cbuff, cbuff); slcomp = xchg(&sl->slcomp, slcomp); +#ifdef CONFIG_SLIP_MODE_SLIP6 sl->xdata = 0; sl->xbits = 0; #endif +#endif end_bh_atomic(); err = 0; @@ -1134,7 +1136,7 @@ slip_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg) it breaks my old poor gcc on alpha --ANK */ tmp = strlen(sl->dev->name) + 1; - if (copy_to_user(arg, sl->dev->name, tmp) < 0) + if (copy_to_user(arg, sl->dev->name, tmp)) return -EFAULT; return 0; diff --git a/drivers/net/tulip.c b/drivers/net/tulip.c index f4484e225..035c47a5c 100644 --- a/drivers/net/tulip.c +++ b/drivers/net/tulip.c @@ -636,7 +636,7 @@ static struct device *tulip_probe1(struct device *dev, int ioaddr, int irq, for (i = 0; i < 5; i++) dev->dev_addr[i] = last_phys_addr[i]; dev->dev_addr[i] = last_phys_addr[i] + 1; -#if defined(__i386) /* This BIOS bug doesn't exist on Alphas. */ +#if defined(__i386__) /* This BIOS bug doesn't exist on Alphas. */ irq = last_irq; #endif } @@ -1094,7 +1094,7 @@ tulip_open(struct device *dev) outl(0x00200000 | 0xE000, ioaddr + CSR0); #elif defined(__powerpc__) outl(0x00200080 | 0x8000, ioaddr + CSR0); -#elif defined(__i386) +#elif defined(__i386__) #if defined(MODULE) /* When a module we don't have 'x86' to check. */ outl(0x00200000 | 0x4800, ioaddr + CSR0); diff --git a/drivers/scsi/ncr53c8xx.c b/drivers/scsi/ncr53c8xx.c index 2dc9c8a66..7956db9d1 100644 --- a/drivers/scsi/ncr53c8xx.c +++ b/drivers/scsi/ncr53c8xx.c @@ -9470,7 +9470,7 @@ printk("ncr53c8xx_pci_init() #1: bus == %d, device_fn == %d\n", bus, device_fn); /* * Try to fix up PCI config according to wished features. */ -#if defined(__i386) && !defined(MODULE) +#if defined(__i386__) && !defined(MODULE) if ((driver_setup.pci_fix_up & 1) && (chip->features & FE_CLSE) && cache_line_size == 0) { #if LINUX_VERSION_CODE < LinuxVersionCode(2,1,75) diff --git a/drivers/scsi/ppa.c b/drivers/scsi/ppa.c index 3c2f083bd..ebdcb560c 100644 --- a/drivers/scsi/ppa.c +++ b/drivers/scsi/ppa.c @@ -50,8 +50,8 @@ NULL, /* cur_cmd */ \ #include "ppa.h" #include <linux/parport.h> -#ifdef CONFIG_KERNELD -#include <linux/kerneld.h> +#ifdef CONFIG_KMOD +#include <linux/kmod.h> #ifndef PARPORT_MODULES #define PARPORT_MODULES "parport_pc" #endif @@ -130,7 +130,7 @@ int ppa_detect(Scsi_Host_Template * host) nhosts = 0; try_again = 0; -#ifdef CONFIG_KERNELD +#ifdef CONFIG_KMOD if (!pb) { request_module(PARPORT_MODULES); pb = parport_enumerate(); diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 4876ad297..90bb6e2d4 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -23,10 +23,13 @@ * Added request_module("scsi_hostadapter") for kerneld: * (Put an "alias scsi_hostadapter your_hostadapter" in /etc/conf.modules) * Bjorn Ekwall <bj0rn@blox.se> + * (changed to kmod) * * Major improvements to the timeout, abort, and reset processing, * as well as performance modifications for large queue depths by * Leonard N. Zubkoff <lnz@dandelion.com> + * + * Converted cli() code to spinlocks, Ingo Molnar */ #include <linux/config.h> @@ -57,8 +60,8 @@ #include "hosts.h" #include "constants.h" -#ifdef CONFIG_KERNELD -#include <linux/kerneld.h> +#ifdef CONFIG_KMOD +#include <linux/kmod.h> #endif #undef USE_STATIC_SCSI_MEMORY @@ -350,8 +353,8 @@ scsi_make_blocked_list(void) * (DB, 4 Feb 1995) */ - save_flags(flags); - cli(); + + spin_lock_irqsave(&io_request_lock, flags); host_active = NULL; for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next) { @@ -383,7 +386,7 @@ scsi_make_blocked_list(void) sh[index]->host_no); } - restore_flags(flags); + spin_unlock_irqrestore(&io_request_lock, flags); } static void scan_scsis_done (Scsi_Cmnd * SCpnt) @@ -1134,21 +1137,21 @@ Scsi_Cmnd * scsi_allocate_device (struct request ** reqp, Scsi_Device * device, SCpnt = found; } - save_flags(flags); - cli(); + __save_flags(flags); + __cli(); /* See if this request has already been queued by an interrupt routine */ if (req && (req->rq_status == RQ_INACTIVE || req->rq_dev != dev)) { - restore_flags(flags); + __restore_flags(flags); return NULL; } if (!SCpnt || SCpnt->request.rq_status != RQ_INACTIVE) /* Might have changed */ { if (wait && SCwait && SCwait->request.rq_status != RQ_INACTIVE){ sleep_on(&device->device_wait); - restore_flags(flags); + __restore_flags(flags); } else { - restore_flags(flags); + __restore_flags(flags); if (!wait) return NULL; if (!SCwait) { printk("Attempt to allocate device channel %d," @@ -1198,7 +1201,7 @@ Scsi_Cmnd * scsi_allocate_device (struct request ** reqp, Scsi_Device * device, * to complete */ } atomic_inc(&SCpnt->host->host_active); - restore_flags(flags); + __restore_flags(flags); SCSI_LOG_MLQUEUE(5, printk("Activating command for device %d (%d)\n", SCpnt->target, atomic_read(&SCpnt->host->host_active))); @@ -1295,8 +1298,7 @@ inline int internal_cmnd (Scsi_Cmnd * SCpnt) host = SCpnt->host; - save_flags(flags); - cli(); + spin_lock_irqsave(&io_request_lock, flags); /* Assign a unique nonzero serial_number. */ if (++serial_number == 0) serial_number = 1; SCpnt->serial_number = serial_number; @@ -1306,6 +1308,8 @@ inline int internal_cmnd (Scsi_Cmnd * SCpnt) * we can avoid the drive not being ready. */ timeout = host->last_reset + MIN_RESET_DELAY; + spin_unlock(&io_request_lock); + if (jiffies < timeout) { int ticks_remaining = timeout - jiffies; /* @@ -1317,11 +1321,11 @@ inline int internal_cmnd (Scsi_Cmnd * SCpnt) * interrupt handler (assuming there is one irq-level per * host). */ - sti(); + __sti(); while (--ticks_remaining >= 0) udelay(1000000/HZ); host->last_reset = jiffies - MIN_RESET_DELAY; } - restore_flags(flags); + __restore_flags(flags); /* this possibly puts us back into __cli() */ if( host->hostt->use_new_eh_code ) { @@ -1452,21 +1456,20 @@ SCSI_LOG_MLQUEUE(4, * ourselves. */ - save_flags(flags); - cli(); + spin_lock_irqsave(&io_request_lock, flags); SCpnt->pid = scsi_pid++; while (SCSI_BLOCK((Scsi_Device *) NULL, host)) { - restore_flags(flags); + spin_unlock_irqrestore(&io_request_lock, flags); SCSI_SLEEP(&host->host_wait, SCSI_BLOCK((Scsi_Device *) NULL, host)); - cli(); + spin_lock_irqsave(&io_request_lock, flags); } if (host->block) host_active = host; host->host_busy++; device->device_busy++; - restore_flags(flags); + spin_unlock_irqrestore(&io_request_lock, flags); /* * Our own function scsi_done (which marks the host as not busy, disables @@ -1820,8 +1823,7 @@ void *scsi_malloc(unsigned int len) if(len % SECTOR_SIZE != 0 || len > PAGE_SIZE) return NULL; - save_flags(flags); - cli(); + spin_lock_irqsave(&io_request_lock, flags); nbits = len >> 9; mask = (1 << nbits) - 1; @@ -1829,7 +1831,7 @@ void *scsi_malloc(unsigned int len) for(j=0; j<=SECTORS_PER_PAGE - nbits; j++){ if ((dma_malloc_freelist[i] & (mask << j)) == 0){ dma_malloc_freelist[i] |= (mask << j); - restore_flags(flags); + spin_unlock_irqrestore(&io_request_lock, flags); scsi_dma_free_sectors -= nbits; #ifdef DEBUG SCSI_LOG_MLQUEUE(3,printk("SMalloc: %d %p [From:%p]\n",len, dma_malloc_pages[i] + (j << 9))); @@ -1838,7 +1840,7 @@ void *scsi_malloc(unsigned int len) return (void *) ((unsigned long) dma_malloc_pages[i] + (j << 9)); } } - restore_flags(flags); + spin_unlock_irqrestore(&io_request_lock, flags); return NULL; /* Nope. No more */ } @@ -1872,19 +1874,20 @@ int scsi_free(void *obj, unsigned int len) if ((mask << sector) >= (1 << SECTORS_PER_PAGE)) panic ("scsi_free:Bad memory alignment"); - save_flags(flags); - cli(); + spin_lock_irqsave(&io_request_lock, flags); if((dma_malloc_freelist[page] & (mask << sector)) != (mask<<sector)){ + spin_unlock_irqrestore(&io_request_lock, flags); #ifdef DEBUG printk("scsi_free(obj=%p, len=%d) called from %08lx\n", obj, len, ret); #endif panic("scsi_free:Trying to free unused memory"); + spin_lock_irqsave(&io_request_lock, flags); } scsi_dma_free_sectors += nbits; dma_malloc_freelist[page] &= ~(mask << sector); - restore_flags(flags); + spin_unlock_irqrestore(&io_request_lock, flags); return 0; } } @@ -2548,8 +2551,7 @@ static void resize_dma_pool(void) /* When we dick with the actual DMA list, we need to * protect things */ - save_flags(flags); - cli(); + spin_lock_irqsave(&io_request_lock, flags); if (dma_malloc_freelist) { size = (dma_sectors / SECTORS_PER_PAGE)*sizeof(FreeSectorBitmap); @@ -2569,7 +2571,7 @@ static void resize_dma_pool(void) dma_malloc_pages = new_dma_malloc_pages; dma_sectors = new_dma_sectors; scsi_need_isa_buffer = new_need_isa_buffer; - restore_flags(flags); + spin_unlock_irqrestore(&io_request_lock, flags); #ifdef DEBUG_INIT printk("resize_dma_pool: dma free sectors = %d\n", scsi_dma_free_sectors); @@ -2808,11 +2810,10 @@ static void scsi_unregister_host(Scsi_Host_Template * tpnt) { online_status = SDpnt->online; SDpnt->online = FALSE; - save_flags(flags); - cli(); + spin_lock_irqsave(&io_request_lock, flags); if(SCpnt->request.rq_status != RQ_INACTIVE) { - restore_flags(flags); + spin_unlock_irqrestore(&io_request_lock, flags); printk("SCSI device not inactive - state=%d, id=%d\n", SCpnt->request.rq_status, SCpnt->target); for(SDpnt1 = shpnt->host_queue; SDpnt1; @@ -2833,7 +2834,7 @@ static void scsi_unregister_host(Scsi_Host_Template * tpnt) */ SCpnt->state = SCSI_STATE_DISCONNECTING; SCpnt->request.rq_status = RQ_SCSI_DISCONNECTING; /* Mark as busy */ - restore_flags(flags); + spin_unlock_irqrestore(&io_request_lock, flags); } } } @@ -3118,7 +3119,7 @@ int scsi_register_module(int module_type, void * ptr) /* Load upper level device handler of some kind */ case MODULE_SCSI_DEV: -#ifdef CONFIG_KERNELD +#ifdef CONFIG_KMOD if (scsi_hosts == NULL) request_module("scsi_hostadapter"); #endif diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index f5e19c547..70adb0040 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -8,7 +8,6 @@ * */ -#include <linux/config.h> #define __NO_VERSION__ #include <linux/module.h> @@ -38,10 +37,6 @@ #define SHUTDOWN_SIGS (sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM)) -#ifdef CONFIG_KERNELD -#include <linux/kerneld.h> -#endif - #ifdef DEBUG #define SENSE_TIMEOUT SCSI_TIMEOUT #define ABORT_TIMEOUT SCSI_TIMEOUT diff --git a/drivers/scsi/scsi_obsolete.c b/drivers/scsi/scsi_obsolete.c index 077b73063..daede4a56 100644 --- a/drivers/scsi/scsi_obsolete.c +++ b/drivers/scsi/scsi_obsolete.c @@ -20,10 +20,6 @@ * Native multichannel, wide scsi, /proc/scsi and hot plugging * support added by Michael Neuffer <mike@i-connect.net> * - * Added request_module("scsi_hostadapter") for kerneld: - * (Put an "alias scsi_hostadapter your_hostadapter" in /etc/conf.modules) - * Bjorn Ekwall <bj0rn@blox.se> - * * Major improvements to the timeout, abort, and reset processing, * as well as performance modifications for large queue depths by * Leonard N. Zubkoff <lnz@dandelion.com> @@ -47,7 +43,6 @@ * driver uses the new code this *ENTIRE* file will be nuked. */ -#include <linux/config.h> /* for CONFIG_KERNELD */ #define __NO_VERSION__ #include <linux/module.h> @@ -70,10 +65,6 @@ #include "hosts.h" #include "constants.h" -#ifdef CONFIG_KERNELD -#include <linux/kerneld.h> -#endif - #undef USE_STATIC_SCSI_MEMORY /* diff --git a/drivers/scsi/scsi_queue.c b/drivers/scsi/scsi_queue.c index b9e2a4feb..3c29353f9 100644 --- a/drivers/scsi/scsi_queue.c +++ b/drivers/scsi/scsi_queue.c @@ -10,7 +10,6 @@ * we attempt to remove commands from the queue and retry them. */ -#include <linux/config.h> #define __NO_VERSION__ #include <linux/module.h> @@ -38,10 +37,6 @@ #include "hosts.h" #include "constants.h" -#ifdef CONFIG_KERNELD -#include <linux/kerneld.h> -#endif - /* * TODO: * 1) Prevent multiple traversals of list to look for commands to diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index c29194b70..5f0668eb6 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -1598,15 +1598,14 @@ int revalidate_scsidisk(kdev_t dev, int maxusage){ target = DEVICE_NR(dev); gdev = &GENDISK_STRUCT; - save_flags(flags); - cli(); + spin_lock_irqsave(&io_request_lock, flags); if (DEVICE_BUSY || USAGE > maxusage) { - restore_flags(flags); + spin_unlock_irqrestore(&io_request_lock, flags); printk("Device busy for revalidation (usage=%d)\n", USAGE); return -EBUSY; } DEVICE_BUSY = 1; - restore_flags(flags); + spin_unlock_irqrestore(&io_request_lock, flags); max_p = gdev->max_p; start = target << gdev->minor_shift; diff --git a/drivers/scsi/wd7000.c b/drivers/scsi/wd7000.c index f8c899535..1b915e3ed 100644 --- a/drivers/scsi/wd7000.c +++ b/drivers/scsi/wd7000.c @@ -127,6 +127,19 @@ * * Thanks to Roger Scott for driver debugging. * + * 06/07/1997 + * + * Added support for /proc file system (/proc/scsi/wd7000/[0...] files). + * Now, driver can handle hard disks with capacity >1GB. + * + * 01/15/1998 + * + * Added support for BUS_ON and BUS_OFF parameters in config line. + * Miscellaneous cleanup. + * + * 03/01/1998 + * + * WD7000 driver now work on kernels >= 2.1.x */ #ifdef MODULE @@ -143,24 +156,29 @@ #include <asm/system.h> #include <asm/dma.h> #include <asm/io.h> -#include <asm/irq.h> #include <linux/ioport.h> #include <linux/proc_fs.h> #include <linux/blk.h> +#include <linux/version.h> #include "scsi.h" #include "hosts.h" #include "sd.h" +#include <scsi/scsicam.h> #define ANY2SCSI_INLINE /* undef this to use old macros */ -#undef DEBUG +#undef WD7000_DEBUG /* general debug */ #include "wd7000.h" +#include <linux/stat.h> -#include<linux/stat.h> -struct proc_dir_entry proc_scsi_wd7000 = { - PROC_SCSI_7000FASST, 6, "wd7000", - S_IFDIR | S_IRUGO | S_IXUGO, 2 +struct proc_dir_entry proc_scsi_wd7000 = +{ + PROC_SCSI_7000FASST, + 6, + "wd7000", + S_IFDIR | S_IRUGO | S_IXUGO, + 2 }; @@ -186,52 +204,48 @@ struct proc_dir_entry proc_scsi_wd7000 = { * WD7000-specific mailbox structure * */ -typedef volatile struct mailbox{ - unchar status; - unchar scbptr[3]; /* SCSI-style - MSB first (big endian) */ +typedef volatile struct mailbox { + unchar status; + unchar scbptr[3]; /* SCSI-style - MSB first (big endian) */ } Mailbox; /* * This structure should contain all per-adapter global data. I.e., any * new global per-adapter data should put in here. - * */ typedef struct adapter { - struct Scsi_Host *sh; /* Pointer to Scsi_Host structure */ - int iobase; /* This adapter's I/O base address */ - int irq; /* This adapter's IRQ level */ - int dma; /* This adapter's DMA channel */ - struct { /* This adapter's mailboxes */ - Mailbox ogmb[OGMB_CNT]; /* Outgoing mailboxes */ - Mailbox icmb[ICMB_CNT]; /* Incoming mailboxes */ - } mb; - int next_ogmb; /* to reduce contention at mailboxes */ - unchar control; /* shadows CONTROL port value */ - unchar rev1, rev2; /* filled in by wd7000_revision */ + struct Scsi_Host *sh; /* Pointer to Scsi_Host structure */ + int iobase; /* This adapter's I/O base address */ + int irq; /* This adapter's IRQ level */ + int dma; /* This adapter's DMA channel */ + int int_counter; /* This adapter's interrupt counter */ + int bus_on; /* This adapter's BUS_ON time */ + int bus_off; /* This adapter's BUS_OFF time */ + struct { /* This adapter's mailboxes */ + Mailbox ogmb[OGMB_CNT]; /* Outgoing mailboxes */ + Mailbox icmb[ICMB_CNT]; /* Incoming mailboxes */ + } mb; + int next_ogmb; /* to reduce contention at mailboxes */ + unchar control; /* shadows CONTROL port value */ + unchar rev1, rev2; /* filled in by wd7000_revision */ } Adapter; /* - * The following is set up by wd7000_detect, and used thereafter by - * wd7000_intr_handle to map the irq level to the corresponding Adapter. - * Note that if SA_INTERRUPT is not used, wd7000_intr_handle must be - * changed to pick up the IRQ level correctly. - */ -static Adapter *irq2host[NR_IRQS] = {NULL}; - -/* * (linear) base address for ROM BIOS */ -static const long wd7000_biosaddr[] = { - 0xc0000, 0xc2000, 0xc4000, 0xc6000, 0xc8000, 0xca000, 0xcc000, 0xce000, - 0xd0000, 0xd2000, 0xd4000, 0xd6000, 0xd8000, 0xda000, 0xdc000, 0xde000 +static const long wd7000_biosaddr[] = +{ + 0xc0000, 0xc2000, 0xc4000, 0xc6000, 0xc8000, 0xca000, 0xcc000, 0xce000, + 0xd0000, 0xd2000, 0xd4000, 0xd6000, 0xd8000, 0xda000, 0xdc000, 0xde000 }; #define NUM_ADDRS (sizeof(wd7000_biosaddr)/sizeof(long)) -static const unsigned short wd7000_iobase[] = { - 0x0300, 0x0308, 0x0310, 0x0318, 0x0320, 0x0328, 0x0330, 0x0338, - 0x0340, 0x0348, 0x0350, 0x0358, 0x0360, 0x0368, 0x0370, 0x0378, - 0x0380, 0x0388, 0x0390, 0x0398, 0x03a0, 0x03a8, 0x03b0, 0x03b8, - 0x03c0, 0x03c8, 0x03d0, 0x03d8, 0x03e0, 0x03e8, 0x03f0, 0x03f8 +static const unsigned short wd7000_iobase[] = +{ + 0x0300, 0x0308, 0x0310, 0x0318, 0x0320, 0x0328, 0x0330, 0x0338, + 0x0340, 0x0348, 0x0350, 0x0358, 0x0360, 0x0368, 0x0370, 0x0378, + 0x0380, 0x0388, 0x0390, 0x0398, 0x03a0, 0x03a8, 0x03b0, 0x03b8, + 0x03c0, 0x03c8, 0x03d0, 0x03d8, 0x03e0, 0x03e8, 0x03f0, 0x03f8 }; #define NUM_IOPORTS (sizeof(wd7000_iobase)/sizeof(unsigned short)) @@ -240,24 +254,48 @@ static const short wd7000_irq[] = { 3, 4, 5, 7, 9, 10, 11, 12, 14, 15 }; static const short wd7000_dma[] = { 5, 6, 7 }; #define NUM_DMAS (sizeof(wd7000_dma)/sizeof(short)) - + +/* + * possible irq range + */ +#define IRQ_MIN 3 +#define IRQ_MAX 15 +#define IRQS (IRQ_MAX - IRQ_MIN + 1) + +/* + * The following is set up by wd7000_detect, and used thereafter by + * wd7000_intr_handle to map the irq level to the corresponding Adapter. + * Note that if SA_INTERRUPT is not used, wd7000_intr_handle must be + * changed to pick up the IRQ level correctly. + */ +static struct Scsi_Host *wd7000_host[IRQS]; + +#define BUS_ON 64 /* x 125ns = 8000ns (BIOS default) */ +#define BUS_OFF 15 /* x 125ns = 1875ns (BIOS default) */ + /* * Standard Adapter Configurations - used by wd7000_detect */ typedef struct { - int irq; /* IRQ level */ - int dma; /* DMA channel */ - unsigned iobase; /* I/O base address */ + short irq; /* IRQ level */ + short dma; /* DMA channel */ + unsigned iobase; /* I/O base address */ + short bus_on; /* Time that WD7000 spends on the AT-bus when */ + /* transferring data. BIOS default is 8000ns. */ + short bus_off; /* Time that WD7000 spends OFF THE BUS after */ + /* while it is transferring data. */ + /* BIOS default is 1875ns */ } Config; /* * Add here your configuration... */ -static const Config configs[] = { - { 15, 6, 0x350 }, /* defaults for single adapter */ - { 11, 5, 0x320 }, /* defaults for second adapter */ - { 7, 6, 0x350 }, /* My configuration (Zaga) */ - { -1, -1, 0x0 } /* Empty slot */ +static Config configs[] = +{ + { 15, 6, 0x350, BUS_ON, BUS_OFF }, /* defaults for single adapter */ + { 11, 5, 0x320, BUS_ON, BUS_OFF }, /* defaults for second adapter */ + { 7, 6, 0x350, BUS_ON, BUS_OFF }, /* My configuration (Zaga) */ + { -1, -1, 0x0, BUS_ON, BUS_OFF } /* Empty slot */ }; #define NUM_CONFIGS (sizeof(configs)/sizeof(Config)) @@ -267,13 +305,14 @@ static const Config configs[] = { * added for the Future Domain version. */ typedef struct signature { - const void *sig; /* String to look for */ - unsigned ofs; /* offset from BIOS base address */ - unsigned len; /* length of string */ + const char *sig; /* String to look for */ + unsigned long ofs; /* offset from BIOS base address */ + unsigned len; /* length of string */ } Signature; -static const Signature signatures[] = { - { "SSTBIOS", 0x0000d, 7 } /* "SSTBIOS" @ offset 0x0000d */ +static const Signature signatures[] = +{ + {"SSTBIOS", 0x0000d, 7} /* "SSTBIOS" @ offset 0x0000d */ }; #define NUM_SIGNATURES (sizeof(signatures)/sizeof(Signature)) @@ -282,22 +321,23 @@ static const Signature signatures[] = { * I/O Port Offsets and Bit Definitions * 4 addresses are used. Those not defined here are reserved. */ -#define ASC_STAT 0 /* Status, Read */ -#define ASC_COMMAND 0 /* Command, Write */ +#define ASC_STAT 0 /* Status, Read */ +#define ASC_COMMAND 0 /* Command, Write */ #define ASC_INTR_STAT 1 /* Interrupt Status, Read */ -#define ASC_INTR_ACK 1 /* Acknowledge, Write */ -#define ASC_CONTROL 2 /* Control, Write */ +#define ASC_INTR_ACK 1 /* Acknowledge, Write */ +#define ASC_CONTROL 2 /* Control, Write */ /* * ASC Status Port */ -#define INT_IM 0x80 /* Interrupt Image Flag */ -#define CMD_RDY 0x40 /* Command Port Ready */ -#define CMD_REJ 0x20 /* Command Port Byte Rejected */ -#define ASC_INIT 0x10 /* ASC Initialized Flag */ +#define INT_IM 0x80 /* Interrupt Image Flag */ +#define CMD_RDY 0x40 /* Command Port Ready */ +#define CMD_REJ 0x20 /* Command Port Byte Rejected */ +#define ASC_INIT 0x10 /* ASC Initialized Flag */ #define ASC_STATMASK 0xf0 /* The lower 4 Bytes are reserved */ -/* COMMAND opcodes +/* + * COMMAND opcodes * * Unfortunately, I have no idea how to properly use some of these commands, * as the OEM manual does not make it clear. I have not been able to use @@ -305,39 +345,38 @@ static const Signature signatures[] = { * discernible effect whatsoever. I think they may be related to certain * ICB commands, but again, the OEM manual doesn't make that clear. */ -#define NO_OP 0 /* NO-OP toggles CMD_RDY bit in ASC_STAT */ -#define INITIALIZATION 1 /* initialization (10 bytes) */ -#define DISABLE_UNS_INTR 2 /* disable unsolicited interrupts */ -#define ENABLE_UNS_INTR 3 /* enable unsolicited interrupts */ -#define INTR_ON_FREE_OGMB 4 /* interrupt on free OGMB */ -#define SOFT_RESET 5 /* SCSI bus soft reset */ -#define HARD_RESET_ACK 6 /* SCSI bus hard reset acknowledge */ -#define START_OGMB 0x80 /* start command in OGMB (n) */ +#define NO_OP 0 /* NO-OP toggles CMD_RDY bit in ASC_STAT */ +#define INITIALIZATION 1 /* initialization (10 bytes) */ +#define DISABLE_UNS_INTR 2 /* disable unsolicited interrupts */ +#define ENABLE_UNS_INTR 3 /* enable unsolicited interrupts */ +#define INTR_ON_FREE_OGMB 4 /* interrupt on free OGMB */ +#define SOFT_RESET 5 /* SCSI bus soft reset */ +#define HARD_RESET_ACK 6 /* SCSI bus hard reset acknowledge */ +#define START_OGMB 0x80 /* start command in OGMB (n) */ #define SCAN_OGMBS 0xc0 /* start multiple commands, signature (n) */ - /* where (n) = lower 6 bits */ -/* For INITIALIZATION: + /* where (n) = lower 6 bits */ +/* + * For INITIALIZATION: */ typedef struct initCmd { - unchar op; /* command opcode (= 1) */ - unchar ID; /* Adapter's SCSI ID */ - unchar bus_on; /* Bus on time, x 125ns (see below) */ - unchar bus_off; /* Bus off time, "" "" */ - unchar rsvd; /* Reserved */ - unchar mailboxes[3]; /* Address of Mailboxes, MSB first */ - unchar ogmbs; /* Number of outgoing MBs, max 64, 0,1 = 1 */ - unchar icmbs; /* Number of incoming MBs, "" "" */ + unchar op; /* command opcode (= 1) */ + unchar ID; /* Adapter's SCSI ID */ + unchar bus_on; /* Bus on time, x 125ns (see below) */ + unchar bus_off; /* Bus off time, "" "" */ + unchar rsvd; /* Reserved */ + unchar mailboxes[3]; /* Address of Mailboxes, MSB first */ + unchar ogmbs; /* Number of outgoing MBs, max 64, 0,1 = 1 */ + unchar icmbs; /* Number of incoming MBs, "" "" */ } InitCmd; -#define BUS_ON 64 /* x 125ns = 8000ns (BIOS default) */ -#define BUS_OFF 15 /* x 125ns = 1875ns (BIOS default) */ - -/* Interrupt Status Port - also returns diagnostic codes at ASC reset +/* + * Interrupt Status Port - also returns diagnostic codes at ASC reset * * if msb is zero, the lower bits are diagnostic status * Diagnostics: - * 01 No diagnostic error occurred - * 02 RAM failure - * 03 FIFO R/W failed + * 01 No diagnostic error occurred + * 02 RAM failure + * 03 FIFO R/W failed * 04 SBIC register read/write failed * 05 Initialization D-FF failed * 06 Host IRQ D-FF failed @@ -346,19 +385,20 @@ typedef struct initCmd { * 10NNNNNN outgoing mailbox NNNNNN is free * 11NNNNNN incoming mailbox NNNNNN needs service */ -#define MB_INTR 0xC0 /* Mailbox Service possible/required */ -#define IMB_INTR 0x40 /* 1 Incoming / 0 Outgoing */ -#define MB_MASK 0x3f /* mask for mailbox number */ +#define MB_INTR 0xC0 /* Mailbox Service possible/required */ +#define IMB_INTR 0x40 /* 1 Incoming / 0 Outgoing */ +#define MB_MASK 0x3f /* mask for mailbox number */ -/* CONTROL port bits +/* + * CONTROL port bits */ -#define INT_EN 0x08 /* Interrupt Enable */ -#define DMA_EN 0x04 /* DMA Enable */ -#define SCSI_RES 0x02 /* SCSI Reset */ -#define ASC_RES 0x01 /* ASC Reset */ +#define INT_EN 0x08 /* Interrupt Enable */ +#define DMA_EN 0x04 /* DMA Enable */ +#define SCSI_RES 0x02 /* SCSI Reset */ +#define ASC_RES 0x01 /* ASC Reset */ /* - * Driver data structures: + * Driver data structures: * - mb and scbs are required for interfacing with the host adapter. * An SCB has extra fields not visible to the adapter; mb's * _cannot_ do this, since the adapter assumes they are contiguous in @@ -387,28 +427,28 @@ typedef struct initCmd { */ typedef struct sgb { unchar len[3]; - unchar ptr[3]; /* Also SCSI-style - MSB first */ + unchar ptr[3]; /* Also SCSI-style - MSB first */ } Sgb; -typedef struct scb { /* Command Control Block 5.4.1 */ - unchar op; /* Command Control Block Operation Code */ - unchar idlun; /* op=0,2:Target Id, op=1:Initiator Id */ - /* Outbound data transfer, length is checked*/ - /* Inbound data transfer, length is checked */ - /* Logical Unit Number */ - unchar cdb[12]; /* SCSI Command Block */ - volatile unchar status; /* SCSI Return Status */ - volatile unchar vue; /* Vendor Unique Error Code */ - unchar maxlen[3]; /* Maximum Data Transfer Length */ - unchar dataptr[3]; /* SCSI Data Block Pointer */ - unchar linkptr[3]; /* Next Command Link Pointer */ - unchar direc; /* Transfer Direction */ - unchar reserved2[6]; /* SCSI Command Descriptor Block */ - /* end of hardware SCB */ - Scsi_Cmnd *SCpnt; /* Scsi_Cmnd using this SCB */ - Sgb sgb[WD7000_SG]; /* Scatter/gather list for this SCB */ - Adapter *host; /* host adapter */ - struct scb *next; /* for lists of scbs */ +typedef struct scb { /* Command Control Block 5.4.1 */ + unchar op; /* Command Control Block Operation Code */ + unchar idlun; /* op=0,2:Target Id, op=1:Initiator Id */ + /* Outbound data transfer, length is checked */ + /* Inbound data transfer, length is checked */ + /* Logical Unit Number */ + unchar cdb[12]; /* SCSI Command Block */ + volatile unchar status; /* SCSI Return Status */ + volatile unchar vue; /* Vendor Unique Error Code */ + unchar maxlen[3]; /* Maximum Data Transfer Length */ + unchar dataptr[3]; /* SCSI Data Block Pointer */ + unchar linkptr[3]; /* Next Command Link Pointer */ + unchar direc; /* Transfer Direction */ + unchar reserved2[6]; /* SCSI Command Descriptor Block */ + /* end of hardware SCB */ + Scsi_Cmnd *SCpnt; /* Scsi_Cmnd using this SCB */ + Sgb sgb[WD7000_SG]; /* Scatter/gather list for this SCB */ + Adapter *host; /* host adapter */ + struct scb *next; /* for lists of scbs */ } Scb; /* @@ -422,110 +462,110 @@ typedef struct scb { /* Command Control Block 5.4.1 */ * (notably, get/set unsolicited interrupt status) in my copy of the OEM * manual, and others are ambiguous/hard to follow. */ -#define ICB_OP_MASK 0x80 /* distinguishes scbs from icbs */ -#define ICB_OP_OPEN_RBUF 0x80 /* open receive buffer */ -#define ICB_OP_RECV_CMD 0x81 /* receive command from initiator */ -#define ICB_OP_RECV_DATA 0x82 /* receive data from initiator */ -#define ICB_OP_RECV_SDATA 0x83 /* receive data with status from init. */ -#define ICB_OP_SEND_DATA 0x84 /* send data with status to initiator */ -#define ICB_OP_SEND_STAT 0x86 /* send command status to initiator */ - /* 0x87 is reserved */ -#define ICB_OP_READ_INIT 0x88 /* read initialization bytes */ -#define ICB_OP_READ_ID 0x89 /* read adapter's SCSI ID */ -#define ICB_OP_SET_UMASK 0x8A /* set unsolicited interrupt mask */ -#define ICB_OP_GET_UMASK 0x8B /* read unsolicited interrupt mask */ -#define ICB_OP_GET_REVISION 0x8C /* read firmware revision level */ -#define ICB_OP_DIAGNOSTICS 0x8D /* execute diagnostics */ -#define ICB_OP_SET_EPARMS 0x8E /* set execution parameters */ -#define ICB_OP_GET_EPARMS 0x8F /* read execution parameters */ +#define ICB_OP_MASK 0x80 /* distinguishes scbs from icbs */ +#define ICB_OP_OPEN_RBUF 0x80 /* open receive buffer */ +#define ICB_OP_RECV_CMD 0x81 /* receive command from initiator */ +#define ICB_OP_RECV_DATA 0x82 /* receive data from initiator */ +#define ICB_OP_RECV_SDATA 0x83 /* receive data with status from init. */ +#define ICB_OP_SEND_DATA 0x84 /* send data with status to initiator */ +#define ICB_OP_SEND_STAT 0x86 /* send command status to initiator */ + /* 0x87 is reserved */ +#define ICB_OP_READ_INIT 0x88 /* read initialization bytes */ +#define ICB_OP_READ_ID 0x89 /* read adapter's SCSI ID */ +#define ICB_OP_SET_UMASK 0x8A /* set unsolicited interrupt mask */ +#define ICB_OP_GET_UMASK 0x8B /* read unsolicited interrupt mask */ +#define ICB_OP_GET_REVISION 0x8C /* read firmware revision level */ +#define ICB_OP_DIAGNOSTICS 0x8D /* execute diagnostics */ +#define ICB_OP_SET_EPARMS 0x8E /* set execution parameters */ +#define ICB_OP_GET_EPARMS 0x8F /* read execution parameters */ typedef struct icbRecvCmd { - unchar op; - unchar IDlun; /* Initiator SCSI ID/lun */ - unchar len[3]; /* command buffer length */ - unchar ptr[3]; /* command buffer address */ - unchar rsvd[7]; /* reserved */ - volatile unchar vue; /* vendor-unique error code */ - volatile unchar status; /* returned (icmb) status */ - volatile unchar phase; /* used by interrupt handler */ + unchar op; + unchar IDlun; /* Initiator SCSI ID/lun */ + unchar len[3]; /* command buffer length */ + unchar ptr[3]; /* command buffer address */ + unchar rsvd[7]; /* reserved */ + volatile unchar vue; /* vendor-unique error code */ + volatile unchar status; /* returned (icmb) status */ + volatile unchar phase; /* used by interrupt handler */ } IcbRecvCmd; typedef struct icbSendStat { - unchar op; - unchar IDlun; /* Target SCSI ID/lun */ - unchar stat; /* (outgoing) completion status byte 1 */ - unchar rsvd[12]; /* reserved */ - volatile unchar vue; /* vendor-unique error code */ - volatile unchar status; /* returned (icmb) status */ - volatile unchar phase; /* used by interrupt handler */ + unchar op; + unchar IDlun; /* Target SCSI ID/lun */ + unchar stat; /* (outgoing) completion status byte 1 */ + unchar rsvd[12]; /* reserved */ + volatile unchar vue; /* vendor-unique error code */ + volatile unchar status; /* returned (icmb) status */ + volatile unchar phase; /* used by interrupt handler */ } IcbSendStat; typedef struct icbRevLvl { - unchar op; - volatile unchar primary; /* primary revision level (returned) */ - volatile unchar secondary; /* secondary revision level (returned) */ - unchar rsvd[12]; /* reserved */ - volatile unchar vue; /* vendor-unique error code */ - volatile unchar status; /* returned (icmb) status */ - volatile unchar phase; /* used by interrupt handler */ + unchar op; + volatile unchar primary; /* primary revision level (returned) */ + volatile unchar secondary; /* secondary revision level (returned) */ + unchar rsvd[12]; /* reserved */ + volatile unchar vue; /* vendor-unique error code */ + volatile unchar status; /* returned (icmb) status */ + volatile unchar phase; /* used by interrupt handler */ } IcbRevLvl; -typedef struct icbUnsMask { /* I'm totally guessing here */ - unchar op; - volatile unchar mask[14]; /* mask bits */ -#if 0 - unchar rsvd[12]; /* reserved */ +typedef struct icbUnsMask { /* I'm totally guessing here */ + unchar op; + volatile unchar mask[14]; /* mask bits */ +#ifdef 0 + unchar rsvd[12]; /* reserved */ #endif - volatile unchar vue; /* vendor-unique error code */ - volatile unchar status; /* returned (icmb) status */ - volatile unchar phase; /* used by interrupt handler */ + volatile unchar vue; /* vendor-unique error code */ + volatile unchar status; /* returned (icmb) status */ + volatile unchar phase; /* used by interrupt handler */ } IcbUnsMask; typedef struct icbDiag { - unchar op; - unchar type; /* diagnostics type code (0-3) */ - unchar len[3]; /* buffer length */ - unchar ptr[3]; /* buffer address */ - unchar rsvd[7]; /* reserved */ - volatile unchar vue; /* vendor-unique error code */ - volatile unchar status; /* returned (icmb) status */ - volatile unchar phase; /* used by interrupt handler */ + unchar op; + unchar type; /* diagnostics type code (0-3) */ + unchar len[3]; /* buffer length */ + unchar ptr[3]; /* buffer address */ + unchar rsvd[7]; /* reserved */ + volatile unchar vue; /* vendor-unique error code */ + volatile unchar status; /* returned (icmb) status */ + volatile unchar phase; /* used by interrupt handler */ } IcbDiag; -#define ICB_DIAG_POWERUP 0 /* Power-up diags only */ -#define ICB_DIAG_WALKING 1 /* walking 1's pattern */ -#define ICB_DIAG_DMA 2 /* DMA - system memory diags */ -#define ICB_DIAG_FULL 3 /* do both 1 & 2 */ +#define ICB_DIAG_POWERUP 0 /* Power-up diags only */ +#define ICB_DIAG_WALKING 1 /* walking 1's pattern */ +#define ICB_DIAG_DMA 2 /* DMA - system memory diags */ +#define ICB_DIAG_FULL 3 /* do both 1 & 2 */ typedef struct icbParms { - unchar op; - unchar rsvd1; /* reserved */ - unchar len[3]; /* parms buffer length */ - unchar ptr[3]; /* parms buffer address */ - unchar idx[2]; /* index (MSB-LSB) */ - unchar rsvd2[5]; /* reserved */ - volatile unchar vue; /* vendor-unique error code */ - volatile unchar status; /* returned (icmb) status */ - volatile unchar phase; /* used by interrupt handler */ + unchar op; + unchar rsvd1; /* reserved */ + unchar len[3]; /* parms buffer length */ + unchar ptr[3]; /* parms buffer address */ + unchar idx[2]; /* index (MSB-LSB) */ + unchar rsvd2[5]; /* reserved */ + volatile unchar vue; /* vendor-unique error code */ + volatile unchar status; /* returned (icmb) status */ + volatile unchar phase; /* used by interrupt handler */ } IcbParms; typedef struct icbAny { - unchar op; - unchar data[14]; /* format-specific data */ - volatile unchar vue; /* vendor-unique error code */ - volatile unchar status; /* returned (icmb) status */ - volatile unchar phase; /* used by interrupt handler */ + unchar op; + unchar data[14]; /* format-specific data */ + volatile unchar vue; /* vendor-unique error code */ + volatile unchar status; /* returned (icmb) status */ + volatile unchar phase; /* used by interrupt handler */ } IcbAny; typedef union icb { - unchar op; /* ICB opcode */ - IcbRecvCmd recv_cmd; /* format for receive command */ - IcbSendStat send_stat; /* format for send status */ - IcbRevLvl rev_lvl; /* format for get revision level */ - IcbDiag diag; /* format for execute diagnostics */ - IcbParms eparms; /* format for get/set exec parms */ - IcbAny icb; /* generic format */ - unchar data[18]; + unchar op; /* ICB opcode */ + IcbRecvCmd recv_cmd; /* format for receive command */ + IcbSendStat send_stat; /* format for send status */ + IcbRevLvl rev_lvl; /* format for get revision level */ + IcbDiag diag; /* format for execute diagnostics */ + IcbParms eparms; /* format for get/set exec parms */ + IcbAny icb; /* generic format */ + unchar data[18]; } Icb; @@ -536,27 +576,34 @@ typedef union icb { * structure is not part of the Adapter structure. */ static Scb scbs[MAX_SCBS]; -static Scb *scbfree = NULL; /* free list */ -static int freescbs = MAX_SCBS; /* free list counter */ - -/* - * - */ -static short wd7000_setupIRQ[NUM_CONFIGS]; -static short wd7000_setupDMA[NUM_CONFIGS]; -static short wd7000_setupIO[NUM_CONFIGS]; -static short wd7000_card_num = 0; +static Scb *scbfree = NULL; /* free list */ +static int freescbs = MAX_SCBS; /* free list counter */ /* * END of data/declarations - code follows. */ +static void setup_error (char *mesg, int *ints) +{ + if (ints[0] == 3) + printk ("wd7000_setup: \"wd7000=%d,%d,0x%x\" -> %s\n", + ints[1], ints[2], ints[3], mesg); + else if (ints[0] == 4) + printk ("wd7000_setup: \"wd7000=%d,%d,0x%x,%d\" -> %s\n", + ints[1], ints[2], ints[3], ints[4], mesg); + else + printk ("wd7000_setup: \"wd7000=%d,%d,0x%x,%d,%d\" -> %s\n", + ints[1], ints[2], ints[3], ints[4], ints[5], mesg); +} /* * Note: You can now set these options from the kernel's "command line". * The syntax is: * - * wd7000=IRQ,DMA,IO + * wd7000=<IRQ>,<DMA>,<IO>[,<BUS_ON>[,<BUS_OFF>]] + * + * , where BUS_ON and BUS_OFF are in nanoseconds. BIOS default values + * are 8000ns for BUS_ON and 1875ns for BUS_OFF. * eg: * wd7000=7,6,0x350 * @@ -565,82 +612,99 @@ static short wd7000_card_num = 0; */ void wd7000_setup (char *str, int *ints) { + static short wd7000_card_num = 0; short i, j; if (wd7000_card_num >= NUM_CONFIGS) { - printk ("wd7000_setup: Too many \"wd7000=\" configurations in " - "command line!\n"); - - return; + printk ("wd7000_setup: Too many \"wd7000=\" configurations in " + "command line!\n"); + return; } - if (ints[0] != 3) + if ((ints[0] < 3) || (ints[0] > 5)) printk ("wd7000_setup: Error in command line! " - "Usage: wd7000=IRQ,DMA,IO\n"); + "Usage: wd7000=<IRQ>,<DMA>,IO>[,<BUS_ON>[,<BUS_OFF>]]\n"); else { for (i = 0; i < NUM_IRQS; i++) if (ints[1] == wd7000_irq[i]) break; if (i == NUM_IRQS) { - printk ("wd7000_setup: \"wd7000=%d,%d,0x%x\" -> " - "invalid IRQ.\n", ints[1], ints[2], ints[3]); + setup_error ("invalid IRQ.", ints); return; } else - wd7000_setupIRQ[wd7000_card_num] = ints[1]; + configs[wd7000_card_num].irq = ints[1]; for (i = 0; i < NUM_DMAS; i++) if (ints[2] == wd7000_dma[i]) break; if (i == NUM_DMAS) { - printk ("wd7000_setup: \"wd7000=%d,%d,0x%x\" -> " - "invalid DMA channel.\n", ints[1], ints[2], ints[3]); + setup_error ("invalid DMA channel.", ints); return; } else - wd7000_setupDMA[wd7000_card_num] = ints[2]; + configs[wd7000_card_num].dma = ints[2]; for (i = 0; i < NUM_IOPORTS; i++) if (ints[3] == wd7000_iobase[i]) break; if (i == NUM_IOPORTS) { - printk ("wd7000_setup: \"wd7000=%d,%d,0x%x\" -> " - "invalid I/O base address.\n", ints[1], ints[2], ints[3]); + setup_error ("invalid I/O base address.", ints); return; } else - wd7000_setupIO[wd7000_card_num] = ints[3]; + configs[wd7000_card_num].iobase = ints[3]; + + if (ints[0] > 3) { + if ((ints[4] < 500) || (ints[4] > 31875)) { + setup_error ("BUS_ON value is out of range (500 to 31875 nanoseconds)!", + ints); + configs[wd7000_card_num].bus_on = BUS_ON; + } + else + configs[wd7000_card_num].bus_on = ints[4] / 125.0; + } + else + configs[wd7000_card_num].bus_on = BUS_ON; + + if (ints[0] > 4) { + if ((ints[5] < 500) || (ints[5] > 31875)) { + setup_error ("BUS_OFF value is out of range (500 to 31875 nanoseconds)!", + ints); + configs[wd7000_card_num].bus_off = BUS_OFF; + } + else + configs[wd7000_card_num].bus_off = ints[5] / 125.0; + } + else + configs[wd7000_card_num].bus_off = BUS_OFF; if (wd7000_card_num) for (i = 0; i < (wd7000_card_num - 1); i++) - for (j = i + 1; j < wd7000_card_num; j++) - if (wd7000_setupIRQ[i] == wd7000_setupIRQ[j]) { - printk ("wd7000_setup: \"wd7000=%d,%d,0x%x\" -> " - "duplicated IRQ!\n", - ints[1], ints[2], ints[3]); - return; - } - else if (wd7000_setupDMA[i] == wd7000_setupDMA[j]) { - printk ("wd7000_setup: \"wd7000=%d,%d,0x%x\" -> " - "duplicated DMA channel!\n", - ints[1], ints[2], ints[3]); - return; - } - else if (wd7000_setupIO[i] == wd7000_setupIO[j]) { - printk ("wd7000_setup: \"wd7000=%d,%d,0x%x\" -> " - "duplicated I/O base address!\n", - ints[1], ints[2], ints[3]); - return; - } - -#ifdef DEBUG - printk ("wd7000_setup: IRQ=%d, DMA=%d, I/O=0x%x\n", - wd7000_setupIRQ[wd7000_card_num], - wd7000_setupDMA[wd7000_card_num], - wd7000_setupIO[wd7000_card_num]); + for (j = i + 1; j < wd7000_card_num; j++) + if (configs[i].irq == configs[j].irq) { + setup_error ("duplicated IRQ!", ints); + return; + } + else if (configs[i].dma == configs[j].dma) { + setup_error ("duplicated DMA channel!", ints); + return; + } + else if (configs[i].iobase == configs[j].iobase) { + setup_error ("duplicated I/O base address!", ints); + return; + } + +#ifdef WD7000_DEBUG + printk ("wd7000_setup: IRQ=%d, DMA=%d, I/O=0x%x, BUS_ON=%dns, BUS_OFF=%dns\n", + configs[wd7000_card_num].irq, + configs[wd7000_card_num].dma, + configs[wd7000_card_num].iobase, + configs[wd7000_card_num].bus_on * 125, + configs[wd7000_card_num].bus_off * 125); #endif wd7000_card_num++; @@ -650,21 +714,20 @@ void wd7000_setup (char *str, int *ints) #ifdef ANY2SCSI_INLINE /* - Since they're used a lot, I've redone the following from the macros - formerly in wd7000.h, hopefully to speed them up by getting rid of - all the shifting (it may not matter; GCC might have done as well anyway). - - xany2scsi and xscsi2int were not being used, and are no longer defined. - (They were simply 4-byte versions of these routines). -*/ - -typedef union { /* let's cheat... */ - int i; - unchar u[sizeof(int)]; /* the sizeof(int) makes it more portable */ + * Since they're used a lot, I've redone the following from the macros + * formerly in wd7000.h, hopefully to speed them up by getting rid of + * all the shifting (it may not matter; GCC might have done as well anyway). + * + * xany2scsi and xscsi2int were not being used, and are no longer defined. + * (They were simply 4-byte versions of these routines). + */ +typedef union { /* let's cheat... */ + int i; + unchar u[sizeof (int)]; /* the sizeof(int) makes it more portable */ } i_u; -static inline void any2scsi( unchar *scsi, int any ) +static inline void any2scsi (unchar * scsi, int any) { *scsi++ = ((i_u) any).u[2]; *scsi++ = ((i_u) any).u[1]; @@ -672,49 +735,50 @@ static inline void any2scsi( unchar *scsi, int any ) } -static inline int scsi2int( unchar *scsi ) +static inline int scsi2int (unchar * scsi) { i_u result; - result.i = 0; /* clears unused bytes */ - *(result.u+2) = *scsi++; - *(result.u+1) = *scsi++; - *(result.u) = *scsi++; - return result.i; + result.i = 0; /* clears unused bytes */ + result.u[2] = *scsi++; + result.u[1] = *scsi++; + result.u[0] = *scsi++; + + return (result.i); } #else /* - These are the old ones - I've just moved them here... -*/ + * These are the old ones - I've just moved them here... + */ #undef any2scsi -#define any2scsi(up, p) \ -(up)[0] = (((unsigned long)(p)) >> 16); \ -(up)[1] = ((unsigned long)(p)) >> 8; \ -(up)[2] = ((unsigned long)(p)); +#define any2scsi(up, p) (up)[0] = (((unsigned long) (p)) >> 16); \ + (up)[1] = ((unsigned long) (p)) >> 8; \ + (up)[2] = ((unsigned long) (p)); #undef scsi2int -#define scsi2int(up) ( (((unsigned long)*(up)) << 16) + \ - (((unsigned long)(up)[1]) << 8) + ((unsigned long)(up)[2]) ) +#define scsi2int(up) ( (((unsigned long) *(up)) << 16) + \ + (((unsigned long) (up)[1]) << 8) + \ + ((unsigned long) (up)[2]) ) #endif - -static inline void wd7000_enable_intr(Adapter *host) + +static inline void wd7000_enable_intr (Adapter *host) { host->control |= INT_EN; - outb(host->control, host->iobase+ASC_CONTROL); + outb (host->control, host->iobase + ASC_CONTROL); } -static inline void wd7000_enable_dma(Adapter *host) +static inline void wd7000_enable_dma (Adapter *host) { host->control |= DMA_EN; - outb(host->control,host->iobase+ASC_CONTROL); - set_dma_mode(host->dma, DMA_MODE_CASCADE); - enable_dma(host->dma); + outb (host->control, host->iobase + ASC_CONTROL); + set_dma_mode (host->dma, DMA_MODE_CASCADE); + enable_dma (host->dma); } -#define WAITnexttimeout 200 /* 2 seconds */ +#define WAITnexttimeout 200 /* 2 seconds */ static inline short WAIT (unsigned port, unsigned mask, unsigned allof, unsigned noneof) { @@ -722,7 +786,7 @@ static inline short WAIT (unsigned port, unsigned mask, unsigned allof, unsigned register unsigned long WAITtimeout = jiffies + WAITnexttimeout; while (jiffies <= WAITtimeout) { - WAITbits = inb (port) & mask; + WAITbits = inb (port) & mask; if (((WAITbits & allof) == allof) && ((WAITbits & noneof) == 0)) return (0); @@ -732,31 +796,32 @@ static inline short WAIT (unsigned port, unsigned mask, unsigned allof, unsigned } -static inline void delay( unsigned how_long ) +static inline void delay (unsigned how_long) { - register unsigned long time = jiffies + how_long; + register unsigned long time = jiffies + how_long; - while (jiffies < time); + while (jiffies < time); } -static inline int command_out(Adapter *host, unchar *cmd, int len) +static inline int command_out (Adapter * host, unchar * cmd, int len) { - if (! WAIT (host->iobase+ASC_STAT,ASC_STATMASK,CMD_RDY,0)) { - while (len--) { - do { - outb(*cmd, host->iobase+ASC_COMMAND); - WAIT(host->iobase+ASC_STAT, ASC_STATMASK, CMD_RDY, 0); - } while (inb(host->iobase+ASC_STAT) & CMD_REJ); + if (!WAIT (host->iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0)) { + while (len--) { + do { + outb (*cmd, host->iobase + ASC_COMMAND); + WAIT (host->iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0); + } while (inb (host->iobase + ASC_STAT) & CMD_REJ); cmd++; } - return 1; + return (1); } - printk("wd7000 command_out: WAIT failed(%d)\n", len+1); - return 0; + printk ("wd7000 command_out: WAIT failed(%d)\n", len + 1); + + return (0); } @@ -770,7 +835,7 @@ static inline int command_out(Adapter *host, unchar *cmd, int len) * the satisfiability of a request is not dependent on the size of the * request. */ -static inline Scb *alloc_scbs(int needed) +static inline Scb *alloc_scbs (int needed) { register Scb *scb, *p; register unsigned long flags; @@ -779,84 +844,89 @@ static inline Scb *alloc_scbs(int needed) static int busy = 0; int i; - if (needed <= 0) return NULL; /* sanity check */ + if (needed <= 0) + return (NULL); /* sanity check */ - save_flags(flags); - cli(); - while (busy) { /* someone else is allocating */ - sti(); /* Yes this is really needed here */ - now = jiffies; while (jiffies == now) /* wait a jiffy */; - cli(); + save_flags (flags); + cli (); + while (busy) { /* someone else is allocating */ + sti (); /* Yes this is really needed here */ + for (now = jiffies; now == jiffies; ); /* wait a jiffy */ + cli (); } - busy = 1; /* not busy now; it's our turn */ + busy = 1; /* not busy now; it's our turn */ - while (freescbs < needed) { + while (freescbs < needed) { timeout = jiffies + WAITnexttimeout; do { - sti(); /* Yes this is really needed here */ - now = jiffies; - while (jiffies == now); /* wait a jiffy */ - cli(); - } while (freescbs < needed && jiffies <= timeout); + sti (); /* Yes this is really needed here */ + for (now = jiffies; now == jiffies; ); /* wait a jiffy */ + cli (); + } while (freescbs < needed && jiffies <= timeout); /* * If we get here with enough free Scbs, we can take them. * Otherwise, we timed out and didn't get enough. */ - if (freescbs < needed) { + if (freescbs < needed) { busy = 0; - panic("wd7000: can't get enough free SCBs.\n"); - restore_flags(flags); - return NULL; + panic ("wd7000: can't get enough free SCBs.\n"); + restore_flags (flags); + return (NULL); } } - scb = scbfree; freescbs -= needed; - for (i = 0; i < needed; i++) { p = scbfree; scbfree = p->next; } + scb = scbfree; + freescbs -= needed; + for (i = 0; i < needed; i++) { + p = scbfree; + scbfree = p->next; + } p->next = NULL; - - busy = 0; /* we're done */ + busy = 0; /* we're done */ - restore_flags(flags); + restore_flags (flags); - return scb; + return (scb); } -static inline void free_scb( Scb *scb ) +static inline void free_scb (Scb *scb) { register unsigned long flags; - save_flags(flags); - cli(); + save_flags (flags); + cli (); - memset(scb, 0, sizeof(Scb)); - scb->next = scbfree; scbfree = scb; + memset (scb, 0, sizeof (Scb)); + scb->next = scbfree; + scbfree = scb; freescbs++; - restore_flags(flags); + restore_flags (flags); } -static inline void init_scbs(void) +static inline void init_scbs (void) { int i; unsigned long flags; - save_flags(flags); - cli(); + save_flags (flags); + cli (); scbfree = &(scbs[0]); - memset(scbs, 0, sizeof(scbs)); - for (i = 0; i < MAX_SCBS-1; i++) { - scbs[i].next = &(scbs[i+1]); scbs[i].SCpnt = NULL; + memset (scbs, 0, sizeof (scbs)); + for (i = 0; i < MAX_SCBS - 1; i++) { + scbs[i].next = &(scbs[i + 1]); + scbs[i].SCpnt = NULL; } - scbs[MAX_SCBS-1].next = NULL; - scbs[MAX_SCBS-1].SCpnt = NULL; + scbs[MAX_SCBS - 1].next = NULL; + scbs[MAX_SCBS - 1].SCpnt = NULL; + + restore_flags (flags); +} - restore_flags(flags); -} - -static int mail_out( Adapter *host, Scb *scbptr ) +static int mail_out (Adapter *host, Scb *scbptr) /* * Note: this can also be used for ICBs; just cast to the parm type. */ @@ -866,30 +936,35 @@ static int mail_out( Adapter *host, Scb *scbptr ) unchar start_ogmb; Mailbox *ogmbs = host->mb.ogmb; int *next_ogmb = &(host->next_ogmb); -#ifdef DEBUG - printk("wd7000 mail_out: 0x%06lx",(long) scbptr); + +#ifdef WD7000_DEBUG + printk ("wd7000_mail_out: 0x%06lx", (long) scbptr); #endif + /* We first look for a free outgoing mailbox */ - save_flags(flags); - cli(); + save_flags (flags); + cli (); ogmb = *next_ogmb; for (i = 0; i < OGMB_CNT; i++) { - if (ogmbs[ogmb].status == 0) { -#ifdef DEBUG - printk(" using OGMB 0x%x",ogmb); + if (ogmbs[ogmb].status == 0) { +#ifdef WD7000_DEBUG + printk (" using OGMB 0x%x", ogmb); #endif ogmbs[ogmb].status = 1; - any2scsi((unchar *) ogmbs[ogmb].scbptr, (int) scbptr); + any2scsi ((unchar *) ogmbs[ogmb].scbptr, (int) scbptr); - *next_ogmb = (ogmb+1) % OGMB_CNT; + *next_ogmb = (ogmb + 1) % OGMB_CNT; break; - } else + } + else ogmb = (++ogmb) % OGMB_CNT; } - restore_flags(flags); -#ifdef DEBUG - printk(", scb is 0x%06lx",(long) scbptr); + restore_flags (flags); + +#ifdef WD7000_DEBUG + printk (", scb is 0x%06lx", (long) scbptr); #endif + if (i >= OGMB_CNT) { /* * Alternatively, we might issue the "interrupt on free OGMB", @@ -899,171 +974,182 @@ static int mail_out( Adapter *host, Scb *scbptr ) * that marks OGMB's free, waiting even with interrupts off * should work, since they are freed very quickly in most cases. */ - #ifdef DEBUG - printk(", no free OGMBs.\n"); +#ifdef WD7000_DEBUG + printk (", no free OGMBs.\n"); #endif - return 0; + return (0); } - wd7000_enable_intr(host); + wd7000_enable_intr (host); start_ogmb = START_OGMB | ogmb; - command_out( host, &start_ogmb, 1 ); -#ifdef DEBUG - printk(", awaiting interrupt.\n"); + command_out (host, &start_ogmb, 1); + +#ifdef WD7000_DEBUG + printk (", awaiting interrupt.\n"); #endif - return 1; + + return (1); } -int make_code(unsigned hosterr, unsigned scsierr) -{ -#ifdef DEBUG +int make_code (unsigned hosterr, unsigned scsierr) +{ +#ifdef WD7000_DEBUG int in_error = hosterr; #endif - switch ((hosterr>>8)&0xff){ - case 0: /* Reserved */ - hosterr = DID_ERROR; - break; - case 1: /* Command Complete, no errors */ - hosterr = DID_OK; - break; - case 2: /* Command complete, error logged in scb status (scsierr) */ - hosterr = DID_OK; - break; - case 4: /* Command failed to complete - timeout */ - hosterr = DID_TIME_OUT; - break; - case 5: /* Command terminated; Bus reset by external device */ - hosterr = DID_RESET; - break; - case 6: /* Unexpected Command Received w/ host as target */ - hosterr = DID_BAD_TARGET; - break; + switch ((hosterr >> 8) & 0xff) { + case 0: /* Reserved */ + hosterr = DID_ERROR; + break; + case 1: /* Command Complete, no errors */ + hosterr = DID_OK; + break; + case 2: /* Command complete, error logged in scb status (scsierr) */ + hosterr = DID_OK; + break; + case 4: /* Command failed to complete - timeout */ + hosterr = DID_TIME_OUT; + break; + case 5: /* Command terminated; Bus reset by external device */ + hosterr = DID_RESET; + break; + case 6: /* Unexpected Command Received w/ host as target */ + hosterr = DID_BAD_TARGET; + break; case 80: /* Unexpected Reselection */ case 81: /* Unexpected Selection */ - hosterr = DID_BAD_INTR; - break; + hosterr = DID_BAD_INTR; + break; case 82: /* Abort Command Message */ - hosterr = DID_ABORT; - break; + hosterr = DID_ABORT; + break; case 83: /* SCSI Bus Software Reset */ case 84: /* SCSI Bus Hardware Reset */ - hosterr = DID_RESET; - break; + hosterr = DID_RESET; + break; default: /* Reserved */ - hosterr = DID_ERROR; - break; - } -#ifdef DEBUG - if (scsierr||hosterr) - printk("\nSCSI command error: SCSI 0x%02x host 0x%04x return %d\n", - scsierr,in_error,hosterr); + hosterr = DID_ERROR; + } +#ifdef WD7000_DEBUG + if (scsierr || hosterr) + printk ("\nSCSI command error: SCSI 0x%02x host 0x%04x return %d\n", + scsierr, in_error, hosterr); #endif - return scsierr | (hosterr << 16); + return (scsierr | (hosterr << 16)); } -static void wd7000_scsi_done(Scsi_Cmnd * SCpnt) +static void wd7000_scsi_done (Scsi_Cmnd *SCpnt) { -#ifdef DEBUG +#ifdef WD7000_DEBUG printk ("wd7000_scsi_done: 0x%06lx\n", (long) SCpnt); #endif + SCpnt->SCp.phase = 0; } -#define wd7000_intr_ack(host) outb(0,host->iobase+ASC_INTR_ACK) +#define wd7000_intr_ack(host) outb (0, host->iobase + ASC_INTR_ACK) -void wd7000_intr_handle(int irq, void *dev_id, struct pt_regs * regs) +void wd7000_intr_handle (int irq, void *dev_id, struct pt_regs *regs) { register int flag, icmb, errstatus, icmb_status; register int host_error, scsi_error; - register Scb *scb; /* for SCSI commands */ - register IcbAny *icb; /* for host commands */ + register Scb *scb; /* for SCSI commands */ + register IcbAny *icb; /* for host commands */ register Scsi_Cmnd *SCpnt; - Adapter *host = irq2host[irq]; /* This MUST be set!!! */ + Adapter *host = (Adapter *) wd7000_host[irq - IRQ_MIN]->hostdata; /* This MUST be set!!! */ Mailbox *icmbs = host->mb.icmb; -#ifdef DEBUG - printk("wd7000_intr_handle: irq = %d, host = 0x%06lx\n", irq, (long) host); + host->int_counter++; + +#ifdef WD7000_DEBUG + printk ("wd7000_intr_handle: irq = %d, host = 0x%06lx\n", irq, (long) host); #endif - flag = inb(host->iobase+ASC_INTR_STAT); -#ifdef DEBUG - printk("wd7000_intr_handle: intr stat = 0x%02x\n",flag); + flag = inb (host->iobase + ASC_INTR_STAT); + +#ifdef WD7000_DEBUG + printk ("wd7000_intr_handle: intr stat = 0x%02x\n", flag); #endif - if (!(inb(host->iobase+ASC_STAT) & INT_IM)) { + if (!(inb (host->iobase + ASC_STAT) & INT_IM)) { /* NB: these are _very_ possible if IRQ 15 is being used, since - it's the "garbage collector" on the 2nd 8259 PIC. Specifically, - any interrupt signal into the 8259 which can't be identified - comes out as 7 from the 8259, which is 15 to the host. Thus, it - is a good thing the WD7000 has an interrupt status port, so we - can sort these out. Otherwise, electrical noise and other such - problems would be indistinguishable from valid interrupts... - */ -#ifdef DEBUG - printk("wd7000_intr_handle: phantom interrupt...\n"); + * it's the "garbage collector" on the 2nd 8259 PIC. Specifically, + * any interrupt signal into the 8259 which can't be identified + * comes out as 7 from the 8259, which is 15 to the host. Thus, it + * is a good thing the WD7000 has an interrupt status port, so we + * can sort these out. Otherwise, electrical noise and other such + * problems would be indistinguishable from valid interrupts... + */ +#ifdef WD7000_DEBUG + printk ("wd7000_intr_handle: phantom interrupt...\n"); #endif - wd7000_intr_ack(host); - return; + wd7000_intr_ack (host); + return; } - if (flag & MB_INTR) { + if (flag & MB_INTR) { /* The interrupt is for a mailbox */ if (!(flag & IMB_INTR)) { -#ifdef DEBUG - printk("wd7000_intr_handle: free outgoing mailbox\n"); +#ifdef WD7000_DEBUG + printk ("wd7000_intr_handle: free outgoing mailbox\n"); #endif /* * If sleep_on() and the "interrupt on free OGMB" command are * used in mail_out(), wake_up() should correspondingly be called * here. For now, we don't need to do anything special. */ - wd7000_intr_ack(host); + wd7000_intr_ack (host); return; - } else { + } + else { /* The interrupt is for an incoming mailbox */ icmb = flag & MB_MASK; icmb_status = icmbs[icmb].status; - if (icmb_status & 0x80) { /* unsolicited - result in ICMB */ -#ifdef DEBUG - printk("wd7000_intr_handle: unsolicited interrupt 0x%02xh\n", - icmb_status); + if (icmb_status & 0x80) { /* unsolicited - result in ICMB */ +#ifdef WD7000_DEBUG + printk ("wd7000_intr_handle: unsolicited interrupt 0x%02x\n", + icmb_status); #endif - wd7000_intr_ack(host); + wd7000_intr_ack (host); return; } - scb = (struct scb *) scsi2int((unchar *)icmbs[icmb].scbptr); + /* Aaaargh! (Zaga) */ + scb = (struct scb *) (scsi2int ((unchar *) icmbs[icmb].scbptr) | PAGE_OFFSET); icmbs[icmb].status = 0; - if (!(scb->op & ICB_OP_MASK)) { /* an SCB is done */ + if (!(scb->op & ICB_OP_MASK)) { /* an SCB is done */ SCpnt = scb->SCpnt; - if (--(SCpnt->SCp.phase) <= 0) { /* all scbs are done */ + if (--(SCpnt->SCp.phase) <= 0) { /* all scbs are done */ host_error = scb->vue | (icmb_status << 8); scsi_error = scb->status; - errstatus = make_code(host_error,scsi_error); + errstatus = make_code (host_error, scsi_error); SCpnt->result = errstatus; - free_scb(scb); + free_scb (scb); - SCpnt->scsi_done(SCpnt); + SCpnt->scsi_done (SCpnt); } - } else { /* an ICB is done */ + } + else { /* an ICB is done */ icb = (IcbAny *) scb; icb->status = icmb_status; - icb->phase = 0; + icb->phase = 0; } - } /* incoming mailbox */ + } /* incoming mailbox */ } - wd7000_intr_ack(host); - return; + wd7000_intr_ack (host); + +#ifdef WD7000_DEBUG + printk ("wd7000_intr_handle: return from interrupt handler\n"); +#endif } -int wd7000_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *)) +int wd7000_queuecommand (Scsi_Cmnd *SCpnt, void (*done) (Scsi_Cmnd *)) { register Scb *scb; register Sgb *sgb; @@ -1076,94 +1162,105 @@ int wd7000_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *)) idlun = ((SCpnt->target << 5) & 0xe0) | (SCpnt->lun & 7); SCpnt->scsi_done = done; SCpnt->SCp.phase = 1; - scb = alloc_scbs(1); + scb = alloc_scbs (1); scb->idlun = idlun; - memcpy(scb->cdb, cdb, cdblen); + memcpy (scb->cdb, cdb, cdblen); scb->direc = 0x40; /* Disable direction check */ - scb->SCpnt = SCpnt; /* so we can find stuff later */ + scb->SCpnt = SCpnt; /* so we can find stuff later */ SCpnt->host_scribble = (unchar *) scb; scb->host = host; - if (SCpnt->use_sg) { + if (SCpnt->use_sg) { struct scatterlist *sg = (struct scatterlist *) SCpnt->request_buffer; unsigned i; - if (SCpnt->host->sg_tablesize == SG_NONE) { - panic("wd7000_queuecommand: scatter/gather not supported.\n"); + if (SCpnt->host->sg_tablesize == SG_NONE) { + panic ("wd7000_queuecommand: scatter/gather not supported.\n"); } -#ifdef DEBUG - printk("Using scatter/gather with %d elements.\n",SCpnt->use_sg); +#ifdef WD7000_DEBUG + printk ("Using scatter/gather with %d elements.\n", SCpnt->use_sg); #endif sgb = scb->sgb; - scb->op = 1; - any2scsi(scb->dataptr, (int) sgb); - any2scsi(scb->maxlen, SCpnt->use_sg * sizeof (Sgb) ); + scb->op = 1; + any2scsi (scb->dataptr, (int) sgb); + any2scsi (scb->maxlen, SCpnt->use_sg * sizeof (Sgb)); - for (i = 0; i < SCpnt->use_sg; i++) { - any2scsi(sgb[i].ptr, (int) sg[i].address); - any2scsi(sgb[i].len, sg[i].length); + for (i = 0; i < SCpnt->use_sg; i++) { + any2scsi (sgb[i].ptr, (int) sg[i].address); + any2scsi (sgb[i].len, sg[i].length); } - } else { + } + else { scb->op = 0; - any2scsi(scb->dataptr, (int) SCpnt->request_buffer); - any2scsi(scb->maxlen, SCpnt->request_bufflen); + any2scsi (scb->dataptr, (int) SCpnt->request_buffer); + any2scsi (scb->maxlen, SCpnt->request_bufflen); } - while (!mail_out(host, scb)) /* keep trying */; - return 1; + while (!mail_out (host, scb)); /* keep trying */ + + return (1); } -int wd7000_command(Scsi_Cmnd *SCpnt) +int wd7000_command (Scsi_Cmnd *SCpnt) { - wd7000_queuecommand(SCpnt, wd7000_scsi_done); + wd7000_queuecommand (SCpnt, wd7000_scsi_done); - while (SCpnt->SCp.phase > 0) barrier(); /* phase counts scbs down to 0 */ + while (SCpnt->SCp.phase > 0) + barrier (); /* phase counts scbs down to 0 */ - return SCpnt->result; + return (SCpnt->result); } -int wd7000_diagnostics( Adapter *host, int code ) +int wd7000_diagnostics (Adapter *host, int code) { static IcbDiag icb = {ICB_OP_DIAGNOSTICS}; static unchar buf[256]; unsigned long timeout; icb.type = code; - any2scsi(icb.len, sizeof(buf)); - any2scsi(icb.ptr, (int) &buf); + any2scsi (icb.len, sizeof (buf)); + any2scsi (icb.ptr, (int) &buf); icb.phase = 1; /* * This routine is only called at init, so there should be OGMBs * available. I'm assuming so here. If this is going to * fail, I can just let the timeout catch the failure. */ - mail_out(host, (struct scb *) &icb); - timeout = jiffies + WAITnexttimeout; /* wait up to 2 seconds */ + mail_out (host, (struct scb *) &icb); + timeout = jiffies + WAITnexttimeout; /* wait up to 2 seconds */ while (icb.phase && jiffies < timeout) - barrier(); /* wait for completion */ + barrier (); /* wait for completion */ - if (icb.phase) { - printk("wd7000_diagnostics: timed out.\n"); - return 0; + if (icb.phase) { + printk ("wd7000_diagnostics: timed out.\n"); + return (0); } - if (make_code(icb.vue|(icb.status << 8),0)) { - printk("wd7000_diagnostics: failed (0x%02x,0x%02x)\n", - icb.vue, icb.status); - return 0; + if (make_code (icb.vue | (icb.status << 8), 0)) { + printk ("wd7000_diagnostics: failed (0x%02x,0x%02x)\n", + icb.vue, icb.status); + return (0); } - return 1; + return (1); } -int wd7000_init( Adapter *host ) +int wd7000_init (Adapter *host) { - InitCmd init_cmd = { - INITIALIZATION, 7, BUS_ON, BUS_OFF, 0, {0,0,0}, OGMB_CNT, ICMB_CNT + InitCmd init_cmd = + { + INITIALIZATION, + 7, + host->bus_on, + host->bus_off, + 0, + { 0, 0, 0 }, + OGMB_CNT, + ICMB_CNT }; int diag; @@ -1171,84 +1268,78 @@ int wd7000_init( Adapter *host ) * Reset the adapter - only. The SCSI bus was initialized at power-up, * and we need to do this just so we control the mailboxes, etc. */ - outb(ASC_RES, host->iobase+ASC_CONTROL); - delay(1); /* reset pulse: this is 10ms, only need 25us */ - outb(0,host->iobase+ASC_CONTROL); - host->control = 0; /* this must always shadow ASC_CONTROL */ - - if (WAIT (host->iobase+ASC_STAT, ASC_STATMASK, CMD_RDY, 0)) { - printk ("wd7000_init: WAIT timed out.\n"); - return 0; /* 0 = not ok */ + outb (ASC_RES, host->iobase + ASC_CONTROL); + delay (1); /* reset pulse: this is 10ms, only need 25us */ + outb (0, host->iobase + ASC_CONTROL); + host->control = 0; /* this must always shadow ASC_CONTROL */ + + if (WAIT (host->iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0)) { + printk ("wd7000_init: WAIT timed out.\n"); + return (0); /* 0 = not ok */ } - if ((diag = inb(host->iobase+ASC_INTR_STAT)) != 1) { - printk("wd7000_init: "); - - switch (diag) { - case 2: - printk("RAM failure.\n"); - break; - case 3: - printk("FIFO R/W failed\n"); - break; - case 4: - printk("SBIC register R/W failed\n"); - break; - case 5: - printk("Initialization D-FF failed.\n"); - break; - case 6: - printk("Host IRQ D-FF failed.\n"); - break; - case 7: - printk("ROM checksum error.\n"); - break; - default: - printk("diagnostic code 0x%02Xh received.\n", diag); + if ((diag = inb (host->iobase + ASC_INTR_STAT)) != 1) { + printk ("wd7000_init: "); + + switch (diag) { + case 2: printk ("RAM failure.\n"); + break; + case 3: printk ("FIFO R/W failed\n"); + break; + case 4: printk ("SBIC register R/W failed\n"); + break; + case 5: printk ("Initialization D-FF failed.\n"); + break; + case 6: printk ("Host IRQ D-FF failed.\n"); + break; + case 7: printk ("ROM checksum error.\n"); + break; + default: printk ("diagnostic code 0x%02Xh received.\n", diag); } - return 0; + return (0); } - + /* Clear mailboxes */ - memset(&(host->mb), 0, sizeof(host->mb)); + memset (&(host->mb), 0, sizeof (host->mb)); /* Execute init command */ - any2scsi((unchar *) &(init_cmd.mailboxes), (int) &(host->mb)); - if (!command_out(host, (unchar *) &init_cmd, sizeof(init_cmd))) { - printk("wd7000_init: adapter initialization failed.\n"); - return 0; + any2scsi ((unchar *) & (init_cmd.mailboxes), (int) &(host->mb)); + if (!command_out (host, (unchar *) &init_cmd, sizeof (init_cmd))) { + printk ("wd7000_init: adapter initialization failed.\n"); + return (0); } - if (WAIT (host->iobase+ASC_STAT, ASC_STATMASK, ASC_INIT, 0)) { - printk ("wd7000_init: WAIT timed out.\n"); - return 0; + if (WAIT (host->iobase + ASC_STAT, ASC_STATMASK, ASC_INIT, 0)) { + printk ("wd7000_init: WAIT timed out.\n"); + return (0); } - if (request_irq(host->irq, wd7000_intr_handle, SA_INTERRUPT, "wd7000", NULL)) { - printk("wd7000_init: can't get IRQ %d.\n", host->irq); - return 0; + if (request_irq (host->irq, wd7000_intr_handle, SA_INTERRUPT, "wd7000", NULL)) { + printk ("wd7000_init: can't get IRQ %d.\n", host->irq); + return (0); } - if (request_dma(host->dma,"wd7000")) { - printk("wd7000_init: can't get DMA channel %d.\n", host->dma); - free_irq(host->irq, NULL); - return 0; + if (request_dma (host->dma, "wd7000")) { + printk ("wd7000_init: can't get DMA channel %d.\n", host->dma); + free_irq (host->irq, NULL); + return (0); } - wd7000_enable_dma(host); - wd7000_enable_intr(host); + wd7000_enable_dma (host); + wd7000_enable_intr (host); - if (!wd7000_diagnostics(host,ICB_DIAG_FULL)) { - free_dma(host->dma); - free_irq(host->irq, NULL); - return 0; + if (!wd7000_diagnostics (host, ICB_DIAG_FULL)) { + free_dma (host->dma); + free_irq (host->irq, NULL); + return (0); } - return 1; + return (1); } -void wd7000_revision(Adapter *host) +void wd7000_revision (Adapter *host) { - static IcbRevLvl icb = {ICB_OP_GET_REVISION}; + static IcbRevLvl icb = + {ICB_OP_GET_REVISION}; icb.phase = 1; /* @@ -1257,14 +1348,160 @@ void wd7000_revision(Adapter *host) * the only damage will be that the revision will show up as 0.0, * which in turn means that scatter/gather will be disabled. */ - mail_out(host, (struct scb *) &icb); + mail_out (host, (struct scb *) &icb); while (icb.phase) - barrier(); /* wait for completion */ + barrier (); /* wait for completion */ host->rev1 = icb.primary; host->rev2 = icb.secondary; } +#undef SPRINTF +#define SPRINTF(args...) { if (pos < (buffer + length)) pos += sprintf (pos, ## args); } + +int wd7000_set_info (char *buffer, int length, struct Scsi_Host *host) +{ + unsigned long flags; + + save_flags (flags); + cli (); + +#ifdef WD7000_DEBUG + printk ("Buffer = <%.*s>, length = %d\n", length, buffer, length); +#endif + + /* + * Currently this is a no-op + */ + printk ("Sorry, this function is currently out of order...\n"); + + restore_flags (flags); + + return (length); +} + + +int wd7000_proc_info (char *buffer, char **start, off_t offset, int length, int hostno, int inout) +{ + struct Scsi_Host *host = NULL; + Scsi_Device *scd; + Adapter *adapter; + unsigned long flags; + char *pos = buffer; + short i; + +#ifdef WD7000_DEBUG + Mailbox *ogmbs, *icmbs; + short count; +#endif + + /* + * Find the specified host board. + */ + for (i = 0; i < IRQS; i++) + if (wd7000_host[i] && (wd7000_host[i]->host_no == hostno)) { + host = wd7000_host[i]; + + break; + } + + /* + * Host not found! + */ + if (! host) + return (-ESRCH); + + /* + * Has data been written to the file ? + */ + if (inout) + return (wd7000_set_info (buffer, length, host)); + + adapter = (Adapter *) host->hostdata; + + save_flags (flags); + cli (); + + SPRINTF ("Host scsi%d: Western Digital WD-7000 (rev %d.%d)\n", hostno, adapter->rev1, adapter->rev2); + SPRINTF (" IO base: 0x%x\n", adapter->iobase); + SPRINTF (" IRQ: %d\n", adapter->irq); + SPRINTF (" DMA channel: %d\n", adapter->dma); + SPRINTF (" Interrupts: %d\n", adapter->int_counter); + SPRINTF (" BUS_ON time: %d nanoseconds\n", adapter->bus_on * 125); + SPRINTF (" BUS_OFF time: %d nanoseconds\n", adapter->bus_off * 125); + +#ifdef WD7000_DEBUG + ogmbs = adapter->mb.ogmb; + icmbs = adapter->mb.icmb; + + SPRINTF ("\nControl port value: 0x%x\n", adapter->control); + SPRINTF ("Incoming mailbox:\n"); + SPRINTF (" size: %d\n", ICMB_CNT); + SPRINTF (" queued messages: "); + + for (i = count = 0; i < ICMB_CNT; i++) + if (icmbs[i].status) { + count++; + SPRINTF ("0x%x ", i); + } + + SPRINTF (count ? "\n" : "none\n"); + + SPRINTF ("Outgoing mailbox:\n"); + SPRINTF (" size: %d\n", OGMB_CNT); + SPRINTF (" next message: 0x%x\n", adapter->next_ogmb); + SPRINTF (" queued messages: "); + + for (i = count = 0; i < OGMB_CNT; i++) + if (ogmbs[i].status) { + count++; + SPRINTF ("0x%x ", i); + } + + SPRINTF (count ? "\n" : "none\n"); +#endif + + /* + * Display driver information for each device attached to the board. + */ + scd = host->host_queue; + + SPRINTF ("\nAttached devices: %s\n", scd ? "" : "none"); + + for ( ; scd; scd = scd->next) + if (scd->host->host_no == hostno) { + SPRINTF (" [Channel: %02d, Id: %02d, Lun: %02d] ", + scd->channel, scd->id, scd->lun); + SPRINTF ("%s ", (scd->type < MAX_SCSI_DEVICE_CODE) ? + scsi_device_types[(short) scd->type] : "Unknown device"); + + for (i = 0; (i < 8) && (scd->vendor[i] >= 0x20); i++) + SPRINTF ("%c", scd->vendor[i]); + SPRINTF (" "); + + for (i = 0; (i < 16) && (scd->model[i] >= 0x20); i++) + SPRINTF ("%c", scd->model[i]); + SPRINTF ("\n"); + } + + SPRINTF ("\n"); + + restore_flags (flags); + + /* + * Calculate start of next buffer, and return value. + */ + *start = buffer + offset; + + if ((pos - buffer) < offset) + return (0); + else if ((pos - buffer - offset) < length) + return (pos - buffer - offset); + else + return (length); +} + + /* * Returns the number of adapters this driver is supporting. * @@ -1277,84 +1514,102 @@ void wd7000_revision(Adapter *host) */ int wd7000_detect (Scsi_Host_Template *tpnt) { - short present = 0, biosaddr_ptr, cfg_ptr, sig_ptr, i, pass; + short present = 0, biosaddr_ptr, sig_ptr, i, pass; short biosptr[NUM_CONFIGS]; unsigned iobase; Adapter *host = NULL; struct Scsi_Host *sh; - for (i = 0; i < NUM_CONFIGS; biosptr[i++] = -1); +#ifdef WD7000_DEBUG + printk ("wd7000_detect: started\n"); +#endif + + for (i = 0; i < IRQS; wd7000_host[i++] = NULL) ; + for (i = 0; i < NUM_CONFIGS; biosptr[i++] = -1) ; tpnt->proc_dir = &proc_scsi_wd7000; + tpnt->proc_info = &wd7000_proc_info; /* * Set up SCB free list, which is shared by all adapters */ init_scbs (); - for (pass = 0, cfg_ptr = 0; pass < NUM_CONFIGS; pass++) { - for (biosaddr_ptr = 0; biosaddr_ptr < NUM_ADDRS; biosaddr_ptr++) + for (pass = 0; pass < NUM_CONFIGS; pass++) { + /* + * First, search for BIOS SIGNATURE... + */ + for (biosaddr_ptr = 0; biosaddr_ptr < NUM_ADDRS; biosaddr_ptr++) for (sig_ptr = 0; sig_ptr < NUM_SIGNATURES; sig_ptr++) { - for (i = 0; i < pass; i++) - if (biosptr[i] == biosaddr_ptr) - break; - - if ((i == pass) && - check_signature(wd7000_biosaddr[biosaddr_ptr] + - signatures[sig_ptr].ofs, - signatures[sig_ptr].sig, - signatures[sig_ptr].len)) - goto bios_matched; - } + for (i = 0; i < pass; i++) + if (biosptr[i] == biosaddr_ptr) + break; -bios_matched: + if (i == pass) { +#if (LINUX_VERSION_CODE < 0x020100) +#else + void *biosaddr = ioremap (wd7000_biosaddr[biosaddr_ptr] + + signatures[sig_ptr].ofs, + signatures[sig_ptr].len); +#endif + short bios_match = memcmp ((char *) biosaddr, signatures[sig_ptr].sig, + signatures[sig_ptr].len); + +#if (LINUX_VERSION_CODE < 0x020100) +#else + iounmap (biosaddr); +#endif -#ifdef DEBUG + if (! bios_match) + goto bios_matched; + } + } + + bios_matched: + /* + * BIOS SIGNATURE has been found. + */ +#ifdef WD7000_DEBUG printk ("wd7000_detect: pass %d\n", pass + 1); - if (biosaddr_ptr == NUM_ADDRS) + if (biosaddr_ptr == NUM_ADDRS) printk ("WD-7000 SST BIOS not detected...\n"); else printk ("WD-7000 SST BIOS detected at 0x%lx: checking...\n", - wd7000_biosaddr[biosaddr_ptr]); + wd7000_biosaddr[biosaddr_ptr]); #endif - if (wd7000_card_num) - iobase = wd7000_setupIO[wd7000_card_num - 1]; - else { - if (configs[cfg_ptr++].irq < 0) - continue; + if (configs[pass].irq < 0) + continue; - iobase = configs[cfg_ptr - 1].iobase; - } + iobase = configs[pass].iobase; -#ifdef DEBUG - printk ("wd7000_detect: check IO 0x%x region...\n", iobase); +#ifdef WD7000_DEBUG + printk ("wd7000_detect: check IO 0x%x region...\n", iobase); #endif - if (! check_region (iobase, 4)) { + if (!check_region (iobase, 4)) { -#ifdef DEBUG - printk ("wd7000_detect: ASC reset (IO 0x%x) ...", iobase); +#ifdef WD7000_DEBUG + printk ("wd7000_detect: ASC reset (IO 0x%x) ...", iobase); #endif - - /* - * ASC reset... - */ - outb (ASC_RES, iobase + ASC_CONTROL); - delay (1); - outb (0, iobase + ASC_CONTROL); - - if (WAIT (iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0)) -#ifdef DEBUG - { - printk ("failed!\n"); - continue; - } - else - printk ("ok!\n"); + /* + * ASC reset... + */ + outb (ASC_RES, iobase + ASC_CONTROL); + delay (1); + outb (0, iobase + ASC_CONTROL); + + if (WAIT (iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0)) +#ifdef WD7000_DEBUG + { + printk ("failed!\n"); + continue; + } + else + printk ("ok!\n"); #else - continue; + continue; #endif if (inb (iobase + ASC_INTR_STAT) == 1) { @@ -1368,33 +1623,27 @@ bios_matched: sh = scsi_register (tpnt, sizeof (Adapter)); host = (Adapter *) sh->hostdata; -#ifdef DEBUG - printk ("wd7000_detect: adapter allocated at 0x%x\n", - (int) host); +#ifdef WD7000_DEBUG + printk ("wd7000_detect: adapter allocated at 0x%x\n", (int) host); #endif memset (host, 0, sizeof (Adapter)); - if (wd7000_card_num) { - host->irq = wd7000_setupIRQ[--wd7000_card_num]; - host->dma = wd7000_setupDMA[wd7000_card_num]; - } - else { - host->irq = configs[cfg_ptr - 1].irq; - host->dma = configs[cfg_ptr - 1].dma; - } - - host->sh = sh; - host->iobase = iobase; - irq2host[host->irq] = host; + host->irq = configs[pass].irq; + host->dma = configs[pass].dma; + host->iobase = iobase; + host->int_counter = 0; + host->bus_on = configs[pass].bus_on; + host->bus_off = configs[pass].bus_off; + host->sh = wd7000_host[host->irq - IRQ_MIN] = sh; -#ifdef DEBUG +#ifdef WD7000_DEBUG printk ("wd7000_detect: Trying init WD-7000 card at IO " "0x%x, IRQ %d, DMA %d...\n", host->iobase, host->irq, host->dma); #endif - if (! wd7000_init (host)) { /* Initialization failed */ + if (!wd7000_init (host)) { /* Initialization failed */ scsi_unregister (sh); continue; @@ -1403,7 +1652,7 @@ bios_matched: /* * OK from here - we'll use this adapter/configuration. */ - wd7000_revision (host); /* important for scatter/gather */ + wd7000_revision (host); /* important for scatter/gather */ /* * Register our ports. @@ -1411,34 +1660,34 @@ bios_matched: request_region (host->iobase, 4, "wd7000"); /* - * For boards before rev 6.0, scatter/gather - * isn't supported. + * For boards before rev 6.0, scatter/gather isn't supported. */ if (host->rev1 < 6) sh->sg_tablesize = SG_NONE; - present++; /* count it */ + present++; /* count it */ if (biosaddr_ptr != NUM_ADDRS) biosptr[pass] = biosaddr_ptr; printk ("Western Digital WD-7000 (rev %d.%d) ", host->rev1, host->rev2); - printk ("using IO 0x%x, IRQ %d, DMA %d.\n", + printk ("using IO 0x%x, IRQ %d, DMA %d.\n", host->iobase, host->irq, host->dma); + printk (" BUS_ON time: %dns, BUS_OFF time: %dns\n", + host->bus_on * 125, host->bus_off * 125); } } -#ifdef DEBUG +#ifdef WD7000_DEBUG else - printk ("wd7000_detect: IO 0x%x region already allocated!\n", - iobase); + printk ("wd7000_detect: IO 0x%x region already allocated!\n", iobase); #endif } - if (! present) - printk ("Failed initialization of WD-7000 SCSI card!\n"); + if (!present) + printk ("Failed initialization of WD-7000 SCSI card!\n"); return (present); } @@ -1447,42 +1696,81 @@ bios_matched: /* * I have absolutely NO idea how to do an abort with the WD7000... */ -int wd7000_abort(Scsi_Cmnd * SCpnt) +int wd7000_abort (Scsi_Cmnd *SCpnt) { Adapter *host = (Adapter *) SCpnt->host->hostdata; - if (inb(host->iobase+ASC_STAT) & INT_IM) { - printk("wd7000_abort: lost interrupt\n"); - wd7000_intr_handle(host->irq, NULL, NULL); - return SCSI_ABORT_SUCCESS; + if (inb (host->iobase + ASC_STAT) & INT_IM) { + printk ("wd7000_abort: lost interrupt\n"); + wd7000_intr_handle (host->irq, NULL, NULL); + + return (SCSI_ABORT_SUCCESS); } - return SCSI_ABORT_SNOOZE; + return (SCSI_ABORT_SNOOZE); } /* * I also have no idea how to do a reset... */ -int wd7000_reset(Scsi_Cmnd * SCpnt, unsigned int ignored) +int wd7000_reset (Scsi_Cmnd *SCpnt, unsigned int unused) { - return SCSI_RESET_PUNT; + return (SCSI_RESET_PUNT); } /* - * This was borrowed directly from aha1542.c, but my disks are organized - * this way, so I think it will work OK. Someone who is ambitious can - * borrow a newer or more complete version from another driver. + * This was borrowed directly from aha1542.c. (Zaga) */ -int wd7000_biosparam(Disk * disk, kdev_t dev, int* ip) +int wd7000_biosparam (Disk *disk, kdev_t dev, int *ip) { - int size = disk->capacity; - ip[0] = 64; - ip[1] = 32; - ip[2] = size >> 11; -/* if (ip[2] >= 1024) ip[2] = 1024; */ - return 0; +#ifdef WD7000_DEBUG + printk ("wd7000_biosparam: dev=%s, size=%d, ", kdevname (dev), disk->capacity); +#endif + + /* + * try default translation + */ + ip[0] = 64; + ip[1] = 32; + ip[2] = disk->capacity / (64 * 32); + + /* + * for disks >1GB do some guessing + */ + if (ip[2] >= 1024) { + int info[3]; + + /* + * try to figure out the geometry from the partition table + */ + if ((scsicam_bios_param (disk, dev, info) < 0) || + !(((info[0] == 64) && (info[1] == 32)) || + ((info[0] == 255) && (info[1] == 63)))) { + printk ("wd7000_biosparam: unable to verify geometry for disk with >1GB.\n" + " using extended translation.\n"); + + ip[0] = 255; + ip[1] = 63; + ip[2] = disk->capacity / (255 * 63); + } + else { + ip[0] = info[0]; + ip[1] = info[1]; + ip[2] = info[2]; + + if (info[0] == 255) + printk ("wd7000_biosparam: current partition table is using extended translation.\n"); + } + } + +#ifdef WD7000_DEBUG + printk ("bios geometry: head=%d, sec=%d, cyl=%d\n", ip[0], ip[1], ip[2]); + printk ("WARNING: check, if the bios geometry is correct.\n"); +#endif + + return (0); } #ifdef MODULE diff --git a/drivers/scsi/wd7000.h b/drivers/scsi/wd7000.h index 8835e5f50..73679c24a 100644 --- a/drivers/scsi/wd7000.h +++ b/drivers/scsi/wd7000.h @@ -8,17 +8,23 @@ * This file has been reduced to only the definitions needed for the * WD7000 host structure. * + * Revision by Miroslav Zagorac <zaga@fly.cc.fer.hr> Jun 1997. */ #include <linux/types.h> #include <linux/kdev_t.h> -int wd7000_detect(Scsi_Host_Template *); -int wd7000_command(Scsi_Cmnd *); -int wd7000_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); -int wd7000_abort(Scsi_Cmnd *); -int wd7000_reset(Scsi_Cmnd *, unsigned int); -int wd7000_biosparam(Disk *, kdev_t, int *); +extern struct proc_dir_entry proc_scsi_wd7000; + + +int wd7000_set_info (char *buffer, int length, struct Scsi_Host *host); +int wd7000_proc_info (char *buffer, char **start, off_t offset, int length, int hostno, int inout); +int wd7000_detect (Scsi_Host_Template *); +int wd7000_command (Scsi_Cmnd *); +int wd7000_queuecommand (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); +int wd7000_abort (Scsi_Cmnd *); +int wd7000_reset (Scsi_Cmnd *, unsigned int); +int wd7000_biosparam (Disk *, kdev_t, int *); #ifndef NULL #define NULL 0L @@ -38,18 +44,22 @@ int wd7000_biosparam(Disk *, kdev_t, int *); #define WD7000_Q 16 #define WD7000_SG 16 -#define WD7000 { \ - name: "Western Digital WD-7000", \ - detect: wd7000_detect, \ - command: wd7000_command, \ - queuecommand: wd7000_queuecommand, \ - abort: wd7000_abort, \ - reset: wd7000_reset, \ - bios_param: wd7000_biosparam, \ - can_queue: WD7000_Q, \ - this_id: 7, \ - sg_tablesize: WD7000_SG, \ - cmd_per_lun: 1, \ - unchecked_isa_dma: 1, \ - use_clustering: ENABLE_CLUSTERING} +#define WD7000 { \ + proc_dir: &proc_scsi_wd7000, \ + proc_info: wd7000_proc_info, \ + name: "Western Digital WD-7000", \ + detect: wd7000_detect, \ + command: wd7000_command, \ + queuecommand: wd7000_queuecommand, \ + abort: wd7000_abort, \ + reset: wd7000_reset, \ + bios_param: wd7000_biosparam, \ + can_queue: WD7000_Q, \ + this_id: 7, \ + sg_tablesize: WD7000_SG, \ + cmd_per_lun: 1, \ + unchecked_isa_dma: 1, \ + use_clustering: ENABLE_CLUSTERING, \ + use_new_eh_code: 0 \ +} #endif diff --git a/drivers/sound/Config.in b/drivers/sound/Config.in index 7ef5f8dd4..d4678da82 100644 --- a/drivers/sound/Config.in +++ b/drivers/sound/Config.in @@ -6,6 +6,8 @@ fi dep_tristate '100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support' CONFIG_SB $CONFIG_SOUND if [ "$CONFIG_SB" = "y" ]; then + bool 'Is the card a Soundman Games ?' CONFIG_SM_GAMES + bool 'Are you using the IBM Mwave "emulation" of SB ?' CONFIG_SB_MWAVE hex 'I/O base for SB Check from manual of the card' CONFIG_SB_BASE 220 int 'Sound Blaster IRQ Check from manual of the card' CONFIG_SB_IRQ 7 int 'Sound Blaster DMA 0, 1 or 3' CONFIG_SB_DMA 1 diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile index e8ec0b12d..6b09e336b 100644 --- a/drivers/sound/Makefile +++ b/drivers/sound/Makefile @@ -117,22 +117,6 @@ else endif endif -ifeq ($(CONFIG_MPU401),y) -LX_OBJS += mpu401.o -else - ifeq ($(CONFIG_MPU401),m) - MX_OBJS += mpu401.o - else - ifeq ($(CONFIG_MPU_EMU),y) - LX_OBJS += mpu401.o - else - ifeq ($(CONFIG_MPU_EMU),m) - MX_OBJS += mpu401.o - endif - endif - endif -endif - ifeq ($(CONFIG_UART401),y) LX_OBJS += uart401.o else @@ -159,9 +143,31 @@ endif ifeq ($(CONFIG_SSCAPE),y) L_OBJS += sscape.o +LX_OBJS += ad1848.o +CONFIG_MPU401 = y else ifeq ($(CONFIG_SSCAPE),m) M_OBJS += sscape.o + MX_OBJS += ad1848.o + ifneq ($(CONFIG_MPU401),y) + CONFIG_MPU401 = m + endif + endif +endif + +ifeq ($(CONFIG_MPU401),y) +LX_OBJS += mpu401.o +else + ifeq ($(CONFIG_MPU401),m) + MX_OBJS += mpu401.o + else + ifeq ($(CONFIG_MPU_EMU),y) + LX_OBJS += mpu401.o + else + ifeq ($(CONFIG_MPU_EMU),m) + MX_OBJS += mpu401.o + endif + endif endif endif @@ -265,7 +271,7 @@ CONFIG_MAUI_BOOT_FILE := $(patsubst "%", %, $(CONFIG_MAUI_BOOT_FILE)) maui.o: maui_boot.h maui_boot.h: $(CONFIG_MAUI_BOOT_FILE) bin2hex - bin2hex maui_os < "$(CONFIG_MAUI_BOOT_FILE)" > $@ + bin2hex -i maui_os < "$(CONFIG_MAUI_BOOT_FILE)" > $@ @ ( \ echo 'ifeq ($(strip $(CONFIG_MAUI_BOOT_FILE)),$$(strip $$(CONFIG_MAUI_BOOT_FILE)))'; \ echo 'FILES_BOOT_UP_TO_DATE += $@'; \ @@ -297,7 +303,7 @@ CONFIG_TRIX_BOOT_FILE := $(patsubst "%", %, $(CONFIG_TRIX_BOOT_FILE)) trix.o: trix_boot.h trix_boot.h: $(CONFIG_TRIX_BOOT_FILE) hex2hex - hex2hex trix_boot < "$(CONFIG_TRIX_BOOT_FILE)" > $@ + hex2hex -i trix_boot < "$(CONFIG_TRIX_BOOT_FILE)" > $@ @ ( \ echo 'ifeq ($(strip $(CONFIG_TRIX_BOOT_FILE)),$$(strip $$(CONFIG_TRIX_BOOT_FILE)))'; \ echo 'FILES_BOOT_UP_TO_DATE += $@'; \ diff --git a/drivers/sound/ad1848.c b/drivers/sound/ad1848.c index 6fdec35ec..d839ed208 100644 --- a/drivers/sound/ad1848.c +++ b/drivers/sound/ad1848.c @@ -23,6 +23,12 @@ */ /* * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) + * general sleep/wakeup clean up. + * Alan Cox : reformatted. Fixed SMP bugs. Moved to kernel alloc/free + * of irqs. Use dev_id. + * + * Status: + * Tested. Believed fully functional. */ #include <linux/config.h> @@ -144,9 +150,9 @@ static void ad1848_tmr_reprogram(int dev); static int ad_read(ad1848_info * devc, int reg) { - unsigned long flags; - int x; - int timeout = 900000; + unsigned long flags; + int x; + int timeout = 900000; while (timeout > 0 && inb(devc->base) == 0x80) /*Are we initializing */ timeout--; @@ -163,11 +169,10 @@ static int ad_read(ad1848_info * devc, int reg) static void ad_write(ad1848_info * devc, int reg, int data) { - unsigned long flags; - int timeout = 900000; + unsigned long flags; + int timeout = 900000; - while (timeout > 0 && - inb(devc->base) == 0x80) /*Are we initializing */ + while (timeout > 0 && inb(devc->base) == 0x80) /* Are we initializing */ timeout--; save_flags(flags); @@ -180,7 +185,7 @@ static void ad_write(ad1848_info * devc, int reg, int data) static void wait_for_calibration(ad1848_info * devc) { - int timeout = 0; + int timeout = 0; /* * Wait until the auto calibration process has finished. @@ -1751,7 +1756,11 @@ int ad1848_init(char *name, int io_base, int irq, int dma_playback, int dma_capt else devc->audio_flags |= DMA_DUPLEX; } - + + portc = (ad1848_port_info *) kmalloc(sizeof(ad1848_port_info), GFP_KERNEL); + if(portc==NULL) + return -1; + if ((my_dev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, dev_name, &ad1848_audio_driver, @@ -1762,12 +1771,11 @@ int ad1848_init(char *name, int io_base, int irq, int dma_playback, int dma_capt dma_playback, dma_capture)) < 0) { + kfree(portc); + portc=NULL; return -1; } - portc = (ad1848_port_info *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(ad1848_port_info))); - sound_mem_sizes[sound_nblocks] = sizeof(ad1848_port_info); - if (sound_nblocks < 1024) - sound_nblocks++;; + audio_devs[my_dev]->portc = portc; memset((char *) portc, 0, sizeof(*portc)); @@ -1777,23 +1785,21 @@ int ad1848_init(char *name, int io_base, int irq, int dma_playback, int dma_capt if (irq > 0) { - irq2dev[irq] = devc->dev_no = my_dev; - if (snd_set_irq_handler(devc->irq, adintr, - devc->name, - NULL) < 0) + devc->dev_no = my_dev; + if (request_irq(devc->irq, adintr, 0, devc->name, (void *)my_dev) < 0) { - printk(KERN_WARNING "ad1848: IRQ in use\n"); + printk(KERN_WARNING "ad1848: Unable to allocate IRQ\n"); } if (devc->model != MD_1848 && devc->model != MD_C930) { - int x; - unsigned char tmp = ad_read(devc, 16); + int x; + unsigned char tmp = ad_read(devc, 16); devc->timer_ticks = 0; ad_write(devc, 21, 0x00); /* Timer MSB */ ad_write(devc, 20, 0x10); /* Timer LSB */ - +#ifndef __SMP__ ad_write(devc, 16, tmp | 0x40); /* Enable timer */ for (x = 0; x < 100000 && devc->timer_ticks == 0; x++); ad_write(devc, 16, tmp & ~0x40); /* Disable timer */ @@ -1805,6 +1811,9 @@ int ad1848_init(char *name, int io_base, int irq, int dma_playback, int dma_capt DDB(printk("Interrupt test OK\n")); devc->irq_ok = 1; } +#else + devc->irq_ok=1; +#endif } else devc->irq_ok = 1; /* Couldn't test. assume it's OK */ @@ -1840,7 +1849,7 @@ int ad1848_init(char *name, int io_base, int irq, int dma_playback, int dma_capt void ad1848_control(int cmd, int arg) { - ad1848_info *devc; + ad1848_info *devc; if (nr_ad1848_devs < 1) return; @@ -1850,7 +1859,7 @@ void ad1848_control(int cmd, int arg) switch (cmd) { case AD1848_SET_XTAL: /* Change clock frequency of AD1845 (only ) */ - if (devc->model != MD_1845) + if (devc->model != MD_1845) return; ad_enter_MCE(devc); ad_write(devc, 29, (ad_read(devc, 29) & 0x1f) | (arg << 5)); @@ -1859,8 +1868,8 @@ void ad1848_control(int cmd, int arg) case AD1848_MIXER_REROUTE: { - int o = (arg >> 8) & 0xff; - int n = arg & 0xff; + int o = (arg >> 8) & 0xff; + int n = arg & 0xff; if (n == SOUND_MIXER_NONE) { /* Just hide this control */ @@ -1906,12 +1915,14 @@ void ad1848_unload(int io_base, int irq, int dma_playback, int dma_capture, int if (devc != NULL) { + if(audio_devs[dev]->portc!=NULL) + kfree(audio_devs[dev]->portc); release_region(devc->base, 4); if (!share_dma) { if (irq > 0) - snd_release_irq(devc->irq); + free_irq(devc->irq, NULL); sound_free_dma(audio_devs[dev]->dmap_out->dma); @@ -1926,35 +1937,15 @@ void ad1848_unload(int io_base, int irq, int dma_playback, int dma_capture, int void adintr(int irq, void *dev_id, struct pt_regs *dummy) { - unsigned char status; - ad1848_info *devc; - int dev; - int alt_stat = 0xff; - unsigned char c930_stat = 0; - int cnt = 0; - - if (irq < 0 || irq > 15) - { - dev = -1; - } - else - dev = irq2dev[irq]; - - if (dev < 0 || dev >= num_audiodevs) - { - for (irq = 0; irq < 17; irq++) - if (irq2dev[irq] != -1) - break; - - if (irq > 15) - { - /* printk("ad1848.c: Bogus interrupt %d\n", irq); */ - return; - } - dev = irq2dev[irq]; - devc = (ad1848_info *) audio_devs[dev]->devc; - } else - devc = (ad1848_info *) audio_devs[dev]->devc; + unsigned char status; + ad1848_info *devc; + int dev; + int alt_stat = 0xff; + unsigned char c930_stat = 0; + int cnt = 0; + + dev = (int)dev_id; + devc = (ad1848_info *) audio_devs[dev]->devc; interrupt_again: /* Jump back here if int status doesn't reset */ @@ -2542,12 +2533,12 @@ EXPORT_SYMBOL(unload_ms_sound); #ifdef MODULE -MODULE_PARM(io, "i"); -MODULE_PARM(irq, "i"); -MODULE_PARM(dma, "i"); -MODULE_PARM(dma2, "i"); -MODULE_PARM(type, "i"); -MODULE_PARM(deskpro_xl, "i"); +MODULE_PARM(io, "i"); /* I/O for a raw AD1848 card */ +MODULE_PARM(irq, "i"); /* IRQ to use */ +MODULE_PARM(dma, "i"); /* First DMA channel */ +MODULE_PARM(dma2, "i"); /* Second DMA channel */ +MODULE_PARM(type, "i"); /* Card type */ +MODULE_PARM(deskpro_xl, "i"); /* Special magic for Deskpro XL boxen */ int io = -1; int irq = -1; diff --git a/drivers/sound/audio.c b/drivers/sound/audio.c index b27b1999a..9c9ccf095 100644 --- a/drivers/sound/audio.c +++ b/drivers/sound/audio.c @@ -21,7 +21,6 @@ #include <linux/config.h> #include <linux/stddef.h> -#include <linux/kerneld.h> #include "sound_config.h" diff --git a/drivers/sound/bin2hex.c b/drivers/sound/bin2hex.c index 351dd24e0..fc49c99d9 100644 --- a/drivers/sound/bin2hex.c +++ b/drivers/sound/bin2hex.c @@ -2,12 +2,27 @@ int main( int argc, const char * argv [] ) { - const char * varname = argv[1]; + const char * varname; int i = 0; int c; + int id = 0; + if(argv[1] && strcmp(argv[1],"-i")==0) + { + argv++; + argc--; + id=1; + } + + if(argc==1) + { + fprintf(stderr, "bin2hex: [-i] firmware\n"); + exit(1); + } + + varname = argv[1]; printf( "/* automatically generated by bin2hex */\n" ); - printf( "static unsigned char %s [] =\n{\n", varname ); + printf( "static unsigned char %s [] %s =\n{\n", varname , id?"__initdata":""); while ( ( c = getchar( ) ) != EOF ) { diff --git a/drivers/sound/cs4232.c b/drivers/sound/cs4232.c index c8732dfe4..fdb5b738c 100644 --- a/drivers/sound/cs4232.c +++ b/drivers/sound/cs4232.c @@ -12,7 +12,14 @@ * CS4232 * CS4236 * CS4236B + * + * Note: You will need a PnP config setup to initialise some CS4232 boards + * anyway. + * + * Changes + * Alan Cox Modularisation, Basic cleanups. */ + /* * Copyright (C) by Hannu Savolainen 1993-1997 * @@ -20,6 +27,7 @@ * Version 2 (June 1991). See the "COPYING" file distributed with this software * for more info. */ + #include <linux/config.h> #include <linux/module.h> @@ -99,6 +107,8 @@ int probe_cs4232(struct address_info *hw_config) * just one CS4232 compatible device can exist on the system. Also this * method conflicts with possible PnP support in the OS. For this reason * driver is just a temporary kludge. + * + * Also the Cirrus/Crystal method doesnt always work. Try ISAPnP first ;) */ /* @@ -106,8 +116,8 @@ int probe_cs4232(struct address_info *hw_config) * first time. */ - for (n = 0; n < 4; n++) { - + for (n = 0; n < 4; n++) + { /* * Wake up the card by sending a 32 byte Crystal key to the key port. */ @@ -298,8 +308,7 @@ struct address_info cfg; * loaded ready. */ -int -init_module(void) +int init_module(void) { if (io == -1 || irq == -1 || dma == -1 || dma2 == -1) { diff --git a/drivers/sound/dev_table.c b/drivers/sound/dev_table.c index 666d7765e..7f3957bfd 100644 --- a/drivers/sound/dev_table.c +++ b/drivers/sound/dev_table.c @@ -148,9 +148,6 @@ void sound_unload_drivers(void) } } - for (i=0;i<num_audiodevs;i++) - DMAbuf_deinit(i); - if (trace_init) printk(KERN_DEBUG "Sound unload complete\n"); } diff --git a/drivers/sound/dmabuf.c b/drivers/sound/dmabuf.c index 151e20449..db6a02d05 100644 --- a/drivers/sound/dmabuf.c +++ b/drivers/sound/dmabuf.c @@ -104,7 +104,7 @@ static int sound_alloc_dmap(struct dma_buffparms *dmap) dmap->raw_buf_phys = virt_to_bus(start_addr); for (i = MAP_NR(start_addr); i <= MAP_NR(end_addr); i++) - set_bit(PG_reserved, &mem_map[i].flags);; + set_bit(PG_reserved, &mem_map[i].flags); return 0; } @@ -115,8 +115,6 @@ static void sound_free_dmap(struct dma_buffparms *dmap) if (dmap->raw_buf == NULL) return; - if (dmap->mapping_flags & DMA_MAP_MAPPED) - return; /* Don't free mmapped buffer. Will use it next time */ for (sz = 0, size = PAGE_SIZE; size < dmap->buffsize; sz++, size <<= 1); start_addr = (unsigned long) dmap->raw_buf; @@ -127,6 +125,8 @@ static void sound_free_dmap(struct dma_buffparms *dmap) free_pages((unsigned long) dmap->raw_buf, sz); dmap->raw_buf = NULL; + /* Remember the buffer is deleted so we dont Oops later */ + dmap->fragment_size = 0; } @@ -206,6 +206,7 @@ static void close_dmap(struct audio_operations *adev, struct dma_buffparms *dmap dmap->dma_mode = DMODE_NONE; dmap->flags &= ~DMA_BUSY; disable_dma(dmap->dma); + sound_free_dmap(dmap); } @@ -832,12 +833,12 @@ int DMAbuf_move_wrpointer(int dev, int l) dmap->user_counter += l; dmap->flags |= DMA_DIRTY; - if (dmap->user_counter >= dmap->max_byte_counter) { + if (dmap->byte_counter >= dmap->max_byte_counter) { /* Wrap the byte counters */ - long decr = dmap->user_counter; - dmap->user_counter = (dmap->user_counter % dmap->bytes_in_use) + dmap->bytes_in_use; - decr -= dmap->user_counter; - dmap->byte_counter -= decr; + long decr = dmap->byte_counter; + dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use); + decr -= dmap->byte_counter; + dmap->user_counter -= decr; } end_ptr = (dmap->user_counter / dmap->fragment_size) * dmap->fragment_size; @@ -928,7 +929,7 @@ static void do_outputintr(int dev, int dummy) dmap->byte_counter += dmap->bytes_in_use; if (dmap->byte_counter >= dmap->max_byte_counter) { /* Overflow */ long decr = dmap->byte_counter; - dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use; + dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use); decr -= dmap->byte_counter; dmap->user_counter -= decr; } @@ -952,7 +953,7 @@ static void do_outputintr(int dev, int dummy) dmap->byte_counter += dmap->bytes_in_use; if (dmap->byte_counter >= dmap->max_byte_counter) { /* Overflow */ long decr = dmap->byte_counter; - dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use; + dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use); decr -= dmap->byte_counter; dmap->user_counter -= decr; } @@ -1211,18 +1212,4 @@ unsigned int DMAbuf_poll(struct file * file, int dev, poll_table *wait) return poll_input(file, dev, wait) | poll_output(file, dev, wait); } -void DMAbuf_deinit(int dev) -{ - struct audio_operations *adev = audio_devs[dev]; - /* This routine is called when driver is being unloaded */ - if (!adev) - return; -#ifdef RUNTIME_DMA_ALLOC - sound_free_dmap(adev->dmap_out); - - if (adev->flags & DMA_DUPLEX) - sound_free_dmap(adev->dmap_in); -#endif -} - #endif diff --git a/drivers/sound/gus_card.c b/drivers/sound/gus_card.c index 0f4f769eb..92e6dbab6 100644 --- a/drivers/sound/gus_card.c +++ b/drivers/sound/gus_card.c @@ -30,7 +30,7 @@ int gus_pnp_flag = 0; void attach_gus_card(struct address_info *hw_config) { - snd_set_irq_handler(hw_config->irq, gusintr, "Gravis Ultrasound", hw_config->osp); + snd_set_irq_handler(hw_config->irq, gusintr, "Gravis Ultrasound", hw_config->osp, hw_config); gus_wave_init(hw_config); @@ -101,7 +101,7 @@ void unload_gus(struct address_info *hw_config) release_region(hw_config->io_base, 16); release_region(hw_config->io_base + 0x100, 12); /* 0x10c-> is MAX */ - snd_release_irq(hw_config->irq); + snd_release_irq(hw_config->irq, hw_config); sound_free_dma(hw_config->dma); @@ -111,14 +111,15 @@ void unload_gus(struct address_info *hw_config) void gusintr(int irq, void *dev_id, struct pt_regs *dummy) { - unsigned char src; - extern int gus_timer_enabled; + unsigned char src; + extern int gus_timer_enabled; + struct address_info *hw_config=dev_id; sti(); #ifdef CONFIG_GUSMAX if (have_gus_max) - adintr(irq, NULL, NULL); + adintr(irq, (void *)hw_config->slots[3], NULL); #endif while (1) diff --git a/drivers/sound/hex2hex.c b/drivers/sound/hex2hex.c index 19753bfc2..4b182625b 100644 --- a/drivers/sound/hex2hex.c +++ b/drivers/sound/hex2hex.c @@ -66,14 +66,27 @@ int loadhex(FILE *inf, unsigned char *buf) int main( int argc, const char * argv [] ) { - const char * varline = argv[1]; + const char * varline; int i,l; + int id=0; + if(argv[1] && strcmp(argv[1], "-i")==0) + { + argv++; + argc--; + id=1; + } + if(argv[1]==NULL) + { + fprintf(stderr,"hex2hex: [-i] filename\n"); + exit(1); + } + varline = argv[1; l = loadhex(stdin, buf); printf("/*\n *\t Computer generated file. Do not edit.\n */\n"); printf("static int %s_len = %d;\n", varline, l); - printf("static unsigned char %s[] = {\n", varline); + printf("static unsigned char %s[] %s = {\n", varline, id?"__initdata":""); for (i=0;i<l;i++) { diff --git a/drivers/sound/maui.c b/drivers/sound/maui.c index 5cd620dbf..a7d37039c 100644 --- a/drivers/sound/maui.c +++ b/drivers/sound/maui.c @@ -9,7 +9,15 @@ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) * Version 2 (June 1991). See the "COPYING" file distributed with this software * for more info. + * + * Changes: + * Alan Cox General clean up, use kernel IRQ + * system + * + * Status: + * Untested */ + #include <linux/config.h> #include <linux/module.h> @@ -327,7 +335,7 @@ int probe_maui(struct address_info *hw_config) maui_base = hw_config->io_base; maui_osp = hw_config->osp; - if (snd_set_irq_handler(hw_config->irq, mauiintr, "Maui", maui_osp) < 0) + if (request_irq(hw_config->irq, mauiintr, 0, "Maui", NULL) < 0) return 0; /* @@ -342,26 +350,26 @@ int probe_maui(struct address_info *hw_config) maui_read() == -1 || maui_read() == -1) if (!maui_init(hw_config->irq)) { - snd_release_irq(hw_config->irq); + free_irq(hw_config->irq, NULL); return 0; } } if (!maui_write(0xCF)) /* Report hardware version */ { printk(KERN_ERR "No WaveFront firmware detected (card uninitialized?)\n"); - snd_release_irq(hw_config->irq); + free_irq(hw_config->irq, NULL); return 0; } if ((tmp1 = maui_read()) == -1 || (tmp2 = maui_read()) == -1) { printk(KERN_ERR "No WaveFront firmware detected (card uninitialized?)\n"); - snd_release_irq(hw_config->irq); + free_irq(hw_config->irq, NULL); return 0; } if (tmp1 == 0xff || tmp2 == 0xff) { - snd_release_irq(hw_config->irq); - return 0; + free_irq(hw_config->irq, NULL); + return 0; } if (trace_init) printk(KERN_DEBUG "WaveFront hardware version %d.%d\n", tmp1, tmp2); @@ -439,7 +447,7 @@ void unload_maui(struct address_info *hw_config) if (irq < 0) irq = -irq; if (irq > 0) - snd_release_irq(irq); + free_irq(irq, NULL); } #ifdef MODULE @@ -452,8 +460,7 @@ static int fw_load = 0; struct address_info cfg; /* - * Install a CS4232 based card. Need to have ad1848 and mpu401 - * loaded ready. + * Install a Maui card. Needs mpu401 loaded already. */ int init_module(void) diff --git a/drivers/sound/midibuf.c b/drivers/sound/midibuf.c index 0c024a454..09dea3a34 100644 --- a/drivers/sound/midibuf.c +++ b/drivers/sound/midibuf.c @@ -15,7 +15,6 @@ */ #include <linux/config.h> #include <linux/stddef.h> -#include <linux/kerneld.h> #define MIDIBUF_C diff --git a/drivers/sound/mpu401.c b/drivers/sound/mpu401.c index 9ae597404..ce92010fc 100644 --- a/drivers/sound/mpu401.c +++ b/drivers/sound/mpu401.c @@ -9,10 +9,12 @@ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) * Version 2 (June 1991). See the "COPYING" file distributed with this software * for more info. + * + * + * Thomas Sailer ioctl code reworked (vmalloc/vfree removed) + * Alan Cox modularisation, use normal request_irq, use dev_id */ -/* - * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) - */ + #include <linux/config.h> #include <linux/module.h> @@ -32,30 +34,30 @@ static int timer_mode = TMR_INTERNAL, timer_caps = TMR_INTERNAL; #endif struct mpu_config - { - int base; /* +{ + int base; /* * I/O base */ - int irq; - int opened; /* - * Open mode - */ - int devno; - int synthno; - int uart_mode; - int initialized; - int mode; + int irq; + int opened; /* + * Open mode + */ + int devno; + int synthno; + int uart_mode; + int initialized; + int mode; #define MODE_MIDI 1 #define MODE_SYNTH 2 - unsigned char version, revision; - unsigned int capabilities; + unsigned char version, revision; + unsigned int capabilities; #define MPU_CAP_INTLG 0x10000000 #define MPU_CAP_SYNC 0x00000010 #define MPU_CAP_FSK 0x00000020 #define MPU_CAP_CLS 0x00000040 #define MPU_CAP_SMPTE 0x00000080 #define MPU_CAP_2PORT 0x00000001 - int timer_flag; + int timer_flag; #define MBUF_MAX 10 #define BUFTEST(dc) if (dc->m_ptr >= MBUF_MAX || dc->m_ptr < 0) \ @@ -75,28 +77,27 @@ struct mpu_config #define COMDPORT(base) (base+1) #define STATPORT(base) (base+1) -static int -mpu401_status(struct mpu_config *devc) +static int mpu401_status(struct mpu_config *devc) { return inb(STATPORT(devc->base)); } + #define input_avail(devc) (!(mpu401_status(devc)&INPUT_AVAIL)) #define output_ready(devc) (!(mpu401_status(devc)&OUTPUT_READY)) -static void -write_command(struct mpu_config *devc, unsigned char cmd) + +static void write_command(struct mpu_config *devc, unsigned char cmd) { - outb((cmd), COMDPORT(devc->base)); + outb(cmd, COMDPORT(devc->base)); } -static int -read_data(struct mpu_config *devc) + +static int read_data(struct mpu_config *devc) { return inb(DATAPORT(devc->base)); } -static void -write_data(struct mpu_config *devc, unsigned char byte) +static void write_data(struct mpu_config *devc, unsigned char byte) { - outb((byte), DATAPORT(devc->base)); + outb(byte, DATAPORT(devc->base)); } #define OUTPUT_READY 0x40 @@ -107,22 +108,27 @@ write_data(struct mpu_config *devc, unsigned char byte) static struct mpu_config dev_conf[MAX_MIDI_DEV] = { - {0}}; + {0} +}; -static int n_mpu_devs = 0; -static volatile int irq2dev[17] = -{-1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1}; +static int n_mpu_devs = 0; -static int reset_mpu401(struct mpu_config *devc); -static void set_uart_mode(int dev, struct mpu_config *devc, int arg); +static int reset_mpu401(struct mpu_config *devc); +static void set_uart_mode(int dev, struct mpu_config *devc, int arg); -static int mpu_timer_init(int midi_dev); -static void mpu_timer_interrupt(void); -static void timer_ext_event(struct mpu_config *devc, int event, int parm); +static int mpu_timer_init(int midi_dev); +static void mpu_timer_interrupt(void); +static void timer_ext_event(struct mpu_config *devc, int event, int parm); -static struct synth_info mpu_synth_info_proto = -{"MPU-401 MIDI interface", 0, SYNTH_TYPE_MIDI, MIDI_TYPE_MPU401, 0, 128, 0, 128, SYNTH_CAP_INPUT}; +static struct synth_info mpu_synth_info_proto = { + "MPU-401 MIDI interface", + 0, + SYNTH_TYPE_MIDI, + MIDI_TYPE_MPU401, + 0, 128, + 0, 128, + SYNTH_CAP_INPUT +}; static struct synth_info mpu_synth_info[MAX_MIDI_DEV]; @@ -158,10 +164,10 @@ static unsigned char len_tab[] = /* # of data bytes following a status #else #define STORE(cmd) \ { \ - int len; \ - unsigned char obuf[8]; \ - cmd; \ - seq_input_event(obuf, len); \ + int len; \ + unsigned char obuf[8]; \ + cmd; \ + seq_input_event(obuf, len); \ } #endif @@ -169,242 +175,242 @@ static unsigned char len_tab[] = /* # of data bytes following a status #define _seqbufptr 0 #define _SEQ_ADVBUF(x) len=x -static int -mpu_input_scanner(struct mpu_config *devc, unsigned char midic) +static int mpu_input_scanner(struct mpu_config *devc, unsigned char midic) { switch (devc->m_state) - { - case ST_INIT: - switch (midic) - { - case 0xf8: - /* Timer overflow */ - break; - - case 0xfc: - printk("<all end>"); - break; - - case 0xfd: - if (devc->timer_flag) - mpu_timer_interrupt(); - break; - - case 0xfe: - return MPU_ACK; - break; - - case 0xf0: - case 0xf1: - case 0xf2: - case 0xf3: - case 0xf4: - case 0xf5: - case 0xf6: - case 0xf7: - printk("<Trk data rq #%d>", midic & 0x0f); - break; - - case 0xf9: - printk("<conductor rq>"); - break; - - case 0xff: - devc->m_state = ST_SYSMSG; - break; - - default: - if (midic <= 0xef) - { - /* printk( "mpu time: %d ", midic); */ - devc->m_state = ST_TIMED; - } else - printk("<MPU: Unknown event %02x> ", midic); - } - break; - - case ST_TIMED: - { - int msg = ((int) (midic & 0xf0) >> 4); - - devc->m_state = ST_DATABYTE; - - if (msg < 8) /* Data byte */ - { - /* printk( "midi msg (running status) "); */ - msg = ((int) (devc->last_status & 0xf0) >> 4); - msg -= 8; - devc->m_left = len_tab[msg] - 1; - - devc->m_ptr = 2; - devc->m_buf[0] = devc->last_status; - devc->m_buf[1] = midic; - - if (devc->m_left <= 0) - { - devc->m_state = ST_INIT; - do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr); - devc->m_ptr = 0; - } - } else if (msg == 0xf) /* MPU MARK */ - { - devc->m_state = ST_INIT; - - switch (midic) - { - case 0xf8: - /* printk( "NOP "); */ - break; - - case 0xf9: - /* printk( "meas end "); */ - break; - - case 0xfc: - /* printk( "data end "); */ - break; - - default: - printk("Unknown MPU mark %02x\n", midic); - } - } else - { - devc->last_status = midic; - /* printk( "midi msg "); */ - msg -= 8; - devc->m_left = len_tab[msg]; - - devc->m_ptr = 1; - devc->m_buf[0] = midic; - - if (devc->m_left <= 0) - { - devc->m_state = ST_INIT; - do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr); - devc->m_ptr = 0; - } - } - } - break; - - case ST_SYSMSG: - switch (midic) - { - case 0xf0: - printk("<SYX>"); - devc->m_state = ST_SYSEX; - break; - - case 0xf1: - devc->m_state = ST_MTC; - break; - - case 0xf2: - devc->m_state = ST_SONGPOS; - devc->m_ptr = 0; - break; - - case 0xf3: - devc->m_state = ST_SONGSEL; - break; - - case 0xf6: - /* printk( "tune_request\n"); */ - devc->m_state = ST_INIT; - - /* - * Real time messages - */ - case 0xf8: - /* midi clock */ - devc->m_state = ST_INIT; - timer_ext_event(devc, TMR_CLOCK, 0); - break; - - case 0xfA: - devc->m_state = ST_INIT; - timer_ext_event(devc, TMR_START, 0); - break; - - case 0xFB: - devc->m_state = ST_INIT; - timer_ext_event(devc, TMR_CONTINUE, 0); - break; - - case 0xFC: - devc->m_state = ST_INIT; - timer_ext_event(devc, TMR_STOP, 0); - break; - - case 0xFE: - /* active sensing */ - devc->m_state = ST_INIT; - break; - - case 0xff: - /* printk( "midi hard reset"); */ - devc->m_state = ST_INIT; - break; - - default: - printk("unknown MIDI sysmsg %0x\n", midic); - devc->m_state = ST_INIT; - } - break; - - case ST_MTC: - devc->m_state = ST_INIT; - printk("MTC frame %x02\n", midic); - break; - - case ST_SYSEX: - if (midic == 0xf7) - { - printk("<EOX>"); - devc->m_state = ST_INIT; - } else - printk("%02x ", midic); - break; - - case ST_SONGPOS: - BUFTEST(devc); - devc->m_buf[devc->m_ptr++] = midic; - if (devc->m_ptr == 2) - { - devc->m_state = ST_INIT; - devc->m_ptr = 0; - timer_ext_event(devc, TMR_SPP, - ((devc->m_buf[1] & 0x7f) << 7) | - (devc->m_buf[0] & 0x7f)); - } - break; - - case ST_DATABYTE: - BUFTEST(devc); - devc->m_buf[devc->m_ptr++] = midic; - if ((--devc->m_left) <= 0) - { - devc->m_state = ST_INIT; - do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr); - devc->m_ptr = 0; - } - break; - - default: - printk("Bad state %d ", devc->m_state); - devc->m_state = ST_INIT; - } + { + case ST_INIT: + switch (midic) + { + case 0xf8: + /* Timer overflow */ + break; + + case 0xfc: + printk("<all end>"); + break; + + case 0xfd: + if (devc->timer_flag) + mpu_timer_interrupt(); + break; + + case 0xfe: + return MPU_ACK; + + case 0xf0: + case 0xf1: + case 0xf2: + case 0xf3: + case 0xf4: + case 0xf5: + case 0xf6: + case 0xf7: + printk("<Trk data rq #%d>", midic & 0x0f); + break; + + case 0xf9: + printk("<conductor rq>"); + break; + + case 0xff: + devc->m_state = ST_SYSMSG; + break; + + default: + if (midic <= 0xef) + { + /* printk( "mpu time: %d ", midic); */ + devc->m_state = ST_TIMED; + } + else + printk("<MPU: Unknown event %02x> ", midic); + } + break; + + case ST_TIMED: + { + int msg = ((int) (midic & 0xf0) >> 4); + + devc->m_state = ST_DATABYTE; + + if (msg < 8) /* Data byte */ + { + /* printk( "midi msg (running status) "); */ + msg = ((int) (devc->last_status & 0xf0) >> 4); + msg -= 8; + devc->m_left = len_tab[msg] - 1; + + devc->m_ptr = 2; + devc->m_buf[0] = devc->last_status; + devc->m_buf[1] = midic; + + if (devc->m_left <= 0) + { + devc->m_state = ST_INIT; + do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr); + devc->m_ptr = 0; + } + } + else if (msg == 0xf) /* MPU MARK */ + { + devc->m_state = ST_INIT; + + switch (midic) + { + case 0xf8: + /* printk( "NOP "); */ + break; + + case 0xf9: + /* printk( "meas end "); */ + break; + + case 0xfc: + /* printk( "data end "); */ + break; + + default: + printk("Unknown MPU mark %02x\n", midic); + } + } + else + { + devc->last_status = midic; + /* printk( "midi msg "); */ + msg -= 8; + devc->m_left = len_tab[msg]; + + devc->m_ptr = 1; + devc->m_buf[0] = midic; + + if (devc->m_left <= 0) + { + devc->m_state = ST_INIT; + do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr); + devc->m_ptr = 0; + } + } + } + break; + + case ST_SYSMSG: + switch (midic) + { + case 0xf0: + printk("<SYX>"); + devc->m_state = ST_SYSEX; + break; + + case 0xf1: + devc->m_state = ST_MTC; + break; + + case 0xf2: + devc->m_state = ST_SONGPOS; + devc->m_ptr = 0; + break; + + case 0xf3: + devc->m_state = ST_SONGSEL; + break; + + case 0xf6: + /* printk( "tune_request\n"); */ + devc->m_state = ST_INIT; + + /* + * Real time messages + */ + case 0xf8: + /* midi clock */ + devc->m_state = ST_INIT; + timer_ext_event(devc, TMR_CLOCK, 0); + break; + + case 0xfA: + devc->m_state = ST_INIT; + timer_ext_event(devc, TMR_START, 0); + break; + + case 0xFB: + devc->m_state = ST_INIT; + timer_ext_event(devc, TMR_CONTINUE, 0); + break; + + case 0xFC: + devc->m_state = ST_INIT; + timer_ext_event(devc, TMR_STOP, 0); + break; + + case 0xFE: + /* active sensing */ + devc->m_state = ST_INIT; + break; + + case 0xff: + /* printk( "midi hard reset"); */ + devc->m_state = ST_INIT; + break; + + default: + printk("unknown MIDI sysmsg %0x\n", midic); + devc->m_state = ST_INIT; + } + break; + + case ST_MTC: + devc->m_state = ST_INIT; + printk("MTC frame %x02\n", midic); + break; + + case ST_SYSEX: + if (midic == 0xf7) + { + printk("<EOX>"); + devc->m_state = ST_INIT; + } + else + printk("%02x ", midic); + break; + + case ST_SONGPOS: + BUFTEST(devc); + devc->m_buf[devc->m_ptr++] = midic; + if (devc->m_ptr == 2) + { + devc->m_state = ST_INIT; + devc->m_ptr = 0; + timer_ext_event(devc, TMR_SPP, + ((devc->m_buf[1] & 0x7f) << 7) | + (devc->m_buf[0] & 0x7f)); + } + break; + case ST_DATABYTE: + BUFTEST(devc); + devc->m_buf[devc->m_ptr++] = midic; + if ((--devc->m_left) <= 0) + { + devc->m_state = ST_INIT; + do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr); + devc->m_ptr = 0; + } + break; + + default: + printk("Bad state %d ", devc->m_state); + devc->m_state = ST_INIT; + } return 1; } -static void -mpu401_input_loop(struct mpu_config *devc) +static void mpu401_input_loop(struct mpu_config *devc) { - unsigned long flags; - int busy; - int n; + unsigned long flags; + int busy; + int n; save_flags(flags); cli(); @@ -418,72 +424,45 @@ mpu401_input_loop(struct mpu_config *devc) n = 50; while (input_avail(devc) && n-- > 0) - { - unsigned char c = read_data(devc); - - if (devc->mode == MODE_SYNTH) - { - mpu_input_scanner(devc, c); - } else if (devc->opened & OPEN_READ && devc->inputintr != NULL) - devc->inputintr(devc->devno, c); - } + { + unsigned char c = read_data(devc); + if (devc->mode == MODE_SYNTH) + { + mpu_input_scanner(devc, c); + } + else if (devc->opened & OPEN_READ && devc->inputintr != NULL) + devc->inputintr(devc->devno, c); + } devc->m_busy = 0; } -void -mpuintr(int irq, void *dev_id, struct pt_regs *dummy) +void mpuintr(int irq, void *dev_id, struct pt_regs *dummy) { struct mpu_config *devc; - int dev; + int dev = (int) dev_id; sti(); - -/* - * FreeBSD (and some others) pass unit number to the interrupt handler. - * In this case we have to scan the table for first handler. - */ - - if (irq < 1 || irq > 15) - { - dev = -1; - } else - dev = irq2dev[irq]; - - if (dev == -1) - { - int origirq = irq; - - for (irq = 0; irq <= 16; irq++) - if (irq2dev[irq] != -1) - break; - if (irq > 15) - { - printk("MPU-401: Bogus interrupt #%d?\n", origirq); - return; - } - dev = irq2dev[irq]; - devc = &dev_conf[dev]; - } else - devc = &dev_conf[dev]; + devc = &dev_conf[dev]; if (input_avail(devc)) + { if (devc->base != 0 && (devc->opened & OPEN_READ || devc->mode == MODE_SYNTH)) mpu401_input_loop(devc); else - { - /* Dummy read (just to acknowledge the interrupt) */ - read_data(devc); - } + { + /* Dummy read (just to acknowledge the interrupt) */ + read_data(devc); + } + } } -static int -mpu401_open(int dev, int mode, +static int mpu401_open(int dev, int mode, void (*input) (int dev, unsigned char data), void (*output) (int dev) ) { - int err; + int err; struct mpu_config *devc; if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL) @@ -492,36 +471,34 @@ mpu401_open(int dev, int mode, devc = &dev_conf[dev]; if (devc->opened) - { - printk("MPU-401: Midi busy\n"); return -EBUSY; - } /* - * Verify that the device is really running. - * Some devices (such as Ensoniq SoundScape don't - * work before the on board processor (OBP) is initialized - * by downloading its microcode. + * Verify that the device is really running. + * Some devices (such as Ensoniq SoundScape don't + * work before the on board processor (OBP) is initialized + * by downloading its microcode. */ if (!devc->initialized) - { - if (mpu401_status(devc) == 0xff) /* Bus float */ - { - printk("MPU-401: Device not initialized properly\n"); - return -EIO; - } - reset_mpu401(devc); - } - irq2dev[devc->irq] = dev; + { + if (mpu401_status(devc) == 0xff) /* Bus float */ + { + printk(KERN_ERR "mpu401: Device not initialized properly\n"); + return -EIO; + } + reset_mpu401(devc); + } if (midi_devs[dev]->coproc) + { if ((err = midi_devs[dev]->coproc-> open(midi_devs[dev]->coproc->devc, COPR_MIDI)) < 0) - { - printk("MPU-401: Can't access coprocessor device\n"); - - return err; - } + { + printk("MPU-401: Can't access coprocessor device\n"); + return err; + } + } + set_uart_mode(dev, devc, 1); devc->mode = MODE_MIDI; devc->synthno = 0; @@ -534,19 +511,16 @@ mpu401_open(int dev, int mode, return 0; } -static void -mpu401_close(int dev) +static void mpu401_close(int dev) { struct mpu_config *devc; devc = &dev_conf[dev]; - if (devc->uart_mode) reset_mpu401(devc); /* * This disables the UART mode */ devc->mode = 0; - devc->inputintr = NULL; if (midi_devs[dev]->coproc) @@ -554,11 +528,10 @@ mpu401_close(int dev) devc->opened = 0; } -static int -mpu401_out(int dev, unsigned char midi_byte) +static int mpu401_out(int dev, unsigned char midi_byte) { - int timeout; - unsigned long flags; + int timeout; + unsigned long flags; struct mpu_config *devc; @@ -574,21 +547,20 @@ mpu401_out(int dev, unsigned char midi_byte) save_flags(flags); cli(); if (!output_ready(devc)) - { - printk("MPU-401: Send data timeout\n"); - restore_flags(flags); - return 0; - } + { + printk(KERN_WARNING "mpu401: Send data timeout\n"); + restore_flags(flags); + return 0; + } write_data(devc, midi_byte); restore_flags(flags); return 1; } -static int -mpu401_command(int dev, mpu_command_rec * cmd) +static int mpu401_command(int dev, mpu_command_rec * cmd) { - int i, timeout, ok; - int ret = 0; + int i, timeout, ok; + int ret = 0; unsigned long flags; struct mpu_config *devc; @@ -597,10 +569,10 @@ mpu401_command(int dev, mpu_command_rec * cmd) if (devc->uart_mode) /* * Not possible in UART mode */ - { - printk("MPU-401 commands not possible in the UART mode\n"); - return -EINVAL; - } + { + printk(KERN_WARNING "mpu401: commands not possible in the UART mode\n"); + return -EINVAL; + } /* * Test for input since pending input seems to block the output. */ @@ -613,83 +585,87 @@ mpu401_command(int dev, mpu_command_rec * cmd) */ timeout = 50000; - retry: +retry: if (timeout-- <= 0) - { - printk("MPU-401: Command (0x%x) timeout\n", (int) cmd->cmd); - return -EIO; - } + { + printk(KERN_WARNING "mpu401: Command (0x%x) timeout\n", (int) cmd->cmd); + return -EIO; + } save_flags(flags); cli(); if (!output_ready(devc)) - { + { restore_flags(flags); goto retry; - } + } write_command(devc, cmd->cmd); ok = 0; for (timeout = 50000; timeout > 0 && !ok; timeout--) + { if (input_avail(devc)) - { - if (devc->opened && devc->mode == MODE_SYNTH) - { - if (mpu_input_scanner(devc, read_data(devc)) == MPU_ACK) - ok = 1; - } else - { /* Device is not currently open. Use simpler method */ - if (read_data(devc) == MPU_ACK) - ok = 1; - } - } + { + if (devc->opened && devc->mode == MODE_SYNTH) + { + if (mpu_input_scanner(devc, read_data(devc)) == MPU_ACK) + ok = 1; + } + else + { + /* Device is not currently open. Use simpler method */ + if (read_data(devc) == MPU_ACK) + ok = 1; + } + } + } if (!ok) - { - restore_flags(flags); - /* printk( "MPU: No ACK to command (0x%x)\n", (int) cmd->cmd); */ - return -EIO; - } + { + restore_flags(flags); + return -EIO; + } if (cmd->nr_args) + { for (i = 0; i < cmd->nr_args; i++) - { - for (timeout = 3000; timeout > 0 && !output_ready(devc); timeout--); - - if (!mpu401_out(dev, cmd->data[i])) - { - restore_flags(flags); - printk("MPU: Command (0x%x), parm send failed.\n", (int) cmd->cmd); - return -EIO; - } - } + { + for (timeout = 3000; timeout > 0 && !output_ready(devc); timeout--); + + if (!mpu401_out(dev, cmd->data[i])) + { + restore_flags(flags); + printk(KERN_WARNING "mpu401: Command (0x%x), parm send failed.\n", (int) cmd->cmd); + return -EIO; + } + } + } ret = 0; cmd->data[0] = 0; if (cmd->nr_returns) + { for (i = 0; i < cmd->nr_returns; i++) - { - ok = 0; - for (timeout = 5000; timeout > 0 && !ok; timeout--) - if (input_avail(devc)) - { - cmd->data[i] = read_data(devc); - ok = 1; - } - if (!ok) - { - restore_flags(flags); - /* printk( "MPU: No response(%d) to command (0x%x)\n", i, (int) cmd->cmd); */ - return -EIO; - } - } + { + ok = 0; + for (timeout = 5000; timeout > 0 && !ok; timeout--) + if (input_avail(devc)) + { + cmd->data[i] = read_data(devc); + ok = 1; + } + if (!ok) + { + restore_flags(flags); + return -EIO; + } + } + } restore_flags(flags); - return ret; } -static int -mpu_cmd(int dev, int cmd, int data) +static int mpu_cmd(int dev, int cmd, int data) { - int ret; + int ret; static mpu_command_rec rec; @@ -699,14 +675,11 @@ mpu_cmd(int dev, int cmd, int data) rec.data[0] = data & 0xff; if ((ret = mpu401_command(dev, &rec)) < 0) - { - return ret; - } + return ret; return (unsigned char) rec.data[0]; } -static int -mpu401_prefix_cmd(int dev, unsigned char status) +static int mpu401_prefix_cmd(int dev, unsigned char status) { struct mpu_config *devc = &dev_conf[dev]; @@ -714,37 +687,29 @@ mpu401_prefix_cmd(int dev, unsigned char status) return 1; if (status < 0xf0) - { - if (mpu_cmd(dev, 0xD0, 0) < 0) - { - return 0; - } - return 1; - } + { + if (mpu_cmd(dev, 0xD0, 0) < 0) + return 0; + return 1; + } switch (status) - { - case 0xF0: - if (mpu_cmd(dev, 0xDF, 0) < 0) - { - return 0; - } - return 1; - break; - - default: - return 0; - } + { + case 0xF0: + if (mpu_cmd(dev, 0xDF, 0) < 0) + return 0; + return 1; + default: + return 0; + } } -static int -mpu401_start_read(int dev) +static int mpu401_start_read(int dev) { return 0; } -static int -mpu401_end_read(int dev) +static int mpu401_end_read(int dev) { return 0; } @@ -756,49 +721,47 @@ static int mpu401_ioctl(int dev, unsigned cmd, caddr_t arg) int val, ret; devc = &dev_conf[dev]; - switch (cmd) { - case SNDCTL_MIDI_MPUMODE: - if (!(devc->capabilities & MPU_CAP_INTLG)) { /* No intelligent mode */ - printk("MPU-401: Intelligent mode not supported by the HW\n"); + switch (cmd) + { + case SNDCTL_MIDI_MPUMODE: + if (!(devc->capabilities & MPU_CAP_INTLG)) { /* No intelligent mode */ + printk(KERN_WARNING "mpu401: Intelligent mode not supported by the HW\n"); + return -EINVAL; + } + if (get_user(val, (int *)arg)) + return -EFAULT; + set_uart_mode(dev, devc, !val); + return 0; + + case SNDCTL_MIDI_MPUCMD: + if (copy_from_user(&rec, arg, sizeof(rec))) + return -EFAULT; + if ((ret = mpu401_command(dev, &rec)) < 0) + return ret; + if (copy_to_user(arg, &rec, sizeof(rec))) + return -EFAULT; + return 0; + + default: return -EINVAL; - } - if (__get_user(val, (int *)arg)) - return -EFAULT; - set_uart_mode(dev, devc, !val); - return 0; - - case SNDCTL_MIDI_MPUCMD: - if (__copy_from_user(&rec, arg, sizeof(rec))) - return -EFAULT; - if ((ret = mpu401_command(dev, &rec)) < 0) - return ret; - if (__copy_to_user(arg, &rec, sizeof(rec))) - return -EFAULT; - return 0; - - default: - return -EINVAL; } } -static void -mpu401_kick(int dev) +static void mpu401_kick(int dev) { } -static int -mpu401_buffer_status(int dev) +static int mpu401_buffer_status(int dev) { return 0; /* * No data in buffers */ } -static int -mpu_synth_ioctl(int dev, +static int mpu_synth_ioctl(int dev, unsigned int cmd, caddr_t arg) { - int midi_dev; + int midi_dev; struct mpu_config *devc; midi_dev = synth_devs[dev]->midi_dev; @@ -809,88 +772,77 @@ mpu_synth_ioctl(int dev, devc = &dev_conf[midi_dev]; switch (cmd) - { - - case SNDCTL_SYNTH_INFO: - memcpy((&((char *) arg)[0]), (char *) &mpu_synth_info[midi_dev], sizeof(struct synth_info)); + { - return 0; - break; + case SNDCTL_SYNTH_INFO: + memcpy((&((char *) arg)[0]), (char *) &mpu_synth_info[midi_dev], sizeof(struct synth_info)); + return 0; - case SNDCTL_SYNTH_MEMAVL: - return 0x7fffffff; - break; + case SNDCTL_SYNTH_MEMAVL: + return 0x7fffffff; - default: - return -EINVAL; - } + default: + return -EINVAL; + } } -static int -mpu_synth_open(int dev, int mode) +static int mpu_synth_open(int dev, int mode) { - int midi_dev, err; + int midi_dev, err; struct mpu_config *devc; midi_dev = synth_devs[dev]->midi_dev; if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev] == NULL) - { - return -ENXIO; - } + return -ENXIO; + devc = &dev_conf[midi_dev]; /* - * Verify that the device is really running. - * Some devices (such as Ensoniq SoundScape don't - * work before the on board processor (OBP) is initialized - * by downloading its microcode. + * Verify that the device is really running. + * Some devices (such as Ensoniq SoundScape don't + * work before the on board processor (OBP) is initialized + * by downloading its microcode. */ if (!devc->initialized) - { - if (mpu401_status(devc) == 0xff) /* Bus float */ - { - printk("MPU-401: Device not initialized properly\n"); - return -EIO; - } - reset_mpu401(devc); - } + { + if (mpu401_status(devc) == 0xff) /* Bus float */ + { + printk(KERN_ERR "mpu401: Device not initialized properly\n"); + return -EIO; + } + reset_mpu401(devc); + } if (devc->opened) - { - printk("MPU-401: Midi busy\n"); - return -EBUSY; - } + return -EBUSY; devc->mode = MODE_SYNTH; devc->synthno = dev; devc->inputintr = NULL; - irq2dev[devc->irq] = midi_dev; if (midi_devs[midi_dev]->coproc) if ((err = midi_devs[midi_dev]->coproc-> open(midi_devs[midi_dev]->coproc->devc, COPR_MIDI)) < 0) - { - printk("MPU-401: Can't access coprocessor device\n"); - - return err; - } + { + printk(KERN_WARNING "mpu401: Can't access coprocessor device\n"); + return err; + } devc->opened = mode; reset_mpu401(devc); if (mode & OPEN_READ) - { - mpu_cmd(midi_dev, 0x8B, 0); /* Enable data in stop mode */ - mpu_cmd(midi_dev, 0x34, 0); /* Return timing bytes in stop mode */ - mpu_cmd(midi_dev, 0x87, 0); /* Enable pitch & controller */ - } + { + mpu_cmd(midi_dev, 0x8B, 0); /* Enable data in stop mode */ + mpu_cmd(midi_dev, 0x34, 0); /* Return timing bytes in stop mode */ + mpu_cmd(midi_dev, 0x87, 0); /* Enable pitch & controller */ + } return 0; } -static void -mpu_synth_close(int dev) -{ - int midi_dev; +static void mpu_synth_close(int dev) +{ + int midi_dev; struct mpu_config *devc; midi_dev = synth_devs[dev]->midi_dev; @@ -958,55 +910,52 @@ static struct midi_operations mpu401_midi_proto = static struct midi_operations mpu401_midi_operations[MAX_MIDI_DEV]; -static void -mpu401_chk_version(int n, struct mpu_config *devc) +static void mpu401_chk_version(int n, struct mpu_config *devc) { - int tmp; - unsigned long flags; + int tmp; + unsigned long flags; devc->version = devc->revision = 0; save_flags(flags); cli(); if ((tmp = mpu_cmd(n, 0xAC, 0)) < 0) - { - restore_flags(flags); - return; - } + { + restore_flags(flags); + return; + } if ((tmp & 0xf0) > 0x20) /* Why it's larger than 2.x ??? */ - { - restore_flags(flags); - return; - } + { + restore_flags(flags); + return; + } devc->version = tmp; if ((tmp = mpu_cmd(n, 0xAD, 0)) < 0) - { - devc->version = 0; - restore_flags(flags); - return; - } + { + devc->version = 0; + restore_flags(flags); + return; + } devc->revision = tmp; - restore_flags(flags); } -void -attach_mpu401(struct address_info *hw_config) +void attach_mpu401(struct address_info *hw_config) { - unsigned long flags; - char revision_char; + unsigned long flags; + char revision_char; - int m; + int m; struct mpu_config *devc; hw_config->slots[1] = -1; m = sound_alloc_mididev(); if (m == -1) - { - printk(KERN_WARNING "MPU-401: Too many midi devices detected\n"); - return; - } + { + printk(KERN_WARNING "MPU-401: Too many midi devices detected\n"); + return; + } devc = &dev_conf[m]; devc->base = hw_config->io_base; devc->osp = hw_config->osp; @@ -1024,35 +973,36 @@ attach_mpu401(struct address_info *hw_config) devc->irq = hw_config->irq; if (devc->irq < 0) - { - devc->irq *= -1; - devc->shared_irq = 1; - } - irq2dev[devc->irq] = m; + { + devc->irq *= -1; + devc->shared_irq = 1; + } if (!hw_config->always_detect) - { - /* Verify the hardware again */ - if (!reset_mpu401(devc)) - { - printk(KERN_WARNING "mpu401: Device didn't respond\n"); - sound_unload_mididev(m); - return; - } - if (!devc->shared_irq) - if (snd_set_irq_handler(devc->irq, mpuintr, "mpu401", devc->osp) < 0) - { - printk(KERN_WARNING "mpu401: Failed to allocate IRQ%d\n", devc->irq); - sound_unload_mididev(m); - return; - } - save_flags(flags); - cli(); - mpu401_chk_version(m, devc); - if (devc->version == 0) - mpu401_chk_version(m, devc); - restore_flags(flags); - } + { + /* Verify the hardware again */ + if (!reset_mpu401(devc)) + { + printk(KERN_WARNING "mpu401: Device didn't respond\n"); + sound_unload_mididev(m); + return; + } + if (!devc->shared_irq) + { + if (request_irq(devc->irq, mpuintr, 0, "mpu401", (void *)m) < 0) + { + printk(KERN_WARNING "mpu401: Failed to allocate IRQ%d\n", devc->irq); + sound_unload_mididev(m); + return; + } + } + save_flags(flags); + cli(); + mpu401_chk_version(m, devc); + if (devc->version == 0) + mpu401_chk_version(m, devc); + restore_flags(flags); + } request_region(hw_config->io_base, 2, "mpu401"); if (devc->version != 0) @@ -1061,36 +1011,32 @@ attach_mpu401(struct address_info *hw_config) devc->capabilities |= MPU_CAP_INTLG; /* Supports intelligent mode */ - mpu401_synth_operations[m] = (struct synth_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct synth_operations))); - sound_mem_sizes[sound_nblocks] = sizeof(struct synth_operations); - - if (sound_nblocks < 1024) - sound_nblocks++;; + mpu401_synth_operations[m] = (struct synth_operations *)kmalloc(sizeof(struct synth_operations), GFP_KERNEL); if (mpu401_synth_operations[m] == NULL) - { - sound_unload_mididev(m); - printk(KERN_ERR "mpu401: Can't allocate memory\n"); - return; - } + { + sound_unload_mididev(m); + printk(KERN_ERR "mpu401: Can't allocate memory\n"); + return; + } if (!(devc->capabilities & MPU_CAP_INTLG)) /* No intelligent mode */ - { - memcpy((char *) mpu401_synth_operations[m], - (char *) &std_midi_synth, + { + memcpy((char *) mpu401_synth_operations[m], + (char *) &std_midi_synth, sizeof(struct synth_operations)); - } else - { - memcpy((char *) mpu401_synth_operations[m], - (char *) &mpu401_synth_proto, + } + else + { + memcpy((char *) mpu401_synth_operations[m], + (char *) &mpu401_synth_proto, sizeof(struct synth_operations)); - } + } memcpy((char *) &mpu401_midi_operations[m], (char *) &mpu401_midi_proto, sizeof(struct midi_operations)); - mpu401_midi_operations[m].converter = - mpu401_synth_operations[m]; + mpu401_midi_operations[m].converter = mpu401_synth_operations[m]; memcpy((char *) &mpu_synth_info[m], (char *) &mpu_synth_info_proto, @@ -1099,37 +1045,36 @@ attach_mpu401(struct address_info *hw_config) n_mpu_devs++; if (devc->version == 0x20 && devc->revision >= 0x07) /* MusicQuest interface */ - { - int ports = (devc->revision & 0x08) ? 32 : 16; - - devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_SMPTE | - MPU_CAP_CLS | MPU_CAP_2PORT; - - revision_char = (devc->revision == 0x7f) ? 'M' : ' '; - sprintf(mpu_synth_info[m].name, - "MQX-%d%c MIDI Interface #%d", - ports, - revision_char, - n_mpu_devs); - } else - { - - revision_char = devc->revision ? devc->revision + '@' : ' '; - if ((int) devc->revision > ('Z' - '@')) - revision_char = '+'; - - devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_FSK; - - if (hw_config->name) - sprintf(mpu_synth_info[m].name, "%s (MPU401)", hw_config->name); - else - sprintf(mpu_synth_info[m].name, - "MPU-401 %d.%d%c Midi interface #%d", - (int) (devc->version & 0xf0) >> 4, - devc->version & 0x0f, - revision_char, - n_mpu_devs); - } + { + int ports = (devc->revision & 0x08) ? 32 : 16; + + devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_SMPTE | + MPU_CAP_CLS | MPU_CAP_2PORT; + + revision_char = (devc->revision == 0x7f) ? 'M' : ' '; + sprintf(mpu_synth_info[m].name, "MQX-%d%c MIDI Interface #%d", + ports, + revision_char, + n_mpu_devs); + } + else + { + revision_char = devc->revision ? devc->revision + '@' : ' '; + if ((int) devc->revision > ('Z' - '@')) + revision_char = '+'; + + devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_FSK; + + if (hw_config->name) + sprintf(mpu_synth_info[m].name, "%s (MPU401)", hw_config->name); + else + sprintf(mpu_synth_info[m].name, + "MPU-401 %d.%d%c Midi interface #%d", + (int) (devc->version & 0xf0) >> 4, + devc->version & 0x0f, + revision_char, + n_mpu_devs); + } strcpy(mpu401_midi_operations[m].info.name, mpu_synth_info[m].name); @@ -1137,24 +1082,21 @@ attach_mpu401(struct address_info *hw_config) conf_printf(mpu_synth_info[m].name, hw_config); mpu401_synth_operations[m]->midi_dev = devc->devno = m; - mpu401_synth_operations[devc->devno]->info = - &mpu_synth_info[devc->devno]; + mpu401_synth_operations[devc->devno]->info = &mpu_synth_info[devc->devno]; if (devc->capabilities & MPU_CAP_INTLG) /* Intelligent mode */ hw_config->slots[2] = mpu_timer_init(m); - irq2dev[devc->irq] = m; midi_devs[m] = &mpu401_midi_operations[devc->devno]; hw_config->slots[1] = m; sequencer_init(); } -static int -reset_mpu401(struct mpu_config *devc) +static int reset_mpu401(struct mpu_config *devc) { - unsigned long flags; - int ok, timeout, n; - int timeout_limit; + unsigned long flags; + int ok, timeout, n; + int timeout_limit; /* * Send the RESET command. Try again if no success at the first time. @@ -1167,30 +1109,30 @@ reset_mpu401(struct mpu_config *devc) devc->initialized = 1; for (n = 0; n < 2 && !ok; n++) - { - for (timeout = timeout_limit; timeout > 0 && !ok; timeout--) + { + for (timeout = timeout_limit; timeout > 0 && !ok; timeout--) ok = output_ready(devc); - write_command(devc, MPU_RESET); /* + write_command(devc, MPU_RESET); /* * Send MPU-401 RESET Command */ - /* - * Wait at least 25 msec. This method is not accurate so let's make the - * loop bit longer. Cannot sleep since this is called during boot. - */ + /* + * Wait at least 25 msec. This method is not accurate so let's make the + * loop bit longer. Cannot sleep since this is called during boot. + */ - for (timeout = timeout_limit * 2; timeout > 0 && !ok; timeout--) - { - save_flags(flags); - cli(); - if (input_avail(devc)) - if (read_data(devc) == MPU_ACK) - ok = 1; - restore_flags(flags); - } + for (timeout = timeout_limit * 2; timeout > 0 && !ok; timeout--) + { + save_flags(flags); + cli(); + if (input_avail(devc)) + if (read_data(devc) == MPU_ACK) + ok = 1; + restore_flags(flags); + } - } + } devc->m_state = ST_INIT; devc->m_ptr = 0; @@ -1201,43 +1143,37 @@ reset_mpu401(struct mpu_config *devc) return ok; } -static void -set_uart_mode(int dev, struct mpu_config *devc, int arg) +static void set_uart_mode(int dev, struct mpu_config *devc, int arg) { if (!arg && (devc->capabilities & MPU_CAP_INTLG)) - { - return; - } + return; if ((devc->uart_mode == 0) == (arg == 0)) - { - return; /* Already set */ - } + return; /* Already set */ reset_mpu401(devc); /* This exits the uart mode */ if (arg) - { - if (mpu_cmd(dev, UART_MODE_ON, 0) < 0) - { - printk("MPU%d: Can't enter UART mode\n", devc->devno); - devc->uart_mode = 0; - return; - } - } + { + if (mpu_cmd(dev, UART_MODE_ON, 0) < 0) + { + printk(KERN_ERR "mpu401: Can't enter UART mode\n"); + devc->uart_mode = 0; + return; + } + } devc->uart_mode = arg; } -int -probe_mpu401(struct address_info *hw_config) +int probe_mpu401(struct address_info *hw_config) { - int ok = 0; + int ok = 0; struct mpu_config tmp_devc; if (check_region(hw_config->io_base, 2)) - { - printk("\n\nmpu401.c: I/O port %x already in use\n\n", hw_config->io_base); - return 0; - } + { + printk(KERN_ERR "mpu401: I/O port %x already in use\n\n", hw_config->io_base); + return 0; + } tmp_devc.base = hw_config->io_base; tmp_devc.irq = hw_config->irq; tmp_devc.initialized = 0; @@ -1248,27 +1184,32 @@ probe_mpu401(struct address_info *hw_config) return 1; if (inb(hw_config->io_base + 1) == 0xff) - { - DDB(printk("MPU401: Port %x looks dead.\n", hw_config->io_base)); - return 0; /* Just bus float? */ - } + { + DDB(printk("MPU401: Port %x looks dead.\n", hw_config->io_base)); + return 0; /* Just bus float? */ + } ok = reset_mpu401(&tmp_devc); if (!ok) - { - DDB(printk("MPU401: Reset failed on port %x\n", hw_config->io_base)); - } + { + DDB(printk("MPU401: Reset failed on port %x\n", hw_config->io_base)); + } return ok; } -void -unload_mpu401(struct address_info *hw_config) +void unload_mpu401(struct address_info *hw_config) { + void *p; + int n=hw_config->slots[1]; + release_region(hw_config->io_base, 2); if (hw_config->always_detect == 0 && hw_config->irq > 0) - snd_release_irq(hw_config->irq); - sound_unload_mididev(hw_config->slots[1]); + free_irq(hw_config->irq, (void *)n); + p=mpu401_synth_operations[n]; + sound_unload_mididev(n); sound_unload_timerdev(hw_config->slots[2]); + if(p) + kfree(p); } /***************************************************** @@ -1285,23 +1226,20 @@ static volatile unsigned long curr_ticks, curr_clocks; static unsigned long prev_event_time; static int metronome_mode; -static unsigned long -clocks2ticks(unsigned long clocks) +static unsigned long clocks2ticks(unsigned long clocks) { /* - * The MPU-401 supports just a limited set of possible timebase values. - * Since the applications require more choices, the driver has to - * program the HW to do its best and to convert between the HW and - * actual timebases. + * The MPU-401 supports just a limited set of possible timebase values. + * Since the applications require more choices, the driver has to + * program the HW to do its best and to convert between the HW and + * actual timebases. */ - return ((clocks * curr_timebase) + (hw_timebase / 2)) / hw_timebase; } -static void -set_timebase(int midi_dev, int val) +static void set_timebase(int midi_dev, int val) { - int hw_val; + int hw_val; if (val < 48) val = 48; @@ -1314,19 +1252,18 @@ set_timebase(int midi_dev, int val) hw_val = max_timebase; if (mpu_cmd(midi_dev, 0xC0 | (hw_val & 0x0f), 0) < 0) - { - printk("MPU: Can't set HW timebase to %d\n", hw_val * 24); - return; - } + { + printk(KERN_WARNING "mpu401: Can't set HW timebase to %d\n", hw_val * 24); + return; + } hw_timebase = hw_val * 24; curr_timebase = val; } -static void -tmr_reset(void) +static void tmr_reset(void) { - unsigned long flags; + unsigned long flags; save_flags(flags); cli(); @@ -1336,8 +1273,7 @@ tmr_reset(void) restore_flags(flags); } -static void -set_timer_mode(int midi_dev) +static void set_timer_mode(int midi_dev) { if (timer_mode & TMR_MODE_CLS) mpu_cmd(midi_dev, 0x3c, 0); /* Use CLS sync */ @@ -1345,31 +1281,31 @@ set_timer_mode(int midi_dev) mpu_cmd(midi_dev, 0x3d, 0); /* Use SMPTE sync */ if (timer_mode & TMR_INTERNAL) - { + { mpu_cmd(midi_dev, 0x80, 0); /* Use MIDI sync */ - } else - { - if (timer_mode & (TMR_MODE_MIDI | TMR_MODE_CLS)) - { - mpu_cmd(midi_dev, 0x82, 0); /* Use MIDI sync */ - mpu_cmd(midi_dev, 0x91, 0); /* Enable ext MIDI ctrl */ - } else if (timer_mode & TMR_MODE_FSK) - mpu_cmd(midi_dev, 0x81, 0); /* Use FSK sync */ - } + } + else + { + if (timer_mode & (TMR_MODE_MIDI | TMR_MODE_CLS)) + { + mpu_cmd(midi_dev, 0x82, 0); /* Use MIDI sync */ + mpu_cmd(midi_dev, 0x91, 0); /* Enable ext MIDI ctrl */ + } + else if (timer_mode & TMR_MODE_FSK) + mpu_cmd(midi_dev, 0x81, 0); /* Use FSK sync */ + } } -static void -stop_metronome(int midi_dev) +static void stop_metronome(int midi_dev) { mpu_cmd(midi_dev, 0x84, 0); /* Disable metronome */ } -static void -setup_metronome(int midi_dev) +static void setup_metronome(int midi_dev) { - int numerator, denominator; - int clks_per_click, num_32nds_per_beat; - int beats_per_measure; + int numerator, denominator; + int clks_per_click, num_32nds_per_beat; + int beats_per_measure; numerator = ((unsigned) metronome_mode >> 24) & 0xff; denominator = ((unsigned) metronome_mode >> 16) & 0xff; @@ -1380,15 +1316,14 @@ setup_metronome(int midi_dev) if (!metronome_mode) mpu_cmd(midi_dev, 0x84, 0); /* Disable metronome */ else - { - mpu_cmd(midi_dev, 0xE4, clks_per_click); - mpu_cmd(midi_dev, 0xE6, beats_per_measure); - mpu_cmd(midi_dev, 0x83, 0); /* Enable metronome without accents */ - } + { + mpu_cmd(midi_dev, 0xE4, clks_per_click); + mpu_cmd(midi_dev, 0xE6, beats_per_measure); + mpu_cmd(midi_dev, 0x83, 0); /* Enable metronome without accents */ + } } -static int -mpu_start_timer(int midi_dev) +static int mpu_start_timer(int midi_dev) { tmr_reset(); set_timer_mode(midi_dev); @@ -1397,25 +1332,24 @@ mpu_start_timer(int midi_dev) return TIMER_NOT_ARMED; /* Already running */ if (timer_mode & TMR_INTERNAL) - { - mpu_cmd(midi_dev, 0x02, 0); /* Send MIDI start */ - tmr_running = 1; - return TIMER_NOT_ARMED; - } else - { - mpu_cmd(midi_dev, 0x35, 0); /* Enable mode messages to PC */ - mpu_cmd(midi_dev, 0x38, 0); /* Enable sys common messages to PC */ - mpu_cmd(midi_dev, 0x39, 0); /* Enable real time messages to PC */ - mpu_cmd(midi_dev, 0x97, 0); /* Enable system exclusive messages to PC */ - } - + { + mpu_cmd(midi_dev, 0x02, 0); /* Send MIDI start */ + tmr_running = 1; + return TIMER_NOT_ARMED; + } + else + { + mpu_cmd(midi_dev, 0x35, 0); /* Enable mode messages to PC */ + mpu_cmd(midi_dev, 0x38, 0); /* Enable sys common messages to PC */ + mpu_cmd(midi_dev, 0x39, 0); /* Enable real time messages to PC */ + mpu_cmd(midi_dev, 0x97, 0); /* Enable system exclusive messages to PC */ + } return TIMER_ARMED; } -static int -mpu_timer_open(int dev, int mode) +static int mpu_timer_open(int dev, int mode) { - int midi_dev = sound_timer_devs[dev]->devlink; + int midi_dev = sound_timer_devs[dev]->devlink; if (timer_open) return -EBUSY; @@ -1435,10 +1369,9 @@ mpu_timer_open(int dev, int mode) return 0; } -static void -mpu_timer_close(int dev) +static void mpu_timer_close(int dev) { - int midi_dev = sound_timer_devs[dev]->devlink; + int midi_dev = sound_timer_devs[dev]->devlink; timer_open = tmr_running = 0; mpu_cmd(midi_dev, 0x15, 0); /* Stop all */ @@ -1447,86 +1380,80 @@ mpu_timer_close(int dev) stop_metronome(midi_dev); } -static int -mpu_timer_event(int dev, unsigned char *event) +static int mpu_timer_event(int dev, unsigned char *event) { - unsigned char command = event[1]; - unsigned long parm = *(unsigned int *) &event[4]; - int midi_dev = sound_timer_devs[dev]->devlink; + unsigned char command = event[1]; + unsigned long parm = *(unsigned int *) &event[4]; + int midi_dev = sound_timer_devs[dev]->devlink; switch (command) - { - case TMR_WAIT_REL: - parm += prev_event_time; - case TMR_WAIT_ABS: - if (parm > 0) - { - long time; - - if (parm <= curr_ticks) /* It's the time */ - return TIMER_NOT_ARMED; - - time = parm; - next_event_time = prev_event_time = time; - - return TIMER_ARMED; - } - break; - - case TMR_START: - if (tmr_running) - break; - return mpu_start_timer(midi_dev); - break; - - case TMR_STOP: - mpu_cmd(midi_dev, 0x01, 0); /* Send MIDI stop */ - stop_metronome(midi_dev); - tmr_running = 0; - break; - - case TMR_CONTINUE: - if (tmr_running) - break; - mpu_cmd(midi_dev, 0x03, 0); /* Send MIDI continue */ - setup_metronome(midi_dev); - tmr_running = 1; - break; - - case TMR_TEMPO: - if (parm) - { - if (parm < 8) - parm = 8; - if (parm > 250) - parm = 250; - - if (mpu_cmd(midi_dev, 0xE0, parm) < 0) - printk("MPU: Can't set tempo to %d\n", (int) parm); - curr_tempo = parm; - } - break; - - case TMR_ECHO: - seq_copy_to_input(event, 8); - break; - - case TMR_TIMESIG: - if (metronome_mode) /* Metronome enabled */ - { - metronome_mode = parm; - setup_metronome(midi_dev); - } - break; - - default:; - } + { + case TMR_WAIT_REL: + parm += prev_event_time; + case TMR_WAIT_ABS: + if (parm > 0) + { + long time; + + if (parm <= curr_ticks) /* It's the time */ + return TIMER_NOT_ARMED; + time = parm; + next_event_time = prev_event_time = time; + + return TIMER_ARMED; + } + break; + + case TMR_START: + if (tmr_running) + break; + return mpu_start_timer(midi_dev); + + case TMR_STOP: + mpu_cmd(midi_dev, 0x01, 0); /* Send MIDI stop */ + stop_metronome(midi_dev); + tmr_running = 0; + break; + case TMR_CONTINUE: + if (tmr_running) + break; + mpu_cmd(midi_dev, 0x03, 0); /* Send MIDI continue */ + setup_metronome(midi_dev); + tmr_running = 1; + break; + + case TMR_TEMPO: + if (parm) + { + if (parm < 8) + parm = 8; + if (parm > 250) + parm = 250; + if (mpu_cmd(midi_dev, 0xE0, parm) < 0) + printk(KERN_WARNING "mpu401: Can't set tempo to %d\n", (int) parm); + curr_tempo = parm; + } + break; + + case TMR_ECHO: + seq_copy_to_input(event, 8); + break; + + case TMR_TIMESIG: + if (metronome_mode) /* Metronome enabled */ + { + metronome_mode = parm; + setup_metronome(midi_dev); + } + break; + + default: + } return TIMER_NOT_ARMED; } -static unsigned long -mpu_timer_get_time(int dev) +static unsigned long mpu_timer_get_time(int dev) { if (!timer_open) return 0; @@ -1534,128 +1461,115 @@ mpu_timer_get_time(int dev) return curr_ticks; } -static int -mpu_timer_ioctl(int dev, - unsigned int command, caddr_t arg) +static int mpu_timer_ioctl(int dev, unsigned int command, caddr_t arg) { - int midi_dev = sound_timer_devs[dev]->devlink; + int midi_dev = sound_timer_devs[dev]->devlink; switch (command) - { - case SNDCTL_TMR_SOURCE: - { - int parm; - - parm = *(int *) arg; - parm &= timer_caps; - - if (parm != 0) - { - timer_mode = parm; - - if (timer_mode & TMR_MODE_CLS) - mpu_cmd(midi_dev, 0x3c, 0); /* Use CLS sync */ - else if (timer_mode & TMR_MODE_SMPTE) - mpu_cmd(midi_dev, 0x3d, 0); /* Use SMPTE sync */ - } - return (*(int *) arg = timer_mode); - } - break; - - case SNDCTL_TMR_START: - mpu_start_timer(midi_dev); - return 0; - break; - - case SNDCTL_TMR_STOP: - tmr_running = 0; - mpu_cmd(midi_dev, 0x01, 0); /* Send MIDI stop */ - stop_metronome(midi_dev); - return 0; - break; - - case SNDCTL_TMR_CONTINUE: - if (tmr_running) - return 0; - tmr_running = 1; - mpu_cmd(midi_dev, 0x03, 0); /* Send MIDI continue */ - return 0; - break; - - case SNDCTL_TMR_TIMEBASE: - { - int val; - - val = *(int *) arg; - if (val) - set_timebase(midi_dev, val); - - return (*(int *) arg = curr_timebase); - } - break; - - case SNDCTL_TMR_TEMPO: - { - int val; - int ret; - - val = *(int *) arg; - - if (val) - { - if (val < 8) - val = 8; - if (val > 250) - val = 250; - if ((ret = mpu_cmd(midi_dev, 0xE0, val)) < 0) - { - printk("MPU: Can't set tempo to %d\n", (int) val); - return ret; - } - curr_tempo = val; - } - return (*(int *) arg = curr_tempo); - } - break; - - case SNDCTL_SEQ_CTRLRATE: - { - int val; - - val = *(int *) arg; - if (val != 0) /* Can't change */ - return -EINVAL; - - return (*(int *) arg = ((curr_tempo * curr_timebase) + 30) / 60); - } - break; - - case SNDCTL_SEQ_GETTIME: - return (*(int *) arg = curr_ticks); - break; - - case SNDCTL_TMR_METRONOME: - metronome_mode = *(int *) arg; - setup_metronome(midi_dev); - return 0; - break; - - default:; - } + { + case SNDCTL_TMR_SOURCE: + { + int parm; + + parm = *(int *) arg; + parm &= timer_caps; + if (parm != 0) + { + timer_mode = parm; + + if (timer_mode & TMR_MODE_CLS) + mpu_cmd(midi_dev, 0x3c, 0); /* Use CLS sync */ + else if (timer_mode & TMR_MODE_SMPTE) + mpu_cmd(midi_dev, 0x3d, 0); /* Use SMPTE sync */ + } + return (*(int *) arg = timer_mode); + } + break; + + case SNDCTL_TMR_START: + mpu_start_timer(midi_dev); + return 0; + + case SNDCTL_TMR_STOP: + tmr_running = 0; + mpu_cmd(midi_dev, 0x01, 0); /* Send MIDI stop */ + stop_metronome(midi_dev); + return 0; + + case SNDCTL_TMR_CONTINUE: + if (tmr_running) + return 0; + tmr_running = 1; + mpu_cmd(midi_dev, 0x03, 0); /* Send MIDI continue */ + return 0; + + case SNDCTL_TMR_TIMEBASE: + { + int val; + + val = *(int *) arg; + if (val) + set_timebase(midi_dev, val); + return (*(int *) arg = curr_timebase); + } + break; + + case SNDCTL_TMR_TEMPO: + { + int val; + int ret; + + val = *(int *) arg; + + if (val) + { + if (val < 8) + val = 8; + if (val > 250) + val = 250; + if ((ret = mpu_cmd(midi_dev, 0xE0, val)) < 0) + { + printk(KERN_WARNING "mpu401: Can't set tempo to %d\n", (int) val); + return ret; + } + curr_tempo = val; + } + return (*(int *) arg = curr_tempo); + } + break; + + case SNDCTL_SEQ_CTRLRATE: + { + int val; + + val = *(int *) arg; + if (val != 0) /* Can't change */ + return -EINVAL; + return (*(int *) arg = ((curr_tempo * curr_timebase) + 30) / 60); + } + break; + + case SNDCTL_SEQ_GETTIME: + return (*(int *) arg = curr_ticks); + + case SNDCTL_TMR_METRONOME: + metronome_mode = *(int *) arg; + setup_metronome(midi_dev); + return 0; + + default: + } return -EINVAL; } -static void -mpu_timer_arm(int dev, long time) +static void mpu_timer_arm(int dev, long time) { if (time < 0) time = curr_ticks + 1; else if (time <= curr_ticks) /* It's the time */ return; - next_event_time = prev_event_time = time; - return; } @@ -1672,10 +1586,8 @@ static struct sound_timer_operations mpu_timer = mpu_timer_arm }; -static void -mpu_timer_interrupt(void) +static void mpu_timer_interrupt(void) { - if (!timer_open) return; @@ -1686,10 +1598,10 @@ mpu_timer_interrupt(void) curr_ticks = clocks2ticks(curr_clocks); if (curr_ticks >= next_event_time) - { - next_event_time = (unsigned long) -1; - sequencer_timer(0); - } + { + next_event_time = (unsigned long) -1; + sequencer_timer(0); + } } static void timer_ext_event(struct mpu_config *devc, int event, int parm) @@ -1706,8 +1618,9 @@ static void timer_ext_event(struct mpu_config *devc, int event, int parm) break; case TMR_START: - printk("Ext MIDI start\n"); + printk("Ext MIDI start\n"); if (!tmr_running) + { if (timer_mode & TMR_EXTERNAL) { tmr_running = 1; @@ -1715,6 +1628,7 @@ static void timer_ext_event(struct mpu_config *devc, int event, int parm) next_event_time = 0; STORE(SEQ_START_TIMER()); } + } break; case TMR_STOP: diff --git a/drivers/sound/opl3.c b/drivers/sound/opl3.c index 36734c2e7..cd768801f 100644 --- a/drivers/sound/opl3.c +++ b/drivers/sound/opl3.c @@ -2,25 +2,30 @@ * sound/opl3.c * * A low level driver for Yamaha YM3812 and OPL-3 -chips - */ -/* + * +* * Copyright (C) by Hannu Savolainen 1993-1997 * * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) * Version 2 (June 1991). See the "COPYING" file distributed with this software * for more info. + * + * + * Changes + * Thomas Sailer ioctl code reworked (vmalloc/vfree removed) + * Alan Cox modularisation, fixed sound_mem allocs. + * + * Status + * Believed to work. Badly needs rewriting a bit to support multiple + * OPL3 devices. */ -/* - * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) - */ + #include <linux/config.h> #include <linux/module.h> #include <linux/delay.h> /* * Major improvements to the FM handling 30AUG92 by Rob Hooft, - */ -/* * hooft@chem.ruu.nl */ @@ -109,32 +114,32 @@ static int opl3_ioctl(int dev, unsigned int cmd, caddr_t arg) struct sbi_instrument ins; switch (cmd) { - case SNDCTL_FM_LOAD_INSTR: - printk(KERN_WARNING "Warning: Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. Fix the program.\n"); - if (__copy_from_user(&ins, arg, sizeof(ins))) - return -EFAULT; - if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR) { - printk("FM Error: Invalid instrument number %d\n", ins.channel); - return -EINVAL; - } - return store_instr(ins.channel, &ins); + case SNDCTL_FM_LOAD_INSTR: + printk(KERN_WARNING "Warning: Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. Fix the program.\n"); + if (copy_from_user(&ins, arg, sizeof(ins))) + return -EFAULT; + if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR) { + printk(KERN_WARNING "FM Error: Invalid instrument number %d\n", ins.channel); + return -EINVAL; + } + return store_instr(ins.channel, &ins); - case SNDCTL_SYNTH_INFO: - devc->fm_info.nr_voices = (devc->nr_voice == 12) ? 6 : devc->nr_voice; - if (__copy_to_user(arg, &devc->fm_info, sizeof(devc->fm_info))) - return -EFAULT; - return 0; + case SNDCTL_SYNTH_INFO: + devc->fm_info.nr_voices = (devc->nr_voice == 12) ? 6 : devc->nr_voice; + if (copy_to_user(arg, &devc->fm_info, sizeof(devc->fm_info))) + return -EFAULT; + return 0; - case SNDCTL_SYNTH_MEMAVL: - return 0x7fffffff; + case SNDCTL_SYNTH_MEMAVL: + return 0x7fffffff; - case SNDCTL_FM_4OP_ENABLE: - if (devc->model == 2) - enter_4op_mode(); - return 0; + case SNDCTL_FM_4OP_ENABLE: + if (devc->model == 2) + enter_4op_mode(); + return 0; - default: - return -EINVAL; + default: + return -EINVAL; } } @@ -151,8 +156,8 @@ int opl3_detect(int ioaddr, int *osp) * Note2! The chip is initialized if detected. */ - unsigned char stat1, signature; - int i; + unsigned char stat1, signature; + int i; if (devc != NULL) { @@ -160,10 +165,7 @@ int opl3_detect(int ioaddr, int *osp) return 0; } - devc = (struct opl_devinfo *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(*devc))); - sound_mem_sizes[sound_nblocks] = sizeof(*devc); - if (sound_nblocks < 1024) - sound_nblocks++;; + devc = (struct opl_devinfo *)kmalloc(sizeof(*devc), GFP_KERNEL); if (devc == NULL) { @@ -333,7 +335,7 @@ static char fm_volume_table[128] = static void calc_vol(unsigned char *regbyte, int volume, int main_vol) { - int level = (~*regbyte & 0x3f); + int level = (~*regbyte & 0x3f); if (main_vol > 127) main_vol = 127; @@ -814,7 +816,8 @@ static int opl3_load_patch(int dev, int format, const char *addr, return -EINVAL; } - copy_from_user(&((char *) &ins)[offs], &(addr)[offs], sizeof(ins) - offs); + if(copy_from_user(&((char *) &ins)[offs], &(addr)[offs], sizeof(ins) - offs)) + return -EFAULT; if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR) { @@ -1199,6 +1202,7 @@ void cleanup_module(void) { if (devc) { + kfree(devc); devc = NULL; sound_unload_synthdev(me); } diff --git a/drivers/sound/pas2_card.c b/drivers/sound/pas2_card.c index 656034d54..bf95b84d5 100644 --- a/drivers/sound/pas2_card.c +++ b/drivers/sound/pas2_card.c @@ -164,7 +164,7 @@ static int config_pas_hw(struct address_info *hw_config) } else { - if (snd_set_irq_handler(pas_irq, pasintr, "PAS16", hw_config->osp) < 0) + if (request_irq(pas_irq, pasintr, "PAS16", 0, NULL) < 0) ok = 0; } } @@ -355,7 +355,7 @@ void unload_pas(struct address_info *hw_config) { sound_free_dma(hw_config->dma); - snd_release_irq(hw_config->irq); + free_irq(hw_config->irq, NULL); } #ifdef MODULE diff --git a/drivers/sound/pss.c b/drivers/sound/pss.c index 9d62dd485..7a0a2e66e 100644 --- a/drivers/sound/pss.c +++ b/drivers/sound/pss.c @@ -11,7 +11,8 @@ * for more info. */ /* - * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) + * Thomas Sailer ioctl code reworked (vmalloc/vfree removed) + * Alan Cox modularisation, clean up. */ #include <linux/config.h> #include <linux/module.h> @@ -62,12 +63,12 @@ static int pss_synthLen = 0; #endif typedef struct pss_confdata - { - int base; - int irq; - int dma; - int *osp; - } +{ + int base; + int irq; + int dma; + int *osp; +} pss_confdata; @@ -77,12 +78,11 @@ static pss_confdata *devc = &pss_data; static int pss_initialized = 0; static int nonstandard_microcode = 0; -static void -pss_write(int data) +static void pss_write(int data) { - int i, limit; + int i, limit; - limit = jiffies + 10; /* The timeout is 0.1 seconds */ + limit = jiffies + HZ/10; /* The timeout is 0.1 seconds */ /* * Note! the i<5000000 is an emergency exit. The dsp_command() is sometimes * called while interrupts are disabled. This means that the timer is @@ -92,21 +92,20 @@ pss_write(int data) */ for (i = 0; i < 5000000 && jiffies < limit; i++) - { - if (inw(devc->base + PSS_STATUS) & PSS_WRITE_EMPTY) - { - outw(devc->base + PSS_DATA, data); - return; - } - } - printk("PSS: DSP Command (%04x) Timeout.\n", data); + { + if (inw(devc->base + PSS_STATUS) & PSS_WRITE_EMPTY) + { + outw(devc->base + PSS_DATA, data); + return; + } + } + printk(KERN_ERR "PSS: DSP Command (%04x) Timeout.\n", data); } -int -probe_pss(struct address_info *hw_config) +int probe_pss(struct address_info *hw_config) { - unsigned short id; - int irq, dma; + unsigned short id; + int irq, dma; devc->base = hw_config->io_base; irq = devc->irq = hw_config->irq; @@ -129,8 +128,7 @@ probe_pss(struct address_info *hw_config) return 1; } -static int -set_irq(pss_confdata * devc, int dev, int irq) +static int set_irq(pss_confdata * devc, int dev, int irq) { static unsigned short irq_bits[16] = { @@ -148,16 +146,15 @@ set_irq(pss_confdata * devc, int dev, int irq) tmp = inw(REG(dev)) & ~0x38; /* Load confreg, mask IRQ bits out */ if ((bits = irq_bits[irq]) == 0 && irq != 0) - { - printk("PSS: Invalid IRQ %d\n", irq); - return 0; - } + { + printk(KERN_ERR "PSS: Invalid IRQ %d\n", irq); + return 0; + } outw(tmp | bits, REG(dev)); return 1; } -static int -set_io_base(pss_confdata * devc, int dev, int base) +static int set_io_base(pss_confdata * devc, int dev, int base) { unsigned short tmp = inw(REG(dev)) & 0x003f; unsigned short bits = (base & 0x0ffc) << 4; @@ -167,8 +164,7 @@ set_io_base(pss_confdata * devc, int dev, int base) return 1; } -static int -set_dma(pss_confdata * devc, int dev, int dma) +static int set_dma(pss_confdata * devc, int dev, int dma) { static unsigned short dma_bits[8] = { @@ -184,150 +180,139 @@ set_dma(pss_confdata * devc, int dev, int dma) tmp = inw(REG(dev)) & ~0x07; /* Load confreg, mask DMA bits out */ if ((bits = dma_bits[dma]) == 0 && dma != 4) - { - printk("PSS: Invalid DMA %d\n", dma); + { + printk(KERN_ERR "PSS: Invalid DMA %d\n", dma); return 0; - } + } outw(tmp | bits, REG(dev)); return 1; } -static int -pss_reset_dsp(pss_confdata * devc) +static int pss_reset_dsp(pss_confdata * devc) { - unsigned long i, limit = jiffies + 10; + unsigned long i, limit = jiffies + HZ/10; outw(0x2000, REG(PSS_CONTROL)); - - for (i = 0; i < 32768 && jiffies < limit; i++) + for (i = 0; i < 32768 && (limit-jiffies >= 0); i++) inw(REG(PSS_CONTROL)); - outw(0x0000, REG(PSS_CONTROL)); - return 1; } -static int -pss_put_dspword(pss_confdata * devc, unsigned short word) +static int pss_put_dspword(pss_confdata * devc, unsigned short word) { - int i, val; + int i, val; for (i = 0; i < 327680; i++) - { - val = inw(REG(PSS_STATUS)); - if (val & PSS_WRITE_EMPTY) - { - outw(word, REG(PSS_DATA)); - return 1; - } - } + { + val = inw(REG(PSS_STATUS)); + if (val & PSS_WRITE_EMPTY) + { + outw(word, REG(PSS_DATA)); + return 1; + } + } return 0; } -static int -pss_get_dspword(pss_confdata * devc, unsigned short *word) +static int pss_get_dspword(pss_confdata * devc, unsigned short *word) { - int i, val; + int i, val; for (i = 0; i < 327680; i++) - { - val = inw(REG(PSS_STATUS)); - if (val & PSS_READ_FULL) - { - *word = inw(REG(PSS_DATA)); - return 1; - } - } - + { + val = inw(REG(PSS_STATUS)); + if (val & PSS_READ_FULL) + { + *word = inw(REG(PSS_DATA)); + return 1; + } + } return 0; } -static int -pss_download_boot(pss_confdata * devc, unsigned char *block, int size, int flags) +static int pss_download_boot(pss_confdata * devc, unsigned char *block, int size, int flags) { - int i, limit, val, count; + int i, limit, val, count; if (flags & CPF_FIRST) - { + { /*_____ Warn DSP software that a boot is coming */ - outw(0x00fe, REG(PSS_DATA)); - - limit = jiffies + 10; + outw(0x00fe, REG(PSS_DATA)); - for (i = 0; i < 32768 && jiffies < limit; i++) - if (inw(REG(PSS_DATA)) == 0x5500) - break; - - outw(*block++, REG(PSS_DATA)); + limit = jiffies + HZ/10; + for (i = 0; i < 32768 && jiffies < limit; i++) + if (inw(REG(PSS_DATA)) == 0x5500) + break; - pss_reset_dsp(devc); - } + outw(*block++, REG(PSS_DATA)); + pss_reset_dsp(devc); + } count = 1; while (1) - { - int j; + { + int j; - for (j = 0; j < 327670; j++) - { + for (j = 0; j < 327670; j++) + { /*_____ Wait for BG to appear */ - if (inw(REG(PSS_STATUS)) & PSS_FLAG3) - break; - } - - if (j == 327670) - { - /* It's ok we timed out when the file was empty */ - if (count >= size && flags & CPF_LAST) - break; - else - { - printk("\nPSS: Download timeout problems, byte %d=%d\n", count, size); - return 0; - } - } + if (inw(REG(PSS_STATUS)) & PSS_FLAG3) + break; + } + + if (j == 327670) + { + /* It's ok we timed out when the file was empty */ + if (count >= size && flags & CPF_LAST) + break; + else + { + printk("\nPSS: Download timeout problems, byte %d=%d\n", count, size); + return 0; + } + } /*_____ Send the next byte */ - outw(*block++, REG(PSS_DATA)); - count++; - } + outw(*block++, REG(PSS_DATA)); + count++; + } if (flags & CPF_LAST) - { + { /*_____ Why */ - outw(0, REG(PSS_DATA)); - - limit = jiffies + 10; - for (i = 0; i < 32768 && jiffies < limit; i++) - val = inw(REG(PSS_STATUS)); - - limit = jiffies + 10; - for (i = 0; i < 32768 && jiffies < limit; i++) - { - val = inw(REG(PSS_STATUS)); - if (val & 0x4000) - break; - } - - /* now read the version */ - for (i = 0; i < 32000; i++) - { - val = inw(REG(PSS_STATUS)); - if (val & PSS_READ_FULL) - break; - } - if (i == 32000) - return 0; - - val = inw(REG(PSS_DATA)); - /* printk( "<PSS: microcode version %d.%d loaded>", val/16, val % 16); */ - } + outw(0, REG(PSS_DATA)); + + limit = jiffies + HZ/10; + for (i = 0; i < 32768 && (limit - jiffies >= 0); i++) + val = inw(REG(PSS_STATUS)); + + limit = jiffies + HZ/10; + for (i = 0; i < 32768 && (limit-jiffies >= 0); i++) + { + val = inw(REG(PSS_STATUS)); + if (val & 0x4000) + break; + } + + /* now read the version */ + for (i = 0; i < 32000; i++) + { + val = inw(REG(PSS_STATUS)); + if (val & PSS_READ_FULL) + break; + } + if (i == 32000) + return 0; + + val = inw(REG(PSS_DATA)); + /* printk( "<PSS: microcode version %d.%d loaded>", val/16, val % 16); */ + } return 1; } -void -attach_pss(struct address_info *hw_config) +void attach_pss(struct address_info *hw_config) { unsigned short id; - char tmp[100]; + char tmp[100]; devc->base = hw_config->io_base; devc->irq = hw_config->irq; @@ -350,20 +335,20 @@ attach_pss(struct address_info *hw_config) #if YOU_REALLY_WANT_TO_ALLOCATE_THESE_RESOURCES if (sound_alloc_dma(hw_config->dma, "PSS")) - { - printk("pss.c: Can't allocate DMA channel\n"); - return; - } + { + printk("pss.c: Can't allocate DMA channel.\n"); + return; + } if (!set_irq(devc, CONF_PSS, devc->irq)) - { - printk("PSS: IRQ error\n"); - return; - } + { + printk("PSS: IRQ allocation error.\n"); + return; + } if (!set_dma(devc, CONF_PSS, devc->dma)) - { - printk("PSS: DRQ error\n"); - return; - } + { + printk(KERN_ERR "PSS: DMA allocation error\n"); + return; + } #endif pss_initialized = 1; @@ -371,8 +356,7 @@ attach_pss(struct address_info *hw_config) conf_printf(tmp, hw_config); } -static void -pss_init_speaker(void) +static void pss_init_speaker(void) { /* Don't ask what are these commands. I really don't know */ pss_write(0x0010); @@ -387,53 +371,52 @@ pss_init_speaker(void) pss_write(0x0800 | 0x00ce); /* Stereo switch? */ } -int -probe_pss_mpu(struct address_info *hw_config) +int probe_pss_mpu(struct address_info *hw_config) { - int timeout; + int timeout; if (!pss_initialized) return 0; if (check_region(hw_config->io_base, 2)) - { - printk("PSS: MPU I/O port conflict\n"); - return 0; - } + { + printk("PSS: MPU I/O port conflict\n"); + return 0; + } if (!set_io_base(devc, CONF_MIDI, hw_config->io_base)) - { - printk("PSS: MIDI base error.\n"); + { + printk("PSS: MIDI base could not be set.\n"); return 0; - } + } if (!set_irq(devc, CONF_MIDI, hw_config->irq)) - { - printk("PSS: MIDI IRQ error.\n"); + { + printk("PSS: MIDI IRQ allocation error.\n"); return 0; - } + } if (!pss_synthLen) - { - printk("PSS: Can't enable MPU. MIDI synth microcode not available.\n"); - return 0; - } + { + printk(KERN_ERR "PSS: Can't enable MPU. MIDI synth microcode not available.\n"); + return 0; + } if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST)) - { - printk("PSS: Unable to load MIDI synth microcode to DSP.\n"); - return 0; - } + { + printk(KERN_ERR "PSS: Unable to load MIDI synth microcode to DSP.\n"); + return 0; + } pss_init_speaker(); -/* - * Finally wait until the DSP algorithm has initialized itself and - * deactivates receive interrupt. - */ + /* + * Finally wait until the DSP algorithm has initialized itself and + * deactivates receive interrupt. + */ for (timeout = 900000; timeout > 0; timeout--) - { - if ((inb(hw_config->io_base + 1) & 0x80) == 0) /* Input data avail */ - inb(hw_config->io_base); /* Discard it */ - else - break; /* No more input */ - } + { + if ((inb(hw_config->io_base + 1) & 0x80) == 0) /* Input data avail */ + inb(hw_config->io_base); /* Discard it */ + else + break; /* No more input */ + } #if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI) return probe_mpu401(hw_config); @@ -442,62 +425,56 @@ probe_pss_mpu(struct address_info *hw_config) #endif } -static int -pss_coproc_open(void *dev_info, int sub_device) +static int pss_coproc_open(void *dev_info, int sub_device) { switch (sub_device) - { - case COPR_MIDI: - - if (pss_synthLen == 0) - { - printk("PSS: MIDI synth microcode not available.\n"); - return -EIO; - } - if (nonstandard_microcode) - if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST)) - { - printk("PSS: Unable to load MIDI synth microcode to DSP.\n"); - return -EIO; - } - nonstandard_microcode = 0; - break; - - default:; - } + { + case COPR_MIDI: + if (pss_synthLen == 0) + { + printk(KERN_ERR "PSS: MIDI synth microcode not available.\n"); + return -EIO; + } + if (nonstandard_microcode) + if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST)) + { + printk(KERN_ERR "PSS: Unable to load MIDI synth microcode to DSP.\n"); + return -EIO; + } + nonstandard_microcode = 0; + break; + + default: + } return 0; } -static void -pss_coproc_close(void *dev_info, int sub_device) +static void pss_coproc_close(void *dev_info, int sub_device) { return; } -static void -pss_coproc_reset(void *dev_info) +static void pss_coproc_reset(void *dev_info) { if (pss_synthLen) if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST)) - { - printk("PSS: Unable to load MIDI synth microcode to DSP.\n"); - } + { + printk(KERN_ERR "PSS: Unable to load MIDI synth microcode to DSP.\n"); + } nonstandard_microcode = 0; } -static int -download_boot_block(void *dev_info, copr_buffer * buf) +static int download_boot_block(void *dev_info, copr_buffer * buf) { if (buf->len <= 0 || buf->len > sizeof(buf->data)) return -EINVAL; if (!pss_download_boot(devc, buf->data, buf->len, buf->flags)) - { - printk("PSS: Unable to load microcode block to DSP.\n"); - return -EIO; - } + { + printk(KERN_ERR "PSS: Unable to load microcode block to DSP.\n"); + return -EIO; + } nonstandard_microcode = 1; /* The MIDI microcode has been overwritten */ - return 0; } @@ -512,169 +489,170 @@ static int pss_coproc_ioctl(void *dev_info, unsigned int cmd, caddr_t arg, int l int i, err; /* printk( "PSS coproc ioctl %x %x %d\n", cmd, arg, local); */ - switch (cmd) { - case SNDCTL_COPR_RESET: - pss_coproc_reset(dev_info); - return 0; + switch (cmd) + { + case SNDCTL_COPR_RESET: + pss_coproc_reset(dev_info); + return 0; - case SNDCTL_COPR_LOAD: - buf = (copr_buffer *) vmalloc(sizeof(copr_buffer)); - if (buf == NULL) - return -ENOSPC; - if (__copy_from_user(buf, arg, sizeof(copr_buffer))) { + case SNDCTL_COPR_LOAD: + buf = (copr_buffer *) vmalloc(sizeof(copr_buffer)); + if (buf == NULL) + return -ENOSPC; + if (copy_from_user(buf, arg, sizeof(copr_buffer))) { + vfree(buf); + return -EFAULT; + } + err = download_boot_block(dev_info, buf); vfree(buf); - return -EFAULT; - } - err = download_boot_block(dev_info, buf); - vfree(buf); - return err; + return err; - case SNDCTL_COPR_SENDMSG: - mbuf = (copr_msg *)vmalloc(sizeof(copr_msg)); - if (mbuf == NULL) - return -ENOSPC; - if (__copy_from_user(mbuf, arg, sizeof(copr_msg))) { - vfree(mbuf); - return -EFAULT; - } - data = (unsigned short *)(mbuf->data); - save_flags(flags); - cli(); - for (i = 0; i < mbuf->len; i++) { - if (!pss_put_dspword(devc, *data++)) { - restore_flags(flags); - mbuf->len = i; /* feed back number of WORDs sent */ - err = __copy_to_user(arg, mbuf, sizeof(copr_msg)); + case SNDCTL_COPR_SENDMSG: + mbuf = (copr_msg *)vmalloc(sizeof(copr_msg)); + if (mbuf == NULL) + return -ENOSPC; + if (copy_from_user(mbuf, arg, sizeof(copr_msg))) { vfree(mbuf); - return err ? -EFAULT : -EIO; + return -EFAULT; } - } - restore_flags(flags); - vfree(mbuf); - return 0; - - case SNDCTL_COPR_RCVMSG: - err = 0; - mbuf = (copr_msg *)vmalloc(sizeof(copr_msg)); - if (mbuf == NULL) - return -ENOSPC; - data = (unsigned short *)mbuf->data; - save_flags(flags); - cli(); - for (i = 0; i < mbuf->len; i++) { - mbuf->len = i; /* feed back number of WORDs read */ - if (!pss_get_dspword(devc, data++)) { - if (i == 0) - err = -EIO; - break; + data = (unsigned short *)(mbuf->data); + save_flags(flags); + cli(); + for (i = 0; i < mbuf->len; i++) { + if (!pss_put_dspword(devc, *data++)) { + restore_flags(flags); + mbuf->len = i; /* feed back number of WORDs sent */ + err = copy_to_user(arg, mbuf, sizeof(copr_msg)); + vfree(mbuf); + return err ? -EFAULT : -EIO; + } } - } - restore_flags(flags); - if (__copy_to_user(arg, mbuf, sizeof(copr_msg))) - err = -EFAULT; - vfree(mbuf); - return err; - - case SNDCTL_COPR_RDATA: - if (__copy_from_user(&dbuf, arg, sizeof(dbuf))) - return -EFAULT; - save_flags(flags); - cli(); - if (!pss_put_dspword(devc, 0x00d0)) { - restore_flags(flags); - return -EIO; - } - if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) { restore_flags(flags); - return -EIO; - } - if (!pss_get_dspword(devc, &tmp)) { + vfree(mbuf); + return 0; + + case SNDCTL_COPR_RCVMSG: + err = 0; + mbuf = (copr_msg *)vmalloc(sizeof(copr_msg)); + if (mbuf == NULL) + return -ENOSPC; + data = (unsigned short *)mbuf->data; + save_flags(flags); + cli(); + for (i = 0; i < mbuf->len; i++) { + mbuf->len = i; /* feed back number of WORDs read */ + if (!pss_get_dspword(devc, data++)) { + if (i == 0) + err = -EIO; + break; + } + } restore_flags(flags); - return -EIO; - } - dbuf.parm1 = tmp; - restore_flags(flags); - if (__copy_to_user(arg, &dbuf, sizeof(dbuf))) - return -EFAULT; - return 0; + if (copy_to_user(arg, mbuf, sizeof(copr_msg))) + err = -EFAULT; + vfree(mbuf); + return err; - case SNDCTL_COPR_WDATA: - if (__copy_from_user(&dbuf, arg, sizeof(dbuf))) - return -EFAULT; - save_flags(flags); - cli(); - if (!pss_put_dspword(devc, 0x00d1)) { - restore_flags(flags); - return -EIO; - } - if (!pss_put_dspword(devc, (unsigned short) (dbuf.parm1 & 0xffff))) { - restore_flags(flags); - return -EIO; - } - tmp = (unsigned int)dbuf.parm2 & 0xffff; - if (!pss_put_dspword(devc, tmp)) { + case SNDCTL_COPR_RDATA: + if (copy_from_user(&dbuf, arg, sizeof(dbuf))) + return -EFAULT; + save_flags(flags); + cli(); + if (!pss_put_dspword(devc, 0x00d0)) { + restore_flags(flags); + return -EIO; + } + if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) { + restore_flags(flags); + return -EIO; + } + if (!pss_get_dspword(devc, &tmp)) { + restore_flags(flags); + return -EIO; + } + dbuf.parm1 = tmp; restore_flags(flags); - return -EIO; - } - restore_flags(flags); - return 0; + if (copy_to_user(arg, &dbuf, sizeof(dbuf))) + return -EFAULT; + return 0; - case SNDCTL_COPR_WCODE: - if (__copy_from_user(&dbuf, arg, sizeof(dbuf))) - return -EFAULT; - save_flags(flags); - cli(); - if (!pss_put_dspword(devc, 0x00d3)) { - restore_flags(flags); - return -EIO; - } - if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) { - restore_flags(flags); - return -EIO; - } - tmp = (unsigned int)dbuf.parm2 & 0x00ff; - if (!pss_put_dspword(devc, tmp)) { - restore_flags(flags); - return -EIO; - } - tmp = ((unsigned int)dbuf.parm2 >> 8) & 0xffff; - if (!pss_put_dspword(devc, tmp)) { + case SNDCTL_COPR_WDATA: + if (copy_from_user(&dbuf, arg, sizeof(dbuf))) + return -EFAULT; + save_flags(flags); + cli(); + if (!pss_put_dspword(devc, 0x00d1)) { + restore_flags(flags); + return -EIO; + } + if (!pss_put_dspword(devc, (unsigned short) (dbuf.parm1 & 0xffff))) { + restore_flags(flags); + return -EIO; + } + tmp = (unsigned int)dbuf.parm2 & 0xffff; + if (!pss_put_dspword(devc, tmp)) { + restore_flags(flags); + return -EIO; + } restore_flags(flags); - return -EIO; - } - restore_flags(flags); - return 0; + return 0; - case SNDCTL_COPR_RCODE: - if (__copy_from_user(&dbuf, arg, sizeof(dbuf))) - return -EFAULT; - save_flags(flags); - cli(); - if (!pss_put_dspword(devc, 0x00d2)) { - restore_flags(flags); - return -EIO; - } - if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) { - restore_flags(flags); - return -EIO; - } - if (!pss_get_dspword(devc, &tmp)) { /* Read MSB */ + case SNDCTL_COPR_WCODE: + if (copy_from_user(&dbuf, arg, sizeof(dbuf))) + return -EFAULT; + save_flags(flags); + cli(); + if (!pss_put_dspword(devc, 0x00d3)) { + restore_flags(flags); + return -EIO; + } + if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) { + restore_flags(flags); + return -EIO; + } + tmp = (unsigned int)dbuf.parm2 & 0x00ff; + if (!pss_put_dspword(devc, tmp)) { + restore_flags(flags); + return -EIO; + } + tmp = ((unsigned int)dbuf.parm2 >> 8) & 0xffff; + if (!pss_put_dspword(devc, tmp)) { + restore_flags(flags); + return -EIO; + } restore_flags(flags); - return -EIO; - } - dbuf.parm1 = tmp << 8; - if (!pss_get_dspword(devc, &tmp)) { /* Read LSB */ + return 0; + + case SNDCTL_COPR_RCODE: + if (copy_from_user(&dbuf, arg, sizeof(dbuf))) + return -EFAULT; + save_flags(flags); + cli(); + if (!pss_put_dspword(devc, 0x00d2)) { + restore_flags(flags); + return -EIO; + } + if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) { + restore_flags(flags); + return -EIO; + } + if (!pss_get_dspword(devc, &tmp)) { /* Read MSB */ + restore_flags(flags); + return -EIO; + } + dbuf.parm1 = tmp << 8; + if (!pss_get_dspword(devc, &tmp)) { /* Read LSB */ + restore_flags(flags); + return -EIO; + } + dbuf.parm1 |= tmp & 0x00ff; restore_flags(flags); - return -EIO; - } - dbuf.parm1 |= tmp & 0x00ff; - restore_flags(flags); - if (__copy_to_user(arg, &dbuf, sizeof(dbuf))) - return -EFAULT; - return 0; + if (copy_to_user(arg, &dbuf, sizeof(dbuf))) + return -EFAULT; + return 0; - default: - return -EINVAL; + default: + return -EINVAL; } return -EINVAL; } @@ -689,52 +667,47 @@ static coproc_operations pss_coproc_operations = &pss_data }; -void -attach_pss_mpu(struct address_info *hw_config) +void attach_pss_mpu(struct address_info *hw_config) { #if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI) - { - attach_mpu401(hw_config); /* Slot 1 */ - - if (hw_config->slots[1] != -1) /* The MPU driver installed itself */ - midi_devs[hw_config->slots[1]]->coproc = &pss_coproc_operations; - } + attach_mpu401(hw_config); /* Slot 1 */ + if (hw_config->slots[1] != -1) /* The MPU driver installed itself */ + midi_devs[hw_config->slots[1]]->coproc = &pss_coproc_operations; #endif } -int -probe_pss_mss(struct address_info *hw_config) +int probe_pss_mss(struct address_info *hw_config) { - volatile int timeout; + volatile int timeout; if (!pss_initialized) return 0; if (check_region(hw_config->io_base, 8)) - { - printk("PSS: WSS I/O port conflict\n"); + { + printk(KERN_ERR "PSS: WSS I/O port conflicts.\n"); return 0; - } + } if (!set_io_base(devc, CONF_WSS, hw_config->io_base)) - { - printk("PSS: WSS base error.\n"); - return 0; - } + { + printk("PSS: WSS base not settable.\n"); + return 0; + } if (!set_irq(devc, CONF_WSS, hw_config->irq)) - { - printk("PSS: WSS IRQ error.\n"); - return 0; - } + { + printk("PSS: WSS IRQ allocation error.\n"); + return 0; + } if (!set_dma(devc, CONF_WSS, hw_config->dma)) - { - printk("PSS: WSS DRQ error\n"); - return 0; - } + { + printk(KERN_ERR "PSS: WSS DMA allocation error\n"); + return 0; + } /* - * For some reason the card returns 0xff in the WSS status register - * immediately after boot. Probably MIDI+SB emulation algorithm - * downloaded to the ADSP2115 spends some time initializing the card. - * Let's try to wait until it finishes this task. + * For some reason the card returns 0xff in the WSS status register + * immediately after boot. Probably MIDI+SB emulation algorithm + * downloaded to the ADSP2115 spends some time initializing the card. + * Let's try to wait until it finishes this task. */ for (timeout = 0; timeout < 100000 && (inb(hw_config->io_base + 3) & 0x3f) != 0x04; @@ -748,8 +721,7 @@ probe_pss_mss(struct address_info *hw_config) return probe_ms_sound(hw_config); } -void -attach_pss_mss(struct address_info *hw_config) +void attach_pss_mss(struct address_info *hw_config) { attach_ms_sound(hw_config); /* Slot 0 */ @@ -757,34 +729,31 @@ attach_pss_mss(struct address_info *hw_config) audio_devs[hw_config->slots[0]]->coproc = &pss_coproc_operations; } -void -unload_pss(struct address_info *hw_config) +void unload_pss(struct address_info *hw_config) { } -void -unload_pss_mpu(struct address_info *hw_config) +void unload_pss_mpu(struct address_info *hw_config) { #if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI) unload_mpu401(hw_config); #endif } -void -unload_pss_mss(struct address_info *hw_config) +void unload_pss_mss(struct address_info *hw_config) { unload_ms_sound(hw_config); } #ifdef MODULE -int pss_io = 0x220; +int pss_io = -1; -int mss_io = 0x530; -int mss_irq = 11; -int mss_dma = 1; +int mss_io = -1; +int mss_irq = -1; +int mss_dma = -1; -int mpu_io = 0x330; +int mpu_io = -1; int mpu_irq = -1; struct address_info cfgpss = { 0 /* pss_io */, 0, -1, -1 }; @@ -798,22 +767,20 @@ MODULE_PARM(mss_dma, "i"); MODULE_PARM(mpu_io, "i"); MODULE_PARM(mpu_irq, "i"); -static int fw_load = 0; -static int pssmpu = 0, pssmss = 0; +static int fw_load = 0; +static int pssmpu = 0, pssmss = 0; /* * Load a PSS sound card module */ -int -init_module(void) +int init_module(void) { -#if 0 - if (pss_io == -1 || irq == -1 || dma == -1) { - printk("pss: dma, irq and io must be set.\n"); - return -EINVAL; + if (pss_io == -1 || mss_io == -1 || mss_irq == -1 || mss_dma == -1) { + printk(KERN_INFO "pss: mss_io, mss_dma, mss_irq and pss_io must be set.\n"); + return -EINVAL; } -#endif + cfgpss.io_base = pss_io; cfgmss.io_base = mss_io; @@ -823,7 +790,8 @@ init_module(void) cfgmpu.io_base = mpu_io; cfgmpu.irq = mpu_irq; - if (!pss_synth) { + if (!pss_synth) + { fw_load = 1; pss_synthLen = mod_firmware_load("/etc/sound/pss_synth", (void *) &pss_synth); } @@ -845,8 +813,7 @@ init_module(void) return 0; } -void -cleanup_module(void) +void cleanup_module(void) { if (fw_load && pss_synth) kfree(pss_synth); diff --git a/drivers/sound/sb_audio.c b/drivers/sound/sb_audio.c index 9a0fe3c79..bf3a8dbef 100644 --- a/drivers/sound/sb_audio.c +++ b/drivers/sound/sb_audio.c @@ -1151,14 +1151,14 @@ void sb_audio_init(sb_devc * devc, char *name) } if ((devc->my_dev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, - name, - driver, - sizeof(struct audio_driver), - audio_flags, - format_mask, - devc, - devc->dma8, - devc->dma8)) < 0) + name, + driver, + sizeof(struct audio_driver), + audio_flags, + format_mask, + devc, + devc->dma8, + devc->dma8)) < 0) { printk(KERN_ERR "sb: unable to install audio.\n"); return; diff --git a/drivers/sound/sb_card.c b/drivers/sound/sb_card.c index fddf74b79..941422c3f 100644 --- a/drivers/sound/sb_card.c +++ b/drivers/sound/sb_card.c @@ -69,6 +69,7 @@ int trix = 0; /* Set trix=1 to load this as support for trix */ int pas2 = 0; /* Set pas2=1 to load this as support for pas2 */ int sm_games = 0; /* Mixer - see sb_mixer.c */ int acer = 0; /* Do acer notebook init */ +int mwave_bug = 0; /* Using the dreadful mwave sb emulation */ MODULE_PARM(io, "i"); MODULE_PARM(irq, "i"); @@ -80,10 +81,11 @@ MODULE_PARM(mad16, "i"); MODULE_PARM(trix, "i"); MODULE_PARM(pas2, "i"); MODULE_PARM(sm_games, "i"); +MODULE_PARM(mwave_bug, "i"); -static int sbmpu = 0; +static int sbmpu = 0; -void *smw_free = NULL; +void *smw_free = NULL; int init_module(void) { @@ -119,8 +121,7 @@ int init_module(void) return 0; } -void -cleanup_module(void) +void cleanup_module(void) { if (smw_free) kfree(smw_free); @@ -133,19 +134,20 @@ cleanup_module(void) #else -#ifdef SM_GAMES +#ifdef CONFIG_SM_GAMES int sm_games = 1; - #else int sm_games = 0; - #endif -#ifdef SB_ACER +#ifdef CONFIG_SB_ACER int acer = 1; - #else int acer = 0; - +#endif +#ifdef CONFIG_SB_MWAVE +int mwave_bug = 1; +#else +int mwave_bug = 0; #endif #endif #endif diff --git a/drivers/sound/sb_common.c b/drivers/sound/sb_common.c index 0f3e1e6aa..fcdcfcf2c 100644 --- a/drivers/sound/sb_common.c +++ b/drivers/sound/sb_common.c @@ -28,7 +28,6 @@ static sb_devc *detected_devc = NULL; /* For communication from probe to init */ static sb_devc *last_devc = NULL; /* For MPU401 initialization */ -static sb_devc *irq2devc[16] = {NULL}; static unsigned char jazz_irq_bits[] = { 0, 0, 2, 3, 0, 1, 0, 4, 0, 2, 5, 0, 0, 0, 0, 6 }; @@ -123,15 +122,9 @@ static void sbintr(int irq, void *dev_id, struct pt_regs *dummy) int status; unsigned char src = 0xff; - sb_devc *devc = irq2devc[irq]; + sb_devc *devc = dev_id; - if (devc == NULL || devc->irq != irq) - { - DEB(printk("sbintr: Bogus interrupt IRQ%d\n", irq)); - return; - } devc->irq_ok = 1; - if (devc->model == MDL_SB16) { src = sb_getmixer(devc, IRQ_STAT); /* Interrupt source register */ @@ -710,6 +703,7 @@ void sb_dsp_init(struct address_info *hw_config) sb_devc *devc; char name[100]; extern int sb_be_quiet; + extern int mwave_bug; /* * Check if we had detected a SB device earlier @@ -743,21 +737,19 @@ void sb_dsp_init(struct address_info *hw_config) if (!(devc->caps & SB_NO_AUDIO && devc->caps & SB_NO_MIDI) && hw_config->irq > 0) { /* IRQ setup */ - if (snd_set_irq_handler(hw_config->irq, sbintr, "soundblaster", devc->osp) < 0) + if (request_irq(hw_config->irq, sbintr, 0, "soundblaster", devc) < 0) { printk(KERN_ERR "SB: Can't allocate IRQ%d\n", hw_config->irq); sound_unload_audiodev(devc->dev); return; } - irq2devc[hw_config->irq] = devc; devc->irq_ok = 0; if (devc->major == 4) if (!sb16_set_irq_hw(devc, devc->irq)) /* Unsupported IRQ */ { - snd_release_irq(devc->irq); + free_irq(devc->irq, devc); sound_unload_audiodev(devc->dev); - irq2devc[hw_config->irq] = NULL; return; } if ((devc->type == 0 || devc->type == MDL_ESS) && @@ -777,7 +769,7 @@ void sb_dsp_init(struct address_info *hw_config) /* Skip IRQ detection if SMP (doesn't work) */ devc->irq_ok = 1; #else - if (devc->major == 4 && devc->minor <= 11) /* Won't work */ + if ((devc->major == 4 && devc->minor <= 11 ) || mwave_bug ) /* Won't work */ devc->irq_ok = 1; else { @@ -883,6 +875,7 @@ void sb_dsp_init(struct address_info *hw_config) } } hw_config->card_subtype = devc->model; + hw_config->slots[0]=devc->dev; last_devc = devc; /* For SB MPU detection */ if (!(devc->caps & SB_NO_AUDIO) && devc->dma8 >= 0) @@ -915,15 +908,8 @@ void sb_dsp_disable_recording(int io_base) void sb_dsp_unload(struct address_info *hw_config) { sb_devc *devc; - int irq = hw_config->irq; - if (irq < 0) - irq *= -1; - - if (irq > 2 && irq < 16) - devc = irq2devc[irq]; - else - devc = NULL; + devc = audio_devs[hw_config->slots[0]]->devc; if (devc && devc->base == hw_config->io_base) { @@ -937,12 +923,12 @@ void sb_dsp_unload(struct address_info *hw_config) } if (!(devc->caps & SB_NO_AUDIO && devc->caps & SB_NO_MIDI) && devc->irq > 0) { - snd_release_irq(devc->irq); - irq2devc[devc->irq] = NULL; + free_irq(devc->irq, devc); sound_unload_mixerdev(devc->my_mixerdev); sound_unload_mididev(devc->my_mididev); sound_unload_audiodev(devc->my_dev); } + kfree(devc); } else release_region(hw_config->io_base, 16); diff --git a/drivers/sound/sound_calls.h b/drivers/sound/sound_calls.h index aed677c89..416737f65 100644 --- a/drivers/sound/sound_calls.h +++ b/drivers/sound/sound_calls.h @@ -11,7 +11,6 @@ int DMAbuf_start_output(int dev, int buff_no, int l); int DMAbuf_move_wrpointer(int dev, int l); /* int DMAbuf_ioctl(int dev, unsigned int cmd, caddr_t arg, int local); */ void DMAbuf_init(int dev, int dma1, int dma2); -void DMAbuf_deinit(int dev); int DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode); int DMAbuf_open_dma (int dev); void DMAbuf_close_dma (int dev); @@ -84,8 +83,9 @@ void MIDIbuf_init(void); /* From soundcard.c */ void request_sound_timer (int count); void sound_stop_timer(void); -int snd_set_irq_handler (int interrupt_level, void(*iproc)(int, void*, struct pt_regs *), char *name, int *osp); -void snd_release_irq(int vect); +/* These two are about to die.. */ +int snd_set_irq_handler (int interrupt_level, void(*iproc)(int, void*, struct pt_regs *), char *name, int *osp, void *dev_id); +void snd_release_irq(int vect, void *ptr); void sound_dma_malloc(int dev); void sound_dma_free(int dev); void conf_printf(char *name, struct address_info *hw_config); diff --git a/drivers/sound/sound_timer.c b/drivers/sound/sound_timer.c index 8a519e7e2..986668316 100644 --- a/drivers/sound/sound_timer.c +++ b/drivers/sound/sound_timer.c @@ -47,6 +47,14 @@ void reprogram_timer(void) { unsigned long usecs_per_tick; + /* + * The user is changing the timer rate before setting a timer + * slap, bad bad not allowed. + */ + + if(!tmr) + return; + usecs_per_tick = (60 * 1000000) / (curr_tempo * curr_timebase); /* diff --git a/drivers/sound/soundcard.c b/drivers/sound/soundcard.c index e23e5486e..fe3622a73 100644 --- a/drivers/sound/soundcard.c +++ b/drivers/sound/soundcard.c @@ -26,7 +26,7 @@ #include <linux/fcntl.h> #include <linux/ctype.h> #include <linux/stddef.h> -#include <linux/kerneld.h> +#include <linux/kmod.h> #ifdef __KERNEL__ #include <asm/io.h> #include <asm/segment.h> @@ -439,7 +439,7 @@ static int sound_open(struct inode *inode, struct file *file) case SND_DEV_CTL: dev >>= 4; -#ifdef CONFIG_KERNELD +#ifdef CONFIG_KMOD if (dev >= 0 && dev < MAX_MIXER_DEV && mixer_devs[dev] == NULL) { char modname[20]; sprintf(modname, "mixer%d", dev); @@ -555,14 +555,14 @@ static int sound_mixer_ioctl(int mixdev, unsigned int cmd, caddr_t arg) { if (mixdev < 0 || mixdev >= MAX_MIXER_DEV) return -ENXIO; -#ifdef CONFIG_KERNELD +#ifdef CONFIG_KMOD /* Try to load the mixer... */ if (mixer_devs[mixdev] == NULL) { char modname[20]; sprintf(modname, "mixer%d", mixdev); request_module(modname); } -#endif /* CONFIG_KERNELD */ +#endif /* CONFIG_KMOD */ if (mixdev >= num_mixers || !mixer_devs[mixdev]) return -ENXIO; if (cmd == SOUND_MIXER_INFO) @@ -801,7 +801,7 @@ free_all_irqs(void) if (irqs & (1ul << i)) { printk(KERN_WARNING "Sound warning: IRQ%d was left allocated - fixed.\n", i); - snd_release_irq(i); + snd_release_irq(i, NULL); } } irqs = 0; @@ -895,14 +895,14 @@ void cleanup_module(void) } #endif -int snd_set_irq_handler(int interrupt_level, void (*iproc) (int, void *, struct pt_regs *), char *name, int *osp) +int snd_set_irq_handler(int interrupt_level, void (*iproc) (int, void *, struct pt_regs *), char *name, int *osp, void *dev_id) { - int retcode; - unsigned long flags; + int retcode; + unsigned long flags; save_flags(flags); cli(); - retcode = request_irq(interrupt_level, iproc, 0, name, NULL); + retcode = request_irq(interrupt_level, iproc, 0, name, dev_id); if (retcode < 0) { @@ -915,13 +915,13 @@ int snd_set_irq_handler(int interrupt_level, void (*iproc) (int, void *, struct return retcode; } -void snd_release_irq(int vect) +void snd_release_irq(int vect, void *dev_id) { if (!(irqs & (1ul << vect))) return; irqs &= ~(1ul << vect); - free_irq(vect, NULL); + free_irq(vect, dev_id); } int sound_alloc_dma(int chn, char *deviceID) diff --git a/drivers/sound/uart401.c b/drivers/sound/uart401.c index 7293e9047..2e7ed2a27 100644 --- a/drivers/sound/uart401.c +++ b/drivers/sound/uart401.c @@ -9,7 +9,15 @@ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) * Version 2 (June 1991). See the "COPYING" file distributed with this software * for more info. + * + * Changes: + * Alan Cox Reformatted, removed sound_mem usage, use normal Linux + * interrupt allocation. + * + * Status: + * Untested */ + #include <linux/config.h> #include <linux/module.h> @@ -19,45 +27,43 @@ #if (defined(CONFIG_UART401)||defined(CONFIG_MIDI)) || defined(MODULE) typedef struct uart401_devc - { - int base; - int irq; - int *osp; - void (*midi_input_intr) (int dev, unsigned char data); - int opened, disabled; - volatile unsigned char input_byte; - int my_dev; - int share_irq; - } +{ + int base; + int irq; + int *osp; + void (*midi_input_intr) (int dev, unsigned char data); + int opened, disabled; + volatile unsigned char input_byte; + int my_dev; + int share_irq; +} uart401_devc; static uart401_devc *detected_devc = NULL; -static uart401_devc *irq2devc[16] = -{NULL}; #define DATAPORT (devc->base) #define COMDPORT (devc->base+1) #define STATPORT (devc->base+1) -static int -uart401_status(uart401_devc * devc) +static int uart401_status(uart401_devc * devc) { return inb(STATPORT); } + #define input_avail(devc) (!(uart401_status(devc)&INPUT_AVAIL)) #define output_ready(devc) (!(uart401_status(devc)&OUTPUT_READY)) -static void -uart401_cmd(uart401_devc * devc, unsigned char cmd) + +static void uart401_cmd(uart401_devc * devc, unsigned char cmd) { outb((cmd), COMDPORT); } -static int -uart401_read(uart401_devc * devc) + +static int uart401_read(uart401_devc * devc) { return inb(DATAPORT); } -static void -uart401_write(uart401_devc * devc, unsigned char byte) + +static void uart401_write(uart401_devc * devc, unsigned char byte) { outb((byte), DATAPORT); } @@ -71,30 +77,26 @@ uart401_write(uart401_devc * devc, unsigned char byte) static int reset_uart401(uart401_devc * devc); static void enter_uart_mode(uart401_devc * devc); -static void -uart401_input_loop(uart401_devc * devc) +static void uart401_input_loop(uart401_devc * devc) { while (input_avail(devc)) - { - unsigned char c = uart401_read(devc); - - if (c == MPU_ACK) - devc->input_byte = c; - else if (devc->opened & OPEN_READ && devc->midi_input_intr) - devc->midi_input_intr(devc->my_dev, c); - } + { + unsigned char c = uart401_read(devc); + + if (c == MPU_ACK) + devc->input_byte = c; + else if (devc->opened & OPEN_READ && devc->midi_input_intr) + devc->midi_input_intr(devc->my_dev, c); + } } -void -uart401intr(int irq, void *dev_id, struct pt_regs *dummy) +void uart401intr(int irq, void *dev_id, struct pt_regs *dummy) { - uart401_devc *devc; + uart401_devc *devc = dev_id; if (irq < 1 || irq > 15) return; - devc = irq2devc[irq]; - if (devc == NULL) return; @@ -108,12 +110,12 @@ uart401_open(int dev, int mode, void (*output) (int dev) ) { - uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc; + uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc; if (devc->opened) - { - return -EBUSY; - } + { + return -EBUSY; + } while (input_avail(devc)) uart401_read(devc); @@ -125,21 +127,19 @@ uart401_open(int dev, int mode, return 0; } -static void -uart401_close(int dev) +static void uart401_close(int dev) { - uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc; + uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc; reset_uart401(devc); devc->opened = 0; } -static int -uart401_out(int dev, unsigned char midi_byte) +static int uart401_out(int dev, unsigned char midi_byte) { - int timeout; - unsigned long flags; - uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc; + int timeout; + unsigned long flags; + uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc; if (devc->disabled) return 1; @@ -163,36 +163,32 @@ uart401_out(int dev, unsigned char midi_byte) for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--); if (!output_ready(devc)) - { - printk("MPU-401: Timeout - Device not responding\n"); + { + printk(KERN_WARNING "uart401: Timeout - Device not responding\n"); devc->disabled = 1; reset_uart401(devc); enter_uart_mode(devc); return 1; - } + } uart401_write(devc, midi_byte); return 1; } -static int -uart401_start_read(int dev) +static int uart401_start_read(int dev) { return 0; } -static int -uart401_end_read(int dev) +static int uart401_end_read(int dev) { return 0; } -static void -uart401_kick(int dev) +static void uart401_kick(int dev) { } -static int -uart401_buffer_status(int dev) +static int uart401_buffer_status(int dev) { return 0; } @@ -203,7 +199,9 @@ uart401_buffer_status(int dev) static struct midi_operations uart401_operations = { - {"MPU-401 (UART) MIDI", 0, 0, SNDCARD_MPU401}, + { + "MPU-401 (UART) MIDI", 0, 0, SNDCARD_MPU401 + }, &std_midi_synth, {0}, uart401_open, @@ -218,11 +216,10 @@ static struct midi_operations uart401_operations = NULL }; -static void -enter_uart_mode(uart401_devc * devc) +static void enter_uart_mode(uart401_devc * devc) { - int ok, timeout; - unsigned long flags; + int ok, timeout; + unsigned long flags; save_flags(flags); cli(); @@ -242,11 +239,10 @@ enter_uart_mode(uart401_devc * devc) restore_flags(flags); } -void -attach_uart401(struct address_info *hw_config) +void attach_uart401(struct address_info *hw_config) { - uart401_devc *devc; - char *name = "MPU-401 (UART) MIDI"; + uart401_devc *devc; + char *name = "MPU-401 (UART) MIDI"; if (hw_config->name) name = hw_config->name; @@ -255,80 +251,75 @@ attach_uart401(struct address_info *hw_config) return; - devc = (uart401_devc *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(uart401_devc))); - sound_mem_sizes[sound_nblocks] = sizeof(uart401_devc); - if (sound_nblocks < 1024) - sound_nblocks++;; + devc = (uart401_devc *) kmalloc(sizeof(uart401_devc), GFP_KERNEL); if (devc == NULL) - { - printk(KERN_WARNING "uart401: Can't allocate memory\n"); - return; - } + { + printk(KERN_WARNING "uart401: Can't allocate memory\n"); + return; + } memcpy((char *) devc, (char *) detected_devc, sizeof(uart401_devc)); detected_devc = NULL; devc->irq = hw_config->irq; if (devc->irq < 0) - { - devc->share_irq = 1; - devc->irq *= -1; - } else + { + devc->share_irq = 1; + devc->irq *= -1; + } + else devc->share_irq = 0; if (devc->irq < 1 || devc->irq > 15) + { + kfree(devc); return; + } if (!devc->share_irq) - if (snd_set_irq_handler(devc->irq, uart401intr, "MPU-401 UART", devc->osp) < 0) - { - printk(KERN_WARNING "uart401: Failed to allocate IRQ%d\n", devc->irq); - devc->share_irq = 1; - } - irq2devc[devc->irq] = devc; + { + if (request_irq(devc->irq, uart401intr, 0, "MPU-401 UART", devc) < 0) + { + printk(KERN_WARNING "uart401: Failed to allocate IRQ%d\n", devc->irq); + devc->share_irq = 1; + } + } devc->my_dev = sound_alloc_mididev(); request_region(hw_config->io_base, 4, "MPU-401 UART"); enter_uart_mode(devc); if (devc->my_dev == -1) - { - printk(KERN_INFO "uart401: Too many midi devices detected\n"); - return; - } + { + printk(KERN_INFO "uart401: Too many midi devices detected\n"); + kfree(devc); + return; + } conf_printf(name, hw_config); std_midi_synth.midi_dev = devc->my_dev; - - - midi_devs[devc->my_dev] = (struct midi_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct midi_operations))); - sound_mem_sizes[sound_nblocks] = sizeof(struct midi_operations); - - if (sound_nblocks < 1024) - sound_nblocks++;; + midi_devs[devc->my_dev] = (struct midi_operations *)kmalloc(sizeof(struct midi_operations), GFP_KERNEL); if (midi_devs[devc->my_dev] == NULL) - { - printk("uart401: Failed to allocate memory\n"); - sound_unload_mididev(devc->my_dev); - return; - } + { + printk(KERN_ERR "uart401: Failed to allocate memory\n"); + sound_unload_mididev(devc->my_dev); + kfree(devc); + devc=NULL; + return; + } memcpy((char *) midi_devs[devc->my_dev], (char *) &uart401_operations, sizeof(struct midi_operations)); midi_devs[devc->my_dev]->devc = devc; - - - midi_devs[devc->my_dev]->converter = (struct synth_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct synth_operations))); - sound_mem_sizes[sound_nblocks] = sizeof(struct synth_operations); - - if (sound_nblocks < 1024) - sound_nblocks++; - + midi_devs[devc->my_dev]->converter = (struct synth_operations *)kmalloc(sizeof(struct synth_operations), GFP_KERNEL); if (midi_devs[devc->my_dev]->converter == NULL) - { - printk(KERN_WARNING "uart401: Failed to allocate memory\n"); - sound_unload_mididev(devc->my_dev); - return; - } + { + printk(KERN_WARNING "uart401: Failed to allocate memory\n"); + sound_unload_mididev(devc->my_dev); + kfree(midi_devs[devc->my_dev]); + kfree(devc); + devc=NULL; + return; + } memcpy((char *) midi_devs[devc->my_dev]->converter, (char *) &std_midi_synth, sizeof(struct synth_operations)); @@ -339,10 +330,9 @@ attach_uart401(struct address_info *hw_config) devc->opened = 0; } -static int -reset_uart401(uart401_devc * devc) +static int reset_uart401(uart401_devc * devc) { - int ok, timeout, n; + int ok, timeout, n; /* * Send the RESET command. Try again if no success at the first time. @@ -351,31 +341,33 @@ reset_uart401(uart401_devc * devc) ok = 0; for (n = 0; n < 2 && !ok; n++) - { - for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--); - - devc->input_byte = 0; - uart401_cmd(devc, MPU_RESET); - - /* - * Wait at least 25 msec. This method is not accurate so let's make the - * loop bit longer. Cannot sleep since this is called during boot. - */ - - for (timeout = 50000; timeout > 0 && !ok; timeout--) - if (devc->input_byte == MPU_ACK) /* Interrupt */ - ok = 1; - else if (input_avail(devc)) - if (uart401_read(devc) == MPU_ACK) - ok = 1; - - } + { + for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--); + devc->input_byte = 0; + uart401_cmd(devc, MPU_RESET); + /* + * Wait at least 25 msec. This method is not accurate so let's make the + * loop bit longer. Cannot sleep since this is called during boot. + */ + + for (timeout = 50000; timeout > 0 && !ok; timeout--) + { + if (devc->input_byte == MPU_ACK) /* Interrupt */ + ok = 1; + else if (input_avail(devc)) + { + if (uart401_read(devc) == MPU_ACK) + ok = 1; + } + } + } if (ok) - { - DEB(printk("Reset UART401 OK\n")); - } else + { + DEB(printk("Reset UART401 OK\n")); + } + else DDB(printk("Reset UART401 failed - No hardware detected.\n")); if (ok) @@ -386,14 +378,12 @@ reset_uart401(uart401_devc * devc) return ok; } -int -probe_uart401(struct address_info *hw_config) +int probe_uart401(struct address_info *hw_config) { - int ok = 0; - unsigned long flags; - + int ok = 0; + unsigned long flags; static uart401_devc hw_info; - uart401_devc *devc = &hw_info; + uart401_devc *devc = &hw_info; DDB(printk("Entered probe_uart401()\n")); @@ -422,21 +412,10 @@ probe_uart401(struct address_info *hw_config) return ok; } -void -unload_uart401(struct address_info *hw_config) +void unload_uart401(struct address_info *hw_config) { - uart401_devc *devc; - - int irq = hw_config->irq; - - if (irq < 0) - { - irq *= -1; - } - if (irq < 1 || irq > 15) - return; - - devc = irq2devc[irq]; + uart401_devc *devc; + devc = midi_devs[hw_config->slots[4]]->devc; if (devc == NULL) return; @@ -444,47 +423,47 @@ unload_uart401(struct address_info *hw_config) release_region(hw_config->io_base, 4); if (!devc->share_irq) - snd_release_irq(devc->irq); - + free_irq(devc->irq, devc); + sound_unload_mididev(hw_config->slots[4]); if (devc) + { + kfree(midi_devs[devc->my_dev]->converter); + kfree(midi_devs[devc->my_dev]); + kfree(devc); devc = NULL; - sound_unload_mididev(hw_config->slots[4]); + } } #ifdef MODULE -int io = -1; -int irq = -1; +int io = -1; +int irq = -1; MODULE_PARM(io, "i"); MODULE_PARM(irq, "i"); struct address_info hw; -int -init_module(void) +int init_module(void) { /* Can be loaded either for module use or to provide functions to others */ if (io != -1 && irq != -1) - { - printk("MPU-401 UART driver Copyright (C) Hannu Savolainen 1993-1997"); - hw.irq = irq; - hw.io_base = io; - if (probe_uart401(&hw) == 0) - return -ENODEV; - attach_uart401(&hw); - } + { + printk(KERN_INFO "MPU-401 UART driver Copyright (C) Hannu Savolainen 1993-1997"); + hw.irq = irq; + hw.io_base = io; + if (probe_uart401(&hw) == 0) + return -ENODEV; + attach_uart401(&hw); + } SOUND_LOCK; return 0; } -void -cleanup_module(void) +void cleanup_module(void) { if (io != -1 && irq != -1) - { - unload_uart401(&hw); - } + unload_uart401(&hw); /* FREE SYMTAB */ SOUND_LOCK_END; } diff --git a/drivers/sound/uart6850.c b/drivers/sound/uart6850.c index 932be1846..b26e75a89 100644 --- a/drivers/sound/uart6850.c +++ b/drivers/sound/uart6850.c @@ -10,6 +10,11 @@ * Extended by Alan Cox for Red Hat Software. Now a loadable MIDI driver. * 28/4/97 - (C) Copyright Alan Cox. Released under the GPL version 2. * + * Alan Cox: Updated for new modular code. Removed snd_* irq handling. Now + * uses native linux resources + * + * Status: Testing required + * */ #include <linux/config.h> #include <linux/module.h> @@ -297,7 +302,7 @@ int probe_uart6850(struct address_info *hw_config) uart6850_base = hw_config->io_base; uart6850_irq = hw_config->irq; - if (snd_set_irq_handler(uart6850_irq, m6850intr, "MIDI6850", uart6850_osp) < 0) + if (request_irq(uart6850_irq, m6850intr, 0, "MIDI6850", NULL) < 0) return 0; ok = reset_uart6850(); @@ -307,7 +312,7 @@ int probe_uart6850(struct address_info *hw_config) void unload_uart6850(struct address_info *hw_config) { - snd_release_irq(hw_config->irq); + free_irq(hw_config->irq, NULL); sound_unload_mididev(hw_config->slots[4]); } diff --git a/drivers/sound/v_midi.c b/drivers/sound/v_midi.c index 12c221a68..4820d5f36 100644 --- a/drivers/sound/v_midi.c +++ b/drivers/sound/v_midi.c @@ -2,15 +2,22 @@ * sound/v_midi.c * * The low level driver for the Sound Blaster DS chips. - */ -/* + * + * * Copyright (C) by Hannu Savolainen 1993-1996 * * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) * Version 2 (June 1991). See the "COPYING" file distributed with this software * for more info. * ?? + * + * Changes + * Alan Cox Modularisation, changed memory allocations + * + * Status + * Untested */ + #include <linux/config.h> #include <linux/module.h> diff --git a/drivers/video/fbcon.c b/drivers/video/fbcon.c index 1341d3196..872d77928 100644 --- a/drivers/video/fbcon.c +++ b/drivers/video/fbcon.c @@ -74,8 +74,8 @@ #include <linux/vt_kern.h> #include <linux/selection.h> #include <linux/init.h> -#ifdef CONFIG_KERNELD -#include <linux/kerneld.h> +#ifdef CONFIG_KMOD +#include <linux/kmod.h> #endif #include <asm/irq.h> @@ -196,7 +196,7 @@ static void fbcon_bmove_rec(struct display *p, int sy, int sx, int dy, int dx, static struct display_switch *probe_list(struct display_switch *dispsw, struct display *disp); -#ifdef CONFIG_KERNELD +#ifdef CONFIG_KMOD static void request_driver(struct display *disp, int is_accel); #endif static struct display_switch *fbcon_get_driver(struct display *disp); @@ -1368,7 +1368,7 @@ static struct display_switch *probe_list(struct display_switch *dispsw, } -#ifdef CONFIG_KERNELD +#ifdef CONFIG_KMOD static void request_driver(struct display *disp, int is_accel) { char modname[30]; @@ -1400,7 +1400,7 @@ static void request_driver(struct display *disp, int is_accel) len += sprintf(modname+len, "-%d", disp->var.accel); request_module(modname); } -#endif /* CONFIG_KERNELD */ +#endif /* CONFIG_KMOD */ static struct display_switch *fbcon_get_driver(struct display *disp) @@ -1410,7 +1410,7 @@ static struct display_switch *fbcon_get_driver(struct display *disp) if (disp->var.accel != FB_ACCEL_NONE) { /* First try an accelerated driver */ dispsw = probe_list(accel_drivers, disp); -#ifdef CONFIG_KERNELD +#ifdef CONFIG_KMOD if (!dispsw) { request_driver(disp, 1); dispsw = probe_list(accel_drivers, disp); @@ -1422,7 +1422,7 @@ static struct display_switch *fbcon_get_driver(struct display *disp) /* Then try an unaccelerated driver */ dispsw = probe_list(drivers, disp); -#ifdef CONFIG_KERNELD +#ifdef CONFIG_KMOD if (!dispsw) { request_driver(disp, 0); dispsw = probe_list(drivers, disp); diff --git a/fs/buffer.c b/fs/buffer.c index 7e45b223e..ec844de9f 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -731,7 +731,8 @@ static void refill_freelist(int size) /* We are going to try to locate this much memory. */ needed = bdf_prm.b_un.nrefill * size; - while ((nr_free_pages > min_free_pages*2) && + while ((nr_free_pages > freepages.min*2) && + BUFFER_MEM < (buffer_mem.max_percent * num_physpages / 100) && grow_buffers(GFP_BUFFER, size)) { obtained += PAGE_SIZE; if (obtained >= needed) @@ -815,7 +816,8 @@ repeat: * are _any_ free buffers. */ while (obtained < (needed >> 1) && - nr_free_pages > min_free_pages + 5 && + nr_free_pages > freepages.min + 5 && + BUFFER_MEM < (buffer_mem.max_percent * num_physpages / 100) && grow_buffers(GFP_BUFFER, size)) obtained += PAGE_SIZE; diff --git a/fs/coda/Makefile b/fs/coda/Makefile index 0a4140745..1f2d0a94c 100644 --- a/fs/coda/Makefile +++ b/fs/coda/Makefile @@ -3,7 +3,7 @@ # O_TARGET := coda.o -O_OBJS := psdev.o cache.o cnode.o super.o dir.o file.o upcall.o coda_linux.o\ +O_OBJS := psdev.o cache.o cnode.o inode.o dir.o file.o upcall.o coda_linux.o\ symlink.o pioctl.o sysctl.o M_OBJS := $(O_TARGET) diff --git a/fs/coda/cache.c b/fs/coda/cache.c index 7673bfbdd..cdf586507 100644 --- a/fs/coda/cache.c +++ b/fs/coda/cache.c @@ -24,16 +24,22 @@ #include <linux/coda_fs_i.h> #include <linux/coda_cache.h> -/* Keep various stats */ -struct cfsnc_statistics cfsnc_stat; +static void coda_ccinsert(struct coda_cache *el, struct super_block *sb); +static void coda_cninsert(struct coda_cache *el, struct coda_inode_info *cii); +static void coda_ccremove(struct coda_cache *el); +static void coda_cnremove(struct coda_cache *el); +static void coda_cache_create(struct inode *inode, int mask); +static struct coda_cache * coda_cache_find(struct inode *inode); -/* we need to call INIT_LIST_HEAD on cnp->c_cnhead and sbi->sbi_cchead */ +/* Keep various stats */ +struct cfsnc_statistics cfsnc_stat; -void coda_ccinsert(struct coda_cache *el, struct super_block *sb) +/* insert a acl-cache entry in sb list */ +static void coda_ccinsert(struct coda_cache *el, struct super_block *sb) { struct coda_sb_info *sbi = coda_sbp(sb); -ENTRY; + ENTRY; if ( !sbi || !el) { printk("coda_ccinsert: NULL sbi or el!\n"); return ; @@ -42,17 +48,19 @@ ENTRY; list_add(&el->cc_cclist, &sbi->sbi_cchead); } -void coda_cninsert(struct coda_cache *el, struct coda_inode_info *cnp) +/* insert a acl-cache entry in the inode list */ +static void coda_cninsert(struct coda_cache *el, struct coda_inode_info *cii) { -ENTRY; - if ( !cnp || !el) { - printk("coda_cninsert: NULL cnp or el!\n"); + ENTRY; + if ( !cii || !el) { + printk("coda_cninsert: NULL cii or el!\n"); return ; } - list_add(&el->cc_cnlist, &cnp->c_cnhead); + list_add(&el->cc_cnlist, &cii->c_cnhead); } -void coda_ccremove(struct coda_cache *el) +/* remove a cache entry from the superblock list */ +static void coda_ccremove(struct coda_cache *el) { ENTRY; if (el->cc_cclist.next && el->cc_cclist.prev) @@ -61,7 +69,8 @@ void coda_ccremove(struct coda_cache *el) printk("coda_cnremove: trying to remove 0 entry!"); } -void coda_cnremove(struct coda_cache *el) +/* remove a cache entry from the inode's list */ +static void coda_cnremove(struct coda_cache *el) { ENTRY; if (el->cc_cnlist.next && el->cc_cnlist.prev) @@ -70,10 +79,10 @@ void coda_cnremove(struct coda_cache *el) printk("coda_cnremove: trying to remove 0 entry!"); } - -void coda_cache_create(struct inode *inode, int mask) +/* create a new cache entry and enlist it */ +static void coda_cache_create(struct inode *inode, int mask) { - struct coda_inode_info *cnp = ITOC(inode); + struct coda_inode_info *cii = ITOC(inode); struct super_block *sb = inode->i_sb; struct coda_cache *cc = NULL; ENTRY; @@ -85,17 +94,19 @@ void coda_cache_create(struct inode *inode, int mask) } coda_load_creds(&cc->cc_cred); cc->cc_mask = mask; - coda_cninsert(cc, cnp); + coda_cninsert(cc, cii); coda_ccinsert(cc, sb); } -struct coda_cache * coda_cache_find(struct inode *inode) +/* see if there is a match for the current + credentials already */ +static struct coda_cache * coda_cache_find(struct inode *inode) { - struct coda_inode_info *cnp = ITOC(inode); + struct coda_inode_info *cii = ITOC(inode); struct list_head *lh, *le; struct coda_cache *cc = NULL; - le = lh = &cnp->c_cnhead; + le = lh = &cii->c_cnhead; while( (le = le->next ) != lh ) { /* compare name and creds */ cc = list_entry(le, struct coda_cache, cc_cnlist); @@ -107,6 +118,7 @@ struct coda_cache * coda_cache_find(struct inode *inode) return NULL; } +/* create or extend an acl cache hit */ void coda_cache_enter(struct inode *inode, int mask) { struct coda_cache *cc; @@ -120,17 +132,21 @@ void coda_cache_enter(struct inode *inode, int mask) } } -void coda_cache_clear_cnp(struct coda_inode_info *cnp) +/* remove all cached acl matches from an inode */ +void coda_cache_clear_inode(struct inode *inode) { struct list_head *lh, *le; + struct coda_inode_info *cii; struct coda_cache *cc; + ENTRY; - if ( !cnp ) { - printk("coda_cache_cnp_clear: NULL cnode\n"); + if ( !inode ) { + CDEBUG(D_CACHE, "coda_cache_clear_inode: NULL inode\n"); return; } + cii = ITOC(inode); - lh = le = &cnp->c_cnhead; + lh = le = &cii->c_cnhead; while ( (le = le->next ) != lh ) { cc = list_entry(le, struct coda_cache, cc_cnlist); coda_cnremove(cc); @@ -139,6 +155,7 @@ void coda_cache_clear_cnp(struct coda_inode_info *cnp) } } +/* remove all acl caches */ void coda_cache_clear_all(struct super_block *sb) { struct list_head *lh, *le; @@ -150,6 +167,9 @@ void coda_cache_clear_all(struct super_block *sb) return; } + if ( list_empty(&sbi->sbi_cchead) ) + return; + lh = le = &sbi->sbi_cchead; while ( (le = le->next ) != lh ) { cc = list_entry(le, struct coda_cache, cc_cclist); @@ -159,6 +179,7 @@ void coda_cache_clear_all(struct super_block *sb) } } +/* remove all acl caches for a principal */ void coda_cache_clear_cred(struct super_block *sb, struct coda_cred *cred) { struct list_head *lh, *le; @@ -170,6 +191,9 @@ void coda_cache_clear_cred(struct super_block *sb, struct coda_cred *cred) return; } + if (list_empty(&sbi->sbi_cchead)) + return; + lh = le = &sbi->sbi_cchead; while ( (le = le->next ) != lh ) { cc = list_entry(le, struct coda_cache, cc_cclist); @@ -180,15 +204,17 @@ void coda_cache_clear_cred(struct super_block *sb, struct coda_cred *cred) } } } - + +/* check if the mask has been matched against the acl + already */ int coda_cache_check(struct inode *inode, int mask) { - struct coda_inode_info *cnp = ITOC(inode); + struct coda_inode_info *cii = ITOC(inode); struct list_head *lh, *le; struct coda_cache *cc = NULL; - le = lh = &cnp->c_cnhead; + le = lh = &cii->c_cnhead; while( (le = le->next ) != lh ) { /* compare name and creds */ cc = list_entry(le, struct coda_cache, cc_cnlist); @@ -204,110 +230,70 @@ int coda_cache_check(struct inode *inode, int mask) } -/* DENTRY related stuff */ +/* DCACHE & ZAPPING related stuff */ -/* when the dentry count falls to 0 this is called. If Venus has - asked for it to be flushed, we take it out of the dentry hash - table with d_drop */ - -static void coda_flag_children(struct dentry *parent) +/* the following routines set flags in the inodes. They are + detected by: + - a dentry method: coda_dentry_revalidate (for lookups) + if the flag is C_PURGE + - an inode method coda_revalidate (for attributes) if the + flag is C_ATTR +*/ +static void coda_flag_children(struct dentry *parent, int flag) { struct list_head *child; - struct coda_inode_info *cnp; struct dentry *de; child = parent->d_subdirs.next; while ( child != &parent->d_subdirs ) { de = list_entry(child, struct dentry, d_child); - cnp = ITOC(de->d_inode); - if (cnp) - cnp->c_flags |= C_ZAPFID; - CDEBUG(D_CACHE, "ZAPFID for %s\n", coda_f2s(&cnp->c_fid)); - + coda_flag_inode(de->d_inode, flag); + CDEBUG(D_CACHE, "%d for %*s/%*s\n", flag, + de->d_name.len, de->d_name.name, + de->d_parent->d_name.len, de->d_parent->d_name.name); child = child->next; + if ( !de->d_inode ) + d_drop(de); } return; } -/* flag dentry and possibly children of a dentry with C_ZAPFID */ -void coda_dentry_delete(struct dentry *dentry) -{ - struct inode *inode = dentry->d_inode; - struct coda_inode_info *cnp = NULL; - ENTRY; - if (inode) { - cnp = ITOC(inode); - if ( cnp ) - CHECK_CNODE(cnp); - } else { - CDEBUG(D_CACHE, "No inode for dentry_delete!\n"); - return; - } - - - if ( !cnp ) { - printk("No cnode for dentry_delete!\n"); - return; - } - - if ( cnp->c_flags & (C_ZAPFID | C_ZAPDIR) ) - d_drop(dentry); - if ( (cnp->c_flags & C_ZAPDIR) && S_ISDIR(inode->i_mode) ) { - coda_flag_children(dentry); - } - return; -} - -static void coda_zap_cnode(struct coda_inode_info *cnp, int flags) +void coda_flag_alias_children(struct inode *inode, int flag) { - cnp->c_flags |= flags; - coda_cache_clear_cnp(cnp); + struct list_head *alias; + struct dentry *alias_de; + + if ( !inode ) + return; + alias = inode->i_dentry.next; + while ( alias != &inode->i_dentry ) { + alias_de = list_entry(alias, struct dentry, d_alias); + if ( !alias_de ) { + printk("Corrupt alias list for %*s\n", + alias_de->d_name.len, alias_de->d_name.name); + return; + } + coda_flag_children(alias_de, flag); + alias= alias->next; + } } - - -/* the dache will notice the flags and drop entries (possibly with - children) the moment they are no longer in use */ -void coda_zapfid(struct ViceFid *fid, struct super_block *sb, int flag) +void coda_flag_inode(struct inode *inode, int flag) { - struct inode *inode = NULL; - struct coda_inode_info *cnp; + struct coda_inode_info *cii; - ENTRY; - - if ( !sb ) { - printk("coda_zapfid: no sb!\n"); + if ( !inode ) { + CDEBUG(D_CACHE, " no inode!\n"); return; } + cii = ITOC(inode); + cii->c_flags |= flag; +} - if ( !fid ) { - printk("coda_zapfid: no fid!\n"); - return; - } - if ( coda_fid_is_volroot(fid) ) { - struct list_head *lh, *le; - struct coda_sb_info *sbi = coda_sbp(sb); - le = lh = &sbi->sbi_volroothead; - while ( (le = le->next) != lh ) { - cnp = list_entry(le, struct coda_inode_info, c_volrootlist); - if ( cnp->c_fid.Volume == fid->Volume) - coda_zap_cnode(cnp, flag); - } - return; - } - inode = coda_fid_to_inode(fid, sb); - if ( !inode ) { - CDEBUG(D_CACHE, "coda_zapfid: no inode!\n"); - return; - } - cnp = ITOC(inode); - coda_zap_cnode(cnp, flag); -} - int cfsnc_nc_info(char *buffer, char **start, off_t offset, int length, int dummy) diff --git a/fs/coda/cnode.c b/fs/coda/cnode.c index 67133f275..aa67a22e4 100644 --- a/fs/coda/cnode.c +++ b/fs/coda/cnode.c @@ -15,8 +15,6 @@ extern int coda_print_entry; /* cnode.c */ - - static void coda_fill_inode (struct inode *inode, struct coda_vattr *attr) { CDEBUG(D_SUPER, "ino: %ld\n", inode->i_ino); @@ -56,8 +54,8 @@ int coda_cnode_make(struct inode **inode, ViceFid *fid, struct super_block *sb) ENTRY; /* - * We get inode numbers from Venus -- see venus source - */ + * We get inode numbers from Venus -- see venus source + */ error = venus_getattr(sb, fid, &attr); if ( error ) { @@ -79,7 +77,7 @@ int coda_cnode_make(struct inode **inode, ViceFid *fid, struct super_block *sb) memset(cnp, 0, (int) sizeof(struct coda_inode_info)); cnp->c_fid = *fid; cnp->c_magic = CODA_CNODE_MAGIC; - cnp->c_flags = C_VATTR; + cnp->c_flags = 0; cnp->c_vnode = *inode; INIT_LIST_HEAD(&(cnp->c_cnhead)); INIT_LIST_HEAD(&(cnp->c_volrootlist)); @@ -111,20 +109,51 @@ inline int coda_fideq(ViceFid *fid1, ViceFid *fid2) -/* convert a fid to an inode. Avoids having a hash table - such as present in the Mach minicache */ +/* convert a fid to an inode. Mostly we can compute + the inode number from the FID, but not for volume + mount points: those are in a list */ struct inode *coda_fid_to_inode(ViceFid *fid, struct super_block *sb) { ino_t nr; struct inode *inode; struct coda_inode_info *cnp; -ENTRY; + ENTRY; CDEBUG(D_INODE, "%s\n", coda_f2s(fid)); + if ( !sb ) { + printk("coda_fid_to_inode: no sb!\n"); + return NULL; + } + + if ( !fid ) { + printk("coda_fid_to_inode: no fid!\n"); + return NULL; + } + + + if ( coda_fid_is_volroot(fid) ) { + struct coda_inode_info *cii; + struct list_head *lh, *le; + struct coda_sb_info *sbi = coda_sbp(sb); + le = lh = &sbi->sbi_volroothead; + + while ( (le = le->next) != lh ) { + cii = list_entry(le, struct coda_inode_info, + c_volrootlist); + if ( cii->c_fid.Volume == fid->Volume) { + inode = cii->c_vnode; + CDEBUG(D_INODE, "volume root, found %ld\n", cii->c_vnode->i_ino); + return cii->c_vnode; + } + + } + return NULL; + } + + /* fid is not volume root, hence ino is computable */ nr = coda_f2i(fid); inode = iget(sb, nr); - if ( !inode ) { printk("coda_fid_to_inode: null from iget, sb %p, nr %ld.\n", sb, nr); @@ -133,19 +162,25 @@ ENTRY; /* check if this inode is linked to a cnode */ cnp = ITOC(inode); - if ( cnp->c_magic != CODA_CNODE_MAGIC ) { + CDEBUG(D_INODE, "uninitialized inode. Return.\n"); iput(inode); return NULL; } - /* make sure fid is the one we want */ - if ( !coda_fideq(fid, &(cnp->c_fid)) ) { + /* make sure fid is the one we want; + unfortunately Venus will shamelessly send us mount-symlinks. + These have the same inode as the root of the volume they + mount, but the fid will be wrong. + */ + if ( !coda_fideq(fid, &(cnp->c_fid)) && + !coda_fid_is_volroot(&(cnp->c_fid))) { printk("coda_fid2inode: bad cnode! Tell Peter.\n"); iput(inode); return NULL; } + CDEBUG(D_INODE, "found %ld\n", inode->i_ino); iput(inode); return inode; } diff --git a/fs/coda/coda_linux.c b/fs/coda/coda_linux.c index e968f3add..4fe096df9 100644 --- a/fs/coda/coda_linux.c +++ b/fs/coda/coda_linux.c @@ -32,14 +32,15 @@ int coda_access_cache = 1; /* caller must allocate 36 byte string ! */ char * coda_f2s(ViceFid *f) { - static char s[50]; + static char s[60]; if ( f ) { - sprintf(s, "(%10lx,%10lx,%10lx)", + sprintf(s, "(%-#lx,%-#lx,%-#lx)", f->Volume, f->Vnode, f->Unique); } return s; } +/* recognize special .CONTROL name */ int coda_iscontrol(const char *name, size_t length) { if ((CFS_CONTROLLEN == length) && @@ -48,16 +49,23 @@ int coda_iscontrol(const char *name, size_t length) return 0; } +/* recognize /coda inode */ int coda_isroot(struct inode *i) { if ( i->i_sb->s_root->d_inode == i ) { - return 1; + return 1; } else { - return 0; + return 0; } } - +/* is this a volume root FID */ +int coda_fid_is_volroot(struct ViceFid *fid) +{ + return ( (fid->Vnode == 1) && (fid->Unique == 1 ) ); +} + +/* put the current process credentials in the cred */ void coda_load_creds(struct coda_cred *cred) { cred->cr_uid = (vuid_t) current->uid; @@ -98,11 +106,6 @@ unsigned short coda_flags_to_cflags(unsigned short flags) } -int coda_fid_is_volroot(struct ViceFid *fid) -{ - return ( (fid->Vnode == 1) && (fid->Unique == 1 ) ); -} - /* utility functions below */ void coda_vattr_to_iattr(struct inode *inode, struct coda_vattr *attr) { diff --git a/fs/coda/dir.c b/fs/coda/dir.c index dd20499dc..8fed69242 100644 --- a/fs/coda/dir.c +++ b/fs/coda/dir.c @@ -40,17 +40,21 @@ static int coda_rename(struct inode *old_inode, struct dentry *old_dentry, /* dir file-ops */ static int coda_readdir(struct file *file, void *dirent, filldir_t filldir); +/* dentry ops */ +int coda_dentry_revalidate(struct dentry *de); + /* support routines */ static int coda_venus_readdir(struct file *filp, void *dirent, filldir_t filldir); int coda_fsync(struct file *, struct dentry *dentry); +static int coda_refresh_inode(struct dentry *dentry); struct dentry_operations coda_dentry_operations = { - NULL, /* revalidate */ + coda_dentry_revalidate, /* revalidate */ NULL, /* hash */ NULL, - coda_dentry_delete + NULL, }; struct inode_operations coda_dir_inode_operations = @@ -74,7 +78,7 @@ struct inode_operations coda_dir_inode_operations = coda_permission, /* permission */ NULL, /* smap */ NULL, /* update page */ - NULL /* revalidate */ + coda_revalidate_inode /* revalidate */ }; struct file_operations coda_dir_operations = { @@ -117,7 +121,6 @@ static int coda_lookup(struct inode *dir, struct dentry *entry) } dircnp = ITOC(dir); - CHECK_CNODE(dircnp); if ( length > CFS_MAXNAMLEN ) { printk("name too long: lookup, %s (%*s)\n", @@ -141,9 +144,13 @@ static int coda_lookup(struct inode *dir, struct dentry *entry) (const char *)name, length, &type, &resfid); res_inode = NULL; - if (!error || (error == -CFS_NOCACHE) ) { - if (error == -CFS_NOCACHE) + if (!error) { + if (type & CFS_NOCACHE) { + type &= (~CFS_NOCACHE); + CDEBUG(D_INODE, "dropme set for %s\n", + coda_f2s(&resfid)); dropme = 1; + } error = coda_cnode_make(&res_inode, &resfid, dir->i_sb); if (error) return -error; @@ -152,7 +159,7 @@ static int coda_lookup(struct inode *dir, struct dentry *entry) coda_f2s(&dircnp->c_fid), length, name, error); return error; } - CDEBUG(D_INODE, "lookup: %s is (%s) type %d result %d, dropme %d\n", + CDEBUG(D_INODE, "lookup: %s is (%s), type %d result %d, dropme %d\n", name, coda_f2s(&resfid), type, error, dropme); exit: @@ -228,7 +235,6 @@ static int coda_create(struct inode *dir, struct dentry *de, int mode) CHECK_CNODE(dircnp); if ( length > CFS_MAXNAMLEN ) { - char str[50]; printk("name too long: create, %s(%s)\n", coda_f2s(&dircnp->c_fid), name); return -ENAMETOOLONG; @@ -238,7 +244,6 @@ static int coda_create(struct inode *dir, struct dentry *de, int mode) 0, mode, &newfid, &attrs); if ( error ) { - char str[50]; CDEBUG(D_INODE, "create: %s, result %d\n", coda_f2s(&newfid), error); d_drop(de); @@ -321,7 +326,6 @@ static int coda_link(struct dentry *source_de, struct inode *dir_inode, const char * name = de->d_name.name; int len = de->d_name.len; struct coda_inode_info *dir_cnp, *cnp; - char str[50]; int error; ENTRY; @@ -408,7 +412,6 @@ int coda_unlink(struct inode *dir, struct dentry *de) int error; const char *name = de->d_name.name; int len = de->d_name.len; - char fidstr[50]; ENTRY; @@ -781,3 +784,82 @@ exit: CODA_FREE(buff, size); return error; } + +int coda_dentry_revalidate(struct dentry *de) +{ + int valid = 1; + struct inode *inode = de->d_inode; + struct coda_inode_info *cii; + ENTRY; + + if (inode) { + if (is_bad_inode(inode)) + return 0; + cii = ITOC(de->d_inode); + if (cii->c_flags & C_PURGE) + valid = 0; + } + return valid || coda_isroot(de->d_inode); +} + + +static int coda_refresh_inode(struct dentry *dentry) +{ + struct coda_vattr attr; + int error; + int old_mode; + ino_t old_ino; + struct inode *inode = dentry->d_inode; + struct coda_inode_info *cii = ITOC(inode); + + ENTRY; + error = venus_getattr(inode->i_sb, &(cii->c_fid), &attr); + if ( error ) { + make_bad_inode(inode); + return -EIO; + } + + /* this baby may be lost if: + - it's type changed + - it's ino changed + */ + old_mode = inode->i_mode; + old_ino = inode->i_ino; + coda_vattr_to_iattr(inode, &attr); + + if ((inode->i_ino != old_ino) || + ((old_mode & S_IFMT) != (inode->i_mode & S_IFMT))) { + make_bad_inode(inode); + inode->i_mode = old_mode; + return -EIO; + } + + cii->c_flags &= ~C_VATTR; + return 0; +} + + +/* + * This is called when we want to check if the inode has + * changed on the server. Coda makes this easy since the + * cache manager Venus issues a downcall to the kernel when this + * happens + */ + +int coda_revalidate_inode(struct dentry *dentry) +{ + int error = 0; + struct coda_inode_info *cii = ITOC(dentry->d_inode); + + ENTRY; + CDEBUG(D_INODE, "revalidating: %*s/%*s\n", + dentry->d_name.len, dentry->d_name.name, + dentry->d_parent->d_name.len, dentry->d_parent->d_name.name); + + if ( cii->c_flags & (C_VATTR | C_PURGE )) { + error = coda_refresh_inode(dentry); + } + + return error; +} + diff --git a/fs/coda/file.c b/fs/coda/file.c index b33680cc3..ae1dd9776 100644 --- a/fs/coda/file.c +++ b/fs/coda/file.c @@ -30,7 +30,7 @@ static ssize_t coda_file_read(struct file *f, char *buf, size_t count, loff_t *o static ssize_t coda_file_write(struct file *f, const char *buf, size_t count, loff_t *off); static int coda_file_mmap(struct file * file, struct vm_area_struct * vma); -/* exported from this file */ +/* also exported from this file (used for dirs) */ int coda_fsync(struct file *, struct dentry *dentry); struct inode_operations coda_file_inode_operations = { @@ -43,7 +43,7 @@ struct inode_operations coda_file_inode_operations = { NULL, /* mkdir */ NULL, /* rmdir */ NULL, /* mknod */ - NULL, /* rename */ + NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ coda_readpage, /* readpage */ @@ -53,7 +53,7 @@ struct inode_operations coda_file_inode_operations = { coda_permission, /* permission */ NULL, /* smap */ NULL, /* update page */ - NULL /* revalidate */ + coda_revalidate_inode /* revalidate */ }; struct file_operations coda_file_operations = { @@ -74,41 +74,47 @@ struct file_operations coda_file_operations = { }; /* File file operations */ -static int coda_readpage(struct file * file, struct page * page) +static int coda_readpage(struct file * coda_file, struct page * page) { - struct dentry *de = file->f_dentry; - struct inode *inode = de->d_inode; + struct dentry *de = coda_file->f_dentry; + struct inode *coda_inode = de->d_inode; struct dentry cont_dentry; - struct inode *cont_inode; - struct coda_inode_info *cnp; + struct file cont_file; + struct coda_inode_info *cii; ENTRY; - cnp = ITOC(inode); - CHECK_CNODE(cnp); + cii = ITOC(coda_inode); - if ( ! cnp->c_ovp ) { - printk("coda_readpage: no open inode for ino %ld\n", inode->i_ino); + if ( ! cii->c_ovp ) { + printk("coda_readpage: no open inode for ino %ld, %s\n", + coda_inode->i_ino, de->d_name.name); return -ENXIO; } + + coda_prepare_openfile(coda_inode, coda_file, cii->c_ovp, + &cont_file, &cont_dentry); - cont_inode = cnp->c_ovp; - cont_dentry.d_inode = cont_inode; - - CDEBUG(D_INODE, "coda ino: %ld, cached ino %ld, page offset: %lx\n", inode->i_ino, cont_inode->i_ino, page->offset); + CDEBUG(D_INODE, "coda ino: %ld, cached ino %ld, page offset: %lx\n", + coda_inode->i_ino, cii->c_ovp->i_ino, page->offset); - generic_readpage(&cont_dentry, page); + generic_readpage(&cont_file, page); EXIT; return 0; } static int coda_file_mmap(struct file * file, struct vm_area_struct * vma) { - struct coda_inode_info *cnp; - cnp = ITOC(file->f_dentry->d_inode); - cnp->c_mmcount++; + struct coda_inode_info *cii; + int res; + + ENTRY; + cii = ITOC(file->f_dentry->d_inode); + cii->c_mmcount++; - return generic_file_mmap(file, vma); + res =generic_file_mmap(file, vma); + EXIT; + return res; } static ssize_t coda_file_read(struct file *coda_file, char *buff, @@ -120,7 +126,6 @@ static ssize_t coda_file_read(struct file *coda_file, char *buff, struct file cont_file; struct dentry cont_dentry; int result = 0; - ENTRY; cnp = ITOC(coda_inode); diff --git a/fs/coda/super.c b/fs/coda/inode.c index 5410fb50d..96d07e265 100644 --- a/fs/coda/super.c +++ b/fs/coda/inode.c @@ -4,7 +4,7 @@ * Copryright (C) 1996 Peter J. Braam <braam@maths.ox.ac.uk> and * Michael Callahan <callahan@maths.ox.ac.uk> * - * Rewritten for Linux 2.1.?? Peter Braam <braam@cs.cmu.edu> + * Rewritten for Linux 2.1. Peter Braam <braam@cs.cmu.edu> * Copyright (C) Carnegie Mellon University */ @@ -81,7 +81,6 @@ static struct super_block * coda_read_super(struct super_block *sb, ViceFid fid; kdev_t dev = sb->s_dev; int error; - char str[50]; ENTRY; MOD_INC_USE_COUNT; @@ -180,10 +179,10 @@ static void coda_put_super(struct super_block *sb) /* all filling in of inodes postponed until lookup */ static void coda_read_inode(struct inode *inode) { - struct coda_inode_info *cnp; + struct coda_inode_info *cii; ENTRY; - cnp = ITOC(inode); - cnp->c_magic = 0; + cii = ITOC(inode); + cii->c_magic = 0; return; } @@ -200,32 +199,32 @@ static void coda_put_inode(struct inode *in) static void coda_delete_inode(struct inode *inode) { - struct coda_inode_info *cnp; + struct coda_inode_info *cii; struct inode *open_inode; ENTRY; CDEBUG(D_SUPER, " inode->ino: %ld, count: %d\n", inode->i_ino, inode->i_count); - cnp = ITOC(inode); - if ( inode->i_ino == CTL_INO || cnp->c_magic != CODA_CNODE_MAGIC ) { + cii = ITOC(inode); + if ( inode->i_ino == CTL_INO || cii->c_magic != CODA_CNODE_MAGIC ) { clear_inode(inode); return; } - if ( coda_fid_is_volroot(&cnp->c_fid) ) - list_del(&cnp->c_volrootlist); + if ( coda_fid_is_volroot(&cii->c_fid) ) + list_del(&cii->c_volrootlist); - open_inode = cnp->c_ovp; + open_inode = cii->c_ovp; if ( open_inode ) { CDEBUG(D_SUPER, "DELINO cached file: ino %ld count %d.\n", open_inode->i_ino, open_inode->i_count); - cnp->c_ovp = NULL; + cii->c_ovp = NULL; iput(open_inode); } - coda_cache_clear_cnp(cnp); + coda_cache_clear_inode(inode); inode->u.generic_ip = NULL; clear_inode(inode); @@ -235,24 +234,24 @@ static void coda_delete_inode(struct inode *inode) static int coda_notify_change(struct dentry *de, struct iattr *iattr) { struct inode *inode = de->d_inode; - struct coda_inode_info *cnp; + struct coda_inode_info *cii; struct coda_vattr vattr; int error; ENTRY; memset(&vattr, 0, sizeof(vattr)); - cnp = ITOC(inode); - CHECK_CNODE(cnp); + cii = ITOC(inode); + CHECK_CNODE(cii); coda_iattr_to_vattr(iattr, &vattr); vattr.va_type = C_VNON; /* cannot set type */ CDEBUG(D_SUPER, "vattr.va_mode %o\n", vattr.va_mode); - error = venus_setattr(inode->i_sb, &cnp->c_fid, &vattr); + error = venus_setattr(inode->i_sb, &cii->c_fid, &vattr); if ( !error ) { coda_vattr_to_iattr(inode, &vattr); - coda_cache_clear_cnp(cnp); + coda_cache_clear_inode(inode); } CDEBUG(D_SUPER, "inode.i_mode %o, error %d\n", inode->i_mode, error); diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c index 04333e046..119d14f51 100644 --- a/fs/coda/psdev.c +++ b/fs/coda/psdev.c @@ -65,7 +65,7 @@ extern int cfsnc_nc_info(char *buffer, char **start, off_t offset, int length, i /* statistics */ struct coda_upcallstats coda_callstats; int coda_hard = 0; /* introduces a timeout on upcalls */ -unsigned long coda_timeout = 10; /* .. secs, then signals will dequeue */ +unsigned long coda_timeout = 30; /* .. secs, then signals will dequeue */ extern struct coda_sb_info coda_super_info[MAX_CODADEVS]; struct vcomm psdev_vcomm[MAX_CODADEVS]; @@ -447,7 +447,7 @@ MODULE_AUTHOR("Peter J. Braam <braam@cs.cmu.edu>"); int init_module(void) { int status; - printk(KERN_INFO "Coda Kernel/User communications module 1.0\n"); + printk(KERN_INFO "Coda Kernel/User communications module 2.0\n"); status = init_coda_psdev(); if ( status ) { diff --git a/fs/coda/upcall.c b/fs/coda/upcall.c index ac625ad17..13d3127c6 100644 --- a/fs/coda/upcall.c +++ b/fs/coda/upcall.c @@ -500,7 +500,6 @@ int venus_pioctl(struct super_block *sb, struct ViceFid *fid, union outputArgs *outp; int insize, outsize, error; int iocsize; - char str[50]; insize = VC_MAXMSGSIZE; UPARG(CFS_IOCTL); @@ -587,7 +586,6 @@ int venus_pioctl(struct super_block *sb, struct ViceFid *fid, static inline void coda_waitfor_upcall(struct vmsg *vmp) { struct wait_queue wait = { current, NULL }; - old_sigset_t pending; vmp->vm_posttime = jiffies; @@ -608,13 +606,9 @@ static inline void coda_waitfor_upcall(struct vmsg *vmp) if ( jiffies > vmp->vm_posttime + coda_timeout * HZ ) break; - spin_lock_irq(¤t->sigmask_lock); - pending = current->blocked.sig[0] & current->signal.sig[0]; - spin_unlock_irq(¤t->sigmask_lock); - /* if this process really wants to die, let it go */ - if ( sigismember(&pending, SIGKILL) || - sigismember(&pending, SIGINT) ) + if ( sigismember(¤t->signal, SIGKILL) || + sigismember(¤t->signal, SIGINT) ) break; else schedule(); @@ -765,10 +759,14 @@ ENTRY; * This call is a result of token expiration. * * The next arise as the result of callbacks on a file or directory. - * CFS_ZAPDIR -- flush the attributes for the dir from its cnode. - * Zap all children of this directory from the namecache. * CFS_ZAPFILE -- flush the cached attributes for a file. - * CFS_ZAPVNODE -- intended to be a zapfile for just one cred. Not used? + + * CFS_ZAPDIR -- flush the attributes for the dir and + * force a new lookup for all the children + of this dir. + + * CFS_ZAPVNODE -- intended to be a zapfile for just one cred. + Not used? * * The next is a result of Venus detecting an inconsistent file. * CFS_PURGEFID -- flush the attribute for the file @@ -803,53 +801,48 @@ int coda_downcall(int opcode, union outputArgs * out, struct super_block *sb) return(0); } case CFS_ZAPDIR : { + struct inode *inode; ViceFid *fid = &out->cfs_zapdir.CodaFid; - char str[50]; if ( !fid ) { printk("ZAPDIR: Null fid\n"); return 0; } CDEBUG(D_DOWNCALL, "zapdir: fid = %s\n", coda_f2s(fid)); clstats(CFS_ZAPDIR); - coda_zapfid(fid, sb, C_ZAPDIR); - return(0); - } - case CFS_ZAPVNODE : { - ViceFid *fid = &out->cfs_zapvnode.VFid; - char str[50]; - struct coda_cred *cred = &out->cfs_zapvnode.cred; - if ( !fid || !cred ) { - printk("ZAPVNODE: Null fid or cred\n"); - return 0; - } - CDEBUG(D_DOWNCALL, "zapvnode: fid = %s\n", coda_f2s(fid)); - coda_zapfid(fid, sb, C_ZAPFID); - coda_cache_clear_cred(sb, cred); - clstats(CFS_ZAPVNODE); + inode = coda_fid_to_inode(fid, sb); + coda_flag_inode(inode, C_VATTR); + coda_cache_clear_inode(inode); + coda_flag_alias_children(inode, C_PURGE); return(0); } + + case CFS_ZAPVNODE : case CFS_ZAPFILE : { + struct inode *inode; struct ViceFid *fid = &out->cfs_zapfile.CodaFid; - char str[50]; clstats(CFS_ZAPFILE); if ( !fid ) { printk("ZAPFILE: Null fid\n"); return 0; } CDEBUG(D_DOWNCALL, "zapfile: fid = %s\n", coda_f2s(fid)); - coda_zapfid(fid, sb, C_ZAPFID); + inode = coda_fid_to_inode(fid, sb); + coda_flag_inode(inode, C_VATTR); + coda_cache_clear_inode(inode); return 0; } case CFS_PURGEFID : { + struct inode *inode; ViceFid *fid = &out->cfs_purgefid.CodaFid; - char str[50]; if ( !fid ) { printk("PURGEFID: Null fid\n"); return 0; } CDEBUG(D_DOWNCALL, "purgefid: fid = %s\n", coda_f2s(fid)); clstats(CFS_PURGEFID); - coda_zapfid(fid, sb, C_ZAPDIR); + inode = coda_fid_to_inode(fid, sb); + coda_flag_inode(inode, C_PURGE); + coda_cache_clear_inode(inode); return 0; } case CFS_REPLACE : { diff --git a/fs/devices.c b/fs/devices.c index 9cae0441e..6a8a60627 100644 --- a/fs/devices.c +++ b/fs/devices.c @@ -6,6 +6,7 @@ * Copyright (C) 1991, 1992 Linus Torvalds * * Added kerneld support: Jacques Gelinas and Bjorn Ekwall + * (changed to kmod) */ #include <linux/config.h> @@ -16,12 +17,12 @@ #include <linux/stat.h> #include <linux/fcntl.h> #include <linux/errno.h> -#ifdef CONFIG_KERNELD -#include <linux/kerneld.h> +#ifdef CONFIG_KMOD +#include <linux/kmod.h> #include <linux/tty.h> -/* serial module kerneld load support */ +/* serial module kmod load support */ struct tty_driver *get_tty_driver(kdev_t device); #define isa_tty_dev(ma) (ma == TTY_MAJOR || ma == TTYAUX_MAJOR) #define need_serial(ma,mi) (get_tty_driver(MKDEV(ma,mi)) == NULL) @@ -74,12 +75,12 @@ static struct file_operations * get_fops( struct file_operations *ret = NULL; if (major < maxdev){ -#ifdef CONFIG_KERNELD +#ifdef CONFIG_KMOD /* * I do get request for device 0. I have no idea why. It happen * at shutdown time for one. Without the following test, the * kernel will happily trigger a request_module() which will - * trigger kerneld and modprobe for nothing (since there + * trigger kmod and modprobe for nothing (since there * is no device with major number == 0. And furthermore * it locks the reboot process :-( * @@ -87,7 +88,7 @@ static struct file_operations * get_fops( * * A. Haritsis <ah@doc.ic.ac.uk>: fix for serial module * though we need the minor here to check if serial dev, - * we pass only the normal major char dev to kerneld + * we pass only the normal major char dev to kmod * as there is no other loadable dev on these majors */ if ((isa_tty_dev(major) && need_serial(major,minor)) || @@ -49,8 +49,9 @@ #include <asm/mmu_context.h> #include <linux/config.h> -#ifdef CONFIG_KERNELD -#include <linux/kerneld.h> + +#ifdef CONFIG_KMOD +#include <linux/kmod.h> #endif asmlinkage int sys_exit(int exit_code); @@ -421,6 +422,7 @@ static int exec_mmap(void) retval = new_page_tables(current); if (retval) goto fail_restore; + up(&mm->mmap_sem); mmput(old_mm); return 0; @@ -697,7 +699,7 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs) } if (retval != -ENOEXEC) { break; -#ifdef CONFIG_KERNELD +#ifdef CONFIG_KMOD }else{ #define printable(c) (((c)=='\t') || ((c)=='\n') || (0x20<=(c) && (c)<=0x7e)) char modname[20]; diff --git a/fs/fcntl.c b/fs/fcntl.c index 8743916ee..5cfeb3658 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -9,6 +9,7 @@ #include <linux/errno.h> #include <linux/stat.h> #include <linux/fcntl.h> +#include <linux/file.h> #include <linux/string.h> #include <linux/mm.h> #include <linux/smp.h> @@ -22,18 +23,32 @@ extern int sock_fcntl (struct file *, unsigned int cmd, unsigned long arg); static inline int dupfd(unsigned int fd, unsigned int arg) { struct files_struct * files = current->files; + struct file * file; + int error; - if (fd >= NR_OPEN || !files->fd[fd]) - return -EBADF; + error = -EINVAL; if (arg >= NR_OPEN) - return -EINVAL; + goto out; + + error = -EBADF; + file = fget(fd); + if (!file) + goto out; + + error = -EMFILE; arg = find_next_zero_bit(&files->open_fds, NR_OPEN, arg); if (arg >= current->rlim[RLIMIT_NOFILE].rlim_cur) - return -EMFILE; + goto out_putf; FD_SET(arg, &files->open_fds); FD_CLR(arg, &files->close_on_exec); - (files->fd[arg] = files->fd[fd])->f_count++; - return arg; + fd_install(arg, file); + error = arg; +out: + return error; + +out_putf: + fput(file); + goto out; } asmlinkage int sys_dup2(unsigned int oldfd, unsigned int newfd) @@ -41,7 +56,7 @@ asmlinkage int sys_dup2(unsigned int oldfd, unsigned int newfd) int err = -EBADF; lock_kernel(); - if (oldfd >= NR_OPEN || !current->files->fd[oldfd]) + if (!fcheck(oldfd)) goto out; err = newfd; if (newfd == oldfd) @@ -51,7 +66,7 @@ asmlinkage int sys_dup2(unsigned int oldfd, unsigned int newfd) goto out; /* following POSIX.1 6.2.1 */ sys_close(newfd); - err = dupfd(oldfd,newfd); + err = dupfd(oldfd, newfd); out: unlock_kernel(); return err; @@ -62,7 +77,7 @@ asmlinkage int sys_dup(unsigned int fildes) int ret; lock_kernel(); - ret = dupfd(fildes,0); + ret = dupfd(fildes, 0); unlock_kernel(); return ret; } @@ -101,12 +116,13 @@ asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) long err = -EBADF; lock_kernel(); - if (fd >= NR_OPEN || !(filp = current->files->fd[fd])) + filp = fget(fd); + if (!filp) goto out; err = 0; switch (cmd) { case F_DUPFD: - err = dupfd(fd,arg); + err = dupfd(fd, arg); break; case F_GETFD: err = FD_ISSET(fd, ¤t->files->close_on_exec); @@ -158,6 +174,7 @@ asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) err = -EINVAL; break; } + fput(filp); out: unlock_kernel(); return err; diff --git a/fs/filesystems.c b/fs/filesystems.c index 01a2a6d24..f56b35c53 100644 --- a/fs/filesystems.c +++ b/fs/filesystems.c @@ -30,8 +30,8 @@ #include <linux/major.h> #include <linux/smp.h> #include <linux/smp_lock.h> -#ifdef CONFIG_KERNELD -#include <linux/kerneld.h> +#ifdef CONFIG_KMOD +#include <linux/kmod.h> #endif #include <linux/lockd/bind.h> #include <linux/lockd/xdr.h> @@ -192,12 +192,12 @@ asmlinkage sys_nfsservctl(int cmd, void *argp, void *resp) ret = do_nfsservctl(cmd, argp, resp); goto out; } -#ifdef CONFIG_KERNELD +#ifdef CONFIG_KMOD if (request_module ("nfsd") == 0) { if (do_nfsservctl) ret = do_nfsservctl(cmd, argp, resp); } -#endif /* CONFIG_KERNELD */ +#endif /* CONFIG_KMOD */ out: unlock_kernel(); return ret; diff --git a/fs/hfs/ChangeLog b/fs/hfs/ChangeLog index aa465a0a6..c063e3f2d 100644 --- a/fs/hfs/ChangeLog +++ b/fs/hfs/ChangeLog @@ -1,12 +1,55 @@ -Wed Jan 7 19:33:33 1998 a sun <asun@zoology.washington.edu> +Wed Jan 21 14:04:26 1998 a sun <asun@zoology.washington.edu> + + * inode.c, sysdep.c + use d_iput to uncache dentry from catalog entry instead of relying + on put_inode. no more NULL pointer dereferences! + + * catalog.c + cleaned up hfs_cat_put a little. + + ISSUES (non-fatal): mv dir dir2 while creating files in dir screws + up directory counts. + + deletion using netatalk screws up directory + counts. + +Thu Jan 15 19:14:28 1998 a sun <asun@zoology.washington.edu> + + * catalog.c + make deletion happen when requested instead of waiting until + an hfs_cat_put as the dcache can hold onto entries for quite + some time. + +Wed Jan 14 14:43:16 1998 a sun <asun@zoology.washington.edu> + + * catalog.c + the current catalog allocation scheme allocates + PAGE_SIZE/sizeof(struct hfs_cat_entry) entries at a time and keeps + a pool of free entries up to this allocation unit * 8. * inode.c - don't hfs_cat_put in hfs_iget. that's a bad idea and results - in screwed up entry counts. + make sure to always hfs_cat_put if hfs_iget is going to return + NULL. + + * string.c, catalog.c + use linux' hashing method to generate hashes. the old hashing was + getting collisions. catalog.c also has a larger hash table to + prevent collisions. + +Tue Jan 13 13:06:01 1998 a sun <asun@zoology.washington.edu> + + * version.c + bumped to 0.95+asun3 * catalog.c - modified hfs_cat_put to undirty deleted entries without trying to - write them out. + re-wrote to dynamically allocate/delete catalog entries. on a 486, + entries fit into the size-256 slab. + +Wed Jan 7 19:33:33 1998 a sun <asun@zoology.washington.edu> + + * inode.c + don't hfs_cat_put gratuitously in hfs_iget. that's a bad + idea and results in screwed up entry counts. Tue Jan 6 14:38:24 1998 a sun <asun@zoology.washington.edu> diff --git a/fs/hfs/Makefile b/fs/hfs/Makefile index bab9a6e4b..7ea8e6560 100644 --- a/fs/hfs/Makefile +++ b/fs/hfs/Makefile @@ -1,5 +1,5 @@ # -# Makefile for the linux nfs-filesystem routines. +# Makefile for the linux hfs-filesystem routines. # # Note! Dependencies are done automagically by 'make dep', which also # removes any old dependencies. DON'T put your own dependencies here diff --git a/fs/hfs/catalog.c b/fs/hfs/catalog.c index 4055012f1..2435ceb31 100644 --- a/fs/hfs/catalog.c +++ b/fs/hfs/catalog.c @@ -10,6 +10,7 @@ * * Cache code shamelessly stolen from * linux/fs/inode.c Copyright (C) 1991, 1992 Linus Torvalds + * re-shamelessly stolen Copyright (C) 1997 Linus Torvalds * * In function preconditions the term "valid" applied to a pointer to * a structure means that the pointer is non-NULL and the structure it @@ -24,16 +25,15 @@ /*================ Variable-like macros ================*/ -#define NUM_FREE_ENTRIES 8 - /* Number of hash table slots */ -#define CCACHE_NR 128 - -/* Max number of entries in memory */ -#define CCACHE_MAX 1024 +#define C_HASHBITS 10 +#define C_HASHSIZE (1UL << C_HASHBITS) +#define C_HASHMASK (C_HASHSIZE - 1) -/* Number of entries to fit in a single page on an i386 */ -#define CCACHE_INC ((PAGE_SIZE - sizeof(void *))/sizeof(struct hfs_cat_entry)) +/* Number of entries to fit in a single page on an i386. + * Actually, now it's used to increment the free entry pool. */ +#define CCACHE_INC (PAGE_SIZE/sizeof(struct hfs_cat_entry)) +#define CCACHE_MAX (CCACHE_INC * 8) /*================ File-local data types ================*/ @@ -94,18 +94,11 @@ struct hfs_cat_rec { } u; }; - -struct allocation_unit { - struct allocation_unit *next; - struct hfs_cat_entry entries[CCACHE_INC]; -}; - /*================ File-local variables ================*/ static LIST_HEAD(entry_in_use); -static LIST_HEAD(entry_dirty); /* all the dirty entries */ static LIST_HEAD(entry_unused); -static struct list_head hash_table[CCACHE_NR]; +static struct list_head hash_table[C_HASHSIZE]; spinlock_t entry_lock = SPIN_LOCK_UNLOCKED; @@ -114,8 +107,6 @@ static struct { int nr_free_entries; } entries_stat; -static struct allocation_unit *allocation = NULL; - /*================ File-local functions ================*/ /* @@ -136,13 +127,16 @@ static inline hfs_u32 brec_to_id(struct hfs_brec *brec) * * hash an (struct mdb *) and a (struct hfs_cat_key *) to an integer. */ -static inline unsigned int hashfn(const struct hfs_mdb *mdb, +static inline unsigned long hashfn(const struct hfs_mdb *mdb, const struct hfs_cat_key *key) { #define LSB(X) (((unsigned char *)(&X))[3]) - return ((unsigned int)LSB(mdb->create_date) ^ - (unsigned int)key->ParID[3] ^ - hfs_strhash(&key->CName)) % CCACHE_NR; + unsigned long hash; + + hash = (unsigned long) mdb | (unsigned long) key->ParID[3] | + hfs_strhash(&key->CName); + hash = hash ^ (hash >> C_HASHBITS) ^ (hash >> C_HASHBITS*2); + return hash & C_HASHMASK; #undef LSB } @@ -208,24 +202,7 @@ static void unlock_entry(struct hfs_cat_entry * entry) hfs_wake_up(&entry->wait); } -/* - * clear_entry() - * - * Zero all the fields of an entry and place it on the free list. - */ -static void clear_entry(struct hfs_cat_entry * entry) -{ - wait_on_entry(entry); - /* zero all but the wait queue */ - memset(&entry->wait, 0, - sizeof(*entry) - offsetof(struct hfs_cat_entry, wait)); - INIT_LIST_HEAD(&entry->hash); - INIT_LIST_HEAD(&entry->list); - INIT_LIST_HEAD(&entry->dirty); -} - -/* put entry on mdb dirty list. this only does it if it's on the hash - * list. we also add it to the global dirty list as well. */ +/* put entry on mdb dirty list. */ void hfs_cat_mark_dirty(struct hfs_cat_entry *entry) { struct hfs_mdb *mdb = entry->mdb; @@ -234,153 +211,74 @@ void hfs_cat_mark_dirty(struct hfs_cat_entry *entry) if (!(entry->state & HFS_DIRTY)) { entry->state |= HFS_DIRTY; - /* Only add valid (ie hashed) entries to the - * dirty list */ + /* Only add valid (ie hashed) entries to the dirty list. */ if (!list_empty(&entry->hash)) { list_del(&entry->list); list_add(&entry->list, &mdb->entry_dirty); - INIT_LIST_HEAD(&entry->dirty); - list_add(&entry->dirty, &entry_dirty); } } spin_unlock(&entry_lock); } -/* prune all entries */ -static void dispose_list(struct list_head *head) +/* delete an entry and remove it from the hash table. */ +static void delete_entry(struct hfs_cat_entry *entry) { - struct list_head *next; - int count = 0; - - next = head->next; - for (;;) { - struct list_head * tmp = next; - - next = next->next; - if (tmp == head) - break; - hfs_cat_prune(list_entry(tmp, struct hfs_cat_entry, list)); - count++; - } -} - -/* - * try_to_free_entries works by getting the underlying - * cache system to release entries. it gets called with the entry lock - * held. - * - * count can be up to 2 due to both a resource and data fork being - * listed. we can unuse dirty entries as well. - */ -#define CAN_UNUSE(tmp) (((tmp)->count < 3) && ((tmp)->state <= HFS_DIRTY)) -static int try_to_free_entries(const int goal) -{ - struct list_head *tmp, *head = &entry_in_use; - LIST_HEAD(freeable); - int found = 0, depth = goal << 1; - - /* try freeing from entry_in_use */ - while ((tmp = head->prev) != head && depth--) { - struct hfs_cat_entry *entry = - list_entry(tmp, struct hfs_cat_entry, list); - list_del(tmp); - if (CAN_UNUSE(entry)) { - list_del(&entry->hash); - INIT_LIST_HEAD(&entry->hash); - list_add(tmp, &freeable); - if (++found < goal) - continue; - break; + if (!(entry->state & HFS_DELETED)) { + entry->state |= HFS_DELETED; + list_del(&entry->hash); + INIT_LIST_HEAD(&entry->hash); + + if (entry->type == HFS_CDR_FIL) { + /* free all extents */ + entry->u.file.data_fork.lsize = 0; + hfs_extent_adj(&entry->u.file.data_fork); + entry->u.file.rsrc_fork.lsize = 0; + hfs_extent_adj(&entry->u.file.rsrc_fork); } - list_add(tmp, head); } +} - if (found < goal) { /* try freeing from global dirty list */ - head = &entry_dirty; - depth = goal << 1; - while ((tmp = head->prev) != head && depth--) { - struct hfs_cat_entry *entry = - list_entry(tmp, struct hfs_cat_entry, dirty); - list_del(tmp); - if (CAN_UNUSE(entry)) { - list_del(&entry->hash); - INIT_LIST_HEAD(&entry->hash); - list_del(&entry->list); - INIT_LIST_HEAD(&entry->list); - list_add(&entry->list, &freeable); - if (++found < goal) - continue; - break; - } - list_add(tmp, head); - } - } - - if (found) { - spin_unlock(&entry_lock); - dispose_list(&freeable); - spin_lock(&entry_lock); - } - return found; -} - -/* init_once */ -static inline void init_once(struct hfs_cat_entry *entry) +static inline void init_entry(struct hfs_cat_entry *entry) { - init_waitqueue(&entry->wait); + memset(entry, 0, sizeof(*entry)); + hfs_init_waitqueue(&entry->wait); INIT_LIST_HEAD(&entry->hash); INIT_LIST_HEAD(&entry->list); - INIT_LIST_HEAD(&entry->dirty); } /* - * grow_entries() + * hfs_cat_alloc() * - * Try to allocate more entries, adding them to the free list. this returns - * with the spinlock held if successful + * Try to allocate another entry. */ -static struct hfs_cat_entry *grow_entries(struct hfs_mdb *mdb) +static inline struct hfs_cat_entry *hfs_cat_alloc(void) { - struct allocation_unit *tmp; - struct hfs_cat_entry * entry; - int i; + struct hfs_cat_entry *entry; - spin_unlock(&entry_lock); - if ((entries_stat.nr_entries < CCACHE_MAX) && - HFS_NEW(tmp)) { - spin_lock(&entry_lock); - memset(tmp, 0, sizeof(*tmp)); - tmp->next = allocation; - allocation = tmp; - entry = tmp->entries; - for (i = 1; i < CCACHE_INC; i++) { - entry++; - init_once(entry); - list_add(&entry->list, &entry_unused); - } - init_once(tmp->entries); + if (!HFS_NEW(entry)) + return NULL; - entries_stat.nr_entries += CCACHE_INC; - entries_stat.nr_free_entries += CCACHE_INC - 1; - return tmp->entries; - } + init_entry(entry); + return entry; +} - /* allocation failed. do some pruning and try again */ - spin_lock(&entry_lock); - try_to_free_entries(entries_stat.nr_entries >> 2); - { - struct list_head *tmp = entry_unused.next; - if (tmp != &entry_unused) { - entries_stat.nr_free_entries--; - list_del(tmp); - entry = list_entry(tmp, struct hfs_cat_entry, list); - return entry; - } +/* this gets called with the spinlock held. */ +static int grow_entries(void) +{ + struct hfs_cat_entry *entry; + int i; + + for (i = 0; i < CCACHE_INC; i++) { + if (!(entry = hfs_cat_alloc())) + break; + list_add(&entry->list, &entry_unused); } - spin_unlock(&entry_lock); - return NULL; + entries_stat.nr_entries += i; + entries_stat.nr_free_entries += i; + + return i; } /* @@ -537,7 +435,8 @@ static void __write_entry(const struct hfs_cat_entry *entry, /* * write_entry() * - * Write a modified entry back to the catalog B-tree. + * Write a modified entry back to the catalog B-tree. this gets called + * with the entry locked. */ static void write_entry(struct hfs_cat_entry * entry) { @@ -577,6 +476,7 @@ static void write_entry(struct hfs_cat_entry * entry) } +/* this gets called with the spinlock held. */ static struct hfs_cat_entry *find_entry(struct hfs_mdb *mdb, const struct hfs_cat_key *key) { @@ -592,8 +492,9 @@ static struct hfs_cat_entry *find_entry(struct hfs_mdb *mdb, entry = list_entry(tmp, struct hfs_cat_entry, hash); if (entry->mdb != mdb) continue; - if (hfs_cat_compare(&entry->key, key)) + if (hfs_cat_compare(&entry->key, key)) { continue; + } entry->count++; break; } @@ -609,13 +510,14 @@ static struct hfs_cat_entry *get_new_entry(struct hfs_mdb *mdb, { struct hfs_cat_entry *entry; struct list_head *head = hash(mdb, key); - struct list_head *tmp = entry_unused.next; + struct list_head *tmp; - if (tmp != &entry_unused) { +add_new_entry: + tmp = entry_unused.next; + if ((tmp != &entry_unused) ) { list_del(tmp); entries_stat.nr_free_entries--; entry = list_entry(tmp, struct hfs_cat_entry, list); -add_new_entry: list_add(&entry->list, &entry_in_use); list_add(&entry->hash, head); entry->mdb = mdb; @@ -629,7 +531,8 @@ add_new_entry: if (hfs_bfind(&brec, mdb->cat_tree, HFS_BKEY(key), HFS_BFIND_READ_EQ)) { - /* uh oh. we failed to read the record */ + /* uh oh. we failed to read the record. + * the entry doesn't actually exist. */ entry->state |= HFS_DELETED; goto read_fail; } @@ -651,28 +554,18 @@ add_new_entry: return entry; } - /* - * Uhhuh.. We need to expand. Note that "grow_entries()" will - * release the spinlock, but will return with the lock held - * again if the allocation succeeded. - */ - entry = grow_entries(mdb); - if (entry) { - /* We released the lock, so.. */ - struct hfs_cat_entry * old = find_entry(mdb, key); - if (!old) - goto add_new_entry; - list_add(&entry->list, &entry_unused); - entries_stat.nr_free_entries++; - spin_unlock(&entry_lock); - wait_on_entry(old); - return old; - } - return entry; + /* try to allocate more entries. grow_entries() doesn't release + * the spinlock. */ + if (grow_entries()) + goto add_new_entry; + spin_unlock(&entry_lock); + return NULL; -read_fail: +read_fail: + /* spinlock unlocked already. we don't need to mark the entry + * dirty here because we know that it doesn't exist. */ remove_hash(entry); entry->state &= ~HFS_LOCK; hfs_wake_up(&entry->wait); @@ -694,11 +587,6 @@ static struct hfs_cat_entry *get_entry(struct hfs_mdb *mdb, struct hfs_cat_entry * entry; spin_lock(&entry_lock); - if (!entries_stat.nr_free_entries && - (entries_stat.nr_entries >= CCACHE_MAX)) - goto restock; - -search: entry = find_entry(mdb, key); if (!entry) { return get_new_entry(mdb, key, read); @@ -706,10 +594,6 @@ search: spin_unlock(&entry_lock); wait_on_entry(entry); return entry; - -restock: - try_to_free_entries(8); - goto search; } /* @@ -753,6 +637,9 @@ static void update_dir(struct hfs_mdb *mdb, struct hfs_cat_entry *dir, /* * Add a writer to dir, excluding readers. + * + * XXX: this is wrong. it allows a move to occur when a directory + * is being written to. */ static inline void start_write(struct hfs_cat_entry *dir) { @@ -880,7 +767,10 @@ static int create_entry(struct hfs_cat_entry *parent, struct hfs_cat_key *key, goto done; bail1: + /* entry really didn't exist, so we don't need to really delete it. + * we do need to remove it from the hash, though. */ entry->state |= HFS_DELETED; + remove_hash(entry); unlock_entry(entry); bail2: hfs_cat_put(entry); @@ -900,13 +790,21 @@ done: * entry that the entry is in a consistent state, since another * process may get the entry while we sleep. That is why we * 'goto repeat' after each operation that might sleep. + * + * ADDITIONAL NOTE: the sys_entries will remove themselves from + * the sys_entry list on the final iput, so we don't need + * to worry about them here. + * + * nothing in hfs_cat_put goes to sleep now except + * on the initial entry. */ void hfs_cat_put(struct hfs_cat_entry * entry) { if (entry) { wait_on_entry(entry); - if (!entry->count) {/* just in case */ + /* just in case. this should never happen. */ + if (!entry->count) { hfs_warn("hfs_cat_put: trying to free free entry: %p\n", entry); return; @@ -914,52 +812,41 @@ void hfs_cat_put(struct hfs_cat_entry * entry) spin_lock(&entry_lock); if (!--entry->count) { -repeat: - if ((entry->state & HFS_DELETED)) { - if (entry->type == HFS_CDR_FIL) { - /* free all extents */ - entry->u.file.data_fork.lsize = 0; - hfs_extent_adj(&entry->u.file.data_fork); - entry->u.file.rsrc_fork.lsize = 0; - hfs_extent_adj(&entry->u.file.rsrc_fork); - } - entry->state = 0; - } else if (entry->type == HFS_CDR_FIL) { + if ((entry->state & HFS_DELETED)) + goto entry_deleted; + + if ((entry->type == HFS_CDR_FIL)) { /* clear out any cached extents */ if (entry->u.file.data_fork.first.next) { hfs_extent_free(&entry->u.file.data_fork); - spin_unlock(&entry_lock); - wait_on_entry(entry); - spin_lock(&entry_lock); - goto repeat; } if (entry->u.file.rsrc_fork.first.next) { hfs_extent_free(&entry->u.file.rsrc_fork); - spin_unlock(&entry_lock); - wait_on_entry(entry); - spin_lock(&entry_lock); - goto repeat; } } /* if we put a dirty entry, write it out. */ if ((entry->state & HFS_DIRTY)) { - list_del(&entry->dirty); - INIT_LIST_HEAD(&entry->dirty); - spin_unlock(&entry_lock); + entry->state ^= HFS_DIRTY | HFS_LOCK; write_entry(entry); - spin_lock(&entry_lock); - entry->state &= ~HFS_DIRTY; - goto repeat; + entry->state &= ~HFS_LOCK; } list_del(&entry->hash); +entry_deleted: /* deleted entries have already been removed + * from the hash list. */ list_del(&entry->list); - spin_unlock(&entry_lock); - clear_entry(entry); - spin_lock(&entry_lock); - list_add(&entry->list, &entry_unused); - entries_stat.nr_free_entries++; + if (entries_stat.nr_free_entries > CCACHE_MAX) { + HFS_DELETE(entry); + entries_stat.nr_entries--; + } else { + spin_unlock(&entry_lock); + wait_on_entry(entry); + init_entry(entry); + spin_lock(&entry_lock); + list_add(&entry->list, &entry_unused); + entries_stat.nr_free_entries++; + } } spin_unlock(&entry_lock); } @@ -995,20 +882,37 @@ static void invalidate_list(struct list_head *head, struct hfs_mdb *mdb, if (entry->mdb != mdb) { continue; } + if (!entry->count) { list_del(&entry->hash); INIT_LIST_HEAD(&entry->hash); - list_del(&entry->dirty); - INIT_LIST_HEAD(&entry->dirty); list_del(&entry->list); list_add(&entry->list, dispose); continue; } - hfs_warn("hfs_fs: entry %p(%u:%lu) busy on removed device %s.\n", - entry, entry->count, entry->state, + + hfs_warn("hfs_fs: entry %p(%u) busy on removed device %s.\n", + entry, entry->count, hfs_mdb_name(entry->mdb->sys_mdb)); } +} + +/* delete entries from a list */ +static void delete_list(struct list_head *head) +{ + struct list_head *next = head->next; + struct hfs_cat_entry *entry; + + for (;;) { + struct list_head * tmp = next; + next = next->next; + if (tmp == head) { + break; + } + entry = list_entry(tmp, struct hfs_cat_entry, list); + HFS_DELETE(entry); + } } /* @@ -1026,7 +930,7 @@ void hfs_cat_invalidate(struct hfs_mdb *mdb) invalidate_list(&mdb->entry_dirty, mdb, &throw_away); spin_unlock(&entry_lock); - dispose_list(&throw_away); + delete_list(&throw_away); } /* @@ -1052,9 +956,6 @@ void hfs_cat_commit(struct hfs_mdb *mdb) if (!entry->count) insert = entry_in_use.prev; - /* remove from global dirty list */ - list_del(&entry->dirty); - INIT_LIST_HEAD(&entry->dirty); /* add to in_use list */ list_del(&entry->list); @@ -1077,16 +978,13 @@ void hfs_cat_commit(struct hfs_mdb *mdb) * * Releases all the memory allocated in grow_entries(). * Must call hfs_cat_invalidate() on all MDBs before calling this. + * This only gets rid of the unused pool of entries. all the other + * entry references should have either been freed by cat_invalidate + * or moved onto the unused list. */ void hfs_cat_free(void) { - struct allocation_unit *tmp; - - while (allocation) { - tmp = allocation->next; - HFS_DELETE(allocation); - allocation = tmp; - } + delete_list(&entry_unused); } /* @@ -1272,6 +1170,9 @@ struct hfs_cat_entry *hfs_cat_parent(struct hfs_cat_entry *entry) * Create a new file with the indicated name in the indicated directory. * The file will have the indicated flags, type and creator. * If successful an (struct hfs_cat_entry) is returned in '*result'. + * + * XXX: the presence of "record" probably means that the following two + * aren't currently SMP safe and need spinlocks. */ int hfs_cat_create(struct hfs_cat_entry *parent, struct hfs_cat_key *key, hfs_u8 flags, hfs_u32 type, hfs_u32 creator, @@ -1358,7 +1259,7 @@ int hfs_cat_delete(struct hfs_cat_entry *parent, struct hfs_cat_entry *entry, /* try to delete the file or directory */ if (!error) { - lock_entry(entry); + lock_entry(entry); if ((entry->state & HFS_DELETED)) { /* somebody beat us to it */ error = -ENOENT; @@ -1371,8 +1272,8 @@ int hfs_cat_delete(struct hfs_cat_entry *parent, struct hfs_cat_entry *entry, if (!error) { /* Mark the entry deleted and remove it from the cache */ - entry->state |= HFS_DELETED; - remove_hash(entry); + lock_entry(entry); + delete_entry(entry); /* try to delete the thread entry if it exists */ if (with_thread) { @@ -1380,6 +1281,7 @@ int hfs_cat_delete(struct hfs_cat_entry *parent, struct hfs_cat_entry *entry, (void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(&key)); } + unlock_entry(entry); update_dir(mdb, parent, is_dir, -1); } @@ -1430,10 +1332,12 @@ int hfs_cat_move(struct hfs_cat_entry *old_dir, struct hfs_cat_entry *new_dir, return -EINVAL; } + spin_lock(&entry_lock); while (mdb->rename_lock) { hfs_sleep_on(&mdb->rename_wait); } mdb->rename_lock = 1; + spin_unlock(&entry_lock); /* keep readers from getting confused by changing dir size */ start_write(new_dir); @@ -1501,7 +1405,7 @@ restart: &new_record, is_dir ? 2 + sizeof(DIR_REC) : 2 + sizeof(FIL_REC)); if (error == -EEXIST) { - dest->state |= HFS_DELETED; + delete_entry(dest); unlock_entry(dest); hfs_cat_put(dest); goto restart; @@ -1590,8 +1494,7 @@ have_distinct: /* Something went seriously wrong. The dir/file has been deleted. */ /* XXX try some recovery? */ - entry->state |= HFS_DELETED; - remove_hash(entry); + delete_entry(entry); goto bail1; } } @@ -1620,7 +1523,7 @@ have_distinct: /* delete any pre-existing or place-holder entry */ if (dest) { - dest->state |= HFS_DELETED; + delete_entry(dest); unlock_entry(dest); if (removed && dest->cnid) { *removed = dest; @@ -1639,7 +1542,7 @@ bail2: (void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(new_key)); update_dir(mdb, new_dir, is_dir, -1); bail3: - dest->state |= HFS_DELETED; + delete_entry(dest); } unlock_entry(dest); hfs_cat_put(dest); @@ -1649,8 +1552,10 @@ done: end_write(old_dir); } end_write(new_dir); + spin_lock(&entry_lock); mdb->rename_lock = 0; hfs_wake_up(&mdb->rename_wait); + spin_unlock(&entry_lock); return error; } @@ -1663,7 +1568,7 @@ void hfs_cat_init(void) int i; struct list_head *head = hash_table; - i = CCACHE_NR; + i = C_HASHSIZE; do { INIT_LIST_HEAD(head); head++; diff --git a/fs/hfs/dir.c b/fs/hfs/dir.c index 144d9d42d..afd794155 100644 --- a/fs/hfs/dir.c +++ b/fs/hfs/dir.c @@ -95,7 +95,7 @@ static inline void update_dirs_plus(struct hfs_cat_entry *dir, int is_dir) } /* - * update_dirs_plus() + * update_dirs_minus() * * Update the fields 'i_size', 'i_nlink', 'i_ctime', 'i_mtime' and * 'i_version' of the inodes associated with a directory that has @@ -138,10 +138,9 @@ static inline void mark_inodes_deleted(struct hfs_cat_entry *entry, for (i = 0; i < 4; ++i) { if ((de = entry->sys_entry[i]) && (dentry != de)) { - entry->sys_entry[i] = NULL; - dget(de); - d_delete(de); - dput(de); + dget(de); + d_delete(de); + dput(de); } } } @@ -198,7 +197,7 @@ int hfs_create(struct inode * dir, struct dentry *dentry, int mode) error = -EIO; } else { if (HFS_I(dir)->d_drop_op) - HFS_I(dir)->d_drop_op(HFS_I(dir)->file_type, dentry); + HFS_I(dir)->d_drop_op(dentry, HFS_I(dir)->file_type); d_instantiate(dentry, inode); } } @@ -285,7 +284,7 @@ int hfs_unlink(struct inode * dir, struct dentry *dentry) struct hfs_cat_key key; int error; - if (build_key(&key, dir, dentry->d_name.name, + if (build_key(&key, dir, dentry->d_name.name, dentry->d_name.len)) { error = -EPERM; } else if (!(victim = hfs_cat_get(entry->mdb, &key))) { @@ -386,15 +385,15 @@ int hfs_rename(struct inode *old_dir, struct dentry *old_dentry, } else { /* no existing inodes. just drop negative dentries */ if (HFS_I(new_dir)->d_drop_op) - HFS_I(new_dir)->d_drop_op(HFS_I(new_dir)->file_type, - new_dentry); + HFS_I(new_dir)->d_drop_op(new_dentry, + HFS_I(new_dir)->file_type); update_dirs_plus(new_parent, is_dir); } /* update dcache */ d_move(old_dentry, new_dentry); } - + hfs_cat_put(victim); /* Note that hfs_cat_put(NULL) is safe. */ return error; } diff --git a/fs/hfs/dir_cap.c b/fs/hfs/dir_cap.c index d489c86ca..a7bb7f633 100644 --- a/fs/hfs/dir_cap.c +++ b/fs/hfs/dir_cap.c @@ -1,5 +1,4 @@ -/* linux/fs/hfs/dir_cap.c - * +/* * Copyright (C) 1995-1997 Paul H. Hargrove * This file may be distributed under the terms of the GNU Public License. * @@ -154,11 +153,12 @@ static int cap_lookup(struct inode * dir, struct dentry *dentry) struct hfs_cat_entry *entry; struct hfs_cat_key key; struct inode *inode = NULL; - + if (!dir || !S_ISDIR(dir->i_mode)) { - goto done; + return -ENOENT; } + dentry->d_op = &hfs_dentry_operations; entry = HFS_I(dir)->entry; dtype = HFS_ITYPE(dir->i_ino); @@ -215,13 +215,13 @@ static int cap_lookup(struct inode * dir, struct dentry *dentry) HFS_I(dir)->file_type, dentry); /* Don't return a resource fork for a directory */ - if (inode && (dtype == HFS_CAP_RDIR) && + if (inode && (dtype == HFS_CAP_RDIR) && (HFS_I(inode)->entry->type == HFS_CDR_DIR)) { + iput(inode); /* this does an hfs_cat_put */ inode = NULL; } done: - dentry->d_op = &hfs_dentry_operations; d_add(dentry, inode); return 0; } @@ -261,7 +261,7 @@ static int cap_readdir(struct file * filp, return -EBADF; } - entry = HFS_I(dir)->entry; + entry = HFS_I(dir)->entry; type = HFS_ITYPE(dir->i_ino); skip_dirs = (type == HFS_CAP_RDIR); @@ -368,7 +368,7 @@ static int cap_readdir(struct file * filp, * related calls (create, rename, and mknod). the directory calls * should be immune. the relevant calls in dir.c call drop_dentry * upon successful completion. */ -void hfs_cap_drop_dentry(const ino_t type, struct dentry *dentry) +void hfs_cap_drop_dentry(struct dentry *dentry, const ino_t type) { if (type == HFS_CAP_DATA) { /* given name */ hfs_drop_special(DOT_FINDERINFO, dentry->d_parent, dentry); diff --git a/fs/hfs/dir_dbl.c b/fs/hfs/dir_dbl.c index c97247dc9..553fe8ef9 100644 --- a/fs/hfs/dir_dbl.c +++ b/fs/hfs/dir_dbl.c @@ -135,11 +135,12 @@ static int dbl_lookup(struct inode * dir, struct dentry *dentry) struct hfs_cat_entry *entry; struct hfs_cat_key key; struct inode *inode = NULL; - + if (!dir || !S_ISDIR(dir->i_mode)) { - goto done; + return -ENOENT; } + dentry->d_op = &hfs_dentry_operations; entry = HFS_I(dir)->entry; /* Perform name-mangling */ @@ -175,12 +176,11 @@ static int dbl_lookup(struct inode * dir, struct dentry *dentry) hfs_nameout(dir, &cname, dentry->d_name.name+1, dentry->d_name.len-1); hfs_cat_build_key(entry->cnid, &cname, &key); - inode = hfs_iget(hfs_cat_get(entry->mdb, &key), + inode = hfs_iget(hfs_cat_get(entry->mdb, &key), HFS_DBL_HDR, dentry); } done: - dentry->d_op = &hfs_dentry_operations; d_add(dentry, inode); return 0; } @@ -219,7 +219,7 @@ static int dbl_readdir(struct file * filp, return -EBADF; } - entry = HFS_I(dir)->entry; + entry = HFS_I(dir)->entry; if (filp->f_pos == 0) { /* Entry 0 is for "." */ @@ -414,14 +414,14 @@ static int dbl_rename(struct inode *old_dir, struct dentry *old_dentry, { int error; - if (is_hdr(new_dir, new_dentry->d_name.name, + if (is_hdr(new_dir, new_dentry->d_name.name, new_dentry->d_name.len)) { error = -EPERM; } else { error = hfs_rename(old_dir, old_dentry, new_dir, new_dentry); if ((error == -ENOENT) /*&& !must_be_dir*/ && - is_hdr(old_dir, old_dentry->d_name.name, + is_hdr(old_dir, old_dentry->d_name.name, old_dentry->d_name.len)) { error = -EPERM; } @@ -435,9 +435,8 @@ static int dbl_rename(struct inode *old_dir, struct dentry *old_dentry, * as far as i can tell, the calls that need to do this are the file * related calls (create, rename, and mknod). the directory calls * should be immune. the relevant calls in dir.c call drop_dentry - * upon successful completion. this allocates an array for %name - * on the first attempt to access it. */ -void hfs_dbl_drop_dentry(const ino_t type, struct dentry *dentry) + * upon successful completion. */ +void hfs_dbl_drop_dentry(struct dentry *dentry, const ino_t type) { unsigned char tmp_name[HFS_NAMEMAX + 1]; struct dentry *de = NULL; diff --git a/fs/hfs/dir_nat.c b/fs/hfs/dir_nat.c index 62c9ea2cb..b29bfdc17 100644 --- a/fs/hfs/dir_nat.c +++ b/fs/hfs/dir_nat.c @@ -142,9 +142,10 @@ static int nat_lookup(struct inode * dir, struct dentry *dentry) struct inode *inode = NULL; if (!dir || !S_ISDIR(dir->i_mode)) { - goto done; + return -ENOENT; } + dentry->d_op = &hfs_dentry_operations; entry = HFS_I(dir)->entry; dtype = HFS_ITYPE(dir->i_ino); @@ -200,12 +201,11 @@ static int nat_lookup(struct inode * dir, struct dentry *dentry) if (inode && (dtype == HFS_NAT_HDIR) && (HFS_I(inode)->entry != entry) && (HFS_I(inode)->entry->type == HFS_CDR_DIR)) { - iput(inode); + iput(inode); /* this does an hfs_cat_put */ inode = NULL; } done: - dentry->d_op = &hfs_dentry_operations; d_add(dentry, inode); return 0; } @@ -241,7 +241,7 @@ static int nat_readdir(struct file * filp, return -EBADF; } - entry = HFS_I(dir)->entry; + entry = HFS_I(dir)->entry; type = HFS_ITYPE(dir->i_ino); skip_dirs = (type == HFS_NAT_HDIR); @@ -329,7 +329,7 @@ static int nat_readdir(struct file * filp, * related calls (create, rename, and mknod). the directory calls * should be immune. the relevant calls in dir.c call drop_dentry * upon successful completion. */ -void hfs_nat_drop_dentry(const ino_t type, struct dentry *dentry) +void hfs_nat_drop_dentry(struct dentry *dentry, const ino_t type) { struct dentry *de; diff --git a/fs/hfs/file.c b/fs/hfs/file.c index 26f498305..e12792036 100644 --- a/fs/hfs/file.c +++ b/fs/hfs/file.c @@ -98,11 +98,10 @@ struct buffer_head *hfs_getblk(struct hfs_fork *fork, int block, int create) to the file until we return, so it can't have moved. */ if (tmp) { - hfs_cat_mark_dirty(fork->entry); - return getblk(dev, tmp, HFS_SECTOR_SIZE); + hfs_cat_mark_dirty(fork->entry); + return getblk(dev, tmp, HFS_SECTOR_SIZE); } return NULL; - } else { /* If reading the block, then retry since the location on disk could have changed while @@ -236,6 +235,7 @@ static hfs_rwret_t hfs_file_write(struct file * filp, const char * buf, */ static void hfs_file_truncate(struct inode * inode) { + /*struct inode *inode = dentry->d_inode;*/ struct hfs_fork *fork = HFS_I(inode)->fork; fork->lsize = inode->i_size; @@ -268,7 +268,7 @@ static inline void xlate_to_user(char *buf, const char *data, int count) */ static inline void xlate_from_user(char *data, const char *buf, int count) { - copy_from_user(data, buf, count); + count -= copy_from_user(data, buf, count); while (count--) { if (*data == '\n') { *data = '\r'; @@ -398,16 +398,16 @@ hfs_s32 hfs_do_read(struct inode *inode, struct hfs_fork * fork, hfs_u32 pos, } else { chars = HFS_SECTOR_SIZE - offset; } - count -= chars; - read += chars; p = (*bhe)->b_data + offset; if (convert) { xlate_to_user(buf, p, chars); } else { - copy_to_user(buf, p, chars); + chars -= copy_to_user(buf, p, chars); } brelse(*bhe); + count -= chars; buf += chars; + read += chars; offset = 0; if (++bhe == &buflist[NBUF]) { bhe = buflist; @@ -479,7 +479,7 @@ hfs_s32 hfs_do_write(struct inode *inode, struct hfs_fork * fork, hfs_u32 pos, if (convert) { xlate_from_user(p, buf, c); } else { - copy_from_user(p, buf, c); + c -= copy_from_user(p, buf, c); } update_vm_cache(inode,pos,p,c); pos += c; diff --git a/fs/hfs/file_cap.c b/fs/hfs/file_cap.c index 7c298264a..10f39f751 100644 --- a/fs/hfs/file_cap.c +++ b/fs/hfs/file_cap.c @@ -164,8 +164,7 @@ static hfs_rwret_t cap_info_read(struct file *filp, char *buf, memcount = left; } cap_build_meta(&meta, entry); - /* is copy_to_user guaranteed to write memcount? */ - copy_to_user(buf, ((char *)&meta) + pos, memcount); + memcount -= copy_to_user(buf, ((char *)&meta) + pos, memcount); left -= memcount; read += memcount; pos += memcount; @@ -291,6 +290,8 @@ static hfs_rwret_t cap_info_write(struct file *filp, const char *buf, */ static void cap_info_truncate(struct inode *inode) { + /*struct inode *inode = dentry->d_inode;*/ + if (inode->i_size > HFS_FORK_MAX) { inode->i_size = HFS_FORK_MAX; } diff --git a/fs/hfs/file_hdr.c b/fs/hfs/file_hdr.c index 468a3f518..049381dd0 100644 --- a/fs/hfs/file_hdr.c +++ b/fs/hfs/file_hdr.c @@ -288,12 +288,10 @@ static inline void adjust_forks(struct hfs_cat_entry *entry, (descr->length != entry->u.file.data_fork.lsize)) { entry->u.file.data_fork.lsize = descr->length; hfs_extent_adj(&entry->u.file.data_fork); - hfs_cat_mark_dirty(entry); } else if ((descr->id == HFS_HDR_RSRC) && (descr->length != entry->u.file.rsrc_fork.lsize)) { entry->u.file.rsrc_fork.lsize = descr->length; hfs_extent_adj(&entry->u.file.rsrc_fork); - hfs_cat_mark_dirty(entry); } } } @@ -414,7 +412,7 @@ static hfs_rwret_t hdr_read(struct file * filp, char * buf, } hdr_build_meta(&meta, layout, entry); - copy_to_user(buf, ((char *)&meta) + pos, left); + left -= copy_to_user(buf, ((char *)&meta) + pos, left); count -= left; read += left; pos += left; @@ -531,7 +529,7 @@ static hfs_rwret_t hdr_read(struct file * filp, char * buf, /* transfer the data */ if (p) { - copy_to_user(buf, p + offset, left); + left -= copy_to_user(buf, p + offset, left); } else if (fork) { left = hfs_do_read(inode, fork, offset, buf, left, filp->f_reada != 0); @@ -654,6 +652,7 @@ static hfs_rwret_t hdr_write(struct file *filp, const char *buf, /* Handle possible size changes for the forks */ if (entry->type == HFS_CDR_FIL) { adjust_forks(entry, layout); + hfs_cat_mark_dirty(entry); } } @@ -887,6 +886,8 @@ done: */ static void hdr_truncate(struct inode *inode) { + /*struct inode *inode = dentry->d_inode;*/ + struct hfs_cat_entry *entry = HFS_I(inode)->entry; struct hfs_hdr_layout *layout; size_t size = inode->i_size; int lcv, last; @@ -907,14 +908,14 @@ static void hdr_truncate(struct inode *inode) } if (descr->id == HFS_HDR_RSRC) { - fork = &HFS_I(inode)->entry->u.file.rsrc_fork; + fork = &entry->u.file.rsrc_fork; #if 0 /* Can't yet truncate the data fork via a header file, since there is the * possibility to truncate via the data file, and the only locking is at * the inode level. */ } else if (descr->id == HFS_HDR_DATA) { - fork = &HFS_I(inode)->entry->u.file.data_fork; + fork = &entry->u.file.data_fork; #endif } else { continue; diff --git a/fs/hfs/hfs.h b/fs/hfs/hfs.h index ccc2f0cae..9112a6db8 100644 --- a/fs/hfs/hfs.h +++ b/fs/hfs/hfs.h @@ -11,10 +11,10 @@ #define _HFS_H #include <linux/hfs_sysdep.h> -#include <linux/hfs_fs.h> #define HFS_NEW(X) ((X) = hfs_malloc(sizeof(*(X)))) -#define HFS_DELETE(X) { hfs_free((X), sizeof(*(X))); (X) = NULL; } +#define HFS_DELETE(X) do { hfs_free((X), sizeof(*(X))); (X) = NULL; } \ + while (0) /* offsets to various blocks */ #define HFS_DD_BLK 0 /* Driver Descriptor block */ @@ -337,13 +337,12 @@ struct hfs_file { * This structure holds information about a * file or directory in an HFS filesystem. * - * 'wait' must remain 1st and 'next' 2nd since we do some pointer arithmetic. + * 'wait' must remain 1st and 'hash' 2nd since we do some pointer arithmetic. */ struct hfs_cat_entry { hfs_wait_queue wait; struct list_head hash; struct list_head list; - struct list_head dirty; struct hfs_mdb *mdb; hfs_sysentry sys_entry; struct hfs_cat_key key; @@ -366,7 +365,6 @@ struct hfs_cat_entry { #define HFS_KEYDIRTY 2 #define HFS_LOCK 4 #define HFS_DELETED 8 -#define HFS_SUPERBLK 16 /* * struct hfs_bnode_ref @@ -486,14 +484,11 @@ extern void hfs_mdb_put(struct hfs_mdb *, int); extern int hfs_part_find(hfs_sysmdb, int, int, hfs_s32 *, hfs_s32 *); /* string.c */ -extern unsigned int hfs_strhash(const struct hfs_name *); +extern unsigned long hfs_strhash(const struct hfs_name *); extern int hfs_strcmp(const struct hfs_name *, const struct hfs_name *); extern int hfs_streq(const struct hfs_name *, const struct hfs_name *); extern void hfs_tolower(unsigned char *, int); -/* sysdep.c */ -extern void hfs_cat_prune(struct hfs_cat_entry *); - extern __inline__ struct dentry *hfs_lookup_dentry(const char *name, const int len, struct dentry *base) diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index a0bf3d576..340e9be79 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c @@ -73,17 +73,13 @@ static void init_file_inode(struct inode *inode, hfs_u8 fork) */ void hfs_put_inode(struct inode * inode) { - struct hfs_cat_entry *entry = HFS_I(inode)->entry; - - entry->sys_entry[HFS_ITYPE_TO_INT(HFS_ITYPE(inode->i_ino))] = NULL; - hfs_cat_put(entry); - if (inode->i_count == 1) { - struct hfs_hdr_layout *tmp = HFS_I(inode)->layout; - if (tmp) { - HFS_I(inode)->layout = NULL; - HFS_DELETE(tmp); - } + struct hfs_hdr_layout *tmp = HFS_I(inode)->layout; + + if (tmp) { + HFS_I(inode)->layout = NULL; + HFS_DELETE(tmp); + } } } @@ -153,7 +149,7 @@ int hfs_notify_change(struct dentry *dentry, struct iattr * attr) /* We must change all in-core inodes corresponding to this file. */ for (i = 0; i < 4; ++i) { if (de[i] && (de[i] != dentry)) { - inode_setattr(de[i]->d_inode, attr); + inode_setattr(de[i]->d_inode, attr); } } @@ -213,7 +209,7 @@ int hfs_notify_change(struct dentry *dentry, struct iattr * attr) * benefit from a way to pass an additional (void *) through iget() to * the VFS read_inode() function. * - * hfs_iget no longer touches hfs_cat_entries. + * this will hfs_cat_put() the entry if it fails. */ struct inode *hfs_iget(struct hfs_cat_entry *entry, ino_t type, struct dentry *dentry) @@ -239,25 +235,15 @@ struct inode *hfs_iget(struct hfs_cat_entry *entry, ino_t type, sb = entry->mdb->sys_mdb; sys_entry = &entry->sys_entry[HFS_ITYPE_TO_INT(type)]; - if (*sys_entry && (inode = (*sys_entry)->d_inode)) { - /* There is an existing inode for this file/dir. Use it. */ - ++inode->i_count; - return inode; - } - - if (!(inode = iget(sb, ntohl(entry->cnid) | type))) + if (!(inode = iget(sb, ntohl(entry->cnid) | type))) { + hfs_cat_put(entry); return NULL; + } if (inode->i_dev != sb->s_dev) { - iput(inode); + iput(inode); /* automatically does an hfs_cat_put */ inode = NULL; - } else if (inode->i_mode) { - /* The inode has been initialized by another process. - Note that if hfs_put_inode() is sleeping in hfs_cat_put() - then we still need to attach it to the entry. */ - if (!(*sys_entry)) - *sys_entry = dentry; /* cache dentry */ - } else { + } else if (!inode->i_mode) { /* Initialize the inode */ struct hfs_sb_info *hsb = HFS_SB(sb); @@ -281,10 +267,11 @@ struct inode *hfs_iget(struct hfs_cat_entry *entry, ino_t type, if (!inode->i_mode) { clear_inode(inode); + hfs_cat_put(entry); inode = NULL; - } + } else + *sys_entry = dentry; /* cache dentry */ - *sys_entry = dentry; /* cache dentry */ } return inode; diff --git a/fs/hfs/string.c b/fs/hfs/string.c index cacc0a604..030850b82 100644 --- a/fs/hfs/string.c +++ b/fs/hfs/string.c @@ -81,11 +81,14 @@ static unsigned char casefold[256] = { /* * Hash a string to an integer in a case-independent way */ -unsigned int hfs_strhash(const struct hfs_name *cname) +unsigned long hfs_strhash(const struct hfs_name *cname) { - /* Currently just sum of the 'order' of first and last characters */ - return ((unsigned int)caseorder[cname->Name[0]] + - (unsigned int)caseorder[cname->Name[cname->Len - 1]]); + unsigned long hash = init_name_hash(); + unsigned int i; + for (i = 0; i < cname->Len; i++) { + hash = partial_name_hash(caseorder[cname->Name[i]], hash); + } + return end_name_hash(hash); } /* diff --git a/fs/hfs/super.c b/fs/hfs/super.c index 897130297..9e278d383 100644 --- a/fs/hfs/super.c +++ b/fs/hfs/super.c @@ -163,8 +163,8 @@ static int hfs_statfs(struct super_block *sb, struct statfs *buf, int len) tmp.f_blocks = mdb->alloc_blksz * mdb->fs_ablocks; tmp.f_bfree = mdb->alloc_blksz * mdb->free_ablocks; tmp.f_bavail = tmp.f_bfree; - tmp.f_files = mdb->fs_ablocks; /* According to the statfs manual page, -1 is the */ - tmp.f_ffree = mdb->free_ablocks; /* correct value when the meaning is undefined. */ + tmp.f_files = mdb->fs_ablocks; + tmp.f_ffree = mdb->free_ablocks; tmp.f_namelen = HFS_NAMELEN; return copy_to_user(buf, &tmp, len) ? -EFAULT : 0; @@ -459,16 +459,13 @@ struct super_block *hfs_read_super(struct super_block *s, void *data, if (!root_inode) goto bail_no_root; - /* cache the dentry in the inode */ - s->s_root = - HFS_I(root_inode)->entry->sys_entry[HFS_ITYPE_TO_INT(HFS_ITYPE_NORM)] = - d_alloc_root(root_inode, NULL); + s->s_root = d_alloc_root(root_inode, NULL); if (!s->s_root) goto bail_no_root; - /* HFS_SUPERBLK prevents the root inode from being flushed - * inadvertantly. */ - HFS_I(root_inode)->entry->state = HFS_SUPERBLK; + /* fix up pointers. */ + HFS_I(root_inode)->entry->sys_entry[HFS_ITYPE_TO_INT(HFS_ITYPE_NORM)] = + s->s_root; s->s_root->d_op = &hfs_dentry_operations; /* everything's okay */ diff --git a/fs/hfs/sysdep.c b/fs/hfs/sysdep.c index fc7368a75..659d0b2fc 100644 --- a/fs/hfs/sysdep.c +++ b/fs/hfs/sysdep.c @@ -19,13 +19,16 @@ #include <linux/hfs_fs.h> static int hfs_hash_dentry(struct dentry *, struct qstr *); -static int hfs_compare_dentry (struct dentry *, struct qstr *, struct qstr *); +static int hfs_compare_dentry(struct dentry *, struct qstr *, struct qstr *); +static void hfs_dentry_iput(struct dentry *, struct inode *); struct dentry_operations hfs_dentry_operations = { NULL, /* d_validate(struct dentry *) */ hfs_hash_dentry, /* d_hash */ hfs_compare_dentry, /* d_compare */ - NULL /* d_delete(struct dentry *) */ + NULL, /* d_delete(struct dentry *) */ + NULL, /* d_release(struct dentry *) */ + hfs_dentry_iput /* d_iput(struct dentry *, struct inode *) */ }; /* @@ -55,19 +58,16 @@ hfs_buffer hfs_buffer_get(hfs_sysmdb sys_mdb, int block, int read) { /* dentry case-handling: just lowercase everything */ -/* should we use hfs_strhash? if so, it probably needs to be beefed - * up a little. */ +/* hfs_strhash now uses the same hashing function as the dcache. */ static int hfs_hash_dentry(struct dentry *dentry, struct qstr *this) { - unsigned char name[HFS_NAMELEN]; - int len = this->len; + struct hfs_name cname; - if (len > HFS_NAMELEN) + if ((cname.Len = this->len) > HFS_NAMELEN) return 0; - - strncpy(name, this->name, len); - hfs_tolower(name, len); - this->hash = full_name_hash(name, len); + + strncpy(cname.Name, this->name, this->len); + this->hash = hfs_strhash(&cname); return 0; } @@ -86,18 +86,11 @@ static int hfs_compare_dentry(struct dentry *dentry, struct qstr *a, return hfs_streq(&s1, &s2); } - -/* toss a catalog entry. this does it by dropping the dentry. */ -void hfs_cat_prune(struct hfs_cat_entry *entry) +static void hfs_dentry_iput(struct dentry *dentry, struct inode *inode) { - int i; + struct hfs_cat_entry *entry = HFS_I(inode)->entry; - for (i = 0; i < 4; i++) { - struct dentry *de = entry->sys_entry[i]; - if (de) { - dget(de); - d_drop(de); - dput(de); - } - } + entry->sys_entry[HFS_ITYPE_TO_INT(HFS_ITYPE(inode->i_ino))] = NULL; + hfs_cat_put(entry); + iput(inode); } diff --git a/fs/hfs/version.c b/fs/hfs/version.c index 8eb74084d..8652c1cca 100644 --- a/fs/hfs/version.c +++ b/fs/hfs/version.c @@ -7,4 +7,4 @@ * This file contains the version string for this release. */ -const char hfs_version[]="0.95+asun2"; +const char hfs_version[]="0.95+asun3"; diff --git a/fs/ncpfs/Config.in b/fs/ncpfs/Config.in index 48cf4aa86..bdb973e41 100644 --- a/fs/ncpfs/Config.in +++ b/fs/ncpfs/Config.in @@ -1,7 +1,7 @@ # # NCP Filesystem configuration # -# bool ' Packet singatures' CONFIG_NCPFS_PACKET_SIGNING +bool ' Packet singatures' CONFIG_NCPFS_PACKET_SIGNING bool ' Proprietary file locking' CONFIG_NCPFS_IOCTL_LOCKING bool ' Clear remove/delete inhibit when needed' CONFIG_NCPFS_STRONG bool ' Use NFS namespace if available' CONFIG_NCPFS_NFS_NS diff --git a/fs/ncpfs/Makefile b/fs/ncpfs/Makefile index 5c83ada11..9c33bc515 100644 --- a/fs/ncpfs/Makefile +++ b/fs/ncpfs/Makefile @@ -8,7 +8,8 @@ # Note 2! The CFLAGS definitions are now in the main makefile... O_TARGET := ncpfs.o -O_OBJS := dir.o file.o inode.o ioctl.o mmap.o ncplib_kernel.o sock.o +O_OBJS := dir.o file.o inode.o ioctl.o mmap.o ncplib_kernel.o sock.o \ + ncpsign_kernel.o M_OBJS := $(O_TARGET) # If you want debugging output, please uncomment the following line diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c index 8424b2ec7..bec1c55a2 100644 --- a/fs/ncpfs/dir.c +++ b/fs/ncpfs/dir.c @@ -112,7 +112,7 @@ static int ncp_hash_dentry(struct dentry *, struct qstr *); static int ncp_compare_dentry (struct dentry *, struct qstr *, struct qstr *); static void ncp_delete_dentry(struct dentry *); -static struct dentry_operations ncp_dentry_operations = +struct dentry_operations ncp_dentry_operations = { ncp_lookup_validate, /* d_validate(struct dentry *) */ ncp_hash_dentry, /* d_hash */ diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c index f11f4640c..c872c2b84 100644 --- a/fs/ncpfs/inode.c +++ b/fs/ncpfs/inode.c @@ -25,9 +25,6 @@ #include <linux/fcntl.h> #include <linux/malloc.h> #include <linux/init.h> -#ifdef CONFIG_KERNELD -#include <linux/kerneld.h> -#endif #include <linux/ncp_fs.h> #include "ncplib_kernel.h" @@ -287,6 +284,7 @@ ncp_read_super(struct super_block *sb, void *raw_data, int silent) server->buffer_size = 0; server->conn_status = 0; server->root_dentry = NULL; + server->root_setuped = 0; #ifdef CONFIG_NCPFS_PACKET_SIGNING server->sign_wanted = 0; server->sign_active = 0; diff --git a/fs/ncpfs/ioctl.c b/fs/ncpfs/ioctl.c index 078d26596..2df6fee09 100644 --- a/fs/ncpfs/ioctl.c +++ b/fs/ncpfs/ioctl.c @@ -32,6 +32,22 @@ int ncp_ioctl(struct inode *inode, struct file *filp, struct ncp_ioctl_request request; struct ncp_fs_info info; +#ifdef NCP_IOC_GETMOUNTUID_INT + /* remove after ncpfs-2.0.13/2.2.0 gets released */ + if ((NCP_IOC_GETMOUNTUID != NCP_IOC_GETMOUNTUID_INT) && + (cmd == NCP_IOC_GETMOUNTUID_INT)) { + int tmp = server->m.mounted_uid; + + if ( (permission(inode, MAY_READ) != 0) + && (current->uid != server->m.mounted_uid)) + { + return -EACCES; + } + if (put_user(tmp, (unsigned int*) arg)) return -EFAULT; + return 0; + } +#endif /* NCP_IOC_GETMOUNTUID_INT */ + switch (cmd) { case NCP_IOC_NCPREQUEST: @@ -80,6 +96,8 @@ int ncp_ioctl(struct inode *inode, struct file *filp, && (current->uid != server->m.mounted_uid)) { return -EACCES; } + if (server->root_setuped) return -EBUSY; + server->root_setuped = 1; return ncp_conn_logged_in(server); case NCP_IOC_GET_FS_INFO: @@ -121,19 +139,6 @@ int ncp_ioctl(struct inode *inode, struct file *filp, put_user(server->m.mounted_uid, (uid_t *) arg); return 0; - case NCP_IOC_GETMOUNTUID_INT: - if ( (permission(inode, MAY_READ) != 0) - && (current->uid != server->m.mounted_uid)) - { - return -EACCES; - } - - { - unsigned int tmp=server->m.mounted_uid; - if (put_user(tmp, (unsigned long*) arg)) return -EFAULT; - } - return 0; - #ifdef CONFIG_NCPFS_MOUNT_SUBDIR case NCP_IOC_GETROOT: { @@ -168,6 +173,7 @@ int ncp_ioctl(struct inode *inode, struct file *filp, { return -EACCES; } + if (server->root_setuped) return -EBUSY; if (copy_from_user(&sr, (struct ncp_setroot_ioctl*)arg, sizeof(sr))) return -EFAULT; @@ -184,6 +190,7 @@ int ncp_ioctl(struct inode *inode, struct file *filp, } } dentry = server->root_dentry; + server->root_setuped = 1; if (dentry) { struct inode* inode = dentry->d_inode; diff --git a/fs/ncpfs/ncpsign_kernel.c b/fs/ncpfs/ncpsign_kernel.c new file mode 100644 index 000000000..bae89c197 --- /dev/null +++ b/fs/ncpfs/ncpsign_kernel.c @@ -0,0 +1,114 @@ +/* + * ncpsign_kernel.c + * + * Arne de Bruijn (arne@knoware.nl), 1997 + * + */ + +#include <linux/config.h> + +#ifdef CONFIG_NCPFS_PACKET_SIGNING + +#include <linux/string.h> +#include <linux/ncp.h> +#include "ncpsign_kernel.h" + +#define rol32(i,c) (((((i)&0xffffffff)<<c)&0xffffffff)| \ + (((i)&0xffffffff)>>(32-c))) +/* i386: 32-bit, little endian, handles mis-alignment */ +#ifdef __i386__ +#define GET_LE32(p) (*(int *)(p)) +#define PUT_LE32(p,v) { *(int *)(p)=v; } +#else +/* from include/ncplib.h */ +#define BVAL(buf,pos) (((__u8 *)(buf))[pos]) +#define PVAL(buf,pos) ((unsigned)BVAL(buf,pos)) +#define BSET(buf,pos,val) (BVAL(buf,pos) = (val)) + +static inline word +WVAL_LH(__u8 * buf, int pos) +{ + return PVAL(buf, pos) | PVAL(buf, pos + 1) << 8; +} +static inline dword +DVAL_LH(__u8 * buf, int pos) +{ + return WVAL_LH(buf, pos) | WVAL_LH(buf, pos + 2) << 16; +} +static inline void +WSET_LH(__u8 * buf, int pos, word val) +{ + BSET(buf, pos, val & 0xff); + BSET(buf, pos + 1, val >> 8); +} +static inline void +DSET_LH(__u8 * buf, int pos, dword val) +{ + WSET_LH(buf, pos, val & 0xffff); + WSET_LH(buf, pos + 2, val >> 16); +} + +#define GET_LE32(p) DVAL_LH(p,0) +#define PUT_LE32(p,v) DSET_LH(p,0,v) +#endif + +#define min(a,b) ((a)<(b)?(a):(b)) + +static void nwsign(char *r_data1, char *r_data2, char *outdata) { + int i; + unsigned int w0,w1,w2,w3; + static int rbit[4]={0, 2, 1, 3}; +#ifdef __i386__ + unsigned int *data2=(int *)r_data2; +#else + unsigned int data2[16]; + for (i=0;i<16;i++) + data2[i]=GET_LE32(r_data2+(i<<2)); +#endif + w0=GET_LE32(r_data1); + w1=GET_LE32(r_data1+4); + w2=GET_LE32(r_data1+8); + w3=GET_LE32(r_data1+12); + for (i=0;i<16;i+=4) { + w0=rol32(w0 + ((w1 & w2) | ((~w1) & w3)) + data2[i+0],3); + w3=rol32(w3 + ((w0 & w1) | ((~w0) & w2)) + data2[i+1],7); + w2=rol32(w2 + ((w3 & w0) | ((~w3) & w1)) + data2[i+2],11); + w1=rol32(w1 + ((w2 & w3) | ((~w2) & w0)) + data2[i+3],19); + } + for (i=0;i<4;i++) { + w0=rol32(w0 + (((w2 | w3) & w1) | (w2 & w3)) + 0x5a827999 + data2[i+0],3); + w3=rol32(w3 + (((w1 | w2) & w0) | (w1 & w2)) + 0x5a827999 + data2[i+4],5); + w2=rol32(w2 + (((w0 | w1) & w3) | (w0 & w1)) + 0x5a827999 + data2[i+8],9); + w1=rol32(w1 + (((w3 | w0) & w2) | (w3 & w0)) + 0x5a827999 + data2[i+12],13); + } + for (i=0;i<4;i++) { + w0=rol32(w0 + ((w1 ^ w2) ^ w3) + 0x6ed9eba1 + data2[rbit[i]+0],3); + w3=rol32(w3 + ((w0 ^ w1) ^ w2) + 0x6ed9eba1 + data2[rbit[i]+8],9); + w2=rol32(w2 + ((w3 ^ w0) ^ w1) + 0x6ed9eba1 + data2[rbit[i]+4],11); + w1=rol32(w1 + ((w2 ^ w3) ^ w0) + 0x6ed9eba1 + data2[rbit[i]+12],15); + } + PUT_LE32(outdata,(w0+GET_LE32(r_data1)) & 0xffffffff); + PUT_LE32(outdata+4,(w1+GET_LE32(r_data1+4)) & 0xffffffff); + PUT_LE32(outdata+8,(w2+GET_LE32(r_data1+8)) & 0xffffffff); + PUT_LE32(outdata+12,(w3+GET_LE32(r_data1+12)) & 0xffffffff); +} + +/* Make a signature for the current packet and add it at the end of the */ +/* packet. */ +void sign_packet(struct ncp_server *server, int *size) { + char data[64]; + + memset(data,0,64); + memcpy(data,server->sign_root,8); + PUT_LE32(data+8,(*size)); + memcpy(data+12,server->packet+sizeof(struct ncp_request_header)-1, + min((*size)-sizeof(struct ncp_request_header)+1,52)); + + nwsign(server->sign_last,data,server->sign_last); + + memcpy(server->packet+(*size),server->sign_last,8); + (*size)+=8; +} + +#endif /* CONFIG_NCPFS_PACKET_SIGNING */ + diff --git a/fs/ncpfs/ncpsign_kernel.h b/fs/ncpfs/ncpsign_kernel.h new file mode 100644 index 000000000..85974f346 --- /dev/null +++ b/fs/ncpfs/ncpsign_kernel.h @@ -0,0 +1,16 @@ +/* + * ncpsign_kernel.h + * + * Arne de Bruijn (arne@knoware.nl), 1997 + * + */ + +#ifndef _NCPSIGN_KERNEL_H +#define _NCPSIGN_KERNEL_H + +#include <linux/ncp_fs.h> +#include <linux/ncp_fs_sb.h> + +void sign_packet(struct ncp_server *server, int *size); + +#endif diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 8300fee67..2de790e42 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -650,10 +650,31 @@ _nfs_revalidate_inode(struct nfs_server *server, struct dentry *dentry) inode->i_ino); status = nfs_proc_getattr(server, NFS_FH(dentry), &fattr); if (status) { + int error; + u32 *fh; + struct nfs_fh fhandle; #ifdef NFS_PARANOIA printk("nfs_revalidate_inode: %s/%s getattr failed, ino=%ld, error=%d\n", dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_ino, status); #endif + if (status != -ESTALE) + goto out; + /* + * A "stale filehandle" error ... show the current fh + * and find out what the filehandle should be. + */ + fh = (u32 *) NFS_FH(dentry); + printk("NFS: bad fh %08x%08x%08x%08x%08x%08x%08x%08x\n", + fh[0],fh[1],fh[2],fh[3],fh[4],fh[5],fh[6],fh[7]); + error = nfs_proc_lookup(server, NFS_FH(dentry->d_parent), + dentry->d_name.name, &fhandle, &fattr); + if (error) { + printk("NFS: lookup failed, error=%d\n", error); + goto out; + } + fh = (u32 *) &fhandle; + printk(" %08x%08x%08x%08x%08x%08x%08x%08x\n", + fh[0],fh[1],fh[2],fh[3],fh[4],fh[5],fh[6],fh[7]); goto out; } diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c index 216aafb80..1c6a74a71 100644 --- a/fs/nfs/nfs2xdr.c +++ b/fs/nfs/nfs2xdr.c @@ -22,6 +22,9 @@ #include <linux/sunrpc/clnt.h> #include <linux/nfs_fs.h> +/* Uncomment this to support servers requiring longword lengths */ +#define NFS_PAD_WRITES 1 + #define NFSDBG_FACILITY NFSDBG_XDR /* #define NFS_PARANOIA 1 */ @@ -181,7 +184,7 @@ nfs_xdr_diropargs(struct rpc_rqst *req, u32 *p, struct nfs_diropargs *args) /* * Arguments to a READ call. Since we read data directly into the page * cache, we also set up the reply iovec here so that iov[1] points - * exactly to the page wewant to fetch. + * exactly to the page we want to fetch. */ static int nfs_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args) @@ -258,18 +261,38 @@ nfs_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res) static int nfs_xdr_writeargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args) { + u32 count = args->count; + p = xdr_encode_fhandle(p, args->fh); *p++ = htonl(args->offset); *p++ = htonl(args->offset); - *p++ = htonl(args->count); - *p++ = htonl(args->count); + *p++ = htonl(count); + *p++ = htonl(count); req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); req->rq_svec[1].iov_base = (void *) args->buffer; - req->rq_svec[1].iov_len = args->count; - req->rq_slen += args->count; + req->rq_svec[1].iov_len = count; + req->rq_slen += count; req->rq_snr = 2; +#ifdef NFS_PAD_WRITES + /* + * Some old servers require that the message length + * be a multiple of 4, so we pad it here if needed. + */ + count = ((count + 3) & ~3) - count; + if (count) { +#if 0 +printk("nfs_writeargs: padding write, len=%d, slen=%d, pad=%d\n", +req->rq_svec[1].iov_len, req->rq_slen, count); +#endif + req->rq_svec[2].iov_base = (void *) "\0\0\0"; + req->rq_svec[2].iov_len = count; + req->rq_slen += count; + req->rq_snr = 3; + } +#endif + return 0; } @@ -334,12 +357,21 @@ nfs_xdr_symlinkargs(struct rpc_rqst *req, u32 *p, struct nfs_symlinkargs *args) static int nfs_xdr_readdirargs(struct rpc_rqst *req, u32 *p, struct nfs_readdirargs *args) { - struct rpc_auth *auth = req->rq_task->tk_auth; + struct rpc_task *task = req->rq_task; + struct rpc_auth *auth = task->tk_auth; + u32 bufsiz = args->bufsiz; int replen; + /* + * Some servers (e.g. HP OS 9.5) seem to expect the buffer size + * to be in longwords ... check whether to convert the size. + */ + if (task->tk_client->cl_flags & NFS_CLNTF_BUFSIZE) + bufsiz = bufsiz >> 2; + p = xdr_encode_fhandle(p, args->fh); *p++ = htonl(args->cookie); - *p++ = htonl(args->bufsiz); + *p++ = htonl(bufsiz); /* see above */ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); /* set up reply iovec */ @@ -380,10 +412,9 @@ static int nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res) { struct iovec *iov = req->rq_rvec; - int status, nr, len; + int status, nr; char *string, *start; - u32 *end; - __u32 fileid, cookie, *entry; + u32 *end, *entry, len, fileid, cookie; if ((status = ntohl(*p++))) return -nfs_stat_to_errno(status); @@ -398,17 +429,25 @@ nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res) end = (u32 *) ((u8 *) p + iov[1].iov_len); /* Get start and end of dirent buffer */ - entry = (__u32 *) res->buffer; + entry = (u32 *) res->buffer; start = (char *) res->buffer; string = (char *) res->buffer + res->bufsiz; for (nr = 0; *p++; nr++) { fileid = ntohl(*p++); len = ntohl(*p++); + /* + * Check whether the server has exceeded our reply buffer, + * and set a flag to convert the size to longwords. + */ if ((p + QUADLEN(len) + 3) > end) { - printk(KERN_WARNING "NFS: short readdir reply! " - "nr=%d, slots=%d, len=%d\n", + struct rpc_clnt *clnt = req->rq_task->tk_client; + printk(KERN_WARNING + "NFS: server %s, readdir reply truncated\n", + clnt->cl_server); + printk(KERN_WARNING "NFS: nr=%d, slots=%d, len=%d\n", nr, (end - p), len); + clnt->cl_flags |= NFS_CLNTF_BUFSIZE; break; } if (len > NFS_MAXNAMLEN) { diff --git a/fs/nls/nls_base.c b/fs/nls/nls_base.c index 33e6dfd26..afc219838 100644 --- a/fs/nls/nls_base.c +++ b/fs/nls/nls_base.c @@ -12,8 +12,8 @@ #include <linux/config.h> #include <linux/nls.h> #include <linux/malloc.h> -#ifdef CONFIG_KERNELD -#include <linux/kerneld.h> +#ifdef CONFIG_KMOD +#include <linux/kmod.h> #endif #include <asm/byteorder.h> @@ -205,7 +205,7 @@ struct nls_table *find_nls(char *charset) struct nls_table *load_nls(char *charset) { struct nls_table *nls; -#ifdef CONFIG_KERNELD +#ifdef CONFIG_KMOD char buf[40]; int ret; #endif @@ -216,7 +216,7 @@ struct nls_table *load_nls(char *charset) return nls; } -#ifndef CONFIG_KERNELD +#ifndef CONFIG_KMOD return NULL; #else if (strlen(charset) > sizeof(buf) - sizeof("nls_")) { diff --git a/fs/ntfs/fs.c b/fs/ntfs/fs.c index aa6a7c40c..d190b21e4 100644 --- a/fs/ntfs/fs.c +++ b/fs/ntfs/fs.c @@ -776,13 +776,13 @@ struct super_block * ntfs_read_super(struct super_block *sb, struct buffer_head *bh; int i; - /* When the driver is compiled as a module, kerneld must know when it + /* When the driver is compiled as a module, kmod must know when it * can safely remove it from memory. To do this, each module owns a * reference counter. */ MOD_INC_USE_COUNT; /* Don't put ntfs_debug() before MOD_INC_USE_COUNT, printk() can block - * so this could lead to a race condition with kerneld. + * so this could lead to a race condition with kmod. */ ntfs_debug(DEBUG_OTHER, "ntfs_read_super\n"); @@ -939,7 +939,7 @@ __initfunc(int init_ntfs_fs(void)) #ifdef MODULE /* A module is a piece of code which can be inserted in and removed * from the running kernel whenever you want using lsmod, or on demand using - * kerneld + * kmod */ /* No function of this module is needed by another module */ @@ -956,7 +956,7 @@ MODULE_PARM_DESC(ntdebug, "Debug level"); /* When this code is compiled as a module, if you use mount -t ntfs when no * ntfs filesystem is registered (see /proc/filesystems), get_fs_type() in - * fs/super.c asks kerneld to load the module named ntfs in memory. + * fs/super.c asks kmod to load the module named ntfs in memory. * * Therefore, this function is the main entry point in this case */ @@ -965,7 +965,7 @@ int init_module(void) return init_ntfs_fs(); } -/* Called by kerneld just before the kernel removes the module from memory */ +/* Called by kmod just before the kernel removes the module from memory */ void cleanup_module(void) { SYSCTL(0); @@ -681,24 +681,37 @@ out: } /* - * Find an empty file descriptor entry, and mark it busy + * Find an empty file descriptor entry, and mark it busy. */ int get_unused_fd(void) { - int fd; struct files_struct * files = current->files; + int fd, error; + error = -EMFILE; fd = find_first_zero_bit(&files->open_fds, NR_OPEN); /* * N.B. For clone tasks sharing a files structure, this test * will limit the total number of files that can be opened. */ - if (fd < current->rlim[RLIMIT_NOFILE].rlim_cur) { - FD_SET(fd, &files->open_fds); - FD_CLR(fd, &files->close_on_exec); - return fd; + if (fd >= current->rlim[RLIMIT_NOFILE].rlim_cur) + goto out; + + /* Check here for fd > files->max_fds to do dynamic expansion */ + + FD_SET(fd, &files->open_fds); + FD_CLR(fd, &files->close_on_exec); +#if 1 + /* Sanity check */ + if (files->fd[fd] != NULL) { + printk("get_unused_fd: slot %d not NULL!\n", fd); + files->fd[fd] = NULL; } - return -EMFILE; +#endif + error = fd; + +out: + return error; } inline void put_unused_fd(unsigned int fd) @@ -796,15 +809,15 @@ asmlinkage int sys_close(unsigned int fd) { int error; struct file * filp; - struct files_struct * files; lock_kernel(); - files = current->files; error = -EBADF; - if (fd < NR_OPEN && (filp = files->fd[fd]) != NULL) { + filp = fcheck(fd); + if (filp) { + struct files_struct * files = current->files; + files->fd[fd] = NULL; put_unused_fd(fd); FD_CLR(fd, &files->close_on_exec); - files->fd[fd] = NULL; error = close_fp(filp, files); } unlock_kernel(); diff --git a/fs/proc/array.c b/fs/proc/array.c index 5364cea14..33df2c56a 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -446,7 +446,11 @@ static int get_array(struct task_struct *p, unsigned long start, unsigned long e static int get_env(int pid, char * buffer) { - struct task_struct *p = find_task_by_pid(pid); + struct task_struct *p; + + read_lock(&tasklist_lock); + p = find_task_by_pid(pid); + read_unlock(&tasklist_lock); /* FIXME!! This should be done after the last use */ if (!p || !p->mm) return 0; @@ -455,8 +459,11 @@ static int get_env(int pid, char * buffer) static int get_arg(int pid, char * buffer) { - struct task_struct *p = find_task_by_pid(pid); + struct task_struct *p; + read_lock(&tasklist_lock); + p = find_task_by_pid(pid); + read_unlock(&tasklist_lock); /* FIXME!! This should be done after the last use */ if (!p || !p->mm) return 0; return get_array(p, p->mm->arg_start, p->mm->arg_end, buffer); @@ -781,8 +788,11 @@ static inline char * task_sig(struct task_struct *p, char *buffer) static int get_status(int pid, char * buffer) { char * orig = buffer; - struct task_struct *tsk = find_task_by_pid(pid); + struct task_struct *tsk; + read_lock(&tasklist_lock); + tsk = find_task_by_pid(pid); + read_unlock(&tasklist_lock); /* FIXME!! This should be done after the last use */ if (!tsk) return 0; buffer = task_name(tsk, buffer); @@ -794,7 +804,7 @@ static int get_status(int pid, char * buffer) static int get_stat(int pid, char * buffer) { - struct task_struct *tsk = find_task_by_pid(pid); + struct task_struct *tsk; unsigned long vsize, eip, esp, wchan; long priority, nice; int tty_pgrp; @@ -805,6 +815,9 @@ static int get_stat(int pid, char * buffer) char sigcatch_str[sizeof(sigset_t)*2+1]; char state; + read_lock(&tasklist_lock); + tsk = find_task_by_pid(pid); + read_unlock(&tasklist_lock); /* FIXME!! This should be done after the last use */ if (!tsk) return 0; state = *get_task_state(tsk); @@ -959,6 +972,9 @@ static int get_statm(int pid, char * buffer) struct task_struct *tsk = find_task_by_pid(pid); int size=0, resident=0, share=0, trs=0, lrs=0, drs=0, dt=0; + read_lock(&tasklist_lock); + tsk = find_task_by_pid(pid); + read_unlock(&tasklist_lock); /* FIXME!! This should be done after the last use */ if (!tsk) return 0; if (tsk->mm && tsk->mm != &init_mm) { @@ -1041,7 +1057,9 @@ static ssize_t read_maps (int pid, struct file * file, char * buf, goto out; retval = -EINVAL; + read_lock(&tasklist_lock); p = find_task_by_pid(pid); + read_unlock(&tasklist_lock); /* FIXME!! This should be done after the last use */ if (!p) goto freepage_out; @@ -1152,9 +1170,11 @@ static int get_pidcpu(int pid, char * buffer) { struct task_struct * tsk = current ; int i, len; - + + read_lock(&tasklist_lock); if (pid != tsk->pid) tsk = find_task_by_pid(pid); + read_unlock(&tasklist_lock); /* FIXME!! This should be done after the last use */ if (tsk == NULL) return 0; diff --git a/fs/proc/base.c b/fs/proc/base.c index dc182682a..e535276bb 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -60,12 +60,14 @@ static void proc_pid_fill_inode(struct inode * inode, int fill) int pid = inode->i_ino >> 16; int ino = inode->i_ino & 0xffff; + read_lock(&tasklist_lock); if (fill && (p = find_task_by_pid(pid)) != NULL) { if (p->dumpable || ino == PROC_PID_INO) { inode->i_uid = p->euid; inode->i_gid = p->gid; } } + read_unlock(&tasklist_lock); } /* diff --git a/fs/proc/fd.c b/fs/proc/fd.c index 4baa299fc..8897578d6 100644 --- a/fs/proc/fd.c +++ b/fs/proc/fd.c @@ -96,7 +96,9 @@ static int proc_lookupfd(struct inode * dir, struct dentry * dentry) break; } } + read_lock(&tasklist_lock); p = find_task_by_pid(pid); + read_unlock(&tasklist_lock); /* FIXME!! This should be done only after not using 'p' any more */ if (!pid || !p) return -ENOENT; @@ -149,7 +151,9 @@ static int proc_readfd(struct file * filp, return 0; } + read_lock(&tasklist_lock); p = find_task_by_pid(pid); + read_unlock(&tasklist_lock); /* FIXME!! This should be done only after not using 'p' any more */ if(!p) return 0; tarrayp = p->tarray_ptr; diff --git a/fs/proc/inode.c b/fs/proc/inode.c index c33616604..9a0e29a84 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -260,7 +260,13 @@ void proc_read_inode(struct inode * inode) inode->i_size = 0; pid = ino >> 16; - if (!pid || ((p = find_task_by_pid(pid)) == NULL)) + if (!pid) + return; + read_lock(&tasklist_lock); + p = find_task_by_pid(pid); + read_unlock(&tasklist_lock); /* FIXME!! This should be done only after we have stopped using 'p' */ + + if (!p) return; ino &= 0x0000ffff; diff --git a/fs/proc/mem.c b/fs/proc/mem.c index c49f187c0..1cbdbad9a 100644 --- a/fs/proc/mem.c +++ b/fs/proc/mem.c @@ -83,7 +83,9 @@ static ssize_t mem_read(struct file * file, char * buf, char *tmp; ssize_t scount, i; + read_lock(&tasklist_lock); tsk = get_task(inode->i_ino >> 16); + read_unlock(&tasklist_lock); /* FIXME: This should really be done only afetr not using tsk any more!!! */ if (!tsk) return -ESRCH; addr = *ppos; diff --git a/fs/proc/root.c b/fs/proc/root.c index 3e344bd09..ad3a541cb 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c @@ -14,8 +14,8 @@ #include <linux/stat.h> #include <linux/config.h> #include <asm/bitops.h> -#ifdef CONFIG_KERNELD -#include <linux/kerneld.h> +#ifdef CONFIG_KMOD +#include <linux/kmod.h> #endif /* @@ -234,7 +234,7 @@ proc_openprom_deregister(void) } #endif -#if defined(CONFIG_SUN_OPENPROMFS_MODULE) && defined(CONFIG_KERNELD) +#if defined(CONFIG_SUN_OPENPROMFS_MODULE) && defined(CONFIG_KMOD) static int proc_openprom_defreaddir(struct inode * inode, struct file * filp, void * dirent, filldir_t filldir) @@ -812,14 +812,18 @@ static int proc_root_lookup(struct inode * dir, struct dentry * dentry) break; } } + read_lock(&tasklist_lock); p = find_task_by_pid(pid); inode = NULL; if (pid && p) { unsigned long ino = (pid << 16) + PROC_PID_INO; inode = proc_get_inode(dir->i_sb, ino, &proc_pid); - if (!inode) + if (!inode) { + read_unlock(&tasklist_lock); return -EINVAL; + } } + read_unlock(&tasklist_lock); dentry->d_op = &proc_dentry_operations; d_add(dentry, inode); diff --git a/fs/super.c b/fs/super.c index 84ef3ffb8..50a6cb9a6 100644 --- a/fs/super.c +++ b/fs/super.c @@ -37,14 +37,14 @@ #include <asm/uaccess.h> #include <asm/bitops.h> -#ifdef CONFIG_KERNELD -#include <linux/kerneld.h> -#endif - #include <linux/nfs_fs.h> #include <linux/nfs_fs_sb.h> #include <linux/nfs_mount.h> +#ifdef CONFIG_KMOD +#include <linux/kmod.h> +#endif + /* * We use a semaphore to synchronize all mount/umount * activity - imagine the mess if we have a race between @@ -405,7 +405,7 @@ struct file_system_type *get_fs_type(const char *name) return fs; for (fs = file_systems; fs && strcmp(fs->name, name); fs = fs->next) ; -#ifdef CONFIG_KERNELD +#ifdef CONFIG_KMOD if (!fs && (request_module(name) == 0)) { for (fs = file_systems; fs && strcmp(fs->name, name); fs = fs->next) ; diff --git a/fs/umsdos/README-WIP.txt b/fs/umsdos/README-WIP.txt index e48a4d184..ddc2911fc 100644 --- a/fs/umsdos/README-WIP.txt +++ b/fs/umsdos/README-WIP.txt @@ -32,34 +32,42 @@ Notes: possible very minor problems with dentry/inode/... kernel structures (ver - long file names - works - read file - works - switching MSDOS/UMSDOS - works? -- switching UMSDOS/MSDOS - untested -- pseudo root things - commented out mostly currently. To be fixed when +- switching UMSDOS/MSDOS - UNTESTED +- pseudo root things - COMMENTED OUT mostly currently. To be fixed when dentries stuff is straightened out. - resolve symlink - seems to work fully now! - dereference symlink - seems to work fully now! - hard links - seems to work now - special files (block/char device, fifos, sockets...) - seems to work ok. -- other ioctls - mostly untested +- other ioctls - MOSTLY UNTESTED - dangling symlink - UNTESTED ! -- create symlink - WARNING: NONE OF WRITE OPERATIONS FIXED YET! -- create hardlink - WARNING: NONE OF WRITE OPERATIONS FIXED YET! -- create file - creates, but corrupts. after reboot seem ok ? -- create special file - WARNING: NONE OF WRITE OPERATIONS FIXED YET! -- write to file - WARNING: NONE OF WRITE OPERATIONS FIXED YET! -- rename file (same dir) - WARNING: NONE OF WRITE OPERATIONS FIXED YET! -- rename file (dif. dir) - WARNING: NONE OF WRITE OPERATIONS FIXED YET! -- rename dir (same dir) - WARNING: NONE OF WRITE OPERATIONS FIXED YET! -- rename dir (dif. dir) - WARNING: NONE OF WRITE OPERATIONS FIXED YET! -- delete file - WARNING: NONE OF WRITE OPERATIONS FIXED YET! -- delete hardlink - WARNING: NONE OF WRITE OPERATIONS FIXED YET! -- mkdir - WARNING: NONE OF WRITE OPERATIONS FIXED YET! -- rmdir - WARNING: NONE OF WRITE OPERATIONS FIXED YET! -- umssyncing - WARNING: NONE OF WRITE OPERATIONS FIXED YET! +- create symlink - works on short names, but fails (gets + truncated on long ones) (also + due to some dentries problems, it may not + be visible right away always - eg. before + umount/mount) +- create hardlink - WARNING: NOT FIXED YET! +- create file - creates short names, but probs with long ones ? +- create special file - seems to work on short names. +- write to file - seems to work on short names. +- rename file (same dir) - WARNING: NOT FIXED YET! +- rename file (dif. dir) - WARNING: NOT FIXED YET! +- rename dir (same dir) - WARNING: NOT FIXED YET! +- rename dir (dif. dir) - WARNING: NOT FIXED YET! +- delete file - WARNING: NOT FIXED YET! +- notify_change (chown,perms) - seems to work! +- delete hardlink - WARNING: NOT FIXED YET! +- mkdir - seems to work, even with long names ! (but + due to some dentries problems, it may not + be visible right away always - eg. before + umount/mount) +- rmdir - WARNING: NOT FIXED YET! +- umssyncing - does something :-), but NEEDS EXTENSIVE TESTING Notes: moderate dentry/inode kernel structures trashing. Probably some other kernel structures compromised. Have SysRq support compiled in, and use -Sync/Emergency-remount-RO. And don't try mounting read/write yet - and then +Sync/Emergency-remount-RO. And if you don't try mounting read/write - you should have no big problems... Notes2: kernel structures trashing seems to be _MUCH_ lower if no @@ -69,6 +77,17 @@ Notes3: Notes2 is probably somewhat outdated now that hardlink/symlink stuff is supposed to be fixed enough to work, but I haven't got the time to test it. +Note4: on failure of creating of long filenames: MSDOS filename gets +created, and EMD entry gets created. Check: either they mismatch, or EMD +entry contains some wrong flags. + +Note5: rmdir(2) probably fails because do_rmdir calls lock_parent, which +uses dentry->d_parent, which we neglect to set, so it returns -ENOENT. +Probably same problem on unlink(2) ? What to do ? How to set +dentry->d_parent to something useful ?? Must I recurse down whole pathname +and set one by one all directory components ?! or only last one is really +needed ? help ! + ------------------------------------------------------------------------------ Some general notes: @@ -85,7 +104,7 @@ compile/test/reboot/set_environment/recompile cycle by removing 'reboot/set_environment' component that now occures every few cycles. But I need some help from someone knowing about dentries/inodes use more -than I. If you can help, please contact me... I'm mostly worries about +than I. If you can help, please contact me... I'm mostly worried about iget/iput and dget/dput, and deallocating temporary dentries we create. should we destroy temp dentries ? using d_invalidate ? using d_drop ? just dput them ? @@ -96,9 +115,9 @@ any direct Email in few days. If I don't - probably I never got your message. You can try mnalis@open.hr or mnalis@voyager.hr; however mnalis@jagor.srce.hr is preferable one. ------------------------------------------------------------------------------- -some of my notes for myself: +------------------------------------------------------------------------------ +some of my notes for myself /mn/: + hardlinks/symlinks. test with files in not_the_same_dir - also test not_the_same_dir for other file operations like rename etc. @@ -108,3 +127,17 @@ some of my notes for myself: - what about .dotfiles ? working ? multiple dots ? etc.... - fix stuff like dir->i_count++ to atomic_inc(&dir->i_count) and simular? + +- umsdos_create_any - calling msdos_create will create dentry for shor name. Hmmmm..? +- kill_dentry - put it where is needed. Also dput() at needed places. + +- when should dput()/iput() be used ?!! + +- probably problem with filename mangling somewhere, since both create and + write to file work on short filenames, but fail on long ones. Path + components may be of any size (eg. mkfifo /mnt/Very_long_dir2/blah1 will + succeed, but mkfifo /mnt/very_long_filename.txt won't) + + +- what is dir->i_count++ ? locking directory ? should this be lock_parent or +something ? diff --git a/fs/umsdos/dir.c b/fs/umsdos/dir.c index be1af20f7..52a72367e 100644 --- a/fs/umsdos/dir.c +++ b/fs/umsdos/dir.c @@ -31,7 +31,7 @@ extern struct inode *pseudo_root; uses. It's easier to do once than hack all the other instances. Probably safer as well */ -int compat_umsdos_real_lookup(struct inode *dir,const char *name,int len, struct inode **inode) +int compat_umsdos_real_lookup (struct inode *dir,const char *name,int len, struct inode **inode) { int rv; struct dentry *dentry; @@ -39,6 +39,7 @@ int compat_umsdos_real_lookup(struct inode *dir,const char *name,int len, struct dentry = creat_dentry (name, len, NULL); rv = umsdos_real_lookup(dir,dentry); if (inode) *inode = dentry->d_inode; + kill_dentry (dentry); return rv; } @@ -458,6 +459,7 @@ void umsdos_lookup_patch ( if (inode->u.umsdos_i.i_emd_owner==0) printk (KERN_WARNING "emd_owner still 0 ???\n"); } } + struct UMSDOS_DIRENT_K{ off_t f_pos; /* will hold the offset of the entry in EMD */ ino_t ino; @@ -533,6 +535,9 @@ int umsdos_inode2entry ( /* This is a DOS directory */ struct UMSDOS_DIR_SEARCH bufk; struct file filp; + + fill_new_filp (&filp, NULL); + Printk ((KERN_ERR "umsdos_inode2entry emddir==NULL: WARNING: Known filp problem. segfaulting :) /mn/\n")); filp.f_reada = 1; filp.f_pos = 0; @@ -548,6 +553,8 @@ int umsdos_inode2entry ( }else{ /* skip . and .. see umsdos_readdir_x() */ struct file filp; + fill_new_filp (&filp, NULL); + filp.f_reada = 1; filp.f_pos = UMSDOS_SPECIAL_DIRFPOS; Printk ((KERN_ERR "umsdos_inode2entry skip./..: WARNING: Known filp problem. segfaulting :) /mn/\n")); @@ -856,6 +863,9 @@ int umsdos_hlink2inode (struct inode *hlink, struct inode **result) struct file filp; loff_t offs = 0; + fill_new_filp (&filp, NULL); + + dentry_src = creat_dentry ("hlink-mn", 8, hlink); memset (&filp, 0, sizeof (filp)); diff --git a/fs/umsdos/emd.c b/fs/umsdos/emd.c index 169a75a45..8ba6571eb 100644 --- a/fs/umsdos/emd.c +++ b/fs/umsdos/emd.c @@ -14,6 +14,7 @@ #include <linux/string.h> #include <linux/msdos_fs.h> #include <linux/umsdos_fs.h> +#include <linux/dcache.h> #include <asm/uaccess.h> @@ -23,6 +24,29 @@ #define Printk(x) printk x /* + * makes empty filp + * + */ + +void fill_new_filp (struct file *filp, struct dentry *dentry) +{ + Printk (("/mn/ fill_new_filp: filling empty filp at %p\n", filp)); + if (dentry) + Printk ((" dentry=%.*s\n", (int) dentry->d_name.len, dentry->d_name.name)); + else + Printk ((" dentry is NULL ! you must fill it later...\n")); + + memset (filp, 0, sizeof (struct file)); + + filp->f_pos = 0; + filp->f_reada = 1; + filp->f_flags = O_RDWR; + filp->f_dentry = dentry; + filp->f_op = &umsdos_file_operations; /* /mn/ - we have to fill it with SOMETHING */ +} + + +/* * makes dentry. for name name with length len. /mn/ * if inode is not NULL, puts it also. * @@ -34,19 +58,49 @@ struct dentry *creat_dentry (const char *name, const int len, struct inode *inod struct qstr qname; if (inode) - Printk (("/mn/ creat_dentry: creating dentry with inode=%lu for %.*s\n", inode->i_ino, len, name)); + Printk ((KERN_DEBUG "/mn/ creat_dentry: creating dentry with inode=%lu for %.*s\n", inode->i_ino, len, name)); else - Printk (("/mn/ creat_dentry: creating empty dentry for %.*s\n", len, name)); + Printk ((KERN_DEBUG "/mn/ creat_dentry: creating empty dentry for %.*s\n", len, name)); qname.name = name; qname.len = len; qname.hash = 0; ret = d_alloc (parent,&qname); /* create new dentry */ - ret->d_inode = inode; + ret->d_inode = NULL; + + if (inode) d_add (ret, inode); + +/* ret->d_inode = inode; /mn/ FIXME this was old, replaced by d_add, delete this ! */ return ret; } +/* + * removes temporary dentry created by creat_dentry + * + */ + +void kill_dentry (struct dentry *dentry) +{ + if (dentry) { + Printk (("/mn/ kill_dentry: kill_dentry %.*s :", (int) dentry->d_name.len, dentry->d_name.name)); + if (dentry->d_inode) + Printk (("inode=%lu\n", dentry->d_inode->i_ino)); + else + Printk (("inode is NULL\n")); + + /* FIXME: is this ok ?! /mn/ */ + /* d_invalidate (dentry); */ + /*dput (dentry);*/ + } else { + Printk (("/mn/ kill_dentry: dentry is NULL ?!\n")); + } + + + Printk ((KERN_DEBUG "/mn/ kill_dentry: exiting...\n")); + return; +} + /* @@ -126,23 +180,24 @@ ssize_t umsdos_file_read_kmem (struct inode *emd_dir, /* - Write to a file from kernel space -*/ -ssize_t umsdos_file_write_kmem (struct inode *emd_dir, - struct file *filp, + * Write to file from kernel space. + * Does the real job, assumes all structures are initialized ! + */ + + +ssize_t umsdos_file_write_kmem_real (struct file *filp, const char *buf, size_t count, - loff_t *offs - ) + loff_t *offs) { - int ret; + ssize_t ret; mm_segment_t old_fs = get_fs(); - struct dentry *old_dentry; - Printk ((KERN_ERR " STARTED WRITE_KMEM /mn/\n")); + set_fs (KERNEL_DS); Printk ((KERN_ERR "umsdos_file_write_kmem /mn/: Checkin: filp=%p, buf=%p, size=%d, offs=%p\n", filp, buf, count, offs)); - Printk ((KERN_ERR " using emd=%ld\n", emd_dir->i_ino)); + Printk ((KERN_ERR " struct dentry=%p\n", filp->f_dentry)); + Printk ((KERN_ERR " struct inode=%p\n", filp->f_dentry->d_inode)); Printk ((KERN_ERR " inode=%lu, i_size=%lu\n", filp->f_dentry->d_inode->i_ino, filp->f_dentry->d_inode->i_size)); Printk ((KERN_ERR " ofs=%ld\n",(unsigned long) *offs)); Printk ((KERN_ERR " f_pos=%Lu\n", filp->f_pos)); @@ -152,23 +207,49 @@ ssize_t umsdos_file_write_kmem (struct inode *emd_dir, Printk ((KERN_ERR " f_owner=%d\n", filp->f_owner.uid)); Printk ((KERN_ERR " f_version=%ld\n", filp->f_version)); Printk ((KERN_ERR " f_reada=%ld, f_ramax=%ld, f_raend=%ld, f_ralen=%ld, f_rawin=%ld\n", filp->f_reada, filp->f_ramax, filp->f_raend, filp->f_ralen, filp->f_rawin)); + + ret = fat_file_write (filp, buf, count, offs); + PRINTK ((KERN_ERR "fat_file_write returned with %ld!\n", ret)); + + set_fs (old_fs); + return ret; +} + + +/* + * Write to a file from kernel space + */ + +ssize_t umsdos_file_write_kmem (struct inode *emd_dir, + struct file *filp, + const char *buf, + size_t count, + loff_t *offs + ) +{ + int ret; + struct dentry *old_dentry; + - set_fs (KERNEL_DS); + Printk ((KERN_ERR " STARTED WRITE_KMEM /mn/\n")); + Printk ((KERN_ERR " using emd=%ld\n", emd_dir->i_ino)); + old_dentry=filp->f_dentry; /* save it */ filp->f_dentry = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, emd_dir); - *offs = filp->f_pos; - ret = fat_file_write (filp, buf, count, offs); - PRINTK ((KERN_ERR "fat_file_write returned with %ld!\n", ret)); + *offs = filp->f_pos; /* FIXME, in read_kmem also: offs is not used so why pass it ?!!! /mn/ */ + + ret=umsdos_file_write_kmem_real (filp, buf, count, offs); filp->f_pos = *offs; filp->f_dentry=old_dentry; - set_fs (old_fs); return ret; } + + /* Write a block of bytes into one EMD file. The block of data is NOT in user space. @@ -201,7 +282,7 @@ ssize_t umsdos_emd_dir_write (struct inode *emd_dir, #endif if (offs) myofs=*offs; /* if offs is not NULL, read it */ - Printk (("umsdos_emd_dir_write /mn/: calling write_kmem with %p, %p, %p, %ld, %Ld\n", emd_dir, filp, buf, count, myofs)); + Printk (("umsdos_emd_dir_write /mn/: calling write_kmem with %p, %p, %p, %d, %Ld\n", emd_dir, filp, buf, count, myofs)); written = umsdos_file_write_kmem (emd_dir, filp, buf, count, &myofs); Printk (("umsdos_emd_dir_write /mn/: write_kmem returned\n")); if (offs) *offs=myofs; /* if offs is not NULL, store myofs there */ @@ -216,6 +297,12 @@ ssize_t umsdos_emd_dir_write (struct inode *emd_dir, d->rdev = le16_to_cpu (d->rdev); d->mode = le16_to_cpu (d->mode); #endif + +#ifdef 1 + if (written != count) Printk ((KERN_ERR "umsdos_emd_dir_write: ERROR: written (%d) != count (%d)\n", written, count)); +#endif + + return written != count ? -EIO : 0; } @@ -409,7 +496,9 @@ int umsdos_writeentry ( struct file filp; struct umsdos_dirent *entry = &info->entry; struct umsdos_dirent entry0; - + + fill_new_filp (&filp, NULL); + Printk (("umsdos_writeentry /mn/: entering...\n")); emd_dentry=creat_dentry ("wremd_mn", 8, emd_dir); @@ -444,7 +533,7 @@ int umsdos_writeentry ( filp.f_op = &umsdos_file_operations; /* /mn/ - we have to fill it with dummy values so we won't segfault */ ret = umsdos_emd_dir_write (emd_dir, &filp, (char*)entry, info->recsize, NULL); - Printk (("emd_dir_write returned !\n")); + Printk (("emd_dir_write returned with %d!\n", ret)); if (ret != 0){ printk ("UMSDOS: problem with EMD file. Can't write\n"); }else{ @@ -452,7 +541,7 @@ int umsdos_writeentry ( /* dir->i_dirt = 1; FIXME iput/dput ??? */ } - Printk (("umsdos_writeentry /mn/: returning...\n")); + Printk (("umsdos_writeentry /mn/: returning %d...\n", ret)); return ret; } @@ -541,10 +630,13 @@ static int umsdos_find ( record, multiple contiguous record are allocated. */ int ret = -ENOENT; - /* FIXME -- /mn/ fixed ? */ - struct inode *emd_dir = umsdos_emd_dir_lookup (dir, 1); + struct inode *emd_dir; + struct umsdos_dirent *entry = &info->entry; + + Printk (("umsdos_find: locating %.*s in dir %lu\n", entry->name_len, entry->name, dir->i_ino)); + + emd_dir = umsdos_emd_dir_lookup (dir, 1); if (emd_dir != NULL){ - struct umsdos_dirent *entry = &info->entry; int recsize = info->recsize; struct { off_t posok; /* Position available to store the entry */ @@ -560,11 +652,7 @@ static int umsdos_find ( dentry = creat_dentry ("umsfind-mn", 10, emd_dir); - buf.filp.f_pos = 0; - buf.filp.f_reada = 1; - buf.filp.f_flags = O_RDONLY; - buf.filp.f_dentry = dentry; - buf.filp.f_op = &umsdos_file_operations; /* /mn/ - we have to fill it with dummy values so we won't segfault */ + fill_new_filp (&buf.filp, dentry); buf.pos = 0; buf.size = 0; @@ -631,6 +719,8 @@ static int umsdos_find ( umsdos_manglename(info); } *pt_emd_dir = emd_dir; + + Printk (("umsdos_find: returning %d\n", ret)); return ret; } @@ -651,7 +741,7 @@ int umsdos_newentry ( ret = -EEXIST; }else if (ret == -ENOENT){ ret = umsdos_writeentry(dir,emd_dir,info,0); - Printk (("umsdos_newentry EDM ret = %d\n",ret)); + Printk (("umsdos_newentry EMD ret = %d\n",ret)); } iput (emd_dir); return ret; @@ -729,6 +819,8 @@ int umsdos_isempty (struct inode *dir) /* If the EMD file does not exist, it is certainly empty :-) */ if (emd_dir != NULL){ struct file filp; + fill_new_filp (&filp, NULL); + /* Find an empty slot */ memset (&filp, 0, sizeof (filp)); @@ -779,6 +871,7 @@ int umsdos_findentry ( } } iput (emd_dir); + Printk (("umsdos_findentry: returning %d\n", ret)); return ret; } diff --git a/fs/umsdos/inode.c b/fs/umsdos/inode.c index 059a780e3..e8b65558c 100644 --- a/fs/umsdos/inode.c +++ b/fs/umsdos/inode.c @@ -105,7 +105,7 @@ void umsdos_setup_dir_inode (struct inode *inode) inode->i_op = &umsdos_dir_inode_operations; } - iput (emd_dir); +/* iput (emd_dir); FIXME /mn/ ! */ } } @@ -119,9 +119,10 @@ void umsdos_set_dirinfo( off_t f_pos) { struct inode *emd_owner; - /* FIXME, I don't have a clue on this one */ - Printk ((KERN_WARNING "umsdos_set_dirinfo: /mn/ FIXME: no clue\n")); + /* FIXME, I don't have a clue on this one - /mn/ hmmm ? ok ? */ +/* Printk ((KERN_WARNING "umsdos_set_dirinfo: /mn/ FIXME: no clue. inode=%lu dir=%lu\n", inode->i_ino, dir->i_ino));*/ emd_owner = umsdos_emd_dir_lookup(dir,1); + Printk (("umsdos_set_dirinfo: emd_owner is %lu for dir %lu\n", emd_owner->i_ino, dir->i_ino)); inode->u.umsdos_i.i_dir_owner = dir->i_ino; inode->u.umsdos_i.i_emd_owner = emd_owner->i_ino; iput (emd_owner); @@ -317,10 +318,10 @@ void UMSDOS_write_inode(struct inode *inode) /* FIXME inode->i_dirt = 0; */ } -int UMSDOS_notify_change(struct dentry *dentry, struct iattr *attr) + +int internal_notify_change(struct inode *inode, struct iattr *attr) { int ret = 0; - struct inode *inode = dentry->d_inode; Printk ((KERN_ERR "UMSDOS_notify_change: /mn/ completly untested\n")); @@ -362,10 +363,15 @@ int UMSDOS_notify_change(struct dentry *dentry, struct iattr *attr) }else{ struct file filp; struct umsdos_dirent entry; + struct dentry *emd_dentry; loff_t offs; - offs = 0; + + emd_dentry = creat_dentry ("notify_emd", 10, emd_owner); + fill_new_filp (&filp, emd_dentry); + filp.f_pos = inode->u.umsdos_i.pos; filp.f_reada = 0; + offs = filp.f_pos; /* FIXME: /mn/ is this ok ? */ Printk (("pos = %Lu ", filp.f_pos)); /* Read only the start of the entry since we don't touch */ /* the name */ @@ -386,7 +392,7 @@ int UMSDOS_notify_change(struct dentry *dentry, struct iattr *attr) entry.nlink = inode->i_nlink; filp.f_pos = inode->u.umsdos_i.pos; - offs = 0; /* FIXME */ + offs = filp.f_pos; /* FIXME: /mn/ is this ok ? */ ret = umsdos_emd_dir_write (emd_owner, &filp, (char*)&entry, UMSDOS_REC_SIZE, &offs); Printk (("notify pos %lu ret %d nlink %d " @@ -407,6 +413,15 @@ int UMSDOS_notify_change(struct dentry *dentry, struct iattr *attr) return ret; } + +int UMSDOS_notify_change(struct dentry *dentry, struct iattr *attr) +{ + return internal_notify_change (dentry->d_inode, attr); +} + + + + /* #Specification: function name / convention A simple convention for function name has been used in the UMSDOS file system. First all function use the prefix @@ -457,7 +472,7 @@ struct super_block *UMSDOS_read_super( PRINTK ((KERN_DEBUG "UMSDOS /mn/: sb = %p\n",sb)); res = msdos_read_super(sb,data,silent); PRINTK ((KERN_DEBUG "UMSDOS /mn/: res = %p\n",res)); - printk (KERN_INFO "UMSDOS dentry-WIP-Beta 0.82-1 (compatibility level %d.%d, fast msdos)\n", UMSDOS_VERSION, UMSDOS_RELEASE); + printk (KERN_INFO "UMSDOS dentry-WIP-Beta 0.82-2 (compatibility level %d.%d, fast msdos)\n", UMSDOS_VERSION, UMSDOS_RELEASE); if (res == NULL) { MOD_DEC_USE_COUNT; return NULL; } @@ -504,7 +519,7 @@ struct super_block *UMSDOS_read_super( The word "linux" is hardcoded in /usr/include/linux/umsdos_fs.h in the macro UMSDOS_PSDROOT_NAME. */ - struct dentry *root, *etc, *etc_rc, *init, *sbin; /* FIXME */ + struct dentry *root, *etc, *etc_rc, *init, *sbin; root = creat_dentry (UMSDOS_PSDROOT_NAME, strlen(UMSDOS_PSDROOT_NAME), NULL); sbin = creat_dentry ("sbin", 4, NULL); diff --git a/fs/umsdos/namei.c b/fs/umsdos/namei.c index 76c486405..dcea137fe 100644 --- a/fs/umsdos/namei.c +++ b/fs/umsdos/namei.c @@ -39,6 +39,7 @@ static int umsdos_waitcreate(struct inode *dir) } return ret; } + /* Wait for any lookup process to finish */ @@ -48,6 +49,7 @@ static void umsdos_waitlookup (struct inode *dir) sleep_on(&dir->u.umsdos_i.u.dir_info.p); } } + /* Lock all other process out of this directory. */ @@ -90,6 +92,7 @@ void umsdos_lockcreate (struct inode *dir) dir->u.umsdos_i.u.dir_info.pid = current->pid; umsdos_waitlookup (dir); } + /* Lock all other process out of those two directories. */ @@ -115,6 +118,7 @@ static void umsdos_lockcreate2 (struct inode *dir1, struct inode *dir2) umsdos_waitlookup(dir1); umsdos_waitlookup(dir2); } + /* Wait until creation is finish in this directory. */ @@ -157,6 +161,7 @@ void umsdos_startlookup (struct inode *dir){} static void umsdos_unlockcreate (struct inode *dir){} void umsdos_endlookup (struct inode *dir){} #endif + static int umsdos_nevercreat( struct inode *dir, struct dentry *dentry, @@ -210,7 +215,11 @@ static int umsdos_create_any ( /* file */ { - int ret = umsdos_nevercreat(dir,dentry,-EEXIST); + int ret; + + Printk (("umsdos_create_any /mn/: create %.*s in dir=%lu - nevercreat=/", (int) dentry->d_name.len, dentry->d_name.name, dir->i_ino)); + ret = umsdos_nevercreat(dir,dentry,-EEXIST); + Printk (("%d/\n", ret)); if (ret == 0){ struct umsdos_info info; ret = umsdos_parse(dentry->d_name.name,dentry->d_name.len,&info); @@ -237,8 +246,8 @@ static int umsdos_create_any ( if (ret == 0){ struct inode *inode = dentry->d_inode; umsdos_lookup_patch (dir,inode,&info.entry,info.f_pos); - Printk (("inode %p[%d] ",inode,inode->i_count)); - Printk (("Creation OK: [%lu] %.*s %d pos %ld\n", dir->i_ino, + Printk (("inode %p[%lu], count=%d ",inode, inode->i_ino, inode->i_count)); + Printk (("Creation OK: [dir %lu] %.*s pid=%d pos %ld\n", dir->i_ino, info.fake.len, info.fake.fname, current->pid, info.f_pos)); }else{ /* #Specification: create / file exist in DOS @@ -276,9 +285,10 @@ static int umsdos_create_any ( umsdos_unlockcreate(dir); } } - d_add(dentry,dir); + /* d_add(dentry,dir); /mn/ FIXME: msdos_create already did this for short name ! */ return ret; } + /* Initialise the new_entry from the old for a rename operation. (Only useful for umsdos_rename_f() below). @@ -425,6 +435,7 @@ static int umsdos_rename_f( Printk (("\n")); return ret; } + /* Setup un Symbolic link or a (pseudo) hard link Return a negative error code or 0 if ok. @@ -456,11 +467,13 @@ static int umsdos_symlink_x( if (ret == 0){ int len = strlen(symname); struct file filp; - filp.f_pos = 0; + loff_t myofs=0; + fill_new_filp (&filp, dentry); + /* Make the inode acceptable to MSDOS FIXME */ Printk ((KERN_ERR "umsdos_symlink_x: FIXME /mn/ Here goes the crash.. known wrong code...\n")); - ret = umsdos_file_write_kmem (dentry->d_inode, &filp,symname,ret,NULL); /* FIXME /mn/: dentry->d_inode->i_ino is totaly wrong, just put in to compile the beast... - PTW dentry->d_inode is "less incorrect" */ + Printk ((KERN_WARNING " symname=%s ; dentry name=%.*s (ino=%lu)\n", symname, (int) dentry->d_name.len, dentry->d_name.name, dentry->d_inode->i_ino)); + ret = umsdos_file_write_kmem_real (&filp, symname, len, &myofs); /* dput(dentry); ?? where did this come from FIXME */ if (ret >= 0){ if (ret != len){ @@ -480,6 +493,7 @@ static int umsdos_symlink_x( Printk (("\n")); return ret; } + /* Setup un Symbolic link. Return a negative error code or 0 if ok. @@ -492,6 +506,7 @@ int UMSDOS_symlink( { return umsdos_symlink_x (dir,dentry,symname,S_IFLNK|0777,0); } + /* Add a link to an inode in a directory */ @@ -671,6 +686,9 @@ int UMSDOS_link ( Printk (("umsdos_link %d\n",ret)); return ret; } + + + /* Add a new file into the alternate directory. The file is added to the real MSDOS directory. If successful, it @@ -680,13 +698,16 @@ int UMSDOS_link ( */ int UMSDOS_create ( struct inode *dir, - struct dentry *dentry, /* Length of the name */ + struct dentry *dentry, int mode /* Permission bit + file type ??? */ ) /* Will hold the inode of the newly created */ /* file */ { return umsdos_create_any (dir,dentry,mode,0,0); } + + + /* Add a sub-directory in a directory */ @@ -735,7 +756,7 @@ int UMSDOS_mkdir( ret = compat_umsdos_real_lookup (dir,info.fake.fname, info.fake.len,&subdir); if (ret == 0){ - struct inode *result; +/* struct inode *result; FIXME /mn/ hmmm what is this supposed to be ? */ struct dentry *tdentry; tdentry = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, NULL); @@ -753,9 +774,10 @@ int UMSDOS_mkdir( } } Printk (("umsdos_mkdir %d\n",ret)); - dput (dentry); +/* dput (dentry); FIXME /mn/ */ return ret; } + /* Add a new device special file into a directory. */ @@ -779,8 +801,9 @@ int UMSDOS_mknod( for ordinary files was causing major trouble with hard link in particular and other parts of the kernel I guess. */ + int ret = umsdos_create_any (dir,dentry,mode,rdev,0); - dput(dentry); +/* dput(dentry); /mn/ FIXME! */ return ret; } @@ -952,13 +975,20 @@ int UMSDOS_unlink ( struct inode * dir, struct dentry *dentry) { - int ret = umsdos_nevercreat(dir,dentry,-EPERM); + int ret; + Printk ((" *** UMSDOS_unlink entering /mn/ *** \n")); + + ret = umsdos_nevercreat(dir,dentry,-EPERM); + + Printk (("UMSDOS_unlink /mn/: nevercreat=%d\n", ret)); + if (ret == 0){ struct umsdos_info info; ret = umsdos_parse (dentry->d_name.name,dentry->d_name.len,&info); if (ret == 0){ umsdos_lockcreate(dir); ret = umsdos_findentry(dir,&info,1); + Printk (("UMSDOS_unlink: findentry returned %d\n", ret)); if (ret == 0){ Printk (("UMSDOS_unlink %.*s ",info.fake.len,info.fake.fname)); /* check sticky bit */ diff --git a/fs/umsdos/symlink.c b/fs/umsdos/symlink.c index 38baba6ca..3ed550742 100644 --- a/fs/umsdos/symlink.c +++ b/fs/umsdos/symlink.c @@ -39,7 +39,8 @@ static int umsdos_readlink_x ( int ret; loff_t loffs = 0; struct file filp; - + + fill_new_filp (&filp, NULL); ret = dentry->d_inode->i_size; diff --git a/include/asm-alpha/fpu.h b/include/asm-alpha/fpu.h index ab9b28f6e..333e5caeb 100644 --- a/include/asm-alpha/fpu.h +++ b/include/asm-alpha/fpu.h @@ -37,21 +37,21 @@ * compatibly. The corresponding definitions are in * /usr/include/machine/fpu.h under OSF/1. */ -#define IEEE_TRAP_ENABLE_INV (1<<1) /* invalid op */ -#define IEEE_TRAP_ENABLE_DZE (1<<2) /* division by zero */ -#define IEEE_TRAP_ENABLE_OVF (1<<3) /* overflow */ -#define IEEE_TRAP_ENABLE_UNF (1<<4) /* underflow */ -#define IEEE_TRAP_ENABLE_INE (1<<5) /* inexact */ +#define IEEE_TRAP_ENABLE_INV (1UL<<1) /* invalid op */ +#define IEEE_TRAP_ENABLE_DZE (1UL<<2) /* division by zero */ +#define IEEE_TRAP_ENABLE_OVF (1UL<<3) /* overflow */ +#define IEEE_TRAP_ENABLE_UNF (1UL<<4) /* underflow */ +#define IEEE_TRAP_ENABLE_INE (1UL<<5) /* inexact */ #define IEEE_TRAP_ENABLE_MASK (IEEE_TRAP_ENABLE_INV | IEEE_TRAP_ENABLE_DZE |\ IEEE_TRAP_ENABLE_OVF | IEEE_TRAP_ENABLE_UNF |\ IEEE_TRAP_ENABLE_INE) /* status bits coming from fpcr: */ -#define IEEE_STATUS_INV (1<<17) -#define IEEE_STATUS_DZE (1<<18) -#define IEEE_STATUS_OVF (1<<19) -#define IEEE_STATUS_UNF (1<<20) -#define IEEE_STATUS_INE (1<<21) +#define IEEE_STATUS_INV (1UL<<17) +#define IEEE_STATUS_DZE (1UL<<18) +#define IEEE_STATUS_OVF (1UL<<19) +#define IEEE_STATUS_UNF (1UL<<20) +#define IEEE_STATUS_INE (1UL<<21) #define IEEE_STATUS_MASK (IEEE_STATUS_INV | IEEE_STATUS_DZE | \ IEEE_STATUS_OVF | IEEE_STATUS_UNF | \ @@ -64,7 +64,7 @@ #define IEEE_INHERIT (1UL<<63) /* inherit on thread create? */ /* - * Convert the spftware IEEE trap enable and status bits into the + * Convert the software IEEE trap enable and status bits into the * hardware fpcr format. */ diff --git a/include/asm-i386/page.h b/include/asm-i386/page.h index 4744df9c8..5889ec880 100644 --- a/include/asm-i386/page.h +++ b/include/asm-i386/page.h @@ -1,12 +1,15 @@ #ifndef _I386_PAGE_H #define _I386_PAGE_H +#include <linux/config.h> + /* PAGE_SHIFT determines the page size */ #define PAGE_SHIFT 12 #define PAGE_SIZE (1UL << PAGE_SHIFT) #define PAGE_MASK (~(PAGE_SIZE-1)) #ifdef __KERNEL__ +#ifndef __ASSEMBLY__ #define STRICT_MM_TYPECHECKS @@ -52,12 +55,14 @@ typedef unsigned long pgprot_t; #define __pgprot(x) (x) #endif +#endif /* !__ASSEMBLY__ */ /* to align the pointer to the (next) page boundary */ #define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) /* This handles the memory map.. */ -#define PAGE_OFFSET 0xC0000000 +#define __PAGE_OFFSET ((0x1000-CONFIG_MAX_MEMSIZE)<<20) +#define PAGE_OFFSET ((unsigned long)__PAGE_OFFSET) #define __pa(x) ((unsigned long)(x)-PAGE_OFFSET) #define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET)) #define MAP_NR(addr) (__pa(addr) >> PAGE_SHIFT) diff --git a/include/asm-i386/pgtable.h b/include/asm-i386/pgtable.h index fe0864913..47e1d2cfc 100644 --- a/include/asm-i386/pgtable.h +++ b/include/asm-i386/pgtable.h @@ -13,6 +13,7 @@ * the i386 page table tree. */ +#ifndef __ASSEMBLY__ /* Caches aren't brain-dead on the intel. */ #define flush_cache_all() do { } while (0) #define flush_cache_mm(mm) do { } while (0) @@ -155,6 +156,7 @@ static inline void flush_tlb_range(struct mm_struct *mm, } #endif #endif +#endif /* !__ASSEMBLY__ */ /* Certain architectures need to do special things when pte's @@ -181,6 +183,16 @@ static inline void flush_tlb_range(struct mm_struct *mm, #define PTRS_PER_PMD 1 #define PTRS_PER_PGD 1024 +/* + * pgd entries used up by user/kernel: + */ + +#define USER_PGD_PTRS (PAGE_OFFSET >> PGDIR_SHIFT) +#define KERNEL_PGD_PTRS (PTRS_PER_PGD-USER_PGD_PTRS) +#define __USER_PGD_PTRS ((__PAGE_OFFSET >> PGDIR_SHIFT) & 0x3ff) +#define __KERNEL_PGD_PTRS (PTRS_PER_PGD-__USER_PGD_PTRS) + +#ifndef __ASSEMBLY__ /* Just any arbitrary offset to the start of the vmalloc VM area: the * current 8MB value just means that there will be a 8MB "hole" after the * physical memory until the kernel virtual memory starts. That means that @@ -497,4 +509,6 @@ extern inline void update_mmu_cache(struct vm_area_struct * vma, #define module_map vmalloc #define module_unmap vfree +#endif /* !__ASSEMBLY__ */ + #endif /* _I386_PAGE_H */ diff --git a/include/asm-i386/processor.h b/include/asm-i386/processor.h index 778466bbe..fc62069a5 100644 --- a/include/asm-i386/processor.h +++ b/include/asm-i386/processor.h @@ -10,6 +10,7 @@ #include <asm/vm86.h> #include <asm/math_emu.h> #include <asm/segment.h> +#include <asm/page.h> /* * CPU type and hardware bug flags. Kept separately for each CPU. @@ -71,10 +72,9 @@ extern unsigned int machine_submodel_id; extern unsigned int BIOS_revision; /* - * User space process size: 3GB. This is hardcoded into a few places, - * so don't change it unless you know what you are doing. + * User space process size: 3GB (default). */ -#define TASK_SIZE (0xC0000000UL) +#define TASK_SIZE (PAGE_OFFSET) /* This decides where the kernel will search for a free chunk of vm * space during mmap's. diff --git a/include/asm-i386/uaccess.h b/include/asm-i386/uaccess.h index ef08ac510..9da2fff06 100644 --- a/include/asm-i386/uaccess.h +++ b/include/asm-i386/uaccess.h @@ -5,6 +5,7 @@ * User space memory access functions */ #include <linux/sched.h> +#include <asm/page.h> #define VERIFY_READ 0 #define VERIFY_WRITE 1 @@ -21,7 +22,7 @@ #define KERNEL_DS MAKE_MM_SEG(0xFFFFFFFF) -#define USER_DS MAKE_MM_SEG(0xC0000000) +#define USER_DS MAKE_MM_SEG(PAGE_OFFSET) #define get_ds() (KERNEL_DS) #define get_fs() (current->addr_limit) diff --git a/include/linux/coda.h b/include/linux/coda.h index 3faa2e9ca..5c3cb563e 100644 --- a/include/linux/coda.h +++ b/include/linux/coda.h @@ -587,9 +587,9 @@ struct cfs_open_by_path_out { }; /* - * Occasionally, don't cache the fid returned by CFS_LOOKUP. For instance, if - * the fid is inconsistent. This case is handled by setting the top bit of the - * return result parameter. + * Occasionally, we don't cache the fid returned by CFS_LOOKUP. + * For instance, if the fid is inconsistent. + * This case is handled by setting the top bit of the type result parameter. */ #define CFS_NOCACHE 0x80000000 diff --git a/include/linux/coda_cache.h b/include/linux/coda_cache.h index 44251867f..fc607fdba 100644 --- a/include/linux/coda_cache.h +++ b/include/linux/coda_cache.h @@ -21,19 +21,16 @@ struct coda_cache { struct coda_cred cc_cred; }; -void coda_ccinsert(struct coda_cache *el, struct super_block *sb); -void coda_cninsert(struct coda_cache *el, struct coda_inode_info *cnp); -void coda_ccremove(struct coda_cache *el); -void coda_cnremove(struct coda_cache *el); -void coda_cache_create(struct inode *inode, int mask); -struct coda_cache *coda_cache_find(struct inode *inode); +/* credential cache */ void coda_cache_enter(struct inode *inode, int mask); -void coda_cache_clear_cnp(struct coda_inode_info *cnp); +void coda_cache_clear_inode(struct inode *); void coda_cache_clear_all(struct super_block *sb); void coda_cache_clear_cred(struct super_block *sb, struct coda_cred *cred); int coda_cache_check(struct inode *inode, int mask); -void coda_dentry_delete(struct dentry *dentry); -void coda_zapfid(struct ViceFid *fid, struct super_block *sb, int flag); + +/* for downcalls and attributes and lookups */ +void coda_flag_inode(struct inode *inode, int flag); +void coda_flag_alias_children(struct inode *inode, int flag); /* diff --git a/include/linux/coda_fs_i.h b/include/linux/coda_fs_i.h index 1277445b9..d312013d5 100644 --- a/include/linux/coda_fs_i.h +++ b/include/linux/coda_fs_i.h @@ -17,7 +17,7 @@ #define CODA_CNODE_MAGIC 0x47114711 /* - * smb fs inode data (in memory only) + * coda fs inode data */ struct coda_inode_info { struct ViceFid c_fid; /* Coda identifier */ @@ -36,7 +36,7 @@ struct coda_inode_info { #define C_VATTR 0x1 /* Validity of vattr in the cnode */ #define C_SYMLINK 0x2 /* Validity of symlink pointer in the cnode */ #define C_DYING 0x4 /* Set for outstanding cnodes from venus (which died) */ -#define C_ZAPFID 0x8 +#define C_PURGE 0x8 #define C_ZAPDIR 0x10 #define C_INITED 0x20 @@ -44,9 +44,6 @@ int coda_cnode_make(struct inode **, struct ViceFid *, struct super_block *); int coda_cnode_makectl(struct inode **inode, struct super_block *sb); struct inode *coda_fid_to_inode(ViceFid *fid, struct super_block *sb); -/* inode to cnode */ -#define ITOC(inode) ((struct coda_inode_info *)&((inode)->u.coda_i)) - #endif #endif diff --git a/include/linux/coda_linux.h b/include/linux/coda_linux.h index fa477cb52..9dd30eaeb 100644 --- a/include/linux/coda_linux.h +++ b/include/linux/coda_linux.h @@ -36,6 +36,7 @@ extern struct file_operations coda_ioctl_operations; int coda_open(struct inode *i, struct file *f); int coda_release(struct inode *i, struct file *f); int coda_permission(struct inode *inode, int mask); +int coda_revalidate_inode(struct dentry *); /* global variables */ extern int coda_debug; @@ -43,10 +44,13 @@ extern int coda_print_entry; extern int coda_access_cache; /* this file: heloers */ +static __inline__ struct ViceFid *coda_i2f(struct inode *); char *coda_f2s(ViceFid *f); int coda_isroot(struct inode *i); int coda_fid_is_volroot(struct ViceFid *); int coda_iscontrol(const char *name, size_t length); + + void coda_load_creds(struct coda_cred *cred); int coda_mycred(struct coda_cred *); void coda_vattr_to_iattr(struct inode *, struct coda_vattr *); @@ -112,4 +116,18 @@ do { \ #define CODA_FREE(ptr,size) do {if (size < 3000) { kfree_s((ptr), (size)); CDEBUG(D_MALLOC, "kfreed: %x at %x.\n", (int) size, (int) ptr); } else { vfree((ptr)); CDEBUG(D_MALLOC, "vfreed: %x at %x.\n", (int) size, (int) ptr);} } while (0) +/* inode to cnode */ + +static __inline__ struct ViceFid *coda_i2f(struct inode *inode) +{ + return &(inode->u.coda_i.c_fid); +} + +#define ITOC(inode) (&((inode)->u.coda_i)) + + + + + + #endif diff --git a/include/linux/file.h b/include/linux/file.h index 3f3870b9e..240a5039c 100644 --- a/include/linux/file.h +++ b/include/linux/file.h @@ -1,19 +1,41 @@ +/* + * Wrapper functions for accessing the file_struct fd array. + */ + #ifndef __LINUX_FILE_H #define __LINUX_FILE_H -extern inline struct file * fget(unsigned long fd) +extern int __fput(struct file *); +extern void insert_file_free(struct file *file); + +/* + * Check whether the specified fd has an open file. + */ +extern inline struct file * fcheck(unsigned int fd) { struct file * file = NULL; - if (fd < NR_OPEN) { + + if (fd < NR_OPEN) file = current->files->fd[fd]; - if (file) - file->f_count++; - } return file; } -extern int __fput(struct file *); -extern void insert_file_free(struct file *file); +extern inline struct file * fget(unsigned int fd) +{ + struct file * file = fcheck(fd); + + if (file) + file->f_count++; + return file; +} + +/* + * Install a file pointer in the fd array. + */ +extern inline void fd_install(unsigned int fd, struct file *file) +{ + current->files->fd[fd] = file; +} /* It does not matter which list it is on. */ extern inline void remove_filp(struct file *file) @@ -47,12 +69,4 @@ extern inline void put_filp(struct file *file) } } -/* - * Install a file pointer in the files structure. - */ -extern inline void fd_install(unsigned long fd, struct file *file) -{ - current->files->fd[fd] = file; -} - #endif diff --git a/include/linux/hfs_fs.h b/include/linux/hfs_fs.h index de51db0b1..9b43579c0 100644 --- a/include/linux/hfs_fs.h +++ b/include/linux/hfs_fs.h @@ -237,20 +237,20 @@ extern const struct hfs_name hfs_cap_reserved2[]; extern struct inode_operations hfs_cap_ndir_inode_operations; extern struct inode_operations hfs_cap_fdir_inode_operations; extern struct inode_operations hfs_cap_rdir_inode_operations; -extern void hfs_cap_drop_dentry(const ino_t, struct dentry *); +extern void hfs_cap_drop_dentry(struct dentry *, const ino_t); /* dir_dbl.c */ extern const struct hfs_name hfs_dbl_reserved1[]; extern const struct hfs_name hfs_dbl_reserved2[]; extern struct inode_operations hfs_dbl_dir_inode_operations; -extern void hfs_dbl_drop_dentry(const ino_t, struct dentry *); +extern void hfs_dbl_drop_dentry(struct dentry *, const ino_t); /* dir_nat.c */ extern const struct hfs_name hfs_nat_reserved1[]; extern const struct hfs_name hfs_nat_reserved2[]; extern struct inode_operations hfs_nat_ndir_inode_operations; extern struct inode_operations hfs_nat_hdir_inode_operations; -extern void hfs_nat_drop_dentry(const ino_t, struct dentry *); +extern void hfs_nat_drop_dentry(struct dentry *, const ino_t); /* dir_sngl.c */ extern const struct hfs_name hfs_sngl_reserved1[]; diff --git a/include/linux/hfs_fs_i.h b/include/linux/hfs_fs_i.h index cf9ed53e0..453896882 100644 --- a/include/linux/hfs_fs_i.h +++ b/include/linux/hfs_fs_i.h @@ -34,7 +34,7 @@ struct hfs_inode_info { struct hfs_hdr_layout *layout; /* for dentry cleanup */ - void (*d_drop_op)(const ino_t, struct dentry *); + void (*d_drop_op)(struct dentry *, const ino_t); }; #endif diff --git a/include/linux/hfs_sysdep.h b/include/linux/hfs_sysdep.h index 93de05aad..22e2ac66b 100644 --- a/include/linux/hfs_sysdep.h +++ b/include/linux/hfs_sysdep.h @@ -78,6 +78,10 @@ extern inline hfs_u32 hfs_time(void) { */ typedef struct wait_queue *hfs_wait_queue; +extern inline void hfs_init_waitqueue(hfs_wait_queue *queue) { + init_waitqueue(queue); +} + extern inline void hfs_sleep_on(hfs_wait_queue *queue) { sleep_on(queue); } diff --git a/include/linux/kerneld.h b/include/linux/kerneld.h deleted file mode 100644 index b2db5f8c7..000000000 --- a/include/linux/kerneld.h +++ /dev/null @@ -1,135 +0,0 @@ -#ifndef _LINUX_KERNELD_H -#define _LINUX_KERNELD_H - -#define KERNELD_SYSTEM 1 -#define KERNELD_REQUEST_MODULE 2 /* "insmod" */ -#define KERNELD_RELEASE_MODULE 3 /* "rmmod" */ -#define KERNELD_DELAYED_RELEASE_MODULE 4 /* "rmmod" */ -#define KERNELD_CANCEL_RELEASE_MODULE 5 /* "rmmod" */ -#define KERNELD_REQUEST_ROUTE 6 /* from net/ipv4/route.c */ -#define KERNELD_BLANKER 7 /* from drivers/char/console.c */ -#define KERNELD_PNP 8 /* from drivers/pnp/kerneld.c */ -#define KERNELD_ARP 256 /* from net/ipv4/arp.c */ - -/* - * Uncomment the following line for the new kerneld protocol - * This includes the pid of the kernel level requester into the kerneld header - */ -/* -#define NEW_KERNELD_PROTOCOL - */ -#ifdef NEW_KERNELD_PROTOCOL -#define OLDIPC_KERNELD 00040000 /* use the kerneld message channel */ -#define IPC_KERNELD 00140000 /* use the kerneld message channel, new protocol */ -#define KDHDR (sizeof(long) + sizeof(short) + sizeof(short)) -#define NULL_KDHDR 0, 2, 0 -#else -#define IPC_KERNELD 00040000 /* use the kerneld message channel */ -#define KDHDR (sizeof(long)) -#define NULL_KDHDR 0 -#endif -#define KERNELD_MAXCMD 0x7ffeffff -#define KERNELD_MINSEQ 0x7fff0000 /* "commands" legal up to 0x7ffeffff */ -#define KERNELD_WAIT 0x80000000 -#define KERNELD_NOWAIT 0 - -struct kerneld_msg { - long mtype; - long id; -#ifdef NEW_KERNELD_PROTOCOL - short version; - short pid; -#endif -#ifdef __KERNEL__ - char *text; -#else - char text[1]; -#endif /* __KERNEL__ */ -}; - -#ifdef __KERNEL__ -#include <linux/string.h> - -extern int kerneld_send(int msgtype, int ret_size, int msgsz, - const char *text, const char *ret_val); - -/* - * Request that a module should be loaded. - * Wait for the exit status from insmod/modprobe. - * If it fails, it fails... at least we tried... - */ -static inline int request_module(const char *name) -{ - return kerneld_send(KERNELD_REQUEST_MODULE, - 0 | KERNELD_WAIT, - strlen(name), name, NULL); -} - -/* - * Request the removal of a module, maybe don't wait for it. - * It doesn't matter if the removal fails, now does it? - */ -static inline int release_module(const char *name, int waitflag) -{ - return kerneld_send(KERNELD_RELEASE_MODULE, - 0 | (waitflag?KERNELD_WAIT:KERNELD_NOWAIT), - strlen(name), name, NULL); -} - -/* - * Request a delayed removal of a module, but don't wait for it. - * The delay is done by kerneld (default: 60 seconds) - */ -static inline int delayed_release_module(const char *name) -{ - return kerneld_send(KERNELD_DELAYED_RELEASE_MODULE, - 0 | KERNELD_NOWAIT, - strlen(name), name, NULL); -} - -/* - * Attempt to cancel a previous request for removal of a module, - * but don't wait for it. - * This call can be made if the kernel wants to prevent a delayed - * unloading of a module. - */ -static inline int cancel_release_module(const char *name) -{ - return kerneld_send(KERNELD_CANCEL_RELEASE_MODULE, - 0 | KERNELD_NOWAIT, - strlen(name), name, NULL); -} - -/* - * Perform an "inverted" system call, maybe return the exit status - */ -static inline int ksystem(const char *cmd, int waitflag) -{ - return kerneld_send(KERNELD_SYSTEM, - 0 | (waitflag?KERNELD_WAIT:KERNELD_NOWAIT), - strlen(cmd), cmd, NULL); -} - -/* - * Try to create a route, possibly by opening a ppp-connection - */ -static inline int kerneld_route(const char *ip_route) -{ - return kerneld_send(KERNELD_REQUEST_ROUTE, - 0 | KERNELD_WAIT, - strlen(ip_route), ip_route, NULL); -} - -/* - * Handle an external screen blanker - */ -static inline int kerneld_blanker(int on_off) -{ - char *s = on_off ? "on" : "off"; - return kerneld_send(KERNELD_BLANKER, - 0 | (on_off ? KERNELD_NOWAIT : KERNELD_WAIT), - strlen(s), s, NULL); -} - -#endif /* __KERNEL__ */ -#endif diff --git a/include/linux/kmod.h b/include/linux/kmod.h new file mode 100644 index 000000000..876c7f222 --- /dev/null +++ b/include/linux/kmod.h @@ -0,0 +1,4 @@ +/* + kmod header +*/ +extern int request_module(const char * name); diff --git a/include/linux/module.h b/include/linux/module.h index 475c68854..ad3d10baf 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -148,7 +148,7 @@ const char __module_author[] __attribute__((section(".modinfo"))) = \ const char __module_description[] __attribute__((section(".modinfo"))) = \ "description=" desc -/* Could potentially be used by kerneld... */ +/* Could potentially be used by kmod... */ #define MODULE_SUPPORTED_DEVICE(dev) \ const char __module_device[] __attribute__((section(".modinfo"))) = \ diff --git a/include/linux/mroute.h b/include/linux/mroute.h index 55193867d..b57519b72 100644 --- a/include/linux/mroute.h +++ b/include/linux/mroute.h @@ -217,7 +217,7 @@ extern int pim_rcv(struct sk_buff * , unsigned short); extern int pim_rcv_v1(struct sk_buff * , unsigned short len); struct rtmsg; -extern int ipmr_get_route(struct sk_buff *skb, struct rtmsg *rtm); +extern int ipmr_get_route(struct sk_buff *skb, struct rtmsg *rtm, int nowait); #endif #endif diff --git a/include/linux/ncp_fs.h b/include/linux/ncp_fs.h index 03904df71..eb83cfe01 100644 --- a/include/linux/ncp_fs.h +++ b/include/linux/ncp_fs.h @@ -83,8 +83,15 @@ struct ncp_privatedata_ioctl }; #define NCP_IOC_NCPREQUEST _IOR('n', 1, struct ncp_ioctl_request) -#define NCP_IOC_GETMOUNTUID _IOW('n', 2, uid_t) -#define NCP_IOC_GETMOUNTUID_INT _IOW('n', 2, unsigned int) +#define NCP_IOC_GETMOUNTUID _IOW('n', 2, __kernel_uid_t) + +#if 1 +#ifdef __KERNEL__ +/* remove after ncpfs-2.0.13 gets released or at the beginning of kernel-2.1. codefreeze */ +#define NCP_IOC_GETMOUNTUID_INT _IOW('n', 2, unsigned int) +#endif +#endif + #define NCP_IOC_CONN_LOGGED_IN _IO('n', 3) #define NCP_GET_FS_INFO_VERSION (1) diff --git a/include/linux/ncp_fs_sb.h b/include/linux/ncp_fs_sb.h index efcc20556..38492fc92 100644 --- a/include/linux/ncp_fs_sb.h +++ b/include/linux/ncp_fs_sb.h @@ -51,11 +51,9 @@ struct ncp_server { int ncp_reply_size; struct ncp_inode_info root; -#if 0 - char root_path; /* '\0' */ -#else struct dentry* root_dentry; -#endif + + int root_setuped; /* info for packet signing */ int sign_wanted; /* 1=Server needs signed packets */ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 72430508a..d1c005c70 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -125,6 +125,9 @@ struct net_device_stats unsigned long tx_heartbeat_errors; unsigned long tx_window_errors; + /* for cslip etc */ + unsigned long rx_compressed; + unsigned long tx_compressed; }; #ifdef CONFIG_NET_FASTROUTE @@ -352,6 +355,7 @@ extern __inline__ int unregister_gifconf(unsigned int family) #define HAVE_NETIF_RX 1 extern void netif_rx(struct sk_buff *skb); extern void net_bh(void); +extern void dev_tint(struct device *dev); extern int dev_get_info(char *buffer, char **start, off_t offset, int length, int dummy); extern int dev_ioctl(unsigned int cmd, void *); extern int dev_change_flags(struct device *, unsigned); @@ -423,7 +427,7 @@ extern int dev_mc_add(struct device *dev, void *addr, int alen, int newonly); extern void dev_mc_discard(struct device *dev); extern void dev_set_promiscuity(struct device *dev, int inc); extern void dev_set_allmulti(struct device *dev, int inc); -/* Load a device via the kerneld */ +/* Load a device via the kmod */ extern void dev_load(const char *name); extern void dev_mcast_init(void); extern int netdev_register_fc(struct device *dev, void (*stimul)(struct device *dev)); diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index a7b51b977..b72ad4ed1 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -85,6 +85,9 @@ do { \ */ #define NFS_RPC_SWAPFLAGS (RPC_TASK_SWAPPER|RPC_TASK_ROOTCREDS) +/* Flags in the RPC client structure */ +#define NFS_CLNTF_BUFSIZE 0x0001 /* readdir buffer in longwords */ + #ifdef __KERNEL__ /* diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index 4a309eb91..8c6467010 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -66,7 +66,7 @@ struct rtattr #define RTA_ALIGNTO 4 #define RTA_ALIGN(len) ( ((len)+RTA_ALIGNTO-1) & ~(RTA_ALIGNTO-1) ) -#define RTA_OK(rta,len) ((rta)->rta_len > sizeof(struct rtattr) && \ +#define RTA_OK(rta,len) ((rta)->rta_len >= sizeof(struct rtattr) && \ (rta)->rta_len <= (len)) #define RTA_NEXT(rta,attrlen) ((attrlen) -= RTA_ALIGN((rta)->rta_len), \ (struct rtattr*)(((char*)(rta)) + RTA_ALIGN((rta)->rta_len))) diff --git a/include/linux/sched.h b/include/linux/sched.h index 096d0656c..7eae346a5 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -86,6 +86,12 @@ extern int last_pid; #define SCHED_FIFO 1 #define SCHED_RR 2 +/* + * This is an additional bit set when we want to + * yield the CPU for one re-schedule.. + */ +#define SCHED_YIELD 0x10 + struct sched_param { int sched_priority; }; @@ -113,19 +119,24 @@ extern void trap_init(void); asmlinkage void schedule(void); -/* Open file table structure */ + +/* + * Open file table structure + */ struct files_struct { int count; + int max_fds; + struct file ** fd; /* current fd array */ fd_set close_on_exec; fd_set open_fds; - struct file * fd[NR_OPEN]; }; #define INIT_FILES { \ 1, \ + NR_OPEN, \ + &init_fd_array[0], \ { { 0, } }, \ - { { 0, } }, \ - { NULL, } \ + { { 0, } } \ } struct fs_struct { @@ -387,43 +398,32 @@ extern __inline__ struct task_struct **get_free_taskslot(void) /* PID hashing. */ #define PIDHASH_SZ (NR_TASKS >> 2) extern struct task_struct *pidhash[PIDHASH_SZ]; -extern spinlock_t pidhash_lock; #define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1)) extern __inline__ void hash_pid(struct task_struct *p) { struct task_struct **htable = &pidhash[pid_hashfn(p->pid)]; - unsigned long flags; - spin_lock_irqsave(&pidhash_lock, flags); if((p->pidhash_next = *htable) != NULL) (*htable)->pidhash_pprev = &p->pidhash_next; *htable = p; p->pidhash_pprev = htable; - spin_unlock_irqrestore(&pidhash_lock, flags); } extern __inline__ void unhash_pid(struct task_struct *p) { - unsigned long flags; - - spin_lock_irqsave(&pidhash_lock, flags); if(p->pidhash_next) p->pidhash_next->pidhash_pprev = p->pidhash_pprev; *p->pidhash_pprev = p->pidhash_next; - spin_unlock_irqrestore(&pidhash_lock, flags); } extern __inline__ struct task_struct *find_task_by_pid(int pid) { struct task_struct *p, **htable = &pidhash[pid_hashfn(pid)]; - unsigned long flags; - spin_lock_irqsave(&pidhash_lock, flags); for(p = *htable; p && p->pid != pid; p = p->pidhash_next) ; - spin_unlock_irqrestore(&pidhash_lock, flags); return p; } @@ -571,19 +571,6 @@ extern void exit_sighand(struct task_struct *); extern int do_execve(char *, char **, char **, struct pt_regs *); extern int do_fork(unsigned long, unsigned long, struct pt_regs *); -/* See if we have a valid user level fd. - * If it makes sense, return the file structure it references. - * Otherwise return NULL. - */ -extern inline struct file *file_from_fd(const unsigned int fd) -{ - - if (fd >= NR_OPEN) - return NULL; - /* either valid or null */ - return current->files->fd[fd]; -} - /* * The wait-queues are circular lists, and you have to be *very* sure * to keep them correct. Use only these two functions to add/remove @@ -627,11 +614,9 @@ extern inline void remove_wait_queue(struct wait_queue ** p, struct wait_queue * write_unlock_irqrestore(&waitqueue_lock, flags); } -#define REMOVE_LINKS(p) do { unsigned long flags; \ - write_lock_irqsave(&tasklist_lock, flags); \ +#define REMOVE_LINKS(p) do { \ (p)->next_task->prev_task = (p)->prev_task; \ (p)->prev_task->next_task = (p)->next_task; \ - write_unlock_irqrestore(&tasklist_lock, flags); \ if ((p)->p_osptr) \ (p)->p_osptr->p_ysptr = (p)->p_ysptr; \ if ((p)->p_ysptr) \ @@ -640,13 +625,11 @@ extern inline void remove_wait_queue(struct wait_queue ** p, struct wait_queue * (p)->p_pptr->p_cptr = (p)->p_osptr; \ } while (0) -#define SET_LINKS(p) do { unsigned long flags; \ - write_lock_irqsave(&tasklist_lock, flags); \ +#define SET_LINKS(p) do { \ (p)->next_task = &init_task; \ (p)->prev_task = init_task.prev_task; \ init_task.prev_task->next_task = (p); \ init_task.prev_task = (p); \ - write_unlock_irqrestore(&tasklist_lock, flags); \ (p)->p_ysptr = NULL; \ if (((p)->p_osptr = (p)->p_pptr->p_cptr) != NULL) \ (p)->p_osptr->p_ysptr = p; \ diff --git a/include/linux/socket.h b/include/linux/socket.h index e274a3c51..afff2fd5c 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -137,6 +137,7 @@ struct ucred { #define AF_NETLINK 16 #define AF_ROUTE AF_NETLINK /* Alias to emulate 4.4BSD */ #define AF_PACKET 17 /* Packet family */ +#define AF_ASH 18 /* Ash */ #define AF_MAX 32 /* For now.. */ /* Protocol families, same as address families. */ @@ -160,6 +161,7 @@ struct ucred { #define PF_NETLINK AF_NETLINK #define PF_ROUTE AF_ROUTE #define PF_PACKET AF_PACKET +#define PF_ASH AF_ASH #define PF_MAX AF_MAX diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index 60fb2d74f..da2b2cdd1 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -30,6 +30,7 @@ struct rpc_portmap { * The high-level client handle */ struct rpc_clnt { + unsigned int cl_users; /* number of references */ struct rpc_xprt * cl_xprt; /* transport */ struct rpc_procinfo * cl_procinfo; /* procedure info */ u32 cl_maxproc; /* max procedure number */ @@ -37,7 +38,6 @@ struct rpc_clnt { char * cl_server; /* server machine name */ char * cl_protname; /* protocol name */ struct rpc_auth * cl_auth; /* authenticator */ - struct rpc_portmap cl_pmap; /* port mapping */ struct rpc_stat * cl_stats; /* statistics */ unsigned int cl_softrtry : 1,/* soft timeouts */ @@ -47,10 +47,11 @@ struct rpc_clnt { cl_binding : 1,/* doing a getport() */ cl_oneshot : 1,/* dispose after use */ cl_dead : 1;/* abandoned */ + unsigned int cl_flags; /* misc client flags */ unsigned long cl_hardmax; /* max hard timeout */ + struct rpc_portmap cl_pmap; /* port mapping */ struct rpc_wait_queue cl_bindwait; /* waiting on getport() */ - unsigned int cl_users; /* number of references */ }; #define cl_timeout cl_xprt->timeout #define cl_prog cl_pmap.pm_prog diff --git a/include/linux/swap.h b/include/linux/swap.h index 4d291146e..494490c32 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -36,10 +36,10 @@ struct swap_info_struct { extern int nr_swap_pages; extern int nr_free_pages; extern atomic_t nr_async_pages; -extern int min_free_pages; -extern int free_pages_low; -extern int free_pages_high; extern struct inode swapper_inode; +extern unsigned long page_cache_size; +extern int buffermem; +#define BUFFER_MEM ((buffermem >> PAGE_SHIFT) + page_cache_size) /* Incomplete types for prototype declarations: */ struct task_struct; diff --git a/include/linux/swapctl.h b/include/linux/swapctl.h index e71dcd067..cc169d2da 100644 --- a/include/linux/swapctl.h +++ b/include/linux/swapctl.h @@ -6,29 +6,18 @@ /* Swap tuning control */ -/* First, enumerate the different reclaim policies */ -enum RCL_POLICY {RCL_ROUND_ROBIN, RCL_BUFF_FIRST, RCL_PERSIST}; - -typedef struct swap_control_v5 +typedef struct swap_control_v6 { unsigned int sc_max_page_age; unsigned int sc_page_advance; unsigned int sc_page_decline; unsigned int sc_page_initial_age; - unsigned int sc_max_buff_age; - unsigned int sc_buff_advance; - unsigned int sc_buff_decline; - unsigned int sc_buff_initial_age; unsigned int sc_age_cluster_fract; unsigned int sc_age_cluster_min; unsigned int sc_pageout_weight; unsigned int sc_bufferout_weight; - unsigned int sc_buffer_grace; - unsigned int sc_nr_buffs_to_free; - unsigned int sc_nr_pages_to_free; - enum RCL_POLICY sc_policy; -} swap_control_v5; -typedef struct swap_control_v5 swap_control_t; +} swap_control_v6; +typedef struct swap_control_v6 swap_control_t; extern swap_control_t swap_control; typedef struct swapstat_v1 @@ -42,7 +31,23 @@ typedef struct swapstat_v1 typedef swapstat_v1 swapstat_t; extern swapstat_t swapstats; -extern int min_free_pages, free_pages_low, free_pages_high; +typedef struct buffer_mem_v1 +{ + unsigned int min_percent; + unsigned int borrow_percent; + unsigned int max_percent; +} buffer_mem_v1; +typedef buffer_mem_v1 buffer_mem_t; +extern buffer_mem_t buffer_mem; + +typedef struct freepages_v1 +{ + unsigned int min; + unsigned int low; + unsigned int high; +} freepages_v1; +typedef freepages_v1 freepages_t; +extern freepages_t freepages; #define SC_VERSION 1 #define SC_MAX_VERSION 1 @@ -55,17 +60,11 @@ extern int min_free_pages, free_pages_low, free_pages_high; failure to free a resource at any priority */ #define RCL_FAILURE (RCL_MAXPRI + 1) -#define RCL_POLICY (swap_control.sc_policy) #define AGE_CLUSTER_FRACT (swap_control.sc_age_cluster_fract) #define AGE_CLUSTER_MIN (swap_control.sc_age_cluster_min) #define PAGEOUT_WEIGHT (swap_control.sc_pageout_weight) #define BUFFEROUT_WEIGHT (swap_control.sc_bufferout_weight) -#define NR_BUFFS_TO_FREE (swap_control.sc_nr_buffs_to_free) -#define NR_PAGES_TO_FREE (swap_control.sc_nr_pages_to_free) - -#define BUFFERMEM_GRACE (swap_control.sc_buffer_grace) - /* Page aging (see mm/swap.c) */ #define MAX_PAGE_AGE (swap_control.sc_max_page_age) @@ -73,11 +72,6 @@ extern int min_free_pages, free_pages_low, free_pages_high; #define PAGE_DECLINE (swap_control.sc_page_decline) #define PAGE_INITIAL_AGE (swap_control.sc_page_initial_age) -#define MAX_BUFF_AGE (swap_control.sc_max_buff_age) -#define BUFF_ADVANCE (swap_control.sc_buff_advance) -#define BUFF_DECLINE (swap_control.sc_buff_decline) -#define BUFF_INITIAL_AGE (swap_control.sc_buff_initial_age) - /* Given a resource of N units (pages or buffers etc), we only try to * age and reclaim AGE_CLUSTER_FRACT per 1024 resources each time we * scan the resource list. */ diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 865bdd1dd..b7550ba2c 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -70,7 +70,9 @@ enum KERN_PRINTK, /* sturct: control printk logging parameters */ KERN_NAMETRANS, /* Name translation */ KERN_STATINODE, - KERN_DENTRY /* dentry statistics */ + KERN_DENTRY, /* dentry statistics */ + KERN_MODPROBE, + KERN_KMOD_UNLOAD_DELAY }; @@ -82,6 +84,7 @@ enum VM_FREEPG, /* struct: Set free page thresholds */ VM_BDFLUSH, /* struct: Control buffer cache flushing */ VM_OVERCOMMIT_MEMORY, /* Turn off the virtual memory safety limit */ + VM_BUFFERMEM /* struct: Set cache memory thresholds */ }; @@ -118,6 +121,7 @@ enum NET_CORE_FASTROUTE, NET_CORE_MSG_COST, NET_CORE_MSG_BURST, + NET_CORE_OPTMEM_MAX, }; /* /proc/sys/net/ethernet */ @@ -145,8 +149,6 @@ enum NET_IPV4_FIB_HASH = 19, NET_IPV4_TCP_HOE_RETRANSMITS=32, - NET_IPV4_TCP_SACK, - NET_IPV4_TCP_TSACK, NET_IPV4_TCP_TIMESTAMPS, NET_IPV4_TCP_WINDOW_SCALING, NET_IPV4_TCP_VEGAS_CONG_AVOID, @@ -167,6 +169,7 @@ enum NET_IPV4_IP_MASQ_DEBUG, NET_TCP_SYNCOOKIES, NET_TCP_STDURG, + NET_TCP_RFC1337, NET_TCP_SYN_TAILDROP, NET_TCP_MAX_SYN_BACKLOG, NET_IPV4_LOCAL_PORT_RANGE, diff --git a/include/linux/tty.h b/include/linux/tty.h index 6b00c4329..34c88d721 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -320,8 +320,7 @@ extern int espserial_init(void); extern int tty_paranoia_check(struct tty_struct *tty, kdev_t device, const char *routine); -extern char *_tty_name(struct tty_struct *tty, char *buf); -extern char *tty_name(struct tty_struct *tty); +extern char *tty_name(struct tty_struct *tty, char *buf); extern void tty_wait_until_sent(struct tty_struct * tty, int timeout); extern int tty_check_change(struct tty_struct * tty); extern void stop_tty(struct tty_struct * tty); diff --git a/include/linux/umsdos_fs.p b/include/linux/umsdos_fs.p index 62ce67d0c..7c0e64ec3 100644 --- a/include/linux/umsdos_fs.p +++ b/include/linux/umsdos_fs.p @@ -1,9 +1,6 @@ /* check.c 23/01/95 03.38.30 */ void check_page_tables (void); /* dir.c 22/06/95 00.22.12 */ -struct dentry *creat_dentry (const char *name, - const int len, - struct inode *inode); int compat_msdos_create(struct inode *dir, const char *name, int len, @@ -30,6 +27,16 @@ int UMSDOS_lookup(struct inode *dir,struct dentry *dentry); int umsdos_hlink2inode (struct inode *hlink, struct inode **result); /* emd.c 22/06/95 00.22.04 */ +void fill_new_filp (struct file *filp, struct dentry *dentry); +void kill_dentry (struct dentry *dentry); +struct dentry *creat_dentry (const char *name, + const int len, + struct inode *inode); +ssize_t umsdos_file_write_kmem_real (struct file *filp, + const char *buf, + size_t count, + loff_t *offs); + ssize_t umsdos_file_read_kmem (struct inode *emd_dir, struct file *filp, char *buf, diff --git a/include/net/dst.h b/include/net/dst.h index b879bb059..0d18f60d2 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -120,6 +120,8 @@ extern void dst_destroy(struct dst_entry * dst); extern __inline__ void dst_free(struct dst_entry * dst) { + if (dst->obsolete > 1) + return; if (!atomic_read(&dst->use)) { dst_destroy(dst); return; diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index 42233aadf..863037b23 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -114,7 +114,7 @@ extern __inline__ void ip6_dst_store(struct sock *sk, struct dst_entry *dst) struct rt6_info *rt; np = &sk->net_pinfo.af_inet6; - sk->dst_cache = dst; + dst_release(xchg(&sk->dst_cache,dst)); rt = (struct rt6_info *) dst; diff --git a/include/net/ipv6.h b/include/net/ipv6.h index b6055ae44..1a322a498 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -4,7 +4,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: ipv6.h,v 1.8 1997/12/29 19:52:09 kuznet Exp $ + * $Id: ipv6.h,v 1.9 1998/03/08 05:55:20 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/include/net/route.h b/include/net/route.h index 338e158fd..624fd233a 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -102,6 +102,7 @@ extern unsigned inet_addr_type(u32 addr); extern void ip_rt_multicast_event(struct in_device *); extern int ip_rt_ioctl(unsigned int cmd, void *arg); extern void ip_rt_get_source(u8 *src, struct rtable *rt); +extern int ip_rt_dump(struct sk_buff *skb, struct netlink_callback *cb); extern __inline__ void ip_rt_put(struct rtable * rt) diff --git a/include/net/sock.h b/include/net/sock.h index c225a0015..589f58c7c 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -191,42 +191,75 @@ struct raw_opt { struct tcp_opt { + int tcp_header_len; /* Bytes of tcp header to send */ + +/* + * Header prediction flags + * 0x5?10 << 16 + snd_wnd in net byte order + */ + __u32 pred_flags; + /* * RFC793 variables by their proper names. This means you can * read the code and the spec side by side (and laugh ...) * See RFC793 and RFC1122. The RFC writes these in capitals. */ __u32 rcv_nxt; /* What we want to receive next */ - __u32 rcv_up; /* The urgent point (may not be valid) */ - __u32 rcv_wnd; /* Current receiver window */ __u32 snd_nxt; /* Next sequence we send */ + __u32 snd_una; /* First byte we want an ack for */ - __u32 snd_up; /* Outgoing urgent pointer */ - __u32 snd_wl1; /* Sequence for window update */ - __u32 snd_wl2; /* Ack sequence for update */ + __u32 rcv_tstamp; /* timestamp of last received packet */ + __u32 lrcvtime; /* timestamp of last received data packet*/ + __u32 srtt; /* smothed round trip time << 3 */ - __u32 rcv_wup; /* rcv_nxt on last window update sent */ + __u32 ato; /* delayed ack timeout */ + __u32 snd_wl1; /* Sequence for window update */ - __u32 fin_seq; /* XXX This one should go, we don't need it. -DaveM */ + __u32 snd_wl2; /* Ack sequence for update */ + __u32 snd_wnd; /* The window we expect to receive */ + __u32 max_window; + __u8 pending; /* pending events */ + __u8 retransmits; + __u32 last_ack_sent; /* last ack we sent */ - __u32 srtt; /* smothed round trip time << 3 */ + __u32 backoff; /* backoff */ __u32 mdev; /* medium deviation */ + __u32 snd_cwnd; /* Sending congestion window */ __u32 rto; /* retransmit timeout */ - __u32 backoff; /* backoff */ + + __u32 packets_out; /* Packets which are "in flight" */ + __u32 high_seq; /* highest sequence number sent by onset of congestion */ /* * Slow start and congestion control (see also Nagle, and Karn & Partridge) */ - __u32 snd_cwnd; /* Sending congestion window */ __u32 snd_ssthresh; /* Slow start size threshold */ __u16 snd_cwnd_cnt; - __u16 max_window; + __u8 dup_acks; /* Consequetive duplicate acks seen from other end */ + __u8 delayed_acks; + + /* Two commonly used timers in both sender and receiver paths. */ + struct timer_list retransmit_timer; /* Resend (no ack) */ + struct timer_list delack_timer; /* Ack delay */ + + struct sk_buff_head out_of_order_queue; /* Out of order segments go here */ + struct tcp_func *af_specific; /* Operations which are AF_INET{4,6} specific */ + struct sk_buff *send_head; /* Front of stuff to transmit */ + struct sk_buff *retrans_head; /* retrans head can be + * different to the head of + * write queue if we are doing + * fast retransmit + */ + + __u32 rcv_wnd; /* Current receiver window */ + __u32 rcv_wup; /* rcv_nxt on last window update sent */ + __u32 write_seq; + __u32 copied_seq; /* * Options received (usually on last packet, some only on SYN packets). */ char tstamp_ok, /* TIMESTAMP seen on SYN packet */ - wscale_ok, /* Wscale seen on SYN packet */ - sack_ok; /* SACK_PERM seen on SYN packet */ + wscale_ok; /* Wscale seen on SYN packet */ char saw_tstamp; /* Saw TIMESTAMP on last packet */ __u16 in_mss; /* MSS option received from sender */ __u8 snd_wscale; /* Window scaling received from sender */ @@ -235,60 +268,20 @@ struct tcp_opt __u32 rcv_tsecr; /* Time stamp echo reply */ __u32 ts_recent; /* Time stamp to echo next */ __u32 ts_recent_stamp;/* Time we stored ts_recent (for aging) */ - __u32 last_ack_sent; /* last ack we sent */ - int sacks; /* Number of SACK blocks if any */ - __u32 left_sack[4]; /* Left edges of blocks */ - __u32 right_sack[4]; /* Right edges of blocks */ - int tcp_header_len; /* Bytes of tcp header to send */ -/* - * Timers used by the TCP protocol layer - */ - struct timer_list delack_timer; /* Ack delay */ - struct timer_list idle_timer; /* Idle watch */ - struct timer_list completion_timer; /* Up/Down timer */ struct timer_list probe_timer; /* Probes */ - struct timer_list retransmit_timer; /* Resend (no ack) */ - - __u32 basertt; /* Vegas baseRTT */ - __u32 packets_out; /* Packets which are "in flight" */ - __u32 window_clamp; /* XXX Document this... -DaveM */ - - __u8 pending; /* pending events */ - __u8 delayed_acks; - __u8 dup_acks; /* Consequetive duplicate acks seen from other end */ - __u8 retransmits; - - __u32 lrcvtime; /* timestamp of last received data packet */ - __u32 rcv_tstamp; /* timestamp of last received packet */ - __u32 iat_mdev; /* interarrival time medium deviation */ - __u32 iat; /* interarrival time */ - __u32 ato; /* delayed ack timeout */ - __u32 high_seq; /* highest sequence number sent by onset of congestion */ - -/* - * new send pointers - */ - struct sk_buff * send_head; - struct sk_buff * retrans_head; /* retrans head can be - * different to the head of - * write queue if we are doing - * fast retransmit - */ -/* - * Header prediction flags - * 0x5?10 << 16 + snd_wnd in net byte order - */ - __u32 pred_flags; - __u32 snd_wnd; /* The window we expect to receive */ - - __u32 probes_out; /* unanswered 0 window probes */ + __u32 basertt; /* Vegas baseRTT */ + __u32 window_clamp; /* XXX Document this... -DaveM */ + __u32 probes_out; /* unanswered 0 window probes */ + __u32 syn_seq; + __u32 fin_seq; + __u32 urg_seq; + __u32 urg_data; struct open_request *syn_wait_queue; struct open_request **syn_wait_last; int syn_backlog; - struct tcp_func *af_specific; }; @@ -347,73 +340,73 @@ struct sock struct sock *sklist_next; struct sock *sklist_prev; - atomic_t wmem_alloc; - atomic_t rmem_alloc; - unsigned long allocation; /* Allocation mode */ + /* Local port binding hash linkage. */ + struct sock *bind_next; + struct sock **bind_pprev; + + /* Main hash linkage for various protocol lookup tables. */ + struct sock *next; + struct sock **pprev; - /* The following stuff should probably move to the tcp private area */ - __u32 write_seq; - __u32 copied_seq; - __u32 syn_seq; - __u32 urg_seq; - __u32 urg_data; - unsigned char delayed_acks; - /* End of block to move */ + /* Socket demultiplex comparisons on incoming packets. */ + __u32 daddr; /* Foreign IPv4 addr */ + __u32 rcv_saddr; /* Bound local IPv4 addr */ + int bound_dev_if; /* Bound device index if != 0 */ + unsigned short num; /* Local port */ + volatile unsigned char state, /* Connection state */ + zapped; /* In ax25 & ipx means not linked */ + struct tcphdr dummy_th; /* TCP header template */ - int sock_readers; /* user count */ + int sock_readers; /* user count */ + int rcvbuf; + + struct wait_queue **sleep; + struct dst_entry *dst_cache; /* Destination cache */ + atomic_t rmem_alloc; /* Receive queue bytes committed */ + struct sk_buff_head receive_queue; /* Incoming packets */ + atomic_t wmem_alloc; /* Transmit queue bytes committed */ + struct sk_buff_head write_queue; /* Packet sending queue */ + atomic_t omem_alloc; /* "o" is "option" or "other" */ + __u32 saddr; /* Sending source */ + unsigned int allocation; /* Allocation mode */ + int sndbuf; + struct sock *prev; /* * Not all are volatile, but some are, so we * might as well say they all are. */ volatile char dead, - urginline, done, + urginline, reuse, keepopen, linger, destroy, no_check, - zapped, /* In ax25 & ipx means not linked */ broadcast, nonagle, bsdism; - int bound_dev_if; - unsigned long lingertime; + unsigned char debug; int proc; + unsigned long lingertime; - struct sock *next; - struct sock **pprev; - struct sock *bind_next; - struct sock **bind_pprev; - struct sock *prev; int hashent; struct sock *pair; - struct sk_buff_head back_log; - - struct sk_buff_head write_queue, - receive_queue, - out_of_order_queue, + /* Error and backlog packet queues, rarely used. */ + struct sk_buff_head back_log, error_queue; unsigned short family; struct proto *prot; - struct wait_queue **sleep; - - __u32 daddr; - __u32 saddr; /* Sending source */ - __u32 rcv_saddr; /* Bound address */ - struct dst_entry *dst_cache; /* * mss is min(mtu, max_window) */ unsigned short mtu; /* mss negotiated in the syn's */ unsigned short mss; /* current eff. mss - can change */ unsigned short user_mss; /* mss requested by user in ioctl */ - unsigned short num; - unsigned short shutdown; #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) @@ -436,16 +429,12 @@ struct sock cause failure but are the cause of a persistent failure not just 'timed out' */ - unsigned char protocol; - volatile unsigned char state; unsigned short ack_backlog; unsigned short max_ack_backlog; - unsigned char debug; __u32 priority; - int rcvbuf; - int sndbuf; unsigned short type; unsigned char localroute; /* Route locally only */ + unsigned char protocol; struct ucred peercred; #ifdef CONFIG_FILTER @@ -472,11 +461,6 @@ struct sock #if defined (CONFIG_PACKET) || defined(CONFIG_PACKET_MODULE) struct packet_opt *af_packet; #endif -#ifdef CONFIG_INET -#ifdef CONFIG_NUTCP - struct tcp_opt af_tcp; -#endif -#endif #if defined(CONFIG_X25) || defined(CONFIG_X25_MODULE) x25_cb *x25; #endif @@ -503,7 +487,6 @@ struct sock int ip_ttl; /* TTL setting */ int ip_tos; /* TOS */ unsigned ip_cmsg_flags; - struct tcphdr dummy_th; struct ip_options *opt; unsigned char ip_hdrincl; /* Include headers ? */ __u8 ip_mc_ttl; /* Multicasting TTL */ @@ -731,7 +714,7 @@ here: } /* - * This might not be the most apropriate place for this two + * This might not be the most appropriate place for this two * but since they are used by a lot of the net related code * at least they get declared on a include that is common to all */ @@ -750,7 +733,7 @@ static __inline__ int max(unsigned int a, unsigned int b) return a; } -extern struct sock * sk_alloc(int family, int priority); +extern struct sock * sk_alloc(int family, int priority, int zero_it); extern void sk_free(struct sock *sk); extern void destroy_sock(struct sock *sk); @@ -884,7 +867,6 @@ extern __inline__ int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) */ if (atomic_read(&sk->rmem_alloc) + skb->truesize >= (unsigned)sk->rcvbuf) return -ENOMEM; - skb_set_owner_r(skb, sk); #ifdef CONFIG_FILTER if (sk->filter) @@ -894,7 +876,8 @@ extern __inline__ int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) } #endif /* CONFIG_FILTER */ - skb_queue_tail(&sk->receive_queue,skb); + skb_set_owner_r(skb, sk); + skb_queue_tail(&sk->receive_queue, skb); if (!sk->dead) sk->data_ready(sk,skb->len); return 0; diff --git a/include/net/tcp.h b/include/net/tcp.h index 4c445ca1a..cec01dfe6 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -27,13 +27,13 @@ * New scheme, half the table is for TIME_WAIT, the other half is * for the rest. I'll experiment with dynamic table growth later. */ -#define TCP_HTABLE_SIZE 1024 +#define TCP_HTABLE_SIZE 512 /* This is for listening sockets, thus all sockets which possess wildcards. */ #define TCP_LHTABLE_SIZE 32 /* Yes, really, this is all you need. */ /* This is for all sockets, to keep track of the local port allocations. */ -#define TCP_BHTABLE_SIZE 64 +#define TCP_BHTABLE_SIZE 512 /* tcp_ipv4.c: These need to be shared by v4 and v6 because the lookup * and hashing code needs to work with different AF's yet @@ -41,47 +41,153 @@ */ extern struct sock *tcp_established_hash[TCP_HTABLE_SIZE]; extern struct sock *tcp_listening_hash[TCP_LHTABLE_SIZE]; -extern struct sock *tcp_bound_hash[TCP_BHTABLE_SIZE]; -/* tcp_ipv4.c: These sysctl variables need to be shared between v4 and v6 - * because the v6 tcp code to intialize a connection needs to interoperate - * with the v4 code using the same variables. - * FIXME: It would be better to rewrite the connection code to be - * address family independent and just leave one copy in the ipv4 section. - * This would also clean up some code duplication. -- erics +/* There are a few simple rules, which allow for local port reuse by + * an application. In essence: + * + * 1) Sockets bound to different interfaces may share a local port. + * Failing that, goto test 2. + * 2) If all sockets have sk->reuse set, and none of them are in + * TCP_LISTEN state, the port may be shared. + * Failing that, goto test 3. + * 3) If all sockets are bound to a specific sk->rcv_saddr local + * address, and none of them are the same, the port may be + * shared. + * Failing this, the port cannot be shared. + * + * The interesting point, is test #2. This is what an FTP server does + * all day. To optimize this case we use a specific flag bit defined + * below. As we add sockets to a bind bucket list, we perform a + * check of: (newsk->reuse && (newsk->state != TCP_LISTEN)) + * As long as all sockets added to a bind bucket pass this test, + * the flag bit will be set. + * The resulting situation is that tcp_v[46]_verify_bind() can just check + * for this flag bit, if it is set and the socket trying to bind has + * sk->reuse set, we don't even have to walk the owners list at all, + * we return that it is ok to bind this socket to the requested local port. + * + * Sounds like a lot of work, but it is worth it. In a more naive + * implementation (ie. current FreeBSD etc.) the entire list of ports + * must be walked for each data port opened by an ftp server. Needless + * to say, this does not scale at all. With a couple thousand FTP + * users logged onto your box, isn't it nice to know that new data + * ports are created in O(1) time? I thought so. ;-) -DaveM */ -extern int sysctl_tcp_sack; -extern int sysctl_tcp_timestamps; -extern int sysctl_tcp_window_scaling; +struct tcp_bind_bucket { + unsigned short port; + unsigned short flags; +#define TCPB_FLAG_LOCKED 0x0001 +#define TCPB_FLAG_FASTREUSE 0x0002 + + struct tcp_bind_bucket *next; + struct sock *owners; + struct tcp_bind_bucket **pprev; +}; -/* These are AF independent. */ -static __inline__ int tcp_bhashfn(__u16 lport) +extern struct tcp_bind_bucket *tcp_bound_hash[TCP_BHTABLE_SIZE]; +extern kmem_cache_t *tcp_bucket_cachep; +extern struct tcp_bind_bucket *tcp_bucket_create(unsigned short snum); +extern void tcp_bucket_unlock(struct sock *sk); +extern int tcp_port_rover; + +/* Level-1 socket-demux cache. */ +#define TCP_NUM_REGS 32 +extern struct sock *tcp_regs[TCP_NUM_REGS]; + +#define TCP_RHASH_FN(__fport) \ + ((((__fport) >> 7) ^ (__fport)) & (TCP_NUM_REGS - 1)) +#define TCP_RHASH(__fport) tcp_regs[TCP_RHASH_FN((__fport))] +#define TCP_SK_RHASH_FN(__sock) TCP_RHASH_FN((__sock)->dummy_th.dest) +#define TCP_SK_RHASH(__sock) tcp_regs[TCP_SK_RHASH_FN((__sock))] + +static __inline__ void tcp_reg_zap(struct sock *sk) { - return (lport ^ (lport >> 7)) & (TCP_BHTABLE_SIZE - 1); + struct sock **rpp; + + rpp = &(TCP_SK_RHASH(sk)); + if(*rpp == sk) + *rpp = NULL; } -/* Find the next port that hashes h that is larger than lport. - * If you change the hash, change this function to match, or you will - * break TCP port selection. This function must also NOT wrap around - * when the next number exceeds the largest possible port (2^16-1). - */ -static __inline__ int tcp_bhashnext(__u16 lport, __u16 h) +/* These are AF independent. */ +static __inline__ int tcp_bhashfn(__u16 lport) { - __u32 s; /* don't change this to a smaller type! */ - - s = (lport ^ (h ^ tcp_bhashfn(lport))); - if (s > lport) - return s; - s = lport + TCP_BHTABLE_SIZE; - return (s ^ (h ^ tcp_bhashfn(s))); + return (lport & (TCP_BHTABLE_SIZE - 1)); } -static __inline__ int tcp_sk_bhashfn(struct sock *sk) +static __inline__ void tcp_sk_bindify(struct sock *sk) { - __u16 lport = sk->num; - return tcp_bhashfn(lport); + struct tcp_bind_bucket *tb; + unsigned short snum = sk->num; + + for(tb = tcp_bound_hash[tcp_bhashfn(snum)]; tb->port != snum; tb = tb->next) + ; + /* Update bucket flags. */ + if(tb->owners == NULL) { + /* We're the first. */ + if(sk->reuse && sk->state != TCP_LISTEN) + tb->flags = TCPB_FLAG_FASTREUSE; + else + tb->flags = 0; + } else { + if((tb->flags & TCPB_FLAG_FASTREUSE) && + ((sk->reuse == 0) || (sk->state == TCP_LISTEN))) + tb->flags &= ~TCPB_FLAG_FASTREUSE; + } + if((sk->bind_next = tb->owners) != NULL) + tb->owners->bind_pprev = &sk->bind_next; + tb->owners = sk; + sk->bind_pprev = &tb->owners; + sk->prev = (struct sock *) tb; } +/* This is a TIME_WAIT bucket. It works around the memory consumption + * problems of sockets in such a state on heavily loaded servers, but + * without violating the protocol specification. + */ +struct tcp_tw_bucket { + /* These _must_ match the beginning of struct sock precisely. + * XXX Yes I know this is gross, but I'd have to edit every single + * XXX networking file if I created a "struct sock_header". -DaveM + */ + struct sock *sklist_next; + struct sock *sklist_prev; + struct sock *bind_next; + struct sock **bind_pprev; + struct sock *next; + struct sock **pprev; + __u32 daddr; + __u32 rcv_saddr; + int bound_dev_if; + unsigned short num; + unsigned char state, + family; /* sk->zapped */ + __u16 source; /* sk->dummy_th.source */ + __u16 dest; /* sk->dummy_th.dest */ + + /* And these are ours. */ + __u32 rcv_nxt; + struct tcp_func *af_specific; + struct tcp_bind_bucket *tb; + struct timer_list timer; +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + struct in6_addr v6_daddr; + struct in6_addr v6_rcv_saddr; +#endif +}; + +extern kmem_cache_t *tcp_timewait_cachep; + +/* tcp_ipv4.c: These sysctl variables need to be shared between v4 and v6 + * because the v6 tcp code to intialize a connection needs to interoperate + * with the v4 code using the same variables. + * FIXME: It would be better to rewrite the connection code to be + * address family independent and just leave one copy in the ipv4 section. + * This would also clean up some code duplication. -- erics + */ +extern int sysctl_tcp_timestamps; +extern int sysctl_tcp_window_scaling; + /* These can have wildcards, don't try too hard. */ static __inline__ int tcp_lhashfn(unsigned short num) { @@ -93,28 +199,6 @@ static __inline__ int tcp_sk_listen_hashfn(struct sock *sk) return tcp_lhashfn(sk->num); } -/* Only those holding the sockhash lock call these two things here. - * Note the slightly gross overloading of sk->prev, AF_UNIX is the - * only other main benefactor of that member of SK, so who cares. - */ -static __inline__ void tcp_sk_bindify(struct sock *sk) -{ - int hashent = tcp_sk_bhashfn(sk); - struct sock **htable = &tcp_bound_hash[hashent]; - - if((sk->bind_next = *htable) != NULL) - (*htable)->bind_pprev = &sk->bind_next; - *htable = sk; - sk->bind_pprev = htable; -} - -static __inline__ void tcp_sk_unbindify(struct sock *sk) -{ - if(sk->bind_next) - sk->bind_next->bind_pprev = sk->bind_pprev; - *(sk->bind_pprev) = sk->bind_next; -} - #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) #define NETHDR_SIZE sizeof(struct ipv6hdr) #else @@ -186,6 +270,8 @@ static __inline__ void tcp_sk_unbindify(struct sock *sk) * we tell the LL layer that it is something * wrong (e.g. that it can expire redirects) */ +#define TCP_BUCKETGC_PERIOD (HZ) + /* * TCP option */ @@ -193,9 +279,6 @@ static __inline__ void tcp_sk_unbindify(struct sock *sk) #define TCPOPT_NOP 1 /* Padding */ #define TCPOPT_EOL 0 /* End of options */ #define TCPOPT_MSS 2 /* Segment size negotiating */ -/* - * We don't use these yet, but they are for PAWS and big windows - */ #define TCPOPT_WINDOW 3 /* Window scaling */ #define TCPOPT_SACK_PERM 4 /* SACK Permitted */ #define TCPOPT_SACK 5 /* SACK Block */ @@ -210,6 +293,10 @@ static __inline__ void tcp_sk_unbindify(struct sock *sk) #define TCPOLEN_SACK_PERM 2 #define TCPOLEN_TIMESTAMP 10 +/* But this is what stacks really send out. */ +#define TCPOLEN_TSTAMP_ALIGNED 12 +#define TCPOLEN_WSCALE_ALIGNED 4 + /* * TCP option flags for parsed options. */ @@ -259,7 +346,6 @@ struct open_request { __u8 __pad; unsigned snd_wscale : 4, rcv_wscale : 4, - sack_ok : 1, tstamp_ok : 1, wscale_ok : 1; /* The following two fields can be easily recomputed I think -AK */ @@ -355,7 +441,7 @@ extern __inline int after(__u32 seq1, __u32 seq2) /* is s2<=s1<=s3 ? */ extern __inline int between(__u32 seq1, __u32 seq2, __u32 seq3) { - return (after(seq1+1, seq2) && before(seq1, seq3+1)); + return seq3 - seq2 >= seq1 - seq2; } @@ -390,6 +476,11 @@ extern int tcp_rcv_established(struct sock *sk, struct tcphdr *th, __u16 len); +extern int tcp_timewait_state_process(struct tcp_tw_bucket *tw, + struct sk_buff *skb, + struct tcphdr *th, + void *opt, __u16 len); + extern void tcp_close(struct sock *sk, unsigned long timeout); extern struct sock * tcp_accept(struct sock *sk, int flags); @@ -427,6 +518,10 @@ extern int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr, __u32 isn); +extern struct sock * tcp_create_openreq_child(struct sock *sk, + struct open_request *req, + struct sk_buff *skb); + extern struct sock * tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, struct open_request *req, @@ -457,10 +552,11 @@ extern void tcp_send_probe0(struct sock *); extern void tcp_send_partial(struct sock *); extern void tcp_write_wakeup(struct sock *); extern void tcp_send_fin(struct sock *sk); +extern void tcp_send_active_reset(struct sock *sk); extern int tcp_send_synack(struct sock *); -extern void tcp_send_skb(struct sock *, struct sk_buff *); +extern void tcp_send_skb(struct sock *, struct sk_buff *, int force_queue); extern void tcp_send_ack(struct sock *sk); -extern void tcp_send_delayed_ack(struct sock *sk, int max_timeout); +extern void tcp_send_delayed_ack(struct tcp_opt *tp, int max_timeout); /* CONFIG_IP_TRANSPARENT_PROXY */ extern int tcp_chkaddr(struct sk_buff *); @@ -492,40 +588,94 @@ struct tcp_sl_timer { #define TCP_SLT_SYNACK 0 #define TCP_SLT_KEEPALIVE 1 -#define TCP_SLT_MAX 2 +#define TCP_SLT_BUCKETGC 2 +#define TCP_SLT_MAX 3 extern struct tcp_sl_timer tcp_slt_array[TCP_SLT_MAX]; -/* - * FIXME: this method of choosing when to send a window update - * does not seem correct to me. -- erics +/* Compute the actual receive window we are currently advertising. */ +static __inline__ u32 tcp_receive_window(struct tcp_opt *tp) +{ + return tp->rcv_wup - (tp->rcv_nxt - tp->rcv_wnd); +} + +/* Choose a new window, without checks for shrinking, and without + * scaling applied to the result. The caller does these things + * if necessary. This is a "raw" window selection. */ -static __inline__ unsigned short tcp_raise_window(struct sock *sk) +extern u32 __tcp_select_window(struct sock *sk); + +/* Chose a new window to advertise, update state in tcp_opt for the + * socket, and return result with RFC1323 scaling applied. The return + * value can be stuffed directly into th->window for an outgoing + * frame. + */ +extern __inline__ u16 tcp_select_window(struct sock *sk) { - struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; - long cur_win; - int res = 0; - - /* - * compute the actual window i.e. - * old_window - received_bytes_on_that_win - */ + struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + u32 new_win = __tcp_select_window(sk); + u32 cur_win = tcp_receive_window(tp); - cur_win = tp->rcv_wup - (tp->rcv_nxt - tp->rcv_wnd); + /* Never shrink the offered window */ + if(new_win < cur_win) + new_win = cur_win; + tp->rcv_wnd = new_win; + tp->rcv_wup = tp->rcv_nxt; + /* RFC1323 scaling applied */ + return new_win >> tp->rcv_wscale; +} - /* - * We need to send an ack right away if - * our rcv window is blocking the sender and - * we have more free space to offer. - */ +/* See if we can advertise non-zero, and if so how much we + * can increase our advertisement. If it becomes more than + * twice what we are talking about right now, return true. + */ +extern __inline__ int tcp_raise_window(struct sock *sk) +{ + struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + u32 new_win = __tcp_select_window(sk); + u32 cur_win = tcp_receive_window(tp); - if (cur_win < (sk->mss << 1)) - res = 1; - return res; + return (new_win && (new_win > (cur_win << 1))); } -extern unsigned short tcp_select_window(struct sock *sk); +/* This checks if the data bearing packet SKB (usually tp->send_head) + * should be put on the wire right now. + */ +static __inline__ int tcp_snd_test(struct sock *sk, struct sk_buff *skb) +{ + struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + int nagle_check = 1; + int len; + + /* RFC 1122 - section 4.2.3.4 + * + * We must queue if + * + * a) The right edge of this frame exceeds the window + * b) There are packets in flight and we have a small segment + * [SWS avoidance and Nagle algorithm] + * (part of SWS is done on packetization) + * c) We are retransmiting [Nagle] + * d) We have too many packets 'in flight' + * + * Don't use the nagle rule for urgent data. + */ + len = skb->end_seq - skb->seq; + if (!sk->nonagle && len < (sk->mss >> 1) && tp->packets_out && + !skb->h.th->urg) + nagle_check = 0; + + return (nagle_check && tp->packets_out < tp->snd_cwnd && + !after(skb->end_seq, tp->snd_una + tp->snd_wnd) && + tp->retransmits == 0); +} + +/* This tells the input processing path that an ACK should go out + * right now. + */ +#define tcp_enter_quickack_mode(__tp) ((__tp)->ato = (HZ/100)) +#define tcp_in_quickack_mode(__tp) ((__tp)->ato == (HZ/100)) /* * List all states of a TCP socket that can be viewed as a "connected" @@ -581,41 +731,49 @@ static __inline__ void tcp_set_state(struct sock *sk, int state) case TCP_CLOSE: /* Should be about 2 rtt's */ net_reset_timer(sk, TIME_DONE, min(tp->srtt * 2, TCP_DONE_TIME)); + sk->prot->unhash(sk); /* fall through */ default: if (oldstate==TCP_ESTABLISHED) tcp_statistics.TcpCurrEstab--; - if (state == TCP_TIME_WAIT || state == TCP_CLOSE) - sk->prot->rehash(sk); } } static __inline__ void tcp_build_options(__u32 *ptr, struct tcp_opt *tp) { - /* FIXME: We will still need to do SACK here. */ if (tp->tstamp_ok) { - *ptr = ntohl((TCPOPT_NOP << 24) - | (TCPOPT_NOP << 16) - | (TCPOPT_TIMESTAMP << 8) - | TCPOLEN_TIMESTAMP); + *ptr = __constant_htonl((TCPOPT_NOP << 24) | + (TCPOPT_NOP << 16) | + (TCPOPT_TIMESTAMP << 8) | + TCPOLEN_TIMESTAMP); /* rest filled in by tcp_update_options */ } } static __inline__ void tcp_update_options(__u32 *ptr, struct tcp_opt *tp) { - /* FIXME: We will still need to do SACK here. */ if (tp->tstamp_ok) { *++ptr = htonl(jiffies); *++ptr = htonl(tp->ts_recent); } } +static __inline__ void tcp_build_and_update_options(__u32 *ptr, struct tcp_opt *tp) +{ + if (tp->tstamp_ok) { + *ptr++ = __constant_htonl((TCPOPT_NOP << 24) | + (TCPOPT_NOP << 16) | + (TCPOPT_TIMESTAMP << 8) | + TCPOLEN_TIMESTAMP); + *ptr++ = htonl(jiffies); + *ptr = htonl(tp->ts_recent); + } +} + /* * This routines builds a generic TCP header. * They also build the RFC1323 Timestamp, but don't fill the * actual timestamp in (you need to call tcp_update_options for this). - * It can't (unfortunately) do SACK as well. * XXX: pass tp instead of sk here. */ @@ -624,23 +782,12 @@ static inline void tcp_build_header_data(struct tcphdr *th, struct sock *sk, int struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); memcpy(th,(void *) &(sk->dummy_th), sizeof(*th)); - th->seq = htonl(sk->write_seq); + th->seq = htonl(tp->write_seq); if (!push) th->psh = 1; tcp_build_options((__u32*)(th+1), tp); } -static inline void tcp_build_header(struct tcphdr *th, struct sock *sk) -{ - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); - - memcpy(th,(void *) &(sk->dummy_th), sizeof(*th)); - th->seq = htonl(sk->write_seq); - th->ack_seq = htonl(tp->last_ack_sent = tp->rcv_nxt); - th->window = htons(tcp_select_window(sk)); - tcp_build_options((__u32 *)(th+1), tp); -} - /* * Construct a tcp options header for a SYN or SYN_ACK packet. * If this is every changed make sure to change the definition of @@ -651,31 +798,32 @@ static inline void tcp_build_header(struct tcphdr *th, struct sock *sk) * It would be especially magical to compute the checksum for this * stuff on the fly here. */ -extern __inline__ int tcp_syn_build_options(struct sk_buff *skb, int mss, int sack, int ts, int offer_wscale, int wscale) +extern __inline__ int tcp_syn_build_options(struct sk_buff *skb, int mss, int ts, int offer_wscale, int wscale) { - int count = 4 + (offer_wscale ? 4 : 0) + ((ts || sack) ? 4 : 0) + (ts ? 8 : 0); + int count = 4 + (offer_wscale ? TCPOLEN_WSCALE_ALIGNED : 0) + + ((ts) ? TCPOLEN_TSTAMP_ALIGNED : 0); unsigned char *optr = skb_put(skb,count); __u32 *ptr = (__u32 *)optr; - /* - * We always get an MSS option. + /* We always get an MSS option. + * The option bytes which will be seen in normal data + * packets should timestamps be used, must be in the MSS + * advertised. But we subtract them from sk->mss so + * that calculations in tcp_sendmsg are simpler etc. + * So account for this fact here if necessary. If we + * don't do this correctly, as a receiver we won't + * recognize data packets as being full sized when we + * should, and thus we won't abide by the delayed ACK + * rules correctly. */ + if(ts) + mss += TCPOLEN_TSTAMP_ALIGNED; *ptr++ = htonl((TCPOPT_MSS << 24) | (TCPOLEN_MSS << 16) | mss); if (ts) { - if (sack) { - *ptr++ = htonl((TCPOPT_SACK_PERM << 24) | (TCPOLEN_SACK_PERM << 16) - | (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP); - *ptr++ = htonl(jiffies); /* TSVAL */ - *ptr++ = htonl(0); /* TSECR */ - } else { - *ptr++ = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) - | (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP); - *ptr++ = htonl(jiffies); /* TSVAL */ - *ptr++ = htonl(0); /* TSECR */ - } - } else if (sack) { - *ptr++ = htonl((TCPOPT_SACK_PERM << 24) | (TCPOLEN_SACK_PERM << 16) - | (TCPOPT_NOP << 8) | TCPOPT_NOP); + *ptr++ = __constant_htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) | + (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP); + *ptr++ = htonl(jiffies); /* TSVAL */ + *ptr++ = __constant_htonl(0); /* TSECR */ } if (offer_wscale) *ptr++ = htonl((TCPOPT_WINDOW << 24) | (TCPOLEN_WINDOW << 16) | (wscale << 8)); @@ -724,33 +872,15 @@ extern __inline__ void tcp_select_initial_window(__u32 space, __u16 mss, (*window_clamp) = min(65535<<(*rcv_wscale),*window_clamp); } -#define SYNQ_DEBUG 1 - extern __inline__ void tcp_synq_unlink(struct tcp_opt *tp, struct open_request *req, struct open_request *prev) { -#ifdef SYNQ_DEBUG - if (prev->dl_next != req) { - printk(KERN_DEBUG "synq_unlink: bad prev ptr: %p\n",prev); - return; - } -#endif - if(!req->dl_next) { -#ifdef SYNQ_DEBUG - if (tp->syn_wait_last != (void*) req) - printk(KERN_DEBUG "synq_unlink: bad last ptr %p,%p\n", - req,tp->syn_wait_last); -#endif + if(!req->dl_next) tp->syn_wait_last = (struct open_request **)prev; - } prev->dl_next = req->dl_next; } extern __inline__ void tcp_synq_queue(struct tcp_opt *tp, struct open_request *req) { -#ifdef SYNQ_DEBUG - if (*tp->syn_wait_last != NULL) - printk("synq_queue: last ptr doesn't point to last req.\n"); -#endif req->dl_next = NULL; *tp->syn_wait_last = req; tp->syn_wait_last = &req->dl_next; @@ -765,14 +895,11 @@ extern __inline__ void tcp_synq_init(struct tcp_opt *tp) extern __inline__ struct open_request *tcp_synq_unlink_tail(struct tcp_opt *tp) { struct open_request *head = tp->syn_wait_queue; -#ifdef SYNQ_DEBUG - if (!head) { - printk(KERN_DEBUG "tail drop on empty queue? - bug\n"); - return NULL; - } -#endif +#if 0 + /* Should be a net-ratelimit'd thing, not all the time. */ printk(KERN_DEBUG "synq tail drop with expire=%ld\n", head->expires-jiffies); +#endif if (head->dl_next == NULL) tp->syn_wait_last = &tp->syn_wait_queue; tp->syn_wait_queue = head->dl_next; @@ -799,6 +926,17 @@ extern __inline__ void tcp_dec_slow_timer(int timer) atomic_dec(&slt->count); } +/* This needs to use a slow timer, so it is here. */ +static __inline__ void tcp_sk_unbindify(struct sock *sk) +{ + struct tcp_bind_bucket *tb = (struct tcp_bind_bucket *) sk->prev; + if(sk->bind_next) + sk->bind_next->bind_pprev = sk->bind_pprev; + *sk->bind_pprev = sk->bind_next; + if(tb->owners == NULL) + tcp_inc_slow_timer(TCP_SLT_BUCKETGC); +} + extern const char timer_bug_msg[]; static inline void tcp_clear_xmit_timer(struct sock *sk, int what) @@ -820,7 +958,8 @@ static inline void tcp_clear_xmit_timer(struct sock *sk, int what) printk(timer_bug_msg); return; }; - del_timer(timer); + if(timer->prev != NULL) + del_timer(timer); } static inline int tcp_timer_is_set(struct sock *sk, int what) @@ -829,13 +968,13 @@ static inline int tcp_timer_is_set(struct sock *sk, int what) switch (what) { case TIME_RETRANS: - return tp->retransmit_timer.next != NULL; + return tp->retransmit_timer.prev != NULL; break; case TIME_DACK: - return tp->delack_timer.next != NULL; + return tp->delack_timer.prev != NULL; break; case TIME_PROBE0: - return tp->probe_timer.next != NULL; + return tp->probe_timer.prev != NULL; break; default: printk(timer_bug_msg); diff --git a/init/main.c b/init/main.c index 85f56fda6..cc6657bbc 100644 --- a/init/main.c +++ b/init/main.c @@ -82,8 +82,6 @@ extern void dquot_init(void); extern void smp_setup(char *str, int *ints); extern void ioapic_pirq_setup(char *str, int *ints); extern void no_scroll(char *str, int *ints); -extern void swap_setup(char *str, int *ints); -extern void buff_setup(char *str, int *ints); extern void panic_setup(char *str, int *ints); extern void bmouse_setup(char *str, int *ints); extern void msmouse_setup(char *str, int *ints); @@ -282,7 +280,7 @@ extern void nfs_root_setup(char *str, int *ints); extern void ftape_setup(char *str, int *ints); #endif -#if defined(CONFIG_SYSVIPC) || defined(CONFIG_KERNELD) +#if defined(CONFIG_SYSVIPC) extern void ipc_init(void); #endif #ifdef CONFIG_MIPS_JAZZ @@ -502,8 +500,6 @@ static struct kernel_param cooked_params[] __initdata = { #if defined (CONFIG_AMIGA) || defined (CONFIG_ATARI) { "video=", video_setup }, #endif - { "swap=", swap_setup }, - { "buff=", buff_setup }, { "panic=", panic_setup }, { "console=", console_setup }, #ifdef CONFIG_VT @@ -1083,7 +1079,7 @@ __initfunc(asmlinkage void start_kernel(void)) inode_init(); file_table_init(); sock_init(); -#if defined(CONFIG_SYSVIPC) || defined(CONFIG_KERNELD) +#if defined(CONFIG_SYSVIPC) ipc_init(); #endif dquot_init(); @@ -1205,6 +1201,13 @@ static int init(void * unused) } #endif +#ifdef CONFIG_KMOD + { + extern int kmod_init(void); + kmod_init(); + } +#endif + setup(1); if (open("/dev/console", O_RDWR, 0) < 0) diff --git a/ipc/Makefile b/ipc/Makefile index 424052017..4e947582b 100644 --- a/ipc/Makefile +++ b/ipc/Makefile @@ -10,10 +10,6 @@ O_TARGET := ipc.o O_OBJS := util.o -ifdef CONFIG_KERNELD -CONFIG_SYSVIPC=1 -endif - ifdef CONFIG_SYSVIPC O_OBJS += msg.o sem.o shm.o endif @@ -1,19 +1,13 @@ /* * linux/ipc/msg.c * Copyright (C) 1992 Krishna Balasubramanian - * - * Kerneld extensions by Bjorn Ekwall <bj0rn@blox.se> in May 1995, and May 1996 - * - * See <linux/kerneld.h> for the (optional) new kerneld protocol */ -#include <linux/config.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/msg.h> #include <linux/stat.h> #include <linux/malloc.h> -#include <linux/kerneld.h> #include <linux/interrupt.h> #include <linux/smp.h> #include <linux/smp_lock.h> @@ -34,11 +28,6 @@ static unsigned short msg_seq = 0; static int used_queues = 0; static int max_msqid = 0; static struct wait_queue *msg_lock = NULL; -static int kerneld_msqid = -1; - -#define MAX_KERNELDS 20 -static int kerneld_arr[MAX_KERNELDS]; -static int n_kernelds = 0; __initfunc(void msg_init (void)) { @@ -51,39 +40,6 @@ __initfunc(void msg_init (void)) return; } -/* - * If the send queue is full, try to free any old messages. - * These are most probably unwanted, since no one has picked them up... - */ -#define MSG_FLUSH_TIME 10 /* seconds */ -static void flush_msg(struct msqid_ds *msq) -{ - struct msg *nmsg; - unsigned long flags; - int flushed = 0; - - save_flags(flags); - cli(); - - /* messages were put on the queue in time order */ - while ( (nmsg = msq->msg_first) && - ((CURRENT_TIME - nmsg->msg_stime) > MSG_FLUSH_TIME)) { - msgbytes -= nmsg->msg_ts; - msghdrs--; - msq->msg_cbytes -= nmsg->msg_ts; - msq->msg_qnum--; - msq->msg_first = nmsg->msg_next; - ++flushed; - kfree(nmsg); - } - - if (msq->msg_qnum == 0) - msq->msg_first = msq->msg_last = NULL; - restore_flags(flags); - if (flushed) - printk(KERN_WARNING "flushed %d old SYSVIPC messages", flushed); -} - static int real_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg) { int id, err; @@ -97,20 +53,12 @@ static int real_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg return -EINVAL; if (!msgp) return -EFAULT; - /* - * Calls from kernel level (IPC_KERNELD set) - * have the message somewhere in kernel space already! - */ - if ((msgflg & IPC_KERNELD)) - mtype = msgp->mtype; - else { - err = verify_area (VERIFY_READ, msgp->mtext, msgsz); - if (err) - return err; - get_user(mtype, &msgp->mtype); - if (mtype < 1) - return -EINVAL; - } + err = verify_area (VERIFY_READ, msgp->mtext, msgsz); + if (err) + return err; + get_user(mtype, &msgp->mtype); + if (mtype < 1) + return -EINVAL; id = (unsigned int) msqid % MSGMNI; msq = msgque [id]; if (msq == IPC_UNUSED || msq == IPC_NOID) @@ -120,29 +68,17 @@ static int real_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg slept: if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) return -EIDRM; - /* - * Non-root kernel level processes may send to kerneld! - * i.e. no permission check if called from the kernel - * otoh we don't want user level non-root snoopers... - */ - if ((msgflg & IPC_KERNELD) == 0) - if (ipcperms(ipcp, S_IWUGO)) - return -EACCES; + + if (ipcperms(ipcp, S_IWUGO)) + return -EACCES; if (msgsz + msq->msg_cbytes > msq->msg_qbytes) { - if ((kerneld_msqid != -1) && (kerneld_msqid == msqid)) - flush_msg(msq); /* flush the kerneld channel only */ if (msgsz + msq->msg_cbytes > msq->msg_qbytes) { /* still no space in queue */ if (msgflg & IPC_NOWAIT) return -EAGAIN; if (signal_pending(current)) return -EINTR; - if (in_interrupt()) { - /* Very unlikely, but better safe than sorry */ - printk(KERN_WARNING "Ouch, kerneld:msgsnd buffers full!\n"); - return -EINTR; - } interruptible_sleep_on (&msq->wwait); goto slept; } @@ -154,22 +90,7 @@ static int real_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg return -ENOMEM; msgh->msg_spot = (char *) (msgh + 1); - /* - * Calls from kernel level (IPC_KERNELD set) - * have the message somewhere in kernel space already! - */ - if (msgflg & IPC_KERNELD) { - struct kerneld_msg *kdmp = (struct kerneld_msg *)msgp; - - /* - * Note that the kernel supplies a pointer - * but the user-level kerneld uses a char array... - */ - memcpy(msgh->msg_spot, (char *)(&(kdmp->id)), KDHDR); - memcpy(msgh->msg_spot + KDHDR, kdmp->text, msgsz - KDHDR); - } - else - copy_from_user (msgh->msg_spot, msgp->mtext, msgsz); + copy_from_user (msgh->msg_spot, msgp->mtext, msgsz); if (msgque[id] == IPC_UNUSED || msgque[id] == IPC_NOID || msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) { @@ -201,42 +122,8 @@ static int real_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg return 0; } -/* - * Take care of missing kerneld, especially in case of multiple daemons - */ -#define KERNELD_TIMEOUT 1 * (HZ) -#define DROP_TIMER del_timer(&kd_timer) -/*#define DROP_TIMER if ((msgflg & IPC_KERNELD) && kd_timer.next && kd_timer.prev) del_timer(&kd_timer)*/ - -static void kd_timeout(unsigned long msgid) -{ - struct msqid_ds *msq; - struct msg *tmsg; - unsigned long flags; - - msq = msgque [ (unsigned int) kerneld_msqid % MSGMNI ]; - if (msq == IPC_NOID || msq == IPC_UNUSED) - return; - - save_flags(flags); - cli(); - for (tmsg = msq->msg_first; tmsg; tmsg = tmsg->msg_next) - if (*(long *)(tmsg->msg_spot) == msgid) - break; - restore_flags(flags); - if (tmsg) { /* still there! */ - struct kerneld_msg kmsp = { msgid, NULL_KDHDR, "" }; - - printk(KERN_ALERT "Ouch, no kerneld for message %ld\n", msgid); - kmsp.id = -ENODEV; - real_msgsnd(kerneld_msqid, (struct msgbuf *)&kmsp, KDHDR, - S_IRUSR | S_IWUSR | IPC_KERNELD | MSG_NOERROR); - } -} - static int real_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, int msgflg) { - struct timer_list kd_timer = { NULL, NULL, 0, 0, 0}; struct msqid_ds *msq; struct ipc_perm *ipcp; struct msg *tmsg, *leastp = NULL; @@ -248,15 +135,10 @@ static int real_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz, long msgty return -EINVAL; if (!msgp || !msgp->mtext) return -EFAULT; - /* - * Calls from kernel level (IPC_KERNELD set) - * wants the message put in kernel space! - */ - if ((msgflg & IPC_KERNELD) == 0) { - err = verify_area (VERIFY_WRITE, msgp->mtext, msgsz); - if (err) - return err; - } + + err = verify_area (VERIFY_WRITE, msgp->mtext, msgsz); + if (err) + return err; id = (unsigned int) msqid % MSGMNI; msq = msgque [id]; @@ -264,16 +146,6 @@ static int real_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz, long msgty return -EINVAL; ipcp = &msq->msg_perm; - /* - * Start timer for missing kerneld - */ - if (msgflg & IPC_KERNELD) { - kd_timer.data = (unsigned long)msgtyp; - kd_timer.expires = jiffies + KERNELD_TIMEOUT; - kd_timer.function = kd_timeout; - add_timer(&kd_timer); - } - /* * find message of correct type. * msgtyp = 0 => get first. @@ -282,19 +154,10 @@ static int real_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz, long msgty */ while (!nmsg) { if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) { - DROP_TIMER; return -EIDRM; } - if ((msgflg & IPC_KERNELD) == 0) { - /* - * All kernel level processes may receive from kerneld! - * i.e. no permission check if called from the kernel - * otoh we don't want user level non-root snoopers... - */ - if (ipcperms (ipcp, S_IRUGO)) { - DROP_TIMER; /* Not needed, but doesn't hurt */ - return -EACCES; - } + if (ipcperms (ipcp, S_IRUGO)) { + return -EACCES; } save_flags(flags); @@ -326,7 +189,6 @@ static int real_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz, long msgty restore_flags(flags); if (nmsg) { /* done finding a message */ - DROP_TIMER; if ((msgsz < nmsg->msg_ts) && !(msgflg & MSG_NOERROR)) { return -E2BIG; } @@ -354,43 +216,20 @@ static int real_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz, long msgty msq->msg_cbytes -= nmsg->msg_ts; restore_flags(flags); wake_up (&msq->wwait); - /* - * Calls from kernel level (IPC_KERNELD set) - * wants the message copied to kernel space! - */ - if (msgflg & IPC_KERNELD) { - struct kerneld_msg *kdmp = (struct kerneld_msg *) msgp; - - memcpy((char *)(&(kdmp->id)), - nmsg->msg_spot, KDHDR); - /* - * Note that kdmp->text is a pointer - * when called from kernel space! - */ - if ((msgsz > KDHDR) && kdmp->text) - memcpy(kdmp->text, - nmsg->msg_spot + KDHDR, - msgsz - KDHDR); - } - else { - put_user (nmsg->msg_type, &msgp->mtype); - copy_to_user (msgp->mtext, nmsg->msg_spot, msgsz); - } + put_user (nmsg->msg_type, &msgp->mtype); + copy_to_user (msgp->mtext, nmsg->msg_spot, msgsz); kfree(nmsg); return msgsz; } else { /* did not find a message */ if (msgflg & IPC_NOWAIT) { - DROP_TIMER; return -ENOMSG; } if (signal_pending(current)) { - DROP_TIMER; return -EINTR; } interruptible_sleep_on (&msq->rwait); } } /* end while */ - DROP_TIMER; return -1; } @@ -398,9 +237,8 @@ asmlinkage int sys_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msg { int ret; - /* IPC_KERNELD is used as a marker for kernel level calls */ lock_kernel(); - ret = real_msgsnd(msqid, msgp, msgsz, msgflg & ~IPC_KERNELD); + ret = real_msgsnd(msqid, msgp, msgsz, msgflg); unlock_kernel(); return ret; } @@ -410,9 +248,8 @@ asmlinkage int sys_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz, { int ret; - /* IPC_KERNELD is used as a marker for kernel level calls */ lock_kernel(); - ret = real_msgrcv (msqid, msgp, msgsz, msgtyp, msgflg & ~IPC_KERNELD); + ret = real_msgrcv (msqid, msgp, msgsz, msgtyp, msgflg); unlock_kernel(); return ret; } @@ -479,36 +316,7 @@ asmlinkage int sys_msgget (key_t key, int msgflg) int id, ret = -EPERM; struct msqid_ds *msq; - /* - * If the IPC_KERNELD flag is set, the key is forced to IPC_PRIVATE, - * and a designated kerneld message queue is created/referred to - */ lock_kernel(); - if ((msgflg & IPC_KERNELD)) { - int i; - if (!suser()) - goto out; -#ifdef NEW_KERNELD_PROTOCOL - if ((msgflg & IPC_KERNELD) == OLDIPC_KERNELD) { - printk(KERN_ALERT "Please recompile your kerneld daemons!\n"); - goto out; - } -#endif - ret = -ENOSPC; - if ((kerneld_msqid == -1) && (kerneld_msqid = - newque(IPC_PRIVATE, msgflg & S_IRWXU)) < 0) - goto out; - for (i = 0; i < MAX_KERNELDS; ++i) { - if (kerneld_arr[i] == 0) { - kerneld_arr[i] = current->pid; - ++n_kernelds; - ret = kerneld_msqid; - goto out; - } - } - goto out; - } - /* else it is a "normal" request */ if (key == IPC_PRIVATE) ret = newque(key, msgflg); else if ((id = findkey (key)) == -1) { /* key not used */ @@ -527,7 +335,6 @@ asmlinkage int sys_msgget (key_t key, int msgflg) else ret = (unsigned int) msq->msg_perm.seq * MSGMNI + id; } -out: unlock_kernel(); return ret; } @@ -687,12 +494,7 @@ asmlinkage int sys_msgctl (int msqid, int cmd, struct msqid_ds *buf) if (current->euid != ipcp->cuid && current->euid != ipcp->uid && !suser()) goto out; - /* - * There is only one kerneld message queue, - * mark it as non-existent - */ - if ((kerneld_msqid >= 0) && (msqid == kerneld_msqid)) - kerneld_msqid = -1; + freeque (id); err = 0; goto out; @@ -705,104 +507,3 @@ out: return err; } -/* - * We do perhaps need a "flush" for waiting processes, - * so that if they are terminated, a call from do_exit - * will minimize the possibility of orphaned received - * messages in the queue. For now we just make sure - * that the queue is shut down whenever all kernelds have died. - */ -void kerneld_exit(void) -{ - int i; - - if (kerneld_msqid == -1) - return; - for (i = 0; i < MAX_KERNELDS; ++i) { - if (kerneld_arr[i] == current->pid) { - kerneld_arr[i] = 0; - --n_kernelds; - if (n_kernelds == 0) - sys_msgctl(kerneld_msqid, IPC_RMID, NULL); - break; - } - } -} - -/* - * Kerneld internal message format/syntax: - * - * The message type from the kernel to kerneld is used to specify _what_ - * function we want kerneld to perform. - * - * The "normal" message area is divided into a header, followed by a char array. - * The header is used to hold the sequence number of the request, which will - * be used as the return message type from kerneld back to the kernel. - * In the return message, the header will be used to store the exit status - * of the kerneld "job", or task. - * The character array is used to pass parameters to kerneld and (optional) - * return information from kerneld back to the kernel. - * It is the responsibility of kerneld and the kernel level caller - * to set usable sizes on the parameter/return value array, since - * that information is _not_ included in the message format - */ - -/* - * The basic kernel level entry point to kerneld. - * msgtype should correspond to a task type for (a) kerneld - * ret_size is the size of the (optional) return _value, - * OR-ed with KERNELD_WAIT if we want an answer - * msgsize is the size (in bytes) of the message, not including - * the header that is always sent first in a kerneld message - * text is the parameter for the kerneld specific task - * ret_val is NULL or the kernel address where an expected answer - * from kerneld should be placed. - * - * See <linux/kerneld.h> for usage (inline convenience functions) - * - */ -int kerneld_send(int msgtype, int ret_size, int msgsz, - const char *text, const char *ret_val) -{ - int status = -ENOSYS; -#ifdef CONFIG_KERNELD - static int id = KERNELD_MINSEQ; - struct kerneld_msg kmsp = { msgtype, NULL_KDHDR, (char *)text }; - int msgflg = S_IRUSR | S_IWUSR | IPC_KERNELD | MSG_NOERROR; - unsigned long flags; - - if (kerneld_msqid == -1) - return -ENODEV; - - /* Do not wait for an answer at interrupt-time! */ - if (in_interrupt()) - ret_size &= ~KERNELD_WAIT; -#ifdef NEW_KERNELD_PROTOCOL - else - kmsp.pid = current->pid; -#endif - - msgsz += KDHDR; - if (ret_size & KERNELD_WAIT) { - save_flags(flags); - cli(); - if (++id <= 0) /* overflow */ - id = KERNELD_MINSEQ; - kmsp.id = id; - restore_flags(flags); - } - - status = real_msgsnd(kerneld_msqid, (struct msgbuf *)&kmsp, msgsz, msgflg); - if ((status >= 0) && (ret_size & KERNELD_WAIT)) { - ret_size &= ~KERNELD_WAIT; - kmsp.text = (char *)ret_val; - status = real_msgrcv(kerneld_msqid, (struct msgbuf *)&kmsp, - KDHDR + ((ret_val)?ret_size:0), - kmsp.id, msgflg); - if (status > 0) /* a valid answer contains at least a long */ - status = kmsp.id; - } - -#endif /* CONFIG_KERNELD */ - return status; -} diff --git a/ipc/util.c b/ipc/util.c index f0ba7fedd..eacea2b62 100644 --- a/ipc/util.c +++ b/ipc/util.c @@ -20,7 +20,7 @@ #include <asm/uaccess.h> -#if defined(CONFIG_SYSVIPC) || defined(CONFIG_KERNELD) +#if defined(CONFIG_SYSVIPC) extern void sem_init (void), msg_init (void), shm_init (void); @@ -123,7 +123,4 @@ asmlinkage int sys_shmctl (int shmid, int cmd, struct shmid_ds *buf) return -ENOSYS; } -void kerneld_exit(void) -{ -} #endif /* CONFIG_SYSVIPC */ diff --git a/kernel/Makefile b/kernel/Makefile index ff908f68a..4e0a1d87d 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -17,6 +17,10 @@ O_OBJS = sched.o dma.o fork.o exec_domain.o panic.o printk.o sys.o \ OX_OBJS += signal.o +ifeq ($(CONFIG_KMOD),y) +O_OBJS += kmod.o +endif + ifeq ($(CONFIG_MODULES),y) OX_OBJS += ksyms.o endif diff --git a/kernel/exit.c b/kernel/exit.c index 9824f5806..2d5835ac8 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -27,7 +27,6 @@ #include <asm/mmu_context.h> extern void sem_exit (void); -extern void kerneld_exit(void); int getrusage(struct task_struct *, int, struct rusage *); @@ -44,8 +43,14 @@ void release(struct task_struct * p) charge_uid(p, -1); nr_tasks--; add_free_taskslot(p->tarray_ptr); - unhash_pid(p); - REMOVE_LINKS(p); + { + unsigned long flags; + + write_lock_irqsave(&tasklist_lock, flags); + unhash_pid(p); + REMOVE_LINKS(p); + write_unlock_irqrestore(&tasklist_lock, flags); + } release_thread(p); current->cmin_flt += p->min_flt + p->cmin_flt; current->cmaj_flt += p->maj_flt + p->cmaj_flt; @@ -157,7 +162,7 @@ static inline void close_files(struct files_struct * files) unsigned long set = files->open_fds.fds_bits[j]; i = j * __NFDBITS; j++; - if (i >= NR_OPEN) + if (i >= files->max_fds) break; while (set) { if (set & 1) { @@ -183,6 +188,13 @@ static inline void __exit_files(struct task_struct *tsk) tsk->files = NULL; if (!--files->count) { close_files(files); + /* + * Free the fd array as appropriate ... + */ + if (NR_OPEN * sizeof(struct file *) == PAGE_SIZE) + free_page((unsigned long) files->fd); + else + kfree(files->fd); kmem_cache_free(files_cachep, files); } } @@ -328,7 +340,6 @@ fake_volatile: acct_process(code); del_timer(¤t->real_timer); sem_exit(); - kerneld_exit(); __exit_mm(current); #if CONFIG_AP1000 exit_msc(current); diff --git a/kernel/fork.c b/kernel/fork.c index 38c98b0a8..a08aa2c64 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -41,7 +41,6 @@ kmem_cache_t *mm_cachep; kmem_cache_t *files_cachep; struct task_struct *pidhash[PIDHASH_SZ]; -spinlock_t pidhash_lock = SPIN_LOCK_UNLOCKED; struct task_struct **tarray_freelist = NULL; spinlock_t taskslot_lock = SPIN_LOCK_UNLOCKED; @@ -263,6 +262,9 @@ fail_nomem: /* * Allocate and initialize an mm_struct. + * + * NOTE! The mm mutex will be locked until the + * caller decides that all systems are go.. */ struct mm_struct * mm_alloc(void) { @@ -275,7 +277,7 @@ struct mm_struct * mm_alloc(void) mm->count = 1; mm->map_count = 0; mm->def_flags = 0; - mm->mmap_sem = MUTEX; + mm->mmap_sem = MUTEX_LOCKED; /* * Leave mm->pgd set to the parent's pgd * so that pgd_offset() is always valid. @@ -328,6 +330,7 @@ static inline int copy_mm(unsigned long clone_flags, struct task_struct * tsk) retval = dup_mmap(mm); if (retval) goto free_pt; + up(&mm->mmap_sem); return 0; free_mm: @@ -375,44 +378,66 @@ static inline int copy_fdset(fd_set *dst, fd_set *src) return __copy_fdset(dst->fds_bits, src->fds_bits); } -static inline int copy_files(unsigned long clone_flags, struct task_struct * tsk) +static int copy_files(unsigned long clone_flags, struct task_struct * tsk) { - int i; struct files_struct *oldf, *newf; struct file **old_fds, **new_fds; + int size, i, error = 0; /* * A background process may not have any files ... */ oldf = current->files; if (!oldf) - return 0; + goto out; if (clone_flags & CLONE_FILES) { oldf->count++; - return 0; + goto out; } + tsk->files = NULL; + error = -ENOMEM; newf = kmem_cache_alloc(files_cachep, SLAB_KERNEL); - tsk->files = newf; if (!newf) - return -1; + goto out; + + /* + * Allocate the fd array, using get_free_page() if possible. + * Eventually we want to make the array size variable ... + */ + size = NR_OPEN * sizeof(struct file *); + if (size == PAGE_SIZE) + new_fds = (struct file **) __get_free_page(GFP_KERNEL); + else + new_fds = (struct file **) kmalloc(size, GFP_KERNEL); + if (!new_fds) + goto out_release; + memset((void *) new_fds, 0, size); newf->count = 1; + newf->max_fds = NR_OPEN; + newf->fd = new_fds; newf->close_on_exec = oldf->close_on_exec; - i = copy_fdset(&newf->open_fds,&oldf->open_fds); + i = copy_fdset(&newf->open_fds, &oldf->open_fds); old_fds = oldf->fd; - new_fds = newf->fd; for (; i != 0; i--) { struct file * f = *old_fds; old_fds++; *new_fds = f; - new_fds++; if (f) f->f_count++; + new_fds++; } - return 0; + tsk->files = newf; + error = 0; +out: + return error; + +out_release: + kmem_cache_free(files_cachep, newf); + goto out; } static inline int copy_sighand(unsigned long clone_flags, struct task_struct * tsk) @@ -495,8 +520,15 @@ int do_fork(unsigned long clone_flags, unsigned long usp, struct pt_regs *regs) p->start_time = jiffies; p->tarray_ptr = &task[nr]; *p->tarray_ptr = p; - SET_LINKS(p); - hash_pid(p); + + { + unsigned long flags; + write_lock_irqsave(&tasklist_lock, flags); + SET_LINKS(p); + hash_pid(p); + write_unlock_irqrestore(&tasklist_lock, flags); + } + nr_tasks++; error = -ENOMEM; @@ -553,8 +585,15 @@ bad_fork_cleanup: if (p->binfmt && p->binfmt->module) __MOD_DEC_USE_COUNT(p->binfmt->module); add_free_taskslot(p->tarray_ptr); - unhash_pid(p); - REMOVE_LINKS(p); + + { + unsigned long flags; + write_lock_irqsave(&tasklist_lock, flags); + unhash_pid(p); + REMOVE_LINKS(p); + write_unlock_irqrestore(&tasklist_lock, flags); + } + nr_tasks--; bad_fork_free: free_task_struct(p); diff --git a/kernel/kmod.c b/kernel/kmod.c new file mode 100644 index 000000000..a0f58d485 --- /dev/null +++ b/kernel/kmod.c @@ -0,0 +1,149 @@ +/* + kmod, the new module loader (replaces kerneld) + Kirk Petersen +*/ + +#define __KERNEL_SYSCALLS__ + +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/unistd.h> + +static inline _syscall1(int,delete_module,const char *,name_user) + +/* + kmod_unload_delay and modprobe_path are set via /proc/sys. +*/ +int kmod_unload_delay = 60; +char modprobe_path[256] = "/sbin/modprobe"; +char module_name[64] = ""; +char * argv[] = { "modprobe", "-k", NULL, NULL, }; +char * envp[] = { "HOME=/", "TERM=linux", NULL, }; + +/* + kmod_queue synchronizes the kmod thread and the rest of the system + kmod_unload_timer is what we use to unload modules + after kmod_unload_delay seconds +*/ +struct wait_queue * kmod_queue = NULL; +struct timer_list kmod_unload_timer; + +/* + kmod_thread is the thread that does most of the work. kmod_unload and + request_module tell it to wake up and do work. +*/ +int kmod_thread(void * data) +{ + int pid; + + /* + Initialize basic thread information + */ + current->session = 1; + current->pgrp = 1; + sprintf(current->comm, "kmod"); + sigfillset(¤t->blocked); + + /* + This is the main kmod_thread loop. It first sleeps, then + handles requests from request_module or kmod_unload. + */ + + while (1) { + interruptible_sleep_on(&kmod_queue); + + /* + If request_module woke us up, we should try to + load module_name. If not, kmod_unload woke us up, + do call delete_module. + (if somehow both want us to do something, ignore the + delete_module request) + */ + if (module_name[0] == '\0') { + delete_module(NULL); + } else { + pid = fork(); + if (pid > 0) { + waitpid(pid, NULL, 0); + module_name[0] = '\0'; + wake_up(&kmod_queue); + } else + if (pid == 0) { + + /* + Call modprobe with module_name. If execve returns, + print out an error. + */ + argv[2] = module_name; + execve(modprobe_path, argv, envp); + + printk("kmod: failed to load module %s\n", module_name); + _exit(0); + } else { + printk("error, fork failed in kmod\n"); + } + } + } + + return 0; /* Never reached. */ +} + +/* + kmod_unload is the function that the kernel calls when + the kmod_unload_timer expires +*/ +void kmod_unload(unsigned long x) +{ + /* + wake up the kmod thread, which does the work + (we can't call delete_module, as it locks the kernel and + we are in the bottom half of the kernel (right?)) + once it is awake, reset the timer + */ + wake_up(&kmod_queue); + kmod_unload_timer.expires = jiffies + (kmod_unload_delay * HZ); + add_timer(&kmod_unload_timer); +} + +int kmod_init(void) +{ + printk ("Starting kmod\n"); + + kernel_thread(kmod_thread, NULL, 0); + + kmod_unload_timer.next = NULL; + kmod_unload_timer.prev = NULL; + kmod_unload_timer.expires = jiffies + (5 * 60 * HZ); + kmod_unload_timer.data = 0L; + kmod_unload_timer.function = kmod_unload; + add_timer(&kmod_unload_timer); + + return 0; +} + +/* + request_module, the function that everyone calls when they need a + module to be loaded +*/ +int request_module(const char * name) +{ + /* first, copy the name of the module into module_name */ + /* then wake_up() the kmod daemon */ + /* wait for the kmod daemon to finish (it will wake us up) */ + + /* + kmod_thread is sleeping, so start by copying the name of + the module into module_name. Once that is done, wake up + kmod_thread. + */ + strcpy(module_name, name); + wake_up(&kmod_queue); + + /* + Now that we have told kmod_thread what to do, we want to + go to sleep and let it do its work. It will wake us up, + at which point we will be done (the module will be loaded). + */ + interruptible_sleep_on(&kmod_queue); + return 0; +} diff --git a/kernel/ksyms.c b/kernel/ksyms.c index 869e5e5bb..7ff40d7bd 100644 --- a/kernel/ksyms.c +++ b/kernel/ksyms.c @@ -61,8 +61,8 @@ extern unsigned char aux_device_present, kbd_read_mask; #if defined(CONFIG_PROC_FS) #include <linux/proc_fs.h> #endif -#ifdef CONFIG_KERNELD -#include <linux/kerneld.h> +#ifdef CONFIG_KMOD +#include <linux/kmod.h> #endif #include <asm/irq.h> #ifdef __SMP__ @@ -91,12 +91,13 @@ __attribute__((section("__ksymtab"))) = { #endif +#ifdef CONFIG_KMOD +EXPORT_SYMBOL(request_module); +#endif + #ifdef CONFIG_MODULES EXPORT_SYMBOL(get_module_symbol); #endif -#ifdef CONFIG_KERNELD -EXPORT_SYMBOL(kerneld_send); -#endif EXPORT_SYMBOL(get_options); #ifdef CONFIG_PCI diff --git a/kernel/module.c b/kernel/module.c index efee5902e..90f0bf1a2 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -324,7 +324,7 @@ sys_init_module(const char *name_user, struct module *mod_user) dep->next_ref = d->refs; d->refs = dep; /* Being referenced by a dependant module counts as a - use as far as kerneld is concerned. */ + use as far as kmod is concerned. */ d->flags |= MOD_USED_ONCE; } diff --git a/kernel/sched.c b/kernel/sched.c index f48f520ff..a86cb0413 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -155,9 +155,9 @@ static inline void move_last_runqueue(struct task_struct * p) * The run-queue lock locks the parts that actually access * and change the run-queues, and have to be interrupt-safe. */ -rwlock_t tasklist_lock = RW_LOCK_UNLOCKED; -spinlock_t scheduler_lock = SPIN_LOCK_UNLOCKED; -spinlock_t runqueue_lock = SPIN_LOCK_UNLOCKED; +spinlock_t scheduler_lock = SPIN_LOCK_UNLOCKED; /* should be aquired first */ +spinlock_t runqueue_lock = SPIN_LOCK_UNLOCKED; /* second */ +rwlock_t tasklist_lock = RW_LOCK_UNLOCKED; /* third */ /* * Wake up a process. Put it on the run-queue if it's not @@ -201,14 +201,20 @@ static void process_timeout(unsigned long __data) */ static inline int goodness(struct task_struct * p, struct task_struct * prev, int this_cpu) { + int policy = p->policy; int weight; + if (policy & SCHED_YIELD) { + p->policy = policy & ~SCHED_YIELD; + return 0; + } + /* * Realtime process, select the first one on the * runqueue (taking priorities within processes * into account). */ - if (p->policy != SCHED_OTHER) + if (policy != SCHED_OTHER) return 1000 + p->rt_priority; /* @@ -228,9 +234,10 @@ static inline int goodness(struct task_struct * p, struct task_struct * prev, in weight += PROC_CHANGE_PENALTY; #endif - /* .. and a slight advantage to the current process */ - if (p == prev) + /* .. and a slight advantage to the current thread */ + if (p->mm == prev->mm) weight += 1; + weight += p->priority; } return weight; @@ -1253,10 +1260,11 @@ asmlinkage int sys_nice(int increment) static inline struct task_struct *find_process_by_pid(pid_t pid) { + struct task_struct *tsk = current; + if (pid) - return find_task_by_pid(pid); - else - return current; + tsk = find_task_by_pid(pid); + return tsk; } static int setscheduler(pid_t pid, int policy, @@ -1264,48 +1272,70 @@ static int setscheduler(pid_t pid, int policy, { struct sched_param lp; struct task_struct *p; + int retval; + retval = -EINVAL; if (!param || pid < 0) - return -EINVAL; + goto out_nounlock; + retval = -EFAULT; if (copy_from_user(&lp, param, sizeof(struct sched_param))) - return -EFAULT; + goto out_nounlock; + + /* + * We play safe to avoid deadlocks. + */ + spin_lock_irq(&scheduler_lock); + spin_lock(&runqueue_lock); + read_lock(&tasklist_lock); p = find_process_by_pid(pid); + + retval = -ESRCH; if (!p) - return -ESRCH; + goto out_unlock; if (policy < 0) policy = p->policy; - else if (policy != SCHED_FIFO && policy != SCHED_RR && - policy != SCHED_OTHER) - return -EINVAL; + else { + retval = -EINVAL; + if (policy != SCHED_FIFO && policy != SCHED_RR && + policy != SCHED_OTHER) + goto out_unlock; + } /* * Valid priorities for SCHED_FIFO and SCHED_RR are 1..99, valid * priority for SCHED_OTHER is 0. */ + retval = -EINVAL; if (lp.sched_priority < 0 || lp.sched_priority > 99) - return -EINVAL; + goto out_unlock; if ((policy == SCHED_OTHER) != (lp.sched_priority == 0)) - return -EINVAL; + goto out_unlock; + retval = -EPERM; if ((policy == SCHED_FIFO || policy == SCHED_RR) && !suser()) - return -EPERM; + goto out_unlock; if ((current->euid != p->euid) && (current->euid != p->uid) && !suser()) - return -EPERM; + goto out_unlock; + retval = 0; p->policy = policy; p->rt_priority = lp.sched_priority; - spin_lock(&scheduler_lock); - spin_lock_irq(&runqueue_lock); if (p->next_run) move_last_runqueue(p); - spin_unlock_irq(&runqueue_lock); - spin_unlock(&scheduler_lock); + need_resched = 1; - return 0; + +out_unlock: + read_unlock(&tasklist_lock); + spin_unlock(&runqueue_lock); + spin_unlock_irq(&scheduler_lock); + +out_nounlock: + return retval; } asmlinkage int sys_sched_setscheduler(pid_t pid, int policy, @@ -1322,42 +1352,64 @@ asmlinkage int sys_sched_setparam(pid_t pid, struct sched_param *param) asmlinkage int sys_sched_getscheduler(pid_t pid) { struct task_struct *p; + int retval; + retval = -EINVAL; if (pid < 0) - return -EINVAL; + goto out_nounlock; + read_lock(&tasklist_lock); + + retval = -ESRCH; p = find_process_by_pid(pid); if (!p) - return -ESRCH; + goto out_unlock; - return p->policy; + retval = p->policy; + +out_unlock: + read_unlock(&tasklist_lock); + +out_nounlock: + return retval; } asmlinkage int sys_sched_getparam(pid_t pid, struct sched_param *param) { struct task_struct *p; struct sched_param lp; + int retval; + retval = -EINVAL; if (!param || pid < 0) - return -EINVAL; + goto out_nounlock; + read_lock(&tasklist_lock); p = find_process_by_pid(pid); + retval = -ESRCH; if (!p) - return -ESRCH; - + goto out_unlock; lp.sched_priority = p->rt_priority; - return copy_to_user(param, &lp, sizeof(struct sched_param)) ? -EFAULT : 0; + read_unlock(&tasklist_lock); + + /* + * This one might sleep, we cannot do it with a spinlock held ... + */ + retval = copy_to_user(param, &lp, sizeof(*param)) ? -EFAULT : 0; + +out_nounlock: + return retval; + +out_unlock: + read_unlock(&tasklist_lock); + return retval; } asmlinkage int sys_sched_yield(void) { - /* - * This is not really right. We'd like to reschedule - * just _once_ with this process having a zero count. - */ - current->counter = 0; spin_lock(&scheduler_lock); spin_lock_irq(&runqueue_lock); + current->policy |= SCHED_YIELD; move_last_runqueue(current); spin_unlock_irq(&runqueue_lock); spin_unlock(&scheduler_lock); diff --git a/kernel/signal.c b/kernel/signal.c index 53228eb31..c313b0a11 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -438,8 +438,16 @@ kill_sl_info(int sig, struct siginfo *info, pid_t sess) inline int kill_proc_info(int sig, struct siginfo *info, pid_t pid) { - struct task_struct *p = find_task_by_pid(pid); - return p ? send_sig_info(sig, info, p) : -ESRCH; + int error; + struct task_struct *p; + + read_lock(&tasklist_lock); + p = find_task_by_pid(pid); + error = -ESRCH; + if (p) + error = send_sig_info(sig, info, p); + read_unlock(&tasklist_lock); + return error; } /* diff --git a/kernel/sys.c b/kernel/sys.c index 1d8356de0..e86d18c09 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -578,14 +578,16 @@ asmlinkage int sys_setpgid(pid_t pid, pid_t pgid) if (pgid < 0) return -EINVAL; - if((p = find_task_by_pid(pid)) == NULL) - return -ESRCH; - /* From this point forward we keep holding onto the tasklist lock * so that our parent does not change from under us. -DaveM */ read_lock(&tasklist_lock); + err = -ESRCH; + p = find_task_by_pid(pid); + if (!p) + goto out; + if (p->p_pptr == current || p->p_opptr == current) { err = -EPERM; if (p->session != current->session) @@ -622,12 +624,17 @@ asmlinkage int sys_getpgid(pid_t pid) if (!pid) { return current->pgrp; } else { - struct task_struct *p = find_task_by_pid(pid); + int retval; + struct task_struct *p; - if(p) - return p->pgrp; - else - return -ESRCH; + read_lock(&tasklist_lock); + p = find_task_by_pid(pid); + + retval = -ESRCH; + if (p) + retval = p->pgrp; + read_unlock(&tasklist_lock); + return retval; } } @@ -642,12 +649,17 @@ asmlinkage int sys_getsid(pid_t pid) if (!pid) { return current->session; } else { - struct task_struct *p = find_task_by_pid(pid); + int retval; + struct task_struct *p; + read_lock(&tasklist_lock); + p = find_task_by_pid(pid); + + retval = -ESRCH; if(p) - return p->session; - else - return -ESRCH; + retval = p->session; + read_unlock(&tasklist_lock); + return retval; } } diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 1b93ad7bd..e6864541f 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -41,6 +41,10 @@ extern int console_loglevel, C_A_D, swapout_interval; extern int bdf_prm[], bdflush_min[], bdflush_max[]; extern char binfmt_java_interpreter[], binfmt_java_appletviewer[]; extern int sysctl_overcommit_memory; +#ifdef CONFIG_KMOD +extern char modprobe_path[]; +extern int kmod_unload_delay; +#endif #ifdef __sparc__ extern char reboot_command []; @@ -174,6 +178,12 @@ static ctl_table kern_table[] = { 0644, NULL, &proc_dointvec}, {KERN_PRINTK, "printk", &console_loglevel, 4*sizeof(int), 0644, NULL, &proc_dointvec}, +#ifdef CONFIG_KMOD + {KERN_MODPROBE, "modprobe", &modprobe_path, 256, + 0644, NULL, &proc_dostring, &sysctl_string }, + {KERN_KMOD_UNLOAD_DELAY, "kmod_unload_delay", &kmod_unload_delay, + sizeof(int), 0644, NULL, &proc_dointvec}, +#endif {0} }; @@ -183,12 +193,14 @@ static ctl_table vm_table[] = { {VM_SWAPOUT, "swapout_interval", &swapout_interval, sizeof(int), 0600, NULL, &proc_dointvec_jiffies}, {VM_FREEPG, "freepages", - &min_free_pages, 3*sizeof(int), 0600, NULL, &proc_dointvec}, + &freepages, sizeof(freepages_t), 0600, NULL, &proc_dointvec}, {VM_BDFLUSH, "bdflush", &bdf_prm, 9*sizeof(int), 0600, NULL, &proc_dointvec_minmax, &sysctl_intvec, NULL, &bdflush_min, &bdflush_max}, {VM_OVERCOMMIT_MEMORY, "overcommit_memory", &sysctl_overcommit_memory, sizeof(sysctl_overcommit_memory), 0644, NULL, &proc_dointvec}, + {VM_BUFFERMEM, "buffermem", + &buffer_mem, sizeof(buffer_mem_t), 0600, NULL, &proc_dointvec}, {0} }; diff --git a/mm/page_alloc.c b/mm/page_alloc.c index ed748bbfb..a3b1c0e8c 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -331,9 +331,9 @@ __initfunc(unsigned long free_area_init(unsigned long start_mem, unsigned long e i = (end_mem - PAGE_OFFSET) >> (PAGE_SHIFT+7); if (i < 48) i = 48; - min_free_pages = i; - free_pages_low = i + (i>>1); - free_pages_high = i + i; + freepages.min = i; + freepages.low = i + (i>>1); + freepages.high = i + i; mem_map = (mem_map_t *) LONG_ALIGN(start_mem); p = mem_map + MAP_NR(end_mem); start_mem = LONG_ALIGN((unsigned long) p); @@ -1824,7 +1824,9 @@ next: } spin_lock_irq(&best_cachep->c_spinlock); - if (!best_cachep->c_growing && !(slabp = best_cachep->c_lastp)->s_inuse && slabp != kmem_slab_end(best_cachep)) { + while (!best_cachep->c_growing && + !(slabp = best_cachep->c_lastp)->s_inuse && + slabp != kmem_slab_end(best_cachep)) { if (gfp_mask & GFP_DMA) { do { if (slabp->s_dma) @@ -1848,7 +1850,7 @@ good_dma: */ spin_unlock_irq(&best_cachep->c_spinlock); kmem_slab_destroy(best_cachep, slabp); - return; + spin_lock_irq(&best_cachep->c_spinlock); } dma_fail: spin_unlock_irq(&best_cachep->c_spinlock); @@ -5,10 +5,12 @@ */ /* - * This file should contain most things doing the swapping from/to disk. + * This file contains the default values for the opereation of the + * Linux VM subsystem. Finetuning documentation can be found in + * linux/Documentation/sysctl/vm.txt. * Started 18.12.91 - * * Swap aging added 23.2.95, Stephen Tweedie. + * Buffermem limits added 12.3.98, Rik van Riel. */ #include <linux/mm.h> @@ -33,15 +35,18 @@ /* * We identify three levels of free memory. We never let free mem - * fall below the min_free_pages except for atomic allocations. We - * start background swapping if we fall below free_pages_high free - * pages, and we begin intensive swapping below free_pages_low. + * fall below the freepages.min except for atomic allocations. We + * start background swapping if we fall below freepages.high free + * pages, and we begin intensive swapping below freepages.low. * - * Keep these three variables contiguous for sysctl(2). + * These values are there to keep GCC from complaining. Actual + * initialization is done in mm/page_alloc.c or arch/sparc(64)/mm/init.c. */ -int min_free_pages = 48; -int free_pages_low = 72; -int free_pages_high = 96; +freepages_t freepages = { + 48, /* freepages.min */ + 72, /* freepages.low */ + 96 /* freepages.high */ +}; /* We track the number of pages currently being asynchronously swapped out, so that we don't try to swap TOO many pages out at once */ @@ -55,53 +60,15 @@ atomic_t nr_async_pages = ATOMIC_INIT(0); swap_control_t swap_control = { 20, 3, 1, 3, /* Page aging */ - 10, 2, 2, 4, /* Buffer aging */ 32, 4, /* Aging cluster */ 8192, 8192, /* Pageout and bufferout weights */ - -200, /* Buffer grace */ - 1, 1, /* Buffs/pages to free */ - RCL_ROUND_ROBIN /* Balancing policy */ }; swapstat_t swapstats = {0}; -/* General swap control */ - -/* Parse the kernel command line "swap=" option at load time: */ -__initfunc(void swap_setup(char *str, int *ints)) -{ - int * swap_vars[8] = { - &MAX_PAGE_AGE, - &PAGE_ADVANCE, - &PAGE_DECLINE, - &PAGE_INITIAL_AGE, - &AGE_CLUSTER_FRACT, - &AGE_CLUSTER_MIN, - &PAGEOUT_WEIGHT, - &BUFFEROUT_WEIGHT - }; - int i; - for (i=0; i < ints[0] && i < 8; i++) { - if (ints[i+1]) - *(swap_vars[i]) = ints[i+1]; - } -} - -/* Parse the kernel command line "buff=" option at load time: */ -__initfunc(void buff_setup(char *str, int *ints)) -{ - int * buff_vars[6] = { - &MAX_BUFF_AGE, - &BUFF_ADVANCE, - &BUFF_DECLINE, - &BUFF_INITIAL_AGE, - &BUFFEROUT_WEIGHT, - &BUFFERMEM_GRACE - }; - int i; - for (i=0; i < ints[0] && i < 6; i++) { - if (ints[i+1]) - *(buff_vars[i]) = ints[i+1]; - } -} +buffer_mem_t buffer_mem = { + 6, /* minimum percent buffer + cache memory */ + 20, /* borrow percent buffer + cache memory */ + 90 /* maximum percent buffer + cache memory */ +}; diff --git a/mm/swap_state.c b/mm/swap_state.c index 4ebc5c05f..b575877ff 100644 --- a/mm/swap_state.c +++ b/mm/swap_state.c @@ -64,13 +64,13 @@ int add_to_swap_cache(struct page *page, unsigned long entry) #endif if (PageTestandSetSwapCache(page)) { printk("swap_cache: replacing non-empty entry %08lx " - "on page %08lx", + "on page %08lx\n", page->offset, page_address(page)); return 0; } if (page->inode) { printk("swap_cache: replacing page-cached entry " - "on page %08lx", page_address(page)); + "on page %08lx\n", page_address(page)); return 0; } atomic_inc(&page->count); @@ -138,18 +138,18 @@ void remove_from_swap_cache(struct page *page) { if (!page->inode) { printk ("VM: Removing swap cache page with zero inode hash " - "on page %08lx", page_address(page)); + "on page %08lx\n", page_address(page)); return; } if (page->inode != &swapper_inode) { printk ("VM: Removing swap cache page with wrong inode hash " - "on page %08lx", page_address(page)); + "on page %08lx\n", page_address(page)); } /* * This will be a legal case once we have a more mature swap cache. */ if (atomic_read(&page->count) == 1) { - printk ("VM: Removing page cache on unshared page %08lx", + printk ("VM: Removing page cache on unshared page %08lx\n", page_address(page)); return; } diff --git a/mm/vmscan.c b/mm/vmscan.c index ebef7a362..5d4188ae5 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -6,7 +6,7 @@ * Swap reorganised 29.12.95, Stephen Tweedie. * kswapd added: 7.1.96 sct * Removed kswapd_ctl limits, and swap out as many pages as needed - * to bring the system back to free_pages_high: 2.4.97, Rik van Riel. + * to bring the system back to freepages.high: 2.4.97, Rik van Riel. * Version: $Id: vmscan.c,v 1.5 1998/02/23 22:14:28 sct Exp $ */ @@ -22,6 +22,8 @@ #include <linux/smp_lock.h> #include <linux/slab.h> #include <linux/dcache.h> +#include <linux/fs.h> +#include <linux/pagemap.h> #include <asm/bitops.h> #include <asm/pgtable.h> @@ -454,11 +456,14 @@ static inline int do_try_to_free_page(int gfp_mask) stop = 3; if (gfp_mask & __GFP_WAIT) stop = 0; + if (BUFFER_MEM > buffer_mem.borrow_percent * num_physpages / 100) + state = 0; switch (state) { do { case 0: - if (shrink_mmap(i, gfp_mask)) + if (BUFFER_MEM > (buffer_mem.min_percent * num_physpages /100) && + shrink_mmap(i, gfp_mask)) return 1; state = 1; case 1: @@ -511,7 +516,6 @@ void kswapd_setup(void) printk ("Starting kswapd v%.*s\n", i, s); } -#define MAX_SWAP_FAIL 3 /* * The background pageout daemon. * Started as a kernel thread from the init process. @@ -542,32 +546,25 @@ int kswapd(void *unused) while (1) { int tries; + current->state = TASK_INTERRUPTIBLE; kswapd_awake = 0; flush_signals(current); run_task_queue(&tq_disk); schedule(); - current->state = TASK_INTERRUPTIBLE; kswapd_awake = 1; swapstats.wakeups++; /* Do the background pageout: * When we've got loads of memory, we try - * (free_pages_high - nr_free_pages) times to + * (freepages.high - nr_free_pages) times to * free memory. As memory gets tighter, kswapd * gets more and more agressive. -- Rik. */ - tries = free_pages_high - nr_free_pages; - if (tries < min_free_pages) { - tries = min_free_pages; + tries = freepages.high - nr_free_pages; + if (tries < freepages.min) { + tries = freepages.min; } - else if (nr_free_pages < (free_pages_high + free_pages_low) / 2) { + if (nr_free_pages < freepages.high + freepages.low) tries <<= 1; - if (nr_free_pages < free_pages_low) { - tries <<= 1; - if (nr_free_pages <= min_free_pages) { - tries <<= 1; - } - } - } while (tries--) { int gfp_mask; @@ -583,14 +580,6 @@ int kswapd(void *unused) run_task_queue(&tq_disk); } -#if 0 - /* - * Report failure if we couldn't even reach min_free_pages. - */ - if (nr_free_pages < min_free_pages) - printk("kswapd: failed, got %d of %d\n", - nr_free_pages, min_free_pages); -#endif } /* As if we could ever get here - maybe we want to make this killable */ remove_wait_queue(&kswapd_wait, &wait); @@ -606,9 +595,10 @@ void swap_tick(void) int want_wakeup = 0, memory_low = 0; int pages = nr_free_pages + atomic_read(&nr_async_pages); - if (pages < free_pages_low) + if (pages < freepages.low) memory_low = want_wakeup = 1; - else if (pages < free_pages_high && jiffies >= next_swap_jiffies) + else if ((pages < freepages.high || BUFFER_MEM > (num_physpages * buffer_mem.max_percent / 100)) + && jiffies >= next_swap_jiffies) want_wakeup = 1; if (want_wakeup) { diff --git a/net/802/sysctl_net_802.c b/net/802/sysctl_net_802.c index f97141d3c..19cd47af5 100644 --- a/net/802/sysctl_net_802.c +++ b/net/802/sysctl_net_802.c @@ -23,5 +23,6 @@ extern int sysctl_tr_rif_timeout; ctl_table tr_table[] = { {NET_TR_RIF_TIMEOUT, "rif_timeout", &sysctl_tr_rif_timeout, sizeof(int), 0644, NULL, &proc_dointvec}, + {0} }; #endif diff --git a/net/802/tr.c b/net/802/tr.c index bf6cd83d7..3550b81ed 100644 --- a/net/802/tr.c +++ b/net/802/tr.c @@ -510,10 +510,18 @@ int rif_get_info(char *buffer,char **start, off_t offset, int length, int dummy) * Called during bootup. We don't actually have to initialise * too much for this. */ - + +#ifdef CONFIG_PROC_FS +static struct proc_dir_entry tr_rif_proc = { + PROC_NET_TR_RIF, 6, "tr_rif", + S_IFREG | S_IRUGO, 1, 0, 0, + 0, &proc_net_inode_operations, + rif_get_info +}; +#endif + __initfunc(void rif_init(struct net_proto *unused)) { - rif_timer.expires = RIF_TIMEOUT; rif_timer.data = 0L; rif_timer.function = rif_check_expire; @@ -521,11 +529,6 @@ __initfunc(void rif_init(struct net_proto *unused)) add_timer(&rif_timer); #ifdef CONFIG_PROC_FS - proc_net_register(&(struct proc_dir_entry) { - PROC_NET_TR_RIF, 6, "tr_rif", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_net_inode_operations, - rif_get_info - }); + proc_net_register(&tr_rif_proc); #endif } diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c index 8b724361d..c56adc148 100644 --- a/net/appletalk/ddp.c +++ b/net/appletalk/ddp.c @@ -990,7 +990,7 @@ static int atalk_create(struct socket *sock, int protocol) { struct sock *sk; - sk = sk_alloc(AF_APPLETALK, GFP_KERNEL); + sk = sk_alloc(AF_APPLETALK, GFP_KERNEL, 1); if(sk == NULL) return (-ENOMEM); @@ -1404,6 +1404,31 @@ static int atalk_rcv(struct sk_buff *skb, struct device *dev, struct packet_type return (0); } +#if defined(CONFIG_IPDDP) || defined(CONFIG_IPDDP_MODULE) + /* + * Check if IP-over-DDP + */ + if(skb->data[12] == 22) + { + struct device *dev; + + /* This needs to be able to handle ipddp"N" devices */ + if((dev = dev_get("ipddp0")) == NULL) + return (-ENODEV); + + skb->protocol = htons(ETH_P_IP); + skb_pull(skb, 13); + skb->dev = dev; + skb->h.raw = skb->data; + + ((struct net_device_stats *)dev->priv)->rx_packets++; + ((struct net_device_stats *)dev->priv)->rx_bytes += skb->len+13; + netif_rx(skb); /* Send the SKB up to a higher place. */ + + return (0); + } +#endif + /* * Which socket - atalk_search_socket() looks for a *full match* * of the <net,node,port> tuple. @@ -1420,38 +1445,6 @@ static int atalk_rcv(struct sk_buff *skb, struct device *dev, struct packet_type return (0); } -#ifdef CONFIG_IPDDP - /* - * Check if IP-over-DDP - */ - if(skb->data[12] == 22) - { - struct device *dev; - struct net_device_stats *estats; - - if((dev = dev_get("ipddp0")) == NULL) - return (-ENODEV); - - estats = (struct net_device_stats *) dev->priv; - skb->protocol = htons(ETH_P_IP); - skb_pull(skb, 13); - skb->dev = dev; - skb->h.raw = skb->data; - skb->nh.raw = skb->data; - - /* printk("passing up ipddp, 0x%02x better be 45\n",skb->data[0]); - * printk("tot_len %d, skb->len %d\n", - * ntohs(skb->h.iph->tot_len),skb->len); - */ - - estats->rx_packets++; - estats->rx_bytes += skb->len + 13; - netif_rx(skb); /* Send the SKB up to a higher place. */ - - return (0); - } -#endif /* CONFIG_IPDDP */ - /* * Queue packet (standard) */ diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c index 3a4196b3f..107f481d6 100644 --- a/net/ax25/af_ax25.c +++ b/net/ax25/af_ax25.c @@ -828,7 +828,7 @@ int ax25_create(struct socket *sock, int protocol) return -ESOCKTNOSUPPORT; } - if ((sk = sk_alloc(AF_AX25, GFP_ATOMIC)) == NULL) + if ((sk = sk_alloc(AF_AX25, GFP_ATOMIC, 1)) == NULL) return -ENOMEM; if ((ax25 = ax25_create_cb()) == NULL) { @@ -854,7 +854,7 @@ struct sock *ax25_make_new(struct sock *osk, struct ax25_dev *ax25_dev) struct sock *sk; ax25_cb *ax25; - if ((sk = sk_alloc(AF_AX25, GFP_ATOMIC)) == NULL) + if ((sk = sk_alloc(AF_AX25, GFP_ATOMIC, 1)) == NULL) return NULL; if ((ax25 = ax25_create_cb()) == NULL) { @@ -1237,6 +1237,8 @@ static int ax25_accept(struct socket *sock, struct socket *newsock, int flags) newsk = skb->sk; newsk->pair = NULL; + newsk->socket = newsock; + newsk->sleep = &newsock->wait; sti(); /* Now attach up the new socket */ diff --git a/net/core/dev.c b/net/core/dev.c index b06d0053e..36efa363b 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -50,6 +50,7 @@ * is no device open function. * Andi Kleen : Fix error reporting for SIOCGIFCONF * Michael Chastain : Fix signed/unsigned for SIOCGIFCONF + * Cyrus Durgin : Cleaned for KMOD * */ @@ -81,7 +82,7 @@ #include <net/pkt_sched.h> #include <net/profile.h> #include <linux/init.h> -#include <linux/kerneld.h> +#include <linux/kmod.h> #ifdef CONFIG_NET_RADIO #include <linux/wireless.h> #endif /* CONFIG_NET_RADIO */ @@ -316,7 +317,7 @@ struct device *dev_alloc(const char *name, int *err) * Find and possibly load an interface. */ -#ifdef CONFIG_KERNELD +#ifdef CONFIG_KMOD void dev_load(const char *name) { @@ -398,20 +399,24 @@ int dev_open(struct device *dev) } #ifdef CONFIG_NET_FASTROUTE -void dev_clear_fastroute(struct device *dev) + +static __inline__ void dev_do_clear_fastroute(struct device *dev) { - int i; + if (dev->accept_fastpath) { + int i; - if (dev) { for (i=0; i<=NETDEV_FASTROUTE_HMASK; i++) dst_release(xchg(dev->fastpath+i, NULL)); + } +} + +void dev_clear_fastroute(struct device *dev) +{ + if (dev) { + dev_do_clear_fastroute(dev); } else { - for (dev = dev_base; dev; dev = dev->next) { - if (dev->accept_fastpath) { - for (i=0; i<=NETDEV_FASTROUTE_HMASK; i++) - dst_release(xchg(dev->fastpath+i, NULL)); - } - } + for (dev = dev_base; dev; dev = dev->next) + dev_do_clear_fastroute(dev); } } #endif @@ -643,7 +648,7 @@ int netdev_register_fc(struct device *dev, void (*stimul)(struct device *dev)) set_bit(bit, &netdev_fc_mask); clear_bit(bit, &netdev_fc_xoff); } - sti(); + restore_flags(flags); return bit; } @@ -659,7 +664,7 @@ void netdev_unregister_fc(int bit) clear_bit(bit, &netdev_fc_mask); clear_bit(bit, &netdev_fc_xoff); } - sti(); + restore_flags(flags); } static void netdev_wakeup(void) @@ -978,39 +983,6 @@ int register_gifconf(unsigned int family, gifconf_func_t * gifconf) /* - This ioctl is wrong by design. It really existed in some - old SYSV systems, only was named SIOCGIFNUM. - In multiprotocol environment it is just useless. - Well, SIOCGIFCONF is wrong too, but we have to preserve - it by compatibility reasons. - - If someone wants to achieve the same effect, please, use undocumented - feature of SIOCGIFCONF: it returns buffer length, if buffer - is not supplied. - - Let's remove it, until someone started to use it. --ANK - - In any case, if someone cannot live without it, it should - be renamed to SIOCGIFNUM. - */ - - -/* - * Count the installed interfaces (SIOCGIFCOUNT) - */ - -static int dev_ifcount(unsigned int *arg) -{ - struct device *dev; - unsigned int count = 0; - - for (dev = dev_base; dev != NULL; dev = dev->next) - count++; - - return put_user(count, arg); -} - -/* * Map an interface index to its name (SIOCGIFNAME) */ @@ -1022,6 +994,11 @@ static int dev_ifcount(unsigned int *arg) * Besides that, it is pretty silly to put "drawing" facility * to kernel, it is useful only to print ifindices * in readable form, is not it? --ANK + * + * We need this ioctl for efficient implementation of the + * if_indextoname() function required by the IPv6 API. Without + * it, we would have to search all the interfaces to find a + * match. --pb */ static int dev_ifname(struct ifreq *arg) @@ -1120,20 +1097,21 @@ static int sprintf_stats(char *buffer, struct device *dev) int size; if (stats) - size = sprintf(buffer, "%6s:%8lu %7lu %4lu %4lu %4lu %4lu %8lu %8lu %4lu %4lu %4lu %5lu %4lu %4lu\n", - dev->name, + size = sprintf(buffer, "%6s:%8lu %7lu %4lu %4lu %4lu %5lu %10lu %9lu %8lu %8lu %4lu %4lu %4lu %5lu %4lu %4lu\n", + dev->name, stats->rx_bytes, stats->rx_packets, stats->rx_errors, stats->rx_dropped + stats->rx_missed_errors, stats->rx_fifo_errors, stats->rx_length_errors + stats->rx_over_errors + stats->rx_crc_errors + stats->rx_frame_errors, + stats->rx_compressed, stats->multicast, stats->tx_bytes, stats->tx_packets, stats->tx_errors, stats->tx_dropped, stats->tx_fifo_errors, stats->collisions, stats->tx_carrier_errors + stats->tx_aborted_errors + stats->tx_window_errors + stats->tx_heartbeat_errors, - stats->multicast); + stats->tx_compressed); else size = sprintf(buffer, "%6s: No statistics available.\n", dev->name); @@ -1156,8 +1134,8 @@ int dev_get_info(char *buffer, char **start, off_t offset, int length, int dummy size = sprintf(buffer, - "Inter-| Receive | Transmit\n" - " face |bytes packets errs drop fifo frame|bytes packets errs drop fifo colls carrier multicast\n"); + "Inter-| Receive | Transmit\n" + " face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed\n"); pos+=size; len+=size; @@ -1555,9 +1533,6 @@ int dev_ioctl(unsigned int cmd, void *arg) rtnl_shunlock(); return ret; } - if (cmd == SIOCGIFCOUNT) { - return dev_ifcount((unsigned int*)arg); - } if (cmd == SIOCGIFNAME) { return dev_ifname((struct ifreq *)arg); } diff --git a/net/core/dst.c b/net/core/dst.c index e94ef2967..4cad680c2 100644 --- a/net/core/dst.c +++ b/net/core/dst.c @@ -101,7 +101,7 @@ void * dst_alloc(int size, struct dst_ops * ops) void __dst_free(struct dst_entry * dst) { start_bh_atomic(); - dst->obsolete = 1; + dst->obsolete = 2; dst->next = dst_garbage_list; dst_garbage_list = dst; if (dst_gc_timer_inc > DST_GC_INC) { diff --git a/net/core/iovec.c b/net/core/iovec.c index 18a9a3b5b..9e8873646 100644 --- a/net/core/iovec.c +++ b/net/core/iovec.c @@ -80,18 +80,21 @@ out_free: /* * Copy kernel to iovec. + * + * Note: this modifies the original iovec. */ int memcpy_toiovec(struct iovec *iov, unsigned char *kdata, int len) { - int err = -EFAULT; + int err; while(len>0) { if(iov->iov_len) { int copy = min(iov->iov_len, len); - if (copy_to_user(iov->iov_base, kdata, copy)) + err = copy_to_user(iov->iov_base, kdata, copy); + if (err) goto out; kdata+=copy; len-=copy; @@ -107,6 +110,8 @@ out: /* * Copy iovec to kernel. + * + * Note: this modifies the original iovec. */ int memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len) @@ -187,9 +192,8 @@ out: * call to this function will be unaligned also. */ -int csum_partial_copy_fromiovecend(unsigned char *kdata, - struct iovec *iov, int offset, - unsigned int len, int *csump) +int csum_partial_copy_fromiovecend(unsigned char *kdata, struct iovec *iov, + int offset, unsigned int len, int *csump) { int partial_cnt = 0; int err = 0; @@ -246,9 +250,9 @@ int csum_partial_copy_fromiovecend(unsigned char *kdata, if (copy_from_user(kdata, base, copy)) goto out_fault; kdata += copy; - base += copy; + base += copy; partial_cnt += copy; - len -= copy; + len -= copy; iov++; if (len) continue; @@ -260,9 +264,9 @@ int csum_partial_copy_fromiovecend(unsigned char *kdata, goto out_fault; csum = csum_partial(kdata - partial_cnt, 4, csum); kdata += par_len; - base += par_len; - copy -= par_len; - len -= par_len; + base += par_len; + copy -= par_len; + len -= par_len; partial_cnt = 0; } @@ -278,16 +282,12 @@ int csum_partial_copy_fromiovecend(unsigned char *kdata, } } - /* Why do we want to break?? There may be more to copy ... */ - if (copy == 0) { -if (len > partial_cnt) -printk("csum_iovec: early break? len=%d, partial=%d\n", len, partial_cnt); - break; + if (copy) { + csum = csum_and_copy_from_user(base, kdata, copy, + csum, &err); + if (err) + goto out; } - - csum = csum_and_copy_from_user(base, kdata, copy, csum, &err); - if (err) - goto out; len -= copy + partial_cnt; kdata += copy + partial_cnt; iov++; diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 3de3743e0..a8d72604d 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -153,12 +153,14 @@ int neigh_ifdown(struct neigh_table *tbl, struct device *dev) static struct neighbour *neigh_alloc(struct neigh_table *tbl, int creat) { struct neighbour *n; + unsigned long now = jiffies; if (tbl->entries > tbl->gc_thresh1) { if (creat < 0) return NULL; - if (tbl->entries > tbl->gc_thresh2 || - jiffies - tbl->last_flush > 5*HZ) { + if (tbl->entries > tbl->gc_thresh3 || + (tbl->entries > tbl->gc_thresh2 && + now - tbl->last_flush > 5*HZ)) { if (neigh_forced_gc(tbl) == 0 && tbl->entries > tbl->gc_thresh3) return NULL; @@ -172,7 +174,7 @@ static struct neighbour *neigh_alloc(struct neigh_table *tbl, int creat) memset(n, 0, tbl->entry_size); skb_queue_head_init(&n->arp_queue); - n->updated = n->used = jiffies; + n->updated = n->used = now; n->nud_state = NUD_NONE; n->output = neigh_blackhole; n->parms = &tbl->parms; @@ -666,8 +668,18 @@ int neigh_update(struct neighbour *neigh, u8 *lladdr, u8 new, int override, int neigh_suspect(neigh); if (!(old&NUD_VALID)) { struct sk_buff *skb; - while ((skb=__skb_dequeue(&neigh->arp_queue)) != NULL) - neigh->output(skb); + + /* Again: avoid dead loop if something went wrong */ + + while (neigh->nud_state&NUD_VALID && + (skb=__skb_dequeue(&neigh->arp_queue)) != NULL) { + struct neighbour *n1 = neigh; + /* On shaper/eql skb->dst->neighbour != neigh :( */ + if (skb->dst && skb->dst->neighbour) + n1 = skb->dst->neighbour; + n1->output(skb); + } + skb_queue_purge(&neigh->arp_queue); } return 0; } @@ -1228,7 +1240,7 @@ struct neigh_sysctl_table &proc_dointvec}, {0}}, - {{1, "default", NULL, 0, 0555, NULL},{0}}, + {{NET_PROTO_CONF_DEFAULT, "default", NULL, 0, 0555, NULL},{0}}, {{0, "neigh", NULL, 0, 0555, NULL},{0}}, {{0, NULL, NULL, 0, 0555, NULL},{0}}, {{CTL_NET, "net", NULL, 0, 0555, NULL},{0}} @@ -1243,10 +1255,11 @@ int neigh_sysctl_register(struct device *dev, struct neigh_parms *p, if (t == NULL) return -ENOBUFS; memcpy(t, &neigh_sysctl_template, sizeof(*t)); + t->neigh_vars[0].data = &p->mcast_probes; t->neigh_vars[1].data = &p->ucast_probes; t->neigh_vars[2].data = &p->app_probes; t->neigh_vars[3].data = &p->retrans_time; - t->neigh_vars[4].data = &p->reachable_time; + t->neigh_vars[4].data = &p->base_reachable_time; t->neigh_vars[5].data = &p->delay_probe_time; t->neigh_vars[6].data = &p->gc_staletime; t->neigh_vars[7].data = &p->queue_len; @@ -1256,7 +1269,7 @@ int neigh_sysctl_register(struct device *dev, struct neigh_parms *p, t->neigh_vars[11].data = &p->locktime; if (dev) { t->neigh_dev[0].procname = dev->name; - t->neigh_dev[0].ctl_name = dev->ifindex+1; + t->neigh_dev[0].ctl_name = dev->ifindex; memset(&t->neigh_vars[12], 0, sizeof(ctl_table)); } else { t->neigh_vars[12].data = (&p->locktime) + 1; diff --git a/net/core/sock.c b/net/core/sock.c index 6da5f5a0d..f940e5a80 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -137,6 +137,8 @@ __u32 sysctl_wmem_default = SK_WMEM_MAX; __u32 sysctl_rmem_default = SK_RMEM_MAX; int sysctl_core_destroy_delay = SOCK_DESTROY_TIME; +/* Maximal space eaten by iovec (still not made (2.1.88)!) plus some space */ +int sysctl_optmem_max = sizeof(unsigned long)*(2*UIO_MAXIOV + 512); /* * This is meant for all protocols to use and covers goings on @@ -472,11 +474,11 @@ static kmem_cache_t *sk_cachep; * usage. */ -struct sock *sk_alloc(int family, int priority) +struct sock *sk_alloc(int family, int priority, int zero_it) { struct sock *sk = kmem_cache_alloc(sk_cachep, priority); - if(sk) { + if(sk && zero_it) { memset(sk, 0, sizeof(struct sock)); sk->family = family; } @@ -561,34 +563,22 @@ struct sk_buff *sock_rmalloc(struct sock *sk, unsigned long size, int force, int void *sock_kmalloc(struct sock *sk, int size, int priority) { void *mem = NULL; - /* Always use wmem.. */ - if (atomic_read(&sk->wmem_alloc)+size < sk->sndbuf) { + if (atomic_read(&sk->omem_alloc)+size < sysctl_optmem_max) { /* First do the add, to avoid the race if kmalloc * might sleep. */ - atomic_add(size, &sk->wmem_alloc); + atomic_add(size, &sk->omem_alloc); mem = kmalloc(size, priority); - if (mem) - return mem; - atomic_sub(size, &sk->wmem_alloc); } return mem; } void sock_kfree_s(struct sock *sk, void *mem, int size) { -#if 1 /* Debug */ - if (atomic_read(&sk->wmem_alloc) < size) { - printk(KERN_DEBUG "sock_kfree_s: mem not accounted.\n"); - return; - } -#endif kfree_s(mem, size); - atomic_sub(size, &sk->wmem_alloc); - sk->write_space(sk); + atomic_sub(size, &sk->omem_alloc); } - /* FIXME: this is insane. We are trying suppose to be controlling how * how much space we have for data bytes, not packet headers. * This really points out that we need a better system for doing the @@ -633,6 +623,30 @@ unsigned long sock_wspace(struct sock *sk) return(0); } +/* It is almost wait_for_tcp_memory minus release_sock/lock_sock. + I think, these locks should be removed for datagram sockets. + */ +static void sock_wait_for_wmem(struct sock * sk) +{ + struct wait_queue wait = { current, NULL }; + + sk->socket->flags &= ~SO_NOSPACE; + add_wait_queue(sk->sleep, &wait); + for (;;) { + if (signal_pending(current)) + break; + current->state = TASK_INTERRUPTIBLE; + if (atomic_read(&sk->wmem_alloc) < sk->sndbuf) + break; + if (sk->shutdown & SEND_SHUTDOWN) + break; + if (sk->err) + break; + schedule(); + } + current->state = TASK_RUNNING; + remove_wait_queue(sk->sleep, &wait); +} /* @@ -641,94 +655,78 @@ unsigned long sock_wspace(struct sock *sk) struct sk_buff *sock_alloc_send_skb(struct sock *sk, unsigned long size, unsigned long fallback, int noblock, int *errcode) { + int err; struct sk_buff *skb; - do - { - if(sk->err!=0) - { - *errcode=xchg(&sk->err,0); - return NULL; - } - - if(sk->shutdown&SEND_SHUTDOWN) - { - /* - * FIXME: Check 1003.1g should we deliver - * a signal here ??? - */ - *errcode=-EPIPE; - return NULL; - } - - if(!fallback) + do { + if ((err = xchg(&sk->err,0)) != 0) + goto failure; + + /* + * FIXME: Check 1003.1g should we deliver + * a signal here ??? + * + * Alan, could we solve this question once and forever? + * + * I believe, datagram sockets should never + * generate SIGPIPE. Moreover, I DO think that + * TCP is allowed to generate it only on write() + * call, but never on send/sendto/sendmsg. + * (btw, Solaris generates it even on read() :-)) + * + * The reason is that SIGPIPE is global flag, + * so that library function using sockets (f.e. syslog()), + * must save/disable it on entry and restore on exit. + * As result, signal arriving for another thread will + * be lost. Generation it on write() is still necessary + * because a lot of stupid programs never check write() + * return value. + * + * Seems, SIGPIPE is very bad idea, sort of gets(). + * At least, we could have an option disabling + * this behaviour on per-socket and/or per-message base. + * BTW it is very easy - MSG_SIGPIPE flag, which + * always set by read/write and checked here. + * --ANK + */ + + err = -EPIPE; + if (sk->shutdown&SEND_SHUTDOWN) + goto failure; + + if (!fallback) skb = sock_wmalloc(sk, size, 0, sk->allocation); - else - { + else { /* The buffer get won't block, or use the atomic queue. It does produce annoying no free page messages still.... */ skb = sock_wmalloc(sk, size, 0 , GFP_BUFFER); - if(!skb) + if (!skb) skb=sock_wmalloc(sk, fallback, 0, sk->allocation); } - + /* * This means we have too many buffers for this socket already. */ - - if(skb==NULL) - { - unsigned long tmp; + /* The following code is stolen "as is" from tcp.c */ + + if (skb==NULL) { sk->socket->flags |= SO_NOSPACE; - if(noblock) - { - *errcode=-EAGAIN; - return NULL; - } - if(sk->shutdown&SEND_SHUTDOWN) - { - *errcode=-EPIPE; - return NULL; - } - tmp = atomic_read(&sk->wmem_alloc); - cli(); - if(sk->shutdown&SEND_SHUTDOWN) - { - sti(); - *errcode=-EPIPE; - return NULL; - } - -#if 1 - if( tmp <= atomic_read(&sk->wmem_alloc)) -#else - /* ANK: Line above seems either incorrect - * or useless. sk->wmem_alloc has a tiny chance to change - * between tmp = sk->w... and cli(), - * but it might(?) change earlier. In real life - * it does not (I never seen the message). - * In any case I'd delete this check at all, or - * change it to: - */ - if (atomic_read(&sk->wmem_alloc) >= sk->sndbuf) -#endif - { - sk->socket->flags &= ~SO_NOSPACE; - interruptible_sleep_on(sk->sleep); - if (signal_pending(current)) - { - sti(); - *errcode = -ERESTARTSYS; - return NULL; - } - } - sti(); + err = -EAGAIN; + if (noblock) + goto failure; + err = -ERESTARTSYS; + if (signal_pending(current)) + goto failure; + sock_wait_for_wmem(sk); } - } - while(skb==NULL); - + } while (skb==NULL); + return skb; + +failure: + *errcode = err; + return NULL; } diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c index 1da2cc152..47c85d006 100644 --- a/net/core/sysctl_net_core.c +++ b/net/core/sysctl_net_core.c @@ -22,6 +22,7 @@ extern __u32 sysctl_wmem_default; extern __u32 sysctl_rmem_default; extern int sysctl_core_destroy_delay; +extern int sysctl_optmem_max; ctl_table core_table[] = { {NET_CORE_WMEM_MAX, "wmem_max", @@ -53,6 +54,9 @@ ctl_table core_table[] = { {NET_CORE_MSG_BURST, "message_burst", &net_msg_burst, sizeof(int), 0644, NULL, &proc_dointvec_jiffies}, + {NET_CORE_OPTMEM_MAX, "optmem_max", + &sysctl_optmem_max, sizeof(int), 0644, NULL, + &proc_dointvec}, { 0 } }; #endif diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 584ad8c7a..ef1c44620 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -5,7 +5,7 @@ * * AF_INET protocol family socket handler. * - * Version: $Id: af_inet.c,v 1.5 1997/12/16 05:37:33 ralf Exp $ + * Version: $Id: af_inet.c,v 1.6 1998/03/17 22:18:20 ralf Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -52,6 +52,7 @@ * Willy Konynenberg : Transparent proxying support. * David S. Miller : New socket lookup architecture. * Some other random speedups. + * Cyrus Durgin : Cleaned up file for kmod hacks. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -107,8 +108,8 @@ #ifdef CONFIG_BRIDGE #include <net/br.h> #endif -#ifdef CONFIG_KERNELD -#include <linux/kerneld.h> +#ifdef CONFIG_KMOD +#include <linux/kmod.h> #endif #ifdef CONFIG_NET_RADIO #include <linux/wireless.h> @@ -327,7 +328,7 @@ static int inet_create(struct socket *sock, int protocol) static int warned; if (net_families[AF_PACKET]==NULL) { -#if defined(CONFIG_KERNELD) && defined(CONFIG_PACKET_MODULE) +#if defined(CONFIG_KMOD) && defined(CONFIG_PACKET_MODULE) char module_name[30]; sprintf(module_name,"net-pf-%d", AF_PACKET); request_module(module_name); @@ -341,7 +342,7 @@ static int inet_create(struct socket *sock, int protocol) } sock->state = SS_UNCONNECTED; - sk = sk_alloc(AF_INET, GFP_KERNEL); + sk = sk_alloc(AF_INET, GFP_KERNEL, 1); if (sk == NULL) goto do_oom; @@ -894,7 +895,7 @@ static int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) case SIOCDRARP: case SIOCGRARP: case SIOCSRARP: -#ifdef CONFIG_KERNELD +#ifdef CONFIG_KMOD if (rarp_ioctl_hook == NULL) request_module("rarp"); #endif @@ -928,7 +929,7 @@ static int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) #ifdef CONFIG_DLCI_MODULE -#ifdef CONFIG_KERNELD +#ifdef CONFIG_KMOD if (dlci_ioctl_hook == NULL) request_module("dlci"); #endif diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 94ae4263e..dd7ce9e0f 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -1,6 +1,6 @@ /* linux/net/inet/arp.c * - * Version: $Id: arp.c,v 1.4 1998/03/03 01:23:36 ralf Exp $ + * Version: $Id: arp.c,v 1.5 1998/03/17 22:18:21 ralf Exp $ * * Copyright (C) 1994 by Florian La Roche * @@ -189,7 +189,7 @@ struct neigh_table arp_tbl = NULL, parp_redo, { NULL, NULL, &arp_tbl, 0, NULL, NULL, - 30*HZ, 1*HZ, 60*HZ, 30*HZ, 5*HZ, 3, 3, 0, 3, 1*HZ, (8*HZ)/10, 1*HZ, 64 }, + 30*HZ, 1*HZ, 60*HZ, 30*HZ, 5*HZ, 3, 3, 0, 3, 1*HZ, (8*HZ)/10, 64, 1*HZ }, 30*HZ, 128, 512, 1024, }; @@ -954,6 +954,10 @@ int arp_get_info(char *buffer, char **start, off_t offset, int length, int dummy struct device *dev = n->dev; int hatype = dev->type; + /* Do not confuse users "arp -a" with magic entries */ + if (!(n->nud_state&~NUD_NOARP)) + continue; + /* I'd get great pleasure deleting this ugly code. Let's output it in hexadecimal format. "arp" utility will eventually repaired --ANK diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 7d5f0021f..87394f906 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -1,7 +1,7 @@ /* * NET3 IP device support routines. * - * Version: $Id: devinet.c,v 1.3 1997/12/16 05:37:35 ralf Exp $ + * Version: $Id: devinet.c,v 1.4 1998/03/17 22:18:21 ralf Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -19,6 +19,7 @@ * * Changes: * Alexey Kuznetsov: pa_* fields are replaced with ifaddr lists. + Cyrus Durgin: updated for kmod */ #include <linux/config.h> @@ -49,8 +50,8 @@ #ifdef CONFIG_SYSCTL #include <linux/sysctl.h> #endif -#ifdef CONFIG_KERNELD -#include <linux/kerneld.h> +#ifdef CONFIG_KMOD +#include <linux/kmod.h> #endif #include <net/ip.h> @@ -157,28 +158,32 @@ static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, int destroy) { struct in_ifaddr *ifa1 = *ifap; - struct in_ifaddr *ifa; - - /* 1. Unlink it */ - *ifap = ifa1->ifa_next; - - /* 2. Deleting primary ifaddr forces deletion all secondaries */ + /* 1. Deleting primary ifaddr forces deletion all secondaries */ if (!(ifa1->ifa_flags&IFA_F_SECONDARY)) { - while ((ifa=*ifap) != NULL) { - if (ifa1->ifa_mask != ifa->ifa_mask || + struct in_ifaddr *ifa; + struct in_ifaddr **ifap1 = &ifa1->ifa_next; + + while ((ifa=*ifap1) != NULL) { + if (!(ifa->ifa_flags&IFA_F_SECONDARY) || + ifa1->ifa_mask != ifa->ifa_mask || !inet_ifa_match(ifa1->ifa_address, ifa)) { - ifap = &ifa->ifa_next; + ifap1 = &ifa->ifa_next; continue; } - *ifap = ifa->ifa_next; + *ifap1 = ifa->ifa_next; rtmsg_ifa(RTM_DELADDR, ifa); notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa); inet_free_ifa(ifa); } } + /* 2. Unlink it */ + + *ifap = ifa1->ifa_next; + + /* 3. Announce address deletion */ /* Send message first, then call notifier. @@ -232,10 +237,9 @@ inet_insert_ifa(struct in_device *in_dev, struct in_ifaddr *ifa) ifap = last_primary; } - cli(); ifa->ifa_next = *ifap; + /* ATOMIC_SET */ *ifap = ifa; - sti(); /* Send message first, then call notifier. Notifier will trigger FIB update, so that @@ -413,7 +417,7 @@ int devinet_ioctl(unsigned int cmd, void *arg) *colon = 0; #endif -#ifdef CONFIG_KERNELD +#ifdef CONFIG_KMOD dev_load(ifr.ifr_name); #endif @@ -960,6 +964,8 @@ static void devinet_sysctl_register(struct in_device *in_dev, struct ipv4_devcon t->sysctl_header = register_sysctl_table(t->devinet_root_dir, 0); if (t->sysctl_header == NULL) kfree(t); + else + p->sysctl = t; } static void devinet_sysctl_unregister(struct ipv4_devconf *p) diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 409db8209..6350a6366 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -5,7 +5,7 @@ * * IPv4 Forwarding Information Base: FIB frontend. * - * Version: $Id: fib_frontend.c,v 1.6 1997/12/13 21:52:48 kuznet Exp $ + * Version: $Id: fib_frontend.c,v 1.9 1998/03/08 20:52:36 davem Exp $ * * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> * @@ -151,7 +151,6 @@ struct device * ip_dev_find(u32 addr) memset(&key, 0, sizeof(key)); key.dst = addr; - key.scope = RT_SCOPE_UNIVERSE; if (!local_table || local_table->tb_lookup(local_table, &key, &res) || res.type != RTN_LOCAL) @@ -344,6 +343,10 @@ int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) int s_t; struct fib_table *tb; + if (NLMSG_PAYLOAD(cb->nlh, 0) >= sizeof(struct rtmsg) && + ((struct rtmsg*)NLMSG_DATA(cb->nlh))->rtm_flags&RTM_F_CLONED) + return ip_rt_dump(skb, cb); + s_t = cb->args[0]; if (s_t == 0) s_t = cb->args[0] = RT_TABLE_MIN; @@ -423,8 +426,13 @@ static void fib_add_ifaddr(struct in_ifaddr *ifa) u32 addr = ifa->ifa_local; u32 prefix = ifa->ifa_address&mask; - if (ifa->ifa_flags&IFA_F_SECONDARY) + if (ifa->ifa_flags&IFA_F_SECONDARY) { prim = inet_ifa_byprefix(in_dev, prefix, mask); + if (prim == NULL) { + printk(KERN_DEBUG "fib_add_ifaddr: bug: prim == NULL\n"); + return; + } + } fib_magic(RTM_NEWROUTE, RTN_LOCAL, addr, 32, prim); @@ -435,7 +443,8 @@ static void fib_add_ifaddr(struct in_ifaddr *ifa) if (ifa->ifa_broadcast && ifa->ifa_broadcast != 0xFFFFFFFF) fib_magic(RTM_NEWROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32, prim); - if (!ZERONET(prefix) && !(ifa->ifa_flags&IFA_F_SECONDARY)) { + if (!ZERONET(prefix) && !(ifa->ifa_flags&IFA_F_SECONDARY) && + (prefix != addr || ifa->ifa_prefixlen < 32)) { fib_magic(RTM_NEWROUTE, dev->flags&IFF_LOOPBACK ? RTN_LOCAL : RTN_UNICAST, prefix, ifa->ifa_prefixlen, prim); @@ -464,8 +473,13 @@ static void fib_del_ifaddr(struct in_ifaddr *ifa) if (!(ifa->ifa_flags&IFA_F_SECONDARY)) fib_magic(RTM_DELROUTE, dev->flags&IFF_LOOPBACK ? RTN_LOCAL : RTN_UNICAST, any, ifa->ifa_prefixlen, prim); - else + else { prim = inet_ifa_byprefix(in_dev, any, ifa->ifa_mask); + if (prim == NULL) { + printk(KERN_DEBUG "fib_del_ifaddr: bug: prim == NULL\n"); + return; + } + } /* Deletion is more complicated than add. We should take care of not to delete too much :-) diff --git a/net/ipv4/fib_hash.c b/net/ipv4/fib_hash.c index 33bcf0321..4b89ab676 100644 --- a/net/ipv4/fib_hash.c +++ b/net/ipv4/fib_hash.c @@ -5,7 +5,7 @@ * * IPv4 FIB: lookup engine and maintenance routines. * - * Version: $Id: fib_hash.c,v 1.1 1997/11/09 19:53:13 kuznet Exp $ + * Version: $Id: fib_hash.c,v 1.3 1998/03/08 05:56:16 davem Exp $ * * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> * diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index 3ffb404b5..7ec60a5be 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -5,7 +5,7 @@ * * IPv4 Forwarding Information Base: policy rules. * - * Version: $Id: fib_rules.c,v 1.2 1997/10/10 22:40:49 davem Exp $ + * Version: $Id: fib_rules.c,v 1.3 1998/03/08 05:56:17 davem Exp $ * * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> * diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 3883fcba0..d2d37e11e 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -5,7 +5,7 @@ * * IPv4 Forwarding Information Base: semantics. * - * Version: $Id: fib_semantics.c,v 1.6 1997/12/13 21:52:49 kuznet Exp $ + * Version: $Id: fib_semantics.c,v 1.7 1998/03/08 05:56:18 davem Exp $ * * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> * diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index b2c7151d1..e8f636e21 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -3,7 +3,7 @@ * * Alan Cox, <alan@cymru.net> * - * Version: $Id: icmp.c,v 1.4 1998/03/03 01:23:37 ralf Exp $ + * Version: $Id: icmp.c,v 1.5 1998/03/17 22:18:23 ralf Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -928,10 +928,8 @@ int icmp_chkaddr(struct sk_buff *skb) struct tcphdr *th = (struct tcphdr *)(((unsigned char *)iph)+(iph->ihl<<2)); sk = tcp_v4_lookup(iph->daddr, th->dest, iph->saddr, th->source, skb->dev->ifindex); - if (!sk) return 0; - if (sk->saddr != iph->saddr) return 0; - if (sk->daddr != iph->daddr) return 0; - if (sk->dummy_th.dest != th->dest) return 0; + if (!sk || (sk->state == TCP_LISTEN)) + return 0; /* * This packet came from us. */ diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 166b68b42..d3414a0fe 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -8,7 +8,7 @@ * the older version didn't come out right using gcc 2.5.8, the newer one * seems to fall out with gcc 2.6.2. * - * Version: $Id: igmp.c,v 1.3 1997/12/16 05:37:36 ralf Exp $ + * Version: $Id: igmp.c,v 1.4 1998/03/17 22:18:24 ralf Exp $ * * Authors: * Alan Cox <Alan.Cox@linux.org> diff --git a/net/ipv4/ip_forward.c b/net/ipv4/ip_forward.c index 45a2ed588..8df8414cd 100644 --- a/net/ipv4/ip_forward.c +++ b/net/ipv4/ip_forward.c @@ -5,7 +5,7 @@ * * The IP forwarding functionality. * - * Version: $Id: ip_forward.c,v 1.3 1998/03/03 01:23:37 ralf Exp $ + * Version: $Id: ip_forward.c,v 1.4 1998/03/17 22:18:25 ralf Exp $ * * Authors: see ip.c * diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index 9dccb5324..e6831adb8 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -5,7 +5,7 @@ * * The IP fragmentation functionality. * - * Version: $Id: ip_fragment.c,v 1.30 1997/12/29 19:52:32 kuznet Exp $ + * Version: $Id: ip_fragment.c,v 1.32 1998/03/08 05:56:21 davem Exp $ * * Authors: Fred N. van Kempen <waltje@uWalt.NL.Mugnet.ORG> * Alan Cox <Alan.Cox@linux.org> diff --git a/net/ipv4/ip_fw.c b/net/ipv4/ip_fw.c index d78aa0f66..4eb41c325 100644 --- a/net/ipv4/ip_fw.c +++ b/net/ipv4/ip_fw.c @@ -6,7 +6,7 @@ * license in recognition of the original copyright. * -- Alan Cox. * - * $Id: ip_fw.c,v 1.3 1997/12/16 05:37:37 ralf Exp $ + * $Id: ip_fw.c,v 1.4 1998/03/17 22:18:25 ralf Exp $ * * Ported from BSD to Linux, * Alan Cox 22/Nov/1994. @@ -392,6 +392,39 @@ int ip_fw_chk(struct iphdr *ip, struct device *rif, __u16 *redirport, struct ip_ continue; /* Mismatch */ } + /* This looks stupid, because we scan almost static + list, searching for static key. However, this way seems + to be only reasonable way of handling fw_via rules + (btw bsd makes the same thing). + + It will not affect performance if you will follow + the following simple rules: + + - if inteface is aliased, ALWAYS specify fw_viadev, + so that previous check will guarantee, that we will + not waste time when packet arrive on another interface. + + - avoid using fw_via.s_addr if fw_via.s_addr is owned + by an aliased interface. + + --ANK + */ + if (f->fw_via.s_addr && rif) { + struct in_ifaddr *ifa; + + if (rif->ip_ptr == NULL) + continue; /* Mismatch */ + + for (ifa = ((struct in_device*)(rif->ip_ptr))->ifa_list; + ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_local == f->fw_via.s_addr) + goto ifa_ok; + } + continue; /* Mismatch */ + + ifa_ok: + } + /* * Ok the chain addresses match. */ diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index 61c364542..fa8208959 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -5,7 +5,7 @@ * * The Internet Protocol (IP) module. * - * Version: $Id: ip_input.c,v 1.2 1997/12/16 05:37:38 ralf Exp $ + * Version: $Id: ip_input.c,v 1.3 1998/03/17 22:18:26 ralf Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> diff --git a/net/ipv4/ip_masq_mod.c b/net/ipv4/ip_masq_mod.c index 797f9112f..2265161f3 100644 --- a/net/ipv4/ip_masq_mod.c +++ b/net/ipv4/ip_masq_mod.c @@ -12,6 +12,8 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * + * Changes: + * Cyrus Durgin: fixed kerneld stuff for kmod. */ #include <linux/config.h> @@ -21,8 +23,8 @@ #include <linux/errno.h> #include <net/ip_masq.h> #include <net/ip_masq_mod.h> -#ifdef CONFIG_KERNELD -#include <linux/kerneld.h> +#ifdef CONFIG_KMOD +#include <linux/kmod.h> #endif EXPORT_SYMBOL(register_ip_masq_mod); @@ -290,7 +292,7 @@ struct ip_masq_mod * ip_masq_mod_getbyname(const char *mmod_name) int ip_masq_mod_ctl(int optname, struct ip_fw_masqctl *mctl, int optlen) { struct ip_masq_mod * mmod; -#ifdef CONFIG_KERNELD +#ifdef CONFIG_KMOD char kmod_name[IP_MASQ_MOD_NMAX+8]; #endif /* tappo */ @@ -299,7 +301,7 @@ int ip_masq_mod_ctl(int optname, struct ip_fw_masqctl *mctl, int optlen) mmod = ip_masq_mod_getbyname(mctl->u.mod.name); if (mmod) return mmod->mmod_ctl(optname, mctl, optlen); -#ifdef CONFIG_KERNELD +#ifdef CONFIG_KMOD sprintf(kmod_name,"ip_masq_%s", mctl->u.mod.name); IP_MASQ_DEBUG(1, "About to request \"%s\" module\n", kmod_name); diff --git a/net/ipv4/ip_masq_raudio.c b/net/ipv4/ip_masq_raudio.c index f7e28f21a..377b8223e 100644 --- a/net/ipv4/ip_masq_raudio.c +++ b/net/ipv4/ip_masq_raudio.c @@ -2,7 +2,7 @@ * IP_MASQ_RAUDIO - Real Audio masquerading module * * - * Version: @(#)$Id: ip_masq_raudio.c,v 1.8 1997/11/28 15:32:32 alan Exp $ + * Version: @(#)$Id: ip_masq_raudio.c,v 1.9 1998/02/23 02:50:19 davem Exp $ * * Author: Nigel Metheringham * Real Time Streaming code by Progressive Networks diff --git a/net/ipv4/ip_nat_dumb.c b/net/ipv4/ip_nat_dumb.c index 06e9be8fb..def66858c 100644 --- a/net/ipv4/ip_nat_dumb.c +++ b/net/ipv4/ip_nat_dumb.c @@ -5,7 +5,7 @@ * * Dumb Network Address Translation. * - * Version: $Id: ip_nat_dumb.c,v 1.2 1997/10/10 22:41:05 davem Exp $ + * Version: $Id: ip_nat_dumb.c,v 1.2 1997/12/16 05:37:40 ralf Exp $ * * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> * @@ -14,6 +14,9 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * + * Fixes: + * Rani Assaf : A zero checksum is a special case + * only in UDP * * NOTE: It is just working model of real NAT. */ @@ -49,7 +52,6 @@ ip_do_nat(struct sk_buff *skb) u32 odaddr = iph->daddr; u32 osaddr = iph->saddr; u16 check; - u16 *cksum = NULL; IPCB(skb)->flags |= IPSKB_TRANSLATED; @@ -62,17 +64,23 @@ ip_do_nat(struct sk_buff *skb) /* If it is the first fragment, rewrite protocol headers */ if (!(iph->frag_off & htons(IP_OFFSET))) { - /* Only plain TCP/UDP headers rewriting is implemented :-( */ - if (iph->protocol == IPPROTO_TCP) - cksum = (u16*)&((struct tcphdr*)(((char*)iph) + iph->ihl*4))->check; - else if (iph->protocol == IPPROTO_UDP) - cksum = (u16*)&((struct udphdr*)(((char*)iph) + iph->ihl*4))->check; - if (cksum && (check = *cksum) != 0) { - check = csum_tcpudp_magic(iph->saddr, iph->daddr, 0, 0, ~check); - check = csum_tcpudp_magic(~osaddr, ~odaddr, 0, 0, ~check); - if (!check) - check = 0xFFFF; - *cksum = check; + u16 *cksum; + + switch(iph->protocol) { + case IPPROTO_TCP: + cksum = (u16*)&((struct tcphdr*)(((char*)iph) + iph->ihl*4))->check; + check = csum_tcpudp_magic(iph->saddr, iph->daddr, 0, 0, ~(*cksum)); + *cksum = csum_tcpudp_magic(~osaddr, ~odaddr, 0, 0, ~check); + break; + case IPPROTO_UDP: + cksum = (u16*)&((struct udphdr*)(((char*)iph) + iph->ihl*4))->check; + if ((check = *cksum) != 0) { + check = csum_tcpudp_magic(iph->saddr, iph->daddr, 0, 0, ~check); + check = csum_tcpudp_magic(~osaddr, ~odaddr, 0, 0, ~check); + *cksum = check ? : 0xFFFF; + } + default: + break; } } return 0; diff --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c index 53c680eed..d78cc1ff0 100644 --- a/net/ipv4/ip_options.c +++ b/net/ipv4/ip_options.c @@ -5,7 +5,7 @@ * * The options processing module for ip.c * - * Version: $Id: ip_options.c,v 1.2 1997/12/16 05:37:40 ralf Exp $ + * Version: $Id: ip_options.c,v 1.3 1998/03/17 22:18:28 ralf Exp $ * * Authors: A.N.Kuznetsov * diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index ac4ac22ae..63fbbfe1e 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -5,7 +5,7 @@ * * The Internet Protocol (IP) output module. * - * Version: $Id: ip_output.c,v 1.4 1998/03/03 01:23:41 ralf Exp $ + * Version: $Id: ip_output.c,v 1.5 1998/03/17 22:18:29 ralf Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index a500a72e5..1b7f44e8f 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -5,7 +5,7 @@ * * The IP to API glue. * - * Version: $Id: ip_sockglue.c,v 1.4 1998/03/03 01:23:41 ralf Exp $ + * Version: $Id: ip_sockglue.c,v 1.5 1998/03/17 22:18:29 ralf Exp $ * * Authors: see ip.c * diff --git a/net/ipv4/ipconfig.c b/net/ipv4/ipconfig.c index 20521e643..1e44ae8aa 100644 --- a/net/ipv4/ipconfig.c +++ b/net/ipv4/ipconfig.c @@ -1,5 +1,5 @@ /* - * $Id: ipconfig.c,v 1.6 1998/01/09 17:19:46 mj Exp $ + * $Id: ipconfig.c,v 1.11 1998/02/12 07:43:16 davem Exp $ * * Automatic Configuration of IP -- use BOOTP or RARP or user-supplied * information to configure own IP address and routes. diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index 949661f41..ce071d406 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -1,7 +1,7 @@ /* * Linux NET3: IP/IP protocol decoder. * - * Version: $Id: ipip.c,v 1.4 1997/12/16 05:37:42 ralf Exp $ + * Version: $Id: ipip.c,v 1.5 1998/03/17 22:18:30 ralf Exp $ * * Authors: * Sam Lantinga (slouken@cs.ucdavis.edu) 02/01/95 diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index d3c07dca3..1177f33ac 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -9,7 +9,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * Version: $Id: ipmr.c,v 1.29 1997/12/13 21:52:55 kuznet Exp $ + * Version: $Id: ipmr.c,v 1.4 1998/03/17 22:18:31 ralf Exp $ * * Fixes: * Michael Chastain : Incorrect size of copying. @@ -1351,6 +1351,7 @@ ipmr_fill_mroute(struct sk_buff *skb, struct mfc_cache *c, struct rtmsg *rtm) int ct; struct rtnexthop *nhp; struct device *dev = vif_table[c->mfc_parent].dev; + u8 *b = skb->tail; #ifdef CONFIG_RTNL_OLD_IFINFO if (dev) { @@ -1389,10 +1390,11 @@ ipmr_fill_mroute(struct sk_buff *skb, struct mfc_cache *c, struct rtmsg *rtm) return 1; rtattr_failure: + skb_trim(skb, b - skb->data); return -EMSGSIZE; } -int ipmr_get_route(struct sk_buff *skb, struct rtmsg *rtm) +int ipmr_get_route(struct sk_buff *skb, struct rtmsg *rtm, int nowait) { struct mfc_cache *cache; struct rtable *rt = (struct rtable*)skb->dst; @@ -1400,10 +1402,16 @@ int ipmr_get_route(struct sk_buff *skb, struct rtmsg *rtm) start_bh_atomic(); cache = ipmr_cache_find(rt->rt_src, rt->rt_dst); if (cache==NULL || (cache->mfc_flags&MFC_QUEUED)) { - struct device *dev = skb->dev; + struct device *dev; int vif; int err; + if (nowait) { + end_bh_atomic(); + return -EAGAIN; + } + + dev = skb->dev; if (dev == NULL || (vif = ipmr_find_vif(dev)) == ALL_VIFS) { end_bh_atomic(); return -ENODEV; @@ -1422,7 +1430,7 @@ int ipmr_get_route(struct sk_buff *skb, struct rtmsg *rtm) */ end_bh_atomic(); - if (rtm->rtm_flags & RTM_F_NOTIFY) + if (!nowait && (rtm->rtm_flags&RTM_F_NOTIFY)) cache->mfc_flags |= MFC_NOTIFY; return ipmr_fill_mroute(skb, cache, rtm); } diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index 7f3b5f9bb..221207205 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -7,7 +7,7 @@ * PROC file system. It is mainly used for debugging and * statistics. * - * Version: $Id: proc.c,v 1.23 1997/10/30 23:52:20 davem Exp $ + * Version: $Id: proc.c,v 1.4 1997/12/16 05:37:43 ralf Exp $ * * Authors: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> * Gerald J. Heim, <heim@peanuts.informatik.uni-tuebingen.de> @@ -77,11 +77,12 @@ static inline void get__sock(struct sock *sp, char *tmpbuf, int i, int format) unsigned long dest, src; unsigned short destp, srcp; int timer_active, timer_active1, timer_active2; + int tw_bucket = 0; unsigned long timer_expires; struct tcp_opt *tp = &sp->tp_pinfo.af_tcp; dest = sp->daddr; - src = sp->saddr; + src = sp->rcv_saddr; destp = sp->dummy_th.dest; srcp = sp->dummy_th.source; @@ -96,30 +97,47 @@ static inline void get__sock(struct sock *sp, char *tmpbuf, int i, int format) destp = ntohs(destp); srcp = ntohs(srcp); - timer_active1 = del_timer(&tp->retransmit_timer); - timer_active2 = del_timer(&sp->timer); - if (!timer_active1) tp->retransmit_timer.expires=0; - if (!timer_active2) sp->timer.expires=0; - timer_active=0; - timer_expires=(unsigned)-1; + if((format == 0) && (sp->state == TCP_TIME_WAIT)) { + struct tcp_tw_bucket *tw = (struct tcp_tw_bucket *)sp; + + tw_bucket = 1; + timer_active1 = timer_active2 = 0; + timer_active = 3; + timer_expires = tw->timer.expires; + } else { + timer_active1 = del_timer(&tp->retransmit_timer); + timer_active2 = del_timer(&sp->timer); + if (!timer_active1) tp->retransmit_timer.expires=0; + if (!timer_active2) sp->timer.expires=0; + timer_active = 0; + timer_expires = (unsigned) -1; + } if (timer_active1 && tp->retransmit_timer.expires < timer_expires) { - timer_active=timer_active1; - timer_expires=tp->retransmit_timer.expires; + timer_active = 1; + timer_expires = tp->retransmit_timer.expires; } if (timer_active2 && sp->timer.expires < timer_expires) { - timer_active=timer_active2; - timer_expires=sp->timer.expires; - } + timer_active = 2; + timer_expires = sp->timer.expires; + } + if(timer_active == 0) + timer_expires = jiffies; sprintf(tmpbuf, "%4d: %08lX:%04X %08lX:%04X" " %02X %08X:%08X %02X:%08lX %08X %5d %8d %ld", i, src, srcp, dest, destp, sp->state, - format==0?sp->write_seq-tp->snd_una:atomic_read(&sp->wmem_alloc), - format==0?tp->rcv_nxt-sp->copied_seq:atomic_read(&sp->rmem_alloc), - timer_active, timer_expires-jiffies, - tp->retransmits, - sp->socket ? sp->socket->inode->i_uid:0, - timer_active?sp->timeout:0, - sp->socket ? sp->socket->inode->i_ino:0); + (tw_bucket ? + 0 : + (format == 0) ? + tp->write_seq-tp->snd_una : atomic_read(&sp->wmem_alloc)), + (tw_bucket ? + 0 : + (format == 0) ? + tp->rcv_nxt-tp->copied_seq: atomic_read(&sp->rmem_alloc)), + timer_active, timer_expires-jiffies, + (tw_bucket ? 0 : tp->retransmits), + (!tw_bucket && sp->socket) ? sp->socket->inode->i_uid : 0, + (!tw_bucket && timer_active) ? sp->timeout : 0, + (!tw_bucket && sp->socket) ? sp->socket->inode->i_ino : 0); if (timer_active1) add_timer(&tp->retransmit_timer); if (timer_active2) add_timer(&sp->timer); diff --git a/net/ipv4/rarp.c b/net/ipv4/rarp.c index 9e944495f..e1eba43c5 100644 --- a/net/ipv4/rarp.c +++ b/net/ipv4/rarp.c @@ -3,7 +3,7 @@ * Copyright (C) 1994 by Ross Martin * Based on linux/net/inet/arp.c, Copyright (C) 1994 by Florian La Roche * - * $Id: rarp.c,v 1.3 1997/12/16 05:37:44 ralf Exp $ + * $Id: rarp.c,v 1.4 1998/03/17 22:18:31 ralf Exp $ * * This module implements the Reverse Address Resolution Protocol * (RARP, RFC 903), which is used to convert low level addresses such diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index b3644f10d..baebab777 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -5,7 +5,7 @@ * * RAW - implementation of IP "raw" sockets. * - * Version: $Id: raw.c,v 1.3 1997/12/16 05:37:44 ralf Exp $ + * Version: $Id: raw.c,v 1.4 1998/03/17 22:18:32 ralf Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> diff --git a/net/ipv4/route.c b/net/ipv4/route.c index b73c3ed11..8ce4a95f4 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -5,7 +5,7 @@ * * ROUTE - implementation of the IP router. * - * Version: $Id: route.c,v 1.4 1998/03/03 01:23:42 ralf Exp $ + * Version: $Id: route.c,v 1.5 1998/03/17 22:18:32 ralf Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -167,7 +167,7 @@ __u8 ip_tos2prio[16] = { static struct rtable *rt_hash_table[RT_HASH_DIVISOR]; -static struct rtable * rt_intern_hash(unsigned hash, struct rtable * rth, u16 protocol); +static struct rtable * rt_intern_hash(unsigned hash, struct rtable * rth); static __inline__ unsigned rt_hash_code(u32 daddr, u32 saddr, u8 tos) { @@ -301,6 +301,8 @@ static void rt_run_flush(unsigned long dummy) int i; struct rtable * rth, * next; + rt_deadline = 0; + for (i=0; i<RT_HASH_DIVISOR; i++) { int nr=0; @@ -322,37 +324,41 @@ static void rt_run_flush(unsigned long dummy) void rt_cache_flush(int delay) { + unsigned long now = jiffies; + int user_mode = !in_interrupt(); + if (delay < 0) delay = ip_rt_min_delay; start_bh_atomic(); if (del_timer(&rt_flush_timer) && delay > 0 && rt_deadline) { - long tmo = (long)(rt_deadline - rt_flush_timer.expires); + long tmo = (long)(rt_deadline - now); /* If flush timer is already running and flush request is not immediate (delay > 0): - if deadline is not achieved, prolongate timer to "dealy", + if deadline is not achieved, prolongate timer to "delay", otherwise fire it at deadline time. */ + if (user_mode && (long)(rt_deadline-now) < ip_rt_max_delay-ip_rt_min_delay) + tmo = 0; + if (delay > tmo) delay = tmo; } if (delay <= 0) { - rt_deadline = 0; end_bh_atomic(); - rt_run_flush(0); return; } if (rt_deadline == 0) - rt_deadline = jiffies + ip_rt_max_delay; + rt_deadline = now + ip_rt_max_delay; - rt_flush_timer.expires = jiffies + delay; + rt_flush_timer.expires = now + delay; add_timer(&rt_flush_timer); end_bh_atomic(); } @@ -400,7 +406,7 @@ out: return (atomic_read(&ipv4_dst_ops.entries) > ip_rt_max_size); } -static struct rtable *rt_intern_hash(unsigned hash, struct rtable * rt, u16 protocol) +static struct rtable *rt_intern_hash(unsigned hash, struct rtable * rt) { struct rtable *rth, **rthp; unsigned long now = jiffies; @@ -472,7 +478,9 @@ void ip_rt_redirect(u32 old_gw, u32 daddr, u32 new_gw, goto reject_redirect; if (!IN_DEV_SHARED_MEDIA(in_dev)) { - if (ip_fib_check_default(new_gw, dev)) + if (!inet_addr_onlink(in_dev, new_gw, old_gw)) + goto reject_redirect; + if (IN_DEV_SEC_REDIRECTS(in_dev) && ip_fib_check_default(new_gw, dev)) goto reject_redirect; } else { if (inet_addr_type(new_gw) != RTN_UNICAST) @@ -504,9 +512,13 @@ void ip_rt_redirect(u32 old_gw, u32 daddr, u32 new_gw, rth->u.dst.dev != dev) break; + dst_clone(&rth->u.dst); + rt = dst_alloc(sizeof(struct rtable), &ipv4_dst_ops); - if (rt == NULL) + if (rt == NULL) { + ip_rt_put(rth); return; + } /* * Copy all the information. @@ -531,14 +543,16 @@ void ip_rt_redirect(u32 old_gw, u32 daddr, u32 new_gw, if (rt->u.dst.neighbour) neigh_event_send(rt->u.dst.neighbour, NULL); ip_rt_put(rt); + ip_rt_put(rth); rt_free(rt); break; } *rthp = rth->u.rt_next; - rt_free(rth); - rt = rt_intern_hash(hash, rt, ETH_P_IP); + rt = rt_intern_hash(hash, rt); ip_rt_put(rt); + ip_rt_put(rth); + rt_free(rth); break; } } @@ -762,19 +776,45 @@ void ip_rt_get_source(u8 *addr, struct rtable *rt) u32 src; struct fib_result res; - if (rt->key.iif == 0) { - memcpy(addr, &rt->rt_src, 4); - return; - } - if (fib_lookup(&rt->key, &res) == 0) { + if (rt->key.iif == 0) + src = rt->rt_src; + else if (fib_lookup(&rt->key, &res) == 0) src = FIB_RES_PREFSRC(res); - memcpy(addr, &src, 4); - return; - } - src = inet_select_addr(rt->u.dst.dev, rt->rt_gateway, RT_SCOPE_UNIVERSE); + else + src = inet_select_addr(rt->u.dst.dev, rt->rt_gateway, RT_SCOPE_UNIVERSE); memcpy(addr, &src, 4); } +static void rt_set_nexthop(struct rtable *rt, struct fib_result *res) +{ + struct fib_info *fi = res->fi; + + if (fi) { + if (FIB_RES_GW(*res) && FIB_RES_NH(*res).nh_scope == RT_SCOPE_LINK) + rt->rt_gateway = FIB_RES_GW(*res); +#ifndef CONFIG_RTNL_OLD_IFINFO + rt->u.dst.mxlock = fi->fib_metrics[RTAX_LOCK-1]; + rt->u.dst.pmtu = fi->fib_mtu; + if (fi->fib_mtu == 0) { + rt->u.dst.pmtu = rt->u.dst.dev->mtu; + if (rt->u.dst.mxlock&(1<<RTAX_MTU) && + rt->rt_gateway != rt->rt_dst && + rt->u.dst.pmtu > 576) + rt->u.dst.pmtu = 576; + } +#else + rt->u.dst.pmtu = fi->fib_mtu ? : rt->u.dst.dev->mtu; +#endif + rt->u.dst.window= fi->fib_window ? : 0; + rt->u.dst.rtt = fi->fib_rtt ? : TCP_TIMEOUT_INIT; + } else { + rt->u.dst.pmtu = rt->u.dst.dev->mtu; + rt->u.dst.window= 0; + rt->u.dst.rtt = TCP_TIMEOUT_INIT; + } + rt->rt_type = res->type; +} + static int ip_route_input_mc(struct sk_buff *skb, u32 daddr, u32 saddr, u8 tos, struct device *dev, int our) @@ -832,7 +872,7 @@ ip_route_input_mc(struct sk_buff *skb, u32 daddr, u32 saddr, #endif hash = rt_hash_code(daddr, saddr^(dev->ifindex<<5), tos); - skb->dst = (struct dst_entry*)rt_intern_hash(hash, rth, 0); + skb->dst = (struct dst_entry*)rt_intern_hash(hash, rth); return 0; } @@ -990,18 +1030,9 @@ int ip_route_input_slow(struct sk_buff *skb, u32 daddr, u32 saddr, rth->u.dst.input = ip_forward; rth->u.dst.output = ip_output; - rth->u.dst.pmtu = res.fi->fib_mtu ? : out_dev->dev->mtu; - rth->u.dst.window=res.fi->fib_window ? : 0; - rth->u.dst.rtt = res.fi->fib_rtt ? : TCP_TIMEOUT_INIT; -#ifndef CONFIG_RTNL_OLD_IFINFO - rth->u.dst.mxlock = res.fi->fib_metrics[RTAX_LOCK-1]; -#endif - - if (FIB_RES_GW(res) && FIB_RES_NH(res).nh_scope == RT_SCOPE_LINK) - rth->rt_gateway = FIB_RES_GW(res); + rt_set_nexthop(rth, &res); rth->rt_flags = flags; - rth->rt_type = res.type; #ifdef CONFIG_NET_FASTROUTE if (netdev_fastroute && !(flags&(RTCF_NAT|RTCF_MASQ|RTCF_DOREDIRECT))) { @@ -1014,7 +1045,7 @@ int ip_route_input_slow(struct sk_buff *skb, u32 daddr, u32 saddr, } #endif - skb->dst = (struct dst_entry*)rt_intern_hash(hash, rth, ntohs(skb->protocol)); + skb->dst = (struct dst_entry*)rt_intern_hash(hash, rth); return 0; brd_input: @@ -1062,7 +1093,7 @@ local_input: } rth->rt_flags = flags|RTCF_LOCAL; rth->rt_type = res.type; - skb->dst = (struct dst_entry*)rt_intern_hash(hash, rth, 0); + skb->dst = (struct dst_entry*)rt_intern_hash(hash, rth); return 0; no_route: @@ -1362,7 +1393,7 @@ make_route: rth->rt_dst_map = key.dst; rth->rt_src_map = key.src; #endif - rth->rt_iif = dev_out->ifindex; + rth->rt_iif = oif ? : dev_out->ifindex; rth->u.dst.dev = dev_out; rth->rt_gateway = key.dst; rth->rt_spec_dst= key.src; @@ -1388,24 +1419,12 @@ make_route: #endif } - if (res.fi) { - if (FIB_RES_GW(res) && FIB_RES_NH(res).nh_scope == RT_SCOPE_LINK) - rth->rt_gateway = FIB_RES_GW(res); - rth->u.dst.pmtu = res.fi->fib_mtu ? : dev_out->mtu; - rth->u.dst.window=res.fi->fib_window ? : 0; - rth->u.dst.rtt = res.fi->fib_rtt ? : TCP_TIMEOUT_INIT; -#ifndef CONFIG_RTNL_OLD_IFINFO - rth->u.dst.mxlock = res.fi->fib_metrics[RTAX_LOCK-1]; -#endif - } else { - rth->u.dst.pmtu = dev_out->mtu; - rth->u.dst.window=0; - rth->u.dst.rtt = TCP_TIMEOUT_INIT; - } + rt_set_nexthop(rth, &res); + rth->rt_flags = flags; - rth->rt_type = res.type; + hash = rt_hash_code(daddr, saddr^(oif<<5), tos); - *rp = rt_intern_hash(hash, rth, ETH_P_IP); + *rp = rt_intern_hash(hash, rth); return 0; } @@ -1444,6 +1463,113 @@ int ip_route_output(struct rtable **rp, u32 daddr, u32 saddr, u32 tos, int oif) #ifdef CONFIG_RTNETLINK +static int rt_fill_info(struct sk_buff *skb, pid_t pid, u32 seq, int event, int nowait) +{ + struct rtable *rt = (struct rtable*)skb->dst; + struct rtmsg *r; + struct nlmsghdr *nlh; + unsigned char *b = skb->tail; + struct rta_cacheinfo ci; +#ifdef CONFIG_IP_MROUTE + struct rtattr *eptr; +#endif +#ifdef CONFIG_RTNL_OLD_IFINFO + unsigned char *o; +#else + struct rtattr *mx; +#endif + + nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*r)); + r = NLMSG_DATA(nlh); + nlh->nlmsg_flags = nowait ? NLM_F_MULTI : 0; + r->rtm_family = AF_INET; + r->rtm_dst_len = 32; + r->rtm_src_len = 32; + r->rtm_tos = rt->key.tos; + r->rtm_table = RT_TABLE_MAIN; + r->rtm_type = rt->rt_type; + r->rtm_scope = RT_SCOPE_UNIVERSE; + r->rtm_protocol = RTPROT_UNSPEC; + r->rtm_flags = (rt->rt_flags&~0xFFFF) | RTM_F_CLONED; +#ifdef CONFIG_RTNL_OLD_IFINFO + r->rtm_nhs = 0; + + o = skb->tail; +#endif + RTA_PUT(skb, RTA_DST, 4, &rt->rt_dst); + RTA_PUT(skb, RTA_SRC, 4, &rt->rt_src); + if (rt->u.dst.dev) + RTA_PUT(skb, RTA_OIF, sizeof(int), &rt->u.dst.dev->ifindex); + if (rt->rt_dst != rt->rt_gateway) + RTA_PUT(skb, RTA_GATEWAY, 4, &rt->rt_gateway); +#ifdef CONFIG_RTNL_OLD_IFINFO + RTA_PUT(skb, RTA_MTU, sizeof(unsigned), &rt->u.dst.pmtu); + RTA_PUT(skb, RTA_WINDOW, sizeof(unsigned), &rt->u.dst.window); + RTA_PUT(skb, RTA_RTT, sizeof(unsigned), &rt->u.dst.rtt); +#else + mx = (struct rtattr*)skb->tail; + RTA_PUT(skb, RTA_METRICS, 0, NULL); + if (rt->u.dst.mxlock) + RTA_PUT(skb, RTAX_LOCK, sizeof(unsigned), &rt->u.dst.mxlock); + if (rt->u.dst.pmtu) + RTA_PUT(skb, RTAX_MTU, sizeof(unsigned), &rt->u.dst.pmtu); + if (rt->u.dst.window) + RTA_PUT(skb, RTAX_WINDOW, sizeof(unsigned), &rt->u.dst.window); + if (rt->u.dst.rtt) + RTA_PUT(skb, RTAX_RTT, sizeof(unsigned), &rt->u.dst.rtt); + mx->rta_len = skb->tail - (u8*)mx; + if (mx->rta_len == RTA_LENGTH(0)) + skb_trim(skb, (u8*)mx - skb->data); +#endif + RTA_PUT(skb, RTA_PREFSRC, 4, &rt->rt_spec_dst); + ci.rta_lastuse = jiffies - rt->u.dst.lastuse; + ci.rta_used = atomic_read(&rt->u.dst.refcnt); + ci.rta_clntref = atomic_read(&rt->u.dst.use); + ci.rta_expires = 0; + ci.rta_error = rt->u.dst.error; +#ifdef CONFIG_IP_MROUTE + eptr = (struct rtattr*)skb->tail; +#endif + RTA_PUT(skb, RTA_CACHEINFO, sizeof(ci), &ci); +#ifdef CONFIG_RTNL_OLD_IFINFO + r->rtm_optlen = skb->tail - o; +#endif + if (rt->key.iif) { +#ifdef CONFIG_IP_MROUTE + u32 dst = rt->rt_dst; + + if (MULTICAST(dst) && !LOCAL_MCAST(dst) && ipv4_devconf.mc_forwarding) { + int err = ipmr_get_route(skb, r, nowait); + if (err <= 0) { + if (!nowait) { + if (err == 0) + return 0; + goto nlmsg_failure; + } else { + if (err == -EMSGSIZE) + goto nlmsg_failure; + ((struct rta_cacheinfo*)RTA_DATA(eptr))->rta_error = err; + } + } + } else +#endif + { + RTA_PUT(skb, RTA_IIF, sizeof(int), &rt->key.iif); +#ifdef CONFIG_RTNL_OLD_IFINFO + r->rtm_optlen = skb->tail - o; +#endif + } + } + + nlh->nlmsg_len = skb->tail - b; + return skb->len; + +nlmsg_failure: +rtattr_failure: + skb_trim(skb, b - skb->data); + return -1; +} + int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg) { struct rtattr **rta = arg; @@ -1454,12 +1580,6 @@ int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg) int iif = 0; int err; struct sk_buff *skb; - struct rta_cacheinfo ci; -#ifdef CONFIG_RTNL_OLD_IFINFO - unsigned char *o; -#else - struct rtattr *mx; -#endif skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); if (skb == NULL) @@ -1506,83 +1626,53 @@ int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg) if (rtm->rtm_flags & RTM_F_NOTIFY) rt->rt_flags |= RTCF_NOTIFY; - nlh = NLMSG_PUT(skb, NETLINK_CB(in_skb).pid, nlh->nlmsg_seq, - RTM_NEWROUTE, sizeof(*rtm)); - rtm = NLMSG_DATA(nlh); - nlh->nlmsg_flags = 0; - rtm->rtm_family = AF_INET; - rtm->rtm_dst_len = 32; - rtm->rtm_src_len = 32; - rtm->rtm_tos = rt->key.tos; - rtm->rtm_table = RT_TABLE_MAIN; - rtm->rtm_type = rt->rt_type; - rtm->rtm_scope = RT_SCOPE_UNIVERSE; - rtm->rtm_protocol = RTPROT_UNSPEC; - rtm->rtm_flags = (rt->rt_flags&~0xFFFF) | RTM_F_CLONED; -#ifdef CONFIG_RTNL_OLD_IFINFO - rtm->rtm_nhs = 0; + NETLINK_CB(skb).pid = NETLINK_CB(in_skb).pid; + + err = rt_fill_info(skb, NETLINK_CB(in_skb).pid, nlh->nlmsg_seq, RTM_NEWROUTE, 0); + if (err == 0) + return 0; + if (err < 0) + return -EMSGSIZE; - o = skb->tail; -#endif - RTA_PUT(skb, RTA_DST, 4, &rt->rt_dst); - RTA_PUT(skb, RTA_SRC, 4, &rt->rt_src); - if (rt->u.dst.dev) - RTA_PUT(skb, RTA_OIF, sizeof(int), &rt->u.dst.dev->ifindex); - if (rt->rt_dst != rt->rt_gateway) - RTA_PUT(skb, RTA_GATEWAY, 4, &rt->rt_gateway); -#ifdef CONFIG_RTNL_OLD_IFINFO - RTA_PUT(skb, RTA_MTU, sizeof(unsigned), &rt->u.dst.pmtu); - RTA_PUT(skb, RTA_WINDOW, sizeof(unsigned), &rt->u.dst.window); - RTA_PUT(skb, RTA_RTT, sizeof(unsigned), &rt->u.dst.rtt); -#else - mx = (struct rtattr*)skb->tail; - RTA_PUT(skb, RTA_METRICS, 0, NULL); - if (rt->u.dst.mxlock) - RTA_PUT(skb, RTAX_LOCK, sizeof(unsigned), &rt->u.dst.mxlock); - if (rt->u.dst.pmtu) - RTA_PUT(skb, RTAX_MTU, sizeof(unsigned), &rt->u.dst.pmtu); - if (rt->u.dst.window) - RTA_PUT(skb, RTAX_WINDOW, sizeof(unsigned), &rt->u.dst.window); - if (rt->u.dst.rtt) - RTA_PUT(skb, RTAX_RTT, sizeof(unsigned), &rt->u.dst.rtt); - mx->rta_len = skb->tail - (u8*)mx; -#endif - RTA_PUT(skb, RTA_PREFSRC, 4, &rt->rt_spec_dst); - ci.rta_lastuse = jiffies - rt->u.dst.lastuse; - ci.rta_used = atomic_read(&rt->u.dst.refcnt); - ci.rta_clntref = atomic_read(&rt->u.dst.use); - ci.rta_expires = 0; - ci.rta_error = rt->u.dst.error; - RTA_PUT(skb, RTA_CACHEINFO, sizeof(ci), &ci); -#ifdef CONFIG_RTNL_OLD_IFINFO - rtm->rtm_optlen = skb->tail - o; -#endif - if (iif) { -#ifdef CONFIG_IP_MROUTE - if (MULTICAST(dst) && !LOCAL_MCAST(dst) && ipv4_devconf.mc_forwarding) { - NETLINK_CB(skb).pid = NETLINK_CB(in_skb).pid; - err = ipmr_get_route(skb, rtm); - if (err <= 0) - return err; - } else -#endif - { - RTA_PUT(skb, RTA_IIF, sizeof(int), &iif); -#ifdef CONFIG_RTNL_OLD_IFINFO - rtm->rtm_optlen = skb->tail - o; -#endif - } - } - nlh->nlmsg_len = skb->tail - (u8*)nlh; err = netlink_unicast(rtnl, skb, NETLINK_CB(in_skb).pid, MSG_DONTWAIT); if (err < 0) return err; return 0; +} -nlmsg_failure: -rtattr_failure: - kfree_skb(skb); - return -EMSGSIZE; + +int ip_rt_dump(struct sk_buff *skb, struct netlink_callback *cb) +{ + struct rtable *rt; + int h, s_h; + int idx, s_idx; + + s_h = cb->args[0]; + s_idx = idx = cb->args[1]; + for (h=0; h < RT_HASH_DIVISOR; h++) { + if (h < s_h) continue; + if (h > s_h) + memset(&cb->args[1], 0, sizeof(cb->args) - sizeof(int)); + start_bh_atomic(); + for (rt = rt_hash_table[h], idx = 0; rt; rt = rt->u.rt_next, idx++) { + if (idx < s_idx) + continue; + skb->dst = dst_clone(&rt->u.dst); + if (rt_fill_info(skb, NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, RTM_NEWROUTE, 1) <= 0) { + dst_release(xchg(&skb->dst, NULL)); + end_bh_atomic(); + goto done; + } + dst_release(xchg(&skb->dst, NULL)); + } + end_bh_atomic(); + } + +done: + cb->args[0] = h; + cb->args[1] = idx; + return skb->len; } #endif /* CONFIG_RTNETLINK */ diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index 7d119716e..00dd0a8ef 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -9,7 +9,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * $Id: syncookies.c,v 1.3 1997/09/16 17:16:21 freitag Exp $ + * $Id: syncookies.c,v 1.4 1998/03/08 05:56:34 davem Exp $ * * Missing: IPv6 support. * Some counter so that the Administrator can see when the machine diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 3a8a7efb4..767c5d00b 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -1,7 +1,7 @@ /* * sysctl_net_ipv4.c: sysctl interface to net IPV4 subsystem. * - * $Id: sysctl_net_ipv4.c,v 1.6 1998/03/03 01:23:42 ralf Exp $ + * $Id: sysctl_net_ipv4.c,v 1.7 1998/03/17 22:18:33 ralf Exp $ * * Begun April 1, 1996, Mike Shaver. * Added /proc/sys/net/ipv4 directory entry (empty =) ). [MS] @@ -45,8 +45,6 @@ extern int sysctl_ip_masq_debug; extern int sysctl_tcp_cong_avoidance; extern int sysctl_tcp_hoe_retransmits; -extern int sysctl_tcp_sack; -extern int sysctl_tcp_tsack; extern int sysctl_tcp_timestamps; extern int sysctl_tcp_window_scaling; extern int sysctl_tcp_keepalive_time; @@ -57,7 +55,8 @@ extern int sysctl_tcp_retries2; extern int sysctl_tcp_fin_timeout; extern int sysctl_tcp_syncookies; extern int sysctl_tcp_syn_retries; -extern int sysctl_tcp_stdurg; +extern int sysctl_tcp_stdurg; +extern int sysctl_tcp_rfc1337; extern int sysctl_tcp_syn_taildrop; extern int sysctl_max_syn_backlog; @@ -99,12 +98,6 @@ ctl_table ipv4_table[] = { {NET_IPV4_TCP_HOE_RETRANSMITS, "tcp_hoe_retransmits", &sysctl_tcp_hoe_retransmits, sizeof(int), 0644, NULL, &proc_dointvec}, - {NET_IPV4_TCP_SACK, "tcp_sack", - &sysctl_tcp_sack, sizeof(int), 0644, NULL, - &proc_dointvec}, - {NET_IPV4_TCP_TSACK, "tcp_tsack", - &sysctl_tcp_tsack, sizeof(int), 0644, NULL, - &proc_dointvec}, {NET_IPV4_TCP_TIMESTAMPS, "tcp_timestamps", &sysctl_tcp_timestamps, sizeof(int), 0644, NULL, &proc_dointvec}, @@ -162,6 +155,8 @@ ctl_table ipv4_table[] = { #endif {NET_TCP_STDURG, "tcp_stdurg", &sysctl_tcp_stdurg, sizeof(int), 0644, NULL, &proc_dointvec}, + {NET_TCP_RFC1337, "tcp_rfc1337", &sysctl_tcp_rfc1337, + sizeof(int), 0644, NULL, &proc_dointvec}, {NET_TCP_SYN_TAILDROP, "tcp_syn_taildrop", &sysctl_tcp_syn_taildrop, sizeof(int), 0644, NULL, &proc_dointvec}, {NET_TCP_MAX_SYN_BACKLOG, "tcp_max_syn_backlog", &sysctl_max_syn_backlog, diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 17ec6def9..b20df83d2 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp.c,v 1.77 1998/01/15 22:40:18 freitag Exp $ + * Version: $Id: tcp.c,v 1.96 1998/03/16 02:25:55 davem Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -169,7 +169,7 @@ * Fixed tcp_write_timeout: stuck close, * and TCP syn retries gets used now. * Mark Yarvis : In tcp_read_wakeup(), don't send an - * ack if stat is TCP_CLOSED. + * ack if state is TCP_CLOSED. * Alan Cox : Look up device on a retransmit - routes may * change. Doesn't yet cope with MSS shrink right * but its a start! @@ -425,6 +425,8 @@ int sysctl_tcp_fin_timeout = TCP_FIN_TIMEOUT; struct tcp_mib tcp_statistics; kmem_cache_t *tcp_openreq_cachep; +kmem_cache_t *tcp_bucket_cachep; +kmem_cache_t *tcp_timewait_cachep; /* * Find someone to 'accept'. Must be called with @@ -478,20 +480,6 @@ static void tcp_close_pending (struct sock *sk) } /* - * Enter the time wait state. - */ - -void tcp_time_wait(struct sock *sk) -{ - tcp_set_state(sk,TCP_TIME_WAIT); - sk->shutdown = SHUTDOWN_MASK; - if (!sk->dead) - sk->state_change(sk); - tcp_reset_msl_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); -} - - -/* * Walk down the receive queue counting readable data. * * Must be called with the socket lock held. @@ -512,7 +500,7 @@ static int tcp_readable(struct sock *sk) return(0); } - counted = sk->copied_seq; /* Where we are at the moment */ + counted = sk->tp_pinfo.af_tcp.copied_seq; /* Where we are at the moment */ amount = 0; /* Do until a push or until we are out of data. */ @@ -606,10 +594,10 @@ unsigned int tcp_poll(struct file * file, struct socket *sock, poll_table *wait) if (sk->shutdown & RCV_SHUTDOWN) mask |= POLLHUP; - if ((tp->rcv_nxt != sk->copied_seq) && - (sk->urg_seq != sk->copied_seq || - tp->rcv_nxt != sk->copied_seq+1 || - sk->urginline || !sk->urg_data)) + if ((tp->rcv_nxt != tp->copied_seq) && + (tp->urg_seq != tp->copied_seq || + tp->rcv_nxt != tp->copied_seq+1 || + sk->urginline || !tp->urg_data)) mask |= POLLIN | POLLRDNORM; #if 1 /* This needs benchmarking and real world tests */ @@ -621,9 +609,9 @@ unsigned int tcp_poll(struct file * file, struct socket *sock, poll_table *wait) space = atomic_read(&sk->wmem_alloc) / 2; #endif /* Always wake the user up when an error occured */ - if (sock_wspace(sk) >= space) + if (sock_wspace(sk) >= space || sk->err) mask |= POLLOUT | POLLWRNORM; - if (sk->urg_data) + if (tp->urg_data) mask |= POLLPRI; } return mask; @@ -649,7 +637,8 @@ int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg) } case SIOCATMARK: { - int answ = sk->urg_data && sk->urg_seq == sk->copied_seq; + struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + int answ = tp->urg_data && tp->urg_seq == tp->copied_seq; return put_user(answ,(int *) arg); } case TIOCOUTQ: @@ -669,21 +658,38 @@ int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg) /* * Wait for a socket to get into the connected state */ -static void wait_for_tcp_connect(struct sock * sk) +static int wait_for_tcp_connect(struct sock * sk, int flags) { struct task_struct *tsk = current; struct wait_queue wait = { tsk, NULL }; - tsk->state = TASK_INTERRUPTIBLE; - add_wait_queue(sk->sleep, &wait); - release_sock(sk); + while((1 << sk->state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)) { + if(sk->err) + return sock_error(sk); + if((1 << sk->state) & + ~(TCPF_SYN_SENT | TCPF_SYN_RECV)) { + if(sk->keepopen) + send_sig(SIGPIPE, tsk, 0); + return -EPIPE; + } + if(flags & MSG_DONTWAIT) + return -EAGAIN; + if(signal_pending(tsk)) + return -ERESTARTSYS; - if (((1 << sk->state) & ~(TCPF_ESTABLISHED|TCPF_CLOSE_WAIT)) && sk->err == 0) - schedule(); + tsk->state = TASK_INTERRUPTIBLE; + add_wait_queue(sk->sleep, &wait); + release_sock(sk); - tsk->state = TASK_RUNNING; - remove_wait_queue(sk->sleep, &wait); - lock_sock(sk); + if (((1 << sk->state) & ~(TCPF_ESTABLISHED|TCPF_CLOSE_WAIT)) && + sk->err == 0) + schedule(); + + tsk->state = TASK_RUNNING; + remove_wait_queue(sk->sleep, &wait); + lock_sock(sk); + } + return 0; } static inline int tcp_memory_free(struct sock *sk) @@ -720,32 +726,6 @@ static void wait_for_tcp_memory(struct sock * sk) lock_sock(sk); } - -static int tcp_append_tail(struct sock *sk, struct sk_buff *skb, u8 *from, - int tcp_size, int seglen) -{ - int fault; - int copy; - - /* Add more stuff to the end of the skb. */ - copy = min(sk->mss - tcp_size, skb_tailroom(skb)); - copy = min(copy, seglen); - - tcp_size += copy; - - fault = copy_from_user(skb->tail, from, copy); - if (fault) - return -1; - - skb_put(skb, copy); - skb->csum = csum_partial(skb->tail - tcp_size, tcp_size, 0); - - sk->write_seq += copy; - skb->end_seq += copy; - - return copy; -} - /* * This routine copies from a user buffer into a socket, * and starts the transmit system. @@ -758,24 +738,9 @@ int tcp_do_sendmsg(struct sock *sk, int iovlen, struct iovec *iov, int flags) struct tcp_opt *tp=&(sk->tp_pinfo.af_tcp); /* Wait for a connection to finish. */ - while ((1 << sk->state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)) { - if (sk->err) - return sock_error(sk); - - if ((1 << sk->state) & ~(TCPF_SYN_SENT | TCPF_SYN_RECV)) { - if (sk->keepopen) - send_sig(SIGPIPE, current, 0); - return -EPIPE; - } - - if (flags&MSG_DONTWAIT) - return -EAGAIN; - - if (signal_pending(current)) - return -ERESTARTSYS; - - wait_for_tcp_connect(sk); - } + if ((1 << sk->state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)) + if((err = wait_for_tcp_connect(sk, flags)) != 0) + return err; /* Ok commence sending. */ while(--iovlen >= 0) { @@ -785,41 +750,28 @@ int tcp_do_sendmsg(struct sock *sk, int iovlen, struct iovec *iov, int flags) iov++; while(seglen > 0) { - unsigned int actual_win; - int copy; - int tmp; + int copy, tmp, queue_it; struct sk_buff *skb; if (err) return -EFAULT; /* Stop on errors. */ - if (sk->err) { - if (copied) - return copied; - return sock_error(sk); - } + if (sk->err) + goto do_sock_err; /* Make sure that we are established. */ - if (sk->shutdown & SEND_SHUTDOWN) { - if (copied) - return copied; - send_sig(SIGPIPE,current,0); - return -EPIPE; - } + if (sk->shutdown & SEND_SHUTDOWN) + goto do_shutdown; - /* Now we need to check if we have a half built packet. */ - - /* If we have queued packets.. */ + /* Now we need to check if we have a half + * built packet we can tack some data onto. + */ if (tp->send_head && !(flags & MSG_OOB)) { - int tcp_size; - - /* Tail */ - skb = sk->write_queue.prev; - tcp_size = skb->tail - - ((unsigned char *)(skb->h.th) + tp->tcp_header_len); - + copy = skb->tail - + ((unsigned char *)(skb->h.th) + + tp->tcp_header_len); /* This window_seq test is somewhat dangerous * If the remote does SWS avoidance we should * queue the best we can if not we should in @@ -827,79 +779,92 @@ int tcp_do_sendmsg(struct sock *sk, int iovlen, struct iovec *iov, int flags) * a method for detecting this would be most * welcome */ - if (skb->end > skb->tail && - sk->mss - tcp_size > 0 && + if (skb_tailroom(skb) > 0 && + (sk->mss - copy) > 0 && tp->snd_nxt < skb->end_seq) { - int tcopy; - - tcopy = tcp_append_tail(sk, skb, from, - tcp_size, - seglen); - if (tcopy == -1) - return -EFAULT; - - from += tcopy; - copied += tcopy; - seglen -= tcopy; - - /* FIXME: if we're nagling we - * should send here. - */ + int last_byte_was_odd = (copy & 1); + + copy = sk->mss - copy; + if(copy > skb_tailroom(skb)) + copy = skb_tailroom(skb); + if(copy > seglen) + copy = seglen; + if(last_byte_was_odd) { + if(copy_from_user(skb_put(skb, copy), + from, copy)) + err = -EFAULT; + skb->csum = csum_partial( + (((unsigned char *)skb->h.th) + + tp->tcp_header_len), + (skb->tail - + (((unsigned char *)skb->h.th) + + tp->tcp_header_len)), 0); + } else { + skb->csum = + csum_and_copy_from_user( + from, skb_put(skb, copy), + copy, skb->csum, &err); + } + tp->write_seq += copy; + skb->end_seq += copy; + from += copy; + copied += copy; + seglen -= copy; continue; } } - /* We also need to worry about the window. - * If window < 1/2 the maximum window we've seen from this - * host, don't use it. This is sender side - * silly window prevention, as specified in RFC1122. - * (Note that this is different than earlier versions of - * SWS prevention, e.g. RFC813.). What we actually do is - * use the whole MSS. Since the results in the right - * edge of the packet being outside the window, it will - * be queued for later rather than sent. + /* We also need to worry about the window. If + * window < 1/2 the maximum window we've seen + * from this host, don't use it. This is + * sender side silly window prevention, as + * specified in RFC1122. (Note that this is + * different than earlier versions of SWS + * prevention, e.g. RFC813.). What we + * actually do is use the whole MSS. Since + * the results in the right edge of the packet + * being outside the window, it will be queued + * for later rather than sent. */ - copy = min(seglen, sk->mss); - actual_win = tp->snd_wnd - (tp->snd_nxt - tp->snd_una); - - if (copy > actual_win && - (((int) actual_win) >= (tp->max_window >> 1)) && - actual_win) - copy = actual_win; - - if (copy <= 0) { - printk(KERN_DEBUG "sendmsg: copy < 0\n"); - return -EIO; - } + copy = tp->snd_wnd - (tp->snd_nxt - tp->snd_una); + if(copy >= (tp->max_window >> 1)) + copy = min(copy, sk->mss); + else + copy = sk->mss; + if(copy > seglen) + copy = seglen; - /* If tp->packets_out > 0 segment will be nagled - * else we kick it right away. - */ - tmp = MAX_HEADER + sk->prot->max_header + + tmp = MAX_HEADER + sk->prot->max_header + sizeof(struct sk_buff) + 15; - if (copy < min(sk->mss, tp->max_window >> 1) && - !(flags & MSG_OOB) && tp->packets_out) + queue_it = 0; + if (copy < min(sk->mss, tp->max_window >> 1) && + !(flags & MSG_OOB)) { tmp += min(sk->mss, tp->max_window); - else - tmp += copy; + /* What is happening here is that we want to + * tack on later members of the users iovec + * if possible into a single frame. When we + * leave this loop our caller checks to see if + * we can send queued frames onto the wire. + * See tcp_v[46]_sendmsg() for this. + */ + queue_it = 1; + } else { + tmp += copy; + } skb = sock_wmalloc(sk, tmp, 0, GFP_KERNEL); /* If we didn't get any memory, we need to sleep. */ if (skb == NULL) { sk->socket->flags |= SO_NOSPACE; if (flags&MSG_DONTWAIT) { - if (copied) - return copied; - return -EAGAIN; + err = -EAGAIN; + goto do_interrupted; } - if (signal_pending(current)) { - if (copied) - return copied; - return -ERESTARTSYS; + err = -ERESTARTSYS; + goto do_interrupted; } - wait_for_tcp_memory(sk); continue; } @@ -910,9 +875,8 @@ int tcp_do_sendmsg(struct sock *sk, int iovlen, struct iovec *iov, int flags) tmp = tp->af_specific->build_net_header(sk, skb); if (tmp < 0) { kfree_skb(skb); - if (copied) - return(copied); - return(tmp); + err = tmp; + goto do_interrupted; } skb->h.th =(struct tcphdr *) @@ -920,7 +884,6 @@ int tcp_do_sendmsg(struct sock *sk, int iovlen, struct iovec *iov, int flags) seglen -= copy; tcp_build_header_data(skb->h.th, sk, seglen || iovlen); - /* FIXME: still need to think about SACK options here. */ if (flags & MSG_OOB) { skb->h.th->urg = 1; @@ -933,21 +896,29 @@ int tcp_do_sendmsg(struct sock *sk, int iovlen, struct iovec *iov, int flags) from += copy; copied += copy; - sk->write_seq += copy; + tp->write_seq += copy; - tcp_send_skb(sk, skb); - - release_sock(sk); - lock_sock(sk); + tcp_send_skb(sk, skb, queue_it); } } - sk->err = 0; - if (err) return -EFAULT; - return copied; + +do_sock_err: + if(copied) + return copied; + return sock_error(sk); +do_shutdown: + if(copied) + return copied; + send_sig(SIGPIPE, current, 0); + return -EPIPE; +do_interrupted: + if(copied) + return copied; + return err; } /* @@ -980,7 +951,7 @@ static int tcp_recv_urg(struct sock * sk, int nonblock, struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); /* No URG data to read. */ - if (sk->urginline || !sk->urg_data || sk->urg_data == URG_READ) + if (sk->urginline || !tp->urg_data || tp->urg_data == URG_READ) return -EINVAL; /* Yes this is right ! */ if (sk->err) @@ -1000,18 +971,10 @@ static int tcp_recv_urg(struct sock * sk, int nonblock, } lock_sock(sk); - if (sk->urg_data & URG_VALID) { - char c = sk->urg_data; + if (tp->urg_data & URG_VALID) { + char c = tp->urg_data; if (!(flags & MSG_PEEK)) - sk->urg_data = URG_READ; - - if(len>0) - { - err = memcpy_toiovec(msg->msg_iov, &c, 1); - msg->msg_flags|=MSG_OOB; - } - else - msg->msg_flags|=MSG_TRUNC; + tp->urg_data = URG_READ; if(msg->msg_name) tp->af_specific->addr2sockaddr(sk, (struct sockaddr *) @@ -1023,6 +986,15 @@ static int tcp_recv_urg(struct sock * sk, int nonblock, /* Read urgent data. */ msg->msg_flags|=MSG_OOB; release_sock(sk); + + if(len>0) + { + err = memcpy_toiovec(msg->msg_iov, &c, 1); + msg->msg_flags|=MSG_OOB; + } + else + msg->msg_flags|=MSG_TRUNC; + return err ? -EFAULT : 1; } release_sock(sk); @@ -1044,45 +1016,37 @@ static int tcp_recv_urg(struct sock * sk, int nonblock, static inline void tcp_eat_skb(struct sock *sk, struct sk_buff * skb) { - sk->tp_pinfo.af_tcp.delayed_acks++; - __skb_unlink(skb, &sk->receive_queue); kfree_skb(skb); } - -static void cleanup_rbuf(struct sock *sk) +/* Clean up the receive buffer for full frames taken by the user, + * then send an ACK if necessary. COPIED is the number of bytes + * tcp_recvmsg has given to the user so far, it speeds up the + * calculation of whether or not we must ACK for the sake of + * a window update. + */ +static void cleanup_rbuf(struct sock *sk, int copied) { struct sk_buff *skb; - struct tcp_opt *tp; /* NOTE! The socket must be locked, so that we don't get * a messed-up receive queue. */ while ((skb=skb_peek(&sk->receive_queue)) != NULL) { - if (!skb->used || atomic_read(&skb->users)>1) + if (!skb->used || atomic_read(&skb->users) > 1) break; tcp_eat_skb(sk, skb); } SOCK_DEBUG(sk, "sk->rspace = %lu\n", sock_rspace(sk)); - tp = &(sk->tp_pinfo.af_tcp); - - /* We send a ACK if the sender is blocked - * else let tcp_data deal with the acking policy. + /* We send an ACK if we can now advertise a non-zero window + * which has been raised "significantly". */ - if (tp->delayed_acks) { - __u32 rcv_wnd; - - /* FIXME: double check this rule, then check against - * other use of similar rules. Abtract if possible. - */ - rcv_wnd = tp->rcv_wnd - (tp->rcv_nxt - tp->rcv_wup); - - if ((rcv_wnd < sk->mss) && (sock_rspace(sk) > rcv_wnd)) - tcp_read_wakeup(sk); - } + if((copied > 0) && + (copied >= tcp_receive_window(&sk->tp_pinfo.af_tcp))) + tcp_read_wakeup(sk); } @@ -1100,7 +1064,7 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg, volatile u32 *seq; /* So gcc doesn't overoptimise */ unsigned long used; int err = 0; - int target = 1; /* Read at least this may bytes */ + int target = 1; /* Read at least this many bytes */ if (sk->state == TCP_LISTEN) return -ENOTCONN; @@ -1113,8 +1077,8 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg, * the multi-reader case neatly (memcpy_to/fromfs might be * inline and thus not flush cached variables otherwise). */ - peek_seq = sk->copied_seq; - seq = &sk->copied_seq; + peek_seq = tp->copied_seq; + seq = &tp->copied_seq; if (flags & MSG_PEEK) seq = &peek_seq; @@ -1129,7 +1093,7 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg, u32 offset; /* Are we at urgent data? Stop if we have read anything. */ - if (copied && sk->urg_data && sk->urg_seq == *seq) + if (copied && tp->urg_data && tp->urg_seq == *seq) break; /* We need to check signals first, to get correct SIGURG @@ -1200,7 +1164,7 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg, break; } - cleanup_rbuf(sk); + cleanup_rbuf(sk, copied); release_sock(sk); sk->socket->flags |= SO_WAITDATA; schedule(); @@ -1222,8 +1186,8 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg, used = len; /* Do we have urgent data here? */ - if (sk->urg_data) { - u32 urg_offset = sk->urg_seq - *seq; + if (tp->urg_data) { + u32 urg_offset = tp->urg_seq - *seq; if (urg_offset < used) { if (!urg_offset) { if (!sk->urginline) { @@ -1264,8 +1228,8 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg, */ atomic_dec(&skb->users); - if (after(sk->copied_seq,sk->urg_seq)) - sk->urg_data = 0; + if (after(tp->copied_seq,tp->urg_seq)) + tp->urg_data = 0; if (used + offset < skb->len) continue; @@ -1303,7 +1267,7 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg, current->state = TASK_RUNNING; /* Clean up data we have read: This will do ACK frames. */ - cleanup_rbuf(sk); + cleanup_rbuf(sk, copied); release_sock(sk); return copied; } @@ -1356,8 +1320,7 @@ static int tcp_close_state(struct sock *sk, int dead) * reset mistake. */ if(dead && ns==TCP_FIN_WAIT2) { - int timer_active=del_timer(&sk->timer); - if(timer_active) + if(sk->timer.prev && del_timer(&sk->timer)) add_timer(&sk->timer); else tcp_reset_msl_timer(sk, TIME_CLOSE, sysctl_tcp_fin_timeout); @@ -1410,6 +1373,7 @@ static inline int closing(struct sock * sk) void tcp_close(struct sock *sk, unsigned long timeout) { struct sk_buff *skb; + int data_was_unread = 0; /* We need to grab some memory, and put together a FIN, * and then put it into the queue to be sent. @@ -1421,7 +1385,6 @@ void tcp_close(struct sock *sk, unsigned long timeout) tcp_close_pending(sk); release_sock(sk); sk->dead = 1; - sk->prot->unhash(sk); return; } @@ -1435,14 +1398,30 @@ void tcp_close(struct sock *sk, unsigned long timeout) * descriptor close, not protocol-sourced closes, because the * reader process may not have drained the data yet! */ - while((skb=skb_dequeue(&sk->receive_queue))!=NULL) + while((skb=skb_dequeue(&sk->receive_queue))!=NULL) { + data_was_unread++; kfree_skb(skb); + } - /* Timeout is not the same thing - however the code likes - * to send both the same way (sigh). + /* As outlined in draft-ietf-tcpimpl-prob-03.txt, section + * 3.10, we send a RST here because data was lost. To + * witness the awful effects of the old behavior of always + * doing a FIN, run an older 2.1.x kernel or 2.0.x, start + * a bulk GET in an FTP client, suspend the process, wait + * for the client to advertise a zero window, then kill -9 + * the FTP client, wheee... Note: timeout is always zero + * in such a case. */ - if (tcp_close_state(sk,1)==1) + if(data_was_unread != 0) { + /* Unread data was tossed, zap the connection. */ + tcp_set_state(sk, TCP_CLOSE); + tcp_send_active_reset(sk); + } else if (tcp_close_state(sk,1)) { + /* We FIN if the application ate all the data before + * zapping the connection. + */ tcp_send_fin(sk); + } if (timeout) { struct task_struct *tsk = current; @@ -1470,8 +1449,7 @@ void tcp_close(struct sock *sk, unsigned long timeout) * we may need to set up a timer. */ if (sk->state==TCP_FIN_WAIT2) { - int timer_active=del_timer(&sk->timer); - if(timer_active) + if(sk->timer.prev && del_timer(&sk->timer)) add_timer(&sk->timer); else tcp_reset_msl_timer(sk, TIME_CLOSE, sysctl_tcp_fin_timeout); @@ -1479,9 +1457,6 @@ void tcp_close(struct sock *sk, unsigned long timeout) sk->dead = 1; release_sock(sk); - - if(sk->state == TCP_CLOSE) - sk->prot->unhash(sk); } /* @@ -1538,13 +1513,12 @@ struct sock *tcp_accept(struct sock *sk, int flags) /* If this is a non blocking socket don't sleep */ error = EAGAIN; if (flags & O_NONBLOCK) - goto out; + goto out; error = ERESTARTSYS; req = wait_for_connect(sk, &prev); if (!req) - goto out; - error = 0; + goto out; } tcp_synq_unlink(tp, req, prev); @@ -1647,9 +1621,23 @@ void tcp_set_keepalive(struct sock *sk, int val) __initfunc(void tcp_init(void)) { tcp_openreq_cachep = kmem_cache_create("tcp_open_request", - sizeof(struct open_request), + sizeof(struct open_request), 0, SLAB_HWCACHE_ALIGN, NULL, NULL); if(!tcp_openreq_cachep) panic("tcp_init: Cannot alloc open_request cache."); + + tcp_bucket_cachep = kmem_cache_create("tcp_bind_bucket", + sizeof(struct tcp_bind_bucket), + 0, SLAB_HWCACHE_ALIGN, + NULL, NULL); + if(!tcp_bucket_cachep) + panic("tcp_init: Cannot alloc tcp_bind_bucket cache."); + + tcp_timewait_cachep = kmem_cache_create("tcp_tw_bucket", + sizeof(struct tcp_tw_bucket), + 0, SLAB_HWCACHE_ALIGN, + NULL, NULL); + if(!tcp_timewait_cachep) + panic("tcp_init: Cannot alloc tcp_tw_bucket cache."); } diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 841359739..4b7dcc9e9 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_input.c,v 1.66 1998/01/15 22:40:29 freitag Exp $ + * Version: $Id: tcp_input.c,v 1.84 1998/03/15 03:23:20 davem Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -67,57 +67,54 @@ static void tcp_cong_avoid_vegas(struct sock *sk, u32 seq, u32 ack, extern int sysctl_tcp_fin_timeout; +/* These are on by default so the code paths get tested. + * For the final 2.2 this may be undone at our discretion. -DaveM + */ +int sysctl_tcp_timestamps = 1; +int sysctl_tcp_window_scaling = 1; + int sysctl_tcp_cong_avoidance; int sysctl_tcp_hoe_retransmits; -int sysctl_tcp_sack; -int sysctl_tcp_tsack; -int sysctl_tcp_timestamps; -int sysctl_tcp_window_scaling; int sysctl_tcp_syncookies = SYNC_INIT; int sysctl_tcp_stdurg; +int sysctl_tcp_rfc1337; static tcp_sys_cong_ctl_t tcp_sys_cong_ctl_f = &tcp_cong_avoid_vanj; -/* - * Called each time to estimate the delayed ack timeout. This is - * how it should be done so a fast link isnt impacted by ack delay. - * - * I think we need a medium deviation here also... - * The estimated value is changing to fast +/* There is something which you must keep in mind when you analyze the + * behavior of the tp->ato delayed ack timeout interval. When a + * connection starts up, we want to ack as quickly as possible. The + * problem is that "good" TCP's do slow start at the beginning of data + * transmission. The means that until we send the first few ACK's the + * sender will sit on his end and only queue most of his data, because + * he can only send snd_cwnd unacked packets at any given time. For + * each ACK we send, he increments snd_cwnd and transmits more of his + * queue. -DaveM */ - static void tcp_delack_estimator(struct tcp_opt *tp) { - int m; - - /* Delayed ACK time estimator. */ - - m = jiffies - tp->lrcvtime; - - tp->lrcvtime = jiffies; + if(tp->ato == 0) { + tp->lrcvtime = jiffies; - if (m < 0) - return; - - /* if the mesured value is bigger than - * twice the round trip time ignore it. - */ - if ((m << 2) <= tp->srtt) { - m -= (tp->iat >> 3); - tp->iat += m; - - if (m <0) - m = -m; - - m -= (tp->iat_mdev >> 2); - tp->iat_mdev += m; + /* Help sender leave slow start quickly, + * this sets our initial ato value. + */ + tcp_enter_quickack_mode(tp); + } else { + int m = jiffies - tp->lrcvtime; - tp->ato = (tp->iat >> 3) + (tp->iat_mdev >> 2); + tp->lrcvtime = jiffies; + if(m <= 0) + m = 1; + if(m > tp->rto) + tp->ato = tp->rto; + else + tp->ato = (tp->ato >> 1) + m; - if (tp->ato < HZ/50) - tp->ato = HZ/50; - } else - tp->ato = 0; + /* We are not in "quick ack" mode. */ + if(tp->ato <= (HZ/100)) + tp->ato = ((HZ/100)*2); + } } /* Called to compute a smoothed rtt estimate. The data fed to this @@ -132,9 +129,9 @@ static void tcp_delack_estimator(struct tcp_opt *tp) static __inline__ void tcp_rtt_estimator(struct tcp_opt *tp, __u32 mrtt) { - long m; - /* - * The following amusing code comes from Jacobson's + long m = mrtt; /* RTT */ + + /* The following amusing code comes from Jacobson's * article in SIGCOMM '88. Note that rtt and mdev * are scaled versions of rtt and mean deviation. * This is designed to be as fast as possible @@ -143,12 +140,9 @@ static __inline__ void tcp_rtt_estimator(struct tcp_opt *tp, __u32 mrtt) * On a 1990 paper the rto value is changed to: * RTO = rtt + 4 * mdev */ - - m = mrtt; /* RTT */ - + if(m == 0) + m = 1; if (tp->srtt != 0) { - if(m<=0) - m=1; /* IS THIS RIGHT FOR <0 ??? */ m -= (tp->srtt >> 3); /* m is now error in rtt est */ tp->srtt += m; /* rtt = 7/8 rtt + 1/8 new */ if (m < 0) @@ -202,19 +196,17 @@ extern __inline__ void tcp_replace_ts_recent(struct tcp_opt *tp, __u32 end_seq) */ if (!before(end_seq,tp->last_ack_sent)) { tp->ts_recent = tp->rcv_tsval; - /* FIXME: need a corse timestamp. Days uptime - * would be good. - */ tp->ts_recent_stamp = jiffies; } } +#define PAWS_24DAYS (HZ * 60 * 60 * 24 * 24) + extern __inline__ int tcp_paws_discard(struct tcp_opt *tp) { - /* FIXME: must check that ts_recent is not - * more than 24 days old here. Yuck. - */ - return ((s32)(tp->rcv_tsval-tp->ts_recent) < 0); + /* ts_recent must be younger than 24 days */ + return (((jiffies - tp->ts_recent_stamp) >= PAWS_24DAYS) || + ((s32)(tp->rcv_tsval-tp->ts_recent) < 0)); } @@ -257,8 +249,6 @@ static void tcp_reset(struct sock *sk, struct sk_buff *skb) /* We want the right error as BSD sees it (and indeed as we do). */ switch (sk->state) { - case TCP_TIME_WAIT: - break; case TCP_SYN_SENT: sk->err = ECONNREFUSED; break; @@ -268,23 +258,8 @@ static void tcp_reset(struct sock *sk, struct sk_buff *skb) default: sk->err = ECONNRESET; }; -#ifdef CONFIG_TCP_RFC1337 - /* - * Time wait assassination protection [RFC1337] - * - * This is a good idea, but causes more sockets to take time to close. - * - * Ian Heavens has since shown this is an inadequate fix for the protocol - * bug in question. - */ - if(sk->state!=TCP_TIME_WAIT) { - tcp_set_state(sk,TCP_CLOSE); - sk->shutdown = SHUTDOWN_MASK; - } -#else tcp_set_state(sk,TCP_CLOSE); sk->shutdown = SHUTDOWN_MASK; -#endif if (!sk->dead) sk->state_change(sk); } @@ -302,7 +277,6 @@ void tcp_parse_options(struct tcphdr *th, struct tcp_opt *tp, int no_fancy) int length=(th->doff*4)-sizeof(struct tcphdr); ptr = (unsigned char *)(th + 1); - tp->sacks = 0; tp->saw_tstamp = 0; while(length>0) { @@ -336,10 +310,6 @@ void tcp_parse_options(struct tcphdr *th, struct tcp_opt *tp, int no_fancy) tp->snd_wscale = *(__u8 *)ptr; } break; - case TCPOPT_SACK_PERM: - if(opsize==TCPOLEN_SACK_PERM && th->syn) - if (sysctl_tcp_sack && !no_fancy) - tp->sack_ok = 1; case TCPOPT_TIMESTAMP: if(opsize==TCPOLEN_TIMESTAMP) { /* Cheaper to set again then to @@ -353,18 +323,6 @@ void tcp_parse_options(struct tcphdr *th, struct tcp_opt *tp, int no_fancy) } } break; - case TCPOPT_SACK: - if (no_fancy || !sysctl_tcp_sack) - break; - tp->sacks = (opsize-2)>>3; - if (tp->sacks<<3 == opsize-2) { - int i; - for (i = 0; i < tp->sacks; i++) { - tp->left_sack[i] = ntohl(((__u32 *)ptr)[2*i]); - tp->right_sack[i] = ntohl(((__u32 *)ptr)[2*i+1]); - } - } else - tp->sacks = 0; } ptr+=opsize-2; length-=opsize; @@ -374,7 +332,7 @@ void tcp_parse_options(struct tcphdr *th, struct tcp_opt *tp, int no_fancy) /* Fast parse options. This hopes to only see timestamps. * If it is wrong it falls back on tcp_parse_option(). - * This should probably get extended for timestamps + SACK as well. + * This should probably get extended for timestamps as well. * Assembly code anyone? -- erics */ static __inline__ int tcp_fast_parse_options(struct tcphdr *th, struct tcp_opt *tp) @@ -384,14 +342,12 @@ static __inline__ int tcp_fast_parse_options(struct tcphdr *th, struct tcp_opt * return 0; if (th->doff == sizeof(struct tcphdr)>>2) { tp->saw_tstamp = 0; - tp->sacks = 0; return 0; - } else if (th->doff == (sizeof(struct tcphdr)>>2)+3) { + } else if (th->doff == (sizeof(struct tcphdr)>>2)+(TCPOLEN_TSTAMP_ALIGNED>>2)) { __u32 *ptr = (__u32 *)(th + 1); - if (*ptr == htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) - | (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP)) { + if (*ptr == __constant_ntohl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) + | (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP)) { tp->saw_tstamp = 1; - tp->sacks = 0; tp->rcv_tsval = ntohl(*++ptr); tp->rcv_tsecr = ntohl(*++ptr); return 1; @@ -401,89 +357,6 @@ static __inline__ int tcp_fast_parse_options(struct tcphdr *th, struct tcp_opt * return 1; } -#if 0 - -/* - * This is the old fast retransmit code. It will go away eventually. -- erics - */ - -/* - * See draft-stevens-tcpca-spec-01 for documentation. - */ - -static void tcp_fast_retrans(struct sock *sk, u32 ack, int not_dup) -{ - struct tcp_opt *tp=&(sk->tp_pinfo.af_tcp); - - /* FIXME: if we are already retransmitting should this code - * be skipped? [Floyd high_seq check sort of does this] - * The case I'm worried about is falling into a fast - * retransmit on a link with a congestion window of 1 or 2. - * There was some evidence in 2.0.x that this was problem - * on really slow links (1200 or 2400 baud). I need to - * try this situation again and see what happens. - */ - - /* - * An ACK is a duplicate if: - * (1) it has the same sequence number as the largest number we've - * seen, - * (2) it has the same window as the last ACK, - * (3) we have outstanding data that has not been ACKed - * (4) The packet was not carrying any data. - * (5) [From Floyds paper on fast retransmit wars] - * The packet acked data after high_seq; - */ - - if (ack == tp->snd_una && tp->packets_out && (not_dup == 0)) { - /* 1. When the third duplicate ack is received, set ssthresh - * to one half the current congestion window, but no less - * than two segments. Retransmit the missing segment. - */ - if (tp->high_seq == 0 || after(ack, tp->high_seq)) { - tp->dup_acks++; - - if (tp->dup_acks == 3) { - tp->snd_ssthresh = max(tp->snd_cwnd >> 1, 2); - tp->snd_cwnd = tp->snd_ssthresh + 3; - tcp_do_retransmit(sk, 0); - - /* Careful not to timeout just after fast - * retransmit! - */ - tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto); - } - } - - /* 2. Each time another duplicate ACK arrives, increment - * cwnd by the segment size. [...] Transmit a packet... - * - * Packet transmission will be done on normal flow processing - * since we're not in "retransmit mode". - */ - if (tp->dup_acks >= 3) { - tp->dup_acks++; - tp->snd_cwnd++; - } - } else { - /* 3. When the next ACK arrives that acknowledges new data, - * set cwnd to ssthresh. - */ - if (tp->dup_acks >= 3) { - tp->retrans_head = NULL; - tp->snd_cwnd = max(tp->snd_ssthresh, 1); - tp->retransmits = 0; - } - tp->dup_acks = 0; - - /* FIXME: This is wrong if the new ack that arrives - * is below the value for high_seq. - */ - tp->high_seq = 0; - } -} -#endif - #define FLAG_DATA 0x01 #define FLAG_WIN_UPDATE 0x02 #define FLAG_DATA_ACKED 0x04 @@ -579,9 +452,8 @@ static void tcp_fast_retrans(struct sock *sk, u32 ack, int not_dup) * not indicate a packet left the system. * We can test this by just checking * if ack changed from snd_una, since - * the only way to get here without changing - * advancing from snd_una is if this was a - * window update. + * the only way to get here without advancing + * from snd_una is if this was a window update. */ if (ack != tp->snd_una && before(ack,tp->high_seq)) { tcp_do_retransmit(sk, 0); @@ -596,9 +468,6 @@ static void tcp_fast_retrans(struct sock *sk, u32 ack, int not_dup) clear_fast_retransmit(sk); } } - } else { - /* Clear any aborted fast retransmit starts. */ - tp->dup_acks = 0; } } @@ -649,7 +518,6 @@ static void tcp_cong_avoid_vegas(struct sock *sk, u32 seq, u32 ack, expected = (tp->snd_nxt - tp->snd_una) * inv_basertt; - /* XXX sk->mss should move into tcp_opt as well -DaveM */ inv_basebd = sk->mss * inv_basertt; /* Slow Start */ @@ -731,13 +599,6 @@ static int tcp_clean_rtx_queue(struct sock *sk, __u32 ack, __u32 *seq, int acked = 0; while((skb=skb_peek(&sk->write_queue)) && (skb != tp->send_head)) { -#ifdef TCP_DEBUG - /* Check for a bug. */ - if (skb->next != (struct sk_buff*) &sk->write_queue && - after(skb->end_seq, skb->next->seq)) - printk(KERN_DEBUG "INET: tcp_input.c: *** " - "bug send_list out of order.\n"); -#endif /* If our packet is before the ack sequence we can * discard it as it's confirmed to have arrived the * other end. @@ -745,12 +606,15 @@ static int tcp_clean_rtx_queue(struct sock *sk, __u32 ack, __u32 *seq, if (after(skb->end_seq, ack)) break; -#if 0 - SOCK_DEBUG(sk, "removing seg %x-%x from retransmit queue\n", - skb->seq, skb->end_seq); -#endif - - acked = FLAG_DATA_ACKED; + /* Initial outgoing SYN's get put onto the write_queue + * just like anything else we transmit. It is not + * true data, and if we misinform our callers that + * this ACK acks real data, we will erroneously exit + * connection startup slow start one packet too + * quickly. This is severely frowned upon behavior. + */ + if(!skb->h.th->syn) + acked = FLAG_DATA_ACKED; /* FIXME: packet counting may break if we have to * do packet "repackaging" for stacks that don't @@ -766,11 +630,9 @@ static int tcp_clean_rtx_queue(struct sock *sk, __u32 ack, __u32 *seq, kfree_skb(skb); } - if (acked) { + if (acked) tp->retrans_head = NULL; - if (!sk->dead) - sk->write_space(sk); - } + return acked; } @@ -795,6 +657,66 @@ static void tcp_ack_probe(struct sock *sk, __u32 ack) } } +/* Read draft-ietf-tcplw-high-performance before mucking + * with this code. (Superceeds RFC1323) + */ +static void tcp_ack_saw_tstamp(struct sock *sk, struct tcp_opt *tp, + u32 seq, u32 ack, int flag) +{ + __u32 seq_rtt = (jiffies-tp->rcv_tsecr); + tcp_rtt_estimator(tp, seq_rtt); + if (tp->retransmits) { + if (tp->packets_out == 0) { + tp->retransmits = 0; + tp->backoff = 0; + tcp_set_rto(tp); + } else { + /* Still retransmitting, use backoff */ + tcp_set_rto(tp); + tp->rto = tp->rto << tp->backoff; + } + } else { + tcp_set_rto(tp); + if (flag & FLAG_DATA_ACKED) + (*tcp_sys_cong_ctl_f)(sk, seq, ack, seq_rtt); + } + /* NOTE: safe here so long as cong_ctl doesn't use rto */ + tcp_bound_rto(tp); +} + +static void tcp_ack_packets_out(struct sock *sk, struct tcp_opt *tp) +{ + struct sk_buff *skb; + long when; + + skb = skb_peek(&sk->write_queue); + when = tp->rto - (jiffies - skb->when); + + /* FIXME: This assumes that when we are retransmitting + * we should only ever respond with one packet. + * This means congestion windows should not grow + * during recovery. In 2.0.X we allow the congestion + * window to grow. It is not clear to me which + * decision is correct. The RFCs should be double + * checked as should the behavior of other stacks. + * Also note that if we do want to allow the + * congestion window to grow during retransmits + * we have to fix the call to congestion window + * updates so that it works during retransmission. + */ + if (tp->retransmits) { + tp->retrans_head = NULL; + + /* This is tricky. We are retransmiting a + * segment of a window when congestion occured. + */ + tcp_do_retransmit(sk, 0); + tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto); + } else { + tcp_reset_xmit_timer(sk, TIME_RETRANS, when); + } +} + /* * This routine deals with incoming acks, but not outgoing ones. */ @@ -806,7 +728,6 @@ static int tcp_ack(struct sock *sk, struct tcphdr *th, int flag = 0; u32 seq = 0; u32 seq_rtt = 0; - struct sk_buff *skb; if(sk->zapped) return(1); /* Dead, can't ack any more so why bother */ @@ -838,7 +759,7 @@ static int tcp_ack(struct sock *sk, struct tcphdr *th, */ if (before(tp->snd_wl1, ack_seq) || (tp->snd_wl1 == ack_seq && !after(tp->snd_wl2, ack))) { - unsigned long nwin = ntohs(th->window) << tp->snd_wscale; + u32 nwin = ntohs(th->window) << tp->snd_wscale; if ((tp->snd_wl2 != ack) || (nwin > tp->snd_wnd)) { flag |= FLAG_WIN_UPDATE; @@ -869,28 +790,7 @@ static int tcp_ack(struct sock *sk, struct tcphdr *th, /* If we have a timestamp, we always do rtt estimates. */ if (tp->saw_tstamp) { - /* Read draft-ietf-tcplw-high-performance before mucking - * with this code. (Superceeds RFC1323) - */ - seq_rtt = (jiffies-tp->rcv_tsecr); - tcp_rtt_estimator(tp, seq_rtt); - if (tp->retransmits) { - if (tp->packets_out == 0) { - tp->retransmits = 0; - tp->backoff = 0; - tcp_set_rto(tp); - } else { - /* Still retransmitting, use backoff */ - tcp_set_rto(tp); - tp->rto = tp->rto << tp->backoff; - } - } else { - tcp_set_rto(tp); - if (flag & FLAG_DATA_ACKED) - (*tcp_sys_cong_ctl_f)(sk, seq, ack, seq_rtt); - } - /* NOTE: safe here so long as cong_ctl doesn't use rto */ - tcp_bound_rto(tp); + tcp_ack_saw_tstamp(sk, tp, seq, ack, flag); } else { /* If we were retransmiting don't count rtt estimate. */ if (tp->retransmits) { @@ -916,51 +816,217 @@ static int tcp_ack(struct sock *sk, struct tcphdr *th, } if (tp->packets_out) { - if (flag & FLAG_DATA_ACKED) { - long when; - - skb = skb_peek(&sk->write_queue); - when = tp->rto - (jiffies - skb->when); - - /* FIXME: This assumes that when we are retransmitting - * we should only ever respond with one packet. - * This means congestion windows should not grow - * during recovery. In 2.0.X we allow the congestion - * window to grow. It is not clear to me which - * decision is correct. The RFCs should be double - * checked as should the behavior of other stacks. - * Also note that if we do want to allow the - * congestion window to grow during retransmits - * we have to fix the call to congestion window - * updates so that it works during retransmission. - */ - if (tp->retransmits) { - tp->retrans_head = NULL; - - /* This is tricky. We are retransmiting a - * segment of a window when congestion occured. - */ - tcp_do_retransmit(sk, 0); - tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto); - } else - tcp_reset_xmit_timer(sk, TIME_RETRANS, when); - } - } else + if (flag & FLAG_DATA_ACKED) + tcp_ack_packets_out(sk, tp); + } else { tcp_clear_xmit_timer(sk, TIME_RETRANS); + } - tcp_fast_retrans(sk, ack, (flag & (FLAG_DATA|FLAG_WIN_UPDATE))); - + flag &= (FLAG_DATA | FLAG_WIN_UPDATE); + if ((ack == tp->snd_una && tp->packets_out && flag == 0) || + (tp->high_seq != 0)) { + tcp_fast_retrans(sk, ack, flag); + } else { + /* Clear any aborted fast retransmit starts. */ + tp->dup_acks = 0; + } /* Remember the highest ack received. */ tp->snd_una = ack; - return 1; uninteresting_ack: - SOCK_DEBUG(sk, "Ack ignored %u %u\n", ack, tp->snd_nxt); return 0; } +/* New-style handling of TIME_WAIT sockets. */ +static void tcp_timewait_kill(unsigned long __arg) +{ + struct tcp_tw_bucket *tw = (struct tcp_tw_bucket *)__arg; + + /* Zap the timer. */ + del_timer(&tw->timer); + + /* Unlink from various places. */ + if(tw->bind_next) + tw->bind_next->bind_pprev = tw->bind_pprev; + *(tw->bind_pprev) = tw->bind_next; + if(tw->tb->owners == NULL) + tcp_inc_slow_timer(TCP_SLT_BUCKETGC); + + if(tw->next) + tw->next->pprev = tw->pprev; + *tw->pprev = tw->next; + + /* We decremented the prot->inuse count when we entered TIME_WAIT + * and the sock from which this came was destroyed. + */ + tw->sklist_next->sklist_prev = tw->sklist_prev; + tw->sklist_prev->sklist_next = tw->sklist_next; + + /* Ok, now free it up. */ + kmem_cache_free(tcp_timewait_cachep, tw); +} + +/* We come here as a special case from the AF specific TCP input processing, + * and the SKB has no owner. Essentially handling this is very simple, + * we just keep silently eating rx'd packets until none show up for the + * entire timeout period. The only special cases are for BSD TIME_WAIT + * reconnects and SYN/RST bits being set in the TCP header. + */ +int tcp_timewait_state_process(struct tcp_tw_bucket *tw, struct sk_buff *skb, + struct tcphdr *th, void *opt, __u16 len) +{ + /* RFC 1122: + * "When a connection is [...] on TIME-WAIT state [...] + * [a TCP] MAY accept a new SYN from the remote TCP to + * reopen the connection directly, if it: + * + * (1) assigns its initial sequence number for the new + * connection to be larger than the largest sequence + * number it used on the previous connection incarnation, + * and + * + * (2) returns to TIME-WAIT state if the SYN turns out + * to be an old duplicate". + */ + if(th->syn && !th->rst && after(skb->seq, tw->rcv_nxt)) { + struct sock *sk; + struct tcp_func *af_specific = tw->af_specific; + __u32 isn; + + isn = tw->rcv_nxt + 128000; + if(isn == 0) + isn++; + tcp_timewait_kill((unsigned long)tw); + sk = af_specific->get_sock(skb, th); + if(sk == NULL || !ipsec_sk_policy(sk,skb)) + return 0; + skb_set_owner_r(skb, sk); + af_specific = sk->tp_pinfo.af_tcp.af_specific; + if(af_specific->conn_request(sk, skb, opt, isn) < 0) + return 1; /* Toss a reset back. */ + return 0; /* Discard the frame. */ + } + + /* Check RST or SYN */ + if(th->rst || th->syn) { + /* This is TIME_WAIT assasination, in two flavors. + * Oh well... nobody has a sufficient solution to this + * protocol bug yet. + */ + if(sysctl_tcp_rfc1337 == 0) + tcp_timewait_kill((unsigned long)tw); + + if(!th->rst) + return 1; /* toss a reset back */ + } else { + if(th->ack) { + /* In this case we must reset the TIMEWAIT timer. */ + del_timer(&tw->timer); + tw->timer.expires = jiffies + TCP_TIMEWAIT_LEN; + add_timer(&tw->timer); + } + } + return 0; /* Discard the frame. */ +} + +/* Enter the time wait state. This is always called from BH + * context. Essentially we whip up a timewait bucket, copy the + * relevant info into it from the SK, and mess with hash chains + * and list linkage. + */ +static __inline__ void tcp_tw_hashdance(struct sock *sk, struct tcp_tw_bucket *tw) +{ + struct sock **head, *sktw; + + /* Step 1: Remove SK from established hash. */ + if(sk->next) + sk->next->pprev = sk->pprev; + *sk->pprev = sk->next; + sk->pprev = NULL; + tcp_reg_zap(sk); + + /* Step 2: Put TW into bind hash where SK was. */ + tw->tb = (struct tcp_bind_bucket *)sk->prev; + if((tw->bind_next = sk->bind_next) != NULL) + sk->bind_next->bind_pprev = &tw->bind_next; + tw->bind_pprev = sk->bind_pprev; + *sk->bind_pprev = (struct sock *)tw; + + /* Step 3: Same for the protocol sklist. */ + (tw->sklist_next = sk->sklist_next)->sklist_prev = (struct sock *)tw; + (tw->sklist_prev = sk->sklist_prev)->sklist_next = (struct sock *)tw; + sk->sklist_next = NULL; + sk->prot->inuse--; + + /* Step 4: Hash TW into TIMEWAIT half of established hash table. */ + head = &tcp_established_hash[sk->hashent + (TCP_HTABLE_SIZE/2)]; + sktw = (struct sock *)tw; + if((sktw->next = *head) != NULL) + (*head)->pprev = &sktw->next; + *head = sktw; + sktw->pprev = head; +} + +void tcp_time_wait(struct sock *sk) +{ + struct tcp_tw_bucket *tw; + + tw = kmem_cache_alloc(tcp_timewait_cachep, SLAB_ATOMIC); + if(tw != NULL) { + /* Give us an identity. */ + tw->daddr = sk->daddr; + tw->rcv_saddr = sk->rcv_saddr; + tw->bound_dev_if= sk->bound_dev_if; + tw->num = sk->num; + tw->state = TCP_TIME_WAIT; + tw->family = sk->family; + tw->source = sk->dummy_th.source; + tw->dest = sk->dummy_th.dest; + tw->rcv_nxt = sk->tp_pinfo.af_tcp.rcv_nxt; + tw->af_specific = sk->tp_pinfo.af_tcp.af_specific; + +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + if(tw->family == AF_INET6) { + memcpy(&tw->v6_daddr, + &sk->net_pinfo.af_inet6.daddr, + sizeof(struct in6_addr)); + memcpy(&tw->v6_rcv_saddr, + &sk->net_pinfo.af_inet6.rcv_saddr, + sizeof(struct in6_addr)); + } +#endif + /* Linkage updates. */ + tcp_tw_hashdance(sk, tw); + + /* Get the TIME_WAIT timeout firing. */ + init_timer(&tw->timer); + tw->timer.function = tcp_timewait_kill; + tw->timer.data = (unsigned long) tw; + tw->timer.expires = jiffies + TCP_TIMEWAIT_LEN; + add_timer(&tw->timer); + + /* CLOSE the SK. */ + if(sk->state == TCP_ESTABLISHED) + tcp_statistics.TcpCurrEstab--; + sk->state = TCP_CLOSE; + net_reset_timer(sk, TIME_DONE, + min(sk->tp_pinfo.af_tcp.srtt * 2, TCP_DONE_TIME)); + } else { + /* Sorry, we're out of memory, just CLOSE this + * socket up. We've got bigger problems than + * non-graceful socket closings. + */ + tcp_set_state(sk, TCP_CLOSE); + } + + /* Prevent rcvmsg/sndmsg calls, and wake people up. */ + sk->shutdown = SHUTDOWN_MASK; + if(!sk->dead) + sk->state_change(sk); +} + /* * Process the FIN bit. This now behaves as it is supposed to work * and the FIN takes effect when it is validly part of sequence @@ -976,17 +1042,9 @@ uninteresting_ack: * If we are in FINWAIT-2, a received FIN moves us to TIME-WAIT. */ -static int tcp_fin(struct sk_buff *skb, struct sock *sk, struct tcphdr *th) +static void tcp_fin(struct sk_buff *skb, struct sock *sk, struct tcphdr *th) { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); - - if(sk->state == TCP_SYN_SENT) { - /* RFC793 says to drop the segment and return. */ - return 1; - } - - /* XXX This fin_seq thing should disappear... -DaveM */ - tp->fin_seq = skb->end_seq; + sk->tp_pinfo.af_tcp.fin_seq = skb->end_seq; tcp_send_ack(sk); @@ -1013,12 +1071,6 @@ static int tcp_fin(struct sk_buff *skb, struct sock *sk, struct tcphdr *th) case TCP_LAST_ACK: /* RFC793: Remain in the LAST-ACK state. */ break; - case TCP_TIME_WAIT: - /* Received a retransmission of the FIN, - * restart the TIME_WAIT timer. - */ - tcp_reset_msl_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); - break; case TCP_FIN_WAIT1: /* This case occurs when a simultaneous close @@ -1035,21 +1087,15 @@ static int tcp_fin(struct sk_buff *skb, struct sock *sk, struct tcphdr *th) break; case TCP_FIN_WAIT2: /* Received a FIN -- send ACK and enter TIME_WAIT. */ - tcp_reset_msl_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); - sk->shutdown |= SHUTDOWN_MASK; - tcp_set_state(sk,TCP_TIME_WAIT); - break; - case TCP_CLOSE: - /* Already in CLOSE. */ + tcp_time_wait(sk); break; default: - /* Only TCP_LISTEN is left, in that case we should never - * reach this piece of code. + /* Only TCP_LISTEN and TCP_CLOSE are left, in these + * cases we should never reach this piece of code. */ printk("tcp_fin: Impossible, sk->state=%d\n", sk->state); break; }; - return 0; } /* This one checks to see if we can put data from the @@ -1060,7 +1106,7 @@ static void tcp_ofo_queue(struct sock *sk) struct sk_buff *skb; struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); - while ((skb = skb_peek(&sk->out_of_order_queue))) { + while ((skb = skb_peek(&tp->out_of_order_queue))) { if (after(skb->seq, tp->rcv_nxt)) break; @@ -1076,6 +1122,8 @@ static void tcp_ofo_queue(struct sock *sk) skb_unlink(skb); skb_queue_tail(&sk->receive_queue, skb); tp->rcv_nxt = skb->end_seq; + if(skb->h.th->fin) + tcp_fin(skb, sk, skb->h.th); } } @@ -1094,8 +1142,12 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) dst_confirm(sk->dst_cache); skb_queue_tail(&sk->receive_queue, skb); tp->rcv_nxt = skb->end_seq; + if(skb->h.th->fin) + tcp_fin(skb, sk, skb->h.th); + else + tp->delayed_acks++; tcp_ofo_queue(sk); - if (skb_queue_len(&sk->out_of_order_queue) == 0) + if (skb_queue_len(&tp->out_of_order_queue) == 0) tp->pred_flags = htonl((0x5010 << 16) | tp->snd_wnd); return; } @@ -1104,8 +1156,7 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) if (!after(skb->end_seq, tp->rcv_nxt)) { /* A retransmit, 2nd most common case. Force an imediate ack. */ SOCK_DEBUG(sk, "retransmit received: seq %X\n", skb->seq); - - tp->delayed_acks = MAX_DELAY_ACK; + tcp_enter_quickack_mode(tp); kfree_skb(skb); return; } @@ -1119,7 +1170,8 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) } /* Ok. This is an out_of_order segment, force an ack. */ - tp->delayed_acks = MAX_DELAY_ACK; + tp->delayed_acks++; + tcp_enter_quickack_mode(tp); /* Disable header predition. */ tp->pred_flags = 0; @@ -1127,10 +1179,10 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) SOCK_DEBUG(sk, "out of order segment: rcv_next %X seq %X - %X\n", tp->rcv_nxt, skb->seq, skb->end_seq); - if (skb_peek(&sk->out_of_order_queue) == NULL) { - skb_queue_head(&sk->out_of_order_queue,skb); + if (skb_peek(&tp->out_of_order_queue) == NULL) { + skb_queue_head(&tp->out_of_order_queue,skb); } else { - for(skb1=sk->out_of_order_queue.prev; ; skb1 = skb1->prev) { + for(skb1=tp->out_of_order_queue.prev; ; skb1 = skb1->prev) { /* Already there. */ if (skb->seq == skb1->seq && skb->len >= skb1->len) { skb_append(skb1, skb); @@ -1145,8 +1197,8 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) } /* See if we've hit the start. If so insert. */ - if (skb1 == skb_peek(&sk->out_of_order_queue)) { - skb_queue_head(&sk->out_of_order_queue,skb); + if (skb1 == skb_peek(&tp->out_of_order_queue)) { + skb_queue_head(&tp->out_of_order_queue,skb); break; } } @@ -1172,23 +1224,17 @@ static int tcp_data(struct sk_buff *skb, struct sock *sk, unsigned int len) if (skb->len == 0 && !th->fin) return(0); - /* FIXME: don't accept data after the received fin. - * - * Would checking snd_seq against fin_seq be enough? - * If so, how do we handle that case exactly? -DaveM - */ - /* We no longer have anyone receiving data on this connection. */ tcp_data_queue(sk, skb); - if (before(tp->rcv_nxt, sk->copied_seq)) { + if (before(tp->rcv_nxt, tp->copied_seq)) { printk(KERN_DEBUG "*** tcp.c:tcp_data bug acked < copied\n"); - tp->rcv_nxt = sk->copied_seq; + tp->rcv_nxt = tp->copied_seq; } - tp->delayed_acks++; - - /* Now tell the user we may have some data. */ + /* Above, tcp_data_queue() increments delayed_acks appropriately. + * Now tell the user we may have some data. + */ if (!sk->dead) { SOCK_DEBUG(sk, "Data wakeup.\n"); sk->data_ready(sk,0); @@ -1204,23 +1250,10 @@ static void tcp_data_snd_check(struct sock *sk) if ((skb = tp->send_head)) { if (!after(skb->end_seq, tp->snd_una + tp->snd_wnd) && tp->packets_out < tp->snd_cwnd ) { - /* Add more data to the send queue. */ - - /* FIXME: the congestion window is checked - * again in tcp_write_xmit anyway?! -- erics - * - * I think it must, it bumps tp->packets_out for - * each packet it fires onto the wire. -DaveM - */ + /* Put more data onto the wire. */ tcp_write_xmit(sk); - if(!sk->dead) - sk->write_space(sk); } else if (tp->packets_out == 0 && !tp->pending) { - /* Data to queue but no room. */ - - /* FIXME: Is it right to do a zero window probe into - * a congestion window limited window??? -- erics - */ + /* Start probing the receivers window. */ tcp_reset_xmit_timer(sk, TIME_PROBE0, tp->rto); } } @@ -1240,12 +1273,24 @@ static __inline__ void __tcp_ack_snd_check(struct sock *sk) * - delay time <= 0.5 HZ * - we don't have a window update to send * - must send at least every 2 full sized packets + * + * With an extra heuristic to handle loss of packet + * situations and also helping the sender leave slow + * start in an expediant manner. */ - if (tp->delayed_acks >= MAX_DELAY_ACK || tcp_raise_window(sk)) + /* Two full frames received or... */ + if (((tp->rcv_nxt - tp->rcv_wup) >= (sk->mss << 1)) || + /* We will update the window "significantly" or... */ + tcp_raise_window(sk) || + /* We entered "quick ACK" mode */ + tcp_in_quickack_mode(tp)) { + /* Then ack it now */ tcp_send_ack(sk); - else - tcp_send_delayed_ack(sk, HZ/2); + } else { + /* Else, send delayed ack. */ + tcp_send_delayed_ack(tp, HZ/2); + } } static __inline__ void tcp_ack_snd_check(struct sock *sk) @@ -1279,11 +1324,11 @@ static void tcp_check_urg(struct sock * sk, struct tcphdr * th) ptr += ntohl(th->seq); /* Ignore urgent data that we've already seen and read. */ - if (after(sk->copied_seq, ptr)) + if (after(tp->copied_seq, ptr)) return; /* Do we already have a newer (or duplicate) urgent pointer? */ - if (sk->urg_data && !after(ptr, sk->urg_seq)) + if (tp->urg_data && !after(ptr, tp->urg_seq)) return; /* Tell the world about our new urgent pointer. */ @@ -1296,14 +1341,14 @@ static void tcp_check_urg(struct sock * sk, struct tcphdr * th) /* We may be adding urgent data when the last byte read was * urgent. To do this requires some care. We cannot just ignore - * sk->copied_seq since we would read the last urgent byte again + * tp->copied_seq since we would read the last urgent byte again * as data, nor can we alter copied_seq until this data arrives * or we break the sematics of SIOCATMARK (and thus sockatmark()) */ - if (sk->urg_seq == sk->copied_seq) - sk->copied_seq++; /* Move the copied sequence on correctly */ - sk->urg_data = URG_NOTYET; - sk->urg_seq = ptr; + if (tp->urg_seq == tp->copied_seq) + tp->copied_seq++; /* Move the copied sequence on correctly */ + tp->urg_data = URG_NOTYET; + tp->urg_seq = ptr; /* Disable header prediction. */ tp->pred_flags = 0; @@ -1312,17 +1357,19 @@ static void tcp_check_urg(struct sock * sk, struct tcphdr * th) /* This is the 'fast' part of urgent handling. */ static inline void tcp_urg(struct sock *sk, struct tcphdr *th, unsigned long len) { + struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + /* Check if we get a new urgent pointer - normally not. */ if (th->urg) tcp_check_urg(sk,th); /* Do we wait for any urgent data? - normally not... */ - if (sk->urg_data == URG_NOTYET) { - u32 ptr = sk->urg_seq - ntohl(th->seq) + (th->doff*4); + if (tp->urg_data == URG_NOTYET) { + u32 ptr = tp->urg_seq - ntohl(th->seq) + (th->doff*4); /* Is the urgent pointer pointing into this packet? */ if (ptr < len) { - sk->urg_data = URG_VALID | *(ptr + (unsigned char *) th); + tp->urg_data = URG_VALID | *(ptr + (unsigned char *) th); if (!sk->dead) sk->data_ready(sk,0); } @@ -1335,33 +1382,39 @@ static inline void tcp_urg(struct sock *sk, struct tcphdr *th, unsigned long len */ static void prune_queue(struct sock *sk) { - struct tcp_opt *tp; + struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; struct sk_buff * skb; - SOCK_DEBUG(sk, "prune_queue: c=%x\n", sk->copied_seq); + SOCK_DEBUG(sk, "prune_queue: c=%x\n", tp->copied_seq); /* First Clean the out_of_order queue. */ /* Start with the end because there are probably the least * useful packets (crossing fingers). */ - while ((skb = skb_dequeue_tail(&sk->out_of_order_queue))) { + while ((skb = skb_dequeue_tail(&tp->out_of_order_queue))) { kfree_skb(skb); if (atomic_read(&sk->rmem_alloc) <= sk->rcvbuf) return; } - tp = &sk->tp_pinfo.af_tcp; - /* Now continue with the receive queue if it wasn't enough */ while ((skb = skb_peek_tail(&sk->receive_queue))) { + /* Never toss anything when we've seen the FIN. + * It's just too complex to recover from it. + */ + if(skb->h.th->fin) + break; + /* Never remove packets that have been already acked */ if (before(skb->end_seq, tp->last_ack_sent+1)) { printk(KERN_DEBUG "prune_queue: hit acked data c=%x,%x,%x\n", - sk->copied_seq, skb->end_seq, tp->last_ack_sent); + tp->copied_seq, skb->end_seq, tp->last_ack_sent); break; } skb_unlink(skb); tp->rcv_nxt = skb->seq; + SOCK_DEBUG(sk, "prune_queue: removing %x-%x (c=%x)\n", + skb->seq, skb->end_seq, tp->copied_seq); kfree_skb(skb); if (atomic_read(&sk->rmem_alloc) <= sk->rcvbuf) break; @@ -1429,7 +1482,6 @@ int tcp_rcv_established(struct sock *sk, struct sk_buff *skb, } } else if (skb->ack_seq == tp->snd_una) { /* Bulk data transfer: receiver */ - if (atomic_read(&sk->rmem_alloc) > sk->rcvbuf) goto discard; @@ -1441,18 +1493,13 @@ int tcp_rcv_established(struct sock *sk, struct sk_buff *skb, skb_queue_tail(&sk->receive_queue, skb); tp->rcv_nxt = skb->end_seq; + /* FIN bit check is not done since if FIN is set in + * this frame, the pred_flags won't match up. -DaveM + */ sk->data_ready(sk, 0); tcp_delack_estimator(tp); - -#if 1 /* This checks for required window updates too. */ tp->delayed_acks++; __tcp_ack_snd_check(sk); -#else - if (tp->delayed_acks++ == 0) - tcp_send_delayed_ack(sk, HZ/2); - else - tcp_send_ack(sk); -#endif return 0; } } @@ -1469,7 +1516,7 @@ int tcp_rcv_established(struct sock *sk, struct sk_buff *skb, } } - if(th->syn && skb->seq != sk->syn_seq) { + if(th->syn && skb->seq != tp->syn_seq) { SOCK_DEBUG(sk, "syn in established state\n"); tcp_statistics.TcpInErrs++; tcp_reset(sk, skb); @@ -1490,10 +1537,6 @@ int tcp_rcv_established(struct sock *sk, struct sk_buff *skb, /* step 7: process the segment text */ queued = tcp_data(skb, sk, len); - /* step 8: check the FIN bit */ - if (th->fin) - (void) tcp_fin(skb, sk, th); - tcp_data_snd_check(sk); /* If our receive queue has grown past its limits shrink it */ @@ -1657,19 +1700,19 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, tp->snd_wnd = htons(th->window) << tp->snd_wscale; tp->snd_wl1 = skb->seq; tp->snd_wl2 = skb->ack_seq; - tp->fin_seq = skb->seq; tcp_set_state(sk, TCP_ESTABLISHED); tcp_parse_options(th,tp,0); - /* FIXME: need to make room for SACK still */ + if (tp->wscale_ok == 0) { tp->snd_wscale = tp->rcv_wscale = 0; tp->window_clamp = min(tp->window_clamp,65535); } if (tp->tstamp_ok) { - tp->tcp_header_len = sizeof(struct tcphdr) + 12; /* FIXME: Define constant! */ - sk->dummy_th.doff += 3; /* reserve space of options */ + tp->tcp_header_len = + sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED; + sk->dummy_th.doff += (TCPOLEN_TSTAMP_ALIGNED >> 2); } else tp->tcp_header_len = sizeof(struct tcphdr); if (tp->saw_tstamp) { @@ -1680,14 +1723,30 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, /* Can't be earlier, doff would be wrong. */ tcp_send_ack(sk); - if (tp->in_mss) - sk->mss = min(sk->mss, tp->in_mss); - - /* Take out space for tcp options. */ - sk->mss -= tp->tcp_header_len - sizeof(struct tcphdr); + /* Check for the case where we tried to advertise + * a window including timestamp options, but did not + * end up using them for this connection. + */ + if((tp->tstamp_ok == 0) && sysctl_tcp_timestamps) + sk->mss += TCPOLEN_TSTAMP_ALIGNED; + /* Now limit it if the other end negotiated a smaller + * value. + */ + if (tp->in_mss) { + int real_mss = tp->in_mss; + + /* We store MSS locally with the timestamp bytes + * subtracted, TCP's advertise it with them + * included. Account for this fact. + */ + if(tp->tstamp_ok) + real_mss -= TCPOLEN_TSTAMP_ALIGNED; + sk->mss = min(sk->mss, real_mss); + } + sk->dummy_th.dest = th->source; - sk->copied_seq = tp->rcv_nxt; + tp->copied_seq = tp->rcv_nxt; if(!sk->dead) { sk->state_change(sk); @@ -1722,52 +1781,10 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, } break; - - case TCP_TIME_WAIT: - /* RFC 1122: - * "When a connection is [...] on TIME-WAIT state [...] - * [a TCP] MAY accept a new SYN from the remote TCP to - * reopen the connection directly, if it: - * - * (1) assigns its initial sequence number for the new - * connection to be larger than the largest sequence - * number it used on the previous connection incarnation, - * and - * - * (2) returns to TIME-WAIT state if the SYN turns out - * to be an old duplicate". - */ - if (th->syn && !th->rst && after(skb->seq, tp->rcv_nxt)) { - __u32 isn; - - skb_orphan(skb); - sk->err = ECONNRESET; - tcp_set_state(sk, TCP_CLOSE); - sk->shutdown = SHUTDOWN_MASK; - - isn = tp->rcv_nxt + 128000; - if (isn == 0) - isn++; - - sk = tp->af_specific->get_sock(skb, th); - - if (sk == NULL || !ipsec_sk_policy(sk,skb)) - goto discard; - - skb_set_owner_r(skb, sk); - tp = &sk->tp_pinfo.af_tcp; - - if(tp->af_specific->conn_request(sk, skb, opt, isn) < 0) - return 1; - - goto discard; - } - - break; } /* Parse the tcp_options present on this header. - * By this point we really only expect timestamps and SACKs. + * By this point we really only expect timestamps. * Note that this really has to be here and not later for PAWS * (RFC1323) to work. */ @@ -1819,7 +1836,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, * original syn. */ - if (th->syn && skb->seq!=sk->syn_seq) { + if (th->syn && skb->seq!=tp->syn_seq) { tcp_reset(sk, skb); return 1; } @@ -1833,7 +1850,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, if (acceptable) { tcp_set_state(sk, TCP_ESTABLISHED); sk->dummy_th.dest=th->source; - sk->copied_seq = tp->rcv_nxt; + tp->copied_seq = tp->rcv_nxt; if(!sk->dead) sk->state_change(sk); @@ -1850,7 +1867,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, break; case TCP_FIN_WAIT1: - if (tp->snd_una == sk->write_seq) { + if (tp->snd_una == tp->write_seq) { sk->shutdown |= SEND_SHUTDOWN; tcp_set_state(sk, TCP_FIN_WAIT2); if (!sk->dead) @@ -1861,12 +1878,12 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, break; case TCP_CLOSING: - if (tp->snd_una == sk->write_seq) + if (tp->snd_una == tp->write_seq) tcp_time_wait(sk); break; case TCP_LAST_ACK: - if (tp->snd_una == sk->write_seq) { + if (tp->snd_una == tp->write_seq) { sk->shutdown = SHUTDOWN_MASK; tcp_set_state(sk,TCP_CLOSE); if (!sk->dead) @@ -1874,13 +1891,6 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, goto discard; } break; - - case TCP_TIME_WAIT: - /* Keep us in TIME_WAIT until we stop getting - * packets, reset the timeout. - */ - tcp_reset_msl_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); - break; } } else goto discard; @@ -1918,12 +1928,6 @@ step6: break; } - /* step 8: check the FIN bit */ - if (th->fin) { - if(tcp_fin(skb, sk, th) != 0) - goto discard; - } - tcp_data_snd_check(sk); tcp_ack_snd_check(sk); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index e4f8981ac..91f21ff75 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_ipv4.c,v 1.79 1998/01/15 22:40:47 freitag Exp $ + * Version: $Id: tcp_ipv4.c,v 1.109 1998/03/15 07:24:15 davem Exp $ * * IPv4 specific functions * @@ -60,8 +60,6 @@ #include <linux/inet.h> -extern int sysctl_tcp_sack; -extern int sysctl_tcp_tsack; extern int sysctl_tcp_timestamps; extern int sysctl_tcp_window_scaling; extern int sysctl_tcp_syncookies; @@ -89,16 +87,19 @@ void tcp_v4_send_check(struct sock *sk, struct tcphdr *th, int len, */ struct sock *tcp_established_hash[TCP_HTABLE_SIZE]; +/* Ok, let's try this, I give up, we do need a local binding + * TCP hash as well as the others for fast bind/connect. + */ +struct tcp_bind_bucket *tcp_bound_hash[TCP_BHTABLE_SIZE]; + /* All sockets in TCP_LISTEN state will be in here. This is the only table * where wildcard'd TCP sockets can exist. Hash function here is just local * port number. */ struct sock *tcp_listening_hash[TCP_LHTABLE_SIZE]; -/* Ok, let's try this, I give up, we do need a local binding - * TCP hash as well as the others for fast bind/connect. - */ -struct sock *tcp_bound_hash[TCP_BHTABLE_SIZE]; +/* Register cache. */ +struct sock *tcp_regs[TCP_NUM_REGS]; /* * This array holds the first and last local port number. @@ -106,6 +107,7 @@ struct sock *tcp_bound_hash[TCP_BHTABLE_SIZE]; * 32768-61000 */ int sysctl_local_port_range[2] = { 1024, 4999 }; +int tcp_port_rover = (1024 - 1); static __inline__ int tcp_hashfn(__u32 laddr, __u16 lport, __u32 faddr, __u16 fport) @@ -123,155 +125,135 @@ static __inline__ int tcp_sk_hashfn(struct sock *sk) return tcp_hashfn(laddr, lport, faddr, fport); } -static int tcp_v4_verify_bind(struct sock *sk, unsigned short snum) +/* Invariant, sk->num is non-zero. */ +void tcp_bucket_unlock(struct sock *sk) { - struct sock *sk2; - int retval = 0, sk_reuse = sk->reuse; + struct tcp_bind_bucket *tb; + unsigned short snum = sk->num; SOCKHASH_LOCK(); - sk2 = tcp_bound_hash[tcp_bhashfn(snum)]; - for(; sk2 != NULL; sk2 = sk2->bind_next) { - if((sk2->num == snum) && (sk2 != sk)) { - unsigned char state = sk2->state; - int sk2_reuse = sk2->reuse; - - /* Two sockets can be bound to the same port if they're - * bound to different interfaces. - */ - - if(sk->bound_dev_if != sk2->bound_dev_if) - continue; - - if(!sk2->rcv_saddr || !sk->rcv_saddr) { - if((!sk2_reuse) || - (!sk_reuse) || - (state == TCP_LISTEN)) { - retval = 1; - break; - } - } else if(sk2->rcv_saddr == sk->rcv_saddr) { - if((!sk_reuse) || - (!sk2_reuse) || - (state == TCP_LISTEN)) { - retval = 1; - break; - } + for(tb = tcp_bound_hash[tcp_bhashfn(snum)]; tb; tb = tb->next) { + if(tb->port == snum) { + if(tb->owners == NULL && + (tb->flags & TCPB_FLAG_LOCKED)) { + tb->flags &= ~TCPB_FLAG_LOCKED; + tcp_inc_slow_timer(TCP_SLT_BUCKETGC); } + break; } } SOCKHASH_UNLOCK(); +} - return retval; +struct tcp_bind_bucket *tcp_bucket_create(unsigned short snum) +{ + struct tcp_bind_bucket *tb; + + tb = kmem_cache_alloc(tcp_bucket_cachep, SLAB_ATOMIC); + if(tb != NULL) { + struct tcp_bind_bucket **head = + &tcp_bound_hash[tcp_bhashfn(snum)]; + tb->port = snum; + tb->flags = TCPB_FLAG_LOCKED; + tb->owners = NULL; + if((tb->next = *head) != NULL) + tb->next->pprev = &tb->next; + *head = tb; + tb->pprev = head; + } + return tb; } -static __inline__ int tcp_lport_inuse(int num) +static int tcp_v4_verify_bind(struct sock *sk, unsigned short snum) { - struct sock *sk = tcp_bound_hash[tcp_bhashfn(num)]; + struct tcp_bind_bucket *tb; + int result = 0; - for(; sk != NULL; sk = sk->bind_next) { - if(sk->num == num) - return 1; + SOCKHASH_LOCK(); + for(tb = tcp_bound_hash[tcp_bhashfn(snum)]; + (tb && (tb->port != snum)); + tb = tb->next) + ; + if(tb && tb->owners) { + /* Fast path for reuse ports, see include/net/tcp.h for a very + * detailed description of why this works, and why it is worth + * the effort at all. -DaveM + */ + if((tb->flags & TCPB_FLAG_FASTREUSE) && + (sk->reuse != 0)) { + goto go_like_smoke; + } else { + struct sock *sk2; + int sk_reuse = sk->reuse; + + /* We must walk the whole port owner list in this case. -DaveM */ + for(sk2 = tb->owners; sk2; sk2 = sk2->bind_next) { + if(sk->bound_dev_if == sk2->bound_dev_if) { + if(!sk_reuse || !sk2->reuse || sk2->state == TCP_LISTEN) { + if(!sk2->rcv_saddr || + !sk->rcv_saddr || + (sk2->rcv_saddr == sk->rcv_saddr)) + break; + } + } + } + if(sk2 != NULL) + result = 1; + } } - return 0; + if((result == 0) && + (tb == NULL) && + (tcp_bucket_create(snum) == NULL)) + result = 1; +go_like_smoke: + SOCKHASH_UNLOCK(); + return result; } -/* Find a "good" local port, this is family independent. - * There are several strategies working in unison here to - * get the best possible performance. The current socket - * load is kept track of, if it is zero there is a strong - * likely hood that there is a zero length chain we will - * find with a small amount of searching, else the load is - * what we shoot for for when the chains all have at least - * one entry. The base helps us walk the chains in an - * order such that a good chain is found as quickly as possible. -DaveM - */ unsigned short tcp_good_socknum(void) { - static int start = 0; - static int binding_contour = 0; - int best = 0; - int size = 32767; /* a big num. */ - int retval = 0, i, end, bc; + struct tcp_bind_bucket *tb; + int low = sysctl_local_port_range[0]; + int high = sysctl_local_port_range[1]; + int remaining = high - low; + int rover; SOCKHASH_LOCK(); - if (start > sysctl_local_port_range[1] || start < sysctl_local_port_range[0]) - start = sysctl_local_port_range[0]; - i = tcp_bhashfn(start); - end = i + TCP_BHTABLE_SIZE; - bc = binding_contour; - do { - struct sock *sk = tcp_bound_hash[i&(TCP_BHTABLE_SIZE-1)]; - if(!sk) { - /* find the smallest value no smaller than start - * that has this hash value. - */ - retval = tcp_bhashnext(start-1,i&(TCP_BHTABLE_SIZE-1)); - - /* Check for decreasing load. */ - if (bc != 0) - binding_contour = 0; - goto done; - } else { - int j = 0; - do { sk = sk->bind_next; } while (++j < size && sk); - if (j < size) { - best = i&(TCP_BHTABLE_SIZE-1); - size = j; - if (bc && size <= bc) - goto verify; - } - } - } while(++i != end); - i = best; - - /* Socket load is increasing, adjust our load average. */ - binding_contour = size; -verify: - if (size < binding_contour) - binding_contour = size; - - retval = tcp_bhashnext(start-1,i); - - best = retval; /* mark the starting point to avoid infinite loops */ - while(tcp_lport_inuse(retval)) { - retval = tcp_bhashnext(retval,i); - if (retval > sysctl_local_port_range[1]) /* Upper bound */ - retval = tcp_bhashnext(sysctl_local_port_range[0],i); - if (retval == best) { - /* This hash chain is full. No answer. */ - retval = 0; - break; + rover = tcp_port_rover; + do { + rover += 1; + if((rover < low) || (rover > high)) + rover = low; + tb = tcp_bound_hash[tcp_bhashfn(rover)]; + for( ; tb; tb = tb->next) { + if(tb->port == rover) + goto next; } - } - -done: - start = (retval + 1); + break; + next: + } while(--remaining > 0); + tcp_port_rover = rover; + if((remaining <= 0) || (tcp_bucket_create(rover) == NULL)) + rover = 0; SOCKHASH_UNLOCK(); - return retval; + return rover; } static void tcp_v4_hash(struct sock *sk) { - unsigned char state; - - SOCKHASH_LOCK(); - state = sk->state; - if(state != TCP_CLOSE || !sk->dead) { + if (sk->state != TCP_CLOSE) { struct sock **skp; - if(state == TCP_LISTEN) - skp = &tcp_listening_hash[tcp_sk_listen_hashfn(sk)]; - else - skp = &tcp_established_hash[tcp_sk_hashfn(sk)]; - + SOCKHASH_LOCK(); + skp = &tcp_established_hash[(sk->hashent = tcp_sk_hashfn(sk))]; if((sk->next = *skp) != NULL) (*skp)->pprev = &sk->next; *skp = sk; sk->pprev = skp; tcp_sk_bindify(sk); + SOCKHASH_UNLOCK(); } - SOCKHASH_UNLOCK(); } static void tcp_v4_unhash(struct sock *sk) @@ -282,6 +264,7 @@ static void tcp_v4_unhash(struct sock *sk) sk->next->pprev = sk->pprev; *sk->pprev = sk->next; sk->pprev = NULL; + tcp_reg_zap(sk); tcp_sk_unbindify(sk); } SOCKHASH_UNLOCK(); @@ -293,30 +276,27 @@ static void tcp_v4_rehash(struct sock *sk) SOCKHASH_LOCK(); state = sk->state; - if(sk->pprev) { + if(sk->pprev != NULL) { if(sk->next) sk->next->pprev = sk->pprev; *sk->pprev = sk->next; sk->pprev = NULL; - tcp_sk_unbindify(sk); + tcp_reg_zap(sk); } - if(state != TCP_CLOSE || !sk->dead) { + if(state != TCP_CLOSE) { struct sock **skp; - if(state == TCP_LISTEN) { + if(state == TCP_LISTEN) skp = &tcp_listening_hash[tcp_sk_listen_hashfn(sk)]; - } else { - int hash= tcp_sk_hashfn(sk); - if(state == TCP_TIME_WAIT) - hash += (TCP_HTABLE_SIZE/2); - skp = &tcp_established_hash[hash]; - } + else + skp = &tcp_established_hash[(sk->hashent = tcp_sk_hashfn(sk))]; if((sk->next = *skp) != NULL) (*skp)->pprev = &sk->next; *skp = sk; sk->pprev = skp; - tcp_sk_bindify(sk); + if(state == TCP_LISTEN) + tcp_sk_bindify(sk); } SOCKHASH_UNLOCK(); } @@ -360,37 +340,64 @@ static struct sock *tcp_v4_lookup_listener(u32 daddr, unsigned short hnum, int d return result; } +/* Until this is verified... -DaveM */ +/* #define USE_QUICKSYNS */ + /* Sockets in TCP_CLOSE state are _always_ taken out of the hash, so * we need not check it for TCP lookups anymore, thanks Alexey. -DaveM + * It is assumed that this code only gets called from within NET_BH. */ static inline struct sock *__tcp_v4_lookup(struct tcphdr *th, - u32 saddr, u16 sport, u32 daddr, u16 dport, int dif) + u32 saddr, u16 sport, + u32 daddr, u16 dport, int dif) { unsigned short hnum = ntohs(dport); struct sock *sk; - int hash = tcp_hashfn(daddr, hnum, saddr, sport); + int hash; + +#ifdef USE_QUICKSYNS + /* Incomming connection short-cut. */ + if (th && th->syn == 1 && th->ack == 0) + goto listener_shortcut; +#endif + + /* Check TCP register quick cache first. */ + sk = TCP_RHASH(sport); + if(sk && + sk->daddr == saddr && /* remote address */ + sk->dummy_th.dest == sport && /* remote port */ + sk->num == hnum && /* local port */ + sk->rcv_saddr == daddr && /* local address */ + (!sk->bound_dev_if || sk->bound_dev_if == dif)) + goto hit; /* Optimize here for direct hit, only listening connections can - * have wildcards anyways. It is assumed that this code only - * gets called from within NET_BH. + * have wildcards anyways. */ - for(sk = tcp_established_hash[hash]; sk; sk = sk->next) + hash = tcp_hashfn(daddr, hnum, saddr, sport); + for(sk = tcp_established_hash[hash]; sk; sk = sk->next) { if(sk->daddr == saddr && /* remote address */ sk->dummy_th.dest == sport && /* remote port */ sk->num == hnum && /* local port */ sk->rcv_saddr == daddr && /* local address */ - (!sk->bound_dev_if || sk->bound_dev_if == dif)) + (!sk->bound_dev_if || sk->bound_dev_if == dif)) { + if (sk->state == TCP_ESTABLISHED) + TCP_RHASH(sport) = sk; goto hit; /* You sunk my battleship! */ - + } + } /* Must check for a TIME_WAIT'er before going to listener hash. */ - for(sk = tcp_established_hash[hash+(TCP_HTABLE_SIZE/2)]; sk; sk = sk->next) + for(sk = tcp_established_hash[hash+(TCP_HTABLE_SIZE/2)]; sk; sk = sk->next) { if(sk->daddr == saddr && /* remote address */ sk->dummy_th.dest == sport && /* remote port */ sk->num == hnum && /* local port */ sk->rcv_saddr == daddr && /* local address */ (!sk->bound_dev_if || sk->bound_dev_if == dif)) goto hit; - + } +#ifdef USE_QUICKSYNS +listener_shortcut: +#endif sk = tcp_v4_lookup_listener(daddr, hnum, dif); hit: return sk; @@ -402,20 +409,11 @@ __inline__ struct sock *tcp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport } #ifdef CONFIG_IP_TRANSPARENT_PROXY -#define secondlist(hpnum, sk, fpass) \ -({ struct sock *s1; if(!(sk) && (fpass)--) \ - s1 = tcp_bound_hash[tcp_bhashfn(hpnum)]; \ - else \ - s1 = (sk); \ - s1; \ -}) - -#define tcp_v4_proxy_loop_init(hnum, hpnum, sk, fpass) \ - secondlist((hpnum), tcp_bound_hash[tcp_bhashfn(hnum)],(fpass)) - -#define tcp_v4_proxy_loop_next(hnum, hpnum, sk, fpass) \ - secondlist((hpnum),(sk)->bind_next,(fpass)) - +/* Cleaned up a little and adapted to new bind bucket scheme. + * Oddly, this should increase performance here for + * transparent proxy, as tests within the inner loop have + * been eliminated. -DaveM + */ static struct sock *tcp_v4_proxy_lookup(unsigned short num, unsigned long raddr, unsigned short rnum, unsigned long laddr, struct device *dev, unsigned short pnum, @@ -436,51 +434,60 @@ static struct sock *tcp_v4_proxy_lookup(unsigned short num, unsigned long raddr, } /* This code must run only from NET_BH. */ - for(s = tcp_v4_proxy_loop_init(hnum, hpnum, s, firstpass); - s != NULL; - s = tcp_v4_proxy_loop_next(hnum, hpnum, s, firstpass)) { - if(s->num == hnum || s->num == hpnum) { - int score = 0; - if(s->dead && (s->state == TCP_CLOSE)) + { + struct tcp_bind_bucket *tb = tcp_bound_hash[tcp_bhashfn(hnum)]; + for( ; (tb && tb->port != hnum); tb = tb->next) + ; + if(tb == NULL) + goto next; + s = tb->owners; + } +pass2: + for(; s; s = s->bind_next) { + int score = 0; + if(s->rcv_saddr) { + if((s->num != hpnum || s->rcv_saddr != paddr) && + (s->num != hnum || s->rcv_saddr != laddr)) continue; - if(s->rcv_saddr) { - if((s->num != hpnum || s->rcv_saddr != paddr) && - (s->num != hnum || s->rcv_saddr != laddr)) - continue; - score++; - } - if(s->daddr) { - if(s->daddr != raddr) - continue; - score++; - } - if(s->dummy_th.dest) { - if(s->dummy_th.dest != rnum) - continue; - score++; - } - if(s->bound_dev_if) { - if(s->bound_dev_if != dif) - continue; - score++; - } - if(score == 4 && s->num == hnum) { - result = s; - break; - } else if(score > badness && (s->num == hpnum || s->rcv_saddr)) { - result = s; - badness = score; - } + score++; + } + if(s->daddr) { + if(s->daddr != raddr) + continue; + score++; + } + if(s->dummy_th.dest) { + if(s->dummy_th.dest != rnum) + continue; + score++; + } + if(s->bound_dev_if) { + if(s->bound_dev_if != dif) + continue; + score++; + } + if(score == 4 && s->num == hnum) { + result = s; + goto gotit; + } else if(score > badness && (s->num == hpnum || s->rcv_saddr)) { + result = s; + badness = score; } } +next: + if(firstpass--) { + struct tcp_bind_bucket *tb = tcp_bound_hash[tcp_bhashfn(hpnum)]; + for( ; (tb && tb->port != hpnum); tb = tb->next) + ; + if(tb) { + s = tb->owners; + goto pass2; + } + } +gotit: return result; } - -#undef secondlist -#undef tcp_v4_proxy_loop_init -#undef tcp_v4_proxy_loop_next - -#endif +#endif /* CONFIG_IP_TRANSPARENT_PROXY */ static inline __u32 tcp_v4_init_sequence(struct sock *sk, struct sk_buff *skb) { @@ -495,41 +502,35 @@ static inline __u32 tcp_v4_init_sequence(struct sock *sk, struct sk_buff *skb) /* * Check that a TCP address is unique, don't allow multiple - * connects to/from the same address + * connects to/from the same address. Actually we can optimize + * quite a bit, since the socket about to connect is still + * in TCP_CLOSE, a tcp_bind_bucket for the local port he will + * use will exist, with a NULL owners list. So check for that. + * The good_socknum and verify_bind scheme we use makes this + * work. */ -static int tcp_unique_address(u32 saddr, u16 snum, u32 daddr, u16 dnum) +static int tcp_unique_address(struct sock *sk) { - int retval = 1, hashent = tcp_hashfn(saddr, snum, daddr, dnum); - struct sock * sk; + struct tcp_bind_bucket *tb; + unsigned short snum = sk->num; + int retval = 1; - /* Make sure we are allowed to connect here. - * But freeze the hash while we snoop around. - */ + /* Freeze the hash while we snoop around. */ SOCKHASH_LOCK(); - sk = tcp_established_hash[hashent]; - for (; sk != NULL; sk = sk->next) { - if(sk->daddr == daddr && /* remote address */ - sk->dummy_th.dest == dnum && /* remote port */ - sk->num == snum && /* local port */ - sk->saddr == saddr) { /* local address */ - retval = 0; - goto out; - } - } - - /* Must check TIME_WAIT'ers too. */ - sk = tcp_established_hash[hashent + (TCP_HTABLE_SIZE/2)]; - for (; sk != NULL; sk = sk->next) { - if(sk->daddr == daddr && /* remote address */ - sk->dummy_th.dest == dnum && /* remote port */ - sk->num == snum && /* local port */ - sk->saddr == saddr) { /* local address */ - retval = 0; - goto out; + tb = tcp_bound_hash[tcp_bhashfn(snum)]; + for(; tb; tb = tb->next) { + if(tb->port == snum && tb->owners != NULL) { + /* Almost certainly the re-use port case, search the real hashes + * so it actually scales. + */ + sk = __tcp_v4_lookup(NULL, sk->daddr, sk->dummy_th.dest, + sk->rcv_saddr, snum, sk->bound_dev_if); + if((sk != NULL) && (sk->state != TCP_LISTEN)) + retval = 0; + break; } } -out: SOCKHASH_UNLOCK(); return retval; } @@ -578,8 +579,7 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) return -ENETUNREACH; } - if (!tcp_unique_address(rt->rt_src, sk->num, rt->rt_dst, - usin->sin_port)) { + if (!tcp_unique_address(sk)) { ip_rt_put(rt); return -EADDRNOTAVAIL; } @@ -587,7 +587,8 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) lock_sock(sk); /* Do this early, so there is less state to unwind on failure. */ - buff = sock_wmalloc(sk, MAX_SYN_SIZE, 0, GFP_KERNEL); + buff = sock_wmalloc(sk, (MAX_SYN_SIZE + sizeof(struct sk_buff)), + 0, GFP_KERNEL); if (buff == NULL) { release_sock(sk); ip_rt_put(rt); @@ -605,15 +606,13 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) sk->dummy_th.dest = usin->sin_port; - sk->write_seq = secure_tcp_sequence_number(sk->saddr, sk->daddr, + tp->write_seq = secure_tcp_sequence_number(sk->saddr, sk->daddr, sk->dummy_th.source, usin->sin_port); - tp->snd_wnd = 0; tp->snd_wl1 = 0; - tp->snd_wl2 = sk->write_seq; - tp->snd_una = sk->write_seq; - + tp->snd_wl2 = tp->write_seq; + tp->snd_una = tp->write_seq; tp->rcv_nxt = 0; sk->err = 0; @@ -635,14 +634,22 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) /* No failure conditions can result past this point. */ + /* We'll fix this up when we get a response from the other end. + * See tcp_input.c:tcp_rcv_state_process case TCP_SYN_SENT. + */ + tp->tcp_header_len = sizeof(struct tcphdr) + + (sysctl_tcp_timestamps ? TCPOLEN_TSTAMP_ALIGNED : 0); + th = (struct tcphdr *) skb_put(buff,sizeof(struct tcphdr)); buff->h.th = th; memcpy(th,(void *)&(sk->dummy_th), sizeof(*th)); - buff->seq = sk->write_seq++; + /* th->doff gets fixed up below if we tack on options. */ + + buff->seq = tp->write_seq++; th->seq = htonl(buff->seq); - tp->snd_nxt = sk->write_seq; - buff->end_seq = sk->write_seq; + tp->snd_nxt = tp->write_seq; + buff->end_seq = tp->write_seq; th->ack = 0; th->syn = 1; @@ -656,11 +663,9 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) if(sk->mtu < 64) sk->mtu = 64; /* Sanity limit */ - if (sk->user_mss) - sk->mss = sk->user_mss; - else - sk->mss = (sk->mtu - sizeof(struct iphdr) - - sizeof(struct tcphdr)); + sk->mss = (sk->mtu - sizeof(struct iphdr) - tp->tcp_header_len); + if(sk->user_mss) + sk->mss = min(sk->mss, sk->user_mss); if (sk->mss < 1) { printk(KERN_DEBUG "intial sk->mss below 1\n"); @@ -675,9 +680,8 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) &tp->rcv_wscale); th->window = htons(tp->rcv_wnd); - tmp = tcp_syn_build_options(buff, sk->mss, sysctl_tcp_sack, - sysctl_tcp_timestamps, - sysctl_tcp_window_scaling,tp->rcv_wscale); + tmp = tcp_syn_build_options(buff, sk->mss, sysctl_tcp_timestamps, + sysctl_tcp_window_scaling, tp->rcv_wscale); buff->csum = 0; th->doff = (sizeof(*th)+ tmp)>>2; @@ -686,9 +690,10 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) tcp_set_state(sk,TCP_SYN_SENT); /* Socket identity change complete, no longer - * in TCP_CLOSE, so rehash. + * in TCP_CLOSE, so enter ourselves into the + * hash tables. */ - tcp_v4_rehash(sk); + tcp_v4_hash(sk); tp->rto = rt->u.dst.rtt; @@ -715,6 +720,7 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) static int tcp_v4_sendmsg(struct sock *sk, struct msghdr *msg, int len) { + struct tcp_opt *tp; int retval = -EINVAL; /* Do sanity checking for sendmsg/sendto/send. */ @@ -740,7 +746,10 @@ static int tcp_v4_sendmsg(struct sock *sk, struct msghdr *msg, int len) lock_sock(sk); retval = tcp_do_sendmsg(sk, msg->msg_iovlen, msg->msg_iov, msg->msg_flags); - + /* Push out partial tail frames if needed. */ + tp = &(sk->tp_pinfo.af_tcp); + if(tp->send_head && tcp_snd_test(sk, tp->send_head)) + tcp_write_xmit(sk); release_sock(sk); out: @@ -854,7 +863,7 @@ void tcp_v4_err(struct sk_buff *skb, unsigned char *dp, int len) th = (struct tcphdr*)(dp+(iph->ihl<<2)); sk = tcp_v4_lookup(iph->daddr, th->dest, iph->saddr, th->source, skb->dev->ifindex); - if (sk == NULL) { + if (sk == NULL || sk->state == TCP_TIME_WAIT) { icmp_statistics.IcmpInErrors++; return; } @@ -1011,7 +1020,8 @@ static void tcp_v4_send_reset(struct sk_buff *skb) skb1->csum = csum_partial((u8 *) th1, sizeof(*th1), 0); th1->check = tcp_v4_check(th1, sizeof(*th1), skb1->nh.iph->saddr, skb1->nh.iph->daddr, skb1->csum); - /* FIXME: should this carry an options packet? */ + + /* Do not place TCP options in a reset. */ ip_queue_xmit(skb1); tcp_statistics.TcpOutSegs++; tcp_statistics.TcpOutRsts++; @@ -1063,6 +1073,14 @@ static void tcp_v4_send_synack(struct sock *sk, struct open_request *req) mss = (skb->dst->pmtu - sizeof(struct iphdr) - sizeof(struct tcphdr)); if (sk->user_mss) mss = min(mss, sk->user_mss); + if(req->tstamp_ok) + mss -= TCPOLEN_TSTAMP_ALIGNED; + else + req->mss += TCPOLEN_TSTAMP_ALIGNED; + + /* tcp_syn_build_options will do an skb_put() to obtain the TCP + * options bytes below. + */ skb->h.th = th = (struct tcphdr *) skb_put(skb, sizeof(struct tcphdr)); /* Don't offer more than they did. @@ -1081,9 +1099,8 @@ static void tcp_v4_send_synack(struct sock *sk, struct open_request *req) memset(th, 0, sizeof(struct tcphdr)); th->syn = 1; th->ack = 1; - th->source = #ifdef CONFIG_IP_TRANSPARENT_PROXY - req->lcl_port; /* LVE */ + th->source = req->lcl_port; /* LVE */ #else th->source = sk->dummy_th.source; #endif @@ -1104,16 +1121,7 @@ static void tcp_v4_send_synack(struct sock *sk, struct open_request *req) req->rcv_wscale = rcv_wscale; } th->window = htons(req->rcv_wnd); - - /* XXX Partial csum of 4 byte quantity is itself! -DaveM - * Yes, but it's a bit harder to special case now. It's - * now computed inside the tcp_v4_send_check() to clean up - * updating the options fields in the mainline send code. - * If someone thinks this is really bad let me know and - * I'll try to do it a different way. -- erics - */ - - tmp = tcp_syn_build_options(skb, req->mss, req->sack_ok, req->tstamp_ok, + tmp = tcp_syn_build_options(skb, req->mss, req->tstamp_ok, req->wscale_ok,req->rcv_wscale); skb->csum = 0; th->doff = (sizeof(*th) + tmp)>>2; @@ -1232,14 +1240,15 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr, req->rcv_wnd = 0; /* So that tcp_send_synack() knows! */ req->rcv_isn = skb->seq; - tp.tstamp_ok = tp.sack_ok = tp.wscale_ok = tp.snd_wscale = 0; + tp.tstamp_ok = tp.wscale_ok = tp.snd_wscale = 0; tp.in_mss = 536; tcp_parse_options(th,&tp,want_cookie); - if (tp.saw_tstamp) - req->ts_recent = tp.rcv_tsval; req->mss = tp.in_mss; + if (tp.saw_tstamp) { + req->mss -= TCPOLEN_TSTAMP_ALIGNED; + req->ts_recent = tp.rcv_tsval; + } req->tstamp_ok = tp.tstamp_ok; - req->sack_ok = tp.sack_ok; req->snd_wscale = tp.snd_wscale; req->wscale_ok = tp.wscale_ok; req->rmt_port = th->source; @@ -1289,6 +1298,113 @@ error: return 0; } +/* This is not only more efficient than what we used to do, it eliminates + * a lot of code duplication between IPv4/IPv6 SYN recv processing. -DaveM + */ +struct sock *tcp_create_openreq_child(struct sock *sk, struct open_request *req, struct sk_buff *skb) +{ + struct sock *newsk = sk_alloc(AF_INET, GFP_ATOMIC, 0); + + if(newsk != NULL) { + struct tcp_opt *newtp; + + memcpy(newsk, sk, sizeof(*newsk)); + newsk->sklist_next = NULL; + newsk->daddr = req->af.v4_req.rmt_addr; + newsk->rcv_saddr = req->af.v4_req.loc_addr; +#ifdef CONFIG_IP_TRANSPARENT_PROXY + newsk->num = ntohs(skb->h.th->dest); +#endif + newsk->state = TCP_SYN_RECV; + + /* Clone the TCP header template */ +#ifdef CONFIG_IP_TRANSPARENT_PROXY + newsk->dummy_th.source = req->lcl_port; +#endif + newsk->dummy_th.dest = req->rmt_port; + newsk->dummy_th.ack = 1; + newsk->dummy_th.doff = sizeof(struct tcphdr)>>2; + + newsk->sock_readers = 0; + atomic_set(&newsk->rmem_alloc, 0); + skb_queue_head_init(&newsk->receive_queue); + atomic_set(&newsk->wmem_alloc, 0); + skb_queue_head_init(&newsk->write_queue); + newsk->saddr = req->af.v4_req.loc_addr; + + newsk->done = 0; + newsk->proc = 0; + newsk->pair = NULL; + skb_queue_head_init(&newsk->back_log); + skb_queue_head_init(&newsk->error_queue); + + /* Now setup tcp_opt */ + newtp = &(newsk->tp_pinfo.af_tcp); + newtp->pred_flags = 0; + newtp->rcv_nxt = req->rcv_isn + 1; + newtp->snd_nxt = req->snt_isn + 1; + newtp->snd_una = req->snt_isn + 1; + newtp->srtt = 0; + newtp->ato = 0; + newtp->snd_wl1 = req->rcv_isn; + newtp->snd_wl2 = req->snt_isn; + newtp->snd_wnd = ntohs(skb->h.th->window); + newtp->max_window = newtp->snd_wnd; + newtp->pending = 0; + newtp->retransmits = 0; + newtp->last_ack_sent = req->rcv_isn + 1; + newtp->backoff = 0; + newtp->mdev = TCP_TIMEOUT_INIT; + newtp->snd_cwnd = 1; + newtp->rto = TCP_TIMEOUT_INIT; + newtp->packets_out = 0; + newtp->high_seq = 0; + newtp->snd_ssthresh = 0x7fffffff; + newtp->snd_cwnd_cnt = 0; + newtp->dup_acks = 0; + newtp->delayed_acks = 0; + init_timer(&newtp->retransmit_timer); + newtp->retransmit_timer.function = &tcp_retransmit_timer; + newtp->retransmit_timer.data = (unsigned long) newsk; + init_timer(&newtp->delack_timer); + newtp->delack_timer.function = &tcp_delack_timer; + newtp->delack_timer.data = (unsigned long) newsk; + skb_queue_head_init(&newtp->out_of_order_queue); + newtp->send_head = newtp->retrans_head = NULL; + newtp->rcv_wup = req->rcv_isn + 1; + newtp->write_seq = req->snt_isn + 1; + newtp->copied_seq = req->rcv_isn + 1; + + newtp->saw_tstamp = 0; + newtp->in_mss = 536; + + init_timer(&newtp->probe_timer); + newtp->probe_timer.function = &tcp_probe_timer; + newtp->probe_timer.data = (unsigned long) newsk; + newtp->probes_out = 0; + newtp->syn_seq = req->rcv_isn; + newtp->fin_seq = req->rcv_isn; + newtp->urg_data = 0; + tcp_synq_init(newtp); + newtp->syn_backlog = 0; + + /* Back to base struct sock members. */ + newsk->err = 0; + newsk->ack_backlog = 0; + newsk->max_ack_backlog = SOMAXCONN; + newsk->priority = 1; + + /* IP layer stuff */ + newsk->opt = req->af.v4_req.opt; + newsk->timeout = 0; + init_timer(&newsk->timer); + newsk->timer.function = &net_timer; + newsk->timer.data = (unsigned long) newsk; + newsk->socket = NULL; + } + return newsk; +} + struct sock * tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, struct open_request *req, struct dst_entry *dst) @@ -1301,98 +1417,14 @@ struct sock * tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, if (sk->ack_backlog > sk->max_ack_backlog) goto exit; /* head drop */ #endif - newsk = sk_alloc(AF_INET, GFP_ATOMIC); + newsk = tcp_create_openreq_child(sk, req, skb); if (!newsk) goto exit; #ifdef NEW_LISTEN sk->ack_backlog++; #endif - memcpy(newsk, sk, sizeof(*newsk)); - - /* Or else we die! -DaveM */ - newsk->sklist_next = NULL; - - newsk->opt = req->af.v4_req.opt; - skb_queue_head_init(&newsk->write_queue); - skb_queue_head_init(&newsk->receive_queue); - skb_queue_head_init(&newsk->out_of_order_queue); - skb_queue_head_init(&newsk->error_queue); - - /* Unused */ newtp = &(newsk->tp_pinfo.af_tcp); - newtp->send_head = NULL; - newtp->retrans_head = NULL; - - newtp->pending = 0; - - skb_queue_head_init(&newsk->back_log); - - newsk->prot->init(newsk); - - newtp->snd_cwnd_cnt = 0; - newtp->backoff = 0; - newsk->proc = 0; - newsk->done = 0; - newsk->pair = NULL; - atomic_set(&newsk->wmem_alloc, 0); - atomic_set(&newsk->rmem_alloc, 0); - newsk->localroute = sk->localroute; - - newsk->err = 0; - newsk->shutdown = 0; - newsk->ack_backlog = 0; - - newtp->fin_seq = req->rcv_isn; - newsk->syn_seq = req->rcv_isn; - newsk->state = TCP_SYN_RECV; - newsk->timeout = 0; - - newsk->write_seq = req->snt_isn; - - newtp->snd_wnd = ntohs(skb->h.th->window); - newtp->max_window = newtp->snd_wnd; - newtp->snd_wl1 = req->rcv_isn; - newtp->snd_wl2 = newsk->write_seq; - newtp->snd_una = newsk->write_seq++; - newtp->snd_nxt = newsk->write_seq; - - newsk->urg_data = 0; - newtp->packets_out = 0; - newtp->retransmits = 0; - newsk->linger=0; - newsk->destroy = 0; - init_timer(&newsk->timer); - newsk->timer.data = (unsigned long) newsk; - newsk->timer.function = &net_timer; - - tcp_init_xmit_timers(newsk); - - newsk->dummy_th.source = -#ifdef CONFIG_IP_TRANSPARENT_PROXY - req->lcl_port; /* LVE */ -#else - sk->dummy_th.source; -#endif - newsk->dummy_th.dest = req->rmt_port; - newsk->sock_readers=0; - - newtp->last_ack_sent = newtp->rcv_nxt = req->rcv_isn + 1; - newtp->rcv_wup = req->rcv_isn + 1; - newsk->copied_seq = req->rcv_isn + 1; - - newsk->socket = NULL; - -#ifdef CONFIG_IP_TRANSPARENT_PROXY - /* - * Deal with possibly redirected traffic by setting num to - * the intended destination port of the received packet. - */ - newsk->num = ntohs(skb->h.th->dest); -#endif - newsk->daddr = req->af.v4_req.rmt_addr; - newsk->saddr = req->af.v4_req.loc_addr; - newsk->rcv_saddr = req->af.v4_req.loc_addr; /* options / mss / route_cache */ if (dst == NULL) { @@ -1418,7 +1450,6 @@ struct sock * tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, if (newsk->mtu < 64) newsk->mtu = 64; - newtp->sack_ok = req->sack_ok; newtp->tstamp_ok = req->tstamp_ok; newtp->window_clamp = req->window_clamp; newtp->rcv_wnd = req->rcv_wnd; @@ -1433,8 +1464,8 @@ struct sock * tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, if (newtp->tstamp_ok) { newtp->ts_recent = req->ts_recent; newtp->ts_recent_stamp = jiffies; - newtp->tcp_header_len = sizeof(struct tcphdr) + 12; /* FIXME: define constant! */ - newsk->dummy_th.doff += 3; + newtp->tcp_header_len = sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED; + newsk->dummy_th.doff += (TCPOLEN_TSTAMP_ALIGNED >> 2); } else { newtp->tcp_header_len = sizeof(struct tcphdr); } @@ -1446,13 +1477,13 @@ struct sock * tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, /* Make sure our mtu is adjusted for headers. */ newsk->mss = min(req->mss, snd_mss) + sizeof(struct tcphdr) - newtp->tcp_header_len; - tcp_v4_hash(newsk); + /* Must use the af_specific ops here for the case of IPv6 mapped. */ + newsk->prot->hash(newsk); add_to_prot_sklist(newsk); return newsk; exit: - if (dst) - dst_release(dst); + dst_release(dst); return NULL; } @@ -1623,6 +1654,8 @@ int tcp_v4_rcv(struct sk_buff *skb, unsigned short len) skb->used = 0; + if (sk->state == TCP_TIME_WAIT) + goto do_time_wait; if (!sk->sock_readers) return tcp_v4_do_rcv(sk, skb); @@ -1636,6 +1669,12 @@ discard_it: /* Discard frame. */ kfree_skb(skb); return 0; + +do_time_wait: + if(tcp_timewait_state_process((struct tcp_tw_bucket *)sk, + skb, th, &(IPCB(skb)->opt), skb->len)) + goto no_tcp_socket; + goto discard_it; } int tcp_v4_build_header(struct sock *sk, struct sk_buff *skb) @@ -1770,33 +1809,21 @@ struct tcp_func ipv4_specific = { sizeof(struct sockaddr_in) }; +/* NOTE: A lot of things set to zero explicitly by call to + * sk_alloc() so need not be done here. + */ static int tcp_v4_init_sock(struct sock *sk) { struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); - skb_queue_head_init(&sk->out_of_order_queue); + skb_queue_head_init(&tp->out_of_order_queue); tcp_init_xmit_timers(sk); - tp->srtt = 0; tp->rto = TCP_TIMEOUT_INIT; /*TCP_WRITE_TIME*/ tp->mdev = TCP_TIMEOUT_INIT; - - tp->ato = 0; - tp->iat = (HZ/5) << 3; - - /* FIXME: tie this to sk->rcvbuf? (May be unnecessary) */ - /* tp->rcv_wnd = 8192; */ - tp->tstamp_ok = 0; - tp->sack_ok = 0; - tp->wscale_ok = 0; tp->in_mss = 536; - tp->snd_wscale = 0; - tp->sacks = 0; - tp->saw_tstamp = 0; - tp->syn_backlog = 0; - /* - * See draft-stevens-tcpca-spec-01 for discussion of the + /* See draft-stevens-tcpca-spec-01 for discussion of the * initialization of these values. */ tp->snd_cwnd = 1; @@ -1804,9 +1831,7 @@ static int tcp_v4_init_sock(struct sock *sk) sk->priority = 1; sk->state = TCP_CLOSE; - sk->max_ack_backlog = SOMAXCONN; - sk->mtu = 576; sk->mss = 536; @@ -1824,6 +1849,7 @@ static int tcp_v4_init_sock(struct sock *sk) static int tcp_v4_destroy_sock(struct sock *sk) { + struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); struct sk_buff *skb; tcp_clear_xmit_timers(sk); @@ -1836,9 +1862,17 @@ static int tcp_v4_destroy_sock(struct sock *sk) kfree_skb(skb); /* Cleans up our, hopefuly empty, out_of_order_queue. */ - while((skb = skb_dequeue(&sk->out_of_order_queue)) != NULL) + while((skb = skb_dequeue(&tp->out_of_order_queue)) != NULL) kfree_skb(skb); + /* Clean up a locked TCP bind bucket, this only happens if a + * port is allocated for a socket, but it never fully connects. + * In which case we will find num to be non-zero and daddr to + * be zero. + */ + if(sk->daddr == 0 && sk->num != 0) + tcp_bucket_unlock(sk); + return 0; } diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index fbae5cfa6..d8c3c6480 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_output.c,v 1.51 1998/01/15 22:40:39 freitag Exp $ + * Version: $Id: tcp_output.c,v 1.65 1998/03/15 12:07:03 davem Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -34,8 +34,6 @@ #include <net/tcp.h> -extern int sysctl_tcp_sack; -extern int sysctl_tcp_tsack; extern int sysctl_tcp_timestamps; extern int sysctl_tcp_window_scaling; @@ -45,7 +43,8 @@ static __inline__ void clear_delayed_acks(struct sock * sk) struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); tp->delayed_acks = 0; - sk->ack_backlog = 0; + if(tcp_in_quickack_mode(tp)) + tp->ato = ((HZ/100)*2); tcp_clear_xmit_timer(sk, TIME_DACK); } @@ -58,69 +57,26 @@ static __inline__ void update_send_head(struct sock *sk) tp->send_head = NULL; } -static __inline__ int tcp_snd_test(struct sock *sk, struct sk_buff *skb) -{ - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); - int nagle_check = 1; - int len; - - /* RFC 1122 - section 4.2.3.4 - * - * We must queue if - * - * a) The right edge of this frame exceeds the window - * b) There are packets in flight and we have a small segment - * [SWS avoidance and Nagle algorithm] - * (part of SWS is done on packetization) - * c) We are retransmiting [Nagle] - * d) We have too many packets 'in flight' - * - * Don't use the nagle rule for urgent data. - */ - len = skb->end_seq - skb->seq; - if (!sk->nonagle && len < (sk->mss >> 1) && tp->packets_out && - !skb->h.th->urg) - nagle_check = 0; - - return (nagle_check && tp->packets_out < tp->snd_cwnd && - !after(skb->end_seq, tp->snd_una + tp->snd_wnd) && - tp->retransmits == 0); -} - /* * This is the main buffer sending routine. We queue the buffer * having checked it is sane seeming. */ -void tcp_send_skb(struct sock *sk, struct sk_buff *skb) +void tcp_send_skb(struct sock *sk, struct sk_buff *skb, int force_queue) { - struct tcphdr * th = skb->h.th; + struct tcphdr *th = skb->h.th; struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); int size; /* Length of packet (not counting length of pre-tcp headers). */ size = skb->len - ((unsigned char *) th - skb->data); - /* Sanity check it.. */ - if (size < sizeof(struct tcphdr) || size > skb->len) { - printk(KERN_DEBUG "tcp_send_skb: bad skb " - "(skb = %p, data = %p, th = %p, len = %u)\n", - skb, skb->data, th, skb->len); - kfree_skb(skb); - return; - } - - /* If we have queued a header size packet.. (these crash a few - * tcp stacks if ack is not set) - * FIXME: What is the equivalent below when we have options? - */ - if (size == sizeof(struct tcphdr)) { - /* If it's got a syn or fin discard. */ - if(!th->syn && !th->fin) { - printk(KERN_DEBUG "tcp_send_skb: attempt to queue a bogon.\n"); - kfree_skb(skb); - return; - } + /* If there is a FIN or a SYN we add it onto the size. */ + if (th->fin || th->syn) { + if(th->syn) + size++; + if(th->fin) + size++; } /* Actual processing. */ @@ -129,14 +85,14 @@ void tcp_send_skb(struct sock *sk, struct sk_buff *skb) skb_queue_tail(&sk->write_queue, skb); - if (tp->send_head == NULL && tcp_snd_test(sk, skb)) { + if (!force_queue && tp->send_head == NULL && tcp_snd_test(sk, skb)) { struct sk_buff * buff; /* This is going straight out. */ tp->last_ack_sent = tp->rcv_nxt; th->ack_seq = htonl(tp->rcv_nxt); th->window = htons(tcp_select_window(sk)); - tcp_update_options((__u32 *)(th+1),tp); + tcp_update_options((__u32 *)(th + 1),tp); tp->af_specific->send_check(sk, th, size, skb); @@ -165,11 +121,10 @@ queue: /* Remember where we must start sending. */ if (tp->send_head == NULL) tp->send_head = skb; - if (tp->packets_out == 0 && !tp->pending) { + if (!force_queue && tp->packets_out == 0 && !tp->pending) { tp->pending = TIME_PROBE0; tcp_reset_xmit_timer(sk, TIME_PROBE0, tp->rto); } - return; } /* @@ -214,8 +169,6 @@ static int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len) buff->h.th = nth; memcpy(nth, th, tp->tcp_header_len); - /* FIXME: Make sure this gets tcp options right. */ - /* Correct the new header. */ buff->seq = skb->seq + len; buff->end_seq = skb->end_seq; @@ -281,14 +234,6 @@ static int tcp_wrxmit_frag(struct sock *sk, struct sk_buff *skb, int size) tp->send_head = skb; tp->packets_out--; return -1; - } else { -#if 0 - /* If tcp_fragment succeded then - * the send head is the resulting - * fragment - */ - tp->send_head = skb->next; -#endif } return 0; } @@ -346,9 +291,10 @@ void tcp_write_xmit(struct sock *sk) size = skb->len - (((unsigned char*)th) - skb->data); } - tp->last_ack_sent = th->ack_seq = htonl(tp->rcv_nxt); + tp->last_ack_sent = tp->rcv_nxt; + th->ack_seq = htonl(tp->rcv_nxt); th->window = rcv_wnd; - tcp_update_options((__u32 *)(th+1),tp); + tcp_update_options((__u32 *)(th + 1),tp); tp->af_specific->send_check(sk, th, size, skb); @@ -437,128 +383,44 @@ void tcp_write_xmit(struct sock *sk) * taken by headers, and the remaining space will be available for TCP data. * This should be accounted for correctly instead. */ -unsigned short tcp_select_window(struct sock *sk) +u32 __tcp_select_window(struct sock *sk) { struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; - int mss = sk->mss; - long free_space = sock_rspace(sk) / 2; - long window, cur_win; + unsigned int mss = sk->mss; + unsigned int free_space; + u32 window, cur_win; + free_space = (sk->rcvbuf - atomic_read(&sk->rmem_alloc)) / 2; if (tp->window_clamp) { free_space = min(tp->window_clamp, free_space); mss = min(tp->window_clamp, mss); - } -#ifdef NO_ANK_FIX - /* I am tired of this message */ - else - printk(KERN_DEBUG "Clamp failure. Water leaking.\n"); -#endif + } else { + printk("tcp_select_window: tp->window_clamp == 0.\n"); + } if (mss < 1) { mss = 1; - printk(KERN_DEBUG "tcp_select_window: mss fell to 0.\n"); + printk("tcp_select_window: sk->mss fell to 0.\n"); } - /* compute the actual window i.e. - * old_window - received_bytes_on_that_win - */ - cur_win = tp->rcv_wnd - (tp->rcv_nxt - tp->rcv_wup); - window = tp->rcv_wnd; - - if (cur_win < 0) { - cur_win = 0; -#ifdef NO_ANK_FIX - /* And this too. */ - printk(KERN_DEBUG "TSW: win < 0 w=%d 1=%u 2=%u\n", - tp->rcv_wnd, tp->rcv_nxt, tp->rcv_wup); -#endif - } - - if (free_space < sk->rcvbuf/4 && free_space < mss/2) + cur_win = tcp_receive_window(tp); + if (free_space < sk->rcvbuf/4 && free_space < mss/2) { window = 0; - - /* Get the largest window that is a nice multiple of mss. - * Window clamp already applied above. - * If our current window offering is within 1 mss of the - * free space we just keep it. This prevents the divide - * and multiply from happening most of the time. - * We also don't do any window rounding when the free space - * is too small. - */ - if (window < free_space - mss && free_space > mss) - window = (free_space/mss)*mss; - - /* Never shrink the offered window */ - if (window < cur_win) - window = cur_win; - - tp->rcv_wnd = window; - tp->rcv_wup = tp->rcv_nxt; - return window >> tp->rcv_wscale; /* RFC1323 scaling applied */ -} - -#if 0 -/* Old algorithm for window selection */ -unsigned short tcp_select_window(struct sock *sk) -{ - struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; - int mss = sk->mss; - long free_space = sock_rspace(sk); - long window, cur_win, usable; - - if (tp->window_clamp) { - free_space = min(tp->window_clamp, free_space); - mss = min(tp->window_clamp, mss); - } - - /* compute the actual window i.e. - * old_window - received_bytes_on_that_win - */ - cur_win = tp->rcv_wnd - (tp->rcv_nxt - tp->rcv_wup); - window = tp->rcv_wnd; - - if (cur_win < 0) { - cur_win = 0; - printk(KERN_DEBUG "TSW: win < 0 w=%d 1=%u 2=%u\n", - tp->rcv_wnd, tp->rcv_nxt, tp->rcv_wup); - } - - /* RFC 1122: - * "the suggested [SWS] avoidance algoritm for the receiver is to keep - * RECV.NEXT + RCV.WIN fixed until: - * RCV.BUFF - RCV.USER - RCV.WINDOW >= min(1/2 RCV.BUFF, MSS)" - * - * i.e. don't raise the right edge of the window until you can raise - * it at least MSS bytes. - */ - - usable = free_space - cur_win; - if (usable < 0) - usable = 0; - - if (window < usable) { - /* Window is not blocking the sender - * and we have enough free space for it - */ - if (cur_win > (sk->mss << 1)) - goto out; - } - - if (window >= usable) { - /* We are offering too much, cut it down... - * but don't shrink the window - */ - window = max(usable, cur_win); } else { - while ((usable - window) >= mss) - window += mss; + /* Get the largest window that is a nice multiple of mss. + * Window clamp already applied above. + * If our current window offering is within 1 mss of the + * free space we just keep it. This prevents the divide + * and multiply from happening most of the time. + * We also don't do any window rounding when the free space + * is too small. + */ + window = tp->rcv_wnd; + if ((window <= (free_space - mss)) || (window > free_space)) + window = (free_space/mss)*mss; } -out: - tp->rcv_wnd = window; - tp->rcv_wup = tp->rcv_nxt; return window; } -#endif static int tcp_retrans_try_collapse(struct sock *sk, struct sk_buff *skb) { @@ -729,84 +591,123 @@ void tcp_do_retransmit(struct sock *sk, int all) } } -/* - * Send a fin. +/* Send a fin. The caller locks the socket for us. This cannot be + * allowed to fail queueing a FIN frame under any circumstances. */ - void tcp_send_fin(struct sock *sk) { - struct tcphdr *th =(struct tcphdr *)&sk->dummy_th; struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); - struct tcphdr *t1; - struct sk_buff *buff; - int tmp; - buff = sock_wmalloc(sk, BASE_ACK_SIZE + tp->tcp_header_len, 1, GFP_KERNEL); - if (buff == NULL) { - /* FIXME: This is a disaster if it occurs. */ - printk(KERN_INFO "tcp_send_fin: Impossible malloc failure"); - return; - } + /* Optimization, tack on the FIN if we have a queue of + * unsent frames. + */ + if(tp->send_head != NULL) { + struct sk_buff *tail = skb_peek_tail(&sk->write_queue); + struct tcphdr *th = tail->h.th; + int data_len; + + /* Unfortunately tcp_write_xmit won't check for going over + * the MSS due to the FIN sequence number, so we have to + * watch out for it here. + */ + data_len = (tail->tail - (((unsigned char *)th)+tp->tcp_header_len)); + if(data_len >= sk->mss) + goto build_new_frame; /* ho hum... */ - /* Administrivia. */ - buff->csum = 0; + /* tcp_write_xmit() will checksum the header etc. for us. */ + th->fin = 1; + tail->end_seq++; + } else { + struct sk_buff *buff; + struct tcphdr *th; - /* Put in the IP header and routing stuff. */ - tmp = tp->af_specific->build_net_header(sk, buff); - if (tmp < 0) { - int t; +build_new_frame: + buff = sock_wmalloc(sk, + (BASE_ACK_SIZE + tp->tcp_header_len + + sizeof(struct sk_buff)), + 1, GFP_KERNEL); + if (buff == NULL) { + /* We can only fail due to low memory situations, not + * due to going over our sndbuf limits (due to the + * force flag passed to sock_wmalloc). So just keep + * trying. We cannot allow this fail. The socket is + * still locked, so we need not check if the connection + * was reset in the meantime etc. + */ + goto build_new_frame; + } - /* FIXME: We must not throw this out. Eventually we must - * put a FIN into the queue, otherwise it never gets queued. - */ - kfree_skb(buff); - sk->write_seq++; - t = del_timer(&sk->timer); - if (t) - add_timer(&sk->timer); - else - tcp_reset_msl_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); - return; - } - - /* We ought to check if the end of the queue is a buffer and - * if so simply add the fin to that buffer, not send it ahead. - */ - t1 =(struct tcphdr *)skb_put(buff,tp->tcp_header_len); - buff->h.th = t1; - tcp_build_options((__u32 *)(t1+1),tp); - - memcpy(t1, th, sizeof(*t1)); - buff->seq = sk->write_seq; - sk->write_seq++; - buff->end_seq = sk->write_seq; - t1->seq = htonl(buff->seq); - t1->ack_seq = htonl(tp->rcv_nxt); - t1->window = htons(tcp_select_window(sk)); - t1->fin = 1; - - tp->af_specific->send_check(sk, t1, tp->tcp_header_len, buff); - - /* The fin can only be transmited after the data. */ - skb_queue_tail(&sk->write_queue, buff); - if (tp->send_head == NULL) { - /* FIXME: BUG! we need to check if the fin fits into the window - * here. If not we need to do window probing (sick, but true) + /* Administrivia. */ + buff->csum = 0; + + /* Put in the IP header and routing stuff. + * + * FIXME: + * We can fail if the interface for the route + * this socket takes goes down right before + * we get here. ANK is there a way to point + * this into a "black hole" route in such a + * case? Ideally, we should still be able to + * queue this and let the retransmit timer + * keep trying until the destination becomes + * reachable once more. -DaveM */ - struct sk_buff *skb1; + if(tp->af_specific->build_net_header(sk, buff) < 0) { + kfree_skb(buff); + goto update_write_seq; + } + th = (struct tcphdr *) skb_put(buff, tp->tcp_header_len); + buff->h.th = th; - tp->packets_out++; - tp->snd_nxt = sk->write_seq; - buff->when = jiffies; + memcpy(th, (void *) &(sk->dummy_th), sizeof(*th)); + th->seq = htonl(tp->write_seq); + th->fin = 1; + tcp_build_options((__u32 *)(th + 1), tp); - skb1 = skb_clone(buff, GFP_KERNEL); - if (skb1) { - skb_set_owner_w(skb1, sk); - tp->af_specific->queue_xmit(skb1); - } + /* This makes sure we do things like abide by the congestion + * window and other constraints which prevent us from sending. + */ + tcp_send_skb(sk, buff, 0); + } +update_write_seq: + /* So that we recognize the ACK coming back for + * this FIN as being legitimate. + */ + tp->write_seq++; +} - if (!tcp_timer_is_set(sk, TIME_RETRANS)) - tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto); +/* We get here when a process closes a file descriptor (either due to + * an explicit close() or as a byproduct of exit()'ing) and there + * was unread data in the receive queue. This behavior is recommended + * by draft-ietf-tcpimpl-prob-03.txt section 3.10. -DaveM + */ +void tcp_send_active_reset(struct sock *sk) +{ + struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct sk_buff *skb; + struct tcphdr *th; + +again: + /* NOTE: No TCP options attached and we never retransmit this. */ + skb = sock_wmalloc(sk, (BASE_ACK_SIZE + sizeof(*th)), 1, GFP_KERNEL); + if(skb == NULL) + goto again; + skb->csum = 0; + if(tp->af_specific->build_net_header(sk, skb) < 0) { + kfree_skb(skb); + } else { + th = (struct tcphdr *) skb_put(skb, sizeof(*th)); + memcpy(th, &(sk->dummy_th), sizeof(*th)); + th->seq = htonl(tp->write_seq); + th->rst = 1; + th->doff = sizeof(*th) / 4; + tp->last_ack_sent = tp->rcv_nxt; + th->ack_seq = htonl(tp->rcv_nxt); + th->window = htons(tcp_select_window(sk)); + tp->af_specific->send_check(sk, th, sizeof(*th), skb); + tp->af_specific->queue_xmit(skb); + tcp_statistics.TcpOutSegs++; + tcp_statistics.TcpOutRsts++; } } @@ -814,6 +715,9 @@ void tcp_send_fin(struct sock *sk) * a SYN packet that crossed the incoming SYN that caused this routine * to get called. If this assumption fails then the initial rcv_wnd * and rcv_wscale values will not be correct. + * + * XXX When you have time Dave, redo this to use tcp_send_skb() just + * XXX like tcp_send_fin() above now does.... -DaveM */ int tcp_send_synack(struct sock *sk) { @@ -823,7 +727,7 @@ int tcp_send_synack(struct sock *sk) struct tcphdr *th; int tmp; - skb = sock_wmalloc(sk, MAX_SYN_SIZE, 1, GFP_ATOMIC); + skb = sock_wmalloc(sk, MAX_SYN_SIZE + sizeof(struct sk_buff), 1, GFP_ATOMIC); if (skb == NULL) return -ENOMEM; @@ -855,8 +759,7 @@ int tcp_send_synack(struct sock *sk) tp->last_ack_sent = th->ack_seq = htonl(tp->rcv_nxt); tmp = tcp_syn_build_options(skb, sk->mss, - tp->sack_ok, tp->tstamp_ok, - tp->wscale_ok,tp->rcv_wscale); + tp->tstamp_ok, tp->wscale_ok, tp->rcv_wscale); skb->csum = 0; th->doff = (sizeof(*th) + tmp)>>2; @@ -880,31 +783,24 @@ int tcp_send_synack(struct sock *sk) } /* - * Set up the timers for sending a delayed ack.. - * - * rules for delaying an ack: - * - delay time <= 0.5 HZ - * - must send at least every 2 full sized packets - * - we don't have a window update to send + * Send out a delayed ack, the caller does the policy checking + * to see if we should even be here. See tcp_input.c:tcp_ack_snd_check() + * for details. */ -void tcp_send_delayed_ack(struct sock * sk, int max_timeout) +void tcp_send_delayed_ack(struct tcp_opt *tp, int max_timeout) { - struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; - unsigned long timeout, now; + unsigned long timeout; - /* Calculate new timeout. */ - now = jiffies; + /* Stay within the limit we were given */ timeout = tp->ato; - - if (timeout > max_timeout || - ((tp->rcv_nxt - tp->rcv_wup) > (sk->mss << 2))) - timeout = now; - else - timeout += now; + if (timeout > max_timeout) + timeout = max_timeout; + timeout += jiffies; /* Use new timeout only if there wasn't a older one earlier. */ - if (!del_timer(&tp->delack_timer) || timeout < tp->delack_timer.expires) + if ((!tp->delack_timer.prev || !del_timer(&tp->delack_timer)) || + (timeout < tp->delack_timer.expires)) tp->delack_timer.expires = timeout; add_timer(&tp->delack_timer); @@ -928,8 +824,6 @@ void tcp_send_ack(struct sock *sk) /* We need to grab some memory, and put together an ack, * and then put it into the queue to be sent. - * FIXME: is it better to waste memory here and use a - * constant sized ACK? */ buff = sock_wmalloc(sk, BASE_ACK_SIZE + tp->tcp_header_len, 1, GFP_ATOMIC); if (buff == NULL) { @@ -938,7 +832,7 @@ void tcp_send_ack(struct sock *sk) * bandwidth on slow links to send a spare ack than * resend packets. */ - tcp_send_delayed_ack(sk, HZ/2); + tcp_send_delayed_ack(tp, HZ/2); return; } @@ -956,22 +850,16 @@ void tcp_send_ack(struct sock *sk) th = (struct tcphdr *)skb_put(buff,tp->tcp_header_len); memcpy(th, &sk->dummy_th, sizeof(struct tcphdr)); - tcp_build_options((__u32 *)(th+1),tp); /* Swap the send and the receive. */ th->window = ntohs(tcp_select_window(sk)); th->seq = ntohl(tp->snd_nxt); tp->last_ack_sent = tp->rcv_nxt; th->ack_seq = htonl(tp->rcv_nxt); + tcp_build_and_update_options((__u32 *)(th + 1), tp); /* Fill in the packet and send it. */ tp->af_specific->send_check(sk, th, tp->tcp_header_len, buff); - -#if 0 - SOCK_DEBUG(sk, "\rtcp_send_ack: seq %x ack %x\n", - tp->snd_nxt, tp->rcv_nxt); -#endif - tp->af_specific->queue_xmit(buff); tcp_statistics.TcpOutSegs++; } @@ -1017,6 +905,7 @@ void tcp_write_wakeup(struct sock *sk) } th = skb->h.th; + tcp_update_options((__u32 *)(th + 1), tp); tp->af_specific->send_check(sk, th, th->doff * 4 + win_size, skb); buff = skb_clone(skb, GFP_ATOMIC); if (buff == NULL) @@ -1047,25 +936,19 @@ void tcp_write_wakeup(struct sock *sk) return; } - t1 = (struct tcphdr *) skb_put(buff, sizeof(struct tcphdr)); + t1 = (struct tcphdr *) skb_put(buff, tp->tcp_header_len); memcpy(t1,(void *) &sk->dummy_th, sizeof(*t1)); - /* FIXME: should zero window probes have SACK and/or TIMESTAMP data? - * If so we have to tack them on here. - */ /* Use a previous sequence. * This should cause the other end to send an ack. */ t1->seq = htonl(tp->snd_nxt-1); -/* t1->fin = 0; -- We are sending a 'previous' sequence, and 0 bytes of data - thus no FIN bit */ t1->ack_seq = htonl(tp->rcv_nxt); t1->window = htons(tcp_select_window(sk)); + tcp_build_and_update_options((__u32 *)(t1 + 1), tp); - /* Value from dummy_th may be larger. */ - t1->doff = sizeof(struct tcphdr)/4; - - tp->af_specific->send_check(sk, t1, sizeof(*t1), buff); + tp->af_specific->send_check(sk, t1, tp->tcp_header_len, buff); } /* Send it. */ diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index 76ccedab2..fdf8f50ec 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_timer.c,v 1.5 1998/03/03 01:23:44 ralf Exp $ + * Version: $Id: tcp_timer.c,v 1.6 1998/03/17 22:18:35 ralf Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -31,6 +31,7 @@ int sysctl_tcp_retries2 = TCP_RETR2; static void tcp_sltimer_handler(unsigned long); static void tcp_syn_recv_timer(unsigned long); static void tcp_keepalive(unsigned long data); +static void tcp_bucketgc(unsigned long); struct timer_list tcp_slow_timer = { NULL, NULL, @@ -41,7 +42,8 @@ struct timer_list tcp_slow_timer = { struct tcp_sl_timer tcp_slt_array[TCP_SLT_MAX] = { {ATOMIC_INIT(0), TCP_SYNACK_PERIOD, 0, tcp_syn_recv_timer},/* SYNACK */ - {ATOMIC_INIT(0), TCP_KEEPALIVE_PERIOD, 0, tcp_keepalive} /* KEEPALIVE */ + {ATOMIC_INIT(0), TCP_KEEPALIVE_PERIOD, 0, tcp_keepalive}, /* KEEPALIVE */ + {ATOMIC_INIT(0), TCP_BUCKETGC_PERIOD, 0, tcp_bucketgc} /* BUCKETGC */ }; const char timer_bug_msg[] = KERN_DEBUG "tcpbug: unknown timer value\n"; @@ -87,20 +89,24 @@ void tcp_reset_xmit_timer(struct sock *sk, int what, unsigned long when) * The delayed ack timer can be set if we are changing the * retransmit timer when removing acked frames. */ - del_timer(&tp->probe_timer); - del_timer(&tp->retransmit_timer); + if(tp->probe_timer.prev) + del_timer(&tp->probe_timer); + if(tp->retransmit_timer.prev) + del_timer(&tp->retransmit_timer); tp->retransmit_timer.expires=jiffies+when; add_timer(&tp->retransmit_timer); break; case TIME_DACK: - del_timer(&tp->delack_timer); + if(tp->delack_timer.prev) + del_timer(&tp->delack_timer); tp->delack_timer.expires=jiffies+when; add_timer(&tp->delack_timer); break; case TIME_PROBE0: - del_timer(&tp->probe_timer); + if(tp->probe_timer.prev) + del_timer(&tp->probe_timer); tp->probe_timer.expires=jiffies+when; add_timer(&tp->probe_timer); break; @@ -118,9 +124,12 @@ void tcp_clear_xmit_timers(struct sock *sk) { struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; - del_timer(&tp->retransmit_timer); - del_timer(&tp->delack_timer); - del_timer(&tp->probe_timer); + if(tp->retransmit_timer.prev) + del_timer(&tp->retransmit_timer); + if(tp->delack_timer.prev) + del_timer(&tp->delack_timer); + if(tp->probe_timer.prev) + del_timer(&tp->probe_timer); } static int tcp_write_err(struct sock *sk, int force) @@ -131,9 +140,8 @@ static int tcp_write_err(struct sock *sk, int force) tcp_clear_xmit_timers(sk); /* Time wait the socket. */ - if (!force && (1<<sk->state) & (TCPF_FIN_WAIT1|TCPF_FIN_WAIT2|TCPF_CLOSING)) { - tcp_set_state(sk,TCP_TIME_WAIT); - tcp_reset_msl_timer (sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); + if (!force && ((1<<sk->state) & (TCPF_FIN_WAIT1|TCPF_FIN_WAIT2|TCPF_CLOSING))) { + tcp_time_wait(sk); } else { /* Clean up time. */ tcp_set_state(sk, TCP_CLOSE); @@ -173,9 +181,8 @@ static int tcp_write_timeout(struct sock *sk) return 1; } - -void tcp_delack_timer(unsigned long data) { - +void tcp_delack_timer(unsigned long data) +{ struct sock *sk = (struct sock*)data; if(sk->zapped) @@ -185,8 +192,8 @@ void tcp_delack_timer(unsigned long data) { tcp_read_wakeup(sk); } -void tcp_probe_timer(unsigned long data) { - +void tcp_probe_timer(unsigned long data) +{ struct sock *sk = (struct sock*)data; struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; @@ -212,10 +219,9 @@ void tcp_probe_timer(unsigned long data) { sk->err = ETIMEDOUT; sk->error_report(sk); - /* Time wait the socket. */ if ((1<<sk->state) & (TCPF_FIN_WAIT1|TCPF_FIN_WAIT2|TCPF_CLOSING)) { - tcp_set_state(sk, TCP_TIME_WAIT); - tcp_reset_msl_timer (sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); + /* Time wait the socket. */ + tcp_time_wait(sk); } else { /* Clean up time. */ tcp_set_state(sk, TCP_CLOSE); @@ -252,6 +258,35 @@ static __inline__ int tcp_keepopen_proc(struct sock *sk) return res; } +/* Garbage collect TCP bind buckets. */ +static void tcp_bucketgc(unsigned long __unused) +{ + int i; + + for(i = 0; i < TCP_BHTABLE_SIZE; i++) { + struct tcp_bind_bucket *tb = tcp_bound_hash[i]; + + while(tb) { + struct tcp_bind_bucket *next = tb->next; + + if((tb->owners == NULL) && + !(tb->flags & TCPB_FLAG_LOCKED)) { + /* Eat timer reference. */ + tcp_dec_slow_timer(TCP_SLT_BUCKETGC); + + /* Unlink bucket. */ + if(tb->next) + tb->next->pprev = tb->pprev; + *tb->pprev = tb->next; + + /* Finally, free it up. */ + kmem_cache_free(tcp_bucket_cachep, tb); + } + tb = next; + } + } +} + /* * Check all sockets for keepalive timer * Called every 75 seconds diff --git a/net/ipv4/timer.c b/net/ipv4/timer.c index fe02b3f4c..79ae3309e 100644 --- a/net/ipv4/timer.c +++ b/net/ipv4/timer.c @@ -5,7 +5,7 @@ * * TIMER - implementation of software timers for IP. * - * Version: $Id: timer.c,v 1.7 1997/09/17 18:50:26 freitag Exp $ + * Version: $Id: timer.c,v 1.2 1997/12/16 05:37:48 ralf Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -52,76 +52,52 @@ void net_delete_timer (struct sock *t) { - unsigned long flags; - - save_flags (flags); - cli(); - + if(t->timer.prev) + del_timer (&t->timer); t->timeout = 0; - del_timer (&t->timer); - - restore_flags (flags); } void net_reset_timer (struct sock *t, int timeout, unsigned long len) { net_delete_timer (t); t->timeout = timeout; -#if 1 - /* FIXME: ??? */ - if ((int) len < 0) /* prevent close to infinite timers. THEY _DO_ */ - len = 3; /* happen (negative values ?) - don't ask me why ! -FB */ -#endif t->timer.expires = jiffies+len; add_timer (&t->timer); } - -/* - * Now we will only be called whenever we need to do - * something, but we must be sure to process all of the - * sockets that need it. +/* Now we will only be called whenever we need to do + * something, but we must be sure to process all of the + * sockets that need it. */ - void net_timer (unsigned long data) { struct sock *sk = (struct sock*)data; int why = sk->timeout; - /* - * only process if socket is not in use - */ - - if (sk->sock_readers) - { + /* Only process if socket is not in use. */ + if (sk->sock_readers) { sk->timer.expires = jiffies+HZ; add_timer(&sk->timer); - sti(); return; } /* Always see if we need to send an ack. */ - - if (sk->ack_backlog && !sk->zapped) - { + if (sk->tp_pinfo.af_tcp.delayed_acks && !sk->zapped) { sk->prot->read_wakeup (sk); - if (! sk->dead) - sk->data_ready(sk,0); + if (!sk->dead) + sk->data_ready(sk,0); } /* Now we need to figure out why the socket was on the timer. */ - - switch (why) - { + switch (why) { case TIME_DONE: - /* If the socket hasn't been closed off, re-try a bit later */ + /* If the socket hasn't been closed off, re-try a bit later. */ if (!sk->dead) { net_reset_timer(sk, TIME_DONE, TCP_DONE_TIME); break; } - if (sk->state != TCP_CLOSE) - { + if (sk->state != TCP_CLOSE) { printk (KERN_DEBUG "non CLOSE socket in time_done\n"); break; } @@ -129,11 +105,9 @@ void net_timer (unsigned long data) break; case TIME_DESTROY: - /* - * We've waited for a while for all the memory associated with - * the socket to be freed. - */ - + /* We've waited for a while for all the memory associated with + * the socket to be freed. + */ destroy_sock(sk); break; @@ -148,7 +122,8 @@ void net_timer (unsigned long data) break; default: - printk (KERN_DEBUG "net_timer: timer expired - reason %d is unknown\n", why); + /* I want to see these... */ + printk ("net_timer: timer expired - reason %d is unknown\n", why); break; } } diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index f355caa85..6ba50b280 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -5,7 +5,7 @@ * * The User Datagram Protocol (UDP). * - * Version: $Id: udp.c,v 1.3 1998/03/03 01:23:44 ralf Exp $ + * Version: $Id: udp.c,v 1.4 1998/03/17 22:18:36 ralf Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -828,7 +828,7 @@ int udp_ioctl(struct sock *sk, int cmd, unsigned long arg) * of this packet since that is all * that will be read. */ - amount = skb->tail - skb->h.raw; + amount = skb->len - sizeof(struct udphdr); } return put_user(amount, (int *)arg); } @@ -1033,17 +1033,18 @@ static inline void udp_deliver(struct sock *sk, struct sk_buff *skb) /* * Multicasts and broadcasts go to each listener. + * + * Note: called only from the BH handler context, + * so we don't need to lock the hashes. */ static int udp_v4_mcast_deliver(struct sk_buff *skb, struct udphdr *uh, u32 saddr, u32 daddr) { struct sock *sk; - int given = 0; - SOCKHASH_LOCK(); sk = udp_hash[ntohs(uh->dest) & (UDP_HTABLE_SIZE - 1)]; sk = udp_v4_mcast_next(sk, uh->dest, saddr, uh->source, daddr); - if(sk) { + if (sk) { struct sock *sknext = NULL; do { @@ -1058,10 +1059,7 @@ static int udp_v4_mcast_deliver(struct sk_buff *skb, struct udphdr *uh, udp_deliver(sk, skb1); sk = sknext; } while(sknext); - given = 1; - } - SOCKHASH_UNLOCK(); - if(!given) + } else kfree_skb(skb); return 0; } diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index c4faba4b7..4a4060601 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: addrconf.c,v 1.32 1997/12/27 20:41:18 kuznet Exp $ + * $Id: addrconf.c,v 1.37 1998/03/08 20:52:46 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -1753,6 +1753,8 @@ static void addrconf_sysctl_register(struct inet6_dev *idev, struct ipv6_devconf t->sysctl_header = register_sysctl_table(t->addrconf_root_dir, 0); if (t->sysctl_header == NULL) kfree(t); + else + p->sysctl = t; } static void addrconf_sysctl_unregister(struct ipv6_devconf *p) diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index b0a0eb702..bc5ba892a 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -7,7 +7,7 @@ * * Adapted from linux/net/ipv4/af_inet.c * - * $Id: af_inet6.c,v 1.24 1997/12/13 21:53:08 kuznet Exp $ + * $Id: af_inet6.c,v 1.28 1998/03/08 05:56:49 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -71,7 +71,7 @@ static int inet6_create(struct socket *sock, int protocol) struct sock *sk; struct proto *prot; - sk = sk_alloc(AF_INET6, GFP_KERNEL); + sk = sk_alloc(AF_INET6, GFP_KERNEL, 1); if (sk == NULL) goto do_oom; @@ -139,8 +139,7 @@ static int inet6_create(struct socket *sock, int protocol) * creation time automatically shares. */ sk->dummy_th.source = ntohs(sk->num); - if(sk->prot->hash) - sk->prot->hash(sk); + sk->prot->hash(sk); add_to_prot_sklist(sk); } diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index 6b7508666..af29057ec 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: exthdrs.c,v 1.4 1997/03/18 18:24:29 davem Exp $ + * $Id: exthdrs.c,v 1.5 1998/02/12 07:43:39 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index b84dc9268..96867403b 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: icmp.c,v 1.12 1997/12/13 21:53:10 kuznet Exp $ + * $Id: icmp.c,v 1.13 1998/02/12 07:43:41 davem Exp $ * * Based on net/ipv4/icmp.c * diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 15ce420ac..9fce1acca 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: ip6_fib.c,v 1.10 1997/12/13 21:53:10 kuznet Exp $ + * $Id: ip6_fib.c,v 1.11 1998/03/08 05:56:50 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/net/ipv6/ip6_fw.c b/net/ipv6/ip6_fw.c index 7316a30f1..3c3a0cfc5 100644 --- a/net/ipv6/ip6_fw.c +++ b/net/ipv6/ip6_fw.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: ip6_fw.c,v 1.8 1997/12/13 21:53:11 kuznet Exp $ + * $Id: ip6_fw.c,v 1.9 1998/02/12 07:43:42 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c index ead32047a..71ad7e1a0 100644 --- a/net/ipv6/ip6_input.c +++ b/net/ipv6/ip6_input.c @@ -6,7 +6,7 @@ * Pedro Roque <roque@di.fc.ul.pt> * Ian P. Morris <I.P.Morris@soton.ac.uk> * - * $Id: ip6_input.c,v 1.7 1997/09/20 20:48:27 davem Exp $ + * $Id: ip6_input.c,v 1.8 1998/02/12 07:43:43 davem Exp $ * * Based in linux/net/ipv4/ip_input.c * diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 67b81d041..13029e175 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: ip6_output.c,v 1.7 1997/12/29 19:52:46 kuznet Exp $ + * $Id: ip6_output.c,v 1.9 1998/03/08 05:56:50 davem Exp $ * * Based on linux/net/ipv4/ip_output.c * diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index f2ef3fd76..c6714eea3 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -7,7 +7,7 @@ * * Based on linux/net/ipv4/ip_sockglue.c * - * $Id: ipv6_sockglue.c,v 1.16 1997/12/13 21:53:13 kuznet Exp $ + * $Id: ipv6_sockglue.c,v 1.17 1998/03/08 05:56:51 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 3fb0680bc..ce37117a3 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -132,7 +132,7 @@ struct neigh_table nd_tbl = pndisc_destructor, pndisc_redo, { NULL, NULL, &nd_tbl, 0, NULL, NULL, - 30*HZ, 1*HZ, 60*HZ, 30*HZ, 5*HZ, 3, 3, 0, 3, 1*HZ, (8*HZ)/10, 0, 64 }, + 30*HZ, 1*HZ, 60*HZ, 30*HZ, 5*HZ, 3, 3, 0, 3, 1*HZ, (8*HZ)/10, 64, 0 }, 30*HZ, 128, 512, 1024, }; diff --git a/net/ipv6/proc.c b/net/ipv6/proc.c index b9b811e35..b87d4696b 100644 --- a/net/ipv6/proc.c +++ b/net/ipv6/proc.c @@ -7,7 +7,7 @@ * PROC file system. This is very similar to the IPv4 version, * except it reports the sockets in the INET6 address family. * - * Version: $Id: proc.c,v 1.4 1997/04/20 22:50:44 schenk Exp $ + * Version: $Id: proc.c,v 1.6 1998/03/13 08:02:19 davem Exp $ * * Authors: David S. Miller (davem@caip.rutgers.edu) * @@ -21,6 +21,7 @@ #include <linux/net.h> #include <linux/in6.h> #include <net/sock.h> +#include <net/tcp.h> #include <net/transp_v6.h> /* This is the main implementation workhorse of all these routines. */ @@ -52,21 +53,35 @@ static int get__netinfo6(struct proto *pro, char *buffer, int format, char **sta SOCKHASH_LOCK(); sp = pro->sklist_next; while(sp != (struct sock *)pro) { + struct tcp_tw_bucket *tw = (struct tcp_tw_bucket *)sp; + int tw_bucket = 0; + pos += 149; if(pos < offset) goto next; tp = &(sp->tp_pinfo.af_tcp); - dest = &sp->net_pinfo.af_inet6.daddr; - src = &sp->net_pinfo.af_inet6.rcv_saddr; + if((format == 0) && (sp->state == TCP_TIME_WAIT)) { + tw_bucket = 1; + dest = &tw->v6_daddr; + src = &tw->v6_rcv_saddr; + } else { + dest = &sp->net_pinfo.af_inet6.daddr; + src = &sp->net_pinfo.af_inet6.rcv_saddr; + } destp = ntohs(sp->dummy_th.dest); srcp = ntohs(sp->dummy_th.source); - - timer_active1 = del_timer(&tp->retransmit_timer); - timer_active2 = del_timer(&sp->timer); - if(!timer_active1) tp->retransmit_timer.expires = 0; - if(!timer_active2) sp->timer.expires = 0; - timer_active = 0; - timer_expires = (unsigned) -1; + if((format == 0) && (sp->state == TCP_TIME_WAIT)) { + timer_active1 = timer_active2 = 0; + timer_active = 3; + timer_expires = tw->timer.expires; + } else { + timer_active1 = del_timer(&tp->retransmit_timer); + timer_active2 = del_timer(&sp->timer); + if(!timer_active1) tp->retransmit_timer.expires = 0; + if(!timer_active2) sp->timer.expires = 0; + timer_active = 0; + timer_expires = (unsigned) -1; + } if(timer_active1 && tp->retransmit_timer.expires < timer_expires) { timer_active = timer_active1; timer_expires = tp->retransmit_timer.expires; @@ -75,6 +90,8 @@ static int get__netinfo6(struct proto *pro, char *buffer, int format, char **sta timer_active = timer_active2; timer_expires = sp->timer.expires; } + if(timer_active == 0) + timer_expires = jiffies; sprintf(tmpbuf, "%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X " "%02X %08X:%08X %02X:%08lX %08X %5d %8d %ld", i, @@ -83,13 +100,23 @@ static int get__netinfo6(struct proto *pro, char *buffer, int format, char **sta dest->s6_addr32[0], dest->s6_addr32[1], dest->s6_addr32[2], dest->s6_addr32[3], destp, sp->state, - format==0?sp->write_seq-tp->snd_una:atomic_read(&sp->wmem_alloc), - format==0?tp->rcv_nxt-sp->copied_seq:atomic_read(&sp->rmem_alloc), + (tw_bucket ? + 0 : + (format == 0) ? + tp->write_seq-tp->snd_una : + atomic_read(&sp->wmem_alloc)), + (tw_bucket ? + 0 : + (format == 0) ? + tp->rcv_nxt-tp->copied_seq : + atomic_read(&sp->rmem_alloc)), timer_active, timer_expires-jiffies, - tp->retransmits, - sp->socket ? sp->socket->inode->i_uid:0, - timer_active?sp->timeout:0, - sp->socket ? sp->socket->inode->i_ino:0); + (tw_bucket ? 0 : tp->retransmits), + ((!tw_bucket && sp->socket) ? + sp->socket->inode->i_uid : 0), + (!tw_bucket && timer_active) ? sp->timeout : 0, + ((!tw_bucket && sp->socket) ? + sp->socket->inode->i_ino : 0)); if(timer_active1) add_timer(&tp->retransmit_timer); if(timer_active2) add_timer(&sp->timer); diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 4ee1b13ad..5b182b7ef 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -7,7 +7,7 @@ * * Adapted from linux/net/ipv4/raw.c * - * $Id: raw.c,v 1.16 1997/12/29 19:52:48 kuznet Exp $ + * $Id: raw.c,v 1.18 1998/03/08 05:56:54 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index aa027da14..55fecc676 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: reassembly.c,v 1.8 1997/12/29 19:52:50 kuznet Exp $ + * $Id: reassembly.c,v 1.9 1998/02/12 07:43:48 davem Exp $ * * Based on: net/ipv4/ip_fragment.c * diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 28ee43e78..5188de864 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: route.c,v 1.19 1997/12/13 21:53:16 kuznet Exp $ + * $Id: route.c,v 1.25 1998/03/15 03:31:47 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -85,18 +85,18 @@ struct dst_ops ip6_dst_ops = { }; struct rt6_info ip6_null_entry = { - {{NULL, ATOMIC_INIT(0), ATOMIC_INIT(0), NULL, + {{NULL, ATOMIC_INIT(1), ATOMIC_INIT(1), NULL, -1, 0, 0, 0, 0, 0, 0, 0, 0, -ENETUNREACH, NULL, NULL, ip6_pkt_discard, ip6_pkt_discard, &ip6_dst_ops}}, NULL, {{{0}}}, 256, RTF_REJECT|RTF_NONEXTHOP, ~0U, - 0, 255, {NULL}, {{{{0}}}, 128}, {{{{0}}}, 128} + 255, 0, {NULL}, {{{{0}}}, 0}, {{{{0}}}, 0} }; struct fib6_node ip6_routing_table = { NULL, NULL, NULL, NULL, &ip6_null_entry, - 0, RTN_ROOT|RTN_TL_ROOT, 0 + 0, RTN_ROOT|RTN_TL_ROOT|RTN_RTINFO, 0 }; #ifdef CONFIG_RT6_POLICY @@ -709,14 +709,14 @@ struct rt6_info *ip6_route_add(struct in6_rtmsg *rtmsg, int *err) if (rt == NULL) { RDBG(("dalloc fails, ")); *err = -ENOMEM; - goto out; + return NULL; } rt->u.dst.obsolete = -1; rt->rt6i_expires = rtmsg->rtmsg_info; addr_type = ipv6_addr_type(&rtmsg->rtmsg_dst); - + if (addr_type & IPV6_ADDR_MULTICAST) { RDBG(("MCAST, ")); rt->u.dst.input = ip6_mc_input; @@ -743,6 +743,21 @@ struct rt6_info *ip6_route_add(struct in6_rtmsg *rtmsg, int *err) rt->rt6i_src.plen = rtmsg->rtmsg_src_len; ipv6_wash_prefix(&rt->rt6i_src.addr, rt->rt6i_src.plen); + /* We cannot add true routes via loopback here, + they would result in kernel looping; promote them to reject routes + */ + if ((rtmsg->rtmsg_flags&RTF_REJECT) || + (dev && (dev->flags&IFF_LOOPBACK) && !(addr_type&IPV6_ADDR_LOOPBACK))) { + dev = dev_get("lo"); + rt->u.dst.output = ip6_pkt_discard; + rt->u.dst.input = ip6_pkt_discard; + rt->u.dst.error = -ENETUNREACH; + rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP; + rt->rt6i_metric = rtmsg->rtmsg_metric; + rt->rt6i_dev = dev; + goto install_route; + } + if (rtmsg->rtmsg_flags & RTF_GATEWAY) { struct in6_addr *gw_addr; int gwa_type; @@ -773,7 +788,7 @@ struct rt6_info *ip6_route_add(struct in6_rtmsg *rtmsg, int *err) } dev = grt->rt6i_dev; } - if (dev == NULL) { + if (dev == NULL || (dev->flags&IFF_LOOPBACK)) { *err = -EINVAL; goto out; } @@ -805,6 +820,7 @@ struct rt6_info *ip6_route_add(struct in6_rtmsg *rtmsg, int *err) rt->rt6i_hoplimit = ipv6_get_hoplimit(dev); rt->rt6i_flags = rtmsg->rtmsg_flags; +install_route: RDBG(("rt6ins(%p) ", rt)); rt6_lock(); @@ -1421,6 +1437,7 @@ int ipv6_route_ioctl(unsigned int cmd, void *arg) int ip6_pkt_discard(struct sk_buff *skb) { ipv6_statistics.Ip6OutNoRoutes++; + icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0, skb->dev); kfree_skb(skb); return 0; } @@ -1671,7 +1688,8 @@ static int inet6_rtm_to_rtmsg(struct rtmsg *r, struct rtattr **rta, rtmsg->rtmsg_dst_len = r->rtm_dst_len; rtmsg->rtmsg_src_len = r->rtm_src_len; rtmsg->rtmsg_flags = RTF_UP; - rtmsg->rtmsg_metric = IP6_RT_PRIO_USER; + if (r->rtm_type == RTN_UNREACHABLE) + rtmsg->rtmsg_flags |= RTF_REJECT; if (rta[RTA_GATEWAY-1]) { if (rta[RTA_GATEWAY-1]->rta_len != RTA_LENGTH(16)) @@ -1754,7 +1772,12 @@ static int rt6_fill_node(struct sk_buff *skb, struct rt6_info *rt, rtm->rtm_src_len = rt->rt6i_src.plen; rtm->rtm_tos = 0; rtm->rtm_table = RT_TABLE_MAIN; - rtm->rtm_type = RTN_UNICAST; + if (rt->rt6i_flags&RTF_REJECT) + rtm->rtm_type = RTN_UNREACHABLE; + else if (rt->rt6i_dev && (rt->rt6i_dev->flags&IFF_LOOPBACK)) + rtm->rtm_type = RTN_LOCAL; + else + rtm->rtm_type = RTN_UNICAST; rtm->rtm_flags = 0; rtm->rtm_scope = RT_SCOPE_UNIVERSE; #ifdef CONFIG_RTNL_OLD_IFINFO @@ -1795,6 +1818,8 @@ static int rt6_fill_node(struct sk_buff *skb, struct rt6_info *rt, if (rt->u.dst.rtt) RTA_PUT(skb, RTAX_RTT, sizeof(unsigned), &rt->u.dst.rtt); mx->rta_len = skb->tail - (u8*)mx; + if (mx->rta_len == RTA_LENGTH(0)) + skb_trim(skb, (u8*)mx - skb->data); #endif if (rt->u.dst.neighbour) RTA_PUT(skb, RTA_GATEWAY, 16, &rt->u.dst.neighbour->primary_key); diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index f029942df..577b85d0f 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -6,7 +6,7 @@ * Pedro Roque <roque@di.fc.ul.pt> * Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> * - * $Id: sit.c,v 1.24 1997/12/13 21:53:17 kuznet Exp $ + * $Id: sit.c,v 1.27 1998/03/08 05:56:57 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index f7a080a0d..1d082c195 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: tcp_ipv6.c,v 1.44 1997/12/13 21:53:18 kuznet Exp $ + * $Id: tcp_ipv6.c,v 1.60 1998/03/15 02:59:32 davem Exp $ * * Based on: * linux/net/ipv4/tcp.c @@ -44,7 +44,6 @@ #define ICMP_PARANOIA -extern int sysctl_tcp_sack; extern int sysctl_tcp_timestamps; extern int sysctl_tcp_window_scaling; @@ -86,62 +85,69 @@ static __inline__ int tcp_v6_sk_hashfn(struct sock *sk) /* Grrr, addr_type already calculated by caller, but I don't want * to add some silly "cookie" argument to this method just for that. + * But it doesn't matter, the recalculation is in the rarest path + * this function ever takes. */ static int tcp_v6_verify_bind(struct sock *sk, unsigned short snum) { - struct sock *sk2; - int addr_type = ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr); - int retval = 0, sk_reuse = sk->reuse; + struct tcp_bind_bucket *tb; + int result = 0; SOCKHASH_LOCK(); - sk2 = tcp_bound_hash[tcp_sk_bhashfn(sk)]; - for(; sk2 != NULL; sk2 = sk2->bind_next) { - if((sk2->num == snum) && (sk2 != sk)) { - unsigned char state = sk2->state; - int sk2_reuse = sk2->reuse; - if(addr_type == IPV6_ADDR_ANY || (!sk2->rcv_saddr)) { - if((!sk2_reuse) || - (!sk_reuse) || - (state == TCP_LISTEN)) { - retval = 1; - break; - } - } else if(!ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, - &sk2->net_pinfo.af_inet6.rcv_saddr)) { - if((!sk_reuse) || - (!sk2_reuse) || - (state == TCP_LISTEN)) { - retval = 1; - break; + for(tb = tcp_bound_hash[tcp_bhashfn(snum)]; + (tb && (tb->port != snum)); + tb = tb->next) + ; + if(tb && tb->owners) { + /* Fast path for reuse ports, see include/net/tcp.h for a very + * detailed description of why this works, and why it is worth + * the effort at all. -DaveM + */ + if((tb->flags & TCPB_FLAG_FASTREUSE) && + (sk->reuse != 0)) { + goto go_like_smoke; + } else { + struct sock *sk2; + int sk_reuse = sk->reuse; + int addr_type = ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr); + + /* We must walk the whole port owner list in this case. -DaveM */ + for(sk2 = tb->owners; sk2; sk2 = sk2->bind_next) { + if(!sk_reuse || !sk2->reuse || sk2->state == TCP_LISTEN) { + if(addr_type == IPV6_ADDR_ANY || + !sk2->rcv_saddr || + !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, + &sk2->net_pinfo.af_inet6.rcv_saddr)) + break; } } + if(sk2 != NULL) + result = 1; } } + if((result == 0) && + (tb == NULL) && + (tcp_bucket_create(snum) == NULL)) + result = 1; +go_like_smoke: SOCKHASH_UNLOCK(); - - return retval; + return result; } static void tcp_v6_hash(struct sock *sk) { - unsigned char state; - - SOCKHASH_LOCK(); - state = sk->state; - if(state != TCP_CLOSE) { + if(sk->state != TCP_CLOSE) { struct sock **skp; - if(state == TCP_LISTEN) - skp = &tcp_listening_hash[tcp_sk_listen_hashfn(sk)]; - else - skp = &tcp_established_hash[tcp_v6_sk_hashfn(sk)]; + SOCKHASH_LOCK(); + skp = &tcp_established_hash[(sk->hashent = tcp_v6_sk_hashfn(sk))]; if((sk->next = *skp) != NULL) (*skp)->pprev = &sk->next; *skp = sk; sk->pprev = skp; tcp_sk_bindify(sk); + SOCKHASH_UNLOCK(); } - SOCKHASH_UNLOCK(); } static void tcp_v6_unhash(struct sock *sk) @@ -153,6 +159,7 @@ static void tcp_v6_unhash(struct sock *sk) *sk->pprev = sk->next; sk->pprev = NULL; tcp_sk_unbindify(sk); + tcp_reg_zap(sk); } SOCKHASH_UNLOCK(); } @@ -163,29 +170,27 @@ static void tcp_v6_rehash(struct sock *sk) SOCKHASH_LOCK(); state = sk->state; - if(sk->pprev) { + if(sk->pprev != NULL) { if(sk->next) sk->next->pprev = sk->pprev; *sk->pprev = sk->next; sk->pprev = NULL; - tcp_sk_unbindify(sk); + tcp_reg_zap(sk); } if(state != TCP_CLOSE) { struct sock **skp; - if(state == TCP_LISTEN) { + if(state == TCP_LISTEN) skp = &tcp_listening_hash[tcp_sk_listen_hashfn(sk)]; - } else { - int hash = tcp_v6_sk_hashfn(sk); - if(state == TCP_TIME_WAIT) - hash += (TCP_HTABLE_SIZE/2); - skp = &tcp_established_hash[hash]; - } + else + skp = &tcp_established_hash[(sk->hashent = tcp_v6_sk_hashfn(sk))]; + if((sk->next = *skp) != NULL) (*skp)->pprev = &sk->next; *skp = sk; sk->pprev = skp; - tcp_sk_bindify(sk); + if(state == TCP_LISTEN) + tcp_sk_bindify(sk); } SOCKHASH_UNLOCK(); } @@ -209,8 +214,12 @@ static struct sock *tcp_v6_lookup_listener(struct in6_addr *daddr, unsigned shor return result; } +/* Until this is verified... -DaveM */ +/* #define USE_QUICKSYNS */ + /* Sockets in TCP_CLOSE state are _always_ taken out of the hash, so * we need not check it for TCP lookups anymore, thanks Alexey. -DaveM + * It is assumed that this code only gets called from within NET_BH. */ static inline struct sock *__tcp_v6_lookup(struct tcphdr *th, struct in6_addr *saddr, u16 sport, @@ -218,30 +227,53 @@ static inline struct sock *__tcp_v6_lookup(struct tcphdr *th, { unsigned short hnum = ntohs(dport); struct sock *sk; - int hash = tcp_v6_hashfn(daddr, hnum, saddr, sport); + int hash; + +#ifdef USE_QUICKSYNS + /* Incomming connection short-cut. */ + if (th && th->syn == 1 && th->ack == 0) + goto listener_shortcut; +#endif + + /* Check TCP register quick cache first. */ + sk = TCP_RHASH(sport); + if(sk && + sk->num == hnum && /* local port */ + sk->family == AF_INET6 && /* address family */ + sk->dummy_th.dest == sport && /* remote port */ + !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.daddr, saddr) && + !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, daddr)) + goto hit; /* Optimize here for direct hit, only listening connections can - * have wildcards anyways. It is assumed that this code only - * gets called from within NET_BH. + * have wildcards anyways. */ - for(sk = tcp_established_hash[hash]; sk; sk = sk->next) + hash = tcp_v6_hashfn(daddr, hnum, saddr, sport); + for(sk = tcp_established_hash[hash]; sk; sk = sk->next) { /* For IPV6 do the cheaper port and family tests first. */ if(sk->num == hnum && /* local port */ sk->family == AF_INET6 && /* address family */ sk->dummy_th.dest == sport && /* remote port */ !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.daddr, saddr) && - !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, daddr)) + !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, daddr)) { + if (sk->state == TCP_ESTABLISHED) + TCP_RHASH(sport) = sk; goto hit; /* You sunk my battleship! */ - + } + } /* Must check for a TIME_WAIT'er before going to listener hash. */ for(sk = tcp_established_hash[hash+(TCP_HTABLE_SIZE/2)]; sk; sk = sk->next) if(sk->num == hnum && /* local port */ sk->family == AF_INET6 && /* address family */ - sk->dummy_th.dest == sport && /* remote port */ - !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.daddr, saddr) && - !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, daddr)) - goto hit; - + sk->dummy_th.dest == sport) { /* remote port */ + struct tcp_tw_bucket *tw = (struct tcp_tw_bucket *)sk; + if(!ipv6_addr_cmp(&tw->v6_daddr, saddr) && + !ipv6_addr_cmp(&tw->v6_rcv_saddr, daddr)) + goto hit; + } +#ifdef USE_QUICKSYNS +listener_shortcut: +#endif sk = tcp_v6_lookup_listener(daddr, hnum); hit: return sk; @@ -275,6 +307,33 @@ static __u32 tcp_v6_init_sequence(struct sock *sk, struct sk_buff *skb) skb->h.th->source); } +static int tcp_v6_unique_address(struct sock *sk) +{ + struct tcp_bind_bucket *tb; + unsigned short snum = sk->num; + int retval = 1; + + /* Freeze the hash while we snoop around. */ + SOCKHASH_LOCK(); + tb = tcp_bound_hash[tcp_bhashfn(snum)]; + for(; tb; tb = tb->next) { + if(tb->port == snum && tb->owners != NULL) { + /* Almost certainly the re-use port case, search the real hashes + * so it actually scales. (we hope that all ipv6 ftp servers will + * use passive ftp, I just cover this case for completeness) + */ + sk = __tcp_v6_lookup(NULL, &sk->net_pinfo.af_inet6.daddr, + sk->dummy_th.dest, + &sk->net_pinfo.af_inet6.rcv_saddr, snum); + if((sk != NULL) && (sk->state != TCP_LISTEN)) + retval = 0; + break; + } + } + SOCKHASH_UNLOCK(); + return retval; +} + static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) { @@ -390,7 +449,9 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, ipv6_addr_copy(&np->saddr, saddr); } - /* FIXME: Need to do tcp_v6_unique_address() here! -DaveM */ + sk->dummy_th.dest = usin->sin6_port; + if (!tcp_v6_unique_address(sk)) + return -EADDRNOTAVAIL; /* * Init variables @@ -398,16 +459,15 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, lock_sock(sk); - sk->dummy_th.dest = usin->sin6_port; - sk->write_seq = secure_tcp_sequence_number(np->saddr.s6_addr32[3], + tp->write_seq = secure_tcp_sequence_number(np->saddr.s6_addr32[3], np->daddr.s6_addr32[3], sk->dummy_th.source, sk->dummy_th.dest); tp->snd_wnd = 0; tp->snd_wl1 = 0; - tp->snd_wl2 = sk->write_seq; - tp->snd_una = sk->write_seq; + tp->snd_wl2 = tp->write_seq; + tp->snd_una = tp->write_seq; tp->rcv_nxt = 0; @@ -415,30 +475,35 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, release_sock(sk); - buff = sock_wmalloc(sk, MAX_SYN_SIZE, 0, GFP_KERNEL); - - if (buff == NULL) + buff = sock_wmalloc(sk, (MAX_SYN_SIZE + sizeof(struct sk_buff)), + 0, GFP_KERNEL); + if (buff == NULL) { + /* FIXME: Free route references etc??? */ return(-ENOMEM); + } lock_sock(sk); tcp_v6_build_header(sk, buff); + tp->tcp_header_len = sizeof(struct tcphdr) + + (sysctl_tcp_timestamps ? TCPOLEN_TSTAMP_ALIGNED : 0); + /* build the tcp header */ th = (struct tcphdr *) skb_put(buff,sizeof(struct tcphdr)); buff->h.th = th; memcpy(th, (void *) &(sk->dummy_th), sizeof(*th)); - buff->seq = sk->write_seq++; + buff->seq = tp->write_seq++; th->seq = htonl(buff->seq); - tp->snd_nxt = sk->write_seq; - buff->end_seq = sk->write_seq; + tp->snd_nxt = tp->write_seq; + buff->end_seq = tp->write_seq; th->ack = 0; th->syn = 1; sk->mtu = dst->pmtu; - sk->mss = sk->mtu - sizeof(struct ipv6hdr) - sizeof(struct tcphdr); + sk->mss = (sk->mtu - sizeof(struct ipv6hdr) - tp->tcp_header_len); if (sk->mss < 1) { printk(KERN_DEBUG "intial ipv6 sk->mss below 1\n"); @@ -457,8 +522,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, * Put in the TCP options to say MTU. */ - tmp = tcp_syn_build_options(buff, sk->mss, sysctl_tcp_sack, - sysctl_tcp_timestamps, + tmp = tcp_syn_build_options(buff, sk->mss, sysctl_tcp_timestamps, sysctl_tcp_window_scaling,tp->rcv_wscale); th->doff = sizeof(*th)/4 + (tmp>>2); buff->csum = 0; @@ -467,9 +531,10 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, tcp_set_state(sk, TCP_SYN_SENT); /* Socket identity change complete, no longer - * in TCP_CLOSE, so rehash. + * in TCP_CLOSE, so enter ourselves into the + * hash tables. */ - sk->prot->rehash(sk); + sk->prot->hash(sk); /* FIXME: should use dcache->rtt if availiable */ tp->rto = TCP_TIMEOUT_INIT; @@ -482,12 +547,12 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, tp->packets_out++; buff->when = jiffies; skb1 = skb_clone(buff, GFP_KERNEL); - skb_set_owner_w(skb1, sk); - - tcp_v6_xmit(skb1); + if(skb1 != NULL) { + skb_set_owner_w(skb1, sk); + tcp_v6_xmit(skb1); + } /* Timer for repeating the SYN until an answer */ - tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto); tcp_statistics.TcpActiveOpens++; tcp_statistics.TcpOutSegs++; @@ -499,6 +564,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, static int tcp_v6_sendmsg(struct sock *sk, struct msghdr *msg, int len) { + struct tcp_opt *tp; struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; int retval = -EINVAL; @@ -530,7 +596,10 @@ static int tcp_v6_sendmsg(struct sock *sk, struct msghdr *msg, int len) lock_sock(sk); retval = tcp_do_sendmsg(sk, msg->msg_iovlen, msg->msg_iov, msg->msg_flags); - + /* Push out partial tail frames if needed. */ + tp = &(sk->tp_pinfo.af_tcp); + if(tp->send_head && tcp_snd_test(sk, tp->send_head)) + tcp_write_xmit(sk); release_sock(sk); out: @@ -555,7 +624,7 @@ void tcp_v6_err(int type, int code, unsigned char *header, __u32 info, sk = tcp_v6_lookup(daddr, th->dest, saddr, th->source); - if (sk == NULL) { + if (sk == NULL || sk->state == TCP_TIME_WAIT) { /* XXX: Update ICMP error count */ return; } @@ -596,11 +665,14 @@ void tcp_v6_err(int type, int code, unsigned char *header, __u32 info, ip6_dst_store(sk, dst); } - if (sk->dst_cache->error) + if (sk->dst_cache->error) { sk->err_soft = sk->dst_cache->error; - else + } else { + /* FIXME: Reset sk->mss, taking into account TCP option + * bytes for timestamps. -DaveM + */ sk->mtu = sk->dst_cache->pmtu; - + } if (sk->sock_readers) { /* remove later */ printk(KERN_DEBUG "tcp_v6_err: pmtu disc: socket locked.\n"); return; @@ -713,11 +785,10 @@ static void tcp_v6_send_synack(struct sock *sk, struct open_request *req) * match what happens under IPV4. Figure out the right thing to do. */ req->mss = min(sk->mss, req->mss); - - if (req->mss < 1) { - printk(KERN_DEBUG "initial req->mss below 1\n"); - req->mss = 1; - } + if(sk->user_mss) + req->mss = min(req->mss, sk->user_mss); + if(req->tstamp_ok == 0) + req->mss += TCPOLEN_TSTAMP_ALIGNED; if (req->rcv_wnd == 0) { __u8 rcv_wscale; @@ -732,7 +803,7 @@ static void tcp_v6_send_synack(struct sock *sk, struct open_request *req) } th->window = htons(req->rcv_wnd); - tmp = tcp_syn_build_options(skb, req->mss, req->sack_ok, req->tstamp_ok, + tmp = tcp_syn_build_options(skb, req->mss, req->tstamp_ok, req->wscale_ok,req->rcv_wscale); skb->csum = 0; th->doff = (sizeof(*th) + tmp)>>2; @@ -740,9 +811,13 @@ static void tcp_v6_send_synack(struct sock *sk, struct open_request *req) &req->af.v6_req.loc_addr, &req->af.v6_req.rmt_addr, csum_partial((char *)th, sizeof(*th)+tmp, skb->csum)); + /* Actually we should not attach dst to socket in state LISTEN, + it results in stale destination per listen socket and + overflow of routing cache. + (IPv4 has the same flaw with more unpleasant consequences.) + */ ip6_dst_store(sk, dst); ip6_xmit(sk, skb, &fl, req->af.v6_req.opt); - dst_release(dst); tcp_statistics.TcpOutSegs++; } @@ -801,14 +876,15 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr, req->rcv_isn = skb->seq; req->snt_isn = isn; - tp.tstamp_ok = tp.sack_ok = tp.wscale_ok = tp.snd_wscale = 0; + tp.tstamp_ok = tp.wscale_ok = tp.snd_wscale = 0; tp.in_mss = 536; tcp_parse_options(skb->h.th,&tp,0); - if (tp.saw_tstamp) - req->ts_recent = tp.rcv_tsval; req->mss = tp.in_mss; + if (tp.saw_tstamp) { + req->mss -= TCPOLEN_TSTAMP_ALIGNED; + req->ts_recent = tp.rcv_tsval; + } req->tstamp_ok = tp.tstamp_ok; - req->sack_ok = tp.sack_ok; req->snd_wscale = tp.snd_wscale; req->wscale_ok = tp.wscale_ok; req->rmt_port = skb->h.th->source; @@ -879,92 +955,17 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, return newsk; } - newsk = sk_alloc(AF_INET6, GFP_ATOMIC); + newsk = tcp_create_openreq_child(sk, req, skb); if (newsk == NULL) { - if (dst) - dst_release(dst); + dst_release(dst); return NULL; } - memcpy(newsk, sk, sizeof(*newsk)); - - /* Or else we die! -DaveM */ - newsk->sklist_next = NULL; - - newsk->opt = NULL; newsk->dst_cache = NULL; - skb_queue_head_init(&newsk->write_queue); - skb_queue_head_init(&newsk->receive_queue); - skb_queue_head_init(&newsk->out_of_order_queue); - skb_queue_head_init(&newsk->error_queue); - - /* - * Unused - */ newtp = &(newsk->tp_pinfo.af_tcp); - np = &newsk->net_pinfo.af_inet6; - - newtp->send_head = NULL; - newtp->retrans_head = NULL; - - newtp->pending = 0; - - skb_queue_head_init(&newsk->back_log); - - newsk->prot->init(newsk); - - newtp->snd_cwnd_cnt = 0; -#if 0 /* Don't mess up the initialization we did in the init routine! */ - newtp->snd_ssthresh = 0; -#endif - newtp->backoff = 0; - newsk->proc = 0; - newsk->done = 0; - newsk->pair = NULL; - atomic_set(&newsk->wmem_alloc, 0); - atomic_set(&newsk->rmem_alloc, 0); - newsk->localroute = sk->localroute; - - newsk->err = 0; - newsk->shutdown = 0; - newsk->ack_backlog = 0; - - newtp->fin_seq = req->rcv_isn; - newsk->syn_seq = req->rcv_isn; - newsk->state = TCP_SYN_RECV; - newsk->timeout = 0; - - newsk->write_seq = req->snt_isn; - - newtp->snd_wnd = ntohs(skb->h.th->window); - newtp->max_window = newtp->snd_wnd; - newtp->snd_wl1 = req->rcv_isn; - newtp->snd_wl2 = newsk->write_seq; - newtp->snd_una = newsk->write_seq++; - newtp->snd_nxt = newsk->write_seq; - - newsk->urg_data = 0; - newtp->packets_out = 0; - newtp->retransmits = 0; - newsk->linger=0; - newsk->destroy = 0; - init_timer(&newsk->timer); - newsk->timer.data = (unsigned long) newsk; - newsk->timer.function = &net_timer; - - tcp_init_xmit_timers(newsk); - - newsk->dummy_th.source = sk->dummy_th.source; - newsk->dummy_th.dest = req->rmt_port; - newsk->sock_readers=0; - - newtp->rcv_nxt = req->rcv_isn + 1; - newtp->rcv_wup = req->rcv_isn + 1; - newsk->copied_seq = req->rcv_isn + 1; - - newsk->socket = NULL; + np = &newsk->net_pinfo.af_inet6; ipv6_addr_copy(&np->daddr, &req->af.v6_req.rmt_addr); ipv6_addr_copy(&np->saddr, &req->af.v6_req.loc_addr); ipv6_addr_copy(&np->rcv_saddr, &req->af.v6_req.loc_addr); @@ -987,14 +988,22 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, ip6_dst_store(newsk, dst); - newtp->sack_ok = req->sack_ok; newtp->tstamp_ok = req->tstamp_ok; - newtp->snd_wscale = req->snd_wscale; + newtp->window_clamp = req->window_clamp; + newtp->rcv_wnd = req->rcv_wnd; newtp->wscale_ok = req->wscale_ok; - newtp->ts_recent = req->ts_recent; + if (newtp->wscale_ok) { + newtp->snd_wscale = req->snd_wscale; + newtp->rcv_wscale = req->rcv_wscale; + } else { + newtp->snd_wscale = newtp->rcv_wscale = 0; + newtp->window_clamp = min(newtp->window_clamp,65535); + } if (newtp->tstamp_ok) { - newtp->tcp_header_len = sizeof(struct tcphdr) + 12; /* FIXME: define the contant. */ - newsk->dummy_th.doff += 3; + newtp->ts_recent = req->ts_recent; + newtp->ts_recent_stamp = jiffies; + newtp->tcp_header_len = sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED; + newsk->dummy_th.doff += (TCPOLEN_TSTAMP_ALIGNED >> 2); } else { newtp->tcp_header_len = sizeof(struct tcphdr); } @@ -1006,7 +1015,6 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, newsk->mss = min(req->mss+sizeof(struct tcphdr)-newtp->tcp_header_len, (newsk->mtu - sizeof(struct ipv6hdr) - newtp->tcp_header_len)); - /* XXX tp->window_clamp??? -DaveM */ newsk->daddr = LOOPBACK4_IPV6; newsk->saddr = LOOPBACK4_IPV6; @@ -1181,12 +1189,14 @@ int tcp_v6_rcv(struct sk_buff *skb, struct device *dev, goto no_tcp_socket; } - skb->sk = sk; skb->seq = ntohl(th->seq); skb->end_seq = skb->seq + th->syn + th->fin + len - th->doff*4; skb->ack_seq = ntohl(th->ack_seq); - skb->used = 0; + if(sk->state == TCP_TIME_WAIT) + goto do_time_wait; + + skb->sk = sk; } /* @@ -1249,6 +1259,12 @@ discard_it: kfree_skb(skb); return 0; + +do_time_wait: + if(tcp_timewait_state_process((struct tcp_tw_bucket *)sk, + skb, th, &(IPCB(skb)->opt), skb->len)) + goto no_tcp_socket; + goto discard_it; } static int tcp_v6_rebuild_header(struct sock *sk, struct sk_buff *skb) @@ -1384,51 +1400,34 @@ static struct tcp_func ipv6_mapped = { sizeof(struct sockaddr_in6) }; +/* NOTE: A lot of things set to zero explicitly by call to + * sk_alloc() so need not be done here. + */ static int tcp_v6_init_sock(struct sock *sk) { struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); - skb_queue_head_init(&sk->out_of_order_queue); + skb_queue_head_init(&tp->out_of_order_queue); tcp_init_xmit_timers(sk); - tp->srtt = 0; tp->rto = TCP_TIMEOUT_INIT; /*TCP_WRITE_TIME*/ tp->mdev = TCP_TIMEOUT_INIT; - - tp->ato = 0; - tp->iat = (HZ/5) << 3; - - /* FIXME: right thing? */ - tp->rcv_wnd = 0; tp->in_mss = 536; - /* tp->rcv_wnd = 8192; */ - tp->tstamp_ok = 0; - tp->sack_ok = 0; - tp->wscale_ok = 0; - tp->snd_wscale = 0; - tp->sacks = 0; - tp->saw_tstamp = 0; - tp->syn_backlog = 0; - - /* start with only sending one packet at a time. */ + + /* See draft-stevens-tcpca-spec-01 for discussion of the + * initialization of these values. + */ tp->snd_cwnd = 1; tp->snd_ssthresh = 0x7fffffff; - - sk->priority = 1; sk->state = TCP_CLOSE; - sk->max_ack_backlog = SOMAXCONN; - sk->mtu = 576; sk->mss = 536; - sk->dummy_th.doff = sizeof(sk->dummy_th)/4; - /* - * Speed up by setting some standard state for the dummy_th. - */ + /* Speed up by setting some standard state for the dummy_th. */ sk->dummy_th.ack=1; sk->dummy_th.doff=sizeof(struct tcphdr)>>2; @@ -1442,6 +1441,7 @@ static int tcp_v6_init_sock(struct sock *sk) static int tcp_v6_destroy_sock(struct sock *sk) { + struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); struct sk_buff *skb; tcp_clear_xmit_timers(sk); @@ -1460,15 +1460,22 @@ static int tcp_v6_destroy_sock(struct sock *sk) * Cleans up our, hopefuly empty, out_of_order_queue */ - while((skb = skb_dequeue(&sk->out_of_order_queue)) != NULL) + while((skb = skb_dequeue(&tp->out_of_order_queue)) != NULL) kfree_skb(skb); /* * Release destination entry */ - dst_release(sk->dst_cache); - sk->dst_cache = NULL; + dst_release(xchg(&sk->dst_cache,NULL)); + + /* Clean up a locked TCP bind bucket, this only happens if a + * port is allocated for a socket, but it never fully connects. + * In which case we will find num to be non-zero and daddr to + * be zero. + */ + if(ipv6_addr_any(&(sk->net_pinfo.af_inet6.daddr)) && sk->num != 0) + tcp_bucket_unlock(sk); return 0; } diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index b99dc19e3..40e9b0233 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -7,7 +7,7 @@ * * Based on linux/ipv4/udp.c * - * $Id: udp.c,v 1.21 1997/12/29 19:52:52 kuznet Exp $ + * $Id: udp.c,v 1.24 1998/03/12 03:20:21 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -448,32 +448,43 @@ static struct sock *udp_v6_mcast_next(struct sock *sk, return NULL; } +/* + * Note: called only from the BH handler context, + * so we don't need to lock the hashes. + */ static void udpv6_mcast_deliver(struct udphdr *uh, struct in6_addr *saddr, struct in6_addr *daddr, struct sk_buff *skb) { struct sock *sk, *sk2; + struct sk_buff *buff; - SOCKHASH_LOCK(); sk = udp_hash[ntohs(uh->dest) & (UDP_HTABLE_SIZE - 1)]; sk = udp_v6_mcast_next(sk, uh->dest, daddr, uh->source, saddr); - if(sk) { - sk2 = sk; - while((sk2 = udp_v6_mcast_next(sk2->next, - uh->dest, saddr, - uh->source, daddr))) { - struct sk_buff *buff = skb_clone(skb, GFP_ATOMIC); - if (buff && sock_queue_rcv_skb(sk2, buff) < 0) { - buff->sk = NULL; - kfree_skb(buff); - } + if (!sk) + goto free_skb; + + buff = NULL; + sk2 = sk; + while((sk2 = udp_v6_mcast_next(sk2->next, uh->dest, saddr, + uh->source, daddr))) { + if (!buff) { + buff = skb_clone(skb, GFP_ATOMIC); + if (!buff) + continue; } + if (sock_queue_rcv_skb(sk2, buff) >= 0) + buff = NULL; + } + if (buff) { + buff->sk = NULL; + kfree_skb(buff); } - if(!sk || sock_queue_rcv_skb(sk, skb) < 0) { + if (sock_queue_rcv_skb(sk, skb) < 0) { + free_skb: skb->sk = NULL; kfree_skb(skb); } - SOCKHASH_UNLOCK(); } int udpv6_rcv(struct sk_buff *skb, struct device *dev, diff --git a/net/ipx/af_ipx.c b/net/ipx/af_ipx.c index cf56df492..904fa1174 100644 --- a/net/ipx/af_ipx.c +++ b/net/ipx/af_ipx.c @@ -1713,7 +1713,7 @@ static int ipx_getsockopt(struct socket *sock, int level, int optname, static int ipx_create(struct socket *sock, int protocol) { struct sock *sk; - sk=sk_alloc(AF_IPX, GFP_KERNEL); + sk=sk_alloc(AF_IPX, GFP_KERNEL, 1); if(sk==NULL) return(-ENOMEM); switch(sock->type) diff --git a/net/netbeui/af_netbeui.c b/net/netbeui/af_netbeui.c index 85bd8f4d1..6769edde5 100644 --- a/net/netbeui/af_netbeui.c +++ b/net/netbeui/af_netbeui.c @@ -150,7 +150,7 @@ static int netbeui_listen(struct socket *sock, int backlog) static int netbeui_create(struct socket *sock, int protocol) { netbeui_socket *sk; - sk=(netbeui_socket *)sk_alloc(GFP_KERNEL); + sk=(netbeui_socket *)sk_alloc(GFP_KERNEL, 1); if(sk==NULL) return(-ENOBUFS); switch(sock->type) diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 3f02f4c3c..8b8e5a4b8 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -147,7 +147,7 @@ static int netlink_create(struct socket *sock, int protocol) sock->ops = &netlink_ops; - sk = sk_alloc(AF_NETLINK, GFP_KERNEL); + sk = sk_alloc(AF_NETLINK, GFP_KERNEL, 1); if (!sk) return -ENOMEM; diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c index a84d1fd53..9d8a206da 100644 --- a/net/netrom/af_netrom.c +++ b/net/netrom/af_netrom.c @@ -98,7 +98,7 @@ static struct sock *nr_alloc_sock(void) struct sock *sk; nr_cb *nr; - if ((sk = sk_alloc(AF_NETROM, GFP_ATOMIC)) == NULL) + if ((sk = sk_alloc(AF_NETROM, GFP_ATOMIC, 1)) == NULL) return NULL; if ((nr = kmalloc(sizeof(*nr), GFP_ATOMIC)) == NULL) { @@ -759,6 +759,8 @@ static int nr_accept(struct socket *sock, struct socket *newsock, int flags) newsk = skb->sk; newsk->pair = NULL; + newsk->socket = newsock; + newsk->sleep = &newsock->wait; sti(); /* Now attach up the new socket */ diff --git a/net/netsyms.c b/net/netsyms.c index b7809863b..ad51e9a3e 100644 --- a/net/netsyms.c +++ b/net/netsyms.c @@ -244,7 +244,6 @@ EXPORT_SYMBOL(csum_partial_copy_fromiovecend); EXPORT_SYMBOL(__release_sock); EXPORT_SYMBOL(net_timer); /* UDP/TCP exported functions for TCPv6 */ -EXPORT_SYMBOL(sysctl_tcp_sack); EXPORT_SYMBOL(sysctl_tcp_timestamps); EXPORT_SYMBOL(sysctl_tcp_window_scaling); EXPORT_SYMBOL(sock_rspace); @@ -272,11 +271,15 @@ EXPORT_SYMBOL(tcp_slt_array); EXPORT_SYMBOL(__tcp_inc_slow_timer); EXPORT_SYMBOL(tcp_statistics); EXPORT_SYMBOL(tcp_rcv_state_process); +EXPORT_SYMBOL(tcp_timewait_state_process); EXPORT_SYMBOL(tcp_do_sendmsg); EXPORT_SYMBOL(tcp_v4_build_header); EXPORT_SYMBOL(tcp_v4_rebuild_header); EXPORT_SYMBOL(tcp_v4_send_check); EXPORT_SYMBOL(tcp_v4_conn_request); +EXPORT_SYMBOL(tcp_create_openreq_child); +EXPORT_SYMBOL(tcp_bucket_create); +EXPORT_SYMBOL(tcp_bucket_unlock); EXPORT_SYMBOL(tcp_v4_syn_recv_sock); EXPORT_SYMBOL(tcp_v4_do_rcv); EXPORT_SYMBOL(tcp_v4_connect); @@ -290,6 +293,11 @@ EXPORT_SYMBOL(ipv4_specific); EXPORT_SYMBOL(tcp_simple_retransmit); EXPORT_SYMBOL(xrlim_allow); + +EXPORT_SYMBOL(tcp_write_xmit); +EXPORT_SYMBOL(dev_loopback_xmit); +EXPORT_SYMBOL(tcp_regs); + #endif #ifdef CONFIG_NETLINK diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index a098f59b9..74fc7af82 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -35,6 +35,7 @@ * Alan Cox : sendmsg/recvmsg support. * Alan Cox : Protocol setting support * Alexey Kuznetsov : Untied from IPv4 stack. + * Cyrus Durgin : Fixed kerneld for kmod. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -54,7 +55,7 @@ #include <linux/netdevice.h> #include <linux/if_packet.h> #include <linux/wireless.h> -#include <linux/kerneld.h> +#include <linux/kmod.h> #include <net/ip.h> #include <net/protocol.h> #include <linux/skbuff.h> @@ -710,7 +711,7 @@ static int packet_create(struct socket *sock, int protocol) sock->state = SS_UNCONNECTED; MOD_INC_USE_COUNT; - sk = sk_alloc(AF_PACKET, GFP_KERNEL); + sk = sk_alloc(AF_PACKET, GFP_KERNEL, 1); if (sk == NULL) { MOD_DEC_USE_COUNT; return -ENOBUFS; @@ -831,9 +832,10 @@ static int packet_recvmsg(struct socket *sock, struct msghdr *msg, int len, /* We can't use skb_copy_datagram here */ err = memcpy_toiovec(msg->msg_iov, skb->data, copied); - if (err) + if (err) { + err = -EFAULT; goto out_free; - + } sk->stamp=skb->stamp; if (msg->msg_name) diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c index eeb396350..a575402c7 100644 --- a/net/rose/af_rose.c +++ b/net/rose/af_rose.c @@ -149,7 +149,7 @@ static struct sock *rose_alloc_sock(void) struct sock *sk; rose_cb *rose; - if ((sk = sk_alloc(AF_ROSE, GFP_ATOMIC)) == NULL) + if ((sk = sk_alloc(AF_ROSE, GFP_ATOMIC, 1)) == NULL) return NULL; if ((rose = kmalloc(sizeof(*rose), GFP_ATOMIC)) == NULL) { @@ -847,6 +847,8 @@ static int rose_accept(struct socket *sock, struct socket *newsock, int flags) newsk = skb->sk; newsk->pair = NULL; + newsk->socket = newsock; + newsk->sleep = &newsock->wait; sti(); /* Now attach up the new socket */ diff --git a/net/socket.c b/net/socket.c index 5c9534031..dc77ef3e8 100644 --- a/net/socket.c +++ b/net/socket.c @@ -76,8 +76,8 @@ #include <linux/init.h> #include <linux/poll.h> -#if defined(CONFIG_KERNELD) && defined(CONFIG_NET) -#include <linux/kerneld.h> +#if defined(CONFIG_KMOD) && defined(CONFIG_NET) +#include <linux/kmod.h> #endif #include <asm/system.h> @@ -577,7 +577,7 @@ int sock_create(int family, int type, int protocol, struct socket **res) if(family<0||family>=NPROTO) return -EINVAL; -#if defined(CONFIG_KERNELD) && defined(CONFIG_NET) +#if defined(CONFIG_KMOD) && defined(CONFIG_NET) /* Attempt to load a protocol module if the find failed. * * 12/09/1996 Marcin: But! this makes REALLY only sense, if the user @@ -814,7 +814,7 @@ restart: newsock = socki_lookup(inode); if ((err = get_fd(inode)) < 0) - goto out_inval; + goto out_release; newsock->file = current->files->fd[err]; if (upeer_sockaddr) @@ -835,8 +835,6 @@ out: unlock_kernel(); return err; -out_inval: - err = -EINVAL; out_release: sock_release(newsock); goto out_put; diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 2fbce16fe..b04072d80 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -364,7 +364,7 @@ static int unix_create(struct socket *sock, int protocol) default: return -ESOCKTNOSUPPORT; } - sk = sk_alloc(AF_UNIX, GFP_KERNEL); + sk = sk_alloc(AF_UNIX, GFP_KERNEL, 1); if (!sk) return -ENOMEM; @@ -1265,7 +1265,9 @@ static int unix_stream_recvmsg(struct socket *sock, struct msghdr *msg, int size } chunk = min(skb->len, size); - /* N.B. This could fail with -EFAULT */ + /* N.B. This could fail with a non-zero value (which means -EFAULT + * and the non-zero value is the number of bytes not copied). + */ memcpy_toiovec(msg->msg_iov, skb->data, chunk); copied += chunk; size -= chunk; diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c index 7e3c9cae2..a85aeea5f 100644 --- a/net/x25/af_x25.c +++ b/net/x25/af_x25.c @@ -423,7 +423,7 @@ static struct sock *x25_alloc_socket(void) struct sock *sk; x25_cb *x25; - if ((sk = sk_alloc(AF_X25, GFP_ATOMIC)) == NULL) + if ((sk = sk_alloc(AF_X25, GFP_ATOMIC, 1)) == NULL) return NULL; if ((x25 = kmalloc(sizeof(*x25), GFP_ATOMIC)) == NULL) { |