diff options
253 files changed, 7584 insertions, 2692 deletions
@@ -950,7 +950,7 @@ S: London SE16 1GD S: United Kingdom N: Kai Harrekilde-Petersen -E: khp@olicom.dk +E: kai.harrekilde@get2net.dk D: Original author of the ftape-HOWTO, i82078 fdc detection code. N: Bart Hartgers @@ -1216,6 +1216,10 @@ S: Treforest, Pontypridd, S: Mid Glamorgan, CF37 1NW, S: Wales, United Kingdom +N: Ani Joshi +E: ajoshi@shell.unixbox.com +D: fbdev hacking + N: Bernhard Kaindl E: bkaindl@netway.at E: edv@bartelt.via.at @@ -2662,10 +2666,10 @@ S: USA N: Tim Waugh E: tim@cyberelk.demon.co.uk D: Co-architect of the parallel-port sharing system -S: 34 Bladon Close +S: 17 Curling Vale S: GUILDFORD S: Surrey -S: GU1 1TY +S: GU2 7PJ S: United Kingdom N: Juergen Weigert diff --git a/Documentation/Changes b/Documentation/Changes index 0b9e549fc..525932df1 100644 --- a/Documentation/Changes +++ b/Documentation/Changes @@ -41,7 +41,7 @@ Card) hardware, for example, you probably needn't concern yourself with pcmcia-cs. o Gnu C 2.7.2.3 # gcc --version -o binutils 2.9.1.0.7 # ld -v +o binutils 2.9.1.0.22 # ld -v o util-linux 2.10g # chsh -v o modutils 2.3.10 # insmod -V o e2fsprogs 1.18 # /sbin/tune2fs --version @@ -78,7 +78,7 @@ release of binutils. If you can, upgrade to the latest 2.9.5 binutils release. Older releases such as 2.8, 2.8.xx, and the FSF's 2.9.1 should be avoided if -at all possible. The later releases of 2.9.1.0.x (anything where x >= 7) +at all possible. The later releases of 2.9.1.0.x (anything where x >= 22) can and do compile the kernel properly, but there are many benefits to upgrading to 2.9.5 if you're up to it. @@ -248,8 +248,8 @@ o ftp://ftp.varesearch.com/pub/support/hjl/binutils/2.9.1/binutils-2.9.1.0.25.t 2.9.5 series ------------ -o ftp://ftp.varesearch.com/pub/support/hjl/binutils/binutils-2.9.5.0.29.tar.gz - <ftp://ftp.varesearch.com/pub/support/hjl/binutils/binutils-2.9.5.0.29.tar.bz2> +o ftp://ftp.varesearch.com/pub/support/hjl/binutils/binutils-2.9.5.0.46.tar.gz + <ftp://ftp.varesearch.com/pub/support/hjl/binutils/binutils-2.9.5.0.46.tar.bz2> System utilities **************** diff --git a/Documentation/Configure.help b/Documentation/Configure.help index 5e4f351e5..edaa621b0 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -2136,6 +2136,20 @@ CONFIG_ALPHA_SRM If unsure, say N. +Legacy kernel start address +CONFIG_ALPHA_LEGACY_START_ADDRESS + The 2.4 kernel changed the kernel start address from 0x310000 + to 0x810000 to make room for the Wildfire's larger SRM console. + + If you're using aboot 0.7 or later, the bootloader will examine + the ELF headers to determine where to transfer control. Unfortunately, + most older bootloaders -- APB, or MILO -- hardcoded the kernel + start address rather than examining the ELF headers, and the result + is a hard lockup. + + Say Y if you have a broken bootloader. Say N if you do not, or + if you wish to run on Wildfire. + Non-standard serial port support CONFIG_SERIAL_NONSTANDARD Say Y here if you have any non-standard serial boards -- boards @@ -3260,6 +3274,14 @@ CONFIG_FB_VIRTUAL If unsure, say N. +CONFIG_FB_SA1100 + This is a framebuffer device for the SA-1100 LCD Controller. + See http://www.linux-fbdev.org/ for information on framebuffer + devices. + + If you plan to use the LCD display with your SA-1100 system, say + Y here. + Advanced low level driver options CONFIG_FBCON_ADVANCED The frame buffer console uses character drawing routines that are @@ -10049,8 +10071,9 @@ CONFIG_USB_DSBR Microtek USB scanner support CONFIG_USB_MICROTEK Say Y here if you want support for the Microtek X6USB and possibly - some other scanners. The scanner will appear as a scsi device to the - rest of the system. A patched version of SANE is necessary to use the + some other scanners by that vendor. The scanner will appear as a + scsi generic device to the rest of the system. + A patched version of SANE is necessary to use the scanner. It's available at http://fachschaft.cup.uni-muenchen.de/~neukum/scanner.html This driver can be compiled as a module. @@ -15450,6 +15473,20 @@ CONFIG_ARCH_PERSONAL_SERVER If you have any questions or comments about the Compaq Personal Server, send e-mail to skiff@crl.dec.com +Include support for Assabet +CONFIG_SA1100_ASSABET + Say Y here if you are using the Intel(R) StrongARM(R) SA-1110 + Microprocessor Development Board (also known as the Assabet). + +Include support for the Compaq iPAQ 3600 +CONFIG_SA1100_BITSY + Say Y here if you intend to run this kernel on the Compaq iPAQ 3600 + handheld computer. Information about this machine and the Linux + port to this machine can be found at: + + http://www.handhelds.org/Compaq/index.html#iPAQ_H3600 + http://www.compaq.com/products/handhelds/pocketpc/ + Math emulation CONFIG_NWFPE Say Y to include the NWFPE floating point emulator in the kernel. @@ -15506,6 +15543,17 @@ CONFIG_DEBUG_INFO time and disk space needed for compilation of the kernel. If in doubt say N. +Kernel low-level debugging functions +CONFIG_DEBUG_LL + Say Y here to include definitions of printascii, printchar, printhex + in the kernel. This is helpful if you are debugging code that + executes before the console is initialized. + +Kernel low-level debugging messages via footbridge serial port +CONFIG_DEBUG_DC21285_PORT + Say Y here if you want the low-level print routines to direct their + output to the serial port in the DC21285 (Footbridge). + Split initialisation functions into discardable section CONFIG_TEXT_SECTIONS If you say Y here, kernel code that is only used during diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index 7256f5aaf..7d1f5ca72 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -176,7 +176,7 @@ filesystem. As of kernel 2.1.99, the following members are defined: struct super_operations { void (*read_inode) (struct inode *); - void (*write_inode) (struct inode *); + void (*write_inode) (struct inode *, int); void (*put_inode) (struct inode *); void (*delete_inode) (struct inode *); int (*notify_change) (struct dentry *, struct iattr *); @@ -198,7 +198,8 @@ or bottom half). read. Other members are filled in by this method write_inode: this method is called when the VFS needs to write an - inode to disc + inode to disc. The second parameter indicates whether the write + should be synchronous or not, not all filesystems check this flag. put_inode: called when the VFS inode is removed from the inode cache. This method is optional diff --git a/Documentation/networking/tulip.txt b/Documentation/networking/tulip.txt index b936846af..11494e1dc 100644 --- a/Documentation/networking/tulip.txt +++ b/Documentation/networking/tulip.txt @@ -142,6 +142,10 @@ tulip_core.c - Driver core (a.k.a. where "everything else" goes) Version history =============== +0.9.7 (June 17, 2000): +* Timer cleanups (Andrew Morton) +* Alpha compile fix (somebody?) + 0.9.6 (May 31, 2000): * Revert 21143-related support flag patch * Add HPPA/media-table debugging printk diff --git a/Documentation/usb/ov511.txt b/Documentation/usb/ov511.txt index a92453129..e5634fd7f 100644 --- a/Documentation/usb/ov511.txt +++ b/Documentation/usb/ov511.txt @@ -6,7 +6,7 @@ Author: Mark McClelland Homepage: http://alpha.dyndns.org/ov511 NEW IN THIS VERSION: - o Race conditions and other bugs fixed + o Preliminary support for YUV422 and YUV422P V4L modes INTRODUCTION: @@ -209,7 +209,7 @@ TODO: o Setting of contrast and brightness not working with 7620 o Driver/camera state save/restore for when USB supports suspend/resume o Multiple cameras reportedly do not work simultaneously - o Problems with OHCI + o Unstable on SMP systems HOW TO CONTACT ME: diff --git a/MAINTAINERS b/MAINTAINERS index 6ce7ccf42..3f1cec084 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -784,8 +784,8 @@ L: linux-kernel@vger.rutgers.edu S: Odd Fixes NVIDIA (RIVA) FRAMEBUFFER DRIVER -P: Jeff Garzik -M: jgarzik@mandrakesoft.com +P: Ani Joshi +M: ajoshi@shell.unixbox.com L: linux-nvidia@lists.surfsouth.com S: Maintained @@ -1135,7 +1135,8 @@ S: Maintained USB SUBSYSTEM P: Randy Dunlap M: randy.dunlap@intel.com -L: linux-usb@suse.com +L: linux-usb-users@lists.sourceforge.net +L: linux-usb-devel@lists.sourceforge.net W: http://www.linux-usb.org S: Supported @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 4 SUBLEVEL = 0 -EXTRAVERSION = -test2 +EXTRAVERSION = -test3 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) @@ -82,7 +82,7 @@ endif CPPFLAGS := -D__KERNEL__ -I$(HPATH) -CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer +CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -Werror AFLAGS := -D__ASSEMBLY__ $(CPPFLAGS) # @@ -329,6 +329,7 @@ modules_install: if [ -f SK98LIN_MODULES ]; then inst_mod SK98LIN_MODULES net; fi; \ if [ -f SKFP_MODULES ]; then inst_mod SKFP_MODULES net; fi; \ if [ -f USB_MODULES ]; then inst_mod USB_MODULES usb; fi; \ + if [ -f USB_STORAGE_MODULES ]; then inst_mod USB_STORAGE_MODULES usb; fi; \ if [ -f USB_SERIAL_MODULES ]; then inst_mod USB_SERIAL_MODULES usb; fi; \ if [ -f IEEE1394_MODULES ]; then inst_mod IEEE1394_MODULES ieee1394; fi; \ if [ -f PCMCIA_MODULES ]; then inst_mod PCMCIA_MODULES pcmcia; fi; \ diff --git a/arch/alpha/Makefile b/arch/alpha/Makefile index 36f4d4e6d..3daa2c73c 100644 --- a/arch/alpha/Makefile +++ b/arch/alpha/Makefile @@ -10,7 +10,7 @@ NM := nm -B -LINKFLAGS = -static -T arch/alpha/vmlinux.lds #-N -relax +LINKFLAGS = -static -T arch/alpha/vmlinux.lds -N #-relax CFLAGS := $(CFLAGS) -pipe -mno-fp-regs -ffixed-8 # Determine if we can use the BWX instructions with GAS. @@ -90,7 +90,7 @@ endif LIBS := $(TOPDIR)/arch/alpha/lib/lib.a $(LIBS) $(TOPDIR)/arch/alpha/lib/lib.a -MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot +MAKEBOOT = $(MAKE) -C arch/alpha/boot rawboot: @$(MAKEBOOT) rawboot @@ -109,13 +109,15 @@ srmboot: @$(MAKEBOOT) srmboot archclean: - @$(MAKE) -C arch/$(ARCH)/kernel clean + @$(MAKE) -C arch/alpha/kernel clean @$(MAKEBOOT) clean + rm -f arch/alpha/vmlinux.lds archmrproper: archdep: @$(MAKEBOOT) dep + $(CPP) $(CPPFLAGS) -xc -P arch/alpha/vmlinux.lds.in -o arch/alpha/vmlinux.lds bootpfile: @$(MAKEBOOT) bootpfile diff --git a/arch/alpha/config.in b/arch/alpha/config.in index 5f344e0f5..7441083b6 100644 --- a/arch/alpha/config.in +++ b/arch/alpha/config.in @@ -331,7 +331,6 @@ source drivers/usb/Config.in mainmenu_option next_comment comment 'Kernel hacking' -#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate 'Kernel FP software completion' CONFIG_MATHEMU else @@ -339,4 +338,7 @@ else fi bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ + +bool 'Legacy kernel start address' CONFIG_ALPHA_LEGACY_START_ADDRESS + endmenu diff --git a/arch/alpha/kernel/core_cia.c b/arch/alpha/kernel/core_cia.c index 88c88a4e6..348bd843f 100644 --- a/arch/alpha/kernel/core_cia.c +++ b/arch/alpha/kernel/core_cia.c @@ -19,6 +19,7 @@ #include <asm/system.h> #include <asm/ptrace.h> +#include <asm/hwrpb.h> #define __EXTERN_INLINE inline #include <asm/io.h> @@ -712,6 +713,25 @@ cia_init_arch(void) void __init pyxis_init_arch(void) { + /* On pyxis machines we can precisely calculate the + CPU clock frequency using pyxis real time counter. + It's especially useful for SX164 with broken RTC. + + Both CPU and chipset are driven by the single 16.666M + or 16.667M crystal oscillator. PYXIS_RT_COUNT clock is + 66.66 MHz. -ink */ + + unsigned int cc0, cc1; + unsigned long pyxis_cc; + + __asm__ __volatile__ ("rpcc %0" : "=r"(cc0)); + pyxis_cc = *(vulp)PYXIS_RT_COUNT; + do { } while(*(vulp)PYXIS_RT_COUNT - pyxis_cc < 4096); + __asm__ __volatile__ ("rpcc %0" : "=r"(cc1)); + cc1 -= cc0; + hwrpb->cycle_freq = ((cc1 >> 11) * 100000000UL) / 3; + hwrpb_update_checksum(hwrpb); + do_init_arch(1); } diff --git a/arch/alpha/kernel/pci.c b/arch/alpha/kernel/pci.c index 7a916ad08..2dc6f00dd 100644 --- a/arch/alpha/kernel/pci.c +++ b/arch/alpha/kernel/pci.c @@ -369,6 +369,22 @@ pcibios_enable_device(struct pci_dev *dev) return 0; } +/* + * If we set up a device for bus mastering, we need to check the latency + * timer as certain firmware forgets to set it properly, as seen + * on SX164 and LX164 with SRM. + */ +void +pcibios_set_master(struct pci_dev *dev) +{ + u8 lat; + pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat); + if (lat >= 16) return; + printk("PCI: Setting latency timer of device %s to 64\n", + dev->slot_name); + pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64); +} + #define ROUND_UP(x, a) (((x) + (a) - 1) & ~((a) - 1)) static void __init diff --git a/arch/alpha/kernel/setup.c b/arch/alpha/kernel/setup.c index 988c3cbec..073ee5381 100644 --- a/arch/alpha/kernel/setup.c +++ b/arch/alpha/kernel/setup.c @@ -246,11 +246,12 @@ get_mem_size_limit(char *s) } static void __init -setup_memory(void * kernel_end) +setup_memory(void *kernel_end) { struct memclust_struct * cluster; struct memdesc_struct * memdesc; - unsigned long start_pfn, bootmap_size, bootmap_pages, bootmap_start; + unsigned long start_kernel_pfn, end_kernel_pfn; + unsigned long bootmap_size, bootmap_pages, bootmap_start; unsigned long start, end; int i; @@ -282,12 +283,13 @@ setup_memory(void * kernel_end) max_low_pfn = mem_size_limit; } - /* Find the end of the memory used by the kernel. */ - start_pfn = PFN_UP(virt_to_phys(kernel_end)); + /* Find the bounds of kernel memory. */ + start_kernel_pfn = PFN_DOWN(KERNEL_START_PHYS); + end_kernel_pfn = PFN_UP(virt_to_phys(kernel_end)); bootmap_start = -1; try_again: - if (max_low_pfn <= start_pfn) + if (max_low_pfn <= end_kernel_pfn) panic("not enough memory to boot"); /* We need to know how many physically contiguous pages @@ -301,14 +303,19 @@ setup_memory(void * kernel_end) start = cluster->start_pfn; end = start + cluster->numpages; - if (end <= start_pfn) - continue; if (start >= max_low_pfn) continue; - if (start < start_pfn) - start = start_pfn; if (end > max_low_pfn) end = max_low_pfn; + if (start < start_kernel_pfn) { + if (end > end_kernel_pfn + && end - end_kernel_pfn >= bootmap_pages) { + bootmap_start = end_kernel_pfn; + break; + } else if (end > start_kernel_pfn) + end = start_kernel_pfn; + } else if (start < end_kernel_pfn) + start = end_kernel_pfn; if (end - start >= bootmap_pages) { bootmap_start = start; break; @@ -329,22 +336,28 @@ setup_memory(void * kernel_end) continue; start = cluster->start_pfn; - if (start < start_pfn) - start = start_pfn; - end = cluster->start_pfn + cluster->numpages; + if (start >= max_low_pfn) + continue; if (end > max_low_pfn) end = max_low_pfn; - + if (start < start_kernel_pfn) { + if (end > end_kernel_pfn) { + free_bootmem(PFN_PHYS(start), + (PFN_PHYS(start_kernel_pfn) + - PFN_PHYS(start))); + printk("freeing pages %ld:%ld\n", + start, start_kernel_pfn); + start = end_kernel_pfn; + } else if (end > start_kernel_pfn) + end = start_kernel_pfn; + } else if (start < end_kernel_pfn) + start = end_kernel_pfn; if (start >= end) continue; - start = PFN_PHYS(start); - end = PFN_PHYS(end); - - free_bootmem(start, end - start); - printk("freeing pages %ld:%ld\n", - PFN_UP(start), PFN_DOWN(end)); + free_bootmem(PFN_PHYS(start), PFN_PHYS(end) - PFN_PHYS(start)); + printk("freeing pages %ld:%ld\n", start, end); } /* Reserve the bootmap memory. */ @@ -461,11 +474,12 @@ void __init unregister_srm_console(void) void __init setup_arch(char **cmdline_p) { + extern char _end[]; + struct alpha_machine_vector *vec = NULL; struct percpu_struct *cpu; char *type_name, *var_name, *p; - extern char _end; - void * kernel_end = &_end; /* end of kernel */ + void *kernel_end = _end; /* end of kernel */ hwrpb = (struct hwrpb_struct*) __va(INIT_HWRPB->phys_addr); boot_cpuid = hard_smp_processor_id(); diff --git a/arch/alpha/kernel/sys_sx164.c b/arch/alpha/kernel/sys_sx164.c index b98e46028..19abbb6c3 100644 --- a/arch/alpha/kernel/sys_sx164.c +++ b/arch/alpha/kernel/sys_sx164.c @@ -24,6 +24,7 @@ #include <asm/io.h> #include <asm/pgtable.h> #include <asm/core_cia.h> +#include <asm/hwrpb.h> #include "proto.h" #include "irq_impl.h" @@ -114,6 +115,36 @@ sx164_init_pci(void) SMC669_Init(0); } +static void __init +sx164_init_arch(void) +{ + /* + * OSF palcode v1.23 forgets to enable PCA56 Motion Video + * Instructions. Let's enable it. + * We have to check palcode revision because CSERVE interface + * is subject to change without notice. For example, it + * has been changed completely since v1.16 (found in MILO + * distribution). -ink + */ + struct percpu_struct *cpu = (struct percpu_struct*) + ((char*)hwrpb + hwrpb->processor_offset); + + if (alpha_using_srm && (cpu->pal_revision & 0xffff) == 0x117) { + __asm__ __volatile__( + "lda $16,8($31)\n" + "call_pal 9\n" /* Allow PALRES insns in kernel mode */ + ".long 0x64000118\n\n" /* hw_mfpr $0,icsr */ + "ldah $16,(1<<(19-16))($31)\n" + "or $0,$16,$0\n" /* set MVE bit */ + ".long 0x74000118\n" /* hw_mtpr $0,icsr */ + "lda $16,9($31)\n" + "call_pal 9" /* Disable PALRES insns */ + : : : "$0", "$16"); + printk("PCA56 MVI set enabled\n"); + } + + pyxis_init_arch(); +} /* * The System Vector @@ -133,7 +164,7 @@ struct alpha_machine_vector sx164_mv __initmv = { nr_irqs: 48, device_interrupt: pyxis_device_interrupt, - init_arch: pyxis_init_arch, + init_arch: sx164_init_arch, init_irq: sx164_init_irq, init_rtc: common_init_rtc, init_pci: sx164_init_pci, diff --git a/arch/alpha/vmlinux.lds.in b/arch/alpha/vmlinux.lds.in new file mode 100644 index 000000000..a153e5bd1 --- /dev/null +++ b/arch/alpha/vmlinux.lds.in @@ -0,0 +1,96 @@ +#include <linux/config.h> + +OUTPUT_FORMAT("elf64-alpha") +ENTRY(__start) +PHDRS { kernel PT_LOAD ; } +SECTIONS +{ +#ifdef CONFIG_ALPHA_LEGACY_START_ADDRESS + . = 0xfffffc0000310000; +#else + . = 0xfffffc0000810000; +#endif + + _text = .; + .text : { *(.text) } :kernel + _etext = .; + + /* Exception table */ + . = ALIGN(16); + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + /* Kernel symbol table */ + . = ALIGN(8); + __start___ksymtab = .; + __ksymtab : { *(__ksymtab) } + __stop___ksymtab = .; + .kstrtab : { *(.kstrtab) } + + /* Startup code */ + . = ALIGN(8192); + __init_begin = .; + .text.init : { *(.text.init) } + .data.init : { *(.data.init) } + + . = ALIGN(16); + __setup_start = .; + .setup.init : { *(.setup.init) } + __setup_end = .; + + . = ALIGN(8); + __initcall_start = .; + .initcall.init : { *(.initcall.init) } + __initcall_end = .; + + . = ALIGN(2*8192); /* Align double page for init_task_union */ + __init_end = .; + + /* The initial task and kernel stack */ + init_task : { *(init_task) } + + /* Global data */ + _data = .; + .data.cacheline_aligned : { *(.data.cacheline_aligned) } + .rodata : { *(.rodata) } + .data : { *(.data) CONSTRUCTORS } + .got : { *(.got) } + .sdata : { *(.sdata) } + _edata = .; + + __bss_start = .; + .sbss : { *(.sbss) *(.scommon) } + .bss : { *(.bss) *(COMMON) } + __bss_stop = .; + _end = .; + + .mdebug 0 : { *(.mdebug) } + .note 0 : { *(.note) } + .comment 0 : { *(.comment) } + + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + + /DISCARD/ : { *(.text.exit) *(.data.exit) } +} diff --git a/arch/arm/config.in b/arch/arm/config.in index ff683b30b..15fba3403 100644 --- a/arch/arm/config.in +++ b/arch/arm/config.in @@ -12,6 +12,7 @@ define_bool CONFIG_UID16 y mainmenu_option next_comment comment 'Code maturity level options' bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL +bool 'Prompt for obsolete code/drivers' CONFIG_OBSOLETE endmenu @@ -45,8 +46,8 @@ if [ "$CONFIG_ARCH_SA1100" = "y" ]; then if [ "$CONFIG_SA1100_ASSABET" = "y" ]; then bool ' Include support for Neponset' CONFIG_ASSABET_NEPONSET fi - bool ' Include support for Bitsy' CONFIG_SA1100_BITSY bool ' Include support for Brutus' CONFIG_SA1100_BRUTUS + bool ' Include support for Compaq iPAQ 3600 (Bitsy)' CONFIG_SA1100_BITSY # bool ' Include support for Empeg' CONFIG_SA1100_EMPEG # bool ' Include support for Itsy' CONFIG_SA1100_ITSY bool ' Include support for LART' CONFIG_SA1100_LART @@ -342,7 +343,7 @@ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then bool 'Kernel low-level debugging functions' CONFIG_DEBUG_LL if [ "$CONFIG_DEBUG_LL" = "y" ]; then if [ "$CONFIG_FOOTBRIDGE" = "y" ]; then - bool 'Kernel low-level debugging messages via DC21285 port' CONFIG_DEBUG_DC21285_PORT + bool 'Kernel low-level debugging messages via footbridge serial port' CONFIG_DEBUG_DC21285_PORT fi fi fi diff --git a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c index ffd0f1b5e..5ac0743be 100644 --- a/arch/arm/kernel/armksyms.c +++ b/arch/arm/kernel/armksyms.c @@ -31,6 +31,8 @@ extern void outswb(unsigned int port, const void *to, int len); extern unsigned int local_bh_count[NR_CPUS]; extern unsigned int local_irq_count[NR_CPUS]; +extern void __bad_xchg(volatile void *ptr, int size); + /* * syscalls */ @@ -90,7 +92,6 @@ EXPORT_SYMBOL(kd_mksound); EXPORT_SYMBOL(dump_thread); EXPORT_SYMBOL(dump_fpu); EXPORT_SYMBOL(udelay); -EXPORT_SYMBOL(xchg_str); EXPORT_SYMBOL(local_bh_count); EXPORT_SYMBOL(local_irq_count); #ifdef CONFIG_CPU_32 @@ -103,6 +104,7 @@ EXPORT_SYMBOL(system_serial_low); EXPORT_SYMBOL(system_serial_high); EXPORT_SYMBOL(mem_fclk_21285); EXPORT_SYMBOL(__bug); +EXPORT_SYMBOL(__bad_xchg); EXPORT_SYMBOL(__readwrite_bug); EXPORT_SYMBOL(enable_irq); EXPORT_SYMBOL(disable_irq); diff --git a/arch/arm/kernel/arthur.c b/arch/arm/kernel/arthur.c index 8a8a5510d..0547302f8 100644 --- a/arch/arm/kernel/arthur.c +++ b/arch/arm/kernel/arthur.c @@ -3,8 +3,8 @@ * Copyright (C) 1998-1999 Philip Blundell */ -#include <linux/personality.h> #include <linux/module.h> +#include <linux/personality.h> #include <linux/stddef.h> #include <linux/signal.h> #include <linux/sched.h> diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c index 2fbda24be..a077f13b9 100644 --- a/arch/arm/kernel/bios32.c +++ b/arch/arm/kernel/bios32.c @@ -8,7 +8,6 @@ #include <linux/config.h> #include <linux/kernel.h> #include <linux/pci.h> -#include <linux/errno.h> #include <linux/init.h> #include <asm/irq.h> diff --git a/arch/arm/kernel/dma-footbridge.c b/arch/arm/kernel/dma-footbridge.c index 65875831c..1d2ef26c4 100644 --- a/arch/arm/kernel/dma-footbridge.c +++ b/arch/arm/kernel/dma-footbridge.c @@ -13,7 +13,6 @@ #include <linux/config.h> #include <linux/sched.h> -#include <linux/errno.h> #include <linux/init.h> #include <asm/dma.h> diff --git a/arch/arm/kernel/dma-rpc.c b/arch/arm/kernel/dma-rpc.c index e1b54233b..f4bc97f1d 100644 --- a/arch/arm/kernel/dma-rpc.c +++ b/arch/arm/kernel/dma-rpc.c @@ -15,6 +15,7 @@ #include <asm/fiq.h> #include <asm/io.h> #include <asm/iomd.h> +#include <asm/irq.h> #include <asm/hardware.h> #include <asm/uaccess.h> diff --git a/arch/arm/kernel/dma.c b/arch/arm/kernel/dma.c index 7d1a11cd5..ff8322d34 100644 --- a/arch/arm/kernel/dma.c +++ b/arch/arm/kernel/dma.c @@ -14,9 +14,9 @@ * * Moved DMA resource allocation here... */ +#include <linux/malloc.h> #include <linux/sched.h> #include <linux/module.h> -#include <linux/malloc.h> #include <linux/mman.h> #include <linux/init.h> #include <linux/spinlock.h> diff --git a/arch/arm/kernel/ecard.c b/arch/arm/kernel/ecard.c index 61eb422b2..b4d38e00f 100644 --- a/arch/arm/kernel/ecard.c +++ b/arch/arm/kernel/ecard.c @@ -33,9 +33,7 @@ #include <linux/interrupt.h> #include <linux/mm.h> #include <linux/malloc.h> -#include <linux/errno.h> #include <linux/proc_fs.h> -#include <linux/unistd.h> #include <linux/init.h> #include <asm/dma.h> @@ -913,7 +911,6 @@ ecard_probe(int slot, card_type_t type) ecard_t **ecp; ecard_t *ec; struct ex_ecid cid; - char buffer[200]; int i, rc = -ENOMEM; ec = kmalloc(sizeof(ecard_t), GFP_KERNEL); @@ -994,12 +991,9 @@ ecard_probe(int slot, card_type_t type) nodev: if (rc && ec) kfree(ec); - else { + else slot_to_expcard[slot] = ec; - ecard_prints(buffer, ec); - printk("%s", buffer); - } return rc; } @@ -1075,7 +1069,7 @@ void __init ecard_init(void) init_waitqueue_head(&ecard_done); #endif - printk("Probing expansion cards: (does not imply support)\n"); + printk("Probing expansion cards\n"); for (slot = 0; slot < 8; slot ++) { if (ecard_probe(slot, ECARD_EASI) == -ENODEV) diff --git a/arch/arm/kernel/hw-footbridge.c b/arch/arm/kernel/hw-footbridge.c index 08aac078e..b56b944e7 100644 --- a/arch/arm/kernel/hw-footbridge.c +++ b/arch/arm/kernel/hw-footbridge.c @@ -8,19 +8,12 @@ #include <linux/config.h> #include <linux/module.h> #include <linux/sched.h> +#include <linux/ioport.h> #include <linux/kernel.h> #include <linux/delay.h> -#include <linux/pci.h> -#include <linux/ptrace.h> -#include <linux/interrupt.h> -#include <linux/ioport.h> -#include <linux/smp.h> -#include <linux/mm.h> #include <linux/init.h> -#include <asm/dec21285.h> #include <asm/io.h> -#include <asm/irq.h> #include <asm/leds.h> #include <asm/system.h> @@ -28,6 +21,13 @@ #define GP1_IO_BASE 0x338 #define GP2_IO_BASE 0x33a + +#ifdef CONFIG_LEDS +#define DEFAULT_LEDS 0 +#else +#define DEFAULT_LEDS GPIO_GREEN_LED +#endif + /* * Netwinder stuff */ @@ -396,9 +396,9 @@ static unsigned char rwa_unlock[] __initdata = 0x3a, 0x9d, 0xce, 0xe7, 0x73, 0x39 }; #ifndef DEBUG -#define dprintk if (0) printk +#define dprintk(x...) #else -#define dprintk printk +#define dprintk(x...) printk(x) #endif #define WRITE_RWA(r,v) do { outb((r), 0x279); udelay(10); outb((v), 0xa79); } while (0) @@ -602,74 +602,13 @@ EXPORT_SYMBOL(gpio_modify_op); EXPORT_SYMBOL(gpio_modify_io); EXPORT_SYMBOL(cpld_modify); -#endif - -#ifdef CONFIG_LEDS -#define DEFAULT_LEDS 0 -#else -#define DEFAULT_LEDS GPIO_GREEN_LED -#endif - -/* - * CATS stuff - */ -#ifdef CONFIG_ARCH_CATS - -#define CONFIG_PORT 0x370 -#define INDEX_PORT (CONFIG_PORT) -#define DATA_PORT (CONFIG_PORT + 1) - -static void __init cats_hw_init(void) -{ - /* Set Aladdin to CONFIGURE mode */ - outb(0x51, CONFIG_PORT); - outb(0x23, CONFIG_PORT); - - /* Select logical device 3 */ - outb(0x07, INDEX_PORT); - outb(0x03, DATA_PORT); - - /* Set parallel port to DMA channel 3, ECP+EPP1.9, - enable EPP timeout */ - outb(0x74, INDEX_PORT); - outb(0x03, DATA_PORT); - - outb(0xf0, INDEX_PORT); - outb(0x0f, DATA_PORT); - - outb(0xf1, INDEX_PORT); - outb(0x07, DATA_PORT); - - /* Select logical device 4 */ - outb(0x07, INDEX_PORT); - outb(0x04, DATA_PORT); - - /* UART1 high speed mode */ - outb(0xf0, INDEX_PORT); - outb(0x02, DATA_PORT); - - /* Select logical device 5 */ - outb(0x07, INDEX_PORT); - outb(0x05, DATA_PORT); - - /* UART2 high speed mode */ - outb(0xf0, INDEX_PORT); - outb(0x02, DATA_PORT); - - /* Set Aladdin to RUN mode */ - outb(0xbb, CONFIG_PORT); -} - -#endif - /* * Initialise any other hardware after we've got the PCI bus * initialised. We may need the PCI bus to talk to this other * hardware. */ -static int __init hw_init(void) +static int __init nw_hw_init(void) { -#ifdef CONFIG_ARCH_NETWINDER /* * this ought to have a better home... * Since this calls the above routines, which are @@ -688,12 +627,66 @@ static int __init hw_init(void) gpio_modify_op(GPIO_RED_LED|GPIO_GREEN_LED, DEFAULT_LEDS); spin_unlock_irqrestore(&gpio_lock, flags); } + return 0; +} + +__initcall(nw_hw_init); #endif + +/* + * CATS stuff + */ #ifdef CONFIG_ARCH_CATS - if (machine_is_cats()) - cats_hw_init(); -#endif + +#define CONFIG_PORT 0x370 +#define INDEX_PORT (CONFIG_PORT) +#define DATA_PORT (CONFIG_PORT + 1) + +static int __init cats_hw_init(void) +{ + if (machine_is_cats()) { + /* Set Aladdin to CONFIGURE mode */ + outb(0x51, CONFIG_PORT); + outb(0x23, CONFIG_PORT); + + /* Select logical device 3 */ + outb(0x07, INDEX_PORT); + outb(0x03, DATA_PORT); + + /* Set parallel port to DMA channel 3, ECP+EPP1.9, + enable EPP timeout */ + outb(0x74, INDEX_PORT); + outb(0x03, DATA_PORT); + + outb(0xf0, INDEX_PORT); + outb(0x0f, DATA_PORT); + + outb(0xf1, INDEX_PORT); + outb(0x07, DATA_PORT); + + /* Select logical device 4 */ + outb(0x07, INDEX_PORT); + outb(0x04, DATA_PORT); + + /* UART1 high speed mode */ + outb(0xf0, INDEX_PORT); + outb(0x02, DATA_PORT); + + /* Select logical device 5 */ + outb(0x07, INDEX_PORT); + outb(0x05, DATA_PORT); + + /* UART2 high speed mode */ + outb(0xf0, INDEX_PORT); + outb(0x02, DATA_PORT); + + /* Set Aladdin to RUN mode */ + outb(0xbb, CONFIG_PORT); + } + return 0; } -__initcall(hw_init); +__initcall(cats_hw_init); +#endif + diff --git a/arch/arm/kernel/hw-sa1100.c b/arch/arm/kernel/hw-sa1100.c index 539bb721b..862c3a2c4 100644 --- a/arch/arm/kernel/hw-sa1100.c +++ b/arch/arm/kernel/hw-sa1100.c @@ -10,9 +10,9 @@ * */ #include <linux/config.h> +#include <linux/module.h> #include <linux/init.h> #include <linux/kernel.h> -#include <linux/module.h> #include <linux/sched.h> #include <asm/delay.h> diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c index 7a761b7c6..40a47c45f 100644 --- a/arch/arm/kernel/irq.c +++ b/arch/arm/kernel/irq.c @@ -17,7 +17,6 @@ */ #include <linux/config.h> #include <linux/ptrace.h> -#include <linux/errno.h> #include <linux/kernel_stat.h> #include <linux/signal.h> #include <linux/sched.h> @@ -26,7 +25,6 @@ #include <linux/malloc.h> #include <linux/random.h> #include <linux/smp.h> -#include <linux/smp_lock.h> #include <linux/init.h> #include <asm/hardware.h> diff --git a/arch/arm/kernel/isa.c b/arch/arm/kernel/isa.c index a5424f8b6..17696acb0 100644 --- a/arch/arm/kernel/isa.c +++ b/arch/arm/kernel/isa.c @@ -13,7 +13,6 @@ #include <linux/stddef.h> #include <linux/types.h> -#include <linux/linkage.h> #include <linux/fs.h> #include <linux/sysctl.h> #include <linux/init.h> diff --git a/arch/arm/kernel/leds-footbridge.c b/arch/arm/kernel/leds-footbridge.c index 4fa2237eb..b309c2ea3 100644 --- a/arch/arm/kernel/leds-footbridge.c +++ b/arch/arm/kernel/leds-footbridge.c @@ -18,8 +18,8 @@ * 02-05-1999 RMK Various cleanups */ #include <linux/config.h> -#include <linux/kernel.h> #include <linux/module.h> +#include <linux/kernel.h> #include <linux/init.h> #include <linux/spinlock.h> diff --git a/arch/arm/kernel/leds-sa1100.c b/arch/arm/kernel/leds-sa1100.c index ef6918d7c..f2f0325c3 100644 --- a/arch/arm/kernel/leds-sa1100.c +++ b/arch/arm/kernel/leds-sa1100.c @@ -29,8 +29,8 @@ * */ #include <linux/config.h> -#include <linux/kernel.h> #include <linux/module.h> +#include <linux/kernel.h> #include <linux/init.h> #include <linux/spinlock.h> diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 40b3b9e72..cab969c5c 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -7,28 +7,22 @@ #include <stdarg.h> -#include <linux/errno.h> +#include <linux/config.h> #include <linux/sched.h> #include <linux/kernel.h> #include <linux/mm.h> -#include <linux/smp.h> -#include <linux/smp_lock.h> #include <linux/stddef.h> #include <linux/unistd.h> #include <linux/ptrace.h> #include <linux/malloc.h> -#include <linux/vmalloc.h> #include <linux/user.h> -#include <linux/a.out.h> -#include <linux/interrupt.h> -#include <linux/config.h> #include <linux/delay.h> #include <linux/reboot.h> #include <linux/init.h> -#include <asm/uaccess.h> #include <asm/system.h> #include <asm/io.h> +#include <asm/uaccess.h> /* * Values for cpu_do_idle() diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c index 1684c5f5f..e45e03fcb 100644 --- a/arch/arm/kernel/ptrace.c +++ b/arch/arm/kernel/ptrace.c @@ -8,7 +8,6 @@ #include <linux/mm.h> #include <linux/smp.h> #include <linux/smp_lock.h> -#include <linux/errno.h> #include <linux/ptrace.h> #include <linux/user.h> @@ -16,6 +15,10 @@ #include <asm/pgtable.h> #include <asm/system.h> +#include "ptrace.h" + +#define REG_PC 15 +#define REG_PSR 16 /* * does not yet catch signals sent when the child dies. * in exit.c or in signal.c. @@ -27,6 +30,18 @@ #define BREAKINST 0xef9f0001 /* + * Get the address of the live pt_regs for the specified task. + * These are saved onto the top kernel stack when the process + * is not running. + */ +static inline struct pt_regs * +get_user_regs(struct task_struct *task) +{ + return (struct pt_regs *) + ((unsigned long)task + 8192 - sizeof(struct pt_regs)); +} + +/* * this routine will get a word off of the processes privileged stack. * the offset is how far from the base addr as stored in the THREAD. * this routine assumes that all the privileged stacks are in our @@ -34,11 +49,7 @@ */ static inline long get_stack_long(struct task_struct *task, int offset) { - struct pt_regs *regs; - - regs = (struct pt_regs *)((unsigned long)task + 8192 - sizeof(struct pt_regs)); - - return regs->uregs[offset]; + return get_user_regs(task)->uregs[offset]; } /* @@ -47,20 +58,16 @@ static inline long get_stack_long(struct task_struct *task, int offset) * this routine assumes that all the privileged stacks are in our * data space. */ -static inline long put_stack_long(struct task_struct *task, int offset, - unsigned long data) +static inline int +put_stack_long(struct task_struct *task, int offset, long data) { - struct pt_regs *regs; - - regs = (struct pt_regs *)((unsigned long)task + 8192 - sizeof(struct pt_regs)); - - regs->uregs[offset] = data; + get_user_regs(task)->uregs[offset] = data; return 0; } -static int -read_long(struct task_struct *child, unsigned long addr, unsigned long *res) +static inline int +read_tsk_long(struct task_struct *child, unsigned long addr, unsigned long *res) { int copied; @@ -69,8 +76,8 @@ read_long(struct task_struct *child, unsigned long addr, unsigned long *res) return copied != sizeof(*res) ? -EIO : 0; } -static int -write_long(struct task_struct *child, unsigned long addr, unsigned long val) +static inline int +write_tsk_long(struct task_struct *child, unsigned long addr, unsigned long val) { int copied; @@ -82,35 +89,33 @@ write_long(struct task_struct *child, unsigned long addr, unsigned long val) /* * Get value of register `rn' (in the instruction) */ -static unsigned long ptrace_getrn (struct task_struct *child, unsigned long insn) +static unsigned long +ptrace_getrn(struct task_struct *child, unsigned long insn) { unsigned int reg = (insn >> 16) & 15; unsigned long val; + val = get_stack_long(child, reg); if (reg == 15) - val = pc_pointer (get_stack_long (child, reg)); - else - val = get_stack_long (child, reg); + val = pc_pointer(val); -printk ("r%02d=%08lX ", reg, val); return val; } /* * Get value of operand 2 (in an ALU instruction) */ -static unsigned long ptrace_getaluop2 (struct task_struct *child, unsigned long insn) +static unsigned long +ptrace_getaluop2(struct task_struct *child, unsigned long insn) { unsigned long val; int shift; int type; -printk ("op2="); if (insn & 1 << 25) { val = insn & 255; shift = (insn >> 8) & 15; type = 3; -printk ("(imm)"); } else { val = get_stack_long (child, insn & 15); @@ -120,9 +125,8 @@ printk ("(imm)"); shift = (insn >> 7) & 31; type = (insn >> 5) & 3; -printk ("(r%02ld)", insn & 15); } -printk ("sh%dx%d", type, shift); + switch (type) { case 0: val <<= shift; break; case 1: val >>= shift; break; @@ -133,24 +137,23 @@ printk ("sh%dx%d", type, shift); val = (val >> shift) | (val << (32 - shift)); break; } -printk ("=%08lX ", val); return val; } /* * Get value of operand 2 (in a LDR instruction) */ -static unsigned long ptrace_getldrop2 (struct task_struct *child, unsigned long insn) +static unsigned long +ptrace_getldrop2(struct task_struct *child, unsigned long insn) { unsigned long val; int shift; int type; - val = get_stack_long (child, insn & 15); + val = get_stack_long(child, insn & 15); shift = (insn >> 7) & 31; type = (insn >> 5) & 3; -printk ("op2=r%02ldsh%dx%d", insn & 15, shift, type); switch (type) { case 0: val <<= shift; break; case 1: val >>= shift; break; @@ -161,7 +164,6 @@ printk ("op2=r%02ldsh%dx%d", insn & 15, shift, type); val = (val >> shift) | (val << (32 - shift)); break; } -printk ("=%08lX ", val); return val; } @@ -170,95 +172,72 @@ get_branch_address(struct task_struct *child, unsigned long pc, unsigned long in { unsigned long alt = 0; -printk(KERN_DEBUG "ptrace_set_bpt: insn=%08lX pc=%08lX ", insn, pc); - switch (insn & 0x0e100000) { + switch (insn & 0x0e000000) { case 0x00000000: - case 0x00100000: - case 0x02000000: - case 0x02100000: /* data processing */ - printk ("data "); - switch (insn & 0x01e0f000) { - case 0x0000f000: - alt = ptrace_getrn(child, insn) & ptrace_getaluop2(child, insn); - break; - case 0x0020f000: - alt = ptrace_getrn(child, insn) ^ ptrace_getaluop2(child, insn); - break; - case 0x0040f000: - alt = ptrace_getrn(child, insn) - ptrace_getaluop2(child, insn); - break; - case 0x0060f000: - alt = ptrace_getaluop2(child, insn) - ptrace_getrn(child, insn); - break; - case 0x0080f000: - alt = ptrace_getrn(child, insn) + ptrace_getaluop2(child, insn); - break; - case 0x00a0f000: - alt = ptrace_getrn(child, insn) + ptrace_getaluop2(child, insn) + - (get_stack_long (child, 16/*REG_PSR*/) & CC_C_BIT ? 1 : 0); - break; - case 0x00c0f000: - alt = ptrace_getrn(child, insn) - ptrace_getaluop2(child, insn) + - (get_stack_long (child, 16/*REG_PSR*/) & CC_C_BIT ? 1 : 0); - break; - case 0x00e0f000: - alt = ptrace_getaluop2(child, insn) - ptrace_getrn(child, insn) + - (get_stack_long (child, 16/*REG_PSR*/) & CC_C_BIT ? 1 : 0); - break; - case 0x0180f000: - alt = ptrace_getrn(child, insn) | ptrace_getaluop2(child, insn); - break; - case 0x01a0f000: - alt = ptrace_getaluop2(child, insn); - break; - case 0x01c0f000: - alt = ptrace_getrn(child, insn) & ~ptrace_getaluop2(child, insn); - break; - case 0x01e0f000: - alt = ~ptrace_getaluop2(child, insn); + case 0x02000000: { + /* + * data processing + */ + long aluop1, aluop2, ccbit; + + if ((insn & 0xf000) != 0xf000) break; + + aluop1 = ptrace_getrn(child, insn); + aluop2 = ptrace_getaluop2(child, insn); + ccbit = get_stack_long(child, REG_PSR) & CC_C_BIT ? 1 : 0; + + switch (insn & 0x01e00000) { + case 0x00000000: alt = aluop1 & aluop2; break; + case 0x00200000: alt = aluop1 ^ aluop2; break; + case 0x00400000: alt = aluop1 - aluop2; break; + case 0x00600000: alt = aluop2 - aluop1; break; + case 0x00800000: alt = aluop1 + aluop2; break; + case 0x00a00000: alt = aluop1 + aluop2 + ccbit; break; + case 0x00c00000: alt = aluop1 - aluop2 + ccbit; break; + case 0x00e00000: alt = aluop2 - aluop1 + ccbit; break; + case 0x01800000: alt = aluop1 | aluop2; break; + case 0x01a00000: alt = aluop2; break; + case 0x01c00000: alt = aluop1 & ~aluop2; break; + case 0x01e00000: alt = ~aluop2; break; } break; + } + + case 0x04000000: + case 0x06000000: + /* + * ldr + */ + if ((insn & 0x0010f000) == 0x0010f000) { + unsigned long base; - case 0x04100000: /* ldr */ - if ((insn & 0xf000) == 0xf000) { -printk ("ldr "); - alt = ptrace_getrn(child, insn); + base = ptrace_getrn(child, insn); if (insn & 1 << 24) { - if (insn & 1 << 23) - alt += ptrace_getldrop2 (child, insn); + long aluop2; + + if (insn & 0x02000000) + aluop2 = ptrace_getldrop2(child, insn); else - alt -= ptrace_getldrop2 (child, insn); - } - if (read_long (child, alt, &alt) < 0) - alt = 0; /* not valid */ - else - alt = pc_pointer (alt); - } - break; + aluop2 = insn & 0xfff; - case 0x06100000: /* ldr imm */ - if ((insn & 0xf000) == 0xf000) { -printk ("ldrimm "); - alt = ptrace_getrn(child, insn); - if (insn & 1 << 24) { if (insn & 1 << 23) - alt += insn & 0xfff; + base += aluop2; else - alt -= insn & 0xfff; + base -= aluop2; } - if (read_long (child, alt, &alt) < 0) - alt = 0; /* not valid */ - else - alt = pc_pointer (alt); + if (read_tsk_long(child, base, &alt) == 0) + alt = pc_pointer(alt); } break; - case 0x08100000: /* ldm */ - if (insn & (1 << 15)) { + case 0x08000000: + /* + * ldm + */ + if ((insn & 0x00108000) == 0x00108000) { unsigned long base; - int nr_regs; -printk ("ldm "); + unsigned int nr_regs; if (insn & (1 << 23)) { nr_regs = insn & 65535; @@ -278,23 +257,22 @@ printk ("ldm "); nr_regs = 0; } - base = ptrace_getrn (child, insn); + base = ptrace_getrn(child, insn); - if (read_long (child, base + nr_regs, &alt) < 0) - alt = 0; /* not valid */ - else + if (read_tsk_long(child, base + nr_regs, &alt) == 0) alt = pc_pointer (alt); break; } break; - case 0x0a000000: - case 0x0a100000: { /* bl or b */ + case 0x0a000000: { + /* + * bl or b + */ signed long displ; -printk ("b/bl "); /* It's a branch/branch link: instead of trying to * figure out whether the branch will be taken or not, - * we'll put a breakpoint at either location. This is + * we'll put a breakpoint at both locations. This is * simpler, more reliable, and probably not a whole lot * slower than the alternative approach of emulating the * branch. @@ -306,7 +284,6 @@ printk ("b/bl "); } break; } -printk ("=%08lX\n", alt); return alt; } @@ -318,9 +295,9 @@ add_breakpoint(struct task_struct *child, struct debug_info *dbg, unsigned long int res = -EINVAL; if (nr < 2) { - res = read_long(child, addr, &dbg->bp[nr].insn); + res = read_tsk_long(child, addr, &dbg->bp[nr].insn); if (res == 0) - res = write_long(child, addr, BREAKINST); + res = write_tsk_long(child, addr, BREAKINST); if (res == 0) { dbg->bp[nr].address = addr; @@ -332,257 +309,309 @@ add_breakpoint(struct task_struct *child, struct debug_info *dbg, unsigned long return res; } -int ptrace_set_bpt (struct task_struct *child) +int ptrace_set_bpt(struct task_struct *child) { - struct debug_info *dbg = &child->thread.debug; - unsigned long insn, pc, alt; + unsigned long insn, pc; int res; - pc = pc_pointer (get_stack_long (child, 15/*REG_PC*/)); + pc = pc_pointer(get_stack_long(child, REG_PC)); - res = read_long(child, pc, &insn); - if (res >= 0) { - res = 0; + res = read_tsk_long(child, pc, &insn); + if (!res) { + struct debug_info *dbg = &child->thread.debug; + unsigned long alt; dbg->nsaved = 0; - res = add_breakpoint(child, dbg, pc + 4); + alt = get_branch_address(child, pc, insn); + if (alt) + res = add_breakpoint(child, dbg, alt); - if (res == 0) { - alt = get_branch_address(child, pc, insn); - if (alt) - res = add_breakpoint(child, dbg, alt); - } + if (!res && (!alt || predicate(insn) != PREDICATE_ALWAYS)) + res = add_breakpoint(child, dbg, pc + 4); } return res; } -/* Ensure no single-step breakpoint is pending. Returns non-zero +/* + * Ensure no single-step breakpoint is pending. Returns non-zero * value if child was being single-stepped. */ -int ptrace_cancel_bpt (struct task_struct *child) +void __ptrace_cancel_bpt(struct task_struct *child) { struct debug_info *dbg = &child->thread.debug; - unsigned long tmp; int i, nsaved = dbg->nsaved; dbg->nsaved = 0; if (nsaved > 2) { - printk ("ptrace_cancel_bpt: bogus nsaved: %d!\n", nsaved); + printk("ptrace_cancel_bpt: bogus nsaved: %d!\n", nsaved); nsaved = 2; } for (i = 0; i < nsaved; i++) { - read_long(child, dbg->bp[i].address, &tmp); + unsigned long tmp; + + read_tsk_long(child, dbg->bp[i].address, &tmp); if (tmp != BREAKINST) printk(KERN_ERR "ptrace_cancel_bpt: weirdness\n"); - write_long(child, dbg->bp[i].address, dbg->bp[i].insn); + write_tsk_long(child, dbg->bp[i].address, dbg->bp[i].insn); } - - return nsaved != 0; } -asmlinkage int sys_ptrace(long request, long pid, long addr, long data) +static int do_ptrace(int request, struct task_struct *child, long addr, long data) { - struct task_struct *child; + unsigned long tmp; int ret; - lock_kernel(); - ret = -EPERM; - if (request == PTRACE_TRACEME) { - /* are we already being traced? */ - if (current->ptrace & PT_PTRACED) - goto out; - /* set the ptrace bit in the process flags. */ - current->ptrace |= PT_PTRACED; - ret = 0; - goto out; - } - if (pid == 1) /* you may not mess with init */ - goto out; - ret = -ESRCH; - if (!(child = find_task_by_pid(pid))) - goto out; - ret = -EPERM; - if (request == PTRACE_ATTACH) { - if (child == current) - goto out; - if ((!child->dumpable || - (current->uid != child->euid) || - (current->uid != child->suid) || - (current->uid != child->uid) || - (current->gid != child->egid) || - (current->gid != child->sgid) || - (!cap_issubset(child->cap_permitted, current->cap_permitted)) || - (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE)) - goto out; - /* the same process cannot be attached many times */ - if (child->ptrace & PT_PTRACED) - goto out; - child->ptrace |= PT_PTRACED; - if (child->p_pptr != current) { - REMOVE_LINKS(child); - child->p_pptr = current; - SET_LINKS(child); - } - send_sig(SIGSTOP, child, 1); - ret = 0; - goto out; - } - ret = -ESRCH; - if (!(child->ptrace & PT_PTRACED)) - goto out; - if (child->state != TASK_STOPPED) { - if (request != PTRACE_KILL) - goto out; - } - if (child->p_pptr != current) - goto out; - switch (request) { - case PTRACE_PEEKTEXT: /* read word at location addr. */ - case PTRACE_PEEKDATA: { - unsigned long tmp; - - ret = read_long(child, addr, &tmp); + /* + * read word at location "addr" in the child process. + */ + case PTRACE_PEEKTEXT: + case PTRACE_PEEKDATA: + ret = read_tsk_long(child, addr, &tmp); if (!ret) ret = put_user(tmp, (unsigned long *) data); - goto out; - } - - case PTRACE_PEEKUSR: { /* read the word at location addr in the USER area. */ - unsigned long tmp; + break; + /* + * read the word at location "addr" in the user registers. + */ + case PTRACE_PEEKUSR: ret = -EIO; if ((addr & 3) || addr < 0 || addr >= sizeof(struct user)) - goto out; + break; tmp = 0; /* Default return condition */ - if (addr < sizeof (struct pt_regs)) + if (addr < sizeof(struct pt_regs)) tmp = get_stack_long(child, (int)addr >> 2); ret = put_user(tmp, (unsigned long *)data); - goto out; - } + break; - case PTRACE_POKETEXT: /* write the word at location addr. */ + /* + * write the word at location addr. + */ + case PTRACE_POKETEXT: case PTRACE_POKEDATA: - ret = write_long(child, addr, data); - goto out; + ret = write_tsk_long(child, addr, data); + break; - case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ + /* + * write the word at location addr in the user registers. + */ + case PTRACE_POKEUSR: ret = -EIO; if ((addr & 3) || addr < 0 || addr >= sizeof(struct user)) - goto out; + break; if (addr < sizeof (struct pt_regs)) ret = put_stack_long(child, (int)addr >> 2, data); - goto out; + break; - case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ - case PTRACE_CONT: /* restart after signal. */ + /* + * continue/restart and stop at next (return from) syscall + */ + case PTRACE_SYSCALL: + case PTRACE_CONT: ret = -EIO; if ((unsigned long) data > _NSIG) - goto out; + break; if (request == PTRACE_SYSCALL) child->ptrace |= PT_TRACESYS; else child->ptrace &= ~PT_TRACESYS; child->exit_code = data; - wake_up_process (child); /* make sure single-step breakpoint is gone. */ - ptrace_cancel_bpt (child); + __ptrace_cancel_bpt(child); + wake_up_process(child); ret = 0; - goto out; + break; - /* make the child exit. Best I can do is send it a sigkill. + /* + * make the child exit. Best I can do is send it a sigkill. * perhaps it should be put in the status that it wants to * exit. */ case PTRACE_KILL: - if (child->state == TASK_ZOMBIE) /* already dead */ - return 0; - wake_up_process (child); + /* already dead */ + ret = 0; + if (child->state == TASK_ZOMBIE) + break; child->exit_code = SIGKILL; /* make sure single-step breakpoint is gone. */ - ptrace_cancel_bpt (child); + __ptrace_cancel_bpt(child); + wake_up_process(child); ret = 0; - goto out; + break; - case PTRACE_SINGLESTEP: /* execute single instruction. */ + /* + * execute single instruction. + */ + case PTRACE_SINGLESTEP: ret = -EIO; if ((unsigned long) data > _NSIG) - goto out; + break; child->thread.debug.nsaved = -1; child->ptrace &= ~PT_TRACESYS; - wake_up_process(child); child->exit_code = data; /* give it a chance to run. */ + wake_up_process(child); ret = 0; - goto out; - - case PTRACE_GETREGS: - { /* Get all gp regs from the child. */ - unsigned char *stack; + break; + + /* + * detach a process that was attached. + */ + case PTRACE_DETACH: + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + child->ptrace &= ~(PT_PTRACED|PT_TRACESYS); + child->exit_code = data; + write_lock_irq(&tasklist_lock); + REMOVE_LINKS(child); + child->p_pptr = child->p_opptr; + SET_LINKS(child); + write_unlock_irq(&tasklist_lock); + /* make sure single-step breakpoint is gone. */ + __ptrace_cancel_bpt(child); + wake_up_process (child); + ret = 0; + break; + + /* + * Get all gp regs from the child. + */ + case PTRACE_GETREGS: { + struct pt_regs *regs = get_user_regs(child); ret = 0; - stack = (unsigned char *)((unsigned long)child + 8192 - sizeof(struct pt_regs)); - if (copy_to_user((void *)data, stack, + if (copy_to_user((void *)data, regs, sizeof(struct pt_regs))) ret = -EFAULT; - goto out; - }; + break; + } - case PTRACE_SETREGS: - { - /* Set all gp regs in the child. */ - unsigned char *stack; + /* + * Set all gp regs in the child. + */ + case PTRACE_SETREGS: { + struct pt_regs *regs = get_user_regs(child); ret = 0; - stack = (unsigned char *)((unsigned long)child + 8192 - sizeof(struct pt_regs)); - if (copy_from_user(stack, (void *)data, + if (copy_from_user(regs, (void *)data, sizeof(struct pt_regs))) ret = -EFAULT; - goto out; - }; + break; + } - case PTRACE_GETFPREGS: - /* Get the child FPU state. */ - ret = 0; - if (copy_to_user((void *)data, &child->thread.fpstate, - sizeof(struct user_fp))) - ret = -EFAULT; - goto out; + /* + * Get the child FPU state. + */ + case PTRACE_GETFPREGS: + ret = -EIO; + if (!access_ok(VERIFY_WRITE, (void *)data, sizeof(struct user_fp))) + break; + + /* we should check child->used_math here */ + ret = __copy_to_user((void *)data, &child->thread.fpstate, + sizeof(struct user_fp)) ? -EFAULT : 0; + break; + /* + * Set the child FPU state. + */ case PTRACE_SETFPREGS: - /* Set the child FPU state. */ - ret = 0; - if (copy_from_user(&child->thread.fpstate, (void *)data, - sizeof(struct user_fp))) - ret = -EFAULT; - goto out; - - case PTRACE_DETACH: /* detach a process that was attached. */ ret = -EIO; - if ((unsigned long) data > _NSIG) - goto out; - child->ptrace &= ~(PT_PTRACED|PT_TRACESYS); - wake_up_process (child); - child->exit_code = data; - REMOVE_LINKS(child); - child->p_pptr = child->p_opptr; - SET_LINKS(child); - /* make sure single-step breakpoint is gone. */ - ptrace_cancel_bpt (child); - ret = 0; - goto out; + if (!access_ok(VERIFY_READ, (void *)data, sizeof(struct user_fp))) + break; + + child->used_math = 1; + ret = __copy_from_user(&child->thread.fpstate, (void *)data, + sizeof(struct user_fp)) ? -EFAULT : 0; + break; default: ret = -EIO; + break; + } + + return ret; +} + +asmlinkage int sys_ptrace(long request, long pid, long addr, long data) +{ + struct task_struct *child; + int ret; + + lock_kernel(); + ret = -EPERM; + if (request == PTRACE_TRACEME) { + /* are we already being traced? */ + if (current->ptrace & PT_PTRACED) goto out; + /* set the ptrace bit in the process flags. */ + current->ptrace |= PT_PTRACED; + ret = 0; + goto out; } + ret = -ESRCH; + read_lock(&tasklist_lock); + child = find_task_by_pid(pid); + if (child) + get_task_struct(child); + read_unlock(&tasklist_lock); + if (!child) + goto out; + + ret = -EPERM; + if (pid == 1) /* you may not mess with init */ + goto out_tsk; + + if (request == PTRACE_ATTACH) { + if (child == current) + goto out_tsk; + if ((!child->dumpable || + (current->uid != child->euid) || + (current->uid != child->suid) || + (current->uid != child->uid) || + (current->gid != child->egid) || + (current->gid != child->sgid) || + (!cap_issubset(child->cap_permitted, current->cap_permitted)) || + (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE)) + goto out_tsk; + /* the same process cannot be attached many times */ + if (child->ptrace & PT_PTRACED) + goto out_tsk; + child->ptrace |= PT_PTRACED; + + write_lock_irq(&tasklist_lock); + if (child->p_pptr != current) { + REMOVE_LINKS(child); + child->p_pptr = current; + SET_LINKS(child); + } + write_unlock_irq(&tasklist_lock); + + send_sig(SIGSTOP, child, 1); + ret = 0; + goto out_tsk; + } + ret = -ESRCH; + if (!(child->ptrace & PT_PTRACED)) + goto out_tsk; + if (child->state != TASK_STOPPED && request != PTRACE_KILL) + goto out_tsk; + if (child->p_pptr != current) + goto out_tsk; + + ret = do_ptrace(request, child, addr, data); + +out_tsk: + free_task_struct(child); out: unlock_kernel(); return ret; diff --git a/arch/arm/kernel/ptrace.h b/arch/arm/kernel/ptrace.h new file mode 100644 index 000000000..feae0acd8 --- /dev/null +++ b/arch/arm/kernel/ptrace.h @@ -0,0 +1,16 @@ +extern void __ptrace_cancel_bpt(struct task_struct *); +extern int ptrace_set_bpt(struct task_struct *); + +/* + * Clear a breakpoint, if one exists. + */ +static inline int ptrace_cancel_bpt(struct task_struct *tsk) +{ + int nsaved = tsk->thread.debug.nsaved; + + if (nsaved) + __ptrace_cancel_bpt(tsk); + + return nsaved; +} + diff --git a/arch/arm/kernel/semaphore.c b/arch/arm/kernel/semaphore.c index 93a370f2d..8118b6a68 100644 --- a/arch/arm/kernel/semaphore.c +++ b/arch/arm/kernel/semaphore.c @@ -8,7 +8,6 @@ * Modified for ARM by Russell King */ #include <linux/sched.h> -#include <linux/errno.h> #include <asm/semaphore.h> diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index 1f4295540..c6010476a 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -35,6 +35,7 @@ extern void paging_init(struct meminfo *); extern void bootmem_init(struct meminfo *); extern void reboot_setup(char *str); extern void disable_hlt(void); +extern unsigned long memparse(char *ptr, char **retptr); extern int root_mountflags; extern int _stext, _text, _etext, _edata, _end; diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index 431dd96c1..3e6cf6cb4 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c @@ -10,18 +10,19 @@ #include <linux/smp.h> #include <linux/smp_lock.h> #include <linux/kernel.h> -#include <linux/signal.h> #include <linux/errno.h> +#include <linux/signal.h> #include <linux/wait.h> #include <linux/ptrace.h> -#include <linux/unistd.h> #include <linux/stddef.h> -#include <linux/binfmts.h> +#include <linux/unistd.h> #include <linux/tty.h> +#include <asm/pgalloc.h> #include <asm/ucontext.h> #include <asm/uaccess.h> -#include <asm/pgalloc.h> + +#include "ptrace.h" #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) @@ -31,8 +32,6 @@ asmlinkage int sys_wait4(pid_t pid, unsigned long * stat_addr, int options, unsigned long *ru); asmlinkage int do_signal(sigset_t *oldset, struct pt_regs * regs, int syscall); -extern int ptrace_cancel_bpt (struct task_struct *); -extern int ptrace_set_bpt (struct task_struct *); int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from) { @@ -234,7 +233,7 @@ asmlinkage int sys_sigreturn(struct pt_regs *regs) goto badframe; /* Send SIGTRAP if we're single-stepping */ - if (ptrace_cancel_bpt (current)) + if (ptrace_cancel_bpt(current)) send_sig(SIGTRAP, current, 1); return regs->ARM_r0; @@ -274,7 +273,7 @@ asmlinkage int sys_rt_sigreturn(struct pt_regs *regs) goto badframe; /* Send SIGTRAP if we're single-stepping */ - if (ptrace_cancel_bpt (current)) + if (ptrace_cancel_bpt(current)) send_sig(SIGTRAP, current, 1); return regs->ARM_r0; @@ -500,7 +499,7 @@ asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) if (!oldset) oldset = ¤t->blocked; - single_stepping = ptrace_cancel_bpt (current); + single_stepping = ptrace_cancel_bpt(current); for (;;) { unsigned long signr; @@ -518,7 +517,7 @@ asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) current->state = TASK_STOPPED; notify_parent(current, SIGCHLD); schedule(); - single_stepping |= ptrace_cancel_bpt (current); + single_stepping |= ptrace_cancel_bpt(current); /* We're back. Did the debugger cancel the sig? */ if (!(signr = current->exit_code)) @@ -617,7 +616,7 @@ asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) /* Whee! Actually deliver the signal. */ handle_signal(signr, ka, &info, oldset, regs); if (single_stepping) - ptrace_set_bpt (current); + ptrace_set_bpt(current); return 1; } @@ -629,6 +628,6 @@ asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) regs->ARM_pc -= 4; } if (single_stepping) - ptrace_set_bpt (current); + ptrace_set_bpt(current); return 0; } diff --git a/arch/arm/kernel/time.c b/arch/arm/kernel/time.c index bdb725551..d7f6640eb 100644 --- a/arch/arm/kernel/time.c +++ b/arch/arm/kernel/time.c @@ -13,7 +13,6 @@ * "A Kernel Model for Precision Timekeeping" by Dave Mills */ #include <linux/config.h> -#include <linux/errno.h> #include <linux/sched.h> #include <linux/kernel.h> #include <linux/interrupt.h> diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index 1d692dd35..188f89722 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c @@ -17,16 +17,18 @@ #include <linux/sched.h> #include <linux/mm.h> #include <linux/spinlock.h> +#include <linux/ptrace.h> #include <linux/init.h> -#include <asm/system.h> -#include <asm/uaccess.h> -#include <asm/io.h> #include <asm/atomic.h> +#include <asm/io.h> #include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/uaccess.h> + +#include "ptrace.h" extern void c_backtrace (unsigned long fp, int pmode); -extern int ptrace_cancel_bpt (struct task_struct *); char *processor_modes[]= { "USER_26", "FIQ_26" , "IRQ_26" , "SVC_26" , "UK4_26" , "UK5_26" , "UK6_26" , "UK7_26" , @@ -46,8 +48,6 @@ static inline void console_verbose(void) console_loglevel = 15; } -int kstack_depth_to_print = 200; - /* * Stack pointers should always be within the kernels view of * physical memory. If it is not there, then we can't dump @@ -199,37 +199,48 @@ void die_if_kernel(const char *str, struct pt_regs *regs, int err) die(str, regs, err); } -void bad_user_access_alignment(const void *ptr) -{ - printk(KERN_ERR "bad user access alignment: ptr = %p, pc = %p\n", ptr, - __builtin_return_address(0)); - current->thread.error_code = 0; - current->thread.trap_no = 11; - force_sig(SIGBUS, current); -/* die_if_kernel("Oops - bad user access alignment", regs, mode);*/ -} - asmlinkage void do_undefinstr(int address, struct pt_regs *regs, int mode) { + unsigned long addr = instruction_pointer(regs); + siginfo_t info; + #ifdef CONFIG_DEBUG_USER printk(KERN_INFO "%s (%d): undefined instruction: pc=%08lx\n", - current->comm, current->pid, instruction_pointer(regs)); + current->comm, current->pid, addr); #endif + current->thread.error_code = 0; current->thread.trap_no = 6; - force_sig(SIGILL, current); + + info.si_signo = SIGILL; + info.si_errno = 0; + info.si_code = ILL_ILLOPC; + info.si_addr = (void *)addr; + + force_sig_info(SIGILL, &info, current); + die_if_kernel("Oops - undefined instruction", regs, mode); } asmlinkage void do_excpt(int address, struct pt_regs *regs, int mode) { + siginfo_t info; + #ifdef CONFIG_DEBUG_USER printk(KERN_INFO "%s (%d): address exception: pc=%08lx\n", current->comm, current->pid, instruction_pointer(regs)); #endif + current->thread.error_code = 0; current->thread.trap_no = 11; - force_sig(SIGBUS, current); + + info.si_signo = SIGBUS; + info.si_errno = 0; + info.si_code = BUS_ADRERR; + info.si_addr = (void *)address; + + force_sig_info(SIGBUS, &info, current); + die_if_kernel("Oops - address exception", regs, mode); } @@ -269,32 +280,38 @@ asmlinkage void bad_mode(struct pt_regs *regs, int reason, int proc_mode) } /* - * 'math_state_restore()' saves the current math information in the - * old math state array, and gets the new ones from the current task. - * - * We no longer save/restore the math state on every context switch - * any more. We only do this now if it actually gets used. - */ -asmlinkage void math_state_restore (void) -{ - current->used_math = 1; -} - -/* * Handle some more esoteric system calls */ -asmlinkage int arm_syscall (int no, struct pt_regs *regs) +asmlinkage int arm_syscall(int no, struct pt_regs *regs) { + siginfo_t info; + switch (no) { case 0: /* branch through 0 */ - force_sig(SIGSEGV, current); + info.si_signo = SIGSEGV; + info.si_errno = 0; + info.si_code = SEGV_MAPERR; + info.si_addr = NULL; + + force_sig_info(SIGSEGV, &info, current); + die_if_kernel("branch through zero", regs, 0); break; - case 1: /* SWI_BREAK_POINT */ - regs->ARM_pc -= 4; /* Decrement PC by one instruction */ - ptrace_cancel_bpt(current); - force_sig(SIGTRAP, current); + case 1: /* SWI BREAK_POINT */ + /* + * The PC is always left pointing at the next + * instruction. Fix this. + */ + regs->ARM_pc -= 4; + __ptrace_cancel_bpt(current); + + info.si_signo = SIGTRAP; + info.si_errno = 0; + info.si_code = TRAP_BRKPT; + info.si_addr = (void *)instruction_pointer(regs); + + force_sig_info(SIGTRAP, &info, current); return regs->ARM_r0; case 2: /* sys_cacheflush */ @@ -350,29 +367,24 @@ asmlinkage void deferred(int n, struct pt_regs *regs) die_if_kernel("Oops", regs, n); } -asmlinkage void arm_malalignedptr(const char *str, void *pc, volatile void *ptr) -{ - printk("Mal-aligned pointer in %s: %p (PC=%p)\n", str, ptr, pc); -} - -asmlinkage void arm_invalidptr(const char *function, int size) +void __bad_xchg(volatile void *ptr, int size) { - printk("Invalid pointer size in %s (pc=%p) size %d\n", - function, __builtin_return_address(0), size); + printk("xchg: bad data size: pc 0x%p, ptr 0x%p, size %d\n", + __builtin_return_address(0), ptr, size); + BUG(); } /* - * A data abort trap was taken, but the instruction was not an instruction - * which should cause the trap to be taken. Try to abort it. Note that - * the while(1) is there because we cannot currently handle returning from - * this function. + * A data abort trap was taken, but we did not handle the instruction. + * Try to abort the user program, or panic if it was the kernel. */ asmlinkage void baddataabort(int code, unsigned long instr, struct pt_regs *regs) { unsigned long addr = instruction_pointer(regs); + siginfo_t info; -#ifdef CONFIG_DEBUG_ERRORS +#ifdef CONFIG_DEBUG_USER dump_instr(addr, 1); { pgd_t *pgd; @@ -389,16 +401,22 @@ baddataabort(int code, unsigned long instr, struct pt_regs *regs) printk ("\n"); } #endif - force_sig(SIGILL, current); + + info.si_signo = SIGILL; + info.si_errno = 0; + info.si_code = ILL_ILLOPC; + info.si_addr = (void *)addr; + + force_sig_info(SIGILL, &info, current); die_if_kernel("unknown data abort code", regs, instr); - while (1); } void __bug(const char *file, int line, void *data) { - printk(KERN_CRIT"kernel BUG at %s:%d!\n", file, line); + printk(KERN_CRIT"kernel BUG at %s:%d!", file, line); if (data) - printk(KERN_CRIT"extra data = %p\n", data); + printk(KERN_CRIT" - extra data = %p", data); + printk("\n"); BUG(); } diff --git a/arch/arm/mm/fault-common.c b/arch/arm/mm/fault-common.c index 385937708..519f1965a 100644 --- a/arch/arm/mm/fault-common.c +++ b/arch/arm/mm/fault-common.c @@ -187,6 +187,7 @@ static int do_page_fault(unsigned long addr, int mode, struct pt_regs *regs) tsk->thread.error_code = mode; tsk->thread.trap_no = 14; si.si_signo = SIGSEGV; + si.si_errno = 0; si.si_code = fault == -1 ? SEGV_ACCERR : SEGV_MAPERR; si.si_addr = (void *)addr; force_sig_info(SIGSEGV, &si, tsk); diff --git a/arch/arm/mm/mm-armv.c b/arch/arm/mm/mm-armv.c index 34bd51366..1edbc35fd 100644 --- a/arch/arm/mm/mm-armv.c +++ b/arch/arm/mm/mm-armv.c @@ -410,9 +410,24 @@ void __init pagetable_init(struct meminfo *mi) flush_cache_all(); } -/* - * The mem_map array can get very big. Free the unused area of the memory map. - */ +static inline void free_memmap(unsigned long start, unsigned long end) +{ + unsigned long pg, pgend; + + start = __phys_to_virt(start); + end = __phys_to_virt(end); + + pg = PAGE_ALIGN((unsigned long)(mem_map + MAP_NR(start))); + pgend = ((unsigned long)(mem_map + MAP_NR(end))) & PAGE_MASK; + + start = __virt_to_phys(pg); + end = __virt_to_phys(pgend); + /* + * The mem_map is always stored in node 0 + */ + free_bootmem_node(0, start, end - start); +} + static inline void free_unused_memmap_node(int node, struct meminfo *mi) { unsigned long bank_start, prev_bank_end = 0; @@ -434,14 +449,17 @@ static inline void free_unused_memmap_node(int node, struct meminfo *mi) * between the current bank and the previous, free it. */ if (prev_bank_end && prev_bank_end != bank_start) - free_bootmem_node(node, prev_bank_end, - bank_start - prev_bank_end); + free_memmap(prev_bank_end, bank_start); prev_bank_end = PAGE_ALIGN(mi->bank[i].start + mi->bank[i].size); } } +/* + * The mem_map array can get very big. Free + * the unused area of the memory map. + */ void __init create_memmap_holes(struct meminfo *mi) { int node; diff --git a/arch/arm/mm/proc-arm2,3.S b/arch/arm/mm/proc-arm2,3.S index 36a9d8b28..6dd48c919 100644 --- a/arch/arm/mm/proc-arm2,3.S +++ b/arch/arm/mm/proc-arm2,3.S @@ -286,7 +286,6 @@ SYMBOL_NAME(arm2_processor_functions): .word _arm2_proc_fin .word _arm2_set_pgd .word _arm2_xchg_1 - .word SYMBOL_NAME(abort) .word _arm2_xchg_4 cpu_arm2_info: @@ -300,7 +299,6 @@ SYMBOL_NAME(arm250_processor_functions): .word _arm2_proc_fin .word _arm2_set_pgd .word _arm3_xchg_1 - .word SYMBOL_NAME(abort) .word _arm3_xchg_4 cpu_arm250_info: @@ -314,7 +312,6 @@ SYMBOL_NAME(arm3_processor_functions): .word _arm3_proc_fin .word _arm3_set_pgd .word _arm3_xchg_1 - .word SYMBOL_NAME(abort) .word _arm3_xchg_4 cpu_arm3_info: diff --git a/arch/arm/mm/proc-arm6,7.S b/arch/arm/mm/proc-arm6,7.S index 5d7605b85..b18d69d98 100644 --- a/arch/arm/mm/proc-arm6,7.S +++ b/arch/arm/mm/proc-arm6,7.S @@ -158,8 +158,8 @@ Ldata_unknown: @ Part of jumptable mov r0, r2 mov r1, r4 mov r2, r3 - b baddataabort - + bl baddataabort + b ret_from_sys_call Ldata_lateldrpreconst: tst r4, #1 << 21 @ check writeback bit diff --git a/arch/arm/mm/proc-arm720.S b/arch/arm/mm/proc-arm720.S index 738ff9a43..d49196625 100644 --- a/arch/arm/mm/proc-arm720.S +++ b/arch/arm/mm/proc-arm720.S @@ -146,8 +146,8 @@ Ldata_unknown: @ Part of jumptable mov r0, r2 mov r1, r4 mov r2, r3 - b baddataabort - + bl baddataabort + b ret_from_sys_call Ldata_lateldrpreconst: tst r4, #1 << 21 @ check writeback bit diff --git a/arch/i386/kernel/apic.c b/arch/i386/kernel/apic.c index 6a1a8aa40..100a5ba5a 100644 --- a/arch/i386/kernel/apic.c +++ b/arch/i386/kernel/apic.c @@ -597,7 +597,9 @@ static inline void handle_smp_time (int user, int cpu) } kstat.cpu_system += system; kstat.per_cpu_system[cpu] += system; - + } else if (local_bh_count(cpu) || local_irq_count(cpu) > 1) { + kstat.cpu_system += system; + kstat.per_cpu_system[cpu] += system; } irq_exit(cpu, 0); } diff --git a/arch/i386/kernel/pci-irq.c b/arch/i386/kernel/pci-irq.c index 933d6c3c9..b98bf4748 100644 --- a/arch/i386/kernel/pci-irq.c +++ b/arch/i386/kernel/pci-irq.c @@ -464,7 +464,7 @@ void __init pcibios_fixup_irqs(void) } } -void __init pcibios_penalize_isa_irq(int irq) +void pcibios_penalize_isa_irq(int irq) { /* * If any ISAPnP device reports an IRQ in its list of possible diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index e0425f411..771caa7e7 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -43,6 +43,11 @@ * Pentium III FXSR, SSE support * General FPU state handling cleanups * Gareth Hughes <gareth@valinux.com>, May 2000 + * + * Added proper Cascades CPU and L2 cache detection for Cascades + * and 8-way type cache happy bunch from Intel:^) + * Dragan Stancevic <visitor@valinux.com>, May 2000 + * */ /* diff --git a/arch/mips/kernel/irixelf.c b/arch/mips/kernel/irixelf.c index 8e0e59907..24a18824e 100644 --- a/arch/mips/kernel/irixelf.c +++ b/arch/mips/kernel/irixelf.c @@ -778,9 +778,8 @@ out: return retval; out_free_dentry: - lock_kernel(); + allow_write_access(interpreter); fput(interpreter); - unlock_kernel(); out_free_interp: if (elf_interpreter) kfree(elf_interpreter); diff --git a/arch/sparc/config.in b/arch/sparc/config.in index 0f4bb555f..3ed5915ba 100644 --- a/arch/sparc/config.in +++ b/arch/sparc/config.in @@ -1,6 +1,6 @@ -# $Id: config.in,v 1.96 2000/06/20 01:10:00 anton Exp $ +# $Id: config.in,v 1.94 2000/06/04 22:23:10 anton Exp $ # For a description of the syntax of this configuration file, -# see the Configure script. +# see Documentation/kbuild/config-language.txt. # mainmenu_name "Linux/SPARC Kernel Configuration" diff --git a/arch/sparc/kernel/sys_sparc.c b/arch/sparc/kernel/sys_sparc.c index 05ed9d932..82d3027aa 100644 --- a/arch/sparc/kernel/sys_sparc.c +++ b/arch/sparc/kernel/sys_sparc.c @@ -215,7 +215,6 @@ static unsigned long do_mmap2(unsigned long addr, unsigned long len, goto out; } - down(¤t->mm->mmap_sem); lock_kernel(); retval = -EINVAL; len = PAGE_ALIGN(len); @@ -230,11 +229,12 @@ static unsigned long do_mmap2(unsigned long addr, unsigned long len, goto out_putf; flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + down(¤t->mm->mmap_sem); retval = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); + up(¤t->mm->mmap_sem); out_putf: unlock_kernel(); - up(¤t->mm->mmap_sem); if (file) fput(file); out: diff --git a/arch/sparc/kernel/sys_sunos.c b/arch/sparc/kernel/sys_sunos.c index 1c3dfe6e2..f64563cab 100644 --- a/arch/sparc/kernel/sys_sunos.c +++ b/arch/sparc/kernel/sys_sunos.c @@ -68,7 +68,6 @@ asmlinkage unsigned long sunos_mmap(unsigned long addr, unsigned long len, struct file * file = NULL; unsigned long retval, ret_type; - down(¤t->mm->mmap_sem); lock_kernel(); if(flags & MAP_NORESERVE) { static int cnt; @@ -118,7 +117,9 @@ asmlinkage unsigned long sunos_mmap(unsigned long addr, unsigned long len, } flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + down(¤t->mm->mmap_sem); retval = do_mmap(file, addr, len, prot, flags, off); + up(¤t->mm->mmap_sem); if(!ret_type) retval = ((retval < PAGE_OFFSET) ? 0 : retval); @@ -127,7 +128,6 @@ out_putf: fput(file); out: unlock_kernel(); - up(¤t->mm->mmap_sem); return retval; } diff --git a/arch/sparc64/kernel/sys_sparc.c b/arch/sparc64/kernel/sys_sparc.c index 9e51aadad..2aff3033a 100644 --- a/arch/sparc64/kernel/sys_sparc.c +++ b/arch/sparc64/kernel/sys_sparc.c @@ -227,7 +227,6 @@ asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len, len = PAGE_ALIGN(len); retval = -EINVAL; - down(¤t->mm->mmap_sem); lock_kernel(); if (current->thread.flags & SPARC_FLAG_32BIT) { @@ -241,11 +240,12 @@ asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len, goto out_putf; } + down(¤t->mm->mmap_sem); retval = do_mmap(file, addr, len, prot, flags, off); + up(¤t->mm->mmap_sem); out_putf: unlock_kernel(); - up(¤t->mm->mmap_sem); if (file) fput(file); out: diff --git a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc64/kernel/sys_sparc32.c index 26c5faecd..e27892de3 100644 --- a/arch/sparc64/kernel/sys_sparc32.c +++ b/arch/sparc64/kernel/sys_sparc32.c @@ -3029,9 +3029,7 @@ do_execve32(char * filename, u32 * argv, u32 * envp, struct pt_regs * regs) bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *); memset(bprm.page, 0, MAX_ARG_PAGES * sizeof(bprm.page[0])); - lock_kernel(); file = open_exec(filename); - unlock_kernel(); retval = PTR_ERR(file); if (IS_ERR(file)) @@ -3043,10 +3041,12 @@ do_execve32(char * filename, u32 * argv, u32 * envp, struct pt_regs * regs) bprm.loader = 0; bprm.exec = 0; if ((bprm.argc = count32(argv)) < 0) { + allow_write_access(file); fput(file); return bprm.argc; } if ((bprm.envc = count32(envp)) < 0) { + allow_write_access(file); fput(file); return bprm.envc; } @@ -3075,6 +3075,7 @@ do_execve32(char * filename, u32 * argv, u32 * envp, struct pt_regs * regs) out: /* Something went wrong, return the inode and free the argument pages*/ + allow_write_access(bprm.file); if (bprm.file) fput(bprm.file); diff --git a/arch/sparc64/kernel/sys_sunos32.c b/arch/sparc64/kernel/sys_sunos32.c index b13846fe9..70adfe21a 100644 --- a/arch/sparc64/kernel/sys_sunos32.c +++ b/arch/sparc64/kernel/sys_sunos32.c @@ -68,7 +68,6 @@ asmlinkage u32 sunos_mmap(u32 addr, u32 len, u32 prot, u32 flags, u32 fd, u32 of struct file *file = NULL; unsigned long retval, ret_type; - down(¤t->mm->mmap_sem); lock_kernel(); if(flags & MAP_NORESERVE) { static int cnt; @@ -102,10 +101,12 @@ asmlinkage u32 sunos_mmap(u32 addr, u32 len, u32 prot, u32 flags, u32 fd, u32 of flags &= ~_MAP_NEW; flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + down(¤t->mm->mmap_sem); retval = do_mmap(file, (unsigned long) addr, (unsigned long) len, (unsigned long) prot, (unsigned long) flags, (unsigned long) off); + up(¤t->mm->mmap_sem); if(!ret_type) retval = ((retval < 0xf0000000) ? 0 : retval); out_putf: @@ -113,7 +114,6 @@ out_putf: fput(file); out: unlock_kernel(); - up(¤t->mm->mmap_sem); return (u32) retval; } diff --git a/drivers/acorn/char/Makefile b/drivers/acorn/char/Makefile index 316af0d4e..d4658724a 100644 --- a/drivers/acorn/char/Makefile +++ b/drivers/acorn/char/Makefile @@ -33,6 +33,7 @@ obj-rpc := keyb_ps2.o obj-$(CONFIG_RPCMOUSE) += mouse_rpc.o obj-$(CONFIG_ATOMWIDE_SERIAL) += serial-atomwide.o obj-$(CONFIG_DUALSP_SERIAL) += serial-dualsp.o +obj-$(CONFIG_ARCH_ACORN) += defkeymap-acorn.o # Do the i2c and rtc last obj-y += $(obj-$(MACHINE)) i2c.o pcf8583.o diff --git a/drivers/acorn/char/defkeymap-acorn.c b/drivers/acorn/char/defkeymap-acorn.c new file mode 100644 index 000000000..4974cd2b6 --- /dev/null +++ b/drivers/acorn/char/defkeymap-acorn.c @@ -0,0 +1,358 @@ +/* + * linux/arch/arm/drivers/char/defkeymap.c + * + * Copyright (C) 1995, 1996 Russell King + */ + +#include <linux/types.h> +#include <linux/keyboard.h> +#include <linux/kd.h> + +/* Normal (maps 1:1 with no processing) */ +#define KTn 0xF0 +/* Function keys */ +#define KTf 0xF1 +/* Special (Performs special house-keeping funcs) */ +#define KTs 0xF2 +#define KIGNORE K(KTs, 0) /* Ignore */ +#define KENTER K(KTs, 1) /* Enter */ +#define KREGS K(KTs, 2) /* Regs */ +#define KMEM K(KTs, 3) /* Mem */ +#define KSTAT K(KTs, 4) /* State */ +#define KINTR K(KTs, 5) /* Intr */ +#define Ksl 6 /* Last console */ +#define KCAPSLK K(KTs, 7) /* Caps lock */ +#define KNUMLK K(KTs, 8) /* Num-lock */ +#define KSCRLLK K(KTs, 9) /* Scroll-lock */ +#define KSCRLFOR K(KTs,10) /* Scroll forward */ +#define KSCRLBAK K(KTs,11) /* Scroll back */ +#define KREBOOT K(KTs,12) /* Reboot */ +#define KCAPSON K(KTs,13) /* Caps on */ +#define KCOMPOSE K(KTs,14) /* Compose */ +#define KSAK K(KTs,15) /* SAK */ +#define CONS_DEC K(KTs,16) /* Dec console */ +#define CONS_INC K(KTs,17) /* Incr console */ +#define KFLOPPY K(KTs,18) /* Floppy */ +/* Key pad (0-9 = digits, 10=+, 11=-, 12=*, 13=/, 14=enter, 16=., 17=# */ +#define KTp 0xF3 +#define KPAD_0 K(KTp, 0 ) +#define KPAD_1 K(KTp, 1 ) +#define KPAD_2 K(KTp, 2 ) +#define KPAD_3 K(KTp, 3 ) +#define KPAD_4 K(KTp, 4 ) +#define KPAD_5 K(KTp, 5 ) +#define KPAD_6 K(KTp, 6 ) +#define KPAD_7 K(KTp, 7 ) +#define KPAD_8 K(KTp, 8 ) +#define KPAD_9 K(KTp, 9 ) +#define KPAD_PL K(KTp,10 ) +#define KPAD_MI K(KTp,11 ) +#define KPAD_ML K(KTp,12 ) +#define KPAD_DV K(KTp,13 ) +#define KPAD_EN K(KTp,14 ) +#define KPAD_DT K(KTp,16 ) +#define KPAD_HS K(KTp,20 ) +/* Console switching */ +#define KCn 0xF5 +/* Cursor */ +#define KTc 0xF6 +#define Kcd 0 /* Cursor down */ +#define Kcl 1 /* Cursor left */ +#define Kcr 2 /* Cursor right */ +#define Kcu 3 /* Cursor up */ +/* Shift/alt modifiers etc */ +#define KMd 0xF7 +#define KSHIFT K(KMd, 0 ) +#define KALTGR K(KMd, 1 ) +#define KCTRL K(KMd, 2 ) +#define KALT K(KMd, 3 ) +/* Meta */ +#define KMt 0xF8 +#define KAs 0xF9 +#define KPADA_0 K(KAs, 0 ) +#define KPADA_1 K(KAs, 1 ) +#define KPADA_2 K(KAs, 2 ) +#define KPADA_3 K(KAs, 3 ) +#define KPADA_4 K(KAs, 4 ) +#define KPADA_5 K(KAs, 5 ) +#define KPADA_6 K(KAs, 6 ) +#define KPADA_7 K(KAs, 7 ) +#define KPADA_8 K(KAs, 8 ) +#define KPADA_9 K(KAs, 9 ) +#define KPADB_0 K(KAs,10 ) +#define KPADB_1 K(KAs,11 ) +#define KPADB_2 K(KAs,12 ) +#define KPADB_3 K(KAs,13 ) +#define KPADB_4 K(KAs,14 ) +#define KPADB_5 K(KAs,15 ) +#define KPADB_6 K(KAs,16 ) +#define KPADB_7 K(KAs,17 ) +#define KPADB_8 K(KAs,18 ) +#define KPADB_9 K(KAs,19 ) +/* Locking keys */ +#define KLk 0xFA +/* Letters */ +#define KTl 0xFB + +u_short plain_map[NR_KEYS]= +{ + K(KTn, 27),K(KTf, 0),K(KTf, 1),K(KTf, 2 ),K(KTf, 3),K(KTf, 4),K(KTf, 5 ),K(KTf, 6), + K(KTf, 7),K(KTf, 8),K(KTf, 9),K(KTf, 10 ),K(KTf, 11),KIGNORE ,KSCRLLK ,KINTR , + K(KTn,'`'),K(KTn,'1'),K(KTn,'2'),K(KTn,'3' ),K(KTn,'4'),K(KTn,'5'),K(KTn,'6' ),K(KTn,'7'), + K(KTn,'8'),K(KTn,'9'),K(KTn,'0'),K(KTn,'-' ),K(KTn,'='),K(KTn,'£'),K(KTn,127 ),K(KTf,21 ), + K(KTf,20 ),K(KTf,24 ),KNUMLK ,KPAD_DV ,KPAD_ML ,KPAD_HS ,K(KTn, 9 ),K(KTl,'q'), + K(KTl,'w'),K(KTl,'e'),K(KTl,'r'),K(KTl,'t' ),K(KTl,'y'),K(KTl,'u'),K(KTl,'i' ),K(KTl,'o'), + K(KTl,'p'),K(KTn,'['),K(KTn,']'),K(KTn,'\\'),K(KTf,22 ),K(KTf,23 ),K(KTf,25 ),KPAD_7 , + KPAD_8 ,KPAD_9 ,KPAD_MI ,KCTRL ,K(KTl,'a'),K(KTl,'s'),K(KTl,'d' ),K(KTl,'f'), + K(KTl,'g'),K(KTl,'h'),K(KTl,'j'),K(KTl,'k' ),K(KTl,'l'),K(KTn,';'),K(KTn,'\''),KENTER , + KPAD_4 ,KPAD_5 ,KPAD_6 ,KPAD_PL ,KSHIFT ,KIGNORE ,K(KTl,'z' ),K(KTl,'x'), + K(KTl,'c'),K(KTl,'v'),K(KTl,'b'),K(KTl,'n' ),K(KTl,'m'),K(KTn,','),K(KTn,'.' ),K(KTn,'/'), + KSHIFT ,K(KTc,Kcu),KPAD_1 ,KPAD_2 ,KPAD_3 ,KCAPSLK ,KALT ,K(KTn,' '), + KALTGR ,KCTRL ,K(KTc,Kcl),K(KTc,Kcd ),K(KTc,Kcr),KPAD_0 ,KPAD_DT ,KPAD_EN , + KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , + KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , + KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , +}; + +u_short shift_map[NR_KEYS]= +{ + K(KTn, 27),K(KTf, 10),K(KTf, 11),K(KTf, 12 ),K(KTf, 13),K(KTf, 14),K(KTf, 15 ),K(KTf, 16), + K(KTf, 17),K(KTf, 18),K(KTf, 19),K(KTf, 20 ),K(KTf, 21),KIGNORE ,KMEM ,KINTR , + K(KTn,'~'),K(KTn,'!'),K(KTn,'@'),K(KTn,'#' ),K(KTn,'$'),K(KTn,'%'),K(KTn,'^' ),K(KTn,'&'), + K(KTn,'*'),K(KTn,'('),K(KTn,')'),K(KTn,'_' ),K(KTn,'+'),K(KTn,'¤'),K(KTn,127 ),K(KTf,21 ), + K(KTf,20 ),KSCRLBAK ,KNUMLK ,KPAD_DV ,KPAD_ML ,KPAD_HS ,K(KTn, 9 ),K(KTl,'Q'), + K(KTl,'W'),K(KTl,'E'),K(KTl,'R'),K(KTl,'T' ),K(KTl,'Y'),K(KTl,'U'),K(KTl,'I' ),K(KTl,'O'), + K(KTl,'P'),K(KTn,'{'),K(KTn,'}'),K(KTn,'|' ),K(KTf,22 ),K(KTf,23 ),KSCRLFOR ,KPAD_7 , + KPAD_8 ,KPAD_9 ,KPAD_MI ,KCTRL ,K(KTl,'A'),K(KTl,'S'),K(KTl,'D' ),K(KTl,'F'), + K(KTl,'G'),K(KTl,'H'),K(KTl,'J'),K(KTl,'K' ),K(KTl,'L'),K(KTn,':'),K(KTn,'"' ),KENTER , + KPAD_4 ,KPAD_5 ,KPAD_6 ,KPAD_PL ,KSHIFT ,KIGNORE ,K(KTl,'Z' ),K(KTl,'X'), + K(KTl,'C'),K(KTl,'V'),K(KTl,'B'),K(KTl,'N' ),K(KTl,'M'),K(KTn,'<'),K(KTn,'>' ),K(KTn,'?'), + KSHIFT ,K(KTc,Kcu),KPAD_1 ,KPAD_2 ,KPAD_3 ,KCAPSLK ,KALT ,K(KTn,' '), + KALTGR ,KCTRL ,K(KTc,Kcl),K(KTc,Kcd ),K(KTc,Kcr),KPAD_0 ,KPAD_DT ,KPAD_EN , + KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , + KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , + KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , +}; + +u_short altgr_map[NR_KEYS]= +{ + KIGNORE ,K(KCn,12 ),K(KCn,13 ),K(KCn,14 ),K(KCn,15 ),K(KCn,16 ),K(KCn,17 ),K(KCn, 18), + K(KCn, 19),K(KCn,20 ),K(KCn,21 ),K(KCn,22 ),K(KCn,23 ),KIGNORE ,KREGS ,KINTR , + KIGNORE ,KIGNORE ,K(KTn,'@'),KIGNORE ,K(KTn,'$'),KIGNORE ,KIGNORE ,K(KTn,'{'), + K(KTn,'['),K(KTn,']'),K(KTn,'}'),K(KTn,'\\'),KIGNORE ,KIGNORE ,KIGNORE ,K(KTf,21 ), + K(KTf,20 ),K(KTf,24 ),KNUMLK ,KPAD_DV ,KPAD_ML ,KPAD_HS ,KIGNORE ,K(KTl,'q'), + K(KTl,'w'),K(KTl,'e'),K(KTl,'r'),K(KTl,'t' ),K(KTl,'y'),K(KTl,'u'),K(KTl,'i' ),K(KTl,'o'), + K(KTl,'p'),KIGNORE ,K(KTn,'~'),KIGNORE ,K(KTf,22 ),K(KTf,23 ),K(KTf,25 ),KPADB_7 , + KPADB_8 ,KPADB_9 ,KPAD_MI ,KCTRL ,K(KAs,20 ),K(KTl,'s'),K(KAs,23 ),K(KAs,25 ), + K(KTl,'g'),K(KTl,'h'),K(KTl,'j'),K(KTl,'k' ),K(KTl,'l'),KIGNORE ,KIGNORE ,KENTER , + KPADB_4 ,KPADB_5 ,KPADB_6 ,KPAD_PL ,KSHIFT ,KIGNORE ,K(KTl,'z' ),K(KTl,'x'), + K(KAs,22 ),K(KTl,'v'),K(KTl,21 ),K(KTl,'n' ),K(KTl,'m'),KIGNORE ,KIGNORE ,KIGNORE , + KSHIFT ,K(KTc,Kcu),KPADB_1 ,KPADB_2 ,KPADB_3 ,KCAPSLK ,KALT ,KIGNORE , + KALTGR ,KCTRL ,K(KTc,Kcl),K(KTc,Kcd ),K(KTc,Kcr),KPADB_0 ,KPAD_DT ,KPAD_EN , + KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , + KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , + KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , +}; + +u_short ctrl_map[NR_KEYS]= +{ + KIGNORE ,K(KTf, 0),K(KTf, 1),K(KTf, 2 ),K(KTf, 3),K(KTf, 4),K(KTf, 5 ),K(KTf, 6), + K(KTf, 7),K(KTf, 8),K(KTf, 9),K(KTf, 10 ),K(KTf, 11),KIGNORE ,KSTAT ,KINTR , + KIGNORE ,K(KTn, 1 ),K(KTn, 2 ),K(KTn, 3 ),K(KTn, 4 ),K(KTn, 5 ),K(KTn, 6 ),K(KTn, 7 ), + K(KTn, 8 ),K(KTn, 9 ),K(KTn, 0 ),K(KTn,31 ),KIGNORE ,KIGNORE ,K(KTn, 8 ),K(KTf,21 ), + K(KTf,20 ),K(KTf,24 ),KNUMLK ,KPAD_DV ,KPAD_ML ,KPAD_HS ,KIGNORE ,K(KTn,17 ), + K(KTn,23 ),K(KTn, 5 ),K(KTn,18 ),K(KTn,20 ),K(KTn,25 ),K(KTn,21 ),K(KTn, 9 ),K(KTn,15 ), + K(KTn,16 ),K(KTn,27 ),K(KTn,29 ),K(KTn,28 ),K(KTf,22 ),K(KTf,23 ),K(KTf,25 ),KPAD_7 , + KPAD_8 ,KPAD_9 ,KPAD_MI ,KCTRL ,K(KTn, 1 ),K(KTn,19 ),K(KTn, 4 ),K(KTn, 6 ), + K(KTn, 7 ),K(KTn, 8 ),K(KTn,10 ),K(KTn,11 ),K(KTn,12 ),KIGNORE ,K(KTn, 7 ),KENTER , + KPAD_4 ,KPAD_5 ,KPAD_6 ,KPAD_PL ,KSHIFT ,KIGNORE ,K(KTn,26 ),K(KTn,24 ), + K(KTn, 3 ),K(KTn,22 ),K(KTn, 2 ),K(KTn,14 ),K(KTn,13 ),KIGNORE ,KCOMPOSE ,K(KTn,127), + KSHIFT ,K(KTc,Kcu),KPAD_1 ,KPAD_2 ,KPAD_3 ,KCAPSLK ,KALT ,K(KTn, 0 ), + KALTGR ,KCTRL ,K(KTc,Kcl),K(KTc,Kcd ),K(KTc,Kcr),KPAD_0 ,KPAD_DT ,KPAD_EN , + KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , + KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , + KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , +}; + +u_short shift_ctrl_map[NR_KEYS]= +{ + KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , + KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KFLOPPY ,KINTR , + KIGNORE ,KIGNORE ,K(KTn, 0 ),KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , + KIGNORE ,KIGNORE ,KIGNORE ,K(KTn,31 ),KIGNORE ,KIGNORE ,KIGNORE ,K(KTf,21 ), + K(KTf,20 ),K(KTf,24 ),KNUMLK ,KPAD_DV ,KPAD_ML ,KPAD_HS ,KIGNORE ,K(KTn,17 ), + K(KTn,23 ),K(KTn, 5 ),K(KTn,18 ),K(KTn,20 ),K(KTn,25 ),K(KTn,21 ),K(KTn, 9 ),K(KTn,15 ), + K(KTn,16 ),KIGNORE ,KIGNORE ,KIGNORE ,K(KTf,22 ),K(KTf,23 ),K(KTf,25 ),KPAD_7 , + KPAD_8 ,KPAD_9 ,KPAD_MI ,KCTRL ,K(KTn, 1 ),K(KTn,19 ),K(KTn, 4 ),K(KTn, 6 ), + K(KTn, 7 ),K(KTn, 8 ),K(KTn,10 ),K(KTn,11 ),K(KTn,12 ),KIGNORE ,K(KTn, 7 ),KENTER , + KPAD_4 ,KPAD_5 ,KPAD_6 ,KPAD_PL ,KSHIFT ,KIGNORE ,K(KTn,26 ),K(KTn,24 ), + K(KTn, 3 ),K(KTn,22 ),K(KTn, 2 ),K(KTn,14 ),K(KTn,13 ),KIGNORE ,KIGNORE ,KIGNORE , + KSHIFT ,K(KTc,Kcu),KPAD_1 ,KPAD_2 ,KPAD_3 ,KCAPSLK ,KALT ,K(KTn, 0 ), + KALTGR ,KCTRL ,K(KTc,Kcl),K(KTc,Kcd ),K(KTc,Kcr),KPAD_0 ,KPAD_DT ,KPAD_EN , + KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , + KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , + KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , +}; + +u_short alt_map[NR_KEYS]= +{ + K(KMt,27 ),K(KCn, 0 ),K(KCn, 1 ),K(KCn, 2 ),K(KCn, 3 ),K(KCn, 4 ),K(KCn, 5 ),K(KCn, 6 ), + K(KCn, 7 ),K(KCn, 8 ),K(KCn, 9 ),K(KCn,10 ),K(KCn,11 ),KIGNORE ,KSCRLLK ,KINTR , + K(KMt,'`'),K(KMt,'1'),K(KMt,'2'),K(KMt,'3' ),K(KMt,'4'),K(KMt,'5'),K(KMt,'6' ),K(KMt,'7'), + K(KMt,'8'),K(KMt,'9'),K(KMt,'0'),K(KMt,'-' ),K(KMt,'='),K(KMt,'£'),K(KMt,127 ),K(KTf,21 ), + K(KTf,20 ),K(KTf,24 ),KNUMLK ,KPAD_DV ,KPAD_ML ,KPAD_HS ,K(KMt, 9 ),K(KMt,'q'), + K(KMt,'w'),K(KMt,'e'),K(KMt,'r'),K(KMt,'t' ),K(KMt,'y'),K(KMt,'u'),K(KMt,'i' ),K(KMt,'o'), + K(KMt,'p'),K(KMt,'['),K(KMt,']'),K(KMt,'\\'),K(KTf,22 ),K(KTf,23 ),K(KTf,25 ),KPADA_7 , + KPADA_8 ,KPADA_9 ,KPAD_MI ,KCTRL ,K(KMt,'a'),K(KMt,'s'),K(KMt,'d' ),K(KMt,'f'), + K(KMt,'g'),K(KMt,'h'),K(KMt,'j'),K(KMt,'k' ),K(KMt,'l'),K(KMt,';'),K(KMt,'\''),K(KMt,13 ), + KPADA_4 ,KPADA_5 ,KPADA_6 ,KPAD_PL ,KSHIFT ,KIGNORE ,K(KMt,'z' ),K(KMt,'x'), + K(KMt,'c'),K(KMt,'v'),K(KMt,'b'),K(KMt,'n' ),K(KMt,'m'),K(KMt,','),K(KMt,'.' ),KIGNORE , + KSHIFT ,K(KTc,Kcu),KPADA_1 ,KPADA_2 ,KPADA_3 ,KCAPSLK ,KALT ,K(KMt,' '), + KALTGR ,KCTRL ,CONS_DEC ,K(KTc,Kcd ),CONS_INC ,KPADA_0 ,KPAD_DT ,KPAD_EN , + KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , + KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , + KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , +}; + +u_short ctrl_alt_map[NR_KEYS]= +{ + KIGNORE ,K(KCn, 0 ),K(KCn, 1 ),K(KCn, 2 ),K(KCn, 3 ),K(KCn, 4 ),K(KCn, 5 ),K(KCn, 6 ), + K(KCn, 7 ),K(KCn, 8 ),K(KCn, 9 ),K(KCn,10 ),K(KCn,11 ),KIGNORE ,KIGNORE ,KINTR , + KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , + KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,K(KTf,21 ), + K(KTf,20 ),K(KTf,24 ),KNUMLK ,KPAD_DV ,KPAD_ML ,KPAD_HS ,KIGNORE ,K(KMt,17 ), + K(KMt,23 ),K(KMt, 5 ),K(KMt,18 ),K(KMt,20 ),K(KMt,25 ),K(KMt,21 ),K(KMt, 9 ),K(KMt,15 ), + K(KMt,16 ),KIGNORE ,KIGNORE ,KIGNORE ,KREBOOT ,K(KTf,23 ),K(KTf,25 ),KPAD_7 , + KPAD_8 ,KPAD_9 ,KPAD_MI ,KCTRL ,K(KMt, 1 ),K(KMt,19 ),K(KMt, 4 ),K(KMt, 6 ), + K(KMt, 7 ),K(KMt, 8 ),K(KMt,10 ),K(KMt,11 ),K(KMt,12 ),KIGNORE ,KIGNORE ,KENTER , + KPAD_4 ,KPAD_5 ,KPAD_6 ,KPAD_PL ,KSHIFT ,KIGNORE ,K(KMt,26 ),K(KMt,24 ), + K(KMt, 3 ),K(KMt,22 ),K(KMt, 2 ),K(KMt,14 ),K(KMt,13 ),KIGNORE ,KIGNORE ,KIGNORE , + KSHIFT ,K(KTc,Kcu),KPAD_1 ,KPAD_2 ,KPAD_3 ,KCAPSLK ,KALT ,KIGNORE , + KALTGR ,KCTRL ,K(KTc,Kcl),K(KTc,Kcd ),K(KTc,Kcr),KPAD_0 ,KREBOOT ,KPAD_EN , + KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , + KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , + KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE , +}; + +ushort *key_maps[MAX_NR_KEYMAPS] = { + plain_map, shift_map, altgr_map, 0, + ctrl_map, shift_ctrl_map, 0, 0, + alt_map, 0, 0, 0, + ctrl_alt_map, 0 +}; + +unsigned int keymap_count = 7; + +/* + * Philosophy: most people do not define more strings, but they who do + * often want quite a lot of string space. So, we statically allocate + * the default and allocate dynamically in chunks of 512 bytes. + */ + +char func_buf[] = { + '\033', '[', '[', 'A', 0, + '\033', '[', '[', 'B', 0, + '\033', '[', '[', 'C', 0, + '\033', '[', '[', 'D', 0, + '\033', '[', '[', 'E', 0, + '\033', '[', '1', '7', '~', 0, + '\033', '[', '1', '8', '~', 0, + '\033', '[', '1', '9', '~', 0, + '\033', '[', '2', '0', '~', 0, + '\033', '[', '2', '1', '~', 0, + '\033', '[', '2', '3', '~', 0, + '\033', '[', '2', '4', '~', 0, + '\033', '[', '2', '5', '~', 0, + '\033', '[', '2', '6', '~', 0, + '\033', '[', '2', '8', '~', 0, + '\033', '[', '2', '9', '~', 0, + '\033', '[', '3', '1', '~', 0, + '\033', '[', '3', '2', '~', 0, + '\033', '[', '3', '3', '~', 0, + '\033', '[', '3', '4', '~', 0, + '\033', '[', '1', '~', 0, + '\033', '[', '2', '~', 0, + '\033', '[', '3', '~', 0, + '\033', '[', '4', '~', 0, + '\033', '[', '5', '~', 0, + '\033', '[', '6', '~', 0, + '\033', '[', 'M', 0, + '\033', '[', 'P', 0, +}; + +char *funcbufptr = func_buf; +int funcbufsize = sizeof(func_buf); +int funcbufleft = 0; /* space left */ + +char *func_table[MAX_NR_FUNC] = { + func_buf + 0, + func_buf + 5, + func_buf + 10, + func_buf + 15, + func_buf + 20, + func_buf + 25, + func_buf + 31, + func_buf + 37, + func_buf + 43, + func_buf + 49, + func_buf + 55, + func_buf + 61, + func_buf + 67, + func_buf + 73, + func_buf + 79, + func_buf + 85, + func_buf + 91, + func_buf + 97, + func_buf + 103, + func_buf + 109, + func_buf + 115, + func_buf + 120, + func_buf + 125, + func_buf + 130, + func_buf + 135, + func_buf + 140, + func_buf + 145, + 0, + 0, + func_buf + 149, + 0, +}; + +struct kbdiacr accent_table[MAX_DIACR] = { + {'`', 'A', '\300'}, {'`', 'a', '\340'}, + {'\'', 'A', '\301'}, {'\'', 'a', '\341'}, + {'^', 'A', '\302'}, {'^', 'a', '\342'}, + {'~', 'A', '\303'}, {'~', 'a', '\343'}, + {'"', 'A', '\304'}, {'"', 'a', '\344'}, + {'O', 'A', '\305'}, {'o', 'a', '\345'}, + {'0', 'A', '\305'}, {'0', 'a', '\345'}, + {'A', 'A', '\305'}, {'a', 'a', '\345'}, + {'A', 'E', '\306'}, {'a', 'e', '\346'}, + {',', 'C', '\307'}, {',', 'c', '\347'}, + {'`', 'E', '\310'}, {'`', 'e', '\350'}, + {'\'', 'E', '\311'}, {'\'', 'e', '\351'}, + {'^', 'E', '\312'}, {'^', 'e', '\352'}, + {'"', 'E', '\313'}, {'"', 'e', '\353'}, + {'`', 'I', '\314'}, {'`', 'i', '\354'}, + {'\'', 'I', '\315'}, {'\'', 'i', '\355'}, + {'^', 'I', '\316'}, {'^', 'i', '\356'}, + {'"', 'I', '\317'}, {'"', 'i', '\357'}, + {'-', 'D', '\320'}, {'-', 'd', '\360'}, + {'~', 'N', '\321'}, {'~', 'n', '\361'}, + {'`', 'O', '\322'}, {'`', 'o', '\362'}, + {'\'', 'O', '\323'}, {'\'', 'o', '\363'}, + {'^', 'O', '\324'}, {'^', 'o', '\364'}, + {'~', 'O', '\325'}, {'~', 'o', '\365'}, + {'"', 'O', '\326'}, {'"', 'o', '\366'}, + {'/', 'O', '\330'}, {'/', 'o', '\370'}, + {'`', 'U', '\331'}, {'`', 'u', '\371'}, + {'\'', 'U', '\332'}, {'\'', 'u', '\372'}, + {'^', 'U', '\333'}, {'^', 'u', '\373'}, + {'"', 'U', '\334'}, {'"', 'u', '\374'}, + {'\'', 'Y', '\335'}, {'\'', 'y', '\375'}, + {'T', 'H', '\336'}, {'t', 'h', '\376'}, + {'s', 's', '\337'}, {'"', 'y', '\377'}, + {'s', 'z', '\337'}, {'i', 'j', '\377'}, +}; + +unsigned int accent_table_size = 68; diff --git a/drivers/atm/fore200e.c b/drivers/atm/fore200e.c index 82b2cc510..164ef4523 100644 --- a/drivers/atm/fore200e.c +++ b/drivers/atm/fore200e.c @@ -56,9 +56,7 @@ #include <asm/pgtable.h> #endif -#ifdef MODULE #include <linux/module.h> -#endif #include "fore200e.h" #include "suni.h" diff --git a/drivers/block/elevator.c b/drivers/block/elevator.c index 2bf92251c..1eee6d545 100644 --- a/drivers/block/elevator.c +++ b/drivers/block/elevator.c @@ -138,6 +138,8 @@ void elevator_linus(struct request *req, elevator_t *elevator, struct list_head *entry = real_head; struct request *tmp; + req->elevator_sequence = orig_latency; + if (list_empty(real_head)) { list_add(&req->queue, real_head); return; diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index 2ed93b300..9646adbd0 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -345,8 +345,6 @@ static inline struct request *get_request(request_queue_t *q, int rw) register struct request *rq = NULL; if (!list_empty(&q->request_freelist)) { - elevator_t *e = &q->elevator; - if ((q->queue_requests > QUEUE_WRITES_MAX) && (rw == WRITE)) return NULL; @@ -355,10 +353,6 @@ static inline struct request *get_request(request_queue_t *q, int rw) rq->rq_status = RQ_ACTIVE; rq->special = NULL; rq->q = q; - if (rq->cmd == READ) - rq->elevator_sequence = e->read_latency; - else - rq->elevator_sequence = e->write_latency; q->queue_requests++; } return rq; @@ -657,6 +651,13 @@ static inline void __make_request(request_queue_t * q, int rw, goto get_rq; } + /* + * skip first entry, for devices with active queue head + */ + head = &q->queue_head; + if (q->head_active && !q->plugged) + head = head->next; + el_ret = elevator->elevator_merge_fn(q, &req, bh, rw, &max_sectors, &max_segments); switch (el_ret) { @@ -709,12 +710,12 @@ get_rq: req = __get_request_wait(q, rw); spin_lock_irq(&io_request_lock); + + head = &q->queue_head; + if (q->head_active && !q->plugged) + head = head->next; } - head = &q->queue_head; - if (q->head_active && !q->plugged) - head = head->next; - /* fill up the request-info, and add it to the queue */ req->cmd = rw; req->errors = 0; diff --git a/drivers/char/i810_rng.c b/drivers/char/i810_rng.c index 191ce5001..905d8e8cd 100644 --- a/drivers/char/i810_rng.c +++ b/drivers/char/i810_rng.c @@ -2,35 +2,35 @@ Hardware driver for Intel i810 Random Number Generator (RNG) Copyright 2000 Jeff Garzik <jgarzik@mandrakesoft.com> - + Driver Web site: http://gtf.org/garzik/drivers/i810_rng/ - + Based on: Intel 82802AB/82802AC Firmware Hub (FWH) Datasheet May 1999 Order Number: 290658-002 R - + Intel 82802 Firmware Hub: Random Number Generator Programmer's Reference Manual December 1999 Order Number: 298029-001 R - + Intel 82802 Firmware HUB Random Number Generator Driver Copyright (c) 2000 Matt Sottek <msottek@quiknet.com> Special thanks to Matt Sottek. I did the "guts", he did the "brains" and all the testing. (Anybody wanna send me an i810 or i820?) - + ---------------------------------------------------------- - + This software may be used and distributed according to the terms of the GNU Public License, incorporated herein by reference. ---------------------------------------------------------- - + From the firmware hub datasheet: - + The Firmware Hub integrates a Random Number Generator (RNG) using thermal noise generated from inherently random quantum mechanical properties of silicon. When not generating new random @@ -44,7 +44,7 @@ Theory of operation: This driver has TWO modes of operation: - + Mode 1 ------ Character driver. Using the standard open() @@ -52,16 +52,16 @@ the i810 RNG device. This data is NOT CHECKED by any fitness tests, and could potentially be bogus (if the hardware is faulty or has been tampered with). - + /dev/intel_rng is char device major 10, minor 183. - + Mode 2 ------ Injection of entropy into the kernel entropy pool via a timer function. - A timer is run at RNG_TIMER_LEN intervals, reading 8 bits + A timer is run at rng_timer_len intervals, reading 8 bits of data from the RNG. If the RNG has previously passed a FIPS test, then the data will be added to the /dev/random entropy pool. Then, those 8 bits are added to an internal @@ -71,12 +71,12 @@ Thus, the RNG will never be enabled until it passes a FIPS test. And, data will stop flowing into the system entropy pool if the data is determined to be non-random. - + Finally, note that the timer defaults to OFF. This ensures that the system entropy pool will not be polluted with RNG-originated data unless a conscious decision is made by the user. - + HOWEVER NOTE THAT UP TO 2499 BYTES OF DATA CAN BE BOGUS BEFORE THE SYSTEM WILL NOTICE VIA THE FIPS TEST. @@ -84,14 +84,13 @@ Driver notes: - * You may enable and disable the RNG hardware (and this - driver) via sysctl: + * You may enable and disable the RNG timer via sysctl: # disable RNG - echo 0 > /proc/sys/dev/i810_hw_enabled + echo 0 > /proc/sys/dev/i810_rng_timer # enable RNG - echo 1 > /proc/sys/dev/i810_hw_enabled + echo 1 > /proc/sys/dev/i810_rng_timer * The default number of entropy bits added by default is the full 8 bits. If you wish to reduce this value for @@ -103,10 +102,56 @@ * The default number of entropy bits can also be set via a module parameter "rng_entropy" at module load time. + * When the RNG timer is enabled, the driver reads 1 byte + from the hardware RNG every N jiffies. By default, every + half-second. If you would like to change the timer interval, + do so via another sysctl: + + echo 200 > /proc/sys/dev/i810_rng_interval + + NOTE THIS VALUE IS IN JIFFIES, NOT SECONDS OR MILLISECONDS. + Minimum interval is 1 jiffy, maximum interval is 24 hours. + * In order to unload the i810_rng module, you must first disable the hardware via sysctl i810_hw_enabled, as shown above, - and make sure all users of the character device - + and make sure all users of the character device have closed + + * The timer and the character device may be used simultaneously, + if desired. + + * FIXME: Currently only one open() of the character device is allowed. + If another user tries to open() the device, they will get an + -EBUSY error. Instead, this really should either support + multiple simultaneous users of the character device (not hard), + or simply block open() until the current user of the chrdev + calls close(). + + * FIXME: support poll() + + * FIXME: should we be crazy and support mmap()? + + * FIXME: It is possible for the timer function to read, + and shove into the kernel entropy pool, 2499 bytes of data + before the internal FIPS test notices that the data is bad. + The kernel should handle this (I think???), but we should use a + 2500-byte array, and re-run the FIPS test for every byte read. + This will slow things down but guarantee that bad data is + never passed upstream. + + ---------------------------------------------------------- + + Change history: + + 0.6.2: + * Clean up spinlocks. Since we don't have any interrupts + to worry about, but we do have a timer to worry about, + we use spin_lock_bh everywhere except the timer function + itself. + * Fix module load/unload. + * Fix timer function and h/w enable/disable logic + * New timer interval sysctl + * Clean up sysctl names + */ @@ -115,6 +160,7 @@ #include <linux/fs.h> #include <linux/init.h> #include <linux/pci.h> +#include <linux/interrupt.h> #include <linux/spinlock.h> #include <linux/random.h> #include <linux/sysctl.h> @@ -127,24 +173,24 @@ /* * core module and version information */ -#define RNG_VERSION "0.6.1" +#define RNG_VERSION "0.6.2" #define RNG_MODULE_NAME "i810_rng" #define RNG_DRIVER_NAME RNG_MODULE_NAME " hardware driver " RNG_VERSION -#define PFX RNG_MODULE_NAME ": " +#define PFX RNG_MODULE_NAME ": " /* * debugging macros */ #undef RNG_DEBUG /* define to 1 to enable copious debugging info */ - + #ifdef RNG_DEBUG /* note: prints function name for you */ #define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args) #else #define DPRINTK(fmt, args...) #endif - + #define RNG_NDEBUG 0 /* define to 1 to disable lightweight runtime checks */ #if RNG_NDEBUG #define assert(expr) @@ -156,11 +202,11 @@ } #endif - + /* * misc helper macros */ -#define arraysize(x) (sizeof(x)/sizeof(*(x))) +#define arraysize(x) (sizeof(x)/sizeof(*(x))) /* * prototypes @@ -191,7 +237,7 @@ static void rng_run_fips_test (void); * Frequency that data is added to kernel entropy pool * HZ>>1 == every half-second */ -#define RNG_TIMER_LEN (HZ >> 1) +#define RNG_DEF_TIMER_LEN (HZ >> 1) /* @@ -206,18 +252,22 @@ static void rng_run_fips_test (void); * various RNG status variables. they are globals * as we only support a single RNG device */ -static int rng_allocated = 0; /* is someone using the RNG region? */ -static int rng_hw_enabled = 0; /* is the RNG h/w, and timer, enabled? */ -static int rng_trusted = 0; /* does FIPS trust out data? */ +static int rng_allocated; /* is someone using the RNG region? */ +static int rng_hw_enabled; /* is the RNG h/w enabled? */ +static int rng_timer_enabled; /* is the RNG timer enabled? */ +static int rng_use_count; /* number of times RNG has been enabled */ +static int rng_trusted; /* does FIPS trust out data? */ static int rng_enabled_sysctl; /* sysctl for enabling/disabling RNG */ -static int rng_entropy = 8; /* number of entropy bits we submit to /dev/random */ -static int rng_entropy_sysctl; /* sysctl for changing entropy bits */ -static int rng_have_mem_region = 0; /* did we grab RNG region via request_mem_region? */ -static int rng_fips_counter = 0; /* size of internal FIPS test data pool */ -static void *rng_mem = NULL; /* token to our ioremap'd RNG register area */ +static int rng_entropy = 8; /* number of entropy bits we submit to /dev/random */ +static int rng_entropy_sysctl; /* sysctl for changing entropy bits */ +static int rng_interval_sysctl; /* sysctl for changing timer interval */ +static int rng_have_mem_region; /* did we grab RNG region via request_mem_region? */ +static int rng_fips_counter; /* size of internal FIPS test data pool */ +static int rng_timer_len = RNG_DEF_TIMER_LEN; /* timer interval, in jiffies */ +static void *rng_mem; /* token to our ioremap'd RNG register area */ static spinlock_t rng_lock = SPIN_LOCK_UNLOCKED; /* hardware lock */ static struct timer_list rng_timer; /* kernel timer for RNG hardware reads and tests */ -static atomic_t rng_open; +static int rng_open; /* boolean, 0 (false) if chrdev is closed, 1 (true) if open */ /* * inlined helper functions for accessing RNG registers @@ -240,7 +290,7 @@ static inline int rng_data_present (void) { assert (rng_mem != NULL); assert (rng_hw_enabled == 1); - + return (readb (rng_mem + RNG_STATUS) & RNG_DATA_PRESENT) ? 1 : 0; } @@ -249,22 +299,21 @@ static inline int rng_data_read (void) { assert (rng_mem != NULL); assert (rng_hw_enabled == 1); - + return readb (rng_mem + RNG_DATA); } /* - * rng_timer_ticker - executes every RNG_TIMER_LEN jiffies, + * rng_timer_ticker - executes every rng_timer_len jiffies, * adds a single byte to system entropy * and internal FIPS test pools */ static void rng_timer_tick (unsigned long data) { - unsigned long flags; int rng_data; - spin_lock_irqsave (&rng_lock, flags); + spin_lock (&rng_lock); if (rng_data_present ()) { /* gimme some thermal noise, baby */ @@ -285,11 +334,14 @@ static void rng_timer_tick (unsigned long data) rng_run_fips_test (); } - spin_unlock_irqrestore (&rng_lock, flags); + /* run the timer again, if enabled */ + if (rng_timer_enabled) { + rng_timer.expires = jiffies + rng_timer_len; + add_timer (&rng_timer); + } + + spin_unlock (&rng_lock); - /* run the timer again */ - rng_timer.expires = jiffies + RNG_TIMER_LEN; - add_timer (&rng_timer); } @@ -298,43 +350,44 @@ static void rng_timer_tick (unsigned long data) */ static int rng_enable (int enable) { - unsigned long flags; int rc = 0; u8 hw_status; - + DPRINTK ("ENTER\n"); - - spin_lock_irqsave (&rng_lock, flags); + + spin_lock_bh (&rng_lock); hw_status = rng_hwstatus (); - - if (enable && !rng_hw_enabled) { - rng_hwstatus_set (hw_status | RNG_ENABLED); - printk (KERN_INFO PFX "RNG h/w enabled\n"); + if (enable) { rng_hw_enabled = 1; + rng_use_count++; MOD_INC_USE_COUNT; + } else { + rng_use_count--; + if (rng_use_count == 0) + rng_hw_enabled = 0; + MOD_DEC_USE_COUNT; } - - else if (!enable && rng_hw_enabled) { - del_timer (&rng_timer); - rng_hwstatus_set (hw_status & ~RNG_ENABLED); + if (rng_hw_enabled && ((hw_status & RNG_ENABLED) == 0)) { + rng_hwstatus_set (hw_status | RNG_ENABLED); + printk (KERN_INFO PFX "RNG h/w enabled\n"); + } + else if (!rng_hw_enabled && (hw_status & RNG_ENABLED)) { + rng_hwstatus_set (hw_status & ~RNG_ENABLED); printk (KERN_INFO PFX "RNG h/w disabled\n"); - rng_hw_enabled = 0; - MOD_DEC_USE_COUNT; } - - if (enable != (rng_hwstatus () & RNG_ENABLED) ) { - del_timer (&rng_timer); + + spin_unlock_bh (&rng_lock); + + if ((!!enable) != (!!(rng_hwstatus () & RNG_ENABLED))) { printk (KERN_ERR PFX "Unable to %sable the RNG\n", enable ? "en" : "dis"); rc = -EIO; } - spin_unlock_irqrestore (&rng_lock, flags); - DPRINTK ("EXIT, returning %d\n", rc); return rc; } @@ -343,26 +396,42 @@ static int rng_enable (int enable) /* * rng_handle_sysctl_enable - handle a read or write of our enable/disable sysctl */ - + static int rng_handle_sysctl_enable (ctl_table * table, int write, struct file *filp, void *buffer, size_t * lenp) { - unsigned long flags; int enabled_save, rc; DPRINTK ("ENTER\n"); - - spin_lock_irqsave (&rng_lock, flags); - rng_enabled_sysctl = enabled_save = rng_hw_enabled; - spin_unlock_irqrestore (&rng_lock, flags); + + spin_lock_bh (&rng_lock); + + rng_enabled_sysctl = enabled_save = rng_timer_enabled; rc = proc_dointvec (table, write, filp, buffer, lenp); - if (rc) + if (rc) { + spin_unlock_bh (&rng_lock); return rc; - - if (enabled_save != rng_enabled_sysctl) + } + + if (enabled_save != rng_enabled_sysctl) { + rng_timer_enabled = rng_enabled_sysctl; + spin_unlock_bh (&rng_lock); + + /* enable/disable hardware */ rng_enable (rng_enabled_sysctl); + /* enable/disable timer */ + if (rng_enabled_sysctl) { + rng_timer.expires = jiffies + rng_timer_len; + add_timer (&rng_timer); + } else { + del_timer_sync (&rng_timer); + } + } else { + spin_unlock_bh (&rng_lock); + } + DPRINTK ("EXIT, returning 0\n"); return 0; } @@ -371,31 +440,30 @@ static int rng_handle_sysctl_enable (ctl_table * table, int write, struct file * /* * rng_handle_sysctl_entropy - handle a read or write of our entropy bits sysctl */ - + static int rng_handle_sysctl_entropy (ctl_table * table, int write, struct file *filp, void *buffer, size_t * lenp) { - unsigned long flags; int entropy_bits_save, rc; DPRINTK ("ENTER\n"); - - spin_lock_irqsave (&rng_lock, flags); + + spin_lock_bh (&rng_lock); rng_entropy_sysctl = entropy_bits_save = rng_entropy; - spin_unlock_irqrestore (&rng_lock, flags); + spin_unlock_bh (&rng_lock); rc = proc_dointvec (table, write, filp, buffer, lenp); if (rc) return rc; - + if (entropy_bits_save == rng_entropy_sysctl) goto out; if ((rng_entropy_sysctl >= 0) && (rng_entropy_sysctl <= 8)) { - spin_lock_irqsave (&rng_lock, flags); + spin_lock_bh (&rng_lock); rng_entropy = rng_entropy_sysctl; - spin_unlock_irqrestore (&rng_lock, flags); + spin_unlock_bh (&rng_lock); printk (KERN_INFO PFX "entropy bits now %d\n", rng_entropy_sysctl); } else { @@ -408,36 +476,89 @@ out: return 0; } +/* + * rng_handle_sysctl_interval - handle a read or write of our timer interval len sysctl + */ + +static int rng_handle_sysctl_interval (ctl_table * table, int write, struct file *filp, + void *buffer, size_t * lenp) +{ + int timer_len_save, rc; + + DPRINTK ("ENTER\n"); + + spin_lock_bh (&rng_lock); + rng_interval_sysctl = timer_len_save = rng_timer_len; + spin_unlock_bh (&rng_lock); + + rc = proc_dointvec (table, write, filp, buffer, lenp); + if (rc) + return rc; + + if (timer_len_save == rng_interval_sysctl) + goto out; + + if ((rng_interval_sysctl > 0) && + (rng_interval_sysctl < (HZ*86400))) { + spin_lock_bh (&rng_lock); + rng_timer_len = rng_interval_sysctl; + spin_unlock_bh (&rng_lock); + + printk (KERN_INFO PFX "timer interval now %d\n", rng_interval_sysctl); + } else { + printk (KERN_INFO PFX "ignoring invalid timer interval (%d)\n", + rng_interval_sysctl); + } + +out: + DPRINTK ("EXIT, returning 0\n"); + return 0; +} + /* * rng_sysctl - add or remove the rng sysctl */ static void rng_sysctl (int add) { -#define DEV_I810_RNG 1 -#define DEV_I810_RNG_ENTROPY 2 +#define DEV_I810_TIMER 1 +#define DEV_I810_ENTROPY 2 +#define DEV_I810_INTERVAL 3 + /* Definition of the sysctl */ + /* FIXME: use new field:value style of struct initialization */ static ctl_table rng_sysctls[] = { - {DEV_I810_RNG, /* ID */ - RNG_MODULE_NAME "_enabled", /* name in /proc */ + {DEV_I810_TIMER, /* ID */ + RNG_MODULE_NAME "_timer", /* name in /proc */ &rng_enabled_sysctl, sizeof (rng_enabled_sysctl), /* data ptr, data size */ - 0644, /* mode */ - 0, /* child */ + 0644, /* mode */ + 0, /* child */ rng_handle_sysctl_enable, /* proc handler */ - 0, /* strategy */ - 0, /* proc control block */ + 0, /* strategy */ + 0, /* proc control block */ 0, 0} , - {DEV_I810_RNG_ENTROPY, /* ID */ + {DEV_I810_ENTROPY, /* ID */ RNG_MODULE_NAME "_entropy", /* name in /proc */ &rng_entropy_sysctl, sizeof (rng_entropy_sysctl), /* data ptr, data size */ - 0644, /* mode */ - 0, /* child */ + 0644, /* mode */ + 0, /* child */ rng_handle_sysctl_entropy, /* proc handler */ - 0, /* strategy */ - 0, /* proc control block */ + 0, /* strategy */ + 0, /* proc control block */ + 0, 0} + , + {DEV_I810_INTERVAL, /* ID */ + RNG_MODULE_NAME "_interval", /* name in /proc */ + &rng_interval_sysctl, + sizeof (rng_interval_sysctl), /* data ptr, data size */ + 0644, /* mode */ + 0, /* child */ + rng_handle_sysctl_interval, /* proc handler */ + 0, /* strategy */ + 0, /* proc control block */ 0, 0} , {0} @@ -467,35 +588,36 @@ static void rng_sysctl (int add) static int rng_dev_open (struct inode *inode, struct file *filp) { int rc = -EINVAL; - unsigned long flags; if ((filp->f_mode & FMODE_READ) == 0) goto err_out; if (filp->f_mode & FMODE_WRITE) goto err_out; - spin_lock_irqsave (&rng_lock, flags); - - if (atomic_read(&rng_open)) { - spin_unlock_irqrestore (&rng_lock, flags); + spin_lock_bh (&rng_lock); + + /* only allow one open of this device, exit with -EBUSY if already open */ + /* FIXME: we should sleep on a semaphore here, unless O_NONBLOCK */ + if (rng_open) { + spin_unlock_bh (&rng_lock); rc = -EBUSY; goto err_out; } - - atomic_set (&rng_open, 1); - spin_unlock_irqrestore (&rng_lock, flags); - + rng_open = 1; + + spin_unlock_bh (&rng_lock); + if (rng_enable(1) != 0) { - spin_lock_irqsave (&rng_lock, flags); - atomic_set (&rng_open, 0); - spin_unlock_irqrestore (&rng_lock, flags); + spin_lock_bh (&rng_lock); + rng_open = 0; + spin_unlock_bh (&rng_lock); rc = -EIO; goto err_out; } - + return 0; - + err_out: return rc; } @@ -503,14 +625,13 @@ err_out: static int rng_dev_release (struct inode *inode, struct file *filp) { - unsigned long flags; if (rng_enable(0) != 0) return -EIO; - spin_lock_irqsave (&rng_lock, flags); - atomic_set (&rng_open, 0); - spin_unlock_irqrestore (&rng_lock, flags); + spin_lock_bh (&rng_lock); + rng_open = 0; + spin_unlock_bh (&rng_lock); return 0; } @@ -520,14 +641,13 @@ static ssize_t rng_dev_read (struct file *filp, char * buf, size_t size, loff_t *offp) { int have_data, copied = 0; - unsigned long flags; u8 data=0; u8 *page; - + if (size < 1) return 0; - - page = (unsigned char *) get_zeroed_page (GFP_KERNEL); + + page = (unsigned char *) get_free_page (GFP_KERNEL); if (!page) return -ENOMEM; @@ -545,7 +665,7 @@ read_loop: return tmpsize; } - spin_lock_irqsave (&rng_lock, flags); + spin_lock_bh (&rng_lock); have_data = 0; if (rng_data_present ()) { @@ -553,8 +673,8 @@ read_loop: have_data = 1; } - spin_unlock_irqrestore (&rng_lock, flags); - + spin_unlock_bh (&rng_lock); + if (have_data) { page[copied] = data; copied++; @@ -567,12 +687,12 @@ read_loop: if (current->need_resched) schedule (); - + if (signal_pending (current)) { free_page ((long)page); return -ERESTARTSYS; } - + goto read_loop; } @@ -585,7 +705,7 @@ static int __init rng_init_one (struct pci_dev *dev, { int rc; u8 hw_status; - + DPRINTK ("ENTER\n"); if (rng_allocated) { @@ -619,7 +739,7 @@ static int __init rng_init_one (struct pci_dev *dev, if (rng_entropy < 0 || rng_entropy > RNG_MAX_ENTROPY) rng_entropy = RNG_MAX_ENTROPY; - + /* init core RNG timer, but do not add it */ init_timer (&rng_timer); rng_timer.function = rng_timer_tick; @@ -653,7 +773,7 @@ const static struct pci_device_id rng_pci_tbl[] __initdata = { { 0x8086, 0x2428, PCI_ANY_ID, PCI_ANY_ID, }, { 0, }, }; -MODULE_DEVICE_TABLE (pci, rng_pci_tbl); +MODULE_DEVICE_TABLE (pci, rng_pci_tbl); static struct pci_driver rng_driver = { name: RNG_MODULE_NAME, @@ -690,31 +810,21 @@ static int __init rng_init (void) int rc; DPRINTK ("ENTER\n"); - - MOD_INC_USE_COUNT; if (pci_register_driver (&rng_driver) < 1) { DPRINTK ("EXIT, returning -ENODEV\n"); - MOD_DEC_USE_COUNT; return -ENODEV; } - + rc = misc_register (&rng_miscdev); if (rc) { pci_unregister_driver (&rng_driver); DPRINTK ("EXIT, returning %d\n", rc); - MOD_DEC_USE_COUNT; return rc; } printk (KERN_INFO RNG_DRIVER_NAME " loaded\n"); - /* FIXME: verify module unload logic, then remove - * this additional MOD_INC_USE_COUNT */ - MOD_INC_USE_COUNT; - - MOD_DEC_USE_COUNT; /* init complete, unload allowed now */ - DPRINTK ("EXIT, returning 0\n"); return 0; } @@ -725,13 +835,9 @@ static int __init rng_init (void) */ static void __exit rng_cleanup (void) { - unsigned long flags; - DPRINTK ("ENTER\n"); - spin_lock_irqsave (&rng_lock, flags); - del_timer (&rng_timer); - spin_unlock_irqrestore (&rng_lock, flags); + del_timer_sync (&rng_timer); rng_sysctl (0); pci_unregister_driver (&rng_driver); diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c index f488d9124..0e9d7b57f 100644 --- a/drivers/char/n_tty.c +++ b/drivers/char/n_tty.c @@ -18,6 +18,8 @@ * This file may be redistributed under the terms of the GNU Public * License. * + * Reduced memory usage for older ARM systems - Russell King. + * * 2000/01/20 Fixed SMP locking on put_tty_queue using bits of * the patch by Andrew J. Kroll <ag784@freenet.buffalo.edu> * who actually finally proved there really was a race. @@ -61,6 +63,29 @@ #define TTY_THRESHOLD_THROTTLE 128 /* now based on remaining room */ #define TTY_THRESHOLD_UNTHROTTLE 128 +static inline unsigned char *alloc_buf(void) +{ + unsigned char *p; + int prio = in_interrupt() ? GFP_ATOMIC : GFP_KERNEL; + + if (PAGE_SIZE != N_TTY_BUF_SIZE) { + p = kmalloc(N_TTY_BUF_SIZE, prio); + if (p) + memset(p, 0, N_TTY_BUF_SIZE); + } else + p = (unsigned char *)get_zeroed_page(prio); + + return p; +} + +static inline void free_buf(unsigned char *buf) +{ + if (PAGE_SIZE != N_TTY_BUF_SIZE) + kfree(buf); + else + free_page((unsigned long) buf); +} + static inline void put_tty_queue(unsigned char c, struct tty_struct *tty) { unsigned long flags; @@ -827,7 +852,7 @@ static void n_tty_close(struct tty_struct *tty) { n_tty_flush_buffer(tty); if (tty->read_buf) { - free_page((unsigned long) tty->read_buf); + free_buf(tty->read_buf); tty->read_buf = 0; } } @@ -838,8 +863,7 @@ static int n_tty_open(struct tty_struct *tty) return -EINVAL; if (!tty->read_buf) { - tty->read_buf = (unsigned char *) - get_zeroed_page(in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); + tty->read_buf = alloc_buf(); if (!tty->read_buf) return -ENOMEM; } diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index af52cf98f..ae4b9e112 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -106,6 +106,7 @@ static void pp_attach (struct parport *port) } add->next = pp_port_list; + add->port = port; down (&pp_port_list_lock); pp_port_list = add; up (&pp_port_list_lock); diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c index ba1b9caa9..235b0f131 100644 --- a/drivers/char/stallion.c +++ b/drivers/char/stallion.c @@ -780,7 +780,7 @@ void cleanup_module() stlpanel_t *panelp; stlport_t *portp; unsigned long flags; - int i, j, k, l; + int i, j, k; #if DEBUG printk("cleanup_module()\n"); diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 613b2f967..4c0d29662 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -57,6 +57,9 @@ * * Added support for a Unix98-style ptmx device. * -- C. Scott Ananian <cananian@alumni.princeton.edu>, 14-Jan-1998 + * + * Reduced memory usage for older ARM systems + * -- Russell King <rmk@arm.linux.org.uk> */ #include <linux/config.h> @@ -111,7 +114,7 @@ extern void con_init_devfs (void); #define CHECK_TTY_COUNT 1 struct termios tty_std_termios; /* for the benefit of tty drivers */ -struct tty_driver *tty_drivers = NULL; /* linked list of tty drivers */ +struct tty_driver *tty_drivers; /* linked list of tty drivers */ struct tty_ldisc ldiscs[NR_LDISCS]; /* line disc dispatch table */ #ifdef CONFIG_UNIX98_PTYS @@ -123,7 +126,7 @@ extern struct tty_driver pts_driver[]; /* Unix98 pty slaves; for /dev/ptmx */ * redirect is the pseudo-tty that console output * is redirected to if asked by TIOCCONS. */ -struct tty_struct * redirect = NULL; +struct tty_struct * redirect; static void initialize_tty_struct(struct tty_struct *tty); @@ -135,35 +138,49 @@ static int tty_release(struct inode *, struct file *); int tty_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg); static int tty_fasync(int fd, struct file * filp, int on); -#ifdef CONFIG_SX extern int sx_init (void); -#endif -#if defined(CONFIG_MVME162_SCC) || defined(CONFIG_BVME6000_SCC) || defined(CONFIG_MVME147_SCC) extern int vme_scc_init (void); extern long vme_scc_console_init(void); -#endif -#ifdef CONFIG_SERIAL167 extern int serial167_init(void); extern long serial167_console_init(void); -#endif -#if (defined(CONFIG_8xx) || defined(CONFIG_8260)) extern void console_8xx_init(void); extern int rs_8xx_init(void); -#endif /* CONFIG_8xx */ -#ifdef CONFIG_SGI_SERIAL -extern void sgi_serial_console_init(void); -#endif -#ifdef CONFIG_HWC extern void hwc_console_init(void); -#endif -#ifdef CONFIG_3215 extern void con3215_init(void); -#endif /* CONFIG_3215 */ - +extern void rs285_console_init(void); +extern void rs285_init(void); +extern void sa1100_rs_console_init(void); +extern void sa1100_rs_init(void); +extern void sgi_serial_console_init(void); #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif +#ifndef MAX +#define MAX(a,b) ((a) < (b) ? (b) : (a)) +#endif + +static inline struct tty_struct *alloc_tty_struct(void) +{ + struct tty_struct *tty; + + if (PAGE_SIZE > 8192) { + tty = kmalloc(sizeof(struct tty_struct), GFP_KERNEL); + if (tty) + memset(tty, 0, sizeof(struct tty_struct)); + } else + tty = (struct tty_struct *)get_zeroed_page(GFP_KERNEL); + + return tty; +} + +static inline void free_tty_struct(struct tty_struct *tty) +{ + if (PAGE_SIZE > 8192) + kfree(tty); + else + free_page((unsigned long) tty); +} /* * This routine returns the name of tty. @@ -695,7 +712,7 @@ static inline ssize_t do_tty_write( unlock_kernel(); } else { for (;;) { - unsigned long size = PAGE_SIZE*2; + unsigned long size = MAX(PAGE_SIZE*2,16384); if (size > count) size = count; lock_kernel(); @@ -826,7 +843,7 @@ static int init_dev(kdev_t device, struct tty_struct **ret_tty) tp = o_tp = NULL; ltp = o_ltp = NULL; - tty = (struct tty_struct*) get_zeroed_page(GFP_KERNEL); + tty = alloc_tty_struct(); if(!tty) goto fail_no_mem; initialize_tty_struct(tty); @@ -852,7 +869,7 @@ static int init_dev(kdev_t device, struct tty_struct **ret_tty) } if (driver->type == TTY_DRIVER_TYPE_PTY) { - o_tty = (struct tty_struct *) get_zeroed_page(GFP_KERNEL); + o_tty = alloc_tty_struct(); if (!o_tty) goto free_mem_out; initialize_tty_struct(o_tty); @@ -972,12 +989,12 @@ free_mem_out: if (o_tp) kfree_s(o_tp, sizeof(struct termios)); if (o_tty) - free_page((unsigned long) o_tty); + free_tty_struct(o_tty); if (ltp) kfree_s(ltp, sizeof(struct termios)); if (tp) kfree_s(tp, sizeof(struct termios)); - free_page((unsigned long) tty); + free_tty_struct(tty); fail_no_mem: retval = -ENOMEM; @@ -1008,7 +1025,7 @@ static void release_mem(struct tty_struct *tty, int idx) } o_tty->magic = 0; (*o_tty->driver.refcount)--; - free_page((unsigned long) o_tty); + free_tty_struct(o_tty); } tty->driver.table[idx] = NULL; @@ -1019,7 +1036,7 @@ static void release_mem(struct tty_struct *tty, int idx) } tty->magic = 0; (*tty->driver.refcount)--; - free_page((unsigned long) tty); + free_tty_struct(tty); } /* @@ -1400,9 +1417,9 @@ init_dev_done: static int nr_warns = 0; if (nr_warns < 5) { printk(KERN_WARNING "tty_io.c: " - "process %d (%s) used obsolete /dev/%s - " + "process %d (%s) used obsolete /dev/%s - " "update software to use /dev/ttyS%d\n", - current->pid, current->comm, + current->pid, current->comm, tty_name(tty, buf), TTY_NUMBER(tty)); nr_warns++; } diff --git a/drivers/isdn/avmb1/Makefile b/drivers/isdn/avmb1/Makefile index 77a701611..cd2223911 100644 --- a/drivers/isdn/avmb1/Makefile +++ b/drivers/isdn/avmb1/Makefile @@ -1,5 +1,5 @@ # -# $Id: Makefile,v 1.16 2000/03/17 12:15:44 calle Exp $ +# $Id: Makefile,v 1.18 2000/04/03 16:39:25 calle Exp $ # # Makefile for the CAPI and AVM-B1 device drivers. # @@ -146,6 +146,11 @@ ifeq ($(CONFIG_ISDN_CAPI),y) endif ifdef CONFIG_ISDN_DRV_AVMB1_B1PCMCIA OX_OBJS += b1pcmcia.o + ifeq ($(CONFIG_HOTPLUG),y) + ifneq ($(CONFIG_PCMCIA),n) + M_OBJS += avm_cs.o + endif + endif endif ifdef CONFIG_ISDN_DRV_AVMB1_T1PCI O_OBJS += t1pci.o diff --git a/drivers/isdn/avmb1/b1.c b/drivers/isdn/avmb1/b1.c index 65c4368cd..ff13e1636 100644 --- a/drivers/isdn/avmb1/b1.c +++ b/drivers/isdn/avmb1/b1.c @@ -1,11 +1,14 @@ /* - * $Id: b1.c,v 1.13 2000/01/25 14:33:38 calle Exp $ + * $Id: b1.c,v 1.14 2000/06/19 16:51:53 keil Exp $ * * Common module for AVM B1 cards. * * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: b1.c,v $ + * Revision 1.14 2000/06/19 16:51:53 keil + * don't free skb in irq context + * * Revision 1.13 2000/01/25 14:33:38 calle * - Added Support AVM B1 PCI V4.0 (tested with prototype) * - splitted up t1pci.c into b1dma.c for common function with b1pciv4 @@ -86,12 +89,13 @@ #include <linux/capi.h> #include <asm/io.h> #include <asm/uaccess.h> +#include <linux/netdevice.h> #include "capilli.h" #include "avmcard.h" #include "capicmd.h" #include "capiutil.h" -static char *revision = "$Revision: 1.13 $"; +static char *revision = "$Revision: 1.14 $"; /* ------------------------------------------------------------- */ @@ -420,7 +424,7 @@ void b1_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) b1_put_slice(port, skb->data, len); } restore_flags(flags); - dev_kfree_skb(skb); + dev_kfree_skb_any(skb); } /* ------------------------------------------------------------- */ diff --git a/drivers/isdn/avmb1/b1dma.c b/drivers/isdn/avmb1/b1dma.c index b40fafae9..168c7202f 100644 --- a/drivers/isdn/avmb1/b1dma.c +++ b/drivers/isdn/avmb1/b1dma.c @@ -1,11 +1,14 @@ /* - * $Id: b1dma.c,v 1.4 2000/04/03 16:38:05 calle Exp $ + * $Id: b1dma.c,v 1.5 2000/06/19 16:51:53 keil Exp $ * * Common module for AVM B1 cards that support dma with AMCC * * (c) Copyright 2000 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: b1dma.c,v $ + * Revision 1.5 2000/06/19 16:51:53 keil + * don't free skb in irq context + * * Revision 1.4 2000/04/03 16:38:05 calle * made suppress_pollack static. * @@ -32,12 +35,13 @@ #include <linux/capi.h> #include <asm/io.h> #include <asm/uaccess.h> +#include <linux/netdevice.h> #include "capilli.h" #include "avmcard.h" #include "capicmd.h" #include "capiutil.h" -static char *revision = "$Revision: 1.4 $"; +static char *revision = "$Revision: 1.5 $"; /* ------------------------------------------------------------- */ @@ -430,7 +434,7 @@ static void b1dma_dispatch_tx(avmcard *card) b1dmaoutmeml(card->mbase+AMCC_INTCSR, card->csr); restore_flags(flags); - dev_kfree_skb(skb); + dev_kfree_skb_any(skb); } /* ------------------------------------------------------------- */ @@ -615,11 +619,6 @@ static void b1dma_handle_interrupt(avmcard *card) if ((status & TX_TC_INT) != 0) { card->csr &= ~EN_TX_TC_INT; b1dma_dispatch_tx(card); - } else if (card->csr & EN_TX_TC_INT) { - if (b1dmainmeml(card->mbase+AMCC_TXLEN) == 0) { - card->csr &= ~EN_TX_TC_INT; - b1dma_dispatch_tx(card); - } } b1dmaoutmeml(card->mbase+AMCC_INTCSR, card->csr); } diff --git a/drivers/isdn/avmb1/b1pci.c b/drivers/isdn/avmb1/b1pci.c index e98066806..9e230cb2e 100644 --- a/drivers/isdn/avmb1/b1pci.c +++ b/drivers/isdn/avmb1/b1pci.c @@ -1,11 +1,24 @@ /* - * $Id: b1pci.c,v 1.21 2000/04/03 13:29:24 calle Exp $ + * $Id: b1pci.c,v 1.25 2000/05/29 12:29:18 keil Exp $ * * Module for AVM B1 PCI-card. * * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: b1pci.c,v $ + * Revision 1.25 2000/05/29 12:29:18 keil + * make pci_enable_dev compatible to 2.2 kernel versions + * + * Revision 1.24 2000/05/19 15:43:22 calle + * added calls to pci_device_start(). + * + * Revision 1.23 2000/05/06 00:52:36 kai + * merged changes from kernel tree + * fixed timer and net_device->name breakage + * + * Revision 1.22 2000/04/21 13:01:33 calle + * Revision in b1pciv4 driver was missing. + * * Revision 1.21 2000/04/03 13:29:24 calle * make Tim Waugh happy (module unload races in 2.3.99-pre3). * no real problem there, but now it is much cleaner ... @@ -74,12 +87,13 @@ #include <linux/pci.h> #include <linux/capi.h> #include <asm/io.h> +#include <linux/isdn.h> #include "capicmd.h" #include "capiutil.h" #include "capilli.h" #include "avmcard.h" -static char *revision = "$Revision: 1.21 $"; +static char *revision = "$Revision: 1.25 $"; /* ------------------------------------------------------------- */ @@ -466,16 +480,26 @@ static int add_card(struct pci_dev *dev) struct capicardparams param; int retval; - if (pci_enable_device(dev)) - return 0; /* return failure */ - - if (pci_resource_flags(dev, 2) & IORESOURCE_IO) { /* B1 PCI V4 */ + if (pci_resource_start(dev, 2)) { /* B1 PCI V4 */ #ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4 driver = &b1pciv4_driver; #endif - param.membase = pci_resource_start (dev, 0); - param.port = pci_resource_start (dev, 2); + param.membase = pci_resource_start(dev, 0); + param.port = pci_resource_start(dev, 2); param.irq = dev->irq; + + retval = pci_enable_device (dev); + if (retval != 0) { + printk(KERN_ERR + "%s: failed to enable AVM-B1 V4 at i/o %#x, irq %d, mem %#x err=%d\n", + driver->name, param.port, param.irq, param.membase, retval); +#ifdef MODULE + cleanup_module(); +#endif + MOD_DEC_USE_COUNT; + return -EIO; + } + printk(KERN_INFO "%s: PCI BIOS reports AVM-B1 V4 at i/o %#x, irq %d, mem %#x\n", driver->name, param.port, param.irq, param.membase); @@ -491,8 +515,20 @@ static int add_card(struct pci_dev *dev) } } else { param.membase = 0; - param.port = pci_resource_start (dev, 1); + param.port = pci_resource_start(dev, 1); param.irq = dev->irq; + + retval = pci_enable_device (dev); + if (retval != 0) { + printk(KERN_ERR + "%s: failed to enable AVM-B1 at i/o %#x, irq %d, err=%d\n", + driver->name, param.port, param.irq, retval); +#ifdef MODULE + cleanup_module(); +#endif + MOD_DEC_USE_COUNT; + return -EIO; + } printk(KERN_INFO "%s: PCI BIOS reports AVM-B1 at i/o %#x, irq %d\n", driver->name, param.port, param.irq); @@ -522,6 +558,12 @@ int b1pci_init(void) strncpy(driver->revision, p + 1, sizeof(driver->revision)); p = strchr(driver->revision, '$'); *p = 0; +#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4 + p = strchr(revision, ':'); + strncpy(driverv4->revision, p + 1, sizeof(driverv4->revision)); + p = strchr(driverv4->revision, '$'); + *p = 0; +#endif } printk(KERN_INFO "%s: revision %s\n", driver->name, driver->revision); diff --git a/drivers/isdn/avmb1/c4.c b/drivers/isdn/avmb1/c4.c index 104c0f5cc..09c9bad99 100644 --- a/drivers/isdn/avmb1/c4.c +++ b/drivers/isdn/avmb1/c4.c @@ -1,11 +1,24 @@ /* - * $Id: c4.c,v 1.8 2000/04/03 16:38:05 calle Exp $ + * $Id: c4.c,v 1.12 2000/06/19 16:51:53 keil Exp $ * * Module for AVM C4 card. * * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: c4.c,v $ + * Revision 1.12 2000/06/19 16:51:53 keil + * don't free skb in irq context + * + * Revision 1.11 2000/06/19 15:11:24 keil + * avoid use of freed structs + * changes from 2.4.0-ac21 + * + * Revision 1.10 2000/05/29 12:29:18 keil + * make pci_enable_dev compatible to 2.2 kernel versions + * + * Revision 1.9 2000/05/19 15:43:22 calle + * added calls to pci_device_start(). + * * Revision 1.8 2000/04/03 16:38:05 calle * made suppress_pollack static. * @@ -46,6 +59,7 @@ #include <linux/ioport.h> #include <linux/pci.h> #include <linux/capi.h> +#include <linux/isdn.h> #include <asm/io.h> #include <asm/uaccess.h> #include "capicmd.h" @@ -53,7 +67,7 @@ #include "capilli.h" #include "avmcard.h" -static char *revision = "$Revision: 1.8 $"; +static char *revision = "$Revision: 1.12 $"; #undef CONFIG_C4_DEBUG #undef CONFIG_C4_POLLDEBUG @@ -510,7 +524,7 @@ static void c4_dispatch_tx(avmcard *card) c4outmeml(card->mbase+DOORBELL, DBELL_DOWN_ARM); restore_flags(flags); - dev_kfree_skb(skb); + dev_kfree_skb_any(skb); } /* ------------------------------------------------------------- */ @@ -949,8 +963,10 @@ static void c4_remove_ctr(struct capi_ctr *ctrl) for (i=0; i < 4; i++) { cinfo = &card->ctrlinfo[i]; - if (cinfo->capi_ctrl) + if (cinfo->capi_ctrl) { di->detach_ctr(cinfo->capi_ctrl); + cinfo->capi_ctrl = NULL; + } } free_irq(card->irq, card); @@ -1163,7 +1179,6 @@ static int c4_add_card(struct capi_driver *driver, struct capicardparams *p) cinfo = (avmctrl_info *) kmalloc(sizeof(avmctrl_info)*4, GFP_ATOMIC); if (!cinfo) { printk(KERN_WARNING "%s: no memory.\n", driver->name); - kfree(card->ctrlinfo); kfree(card->dma); kfree(card); MOD_DEC_USE_COUNT; @@ -1333,12 +1348,21 @@ int c4_init(void) PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_C4, dev))) { struct capicardparams param; - if (pci_enable_device(dev)) - continue; - - param.port = pci_resource_start (dev, 1); + param.port = pci_resource_start(dev, 1); param.irq = dev->irq; - param.membase = pci_resource_start (dev, 0); + param.membase = pci_resource_start(dev, 0); + + retval = pci_enable_device (dev); + if (retval != 0) { + printk(KERN_ERR + "%s: failed to enable AVM-C4 at i/o %#x, irq %d, mem %#x err=%d\n", + driver->name, param.port, param.irq, param.membase, retval); +#ifdef MODULE + cleanup_module(); +#endif + MOD_DEC_USE_COUNT; + return -EIO; + } printk(KERN_INFO "%s: PCI BIOS reports AVM-C4 at i/o %#x, irq %d, mem %#x\n", diff --git a/drivers/isdn/avmb1/capi.c b/drivers/isdn/avmb1/capi.c index 8169df482..610c20b6d 100644 --- a/drivers/isdn/avmb1/capi.c +++ b/drivers/isdn/avmb1/capi.c @@ -1,11 +1,24 @@ /* - * $Id: capi.c,v 1.31 2000/04/03 13:29:24 calle Exp $ + * $Id: capi.c,v 1.35 2000/06/19 15:11:24 keil Exp $ * * CAPI 2.0 Interface for Linux * * Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: capi.c,v $ + * Revision 1.35 2000/06/19 15:11:24 keil + * avoid use of freed structs + * changes from 2.4.0-ac21 + * + * Revision 1.34 2000/06/18 16:09:54 keil + * more changes for 2.4 + * + * Revision 1.33 2000/05/18 16:35:43 calle + * Uaaahh. Bad memory leak fixed. + * + * Revision 1.32 2000/04/21 12:38:42 calle + * Bugfix: error in proc_ functions, begin-off => off-begin + * * Revision 1.31 2000/04/03 13:29:24 calle * make Tim Waugh happy (module unload races in 2.3.99-pre3). * no real problem there, but now it is much cleaner ... @@ -158,7 +171,6 @@ * */ -#include <linux/config.h> #include <linux/module.h> #include <linux/errno.h> #include <linux/kernel.h> @@ -193,7 +205,7 @@ #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ #include <linux/slab.h> -static char *revision = "$Revision: 1.31 $"; +static char *revision = "$Revision: 1.35 $"; MODULE_AUTHOR("Carsten Paeth (calle@calle.in-berlin.de)"); @@ -816,7 +828,6 @@ static void capi_signal(__u16 applid, void *param) if (CAPIMSG_SUBCOMMAND(skb->data) == CAPI_IND) { - datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN+4+4+2); #ifdef _DEBUG_DATAFLOW printk(KERN_DEBUG "capi_signal: DATA_B3_IND %u len=%d\n", @@ -854,14 +865,14 @@ static void capi_signal(__u16 applid, void *param) /* -------- file_operations for capidev ----------------------------- */ -static long long capi_llseek(struct file *file, - long long offset, int origin) +static loff_t +capi_llseek(struct file *file, loff_t offset, int origin) { return -ESPIPE; } -static ssize_t capi_read(struct file *file, char *buf, - size_t count, loff_t *ppos) +static ssize_t +capi_read(struct file *file, char *buf, size_t count, loff_t *ppos) { struct capidev *cdev = (struct capidev *)file->private_data; struct sk_buff *skb; @@ -911,8 +922,8 @@ static ssize_t capi_read(struct file *file, char *buf, return copied; } -static ssize_t capi_write(struct file *file, const char *buf, - size_t count, loff_t *ppos) +static ssize_t +capi_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { struct capidev *cdev = (struct capidev *)file->private_data; struct sk_buff *skb; @@ -975,7 +986,8 @@ capi_poll(struct file *file, poll_table * wait) return mask; } -static int capi_ioctl(struct inode *inode, struct file *file, +static int +capi_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct capidev *cdev = (struct capidev *)file->private_data; @@ -1190,7 +1202,8 @@ static int capi_ioctl(struct inode *inode, struct file *file, return -EINVAL; } -static int capi_open(struct inode *inode, struct file *file) +static int +capi_open(struct inode *inode, struct file *file) { if (file->private_data) return -EEXIST; @@ -1198,19 +1211,23 @@ static int capi_open(struct inode *inode, struct file *file) if ((file->private_data = capidev_alloc(file)) == 0) return -ENOMEM; + MOD_INC_USE_COUNT; #ifdef _DEBUG_REFCOUNT printk(KERN_DEBUG "capi_open %d\n", GET_USE_COUNT(THIS_MODULE)); #endif return 0; } -static int capi_release(struct inode *inode, struct file *file) +static int +capi_release(struct inode *inode, struct file *file) { struct capidev *cdev = (struct capidev *)file->private_data; capincci_free(cdev, 0xffffffff); capidev_free(cdev); + file->private_data = NULL; + MOD_DEC_USE_COUNT; #ifdef _DEBUG_REFCOUNT printk(KERN_DEBUG "capi_release %d\n", GET_USE_COUNT(THIS_MODULE)); #endif @@ -1232,7 +1249,8 @@ static struct file_operations capi_fops = #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE /* -------- file_operations for capincci ---------------------------- */ -int capinc_raw_open(struct inode *inode, struct file *file) +static int +capinc_raw_open(struct inode *inode, struct file *file) { struct capiminor *mp; @@ -1256,14 +1274,14 @@ int capinc_raw_open(struct inode *inode, struct file *file) return 0; } -long long capinc_raw_llseek(struct file *file, - long long offset, int origin) +static loff_t +capinc_raw_llseek(struct file *file, loff_t offset, int origin) { return -ESPIPE; } -ssize_t capinc_raw_read(struct file *file, char *buf, - size_t count, loff_t *ppos) +static ssize_t +capinc_raw_read(struct file *file, char *buf, size_t count, loff_t *ppos) { struct capiminor *mp = (struct capiminor *)file->private_data; struct sk_buff *skb; @@ -1320,8 +1338,8 @@ ssize_t capinc_raw_read(struct file *file, char *buf, return copied; } -ssize_t capinc_raw_write(struct file *file, const char *buf, - size_t count, loff_t *ppos) +static ssize_t +capinc_raw_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { struct capiminor *mp = (struct capiminor *)file->private_data; struct sk_buff *skb; @@ -1358,7 +1376,7 @@ ssize_t capinc_raw_write(struct file *file, const char *buf, return count; } -unsigned int +static unsigned int capinc_raw_poll(struct file *file, poll_table * wait) { struct capiminor *mp = (struct capiminor *)file->private_data; @@ -1376,7 +1394,8 @@ capinc_raw_poll(struct file *file, poll_table * wait) return mask; } -int capinc_raw_ioctl(struct inode *inode, struct file *file, +static int +capinc_raw_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct capiminor *mp = (struct capiminor *)file->private_data; @@ -1388,15 +1407,17 @@ int capinc_raw_ioctl(struct inode *inode, struct file *file, return -EINVAL; } -int +static int capinc_raw_release(struct inode *inode, struct file *file) { struct capiminor *mp = (struct capiminor *)file->private_data; if (mp) { mp->file = 0; - if (mp->nccip == 0) + if (mp->nccip == 0) { capiminor_free(mp); + file->private_data = NULL; + } } #ifdef _DEBUG_REFCOUNT @@ -1405,7 +1426,7 @@ capinc_raw_release(struct inode *inode, struct file *file) return 0; } -struct file_operations capinc_raw_fops = +static struct file_operations capinc_raw_fops = { owner: THIS_MODULE, llseek: capinc_raw_llseek, @@ -1875,7 +1896,7 @@ endloop: *eof = 1; if (off >= len+begin) return 0; - *start = page + (off-begin); + *start = page + (begin-off); return ((count < begin+len-off) ? count : begin+len-off); } diff --git a/drivers/isdn/avmb1/capidrv.c b/drivers/isdn/avmb1/capidrv.c index 9521140f1..0b04920f8 100644 --- a/drivers/isdn/avmb1/capidrv.c +++ b/drivers/isdn/avmb1/capidrv.c @@ -1,11 +1,25 @@ /* - * $Id: capidrv.c,v 1.32 2000/04/07 15:19:58 calle Exp $ + * $Id: capidrv.c,v 1.36 2000/06/26 15:13:41 keil Exp $ * * ISDN4Linux Driver, using capi20 interface (kernelcapi) * * Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: capidrv.c,v $ + * Revision 1.36 2000/06/26 15:13:41 keil + * features should be or'ed + * + * Revision 1.35 2000/06/19 15:11:25 keil + * avoid use of freed structs + * changes from 2.4.0-ac21 + * + * Revision 1.34 2000/06/19 13:13:55 calle + * Added Modemsupport! + * + * Revision 1.33 2000/05/06 00:52:36 kai + * merged changes from kernel tree + * fixed timer and net_device->name breakage + * * Revision 1.32 2000/04/07 15:19:58 calle * remove warnings * @@ -192,7 +206,7 @@ #include "capicmd.h" #include "capidrv.h" -static char *revision = "$Revision: 1.32 $"; +static char *revision = "$Revision: 1.36 $"; static int debugmode = 0; MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>"); @@ -327,6 +341,8 @@ static inline __u32 b1prot(int l2, int l3) return 2; case ISDN_PROTO_L2_FAX: return 4; + case ISDN_PROTO_L2_MODEM: + return 8; } } @@ -343,6 +359,7 @@ static inline __u32 b2prot(int l2, int l3) case ISDN_PROTO_L2_V11096: case ISDN_PROTO_L2_V11019: case ISDN_PROTO_L2_V11038: + case ISDN_PROTO_L2_MODEM: return 1; case ISDN_PROTO_L2_FAX: return 4; @@ -360,6 +377,7 @@ static inline __u32 b3prot(int l2, int l3) case ISDN_PROTO_L2_V11096: case ISDN_PROTO_L2_V11019: case ISDN_PROTO_L2_V11038: + case ISDN_PROTO_L2_MODEM: default: return 0; case ISDN_PROTO_L2_FAX: @@ -2269,16 +2287,19 @@ static int capidrv_addcontr(__u16 contr, struct capi_profile *profp) card->interface.writebuf_skb = if_sendbuf; card->interface.writecmd = 0; card->interface.readstat = if_readstat; - card->interface.features = ISDN_FEATURE_L2_X75I | - ISDN_FEATURE_L2_X75UI | - ISDN_FEATURE_L2_X75BUI | - ISDN_FEATURE_L2_HDLC | + card->interface.features = ISDN_FEATURE_L2_HDLC | ISDN_FEATURE_L2_TRANS | ISDN_FEATURE_L3_TRANS | - ISDN_FEATURE_L2_V11096 | + ISDN_FEATURE_P_UNKNOWN | + ISDN_FEATURE_L2_X75I | + ISDN_FEATURE_L2_X75UI | + ISDN_FEATURE_L2_X75BUI; + if (profp->support1 & (1<<2)) + card->interface.features |= ISDN_FEATURE_L2_V11096 | ISDN_FEATURE_L2_V11019 | - ISDN_FEATURE_L2_V11038 | - ISDN_FEATURE_P_UNKNOWN; + ISDN_FEATURE_L2_V11038; + if (profp->support1 & (1<<8)) + card->interface.features |= ISDN_FEATURE_L2_MODEM; card->interface.hl_hdrlen = 22; /* len of DATA_B3_REQ */ strncpy(card->interface.id, id, sizeof(card->interface.id) - 1); @@ -2401,10 +2422,10 @@ static int capidrv_delcontr(__u16 contr) } spin_unlock_irqrestore(&global_lock, flags); - kfree(card); - printk(KERN_INFO "%s: now down.\n", card->name); + kfree(card); + MOD_DEC_USE_COUNT; return 0; diff --git a/drivers/isdn/avmb1/kcapi.c b/drivers/isdn/avmb1/kcapi.c index 848f4af97..d8dd4f97b 100644 --- a/drivers/isdn/avmb1/kcapi.c +++ b/drivers/isdn/avmb1/kcapi.c @@ -1,11 +1,17 @@ /* - * $Id: kcapi.c,v 1.15 2000/04/06 15:01:25 calle Exp $ + * $Id: kcapi.c,v 1.17 2000/04/21 13:00:56 calle Exp $ * * Kernel CAPI 2.0 Module * * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: kcapi.c,v $ + * Revision 1.17 2000/04/21 13:00:56 calle + * Bugfix: driver_proc_info was also wrong. + * + * Revision 1.16 2000/04/21 12:38:42 calle + * Bugfix: error in proc_ functions, begin-off => off-begin + * * Revision 1.15 2000/04/06 15:01:25 calle * Bugfix: crash in capidrv.c when reseting a capi controller. * - changed code order on remove of controller. @@ -109,7 +115,7 @@ #include <linux/b1lli.h> #endif -static char *revision = "$Revision: 1.15 $"; +static char *revision = "$Revision: 1.17 $"; /* ------------------------------------------------------------- */ @@ -1100,7 +1106,7 @@ static int driver_read_proc(char *page, char **start, off_t off, if (len < off) return 0; *eof = 1; - *start = page - off; + *start = page + off; return ((count < len-off) ? count : len-off); } diff --git a/drivers/isdn/avmb1/t1pci.c b/drivers/isdn/avmb1/t1pci.c index 37ed305c2..07625bd0d 100644 --- a/drivers/isdn/avmb1/t1pci.c +++ b/drivers/isdn/avmb1/t1pci.c @@ -1,11 +1,18 @@ /* - * $Id: t1pci.c,v 1.7 2000/04/07 15:26:55 calle Exp $ + * $Id: t1pci.c,v 1.9 2000/05/19 15:43:22 calle Exp $ * * Module for AVM T1 PCI-card. * * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: t1pci.c,v $ + * Revision 1.9 2000/05/19 15:43:22 calle + * added calls to pci_device_start(). + * + * Revision 1.8 2000/05/06 00:52:36 kai + * merged changes from kernel tree + * fixed timer and net_device->name breakage + * * Revision 1.7 2000/04/07 15:26:55 calle * better error message if cabel not connected or T1 has no power. * @@ -49,12 +56,13 @@ #include <linux/pci.h> #include <linux/capi.h> #include <asm/io.h> +#include <linux/isdn.h> #include "capicmd.h" #include "capiutil.h" #include "capilli.h" #include "avmcard.h" -static char *revision = "$Revision: 1.7 $"; +static char *revision = "$Revision: 1.9 $"; #undef CONFIG_T1PCI_DEBUG #undef CONFIG_T1PCI_POLLDEBUG @@ -305,12 +313,21 @@ int t1pci_init(void) while ((dev = pci_find_device(PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_T1, dev))) { struct capicardparams param; - if (pci_enable_device(dev)) - continue; - - param.port = pci_resource_start (dev, 1); + param.port = pci_resource_start(dev, 1); param.irq = dev->irq; - param.membase = pci_resource_start (dev, 0); + param.membase = pci_resource_start(dev, 0); + + retval = pci_enable_device (dev); + if (retval != 0) { + printk(KERN_ERR + "%s: failed to enable AVM-T1-PCI at i/o %#x, irq %d, mem %#x err=%d\n", + driver->name, param.port, param.irq, param.membase, retval); +#ifdef MODULE + cleanup_module(); +#endif + MOD_DEC_USE_COUNT; + return -EIO; + } printk(KERN_INFO "%s: PCI BIOS reports AVM-T1-PCI at i/o %#x, irq %d, mem %#x\n", diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c index 776345f21..3f74c6537 100644 --- a/drivers/net/3c59x.c +++ b/drivers/net/3c59x.c @@ -1452,7 +1452,6 @@ static void vortex_timer(unsigned long data) mod_timer(&vp->timer, RUN_AT(next_tick)); if (vp->deferred) outw(FakeIntr, ioaddr + EL3_CMD); - timer_exit(&vp->timer); return; } diff --git a/drivers/net/8390.c b/drivers/net/8390.c index 1957b189f..9bbaf9631 100644 --- a/drivers/net/8390.c +++ b/drivers/net/8390.c @@ -1144,14 +1144,14 @@ static void NS8390_trigger_send(struct net_device *dev, unsigned int length, outb_p(E8390_NODMA+E8390_TRANS+E8390_START, e8390_base+E8390_CMD); } -#ifdef MODULE - EXPORT_SYMBOL(ei_open); EXPORT_SYMBOL(ei_close); EXPORT_SYMBOL(ei_interrupt); EXPORT_SYMBOL(ethdev_init); EXPORT_SYMBOL(NS8390_init); +#if defined(MODULE) + int init_module(void) { return 0; diff --git a/drivers/net/Config.in b/drivers/net/Config.in index 5084ea70e..2238ce5a9 100644 --- a/drivers/net/Config.in +++ b/drivers/net/Config.in @@ -81,9 +81,7 @@ if [ "$CONFIG_NET_ETHERNET" = "y" ]; then fi tristate ' SMC Ultra support' CONFIG_ULTRA tristate ' SMC Ultra32 EISA support' CONFIG_ULTRA32 - if [ "$CONFIG_OBSOLETE" = "y" ]; then - tristate ' SMC 9194 support' CONFIG_SMC9194 - fi + tristate ' SMC 9194 support' CONFIG_SMC9194 fi bool ' Racal-Interlan (Micom) NI cards' CONFIG_NET_VENDOR_RACAL if [ "$CONFIG_NET_VENDOR_RACAL" = "y" ]; then @@ -116,7 +114,7 @@ if [ "$CONFIG_NET_ETHERNET" = "y" ]; then if [ "$CONFIG_OBSOLETE" = "y" ]; then tristate ' SEEQ8005 support (EXPERIMENTAL)' CONFIG_SEEQ8005 fi - bool ' SK_G16 support' CONFIG_SK_G16 + tristate ' SK_G16 support' CONFIG_SK_G16 fi if [ "$CONFIG_MCA" = "y" ]; then tristate ' SKnet MCA support' CONFIG_SKMC diff --git a/drivers/net/am79c961a.h b/drivers/net/am79c961a.h index 377c1b5d1..b2c549334 100644 --- a/drivers/net/am79c961a.h +++ b/drivers/net/am79c961a.h @@ -73,7 +73,8 @@ #define MODE_COLL 0x0010 #define MODE_DRETRY 0x0020 #define MODE_INTLOOP 0x0040 -#define MODE_PORT0 0x0080 +#define MODE_PORT_AUI 0x0000 +#define MODE_PORT_10BT 0x0080 #define MODE_DRXPA 0x2000 #define MODE_DRXBA 0x4000 #define MODE_PROMISC 0x8000 @@ -105,6 +106,7 @@ #define TST_LCAR 0x0800 #define TST_LCOL 0x1000 #define TST_UFLO 0x4000 +#define TST_BUFF 0x8000 struct dev_priv { struct enet_statistics stats; diff --git a/drivers/net/arcnet/arcnet.c b/drivers/net/arcnet/arcnet.c index 5bfa64f95..dd65e6c4e 100644 --- a/drivers/net/arcnet/arcnet.c +++ b/drivers/net/arcnet/arcnet.c @@ -672,21 +672,20 @@ static void arcnet_timeout(struct net_device *dev) unsigned long flags; struct arcnet_local *lp = (struct arcnet_local *) dev->priv; int status = ASTATUS(); + char *msg; save_flags(flags); cli(); if (status & TXFREEflag) { /* transmit _DID_ finish */ - BUGMSG(D_NORMAL, "tx timeout - missed IRQ? (status=%Xh, mask=%Xh, dest=%02Xh)\n", - status, lp->intmask, lp->lasttrans_dest); - lp->stats.tx_errors++; + msg = " - missed IRQ?"; } else { - BUGMSG(D_EXTRA, "tx timed out (status=%Xh, intmask=%Xh, dest=%02Xh)\n", - status, lp->intmask, lp->lasttrans_dest); - lp->stats.tx_errors++; + msg = ""; lp->stats.tx_aborted_errors++; + lp->timed_out = 1; ACOMMAND(NOTXcmd | (lp->cur_tx << 3)); } + lp->stats.tx_errors++; /* make sure we didn't miss a TX IRQ */ AINTMASK(0); @@ -694,6 +693,12 @@ static void arcnet_timeout(struct net_device *dev) AINTMASK(lp->intmask); restore_flags(flags); + + if (jiffies - lp->last_timeout > 10*HZ) { + BUGMSG(D_EXTRA, "tx timed out%s (status=%Xh, intmask=%Xh, dest=%02Xh)\n", + msg, status, lp->intmask, lp->lasttrans_dest); + lp->last_timeout = jiffies; + } } @@ -778,12 +783,12 @@ void arcnet_interrupt(int irq, void *dev_id, struct pt_regs *regs) didsomething++; } /* a transmit finished, and we're interested in it. */ - if (status & lp->intmask & TXFREEflag) { + if ((status & lp->intmask & TXFREEflag) || lp->timed_out) { lp->intmask &= ~TXFREEflag; BUGMSG(D_DURING, "TX IRQ (stat=%Xh)\n", status); - if (lp->cur_tx != -1 && !(status & TXACKflag)) { + if (lp->cur_tx != -1 && !(status & TXACKflag) && !lp->timed_out) { if (lp->lasttrans_dest != 0) { BUGMSG(D_EXTRA, "transmit was not acknowledged! " "(status=%Xh, dest=%02Xh)\n", @@ -801,6 +806,7 @@ void arcnet_interrupt(int irq, void *dev_id, struct pt_regs *regs) release_arcbuf(dev, lp->cur_tx); lp->cur_tx = -1; + lp->timed_out = 0; didsomething++; /* send another packet if there is one */ diff --git a/drivers/net/arcnet/com90xx.c b/drivers/net/arcnet/com90xx.c index 97f8d0a41..1bc6f6667 100644 --- a/drivers/net/arcnet/com90xx.c +++ b/drivers/net/arcnet/com90xx.c @@ -562,7 +562,8 @@ int com90xx_reset(struct net_device *dev, int really_reset) /* verify that the ARCnet signature byte is present */ if (readb(lp->mem_start) != TESTvalue) { - BUGMSG(D_NORMAL, "reset failed: TESTvalue not present.\n"); + if (really_reset) + BUGMSG(D_NORMAL, "reset failed: TESTvalue not present.\n"); return 1; } /* enable extended (512-byte) packets */ diff --git a/drivers/net/eepro.c b/drivers/net/eepro.c index fa7a5befd..324cf3934 100644 --- a/drivers/net/eepro.c +++ b/drivers/net/eepro.c @@ -23,6 +23,7 @@ This is a compatibility hardware problem. Versions: + 0.12b misc fixes (aris, 06/26/2000) 0.12a port of version 0.12a of 2.2.x kernels to 2.3.x (aris (aris@conectiva.com.br), 05/19/2000) 0.11e some tweaks about multiple cards support (PdP, jul/aug 1999) @@ -95,7 +96,7 @@ */ static const char *version = - "eepro.c: v0.12a 04/26/2000 aris@conectiva.com.br\n"; + "eepro.c: v0.12b 04/26/2000 aris@conectiva.com.br\n"; #include <linux/module.h> @@ -509,6 +510,20 @@ static unsigned eeprom_reg = EEPROM_REG_PRO; /* ack for tx int */ #define eepro_ack_tx(ioaddr) outb (TX_INT, ioaddr + STATUS_REG) +/* a complete sel reset */ +#define eepro_complete_selreset(ioaddr) { eepro_dis_int(ioaddr);\ + lp->stats.tx_errors++;\ + eepro_sel_reset(ioaddr);\ + lp->tx_end = \ + (XMT_LOWER_LIMIT << 8);\ + lp->tx_start = lp->tx_end;\ + lp->tx_last = 0;\ + dev->trans_start = jiffies;\ + netif_wake_queue(dev);\ + eepro_en_int(ioaddr);\ + eepro_en_rx(ioaddr);\ + } + /* Check for a network adaptor of this type, and return '0' if one exists. If dev->base_addr == 0, probe all likely locations. If dev->base_addr == 1, always return failure. @@ -738,7 +753,8 @@ int eepro_probe1(struct net_device *dev, short ioaddr) */ if (net_debug > 3) - printk(", %dK RCV buffer", (int)(dev->mem_end)/1024); + printk(", %dK RCV buffer", (int)(dev->mem_end - + dev->mem_start)/1024); /* ............... */ @@ -1085,23 +1101,7 @@ static void eepro_tx_timeout (struct net_device *dev) one for the the log file */ printk (KERN_DEBUG "%s: transmit timed out, %s?\n", dev->name, "network cable problem"); - lp->stats.tx_errors++; - - /* Try to restart the adaptor. */ - eepro_sel_reset(ioaddr); - - /* Do I also need to flush the transmit buffers here? YES? */ - lp->tx_start = lp->tx_end = XMT_LOWER_LIMIT; - lp->tx_last = 0; - - dev->trans_start = jiffies; - netif_wake_queue (dev); - - /* enabling interrupts */ - eepro_en_int(ioaddr); - - /* enabling rx */ - eepro_en_rx(ioaddr); + eepro_complete_selreset(ioaddr); } @@ -1375,7 +1375,7 @@ set_multicast_list(struct net_device *dev) /* Re-enable RX and TX interrupts */ eepro_en_int(ioaddr); } - eepro_en_rx(ioaddr); + eepro_complete_selreset(ioaddr); } /* The horrible routine to read a word from the serial EEPROM. */ @@ -1481,7 +1481,8 @@ hardware_send_packet(struct net_device *dev, void *buf, short length) end = last + (((length + 3) >> 1) << 1) + XMT_HEADER; } - else end = (XMT_LOWER_LIMIT << 8) + (end - XMT_RAM); + else end = (XMT_LOWER_LIMIT << 8) + (end - + (XMT_UPPER_LIMIT <<8)); } outw(last, ioaddr + HOST_ADDRESS_REG); outw(XMT_CMD, ioaddr + IO_PORT); @@ -1542,8 +1543,6 @@ hardware_send_packet(struct net_device *dev, void *buf, short length) printk(KERN_DEBUG "%s: exiting hardware_send_packet routine.\n", dev->name); return; } - eepro_en_int(ioaddr); - netif_stop_queue(dev); if (net_debug > 5) printk(KERN_DEBUG "%s: exiting hardware_send_packet routine.\n", dev->name); @@ -1561,9 +1560,6 @@ eepro_rx(struct net_device *dev) if (net_debug > 5) printk(KERN_DEBUG "%s: entering eepro_rx routine.\n", dev->name); - /* clear all interrupts */ - eepro_clear_int(ioaddr); - /* Set the read pointer to the start of the RCV */ outw(rcv_car, ioaddr + HOST_ADDRESS_REG); @@ -1642,9 +1638,6 @@ eepro_rx(struct net_device *dev) if (net_debug > 5) printk(KERN_DEBUG "%s: exiting eepro_rx routine.\n", dev->name); - - /* enable tx/rx interrupts */ - eepro_en_int(ioaddr); } static void @@ -1734,6 +1727,12 @@ eepro_transmit_interrupt(struct net_device *dev) boguscount--; } + /* if it reached here then it's probable that the adapter won't + * interrupt again for tx. in other words: tx timeout what will take + * a lot of time to happen, so we'll do a complete selreset. + */ + if (!boguscount) + eepro_complete_selreset(ioaddr); } #define MAX_EEPRO 8 diff --git a/drivers/net/eepro100.c b/drivers/net/eepro100.c index 10477c624..27a6b188c 100644 --- a/drivers/net/eepro100.c +++ b/drivers/net/eepro100.c @@ -1145,16 +1145,11 @@ static void speedo_timer(unsigned long data) /* We must continue to monitor the media. */ sp->timer.expires = RUN_AT(2*HZ); /* 2.0 sec. */ add_timer(&sp->timer); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,43) - timer_exit(&sp->timer); -#endif /* LINUX_VERSION_CODE */ } static void speedo_show_state(struct net_device *dev) { struct speedo_private *sp = (struct speedo_private *)dev->priv; - long ioaddr = dev->base_addr; - int phy_num = sp->phy[0] & 0x1f; int i; /* Print a few items for debugging. */ @@ -1181,6 +1176,8 @@ static void speedo_show_state(struct net_device *dev) (unsigned)sp->rx_ringp[i]->status : 0); #if 0 + long ioaddr = dev->base_addr; + int phy_num = sp->phy[0] & 0x1f; for (i = 0; i < 16; i++) { /* FIXME: what does it mean? --SAW */ if (i == 6) i = 21; diff --git a/drivers/net/epic100.c b/drivers/net/epic100.c index 0a5f229b9..f1723670c 100644 --- a/drivers/net/epic100.c +++ b/drivers/net/epic100.c @@ -25,6 +25,9 @@ LK1.1.2 (jgarzik): * Merge becker version 1.09 + LK1.1.3: + * Major bugfix to 1.09 driver (Francis Romieu) + */ /* The user-configurable values. @@ -90,7 +93,7 @@ static int rx_copybreak = 200; /* These identify the driver base version and may not be removed. */ static char version[] __devinitdata = -"epic100.c:v1.09+LK1.1.2 4/28/2000 Written by Donald Becker <becker@scyld.com>\n"; +"epic100.c:v1.09+LK1.1.3 6/17/2000 Written by Donald Becker <becker@scyld.com>\n"; static char version2[] __devinitdata = " http://www.scyld.com/network/epic100.html\n"; @@ -576,6 +579,7 @@ static int epic_open(struct net_device *dev) struct epic_private *ep = (struct epic_private *)dev->priv; long ioaddr = dev->base_addr; int i; + int retval; ep->full_duplex = ep->force_fd; @@ -584,9 +588,9 @@ static int epic_open(struct net_device *dev) MOD_INC_USE_COUNT; - if (request_irq(dev->irq, &epic_interrupt, SA_SHIRQ, dev->name, dev)) { + if ((retval = request_irq(dev->irq, &epic_interrupt, SA_SHIRQ, dev->name, dev))) { MOD_DEC_USE_COUNT; - return -EAGAIN; + return retval; } epic_init_ring(dev); @@ -1142,8 +1146,8 @@ static int epic_close(struct net_device *dev) printk(KERN_DEBUG "%s: Shutting down ethercard, status was %2.2x.\n", dev->name, inl(ioaddr + INTSTAT)); + del_timer_sync(&ep->timer); epic_pause(dev); - del_timer(&ep->timer); free_irq(dev->irq, dev); /* Free all the skbuffs in the Rx queue. */ diff --git a/drivers/net/shaper.c b/drivers/net/shaper.c index acc1fcd46..b476e290c 100644 --- a/drivers/net/shaper.c +++ b/drivers/net/shaper.c @@ -300,7 +300,6 @@ static void shaper_timer(unsigned long data) { struct shaper *sh=(struct shaper *)data; shaper_kick(sh); - timer_exit(&sh->timer); } /* diff --git a/drivers/net/sk_g16.c b/drivers/net/sk_g16.c index 0699fc879..0f0d15518 100644 --- a/drivers/net/sk_g16.c +++ b/drivers/net/sk_g16.c @@ -19,6 +19,8 @@ * Paul Gortmaker, 03/97: Fix for v2.1.x to use read{b,w} * write{b,w} and memcpy -> memcpy_{to,from}io * + * Jeff Garzik, 06/2000, Modularize + * -*/ static const char *rcsid = "$Id: sk_g16.c,v 1.1 1994/06/30 16:25:15 root Exp $"; @@ -53,8 +55,10 @@ static const char *rcsid = "$Id: sk_g16.c,v 1.1 1994/06/30 16:25:15 root Exp $"; * - Try to find out if the board is in 8 Bit or 16 Bit slot. * If in 8 Bit mode don't use IRQ 11. * - (Try to make it slightly faster.) + * - Power management support */ +#include <linux/module.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/ptrace.h> @@ -455,7 +459,8 @@ struct priv /* static variables */ static SK_RAM *board; /* pointer to our memory mapped board components */ - +static struct net_device *SK_dev; +unsigned long SK_ioaddr; static spinlock_t SK_lock = SPIN_LOCK_UNLOCKED; /* Macros */ @@ -537,7 +542,7 @@ void SK_print_ram(struct net_device *dev); int __init SK_init(struct net_device *dev) { - int ioaddr = 0; /* I/O port address used for POS regs */ + int ioaddr; /* I/O port address used for POS regs */ int *port, ports[] = SK_IO_PORTS; /* SK_G16 supported ports */ /* get preconfigured base_addr from dev which is done in Space.c */ @@ -548,15 +553,23 @@ int __init SK_init(struct net_device *dev) if (base_addr > 0x0ff) /* Check a single specified address */ { + int rc = -ENODEV; + + ioaddr = base_addr; + /* Check if on specified address is a SK_G16 */ + if (!request_region(ioaddr, ETHERCARD_TOTAL_SIZE, "sk_g16")) + return -EBUSY; if ( (inb(SK_POS0) == SK_IDLOW) || (inb(SK_POS1) == SK_IDHIGH) ) { - return SK_probe(dev, base_addr); + rc = SK_probe(dev, ioaddr); } - return -ENODEV; /* Sorry, but on specified address NO SK_G16 */ + if (rc) + release_region(ioaddr, ETHERCARD_TOTAL_SIZE); + return rc; } else if (base_addr > 0) /* Don't probe at all */ { @@ -571,7 +584,7 @@ int __init SK_init(struct net_device *dev) /* Check if I/O Port region is used by another board */ - if (check_region(ioaddr, ETHERCARD_TOTAL_SIZE)) + if (!request_region(ioaddr, ETHERCARD_TOTAL_SIZE, "sk_g16")) { continue; /* Try next Port address */ } @@ -581,6 +594,7 @@ int __init SK_init(struct net_device *dev) if ( !(inb(SK_POS0) == SK_IDLOW) || !(inb(SK_POS1) == SK_IDHIGH) ) { + release_region(ioaddr, ETHERCARD_TOTAL_SIZE); continue; /* Try next Port address */ } @@ -590,6 +604,8 @@ int __init SK_init(struct net_device *dev) { return 0; /* Card found and initialized */ } + + release_region(ioaddr, ETHERCARD_TOTAL_SIZE); } dev->base_addr = base_addr; /* Write back original base_addr */ @@ -598,6 +614,60 @@ int __init SK_init(struct net_device *dev) } /* End of SK_init */ + +static int io = 0; /* 0 == probe */ +MODULE_AUTHOR("Patrick J.D. Weichmann"); +MODULE_DESCRIPTION("Schneider & Koch G16 Ethernet Device Driver"); +MODULE_PARM(io, "i"); +MODULE_PARM_DESC(io, "0 to probe common ports (unsafe), or the I/O base of the board"); + + +#ifdef MODULE +static int __init SK_init_module (void) +{ + int rc; + + SK_dev = init_etherdev (NULL, 0); + if (!SK_dev) + return -ENOMEM; + + rc = SK_init (SK_dev); + if (rc) { + unregister_netdev (SK_dev); + kfree (SK_dev); + SK_dev = NULL; + } + + return rc; +} +#endif /* MODULE */ + + +static void __exit SK_cleanup_module (void) +{ + if (SK_dev) { + if (SK_dev->priv) { + kfree(SK_dev->priv); + SK_dev->priv = NULL; + } + unregister_netdev(SK_dev); + kfree(SK_dev); + SK_dev = NULL; + } + if (SK_ioaddr) { + release_region(SK_ioaddr, ETHERCARD_TOTAL_SIZE); + SK_ioaddr = 0; + } + +} + + +#ifdef MODULE +module_init(SK_init_module); +#endif +module_exit(SK_cleanup_module); + + /*- * Function : SK_probe @@ -774,9 +844,6 @@ int __init SK_probe(struct net_device *dev, short ioaddr) } memset((char *) dev->priv, 0, sizeof(struct priv)); /* clear memory */ - /* Grab the I/O Port region */ - request_region(ioaddr, ETHERCARD_TOTAL_SIZE,"sk_g16"); - /* Assign our Device Driver functions */ dev->open = SK_open; @@ -817,6 +884,9 @@ int __init SK_probe(struct net_device *dev, short ioaddr) SK_print_ram(dev); #endif + SK_dev = dev; + SK_ioaddr = ioaddr; + return 0; /* Initialization done */ } /* End of SK_probe() */ @@ -1078,8 +1148,7 @@ static int SK_lance_init(struct net_device *dev, unsigned short mode) /* Prepare LANCE Control and Status Registers */ - save_flags(flags); - cli(); + spin_lock_irqsave(&SK_lock, flags); SK_write_reg(CSR3, CSR3_ACON); /* Ale Control !!!THIS MUST BE SET!!!! */ @@ -1114,7 +1183,7 @@ static int SK_lance_init(struct net_device *dev, unsigned short mode) SK_write_reg(CSR0, CSR0_INIT); - restore_flags(flags); + spin_unlock_irqrestore(&SK_lock, flags); /* Wait until LANCE finished initialization */ diff --git a/drivers/net/sk_mca.c b/drivers/net/sk_mca.c index f28196a2f..1b8e48e0f 100644 --- a/drivers/net/sk_mca.c +++ b/drivers/net/sk_mca.c @@ -95,10 +95,8 @@ History: #include <asm/bitops.h> #include <asm/io.h> -#ifdef MODULE #include <linux/module.h> #include <linux/version.h> -#endif #include <linux/netdevice.h> #include <linux/etherdevice.h> diff --git a/drivers/net/slip.c b/drivers/net/slip.c index 9e395d361..51e2d6494 100644 --- a/drivers/net/slip.c +++ b/drivers/net/slip.c @@ -1479,7 +1479,6 @@ static void sl_outfill(unsigned long sls) } out: spin_unlock(&sl->lock); - timer_exit(&sl->outfill_timer); } static void sl_keepalive(unsigned long sls) @@ -1511,7 +1510,6 @@ static void sl_keepalive(unsigned long sls) out: spin_unlock(&sl->lock); - timer_exit(&sl->keepalive_timer); } #endif diff --git a/drivers/net/starfire.c b/drivers/net/starfire.c index b5f70ff16..85089a8ea 100644 --- a/drivers/net/starfire.c +++ b/drivers/net/starfire.c @@ -26,6 +26,9 @@ LK1.1.2 (jgarzik): - Merge Becker version 0.15 + + LK1.1.3 (Andrew Morton) + - Timer cleanups */ /* The user-configurable values. @@ -100,7 +103,7 @@ static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; /* These identify the driver base version and may not be removed. */ static char version1[] __devinitdata = -"starfire.c:v0.15+LK1.1.2 4/28/2000 Written by Donald Becker <becker@scyld.com>\n"; +"starfire.c:v0.15+LK1.1.3 6/17/2000 Written by Donald Becker <becker@scyld.com>\n"; static char version2[] __devinitdata = " Updates and info at http://www.scyld.com/network/starfire.html\n"; @@ -536,15 +539,16 @@ static int netdev_open(struct net_device *dev) { struct netdev_private *np = (struct netdev_private *)dev->priv; long ioaddr = dev->base_addr; - int i; + int i, retval; /* Do we ever need to reset the chip??? */ MOD_INC_USE_COUNT; - if (request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev)) { + retval = request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev); + if (retval) { MOD_DEC_USE_COUNT; - return -EAGAIN; + return retval; } /* Disable the Rx and Tx, and reset the chip. */ @@ -1260,6 +1264,8 @@ static int netdev_close(struct net_device *dev) netif_stop_queue(dev); + del_timer_sync(&np->timer); + if (debug > 1) { printk(KERN_DEBUG "%s: Shutting down ethercard, status was Int %4.4x.\n", dev->name, readl(ioaddr + IntrStatus)); @@ -1272,8 +1278,6 @@ static int netdev_close(struct net_device *dev) /* Stop the chip's Tx and Rx processes. */ - del_timer(&np->timer); - #ifdef __i386__ if (debug > 2) { printk("\n"KERN_DEBUG" Tx ring at %8.8x:\n", diff --git a/drivers/net/sunlance.c b/drivers/net/sunlance.c index ada2ba5ca..ae14cf96d 100644 --- a/drivers/net/sunlance.c +++ b/drivers/net/sunlance.c @@ -1290,7 +1290,6 @@ static void lance_set_multicast_retry(unsigned long _opaque) struct lance_private *lp = (struct lance_private *) dev->priv; lance_set_multicast(dev); - timer_exit(&lp->multicast_timer); } static void lance_free_hwresources(struct lance_private *lp) diff --git a/drivers/net/tulip/21142.c b/drivers/net/tulip/21142.c index 24d0d49c9..8398e1bea 100644 --- a/drivers/net/tulip/21142.c +++ b/drivers/net/tulip/21142.c @@ -198,7 +198,7 @@ void t21142_lnk_change(struct net_device *dev, int csr5) && (csr12 & 2) == 2) || (tp->nway && (csr5 & (TPLnkFail)))) { /* Link blew? Maybe restart NWay. */ - del_timer(&tp->timer); + del_timer_sync(&tp->timer); t21142_start_nway(dev); tp->timer.expires = RUN_AT(3*HZ); add_timer(&tp->timer); @@ -208,7 +208,7 @@ void t21142_lnk_change(struct net_device *dev, int csr5) dev->name, medianame[dev->if_port], (csr12 & 2) ? "failed" : "good"); if ((csr12 & 2) && ! tp->medialock) { - del_timer(&tp->timer); + del_timer_sync(&tp->timer); t21142_start_nway(dev); tp->timer.expires = RUN_AT(3*HZ); add_timer(&tp->timer); diff --git a/drivers/net/tulip/interrupt.c b/drivers/net/tulip/interrupt.c index 918357aff..39f87f7b7 100644 --- a/drivers/net/tulip/interrupt.c +++ b/drivers/net/tulip/interrupt.c @@ -298,6 +298,10 @@ void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs) tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; tulip_outl_csr(tp, tp->csr6 | csr6_st | csr6_sr, CSR6); } + /* + * NB: t21142_lnk_change() does a del_timer_sync(), so be careful if this + * call is ever done under the spinlock + */ if (csr5 & (TPLnkPass | TPLnkFail | 0x08000000)) { if (tp->link_change) (tp->link_change)(dev, csr5); diff --git a/drivers/net/tulip/tulip_core.c b/drivers/net/tulip/tulip_core.c index ae777d165..6b0c7dd15 100644 --- a/drivers/net/tulip/tulip_core.c +++ b/drivers/net/tulip/tulip_core.c @@ -19,7 +19,7 @@ */ -static const char version[] = "Linux Tulip driver version 0.9.6 (May 31, 2000)\n"; +static const char version[] = "Linux Tulip driver version 0.9.7 (June 17, 2000)\n"; #include <linux/module.h> #include "tulip.h" @@ -401,11 +401,12 @@ media_picked: static int tulip_open(struct net_device *dev) { + int retval; MOD_INC_USE_COUNT; - - if (request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ, dev->name, dev)) { + + if ((retval = request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ, dev->name, dev))) { MOD_DEC_USE_COUNT; - return -EBUSY; + return retval; } tulip_init_ring (dev); @@ -639,7 +640,7 @@ static void tulip_down (struct net_device *dev) struct tulip_private *tp = (struct tulip_private *) dev->priv; unsigned long flags; - del_timer (&tp->timer); + del_timer_sync (&tp->timer); spin_lock_irqsave (&tp->lock, flags); diff --git a/drivers/pci/pci.ids b/drivers/pci/pci.ids index 9219557f4..068c2fd14 100644 --- a/drivers/pci/pci.ids +++ b/drivers/pci/pci.ids @@ -4,7 +4,7 @@ # Maintained by Martin Mares <pci-ids@ucw.cz> # If you have any new entries, send them to the maintainer. # -# $Id: pci.ids,v 1.60 2000/05/01 19:53:05 mj Exp $ +# $Id: pci.ids,v 1.62 2000/06/28 10:56:36 mj Exp $ # # Vendors, devices and subsystems. Please keep sorted. @@ -406,6 +406,7 @@ 1014 0143 Yotta Input Controller (ytin) 0144 Yotta Video Compositor Output 1014 0145 Yotta Output Controller (ytout) + 0156 405GP PLB to PCI Bridge ffff MPIC-2 interrupt controller 1015 LSI Logic Corp of Canada 1016 ICL Personal Systems @@ -570,6 +571,10 @@ 0001 PowerEdge Expandable RAID Controller 2/Si 0002 PowerEdge Expandable RAID Controller 3/Di 0003 PowerEdge Expandable RAID Controller 3/Si + 0004 PowerEdge Expandable RAID Controller 3/Si + 0005 PowerEdge Expandable RAID Controller 3/Di + 0006 PowerEdge Expandable RAID Controller 3/Di + 0008 PowerEdge Expandable RAID Controller 3/Di 1029 Siemens Nixdorf IS 102a LSI Logic 0000 HYDRA @@ -919,6 +924,7 @@ 8009 CXD1947Q i.LINK Controller 8039 CXD3222 i.LINK Controller 8056 Rockwell HCF 56K modem + 808a Memory Stick Controller 104e Oak Technology, Inc 0017 OTI-64017 0107 OTI-107 [Spitfire] @@ -1250,6 +1256,7 @@ 036c Bt879(??) Video Capture 13e9 0070 Win/TV (Video Section) 036e Bt878 + 0070 13eb WinTV/GO 127a 0001 Bt878 Mediastream Controller NTSC 127a 0002 Bt878 Mediastream Controller PAL BG 127a 0003 Bt878a Mediastream Controller PAL BG @@ -1297,6 +1304,7 @@ 1851 1851 FlyVideo'98 EZ - video 1852 1852 FlyVideo'98 (with FM Tuner) 0878 Bt878 + 0070 13eb WinTV/GO 127a 0001 Bt878 Video Capture (Audio Section) 127a 0002 Bt878 Video Capture (Audio Section) 127a 0003 Bt878 Video Capture (Audio Section) @@ -1461,8 +1469,6 @@ 5055 3c555 Laptop Hurricane 5057 3c575 [Megahertz] 10/100 LAN CardBus 10b7 5a57 3C575 Megahertz 10/100 LAN Cardbus PC Card - 5b57 3c575 [Megahertz] 10/100 LAN CardBus - 10b7 5a57 3C575 Megahertz 10/100 LAN Cardbus 5157 3c575 [Megahertz] 10/100 LAN CardBus 10b7 5b57 3C575 Megahertz 10/100 LAN Cardbus PC Card 5257 3CCFE575CT Cyclone CardBus @@ -1472,9 +1478,11 @@ 5951 3c595 100BaseT4 [Vortex] 5952 3c595 100Base-MII [Vortex] 5970 3c597 EISA Fast Demon/Vortex + 5b57 3c595 [Megahertz] 10/100 LAN CardBus + 10b7 5b57 3C575 Megahertz 10/100 LAN Cardbus PC Card 6560 3CCFE656 Cyclone CardBus - 6562 3CCFEM656 Cyclone CardBus - 6564 3CCFEM656 Cyclone CardBus (0x6564) + 6562 3CCFEM656 [id 6562] Cyclone CardBus + 6564 3CCFEM656 [id 6564] Cyclone CardBus 7646 3cSOHO100-TX Hurricane 8811 Token ring 9000 3c900 10BaseT [Boomerang] @@ -1509,14 +1517,15 @@ 1028 0098 3C905B Fast Etherlink XL 10/100 1028 0099 3C905B Fast Etherlink XL 10/100 10b7 9055 3C905B Fast Etherlink XL 10/100 + 9056 3c905B-T4 9058 3c905B-Combo [Deluxe Etherlink XL 10/100] 905a 3c905B-FX [Fast Etherlink XL FX 10/100] 9200 3c905C-TX [Fast Etherlink] 10b7 1000 3C905C-TX Fast Etherlink for PC Management NIC 9800 3c980-TX [Fast Etherlink XL Server Adapter] 10b7 9800 3c980-TX Fast Etherlink XL Server Adapter - 9805 3c980-TX [10/100 Base-TX NIC(Python-T)] - 10b7 9805 3c980 10/100 Base-TX NIC(Python-T) + 9805 3c980-TX 10/100baseTX NIC [Python-T] + 10b7 9805 3c980 10/100baseTX NIC [Python-T] 10b8 Standard Microsystems Corp [SMC] 0005 83C170QF 1055 e000 LANEPIC @@ -1683,6 +1692,7 @@ 0010 Mutara V08 [NV2] 0020 Riva TnT 128 [NV04] 1043 0200 V3400 TNT + 1048 0c18 Erazor II SGRAM 1092 0550 Viper V550 1092 0552 Viper V550 1092 4804 Viper V550 @@ -1751,11 +1761,13 @@ 1043 400a AGP-V6800 DDR SGRAM 1043 400b AGP-V6800 DDR SDRAM 1102 102e CT6971 GeForce 256 DDR + 14af 5021 3D Prophet DDR-DVI 0103 Quadro (GeForce 256 GL) 0110 NV11 0111 NV11 DDR 0113 NV11 GL 0150 NV15 (Geforce2 GTS) + 107d 2840 WinFast GeForce2 GTS with TV output 0151 NV15 DDR (Geforce2 GTS) 0152 NV15 Bladerunner (Geforce2 GTS) 0153 NV15 GL (Quadro2) @@ -2051,10 +2063,18 @@ 111a Efficient Networks, Inc 0000 155P-MF1 (FPGA) 0002 155P-MF1 (ASIC) - 0003 ENI-25P ATM Adapter + 0003 ENI-25P ATM 111a 0000 ENI-25p Miniport ATM Adapter - 0005 Speedstream 30xx ATM Adapter - 111a 0001 SS-3010 Miniport ATM Adapter + 0005 SpeedStream (LANAI) + 111a 0001 ENI-3010 ATM + 111a 0101 ENI-3010 ATM + 111a 0009 ENI-3060 ADSL (VPI=0) + 111a 0109 ENI-3060CO ADSL (VPI=0) + 111a 0809 ENI-3060 ADSL (VPI=0 or 8) + 111a 0909 ENI-3060CO ADSL (VPI=0 or 8) + 111a 0a09 ENI-3060 ADSL (VPI=<0..15>) + 0007 SpeedStream ADSL + 111a 1001 ENI-3061 ADSL [ASIC] 111b Teledyne Electronic Systems 111c Tricord Systems Inc. 0001 Powerbis Bridge @@ -2114,7 +2134,7 @@ 1133 e004 DIVA 2.0 U e010 DIVA Server BRI-2M 1133 e010 DIVA Server BRI-2M - e014 DIVA Server PRO-30M + e014 DIVA Server PRI-30M 1133 e014 DIVA Server PRI-30M 1134 Mercury Computer Systems 0001 Raceway Bridge @@ -2281,7 +2301,8 @@ 0008 CNB20HE 0009 CNB20LE 0010 CIOB30 - 0011 CMIC_HE + 0011 CMIC-HE + 0200 OSB4 0201 CSB5 1167 Mutoh Industries Inc 1168 Thine Electronics Inc @@ -2422,7 +2443,7 @@ 11ad f003 LNE100TX 11ad ffff LNE100TX 1385 f004 FA310TX - c115 LNE100TX Fast Ethernet Adapter + c115 LNE100TX [Linksys EtherFast 10/100] 11ae Aztech System Ltd 11af Avid Technology Inc. 11b0 V3 Semiconductor Inc. @@ -2723,7 +2744,17 @@ 1220 AMCC 5933 TMS320C80 DSP/Imaging board 1221 Contec Co., Ltd 1222 Ancor Communications, Inc. -1223 Heurikon/Computer Products +1223 Artesyn Communication Products + 0003 PM/Link + 0004 PM/T1 + 0005 PM/E1 + 0008 PM/SLS + 0009 BajaSpan Resource Target + 000a BajaSpan Section 0 + 000b BajaSpan Section 1 + 000c BajaSpan Section 2 + 000d BajaSpan Section 3 + 000e PM/PPC 1224 Interactive Images 1225 Power I/O, Inc. 1227 Tech-Source @@ -4506,25 +4537,25 @@ 1a21 82840 840 (Carmel) Chipset Host Bridge (Hub A) 1a23 82840 840 (Carmel) Chipset AGP Bridge 1a24 82840 840 (Carmel) Chipset PCI Bridge (Hub B) - 2410 82801AA 82810 Chipset ISA Bridge (LPC) - 2411 82801AA 82810 Chipset IDE - 2412 82801AA 82810 Chipset USB - 2413 82801AA 82810 Chipset SMBus - 2415 82801AA 82810 AC'97 Audio + 2410 82801AA ISA Bridge (LPC) + 2411 82801AA IDE + 2412 82801AA USB + 2413 82801AA SMBus + 2415 82801AA AC'97 Audio 11d4 0040 SoundMAX Integrated Digital Audio 11d4 0048 SoundMAX Integrated Digital Audio 11d4 5340 SoundMAX Integrated Digital Audio - 2416 82801AA 82810 AC'97 Modem - 2418 82801AA 82810 PCI Bridge - 2420 82801AB 82810 Chipset ISA Bridge (LPC) - 2421 82801AB 82810 Chipset IDE - 2422 82801AB 82810 Chipset USB - 2423 82801AB 82810 Chipset SMBus - 2425 82801AB 82810 AC'97 Audio + 2416 82801AA AC'97 Modem + 2418 82801AA PCI Bridge + 2420 82801AB ISA Bridge (LPC) + 2421 82801AB IDE + 2422 82801AB USB + 2423 82801AB SMBus + 2425 82801AB AC'97 Audio 11d4 0040 SoundMAX Integrated Digital Audio 11d4 0048 SoundMAX Integrated Digital Audio - 2426 82801AB 82810 AC'97 Modem - 2428 82801AB 82810 PCI Bridge + 2426 82801AB AC'97 Modem + 2428 82801AB PCI Bridge 2440 82820 820 (Camino 2) Chipset ISA Bridge (ICH2) 2442 82820 820 (Camino 2) Chipset USB (Hub A) 2443 82820 820 (Camino 2) Chipset SMBus diff --git a/drivers/pnp/isapnp.c b/drivers/pnp/isapnp.c index 2115cdac9..4b2aacce6 100644 --- a/drivers/pnp/isapnp.c +++ b/drivers/pnp/isapnp.c @@ -527,9 +527,11 @@ static void __init isapnp_add_irq_resource(struct pci_dev *dev, ptr->next = irq; else (*res)->irq = irq; +#ifdef CONFIG_PCI for (i=0; i<16; i++) if (irq->map & i) pcibios_penalize_isa_irq(i); +#endif } /* diff --git a/drivers/scsi/aic7xxx.c b/drivers/scsi/aic7xxx.c index d9f952ca6..7cbbc3f1f 100644 --- a/drivers/scsi/aic7xxx.c +++ b/drivers/scsi/aic7xxx.c @@ -9092,7 +9092,7 @@ aic7xxx_load_seeprom(struct aic7xxx_host *p, unsigned char *sxfrctl1) p->flags |= AHC_TERM_ENB_SE_LOW | AHC_TERM_ENB_SE_HIGH; } } - p->sc = *sc; + memcpy(&p->sc, sc, sizeof(struct seeprom_config)); } p->discenable = 0; diff --git a/drivers/scsi/gdth.c b/drivers/scsi/gdth.c index d04270af9..8f9d9216d 100644 --- a/drivers/scsi/gdth.c +++ b/drivers/scsi/gdth.c @@ -1493,7 +1493,7 @@ static int __init gdth_search_drives(int hanum) ha->more_proc = FALSE; if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,BOARD_INFO, INVALID_CHANNEL,sizeof(gdth_binfo_str))) { - ha->binfo = *(gdth_binfo_str *)ha->pscratch; + memcpy(&ha->binfo, (gdth_binfo_str *)ha->pscratch, sizeof(gdth_binfo_str)); if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,BOARD_FEATURES, INVALID_CHANNEL,sizeof(gdth_bfeat_str))) { TRACE2(("BOARD_INFO/BOARD_FEATURES supported\n")); diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index fd1a3ba72..ab934fb2d 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -672,7 +672,7 @@ static Scsi_Host_Template builtin_scsi_hosts[] = #endif /* Put I2O last so that host specific controllers always win */ #ifdef CONFIG_I2O_SCSI - I2OSCSI + I2OSCSI, #endif /* "Removable host adapters" below this line (Parallel Port/USB/other) */ #ifdef CONFIG_SCSI_PPA diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index bdc3e8002..a605233c0 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -12,7 +12,7 @@ Copyright 1992 - 2000 Kai Makisara email Kai.Makisara@metla.fi - Last modified: Sun Apr 23 23:41:32 2000 by makisara@kai.makisara.local + Last modified: Sat Jun 17 15:21:49 2000 by makisara@kai.makisara.local Some small formal changes - aeb, 950809 Last modified: 18-JAN-1998 Richard Gooch <rgooch@atnf.csiro.au> Devfs support @@ -65,10 +65,10 @@ #include "constants.h" -static int buffer_kbs = 0; -static int write_threshold_kbs = 0; +static int buffer_kbs; +static int write_threshold_kbs; static int max_buffers = (-1); -static int max_sg_segs = 0; +static int max_sg_segs; MODULE_AUTHOR("Kai Makisara"); MODULE_DESCRIPTION("SCSI Tape Driver"); @@ -138,7 +138,7 @@ static int st_max_sg_segs = ST_MAX_SG; static Scsi_Tape **scsi_tapes = NULL; -static int modes_defined = FALSE; +static int modes_defined; static ST_buffer *new_tape_buffer(int, int); static int enlarge_buffer(ST_buffer *, int, int); @@ -870,7 +870,7 @@ static int scsi_tape_open(struct inode *inode, struct file *filp) static int scsi_tape_flush(struct file *filp) { int result = 0, result2; - static unsigned char cmd[MAX_COMMAND_SIZE]; + unsigned char cmd[MAX_COMMAND_SIZE]; Scsi_Request *SRpnt; Scsi_Tape *STp; ST_mode *STm; @@ -1027,7 +1027,7 @@ static ssize_t ssize_t retval = 0; int write_threshold; int doing_write = 0; - static unsigned char cmd[MAX_COMMAND_SIZE]; + unsigned char cmd[MAX_COMMAND_SIZE]; const char *b_point; Scsi_Request *SRpnt = NULL; Scsi_Tape *STp; @@ -1380,7 +1380,7 @@ static ssize_t static long read_tape(Scsi_Tape *STp, long count, Scsi_Request ** aSRpnt) { int transfer, blks, bytes; - static unsigned char cmd[MAX_COMMAND_SIZE]; + unsigned char cmd[MAX_COMMAND_SIZE]; Scsi_Request *SRpnt; ST_mode *STm; ST_partstat *STps; @@ -2945,24 +2945,34 @@ static int st_ioctl(struct inode *inode, struct file *file, if (mtc.mt_op == MTSETPART) { if (!STp->can_partitions || - mtc.mt_count < 0 || mtc.mt_count >= ST_NBR_PARTITIONS) - return (-EINVAL); + mtc.mt_count < 0 || mtc.mt_count >= ST_NBR_PARTITIONS) { + retval = (-EINVAL); + goto out; + } if (mtc.mt_count >= STp->nbr_partitions && - (STp->nbr_partitions = nbr_partitions(STp)) < 0) - return (-EIO); - if (mtc.mt_count >= STp->nbr_partitions) - return (-EINVAL); + (STp->nbr_partitions = nbr_partitions(STp)) < 0) { + retval = (-EIO); + goto out; + } + if (mtc.mt_count >= STp->nbr_partitions) { + retval = (-EINVAL); + goto out; + } STp->new_partition = mtc.mt_count; retval = 0; goto out; } if (mtc.mt_op == MTMKPART) { - if (!STp->can_partitions) - return (-EINVAL); + if (!STp->can_partitions) { + retval = (-EINVAL); + goto out; + } if ((i = st_int_ioctl(STp, MTREW, 0)) < 0 || - (i = partition_tape(STp, mtc.mt_count)) < 0) - return i; + (i = partition_tape(STp, mtc.mt_count)) < 0) { + retval = i; + goto out; + } for (i = 0; i < ST_NBR_PARTITIONS; i++) { STp->ps[i].rw = ST_IDLE; STp->ps[i].at_sm = 0; diff --git a/drivers/sound/i810_audio.c b/drivers/sound/i810_audio.c index 1ac21c429..f04636a65 100644 --- a/drivers/sound/i810_audio.c +++ b/drivers/sound/i810_audio.c @@ -1627,12 +1627,11 @@ static int i810_release(struct inode *inode, struct file *file) state->card->free_pcm_channel(state->card, dmabuf->channel->num); } - kfree(state->card->states[state->virt]); - state->card->states[state->virt] = NULL; - state->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); - /* we're covered by the open_sem */ up(&state->open_sem); + + kfree(state->card->states[state->virt]); + state->card->states[state->virt] = NULL; return 0; } diff --git a/drivers/sound/vidc.c b/drivers/sound/vidc.c index 6342e511a..01fb315ab 100644 --- a/drivers/sound/vidc.c +++ b/drivers/sound/vidc.c @@ -22,6 +22,7 @@ #include <asm/dma.h> #include <asm/io.h> #include <asm/iomd.h> +#include <asm/irq.h> #include <asm/system.h> #include "sound_config.h" diff --git a/drivers/usb/Config.in b/drivers/usb/Config.in index 5032a9b75..8695c9529 100644 --- a/drivers/usb/Config.in +++ b/drivers/usb/Config.in @@ -48,10 +48,10 @@ comment 'USB Devices' dep_tristate ' USB Kodak DC-2xx Camera support' CONFIG_USB_DC2XX $CONFIG_USB if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then dep_tristate ' USB Mustek MDC800 Digital Camera support (EXPERIMENTAL)' CONFIG_USB_MDC800 $CONFIG_USB - dep_tristate ' USB Mass Storage support (EXPERIMENTAL)' CONFIG_USB_STORAGE $CONFIG_USB $CONFIG_SCSI - if [ "$CONFIG_USB_STORAGE" != "n" ]; then - bool ' USB Mass Storage verbose debug' CONFIG_USB_STORAGE_DEBUG - fi + fi + dep_tristate ' USB Mass Storage support' CONFIG_USB_STORAGE $CONFIG_USB $CONFIG_SCSI + if [ "$CONFIG_USB_STORAGE" != "n" ]; then + bool ' USB Mass Storage verbose debug' CONFIG_USB_STORAGE_DEBUG fi dep_tristate ' USS720 parport driver' CONFIG_USB_USS720 $CONFIG_USB $CONFIG_PARPORT dep_tristate ' DABUSB driver' CONFIG_USB_DABUSB $CONFIG_USB diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 7d95c0c98..f59b44497 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -7,7 +7,7 @@ SUB_DIRS := MOD_SUB_DIRS := $(SUB_DIRS) MOD_IN_SUB_DIRS := $(SUB_DIRS) -ALL_SUB_DIRS := $(SUB_DIRS) serial +ALL_SUB_DIRS := $(SUB_DIRS) serial storage # The target object and module list name. @@ -49,6 +49,15 @@ else endif endif +ifeq ($(CONFIG_USB_STORAGE),y) + SUB_DIRS += storage + obj-y += storage/usb-storage.o +else + ifeq ($(CONFIG_USB_STORAGE),m) + MOD_IN_SUB_DIRS += storage + endif +endif + # Each configuration option enables a list of files. @@ -73,8 +82,7 @@ obj-$(CONFIG_USB_PRINTER) += printer.o obj-$(CONFIG_USB_AUDIO) += audio.o obj-$(CONFIG_USB_IBMCAM) += ibmcam.o obj-$(CONFIG_USB_DC2XX) += dc2xx.o -obj-$(CONFIG_USB_MDC800) += mdc800.o -obj-$(CONFIG_USB_STORAGE) += usb-storage.o +obj-$(CONFIG_USB_MDC800) += mdc800.o obj-$(CONFIG_USB_USS720) += uss720.o obj-$(CONFIG_USB_DABUSB) += dabusb.o obj-$(CONFIG_USB_PLUSB) += plusb.o diff --git a/drivers/usb/microtek.c b/drivers/usb/microtek.c index 6586b8c40..70a50d91f 100644 --- a/drivers/usb/microtek.c +++ b/drivers/usb/microtek.c @@ -52,11 +52,10 @@ * you want it, just send mail. * * Status: - * - * This driver does not work properly yet. + * * Untested with multiple scanners. * Untested on SMP. - * Untested on UHCI. + * Untested on a bigendian machine. * * History: * @@ -97,6 +96,8 @@ * 20000602 Version 0.2.0 * 20000603 various cosmetic changes * 20000603 Version 0.2.1 + * 20000620 minor cosmetic changes + * 20000620 Version 0.2.2 */ #include <linux/module.h> @@ -146,7 +147,7 @@ static struct usb_driver mts_usb_driver = { /* Internal driver stuff */ -#define MTS_VERSION "0.2.1" +#define MTS_VERSION "0.2.2" #define MTS_NAME "microtek usb (rev " MTS_VERSION "): " #define MTS_WARNING(x...) \ @@ -409,7 +410,7 @@ static int mts_scsi_host_reset (Scsi_Cmnd *srb) MTS_DEBUG_GOT_HERE(); mts_debug_dump(desc); - usb_reset_device(desc->usb_dev); + usb_reset_device(desc->usb_dev); /*FIXME: untested on new reset code */ return 0; /* RANT why here 0 and not SUCCESS */ } @@ -519,7 +520,7 @@ static void mts_transfer_done( struct urb *transfer ) MTS_INT_INIT(); - context->srb->result &= MTS_MAX_CHUNK_MASK; + context->srb->result &= MTS_SCSI_ERR_MASK; context->srb->result |= (unsigned)context->status<<1; mts_transfer_cleanup(transfer); diff --git a/drivers/usb/microtek.h b/drivers/usb/microtek.h index 939234560..8dcf734d3 100644 --- a/drivers/usb/microtek.h +++ b/drivers/usb/microtek.h @@ -68,6 +68,5 @@ struct mts_desc { #define MTS_EP_IMAGE 0x3 #define MTS_EP_TOTAL 0x3 -#define MTS_MAX_CHUNK_MASK ~0x3fu -/*maximum amount the scanner will transmit at once */ +#define MTS_SCSI_ERR_MASK ~0x3fu diff --git a/drivers/usb/ov511.c b/drivers/usb/ov511.c index faf057c16..66f73d3c1 100644 --- a/drivers/usb/ov511.c +++ b/drivers/usb/ov511.c @@ -30,7 +30,7 @@ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -static const char version[] = "1.17"; +static const char version[] = "1.18"; #define __NO_VERSION__ @@ -49,10 +49,6 @@ static const char version[] = "1.17"; #include <asm/semaphore.h> #include <linux/wrapper.h> -#ifdef CONFIG_KMOD -#include <linux/kmod.h> -#endif - #include "ov511.h" #undef OV511_GBR422 /* Experimental -- sets the 7610 to GBR422 */ @@ -67,7 +63,7 @@ static const char version[] = "1.17"; #define DEFAULT_HEIGHT 480 #define GET_SEGSIZE(p) ((p) == VIDEO_PALETTE_GREY ? 256 : 384) -#define GET_DEPTH(p) ((p) == VIDEO_PALETTE_GREY ? 8 : 24) +#define GET_DEPTH(p) ((p) == VIDEO_PALETTE_GREY ? 8 : ((p) == VIDEO_PALETTE_YUV422 ? 8 : 24)) /* PARAMETER VARIABLES: */ static int autoadjust = 1; /* CCD dynamically changes exposure, etc... */ @@ -626,6 +622,7 @@ static void ov511_dump_i2c_regs(struct usb_device *dev) ov511_dump_i2c_range(dev, 0x00, 0x38); } +#if 0 static void ov511_dump_reg_range(struct usb_device *dev, int reg1, int regn) { int i; @@ -636,7 +633,6 @@ static void ov511_dump_reg_range(struct usb_device *dev, int reg1, int regn) } } -#if 0 static void ov511_dump_regs(struct usb_device *dev) { PDEBUG(1, "CAMERA INTERFACE REGS"); @@ -1088,7 +1084,7 @@ ov511_move_420_block(int yTL, int yTR, int yBL, int yBR, int u, int v, #ifdef OV511_GBR422 static void ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0, - int iOutY, int iOutUV, int iHalf, int iWidth) + int iOutY, int iOutUV, int iHalf, int iWidth) { int k, l, m; unsigned char *pIn; @@ -1138,7 +1134,7 @@ ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0, static void ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0, - int iOutY, int iOutUV, int iHalf, int iWidth) + int iOutY, int iOutUV, int iHalf, int iWidth) { #ifndef OV511_DUMPPIX int k, l, m; @@ -1231,6 +1227,139 @@ ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0, } #endif +static void +ov511_parse_data_yuv422(unsigned char *pIn0, unsigned char *pOut0, + int iOutY, int iOutUV, int iHalf, int iWidth) +{ + int k, l, m; + unsigned char *pIn; + unsigned char *pOut, *pOut1; + + /* Just copy the Y's if in the first stripe */ + if (!iHalf) { + pIn = pIn0 + 128; + pOut = pOut0 + iOutY; + for (k = 0; k < 4; k++) { + pOut1 = pOut; + for (l = 0; l < 8; l++) { + for (m = 0; m < 8; m++) { + *pOut1++ = (*pIn++) & 0xF0; + } + pOut1 += iWidth - 8; + } + pOut += 8; + } + } + + /* Use the first half of VUs to calculate value */ + pIn = pIn0; + pOut = pOut0 + iOutUV; + for (l = 0; l < 4; l++) { + for (m=0; m<8; m++) { + unsigned char *p00 = pOut; + unsigned char *p01 = pOut+1; + unsigned char *p10 = pOut+iWidth; + unsigned char *p11 = pOut+iWidth+1; + int v = *(pIn+64) - 128; + int u = *pIn++ - 128; + int uv = ((u >> 4) & 0x0C) + (v >> 6); + + *p00 |= uv; + *p01 |= uv; + *p10 |= uv; + *p11 |= uv; + + pOut += 2; + } + pOut += (iWidth*2 - 16); + } + + /* Just copy the other UV rows */ + for (l = 0; l < 4; l++) { + for (m = 0; m < 8; m++) { + int v = *(pIn + 64) - 128; + int u = (*pIn++) - 128; + int uv = ((u >> 4) & 0x0C) + (v >> 6); + *(pOut) = uv; + pOut += 2; + } + pOut += (iWidth*2 - 16); + } + + /* Calculate values if it's the second half */ + if (iHalf) { + pIn = pIn0 + 128; + pOut = pOut0 + iOutY; + for (k = 0; k < 4; k++) { + pOut1 = pOut; + for (l=0; l<4; l++) { + for (m=0; m<4; m++) { + int y10 = *(pIn+8); + int y00 = *pIn++; + int y11 = *(pIn+8); + int y01 = *pIn++; + int uv = *pOut1; + + *pOut1 = (y00 & 0xF0) | uv; + *(pOut1+1) = (y01 & 0xF0) | uv; + *(pOut1+iWidth) = (y10 & 0xF0) | uv; + *(pOut1+iWidth+1) = (y11 & 0xF0) | uv; + + pOut1 += 2; + } + pOut1 += (iWidth*2 - 8); + pIn += 8; + } + pOut += 8; + } + } +} + +static void +ov511_parse_data_yuv422p(unsigned char *pIn0, unsigned char *pOut0, + int iOutY, int iOutUV, int iWidth, int iHeight) +{ + int k, l, m; + unsigned char *pIn; + unsigned char *pOut, *pOut1; + unsigned a = iWidth * iHeight; + unsigned w = iWidth / 2; + + pIn = pIn0; + pOut = pOut0 + iOutUV + a; + for (k = 0; k < 8; k++) { + pOut1 = pOut; + for (l = 0; l < 8; l++) { + *pOut1 = *(pOut1 + w) = *pIn++; + pOut1++; + } + pOut += iWidth; + } + + pIn = pIn0 + 64; + pOut = pOut0 + iOutUV + a + a/2; + for (k = 0; k < 8; k++) { + pOut1 = pOut; + for (l = 0; l < 8; l++) { + *pOut1 = *(pOut1 + w) = *pIn++; + pOut1++; + } + pOut += iWidth; + } + + pIn = pIn0 + 128; + pOut = pOut0 + iOutY; + for (k = 0; k < 4; k++) { + pOut1 = pOut; + for (l = 0; l < 8; l++) { + for (m = 0; m < 8; m++) + *pOut1++ =*pIn++; + pOut1 += iWidth - 8; + } + pOut += 8; + } +} + /* * For 640x480 RAW BW images, data shows up in 1200 256 byte segments. * The segments represent 4 squares of 8x8 pixels as follows: @@ -1432,7 +1561,7 @@ check_middle: frame->segment < frame->width * frame->height / 256) { int iSegY, iSegUV; int iY, jY, iUV, jUV; - int iOutY, iOutUV; + int iOutY, iOutYP, iOutUV, iOutUVP; unsigned char *pOut; iSegY = iSegUV = frame->segment; @@ -1465,10 +1594,12 @@ check_middle: */ iY = iSegY / (frame->width / WDIV); jY = iSegY - iY * (frame->width / WDIV); - iOutY = (iY*HDIV*frame->width + jY*WDIV) * (frame->depth >> 3); + iOutYP = iY*HDIV*frame->width + jY*WDIV; + iOutY = iOutYP * (frame->depth >> 3); iUV = iSegUV / (frame->width / WDIV * 2); jUV = iSegUV - iUV * (frame->width / WDIV * 2); - iOutUV = (iUV*HDIV*2*frame->width + jUV*WDIV/2) * (frame->depth >> 3); + iOutUVP = iUV*HDIV*2*frame->width + jUV*WDIV/2; + iOutUV = iOutUVP * (frame->depth >> 3); switch (frame->format) { case VIDEO_PALETTE_GREY: @@ -1478,6 +1609,16 @@ check_middle: ov511_parse_data_rgb24 (pData, pOut, iOutY, iOutUV, iY & 1, frame->width); break; + case VIDEO_PALETTE_YUV422: + ov511_parse_data_yuv422(pData, pOut, iOutY, iOutUV, + iY & 1, frame->width); + break; + case VIDEO_PALETTE_YUV422P: + ov511_parse_data_yuv422p (pData, pOut, iOutYP, iOutUVP/2, + frame->width, frame->height); + break; + default: + err("Unsupported format: %d", frame->format); } pData = &cdata[iPix]; @@ -1995,17 +2136,22 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm))) return -EFAULT; - PDEBUG(4, "MCAPTURE"); + PDEBUG(4, "CMCAPTURE"); PDEBUG(4, "frame: %d, size: %dx%d, format: %d", vm.frame, vm.width, vm.height, vm.format); if (vm.format != VIDEO_PALETTE_RGB24 && + vm.format != VIDEO_PALETTE_YUV422 && + vm.format != VIDEO_PALETTE_YUV422P && vm.format != VIDEO_PALETTE_GREY) return -EINVAL; if ((vm.frame != 0) && (vm.frame != 1)) return -EINVAL; + if (vm.width > DEFAULT_WIDTH || vm.height > DEFAULT_HEIGHT) + return -EINVAL; + if (ov511->frame[vm.frame].grabstate == FRAME_GRABBING) return -EBUSY; diff --git a/drivers/usb/printer.c b/drivers/usb/printer.c index a36cd70d1..638a91ed9 100644 --- a/drivers/usb/printer.c +++ b/drivers/usb/printer.c @@ -70,7 +70,7 @@ MFG:HEWLETT-PACKARD;MDL:DESKJET 970C;CMD:MLC,PCL,PML;CLASS:PRINTER;DESCRIPTION:H #define USBLP_MINORS 16 #define USBLP_MINOR_BASE 0 -#define USBLP_WRITE_TIMEOUT (60*HZ) /* 60 seconds */ +#define USBLP_WRITE_TIMEOUT (60*60*HZ) /* 60 minutes */ struct usblp { struct usb_device *dev; /* USB device */ @@ -147,10 +147,8 @@ static int usblp_check_status(struct usblp *usblp) info("usblp%d: off-line", usblp->minor); return -EIO; } - if (~status & LP_PERRORP) { - info("usblp%d: on fire", usblp->minor); - return -EIO; - } + info("usblp%d: on fire", usblp->minor); + return -EIO; } return 0; diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index c3cce34c3..4355f3773 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -14,6 +14,25 @@ * Peter Berger (pberger@brimson.com) * Al Borchers (borchers@steinerpoint.com) * +* (6/27/2000) pberger and borchers +* -- Zeroed out sync field in the wakeup_task before first use; +* otherwise the uninitialized value might prevent the task from +* being scheduled. +* -- Initialized ret value to 0 in write_bulk_callback, otherwise +* the uninitialized value could cause a spurious debugging message. +* +* (6/22/2000) pberger and borchers +* -- Made cond_wait_... inline--apparently on SPARC the flags arg +* to spin_lock_irqsave cannot be passed to another function +* to call spin_unlock_irqrestore. Thanks to Pauline Middelink. +* -- In digi_set_modem_signals the inner nested spin locks use just +* spin_lock() rather than spin_lock_irqsave(). The old code +* mistakenly left interrupts off. Thanks to Pauline Middelink. +* -- copy_from_user (which can sleep) is no longer called while a +* spinlock is held. We copy to a local buffer before getting +* the spinlock--don't like the extra copy but the code is simpler. +* -- Printk and dbg are no longer called while a spin lock is held. +* * (6/4/2000) pberger and borchers * -- Replaced separate calls to spin_unlock_irqrestore and * interruptible_sleep_on_interruptible with a new function @@ -119,8 +138,10 @@ * - All sleeps use a timeout of DIGI_RETRY_TIMEOUT before looping to * recheck the condition they are sleeping on. This is defensive, * in case a wake up is lost. +* - Following Documentation/DocBook/kernel-locking.pdf no spin locks +* are held when calling copy_to/from_user or printk. * -* $Id: digi_acceleport.c,v 1.56 2000/06/07 22:47:30 root Exp root $ +* $Id: digi_acceleport.c,v 1.63 2000/06/28 18:28:31 root Exp root $ */ #include <linux/config.h> @@ -307,7 +328,7 @@ typedef struct digi_private { int dp_transmit_idle; int dp_in_close; wait_queue_head_t dp_close_wait; /* wait queue for close */ - struct tq_struct dp_tasks; + struct tq_struct dp_wakeup_task; } digi_private_t; @@ -402,7 +423,7 @@ struct usb_serial_device_type digi_acceleport_device = { * wake ups. This is used to implement condition variables. */ -static long cond_wait_interruptible_timeout_irqrestore( +static inline long cond_wait_interruptible_timeout_irqrestore( wait_queue_head_t *q, long timeout, spinlock_t *lock, unsigned long flags ) { @@ -465,6 +486,7 @@ static void digi_wakeup_write( struct usb_serial_port *port ) /* wake up other tty processes */ wake_up_interruptible( &tty->write_wait ); + /* For 2.2.16 backport -- wake_up_interruptible( &tty->poll_wait ); */ } @@ -515,17 +537,17 @@ dbg( "digi_write_oob_command: TOP: port=%d, count=%d", oob_port_num, count ); if( (ret=usb_submit_urb(oob_port->write_urb)) == 0 ) { count -= len; buf += len; - } else { - dbg( - "digi_write_oob_command: usb_submit_urb failed, ret=%d", - ret ); - break; } } spin_unlock_irqrestore( &oob_priv->dp_port_lock, flags ); + if( ret ) { + dbg( "digi_write_oob_command: usb_submit_urb failed, ret=%d", + ret ); + } + return( ret ); } @@ -569,8 +591,8 @@ count ); } /* len must be a multiple of 4 and small enough to */ - /* guarantee the write will send all data (or none), */ - /* so commands are not split */ + /* guarantee the write will send buffered data first, */ + /* so commands are in order with data and not split */ len = MIN( count, port->bulk_out_size-2-priv->dp_buf_len ); if( len > 4 ) len &= ~3; @@ -588,35 +610,21 @@ count ); port->write_urb->transfer_buffer_length = len; } -#ifdef DEBUG_DATA - { - int i; - - printk( KERN_DEBUG __FILE__ ": digi_write: port=%d, length=%d, data=", - priv->dp_port_num, port->write_urb->transfer_buffer_length ); - for( i=0; i<port->write_urb->transfer_buffer_length; ++i ) { - printk( "%.2x ", - ((unsigned char *)port->write_urb->transfer_buffer)[i] ); - } - printk( "\n" ); - } -#endif - if( (ret=usb_submit_urb(port->write_urb)) == 0 ) { priv->dp_buf_len = 0; count -= len; buf += len; - } else { - dbg( - "digi_write_inb_command: usb_submit_urb failed, ret=%d", - ret ); - break; } } spin_unlock_irqrestore( &priv->dp_port_lock, flags ); + if( ret ) { + dbg( "digi_write_inb_command: usb_submit_urb failed, ret=%d", + ret ); + } + return( ret ); } @@ -647,10 +655,10 @@ dbg( "digi_set_modem_signals: TOP: port=%d, modem_signals=0x%x", port_priv->dp_port_num, modem_signals ); spin_lock_irqsave( &oob_priv->dp_port_lock, flags ); - spin_lock_irqsave( &port_priv->dp_port_lock, flags ); + spin_lock( &port_priv->dp_port_lock ); while( oob_port->write_urb->status == -EINPROGRESS ) { - spin_unlock_irqrestore( &port_priv->dp_port_lock, flags ); + spin_unlock( &port_priv->dp_port_lock ); cond_wait_interruptible_timeout_irqrestore( &oob_port->write_wait, DIGI_RETRY_TIMEOUT, &oob_priv->dp_port_lock, flags ); @@ -658,7 +666,7 @@ port_priv->dp_port_num, modem_signals ); return( -EINTR ); } spin_lock_irqsave( &oob_priv->dp_port_lock, flags ); - spin_lock_irqsave( &port_priv->dp_port_lock, flags ); + spin_lock( &port_priv->dp_port_lock ); } data[0] = DIGI_CMD_SET_DTR_SIGNAL; @@ -679,14 +687,16 @@ port_priv->dp_port_num, modem_signals ); port_priv->dp_modem_signals = (port_priv->dp_modem_signals&~(TIOCM_DTR|TIOCM_RTS)) | (modem_signals&(TIOCM_DTR|TIOCM_RTS)); - } else { - dbg( "digi_set_modem_signals: usb_submit_urb failed, ret=%d", - ret ); } - spin_unlock_irqrestore( &port_priv->dp_port_lock, flags ); + spin_unlock( &port_priv->dp_port_lock ); spin_unlock_irqrestore( &oob_priv->dp_port_lock, flags ); + if( ret ) { + dbg( "digi_set_modem_signals: usb_submit_urb failed, ret=%d", + ret ); + } + return( ret ); } @@ -1046,12 +1056,19 @@ static int digi_write( struct usb_serial_port *port, int from_user, int ret,data_len,new_len; digi_private_t *priv = (digi_private_t *)(port->private); unsigned char *data = port->write_urb->transfer_buffer; + unsigned char user_buf[64]; /* 64 bytes is max USB bulk packet */ unsigned long flags = 0; dbg( "digi_write: TOP: port=%d, count=%d, from_user=%d, in_interrupt=%d", priv->dp_port_num, count, from_user, in_interrupt() ); + /* copy user data (which can sleep) before getting spin lock */ + count = MIN( 64, MIN( count, port->bulk_out_size-2 ) ); + if( from_user && copy_from_user( user_buf, buf, count ) ) { + return( -EFAULT ); + } + /* be sure only one write proceeds at a time */ /* there are races on the port private buffer */ /* and races to check write_urb->status */ @@ -1096,40 +1113,19 @@ priv->dp_port_num, count, from_user, in_interrupt() ); data += priv->dp_buf_len; /* copy in new data */ - if( from_user ) { - if( copy_from_user( data, buf, new_len ) ) { - spin_unlock_irqrestore( &priv->dp_port_lock, flags ); - return( -EFAULT ); - } - } else { - memcpy( data, buf, new_len ); - } - -#ifdef DEBUG_DATA - { - int i; - - printk( KERN_DEBUG __FILE__ ": digi_write: port=%d, length=%d, data=", - priv->dp_port_num, port->write_urb->transfer_buffer_length ); - for( i=0; i<port->write_urb->transfer_buffer_length; ++i ) { - printk( "%.2x ", - ((unsigned char *)port->write_urb->transfer_buffer)[i] ); - } - printk( "\n" ); - } -#endif + memcpy( data, from_user ? user_buf : buf, new_len ); if( (ret=usb_submit_urb(port->write_urb)) == 0 ) { ret = new_len; priv->dp_buf_len = 0; - } else { - dbg( "digi_write: usb_submit_urb failed, ret=%d", - ret ); } /* return length of new data written, or error */ -dbg( "digi_write: returning %d", ret ); spin_unlock_irqrestore( &priv->dp_port_lock, flags ); + if( ret < 0 ) { + dbg( "digi_write: usb_submit_urb failed, ret=%d", ret ); + } +dbg( "digi_write: returning %d", ret ); return( ret ); } @@ -1141,7 +1137,7 @@ static void digi_write_bulk_callback( struct urb *urb ) struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct usb_serial *serial = port->serial; digi_private_t *priv = (digi_private_t *)(port->private); - int ret; + int ret = 0; dbg( "digi_write_bulk_callback: TOP: port=%d", priv->dp_port_num ); @@ -1175,24 +1171,8 @@ dbg( "digi_write_bulk_callback: TOP: port=%d", priv->dp_port_num ); memcpy( port->write_urb->transfer_buffer+2, priv->dp_buf, priv->dp_buf_len ); -#ifdef DEBUG_DATA - { - int i; - - printk( KERN_DEBUG __FILE__ ": digi_write_bulk_callback: port=%d, length=%d, data=", - priv->dp_port_num, port->write_urb->transfer_buffer_length ); - for( i=0; i<port->write_urb->transfer_buffer_length; ++i ) { - printk( "%.2x ", - ((unsigned char *)port->write_urb->transfer_buffer)[i] ); - } - printk( "\n" ); - } -#endif - if( (ret=usb_submit_urb(port->write_urb)) == 0 ) { priv->dp_buf_len = 0; - } else { - dbg( "digi_write_bulk_callback: usb_submit_urb failed, ret=%d", ret ); } } @@ -1200,13 +1180,16 @@ dbg( "digi_write_bulk_callback: TOP: port=%d", priv->dp_port_num ); /* wake up processes sleeping on writes immediately */ digi_wakeup_write( port ); - spin_unlock( &priv->dp_port_lock ); - /* also queue up a wakeup at scheduler time, in case we */ /* lost the race in write_chan(). */ - priv->dp_tasks.routine = (void *)digi_wakeup_write_lock; - priv->dp_tasks.data = (void *)port; - queue_task( &(priv->dp_tasks), &tq_scheduler ); + queue_task( &priv->dp_wakeup_task, &tq_scheduler ); + + spin_unlock( &priv->dp_port_lock ); + + if( ret ) { + dbg( "digi_write_bulk_callback: usb_submit_urb failed, ret=%d", + ret ); + } } @@ -1219,8 +1202,6 @@ static int digi_write_room( struct usb_serial_port *port ) unsigned long flags = 0; -dbg( "digi_write_room: TOP: port=%d", priv->dp_port_num ); - spin_lock_irqsave( &priv->dp_port_lock, flags ); if( port->write_urb->status == -EINPROGRESS ) @@ -1242,8 +1223,6 @@ static int digi_chars_in_buffer( struct usb_serial_port *port ) digi_private_t *priv = (digi_private_t *)(port->private); -dbg( "digi_chars_in_buffer: TOP: port=%d", priv->dp_port_num ); - if( port->write_urb->status == -EINPROGRESS ) { dbg( "digi_chars_in_buffer: port=%d, chars=%d", priv->dp_port_num, port->bulk_out_size - 2 ); /* return( port->bulk_out_size - 2 ); */ @@ -1455,13 +1434,14 @@ static int digi_startup_device( struct usb_serial *serial ) int i,ret = 0; - spin_lock( &startup_lock ); - /* be sure this happens exactly once */ + spin_lock( &startup_lock ); if( device_startup ) { spin_unlock( &startup_lock ); return( 0 ); } + device_startup = 1; + spin_unlock( &startup_lock ); /* start reading from each bulk in endpoint for the device */ for( i=0; i<digi_acceleport_device.num_ports+1; i++ ) { @@ -1474,10 +1454,6 @@ static int digi_startup_device( struct usb_serial *serial ) } - device_startup = 1; - - spin_unlock( &startup_lock ); - return( ret ); } @@ -1516,8 +1492,10 @@ dbg( "digi_startup: TOP" ); priv->dp_transmit_idle = 0; priv->dp_in_close = 0; init_waitqueue_head( &priv->dp_close_wait ); - priv->dp_tasks.next = NULL; - priv->dp_tasks.data = NULL; + priv->dp_wakeup_task.next = NULL; + priv->dp_wakeup_task.sync = 0; + priv->dp_wakeup_task.routine = (void *)digi_wakeup_write_lock; + priv->dp_wakeup_task.data = (void *)(&serial->port[i]); spin_lock_init( &priv->dp_port_lock ); /* initialize write wait queue for this port */ @@ -1595,17 +1573,6 @@ dbg( "digi_read_bulk_callback: TOP: port=%d", priv->dp_port_num ); goto resubmit; } -#ifdef DEBUG_DATA -if( urb->actual_length ) { - printk( KERN_DEBUG __FILE__ ": digi_read_bulk_callback: port=%d, length=%d, data=", - priv->dp_port_num, urb->actual_length ); - for( i=0; i<urb->actual_length; ++i ) { - printk( "%.2x ", ((unsigned char *)urb->transfer_buffer)[i] ); - } - printk( "\n" ); -} -#endif - if( urb->actual_length != len + 2 ) err( KERN_INFO "digi_read_bulk_callback: INCOMPLETE PACKET, port=%d, opcode=%d, len=%d, actual_length=%d, status=%d", priv->dp_port_num, opcode, len, urb->actual_length, status ); diff --git a/drivers/usb/serial/usb-serial.h b/drivers/usb/serial/usb-serial.h index b8e2e1d74..430a8fa64 100644 --- a/drivers/usb/serial/usb-serial.h +++ b/drivers/usb/serial/usb-serial.h @@ -51,6 +51,8 @@ struct usb_serial_port { wait_queue_head_t write_wait; + struct tq_struct tqueue; /* task queue for line discipline waking up */ + void * private; /* data private to the specific port */ }; @@ -178,6 +180,5 @@ static inline int port_paranoia_check (struct usb_serial_port *port, const char return 0; } - #endif /* ifdef __LINUX_USB_SERIAL_H */ diff --git a/drivers/usb/serial/usbserial.c b/drivers/usb/serial/usbserial.c index d60fd8e33..261d5ac00 100644 --- a/drivers/usb/serial/usbserial.c +++ b/drivers/usb/serial/usbserial.c @@ -14,6 +14,10 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * (06/25/2000) gkh + * Changed generic_write_bulk_callback to not call wake_up_interruptible + * directly, but to have port_softint do it at a safer time. + * * (06/23/2000) gkh * Cleaned up debugging statements in a quest to find UHCI timeout bug. * @@ -222,6 +226,8 @@ #include <linux/tty.h> #include <linux/module.h> #include <linux/spinlock.h> +#include <linux/list.h> +#include <linux/smp_lock.h> #ifdef CONFIG_USB_SERIAL_DEBUG #define DEBUG @@ -803,36 +809,32 @@ static int generic_write (struct usb_serial_port *port, int from_user, const uns static int generic_write_room (struct usb_serial_port *port) { struct usb_serial *serial = port->serial; - int room; + int room = 0; dbg(__FUNCTION__ " - port %d", port->number); - if (serial->num_bulk_out) { - if (port->write_urb->status == -EINPROGRESS) - room = 0; - else + if (serial->num_bulk_out) + if (port->write_urb->status != -EINPROGRESS) room = port->bulk_out_size; - dbg(__FUNCTION__ " returns %d", room); - return (room); - } - return (0); + dbg(__FUNCTION__ " - returns %d", room); + return (room); } static int generic_chars_in_buffer (struct usb_serial_port *port) { struct usb_serial *serial = port->serial; + int chars = 0; dbg(__FUNCTION__ " - port %d", port->number); - if (serial->num_bulk_out) { - if (port->write_urb->status == -EINPROGRESS) { - return (port->bulk_out_size); - } - } + if (serial->num_bulk_out) + if (port->write_urb->status == -EINPROGRESS) + chars = port->write_urb->transfer_buffer_length; - return (0); + dbg (__FUNCTION__ " - returns %d", chars); + return (chars); } @@ -844,9 +846,10 @@ static void generic_read_bulk_callback (struct urb *urb) unsigned char *data = urb->transfer_buffer; int i; - dbg (__FUNCTION__ " - enter"); + dbg(__FUNCTION__ " - port %d", port->number); if (!serial) { + dbg(__FUNCTION__ " - bad serial pointer, exiting"); return; } @@ -877,8 +880,6 @@ static void generic_read_bulk_callback (struct urb *urb) if (usb_submit_urb(urb)) dbg(__FUNCTION__ " - failed resubmitting read urb"); - dbg (__FUNCTION__ " - exit"); - return; } @@ -887,11 +888,11 @@ static void generic_write_bulk_callback (struct urb *urb) { struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); - struct tty_struct *tty; - dbg (__FUNCTION__ " - enter"); + dbg(__FUNCTION__ " - port %d", port->number); if (!serial) { + dbg(__FUNCTION__ " - bad serial pointer, exiting"); return; } @@ -900,18 +901,36 @@ static void generic_write_bulk_callback (struct urb *urb) return; } + queue_task(&port->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); + + return; +} + + +static void port_softint(void *private) +{ + struct usb_serial_port *port = (struct usb_serial_port *)private; + struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); + struct tty_struct *tty; + + dbg(__FUNCTION__ " - port %d", port->number); + + if (!serial) { + return; + } + tty = port->tty; - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) { + dbg(__FUNCTION__ " - write wakeup call."); (tty->ldisc.write_wakeup)(tty); + } wake_up_interruptible(&tty->write_wait); - - dbg (__FUNCTION__ " - exit"); - - return; } + static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum) { struct usb_serial *serial = NULL; @@ -1117,6 +1136,8 @@ static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum) port->number = i + serial->minor; port->serial = serial; port->magic = USB_SERIAL_PORT_MAGIC; + port->tqueue.routine = port_softint; + port->tqueue.data = port; } /* initialize the devfs nodes for this device and let the user know what ports we are bound to */ diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index 8be2dd351..fa7f475c6 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -11,6 +11,10 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * (06/25/2000) gkh + * Fixed bug in visor_unthrottle that should help with the disconnect in PPP + * bug that people have been reporting. + * * (06/23/2000) gkh * Cleaned up debugging statements in a quest to find UHCI timeout bug. * @@ -137,7 +141,7 @@ static void visor_unthrottle (struct usb_serial_port *port) { dbg(__FUNCTION__ " - port %d", port->number); - if (usb_unlink_urb (port->read_urb)) + if (usb_submit_urb (port->read_urb)) dbg(__FUNCTION__ " - usb_submit_urb(read bulk) failed"); return; diff --git a/drivers/usb/storage/Makefile b/drivers/usb/storage/Makefile new file mode 100644 index 000000000..c3dd30dee --- /dev/null +++ b/drivers/usb/storage/Makefile @@ -0,0 +1,32 @@ +# +# Makefile for the USB Mass Storage device drivers. +# + +O_TARGET := usb-storage.o +M_OBJS := usb-storage.o +O_OBJS := scsiglue.o protocol.o transport.o debug.o usb.o +MOD_LIST_NAME := USB_STORAGE_MODULES + +CFLAGS_scsiglue.o:= -I../../scsi/ +CFLAGS_protocol.o:= -I../../scsi/ +CFLAGS_transport.o:= -I../../scsi/ +CFLAGS_debug.o:= -I../../scsi/ +CFLAGS_usb.o:= -I../../scsi/ + +ifeq ($(CONFIG_USB_STORAGE_DEBUG),y) + O_OBJS += debug.o +endif + +ifeq ($(CONFIG_USB_STORAGE_FREECOM),y) + O_OBJS += freecom.o +endif + +ifeq ($(CONFIG_USB_STORAGE_SHUTTLE_SMARTMEDIA),y) + O_OBJS += shuttle_sm.o +endif + +ifeq ($(CONFIG_USB_STORAGE_SHUTTLE_COMPACTFLASH),y) + O_OBJS += shuttle_cf.o +endif + +include $(TOPDIR)/Rules.make diff --git a/drivers/usb/storage/debug.c b/drivers/usb/storage/debug.c new file mode 100644 index 000000000..d8845d8c2 --- /dev/null +++ b/drivers/usb/storage/debug.c @@ -0,0 +1,185 @@ +/* Driver for USB Mass Storage compliant devices + * Debugging Functions Source Code File + * + * $Id: debug.c,v 1.1 2000/06/27 01:25:28 mdharm Exp $ + * + * Current development and maintainance by: + * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) + * + * Initial work by: + * (c) 1999 Michael Gee (michael@linuxspecific.com) + * + * This driver is based on the 'USB Mass Storage Class' document. This + * describes in detail the protocol used to communicate with such + * devices. Clearly, the designers had SCSI and ATAPI commands in + * mind when they created this document. The commands are all very + * similar to commands in the SCSI-II and ATAPI specifications. + * + * It is important to note that in a number of cases this class + * exhibits class-specific exemptions from the USB specification. + * Notably the usage of NAK, STALL and ACK differs from the norm, in + * that they are used to communicate wait, failed and OK on commands. + * + * Also, for certain devices, the interrupt endpoint is used to convey + * status of a command. + * + * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more + * information about this driver. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "debug.h" + +void usb_stor_show_command(Scsi_Cmnd *srb) +{ + char *what = NULL; + + switch (srb->cmnd[0]) { + case TEST_UNIT_READY: what = "TEST_UNIT_READY"; break; + case REZERO_UNIT: what = "REZERO_UNIT"; break; + case REQUEST_SENSE: what = "REQUEST_SENSE"; break; + case FORMAT_UNIT: what = "FORMAT_UNIT"; break; + case READ_BLOCK_LIMITS: what = "READ_BLOCK_LIMITS"; break; + case REASSIGN_BLOCKS: what = "REASSIGN_BLOCKS"; break; + case READ_6: what = "READ_6"; break; + case WRITE_6: what = "WRITE_6"; break; + case SEEK_6: what = "SEEK_6"; break; + case READ_REVERSE: what = "READ_REVERSE"; break; + case WRITE_FILEMARKS: what = "WRITE_FILEMARKS"; break; + case SPACE: what = "SPACE"; break; + case INQUIRY: what = "INQUIRY"; break; + case RECOVER_BUFFERED_DATA: what = "RECOVER_BUFFERED_DATA"; break; + case MODE_SELECT: what = "MODE_SELECT"; break; + case RESERVE: what = "RESERVE"; break; + case RELEASE: what = "RELEASE"; break; + case COPY: what = "COPY"; break; + case ERASE: what = "ERASE"; break; + case MODE_SENSE: what = "MODE_SENSE"; break; + case START_STOP: what = "START_STOP"; break; + case RECEIVE_DIAGNOSTIC: what = "RECEIVE_DIAGNOSTIC"; break; + case SEND_DIAGNOSTIC: what = "SEND_DIAGNOSTIC"; break; + case ALLOW_MEDIUM_REMOVAL: what = "ALLOW_MEDIUM_REMOVAL"; break; + case SET_WINDOW: what = "SET_WINDOW"; break; + case READ_CAPACITY: what = "READ_CAPACITY"; break; + case READ_10: what = "READ_10"; break; + case WRITE_10: what = "WRITE_10"; break; + case SEEK_10: what = "SEEK_10"; break; + case WRITE_VERIFY: what = "WRITE_VERIFY"; break; + case VERIFY: what = "VERIFY"; break; + case SEARCH_HIGH: what = "SEARCH_HIGH"; break; + case SEARCH_EQUAL: what = "SEARCH_EQUAL"; break; + case SEARCH_LOW: what = "SEARCH_LOW"; break; + case SET_LIMITS: what = "SET_LIMITS"; break; + case READ_POSITION: what = "READ_POSITION"; break; + case SYNCHRONIZE_CACHE: what = "SYNCHRONIZE_CACHE"; break; + case LOCK_UNLOCK_CACHE: what = "LOCK_UNLOCK_CACHE"; break; + case READ_DEFECT_DATA: what = "READ_DEFECT_DATA"; break; + case MEDIUM_SCAN: what = "MEDIUM_SCAN"; break; + case COMPARE: what = "COMPARE"; break; + case COPY_VERIFY: what = "COPY_VERIFY"; break; + case WRITE_BUFFER: what = "WRITE_BUFFER"; break; + case READ_BUFFER: what = "READ_BUFFER"; break; + case UPDATE_BLOCK: what = "UPDATE_BLOCK"; break; + case READ_LONG: what = "READ_LONG"; break; + case WRITE_LONG: what = "WRITE_LONG"; break; + case CHANGE_DEFINITION: what = "CHANGE_DEFINITION"; break; + case WRITE_SAME: what = "WRITE_SAME"; break; + case READ_TOC: what = "READ_TOC"; break; + case LOG_SELECT: what = "LOG_SELECT"; break; + case LOG_SENSE: what = "LOG_SENSE"; break; + case MODE_SELECT_10: what = "MODE_SELECT_10"; break; + case MODE_SENSE_10: what = "MODE_SENSE_10"; break; + case MOVE_MEDIUM: what = "MOVE_MEDIUM"; break; + case READ_12: what = "READ_12"; break; + case WRITE_12: what = "WRITE_12"; break; + case WRITE_VERIFY_12: what = "WRITE_VERIFY_12"; break; + case SEARCH_HIGH_12: what = "SEARCH_HIGH_12"; break; + case SEARCH_EQUAL_12: what = "SEARCH_EQUAL_12"; break; + case SEARCH_LOW_12: what = "SEARCH_LOW_12"; break; + case READ_ELEMENT_STATUS: what = "READ_ELEMENT_STATUS"; break; + case SEND_VOLUME_TAG: what = "SEND_VOLUME_TAG"; break; + case WRITE_LONG_2: what = "WRITE_LONG_2"; break; + default: break; + } + US_DEBUGP("Command %s (%d bytes)\n", what, srb->cmd_len); + US_DEBUGP("%02x %02x %02x %02x " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x\n", + srb->cmnd[0], srb->cmnd[1], srb->cmnd[2], srb->cmnd[3], + srb->cmnd[4], srb->cmnd[5], srb->cmnd[6], srb->cmnd[7], + srb->cmnd[8], srb->cmnd[9], srb->cmnd[10], + srb->cmnd[11]); +} + +void usb_stor_print_Scsi_Cmnd( Scsi_Cmnd* cmd ) +{ + int i=0, bufferSize = cmd->request_bufflen; + u8* buffer = cmd->request_buffer; + struct scatterlist* sg = (struct scatterlist*)cmd->request_buffer; + + US_DEBUGP("Dumping information about %p.\n", cmd ); + US_DEBUGP("cmd->cmnd[0] value is %d.\n", cmd->cmnd[0] ); + US_DEBUGP("(MODE_SENSE is %d and MODE_SENSE_10 is %d)\n", + MODE_SENSE, MODE_SENSE_10 ); + + US_DEBUGP("buffer is %p with length %d.\n", buffer, bufferSize ); + for ( i=0; i<bufferSize; i+=16 ) + { + US_DEBUGP("%02x %02x %02x %02x %02x %02x %02x %02x\n" + "%02x %02x %02x %02x %02x %02x %02x %02x\n", + buffer[i], + buffer[i+1], + buffer[i+2], + buffer[i+3], + buffer[i+4], + buffer[i+5], + buffer[i+6], + buffer[i+7], + buffer[i+8], + buffer[i+9], + buffer[i+10], + buffer[i+11], + buffer[i+12], + buffer[i+13], + buffer[i+14], + buffer[i+15] ); + } + + US_DEBUGP("Buffer has %d scatterlists.\n", cmd->use_sg ); + for ( i=0; i<cmd->use_sg; i++ ) + { + US_DEBUGP("Length of scatterlist %d is %d.\n",i,sg[i].length); + US_DEBUGP("%02x %02x %02x %02x %02x %02x %02x %02x\n" + "%02x %02x %02x %02x %02x %02x %02x %02x\n", + sg[i].address[0], + sg[i].address[1], + sg[i].address[2], + sg[i].address[3], + sg[i].address[4], + sg[i].address[5], + sg[i].address[6], + sg[i].address[7], + sg[i].address[8], + sg[i].address[9], + sg[i].address[10], + sg[i].address[11], + sg[i].address[12], + sg[i].address[13], + sg[i].address[14], + sg[i].address[15]); + } +} + diff --git a/drivers/usb/storage/debug.h b/drivers/usb/storage/debug.h new file mode 100644 index 000000000..ab5bef0ac --- /dev/null +++ b/drivers/usb/storage/debug.h @@ -0,0 +1,66 @@ +/* Driver for USB Mass Storage compliant devices + * Debugging Functions Header File + * + * $Id: debug.h,v 1.1 2000/06/27 01:25:28 mdharm Exp $ + * + * Current development and maintainance by: + * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) + * + * Initial work by: + * (c) 1999 Michael Gee (michael@linuxspecific.com) + * + * This driver is based on the 'USB Mass Storage Class' document. This + * describes in detail the protocol used to communicate with such + * devices. Clearly, the designers had SCSI and ATAPI commands in + * mind when they created this document. The commands are all very + * similar to commands in the SCSI-II and ATAPI specifications. + * + * It is important to note that in a number of cases this class + * exhibits class-specific exemptions from the USB specification. + * Notably the usage of NAK, STALL and ACK differs from the norm, in + * that they are used to communicate wait, failed and OK on commands. + * + * Also, for certain devices, the interrupt endpoint is used to convey + * status of a command. + * + * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more + * information about this driver. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _DEBUG_H_ +#define _DEBUG_H_ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/blk.h> +#include "scsi.h" + +#define USB_STORAGE "usb-storage: " + +#ifdef CONFIG_USB_STORAGE_DEBUG +void usb_stor_show_command(Scsi_Cmnd *srb); +void usb_stor_print_Scsi_Cmnd( Scsi_Cmnd* cmd ); +#define US_DEBUGP(x...) printk( KERN_DEBUG USB_STORAGE ## x ) +#define US_DEBUGPX(x...) printk( ## x ) +#define US_DEBUG(x) x +#else +#define US_DEBUGP(x...) +#define US_DEBUGPX(x...) +#define US_DEBUG(x) +#endif + +#endif diff --git a/drivers/usb/storage/protocol.c b/drivers/usb/storage/protocol.c new file mode 100644 index 000000000..d3624dc76 --- /dev/null +++ b/drivers/usb/storage/protocol.c @@ -0,0 +1,304 @@ +/* Driver for USB Mass Storage compliant devices + * + * $Id: protocol.c,v 1.1 2000/06/27 01:25:28 mdharm Exp $ + * + * Current development and maintainance by: + * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) + * + * Developed with the assistance of: + * (c) 2000 David L. Brown, Jr. (usb-storage@davidb.org) + * + * Initial work by: + * (c) 1999 Michael Gee (michael@linuxspecific.com) + * + * This driver is based on the 'USB Mass Storage Class' document. This + * describes in detail the protocol used to communicate with such + * devices. Clearly, the designers had SCSI and ATAPI commands in + * mind when they created this document. The commands are all very + * similar to commands in the SCSI-II and ATAPI specifications. + * + * It is important to note that in a number of cases this class + * exhibits class-specific exemptions from the USB specification. + * Notably the usage of NAK, STALL and ACK differs from the norm, in + * that they are used to communicate wait, failed and OK on commands. + * + * Also, for certain devices, the interrupt endpoint is used to convey + * status of a command. + * + * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more + * information about this driver. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "protocol.h" +#include "usb.h" +#include "debug.h" +#include "scsiglue.h" +#include "transport.h" + +/*********************************************************************** + * Protocol routines + ***********************************************************************/ + +void usb_stor_ATAPI_command(Scsi_Cmnd *srb, struct us_data *us) +{ + int old_cmnd = 0; + + /* Fix some commands -- this is a form of mode translation + * ATAPI devices only accept 12 byte long commands + * + * NOTE: This only works because a Scsi_Cmnd struct field contains + * a unsigned char cmnd[12], so we know we have storage available + */ + + /* set command length to 12 bytes */ + srb->cmd_len = 12; + + /* determine the correct (or minimum) data length for these commands */ + switch (srb->cmnd[0]) { + + /* change MODE_SENSE/MODE_SELECT from 6 to 10 byte commands */ + case MODE_SENSE: + case MODE_SELECT: + /* save the command so we can tell what it was */ + old_cmnd = srb->cmnd[0]; + + srb->cmnd[11] = 0; + srb->cmnd[10] = 0; + srb->cmnd[9] = 0; + srb->cmnd[8] = srb->cmnd[4]; + srb->cmnd[7] = 0; + srb->cmnd[6] = 0; + srb->cmnd[5] = 0; + srb->cmnd[4] = 0; + srb->cmnd[3] = 0; + srb->cmnd[2] = srb->cmnd[2]; + srb->cmnd[1] = srb->cmnd[1]; + srb->cmnd[0] = srb->cmnd[0] | 0x40; + break; + + /* change READ_6/WRITE_6 to READ_10/WRITE_10, which + * are ATAPI commands */ + case WRITE_6: + case READ_6: + srb->cmnd[11] = 0; + srb->cmnd[10] = 0; + srb->cmnd[9] = 0; + srb->cmnd[8] = srb->cmnd[4]; + srb->cmnd[7] = 0; + srb->cmnd[6] = 0; + srb->cmnd[5] = srb->cmnd[3]; + srb->cmnd[4] = srb->cmnd[2]; + srb->cmnd[3] = srb->cmnd[1] & 0x1F; + srb->cmnd[2] = 0; + srb->cmnd[1] = srb->cmnd[1] & 0xE0; + srb->cmnd[0] = srb->cmnd[0] | 0x20; + break; + } /* end switch on cmnd[0] */ + + /* convert MODE_SELECT data here */ + if (old_cmnd == MODE_SELECT) + usb_stor_scsiSense6to10(srb); + + /* send the command to the transport layer */ + usb_stor_invoke_transport(srb, us); + + /* Fix the MODE_SENSE data if we translated the command */ + if ((old_cmnd == MODE_SENSE) && (srb->result == GOOD)) + usb_stor_scsiSense10to6(srb); + + /* Fix-up the return data from an INQUIRY command to show + * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us + */ + if (srb->cmnd[0] == INQUIRY) { + ((unsigned char *)us->srb->request_buffer)[2] |= 0x2; + } +} + + +void usb_stor_ufi_command(Scsi_Cmnd *srb, struct us_data *us) +{ + int old_cmnd = 0; + + /* fix some commands -- this is a form of mode translation + * UFI devices only accept 12 byte long commands + * + * NOTE: This only works because a Scsi_Cmnd struct field contains + * a unsigned char cmnd[12], so we know we have storage available + */ + + /* set command length to 12 bytes (this affects the transport layer) */ + srb->cmd_len = 12; + + /* determine the correct (or minimum) data length for these commands */ + switch (srb->cmnd[0]) { + + /* for INQUIRY, UFI devices only ever return 36 bytes */ + case INQUIRY: + srb->cmnd[4] = 36; + break; + + /* change MODE_SENSE/MODE_SELECT from 6 to 10 byte commands */ + case MODE_SENSE: + case MODE_SELECT: + /* save the command so we can tell what it was */ + old_cmnd = srb->cmnd[0]; + + srb->cmnd[11] = 0; + srb->cmnd[10] = 0; + srb->cmnd[9] = 0; + + /* if we're sending data, we send all. If getting data, + * get the minimum */ + if (srb->cmnd[0] == MODE_SELECT) + srb->cmnd[8] = srb->cmnd[4]; + else + srb->cmnd[8] = 8; + + srb->cmnd[7] = 0; + srb->cmnd[6] = 0; + srb->cmnd[5] = 0; + srb->cmnd[4] = 0; + srb->cmnd[3] = 0; + srb->cmnd[2] = srb->cmnd[2]; + srb->cmnd[1] = srb->cmnd[1]; + srb->cmnd[0] = srb->cmnd[0] | 0x40; + break; + + /* again, for MODE_SENSE_10, we get the minimum (8) */ + case MODE_SENSE_10: + srb->cmnd[7] = 0; + srb->cmnd[8] = 8; + break; + + /* for REQUEST_SENSE, UFI devices only ever return 18 bytes */ + case REQUEST_SENSE: + srb->cmnd[4] = 18; + break; + + /* change READ_6/WRITE_6 to READ_10/WRITE_10, which + * are UFI commands */ + case WRITE_6: + case READ_6: + srb->cmnd[11] = 0; + srb->cmnd[10] = 0; + srb->cmnd[9] = 0; + srb->cmnd[8] = srb->cmnd[4]; + srb->cmnd[7] = 0; + srb->cmnd[6] = 0; + srb->cmnd[5] = srb->cmnd[3]; + srb->cmnd[4] = srb->cmnd[2]; + srb->cmnd[3] = srb->cmnd[1] & 0x1F; + srb->cmnd[2] = 0; + srb->cmnd[1] = srb->cmnd[1] & 0xE0; + srb->cmnd[0] = srb->cmnd[0] | 0x20; + break; + } /* end switch on cmnd[0] */ + + /* convert MODE_SELECT data here */ + if (old_cmnd == MODE_SELECT) + usb_stor_scsiSense6to10(srb); + + /* send the command to the transport layer */ + usb_stor_invoke_transport(srb, us); + + /* Fix the MODE_SENSE data if we translated the command */ + if ((old_cmnd == MODE_SENSE) && (srb->result == GOOD)) + usb_stor_scsiSense10to6(srb); + + /* Fix-up the return data from an INQUIRY command to show + * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us + */ + if (srb->cmnd[0] == INQUIRY) { + ((unsigned char *)us->srb->request_buffer)[2] |= 0x2; + } +} + +void usb_stor_transparent_scsi_command(Scsi_Cmnd *srb, struct us_data *us) +{ + /* This code supports devices which do not support {READ|WRITE}_6 + * Apparently, neither Windows or MacOS will use these commands, + * so some devices do not support them + */ + if (us->flags & US_FL_MODE_XLATE) { + + /* translate READ_6 to READ_10 */ + if (srb->cmnd[0] == 0x08) { + + /* get the control */ + srb->cmnd[9] = us->srb->cmnd[5]; + + /* get the length */ + srb->cmnd[8] = us->srb->cmnd[6]; + srb->cmnd[7] = 0; + + /* set the reserved area to 0 */ + srb->cmnd[6] = 0; + + /* get LBA */ + srb->cmnd[5] = us->srb->cmnd[3]; + srb->cmnd[4] = us->srb->cmnd[2]; + srb->cmnd[3] = 0; + srb->cmnd[2] = 0; + + /* LUN and other info in cmnd[1] can stay */ + + /* fix command code */ + srb->cmnd[0] = 0x28; + + US_DEBUGP("Changing READ_6 to READ_10\n"); + US_DEBUG(usb_stor_show_command(srb)); + } + + /* translate WRITE_6 to WRITE_10 */ + if (srb->cmnd[0] == 0x0A) { + + /* get the control */ + srb->cmnd[9] = us->srb->cmnd[5]; + + /* get the length */ + srb->cmnd[8] = us->srb->cmnd[4]; + srb->cmnd[7] = 0; + + /* set the reserved area to 0 */ + srb->cmnd[6] = 0; + + /* get LBA */ + srb->cmnd[5] = us->srb->cmnd[3]; + srb->cmnd[4] = us->srb->cmnd[2]; + srb->cmnd[3] = 0; + srb->cmnd[2] = 0; + + /* LUN and other info in cmnd[1] can stay */ + + /* fix command code */ + srb->cmnd[0] = 0x2A; + + US_DEBUGP("Changing WRITE_6 to WRITE_10\n"); + US_DEBUG(usb_stor_show_command(us->srb)); + } + } /* if (us->flags & US_FL_MODE_XLATE) */ + + /* send the command to the transport layer */ + usb_stor_invoke_transport(srb, us); + + /* fix the results of an INQUIRY */ + if (srb->cmnd[0] == INQUIRY) { + US_DEBUGP("Fixing INQUIRY data, setting SCSI rev to 2\n"); + ((unsigned char*)us->srb->request_buffer)[2] |= 2; + } +} + diff --git a/drivers/usb/storage/protocol.h b/drivers/usb/storage/protocol.h new file mode 100644 index 000000000..1a343af41 --- /dev/null +++ b/drivers/usb/storage/protocol.h @@ -0,0 +1,63 @@ +/* Driver for USB Mass Storage compliant devices + * Protocol Functions Header File + * + * $Id: protocol.h,v 1.1 2000/06/27 01:25:28 mdharm Exp $ + * + * Current development and maintainance by: + * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) + * + * This driver is based on the 'USB Mass Storage Class' document. This + * describes in detail the protocol used to communicate with such + * devices. Clearly, the designers had SCSI and ATAPI commands in + * mind when they created this document. The commands are all very + * similar to commands in the SCSI-II and ATAPI specifications. + * + * It is important to note that in a number of cases this class + * exhibits class-specific exemptions from the USB specification. + * Notably the usage of NAK, STALL and ACK differs from the norm, in + * that they are used to communicate wait, failed and OK on commands. + * + * Also, for certain devices, the interrupt endpoint is used to convey + * status of a command. + * + * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more + * information about this driver. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _PROTOCOL_H_ +#define _PROTOCOL_H_ + +#include <linux/blk.h> +#include "scsi.h" +#include "usb.h" + +/* Sub Classes */ + +#define US_SC_RBC 0x01 /* Typically, flash devices */ +#define US_SC_8020 0x02 /* CD-ROM */ +#define US_SC_QIC 0x03 /* QIC-157 Tapes */ +#define US_SC_UFI 0x04 /* Floppy */ +#define US_SC_8070 0x05 /* Removable media */ +#define US_SC_SCSI 0x06 /* Transparent */ +#define US_SC_MIN US_SC_RBC +#define US_SC_MAX US_SC_SCSI + +extern void usb_stor_ATAPI_command(Scsi_Cmnd*, struct us_data*); +extern void usb_stor_ufi_command(Scsi_Cmnd*, struct us_data*); +extern void usb_stor_transparent_scsi_command(Scsi_Cmnd*, struct us_data*); + +#endif diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c new file mode 100644 index 000000000..71487235d --- /dev/null +++ b/drivers/usb/storage/scsiglue.c @@ -0,0 +1,824 @@ +/* Driver for USB Mass Storage compliant devices + * SCSI layer glue code + * + * $Id: scsiglue.c,v 1.2 2000/06/27 10:20:39 mdharm Exp $ + * + * Current development and maintainance by: + * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) + * + * Developed with the assistance of: + * (c) 2000 David L. Brown, Jr. (usb-storage@davidb.org) + * + * Initial work by: + * (c) 1999 Michael Gee (michael@linuxspecific.com) + * + * This driver is based on the 'USB Mass Storage Class' document. This + * describes in detail the protocol used to communicate with such + * devices. Clearly, the designers had SCSI and ATAPI commands in + * mind when they created this document. The commands are all very + * similar to commands in the SCSI-II and ATAPI specifications. + * + * It is important to note that in a number of cases this class + * exhibits class-specific exemptions from the USB specification. + * Notably the usage of NAK, STALL and ACK differs from the norm, in + * that they are used to communicate wait, failed and OK on commands. + * + * Also, for certain devices, the interrupt endpoint is used to convey + * status of a command. + * + * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more + * information about this driver. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include "scsiglue.h" +#include "usb.h" +#include "debug.h" + +#include <linux/malloc.h> + +/* direction table -- this indicates the direction of the data + * transfer for each command code -- a 1 indicates input + */ +/* FIXME: we need to use the new direction indicators in the Scsi_Cmnd + * structure, not this table. First we need to evaluate if it's being set + * correctly for us, though + */ +unsigned char us_direction[256/8] = { + 0x28, 0x81, 0x14, 0x14, 0x20, 0x01, 0x90, 0x77, + 0x0C, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + + +/* + * kernel thread actions + */ + +#define US_ACT_COMMAND 1 +#define US_ACT_DEVICE_RESET 2 +#define US_ACT_BUS_RESET 3 +#define US_ACT_HOST_RESET 4 +#define US_ACT_EXIT 5 + +/*********************************************************************** + * Host functions + ***********************************************************************/ + +static const char* host_info(struct Scsi_Host *host) +{ + return "SCSI emulation for USB Mass Storage devices"; +} + +/* detect a virtual adapter (always works) */ +static int detect(struct SHT *sht) +{ + struct us_data *us; + char local_name[32]; + + /* This is not nice at all, but how else are we to get the + * data here? */ + us = (struct us_data *)sht->proc_dir; + + /* set up the name of our subdirectory under /proc/scsi/ */ + sprintf(local_name, "usb-storage-%d", us->host_number); + sht->proc_name = kmalloc (strlen(local_name) + 1, GFP_KERNEL); + if (!sht->proc_name) + return 0; + strcpy(sht->proc_name, local_name); + + /* we start with no /proc directory entry */ + sht->proc_dir = NULL; + + /* register the host */ + us->host = scsi_register(sht, sizeof(us)); + if (us->host) { + us->host->hostdata[0] = (unsigned long)us; + us->host_no = us->host->host_no; + return 1; + } + + /* odd... didn't register properly. Abort and free pointers */ + kfree(sht->proc_name); + sht->proc_name = NULL; + return 0; +} + +/* Release all resources used by the virtual host + * + * NOTE: There is no contention here, because we're allready deregistered + * the driver and we're doing each virtual host in turn, not in parallel + */ +static int release(struct Scsi_Host *psh) +{ + struct us_data *us = (struct us_data *)psh->hostdata[0]; + + US_DEBUGP("us_release() called for host %s\n", us->htmplt.name); + + /* Kill the control threads + * + * Enqueue the command, wake up the thread, and wait for + * notification that it's exited. + */ + US_DEBUGP("-- sending US_ACT_EXIT command to thread\n"); + us->action = US_ACT_EXIT; + up(&(us->sleeper)); + down(&(us->notify)); + + /* free the data structure we were using */ + US_DEBUGP("-- freeing URB\n"); + kfree(us->current_urb); + (struct us_data*)psh->hostdata[0] = NULL; + + /* we always have a successful release */ + return 0; +} + +/* run command */ +static int command( Scsi_Cmnd *srb ) +{ + US_DEBUGP("Bad use of us_command\n"); + + return DID_BAD_TARGET << 16; +} + +/* run command */ +static int queuecommand( Scsi_Cmnd *srb , void (*done)(Scsi_Cmnd *)) +{ + struct us_data *us = (struct us_data *)srb->host->hostdata[0]; + + US_DEBUGP("queuecommand() called\n"); + srb->host_scribble = (unsigned char *)us; + + /* get exclusive access to the structures we want */ + down(&(us->queue_exclusion)); + + /* enqueue the command */ + us->queue_srb = srb; + srb->scsi_done = done; + us->action = US_ACT_COMMAND; + + /* wake up the process task */ + up(&(us->queue_exclusion)); + up(&(us->sleeper)); + + return 0; +} + +/*********************************************************************** + * Error handling functions + ***********************************************************************/ + +/* Command abort + * + * Note that this is really only meaningful right now for CBI transport + * devices which have failed to give us the command completion interrupt + */ +static int command_abort( Scsi_Cmnd *srb ) +{ + struct us_data *us = (struct us_data *)srb->host->hostdata[0]; + api_wrapper_data *awd = (api_wrapper_data *)us->current_urb->context; + + US_DEBUGP("command_abort() called\n"); + + /* if we're stuck waiting for an IRQ, simulate it */ + if (us->ip_wanted) { + US_DEBUGP("-- simulating missing IRQ\n"); + up(&(us->ip_waitq)); + return SUCCESS; + } + + /* if we have an urb pending, let's wake the control thread up */ + if (us->current_urb->status == -EINPROGRESS) { + /* cancel the URB */ + usb_unlink_urb(us->current_urb); + + /* wake the control thread up */ + if (waitqueue_active(awd->wakeup)) + wake_up(awd->wakeup); + + /* wait for us to be done */ + down(&(us->notify)); + return SUCCESS; + } + + US_DEBUGP ("-- nothing to abort\n"); + return FAILED; +} + +/* FIXME: this doesn't do anything right now */ +static int bus_reset( Scsi_Cmnd *srb ) +{ + // struct us_data *us = (struct us_data *)srb->host->hostdata[0]; + + printk(KERN_CRIT "usb-storage: bus_reset() requested but not implemented\n" ); + US_DEBUGP("Bus reset requested\n"); + // us->transport_reset(us); + return FAILED; +} + +/* FIXME: This doesn't actually reset anything */ +static int host_reset( Scsi_Cmnd *srb ) +{ + printk(KERN_CRIT "usb-storage: host_reset() requested but not implemented\n" ); + return FAILED; +} + +/*********************************************************************** + * /proc/scsi/ functions + ***********************************************************************/ + +/* we use this macro to help us write into the buffer */ +#undef SPRINTF +#define SPRINTF(args...) \ + do { if (pos < buffer+length) pos += sprintf(pos, ## args); } while (0) + +static int proc_info (char *buffer, char **start, off_t offset, int length, + int hostno, int inout) +{ + struct us_data *us; + char *pos = buffer; + + /* if someone is sending us data, just throw it away */ + if (inout) + return length; + + /* lock the data structures */ + down(&us_list_semaphore); + + /* find our data from hostno */ + us = us_list; + while (us) { + if (us->host_no == hostno) + break; + us = us->next; + } + + /* if we couldn't find it, we return an error */ + if (!us) { + up(&us_list_semaphore); + return -ESRCH; + } + + /* print the controler name */ + SPRINTF(" Host scsi%d: usb-storage\n", hostno); + + /* print product, vendor, and serial number strings */ + SPRINTF(" Vendor: %s\n", us->vendor); + SPRINTF(" Product: %s\n", us->product); + SPRINTF("Serial Number: %s\n", us->serial); + + /* show the protocol and transport */ + SPRINTF(" Protocol: %s\n", us->protocol_name); + SPRINTF(" Transport: %s\n", us->transport_name); + + /* show the GUID of the device */ + SPRINTF(" GUID: " GUID_FORMAT "\n", GUID_ARGS(us->guid)); + + /* release our lock on the data structures */ + up(&us_list_semaphore); + + /* + * 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); +} + +/* + * this defines our 'host' + */ + +Scsi_Host_Template usb_stor_host_template = { + name: "usb-storage", + proc_info: proc_info, + info: host_info, + + detect: detect, + release: release, + command: command, + queuecommand: queuecommand, + + eh_abort_handler: command_abort, + eh_device_reset_handler:bus_reset, + eh_bus_reset_handler: bus_reset, + eh_host_reset_handler: host_reset, + + can_queue: 1, + this_id: -1, + + sg_tablesize: SG_ALL, + cmd_per_lun: 1, + present: 0, + unchecked_isa_dma: FALSE, + use_clustering: TRUE, + use_new_eh_code: TRUE, + emulated: TRUE +}; + +unsigned char usb_stor_sense_notready[12] = { + [0] = 0x70, /* current error */ + [2] = 0x02, /* not ready */ + [5] = 0x0a, /* additional length */ + [10] = 0x04, /* not ready */ + [11] = 0x03 /* manual intervention */ +}; + +#define USB_STOR_SCSI_SENSE_HDRSZ 4 +#define USB_STOR_SCSI_SENSE_10_HDRSZ 8 + +struct usb_stor_scsi_sense_hdr +{ + __u8* dataLength; + __u8* mediumType; + __u8* devSpecParms; + __u8* blkDescLength; +}; + +typedef struct usb_stor_scsi_sense_hdr Usb_Stor_Scsi_Sense_Hdr; + +union usb_stor_scsi_sense_hdr_u +{ + Usb_Stor_Scsi_Sense_Hdr hdr; + __u8* array[USB_STOR_SCSI_SENSE_HDRSZ]; +}; + +typedef union usb_stor_scsi_sense_hdr_u Usb_Stor_Scsi_Sense_Hdr_u; + +struct usb_stor_scsi_sense_hdr_10 +{ + __u8* dataLengthMSB; + __u8* dataLengthLSB; + __u8* mediumType; + __u8* devSpecParms; + __u8* reserved1; + __u8* reserved2; + __u8* blkDescLengthMSB; + __u8* blkDescLengthLSB; +}; + +typedef struct usb_stor_scsi_sense_hdr_10 Usb_Stor_Scsi_Sense_Hdr_10; + +union usb_stor_scsi_sense_hdr_10_u +{ + Usb_Stor_Scsi_Sense_Hdr_10 hdr; + __u8* array[USB_STOR_SCSI_SENSE_10_HDRSZ]; +}; + +typedef union usb_stor_scsi_sense_hdr_10_u Usb_Stor_Scsi_Sense_Hdr_10_u; + +void usb_stor_scsiSenseParseBuffer( Scsi_Cmnd* , Usb_Stor_Scsi_Sense_Hdr_u*, + Usb_Stor_Scsi_Sense_Hdr_10_u*, int* ); + +int usb_stor_scsiSense10to6( Scsi_Cmnd* the10 ) +{ + __u8 *buffer=0; + int outputBufferSize = 0; + int length=0; + struct scatterlist *sg = 0; + int i=0, j=0, element=0; + Usb_Stor_Scsi_Sense_Hdr_u the6Locations; + Usb_Stor_Scsi_Sense_Hdr_10_u the10Locations; + int sb=0,si=0,db=0,di=0; + int sgLength=0; + + US_DEBUGP("-- converting 10 byte sense data to 6 byte\n"); + the10->cmnd[0] = the10->cmnd[0] & 0xBF; + + /* Determine buffer locations */ + usb_stor_scsiSenseParseBuffer( the10, &the6Locations, &the10Locations, + &length ); + + /* Work out minimum buffer to output */ + outputBufferSize = *the10Locations.hdr.dataLengthLSB; + outputBufferSize += USB_STOR_SCSI_SENSE_HDRSZ; + + /* Check to see if we need to trucate the output */ + if ( outputBufferSize > length ) + { + printk( KERN_WARNING USB_STORAGE + "Had to truncate MODE_SENSE_10 buffer into MODE_SENSE.\n" ); + printk( KERN_WARNING USB_STORAGE + "outputBufferSize is %d and length is %d.\n", + outputBufferSize, length ); + } + outputBufferSize = length; + + /* Data length */ + if ( *the10Locations.hdr.dataLengthMSB != 0 ) /* MSB must be zero */ + { + printk( KERN_WARNING USB_STORAGE + "Command will be truncated to fit in SENSE6 buffer.\n" ); + *the6Locations.hdr.dataLength = 0xff; + } + else + { + *the6Locations.hdr.dataLength = *the10Locations.hdr.dataLengthLSB; + } + + /* Medium type and DevSpecific parms */ + *the6Locations.hdr.mediumType = *the10Locations.hdr.mediumType; + *the6Locations.hdr.devSpecParms = *the10Locations.hdr.devSpecParms; + + /* Block descriptor length */ + if ( *the10Locations.hdr.blkDescLengthMSB != 0 ) /* MSB must be zero */ + { + printk( KERN_WARNING USB_STORAGE + "Command will be truncated to fit in SENSE6 buffer.\n" ); + *the6Locations.hdr.blkDescLength = 0xff; + } + else + { + *the6Locations.hdr.blkDescLength = *the10Locations.hdr.blkDescLengthLSB; + } + + if ( the10->use_sg == 0 ) + { + buffer = the10->request_buffer; + /* Copy the rest of the data */ + memmove( &(buffer[USB_STOR_SCSI_SENSE_HDRSZ]), + &(buffer[USB_STOR_SCSI_SENSE_10_HDRSZ]), + outputBufferSize - USB_STOR_SCSI_SENSE_HDRSZ ); + /* initialise last bytes left in buffer due to smaller header */ + memset( &(buffer[outputBufferSize + -(USB_STOR_SCSI_SENSE_10_HDRSZ-USB_STOR_SCSI_SENSE_HDRSZ)]), + 0, + USB_STOR_SCSI_SENSE_10_HDRSZ-USB_STOR_SCSI_SENSE_HDRSZ ); + } + else + { + sg = (struct scatterlist *) the10->request_buffer; + /* scan through this scatterlist and figure out starting positions */ + for ( i=0; i < the10->use_sg; i++) + { + sgLength = sg[i].length; + for ( j=0; j<sgLength; j++ ) + { + /* get to end of header */ + if ( element == USB_STOR_SCSI_SENSE_HDRSZ ) + { + db=i; + di=j; + } + if ( element == USB_STOR_SCSI_SENSE_10_HDRSZ ) + { + sb=i; + si=j; + /* we've found both sets now, exit loops */ + j=sgLength; + i=the10->use_sg; + } + element++; + } + } + + /* Now we know where to start the copy from */ + element = USB_STOR_SCSI_SENSE_HDRSZ; + while ( element < outputBufferSize + -(USB_STOR_SCSI_SENSE_10_HDRSZ-USB_STOR_SCSI_SENSE_HDRSZ) ) + { + /* check limits */ + if ( sb >= the10->use_sg || + si >= sg[sb].length || + db >= the10->use_sg || + di >= sg[db].length ) + { + printk( KERN_ERR USB_STORAGE + "Buffer overrun averted, this shouldn't happen!\n" ); + break; + } + + /* copy one byte */ + sg[db].address[di] = sg[sb].address[si]; + + /* get next destination */ + if ( sg[db].length-1 == di ) + { + db++; + di=0; + } + else + { + di++; + } + + /* get next source */ + if ( sg[sb].length-1 == si ) + { + sb++; + si=0; + } + else + { + si++; + } + + element++; + } + /* zero the remaining bytes */ + while ( element < outputBufferSize ) + { + /* check limits */ + if ( db >= the10->use_sg || + di >= sg[db].length ) + { + printk( KERN_ERR USB_STORAGE + "Buffer overrun averted, this shouldn't happen!\n" ); + break; + } + + sg[db].address[di] = 0; + + /* get next destination */ + if ( sg[db].length-1 == di ) + { + db++; + di=0; + } + else + { + di++; + } + element++; + } + } + + /* All done any everything was fine */ + return 0; +} + +int usb_stor_scsiSense6to10( Scsi_Cmnd* the6 ) +{ + /* will be used to store part of buffer */ + __u8 tempBuffer[USB_STOR_SCSI_SENSE_10_HDRSZ-USB_STOR_SCSI_SENSE_HDRSZ], + *buffer=0; + int outputBufferSize = 0; + int length=0; + struct scatterlist *sg = 0; + int i=0, j=0, element=0; + Usb_Stor_Scsi_Sense_Hdr_u the6Locations; + Usb_Stor_Scsi_Sense_Hdr_10_u the10Locations; + int sb=0,si=0,db=0,di=0; + int lsb=0,lsi=0,ldb=0,ldi=0; + + US_DEBUGP("-- converting 6 byte sense data to 10 byte\n"); + the6->cmnd[0] = the6->cmnd[0] | 0x40; + + /* Determine buffer locations */ + usb_stor_scsiSenseParseBuffer( the6, &the6Locations, &the10Locations, + &length ); + + /* Work out minimum buffer to output */ + outputBufferSize = *the6Locations.hdr.dataLength; + outputBufferSize += USB_STOR_SCSI_SENSE_10_HDRSZ; + + /* Check to see if we need to trucate the output */ + if ( outputBufferSize > length ) + { + printk( KERN_WARNING USB_STORAGE + "Had to truncate MODE_SENSE into MODE_SENSE_10 buffer.\n" ); + printk( KERN_WARNING USB_STORAGE + "outputBufferSize is %d and length is %d.\n", + outputBufferSize, length ); + } + outputBufferSize = length; + + /* Block descriptor length - save these before overwriting */ + tempBuffer[2] = *the10Locations.hdr.blkDescLengthMSB; + tempBuffer[3] = *the10Locations.hdr.blkDescLengthLSB; + *the10Locations.hdr.blkDescLengthLSB = *the6Locations.hdr.blkDescLength; + *the10Locations.hdr.blkDescLengthMSB = 0; + + /* reserved - save these before overwriting */ + tempBuffer[0] = *the10Locations.hdr.reserved1; + tempBuffer[1] = *the10Locations.hdr.reserved2; + *the10Locations.hdr.reserved1 = *the10Locations.hdr.reserved2 = 0; + + /* Medium type and DevSpecific parms */ + *the10Locations.hdr.devSpecParms = *the6Locations.hdr.devSpecParms; + *the10Locations.hdr.mediumType = *the6Locations.hdr.mediumType; + + /* Data length */ + *the10Locations.hdr.dataLengthLSB = *the6Locations.hdr.dataLength; + *the10Locations.hdr.dataLengthMSB = 0; + + if ( !the6->use_sg ) + { + buffer = the6->request_buffer; + /* Copy the rest of the data */ + memmove( &(buffer[USB_STOR_SCSI_SENSE_10_HDRSZ]), + &(buffer[USB_STOR_SCSI_SENSE_HDRSZ]), + outputBufferSize-USB_STOR_SCSI_SENSE_10_HDRSZ ); + /* Put the first four bytes (after header) in place */ + memcpy( &(buffer[USB_STOR_SCSI_SENSE_10_HDRSZ]), + tempBuffer, + USB_STOR_SCSI_SENSE_10_HDRSZ-USB_STOR_SCSI_SENSE_HDRSZ ); + } + else + { + sg = (struct scatterlist *) the6->request_buffer; + /* scan through this scatterlist and figure out ending positions */ + for ( i=0; i < the6->use_sg; i++) + { + for ( j=0; j<sg[i].length; j++ ) + { + /* get to end of header */ + if ( element == USB_STOR_SCSI_SENSE_HDRSZ ) + { + ldb=i; + ldi=j; + } + if ( element == USB_STOR_SCSI_SENSE_10_HDRSZ ) + { + lsb=i; + lsi=j; + /* we've found both sets now, exit loops */ + j=sg[i].length; + i=the6->use_sg; + break; + } + element++; + } + } + /* scan through this scatterlist and figure out starting positions */ + element = length-1; + /* destination is the last element */ + db=the6->use_sg-1; + di=sg[db].length-1; + for ( i=the6->use_sg-1; i >= 0; i--) + { + for ( j=sg[i].length-1; j>=0; j-- ) + { + /* get to end of header and find source for copy */ + if ( element == length - 1 + - (USB_STOR_SCSI_SENSE_10_HDRSZ-USB_STOR_SCSI_SENSE_HDRSZ) ) + { + sb=i; + si=j; + /* we've found both sets now, exit loops */ + j=-1; + i=-1; + } + element--; + } + } + /* Now we know where to start the copy from */ + element = length-1 + - (USB_STOR_SCSI_SENSE_10_HDRSZ-USB_STOR_SCSI_SENSE_HDRSZ); + while ( element >= USB_STOR_SCSI_SENSE_10_HDRSZ ) + { + /* check limits */ + if ( ( sb <= lsb && si < lsi ) || + ( db <= ldb && di < ldi ) ) + { + printk( KERN_ERR USB_STORAGE + "Buffer overrun averted, this shouldn't happen!\n" ); + break; + } + + /* copy one byte */ + sg[db].address[di] = sg[sb].address[si]; + + /* get next destination */ + if ( di == 0 ) + { + db--; + di=sg[db].length-1; + } + else + { + di--; + } + + /* get next source */ + if ( si == 0 ) + { + sb--; + si=sg[sb].length-1; + } + else + { + si--; + } + + element--; + } + /* copy the remaining four bytes */ + while ( element >= USB_STOR_SCSI_SENSE_HDRSZ ) + { + /* check limits */ + if ( db <= ldb && di < ldi ) + { + printk( KERN_ERR USB_STORAGE + "Buffer overrun averted, this shouldn't happen!\n" ); + break; + } + + sg[db].address[di] = tempBuffer[element-USB_STOR_SCSI_SENSE_HDRSZ]; + + /* get next destination */ + if ( di == 0 ) + { + db--; + di=sg[db].length-1; + } + else + { + di--; + } + element--; + } + } + + /* All done and everything was fine */ + return 0; +} + +void usb_stor_scsiSenseParseBuffer( Scsi_Cmnd* srb, Usb_Stor_Scsi_Sense_Hdr_u* the6, + Usb_Stor_Scsi_Sense_Hdr_10_u* the10, + int* length_p ) + +{ + int i = 0, j=0, element=0; + struct scatterlist *sg = 0; + int length = 0; + __u8* buffer=0; + + /* are we scatter-gathering? */ + if ( srb->use_sg != 0 ) + { + /* loop over all the scatter gather structures and + * get pointer to the data members in the headers + * (also work out the length while we're here) + */ + sg = (struct scatterlist *) srb->request_buffer; + for (i = 0; i < srb->use_sg; i++) + { + length += sg[i].length; + /* We only do the inner loop for the headers */ + if ( element < USB_STOR_SCSI_SENSE_10_HDRSZ ) + { + /* scan through this scatterlist */ + for ( j=0; j<sg[i].length; j++ ) + { + if ( element < USB_STOR_SCSI_SENSE_HDRSZ ) + { + /* fill in the pointers for both header types */ + the6->array[element] = &(sg[i].address[j]); + the10->array[element] = &(sg[i].address[j]); + } + else if ( element < USB_STOR_SCSI_SENSE_10_HDRSZ ) + { + /* only the longer headers still cares now */ + the10->array[element] = &(sg[i].address[j]); + } + /* increase element counter */ + element++; + } + } + } + } + else + { + length = srb->request_bufflen; + buffer = srb->request_buffer; + if ( length < USB_STOR_SCSI_SENSE_10_HDRSZ ) + printk( KERN_ERR USB_STORAGE + "Buffer length smaller than header!!" ); + for( i=0; i<USB_STOR_SCSI_SENSE_10_HDRSZ; i++ ) + { + if ( i < USB_STOR_SCSI_SENSE_HDRSZ ) + { + the6->array[i] = &(buffer[i]); + the10->array[i] = &(buffer[i]); + } + else + { + the10->array[i] = &(buffer[i]); + } + } + } + + /* Set value of length passed in */ + *length_p = length; +} + diff --git a/drivers/usb/storage/scsiglue.h b/drivers/usb/storage/scsiglue.h new file mode 100644 index 000000000..ea8a34504 --- /dev/null +++ b/drivers/usb/storage/scsiglue.h @@ -0,0 +1,54 @@ +/* Driver for USB Mass Storage compliant devices + * SCSI Connecting Glue Header File + * + * $Id: scsiglue.h,v 1.1 2000/06/27 01:25:28 mdharm Exp $ + * + * Current development and maintainance by: + * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) + * + * This driver is based on the 'USB Mass Storage Class' document. This + * describes in detail the protocol used to communicate with such + * devices. Clearly, the designers had SCSI and ATAPI commands in + * mind when they created this document. The commands are all very + * similar to commands in the SCSI-II and ATAPI specifications. + * + * It is important to note that in a number of cases this class + * exhibits class-specific exemptions from the USB specification. + * Notably the usage of NAK, STALL and ACK differs from the norm, in + * that they are used to communicate wait, failed and OK on commands. + * + * Also, for certain devices, the interrupt endpoint is used to convey + * status of a command. + * + * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more + * information about this driver. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _SCSIGLUE_H_ +#define _SCSIGLUE_H_ + +#include <linux/blk.h> +#include "scsi.h" +#include "hosts.h" + +extern unsigned char usb_stor_sense_notready[12]; +extern unsigned char us_direction[256/8]; +extern Scsi_Host_Template usb_stor_host_template; +extern int usb_stor_scsiSense10to6(Scsi_Cmnd*); +extern int usb_stor_scsiSense6to10(Scsi_Cmnd*); + +#endif diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c new file mode 100644 index 000000000..c83a7512d --- /dev/null +++ b/drivers/usb/storage/transport.c @@ -0,0 +1,904 @@ +/* Driver for USB Mass Storage compliant devices + * + * $Id: transport.c,v 1.2 2000/06/27 10:20:39 mdharm Exp $ + * + * Current development and maintainance by: + * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) + * + * Developed with the assistance of: + * (c) 2000 David L. Brown, Jr. (usb-storage@davidb.org) + * + * Initial work by: + * (c) 1999 Michael Gee (michael@linuxspecific.com) + * + * This driver is based on the 'USB Mass Storage Class' document. This + * describes in detail the protocol used to communicate with such + * devices. Clearly, the designers had SCSI and ATAPI commands in + * mind when they created this document. The commands are all very + * similar to commands in the SCSI-II and ATAPI specifications. + * + * It is important to note that in a number of cases this class + * exhibits class-specific exemptions from the USB specification. + * Notably the usage of NAK, STALL and ACK differs from the norm, in + * that they are used to communicate wait, failed and OK on commands. + * + * Also, for certain devices, the interrupt endpoint is used to convey + * status of a command. + * + * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more + * information about this driver. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include "transport.h" +#include "protocol.h" +#include "usb.h" +#include "debug.h" + +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/malloc.h> + +/*********************************************************************** + * Data transfer routines + ***********************************************************************/ + +/* This is the completion handler which will wake us up when an URB + * completes. + */ +static void usb_stor_blocking_completion(urb_t *urb) +{ + api_wrapper_data *awd = (api_wrapper_data *)urb->context; + + if (waitqueue_active(awd->wakeup)) + wake_up(awd->wakeup); +} + +/* This is our function to emulate usb_control_msg() but give us enough + * access to make aborts/resets work + */ +int usb_stor_control_msg(struct us_data *us, unsigned int pipe, + u8 request, u8 requesttype, u16 value, u16 index, + void *data, u16 size) +{ + DECLARE_WAITQUEUE(wait, current); + DECLARE_WAIT_QUEUE_HEAD(wqh); + api_wrapper_data awd; + int status; + devrequest *dr; + + /* allocate the device request structure */ + dr = kmalloc(sizeof(devrequest), GFP_KERNEL); + if (!dr) + return -ENOMEM; + + /* fill in the structure */ + dr->requesttype = requesttype; + dr->request = request; + dr->value = cpu_to_le16(value); + dr->index = cpu_to_le16(index); + dr->length = cpu_to_le16(size); + + /* set up data structures for the wakeup system */ + awd.wakeup = &wqh; + awd.handler = 0; + init_waitqueue_head(&wqh); + add_wait_queue(&wqh, &wait); + + /* lock the URB */ + down(&(us->current_urb_sem)); + + /* fill the URB */ + FILL_CONTROL_URB(us->current_urb, us->pusb_dev, pipe, + (unsigned char*) dr, data, size, + usb_stor_blocking_completion, &awd); + + /* submit the URB */ + set_current_state(TASK_UNINTERRUPTIBLE); + status = usb_submit_urb(us->current_urb); + if (status) { + /* something went wrong */ + up(&(us->current_urb_sem)); + remove_wait_queue(&wqh, &wait); + kfree(dr); + return status; + } + + /* wait for the completion of the URB */ + up(&(us->current_urb_sem)); + if (us->current_urb->status == -EINPROGRESS) + schedule(); + down(&(us->current_urb_sem)); + + /* we either timed out or got woken up -- clean up either way */ + set_current_state(TASK_RUNNING); + remove_wait_queue(&wqh, &wait); + + /* return the actual length of the data transferred if no error*/ + status = us->current_urb->status; + if (status >= 0) + status = us->current_urb->actual_length; + + /* release the lock and return status */ + up(&(us->current_urb_sem)); + kfree(dr); + return status; +} + +/* This is our function to emulate usb_bulk_msg() but give us enough + * access to make aborts/resets work + */ +int usb_stor_bulk_msg(struct us_data *us, void *data, int pipe, + unsigned int len, unsigned int *act_len) +{ + DECLARE_WAITQUEUE(wait, current); + DECLARE_WAIT_QUEUE_HEAD(wqh); + api_wrapper_data awd; + int status; + + /* set up data structures for the wakeup system */ + awd.wakeup = &wqh; + awd.handler = 0; + init_waitqueue_head(&wqh); + add_wait_queue(&wqh, &wait); + + /* lock the URB */ + down(&(us->current_urb_sem)); + + /* fill the URB */ + FILL_BULK_URB(us->current_urb, us->pusb_dev, pipe, data, len, + usb_stor_blocking_completion, &awd); + + /* submit the URB */ + set_current_state(TASK_UNINTERRUPTIBLE); + status = usb_submit_urb(us->current_urb); + if (status) { + /* something went wrong */ + up(&(us->current_urb_sem)); + remove_wait_queue(&wqh, &wait); + return status; + } + + /* wait for the completion of the URB */ + up(&(us->current_urb_sem)); + if (us->current_urb->status == -EINPROGRESS) + schedule(); + down(&(us->current_urb_sem)); + + /* we either timed out or got woken up -- clean up either way */ + set_current_state(TASK_RUNNING); + remove_wait_queue(&wqh, &wait); + + /* return the actual length of the data transferred */ + *act_len = us->current_urb->actual_length; + + /* release the lock and return status */ + up(&(us->current_urb_sem)); + return us->current_urb->status; +} + +/* + * Transfer one SCSI scatter-gather buffer via bulk transfer + * + * Note that this function is necessary because we want the ability to + * use scatter-gather memory. Good performance is achieved by a combination + * of scatter-gather and clustering (which makes each chunk bigger). + * + * Note that the lower layer will always retry when a NAK occurs, up to the + * timeout limit. Thus we don't have to worry about it for individual + * packets. + */ +static int us_transfer_partial(struct us_data *us, char *buf, int length) +{ + int result; + int partial; + int pipe; + + /* calculate the appropriate pipe information */ + if (US_DIRECTION(us->srb->cmnd[0])) + pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in); + else + pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out); + + /* transfer the data */ + US_DEBUGP("us_transfer_partial(): xfer %d bytes\n", length); + result = usb_stor_bulk_msg(us, buf, pipe, length, &partial); + US_DEBUGP("usb_stor_bulk_msg() returned %d xferred %d/%d\n", + result, partial, length); + + /* if we stall, we need to clear it before we go on */ + if (result == -EPIPE) { + US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe); + usb_clear_halt(us->pusb_dev, pipe); + } + + /* did we send all the data? */ + if (partial == length) { + US_DEBUGP("us_transfer_partial(): transfer complete\n"); + return US_BULK_TRANSFER_GOOD; + } + + /* uh oh... we have an error code, so something went wrong. */ + if (result) { + /* NAK - that means we've retried a few times allready */ + if (result == -ETIMEDOUT) { + US_DEBUGP("us_transfer_partial(): device NAKed\n"); + return US_BULK_TRANSFER_FAILED; + } + + /* -ENOENT -- we canceled this transfer */ + if (result == -ENOENT) { + US_DEBUGP("us_transfer_partial(): transfer aborted\n"); + return US_BULK_TRANSFER_ABORTED; + } + + /* the catch-all case */ + US_DEBUGP("us_transfer_partial(): unknown error\n"); + return US_BULK_TRANSFER_FAILED; + } + + /* no error code, so we must have transferred some data, + * just not all of it */ + return US_BULK_TRANSFER_SHORT; +} + +/* + * Transfer an entire SCSI command's worth of data payload over the bulk + * pipe. + * + * Note that this uses us_transfer_partial to achieve it's goals -- this + * function simply determines if we're going to use scatter-gather or not, + * and acts appropriately. For now, it also re-interprets the error codes. + */ +static void us_transfer(Scsi_Cmnd *srb, struct us_data* us, int dir_in) +{ + int i; + int result = -1; + struct scatterlist *sg; + + /* are we scatter-gathering? */ + if (srb->use_sg) { + + /* loop over all the scatter gather structures and + * make the appropriate requests for each, until done + */ + sg = (struct scatterlist *) srb->request_buffer; + for (i = 0; i < srb->use_sg; i++) { + result = us_transfer_partial(us, sg[i].address, + sg[i].length); + if (result) + break; + } + } + else + /* no scatter-gather, just make the request */ + result = us_transfer_partial(us, srb->request_buffer, + srb->request_bufflen); + + /* return the result in the data structure itself */ + srb->result = result; +} + +/* Calculate the length of the data transfer (not the command) for any + * given SCSI command + */ +static unsigned int us_transfer_length(Scsi_Cmnd *srb, struct us_data *us) +{ + int i; + unsigned int total = 0; + struct scatterlist *sg; + + /* support those devices which need the length calculated + * differently + */ + if (us->flags & US_FL_ALT_LENGTH) { + if (srb->cmnd[0] == INQUIRY) { + srb->cmnd[4] = 36; + } + + if ((srb->cmnd[0] == INQUIRY) || (srb->cmnd[0] == MODE_SENSE)) + return srb->cmnd[4]; + + if (srb->cmnd[0] == TEST_UNIT_READY) + return 0; + } + + /* Are we going to scatter gather? */ + if (srb->use_sg) { + /* Add up the sizes of all the scatter-gather segments */ + sg = (struct scatterlist *) srb->request_buffer; + for (i = 0; i < srb->use_sg; i++) + total += sg[i].length; + + return total; + } + else + /* Just return the length of the buffer */ + return srb->request_bufflen; +} + +/*********************************************************************** + * Transport routines + ***********************************************************************/ + +/* Invoke the transport and basic error-handling/recovery methods + * + * This is used by the protocol layers to actually send the message to + * the device and recieve the response. + */ +void usb_stor_invoke_transport(Scsi_Cmnd *srb, struct us_data *us) +{ + int need_auto_sense; + int result; + + /* send the command to the transport layer */ + result = us->transport(srb, us); + + /* if the command gets aborted by the higher layers, we need to + * short-circuit all other processing + */ + if (result == USB_STOR_TRANSPORT_ABORTED) { + US_DEBUGP("-- transport indicates command was aborted\n"); + srb->result = DID_ABORT << 16; + return; + } + + /* Determine if we need to auto-sense + * + * I normally don't use a flag like this, but it's almost impossible + * to understand what's going on here if I don't. + */ + need_auto_sense = 0; + + /* + * If we're running the CB transport, which is incapable + * of determining status on it's own, we need to auto-sense almost + * every time. + */ + if (us->protocol == US_PR_CB) { + US_DEBUGP("-- CB transport device requiring auto-sense\n"); + need_auto_sense = 1; + + /* There are some exceptions to this. Notably, if this is + * a UFI device and the command is REQUEST_SENSE or INQUIRY, + * then it is impossible to truly determine status. + */ + if (us->subclass == US_SC_UFI && + ((srb->cmnd[0] == REQUEST_SENSE) || + (srb->cmnd[0] == INQUIRY))) { + US_DEBUGP("** no auto-sense for a special command\n"); + need_auto_sense = 0; + } + } + + /* + * If we have an error, we're going to do a REQUEST_SENSE + * automatically. Note that we differentiate between a command + * "failure" and an "error" in the transport mechanism. + */ + if (result == USB_STOR_TRANSPORT_FAILED) { + US_DEBUGP("-- transport indicates command failure\n"); + need_auto_sense = 1; + } + if (result == USB_STOR_TRANSPORT_ERROR) { + /* FIXME: we need to invoke a transport reset here */ + US_DEBUGP("-- transport indicates transport failure\n"); + need_auto_sense = 0; + srb->result = DID_ERROR << 16; + return; + } + + /* + * Also, if we have a short transfer on a command that can't have + * a short transfer, we're going to do this. + */ + if ((srb->result == US_BULK_TRANSFER_SHORT) && + !((srb->cmnd[0] == REQUEST_SENSE) || + (srb->cmnd[0] == INQUIRY) || + (srb->cmnd[0] == MODE_SENSE) || + (srb->cmnd[0] == LOG_SENSE) || + (srb->cmnd[0] == MODE_SENSE_10))) { + US_DEBUGP("-- unexpectedly short transfer\n"); + need_auto_sense = 1; + } + + /* Now, if we need to do the auto-sense, let's do it */ + if (need_auto_sense) { + int temp_result; + void* old_request_buffer; + int old_sg; + int old_request_bufflen; + unsigned char old_cmnd[MAX_COMMAND_SIZE]; + + US_DEBUGP("Issuing auto-REQUEST_SENSE\n"); + + /* save the old command */ + memcpy(old_cmnd, srb->cmnd, MAX_COMMAND_SIZE); + + /* set the command and the LUN */ + srb->cmnd[0] = REQUEST_SENSE; + srb->cmnd[1] = old_cmnd[1] & 0xE0; + srb->cmnd[2] = 0; + srb->cmnd[3] = 0; + srb->cmnd[4] = 18; + srb->cmnd[5] = 0; + + /* set the buffer length for transfer */ + old_request_buffer = srb->request_buffer; + old_request_bufflen = srb->request_bufflen; + old_sg = srb->use_sg; + srb->use_sg = 0; + srb->request_bufflen = 18; + srb->request_buffer = srb->sense_buffer; + + /* issue the auto-sense command */ + temp_result = us->transport(us->srb, us); + if (temp_result != USB_STOR_TRANSPORT_GOOD) { + /* FIXME: we need to invoke a transport reset here */ + US_DEBUGP("-- auto-sense failure\n"); + srb->result = DID_ERROR << 16; + return; + } + + US_DEBUGP("-- Result from auto-sense is %d\n", temp_result); + US_DEBUGP("-- code: 0x%x, key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n", + srb->sense_buffer[0], + srb->sense_buffer[2] & 0xf, + srb->sense_buffer[12], + srb->sense_buffer[13]); + + /* set the result so the higher layers expect this data */ + srb->result = CHECK_CONDITION; + + /* we're done here, let's clean up */ + srb->request_buffer = old_request_buffer; + srb->request_bufflen = old_request_bufflen; + srb->use_sg = old_sg; + memcpy(srb->cmnd, old_cmnd, MAX_COMMAND_SIZE); + + /* If things are really okay, then let's show that */ + if ((srb->sense_buffer[2] & 0xf) == 0x0) + srb->result = GOOD; + } else /* if (need_auto_sense) */ + srb->result = GOOD; + + /* Regardless of auto-sense, if we _know_ we have an error + * condition, show that in the result code + */ + if (result == USB_STOR_TRANSPORT_FAILED) + srb->result = CHECK_CONDITION; + + /* If we think we're good, then make sure the sense data shows it. + * This is necessary because the auto-sense for some devices always + * sets byte 0 == 0x70, even if there is no error + */ + if ((us->protocol == US_PR_CB) && + (result == USB_STOR_TRANSPORT_GOOD) && + ((srb->sense_buffer[2] & 0xf) == 0x0)) + srb->sense_buffer[0] = 0x0; +} + +/* + * Control/Bulk/Interrupt transport + */ + +/* The interrupt handler for CBI devices */ +void usb_stor_CBI_irq(struct urb *urb) +{ + struct us_data *us = (struct us_data *)urb->context; + + US_DEBUGP("USB IRQ recieved for device on host %d\n", us->host_no); + US_DEBUGP("-- IRQ data length is %d\n", urb->actual_length); + US_DEBUGP("-- IRQ state is %d\n", urb->status); + + /* is the device removed? */ + if (urb->status != -ENOENT) { + /* save the data for interpretation later */ + US_DEBUGP("-- Interrupt Status (0x%x, 0x%x)\n", + ((unsigned char*)urb->transfer_buffer)[0], + ((unsigned char*)urb->transfer_buffer)[1]); + + + /* was this a wanted interrupt? */ + if (us->ip_wanted) { + us->ip_wanted = 0; + up(&(us->ip_waitq)); + } else + US_DEBUGP("ERROR: Unwanted interrupt received!\n"); + } else + US_DEBUGP("-- device has been removed\n"); +} + +int usb_stor_CBI_transport(Scsi_Cmnd *srb, struct us_data *us) +{ + int result; + + /* COMMAND STAGE */ + /* let's send the command via the control pipe */ + result = usb_stor_control_msg(us, usb_sndctrlpipe(us->pusb_dev,0), + US_CBI_ADSC, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, + us->ifnum, srb->cmnd, srb->cmd_len); + + /* check the return code for the command */ + US_DEBUGP("Call to usb_stor_control_msg() returned %d\n", result); + if (result < 0) { + /* if the command was aborted, indicate that */ + if (result == -ENOENT) + return USB_STOR_TRANSPORT_ABORTED; + + /* STALL must be cleared when they are detected */ + if (result == -EPIPE) { + US_DEBUGP("-- Stall on control pipe. Clearing\n"); + result = usb_clear_halt(us->pusb_dev, + usb_sndctrlpipe(us->pusb_dev, + 0)); + US_DEBUGP("-- usb_clear_halt() returns %d\n", result); + return USB_STOR_TRANSPORT_FAILED; + } + + /* Uh oh... serious problem here */ + return USB_STOR_TRANSPORT_ERROR; + } + + /* Set up for status notification */ + us->ip_wanted = 1; + + /* DATA STAGE */ + /* transfer the data payload for this command, if one exists*/ + if (us_transfer_length(srb, us)) { + us_transfer(srb, us, US_DIRECTION(srb->cmnd[0])); + US_DEBUGP("CBI data stage result is 0x%x\n", srb->result); + + /* if it was aborted, we need to indicate that */ + if (srb->result == USB_STOR_TRANSPORT_ABORTED) + return USB_STOR_TRANSPORT_ABORTED; + } + + /* STATUS STAGE */ + + /* go to sleep until we get this interrupt */ + down(&(us->ip_waitq)); + + /* if we were woken up by an abort instead of the actual interrupt */ + if (us->ip_wanted) { + US_DEBUGP("Did not get interrupt on CBI\n"); + us->ip_wanted = 0; + return USB_STOR_TRANSPORT_ABORTED; + } + + US_DEBUGP("Got interrupt data (0x%x, 0x%x)\n", + ((unsigned char*)us->irq_urb->transfer_buffer)[0], + ((unsigned char*)us->irq_urb->transfer_buffer)[1]); + + /* UFI gives us ASC and ASCQ, like a request sense + * + * REQUEST_SENSE and INQUIRY don't affect the sense data on UFI + * devices, so we ignore the information for those commands. Note + * that this means we could be ignoring a real error on these + * commands, but that can't be helped. + */ + if (us->subclass == US_SC_UFI) { + if (srb->cmnd[0] == REQUEST_SENSE || + srb->cmnd[0] == INQUIRY) + return USB_STOR_TRANSPORT_GOOD; + else + if (((unsigned char*)us->irq_urb->transfer_buffer)[0]) + return USB_STOR_TRANSPORT_FAILED; + else + return USB_STOR_TRANSPORT_GOOD; + } + + /* If not UFI, we interpret the data as a result code + * The first byte should always be a 0x0 + * The second byte & 0x0F should be 0x0 for good, otherwise error + */ + if (((unsigned char*)us->irq_urb->transfer_buffer)[0]) { + US_DEBUGP("CBI IRQ data showed reserved bType\n"); + return USB_STOR_TRANSPORT_ERROR; + } + switch (((unsigned char*)us->irq_urb->transfer_buffer)[1] & 0x0F) { + case 0x00: + return USB_STOR_TRANSPORT_GOOD; + case 0x01: + return USB_STOR_TRANSPORT_FAILED; + default: + return USB_STOR_TRANSPORT_ERROR; + } + + /* we should never get here, but if we do, we're in trouble */ + return USB_STOR_TRANSPORT_ERROR; +} + +/* + * Control/Bulk transport + */ +int usb_stor_CB_transport(Scsi_Cmnd *srb, struct us_data *us) +{ + int result; + + /* COMMAND STAGE */ + /* let's send the command via the control pipe */ + result = usb_stor_control_msg(us, usb_sndctrlpipe(us->pusb_dev,0), + US_CBI_ADSC, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, + us->ifnum, srb->cmnd, srb->cmd_len); + + /* check the return code for the command */ + US_DEBUGP("Call to usb_stor_control_msg() returned %d\n", result); + if (result < 0) { + /* if the command was aborted, indicate that */ + if (result == -ENOENT) + return USB_STOR_TRANSPORT_ABORTED; + + /* a stall is a fatal condition from the device */ + if (result == -EPIPE) { + US_DEBUGP("-- Stall on control pipe. Clearing\n"); + result = usb_clear_halt(us->pusb_dev, + usb_sndctrlpipe(us->pusb_dev, + 0)); + US_DEBUGP("-- usb_clear_halt() returns %d\n", result); + return USB_STOR_TRANSPORT_FAILED; + } + + /* Uh oh... serious problem here */ + return USB_STOR_TRANSPORT_ERROR; + } + + /* DATA STAGE */ + /* transfer the data payload for this command, if one exists*/ + if (us_transfer_length(srb, us)) { + us_transfer(srb, us, US_DIRECTION(srb->cmnd[0])); + US_DEBUGP("CB data stage result is 0x%x\n", srb->result); + + /* if it was aborted, we need to indicate that */ + if (srb->result == USB_STOR_TRANSPORT_ABORTED) + return USB_STOR_TRANSPORT_ABORTED; + } + + /* STATUS STAGE */ + /* NOTE: CB does not have a status stage. Silly, I know. So + * we have to catch this at a higher level. + */ + return USB_STOR_TRANSPORT_GOOD; +} + +/* + * Bulk only transport + */ + +/* Determine what the maximum LUN supported is */ +int usb_stor_Bulk_max_lun(struct us_data *us) +{ + unsigned char data; + int result; + int pipe; + + /* issue the command */ + pipe = usb_rcvctrlpipe(us->pusb_dev, 0); + result = usb_control_msg(us->pusb_dev, pipe, + US_BULK_GET_MAX_LUN, + USB_DIR_IN | USB_TYPE_CLASS | + USB_RECIP_INTERFACE, + 0, us->ifnum, &data, sizeof(data), HZ); + + US_DEBUGP("GetMaxLUN command result is %d, data is %d\n", + result, data); + + /* if we have a successful request, return the result */ + if (result == 1) + return data; + + /* if we get a STALL, clear the stall */ + if (result == -EPIPE) { + US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe); + usb_clear_halt(us->pusb_dev, pipe); + } + + /* return the default -- no LUNs */ + return 0; +} + +int usb_stor_Bulk_transport(Scsi_Cmnd *srb, struct us_data *us) +{ + struct bulk_cb_wrap bcb; + struct bulk_cs_wrap bcs; + int result; + int pipe; + int partial; + + /* set up the command wrapper */ + bcb.Signature = cpu_to_le32(US_BULK_CB_SIGN); + bcb.DataTransferLength = cpu_to_le32(us_transfer_length(srb, us)); + bcb.Flags = US_DIRECTION(srb->cmnd[0]) << 7; + bcb.Tag = srb->serial_number; + bcb.Lun = srb->cmnd[1] >> 5; + bcb.Length = srb->cmd_len; + + /* construct the pipe handle */ + pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out); + + /* copy the command payload */ + memset(bcb.CDB, 0, sizeof(bcb.CDB)); + memcpy(bcb.CDB, srb->cmnd, bcb.Length); + + /* send it to out endpoint */ + US_DEBUGP("Bulk command S 0x%x T 0x%x LUN %d L %d F %d CL %d\n", + le32_to_cpu(bcb.Signature), bcb.Tag, bcb.Lun, + bcb.DataTransferLength, bcb.Flags, bcb.Length); + result = usb_stor_bulk_msg(us, &bcb, pipe, US_BULK_CB_WRAP_LEN, + &partial); + US_DEBUGP("Bulk command transfer result=%d\n", result); + + /* if the command was aborted, indicate that */ + if (result == -ENOENT) + return USB_STOR_TRANSPORT_ABORTED; + + /* if we stall, we need to clear it before we go on */ + if (result == -EPIPE) { + US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe); + usb_clear_halt(us->pusb_dev, pipe); + } else if (result) { + /* unknown error -- we've got a problem */ + return USB_STOR_TRANSPORT_ERROR; + } + + /* if the command transfered well, then we go to the data stage */ + if (result == 0) { + /* send/receive data payload, if there is any */ + if (bcb.DataTransferLength) { + us_transfer(srb, us, bcb.Flags); + US_DEBUGP("Bulk data transfer result 0x%x\n", + srb->result); + + /* if it was aborted, we need to indicate that */ + if (srb->result == USB_STOR_TRANSPORT_ABORTED) + return USB_STOR_TRANSPORT_ABORTED; + } + } + + /* See flow chart on pg 15 of the Bulk Only Transport spec for + * an explanation of how this code works. + */ + + /* construct the pipe handle */ + pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in); + + /* get CSW for device status */ + US_DEBUGP("Attempting to get CSW...\n"); + result = usb_stor_bulk_msg(us, &bcs, pipe, US_BULK_CS_WRAP_LEN, + &partial); + + /* if the command was aborted, indicate that */ + if (result == -ENOENT) + return USB_STOR_TRANSPORT_ABORTED; + + /* did the attempt to read the CSW fail? */ + if (result == -EPIPE) { + US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe); + usb_clear_halt(us->pusb_dev, pipe); + + /* get the status again */ + US_DEBUGP("Attempting to get CSW (2nd try)...\n"); + result = usb_stor_bulk_msg(us, &bcs, pipe, + US_BULK_CS_WRAP_LEN, &partial); + + /* if the command was aborted, indicate that */ + if (result == -ENOENT) + return USB_STOR_TRANSPORT_ABORTED; + + /* if it fails again, we need a reset and return an error*/ + if (result == -EPIPE) { + US_DEBUGP("clearing halt for pipe 0x%x\n", pipe); + usb_clear_halt(us->pusb_dev, pipe); + return USB_STOR_TRANSPORT_ERROR; + } + } + + /* if we still have a failure at this point, we're in trouble */ + US_DEBUGP("Bulk status result = %d\n", result); + if (result) { + return USB_STOR_TRANSPORT_ERROR; + } + + /* check bulk status */ + US_DEBUGP("Bulk status S 0x%x T 0x%x R %d V 0x%x\n", + le32_to_cpu(bcs.Signature), bcs.Tag, + bcs.Residue, bcs.Status); + if (bcs.Signature != cpu_to_le32(US_BULK_CS_SIGN) || + bcs.Tag != bcb.Tag || + bcs.Status > US_BULK_STAT_PHASE || partial != 13) { + US_DEBUGP("Bulk logical error\n"); + return USB_STOR_TRANSPORT_ERROR; + } + + /* based on the status code, we report good or bad */ + switch (bcs.Status) { + case US_BULK_STAT_OK: + /* command good -- note that we could be short on data */ + return USB_STOR_TRANSPORT_GOOD; + + case US_BULK_STAT_FAIL: + /* command failed */ + return USB_STOR_TRANSPORT_FAILED; + + case US_BULK_STAT_PHASE: + /* phase error */ + return USB_STOR_TRANSPORT_ERROR; + } + + /* we should never get here, but if we do, we're in trouble */ + return USB_STOR_TRANSPORT_ERROR; +} + +/*********************************************************************** + * Reset routines + ***********************************************************************/ + +/* This issues a CB[I] Reset to the device in question + */ +int usb_stor_CB_reset(struct us_data *us) +{ + unsigned char cmd[12]; + int result; + + US_DEBUGP("CB_reset() called\n"); + + memset(cmd, 0xFF, sizeof(cmd)); + cmd[0] = SEND_DIAGNOSTIC; + cmd[1] = 4; + result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), + US_CBI_ADSC, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0, us->ifnum, cmd, sizeof(cmd), HZ*5); + + /* long wait for reset */ + schedule_timeout(HZ*6); + + US_DEBUGP("CB_reset: clearing endpoint halt\n"); + usb_clear_halt(us->pusb_dev, + usb_rcvbulkpipe(us->pusb_dev, us->ep_in)); + usb_clear_halt(us->pusb_dev, + usb_rcvbulkpipe(us->pusb_dev, us->ep_out)); + + US_DEBUGP("CB_reset done\n"); + return 0; +} + +/* FIXME: Does this work? */ +int usb_stor_Bulk_reset(struct us_data *us) +{ + int result; + + result = usb_control_msg(us->pusb_dev, + usb_sndctrlpipe(us->pusb_dev,0), + US_BULK_RESET_REQUEST, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0, us->ifnum, NULL, 0, HZ*5); + + if (result < 0) + US_DEBUGP("Bulk hard reset failed %d\n", result); + + usb_clear_halt(us->pusb_dev, + usb_rcvbulkpipe(us->pusb_dev, us->ep_in)); + usb_clear_halt(us->pusb_dev, + usb_sndbulkpipe(us->pusb_dev, us->ep_out)); + + /* long wait for reset */ + schedule_timeout(HZ*6); + + return result; +} + diff --git a/drivers/usb/storage/transport.h b/drivers/usb/storage/transport.h new file mode 100644 index 000000000..fb8699781 --- /dev/null +++ b/drivers/usb/storage/transport.h @@ -0,0 +1,132 @@ +/* Driver for USB Mass Storage compliant devices + * Transport Functions Header File + * + * $Id: transport.h,v 1.1 2000/06/27 01:25:28 mdharm Exp $ + * + * Current development and maintainance by: + * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) + * + * This driver is based on the 'USB Mass Storage Class' document. This + * describes in detail the protocol used to communicate with such + * devices. Clearly, the designers had SCSI and ATAPI commands in + * mind when they created this document. The commands are all very + * similar to commands in the SCSI-II and ATAPI specifications. + * + * It is important to note that in a number of cases this class + * exhibits class-specific exemptions from the USB specification. + * Notably the usage of NAK, STALL and ACK differs from the norm, in + * that they are used to communicate wait, failed and OK on commands. + * + * Also, for certain devices, the interrupt endpoint is used to convey + * status of a command. + * + * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more + * information about this driver. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _TRANSPORT_H_ +#define _TRANSPORT_H_ + +#include <linux/blk.h> +#include "usb.h" +#include "scsi.h" + +/* bit set if input */ +extern unsigned char us_direction[256/8]; +#define US_DIRECTION(x) ((us_direction[x>>3] >> (x & 7)) & 1) + +/* Protocols */ + +#define US_PR_CBI 0x00 /* Control/Bulk/Interrupt */ +#define US_PR_CB 0x01 /* Control/Bulk w/o interrupt */ +#define US_PR_BULK 0x50 /* bulk only */ + +/* + * Bulk only data structures + */ + +/* command block wrapper */ +struct bulk_cb_wrap { + __u32 Signature; /* contains 'USBC' */ + __u32 Tag; /* unique per command id */ + __u32 DataTransferLength; /* size of data */ + __u8 Flags; /* direction in bit 0 */ + __u8 Lun; /* LUN normally 0 */ + __u8 Length; /* of of the CDB */ + __u8 CDB[16]; /* max command */ +}; + +#define US_BULK_CB_WRAP_LEN 31 +#define US_BULK_CB_SIGN 0x43425355 /*spells out USBC */ +#define US_BULK_FLAG_IN 1 +#define US_BULK_FLAG_OUT 0 + +/* command status wrapper */ +struct bulk_cs_wrap { + __u32 Signature; /* should = 'USBS' */ + __u32 Tag; /* same as original command */ + __u32 Residue; /* amount not transferred */ + __u8 Status; /* see below */ + __u8 Filler[18]; +}; + +#define US_BULK_CS_WRAP_LEN 13 +#define US_BULK_CS_SIGN 0x53425355 /* spells out 'USBS' */ +#define US_BULK_STAT_OK 0 +#define US_BULK_STAT_FAIL 1 +#define US_BULK_STAT_PHASE 2 + +/* bulk-only class specific requests */ +#define US_BULK_RESET_REQUEST 0xff +#define US_BULK_GET_MAX_LUN 0xfe + +/* + * us_bulk_transfer() return codes + */ +#define US_BULK_TRANSFER_GOOD 0 /* good transfer */ +#define US_BULK_TRANSFER_SHORT 1 /* transfered less than expected */ +#define US_BULK_TRANSFER_FAILED 2 /* transfer died in the middle */ +#define US_BULK_TRANSFER_ABORTED 3 /* transfer canceled */ + +/* + * Transport return codes + */ + +#define USB_STOR_TRANSPORT_GOOD 0 /* Transport good, command good */ +#define USB_STOR_TRANSPORT_FAILED 1 /* Transport good, command failed */ +#define USB_STOR_TRANSPORT_ERROR 2 /* Transport bad (i.e. device dead) */ +#define USB_STOR_TRANSPORT_ABORTED 3 /* Transport aborted */ + +/* + * CBI accept device specific command + */ + +#define US_CBI_ADSC 0 + +extern void usb_stor_CBI_irq(struct urb*); +extern int usb_stor_CBI_transport(Scsi_Cmnd*, struct us_data*); + +extern int usb_stor_CB_transport(Scsi_Cmnd*, struct us_data*); +extern int usb_stor_CB_reset(struct us_data*); + +extern int usb_stor_Bulk_transport(Scsi_Cmnd*, struct us_data*); +extern int usb_stor_Bulk_max_lun(struct us_data*); +extern int usb_stor_Bulk_reset(struct us_data*); + +void usb_stor_invoke_transport(Scsi_Cmnd *, struct us_data *); + +#endif diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c new file mode 100644 index 000000000..d493bc695 --- /dev/null +++ b/drivers/usb/storage/usb.c @@ -0,0 +1,803 @@ +/* Driver for USB Mass Storage compliant devices + * + * $Id: usb.c,v 1.3 2000/06/27 10:20:39 mdharm Exp $ + * + * Current development and maintainance by: + * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) + * + * Developed with the assistance of: + * (c) 2000 David L. Brown, Jr. (usb-storage@davidb.org) + * + * Initial work by: + * (c) 1999 Michael Gee (michael@linuxspecific.com) + * + * This driver is based on the 'USB Mass Storage Class' document. This + * describes in detail the protocol used to communicate with such + * devices. Clearly, the designers had SCSI and ATAPI commands in + * mind when they created this document. The commands are all very + * similar to commands in the SCSI-II and ATAPI specifications. + * + * It is important to note that in a number of cases this class + * exhibits class-specific exemptions from the USB specification. + * Notably the usage of NAK, STALL and ACK differs from the norm, in + * that they are used to communicate wait, failed and OK on commands. + * + * Also, for certain devices, the interrupt endpoint is used to convey + * status of a command. + * + * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more + * information about this driver. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "usb.h" +#include "scsiglue.h" +#include "transport.h" +#include "protocol.h" +#include "debug.h" + +#include <linux/module.h> + /*FIXME: note that this next include is needed for the new sleeping system + * which is not implemented yet + */ +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/malloc.h> + +/* + * Per device data + */ + +static int my_host_number; + +/* + * kernel thread actions + */ + +#define US_ACT_COMMAND 1 +#define US_ACT_DEVICE_RESET 2 +#define US_ACT_BUS_RESET 3 +#define US_ACT_HOST_RESET 4 +#define US_ACT_EXIT 5 + +/* The list of structures and the protective lock for them */ +struct us_data *us_list; +struct semaphore us_list_semaphore; + +static void * storage_probe(struct usb_device *dev, unsigned int ifnum); +static void storage_disconnect(struct usb_device *dev, void *ptr); +static struct usb_driver storage_driver = { + name: "usb-storage", + probe: storage_probe, + disconnect: storage_disconnect, +}; + +static int usb_stor_control_thread(void * __us) +{ + struct us_data *us = (struct us_data *)__us; + int action; + + lock_kernel(); + + /* + * This thread doesn't need any user-level access, + * so get rid of all our resources.. + */ + daemonize(); + + /* set our name for identification purposes */ + sprintf(current->comm, "usb-storage-%d", us->host_number); + + unlock_kernel(); + + /* signal that we've started the thread */ + up(&(us->notify)); + + for(;;) { + US_DEBUGP("*** thread sleeping.\n"); + down(&(us->sleeper)); + down(&(us->queue_exclusion)); + US_DEBUGP("*** thread awakened.\n"); + + /* take the command off the queue */ + action = us->action; + us->action = 0; + us->srb = us->queue_srb; + + /* release the queue lock as fast as possible */ + up(&(us->queue_exclusion)); + + switch (action) { + case US_ACT_COMMAND: + /* reject if target != 0 or if LUN is higher than + * the maximum known LUN + */ + if (us->srb->target || (us->srb->lun > us->max_lun)) { + US_DEBUGP("Bad device number (%d/%d)\n", + us->srb->target, us->srb->lun); + + us->srb->result = DID_BAD_TARGET << 16; + + us->srb->scsi_done(us->srb); + us->srb = NULL; + break; + } + + /* handle those devices which can't do a START_STOP */ + if ((us->srb->cmnd[0] == START_STOP) && + (us->flags & US_FL_START_STOP)) { + us->srb->result = GOOD; + us->srb->scsi_done(us->srb); + us->srb = NULL; + break; + } + + /* lock the device pointers */ + down(&(us->dev_semaphore)); + + /* our device has gone - pretend not ready */ + if (!us->pusb_dev) { + US_DEBUGP("Request is for removed device\n"); + /* For REQUEST_SENSE, it's the data. But + * for anything else, it should look like + * we auto-sensed for it. + */ + if (us->srb->cmnd[0] == REQUEST_SENSE) { + memcpy(us->srb->request_buffer, + usb_stor_sense_notready, + sizeof(usb_stor_sense_notready)); + us->srb->result = GOOD; + } else { + memcpy(us->srb->sense_buffer, + usb_stor_sense_notready, + sizeof(usb_stor_sense_notready)); + us->srb->result = CHECK_CONDITION; + } + } else { /* !us->pusb_dev */ + /* we've got a command, let's do it! */ + US_DEBUG(usb_stor_show_command(us->srb)); + us->proto_handler(us->srb, us); + } + + /* unlock the device pointers */ + up(&(us->dev_semaphore)); + + /* indicate that the command is done */ + if (us->srb->result != DID_ABORT << 16) { + US_DEBUGP("scsi cmd done, result=0x%x\n", + us->srb->result); + us->srb->scsi_done(us->srb); + } else { + US_DEBUGP("scsi command aborted\n"); + up(&(us->notify)); + } + us->srb = NULL; + break; + + case US_ACT_DEVICE_RESET: + break; + + case US_ACT_BUS_RESET: + break; + + case US_ACT_HOST_RESET: + break; + + } /* end switch on action */ + + /* exit if we get a signal to exit */ + if (action == US_ACT_EXIT) { + US_DEBUGP("-- US_ACT_EXIT command recieved\n"); + break; + } + } /* for (;;) */ + + /* notify the exit routine that we're actually exiting now */ + up(&(us->notify)); + + return 0; +} + +/* This is the list of devices we recognize, along with their flag data */ +static struct us_unusual_dev us_unusual_dev_list[] = { + { 0x03f0, 0x0107, 0x0200, 0x0200, "HP USB CD-Writer Plus", + US_SC_8070, US_PR_CB, 0}, + { 0x04e6, 0x0001, 0x0200, 0x0200, "Matshita LS-120", + US_SC_8020, US_PR_CB, US_FL_SINGLE_LUN}, + { 0x04e6, 0x0002, 0x0100, 0x0100, "Shuttle eUSCSI Bridge", + US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH}, + { 0x04e6, 0x0006, 0x0100, 0x0100, "Shuttle eUSB MMC Adapter", + US_SC_SCSI, US_PR_CB, US_FL_SINGLE_LUN}, + { 0x054c, 0x0010, 0x0210, 0x0210, "Sony DSC-S30/S70", + US_SC_SCSI, US_PR_CB, US_FL_SINGLE_LUN | US_FL_START_STOP | + US_FL_MODE_XLATE | US_FL_ALT_LENGTH | US_FL_ALT_LENGTH}, + { 0x054c, 0x002d, 0x0100, 0x0100, "Sony Memorystick MSAC-US1", + US_SC_SCSI, US_PR_CB, US_FL_SINGLE_LUN | US_FL_START_STOP | + US_FL_MODE_XLATE | US_FL_ALT_LENGTH}, + { 0x057b, 0x0000, 0x0000, 0x0299, "Y-E Data Flashbuster-U", + US_SC_UFI, US_PR_CB, US_FL_SINGLE_LUN}, + { 0x057b, 0x0000, 0x0300, 0x9999, "Y-E Data Flashbuster-U", + US_SC_UFI, US_PR_CBI, US_FL_SINGLE_LUN}, + { 0x0693, 0x0002, 0x0100, 0x0100, "Hagiwara FlashGate SmartMedia", + US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH}, + { 0x0781, 0x0001, 0x0200, 0x0200, "Sandisk ImageMate (SDDR-01)", + US_SC_SCSI, US_PR_CB, US_FL_SINGLE_LUN | US_FL_START_STOP}, + { 0x0781, 0x0002, 0x0009, 0x0009, "Sandisk Imagemate (SDDR-31)", + US_SC_SCSI, US_PR_BULK, US_FL_IGNORE_SER}, + { 0x07af, 0x0004, 0x0100, 0x0100, "Microtech USB-SCSI-DB25", + US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH}, + { 0x07af, 0x0005, 0x0100, 0x0100, "Microtech USB-SCSI-HD50", + US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH}, + { 0x05ab, 0x0031, 0x0100, 0x0100, "In-System USB/IDE Bridge", + US_SC_8070, US_PR_BULK, US_FL_ALT_LENGTH}, + { 0x0693, 0x0005, 0x0100, 0x0100, "Hagiwara Flashgate", + US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH}, + { 0 }}; + +/* Search our ususual device list, based on vendor/product combinations + * to see if we can support this device. Returns a pointer to a structure + * defining how we should support this device, or NULL if it's not in the + * list + */ +static struct us_unusual_dev* us_find_dev(u16 idVendor, u16 idProduct, + u16 bcdDevice) +{ + struct us_unusual_dev* ptr; + + US_DEBUGP("Searching unusual device list for (0x%x, 0x%x, 0x%x)...\n", + idVendor, idProduct, bcdDevice); + + ptr = us_unusual_dev_list; + while ((ptr->idVendor != 0x0000) && + !((ptr->idVendor == idVendor) && + (ptr->idProduct == idProduct) && + (ptr->bcdDeviceMin <= bcdDevice) && + (ptr->bcdDeviceMax >= bcdDevice))) + ptr++; + + /* if the search ended because we hit the end record, we failed */ + if (ptr->idVendor == 0x0000) { + US_DEBUGP("-- did not find a matching device\n"); + return NULL; + } + + /* otherwise, we found one! */ + US_DEBUGP("-- found matching device: %s\n", ptr->name); + return ptr; +} + +/* Set up the IRQ pipe and handler + * Note that this function assumes that all the data in the us_data + * strucuture is current. This includes the ep_int field, which gives us + * the endpoint for the interrupt. + * Returns non-zero on failure, zero on success + */ +static int usb_stor_allocate_irq(struct us_data *ss) +{ + unsigned int pipe; + int maxp; + int result; + + US_DEBUGP("Allocating IRQ for CBI transport\n"); + + /* lock access to the data structure */ + down(&(ss->irq_urb_sem)); + + /* allocate the URB */ + ss->irq_urb = usb_alloc_urb(0); + if (!ss->irq_urb) { + up(&(ss->irq_urb_sem)); + US_DEBUGP("couldn't allocate interrupt URB"); + return 1; + } + + /* calculate the pipe and max packet size */ + pipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int->bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK); + maxp = usb_maxpacket(ss->pusb_dev, pipe, usb_pipeout(pipe)); + if (maxp > sizeof(ss->irqbuf)) + maxp = sizeof(ss->irqbuf); + + /* fill in the URB with our data */ + FILL_INT_URB(ss->irq_urb, ss->pusb_dev, pipe, ss->irqbuf, maxp, + usb_stor_CBI_irq, ss, ss->ep_int->bInterval); + + /* submit the URB for processing */ + result = usb_submit_urb(ss->irq_urb); + US_DEBUGP("usb_submit_urb() returns %d\n", result); + if (result) { + usb_free_urb(ss->irq_urb); + up(&(ss->irq_urb_sem)); + return 2; + } + + /* unlock the data structure and return success */ + up(&(ss->irq_urb_sem)); + return 0; +} + +/* Probe to see if a new device is actually a SCSI device */ +static void * storage_probe(struct usb_device *dev, unsigned int ifnum) +{ + int i; + char mf[USB_STOR_STRING_LEN]; /* manufacturer */ + char prod[USB_STOR_STRING_LEN]; /* product */ + char serial[USB_STOR_STRING_LEN]; /* serial number */ + GUID(guid); /* Global Unique Identifier */ + unsigned int flags; + struct us_unusual_dev *unusual_dev; + struct us_data *ss = NULL; + int result; + + /* these are temporary copies -- we test on these, then put them + * in the us-data structure + */ + struct usb_endpoint_descriptor *ep_in = NULL; + struct usb_endpoint_descriptor *ep_out = NULL; + struct usb_endpoint_descriptor *ep_int = NULL; + u8 subclass = 0; + u8 protocol = 0; + + /* the altsettting 0 on the interface we're probing */ + struct usb_interface_descriptor *altsetting = + &(dev->actconfig->interface[ifnum].altsetting[0]); + + /* clear the temporary strings */ + memset(mf, 0, sizeof(mf)); + memset(prod, 0, sizeof(prod)); + memset(serial, 0, sizeof(serial)); + + /* search for this device in our unusual device list */ + unusual_dev = us_find_dev(dev->descriptor.idVendor, + dev->descriptor.idProduct, + dev->descriptor.bcdDevice); + + /* + * Can we support this device, either because we know about it + * from our unusual device list, or because it advertises that it's + * compliant to the specification? + */ + if (!unusual_dev && + !(dev->descriptor.bDeviceClass == 0 && + altsetting->bInterfaceClass == USB_CLASS_MASS_STORAGE && + altsetting->bInterfaceSubClass >= US_SC_MIN && + altsetting->bInterfaceSubClass <= US_SC_MAX)) { + /* if it's not a mass storage, we go no further */ + return NULL; + } + + /* At this point, we know we've got a live one */ + US_DEBUGP("USB Mass Storage device detected\n"); + + /* Determine subclass and protocol, or copy from the interface */ + if (unusual_dev) { + subclass = unusual_dev->useProtocol; + protocol = unusual_dev->useTransport; + flags = unusual_dev->flags; + } else { + subclass = altsetting->bInterfaceSubClass; + protocol = altsetting->bInterfaceProtocol; + flags = 0; + } + + /* + * Find the endpoints we need + * We are expecting a minimum of 2 endpoints - in and out (bulk). + * An optional interrupt is OK (necessary for CBI protocol). + * We will ignore any others. + */ + for (i = 0; i < altsetting->bNumEndpoints; i++) { + /* is it an BULK endpoint? */ + if ((altsetting->endpoint[i].bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) { + /* BULK in or out? */ + if (altsetting->endpoint[i].bEndpointAddress & + USB_DIR_IN) + ep_in = &altsetting->endpoint[i]; + else + ep_out = &altsetting->endpoint[i]; + } + + /* is it an interrupt endpoint? */ + if ((altsetting->endpoint[i].bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) { + ep_int = &altsetting->endpoint[i]; + } + } + US_DEBUGP("Endpoints: In: 0x%p Out: 0x%p Int: 0x%p (Period %d)\n", + ep_in, ep_out, ep_int, ep_int ? ep_int->bInterval : 0); + + /* set the interface -- STALL is an acceptable response here */ + result = usb_set_interface(dev, altsetting->bInterfaceNumber, 0); + US_DEBUGP("Result from usb_set_interface is %d\n", result); + if (result == -EPIPE) { + US_DEBUGP("-- clearing stall on control interface\n"); + usb_clear_halt(dev, usb_sndctrlpipe(dev, 0)); + } else if (result != 0) { + /* it's not a stall, but another error -- time to bail */ + US_DEBUGP("-- Unknown error. Rejecting device\n"); + return NULL; + } + + /* Do some basic sanity checks, and bail if we find a problem */ + if (!ep_in || !ep_out || (protocol == US_PR_CBI && !ep_int)) { + US_DEBUGP("Sanity check failed. Rejecting device.\n"); + return NULL; + } + + /* At this point, we're committed to using the device */ + + /* clear the GUID and fetch the strings */ + GUID_CLEAR(guid); + if (dev->descriptor.iManufacturer) + usb_string(dev, dev->descriptor.iManufacturer, + mf, sizeof(mf)); + if (dev->descriptor.iProduct) + usb_string(dev, dev->descriptor.iProduct, + prod, sizeof(prod)); + if (dev->descriptor.iSerialNumber && !(flags & US_FL_IGNORE_SER)) + usb_string(dev, dev->descriptor.iSerialNumber, + serial, sizeof(serial)); + + /* Create a GUID for this device */ + if (dev->descriptor.iSerialNumber && serial[0]) { + /* If we have a serial number, and it's a non-NULL string */ + make_guid(guid, dev->descriptor.idVendor, + dev->descriptor.idProduct, serial); + } else { + /* We don't have a serial number, so we use 0 */ + make_guid(guid, dev->descriptor.idVendor, + dev->descriptor.idProduct, "0"); + } + + /* lock access to the data structures */ + down(&us_list_semaphore); + + /* + * Now check if we have seen this GUID before + * We're looking for a device with a matching GUID that isn't + * allready on the system + */ + ss = us_list; + while ((ss != NULL) && + ((ss->pusb_dev) || !GUID_EQUAL(guid, ss->guid))) + ss = ss->next; + + if (ss != NULL) { + /* Existing device -- re-connect */ + US_DEBUGP("Found existing GUID " GUID_FORMAT "\n", + GUID_ARGS(guid)); + + /* establish the connection to the new device upon reconnect */ + ss->ifnum = ifnum; + ss->pusb_dev = dev; + + /* copy over the endpoint data */ + if (ep_in) + ss->ep_in = ep_in->bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + if (ep_out) + ss->ep_out = ep_out->bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + ss->ep_int = ep_int; + + /* allocate an IRQ callback if one is needed */ + if ((ss->protocol == US_PR_CBI) && usb_stor_allocate_irq(ss)) + return NULL; + } else { + /* New device -- allocate memory and initialize */ + US_DEBUGP("New GUID " GUID_FORMAT "\n", GUID_ARGS(guid)); + + if ((ss = (struct us_data *)kmalloc(sizeof(struct us_data), + GFP_KERNEL)) == NULL) { + printk(KERN_WARNING USB_STORAGE "Out of memory\n"); + up(&us_list_semaphore); + return NULL; + } + memset(ss, 0, sizeof(struct us_data)); + + /* allocate the URB we're going to use */ + ss->current_urb = usb_alloc_urb(0); + if (!ss->current_urb) { + kfree(ss); + return NULL; + } + + /* Initialize the mutexes only when the struct is new */ + init_MUTEX_LOCKED(&(ss->sleeper)); + init_MUTEX_LOCKED(&(ss->notify)); + init_MUTEX_LOCKED(&(ss->ip_waitq)); + init_MUTEX(&(ss->queue_exclusion)); + init_MUTEX(&(ss->irq_urb_sem)); + init_MUTEX(&(ss->current_urb_sem)); + init_MUTEX(&(ss->dev_semaphore)); + + /* copy over the subclass and protocol data */ + ss->subclass = subclass; + ss->protocol = protocol; + ss->flags = flags; + + /* copy over the endpoint data */ + if (ep_in) + ss->ep_in = ep_in->bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + if (ep_out) + ss->ep_out = ep_out->bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + ss->ep_int = ep_int; + + /* establish the connection to the new device */ + ss->ifnum = ifnum; + ss->pusb_dev = dev; + + /* copy over the identifiying strings */ + strncpy(ss->vendor, mf, USB_STOR_STRING_LEN); + strncpy(ss->product, prod, USB_STOR_STRING_LEN); + strncpy(ss->serial, serial, USB_STOR_STRING_LEN); + if (strlen(ss->vendor) == 0) + strncpy(ss->vendor, "Unknown", USB_STOR_STRING_LEN); + if (strlen(ss->product) == 0) + strncpy(ss->product, "Unknown", USB_STOR_STRING_LEN); + if (strlen(ss->serial) == 0) + strncpy(ss->serial, "None", USB_STOR_STRING_LEN); + + /* copy the GUID we created before */ + memcpy(ss->guid, guid, sizeof(guid)); + + /* + * Set the handler pointers based on the protocol + * Again, this data is persistant across reattachments + */ + switch (ss->protocol) { + case US_PR_CB: + ss->transport_name = "Control/Bulk"; + ss->transport = usb_stor_CB_transport; + ss->transport_reset = usb_stor_CB_reset; + ss->max_lun = 7; + break; + + case US_PR_CBI: + ss->transport_name = "Control/Bulk/Interrupt"; + ss->transport = usb_stor_CBI_transport; + ss->transport_reset = usb_stor_CB_reset; + ss->max_lun = 7; + break; + + case US_PR_BULK: + ss->transport_name = "Bulk"; + ss->transport = usb_stor_Bulk_transport; + ss->transport_reset = usb_stor_Bulk_reset; + ss->max_lun = usb_stor_Bulk_max_lun(ss); + break; + + default: + ss->transport_name = "Unknown"; + up(&us_list_semaphore); + kfree(ss->current_urb); + kfree(ss); + return NULL; + break; + } + US_DEBUGP("Transport: %s\n", ss->transport_name); + + /* fix for single-lun devices */ + if (ss->flags & US_FL_SINGLE_LUN) + ss->max_lun = 0; + + switch (ss->subclass) { + case US_SC_RBC: + ss->protocol_name = "Reduced Block Commands (RBC)"; + ss->proto_handler = usb_stor_transparent_scsi_command; + break; + + case US_SC_8020: + ss->protocol_name = "8020i"; + ss->proto_handler = usb_stor_ATAPI_command; + break; + + case US_SC_QIC: + ss->protocol_name = "QIC-157"; + US_DEBUGP("Sorry, device not supported. Please\n"); + US_DEBUGP("contact mdharm-usb@one-eyed-alien.net\n"); + US_DEBUGP("if you see this message.\n"); + up(&us_list_semaphore); + kfree(ss->current_urb); + kfree(ss); + return NULL; + break; + + case US_SC_8070: + ss->protocol_name = "8070i"; + ss->proto_handler = usb_stor_ATAPI_command; + break; + + case US_SC_SCSI: + ss->protocol_name = "Transparent SCSI"; + ss->proto_handler = usb_stor_transparent_scsi_command; + break; + + case US_SC_UFI: + ss->protocol_name = "Uniform Floppy Interface (UFI)"; + ss->proto_handler = usb_stor_ufi_command; + break; + + default: + ss->protocol_name = "Unknown"; + up(&us_list_semaphore); + kfree(ss->current_urb); + kfree(ss); + return NULL; + break; + } + US_DEBUGP("Protocol: %s\n", ss->protocol_name); + + /* allocate an IRQ callback if one is needed */ + if ((ss->protocol == US_PR_CBI) && usb_stor_allocate_irq(ss)) + return NULL; + + /* + * Since this is a new device, we need to generate a scsi + * host definition, and register with the higher SCSI layers + */ + + /* Initialize the host template based on the default one */ + memcpy(&(ss->htmplt), &usb_stor_host_template, + sizeof(usb_stor_host_template)); + + /* Grab the next host number */ + ss->host_number = my_host_number++; + + /* We abuse this pointer so we can pass the ss pointer to + * the host controler thread in us_detect. But how else are + * we to do it? + */ + (struct us_data *)ss->htmplt.proc_dir = ss; + + /* start up our control thread */ + ss->pid = kernel_thread(usb_stor_control_thread, ss, + CLONE_FS | CLONE_FILES | + CLONE_SIGHAND); + if (ss->pid < 0) { + printk(KERN_WARNING USB_STORAGE + "Unable to start control thread\n"); + kfree(ss->current_urb); + kfree(ss); + return NULL; + } + + /* wait for the thread to start */ + down(&(ss->notify)); + + /* now register - our detect function will be called */ + ss->htmplt.module = THIS_MODULE; + scsi_register_module(MODULE_SCSI_HA, &(ss->htmplt)); + + /* put us in the list */ + ss->next = us_list; + us_list = ss; + } + + /* release the data structure lock */ + up(&us_list_semaphore); + + printk(KERN_DEBUG + "WARNING: USB Mass Storage data integrity not assured\n"); + printk(KERN_DEBUG + "USB Mass Storage device found at %d\n", dev->devnum); + + /* return a pointer for the disconnect function */ + return ss; +} + +/* Handle a disconnect event from the USB core */ +static void storage_disconnect(struct usb_device *dev, void *ptr) +{ + struct us_data *ss = ptr; + int result; + + US_DEBUGP("storage_disconnect() called\n"); + + /* this is the odd case -- we disconnected but weren't using it */ + if (!ss) { + US_DEBUGP("-- device was not in use\n"); + return; + } + + /* lock access to the device data structure */ + down(&(ss->dev_semaphore)); + + /* release the IRQ, if we have one */ + down(&(ss->irq_urb_sem)); + if (ss->irq_urb) { + US_DEBUGP("-- releasing irq handle\n"); + result = usb_unlink_urb(ss->irq_urb); + ss->irq_urb = NULL; + US_DEBUGP("-- usb_unlink_urb() returned %d\n", result); + usb_free_urb(ss->irq_urb); + } + up(&(ss->irq_urb_sem)); + + /* mark the device as gone */ + ss->pusb_dev = NULL; + + /* lock access to the device data structure */ + up(&(ss->dev_semaphore)); +} + +/*********************************************************************** + * Initialization and registration + ***********************************************************************/ + +int __init usb_stor_init(void) +{ + /* initialize internal global data elements */ + us_list = NULL; + init_MUTEX(&us_list_semaphore); + my_host_number = 0; + + /* register the driver, return -1 if error */ + if (usb_register(&storage_driver) < 0) + return -1; + + /* we're all set */ + printk(KERN_INFO "USB Mass Storage support registered.\n"); + return 0; +} + +void __exit usb_stor_exit(void) +{ + struct us_data *next; + + US_DEBUGP("usb_stor_exit() called\n"); + + /* Deregister the driver + * This eliminates races with probes and disconnects + */ + US_DEBUGP("-- calling usb_deregister()\n"); + usb_deregister(&storage_driver) ; + + /* lock access to the data structures */ + down(&us_list_semaphore); + + /* While there are still virtual hosts, unregister them + * + * Note that the us_release() routine will destroy the local data + * structure. So we have to peel these off the top of the list + * and keep updating the head pointer as we go. + */ + while (us_list) { + /* keep track of where the next one is */ + next = us_list->next; + + US_DEBUGP("-- calling scsi_unregister_module()\n"); + scsi_unregister_module(MODULE_SCSI_HA, &(us_list->htmplt)); + + /* Now that scsi_unregister_module is done with the host + * template, we can free the us_data structure (the host + * template is inline in this structure). */ + kfree (us_list); + + /* advance the list pointer */ + us_list = next; + } + + /* unlock the data structures */ + up(&us_list_semaphore); +} + +module_init(usb_stor_init) ; +module_exit(usb_stor_exit) ; diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h new file mode 100644 index 000000000..f18b6c868 --- /dev/null +++ b/drivers/usb/storage/usb.h @@ -0,0 +1,182 @@ +/* Driver for USB Mass Storage compliant devices + * Main Header File + * + * $Id: usb.h,v 1.1 2000/06/27 01:25:28 mdharm Exp $ + * + * Current development and maintainance by: + * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) + * + * Initial work by: + * (c) 1999 Michael Gee (michael@linuxspecific.com) + * + * This driver is based on the 'USB Mass Storage Class' document. This + * describes in detail the protocol used to communicate with such + * devices. Clearly, the designers had SCSI and ATAPI commands in + * mind when they created this document. The commands are all very + * similar to commands in the SCSI-II and ATAPI specifications. + * + * It is important to note that in a number of cases this class + * exhibits class-specific exemptions from the USB specification. + * Notably the usage of NAK, STALL and ACK differs from the norm, in + * that they are used to communicate wait, failed and OK on commands. + * + * Also, for certain devices, the interrupt endpoint is used to convey + * status of a command. + * + * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more + * information about this driver. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _USB_H_ +#define _USB_H_ + +#include <linux/usb.h> +#include <linux/blk.h> +#include <linux/smp_lock.h> +#include "scsi.h" +#include "hosts.h" + +/* + * GUID definitions + */ + +#define GUID(x) __u32 x[3] +#define GUID_EQUAL(x, y) (x[0] == y[0] && x[1] == y[1] && x[2] == y[2]) +#define GUID_CLEAR(x) x[0] = x[1] = x[2] = 0; +#define GUID_NONE(x) (!x[0] && !x[1] && !x[2]) +#define GUID_FORMAT "%08x%08x%08x" +#define GUID_ARGS(x) x[0], x[1], x[2] + +static inline void make_guid( __u32 *pg, __u16 vendor, __u16 product, char *serial) +{ + pg[0] = (vendor << 16) | product; + pg[1] = pg[2] = 0; + while (*serial) { + pg[1] <<= 4; + pg[1] |= pg[2] >> 28; + pg[2] <<= 4; + if (*serial >= 'a') + *serial -= 'a' - 'A'; + pg[2] |= (*serial <= '9' && *serial >= '0') ? *serial - '0' + : *serial - 'A' + 10; + serial++; + } +} + +/* + * Unusual device list definitions + */ + +struct us_unusual_dev { + /* we search the list based on these parameters */ + __u16 idVendor; + __u16 idProduct; + __u16 bcdDeviceMin; + __u16 bcdDeviceMax; + + /* the list specifies these parameters */ + const char* name; + __u8 useProtocol; + __u8 useTransport; + unsigned int flags; +}; + +/* Flag definitions */ +#define US_FL_SINGLE_LUN 0x00000001 /* allow access to only LUN 0 */ +#define US_FL_MODE_XLATE 0x00000002 /* translate _6 to _10 comands for + Win/MacOS compatibility */ +#define US_FL_START_STOP 0x00000004 /* ignore START_STOP commands */ +#define US_FL_ALT_LENGTH 0x00000008 /* use the alternate algorithm for + us_transfer_length() */ +#define US_FL_IGNORE_SER 0x00000010 /* Ignore the serial number given */ + +#define USB_STOR_STRING_LEN 32 + +struct us_data; + +typedef int (*trans_cmnd)(Scsi_Cmnd*, struct us_data*); +typedef int (*trans_reset)(struct us_data*); +typedef void (*proto_cmnd)(Scsi_Cmnd*, struct us_data*); + +/* we allocate one of these for every device that we remember */ +struct us_data { + struct us_data *next; /* next device */ + + /* the device we're working with */ + struct semaphore dev_semaphore; /* protect pusb_dev */ + struct usb_device *pusb_dev; /* this usb_device */ + + unsigned int flags; /* from filter initially */ + + /* information about the device -- always good */ + char vendor[USB_STOR_STRING_LEN]; + char product[USB_STOR_STRING_LEN]; + char serial[USB_STOR_STRING_LEN]; + char *transport_name; + char *protocol_name; + u8 subclass; + u8 protocol; + u8 max_lun; + + /* information about the device -- only good if device is attached */ + u8 ifnum; /* interface number */ + u8 ep_in; /* bulk in endpoint */ + u8 ep_out; /* bulk out endpoint */ + struct usb_endpoint_descriptor *ep_int; /* interrupt endpoint */ + + /* function pointers for this device */ + trans_cmnd transport; /* transport function */ + trans_reset transport_reset; /* transport device reset */ + proto_cmnd proto_handler; /* protocol handler */ + + /* SCSI interfaces */ + GUID(guid); /* unique dev id */ + struct Scsi_Host *host; /* our dummy host data */ + Scsi_Host_Template htmplt; /* own host template */ + int host_number; /* to find us */ + int host_no; /* allocated by scsi */ + Scsi_Cmnd *srb; /* current srb */ + + /* thread information */ + Scsi_Cmnd *queue_srb; /* the single queue slot */ + int action; /* what to do */ + int pid; /* control thread */ + + /* interrupt info for CBI devices -- only good if attached */ + struct semaphore ip_waitq; /* for CBI interrupts */ + int ip_wanted; /* is an IRQ expected? */ + + /* interrupt communications data */ + struct semaphore irq_urb_sem; /* to protect irq_urb */ + struct urb *irq_urb; /* for USB int requests */ + unsigned char irqbuf[2]; /* buffer for USB IRQ */ + + /* control and bulk communications data */ + struct semaphore current_urb_sem; /* to protect irq_urb */ + struct urb *current_urb; /* non-int USB requests */ + + /* mutual exclusion structures */ + struct semaphore notify; /* thread begin/end */ + struct semaphore sleeper; /* to sleep the thread on */ + struct semaphore queue_exclusion; /* to protect data structs */ +}; + +/* The list of structures and the protective lock for them */ +extern struct us_data *us_list; +extern struct semaphore us_list_semaphore; + +#endif diff --git a/drivers/usb/usb.c b/drivers/usb/usb.c index 08ef79fe4..3fba783e0 100644 --- a/drivers/usb/usb.c +++ b/drivers/usb/usb.c @@ -1683,8 +1683,9 @@ int usb_new_device(struct usb_device *dev) usb_set_maxpacket(dev); /* we set the default configuration here */ - if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) { - err("failed to set default configuration"); + err = usb_set_configuration(dev, dev->config[0].bConfigurationValue); + if (err) { + err("failed to set default configuration (error=%d)", err); return -1; } diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index 864e30f0c..3707b611a 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -292,19 +292,27 @@ fb_read(struct file *file, char *buf, size_t count, loff_t *ppos) struct fb_info *info = registered_fb[fbidx]; struct fb_ops *fb = info->fbops; struct fb_fix_screeninfo fix; - char *base_addr; - ssize_t copy_size; if (! fb || ! info->disp) return -ENODEV; fb->fb_get_fix(&fix,PROC_CONSOLE(info), info); - base_addr=info->disp->screen_base; - copy_size=(count + p <= fix.smem_len ? count : fix.smem_len - p); - if (copy_to_user(buf, base_addr+p, copy_size)) - return -EFAULT; - *ppos += copy_size; - return copy_size; + if (p >= fix.smem_len) + return 0; + if (count >= fix.smem_len) + count = fix.smem_len; + if (count + p > fix.smem_len) + count = fix.smem_len - p; + if (count) { + char *base_addr; + + base_addr = info->disp->screen_base; + count -= copy_to_user(buf, base_addr+p, count); + if (!count) + return -EFAULT; + *ppos += count; + } + return count; } static ssize_t @@ -316,19 +324,32 @@ fb_write(struct file *file, const char *buf, size_t count, loff_t *ppos) struct fb_info *info = registered_fb[fbidx]; struct fb_ops *fb = info->fbops; struct fb_fix_screeninfo fix; - char *base_addr; - ssize_t copy_size; + int err; if (! fb || ! info->disp) return -ENODEV; fb->fb_get_fix(&fix, PROC_CONSOLE(info), info); - base_addr=info->disp->screen_base; - copy_size=(count + p <= fix.smem_len ? count : fix.smem_len - p); - if (copy_from_user(base_addr+p, buf, copy_size)) - return -EFAULT; - file->f_pos += copy_size; - return copy_size; + if (p > fix.smem_len) + return -ENOSPC; + if (count >= fix.smem_len) + count = fix.smem_len; + err = 0; + if (count + p > fix.smem_len) { + count = fix.smem_len - p; + err = -ENOSPC; + } + if (count) { + char *base_addr; + + base_addr = info->disp->screen_base; + count -= copy_from_user(base_addr+p, buf, count); + *ppos += count; + err = -EFAULT; + } + if (count) + return count; + return err; } #ifdef CONFIG_KMOD diff --git a/drivers/video/riva/fbdev.c b/drivers/video/riva/fbdev.c index 91dfca4ab..4c2a13791 100644 --- a/drivers/video/riva/fbdev.c +++ b/drivers/video/riva/fbdev.c @@ -1,7 +1,9 @@ /* * linux/drivers/video/rivafb.c - nVidia RIVA 128/TNT/TNT2 fb driver * - * Copyright 1999 Jeff Garzik <jgarzik@mandrakesoft.com> + * Maintained by Ani Joshi <ajoshi@shell.unixbox.com> + * + * Copyright 1999-2000 Jeff Garzik * * Contributors: * @@ -20,7 +22,7 @@ */ /* version number of this driver */ -#define RIVAFB_VERSION "0.7.1" +#define RIVAFB_VERSION "0.7.2" #include <linux/config.h> #include <linux/module.h> @@ -208,10 +210,10 @@ struct rivafb_info { static struct rivafb_info *riva_boards = NULL; /* command line data, set in rivafb_setup() */ -static char fontname[40] __initdata; +static char fontname[40] __initdata = { 0 }; #ifndef MODULE -static char noaccel __initdata; /* unused */ -static const char *mode_option __initdata; +static char noaccel __initdata = 0; /* unused */ +static const char *mode_option __initdata = NULL; #endif static struct fb_var_screeninfo rivafb_default_var = { @@ -253,14 +255,14 @@ static int riva_setcolreg (unsigned regno, unsigned red, unsigned green, struct fb_info *info); static int riva_get_cmap_len (const struct fb_var_screeninfo *var); -static int riva_pci_register (struct pci_dev *pd, - const struct riva_chip_info *rci); static int riva_set_fbinfo (struct rivafb_info *rinfo); static void riva_save_state (struct rivafb_info *rinfo, struct riva_regs *regs); static void riva_load_state (struct rivafb_info *rinfo, struct riva_regs *regs); static struct rivafb_info *riva_board_list_add (struct rivafb_info *board_list, struct rivafb_info *new_node); +static struct rivafb_info *riva_board_list_del (struct rivafb_info *board_list, + struct rivafb_info *del_node); static void riva_wclut (unsigned char regnum, unsigned char red, unsigned char green, unsigned char blue); @@ -286,7 +288,7 @@ static struct fb_ops riva_fb_ops = { static const struct riva_regs reg_template = { {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* ATTR */ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x41, 0x01, 0x0F, 0x13, 0x00}, + 0x41, 0x01, 0x0F, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* CRT */ 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE3, /* 0x10 */ @@ -378,9 +380,6 @@ static int riva_init_disp_var (struct rivafb_info *rinfo) if (mode_option) fb_find_mode (&rinfo->disp.var, &rinfo->info, mode_option, NULL, 0, NULL, 8); - else - fb_find_mode (&rinfo->disp.var, &rinfo->info, - "640x480-8@60", NULL, 0, NULL, 8); #endif /* !MODULE */ return 0; } @@ -447,13 +446,13 @@ static int __devinit riva_set_fbinfo (struct rivafb_info *rinfo) info->flags = FBINFO_FLAG_DEFAULT; info->fbops = &riva_fb_ops; -#warning FIXME: set monspecs to what??? + /* FIXME: set monspecs to what??? */ - info->display_fg = NULL; /* FIXME: correct? */ + info->display_fg = NULL; strncpy (info->fontname, fontname, sizeof (info->fontname)); info->fontname[sizeof (info->fontname) - 1] = 0; - info->changevar = NULL; /* FIXME: needed? */ + info->changevar = NULL; info->switch_con = rivafb_switch; info->updatevar = rivafb_updatevar; info->blank = rivafb_blank; @@ -472,32 +471,6 @@ static int __devinit riva_set_fbinfo (struct rivafb_info *rinfo) -static void __devinit riva_init_clut (struct rivafb_info *fb_info) -{ - int j, k, red, green, blue; - - for (j = 0; j < 256; j++) { - if (j < 16) { - /* use default fbcon colors for first 16 */ - k = color_table[j]; - red = default_red[k]; - green = default_grn[k]; - blue = default_blu[k]; - } else { - /* grey ramp for rest of colors */ - red = green = blue = j; - } - - riva_wclut (j, red, green, blue); - - fb_info->palette[j].red = red; - fb_info->palette[j].green = green; - fb_info->palette[j].blue = blue; - } -} - - - static int __devinit rivafb_init_one (struct pci_dev *pd, const struct pci_device_id *ent) { @@ -597,7 +570,7 @@ static int __devinit rivafb_init_one (struct pci_dev *pd, riva_set_fbinfo (rinfo); - riva_init_clut (rinfo); + fb_memset (rinfo->fb_base, 0, rinfo->ram_amount); riva_load_video_mode (rinfo, &rinfo->disp.var); @@ -607,6 +580,8 @@ static int __devinit rivafb_init_one (struct pci_dev *pd, goto err_out_iounmap_fb; } + riva_boards = riva_board_list_add(riva_boards, rinfo); + pd->driver_data = rinfo; printk ("PCI Riva NV%d framebuffer ver %s (%s, %dMB @ 0x%lX)\n", @@ -640,6 +615,8 @@ static void __devexit rivafb_remove_one (struct pci_dev *pd) if (!board) return; + riva_boards = riva_board_list_del(riva_boards, board); + riva_load_state (board, &board->initial_state); unregister_framebuffer ((struct fb_info *) board); @@ -1355,6 +1332,7 @@ static int riva_setcolreg (unsigned regno, unsigned red, unsigned green, { struct rivafb_info *rivainfo = (struct rivafb_info *) info; struct display *p; + unsigned shift = 8; DPRINTK ("ENTER\n"); @@ -1370,11 +1348,22 @@ static int riva_setcolreg (unsigned regno, unsigned red, unsigned green, red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8; } + + switch (rivainfo->riva.Architecture) { + case 3: + shift = 10; + break; + case 4: + case 5: + shift = 8; + break; + } + #ifdef FBCON_HAS_CFB8 switch (p->var.bits_per_pixel) { case 8: /* "transparent" stuff is completely ignored. */ - riva_wclut (regno, red >> 10, green >> 10, blue >> 10); + riva_wclut (regno, red >> shift, green >> shift, blue >> shift); break; default: /* do nothing */ @@ -1469,24 +1458,24 @@ static void riva_load_video_mode (struct rivafb_info *rinfo, newmode.crtc[0x0] = Set8Bits (hTotal - 4); newmode.crtc[0x1] = Set8Bits (hDisplay); newmode.crtc[0x2] = Set8Bits (hDisplay); - newmode.crtc[0x3] = SetBitField (hTotal, 4: 0, 4:0) | SetBit (7); + newmode.crtc[0x3] = SetBitField (hTotal, 4: 0, 4:0) | SetBit (7); newmode.crtc[0x4] = Set8Bits (hStart); - newmode.crtc[0x5] = SetBitField (hTotal, 5: 5, 7:7) - | SetBitField (hEnd, 4: 0, 4:0); - newmode.crtc[0x6] = SetBitField (vTotal, 7: 0, 7:0); - newmode.crtc[0x7] = SetBitField (vTotal, 8: 8, 0:0) - | SetBitField (vDisplay, 8: 8, 1:1) - | SetBitField (vStart, 8: 8, 2:2) - | SetBitField (vDisplay, 8: 8, 3:3) - | SetBit (4) - | SetBitField (vTotal, 9: 9, 5:5) - | SetBitField (vDisplay, 9: 9, 6:6) - | SetBitField (vStart, 9: 9, 7:7); - newmode.crtc[0x9] = SetBitField (vDisplay, 9: 9, 5:5) - | SetBit (6); + newmode.crtc[0x5] = SetBitField (hTotal, 5: 5, 7:7) + | SetBitField (hEnd, 4: 0, 4:0); + newmode.crtc[0x6] = SetBitField (vTotal, 7: 0, 7:0); + newmode.crtc[0x7] = SetBitField (vTotal, 8: 8, 0:0) + | SetBitField (vDisplay, 8: 8, 1:1) + | SetBitField (vStart, 8: 8, 2:2) + | SetBitField (vDisplay, 8: 8, 3:3) + | SetBit (4) + | SetBitField (vTotal, 9: 9, 5:5) + | SetBitField (vDisplay, 9: 9, 6:6) + | SetBitField (vStart, 9: 9, 7:7); + newmode.crtc[0x9] = SetBitField (vDisplay, 9: 9, 5:5) + | SetBit (6); newmode.crtc[0x10] = Set8Bits (vStart); - newmode.crtc[0x11] = SetBitField (vEnd, 3: 0, 3:0) - | SetBit (5); + newmode.crtc[0x11] = SetBitField (vEnd, 3: 0, 3:0) + | SetBit (5); newmode.crtc[0x12] = Set8Bits (vDisplay); newmode.crtc[0x13] = ((width / 8) * (bpp / 8)) & 0xFF; newmode.crtc[0x15] = Set8Bits (vDisplay); @@ -1538,7 +1527,7 @@ module_init(rivafb_init); #endif /* MODULE */ module_exit(rivafb_exit); -MODULE_AUTHOR("Jeff Garzik <jgarzik@mandrakesoft.com>"); +MODULE_AUTHOR("Ani Joshi, maintainer"); MODULE_DESCRIPTION("Framebuffer driver for nVidia Riva 128, TNT, TNT2"); @@ -1644,7 +1633,7 @@ void riva_load_state (struct rivafb_info *rinfo, struct riva_regs *regs) for (i = 0; i < NUM_ATC_REGS; i++) { io_out8 (i, 0x3C0); - io_out8 (regs->attr[i], 0x3C1); + io_out8 (regs->attr[i], 0x3C0); } for (i = 0; i < NUM_GRC_REGS; i++) { @@ -1689,3 +1678,33 @@ struct rivafb_info *riva_board_list_add (struct rivafb_info *board_list, return board_list; } + + + +/** + * riva_board_list_del + * @board_list: Root node of list of boards + * @del_node: Node to be removed + * + * DESCRIPTION: + * Removes @del_node from the list referenced by @board_list + * + * RETURNS: + * New root node + */ +static +struct rivafb_info *riva_board_list_del (struct rivafb_info *board_list, + struct rivafb_info *del_node) +{ + struct rivafb_info *i_p = board_list; + + if (board_list == del_node) + return del_node->next; + + while (i_p->next != del_node) + i_p = i_p->next; + i_p->next = del_node->next; + + return board_list; +} + diff --git a/drivers/video/sisfb.c b/drivers/video/sisfb.c index d42448fbf..019a86a8e 100644 --- a/drivers/video/sisfb.c +++ b/drivers/video/sisfb.c @@ -175,9 +175,9 @@ static struct board { const char *name; } dev_list[] = { { - PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_300, "SIS 300"}, { - PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_540, "SIS 540"}, { - PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_630, "SIS 630"}, { + PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_300, "SIS 300"}, { + PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5300, "SIS 540"}, { + PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_6300, "SIS 630"}, { 0, 0, NULL} }; diff --git a/drivers/video/tdfxfb.c b/drivers/video/tdfxfb.c index 473f5019a..499d8fef7 100644 --- a/drivers/video/tdfxfb.c +++ b/drivers/video/tdfxfb.c @@ -1994,7 +1994,7 @@ int __init tdfxfb_init(void) { fb_info.iobase = pdev->base_address[2] & PCI_BASE_ADDRESS_IO_MASK; #else - fb_info.regbase_phys = pdev->resource[0].start; + fb_info.regbase_phys = pci_resource_start(pdev, 0); fb_info.regbase_size = 1 << 24; fb_info.regbase_virt = (unsigned long)ioremap_nocache(fb_info.regbase_phys, 1 << 24); diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 9fd867d0e..7f0e51187 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -674,9 +674,8 @@ static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) interpreter, &interp_load_addr); - lock_kernel(); + allow_write_access(interpreter); fput(interpreter); - unlock_kernel(); kfree(elf_interpreter); if (elf_entry == ~0UL) { @@ -763,9 +762,8 @@ out: /* error cleanup */ out_free_dentry: - lock_kernel(); + allow_write_access(interpreter); fput(interpreter); - unlock_kernel(); out_free_interp: if (elf_interpreter) kfree(elf_interpreter); diff --git a/fs/binfmt_em86.c b/fs/binfmt_em86.c index 1b18094eb..95c24a70a 100644 --- a/fs/binfmt_em86.c +++ b/fs/binfmt_em86.c @@ -17,6 +17,7 @@ #include <linux/binfmts.h> #include <linux/elf.h> #include <linux/init.h> +#include <linux/file.h> #define EM86_INTERP "/usr/bin/em86" @@ -43,6 +44,7 @@ static int load_em86(struct linux_binprm *bprm,struct pt_regs *regs) } bprm->sh_bang++; /* Well, the bang-shell is implicit... */ + allow_write_access(bprm->file); fput(bprm->file); bprm->file = NULL; diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index 0d44c3d4e..f9c30df1b 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -201,6 +201,7 @@ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs) if (!fmt) goto _ret; + allow_write_access(bprm->file); fput(bprm->file); bprm->file = NULL; diff --git a/fs/binfmt_script.c b/fs/binfmt_script.c index dc78f8389..3d5023e2d 100644 --- a/fs/binfmt_script.c +++ b/fs/binfmt_script.c @@ -29,6 +29,7 @@ static int load_script(struct linux_binprm *bprm,struct pt_regs *regs) */ bprm->sh_bang++; + allow_write_access(bprm->file); fput(bprm->file); bprm->file = NULL; diff --git a/fs/block_dev.c b/fs/block_dev.c index c455a735d..29972c8ca 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -313,7 +313,7 @@ ssize_t block_read(struct file * filp, char * buf, size_t count, loff_t *ppos) * since the vma has no handle. */ -static int block_fsync(struct file *filp, struct dentry *dentry) +static int block_fsync(struct file *filp, struct dentry *dentry, int datasync) { return fsync_dev(dentry->d_inode->i_rdev); } @@ -597,6 +597,8 @@ int blkdev_get(struct block_device *bdev, mode_t mode, unsigned flags, int kind) ret = bdev->bd_op->open(fake_inode, &fake_file); if (!ret) atomic_inc(&bdev->bd_openers); + else if (!atomic_read(&bdev->bd_openers)) + bdev->bd_op = NULL; iput(fake_inode); } } @@ -617,6 +619,8 @@ int blkdev_open(struct inode * inode, struct file * filp) ret = bdev->bd_op->open(inode,filp); if (!ret) atomic_inc(&bdev->bd_openers); + else if (!atomic_read(&bdev->bd_openers)) + bdev->bd_op = NULL; } up(&bdev->bd_sem); return ret; diff --git a/fs/buffer.c b/fs/buffer.c index 47d690fa4..b1e1c33b7 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -323,7 +323,7 @@ asmlinkage long sys_sync(void) * filp may be NULL if called via the msync of a vma. */ -int file_fsync(struct file *filp, struct dentry *dentry) +int file_fsync(struct file *filp, struct dentry *dentry, int datasync) { struct inode * inode = dentry->d_inode; struct super_block * sb; @@ -332,7 +332,7 @@ int file_fsync(struct file *filp, struct dentry *dentry) lock_kernel(); /* sync the inode to buffers */ - write_inode_now(inode); + write_inode_now(inode, 0); /* sync the superblock to buffers */ sb = inode->i_sb; @@ -360,12 +360,7 @@ asmlinkage long sys_fsync(unsigned int fd) goto out; dentry = file->f_dentry; - if (!dentry) - goto out_putf; - inode = dentry->d_inode; - if (!inode) - goto out_putf; err = -EINVAL; if (!file->f_op || !file->f_op->fsync) @@ -373,7 +368,7 @@ asmlinkage long sys_fsync(unsigned int fd) /* We need to protect against concurrent writers.. */ down(&inode->i_sem); - err = file->f_op->fsync(file, dentry); + err = file->f_op->fsync(file, dentry, 0); up(&inode->i_sem); out_putf: @@ -395,20 +390,14 @@ asmlinkage long sys_fdatasync(unsigned int fd) goto out; dentry = file->f_dentry; - if (!dentry) - goto out_putf; - inode = dentry->d_inode; - if (!inode) - goto out_putf; err = -EINVAL; if (!file->f_op || !file->f_op->fsync) goto out_putf; - /* this needs further work, at the moment it is identical to fsync() */ down(&inode->i_sem); - err = file->f_op->fsync(file, dentry); + err = file->f_op->fsync(file, dentry, 1); up(&inode->i_sem); out_putf: @@ -2101,6 +2090,7 @@ static int grow_buffers(int size) spin_unlock(&free_list[isize].lock); page->buffers = bh; + page->flags &= ~(1 << PG_referenced); lru_cache_add(page); atomic_inc(&buffermem_pages); return 1; @@ -2499,7 +2489,7 @@ asmlinkage long sys_bdflush(int func, long data) * the syscall above, but now we launch it ourselves internally with * kernel_thread(...) directly after the first thread in init/main.c */ -int bdflush(void * unused) +int bdflush(void *sem) { struct task_struct *tsk = current; int flushed; @@ -2521,6 +2511,8 @@ int bdflush(void * unused) recalc_sigpending(tsk); spin_unlock_irq(&tsk->sigmask_lock); + up((struct semaphore *)sem); + for (;;) { CHECK_EMERGENCY_SYNC @@ -2555,7 +2547,7 @@ int bdflush(void * unused) * You don't need to change your userspace configuration since * the userspace `update` will do_exit(0) at the first sys_bdflush(). */ -int kupdate(void * unused) +int kupdate(void *sem) { struct task_struct * tsk = current; int interval; @@ -2571,6 +2563,8 @@ int kupdate(void * unused) recalc_sigpending(tsk); spin_unlock_irq(&tsk->sigmask_lock); + up((struct semaphore *)sem); + for (;;) { /* update interval */ interval = bdf_prm.b_un.interval; @@ -2604,8 +2598,11 @@ int kupdate(void * unused) static int __init bdflush_init(void) { - kernel_thread(bdflush, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); - kernel_thread(kupdate, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); + DECLARE_MUTEX_LOCKED(sem); + kernel_thread(bdflush, &sem, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); + down(&sem); + kernel_thread(kupdate, &sem, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); + down(&sem); return 0; } diff --git a/fs/coda/dir.c b/fs/coda/dir.c index e949f7986..0e6fa5625 100644 --- a/fs/coda/dir.c +++ b/fs/coda/dir.c @@ -55,7 +55,7 @@ static void coda_prepare_fakefile(struct inode *coda_inode, struct dentry *open_dentry); static int coda_venus_readdir(struct file *filp, void *dirent, filldir_t filldir); -int coda_fsync(struct file *, struct dentry *dentry); +int coda_fsync(struct file *, struct dentry *dentry, int datasync); int coda_hasmknod = 0; diff --git a/fs/coda/file.c b/fs/coda/file.c index 9aecd716a..128b07d44 100644 --- a/fs/coda/file.c +++ b/fs/coda/file.c @@ -40,7 +40,7 @@ coda_file_write(struct file *file,const char *buf,size_t count,loff_t *ppos) } /* exported from this file (used for dirs) */ -int coda_fsync(struct file *coda_file, struct dentry *coda_dentry) +int coda_fsync(struct file *coda_file, struct dentry *coda_dentry, int datasync) { struct inode *inode = coda_dentry->d_inode; struct dentry cont_dentry; @@ -60,7 +60,7 @@ int coda_fsync(struct file *coda_file, struct dentry *coda_dentry) cont_dentry.d_inode = (struct inode *)inode->i_mapping->host; down(&cont_dentry.d_inode->i_sem); - result = file_fsync(NULL, &cont_dentry); + result = file_fsync(NULL, &cont_dentry, datasync); up(&cont_dentry.d_inode->i_sem); if ( result == 0 ) { diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c index 45025e871..14fe68ad4 100644 --- a/fs/coda/psdev.c +++ b/fs/coda/psdev.c @@ -109,7 +109,7 @@ static ssize_t coda_psdev_write(struct file *file, const char *buf, ssize_t retval = 0, count = 0; int error; - if ( !coda_upc_comm.vc_pid ) + if ( !coda_upc_comm.vc_inuse ) return -EIO; /* Peek at the opcode, uniquefier */ if (copy_from_user(&hdr, buf, 2 * sizeof(u_long))) @@ -291,29 +291,14 @@ static int coda_psdev_open(struct inode * inode, struct file * file) struct venus_comm *vcp = &coda_upc_comm; ENTRY; - /* first opener: must be lento. Initialize & take its pid */ - if ( (file->f_flags & O_ACCMODE) == O_RDWR ) { - if ( vcp->vc_pid ) { - printk("Venus pid already set to %d!!\n", vcp->vc_pid); - return -1; - } - if ( vcp->vc_inuse ) { - printk("psdev_open: Cannot O_RDWR while open.\n"); - return -1; - } - } - - vcp->vc_inuse++; - - if ( (file->f_flags & O_ACCMODE) == O_RDWR ) { - vcp->vc_pid = current->pid; - vcp->vc_seq = 0; - INIT_LIST_HEAD(&vcp->vc_pending); - INIT_LIST_HEAD(&vcp->vc_processing); + /* first opener, initialize */ + if (!vcp->vc_inuse++) { + INIT_LIST_HEAD(&vcp->vc_pending); + INIT_LIST_HEAD(&vcp->vc_processing); + vcp->vc_seq = 0; } - CDEBUG(D_PSDEV, "inuse: %d, vc_pid %d, caller %d\n", - vcp->vc_inuse, vcp->vc_pid, current->pid); + CDEBUG(D_PSDEV, "inuse: %d\n", vcp->vc_inuse); EXIT; return 0; @@ -332,17 +317,9 @@ static int coda_psdev_release(struct inode * inode, struct file * file) return -1; } - vcp->vc_inuse--; - CDEBUG(D_PSDEV, "inuse: %d, vc_pid %d, caller %d\n", - vcp->vc_inuse, vcp->vc_pid, current->pid); - - if ( vcp->vc_pid != current->pid ) { - /* FIXME: this is broken. If venus does fork(), accounting goes wrong */ - printk( "Closed by someone else than caller?\n" ); - return 0; - } + CDEBUG(D_PSDEV, "psdev_release: inuse %d\n", vcp->vc_inuse); + if (--vcp->vc_inuse) return 0; - vcp->vc_pid = 0; /* Wakeup clients so they can return. */ CDEBUG(D_PSDEV, "wake up pending clients\n"); lh = vcp->vc_pending.next; diff --git a/fs/coda/upcall.c b/fs/coda/upcall.c index 674c8cb3b..206c9d8b0 100644 --- a/fs/coda/upcall.c +++ b/fs/coda/upcall.c @@ -625,7 +625,7 @@ static inline unsigned long coda_waitfor_upcall(struct upc_req *vmp) set_current_state(TASK_UNINTERRUPTIBLE); /* venus died */ - if ( !coda_upc_comm.vc_pid ) + if ( !coda_upc_comm.vc_inuse ) break; /* got a reply */ @@ -688,7 +688,7 @@ static int coda_upcall(struct coda_sb_info *sbi, ENTRY; vcommp = &coda_upc_comm; - if ( !vcommp->vc_pid ) { + if ( !vcommp->vc_inuse ) { printk("No pseudo device in upcall comms at %p\n", vcommp); return -ENXIO; } @@ -733,7 +733,7 @@ ENTRY; CDEBUG(D_UPCALL, "..process %d woken up by Venus for req at %p, data at %p\n", current->pid, req, req->uc_data); - if (vcommp->vc_pid) { /* i.e. Venus is still alive */ + if (vcommp->vc_inuse) { /* i.e. Venus is still alive */ /* Op went through, interrupt or not... */ if (req->uc_flags & REQ_WRITE) { out = (union outputArgs *)req->uc_data; @@ -101,37 +101,54 @@ static inline void put_binfmt(struct linux_binfmt * fmt) */ asmlinkage long sys_uselib(const char * library) { - int fd, retval; struct file * file; + struct nameidata nd; + int error; - fd = sys_open(library, 0, 0); - if (fd < 0) - return fd; - file = fget(fd); - retval = -ENOEXEC; - if (file) { - if(file->f_op && file->f_op->read) { - struct linux_binfmt * fmt; + error = user_path_walk(library, &nd); + if (error) + goto out; - read_lock(&binfmt_lock); - for (fmt = formats ; fmt ; fmt = fmt->next) { - if (!fmt->load_shlib) - continue; - if (!try_inc_mod_count(fmt->module)) - continue; - read_unlock(&binfmt_lock); - retval = fmt->load_shlib(file); - read_lock(&binfmt_lock); - put_binfmt(fmt); - if (retval != -ENOEXEC) - break; - } + error = -EINVAL; + if (!S_ISREG(nd.dentry->d_inode->i_mode)) + goto exit; + + error = permission(nd.dentry->d_inode, MAY_READ | MAY_EXEC); + if (error) + goto exit; + + lock_kernel(); + file = dentry_open(nd.dentry, nd.mnt, O_RDONLY); + unlock_kernel(); + error = PTR_ERR(file); + if (IS_ERR(file)) + goto out; + + error = -ENOEXEC; + if(file->f_op && file->f_op->read) { + struct linux_binfmt * fmt; + + read_lock(&binfmt_lock); + for (fmt = formats ; fmt ; fmt = fmt->next) { + if (!fmt->load_shlib) + continue; + if (!try_inc_mod_count(fmt->module)) + continue; read_unlock(&binfmt_lock); + error = fmt->load_shlib(file); + read_lock(&binfmt_lock); + put_binfmt(fmt); + if (error != -ENOEXEC) + break; } - fput(file); + read_unlock(&binfmt_lock); } - sys_close(fd); - return retval; + fput(file); +out: + return error; +exit: + path_release(&nd); + goto out; } /* @@ -319,6 +336,7 @@ int setup_arg_pages(struct linux_binprm *bprm) struct file *open_exec(const char *name) { struct nameidata nd; + struct inode *inode; struct file *file; int err = 0; @@ -328,14 +346,22 @@ struct file *open_exec(const char *name) unlock_kernel(); file = ERR_PTR(err); if (!err) { + inode = nd.dentry->d_inode; file = ERR_PTR(-EACCES); - if (S_ISREG(nd.dentry->d_inode->i_mode)) { - int err = permission(nd.dentry->d_inode, MAY_EXEC); + if (!IS_NOEXEC(inode) && S_ISREG(inode->i_mode)) { + int err = permission(inode, MAY_EXEC); file = ERR_PTR(err); if (!err) { lock_kernel(); file = dentry_open(nd.dentry, nd.mnt, O_RDONLY); unlock_kernel(); + if (!IS_ERR(file)) { + err = deny_write_access(file); + if (err) { + fput(file); + file = ERR_PTR(err); + } + } out: return file; } @@ -540,23 +566,13 @@ static inline int must_not_trace_exec(struct task_struct * p) int prepare_binprm(struct linux_binprm *bprm) { int mode; - int retval,id_change,cap_raised; + int id_change,cap_raised; struct inode * inode = bprm->file->f_dentry->d_inode; mode = inode->i_mode; - if (!S_ISREG(mode)) /* must be regular file */ - return -EACCES; - if (!(mode & 0111)) /* with at least _one_ execute bit set */ + /* Huh? We had already checked for MAY_EXEC, WTF do we check this? */ + if (!(mode & 0111)) /* with at least _one_ execute bit set */ return -EACCES; - if (IS_NOEXEC(inode)) /* FS mustn't be mounted noexec */ - return -EACCES; - if (!inode->i_sb) - return -EACCES; - if ((retval = permission(inode, MAY_EXEC)) != 0) - return retval; - /* better not execute files which are being written to */ - if (atomic_read(&inode->i_writecount) > 0) - return -ETXTBSY; bprm->e_uid = current->euid; bprm->e_gid = current->egid; @@ -728,6 +744,7 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs) char * dynloader[] = { "/sbin/loader" }; struct file * file; + allow_write_access(bprm->file); fput(bprm->file); bprm->file = NULL; @@ -761,6 +778,7 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs) retval = fn(bprm, regs); if (retval >= 0) { put_binfmt(fmt); + allow_write_access(bprm->file); if (bprm->file) fput(bprm->file); bprm->file = NULL; @@ -822,11 +840,13 @@ int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs bprm.loader = 0; bprm.exec = 0; if ((bprm.argc = count(argv, bprm.p / sizeof(void *))) < 0) { + allow_write_access(file); fput(file); return bprm.argc; } if ((bprm.envc = count(envp, bprm.p / sizeof(void *))) < 0) { + allow_write_access(file); fput(file); return bprm.envc; } @@ -855,6 +875,7 @@ int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs out: /* Something went wrong, return the inode and free the argument pages*/ + allow_write_access(bprm.file); if (bprm.file) fput(bprm.file); diff --git a/fs/ext2/fsync.c b/fs/ext2/fsync.c index 52ffd6138..1e4478cc7 100644 --- a/fs/ext2/fsync.c +++ b/fs/ext2/fsync.c @@ -124,7 +124,7 @@ static int sync_tindirect(struct inode * inode, u32 * tiblock, int wait) * even pass file to fsync ? */ -int ext2_sync_file(struct file * file, struct dentry *dentry) +int ext2_sync_file(struct file * file, struct dentry *dentry, int datasync) { int wait, err = 0; struct inode *inode = dentry->d_inode; diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 7e5263fb1..d999b2b4f 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -904,7 +904,7 @@ static int ext2_update_inode(struct inode * inode, int do_sync) return err; } -void ext2_write_inode (struct inode * inode) +void ext2_write_inode (struct inode * inode, int wait) { lock_kernel(); ext2_update_inode (inode, 0); diff --git a/fs/ext2/super.c b/fs/ext2/super.c index aa6a599fc..d3af3b992 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -593,7 +593,6 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data, /* * set up enough so that it can read an inode */ - sb->s_dev = dev; sb->s_op = &ext2_sops; sb->s_root = d_alloc_root(iget(sb, EXT2_ROOT_INO)); if (!sb->s_root) { diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 85cc4e1a6..bd8d0ae26 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -837,7 +837,7 @@ static void fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de) MSDOS_I(inode)->i_ctime_ms = de->ctime_ms; } -void fat_write_inode(struct inode *inode) +void fat_write_inode(struct inode *inode, int wait) { struct super_block *sb = inode->i_sb; struct buffer_head *bh; diff --git a/fs/fcntl.c b/fs/fcntl.c index f6e4e1651..37e32a012 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -252,8 +252,8 @@ asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) err = sock_fcntl (filp, cmd, arg); break; } - fput(filp); unlock_kernel(); + fput(filp); out: return err; } diff --git a/fs/file_table.c b/fs/file_table.c index ecaa46896..5c722143d 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -16,9 +16,7 @@ static kmem_cache_t *filp_cache; /* sysctl tunables... */ -int nr_files; /* read only */ -int nr_free_files; /* read only */ -int max_files = NR_FILE;/* tunable */ +struct files_stat_struct files_stat = {0, 0, NR_FILE}; /* Here the new files go */ static LIST_HEAD(anon_list); @@ -53,11 +51,11 @@ struct file * get_empty_filp(void) struct file * f; file_list_lock(); - if (nr_free_files > NR_RESERVED_FILES) { + if (files_stat.nr_free_files > NR_RESERVED_FILES) { used_one: f = list_entry(free_list.next, struct file, f_list); list_del(&f->f_list); - nr_free_files--; + files_stat.nr_free_files--; new_one: file_list_unlock(); memset(f, 0, sizeof(*f)); @@ -73,25 +71,25 @@ struct file * get_empty_filp(void) /* * Use a reserved one if we're the superuser */ - if (nr_free_files && !current->euid) + if (files_stat.nr_free_files && !current->euid) goto used_one; /* * Allocate a new one if we're below the limit. */ - if (nr_files < max_files) { + if (files_stat.nr_files < files_stat.max_files) { file_list_unlock(); f = kmem_cache_alloc(filp_cache, SLAB_KERNEL); file_list_lock(); if (f) { - nr_files++; + files_stat.nr_files++; goto new_one; } /* Big problems... */ printk("VFS: filp allocation failed\n"); - } else if (max_files > old_max) { - printk("VFS: file-max limit %d reached\n", max_files); - old_max = max_files; + } else if (files_stat.max_files > old_max) { + printk("VFS: file-max limit %d reached\n", files_stat.max_files); + old_max = files_stat.max_files; } file_list_unlock(); return NULL; @@ -148,7 +146,7 @@ void _fput(struct file *file) file_list_lock(); list_del(&file->f_list); list_add(&file->f_list, &free_list); - nr_free_files++; + files_stat.nr_free_files++; file_list_unlock(); } @@ -160,7 +158,7 @@ void put_filp(struct file *file) file_list_lock(); list_del(&file->f_list); list_add(&file->f_list, &free_list); - nr_free_files++; + files_stat.nr_free_files++; file_list_unlock(); } } diff --git a/fs/hpfs/file.c b/fs/hpfs/file.c index c0707b52c..4a301f593 100644 --- a/fs/hpfs/file.c +++ b/fs/hpfs/file.c @@ -23,7 +23,7 @@ int hpfs_file_release(struct inode *inode, struct file *file) return 0; } -int hpfs_file_fsync(struct file *file, struct dentry *dentry) +int hpfs_file_fsync(struct file *file, struct dentry *dentry, int datasync) { /*return file_fsync(file, dentry);*/ return 0; /* Don't fsync :-) */ diff --git a/fs/hpfs/hpfs_fn.h b/fs/hpfs/hpfs_fn.h index a01140f1f..78341ca16 100644 --- a/fs/hpfs/hpfs_fn.h +++ b/fs/hpfs/hpfs_fn.h @@ -256,7 +256,7 @@ void hpfs_set_ea(struct inode *, struct fnode *, char *, char *, int); int hpfs_file_release(struct inode *, struct file *); int hpfs_open(struct inode *, struct file *); -int hpfs_file_fsync(struct file *, struct dentry *); +int hpfs_file_fsync(struct file *, struct dentry *, int); secno hpfs_bmap(struct inode *, unsigned); void hpfs_truncate(struct inode *); int hpfs_get_block(struct inode *inode, long iblock, struct buffer_head *bh_result, int create); diff --git a/fs/hpfs/namei.c b/fs/hpfs/namei.c index b09ad98ea..5684801df 100644 --- a/fs/hpfs/namei.c +++ b/fs/hpfs/namei.c @@ -330,7 +330,15 @@ int hpfs_unlink(struct inode *dir, struct dentry *dentry) struct iattr newattrs; int err; hpfs_unlock_2inodes(dir, inode); - if (rep || dentry->d_count > 1 || permission(inode, MAY_WRITE) || get_write_access(inode)) goto ret; + if (rep) + goto ret; + d_drop(dentry); + if (dentry->d_count > 1 || + permission(inode, MAY_WRITE) || + get_write_access(inode)) { + d_rehash(dentry); + goto ret; + } /*printk("HPFS: truncating file before delete.\n");*/ down(&inode->i_sem); newattrs.ia_size = 0; diff --git a/fs/inode.c b/fs/inode.c index e46359b03..3dbd9f54e 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -162,10 +162,10 @@ static inline void wait_on_inode(struct inode *inode) } -static inline void write_inode(struct inode *inode) +static inline void write_inode(struct inode *inode, int sync) { if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->write_inode) - inode->i_sb->s_op->write_inode(inode); + inode->i_sb->s_op->write_inode(inode, sync); } static inline void __iget(struct inode * inode) @@ -182,7 +182,7 @@ static inline void __iget(struct inode * inode) inodes_stat.nr_unused--; } -static inline void sync_one(struct inode *inode) +static inline void sync_one(struct inode *inode, int sync) { if (inode->i_state & I_LOCK) { __iget(inode); @@ -199,7 +199,7 @@ static inline void sync_one(struct inode *inode) inode->i_state ^= I_DIRTY | I_LOCK; spin_unlock(&inode_lock); - write_inode(inode); + write_inode(inode, sync); spin_lock(&inode_lock); inode->i_state &= ~I_LOCK; @@ -212,7 +212,7 @@ static inline void sync_list(struct list_head *head) struct list_head * tmp; while ((tmp = head->prev) != head) - sync_one(list_entry(tmp, struct inode, i_list)); + sync_one(list_entry(tmp, struct inode, i_list), 0); } /** @@ -266,14 +266,14 @@ static void sync_all_inodes(void) * dirty. This is primarily needed by knfsd. */ -void write_inode_now(struct inode *inode) +void write_inode_now(struct inode *inode, int sync) { struct super_block * sb = inode->i_sb; if (sb) { spin_lock(&inode_lock); while (inode->i_state & I_DIRTY) - sync_one(inode); + sync_one(inode, sync); spin_unlock(&inode_lock); } else diff --git a/fs/ioctl.c b/fs/ioctl.c index 16ad5ec26..f02d766bd 100644 --- a/fs/ioctl.c +++ b/fs/ioctl.c @@ -107,8 +107,8 @@ asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) else if (filp->f_op && filp->f_op->ioctl) error = filp->f_op->ioctl(filp->f_dentry->d_inode, filp, cmd, arg); } - fput(filp); unlock_kernel(); + fput(filp); out: return error; diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c index f89188d12..a3a4f072f 100644 --- a/fs/lockd/clntlock.c +++ b/fs/lockd/clntlock.c @@ -162,8 +162,7 @@ reclaimer(void *ptr) { struct nlm_host *host = (struct nlm_host *) ptr; struct nlm_wait *block; - struct file_lock *fl; - struct inode *inode; + struct list_head *tmp; /* This one ensures that our parent doesn't terminate while the * reclaim is in progress */ @@ -171,19 +170,21 @@ reclaimer(void *ptr) lockd_up(); /* First, reclaim all locks that have been granted previously. */ - do { - for (fl = file_lock_table; fl; fl = fl->fl_nextlink) { - inode = fl->fl_file->f_dentry->d_inode; - if (inode->i_sb->s_magic == NFS_SUPER_MAGIC - && nlm_cmp_addr(NFS_ADDR(inode), &host->h_addr) - && fl->fl_u.nfs_fl.state != host->h_state - && (fl->fl_u.nfs_fl.flags & NFS_LCK_GRANTED)) { - fl->fl_u.nfs_fl.flags &= ~ NFS_LCK_GRANTED; - nlmclnt_reclaim(host, fl); - break; - } +restart: + tmp = file_lock_list.next; + while (tmp != &file_lock_list) { + struct file_lock *fl = list_entry(tmp, struct file_lock, fl_link); + struct inode *inode = fl->fl_file->f_dentry->d_inode; + if (inode->i_sb->s_magic == NFS_SUPER_MAGIC && + nlm_cmp_addr(NFS_ADDR(inode), &host->h_addr) && + fl->fl_u.nfs_fl.state != host->h_state && + (fl->fl_u.nfs_fl.flags & NFS_LCK_GRANTED)) { + fl->fl_u.nfs_fl.flags &= ~ NFS_LCK_GRANTED; + nlmclnt_reclaim(host, fl); + goto restart; } - } while (fl); + tmp = tmp->next; + } host->h_reclaiming = 0; wake_up(&host->h_gracewait); diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index 279fcc3c1..56c8d8173 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -347,7 +347,7 @@ again: /* Append to list of blocked */ nlmsvc_insert_block(block, NLM_NEVER); - if (!block->b_call.a_args.lock.fl.fl_prevblock) { + if (!list_empty(&block->b_call.a_args.lock.fl.fl_block)) { /* Now add block to block list of the conflicting lock if we haven't done so. */ dprintk("lockd: blocking on this lock.\n"); diff --git a/fs/locks.c b/fs/locks.c index 015b8e87a..6ce980735 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -108,57 +108,150 @@ #include <linux/malloc.h> #include <linux/file.h> #include <linux/smp_lock.h> +#include <linux/init.h> #include <asm/uaccess.h> -static int flock_make_lock(struct file *filp, struct file_lock *fl, - unsigned int cmd); -static int posix_make_lock(struct file *filp, struct file_lock *fl, - struct flock *l); -static int flock_locks_conflict(struct file_lock *caller_fl, - struct file_lock *sys_fl); -static int posix_locks_conflict(struct file_lock *caller_fl, - struct file_lock *sys_fl); -static int locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl); -static int flock_lock_file(struct file *filp, struct file_lock *caller, - unsigned int wait); -static int posix_locks_deadlock(struct file_lock *caller, - struct file_lock *blocker); - -static struct file_lock *locks_empty_lock(void); -static struct file_lock *locks_init_lock(struct file_lock *, - struct file_lock *); -static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl); -static void locks_delete_lock(struct file_lock **thisfl_p, unsigned int wait); -static void lock_get_status(char* out, struct file_lock *fl, int id, char *pfx); - -static void locks_insert_block(struct file_lock *blocker, struct file_lock *waiter); -static void locks_delete_block(struct file_lock *blocker, struct file_lock *waiter); -static void locks_wake_up_blocks(struct file_lock *blocker, unsigned int wait); - -struct file_lock *file_lock_table = NULL; - -/* Allocate a new lock, and initialize its fields from fl. - * The lock is not inserted into any lists until locks_insert_lock() or - * locks_insert_block() are called. - */ -static inline struct file_lock *locks_alloc_lock(struct file_lock *fl) +LIST_HEAD(file_lock_list); +static LIST_HEAD(blocked_list); + +static kmem_cache_t *filelock_cache; + +/* Allocate an empty lock structure. */ +static struct file_lock *locks_alloc_lock(void) { - return locks_init_lock(locks_empty_lock(), fl); + struct file_lock *fl; + fl = kmem_cache_alloc(filelock_cache, SLAB_KERNEL); + return fl; } -/* Free lock not inserted in any queue. - */ +/* Free a lock which is not in use. */ static inline void locks_free_lock(struct file_lock *fl) { + if (fl == NULL) { + BUG(); + return; + } + if (waitqueue_active(&fl->fl_wait)) panic("Attempting to free lock with active wait queue"); - if (fl->fl_nextblock != NULL || fl->fl_prevblock != NULL) + if (!list_empty(&fl->fl_block)) panic("Attempting to free lock with active block list"); - - kfree(fl); - return; + + if (!list_empty(&fl->fl_link)) + panic("Attempting to free lock on active lock list"); + + kmem_cache_free(filelock_cache, fl); +} + +/* + * Initialises the fields of the file lock which are invariant for + * free file_locks. + */ +static void init_once(void *foo, kmem_cache_t *cache, unsigned long flags) +{ + struct file_lock *lock = (struct file_lock *) foo; + + if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) != + SLAB_CTOR_CONSTRUCTOR) + return; + + lock->fl_next = NULL; + INIT_LIST_HEAD(&lock->fl_link); + INIT_LIST_HEAD(&lock->fl_block); + init_waitqueue_head(&lock->fl_wait); +} + +/* + * Initialize a new lock from an existing file_lock structure. + */ +static void locks_copy_lock(struct file_lock *new, struct file_lock *fl) +{ + new->fl_owner = fl->fl_owner; + new->fl_pid = fl->fl_pid; + new->fl_file = fl->fl_file; + new->fl_flags = fl->fl_flags; + new->fl_type = fl->fl_type; + new->fl_start = fl->fl_start; + new->fl_end = fl->fl_end; + new->fl_notify = fl->fl_notify; + new->fl_insert = fl->fl_insert; + new->fl_remove = fl->fl_remove; + new->fl_u = fl->fl_u; +} + +/* Fill in a file_lock structure with an appropriate FLOCK lock. */ +static struct file_lock *flock_make_lock(struct file *filp, unsigned int type) +{ + struct file_lock *fl = locks_alloc_lock(); + if (fl == NULL) + return NULL; + + fl->fl_owner = NULL; + fl->fl_file = filp; + fl->fl_pid = current->pid; + fl->fl_flags = FL_FLOCK; + fl->fl_type = type; + fl->fl_start = 0; + fl->fl_end = OFFSET_MAX; + fl->fl_notify = NULL; + fl->fl_insert = NULL; + fl->fl_remove = NULL; + + return fl; +} + +/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX + * style lock. + */ +static int posix_make_lock(struct file *filp, struct file_lock *fl, + struct flock *l) +{ + loff_t start; + + switch (l->l_whence) { + case 0: /*SEEK_SET*/ + start = 0; + break; + case 1: /*SEEK_CUR*/ + start = filp->f_pos; + break; + case 2: /*SEEK_END*/ + start = filp->f_dentry->d_inode->i_size; + break; + default: + return (0); + } + + if (((start += l->l_start) < 0) || (l->l_len < 0)) + return (0); + fl->fl_end = start + l->l_len - 1; + if (l->l_len > 0 && fl->fl_end < 0) + return (0); + fl->fl_start = start; /* we record the absolute position */ + if (l->l_len == 0) + fl->fl_end = OFFSET_MAX; + + fl->fl_owner = current->files; + fl->fl_pid = current->pid; + fl->fl_file = filp; + fl->fl_flags = FL_POSIX; + fl->fl_notify = NULL; + fl->fl_insert = NULL; + fl->fl_remove = NULL; + + switch (l->l_type) { + case F_RDLCK: + case F_WRLCK: + case F_UNLCK: + fl->fl_type = l->l_type; + break; + default: + return (0); + } + + return (1); } /* Check if two locks overlap each other. @@ -181,6 +274,17 @@ locks_same_owner(struct file_lock *fl1, struct file_lock *fl2) (fl1->fl_pid == fl2->fl_pid); } +/* Remove waiter from blocker's block list. + * When blocker ends up pointing to itself then the list is empty. + */ +static void locks_delete_block(struct file_lock *waiter) +{ + list_del(&waiter->fl_block); + INIT_LIST_HEAD(&waiter->fl_block); + list_del(&waiter->fl_link); + INIT_LIST_HEAD(&waiter->fl_link); +} + /* Insert waiter into blocker's block list. * We use a circular list so that processes can be easily woken up in * the order they blocked. The documentation doesn't require this but @@ -189,71 +293,15 @@ locks_same_owner(struct file_lock *fl1, struct file_lock *fl2) static void locks_insert_block(struct file_lock *blocker, struct file_lock *waiter) { - struct file_lock *prevblock; - - if (waiter->fl_prevblock) { - printk(KERN_ERR "locks_insert_block: remove duplicated lock " - "(pid=%d %Ld-%Ld type=%d)\n", - waiter->fl_pid, (long long)waiter->fl_start, - (long long)waiter->fl_end, waiter->fl_type); - locks_delete_block(waiter->fl_prevblock, waiter); + if (!list_empty(&waiter->fl_block)) { + printk(KERN_ERR "locks_insert_block: removing duplicated lock " + "(pid=%d %Ld-%Ld type=%d)\n", waiter->fl_pid, + waiter->fl_start, waiter->fl_end, waiter->fl_type); + locks_delete_block(waiter); } - - if (blocker->fl_prevblock == NULL) - /* No previous waiters - list is empty */ - prevblock = blocker; - else - /* Previous waiters exist - add to end of list */ - prevblock = blocker->fl_prevblock; - - prevblock->fl_nextblock = waiter; - blocker->fl_prevblock = waiter; - waiter->fl_nextblock = blocker; - waiter->fl_prevblock = prevblock; - - return; -} - -/* Remove waiter from blocker's block list. - * When blocker ends up pointing to itself then the list is empty. - */ -static void locks_delete_block(struct file_lock *blocker, - struct file_lock *waiter) -{ - struct file_lock *nextblock; - struct file_lock *prevblock; - - nextblock = waiter->fl_nextblock; - prevblock = waiter->fl_prevblock; - - if (nextblock == NULL) - return; - - nextblock->fl_prevblock = prevblock; - prevblock->fl_nextblock = nextblock; - - waiter->fl_prevblock = waiter->fl_nextblock = NULL; - if (blocker->fl_nextblock == blocker) - /* No more locks on blocker's blocked list */ - blocker->fl_prevblock = blocker->fl_nextblock = NULL; - return; -} - -/* The following two are for the benefit of lockd. - */ -void -posix_block_lock(struct file_lock *blocker, struct file_lock *waiter) -{ - locks_insert_block(blocker, waiter); - return; -} - -void -posix_unblock_lock(struct file_lock *waiter) -{ - if (waiter->fl_prevblock) - locks_delete_block(waiter->fl_prevblock, waiter); - return; + list_add_tail(&waiter->fl_block, &blocker->fl_block); +// list_add(&waiter->fl_link, &blocked_list); +// waiter->fl_next = blocker; } /* Wake up processes blocked waiting for blocker. @@ -262,9 +310,8 @@ posix_unblock_lock(struct file_lock *waiter) */ static void locks_wake_up_blocks(struct file_lock *blocker, unsigned int wait) { - struct file_lock *waiter; - - while ((waiter = blocker->fl_nextblock) != NULL) { + while (!list_empty(&blocker->fl_block)) { + struct file_lock *waiter = list_entry(blocker->fl_block.next, struct file_lock, fl_block); /* N.B. Is it possible for the notify function to block?? */ if (waiter->fl_notify) waiter->fl_notify(waiter); @@ -279,262 +326,105 @@ static void locks_wake_up_blocks(struct file_lock *blocker, unsigned int wait) /* Remove waiter from the block list, because by the * time it wakes up blocker won't exist any more. */ - locks_delete_block(blocker, waiter); + locks_delete_block(waiter); } } - return; } -/* flock() system call entry point. Apply a FL_FLOCK style lock to - * an open file descriptor. +/* Insert file lock fl into an inode's lock list at the position indicated + * by pos. At the same time add the lock to the global file lock list. */ -asmlinkage long sys_flock(unsigned int fd, unsigned int cmd) +static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl) { - struct file_lock file_lock; - struct file *filp; - int error; + list_add(&fl->fl_link, &file_lock_list); - lock_kernel(); - error = -EBADF; - filp = fget(fd); - if (!filp) - goto out; - error = -EINVAL; - if (!flock_make_lock(filp, &file_lock, cmd)) - goto out_putf; - error = -EBADF; - if ((file_lock.fl_type != F_UNLCK) && !(filp->f_mode & 3)) - goto out_putf; - error = flock_lock_file(filp, &file_lock, - (cmd & (LOCK_UN | LOCK_NB)) ? 0 : 1); -out_putf: - fput(filp); -out: - unlock_kernel(); - return (error); + /* insert into file's list */ + fl->fl_next = *pos; + *pos = fl; + + if (fl->fl_insert) + fl->fl_insert(fl); } -/* Report the first existing lock that would conflict with l. - * This implements the F_GETLK command of fcntl(). +/* Delete a lock and free it. + * First remove our lock from the active lock lists. Then call + * locks_wake_up_blocks() to wake up processes that are blocked + * waiting for this lock. Finally free the lock structure. */ -int fcntl_getlk(unsigned int fd, struct flock *l) +static void locks_delete_lock(struct file_lock **thisfl_p, unsigned int wait) { - struct file *filp; - struct file_lock *fl,file_lock; - struct flock flock; - int error; + int (*lock)(struct file *, int, struct file_lock *); + struct file_lock *fl = *thisfl_p; - error = -EFAULT; - if (copy_from_user(&flock, l, sizeof(flock))) - goto out; - error = -EINVAL; - if ((flock.l_type != F_RDLCK) && (flock.l_type != F_WRLCK)) - goto out; + *thisfl_p = fl->fl_next; + fl->fl_next = NULL; - error = -EBADF; - filp = fget(fd); - if (!filp) - goto out; + list_del(&fl->fl_link); + INIT_LIST_HEAD(&fl->fl_link); - if (!posix_make_lock(filp, &file_lock, &flock)) - goto out_putf; + if (fl->fl_remove) + fl->fl_remove(fl); - if (filp->f_op->lock) { - error = filp->f_op->lock(filp, F_GETLK, &file_lock); - if (error < 0) - goto out_putf; - else if (error == LOCK_USE_CLNT) - /* Bypass for NFS with no locking - 2.0.36 compat */ - fl = posix_test_lock(filp, &file_lock); - else - fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock); - } else { - fl = posix_test_lock(filp, &file_lock); - } - - flock.l_type = F_UNLCK; - if (fl != NULL) { - flock.l_pid = fl->fl_pid; - flock.l_start = fl->fl_start; - flock.l_len = fl->fl_end == OFFSET_MAX ? 0 : - fl->fl_end - fl->fl_start + 1; - flock.l_whence = 0; - flock.l_type = fl->fl_type; + locks_wake_up_blocks(fl, wait); + lock = fl->fl_file->f_op->lock; + if (lock) { + fl->fl_type = F_UNLCK; + lock(fl->fl_file, F_SETLK, fl); } - error = -EFAULT; - if (!copy_to_user(l, &flock, sizeof(flock))) - error = 0; - -out_putf: - fput(filp); -out: - return error; + locks_free_lock(fl); } -/* Apply the lock described by l to an open file descriptor. - * This implements both the F_SETLK and F_SETLKW commands of fcntl(). +/* Determine if lock sys_fl blocks lock caller_fl. Common functionality + * checks for overlapping locks and shared/exclusive status. */ -int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l) +static int locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) { - struct file *filp; - struct file_lock file_lock; - struct flock flock; - struct inode *inode; - int error; - - /* - * This might block, so we do it before checking the inode. - */ - error = -EFAULT; - if (copy_from_user(&flock, l, sizeof(flock))) - goto out; - - /* Get arguments and validate them ... - */ - - error = -EBADF; - filp = fget(fd); - if (!filp) - goto out; - - error = -EINVAL; - inode = filp->f_dentry->d_inode; - - /* Don't allow mandatory locks on files that may be memory mapped - * and shared. - */ - if (IS_MANDLOCK(inode) && - (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID) { - struct vm_area_struct *vma; - struct address_space *mapping = inode->i_mapping; - spin_lock(&mapping->i_shared_lock); - for(vma = mapping->i_mmap;vma;vma = vma->vm_next_share) { - if (!(vma->vm_flags & VM_MAYSHARE)) - continue; - spin_unlock(&mapping->i_shared_lock); - error = -EAGAIN; - goto out_putf; - } - spin_unlock(&mapping->i_shared_lock); - } + if (!locks_overlap(caller_fl, sys_fl)) + return (0); - error = -EINVAL; - if (!posix_make_lock(filp, &file_lock, &flock)) - goto out_putf; - - error = -EBADF; - switch (flock.l_type) { + switch (caller_fl->fl_type) { case F_RDLCK: - if (!(filp->f_mode & FMODE_READ)) - goto out_putf; - break; + return (sys_fl->fl_type == F_WRLCK); + case F_WRLCK: - if (!(filp->f_mode & FMODE_WRITE)) - goto out_putf; - break; - case F_UNLCK: - break; - case F_SHLCK: - case F_EXLCK: -#ifdef __sparc__ -/* warn a bit for now, but don't overdo it */ -{ - static int count = 0; - if (!count) { - count=1; - printk(KERN_WARNING - "fcntl_setlk() called by process %d (%s) with broken flock() emulation\n", - current->pid, current->comm); - } -} - if (!(filp->f_mode & 3)) - goto out_putf; - break; -#endif - default: - error = -EINVAL; - goto out_putf; - } + return (1); - if (filp->f_op->lock != NULL) { - error = filp->f_op->lock(filp, cmd, &file_lock); - if (error < 0) - goto out_putf; + default: + printk("locks_conflict(): impossible lock type - %d\n", + caller_fl->fl_type); + break; } - error = posix_lock_file(filp, &file_lock, cmd == F_SETLKW); - -out_putf: - fput(filp); -out: - return error; + return (0); /* This should never happen */ } -/* - * This function is called when the file is being removed - * from the task's fd array. +/* Determine if lock sys_fl blocks lock caller_fl. POSIX specific + * checking before calling the locks_conflict(). */ -void locks_remove_posix(struct file *filp, fl_owner_t owner) +static int posix_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) { - struct inode * inode = filp->f_dentry->d_inode; - struct file_lock file_lock, *fl; - struct file_lock **before; - - /* - * For POSIX locks we free all locks on this file for the given task. + /* POSIX locks owned by the same process do not conflict with + * each other. */ -repeat: - before = &inode->i_flock; - while ((fl = *before) != NULL) { - if ((fl->fl_flags & FL_POSIX) && fl->fl_owner == owner) { - int (*lock)(struct file *, int, struct file_lock *); - lock = filp->f_op->lock; - if (lock) { - file_lock = *fl; - file_lock.fl_type = F_UNLCK; - } - locks_delete_lock(before, 0); - if (lock) { - lock(filp, F_SETLK, &file_lock); - /* List may have changed: */ - goto repeat; - } - continue; - } - before = &fl->fl_next; - } + if (!(sys_fl->fl_flags & FL_POSIX) || + locks_same_owner(caller_fl, sys_fl)) + return (0); + + return (locks_conflict(caller_fl, sys_fl)); } -/* - * This function is called on the last close of an open file. +/* Determine if lock sys_fl blocks lock caller_fl. FLOCK specific + * checking before calling the locks_conflict(). */ -void locks_remove_flock(struct file *filp) +static int flock_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) { - struct inode * inode = filp->f_dentry->d_inode; - struct file_lock file_lock, *fl; - struct file_lock **before; + /* FLOCK locks referring to the same filp do not conflict with + * each other. + */ + if (!(sys_fl->fl_flags & FL_FLOCK) || + (caller_fl->fl_file == sys_fl->fl_file)) + return (0); -repeat: - before = &inode->i_flock; - while ((fl = *before) != NULL) { - if ((fl->fl_flags & FL_FLOCK) && fl->fl_file == filp) { - int (*lock)(struct file *, int, struct file_lock *); - lock = NULL; - if (filp->f_op) - lock = filp->f_op->lock; - if (lock) { - file_lock = *fl; - file_lock.fl_type = F_UNLCK; - } - locks_delete_lock(before, 0); - if (lock) { - lock(filp, F_SETLK, &file_lock); - /* List may have changed: */ - goto repeat; - } - continue; - } - before = &fl->fl_next; - } + return (locks_conflict(caller_fl, sys_fl)); } struct file_lock * @@ -552,6 +442,57 @@ posix_test_lock(struct file *filp, struct file_lock *fl) return (cfl); } +/* This function tests for deadlock condition before putting a process to + * sleep. The detection scheme is no longer recursive. Recursive was neat, + * but dangerous - we risked stack corruption if the lock data was bad, or + * if the recursion was too deep for any other reason. + * + * We rely on the fact that a task can only be on one lock's wait queue + * at a time. When we find blocked_task on a wait queue we can re-search + * with blocked_task equal to that queue's owner, until either blocked_task + * isn't found, or blocked_task is found on a queue owned by my_task. + * + * Note: the above assumption may not be true when handling lock requests + * from a broken NFS client. But broken NFS clients have a lot more to + * worry about than proper deadlock detection anyway... --okir + */ +static int posix_locks_deadlock(struct file_lock *caller_fl, + struct file_lock *block_fl) +{ + struct list_head *tmp; + void *caller_owner, *blocked_owner; + unsigned int caller_pid, blocked_pid; + + caller_owner = caller_fl->fl_owner; + caller_pid = caller_fl->fl_pid; + blocked_owner = block_fl->fl_owner; + blocked_pid = block_fl->fl_pid; + +next_task: + if (caller_owner == blocked_owner && caller_pid == blocked_pid) + return 1; + list_for_each(tmp, &file_lock_list) { + struct list_head *btmp; + struct file_lock *fl = list_entry(tmp, struct file_lock, fl_link); + if (fl->fl_owner == NULL || list_empty(&fl->fl_block)) + continue; + list_for_each(btmp, &fl->fl_block) { + struct file_lock *bfl = list_entry(tmp, struct file_lock, fl_block); + if (bfl->fl_owner == blocked_owner && + bfl->fl_pid == blocked_pid) { + if (fl->fl_owner == caller_owner && + fl->fl_pid == caller_pid) { + return (1); + } + blocked_owner = fl->fl_owner; + blocked_pid = fl->fl_pid; + goto next_task; + } + } + } + return 0; +} + int locks_mandatory_locked(struct inode *inode) { fl_owner_t owner = current->files; @@ -576,19 +517,16 @@ int locks_mandatory_area(int read_write, struct inode *inode, size_t count) { struct file_lock *fl; - struct file_lock tfl; + struct file_lock *new_fl = locks_alloc_lock(); int error; - memset(&tfl, 0, sizeof(tfl)); - - tfl.fl_file = filp; - tfl.fl_flags = FL_POSIX | FL_ACCESS; - tfl.fl_owner = current->files; - tfl.fl_pid = current->pid; - init_waitqueue_head(&tfl.fl_wait); - tfl.fl_type = (read_write == FLOCK_VERIFY_WRITE) ? F_WRLCK : F_RDLCK; - tfl.fl_start = offset; - tfl.fl_end = offset + count - 1; + new_fl->fl_owner = current->files; + new_fl->fl_pid = current->pid; + new_fl->fl_file = filp; + new_fl->fl_flags = FL_POSIX | FL_ACCESS; + new_fl->fl_type = (read_write == FLOCK_VERIFY_WRITE) ? F_WRLCK : F_RDLCK; + new_fl->fl_start = offset; + new_fl->fl_end = offset + count - 1; error = 0; lock_kernel(); @@ -606,7 +544,7 @@ repeat: /* Block for writes against a "read" lock, * and both reads and writes against a "write" lock. */ - if (posix_locks_conflict(&tfl, fl)) { + if (posix_locks_conflict(new_fl, fl)) { error = -EAGAIN; if (filp && (filp->f_flags & O_NONBLOCK)) break; @@ -614,12 +552,12 @@ repeat: if (signal_pending(current)) break; error = -EDEADLK; - if (posix_locks_deadlock(&tfl, fl)) + if (posix_locks_deadlock(new_fl, fl)) break; - locks_insert_block(fl, &tfl); - interruptible_sleep_on(&tfl.fl_wait); - locks_delete_block(fl, &tfl); + locks_insert_block(fl, new_fl); + interruptible_sleep_on(&new_fl->fl_wait); + locks_delete_block(new_fl); /* * If we've been sleeping someone might have @@ -631,202 +569,15 @@ repeat: } } unlock_kernel(); + locks_free_lock(new_fl); return error; } -/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX - * style lock. - */ -static int posix_make_lock(struct file *filp, struct file_lock *fl, - struct flock *l) -{ - loff_t start; - - memset(fl, 0, sizeof(*fl)); - - init_waitqueue_head(&fl->fl_wait); - fl->fl_flags = FL_POSIX; - - switch (l->l_type) { - case F_RDLCK: - case F_WRLCK: - case F_UNLCK: - fl->fl_type = l->l_type; - break; - default: - return (0); - } - - switch (l->l_whence) { - case 0: /*SEEK_SET*/ - start = 0; - break; - case 1: /*SEEK_CUR*/ - start = filp->f_pos; - break; - case 2: /*SEEK_END*/ - start = filp->f_dentry->d_inode->i_size; - break; - default: - return (0); - } - - if (((start += l->l_start) < 0) || (l->l_len < 0)) - return (0); - fl->fl_end = start + l->l_len - 1; - if (l->l_len > 0 && fl->fl_end < 0) - return (0); - fl->fl_start = start; /* we record the absolute position */ - if (l->l_len == 0) - fl->fl_end = OFFSET_MAX; - - fl->fl_file = filp; - fl->fl_owner = current->files; - fl->fl_pid = current->pid; - - return (1); -} - -/* Verify a call to flock() and fill in a file_lock structure with - * an appropriate FLOCK lock. - */ -static int flock_make_lock(struct file *filp, struct file_lock *fl, - unsigned int cmd) -{ - memset(fl, 0, sizeof(*fl)); - - init_waitqueue_head(&fl->fl_wait); - - switch (cmd & ~LOCK_NB) { - case LOCK_SH: - fl->fl_type = F_RDLCK; - break; - case LOCK_EX: - fl->fl_type = F_WRLCK; - break; - case LOCK_UN: - fl->fl_type = F_UNLCK; - break; - default: - return (0); - } - - fl->fl_flags = FL_FLOCK; - fl->fl_start = 0; - fl->fl_end = OFFSET_MAX; - fl->fl_file = filp; - fl->fl_owner = NULL; - - return (1); -} - -/* Determine if lock sys_fl blocks lock caller_fl. POSIX specific - * checking before calling the locks_conflict(). - */ -static int posix_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) -{ - /* POSIX locks owned by the same process do not conflict with - * each other. - */ - if (!(sys_fl->fl_flags & FL_POSIX) || - locks_same_owner(caller_fl, sys_fl)) - return (0); - - return (locks_conflict(caller_fl, sys_fl)); -} - -/* Determine if lock sys_fl blocks lock caller_fl. FLOCK specific - * checking before calling the locks_conflict(). - */ -static int flock_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) -{ - /* FLOCK locks referring to the same filp do not conflict with - * each other. - */ - if (!(sys_fl->fl_flags & FL_FLOCK) || - (caller_fl->fl_file == sys_fl->fl_file)) - return (0); - - return (locks_conflict(caller_fl, sys_fl)); -} - -/* Determine if lock sys_fl blocks lock caller_fl. Common functionality - * checks for overlapping locks and shared/exclusive status. - */ -static int locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) -{ - if (!locks_overlap(caller_fl, sys_fl)) - return (0); - - switch (caller_fl->fl_type) { - case F_RDLCK: - return (sys_fl->fl_type == F_WRLCK); - - case F_WRLCK: - return (1); - - default: - printk("locks_conflict(): impossible lock type - %d\n", - caller_fl->fl_type); - break; - } - return (0); /* This should never happen */ -} - -/* This function tests for deadlock condition before putting a process to - * sleep. The detection scheme is no longer recursive. Recursive was neat, - * but dangerous - we risked stack corruption if the lock data was bad, or - * if the recursion was too deep for any other reason. - * - * We rely on the fact that a task can only be on one lock's wait queue - * at a time. When we find blocked_task on a wait queue we can re-search - * with blocked_task equal to that queue's owner, until either blocked_task - * isn't found, or blocked_task is found on a queue owned by my_task. - * - * Note: the above assumption may not be true when handling lock requests - * from a broken NFS client. But broken NFS clients have a lot more to - * worry about than proper deadlock detection anyway... --okir - */ -static int posix_locks_deadlock(struct file_lock *caller_fl, - struct file_lock *block_fl) -{ - struct file_lock *fl; - struct file_lock *bfl; - void *caller_owner, *blocked_owner; - unsigned int caller_pid, blocked_pid; - - caller_owner = caller_fl->fl_owner; - caller_pid = caller_fl->fl_pid; - blocked_owner = block_fl->fl_owner; - blocked_pid = block_fl->fl_pid; - -next_task: - if (caller_owner == blocked_owner && caller_pid == blocked_pid) - return (1); - for (fl = file_lock_table; fl != NULL; fl = fl->fl_nextlink) { - if (fl->fl_owner == NULL || fl->fl_nextblock == NULL) - continue; - for (bfl = fl->fl_nextblock; bfl != fl; bfl = bfl->fl_nextblock) { - if (bfl->fl_owner == blocked_owner && - bfl->fl_pid == blocked_pid) { - if (fl->fl_owner == caller_owner && - fl->fl_pid == caller_pid) { - return (1); - } - blocked_owner = fl->fl_owner; - blocked_pid = fl->fl_pid; - goto next_task; - } - } - } - return (0); -} - /* Try to create a FLOCK lock on filp. We always insert new FLOCK locks at * the head of the list, but that's secret knowledge known only to the next * two functions. */ -static int flock_lock_file(struct file *filp, struct file_lock *caller, +static int flock_lock_file(struct file *filp, unsigned int lock_type, unsigned int wait) { struct file_lock *fl; @@ -834,14 +585,14 @@ static int flock_lock_file(struct file *filp, struct file_lock *caller, struct file_lock **before; struct inode * inode = filp->f_dentry->d_inode; int error, change; - int unlock = (caller->fl_type == F_UNLCK); + int unlock = (lock_type == F_UNLCK); /* * If we need a new lock, get it in advance to avoid races. */ if (!unlock) { error = -ENOLCK; - new_fl = locks_alloc_lock(caller); + new_fl = flock_make_lock(filp, lock_type); if (!new_fl) goto out; } @@ -851,8 +602,8 @@ search: change = 0; before = &inode->i_flock; while (((fl = *before) != NULL) && (fl->fl_flags & FL_FLOCK)) { - if (caller->fl_file == fl->fl_file) { - if (caller->fl_type == fl->fl_type) + if (filp == fl->fl_file) { + if (lock_type == fl->fl_type) goto out; change = 1; break; @@ -888,7 +639,7 @@ repeat: goto out; locks_insert_block(fl, new_fl); interruptible_sleep_on(&new_fl->fl_wait); - locks_delete_block(fl, new_fl); + locks_delete_block(new_fl); goto repeat; } locks_insert_lock(&inode->i_flock, new_fl); @@ -928,8 +679,8 @@ int posix_lock_file(struct file *filp, struct file_lock *caller, * We may need two file_lock structures for this operation, * so we get them in advance to avoid races. */ - new_fl = locks_empty_lock(); - new_fl2 = locks_empty_lock(); + new_fl = locks_alloc_lock(); + new_fl2 = locks_alloc_lock(); error = -ENOLCK; /* "no luck" */ if (!(new_fl && new_fl2)) goto out; @@ -952,7 +703,7 @@ int posix_lock_file(struct file *filp, struct file_lock *caller, goto out; locks_insert_block(fl, caller); interruptible_sleep_on(&caller->fl_wait); - locks_delete_block(fl, caller); + locks_delete_block(caller); goto repeat; } } @@ -1058,7 +809,7 @@ int posix_lock_file(struct file *filp, struct file_lock *caller, if (!added) { if (caller->fl_type == F_UNLCK) goto out; - locks_init_lock(new_fl, caller); + locks_copy_lock(new_fl, caller); locks_insert_lock(before, new_fl); new_fl = NULL; } @@ -1068,8 +819,9 @@ int posix_lock_file(struct file *filp, struct file_lock *caller, * so we have to use the second new lock (in this * case, even F_UNLCK may fail!). */ - left = locks_init_lock(new_fl2, right); + locks_copy_lock(new_fl2, right); locks_insert_lock(before, left); + left = new_fl2; new_fl2 = NULL; } right->fl_start = caller->fl_end + 1; @@ -1081,101 +833,288 @@ int posix_lock_file(struct file *filp, struct file_lock *caller, } out: /* - * Free any unused locks. (They haven't - * ever been used, so we use kfree().) + * Free any unused locks. */ if (new_fl) - kfree(new_fl); + locks_free_lock(new_fl); if (new_fl2) - kfree(new_fl2); + locks_free_lock(new_fl2); return error; } -/* - * Allocate an empty lock structure. We can use GFP_KERNEL now that - * all allocations are done in advance. +static inline int flock_translate_cmd(int cmd) { + switch (cmd &~ LOCK_NB) { + case LOCK_SH: + return F_RDLCK; + case LOCK_EX: + return F_WRLCK; + case LOCK_UN: + return F_UNLCK; + } + return -EINVAL; +} + +/* flock() system call entry point. Apply a FL_FLOCK style lock to + * an open file descriptor. */ -static struct file_lock *locks_empty_lock(void) +asmlinkage long sys_flock(unsigned int fd, unsigned int cmd) { - /* Okay, let's make a new file_lock structure... */ - return ((struct file_lock *) kmalloc(sizeof(struct file_lock), - GFP_KERNEL)); + struct file *filp; + int error, type; + + error = -EBADF; + filp = fget(fd); + if (!filp) + goto out; + + error = flock_translate_cmd(cmd); + if (error < 0) + goto out_putf; + type = error; + + error = -EBADF; + if ((type != F_UNLCK) && !(filp->f_mode & 3)) + goto out_putf; + + lock_kernel(); + error = flock_lock_file(filp, type, + (cmd & (LOCK_UN | LOCK_NB)) ? 0 : 1); + unlock_kernel(); + +out_putf: + fput(filp); +out: + return error; } -/* - * Initialize a new lock from an existing file_lock structure. +/* Report the first existing lock that would conflict with l. + * This implements the F_GETLK command of fcntl(). */ -static struct file_lock *locks_init_lock(struct file_lock *new, - struct file_lock *fl) +int fcntl_getlk(unsigned int fd, struct flock *l) { - if (new) { - memset(new, 0, sizeof(*new)); - new->fl_owner = fl->fl_owner; - new->fl_pid = fl->fl_pid; - init_waitqueue_head(&new->fl_wait); - new->fl_file = fl->fl_file; - new->fl_flags = fl->fl_flags; - new->fl_type = fl->fl_type; - new->fl_start = fl->fl_start; - new->fl_end = fl->fl_end; - new->fl_notify = fl->fl_notify; - new->fl_insert = fl->fl_insert; - new->fl_remove = fl->fl_remove; - new->fl_u = fl->fl_u; + struct file *filp; + struct file_lock *fl, *file_lock = locks_alloc_lock(); + struct flock flock; + int error; + + error = -EFAULT; + if (copy_from_user(&flock, l, sizeof(flock))) + goto out; + error = -EINVAL; + if ((flock.l_type != F_RDLCK) && (flock.l_type != F_WRLCK)) + goto out; + + error = -EBADF; + filp = fget(fd); + if (!filp) + goto out; + + if (!posix_make_lock(filp, file_lock, &flock)) + goto out_putf; + + if (filp->f_op->lock) { + error = filp->f_op->lock(filp, F_GETLK, file_lock); + if (error < 0) + goto out_putf; + else if (error == LOCK_USE_CLNT) + /* Bypass for NFS with no locking - 2.0.36 compat */ + fl = posix_test_lock(filp, file_lock); + else + fl = (file_lock->fl_type == F_UNLCK ? NULL : file_lock); + } else { + fl = posix_test_lock(filp, file_lock); } - return new; + + flock.l_type = F_UNLCK; + if (fl != NULL) { + flock.l_pid = fl->fl_pid; + flock.l_start = fl->fl_start; + flock.l_len = fl->fl_end == OFFSET_MAX ? 0 : + fl->fl_end - fl->fl_start + 1; + flock.l_whence = 0; + flock.l_type = fl->fl_type; + } + error = -EFAULT; + if (!copy_to_user(l, &flock, sizeof(flock))) + error = 0; + +out_putf: + fput(filp); +out: + locks_free_lock(file_lock); + return error; } -/* Insert file lock fl into an inode's lock list at the position indicated - * by pos. At the same time add the lock to the global file lock list. +/* Apply the lock described by l to an open file descriptor. + * This implements both the F_SETLK and F_SETLKW commands of fcntl(). */ -static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl) +int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l) { - fl->fl_nextlink = file_lock_table; - fl->fl_prevlink = NULL; - if (file_lock_table != NULL) - file_lock_table->fl_prevlink = fl; - file_lock_table = fl; - fl->fl_next = *pos; /* insert into file's list */ - *pos = fl; + struct file *filp; + struct file_lock *file_lock = locks_alloc_lock(); + struct flock flock; + struct inode *inode; + int error; - if (fl->fl_insert) - fl->fl_insert(fl); + /* + * This might block, so we do it before checking the inode. + */ + error = -EFAULT; + if (copy_from_user(&flock, l, sizeof(flock))) + goto out; - return; + /* Get arguments and validate them ... + */ + + error = -EBADF; + filp = fget(fd); + if (!filp) + goto out; + + error = -EINVAL; + inode = filp->f_dentry->d_inode; + + /* Don't allow mandatory locks on files that may be memory mapped + * and shared. + */ + if (IS_MANDLOCK(inode) && + (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID) { + struct vm_area_struct *vma; + struct address_space *mapping = inode->i_mapping; + spin_lock(&mapping->i_shared_lock); + for(vma = mapping->i_mmap;vma;vma = vma->vm_next_share) { + if (!(vma->vm_flags & VM_MAYSHARE)) + continue; + spin_unlock(&mapping->i_shared_lock); + error = -EAGAIN; + goto out_putf; + } + spin_unlock(&mapping->i_shared_lock); + } + + error = -EINVAL; + if (!posix_make_lock(filp, file_lock, &flock)) + goto out_putf; + + error = -EBADF; + switch (flock.l_type) { + case F_RDLCK: + if (!(filp->f_mode & FMODE_READ)) + goto out_putf; + break; + case F_WRLCK: + if (!(filp->f_mode & FMODE_WRITE)) + goto out_putf; + break; + case F_UNLCK: + break; + case F_SHLCK: + case F_EXLCK: +#ifdef __sparc__ +/* warn a bit for now, but don't overdo it */ +{ + static int count = 0; + if (!count) { + count=1; + printk(KERN_WARNING + "fcntl_setlk() called by process %d (%s) with broken flock() emulation\n", + current->pid, current->comm); + } } + if (!(filp->f_mode & 3)) + goto out_putf; + break; +#endif + default: + error = -EINVAL; + goto out_putf; + } -/* Delete a lock and free it. - * First remove our lock from the active lock lists. Then call - * locks_wake_up_blocks() to wake up processes that are blocked - * waiting for this lock. Finally free the lock structure. + if (filp->f_op->lock != NULL) { + error = filp->f_op->lock(filp, cmd, file_lock); + if (error < 0) + goto out_putf; + } + error = posix_lock_file(filp, file_lock, cmd == F_SETLKW); + +out_putf: + fput(filp); +out: + locks_free_lock(file_lock); + return error; +} + +/* + * This function is called when the file is being removed + * from the task's fd array. */ -static void locks_delete_lock(struct file_lock **thisfl_p, unsigned int wait) +void locks_remove_posix(struct file *filp, fl_owner_t owner) { - struct file_lock *thisfl; - struct file_lock *prevfl; - struct file_lock *nextfl; - - thisfl = *thisfl_p; - *thisfl_p = thisfl->fl_next; + struct inode * inode = filp->f_dentry->d_inode; + struct file_lock *fl; + struct file_lock **before; - prevfl = thisfl->fl_prevlink; - nextfl = thisfl->fl_nextlink; + /* + * For POSIX locks we free all locks on this file for the given task. + */ +repeat: + before = &inode->i_flock; + while ((fl = *before) != NULL) { + if ((fl->fl_flags & FL_POSIX) && fl->fl_owner == owner) { + locks_delete_lock(before, 0); + goto repeat; + } + before = &fl->fl_next; + } +} - if (nextfl != NULL) - nextfl->fl_prevlink = prevfl; +/* + * This function is called on the last close of an open file. + */ +void locks_remove_flock(struct file *filp) +{ + struct inode * inode = filp->f_dentry->d_inode; + struct file_lock file_lock, *fl; + struct file_lock **before; - if (prevfl != NULL) - prevfl->fl_nextlink = nextfl; - else - file_lock_table = nextfl; +repeat: + before = &inode->i_flock; + while ((fl = *before) != NULL) { + if ((fl->fl_flags & FL_FLOCK) && fl->fl_file == filp) { + int (*lock)(struct file *, int, struct file_lock *); + lock = NULL; + if (filp->f_op) + lock = filp->f_op->lock; + if (lock) { + file_lock = *fl; + file_lock.fl_type = F_UNLCK; + } + locks_delete_lock(before, 0); + if (lock) { + lock(filp, F_SETLK, &file_lock); + /* List may have changed: */ + goto repeat; + } + continue; + } + before = &fl->fl_next; + } +} - if (thisfl->fl_remove) - thisfl->fl_remove(thisfl); - - locks_wake_up_blocks(thisfl, wait); - locks_free_lock(thisfl); +/* The following two are for the benefit of lockd. + */ +void +posix_block_lock(struct file_lock *blocker, struct file_lock *waiter) +{ + lock_kernel(); + locks_insert_block(blocker, waiter); + unlock_kernel(); +} +void +posix_unblock_lock(struct file_lock *waiter) +{ + locks_delete_block(waiter); return; } @@ -1202,8 +1141,8 @@ static void lock_get_status(char* out, struct file_lock *fl, int id, char *pfx) kdevname(inode->i_dev), inode->i_ino, (long long)fl->fl_start, (long long)fl->fl_end); sprintf(out, "%08lx %08lx %08lx %08lx %08lx\n", - (long)fl, (long)fl->fl_prevlink, (long)fl->fl_nextlink, - (long)fl->fl_next, (long)fl->fl_nextblock); + (long)fl, (long)fl->fl_link.prev, (long)fl->fl_link.next, + (long)fl->fl_next, (long)fl->fl_block.next); } static void move_lock_status(char **p, off_t* pos, off_t offset) @@ -1230,35 +1169,46 @@ static void move_lock_status(char **p, off_t* pos, off_t offset) int get_locks_status(char *buffer, char **start, off_t offset, int length) { - struct file_lock *fl; - struct file_lock *bfl; + struct list_head *tmp; char *q = buffer; off_t pos = 0; - int i; + int i = 0; - for (fl = file_lock_table, i = 1; fl != NULL; fl = fl->fl_nextlink, i++) { - lock_get_status(q, fl, i, ""); + lock_kernel(); + list_for_each(tmp, &file_lock_list) { + struct list_head *btmp; + struct file_lock *fl = list_entry(tmp, struct file_lock, fl_link); + lock_get_status(q, fl, ++i, ""); move_lock_status(&q, &pos, offset); if(pos >= offset+length) goto done; - if ((bfl = fl->fl_nextblock) == NULL) - continue; - do { + list_for_each(btmp, &fl->fl_block) { + struct file_lock *bfl = list_entry(btmp, + struct file_lock, fl_block); lock_get_status(q, bfl, i, " ->"); move_lock_status(&q, &pos, offset); if(pos >= offset+length) goto done; - } while ((bfl = bfl->fl_nextblock) != fl); + } } done: + unlock_kernel(); *start = buffer; if(q-buffer < length) return (q-buffer); return length; } +static int __init filelock_init(void) +{ + filelock_cache = kmem_cache_create("file lock cache", + sizeof(struct file_lock), 0, 0, init_once, NULL); + if (!filelock_cache) + panic("cannot create file lock slab cache"); + return 0; +} - +module_init(filelock_init) diff --git a/fs/minix/bitmap.c b/fs/minix/bitmap.c index 075574876..ca30b7753 100644 --- a/fs/minix/bitmap.c +++ b/fs/minix/bitmap.c @@ -276,16 +276,13 @@ struct inode * minix_new_inode(const struct inode * dir, int * error) mark_inode_dirty(inode); unlock_super(sb); -printk("m_n_i: allocated inode "); if(DQUOT_ALLOC_INODE(sb, inode)) { -printk("fails quota test\n"); sb->dq_op->drop(inode); inode->i_nlink = 0; iput(inode); *error = -EDQUOT; return NULL; } -printk("is within quota\n"); *error = 0; return inode; diff --git a/fs/minix/fsync.c b/fs/minix/fsync.c index 30794d27a..96e1ffa86 100644 --- a/fs/minix/fsync.c +++ b/fs/minix/fsync.c @@ -329,7 +329,7 @@ static int V2_minix_sync_file(struct inode * inode, struct file * file) * NULL */ -int minix_sync_file(struct file * file, struct dentry *dentry) +int minix_sync_file(struct file * file, struct dentry *dentry, int datasync) { struct inode *inode = dentry->d_inode; diff --git a/fs/minix/inode.c b/fs/minix/inode.c index 6ddc278aa..fac903800 100644 --- a/fs/minix/inode.c +++ b/fs/minix/inode.c @@ -28,7 +28,7 @@ #include <linux/minix_fs.h> static void minix_read_inode(struct inode * inode); -static void minix_write_inode(struct inode * inode); +static void minix_write_inode(struct inode * inode, int wait); static int minix_statfs(struct super_block *sb, struct statfs *buf); static int minix_remount (struct super_block * sb, int * flags, char * data); @@ -1232,7 +1232,7 @@ static struct buffer_head *minix_update_inode(struct inode *inode) return V2_minix_update_inode(inode); } -static void minix_write_inode(struct inode * inode) +static void minix_write_inode(struct inode * inode, int wait) { struct buffer_head *bh; diff --git a/fs/namei.c b/fs/namei.c index 501000381..fcda2fd61 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -89,6 +89,12 @@ * if the pathname has trailing slashes - follow. * otherwise - don't follow. * (applied in that order). + * + * [Jun 2000 AV] Inconsistent behaviour of open() in case if flags==O_CREAT + * restored for 2.4. This is the last surviving part of old 4.2BSD bug. + * During the 2.4 we need to fix the userland stuff depending on it - + * hopefully we will be able to get rid of that wart in 2.5. So far only + * XEmacs seems to be relying on it... */ /* In order to reduce some races, while at the same time doing additional @@ -191,21 +197,35 @@ int permission(struct inode * inode,int mask) * < 0: (-i_writecount) vm_area_structs with VM_DENYWRITE set exist * > 0: (i_writecount) users are writing to the file. * - * WARNING: as soon as we will move get_write_access(), do_mmap() or - * prepare_binfmt() out of the big lock we will need a spinlock protecting - * the checks in all 3. For the time being it is not needed. + * Normally we operate on that counter with atomic_{inc,dec} and it's safe + * except for the cases where we don't hold i_writecount yet. Then we need to + * use {get,deny}_write_access() - these functions check the sign and refuse + * to do the change if sign is wrong. Exclusion between them is provided by + * spinlock (arbitration_lock) and I'll rip the second arsehole to the first + * who will try to move it in struct inode - just leave it here. */ +static spinlock_t arbitration_lock = SPIN_LOCK_UNLOCKED; int get_write_access(struct inode * inode) { - if (atomic_read(&inode->i_writecount) < 0) + spin_lock(&arbitration_lock); + if (atomic_read(&inode->i_writecount) < 0) { + spin_unlock(&arbitration_lock); return -ETXTBSY; + } atomic_inc(&inode->i_writecount); + spin_unlock(&arbitration_lock); return 0; } - -void put_write_access(struct inode * inode) +int deny_write_access(struct file * file) { - atomic_dec(&inode->i_writecount); + spin_lock(&arbitration_lock); + if (atomic_read(&file->f_dentry->d_inode->i_writecount) > 0) { + spin_unlock(&arbitration_lock); + return -ETXTBSY; + } + atomic_dec(&file->f_dentry->d_inode->i_writecount); + spin_unlock(&arbitration_lock); + return 0; } void path_release(struct nameidata *nd) @@ -337,7 +357,34 @@ int follow_down(struct vfsmount **mnt, struct dentry **dentry) { return __follow_down(mnt,dentry); } - + +static inline void follow_dotdot(struct nameidata *nd) +{ + while(1) { + struct vfsmount *parent; + struct dentry *dentry; + if (nd->dentry == current->fs->root && + nd->mnt == current->fs->rootmnt) { + break; + } + if (nd->dentry != nd->mnt->mnt_root) { + dentry = dget(nd->dentry->d_parent); + dput(nd->dentry); + nd->dentry = dentry; + break; + } + parent=nd->mnt->mnt_parent; + if (parent == nd->mnt) { + break; + } + mntget(parent); + dentry=dget(nd->mnt->mnt_mountpoint); + dput(nd->dentry); + nd->dentry = dentry; + mntput(nd->mnt); + nd->mnt = parent; + } +} /* * Name resolution. * @@ -403,19 +450,7 @@ int path_walk(const char * name, struct nameidata *nd) case 2: if (this.name[1] != '.') break; - while (1) { - if (nd->dentry == current->fs->root && - nd->mnt == current->fs->rootmnt) - break; - if (nd->dentry != nd->mnt->mnt_root) { - dentry = dget(nd->dentry->d_parent); - dput(nd->dentry); - nd->dentry = dentry; - break; - } - if (!__follow_up(&nd->mnt, &nd->dentry)) - break; - } + follow_dotdot(nd); inode = nd->dentry->d_inode; /* fallthrough */ case 1: @@ -483,19 +518,7 @@ last_component: case 2: if (this.name[1] != '.') break; - while (1) { - if (nd->dentry == current->fs->root && - nd->mnt == current->fs->rootmnt) - break; - if (nd->dentry != nd->mnt->mnt_root) { - dentry = dget(nd->dentry->d_parent); - dput(nd->dentry); - nd->dentry = dentry; - break; - } - if (!__follow_up(&nd->mnt, &nd->dentry)) - break; - } + follow_dotdot(nd); inode = nd->dentry->d_inode; /* fallthrough */ case 1: @@ -771,8 +794,6 @@ static inline int may_delete(struct inode *dir,struct dentry *victim, int isdir) int error; if (!victim->d_inode || victim->d_parent->d_inode != dir) return -ENOENT; - if (IS_DEADDIR(dir)) - return -ENOENT; error = permission(dir,MAY_WRITE | MAY_EXEC); if (error) return error; @@ -786,8 +807,6 @@ static inline int may_delete(struct inode *dir,struct dentry *victim, int isdir) return -ENOTDIR; if (IS_ROOT(victim)) return -EBUSY; - if (d_mountpoint(victim)) - return -EBUSY; } else if (S_ISDIR(victim->d_inode->i_mode)) return -EISDIR; return 0; @@ -872,83 +891,92 @@ int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd) int acc_mode, error = 0; struct inode *inode; struct dentry *dentry; + struct dentry *dir; + int count = 0; acc_mode = ACC_MODE(flag); + + /* + * The simplest case - just a plain lookup. + */ if (!(flag & O_CREAT)) { if (path_init(pathname, lookup_flags(flag), nd)) error = path_walk(pathname, nd); if (error) return error; - dentry = nd->dentry; - } else { - struct dentry *dir; + goto ok; + } - if (path_init(pathname, LOOKUP_PARENT, nd)) - error = path_walk(pathname, nd); + /* + * Create - we need to know the parent. + */ + if (path_init(pathname, LOOKUP_PARENT, nd)) + error = path_walk(pathname, nd); + if (error) + return error; + + /* + * We have the parent and last component. First of all, check + * that we are not asked to creat(2) an obvious directory - that + * will not do. + */ + error = -EISDIR; + if (nd->last_type != LAST_NORM || nd->last.name[nd->last.len]) + goto exit; + + dir = nd->dentry; + down(&dir->d_inode->i_sem); + dentry = lookup_hash(&nd->last, nd->dentry); + +do_last: + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) { + up(&dir->d_inode->i_sem); + goto exit; + } + + /* Negative dentry, just create the file */ + if (!dentry->d_inode) { + error = vfs_create(dir->d_inode, dentry, mode); + up(&dir->d_inode->i_sem); + dput(nd->dentry); + nd->dentry = dentry; if (error) - return error; - /* - * It's not obvious that open(".", O_CREAT, foo) should - * fail, but it's even less obvious that it should succeed. - * Since O_CREAT means an intention to create the thing and - * open(2) had never created directories, count it as caller's - * luserdom and let him sod off - -EISDIR it is. - */ - error = -EISDIR; - if (nd->last_type != LAST_NORM) - goto exit; - /* same for foo/ */ - if (nd->last.name[nd->last.len]) goto exit; + /* Don't check for write permission, don't truncate */ + acc_mode = 0; + flag &= ~O_TRUNC; + goto ok; + } - dir = nd->dentry; - down(&dir->d_inode->i_sem); + /* + * It already exists. + */ + up(&dir->d_inode->i_sem); - dentry = lookup_hash(&nd->last, nd->dentry); - error = PTR_ERR(dentry); - if (IS_ERR(dentry)) { - up(&dir->d_inode->i_sem); - goto exit; - } + error = -EEXIST; + if (flag & O_EXCL) + goto exit_dput; - if (dentry->d_inode) { - up(&dir->d_inode->i_sem); - error = -EEXIST; - if (flag & O_EXCL) - goto exit_dput; - if (dentry->d_inode->i_op && - dentry->d_inode->i_op->follow_link) { - /* - * With O_EXCL it would be -EEXIST. - * If symlink is a dangling one it's -ENOENT. - * Otherwise we open the object it points to. - */ - error = do_follow_link(dentry, nd); - dput(dentry); - if (error) - return error; - dentry = nd->dentry; - } else { - dput(nd->dentry); - nd->dentry = dentry; - } - error = -EISDIR; - if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) - goto exit; - } else { - error = vfs_create(dir->d_inode, dentry, mode); - up(&dir->d_inode->i_sem); - /* Don't check for write permission, don't truncate */ - acc_mode = 0; - flag &= ~O_TRUNC; - dput(nd->dentry); - nd->dentry = dentry; - if (error) - goto exit; - } + if (d_mountpoint(dentry)) { + error = -ELOOP; + if (flag & O_NOFOLLOW) + goto exit_dput; + do __follow_down(&nd->mnt,&dentry); while(d_mountpoint(dentry)); } + error = -ENOENT; + if (!dentry->d_inode) + goto exit_dput; + if (dentry->d_inode->i_op && dentry->d_inode->i_op->follow_link) + goto do_link; + dput(nd->dentry); + nd->dentry = dentry; + error = -EISDIR; + if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) + goto exit; +ok: error = -ENOENT; inode = dentry->d_inode; if (!inode) @@ -1023,6 +1051,47 @@ exit_dput: exit: path_release(nd); return error; + +do_link: + error = -ELOOP; + if (flag & O_NOFOLLOW) + goto exit_dput; + /* + * This is subtle. Instead of calling do_follow_link() we do the + * thing by hands. The reason is that this way we have zero link_count + * and path_walk() (called from ->follow_link) honoring LOOKUP_PARENT. + * After that we have the parent and last component, i.e. + * we are in the same situation as after the first path_walk(). + * Well, almost - if the last component is normal we get its copy + * stored in nd->last.name and we will have to putname() it when we + * are done. Procfs-like symlinks just set LAST_BIND. + */ + UPDATE_ATIME(dentry->d_inode); + error = dentry->d_inode->i_op->follow_link(dentry, nd); + dput(dentry); + if (error) + return error; + if (nd->last_type == LAST_BIND) { + dentry = nd->dentry; + goto ok; + } + error = -EISDIR; + if (nd->last_type != LAST_NORM) + goto exit; + if (nd->last.name[nd->last.len]) { + putname(nd->last.name); + goto exit; + } + if (count++==32) { + dentry = nd->dentry; + putname(nd->last.name); + goto ok; + } + dir = nd->dentry; + down(&dir->d_inode->i_sem); + dentry = lookup_hash(&nd->last, nd->dentry); + putname(nd->last.name); + goto do_last; } static struct dentry *lookup_create(struct nameidata *nd, int is_dir) @@ -1213,9 +1282,15 @@ int vfs_rmdir(struct inode *dir, struct dentry *dentry) double_down(&dir->i_zombie, &dentry->d_inode->i_zombie); d_unhash(dentry); - error = dir->i_op->rmdir(dir, dentry); - if (!error) - dentry->d_inode->i_flags |= S_DEAD; + if (IS_DEADDIR(dir)) + error = -ENOENT; + else if (d_mountpoint(dentry)) + error = -EBUSY; + else { + error = dir->i_op->rmdir(dir, dentry); + if (!error) + dentry->d_inode->i_flags |= S_DEAD; + } double_up(&dir->i_zombie, &dentry->d_inode->i_zombie); if (!error) d_delete(dentry); @@ -1275,9 +1350,13 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry) error = -EPERM; if (dir->i_op && dir->i_op->unlink) { DQUOT_INIT(dir); - error = dir->i_op->unlink(dir, dentry); - if (!error) - d_delete(dentry); + if (d_mountpoint(dentry)) + error = -EBUSY; + else { + error = dir->i_op->unlink(dir, dentry); + if (!error) + d_delete(dentry); + } } } up(&dir->i_zombie); @@ -1555,7 +1634,12 @@ int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry, } else double_down(&old_dir->i_zombie, &new_dir->i_zombie); - error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry); + if (IS_DEADDIR(old_dir)||IS_DEADDIR(new_dir)) + error = -ENOENT; + else if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry)) + error = -EBUSY; + else + error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry); if (target) { if (!error) target->i_flags |= S_DEAD; @@ -1603,7 +1687,10 @@ int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry, DQUOT_INIT(old_dir); DQUOT_INIT(new_dir); double_down(&old_dir->i_zombie, &new_dir->i_zombie); - error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry); + if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry)) + error = -EBUSY; + else + error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry); double_up(&old_dir->i_zombie, &new_dir->i_zombie); if (error) return error; @@ -1734,6 +1821,8 @@ out: static inline int __vfs_follow_link(struct nameidata *nd, const char *link) { + int res = 0; + char *name; if (IS_ERR(link)) goto fail; @@ -1741,10 +1830,25 @@ __vfs_follow_link(struct nameidata *nd, const char *link) path_release(nd); if (!walk_init_root(link, nd)) /* weird __emul_prefix() stuff did it */ - return 0; + goto out; } - return path_walk(link, nd); - + res = path_walk(link, nd); +out: + if (current->link_count || res || nd->last_type!=LAST_NORM) + return res; + /* + * If it is an iterative symlinks resolution in open_namei() we + * have to copy the last component. And all that crap because of + * bloody create() on broken symlinks. Furrfu... + */ + name = __getname(); + if (IS_ERR(name)) + goto fail_name; + strcpy(name, nd->last.name); + nd->last.name = name; + return 0; +fail_name: + link = name; fail: path_release(nd); return PTR_ERR(link); diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c index 55daea198..11694e79b 100644 --- a/fs/ncpfs/dir.c +++ b/fs/ncpfs/dir.c @@ -973,7 +973,7 @@ static int ncp_unlink(struct inode *dir, struct dentry *dentry) /* * Check whether to close the file ... */ - if (inode && NCP_FINFO(inode)->opened) { + if (inode) { PPRINTK("ncp_unlink: closing file\n"); ncp_make_closed(inode); } @@ -982,7 +982,7 @@ static int ncp_unlink(struct inode *dir, struct dentry *dentry) #ifdef CONFIG_NCPFS_STRONG /* 9C is Invalid path.. It should be 8F, 90 - read only, but it is not :-( */ - if (error == 0x9C && server->m.flags & NCP_MOUNT_STRONG) { /* R/O */ + if ((error == 0x9C || error == 0x90) && server->m.flags & NCP_MOUNT_STRONG) { /* R/O */ error = ncp_force_unlink(dir, dentry); } #endif @@ -1051,7 +1051,7 @@ static int ncp_rename(struct inode *old_dir, struct dentry *old_dentry, error = ncp_ren_or_mov_file_or_subdir(server, old_dir, __old_name, new_dir, __new_name); #ifdef CONFIG_NCPFS_STRONG - if ((error == 0x90 || error == -EACCES) && + if ((error == 0x90 || error == 0x8B || error == -EACCES) && server->m.flags & NCP_MOUNT_STRONG) { /* RO */ error = ncp_force_rename(old_dir, old_dentry, __old_name, new_dir, new_dentry, __new_name); diff --git a/fs/ncpfs/file.c b/fs/ncpfs/file.c index 6f8fd2d63..3442c3f9f 100644 --- a/fs/ncpfs/file.c +++ b/fs/ncpfs/file.c @@ -26,7 +26,7 @@ static inline unsigned int min(unsigned int a, unsigned int b) return a < b ? a : b; } -static int ncp_fsync(struct file *file, struct dentry *dentry) +static int ncp_fsync(struct file *file, struct dentry *dentry, int datasync) { return 0; } @@ -46,12 +46,12 @@ int ncp_make_open(struct inode *inode, int right) } DPRINTK("ncp_make_open: opened=%d, volume # %u, dir entry # %u\n", - NCP_FINFO(inode)->opened, + atomic_read(&NCP_FINFO(inode)->opened), NCP_FINFO(inode)->volNumber, NCP_FINFO(inode)->dirEntNum); error = -EACCES; - lock_super(inode->i_sb); - if (!NCP_FINFO(inode)->opened) { + down(&NCP_FINFO(inode)->open_sem); + if (!atomic_read(&NCP_FINFO(inode)->opened)) { struct ncp_entry_info finfo; int result; @@ -88,15 +88,18 @@ int ncp_make_open(struct inode *inode, int right) */ update: ncp_update_inode(inode, &finfo); + atomic_set(&NCP_FINFO(inode)->opened, 1); } access = NCP_FINFO(inode)->access; PPRINTK("ncp_make_open: file open, access=%x\n", access); - if (access == right || access == O_RDWR) + if (access == right || access == O_RDWR) { + atomic_inc(&NCP_FINFO(inode)->opened); error = 0; + } out_unlock: - unlock_super(inode->i_sb); + up(&NCP_FINFO(inode)->open_sem); out: return error; } @@ -153,7 +156,7 @@ ncp_file_read(struct file *file, char *buf, size_t count, loff_t *ppos) freelen = ncp_read_bounce_size(bufsize); freepage = kmalloc(freelen, GFP_NFS); if (!freepage) - goto out; + goto outrel; error = 0; /* First read in as much as possible for each bufsize. */ while (already_read < count) { @@ -166,9 +169,8 @@ ncp_file_read(struct file *file, char *buf, size_t count, loff_t *ppos) pos, to_read, buf, &read_this_time, freepage, freelen); if (error) { - kfree(freepage); - error = -EIO; /* This is not exact, i know.. */ - goto out; + error = -EIO; /* NW errno -> Linux errno */ + break; } pos += read_this_time; buf += read_this_time; @@ -188,6 +190,8 @@ ncp_file_read(struct file *file, char *buf, size_t count, loff_t *ppos) DPRINTK("ncp_file_read: exit %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); +outrel: + ncp_inode_close(inode); out: return already_read ? already_read : error; } @@ -236,8 +240,10 @@ ncp_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) already_written = 0; bouncebuffer = kmalloc(bufsize, GFP_NFS); - if (!bouncebuffer) - return -EIO; /* -ENOMEM */ + if (!bouncebuffer) { + errno = -EIO; /* -ENOMEM */ + goto outrel; + } while (already_written < count) { int written_this_time; size_t to_write = min(bufsize - (pos % bufsize), @@ -271,15 +277,15 @@ ncp_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) } DPRINTK("ncp_file_write: exit %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); +outrel: + ncp_inode_close(inode); out: return already_written ? already_written : errno; } static int ncp_release(struct inode *inode, struct file *file) { - if (NCP_FINFO(inode)->opened) { - if (ncp_make_closed(inode)) { - DPRINTK("ncp_release: failed to close\n"); - } + if (ncp_make_closed(inode)) { + DPRINTK("ncp_release: failed to close\n"); } return 0; } diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c index e885aed47..b6104831e 100644 --- a/fs/ncpfs/inode.c +++ b/fs/ncpfs/inode.c @@ -61,7 +61,6 @@ void ncp_update_inode(struct inode *inode, struct ncp_entry_info *nwinfo) #ifdef CONFIG_NCPFS_STRONG NCP_FINFO(inode)->nwattr = nwinfo->i.attributes; #endif - NCP_FINFO(inode)->opened = nwinfo->opened; NCP_FINFO(inode)->access = nwinfo->access; NCP_FINFO(inode)->server_file_handle = nwinfo->server_file_handle; memcpy(NCP_FINFO(inode)->file_handle, nwinfo->file_handle, @@ -76,7 +75,7 @@ void ncp_update_inode2(struct inode* inode, struct ncp_entry_info *nwinfo) struct nw_info_struct *nwi = &nwinfo->i; struct ncp_server *server = NCP_SERVER(inode); - if (!NCP_FINFO(inode)->opened) { + if (!atomic_read(&NCP_FINFO(inode)->opened)) { #ifdef CONFIG_NCPFS_STRONG NCP_FINFO(inode)->nwattr = nwi->attributes; #endif @@ -216,6 +215,9 @@ ncp_iget(struct super_block *sb, struct ncp_entry_info *info) inode = get_empty_inode(); if (inode) { + init_MUTEX(&NCP_FINFO(inode)->open_sem); + atomic_set(&NCP_FINFO(inode)->opened, info->opened); + inode->i_sb = sb; inode->i_dev = sb->s_dev; inode->i_ino = info->ino; @@ -245,7 +247,7 @@ ncp_delete_inode(struct inode *inode) DDPRINTK("ncp_delete_inode: put directory %ld\n", inode->i_ino); } - if (NCP_FINFO(inode)->opened && ncp_make_closed(inode) != 0) { + if (ncp_make_closed(inode) != 0) { /* We can't do anything but complain. */ printk(KERN_ERR "ncp_delete_inode: could not close\n"); } @@ -259,7 +261,6 @@ ncp_read_super(struct super_block *sb, void *raw_data, int silent) struct ncp_server *server; struct file *ncp_filp; struct inode *root_inode; - kdev_t dev = sb->s_dev; int error; #ifdef CONFIG_NCPFS_PACKET_SIGNING int options; @@ -318,7 +319,6 @@ ncp_read_super(struct super_block *sb, void *raw_data, int silent) sb->s_blocksize = 1024; /* Eh... Is this correct? */ sb->s_blocksize_bits = 10; sb->s_magic = NCP_SUPER_MAGIC; - sb->s_dev = dev; sb->s_op = &ncp_sops; server = NCP_SBP(sb); @@ -676,6 +676,7 @@ int ncp_notify_change(struct dentry *dentry, struct iattr *attr) /* According to ndir, the changes only take effect after closing the file */ + ncp_inode_close(inode); result = ncp_make_closed(inode); if (!result) vmtruncate(inode, attr->ia_size); diff --git a/fs/ncpfs/ioctl.c b/fs/ncpfs/ioctl.c index 26c95fc8f..24e616396 100644 --- a/fs/ncpfs/ioctl.c +++ b/fs/ncpfs/ioctl.c @@ -335,18 +335,12 @@ int ncp_ioctl(struct inode *inode, struct file *filp, { return result; } + result = -EIO; if (!ncp_conn_valid(server)) - { - return -EIO; - } + goto outrel; + result = -EISDIR; if (!S_ISREG(inode->i_mode)) - { - return -EISDIR; - } - if (!NCP_FINFO(inode)->opened) - { - return -EBADFD; - } + goto outrel; if (rqdata.cmd == NCP_LOCK_CLEAR) { result = ncp_ClearPhysicalRecord(NCP_SERVER(inode), @@ -373,6 +367,8 @@ int ncp_ioctl(struct inode *inode, struct file *filp, rqdata.timeout); if (result > 0) result = -EAGAIN; } +outrel: + ncp_inode_close(inode); return result; } #endif /* CONFIG_NCPFS_IOCTL_LOCKING */ diff --git a/fs/ncpfs/mmap.c b/fs/ncpfs/mmap.c index 752ae1e1e..08d28d895 100644 --- a/fs/ncpfs/mmap.c +++ b/fs/ncpfs/mmap.c @@ -82,6 +82,7 @@ static struct page* ncp_file_mmap_nopage(struct vm_area_struct *area, break; } } + ncp_inode_close(inode); } diff --git a/fs/ncpfs/ncplib_kernel.c b/fs/ncpfs/ncplib_kernel.c index 73afd107a..0353882b9 100644 --- a/fs/ncpfs/ncplib_kernel.c +++ b/fs/ncpfs/ncplib_kernel.c @@ -221,20 +221,23 @@ ncp_close_file(struct ncp_server *server, const char *file_id) return result; } -/* - * Called with the superblock locked. - */ int ncp_make_closed(struct inode *inode) { int err; - NCP_FINFO(inode)->opened = 0; - err = ncp_close_file(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle); - if (!err) - PPRINTK("ncp_make_closed: volnum=%d, dirent=%u, error=%d\n", - NCP_FINFO(inode)->volNumber, - NCP_FINFO(inode)->dirEntNum, err); + err = 0; + down(&NCP_FINFO(inode)->open_sem); + if (atomic_read(&NCP_FINFO(inode)->opened) == 1) { + atomic_set(&NCP_FINFO(inode)->opened, 0); + err = ncp_close_file(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle); + + if (!err) + PPRINTK("ncp_make_closed: volnum=%d, dirent=%u, error=%d\n", + NCP_FINFO(inode)->volNumber, + NCP_FINFO(inode)->dirEntNum, err); + } + up(&NCP_FINFO(inode)->open_sem); return err; } @@ -613,7 +616,8 @@ int ncp_open_create_file_or_subdir(struct ncp_server *server, if ((result = ncp_request(server, 87)) != 0) goto out; - target->opened = 1; + if (!(create_attributes & aDIR)) + target->opened = 1; target->server_file_handle = ncp_reply_dword(server, 0); target->open_create_action = ncp_reply_byte(server, 4); diff --git a/fs/ncpfs/ncplib_kernel.h b/fs/ncpfs/ncplib_kernel.h index 8b33a5c2e..31797a3c3 100644 --- a/fs/ncpfs/ncplib_kernel.h +++ b/fs/ncpfs/ncplib_kernel.h @@ -57,6 +57,10 @@ int ncp_read_kernel(struct ncp_server *, const char *, __u32, __u16, int ncp_write_kernel(struct ncp_server *, const char *, __u32, __u16, const char *, int *); +static inline void ncp_inode_close(struct inode *inode) { + atomic_dec(&NCP_FINFO(inode)->opened); +} + int ncp_obtain_info(struct ncp_server *server, struct inode *, char *, struct nw_info_struct *target); int ncp_lookup_volume(struct ncp_server *, char *, struct nw_info_struct *); diff --git a/fs/ncpfs/symlink.c b/fs/ncpfs/symlink.c index 46925eb6d..0962593da 100644 --- a/fs/ncpfs/symlink.c +++ b/fs/ncpfs/symlink.c @@ -50,10 +50,6 @@ static int ncp_symlink_readpage(struct file *file, struct page *page) char *link; char *buf = (char*)kmap(page); - error = -EIO; - if (ncp_make_open(inode,O_RDONLY)) - goto fail; - error = -ENOMEM; for (cnt = 0; (link=(char *)kmalloc(NCP_MAX_SYMLINK_SIZE, GFP_NFS))==NULL; cnt++) { if (cnt > 10) @@ -61,20 +57,22 @@ static int ncp_symlink_readpage(struct file *file, struct page *page) schedule(); } + if (ncp_make_open(inode,O_RDONLY)) + goto failEIO; + error=ncp_read_kernel(NCP_SERVER(inode),NCP_FINFO(inode)->file_handle, 0,NCP_MAX_SYMLINK_SIZE,link,&length); - if (error) { - kfree(link); - goto fail; - } + ncp_inode_close(inode); + /* Close file handle if no other users... */ + ncp_make_closed(inode); + if (error) + goto failEIO; + if (length<NCP_MIN_SYMLINK_SIZE || ((__u32 *)link)[0]!=NCP_SYMLINK_MAGIC0 || - ((__u32 *)link)[1]!=NCP_SYMLINK_MAGIC1) { - error = -EIO; - kfree(link); - goto fail; - } + ((__u32 *)link)[1]!=NCP_SYMLINK_MAGIC1) + goto failEIO; len = NCP_MAX_SYMLINK_SIZE; error = ncp_vol2io(NCP_SERVER(inode), buf, &len, link+8, length-8, 0); @@ -86,6 +84,9 @@ static int ncp_symlink_readpage(struct file *file, struct page *page) UnlockPage(page); return 0; +failEIO: + error = -EIO; + kfree(link); fail: SetPageError(page); kunmap(page); @@ -120,13 +121,15 @@ int ncp_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { if ((link=(char *)kmalloc(length+9,GFP_NFS))==NULL) return -ENOMEM; - if (ncp_create_new(dir,dentry,0,aSHARED|aHIDDEN)) { - kfree(link); - return -EIO; - } + err = -EIO; + if (ncp_create_new(dir,dentry,0,aSHARED|aHIDDEN)) + goto failfree; inode=dentry->d_inode; + if (ncp_make_open(inode, O_WRONLY)) + goto failfree; + ((__u32 *)link)[0]=NCP_SYMLINK_MAGIC0; ((__u32 *)link)[1]=NCP_SYMLINK_MAGIC1; @@ -134,19 +137,26 @@ int ncp_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { symlink can point out of ncp filesystem */ length += 1; err = ncp_io2vol(NCP_SERVER(inode),link+8,&length,symname,length-1,0); - if (err) { - kfree(link); - return err; - } + if (err) + goto fail; if(ncp_write_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, 0, length+8, link, &i) || i!=length+8) { - kfree(link); - return -EIO; + err = -EIO; + goto fail; } + ncp_inode_close(inode); + ncp_make_closed(inode); kfree(link); return 0; + +fail: + ncp_inode_close(inode); + ncp_make_closed(inode); +failfree: + kfree(link); + return err; } #endif diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 62b37c8cf..06f067eea 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -38,7 +38,7 @@ static int nfs_file_mmap(struct file *, struct vm_area_struct *); static ssize_t nfs_file_read(struct file *, char *, size_t, loff_t *); static ssize_t nfs_file_write(struct file *, const char *, size_t, loff_t *); static int nfs_file_flush(struct file *); -static int nfs_fsync(struct file *, struct dentry *dentry); +static int nfs_fsync(struct file *, struct dentry *dentry, int datasync); struct file_operations nfs_file_operations = { read: nfs_file_read, @@ -123,7 +123,7 @@ nfs_file_mmap(struct file * file, struct vm_area_struct * vma) * whether any write errors occurred for this process. */ static int -nfs_fsync(struct file *file, struct dentry *dentry) +nfs_fsync(struct file *file, struct dentry *dentry, int datasync) { struct inode *inode = dentry->d_inode; int status; diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index dee52dd8a..df2532048 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -9,8 +9,6 @@ * creates a client control block and adds it to the hash * table. Then, you call NFSCTL_EXPORT for each fs. * - * You cannot currently read the export information from the - * kernel. It would be nice to have a /proc file though. * * Copyright (C) 1995, 1996 Olaf Kirch, <okir@monad.swb.de> */ @@ -388,12 +386,10 @@ exp_rootfh(struct svc_client *clp, kdev_t dev, ino_t ino, err = -EPERM; if (path) { - err = 0; - if (path_init(path, LOOKUP_POSITIVE, &nd)) - err = path_walk(path, &nd); - if (err) { + if (path_init(path, LOOKUP_POSITIVE, &nd) && + path_walk(path, &nd)) { printk("nfsd: exp_rootfh path not found %s", path); - return -EPERM; + return err; } dev = nd.dentry->d_inode->i_dev; ino = nd.dentry->d_inode->i_ino; @@ -438,7 +434,8 @@ exp_rootfh(struct svc_client *clp, kdev_t dev, ino_t ino, fh_put(&fh); out: - path_release(&nd); + if (path) + path_release(&nd); return err; } diff --git a/fs/nfsd/nfscache.c b/fs/nfsd/nfscache.c index 357a297f6..f5795583b 100644 --- a/fs/nfsd/nfscache.c +++ b/fs/nfsd/nfscache.c @@ -174,8 +174,9 @@ int nfsd_cache_lookup(struct svc_rqst *rqstp, int type) { struct svc_cacherep *rh, *rp; - struct svc_client *clp = rqstp->rq_client; u32 xid = rqstp->rq_xid, + proto = rqstp->rq_prot, + vers = rqstp->rq_vers, proc = rqstp->rq_proc; unsigned long age; @@ -189,7 +190,9 @@ nfsd_cache_lookup(struct svc_rqst *rqstp, int type) while ((rp = rp->c_hash_next) != rh) { if (rp->c_state != RC_UNUSED && xid == rp->c_xid && proc == rp->c_proc && - exp_checkaddr(clp, rp->c_client)) { + proto == rp->c_prot && vers == rp->c_vers && + time_before(jiffies, rp->c_timestamp + 120*HZ) && + memcmp((char*)&rqstp->rq_addr, (char*)&rp->c_addr, rqstp->rq_addrlen)==0) { nfsdstats.rchits++; goto found_entry; } @@ -226,7 +229,11 @@ nfsd_cache_lookup(struct svc_rqst *rqstp, int type) rp->c_state = RC_INPROG; rp->c_xid = xid; rp->c_proc = proc; - rp->c_client = rqstp->rq_addr.sin_addr; + rp->c_addr = rqstp->rq_addr; + rp->c_prot = proto; + rp->c_vers = vers; + rp->c_timestamp = jiffies; + hash_refile(rp); /* release any buffer */ diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index c4e456185..913cbf5f8 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -5,7 +5,6 @@ * * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> */ -#define NFS_GETFH_NEW #include <linux/config.h> #include <linux/module.h> diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index 85a98c874..78f399bd3 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -495,17 +495,15 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) dprintk("nfsd: fh_verify(%s)\n", SVCFH_fmt(fhp)); - if (!fhp->fh_dverified) { + if (!fhp->fh_dentry) { kdev_t xdev; ino_t xino; __u32 *datap=NULL; int data_left = fh->fh_size/4; int nfsdev; error = nfserr_stale; -#if CONFIG_NFSD_V3 if (rqstp->rq_vers == 3) error = nfserr_badhandle; -#endif if (fh->fh_version == 1) { datap = fh->fh_auth; @@ -562,10 +560,8 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) * Look up the dentry using the NFS file handle. */ error = nfserr_stale; -#if CONFIG_NFSD_V3 if (rqstp->rq_vers == 3) error = nfserr_badhandle; -#endif if (fh->fh_version == 1) { /* if fileid_type != 0, and super_operations provide fh_to_dentry lookup, @@ -611,7 +607,6 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) fhp->fh_dentry = dentry; fhp->fh_export = exp; - fhp->fh_dverified = 1; nfsd_nr_verified++; } else { /* just rechecking permissions @@ -731,7 +726,7 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry) parent->d_name.name, dentry->d_name.name, (inode ? inode->i_ino : 0)); - if (fhp->fh_dverified || fhp->fh_locked || fhp->fh_dentry) { + if (fhp->fh_locked || fhp->fh_dentry) { printk(KERN_ERR "fh_compose: fh %s/%s not initialized!\n", parent->d_name.name, dentry->d_name.name); } @@ -757,8 +752,6 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry) fhp->fh_handle.fh_size = (datap-fhp->fh_handle.fh_auth+1)*4; - /* We stuck it there, we know it's good. */ - fhp->fh_dverified = 1; nfsd_nr_verified++; if (fhp->fh_handle.fh_fileid_type == 255) return nfserr_opnotsupp; @@ -775,7 +768,7 @@ fh_update(struct svc_fh *fhp) struct dentry *dentry; __u32 *datap; - if (!fhp->fh_dverified) + if (!fhp->fh_dentry) goto out_bad; dentry = fhp->fh_dentry; @@ -811,10 +804,9 @@ void fh_put(struct svc_fh *fhp) { struct dentry * dentry = fhp->fh_dentry; - if (fhp->fh_dverified) { + if (dentry) { fh_unlock(fhp); fhp->fh_dentry = NULL; - fhp->fh_dverified = 0; dput(dentry); nfsd_nr_put++; } diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c index 763970736..b5057d57b 100644 --- a/fs/nfsd/nfsproc.c +++ b/fs/nfsd/nfsproc.c @@ -239,7 +239,7 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp, * whether the file exists or not. Time to bail ... */ nfserr = nfserr_acces; - if (!newfhp->fh_dverified) { + if (!newfhp->fh_dentry) { printk(KERN_WARNING "nfsd_proc_create: file handle not verified\n"); goto out_unlock; @@ -415,7 +415,7 @@ nfsd_proc_mkdir(struct svc_rqst *rqstp, struct nfsd_createargs *argp, dprintk("nfsd: MKDIR %s %s\n", SVCFH_fmt(&argp->fh), argp->name); - if (resp->fh.fh_dverified) { + if (resp->fh.fh_dentry) { printk(KERN_WARNING "nfsd_proc_mkdir: response already verified??\n"); } diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index fb3b32f8d..9a4d12a7d 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -275,7 +275,6 @@ nfsd_dispatch(struct svc_rqst *rqstp, u32 *statp) /* Encode result. * For NFSv2, additional info is never returned in case of an error. */ -#ifdef CONFIG_NFSD_V3 if (!(nfserr && rqstp->rq_vers == 2)) { xdr = proc->pc_encode; if (xdr && !xdr(rqstp, rqstp->rq_resbuf.buf, rqstp->rq_resp)) { @@ -286,17 +285,6 @@ nfsd_dispatch(struct svc_rqst *rqstp, u32 *statp) return 1; } } -#else - xdr = proc->pc_encode; - if (!nfserr && xdr - && !xdr(rqstp, rqstp->rq_resbuf.buf, rqstp->rq_resp)) { - /* Failed to encode result. Release cache entry */ - dprintk("nfsd: failed to encode result!\n"); - nfsd_cache_update(rqstp, RC_NOCACHE, NULL); - *statp = rpc_system_err; - return 1; - } -#endif /* CONFIG_NFSD_V3 */ /* Store reply in cache. */ nfsd_cache_update(rqstp, proc->pc_cachetype, statp + 1); diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 40f1ab85a..7a144d707 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -165,6 +165,7 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, dentry = mounts; } else dput(mounts); + mntput(mnt); } } /* @@ -253,8 +254,10 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap) goto out_nfserr; err = locks_verify_truncate(inode, NULL, iap->ia_size); - if (err) + if (err) { + put_write_access(inode); goto out_nfserr; + } DQUOT_INIT(inode); } @@ -314,11 +317,8 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap) if (err) goto out_nfserr; if (EX_ISSYNC(fhp->fh_export)) - write_inode_now(inode); + write_inode_now(inode, 0); err = 0; - - /* Don't unlock inode; the nfssvc_release functions are supposed - * to do this. */ out: return err; @@ -413,7 +413,7 @@ nfsd_access(struct svc_rqst *rqstp, struct svc_fh *fhp, u32 *access) out: return error; } -#endif +#endif /* CONFIG_NFSD_V3 */ @@ -512,7 +512,7 @@ nfsd_sync(struct file *filp) { dprintk("nfsd: sync file %s\n", filp->f_dentry->d_name.name); down(&filp->f_dentry->d_inode->i_sem); - filp->f_op->fsync(filp, filp->f_dentry); + filp->f_op->fsync(filp, filp->f_dentry, 0); up(&filp->f_dentry->d_inode->i_sem); } @@ -520,10 +520,10 @@ void nfsd_sync_dir(struct dentry *dp) { struct inode *inode = dp->d_inode; - int (*fsync) (struct file *, struct dentry *); + int (*fsync) (struct file *, struct dentry *, int); if (inode->i_fop && (fsync = inode->i_fop->fsync)) { - fsync(NULL, dp); + fsync(NULL, dp, 0); } } @@ -598,7 +598,6 @@ nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, oldfs = get_fs(); set_fs(KERNEL_DS); err = file.f_op->read(&file, buf, *count, &file.f_pos); set_fs(oldfs); - nfsdstats.io_read += *count; /* Write back readahead params */ if (ra != NULL) { @@ -614,6 +613,7 @@ nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, } if (err >= 0) { + nfsdstats.io_read += err; *count = err; err = 0; } else @@ -665,19 +665,16 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, * When gathered writes have been configured for this volume, * flushing the data to disk is handled separately below. */ -#ifdef CONFIG_NFSD_V3 + if (file.f_op->fsync == 0) {/* COMMIT3 cannot work */ stable = 2; *stablep = 2; /* FILE_SYNC */ } + if (!EX_ISSYNC(exp)) stable = 0; if (stable && !EX_WGATHER(exp)) file.f_flags |= O_SYNC; -#else - if ((stable || (stable = EX_ISSYNC(exp))) && !EX_WGATHER(exp)) - file.f_flags |= O_SYNC; -#endif /* CONFIG_NFSD_V3 */ file.f_pos = offset; /* set write offset */ @@ -692,7 +689,8 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, #else err = file.f_op->write(&file, buf, cnt, &file.f_pos); #endif - nfsdstats.io_write += cnt; + if (err >= 0) + nfsdstats.io_write += cnt; set_fs(oldfs); /* clear setuid/setgid flag after write */ @@ -734,7 +732,9 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, #else dprintk("nfsd: write defer %d\n", current->pid); /* FIXME: Olaf commented this out [gam3] */ + set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((HZ+99)/100); + current->state = TASK_RUNNING; dprintk("nfsd: write resume %d\n", current->pid); #endif } @@ -743,7 +743,9 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, dprintk("nfsd: write sync %d\n", current->pid); nfsd_sync(&file); } +#if 0 wake_up(&inode->i_wait); +#endif last_ino = inode->i_ino; last_dev = inode->i_dev; } @@ -762,11 +764,12 @@ out: #ifdef CONFIG_NFSD_V3 /* - * Commit all pendig writes to stable storage. - * Strictly speaking, we could sync just indicated the file region here, + * Commit all pending writes to stable storage. + * Strictly speaking, we could sync just the indicated file region here, * but there's currently no way we can ask the VFS to do so. * - * We lock the file to make sure we return full WCC data to the client. + * Unfortunately we cannot lock the file to make sure we return full WCC + * data to the client, as locking happens lower down in the filesystem. */ int nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp, @@ -828,7 +831,7 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, * Check whether the response file handle has been verified yet. * If it has, the parent directory should already be locked. */ - if (!resfhp->fh_dverified) { + if (!resfhp->fh_dentry) { /* called from nfsd_proc_mkdir, or possibly nfsd3_proc_create */ fh_lock(fhp); dchild = lookup_one(fname, dentry); @@ -891,7 +894,7 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, if (EX_ISSYNC(fhp->fh_export)) { nfsd_sync_dir(dentry); - write_inode_now(dchild->d_inode); + write_inode_now(dchild->d_inode, 0); } @@ -928,6 +931,8 @@ nfsd_create_v3(struct svc_rqst *rqstp, struct svc_fh *fhp, struct dentry *dentry, *dchild; struct inode *dirp; int err; + __u32 v_mtime=0, v_atime=0; + int v_mode=0; err = nfserr_perm; if (!flen) @@ -963,6 +968,19 @@ nfsd_create_v3(struct svc_rqst *rqstp, struct svc_fh *fhp, if (err) goto out; + if (createmode == NFS3_CREATE_EXCLUSIVE) { + /* while the verifier would fit in mtime+atime, + * solaris7 gets confused (bugid 4218508) if these have + * the high bit set, so we use the mode as well + */ + v_mtime = verifier[0]&0x7fffffff; + v_atime = verifier[1]&0x7fffffff; + v_mode = S_IFREG + | ((verifier[0]&0x80000000) >> (32-7)) /* u+x */ + | ((verifier[1]&0x80000000) >> (32-9)) /* u+r */ + ; + } + if (dchild->d_inode) { err = 0; @@ -976,10 +994,10 @@ nfsd_create_v3(struct svc_rqst *rqstp, struct svc_fh *fhp, } break; case NFS3_CREATE_EXCLUSIVE: - if ( dchild->d_inode->i_mtime == verifier[0] - && dchild->d_inode->i_atime == verifier[1] - && dchild->d_inode->i_mode == S_IFREG - && dchild->d_inode->i_size == 0 ) + if ( dchild->d_inode->i_mtime == v_mtime + && dchild->d_inode->i_atime == v_atime + && dchild->d_inode->i_mode == v_mode + && dchild->d_inode->i_size == 0 ) break; /* fallthru */ case NFS3_CREATE_GUARDED: @@ -1005,19 +1023,23 @@ nfsd_create_v3(struct svc_rqst *rqstp, struct svc_fh *fhp, goto out; if (createmode == NFS3_CREATE_EXCLUSIVE) { - /* Cram the verifier into atime/mtime */ - iap->ia_valid = ATTR_MTIME|ATTR_ATIME|ATTR_MTIME_SET|ATTR_ATIME_SET; - iap->ia_mtime = verifier[0]; - iap->ia_atime = verifier[1]; + /* Cram the verifier into atime/mtime/mode */ + iap->ia_valid = ATTR_MTIME|ATTR_ATIME + | ATTR_MTIME_SET|ATTR_ATIME_SET + | ATTR_MODE; + iap->ia_mtime = v_mtime; + iap->ia_atime = v_atime; + iap->ia_mode = v_mode; } - /* Set file attributes. Mode has already been set and - * setting uid/gid works only for root. Irix appears to - * send along the gid when it tries to implement setgid - * directories via NFS. Clear out all that cruft. + /* Set file attributes. + * Mode has already been set but we might need to reset it + * for CREATE_EXCLUSIVE + * Irix appears to send along the gid when it tries to + * implement setgid directories via NFS. Clear out all that cruft. */ set_attr: - if ((iap->ia_valid &= ~(ATTR_UID|ATTR_GID|ATTR_MODE)) != 0) + if ((iap->ia_valid &= ~(ATTR_UID|ATTR_GID)) != 0) err = nfsd_setattr(rqstp, resfhp, iap); out: @@ -1118,7 +1140,7 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp, | S_IFLNK; err = notify_change(dnew, iap); if (!err && EX_ISSYNC(fhp->fh_export)) - write_inode_now(dentry->d_inode); + write_inode_now(dentry->d_inode, 0); } } } else @@ -1178,7 +1200,7 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp, if (!err) { if (EX_ISSYNC(ffhp->fh_export)) { nfsd_sync_dir(ddir); - write_inode_now(dest); + write_inode_now(dest, 0); } } else { if (err == -EXDEV && rqstp->rq_vers == 2) @@ -1230,7 +1252,13 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen, if (!flen || isdotent(fname, flen) || !tlen || isdotent(tname, tlen)) goto out; + /* cannot use fh_lock as we need deadlock protective ordering + * so do it by hand */ double_down(&tdir->i_sem, &fdir->i_sem); + ffhp->fh_locked = tfhp->fh_locked = 1; + fill_pre_wcc(ffhp); + fill_pre_wcc(tfhp); + odentry = lookup_one(fname, fdentry); err = PTR_ERR(odentry); if (IS_ERR(odentry)) @@ -1245,39 +1273,31 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen, if (IS_ERR(ndentry)) goto out_dput_old; -#ifdef CONFIG_NFSD_V3 - /* Fill in the pre-op attr for the wcc data for both - * tdir and fdir - */ - fill_pre_wcc(ffhp); - fill_pre_wcc(tfhp); -#endif /* CONFIG_NFSD_V3 */ err = vfs_rename(fdir, odentry, tdir, ndentry); if (!err && EX_ISSYNC(tfhp->fh_export)) { nfsd_sync_dir(tdentry); nfsd_sync_dir(fdentry); } -#ifdef CONFIG_NFSD_V3 - /* Fill in the post-op attr for the wcc data for both - * tdir and fdir - */ - fill_post_wcc(ffhp); - fill_post_wcc(tfhp); -#endif /* CONFIG_NFSD_V3 */ - double_up(&tdir->i_sem, &fdir->i_sem); dput(ndentry); -out_dput_old: + out_dput_old: dput(odentry); + out_nfserr: if (err) - goto out_nfserr; + err = nfserrno(err); + + /* we cannot reply on fh_unlock on the two filehandles, + * as that would do the wrong thing if the two directories + * were the same, so again we do it by hand + */ + fill_post_wcc(ffhp); + fill_post_wcc(tfhp); + double_up(&tdir->i_sem, &fdir->i_sem); + ffhp->fh_locked = tfhp->fh_locked = 0; + out: return err; - -out_nfserr: - err = nfserrno(err); - goto out; } /* @@ -1320,17 +1340,13 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, err = vfs_rmdir(dirp, rdentry); } - fh_unlock(fhp); - dput(rdentry); if (err) goto out_nfserr; - if (EX_ISSYNC(fhp->fh_export)) { - down(&dentry->d_inode->i_sem); + if (EX_ISSYNC(fhp->fh_export)) nfsd_sync_dir(dentry); - up(&dentry->d_inode->i_sem); - } + out: return err; @@ -1353,13 +1369,11 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, struct file file; struct readdir_cd cd; - err = 0; - if (offset > ~(u32) 0) - goto out; - err = nfsd_open(rqstp, fhp, S_IFDIR, MAY_READ, &file); if (err) goto out; + if (offset > ~(u32) 0) + goto out_close; err = nfserr_notdir; if (!file.f_op->readdir) @@ -1402,11 +1416,9 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, eof = !cd.eob; if (cd.offset) { -#ifdef CONFIG_NFSD_V3 if (rqstp->rq_vers == 3) (void)xdr_encode_hyper(cd.offset, file.f_pos); else -#endif /* CONFIG_NFSD_V3 */ *cd.offset = htonl(file.f_pos); } diff --git a/fs/ntfs/fs.c b/fs/ntfs/fs.c index e0649ec7b..470f15c90 100644 --- a/fs/ntfs/fs.c +++ b/fs/ntfs/fs.c @@ -546,6 +546,7 @@ _linux_ntfs_mkdir(struct inode *dir, struct dentry* d, int mode) } #endif +#if 0 static int ntfs_bmap(struct inode *ino,int block) { @@ -554,6 +555,7 @@ ntfs_bmap(struct inode *ino,int block) ino->i_ino,block,ret); return (ret==-1) ? 0:ret; } +#endif /* It's fscking broken. */ @@ -607,6 +607,8 @@ static struct super_block * pipefs_read_super(struct super_block *sb, void *data root->i_mode = S_IFDIR | S_IRUSR | S_IWUSR; root->i_uid = root->i_gid = 0; root->i_atime = root->i_mtime = root->i_ctime = CURRENT_TIME; + root->i_sb = sb; + root->i_dev = sb->s_dev; sb->s_blocksize = 1024; sb->s_blocksize_bits = 10; sb->s_magic = PIPEFS_MAGIC; diff --git a/fs/proc/base.c b/fs/proc/base.c index fb63722d5..01f5b22ea 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -408,6 +408,7 @@ static int proc_pid_follow_link(struct dentry *dentry, struct nameidata *nd) goto out; error = inode->u.proc_i.op.proc_get_link(inode, &nd->dentry, &nd->mnt); + nd->last_type = LAST_BIND; out: #ifdef NULL_VFSMNT mntput(dummy); @@ -706,6 +707,7 @@ static struct dentry_operations pid_base_dentry_operations = }; /* Lookups */ +#define MAX_MULBY10 ((~0U-9)/10) static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry) { @@ -726,10 +728,10 @@ static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry) name++; if (c > 9) goto out; + if (fd >= MAX_MULBY10) + goto out; fd *= 10; fd += c; - if (fd & 0xffff8000) - goto out; } inode = proc_pid_make_inode(dir->i_sb, task, PROC_PID_FD_DIR+fd); @@ -940,12 +942,12 @@ struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry) name++; if (c > 9) goto out; + if (pid >= MAX_MULBY10) + goto out; pid *= 10; pid += c; if (!pid) goto out; - if (pid & 0xffff0000) - goto out; } read_lock(&tasklist_lock); diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c index 60393eb91..3576482ca 100644 --- a/fs/qnx4/inode.c +++ b/fs/qnx4/inode.c @@ -340,7 +340,6 @@ static struct super_block *qnx4_read_super(struct super_block *s, set_blocksize(dev, QNX4_BLOCK_SIZE); s->s_blocksize = QNX4_BLOCK_SIZE; s->s_blocksize_bits = QNX4_BLOCK_SIZE_BITS; - s->s_dev = dev; /* Check the boot signature. Since the qnx4 code is dangerous, we should leave as quickly as possible diff --git a/fs/ramfs/inode.c b/fs/ramfs/inode.c index f87d30e0b..9bb7611c1 100644 --- a/fs/ramfs/inode.c +++ b/fs/ramfs/inode.c @@ -298,15 +298,9 @@ static struct inode_operations ramfs_dir_inode_operations = { rename: ramfs_rename, }; -static void ramfs_put_super(struct super_block *sb) -{ - d_genocide(sb->s_root); - shrink_dcache_parent(sb->s_root); -} - static struct super_operations ramfs_ops = { - put_super: ramfs_put_super, statfs: ramfs_statfs, + put_inode: force_delete, }; static struct super_block *ramfs_read_super(struct super_block * sb, void * data, int silent) @@ -331,7 +325,7 @@ static struct super_block *ramfs_read_super(struct super_block * sb, void * data return sb; } -static DECLARE_FSTYPE(ramfs_fs_type, "ramfs", ramfs_read_super, 0); +static DECLARE_FSTYPE(ramfs_fs_type, "ramfs", ramfs_read_super, FS_LITTER); static int __init init_ramfs_fs(void) { diff --git a/fs/smbfs/file.c b/fs/smbfs/file.c index b47e236b0..49d47afa7 100644 --- a/fs/smbfs/file.c +++ b/fs/smbfs/file.c @@ -27,7 +27,7 @@ /* #define pr_debug printk */ static int -smb_fsync(struct file *file, struct dentry * dentry) +smb_fsync(struct file *file, struct dentry * dentry, int datasync) { #ifdef SMBFS_DEBUG_VERBOSE printk("smb_fsync: sync file %s/%s\n", diff --git a/fs/super.c b/fs/super.c index 5b8974e5b..8def1c9c4 100644 --- a/fs/super.c +++ b/fs/super.c @@ -260,7 +260,7 @@ int get_filesystem_list(char * buf) return len; } -static struct file_system_type *get_fs_type(const char *name) +struct file_system_type *get_fs_type(const char *name) { struct file_system_type *fs; @@ -281,14 +281,28 @@ static struct file_system_type *get_fs_type(const char *name) static LIST_HEAD(vfsmntlist); -static struct vfsmount *add_vfsmnt(struct super_block *sb, - struct dentry *mountpoint, +/** + * add_vfsmnt - add a new mount node + * @nd: location of mountpoint or %NULL if we want a root node + * @root: root of (sub)tree to be mounted + * @dev_name: device name to show in /proc/mounts + * + * This is VFS idea of mount. New node is allocated, bound to a tree + * we are mounting and optionally (OK, usually) registered as mounted + * on a given mountpoint. Returns a pointer to new node or %NULL in + * case of failure. + * + * Potential reason for failure (aside of trivial lack of memory) is a + * deleted mountpoint. Caller must hold ->i_zombie on mountpoint + * dentry (if any). + */ + +static struct vfsmount *add_vfsmnt(struct nameidata *nd, struct dentry *root, - struct vfsmount *parent, - const char *dev_name, - const char *dir_name) + const char *dev_name) { struct vfsmount *mnt; + struct super_block *sb = root->d_inode->i_sb; char *name; mnt = kmalloc(sizeof(struct vfsmount), GFP_KERNEL); @@ -296,13 +310,7 @@ static struct vfsmount *add_vfsmnt(struct super_block *sb, goto out; memset(mnt, 0, sizeof(struct vfsmount)); - atomic_set(&mnt->mnt_count,1); - mnt->mnt_sb = sb; - mnt->mnt_mountpoint = dget(mountpoint); - mnt->mnt_root = dget(root); - mnt->mnt_parent = parent ? mntget(parent) : mnt; - - /* N.B. Is it really OK to have a vfsmount without names? */ + /* It may be NULL, but who cares? */ if (dev_name) { name = kmalloc(strlen(dev_name)+1, GFP_KERNEL); if (name) { @@ -310,51 +318,53 @@ static struct vfsmount *add_vfsmnt(struct super_block *sb, mnt->mnt_devname = name; } } - name = kmalloc(strlen(dir_name)+1, GFP_KERNEL); - if (name) { - strcpy(name, dir_name); - mnt->mnt_dirname = name; - } mnt->mnt_owner = current->uid; + atomic_set(&mnt->mnt_count,1); + mnt->mnt_sb = sb; - if (parent) - list_add(&mnt->mnt_child, &parent->mnt_mounts); - else + if (nd && !IS_ROOT(nd->dentry) && d_unhashed(nd->dentry)) + goto fail; + mnt->mnt_root = dget(root); + mnt->mnt_mountpoint = nd ? dget(nd->dentry) : dget(root); + mnt->mnt_parent = nd ? mntget(nd->mnt) : mnt; + + if (nd) { + list_add(&mnt->mnt_child, &nd->mnt->mnt_mounts); + list_add(&mnt->mnt_clash, &nd->dentry->d_vfsmnt); + } else { INIT_LIST_HEAD(&mnt->mnt_child); + INIT_LIST_HEAD(&mnt->mnt_clash); + } INIT_LIST_HEAD(&mnt->mnt_mounts); list_add(&mnt->mnt_instances, &sb->s_mounts); - list_add(&mnt->mnt_clash, &mountpoint->d_vfsmnt); list_add(&mnt->mnt_list, vfsmntlist.prev); out: return mnt; +fail: + kfree(mnt->mnt_devname); + kfree(mnt); + return NULL; } static void move_vfsmnt(struct vfsmount *mnt, struct dentry *mountpoint, struct vfsmount *parent, - const char *dev_name, - const char *dir_name) + const char *dev_name) { - struct dentry *old_mountpoint = mnt->mnt_mountpoint; - struct vfsmount *old_parent = mnt->mnt_parent; - char *new_devname = NULL, *new_dirname = NULL; + struct dentry *old_mountpoint; + struct vfsmount *old_parent; + char *new_devname = NULL; if (dev_name) { new_devname = kmalloc(strlen(dev_name)+1, GFP_KERNEL); if (new_devname) strcpy(new_devname, dev_name); } - if (dir_name) { - new_dirname = kmalloc(strlen(dir_name)+1, GFP_KERNEL); - if (new_dirname) - strcpy(new_dirname, dir_name); - } + + old_mountpoint = mnt->mnt_mountpoint; + old_parent = mnt->mnt_parent; /* flip names */ - if (new_dirname) { - kfree(mnt->mnt_dirname); - mnt->mnt_dirname = new_dirname; - } if (new_devname) { kfree(mnt->mnt_devname); mnt->mnt_devname = new_devname; @@ -365,11 +375,13 @@ static void move_vfsmnt(struct vfsmount *mnt, mnt->mnt_parent = parent ? mntget(parent) : mnt; list_del(&mnt->mnt_clash); list_del(&mnt->mnt_child); - list_add(&mnt->mnt_clash, &mountpoint->d_vfsmnt); - if (parent) + if (parent) { list_add(&mnt->mnt_child, &parent->mnt_mounts); - else + list_add(&mnt->mnt_clash, &mountpoint->d_vfsmnt); + } else { INIT_LIST_HEAD(&mnt->mnt_child); + INIT_LIST_HEAD(&mnt->mnt_clash); + } /* put the old stuff */ dput(old_mountpoint); @@ -391,7 +403,6 @@ static void remove_vfsmnt(struct vfsmount *mnt) dput(mnt->mnt_mountpoint); dput(mnt->mnt_root); kfree(mnt->mnt_devname); - kfree(mnt->mnt_dirname); kfree(mnt); } @@ -738,10 +749,6 @@ static struct super_block *get_sb_bdev(struct file_system_type *fs_type, /* Done with lookups, semaphore down */ down(&mount_sem); dev = to_kdev_t(bdev->bd_dev); - check_disk_change(dev); - error = -EACCES; - if (!(flags & MS_RDONLY) && is_read_only(dev)) - goto out; sb = get_super(dev); if (sb) { if (fs_type == sb->s_type) { @@ -755,6 +762,10 @@ static struct super_block *get_sb_bdev(struct file_system_type *fs_type, error = blkdev_get(bdev, mode, 0, BDEV_FS); if (error) goto out; + check_disk_change(dev); + error = -EACCES; + if (!(flags & MS_RDONLY) && is_read_only(dev)) + goto out1; error = -EINVAL; sb = read_super(dev, bdev, fs_type, flags, data, 0); if (sb) { @@ -762,6 +773,7 @@ static struct super_block *get_sb_bdev(struct file_system_type *fs_type, path_release(&nd); return sb; } +out1: blkdev_put(bdev, BDEV_FS); } out: @@ -812,8 +824,14 @@ static struct block_device *kill_super(struct super_block *sb, int umount_root) { struct block_device *bdev; kdev_t dev; - dput(sb->s_root); + struct dentry *root = sb->s_root; sb->s_root = NULL; + /* Need to clean after the sucker */ + if (sb->s_type->fs_flags & FS_LITTER) + d_genocide(root); + if (sb->s_type->fs_flags & (FS_SINGLE|FS_LITTER)) + shrink_dcache_parent(root); + dput(root); lock_super(sb); if (sb->s_op) { if (sb->s_op->write_super && sb->s_dirt) @@ -895,7 +913,7 @@ struct vfsmount *kern_mount(struct file_system_type *type) put_unnamed_dev(dev); return ERR_PTR(-EINVAL); } - mnt = add_vfsmnt(sb, sb->s_root, sb->s_root, NULL, "none", type->name); + mnt = add_vfsmnt(NULL, sb->s_root, "none"); if (!mnt) { kill_super(sb, 0); return ERR_PTR(-ENOMEM); @@ -909,10 +927,7 @@ struct vfsmount *kern_mount(struct file_system_type *type) void kern_umount(struct vfsmount *mnt) { struct super_block *sb = mnt->mnt_sb; - struct dentry *root = sb->s_root; remove_vfsmnt(mnt); - dput(root); - sb->s_root = NULL; kill_super(sb, 0); } @@ -932,6 +947,16 @@ static int do_umount(struct vfsmount *mnt, int umount_root, int flags) { struct super_block * sb = mnt->mnt_sb; + /* + * No sense to grab the lock for this test, but test itself looks + * somewhat bogus. Suggestions for better replacement? + * Ho-hum... In principle, we might treat that as umount + switch + * to rootfs. GC would eventually take care of the old vfsmount. + * The problem being: we have to implement rootfs and GC for that ;-) + * Actually it makes sense, especially if rootfs would contain a + * /reboot - static binary that would close all descriptors and + * call reboot(9). Then init(8) could umount root and exec /reboot. + */ if (mnt == current->fs->rootmnt && !umount_root) { int retval = 0; /* @@ -952,6 +977,7 @@ static int do_umount(struct vfsmount *mnt, int umount_root, int flags) if (mnt->mnt_instances.next != mnt->mnt_instances.prev) { if (sb->s_type->fs_flags & FS_SINGLE) put_filesystem(sb->s_type); + /* We hold two references, so mntput() is safe */ mntput(mnt); remove_vfsmnt(mnt); return 0; @@ -988,14 +1014,14 @@ static int do_umount(struct vfsmount *mnt, int umount_root, int flags) shrink_dcache_sb(sb); fsync_dev(sb->s_dev); - /* Something might grab it again - redo checks */ - - if (atomic_read(&mnt->mnt_count) > 2) { + if (sb->s_root->d_inode->i_state) { mntput(mnt); return -EBUSY; } - if (sb->s_root->d_inode->i_state) { + /* Something might grab it again - redo checks */ + + if (atomic_read(&mnt->mnt_count) > 2) { mntput(mnt); return -EBUSY; } @@ -1067,6 +1093,8 @@ static int mount_is_safe(struct nameidata *nd) { if (capable(CAP_SYS_ADMIN)) return 0; + return -EPERM; +#ifdef notyet if (S_ISLNK(nd->dentry->d_inode->i_mode)) return -EPERM; if (nd->dentry->d_inode->i_mode & S_ISVTX) { @@ -1076,6 +1104,7 @@ static int mount_is_safe(struct nameidata *nd) if (permission(nd->dentry->d_inode, MAY_WRITE)) return -EPERM; return 0; +#endif } /* @@ -1102,22 +1131,22 @@ static int do_loopback(char *old_name, char *new_name) if (S_ISDIR(new_nd.dentry->d_inode->i_mode) != S_ISDIR(old_nd.dentry->d_inode->i_mode)) goto out2; - - down(&mount_sem); - err = -ENOENT; - if (d_unhashed(old_nd.dentry) && !IS_ROOT(old_nd.dentry)) - goto out3; - if (d_unhashed(new_nd.dentry) && !IS_ROOT(new_nd.dentry)) - goto out3; - /* there we go */ + err = -ENOMEM; if (old_nd.mnt->mnt_sb->s_type->fs_flags & FS_SINGLE) get_filesystem(old_nd.mnt->mnt_sb->s_type); - if (add_vfsmnt(old_nd.mnt->mnt_sb, new_nd.dentry, old_nd.dentry, - new_nd.mnt, old_nd.mnt->mnt_devname, new_name)) + + down(&mount_sem); + /* there we go */ + down(&new_nd.dentry->d_inode->i_zombie); + if (IS_DEADDIR(new_nd.dentry->d_inode)) + err = -ENOENT; + else if (add_vfsmnt(&new_nd, old_nd.dentry, old_nd.mnt->mnt_devname)) err = 0; -out3: + up(&new_nd.dentry->d_inode->i_zombie); up(&mount_sem); + if (err && old_nd.mnt->mnt_sb->s_type->fs_flags & FS_SINGLE) + put_filesystem(old_nd.mnt->mnt_sb->s_type); out2: path_release(&new_nd); out1: @@ -1215,7 +1244,7 @@ long do_mount(char * dev_name, char * dir_name, char *type_page, { struct file_system_type * fstype; struct nameidata nd; - struct vfsmount *mnt; + struct vfsmount *mnt = NULL; struct super_block *sb; int retval = 0; unsigned long flags = 0; @@ -1224,8 +1253,6 @@ long do_mount(char * dev_name, char * dir_name, char *type_page, if (!dir_name || !*dir_name || !memchr(dir_name, 0, PAGE_SIZE)) return -EINVAL; - if (!type_page || !memchr(type_page, 0, PAGE_SIZE)) - return -EINVAL; if (dev_name && !memchr(dev_name, 0, PAGE_SIZE)) return -EINVAL; @@ -1239,6 +1266,11 @@ long do_mount(char * dev_name, char * dir_name, char *type_page, if ((new_flags & MS_MGC_MSK) == MS_MGC_VAL) flags = new_flags & ~MS_MGC_MSK; + /* For the rest we need the type */ + + if (!type_page || !memchr(type_page, 0, PAGE_SIZE)) + return -EINVAL; + /* loopback mount? This is special - requires fewer capabilities */ if (strcmp(type_page, "bind")==0) return do_loopback(dev_name, dir_name); @@ -1272,16 +1304,18 @@ long do_mount(char * dev_name, char * dir_name, char *type_page, if (IS_ERR(sb)) goto dput_out; - retval = -ENOENT; - if (d_unhashed(nd.dentry) && !IS_ROOT(nd.dentry)) - goto fail; - /* Something was mounted here while we slept */ while(d_mountpoint(nd.dentry) && follow_down(&nd.mnt, &nd.dentry)) ; - - retval = -ENOMEM; - mnt = add_vfsmnt(sb, nd.dentry, sb->s_root, nd.mnt, dev_name, dir_name); + retval = -ENOENT; + if (!nd.dentry->d_inode) + goto fail; + down(&nd.dentry->d_inode->i_zombie); + if (!IS_DEADDIR(nd.dentry->d_inode)) { + retval = -ENOMEM; + mnt = add_vfsmnt(&nd, sb->s_root, dev_name); + } + up(&nd.dentry->d_inode->i_zombie); if (!mnt) goto fail; retval = 0; @@ -1312,15 +1346,6 @@ asmlinkage long sys_mount(char * dev_name, char * dir_name, char * type, if (retval < 0) return retval; - /* copy_mount_options allows a NULL user pointer, - * and just returns zero in that case. But if we - * allow the type to be NULL we will crash. - * Previously we did not check this case. - */ - if (type_page == 0) - return -EINVAL; - - lock_kernel(); dir_page = getname(dir_name); retval = PTR_ERR(dir_page); if (IS_ERR(dir_page)) @@ -1331,8 +1356,10 @@ asmlinkage long sys_mount(char * dev_name, char * dir_name, char * type, goto out2; retval = copy_mount_options (data, &data_page); if (retval >= 0) { + lock_kernel(); retval = do_mount((char*)dev_page,dir_page,(char*)type_page, new_flags, (void*)data_page); + unlock_kernel(); free_page(data_page); } free_page(dev_page); @@ -1340,7 +1367,6 @@ out2: putname(dir_page); out1: free_page(type_page); - unlock_kernel(); return retval; } @@ -1490,12 +1516,11 @@ mount_it: path + 5 + path_start, 0, NULL, NULL); memcpy (path + path_start, "/dev/", 5); - vfsmnt = add_vfsmnt (sb, sb->s_root, sb->s_root, NULL, - path + path_start, "/"); + vfsmnt = add_vfsmnt(NULL, sb->s_root, path + path_start); } else - vfsmnt = add_vfsmnt (sb, sb->s_root, sb->s_root, NULL, - "/dev/root", "/"); + vfsmnt = add_vfsmnt(NULL, sb->s_root, "/dev/root"); + /* FIXME: if something will try to umount us right now... */ if (vfsmnt) { set_fs_root(current->fs, vfsmnt, sb->s_root); set_fs_pwd(current->fs, vfsmnt, sb->s_root); @@ -1516,6 +1541,7 @@ static void chroot_fs_refs(struct dentry *old_root, read_lock(&tasklist_lock); for_each_task(p) { + /* FIXME - unprotected usage of ->fs + (harmless) race */ if (!p->fs) continue; if (p->fs->root == old_root && p->fs->rootmnt == old_rootmnt) set_fs_root(p->fs, new_rootmnt, new_root); @@ -1576,7 +1602,10 @@ asmlinkage long sys_pivot_root(const char *new_root, const char *put_old) root_mnt = mntget(current->fs->rootmnt); root = dget(current->fs->root); down(&mount_sem); + down(&old_nd.dentry->d_inode->i_zombie); error = -ENOENT; + if (IS_DEADDIR(new_nd.dentry->d_inode)) + goto out2; if (d_unhashed(new_nd.dentry) && !IS_ROOT(new_nd.dentry)) goto out2; if (d_unhashed(old_nd.dentry) && !IS_ROOT(old_nd.dentry)) @@ -1599,19 +1628,12 @@ asmlinkage long sys_pivot_root(const char *new_root, const char *put_old) } else if (!is_subdir(old_nd.dentry, new_nd.dentry)) goto out2; - error = -ENOMEM; - name = __getname(); - if (!name) - goto out2; - - move_vfsmnt(new_nd.mnt, new_nd.dentry, NULL, NULL, "/"); - move_vfsmnt(root_mnt, old_nd.dentry, old_nd.mnt, NULL, - __d_path(old_nd.dentry, old_nd.mnt, new_nd.dentry, - new_nd.mnt, name, PAGE_SIZE)); - putname(name); + move_vfsmnt(new_nd.mnt, new_nd.dentry, NULL, NULL); + move_vfsmnt(root_mnt, old_nd.dentry, old_nd.mnt, NULL); chroot_fs_refs(root,root_mnt,new_nd.dentry,new_nd.mnt); error = 0; out2: + up(&old_nd.dentry->d_inode->i_zombie); up(&mount_sem); dput(root); mntput(root_mnt); @@ -1629,10 +1651,11 @@ out0: int __init change_root(kdev_t new_root_dev,const char *put_old) { kdev_t old_root_dev = ROOT_DEV; - struct vfsmount *old_rootmnt = mntget(current->fs->rootmnt); + struct vfsmount *old_rootmnt; struct nameidata devfs_nd, nd; int error = 0; + old_rootmnt = mntget(current->fs->rootmnt); /* First unmount devfs if mounted */ if (path_init("/dev", LOOKUP_FOLLOW|LOOKUP_POSITIVE, &devfs_nd)) error = path_walk("/dev", &devfs_nd); @@ -1675,7 +1698,8 @@ int __init change_root(kdev_t new_root_dev,const char *put_old) printk(KERN_ERR "error %ld\n",blivet); return error; } - move_vfsmnt(old_rootmnt, nd.dentry, nd.mnt, "/dev/root.old", put_old); + /* FIXME: we should hold i_zombie on nd.dentry */ + move_vfsmnt(old_rootmnt, nd.dentry, nd.mnt, "/dev/root.old"); mntput(old_rootmnt); path_release(&nd); return 0; diff --git a/fs/sysv/fsync.c b/fs/sysv/fsync.c index 3c9871be6..091605cd1 100644 --- a/fs/sysv/fsync.c +++ b/fs/sysv/fsync.c @@ -178,7 +178,7 @@ static int sync_tindirect(struct inode *inode, u32 *tiblockp, int convert, return err; } -int sysv_sync_file(struct file * file, struct dentry *dentry) +int sysv_sync_file(struct file * file, struct dentry *dentry, int datasync) { int wait, err = 0; struct inode *inode = dentry->d_inode; diff --git a/fs/sysv/ialloc.c b/fs/sysv/ialloc.c index d7cc12187..bbd88336c 100644 --- a/fs/sysv/ialloc.c +++ b/fs/sysv/ialloc.c @@ -142,7 +142,7 @@ struct inode * sysv_new_inode(const struct inode * dir) /* Change directory entry: */ inode->i_mode = 0; /* for sysv_write_inode() */ inode->i_size = 0; /* ditto */ - sysv_write_inode(inode); /* ensure inode not allocated again */ + sysv_write_inode(inode, 0); /* ensure inode not allocated again */ /* FIXME: caller may call this too. */ mark_inode_dirty(inode); /* cleared by sysv_write_inode() */ /* That's it. */ diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c index 455818959..9ac81643b 100644 --- a/fs/sysv/inode.c +++ b/fs/sysv/inode.c @@ -496,7 +496,6 @@ static struct super_block *sysv_read_super(struct super_block *sb, sb->s_blocksize = sb->sv_block_size; sb->s_blocksize_bits = sb->sv_block_size_bits; /* set up enough so that it can read an inode */ - sb->s_dev = dev; sb->s_op = &sysv_sops; root_inode = iget(sb,SYSV_ROOT_INO); sb->s_root = d_alloc_root(root_inode); @@ -1154,7 +1153,7 @@ static struct buffer_head * sysv_update_inode(struct inode * inode) return bh; } -void sysv_write_inode(struct inode * inode) +void sysv_write_inode(struct inode * inode, int wait) { struct buffer_head *bh; bh = sysv_update_inode(inode); diff --git a/fs/udf/fsync.c b/fs/udf/fsync.c index e7d067e62..bc107046d 100644 --- a/fs/udf/fsync.c +++ b/fs/udf/fsync.c @@ -96,7 +96,7 @@ static int sync_all_extents(struct inode * inode, int wait) * even pass file to fsync ? */ -int udf_sync_file(struct file * file, struct dentry *dentry) +int udf_sync_file(struct file * file, struct dentry *dentry, int datasync) { int wait, err = 0; struct inode *inode = dentry->d_inode; diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 8c38883c0..360c12ba0 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -1213,10 +1213,10 @@ udf_convert_permissions(struct FileEntry *fe) * Written, tested, and released. */ -void udf_write_inode(struct inode * inode) +void udf_write_inode(struct inode * inode, int sync) { lock_kernel(); - udf_update_inode(inode, 0); + udf_update_inode(inode, sync); unlock_kernel(); } diff --git a/fs/udf/super.c b/fs/udf/super.c index 5f76abbb0..f3f575d7e 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -1419,7 +1419,6 @@ udf_read_super(struct super_block *sb, void *options, int silent) return sb; error_out: - sb->s_dev = NODEV; if (UDF_SB_VAT(sb)) iput(UDF_SB_VAT(sb)); if (!(sb->s_flags & MS_RDONLY)) diff --git a/fs/udf/udfdecl.h b/fs/udf/udfdecl.h index 7dd00bc19..22cdd2c43 100644 --- a/fs/udf/udfdecl.h +++ b/fs/udf/udfdecl.h @@ -134,7 +134,7 @@ extern struct buffer_head * udf_bread(struct inode *, int, int, int *); extern void udf_read_inode(struct inode *); extern void udf_put_inode(struct inode *); extern void udf_delete_inode(struct inode *); -extern void udf_write_inode(struct inode *); +extern void udf_write_inode(struct inode *, int); extern long udf_locked_block_map(struct inode *, long); extern long udf_block_map(struct inode *, long); extern int inode_bmap(struct inode *, int, lb_addr *, Uint32 *, lb_addr *, Uint32 *, Uint32 *, struct buffer_head **); @@ -184,7 +184,7 @@ extern int udf_prealloc_blocks(const struct inode *, Uint16, Uint32, Uint32); extern int udf_new_block(const struct inode *, Uint16, Uint32, int *); /* fsync.c */ -extern int udf_sync_file(struct file *, struct dentry *); +extern int udf_sync_file(struct file *, struct dentry *, int); /* directory.c */ extern Uint8 * udf_filead_read(struct inode *, Uint8 *, Uint8, lb_addr, int *, int *, struct buffer_head **, int *); diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c index 7801add9a..eb1d86d18 100644 --- a/fs/ufs/inode.c +++ b/fs/ufs/inode.c @@ -136,6 +136,7 @@ int ufs_frag_map(struct inode *inode, int frag) ufs_block_bmap(bread(sb->s_dev, uspi->s_sbbase + i, sb->s_blocksize), frag & uspi->s_apbmask, uspi, swab)); + goto out; } frag -= 1 << (uspi->s_apbshift + uspi->s_fpbshift); if (frag < (1 << (uspi->s_2apbshift + uspi->s_fpbshift))) { @@ -744,9 +745,9 @@ static int ufs_update_inode(struct inode * inode, int do_sync) return 0; } -void ufs_write_inode (struct inode * inode) +void ufs_write_inode (struct inode * inode, int wait) { - ufs_update_inode (inode, 0); + ufs_update_inode (inode, wait); } int ufs_sync_inode (struct inode *inode) diff --git a/fs/umsdos/inode.c b/fs/umsdos/inode.c index 14b23467d..8820a49dd 100644 --- a/fs/umsdos/inode.c +++ b/fs/umsdos/inode.c @@ -293,11 +293,11 @@ out: /* * Update the disk with the inode content */ -void UMSDOS_write_inode (struct inode *inode) +void UMSDOS_write_inode (struct inode *inode, int wait) { struct iattr newattrs; - fat_write_inode (inode); + fat_write_inode (inode, wait); newattrs.ia_mtime = inode->i_mtime; newattrs.ia_atime = inode->i_atime; newattrs.ia_ctime = inode->i_ctime; diff --git a/include/asm-alpha/pci.h b/include/asm-alpha/pci.h index a74290f55..86d1db58c 100644 --- a/include/asm-alpha/pci.h +++ b/include/asm-alpha/pci.h @@ -51,10 +51,7 @@ struct pci_controler { #define PCIBIOS_MIN_IO alpha_mv.min_io_address #define PCIBIOS_MIN_MEM alpha_mv.min_mem_address -extern inline void pcibios_set_master(struct pci_dev *dev) -{ - /* No special bus mastering setup handling */ -} +extern void pcibios_set_master(struct pci_dev *dev); extern inline void pcibios_penalize_isa_irq(int irq) { diff --git a/include/asm-alpha/system.h b/include/asm-alpha/system.h index 4c2f70170..6328750e1 100644 --- a/include/asm-alpha/system.h +++ b/include/asm-alpha/system.h @@ -14,16 +14,17 @@ * We leave one page for the initial stack page, and one page for * the initial process structure. Also, the console eats 3 MB for * the initial bootloader (one of which we can reclaim later). - * With a few other pages for various reasons, we'll use an initial - * load address of PAGE_OFFSET+0x310000UL */ #define BOOT_PCB 0x20000000 #define BOOT_ADDR 0x20000000 /* Remove when official MILO sources have ELF support: */ #define BOOT_SIZE (16*1024) - +#ifdef CONFIG_ALPHA_LEGACY_START_ADDRESS +#define KERNEL_START_PHYS 0x300000 /* Old bootloaders hardcoded this. */ +#else #define KERNEL_START_PHYS 0x800000 /* Wildfire has a huge console */ +#endif #define KERNEL_START (PAGE_OFFSET+KERNEL_START_PHYS) #define SWAPPER_PGD KERNEL_START diff --git a/include/asm-arm/arch-arc/ide.h b/include/asm-arm/arch-arc/ide.h index bccbe3f50..39f91b60b 100644 --- a/include/asm-arm/arch-arc/ide.h +++ b/include/asm-arm/arch-arc/ide.h @@ -20,7 +20,7 @@ * This should follow whatever the default interface uses. */ static __inline__ void -ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int irq) +ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int *irq) { ide_ioreg_t reg = (ide_ioreg_t) data_port; int i; @@ -30,7 +30,8 @@ ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int irq) reg += 1; } hw->io_ports[IDE_CONTROL_OFFSET] = (ide_ioreg_t) ctrl_port; - hw->irq = irq; + if (irq) + *irq = 0; } /* @@ -44,7 +45,8 @@ static __inline__ void ide_init_default_hwifs(void) memset(&hw, 0, sizeof(hw)); - ide_init_hwif_ports(&hw, 0x1f0, 0x3f6, IRQ_HARDDISK); + ide_init_hwif_ports(&hw, 0x1f0, 0x3f6, NULL); + hw.irq = IRQ_HARDDISK; ide_register_hw(&hw, NULL); #endif } diff --git a/include/asm-arm/arch-cl7500/ide.h b/include/asm-arm/arch-cl7500/ide.h index 590579747..53daa0969 100644 --- a/include/asm-arm/arch-cl7500/ide.h +++ b/include/asm-arm/arch-cl7500/ide.h @@ -13,7 +13,7 @@ * This should follow whatever the default interface uses. */ static __inline__ void -ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int irq) +ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int *irq) { ide_ioreg_t reg = (ide_ioreg_t) data_port; int i; @@ -25,7 +25,8 @@ ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int irq) reg += 1; } hw->io_ports[IDE_CONTROL_OFFSET] = (ide_ioreg_t) ctrl_port; - hw->irq = irq; + if (irq) + *irq = 0; } /* diff --git a/include/asm-arm/arch-ebsa285/ide.h b/include/asm-arm/arch-ebsa285/ide.h index 1a09f1827..dbdeb1fab 100644 --- a/include/asm-arm/arch-ebsa285/ide.h +++ b/include/asm-arm/arch-ebsa285/ide.h @@ -13,7 +13,7 @@ * This should follow whatever the default interface uses. */ static __inline__ void -ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int irq) +ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int *irq) { ide_ioreg_t reg = (ide_ioreg_t) data_port; int i; @@ -23,7 +23,8 @@ ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int irq) reg += 1; } hw->io_ports[IDE_CONTROL_OFFSET] = (ide_ioreg_t) ctrl_port; - hw->irq = irq; + if (irq) + *irq = 0; } /* diff --git a/include/asm-arm/arch-l7200/ide.h b/include/asm-arm/arch-l7200/ide.h index 0cfcf3aac..aff8aaf9c 100644 --- a/include/asm-arm/arch-l7200/ide.h +++ b/include/asm-arm/arch-l7200/ide.h @@ -13,7 +13,7 @@ * This should follow whatever the default interface uses. */ static __inline__ void -ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int irq) +ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int *irq) { } diff --git a/include/asm-arm/arch-nexuspci/ide.h b/include/asm-arm/arch-nexuspci/ide.h index cb1eac75a..4a4d1b20c 100644 --- a/include/asm-arm/arch-nexuspci/ide.h +++ b/include/asm-arm/arch-nexuspci/ide.h @@ -13,7 +13,7 @@ * This should follow whatever the default interface uses. */ static __inline__ void -ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int irq) +ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int *irq) { ide_ioreg_t reg = (ide_ioreg_t) data_port; int i; @@ -23,7 +23,8 @@ ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int irq) reg += 1; } hw->io_ports[IDE_CONTROL_OFFSET] = (ide_ioreg_t) ctrl_port; - hw->irq = irq; + if (irq) + *irq = 0; } /* diff --git a/include/asm-arm/arch-rpc/ide.h b/include/asm-arm/arch-rpc/ide.h index ccbc7cf76..827d81c2b 100644 --- a/include/asm-arm/arch-rpc/ide.h +++ b/include/asm-arm/arch-rpc/ide.h @@ -13,7 +13,7 @@ * This should follow whatever the default interface uses. */ static __inline__ void -ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int irq) +ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int *irq) { ide_ioreg_t reg = (ide_ioreg_t) data_port; int i; @@ -25,7 +25,8 @@ ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int irq) reg += 1; } hw->io_ports[IDE_CONTROL_OFFSET] = (ide_ioreg_t) ctrl_port; - hw->irq = irq; + if (irq) + *irq = 0; } /* @@ -37,6 +38,7 @@ ide_init_default_hwifs(void) { hw_regs_t hw; - ide_init_hwif_ports(&hw, 0x1f0, 0x3f6, IRQ_HARDDISK); + ide_init_hwif_ports(&hw, 0x1f0, 0x3f6, NULL); + hw.irq = IRQ_HARDDISK; ide_register_hw(&hw, NULL); } diff --git a/include/asm-arm/arch-sa1100/ide.h b/include/asm-arm/arch-sa1100/ide.h index 3a1f00e3d..3a9935c0b 100644 --- a/include/asm-arm/arch-sa1100/ide.h +++ b/include/asm-arm/arch-sa1100/ide.h @@ -17,7 +17,7 @@ * This should follow whatever the default interface uses. */ static __inline__ void -ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int irq) +ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int *irq) { ide_ioreg_t reg; int i; @@ -37,7 +37,8 @@ ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int irq) reg += (1 << IO_SHIFT); } hw->io_ports[IDE_CONTROL_OFFSET] = (ide_ioreg_t) (ctrl_port << IO_SHIFT); - hw->irq = irq; + if (irq) + *irq = 0; } /* @@ -73,9 +74,11 @@ ide_init_default_hwifs(void) /* MAC 23/4/1999, swap these round so that the left hand hard disk is hda when viewed from the front. This doesn't match the silkscreen however. */ - ide_init_hwif_ports(&hw,0x10,0x1e,EMPEG_IRQ_IDE2); + ide_init_hwif_ports(&hw,0x10,0x1e,NULL); + hw.irq = EMPEG_IRQ_IDE2; ide_register_hw(&hw, NULL); - ide_init_hwif_ports(&hw,0x00,0x0e,EMPEG_IRQ_IDE1); + ide_init_hwif_ports(&hw,0x00,0x0e,NULL); + hw.irq = EMPEG_IRQ_IDE1; ide_register_hw(&hw, NULL); #elif defined( CONFIG_SA1100_VICTOR ) @@ -87,7 +90,8 @@ ide_init_default_hwifs(void) /* set the pcmcia interface timing */ MECR = 0x00060006; - ide_init_hwif_ports(&hw, 0x1f0, 0x3f6, IRQ_GPIO7); + ide_init_hwif_ports(&hw, 0x1f0, 0x3f6, NULL); + hw.irq = IRQ_GPIO7; ide_register_hw(&hw, NULL); #else #error Missing IDE interface definition in include/asm/arch/ide.h diff --git a/include/asm-arm/arch-sa1100/irq.h b/include/asm-arm/arch-sa1100/irq.h index 6f447f497..2f1e63d82 100644 --- a/include/asm-arm/arch-sa1100/irq.h +++ b/include/asm-arm/arch-sa1100/irq.h @@ -15,6 +15,7 @@ * 26-05-2000 JD SA-1111 support added */ #include <linux/config.h> +#include <asm/irq.h> #define fixup_irq(x) (x) @@ -73,7 +74,7 @@ static int GPIO_11_27_spurious; /* GPIOs that triggered when masked */ static void sa1100_GPIO11_27_demux(int irq, void *dev_id, struct pt_regs *regs) { - int i, irq, spurious; + int i, spurious; while( (irq = (GEDR & 0xfffff800)) ){ /* diff --git a/include/asm-arm/arch-shark/hardware.h b/include/asm-arm/arch-shark/hardware.h index 1fb25abd1..fc38d57aa 100644 --- a/include/asm-arm/arch-shark/hardware.h +++ b/include/asm-arm/arch-shark/hardware.h @@ -10,7 +10,7 @@ #ifndef __ASM_ARCH_HARDWARE_H #define __ASM_ARCH_HARDWARE_H -#ifndef __ASSEMBLER__ +#ifndef __ASSEMBLY__ /* * Mapping areas diff --git a/include/asm-arm/arch-shark/ide.h b/include/asm-arm/arch-shark/ide.h index a9e373e98..2ef90b558 100644 --- a/include/asm-arm/arch-shark/ide.h +++ b/include/asm-arm/arch-shark/ide.h @@ -15,7 +15,7 @@ * This should follow whatever the default interface uses. */ static __inline__ void -ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int irq) +ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int *irq) { ide_ioreg_t reg = (ide_ioreg_t) data_port; int i; @@ -27,7 +27,8 @@ ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int irq) reg += 1; } hw->io_ports[IDE_CONTROL_OFFSET] = (ide_ioreg_t) ctrl_port; - hw->irq = irq; + if (irq) + *irq = 0; } /* @@ -39,7 +40,8 @@ ide_init_default_hwifs(void) { hw_regs_t hw; - ide_init_hwif_ports(&hw, 0x1f0, 0x3f6, 14); + ide_init_hwif_ports(&hw, 0x1f0, 0x3f6, NULL); + hw.irq = 14; ide_register_hw(&hw, NULL); } diff --git a/include/asm-arm/atomic.h b/include/asm-arm/atomic.h index d5dfe4f91..94fd17170 100644 --- a/include/asm-arm/atomic.h +++ b/include/asm-arm/atomic.h @@ -36,36 +36,36 @@ static __inline__ void atomic_add(int i, volatile atomic_t *v) { unsigned long flags; - save_flags_cli (flags); + __save_flags_cli(flags); v->counter += i; - restore_flags (flags); + __restore_flags(flags); } static __inline__ void atomic_sub(int i, volatile atomic_t *v) { unsigned long flags; - save_flags_cli (flags); + __save_flags_cli(flags); v->counter -= i; - restore_flags (flags); + __restore_flags(flags); } static __inline__ void atomic_inc(volatile atomic_t *v) { unsigned long flags; - save_flags_cli (flags); + __save_flags_cli(flags); v->counter += 1; - restore_flags (flags); + __restore_flags(flags); } static __inline__ void atomic_dec(volatile atomic_t *v) { unsigned long flags; - save_flags_cli (flags); + __save_flags_cli(flags); v->counter -= 1; - restore_flags (flags); + __restore_flags(flags); } static __inline__ int atomic_dec_and_test(volatile atomic_t *v) @@ -73,10 +73,10 @@ static __inline__ int atomic_dec_and_test(volatile atomic_t *v) unsigned long flags; int result; - save_flags_cli (flags); + __save_flags_cli(flags); v->counter -= 1; result = (v->counter == 0); - restore_flags (flags); + __restore_flags(flags); return result; } @@ -86,10 +86,10 @@ extern __inline__ int atomic_add_negative(int i, volatile atomic_t *v) unsigned long flags; int result; - save_flags_cli(flags); + __save_flags_cli(flags); v->counter += i; result = (v->counter < 0); - restore_flags(flags); + __restore_flags(flags); return result; } @@ -98,9 +98,9 @@ static __inline__ void atomic_clear_mask(unsigned long mask, unsigned long *addr { unsigned long flags; - save_flags_cli (flags); + __save_flags_cli(flags); *addr &= ~mask; - restore_flags (flags); + __restore_flags(flags); } #endif diff --git a/include/asm-arm/cpu-multi26.h b/include/asm-arm/cpu-multi26.h index c09edf8b1..50639037d 100644 --- a/include/asm-arm/cpu-multi26.h +++ b/include/asm-arm/cpu-multi26.h @@ -20,7 +20,6 @@ extern struct processor { void (*_set_pgd)(pgd_t *pgd); /* XCHG */ unsigned long (*_xchg_1)(unsigned long x, volatile void *ptr); - unsigned long (*_xchg_2)(unsigned long x, volatile void *ptr); unsigned long (*_xchg_4)(unsigned long x, volatile void *ptr); } processor; @@ -34,7 +33,6 @@ extern const struct processor arm3_processor_functions; #define cpu_do_idle() do { } while (0) #define cpu_switch_mm(pgd,tsk) processor._set_pgd(pgd) #define cpu_xchg_1(x,ptr) processor._xchg_1(x,ptr) -#define cpu_xchg_2(x,ptr) processor._xchg_2(x,ptr) #define cpu_xchg_4(x,ptr) processor._xchg_4(x,ptr) extern void cpu_memc_update_all(pgd_t *pgd); diff --git a/include/asm-arm/dma.h b/include/asm-arm/dma.h index b67e33a9d..67db9ab90 100644 --- a/include/asm-arm/dma.h +++ b/include/asm-arm/dma.h @@ -6,7 +6,8 @@ typedef unsigned int dmach_t; #include <linux/config.h> #include <linux/kernel.h> #include <linux/spinlock.h> -#include <asm/irq.h> +#include <asm/system.h> +#include <asm/memory.h> #include <asm/arch/dma.h> /* diff --git a/include/asm-arm/floppy.h b/include/asm-arm/floppy.h index 9d9848644..05a94a2e6 100644 --- a/include/asm-arm/floppy.h +++ b/include/asm-arm/floppy.h @@ -1,7 +1,9 @@ /* * linux/include/asm-arm/floppy.h * - * (C) 1996 Russell King + * (C) 1996-2000 Russell King + * + * Note that we don't touch FLOPPY_DMA nor FLOPPY_IRQ here */ #ifndef __ASM_ARM_FLOPPY_H #define __ASM_ARM_FLOPPY_H @@ -24,14 +26,14 @@ #define fd_disable_irq() disable_irq(IRQ_FLOPPYDISK) #define fd_enable_irq() enable_irq(IRQ_FLOPPYDISK) -#define fd_request_dma() request_dma(FLOPPY_DMA,"floppy") -#define fd_free_dma() free_dma(FLOPPY_DMA) -#define fd_disable_dma() disable_dma(FLOPPY_DMA) -#define fd_enable_dma() enable_dma(FLOPPY_DMA) -#define fd_clear_dma_ff() clear_dma_ff(FLOPPY_DMA) -#define fd_set_dma_mode(mode) set_dma_mode(FLOPPY_DMA, (mode)) -#define fd_set_dma_addr(addr) set_dma_addr(FLOPPY_DMA, virt_to_bus((addr))) -#define fd_set_dma_count(len) set_dma_count(FLOPPY_DMA, (len)) +#define fd_request_dma() request_dma(DMA_FLOPPY,"floppy") +#define fd_free_dma() free_dma(DMA_FLOPPY) +#define fd_disable_dma() disable_dma(DMA_FLOPPY) +#define fd_enable_dma() enable_dma(DMA_FLOPPY) +#define fd_clear_dma_ff() clear_dma_ff(DMA_FLOPPY) +#define fd_set_dma_mode(mode) set_dma_mode(DMA_FLOPPY, (mode)) +#define fd_set_dma_addr(addr) set_dma_addr(DMA_FLOPPY, virt_to_bus((addr))) +#define fd_set_dma_count(len) set_dma_count(DMA_FLOPPY, (len)) #define fd_cacheflush(addr,sz) /* need to clean up dma.h */ @@ -109,13 +111,12 @@ extern __inline__ void fd_scandrives (void) } #define FDC1 (0x3f0) -static int FDC2 = -1; #define FLOPPY0_TYPE 4 #define FLOPPY1_TYPE 4 #define N_FDC 1 -#define N_DRIVE 8 +#define N_DRIVE 4 #define FLOPPY_MOTOR_MASK 0xf0 diff --git a/include/asm-arm/io.h b/include/asm-arm/io.h index 871e50a65..fad9e7412 100644 --- a/include/asm-arm/io.h +++ b/include/asm-arm/io.h @@ -1,7 +1,7 @@ /* * linux/include/asm-arm/io.h * - * Copyright (C) 1996-1999 Russell King + * Copyright (C) 1996-2000 Russell King * * Modifications: * 16-Sep-1996 RMK Inlined the inx/outx functions & optimised for both @@ -11,6 +11,7 @@ * 27-Mar-1999 PJB Second parameter of memcpy_toio is const.. * 04-Apr-1999 PJB Added check_signature. * 12-Dec-1999 RMK More cleanups + * 18-Jun-2000 RMK Removed virt_to_* and friends definitions */ #ifndef __ASM_ARM_IO_H #define __ASM_ARM_IO_H @@ -43,23 +44,7 @@ extern void insl(unsigned int port, void *from, int len); #ifdef __KERNEL__ -#include <asm/arch/memory.h> - -extern __inline__ unsigned long virt_to_phys(volatile void *x) -{ - return __virt_to_phys((unsigned long)(x)); -} - -extern __inline__ void *phys_to_virt(unsigned long x) -{ - return (void *)(__phys_to_virt((unsigned long)(x))); -} - -/* - * Virtual <-> DMA view memory address translations - */ -#define virt_to_bus(x) (__virt_to_bus((unsigned long)(x))) -#define bus_to_virt(x) ((void *)(__bus_to_virt((unsigned long)(x)))) +#include <asm/memory.h> /* the following macro is depreciated */ #define ioaddr(port) __ioaddr((port)) diff --git a/include/asm-arm/memory.h b/include/asm-arm/memory.h new file mode 100644 index 000000000..837ea199a --- /dev/null +++ b/include/asm-arm/memory.h @@ -0,0 +1,31 @@ +/* + * linux/include/asm-arm/memory.h + * + * Copyright (C) 2000 Russell King + * + * Note: this file should not be included by non-asm/.h files + * + * Modifications: + */ +#ifndef __ASM_ARM_MEMORY_H +#define __ASM_ARM_MEMORY_H + +#include <asm/arch/memory.h> + +extern __inline__ unsigned long virt_to_phys(volatile void *x) +{ + return __virt_to_phys((unsigned long)(x)); +} + +extern __inline__ void *phys_to_virt(unsigned long x) +{ + return (void *)(__phys_to_virt((unsigned long)(x))); +} + +/* + * Virtual <-> DMA view memory address translations + */ +#define virt_to_bus(x) (__virt_to_bus((unsigned long)(x))) +#define bus_to_virt(x) ((void *)(__bus_to_virt((unsigned long)(x)))) + +#endif diff --git a/include/asm-arm/proc-armo/system.h b/include/asm-arm/proc-armo/system.h index bc113ae6e..36a3515e7 100644 --- a/include/asm-arm/proc-armo/system.h +++ b/include/asm-arm/proc-armo/system.h @@ -7,20 +7,16 @@ #ifndef __ASM_PROC_SYSTEM_H #define __ASM_PROC_SYSTEM_H -extern const char xchg_str[]; - -#include <linux/config.h> #include <asm/proc-fns.h> extern __inline__ unsigned long __xchg(unsigned long x, volatile void *ptr, int size) { - extern void arm_invalidptr(const char *, int); + extern void __bad_xchg(volatile void *, int); switch (size) { case 1: return cpu_xchg_1(x, ptr); - case 2: return cpu_xchg_2(x, ptr); case 4: return cpu_xchg_4(x, ptr); - default: arm_invalidptr(xchg_str, size); + default: __bad_xchg(ptr, size); } return 0; } @@ -108,22 +104,4 @@ extern __inline__ unsigned long __xchg(unsigned long x, volatile void *ptr, int : "memory"); \ } while (0) -/* For spinlocks etc */ -#define local_irq_save(x) __save_flags_cli(x) -#define local_irq_restore(x) __restore_flags(x) -#define local_irq_disable() __cli() -#define local_irq_enable() __sti() - -#ifdef CONFIG_SMP -#error SMP not supported -#else - -#define cli() __cli() -#define sti() __sti() -#define save_flags(x) __save_flags(x) -#define restore_flags(x) __restore_flags(x) -#define save_flags_cli(x) __save_flags_cli(x) - -#endif - #endif diff --git a/include/asm-arm/proc-armv/system.h b/include/asm-arm/proc-armv/system.h index 3b35be309..d9ef8ffd9 100644 --- a/include/asm-arm/proc-armv/system.h +++ b/include/asm-arm/proc-armv/system.h @@ -7,20 +7,16 @@ #ifndef __ASM_PROC_SYSTEM_H #define __ASM_PROC_SYSTEM_H -#include <linux/config.h> - -extern const char xchg_str[]; - extern __inline__ unsigned long __xchg(unsigned long x, volatile void *ptr, int size) { - extern void arm_invalidptr(const char *, int); + extern void __bad_xchg(volatile void *, int); switch (size) { case 1: __asm__ __volatile__ ("swpb %0, %1, [%2]" : "=r" (x) : "r" (x), "r" (ptr) : "memory"); break; case 4: __asm__ __volatile__ ("swp %0, %1, [%2]" : "=r" (x) : "r" (x), "r" (ptr) : "memory"); break; - default: arm_invalidptr(xchg_str, size); + default: __bad_xchg(ptr, size); } return x; } @@ -102,22 +98,4 @@ extern unsigned long cr_alignment; /* defined in entry-armv.S */ : "r" (x) \ : "memory") -/* For spinlocks etc */ -#define local_irq_save(x) __save_flags_cli(x) -#define local_irq_restore(x) __restore_flags(x) -#define local_irq_disable() __cli() -#define local_irq_enable() __sti() - -#ifdef CONFIG_SMP -#error SMP not supported -#else - -#define cli() __cli() -#define sti() __sti() -#define save_flags(x) __save_flags(x) -#define restore_flags(x) __restore_flags(x) -#define save_flags_cli(x) __save_flags_cli(x) - -#endif - #endif diff --git a/include/asm-arm/ptrace.h b/include/asm-arm/ptrace.h index 961363cc7..3126767a4 100644 --- a/include/asm-arm/ptrace.h +++ b/include/asm-arm/ptrace.h @@ -17,6 +17,10 @@ #ifdef __KERNEL__ extern void show_regs(struct pt_regs *); + +#define predicate(x) (x & 0xf0000000) +#define PREDICATE_ALWAYS 0xe0000000 + #endif #endif /* __ASSEMBLY__ */ diff --git a/include/asm-arm/system.h b/include/asm-arm/system.h index b75a88411..94073475d 100644 --- a/include/asm-arm/system.h +++ b/include/asm-arm/system.h @@ -381,7 +381,6 @@ extern unsigned int __machine_arch_type; #define tas(ptr) (xchg((ptr),1)) -extern void arm_malalignedptr(const char *, void *, volatile void *); extern asmlinkage void __backtrace(void); /* @@ -411,4 +410,22 @@ extern struct task_struct *__switch_to(struct task_struct *prev, struct task_str #endif +/* For spinlocks etc */ +#define local_irq_save(x) __save_flags_cli(x) +#define local_irq_restore(x) __restore_flags(x) +#define local_irq_disable() __cli() +#define local_irq_enable() __sti() + +#ifdef CONFIG_SMP +#error SMP not supported +#else + +#define cli() __cli() +#define sti() __sti() +#define save_flags(x) __save_flags(x) +#define restore_flags(x) __restore_flags(x) +#define save_flags_cli(x) __save_flags_cli(x) + +#endif + #endif diff --git a/include/asm-ppc/unistd.h b/include/asm-ppc/unistd.h index d4609a9a8..4c64c151a 100644 --- a/include/asm-ppc/unistd.h +++ b/include/asm-ppc/unistd.h @@ -194,7 +194,7 @@ #define __NR_getpmsg 187 /* some people actually want streams */ #define __NR_putpmsg 188 /* some people actually want streams */ #define __NR_vfork 189 -#define __NR_ugetrlimit 191 /* SuS compliant getrlimit */ +#define __NR_ugetrlimit 190 /* SuS compliant getrlimit */ #define __NR_mmap2 192 #define __NR_truncate64 193 #define __NR_ftruncate64 194 diff --git a/include/linux/affs_fs.h b/include/linux/affs_fs.h index 60af0d40c..8a09cd72d 100644 --- a/include/linux/affs_fs.h +++ b/include/linux/affs_fs.h @@ -84,7 +84,7 @@ extern int affs_add_entry(struct inode *dir, struct inode *link, extern void affs_put_inode(struct inode *inode); extern void affs_delete_inode(struct inode *inode); extern void affs_read_inode(struct inode *inode); -extern void affs_write_inode(struct inode *inode); +extern void affs_write_inode(struct inode *inode, int); /* super.c */ diff --git a/include/linux/arcdevice.h b/include/linux/arcdevice.h index b5a9999a5..d2334ba95 100644 --- a/include/linux/arcdevice.h +++ b/include/linux/arcdevice.h @@ -241,11 +241,13 @@ struct arcnet_local { setup2, /* Contents of setup2 register */ intmask; /* current value of INTMASK register */ uint8_t default_proto[256]; /* default encap to use for each host */ - int cur_tx, /* buffer used by current transmit, or -1 */ + int cur_tx, /* buffer used by current transmit, or -1 */ next_tx, /* buffer where a packet is ready to send */ cur_rx; /* current receive buffer */ - int lastload_dest, /* can last loaded packet be acked? */ + int lastload_dest, /* can last loaded packet be acked? */ lasttrans_dest; /* can last TX'd packet be acked? */ + int timed_out; /* need to process TX timeout and drop packet */ + unsigned long last_timeout; /* time of last reported timeout */ char *card_name; /* card ident string */ int card_flags; /* special card features */ diff --git a/include/linux/coda_psdev.h b/include/linux/coda_psdev.h index 2b6bbadd7..73757bb53 100644 --- a/include/linux/coda_psdev.h +++ b/include/linux/coda_psdev.h @@ -11,9 +11,8 @@ extern struct coda_sb_info coda_super_info; struct coda_sb_info { struct inode * sbi_psdev; /* /dev/cfs? Venus/kernel device */ - struct inode * sbi_ctlcp; /* control magic file */ int sbi_refct; - struct venus_comm * sbi_vcomm; + struct venus_comm * sbi_vcomm; struct inode * sbi_root; struct super_block *sbi_sb; struct list_head sbi_cchead; @@ -27,7 +26,6 @@ struct venus_comm { struct list_head vc_pending; struct list_head vc_processing; int vc_inuse; - pid_t vc_pid; /* Venus pid */ }; diff --git a/include/linux/ext2_fs.h b/include/linux/ext2_fs.h index 25178b66b..af962e94f 100644 --- a/include/linux/ext2_fs.h +++ b/include/linux/ext2_fs.h @@ -548,7 +548,7 @@ extern int ext2_read (struct inode *, struct file *, char *, int); extern int ext2_write (struct inode *, struct file *, char *, int); /* fsync.c */ -extern int ext2_sync_file (struct file *, struct dentry *); +extern int ext2_sync_file (struct file *, struct dentry *, int); /* ialloc.c */ extern struct inode * ext2_new_inode (const struct inode *, int, int *); @@ -562,7 +562,7 @@ extern struct buffer_head * ext2_getblk (struct inode *, long, int, int *); extern struct buffer_head * ext2_bread (struct inode *, int, int, int *); extern void ext2_read_inode (struct inode *); -extern void ext2_write_inode (struct inode *); +extern void ext2_write_inode (struct inode *, int); extern void ext2_put_inode (struct inode *); extern void ext2_delete_inode (struct inode *); extern int ext2_sync_inode (struct inode *); diff --git a/include/linux/fs.h b/include/linux/fs.h index 8a1f8e9b6..b30bb8a08 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -47,7 +47,12 @@ struct poll_table_struct; #define BLOCK_SIZE (1<<BLOCK_SIZE_BITS) /* And dynamically-tunable limits and defaults: */ -extern int max_files, nr_files, nr_free_files; +struct files_stat_struct { + int nr_files; /* read only */ + int nr_free_files; /* read only */ + int max_files; /* tunable */ +}; +extern struct files_stat_struct files_stat; extern int max_super_blocks, nr_super_blocks; #define NR_FILE 8192 /* this can well be larger on a larger system */ @@ -84,6 +89,7 @@ extern int max_super_blocks, nr_super_blocks; * kernel-wide vfsmnt is kept in ->kern_mnt. */ #define FS_NOMOUNT 16 /* Never mount from userland */ +#define FS_LITTER 32 /* Keeps the tree in dcache */ /* * These are the fs-independent mount-flags: up to 16 flags are supported */ @@ -504,10 +510,8 @@ typedef struct files_struct *fl_owner_t; struct file_lock { struct file_lock *fl_next; /* singly linked list for this inode */ - struct file_lock *fl_nextlink; /* doubly linked list of all locks */ - struct file_lock *fl_prevlink; /* used to simplify lock removal */ - struct file_lock *fl_nextblock; /* circular list of blocked processes */ - struct file_lock *fl_prevblock; + struct list_head fl_link; /* doubly linked list of all locks */ + struct list_head fl_block; /* circular list of blocked processes */ fl_owner_t fl_owner; unsigned int fl_pid; wait_queue_head_t fl_wait; @@ -532,7 +536,7 @@ struct file_lock { #define OFFSET_MAX INT_LIMIT(loff_t) #endif -extern struct file_lock *file_lock_table; +extern struct list_head file_lock_list; #include <linux/fcntl.h> @@ -721,7 +725,7 @@ struct file_operations { int (*open) (struct inode *, struct file *); int (*flush) (struct file *); int (*release) (struct inode *, struct file *); - int (*fsync) (struct file *, struct dentry *); + int (*fsync) (struct file *, struct dentry *, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *); @@ -754,7 +758,7 @@ struct inode_operations { */ struct super_operations { void (*read_inode) (struct inode *); - void (*write_inode) (struct inode *); + void (*write_inode) (struct inode *, int); void (*put_inode) (struct inode *); void (*delete_inode) (struct inode *); void (*put_super) (struct super_block *); @@ -859,7 +863,8 @@ static inline int locks_verify_truncate(struct inode *inode, return locks_mandatory_area( FLOCK_VERIFY_WRITE, inode, filp, size < inode->i_size ? size : inode->i_size, - abs(inode->i_size - size) + (size < inode->i_size ? inode->i_size - size + : size - inode->i_size) ); return 0; } @@ -989,7 +994,7 @@ extern void invalidate_inode_pages(struct inode *); #define destroy_buffers(dev) __invalidate_buffers((dev), 1) extern void __invalidate_buffers(kdev_t dev, int); extern void sync_inodes(kdev_t); -extern void write_inode_now(struct inode *); +extern void write_inode_now(struct inode *, int); extern void sync_dev(kdev_t); extern int fsync_dev(kdev_t); extern void sync_supers(kdev_t); @@ -997,7 +1002,16 @@ extern int bmap(struct inode *, int); extern int notify_change(struct dentry *, struct iattr *); extern int permission(struct inode *, int); extern int get_write_access(struct inode *); -extern void put_write_access(struct inode *); +extern int deny_write_access(struct file *); +static inline void put_write_access(struct inode * inode) +{ + atomic_dec(&inode->i_writecount); +} +static inline void allow_write_access(struct file *file) +{ + if (file) + atomic_inc(&file->f_dentry->d_inode->i_writecount); +} extern int do_pipe(int *); extern int open_namei(const char *, int, int, struct nameidata *); @@ -1037,7 +1051,7 @@ extern ino_t find_inode_number(struct dentry *, struct qstr *); /* * Type of the last component on LOOKUP_PARENT */ -enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT }; +enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND}; /* * "descriptor" for what we're up to with a read for sendfile(). @@ -1148,6 +1162,7 @@ extern struct inode_operations page_symlink_inode_operations; extern int vfs_readdir(struct file *, filldir_t, void *); extern int dcache_readdir(struct file *, void *, filldir_t); +extern struct file_system_type *get_fs_type(const char *name); extern struct super_block *get_super(kdev_t); struct super_block *get_empty_super(void); extern void put_super(kdev_t); @@ -1172,7 +1187,7 @@ extern int read_ahead[]; extern ssize_t char_write(struct file *, const char *, size_t, loff_t *); extern ssize_t block_write(struct file *, const char *, size_t, loff_t *); -extern int file_fsync(struct file *, struct dentry *); +extern int file_fsync(struct file *, struct dentry *, int); extern int generic_buffer_fdatasync(struct inode *inode, unsigned long start_idx, unsigned long end_idx); extern int inode_change_ok(struct inode *, struct iattr *); @@ -1186,20 +1201,6 @@ extern void inode_setattr(struct inode *, struct iattr *); */ /* - * We need to do a check-parent every time - * after we have locked the parent - to verify - * that the parent is still our parent and - * that we are still hashed onto it.. - * - * This is required in case two processes race - * on removing (or moving) the same entry: the - * parent lock will serialize them, but the - * other process will be too late.. - */ -#define check_parent(dir, dentry) \ - ((dir) == (dentry)->d_parent && !d_unhashed(dentry)) - -/* * Locking the parent is needed to: * - serialize directory operations * - make sure the parent doesn't change from diff --git a/include/linux/kernelcapi.h b/include/linux/kernelcapi.h index 250191081..b9efcb696 100644 --- a/include/linux/kernelcapi.h +++ b/include/linux/kernelcapi.h @@ -58,12 +58,8 @@ #ifndef __KERNELCAPI_H__ #define __KERNELCAPI_H__ -#define CAPI_MAXAPPL 20 /* - * maximum number of applications - */ -#define CAPI_MAXCONTR 10 /* - * maximum number of controller - */ +#define CAPI_MAXAPPL 128 /* maximum number of applications */ +#define CAPI_MAXCONTR 16 /* maximum number of controller */ #define CAPI_MAXDATAWINDOW 8 diff --git a/include/linux/minix_fs.h b/include/linux/minix_fs.h index 1eca767b4..99cd63717 100644 --- a/include/linux/minix_fs.h +++ b/include/linux/minix_fs.h @@ -101,7 +101,7 @@ extern struct buffer_head * minix_bread(struct inode *, int, int); extern void minix_truncate(struct inode *); extern int minix_sync_inode(struct inode *); -extern int minix_sync_file(struct file *, struct dentry *); +extern int minix_sync_file(struct file *, struct dentry *, int); extern struct address_space_operations minix_aops; extern struct inode_operations minix_file_inode_operations; diff --git a/include/linux/mm.h b/include/linux/mm.h index 37cb9664e..e6325a298 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -185,6 +185,7 @@ typedef struct page { #define ClearPageUptodate(page) clear_bit(PG_uptodate, &(page)->flags) #define PageDirty(page) test_bit(PG_dirty, &(page)->flags) #define SetPageDirty(page) set_bit(PG_dirty, &(page)->flags) +#define ClearPageDirty(page) clear_bit(PG_dirty, &(page)->flags) #define PageLocked(page) test_bit(PG_locked, &(page)->flags) #define LockPage(page) set_bit(PG_locked, &(page)->flags) #define TryLockPage(page) test_and_set_bit(PG_locked, &(page)->flags) diff --git a/include/linux/mount.h b/include/linux/mount.h index 61ab19b1f..adb571de2 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h @@ -26,7 +26,6 @@ struct vfsmount atomic_t mnt_count; char *mnt_devname; /* Name of device e.g. /dev/dsk/hda1 */ - char *mnt_dirname; /* Name of directory mounted on */ struct list_head mnt_list; uid_t mnt_owner; }; diff --git a/include/linux/msdos_fs.h b/include/linux/msdos_fs.h index 4897f2ec9..cbbf78528 100644 --- a/include/linux/msdos_fs.h +++ b/include/linux/msdos_fs.h @@ -248,7 +248,7 @@ extern struct inode *fat_build_inode(struct super_block*,struct msdos_dir_entry* extern struct super_block *fat_read_super(struct super_block *s, void *data, int silent, struct inode_operations *dir_ops); extern void msdos_put_super(struct super_block *sb); extern int fat_statfs(struct super_block *sb,struct statfs *buf); -extern void fat_write_inode(struct inode *inode); +extern void fat_write_inode(struct inode *inode, int); /* dir.c */ extern struct file_operations fat_dir_operations; diff --git a/include/linux/ncp_fs_i.h b/include/linux/ncp_fs_i.h index 96728bcdd..ffdf49f4c 100644 --- a/include/linux/ncp_fs_i.h +++ b/include/linux/ncp_fs_i.h @@ -19,7 +19,8 @@ struct ncp_inode_info { __u32 DosDirNum __attribute__((packed)); __u32 volNumber __attribute__((packed)); __u32 nwattr; - int opened; + struct semaphore open_sem; + atomic_t opened; int access; __u32 server_file_handle __attribute__((packed)); __u8 open_create_action __attribute__((packed)); diff --git a/include/linux/nfsd/cache.h b/include/linux/nfsd/cache.h index 8e675705e..ae2da13be 100644 --- a/include/linux/nfsd/cache.h +++ b/include/linux/nfsd/cache.h @@ -25,9 +25,11 @@ struct svc_cacherep { unsigned char c_state, /* unused, inprog, done */ c_type, /* status, buffer */ c_secure : 1; /* req came from port < 1024 */ - struct in_addr c_client; + struct sockaddr_in c_addr; u32 c_xid; + u32 c_prot; u32 c_proc; + u32 c_vers; unsigned long c_timestamp; union { struct svc_buf u_buffer; diff --git a/include/linux/nfsd/export.h b/include/linux/nfsd/export.h index 06a21296f..5fb55c738 100644 --- a/include/linux/nfsd/export.h +++ b/include/linux/nfsd/export.h @@ -95,18 +95,6 @@ int nfserrno(int errno); void exp_nlmdetach(void); -extern __inline__ int -exp_checkaddr(struct svc_client *clp, struct in_addr addr) -{ - struct in_addr *ap = clp->cl_addr; - int i; - - for (i = clp->cl_naddr; i--; ap++) - if (ap->s_addr == addr.s_addr) - return 1; - return 0; -} - #endif /* __KERNEL__ */ #endif /* NFSD_EXPORT_H */ diff --git a/include/linux/nfsd/nfsd.h b/include/linux/nfsd/nfsd.h index 42663e79b..26e8edd22 100644 --- a/include/linux/nfsd/nfsd.h +++ b/include/linux/nfsd/nfsd.h @@ -80,19 +80,19 @@ int nfsd_racache_init(int); void nfsd_racache_shutdown(void); int nfsd_lookup(struct svc_rqst *, struct svc_fh *, const char *, int, struct svc_fh *); -#ifdef CONFIG_NFSD_V3 -int nfsd_access(struct svc_rqst *, struct svc_fh *, u32 *); -#endif /* CONFIG_NFSD_V3 */ int nfsd_setattr(struct svc_rqst *, struct svc_fh *, struct iattr *); int nfsd_create(struct svc_rqst *, struct svc_fh *, char *name, int len, struct iattr *attrs, int type, dev_t rdev, struct svc_fh *res); #ifdef CONFIG_NFSD_V3 +int nfsd_access(struct svc_rqst *, struct svc_fh *, u32 *); int nfsd_create_v3(struct svc_rqst *, struct svc_fh *, char *name, int len, struct iattr *attrs, struct svc_fh *res, int createmode, u32 *verifier); +int nfsd_commit(struct svc_rqst *, struct svc_fh *, + off_t, unsigned long); #endif /* CONFIG_NFSD_V3 */ int nfsd_open(struct svc_rqst *, struct svc_fh *, int, int, struct file *); @@ -122,10 +122,7 @@ int nfsd_readdir(struct svc_rqst *, struct svc_fh *, u32 *buffer, int *countp, u32 *verf); int nfsd_statfs(struct svc_rqst *, struct svc_fh *, struct statfs *); -#ifdef CONFIG_NFSD_V3 -int nfsd_commit(struct svc_rqst *, struct svc_fh *, - off_t, unsigned long); -#endif /* CONFIG_NFSD_V3 */ + int nfsd_notify_change(struct inode *, struct iattr *); int nfsd_permission(struct svc_export *, struct dentry *, int); diff --git a/include/linux/nfsd/nfsfh.h b/include/linux/nfsd/nfsfh.h index 83320b810..39ab97f14 100644 --- a/include/linux/nfsd/nfsfh.h +++ b/include/linux/nfsd/nfsfh.h @@ -90,7 +90,7 @@ struct nfs_fhbase_new { }; struct knfsd_fh { - int fh_size; /* significant for NFSv3. + unsigned int fh_size; /* significant for NFSv3. * Points to the current size while building * a new file handle */ @@ -149,14 +149,13 @@ typedef struct svc_fh { struct dentry * fh_dentry; /* validated dentry */ struct svc_export * fh_export; /* export pointer */ int fh_maxsize; /* max size for fh_handle */ + + unsigned char fh_locked; /* inode locked by us */ + #ifdef CONFIG_NFSD_V3 unsigned char fh_post_saved; /* post-op attrs saved */ unsigned char fh_pre_saved; /* pre-op attrs saved */ -#endif /* CONFIG_NFSD_V3 */ - unsigned char fh_locked; /* inode locked by us */ - unsigned char fh_dverified; /* dentry has been checked */ -#ifdef CONFIG_NFSD_V3 /* Pre-op attributes saved during fh_lock */ __u64 fh_pre_size; /* size before operation */ time_t fh_pre_mtime; /* mtime before oper */ @@ -207,7 +206,7 @@ void fh_put(struct svc_fh *); static __inline__ struct svc_fh * fh_copy(struct svc_fh *dst, struct svc_fh *src) { - if (src->fh_dverified || src->fh_locked) { + if (src->fh_dentry || src->fh_locked) { struct dentry *dentry = src->fh_dentry; printk(KERN_ERR "fh_copy: copying %s/%s, already verified!\n", dentry->d_parent->d_name.name, dentry->d_name.name); @@ -241,7 +240,6 @@ fill_pre_wcc(struct svc_fh *fhp) fhp->fh_pre_size = inode->i_size; fhp->fh_pre_saved = 1; } - fhp->fh_locked = 1; } /* @@ -273,13 +271,18 @@ fill_post_wcc(struct svc_fh *fhp) fhp->fh_post_mtime = inode->i_mtime; fhp->fh_post_ctime = inode->i_ctime; fhp->fh_post_saved = 1; - fhp->fh_locked = 0; } +#else +#define fill_pre_wcc(ignored) +#define fill_post_wcc(notused) #endif /* CONFIG_NFSD_V3 */ /* * Lock a file handle/inode + * NOTE: both fh_lock and fh_unlock are done "by hand" in + * vfs.c:nfsd_rename as it needs to grab 2 i_sem's at once + * so, any changes here should be reflected there. */ static inline void fh_lock(struct svc_fh *fhp) @@ -290,7 +293,7 @@ fh_lock(struct svc_fh *fhp) dfprintk(FILEOP, "nfsd: fh_lock(%s) locked = %d\n", SVCFH_fmt(fhp), fhp->fh_locked); - if (!fhp->fh_dverified) { + if (!fhp->fh_dentry) { printk(KERN_ERR "fh_lock: fh not verified!\n"); return; } @@ -302,11 +305,8 @@ fh_lock(struct svc_fh *fhp) inode = dentry->d_inode; down(&inode->i_sem); -#ifdef CONFIG_NFSD_V3 fill_pre_wcc(fhp); -#else fhp->fh_locked = 1; -#endif /* CONFIG_NFSD_V3 */ } /* @@ -315,20 +315,13 @@ fh_lock(struct svc_fh *fhp) static inline void fh_unlock(struct svc_fh *fhp) { - if (!fhp->fh_dverified) + if (!fhp->fh_dentry) printk(KERN_ERR "fh_unlock: fh not verified!\n"); if (fhp->fh_locked) { -#ifdef CONFIG_NFSD_V3 fill_post_wcc(fhp); up(&fhp->fh_dentry->d_inode->i_sem); -#else - struct dentry *dentry = fhp->fh_dentry; - struct inode *inode = dentry->d_inode; - fhp->fh_locked = 0; - up(&inode->i_sem); -#endif /* CONFIG_NFSD_V3 */ } } #endif /* __KERNEL__ */ diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 5512d2a51..04ae938be 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -330,13 +330,14 @@ #define PCI_DEVICE_ID_SI_6205 0x0205 #define PCI_DEVICE_ID_SI_501 0x0406 #define PCI_DEVICE_ID_SI_496 0x0496 -#define PCI_DEVICE_ID_SI_300 0x0300 +#define PCI_DEVICE_ID_SI_300 0x0300 #define PCI_DEVICE_ID_SI_530 0x0530 -#define PCI_DEVICE_ID_SI_540 0x5300 +#define PCI_DEVICE_ID_SI_540 0x0540 #define PCI_DEVICE_ID_SI_601 0x0601 #define PCI_DEVICE_ID_SI_620 0x0620 -#define PCI_DEVICE_ID_SI_630 0x6300 +#define PCI_DEVICE_ID_SI_630 0x0630 #define PCI_DEVICE_ID_SI_5107 0x5107 +#define PCI_DEVICE_ID_SI_5300 0x5300 #define PCI_DEVICE_ID_SI_5511 0x5511 #define PCI_DEVICE_ID_SI_5513 0x5513 #define PCI_DEVICE_ID_SI_5571 0x5571 @@ -344,6 +345,7 @@ #define PCI_DEVICE_ID_SI_5597 0x5597 #define PCI_DEVICE_ID_SI_5598 0x5598 #define PCI_DEVICE_ID_SI_5600 0x5600 +#define PCI_DEVICE_ID_SI_6300 0x6300 #define PCI_DEVICE_ID_SI_6306 0x6306 #define PCI_DEVICE_ID_SI_6326 0x6326 #define PCI_DEVICE_ID_SI_7001 0x7001 diff --git a/include/linux/qnx4_fs.h b/include/linux/qnx4_fs.h index ad5ca5543..dd9b7cb6e 100644 --- a/include/linux/qnx4_fs.h +++ b/include/linux/qnx4_fs.h @@ -116,7 +116,7 @@ extern void qnx4_truncate(struct inode *inode); extern void qnx4_free_inode(struct inode *inode); extern int qnx4_unlink(struct inode *dir, struct dentry *dentry); extern int qnx4_rmdir(struct inode *dir, struct dentry *dentry); -extern int qnx4_sync_file(struct file *file, struct dentry *dentry); +extern int qnx4_sync_file(struct file *file, struct dentry *dentry, int); extern int qnx4_sync_inode(struct inode *inode); extern int qnx4_get_block(struct inode *inode, long iblock, struct buffer_head *bh, int create); diff --git a/include/linux/sysv_fs.h b/include/linux/sysv_fs.h index d9c2557e5..4cff4260c 100644 --- a/include/linux/sysv_fs.h +++ b/include/linux/sysv_fs.h @@ -377,9 +377,9 @@ extern unsigned long sysv_count_free_blocks(struct super_block *sb); extern struct buffer_head * sysv_file_bread(struct inode *, int, int); extern void sysv_truncate(struct inode *); -extern void sysv_write_inode(struct inode *); +extern void sysv_write_inode(struct inode *, int); extern int sysv_sync_inode(struct inode *); -extern int sysv_sync_file(struct file *, struct dentry *); +extern int sysv_sync_file(struct file *, struct dentry *, int); extern int sysv_notify_change(struct dentry *, struct iattr *); extern struct inode_operations sysv_file_inode_operations; diff --git a/include/linux/timer.h b/include/linux/timer.h index 2de8050ea..9d6b8c3a2 100644 --- a/include/linux/timer.h +++ b/include/linux/timer.h @@ -52,9 +52,10 @@ struct timer_list { unsigned long expires; unsigned long data; void (*function)(unsigned long); - volatile int running; + unsigned long sequence; }; +extern volatile unsigned long timer_sequence; extern void add_timer(struct timer_list * timer); extern int del_timer(struct timer_list * timer); @@ -71,7 +72,7 @@ static inline void init_timer(struct timer_list * timer) { timer->list.next = timer->list.prev = NULL; #ifdef CONFIG_SMP - timer->running = 0; + timer->sequence = timer_sequence-1; #endif } @@ -81,17 +82,17 @@ static inline int timer_pending (const struct timer_list * timer) } #ifdef CONFIG_SMP -#define timer_exit(t) do { (t)->running = 0; mb(); } while (0) -#define timer_set_running(t) do { (t)->running = 1; mb(); } while (0) -#define timer_is_running(t) ((t)->running != 0) +#define timer_enter(t) do { (t)->sequence = timer_sequence; mb(); } while (0) +#define timer_exit() do { timer_sequence++; } while (0) +#define timer_is_running(t) ((t)->sequence == timer_sequence) #define timer_synchronize(t) while (timer_is_running(t)) barrier() extern int del_timer_sync(struct timer_list * timer); #else -#define timer_exit(t) (void)(t) -#define timer_set_running(t) (void)(t) -#define timer_is_running(t) (0) -#define timer_synchronize(t) do { (void)(t); barrier(); } while(0) -#define del_timer_sync(t) del_timer(t) +#define timer_enter(t) do { } while (0) +#define timer_exit() do { } while (0) +#define timer_is_running(t) (0) +#define timer_synchronize(t) do { (void)(t); barrier(); } while(0) +#define del_timer_sync(t) del_timer(t) #endif /* diff --git a/include/linux/ufs_fs.h b/include/linux/ufs_fs.h index 96cb38e67..3c8f1d415 100644 --- a/include/linux/ufs_fs.h +++ b/include/linux/ufs_fs.h @@ -560,9 +560,8 @@ extern struct inode * ufs_new_inode (const struct inode *, int, int *); extern int ufs_frag_map (struct inode *, int); extern void ufs_read_inode (struct inode *); extern void ufs_put_inode (struct inode *); -extern void ufs_write_inode (struct inode *); +extern void ufs_write_inode (struct inode *, int); extern int ufs_sync_inode (struct inode *); -extern void ufs_write_inode (struct inode *); extern void ufs_delete_inode (struct inode *); extern struct buffer_head * ufs_getfrag (struct inode *, unsigned, int, int *); extern struct buffer_head * ufs_bread (struct inode *, unsigned, int, int *); diff --git a/include/linux/umsdos_fs.p b/include/linux/umsdos_fs.p index 76436a479..677bee22e 100644 --- a/include/linux/umsdos_fs.p +++ b/include/linux/umsdos_fs.p @@ -48,7 +48,7 @@ int umsdos_isempty (struct dentry *); /* inode.c 12/06/95 09.49.40 */ void fill_new_filp (struct file *filp, struct dentry *dentry); void UMSDOS_read_inode (struct inode *); -void UMSDOS_write_inode (struct inode *); +void UMSDOS_write_inode (struct inode *, int); int UMSDOS_notify_change (struct dentry *, struct iattr *attr); int umsdos_notify_change_locked(struct dentry *, struct iattr *attr); void UMSDOS_put_inode (struct inode *); @@ -52,7 +52,7 @@ static struct super_block *shm_read_super(struct super_block *,void *, int); static void shm_put_super (struct super_block *); static int shm_remount_fs (struct super_block *, int *, char *); static void shm_read_inode (struct inode *); -static void shm_write_inode(struct inode *); +static void shm_write_inode(struct inode *, int); static int shm_statfs (struct super_block *, struct statfs *); static int shm_create (struct inode *,struct dentry *,int); static struct dentry *shm_lookup (struct inode *,struct dentry *); @@ -371,7 +371,7 @@ static int shm_statfs(struct super_block *sb, struct statfs *buf) return 0; } -static void shm_write_inode(struct inode * inode) +static void shm_write_inode(struct inode * inode, int sync) { } @@ -1490,7 +1490,7 @@ static int shm_swap_core(struct shmid_kernel *shp, unsigned long idx, swp_entry_ return RETRY; if (shp->id != zero_id) swap_attempts++; - if (--counter < 0) /* failed */ + if (--*counter < 0) /* failed */ return FAILED; if (page_count(page_map) != 1) return RETRY; diff --git a/kernel/exec_domain.c b/kernel/exec_domain.c index 3f3b5fc16..1daf64cc1 100644 --- a/kernel/exec_domain.c +++ b/kernel/exec_domain.c @@ -104,32 +104,37 @@ int unregister_exec_domain(struct exec_domain *it) void __set_personality(unsigned long personality) { - struct exec_domain *it; + struct exec_domain *it, *prev; it = lookup_exec_domain(personality); - if (it) { - if (atomic_read(¤t->fs->count) != 1) { - struct fs_struct *new = copy_fs_struct(current->fs); - struct fs_struct *old; - if (!new) { - put_exec_domain(it); - return; - } - task_lock(current); - old = current->fs; - current->fs = new; - task_unlock(current); - put_fs_struct(old); - } - /* - * At that point we are guaranteed to be the sole owner of - * current->fs. - */ + if (it == current->exec_domain) { current->personality = personality; - current->exec_domain = it; - set_fs_altroot(); - put_exec_domain(current->exec_domain); + return; + } + if (!it) + return; + if (atomic_read(¤t->fs->count) != 1) { + struct fs_struct *new = copy_fs_struct(current->fs); + struct fs_struct *old; + if (!new) { + put_exec_domain(it); + return; + } + task_lock(current); + old = current->fs; + current->fs = new; + task_unlock(current); + put_fs_struct(old); } + /* + * At that point we are guaranteed to be the sole owner of + * current->fs. + */ + current->personality = personality; + prev = current->exec_domain; + current->exec_domain = it; + set_fs_altroot(); + put_exec_domain(prev); } asmlinkage long sys_personality(unsigned long personality) diff --git a/kernel/itimer.c b/kernel/itimer.c index 6c38477be..79d58220c 100644 --- a/kernel/itimer.c +++ b/kernel/itimer.c @@ -103,7 +103,6 @@ void it_real_fn(unsigned long __data) p->real_timer.expires = jiffies + interval; add_timer(&p->real_timer); } - timer_exit(&p->real_timer); } int do_setitimer(int which, struct itimerval *value, struct itimerval *ovalue) diff --git a/kernel/ksyms.c b/kernel/ksyms.c index f1032de30..2189d87a4 100644 --- a/kernel/ksyms.c +++ b/kernel/ksyms.c @@ -130,6 +130,7 @@ EXPORT_SYMBOL(highmem_start_page); /* filesystem internal functions */ EXPORT_SYMBOL(def_blk_fops); EXPORT_SYMBOL(update_atime); +EXPORT_SYMBOL(get_fs_type); EXPORT_SYMBOL(get_super); EXPORT_SYMBOL(get_empty_super); EXPORT_SYMBOL(getname); @@ -205,7 +206,7 @@ EXPORT_SYMBOL(generic_ro_fops); EXPORT_SYMBOL(generic_buffer_fdatasync); EXPORT_SYMBOL(page_hash_bits); EXPORT_SYMBOL(page_hash_table); -EXPORT_SYMBOL(file_lock_table); +EXPORT_SYMBOL(file_lock_list); EXPORT_SYMBOL(posix_lock_file); EXPORT_SYMBOL(posix_test_lock); EXPORT_SYMBOL(posix_block_lock); @@ -213,7 +214,6 @@ EXPORT_SYMBOL(posix_unblock_lock); EXPORT_SYMBOL(locks_mandatory_area); EXPORT_SYMBOL(dput); EXPORT_SYMBOL(have_submounts); -EXPORT_SYMBOL(d_genocide); EXPORT_SYMBOL(d_find_alias); EXPORT_SYMBOL(d_prune_aliases); EXPORT_SYMBOL(prune_dcache); @@ -502,7 +502,6 @@ EXPORT_SYMBOL(disk_name); /* for md.c */ /* binfmt_aout */ EXPORT_SYMBOL(get_write_access); -EXPORT_SYMBOL(put_write_access); /* dynamic registering of consoles */ EXPORT_SYMBOL(register_console); diff --git a/kernel/module.c b/kernel/module.c index c0c5c9053..5e5fbfe1b 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -326,10 +326,11 @@ sys_init_module(const char *name_user, struct module *mod_user) /* Initialize the module. */ mod->flags |= MOD_INITIALIZING; atomic_set(&mod->uc.usecount,1); - if (mod->init && mod->init() != 0) { + if (mod->init && (error = mod->init()) != 0) { atomic_set(&mod->uc.usecount,0); mod->flags &= ~MOD_INITIALIZING; - error = -EBUSY; + if (error > 0) /* Buggy module */ + error = -EBUSY; goto err0; } atomic_dec(&mod->uc.usecount); diff --git a/kernel/sched.c b/kernel/sched.c index f85cc4213..fa30b0645 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -60,8 +60,8 @@ struct task_struct * init_tasks[NR_CPUS] = {&init_task, }; * The run-queue lock locks the parts that actually access * and change the run-queues, and have to be interrupt-safe. */ -__cacheline_aligned spinlock_t runqueue_lock = SPIN_LOCK_UNLOCKED; /* second */ -__cacheline_aligned rwlock_t tasklist_lock = RW_LOCK_UNLOCKED; /* third */ +spinlock_t runqueue_lock __cacheline_aligned = SPIN_LOCK_UNLOCKED; /* second */ +rwlock_t tasklist_lock __cacheline_aligned = RW_LOCK_UNLOCKED; /* third */ static LIST_HEAD(runqueue_head); diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 5fc0418f9..ab62787d1 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -255,9 +255,9 @@ static ctl_table fs_table[] = { 0444, NULL, &proc_dointvec}, {FS_STATINODE, "inode-state", &inodes_stat, 7*sizeof(int), 0444, NULL, &proc_dointvec}, - {FS_NRFILE, "file-nr", &nr_files, 3*sizeof(int), + {FS_NRFILE, "file-nr", &files_stat, 3*sizeof(int), 0444, NULL, &proc_dointvec}, - {FS_MAXFILE, "file-max", &max_files, sizeof(int), + {FS_MAXFILE, "file-max", &files_stat.max_files, sizeof(int), 0644, NULL, &proc_dointvec}, {FS_NRSUPER, "super-nr", &nr_super_blocks, sizeof(int), 0444, NULL, &proc_dointvec}, diff --git a/kernel/timer.c b/kernel/timer.c index 9fa35a63b..873fef479 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -160,7 +160,9 @@ static inline void internal_add_timer(struct timer_list *timer) list_add(&timer->list, vec->prev); } +/* Initialize both explicitly - let's try to have them in the same cache line */ spinlock_t timerlist_lock = SPIN_LOCK_UNLOCKED; +volatile unsigned long timer_sequence = 0xfee1bad; void add_timer(struct timer_list *timer) { @@ -233,11 +235,12 @@ int del_timer_sync(struct timer_list * timer) spin_lock_irqsave(&timerlist_lock, flags); ret += detach_timer(timer); timer->list.next = timer->list.prev = 0; - running = timer->running; + running = timer_is_running(timer); spin_unlock_irqrestore(&timerlist_lock, flags); if (!running) - return ret; + break; + timer_synchronize(timer); } @@ -295,10 +298,11 @@ repeat: detach_timer(timer); timer->list.next = timer->list.prev = NULL; - timer_set_running(timer); + timer_enter(timer); spin_unlock_irq(&timerlist_lock); fn(data); spin_lock_irq(&timerlist_lock); + timer_exit(); goto repeat; } ++timer_jiffies; @@ -577,27 +581,32 @@ void update_one_process(struct task_struct *p, do_it_prof(p, ticks); } -static void update_process_times(unsigned long ticks, unsigned long system) +/* + * Called from the timer interrupt handler to charge one tick to the current + * process. user_tick is 1 if the tick is user time, 0 for system. + */ +static void update_process_times(int user_tick) { /* * SMP does this on a per-CPU basis elsewhere */ #ifndef CONFIG_SMP - struct task_struct * p = current; - unsigned long user = ticks - system; + struct task_struct *p = current; + int system = !user_tick; + if (p->pid) { - p->counter -= ticks; - if (p->counter <= 0) { + if (--p->counter <= 0) { p->counter = 0; p->need_resched = 1; } if (p->priority < DEF_PRIORITY) - kstat.cpu_nice += user; + kstat.cpu_nice += user_tick; else - kstat.cpu_user += user; + kstat.cpu_user += user_tick; kstat.cpu_system += system; - } - update_one_process(p, ticks, user, system, 0); + } else if (local_bh_count(0) || local_irq_count(0) > 1) + kstat.cpu_system += system; + update_one_process(p, 1, user_tick, system, 0); #endif } @@ -643,7 +652,6 @@ static inline void calc_load(unsigned long ticks) } volatile unsigned long lost_ticks; -static unsigned long lost_ticks_system; /* * This spinlock protect us from races in SMP while playing with xtime. -arca @@ -665,17 +673,10 @@ static inline void update_times(void) lost_ticks = 0; if (ticks) { - unsigned long system; - system = xchg(&lost_ticks_system, 0); - calc_load(ticks); update_wall_time(ticks); - write_unlock_irq(&xtime_lock); - - update_process_times(ticks, system); - - } else - write_unlock_irq(&xtime_lock); + } + write_unlock_irq(&xtime_lock); } void timer_bh(void) @@ -685,13 +686,12 @@ void timer_bh(void) run_timer_list(); } -void do_timer(struct pt_regs * regs) +void do_timer(struct pt_regs *regs) { (*(unsigned long *)&jiffies)++; lost_ticks++; + update_process_times(user_mode(regs)); mark_bh(TIMER_BH); - if (!user_mode(regs)) - lost_ticks_system++; if (tq_timer) mark_bh(TQUEUE_BH); } diff --git a/mm/filemap.c b/mm/filemap.c index b1e2b8547..74eb0ef83 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -195,6 +195,7 @@ repeat: * to it causing all sorts of fun problems ... */ remove_inode_page(page); + ClearPageDirty(page); UnlockPage(page); page_cache_release(page); @@ -500,7 +501,7 @@ void add_to_page_cache_locked(struct page * page, struct address_space *mapping, /* * This adds a page to the page cache, starting out as locked, - * owned by us, referenced, but not uptodate and with no errors. + * owned by us, but unreferenced, not uptodate and with no errors. */ static inline void __add_to_page_cache(struct page * page, struct address_space *mapping, unsigned long offset, @@ -512,8 +513,8 @@ static inline void __add_to_page_cache(struct page * page, if (PageLocked(page)) BUG(); - flags = page->flags & ~((1 << PG_uptodate) | (1 << PG_error) | (1 << PG_dirty)); - page->flags = flags | (1 << PG_locked) | (1 << PG_referenced); + flags = page->flags & ~((1 << PG_uptodate) | (1 << PG_error) | (1 << PG_dirty) | (1 << PG_referenced)); + page->flags = flags | (1 << PG_locked); page_cache_get(page); page->index = offset; add_page_to_inode_queue(mapping, page); @@ -1744,7 +1745,7 @@ static int msync_interval(struct vm_area_struct * vma, if (!error && (flags & MS_SYNC)) { struct file * file = vma->vm_file; if (file && file->f_op && file->f_op->fsync) - error = file->f_op->fsync(file, file->f_dentry); + error = file->f_op->fsync(file, file->f_dentry, 1); } return error; } @@ -166,6 +166,7 @@ unsigned long do_mmap_pgoff(struct file * file, unsigned long addr, unsigned lon { struct mm_struct * mm = current->mm; struct vm_area_struct * vma; + int correct_wcount = 0; int error; if (file && (!file->f_op || !file->f_op->mmap)) @@ -296,26 +297,15 @@ unsigned long do_mmap_pgoff(struct file * file, unsigned long addr, unsigned lon goto free_vma; if (file) { - int correct_wcount = 0; if (vma->vm_flags & VM_DENYWRITE) { - if (atomic_read(&file->f_dentry->d_inode->i_writecount) > 0) { - error = -ETXTBSY; + error = deny_write_access(file); + if (error) goto free_vma; - } - /* f_op->mmap might possibly sleep - * (generic_file_mmap doesn't, but other code - * might). In any case, this takes care of any - * race that this might cause. - */ - atomic_dec(&file->f_dentry->d_inode->i_writecount); correct_wcount = 1; } vma->vm_file = file; get_file(file); error = file->f_op->mmap(file, vma); - /* Fix up the count if necessary, then check for an error */ - if (correct_wcount) - atomic_inc(&file->f_dentry->d_inode->i_writecount); if (error) goto unmap_and_free_vma; } else if (flags & MAP_SHARED) { @@ -332,6 +322,8 @@ unsigned long do_mmap_pgoff(struct file * file, unsigned long addr, unsigned lon addr = vma->vm_start; /* can addr have changed?? */ vmlist_modify_lock(mm); insert_vm_struct(mm, vma); + if (correct_wcount) + atomic_inc(&file->f_dentry->d_inode->i_writecount); merge_segments(mm, vma->vm_start, vma->vm_end); vmlist_modify_unlock(mm); @@ -343,6 +335,8 @@ unsigned long do_mmap_pgoff(struct file * file, unsigned long addr, unsigned lon return addr; unmap_and_free_vma: + if (correct_wcount) + atomic_inc(&file->f_dentry->d_inode->i_writecount); vma->vm_file = NULL; fput(file); /* Undo any partial mapping done by a device driver. */ @@ -694,9 +688,11 @@ int do_munmap(struct mm_struct *mm, unsigned long addr, size_t len) * so release them, and unmap the page range.. * If the one of the segments is only being partially unmapped, * it will put new vm_area_struct(s) into the address space. + * In that case we have to be careful with VM_DENYWRITE. */ while ((mpnt = free) != NULL) { unsigned long st, end, size; + struct file *file = NULL; free = free->vm_next; @@ -708,6 +704,11 @@ int do_munmap(struct mm_struct *mm, unsigned long addr, size_t len) if (mpnt->vm_ops && mpnt->vm_ops->unmap) mpnt->vm_ops->unmap(mpnt, st, size); + if (mpnt->vm_flags & VM_DENYWRITE && + (st != mpnt->vm_start || end != mpnt->vm_end) && + (file = mpnt->vm_file) != NULL) { + atomic_dec(&file->f_dentry->d_inode->i_writecount); + } remove_shared_vm_struct(mpnt); mm->map_count--; @@ -719,6 +720,8 @@ int do_munmap(struct mm_struct *mm, unsigned long addr, size_t len) * Fix the mapping, and free the old area if it wasn't reused. */ extra = unmap_fixup(mm, mpnt, st, size, extra); + if (file) + atomic_inc(&file->f_dentry->d_inode->i_writecount); } /* Release the extra vma struct if it wasn't used */ diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 926364499..18a60fdbd 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -93,6 +93,8 @@ void __free_pages_ok (struct page *page, unsigned long order) BUG(); if (PageDecrAfter(page)) BUG(); + if (PageDirty(page)) + BUG(); zone = page->zone; diff --git a/mm/swap_state.c b/mm/swap_state.c index 2405aba2f..72f3eaca4 100644 --- a/mm/swap_state.c +++ b/mm/swap_state.c @@ -58,8 +58,8 @@ void add_to_swap_cache(struct page *page, swp_entry_t entry) BUG(); if (page->mapping) BUG(); - flags = page->flags & ~((1 << PG_error) | (1 << PG_dirty)); - page->flags = flags | (1 << PG_referenced) | (1 << PG_uptodate); + flags = page->flags & ~((1 << PG_error) | (1 << PG_dirty) | (1 << PG_referenced)); + page->flags = flags | (1 << PG_uptodate); add_to_page_cache_locked(page, &swapper_space, entry.val); } diff --git a/mm/swapfile.c b/mm/swapfile.c index 55ef476a3..5d3a7f23e 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -407,11 +407,11 @@ asmlinkage long sys_swapoff(const char * specialfile) if (!capable(CAP_SYS_ADMIN)) return -EPERM; - lock_kernel(); err = user_path_walk(specialfile, &nd); if (err) goto out; + lock_kernel(); prev = -1; swap_list_lock(); for (type = swap_list.head; type >= 0; type = swap_info[type].next) { @@ -478,9 +478,9 @@ asmlinkage long sys_swapoff(const char * specialfile) err = 0; out_dput: + unlock_kernel(); path_release(&nd); out: - unlock_kernel(); return err; } @@ -555,7 +555,6 @@ asmlinkage long sys_swapon(const char * specialfile, int swap_flags) unsigned long maxpages; int swapfilesize; struct block_device *bdev = NULL; - char *name; if (!capable(CAP_SYS_ADMIN)) return -EPERM; @@ -586,14 +585,7 @@ asmlinkage long sys_swapon(const char * specialfile, int swap_flags) } else { p->prio = --least_priority; } - name = getname(specialfile); - error = PTR_ERR(name); - if (IS_ERR(name)) - goto bad_swap_2; - error = 0; - if (path_init(name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, &nd)) - error = path_walk(name, &nd); - putname(name); + error = user_path_walk(specialfile, &nd); if (error) goto bad_swap_2; diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c index d99065efe..57718cf00 100644 --- a/net/ax25/af_ax25.c +++ b/net/ax25/af_ax25.c @@ -964,7 +964,7 @@ struct sock *ax25_make_new(struct sock *osk, struct ax25_dev *ax25_dev) return NULL; } - *ax25->digipeat = *osk->protinfo.ax25->digipeat; + memcpy(ax25->digipeat, osk->protinfo.ax25->digipeat, sizeof(ax25_digi)); } sk->protinfo.ax25 = ax25; diff --git a/net/ax25/ax25_in.c b/net/ax25/ax25_in.c index f4b5e1a75..6efbc57f2 100644 --- a/net/ax25/ax25_in.c +++ b/net/ax25/ax25_in.c @@ -426,7 +426,7 @@ static int ax25_rcv(struct sk_buff *skb, struct net_device *dev, ax25_address *d } } else { /* Reverse the source SABM's path */ - *ax25->digipeat = reverse_dp; + memcpy(&ax25->digipeat, &reverse_dp, sizeof(ax25_digi)); } if ((*skb->data & ~AX25_PF) == AX25_SABME) { diff --git a/net/ax25/ax25_out.c b/net/ax25/ax25_out.c index 719059b21..e6b19f53b 100644 --- a/net/ax25/ax25_out.c +++ b/net/ax25/ax25_out.c @@ -98,7 +98,7 @@ ax25_cb *ax25_send_frame(struct sk_buff *skb, int paclen, ax25_address *src, ax2 ax25_free_cb(ax25); return NULL; } - *ax25->digipeat = *digi; + memcpy(ax25->digipeat, digi, sizeof(ax25_digi)); } switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { diff --git a/net/ax25/ax25_route.c b/net/ax25/ax25_route.c index e92a1b39e..6d58c5d04 100644 --- a/net/ax25/ax25_route.c +++ b/net/ax25/ax25_route.c @@ -373,7 +373,7 @@ int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr) if (ax25_rt->digipeat != NULL) { if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) return -ENOMEM; - *ax25->digipeat = *ax25_rt->digipeat; + memcpy(ax25->digipeat, ax25_rt->digipeat, sizeof(ax25_digi)); ax25_adjust_path(addr, ax25->digipeat); } diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 5a3ccd062..88322c8d6 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -603,8 +603,6 @@ static void neigh_periodic_timer(unsigned long arg) struct neigh_table *tbl = (struct neigh_table*)arg; tasklet_schedule(&tbl->gc_task); - - timer_exit(&tbl->gc_timer); } #endif @@ -676,7 +674,6 @@ static void neigh_timer_handler(unsigned long arg) neigh->ops->solicit(neigh, skb_peek(&neigh->arp_queue)); atomic_inc(&neigh->probes); - timer_exit(&neigh->timer); return; out: @@ -685,7 +682,6 @@ out: if (notify && neigh->parms->app_probes) neigh_app_notify(neigh); #endif - timer_exit(&neigh->timer); neigh_release(neigh); } @@ -1021,7 +1017,6 @@ static void neigh_proxy_process(unsigned long arg) tbl->proxy_timer.expires = jiffies + sched_next; add_timer(&tbl->proxy_timer); } - timer_exit(&tbl->proxy_timer); } void pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p, diff --git a/net/decnet/dn_dev.c b/net/decnet/dn_dev.c index 3ab33c220..bc9e2cb48 100644 --- a/net/decnet/dn_dev.c +++ b/net/decnet/dn_dev.c @@ -911,7 +911,6 @@ static void dn_dev_timer_func(unsigned long arg) } dn_dev_set_timer(dev); - timer_exit(&dn_db->timer); } static void dn_dev_set_timer(struct net_device *dev) diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index 1f52d293d..de4722707 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -241,7 +241,6 @@ static void tcp_delack_timer(unsigned long data) TCP_CHECK_TIMER(sk); out_unlock: - timer_exit(&tp->delack_timer); bh_unlock_sock(sk); sock_put(sk); } @@ -299,7 +298,6 @@ static void tcp_probe_timer(unsigned long data) TCP_CHECK_TIMER(sk); } out_unlock: - timer_exit(&tp->probe_timer); bh_unlock_sock(sk); sock_put(sk); } @@ -611,7 +609,6 @@ static void tcp_retransmit_timer(unsigned long data) TCP_CHECK_TIMER(sk); out_unlock: - timer_exit(&tp->retransmit_timer); bh_unlock_sock(sk); sock_put(sk); } @@ -806,7 +803,6 @@ death: tcp_done(sk); out: - timer_exit(&sk->timer); bh_unlock_sock(sk); sock_put(sk); } diff --git a/net/netrom/nr_route.c b/net/netrom/nr_route.c index fd36a887e..308e73af2 100644 --- a/net/netrom/nr_route.c +++ b/net/netrom/nr_route.c @@ -122,7 +122,7 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2 kfree(nr_neigh); return -ENOMEM; } - *nr_neigh->digipeat = *ax25_digi; + memcpy(nr_neigh->digipeat, ax25_digi, sizeof(ax25_digi)); } save_flags(flags); @@ -402,7 +402,7 @@ static int nr_add_neigh(ax25_address *callsign, ax25_digi *ax25_digi, struct net kfree(nr_neigh); return -ENOMEM; } - *nr_neigh->digipeat = *ax25_digi; + memcpy(nr_neigh->digipeat, ax25_digi, sizeof(ax25_digi)); } save_flags(flags); diff --git a/net/netsyms.c b/net/netsyms.c index c209ff991..cd4a2bdb9 100644 --- a/net/netsyms.c +++ b/net/netsyms.c @@ -196,7 +196,7 @@ EXPORT_SYMBOL(__scm_send); /* Needed by unix.o */ EXPORT_SYMBOL(scm_fp_dup); -EXPORT_SYMBOL(max_files); +EXPORT_SYMBOL(files_stat); EXPORT_SYMBOL(memcpy_toiovec); EXPORT_SYMBOL(csum_partial); diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index e0a13d725..f0f714ff0 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -32,6 +32,7 @@ #include <linux/netdevice.h> #include <linux/skbuff.h> #include <net/sock.h> +#include <net/checksum.h> #include <net/ip.h> #include <asm/uaccess.h> @@ -371,6 +372,16 @@ svc_udp_recvfrom(struct svc_rqst *rqstp) dprintk("svc: recvfrom returned error %d\n", -err); } + if (skb->ip_summed != CHECKSUM_UNNECESSARY) { + unsigned int csum = skb->csum; + csum = csum_partial(skb->h.raw, skb->len, csum); + if ((unsigned short)csum_fold(csum)) { + skb_free_datagram(svsk->sk_sk, skb); + svc_sock_received(svsk, 0); + return 0; + } + } + /* There may be more data */ svsk->sk_data = 1; diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index d71a527fe..20e0fc8c7 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -445,7 +445,7 @@ static struct sock * unix_create1(struct socket *sock) { struct sock *sk; - if (atomic_read(&unix_nr_socks) >= 2*max_files) + if (atomic_read(&unix_nr_socks) >= 2*files_stat.max_files) return NULL; MOD_INC_USE_COUNT; diff --git a/scripts/kernel-doc b/scripts/kernel-doc index f58867de7..649cb6cb3 100644 --- a/scripts/kernel-doc +++ b/scripts/kernel-doc @@ -673,9 +673,9 @@ sub dump_function { $args = $3; # allow for up to fours args to function pointers - $args =~ s/(\([^\),]+),/\1#/; - $args =~ s/(\([^\),]+),/\1#/; - $args =~ s/(\([^\),]+),/\1#/; + $args =~ s/(\([^\),]+),/\1#/g; + $args =~ s/(\([^\),]+),/\1#/g; + $args =~ s/(\([^\),]+),/\1#/g; # print STDERR "ARGS = '$args'\n"; foreach $arg (split ',', $args) { |