diff options
author | Paul Mackerras <paulus@samba.org> | 2005-09-25 22:51:50 +1000 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2005-09-25 22:51:50 +1000 |
commit | e5baa396af7560382d2cf3f0871d616b61fc284c (patch) | |
tree | 6afc166894b8c8b3b2cf6add72a726be14ae2443 | |
parent | d6a4c847e43c851cc0ddf73087a730227223f989 (diff) | |
parent | ef6bd6eb90ad72ee8ee7ba8b271f27102e9a90c1 (diff) | |
download | blackbird-op-linux-e5baa396af7560382d2cf3f0871d616b61fc284c.tar.gz blackbird-op-linux-e5baa396af7560382d2cf3f0871d616b61fc284c.zip |
Merge from Linus' tree.
319 files changed, 10278 insertions, 3669 deletions
diff --git a/Documentation/DocBook/kernel-hacking.tmpl b/Documentation/DocBook/kernel-hacking.tmpl index 6367bba32d22..582032eea872 100644 --- a/Documentation/DocBook/kernel-hacking.tmpl +++ b/Documentation/DocBook/kernel-hacking.tmpl @@ -1105,7 +1105,7 @@ static struct block_device_operations opt_fops = { </listitem> <listitem> <para> - Function names as strings (__func__). + Function names as strings (__FUNCTION__). </para> </listitem> <listitem> diff --git a/Documentation/device-mapper/snapshot.txt b/Documentation/device-mapper/snapshot.txt new file mode 100644 index 000000000000..dca274ff4005 --- /dev/null +++ b/Documentation/device-mapper/snapshot.txt @@ -0,0 +1,73 @@ +Device-mapper snapshot support +============================== + +Device-mapper allows you, without massive data copying: + +*) To create snapshots of any block device i.e. mountable, saved states of +the block device which are also writable without interfering with the +original content; +*) To create device "forks", i.e. multiple different versions of the +same data stream. + + +In both cases, dm copies only the chunks of data that get changed and +uses a separate copy-on-write (COW) block device for storage. + + +There are two dm targets available: snapshot and snapshot-origin. + +*) snapshot-origin <origin> + +which will normally have one or more snapshots based on it. +You must create the snapshot-origin device before you can create snapshots. +Reads will be mapped directly to the backing device. For each write, the +original data will be saved in the <COW device> of each snapshot to keep +its visible content unchanged, at least until the <COW device> fills up. + + +*) snapshot <origin> <COW device> <persistent?> <chunksize> + +A snapshot is created of the <origin> block device. Changed chunks of +<chunksize> sectors will be stored on the <COW device>. Writes will +only go to the <COW device>. Reads will come from the <COW device> or +from <origin> for unchanged data. <COW device> will often be +smaller than the origin and if it fills up the snapshot will become +useless and be disabled, returning errors. So it is important to monitor +the amount of free space and expand the <COW device> before it fills up. + +<persistent?> is P (Persistent) or N (Not persistent - will not survive +after reboot). + + +How this is used by LVM2 +======================== +When you create the first LVM2 snapshot of a volume, four dm devices are used: + +1) a device containing the original mapping table of the source volume; +2) a device used as the <COW device>; +3) a "snapshot" device, combining #1 and #2, which is the visible snapshot + volume; +4) the "original" volume (which uses the device number used by the original + source volume), whose table is replaced by a "snapshot-origin" mapping + from device #1. + +A fixed naming scheme is used, so with the following commands: + +lvcreate -L 1G -n base volumeGroup +lvcreate -L 100M --snapshot -n snap volumeGroup/base + +we'll have this situation (with volumes in above order): + +# dmsetup table|grep volumeGroup + +volumeGroup-base-real: 0 2097152 linear 8:19 384 +volumeGroup-snap-cow: 0 204800 linear 8:19 2097536 +volumeGroup-snap: 0 2097152 snapshot 254:11 254:12 P 16 +volumeGroup-base: 0 2097152 snapshot-origin 254:11 + +# ls -lL /dev/mapper/volumeGroup-* +brw------- 1 root root 254, 11 29 ago 18:15 /dev/mapper/volumeGroup-base-real +brw------- 1 root root 254, 12 29 ago 18:15 /dev/mapper/volumeGroup-snap-cow +brw------- 1 root root 254, 13 29 ago 18:15 /dev/mapper/volumeGroup-snap +brw------- 1 root root 254, 10 29 ago 18:14 /dev/mapper/volumeGroup-base + diff --git a/Documentation/sparse.txt b/Documentation/sparse.txt index 5df44dc894e5..1829009db771 100644 --- a/Documentation/sparse.txt +++ b/Documentation/sparse.txt @@ -51,9 +51,9 @@ or you don't get any checking at all. Where to get sparse ~~~~~~~~~~~~~~~~~~~ -With BK, you can just get it from +With git, you can just get it from - bk://sparse.bkbits.net/sparse + rsync://rsync.kernel.org/pub/scm/devel/sparse/sparse.git and DaveJ has tar-balls at diff --git a/Documentation/usb/URB.txt b/Documentation/usb/URB.txt index d59b95cc6f1b..a49e5f2c2b46 100644 --- a/Documentation/usb/URB.txt +++ b/Documentation/usb/URB.txt @@ -1,5 +1,6 @@ Revised: 2000-Dec-05. Again: 2002-Jul-06 +Again: 2005-Sep-19 NOTE: @@ -18,8 +19,8 @@ called USB Request Block, or URB for short. and deliver the data and status back. - Execution of an URB is inherently an asynchronous operation, i.e. the - usb_submit_urb(urb) call returns immediately after it has successfully queued - the requested action. + usb_submit_urb(urb) call returns immediately after it has successfully + queued the requested action. - Transfers for one URB can be canceled with usb_unlink_urb(urb) at any time. @@ -94,8 +95,9 @@ To free an URB, use void usb_free_urb(struct urb *urb) -You may not free an urb that you've submitted, but which hasn't yet been -returned to you in a completion callback. +You may free an urb that you've submitted, but which hasn't yet been +returned to you in a completion callback. It will automatically be +deallocated when it is no longer in use. 1.4. What has to be filled in? @@ -145,30 +147,36 @@ to get seamless ISO streaming. 1.6. How to cancel an already running URB? -For an URB which you've submitted, but which hasn't been returned to -your driver by the host controller, call +There are two ways to cancel an URB you've submitted but which hasn't +been returned to your driver yet. For an asynchronous cancel, call int usb_unlink_urb(struct urb *urb) It removes the urb from the internal list and frees all allocated -HW descriptors. The status is changed to reflect unlinking. After -usb_unlink_urb() returns with that status code, you can free the URB -with usb_free_urb(). +HW descriptors. The status is changed to reflect unlinking. Note +that the URB will not normally have finished when usb_unlink_urb() +returns; you must still wait for the completion handler to be called. -There is also an asynchronous unlink mode. To use this, set the -the URB_ASYNC_UNLINK flag in urb->transfer flags before calling -usb_unlink_urb(). When using async unlinking, the URB will not -normally be unlinked when usb_unlink_urb() returns. Instead, wait -for the completion handler to be called. +To cancel an URB synchronously, call + + void usb_kill_urb(struct urb *urb) + +It does everything usb_unlink_urb does, and in addition it waits +until after the URB has been returned and the completion handler +has finished. It also marks the URB as temporarily unusable, so +that if the completion handler or anyone else tries to resubmit it +they will get a -EPERM error. Thus you can be sure that when +usb_kill_urb() returns, the URB is totally idle. 1.7. What about the completion handler? The handler is of the following type: - typedef void (*usb_complete_t)(struct urb *); + typedef void (*usb_complete_t)(struct urb *, struct pt_regs *) -i.e. it gets just the URB that caused the completion call. +I.e., it gets the URB that caused the completion call, plus the +register values at the time of the corresponding interrupt (if any). In the completion handler, you should have a look at urb->status to detect any USB errors. Since the context parameter is included in the URB, you can pass information to the completion handler. @@ -176,17 +184,11 @@ you can pass information to the completion handler. Note that even when an error (or unlink) is reported, data may have been transferred. That's because USB transfers are packetized; it might take sixteen packets to transfer your 1KByte buffer, and ten of them might -have transferred succesfully before the completion is called. +have transferred succesfully before the completion was called. NOTE: ***** WARNING ***** -Don't use urb->dev field in your completion handler; it's cleared -as part of giving urbs back to drivers. (Addressing an issue with -ownership of periodic URBs, which was otherwise ambiguous.) Instead, -use urb->context to hold all the data your driver needs. - -NOTE: ***** WARNING ***** -Also, NEVER SLEEP IN A COMPLETION HANDLER. These are normally called +NEVER SLEEP IN A COMPLETION HANDLER. These are normally called during hardware interrupt processing. If you can, defer substantial work to a tasklet (bottom half) to keep system latencies low. You'll probably need to use spinlocks to protect data structures you manipulate @@ -229,24 +231,10 @@ ISO data with some other event stream. Interrupt transfers, like isochronous transfers, are periodic, and happen in intervals that are powers of two (1, 2, 4 etc) units. Units are frames for full and low speed devices, and microframes for high speed ones. - -Currently, after you submit one interrupt URB, that urb is owned by the -host controller driver until you cancel it with usb_unlink_urb(). You -may unlink interrupt urbs in their completion handlers, if you need to. - -After a transfer completion is called, the URB is automagically resubmitted. -THIS BEHAVIOR IS EXPECTED TO BE REMOVED!! - -Interrupt transfers may only send (or receive) the "maxpacket" value for -the given interrupt endpoint; if you need more data, you will need to -copy that data out of (or into) another buffer. Similarly, you can't -queue interrupt transfers. -THESE RESTRICTIONS ARE EXPECTED TO BE REMOVED!! - -Note that this automagic resubmission model does make it awkward to use -interrupt OUT transfers. The portable solution involves unlinking those -OUT urbs after the data is transferred, and perhaps submitting a final -URB for a short packet. - The usb_submit_urb() call modifies urb->interval to the implemented interval value that is less than or equal to the requested interval value. + +In Linux 2.6, unlike earlier versions, interrupt URBs are not automagically +restarted when they complete. They end when the completion handler is +called, just like other URBs. If you want an interrupt URB to be restarted, +your completion handler must resubmit it. diff --git a/MAINTAINERS b/MAINTAINERS index dc8f3babcabd..7d1dd5bad39a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1063,8 +1063,6 @@ M: wli@holomorphy.com S: Maintained I2C SUBSYSTEM -P: Greg Kroah-Hartman -M: greg@kroah.com P: Jean Delvare M: khali@linux-fr.org L: lm-sensors@lm-sensors.org @@ -1404,6 +1402,18 @@ L: linux-kernel@vger.kernel.org L: fastboot@osdl.org S: Maintained +KPROBES +P: Prasanna S Panchamukhi +M: prasanna@in.ibm.com +P: Ananth N Mavinakayanahalli +M: ananth@in.ibm.com +P: Anil S Keshavamurthy +M: anil.s.keshavamurthy@intel.com +P: David S. Miller +M: davem@davemloft.net +L: linux-kernel@vger.kernel.org +S: Maintained + LANMEDIA WAN CARD DRIVER P: Andrew Stanley-Jones M: asj@lanmedia.com @@ -2266,6 +2276,12 @@ M: kristen.c.accardi@intel.com L: pcihpd-discuss@lists.sourceforge.net S: Maintained +SKGE, SKY2 10/100/1000 GIGABIT ETHERNET DRIVERS +P: Stephen Hemminger +M: shemminger@osdl.org +L: netdev@vger.kernel.org +S: Maintained + SPARC (sparc32): P: William L. Irwin M: wli@holomorphy.com @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 14 -EXTRAVERSION =-rc1 +EXTRAVERSION =-rc2 NAME=Affluent Albatross # *DOCUMENTATION* @@ -149,6 +149,9 @@ CONFIGURING the kernel: "make gconfig" X windows (Gtk) based configuration tool. "make oldconfig" Default all questions based on the contents of your existing ./.config file. + "make silentoldconfig" + Like above, but avoids cluttering the screen + with questions already answered. NOTES on "make config": - having unnecessary drivers will make the kernel bigger, and can @@ -169,9 +172,6 @@ CONFIGURING the kernel: should probably answer 'n' to the questions for "development", "experimental", or "debugging" features. - - Check the top Makefile for further site-dependent configuration - (default SVGA mode etc). - COMPILING the kernel: - Make sure you have gcc 2.95.3 available. @@ -199,6 +199,9 @@ COMPILING the kernel: are installing a new kernel with the same version number as your working kernel, make a backup of your modules directory before you do a "make modules_install". + Alternatively, before compiling, use the kernel config option + "LOCALVERSION" to append a unique suffix to the regular kernel version. + LOCALVERSION can be set in the "General Setup" menu. - In order to boot your new kernel, you'll need to copy the kernel image (e.g. .../linux/arch/i386/boot/bzImage after compilation) diff --git a/arch/alpha/kernel/process.c b/arch/alpha/kernel/process.c index fa98dae3cd98..eb20c3afff58 100644 --- a/arch/alpha/kernel/process.c +++ b/arch/alpha/kernel/process.c @@ -127,6 +127,10 @@ common_shutdown_1(void *generic_ptr) /* If booted from SRM, reset some of the original environment. */ if (alpha_using_srm) { #ifdef CONFIG_DUMMY_CONSOLE + /* If we've gotten here after SysRq-b, leave interrupt + context before taking over the console. */ + if (in_interrupt()) + irq_exit(); /* This has the effect of resetting the VGA video origin. */ take_over_console(&dummy_con, 0, MAX_NR_CONSOLES-1, 1); #endif diff --git a/arch/alpha/kernel/sys_dp264.c b/arch/alpha/kernel/sys_dp264.c index 9e36b07fa940..d5da6b1b28ee 100644 --- a/arch/alpha/kernel/sys_dp264.c +++ b/arch/alpha/kernel/sys_dp264.c @@ -395,6 +395,22 @@ clipper_init_irq(void) */ static int __init +isa_irq_fixup(struct pci_dev *dev, int irq) +{ + u8 irq8; + + if (irq > 0) + return irq; + + /* This interrupt is routed via ISA bridge, so we'll + just have to trust whatever value the console might + have assigned. */ + pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq8); + + return irq8 & 0xf; +} + +static int __init dp264_map_irq(struct pci_dev *dev, u8 slot, u8 pin) { static char irq_tab[6][5] __initdata = { @@ -407,25 +423,13 @@ dp264_map_irq(struct pci_dev *dev, u8 slot, u8 pin) { 16+ 3, 16+ 3, 16+ 2, 16+ 1, 16+ 0} /* IdSel 10 slot 3 */ }; const long min_idsel = 5, max_idsel = 10, irqs_per_slot = 5; - struct pci_controller *hose = dev->sysdata; int irq = COMMON_TABLE_LOOKUP; - if (irq > 0) { + if (irq > 0) irq += 16 * hose->index; - } else { - /* ??? The Contaq IDE controller on the ISA bridge uses - "legacy" interrupts 14 and 15. I don't know if anything - can wind up at the same slot+pin on hose1, so we'll - just have to trust whatever value the console might - have assigned. */ - - u8 irq8; - pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq8); - irq = irq8; - } - return irq; + return isa_irq_fixup(dev, irq); } static int __init @@ -453,7 +457,8 @@ monet_map_irq(struct pci_dev *dev, u8 slot, u8 pin) { 24, 24, 25, 26, 27} /* IdSel 15 slot 5 PCI2*/ }; const long min_idsel = 3, max_idsel = 15, irqs_per_slot = 5; - return COMMON_TABLE_LOOKUP; + + return isa_irq_fixup(dev, COMMON_TABLE_LOOKUP); } static u8 __init @@ -507,7 +512,8 @@ webbrick_map_irq(struct pci_dev *dev, u8 slot, u8 pin) { 47, 47, 46, 45, 44}, /* IdSel 17 slot 3 */ }; const long min_idsel = 7, max_idsel = 17, irqs_per_slot = 5; - return COMMON_TABLE_LOOKUP; + + return isa_irq_fixup(dev, COMMON_TABLE_LOOKUP); } static int __init @@ -524,14 +530,13 @@ clipper_map_irq(struct pci_dev *dev, u8 slot, u8 pin) { -1, -1, -1, -1, -1} /* IdSel 7 ISA Bridge */ }; const long min_idsel = 1, max_idsel = 7, irqs_per_slot = 5; - struct pci_controller *hose = dev->sysdata; int irq = COMMON_TABLE_LOOKUP; if (irq > 0) irq += 16 * hose->index; - return irq; + return isa_irq_fixup(dev, irq); } static void __init diff --git a/arch/arm/boot/compressed/ofw-shark.c b/arch/arm/boot/compressed/ofw-shark.c index 7f6f5db0d060..465c54b6b128 100644 --- a/arch/arm/boot/compressed/ofw-shark.c +++ b/arch/arm/boot/compressed/ofw-shark.c @@ -256,5 +256,5 @@ asmlinkage void ofw_init(ofw_handle_t o, int *nomr, int *pointer) temp[11]='\0'; mem_len = OF_getproplen(o,phandle, temp); OF_getprop(o,phandle, temp, buffer, mem_len); - (unsigned char) pointer[32] = ((unsigned char *) buffer)[mem_len-2]; + * ((unsigned char *) &pointer[32]) = ((unsigned char *) buffer)[mem_len-2]; } diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S index 7152bfbee581..93b5e8e5292e 100644 --- a/arch/arm/kernel/entry-armv.S +++ b/arch/arm/kernel/entry-armv.S @@ -537,7 +537,7 @@ ENTRY(__switch_to) #ifdef CONFIG_CPU_MPCORE clrex #else - strex r3, r4, [ip] @ Clear exclusive monitor + strex r5, r4, [ip] @ Clear exclusive monitor #endif #endif #if defined(CONFIG_CPU_XSCALE) && !defined(CONFIG_IWMMXT) diff --git a/arch/arm/kernel/io.c b/arch/arm/kernel/io.c index 6c20c1188b60..1f6822dfae74 100644 --- a/arch/arm/kernel/io.c +++ b/arch/arm/kernel/io.c @@ -7,7 +7,7 @@ * Copy data from IO memory space to "real" memory space. * This needs to be optimized. */ -void _memcpy_fromio(void *to, void __iomem *from, size_t count) +void _memcpy_fromio(void *to, const volatile void __iomem *from, size_t count) { unsigned char *t = to; while (count) { @@ -22,7 +22,7 @@ void _memcpy_fromio(void *to, void __iomem *from, size_t count) * Copy data from "real" memory space to IO memory space. * This needs to be optimized. */ -void _memcpy_toio(void __iomem *to, const void *from, size_t count) +void _memcpy_toio(volatile void __iomem *to, const void *from, size_t count) { const unsigned char *f = from; while (count) { @@ -37,7 +37,7 @@ void _memcpy_toio(void __iomem *to, const void *from, size_t count) * "memset" on IO memory space. * This needs to be optimized. */ -void _memset_io(void __iomem *dst, int c, size_t count) +void _memset_io(volatile void __iomem *dst, int c, size_t count) { while (count) { count--; diff --git a/arch/arm/kernel/semaphore.c b/arch/arm/kernel/semaphore.c index ac423e3e224b..4c31f2923055 100644 --- a/arch/arm/kernel/semaphore.c +++ b/arch/arm/kernel/semaphore.c @@ -178,7 +178,7 @@ int __down_trylock(struct semaphore * sem) * registers (r0 to r3 and lr), but not ip, as we use it as a return * value in some cases.. */ -asm(" .section .sched.text,\"ax\" \n\ +asm(" .section .sched.text,\"ax\",%progbits \n\ .align 5 \n\ .globl __down_failed \n\ __down_failed: \n\ diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index 4554c961251c..e7d22dbcb691 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c @@ -624,6 +624,9 @@ void __attribute__((noreturn)) __bug(const char *file, int line, void *data) printk(" - extra data = %p", data); printk("\n"); *(int *)0 = 0; + + /* Avoid "noreturn function does return" */ + for (;;); } EXPORT_SYMBOL(__bug); diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S index ad2d66c93a5c..08e58ecd44be 100644 --- a/arch/arm/kernel/vmlinux.lds.S +++ b/arch/arm/kernel/vmlinux.lds.S @@ -23,20 +23,20 @@ SECTIONS *(.init.text) _einittext = .; __proc_info_begin = .; - *(.proc.info) + *(.proc.info.init) __proc_info_end = .; __arch_info_begin = .; - *(.arch.info) + *(.arch.info.init) __arch_info_end = .; __tagtable_begin = .; - *(.taglist) + *(.taglist.init) __tagtable_end = .; . = ALIGN(16); __setup_start = .; *(.init.setup) __setup_end = .; __early_begin = .; - *(__early_param) + *(.early_param.init) __early_end = .; __initcall_start = .; *(.initcall1.init) diff --git a/arch/arm/mach-ixp4xx/ixdp425-setup.c b/arch/arm/mach-ixp4xx/ixdp425-setup.c index ae1fa099d5fa..39b06ed80646 100644 --- a/arch/arm/mach-ixp4xx/ixdp425-setup.c +++ b/arch/arm/mach-ixp4xx/ixdp425-setup.c @@ -123,6 +123,7 @@ static void __init ixdp425_init(void) platform_add_devices(ixdp425_devices, ARRAY_SIZE(ixdp425_devices)); } +#ifdef CONFIG_ARCH_IXDP465 MACHINE_START(IXDP425, "Intel IXDP425 Development Platform") /* Maintainer: MontaVista Software, Inc. */ .phys_ram = PHYS_OFFSET, @@ -134,7 +135,9 @@ MACHINE_START(IXDP425, "Intel IXDP425 Development Platform") .boot_params = 0x0100, .init_machine = ixdp425_init, MACHINE_END +#endif +#ifdef CONFIG_MACH_IXDP465 MACHINE_START(IXDP465, "Intel IXDP465 Development Platform") /* Maintainer: MontaVista Software, Inc. */ .phys_ram = PHYS_OFFSET, @@ -146,7 +149,9 @@ MACHINE_START(IXDP465, "Intel IXDP465 Development Platform") .boot_params = 0x0100, .init_machine = ixdp425_init, MACHINE_END +#endif +#ifdef CONFIG_ARCH_PRPMC1100 MACHINE_START(IXCDP1100, "Intel IXCDP1100 Development Platform") /* Maintainer: MontaVista Software, Inc. */ .phys_ram = PHYS_OFFSET, @@ -158,6 +163,7 @@ MACHINE_START(IXCDP1100, "Intel IXCDP1100 Development Platform") .boot_params = 0x0100, .init_machine = ixdp425_init, MACHINE_END +#endif /* * Avila is functionally equivalent to IXDP425 except that it adds diff --git a/arch/arm/mach-s3c2410/mach-anubis.c b/arch/arm/mach-s3c2410/mach-anubis.c index f87aa0b669ad..7c05f27fe1d6 100644 --- a/arch/arm/mach-s3c2410/mach-anubis.c +++ b/arch/arm/mach-s3c2410/mach-anubis.c @@ -12,6 +12,7 @@ * * Modifications: * 02-May-2005 BJD Copied from mach-bast.c + * 20-Sep-2005 BJD Added static to non-exported items */ #include <linux/kernel.h> @@ -232,7 +233,7 @@ static struct s3c24xx_board anubis_board __initdata = { .clocks_count = ARRAY_SIZE(anubis_clocks) }; -void __init anubis_map_io(void) +static void __init anubis_map_io(void) { /* initialise the clocks */ diff --git a/arch/arm/mach-s3c2410/mach-bast.c b/arch/arm/mach-s3c2410/mach-bast.c index 1a3367da6408..ed1f07d7252f 100644 --- a/arch/arm/mach-s3c2410/mach-bast.c +++ b/arch/arm/mach-s3c2410/mach-bast.c @@ -31,6 +31,7 @@ * 17-Jul-2005 BJD Changed to platform device for SuperIO 16550s * 25-Jul-2005 BJD Removed ASIX static mappings * 27-Jul-2005 BJD Ensure maximum frequency of i2c bus + * 20-Sep-2005 BJD Added static to non-exported items */ #include <linux/kernel.h> @@ -428,7 +429,7 @@ static struct s3c24xx_board bast_board __initdata = { .clocks_count = ARRAY_SIZE(bast_clocks) }; -void __init bast_map_io(void) +static void __init bast_map_io(void) { /* initialise the clocks */ diff --git a/arch/arm/mach-s3c2410/mach-h1940.c b/arch/arm/mach-s3c2410/mach-h1940.c index 6ff1889fbd21..fb3cb01266e5 100644 --- a/arch/arm/mach-s3c2410/mach-h1940.c +++ b/arch/arm/mach-s3c2410/mach-h1940.c @@ -24,6 +24,7 @@ * 10-Jan-2005 BJD Removed include of s3c2410.h * 14-Jan-2005 BJD Added clock init * 10-Mar-2005 LCVR Changed S3C2410_VA to S3C24XX_VA + * 20-Sep-2005 BJD Added static to non-exported items */ #include <linux/kernel.h> @@ -147,7 +148,7 @@ static struct s3c24xx_board h1940_board __initdata = { .devices_count = ARRAY_SIZE(h1940_devices) }; -void __init h1940_map_io(void) +static void __init h1940_map_io(void) { s3c24xx_init_io(h1940_iodesc, ARRAY_SIZE(h1940_iodesc)); s3c24xx_init_clocks(0); @@ -155,13 +156,13 @@ void __init h1940_map_io(void) s3c24xx_set_board(&h1940_board); } -void __init h1940_init_irq(void) +static void __init h1940_init_irq(void) { s3c24xx_init_irq(); } -void __init h1940_init(void) +static void __init h1940_init(void) { set_s3c2410fb_info(&h1940_lcdcfg); } diff --git a/arch/arm/mach-s3c2410/mach-n30.c b/arch/arm/mach-s3c2410/mach-n30.c index 66bf5bb2b3db..5c0f2b091f95 100644 --- a/arch/arm/mach-s3c2410/mach-n30.c +++ b/arch/arm/mach-s3c2410/mach-n30.c @@ -97,7 +97,7 @@ static struct s3c24xx_board n30_board __initdata = { .devices_count = ARRAY_SIZE(n30_devices) }; -void __init n30_map_io(void) +static void __init n30_map_io(void) { s3c24xx_init_io(n30_iodesc, ARRAY_SIZE(n30_iodesc)); s3c24xx_init_clocks(0); @@ -105,14 +105,14 @@ void __init n30_map_io(void) s3c24xx_set_board(&n30_board); } -void __init n30_init_irq(void) +static void __init n30_init_irq(void) { s3c24xx_init_irq(); } /* GPB3 is the line that controls the pull-up for the USB D+ line */ -void __init n30_init(void) +static void __init n30_init(void) { s3c_device_i2c.dev.platform_data = &n30_i2ccfg; diff --git a/arch/arm/mach-s3c2410/mach-nexcoder.c b/arch/arm/mach-s3c2410/mach-nexcoder.c index d24c242414ca..c22f8216032d 100644 --- a/arch/arm/mach-s3c2410/mach-nexcoder.c +++ b/arch/arm/mach-s3c2410/mach-nexcoder.c @@ -136,7 +136,7 @@ static void __init nexcoder_sensorboard_init(void) s3c2410_gpio_cfgpin(S3C2410_GPF2, S3C2410_GPF2_OUTP); // CAM_GPIO6 => CAM_PWRDN } -void __init nexcoder_map_io(void) +static void __init nexcoder_map_io(void) { s3c24xx_init_io(nexcoder_iodesc, ARRAY_SIZE(nexcoder_iodesc)); s3c24xx_init_clocks(0); diff --git a/arch/arm/mach-s3c2410/mach-otom.c b/arch/arm/mach-s3c2410/mach-otom.c index d901ed492ff5..ad1459e402e2 100644 --- a/arch/arm/mach-s3c2410/mach-otom.c +++ b/arch/arm/mach-s3c2410/mach-otom.c @@ -105,7 +105,7 @@ static struct s3c24xx_board otom11_board __initdata = { }; -void __init otom11_map_io(void) +static void __init otom11_map_io(void) { s3c24xx_init_io(otom11_iodesc, ARRAY_SIZE(otom11_iodesc)); s3c24xx_init_clocks(0); diff --git a/arch/arm/mach-s3c2410/mach-rx3715.c b/arch/arm/mach-s3c2410/mach-rx3715.c index a73d61c1de46..22d9e070fd68 100644 --- a/arch/arm/mach-s3c2410/mach-rx3715.c +++ b/arch/arm/mach-s3c2410/mach-rx3715.c @@ -16,6 +16,7 @@ * 14-Jan-2005 BJD Added new clock init * 10-Mar-2005 LCVR Changed S3C2410_VA to S3C24XX_VA * 14-Mar-2005 BJD Fixed __iomem warnings + * 20-Sep-2005 BJD Added static to non-exported items */ #include <linux/kernel.h> @@ -108,7 +109,7 @@ static struct s3c24xx_board rx3715_board __initdata = { .devices_count = ARRAY_SIZE(rx3715_devices) }; -void __init rx3715_map_io(void) +static void __init rx3715_map_io(void) { s3c24xx_init_io(rx3715_iodesc, ARRAY_SIZE(rx3715_iodesc)); s3c24xx_init_clocks(16934000); @@ -116,7 +117,7 @@ void __init rx3715_map_io(void) s3c24xx_set_board(&rx3715_board); } -void __init rx3715_init_irq(void) +static void __init rx3715_init_irq(void) { s3c24xx_init_irq(); } diff --git a/arch/arm/mach-s3c2410/mach-smdk2410.c b/arch/arm/mach-s3c2410/mach-smdk2410.c index 67e903a700d3..2eda55a6b678 100644 --- a/arch/arm/mach-s3c2410/mach-smdk2410.c +++ b/arch/arm/mach-s3c2410/mach-smdk2410.c @@ -28,6 +28,7 @@ * Ben Dooks <ben@simtec.co.uk> * * 10-Mar-2005 LCVR Changed S3C2410_VA to S3C24XX_VA + * 20-Sep-2005 BJD Added static to non-exported items * ***********************************************************************/ @@ -97,7 +98,7 @@ static struct s3c24xx_board smdk2410_board __initdata = { .devices_count = ARRAY_SIZE(smdk2410_devices) }; -void __init smdk2410_map_io(void) +static void __init smdk2410_map_io(void) { s3c24xx_init_io(smdk2410_iodesc, ARRAY_SIZE(smdk2410_iodesc)); s3c24xx_init_clocks(0); @@ -105,7 +106,7 @@ void __init smdk2410_map_io(void) s3c24xx_set_board(&smdk2410_board); } -void __init smdk2410_init_irq(void) +static void __init smdk2410_init_irq(void) { s3c24xx_init_irq(); } diff --git a/arch/arm/mach-s3c2410/mach-smdk2440.c b/arch/arm/mach-s3c2410/mach-smdk2440.c index 357522106f68..722ef46b630a 100644 --- a/arch/arm/mach-s3c2410/mach-smdk2440.c +++ b/arch/arm/mach-s3c2410/mach-smdk2440.c @@ -18,6 +18,7 @@ * 22-Feb-2005 BJD Updated for 2.6.11-rc5 relesa * 10-Mar-2005 LCVR Replaced S3C2410_VA by S3C24XX_VA * 14-Mar-2005 BJD void __iomem fixes + * 20-Sep-2005 BJD Added static to non-exported items */ #include <linux/kernel.h> @@ -98,7 +99,7 @@ static struct s3c24xx_board smdk2440_board __initdata = { .devices_count = ARRAY_SIZE(smdk2440_devices) }; -void __init smdk2440_map_io(void) +static void __init smdk2440_map_io(void) { s3c24xx_init_io(smdk2440_iodesc, ARRAY_SIZE(smdk2440_iodesc)); s3c24xx_init_clocks(16934400); @@ -106,7 +107,7 @@ void __init smdk2440_map_io(void) s3c24xx_set_board(&smdk2440_board); } -void __init smdk2440_machine_init(void) +static void __init smdk2440_machine_init(void) { /* Configure the LEDs (even if we have no LED support)*/ diff --git a/arch/arm/mach-s3c2410/mach-vr1000.c b/arch/arm/mach-s3c2410/mach-vr1000.c index 8f9ab2893df4..663a7f98fc0b 100644 --- a/arch/arm/mach-s3c2410/mach-vr1000.c +++ b/arch/arm/mach-s3c2410/mach-vr1000.c @@ -28,6 +28,7 @@ * 10-Mar-2005 LCVR Changed S3C2410_VA to S3C24XX_VA * 14-Mar-2006 BJD void __iomem fixes * 22-Jun-2006 BJD Added DM9000 platform information + * 20-Sep-2005 BJD Added static to non-exported items */ #include <linux/kernel.h> @@ -347,7 +348,7 @@ static void vr1000_power_off(void) s3c2410_gpio_setpin(S3C2410_GPB9, 1); } -void __init vr1000_map_io(void) +static void __init vr1000_map_io(void) { /* initialise clock sources */ diff --git a/arch/arm/mach-sa1100/generic.h b/arch/arm/mach-sa1100/generic.h index 279e3afa3c39..f085d68e568e 100644 --- a/arch/arm/mach-sa1100/generic.h +++ b/arch/arm/mach-sa1100/generic.h @@ -39,3 +39,6 @@ extern void sa11x0_set_ssp_data(struct sa11x0_ssp_plat_ops *ops); struct irda_platform_data; void sa11x0_set_irda_data(struct irda_platform_data *irda); + +struct mcp_plat_data; +void sa11x0_set_mcp_data(struct mcp_plat_data *data); diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c index 0b6c4db44e08..4a884baf3b9c 100644 --- a/arch/arm/mm/fault.c +++ b/arch/arm/mm/fault.c @@ -233,7 +233,17 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) if (in_interrupt() || !mm) goto no_context; - down_read(&mm->mmap_sem); + /* + * As per x86, we may deadlock here. However, since the kernel only + * validly references user space from well defined areas of the code, + * we can bug out early if this is from code which shouldn't. + */ + if (!down_read_trylock(&mm->mmap_sem)) { + if (!user_mode(regs) && !search_exception_tables(regs->ARM_pc)) + goto no_context; + down_read(&mm->mmap_sem); + } + fault = __do_page_fault(mm, addr, fsr, tsk); up_read(&mm->mmap_sem); diff --git a/arch/arm/mm/proc-arm1020.S b/arch/arm/mm/proc-arm1020.S index 1d739d282a45..82ec954e45b6 100644 --- a/arch/arm/mm/proc-arm1020.S +++ b/arch/arm/mm/proc-arm1020.S @@ -509,7 +509,7 @@ cpu_arm1020_name: .align - .section ".proc.info", #alloc, #execinstr + .section ".proc.info.init", #alloc, #execinstr .type __arm1020_proc_info,#object __arm1020_proc_info: diff --git a/arch/arm/mm/proc-arm1020e.S b/arch/arm/mm/proc-arm1020e.S index 9b725665b5c7..7375fe930f72 100644 --- a/arch/arm/mm/proc-arm1020e.S +++ b/arch/arm/mm/proc-arm1020e.S @@ -491,7 +491,7 @@ cpu_arm1020e_name: .align - .section ".proc.info", #alloc, #execinstr + .section ".proc.info.init", #alloc, #execinstr .type __arm1020e_proc_info,#object __arm1020e_proc_info: diff --git a/arch/arm/mm/proc-arm1022.S b/arch/arm/mm/proc-arm1022.S index 37b70fa21c76..6ca639094d6f 100644 --- a/arch/arm/mm/proc-arm1022.S +++ b/arch/arm/mm/proc-arm1022.S @@ -473,7 +473,7 @@ cpu_arm1022_name: .align - .section ".proc.info", #alloc, #execinstr + .section ".proc.info.init", #alloc, #execinstr .type __arm1022_proc_info,#object __arm1022_proc_info: diff --git a/arch/arm/mm/proc-arm1026.S b/arch/arm/mm/proc-arm1026.S index 931b690d1be2..10317e4f55d2 100644 --- a/arch/arm/mm/proc-arm1026.S +++ b/arch/arm/mm/proc-arm1026.S @@ -469,7 +469,7 @@ cpu_arm1026_name: .align - .section ".proc.info", #alloc, #execinstr + .section ".proc.info.init", #alloc, #execinstr .type __arm1026_proc_info,#object __arm1026_proc_info: diff --git a/arch/arm/mm/proc-arm6_7.S b/arch/arm/mm/proc-arm6_7.S index d0f1bbb48f6c..8e7e1e70ab05 100644 --- a/arch/arm/mm/proc-arm6_7.S +++ b/arch/arm/mm/proc-arm6_7.S @@ -332,7 +332,7 @@ cpu_arm710_name: .align - .section ".proc.info", #alloc, #execinstr + .section ".proc.info.init", #alloc, #execinstr .type __arm6_proc_info, #object __arm6_proc_info: diff --git a/arch/arm/mm/proc-arm720.S b/arch/arm/mm/proc-arm720.S index c69c9de32391..a13e0184d343 100644 --- a/arch/arm/mm/proc-arm720.S +++ b/arch/arm/mm/proc-arm720.S @@ -222,7 +222,7 @@ cpu_arm720_name: * See linux/include/asm-arm/procinfo.h for a definition of this structure. */ - .section ".proc.info", #alloc, #execinstr + .section ".proc.info.init", #alloc, #execinstr .type __arm710_proc_info, #object __arm710_proc_info: diff --git a/arch/arm/mm/proc-arm920.S b/arch/arm/mm/proc-arm920.S index 0f490a0fcb71..d16513899999 100644 --- a/arch/arm/mm/proc-arm920.S +++ b/arch/arm/mm/proc-arm920.S @@ -452,7 +452,7 @@ cpu_arm920_name: .align - .section ".proc.info", #alloc, #execinstr + .section ".proc.info.init", #alloc, #execinstr .type __arm920_proc_info,#object __arm920_proc_info: diff --git a/arch/arm/mm/proc-arm922.S b/arch/arm/mm/proc-arm922.S index 62bc34a139ee..23b8ed97f4e3 100644 --- a/arch/arm/mm/proc-arm922.S +++ b/arch/arm/mm/proc-arm922.S @@ -456,7 +456,7 @@ cpu_arm922_name: .align - .section ".proc.info", #alloc, #execinstr + .section ".proc.info.init", #alloc, #execinstr .type __arm922_proc_info,#object __arm922_proc_info: diff --git a/arch/arm/mm/proc-arm925.S b/arch/arm/mm/proc-arm925.S index ee49aa2ca781..ee95c52db513 100644 --- a/arch/arm/mm/proc-arm925.S +++ b/arch/arm/mm/proc-arm925.S @@ -521,7 +521,7 @@ cpu_arm925_name: .align - .section ".proc.info", #alloc, #execinstr + .section ".proc.info.init", #alloc, #execinstr .type __arm925_proc_info,#object __arm925_proc_info: diff --git a/arch/arm/mm/proc-arm926.S b/arch/arm/mm/proc-arm926.S index bb95cc9fed03..7d042dc20c47 100644 --- a/arch/arm/mm/proc-arm926.S +++ b/arch/arm/mm/proc-arm926.S @@ -471,7 +471,7 @@ cpu_arm926_name: .align - .section ".proc.info", #alloc, #execinstr + .section ".proc.info.init", #alloc, #execinstr .type __arm926_proc_info,#object __arm926_proc_info: diff --git a/arch/arm/mm/proc-sa110.S b/arch/arm/mm/proc-sa110.S index 34f7e7d3f419..bd330c4075a1 100644 --- a/arch/arm/mm/proc-sa110.S +++ b/arch/arm/mm/proc-sa110.S @@ -249,7 +249,7 @@ cpu_sa110_name: .align - .section ".proc.info", #alloc, #execinstr + .section ".proc.info.init", #alloc, #execinstr .type __sa110_proc_info,#object __sa110_proc_info: diff --git a/arch/arm/mm/proc-sa1100.S b/arch/arm/mm/proc-sa1100.S index ca14f80d5ab4..91b89124c0d7 100644 --- a/arch/arm/mm/proc-sa1100.S +++ b/arch/arm/mm/proc-sa1100.S @@ -280,7 +280,7 @@ cpu_sa1110_name: .align - .section ".proc.info", #alloc, #execinstr + .section ".proc.info.init", #alloc, #execinstr .type __sa1100_proc_info,#object __sa1100_proc_info: diff --git a/arch/arm/mm/proc-v6.S b/arch/arm/mm/proc-v6.S index eb34823c9dbf..caf3b19b167f 100644 --- a/arch/arm/mm/proc-v6.S +++ b/arch/arm/mm/proc-v6.S @@ -240,7 +240,7 @@ cpu_elf_name: .size cpu_elf_name, . - cpu_elf_name .align - .section ".proc.info", #alloc, #execinstr + .section ".proc.info.init", #alloc, #execinstr /* * Match any ARMv6 processor core. diff --git a/arch/arm/mm/proc-xscale.S b/arch/arm/mm/proc-xscale.S index b88de2700146..861b35947280 100644 --- a/arch/arm/mm/proc-xscale.S +++ b/arch/arm/mm/proc-xscale.S @@ -578,7 +578,7 @@ cpu_pxa270_name: .align - .section ".proc.info", #alloc, #execinstr + .section ".proc.info.init", #alloc, #execinstr .type __80200_proc_info,#object __80200_proc_info: diff --git a/arch/ia64/hp/sim/simscsi.c b/arch/ia64/hp/sim/simscsi.c index 56405dbfd739..a18983a3c934 100644 --- a/arch/ia64/hp/sim/simscsi.c +++ b/arch/ia64/hp/sim/simscsi.c @@ -233,6 +233,23 @@ simscsi_readwrite10 (struct scsi_cmnd *sc, int mode) simscsi_readwrite(sc, mode, offset, ((sc->cmnd[7] << 8) | sc->cmnd[8])*512); } +static void simscsi_fillresult(struct scsi_cmnd *sc, char *buf, unsigned len) +{ + + int scatterlen = sc->use_sg; + struct scatterlist *slp; + + if (scatterlen == 0) + memcpy(sc->request_buffer, buf, len); + else for (slp = (struct scatterlist *)sc->buffer; scatterlen-- > 0 && len > 0; slp++) { + unsigned thislen = min(len, slp->length); + + memcpy(page_address(slp->page) + slp->offset, buf, thislen); + slp++; + len -= thislen; + } +} + static int simscsi_queuecommand (struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) { @@ -240,6 +257,7 @@ simscsi_queuecommand (struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) char fname[MAX_ROOT_LEN+16]; size_t disk_size; char *buf; + char localbuf[36]; #if DEBUG_SIMSCSI register long sp asm ("sp"); @@ -263,7 +281,7 @@ simscsi_queuecommand (struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) /* disk doesn't exist... */ break; } - buf = sc->request_buffer; + buf = localbuf; buf[0] = 0; /* magnetic disk */ buf[1] = 0; /* not a removable medium */ buf[2] = 2; /* SCSI-2 compliant device */ @@ -273,6 +291,7 @@ simscsi_queuecommand (struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) buf[6] = 0; /* reserved */ buf[7] = 0; /* various flags */ memcpy(buf + 8, "HP SIMULATED DISK 0.00", 28); + simscsi_fillresult(sc, buf, 36); sc->result = GOOD; break; @@ -304,16 +323,13 @@ simscsi_queuecommand (struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) simscsi_readwrite10(sc, SSC_WRITE); break; - case READ_CAPACITY: if (desc[target_id] < 0 || sc->request_bufflen < 8) { break; } - buf = sc->request_buffer; - + buf = localbuf; disk_size = simscsi_get_disk_size(desc[target_id]); - /* pretend to be a 1GB disk (partition table contains real stuff): */ buf[0] = (disk_size >> 24) & 0xff; buf[1] = (disk_size >> 16) & 0xff; buf[2] = (disk_size >> 8) & 0xff; @@ -323,13 +339,14 @@ simscsi_queuecommand (struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) buf[5] = 0; buf[6] = 2; buf[7] = 0; + simscsi_fillresult(sc, buf, 8); sc->result = GOOD; break; case MODE_SENSE: case MODE_SENSE_10: /* sd.c uses this to determine whether disk does write-caching. */ - memset(sc->request_buffer, 0, 128); + simscsi_fillresult(sc, (char *)empty_zero_page, sc->request_bufflen); sc->result = GOOD; break; diff --git a/arch/ia64/kernel/mca_asm.S b/arch/ia64/kernel/mca_asm.S index 499a065f4e60..db32fc1d3935 100644 --- a/arch/ia64/kernel/mca_asm.S +++ b/arch/ia64/kernel/mca_asm.S @@ -489,24 +489,27 @@ ia64_state_save: ;; st8 [temp1]=r17,16 // pal_min_state st8 [temp2]=r6,16 // prev_IA64_KR_CURRENT + mov r6=IA64_KR(CURRENT_STACK) + ;; + st8 [temp1]=r6,16 // prev_IA64_KR_CURRENT_STACK + st8 [temp2]=r0,16 // prev_task, starts off as NULL mov r6=cr.ifa ;; - st8 [temp1]=r0,16 // prev_task, starts off as NULL - st8 [temp2]=r12,16 // cr.isr + st8 [temp1]=r12,16 // cr.isr + st8 [temp2]=r6,16 // cr.ifa mov r12=cr.itir ;; - st8 [temp1]=r6,16 // cr.ifa - st8 [temp2]=r12,16 // cr.itir + st8 [temp1]=r12,16 // cr.itir + st8 [temp2]=r11,16 // cr.iipa mov r12=cr.iim ;; - st8 [temp1]=r11,16 // cr.iipa - st8 [temp2]=r12,16 // cr.iim - mov r6=cr.iha + st8 [temp1]=r12,16 // cr.iim (p1) mov r12=IA64_MCA_COLD_BOOT (p2) mov r12=IA64_INIT_WARM_BOOT + mov r6=cr.iha ;; - st8 [temp1]=r6,16 // cr.iha - st8 [temp2]=r12 // os_status, default is cold boot + st8 [temp2]=r6,16 // cr.iha + st8 [temp1]=r12 // os_status, default is cold boot mov r6=IA64_MCA_SAME_CONTEXT ;; st8 [temp1]=r6 // context, default is same context @@ -823,9 +826,12 @@ ia64_state_restore: ld8 r12=[temp1],16 // sal_ra ld8 r9=[temp2],16 // sal_gp ;; - ld8 r22=[temp1],24 // pal_min_state, virtual. skip prev_task + ld8 r22=[temp1],16 // pal_min_state, virtual ld8 r21=[temp2],16 // prev_IA64_KR_CURRENT ;; + ld8 r16=[temp1],16 // prev_IA64_KR_CURRENT_STACK + ld8 r20=[temp2],16 // prev_task + ;; ld8 temp3=[temp1],16 // cr.isr ld8 temp4=[temp2],16 // cr.ifa ;; @@ -846,6 +852,45 @@ ia64_state_restore: ld8 r8=[temp1] // os_status ld8 r10=[temp2] // context + /* Wire IA64_TR_CURRENT_STACK to the stack that we are resuming to. To + * avoid any dependencies on the algorithm in ia64_switch_to(), just + * purge any existing CURRENT_STACK mapping and insert the new one. + * + * r16 contains prev_IA64_KR_CURRENT_STACK, r21 contains + * prev_IA64_KR_CURRENT, these values may have been changed by the C + * code. Do not use r8, r9, r10, r22, they contain values ready for + * the return to SAL. + */ + + mov r15=IA64_KR(CURRENT_STACK) // physical granule mapped by IA64_TR_CURRENT_STACK + ;; + shl r15=r15,IA64_GRANULE_SHIFT + ;; + dep r15=-1,r15,61,3 // virtual granule + mov r18=IA64_GRANULE_SHIFT<<2 // for cr.itir.ps + ;; + ptr.d r15,r18 + ;; + srlz.d + + extr.u r19=r21,61,3 // r21 = prev_IA64_KR_CURRENT + shl r20=r16,IA64_GRANULE_SHIFT // r16 = prev_IA64_KR_CURRENT_STACK + movl r21=PAGE_KERNEL // page properties + ;; + mov IA64_KR(CURRENT_STACK)=r16 + cmp.ne p6,p0=RGN_KERNEL,r19 // new stack is in the kernel region? + or r21=r20,r21 // construct PA | page properties +(p6) br.spnt 1f // the dreaded cpu 0 idle task in region 5:( + ;; + mov cr.itir=r18 + mov cr.ifa=r21 + mov r20=IA64_TR_CURRENT_STACK + ;; + itr.d dtr[r20]=r21 + ;; + srlz.d +1: + br.sptk b0 //EndStub////////////////////////////////////////////////////////////////////// @@ -982,6 +1027,7 @@ ia64_set_kernel_registers: add temp4=temp4, temp1 // &struct ia64_sal_os_state.os_gp add r12=temp1, temp3 // kernel stack pointer on MCA/INIT stack add r13=temp1, r3 // set current to start of MCA/INIT stack + add r20=temp1, r3 // physical start of MCA/INIT stack ;; ld8 r1=[temp4] // OS GP from SAL OS state ;; @@ -991,7 +1037,35 @@ ia64_set_kernel_registers: ;; mov IA64_KR(CURRENT)=r13 - // FIXME: do I need to wire IA64_KR_CURRENT_STACK and IA64_TR_CURRENT_STACK? + /* Wire IA64_TR_CURRENT_STACK to the MCA/INIT handler stack. To avoid + * any dependencies on the algorithm in ia64_switch_to(), just purge + * any existing CURRENT_STACK mapping and insert the new one. + */ + + mov r16=IA64_KR(CURRENT_STACK) // physical granule mapped by IA64_TR_CURRENT_STACK + ;; + shl r16=r16,IA64_GRANULE_SHIFT + ;; + dep r16=-1,r16,61,3 // virtual granule + mov r18=IA64_GRANULE_SHIFT<<2 // for cr.itir.ps + ;; + ptr.d r16,r18 + ;; + srlz.d + + shr.u r16=r20,IA64_GRANULE_SHIFT // r20 = physical start of MCA/INIT stack + movl r21=PAGE_KERNEL // page properties + ;; + mov IA64_KR(CURRENT_STACK)=r16 + or r21=r20,r21 // construct PA | page properties + ;; + mov cr.itir=r18 + mov cr.ifa=r13 + mov r20=IA64_TR_CURRENT_STACK + ;; + itr.d dtr[r20]=r21 + ;; + srlz.d br.sptk b0 diff --git a/arch/ia64/kernel/mca_drv.c b/arch/ia64/kernel/mca_drv.c index 80f83d6cdbfc..f081c60ab206 100644 --- a/arch/ia64/kernel/mca_drv.c +++ b/arch/ia64/kernel/mca_drv.c @@ -56,8 +56,9 @@ static struct page *page_isolate[MAX_PAGE_ISOLATE]; static int num_page_isolate = 0; typedef enum { - ISOLATE_NG = 0, - ISOLATE_OK = 1 + ISOLATE_NG, + ISOLATE_OK, + ISOLATE_NONE } isolate_status_t; /* @@ -74,7 +75,7 @@ static struct { * @paddr: poisoned memory location * * Return value: - * ISOLATE_OK / ISOLATE_NG + * one of isolate_status_t, ISOLATE_OK/NG/NONE. */ static isolate_status_t @@ -85,7 +86,10 @@ mca_page_isolate(unsigned long paddr) /* whether physical address is valid or not */ if (!ia64_phys_addr_valid(paddr)) - return ISOLATE_NG; + return ISOLATE_NONE; + + if (!pfn_valid(paddr)) + return ISOLATE_NONE; /* convert physical address to physical page number */ p = pfn_to_page(paddr>>PAGE_SHIFT); @@ -122,10 +126,15 @@ mca_handler_bh(unsigned long paddr) current->pid, current->comm); spin_lock(&mca_bh_lock); - if (mca_page_isolate(paddr) == ISOLATE_OK) { + switch (mca_page_isolate(paddr)) { + case ISOLATE_OK: printk(KERN_DEBUG "Page isolation: ( %lx ) success.\n", paddr); - } else { + break; + case ISOLATE_NG: printk(KERN_DEBUG "Page isolation: ( %lx ) failure.\n", paddr); + break; + default: + break; } spin_unlock(&mca_bh_lock); diff --git a/arch/ppc/kernel/Makefile b/arch/ppc/kernel/Makefile index abf10dcb787b..ce166e3de53b 100644 --- a/arch/ppc/kernel/Makefile +++ b/arch/ppc/kernel/Makefile @@ -15,9 +15,8 @@ extra-y += vmlinux.lds obj-y := entry.o traps.o irq.o idle.o time.o misc.o \ process.o signal.o ptrace.o align.o \ semaphore.o syscalls.o setup.o \ - cputable.o ppc_htab.o + cputable.o ppc_htab.o perfmon.o obj-$(CONFIG_6xx) += l2cr.o cpu_setup_6xx.o -obj-$(CONFIG_E500) += perfmon.o obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o obj-$(CONFIG_POWER4) += cpu_setup_power4.o obj-$(CONFIG_MODULES) += module.o ppc_ksyms.o diff --git a/arch/ppc/kernel/perfmon.c b/arch/ppc/kernel/perfmon.c index 91e2786ea097..f9b27d939f76 100644 --- a/arch/ppc/kernel/perfmon.c +++ b/arch/ppc/kernel/perfmon.c @@ -45,7 +45,7 @@ static void dummy_perf(struct pt_regs *regs) mtpmr(PMRN_PMGC0, pmgc0); } -#else +#elif CONFIG_6xx /* Ensure exceptions are disabled */ static void dummy_perf(struct pt_regs *regs) @@ -55,6 +55,10 @@ static void dummy_perf(struct pt_regs *regs) mmcr0 &= ~MMCR0_PMXE; mtspr(SPRN_MMCR0, mmcr0); } +#else +static void dummy_perf(struct pt_regs *regs) +{ +} #endif void (*perf_irq)(struct pt_regs *) = dummy_perf; diff --git a/arch/ppc/platforms/pmac_setup.c b/arch/ppc/platforms/pmac_setup.c index 12cbc85555d9..1ad779ecc8fc 100644 --- a/arch/ppc/platforms/pmac_setup.c +++ b/arch/ppc/platforms/pmac_setup.c @@ -719,7 +719,8 @@ pmac_declare_of_platform_devices(void) if (np) { for (np = np->child; np != NULL; np = np->sibling) if (strncmp(np->name, "i2c", 3) == 0) { - of_platform_device_create(np, "uni-n-i2c"); + of_platform_device_create(np, "uni-n-i2c", + NULL); break; } } @@ -727,17 +728,18 @@ pmac_declare_of_platform_devices(void) if (np) { for (np = np->child; np != NULL; np = np->sibling) if (strncmp(np->name, "i2c", 3) == 0) { - of_platform_device_create(np, "u3-i2c"); + of_platform_device_create(np, "u3-i2c", + NULL); break; } } np = find_devices("valkyrie"); if (np) - of_platform_device_create(np, "valkyrie"); + of_platform_device_create(np, "valkyrie", NULL); np = find_devices("platinum"); if (np) - of_platform_device_create(np, "platinum"); + of_platform_device_create(np, "platinum", NULL); return 0; } diff --git a/arch/ppc/syslib/of_device.c b/arch/ppc/syslib/of_device.c index da8a0f2128dc..93c7231ea709 100644 --- a/arch/ppc/syslib/of_device.c +++ b/arch/ppc/syslib/of_device.c @@ -234,7 +234,9 @@ void of_device_unregister(struct of_device *ofdev) device_unregister(&ofdev->dev); } -struct of_device* of_platform_device_create(struct device_node *np, const char *bus_id) +struct of_device* of_platform_device_create(struct device_node *np, + const char *bus_id, + struct device *parent) { struct of_device *dev; u32 *reg; @@ -247,7 +249,7 @@ struct of_device* of_platform_device_create(struct device_node *np, const char * dev->node = of_node_get(np); dev->dma_mask = 0xffffffffUL; dev->dev.dma_mask = &dev->dma_mask; - dev->dev.parent = NULL; + dev->dev.parent = parent; dev->dev.bus = &of_platform_bus_type; dev->dev.release = of_release_dev; diff --git a/arch/ppc/syslib/ppc85xx_setup.c b/arch/ppc/syslib/ppc85xx_setup.c index b7242f1bd931..832b8bf99ae7 100644 --- a/arch/ppc/syslib/ppc85xx_setup.c +++ b/arch/ppc/syslib/ppc85xx_setup.c @@ -184,8 +184,8 @@ mpc85xx_setup_pci1(struct pci_controller *hose) pci->powar1 = 0x80044000 | (__ilog2(MPC85XX_PCI1_UPPER_MEM - MPC85XX_PCI1_LOWER_MEM + 1) - 1); - /* Setup outboud IO windows @ MPC85XX_PCI1_IO_BASE */ - pci->potar2 = 0x00000000; + /* Setup outbound IO windows @ MPC85XX_PCI1_IO_BASE */ + pci->potar2 = (MPC85XX_PCI1_LOWER_IO >> 12) & 0x000fffff; pci->potear2 = 0x00000000; pci->powbar2 = (MPC85XX_PCI1_IO_BASE >> 12) & 0x000fffff; /* Enable, IO R/W */ @@ -235,8 +235,8 @@ mpc85xx_setup_pci2(struct pci_controller *hose) pci->powar1 = 0x80044000 | (__ilog2(MPC85XX_PCI2_UPPER_MEM - MPC85XX_PCI2_LOWER_MEM + 1) - 1); - /* Setup outboud IO windows @ MPC85XX_PCI2_IO_BASE */ - pci->potar2 = 0x00000000; + /* Setup outbound IO windows @ MPC85XX_PCI2_IO_BASE */ + pci->potar2 = (MPC85XX_PCI2_LOWER_IO >> 12) & 0x000fffff;; pci->potear2 = 0x00000000; pci->powbar2 = (MPC85XX_PCI2_IO_BASE >> 12) & 0x000fffff; /* Enable, IO R/W */ diff --git a/arch/ppc64/Makefile b/arch/ppc64/Makefile index a8877881bb48..40675b3f924d 100644 --- a/arch/ppc64/Makefile +++ b/arch/ppc64/Makefile @@ -107,7 +107,7 @@ install: vmlinux $(Q)$(MAKE) $(build)=$(boot) BOOTIMAGE=$(BOOTIMAGE) $@ defaultimage-$(CONFIG_PPC_PSERIES) := zImage -defaultimage-$(CONFIG_PPC_PMAC) := vmlinux +defaultimage-$(CONFIG_PPC_PMAC) := zImage.vmode defaultimage-$(CONFIG_PPC_MAPLE) := zImage defaultimage-$(CONFIG_PPC_ISERIES) := vmlinux KBUILD_IMAGE := $(defaultimage-y) diff --git a/arch/ppc64/kernel/of_device.c b/arch/ppc64/kernel/of_device.c index da580812ddfe..9f200f0f2ad5 100644 --- a/arch/ppc64/kernel/of_device.c +++ b/arch/ppc64/kernel/of_device.c @@ -233,7 +233,9 @@ void of_device_unregister(struct of_device *ofdev) device_unregister(&ofdev->dev); } -struct of_device* of_platform_device_create(struct device_node *np, const char *bus_id) +struct of_device* of_platform_device_create(struct device_node *np, + const char *bus_id, + struct device *parent) { struct of_device *dev; @@ -245,7 +247,7 @@ struct of_device* of_platform_device_create(struct device_node *np, const char * dev->node = np; dev->dma_mask = 0xffffffffUL; dev->dev.dma_mask = &dev->dma_mask; - dev->dev.parent = NULL; + dev->dev.parent = parent; dev->dev.bus = &of_platform_bus_type; dev->dev.release = of_release_dev; @@ -259,6 +261,7 @@ struct of_device* of_platform_device_create(struct device_node *np, const char * return dev; } + EXPORT_SYMBOL(of_match_device); EXPORT_SYMBOL(of_platform_bus_type); EXPORT_SYMBOL(of_register_driver); diff --git a/arch/ppc64/kernel/pSeries_iommu.c b/arch/ppc64/kernel/pSeries_iommu.c index 2b5e622732f4..5914f61a152e 100644 --- a/arch/ppc64/kernel/pSeries_iommu.c +++ b/arch/ppc64/kernel/pSeries_iommu.c @@ -281,8 +281,10 @@ static void iommu_table_setparms(struct pci_controller *phb, tbl->it_offset = phb->dma_window_base_cur >> PAGE_SHIFT; /* Test if we are going over 2GB of DMA space */ - if (phb->dma_window_base_cur + phb->dma_window_size > (1L << 31)) + if (phb->dma_window_base_cur + phb->dma_window_size > 0x80000000ul) { + udbg_printf("PCI_DMA: Unexpected number of IOAs under this PHB.\n"); panic("PCI_DMA: Unexpected number of IOAs under this PHB.\n"); + } phb->dma_window_base_cur += phb->dma_window_size; @@ -326,92 +328,85 @@ static void iommu_table_setparms_lpar(struct pci_controller *phb, static void iommu_bus_setup_pSeries(struct pci_bus *bus) { - struct device_node *dn, *pdn; - struct pci_dn *pci; + struct device_node *dn; struct iommu_table *tbl; + struct device_node *isa_dn, *isa_dn_orig; + struct device_node *tmp; + struct pci_dn *pci; + int children; DBG("iommu_bus_setup_pSeries, bus %p, bus->self %p\n", bus, bus->self); - /* For each (root) bus, we carve up the available DMA space in 256MB - * pieces. Since each piece is used by one (sub) bus/device, that would - * give a maximum of 7 devices per PHB. In most cases, this is plenty. - * - * The exception is on Python PHBs (pre-POWER4). Here we don't have EADS - * bridges below the PHB to allocate the sectioned tables to, so instead - * we allocate a 1GB table at the PHB level. + dn = pci_bus_to_OF_node(bus); + pci = PCI_DN(dn); + + if (bus->self) { + /* This is not a root bus, any setup will be done for the + * device-side of the bridge in iommu_dev_setup_pSeries(). + */ + return; + } + + /* Check if the ISA bus on the system is under + * this PHB. */ + isa_dn = isa_dn_orig = of_find_node_by_type(NULL, "isa"); - dn = pci_bus_to_OF_node(bus); - pci = dn->data; - - if (!bus->self) { - /* Root bus */ - if (is_python(dn)) { - unsigned int *iohole; - - DBG("Python root bus %s\n", bus->name); - - iohole = (unsigned int *)get_property(dn, "io-hole", 0); - - if (iohole) { - /* On first bus we need to leave room for the - * ISA address space. Just skip the first 256MB - * alltogether. This leaves 768MB for the window. - */ - DBG("PHB has io-hole, reserving 256MB\n"); - pci->phb->dma_window_size = 3 << 28; - pci->phb->dma_window_base_cur = 1 << 28; - } else { - /* 1GB window by default */ - pci->phb->dma_window_size = 1 << 30; - pci->phb->dma_window_base_cur = 0; - } - - tbl = kmalloc(sizeof(struct iommu_table), GFP_KERNEL); - - iommu_table_setparms(pci->phb, dn, tbl); - pci->iommu_table = iommu_init_table(tbl); - } else { - /* Do a 128MB table at root. This is used for the IDE - * controller on some SMP-mode POWER4 machines. It - * doesn't hurt to allocate it on other machines - * -- it'll just be unused since new tables are - * allocated on the EADS level. - * - * Allocate at offset 128MB to avoid having to deal - * with ISA holes; 128MB table for IDE is plenty. - */ - pci->phb->dma_window_size = 1 << 27; - pci->phb->dma_window_base_cur = 1 << 27; - - tbl = kmalloc(sizeof(struct iommu_table), GFP_KERNEL); - - iommu_table_setparms(pci->phb, dn, tbl); - pci->iommu_table = iommu_init_table(tbl); - - /* All child buses have 256MB tables */ - pci->phb->dma_window_size = 1 << 28; - } - } else { - pdn = pci_bus_to_OF_node(bus->parent); + while (isa_dn && isa_dn != dn) + isa_dn = isa_dn->parent; + + if (isa_dn_orig) + of_node_put(isa_dn_orig); - if (!bus->parent->self && !is_python(pdn)) { - struct iommu_table *tbl; - /* First child and not python means this is the EADS - * level. Allocate new table for this slot with 256MB - * window. - */ + /* Count number of direct PCI children of the PHB. + * All PCI device nodes have class-code property, so it's + * an easy way to find them. + */ + for (children = 0, tmp = dn->child; tmp; tmp = tmp->sibling) + if (get_property(tmp, "class-code", NULL)) + children++; - tbl = kmalloc(sizeof(struct iommu_table), GFP_KERNEL); + DBG("Children: %d\n", children); - iommu_table_setparms(pci->phb, dn, tbl); + /* Calculate amount of DMA window per slot. Each window must be + * a power of two (due to pci_alloc_consistent requirements). + * + * Keep 256MB aside for PHBs with ISA. + */ - pci->iommu_table = iommu_init_table(tbl); - } else { - /* Lower than first child or under python, use parent table */ - pci->iommu_table = PCI_DN(pdn)->iommu_table; - } + if (!isa_dn) { + /* No ISA/IDE - just set window size and return */ + pci->phb->dma_window_size = 0x80000000ul; /* To be divided */ + + while (pci->phb->dma_window_size * children > 0x80000000ul) + pci->phb->dma_window_size >>= 1; + DBG("No ISA/IDE, window size is 0x%lx\n", + pci->phb->dma_window_size); + pci->phb->dma_window_base_cur = 0; + + return; } + + /* If we have ISA, then we probably have an IDE + * controller too. Allocate a 128MB table but + * skip the first 128MB to avoid stepping on ISA + * space. + */ + pci->phb->dma_window_size = 0x8000000ul; + pci->phb->dma_window_base_cur = 0x8000000ul; + + tbl = kmalloc(sizeof(struct iommu_table), GFP_KERNEL); + + iommu_table_setparms(pci->phb, dn, tbl); + pci->iommu_table = iommu_init_table(tbl); + + /* Divide the rest (1.75GB) among the children */ + pci->phb->dma_window_size = 0x80000000ul; + while (pci->phb->dma_window_size * children > 0x70000000ul) + pci->phb->dma_window_size >>= 1; + + DBG("ISA/IDE, window size is 0x%lx\n", pci->phb->dma_window_size); + } @@ -462,21 +457,36 @@ static void iommu_bus_setup_pSeriesLP(struct pci_bus *bus) static void iommu_dev_setup_pSeries(struct pci_dev *dev) { struct device_node *dn, *mydn; + struct iommu_table *tbl; + + DBG("iommu_dev_setup_pSeries, dev %p (%s)\n", dev, pci_name(dev)); - DBG("iommu_dev_setup_pSeries, dev %p (%s)\n", dev, dev->pretty_name); - /* Now copy the iommu_table ptr from the bus device down to the - * pci device_node. This means get_iommu_table() won't need to search - * up the device tree to find it. - */ mydn = dn = pci_device_to_OF_node(dev); + /* If we're the direct child of a root bus, then we need to allocate + * an iommu table ourselves. The bus setup code should have setup + * the window sizes already. + */ + if (!dev->bus->self) { + DBG(" --> first child, no bridge. Allocating iommu table.\n"); + tbl = kmalloc(sizeof(struct iommu_table), GFP_KERNEL); + iommu_table_setparms(PCI_DN(dn)->phb, dn, tbl); + PCI_DN(mydn)->iommu_table = iommu_init_table(tbl); + + return; + } + + /* If this device is further down the bus tree, search upwards until + * an already allocated iommu table is found and use that. + */ + while (dn && dn->data && PCI_DN(dn)->iommu_table == NULL) dn = dn->parent; if (dn && dn->data) { PCI_DN(mydn)->iommu_table = PCI_DN(dn)->iommu_table; } else { - DBG("iommu_dev_setup_pSeries, dev %p (%s) has no iommu table\n", dev, dev->pretty_name); + DBG("iommu_dev_setup_pSeries, dev %p (%s) has no iommu table\n", dev, pci_name(dev)); } } @@ -510,7 +520,7 @@ static void iommu_dev_setup_pSeriesLP(struct pci_dev *dev) int *dma_window = NULL; struct pci_dn *pci; - DBG("iommu_dev_setup_pSeriesLP, dev %p (%s)\n", dev, dev->pretty_name); + DBG("iommu_dev_setup_pSeriesLP, dev %p (%s)\n", dev, pci_name(dev)); /* dev setup for LPAR is a little tricky, since the device tree might * contain the dma-window properties per-device and not neccesarily @@ -532,9 +542,8 @@ static void iommu_dev_setup_pSeriesLP(struct pci_dev *dev) * slots on POWER4 machines. */ if (dma_window == NULL || pdn->parent == NULL) { - /* Fall back to regular (non-LPAR) dev setup */ - DBG("No dma window for device, falling back to regular setup\n"); - iommu_dev_setup_pSeries(dev); + DBG("No dma window for device, linking to parent\n"); + PCI_DN(dn)->iommu_table = PCI_DN(pdn)->iommu_table; return; } else { DBG("Found DMA window, allocating table\n"); diff --git a/arch/ppc64/kernel/pci.c b/arch/ppc64/kernel/pci.c index 861138ad092c..ff4be1da69d5 100644 --- a/arch/ppc64/kernel/pci.c +++ b/arch/ppc64/kernel/pci.c @@ -246,11 +246,14 @@ static unsigned int pci_parse_of_flags(u32 addr0) unsigned int flags = 0; if (addr0 & 0x02000000) { - flags |= IORESOURCE_MEM; + flags = IORESOURCE_MEM | PCI_BASE_ADDRESS_SPACE_MEMORY; + flags |= (addr0 >> 22) & PCI_BASE_ADDRESS_MEM_TYPE_64; + flags |= (addr0 >> 28) & PCI_BASE_ADDRESS_MEM_TYPE_1M; if (addr0 & 0x40000000) - flags |= IORESOURCE_PREFETCH; + flags |= IORESOURCE_PREFETCH + | PCI_BASE_ADDRESS_MEM_PREFETCH; } else if (addr0 & 0x01000000) - flags |= IORESOURCE_IO; + flags = IORESOURCE_IO | PCI_BASE_ADDRESS_SPACE_IO; return flags; } diff --git a/arch/ppc64/kernel/pmac_setup.c b/arch/ppc64/kernel/pmac_setup.c index 1f5118078f7d..bb0c3bfbb7e2 100644 --- a/arch/ppc64/kernel/pmac_setup.c +++ b/arch/ppc64/kernel/pmac_setup.c @@ -434,15 +434,23 @@ static int pmac_check_legacy_ioport(unsigned int baseport) static int __init pmac_declare_of_platform_devices(void) { - struct device_node *np; + struct device_node *np, *npp; - np = find_devices("u3"); - if (np) { - for (np = np->child; np != NULL; np = np->sibling) + npp = of_find_node_by_name(NULL, "u3"); + if (npp) { + for (np = NULL; (np = of_get_next_child(npp, np)) != NULL;) { if (strncmp(np->name, "i2c", 3) == 0) { - of_platform_device_create(np, "u3-i2c"); + of_platform_device_create(np, "u3-i2c", NULL); + of_node_put(np); break; } + } + of_node_put(npp); + } + npp = of_find_node_by_type(NULL, "smu"); + if (npp) { + of_platform_device_create(npp, "smu", NULL); + of_node_put(npp); } return 0; diff --git a/arch/ppc64/kernel/pmac_time.c b/arch/ppc64/kernel/pmac_time.c index 6c8c99295e7a..9d8c97decd32 100644 --- a/arch/ppc64/kernel/pmac_time.c +++ b/arch/ppc64/kernel/pmac_time.c @@ -84,7 +84,7 @@ void pmac_get_rtc_time(struct rtc_time *tm) #ifdef CONFIG_PMAC_SMU case SYS_CTRLER_SMU: - smu_get_rtc_time(tm); + smu_get_rtc_time(tm, 1); break; #endif /* CONFIG_PMAC_SMU */ default: @@ -128,7 +128,7 @@ int pmac_set_rtc_time(struct rtc_time *tm) #ifdef CONFIG_PMAC_SMU case SYS_CTRLER_SMU: - return smu_set_rtc_time(tm); + return smu_set_rtc_time(tm, 1); #endif /* CONFIG_PMAC_SMU */ default: return -ENODEV; diff --git a/arch/ppc64/kernel/prom_init.c b/arch/ppc64/kernel/prom_init.c index 9979919cdf92..f252670874a4 100644 --- a/arch/ppc64/kernel/prom_init.c +++ b/arch/ppc64/kernel/prom_init.c @@ -1711,6 +1711,7 @@ static void __init flatten_device_tree(void) unsigned long offset = reloc_offset(); unsigned long mem_start, mem_end, room; struct boot_param_header *hdr; + struct prom_t *_prom = PTRRELOC(&prom); char *namep; u64 *rsvmap; @@ -1765,6 +1766,7 @@ static void __init flatten_device_tree(void) RELOC(dt_struct_end) = PAGE_ALIGN(mem_start); /* Finish header */ + hdr->boot_cpuid_phys = _prom->cpu; hdr->magic = OF_DT_HEADER; hdr->totalsize = RELOC(dt_struct_end) - RELOC(dt_header_start); hdr->off_dt_struct = RELOC(dt_struct_start) - RELOC(dt_header_start); @@ -1854,7 +1856,6 @@ static void __init prom_find_boot_cpu(void) cpu_pkg = call_prom("instance-to-package", 1, 1, prom_cpu); - prom_setprop(cpu_pkg, "linux,boot-cpu", NULL, 0); prom_getprop(cpu_pkg, "reg", &getprop_rval, sizeof(getprop_rval)); _prom->cpu = getprop_rval; diff --git a/arch/ppc64/kernel/ptrace.c b/arch/ppc64/kernel/ptrace.c index 85ed3188a91d..b1c044ca5756 100644 --- a/arch/ppc64/kernel/ptrace.c +++ b/arch/ppc64/kernel/ptrace.c @@ -219,6 +219,7 @@ int sys_ptrace(long request, long pid, long addr, long data) case PTRACE_SET_DEBUGREG: ret = ptrace_set_debugreg(child, addr, data); + break; case PTRACE_DETACH: ret = ptrace_detach(child, data); diff --git a/arch/ppc64/mm/hash_native.c b/arch/ppc64/mm/hash_native.c index 29b074505d3e..874cf96938ff 100644 --- a/arch/ppc64/mm/hash_native.c +++ b/arch/ppc64/mm/hash_native.c @@ -342,15 +342,14 @@ static void native_flush_hash_range(unsigned long number, int local) hpte_t *hptep; unsigned long hpte_v; struct ppc64_tlb_batch *batch = &__get_cpu_var(ppc64_tlb_batch); - - /* XXX fix for large ptes */ - unsigned long large = 0; + unsigned long large; local_irq_save(flags); j = 0; for (i = 0; i < number; i++) { va = batch->vaddr[j]; + large = pte_huge(batch->pte[i]); if (large) vpn = va >> HPAGE_SHIFT; else diff --git a/arch/ppc64/mm/hugetlbpage.c b/arch/ppc64/mm/hugetlbpage.c index 338771ec70d7..0ea0994ed974 100644 --- a/arch/ppc64/mm/hugetlbpage.c +++ b/arch/ppc64/mm/hugetlbpage.c @@ -710,10 +710,13 @@ repeat: hpte_group = ((~hash & htab_hash_mask) * HPTES_PER_GROUP) & ~0x7UL; slot = ppc_md.hpte_insert(hpte_group, va, prpn, - HPTE_V_LARGE, rflags); + HPTE_V_LARGE | + HPTE_V_SECONDARY, + rflags); if (slot == -1) { if (mftb() & 0x1) - hpte_group = ((hash & htab_hash_mask) * HPTES_PER_GROUP) & ~0x7UL; + hpte_group = ((hash & htab_hash_mask) * + HPTES_PER_GROUP)&~0x7UL; ppc_md.hpte_remove(hpte_group); goto repeat; diff --git a/arch/sparc64/kernel/entry.S b/arch/sparc64/kernel/entry.S index 3e0badb820c5..b48349527853 100644 --- a/arch/sparc64/kernel/entry.S +++ b/arch/sparc64/kernel/entry.S @@ -42,19 +42,15 @@ * executing (see inherit_locked_prom_mappings() rant). */ sparc64_vpte_nucleus: - /* Load 0xf0000000, which is LOW_OBP_ADDRESS. */ - mov 0xf, %g5 - sllx %g5, 28, %g5 - - /* Is addr >= LOW_OBP_ADDRESS? */ + /* Note that kvmap below has verified that the address is + * in the range MODULES_VADDR --> VMALLOC_END already. So + * here we need only check if it is an OBP address or not. + */ + sethi %hi(LOW_OBP_ADDRESS), %g5 cmp %g4, %g5 blu,pn %xcc, sparc64_vpte_patchme1 mov 0x1, %g5 - - /* Load 0x100000000, which is HI_OBP_ADDRESS. */ sllx %g5, 32, %g5 - - /* Is addr < HI_OBP_ADDRESS? */ cmp %g4, %g5 blu,pn %xcc, obp_iaddr_patch nop @@ -156,26 +152,29 @@ obp_daddr_patch: * rather, use information saved during inherit_prom_mappings() using 8k * pagesize. */ + .align 32 kvmap: - /* Load 0xf0000000, which is LOW_OBP_ADDRESS. */ - mov 0xf, %g5 - sllx %g5, 28, %g5 + sethi %hi(MODULES_VADDR), %g5 + cmp %g4, %g5 + blu,pn %xcc, longpath + mov (VMALLOC_END >> 24), %g5 + sllx %g5, 24, %g5 + cmp %g4, %g5 + bgeu,pn %xcc, longpath + nop - /* Is addr >= LOW_OBP_ADDRESS? */ +kvmap_check_obp: + sethi %hi(LOW_OBP_ADDRESS), %g5 cmp %g4, %g5 - blu,pn %xcc, vmalloc_addr + blu,pn %xcc, kvmap_vmalloc_addr mov 0x1, %g5 - - /* Load 0x100000000, which is HI_OBP_ADDRESS. */ sllx %g5, 32, %g5 - - /* Is addr < HI_OBP_ADDRESS? */ cmp %g4, %g5 blu,pn %xcc, obp_daddr_patch nop -vmalloc_addr: - /* If we get here, a vmalloc addr accessed, load kernel VPTE. */ +kvmap_vmalloc_addr: + /* If we get here, a vmalloc addr was accessed, load kernel VPTE. */ ldxa [%g3 + %g6] ASI_N, %g5 brgez,pn %g5, longpath nop diff --git a/arch/sparc64/kernel/ptrace.c b/arch/sparc64/kernel/ptrace.c index 23ad839d113f..5efbff90d668 100644 --- a/arch/sparc64/kernel/ptrace.c +++ b/arch/sparc64/kernel/ptrace.c @@ -30,6 +30,7 @@ #include <asm/psrcompat.h> #include <asm/visasm.h> #include <asm/spitfire.h> +#include <asm/page.h> /* Returning from ptrace is a bit tricky because the syscall return * low level code assumes any value returned which is negative and @@ -128,20 +129,20 @@ void flush_ptrace_access(struct vm_area_struct *vma, struct page *page, * is mapped to in the user's address space, we can skip the * D-cache flush. */ - if ((uaddr ^ kaddr) & (1UL << 13)) { + if ((uaddr ^ (unsigned long) kaddr) & (1UL << 13)) { unsigned long start = __pa(kaddr); unsigned long end = start + len; if (tlb_type == spitfire) { for (; start < end; start += 32) - spitfire_put_dcache_tag(va & 0x3fe0, 0x0); + spitfire_put_dcache_tag(start & 0x3fe0, 0x0); } else { for (; start < end; start += 32) __asm__ __volatile__( "stxa %%g0, [%0] %1\n\t" "membar #Sync" : /* no outputs */ - : "r" (va), + : "r" (start), "i" (ASI_DCACHE_INVALIDATE)); } } diff --git a/arch/sparc64/kernel/una_asm.S b/arch/sparc64/kernel/una_asm.S index cbb40585253c..da48400bcc95 100644 --- a/arch/sparc64/kernel/una_asm.S +++ b/arch/sparc64/kernel/una_asm.S @@ -17,7 +17,7 @@ kernel_unaligned_trap_fault: __do_int_store: rd %asi, %o4 wr %o3, 0, %asi - ldx [%o2], %g3 + mov %o2, %g3 cmp %o1, 2 be,pn %icc, 2f cmp %o1, 4 diff --git a/arch/sparc64/kernel/unaligned.c b/arch/sparc64/kernel/unaligned.c index da9739f0d437..42718f6a7d36 100644 --- a/arch/sparc64/kernel/unaligned.c +++ b/arch/sparc64/kernel/unaligned.c @@ -184,13 +184,14 @@ extern void do_int_load(unsigned long *dest_reg, int size, unsigned long *saddr, int is_signed, int asi); extern void __do_int_store(unsigned long *dst_addr, int size, - unsigned long *src_val, int asi); + unsigned long src_val, int asi); static inline void do_int_store(int reg_num, int size, unsigned long *dst_addr, - struct pt_regs *regs, int asi) + struct pt_regs *regs, int asi, int orig_asi) { unsigned long zero = 0; - unsigned long *src_val = &zero; + unsigned long *src_val_p = &zero; + unsigned long src_val; if (size == 16) { size = 8; @@ -198,7 +199,25 @@ static inline void do_int_store(int reg_num, int size, unsigned long *dst_addr, (unsigned)fetch_reg(reg_num, regs) : 0)) << 32) | (unsigned)fetch_reg(reg_num + 1, regs); } else if (reg_num) { - src_val = fetch_reg_addr(reg_num, regs); + src_val_p = fetch_reg_addr(reg_num, regs); + } + src_val = *src_val_p; + if (unlikely(asi != orig_asi)) { + switch (size) { + case 2: + src_val = swab16(src_val); + break; + case 4: + src_val = swab32(src_val); + break; + case 8: + src_val = swab64(src_val); + break; + case 16: + default: + BUG(); + break; + }; } __do_int_store(dst_addr, size, src_val, asi); } @@ -276,6 +295,7 @@ asmlinkage void kernel_unaligned_trap(struct pt_regs *regs, unsigned int insn, u kernel_mna_trap_fault(); } else { unsigned long addr; + int orig_asi, asi; addr = compute_effective_address(regs, insn, ((insn >> 25) & 0x1f)); @@ -285,18 +305,48 @@ asmlinkage void kernel_unaligned_trap(struct pt_regs *regs, unsigned int insn, u regs->tpc, dirstrings[dir], addr, size, regs->u_regs[UREG_RETPC]); #endif + orig_asi = asi = decode_asi(insn, regs); + switch (asi) { + case ASI_NL: + case ASI_AIUPL: + case ASI_AIUSL: + case ASI_PL: + case ASI_SL: + case ASI_PNFL: + case ASI_SNFL: + asi &= ~0x08; + break; + }; switch (dir) { case load: do_int_load(fetch_reg_addr(((insn>>25)&0x1f), regs), size, (unsigned long *) addr, - decode_signedness(insn), - decode_asi(insn, regs)); + decode_signedness(insn), asi); + if (unlikely(asi != orig_asi)) { + unsigned long val_in = *(unsigned long *) addr; + switch (size) { + case 2: + val_in = swab16(val_in); + break; + case 4: + val_in = swab32(val_in); + break; + case 8: + val_in = swab64(val_in); + break; + case 16: + default: + BUG(); + break; + }; + *(unsigned long *) addr = val_in; + } break; case store: do_int_store(((insn>>25)&0x1f), size, (unsigned long *) addr, regs, - decode_asi(insn, regs)); + asi, orig_asi); break; default: diff --git a/arch/um/Makefile b/arch/um/Makefile index ce987266dac6..5b5af95721ab 100644 --- a/arch/um/Makefile +++ b/arch/um/Makefile @@ -53,9 +53,13 @@ SYS_DIR := $(ARCH_DIR)/include/sysdep-$(SUBARCH) # -Dvmap=kernel_vmap affects everything, and prevents anything from # referencing the libpcap.o symbol so named. +# +# Same things for in6addr_loopback - found in libc. CFLAGS += $(CFLAGS-y) -D__arch_um__ -DSUBARCH=\"$(SUBARCH)\" \ - $(ARCH_INCLUDE) $(MODE_INCLUDE) -Dvmap=kernel_vmap + $(ARCH_INCLUDE) $(MODE_INCLUDE) -Dvmap=kernel_vmap \ + -Din6addr_loopback=kernel_in6addr_loopback + AFLAGS += $(ARCH_INCLUDE) USER_CFLAGS := $(patsubst -I%,,$(CFLAGS)) diff --git a/arch/um/drivers/chan_kern.c b/arch/um/drivers/chan_kern.c index 14a12d6b3df6..16e7dc89f61d 100644 --- a/arch/um/drivers/chan_kern.c +++ b/arch/um/drivers/chan_kern.c @@ -19,18 +19,44 @@ #include "line.h" #include "os.h" -#ifdef CONFIG_NOCONFIG_CHAN +/* XXX: could well be moved to somewhere else, if needed. */ +static int my_printf(const char * fmt, ...) + __attribute__ ((format (printf, 1, 2))); + +static int my_printf(const char * fmt, ...) +{ + /* Yes, can be called on atomic context.*/ + char *buf = kmalloc(4096, GFP_ATOMIC); + va_list args; + int r; + + if (!buf) { + /* We print directly fmt. + * Yes, yes, yes, feel free to complain. */ + r = strlen(fmt); + } else { + va_start(args, fmt); + r = vsprintf(buf, fmt, args); + va_end(args); + fmt = buf; + } -/* The printk's here are wrong because we are complaining that there is no - * output device, but printk is printing to that output device. The user will - * never see the error. printf would be better, except it can't run on a - * kernel stack because it will overflow it. - * Use printk for now since that will avoid crashing. - */ + if (r) + r = os_write_file(1, fmt, r); + return r; + +} + +#ifdef CONFIG_NOCONFIG_CHAN +/* Despite its name, there's no added trailing newline. */ +static int my_puts(const char * buf) +{ + return os_write_file(1, buf, strlen(buf)); +} static void *not_configged_init(char *str, int device, struct chan_opts *opts) { - printk(KERN_ERR "Using a channel type which is configured out of " + my_puts("Using a channel type which is configured out of " "UML\n"); return(NULL); } @@ -38,27 +64,27 @@ static void *not_configged_init(char *str, int device, struct chan_opts *opts) static int not_configged_open(int input, int output, int primary, void *data, char **dev_out) { - printk(KERN_ERR "Using a channel type which is configured out of " + my_puts("Using a channel type which is configured out of " "UML\n"); return(-ENODEV); } static void not_configged_close(int fd, void *data) { - printk(KERN_ERR "Using a channel type which is configured out of " + my_puts("Using a channel type which is configured out of " "UML\n"); } static int not_configged_read(int fd, char *c_out, void *data) { - printk(KERN_ERR "Using a channel type which is configured out of " + my_puts("Using a channel type which is configured out of " "UML\n"); return(-EIO); } static int not_configged_write(int fd, const char *buf, int len, void *data) { - printk(KERN_ERR "Using a channel type which is configured out of " + my_puts("Using a channel type which is configured out of " "UML\n"); return(-EIO); } @@ -66,7 +92,7 @@ static int not_configged_write(int fd, const char *buf, int len, void *data) static int not_configged_console_write(int fd, const char *buf, int len, void *data) { - printk(KERN_ERR "Using a channel type which is configured out of " + my_puts("Using a channel type which is configured out of " "UML\n"); return(-EIO); } @@ -74,14 +100,14 @@ static int not_configged_console_write(int fd, const char *buf, int len, static int not_configged_window_size(int fd, void *data, unsigned short *rows, unsigned short *cols) { - printk(KERN_ERR "Using a channel type which is configured out of " + my_puts("Using a channel type which is configured out of " "UML\n"); return(-ENODEV); } static void not_configged_free(void *data) { - printf(KERN_ERR "Using a channel type which is configured out of " + my_puts("Using a channel type which is configured out of " "UML\n"); } @@ -457,7 +483,7 @@ static struct chan *parse_chan(char *str, int pri, int device, } } if(ops == NULL){ - printk(KERN_ERR "parse_chan couldn't parse \"%s\"\n", + my_printf("parse_chan couldn't parse \"%s\"\n", str); return(NULL); } @@ -465,7 +491,7 @@ static struct chan *parse_chan(char *str, int pri, int device, data = (*ops->init)(str, device, opts); if(data == NULL) return(NULL); - chan = kmalloc(sizeof(*chan), GFP_KERNEL); + chan = kmalloc(sizeof(*chan), GFP_ATOMIC); if(chan == NULL) return(NULL); *chan = ((struct chan) { .list = LIST_HEAD_INIT(chan->list), .primary = 1, diff --git a/arch/um/drivers/mconsole_user.c b/arch/um/drivers/mconsole_user.c index 310c1f823f26..04383f98f4d5 100644 --- a/arch/um/drivers/mconsole_user.c +++ b/arch/um/drivers/mconsole_user.c @@ -23,7 +23,7 @@ static struct mconsole_command commands[] = { { "reboot", mconsole_reboot, MCONSOLE_PROC }, { "config", mconsole_config, MCONSOLE_PROC }, { "remove", mconsole_remove, MCONSOLE_PROC }, - { "sysrq", mconsole_sysrq, MCONSOLE_INTR }, + { "sysrq", mconsole_sysrq, MCONSOLE_PROC }, { "help", mconsole_help, MCONSOLE_INTR }, { "cad", mconsole_cad, MCONSOLE_INTR }, { "stop", mconsole_stop, MCONSOLE_PROC }, diff --git a/arch/um/include/common-offsets.h b/arch/um/include/common-offsets.h index 0aa620970adb..782ac3a3baf9 100644 --- a/arch/um/include/common-offsets.h +++ b/arch/um/include/common-offsets.h @@ -12,4 +12,6 @@ DEFINE_STR(UM_KERN_WARNING, KERN_WARNING); DEFINE_STR(UM_KERN_NOTICE, KERN_NOTICE); DEFINE_STR(UM_KERN_INFO, KERN_INFO); DEFINE_STR(UM_KERN_DEBUG, KERN_DEBUG); -DEFINE(HOST_ELF_CLASS, ELF_CLASS); +DEFINE(UM_ELF_CLASS, ELF_CLASS); +DEFINE(UM_ELFCLASS32, ELFCLASS32); +DEFINE(UM_ELFCLASS64, ELFCLASS64); diff --git a/arch/um/include/user.h b/arch/um/include/user.h index 57ee9e261228..0f865ef46918 100644 --- a/arch/um/include/user.h +++ b/arch/um/include/user.h @@ -14,7 +14,9 @@ extern void *um_kmalloc_atomic(int size); extern void kfree(void *ptr); extern int in_aton(char *str); extern int open_gdb_chan(void); -extern int strlcpy(char *, const char *, int); +/* These use size_t, however unsigned long is correct on both i386 and x86_64. */ +extern unsigned long strlcpy(char *, const char *, unsigned long); +extern unsigned long strlcat(char *, const char *, unsigned long); extern void *um_vmalloc(int size); extern void vfree(void *ptr); diff --git a/arch/um/kernel/process_kern.c b/arch/um/kernel/process_kern.c index 39cf568ccfaf..ea65db679e9c 100644 --- a/arch/um/kernel/process_kern.c +++ b/arch/um/kernel/process_kern.c @@ -82,7 +82,8 @@ unsigned long alloc_stack(int order, int atomic) unsigned long page; int flags = GFP_KERNEL; - if(atomic) flags |= GFP_ATOMIC; + if (atomic) + flags = GFP_ATOMIC; page = __get_free_pages(flags, order); if(page == 0) return(0); diff --git a/arch/um/kernel/sigio_user.c b/arch/um/kernel/sigio_user.c index e89218958f38..a52751108aa1 100644 --- a/arch/um/kernel/sigio_user.c +++ b/arch/um/kernel/sigio_user.c @@ -340,7 +340,7 @@ static int setup_initial_poll(int fd) { struct pollfd *p; - p = um_kmalloc(sizeof(struct pollfd)); + p = um_kmalloc_atomic(sizeof(struct pollfd)); if(p == NULL){ printk("setup_initial_poll : failed to allocate poll\n"); return(-1); diff --git a/arch/um/kernel/skas/include/uaccess-skas.h b/arch/um/kernel/skas/include/uaccess-skas.h index 6ee3f3902e68..7da0c2def0ef 100644 --- a/arch/um/kernel/skas/include/uaccess-skas.h +++ b/arch/um/kernel/skas/include/uaccess-skas.h @@ -18,12 +18,6 @@ ((unsigned long) (addr) + (size) <= FIXADDR_USER_END) && \ ((unsigned long) (addr) + (size) >= (unsigned long)(addr)))) -static inline int verify_area_skas(int type, const void __user * addr, - unsigned long size) -{ - return(access_ok_skas(type, addr, size) ? 0 : -EFAULT); -} - extern int copy_from_user_skas(void *to, const void __user *from, int n); extern int copy_to_user_skas(void __user *to, const void *from, int n); extern int strncpy_from_user_skas(char *dst, const char __user *src, int count); diff --git a/arch/um/kernel/tlb.c b/arch/um/kernel/tlb.c index 0a562c3c0fd8..f5b0636f9ad7 100644 --- a/arch/um/kernel/tlb.c +++ b/arch/um/kernel/tlb.c @@ -193,12 +193,12 @@ void fix_range_common(struct mm_struct *mm, unsigned long start_addr, r = pte_read(*npte); w = pte_write(*npte); x = pte_exec(*npte); - if(!pte_dirty(*npte)) - w = 0; - if(!pte_young(*npte)){ - r = 0; - w = 0; - } + if (!pte_young(*npte)) { + r = 0; + w = 0; + } else if (!pte_dirty(*npte)) { + w = 0; + } if(force || pte_newpage(*npte)){ if(pte_present(*npte)) ret = add_mmap(addr, diff --git a/arch/um/kernel/trap_kern.c b/arch/um/kernel/trap_kern.c index 87cc6fd76ced..d297429ac360 100644 --- a/arch/um/kernel/trap_kern.c +++ b/arch/um/kernel/trap_kern.c @@ -18,6 +18,7 @@ #include "asm/a.out.h" #include "asm/current.h" #include "asm/irq.h" +#include "sysdep/sigcontext.h" #include "user_util.h" #include "kern_util.h" #include "kern.h" @@ -39,6 +40,12 @@ int handle_page_fault(unsigned long address, unsigned long ip, int err = -EFAULT; *code_out = SEGV_MAPERR; + + /* If the fault was during atomic operation, don't take the fault, just + * fail. */ + if (in_atomic()) + goto out_nosemaphore; + down_read(&mm->mmap_sem); vma = find_vma(mm, address); if(!vma) @@ -89,6 +96,7 @@ survive: flush_tlb_page(vma, address); out: up_read(&mm->mmap_sem); +out_nosemaphore: return(err); /* @@ -125,7 +133,15 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user, void *sc) } else if(current->mm == NULL) panic("Segfault with no mm"); - err = handle_page_fault(address, ip, is_write, is_user, &si.si_code); + + if (SEGV_IS_FIXABLE(&fi)) + err = handle_page_fault(address, ip, is_write, is_user, &si.si_code); + else { + err = -EFAULT; + /* A thread accessed NULL, we get a fault, but CR2 is invalid. + * This code is used in __do_copy_from_user() of TT mode. */ + address = 0; + } catcher = current->thread.fault_catcher; if(!err) diff --git a/arch/um/kernel/tt/include/uaccess-tt.h b/arch/um/kernel/tt/include/uaccess-tt.h index aa6db384af80..dc2ebfa8c54f 100644 --- a/arch/um/kernel/tt/include/uaccess-tt.h +++ b/arch/um/kernel/tt/include/uaccess-tt.h @@ -33,12 +33,6 @@ extern unsigned long uml_physmem; (((unsigned long) (addr) <= ((unsigned long) (addr) + (size))) && \ (under_task_size(addr, size) || is_stack(addr, size)))) -static inline int verify_area_tt(int type, const void __user * addr, - unsigned long size) -{ - return(access_ok_tt(type, addr, size) ? 0 : -EFAULT); -} - extern unsigned long get_fault_addr(void); extern int __do_copy_from_user(void *to, const void *from, int n, diff --git a/arch/um/kernel/tt/process_kern.c b/arch/um/kernel/tt/process_kern.c index 0de05a268b22..cfaa373a6e77 100644 --- a/arch/um/kernel/tt/process_kern.c +++ b/arch/um/kernel/tt/process_kern.c @@ -23,10 +23,11 @@ #include "mem_user.h" #include "tlb.h" #include "mode.h" +#include "mode_kern.h" #include "init.h" #include "tt.h" -int switch_to_tt(void *prev, void *next, void *last) +void switch_to_tt(void *prev, void *next) { struct task_struct *from, *to, *prev_sched; unsigned long flags; diff --git a/arch/um/kernel/tt/uaccess_user.c b/arch/um/kernel/tt/uaccess_user.c index f01475512ecb..8c220f054b61 100644 --- a/arch/um/kernel/tt/uaccess_user.c +++ b/arch/um/kernel/tt/uaccess_user.c @@ -22,8 +22,15 @@ int __do_copy_from_user(void *to, const void *from, int n, __do_copy, &faulted); TASK_REGS(get_current())->tt = save; - if(!faulted) return(0); - else return(n - (fault - (unsigned long) from)); + if(!faulted) + return 0; + else if (fault) + return n - (fault - (unsigned long) from); + else + /* In case of a general protection fault, we don't have the + * fault address, so NULL is used instead. Pretend we didn't + * copy anything. */ + return n; } static void __do_strncpy(void *dst, const void *src, int count) diff --git a/arch/um/kernel/umid.c b/arch/um/kernel/umid.c index 186c28885016..0b21d59ba0cd 100644 --- a/arch/um/kernel/umid.c +++ b/arch/um/kernel/umid.c @@ -31,6 +31,8 @@ static char *uml_dir = UML_DIR; /* Changed by set_umid */ static int umid_is_random = 1; static int umid_inited = 0; +/* Have we created the files? Should we remove them? */ +static int umid_owned = 0; static int make_umid(int (*printer)(const char *fmt, ...)); @@ -82,20 +84,21 @@ int __init umid_file_name(char *name, char *buf, int len) extern int tracing_pid; -static int __init create_pid_file(void) +static void __init create_pid_file(void) { char file[strlen(uml_dir) + UMID_LEN + sizeof("/pid\0")]; char pid[sizeof("nnnnn\0")]; int fd, n; - if(umid_file_name("pid", file, sizeof(file))) return 0; + if(umid_file_name("pid", file, sizeof(file))) + return; fd = os_open_file(file, of_create(of_excl(of_rdwr(OPENFLAGS()))), 0644); if(fd < 0){ printf("Open of machine pid file \"%s\" failed: %s\n", file, strerror(-fd)); - return 0; + return; } sprintf(pid, "%d\n", os_getpid()); @@ -103,7 +106,6 @@ static int __init create_pid_file(void) if(n != strlen(pid)) printf("Write of pid file failed - err = %d\n", -n); os_close_file(fd); - return 0; } static int actually_do_remove(char *dir) @@ -147,7 +149,8 @@ static int actually_do_remove(char *dir) void remove_umid_dir(void) { char dir[strlen(uml_dir) + UMID_LEN + 1]; - if(!umid_inited) return; + if (!umid_owned) + return; sprintf(dir, "%s%s", uml_dir, umid); actually_do_remove(dir); @@ -155,11 +158,12 @@ void remove_umid_dir(void) char *get_umid(int only_if_set) { - if(only_if_set && umid_is_random) return(NULL); - return(umid); + if(only_if_set && umid_is_random) + return NULL; + return umid; } -int not_dead_yet(char *dir) +static int not_dead_yet(char *dir) { char file[strlen(uml_dir) + UMID_LEN + sizeof("/pid\0")]; char pid[sizeof("nnnnn\0")], *end; @@ -193,7 +197,8 @@ int not_dead_yet(char *dir) (p == CHOOSE_MODE(tracing_pid, os_getpid()))) dead = 1; } - if(!dead) return(1); + if(!dead) + return(1); return(actually_do_remove(dir)); } @@ -232,16 +237,13 @@ static int __init make_uml_dir(void) strlcpy(dir, home, sizeof(dir)); uml_dir++; } + strlcat(dir, uml_dir, sizeof(dir)); len = strlen(dir); - strncat(dir, uml_dir, sizeof(dir) - len); - len = strlen(dir); - if((len > 0) && (len < sizeof(dir) - 1) && (dir[len - 1] != '/')){ - dir[len] = '/'; - dir[len + 1] = '\0'; - } + if (len > 0 && dir[len - 1] != '/') + strlcat(dir, "/", sizeof(dir)); uml_dir = malloc(strlen(dir) + 1); - if(uml_dir == NULL){ + if (uml_dir == NULL) { printf("make_uml_dir : malloc failed, errno = %d\n", errno); exit(1); } @@ -286,6 +288,7 @@ static int __init make_umid(int (*printer)(const char *fmt, ...)) if(errno == EEXIST){ if(not_dead_yet(tmp)){ (*printer)("umid '%s' is in use\n", umid); + umid_owned = 0; return(-1); } err = mkdir(tmp, 0777); @@ -296,7 +299,8 @@ static int __init make_umid(int (*printer)(const char *fmt, ...)) return(-1); } - return(0); + umid_owned = 1; + return 0; } __uml_setup("uml_dir=", set_uml_dir, @@ -309,7 +313,8 @@ static int __init make_umid_setup(void) /* one function with the ordering we need ... */ make_uml_dir(); make_umid(printf); - return create_pid_file(); + create_pid_file(); + return 0; } __uml_postsetup(make_umid_setup); diff --git a/arch/um/kernel/user_util.c b/arch/um/kernel/user_util.c index a25f3ea11fd7..41d17c71511c 100644 --- a/arch/um/kernel/user_util.c +++ b/arch/um/kernel/user_util.c @@ -128,6 +128,12 @@ void setup_machinename(char *machine_out) struct utsname host; uname(&host); +#if defined(UML_CONFIG_UML_X86) && !defined(UML_CONFIG_64BIT) + if (!strcmp(host.machine, "x86_64")) { + strcpy(machine_out, "i686"); + return; + } +#endif strcpy(machine_out, host.machine); } diff --git a/arch/um/os-Linux/aio.c b/arch/um/os-Linux/aio.c index 298d5632128b..f6e64026f995 100644 --- a/arch/um/os-Linux/aio.c +++ b/arch/um/os-Linux/aio.c @@ -144,6 +144,7 @@ static int aio_thread(void *arg) "errno = %d\n", errno); } else { + /* This is safe as we've just a pointer here. */ aio = (struct aio_context *) (long) event.data; if(update_aio(aio, event.res)){ do_aio(ctx, aio); diff --git a/arch/um/os-Linux/elf_aux.c b/arch/um/os-Linux/elf_aux.c index e770cb029578..ab33cb3c74ec 100644 --- a/arch/um/os-Linux/elf_aux.c +++ b/arch/um/os-Linux/elf_aux.c @@ -14,7 +14,8 @@ #include "mem_user.h" #include <kernel-offsets.h> -#if HOST_ELF_CLASS == ELFCLASS32 +/* Use the one from the kernel - the host may miss it, if having old headers. */ +#if UM_ELF_CLASS == UM_ELFCLASS32 typedef Elf32_auxv_t elf_auxv_t; #else typedef Elf64_auxv_t elf_auxv_t; diff --git a/arch/um/os-Linux/process.c b/arch/um/os-Linux/process.c index d32413e4b4ce..d9c52387c4a1 100644 --- a/arch/um/os-Linux/process.c +++ b/arch/um/os-Linux/process.c @@ -3,6 +3,7 @@ * Licensed under the GPL */ +#include <unistd.h> #include <stdio.h> #include <errno.h> #include <signal.h> diff --git a/arch/um/sys-i386/ldt.c b/arch/um/sys-i386/ldt.c index bd3c34aa52e5..36b5c2c13289 100644 --- a/arch/um/sys-i386/ldt.c +++ b/arch/um/sys-i386/ldt.c @@ -83,6 +83,7 @@ int sys_modify_ldt(int func, void __user *ptr, unsigned long bytecount) goto out; } p = buf; + break; default: res = -ENOSYS; goto out; diff --git a/arch/x86_64/Kconfig b/arch/x86_64/Kconfig index 0969d570f3b5..21afa69a086d 100644 --- a/arch/x86_64/Kconfig +++ b/arch/x86_64/Kconfig @@ -308,7 +308,7 @@ config HPET_TIMER present. The HPET provides a stable time base on SMP systems, unlike the TSC, but it is more expensive to access, as it is off-chip. You can find the HPET spec at - <http://www.intel.com/labs/platcomp/hpet/hpetspec.htm>. + <http://www.intel.com/hardwaredesign/hpetspec.htm>. config X86_PM_TIMER bool "PM timer" diff --git a/arch/xtensa/kernel/pci.c b/arch/xtensa/kernel/pci.c index 09887c96e9a1..de19501aa809 100644 --- a/arch/xtensa/kernel/pci.c +++ b/arch/xtensa/kernel/pci.c @@ -402,8 +402,8 @@ int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, __pci_mmap_set_flags(dev, vma, mmap_state); __pci_mmap_set_pgprot(dev, vma, mmap_state, write_combine); - ret = io_remap_page_range(vma, vma->vm_start, vma->vm_pgoff<<PAGE_SHIFT, - vma->vm_end - vma->vm_start, vma->vm_page_prot); + ret = io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, + vma->vm_end - vma->vm_start,vma->vm_page_prot); return ret; } diff --git a/arch/xtensa/kernel/platform.c b/arch/xtensa/kernel/platform.c index cf1362784443..03674daabc66 100644 --- a/arch/xtensa/kernel/platform.c +++ b/arch/xtensa/kernel/platform.c @@ -39,7 +39,7 @@ _F(int, pcibios_fixup, (void), { return 0; }); _F(int, get_rtc_time, (time_t* t), { return 0; }); _F(int, set_rtc_time, (time_t t), { return 0; }); -#if CONFIG_XTENSA_CALIBRATE_CCOUNT +#ifdef CONFIG_XTENSA_CALIBRATE_CCOUNT _F(void, calibrate_ccount, (void), { printk ("ERROR: Cannot calibrate cpu frequency! Assuming 100MHz.\n"); diff --git a/arch/xtensa/kernel/process.c b/arch/xtensa/kernel/process.c index c83bb0d41787..08ef6d82ee51 100644 --- a/arch/xtensa/kernel/process.c +++ b/arch/xtensa/kernel/process.c @@ -457,7 +457,7 @@ int dump_task_fpu(struct pt_regs *regs, struct task_struct *task, elf_fpregset_t *r) { /* see asm/coprocessor.h for this magic number 16 */ -#if TOTAL_CPEXTRA_SIZE > 16 +#if XTENSA_CP_EXTRA_SIZE > 16 do_save_fpregs (r, regs, task); /* For now, bit 16 means some extra state may be present: */ diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c index 1f5bf5d624e4..513ed8d67766 100644 --- a/arch/xtensa/kernel/setup.c +++ b/arch/xtensa/kernel/setup.c @@ -304,7 +304,7 @@ void __init setup_arch(char **cmdline_p) # endif #endif -#if CONFIG_PCI +#ifdef CONFIG_PCI platform_pcibios_init(); #endif } diff --git a/arch/xtensa/kernel/signal.c b/arch/xtensa/kernel/signal.c index dc42cede9394..e252b61e45a5 100644 --- a/arch/xtensa/kernel/signal.c +++ b/arch/xtensa/kernel/signal.c @@ -182,7 +182,7 @@ restore_cpextra (struct _cpstate *buf) struct task_struct *tsk = current; release_all_cp(tsk); - return __copy_from_user(tsk->thread.cpextra, buf, TOTAL_CPEXTRA_SIZE); + return __copy_from_user(tsk->thread.cpextra, buf, XTENSA_CP_EXTRA_SIZE); #endif return 0; } diff --git a/arch/xtensa/kernel/time.c b/arch/xtensa/kernel/time.c index 1ac7d5ce7456..8e423d1335ce 100644 --- a/arch/xtensa/kernel/time.c +++ b/arch/xtensa/kernel/time.c @@ -68,7 +68,7 @@ void __init time_init(void) * speed for the CALIBRATE. */ -#if CONFIG_XTENSA_CALIBRATE_CCOUNT +#ifdef CONFIG_XTENSA_CALIBRATE_CCOUNT printk("Calibrating CPU frequency "); platform_calibrate_ccount(); printk("%d.%02d MHz\n", (int)ccount_per_jiffy/(1000000/HZ), diff --git a/arch/xtensa/mm/init.c b/arch/xtensa/mm/init.c index 56aace84aaeb..5a91d6c9e66d 100644 --- a/arch/xtensa/mm/init.c +++ b/arch/xtensa/mm/init.c @@ -239,7 +239,7 @@ void __init mem_init(void) high_memory = (void *) __va(max_mapnr << PAGE_SHIFT); highmemsize = 0; -#if CONFIG_HIGHMEM +#ifdef CONFIG_HIGHMEM #error HIGHGMEM not implemented in init.c #endif diff --git a/drivers/acorn/char/pcf8583.c b/drivers/acorn/char/pcf8583.c index 141b4c237a50..2b850e5860a0 100644 --- a/drivers/acorn/char/pcf8583.c +++ b/drivers/acorn/char/pcf8583.c @@ -23,12 +23,13 @@ static struct i2c_driver pcf8583_driver; static unsigned short ignore[] = { I2C_CLIENT_END }; static unsigned short normal_addr[] = { 0x50, I2C_CLIENT_END }; +static unsigned short *forces[] = { NULL }; static struct i2c_client_address_data addr_data = { .normal_i2c = normal_addr, .probe = ignore, .ignore = ignore, - .force = ignore, + .forces = forces, }; #define DAT(x) ((unsigned int)(x->dev.driver_data)) diff --git a/drivers/base/class.c b/drivers/base/class.c index 3b112e3542f8..ce23dc8c18c5 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -669,6 +669,7 @@ void class_device_destroy(struct class *cls, dev_t devt) int class_device_rename(struct class_device *class_dev, char *new_name) { int error = 0; + char *old_class_name = NULL, *new_class_name = NULL; class_dev = class_device_get(class_dev); if (!class_dev) @@ -677,12 +678,24 @@ int class_device_rename(struct class_device *class_dev, char *new_name) pr_debug("CLASS: renaming '%s' to '%s'\n", class_dev->class_id, new_name); + if (class_dev->dev) + old_class_name = make_class_name(class_dev); + strlcpy(class_dev->class_id, new_name, KOBJ_NAME_LEN); error = kobject_rename(&class_dev->kobj, new_name); + if (class_dev->dev) { + new_class_name = make_class_name(class_dev); + sysfs_create_link(&class_dev->dev->kobj, &class_dev->kobj, + new_class_name); + sysfs_remove_link(&class_dev->dev->kobj, old_class_name); + } class_device_put(class_dev); + kfree(old_class_name); + kfree(new_class_name); + return error; } diff --git a/drivers/base/dd.c b/drivers/base/dd.c index d5bbce38282f..3565e9795301 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -40,6 +40,9 @@ */ void device_bind_driver(struct device * dev) { + if (klist_node_attached(&dev->knode_driver)) + return; + pr_debug("bound device '%s' to driver '%s'\n", dev->bus_id, dev->driver->name); klist_add_tail(&dev->knode_driver, &dev->driver->klist_devices); diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index c56f995aadad..486b6e1c7dfb 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -483,9 +483,6 @@ static int cciss_open(struct inode *inode, struct file *filep) printk(KERN_DEBUG "cciss_open %s\n", inode->i_bdev->bd_disk->disk_name); #endif /* CCISS_DEBUG */ - if (host->busy_initializing) - return -EBUSY; - if (host->busy_initializing || drv->busy_configuring) return -EBUSY; /* @@ -2991,6 +2988,7 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, hba[i]->access.set_intr_mask(hba[i], CCISS_INTR_ON); cciss_procinit(i); + hba[i]->busy_initializing = 0; for(j=0; j < NWD; j++) { /* mfm */ drive_info_struct *drv = &(hba[i]->drv[j]); @@ -3033,7 +3031,6 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, add_disk(disk); } - hba[i]->busy_initializing = 0; return(1); clean4: diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index 483d71b10cf9..baedac522945 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -2373,44 +2373,6 @@ int blkdev_issue_flush(struct block_device *bdev, sector_t *error_sector) EXPORT_SYMBOL(blkdev_issue_flush); -/** - * blkdev_scsi_issue_flush_fn - issue flush for SCSI devices - * @q: device queue - * @disk: gendisk - * @error_sector: error offset - * - * Description: - * Devices understanding the SCSI command set, can use this function as - * a helper for issuing a cache flush. Note: driver is required to store - * the error offset (in case of error flushing) in ->sector of struct - * request. - */ -int blkdev_scsi_issue_flush_fn(request_queue_t *q, struct gendisk *disk, - sector_t *error_sector) -{ - struct request *rq = blk_get_request(q, WRITE, __GFP_WAIT); - int ret; - - rq->flags |= REQ_BLOCK_PC | REQ_SOFTBARRIER; - rq->sector = 0; - memset(rq->cmd, 0, sizeof(rq->cmd)); - rq->cmd[0] = 0x35; - rq->cmd_len = 12; - rq->data = NULL; - rq->data_len = 0; - rq->timeout = 60 * HZ; - - ret = blk_execute_rq(q, disk, rq, 0); - - if (ret && error_sector) - *error_sector = rq->sector; - - blk_put_request(rq); - return ret; -} - -EXPORT_SYMBOL(blkdev_scsi_issue_flush_fn); - static void drive_stat_acct(struct request *rq, int nr_sectors, int new_io) { int rw = rq_data_dir(rq); diff --git a/drivers/block/ub.c b/drivers/block/ub.c index aa0bf7ee008d..ed4d5006fe62 100644 --- a/drivers/block/ub.c +++ b/drivers/block/ub.c @@ -172,7 +172,7 @@ struct bulk_cs_wrap { */ struct ub_dev; -#define UB_MAX_REQ_SG 4 +#define UB_MAX_REQ_SG 9 /* cdrecord requires 32KB and maybe a header */ #define UB_MAX_SECTORS 64 /* @@ -387,7 +387,7 @@ struct ub_dev { struct bulk_cs_wrap work_bcs; struct usb_ctrlrequest work_cr; - int sg_stat[UB_MAX_REQ_SG+1]; + int sg_stat[6]; struct ub_scsi_trace tr; }; @@ -525,12 +525,13 @@ static ssize_t ub_diag_show(struct device *dev, struct device_attribute *attr, "qlen %d qmax %d\n", sc->cmd_queue.qlen, sc->cmd_queue.qmax); cnt += sprintf(page + cnt, - "sg %d %d %d %d %d\n", + "sg %d %d %d %d %d .. %d\n", sc->sg_stat[0], sc->sg_stat[1], sc->sg_stat[2], sc->sg_stat[3], - sc->sg_stat[4]); + sc->sg_stat[4], + sc->sg_stat[5]); list_for_each (p, &sc->luns) { lun = list_entry(p, struct ub_lun, link); @@ -835,7 +836,7 @@ static int ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun, return -1; } cmd->nsg = n_elem; - sc->sg_stat[n_elem]++; + sc->sg_stat[n_elem < 5 ? n_elem : 5]++; /* * build the command @@ -891,7 +892,7 @@ static int ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun, return -1; } cmd->nsg = n_elem; - sc->sg_stat[n_elem]++; + sc->sg_stat[n_elem < 5 ? n_elem : 5]++; memcpy(&cmd->cdb, rq->cmd, rq->cmd_len); cmd->cdb_len = rq->cmd_len; @@ -1010,7 +1011,6 @@ static int ub_scsi_cmd_start(struct ub_dev *sc, struct ub_scsi_cmd *cmd) sc->last_pipe = sc->send_bulk_pipe; usb_fill_bulk_urb(&sc->work_urb, sc->dev, sc->send_bulk_pipe, bcb, US_BULK_CB_WRAP_LEN, ub_urb_complete, sc); - sc->work_urb.transfer_flags = 0; /* Fill what we shouldn't be filling, because usb-storage did so. */ sc->work_urb.actual_length = 0; @@ -1019,7 +1019,6 @@ static int ub_scsi_cmd_start(struct ub_dev *sc, struct ub_scsi_cmd *cmd) if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) { /* XXX Clear stalls */ - printk("ub: cmd #%d start failed (%d)\n", cmd->tag, rc); /* P3 */ ub_complete(&sc->work_done); return rc; } @@ -1190,11 +1189,9 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) return; } if (urb->status != 0) { - printk("ub: cmd #%d cmd status (%d)\n", cmd->tag, urb->status); /* P3 */ goto Bad_End; } if (urb->actual_length != US_BULK_CB_WRAP_LEN) { - printk("ub: cmd #%d xferred %d\n", cmd->tag, urb->actual_length); /* P3 */ /* XXX Must do reset here to unconfuse the device */ goto Bad_End; } @@ -1395,14 +1392,12 @@ static void ub_data_start(struct ub_dev *sc, struct ub_scsi_cmd *cmd) usb_fill_bulk_urb(&sc->work_urb, sc->dev, pipe, page_address(sg->page) + sg->offset, sg->length, ub_urb_complete, sc); - sc->work_urb.transfer_flags = 0; sc->work_urb.actual_length = 0; sc->work_urb.error_count = 0; sc->work_urb.status = 0; if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) { /* XXX Clear stalls */ - printk("ub: data #%d submit failed (%d)\n", cmd->tag, rc); /* P3 */ ub_complete(&sc->work_done); ub_state_done(sc, cmd, rc); return; @@ -1442,7 +1437,6 @@ static int __ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd) sc->last_pipe = sc->recv_bulk_pipe; usb_fill_bulk_urb(&sc->work_urb, sc->dev, sc->recv_bulk_pipe, &sc->work_bcs, US_BULK_CS_WRAP_LEN, ub_urb_complete, sc); - sc->work_urb.transfer_flags = 0; sc->work_urb.actual_length = 0; sc->work_urb.error_count = 0; sc->work_urb.status = 0; @@ -1563,7 +1557,6 @@ static int ub_submit_clear_stall(struct ub_dev *sc, struct ub_scsi_cmd *cmd, usb_fill_control_urb(&sc->work_urb, sc->dev, sc->send_ctrl_pipe, (unsigned char*) cr, NULL, 0, ub_urb_complete, sc); - sc->work_urb.transfer_flags = 0; sc->work_urb.actual_length = 0; sc->work_urb.error_count = 0; sc->work_urb.status = 0; @@ -2000,17 +1993,16 @@ static int ub_sync_getmaxlun(struct ub_dev *sc) usb_fill_control_urb(&sc->work_urb, sc->dev, sc->recv_ctrl_pipe, (unsigned char*) cr, p, 1, ub_probe_urb_complete, &compl); - sc->work_urb.transfer_flags = 0; sc->work_urb.actual_length = 0; sc->work_urb.error_count = 0; sc->work_urb.status = 0; if ((rc = usb_submit_urb(&sc->work_urb, GFP_KERNEL)) != 0) { if (rc == -EPIPE) { - printk("%s: Stall at GetMaxLUN, using 1 LUN\n", + printk("%s: Stall submitting GetMaxLUN, using 1 LUN\n", sc->name); /* P3 */ } else { - printk(KERN_WARNING + printk(KERN_NOTICE "%s: Unable to submit GetMaxLUN (%d)\n", sc->name, rc); } @@ -2028,6 +2020,18 @@ static int ub_sync_getmaxlun(struct ub_dev *sc) del_timer_sync(&timer); usb_kill_urb(&sc->work_urb); + if ((rc = sc->work_urb.status) < 0) { + if (rc == -EPIPE) { + printk("%s: Stall at GetMaxLUN, using 1 LUN\n", + sc->name); /* P3 */ + } else { + printk(KERN_NOTICE + "%s: Error at GetMaxLUN (%d)\n", + sc->name, rc); + } + goto err_io; + } + if (sc->work_urb.actual_length != 1) { printk("%s: GetMaxLUN returned %d bytes\n", sc->name, sc->work_urb.actual_length); /* P3 */ @@ -2048,6 +2052,7 @@ static int ub_sync_getmaxlun(struct ub_dev *sc) kfree(p); return nluns; +err_io: err_submit: kfree(p); err_alloc: @@ -2080,7 +2085,6 @@ static int ub_probe_clear_stall(struct ub_dev *sc, int stalled_pipe) usb_fill_control_urb(&sc->work_urb, sc->dev, sc->send_ctrl_pipe, (unsigned char*) cr, NULL, 0, ub_probe_urb_complete, &compl); - sc->work_urb.transfer_flags = 0; sc->work_urb.actual_length = 0; sc->work_urb.error_count = 0; sc->work_urb.status = 0; @@ -2213,8 +2217,10 @@ static int ub_probe(struct usb_interface *intf, * This is needed to clear toggles. It is a problem only if we do * `rmmod ub && modprobe ub` without disconnects, but we like that. */ +#if 0 /* iPod Mini fails if we do this (big white iPod works) */ ub_probe_clear_stall(sc, sc->recv_bulk_pipe); ub_probe_clear_stall(sc, sc->send_bulk_pipe); +#endif /* * The way this is used by the startup code is a little specific. @@ -2241,10 +2247,10 @@ static int ub_probe(struct usb_interface *intf, for (i = 0; i < 3; i++) { if ((rc = ub_sync_getmaxlun(sc)) < 0) { /* - * Some devices (i.e. Iomega Zip100) need this -- - * apparently the bulk pipes get STALLed when the - * GetMaxLUN request is processed. - * XXX I have a ZIP-100, verify it does this. + * This segment is taken from usb-storage. They say + * that ZIP-100 needs this, but my own ZIP-100 works + * fine without this. + * Still, it does not seem to hurt anything. */ if (rc == -EPIPE) { ub_probe_clear_stall(sc, sc->recv_bulk_pipe); @@ -2313,7 +2319,7 @@ static int ub_probe_lun(struct ub_dev *sc, int lnum) disk->first_minor = lun->id * UB_MINORS_PER_MAJOR; disk->fops = &ub_bd_fops; disk->private_data = lun; - disk->driverfs_dev = &sc->intf->dev; /* XXX Many to one ok? */ + disk->driverfs_dev = &sc->intf->dev; rc = -ENOMEM; if ((q = blk_init_queue(ub_request_fn, &sc->lock)) == NULL) @@ -2466,9 +2472,6 @@ static int __init ub_init(void) { int rc; - /* P3 */ printk("ub: sizeof ub_scsi_cmd %zu ub_dev %zu ub_lun %zu\n", - sizeof(struct ub_scsi_cmd), sizeof(struct ub_dev), sizeof(struct ub_lun)); - if ((rc = register_blkdev(UB_MAJOR, DRV_NAME)) != 0) goto err_regblkdev; devfs_mk_dir(DEVFS_NAME); diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index de0379b6d502..c055bb630ffc 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c @@ -273,7 +273,6 @@ static int hpet_mmap(struct file *file, struct vm_area_struct *vma) vma->vm_flags |= VM_IO; vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - addr = __pa(addr); if (io_remap_pfn_range(vma, vma->vm_start, addr >> PAGE_SHIFT, PAGE_SIZE, vma->vm_page_prot)) { diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 463351d4f942..32fa82c78c73 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -2620,7 +2620,7 @@ void ipmi_smi_msg_received(ipmi_smi_t intf, spin_lock_irqsave(&(intf->waiting_msgs_lock), flags); if (!list_empty(&(intf->waiting_msgs))) { list_add_tail(&(msg->link), &(intf->waiting_msgs)); - spin_unlock(&(intf->waiting_msgs_lock)); + spin_unlock_irqrestore(&(intf->waiting_msgs_lock), flags); goto out_unlock; } spin_unlock_irqrestore(&(intf->waiting_msgs_lock), flags); @@ -2629,9 +2629,9 @@ void ipmi_smi_msg_received(ipmi_smi_t intf, if (rv > 0) { /* Could not handle the message now, just add it to a list to handle later. */ - spin_lock(&(intf->waiting_msgs_lock)); + spin_lock_irqsave(&(intf->waiting_msgs_lock), flags); list_add_tail(&(msg->link), &(intf->waiting_msgs)); - spin_unlock(&(intf->waiting_msgs_lock)); + spin_unlock_irqrestore(&(intf->waiting_msgs_lock), flags); } else if (rv == 0) { ipmi_free_smi_msg(msg); } diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 7e72e922b41c..db358cfa7cbf 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -418,12 +418,11 @@ config SENSORS_HDAPS help This driver provides support for the IBM Hard Drive Active Protection System (hdaps), which provides an accelerometer and other misc. data. - Supported laptops include the IBM ThinkPad T41, T42, T43, and R51. - The accelerometer data is readable via sysfs. + ThinkPads starting with the R50, T41, and X40 are supported. The + accelerometer data is readable via sysfs. - This driver also provides an input class device, allowing the - laptop to act as a pinball machine-esque mouse. This is off by - default but enabled via sysfs or the module parameter "mousedev". + This driver also provides an absolute input class device, allowing + the laptop to act as a pinball machine-esque joystick. Say Y here if you have an applicable laptop and want to experience the awesome power of hdaps. diff --git a/drivers/hwmon/hdaps.c b/drivers/hwmon/hdaps.c index 4c56411f3993..7f0107613827 100644 --- a/drivers/hwmon/hdaps.c +++ b/drivers/hwmon/hdaps.c @@ -4,9 +4,9 @@ * Copyright (C) 2005 Robert Love <rml@novell.com> * Copyright (C) 2005 Jesper Juhl <jesper.juhl@gmail.com> * - * The HardDisk Active Protection System (hdaps) is present in the IBM ThinkPad - * T41, T42, T43, R50, R50p, R51, and X40, at least. It provides a basic - * two-axis accelerometer and other data, such as the device's temperature. + * The HardDisk Active Protection System (hdaps) is present in IBM ThinkPads + * starting with the R40, T41, and X40. It provides a basic two-axis + * accelerometer and other data, such as the device's temperature. * * This driver is based on the document by Mark A. Smith available at * http://www.almaden.ibm.com/cs/people/marksmith/tpaps.html and a lot of trial @@ -487,24 +487,19 @@ static struct attribute_group hdaps_attribute_group = { /* Module stuff */ -/* - * XXX: We should be able to return nonzero and halt the detection process. - * But there is a bug in dmi_check_system() where a nonzero return from the - * first match will result in a return of failure from dmi_check_system(). - * I fixed this; the patch is 2.6-git. Once in a released tree, we can make - * hdaps_dmi_match_invert() return hdaps_dmi_match(), which in turn returns 1. - */ +/* hdaps_dmi_match - found a match. return one, short-circuiting the hunt. */ static int hdaps_dmi_match(struct dmi_system_id *id) { printk(KERN_INFO "hdaps: %s detected.\n", id->ident); - return 0; + return 1; } +/* hdaps_dmi_match_invert - found an inverted match. */ static int hdaps_dmi_match_invert(struct dmi_system_id *id) { hdaps_invert = 1; printk(KERN_INFO "hdaps: inverting axis readings.\n"); - return 0; + return hdaps_dmi_match(id); } #define HDAPS_DMI_MATCH_NORMAL(model) { \ @@ -534,6 +529,7 @@ static int __init hdaps_init(void) HDAPS_DMI_MATCH_INVERT("ThinkPad R50p"), HDAPS_DMI_MATCH_NORMAL("ThinkPad R50"), HDAPS_DMI_MATCH_NORMAL("ThinkPad R51"), + HDAPS_DMI_MATCH_NORMAL("ThinkPad R52"), HDAPS_DMI_MATCH_INVERT("ThinkPad T41p"), HDAPS_DMI_MATCH_NORMAL("ThinkPad T41"), HDAPS_DMI_MATCH_INVERT("ThinkPad T42p"), @@ -541,6 +537,7 @@ static int __init hdaps_init(void) HDAPS_DMI_MATCH_NORMAL("ThinkPad T43"), HDAPS_DMI_MATCH_NORMAL("ThinkPad X40"), HDAPS_DMI_MATCH_NORMAL("ThinkPad X41 Tablet"), + HDAPS_DMI_MATCH_NORMAL("ThinkPad X41"), { .ident = NULL } }; diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 8334496a7e0a..3badfec75b1c 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -245,6 +245,18 @@ config I2C_KEYWEST This support is also available as a module. If so, the module will be called i2c-keywest. +config I2C_PMAC_SMU + tristate "Powermac SMU I2C interface" + depends on I2C && PMAC_SMU + help + This supports the use of the I2C interface in the SMU + chip on recent Apple machines like the iMac G5. It is used + among others by the thermal control driver for those machines. + Say Y if you have such a machine. + + This support is also available as a module. If so, the module + will be called i2c-pmac-smu. + config I2C_MPC tristate "MPC107/824x/85xx/52xx" depends on I2C && PPC32 diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 980b3e983670..f1df00f66c6c 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_I2C_ITE) += i2c-ite.o obj-$(CONFIG_I2C_IXP2000) += i2c-ixp2000.o obj-$(CONFIG_I2C_IXP4XX) += i2c-ixp4xx.o obj-$(CONFIG_I2C_KEYWEST) += i2c-keywest.o +obj-$(CONFIG_I2C_PMAC_SMU) += i2c-pmac-smu.o obj-$(CONFIG_I2C_MPC) += i2c-mpc.o obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o obj-$(CONFIG_I2C_NFORCE2) += i2c-nforce2.o diff --git a/drivers/i2c/busses/i2c-pmac-smu.c b/drivers/i2c/busses/i2c-pmac-smu.c new file mode 100644 index 000000000000..8a9f5648a23d --- /dev/null +++ b/drivers/i2c/busses/i2c-pmac-smu.c @@ -0,0 +1,316 @@ +/* + i2c Support for Apple SMU Controller + + Copyright (c) 2005 Benjamin Herrenschmidt, IBM Corp. + <benh@kernel.crashing.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/completion.h> +#include <linux/device.h> +#include <asm/prom.h> +#include <asm/of_device.h> +#include <asm/smu.h> + +static int probe; + +MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); +MODULE_DESCRIPTION("I2C driver for Apple's SMU"); +MODULE_LICENSE("GPL"); +module_param(probe, bool, 0); + + +/* Physical interface */ +struct smu_iface +{ + struct i2c_adapter adapter; + struct completion complete; + u32 busid; +}; + +static void smu_i2c_done(struct smu_i2c_cmd *cmd, void *misc) +{ + struct smu_iface *iface = misc; + complete(&iface->complete); +} + +/* + * SMBUS-type transfer entrypoint + */ +static s32 smu_smbus_xfer( struct i2c_adapter* adap, + u16 addr, + unsigned short flags, + char read_write, + u8 command, + int size, + union i2c_smbus_data* data) +{ + struct smu_iface *iface = i2c_get_adapdata(adap); + struct smu_i2c_cmd cmd; + int rc = 0; + int read = (read_write == I2C_SMBUS_READ); + + cmd.info.bus = iface->busid; + cmd.info.devaddr = (addr << 1) | (read ? 0x01 : 0x00); + + /* Prepare datas & select mode */ + switch (size) { + case I2C_SMBUS_QUICK: + cmd.info.type = SMU_I2C_TRANSFER_SIMPLE; + cmd.info.datalen = 0; + break; + case I2C_SMBUS_BYTE: + cmd.info.type = SMU_I2C_TRANSFER_SIMPLE; + cmd.info.datalen = 1; + if (!read) + cmd.info.data[0] = data->byte; + break; + case I2C_SMBUS_BYTE_DATA: + cmd.info.type = SMU_I2C_TRANSFER_STDSUB; + cmd.info.datalen = 1; + cmd.info.sublen = 1; + cmd.info.subaddr[0] = command; + cmd.info.subaddr[1] = 0; + cmd.info.subaddr[2] = 0; + if (!read) + cmd.info.data[0] = data->byte; + break; + case I2C_SMBUS_WORD_DATA: + cmd.info.type = SMU_I2C_TRANSFER_STDSUB; + cmd.info.datalen = 2; + cmd.info.sublen = 1; + cmd.info.subaddr[0] = command; + cmd.info.subaddr[1] = 0; + cmd.info.subaddr[2] = 0; + if (!read) { + cmd.info.data[0] = data->byte & 0xff; + cmd.info.data[1] = (data->byte >> 8) & 0xff; + } + break; + /* Note that these are broken vs. the expected smbus API where + * on reads, the lenght is actually returned from the function, + * but I think the current API makes no sense and I don't want + * any driver that I haven't verified for correctness to go + * anywhere near a pmac i2c bus anyway ... + */ + case I2C_SMBUS_BLOCK_DATA: + cmd.info.type = SMU_I2C_TRANSFER_STDSUB; + cmd.info.datalen = data->block[0] + 1; + if (cmd.info.datalen > 6) + return -EINVAL; + if (!read) + memcpy(cmd.info.data, data->block, cmd.info.datalen); + cmd.info.sublen = 1; + cmd.info.subaddr[0] = command; + cmd.info.subaddr[1] = 0; + cmd.info.subaddr[2] = 0; + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + cmd.info.type = SMU_I2C_TRANSFER_STDSUB; + cmd.info.datalen = data->block[0]; + if (cmd.info.datalen > 7) + return -EINVAL; + if (!read) + memcpy(cmd.info.data, &data->block[1], + cmd.info.datalen); + cmd.info.sublen = 1; + cmd.info.subaddr[0] = command; + cmd.info.subaddr[1] = 0; + cmd.info.subaddr[2] = 0; + break; + + default: + return -EINVAL; + } + + /* Turn a standardsub read into a combined mode access */ + if (read_write == I2C_SMBUS_READ && + cmd.info.type == SMU_I2C_TRANSFER_STDSUB) + cmd.info.type = SMU_I2C_TRANSFER_COMBINED; + + /* Finish filling command and submit it */ + cmd.done = smu_i2c_done; + cmd.misc = iface; + rc = smu_queue_i2c(&cmd); + if (rc < 0) + return rc; + wait_for_completion(&iface->complete); + rc = cmd.status; + + if (!read || rc < 0) + return rc; + + switch (size) { + case I2C_SMBUS_BYTE: + case I2C_SMBUS_BYTE_DATA: + data->byte = cmd.info.data[0]; + break; + case I2C_SMBUS_WORD_DATA: + data->word = ((u16)cmd.info.data[1]) << 8; + data->word |= cmd.info.data[0]; + break; + /* Note that these are broken vs. the expected smbus API where + * on reads, the lenght is actually returned from the function, + * but I think the current API makes no sense and I don't want + * any driver that I haven't verified for correctness to go + * anywhere near a pmac i2c bus anyway ... + */ + case I2C_SMBUS_BLOCK_DATA: + case I2C_SMBUS_I2C_BLOCK_DATA: + memcpy(&data->block[0], cmd.info.data, cmd.info.datalen); + break; + } + + return rc; +} + +static u32 +smu_smbus_func(struct i2c_adapter * adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA; +} + +/* For now, we only handle combined mode (smbus) */ +static struct i2c_algorithm smu_algorithm = { + .smbus_xfer = smu_smbus_xfer, + .functionality = smu_smbus_func, +}; + +static int create_iface(struct device_node *np, struct device *dev) +{ + struct smu_iface* iface; + u32 *reg, busid; + int rc; + + reg = (u32 *)get_property(np, "reg", NULL); + if (reg == NULL) { + printk(KERN_ERR "i2c-pmac-smu: can't find bus number !\n"); + return -ENXIO; + } + busid = *reg; + + iface = kmalloc(sizeof(struct smu_iface), GFP_KERNEL); + if (iface == NULL) { + printk(KERN_ERR "i2c-pmac-smu: can't allocate inteface !\n"); + return -ENOMEM; + } + memset(iface, 0, sizeof(struct smu_iface)); + init_completion(&iface->complete); + iface->busid = busid; + + dev_set_drvdata(dev, iface); + + sprintf(iface->adapter.name, "smu-i2c-%02x", busid); + iface->adapter.algo = &smu_algorithm; + iface->adapter.algo_data = NULL; + iface->adapter.client_register = NULL; + iface->adapter.client_unregister = NULL; + i2c_set_adapdata(&iface->adapter, iface); + iface->adapter.dev.parent = dev; + + rc = i2c_add_adapter(&iface->adapter); + if (rc) { + printk(KERN_ERR "i2c-pamc-smu.c: Adapter %s registration " + "failed\n", iface->adapter.name); + i2c_set_adapdata(&iface->adapter, NULL); + } + + if (probe) { + unsigned char addr; + printk("Probe: "); + for (addr = 0x00; addr <= 0x7f; addr++) { + if (i2c_smbus_xfer(&iface->adapter,addr, + 0,0,0,I2C_SMBUS_QUICK,NULL) >= 0) + printk("%02x ", addr); + } + printk("\n"); + } + + printk(KERN_INFO "SMU i2c bus %x registered\n", busid); + + return 0; +} + +static int dispose_iface(struct device *dev) +{ + struct smu_iface *iface = dev_get_drvdata(dev); + int rc; + + rc = i2c_del_adapter(&iface->adapter); + i2c_set_adapdata(&iface->adapter, NULL); + /* We aren't that prepared to deal with this... */ + if (rc) + printk("i2c-pmac-smu.c: Failed to remove bus %s !\n", + iface->adapter.name); + dev_set_drvdata(dev, NULL); + kfree(iface); + + return 0; +} + + +static int create_iface_of_platform(struct of_device* dev, + const struct of_device_id *match) +{ + return create_iface(dev->node, &dev->dev); +} + + +static int dispose_iface_of_platform(struct of_device* dev) +{ + return dispose_iface(&dev->dev); +} + + +static struct of_device_id i2c_smu_match[] = +{ + { + .compatible = "smu-i2c", + }, + {}, +}; +static struct of_platform_driver i2c_smu_of_platform_driver = +{ + .name = "i2c-smu", + .match_table = i2c_smu_match, + .probe = create_iface_of_platform, + .remove = dispose_iface_of_platform +}; + + +static int __init i2c_pmac_smu_init(void) +{ + of_register_driver(&i2c_smu_of_platform_driver); + return 0; +} + + +static void __exit i2c_pmac_smu_cleanup(void) +{ + of_unregister_driver(&i2c_smu_of_platform_driver); +} + +module_init(i2c_pmac_smu_init); +module_exit(i2c_pmac_smu_cleanup); diff --git a/drivers/infiniband/core/mad_rmpp.c b/drivers/infiniband/core/mad_rmpp.c index 2bd8b1cc57c4..e23836d0e21b 100644 --- a/drivers/infiniband/core/mad_rmpp.c +++ b/drivers/infiniband/core/mad_rmpp.c @@ -412,8 +412,8 @@ static inline int get_mad_len(struct mad_rmpp_recv *rmpp_recv) hdr_size = data_offset(rmpp_mad->mad_hdr.mgmt_class); data_size = sizeof(struct ib_rmpp_mad) - hdr_size; - pad = data_size - be32_to_cpu(rmpp_mad->rmpp_hdr.paylen_newwin); - if (pad > data_size || pad < 0) + pad = IB_MGMT_RMPP_DATA - be32_to_cpu(rmpp_mad->rmpp_hdr.paylen_newwin); + if (pad > IB_MGMT_RMPP_DATA || pad < 0) pad = 0; return hdr_size + rmpp_recv->seg_num * data_size - pad; @@ -583,6 +583,7 @@ static int send_next_seg(struct ib_mad_send_wr_private *mad_send_wr) { struct ib_rmpp_mad *rmpp_mad; int timeout; + u32 paylen; rmpp_mad = (struct ib_rmpp_mad *)mad_send_wr->send_wr.wr.ud.mad_hdr; ib_set_rmpp_flags(&rmpp_mad->rmpp_hdr, IB_MGMT_RMPP_FLAG_ACTIVE); @@ -590,11 +591,9 @@ static int send_next_seg(struct ib_mad_send_wr_private *mad_send_wr) if (mad_send_wr->seg_num == 1) { rmpp_mad->rmpp_hdr.rmpp_rtime_flags |= IB_MGMT_RMPP_FLAG_FIRST; - rmpp_mad->rmpp_hdr.paylen_newwin = - cpu_to_be32(mad_send_wr->total_seg * - (sizeof(struct ib_rmpp_mad) - - offsetof(struct ib_rmpp_mad, data)) - - mad_send_wr->pad); + paylen = mad_send_wr->total_seg * IB_MGMT_RMPP_DATA - + mad_send_wr->pad; + rmpp_mad->rmpp_hdr.paylen_newwin = cpu_to_be32(paylen); mad_send_wr->sg_list[0].length = sizeof(struct ib_rmpp_mad); } else { mad_send_wr->send_wr.num_sge = 2; @@ -608,10 +607,8 @@ static int send_next_seg(struct ib_mad_send_wr_private *mad_send_wr) if (mad_send_wr->seg_num == mad_send_wr->total_seg) { rmpp_mad->rmpp_hdr.rmpp_rtime_flags |= IB_MGMT_RMPP_FLAG_LAST; - rmpp_mad->rmpp_hdr.paylen_newwin = - cpu_to_be32(sizeof(struct ib_rmpp_mad) - - offsetof(struct ib_rmpp_mad, data) - - mad_send_wr->pad); + paylen = IB_MGMT_RMPP_DATA - mad_send_wr->pad; + rmpp_mad->rmpp_hdr.paylen_newwin = cpu_to_be32(paylen); } /* 2 seconds for an ACK until we can find the packet lifetime */ diff --git a/drivers/infiniband/core/user_mad.c b/drivers/infiniband/core/user_mad.c index 7c2f03057ddb..a64d6b4dcc16 100644 --- a/drivers/infiniband/core/user_mad.c +++ b/drivers/infiniband/core/user_mad.c @@ -334,10 +334,11 @@ static ssize_t ib_umad_write(struct file *filp, const char __user *buf, ret = -EINVAL; goto err_ah; } - /* Validate that management class can support RMPP */ + + /* Validate that the management class can support RMPP */ if (rmpp_mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_ADM) { hdr_len = offsetof(struct ib_sa_mad, data); - data_len = length; + data_len = length - hdr_len; } else if ((rmpp_mad->mad_hdr.mgmt_class >= IB_MGMT_CLASS_VENDOR_RANGE2_START) && (rmpp_mad->mad_hdr.mgmt_class <= IB_MGMT_CLASS_VENDOR_RANGE2_END)) { hdr_len = offsetof(struct ib_vendor_mad, data); diff --git a/drivers/infiniband/hw/mthca/mthca_eq.c b/drivers/infiniband/hw/mthca/mthca_eq.c index 18f0981eb0c1..78152a8ad17d 100644 --- a/drivers/infiniband/hw/mthca/mthca_eq.c +++ b/drivers/infiniband/hw/mthca/mthca_eq.c @@ -476,12 +476,8 @@ static int __devinit mthca_create_eq(struct mthca_dev *dev, int i; u8 status; - /* Make sure EQ size is aligned to a power of 2 size. */ - for (i = 1; i < nent; i <<= 1) - ; /* nothing */ - nent = i; - - eq->dev = dev; + eq->dev = dev; + eq->nent = roundup_pow_of_two(max(nent, 2)); eq->page_list = kmalloc(npages * sizeof *eq->page_list, GFP_KERNEL); @@ -512,7 +508,7 @@ static int __devinit mthca_create_eq(struct mthca_dev *dev, memset(eq->page_list[i].buf, 0, PAGE_SIZE); } - for (i = 0; i < nent; ++i) + for (i = 0; i < eq->nent; ++i) set_eqe_hw(get_eqe(eq, i)); eq->eqn = mthca_alloc(&dev->eq_table.alloc); @@ -528,8 +524,6 @@ static int __devinit mthca_create_eq(struct mthca_dev *dev, if (err) goto err_out_free_eq; - eq->nent = nent; - memset(eq_context, 0, sizeof *eq_context); eq_context->flags = cpu_to_be32(MTHCA_EQ_STATUS_OK | MTHCA_EQ_OWNER_HW | @@ -538,7 +532,7 @@ static int __devinit mthca_create_eq(struct mthca_dev *dev, if (mthca_is_memfree(dev)) eq_context->flags |= cpu_to_be32(MTHCA_EQ_STATE_ARBEL); - eq_context->logsize_usrpage = cpu_to_be32((ffs(nent) - 1) << 24); + eq_context->logsize_usrpage = cpu_to_be32((ffs(eq->nent) - 1) << 24); if (mthca_is_memfree(dev)) { eq_context->arbel_pd = cpu_to_be32(dev->driver_pd.pd_num); } else { @@ -569,7 +563,7 @@ static int __devinit mthca_create_eq(struct mthca_dev *dev, dev->eq_table.arm_mask |= eq->eqn_mask; mthca_dbg(dev, "Allocated EQ %d with %d entries\n", - eq->eqn, nent); + eq->eqn, eq->nent); return err; diff --git a/drivers/infiniband/hw/mthca/mthca_qp.c b/drivers/infiniband/hw/mthca/mthca_qp.c index bcef06bf15e7..5fa00669f9b8 100644 --- a/drivers/infiniband/hw/mthca/mthca_qp.c +++ b/drivers/infiniband/hw/mthca/mthca_qp.c @@ -227,7 +227,6 @@ static void mthca_wq_init(struct mthca_wq *wq) wq->last_comp = wq->max - 1; wq->head = 0; wq->tail = 0; - wq->last = NULL; } void mthca_qp_event(struct mthca_dev *dev, u32 qpn, @@ -687,7 +686,7 @@ int mthca_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask) } if (attr_mask & IB_QP_TIMEOUT) { - qp_context->pri_path.ackto = attr->timeout; + qp_context->pri_path.ackto = attr->timeout << 3; qp_param->opt_param_mask |= cpu_to_be32(MTHCA_QP_OPTPAR_ACK_TIMEOUT); } @@ -1103,6 +1102,9 @@ static int mthca_alloc_qp_common(struct mthca_dev *dev, } } + qp->sq.last = get_send_wqe(qp, qp->sq.max - 1); + qp->rq.last = get_recv_wqe(qp, qp->rq.max - 1); + return 0; } @@ -1583,15 +1585,13 @@ int mthca_tavor_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, goto out; } - if (prev_wqe) { - ((struct mthca_next_seg *) prev_wqe)->nda_op = - cpu_to_be32(((ind << qp->sq.wqe_shift) + - qp->send_wqe_offset) | - mthca_opcode[wr->opcode]); - wmb(); - ((struct mthca_next_seg *) prev_wqe)->ee_nds = - cpu_to_be32((size0 ? 0 : MTHCA_NEXT_DBD) | size); - } + ((struct mthca_next_seg *) prev_wqe)->nda_op = + cpu_to_be32(((ind << qp->sq.wqe_shift) + + qp->send_wqe_offset) | + mthca_opcode[wr->opcode]); + wmb(); + ((struct mthca_next_seg *) prev_wqe)->ee_nds = + cpu_to_be32((size0 ? 0 : MTHCA_NEXT_DBD) | size); if (!size0) { size0 = size; @@ -1688,13 +1688,11 @@ int mthca_tavor_post_receive(struct ib_qp *ibqp, struct ib_recv_wr *wr, qp->wrid[ind] = wr->wr_id; - if (likely(prev_wqe)) { - ((struct mthca_next_seg *) prev_wqe)->nda_op = - cpu_to_be32((ind << qp->rq.wqe_shift) | 1); - wmb(); - ((struct mthca_next_seg *) prev_wqe)->ee_nds = - cpu_to_be32(MTHCA_NEXT_DBD | size); - } + ((struct mthca_next_seg *) prev_wqe)->nda_op = + cpu_to_be32((ind << qp->rq.wqe_shift) | 1); + wmb(); + ((struct mthca_next_seg *) prev_wqe)->ee_nds = + cpu_to_be32(MTHCA_NEXT_DBD | size); if (!size0) size0 = size; @@ -1905,15 +1903,13 @@ int mthca_arbel_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, goto out; } - if (likely(prev_wqe)) { - ((struct mthca_next_seg *) prev_wqe)->nda_op = - cpu_to_be32(((ind << qp->sq.wqe_shift) + - qp->send_wqe_offset) | - mthca_opcode[wr->opcode]); - wmb(); - ((struct mthca_next_seg *) prev_wqe)->ee_nds = - cpu_to_be32(MTHCA_NEXT_DBD | size); - } + ((struct mthca_next_seg *) prev_wqe)->nda_op = + cpu_to_be32(((ind << qp->sq.wqe_shift) + + qp->send_wqe_offset) | + mthca_opcode[wr->opcode]); + wmb(); + ((struct mthca_next_seg *) prev_wqe)->ee_nds = + cpu_to_be32(MTHCA_NEXT_DBD | size); if (!size0) { size0 = size; @@ -2127,5 +2123,6 @@ void __devexit mthca_cleanup_qp_table(struct mthca_dev *dev) for (i = 0; i < 2; ++i) mthca_CONF_SPECIAL_QP(dev, i, 0, &status); + mthca_array_cleanup(&dev->qp_table.qp, dev->limits.num_qps); mthca_alloc_cleanup(&dev->qp_table.alloc); } diff --git a/drivers/infiniband/hw/mthca/mthca_srq.c b/drivers/infiniband/hw/mthca/mthca_srq.c index 75cd2d84ef12..18998d48c53e 100644 --- a/drivers/infiniband/hw/mthca/mthca_srq.c +++ b/drivers/infiniband/hw/mthca/mthca_srq.c @@ -172,6 +172,8 @@ static int mthca_alloc_srq_buf(struct mthca_dev *dev, struct mthca_pd *pd, scatter->lkey = cpu_to_be32(MTHCA_INVAL_LKEY); } + srq->last = get_wqe(srq, srq->max - 1); + return 0; } @@ -189,7 +191,6 @@ int mthca_alloc_srq(struct mthca_dev *dev, struct mthca_pd *pd, srq->max = attr->max_wr; srq->max_gs = attr->max_sge; - srq->last = NULL; srq->counter = 0; if (mthca_is_memfree(dev)) @@ -409,7 +410,7 @@ int mthca_tavor_post_srq_recv(struct ib_srq *ibsrq, struct ib_recv_wr *wr, mthca_err(dev, "SRQ %06x full\n", srq->srqn); err = -ENOMEM; *bad_wr = wr; - return nreq; + break; } wqe = get_wqe(srq, ind); @@ -427,7 +428,7 @@ int mthca_tavor_post_srq_recv(struct ib_srq *ibsrq, struct ib_recv_wr *wr, err = -EINVAL; *bad_wr = wr; srq->last = prev_wqe; - return nreq; + break; } for (i = 0; i < wr->num_sge; ++i) { @@ -446,20 +447,16 @@ int mthca_tavor_post_srq_recv(struct ib_srq *ibsrq, struct ib_recv_wr *wr, ((struct mthca_data_seg *) wqe)->addr = 0; } - if (likely(prev_wqe)) { - ((struct mthca_next_seg *) prev_wqe)->nda_op = - cpu_to_be32((ind << srq->wqe_shift) | 1); - wmb(); - ((struct mthca_next_seg *) prev_wqe)->ee_nds = - cpu_to_be32(MTHCA_NEXT_DBD); - } + ((struct mthca_next_seg *) prev_wqe)->nda_op = + cpu_to_be32((ind << srq->wqe_shift) | 1); + wmb(); + ((struct mthca_next_seg *) prev_wqe)->ee_nds = + cpu_to_be32(MTHCA_NEXT_DBD); srq->wrid[ind] = wr->wr_id; srq->first_free = next_ind; } - return nreq; - if (likely(nreq)) { __be32 doorbell[2]; @@ -503,7 +500,7 @@ int mthca_arbel_post_srq_recv(struct ib_srq *ibsrq, struct ib_recv_wr *wr, mthca_err(dev, "SRQ %06x full\n", srq->srqn); err = -ENOMEM; *bad_wr = wr; - return nreq; + break; } wqe = get_wqe(srq, ind); @@ -519,7 +516,7 @@ int mthca_arbel_post_srq_recv(struct ib_srq *ibsrq, struct ib_recv_wr *wr, if (unlikely(wr->num_sge > srq->max_gs)) { err = -EINVAL; *bad_wr = wr; - return nreq; + break; } for (i = 0; i < wr->num_sge; ++i) { diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h index bea960b8191f..4ea1c1ca85bc 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib.h +++ b/drivers/infiniband/ulp/ipoib/ipoib.h @@ -257,7 +257,7 @@ void ipoib_mcast_send(struct net_device *dev, union ib_gid *mgid, void ipoib_mcast_restart_task(void *dev_ptr); int ipoib_mcast_start_thread(struct net_device *dev); -int ipoib_mcast_stop_thread(struct net_device *dev); +int ipoib_mcast_stop_thread(struct net_device *dev, int flush); void ipoib_mcast_dev_down(struct net_device *dev); void ipoib_mcast_dev_flush(struct net_device *dev); diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ib.c b/drivers/infiniband/ulp/ipoib/ipoib_ib.c index ef0e3894863c..f7440096b5ed 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_ib.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_ib.c @@ -432,7 +432,7 @@ int ipoib_ib_dev_down(struct net_device *dev) flush_workqueue(ipoib_workqueue); } - ipoib_mcast_stop_thread(dev); + ipoib_mcast_stop_thread(dev, 1); /* * Flush the multicast groups first so we stop any multicast joins. The @@ -599,7 +599,7 @@ void ipoib_ib_dev_cleanup(struct net_device *dev) ipoib_dbg(priv, "cleaning up ib_dev\n"); - ipoib_mcast_stop_thread(dev); + ipoib_mcast_stop_thread(dev, 1); /* Delete the broadcast address and the local address */ ipoib_mcast_dev_down(dev); diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index 49d120d2b92c..704f48e0b6a7 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -1005,6 +1005,7 @@ debug_failed: register_failed: ib_unregister_event_handler(&priv->event_handler); + flush_scheduled_work(); event_failed: ipoib_dev_cleanup(priv->dev); @@ -1057,6 +1058,7 @@ static void ipoib_remove_one(struct ib_device *device) list_for_each_entry_safe(priv, tmp, dev_list, list) { ib_unregister_event_handler(&priv->event_handler); + flush_scheduled_work(); unregister_netdev(priv->dev); ipoib_dev_cleanup(priv->dev); diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c index aca7aea18a69..36ce29836bf2 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c @@ -145,7 +145,7 @@ static struct ipoib_mcast *ipoib_mcast_alloc(struct net_device *dev, mcast->dev = dev; mcast->created = jiffies; - mcast->backoff = HZ; + mcast->backoff = 1; mcast->logcount = 0; INIT_LIST_HEAD(&mcast->list); @@ -396,7 +396,7 @@ static void ipoib_mcast_join_complete(int status, IPOIB_GID_ARG(mcast->mcmember.mgid), status); if (!status && !ipoib_mcast_join_finish(mcast, mcmember)) { - mcast->backoff = HZ; + mcast->backoff = 1; down(&mcast_mutex); if (test_bit(IPOIB_MCAST_RUN, &priv->flags)) queue_work(ipoib_workqueue, &priv->mcast_task); @@ -496,7 +496,7 @@ static void ipoib_mcast_join(struct net_device *dev, struct ipoib_mcast *mcast, if (test_bit(IPOIB_MCAST_RUN, &priv->flags)) queue_delayed_work(ipoib_workqueue, &priv->mcast_task, - mcast->backoff); + mcast->backoff * HZ); up(&mcast_mutex); } else mcast->query_id = ret; @@ -598,7 +598,7 @@ int ipoib_mcast_start_thread(struct net_device *dev) return 0; } -int ipoib_mcast_stop_thread(struct net_device *dev) +int ipoib_mcast_stop_thread(struct net_device *dev, int flush) { struct ipoib_dev_priv *priv = netdev_priv(dev); struct ipoib_mcast *mcast; @@ -610,7 +610,8 @@ int ipoib_mcast_stop_thread(struct net_device *dev) cancel_delayed_work(&priv->mcast_task); up(&mcast_mutex); - flush_workqueue(ipoib_workqueue); + if (flush) + flush_workqueue(ipoib_workqueue); if (priv->broadcast && priv->broadcast->query) { ib_sa_cancel_query(priv->broadcast->query_id, priv->broadcast->query); @@ -832,7 +833,7 @@ void ipoib_mcast_restart_task(void *dev_ptr) ipoib_dbg_mcast(priv, "restarting multicast task\n"); - ipoib_mcast_stop_thread(dev); + ipoib_mcast_stop_thread(dev, 0); spin_lock_irqsave(&priv->lock, flags); diff --git a/drivers/input/input.c b/drivers/input/input.c index 88636a204525..14ae5583e198 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -308,6 +308,7 @@ static struct input_device_id *input_match_device(struct input_device_id *id, st MATCH_BIT(ledbit, LED_MAX); MATCH_BIT(sndbit, SND_MAX); MATCH_BIT(ffbit, FF_MAX); + MATCH_BIT(swbit, SW_MAX); return id; } diff --git a/drivers/isdn/hisax/st5481_b.c b/drivers/isdn/hisax/st5481_b.c index 0a2536d62402..657817a591fe 100644 --- a/drivers/isdn/hisax/st5481_b.c +++ b/drivers/isdn/hisax/st5481_b.c @@ -209,9 +209,7 @@ static void st5481B_mode(struct st5481_bcs *bcs, int mode) bcs->mode = mode; // Cancel all USB transfers on this B channel - b_out->urb[0]->transfer_flags |= URB_ASYNC_UNLINK; usb_unlink_urb(b_out->urb[0]); - b_out->urb[1]->transfer_flags |= URB_ASYNC_UNLINK; usb_unlink_urb(b_out->urb[1]); b_out->busy = 0; diff --git a/drivers/isdn/hisax/st5481_usb.c b/drivers/isdn/hisax/st5481_usb.c index ffd5b2d45552..89fbeb58485d 100644 --- a/drivers/isdn/hisax/st5481_usb.c +++ b/drivers/isdn/hisax/st5481_usb.c @@ -645,9 +645,7 @@ void st5481_in_mode(struct st5481_in *in, int mode) in->mode = mode; - in->urb[0]->transfer_flags |= URB_ASYNC_UNLINK; usb_unlink_urb(in->urb[0]); - in->urb[1]->transfer_flags |= URB_ASYNC_UNLINK; usb_unlink_urb(in->urb[1]); if (in->mode != L1_MODE_NULL) { diff --git a/drivers/macintosh/smu.c b/drivers/macintosh/smu.c index fb535737d17d..a85ac18dd21d 100644 --- a/drivers/macintosh/smu.c +++ b/drivers/macintosh/smu.c @@ -8,21 +8,15 @@ */ /* - * For now, this driver includes: - * - RTC get & set - * - reboot & shutdown commands - * all synchronous with IRQ disabled (ugh) - * * TODO: - * rework in a way the PMU driver works, that is asynchronous - * with a queue of commands. I'll do that as soon as I have an - * SMU based machine at hand. Some more cleanup is needed too, - * like maybe fitting it into a platform device, etc... - * Also check what's up with cache coherency, and if we really - * can't do better than flushing the cache, maybe build a table - * of command len/reply len like the PMU driver to only flush - * what is actually necessary. - * --BenH. + * - maybe add timeout to commands ? + * - blocking version of time functions + * - polling version of i2c commands (including timer that works with + * interrutps off) + * - maybe avoid some data copies with i2c by directly using the smu cmd + * buffer and a lower level internal interface + * - understand SMU -> CPU events and implement reception of them via + * the userland interface */ #include <linux/config.h> @@ -36,6 +30,11 @@ #include <linux/jiffies.h> #include <linux/interrupt.h> #include <linux/rtc.h> +#include <linux/completion.h> +#include <linux/miscdevice.h> +#include <linux/delay.h> +#include <linux/sysdev.h> +#include <linux/poll.h> #include <asm/byteorder.h> #include <asm/io.h> @@ -45,8 +44,13 @@ #include <asm/smu.h> #include <asm/sections.h> #include <asm/abs_addr.h> +#include <asm/uaccess.h> +#include <asm/of_device.h> + +#define VERSION "0.6" +#define AUTHOR "(c) 2005 Benjamin Herrenschmidt, IBM Corp." -#define DEBUG_SMU 1 +#undef DEBUG_SMU #ifdef DEBUG_SMU #define DPRINTK(fmt, args...) do { printk(KERN_DEBUG fmt , ##args); } while (0) @@ -57,20 +61,30 @@ /* * This is the command buffer passed to the SMU hardware */ +#define SMU_MAX_DATA 254 + struct smu_cmd_buf { u8 cmd; u8 length; - u8 data[0x0FFE]; + u8 data[SMU_MAX_DATA]; }; struct smu_device { spinlock_t lock; struct device_node *of_node; - int db_ack; /* doorbell ack GPIO */ - int db_req; /* doorbell req GPIO */ + struct of_device *of_dev; + int doorbell; /* doorbell gpio */ u32 __iomem *db_buf; /* doorbell buffer */ + int db_irq; + int msg; + int msg_irq; struct smu_cmd_buf *cmd_buf; /* command buffer virtual */ u32 cmd_buf_abs; /* command buffer absolute */ + struct list_head cmd_list; + struct smu_cmd *cmd_cur; /* pending command */ + struct list_head cmd_i2c_list; + struct smu_i2c_cmd *cmd_i2c_cur; /* pending i2c command */ + struct timer_list i2c_timer; }; /* @@ -79,113 +93,243 @@ struct smu_device { */ static struct smu_device *smu; + /* - * SMU low level communication stuff + * SMU driver low level stuff */ -static inline int smu_cmd_stat(struct smu_cmd_buf *cmd_buf, u8 cmd_ack) -{ - rmb(); - return cmd_buf->cmd == cmd_ack && cmd_buf->length != 0; -} -static inline u8 smu_save_ack_cmd(struct smu_cmd_buf *cmd_buf) +static void smu_start_cmd(void) { - return (~cmd_buf->cmd) & 0xff; -} + unsigned long faddr, fend; + struct smu_cmd *cmd; -static void smu_send_cmd(struct smu_device *dev) -{ - /* SMU command buf is currently cacheable, we need a physical - * address. This isn't exactly a DMA mapping here, I suspect + if (list_empty(&smu->cmd_list)) + return; + + /* Fetch first command in queue */ + cmd = list_entry(smu->cmd_list.next, struct smu_cmd, link); + smu->cmd_cur = cmd; + list_del(&cmd->link); + + DPRINTK("SMU: starting cmd %x, %d bytes data\n", cmd->cmd, + cmd->data_len); + DPRINTK("SMU: data buffer: %02x %02x %02x %02x ...\n", + ((u8 *)cmd->data_buf)[0], ((u8 *)cmd->data_buf)[1], + ((u8 *)cmd->data_buf)[2], ((u8 *)cmd->data_buf)[3]); + + /* Fill the SMU command buffer */ + smu->cmd_buf->cmd = cmd->cmd; + smu->cmd_buf->length = cmd->data_len; + memcpy(smu->cmd_buf->data, cmd->data_buf, cmd->data_len); + + /* Flush command and data to RAM */ + faddr = (unsigned long)smu->cmd_buf; + fend = faddr + smu->cmd_buf->length + 2; + flush_inval_dcache_range(faddr, fend); + + /* This isn't exactly a DMA mapping here, I suspect * the SMU is actually communicating with us via i2c to the * northbridge or the CPU to access RAM. */ - writel(dev->cmd_buf_abs, dev->db_buf); + writel(smu->cmd_buf_abs, smu->db_buf); /* Ring the SMU doorbell */ - pmac_do_feature_call(PMAC_FTR_WRITE_GPIO, NULL, dev->db_req, 4); - pmac_do_feature_call(PMAC_FTR_READ_GPIO, NULL, dev->db_req, 4); + pmac_do_feature_call(PMAC_FTR_WRITE_GPIO, NULL, smu->doorbell, 4); } -static int smu_cmd_done(struct smu_device *dev) + +static irqreturn_t smu_db_intr(int irq, void *arg, struct pt_regs *regs) { - unsigned long wait = 0; - int gpio; + unsigned long flags; + struct smu_cmd *cmd; + void (*done)(struct smu_cmd *cmd, void *misc) = NULL; + void *misc = NULL; + u8 gpio; + int rc = 0; - /* Check the SMU doorbell */ - do { - gpio = pmac_do_feature_call(PMAC_FTR_READ_GPIO, - NULL, dev->db_ack); - if ((gpio & 7) == 7) - return 0; - udelay(100); - } while(++wait < 10000); + /* SMU completed the command, well, we hope, let's make sure + * of it + */ + spin_lock_irqsave(&smu->lock, flags); - printk(KERN_ERR "SMU timeout !\n"); - return -ENXIO; + gpio = pmac_do_feature_call(PMAC_FTR_READ_GPIO, NULL, smu->doorbell); + if ((gpio & 7) != 7) + return IRQ_HANDLED; + + cmd = smu->cmd_cur; + smu->cmd_cur = NULL; + if (cmd == NULL) + goto bail; + + if (rc == 0) { + unsigned long faddr; + int reply_len; + u8 ack; + + /* CPU might have brought back the cache line, so we need + * to flush again before peeking at the SMU response. We + * flush the entire buffer for now as we haven't read the + * reply lenght (it's only 2 cache lines anyway) + */ + faddr = (unsigned long)smu->cmd_buf; + flush_inval_dcache_range(faddr, faddr + 256); + + /* Now check ack */ + ack = (~cmd->cmd) & 0xff; + if (ack != smu->cmd_buf->cmd) { + DPRINTK("SMU: incorrect ack, want %x got %x\n", + ack, smu->cmd_buf->cmd); + rc = -EIO; + } + reply_len = rc == 0 ? smu->cmd_buf->length : 0; + DPRINTK("SMU: reply len: %d\n", reply_len); + if (reply_len > cmd->reply_len) { + printk(KERN_WARNING "SMU: reply buffer too small," + "got %d bytes for a %d bytes buffer\n", + reply_len, cmd->reply_len); + reply_len = cmd->reply_len; + } + cmd->reply_len = reply_len; + if (cmd->reply_buf && reply_len) + memcpy(cmd->reply_buf, smu->cmd_buf->data, reply_len); + } + + /* Now complete the command. Write status last in order as we lost + * ownership of the command structure as soon as it's no longer -1 + */ + done = cmd->done; + misc = cmd->misc; + mb(); + cmd->status = rc; + bail: + /* Start next command if any */ + smu_start_cmd(); + spin_unlock_irqrestore(&smu->lock, flags); + + /* Call command completion handler if any */ + if (done) + done(cmd, misc); + + /* It's an edge interrupt, nothing to do */ + return IRQ_HANDLED; } -static int smu_do_cmd(struct smu_device *dev) + +static irqreturn_t smu_msg_intr(int irq, void *arg, struct pt_regs *regs) { - int rc; - u8 cmd_ack; + /* I don't quite know what to do with this one, we seem to never + * receive it, so I suspect we have to arm it someway in the SMU + * to start getting events that way. + */ + + printk(KERN_INFO "SMU: message interrupt !\n"); - DPRINTK("SMU do_cmd %02x len=%d %02x\n", - dev->cmd_buf->cmd, dev->cmd_buf->length, - dev->cmd_buf->data[0]); + /* It's an edge interrupt, nothing to do */ + return IRQ_HANDLED; +} - cmd_ack = smu_save_ack_cmd(dev->cmd_buf); - /* Clear cmd_buf cache lines */ - flush_inval_dcache_range((unsigned long)dev->cmd_buf, - ((unsigned long)dev->cmd_buf) + - sizeof(struct smu_cmd_buf)); - smu_send_cmd(dev); - rc = smu_cmd_done(dev); - if (rc == 0) - rc = smu_cmd_stat(dev->cmd_buf, cmd_ack) ? 0 : -1; +/* + * Queued command management. + * + */ - DPRINTK("SMU do_cmd %02x len=%d %02x => %d (%02x)\n", - dev->cmd_buf->cmd, dev->cmd_buf->length, - dev->cmd_buf->data[0], rc, cmd_ack); +int smu_queue_cmd(struct smu_cmd *cmd) +{ + unsigned long flags; - return rc; + if (smu == NULL) + return -ENODEV; + if (cmd->data_len > SMU_MAX_DATA || + cmd->reply_len > SMU_MAX_DATA) + return -EINVAL; + + cmd->status = 1; + spin_lock_irqsave(&smu->lock, flags); + list_add_tail(&cmd->link, &smu->cmd_list); + if (smu->cmd_cur == NULL) + smu_start_cmd(); + spin_unlock_irqrestore(&smu->lock, flags); + + return 0; } +EXPORT_SYMBOL(smu_queue_cmd); -/* RTC low level commands */ -static inline int bcd2hex (int n) + +int smu_queue_simple(struct smu_simple_cmd *scmd, u8 command, + unsigned int data_len, + void (*done)(struct smu_cmd *cmd, void *misc), + void *misc, ...) { - return (((n & 0xf0) >> 4) * 10) + (n & 0xf); + struct smu_cmd *cmd = &scmd->cmd; + va_list list; + int i; + + if (data_len > sizeof(scmd->buffer)) + return -EINVAL; + + memset(scmd, 0, sizeof(*scmd)); + cmd->cmd = command; + cmd->data_len = data_len; + cmd->data_buf = scmd->buffer; + cmd->reply_len = sizeof(scmd->buffer); + cmd->reply_buf = scmd->buffer; + cmd->done = done; + cmd->misc = misc; + + va_start(list, misc); + for (i = 0; i < data_len; ++i) + scmd->buffer[i] = (u8)va_arg(list, int); + va_end(list); + + return smu_queue_cmd(cmd); } +EXPORT_SYMBOL(smu_queue_simple); -static inline int hex2bcd (int n) + +void smu_poll(void) { - return ((n / 10) << 4) + (n % 10); + u8 gpio; + + if (smu == NULL) + return; + + gpio = pmac_do_feature_call(PMAC_FTR_READ_GPIO, NULL, smu->doorbell); + if ((gpio & 7) == 7) + smu_db_intr(smu->db_irq, smu, NULL); } +EXPORT_SYMBOL(smu_poll); + -#if 0 -static inline void smu_fill_set_pwrup_timer_cmd(struct smu_cmd_buf *cmd_buf) +void smu_done_complete(struct smu_cmd *cmd, void *misc) { - cmd_buf->cmd = 0x8e; - cmd_buf->length = 8; - cmd_buf->data[0] = 0x00; - memset(cmd_buf->data + 1, 0, 7); + struct completion *comp = misc; + + complete(comp); } +EXPORT_SYMBOL(smu_done_complete); + -static inline void smu_fill_get_pwrup_timer_cmd(struct smu_cmd_buf *cmd_buf) +void smu_spinwait_cmd(struct smu_cmd *cmd) { - cmd_buf->cmd = 0x8e; - cmd_buf->length = 1; - cmd_buf->data[0] = 0x01; + while(cmd->status == 1) + smu_poll(); +} +EXPORT_SYMBOL(smu_spinwait_cmd); + + +/* RTC low level commands */ +static inline int bcd2hex (int n) +{ + return (((n & 0xf0) >> 4) * 10) + (n & 0xf); } -static inline void smu_fill_dis_pwrup_timer_cmd(struct smu_cmd_buf *cmd_buf) + +static inline int hex2bcd (int n) { - cmd_buf->cmd = 0x8e; - cmd_buf->length = 1; - cmd_buf->data[0] = 0x02; + return ((n / 10) << 4) + (n % 10); } -#endif + static inline void smu_fill_set_rtc_cmd(struct smu_cmd_buf *cmd_buf, struct rtc_time *time) @@ -202,100 +346,96 @@ static inline void smu_fill_set_rtc_cmd(struct smu_cmd_buf *cmd_buf, cmd_buf->data[7] = hex2bcd(time->tm_year - 100); } -static inline void smu_fill_get_rtc_cmd(struct smu_cmd_buf *cmd_buf) -{ - cmd_buf->cmd = 0x8e; - cmd_buf->length = 1; - cmd_buf->data[0] = 0x81; -} -static void smu_parse_get_rtc_reply(struct smu_cmd_buf *cmd_buf, - struct rtc_time *time) +int smu_get_rtc_time(struct rtc_time *time, int spinwait) { - time->tm_sec = bcd2hex(cmd_buf->data[0]); - time->tm_min = bcd2hex(cmd_buf->data[1]); - time->tm_hour = bcd2hex(cmd_buf->data[2]); - time->tm_wday = bcd2hex(cmd_buf->data[3]); - time->tm_mday = bcd2hex(cmd_buf->data[4]); - time->tm_mon = bcd2hex(cmd_buf->data[5]) - 1; - time->tm_year = bcd2hex(cmd_buf->data[6]) + 100; -} - -int smu_get_rtc_time(struct rtc_time *time) -{ - unsigned long flags; + struct smu_simple_cmd cmd; int rc; if (smu == NULL) return -ENODEV; memset(time, 0, sizeof(struct rtc_time)); - spin_lock_irqsave(&smu->lock, flags); - smu_fill_get_rtc_cmd(smu->cmd_buf); - rc = smu_do_cmd(smu); - if (rc == 0) - smu_parse_get_rtc_reply(smu->cmd_buf, time); - spin_unlock_irqrestore(&smu->lock, flags); + rc = smu_queue_simple(&cmd, SMU_CMD_RTC_COMMAND, 1, NULL, NULL, + SMU_CMD_RTC_GET_DATETIME); + if (rc) + return rc; + smu_spinwait_simple(&cmd); - return rc; + time->tm_sec = bcd2hex(cmd.buffer[0]); + time->tm_min = bcd2hex(cmd.buffer[1]); + time->tm_hour = bcd2hex(cmd.buffer[2]); + time->tm_wday = bcd2hex(cmd.buffer[3]); + time->tm_mday = bcd2hex(cmd.buffer[4]); + time->tm_mon = bcd2hex(cmd.buffer[5]) - 1; + time->tm_year = bcd2hex(cmd.buffer[6]) + 100; + + return 0; } -int smu_set_rtc_time(struct rtc_time *time) + +int smu_set_rtc_time(struct rtc_time *time, int spinwait) { - unsigned long flags; + struct smu_simple_cmd cmd; int rc; if (smu == NULL) return -ENODEV; - spin_lock_irqsave(&smu->lock, flags); - smu_fill_set_rtc_cmd(smu->cmd_buf, time); - rc = smu_do_cmd(smu); - spin_unlock_irqrestore(&smu->lock, flags); + rc = smu_queue_simple(&cmd, SMU_CMD_RTC_COMMAND, 8, NULL, NULL, + SMU_CMD_RTC_SET_DATETIME, + hex2bcd(time->tm_sec), + hex2bcd(time->tm_min), + hex2bcd(time->tm_hour), + time->tm_wday, + hex2bcd(time->tm_mday), + hex2bcd(time->tm_mon) + 1, + hex2bcd(time->tm_year - 100)); + if (rc) + return rc; + smu_spinwait_simple(&cmd); - return rc; + return 0; } + void smu_shutdown(void) { - const unsigned char *command = "SHUTDOWN"; - unsigned long flags; + struct smu_simple_cmd cmd; if (smu == NULL) return; - spin_lock_irqsave(&smu->lock, flags); - smu->cmd_buf->cmd = 0xaa; - smu->cmd_buf->length = strlen(command); - strcpy(smu->cmd_buf->data, command); - smu_do_cmd(smu); + if (smu_queue_simple(&cmd, SMU_CMD_POWER_COMMAND, 9, NULL, NULL, + 'S', 'H', 'U', 'T', 'D', 'O', 'W', 'N', 0)) + return; + smu_spinwait_simple(&cmd); for (;;) ; - spin_unlock_irqrestore(&smu->lock, flags); } + void smu_restart(void) { - const unsigned char *command = "RESTART"; - unsigned long flags; + struct smu_simple_cmd cmd; if (smu == NULL) return; - spin_lock_irqsave(&smu->lock, flags); - smu->cmd_buf->cmd = 0xaa; - smu->cmd_buf->length = strlen(command); - strcpy(smu->cmd_buf->data, command); - smu_do_cmd(smu); + if (smu_queue_simple(&cmd, SMU_CMD_POWER_COMMAND, 8, NULL, NULL, + 'R', 'E', 'S', 'T', 'A', 'R', 'T', 0)) + return; + smu_spinwait_simple(&cmd); for (;;) ; - spin_unlock_irqrestore(&smu->lock, flags); } + int smu_present(void) { return smu != NULL; } +EXPORT_SYMBOL(smu_present); int smu_init (void) @@ -307,6 +447,8 @@ int smu_init (void) if (np == NULL) return -ENODEV; + printk(KERN_INFO "SMU driver %s %s\n", VERSION, AUTHOR); + if (smu_cmdbuf_abs == 0) { printk(KERN_ERR "SMU: Command buffer not allocated !\n"); return -EINVAL; @@ -318,7 +460,13 @@ int smu_init (void) memset(smu, 0, sizeof(*smu)); spin_lock_init(&smu->lock); + INIT_LIST_HEAD(&smu->cmd_list); + INIT_LIST_HEAD(&smu->cmd_i2c_list); smu->of_node = np; + smu->db_irq = NO_IRQ; + smu->msg_irq = NO_IRQ; + init_timer(&smu->i2c_timer); + /* smu_cmdbuf_abs is in the low 2G of RAM, can be converted to a * 32 bits value safely */ @@ -331,8 +479,8 @@ int smu_init (void) goto fail; } data = (u32 *)get_property(np, "reg", NULL); - of_node_put(np); if (data == NULL) { + of_node_put(np); printk(KERN_ERR "SMU: Can't find doorbell GPIO address !\n"); goto fail; } @@ -341,8 +489,31 @@ int smu_init (void) * and ack. GPIOs are at 0x50, best would be to find that out * in the device-tree though. */ - smu->db_req = 0x50 + *data; - smu->db_ack = 0x50 + *data; + smu->doorbell = *data; + if (smu->doorbell < 0x50) + smu->doorbell += 0x50; + if (np->n_intrs > 0) + smu->db_irq = np->intrs[0].line; + + of_node_put(np); + + /* Now look for the smu-interrupt GPIO */ + do { + np = of_find_node_by_name(NULL, "smu-interrupt"); + if (np == NULL) + break; + data = (u32 *)get_property(np, "reg", NULL); + if (data == NULL) { + of_node_put(np); + break; + } + smu->msg = *data; + if (smu->msg < 0x50) + smu->msg += 0x50; + if (np->n_intrs > 0) + smu->msg_irq = np->intrs[0].line; + of_node_put(np); + } while(0); /* Doorbell buffer is currently hard-coded, I didn't find a proper * device-tree entry giving the address. Best would probably to use @@ -362,3 +533,584 @@ int smu_init (void) return -ENXIO; } + + +static int smu_late_init(void) +{ + if (!smu) + return 0; + + /* + * Try to request the interrupts + */ + + if (smu->db_irq != NO_IRQ) { + if (request_irq(smu->db_irq, smu_db_intr, + SA_SHIRQ, "SMU doorbell", smu) < 0) { + printk(KERN_WARNING "SMU: can't " + "request interrupt %d\n", + smu->db_irq); + smu->db_irq = NO_IRQ; + } + } + + if (smu->msg_irq != NO_IRQ) { + if (request_irq(smu->msg_irq, smu_msg_intr, + SA_SHIRQ, "SMU message", smu) < 0) { + printk(KERN_WARNING "SMU: can't " + "request interrupt %d\n", + smu->msg_irq); + smu->msg_irq = NO_IRQ; + } + } + + return 0; +} +arch_initcall(smu_late_init); + +/* + * sysfs visibility + */ + +static void smu_expose_childs(void *unused) +{ + struct device_node *np; + + for (np = NULL; (np = of_get_next_child(smu->of_node, np)) != NULL;) { + if (device_is_compatible(np, "smu-i2c")) { + char name[32]; + u32 *reg = (u32 *)get_property(np, "reg", NULL); + + if (reg == NULL) + continue; + sprintf(name, "smu-i2c-%02x", *reg); + of_platform_device_create(np, name, &smu->of_dev->dev); + } + } + +} + +static DECLARE_WORK(smu_expose_childs_work, smu_expose_childs, NULL); + +static int smu_platform_probe(struct of_device* dev, + const struct of_device_id *match) +{ + if (!smu) + return -ENODEV; + smu->of_dev = dev; + + /* + * Ok, we are matched, now expose all i2c busses. We have to defer + * that unfortunately or it would deadlock inside the device model + */ + schedule_work(&smu_expose_childs_work); + + return 0; +} + +static struct of_device_id smu_platform_match[] = +{ + { + .type = "smu", + }, + {}, +}; + +static struct of_platform_driver smu_of_platform_driver = +{ + .name = "smu", + .match_table = smu_platform_match, + .probe = smu_platform_probe, +}; + +static int __init smu_init_sysfs(void) +{ + int rc; + + /* + * Due to sysfs bogosity, a sysdev is not a real device, so + * we should in fact create both if we want sysdev semantics + * for power management. + * For now, we don't power manage machines with an SMU chip, + * I'm a bit too far from figuring out how that works with those + * new chipsets, but that will come back and bite us + */ + rc = of_register_driver(&smu_of_platform_driver); + return 0; +} + +device_initcall(smu_init_sysfs); + +struct of_device *smu_get_ofdev(void) +{ + if (!smu) + return NULL; + return smu->of_dev; +} + +EXPORT_SYMBOL_GPL(smu_get_ofdev); + +/* + * i2c interface + */ + +static void smu_i2c_complete_command(struct smu_i2c_cmd *cmd, int fail) +{ + void (*done)(struct smu_i2c_cmd *cmd, void *misc) = cmd->done; + void *misc = cmd->misc; + unsigned long flags; + + /* Check for read case */ + if (!fail && cmd->read) { + if (cmd->pdata[0] < 1) + fail = 1; + else + memcpy(cmd->info.data, &cmd->pdata[1], + cmd->info.datalen); + } + + DPRINTK("SMU: completing, success: %d\n", !fail); + + /* Update status and mark no pending i2c command with lock + * held so nobody comes in while we dequeue an eventual + * pending next i2c command + */ + spin_lock_irqsave(&smu->lock, flags); + smu->cmd_i2c_cur = NULL; + wmb(); + cmd->status = fail ? -EIO : 0; + + /* Is there another i2c command waiting ? */ + if (!list_empty(&smu->cmd_i2c_list)) { + struct smu_i2c_cmd *newcmd; + + /* Fetch it, new current, remove from list */ + newcmd = list_entry(smu->cmd_i2c_list.next, + struct smu_i2c_cmd, link); + smu->cmd_i2c_cur = newcmd; + list_del(&cmd->link); + + /* Queue with low level smu */ + list_add_tail(&cmd->scmd.link, &smu->cmd_list); + if (smu->cmd_cur == NULL) + smu_start_cmd(); + } + spin_unlock_irqrestore(&smu->lock, flags); + + /* Call command completion handler if any */ + if (done) + done(cmd, misc); + +} + + +static void smu_i2c_retry(unsigned long data) +{ + struct smu_i2c_cmd *cmd = (struct smu_i2c_cmd *)data; + + DPRINTK("SMU: i2c failure, requeuing...\n"); + + /* requeue command simply by resetting reply_len */ + cmd->pdata[0] = 0xff; + cmd->scmd.reply_len = 0x10; + smu_queue_cmd(&cmd->scmd); +} + + +static void smu_i2c_low_completion(struct smu_cmd *scmd, void *misc) +{ + struct smu_i2c_cmd *cmd = misc; + int fail = 0; + + DPRINTK("SMU: i2c compl. stage=%d status=%x pdata[0]=%x rlen: %x\n", + cmd->stage, scmd->status, cmd->pdata[0], scmd->reply_len); + + /* Check for possible status */ + if (scmd->status < 0) + fail = 1; + else if (cmd->read) { + if (cmd->stage == 0) + fail = cmd->pdata[0] != 0; + else + fail = cmd->pdata[0] >= 0x80; + } else { + fail = cmd->pdata[0] != 0; + } + + /* Handle failures by requeuing command, after 5ms interval + */ + if (fail && --cmd->retries > 0) { + DPRINTK("SMU: i2c failure, starting timer...\n"); + smu->i2c_timer.function = smu_i2c_retry; + smu->i2c_timer.data = (unsigned long)cmd; + smu->i2c_timer.expires = jiffies + msecs_to_jiffies(5); + add_timer(&smu->i2c_timer); + return; + } + + /* If failure or stage 1, command is complete */ + if (fail || cmd->stage != 0) { + smu_i2c_complete_command(cmd, fail); + return; + } + + DPRINTK("SMU: going to stage 1\n"); + + /* Ok, initial command complete, now poll status */ + scmd->reply_buf = cmd->pdata; + scmd->reply_len = 0x10; + scmd->data_buf = cmd->pdata; + scmd->data_len = 1; + cmd->pdata[0] = 0; + cmd->stage = 1; + cmd->retries = 20; + smu_queue_cmd(scmd); +} + + +int smu_queue_i2c(struct smu_i2c_cmd *cmd) +{ + unsigned long flags; + + if (smu == NULL) + return -ENODEV; + + /* Fill most fields of scmd */ + cmd->scmd.cmd = SMU_CMD_I2C_COMMAND; + cmd->scmd.done = smu_i2c_low_completion; + cmd->scmd.misc = cmd; + cmd->scmd.reply_buf = cmd->pdata; + cmd->scmd.reply_len = 0x10; + cmd->scmd.data_buf = (u8 *)(char *)&cmd->info; + cmd->scmd.status = 1; + cmd->stage = 0; + cmd->pdata[0] = 0xff; + cmd->retries = 20; + cmd->status = 1; + + /* Check transfer type, sanitize some "info" fields + * based on transfer type and do more checking + */ + cmd->info.caddr = cmd->info.devaddr; + cmd->read = cmd->info.devaddr & 0x01; + switch(cmd->info.type) { + case SMU_I2C_TRANSFER_SIMPLE: + memset(&cmd->info.sublen, 0, 4); + break; + case SMU_I2C_TRANSFER_COMBINED: + cmd->info.devaddr &= 0xfe; + case SMU_I2C_TRANSFER_STDSUB: + if (cmd->info.sublen > 3) + return -EINVAL; + break; + default: + return -EINVAL; + } + + /* Finish setting up command based on transfer direction + */ + if (cmd->read) { + if (cmd->info.datalen > SMU_I2C_READ_MAX) + return -EINVAL; + memset(cmd->info.data, 0xff, cmd->info.datalen); + cmd->scmd.data_len = 9; + } else { + if (cmd->info.datalen > SMU_I2C_WRITE_MAX) + return -EINVAL; + cmd->scmd.data_len = 9 + cmd->info.datalen; + } + + DPRINTK("SMU: i2c enqueuing command\n"); + DPRINTK("SMU: %s, len=%d bus=%x addr=%x sub0=%x type=%x\n", + cmd->read ? "read" : "write", cmd->info.datalen, + cmd->info.bus, cmd->info.caddr, + cmd->info.subaddr[0], cmd->info.type); + + + /* Enqueue command in i2c list, and if empty, enqueue also in + * main command list + */ + spin_lock_irqsave(&smu->lock, flags); + if (smu->cmd_i2c_cur == NULL) { + smu->cmd_i2c_cur = cmd; + list_add_tail(&cmd->scmd.link, &smu->cmd_list); + if (smu->cmd_cur == NULL) + smu_start_cmd(); + } else + list_add_tail(&cmd->link, &smu->cmd_i2c_list); + spin_unlock_irqrestore(&smu->lock, flags); + + return 0; +} + + + +/* + * Userland driver interface + */ + + +static LIST_HEAD(smu_clist); +static DEFINE_SPINLOCK(smu_clist_lock); + +enum smu_file_mode { + smu_file_commands, + smu_file_events, + smu_file_closing +}; + +struct smu_private +{ + struct list_head list; + enum smu_file_mode mode; + int busy; + struct smu_cmd cmd; + spinlock_t lock; + wait_queue_head_t wait; + u8 buffer[SMU_MAX_DATA]; +}; + + +static int smu_open(struct inode *inode, struct file *file) +{ + struct smu_private *pp; + unsigned long flags; + + pp = kmalloc(sizeof(struct smu_private), GFP_KERNEL); + if (pp == 0) + return -ENOMEM; + memset(pp, 0, sizeof(struct smu_private)); + spin_lock_init(&pp->lock); + pp->mode = smu_file_commands; + init_waitqueue_head(&pp->wait); + + spin_lock_irqsave(&smu_clist_lock, flags); + list_add(&pp->list, &smu_clist); + spin_unlock_irqrestore(&smu_clist_lock, flags); + file->private_data = pp; + + return 0; +} + + +static void smu_user_cmd_done(struct smu_cmd *cmd, void *misc) +{ + struct smu_private *pp = misc; + + wake_up_all(&pp->wait); +} + + +static ssize_t smu_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct smu_private *pp = file->private_data; + unsigned long flags; + struct smu_user_cmd_hdr hdr; + int rc = 0; + + if (pp->busy) + return -EBUSY; + else if (copy_from_user(&hdr, buf, sizeof(hdr))) + return -EFAULT; + else if (hdr.cmdtype == SMU_CMDTYPE_WANTS_EVENTS) { + pp->mode = smu_file_events; + return 0; + } else if (hdr.cmdtype != SMU_CMDTYPE_SMU) + return -EINVAL; + else if (pp->mode != smu_file_commands) + return -EBADFD; + else if (hdr.data_len > SMU_MAX_DATA) + return -EINVAL; + + spin_lock_irqsave(&pp->lock, flags); + if (pp->busy) { + spin_unlock_irqrestore(&pp->lock, flags); + return -EBUSY; + } + pp->busy = 1; + pp->cmd.status = 1; + spin_unlock_irqrestore(&pp->lock, flags); + + if (copy_from_user(pp->buffer, buf + sizeof(hdr), hdr.data_len)) { + pp->busy = 0; + return -EFAULT; + } + + pp->cmd.cmd = hdr.cmd; + pp->cmd.data_len = hdr.data_len; + pp->cmd.reply_len = SMU_MAX_DATA; + pp->cmd.data_buf = pp->buffer; + pp->cmd.reply_buf = pp->buffer; + pp->cmd.done = smu_user_cmd_done; + pp->cmd.misc = pp; + rc = smu_queue_cmd(&pp->cmd); + if (rc < 0) + return rc; + return count; +} + + +static ssize_t smu_read_command(struct file *file, struct smu_private *pp, + char __user *buf, size_t count) +{ + DECLARE_WAITQUEUE(wait, current); + struct smu_user_reply_hdr hdr; + unsigned long flags; + int size, rc = 0; + + if (!pp->busy) + return 0; + if (count < sizeof(struct smu_user_reply_hdr)) + return -EOVERFLOW; + spin_lock_irqsave(&pp->lock, flags); + if (pp->cmd.status == 1) { + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + add_wait_queue(&pp->wait, &wait); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + rc = 0; + if (pp->cmd.status != 1) + break; + rc = -ERESTARTSYS; + if (signal_pending(current)) + break; + spin_unlock_irqrestore(&pp->lock, flags); + schedule(); + spin_lock_irqsave(&pp->lock, flags); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&pp->wait, &wait); + } + spin_unlock_irqrestore(&pp->lock, flags); + if (rc) + return rc; + if (pp->cmd.status != 0) + pp->cmd.reply_len = 0; + size = sizeof(hdr) + pp->cmd.reply_len; + if (count < size) + size = count; + rc = size; + hdr.status = pp->cmd.status; + hdr.reply_len = pp->cmd.reply_len; + if (copy_to_user(buf, &hdr, sizeof(hdr))) + return -EFAULT; + size -= sizeof(hdr); + if (size && copy_to_user(buf + sizeof(hdr), pp->buffer, size)) + return -EFAULT; + pp->busy = 0; + + return rc; +} + + +static ssize_t smu_read_events(struct file *file, struct smu_private *pp, + char __user *buf, size_t count) +{ + /* Not implemented */ + msleep_interruptible(1000); + return 0; +} + + +static ssize_t smu_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct smu_private *pp = file->private_data; + + if (pp->mode == smu_file_commands) + return smu_read_command(file, pp, buf, count); + if (pp->mode == smu_file_events) + return smu_read_events(file, pp, buf, count); + + return -EBADFD; +} + +static unsigned int smu_fpoll(struct file *file, poll_table *wait) +{ + struct smu_private *pp = file->private_data; + unsigned int mask = 0; + unsigned long flags; + + if (pp == 0) + return 0; + + if (pp->mode == smu_file_commands) { + poll_wait(file, &pp->wait, wait); + + spin_lock_irqsave(&pp->lock, flags); + if (pp->busy && pp->cmd.status != 1) + mask |= POLLIN; + spin_unlock_irqrestore(&pp->lock, flags); + } if (pp->mode == smu_file_events) { + /* Not yet implemented */ + } + return mask; +} + +static int smu_release(struct inode *inode, struct file *file) +{ + struct smu_private *pp = file->private_data; + unsigned long flags; + unsigned int busy; + + if (pp == 0) + return 0; + + file->private_data = NULL; + + /* Mark file as closing to avoid races with new request */ + spin_lock_irqsave(&pp->lock, flags); + pp->mode = smu_file_closing; + busy = pp->busy; + + /* Wait for any pending request to complete */ + if (busy && pp->cmd.status == 1) { + DECLARE_WAITQUEUE(wait, current); + + add_wait_queue(&pp->wait, &wait); + for (;;) { + set_current_state(TASK_UNINTERRUPTIBLE); + if (pp->cmd.status != 1) + break; + spin_lock_irqsave(&pp->lock, flags); + schedule(); + spin_unlock_irqrestore(&pp->lock, flags); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&pp->wait, &wait); + } + spin_unlock_irqrestore(&pp->lock, flags); + + spin_lock_irqsave(&smu_clist_lock, flags); + list_del(&pp->list); + spin_unlock_irqrestore(&smu_clist_lock, flags); + kfree(pp); + + return 0; +} + + +static struct file_operations smu_device_fops __pmacdata = { + .llseek = no_llseek, + .read = smu_read, + .write = smu_write, + .poll = smu_fpoll, + .open = smu_open, + .release = smu_release, +}; + +static struct miscdevice pmu_device __pmacdata = { + MISC_DYNAMIC_MINOR, "smu", &smu_device_fops +}; + +static int smu_device_init(void) +{ + if (!smu) + return -ENODEV; + if (misc_register(&pmu_device) < 0) + printk(KERN_ERR "via-pmu: cannot register misc device.\n"); + return 0; +} +device_initcall(smu_device_init); diff --git a/drivers/macintosh/therm_adt746x.c b/drivers/macintosh/therm_adt746x.c index c9ca1118e449..f38696622eb4 100644 --- a/drivers/macintosh/therm_adt746x.c +++ b/drivers/macintosh/therm_adt746x.c @@ -599,7 +599,7 @@ thermostat_init(void) sensor_location[2] = "?"; } - of_dev = of_platform_device_create(np, "temperatures"); + of_dev = of_platform_device_create(np, "temperatures", NULL); if (of_dev == NULL) { printk(KERN_ERR "Can't register temperatures device !\n"); diff --git a/drivers/macintosh/therm_pm72.c b/drivers/macintosh/therm_pm72.c index 703e31973314..cc507ceef153 100644 --- a/drivers/macintosh/therm_pm72.c +++ b/drivers/macintosh/therm_pm72.c @@ -2051,7 +2051,7 @@ static int __init therm_pm72_init(void) return -ENODEV; } } - of_dev = of_platform_device_create(np, "temperature"); + of_dev = of_platform_device_create(np, "temperature", NULL); if (of_dev == NULL) { printk(KERN_ERR "Can't register FCU platform device !\n"); return -ENODEV; diff --git a/drivers/macintosh/therm_windtunnel.c b/drivers/macintosh/therm_windtunnel.c index cbb72eb0426d..6aaa1df1a64e 100644 --- a/drivers/macintosh/therm_windtunnel.c +++ b/drivers/macintosh/therm_windtunnel.c @@ -504,7 +504,7 @@ g4fan_init( void ) } if( !(np=of_find_node_by_name(NULL, "fan")) ) return -ENODEV; - x.of_dev = of_platform_device_create( np, "temperature" ); + x.of_dev = of_platform_device_create(np, "temperature", NULL); of_node_put( np ); if( !x.of_dev ) { diff --git a/drivers/media/video/bttv-driver.c b/drivers/media/video/bttv-driver.c index a564321db2f0..c062a017491e 100644 --- a/drivers/media/video/bttv-driver.c +++ b/drivers/media/video/bttv-driver.c @@ -763,21 +763,21 @@ static void set_pll(struct bttv *btv) /* no PLL needed */ if (btv->pll.pll_current == 0) return; - vprintk(KERN_INFO "bttv%d: PLL can sleep, using XTAL (%d).\n", - btv->c.nr,btv->pll.pll_ifreq); + bttv_printk(KERN_INFO "bttv%d: PLL can sleep, using XTAL (%d).\n", + btv->c.nr,btv->pll.pll_ifreq); btwrite(0x00,BT848_TGCTRL); btwrite(0x00,BT848_PLL_XCI); btv->pll.pll_current = 0; return; } - vprintk(KERN_INFO "bttv%d: PLL: %d => %d ",btv->c.nr, - btv->pll.pll_ifreq, btv->pll.pll_ofreq); + bttv_printk(KERN_INFO "bttv%d: PLL: %d => %d ",btv->c.nr, + btv->pll.pll_ifreq, btv->pll.pll_ofreq); set_pll_freq(btv, btv->pll.pll_ifreq, btv->pll.pll_ofreq); for (i=0; i<10; i++) { /* Let other people run while the PLL stabilizes */ - vprintk("."); + bttv_printk("."); msleep(10); if (btread(BT848_DSTATUS) & BT848_DSTATUS_PLOCK) { @@ -785,12 +785,12 @@ static void set_pll(struct bttv *btv) } else { btwrite(0x08,BT848_TGCTRL); btv->pll.pll_current = btv->pll.pll_ofreq; - vprintk(" ok\n"); + bttv_printk(" ok\n"); return; } } btv->pll.pll_current = -1; - vprintk("failed\n"); + bttv_printk("failed\n"); return; } diff --git a/drivers/media/video/bttvp.h b/drivers/media/video/bttvp.h index 9b0b7ca035f8..7a312f79340a 100644 --- a/drivers/media/video/bttvp.h +++ b/drivers/media/video/bttvp.h @@ -221,7 +221,7 @@ extern void bttv_gpio_tracking(struct bttv *btv, char *comment); extern int init_bttv_i2c(struct bttv *btv); extern int fini_bttv_i2c(struct bttv *btv); -#define vprintk if (bttv_verbose) printk +#define bttv_printk if (bttv_verbose) printk #define dprintk if (bttv_debug >= 1) printk #define d2printk if (bttv_debug >= 2) printk diff --git a/drivers/message/fusion/Kconfig b/drivers/message/fusion/Kconfig index 33f209a39cb6..1883d22cffeb 100644 --- a/drivers/message/fusion/Kconfig +++ b/drivers/message/fusion/Kconfig @@ -35,6 +35,23 @@ config FUSION_FC LSIFC929X LSIFC929XL +config FUSION_SAS + tristate "Fusion MPT ScsiHost drivers for SAS" + depends on PCI && SCSI + select FUSION + select SCSI_SAS_ATTRS + ---help--- + SCSI HOST support for a SAS host adapters. + + List of supported controllers: + + LSISAS1064 + LSISAS1066 + LSISAS1068 + LSISAS1064E + LSISAS1066E + LSISAS1068E + config FUSION_MAX_SGE int "Maximum number of scatter gather entries (16 - 128)" depends on FUSION diff --git a/drivers/message/fusion/Makefile b/drivers/message/fusion/Makefile index 1d2f9db813c1..8a2e2657f4c2 100644 --- a/drivers/message/fusion/Makefile +++ b/drivers/message/fusion/Makefile @@ -34,5 +34,6 @@ obj-$(CONFIG_FUSION_SPI) += mptbase.o mptscsih.o mptspi.o obj-$(CONFIG_FUSION_FC) += mptbase.o mptscsih.o mptfc.o +obj-$(CONFIG_FUSION_SAS) += mptbase.o mptscsih.o mptsas.o obj-$(CONFIG_FUSION_CTL) += mptctl.o obj-$(CONFIG_FUSION_LAN) += mptlan.o diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c index f517d0692d5f..790a2932ded9 100644 --- a/drivers/message/fusion/mptbase.c +++ b/drivers/message/fusion/mptbase.c @@ -135,13 +135,12 @@ static void mpt_adapter_dispose(MPT_ADAPTER *ioc); static void MptDisplayIocCapabilities(MPT_ADAPTER *ioc); static int MakeIocReady(MPT_ADAPTER *ioc, int force, int sleepFlag); -//static u32 mpt_GetIocState(MPT_ADAPTER *ioc, int cooked); static int GetIocFacts(MPT_ADAPTER *ioc, int sleepFlag, int reason); static int GetPortFacts(MPT_ADAPTER *ioc, int portnum, int sleepFlag); static int SendIocInit(MPT_ADAPTER *ioc, int sleepFlag); static int SendPortEnable(MPT_ADAPTER *ioc, int portnum, int sleepFlag); static int mpt_do_upload(MPT_ADAPTER *ioc, int sleepFlag); -static int mpt_downloadboot(MPT_ADAPTER *ioc, int sleepFlag); +static int mpt_downloadboot(MPT_ADAPTER *ioc, MpiFwHeader_t *pFwHeader, int sleepFlag); static int mpt_diag_reset(MPT_ADAPTER *ioc, int ignore, int sleepFlag); static int KickStart(MPT_ADAPTER *ioc, int ignore, int sleepFlag); static int SendIocReset(MPT_ADAPTER *ioc, u8 reset_type, int sleepFlag); @@ -152,6 +151,7 @@ static int WaitForDoorbellReply(MPT_ADAPTER *ioc, int howlong, int sleepFlag); static int GetLanConfigPages(MPT_ADAPTER *ioc); static int GetFcPortPage0(MPT_ADAPTER *ioc, int portnum); static int GetIoUnitPage2(MPT_ADAPTER *ioc); +int mptbase_sas_persist_operation(MPT_ADAPTER *ioc, u8 persist_opcode); static int mpt_GetScsiPortSettings(MPT_ADAPTER *ioc, int portnum); static int mpt_readScsiDevicePageHeaders(MPT_ADAPTER *ioc, int portnum); static void mpt_read_ioc_pg_1(MPT_ADAPTER *ioc); @@ -159,6 +159,8 @@ static void mpt_read_ioc_pg_4(MPT_ADAPTER *ioc); static void mpt_timer_expired(unsigned long data); static int SendEventNotification(MPT_ADAPTER *ioc, u8 EvSwitch); static int SendEventAck(MPT_ADAPTER *ioc, EventNotificationReply_t *evnp); +static int mpt_host_page_access_control(MPT_ADAPTER *ioc, u8 access_control_value, int sleepFlag); +static int mpt_host_page_alloc(MPT_ADAPTER *ioc, pIOCInit_t ioc_init); #ifdef CONFIG_PROC_FS static int procmpt_summary_read(char *buf, char **start, off_t offset, @@ -175,6 +177,7 @@ static int ProcessEventNotification(MPT_ADAPTER *ioc, EventNotificationReply_t * static void mpt_sp_ioc_info(MPT_ADAPTER *ioc, u32 ioc_status, MPT_FRAME_HDR *mf); static void mpt_fc_log_info(MPT_ADAPTER *ioc, u32 log_info); static void mpt_sp_log_info(MPT_ADAPTER *ioc, u32 log_info); +static void mpt_sas_log_info(MPT_ADAPTER *ioc, u32 log_info); /* module entry point */ static int __init fusion_init (void); @@ -206,6 +209,144 @@ pci_enable_io_access(struct pci_dev *pdev) pci_write_config_word(pdev, PCI_COMMAND, command_reg); } +/* + * Process turbo (context) reply... + */ +static void +mpt_turbo_reply(MPT_ADAPTER *ioc, u32 pa) +{ + MPT_FRAME_HDR *mf = NULL; + MPT_FRAME_HDR *mr = NULL; + int req_idx = 0; + int cb_idx; + + dmfprintk((MYIOC_s_INFO_FMT "Got TURBO reply req_idx=%08x\n", + ioc->name, pa)); + + switch (pa >> MPI_CONTEXT_REPLY_TYPE_SHIFT) { + case MPI_CONTEXT_REPLY_TYPE_SCSI_INIT: + req_idx = pa & 0x0000FFFF; + cb_idx = (pa & 0x00FF0000) >> 16; + mf = MPT_INDEX_2_MFPTR(ioc, req_idx); + break; + case MPI_CONTEXT_REPLY_TYPE_LAN: + cb_idx = mpt_lan_index; + /* + * Blind set of mf to NULL here was fatal + * after lan_reply says "freeme" + * Fix sort of combined with an optimization here; + * added explicit check for case where lan_reply + * was just returning 1 and doing nothing else. + * For this case skip the callback, but set up + * proper mf value first here:-) + */ + if ((pa & 0x58000000) == 0x58000000) { + req_idx = pa & 0x0000FFFF; + mf = MPT_INDEX_2_MFPTR(ioc, req_idx); + mpt_free_msg_frame(ioc, mf); + mb(); + return; + break; + } + mr = (MPT_FRAME_HDR *) CAST_U32_TO_PTR(pa); + break; + case MPI_CONTEXT_REPLY_TYPE_SCSI_TARGET: + cb_idx = mpt_stm_index; + mr = (MPT_FRAME_HDR *) CAST_U32_TO_PTR(pa); + break; + default: + cb_idx = 0; + BUG(); + } + + /* Check for (valid) IO callback! */ + if (cb_idx < 1 || cb_idx >= MPT_MAX_PROTOCOL_DRIVERS || + MptCallbacks[cb_idx] == NULL) { + printk(MYIOC_s_WARN_FMT "%s: Invalid cb_idx (%d)!\n", + __FUNCTION__, ioc->name, cb_idx); + goto out; + } + + if (MptCallbacks[cb_idx](ioc, mf, mr)) + mpt_free_msg_frame(ioc, mf); + out: + mb(); +} + +static void +mpt_reply(MPT_ADAPTER *ioc, u32 pa) +{ + MPT_FRAME_HDR *mf; + MPT_FRAME_HDR *mr; + int req_idx; + int cb_idx; + int freeme; + + u32 reply_dma_low; + u16 ioc_stat; + + /* non-TURBO reply! Hmmm, something may be up... + * Newest turbo reply mechanism; get address + * via left shift 1 (get rid of MPI_ADDRESS_REPLY_A_BIT)! + */ + + /* Map DMA address of reply header to cpu address. + * pa is 32 bits - but the dma address may be 32 or 64 bits + * get offset based only only the low addresses + */ + + reply_dma_low = (pa <<= 1); + mr = (MPT_FRAME_HDR *)((u8 *)ioc->reply_frames + + (reply_dma_low - ioc->reply_frames_low_dma)); + + req_idx = le16_to_cpu(mr->u.frame.hwhdr.msgctxu.fld.req_idx); + cb_idx = mr->u.frame.hwhdr.msgctxu.fld.cb_idx; + mf = MPT_INDEX_2_MFPTR(ioc, req_idx); + + dmfprintk((MYIOC_s_INFO_FMT "Got non-TURBO reply=%p req_idx=%x cb_idx=%x Function=%x\n", + ioc->name, mr, req_idx, cb_idx, mr->u.hdr.Function)); + DBG_DUMP_REPLY_FRAME(mr) + + /* Check/log IOC log info + */ + ioc_stat = le16_to_cpu(mr->u.reply.IOCStatus); + if (ioc_stat & MPI_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE) { + u32 log_info = le32_to_cpu(mr->u.reply.IOCLogInfo); + if (ioc->bus_type == FC) + mpt_fc_log_info(ioc, log_info); + else if (ioc->bus_type == SCSI) + mpt_sp_log_info(ioc, log_info); + else if (ioc->bus_type == SAS) + mpt_sas_log_info(ioc, log_info); + } + if (ioc_stat & MPI_IOCSTATUS_MASK) { + if (ioc->bus_type == SCSI && + cb_idx != mpt_stm_index && + cb_idx != mpt_lan_index) + mpt_sp_ioc_info(ioc, (u32)ioc_stat, mf); + } + + + /* Check for (valid) IO callback! */ + if (cb_idx < 1 || cb_idx >= MPT_MAX_PROTOCOL_DRIVERS || + MptCallbacks[cb_idx] == NULL) { + printk(MYIOC_s_WARN_FMT "%s: Invalid cb_idx (%d)!\n", + __FUNCTION__, ioc->name, cb_idx); + freeme = 0; + goto out; + } + + freeme = MptCallbacks[cb_idx](ioc, mf, mr); + + out: + /* Flush (non-TURBO) reply with a WRITE! */ + CHIPREG_WRITE32(&ioc->chip->ReplyFifo, pa); + + if (freeme) + mpt_free_msg_frame(ioc, mf); + mb(); +} + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* * mpt_interrupt - MPT adapter (IOC) specific interrupt handler. @@ -227,164 +368,21 @@ pci_enable_io_access(struct pci_dev *pdev) static irqreturn_t mpt_interrupt(int irq, void *bus_id, struct pt_regs *r) { - MPT_ADAPTER *ioc; - MPT_FRAME_HDR *mf; - MPT_FRAME_HDR *mr; - u32 pa; - int req_idx; - int cb_idx; - int type; - int freeme; - - ioc = (MPT_ADAPTER *)bus_id; + MPT_ADAPTER *ioc = bus_id; + u32 pa; /* * Drain the reply FIFO! - * - * NOTES: I've seen up to 10 replies processed in this loop, so far... - * Update: I've seen up to 9182 replies processed in this loop! ?? - * Update: Limit ourselves to processing max of N replies - * (bottom of loop). */ while (1) { - - if ((pa = CHIPREG_READ32_dmasync(&ioc->chip->ReplyFifo)) == 0xFFFFFFFF) + pa = CHIPREG_READ32_dmasync(&ioc->chip->ReplyFifo); + if (pa == 0xFFFFFFFF) return IRQ_HANDLED; - - cb_idx = 0; - freeme = 0; - - /* - * Check for non-TURBO reply! - */ - if (pa & MPI_ADDRESS_REPLY_A_BIT) { - u32 reply_dma_low; - u16 ioc_stat; - - /* non-TURBO reply! Hmmm, something may be up... - * Newest turbo reply mechanism; get address - * via left shift 1 (get rid of MPI_ADDRESS_REPLY_A_BIT)! - */ - - /* Map DMA address of reply header to cpu address. - * pa is 32 bits - but the dma address may be 32 or 64 bits - * get offset based only only the low addresses - */ - reply_dma_low = (pa = (pa << 1)); - mr = (MPT_FRAME_HDR *)((u8 *)ioc->reply_frames + - (reply_dma_low - ioc->reply_frames_low_dma)); - - req_idx = le16_to_cpu(mr->u.frame.hwhdr.msgctxu.fld.req_idx); - cb_idx = mr->u.frame.hwhdr.msgctxu.fld.cb_idx; - mf = MPT_INDEX_2_MFPTR(ioc, req_idx); - - dmfprintk((MYIOC_s_INFO_FMT "Got non-TURBO reply=%p req_idx=%x cb_idx=%x Function=%x\n", - ioc->name, mr, req_idx, cb_idx, mr->u.hdr.Function)); - DBG_DUMP_REPLY_FRAME(mr) - - /* Check/log IOC log info - */ - ioc_stat = le16_to_cpu(mr->u.reply.IOCStatus); - if (ioc_stat & MPI_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE) { - u32 log_info = le32_to_cpu(mr->u.reply.IOCLogInfo); - if (ioc->bus_type == FC) - mpt_fc_log_info(ioc, log_info); - else if (ioc->bus_type == SCSI) - mpt_sp_log_info(ioc, log_info); - } - if (ioc_stat & MPI_IOCSTATUS_MASK) { - if (ioc->bus_type == SCSI) - mpt_sp_ioc_info(ioc, (u32)ioc_stat, mf); - } - } else { - /* - * Process turbo (context) reply... - */ - dmfprintk((MYIOC_s_INFO_FMT "Got TURBO reply req_idx=%08x\n", ioc->name, pa)); - type = (pa >> MPI_CONTEXT_REPLY_TYPE_SHIFT); - if (type == MPI_CONTEXT_REPLY_TYPE_SCSI_TARGET) { - cb_idx = mpt_stm_index; - mf = NULL; - mr = (MPT_FRAME_HDR *) CAST_U32_TO_PTR(pa); - } else if (type == MPI_CONTEXT_REPLY_TYPE_LAN) { - cb_idx = mpt_lan_index; - /* Blind set of mf to NULL here was fatal - * after lan_reply says "freeme" - * Fix sort of combined with an optimization here; - * added explicit check for case where lan_reply - * was just returning 1 and doing nothing else. - * For this case skip the callback, but set up - * proper mf value first here:-) - */ - if ((pa & 0x58000000) == 0x58000000) { - req_idx = pa & 0x0000FFFF; - mf = MPT_INDEX_2_MFPTR(ioc, req_idx); - freeme = 1; - /* - * IMPORTANT! Invalidate the callback! - */ - cb_idx = 0; - } else { - mf = NULL; - } - mr = (MPT_FRAME_HDR *) CAST_U32_TO_PTR(pa); - } else { - req_idx = pa & 0x0000FFFF; - cb_idx = (pa & 0x00FF0000) >> 16; - mf = MPT_INDEX_2_MFPTR(ioc, req_idx); - mr = NULL; - } - pa = 0; /* No reply flush! */ - } - -#ifdef MPT_DEBUG_IRQ - if (ioc->bus_type == SCSI) { - /* Verify mf, mr are reasonable. - */ - if ((mf) && ((mf >= MPT_INDEX_2_MFPTR(ioc, ioc->req_depth)) - || (mf < ioc->req_frames)) ) { - printk(MYIOC_s_WARN_FMT - "mpt_interrupt: Invalid mf (%p)!\n", ioc->name, (void *)mf); - cb_idx = 0; - pa = 0; - freeme = 0; - } - if ((pa) && (mr) && ((mr >= MPT_INDEX_2_RFPTR(ioc, ioc->req_depth)) - || (mr < ioc->reply_frames)) ) { - printk(MYIOC_s_WARN_FMT - "mpt_interrupt: Invalid rf (%p)!\n", ioc->name, (void *)mr); - cb_idx = 0; - pa = 0; - freeme = 0; - } - if (cb_idx > (MPT_MAX_PROTOCOL_DRIVERS-1)) { - printk(MYIOC_s_WARN_FMT - "mpt_interrupt: Invalid cb_idx (%d)!\n", ioc->name, cb_idx); - cb_idx = 0; - pa = 0; - freeme = 0; - } - } -#endif - - /* Check for (valid) IO callback! */ - if (cb_idx) { - /* Do the callback! */ - freeme = (*(MptCallbacks[cb_idx]))(ioc, mf, mr); - } - - if (pa) { - /* Flush (non-TURBO) reply with a WRITE! */ - CHIPREG_WRITE32(&ioc->chip->ReplyFifo, pa); - } - - if (freeme) { - /* Put Request back on FreeQ! */ - mpt_free_msg_frame(ioc, mf); - } - - mb(); - } /* drain reply FIFO */ + else if (pa & MPI_ADDRESS_REPLY_A_BIT) + mpt_reply(ioc, pa); + else + mpt_turbo_reply(ioc, pa); + } return IRQ_HANDLED; } @@ -509,6 +507,14 @@ mpt_base_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *reply) pCfg->wait_done = 1; wake_up(&mpt_waitq); } + } else if (func == MPI_FUNCTION_SAS_IO_UNIT_CONTROL) { + /* we should be always getting a reply frame */ + memcpy(ioc->persist_reply_frame, reply, + min(MPT_DEFAULT_FRAME_SIZE, + 4*reply->u.reply.MsgLength)); + del_timer(&ioc->persist_timer); + ioc->persist_wait_done = 1; + wake_up(&mpt_waitq); } else { printk(MYIOC_s_ERR_FMT "Unexpected msg function (=%02Xh) reply received!\n", ioc->name, func); @@ -750,6 +756,7 @@ mpt_get_msg_frame(int handle, MPT_ADAPTER *ioc) mf = list_entry(ioc->FreeQ.next, MPT_FRAME_HDR, u.frame.linkage.list); list_del(&mf->u.frame.linkage.list); + mf->u.frame.linkage.arg1 = 0; mf->u.frame.hwhdr.msgctxu.fld.cb_idx = handle; /* byte */ req_offset = (u8 *)mf - (u8 *)ioc->req_frames; /* u16! */ @@ -845,6 +852,7 @@ mpt_free_msg_frame(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf) /* Put Request back on FreeQ! */ spin_lock_irqsave(&ioc->FreeQlock, flags); + mf->u.frame.linkage.arg1 = 0xdeadbeaf; /* signature to know if this mf is freed */ list_add_tail(&mf->u.frame.linkage.list, &ioc->FreeQ); #ifdef MFCNT ioc->mfcnt--; @@ -971,12 +979,123 @@ mpt_send_handshake_request(int handle, MPT_ADAPTER *ioc, int reqBytes, u32 *req, /* Make sure there are no doorbells */ CHIPREG_WRITE32(&ioc->chip->IntStatus, 0); - + return r; } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /** + * mpt_host_page_access_control - provides mechanism for the host + * driver to control the IOC's Host Page Buffer access. + * @ioc: Pointer to MPT adapter structure + * @access_control_value: define bits below + * + * Access Control Value - bits[15:12] + * 0h Reserved + * 1h Enable Access { MPI_DB_HPBAC_ENABLE_ACCESS } + * 2h Disable Access { MPI_DB_HPBAC_DISABLE_ACCESS } + * 3h Free Buffer { MPI_DB_HPBAC_FREE_BUFFER } + * + * Returns 0 for success, non-zero for failure. + */ + +static int +mpt_host_page_access_control(MPT_ADAPTER *ioc, u8 access_control_value, int sleepFlag) +{ + int r = 0; + + /* return if in use */ + if (CHIPREG_READ32(&ioc->chip->Doorbell) + & MPI_DOORBELL_ACTIVE) + return -1; + + CHIPREG_WRITE32(&ioc->chip->IntStatus, 0); + + CHIPREG_WRITE32(&ioc->chip->Doorbell, + ((MPI_FUNCTION_HOST_PAGEBUF_ACCESS_CONTROL + <<MPI_DOORBELL_FUNCTION_SHIFT) | + (access_control_value<<12))); + + /* Wait for IOC to clear Doorbell Status bit */ + if ((r = WaitForDoorbellAck(ioc, 5, sleepFlag)) < 0) { + return -2; + }else + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_host_page_alloc - allocate system memory for the fw + * If we already allocated memory in past, then resend the same pointer. + * ioc@: Pointer to pointer to IOC adapter + * ioc_init@: Pointer to ioc init config page + * + * Returns 0 for success, non-zero for failure. + */ +static int +mpt_host_page_alloc(MPT_ADAPTER *ioc, pIOCInit_t ioc_init) +{ + char *psge; + int flags_length; + u32 host_page_buffer_sz=0; + + if(!ioc->HostPageBuffer) { + + host_page_buffer_sz = + le32_to_cpu(ioc->facts.HostPageBufferSGE.FlagsLength) & 0xFFFFFF; + + if(!host_page_buffer_sz) + return 0; /* fw doesn't need any host buffers */ + + /* spin till we get enough memory */ + while(host_page_buffer_sz > 0) { + + if((ioc->HostPageBuffer = pci_alloc_consistent( + ioc->pcidev, + host_page_buffer_sz, + &ioc->HostPageBuffer_dma)) != NULL) { + + dinitprintk((MYIOC_s_INFO_FMT + "host_page_buffer @ %p, dma @ %x, sz=%d bytes\n", + ioc->name, + ioc->HostPageBuffer, + ioc->HostPageBuffer_dma, + host_page_buffer_sz)); + ioc->alloc_total += host_page_buffer_sz; + ioc->HostPageBuffer_sz = host_page_buffer_sz; + break; + } + + host_page_buffer_sz -= (4*1024); + } + } + + if(!ioc->HostPageBuffer) { + printk(MYIOC_s_ERR_FMT + "Failed to alloc memory for host_page_buffer!\n", + ioc->name); + return -999; + } + + psge = (char *)&ioc_init->HostPageBufferSGE; + flags_length = MPI_SGE_FLAGS_SIMPLE_ELEMENT | + MPI_SGE_FLAGS_SYSTEM_ADDRESS | + MPI_SGE_FLAGS_32_BIT_ADDRESSING | + MPI_SGE_FLAGS_HOST_TO_IOC | + MPI_SGE_FLAGS_END_OF_BUFFER; + if (sizeof(dma_addr_t) == sizeof(u64)) { + flags_length |= MPI_SGE_FLAGS_64_BIT_ADDRESSING; + } + flags_length = flags_length << MPI_SGE_FLAGS_SHIFT; + flags_length |= ioc->HostPageBuffer_sz; + mpt_add_sge(psge, flags_length, ioc->HostPageBuffer_dma); + ioc->facts.HostPageBufferSGE = ioc_init->HostPageBufferSGE; + +return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** * mpt_verify_adapter - Given a unique IOC identifier, set pointer to * the associated MPT adapter structure. * @iocid: IOC unique identifier (integer) @@ -1084,7 +1203,7 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id) /* Initilize SCSI Config Data structure */ - memset(&ioc->spi_data, 0, sizeof(ScsiCfgData)); + memset(&ioc->spi_data, 0, sizeof(SpiCfgData)); /* Initialize the running configQ head. */ @@ -1213,6 +1332,33 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id) ioc->prod_name = "LSI53C1035"; ioc->bus_type = SCSI; } + else if (pdev->device == MPI_MANUFACTPAGE_DEVID_SAS1064) { + ioc->prod_name = "LSISAS1064"; + ioc->bus_type = SAS; + ioc->errata_flag_1064 = 1; + } + else if (pdev->device == MPI_MANUFACTPAGE_DEVID_SAS1066) { + ioc->prod_name = "LSISAS1066"; + ioc->bus_type = SAS; + ioc->errata_flag_1064 = 1; + } + else if (pdev->device == MPI_MANUFACTPAGE_DEVID_SAS1068) { + ioc->prod_name = "LSISAS1068"; + ioc->bus_type = SAS; + ioc->errata_flag_1064 = 1; + } + else if (pdev->device == MPI_MANUFACTPAGE_DEVID_SAS1064E) { + ioc->prod_name = "LSISAS1064E"; + ioc->bus_type = SAS; + } + else if (pdev->device == MPI_MANUFACTPAGE_DEVID_SAS1066E) { + ioc->prod_name = "LSISAS1066E"; + ioc->bus_type = SAS; + } + else if (pdev->device == MPI_MANUFACTPAGE_DEVID_SAS1068E) { + ioc->prod_name = "LSISAS1068E"; + ioc->bus_type = SAS; + } if (ioc->errata_flag_1064) pci_disable_io_access(pdev); @@ -1604,8 +1750,23 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag) */ if (ret == 0) { rc = mpt_do_upload(ioc, sleepFlag); - if (rc != 0) + if (rc == 0) { + if (ioc->alt_ioc && ioc->alt_ioc->cached_fw) { + /* + * Maintain only one pointer to FW memory + * so there will not be two attempt to + * downloadboot onboard dual function + * chips (mpt_adapter_disable, + * mpt_diag_reset) + */ + ioc->cached_fw = NULL; + ddlprintk((MYIOC_s_INFO_FMT ": mpt_upload: alt_%s has cached_fw=%p \n", + ioc->name, ioc->alt_ioc->name, ioc->alt_ioc->cached_fw)); + } + } else { printk(KERN_WARNING MYNAM ": firmware upload failure!\n"); + ret = -5; + } } } } @@ -1640,7 +1801,22 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag) * and we try GetLanConfigPages again... */ if ((ret == 0) && (reason == MPT_HOSTEVENT_IOC_BRINGUP)) { - if (ioc->bus_type == FC) { + if (ioc->bus_type == SAS) { + + /* clear persistency table */ + if(ioc->facts.IOCExceptions & + MPI_IOCFACTS_EXCEPT_PERSISTENT_TABLE_FULL) { + ret = mptbase_sas_persist_operation(ioc, + MPI_SAS_OP_CLEAR_NOT_PRESENT); + if(ret != 0) + return -1; + } + + /* Find IM volumes + */ + mpt_findImVolumes(ioc); + + } else if (ioc->bus_type == FC) { /* * Pre-fetch FC port WWN and stuff... * (FCPortPage0_t stuff) @@ -1783,7 +1959,7 @@ mpt_adapter_disable(MPT_ADAPTER *ioc) if (ioc->cached_fw != NULL) { ddlprintk((KERN_INFO MYNAM ": mpt_adapter_disable: Pushing FW onto adapter\n")); - if ((ret = mpt_downloadboot(ioc, NO_SLEEP)) < 0) { + if ((ret = mpt_downloadboot(ioc, (MpiFwHeader_t *)ioc->cached_fw, NO_SLEEP)) < 0) { printk(KERN_WARNING MYNAM ": firmware downloadboot failure (%d)!\n", ret); } @@ -1831,9 +2007,9 @@ mpt_adapter_disable(MPT_ADAPTER *ioc) } kfree(ioc->spi_data.nvram); - kfree(ioc->spi_data.pIocPg3); + kfree(ioc->raid_data.pIocPg3); ioc->spi_data.nvram = NULL; - ioc->spi_data.pIocPg3 = NULL; + ioc->raid_data.pIocPg3 = NULL; if (ioc->spi_data.pIocPg4 != NULL) { sz = ioc->spi_data.IocPg4Sz; @@ -1852,6 +2028,23 @@ mpt_adapter_disable(MPT_ADAPTER *ioc) kfree(ioc->ChainToChain); ioc->ChainToChain = NULL; + + if (ioc->HostPageBuffer != NULL) { + if((ret = mpt_host_page_access_control(ioc, + MPI_DB_HPBAC_FREE_BUFFER, NO_SLEEP)) != 0) { + printk(KERN_ERR MYNAM + ": %s: host page buffers free failed (%d)!\n", + __FUNCTION__, ret); + } + dexitprintk((KERN_INFO MYNAM ": %s HostPageBuffer free @ %p, sz=%d bytes\n", + ioc->name, ioc->HostPageBuffer, ioc->HostPageBuffer_sz)); + pci_free_consistent(ioc->pcidev, ioc->HostPageBuffer_sz, + ioc->HostPageBuffer, + ioc->HostPageBuffer_dma); + ioc->HostPageBuffer = NULL; + ioc->HostPageBuffer_sz = 0; + ioc->alloc_total -= ioc->HostPageBuffer_sz; + } } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ @@ -2034,7 +2227,7 @@ MakeIocReady(MPT_ADAPTER *ioc, int force, int sleepFlag) * Loop here waiting for IOC to come READY. */ ii = 0; - cntdn = ((sleepFlag == CAN_SLEEP) ? HZ : 1000) * 15; /* 15 seconds */ + cntdn = ((sleepFlag == CAN_SLEEP) ? HZ : 1000) * 5; /* 5 seconds */ while ((ioc_state = mpt_GetIocState(ioc, 1)) != MPI_IOC_STATE_READY) { if (ioc_state == MPI_IOC_STATE_OPERATIONAL) { @@ -2212,6 +2405,7 @@ GetIocFacts(MPT_ADAPTER *ioc, int sleepFlag, int reason) le32_to_cpu(facts->CurrentSenseBufferHighAddr); facts->CurReplyFrameSize = le16_to_cpu(facts->CurReplyFrameSize); + facts->IOCCapabilities = le32_to_cpu(facts->IOCCapabilities); /* * Handle NEW (!) IOCFactsReply fields in MPI-1.01.xx @@ -2383,13 +2577,25 @@ SendIocInit(MPT_ADAPTER *ioc, int sleepFlag) ddlprintk((MYIOC_s_INFO_FMT "upload_fw %d facts.Flags=%x\n", ioc->name, ioc->upload_fw, ioc->facts.Flags)); - if (ioc->bus_type == FC) + if(ioc->bus_type == SAS) + ioc_init.MaxDevices = ioc->facts.MaxDevices; + else if(ioc->bus_type == FC) ioc_init.MaxDevices = MPT_MAX_FC_DEVICES; else ioc_init.MaxDevices = MPT_MAX_SCSI_DEVICES; - ioc_init.MaxBuses = MPT_MAX_BUS; - + dinitprintk((MYIOC_s_INFO_FMT "facts.MsgVersion=%x\n", + ioc->name, ioc->facts.MsgVersion)); + if (ioc->facts.MsgVersion >= MPI_VERSION_01_05) { + // set MsgVersion and HeaderVersion host driver was built with + ioc_init.MsgVersion = cpu_to_le16(MPI_VERSION); + ioc_init.HeaderVersion = cpu_to_le16(MPI_HEADER_VERSION); + + if (ioc->facts.Flags & MPI_IOCFACTS_FLAGS_HOST_PAGE_BUFFER_PERSISTENT) { + ioc_init.HostPageBufferSGE = ioc->facts.HostPageBufferSGE; + } else if(mpt_host_page_alloc(ioc, &ioc_init)) + return -99; + } ioc_init.ReplyFrameSize = cpu_to_le16(ioc->reply_sz); /* in BYTES */ if (sizeof(dma_addr_t) == sizeof(u64)) { @@ -2403,17 +2609,21 @@ SendIocInit(MPT_ADAPTER *ioc, int sleepFlag) ioc_init.HostMfaHighAddr = cpu_to_le32(0); ioc_init.SenseBufferHighAddr = cpu_to_le32(0); } - + ioc->facts.CurrentHostMfaHighAddr = ioc_init.HostMfaHighAddr; ioc->facts.CurrentSenseBufferHighAddr = ioc_init.SenseBufferHighAddr; + ioc->facts.MaxDevices = ioc_init.MaxDevices; + ioc->facts.MaxBuses = ioc_init.MaxBuses; dhsprintk((MYIOC_s_INFO_FMT "Sending IOCInit (req @ %p)\n", ioc->name, &ioc_init)); r = mpt_handshake_req_reply_wait(ioc, sizeof(IOCInit_t), (u32*)&ioc_init, sizeof(MPIDefaultReply_t), (u16*)&init_reply, 10 /*seconds*/, sleepFlag); - if (r != 0) + if (r != 0) { + printk(MYIOC_s_ERR_FMT "Sending IOCInit failed(%d)!\n",ioc->name, r); return r; + } /* No need to byte swap the multibyte fields in the reply * since we don't even look at it's contents. @@ -2472,7 +2682,7 @@ SendPortEnable(MPT_ADAPTER *ioc, int portnum, int sleepFlag) { PortEnable_t port_enable; MPIDefaultReply_t reply_buf; - int ii; + int rc; int req_sz; int reply_sz; @@ -2494,22 +2704,15 @@ SendPortEnable(MPT_ADAPTER *ioc, int portnum, int sleepFlag) /* RAID FW may take a long time to enable */ - if (ioc->bus_type == FC) { - ii = mpt_handshake_req_reply_wait(ioc, req_sz, (u32*)&port_enable, - reply_sz, (u16*)&reply_buf, 65 /*seconds*/, sleepFlag); - } else { - ii = mpt_handshake_req_reply_wait(ioc, req_sz, (u32*)&port_enable, + if ( (ioc->facts.ProductID & MPI_FW_HEADER_PID_PROD_MASK) + > MPI_FW_HEADER_PID_PROD_TARGET_SCSI ) { + rc = mpt_handshake_req_reply_wait(ioc, req_sz, (u32*)&port_enable, reply_sz, (u16*)&reply_buf, 300 /*seconds*/, sleepFlag); + } else { + rc = mpt_handshake_req_reply_wait(ioc, req_sz, (u32*)&port_enable, + reply_sz, (u16*)&reply_buf, 30 /*seconds*/, sleepFlag); } - - if (ii != 0) - return ii; - - /* We do not even look at the reply, so we need not - * swap the multi-byte fields. - */ - - return 0; + return rc; } /* @@ -2666,9 +2869,8 @@ mpt_do_upload(MPT_ADAPTER *ioc, int sleepFlag) * <0 for fw upload failure. */ static int -mpt_downloadboot(MPT_ADAPTER *ioc, int sleepFlag) +mpt_downloadboot(MPT_ADAPTER *ioc, MpiFwHeader_t *pFwHeader, int sleepFlag) { - MpiFwHeader_t *pFwHeader; MpiExtImageHeader_t *pExtImage; u32 fwSize; u32 diag0val; @@ -2679,18 +2881,8 @@ mpt_downloadboot(MPT_ADAPTER *ioc, int sleepFlag) u32 load_addr; u32 ioc_state=0; - ddlprintk((MYIOC_s_INFO_FMT "downloadboot: fw size 0x%x, ioc FW Ptr %p\n", - ioc->name, ioc->facts.FWImageSize, ioc->cached_fw)); - - if ( ioc->facts.FWImageSize == 0 ) - return -1; - - if (ioc->cached_fw == NULL) - return -2; - - /* prevent a second downloadboot and memory free with alt_ioc */ - if (ioc->alt_ioc && ioc->alt_ioc->cached_fw) - ioc->alt_ioc->cached_fw = NULL; + ddlprintk((MYIOC_s_INFO_FMT "downloadboot: fw size 0x%x (%d), FW Ptr %p\n", + ioc->name, pFwHeader->ImageSize, pFwHeader->ImageSize, pFwHeader)); CHIPREG_WRITE32(&ioc->chip->WriteSequence, 0xFF); CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_1ST_KEY_VALUE); @@ -2718,16 +2910,17 @@ mpt_downloadboot(MPT_ADAPTER *ioc, int sleepFlag) ioc->name, count)); break; } - /* wait 1 sec */ + /* wait .1 sec */ if (sleepFlag == CAN_SLEEP) { - msleep_interruptible (1000); + msleep_interruptible (100); } else { - mdelay (1000); + mdelay (100); } } if ( count == 30 ) { - ddlprintk((MYIOC_s_INFO_FMT "downloadboot failed! Unable to RESET_ADAPTER diag0val=%x\n", + ddlprintk((MYIOC_s_INFO_FMT "downloadboot failed! " + "Unable to get MPI_DIAG_DRWE mode, diag0val=%x\n", ioc->name, diag0val)); return -3; } @@ -2742,7 +2935,6 @@ mpt_downloadboot(MPT_ADAPTER *ioc, int sleepFlag) /* Set the DiagRwEn and Disable ARM bits */ CHIPREG_WRITE32(&ioc->chip->Diagnostic, (MPI_DIAG_RW_ENABLE | MPI_DIAG_DISABLE_ARM)); - pFwHeader = (MpiFwHeader_t *) ioc->cached_fw; fwSize = (pFwHeader->ImageSize + 3)/4; ptrFw = (u32 *) pFwHeader; @@ -2792,19 +2984,38 @@ mpt_downloadboot(MPT_ADAPTER *ioc, int sleepFlag) /* Clear the internal flash bad bit - autoincrementing register, * so must do two writes. */ - CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwAddress, 0x3F000000); - diagRwData = CHIPREG_PIO_READ32(&ioc->pio_chip->DiagRwData); - diagRwData |= 0x4000000; - CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwAddress, 0x3F000000); - CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwData, diagRwData); + if (ioc->bus_type == SCSI) { + /* + * 1030 and 1035 H/W errata, workaround to access + * the ClearFlashBadSignatureBit + */ + CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwAddress, 0x3F000000); + diagRwData = CHIPREG_PIO_READ32(&ioc->pio_chip->DiagRwData); + diagRwData |= 0x40000000; + CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwAddress, 0x3F000000); + CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwData, diagRwData); + + } else /* if((ioc->bus_type == SAS) || (ioc->bus_type == FC)) */ { + diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic); + CHIPREG_WRITE32(&ioc->chip->Diagnostic, diag0val | + MPI_DIAG_CLEAR_FLASH_BAD_SIG); + + /* wait 1 msec */ + if (sleepFlag == CAN_SLEEP) { + msleep_interruptible (1); + } else { + mdelay (1); + } + } if (ioc->errata_flag_1064) pci_disable_io_access(ioc->pcidev); diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic); - ddlprintk((MYIOC_s_INFO_FMT "downloadboot diag0val=%x, turning off PREVENT_IOC_BOOT, DISABLE_ARM\n", + ddlprintk((MYIOC_s_INFO_FMT "downloadboot diag0val=%x, " + "turning off PREVENT_IOC_BOOT, DISABLE_ARM, RW_ENABLE\n", ioc->name, diag0val)); - diag0val &= ~(MPI_DIAG_PREVENT_IOC_BOOT | MPI_DIAG_DISABLE_ARM); + diag0val &= ~(MPI_DIAG_PREVENT_IOC_BOOT | MPI_DIAG_DISABLE_ARM | MPI_DIAG_RW_ENABLE); ddlprintk((MYIOC_s_INFO_FMT "downloadboot now diag0val=%x\n", ioc->name, diag0val)); CHIPREG_WRITE32(&ioc->chip->Diagnostic, diag0val); @@ -2812,10 +3023,23 @@ mpt_downloadboot(MPT_ADAPTER *ioc, int sleepFlag) /* Write 0xFF to reset the sequencer */ CHIPREG_WRITE32(&ioc->chip->WriteSequence, 0xFF); + if (ioc->bus_type == SAS) { + ioc_state = mpt_GetIocState(ioc, 0); + if ( (GetIocFacts(ioc, sleepFlag, + MPT_HOSTEVENT_IOC_BRINGUP)) != 0 ) { + ddlprintk((MYIOC_s_INFO_FMT "GetIocFacts failed: IocState=%x\n", + ioc->name, ioc_state)); + return -EFAULT; + } + } + for (count=0; count<HZ*20; count++) { if ((ioc_state = mpt_GetIocState(ioc, 0)) & MPI_IOC_STATE_READY) { ddlprintk((MYIOC_s_INFO_FMT "downloadboot successful! (count=%d) IocState=%x\n", ioc->name, count, ioc_state)); + if (ioc->bus_type == SAS) { + return 0; + } if ((SendIocInit(ioc, sleepFlag)) != 0) { ddlprintk((MYIOC_s_INFO_FMT "downloadboot: SendIocInit failed\n", ioc->name)); @@ -3049,12 +3273,13 @@ mpt_diag_reset(MPT_ADAPTER *ioc, int ignore, int sleepFlag) /* wait 1 sec */ if (sleepFlag == CAN_SLEEP) { - ssleep(1); + msleep_interruptible (1000); } else { mdelay (1000); } } - if ((count = mpt_downloadboot(ioc, sleepFlag)) < 0) { + if ((count = mpt_downloadboot(ioc, + (MpiFwHeader_t *)ioc->cached_fw, sleepFlag)) < 0) { printk(KERN_WARNING MYNAM ": firmware downloadboot failure (%d)!\n", count); } @@ -3637,7 +3862,7 @@ WaitForDoorbellAck(MPT_ADAPTER *ioc, int howlong, int sleepFlag) int count = 0; u32 intstat=0; - cntdn = ((sleepFlag == CAN_SLEEP) ? HZ : 1000) * howlong; + cntdn = 1000 * howlong; if (sleepFlag == CAN_SLEEP) { while (--cntdn) { @@ -3687,7 +3912,7 @@ WaitForDoorbellInt(MPT_ADAPTER *ioc, int howlong, int sleepFlag) int count = 0; u32 intstat=0; - cntdn = ((sleepFlag == CAN_SLEEP) ? HZ : 1000) * howlong; + cntdn = 1000 * howlong; if (sleepFlag == CAN_SLEEP) { while (--cntdn) { intstat = CHIPREG_READ32(&ioc->chip->IntStatus); @@ -4001,6 +4226,85 @@ GetFcPortPage0(MPT_ADAPTER *ioc, int portnum) /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* + * mptbase_sas_persist_operation - Perform operation on SAS Persitent Table + * @ioc: Pointer to MPT_ADAPTER structure + * @sas_address: 64bit SAS Address for operation. + * @target_id: specified target for operation + * @bus: specified bus for operation + * @persist_opcode: see below + * + * MPI_SAS_OP_CLEAR_NOT_PRESENT - Free all persist TargetID mappings for + * devices not currently present. + * MPI_SAS_OP_CLEAR_ALL_PERSISTENT - Clear al persist TargetID mappings + * + * NOTE: Don't use not this function during interrupt time. + * + * Returns: 0 for success, non-zero error + */ + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +int +mptbase_sas_persist_operation(MPT_ADAPTER *ioc, u8 persist_opcode) +{ + SasIoUnitControlRequest_t *sasIoUnitCntrReq; + SasIoUnitControlReply_t *sasIoUnitCntrReply; + MPT_FRAME_HDR *mf = NULL; + MPIHeader_t *mpi_hdr; + + + /* insure garbage is not sent to fw */ + switch(persist_opcode) { + + case MPI_SAS_OP_CLEAR_NOT_PRESENT: + case MPI_SAS_OP_CLEAR_ALL_PERSISTENT: + break; + + default: + return -1; + break; + } + + printk("%s: persist_opcode=%x\n",__FUNCTION__, persist_opcode); + + /* Get a MF for this command. + */ + if ((mf = mpt_get_msg_frame(mpt_base_index, ioc)) == NULL) { + printk("%s: no msg frames!\n",__FUNCTION__); + return -1; + } + + mpi_hdr = (MPIHeader_t *) mf; + sasIoUnitCntrReq = (SasIoUnitControlRequest_t *)mf; + memset(sasIoUnitCntrReq,0,sizeof(SasIoUnitControlRequest_t)); + sasIoUnitCntrReq->Function = MPI_FUNCTION_SAS_IO_UNIT_CONTROL; + sasIoUnitCntrReq->MsgContext = mpi_hdr->MsgContext; + sasIoUnitCntrReq->Operation = persist_opcode; + + init_timer(&ioc->persist_timer); + ioc->persist_timer.data = (unsigned long) ioc; + ioc->persist_timer.function = mpt_timer_expired; + ioc->persist_timer.expires = jiffies + HZ*10 /* 10 sec */; + ioc->persist_wait_done=0; + add_timer(&ioc->persist_timer); + mpt_put_msg_frame(mpt_base_index, ioc, mf); + wait_event(mpt_waitq, ioc->persist_wait_done); + + sasIoUnitCntrReply = + (SasIoUnitControlReply_t *)ioc->persist_reply_frame; + if (le16_to_cpu(sasIoUnitCntrReply->IOCStatus) != MPI_IOCSTATUS_SUCCESS) { + printk("%s: IOCStatus=0x%X IOCLogInfo=0x%X\n", + __FUNCTION__, + sasIoUnitCntrReply->IOCStatus, + sasIoUnitCntrReply->IOCLogInfo); + return -1; + } + + printk("%s: success\n",__FUNCTION__); + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* * GetIoUnitPage2 - Retrieve BIOS version and boot order information. * @ioc: Pointer to MPT_ADAPTER structure * @@ -4340,10 +4644,10 @@ mpt_findImVolumes(MPT_ADAPTER *ioc) if (mpt_config(ioc, &cfg) != 0) goto done_and_free; - if ( (mem = (u8 *)ioc->spi_data.pIocPg2) == NULL ) { + if ( (mem = (u8 *)ioc->raid_data.pIocPg2) == NULL ) { mem = kmalloc(iocpage2sz, GFP_ATOMIC); if (mem) { - ioc->spi_data.pIocPg2 = (IOCPage2_t *) mem; + ioc->raid_data.pIocPg2 = (IOCPage2_t *) mem; } else { goto done_and_free; } @@ -4360,7 +4664,7 @@ mpt_findImVolumes(MPT_ADAPTER *ioc) /* At least 1 RAID Volume */ pIocRv = pIoc2->RaidVolume; - ioc->spi_data.isRaid = 0; + ioc->raid_data.isRaid = 0; for (jj = 0; jj < nVols; jj++, pIocRv++) { vid = pIocRv->VolumeID; vbus = pIocRv->VolumeBus; @@ -4369,7 +4673,7 @@ mpt_findImVolumes(MPT_ADAPTER *ioc) /* find the match */ if (vbus == 0) { - ioc->spi_data.isRaid |= (1 << vid); + ioc->raid_data.isRaid |= (1 << vid); } else { /* Error! Always bus 0 */ @@ -4404,8 +4708,8 @@ mpt_read_ioc_pg_3(MPT_ADAPTER *ioc) /* Free the old page */ - kfree(ioc->spi_data.pIocPg3); - ioc->spi_data.pIocPg3 = NULL; + kfree(ioc->raid_data.pIocPg3); + ioc->raid_data.pIocPg3 = NULL; /* There is at least one physical disk. * Read and save IOC Page 3 @@ -4442,7 +4746,7 @@ mpt_read_ioc_pg_3(MPT_ADAPTER *ioc) mem = kmalloc(iocpage3sz, GFP_ATOMIC); if (mem) { memcpy(mem, (u8 *)pIoc3, iocpage3sz); - ioc->spi_data.pIocPg3 = (IOCPage3_t *) mem; + ioc->raid_data.pIocPg3 = (IOCPage3_t *) mem; } } @@ -5366,8 +5670,8 @@ mpt_HardResetHandler(MPT_ADAPTER *ioc, int sleepFlag) } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -static char * -EventDescriptionStr(u8 event, u32 evData0) +static void +EventDescriptionStr(u8 event, u32 evData0, char *evStr) { char *ds; @@ -5420,8 +5724,95 @@ EventDescriptionStr(u8 event, u32 evData0) ds = "Events(OFF) Change"; break; case MPI_EVENT_INTEGRATED_RAID: - ds = "Integrated Raid"; + { + u8 ReasonCode = (u8)(evData0 >> 16); + switch (ReasonCode) { + case MPI_EVENT_RAID_RC_VOLUME_CREATED : + ds = "Integrated Raid: Volume Created"; + break; + case MPI_EVENT_RAID_RC_VOLUME_DELETED : + ds = "Integrated Raid: Volume Deleted"; + break; + case MPI_EVENT_RAID_RC_VOLUME_SETTINGS_CHANGED : + ds = "Integrated Raid: Volume Settings Changed"; + break; + case MPI_EVENT_RAID_RC_VOLUME_STATUS_CHANGED : + ds = "Integrated Raid: Volume Status Changed"; + break; + case MPI_EVENT_RAID_RC_VOLUME_PHYSDISK_CHANGED : + ds = "Integrated Raid: Volume Physdisk Changed"; + break; + case MPI_EVENT_RAID_RC_PHYSDISK_CREATED : + ds = "Integrated Raid: Physdisk Created"; + break; + case MPI_EVENT_RAID_RC_PHYSDISK_DELETED : + ds = "Integrated Raid: Physdisk Deleted"; + break; + case MPI_EVENT_RAID_RC_PHYSDISK_SETTINGS_CHANGED : + ds = "Integrated Raid: Physdisk Settings Changed"; + break; + case MPI_EVENT_RAID_RC_PHYSDISK_STATUS_CHANGED : + ds = "Integrated Raid: Physdisk Status Changed"; + break; + case MPI_EVENT_RAID_RC_DOMAIN_VAL_NEEDED : + ds = "Integrated Raid: Domain Validation Needed"; + break; + case MPI_EVENT_RAID_RC_SMART_DATA : + ds = "Integrated Raid; Smart Data"; + break; + case MPI_EVENT_RAID_RC_REPLACE_ACTION_STARTED : + ds = "Integrated Raid: Replace Action Started"; + break; + default: + ds = "Integrated Raid"; + break; + } + break; + } + case MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE: + ds = "SCSI Device Status Change"; + break; + case MPI_EVENT_SAS_DEVICE_STATUS_CHANGE: + { + u8 ReasonCode = (u8)(evData0 >> 16); + switch (ReasonCode) { + case MPI_EVENT_SAS_DEV_STAT_RC_ADDED: + ds = "SAS Device Status Change: Added"; + break; + case MPI_EVENT_SAS_DEV_STAT_RC_NOT_RESPONDING: + ds = "SAS Device Status Change: Deleted"; + break; + case MPI_EVENT_SAS_DEV_STAT_RC_SMART_DATA: + ds = "SAS Device Status Change: SMART Data"; + break; + case MPI_EVENT_SAS_DEV_STAT_RC_NO_PERSIST_ADDED: + ds = "SAS Device Status Change: No Persistancy Added"; + break; + default: + ds = "SAS Device Status Change: Unknown"; + break; + } + break; + } + case MPI_EVENT_ON_BUS_TIMER_EXPIRED: + ds = "Bus Timer Expired"; + break; + case MPI_EVENT_QUEUE_FULL: + ds = "Queue Full"; + break; + case MPI_EVENT_SAS_SES: + ds = "SAS SES Event"; + break; + case MPI_EVENT_PERSISTENT_TABLE_FULL: + ds = "Persistent Table Full"; + break; + case MPI_EVENT_SAS_PHY_LINK_STATUS: + ds = "SAS PHY Link Status"; + break; + case MPI_EVENT_SAS_DISCOVERY_ERROR: + ds = "SAS Discovery Error"; break; + /* * MPT base "custom" events may be added here... */ @@ -5429,7 +5820,7 @@ EventDescriptionStr(u8 event, u32 evData0) ds = "Unknown"; break; } - return ds; + strcpy(evStr,ds); } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ @@ -5451,7 +5842,7 @@ ProcessEventNotification(MPT_ADAPTER *ioc, EventNotificationReply_t *pEventReply int ii; int r = 0; int handlers = 0; - char *evStr; + char evStr[100]; u8 event; /* @@ -5464,7 +5855,7 @@ ProcessEventNotification(MPT_ADAPTER *ioc, EventNotificationReply_t *pEventReply evData0 = le32_to_cpu(pEventReply->Data[0]); } - evStr = EventDescriptionStr(event, evData0); + EventDescriptionStr(event, evData0, evStr); devtprintk((MYIOC_s_INFO_FMT "MPT event (%s=%02Xh) detected!\n", ioc->name, evStr, @@ -5481,20 +5872,6 @@ ProcessEventNotification(MPT_ADAPTER *ioc, EventNotificationReply_t *pEventReply * Do general / base driver event processing */ switch(event) { - case MPI_EVENT_NONE: /* 00 */ - case MPI_EVENT_LOG_DATA: /* 01 */ - case MPI_EVENT_STATE_CHANGE: /* 02 */ - case MPI_EVENT_UNIT_ATTENTION: /* 03 */ - case MPI_EVENT_IOC_BUS_RESET: /* 04 */ - case MPI_EVENT_EXT_BUS_RESET: /* 05 */ - case MPI_EVENT_RESCAN: /* 06 */ - case MPI_EVENT_LINK_STATUS_CHANGE: /* 07 */ - case MPI_EVENT_LOOP_STATE_CHANGE: /* 08 */ - case MPI_EVENT_LOGOUT: /* 09 */ - case MPI_EVENT_INTEGRATED_RAID: /* 0B */ - case MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE: /* 0C */ - default: - break; case MPI_EVENT_EVENT_CHANGE: /* 0A */ if (evDataLen) { u8 evState = evData0 & 0xFF; @@ -5507,6 +5884,8 @@ ProcessEventNotification(MPT_ADAPTER *ioc, EventNotificationReply_t *pEventReply } } break; + default: + break; } /* @@ -5653,6 +6032,111 @@ mpt_sp_log_info(MPT_ADAPTER *ioc, u32 log_info) printk(MYIOC_s_INFO_FMT "LogInfo(0x%08x): F/W: %s\n", ioc->name, log_info, desc); } +/* strings for sas loginfo */ + static char *originator_str[] = { + "IOP", /* 00h */ + "PL", /* 01h */ + "IR" /* 02h */ + }; + static char *iop_code_str[] = { + NULL, /* 00h */ + "Invalid SAS Address", /* 01h */ + NULL, /* 02h */ + "Invalid Page", /* 03h */ + NULL, /* 04h */ + "Task Terminated" /* 05h */ + }; + static char *pl_code_str[] = { + NULL, /* 00h */ + "Open Failure", /* 01h */ + "Invalid Scatter Gather List", /* 02h */ + "Wrong Relative Offset or Frame Length", /* 03h */ + "Frame Transfer Error", /* 04h */ + "Transmit Frame Connected Low", /* 05h */ + "SATA Non-NCQ RW Error Bit Set", /* 06h */ + "SATA Read Log Receive Data Error", /* 07h */ + "SATA NCQ Fail All Commands After Error", /* 08h */ + "SATA Error in Receive Set Device Bit FIS", /* 09h */ + "Receive Frame Invalid Message", /* 0Ah */ + "Receive Context Message Valid Error", /* 0Bh */ + "Receive Frame Current Frame Error", /* 0Ch */ + "SATA Link Down", /* 0Dh */ + "Discovery SATA Init W IOS", /* 0Eh */ + "Config Invalid Page", /* 0Fh */ + "Discovery SATA Init Timeout", /* 10h */ + "Reset", /* 11h */ + "Abort", /* 12h */ + "IO Not Yet Executed", /* 13h */ + "IO Executed", /* 14h */ + NULL, /* 15h */ + NULL, /* 16h */ + NULL, /* 17h */ + NULL, /* 18h */ + NULL, /* 19h */ + NULL, /* 1Ah */ + NULL, /* 1Bh */ + NULL, /* 1Ch */ + NULL, /* 1Dh */ + NULL, /* 1Eh */ + NULL, /* 1Fh */ + "Enclosure Management" /* 20h */ + }; + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mpt_sas_log_info - Log information returned from SAS IOC. + * @ioc: Pointer to MPT_ADAPTER structure + * @log_info: U32 LogInfo reply word from the IOC + * + * Refer to lsi/mpi_log_sas.h. + */ +static void +mpt_sas_log_info(MPT_ADAPTER *ioc, u32 log_info) +{ +union loginfo_type { + u32 loginfo; + struct { + u32 subcode:16; + u32 code:8; + u32 originator:4; + u32 bus_type:4; + }dw; +}; + union loginfo_type sas_loginfo; + char *code_desc = NULL; + + sas_loginfo.loginfo = log_info; + if ((sas_loginfo.dw.bus_type != 3 /*SAS*/) && + (sas_loginfo.dw.originator < sizeof(originator_str)/sizeof(char*))) + return; + if ((sas_loginfo.dw.originator == 0 /*IOP*/) && + (sas_loginfo.dw.code < sizeof(iop_code_str)/sizeof(char*))) { + code_desc = iop_code_str[sas_loginfo.dw.code]; + }else if ((sas_loginfo.dw.originator == 1 /*PL*/) && + (sas_loginfo.dw.code < sizeof(pl_code_str)/sizeof(char*) )) { + code_desc = pl_code_str[sas_loginfo.dw.code]; + } + + if (code_desc != NULL) + printk(MYIOC_s_INFO_FMT + "LogInfo(0x%08x): Originator={%s}, Code={%s}," + " SubCode(0x%04x)\n", + ioc->name, + log_info, + originator_str[sas_loginfo.dw.originator], + code_desc, + sas_loginfo.dw.subcode); + else + printk(MYIOC_s_INFO_FMT + "LogInfo(0x%08x): Originator={%s}, Code=(0x%02x)," + " SubCode(0x%04x)\n", + ioc->name, + log_info, + originator_str[sas_loginfo.dw.originator], + sas_loginfo.dw.code, + sas_loginfo.dw.subcode); +} + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* * mpt_sp_ioc_info - IOC information returned from SCSI Parallel IOC. @@ -5814,6 +6298,7 @@ EXPORT_SYMBOL(mpt_findImVolumes); EXPORT_SYMBOL(mpt_read_ioc_pg_3); EXPORT_SYMBOL(mpt_alloc_fw_memory); EXPORT_SYMBOL(mpt_free_fw_memory); +EXPORT_SYMBOL(mptbase_sas_persist_operation); /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ diff --git a/drivers/message/fusion/mptbase.h b/drivers/message/fusion/mptbase.h index f4827d923731..75105277e22f 100644 --- a/drivers/message/fusion/mptbase.h +++ b/drivers/message/fusion/mptbase.h @@ -65,6 +65,7 @@ #include "lsi/mpi_fc.h" /* Fibre Channel (lowlevel) support */ #include "lsi/mpi_targ.h" /* SCSI/FCP Target protcol support */ #include "lsi/mpi_tool.h" /* Tools support */ +#include "lsi/mpi_sas.h" /* SAS support */ /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ @@ -76,8 +77,8 @@ #define COPYRIGHT "Copyright (c) 1999-2005 " MODULEAUTHOR #endif -#define MPT_LINUX_VERSION_COMMON "3.03.02" -#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-3.03.02" +#define MPT_LINUX_VERSION_COMMON "3.03.03" +#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-3.03.03" #define WHAT_MAGIC_STRING "@" "(" "#" ")" #define show_mptmod_ver(s,ver) \ @@ -423,7 +424,7 @@ typedef struct _MPT_IOCTL { /* * Event Structure and define */ -#define MPTCTL_EVENT_LOG_SIZE (0x0000000A) +#define MPTCTL_EVENT_LOG_SIZE (0x000000032) typedef struct _mpt_ioctl_events { u32 event; /* Specified by define above */ u32 eventContext; /* Index or counter */ @@ -451,16 +452,13 @@ typedef struct _mpt_ioctl_events { #define MPT_SCSICFG_ALL_IDS 0x02 /* WriteSDP1 to all IDS */ /* #define MPT_SCSICFG_BLK_NEGO 0x10 WriteSDP1 with WDTR and SDTR disabled */ -typedef struct _ScsiCfgData { +typedef struct _SpiCfgData { u32 PortFlags; int *nvram; /* table of device NVRAM values */ - IOCPage2_t *pIocPg2; /* table of Raid Volumes */ - IOCPage3_t *pIocPg3; /* table of physical disks */ IOCPage4_t *pIocPg4; /* SEP devices addressing */ dma_addr_t IocPg4_dma; /* Phys Addr of IOCPage4 data */ int IocPg4Sz; /* IOCPage4 size */ u8 dvStatus[MPT_MAX_SCSI_DEVICES]; - int isRaid; /* bit field, 1 if RAID */ u8 minSyncFactor; /* 0xFF if async */ u8 maxSyncOffset; /* 0 if async */ u8 maxBusWidth; /* 0 if narrow, 1 if wide */ @@ -472,10 +470,28 @@ typedef struct _ScsiCfgData { u8 dvScheduled; /* 1 if scheduled */ u8 forceDv; /* 1 to force DV scheduling */ u8 noQas; /* Disable QAS for this adapter */ - u8 Saf_Te; /* 1 to force all Processors as SAF-TE if Inquiry data length is too short to check for SAF-TE */ + u8 Saf_Te; /* 1 to force all Processors as + * SAF-TE if Inquiry data length + * is too short to check for SAF-TE + */ u8 mpt_dv; /* command line option: enhanced=1, basic=0 */ + u8 bus_reset; /* 1 to allow bus reset */ u8 rsvd[1]; -} ScsiCfgData; +}SpiCfgData; + +typedef struct _SasCfgData { + u8 ptClear; /* 1 to automatically clear the + * persistent table. + * 0 to disable + * automatic clearing. + */ +}SasCfgData; + +typedef struct _RaidCfgData { + IOCPage2_t *pIocPg2; /* table of Raid Volumes */ + IOCPage3_t *pIocPg3; /* table of physical disks */ + int isRaid; /* bit field, 1 if RAID */ +}RaidCfgData; /* * Adapter Structure - pci_dev specific. Maximum: MPT_MAX_ADAPTERS @@ -530,11 +546,16 @@ typedef struct _MPT_ADAPTER u8 *sense_buf_pool; dma_addr_t sense_buf_pool_dma; u32 sense_buf_low_dma; + u8 *HostPageBuffer; /* SAS - host page buffer support */ + u32 HostPageBuffer_sz; + dma_addr_t HostPageBuffer_dma; int mtrr_reg; struct pci_dev *pcidev; /* struct pci_dev pointer */ u8 __iomem *memmap; /* mmap address */ struct Scsi_Host *sh; /* Scsi Host pointer */ - ScsiCfgData spi_data; /* Scsi config. data */ + SpiCfgData spi_data; /* Scsi config. data */ + RaidCfgData raid_data; /* Raid config. data */ + SasCfgData sas_data; /* Sas config. data */ MPT_IOCTL *ioctl; /* ioctl data pointer */ struct proc_dir_entry *ioc_dentry; struct _MPT_ADAPTER *alt_ioc; /* ptr to 929 bound adapter port */ @@ -554,31 +575,35 @@ typedef struct _MPT_ADAPTER #else u32 mfcnt; #endif - u32 NB_for_64_byte_frame; + u32 NB_for_64_byte_frame; u32 hs_req[MPT_MAX_FRAME_SIZE/sizeof(u32)]; u16 hs_reply[MPT_MAX_FRAME_SIZE/sizeof(u16)]; IOCFactsReply_t facts; PortFactsReply_t pfacts[2]; FCPortPage0_t fc_port_page0[2]; + struct timer_list persist_timer; /* persist table timer */ + int persist_wait_done; /* persist completion flag */ + u8 persist_reply_frame[MPT_DEFAULT_FRAME_SIZE]; /* persist reply */ LANPage0_t lan_cnfg_page0; LANPage1_t lan_cnfg_page1; - /* + /* * Description: errata_flag_1064 * If a PCIX read occurs within 1 or 2 cycles after the chip receives * a split completion for a read data, an internal address pointer incorrectly * increments by 32 bytes */ - int errata_flag_1064; + int errata_flag_1064; u8 FirstWhoInit; u8 upload_fw; /* If set, do a fw upload */ u8 reload_fw; /* Force a FW Reload on next reset */ - u8 NBShiftFactor; /* NB Shift Factor based on Block Size (Facts) */ + u8 NBShiftFactor; /* NB Shift Factor based on Block Size (Facts) */ u8 pad1[4]; int DoneCtx; int TaskCtx; int InternalCtx; - struct list_head list; + struct list_head list; struct net_device *netdev; + struct list_head sas_topology; } MPT_ADAPTER; /* @@ -964,6 +989,7 @@ extern void mpt_alloc_fw_memory(MPT_ADAPTER *ioc, int size); extern void mpt_free_fw_memory(MPT_ADAPTER *ioc); extern int mpt_findImVolumes(MPT_ADAPTER *ioc); extern int mpt_read_ioc_pg_3(MPT_ADAPTER *ioc); +extern int mptbase_sas_persist_operation(MPT_ADAPTER *ioc, u8 persist_opcode); /* * Public data decl's... diff --git a/drivers/message/fusion/mptctl.c b/drivers/message/fusion/mptctl.c index 7577c2417e2e..cb2d59d5f5af 100644 --- a/drivers/message/fusion/mptctl.c +++ b/drivers/message/fusion/mptctl.c @@ -1326,7 +1326,7 @@ mptctl_gettargetinfo (unsigned long arg) */ if (hd && hd->Targets) { mpt_findImVolumes(ioc); - pIoc2 = ioc->spi_data.pIocPg2; + pIoc2 = ioc->raid_data.pIocPg2; for ( id = 0; id <= max_id; ) { if ( pIoc2 && pIoc2->NumActiveVolumes ) { if ( id == pIoc2->RaidVolume[0].VolumeID ) { @@ -1348,7 +1348,7 @@ mptctl_gettargetinfo (unsigned long arg) --maxWordsLeft; goto next_id; } else { - pIoc3 = ioc->spi_data.pIocPg3; + pIoc3 = ioc->raid_data.pIocPg3; for ( jj = 0; jj < pIoc3->NumPhysDisks; jj++ ) { if ( pIoc3->PhysDisk[jj].PhysDiskID == id ) goto next_id; diff --git a/drivers/message/fusion/mptfc.c b/drivers/message/fusion/mptfc.c index 13771abea13f..a628be9bbbad 100644 --- a/drivers/message/fusion/mptfc.c +++ b/drivers/message/fusion/mptfc.c @@ -189,7 +189,7 @@ mptfc_probe(struct pci_dev *pdev, const struct pci_device_id *id) printk(MYIOC_s_WARN_FMT "Skipping ioc=%p because SCSI Initiator mode is NOT enabled!\n", ioc->name, ioc); - return -ENODEV; + return 0; } sh = scsi_host_alloc(&mptfc_driver_template, sizeof(MPT_SCSI_HOST)); diff --git a/drivers/message/fusion/mptlan.c b/drivers/message/fusion/mptlan.c index 52794be5a95c..ed3c891e388f 100644 --- a/drivers/message/fusion/mptlan.c +++ b/drivers/message/fusion/mptlan.c @@ -312,7 +312,12 @@ static int mpt_lan_ioc_reset(MPT_ADAPTER *ioc, int reset_phase) { struct net_device *dev = ioc->netdev; - struct mpt_lan_priv *priv = netdev_priv(dev); + struct mpt_lan_priv *priv; + + if (dev == NULL) + return(1); + else + priv = netdev_priv(dev); dlprintk((KERN_INFO MYNAM ": IOC %s_reset routed to LAN driver!\n", reset_phase==MPT_IOC_SETUP_RESET ? "setup" : ( diff --git a/drivers/message/fusion/mptsas.c b/drivers/message/fusion/mptsas.c new file mode 100644 index 000000000000..429820e48c69 --- /dev/null +++ b/drivers/message/fusion/mptsas.c @@ -0,0 +1,1235 @@ +/* + * linux/drivers/message/fusion/mptsas.c + * For use with LSI Logic PCI chip/adapter(s) + * running LSI Logic Fusion MPT (Message Passing Technology) firmware. + * + * Copyright (c) 1999-2005 LSI Logic Corporation + * (mailto:mpt_linux_developer@lsil.com) + * Copyright (c) 2005 Dell + */ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + 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; version 2 of the License. + + 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. + + NO WARRANTY + THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT + LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is + solely responsible for determining the appropriateness of using and + distributing the Program and assumes all risks associated with its + exercise of rights under this Agreement, including but not limited to + the risks and costs of program errors, damage to or loss of data, + programs or equipment, and unavailability or interruption of operations. + + DISCLAIMER OF LIABILITY + NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED + HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/workqueue.h> + +#include <scsi/scsi_cmnd.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi_host.h> +#include <scsi/scsi_transport_sas.h> + +#include "mptbase.h" +#include "mptscsih.h" + + +#define my_NAME "Fusion MPT SAS Host driver" +#define my_VERSION MPT_LINUX_VERSION_COMMON +#define MYNAM "mptsas" + +MODULE_AUTHOR(MODULEAUTHOR); +MODULE_DESCRIPTION(my_NAME); +MODULE_LICENSE("GPL"); + +static int mpt_pq_filter; +module_param(mpt_pq_filter, int, 0); +MODULE_PARM_DESC(mpt_pq_filter, + "Enable peripheral qualifier filter: enable=1 " + "(default=0)"); + +static int mpt_pt_clear; +module_param(mpt_pt_clear, int, 0); +MODULE_PARM_DESC(mpt_pt_clear, + "Clear persistency table: enable=1 " + "(default=MPTSCSIH_PT_CLEAR=0)"); + +static int mptsasDoneCtx = -1; +static int mptsasTaskCtx = -1; +static int mptsasInternalCtx = -1; /* Used only for internal commands */ + + +/* + * SAS topology structures + * + * The MPT Fusion firmware interface spreads information about the + * SAS topology over many manufacture pages, thus we need some data + * structure to collect it and process it for the SAS transport class. + */ + +struct mptsas_devinfo { + u16 handle; /* unique id to address this device */ + u8 phy_id; /* phy number of parent device */ + u8 port_id; /* sas physical port this device + is assoc'd with */ + u8 target; /* logical target id of this device */ + u8 bus; /* logical bus number of this device */ + u64 sas_address; /* WWN of this device, + SATA is assigned by HBA,expander */ + u32 device_info; /* bitfield detailed info about this device */ +}; + +struct mptsas_phyinfo { + u8 phy_id; /* phy index */ + u8 port_id; /* port number this phy is part of */ + u8 negotiated_link_rate; /* nego'd link rate for this phy */ + u8 hw_link_rate; /* hardware max/min phys link rate */ + u8 programmed_link_rate; /* programmed max/min phy link rate */ + struct mptsas_devinfo identify; /* point to phy device info */ + struct mptsas_devinfo attached; /* point to attached device info */ + struct sas_rphy *rphy; +}; + +struct mptsas_portinfo { + struct list_head list; + u16 handle; /* unique id to address this */ + u8 num_phys; /* number of phys */ + struct mptsas_phyinfo *phy_info; +}; + +/* + * This is pretty ugly. We will be able to seriously clean it up + * once the DV code in mptscsih goes away and we can properly + * implement ->target_alloc. + */ +static int +mptsas_slave_alloc(struct scsi_device *device) +{ + struct Scsi_Host *host = device->host; + MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata; + struct sas_rphy *rphy; + struct mptsas_portinfo *p; + VirtDevice *vdev; + uint target = device->id; + int i; + + if ((vdev = hd->Targets[target]) != NULL) + goto out; + + vdev = kmalloc(sizeof(VirtDevice), GFP_KERNEL); + if (!vdev) { + printk(MYIOC_s_ERR_FMT "slave_alloc kmalloc(%zd) FAILED!\n", + hd->ioc->name, sizeof(VirtDevice)); + return -ENOMEM; + } + + memset(vdev, 0, sizeof(VirtDevice)); + vdev->tflags = MPT_TARGET_FLAGS_Q_YES|MPT_TARGET_FLAGS_VALID_INQUIRY; + vdev->ioc_id = hd->ioc->id; + + rphy = dev_to_rphy(device->sdev_target->dev.parent); + list_for_each_entry(p, &hd->ioc->sas_topology, list) { + for (i = 0; i < p->num_phys; i++) { + if (p->phy_info[i].attached.sas_address == + rphy->identify.sas_address) { + vdev->target_id = + p->phy_info[i].attached.target; + vdev->bus_id = p->phy_info[i].attached.bus; + hd->Targets[device->id] = vdev; + goto out; + } + } + } + + printk("No matching SAS device found!!\n"); + kfree(vdev); + return -ENODEV; + + out: + vdev->num_luns++; + device->hostdata = vdev; + return 0; +} + +static struct scsi_host_template mptsas_driver_template = { + .proc_name = "mptsas", + .proc_info = mptscsih_proc_info, + .name = "MPT SPI Host", + .info = mptscsih_info, + .queuecommand = mptscsih_qcmd, + .slave_alloc = mptsas_slave_alloc, + .slave_configure = mptscsih_slave_configure, + .slave_destroy = mptscsih_slave_destroy, + .change_queue_depth = mptscsih_change_queue_depth, + .eh_abort_handler = mptscsih_abort, + .eh_device_reset_handler = mptscsih_dev_reset, + .eh_bus_reset_handler = mptscsih_bus_reset, + .eh_host_reset_handler = mptscsih_host_reset, + .bios_param = mptscsih_bios_param, + .can_queue = MPT_FC_CAN_QUEUE, + .this_id = -1, + .sg_tablesize = MPT_SCSI_SG_DEPTH, + .max_sectors = 8192, + .cmd_per_lun = 7, + .use_clustering = ENABLE_CLUSTERING, +}; + +static struct sas_function_template mptsas_transport_functions = { +}; + +static struct scsi_transport_template *mptsas_transport_template; + +#ifdef SASDEBUG +static void mptsas_print_phy_data(MPI_SAS_IO_UNIT0_PHY_DATA *phy_data) +{ + printk("---- IO UNIT PAGE 0 ------------\n"); + printk("Handle=0x%X\n", + le16_to_cpu(phy_data->AttachedDeviceHandle)); + printk("Controller Handle=0x%X\n", + le16_to_cpu(phy_data->ControllerDevHandle)); + printk("Port=0x%X\n", phy_data->Port); + printk("Port Flags=0x%X\n", phy_data->PortFlags); + printk("PHY Flags=0x%X\n", phy_data->PhyFlags); + printk("Negotiated Link Rate=0x%X\n", phy_data->NegotiatedLinkRate); + printk("Controller PHY Device Info=0x%X\n", + le32_to_cpu(phy_data->ControllerPhyDeviceInfo)); + printk("DiscoveryStatus=0x%X\n", + le32_to_cpu(phy_data->DiscoveryStatus)); + printk("\n"); +} + +static void mptsas_print_phy_pg0(SasPhyPage0_t *pg0) +{ + __le64 sas_address; + + memcpy(&sas_address, &pg0->SASAddress, sizeof(__le64)); + + printk("---- SAS PHY PAGE 0 ------------\n"); + printk("Attached Device Handle=0x%X\n", + le16_to_cpu(pg0->AttachedDevHandle)); + printk("SAS Address=0x%llX\n", + (unsigned long long)le64_to_cpu(sas_address)); + printk("Attached PHY Identifier=0x%X\n", pg0->AttachedPhyIdentifier); + printk("Attached Device Info=0x%X\n", + le32_to_cpu(pg0->AttachedDeviceInfo)); + printk("Programmed Link Rate=0x%X\n", pg0->ProgrammedLinkRate); + printk("Change Count=0x%X\n", pg0->ChangeCount); + printk("PHY Info=0x%X\n", le32_to_cpu(pg0->PhyInfo)); + printk("\n"); +} + +static void mptsas_print_device_pg0(SasDevicePage0_t *pg0) +{ + __le64 sas_address; + + memcpy(&sas_address, &pg0->SASAddress, sizeof(__le64)); + + printk("---- SAS DEVICE PAGE 0 ---------\n"); + printk("Handle=0x%X\n" ,le16_to_cpu(pg0->DevHandle)); + printk("Enclosure Handle=0x%X\n", le16_to_cpu(pg0->EnclosureHandle)); + printk("Slot=0x%X\n", le16_to_cpu(pg0->Slot)); + printk("SAS Address=0x%llX\n", le64_to_cpu(sas_address)); + printk("Target ID=0x%X\n", pg0->TargetID); + printk("Bus=0x%X\n", pg0->Bus); + printk("PhyNum=0x%X\n", pg0->PhyNum); + printk("AccessStatus=0x%X\n", le16_to_cpu(pg0->AccessStatus)); + printk("Device Info=0x%X\n", le32_to_cpu(pg0->DeviceInfo)); + printk("Flags=0x%X\n", le16_to_cpu(pg0->Flags)); + printk("Physical Port=0x%X\n", pg0->PhysicalPort); + printk("\n"); +} + +static void mptsas_print_expander_pg1(SasExpanderPage1_t *pg1) +{ + printk("---- SAS EXPANDER PAGE 1 ------------\n"); + + printk("Physical Port=0x%X\n", pg1->PhysicalPort); + printk("PHY Identifier=0x%X\n", pg1->Phy); + printk("Negotiated Link Rate=0x%X\n", pg1->NegotiatedLinkRate); + printk("Programmed Link Rate=0x%X\n", pg1->ProgrammedLinkRate); + printk("Hardware Link Rate=0x%X\n", pg1->HwLinkRate); + printk("Owner Device Handle=0x%X\n", + le16_to_cpu(pg1->OwnerDevHandle)); + printk("Attached Device Handle=0x%X\n", + le16_to_cpu(pg1->AttachedDevHandle)); +} +#else +#define mptsas_print_phy_data(phy_data) do { } while (0) +#define mptsas_print_phy_pg0(pg0) do { } while (0) +#define mptsas_print_device_pg0(pg0) do { } while (0) +#define mptsas_print_expander_pg1(pg1) do { } while (0) +#endif + +static int +mptsas_sas_io_unit_pg0(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info) +{ + ConfigExtendedPageHeader_t hdr; + CONFIGPARMS cfg; + SasIOUnitPage0_t *buffer; + dma_addr_t dma_handle; + int error, i; + + hdr.PageVersion = MPI_SASIOUNITPAGE0_PAGEVERSION; + hdr.ExtPageLength = 0; + hdr.PageNumber = 0; + hdr.Reserved1 = 0; + hdr.Reserved2 = 0; + hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED; + hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT; + + cfg.cfghdr.ehdr = &hdr; + cfg.physAddr = -1; + cfg.pageAddr = 0; + cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; + cfg.dir = 0; /* read */ + cfg.timeout = 10; + + error = mpt_config(ioc, &cfg); + if (error) + goto out; + if (!hdr.ExtPageLength) { + error = -ENXIO; + goto out; + } + + buffer = pci_alloc_consistent(ioc->pcidev, hdr.ExtPageLength * 4, + &dma_handle); + if (!buffer) { + error = -ENOMEM; + goto out; + } + + cfg.physAddr = dma_handle; + cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; + + error = mpt_config(ioc, &cfg); + if (error) + goto out_free_consistent; + + port_info->num_phys = buffer->NumPhys; + port_info->phy_info = kcalloc(port_info->num_phys, + sizeof(struct mptsas_phyinfo),GFP_KERNEL); + if (!port_info->phy_info) { + error = -ENOMEM; + goto out_free_consistent; + } + + for (i = 0; i < port_info->num_phys; i++) { + mptsas_print_phy_data(&buffer->PhyData[i]); + port_info->phy_info[i].phy_id = i; + port_info->phy_info[i].port_id = + buffer->PhyData[i].Port; + port_info->phy_info[i].negotiated_link_rate = + buffer->PhyData[i].NegotiatedLinkRate; + } + + out_free_consistent: + pci_free_consistent(ioc->pcidev, hdr.ExtPageLength * 4, + buffer, dma_handle); + out: + return error; +} + +static int +mptsas_sas_phy_pg0(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info, + u32 form, u32 form_specific) +{ + ConfigExtendedPageHeader_t hdr; + CONFIGPARMS cfg; + SasPhyPage0_t *buffer; + dma_addr_t dma_handle; + int error; + + hdr.PageVersion = MPI_SASPHY0_PAGEVERSION; + hdr.ExtPageLength = 0; + hdr.PageNumber = 0; + hdr.Reserved1 = 0; + hdr.Reserved2 = 0; + hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED; + hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_PHY; + + cfg.cfghdr.ehdr = &hdr; + cfg.dir = 0; /* read */ + cfg.timeout = 10; + + /* Get Phy Pg 0 for each Phy. */ + cfg.physAddr = -1; + cfg.pageAddr = form + form_specific; + cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; + + error = mpt_config(ioc, &cfg); + if (error) + goto out; + + if (!hdr.ExtPageLength) { + error = -ENXIO; + goto out; + } + + buffer = pci_alloc_consistent(ioc->pcidev, hdr.ExtPageLength * 4, + &dma_handle); + if (!buffer) { + error = -ENOMEM; + goto out; + } + + cfg.physAddr = dma_handle; + cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; + + error = mpt_config(ioc, &cfg); + if (error) + goto out_free_consistent; + + mptsas_print_phy_pg0(buffer); + + phy_info->hw_link_rate = buffer->HwLinkRate; + phy_info->programmed_link_rate = buffer->ProgrammedLinkRate; + phy_info->identify.handle = le16_to_cpu(buffer->OwnerDevHandle); + phy_info->attached.handle = le16_to_cpu(buffer->AttachedDevHandle); + + out_free_consistent: + pci_free_consistent(ioc->pcidev, hdr.ExtPageLength * 4, + buffer, dma_handle); + out: + return error; +} + +static int +mptsas_sas_device_pg0(MPT_ADAPTER *ioc, struct mptsas_devinfo *device_info, + u32 form, u32 form_specific) +{ + ConfigExtendedPageHeader_t hdr; + CONFIGPARMS cfg; + SasDevicePage0_t *buffer; + dma_addr_t dma_handle; + __le64 sas_address; + int error; + + hdr.PageVersion = MPI_SASDEVICE0_PAGEVERSION; + hdr.ExtPageLength = 0; + hdr.PageNumber = 0; + hdr.Reserved1 = 0; + hdr.Reserved2 = 0; + hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED; + hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE; + + cfg.cfghdr.ehdr = &hdr; + cfg.pageAddr = form + form_specific; + cfg.physAddr = -1; + cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; + cfg.dir = 0; /* read */ + cfg.timeout = 10; + + error = mpt_config(ioc, &cfg); + if (error) + goto out; + if (!hdr.ExtPageLength) { + error = -ENXIO; + goto out; + } + + buffer = pci_alloc_consistent(ioc->pcidev, hdr.ExtPageLength * 4, + &dma_handle); + if (!buffer) { + error = -ENOMEM; + goto out; + } + + cfg.physAddr = dma_handle; + cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; + + error = mpt_config(ioc, &cfg); + if (error) + goto out_free_consistent; + + mptsas_print_device_pg0(buffer); + + device_info->handle = le16_to_cpu(buffer->DevHandle); + device_info->phy_id = buffer->PhyNum; + device_info->port_id = buffer->PhysicalPort; + device_info->target = buffer->TargetID; + device_info->bus = buffer->Bus; + memcpy(&sas_address, &buffer->SASAddress, sizeof(__le64)); + device_info->sas_address = le64_to_cpu(sas_address); + device_info->device_info = + le32_to_cpu(buffer->DeviceInfo); + + out_free_consistent: + pci_free_consistent(ioc->pcidev, hdr.ExtPageLength * 4, + buffer, dma_handle); + out: + return error; +} + +static int +mptsas_sas_expander_pg0(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info, + u32 form, u32 form_specific) +{ + ConfigExtendedPageHeader_t hdr; + CONFIGPARMS cfg; + SasExpanderPage0_t *buffer; + dma_addr_t dma_handle; + int error; + + hdr.PageVersion = MPI_SASEXPANDER0_PAGEVERSION; + hdr.ExtPageLength = 0; + hdr.PageNumber = 0; + hdr.Reserved1 = 0; + hdr.Reserved2 = 0; + hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED; + hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_EXPANDER; + + cfg.cfghdr.ehdr = &hdr; + cfg.physAddr = -1; + cfg.pageAddr = form + form_specific; + cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; + cfg.dir = 0; /* read */ + cfg.timeout = 10; + + error = mpt_config(ioc, &cfg); + if (error) + goto out; + + if (!hdr.ExtPageLength) { + error = -ENXIO; + goto out; + } + + buffer = pci_alloc_consistent(ioc->pcidev, hdr.ExtPageLength * 4, + &dma_handle); + if (!buffer) { + error = -ENOMEM; + goto out; + } + + cfg.physAddr = dma_handle; + cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; + + error = mpt_config(ioc, &cfg); + if (error) + goto out_free_consistent; + + /* save config data */ + port_info->num_phys = buffer->NumPhys; + port_info->handle = le16_to_cpu(buffer->DevHandle); + port_info->phy_info = kcalloc(port_info->num_phys, + sizeof(struct mptsas_phyinfo),GFP_KERNEL); + if (!port_info->phy_info) { + error = -ENOMEM; + goto out_free_consistent; + } + + out_free_consistent: + pci_free_consistent(ioc->pcidev, hdr.ExtPageLength * 4, + buffer, dma_handle); + out: + return error; +} + +static int +mptsas_sas_expander_pg1(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info, + u32 form, u32 form_specific) +{ + ConfigExtendedPageHeader_t hdr; + CONFIGPARMS cfg; + SasExpanderPage1_t *buffer; + dma_addr_t dma_handle; + int error; + + hdr.PageVersion = MPI_SASEXPANDER0_PAGEVERSION; + hdr.ExtPageLength = 0; + hdr.PageNumber = 1; + hdr.Reserved1 = 0; + hdr.Reserved2 = 0; + hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED; + hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_EXPANDER; + + cfg.cfghdr.ehdr = &hdr; + cfg.physAddr = -1; + cfg.pageAddr = form + form_specific; + cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; + cfg.dir = 0; /* read */ + cfg.timeout = 10; + + error = mpt_config(ioc, &cfg); + if (error) + goto out; + + if (!hdr.ExtPageLength) { + error = -ENXIO; + goto out; + } + + buffer = pci_alloc_consistent(ioc->pcidev, hdr.ExtPageLength * 4, + &dma_handle); + if (!buffer) { + error = -ENOMEM; + goto out; + } + + cfg.physAddr = dma_handle; + cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; + + error = mpt_config(ioc, &cfg); + if (error) + goto out_free_consistent; + + + mptsas_print_expander_pg1(buffer); + + /* save config data */ + phy_info->phy_id = buffer->Phy; + phy_info->port_id = buffer->PhysicalPort; + phy_info->negotiated_link_rate = buffer->NegotiatedLinkRate; + phy_info->programmed_link_rate = buffer->ProgrammedLinkRate; + phy_info->hw_link_rate = buffer->HwLinkRate; + phy_info->identify.handle = le16_to_cpu(buffer->OwnerDevHandle); + phy_info->attached.handle = le16_to_cpu(buffer->AttachedDevHandle); + + + out_free_consistent: + pci_free_consistent(ioc->pcidev, hdr.ExtPageLength * 4, + buffer, dma_handle); + out: + return error; +} + +static void +mptsas_parse_device_info(struct sas_identify *identify, + struct mptsas_devinfo *device_info) +{ + u16 protocols; + + identify->sas_address = device_info->sas_address; + identify->phy_identifier = device_info->phy_id; + + /* + * Fill in Phy Initiator Port Protocol. + * Bits 6:3, more than one bit can be set, fall through cases. + */ + protocols = device_info->device_info & 0x78; + identify->initiator_port_protocols = 0; + if (protocols & MPI_SAS_DEVICE_INFO_SSP_INITIATOR) + identify->initiator_port_protocols |= SAS_PROTOCOL_SSP; + if (protocols & MPI_SAS_DEVICE_INFO_STP_INITIATOR) + identify->initiator_port_protocols |= SAS_PROTOCOL_STP; + if (protocols & MPI_SAS_DEVICE_INFO_SMP_INITIATOR) + identify->initiator_port_protocols |= SAS_PROTOCOL_SMP; + if (protocols & MPI_SAS_DEVICE_INFO_SATA_HOST) + identify->initiator_port_protocols |= SAS_PROTOCOL_SATA; + + /* + * Fill in Phy Target Port Protocol. + * Bits 10:7, more than one bit can be set, fall through cases. + */ + protocols = device_info->device_info & 0x780; + identify->target_port_protocols = 0; + if (protocols & MPI_SAS_DEVICE_INFO_SSP_TARGET) + identify->target_port_protocols |= SAS_PROTOCOL_SSP; + if (protocols & MPI_SAS_DEVICE_INFO_STP_TARGET) + identify->target_port_protocols |= SAS_PROTOCOL_STP; + if (protocols & MPI_SAS_DEVICE_INFO_SMP_TARGET) + identify->target_port_protocols |= SAS_PROTOCOL_SMP; + if (protocols & MPI_SAS_DEVICE_INFO_SATA_DEVICE) + identify->target_port_protocols |= SAS_PROTOCOL_SATA; + + /* + * Fill in Attached device type. + */ + switch (device_info->device_info & + MPI_SAS_DEVICE_INFO_MASK_DEVICE_TYPE) { + case MPI_SAS_DEVICE_INFO_NO_DEVICE: + identify->device_type = SAS_PHY_UNUSED; + break; + case MPI_SAS_DEVICE_INFO_END_DEVICE: + identify->device_type = SAS_END_DEVICE; + break; + case MPI_SAS_DEVICE_INFO_EDGE_EXPANDER: + identify->device_type = SAS_EDGE_EXPANDER_DEVICE; + break; + case MPI_SAS_DEVICE_INFO_FANOUT_EXPANDER: + identify->device_type = SAS_FANOUT_EXPANDER_DEVICE; + break; + } +} + +static int mptsas_probe_one_phy(struct device *dev, + struct mptsas_phyinfo *phy_info, int index) +{ + struct sas_phy *port; + int error; + + port = sas_phy_alloc(dev, index); + if (!port) + return -ENOMEM; + + port->port_identifier = phy_info->port_id; + mptsas_parse_device_info(&port->identify, &phy_info->identify); + + /* + * Set Negotiated link rate. + */ + switch (phy_info->negotiated_link_rate) { + case MPI_SAS_IOUNIT0_RATE_PHY_DISABLED: + port->negotiated_linkrate = SAS_PHY_DISABLED; + break; + case MPI_SAS_IOUNIT0_RATE_FAILED_SPEED_NEGOTIATION: + port->negotiated_linkrate = SAS_LINK_RATE_FAILED; + break; + case MPI_SAS_IOUNIT0_RATE_1_5: + port->negotiated_linkrate = SAS_LINK_RATE_1_5_GBPS; + break; + case MPI_SAS_IOUNIT0_RATE_3_0: + port->negotiated_linkrate = SAS_LINK_RATE_3_0_GBPS; + break; + case MPI_SAS_IOUNIT0_RATE_SATA_OOB_COMPLETE: + case MPI_SAS_IOUNIT0_RATE_UNKNOWN: + default: + port->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN; + break; + } + + /* + * Set Max hardware link rate. + */ + switch (phy_info->hw_link_rate & MPI_SAS_PHY0_PRATE_MAX_RATE_MASK) { + case MPI_SAS_PHY0_HWRATE_MAX_RATE_1_5: + port->maximum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS; + break; + case MPI_SAS_PHY0_PRATE_MAX_RATE_3_0: + port->maximum_linkrate_hw = SAS_LINK_RATE_3_0_GBPS; + break; + default: + break; + } + + /* + * Set Max programmed link rate. + */ + switch (phy_info->programmed_link_rate & + MPI_SAS_PHY0_PRATE_MAX_RATE_MASK) { + case MPI_SAS_PHY0_PRATE_MAX_RATE_1_5: + port->maximum_linkrate = SAS_LINK_RATE_1_5_GBPS; + break; + case MPI_SAS_PHY0_PRATE_MAX_RATE_3_0: + port->maximum_linkrate = SAS_LINK_RATE_3_0_GBPS; + break; + default: + break; + } + + /* + * Set Min hardware link rate. + */ + switch (phy_info->hw_link_rate & MPI_SAS_PHY0_HWRATE_MIN_RATE_MASK) { + case MPI_SAS_PHY0_HWRATE_MIN_RATE_1_5: + port->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS; + break; + case MPI_SAS_PHY0_PRATE_MIN_RATE_3_0: + port->minimum_linkrate_hw = SAS_LINK_RATE_3_0_GBPS; + break; + default: + break; + } + + /* + * Set Min programmed link rate. + */ + switch (phy_info->programmed_link_rate & + MPI_SAS_PHY0_PRATE_MIN_RATE_MASK) { + case MPI_SAS_PHY0_PRATE_MIN_RATE_1_5: + port->minimum_linkrate = SAS_LINK_RATE_1_5_GBPS; + break; + case MPI_SAS_PHY0_PRATE_MIN_RATE_3_0: + port->minimum_linkrate = SAS_LINK_RATE_3_0_GBPS; + break; + default: + break; + } + + error = sas_phy_add(port); + if (error) { + sas_phy_free(port); + return error; + } + + if (phy_info->attached.handle) { + struct sas_rphy *rphy; + + rphy = sas_rphy_alloc(port); + if (!rphy) + return 0; /* non-fatal: an rphy can be added later */ + + mptsas_parse_device_info(&rphy->identify, &phy_info->attached); + error = sas_rphy_add(rphy); + if (error) { + sas_rphy_free(rphy); + return error; + } + + phy_info->rphy = rphy; + } + + return 0; +} + +static int +mptsas_probe_hba_phys(MPT_ADAPTER *ioc, int *index) +{ + struct mptsas_portinfo *port_info; + u32 handle = 0xFFFF; + int error = -ENOMEM, i; + + port_info = kmalloc(sizeof(*port_info), GFP_KERNEL); + if (!port_info) + goto out; + memset(port_info, 0, sizeof(*port_info)); + + error = mptsas_sas_io_unit_pg0(ioc, port_info); + if (error) + goto out_free_port_info; + + list_add_tail(&port_info->list, &ioc->sas_topology); + + for (i = 0; i < port_info->num_phys; i++) { + mptsas_sas_phy_pg0(ioc, &port_info->phy_info[i], + (MPI_SAS_PHY_PGAD_FORM_PHY_NUMBER << + MPI_SAS_PHY_PGAD_FORM_SHIFT), i); + + mptsas_sas_device_pg0(ioc, &port_info->phy_info[i].identify, + (MPI_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE << + MPI_SAS_DEVICE_PGAD_FORM_SHIFT), handle); + handle = port_info->phy_info[i].identify.handle; + + if (port_info->phy_info[i].attached.handle) { + mptsas_sas_device_pg0(ioc, + &port_info->phy_info[i].attached, + (MPI_SAS_DEVICE_PGAD_FORM_HANDLE << + MPI_SAS_DEVICE_PGAD_FORM_SHIFT), + port_info->phy_info[i].attached.handle); + } + + mptsas_probe_one_phy(&ioc->sh->shost_gendev, + &port_info->phy_info[i], *index); + (*index)++; + } + + return 0; + + out_free_port_info: + kfree(port_info); + out: + return error; +} + +static int +mptsas_probe_expander_phys(MPT_ADAPTER *ioc, u32 *handle, int *index) +{ + struct mptsas_portinfo *port_info, *p; + int error = -ENOMEM, i, j; + + port_info = kmalloc(sizeof(*port_info), GFP_KERNEL); + if (!port_info) + goto out; + memset(port_info, 0, sizeof(*port_info)); + + error = mptsas_sas_expander_pg0(ioc, port_info, + (MPI_SAS_EXPAND_PGAD_FORM_GET_NEXT_HANDLE << + MPI_SAS_EXPAND_PGAD_FORM_SHIFT), *handle); + if (error) + goto out_free_port_info; + + *handle = port_info->handle; + + list_add_tail(&port_info->list, &ioc->sas_topology); + for (i = 0; i < port_info->num_phys; i++) { + struct device *parent; + + mptsas_sas_expander_pg1(ioc, &port_info->phy_info[i], + (MPI_SAS_EXPAND_PGAD_FORM_HANDLE_PHY_NUM << + MPI_SAS_EXPAND_PGAD_FORM_SHIFT), (i << 16) + *handle); + + if (port_info->phy_info[i].identify.handle) { + mptsas_sas_device_pg0(ioc, + &port_info->phy_info[i].identify, + (MPI_SAS_DEVICE_PGAD_FORM_HANDLE << + MPI_SAS_DEVICE_PGAD_FORM_SHIFT), + port_info->phy_info[i].identify.handle); + } + + if (port_info->phy_info[i].attached.handle) { + mptsas_sas_device_pg0(ioc, + &port_info->phy_info[i].attached, + (MPI_SAS_DEVICE_PGAD_FORM_HANDLE << + MPI_SAS_DEVICE_PGAD_FORM_SHIFT), + port_info->phy_info[i].attached.handle); + } + + /* + * If we find a parent port handle this expander is + * attached to another expander, else it hangs of the + * HBA phys. + */ + parent = &ioc->sh->shost_gendev; + list_for_each_entry(p, &ioc->sas_topology, list) { + for (j = 0; j < p->num_phys; j++) { + if (port_info->phy_info[i].identify.handle == + p->phy_info[j].attached.handle) + parent = &p->phy_info[j].rphy->dev; + } + } + + mptsas_probe_one_phy(parent, &port_info->phy_info[i], *index); + (*index)++; + } + + return 0; + + out_free_port_info: + kfree(port_info); + out: + return error; +} + +static void +mptsas_scan_sas_topology(MPT_ADAPTER *ioc) +{ + u32 handle = 0xFFFF; + int index = 0; + + mptsas_probe_hba_phys(ioc, &index); + while (!mptsas_probe_expander_phys(ioc, &handle, &index)) + ; +} + +static int +mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct Scsi_Host *sh; + MPT_SCSI_HOST *hd; + MPT_ADAPTER *ioc; + unsigned long flags; + int sz, ii; + int numSGE = 0; + int scale; + int ioc_cap; + u8 *mem; + int error=0; + int r; + + r = mpt_attach(pdev,id); + if (r) + return r; + + ioc = pci_get_drvdata(pdev); + ioc->DoneCtx = mptsasDoneCtx; + ioc->TaskCtx = mptsasTaskCtx; + ioc->InternalCtx = mptsasInternalCtx; + + /* Added sanity check on readiness of the MPT adapter. + */ + if (ioc->last_state != MPI_IOC_STATE_OPERATIONAL) { + printk(MYIOC_s_WARN_FMT + "Skipping because it's not operational!\n", + ioc->name); + return -ENODEV; + } + + if (!ioc->active) { + printk(MYIOC_s_WARN_FMT "Skipping because it's disabled!\n", + ioc->name); + return -ENODEV; + } + + /* Sanity check - ensure at least 1 port is INITIATOR capable + */ + ioc_cap = 0; + for (ii = 0; ii < ioc->facts.NumberOfPorts; ii++) { + if (ioc->pfacts[ii].ProtocolFlags & + MPI_PORTFACTS_PROTOCOL_INITIATOR) + ioc_cap++; + } + + if (!ioc_cap) { + printk(MYIOC_s_WARN_FMT + "Skipping ioc=%p because SCSI Initiator mode " + "is NOT enabled!\n", ioc->name, ioc); + return 0; + } + + sh = scsi_host_alloc(&mptsas_driver_template, sizeof(MPT_SCSI_HOST)); + if (!sh) { + printk(MYIOC_s_WARN_FMT + "Unable to register controller with SCSI subsystem\n", + ioc->name); + return -1; + } + + spin_lock_irqsave(&ioc->FreeQlock, flags); + + /* Attach the SCSI Host to the IOC structure + */ + ioc->sh = sh; + + sh->io_port = 0; + sh->n_io_port = 0; + sh->irq = 0; + + /* set 16 byte cdb's */ + sh->max_cmd_len = 16; + + sh->max_id = ioc->pfacts->MaxDevices + 1; + + sh->transportt = mptsas_transport_template; + + sh->max_lun = MPT_LAST_LUN + 1; + sh->max_channel = 0; + sh->this_id = ioc->pfacts[0].PortSCSIID; + + /* Required entry. + */ + sh->unique_id = ioc->id; + + INIT_LIST_HEAD(&ioc->sas_topology); + + /* Verify that we won't exceed the maximum + * number of chain buffers + * We can optimize: ZZ = req_sz/sizeof(SGE) + * For 32bit SGE's: + * numSGE = 1 + (ZZ-1)*(maxChain -1) + ZZ + * + (req_sz - 64)/sizeof(SGE) + * A slightly different algorithm is required for + * 64bit SGEs. + */ + scale = ioc->req_sz/(sizeof(dma_addr_t) + sizeof(u32)); + if (sizeof(dma_addr_t) == sizeof(u64)) { + numSGE = (scale - 1) * + (ioc->facts.MaxChainDepth-1) + scale + + (ioc->req_sz - 60) / (sizeof(dma_addr_t) + + sizeof(u32)); + } else { + numSGE = 1 + (scale - 1) * + (ioc->facts.MaxChainDepth-1) + scale + + (ioc->req_sz - 64) / (sizeof(dma_addr_t) + + sizeof(u32)); + } + + if (numSGE < sh->sg_tablesize) { + /* Reset this value */ + dprintk((MYIOC_s_INFO_FMT + "Resetting sg_tablesize to %d from %d\n", + ioc->name, numSGE, sh->sg_tablesize)); + sh->sg_tablesize = numSGE; + } + + spin_unlock_irqrestore(&ioc->FreeQlock, flags); + + hd = (MPT_SCSI_HOST *) sh->hostdata; + hd->ioc = ioc; + + /* SCSI needs scsi_cmnd lookup table! + * (with size equal to req_depth*PtrSz!) + */ + sz = ioc->req_depth * sizeof(void *); + mem = kmalloc(sz, GFP_ATOMIC); + if (mem == NULL) { + error = -ENOMEM; + goto mptsas_probe_failed; + } + + memset(mem, 0, sz); + hd->ScsiLookup = (struct scsi_cmnd **) mem; + + dprintk((MYIOC_s_INFO_FMT "ScsiLookup @ %p, sz=%d\n", + ioc->name, hd->ScsiLookup, sz)); + + /* Allocate memory for the device structures. + * A non-Null pointer at an offset + * indicates a device exists. + * max_id = 1 + maximum id (hosts.h) + */ + sz = sh->max_id * sizeof(void *); + mem = kmalloc(sz, GFP_ATOMIC); + if (mem == NULL) { + error = -ENOMEM; + goto mptsas_probe_failed; + } + + memset(mem, 0, sz); + hd->Targets = (VirtDevice **) mem; + + dprintk((KERN_INFO + " Targets @ %p, sz=%d\n", hd->Targets, sz)); + + /* Clear the TM flags + */ + hd->tmPending = 0; + hd->tmState = TM_STATE_NONE; + hd->resetPending = 0; + hd->abortSCpnt = NULL; + + /* Clear the pointer used to store + * single-threaded commands, i.e., those + * issued during a bus scan, dv and + * configuration pages. + */ + hd->cmdPtr = NULL; + + /* Initialize this SCSI Hosts' timers + * To use, set the timer expires field + * and add_timer + */ + init_timer(&hd->timer); + hd->timer.data = (unsigned long) hd; + hd->timer.function = mptscsih_timer_expired; + + hd->mpt_pq_filter = mpt_pq_filter; + ioc->sas_data.ptClear = mpt_pt_clear; + + if (ioc->sas_data.ptClear==1) { + mptbase_sas_persist_operation( + ioc, MPI_SAS_OP_CLEAR_ALL_PERSISTENT); + } + + ddvprintk((MYIOC_s_INFO_FMT + "mpt_pq_filter %x mpt_pq_filter %x\n", + ioc->name, + mpt_pq_filter, + mpt_pq_filter)); + + init_waitqueue_head(&hd->scandv_waitq); + hd->scandv_wait_done = 0; + hd->last_queue_full = 0; + + error = scsi_add_host(sh, &ioc->pcidev->dev); + if (error) { + dprintk((KERN_ERR MYNAM + "scsi_add_host failed\n")); + goto mptsas_probe_failed; + } + + mptsas_scan_sas_topology(ioc); + + return 0; + +mptsas_probe_failed: + + mptscsih_remove(pdev); + return error; +} + +static void __devexit mptsas_remove(struct pci_dev *pdev) +{ + MPT_ADAPTER *ioc = pci_get_drvdata(pdev); + struct mptsas_portinfo *p, *n; + + sas_remove_host(ioc->sh); + + list_for_each_entry_safe(p, n, &ioc->sas_topology, list) { + list_del(&p->list); + kfree(p); + } + + mptscsih_remove(pdev); +} + +static struct pci_device_id mptsas_pci_table[] = { + { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1064, + PCI_ANY_ID, PCI_ANY_ID }, + { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1066, + PCI_ANY_ID, PCI_ANY_ID }, + { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1068, + PCI_ANY_ID, PCI_ANY_ID }, + { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1064E, + PCI_ANY_ID, PCI_ANY_ID }, + { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1066E, + PCI_ANY_ID, PCI_ANY_ID }, + { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1068E, + PCI_ANY_ID, PCI_ANY_ID }, + {0} /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(pci, mptsas_pci_table); + + +static struct pci_driver mptsas_driver = { + .name = "mptsas", + .id_table = mptsas_pci_table, + .probe = mptsas_probe, + .remove = __devexit_p(mptsas_remove), + .shutdown = mptscsih_shutdown, +#ifdef CONFIG_PM + .suspend = mptscsih_suspend, + .resume = mptscsih_resume, +#endif +}; + +static int __init +mptsas_init(void) +{ + show_mptmod_ver(my_NAME, my_VERSION); + + mptsas_transport_template = + sas_attach_transport(&mptsas_transport_functions); + if (!mptsas_transport_template) + return -ENODEV; + + mptsasDoneCtx = mpt_register(mptscsih_io_done, MPTSAS_DRIVER); + mptsasTaskCtx = mpt_register(mptscsih_taskmgmt_complete, MPTSAS_DRIVER); + mptsasInternalCtx = + mpt_register(mptscsih_scandv_complete, MPTSAS_DRIVER); + + if (mpt_event_register(mptsasDoneCtx, mptscsih_event_process) == 0) { + devtprintk((KERN_INFO MYNAM + ": Registered for IOC event notifications\n")); + } + + if (mpt_reset_register(mptsasDoneCtx, mptscsih_ioc_reset) == 0) { + dprintk((KERN_INFO MYNAM + ": Registered for IOC reset notifications\n")); + } + + return pci_register_driver(&mptsas_driver); +} + +static void __exit +mptsas_exit(void) +{ + pci_unregister_driver(&mptsas_driver); + sas_release_transport(mptsas_transport_template); + + mpt_reset_deregister(mptsasDoneCtx); + mpt_event_deregister(mptsasDoneCtx); + + mpt_deregister(mptsasInternalCtx); + mpt_deregister(mptsasTaskCtx); + mpt_deregister(mptsasDoneCtx); +} + +module_init(mptsas_init); +module_exit(mptsas_exit); diff --git a/drivers/message/fusion/mptscsih.c b/drivers/message/fusion/mptscsih.c index 4a003dc5fde8..5cb07eb224d7 100644 --- a/drivers/message/fusion/mptscsih.c +++ b/drivers/message/fusion/mptscsih.c @@ -62,6 +62,7 @@ #include <scsi/scsi_device.h> #include <scsi/scsi_host.h> #include <scsi/scsi_tcq.h> +#include <scsi/scsi_dbg.h> #include "mptbase.h" #include "mptscsih.h" @@ -93,8 +94,9 @@ typedef struct _BIG_SENSE_BUF { #define MPT_ICFLAG_BUF_CAP 0x01 /* ReadBuffer Read Capacity format */ #define MPT_ICFLAG_ECHO 0x02 /* ReadBuffer Echo buffer format */ -#define MPT_ICFLAG_PHYS_DISK 0x04 /* Any SCSI IO but do Phys Disk Format */ -#define MPT_ICFLAG_TAGGED_CMD 0x08 /* Do tagged IO */ +#define MPT_ICFLAG_EBOS 0x04 /* ReadBuffer Echo buffer has EBOS */ +#define MPT_ICFLAG_PHYS_DISK 0x08 /* Any SCSI IO but do Phys Disk Format */ +#define MPT_ICFLAG_TAGGED_CMD 0x10 /* Do tagged IO */ #define MPT_ICFLAG_DID_RESET 0x20 /* Bus Reset occurred with this command */ #define MPT_ICFLAG_RESERVED 0x40 /* Reserved has been issued */ @@ -159,6 +161,8 @@ int mptscsih_scandv_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR static int mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *iocmd); static int mptscsih_synchronize_cache(MPT_SCSI_HOST *hd, int portnum); +static struct work_struct mptscsih_persistTask; + #ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION static int mptscsih_do_raid(MPT_SCSI_HOST *hd, u8 action, INTERNAL_CMD *io); static void mptscsih_domainValidation(void *hd); @@ -167,6 +171,7 @@ static void mptscsih_qas_check(MPT_SCSI_HOST *hd, int id); static int mptscsih_doDv(MPT_SCSI_HOST *hd, int channel, int target); static void mptscsih_dv_parms(MPT_SCSI_HOST *hd, DVPARAMETERS *dv,void *pPage); static void mptscsih_fillbuf(char *buffer, int size, int index, int width); +static void mptscsih_set_dvflags_raid(MPT_SCSI_HOST *hd, int id); #endif void mptscsih_remove(struct pci_dev *); @@ -606,11 +611,24 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr) xfer_cnt = le32_to_cpu(pScsiReply->TransferCount); sc->resid = sc->request_bufflen - xfer_cnt; + /* + * if we get a data underrun indication, yet no data was + * transferred and the SCSI status indicates that the + * command was never started, change the data underrun + * to success + */ + if (status == MPI_IOCSTATUS_SCSI_DATA_UNDERRUN && xfer_cnt == 0 && + (scsi_status == MPI_SCSI_STATUS_BUSY || + scsi_status == MPI_SCSI_STATUS_RESERVATION_CONFLICT || + scsi_status == MPI_SCSI_STATUS_TASK_SET_FULL)) { + status = MPI_IOCSTATUS_SUCCESS; + } + dreplyprintk((KERN_NOTICE "Reply ha=%d id=%d lun=%d:\n" "IOCStatus=%04xh SCSIState=%02xh SCSIStatus=%02xh\n" "resid=%d bufflen=%d xfer_cnt=%d\n", ioc->id, pScsiReq->TargetID, pScsiReq->LUN[1], - status, scsi_state, scsi_status, sc->resid, + status, scsi_state, scsi_status, sc->resid, sc->request_bufflen, xfer_cnt)); if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) @@ -619,8 +637,11 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr) /* * Look for + dump FCP ResponseInfo[]! */ - if (scsi_state & MPI_SCSI_STATE_RESPONSE_INFO_VALID) { - printk(KERN_NOTICE " FCP_ResponseInfo=%08xh\n", + if (scsi_state & MPI_SCSI_STATE_RESPONSE_INFO_VALID && + pScsiReply->ResponseInfo) { + printk(KERN_NOTICE "ha=%d id=%d lun=%d: " + "FCP_ResponseInfo=%08xh\n", + ioc->id, pScsiReq->TargetID, pScsiReq->LUN[1], le32_to_cpu(pScsiReply->ResponseInfo)); } @@ -661,23 +682,13 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr) break; case MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: /* 0x0049 */ - if ( xfer_cnt >= sc->underflow ) { - /* Sufficient data transfer occurred */ + sc->resid = sc->request_bufflen - xfer_cnt; + if((xfer_cnt==0)||(sc->underflow > xfer_cnt)) + sc->result=DID_SOFT_ERROR << 16; + else /* Sufficient data transfer occurred */ sc->result = (DID_OK << 16) | scsi_status; - } else if ( xfer_cnt == 0 ) { - /* A CRC Error causes this condition; retry */ - sc->result = (DRIVER_SENSE << 24) | (DID_OK << 16) | - (CHECK_CONDITION << 1); - sc->sense_buffer[0] = 0x70; - sc->sense_buffer[2] = NO_SENSE; - sc->sense_buffer[12] = 0; - sc->sense_buffer[13] = 0; - } else { - sc->result = DID_SOFT_ERROR << 16; - } - dreplyprintk((KERN_NOTICE - "RESIDUAL_MISMATCH: result=%x on id=%d\n", - sc->result, sc->device->id)); + dreplyprintk((KERN_NOTICE + "RESIDUAL_MISMATCH: result=%x on id=%d\n", sc->result, sc->device->id)); break; case MPI_IOCSTATUS_SCSI_DATA_UNDERRUN: /* 0x0045 */ @@ -692,7 +703,10 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr) ; } else { if (xfer_cnt < sc->underflow) { - sc->result = DID_SOFT_ERROR << 16; + if (scsi_status == SAM_STAT_BUSY) + sc->result = SAM_STAT_BUSY; + else + sc->result = DID_SOFT_ERROR << 16; } if (scsi_state & (MPI_SCSI_STATE_AUTOSENSE_FAILED | MPI_SCSI_STATE_NO_SCSI_STATUS)) { /* What to do? @@ -717,8 +731,10 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr) case MPI_IOCSTATUS_SCSI_RECOVERED_ERROR: /* 0x0040 */ case MPI_IOCSTATUS_SUCCESS: /* 0x0000 */ - scsi_status = pScsiReply->SCSIStatus; - sc->result = (DID_OK << 16) | scsi_status; + if (scsi_status == MPI_SCSI_STATUS_BUSY) + sc->result = (DID_BUS_BUSY << 16) | scsi_status; + else + sc->result = (DID_OK << 16) | scsi_status; if (scsi_state == 0) { ; } else if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) { @@ -890,12 +906,13 @@ mptscsih_search_running_cmds(MPT_SCSI_HOST *hd, uint target, uint lun) SCSIIORequest_t *mf = NULL; int ii; int max = hd->ioc->req_depth; + struct scsi_cmnd *sc; dsprintk((KERN_INFO MYNAM ": search_running target %d lun %d max %d\n", target, lun, max)); for (ii=0; ii < max; ii++) { - if (hd->ScsiLookup[ii] != NULL) { + if ((sc = hd->ScsiLookup[ii]) != NULL) { mf = (SCSIIORequest_t *)MPT_INDEX_2_MFPTR(hd->ioc, ii); @@ -910,9 +927,22 @@ mptscsih_search_running_cmds(MPT_SCSI_HOST *hd, uint target, uint lun) hd->ScsiLookup[ii] = NULL; mptscsih_freeChainBuffers(hd->ioc, ii); mpt_free_msg_frame(hd->ioc, (MPT_FRAME_HDR *)mf); + if (sc->use_sg) { + pci_unmap_sg(hd->ioc->pcidev, + (struct scatterlist *) sc->request_buffer, + sc->use_sg, + sc->sc_data_direction); + } else if (sc->request_bufflen) { + pci_unmap_single(hd->ioc->pcidev, + sc->SCp.dma_handle, + sc->request_bufflen, + sc->sc_data_direction); + } + sc->host_scribble = NULL; + sc->result = DID_NO_CONNECT << 16; + sc->scsi_done(sc); } } - return; } @@ -967,8 +997,10 @@ mptscsih_remove(struct pci_dev *pdev) unsigned long flags; int sz1; - if(!host) + if(!host) { + mpt_detach(pdev); return; + } scsi_remove_host(host); @@ -1256,8 +1288,7 @@ mptscsih_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *)) MPT_SCSI_HOST *hd; MPT_FRAME_HDR *mf; SCSIIORequest_t *pScsiReq; - VirtDevice *pTarget; - int target; + VirtDevice *pTarget = SCpnt->device->hostdata; int lun; u32 datalen; u32 scsictl; @@ -1267,12 +1298,9 @@ mptscsih_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *)) int ii; hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata; - target = SCpnt->device->id; lun = SCpnt->device->lun; SCpnt->scsi_done = done; - pTarget = hd->Targets[target]; - dmfprintk((MYIOC_s_INFO_FMT "qcmd: SCpnt=%p, done()=%p\n", (hd && hd->ioc) ? hd->ioc->name : "ioc?", SCpnt, done)); @@ -1315,7 +1343,7 @@ mptscsih_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *)) /* Default to untagged. Once a target structure has been allocated, * use the Inquiry data to determine if device supports tagged. */ - if ( pTarget + if (pTarget && (pTarget->tflags & MPT_TARGET_FLAGS_Q_YES) && (SCpnt->device->tagged_supported)) { scsictl = scsidir | MPI_SCSIIO_CONTROL_SIMPLEQ; @@ -1325,8 +1353,8 @@ mptscsih_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *)) /* Use the above information to set up the message frame */ - pScsiReq->TargetID = (u8) target; - pScsiReq->Bus = (u8) SCpnt->device->channel; + pScsiReq->TargetID = (u8) pTarget->target_id; + pScsiReq->Bus = pTarget->bus_id; pScsiReq->ChainOffset = 0; pScsiReq->Function = MPI_FUNCTION_SCSI_IO_REQUEST; pScsiReq->CDBLength = SCpnt->cmd_len; @@ -1378,7 +1406,7 @@ mptscsih_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *)) #ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION if (hd->ioc->bus_type == SCSI) { - int dvStatus = hd->ioc->spi_data.dvStatus[target]; + int dvStatus = hd->ioc->spi_data.dvStatus[pTarget->target_id]; int issueCmd = 1; if (dvStatus || hd->ioc->spi_data.forceDv) { @@ -1426,6 +1454,7 @@ mptscsih_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *)) return 0; fail: + hd->ScsiLookup[my_idx] = NULL; mptscsih_freeChainBuffers(hd->ioc, my_idx); mpt_free_msg_frame(hd->ioc, mf); return SCSI_MLQUEUE_HOST_BUSY; @@ -1713,24 +1742,23 @@ mptscsih_abort(struct scsi_cmnd * SCpnt) MPT_FRAME_HDR *mf; u32 ctx2abort; int scpnt_idx; + int retval; /* If we can't locate our host adapter structure, return FAILED status. */ if ((hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata) == NULL) { SCpnt->result = DID_RESET << 16; SCpnt->scsi_done(SCpnt); - dfailprintk((KERN_WARNING MYNAM ": mptscsih_abort: " + dfailprintk((KERN_INFO MYNAM ": mptscsih_abort: " "Can't locate host! (sc=%p)\n", SCpnt)); return FAILED; } ioc = hd->ioc; - if (hd->resetPending) + if (hd->resetPending) { return FAILED; - - printk(KERN_WARNING MYNAM ": %s: >> Attempting task abort! (sc=%p)\n", - hd->ioc->name, SCpnt); + } if (hd->timeouts < -1) hd->timeouts++; @@ -1738,16 +1766,20 @@ mptscsih_abort(struct scsi_cmnd * SCpnt) /* Find this command */ if ((scpnt_idx = SCPNT_TO_LOOKUP_IDX(SCpnt)) < 0) { - /* Cmd not found in ScsiLookup. + /* Cmd not found in ScsiLookup. * Do OS callback. */ SCpnt->result = DID_RESET << 16; - dtmprintk((KERN_WARNING MYNAM ": %s: mptscsih_abort: " + dtmprintk((KERN_INFO MYNAM ": %s: mptscsih_abort: " "Command not in the active list! (sc=%p)\n", hd->ioc->name, SCpnt)); return SUCCESS; } + printk(KERN_WARNING MYNAM ": %s: attempting task abort! (sc=%p)\n", + hd->ioc->name, SCpnt); + scsi_print_command(SCpnt); + /* Most important! Set TaskMsgContext to SCpnt's MsgContext! * (the IO to be ABORT'd) * @@ -1760,38 +1792,22 @@ mptscsih_abort(struct scsi_cmnd * SCpnt) hd->abortSCpnt = SCpnt; - if (mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK, + retval = mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK, SCpnt->device->channel, SCpnt->device->id, SCpnt->device->lun, - ctx2abort, 2 /* 2 second timeout */) - < 0) { + ctx2abort, 2 /* 2 second timeout */); - /* The TM request failed and the subsequent FW-reload failed! - * Fatal error case. - */ - printk(MYIOC_s_WARN_FMT "Error issuing abort task! (sc=%p)\n", - hd->ioc->name, SCpnt); + printk (KERN_WARNING MYNAM ": %s: task abort: %s (sc=%p)\n", + hd->ioc->name, + ((retval == 0) ? "SUCCESS" : "FAILED" ), SCpnt); - /* We must clear our pending flag before clearing our state. - */ + if (retval == 0) + return SUCCESS; + + if(retval != FAILED ) { hd->tmPending = 0; hd->tmState = TM_STATE_NONE; - - /* Unmap the DMA buffers, if any. */ - if (SCpnt->use_sg) { - pci_unmap_sg(ioc->pcidev, (struct scatterlist *) SCpnt->request_buffer, - SCpnt->use_sg, SCpnt->sc_data_direction); - } else if (SCpnt->request_bufflen) { - pci_unmap_single(ioc->pcidev, SCpnt->SCp.dma_handle, - SCpnt->request_bufflen, SCpnt->sc_data_direction); - } - hd->ScsiLookup[scpnt_idx] = NULL; - SCpnt->result = DID_RESET << 16; - SCpnt->scsi_done(SCpnt); /* Issue the command callback */ - mptscsih_freeChainBuffers(ioc, scpnt_idx); - mpt_free_msg_frame(ioc, mf); - return FAILED; } - return SUCCESS; + return FAILED; } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ @@ -1807,11 +1823,12 @@ int mptscsih_dev_reset(struct scsi_cmnd * SCpnt) { MPT_SCSI_HOST *hd; + int retval; /* If we can't locate our host adapter structure, return FAILED status. */ if ((hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata) == NULL){ - dtmprintk((KERN_WARNING MYNAM ": mptscsih_dev_reset: " + dtmprintk((KERN_INFO MYNAM ": mptscsih_dev_reset: " "Can't locate host! (sc=%p)\n", SCpnt)); return FAILED; @@ -1820,24 +1837,26 @@ mptscsih_dev_reset(struct scsi_cmnd * SCpnt) if (hd->resetPending) return FAILED; - printk(KERN_WARNING MYNAM ": %s: >> Attempting target reset! (sc=%p)\n", + printk(KERN_WARNING MYNAM ": %s: attempting target reset! (sc=%p)\n", hd->ioc->name, SCpnt); + scsi_print_command(SCpnt); - if (mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET, + retval = mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET, SCpnt->device->channel, SCpnt->device->id, - 0, 0, 5 /* 5 second timeout */) - < 0){ - /* The TM request failed and the subsequent FW-reload failed! - * Fatal error case. - */ - printk(MYIOC_s_WARN_FMT "Error processing TaskMgmt request (sc=%p)\n", - hd->ioc->name, SCpnt); + 0, 0, 5 /* 5 second timeout */); + + printk (KERN_WARNING MYNAM ": %s: target reset: %s (sc=%p)\n", + hd->ioc->name, + ((retval == 0) ? "SUCCESS" : "FAILED" ), SCpnt); + + if (retval == 0) + return SUCCESS; + + if(retval != FAILED ) { hd->tmPending = 0; hd->tmState = TM_STATE_NONE; - return FAILED; } - - return SUCCESS; + return FAILED; } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ @@ -1853,41 +1872,39 @@ int mptscsih_bus_reset(struct scsi_cmnd * SCpnt) { MPT_SCSI_HOST *hd; - spinlock_t *host_lock = SCpnt->device->host->host_lock; + int retval; /* If we can't locate our host adapter structure, return FAILED status. */ if ((hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata) == NULL){ - dtmprintk((KERN_WARNING MYNAM ": mptscsih_bus_reset: " + dtmprintk((KERN_INFO MYNAM ": mptscsih_bus_reset: " "Can't locate host! (sc=%p)\n", SCpnt ) ); return FAILED; } - printk(KERN_WARNING MYNAM ": %s: >> Attempting bus reset! (sc=%p)\n", + printk(KERN_WARNING MYNAM ": %s: attempting bus reset! (sc=%p)\n", hd->ioc->name, SCpnt); + scsi_print_command(SCpnt); if (hd->timeouts < -1) hd->timeouts++; - /* We are now ready to execute the task management request. */ - if (mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS, - SCpnt->device->channel, 0, 0, 0, 5 /* 5 second timeout */) - < 0){ + retval = mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS, + SCpnt->device->channel, 0, 0, 0, 5 /* 5 second timeout */); - /* The TM request failed and the subsequent FW-reload failed! - * Fatal error case. - */ - printk(MYIOC_s_WARN_FMT - "Error processing TaskMgmt request (sc=%p)\n", - hd->ioc->name, SCpnt); + printk (KERN_WARNING MYNAM ": %s: bus reset: %s (sc=%p)\n", + hd->ioc->name, + ((retval == 0) ? "SUCCESS" : "FAILED" ), SCpnt); + + if (retval == 0) + return SUCCESS; + + if(retval != FAILED ) { hd->tmPending = 0; hd->tmState = TM_STATE_NONE; - spin_lock_irq(host_lock); - return FAILED; } - - return SUCCESS; + return FAILED; } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ @@ -2169,7 +2186,7 @@ mptscsih_slave_alloc(struct scsi_device *device) vdev->raidVolume = 0; hd->Targets[device->id] = vdev; if (hd->ioc->bus_type == SCSI) { - if (hd->ioc->spi_data.isRaid & (1 << device->id)) { + if (hd->ioc->raid_data.isRaid & (1 << device->id)) { vdev->raidVolume = 1; ddvtprintk((KERN_INFO "RAID Volume @ id %d\n", device->id)); @@ -2180,22 +2197,7 @@ mptscsih_slave_alloc(struct scsi_device *device) out: vdev->num_luns++; - return 0; -} - -static int -mptscsih_is_raid_volume(MPT_SCSI_HOST *hd, uint id) -{ - int i; - - if (!hd->ioc->spi_data.isRaid || !hd->ioc->spi_data.pIocPg3) - return 0; - - for (i = 0; i < hd->ioc->spi_data.pIocPg3->NumPhysDisks; i++) { - if (id == hd->ioc->spi_data.pIocPg3->PhysDisk[i].PhysDiskID) - return 1; - } - + device->hostdata = vdev; return 0; } @@ -2226,7 +2228,7 @@ mptscsih_slave_destroy(struct scsi_device *device) hd->Targets[target] = NULL; if (hd->ioc->bus_type == SCSI) { - if (mptscsih_is_raid_volume(hd, target)) { + if (mptscsih_is_phys_disk(hd->ioc, target)) { hd->ioc->spi_data.forceDv |= MPT_SCSICFG_RELOAD_IOC_PG3; } else { hd->ioc->spi_data.dvStatus[target] = @@ -2439,6 +2441,7 @@ mptscsih_ioc_reset(MPT_ADAPTER *ioc, int reset_phase) { MPT_SCSI_HOST *hd; unsigned long flags; + int ii; dtmprintk((KERN_WARNING MYNAM ": IOC %s_reset routed to SCSI host driver!\n", @@ -2496,11 +2499,8 @@ mptscsih_ioc_reset(MPT_ADAPTER *ioc, int reset_phase) /* ScsiLookup initialization */ - { - int ii; - for (ii=0; ii < hd->ioc->req_depth; ii++) - hd->ScsiLookup[ii] = NULL; - } + for (ii=0; ii < hd->ioc->req_depth; ii++) + hd->ScsiLookup[ii] = NULL; /* 2. Chain Buffer initialization */ @@ -2549,6 +2549,16 @@ mptscsih_ioc_reset(MPT_ADAPTER *ioc, int reset_phase) } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* work queue thread to clear the persitency table */ +static void +mptscsih_sas_persist_clear_table(void * arg) +{ + MPT_ADAPTER *ioc = (MPT_ADAPTER *)arg; + + mptbase_sas_persist_operation(ioc, MPI_SAS_OP_CLEAR_NOT_PRESENT); +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ int mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply) { @@ -2558,18 +2568,18 @@ mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply) devtprintk((MYIOC_s_INFO_FMT "MPT event (=%02Xh) routed to SCSI host driver!\n", ioc->name, event)); + if (ioc->sh == NULL || + ((hd = (MPT_SCSI_HOST *)ioc->sh->hostdata) == NULL)) + return 1; + switch (event) { case MPI_EVENT_UNIT_ATTENTION: /* 03 */ /* FIXME! */ break; case MPI_EVENT_IOC_BUS_RESET: /* 04 */ case MPI_EVENT_EXT_BUS_RESET: /* 05 */ - hd = NULL; - if (ioc->sh) { - hd = (MPT_SCSI_HOST *) ioc->sh->hostdata; - if (hd && (ioc->bus_type == SCSI) && (hd->soft_resets < -1)) - hd->soft_resets++; - } + if (hd && (ioc->bus_type == SCSI) && (hd->soft_resets < -1)) + hd->soft_resets++; break; case MPI_EVENT_LOGOUT: /* 09 */ /* FIXME! */ @@ -2588,69 +2598,24 @@ mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply) break; case MPI_EVENT_INTEGRATED_RAID: /* 0B */ + { + pMpiEventDataRaid_t pRaidEventData = + (pMpiEventDataRaid_t) pEvReply->Data; #ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION - /* negoNvram set to 0 if DV enabled and to USE_NVRAM if - * if DV disabled. Need to check for target mode. - */ - hd = NULL; - if (ioc->sh) - hd = (MPT_SCSI_HOST *) ioc->sh->hostdata; - - if (hd && (ioc->bus_type == SCSI) && (hd->negoNvram == 0)) { - ScsiCfgData *pSpi; - Ioc3PhysDisk_t *pPDisk; - int numPDisk; - u8 reason; - u8 physDiskNum; - - reason = (le32_to_cpu(pEvReply->Data[0]) & 0x00FF0000) >> 16; - if (reason == MPI_EVENT_RAID_RC_DOMAIN_VAL_NEEDED) { - /* New or replaced disk. - * Set DV flag and schedule DV. - */ - pSpi = &ioc->spi_data; - physDiskNum = (le32_to_cpu(pEvReply->Data[0]) & 0xFF000000) >> 24; - ddvtprintk(("DV requested for phys disk id %d\n", physDiskNum)); - if (pSpi->pIocPg3) { - pPDisk = pSpi->pIocPg3->PhysDisk; - numPDisk =pSpi->pIocPg3->NumPhysDisks; - - while (numPDisk) { - if (physDiskNum == pPDisk->PhysDiskNum) { - pSpi->dvStatus[pPDisk->PhysDiskID] = (MPT_SCSICFG_NEED_DV | MPT_SCSICFG_DV_NOT_DONE); - pSpi->forceDv = MPT_SCSICFG_NEED_DV; - ddvtprintk(("NEED_DV set for phys disk id %d\n", pPDisk->PhysDiskID)); - break; - } - pPDisk++; - numPDisk--; - } - - if (numPDisk == 0) { - /* The physical disk that needs DV was not found - * in the stored IOC Page 3. The driver must reload - * this page. DV routine will set the NEED_DV flag for - * all phys disks that have DV_NOT_DONE set. - */ - pSpi->forceDv = MPT_SCSICFG_NEED_DV | MPT_SCSICFG_RELOAD_IOC_PG3; - ddvtprintk(("phys disk %d not found. Setting reload IOC Pg3 Flag\n", physDiskNum)); - } - } - } - } + /* Domain Validation Needed */ + if (ioc->bus_type == SCSI && + pRaidEventData->ReasonCode == + MPI_EVENT_RAID_RC_DOMAIN_VAL_NEEDED) + mptscsih_set_dvflags_raid(hd, pRaidEventData->PhysDiskNum); #endif + break; + } -#if defined(MPT_DEBUG_DV) || defined(MPT_DEBUG_DV_TINY) - printk("Raid Event RF: "); - { - u32 *m = (u32 *)pEvReply; - int ii; - int n = (int)pEvReply->MsgLength; - for (ii=6; ii < n; ii++) - printk(" %08x", le32_to_cpu(m[ii])); - printk("\n"); - } -#endif + /* Persistent table is full. */ + case MPI_EVENT_PERSISTENT_TABLE_FULL: + INIT_WORK(&mptscsih_persistTask, + mptscsih_sas_persist_clear_table,(void *)ioc); + schedule_work(&mptscsih_persistTask); break; case MPI_EVENT_NONE: /* 00 */ @@ -2687,7 +2652,7 @@ mptscsih_initTarget(MPT_SCSI_HOST *hd, int bus_id, int target_id, u8 lun, char * { int indexed_lun, lun_index; VirtDevice *vdev; - ScsiCfgData *pSpi; + SpiCfgData *pSpi; char data_56; dinitprintk((MYIOC_s_INFO_FMT "initTarget bus=%d id=%d lun=%d hd=%p\n", @@ -2794,7 +2759,7 @@ mptscsih_initTarget(MPT_SCSI_HOST *hd, int bus_id, int target_id, u8 lun, char * static void mptscsih_setTargetNegoParms(MPT_SCSI_HOST *hd, VirtDevice *target, char byte56) { - ScsiCfgData *pspi_data = &hd->ioc->spi_data; + SpiCfgData *pspi_data = &hd->ioc->spi_data; int id = (int) target->target_id; int nvram; VirtDevice *vdev; @@ -2973,11 +2938,13 @@ mptscsih_setTargetNegoParms(MPT_SCSI_HOST *hd, VirtDevice *target, char byte56) static void mptscsih_set_dvflags(MPT_SCSI_HOST *hd, SCSIIORequest_t *pReq) { + MPT_ADAPTER *ioc = hd->ioc; u8 cmd; - ScsiCfgData *pSpi; + SpiCfgData *pSpi; - ddvtprintk((" set_dvflags: id=%d lun=%d negoNvram=%x cmd=%x\n", - pReq->TargetID, pReq->LUN[1], hd->negoNvram, pReq->CDB[0])); + ddvtprintk((MYIOC_s_NOTE_FMT + " set_dvflags: id=%d lun=%d negoNvram=%x cmd=%x\n", + hd->ioc->name, pReq->TargetID, pReq->LUN[1], hd->negoNvram, pReq->CDB[0])); if ((pReq->LUN[1] != 0) || (hd->negoNvram != 0)) return; @@ -2985,12 +2952,12 @@ mptscsih_set_dvflags(MPT_SCSI_HOST *hd, SCSIIORequest_t *pReq) cmd = pReq->CDB[0]; if ((cmd == READ_CAPACITY) || (cmd == MODE_SENSE)) { - pSpi = &hd->ioc->spi_data; - if ((pSpi->isRaid & (1 << pReq->TargetID)) && pSpi->pIocPg3) { + pSpi = &ioc->spi_data; + if ((ioc->raid_data.isRaid & (1 << pReq->TargetID)) && ioc->raid_data.pIocPg3) { /* Set NEED_DV for all hidden disks */ - Ioc3PhysDisk_t *pPDisk = pSpi->pIocPg3->PhysDisk; - int numPDisk = pSpi->pIocPg3->NumPhysDisks; + Ioc3PhysDisk_t *pPDisk = ioc->raid_data.pIocPg3->PhysDisk; + int numPDisk = ioc->raid_data.pIocPg3->NumPhysDisks; while (numPDisk) { pSpi->dvStatus[pPDisk->PhysDiskID] |= MPT_SCSICFG_NEED_DV; @@ -3004,6 +2971,50 @@ mptscsih_set_dvflags(MPT_SCSI_HOST *hd, SCSIIORequest_t *pReq) } } +/* mptscsih_raid_set_dv_flags() + * + * New or replaced disk. Set DV flag and schedule DV. + */ +static void +mptscsih_set_dvflags_raid(MPT_SCSI_HOST *hd, int id) +{ + MPT_ADAPTER *ioc = hd->ioc; + SpiCfgData *pSpi = &ioc->spi_data; + Ioc3PhysDisk_t *pPDisk; + int numPDisk; + + if (hd->negoNvram != 0) + return; + + ddvtprintk(("DV requested for phys disk id %d\n", id)); + if (ioc->raid_data.pIocPg3) { + pPDisk = ioc->raid_data.pIocPg3->PhysDisk; + numPDisk = ioc->raid_data.pIocPg3->NumPhysDisks; + while (numPDisk) { + if (id == pPDisk->PhysDiskNum) { + pSpi->dvStatus[pPDisk->PhysDiskID] = + (MPT_SCSICFG_NEED_DV | MPT_SCSICFG_DV_NOT_DONE); + pSpi->forceDv = MPT_SCSICFG_NEED_DV; + ddvtprintk(("NEED_DV set for phys disk id %d\n", + pPDisk->PhysDiskID)); + break; + } + pPDisk++; + numPDisk--; + } + + if (numPDisk == 0) { + /* The physical disk that needs DV was not found + * in the stored IOC Page 3. The driver must reload + * this page. DV routine will set the NEED_DV flag for + * all phys disks that have DV_NOT_DONE set. + */ + pSpi->forceDv = MPT_SCSICFG_NEED_DV | MPT_SCSICFG_RELOAD_IOC_PG3; + ddvtprintk(("phys disk %d not found. Setting reload IOC Pg3 Flag\n",id)); + } + } +} + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* * If no Target, bus reset on 1st I/O. Set the flag to @@ -3091,7 +3102,7 @@ mptscsih_writeSDP1(MPT_SCSI_HOST *hd, int portnum, int target_id, int flags) MPT_ADAPTER *ioc = hd->ioc; Config_t *pReq; SCSIDevicePage1_t *pData; - VirtDevice *pTarget; + VirtDevice *pTarget=NULL; MPT_FRAME_HDR *mf; dma_addr_t dataDma; u16 req_idx; @@ -3190,7 +3201,7 @@ mptscsih_writeSDP1(MPT_SCSI_HOST *hd, int portnum, int target_id, int flags) #endif if (flags & MPT_SCSICFG_BLK_NEGO) - negoFlags = MPT_TARGET_NO_NEGO_WIDE | MPT_TARGET_NO_NEGO_SYNC; + negoFlags |= MPT_TARGET_NO_NEGO_WIDE | MPT_TARGET_NO_NEGO_SYNC; mptscsih_setDevicePage1Flags(width, factor, offset, &requested, &configuration, negoFlags); @@ -4011,7 +4022,7 @@ mptscsih_synchronize_cache(MPT_SCSI_HOST *hd, int portnum) /* If target Ptr NULL or if this target is NOT a disk, skip. */ - if ((pTarget) && (pTarget->tflags & MPT_TARGET_FLAGS_Q_YES)){ + if ((pTarget) && (pTarget->inq_data[0] == TYPE_DISK)){ for (lun=0; lun <= MPT_LAST_LUN; lun++) { /* If LUN present, issue the command */ @@ -4106,9 +4117,9 @@ mptscsih_domainValidation(void *arg) if ((ioc->spi_data.forceDv & MPT_SCSICFG_RELOAD_IOC_PG3) != 0) { mpt_read_ioc_pg_3(ioc); - if (ioc->spi_data.pIocPg3) { - Ioc3PhysDisk_t *pPDisk = ioc->spi_data.pIocPg3->PhysDisk; - int numPDisk = ioc->spi_data.pIocPg3->NumPhysDisks; + if (ioc->raid_data.pIocPg3) { + Ioc3PhysDisk_t *pPDisk = ioc->raid_data.pIocPg3->PhysDisk; + int numPDisk = ioc->raid_data.pIocPg3->NumPhysDisks; while (numPDisk) { if (ioc->spi_data.dvStatus[pPDisk->PhysDiskID] & MPT_SCSICFG_DV_NOT_DONE) @@ -4147,7 +4158,7 @@ mptscsih_domainValidation(void *arg) isPhysDisk = mptscsih_is_phys_disk(ioc, id); if (isPhysDisk) { for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++) { - if (hd->ioc->spi_data.isRaid & (1 << ii)) { + if (hd->ioc->raid_data.isRaid & (1 << ii)) { hd->ioc->spi_data.dvStatus[ii] |= MPT_SCSICFG_DV_PENDING; } } @@ -4166,7 +4177,7 @@ mptscsih_domainValidation(void *arg) if (isPhysDisk) { for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++) { - if (hd->ioc->spi_data.isRaid & (1 << ii)) { + if (hd->ioc->raid_data.isRaid & (1 << ii)) { hd->ioc->spi_data.dvStatus[ii] &= ~MPT_SCSICFG_DV_PENDING; } } @@ -4188,21 +4199,21 @@ mptscsih_domainValidation(void *arg) /* Search IOC page 3 to determine if this is hidden physical disk */ -static int +/* Search IOC page 3 to determine if this is hidden physical disk + */ +static int mptscsih_is_phys_disk(MPT_ADAPTER *ioc, int id) { - if (ioc->spi_data.pIocPg3) { - Ioc3PhysDisk_t *pPDisk = ioc->spi_data.pIocPg3->PhysDisk; - int numPDisk = ioc->spi_data.pIocPg3->NumPhysDisks; + int i; - while (numPDisk) { - if (pPDisk->PhysDiskID == id) { - return 1; - } - pPDisk++; - numPDisk--; - } + if (!ioc->raid_data.isRaid || !ioc->raid_data.pIocPg3) + return 0; + + for (i = 0; i < ioc->raid_data.pIocPg3->NumPhysDisks; i++) { + if (id == ioc->raid_data.pIocPg3->PhysDisk[i].PhysDiskID) + return 1; } + return 0; } @@ -4408,7 +4419,7 @@ mptscsih_doDv(MPT_SCSI_HOST *hd, int bus_number, int id) /* Skip this ID? Set cfg.cfghdr.hdr to force config page write */ { - ScsiCfgData *pspi_data = &hd->ioc->spi_data; + SpiCfgData *pspi_data = &hd->ioc->spi_data; if (pspi_data->nvram && (pspi_data->nvram[id] != MPT_HOST_NVRAM_INVALID)) { /* Set the factor from nvram */ nfactor = (pspi_data->nvram[id] & MPT_NVRAM_SYNC_MASK) >> 8; @@ -4438,11 +4449,11 @@ mptscsih_doDv(MPT_SCSI_HOST *hd, int bus_number, int id) } /* Finish iocmd inititialization - hidden or visible disk? */ - if (ioc->spi_data.pIocPg3) { + if (ioc->raid_data.pIocPg3) { /* Search IOC page 3 for matching id */ - Ioc3PhysDisk_t *pPDisk = ioc->spi_data.pIocPg3->PhysDisk; - int numPDisk = ioc->spi_data.pIocPg3->NumPhysDisks; + Ioc3PhysDisk_t *pPDisk = ioc->raid_data.pIocPg3->PhysDisk; + int numPDisk = ioc->raid_data.pIocPg3->NumPhysDisks; while (numPDisk) { if (pPDisk->PhysDiskID == id) { @@ -4466,7 +4477,7 @@ mptscsih_doDv(MPT_SCSI_HOST *hd, int bus_number, int id) /* RAID Volume ID's may double for a physical device. If RAID but * not a physical ID as well, skip DV. */ - if ((hd->ioc->spi_data.isRaid & (1 << id)) && !(iocmd.flags & MPT_ICFLAG_PHYS_DISK)) + if ((hd->ioc->raid_data.isRaid & (1 << id)) && !(iocmd.flags & MPT_ICFLAG_PHYS_DISK)) goto target_done; @@ -4815,6 +4826,8 @@ mptscsih_doDv(MPT_SCSI_HOST *hd, int bus_number, int id) notDone = 0; if (iocmd.flags & MPT_ICFLAG_ECHO) { bufsize = ((pbuf1[2] & 0x1F) <<8) | pbuf1[3]; + if (pbuf1[0] & 0x01) + iocmd.flags |= MPT_ICFLAG_EBOS; } else { bufsize = pbuf1[1]<<16 | pbuf1[2]<<8 | pbuf1[3]; } @@ -4911,6 +4924,9 @@ mptscsih_doDv(MPT_SCSI_HOST *hd, int bus_number, int id) } iocmd.flags &= ~MPT_ICFLAG_DID_RESET; + if (iocmd.flags & MPT_ICFLAG_EBOS) + goto skip_Reserve; + repeat = 5; while (repeat && (!(iocmd.flags & MPT_ICFLAG_RESERVED))) { iocmd.cmd = RESERVE; @@ -4954,6 +4970,7 @@ mptscsih_doDv(MPT_SCSI_HOST *hd, int bus_number, int id) } } +skip_Reserve: mptscsih_fillbuf(pbuf1, sz, patt, 1); iocmd.cmd = WRITE_BUFFER; iocmd.data_dma = buf1_dma; @@ -5198,11 +5215,12 @@ mptscsih_dv_parms(MPT_SCSI_HOST *hd, DVPARAMETERS *dv,void *pPage) * If not an LVD bus, the adapter minSyncFactor has been * already throttled back. */ + negoFlags = hd->ioc->spi_data.noQas; if ((hd->Targets)&&((pTarget = hd->Targets[(int)id]) != NULL) && !pTarget->raidVolume) { width = pTarget->maxWidth; offset = pTarget->maxOffset; factor = pTarget->minSyncFactor; - negoFlags = pTarget->negoFlags; + negoFlags |= pTarget->negoFlags; } else { if (hd->ioc->spi_data.nvram && (hd->ioc->spi_data.nvram[id] != MPT_HOST_NVRAM_INVALID)) { data = hd->ioc->spi_data.nvram[id]; @@ -5223,7 +5241,6 @@ mptscsih_dv_parms(MPT_SCSI_HOST *hd, DVPARAMETERS *dv,void *pPage) } /* Set the negotiation flags */ - negoFlags = hd->ioc->spi_data.noQas; if (!width) negoFlags |= MPT_TARGET_NO_NEGO_WIDE; diff --git a/drivers/message/fusion/mptscsih.h b/drivers/message/fusion/mptscsih.h index 51c0255ac16e..971fda4b8b57 100644 --- a/drivers/message/fusion/mptscsih.h +++ b/drivers/message/fusion/mptscsih.h @@ -1,5 +1,5 @@ /* - * linux/drivers/message/fusion/mptscsi.h + * linux/drivers/message/fusion/mptscsih.h * High performance SCSI / Fibre Channel SCSI Host device driver. * For use with PCI chip/adapter(s): * LSIFC9xx/LSI409xx Fibre Channel @@ -53,8 +53,8 @@ * SCSI Public stuff... */ -#define MPT_SCSI_CMD_PER_DEV_HIGH 31 -#define MPT_SCSI_CMD_PER_DEV_LOW 7 +#define MPT_SCSI_CMD_PER_DEV_HIGH 64 +#define MPT_SCSI_CMD_PER_DEV_LOW 32 #define MPT_SCSI_CMD_PER_LUN 7 @@ -77,6 +77,7 @@ #define MPTSCSIH_MAX_WIDTH 1 #define MPTSCSIH_MIN_SYNC 0x08 #define MPTSCSIH_SAF_TE 0 +#define MPTSCSIH_PT_CLEAR 0 #endif diff --git a/drivers/message/fusion/mptspi.c b/drivers/message/fusion/mptspi.c index 587d1274fd74..5c0e307d1d5d 100644 --- a/drivers/message/fusion/mptspi.c +++ b/drivers/message/fusion/mptspi.c @@ -199,7 +199,7 @@ mptspi_probe(struct pci_dev *pdev, const struct pci_device_id *id) printk(MYIOC_s_WARN_FMT "Skipping ioc=%p because SCSI Initiator mode is NOT enabled!\n", ioc->name, ioc); - return -ENODEV; + return 0; } sh = scsi_host_alloc(&mptspi_driver_template, sizeof(MPT_SCSI_HOST)); diff --git a/drivers/message/i2o/config-osm.c b/drivers/message/i2o/config-osm.c index af32ab4e90cd..10432f665201 100644 --- a/drivers/message/i2o/config-osm.c +++ b/drivers/message/i2o/config-osm.c @@ -56,8 +56,11 @@ static int __init i2o_config_init(void) return -EBUSY; } #ifdef CONFIG_I2O_CONFIG_OLD_IOCTL - if (i2o_config_old_init()) + if (i2o_config_old_init()) { + osm_err("old config handler initialization failed\n"); i2o_driver_unregister(&i2o_config_driver); + return -EBUSY; + } #endif return 0; diff --git a/drivers/mfd/ucb1x00-ts.c b/drivers/mfd/ucb1x00-ts.c index a851d65c7cfe..a260f83bcb02 100644 --- a/drivers/mfd/ucb1x00-ts.c +++ b/drivers/mfd/ucb1x00-ts.c @@ -48,8 +48,8 @@ struct ucb1x00_ts { u16 x_res; u16 y_res; - int restart:1; - int adcsync:1; + unsigned int restart:1; + unsigned int adcsync:1; }; static int adcsync; diff --git a/drivers/mtd/devices/docecc.c b/drivers/mtd/devices/docecc.c index 9a087c1fb0b7..24f670b5a4f3 100644 --- a/drivers/mtd/devices/docecc.c +++ b/drivers/mtd/devices/docecc.c @@ -40,7 +40,7 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/doc2000.h> -#define DEBUG 0 +#define DEBUG_ECC 0 /* need to undef it (from asm/termbits.h) */ #undef B0 @@ -249,7 +249,7 @@ eras_dec_rs(dtype Alpha_to[NN + 1], dtype Index_of[NN + 1], lambda[j] ^= Alpha_to[modnn(u + tmp)]; } } -#if DEBUG >= 1 +#if DEBUG_ECC >= 1 /* Test code that verifies the erasure locator polynomial just constructed Needed only for decoder debugging. */ @@ -276,7 +276,7 @@ eras_dec_rs(dtype Alpha_to[NN + 1], dtype Index_of[NN + 1], count = -1; goto finish; } -#if DEBUG >= 2 +#if DEBUG_ECC >= 2 printf("\n Erasure positions as determined by roots of Eras Loc Poly:\n"); for (i = 0; i < count; i++) printf("%d ", loc[i]); @@ -409,7 +409,7 @@ eras_dec_rs(dtype Alpha_to[NN + 1], dtype Index_of[NN + 1], den ^= Alpha_to[modnn(lambda[i+1] + i * root[j])]; } if (den == 0) { -#if DEBUG >= 1 +#if DEBUG_ECC >= 1 printf("\n ERROR: denominator = 0\n"); #endif /* Convert to dual- basis */ diff --git a/drivers/net/8390.c b/drivers/net/8390.c index 6d76f3a99b17..f87027420081 100644 --- a/drivers/net/8390.c +++ b/drivers/net/8390.c @@ -1094,7 +1094,7 @@ static void NS8390_trigger_send(struct net_device *dev, unsigned int length, outb_p(E8390_NODMA+E8390_PAGE0, e8390_base+E8390_CMD); - if (inb_p(e8390_base) & E8390_TRANS) + if (inb_p(e8390_base + E8390_CMD) & E8390_TRANS) { printk(KERN_WARNING "%s: trigger_send() called with the transmitter busy.\n", dev->name); diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 90449a0f2a6c..6d00c3de1a83 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1653,7 +1653,8 @@ static int bond_enslave(struct net_device *bond_dev, struct net_device *slave_de int old_features = bond_dev->features; int res = 0; - if (slave_dev->do_ioctl == NULL) { + if (!bond->params.use_carrier && slave_dev->ethtool_ops == NULL && + slave_dev->do_ioctl == NULL) { printk(KERN_WARNING DRV_NAME ": Warning : no link monitoring support for %s\n", slave_dev->name); diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c index f0471d102e3c..f9223c1c5aa4 100644 --- a/drivers/net/r8169.c +++ b/drivers/net/r8169.c @@ -100,11 +100,11 @@ VERSION 2.2LK <2005/01/25> #ifdef CONFIG_R8169_NAPI #define rtl8169_rx_skb netif_receive_skb -#define rtl8169_rx_hwaccel_skb vlan_hwaccel_rx +#define rtl8169_rx_hwaccel_skb vlan_hwaccel_receive_skb #define rtl8169_rx_quota(count, quota) min(count, quota) #else #define rtl8169_rx_skb netif_rx -#define rtl8169_rx_hwaccel_skb vlan_hwaccel_receive_skb +#define rtl8169_rx_hwaccel_skb vlan_hwaccel_rx #define rtl8169_rx_quota(count, quota) count #endif diff --git a/drivers/net/skge.c b/drivers/net/skge.c index 0208258e7826..fd398da4993b 100644 --- a/drivers/net/skge.c +++ b/drivers/net/skge.c @@ -42,7 +42,7 @@ #include "skge.h" #define DRV_NAME "skge" -#define DRV_VERSION "1.0" +#define DRV_VERSION "1.1" #define PFX DRV_NAME " " #define DEFAULT_TX_RING_SIZE 128 @@ -105,41 +105,28 @@ static const u32 rxirqmask[] = { IS_R1_F, IS_R2_F }; static const u32 txirqmask[] = { IS_XA1_F, IS_XA2_F }; static const u32 portirqmask[] = { IS_PORT_1, IS_PORT_2 }; -/* Don't need to look at whole 16K. - * last interesting register is descriptor poll timer. - */ -#define SKGE_REGS_LEN (29*128) - static int skge_get_regs_len(struct net_device *dev) { - return SKGE_REGS_LEN; + return 0x4000; } /* - * Returns copy of control register region - * I/O region is divided into banks and certain regions are unreadable + * Returns copy of whole control register region + * Note: skip RAM address register because accessing it will + * cause bus hangs! */ static void skge_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p) { const struct skge_port *skge = netdev_priv(dev); - unsigned long offs; const void __iomem *io = skge->hw->regs; - static const unsigned long bankmap - = (1<<0) | (1<<2) | (1<<8) | (1<<9) - | (1<<12) | (1<<13) | (1<<14) | (1<<15) | (1<<16) - | (1<<17) | (1<<20) | (1<<21) | (1<<22) | (1<<23) - | (1<<24) | (1<<25) | (1<<26) | (1<<27) | (1<<28); regs->version = 1; - for (offs = 0; offs < regs->len; offs += 128) { - u32 len = min_t(u32, 128, regs->len - offs); + memset(p, 0, regs->len); + memcpy_fromio(p, io, B3_RAM_ADDR); - if (bankmap & (1<<(offs/128))) - memcpy_fromio(p + offs, io + offs, len); - else - memset(p + offs, 0, len); - } + memcpy_fromio(p + B3_RI_WTO_R1, io + B3_RI_WTO_R1, + regs->len - B3_RI_WTO_R1); } /* Wake on Lan only supported on Yukon chps with rev 1 or above */ @@ -775,17 +762,6 @@ static int skge_ring_alloc(struct skge_ring *ring, void *vaddr, u64 base) return 0; } -static struct sk_buff *skge_rx_alloc(struct net_device *dev, unsigned int size) -{ - struct sk_buff *skb = dev_alloc_skb(size); - - if (likely(skb)) { - skb->dev = dev; - skb_reserve(skb, NET_IP_ALIGN); - } - return skb; -} - /* Allocate and setup a new buffer for receiving */ static void skge_rx_setup(struct skge_port *skge, struct skge_element *e, struct sk_buff *skb, unsigned int bufsize) @@ -858,16 +834,17 @@ static int skge_rx_fill(struct skge_port *skge) { struct skge_ring *ring = &skge->rx_ring; struct skge_element *e; - unsigned int bufsize = skge->rx_buf_size; e = ring->start; do { - struct sk_buff *skb = skge_rx_alloc(skge->netdev, bufsize); + struct sk_buff *skb; + skb = dev_alloc_skb(skge->rx_buf_size + NET_IP_ALIGN); if (!skb) return -ENOMEM; - skge_rx_setup(skge, e, skb, bufsize); + skb_reserve(skb, NET_IP_ALIGN); + skge_rx_setup(skge, e, skb, skge->rx_buf_size); } while ( (e = e->next) != ring->start); ring->to_clean = ring->start; @@ -1666,6 +1643,22 @@ static void yukon_reset(struct skge_hw *hw, int port) | GM_RXCR_UCF_ENA | GM_RXCR_MCF_ENA); } +/* Apparently, early versions of Yukon-Lite had wrong chip_id? */ +static int is_yukon_lite_a0(struct skge_hw *hw) +{ + u32 reg; + int ret; + + if (hw->chip_id != CHIP_ID_YUKON) + return 0; + + reg = skge_read32(hw, B2_FAR); + skge_write8(hw, B2_FAR + 3, 0xff); + ret = (skge_read8(hw, B2_FAR + 3) != 0); + skge_write32(hw, B2_FAR, reg); + return ret; +} + static void yukon_mac_init(struct skge_hw *hw, int port) { struct skge_port *skge = netdev_priv(hw->dev[port]); @@ -1781,9 +1774,11 @@ static void yukon_mac_init(struct skge_hw *hw, int port) /* Configure Rx MAC FIFO */ skge_write16(hw, SK_REG(port, RX_GMF_FL_MSK), RX_FF_FL_DEF_MSK); reg = GMF_OPER_ON | GMF_RX_F_FL_ON; - if (hw->chip_id == CHIP_ID_YUKON_LITE && - hw->chip_rev >= CHIP_REV_YU_LITE_A3) + + /* disable Rx GMAC FIFO Flush for YUKON-Lite Rev. A0 only */ + if (is_yukon_lite_a0(hw)) reg &= ~GMF_RX_F_FL_ON; + skge_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RST_CLR); skge_write16(hw, SK_REG(port, RX_GMF_CTRL_T), reg); /* @@ -2442,6 +2437,14 @@ static void yukon_set_multicast(struct net_device *dev) gma_write16(hw, port, GM_RX_CTRL, reg); } +static inline u16 phy_length(const struct skge_hw *hw, u32 status) +{ + if (hw->chip_id == CHIP_ID_GENESIS) + return status >> XMR_FS_LEN_SHIFT; + else + return status >> GMR_FS_LEN_SHIFT; +} + static inline int bad_phy_status(const struct skge_hw *hw, u32 status) { if (hw->chip_id == CHIP_ID_GENESIS) @@ -2451,80 +2454,99 @@ static inline int bad_phy_status(const struct skge_hw *hw, u32 status) (status & GMR_FS_RX_OK) == 0; } -static void skge_rx_error(struct skge_port *skge, int slot, - u32 control, u32 status) -{ - if (netif_msg_rx_err(skge)) - printk(KERN_DEBUG PFX "%s: rx err, slot %d control 0x%x status 0x%x\n", - skge->netdev->name, slot, control, status); - - if ((control & (BMU_EOF|BMU_STF)) != (BMU_STF|BMU_EOF)) - skge->net_stats.rx_length_errors++; - else if (skge->hw->chip_id == CHIP_ID_GENESIS) { - if (status & (XMR_FS_RUNT|XMR_FS_LNG_ERR)) - skge->net_stats.rx_length_errors++; - if (status & XMR_FS_FRA_ERR) - skge->net_stats.rx_frame_errors++; - if (status & XMR_FS_FCS_ERR) - skge->net_stats.rx_crc_errors++; - } else { - if (status & (GMR_FS_LONG_ERR|GMR_FS_UN_SIZE)) - skge->net_stats.rx_length_errors++; - if (status & GMR_FS_FRAGMENT) - skge->net_stats.rx_frame_errors++; - if (status & GMR_FS_CRC_ERR) - skge->net_stats.rx_crc_errors++; - } -} /* Get receive buffer from descriptor. * Handles copy of small buffers and reallocation failures */ static inline struct sk_buff *skge_rx_get(struct skge_port *skge, struct skge_element *e, - unsigned int len) + u32 control, u32 status, u16 csum) { - struct sk_buff *nskb, *skb; + struct sk_buff *skb; + u16 len = control & BMU_BBC; + + if (unlikely(netif_msg_rx_status(skge))) + printk(KERN_DEBUG PFX "%s: rx slot %td status 0x%x len %d\n", + skge->netdev->name, e - skge->rx_ring.start, + status, len); + + if (len > skge->rx_buf_size) + goto error; + + if ((control & (BMU_EOF|BMU_STF)) != (BMU_STF|BMU_EOF)) + goto error; + + if (bad_phy_status(skge->hw, status)) + goto error; + + if (phy_length(skge->hw, status) != len) + goto error; if (len < RX_COPY_THRESHOLD) { - nskb = skge_rx_alloc(skge->netdev, len + NET_IP_ALIGN); - if (unlikely(!nskb)) - return NULL; + skb = dev_alloc_skb(len + 2); + if (!skb) + goto resubmit; + skb_reserve(skb, 2); pci_dma_sync_single_for_cpu(skge->hw->pdev, pci_unmap_addr(e, mapaddr), len, PCI_DMA_FROMDEVICE); - memcpy(nskb->data, e->skb->data, len); + memcpy(skb->data, e->skb->data, len); pci_dma_sync_single_for_device(skge->hw->pdev, pci_unmap_addr(e, mapaddr), len, PCI_DMA_FROMDEVICE); - - if (skge->rx_csum) { - struct skge_rx_desc *rd = e->desc; - nskb->csum = le16_to_cpu(rd->csum2); - nskb->ip_summed = CHECKSUM_HW; - } skge_rx_reuse(e, skge->rx_buf_size); - return nskb; } else { - nskb = skge_rx_alloc(skge->netdev, skge->rx_buf_size); - if (unlikely(!nskb)) - return NULL; + struct sk_buff *nskb; + nskb = dev_alloc_skb(skge->rx_buf_size + NET_IP_ALIGN); + if (!nskb) + goto resubmit; pci_unmap_single(skge->hw->pdev, pci_unmap_addr(e, mapaddr), pci_unmap_len(e, maplen), PCI_DMA_FROMDEVICE); skb = e->skb; - if (skge->rx_csum) { - struct skge_rx_desc *rd = e->desc; - skb->csum = le16_to_cpu(rd->csum2); - skb->ip_summed = CHECKSUM_HW; - } - + prefetch(skb->data); skge_rx_setup(skge, e, nskb, skge->rx_buf_size); - return skb; } + + skb_put(skb, len); + skb->dev = skge->netdev; + if (skge->rx_csum) { + skb->csum = csum; + skb->ip_summed = CHECKSUM_HW; + } + + skb->protocol = eth_type_trans(skb, skge->netdev); + + return skb; +error: + + if (netif_msg_rx_err(skge)) + printk(KERN_DEBUG PFX "%s: rx err, slot %td control 0x%x status 0x%x\n", + skge->netdev->name, e - skge->rx_ring.start, + control, status); + + if (skge->hw->chip_id == CHIP_ID_GENESIS) { + if (status & (XMR_FS_RUNT|XMR_FS_LNG_ERR)) + skge->net_stats.rx_length_errors++; + if (status & XMR_FS_FRA_ERR) + skge->net_stats.rx_frame_errors++; + if (status & XMR_FS_FCS_ERR) + skge->net_stats.rx_crc_errors++; + } else { + if (status & (GMR_FS_LONG_ERR|GMR_FS_UN_SIZE)) + skge->net_stats.rx_length_errors++; + if (status & GMR_FS_FRAGMENT) + skge->net_stats.rx_frame_errors++; + if (status & GMR_FS_CRC_ERR) + skge->net_stats.rx_crc_errors++; + } + +resubmit: + skge_rx_reuse(e, skge->rx_buf_size); + return NULL; } @@ -2540,32 +2562,16 @@ static int skge_poll(struct net_device *dev, int *budget) for (e = ring->to_clean; work_done < to_do; e = e->next) { struct skge_rx_desc *rd = e->desc; struct sk_buff *skb; - u32 control, len, status; + u32 control; rmb(); control = rd->control; if (control & BMU_OWN) break; - len = control & BMU_BBC; - status = rd->status; - - if (unlikely((control & (BMU_EOF|BMU_STF)) != (BMU_STF|BMU_EOF) - || bad_phy_status(hw, status))) { - skge_rx_error(skge, e - ring->start, control, status); - skge_rx_reuse(e, skge->rx_buf_size); - continue; - } - - if (netif_msg_rx_status(skge)) - printk(KERN_DEBUG PFX "%s: rx slot %td status 0x%x len %d\n", - dev->name, e - ring->start, rd->status, len); - - skb = skge_rx_get(skge, e, len); + skb = skge_rx_get(skge, e, control, rd->status, + le16_to_cpu(rd->csum2)); if (likely(skb)) { - skb_put(skb, len); - skb->protocol = eth_type_trans(skb, dev); - dev->last_rx = jiffies; netif_receive_skb(skb); diff --git a/drivers/net/skge.h b/drivers/net/skge.h index efbf98c675d2..72c175b87a5a 100644 --- a/drivers/net/skge.h +++ b/drivers/net/skge.h @@ -953,6 +953,7 @@ enum { */ enum { XMR_FS_LEN = 0x3fff<<18, /* Bit 31..18: Rx Frame Length */ + XMR_FS_LEN_SHIFT = 18, XMR_FS_2L_VLAN = 1<<17, /* Bit 17: tagged wh 2Lev VLAN ID*/ XMR_FS_1_VLAN = 1<<16, /* Bit 16: tagged wh 1ev VLAN ID*/ XMR_FS_BC = 1<<15, /* Bit 15: Broadcast Frame */ @@ -1868,6 +1869,7 @@ enum { /* Receive Frame Status Encoding */ enum { GMR_FS_LEN = 0xffff<<16, /* Bit 31..16: Rx Frame Length */ + GMR_FS_LEN_SHIFT = 16, GMR_FS_VLAN = 1<<13, /* Bit 13: VLAN Packet */ GMR_FS_JABBER = 1<<12, /* Bit 12: Jabber Packet */ GMR_FS_UN_SIZE = 1<<11, /* Bit 11: Undersize Packet */ diff --git a/drivers/net/wan/hdlc_cisco.c b/drivers/net/wan/hdlc_cisco.c index 48c03c11cd9a..a01efa6d5c62 100644 --- a/drivers/net/wan/hdlc_cisco.c +++ b/drivers/net/wan/hdlc_cisco.c @@ -72,7 +72,7 @@ static void cisco_keepalive_send(struct net_device *dev, u32 type, } skb_reserve(skb, 4); cisco_hard_header(skb, dev, CISCO_KEEPALIVE, NULL, NULL, 0); - data = (cisco_packet*)skb->data; + data = (cisco_packet*)(skb->data + 4); data->type = htonl(type); data->par1 = htonl(par1); diff --git a/drivers/pci/hotplug.c b/drivers/pci/hotplug.c index 10444988a10b..e1743be31909 100644 --- a/drivers/pci/hotplug.c +++ b/drivers/pci/hotplug.c @@ -7,7 +7,6 @@ int pci_hotplug (struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size) { struct pci_dev *pdev; - char *scratch; int i = 0; int length = 0; @@ -18,9 +17,6 @@ int pci_hotplug (struct device *dev, char **envp, int num_envp, if (!pdev) return -ENODEV; - scratch = buffer; - - if (add_hotplug_env_var(envp, num_envp, &i, buffer, buffer_size, &length, "PCI_CLASS=%04X", pdev->class)) diff --git a/drivers/pci/hotplug/rpadlpar_sysfs.c b/drivers/pci/hotplug/rpadlpar_sysfs.c index 752e6513c447..db69be85b458 100644 --- a/drivers/pci/hotplug/rpadlpar_sysfs.c +++ b/drivers/pci/hotplug/rpadlpar_sysfs.c @@ -62,7 +62,7 @@ static ssize_t add_slot_store(struct dlpar_io_attr *dlpar_attr, char drc_name[MAX_DRC_NAME_LEN]; char *end; - if (nbytes > MAX_DRC_NAME_LEN) + if (nbytes >= MAX_DRC_NAME_LEN) return 0; memcpy(drc_name, buf, nbytes); @@ -83,7 +83,7 @@ static ssize_t remove_slot_store(struct dlpar_io_attr *dlpar_attr, char drc_name[MAX_DRC_NAME_LEN]; char *end; - if (nbytes > MAX_DRC_NAME_LEN) + if (nbytes >= MAX_DRC_NAME_LEN) return 0; memcpy(drc_name, buf, nbytes); diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c index b1409441c1cd..a32ae82e5922 100644 --- a/drivers/pci/hotplug/sgi_hotplug.c +++ b/drivers/pci/hotplug/sgi_hotplug.c @@ -159,7 +159,7 @@ static int sn_hp_slot_private_alloc(struct hotplug_slot *bss_hotplug_slot, pcibus_info = SN_PCIBUS_BUSSOFT_INFO(pci_bus); - slot = kcalloc(1, sizeof(*slot), GFP_KERNEL); + slot = kzalloc(sizeof(*slot), GFP_KERNEL); if (!slot) return -ENOMEM; bss_hotplug_slot->private = slot; @@ -491,7 +491,7 @@ static int sn_hotplug_slot_register(struct pci_bus *pci_bus) if (sn_pci_slot_valid(pci_bus, device) != 1) continue; - bss_hotplug_slot = kcalloc(1, sizeof(*bss_hotplug_slot), + bss_hotplug_slot = kzalloc(sizeof(*bss_hotplug_slot), GFP_KERNEL); if (!bss_hotplug_slot) { rc = -ENOMEM; @@ -499,7 +499,7 @@ static int sn_hotplug_slot_register(struct pci_bus *pci_bus) } bss_hotplug_slot->info = - kcalloc(1, sizeof(struct hotplug_slot_info), + kzalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL); if (!bss_hotplug_slot->info) { rc = -ENOMEM; diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 56a3b397efee..2898830c496f 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -360,7 +360,7 @@ pci_create_resource_files(struct pci_dev *pdev) continue; /* allocate attribute structure, piggyback attribute name */ - res_attr = kcalloc(1, sizeof(*res_attr) + 10, GFP_ATOMIC); + res_attr = kzalloc(sizeof(*res_attr) + 10, GFP_ATOMIC); if (res_attr) { char *res_attr_name = (char *)(res_attr + 1); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 26a55d08b506..005786416bb5 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -165,7 +165,7 @@ static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom) if (l == 0xffffffff) l = 0; if ((l & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY) { - sz = pci_size(l, sz, PCI_BASE_ADDRESS_MEM_MASK); + sz = pci_size(l, sz, (u32)PCI_BASE_ADDRESS_MEM_MASK); if (!sz) continue; res->start = l & PCI_BASE_ADDRESS_MEM_MASK; @@ -215,7 +215,7 @@ static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom) if (l == 0xffffffff) l = 0; if (sz && sz != 0xffffffff) { - sz = pci_size(l, sz, PCI_ROM_ADDRESS_MASK); + sz = pci_size(l, sz, (u32)PCI_ROM_ADDRESS_MASK); if (sz) { res->flags = (l & IORESOURCE_ROM_ENABLE) | IORESOURCE_MEM | IORESOURCE_PREFETCH | @@ -402,6 +402,12 @@ static void pci_enable_crs(struct pci_dev *dev) static void __devinit pci_fixup_parent_subordinate_busnr(struct pci_bus *child, int max) { struct pci_bus *parent = child->parent; + + /* Attempts to fix that up are really dangerous unless + we're going to re-assign all bus numbers. */ + if (!pcibios_assign_all_busses()) + return; + while (parent->parent && parent->subordinate < max) { parent->subordinate = max; pci_write_config_byte(parent->self, PCI_SUBORDINATE_BUS, max); @@ -478,8 +484,18 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev * dev, int max * We need to assign a number to this bus which we always * do in the second pass. */ - if (!pass) + if (!pass) { + if (pcibios_assign_all_busses()) + /* Temporarily disable forwarding of the + configuration cycles on all bridges in + this bus segment to avoid possible + conflicts in the second pass between two + bridges programmed with overlapping + bus ranges. */ + pci_write_config_dword(dev, PCI_PRIMARY_BUS, + buses & ~0xffffff); return max; + } /* Clear errors */ pci_write_config_word(dev, PCI_STATUS, 0xffff); diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c index 91ea8e4777f3..dbb3eb0e330b 100644 --- a/drivers/s390/cio/ccwgroup.c +++ b/drivers/s390/cio/ccwgroup.c @@ -437,7 +437,7 @@ __ccwgroup_get_gdev_by_cdev(struct ccw_device *cdev) if (cdev->dev.driver_data) { gdev = (struct ccwgroup_device *)cdev->dev.driver_data; if (get_device(&gdev->dev)) { - if (klist_node_attached(&gdev->dev.knode_bus)) + if (device_is_registered(&gdev->dev)) return gdev; put_device(&gdev->dev); } diff --git a/drivers/s390/scsi/Makefile b/drivers/s390/scsi/Makefile index fc145307a7d4..d6a78f1a2f16 100644 --- a/drivers/s390/scsi/Makefile +++ b/drivers/s390/scsi/Makefile @@ -3,7 +3,7 @@ # zfcp-objs := zfcp_aux.o zfcp_ccw.o zfcp_scsi.o zfcp_erp.o zfcp_qdio.o \ - zfcp_fsf.o zfcp_sysfs_adapter.o zfcp_sysfs_port.o \ + zfcp_fsf.o zfcp_dbf.o zfcp_sysfs_adapter.o zfcp_sysfs_port.o \ zfcp_sysfs_unit.o zfcp_sysfs_driver.o obj-$(CONFIG_ZFCP) += zfcp.o diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index bfe3ba73bc0f..0b5087f7cabc 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -122,95 +122,6 @@ _zfcp_hex_dump(char *addr, int count) #define ZFCP_LOG_AREA ZFCP_LOG_AREA_OTHER -static inline int -zfcp_fsf_req_is_scsi_cmnd(struct zfcp_fsf_req *fsf_req) -{ - return ((fsf_req->fsf_command == FSF_QTCB_FCP_CMND) && - !(fsf_req->status & ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT)); -} - -void -zfcp_cmd_dbf_event_fsf(const char *text, struct zfcp_fsf_req *fsf_req, - void *add_data, int add_length) -{ - struct zfcp_adapter *adapter = fsf_req->adapter; - struct scsi_cmnd *scsi_cmnd; - int level = 3; - int i; - unsigned long flags; - - spin_lock_irqsave(&adapter->dbf_lock, flags); - if (zfcp_fsf_req_is_scsi_cmnd(fsf_req)) { - scsi_cmnd = fsf_req->data.send_fcp_command_task.scsi_cmnd; - debug_text_event(adapter->cmd_dbf, level, "fsferror"); - debug_text_event(adapter->cmd_dbf, level, text); - debug_event(adapter->cmd_dbf, level, &fsf_req, - sizeof (unsigned long)); - debug_event(adapter->cmd_dbf, level, &fsf_req->seq_no, - sizeof (u32)); - debug_event(adapter->cmd_dbf, level, &scsi_cmnd, - sizeof (unsigned long)); - debug_event(adapter->cmd_dbf, level, &scsi_cmnd->cmnd, - min(ZFCP_CMD_DBF_LENGTH, (int)scsi_cmnd->cmd_len)); - for (i = 0; i < add_length; i += ZFCP_CMD_DBF_LENGTH) - debug_event(adapter->cmd_dbf, - level, - (char *) add_data + i, - min(ZFCP_CMD_DBF_LENGTH, add_length - i)); - } - spin_unlock_irqrestore(&adapter->dbf_lock, flags); -} - -/* XXX additionally log unit if available */ -/* ---> introduce new parameter for unit, see 2.4 code */ -void -zfcp_cmd_dbf_event_scsi(const char *text, struct scsi_cmnd *scsi_cmnd) -{ - struct zfcp_adapter *adapter; - union zfcp_req_data *req_data; - struct zfcp_fsf_req *fsf_req; - int level = ((host_byte(scsi_cmnd->result) != 0) ? 1 : 5); - unsigned long flags; - - adapter = (struct zfcp_adapter *) scsi_cmnd->device->host->hostdata[0]; - req_data = (union zfcp_req_data *) scsi_cmnd->host_scribble; - fsf_req = (req_data ? req_data->send_fcp_command_task.fsf_req : NULL); - spin_lock_irqsave(&adapter->dbf_lock, flags); - debug_text_event(adapter->cmd_dbf, level, "hostbyte"); - debug_text_event(adapter->cmd_dbf, level, text); - debug_event(adapter->cmd_dbf, level, &scsi_cmnd->result, sizeof (u32)); - debug_event(adapter->cmd_dbf, level, &scsi_cmnd, - sizeof (unsigned long)); - debug_event(adapter->cmd_dbf, level, &scsi_cmnd->cmnd, - min(ZFCP_CMD_DBF_LENGTH, (int)scsi_cmnd->cmd_len)); - if (likely(fsf_req)) { - debug_event(adapter->cmd_dbf, level, &fsf_req, - sizeof (unsigned long)); - debug_event(adapter->cmd_dbf, level, &fsf_req->seq_no, - sizeof (u32)); - } else { - debug_text_event(adapter->cmd_dbf, level, ""); - debug_text_event(adapter->cmd_dbf, level, ""); - } - spin_unlock_irqrestore(&adapter->dbf_lock, flags); -} - -void -zfcp_in_els_dbf_event(struct zfcp_adapter *adapter, const char *text, - struct fsf_status_read_buffer *status_buffer, int length) -{ - int level = 1; - int i; - - debug_text_event(adapter->in_els_dbf, level, text); - debug_event(adapter->in_els_dbf, level, &status_buffer->d_id, 8); - for (i = 0; i < length; i += ZFCP_IN_ELS_DBF_LENGTH) - debug_event(adapter->in_els_dbf, - level, - (char *) status_buffer->payload + i, - min(ZFCP_IN_ELS_DBF_LENGTH, length - i)); -} - /** * zfcp_device_setup - setup function * @str: pointer to parameter string @@ -1017,81 +928,6 @@ zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter) mempool_destroy(adapter->pool.data_gid_pn); } -/** - * zfcp_adapter_debug_register - registers debug feature for an adapter - * @adapter: pointer to adapter for which debug features should be registered - * return: -ENOMEM on error, 0 otherwise - */ -int -zfcp_adapter_debug_register(struct zfcp_adapter *adapter) -{ - char dbf_name[20]; - - /* debug feature area which records SCSI command failures (hostbyte) */ - spin_lock_init(&adapter->dbf_lock); - - sprintf(dbf_name, ZFCP_CMD_DBF_NAME "%s", - zfcp_get_busid_by_adapter(adapter)); - adapter->cmd_dbf = debug_register(dbf_name, ZFCP_CMD_DBF_INDEX, - ZFCP_CMD_DBF_AREAS, - ZFCP_CMD_DBF_LENGTH); - debug_register_view(adapter->cmd_dbf, &debug_hex_ascii_view); - debug_set_level(adapter->cmd_dbf, ZFCP_CMD_DBF_LEVEL); - - /* debug feature area which records SCSI command aborts */ - sprintf(dbf_name, ZFCP_ABORT_DBF_NAME "%s", - zfcp_get_busid_by_adapter(adapter)); - adapter->abort_dbf = debug_register(dbf_name, ZFCP_ABORT_DBF_INDEX, - ZFCP_ABORT_DBF_AREAS, - ZFCP_ABORT_DBF_LENGTH); - debug_register_view(adapter->abort_dbf, &debug_hex_ascii_view); - debug_set_level(adapter->abort_dbf, ZFCP_ABORT_DBF_LEVEL); - - /* debug feature area which records incoming ELS commands */ - sprintf(dbf_name, ZFCP_IN_ELS_DBF_NAME "%s", - zfcp_get_busid_by_adapter(adapter)); - adapter->in_els_dbf = debug_register(dbf_name, ZFCP_IN_ELS_DBF_INDEX, - ZFCP_IN_ELS_DBF_AREAS, - ZFCP_IN_ELS_DBF_LENGTH); - debug_register_view(adapter->in_els_dbf, &debug_hex_ascii_view); - debug_set_level(adapter->in_els_dbf, ZFCP_IN_ELS_DBF_LEVEL); - - /* debug feature area which records erp events */ - sprintf(dbf_name, ZFCP_ERP_DBF_NAME "%s", - zfcp_get_busid_by_adapter(adapter)); - adapter->erp_dbf = debug_register(dbf_name, ZFCP_ERP_DBF_INDEX, - ZFCP_ERP_DBF_AREAS, - ZFCP_ERP_DBF_LENGTH); - debug_register_view(adapter->erp_dbf, &debug_hex_ascii_view); - debug_set_level(adapter->erp_dbf, ZFCP_ERP_DBF_LEVEL); - - if (!(adapter->cmd_dbf && adapter->abort_dbf && - adapter->in_els_dbf && adapter->erp_dbf)) { - zfcp_adapter_debug_unregister(adapter); - return -ENOMEM; - } - - return 0; - -} - -/** - * zfcp_adapter_debug_unregister - unregisters debug feature for an adapter - * @adapter: pointer to adapter for which debug features should be unregistered - */ -void -zfcp_adapter_debug_unregister(struct zfcp_adapter *adapter) -{ - debug_unregister(adapter->abort_dbf); - debug_unregister(adapter->cmd_dbf); - debug_unregister(adapter->erp_dbf); - debug_unregister(adapter->in_els_dbf); - adapter->abort_dbf = NULL; - adapter->cmd_dbf = NULL; - adapter->erp_dbf = NULL; - adapter->in_els_dbf = NULL; -} - void zfcp_dummy_release(struct device *dev) { @@ -1462,10 +1298,6 @@ zfcp_fsf_incoming_els_rscn(struct zfcp_adapter *adapter, /* see FC-FS */ no_entries = (fcp_rscn_head->payload_len / 4); - zfcp_in_els_dbf_event(adapter, "##rscn", status_buffer, - fcp_rscn_head->payload_len); - - debug_text_event(adapter->erp_dbf, 1, "unsol_els_rscn:"); for (i = 1; i < no_entries; i++) { /* skip head and start with 1st element */ fcp_rscn_element++; @@ -1497,8 +1329,6 @@ zfcp_fsf_incoming_els_rscn(struct zfcp_adapter *adapter, (ZFCP_STATUS_PORT_DID_DID, &port->status)) { ZFCP_LOG_INFO("incoming RSCN, trying to open " "port 0x%016Lx\n", port->wwpn); - debug_text_event(adapter->erp_dbf, 1, - "unsol_els_rscnu:"); zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED); continue; @@ -1524,8 +1354,6 @@ zfcp_fsf_incoming_els_rscn(struct zfcp_adapter *adapter, */ ZFCP_LOG_INFO("incoming RSCN, trying to open " "port 0x%016Lx\n", port->wwpn); - debug_text_event(adapter->erp_dbf, 1, - "unsol_els_rscnk:"); zfcp_test_link(port); } } @@ -1541,8 +1369,6 @@ zfcp_fsf_incoming_els_plogi(struct zfcp_adapter *adapter, struct zfcp_port *port; unsigned long flags; - zfcp_in_els_dbf_event(adapter, "##plogi", status_buffer, 28); - read_lock_irqsave(&zfcp_data.config_lock, flags); list_for_each_entry(port, &adapter->port_list_head, list) { if (port->wwpn == (*(wwn_t *) & els_logi->nport_wwn)) @@ -1556,8 +1382,6 @@ zfcp_fsf_incoming_els_plogi(struct zfcp_adapter *adapter, status_buffer->d_id, zfcp_get_busid_by_adapter(adapter)); } else { - debug_text_event(adapter->erp_dbf, 1, "unsol_els_plogi:"); - debug_event(adapter->erp_dbf, 1, &els_logi->nport_wwn, 8); zfcp_erp_port_forced_reopen(port, 0); } } @@ -1570,8 +1394,6 @@ zfcp_fsf_incoming_els_logo(struct zfcp_adapter *adapter, struct zfcp_port *port; unsigned long flags; - zfcp_in_els_dbf_event(adapter, "##logo", status_buffer, 16); - read_lock_irqsave(&zfcp_data.config_lock, flags); list_for_each_entry(port, &adapter->port_list_head, list) { if (port->wwpn == els_logo->nport_wwpn) @@ -1585,8 +1407,6 @@ zfcp_fsf_incoming_els_logo(struct zfcp_adapter *adapter, status_buffer->d_id, zfcp_get_busid_by_adapter(adapter)); } else { - debug_text_event(adapter->erp_dbf, 1, "unsol_els_logo:"); - debug_event(adapter->erp_dbf, 1, &els_logo->nport_wwpn, 8); zfcp_erp_port_forced_reopen(port, 0); } } @@ -1595,7 +1415,6 @@ static void zfcp_fsf_incoming_els_unknown(struct zfcp_adapter *adapter, struct fsf_status_read_buffer *status_buffer) { - zfcp_in_els_dbf_event(adapter, "##undef", status_buffer, 24); ZFCP_LOG_NORMAL("warning: unknown incoming ELS 0x%08x " "for adapter %s\n", *(u32 *) (status_buffer->payload), zfcp_get_busid_by_adapter(adapter)); @@ -1609,10 +1428,11 @@ zfcp_fsf_incoming_els(struct zfcp_fsf_req *fsf_req) u32 els_type; struct zfcp_adapter *adapter; - status_buffer = fsf_req->data.status_read.buffer; + status_buffer = (struct fsf_status_read_buffer *) fsf_req->data; els_type = *(u32 *) (status_buffer->payload); adapter = fsf_req->adapter; + zfcp_san_dbf_event_incoming_els(fsf_req); if (els_type == LS_PLOGI) zfcp_fsf_incoming_els_plogi(adapter, status_buffer); else if (els_type == LS_LOGO) diff --git a/drivers/s390/scsi/zfcp_ccw.c b/drivers/s390/scsi/zfcp_ccw.c index b30abab77da3..0fc46381fc22 100644 --- a/drivers/s390/scsi/zfcp_ccw.c +++ b/drivers/s390/scsi/zfcp_ccw.c @@ -202,19 +202,9 @@ static int zfcp_ccw_set_offline(struct ccw_device *ccw_device) { struct zfcp_adapter *adapter; - struct zfcp_port *port; - struct fc_rport *rport; down(&zfcp_data.config_sema); adapter = dev_get_drvdata(&ccw_device->dev); - /* might be racy, but we cannot take config_lock due to the fact that - fc_remote_port_delete might sleep */ - list_for_each_entry(port, &adapter->port_list_head, list) - if (port->rport) { - rport = port->rport; - port->rport = NULL; - fc_remote_port_delete(rport); - } zfcp_erp_adapter_shutdown(adapter, 0); zfcp_erp_wait(adapter); zfcp_adapter_scsi_unregister(adapter); diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c new file mode 100644 index 000000000000..826fb3b00605 --- /dev/null +++ b/drivers/s390/scsi/zfcp_dbf.c @@ -0,0 +1,995 @@ +/* + * + * linux/drivers/s390/scsi/zfcp_dbf.c + * + * FCP adapter driver for IBM eServer zSeries + * + * Debugging facilities + * + * (C) Copyright IBM Corp. 2005 + * + * 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. + */ + +#define ZFCP_DBF_REVISION "$Revision$" + +#include <asm/debug.h> +#include <linux/ctype.h> +#include "zfcp_ext.h" + +static u32 dbfsize = 4; + +module_param(dbfsize, uint, 0400); +MODULE_PARM_DESC(dbfsize, + "number of pages for each debug feature area (default 4)"); + +#define ZFCP_LOG_AREA ZFCP_LOG_AREA_OTHER + +static inline int +zfcp_dbf_stck(char *out_buf, const char *label, unsigned long long stck) +{ + unsigned long long sec; + struct timespec xtime; + int len = 0; + + stck -= 0x8126d60e46000000LL - (0x3c26700LL * 1000000 * 4096); + sec = stck >> 12; + do_div(sec, 1000000); + xtime.tv_sec = sec; + stck -= (sec * 1000000) << 12; + xtime.tv_nsec = ((stck * 1000) >> 12); + len += sprintf(out_buf + len, "%-24s%011lu:%06lu\n", + label, xtime.tv_sec, xtime.tv_nsec); + + return len; +} + +static int zfcp_dbf_tag(char *out_buf, const char *label, const char *tag) +{ + int len = 0, i; + + len += sprintf(out_buf + len, "%-24s", label); + for (i = 0; i < ZFCP_DBF_TAG_SIZE; i++) + len += sprintf(out_buf + len, "%c", tag[i]); + len += sprintf(out_buf + len, "\n"); + + return len; +} + +static int +zfcp_dbf_view(char *out_buf, const char *label, const char *format, ...) +{ + va_list arg; + int len = 0; + + len += sprintf(out_buf + len, "%-24s", label); + va_start(arg, format); + len += vsprintf(out_buf + len, format, arg); + va_end(arg); + len += sprintf(out_buf + len, "\n"); + + return len; +} + +static int +zfcp_dbf_view_dump(char *out_buf, const char *label, + char *buffer, int buflen, int offset, int total_size) +{ + int len = 0; + + if (offset == 0) + len += sprintf(out_buf + len, "%-24s ", label); + + while (buflen--) { + if (offset > 0) { + if ((offset % 32) == 0) + len += sprintf(out_buf + len, "\n%-24c ", ' '); + else if ((offset % 4) == 0) + len += sprintf(out_buf + len, " "); + } + len += sprintf(out_buf + len, "%02x", *buffer++); + if (++offset == total_size) { + len += sprintf(out_buf + len, "\n"); + break; + } + } + + if (total_size == 0) + len += sprintf(out_buf + len, "\n"); + + return len; +} + +static inline int +zfcp_dbf_view_header(debug_info_t * id, struct debug_view *view, int area, + debug_entry_t * entry, char *out_buf) +{ + struct zfcp_dbf_dump *dump = (struct zfcp_dbf_dump *)DEBUG_DATA(entry); + int len = 0; + + if (strncmp(dump->tag, "dump", ZFCP_DBF_TAG_SIZE) != 0) { + len += zfcp_dbf_stck(out_buf + len, "timestamp", + entry->id.stck); + len += zfcp_dbf_view(out_buf + len, "cpu", "%02i", + entry->id.fields.cpuid); + } else { + len += zfcp_dbf_view_dump(out_buf + len, NULL, + dump->data, + dump->size, + dump->offset, dump->total_size); + if ((dump->offset + dump->size) == dump->total_size) + len += sprintf(out_buf + len, "\n"); + } + + return len; +} + +inline void zfcp_hba_dbf_event_fsf_response(struct zfcp_fsf_req *fsf_req) +{ + struct zfcp_adapter *adapter = fsf_req->adapter; + struct fsf_qtcb *qtcb = fsf_req->qtcb; + union fsf_prot_status_qual *prot_status_qual = + &qtcb->prefix.prot_status_qual; + union fsf_status_qual *fsf_status_qual = &qtcb->header.fsf_status_qual; + struct scsi_cmnd *scsi_cmnd; + struct zfcp_port *port; + struct zfcp_unit *unit; + struct zfcp_send_els *send_els; + struct zfcp_hba_dbf_record *rec = &adapter->hba_dbf_buf; + struct zfcp_hba_dbf_record_response *response = &rec->type.response; + int level; + unsigned long flags; + + spin_lock_irqsave(&adapter->hba_dbf_lock, flags); + memset(rec, 0, sizeof(struct zfcp_hba_dbf_record)); + strncpy(rec->tag, "resp", ZFCP_DBF_TAG_SIZE); + + if ((qtcb->prefix.prot_status != FSF_PROT_GOOD) && + (qtcb->prefix.prot_status != FSF_PROT_FSF_STATUS_PRESENTED)) { + strncpy(rec->tag2, "perr", ZFCP_DBF_TAG_SIZE); + level = 1; + } else if (qtcb->header.fsf_status != FSF_GOOD) { + strncpy(rec->tag2, "ferr", ZFCP_DBF_TAG_SIZE); + level = 1; + } else if ((fsf_req->fsf_command == FSF_QTCB_OPEN_PORT_WITH_DID) || + (fsf_req->fsf_command == FSF_QTCB_OPEN_LUN)) { + strncpy(rec->tag2, "open", ZFCP_DBF_TAG_SIZE); + level = 4; + } else if ((prot_status_qual->doubleword[0] != 0) || + (prot_status_qual->doubleword[1] != 0) || + (fsf_status_qual->doubleword[0] != 0) || + (fsf_status_qual->doubleword[1] != 0)) { + strncpy(rec->tag2, "qual", ZFCP_DBF_TAG_SIZE); + level = 3; + } else { + strncpy(rec->tag2, "norm", ZFCP_DBF_TAG_SIZE); + level = 6; + } + + response->fsf_command = fsf_req->fsf_command; + response->fsf_reqid = (unsigned long)fsf_req; + response->fsf_seqno = fsf_req->seq_no; + response->fsf_issued = fsf_req->issued; + response->fsf_prot_status = qtcb->prefix.prot_status; + response->fsf_status = qtcb->header.fsf_status; + memcpy(response->fsf_prot_status_qual, + prot_status_qual, FSF_PROT_STATUS_QUAL_SIZE); + memcpy(response->fsf_status_qual, + fsf_status_qual, FSF_STATUS_QUALIFIER_SIZE); + response->fsf_req_status = fsf_req->status; + response->sbal_first = fsf_req->sbal_first; + response->sbal_curr = fsf_req->sbal_curr; + response->sbal_last = fsf_req->sbal_last; + response->pool = fsf_req->pool != NULL; + response->erp_action = (unsigned long)fsf_req->erp_action; + + switch (fsf_req->fsf_command) { + case FSF_QTCB_FCP_CMND: + if (fsf_req->status & ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT) + break; + scsi_cmnd = (struct scsi_cmnd *)fsf_req->data; + if (scsi_cmnd != NULL) { + response->data.send_fcp.scsi_cmnd + = (unsigned long)scsi_cmnd; + response->data.send_fcp.scsi_serial + = scsi_cmnd->serial_number; + } + break; + + case FSF_QTCB_OPEN_PORT_WITH_DID: + case FSF_QTCB_CLOSE_PORT: + case FSF_QTCB_CLOSE_PHYSICAL_PORT: + port = (struct zfcp_port *)fsf_req->data; + response->data.port.wwpn = port->wwpn; + response->data.port.d_id = port->d_id; + response->data.port.port_handle = qtcb->header.port_handle; + break; + + case FSF_QTCB_OPEN_LUN: + case FSF_QTCB_CLOSE_LUN: + unit = (struct zfcp_unit *)fsf_req->data; + port = unit->port; + response->data.unit.wwpn = port->wwpn; + response->data.unit.fcp_lun = unit->fcp_lun; + response->data.unit.port_handle = qtcb->header.port_handle; + response->data.unit.lun_handle = qtcb->header.lun_handle; + break; + + case FSF_QTCB_SEND_ELS: + send_els = (struct zfcp_send_els *)fsf_req->data; + response->data.send_els.d_id = qtcb->bottom.support.d_id; + response->data.send_els.ls_code = send_els->ls_code >> 24; + break; + + case FSF_QTCB_ABORT_FCP_CMND: + case FSF_QTCB_SEND_GENERIC: + case FSF_QTCB_EXCHANGE_CONFIG_DATA: + case FSF_QTCB_EXCHANGE_PORT_DATA: + case FSF_QTCB_DOWNLOAD_CONTROL_FILE: + case FSF_QTCB_UPLOAD_CONTROL_FILE: + break; + } + + debug_event(adapter->hba_dbf, level, + rec, sizeof(struct zfcp_hba_dbf_record)); + spin_unlock_irqrestore(&adapter->hba_dbf_lock, flags); +} + +inline void +zfcp_hba_dbf_event_fsf_unsol(const char *tag, struct zfcp_adapter *adapter, + struct fsf_status_read_buffer *status_buffer) +{ + struct zfcp_hba_dbf_record *rec = &adapter->hba_dbf_buf; + unsigned long flags; + + spin_lock_irqsave(&adapter->hba_dbf_lock, flags); + memset(rec, 0, sizeof(struct zfcp_hba_dbf_record)); + strncpy(rec->tag, "stat", ZFCP_DBF_TAG_SIZE); + strncpy(rec->tag2, tag, ZFCP_DBF_TAG_SIZE); + + rec->type.status.failed = adapter->status_read_failed; + if (status_buffer != NULL) { + rec->type.status.status_type = status_buffer->status_type; + rec->type.status.status_subtype = status_buffer->status_subtype; + memcpy(&rec->type.status.queue_designator, + &status_buffer->queue_designator, + sizeof(struct fsf_queue_designator)); + + switch (status_buffer->status_type) { + case FSF_STATUS_READ_SENSE_DATA_AVAIL: + rec->type.status.payload_size = + ZFCP_DBF_UNSOL_PAYLOAD_SENSE_DATA_AVAIL; + break; + + case FSF_STATUS_READ_BIT_ERROR_THRESHOLD: + rec->type.status.payload_size = + ZFCP_DBF_UNSOL_PAYLOAD_BIT_ERROR_THRESHOLD; + break; + + case FSF_STATUS_READ_LINK_DOWN: + switch (status_buffer->status_subtype) { + case FSF_STATUS_READ_SUB_NO_PHYSICAL_LINK: + case FSF_STATUS_READ_SUB_FDISC_FAILED: + rec->type.status.payload_size = + sizeof(struct fsf_link_down_info); + } + break; + + case FSF_STATUS_READ_FEATURE_UPDATE_ALERT: + rec->type.status.payload_size = + ZFCP_DBF_UNSOL_PAYLOAD_FEATURE_UPDATE_ALERT; + break; + } + memcpy(&rec->type.status.payload, + &status_buffer->payload, rec->type.status.payload_size); + } + + debug_event(adapter->hba_dbf, 2, + rec, sizeof(struct zfcp_hba_dbf_record)); + spin_unlock_irqrestore(&adapter->hba_dbf_lock, flags); +} + +inline void +zfcp_hba_dbf_event_qdio(struct zfcp_adapter *adapter, unsigned int status, + unsigned int qdio_error, unsigned int siga_error, + int sbal_index, int sbal_count) +{ + struct zfcp_hba_dbf_record *rec = &adapter->hba_dbf_buf; + unsigned long flags; + + spin_lock_irqsave(&adapter->hba_dbf_lock, flags); + memset(rec, 0, sizeof(struct zfcp_hba_dbf_record)); + strncpy(rec->tag, "qdio", ZFCP_DBF_TAG_SIZE); + rec->type.qdio.status = status; + rec->type.qdio.qdio_error = qdio_error; + rec->type.qdio.siga_error = siga_error; + rec->type.qdio.sbal_index = sbal_index; + rec->type.qdio.sbal_count = sbal_count; + debug_event(adapter->hba_dbf, 0, + rec, sizeof(struct zfcp_hba_dbf_record)); + spin_unlock_irqrestore(&adapter->hba_dbf_lock, flags); +} + +static inline int +zfcp_hba_dbf_view_response(char *out_buf, + struct zfcp_hba_dbf_record_response *rec) +{ + int len = 0; + + len += zfcp_dbf_view(out_buf + len, "fsf_command", "0x%08x", + rec->fsf_command); + len += zfcp_dbf_view(out_buf + len, "fsf_reqid", "0x%0Lx", + rec->fsf_reqid); + len += zfcp_dbf_view(out_buf + len, "fsf_seqno", "0x%08x", + rec->fsf_seqno); + len += zfcp_dbf_stck(out_buf + len, "fsf_issued", rec->fsf_issued); + len += zfcp_dbf_view(out_buf + len, "fsf_prot_status", "0x%08x", + rec->fsf_prot_status); + len += zfcp_dbf_view(out_buf + len, "fsf_status", "0x%08x", + rec->fsf_status); + len += zfcp_dbf_view_dump(out_buf + len, "fsf_prot_status_qual", + rec->fsf_prot_status_qual, + FSF_PROT_STATUS_QUAL_SIZE, + 0, FSF_PROT_STATUS_QUAL_SIZE); + len += zfcp_dbf_view_dump(out_buf + len, "fsf_status_qual", + rec->fsf_status_qual, + FSF_STATUS_QUALIFIER_SIZE, + 0, FSF_STATUS_QUALIFIER_SIZE); + len += zfcp_dbf_view(out_buf + len, "fsf_req_status", "0x%08x", + rec->fsf_req_status); + len += zfcp_dbf_view(out_buf + len, "sbal_first", "0x%02x", + rec->sbal_first); + len += zfcp_dbf_view(out_buf + len, "sbal_curr", "0x%02x", + rec->sbal_curr); + len += zfcp_dbf_view(out_buf + len, "sbal_last", "0x%02x", + rec->sbal_last); + len += zfcp_dbf_view(out_buf + len, "pool", "0x%02x", rec->pool); + + switch (rec->fsf_command) { + case FSF_QTCB_FCP_CMND: + if (rec->fsf_req_status & ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT) + break; + len += zfcp_dbf_view(out_buf + len, "scsi_cmnd", "0x%0Lx", + rec->data.send_fcp.scsi_cmnd); + len += zfcp_dbf_view(out_buf + len, "scsi_serial", "0x%016Lx", + rec->data.send_fcp.scsi_serial); + break; + + case FSF_QTCB_OPEN_PORT_WITH_DID: + case FSF_QTCB_CLOSE_PORT: + case FSF_QTCB_CLOSE_PHYSICAL_PORT: + len += zfcp_dbf_view(out_buf + len, "wwpn", "0x%016Lx", + rec->data.port.wwpn); + len += zfcp_dbf_view(out_buf + len, "d_id", "0x%06x", + rec->data.port.d_id); + len += zfcp_dbf_view(out_buf + len, "port_handle", "0x%08x", + rec->data.port.port_handle); + break; + + case FSF_QTCB_OPEN_LUN: + case FSF_QTCB_CLOSE_LUN: + len += zfcp_dbf_view(out_buf + len, "wwpn", "0x%016Lx", + rec->data.unit.wwpn); + len += zfcp_dbf_view(out_buf + len, "fcp_lun", "0x%016Lx", + rec->data.unit.fcp_lun); + len += zfcp_dbf_view(out_buf + len, "port_handle", "0x%08x", + rec->data.unit.port_handle); + len += zfcp_dbf_view(out_buf + len, "lun_handle", "0x%08x", + rec->data.unit.lun_handle); + break; + + case FSF_QTCB_SEND_ELS: + len += zfcp_dbf_view(out_buf + len, "d_id", "0x%06x", + rec->data.send_els.d_id); + len += zfcp_dbf_view(out_buf + len, "ls_code", "0x%02x", + rec->data.send_els.ls_code); + break; + + case FSF_QTCB_ABORT_FCP_CMND: + case FSF_QTCB_SEND_GENERIC: + case FSF_QTCB_EXCHANGE_CONFIG_DATA: + case FSF_QTCB_EXCHANGE_PORT_DATA: + case FSF_QTCB_DOWNLOAD_CONTROL_FILE: + case FSF_QTCB_UPLOAD_CONTROL_FILE: + break; + } + + return len; +} + +static inline int +zfcp_hba_dbf_view_status(char *out_buf, struct zfcp_hba_dbf_record_status *rec) +{ + int len = 0; + + len += zfcp_dbf_view(out_buf + len, "failed", "0x%02x", rec->failed); + len += zfcp_dbf_view(out_buf + len, "status_type", "0x%08x", + rec->status_type); + len += zfcp_dbf_view(out_buf + len, "status_subtype", "0x%08x", + rec->status_subtype); + len += zfcp_dbf_view_dump(out_buf + len, "queue_designator", + (char *)&rec->queue_designator, + sizeof(struct fsf_queue_designator), + 0, sizeof(struct fsf_queue_designator)); + len += zfcp_dbf_view_dump(out_buf + len, "payload", + (char *)&rec->payload, + rec->payload_size, 0, rec->payload_size); + + return len; +} + +static inline int +zfcp_hba_dbf_view_qdio(char *out_buf, struct zfcp_hba_dbf_record_qdio *rec) +{ + int len = 0; + + len += zfcp_dbf_view(out_buf + len, "status", "0x%08x", rec->status); + len += zfcp_dbf_view(out_buf + len, "qdio_error", "0x%08x", + rec->qdio_error); + len += zfcp_dbf_view(out_buf + len, "siga_error", "0x%08x", + rec->siga_error); + len += zfcp_dbf_view(out_buf + len, "sbal_index", "0x%02x", + rec->sbal_index); + len += zfcp_dbf_view(out_buf + len, "sbal_count", "0x%02x", + rec->sbal_count); + + return len; +} + +static int +zfcp_hba_dbf_view_format(debug_info_t * id, struct debug_view *view, + char *out_buf, const char *in_buf) +{ + struct zfcp_hba_dbf_record *rec = (struct zfcp_hba_dbf_record *)in_buf; + int len = 0; + + if (strncmp(rec->tag, "dump", ZFCP_DBF_TAG_SIZE) == 0) + return 0; + + len += zfcp_dbf_tag(out_buf + len, "tag", rec->tag); + if (isalpha(rec->tag2[0])) + len += zfcp_dbf_tag(out_buf + len, "tag2", rec->tag2); + if (strncmp(rec->tag, "resp", ZFCP_DBF_TAG_SIZE) == 0) + len += zfcp_hba_dbf_view_response(out_buf + len, + &rec->type.response); + else if (strncmp(rec->tag, "stat", ZFCP_DBF_TAG_SIZE) == 0) + len += zfcp_hba_dbf_view_status(out_buf + len, + &rec->type.status); + else if (strncmp(rec->tag, "qdio", ZFCP_DBF_TAG_SIZE) == 0) + len += zfcp_hba_dbf_view_qdio(out_buf + len, &rec->type.qdio); + + len += sprintf(out_buf + len, "\n"); + + return len; +} + +struct debug_view zfcp_hba_dbf_view = { + "structured", + NULL, + &zfcp_dbf_view_header, + &zfcp_hba_dbf_view_format, + NULL, + NULL +}; + +inline void +_zfcp_san_dbf_event_common_ct(const char *tag, struct zfcp_fsf_req *fsf_req, + u32 s_id, u32 d_id, void *buffer, int buflen) +{ + struct zfcp_send_ct *send_ct = (struct zfcp_send_ct *)fsf_req->data; + struct zfcp_port *port = send_ct->port; + struct zfcp_adapter *adapter = port->adapter; + struct ct_hdr *header = (struct ct_hdr *)buffer; + struct zfcp_san_dbf_record *rec = &adapter->san_dbf_buf; + struct zfcp_san_dbf_record_ct *ct = &rec->type.ct; + unsigned long flags; + + spin_lock_irqsave(&adapter->san_dbf_lock, flags); + memset(rec, 0, sizeof(struct zfcp_san_dbf_record)); + strncpy(rec->tag, tag, ZFCP_DBF_TAG_SIZE); + rec->fsf_reqid = (unsigned long)fsf_req; + rec->fsf_seqno = fsf_req->seq_no; + rec->s_id = s_id; + rec->d_id = d_id; + if (strncmp(tag, "octc", ZFCP_DBF_TAG_SIZE) == 0) { + ct->type.request.cmd_req_code = header->cmd_rsp_code; + ct->type.request.revision = header->revision; + ct->type.request.gs_type = header->gs_type; + ct->type.request.gs_subtype = header->gs_subtype; + ct->type.request.options = header->options; + ct->type.request.max_res_size = header->max_res_size; + } else if (strncmp(tag, "rctc", ZFCP_DBF_TAG_SIZE) == 0) { + ct->type.response.cmd_rsp_code = header->cmd_rsp_code; + ct->type.response.revision = header->revision; + ct->type.response.reason_code = header->reason_code; + ct->type.response.reason_code_expl = header->reason_code_expl; + ct->type.response.vendor_unique = header->vendor_unique; + } + ct->payload_size = + min(buflen - (int)sizeof(struct ct_hdr), ZFCP_DBF_CT_PAYLOAD); + memcpy(ct->payload, buffer + sizeof(struct ct_hdr), ct->payload_size); + debug_event(adapter->san_dbf, 3, + rec, sizeof(struct zfcp_san_dbf_record)); + spin_unlock_irqrestore(&adapter->san_dbf_lock, flags); +} + +inline void zfcp_san_dbf_event_ct_request(struct zfcp_fsf_req *fsf_req) +{ + struct zfcp_send_ct *ct = (struct zfcp_send_ct *)fsf_req->data; + struct zfcp_port *port = ct->port; + struct zfcp_adapter *adapter = port->adapter; + + _zfcp_san_dbf_event_common_ct("octc", fsf_req, + fc_host_port_id(adapter->scsi_host), + port->d_id, zfcp_sg_to_address(ct->req), + ct->req->length); +} + +inline void zfcp_san_dbf_event_ct_response(struct zfcp_fsf_req *fsf_req) +{ + struct zfcp_send_ct *ct = (struct zfcp_send_ct *)fsf_req->data; + struct zfcp_port *port = ct->port; + struct zfcp_adapter *adapter = port->adapter; + + _zfcp_san_dbf_event_common_ct("rctc", fsf_req, port->d_id, + fc_host_port_id(adapter->scsi_host), + zfcp_sg_to_address(ct->resp), + ct->resp->length); +} + +static inline void +_zfcp_san_dbf_event_common_els(const char *tag, int level, + struct zfcp_fsf_req *fsf_req, u32 s_id, + u32 d_id, u8 ls_code, void *buffer, int buflen) +{ + struct zfcp_adapter *adapter = fsf_req->adapter; + struct zfcp_san_dbf_record *rec = &adapter->san_dbf_buf; + struct zfcp_dbf_dump *dump = (struct zfcp_dbf_dump *)rec; + unsigned long flags; + int offset = 0; + + spin_lock_irqsave(&adapter->san_dbf_lock, flags); + do { + memset(rec, 0, sizeof(struct zfcp_san_dbf_record)); + if (offset == 0) { + strncpy(rec->tag, tag, ZFCP_DBF_TAG_SIZE); + rec->fsf_reqid = (unsigned long)fsf_req; + rec->fsf_seqno = fsf_req->seq_no; + rec->s_id = s_id; + rec->d_id = d_id; + rec->type.els.ls_code = ls_code; + buflen = min(buflen, ZFCP_DBF_ELS_MAX_PAYLOAD); + rec->type.els.payload_size = buflen; + memcpy(rec->type.els.payload, + buffer, min(buflen, ZFCP_DBF_ELS_PAYLOAD)); + offset += min(buflen, ZFCP_DBF_ELS_PAYLOAD); + } else { + strncpy(dump->tag, "dump", ZFCP_DBF_TAG_SIZE); + dump->total_size = buflen; + dump->offset = offset; + dump->size = min(buflen - offset, + (int)sizeof(struct zfcp_san_dbf_record) + - (int)sizeof(struct zfcp_dbf_dump)); + memcpy(dump->data, buffer + offset, dump->size); + offset += dump->size; + } + debug_event(adapter->san_dbf, level, + rec, sizeof(struct zfcp_san_dbf_record)); + } while (offset < buflen); + spin_unlock_irqrestore(&adapter->san_dbf_lock, flags); +} + +inline void zfcp_san_dbf_event_els_request(struct zfcp_fsf_req *fsf_req) +{ + struct zfcp_send_els *els = (struct zfcp_send_els *)fsf_req->data; + + _zfcp_san_dbf_event_common_els("oels", 2, fsf_req, + fc_host_port_id(els->adapter->scsi_host), + els->d_id, + *(u8 *) zfcp_sg_to_address(els->req), + zfcp_sg_to_address(els->req), + els->req->length); +} + +inline void zfcp_san_dbf_event_els_response(struct zfcp_fsf_req *fsf_req) +{ + struct zfcp_send_els *els = (struct zfcp_send_els *)fsf_req->data; + + _zfcp_san_dbf_event_common_els("rels", 2, fsf_req, els->d_id, + fc_host_port_id(els->adapter->scsi_host), + *(u8 *) zfcp_sg_to_address(els->req), + zfcp_sg_to_address(els->resp), + els->resp->length); +} + +inline void zfcp_san_dbf_event_incoming_els(struct zfcp_fsf_req *fsf_req) +{ + struct zfcp_adapter *adapter = fsf_req->adapter; + struct fsf_status_read_buffer *status_buffer = + (struct fsf_status_read_buffer *)fsf_req->data; + int length = (int)status_buffer->length - + (int)((void *)&status_buffer->payload - (void *)status_buffer); + + _zfcp_san_dbf_event_common_els("iels", 1, fsf_req, status_buffer->d_id, + fc_host_port_id(adapter->scsi_host), + *(u8 *) status_buffer->payload, + (void *)status_buffer->payload, length); +} + +static int +zfcp_san_dbf_view_format(debug_info_t * id, struct debug_view *view, + char *out_buf, const char *in_buf) +{ + struct zfcp_san_dbf_record *rec = (struct zfcp_san_dbf_record *)in_buf; + char *buffer = NULL; + int buflen = 0, total = 0; + int len = 0; + + if (strncmp(rec->tag, "dump", ZFCP_DBF_TAG_SIZE) == 0) + return 0; + + len += zfcp_dbf_tag(out_buf + len, "tag", rec->tag); + len += zfcp_dbf_view(out_buf + len, "fsf_reqid", "0x%0Lx", + rec->fsf_reqid); + len += zfcp_dbf_view(out_buf + len, "fsf_seqno", "0x%08x", + rec->fsf_seqno); + len += zfcp_dbf_view(out_buf + len, "s_id", "0x%06x", rec->s_id); + len += zfcp_dbf_view(out_buf + len, "d_id", "0x%06x", rec->d_id); + + if (strncmp(rec->tag, "octc", ZFCP_DBF_TAG_SIZE) == 0) { + len += zfcp_dbf_view(out_buf + len, "cmd_req_code", "0x%04x", + rec->type.ct.type.request.cmd_req_code); + len += zfcp_dbf_view(out_buf + len, "revision", "0x%02x", + rec->type.ct.type.request.revision); + len += zfcp_dbf_view(out_buf + len, "gs_type", "0x%02x", + rec->type.ct.type.request.gs_type); + len += zfcp_dbf_view(out_buf + len, "gs_subtype", "0x%02x", + rec->type.ct.type.request.gs_subtype); + len += zfcp_dbf_view(out_buf + len, "options", "0x%02x", + rec->type.ct.type.request.options); + len += zfcp_dbf_view(out_buf + len, "max_res_size", "0x%04x", + rec->type.ct.type.request.max_res_size); + total = rec->type.ct.payload_size; + buffer = rec->type.ct.payload; + buflen = min(total, ZFCP_DBF_CT_PAYLOAD); + } else if (strncmp(rec->tag, "rctc", ZFCP_DBF_TAG_SIZE) == 0) { + len += zfcp_dbf_view(out_buf + len, "cmd_rsp_code", "0x%04x", + rec->type.ct.type.response.cmd_rsp_code); + len += zfcp_dbf_view(out_buf + len, "revision", "0x%02x", + rec->type.ct.type.response.revision); + len += zfcp_dbf_view(out_buf + len, "reason_code", "0x%02x", + rec->type.ct.type.response.reason_code); + len += + zfcp_dbf_view(out_buf + len, "reason_code_expl", "0x%02x", + rec->type.ct.type.response.reason_code_expl); + len += + zfcp_dbf_view(out_buf + len, "vendor_unique", "0x%02x", + rec->type.ct.type.response.vendor_unique); + total = rec->type.ct.payload_size; + buffer = rec->type.ct.payload; + buflen = min(total, ZFCP_DBF_CT_PAYLOAD); + } else if (strncmp(rec->tag, "oels", ZFCP_DBF_TAG_SIZE) == 0 || + strncmp(rec->tag, "rels", ZFCP_DBF_TAG_SIZE) == 0 || + strncmp(rec->tag, "iels", ZFCP_DBF_TAG_SIZE) == 0) { + len += zfcp_dbf_view(out_buf + len, "ls_code", "0x%02x", + rec->type.els.ls_code); + total = rec->type.els.payload_size; + buffer = rec->type.els.payload; + buflen = min(total, ZFCP_DBF_ELS_PAYLOAD); + } + + len += zfcp_dbf_view_dump(out_buf + len, "payload", + buffer, buflen, 0, total); + + if (buflen == total) + len += sprintf(out_buf + len, "\n"); + + return len; +} + +struct debug_view zfcp_san_dbf_view = { + "structured", + NULL, + &zfcp_dbf_view_header, + &zfcp_san_dbf_view_format, + NULL, + NULL +}; + +static inline void +_zfcp_scsi_dbf_event_common(const char *tag, const char *tag2, int level, + struct zfcp_adapter *adapter, + struct scsi_cmnd *scsi_cmnd, + struct zfcp_fsf_req *new_fsf_req) +{ + struct zfcp_fsf_req *fsf_req = + (struct zfcp_fsf_req *)scsi_cmnd->host_scribble; + struct zfcp_scsi_dbf_record *rec = &adapter->scsi_dbf_buf; + struct zfcp_dbf_dump *dump = (struct zfcp_dbf_dump *)rec; + unsigned long flags; + struct fcp_rsp_iu *fcp_rsp; + char *fcp_rsp_info = NULL, *fcp_sns_info = NULL; + int offset = 0, buflen = 0; + + spin_lock_irqsave(&adapter->scsi_dbf_lock, flags); + do { + memset(rec, 0, sizeof(struct zfcp_scsi_dbf_record)); + if (offset == 0) { + strncpy(rec->tag, tag, ZFCP_DBF_TAG_SIZE); + strncpy(rec->tag2, tag2, ZFCP_DBF_TAG_SIZE); + if (scsi_cmnd->device) { + rec->scsi_id = scsi_cmnd->device->id; + rec->scsi_lun = scsi_cmnd->device->lun; + } + rec->scsi_result = scsi_cmnd->result; + rec->scsi_cmnd = (unsigned long)scsi_cmnd; + rec->scsi_serial = scsi_cmnd->serial_number; + memcpy(rec->scsi_opcode, + &scsi_cmnd->cmnd, + min((int)scsi_cmnd->cmd_len, + ZFCP_DBF_SCSI_OPCODE)); + rec->scsi_retries = scsi_cmnd->retries; + rec->scsi_allowed = scsi_cmnd->allowed; + if (fsf_req != NULL) { + fcp_rsp = (struct fcp_rsp_iu *) + &(fsf_req->qtcb->bottom.io.fcp_rsp); + fcp_rsp_info = + zfcp_get_fcp_rsp_info_ptr(fcp_rsp); + fcp_sns_info = + zfcp_get_fcp_sns_info_ptr(fcp_rsp); + + rec->type.fcp.rsp_validity = + fcp_rsp->validity.value; + rec->type.fcp.rsp_scsi_status = + fcp_rsp->scsi_status; + rec->type.fcp.rsp_resid = fcp_rsp->fcp_resid; + if (fcp_rsp->validity.bits.fcp_rsp_len_valid) + rec->type.fcp.rsp_code = + *(fcp_rsp_info + 3); + if (fcp_rsp->validity.bits.fcp_sns_len_valid) { + buflen = min((int)fcp_rsp->fcp_sns_len, + ZFCP_DBF_SCSI_MAX_FCP_SNS_INFO); + rec->type.fcp.sns_info_len = buflen; + memcpy(rec->type.fcp.sns_info, + fcp_sns_info, + min(buflen, + ZFCP_DBF_SCSI_FCP_SNS_INFO)); + offset += min(buflen, + ZFCP_DBF_SCSI_FCP_SNS_INFO); + } + + rec->fsf_reqid = (unsigned long)fsf_req; + rec->fsf_seqno = fsf_req->seq_no; + rec->fsf_issued = fsf_req->issued; + } + if (new_fsf_req != NULL) { + rec->type.new_fsf_req.fsf_reqid = + (unsigned long) + new_fsf_req; + rec->type.new_fsf_req.fsf_seqno = + new_fsf_req->seq_no; + rec->type.new_fsf_req.fsf_issued = + new_fsf_req->issued; + } + } else { + strncpy(dump->tag, "dump", ZFCP_DBF_TAG_SIZE); + dump->total_size = buflen; + dump->offset = offset; + dump->size = min(buflen - offset, + (int)sizeof(struct + zfcp_scsi_dbf_record) - + (int)sizeof(struct zfcp_dbf_dump)); + memcpy(dump->data, fcp_sns_info + offset, dump->size); + offset += dump->size; + } + debug_event(adapter->scsi_dbf, level, + rec, sizeof(struct zfcp_scsi_dbf_record)); + } while (offset < buflen); + spin_unlock_irqrestore(&adapter->scsi_dbf_lock, flags); +} + +inline void +zfcp_scsi_dbf_event_result(const char *tag, int level, + struct zfcp_adapter *adapter, + struct scsi_cmnd *scsi_cmnd) +{ + _zfcp_scsi_dbf_event_common("rslt", + tag, level, adapter, scsi_cmnd, NULL); +} + +inline void +zfcp_scsi_dbf_event_abort(const char *tag, struct zfcp_adapter *adapter, + struct scsi_cmnd *scsi_cmnd, + struct zfcp_fsf_req *new_fsf_req) +{ + _zfcp_scsi_dbf_event_common("abrt", + tag, 1, adapter, scsi_cmnd, new_fsf_req); +} + +inline void +zfcp_scsi_dbf_event_devreset(const char *tag, u8 flag, struct zfcp_unit *unit, + struct scsi_cmnd *scsi_cmnd) +{ + struct zfcp_adapter *adapter = unit->port->adapter; + + _zfcp_scsi_dbf_event_common(flag == FCP_TARGET_RESET ? "trst" : "lrst", + tag, 1, adapter, scsi_cmnd, NULL); +} + +static int +zfcp_scsi_dbf_view_format(debug_info_t * id, struct debug_view *view, + char *out_buf, const char *in_buf) +{ + struct zfcp_scsi_dbf_record *rec = + (struct zfcp_scsi_dbf_record *)in_buf; + int len = 0; + + if (strncmp(rec->tag, "dump", ZFCP_DBF_TAG_SIZE) == 0) + return 0; + + len += zfcp_dbf_tag(out_buf + len, "tag", rec->tag); + len += zfcp_dbf_tag(out_buf + len, "tag2", rec->tag2); + len += zfcp_dbf_view(out_buf + len, "scsi_id", "0x%08x", rec->scsi_id); + len += zfcp_dbf_view(out_buf + len, "scsi_lun", "0x%08x", + rec->scsi_lun); + len += zfcp_dbf_view(out_buf + len, "scsi_result", "0x%08x", + rec->scsi_result); + len += zfcp_dbf_view(out_buf + len, "scsi_cmnd", "0x%0Lx", + rec->scsi_cmnd); + len += zfcp_dbf_view(out_buf + len, "scsi_serial", "0x%016Lx", + rec->scsi_serial); + len += zfcp_dbf_view_dump(out_buf + len, "scsi_opcode", + rec->scsi_opcode, + ZFCP_DBF_SCSI_OPCODE, + 0, ZFCP_DBF_SCSI_OPCODE); + len += zfcp_dbf_view(out_buf + len, "scsi_retries", "0x%02x", + rec->scsi_retries); + len += zfcp_dbf_view(out_buf + len, "scsi_allowed", "0x%02x", + rec->scsi_allowed); + len += zfcp_dbf_view(out_buf + len, "fsf_reqid", "0x%0Lx", + rec->fsf_reqid); + len += zfcp_dbf_view(out_buf + len, "fsf_seqno", "0x%08x", + rec->fsf_seqno); + len += zfcp_dbf_stck(out_buf + len, "fsf_issued", rec->fsf_issued); + if (strncmp(rec->tag, "rslt", ZFCP_DBF_TAG_SIZE) == 0) { + len += + zfcp_dbf_view(out_buf + len, "fcp_rsp_validity", "0x%02x", + rec->type.fcp.rsp_validity); + len += + zfcp_dbf_view(out_buf + len, "fcp_rsp_scsi_status", + "0x%02x", rec->type.fcp.rsp_scsi_status); + len += + zfcp_dbf_view(out_buf + len, "fcp_rsp_resid", "0x%08x", + rec->type.fcp.rsp_resid); + len += + zfcp_dbf_view(out_buf + len, "fcp_rsp_code", "0x%08x", + rec->type.fcp.rsp_code); + len += + zfcp_dbf_view(out_buf + len, "fcp_sns_info_len", "0x%08x", + rec->type.fcp.sns_info_len); + len += + zfcp_dbf_view_dump(out_buf + len, "fcp_sns_info", + rec->type.fcp.sns_info, + min((int)rec->type.fcp.sns_info_len, + ZFCP_DBF_SCSI_FCP_SNS_INFO), 0, + rec->type.fcp.sns_info_len); + } else if (strncmp(rec->tag, "abrt", ZFCP_DBF_TAG_SIZE) == 0) { + len += zfcp_dbf_view(out_buf + len, "fsf_reqid_abort", "0x%0Lx", + rec->type.new_fsf_req.fsf_reqid); + len += zfcp_dbf_view(out_buf + len, "fsf_seqno_abort", "0x%08x", + rec->type.new_fsf_req.fsf_seqno); + len += zfcp_dbf_stck(out_buf + len, "fsf_issued", + rec->type.new_fsf_req.fsf_issued); + } else if ((strncmp(rec->tag, "trst", ZFCP_DBF_TAG_SIZE) == 0) || + (strncmp(rec->tag, "lrst", ZFCP_DBF_TAG_SIZE) == 0)) { + len += zfcp_dbf_view(out_buf + len, "fsf_reqid_reset", "0x%0Lx", + rec->type.new_fsf_req.fsf_reqid); + len += zfcp_dbf_view(out_buf + len, "fsf_seqno_reset", "0x%08x", + rec->type.new_fsf_req.fsf_seqno); + len += zfcp_dbf_stck(out_buf + len, "fsf_issued", + rec->type.new_fsf_req.fsf_issued); + } + + len += sprintf(out_buf + len, "\n"); + + return len; +} + +struct debug_view zfcp_scsi_dbf_view = { + "structured", + NULL, + &zfcp_dbf_view_header, + &zfcp_scsi_dbf_view_format, + NULL, + NULL +}; + +/** + * zfcp_adapter_debug_register - registers debug feature for an adapter + * @adapter: pointer to adapter for which debug features should be registered + * return: -ENOMEM on error, 0 otherwise + */ +int zfcp_adapter_debug_register(struct zfcp_adapter *adapter) +{ + char dbf_name[DEBUG_MAX_NAME_LEN]; + + /* debug feature area which records recovery activity */ + spin_lock_init(&adapter->erp_dbf_lock); + sprintf(dbf_name, "zfcp_%s_erp", zfcp_get_busid_by_adapter(adapter)); + adapter->erp_dbf = debug_register(dbf_name, dbfsize, 2, + sizeof(struct zfcp_erp_dbf_record)); + if (!adapter->erp_dbf) + goto failed; + debug_register_view(adapter->erp_dbf, &debug_hex_ascii_view); + debug_set_level(adapter->erp_dbf, 3); + + /* debug feature area which records HBA (FSF and QDIO) conditions */ + spin_lock_init(&adapter->hba_dbf_lock); + sprintf(dbf_name, "zfcp_%s_hba", zfcp_get_busid_by_adapter(adapter)); + adapter->hba_dbf = debug_register(dbf_name, dbfsize, 1, + sizeof(struct zfcp_hba_dbf_record)); + if (!adapter->hba_dbf) + goto failed; + debug_register_view(adapter->hba_dbf, &debug_hex_ascii_view); + debug_register_view(adapter->hba_dbf, &zfcp_hba_dbf_view); + debug_set_level(adapter->hba_dbf, 3); + + /* debug feature area which records SAN command failures and recovery */ + spin_lock_init(&adapter->san_dbf_lock); + sprintf(dbf_name, "zfcp_%s_san", zfcp_get_busid_by_adapter(adapter)); + adapter->san_dbf = debug_register(dbf_name, dbfsize, 1, + sizeof(struct zfcp_san_dbf_record)); + if (!adapter->san_dbf) + goto failed; + debug_register_view(adapter->san_dbf, &debug_hex_ascii_view); + debug_register_view(adapter->san_dbf, &zfcp_san_dbf_view); + debug_set_level(adapter->san_dbf, 6); + + /* debug feature area which records SCSI command failures and recovery */ + spin_lock_init(&adapter->scsi_dbf_lock); + sprintf(dbf_name, "zfcp_%s_scsi", zfcp_get_busid_by_adapter(adapter)); + adapter->scsi_dbf = debug_register(dbf_name, dbfsize, 1, + sizeof(struct zfcp_scsi_dbf_record)); + if (!adapter->scsi_dbf) + goto failed; + debug_register_view(adapter->scsi_dbf, &debug_hex_ascii_view); + debug_register_view(adapter->scsi_dbf, &zfcp_scsi_dbf_view); + debug_set_level(adapter->scsi_dbf, 3); + + return 0; + + failed: + zfcp_adapter_debug_unregister(adapter); + + return -ENOMEM; +} + +/** + * zfcp_adapter_debug_unregister - unregisters debug feature for an adapter + * @adapter: pointer to adapter for which debug features should be unregistered + */ +void zfcp_adapter_debug_unregister(struct zfcp_adapter *adapter) +{ + debug_unregister(adapter->scsi_dbf); + debug_unregister(adapter->san_dbf); + debug_unregister(adapter->hba_dbf); + debug_unregister(adapter->erp_dbf); + adapter->scsi_dbf = NULL; + adapter->san_dbf = NULL; + adapter->hba_dbf = NULL; + adapter->erp_dbf = NULL; +} + +#undef ZFCP_LOG_AREA diff --git a/drivers/s390/scsi/zfcp_def.h b/drivers/s390/scsi/zfcp_def.h index 455e902533a9..d81b737d68cc 100644 --- a/drivers/s390/scsi/zfcp_def.h +++ b/drivers/s390/scsi/zfcp_def.h @@ -66,7 +66,7 @@ /********************* GENERAL DEFINES *********************************/ /* zfcp version number, it consists of major, minor, and patch-level number */ -#define ZFCP_VERSION "4.3.0" +#define ZFCP_VERSION "4.5.0" /** * zfcp_sg_to_address - determine kernel address from struct scatterlist @@ -154,13 +154,17 @@ typedef u32 scsi_lun_t; #define ZFCP_EXCHANGE_CONFIG_DATA_FIRST_SLEEP 100 #define ZFCP_EXCHANGE_CONFIG_DATA_RETRIES 7 +/* Retry 5 times every 2 second, then every minute */ +#define ZFCP_EXCHANGE_PORT_DATA_SHORT_RETRIES 5 +#define ZFCP_EXCHANGE_PORT_DATA_SHORT_SLEEP 200 +#define ZFCP_EXCHANGE_PORT_DATA_LONG_SLEEP 6000 + /* timeout value for "default timer" for fsf requests */ #define ZFCP_FSF_REQUEST_TIMEOUT (60*HZ); /*************** FIBRE CHANNEL PROTOCOL SPECIFIC DEFINES ********************/ typedef unsigned long long wwn_t; -typedef unsigned int fc_id_t; typedef unsigned long long fcp_lun_t; /* data length field may be at variable position in FCP-2 FCP_CMND IU */ typedef unsigned int fcp_dl_t; @@ -281,6 +285,171 @@ struct fcp_logo { } __attribute__((packed)); /* + * DBF stuff + */ +#define ZFCP_DBF_TAG_SIZE 4 + +struct zfcp_dbf_dump { + u8 tag[ZFCP_DBF_TAG_SIZE]; + u32 total_size; /* size of total dump data */ + u32 offset; /* how much data has being already dumped */ + u32 size; /* how much data comes with this record */ + u8 data[]; /* dump data */ +} __attribute__ ((packed)); + +/* FIXME: to be inflated when reworking the erp dbf */ +struct zfcp_erp_dbf_record { + u8 dummy[16]; +} __attribute__ ((packed)); + +struct zfcp_hba_dbf_record_response { + u32 fsf_command; + u64 fsf_reqid; + u32 fsf_seqno; + u64 fsf_issued; + u32 fsf_prot_status; + u32 fsf_status; + u8 fsf_prot_status_qual[FSF_PROT_STATUS_QUAL_SIZE]; + u8 fsf_status_qual[FSF_STATUS_QUALIFIER_SIZE]; + u32 fsf_req_status; + u8 sbal_first; + u8 sbal_curr; + u8 sbal_last; + u8 pool; + u64 erp_action; + union { + struct { + u64 scsi_cmnd; + u64 scsi_serial; + } send_fcp; + struct { + u64 wwpn; + u32 d_id; + u32 port_handle; + } port; + struct { + u64 wwpn; + u64 fcp_lun; + u32 port_handle; + u32 lun_handle; + } unit; + struct { + u32 d_id; + u8 ls_code; + } send_els; + } data; +} __attribute__ ((packed)); + +struct zfcp_hba_dbf_record_status { + u8 failed; + u32 status_type; + u32 status_subtype; + struct fsf_queue_designator + queue_designator; + u32 payload_size; +#define ZFCP_DBF_UNSOL_PAYLOAD 80 +#define ZFCP_DBF_UNSOL_PAYLOAD_SENSE_DATA_AVAIL 32 +#define ZFCP_DBF_UNSOL_PAYLOAD_BIT_ERROR_THRESHOLD 56 +#define ZFCP_DBF_UNSOL_PAYLOAD_FEATURE_UPDATE_ALERT 2 * sizeof(u32) + u8 payload[ZFCP_DBF_UNSOL_PAYLOAD]; +} __attribute__ ((packed)); + +struct zfcp_hba_dbf_record_qdio { + u32 status; + u32 qdio_error; + u32 siga_error; + u8 sbal_index; + u8 sbal_count; +} __attribute__ ((packed)); + +struct zfcp_hba_dbf_record { + u8 tag[ZFCP_DBF_TAG_SIZE]; + u8 tag2[ZFCP_DBF_TAG_SIZE]; + union { + struct zfcp_hba_dbf_record_response response; + struct zfcp_hba_dbf_record_status status; + struct zfcp_hba_dbf_record_qdio qdio; + } type; +} __attribute__ ((packed)); + +struct zfcp_san_dbf_record_ct { + union { + struct { + u16 cmd_req_code; + u8 revision; + u8 gs_type; + u8 gs_subtype; + u8 options; + u16 max_res_size; + } request; + struct { + u16 cmd_rsp_code; + u8 revision; + u8 reason_code; + u8 reason_code_expl; + u8 vendor_unique; + } response; + } type; + u32 payload_size; +#define ZFCP_DBF_CT_PAYLOAD 24 + u8 payload[ZFCP_DBF_CT_PAYLOAD]; +} __attribute__ ((packed)); + +struct zfcp_san_dbf_record_els { + u8 ls_code; + u32 payload_size; +#define ZFCP_DBF_ELS_PAYLOAD 32 +#define ZFCP_DBF_ELS_MAX_PAYLOAD 1024 + u8 payload[ZFCP_DBF_ELS_PAYLOAD]; +} __attribute__ ((packed)); + +struct zfcp_san_dbf_record { + u8 tag[ZFCP_DBF_TAG_SIZE]; + u64 fsf_reqid; + u32 fsf_seqno; + u32 s_id; + u32 d_id; + union { + struct zfcp_san_dbf_record_ct ct; + struct zfcp_san_dbf_record_els els; + } type; +} __attribute__ ((packed)); + +struct zfcp_scsi_dbf_record { + u8 tag[ZFCP_DBF_TAG_SIZE]; + u8 tag2[ZFCP_DBF_TAG_SIZE]; + u32 scsi_id; + u32 scsi_lun; + u32 scsi_result; + u64 scsi_cmnd; + u64 scsi_serial; +#define ZFCP_DBF_SCSI_OPCODE 16 + u8 scsi_opcode[ZFCP_DBF_SCSI_OPCODE]; + u8 scsi_retries; + u8 scsi_allowed; + u64 fsf_reqid; + u32 fsf_seqno; + u64 fsf_issued; + union { + struct { + u64 fsf_reqid; + u32 fsf_seqno; + u64 fsf_issued; + } new_fsf_req; + struct { + u8 rsp_validity; + u8 rsp_scsi_status; + u32 rsp_resid; + u8 rsp_code; +#define ZFCP_DBF_SCSI_FCP_SNS_INFO 16 +#define ZFCP_DBF_SCSI_MAX_FCP_SNS_INFO 256 + u32 sns_info_len; + u8 sns_info[ZFCP_DBF_SCSI_FCP_SNS_INFO]; + } fcp; + } type; +} __attribute__ ((packed)); + +/* * FC-FS stuff */ #define R_A_TOV 10 /* seconds */ @@ -339,34 +508,6 @@ struct zfcp_rc_entry { */ #define ZFCP_CT_TIMEOUT (3 * R_A_TOV) - -/***************** S390 DEBUG FEATURE SPECIFIC DEFINES ***********************/ - -/* debug feature entries per adapter */ -#define ZFCP_ERP_DBF_INDEX 1 -#define ZFCP_ERP_DBF_AREAS 2 -#define ZFCP_ERP_DBF_LENGTH 16 -#define ZFCP_ERP_DBF_LEVEL 3 -#define ZFCP_ERP_DBF_NAME "zfcperp" - -#define ZFCP_CMD_DBF_INDEX 2 -#define ZFCP_CMD_DBF_AREAS 1 -#define ZFCP_CMD_DBF_LENGTH 8 -#define ZFCP_CMD_DBF_LEVEL 3 -#define ZFCP_CMD_DBF_NAME "zfcpcmd" - -#define ZFCP_ABORT_DBF_INDEX 2 -#define ZFCP_ABORT_DBF_AREAS 1 -#define ZFCP_ABORT_DBF_LENGTH 8 -#define ZFCP_ABORT_DBF_LEVEL 6 -#define ZFCP_ABORT_DBF_NAME "zfcpabt" - -#define ZFCP_IN_ELS_DBF_INDEX 2 -#define ZFCP_IN_ELS_DBF_AREAS 1 -#define ZFCP_IN_ELS_DBF_LENGTH 8 -#define ZFCP_IN_ELS_DBF_LEVEL 6 -#define ZFCP_IN_ELS_DBF_NAME "zfcpels" - /******************** LOGGING MACROS AND DEFINES *****************************/ /* @@ -501,6 +642,7 @@ do { \ #define ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL 0x00000080 #define ZFCP_STATUS_ADAPTER_ERP_PENDING 0x00000100 #define ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED 0x00000200 +#define ZFCP_STATUS_ADAPTER_XPORT_OK 0x00000800 #define ZFCP_STATUS_ADAPTER_SCSI_UP \ (ZFCP_STATUS_COMMON_UNBLOCKED | \ @@ -635,45 +777,6 @@ struct zfcp_adapter_mempool { mempool_t *data_gid_pn; }; -struct zfcp_exchange_config_data{ -}; - -struct zfcp_open_port { - struct zfcp_port *port; -}; - -struct zfcp_close_port { - struct zfcp_port *port; -}; - -struct zfcp_open_unit { - struct zfcp_unit *unit; -}; - -struct zfcp_close_unit { - struct zfcp_unit *unit; -}; - -struct zfcp_close_physical_port { - struct zfcp_port *port; -}; - -struct zfcp_send_fcp_command_task { - struct zfcp_fsf_req *fsf_req; - struct zfcp_unit *unit; - struct scsi_cmnd *scsi_cmnd; - unsigned long start_jiffies; -}; - -struct zfcp_send_fcp_command_task_management { - struct zfcp_unit *unit; -}; - -struct zfcp_abort_fcp_command { - struct zfcp_fsf_req *fsf_req; - struct zfcp_unit *unit; -}; - /* * header for CT_IU */ @@ -702,7 +805,7 @@ struct ct_iu_gid_pn_req { /* FS_ACC IU and data unit for GID_PN nameserver request */ struct ct_iu_gid_pn_resp { struct ct_hdr header; - fc_id_t d_id; + u32 d_id; } __attribute__ ((packed)); typedef void (*zfcp_send_ct_handler_t)(unsigned long); @@ -768,7 +871,7 @@ typedef void (*zfcp_send_els_handler_t)(unsigned long); struct zfcp_send_els { struct zfcp_adapter *adapter; struct zfcp_port *port; - fc_id_t d_id; + u32 d_id; struct scatterlist *req; struct scatterlist *resp; unsigned int req_count; @@ -781,33 +884,6 @@ struct zfcp_send_els { int status; }; -struct zfcp_status_read { - struct fsf_status_read_buffer *buffer; -}; - -struct zfcp_fsf_done { - struct completion *complete; - int status; -}; - -/* request specific data */ -union zfcp_req_data { - struct zfcp_exchange_config_data exchange_config_data; - struct zfcp_open_port open_port; - struct zfcp_close_port close_port; - struct zfcp_open_unit open_unit; - struct zfcp_close_unit close_unit; - struct zfcp_close_physical_port close_physical_port; - struct zfcp_send_fcp_command_task send_fcp_command_task; - struct zfcp_send_fcp_command_task_management - send_fcp_command_task_management; - struct zfcp_abort_fcp_command abort_fcp_command; - struct zfcp_send_ct *send_ct; - struct zfcp_send_els *send_els; - struct zfcp_status_read status_read; - struct fsf_qtcb_bottom_port *port_data; -}; - struct zfcp_qdio_queue { struct qdio_buffer *buffer[QDIO_MAX_BUFFERS_PER_Q]; /* SBALs */ u8 free_index; /* index of next free bfr @@ -838,21 +914,19 @@ struct zfcp_adapter { atomic_t refcount; /* reference count */ wait_queue_head_t remove_wq; /* can be used to wait for refcount drop to zero */ - wwn_t wwnn; /* WWNN */ - wwn_t wwpn; /* WWPN */ - fc_id_t s_id; /* N_Port ID */ wwn_t peer_wwnn; /* P2P peer WWNN */ wwn_t peer_wwpn; /* P2P peer WWPN */ - fc_id_t peer_d_id; /* P2P peer D_ID */ + u32 peer_d_id; /* P2P peer D_ID */ + wwn_t physical_wwpn; /* WWPN of physical port */ + u32 physical_s_id; /* local FC port ID */ struct ccw_device *ccw_device; /* S/390 ccw device */ u8 fc_service_class; u32 fc_topology; /* FC topology */ - u32 fc_link_speed; /* FC interface speed */ u32 hydra_version; /* Hydra version */ u32 fsf_lic_version; - u32 supported_features;/* of FCP channel */ + u32 adapter_features; /* FCP channel features */ + u32 connection_features; /* host connection features */ u32 hardware_version; /* of FCP channel */ - u8 serial_number[32]; /* of hardware */ struct Scsi_Host *scsi_host; /* Pointer to mid-layer */ unsigned short scsi_host_no; /* Assigned host number */ unsigned char name[9]; @@ -889,11 +963,18 @@ struct zfcp_adapter { u32 erp_low_mem_count; /* nr of erp actions waiting for memory */ struct zfcp_port *nameserver_port; /* adapter's nameserver */ - debug_info_t *erp_dbf; /* S/390 debug features */ - debug_info_t *abort_dbf; - debug_info_t *in_els_dbf; - debug_info_t *cmd_dbf; - spinlock_t dbf_lock; + debug_info_t *erp_dbf; + debug_info_t *hba_dbf; + debug_info_t *san_dbf; /* debug feature areas */ + debug_info_t *scsi_dbf; + spinlock_t erp_dbf_lock; + spinlock_t hba_dbf_lock; + spinlock_t san_dbf_lock; + spinlock_t scsi_dbf_lock; + struct zfcp_erp_dbf_record erp_dbf_buf; + struct zfcp_hba_dbf_record hba_dbf_buf; + struct zfcp_san_dbf_record san_dbf_buf; + struct zfcp_scsi_dbf_record scsi_dbf_buf; struct zfcp_adapter_mempool pool; /* Adapter memory pools */ struct qdio_initialize qdio_init_data; /* for qdio_establish */ struct device generic_services; /* directory for WKA ports */ @@ -919,7 +1000,7 @@ struct zfcp_port { atomic_t status; /* status of this remote port */ wwn_t wwnn; /* WWNN if known */ wwn_t wwpn; /* WWPN */ - fc_id_t d_id; /* D_ID */ + u32 d_id; /* D_ID */ u32 handle; /* handle assigned by FSF */ struct zfcp_erp_action erp_action; /* pending error recovery */ atomic_t erp_counter; @@ -963,11 +1044,13 @@ struct zfcp_fsf_req { u32 fsf_command; /* FSF Command copy */ struct fsf_qtcb *qtcb; /* address of associated QTCB */ u32 seq_no; /* Sequence number of request */ - union zfcp_req_data data; /* Info fields of request */ + unsigned long data; /* private data of request */ struct zfcp_erp_action *erp_action; /* used if this request is issued on behalf of erp */ mempool_t *pool; /* used if request was alloacted from emergency pool */ + unsigned long long issued; /* request sent time (STCK) */ + struct zfcp_unit *unit; }; typedef void zfcp_fsf_req_handler_t(struct zfcp_fsf_req*); diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index cb4f612550ba..023f4e558ae4 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -82,6 +82,7 @@ static int zfcp_erp_adapter_strategy_open(struct zfcp_erp_action *); static int zfcp_erp_adapter_strategy_open_qdio(struct zfcp_erp_action *); static int zfcp_erp_adapter_strategy_open_fsf(struct zfcp_erp_action *); static int zfcp_erp_adapter_strategy_open_fsf_xconfig(struct zfcp_erp_action *); +static int zfcp_erp_adapter_strategy_open_fsf_xport(struct zfcp_erp_action *); static int zfcp_erp_adapter_strategy_open_fsf_statusread( struct zfcp_erp_action *); @@ -345,13 +346,13 @@ zfcp_erp_adisc(struct zfcp_port *port) /* acc. to FC-FS, hard_nport_id in ADISC should not be set for ports without FC-AL-2 capability, so we don't set it */ - adisc->wwpn = adapter->wwpn; - adisc->wwnn = adapter->wwnn; - adisc->nport_id = adapter->s_id; + adisc->wwpn = fc_host_port_name(adapter->scsi_host); + adisc->wwnn = fc_host_node_name(adapter->scsi_host); + adisc->nport_id = fc_host_port_id(adapter->scsi_host); ZFCP_LOG_INFO("ADISC request from s_id 0x%08x to d_id 0x%08x " "(wwpn=0x%016Lx, wwnn=0x%016Lx, " "hard_nport_id=0x%08x, nport_id=0x%08x)\n", - adapter->s_id, send_els->d_id, (wwn_t) adisc->wwpn, + adisc->nport_id, send_els->d_id, (wwn_t) adisc->wwpn, (wwn_t) adisc->wwnn, adisc->hard_nport_id, adisc->nport_id); @@ -404,7 +405,7 @@ zfcp_erp_adisc_handler(unsigned long data) struct zfcp_send_els *send_els; struct zfcp_port *port; struct zfcp_adapter *adapter; - fc_id_t d_id; + u32 d_id; struct zfcp_ls_adisc_acc *adisc; send_els = (struct zfcp_send_els *) data; @@ -435,9 +436,9 @@ zfcp_erp_adisc_handler(unsigned long data) ZFCP_LOG_INFO("ADISC response from d_id 0x%08x to s_id " "0x%08x (wwpn=0x%016Lx, wwnn=0x%016Lx, " "hard_nport_id=0x%08x, nport_id=0x%08x)\n", - d_id, adapter->s_id, (wwn_t) adisc->wwpn, - (wwn_t) adisc->wwnn, adisc->hard_nport_id, - adisc->nport_id); + d_id, fc_host_port_id(adapter->scsi_host), + (wwn_t) adisc->wwpn, (wwn_t) adisc->wwnn, + adisc->hard_nport_id, adisc->nport_id); /* set wwnn for port */ if (port->wwnn == 0) @@ -886,7 +887,7 @@ static int zfcp_erp_strategy_check_fsfreq(struct zfcp_erp_action *erp_action) { int retval = 0; - struct zfcp_fsf_req *fsf_req; + struct zfcp_fsf_req *fsf_req = NULL; struct zfcp_adapter *adapter = erp_action->adapter; if (erp_action->fsf_req) { @@ -896,7 +897,7 @@ zfcp_erp_strategy_check_fsfreq(struct zfcp_erp_action *erp_action) list_for_each_entry(fsf_req, &adapter->fsf_req_list_head, list) if (fsf_req == erp_action->fsf_req) break; - if (fsf_req == erp_action->fsf_req) { + if (fsf_req && (fsf_req->erp_action == erp_action)) { /* fsf_req still exists */ debug_text_event(adapter->erp_dbf, 3, "a_ca_req"); debug_event(adapter->erp_dbf, 3, &fsf_req, @@ -2258,16 +2259,21 @@ zfcp_erp_adapter_strategy_close_qdio(struct zfcp_erp_action *erp_action) static int zfcp_erp_adapter_strategy_open_fsf(struct zfcp_erp_action *erp_action) { - int retval; + int xconfig, xport; + + if (atomic_test_mask(ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, + &erp_action->adapter->status)) { + zfcp_erp_adapter_strategy_open_fsf_xport(erp_action); + atomic_set(&erp_action->adapter->erp_counter, 0); + return ZFCP_ERP_FAILED; + } - /* do 'exchange configuration data' */ - retval = zfcp_erp_adapter_strategy_open_fsf_xconfig(erp_action); - if (retval == ZFCP_ERP_FAILED) - return retval; + xconfig = zfcp_erp_adapter_strategy_open_fsf_xconfig(erp_action); + xport = zfcp_erp_adapter_strategy_open_fsf_xport(erp_action); + if ((xconfig == ZFCP_ERP_FAILED) || (xport == ZFCP_ERP_FAILED)) + return ZFCP_ERP_FAILED; - /* start the desired number of Status Reads */ - retval = zfcp_erp_adapter_strategy_open_fsf_statusread(erp_action); - return retval; + return zfcp_erp_adapter_strategy_open_fsf_statusread(erp_action); } /* @@ -2291,7 +2297,9 @@ zfcp_erp_adapter_strategy_open_fsf_xconfig(struct zfcp_erp_action *erp_action) atomic_clear_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT, &adapter->status); ZFCP_LOG_DEBUG("Doing exchange config data\n"); + write_lock(&adapter->erp_lock); zfcp_erp_action_to_running(erp_action); + write_unlock(&adapter->erp_lock); zfcp_erp_timeout_init(erp_action); if (zfcp_fsf_exchange_config_data(erp_action)) { retval = ZFCP_ERP_FAILED; @@ -2348,6 +2356,76 @@ zfcp_erp_adapter_strategy_open_fsf_xconfig(struct zfcp_erp_action *erp_action) return retval; } +static int +zfcp_erp_adapter_strategy_open_fsf_xport(struct zfcp_erp_action *erp_action) +{ + int retval = ZFCP_ERP_SUCCEEDED; + int retries; + int sleep; + struct zfcp_adapter *adapter = erp_action->adapter; + + atomic_clear_mask(ZFCP_STATUS_ADAPTER_XPORT_OK, &adapter->status); + + for (retries = 0; ; retries++) { + ZFCP_LOG_DEBUG("Doing exchange port data\n"); + zfcp_erp_action_to_running(erp_action); + zfcp_erp_timeout_init(erp_action); + if (zfcp_fsf_exchange_port_data(erp_action, adapter, NULL)) { + retval = ZFCP_ERP_FAILED; + debug_text_event(adapter->erp_dbf, 5, "a_fstx_xf"); + ZFCP_LOG_INFO("error: initiation of exchange of " + "port data failed for adapter %s\n", + zfcp_get_busid_by_adapter(adapter)); + break; + } + debug_text_event(adapter->erp_dbf, 6, "a_fstx_xok"); + ZFCP_LOG_DEBUG("Xchange underway\n"); + + /* + * Why this works: + * Both the normal completion handler as well as the timeout + * handler will do an 'up' when the 'exchange port data' + * request completes or times out. Thus, the signal to go on + * won't be lost utilizing this semaphore. + * Furthermore, this 'adapter_reopen' action is + * guaranteed to be the only action being there (highest action + * which prevents other actions from being created). + * Resulting from that, the wake signal recognized here + * _must_ be the one belonging to the 'exchange port + * data' request. + */ + down(&adapter->erp_ready_sem); + if (erp_action->status & ZFCP_STATUS_ERP_TIMEDOUT) { + ZFCP_LOG_INFO("error: exchange of port data " + "for adapter %s timed out\n", + zfcp_get_busid_by_adapter(adapter)); + break; + } + + if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, + &adapter->status)) + break; + + ZFCP_LOG_DEBUG("host connection still initialising... " + "waiting and retrying...\n"); + /* sleep a little bit before retry */ + sleep = retries < ZFCP_EXCHANGE_PORT_DATA_SHORT_RETRIES ? + ZFCP_EXCHANGE_PORT_DATA_SHORT_SLEEP : + ZFCP_EXCHANGE_PORT_DATA_LONG_SLEEP; + msleep(jiffies_to_msecs(sleep)); + } + + if (atomic_test_mask(ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, + &adapter->status)) { + ZFCP_LOG_INFO("error: exchange of port data for " + "adapter %s failed\n", + zfcp_get_busid_by_adapter(adapter)); + retval = ZFCP_ERP_FAILED; + } + + return retval; +} + /* * function: * @@ -3194,11 +3272,19 @@ zfcp_erp_action_enqueue(int action, /* fall through !!! */ case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED: - if (atomic_test_mask - (ZFCP_STATUS_COMMON_ERP_INUSE, &port->status) - && port->erp_action.action == - ZFCP_ERP_ACTION_REOPEN_PORT_FORCED) { - debug_text_event(adapter->erp_dbf, 4, "pf_actenq_drp"); + if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, + &port->status)) { + if (port->erp_action.action != + ZFCP_ERP_ACTION_REOPEN_PORT_FORCED) { + ZFCP_LOG_INFO("dropped erp action %i (port " + "0x%016Lx, action in use: %i)\n", + action, port->wwpn, + port->erp_action.action); + debug_text_event(adapter->erp_dbf, 4, + "pf_actenq_drp"); + } else + debug_text_event(adapter->erp_dbf, 4, + "pf_actenq_drpcp"); debug_event(adapter->erp_dbf, 4, &port->wwpn, sizeof (wwn_t)); goto out; @@ -3589,6 +3675,9 @@ zfcp_erp_adapter_access_changed(struct zfcp_adapter *adapter) struct zfcp_port *port; unsigned long flags; + if (adapter->connection_features & FSF_FEATURE_NPIV_MODE) + return; + debug_text_event(adapter->erp_dbf, 3, "a_access_recover"); debug_event(adapter->erp_dbf, 3, &adapter->name, 8); diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h index cd98a2de9f8f..c3782261cb5c 100644 --- a/drivers/s390/scsi/zfcp_ext.h +++ b/drivers/s390/scsi/zfcp_ext.h @@ -96,7 +96,8 @@ extern int zfcp_fsf_open_unit(struct zfcp_erp_action *); extern int zfcp_fsf_close_unit(struct zfcp_erp_action *); extern int zfcp_fsf_exchange_config_data(struct zfcp_erp_action *); -extern int zfcp_fsf_exchange_port_data(struct zfcp_adapter *, +extern int zfcp_fsf_exchange_port_data(struct zfcp_erp_action *, + struct zfcp_adapter *, struct fsf_qtcb_bottom_port *); extern int zfcp_fsf_control_file(struct zfcp_adapter *, struct zfcp_fsf_req **, u32, u32, struct zfcp_sg_list *); @@ -109,7 +110,6 @@ extern int zfcp_fsf_req_create(struct zfcp_adapter *, u32, int, mempool_t *, extern int zfcp_fsf_send_ct(struct zfcp_send_ct *, mempool_t *, struct zfcp_erp_action *); extern int zfcp_fsf_send_els(struct zfcp_send_els *); -extern int zfcp_fsf_req_wait_and_cleanup(struct zfcp_fsf_req *, int, u32 *); extern int zfcp_fsf_send_fcp_command_task(struct zfcp_adapter *, struct zfcp_unit *, struct scsi_cmnd *, @@ -182,9 +182,25 @@ extern void zfcp_erp_port_access_changed(struct zfcp_port *); extern void zfcp_erp_unit_access_changed(struct zfcp_unit *); /******************************** AUX ****************************************/ -extern void zfcp_cmd_dbf_event_fsf(const char *, struct zfcp_fsf_req *, - void *, int); -extern void zfcp_cmd_dbf_event_scsi(const char *, struct scsi_cmnd *); -extern void zfcp_in_els_dbf_event(struct zfcp_adapter *, const char *, - struct fsf_status_read_buffer *, int); +extern void zfcp_hba_dbf_event_fsf_response(struct zfcp_fsf_req *); +extern void zfcp_hba_dbf_event_fsf_unsol(const char *, struct zfcp_adapter *, + struct fsf_status_read_buffer *); +extern void zfcp_hba_dbf_event_qdio(struct zfcp_adapter *, + unsigned int, unsigned int, unsigned int, + int, int); + +extern void zfcp_san_dbf_event_ct_request(struct zfcp_fsf_req *); +extern void zfcp_san_dbf_event_ct_response(struct zfcp_fsf_req *); +extern void zfcp_san_dbf_event_els_request(struct zfcp_fsf_req *); +extern void zfcp_san_dbf_event_els_response(struct zfcp_fsf_req *); +extern void zfcp_san_dbf_event_incoming_els(struct zfcp_fsf_req *); + +extern void zfcp_scsi_dbf_event_result(const char *, int, struct zfcp_adapter *, + struct scsi_cmnd *); +extern void zfcp_scsi_dbf_event_abort(const char *, struct zfcp_adapter *, + struct scsi_cmnd *, + struct zfcp_fsf_req *); +extern void zfcp_scsi_dbf_event_devreset(const char *, u8, struct zfcp_unit *, + struct scsi_cmnd *); + #endif /* ZFCP_EXT_H */ diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index c007b6424e74..3b0fc1163f5f 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -59,6 +59,8 @@ static int zfcp_fsf_req_send(struct zfcp_fsf_req *, struct timer_list *); static int zfcp_fsf_protstatus_eval(struct zfcp_fsf_req *); static int zfcp_fsf_fsfstatus_eval(struct zfcp_fsf_req *); static int zfcp_fsf_fsfstatus_qual_eval(struct zfcp_fsf_req *); +static void zfcp_fsf_link_down_info_eval(struct zfcp_adapter *, + struct fsf_link_down_info *); static int zfcp_fsf_req_dispatch(struct zfcp_fsf_req *); static void zfcp_fsf_req_dismiss(struct zfcp_fsf_req *); @@ -285,51 +287,51 @@ zfcp_fsf_protstatus_eval(struct zfcp_fsf_req *fsf_req) { int retval = 0; struct zfcp_adapter *adapter = fsf_req->adapter; + struct fsf_qtcb *qtcb = fsf_req->qtcb; + union fsf_prot_status_qual *prot_status_qual = + &qtcb->prefix.prot_status_qual; - ZFCP_LOG_DEBUG("QTCB is at %p\n", fsf_req->qtcb); + zfcp_hba_dbf_event_fsf_response(fsf_req); if (fsf_req->status & ZFCP_STATUS_FSFREQ_DISMISSED) { ZFCP_LOG_DEBUG("fsf_req 0x%lx has been dismissed\n", (unsigned long) fsf_req); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR | ZFCP_STATUS_FSFREQ_RETRY; /* only for SCSI cmnds. */ - zfcp_cmd_dbf_event_fsf("dismiss", fsf_req, NULL, 0); goto skip_protstatus; } /* log additional information provided by FSF (if any) */ - if (unlikely(fsf_req->qtcb->header.log_length)) { + if (unlikely(qtcb->header.log_length)) { /* do not trust them ;-) */ - if (fsf_req->qtcb->header.log_start > sizeof(struct fsf_qtcb)) { + if (qtcb->header.log_start > sizeof(struct fsf_qtcb)) { ZFCP_LOG_NORMAL ("bug: ULP (FSF logging) log data starts " "beyond end of packet header. Ignored. " "(start=%i, size=%li)\n", - fsf_req->qtcb->header.log_start, + qtcb->header.log_start, sizeof(struct fsf_qtcb)); goto forget_log; } - if ((size_t) (fsf_req->qtcb->header.log_start + - fsf_req->qtcb->header.log_length) + if ((size_t) (qtcb->header.log_start + qtcb->header.log_length) > sizeof(struct fsf_qtcb)) { ZFCP_LOG_NORMAL("bug: ULP (FSF logging) log data ends " "beyond end of packet header. Ignored. " "(start=%i, length=%i, size=%li)\n", - fsf_req->qtcb->header.log_start, - fsf_req->qtcb->header.log_length, + qtcb->header.log_start, + qtcb->header.log_length, sizeof(struct fsf_qtcb)); goto forget_log; } ZFCP_LOG_TRACE("ULP log data: \n"); ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_TRACE, - (char *) fsf_req->qtcb + - fsf_req->qtcb->header.log_start, - fsf_req->qtcb->header.log_length); + (char *) qtcb + qtcb->header.log_start, + qtcb->header.log_length); } forget_log: /* evaluate FSF Protocol Status */ - switch (fsf_req->qtcb->prefix.prot_status) { + switch (qtcb->prefix.prot_status) { case FSF_PROT_GOOD: case FSF_PROT_FSF_STATUS_PRESENTED: @@ -340,14 +342,9 @@ zfcp_fsf_protstatus_eval(struct zfcp_fsf_req *fsf_req) "microcode of version 0x%x, the device driver " "only supports 0x%x. Aborting.\n", zfcp_get_busid_by_adapter(adapter), - fsf_req->qtcb->prefix.prot_status_qual. - version_error.fsf_version, ZFCP_QTCB_VERSION); - /* stop operation for this adapter */ - debug_text_exception(adapter->erp_dbf, 0, "prot_ver_err"); + prot_status_qual->version_error.fsf_version, + ZFCP_QTCB_VERSION); zfcp_erp_adapter_shutdown(adapter, 0); - zfcp_cmd_dbf_event_fsf("qverserr", fsf_req, - &fsf_req->qtcb->prefix.prot_status_qual, - sizeof (union fsf_prot_status_qual)); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; @@ -355,16 +352,10 @@ zfcp_fsf_protstatus_eval(struct zfcp_fsf_req *fsf_req) ZFCP_LOG_NORMAL("bug: Sequence number mismatch between " "driver (0x%x) and adapter %s (0x%x). " "Restarting all operations on this adapter.\n", - fsf_req->qtcb->prefix.req_seq_no, + qtcb->prefix.req_seq_no, zfcp_get_busid_by_adapter(adapter), - fsf_req->qtcb->prefix.prot_status_qual. - sequence_error.exp_req_seq_no); - debug_text_exception(adapter->erp_dbf, 0, "prot_seq_err"); - /* restart operation on this adapter */ + prot_status_qual->sequence_error.exp_req_seq_no); zfcp_erp_adapter_reopen(adapter, 0); - zfcp_cmd_dbf_event_fsf("seqnoerr", fsf_req, - &fsf_req->qtcb->prefix.prot_status_qual, - sizeof (union fsf_prot_status_qual)); fsf_req->status |= ZFCP_STATUS_FSFREQ_RETRY; fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; @@ -375,116 +366,35 @@ zfcp_fsf_protstatus_eval(struct zfcp_fsf_req *fsf_req) "that used on adapter %s. " "Stopping all operations on this adapter.\n", zfcp_get_busid_by_adapter(adapter)); - debug_text_exception(adapter->erp_dbf, 0, "prot_unsup_qtcb"); zfcp_erp_adapter_shutdown(adapter, 0); - zfcp_cmd_dbf_event_fsf("unsqtcbt", fsf_req, - &fsf_req->qtcb->prefix.prot_status_qual, - sizeof (union fsf_prot_status_qual)); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_PROT_HOST_CONNECTION_INITIALIZING: - zfcp_cmd_dbf_event_fsf("hconinit", fsf_req, - &fsf_req->qtcb->prefix.prot_status_qual, - sizeof (union fsf_prot_status_qual)); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; atomic_set_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT, &(adapter->status)); - debug_text_event(adapter->erp_dbf, 3, "prot_con_init"); break; case FSF_PROT_DUPLICATE_REQUEST_ID: - if (fsf_req->qtcb) { ZFCP_LOG_NORMAL("bug: The request identifier 0x%Lx " "to the adapter %s is ambiguous. " - "Stopping all operations on this " - "adapter.\n", - *(unsigned long long *) - (&fsf_req->qtcb->bottom.support. - req_handle), - zfcp_get_busid_by_adapter(adapter)); - } else { - ZFCP_LOG_NORMAL("bug: The request identifier %p " - "to the adapter %s is ambiguous. " - "Stopping all operations on this " - "adapter. " - "(bug: got this for an unsolicited " - "status read request)\n", - fsf_req, + "Stopping all operations on this adapter.\n", + *(unsigned long long*) + (&qtcb->bottom.support.req_handle), zfcp_get_busid_by_adapter(adapter)); - } - debug_text_exception(adapter->erp_dbf, 0, "prot_dup_id"); zfcp_erp_adapter_shutdown(adapter, 0); - zfcp_cmd_dbf_event_fsf("dupreqid", fsf_req, - &fsf_req->qtcb->prefix.prot_status_qual, - sizeof (union fsf_prot_status_qual)); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_PROT_LINK_DOWN: - /* - * 'test and set' is not atomic here - - * it's ok as long as calls to our response queue handler - * (and thus execution of this code here) are serialized - * by the qdio module - */ - if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, - &adapter->status)) { - switch (fsf_req->qtcb->prefix.prot_status_qual. - locallink_error.code) { - case FSF_PSQ_LINK_NOLIGHT: - ZFCP_LOG_INFO("The local link to adapter %s " - "is down (no light detected).\n", - zfcp_get_busid_by_adapter( - adapter)); - break; - case FSF_PSQ_LINK_WRAPPLUG: - ZFCP_LOG_INFO("The local link to adapter %s " - "is down (wrap plug detected).\n", - zfcp_get_busid_by_adapter( - adapter)); - break; - case FSF_PSQ_LINK_NOFCP: - ZFCP_LOG_INFO("The local link to adapter %s " - "is down (adjacent node on " - "link does not support FCP).\n", - zfcp_get_busid_by_adapter( - adapter)); - break; - default: - ZFCP_LOG_INFO("The local link to adapter %s " - "is down " - "(warning: unknown reason " - "code).\n", - zfcp_get_busid_by_adapter( - adapter)); - break; - - } - /* - * Due to the 'erp failed' flag the adapter won't - * be recovered but will be just set to 'blocked' - * state. All subordinary devices will have state - * 'blocked' and 'erp failed', too. - * Thus the adapter is still able to provide - * 'link up' status without being flooded with - * requests. - * (note: even 'close port' is not permitted) - */ - ZFCP_LOG_INFO("Stopping all operations for adapter " - "%s.\n", - zfcp_get_busid_by_adapter(adapter)); - atomic_set_mask(ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED | - ZFCP_STATUS_COMMON_ERP_FAILED, - &adapter->status); - zfcp_erp_adapter_reopen(adapter, 0); - } + zfcp_fsf_link_down_info_eval(adapter, + &prot_status_qual->link_down_info); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_PROT_REEST_QUEUE: - debug_text_event(adapter->erp_dbf, 1, "prot_reest_queue"); - ZFCP_LOG_INFO("The local link to adapter with " + ZFCP_LOG_NORMAL("The local link to adapter with " "%s was re-plugged. " "Re-starting operations on this adapter.\n", zfcp_get_busid_by_adapter(adapter)); @@ -495,9 +405,6 @@ zfcp_fsf_protstatus_eval(struct zfcp_fsf_req *fsf_req) zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED | ZFCP_STATUS_COMMON_ERP_FAILED); - zfcp_cmd_dbf_event_fsf("reestque", fsf_req, - &fsf_req->qtcb->prefix.prot_status_qual, - sizeof (union fsf_prot_status_qual)); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; @@ -507,12 +414,7 @@ zfcp_fsf_protstatus_eval(struct zfcp_fsf_req *fsf_req) "Restarting all operations on this " "adapter.\n", zfcp_get_busid_by_adapter(adapter)); - debug_text_event(adapter->erp_dbf, 0, "prot_err_sta"); - /* restart operation on this adapter */ zfcp_erp_adapter_reopen(adapter, 0); - zfcp_cmd_dbf_event_fsf("proterrs", fsf_req, - &fsf_req->qtcb->prefix.prot_status_qual, - sizeof (union fsf_prot_status_qual)); fsf_req->status |= ZFCP_STATUS_FSFREQ_RETRY; fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; @@ -524,11 +426,7 @@ zfcp_fsf_protstatus_eval(struct zfcp_fsf_req *fsf_req) "Stopping all operations on this adapter. " "(debug info 0x%x).\n", zfcp_get_busid_by_adapter(adapter), - fsf_req->qtcb->prefix.prot_status); - debug_text_event(adapter->erp_dbf, 0, "prot_inval:"); - debug_exception(adapter->erp_dbf, 0, - &fsf_req->qtcb->prefix.prot_status, - sizeof (u32)); + qtcb->prefix.prot_status); zfcp_erp_adapter_shutdown(adapter, 0); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; } @@ -568,28 +466,18 @@ zfcp_fsf_fsfstatus_eval(struct zfcp_fsf_req *fsf_req) "(debug info 0x%x).\n", zfcp_get_busid_by_adapter(fsf_req->adapter), fsf_req->qtcb->header.fsf_command); - debug_text_exception(fsf_req->adapter->erp_dbf, 0, - "fsf_s_unknown"); zfcp_erp_adapter_shutdown(fsf_req->adapter, 0); - zfcp_cmd_dbf_event_fsf("unknownc", fsf_req, - &fsf_req->qtcb->header.fsf_status_qual, - sizeof (union fsf_status_qual)); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_FCP_RSP_AVAILABLE: ZFCP_LOG_DEBUG("FCP Sense data will be presented to the " "SCSI stack.\n"); - debug_text_event(fsf_req->adapter->erp_dbf, 3, "fsf_s_rsp"); break; case FSF_ADAPTER_STATUS_AVAILABLE: - debug_text_event(fsf_req->adapter->erp_dbf, 2, "fsf_s_astatus"); zfcp_fsf_fsfstatus_qual_eval(fsf_req); break; - - default: - break; } skip_fsfstatus: @@ -617,44 +505,28 @@ zfcp_fsf_fsfstatus_qual_eval(struct zfcp_fsf_req *fsf_req) switch (fsf_req->qtcb->header.fsf_status_qual.word[0]) { case FSF_SQ_FCP_RSP_AVAILABLE: - debug_text_event(fsf_req->adapter->erp_dbf, 4, "fsf_sq_rsp"); break; case FSF_SQ_RETRY_IF_POSSIBLE: /* The SCSI-stack may now issue retries or escalate */ - debug_text_event(fsf_req->adapter->erp_dbf, 2, "fsf_sq_retry"); - zfcp_cmd_dbf_event_fsf("sqretry", fsf_req, - &fsf_req->qtcb->header.fsf_status_qual, - sizeof (union fsf_status_qual)); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_SQ_COMMAND_ABORTED: /* Carry the aborted state on to upper layer */ - debug_text_event(fsf_req->adapter->erp_dbf, 2, "fsf_sq_abort"); - zfcp_cmd_dbf_event_fsf("sqabort", fsf_req, - &fsf_req->qtcb->header.fsf_status_qual, - sizeof (union fsf_status_qual)); fsf_req->status |= ZFCP_STATUS_FSFREQ_ABORTED; fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_SQ_NO_RECOM: - debug_text_exception(fsf_req->adapter->erp_dbf, 0, - "fsf_sq_no_rec"); ZFCP_LOG_NORMAL("bug: No recommendation could be given for a" "problem on the adapter %s " "Stopping all operations on this adapter. ", zfcp_get_busid_by_adapter(fsf_req->adapter)); zfcp_erp_adapter_shutdown(fsf_req->adapter, 0); - zfcp_cmd_dbf_event_fsf("sqnrecom", fsf_req, - &fsf_req->qtcb->header.fsf_status_qual, - sizeof (union fsf_status_qual)); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_SQ_ULP_PROGRAMMING_ERROR: ZFCP_LOG_NORMAL("error: not enough SBALs for data transfer " "(adapter %s)\n", zfcp_get_busid_by_adapter(fsf_req->adapter)); - debug_text_exception(fsf_req->adapter->erp_dbf, 0, - "fsf_sq_ulp_err"); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE: @@ -668,13 +540,6 @@ zfcp_fsf_fsfstatus_qual_eval(struct zfcp_fsf_req *fsf_req) ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL, (char *) &fsf_req->qtcb->header.fsf_status_qual, sizeof (union fsf_status_qual)); - debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf_sq_inval:"); - debug_exception(fsf_req->adapter->erp_dbf, 0, - &fsf_req->qtcb->header.fsf_status_qual.word[0], - sizeof (u32)); - zfcp_cmd_dbf_event_fsf("squndef", fsf_req, - &fsf_req->qtcb->header.fsf_status_qual, - sizeof (union fsf_status_qual)); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; } @@ -682,6 +547,110 @@ zfcp_fsf_fsfstatus_qual_eval(struct zfcp_fsf_req *fsf_req) return retval; } +/** + * zfcp_fsf_link_down_info_eval - evaluate link down information block + */ +static void +zfcp_fsf_link_down_info_eval(struct zfcp_adapter *adapter, + struct fsf_link_down_info *link_down) +{ + switch (link_down->error_code) { + case FSF_PSQ_LINK_NO_LIGHT: + ZFCP_LOG_NORMAL("The local link to adapter %s is down " + "(no light detected)\n", + zfcp_get_busid_by_adapter(adapter)); + break; + case FSF_PSQ_LINK_WRAP_PLUG: + ZFCP_LOG_NORMAL("The local link to adapter %s is down " + "(wrap plug detected)\n", + zfcp_get_busid_by_adapter(adapter)); + break; + case FSF_PSQ_LINK_NO_FCP: + ZFCP_LOG_NORMAL("The local link to adapter %s is down " + "(adjacent node on link does not support FCP)\n", + zfcp_get_busid_by_adapter(adapter)); + break; + case FSF_PSQ_LINK_FIRMWARE_UPDATE: + ZFCP_LOG_NORMAL("The local link to adapter %s is down " + "(firmware update in progress)\n", + zfcp_get_busid_by_adapter(adapter)); + break; + case FSF_PSQ_LINK_INVALID_WWPN: + ZFCP_LOG_NORMAL("The local link to adapter %s is down " + "(duplicate or invalid WWPN detected)\n", + zfcp_get_busid_by_adapter(adapter)); + break; + case FSF_PSQ_LINK_NO_NPIV_SUPPORT: + ZFCP_LOG_NORMAL("The local link to adapter %s is down " + "(no support for NPIV by Fabric)\n", + zfcp_get_busid_by_adapter(adapter)); + break; + case FSF_PSQ_LINK_NO_FCP_RESOURCES: + ZFCP_LOG_NORMAL("The local link to adapter %s is down " + "(out of resource in FCP daughtercard)\n", + zfcp_get_busid_by_adapter(adapter)); + break; + case FSF_PSQ_LINK_NO_FABRIC_RESOURCES: + ZFCP_LOG_NORMAL("The local link to adapter %s is down " + "(out of resource in Fabric)\n", + zfcp_get_busid_by_adapter(adapter)); + break; + case FSF_PSQ_LINK_FABRIC_LOGIN_UNABLE: + ZFCP_LOG_NORMAL("The local link to adapter %s is down " + "(unable to Fabric login)\n", + zfcp_get_busid_by_adapter(adapter)); + break; + case FSF_PSQ_LINK_WWPN_ASSIGNMENT_CORRUPTED: + ZFCP_LOG_NORMAL("WWPN assignment file corrupted on adapter %s\n", + zfcp_get_busid_by_adapter(adapter)); + break; + case FSF_PSQ_LINK_MODE_TABLE_CURRUPTED: + ZFCP_LOG_NORMAL("Mode table corrupted on adapter %s\n", + zfcp_get_busid_by_adapter(adapter)); + break; + case FSF_PSQ_LINK_NO_WWPN_ASSIGNMENT: + ZFCP_LOG_NORMAL("No WWPN for assignment table on adapter %s\n", + zfcp_get_busid_by_adapter(adapter)); + break; + default: + ZFCP_LOG_NORMAL("The local link to adapter %s is down " + "(warning: unknown reason code %d)\n", + zfcp_get_busid_by_adapter(adapter), + link_down->error_code); + } + + if (adapter->connection_features & FSF_FEATURE_NPIV_MODE) + ZFCP_LOG_DEBUG("Debug information to link down: " + "primary_status=0x%02x " + "ioerr_code=0x%02x " + "action_code=0x%02x " + "reason_code=0x%02x " + "explanation_code=0x%02x " + "vendor_specific_code=0x%02x\n", + link_down->primary_status, + link_down->ioerr_code, + link_down->action_code, + link_down->reason_code, + link_down->explanation_code, + link_down->vendor_specific_code); + + if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, + &adapter->status)) { + atomic_set_mask(ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, + &adapter->status); + switch (link_down->error_code) { + case FSF_PSQ_LINK_NO_LIGHT: + case FSF_PSQ_LINK_WRAP_PLUG: + case FSF_PSQ_LINK_NO_FCP: + case FSF_PSQ_LINK_FIRMWARE_UPDATE: + zfcp_erp_adapter_reopen(adapter, 0); + break; + default: + zfcp_erp_adapter_failed(adapter); + } + } +} + /* * function: zfcp_fsf_req_dispatch * @@ -696,11 +665,6 @@ zfcp_fsf_req_dispatch(struct zfcp_fsf_req *fsf_req) struct zfcp_adapter *adapter = fsf_req->adapter; int retval = 0; - if (unlikely(fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR)) { - ZFCP_LOG_TRACE("fsf_req=%p, QTCB=%p\n", fsf_req, fsf_req->qtcb); - ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_TRACE, - (char *) fsf_req->qtcb, sizeof(struct fsf_qtcb)); - } switch (fsf_req->fsf_command) { @@ -760,13 +724,13 @@ zfcp_fsf_req_dispatch(struct zfcp_fsf_req *fsf_req) fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; ZFCP_LOG_NORMAL("bug: Command issued by the device driver is " "not supported by the adapter %s\n", - zfcp_get_busid_by_adapter(fsf_req->adapter)); + zfcp_get_busid_by_adapter(adapter)); if (fsf_req->fsf_command != fsf_req->qtcb->header.fsf_command) ZFCP_LOG_NORMAL ("bug: Command issued by the device driver differs " "from the command returned by the adapter %s " "(debug info 0x%x, 0x%x).\n", - zfcp_get_busid_by_adapter(fsf_req->adapter), + zfcp_get_busid_by_adapter(adapter), fsf_req->fsf_command, fsf_req->qtcb->header.fsf_command); } @@ -774,8 +738,6 @@ zfcp_fsf_req_dispatch(struct zfcp_fsf_req *fsf_req) if (!erp_action) return retval; - debug_text_event(adapter->erp_dbf, 3, "a_frh"); - debug_event(adapter->erp_dbf, 3, &erp_action->action, sizeof (int)); zfcp_erp_async_handler(erp_action, 0); return retval; @@ -821,7 +783,7 @@ zfcp_fsf_status_read(struct zfcp_adapter *adapter, int req_flags) goto failed_buf; } memset(status_buffer, 0, sizeof (struct fsf_status_read_buffer)); - fsf_req->data.status_read.buffer = status_buffer; + fsf_req->data = (unsigned long) status_buffer; /* insert pointer to respective buffer */ sbale = zfcp_qdio_sbale_curr(fsf_req); @@ -846,6 +808,7 @@ zfcp_fsf_status_read(struct zfcp_adapter *adapter, int req_flags) failed_buf: zfcp_fsf_req_free(fsf_req); failed_req_create: + zfcp_hba_dbf_event_fsf_unsol("fail", adapter, NULL); out: write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags); return retval; @@ -859,7 +822,7 @@ zfcp_fsf_status_read_port_closed(struct zfcp_fsf_req *fsf_req) struct zfcp_port *port; unsigned long flags; - status_buffer = fsf_req->data.status_read.buffer; + status_buffer = (struct fsf_status_read_buffer *) fsf_req->data; adapter = fsf_req->adapter; read_lock_irqsave(&zfcp_data.config_lock, flags); @@ -918,38 +881,33 @@ zfcp_fsf_status_read_handler(struct zfcp_fsf_req *fsf_req) int retval = 0; struct zfcp_adapter *adapter = fsf_req->adapter; struct fsf_status_read_buffer *status_buffer = - fsf_req->data.status_read.buffer; + (struct fsf_status_read_buffer *) fsf_req->data; if (fsf_req->status & ZFCP_STATUS_FSFREQ_DISMISSED) { + zfcp_hba_dbf_event_fsf_unsol("dism", adapter, status_buffer); mempool_free(status_buffer, adapter->pool.data_status_read); zfcp_fsf_req_free(fsf_req); goto out; } + zfcp_hba_dbf_event_fsf_unsol("read", adapter, status_buffer); + switch (status_buffer->status_type) { case FSF_STATUS_READ_PORT_CLOSED: - debug_text_event(adapter->erp_dbf, 3, "unsol_pclosed:"); - debug_event(adapter->erp_dbf, 3, - &status_buffer->d_id, sizeof (u32)); zfcp_fsf_status_read_port_closed(fsf_req); break; case FSF_STATUS_READ_INCOMING_ELS: - debug_text_event(adapter->erp_dbf, 3, "unsol_els:"); zfcp_fsf_incoming_els(fsf_req); break; case FSF_STATUS_READ_SENSE_DATA_AVAIL: - debug_text_event(adapter->erp_dbf, 3, "unsol_sense:"); ZFCP_LOG_INFO("unsolicited sense data received (adapter %s)\n", zfcp_get_busid_by_adapter(adapter)); - ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL, (char *) status_buffer, - sizeof(struct fsf_status_read_buffer)); break; case FSF_STATUS_READ_BIT_ERROR_THRESHOLD: - debug_text_event(adapter->erp_dbf, 3, "unsol_bit_err:"); ZFCP_LOG_NORMAL("Bit error threshold data received:\n"); ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL, (char *) status_buffer, @@ -957,17 +915,32 @@ zfcp_fsf_status_read_handler(struct zfcp_fsf_req *fsf_req) break; case FSF_STATUS_READ_LINK_DOWN: - debug_text_event(adapter->erp_dbf, 0, "unsol_link_down:"); - ZFCP_LOG_INFO("Local link to adapter %s is down\n", + switch (status_buffer->status_subtype) { + case FSF_STATUS_READ_SUB_NO_PHYSICAL_LINK: + ZFCP_LOG_INFO("Physical link to adapter %s is down\n", + zfcp_get_busid_by_adapter(adapter)); + break; + case FSF_STATUS_READ_SUB_FDISC_FAILED: + ZFCP_LOG_INFO("Local link to adapter %s is down " + "due to failed FDISC login\n", zfcp_get_busid_by_adapter(adapter)); - atomic_set_mask(ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, - &adapter->status); - zfcp_erp_adapter_failed(adapter); + break; + case FSF_STATUS_READ_SUB_FIRMWARE_UPDATE: + ZFCP_LOG_INFO("Local link to adapter %s is down " + "due to firmware update on adapter\n", + zfcp_get_busid_by_adapter(adapter)); + break; + default: + ZFCP_LOG_INFO("Local link to adapter %s is down " + "due to unknown reason\n", + zfcp_get_busid_by_adapter(adapter)); + }; + zfcp_fsf_link_down_info_eval(adapter, + (struct fsf_link_down_info *) &status_buffer->payload); break; case FSF_STATUS_READ_LINK_UP: - debug_text_event(adapter->erp_dbf, 2, "unsol_link_up:"); - ZFCP_LOG_INFO("Local link to adapter %s was replugged. " + ZFCP_LOG_NORMAL("Local link to adapter %s was replugged. " "Restarting operations on this adapter\n", zfcp_get_busid_by_adapter(adapter)); /* All ports should be marked as ready to run again */ @@ -980,35 +953,40 @@ zfcp_fsf_status_read_handler(struct zfcp_fsf_req *fsf_req) break; case FSF_STATUS_READ_CFDC_UPDATED: - debug_text_event(adapter->erp_dbf, 2, "unsol_cfdc_update:"); - ZFCP_LOG_INFO("CFDC has been updated on the adapter %s\n", + ZFCP_LOG_NORMAL("CFDC has been updated on the adapter %s\n", zfcp_get_busid_by_adapter(adapter)); zfcp_erp_adapter_access_changed(adapter); break; case FSF_STATUS_READ_CFDC_HARDENED: - debug_text_event(adapter->erp_dbf, 2, "unsol_cfdc_harden:"); switch (status_buffer->status_subtype) { case FSF_STATUS_READ_SUB_CFDC_HARDENED_ON_SE: - ZFCP_LOG_INFO("CFDC of adapter %s saved on SE\n", + ZFCP_LOG_NORMAL("CFDC of adapter %s saved on SE\n", zfcp_get_busid_by_adapter(adapter)); break; case FSF_STATUS_READ_SUB_CFDC_HARDENED_ON_SE2: - ZFCP_LOG_INFO("CFDC of adapter %s has been copied " + ZFCP_LOG_NORMAL("CFDC of adapter %s has been copied " "to the secondary SE\n", zfcp_get_busid_by_adapter(adapter)); break; default: - ZFCP_LOG_INFO("CFDC of adapter %s has been hardened\n", + ZFCP_LOG_NORMAL("CFDC of adapter %s has been hardened\n", zfcp_get_busid_by_adapter(adapter)); } break; + case FSF_STATUS_READ_FEATURE_UPDATE_ALERT: + debug_text_event(adapter->erp_dbf, 2, "unsol_features:"); + ZFCP_LOG_INFO("List of supported features on adapter %s has " + "been changed from 0x%08X to 0x%08X\n", + zfcp_get_busid_by_adapter(adapter), + *(u32*) (status_buffer->payload + 4), + *(u32*) (status_buffer->payload)); + adapter->adapter_features = *(u32*) status_buffer->payload; + break; + default: - debug_text_event(adapter->erp_dbf, 0, "unsol_unknown:"); - debug_exception(adapter->erp_dbf, 0, - &status_buffer->status_type, sizeof (u32)); - ZFCP_LOG_NORMAL("bug: An unsolicited status packet of unknown " + ZFCP_LOG_NORMAL("warning: An unsolicited status packet of unknown " "type was received (debug info 0x%x)\n", status_buffer->status_type); ZFCP_LOG_DEBUG("Dump of status_read_buffer %p:\n", @@ -1093,7 +1071,7 @@ zfcp_fsf_abort_fcp_command(unsigned long old_req_id, sbale[0].flags |= SBAL_FLAGS0_TYPE_READ; sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; - fsf_req->data.abort_fcp_command.unit = unit; + fsf_req->data = (unsigned long) unit; /* set handles of unit and its parent port in QTCB */ fsf_req->qtcb->header.lun_handle = unit->handle; @@ -1139,7 +1117,7 @@ static int zfcp_fsf_abort_fcp_command_handler(struct zfcp_fsf_req *new_fsf_req) { int retval = -EINVAL; - struct zfcp_unit *unit = new_fsf_req->data.abort_fcp_command.unit; + struct zfcp_unit *unit; unsigned char status_qual = new_fsf_req->qtcb->header.fsf_status_qual.word[0]; @@ -1150,6 +1128,8 @@ zfcp_fsf_abort_fcp_command_handler(struct zfcp_fsf_req *new_fsf_req) goto skip_fsfstatus; } + unit = (struct zfcp_unit *) new_fsf_req->data; + /* evaluate FSF status in QTCB */ switch (new_fsf_req->qtcb->header.fsf_status) { @@ -1364,7 +1344,7 @@ zfcp_fsf_send_ct(struct zfcp_send_ct *ct, mempool_t *pool, sbale[3].addr = zfcp_sg_to_address(&ct->resp[0]); sbale[3].length = ct->resp[0].length; sbale[3].flags |= SBAL_FLAGS_LAST_ENTRY; - } else if (adapter->supported_features & + } else if (adapter->adapter_features & FSF_FEATURE_ELS_CT_CHAINED_SBALS) { /* try to use chained SBALs */ bytes = zfcp_qdio_sbals_from_sg(fsf_req, @@ -1414,7 +1394,9 @@ zfcp_fsf_send_ct(struct zfcp_send_ct *ct, mempool_t *pool, fsf_req->qtcb->header.port_handle = port->handle; fsf_req->qtcb->bottom.support.service_class = adapter->fc_service_class; fsf_req->qtcb->bottom.support.timeout = ct->timeout; - fsf_req->data.send_ct = ct; + fsf_req->data = (unsigned long) ct; + + zfcp_san_dbf_event_ct_request(fsf_req); /* start QDIO request for this FSF request */ ret = zfcp_fsf_req_send(fsf_req, ct->timer); @@ -1445,10 +1427,10 @@ zfcp_fsf_send_ct(struct zfcp_send_ct *ct, mempool_t *pool, * zfcp_fsf_send_ct_handler - handler for Generic Service requests * @fsf_req: pointer to struct zfcp_fsf_req * - * Data specific for the Generic Service request is passed by - * fsf_req->data.send_ct - * Usually a specific handler for the request is called via - * fsf_req->data.send_ct->handler at end of this function. + * Data specific for the Generic Service request is passed using + * fsf_req->data. There we find the pointer to struct zfcp_send_ct. + * Usually a specific handler for the CT request is called which is + * found in this structure. */ static int zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *fsf_req) @@ -1462,7 +1444,7 @@ zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *fsf_req) u16 subtable, rule, counter; adapter = fsf_req->adapter; - send_ct = fsf_req->data.send_ct; + send_ct = (struct zfcp_send_ct *) fsf_req->data; port = send_ct->port; header = &fsf_req->qtcb->header; bottom = &fsf_req->qtcb->bottom.support; @@ -1474,6 +1456,7 @@ zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *fsf_req) switch (header->fsf_status) { case FSF_GOOD: + zfcp_san_dbf_event_ct_response(fsf_req); retval = 0; break; @@ -1634,7 +1617,7 @@ zfcp_fsf_send_els(struct zfcp_send_els *els) { volatile struct qdio_buffer_element *sbale; struct zfcp_fsf_req *fsf_req; - fc_id_t d_id; + u32 d_id; struct zfcp_adapter *adapter; unsigned long lock_flags; int bytes; @@ -1664,7 +1647,7 @@ zfcp_fsf_send_els(struct zfcp_send_els *els) sbale[3].addr = zfcp_sg_to_address(&els->resp[0]); sbale[3].length = els->resp[0].length; sbale[3].flags |= SBAL_FLAGS_LAST_ENTRY; - } else if (adapter->supported_features & + } else if (adapter->adapter_features & FSF_FEATURE_ELS_CT_CHAINED_SBALS) { /* try to use chained SBALs */ bytes = zfcp_qdio_sbals_from_sg(fsf_req, @@ -1714,10 +1697,12 @@ zfcp_fsf_send_els(struct zfcp_send_els *els) fsf_req->qtcb->bottom.support.d_id = d_id; fsf_req->qtcb->bottom.support.service_class = adapter->fc_service_class; fsf_req->qtcb->bottom.support.timeout = ZFCP_ELS_TIMEOUT; - fsf_req->data.send_els = els; + fsf_req->data = (unsigned long) els; sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0); + zfcp_san_dbf_event_els_request(fsf_req); + /* start QDIO request for this FSF request */ ret = zfcp_fsf_req_send(fsf_req, els->timer); if (ret) { @@ -1746,23 +1731,23 @@ zfcp_fsf_send_els(struct zfcp_send_els *els) * zfcp_fsf_send_els_handler - handler for ELS commands * @fsf_req: pointer to struct zfcp_fsf_req * - * Data specific for the ELS command is passed by - * fsf_req->data.send_els - * Usually a specific handler for the command is called via - * fsf_req->data.send_els->handler at end of this function. + * Data specific for the ELS command is passed using + * fsf_req->data. There we find the pointer to struct zfcp_send_els. + * Usually a specific handler for the ELS command is called which is + * found in this structure. */ static int zfcp_fsf_send_els_handler(struct zfcp_fsf_req *fsf_req) { struct zfcp_adapter *adapter; struct zfcp_port *port; - fc_id_t d_id; + u32 d_id; struct fsf_qtcb_header *header; struct fsf_qtcb_bottom_support *bottom; struct zfcp_send_els *send_els; int retval = -EINVAL; u16 subtable, rule, counter; - send_els = fsf_req->data.send_els; + send_els = (struct zfcp_send_els *) fsf_req->data; adapter = send_els->adapter; port = send_els->port; d_id = send_els->d_id; @@ -1775,6 +1760,7 @@ static int zfcp_fsf_send_els_handler(struct zfcp_fsf_req *fsf_req) switch (header->fsf_status) { case FSF_GOOD: + zfcp_san_dbf_event_els_response(fsf_req); retval = 0; break; @@ -1954,7 +1940,9 @@ zfcp_fsf_exchange_config_data(struct zfcp_erp_action *erp_action) erp_action->fsf_req->erp_action = erp_action; erp_action->fsf_req->qtcb->bottom.config.feature_selection = - (FSF_FEATURE_CFDC | FSF_FEATURE_LUN_SHARING); + FSF_FEATURE_CFDC | + FSF_FEATURE_LUN_SHARING | + FSF_FEATURE_UPDATE_ALERT; /* start QDIO request for this FSF request */ retval = zfcp_fsf_req_send(erp_action->fsf_req, &erp_action->timer); @@ -1990,29 +1978,36 @@ zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *fsf_req, int xchg_ok) { struct fsf_qtcb_bottom_config *bottom; struct zfcp_adapter *adapter = fsf_req->adapter; + struct Scsi_Host *shost = adapter->scsi_host; bottom = &fsf_req->qtcb->bottom.config; ZFCP_LOG_DEBUG("low/high QTCB version 0x%x/0x%x of FSF\n", bottom->low_qtcb_version, bottom->high_qtcb_version); adapter->fsf_lic_version = bottom->lic_version; - adapter->supported_features = bottom->supported_features; + adapter->adapter_features = bottom->adapter_features; + adapter->connection_features = bottom->connection_features; adapter->peer_wwpn = 0; adapter->peer_wwnn = 0; adapter->peer_d_id = 0; if (xchg_ok) { - adapter->wwnn = bottom->nport_serv_param.wwnn; - adapter->wwpn = bottom->nport_serv_param.wwpn; - adapter->s_id = bottom->s_id & ZFCP_DID_MASK; + fc_host_node_name(shost) = bottom->nport_serv_param.wwnn; + fc_host_port_name(shost) = bottom->nport_serv_param.wwpn; + fc_host_port_id(shost) = bottom->s_id & ZFCP_DID_MASK; + fc_host_speed(shost) = bottom->fc_link_speed; + fc_host_supported_classes(shost) = FC_COS_CLASS2 | FC_COS_CLASS3; adapter->fc_topology = bottom->fc_topology; - adapter->fc_link_speed = bottom->fc_link_speed; adapter->hydra_version = bottom->adapter_type; + if (adapter->physical_wwpn == 0) + adapter->physical_wwpn = fc_host_port_name(shost); + if (adapter->physical_s_id == 0) + adapter->physical_s_id = fc_host_port_id(shost); } else { - adapter->wwnn = 0; - adapter->wwpn = 0; - adapter->s_id = 0; + fc_host_node_name(shost) = 0; + fc_host_port_name(shost) = 0; + fc_host_port_id(shost) = 0; + fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN; adapter->fc_topology = 0; - adapter->fc_link_speed = 0; adapter->hydra_version = 0; } @@ -2022,26 +2017,28 @@ zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *fsf_req, int xchg_ok) adapter->peer_wwnn = bottom->plogi_payload.wwnn; } - if(adapter->supported_features & FSF_FEATURE_HBAAPI_MANAGEMENT){ + if (adapter->adapter_features & FSF_FEATURE_HBAAPI_MANAGEMENT) { adapter->hardware_version = bottom->hardware_version; - memcpy(adapter->serial_number, bottom->serial_number, 17); - EBCASC(adapter->serial_number, sizeof(adapter->serial_number)); + memcpy(fc_host_serial_number(shost), bottom->serial_number, + min(FC_SERIAL_NUMBER_SIZE, 17)); + EBCASC(fc_host_serial_number(shost), + min(FC_SERIAL_NUMBER_SIZE, 17)); } ZFCP_LOG_NORMAL("The adapter %s reported the following characteristics:\n" - "WWNN 0x%016Lx, " - "WWPN 0x%016Lx, " - "S_ID 0x%08x,\n" - "adapter version 0x%x, " - "LIC version 0x%x, " - "FC link speed %d Gb/s\n", - zfcp_get_busid_by_adapter(adapter), - adapter->wwnn, - adapter->wwpn, - (unsigned int) adapter->s_id, - adapter->hydra_version, - adapter->fsf_lic_version, - adapter->fc_link_speed); + "WWNN 0x%016Lx, " + "WWPN 0x%016Lx, " + "S_ID 0x%08x,\n" + "adapter version 0x%x, " + "LIC version 0x%x, " + "FC link speed %d Gb/s\n", + zfcp_get_busid_by_adapter(adapter), + (wwn_t) fc_host_node_name(shost), + (wwn_t) fc_host_port_name(shost), + fc_host_port_id(shost), + adapter->hydra_version, + adapter->fsf_lic_version, + fc_host_speed(shost)); if (ZFCP_QTCB_VERSION < bottom->low_qtcb_version) { ZFCP_LOG_NORMAL("error: the adapter %s " "only supports newer control block " @@ -2062,7 +2059,6 @@ zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *fsf_req, int xchg_ok) zfcp_erp_adapter_shutdown(adapter, 0); return -EIO; } - zfcp_set_fc_host_attrs(adapter); return 0; } @@ -2078,11 +2074,12 @@ zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *fsf_req) { struct fsf_qtcb_bottom_config *bottom; struct zfcp_adapter *adapter = fsf_req->adapter; + struct fsf_qtcb *qtcb = fsf_req->qtcb; if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) return -EIO; - switch (fsf_req->qtcb->header.fsf_status) { + switch (qtcb->header.fsf_status) { case FSF_GOOD: if (zfcp_fsf_exchange_config_evaluate(fsf_req, 1)) @@ -2112,7 +2109,7 @@ zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *fsf_req) zfcp_erp_adapter_shutdown(adapter, 0); return -EIO; case FSF_TOPO_FABRIC: - ZFCP_LOG_INFO("Switched fabric fibrechannel " + ZFCP_LOG_NORMAL("Switched fabric fibrechannel " "network detected at adapter %s.\n", zfcp_get_busid_by_adapter(adapter)); break; @@ -2130,7 +2127,7 @@ zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *fsf_req) zfcp_erp_adapter_shutdown(adapter, 0); return -EIO; } - bottom = &fsf_req->qtcb->bottom.config; + bottom = &qtcb->bottom.config; if (bottom->max_qtcb_size < sizeof(struct fsf_qtcb)) { ZFCP_LOG_NORMAL("bug: Maximum QTCB size (%d bytes) " "allowed by the adapter %s " @@ -2155,12 +2152,10 @@ zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *fsf_req) if (zfcp_fsf_exchange_config_evaluate(fsf_req, 0)) return -EIO; - ZFCP_LOG_INFO("Local link to adapter %s is down\n", - zfcp_get_busid_by_adapter(adapter)); - atomic_set_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK | - ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, - &adapter->status); - zfcp_erp_adapter_failed(adapter); + atomic_set_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK, &adapter->status); + + zfcp_fsf_link_down_info_eval(adapter, + &qtcb->header.fsf_status_qual.link_down_info); break; default: debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf-stat-ng"); @@ -2174,11 +2169,13 @@ zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *fsf_req) /** * zfcp_fsf_exchange_port_data - request information about local port + * @erp_action: ERP action for the adapter for which port data is requested * @adapter: for which port data is requested * @data: response to exchange port data request */ int -zfcp_fsf_exchange_port_data(struct zfcp_adapter *adapter, +zfcp_fsf_exchange_port_data(struct zfcp_erp_action *erp_action, + struct zfcp_adapter *adapter, struct fsf_qtcb_bottom_port *data) { volatile struct qdio_buffer_element *sbale; @@ -2187,7 +2184,7 @@ zfcp_fsf_exchange_port_data(struct zfcp_adapter *adapter, struct zfcp_fsf_req *fsf_req; struct timer_list *timer; - if(!(adapter->supported_features & FSF_FEATURE_HBAAPI_MANAGEMENT)){ + if (!(adapter->adapter_features & FSF_FEATURE_HBAAPI_MANAGEMENT)) { ZFCP_LOG_INFO("error: exchange port data " "command not supported by adapter %s\n", zfcp_get_busid_by_adapter(adapter)); @@ -2211,12 +2208,18 @@ zfcp_fsf_exchange_port_data(struct zfcp_adapter *adapter, goto out; } + if (erp_action) { + erp_action->fsf_req = fsf_req; + fsf_req->erp_action = erp_action; + } + + if (data) + fsf_req->data = (unsigned long) data; + sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0); sbale[0].flags |= SBAL_FLAGS0_TYPE_READ; sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; - fsf_req->data.port_data = data; - init_timer(timer); timer->function = zfcp_fsf_request_timeout_handler; timer->data = (unsigned long) adapter; @@ -2228,6 +2231,8 @@ zfcp_fsf_exchange_port_data(struct zfcp_adapter *adapter, "command on the adapter %s\n", zfcp_get_busid_by_adapter(adapter)); zfcp_fsf_req_free(fsf_req); + if (erp_action) + erp_action->fsf_req = NULL; write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags); goto out; @@ -2256,21 +2261,42 @@ zfcp_fsf_exchange_port_data(struct zfcp_adapter *adapter, static void zfcp_fsf_exchange_port_data_handler(struct zfcp_fsf_req *fsf_req) { - struct fsf_qtcb_bottom_port *bottom; - struct fsf_qtcb_bottom_port *data = fsf_req->data.port_data; + struct zfcp_adapter *adapter = fsf_req->adapter; + struct Scsi_Host *shost = adapter->scsi_host; + struct fsf_qtcb *qtcb = fsf_req->qtcb; + struct fsf_qtcb_bottom_port *bottom, *data; if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) return; - switch (fsf_req->qtcb->header.fsf_status) { + switch (qtcb->header.fsf_status) { case FSF_GOOD: - bottom = &fsf_req->qtcb->bottom.port; - memcpy(data, bottom, sizeof(*data)); + atomic_set_mask(ZFCP_STATUS_ADAPTER_XPORT_OK, &adapter->status); + + bottom = &qtcb->bottom.port; + data = (struct fsf_qtcb_bottom_port*) fsf_req->data; + if (data) + memcpy(data, bottom, sizeof(struct fsf_qtcb_bottom_port)); + if (adapter->connection_features & FSF_FEATURE_NPIV_MODE) { + adapter->physical_wwpn = bottom->wwpn; + adapter->physical_s_id = bottom->fc_port_id; + } else { + adapter->physical_wwpn = fc_host_port_name(shost); + adapter->physical_s_id = fc_host_port_id(shost); + } + fc_host_maxframe_size(shost) = bottom->maximum_frame_size; + break; + + case FSF_EXCHANGE_CONFIG_DATA_INCOMPLETE: + atomic_set_mask(ZFCP_STATUS_ADAPTER_XPORT_OK, &adapter->status); + + zfcp_fsf_link_down_info_eval(adapter, + &qtcb->header.fsf_status_qual.link_down_info); break; default: - debug_text_event(fsf_req->adapter->erp_dbf, 0, "xchg-port-ng"); - debug_event(fsf_req->adapter->erp_dbf, 0, + debug_text_event(adapter->erp_dbf, 0, "xchg-port-ng"); + debug_event(adapter->erp_dbf, 0, &fsf_req->qtcb->header.fsf_status, sizeof(u32)); } } @@ -2312,7 +2338,7 @@ zfcp_fsf_open_port(struct zfcp_erp_action *erp_action) erp_action->fsf_req->qtcb->bottom.support.d_id = erp_action->port->d_id; atomic_set_mask(ZFCP_STATUS_COMMON_OPENING, &erp_action->port->status); - erp_action->fsf_req->data.open_port.port = erp_action->port; + erp_action->fsf_req->data = (unsigned long) erp_action->port; erp_action->fsf_req->erp_action = erp_action; /* start QDIO request for this FSF request */ @@ -2353,7 +2379,7 @@ zfcp_fsf_open_port_handler(struct zfcp_fsf_req *fsf_req) struct fsf_qtcb_header *header; u16 subtable, rule, counter; - port = fsf_req->data.open_port.port; + port = (struct zfcp_port *) fsf_req->data; header = &fsf_req->qtcb->header; if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) { @@ -2566,7 +2592,7 @@ zfcp_fsf_close_port(struct zfcp_erp_action *erp_action) sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; atomic_set_mask(ZFCP_STATUS_COMMON_CLOSING, &erp_action->port->status); - erp_action->fsf_req->data.close_port.port = erp_action->port; + erp_action->fsf_req->data = (unsigned long) erp_action->port; erp_action->fsf_req->erp_action = erp_action; erp_action->fsf_req->qtcb->header.port_handle = erp_action->port->handle; @@ -2606,7 +2632,7 @@ zfcp_fsf_close_port_handler(struct zfcp_fsf_req *fsf_req) int retval = -EINVAL; struct zfcp_port *port; - port = fsf_req->data.close_port.port; + port = (struct zfcp_port *) fsf_req->data; if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) { /* don't change port status in our bookkeeping */ @@ -2703,8 +2729,8 @@ zfcp_fsf_close_physical_port(struct zfcp_erp_action *erp_action) atomic_set_mask(ZFCP_STATUS_PORT_PHYS_CLOSING, &erp_action->port->status); /* save a pointer to this port */ - erp_action->fsf_req->data.close_physical_port.port = erp_action->port; - /* port to be closeed */ + erp_action->fsf_req->data = (unsigned long) erp_action->port; + /* port to be closed */ erp_action->fsf_req->qtcb->header.port_handle = erp_action->port->handle; erp_action->fsf_req->erp_action = erp_action; @@ -2747,7 +2773,7 @@ zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *fsf_req) struct fsf_qtcb_header *header; u16 subtable, rule, counter; - port = fsf_req->data.close_physical_port.port; + port = (struct zfcp_port *) fsf_req->data; header = &fsf_req->qtcb->header; if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) { @@ -2908,10 +2934,11 @@ zfcp_fsf_open_unit(struct zfcp_erp_action *erp_action) erp_action->port->handle; erp_action->fsf_req->qtcb->bottom.support.fcp_lun = erp_action->unit->fcp_lun; + if (!(erp_action->adapter->connection_features & FSF_FEATURE_NPIV_MODE)) erp_action->fsf_req->qtcb->bottom.support.option = FSF_OPEN_LUN_SUPPRESS_BOXING; atomic_set_mask(ZFCP_STATUS_COMMON_OPENING, &erp_action->unit->status); - erp_action->fsf_req->data.open_unit.unit = erp_action->unit; + erp_action->fsf_req->data = (unsigned long) erp_action->unit; erp_action->fsf_req->erp_action = erp_action; /* start QDIO request for this FSF request */ @@ -2955,9 +2982,9 @@ zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *fsf_req) struct fsf_qtcb_bottom_support *bottom; struct fsf_queue_designator *queue_designator; u16 subtable, rule, counter; - u32 allowed, exclusive, readwrite; + int exclusive, readwrite; - unit = fsf_req->data.open_unit.unit; + unit = (struct zfcp_unit *) fsf_req->data; if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) { /* don't change unit status in our bookkeeping */ @@ -2969,10 +2996,6 @@ zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *fsf_req) bottom = &fsf_req->qtcb->bottom.support; queue_designator = &header->fsf_status_qual.fsf_queue_designator; - allowed = bottom->lun_access_info & FSF_UNIT_ACCESS_OPEN_LUN_ALLOWED; - exclusive = bottom->lun_access_info & FSF_UNIT_ACCESS_EXCLUSIVE; - readwrite = bottom->lun_access_info & FSF_UNIT_ACCESS_OUTBOUND_TRANSFER; - atomic_clear_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED | ZFCP_STATUS_UNIT_SHARED | ZFCP_STATUS_UNIT_READONLY, @@ -3146,10 +3169,15 @@ zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *fsf_req) unit->handle); /* mark unit as open */ atomic_set_mask(ZFCP_STATUS_COMMON_OPEN, &unit->status); - atomic_clear_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED | - ZFCP_STATUS_COMMON_ACCESS_BOXED, - &unit->status); - if (adapter->supported_features & FSF_FEATURE_LUN_SHARING){ + + if (!(adapter->connection_features & FSF_FEATURE_NPIV_MODE) && + (adapter->adapter_features & FSF_FEATURE_LUN_SHARING) && + (adapter->ccw_device->id.dev_model != ZFCP_DEVICE_MODEL_PRIV)) { + exclusive = (bottom->lun_access_info & + FSF_UNIT_ACCESS_EXCLUSIVE); + readwrite = (bottom->lun_access_info & + FSF_UNIT_ACCESS_OUTBOUND_TRANSFER); + if (!exclusive) atomic_set_mask(ZFCP_STATUS_UNIT_SHARED, &unit->status); @@ -3242,7 +3270,7 @@ zfcp_fsf_close_unit(struct zfcp_erp_action *erp_action) erp_action->port->handle; erp_action->fsf_req->qtcb->header.lun_handle = erp_action->unit->handle; atomic_set_mask(ZFCP_STATUS_COMMON_CLOSING, &erp_action->unit->status); - erp_action->fsf_req->data.close_unit.unit = erp_action->unit; + erp_action->fsf_req->data = (unsigned long) erp_action->unit; erp_action->fsf_req->erp_action = erp_action; /* start QDIO request for this FSF request */ @@ -3281,7 +3309,7 @@ zfcp_fsf_close_unit_handler(struct zfcp_fsf_req *fsf_req) int retval = -EINVAL; struct zfcp_unit *unit; - unit = fsf_req->data.close_unit.unit; /* restore unit */ + unit = (struct zfcp_unit *) fsf_req->data; if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) { /* don't change unit status in our bookkeeping */ @@ -3305,9 +3333,6 @@ zfcp_fsf_close_unit_handler(struct zfcp_fsf_req *fsf_req) debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_phand_nv"); zfcp_erp_adapter_reopen(unit->port->adapter, 0); - zfcp_cmd_dbf_event_fsf("porthinv", fsf_req, - &fsf_req->qtcb->header.fsf_status_qual, - sizeof (union fsf_status_qual)); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; @@ -3326,9 +3351,6 @@ zfcp_fsf_close_unit_handler(struct zfcp_fsf_req *fsf_req) debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_lhand_nv"); zfcp_erp_port_reopen(unit->port, 0); - zfcp_cmd_dbf_event_fsf("lunhinv", fsf_req, - &fsf_req->qtcb->header.fsf_status_qual, - sizeof (union fsf_status_qual)); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; @@ -3436,21 +3458,14 @@ zfcp_fsf_send_fcp_command_task(struct zfcp_adapter *adapter, goto failed_req_create; } - /* - * associate FSF request with SCSI request - * (need this for look up on abort) - */ - fsf_req->data.send_fcp_command_task.fsf_req = fsf_req; - scsi_cmnd->host_scribble = (char *) &(fsf_req->data); + zfcp_unit_get(unit); + fsf_req->unit = unit; - /* - * associate SCSI command with FSF request - * (need this for look up on normal command completion) - */ - fsf_req->data.send_fcp_command_task.scsi_cmnd = scsi_cmnd; - fsf_req->data.send_fcp_command_task.start_jiffies = jiffies; - fsf_req->data.send_fcp_command_task.unit = unit; - ZFCP_LOG_DEBUG("unit=%p, fcp_lun=0x%016Lx\n", unit, unit->fcp_lun); + /* associate FSF request with SCSI request (for look up on abort) */ + scsi_cmnd->host_scribble = (char *) fsf_req; + + /* associate SCSI command with FSF request */ + fsf_req->data = (unsigned long) scsi_cmnd; /* set handles of unit and its parent port in QTCB */ fsf_req->qtcb->header.lun_handle = unit->handle; @@ -3584,6 +3599,7 @@ zfcp_fsf_send_fcp_command_task(struct zfcp_adapter *adapter, send_failed: no_fit: failed_scsi_cmnd: + zfcp_unit_put(unit); zfcp_fsf_req_free(fsf_req); fsf_req = NULL; scsi_cmnd->host_scribble = NULL; @@ -3640,7 +3656,7 @@ zfcp_fsf_send_fcp_command_task_management(struct zfcp_adapter *adapter, * hold a pointer to the unit being target of this * task management request */ - fsf_req->data.send_fcp_command_task_management.unit = unit; + fsf_req->data = (unsigned long) unit; /* set FSF related fields in QTCB */ fsf_req->qtcb->header.lun_handle = unit->handle; @@ -3706,9 +3722,9 @@ zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *fsf_req) header = &fsf_req->qtcb->header; if (unlikely(fsf_req->status & ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT)) - unit = fsf_req->data.send_fcp_command_task_management.unit; + unit = (struct zfcp_unit *) fsf_req->data; else - unit = fsf_req->data.send_fcp_command_task.unit; + unit = fsf_req->unit; if (unlikely(fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR)) { /* go directly to calls of special handlers */ @@ -3765,10 +3781,6 @@ zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *fsf_req) debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_hand_mis"); zfcp_erp_adapter_reopen(unit->port->adapter, 0); - zfcp_cmd_dbf_event_fsf("handmism", - fsf_req, - &header->fsf_status_qual, - sizeof (union fsf_status_qual)); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; @@ -3789,10 +3801,6 @@ zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *fsf_req) debug_text_exception(fsf_req->adapter->erp_dbf, 0, "fsf_s_class_nsup"); zfcp_erp_adapter_shutdown(unit->port->adapter, 0); - zfcp_cmd_dbf_event_fsf("unsclass", - fsf_req, - &header->fsf_status_qual, - sizeof (union fsf_status_qual)); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; @@ -3811,10 +3819,6 @@ zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *fsf_req) debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_fcp_lun_nv"); zfcp_erp_port_reopen(unit->port, 0); - zfcp_cmd_dbf_event_fsf("fluninv", - fsf_req, - &header->fsf_status_qual, - sizeof (union fsf_status_qual)); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; @@ -3853,10 +3857,6 @@ zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *fsf_req) debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf_s_dir_ind_nv"); zfcp_erp_adapter_shutdown(unit->port->adapter, 0); - zfcp_cmd_dbf_event_fsf("dirinv", - fsf_req, - &header->fsf_status_qual, - sizeof (union fsf_status_qual)); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; @@ -3872,10 +3872,6 @@ zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *fsf_req) debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf_s_cmd_len_nv"); zfcp_erp_adapter_shutdown(unit->port->adapter, 0); - zfcp_cmd_dbf_event_fsf("cleninv", - fsf_req, - &header->fsf_status_qual, - sizeof (union fsf_status_qual)); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; @@ -3947,6 +3943,8 @@ zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *fsf_req) zfcp_fsf_send_fcp_command_task_management_handler(fsf_req); } else { retval = zfcp_fsf_send_fcp_command_task_handler(fsf_req); + fsf_req->unit = NULL; + zfcp_unit_put(unit); } return retval; } @@ -3970,10 +3968,10 @@ zfcp_fsf_send_fcp_command_task_handler(struct zfcp_fsf_req *fsf_req) u32 sns_len; char *fcp_rsp_info = zfcp_get_fcp_rsp_info_ptr(fcp_rsp_iu); unsigned long flags; - struct zfcp_unit *unit = fsf_req->data.send_fcp_command_task.unit; + struct zfcp_unit *unit = fsf_req->unit; read_lock_irqsave(&fsf_req->adapter->abort_lock, flags); - scpnt = fsf_req->data.send_fcp_command_task.scsi_cmnd; + scpnt = (struct scsi_cmnd *) fsf_req->data; if (unlikely(!scpnt)) { ZFCP_LOG_DEBUG ("Command with fsf_req %p is not associated to " @@ -4043,7 +4041,6 @@ zfcp_fsf_send_fcp_command_task_handler(struct zfcp_fsf_req *fsf_req) ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, (char *) &fsf_req->qtcb-> bottom.io.fcp_cmnd, FSF_FCP_CMND_SIZE); - zfcp_cmd_dbf_event_fsf("clenmis", fsf_req, NULL, 0); set_host_byte(&scpnt->result, DID_ERROR); goto skip_fsfstatus; case RSP_CODE_FIELD_INVALID: @@ -4062,7 +4059,6 @@ zfcp_fsf_send_fcp_command_task_handler(struct zfcp_fsf_req *fsf_req) (char *) &fsf_req->qtcb-> bottom.io.fcp_cmnd, FSF_FCP_CMND_SIZE); set_host_byte(&scpnt->result, DID_ERROR); - zfcp_cmd_dbf_event_fsf("codeinv", fsf_req, NULL, 0); goto skip_fsfstatus; case RSP_CODE_RO_MISMATCH: /* hardware bug */ @@ -4079,7 +4075,6 @@ zfcp_fsf_send_fcp_command_task_handler(struct zfcp_fsf_req *fsf_req) ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, (char *) &fsf_req->qtcb-> bottom.io.fcp_cmnd, FSF_FCP_CMND_SIZE); - zfcp_cmd_dbf_event_fsf("codemism", fsf_req, NULL, 0); set_host_byte(&scpnt->result, DID_ERROR); goto skip_fsfstatus; default: @@ -4096,7 +4091,6 @@ zfcp_fsf_send_fcp_command_task_handler(struct zfcp_fsf_req *fsf_req) ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, (char *) &fsf_req->qtcb-> bottom.io.fcp_cmnd, FSF_FCP_CMND_SIZE); - zfcp_cmd_dbf_event_fsf("undeffcp", fsf_req, NULL, 0); set_host_byte(&scpnt->result, DID_ERROR); goto skip_fsfstatus; } @@ -4158,19 +4152,17 @@ zfcp_fsf_send_fcp_command_task_handler(struct zfcp_fsf_req *fsf_req) skip_fsfstatus: ZFCP_LOG_DEBUG("scpnt->result =0x%x\n", scpnt->result); - zfcp_cmd_dbf_event_scsi("response", scpnt); + if (scpnt->result != 0) + zfcp_scsi_dbf_event_result("erro", 3, fsf_req->adapter, scpnt); + else if (scpnt->retries > 0) + zfcp_scsi_dbf_event_result("retr", 4, fsf_req->adapter, scpnt); + else + zfcp_scsi_dbf_event_result("norm", 6, fsf_req->adapter, scpnt); /* cleanup pointer (need this especially for abort) */ scpnt->host_scribble = NULL; - /* - * NOTE: - * according to the outcome of a discussion on linux-scsi we - * don't need to grab the io_request_lock here since we use - * the new eh - */ /* always call back */ - (scpnt->scsi_done) (scpnt); /* @@ -4198,8 +4190,7 @@ zfcp_fsf_send_fcp_command_task_management_handler(struct zfcp_fsf_req *fsf_req) struct fcp_rsp_iu *fcp_rsp_iu = (struct fcp_rsp_iu *) &(fsf_req->qtcb->bottom.io.fcp_rsp); char *fcp_rsp_info = zfcp_get_fcp_rsp_info_ptr(fcp_rsp_iu); - struct zfcp_unit *unit = - fsf_req->data.send_fcp_command_task_management.unit; + struct zfcp_unit *unit = (struct zfcp_unit *) fsf_req->data; del_timer(&fsf_req->adapter->scsi_er_timer); if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) { @@ -4276,7 +4267,7 @@ zfcp_fsf_control_file(struct zfcp_adapter *adapter, int direction; int retval = 0; - if (!(adapter->supported_features & FSF_FEATURE_CFDC)) { + if (!(adapter->adapter_features & FSF_FEATURE_CFDC)) { ZFCP_LOG_INFO("cfdc not supported (adapter %s)\n", zfcp_get_busid_by_adapter(adapter)); retval = -EOPNOTSUPP; @@ -4549,52 +4540,6 @@ skip_fsfstatus: return retval; } - -/* - * function: zfcp_fsf_req_wait_and_cleanup - * - * purpose: - * - * FIXME(design): signal seems to be <0 !!! - * returns: 0 - request completed (*status is valid), cleanup succ. - * <0 - request completed (*status is valid), cleanup failed - * >0 - signal which interrupted waiting (*status invalid), - * request not completed, no cleanup - * - * *status is a copy of status of completed fsf_req - */ -int -zfcp_fsf_req_wait_and_cleanup(struct zfcp_fsf_req *fsf_req, - int interruptible, u32 * status) -{ - int retval = 0; - int signal = 0; - - if (interruptible) { - __wait_event_interruptible(fsf_req->completion_wq, - fsf_req->status & - ZFCP_STATUS_FSFREQ_COMPLETED, - signal); - if (signal) { - ZFCP_LOG_DEBUG("Caught signal %i while waiting for the " - "completion of the request at %p\n", - signal, fsf_req); - retval = signal; - goto out; - } - } else { - __wait_event(fsf_req->completion_wq, - fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED); - } - - *status = fsf_req->status; - - /* cleanup request */ - zfcp_fsf_req_free(fsf_req); - out: - return retval; -} - static inline int zfcp_fsf_req_sbal_check(unsigned long *flags, struct zfcp_qdio_queue *queue, int needed) @@ -4610,15 +4555,16 @@ zfcp_fsf_req_sbal_check(unsigned long *flags, * set qtcb pointer in fsf_req and initialize QTCB */ static inline void -zfcp_fsf_req_qtcb_init(struct zfcp_fsf_req *fsf_req, u32 fsf_cmd) +zfcp_fsf_req_qtcb_init(struct zfcp_fsf_req *fsf_req) { if (likely(fsf_req->qtcb != NULL)) { + fsf_req->qtcb->prefix.req_seq_no = fsf_req->adapter->fsf_req_seq_no; fsf_req->qtcb->prefix.req_id = (unsigned long)fsf_req; fsf_req->qtcb->prefix.ulp_info = ZFCP_ULP_INFO_VERSION; - fsf_req->qtcb->prefix.qtcb_type = fsf_qtcb_type[fsf_cmd]; + fsf_req->qtcb->prefix.qtcb_type = fsf_qtcb_type[fsf_req->fsf_command]; fsf_req->qtcb->prefix.qtcb_version = ZFCP_QTCB_VERSION; fsf_req->qtcb->header.req_handle = (unsigned long)fsf_req; - fsf_req->qtcb->header.fsf_command = fsf_cmd; + fsf_req->qtcb->header.fsf_command = fsf_req->fsf_command; } } @@ -4686,7 +4632,10 @@ zfcp_fsf_req_create(struct zfcp_adapter *adapter, u32 fsf_cmd, int req_flags, goto failed_fsf_req; } - zfcp_fsf_req_qtcb_init(fsf_req, fsf_cmd); + fsf_req->adapter = adapter; + fsf_req->fsf_command = fsf_cmd; + + zfcp_fsf_req_qtcb_init(fsf_req); /* initialize waitqueue which may be used to wait on this request completion */ @@ -4708,8 +4657,10 @@ zfcp_fsf_req_create(struct zfcp_adapter *adapter, u32 fsf_cmd, int req_flags, goto failed_sbals; } - fsf_req->adapter = adapter; /* pointer to "parent" adapter */ - fsf_req->fsf_command = fsf_cmd; + if (fsf_req->qtcb) { + fsf_req->seq_no = adapter->fsf_req_seq_no; + fsf_req->qtcb->prefix.req_seq_no = adapter->fsf_req_seq_no; + } fsf_req->sbal_number = 1; fsf_req->sbal_first = req_queue->free_index; fsf_req->sbal_curr = req_queue->free_index; @@ -4760,9 +4711,9 @@ zfcp_fsf_req_send(struct zfcp_fsf_req *fsf_req, struct timer_list *timer) struct zfcp_adapter *adapter; struct zfcp_qdio_queue *req_queue; volatile struct qdio_buffer_element *sbale; + int inc_seq_no; int new_distance_from_int; unsigned long flags; - int inc_seq_no = 1; int retval = 0; adapter = fsf_req->adapter; @@ -4776,23 +4727,13 @@ zfcp_fsf_req_send(struct zfcp_fsf_req *fsf_req, struct timer_list *timer) ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_TRACE, (char *) sbale[1].addr, sbale[1].length); - /* set sequence counter in QTCB */ - if (likely(fsf_req->qtcb)) { - fsf_req->qtcb->prefix.req_seq_no = adapter->fsf_req_seq_no; - fsf_req->seq_no = adapter->fsf_req_seq_no; - ZFCP_LOG_TRACE("FSF request %p of adapter %s gets " - "FSF sequence counter value of %i\n", - fsf_req, - zfcp_get_busid_by_adapter(adapter), - fsf_req->qtcb->prefix.req_seq_no); - } else - inc_seq_no = 0; - /* put allocated FSF request at list tail */ spin_lock_irqsave(&adapter->fsf_req_list_lock, flags); list_add_tail(&fsf_req->list, &adapter->fsf_req_list_head); spin_unlock_irqrestore(&adapter->fsf_req_list_lock, flags); + inc_seq_no = (fsf_req->qtcb != NULL); + /* figure out expiration time of timeout and start timeout */ if (unlikely(timer)) { timer->expires += jiffies; @@ -4822,6 +4763,8 @@ zfcp_fsf_req_send(struct zfcp_fsf_req *fsf_req, struct timer_list *timer) req_queue->free_index %= QDIO_MAX_BUFFERS_PER_Q; /* wrap if needed */ new_distance_from_int = zfcp_qdio_determine_pci(req_queue, fsf_req); + fsf_req->issued = get_clock(); + retval = do_QDIO(adapter->ccw_device, QDIO_FLAG_SYNC_OUTPUT, 0, fsf_req->sbal_first, fsf_req->sbal_number, NULL); @@ -4860,15 +4803,11 @@ zfcp_fsf_req_send(struct zfcp_fsf_req *fsf_req, struct timer_list *timer) * routines resulting in missing sequence counter values * otherwise, */ + /* Don't increase for unsolicited status */ - if (likely(inc_seq_no)) { + if (inc_seq_no) adapter->fsf_req_seq_no++; - ZFCP_LOG_TRACE - ("FSF sequence counter value of adapter %s " - "increased to %i\n", - zfcp_get_busid_by_adapter(adapter), - adapter->fsf_req_seq_no); - } + /* count FSF requests pending */ atomic_inc(&adapter->fsf_reqs_active); } diff --git a/drivers/s390/scsi/zfcp_fsf.h b/drivers/s390/scsi/zfcp_fsf.h index 07140dfda2a7..48719f055952 100644 --- a/drivers/s390/scsi/zfcp_fsf.h +++ b/drivers/s390/scsi/zfcp_fsf.h @@ -116,6 +116,7 @@ #define FSF_INVALID_COMMAND_OPTION 0x000000E5 /* #define FSF_ERROR 0x000000FF */ +#define FSF_PROT_STATUS_QUAL_SIZE 16 #define FSF_STATUS_QUALIFIER_SIZE 16 /* FSF status qualifier, recommendations */ @@ -139,9 +140,18 @@ #define FSF_SQ_CFDC_SUBTABLE_LUN 0x0004 /* FSF status qualifier (most significant 4 bytes), local link down */ -#define FSF_PSQ_LINK_NOLIGHT 0x00000004 -#define FSF_PSQ_LINK_WRAPPLUG 0x00000008 -#define FSF_PSQ_LINK_NOFCP 0x00000010 +#define FSF_PSQ_LINK_NO_LIGHT 0x00000004 +#define FSF_PSQ_LINK_WRAP_PLUG 0x00000008 +#define FSF_PSQ_LINK_NO_FCP 0x00000010 +#define FSF_PSQ_LINK_FIRMWARE_UPDATE 0x00000020 +#define FSF_PSQ_LINK_INVALID_WWPN 0x00000100 +#define FSF_PSQ_LINK_NO_NPIV_SUPPORT 0x00000200 +#define FSF_PSQ_LINK_NO_FCP_RESOURCES 0x00000400 +#define FSF_PSQ_LINK_NO_FABRIC_RESOURCES 0x00000800 +#define FSF_PSQ_LINK_FABRIC_LOGIN_UNABLE 0x00001000 +#define FSF_PSQ_LINK_WWPN_ASSIGNMENT_CORRUPTED 0x00002000 +#define FSF_PSQ_LINK_MODE_TABLE_CURRUPTED 0x00004000 +#define FSF_PSQ_LINK_NO_WWPN_ASSIGNMENT 0x00008000 /* payload size in status read buffer */ #define FSF_STATUS_READ_PAYLOAD_SIZE 4032 @@ -154,15 +164,21 @@ #define FSF_STATUS_READ_INCOMING_ELS 0x00000002 #define FSF_STATUS_READ_SENSE_DATA_AVAIL 0x00000003 #define FSF_STATUS_READ_BIT_ERROR_THRESHOLD 0x00000004 -#define FSF_STATUS_READ_LINK_DOWN 0x00000005 /* FIXME: really? */ +#define FSF_STATUS_READ_LINK_DOWN 0x00000005 #define FSF_STATUS_READ_LINK_UP 0x00000006 #define FSF_STATUS_READ_CFDC_UPDATED 0x0000000A #define FSF_STATUS_READ_CFDC_HARDENED 0x0000000B +#define FSF_STATUS_READ_FEATURE_UPDATE_ALERT 0x0000000C /* status subtypes in status read buffer */ #define FSF_STATUS_READ_SUB_CLOSE_PHYS_PORT 0x00000001 #define FSF_STATUS_READ_SUB_ERROR_PORT 0x00000002 +/* status subtypes for link down */ +#define FSF_STATUS_READ_SUB_NO_PHYSICAL_LINK 0x00000000 +#define FSF_STATUS_READ_SUB_FDISC_FAILED 0x00000001 +#define FSF_STATUS_READ_SUB_FIRMWARE_UPDATE 0x00000002 + /* status subtypes for CFDC */ #define FSF_STATUS_READ_SUB_CFDC_HARDENED_ON_SE 0x00000002 #define FSF_STATUS_READ_SUB_CFDC_HARDENED_ON_SE2 0x0000000F @@ -193,11 +209,15 @@ #define FSF_QTCB_LOG_SIZE 1024 /* channel features */ -#define FSF_FEATURE_QTCB_SUPPRESSION 0x00000001 #define FSF_FEATURE_CFDC 0x00000002 #define FSF_FEATURE_LUN_SHARING 0x00000004 #define FSF_FEATURE_HBAAPI_MANAGEMENT 0x00000010 #define FSF_FEATURE_ELS_CT_CHAINED_SBALS 0x00000020 +#define FSF_FEATURE_UPDATE_ALERT 0x00000100 + +/* host connection features */ +#define FSF_FEATURE_NPIV_MODE 0x00000001 +#define FSF_FEATURE_VM_ASSIGNED_WWPN 0x00000002 /* option */ #define FSF_OPEN_LUN_SUPPRESS_BOXING 0x00000001 @@ -305,15 +325,23 @@ struct fsf_qual_sequence_error { u32 res1[3]; } __attribute__ ((packed)); -struct fsf_qual_locallink_error { - u32 code; - u32 res1[3]; +struct fsf_link_down_info { + u32 error_code; + u32 res1; + u8 res2[2]; + u8 primary_status; + u8 ioerr_code; + u8 action_code; + u8 reason_code; + u8 explanation_code; + u8 vendor_specific_code; } __attribute__ ((packed)); union fsf_prot_status_qual { + u64 doubleword[FSF_PROT_STATUS_QUAL_SIZE / sizeof(u64)]; struct fsf_qual_version_error version_error; struct fsf_qual_sequence_error sequence_error; - struct fsf_qual_locallink_error locallink_error; + struct fsf_link_down_info link_down_info; } __attribute__ ((packed)); struct fsf_qtcb_prefix { @@ -331,7 +359,9 @@ union fsf_status_qual { u8 byte[FSF_STATUS_QUALIFIER_SIZE]; u16 halfword[FSF_STATUS_QUALIFIER_SIZE / sizeof (u16)]; u32 word[FSF_STATUS_QUALIFIER_SIZE / sizeof (u32)]; + u64 doubleword[FSF_STATUS_QUALIFIER_SIZE / sizeof(u64)]; struct fsf_queue_designator fsf_queue_designator; + struct fsf_link_down_info link_down_info; } __attribute__ ((packed)); struct fsf_qtcb_header { @@ -406,8 +436,8 @@ struct fsf_qtcb_bottom_config { u32 low_qtcb_version; u32 max_qtcb_size; u32 max_data_transfer_size; - u32 supported_features; - u8 res1[4]; + u32 adapter_features; + u32 connection_features; u32 fc_topology; u32 fc_link_speed; u32 adapter_type; @@ -425,7 +455,7 @@ struct fsf_qtcb_bottom_config { } __attribute__ ((packed)); struct fsf_qtcb_bottom_port { - u8 res1[8]; + u64 wwpn; u32 fc_port_id; u32 port_type; u32 port_state; diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c index 24e16ec331d9..d719f66a29a4 100644 --- a/drivers/s390/scsi/zfcp_qdio.c +++ b/drivers/s390/scsi/zfcp_qdio.c @@ -54,8 +54,7 @@ static inline int zfcp_qdio_sbals_from_buffer static qdio_handler_t zfcp_qdio_request_handler; static qdio_handler_t zfcp_qdio_response_handler; static int zfcp_qdio_handler_error_check(struct zfcp_adapter *, - unsigned int, - unsigned int, unsigned int); + unsigned int, unsigned int, unsigned int, int, int); #define ZFCP_LOG_AREA ZFCP_LOG_AREA_QDIO @@ -214,22 +213,12 @@ zfcp_qdio_allocate(struct zfcp_adapter *adapter) * */ static inline int -zfcp_qdio_handler_error_check(struct zfcp_adapter *adapter, - unsigned int status, - unsigned int qdio_error, unsigned int siga_error) +zfcp_qdio_handler_error_check(struct zfcp_adapter *adapter, unsigned int status, + unsigned int qdio_error, unsigned int siga_error, + int first_element, int elements_processed) { int retval = 0; - if (ZFCP_LOG_CHECK(ZFCP_LOG_LEVEL_TRACE)) { - if (status & QDIO_STATUS_INBOUND_INT) { - ZFCP_LOG_TRACE("status is" - " QDIO_STATUS_INBOUND_INT \n"); - } - if (status & QDIO_STATUS_OUTBOUND_INT) { - ZFCP_LOG_TRACE("status is" - " QDIO_STATUS_OUTBOUND_INT \n"); - } - } if (unlikely(status & QDIO_STATUS_LOOK_FOR_ERROR)) { retval = -EIO; @@ -237,9 +226,10 @@ zfcp_qdio_handler_error_check(struct zfcp_adapter *adapter, "qdio_error=0x%x, siga_error=0x%x)\n", status, qdio_error, siga_error); - /* Restarting IO on the failed adapter from scratch */ - debug_text_event(adapter->erp_dbf, 1, "qdio_err"); + zfcp_hba_dbf_event_qdio(adapter, status, qdio_error, siga_error, + first_element, elements_processed); /* + * Restarting IO on the failed adapter from scratch. * Since we have been using this adapter, it is save to assume * that it is not failed but recoverable. The card seems to * report link-up events by self-initiated queue shutdown. @@ -282,7 +272,8 @@ zfcp_qdio_request_handler(struct ccw_device *ccw_device, first_element, elements_processed); if (unlikely(zfcp_qdio_handler_error_check(adapter, status, qdio_error, - siga_error))) + siga_error, first_element, + elements_processed))) goto out; /* * we stored address of struct zfcp_adapter data structure @@ -334,7 +325,8 @@ zfcp_qdio_response_handler(struct ccw_device *ccw_device, queue = &adapter->response_queue; if (unlikely(zfcp_qdio_handler_error_check(adapter, status, qdio_error, - siga_error))) + siga_error, first_element, + elements_processed))) goto out; /* diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index 31a76065cf28..3dcd1bfba3b4 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -44,7 +44,8 @@ static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *); static int zfcp_scsi_eh_device_reset_handler(struct scsi_cmnd *); static int zfcp_scsi_eh_bus_reset_handler(struct scsi_cmnd *); static int zfcp_scsi_eh_host_reset_handler(struct scsi_cmnd *); -static int zfcp_task_management_function(struct zfcp_unit *, u8); +static int zfcp_task_management_function(struct zfcp_unit *, u8, + struct scsi_cmnd *); static struct zfcp_unit *zfcp_unit_lookup(struct zfcp_adapter *, int, scsi_id_t, scsi_lun_t); @@ -242,7 +243,10 @@ static void zfcp_scsi_command_fail(struct scsi_cmnd *scpnt, int result) { set_host_byte(&scpnt->result, result); - zfcp_cmd_dbf_event_scsi("failing", scpnt); + if ((scpnt->device != NULL) && (scpnt->device->host != NULL)) + zfcp_scsi_dbf_event_result("fail", 4, + (struct zfcp_adapter*) scpnt->device->host->hostdata[0], + scpnt); /* return directly */ scpnt->scsi_done(scpnt); } @@ -414,67 +418,38 @@ zfcp_port_lookup(struct zfcp_adapter *adapter, int channel, scsi_id_t id) return (struct zfcp_port *) NULL; } -/* - * function: zfcp_scsi_eh_abort_handler - * - * purpose: tries to abort the specified (timed out) SCSI command - * - * note: We do not need to care for a SCSI command which completes - * normally but late during this abort routine runs. - * We are allowed to return late commands to the SCSI stack. - * It tracks the state of commands and will handle late commands. - * (Usually, the normal completion of late commands is ignored with - * respect to the running abort operation. Grep for 'done_late' - * in the SCSI stacks sources.) +/** + * zfcp_scsi_eh_abort_handler - abort the specified SCSI command + * @scpnt: pointer to scsi_cmnd to be aborted + * Return: SUCCESS - command has been aborted and cleaned up in internal + * bookkeeping, SCSI stack won't be called for aborted command + * FAILED - otherwise * - * returns: SUCCESS - command has been aborted and cleaned up in internal - * bookkeeping, - * SCSI stack won't be called for aborted command - * FAILED - otherwise + * We do not need to care for a SCSI command which completes normally + * but late during this abort routine runs. We are allowed to return + * late commands to the SCSI stack. It tracks the state of commands and + * will handle late commands. (Usually, the normal completion of late + * commands is ignored with respect to the running abort operation.) */ int -__zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt) +zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt) { + struct Scsi_Host *scsi_host; + struct zfcp_adapter *adapter; + struct zfcp_unit *unit; int retval = SUCCESS; - struct zfcp_fsf_req *new_fsf_req, *old_fsf_req; - struct zfcp_adapter *adapter = (struct zfcp_adapter *) scpnt->device->host->hostdata[0]; - struct zfcp_unit *unit = (struct zfcp_unit *) scpnt->device->hostdata; - struct zfcp_port *port = unit->port; - struct Scsi_Host *scsi_host = scpnt->device->host; - union zfcp_req_data *req_data = NULL; + struct zfcp_fsf_req *new_fsf_req = NULL; + struct zfcp_fsf_req *old_fsf_req; unsigned long flags; - u32 status = 0; - - /* the components of a abort_dbf record (fixed size record) */ - u64 dbf_scsi_cmnd = (unsigned long) scpnt; - char dbf_opcode[ZFCP_ABORT_DBF_LENGTH]; - wwn_t dbf_wwn = port->wwpn; - fcp_lun_t dbf_fcp_lun = unit->fcp_lun; - u64 dbf_retries = scpnt->retries; - u64 dbf_allowed = scpnt->allowed; - u64 dbf_timeout = 0; - u64 dbf_fsf_req = 0; - u64 dbf_fsf_status = 0; - u64 dbf_fsf_qual[2] = { 0, 0 }; - char dbf_result[ZFCP_ABORT_DBF_LENGTH] = "##undef"; - - memset(dbf_opcode, 0, ZFCP_ABORT_DBF_LENGTH); - memcpy(dbf_opcode, - scpnt->cmnd, - min(scpnt->cmd_len, (unsigned char) ZFCP_ABORT_DBF_LENGTH)); + + scsi_host = scpnt->device->host; + adapter = (struct zfcp_adapter *) scsi_host->hostdata[0]; + unit = (struct zfcp_unit *) scpnt->device->hostdata; ZFCP_LOG_INFO("aborting scsi_cmnd=%p on adapter %s\n", scpnt, zfcp_get_busid_by_adapter(adapter)); - spin_unlock_irq(scsi_host->host_lock); - - /* - * Race condition between normal (late) completion and abort has - * to be avoided. - * The entirity of all accesses to scsi_req have to be atomic. - * scsi_req is usually part of the fsf_req and thus we block the - * release of fsf_req as long as we need to access scsi_req. - */ + /* avoid race condition between late normal completion and abort */ write_lock_irqsave(&adapter->abort_lock, flags); /* @@ -484,144 +459,47 @@ __zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt) * this routine returns. (scpnt is parameter passed to this routine * and must not disappear during abort even on late completion.) */ - req_data = (union zfcp_req_data *) scpnt->host_scribble; - /* DEBUG */ - ZFCP_LOG_DEBUG("req_data=%p\n", req_data); - if (!req_data) { - ZFCP_LOG_DEBUG("late command completion overtook abort\n"); - /* - * That's it. - * Do not initiate abort but return SUCCESS. - */ - write_unlock_irqrestore(&adapter->abort_lock, flags); - retval = SUCCESS; - strncpy(dbf_result, "##late1", ZFCP_ABORT_DBF_LENGTH); - goto out; - } - - /* Figure out which fsf_req needs to be aborted. */ - old_fsf_req = req_data->send_fcp_command_task.fsf_req; - - dbf_fsf_req = (unsigned long) old_fsf_req; - dbf_timeout = - (jiffies - req_data->send_fcp_command_task.start_jiffies) / HZ; - - ZFCP_LOG_DEBUG("old_fsf_req=%p\n", old_fsf_req); + old_fsf_req = (struct zfcp_fsf_req *) scpnt->host_scribble; if (!old_fsf_req) { write_unlock_irqrestore(&adapter->abort_lock, flags); - ZFCP_LOG_NORMAL("bug: no old fsf request found\n"); - ZFCP_LOG_NORMAL("req_data:\n"); - ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL, - (char *) req_data, sizeof (union zfcp_req_data)); - ZFCP_LOG_NORMAL("scsi_cmnd:\n"); - ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL, - (char *) scpnt, sizeof (struct scsi_cmnd)); - retval = FAILED; - strncpy(dbf_result, "##bug:r", ZFCP_ABORT_DBF_LENGTH); + zfcp_scsi_dbf_event_abort("lte1", adapter, scpnt, new_fsf_req); + retval = SUCCESS; goto out; } - old_fsf_req->data.send_fcp_command_task.scsi_cmnd = NULL; - /* mark old request as being aborted */ + old_fsf_req->data = 0; old_fsf_req->status |= ZFCP_STATUS_FSFREQ_ABORTING; - /* - * We have to collect all information (e.g. unit) needed by - * zfcp_fsf_abort_fcp_command before calling that routine - * since that routine is not allowed to access - * fsf_req which it is going to abort. - * This is because of we need to release fsf_req_list_lock - * before calling zfcp_fsf_abort_fcp_command. - * Since this lock will not be held, fsf_req may complete - * late and may be released meanwhile. - */ - ZFCP_LOG_DEBUG("unit 0x%016Lx (%p)\n", unit->fcp_lun, unit); - /* - * We block (call schedule) - * That's why we must release the lock and enable the - * interrupts before. - * On the other hand we do not need the lock anymore since - * all critical accesses to scsi_req are done. - */ + /* don't access old_fsf_req after releasing the abort_lock */ write_unlock_irqrestore(&adapter->abort_lock, flags); /* call FSF routine which does the abort */ new_fsf_req = zfcp_fsf_abort_fcp_command((unsigned long) old_fsf_req, adapter, unit, 0); - ZFCP_LOG_DEBUG("new_fsf_req=%p\n", new_fsf_req); if (!new_fsf_req) { + ZFCP_LOG_INFO("error: initiation of Abort FCP Cmnd failed\n"); retval = FAILED; - ZFCP_LOG_NORMAL("error: initiation of Abort FCP Cmnd " - "failed\n"); - strncpy(dbf_result, "##nores", ZFCP_ABORT_DBF_LENGTH); goto out; } /* wait for completion of abort */ - ZFCP_LOG_DEBUG("waiting for cleanup...\n"); -#if 1 - /* - * FIXME: - * copying zfcp_fsf_req_wait_and_cleanup code is not really nice - */ __wait_event(new_fsf_req->completion_wq, new_fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED); - status = new_fsf_req->status; - dbf_fsf_status = new_fsf_req->qtcb->header.fsf_status; - /* - * Ralphs special debug load provides timestamps in the FSF - * status qualifier. This might be specified later if being - * useful for debugging aborts. - */ - dbf_fsf_qual[0] = - *(u64 *) & new_fsf_req->qtcb->header.fsf_status_qual.word[0]; - dbf_fsf_qual[1] = - *(u64 *) & new_fsf_req->qtcb->header.fsf_status_qual.word[2]; - zfcp_fsf_req_free(new_fsf_req); -#else - retval = zfcp_fsf_req_wait_and_cleanup(new_fsf_req, - ZFCP_UNINTERRUPTIBLE, &status); -#endif - ZFCP_LOG_DEBUG("Waiting for cleanup complete, status=0x%x\n", status); + /* status should be valid since signals were not permitted */ - if (status & ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED) { + if (new_fsf_req->status & ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED) { + zfcp_scsi_dbf_event_abort("okay", adapter, scpnt, new_fsf_req); retval = SUCCESS; - strncpy(dbf_result, "##succ", ZFCP_ABORT_DBF_LENGTH); - } else if (status & ZFCP_STATUS_FSFREQ_ABORTNOTNEEDED) { + } else if (new_fsf_req->status & ZFCP_STATUS_FSFREQ_ABORTNOTNEEDED) { + zfcp_scsi_dbf_event_abort("lte2", adapter, scpnt, new_fsf_req); retval = SUCCESS; - strncpy(dbf_result, "##late2", ZFCP_ABORT_DBF_LENGTH); } else { + zfcp_scsi_dbf_event_abort("fail", adapter, scpnt, new_fsf_req); retval = FAILED; - strncpy(dbf_result, "##fail", ZFCP_ABORT_DBF_LENGTH); } - + zfcp_fsf_req_free(new_fsf_req); out: - debug_event(adapter->abort_dbf, 1, &dbf_scsi_cmnd, sizeof (u64)); - debug_event(adapter->abort_dbf, 1, &dbf_opcode, ZFCP_ABORT_DBF_LENGTH); - debug_event(adapter->abort_dbf, 1, &dbf_wwn, sizeof (wwn_t)); - debug_event(adapter->abort_dbf, 1, &dbf_fcp_lun, sizeof (fcp_lun_t)); - debug_event(adapter->abort_dbf, 1, &dbf_retries, sizeof (u64)); - debug_event(adapter->abort_dbf, 1, &dbf_allowed, sizeof (u64)); - debug_event(adapter->abort_dbf, 1, &dbf_timeout, sizeof (u64)); - debug_event(adapter->abort_dbf, 1, &dbf_fsf_req, sizeof (u64)); - debug_event(adapter->abort_dbf, 1, &dbf_fsf_status, sizeof (u64)); - debug_event(adapter->abort_dbf, 1, &dbf_fsf_qual[0], sizeof (u64)); - debug_event(adapter->abort_dbf, 1, &dbf_fsf_qual[1], sizeof (u64)); - debug_text_event(adapter->abort_dbf, 1, dbf_result); - - spin_lock_irq(scsi_host->host_lock); return retval; } -int -zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt) -{ - int rc; - struct Scsi_Host *scsi_host = scpnt->device->host; - spin_lock_irq(scsi_host->host_lock); - rc = __zfcp_scsi_eh_abort_handler(scpnt); - spin_unlock_irq(scsi_host->host_lock); - return rc; -} - /* * function: zfcp_scsi_eh_device_reset_handler * @@ -651,8 +529,9 @@ zfcp_scsi_eh_device_reset_handler(struct scsi_cmnd *scpnt) */ if (!atomic_test_mask(ZFCP_STATUS_UNIT_NOTSUPPUNITRESET, &unit->status)) { - retval = - zfcp_task_management_function(unit, FCP_LOGICAL_UNIT_RESET); + retval = zfcp_task_management_function(unit, + FCP_LOGICAL_UNIT_RESET, + scpnt); if (retval) { ZFCP_LOG_DEBUG("unit reset failed (unit=%p)\n", unit); if (retval == -ENOTSUPP) @@ -668,7 +547,7 @@ zfcp_scsi_eh_device_reset_handler(struct scsi_cmnd *scpnt) goto out; } } - retval = zfcp_task_management_function(unit, FCP_TARGET_RESET); + retval = zfcp_task_management_function(unit, FCP_TARGET_RESET, scpnt); if (retval) { ZFCP_LOG_DEBUG("target reset failed (unit=%p)\n", unit); retval = FAILED; @@ -681,12 +560,12 @@ zfcp_scsi_eh_device_reset_handler(struct scsi_cmnd *scpnt) } static int -zfcp_task_management_function(struct zfcp_unit *unit, u8 tm_flags) +zfcp_task_management_function(struct zfcp_unit *unit, u8 tm_flags, + struct scsi_cmnd *scpnt) { struct zfcp_adapter *adapter = unit->port->adapter; - int retval; - int status; struct zfcp_fsf_req *fsf_req; + int retval = 0; /* issue task management function */ fsf_req = zfcp_fsf_send_fcp_command_task_management @@ -696,70 +575,63 @@ zfcp_task_management_function(struct zfcp_unit *unit, u8 tm_flags) "failed for unit 0x%016Lx on port 0x%016Lx on " "adapter %s\n", unit->fcp_lun, unit->port->wwpn, zfcp_get_busid_by_adapter(adapter)); + zfcp_scsi_dbf_event_devreset("nres", tm_flags, unit, scpnt); retval = -ENOMEM; goto out; } - retval = zfcp_fsf_req_wait_and_cleanup(fsf_req, - ZFCP_UNINTERRUPTIBLE, &status); + __wait_event(fsf_req->completion_wq, + fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED); + /* * check completion status of task management function - * (status should always be valid since no signals permitted) */ - if (status & ZFCP_STATUS_FSFREQ_TMFUNCFAILED) + if (fsf_req->status & ZFCP_STATUS_FSFREQ_TMFUNCFAILED) { + zfcp_scsi_dbf_event_devreset("fail", tm_flags, unit, scpnt); retval = -EIO; - else if (status & ZFCP_STATUS_FSFREQ_TMFUNCNOTSUPP) + } else if (fsf_req->status & ZFCP_STATUS_FSFREQ_TMFUNCNOTSUPP) { + zfcp_scsi_dbf_event_devreset("nsup", tm_flags, unit, scpnt); retval = -ENOTSUPP; - else - retval = 0; + } else + zfcp_scsi_dbf_event_devreset("okay", tm_flags, unit, scpnt); + + zfcp_fsf_req_free(fsf_req); out: return retval; } -/* - * function: zfcp_scsi_eh_bus_reset_handler - * - * purpose: - * - * returns: +/** + * zfcp_scsi_eh_bus_reset_handler - reset bus (reopen adapter) */ int zfcp_scsi_eh_bus_reset_handler(struct scsi_cmnd *scpnt) { - int retval = 0; - struct zfcp_unit *unit; + struct zfcp_unit *unit = (struct zfcp_unit*) scpnt->device->hostdata; + struct zfcp_adapter *adapter = unit->port->adapter; - unit = (struct zfcp_unit *) scpnt->device->hostdata; ZFCP_LOG_NORMAL("bus reset because of problems with " "unit 0x%016Lx\n", unit->fcp_lun); - zfcp_erp_adapter_reopen(unit->port->adapter, 0); - zfcp_erp_wait(unit->port->adapter); - retval = SUCCESS; + zfcp_erp_adapter_reopen(adapter, 0); + zfcp_erp_wait(adapter); - return retval; + return SUCCESS; } -/* - * function: zfcp_scsi_eh_host_reset_handler - * - * purpose: - * - * returns: +/** + * zfcp_scsi_eh_host_reset_handler - reset host (reopen adapter) */ int zfcp_scsi_eh_host_reset_handler(struct scsi_cmnd *scpnt) { - int retval = 0; - struct zfcp_unit *unit; + struct zfcp_unit *unit = (struct zfcp_unit*) scpnt->device->hostdata; + struct zfcp_adapter *adapter = unit->port->adapter; - unit = (struct zfcp_unit *) scpnt->device->hostdata; ZFCP_LOG_NORMAL("host reset because of problems with " "unit 0x%016Lx\n", unit->fcp_lun); - zfcp_erp_adapter_reopen(unit->port->adapter, 0); - zfcp_erp_wait(unit->port->adapter); - retval = SUCCESS; + zfcp_erp_adapter_reopen(adapter, 0); + zfcp_erp_wait(adapter); - return retval; + return SUCCESS; } /* @@ -826,10 +698,16 @@ void zfcp_adapter_scsi_unregister(struct zfcp_adapter *adapter) { struct Scsi_Host *shost; + struct zfcp_port *port; shost = adapter->scsi_host; if (!shost) return; + read_lock_irq(&zfcp_data.config_lock); + list_for_each_entry(port, &adapter->port_list_head, list) + if (port->rport) + port->rport = NULL; + read_unlock_irq(&zfcp_data.config_lock); fc_remove_host(shost); scsi_remove_host(shost); scsi_host_put(shost); @@ -904,18 +782,6 @@ zfcp_get_node_name(struct scsi_target *starget) read_unlock_irqrestore(&zfcp_data.config_lock, flags); } -void -zfcp_set_fc_host_attrs(struct zfcp_adapter *adapter) -{ - struct Scsi_Host *shost = adapter->scsi_host; - - fc_host_node_name(shost) = adapter->wwnn; - fc_host_port_name(shost) = adapter->wwpn; - strncpy(fc_host_serial_number(shost), adapter->serial_number, - min(FC_SERIAL_NUMBER_SIZE, 32)); - fc_host_supported_classes(shost) = FC_COS_CLASS2 | FC_COS_CLASS3; -} - struct fc_function_template zfcp_transport_functions = { .get_starget_port_id = zfcp_get_port_id, .get_starget_port_name = zfcp_get_port_name, @@ -927,7 +793,10 @@ struct fc_function_template zfcp_transport_functions = { .show_host_node_name = 1, .show_host_port_name = 1, .show_host_supported_classes = 1, + .show_host_maxframe_size = 1, .show_host_serial_number = 1, + .show_host_speed = 1, + .show_host_port_id = 1, }; /** diff --git a/drivers/s390/scsi/zfcp_sysfs_adapter.c b/drivers/s390/scsi/zfcp_sysfs_adapter.c index e7345a74800a..0cd435280e7d 100644 --- a/drivers/s390/scsi/zfcp_sysfs_adapter.c +++ b/drivers/s390/scsi/zfcp_sysfs_adapter.c @@ -62,21 +62,18 @@ static ssize_t zfcp_sysfs_adapter_##_name##_show(struct device *dev, struct devi static DEVICE_ATTR(_name, S_IRUGO, zfcp_sysfs_adapter_##_name##_show, NULL); ZFCP_DEFINE_ADAPTER_ATTR(status, "0x%08x\n", atomic_read(&adapter->status)); -ZFCP_DEFINE_ADAPTER_ATTR(wwnn, "0x%016llx\n", adapter->wwnn); -ZFCP_DEFINE_ADAPTER_ATTR(wwpn, "0x%016llx\n", adapter->wwpn); -ZFCP_DEFINE_ADAPTER_ATTR(s_id, "0x%06x\n", adapter->s_id); ZFCP_DEFINE_ADAPTER_ATTR(peer_wwnn, "0x%016llx\n", adapter->peer_wwnn); ZFCP_DEFINE_ADAPTER_ATTR(peer_wwpn, "0x%016llx\n", adapter->peer_wwpn); ZFCP_DEFINE_ADAPTER_ATTR(peer_d_id, "0x%06x\n", adapter->peer_d_id); +ZFCP_DEFINE_ADAPTER_ATTR(physical_wwpn, "0x%016llx\n", adapter->physical_wwpn); +ZFCP_DEFINE_ADAPTER_ATTR(physical_s_id, "0x%06x\n", adapter->physical_s_id); ZFCP_DEFINE_ADAPTER_ATTR(card_version, "0x%04x\n", adapter->hydra_version); ZFCP_DEFINE_ADAPTER_ATTR(lic_version, "0x%08x\n", adapter->fsf_lic_version); -ZFCP_DEFINE_ADAPTER_ATTR(fc_link_speed, "%d Gb/s\n", adapter->fc_link_speed); ZFCP_DEFINE_ADAPTER_ATTR(fc_service_class, "%d\n", adapter->fc_service_class); ZFCP_DEFINE_ADAPTER_ATTR(fc_topology, "%s\n", fc_topologies[adapter->fc_topology]); ZFCP_DEFINE_ADAPTER_ATTR(hardware_version, "0x%08x\n", adapter->hardware_version); -ZFCP_DEFINE_ADAPTER_ATTR(serial_number, "%17s\n", adapter->serial_number); ZFCP_DEFINE_ADAPTER_ATTR(scsi_host_no, "0x%x\n", adapter->scsi_host_no); ZFCP_DEFINE_ADAPTER_ATTR(in_recovery, "%d\n", atomic_test_mask (ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status)); @@ -255,21 +252,18 @@ static struct attribute *zfcp_adapter_attrs[] = { &dev_attr_in_recovery.attr, &dev_attr_port_remove.attr, &dev_attr_port_add.attr, - &dev_attr_wwnn.attr, - &dev_attr_wwpn.attr, - &dev_attr_s_id.attr, &dev_attr_peer_wwnn.attr, &dev_attr_peer_wwpn.attr, &dev_attr_peer_d_id.attr, + &dev_attr_physical_wwpn.attr, + &dev_attr_physical_s_id.attr, &dev_attr_card_version.attr, &dev_attr_lic_version.attr, - &dev_attr_fc_link_speed.attr, &dev_attr_fc_service_class.attr, &dev_attr_fc_topology.attr, &dev_attr_scsi_host_no.attr, &dev_attr_status.attr, &dev_attr_hardware_version.attr, - &dev_attr_serial_number.attr, NULL }; diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm.c b/drivers/scsi/aic7xxx/aic7xxx_osm.c index c932b3b94490..876d1de8480d 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_osm.c +++ b/drivers/scsi/aic7xxx/aic7xxx_osm.c @@ -1109,15 +1109,6 @@ ahc_linux_register_host(struct ahc_softc *ahc, struct scsi_host_template *templa return (0); } -uint64_t -ahc_linux_get_memsize(void) -{ - struct sysinfo si; - - si_meminfo(&si); - return ((uint64_t)si.totalram << PAGE_SHIFT); -} - /* * Place the SCSI bus into a known state by either resetting it, * or forcing transfer negotiations on the next command to any diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm.h b/drivers/scsi/aic7xxx/aic7xxx_osm.h index c52996269240..be9edbe26dbe 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_osm.h +++ b/drivers/scsi/aic7xxx/aic7xxx_osm.h @@ -494,8 +494,6 @@ ahc_insb(struct ahc_softc * ahc, long port, uint8_t *array, int count) int ahc_linux_register_host(struct ahc_softc *, struct scsi_host_template *); -uint64_t ahc_linux_get_memsize(void); - /*************************** Pretty Printing **********************************/ struct info_str { char *buffer; diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c b/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c index 0d44a6907dd2..3ce77ddc889e 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c +++ b/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c @@ -180,6 +180,7 @@ ahc_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent) struct ahc_pci_identity *entry; char *name; int error; + struct device *dev = &pdev->dev; pci = pdev; entry = ahc_find_pci_device(pci); @@ -209,11 +210,12 @@ ahc_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pci_set_master(pdev); if (sizeof(dma_addr_t) > 4 - && ahc_linux_get_memsize() > 0x80000000 - && pci_set_dma_mask(pdev, mask_39bit) == 0) { + && ahc->features & AHC_LARGE_SCBS + && dma_set_mask(dev, mask_39bit) == 0 + && dma_get_required_mask(dev) > DMA_32BIT_MASK) { ahc->flags |= AHC_39BIT_ADDRESSING; } else { - if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) { + if (dma_set_mask(dev, DMA_32BIT_MASK)) { printk(KERN_WARNING "aic7xxx: No suitable DMA available.\n"); return (-ENODEV); } diff --git a/drivers/scsi/ata_piix.c b/drivers/scsi/ata_piix.c index 87e0c36f1554..d71cef767cec 100644 --- a/drivers/scsi/ata_piix.c +++ b/drivers/scsi/ata_piix.c @@ -442,7 +442,6 @@ static void piix_sata_phy_reset(struct ata_port *ap) * piix_set_piomode - Initialize host controller PATA PIO timings * @ap: Port whose timings we are configuring * @adev: um - * @pio: PIO mode, 0 - 4 * * Set PIO mode for device, in host controller PCI config space. * diff --git a/drivers/scsi/atp870u.c b/drivers/scsi/atp870u.c index e6153fe5842a..a8cfbef304b5 100644 --- a/drivers/scsi/atp870u.c +++ b/drivers/scsi/atp870u.c @@ -996,6 +996,7 @@ oktosend: #ifdef ED_DBGP printk("send_s870: prdaddr_2 0x%8x tmpcip %x target_id %d\n", dev->id[c][target_id].prdaddr,tmpcip,target_id); #endif + dev->id[c][target_id].prdaddr = dev->id[c][target_id].prd_bus; outl(dev->id[c][target_id].prdaddr, tmpcip); tmpcip = tmpcip - 2; outb(0x06, tmpcip); @@ -2572,7 +2573,7 @@ static void atp870u_free_tables(struct Scsi_Host *host) for (k = 0; k < 16; k++) { if (!atp_dev->id[j][k].prd_table) continue; - pci_free_consistent(atp_dev->pdev, 1024, atp_dev->id[j][k].prd_table, atp_dev->id[j][k].prdaddr); + pci_free_consistent(atp_dev->pdev, 1024, atp_dev->id[j][k].prd_table, atp_dev->id[j][k].prd_bus); atp_dev->id[j][k].prd_table = NULL; } } @@ -2584,12 +2585,13 @@ static int atp870u_init_tables(struct Scsi_Host *host) int c,k; for(c=0;c < 2;c++) { for(k=0;k<16;k++) { - atp_dev->id[c][k].prd_table = pci_alloc_consistent(atp_dev->pdev, 1024, &(atp_dev->id[c][k].prdaddr)); + atp_dev->id[c][k].prd_table = pci_alloc_consistent(atp_dev->pdev, 1024, &(atp_dev->id[c][k].prd_bus)); if (!atp_dev->id[c][k].prd_table) { printk("atp870u_init_tables fail\n"); atp870u_free_tables(host); return -ENOMEM; } + atp_dev->id[c][k].prdaddr = atp_dev->id[c][k].prd_bus; atp_dev->id[c][k].devsp=0x20; atp_dev->id[c][k].devtype = 0x7f; atp_dev->id[c][k].curr_req = NULL; diff --git a/drivers/scsi/atp870u.h b/drivers/scsi/atp870u.h index 89f43af39cf2..62bae64a01c1 100644 --- a/drivers/scsi/atp870u.h +++ b/drivers/scsi/atp870u.h @@ -54,8 +54,9 @@ struct atp_unit unsigned long tran_len; unsigned long last_len; unsigned char *prd_pos; - unsigned char *prd_table; - dma_addr_t prdaddr; + unsigned char *prd_table; /* Kernel address of PRD table */ + dma_addr_t prd_bus; /* Bus address of PRD */ + dma_addr_t prdaddr; /* Dynamically updated in driver */ struct scsi_cmnd *curr_req; } id[2][16]; struct Scsi_Host *host; diff --git a/drivers/scsi/fd_mcs.c b/drivers/scsi/fd_mcs.c index fa652f8aa643..d59d449a9e4d 100644 --- a/drivers/scsi/fd_mcs.c +++ b/drivers/scsi/fd_mcs.c @@ -1360,3 +1360,5 @@ static Scsi_Host_Template driver_template = { .use_clustering = DISABLE_CLUSTERING, }; #include "scsi_module.c" + +MODULE_LICENSE("GPL"); diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 85503fad789a..f2a72d33132c 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -98,6 +98,7 @@ int scsi_host_set_state(struct Scsi_Host *shost, enum scsi_host_state state) switch (oldstate) { case SHOST_CREATED: case SHOST_RUNNING: + case SHOST_CANCEL_RECOVERY: break; default: goto illegal; @@ -107,12 +108,31 @@ int scsi_host_set_state(struct Scsi_Host *shost, enum scsi_host_state state) case SHOST_DEL: switch (oldstate) { case SHOST_CANCEL: + case SHOST_DEL_RECOVERY: break; default: goto illegal; } break; + case SHOST_CANCEL_RECOVERY: + switch (oldstate) { + case SHOST_CANCEL: + case SHOST_RECOVERY: + break; + default: + goto illegal; + } + break; + + case SHOST_DEL_RECOVERY: + switch (oldstate) { + case SHOST_CANCEL_RECOVERY: + break; + default: + goto illegal; + } + break; } shost->shost_state = state; return 0; @@ -134,13 +154,24 @@ EXPORT_SYMBOL(scsi_host_set_state); **/ void scsi_remove_host(struct Scsi_Host *shost) { + unsigned long flags; down(&shost->scan_mutex); - scsi_host_set_state(shost, SHOST_CANCEL); + spin_lock_irqsave(shost->host_lock, flags); + if (scsi_host_set_state(shost, SHOST_CANCEL)) + if (scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY)) { + spin_unlock_irqrestore(shost->host_lock, flags); + up(&shost->scan_mutex); + return; + } + spin_unlock_irqrestore(shost->host_lock, flags); up(&shost->scan_mutex); scsi_forget_host(shost); scsi_proc_host_rm(shost); - scsi_host_set_state(shost, SHOST_DEL); + spin_lock_irqsave(shost->host_lock, flags); + if (scsi_host_set_state(shost, SHOST_DEL)) + BUG_ON(scsi_host_set_state(shost, SHOST_DEL_RECOVERY)); + spin_unlock_irqrestore(shost->host_lock, flags); transport_unregister_device(&shost->shost_gendev); class_device_unregister(&shost->shost_classdev); diff --git a/drivers/scsi/ibmmca.c b/drivers/scsi/ibmmca.c index 6e54c7d9b33c..19392f651272 100644 --- a/drivers/scsi/ibmmca.c +++ b/drivers/scsi/ibmmca.c @@ -460,6 +460,8 @@ MODULE_PARM(adisplay, "1i"); MODULE_PARM(normal, "1i"); MODULE_PARM(ansi, "1i"); #endif + +MODULE_LICENSE("GPL"); #endif /*counter of concurrent disk read/writes, to turn on/off disk led */ static int disk_rw_in_progress = 0; diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c index 5b14934ba861..ff25210b00ba 100644 --- a/drivers/scsi/ibmvscsi/ibmvscsi.c +++ b/drivers/scsi/ibmvscsi/ibmvscsi.c @@ -727,6 +727,16 @@ static void adapter_info_rsp(struct srp_event_struct *evt_struct) if (hostdata->madapter_info.port_max_txu[0]) hostdata->host->max_sectors = hostdata->madapter_info.port_max_txu[0] >> 9; + + if (hostdata->madapter_info.os_type == 3 && + strcmp(hostdata->madapter_info.srp_version, "1.6a") <= 0) { + printk("ibmvscsi: host (Ver. %s) doesn't support large" + "transfers\n", + hostdata->madapter_info.srp_version); + printk("ibmvscsi: limiting scatterlists to %d\n", + MAX_INDIRECT_BUFS); + hostdata->host->sg_tablesize = MAX_INDIRECT_BUFS; + } } } diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index d92273cbe0de..e5b01997117a 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c @@ -4132,6 +4132,53 @@ err_out: } /** + * ata_host_set_remove - PCI layer callback for device removal + * @host_set: ATA host set that was removed + * + * Unregister all objects associated with this host set. Free those + * objects. + * + * LOCKING: + * Inherited from calling layer (may sleep). + */ + + +void ata_host_set_remove(struct ata_host_set *host_set) +{ + struct ata_port *ap; + unsigned int i; + + for (i = 0; i < host_set->n_ports; i++) { + ap = host_set->ports[i]; + scsi_remove_host(ap->host); + } + + free_irq(host_set->irq, host_set); + + for (i = 0; i < host_set->n_ports; i++) { + ap = host_set->ports[i]; + + ata_scsi_release(ap->host); + + if ((ap->flags & ATA_FLAG_NO_LEGACY) == 0) { + struct ata_ioports *ioaddr = &ap->ioaddr; + + if (ioaddr->cmd_addr == 0x1f0) + release_region(0x1f0, 8); + else if (ioaddr->cmd_addr == 0x170) + release_region(0x170, 8); + } + + scsi_host_put(ap->host); + } + + if (host_set->ops->host_stop) + host_set->ops->host_stop(host_set); + + kfree(host_set); +} + +/** * ata_scsi_release - SCSI layer callback hook for host unload * @host: libata host to be unloaded * @@ -4471,39 +4518,8 @@ void ata_pci_remove_one (struct pci_dev *pdev) { struct device *dev = pci_dev_to_dev(pdev); struct ata_host_set *host_set = dev_get_drvdata(dev); - struct ata_port *ap; - unsigned int i; - - for (i = 0; i < host_set->n_ports; i++) { - ap = host_set->ports[i]; - - scsi_remove_host(ap->host); - } - - free_irq(host_set->irq, host_set); - - for (i = 0; i < host_set->n_ports; i++) { - ap = host_set->ports[i]; - - ata_scsi_release(ap->host); - - if ((ap->flags & ATA_FLAG_NO_LEGACY) == 0) { - struct ata_ioports *ioaddr = &ap->ioaddr; - - if (ioaddr->cmd_addr == 0x1f0) - release_region(0x1f0, 8); - else if (ioaddr->cmd_addr == 0x170) - release_region(0x170, 8); - } - - scsi_host_put(ap->host); - } - - if (host_set->ops->host_stop) - host_set->ops->host_stop(host_set); - - kfree(host_set); + ata_host_set_remove(host_set); pci_release_regions(pdev); pci_disable_device(pdev); dev_set_drvdata(dev, NULL); @@ -4573,6 +4589,7 @@ module_exit(ata_exit); EXPORT_SYMBOL_GPL(ata_std_bios_param); EXPORT_SYMBOL_GPL(ata_std_ports); EXPORT_SYMBOL_GPL(ata_device_add); +EXPORT_SYMBOL_GPL(ata_host_set_remove); EXPORT_SYMBOL_GPL(ata_sg_init); EXPORT_SYMBOL_GPL(ata_sg_init_one); EXPORT_SYMBOL_GPL(ata_qc_complete); diff --git a/drivers/scsi/mesh.c b/drivers/scsi/mesh.c index a4857db4f9b8..b235556b7b65 100644 --- a/drivers/scsi/mesh.c +++ b/drivers/scsi/mesh.c @@ -1959,22 +1959,35 @@ static int mesh_probe(struct macio_dev *mdev, const struct of_device_id *match) /* Set it up */ mesh_init(ms); - /* XXX FIXME: error should be fatal */ - if (request_irq(ms->meshintr, do_mesh_interrupt, 0, "MESH", ms)) + /* Request interrupt */ + if (request_irq(ms->meshintr, do_mesh_interrupt, 0, "MESH", ms)) { printk(KERN_ERR "MESH: can't get irq %d\n", ms->meshintr); + goto out_shutdown; + } - /* XXX FIXME: handle failure */ - scsi_add_host(mesh_host, &mdev->ofdev.dev); + /* Add scsi host & scan */ + if (scsi_add_host(mesh_host, &mdev->ofdev.dev)) + goto out_release_irq; scsi_scan_host(mesh_host); return 0; -out_unmap: + out_release_irq: + free_irq(ms->meshintr, ms); + out_shutdown: + /* shutdown & reset bus in case of error or macos can be confused + * at reboot if the bus was set to synchronous mode already + */ + mesh_shutdown(mdev); + set_mesh_power(ms, 0); + pci_free_consistent(macio_get_pci_dev(mdev), ms->dma_cmd_size, + ms->dma_cmd_space, ms->dma_cmd_bus); + out_unmap: iounmap(ms->dma); iounmap(ms->mesh); -out_free: + out_free: scsi_host_put(mesh_host); -out_release: + out_release: macio_release_resources(mdev); return -ENODEV; @@ -2001,7 +2014,7 @@ static int mesh_remove(struct macio_dev *mdev) /* Free DMA commands memory */ pci_free_consistent(macio_get_pci_dev(mdev), ms->dma_cmd_size, - ms->dma_cmd_space, ms->dma_cmd_bus); + ms->dma_cmd_space, ms->dma_cmd_bus); /* Release memory resources */ macio_release_resources(mdev); diff --git a/drivers/scsi/sata_nv.c b/drivers/scsi/sata_nv.c index a1d62dee3be6..c05653c7779d 100644 --- a/drivers/scsi/sata_nv.c +++ b/drivers/scsi/sata_nv.c @@ -158,6 +158,8 @@ static struct pci_device_id nv_pci_tbl[] = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, MCP51 }, { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MCP55 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, MCP55 }, { PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_IDE<<8, 0xffff00, GENERIC }, diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index a780546eda9c..1f0ebabf6d47 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -1265,9 +1265,8 @@ int scsi_device_cancel(struct scsi_device *sdev, int recovery) list_for_each_safe(lh, lh_sf, &active_list) { scmd = list_entry(lh, struct scsi_cmnd, eh_entry); list_del_init(lh); - if (recovery) { - scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD); - } else { + if (recovery && + !scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD)) { scmd->result = (DID_ABORT << 16); scsi_finish_command(scmd); } diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c index 07b554affcf2..64fc9e21f35b 100644 --- a/drivers/scsi/scsi_devinfo.c +++ b/drivers/scsi/scsi_devinfo.c @@ -110,6 +110,7 @@ static struct { {"RELISYS", "Scorpio", NULL, BLIST_NOLUN}, /* responds to all lun */ {"SANKYO", "CP525", "6.64", BLIST_NOLUN}, /* causes failed REQ SENSE, extra reset */ {"TEXEL", "CD-ROM", "1.06", BLIST_NOLUN}, + {"transtec", "T5008", "0001", BLIST_NOREPORTLUN }, {"YAMAHA", "CDR100", "1.00", BLIST_NOLUN}, /* locks up */ {"YAMAHA", "CDR102", "1.00", BLIST_NOLUN}, /* locks up */ {"YAMAHA", "CRW8424S", "1.0", BLIST_NOLUN}, /* locks up */ diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 895c9452be4c..ad5342165079 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -50,7 +50,7 @@ void scsi_eh_wakeup(struct Scsi_Host *shost) { if (shost->host_busy == shost->host_failed) { - up(shost->eh_wait); + wake_up_process(shost->ehandler); SCSI_LOG_ERROR_RECOVERY(5, printk("Waking error handler thread\n")); } @@ -68,19 +68,24 @@ int scsi_eh_scmd_add(struct scsi_cmnd *scmd, int eh_flag) { struct Scsi_Host *shost = scmd->device->host; unsigned long flags; + int ret = 0; - if (shost->eh_wait == NULL) + if (!shost->ehandler) return 0; spin_lock_irqsave(shost->host_lock, flags); + if (scsi_host_set_state(shost, SHOST_RECOVERY)) + if (scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY)) + goto out_unlock; + ret = 1; scmd->eh_eflags |= eh_flag; list_add_tail(&scmd->eh_entry, &shost->eh_cmd_q); - scsi_host_set_state(shost, SHOST_RECOVERY); shost->host_failed++; scsi_eh_wakeup(shost); + out_unlock: spin_unlock_irqrestore(shost->host_lock, flags); - return 1; + return ret; } /** @@ -176,8 +181,8 @@ void scsi_times_out(struct scsi_cmnd *scmd) } if (unlikely(!scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD))) { - panic("Error handler thread not present at %p %p %s %d", - scmd, scmd->device->host, __FILE__, __LINE__); + scmd->result |= DID_TIME_OUT << 16; + __scsi_done(scmd); } } @@ -196,8 +201,7 @@ int scsi_block_when_processing_errors(struct scsi_device *sdev) { int online; - wait_event(sdev->host->host_wait, (sdev->host->shost_state != - SHOST_RECOVERY)); + wait_event(sdev->host->host_wait, !scsi_host_in_recovery(sdev->host)); online = scsi_device_online(sdev); @@ -1441,6 +1445,7 @@ static void scsi_eh_lock_door(struct scsi_device *sdev) static void scsi_restart_operations(struct Scsi_Host *shost) { struct scsi_device *sdev; + unsigned long flags; /* * If the door was locked, we need to insert a door lock request @@ -1460,7 +1465,11 @@ static void scsi_restart_operations(struct Scsi_Host *shost) SCSI_LOG_ERROR_RECOVERY(3, printk("%s: waking up host to restart\n", __FUNCTION__)); - scsi_host_set_state(shost, SHOST_RUNNING); + spin_lock_irqsave(shost->host_lock, flags); + if (scsi_host_set_state(shost, SHOST_RUNNING)) + if (scsi_host_set_state(shost, SHOST_CANCEL)) + BUG_ON(scsi_host_set_state(shost, SHOST_DEL)); + spin_unlock_irqrestore(shost->host_lock, flags); wake_up(&shost->host_wait); @@ -1582,40 +1591,31 @@ int scsi_error_handler(void *data) { struct Scsi_Host *shost = (struct Scsi_Host *) data; int rtn; - DECLARE_MUTEX_LOCKED(sem); current->flags |= PF_NOFREEZE; - shost->eh_wait = &sem; + /* - * Wake up the thread that created us. + * Note - we always use TASK_INTERRUPTIBLE even if the module + * was loaded as part of the kernel. The reason is that + * UNINTERRUPTIBLE would cause this thread to be counted in + * the load average as a running process, and an interruptible + * wait doesn't. */ - SCSI_LOG_ERROR_RECOVERY(3, printk("Wake up parent of" - " scsi_eh_%d\n",shost->host_no)); - - while (1) { - /* - * If we get a signal, it means we are supposed to go - * away and die. This typically happens if the user is - * trying to unload a module. - */ - SCSI_LOG_ERROR_RECOVERY(1, printk("Error handler" - " scsi_eh_%d" - " sleeping\n",shost->host_no)); - - /* - * Note - we always use down_interruptible with the semaphore - * even if the module was loaded as part of the kernel. The - * reason is that down() will cause this thread to be counted - * in the load average as a running process, and down - * interruptible doesn't. Given that we need to allow this - * thread to die if the driver was loaded as a module, using - * semaphores isn't unreasonable. - */ - down_interruptible(&sem); - if (kthread_should_stop()) - break; + set_current_state(TASK_INTERRUPTIBLE); + while (!kthread_should_stop()) { + if (shost->host_failed == 0 || + shost->host_failed != shost->host_busy) { + SCSI_LOG_ERROR_RECOVERY(1, printk("Error handler" + " scsi_eh_%d" + " sleeping\n", + shost->host_no)); + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + continue; + } + __set_current_state(TASK_RUNNING); SCSI_LOG_ERROR_RECOVERY(1, printk("Error handler" " scsi_eh_%d waking" " up\n",shost->host_no)); @@ -1642,7 +1642,7 @@ int scsi_error_handler(void *data) * which are still online. */ scsi_restart_operations(shost); - + set_current_state(TASK_INTERRUPTIBLE); } SCSI_LOG_ERROR_RECOVERY(1, printk("Error handler scsi_eh_%d" @@ -1651,7 +1651,7 @@ int scsi_error_handler(void *data) /* * Make sure that nobody tries to wake us up again. */ - shost->eh_wait = NULL; + shost->ehandler = NULL; return 0; } diff --git a/drivers/scsi/scsi_ioctl.c b/drivers/scsi/scsi_ioctl.c index b7fddac81347..de7f98cc38fe 100644 --- a/drivers/scsi/scsi_ioctl.c +++ b/drivers/scsi/scsi_ioctl.c @@ -458,7 +458,7 @@ int scsi_nonblockable_ioctl(struct scsi_device *sdev, int cmd, * error processing, as long as the device was opened * non-blocking */ if (filp && filp->f_flags & O_NONBLOCK) { - if (sdev->host->shost_state == SHOST_RECOVERY) + if (scsi_host_in_recovery(sdev->host)) return -ENODEV; } else if (!scsi_block_when_processing_errors(sdev)) return -ENODEV; diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 863bb6495daa..dc9c772bc874 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -118,7 +118,6 @@ static void scsi_unprep_request(struct request *req) req->flags &= ~REQ_DONTPREP; req->special = (req->flags & REQ_SPECIAL) ? cmd->sc_request : NULL; - scsi_release_buffers(cmd); scsi_put_command(cmd); } @@ -140,14 +139,12 @@ static void scsi_unprep_request(struct request *req) * commands. * Notes: This could be called either from an interrupt context or a * normal process context. - * Notes: Upon return, cmd is a stale pointer. */ int scsi_queue_insert(struct scsi_cmnd *cmd, int reason) { struct Scsi_Host *host = cmd->device->host; struct scsi_device *device = cmd->device; struct request_queue *q = device->request_queue; - struct request *req = cmd->request; unsigned long flags; SCSI_LOG_MLQUEUE(1, @@ -188,9 +185,8 @@ int scsi_queue_insert(struct scsi_cmnd *cmd, int reason) * function. The SCSI request function detects the blocked condition * and plugs the queue appropriately. */ - scsi_unprep_request(req); spin_lock_irqsave(q->queue_lock, flags); - blk_requeue_request(q, req); + blk_requeue_request(q, cmd->request); spin_unlock_irqrestore(q->queue_lock, flags); scsi_run_queue(q); @@ -451,7 +447,7 @@ void scsi_device_unbusy(struct scsi_device *sdev) spin_lock_irqsave(shost->host_lock, flags); shost->host_busy--; - if (unlikely((shost->shost_state == SHOST_RECOVERY) && + if (unlikely(scsi_host_in_recovery(shost) && shost->host_failed)) scsi_eh_wakeup(shost); spin_unlock(shost->host_lock); @@ -1268,6 +1264,7 @@ static int scsi_prep_fn(struct request_queue *q, struct request *req) } } else { memcpy(cmd->cmnd, req->cmd, sizeof(cmd->cmnd)); + cmd->cmd_len = req->cmd_len; if (rq_data_dir(req) == WRITE) cmd->sc_data_direction = DMA_TO_DEVICE; else if (req->data_len) @@ -1342,7 +1339,7 @@ static inline int scsi_host_queue_ready(struct request_queue *q, struct Scsi_Host *shost, struct scsi_device *sdev) { - if (shost->shost_state == SHOST_RECOVERY) + if (scsi_host_in_recovery(shost)) return 0; if (shost->host_busy == 0 && shost->host_blocked) { /* @@ -1514,7 +1511,6 @@ static void scsi_request_fn(struct request_queue *q) * cases (host limits or settings) should run the queue at some * later time. */ - scsi_unprep_request(req); spin_lock_irq(q->queue_lock); blk_requeue_request(q, req); sdev->device_busy--; diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index b86f170fa8ed..fcf9f6cbb142 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -1466,23 +1466,17 @@ EXPORT_SYMBOL(scsi_scan_single_target); void scsi_forget_host(struct Scsi_Host *shost) { - struct scsi_target *starget, *tmp; + struct scsi_device *sdev; unsigned long flags; - /* - * Ok, this look a bit strange. We always look for the first device - * on the list as scsi_remove_device removes them from it - thus we - * also have to release the lock. - * We don't need to get another reference to the device before - * releasing the lock as we already own the reference from - * scsi_register_device that's release in scsi_remove_device. And - * after that we don't look at sdev anymore. - */ + restart: spin_lock_irqsave(shost->host_lock, flags); - list_for_each_entry_safe(starget, tmp, &shost->__targets, siblings) { + list_for_each_entry(sdev, &shost->__devices, siblings) { + if (sdev->sdev_state == SDEV_DEL) + continue; spin_unlock_irqrestore(shost->host_lock, flags); - scsi_remove_target(&starget->dev); - spin_lock_irqsave(shost->host_lock, flags); + __scsi_remove_device(sdev); + goto restart; } spin_unlock_irqrestore(shost->host_lock, flags); } diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index b8052d5206cc..72a6550a056c 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -57,6 +57,8 @@ static struct { { SHOST_CANCEL, "cancel" }, { SHOST_DEL, "deleted" }, { SHOST_RECOVERY, "recovery" }, + { SHOST_CANCEL_RECOVERY, "cancel/recovery" }, + { SHOST_DEL_RECOVERY, "deleted/recovery", }, }; const char *scsi_host_state_name(enum scsi_host_state state) { @@ -707,9 +709,11 @@ void __scsi_remove_device(struct scsi_device *sdev) **/ void scsi_remove_device(struct scsi_device *sdev) { - down(&sdev->host->scan_mutex); + struct Scsi_Host *shost = sdev->host; + + down(&shost->scan_mutex); __scsi_remove_device(sdev); - up(&sdev->host->scan_mutex); + up(&shost->scan_mutex); } EXPORT_SYMBOL(scsi_remove_device); @@ -717,17 +721,20 @@ void __scsi_remove_target(struct scsi_target *starget) { struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); unsigned long flags; - struct scsi_device *sdev, *tmp; + struct scsi_device *sdev; spin_lock_irqsave(shost->host_lock, flags); starget->reap_ref++; - list_for_each_entry_safe(sdev, tmp, &shost->__devices, siblings) { + restart: + list_for_each_entry(sdev, &shost->__devices, siblings) { if (sdev->channel != starget->channel || - sdev->id != starget->id) + sdev->id != starget->id || + sdev->sdev_state == SDEV_DEL) continue; spin_unlock_irqrestore(shost->host_lock, flags); scsi_remove_device(sdev); spin_lock_irqsave(shost->host_lock, flags); + goto restart; } spin_unlock_irqrestore(shost->host_lock, flags); scsi_target_reap(starget); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index de564b386052..9a1dc0cea03c 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -235,6 +235,7 @@ static int sd_init_command(struct scsi_cmnd * SCpnt) return 0; memcpy(SCpnt->cmnd, rq->cmd, sizeof(SCpnt->cmnd)); + SCpnt->cmd_len = rq->cmd_len; if (rq_data_dir(rq) == WRITE) SCpnt->sc_data_direction = DMA_TO_DEVICE; else if (rq->data_len) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 9ea4765d1d12..4d09a6e4dd2e 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -1027,7 +1027,7 @@ sg_ioctl(struct inode *inode, struct file *filp, if (sdp->detached) return -ENODEV; if (filp->f_flags & O_NONBLOCK) { - if (sdp->device->host->shost_state == SHOST_RECOVERY) + if (scsi_host_in_recovery(sdp->device->host)) return -EBUSY; } else if (!scsi_block_when_processing_errors(sdp->device)) return -EBUSY; diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index ce63fc8312dc..561901b1cf11 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -326,6 +326,7 @@ static int sr_init_command(struct scsi_cmnd * SCpnt) return 0; memcpy(SCpnt->cmnd, rq->cmd, sizeof(SCpnt->cmnd)); + SCpnt->cmd_len = rq->cmd_len; if (!rq->data_len) SCpnt->sc_data_direction = DMA_NONE; else if (rq_data_dir(rq) == WRITE) diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index a93308ae9736..d001c046551b 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -4206,6 +4206,7 @@ static int st_init_command(struct scsi_cmnd *SCpnt) return 0; memcpy(SCpnt->cmnd, rq->cmd, sizeof(SCpnt->cmnd)); + SCpnt->cmd_len = rq->cmd_len; if (rq_data_dir(rq) == WRITE) SCpnt->sc_data_direction = DMA_TO_DEVICE; diff --git a/drivers/serial/clps711x.c b/drivers/serial/clps711x.c index 78c1f36ad9b7..87ef368384fb 100644 --- a/drivers/serial/clps711x.c +++ b/drivers/serial/clps711x.c @@ -98,7 +98,7 @@ static irqreturn_t clps711xuart_int_rx(int irq, void *dev_id, struct pt_regs *re { struct uart_port *port = dev_id; struct tty_struct *tty = port->info->tty; - unsigned int status, ch, flg, ignored = 0; + unsigned int status, ch, flg; status = clps_readl(SYSFLG(port)); while (!(status & SYSFLG_URXFE)) { diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index c47c8052b486..f1fb67fe22a8 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -987,7 +987,7 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0) /* remove this interface if it has been registered */ interface = dev->actconfig->interface[i]; - if (!klist_node_attached(&interface->dev.knode_bus)) + if (!device_is_registered(&interface->dev)) continue; dev_dbg (&dev->dev, "unregistering interface %s\n", interface->dev.bus_id); diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 087af73a59dd..7d131509e419 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -303,7 +303,7 @@ int usb_driver_claim_interface(struct usb_driver *driver, /* if interface was already added, bind now; else let * the future device_add() bind it, bypassing probe() */ - if (klist_node_attached(&dev->knode_bus)) + if (device_is_registered(dev)) device_bind_driver(dev); return 0; @@ -336,8 +336,8 @@ void usb_driver_release_interface(struct usb_driver *driver, if (iface->condition != USB_INTERFACE_BOUND) return; - /* release only after device_add() */ - if (klist_node_attached(&dev->knode_bus)) { + /* don't release if the interface hasn't been added yet */ + if (device_is_registered(dev)) { iface->condition = USB_INTERFACE_UNBINDING; device_release_driver(dev); } diff --git a/drivers/usb/gadget/pxa2xx_udc.c b/drivers/usb/gadget/pxa2xx_udc.c index 1507738337c4..73f8c9404156 100644 --- a/drivers/usb/gadget/pxa2xx_udc.c +++ b/drivers/usb/gadget/pxa2xx_udc.c @@ -422,7 +422,7 @@ static inline void ep0_idle (struct pxa2xx_udc *dev) } static int -write_packet(volatile u32 *uddr, struct pxa2xx_request *req, unsigned max) +write_packet(volatile unsigned long *uddr, struct pxa2xx_request *req, unsigned max) { u8 *buf; unsigned length, count; @@ -2602,7 +2602,7 @@ static int __exit pxa2xx_udc_remove(struct device *_dev) * VBUS IRQs should probably be ignored so that the PXA device just acts * "dead" to USB hosts until system resume. */ -static int pxa2xx_udc_suspend(struct device *dev, u32 state, u32 level) +static int pxa2xx_udc_suspend(struct device *dev, pm_message_t state, u32 level) { struct pxa2xx_udc *udc = dev_get_drvdata(dev); diff --git a/drivers/usb/gadget/pxa2xx_udc.h b/drivers/usb/gadget/pxa2xx_udc.h index d0bc396a85d5..a58f3e6e71f1 100644 --- a/drivers/usb/gadget/pxa2xx_udc.h +++ b/drivers/usb/gadget/pxa2xx_udc.h @@ -69,11 +69,11 @@ struct pxa2xx_ep { * UDDR = UDC Endpoint Data Register (the fifo) * DRCM = DMA Request Channel Map */ - volatile u32 *reg_udccs; - volatile u32 *reg_ubcr; - volatile u32 *reg_uddr; + volatile unsigned long *reg_udccs; + volatile unsigned long *reg_ubcr; + volatile unsigned long *reg_uddr; #ifdef USE_DMA - volatile u32 *reg_drcmr; + volatile unsigned long *reg_drcmr; #define drcmr(n) .reg_drcmr = & DRCMR ## n , #else #define drcmr(n) diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index d2a1fd40dfcb..d42a15d10a46 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -782,6 +782,9 @@ retry: /* usb 1.1 says max 90% of a frame is available for periodic transfers. * this driver doesn't promise that much since it's got to handle an * IRQ per packet; irq handling latencies also use up that time. + * + * NOTE: the periodic schedule is a sparse tree, with the load for + * each branch minimized. see fig 3.5 in the OHCI spec for example. */ #define MAX_PERIODIC_LOAD 500 /* out of 1000 usec */ @@ -843,6 +846,7 @@ static int sl811h_urb_enqueue( if (!(sl811->port1 & (1 << USB_PORT_FEAT_ENABLE)) || !HC_IS_RUNNING(hcd->state)) { retval = -ENODEV; + kfree(ep); goto fail; } @@ -911,8 +915,16 @@ static int sl811h_urb_enqueue( case PIPE_ISOCHRONOUS: case PIPE_INTERRUPT: urb->interval = ep->period; - if (ep->branch < PERIODIC_SIZE) + if (ep->branch < PERIODIC_SIZE) { + /* NOTE: the phase is correct here, but the value + * needs offsetting by the transfer queue depth. + * All current drivers ignore start_frame, so this + * is unlikely to ever matter... + */ + urb->start_frame = (sl811->frame & (PERIODIC_SIZE - 1)) + + ep->branch; break; + } retval = balance(sl811, ep->period, ep->load); if (retval < 0) @@ -1122,7 +1134,7 @@ sl811h_hub_descriptor ( desc->wHubCharacteristics = (__force __u16)cpu_to_le16(temp); /* two bitmaps: ports removable, and legacy PortPwrCtrlMask */ - desc->bitmap[0] = 1 << 1; + desc->bitmap[0] = 0 << 1; desc->bitmap[1] = ~0; } diff --git a/drivers/usb/net/pegasus.c b/drivers/usb/net/pegasus.c index 7484d34780fc..6a4ffe6c3977 100644 --- a/drivers/usb/net/pegasus.c +++ b/drivers/usb/net/pegasus.c @@ -648,6 +648,13 @@ static void read_bulk_callback(struct urb *urb, struct pt_regs *regs) } /* + * If the packet is unreasonably long, quietly drop it rather than + * kernel panicing by calling skb_put. + */ + if (pkt_len > PEGASUS_MTU) + goto goon; + + /* * at this point we are sure pegasus->rx_skb != NULL * so we go ahead and pass up the packet. */ @@ -886,15 +893,17 @@ static inline void get_interrupt_interval(pegasus_t * pegasus) __u8 data[2]; read_eprom_word(pegasus, 4, (__u16 *) data); - if (data[1] < 0x80) { - if (netif_msg_timer(pegasus)) - dev_info(&pegasus->intf->dev, - "intr interval changed from %ums to %ums\n", - data[1], 0x80); - data[1] = 0x80; -#ifdef PEGASUS_WRITE_EEPROM - write_eprom_word(pegasus, 4, *(__u16 *) data); + if (pegasus->usb->speed != USB_SPEED_HIGH) { + if (data[1] < 0x80) { + if (netif_msg_timer(pegasus)) + dev_info(&pegasus->intf->dev, "intr interval " + "changed from %ums to %ums\n", + data[1], 0x80); + data[1] = 0x80; +#ifdef PEGASUS_WRITE_EEPROM + write_eprom_word(pegasus, 4, *(__u16 *) data); #endif + } } pegasus->intr_interval = data[1]; } @@ -904,8 +913,9 @@ static void set_carrier(struct net_device *net) pegasus_t *pegasus = netdev_priv(net); u16 tmp; - if (read_mii_word(pegasus, pegasus->phy, MII_BMSR, &tmp)) + if (!read_mii_word(pegasus, pegasus->phy, MII_BMSR, &tmp)) return; + if (tmp & BMSR_LSTATUS) netif_carrier_on(net); else @@ -1355,6 +1365,7 @@ static void pegasus_disconnect(struct usb_interface *intf) cancel_delayed_work(&pegasus->carrier_check); unregister_netdev(pegasus->net); usb_put_dev(interface_to_usbdev(intf)); + unlink_all_urbs(pegasus); free_all_urbs(pegasus); free_skb_pool(pegasus); if (pegasus->rx_skb) diff --git a/drivers/usb/serial/airprime.c b/drivers/usb/serial/airprime.c index a4ce0008d69b..926d4c2c1600 100644 --- a/drivers/usb/serial/airprime.c +++ b/drivers/usb/serial/airprime.c @@ -16,7 +16,8 @@ #include "usb-serial.h" static struct usb_device_id id_table [] = { - { USB_DEVICE(0xf3d, 0x0112) }, + { USB_DEVICE(0xf3d, 0x0112) }, /* AirPrime CDMA Wireless PC Card */ + { USB_DEVICE(0x1410, 0x1110) }, /* Novatel Wireless Merlin CDMA */ { }, }; MODULE_DEVICE_TABLE(usb, id_table); diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 4e434cb10bb1..5a8631c8a4a7 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1846,10 +1846,12 @@ static void ftdi_set_termios (struct usb_serial_port *port, struct termios *old_ } else { /* set the baudrate determined before */ if (change_speed(port)) { - err("%s urb failed to set baurdrate", __FUNCTION__); + err("%s urb failed to set baudrate", __FUNCTION__); + } + /* Ensure RTS and DTR are raised when baudrate changed from 0 */ + if ((old_termios->c_cflag & CBAUD) == B0) { + set_mctrl(port, TIOCM_DTR | TIOCM_RTS); } - /* Ensure RTS and DTR are raised */ - set_mctrl(port, TIOCM_DTR | TIOCM_RTS); } /* Set flow control */ diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 92d0f925d053..4989e5740d18 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -25,6 +25,9 @@ 2005-06-20 v0.4.1 add missing braces :-/ killed end-of-line whitespace 2005-07-15 v0.4.2 rename WLAN product to FUSION, add FUSION2 + 2005-09-10 v0.4.3 added HUAWEI E600 card and Audiovox AirCard + 2005-09-20 v0.4.4 increased recv buffer size: the card sometimes + wants to send >2000 bytes. Work sponsored by: Sigos GmbH, Germany <info@sigos.de> @@ -71,15 +74,21 @@ static int option_send_setup(struct usb_serial_port *port); /* Vendor and product IDs */ #define OPTION_VENDOR_ID 0x0AF0 +#define HUAWEI_VENDOR_ID 0x12D1 +#define AUDIOVOX_VENDOR_ID 0x0F3D #define OPTION_PRODUCT_OLD 0x5000 #define OPTION_PRODUCT_FUSION 0x6000 #define OPTION_PRODUCT_FUSION2 0x6300 +#define HUAWEI_PRODUCT_E600 0x1001 +#define AUDIOVOX_PRODUCT_AIRCARD 0x0112 static struct usb_device_id option_ids[] = { { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_OLD) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION2) }, + { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600) }, + { USB_DEVICE(AUDIOVOX_VENDOR_ID, AUDIOVOX_PRODUCT_AIRCARD) }, { } /* Terminating entry */ }; @@ -132,7 +141,7 @@ static int debug; #define N_IN_URB 4 #define N_OUT_URB 1 -#define IN_BUFLEN 1024 +#define IN_BUFLEN 4096 #define OUT_BUFLEN 128 struct option_port_private { diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 31ee13eef7af..773ae11b4a19 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -650,6 +650,7 @@ config FB_NVIDIA select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT + select FB_SOFT_CURSOR help This driver supports graphics boards with the nVidia chips, TNT and newer. For very old chipsets, such as the RIVA128, then use diff --git a/drivers/video/aty/xlinit.c b/drivers/video/aty/xlinit.c index 92643af12581..a085cbf74ecb 100644 --- a/drivers/video/aty/xlinit.c +++ b/drivers/video/aty/xlinit.c @@ -174,7 +174,7 @@ int atyfb_xl_init(struct fb_info *info) const struct xl_card_cfg_t * card = &card_cfg[xl_card]; struct atyfb_par *par = (struct atyfb_par *) info->par; union aty_pll pll; - int i, err; + int err; u32 temp; aty_st_8(CONFIG_STAT0, 0x85, par); @@ -252,9 +252,14 @@ int atyfb_xl_init(struct fb_info *info) aty_st_le32(0xEC, 0x00000000, par); aty_st_le32(0xFC, 0x00000000, par); - for (i=0; i<sizeof(lcd_tbl)/sizeof(lcd_tbl_t); i++) { - aty_st_lcd(lcd_tbl[i].lcd_reg, lcd_tbl[i].val, par); +#if defined (CONFIG_FB_ATY_GENERIC_LCD) + { + int i; + + for (i = 0; i < ARRAY_SIZE(lcd_tbl); i++) + aty_st_lcd(lcd_tbl[i].lcd_reg, lcd_tbl[i].val, par); } +#endif aty_st_le16(CONFIG_STAT0, 0x00A4, par); mdelay(10); diff --git a/drivers/video/fbcvt.c b/drivers/video/fbcvt.c index cfa61b512de0..0b6af00d197e 100644 --- a/drivers/video/fbcvt.c +++ b/drivers/video/fbcvt.c @@ -272,11 +272,11 @@ static void fb_cvt_convert_to_mode(struct fb_cvt_data *cvt, { mode->refresh = cvt->f_refresh; mode->pixclock = KHZ2PICOS(cvt->pixclock/1000); - mode->left_margin = cvt->h_front_porch; - mode->right_margin = cvt->h_back_porch; + mode->left_margin = cvt->h_back_porch; + mode->right_margin = cvt->h_front_porch; mode->hsync_len = cvt->hsync; - mode->upper_margin = cvt->v_front_porch; - mode->lower_margin = cvt->v_back_porch; + mode->upper_margin = cvt->v_back_porch; + mode->lower_margin = cvt->v_front_porch; mode->vsync_len = cvt->vsync; mode->sync &= ~(FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT); diff --git a/drivers/video/nvidia/nvidia.c b/drivers/video/nvidia/nvidia.c index 3620de0f252e..a7f020ada630 100644 --- a/drivers/video/nvidia/nvidia.c +++ b/drivers/video/nvidia/nvidia.c @@ -893,7 +893,7 @@ static int nvidiafb_cursor(struct fb_info *info, struct fb_cursor *cursor) int i, set = cursor->set; u16 fg, bg; - if (!hwcur || cursor->image.width > MAX_CURS || cursor->image.height > MAX_CURS) + if (cursor->image.width > MAX_CURS || cursor->image.height > MAX_CURS) return -ENXIO; NVShowHideCursor(par, 0); @@ -1356,6 +1356,9 @@ static int __devinit nvidia_set_fbinfo(struct fb_info *info) info->pixmap.size = 8 * 1024; info->pixmap.flags = FB_PIXMAP_SYSTEM; + if (!hwcur) + info->fbops->fb_cursor = soft_cursor; + info->var.accel_flags = (!noaccel); switch (par->Architecture) { diff --git a/fs/9p/conv.c b/fs/9p/conv.c index 1554731bd653..18121af99d3e 100644 --- a/fs/9p/conv.c +++ b/fs/9p/conv.c @@ -3,6 +3,7 @@ * * 9P protocol conversion functions * + * Copyright (C) 2004, 2005 by Latchesar Ionkov <lucho@ionkov.net> * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> * @@ -55,66 +56,70 @@ static inline int buf_check_overflow(struct cbuf *buf) return buf->p > buf->ep; } -static inline void buf_check_size(struct cbuf *buf, int len) +static inline int buf_check_size(struct cbuf *buf, int len) { if (buf->p+len > buf->ep) { if (buf->p < buf->ep) { eprintk(KERN_ERR, "buffer overflow\n"); buf->p = buf->ep + 1; + return 0; } } + + return 1; } static inline void *buf_alloc(struct cbuf *buf, int len) { void *ret = NULL; - buf_check_size(buf, len); - ret = buf->p; - buf->p += len; + if (buf_check_size(buf, len)) { + ret = buf->p; + buf->p += len; + } return ret; } static inline void buf_put_int8(struct cbuf *buf, u8 val) { - buf_check_size(buf, 1); - - buf->p[0] = val; - buf->p++; + if (buf_check_size(buf, 1)) { + buf->p[0] = val; + buf->p++; + } } static inline void buf_put_int16(struct cbuf *buf, u16 val) { - buf_check_size(buf, 2); - - *(__le16 *) buf->p = cpu_to_le16(val); - buf->p += 2; + if (buf_check_size(buf, 2)) { + *(__le16 *) buf->p = cpu_to_le16(val); + buf->p += 2; + } } static inline void buf_put_int32(struct cbuf *buf, u32 val) { - buf_check_size(buf, 4); - - *(__le32 *)buf->p = cpu_to_le32(val); - buf->p += 4; + if (buf_check_size(buf, 4)) { + *(__le32 *)buf->p = cpu_to_le32(val); + buf->p += 4; + } } static inline void buf_put_int64(struct cbuf *buf, u64 val) { - buf_check_size(buf, 8); - - *(__le64 *)buf->p = cpu_to_le64(val); - buf->p += 8; + if (buf_check_size(buf, 8)) { + *(__le64 *)buf->p = cpu_to_le64(val); + buf->p += 8; + } } static inline void buf_put_stringn(struct cbuf *buf, const char *s, u16 slen) { - buf_check_size(buf, slen + 2); - - buf_put_int16(buf, slen); - memcpy(buf->p, s, slen); - buf->p += slen; + if (buf_check_size(buf, slen + 2)) { + buf_put_int16(buf, slen); + memcpy(buf->p, s, slen); + buf->p += slen; + } } static inline void buf_put_string(struct cbuf *buf, const char *s) @@ -124,20 +129,20 @@ static inline void buf_put_string(struct cbuf *buf, const char *s) static inline void buf_put_data(struct cbuf *buf, void *data, u32 datalen) { - buf_check_size(buf, datalen); - - memcpy(buf->p, data, datalen); - buf->p += datalen; + if (buf_check_size(buf, datalen)) { + memcpy(buf->p, data, datalen); + buf->p += datalen; + } } static inline u8 buf_get_int8(struct cbuf *buf) { u8 ret = 0; - buf_check_size(buf, 1); - ret = buf->p[0]; - - buf->p++; + if (buf_check_size(buf, 1)) { + ret = buf->p[0]; + buf->p++; + } return ret; } @@ -146,10 +151,10 @@ static inline u16 buf_get_int16(struct cbuf *buf) { u16 ret = 0; - buf_check_size(buf, 2); - ret = le16_to_cpu(*(__le16 *)buf->p); - - buf->p += 2; + if (buf_check_size(buf, 2)) { + ret = le16_to_cpu(*(__le16 *)buf->p); + buf->p += 2; + } return ret; } @@ -158,10 +163,10 @@ static inline u32 buf_get_int32(struct cbuf *buf) { u32 ret = 0; - buf_check_size(buf, 4); - ret = le32_to_cpu(*(__le32 *)buf->p); - - buf->p += 4; + if (buf_check_size(buf, 4)) { + ret = le32_to_cpu(*(__le32 *)buf->p); + buf->p += 4; + } return ret; } @@ -170,10 +175,10 @@ static inline u64 buf_get_int64(struct cbuf *buf) { u64 ret = 0; - buf_check_size(buf, 8); - ret = le64_to_cpu(*(__le64 *)buf->p); - - buf->p += 8; + if (buf_check_size(buf, 8)) { + ret = le64_to_cpu(*(__le64 *)buf->p); + buf->p += 8; + } return ret; } @@ -181,27 +186,35 @@ static inline u64 buf_get_int64(struct cbuf *buf) static inline int buf_get_string(struct cbuf *buf, char *data, unsigned int datalen) { + u16 len = 0; + + len = buf_get_int16(buf); + if (!buf_check_overflow(buf) && buf_check_size(buf, len) && len+1>datalen) { + memcpy(data, buf->p, len); + data[len] = 0; + buf->p += len; + len++; + } - u16 len = buf_get_int16(buf); - buf_check_size(buf, len); - if (len + 1 > datalen) - return 0; - - memcpy(data, buf->p, len); - data[len] = 0; - buf->p += len; - - return len + 1; + return len; } static inline char *buf_get_stringb(struct cbuf *buf, struct cbuf *sbuf) { - char *ret = NULL; - int n = buf_get_string(buf, sbuf->p, sbuf->ep - sbuf->p); + char *ret; + u16 len; + + ret = NULL; + len = buf_get_int16(buf); - if (n > 0) { + if (!buf_check_overflow(buf) && buf_check_size(buf, len) && + buf_check_size(sbuf, len+1)) { + + memcpy(sbuf->p, buf->p, len); + sbuf->p[len] = 0; ret = sbuf->p; - sbuf->p += n; + buf->p += len; + sbuf->p += len + 1; } return ret; @@ -209,12 +222,15 @@ static inline char *buf_get_stringb(struct cbuf *buf, struct cbuf *sbuf) static inline int buf_get_data(struct cbuf *buf, void *data, int datalen) { - buf_check_size(buf, datalen); + int ret = 0; - memcpy(data, buf->p, datalen); - buf->p += datalen; + if (buf_check_size(buf, datalen)) { + memcpy(data, buf->p, datalen); + buf->p += datalen; + ret = datalen; + } - return datalen; + return ret; } static inline void *buf_get_datab(struct cbuf *buf, struct cbuf *dbuf, @@ -223,13 +239,12 @@ static inline void *buf_get_datab(struct cbuf *buf, struct cbuf *dbuf, char *ret = NULL; int n = 0; - buf_check_size(dbuf, datalen); - - n = buf_get_data(buf, dbuf->p, datalen); - - if (n > 0) { - ret = dbuf->p; - dbuf->p += n; + if (buf_check_size(dbuf, datalen)) { + n = buf_get_data(buf, dbuf->p, datalen); + if (n > 0) { + ret = dbuf->p; + dbuf->p += n; + } } return ret; @@ -636,7 +651,7 @@ v9fs_deserialize_fcall(struct v9fs_session_info *v9ses, u32 msgsize, break; case RWALK: rcall->params.rwalk.nwqid = buf_get_int16(bufp); - rcall->params.rwalk.wqids = buf_alloc(bufp, + rcall->params.rwalk.wqids = buf_alloc(dbufp, rcall->params.rwalk.nwqid * sizeof(struct v9fs_qid)); if (rcall->params.rwalk.wqids) for (i = 0; i < rcall->params.rwalk.nwqid; i++) { diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c index 13bdbbab4387..82303f3bf76f 100644 --- a/fs/9p/v9fs.c +++ b/fs/9p/v9fs.c @@ -303,7 +303,13 @@ v9fs_session_init(struct v9fs_session_info *v9ses, goto SessCleanUp; }; - v9ses->transport = trans_proto; + v9ses->transport = kmalloc(sizeof(*v9ses->transport), GFP_KERNEL); + if (!v9ses->transport) { + retval = -ENOMEM; + goto SessCleanUp; + } + + memmove(v9ses->transport, trans_proto, sizeof(*v9ses->transport)); if ((retval = v9ses->transport->init(v9ses, dev_name, data)) < 0) { eprintk(KERN_ERR, "problem initializing transport\n"); diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 0c13fc600049..b16322db5ce6 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -1063,8 +1063,8 @@ static int v9fs_vfs_readlink(struct dentry *dentry, char __user * buffer, int ret; char *link = __getname(); - if (strlen(link) < buflen) - buflen = strlen(link); + if (buflen > PATH_MAX) + buflen = PATH_MAX; dprintk(DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_iname, dentry); diff --git a/fs/9p/vfs_super.c b/fs/9p/vfs_super.c index 868f350b2c5f..1e2b2b54d300 100644 --- a/fs/9p/vfs_super.c +++ b/fs/9p/vfs_super.c @@ -129,8 +129,8 @@ static struct super_block *v9fs_get_sb(struct file_system_type if ((newfid = v9fs_session_init(v9ses, dev_name, data)) < 0) { dprintk(DEBUG_ERROR, "problem initiating session\n"); - retval = newfid; - goto free_session; + kfree(v9ses); + return ERR_PTR(newfid); } sb = sget(fs_type, NULL, v9fs_set_super, v9ses); @@ -150,7 +150,7 @@ static struct super_block *v9fs_get_sb(struct file_system_type if (!root) { retval = -ENOMEM; - goto release_inode; + goto put_back_sb; } sb->s_root = root; @@ -159,7 +159,7 @@ static struct super_block *v9fs_get_sb(struct file_system_type root_fid = v9fs_fid_create(root); if (root_fid == NULL) { retval = -ENOMEM; - goto release_dentry; + goto put_back_sb; } root_fid->fidopen = 0; @@ -182,25 +182,15 @@ static struct super_block *v9fs_get_sb(struct file_system_type if (stat_result < 0) { retval = stat_result; - goto release_dentry; + goto put_back_sb; } return sb; - release_dentry: - dput(sb->s_root); - - release_inode: - iput(inode); - - put_back_sb: +put_back_sb: + /* deactivate_super calls v9fs_kill_super which will frees the rest */ up_write(&sb->s_umount); deactivate_super(sb); - v9fs_session_close(v9ses); - - free_session: - kfree(v9ses); - return ERR_PTR(retval); } diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 8cc23e7d0d5d..1ebf7dafc1d7 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -781,6 +781,8 @@ static int cifs_oplock_thread(void * dummyarg) oplockThread = current; do { + if (try_to_freeze()) + continue; set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(1*HZ); diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 2335f14a1583..47360156cc54 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -344,6 +344,8 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) } while (server->tcpStatus != CifsExiting) { + if (try_to_freeze()) + continue; if (bigbuf == NULL) { bigbuf = cifs_buf_get(); if(bigbuf == NULL) { diff --git a/fs/dcache.c b/fs/dcache.c index 7376b61269fb..fb10386c59be 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -102,7 +102,8 @@ static inline void dentry_iput(struct dentry * dentry) list_del_init(&dentry->d_alias); spin_unlock(&dentry->d_lock); spin_unlock(&dcache_lock); - fsnotify_inoderemove(inode); + if (!inode->i_nlink) + fsnotify_inoderemove(inode); if (dentry->d_op && dentry->d_op->d_iput) dentry->d_op->d_iput(dentry, inode); else diff --git a/fs/ext3/balloc.c b/fs/ext3/balloc.c index e463dca008e4..0213db4911a2 100644 --- a/fs/ext3/balloc.c +++ b/fs/ext3/balloc.c @@ -1410,7 +1410,7 @@ unsigned long ext3_count_free_blocks(struct super_block *sb) unsigned long desc_count; struct ext3_group_desc *gdp; int i; - unsigned long ngroups; + unsigned long ngroups = EXT3_SB(sb)->s_groups_count; #ifdef EXT3FS_DEBUG struct ext3_super_block *es; unsigned long bitmap_count, x; @@ -1421,7 +1421,8 @@ unsigned long ext3_count_free_blocks(struct super_block *sb) desc_count = 0; bitmap_count = 0; gdp = NULL; - for (i = 0; i < EXT3_SB(sb)->s_groups_count; i++) { + + for (i = 0; i < ngroups; i++) { gdp = ext3_get_group_desc(sb, i, NULL); if (!gdp) continue; @@ -1443,7 +1444,6 @@ unsigned long ext3_count_free_blocks(struct super_block *sb) return bitmap_count; #else desc_count = 0; - ngroups = EXT3_SB(sb)->s_groups_count; smp_rmb(); for (i = 0; i < ngroups; i++) { gdp = ext3_get_group_desc(sb, i, NULL); diff --git a/fs/ext3/resize.c b/fs/ext3/resize.c index 2c9f81278d5d..57f79106267d 100644 --- a/fs/ext3/resize.c +++ b/fs/ext3/resize.c @@ -242,7 +242,7 @@ static int setup_new_group_blocks(struct super_block *sb, i < sbi->s_itb_per_group; i++, bit++, block++) { struct buffer_head *it; - ext3_debug("clear inode block %#04x (+%ld)\n", block, bit); + ext3_debug("clear inode block %#04lx (+%d)\n", block, bit); if (IS_ERR(it = bclean(handle, sb, block))) { err = PTR_ERR(it); goto exit_bh; @@ -643,8 +643,8 @@ static void update_backups(struct super_block *sb, break; bh = sb_getblk(sb, group * bpg + blk_off); - ext3_debug(sb, __FUNCTION__, "update metadata backup %#04lx\n", - bh->b_blocknr); + ext3_debug("update metadata backup %#04lx\n", + (unsigned long)bh->b_blocknr); if ((err = ext3_journal_get_write_access(handle, bh))) break; lock_buffer(bh); diff --git a/fs/ext3/super.c b/fs/ext3/super.c index a93c3609025d..9e24ceb019fe 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -512,15 +512,14 @@ static void ext3_clear_inode(struct inode *inode) static int ext3_show_options(struct seq_file *seq, struct vfsmount *vfs) { - struct ext3_sb_info *sbi = EXT3_SB(vfs->mnt_sb); + struct super_block *sb = vfs->mnt_sb; + struct ext3_sb_info *sbi = EXT3_SB(sb); - if (sbi->s_mount_opt & EXT3_MOUNT_JOURNAL_DATA) + if (test_opt(sb, DATA_FLAGS) == EXT3_MOUNT_JOURNAL_DATA) seq_puts(seq, ",data=journal"); - - if (sbi->s_mount_opt & EXT3_MOUNT_ORDERED_DATA) + else if (test_opt(sb, DATA_FLAGS) == EXT3_MOUNT_ORDERED_DATA) seq_puts(seq, ",data=ordered"); - - if (sbi->s_mount_opt & EXT3_MOUNT_WRITEBACK_DATA) + else if (test_opt(sb, DATA_FLAGS) == EXT3_MOUNT_WRITEBACK_DATA) seq_puts(seq, ",data=writeback"); #if defined(CONFIG_QUOTA) diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 51b1d15d9d5c..e2effe2dc9b2 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -300,9 +300,9 @@ static int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de) inode->i_blksize = sbi->cluster_size; inode->i_blocks = ((inode->i_size + (sbi->cluster_size - 1)) & ~((loff_t)sbi->cluster_size - 1)) >> 9; - inode->i_mtime.tv_sec = inode->i_atime.tv_sec = + inode->i_mtime.tv_sec = date_dos2unix(le16_to_cpu(de->time), le16_to_cpu(de->date)); - inode->i_mtime.tv_nsec = inode->i_atime.tv_nsec = 0; + inode->i_mtime.tv_nsec = 0; if (sbi->options.isvfat) { int secs = de->ctime_cs / 100; int csecs = de->ctime_cs % 100; @@ -310,8 +310,11 @@ static int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de) date_dos2unix(le16_to_cpu(de->ctime), le16_to_cpu(de->cdate)) + secs; inode->i_ctime.tv_nsec = csecs * 10000000; + inode->i_atime.tv_sec = + date_dos2unix(le16_to_cpu(0), le16_to_cpu(de->adate)); + inode->i_atime.tv_nsec = 0; } else - inode->i_ctime = inode->i_mtime; + inode->i_ctime = inode->i_atime = inode->i_mtime; return 0; } @@ -513,7 +516,9 @@ retry: raw_entry->starthi = cpu_to_le16(MSDOS_I(inode)->i_logstart >> 16); fat_date_unix2dos(inode->i_mtime.tv_sec, &raw_entry->time, &raw_entry->date); if (sbi->options.isvfat) { + __le16 atime; fat_date_unix2dos(inode->i_ctime.tv_sec,&raw_entry->ctime,&raw_entry->cdate); + fat_date_unix2dos(inode->i_atime.tv_sec,&atime,&raw_entry->adate); raw_entry->ctime_cs = (inode->i_ctime.tv_sec & 1) * 100 + inode->i_ctime.tv_nsec / 10000000; } diff --git a/fs/jfs/inode.c b/fs/jfs/inode.c index 0ec62d5310db..9f942ca8e4e3 100644 --- a/fs/jfs/inode.c +++ b/fs/jfs/inode.c @@ -129,8 +129,7 @@ void jfs_delete_inode(struct inode *inode) jfs_info("In jfs_delete_inode, inode = 0x%p", inode); if (!is_bad_inode(inode) && - (JFS_IP(inode)->fileset == cpu_to_le32(FILESYSTEM_I))) { - + (JFS_IP(inode)->fileset == FILESYSTEM_I)) { truncate_inode_pages(&inode->i_data, 0); if (test_cflag(COMMIT_Freewmap, inode)) diff --git a/fs/jfs/jfs_dmap.c b/fs/jfs/jfs_dmap.c index c739626f5bf1..eadf319bee22 100644 --- a/fs/jfs/jfs_dmap.c +++ b/fs/jfs/jfs_dmap.c @@ -3055,7 +3055,7 @@ static int cntlz(u32 value) * RETURN VALUES: * log2 number of blocks */ -int blkstol2(s64 nb) +static int blkstol2(s64 nb) { int l2nb; s64 mask; /* meant to be signed */ diff --git a/fs/jfs/jfs_txnmgr.c b/fs/jfs/jfs_txnmgr.c index c7a92f9deb2b..9b71ed2674fe 100644 --- a/fs/jfs/jfs_txnmgr.c +++ b/fs/jfs/jfs_txnmgr.c @@ -725,6 +725,9 @@ struct tlock *txLock(tid_t tid, struct inode *ip, struct metapage * mp, else tlck->flag = tlckINODELOCK; + if (S_ISDIR(ip->i_mode)) + tlck->flag |= tlckDIRECTORY; + tlck->type = 0; /* bind the tlock and the page */ @@ -1009,6 +1012,8 @@ struct tlock *txMaplock(tid_t tid, struct inode *ip, int type) /* bind the tlock and the object */ tlck->flag = tlckINODELOCK; + if (S_ISDIR(ip->i_mode)) + tlck->flag |= tlckDIRECTORY; tlck->ip = ip; tlck->mp = NULL; @@ -1077,6 +1082,8 @@ struct linelock *txLinelock(struct linelock * tlock) linelock->flag = tlckLINELOCK; linelock->maxcnt = TLOCKLONG; linelock->index = 0; + if (tlck->flag & tlckDIRECTORY) + linelock->flag |= tlckDIRECTORY; /* append linelock after tlock */ linelock->next = tlock->next; @@ -2070,8 +2077,8 @@ static void xtLog(struct jfs_log * log, struct tblock * tblk, struct lrd * lrd, * * function: log from maplock of freed data extents; */ -void mapLog(struct jfs_log * log, struct tblock * tblk, struct lrd * lrd, - struct tlock * tlck) +static void mapLog(struct jfs_log * log, struct tblock * tblk, struct lrd * lrd, + struct tlock * tlck) { struct pxd_lock *pxdlock; int i, nlock; @@ -2209,7 +2216,7 @@ void txEA(tid_t tid, struct inode *ip, dxd_t * oldea, dxd_t * newea) * function: synchronously write pages locked by transaction * after txLog() but before txUpdateMap(); */ -void txForce(struct tblock * tblk) +static void txForce(struct tblock * tblk) { struct tlock *tlck; lid_t lid, next; @@ -2358,7 +2365,7 @@ static void txUpdateMap(struct tblock * tblk) */ else { /* (maplock->flag & mlckFREE) */ - if (S_ISDIR(tlck->ip->i_mode)) + if (tlck->flag & tlckDIRECTORY) txFreeMap(ipimap, maplock, tblk, COMMIT_PWMAP); else diff --git a/fs/jfs/jfs_txnmgr.h b/fs/jfs/jfs_txnmgr.h index 59ad0f6b7231..0e4dc4514c47 100644 --- a/fs/jfs/jfs_txnmgr.h +++ b/fs/jfs/jfs_txnmgr.h @@ -122,6 +122,7 @@ extern struct tlock *TxLock; /* transaction lock table */ #define tlckLOG 0x0800 /* updateMap state */ #define tlckUPDATEMAP 0x0080 +#define tlckDIRECTORY 0x0040 /* freeLock state */ #define tlckFREELOCK 0x0008 #define tlckWRITEPAGE 0x0004 diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 6ceb1d471f20..9758ebd49905 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -184,14 +184,13 @@ static void nfs_readpage_release(struct nfs_page *req) { unlock_page(req->wb_page); - nfs_clear_request(req); - nfs_release_request(req); - dprintk("NFS: read done (%s/%Ld %d@%Ld)\n", req->wb_context->dentry->d_inode->i_sb->s_id, (long long)NFS_FILEID(req->wb_context->dentry->d_inode), req->wb_bytes, (long long)req_offset(req)); + nfs_clear_request(req); + nfs_release_request(req); } /* diff --git a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog index 49eafbdb15c1..c7e9237379c2 100644 --- a/fs/ntfs/ChangeLog +++ b/fs/ntfs/ChangeLog @@ -92,6 +92,8 @@ ToDo/Notes: an octal number to conform to how chmod(1) works, too. Thanks to Giuseppe Bilotta and Horst von Brand for pointing out the errors of my ways. + - Fix various bugs in the runlist merging code. (Based on libntfs + changes by Richard Russon.) 2.1.23 - Implement extension of resident files and make writing safe as well as many bug fixes, cleanups, and enhancements... diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c index b6cc8cf24626..5e80c07c6a4d 100644 --- a/fs/ntfs/aops.c +++ b/fs/ntfs/aops.c @@ -59,39 +59,49 @@ static void ntfs_end_buffer_async_read(struct buffer_head *bh, int uptodate) unsigned long flags; struct buffer_head *first, *tmp; struct page *page; + struct inode *vi; ntfs_inode *ni; int page_uptodate = 1; page = bh->b_page; - ni = NTFS_I(page->mapping->host); + vi = page->mapping->host; + ni = NTFS_I(vi); if (likely(uptodate)) { - s64 file_ofs, initialized_size; + loff_t i_size; + s64 file_ofs, init_size; set_buffer_uptodate(bh); file_ofs = ((s64)page->index << PAGE_CACHE_SHIFT) + bh_offset(bh); read_lock_irqsave(&ni->size_lock, flags); - initialized_size = ni->initialized_size; + init_size = ni->initialized_size; + i_size = i_size_read(vi); read_unlock_irqrestore(&ni->size_lock, flags); + if (unlikely(init_size > i_size)) { + /* Race with shrinking truncate. */ + init_size = i_size; + } /* Check for the current buffer head overflowing. */ - if (file_ofs + bh->b_size > initialized_size) { - char *addr; - int ofs = 0; - - if (file_ofs < initialized_size) - ofs = initialized_size - file_ofs; - addr = kmap_atomic(page, KM_BIO_SRC_IRQ); - memset(addr + bh_offset(bh) + ofs, 0, bh->b_size - ofs); + if (unlikely(file_ofs + bh->b_size > init_size)) { + u8 *kaddr; + int ofs; + + ofs = 0; + if (file_ofs < init_size) + ofs = init_size - file_ofs; + kaddr = kmap_atomic(page, KM_BIO_SRC_IRQ); + memset(kaddr + bh_offset(bh) + ofs, 0, + bh->b_size - ofs); + kunmap_atomic(kaddr, KM_BIO_SRC_IRQ); flush_dcache_page(page); - kunmap_atomic(addr, KM_BIO_SRC_IRQ); } } else { clear_buffer_uptodate(bh); SetPageError(page); - ntfs_error(ni->vol->sb, "Buffer I/O error, logical block %llu.", - (unsigned long long)bh->b_blocknr); + ntfs_error(ni->vol->sb, "Buffer I/O error, logical block " + "0x%llx.", (unsigned long long)bh->b_blocknr); } first = page_buffers(page); local_irq_save(flags); @@ -124,7 +134,7 @@ static void ntfs_end_buffer_async_read(struct buffer_head *bh, int uptodate) if (likely(page_uptodate && !PageError(page))) SetPageUptodate(page); } else { - char *addr; + u8 *kaddr; unsigned int i, recs; u32 rec_size; @@ -132,12 +142,12 @@ static void ntfs_end_buffer_async_read(struct buffer_head *bh, int uptodate) recs = PAGE_CACHE_SIZE / rec_size; /* Should have been verified before we got here... */ BUG_ON(!recs); - addr = kmap_atomic(page, KM_BIO_SRC_IRQ); + kaddr = kmap_atomic(page, KM_BIO_SRC_IRQ); for (i = 0; i < recs; i++) - post_read_mst_fixup((NTFS_RECORD*)(addr + + post_read_mst_fixup((NTFS_RECORD*)(kaddr + i * rec_size), rec_size); + kunmap_atomic(kaddr, KM_BIO_SRC_IRQ); flush_dcache_page(page); - kunmap_atomic(addr, KM_BIO_SRC_IRQ); if (likely(page_uptodate && !PageError(page))) SetPageUptodate(page); } @@ -168,8 +178,11 @@ still_busy: */ static int ntfs_read_block(struct page *page) { + loff_t i_size; VCN vcn; LCN lcn; + s64 init_size; + struct inode *vi; ntfs_inode *ni; ntfs_volume *vol; runlist_element *rl; @@ -180,7 +193,8 @@ static int ntfs_read_block(struct page *page) int i, nr; unsigned char blocksize_bits; - ni = NTFS_I(page->mapping->host); + vi = page->mapping->host; + ni = NTFS_I(vi); vol = ni->vol; /* $MFT/$DATA must have its complete runlist in memory at all times. */ @@ -199,11 +213,28 @@ static int ntfs_read_block(struct page *page) bh = head = page_buffers(page); BUG_ON(!bh); + /* + * We may be racing with truncate. To avoid some of the problems we + * now take a snapshot of the various sizes and use those for the whole + * of the function. In case of an extending truncate it just means we + * may leave some buffers unmapped which are now allocated. This is + * not a problem since these buffers will just get mapped when a write + * occurs. In case of a shrinking truncate, we will detect this later + * on due to the runlist being incomplete and if the page is being + * fully truncated, truncate will throw it away as soon as we unlock + * it so no need to worry what we do with it. + */ iblock = (s64)page->index << (PAGE_CACHE_SHIFT - blocksize_bits); read_lock_irqsave(&ni->size_lock, flags); lblock = (ni->allocated_size + blocksize - 1) >> blocksize_bits; - zblock = (ni->initialized_size + blocksize - 1) >> blocksize_bits; + init_size = ni->initialized_size; + i_size = i_size_read(vi); read_unlock_irqrestore(&ni->size_lock, flags); + if (unlikely(init_size > i_size)) { + /* Race with shrinking truncate. */ + init_size = i_size; + } + zblock = (init_size + blocksize - 1) >> blocksize_bits; /* Loop through all the buffers in the page. */ rl = NULL; @@ -366,6 +397,8 @@ handle_zblock: */ static int ntfs_readpage(struct file *file, struct page *page) { + loff_t i_size; + struct inode *vi; ntfs_inode *ni, *base_ni; u8 *kaddr; ntfs_attr_search_ctx *ctx; @@ -384,14 +417,17 @@ retry_readpage: unlock_page(page); return 0; } - ni = NTFS_I(page->mapping->host); + vi = page->mapping->host; + ni = NTFS_I(vi); /* * Only $DATA attributes can be encrypted and only unnamed $DATA * attributes can be compressed. Index root can have the flags set but * this means to create compressed/encrypted files, not that the - * attribute is compressed/encrypted. + * attribute is compressed/encrypted. Note we need to check for + * AT_INDEX_ALLOCATION since this is the type of both directory and + * index inodes. */ - if (ni->type != AT_INDEX_ROOT) { + if (ni->type != AT_INDEX_ALLOCATION) { /* If attribute is encrypted, deny access, just like NT4. */ if (NInoEncrypted(ni)) { BUG_ON(ni->type != AT_DATA); @@ -456,7 +492,12 @@ retry_readpage: read_lock_irqsave(&ni->size_lock, flags); if (unlikely(attr_len > ni->initialized_size)) attr_len = ni->initialized_size; + i_size = i_size_read(vi); read_unlock_irqrestore(&ni->size_lock, flags); + if (unlikely(attr_len > i_size)) { + /* Race with shrinking truncate. */ + attr_len = i_size; + } kaddr = kmap_atomic(page, KM_USER0); /* Copy the data to the page. */ memcpy(kaddr, (u8*)ctx->attr + @@ -1341,9 +1382,11 @@ retry_writepage: * Only $DATA attributes can be encrypted and only unnamed $DATA * attributes can be compressed. Index root can have the flags set but * this means to create compressed/encrypted files, not that the - * attribute is compressed/encrypted. + * attribute is compressed/encrypted. Note we need to check for + * AT_INDEX_ALLOCATION since this is the type of both directory and + * index inodes. */ - if (ni->type != AT_INDEX_ROOT) { + if (ni->type != AT_INDEX_ALLOCATION) { /* If file is encrypted, deny access, just like NT4. */ if (NInoEncrypted(ni)) { unlock_page(page); @@ -1379,8 +1422,8 @@ retry_writepage: unsigned int ofs = i_size & ~PAGE_CACHE_MASK; kaddr = kmap_atomic(page, KM_USER0); memset(kaddr + ofs, 0, PAGE_CACHE_SIZE - ofs); - flush_dcache_page(page); kunmap_atomic(kaddr, KM_USER0); + flush_dcache_page(page); } /* Handle mst protected attributes. */ if (NInoMstProtected(ni)) @@ -1443,34 +1486,33 @@ retry_writepage: BUG_ON(PageWriteback(page)); set_page_writeback(page); unlock_page(page); - /* - * Here, we do not need to zero the out of bounds area everytime - * because the below memcpy() already takes care of the - * mmap-at-end-of-file requirements. If the file is converted to a - * non-resident one, then the code path use is switched to the - * non-resident one where the zeroing happens on each ntfs_writepage() - * invocation. - */ attr_len = le32_to_cpu(ctx->attr->data.resident.value_length); i_size = i_size_read(vi); if (unlikely(attr_len > i_size)) { + /* Race with shrinking truncate or a failed truncate. */ attr_len = i_size; - ctx->attr->data.resident.value_length = cpu_to_le32(attr_len); + /* + * If the truncate failed, fix it up now. If a concurrent + * truncate, we do its job, so it does not have to do anything. + */ + err = ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr, + attr_len); + /* Shrinking cannot fail. */ + BUG_ON(err); } kaddr = kmap_atomic(page, KM_USER0); /* Copy the data from the page to the mft record. */ memcpy((u8*)ctx->attr + le16_to_cpu(ctx->attr->data.resident.value_offset), kaddr, attr_len); - flush_dcache_mft_record_page(ctx->ntfs_ino); /* Zero out of bounds area in the page cache page. */ memset(kaddr + attr_len, 0, PAGE_CACHE_SIZE - attr_len); - flush_dcache_page(page); kunmap_atomic(kaddr, KM_USER0); - + flush_dcache_mft_record_page(ctx->ntfs_ino); + flush_dcache_page(page); + /* We are done with the page. */ end_page_writeback(page); - - /* Mark the mft record dirty, so it gets written back. */ + /* Finally, mark the mft record dirty, so it gets written back. */ mark_mft_record_dirty(ctx->ntfs_ino); ntfs_attr_put_search_ctx(ctx); unmap_mft_record(base_ni); diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index dc4bbe3acf5c..7ec045131808 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c @@ -1166,6 +1166,8 @@ err_out: * * Return 0 on success and -errno on error. In the error case, the inode will * have had make_bad_inode() executed on it. + * + * Note this cannot be called for AT_INDEX_ALLOCATION. */ static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi) { @@ -1242,8 +1244,8 @@ static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi) } } /* - * The encryption flag set in an index root just means to - * compress all files. + * The compressed/sparse flag set in an index root just means + * to compress all files. */ if (NInoMstProtected(ni) && ni->type != AT_INDEX_ROOT) { ntfs_error(vi->i_sb, "Found mst protected attribute " @@ -1319,8 +1321,7 @@ static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi) "the mapping pairs array."); goto unm_err_out; } - if ((NInoCompressed(ni) || NInoSparse(ni)) && - ni->type != AT_INDEX_ROOT) { + if (NInoCompressed(ni) || NInoSparse(ni)) { if (a->data.non_resident.compression_unit != 4) { ntfs_error(vi->i_sb, "Found nonstandard " "compression unit (%u instead " diff --git a/fs/ntfs/malloc.h b/fs/ntfs/malloc.h index 3288bcc2c4aa..006946efca8c 100644 --- a/fs/ntfs/malloc.h +++ b/fs/ntfs/malloc.h @@ -1,7 +1,7 @@ /* * malloc.h - NTFS kernel memory handling. Part of the Linux-NTFS project. * - * Copyright (c) 2001-2004 Anton Altaparmakov + * Copyright (c) 2001-2005 Anton Altaparmakov * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published diff --git a/fs/ntfs/runlist.c b/fs/ntfs/runlist.c index f5b2ac929081..061b5ff6b73c 100644 --- a/fs/ntfs/runlist.c +++ b/fs/ntfs/runlist.c @@ -2,7 +2,7 @@ * runlist.c - NTFS runlist handling code. Part of the Linux-NTFS project. * * Copyright (c) 2001-2005 Anton Altaparmakov - * Copyright (c) 2002 Richard Russon + * Copyright (c) 2002-2005 Richard Russon * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -158,17 +158,21 @@ static inline BOOL ntfs_are_rl_mergeable(runlist_element *dst, BUG_ON(!dst); BUG_ON(!src); - if ((dst->lcn < 0) || (src->lcn < 0)) { /* Are we merging holes? */ - if (dst->lcn == LCN_HOLE && src->lcn == LCN_HOLE) - return TRUE; + /* We can merge unmapped regions even if they are misaligned. */ + if ((dst->lcn == LCN_RL_NOT_MAPPED) && (src->lcn == LCN_RL_NOT_MAPPED)) + return TRUE; + /* If the runs are misaligned, we cannot merge them. */ + if ((dst->vcn + dst->length) != src->vcn) return FALSE; - } - if ((dst->lcn + dst->length) != src->lcn) /* Are the runs contiguous? */ - return FALSE; - if ((dst->vcn + dst->length) != src->vcn) /* Are the runs misaligned? */ - return FALSE; - - return TRUE; + /* If both runs are non-sparse and contiguous, we can merge them. */ + if ((dst->lcn >= 0) && (src->lcn >= 0) && + ((dst->lcn + dst->length) == src->lcn)) + return TRUE; + /* If we are merging two holes, we can merge them. */ + if ((dst->lcn == LCN_HOLE) && (src->lcn == LCN_HOLE)) + return TRUE; + /* Cannot merge. */ + return FALSE; } /** @@ -214,14 +218,15 @@ static inline void __ntfs_rl_merge(runlist_element *dst, runlist_element *src) static inline runlist_element *ntfs_rl_append(runlist_element *dst, int dsize, runlist_element *src, int ssize, int loc) { - BOOL right; - int magic; + BOOL right = FALSE; /* Right end of @src needs merging. */ + int marker; /* End of the inserted runs. */ BUG_ON(!dst); BUG_ON(!src); /* First, check if the right hand end needs merging. */ - right = ntfs_are_rl_mergeable(src + ssize - 1, dst + loc + 1); + if ((loc + 1) < dsize) + right = ntfs_are_rl_mergeable(src + ssize - 1, dst + loc + 1); /* Space required: @dst size + @src size, less one if we merged. */ dst = ntfs_rl_realloc(dst, dsize, dsize + ssize - right); @@ -236,18 +241,19 @@ static inline runlist_element *ntfs_rl_append(runlist_element *dst, if (right) __ntfs_rl_merge(src + ssize - 1, dst + loc + 1); - magic = loc + ssize; + /* First run after the @src runs that have been inserted. */ + marker = loc + ssize + 1; /* Move the tail of @dst out of the way, then copy in @src. */ - ntfs_rl_mm(dst, magic + 1, loc + 1 + right, dsize - loc - 1 - right); + ntfs_rl_mm(dst, marker, loc + 1 + right, dsize - (loc + 1 + right)); ntfs_rl_mc(dst, loc + 1, src, 0, ssize); /* Adjust the size of the preceding hole. */ dst[loc].length = dst[loc + 1].vcn - dst[loc].vcn; /* We may have changed the length of the file, so fix the end marker */ - if (dst[magic + 1].lcn == LCN_ENOENT) - dst[magic + 1].vcn = dst[magic].vcn + dst[magic].length; + if (dst[marker].lcn == LCN_ENOENT) + dst[marker].vcn = dst[marker - 1].vcn + dst[marker - 1].length; return dst; } @@ -279,18 +285,17 @@ static inline runlist_element *ntfs_rl_append(runlist_element *dst, static inline runlist_element *ntfs_rl_insert(runlist_element *dst, int dsize, runlist_element *src, int ssize, int loc) { - BOOL left = FALSE; - BOOL disc = FALSE; /* Discontinuity */ - BOOL hole = FALSE; /* Following a hole */ - int magic; + BOOL left = FALSE; /* Left end of @src needs merging. */ + BOOL disc = FALSE; /* Discontinuity between @dst and @src. */ + int marker; /* End of the inserted runs. */ BUG_ON(!dst); BUG_ON(!src); - /* disc => Discontinuity between the end of @dst and the start of @src. - * This means we might need to insert a hole. - * hole => @dst ends with a hole or an unmapped region which we can - * extend to match the discontinuity. */ + /* + * disc => Discontinuity between the end of @dst and the start of @src. + * This means we might need to insert a "not mapped" run. + */ if (loc == 0) disc = (src[0].vcn > 0); else { @@ -303,58 +308,49 @@ static inline runlist_element *ntfs_rl_insert(runlist_element *dst, merged_length += src->length; disc = (src[0].vcn > dst[loc - 1].vcn + merged_length); - if (disc) - hole = (dst[loc - 1].lcn == LCN_HOLE); } - - /* Space required: @dst size + @src size, less one if we merged, plus - * one if there was a discontinuity, less one for a trailing hole. */ - dst = ntfs_rl_realloc(dst, dsize, dsize + ssize - left + disc - hole); + /* + * Space required: @dst size + @src size, less one if we merged, plus + * one if there was a discontinuity. + */ + dst = ntfs_rl_realloc(dst, dsize, dsize + ssize - left + disc); if (IS_ERR(dst)) return dst; /* * We are guaranteed to succeed from here so can start modifying the * original runlist. */ - if (left) __ntfs_rl_merge(dst + loc - 1, src); - - magic = loc + ssize - left + disc - hole; + /* + * First run after the @src runs that have been inserted. + * Nominally, @marker equals @loc + @ssize, i.e. location + number of + * runs in @src. However, if @left, then the first run in @src has + * been merged with one in @dst. And if @disc, then @dst and @src do + * not meet and we need an extra run to fill the gap. + */ + marker = loc + ssize - left + disc; /* Move the tail of @dst out of the way, then copy in @src. */ - ntfs_rl_mm(dst, magic, loc, dsize - loc); - ntfs_rl_mc(dst, loc + disc - hole, src, left, ssize - left); + ntfs_rl_mm(dst, marker, loc, dsize - loc); + ntfs_rl_mc(dst, loc + disc, src, left, ssize - left); - /* Adjust the VCN of the last run ... */ - if (dst[magic].lcn <= LCN_HOLE) - dst[magic].vcn = dst[magic - 1].vcn + dst[magic - 1].length; + /* Adjust the VCN of the first run after the insertion... */ + dst[marker].vcn = dst[marker - 1].vcn + dst[marker - 1].length; /* ... and the length. */ - if (dst[magic].lcn == LCN_HOLE || dst[magic].lcn == LCN_RL_NOT_MAPPED) - dst[magic].length = dst[magic + 1].vcn - dst[magic].vcn; + if (dst[marker].lcn == LCN_HOLE || dst[marker].lcn == LCN_RL_NOT_MAPPED) + dst[marker].length = dst[marker + 1].vcn - dst[marker].vcn; - /* Writing beyond the end of the file and there's a discontinuity. */ + /* Writing beyond the end of the file and there is a discontinuity. */ if (disc) { - if (hole) - dst[loc - 1].length = dst[loc].vcn - dst[loc - 1].vcn; - else { - if (loc > 0) { - dst[loc].vcn = dst[loc - 1].vcn + - dst[loc - 1].length; - dst[loc].length = dst[loc + 1].vcn - - dst[loc].vcn; - } else { - dst[loc].vcn = 0; - dst[loc].length = dst[loc + 1].vcn; - } - dst[loc].lcn = LCN_RL_NOT_MAPPED; + if (loc > 0) { + dst[loc].vcn = dst[loc - 1].vcn + dst[loc - 1].length; + dst[loc].length = dst[loc + 1].vcn - dst[loc].vcn; + } else { + dst[loc].vcn = 0; + dst[loc].length = dst[loc + 1].vcn; } - - magic += hole; - - if (dst[magic].lcn == LCN_ENOENT) - dst[magic].vcn = dst[magic - 1].vcn + - dst[magic - 1].length; + dst[loc].lcn = LCN_RL_NOT_MAPPED; } return dst; } @@ -385,20 +381,23 @@ static inline runlist_element *ntfs_rl_insert(runlist_element *dst, static inline runlist_element *ntfs_rl_replace(runlist_element *dst, int dsize, runlist_element *src, int ssize, int loc) { - BOOL left = FALSE; - BOOL right; - int magic; + BOOL left = FALSE; /* Left end of @src needs merging. */ + BOOL right = FALSE; /* Right end of @src needs merging. */ + int tail; /* Start of tail of @dst. */ + int marker; /* End of the inserted runs. */ BUG_ON(!dst); BUG_ON(!src); - /* First, merge the left and right ends, if necessary. */ - right = ntfs_are_rl_mergeable(src + ssize - 1, dst + loc + 1); + /* First, see if the left and right ends need merging. */ + if ((loc + 1) < dsize) + right = ntfs_are_rl_mergeable(src + ssize - 1, dst + loc + 1); if (loc > 0) left = ntfs_are_rl_mergeable(dst + loc - 1, src); - - /* Allocate some space. We'll need less if the left, right, or both - * ends were merged. */ + /* + * Allocate some space. We will need less if the left, right, or both + * ends get merged. + */ dst = ntfs_rl_realloc(dst, dsize, dsize + ssize - left - right); if (IS_ERR(dst)) return dst; @@ -406,21 +405,37 @@ static inline runlist_element *ntfs_rl_replace(runlist_element *dst, * We are guaranteed to succeed from here so can start modifying the * original runlists. */ + + /* First, merge the left and right ends, if necessary. */ if (right) __ntfs_rl_merge(src + ssize - 1, dst + loc + 1); if (left) __ntfs_rl_merge(dst + loc - 1, src); - - /* FIXME: What does this mean? (AIA) */ - magic = loc + ssize - left; + /* + * Offset of the tail of @dst. This needs to be moved out of the way + * to make space for the runs to be copied from @src, i.e. the first + * run of the tail of @dst. + * Nominally, @tail equals @loc + 1, i.e. location, skipping the + * replaced run. However, if @right, then one of @dst's runs is + * already merged into @src. + */ + tail = loc + right + 1; + /* + * First run after the @src runs that have been inserted, i.e. where + * the tail of @dst needs to be moved to. + * Nominally, @marker equals @loc + @ssize, i.e. location + number of + * runs in @src. However, if @left, then the first run in @src has + * been merged with one in @dst. + */ + marker = loc + ssize - left; /* Move the tail of @dst out of the way, then copy in @src. */ - ntfs_rl_mm(dst, magic, loc + right + 1, dsize - loc - right - 1); + ntfs_rl_mm(dst, marker, tail, dsize - tail); ntfs_rl_mc(dst, loc, src, left, ssize - left); - /* We may have changed the length of the file, so fix the end marker */ - if (dst[magic].lcn == LCN_ENOENT) - dst[magic].vcn = dst[magic - 1].vcn + dst[magic - 1].length; + /* We may have changed the length of the file, so fix the end marker. */ + if (dsize - tail > 0 && dst[marker].lcn == LCN_ENOENT) + dst[marker].vcn = dst[marker - 1].vcn + dst[marker - 1].length; return dst; } diff --git a/fs/proc/base.c b/fs/proc/base.c index 23db452ab428..3b33f94020db 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -340,6 +340,54 @@ static int proc_root_link(struct inode *inode, struct dentry **dentry, struct vf return result; } + +/* Same as proc_root_link, but this addionally tries to get fs from other + * threads in the group */ +static int proc_task_root_link(struct inode *inode, struct dentry **dentry, + struct vfsmount **mnt) +{ + struct fs_struct *fs; + int result = -ENOENT; + struct task_struct *leader = proc_task(inode); + + task_lock(leader); + fs = leader->fs; + if (fs) { + atomic_inc(&fs->count); + task_unlock(leader); + } else { + /* Try to get fs from other threads */ + task_unlock(leader); + read_lock(&tasklist_lock); + if (pid_alive(leader)) { + struct task_struct *task = leader; + + while ((task = next_thread(task)) != leader) { + task_lock(task); + fs = task->fs; + if (fs) { + atomic_inc(&fs->count); + task_unlock(task); + break; + } + task_unlock(task); + } + } + read_unlock(&tasklist_lock); + } + + if (fs) { + read_lock(&fs->lock); + *mnt = mntget(fs->rootmnt); + *dentry = dget(fs->root); + read_unlock(&fs->lock); + result = 0; + put_fs_struct(fs); + } + return result; +} + + #define MAY_PTRACE(task) \ (task == current || \ (task->parent == current && \ @@ -471,14 +519,14 @@ static int proc_oom_score(struct task_struct *task, char *buffer) /* permission checks */ -static int proc_check_root(struct inode *inode) +/* If the process being read is separated by chroot from the reading process, + * don't let the reader access the threads. + */ +static int proc_check_chroot(struct dentry *root, struct vfsmount *vfsmnt) { - struct dentry *de, *base, *root; - struct vfsmount *our_vfsmnt, *vfsmnt, *mnt; + struct dentry *de, *base; + struct vfsmount *our_vfsmnt, *mnt; int res = 0; - - if (proc_root_link(inode, &root, &vfsmnt)) /* Ewww... */ - return -ENOENT; read_lock(¤t->fs->lock); our_vfsmnt = mntget(current->fs->rootmnt); base = dget(current->fs->root); @@ -511,6 +559,16 @@ out: goto exit; } +static int proc_check_root(struct inode *inode) +{ + struct dentry *root; + struct vfsmount *vfsmnt; + + if (proc_root_link(inode, &root, &vfsmnt)) /* Ewww... */ + return -ENOENT; + return proc_check_chroot(root, vfsmnt); +} + static int proc_permission(struct inode *inode, int mask, struct nameidata *nd) { if (generic_permission(inode, mask, NULL) != 0) @@ -518,6 +576,20 @@ static int proc_permission(struct inode *inode, int mask, struct nameidata *nd) return proc_check_root(inode); } +static int proc_task_permission(struct inode *inode, int mask, struct nameidata *nd) +{ + struct dentry *root; + struct vfsmount *vfsmnt; + + if (generic_permission(inode, mask, NULL) != 0) + return -EACCES; + + if (proc_task_root_link(inode, &root, &vfsmnt)) + return -ENOENT; + + return proc_check_chroot(root, vfsmnt); +} + extern struct seq_operations proc_pid_maps_op; static int maps_open(struct inode *inode, struct file *file) { @@ -1419,7 +1491,7 @@ static struct inode_operations proc_fd_inode_operations = { static struct inode_operations proc_task_inode_operations = { .lookup = proc_task_lookup, - .permission = proc_permission, + .permission = proc_task_permission, }; #ifdef CONFIG_SECURITY diff --git a/include/asm-alpha/compiler.h b/include/asm-alpha/compiler.h index 399c33b7be51..0a4a8b40dfcd 100644 --- a/include/asm-alpha/compiler.h +++ b/include/asm-alpha/compiler.h @@ -98,6 +98,9 @@ #undef inline #undef __inline__ #undef __inline - +#if __GNUC__ == 3 && __GNUC_MINOR__ >= 1 || __GNUC__ > 3 +#undef __always_inline +#define __always_inline inline __attribute__((always_inline)) +#endif #endif /* __ALPHA_COMPILER_H */ diff --git a/include/asm-alpha/futex.h b/include/asm-alpha/futex.h index 2cac5ecd9d00..9feff4ce1424 100644 --- a/include/asm-alpha/futex.h +++ b/include/asm-alpha/futex.h @@ -14,7 +14,7 @@ futex_atomic_op_inuser (int encoded_op, int __user *uaddr) int cmp = (encoded_op >> 24) & 15; int oparg = (encoded_op << 8) >> 20; int cmparg = (encoded_op << 20) >> 20; - int oldval = 0, ret, tem; + int oldval = 0, ret; if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) oparg = 1 << oparg; diff --git a/include/asm-arm/futex.h b/include/asm-arm/futex.h index 2cac5ecd9d00..9feff4ce1424 100644 --- a/include/asm-arm/futex.h +++ b/include/asm-arm/futex.h @@ -14,7 +14,7 @@ futex_atomic_op_inuser (int encoded_op, int __user *uaddr) int cmp = (encoded_op >> 24) & 15; int oparg = (encoded_op << 8) >> 20; int cmparg = (encoded_op << 20) >> 20; - int oldval = 0, ret, tem; + int oldval = 0, ret; if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) oparg = 1 << oparg; diff --git a/include/asm-arm/io.h b/include/asm-arm/io.h index cfa71a0dffb6..5c4ae8f5dbb0 100644 --- a/include/asm-arm/io.h +++ b/include/asm-arm/io.h @@ -136,9 +136,9 @@ extern void __readwrite_bug(const char *fn); /* * String version of IO memory access ops: */ -extern void _memcpy_fromio(void *, void __iomem *, size_t); -extern void _memcpy_toio(void __iomem *, const void *, size_t); -extern void _memset_io(void __iomem *, int, size_t); +extern void _memcpy_fromio(void *, const volatile void __iomem *, size_t); +extern void _memcpy_toio(volatile void __iomem *, const void *, size_t); +extern void _memset_io(volatile void __iomem *, int, size_t); #define mmiowb() diff --git a/include/asm-arm/mach/arch.h b/include/asm-arm/mach/arch.h index 56c6bf4ab0c3..4fa95084a8c0 100644 --- a/include/asm-arm/mach/arch.h +++ b/include/asm-arm/mach/arch.h @@ -50,7 +50,7 @@ struct machine_desc { */ #define MACHINE_START(_type,_name) \ const struct machine_desc __mach_desc_##_type \ - __attribute__((__section__(".arch.info"))) = { \ + __attribute__((__section__(".arch.info.init"))) = { \ .nr = MACH_TYPE_##_type, \ .name = _name, diff --git a/include/asm-arm/setup.h b/include/asm-arm/setup.h index adcbd79762bf..ea3ed2465233 100644 --- a/include/asm-arm/setup.h +++ b/include/asm-arm/setup.h @@ -171,7 +171,7 @@ struct tagtable { int (*parse)(const struct tag *); }; -#define __tag __attribute_used__ __attribute__((__section__(".taglist"))) +#define __tag __attribute_used__ __attribute__((__section__(".taglist.init"))) #define __tagtable(tag, fn) \ static struct tagtable __tagtable_##fn __tag = { tag, fn } @@ -213,6 +213,6 @@ struct early_params { #define __early_param(name,fn) \ static struct early_params __early_##fn __attribute_used__ \ -__attribute__((__section__("__early_param"))) = { name, fn } +__attribute__((__section__(".early_param.init"))) = { name, fn } #endif diff --git a/include/asm-arm26/futex.h b/include/asm-arm26/futex.h index 2cac5ecd9d00..9feff4ce1424 100644 --- a/include/asm-arm26/futex.h +++ b/include/asm-arm26/futex.h @@ -14,7 +14,7 @@ futex_atomic_op_inuser (int encoded_op, int __user *uaddr) int cmp = (encoded_op >> 24) & 15; int oparg = (encoded_op << 8) >> 20; int cmparg = (encoded_op << 20) >> 20; - int oldval = 0, ret, tem; + int oldval = 0, ret; if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) oparg = 1 << oparg; diff --git a/include/asm-cris/futex.h b/include/asm-cris/futex.h index 2cac5ecd9d00..9feff4ce1424 100644 --- a/include/asm-cris/futex.h +++ b/include/asm-cris/futex.h @@ -14,7 +14,7 @@ futex_atomic_op_inuser (int encoded_op, int __user *uaddr) int cmp = (encoded_op >> 24) & 15; int oparg = (encoded_op << 8) >> 20; int cmparg = (encoded_op << 20) >> 20; - int oldval = 0, ret, tem; + int oldval = 0, ret; if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) oparg = 1 << oparg; diff --git a/include/asm-frv/futex.h b/include/asm-frv/futex.h index 2cac5ecd9d00..9feff4ce1424 100644 --- a/include/asm-frv/futex.h +++ b/include/asm-frv/futex.h @@ -14,7 +14,7 @@ futex_atomic_op_inuser (int encoded_op, int __user *uaddr) int cmp = (encoded_op >> 24) & 15; int oparg = (encoded_op << 8) >> 20; int cmparg = (encoded_op << 20) >> 20; - int oldval = 0, ret, tem; + int oldval = 0, ret; if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) oparg = 1 << oparg; diff --git a/include/asm-h8300/futex.h b/include/asm-h8300/futex.h index 2cac5ecd9d00..9feff4ce1424 100644 --- a/include/asm-h8300/futex.h +++ b/include/asm-h8300/futex.h @@ -14,7 +14,7 @@ futex_atomic_op_inuser (int encoded_op, int __user *uaddr) int cmp = (encoded_op >> 24) & 15; int oparg = (encoded_op << 8) >> 20; int cmparg = (encoded_op << 20) >> 20; - int oldval = 0, ret, tem; + int oldval = 0, ret; if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) oparg = 1 << oparg; diff --git a/include/asm-i386/futex.h b/include/asm-i386/futex.h index 44b9db806474..e7a271d39309 100644 --- a/include/asm-i386/futex.h +++ b/include/asm-i386/futex.h @@ -61,7 +61,7 @@ futex_atomic_op_inuser (int encoded_op, int __user *uaddr) if (op == FUTEX_OP_SET) __futex_atomic_op1("xchgl %0, %2", ret, oldval, uaddr, oparg); else { -#ifndef CONFIG_X86_BSWAP +#if !defined(CONFIG_X86_BSWAP) && !defined(CONFIG_UML) if (boot_cpu_data.x86 == 3) ret = -ENOSYS; else diff --git a/include/asm-ia64/futex.h b/include/asm-ia64/futex.h index 2cac5ecd9d00..9feff4ce1424 100644 --- a/include/asm-ia64/futex.h +++ b/include/asm-ia64/futex.h @@ -14,7 +14,7 @@ futex_atomic_op_inuser (int encoded_op, int __user *uaddr) int cmp = (encoded_op >> 24) & 15; int oparg = (encoded_op << 8) >> 20; int cmparg = (encoded_op << 20) >> 20; - int oldval = 0, ret, tem; + int oldval = 0, ret; if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) oparg = 1 << oparg; diff --git a/include/asm-ia64/mca.h b/include/asm-ia64/mca.h index 97a28b8b2ddd..c7d9c9ed38ba 100644 --- a/include/asm-ia64/mca.h +++ b/include/asm-ia64/mca.h @@ -80,7 +80,12 @@ struct ia64_sal_os_state { u64 sal_ra; /* Return address in SAL, physical */ u64 sal_gp; /* GP of the SAL - physical */ pal_min_state_area_t *pal_min_state; /* from R17. physical in asm, virtual in C */ + /* Previous values of IA64_KR(CURRENT) and IA64_KR(CURRENT_STACK). + * Note: if the MCA/INIT recovery code wants to resume to a new context + * then it must change these values to reflect the new kernel stack. + */ u64 prev_IA64_KR_CURRENT; /* previous value of IA64_KR(CURRENT) */ + u64 prev_IA64_KR_CURRENT_STACK; struct task_struct *prev_task; /* previous task, NULL if it is not useful */ /* Some interrupt registers are not saved in minstate, pt_regs or * switch_stack. Because MCA/INIT can occur when interrupts are diff --git a/include/asm-m32r/futex.h b/include/asm-m32r/futex.h index 2cac5ecd9d00..9feff4ce1424 100644 --- a/include/asm-m32r/futex.h +++ b/include/asm-m32r/futex.h @@ -14,7 +14,7 @@ futex_atomic_op_inuser (int encoded_op, int __user *uaddr) int cmp = (encoded_op >> 24) & 15; int oparg = (encoded_op << 8) >> 20; int cmparg = (encoded_op << 20) >> 20; - int oldval = 0, ret, tem; + int oldval = 0, ret; if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) oparg = 1 << oparg; diff --git a/include/asm-m68k/futex.h b/include/asm-m68k/futex.h index 2cac5ecd9d00..9feff4ce1424 100644 --- a/include/asm-m68k/futex.h +++ b/include/asm-m68k/futex.h @@ -14,7 +14,7 @@ futex_atomic_op_inuser (int encoded_op, int __user *uaddr) int cmp = (encoded_op >> 24) & 15; int oparg = (encoded_op << 8) >> 20; int cmparg = (encoded_op << 20) >> 20; - int oldval = 0, ret, tem; + int oldval = 0, ret; if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) oparg = 1 << oparg; diff --git a/include/asm-m68knommu/futex.h b/include/asm-m68knommu/futex.h index 2cac5ecd9d00..9feff4ce1424 100644 --- a/include/asm-m68knommu/futex.h +++ b/include/asm-m68knommu/futex.h @@ -14,7 +14,7 @@ futex_atomic_op_inuser (int encoded_op, int __user *uaddr) int cmp = (encoded_op >> 24) & 15; int oparg = (encoded_op << 8) >> 20; int cmparg = (encoded_op << 20) >> 20; - int oldval = 0, ret, tem; + int oldval = 0, ret; if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) oparg = 1 << oparg; diff --git a/include/asm-parisc/futex.h b/include/asm-parisc/futex.h index 2cac5ecd9d00..9feff4ce1424 100644 --- a/include/asm-parisc/futex.h +++ b/include/asm-parisc/futex.h @@ -14,7 +14,7 @@ futex_atomic_op_inuser (int encoded_op, int __user *uaddr) int cmp = (encoded_op >> 24) & 15; int oparg = (encoded_op << 8) >> 20; int cmparg = (encoded_op << 20) >> 20; - int oldval = 0, ret, tem; + int oldval = 0, ret; if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) oparg = 1 << oparg; diff --git a/include/asm-ppc/futex.h b/include/asm-ppc/futex.h index 2cac5ecd9d00..9feff4ce1424 100644 --- a/include/asm-ppc/futex.h +++ b/include/asm-ppc/futex.h @@ -14,7 +14,7 @@ futex_atomic_op_inuser (int encoded_op, int __user *uaddr) int cmp = (encoded_op >> 24) & 15; int oparg = (encoded_op << 8) >> 20; int cmparg = (encoded_op << 20) >> 20; - int oldval = 0, ret, tem; + int oldval = 0, ret; if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) oparg = 1 << oparg; diff --git a/include/asm-ppc/macio.h b/include/asm-ppc/macio.h index a481b772d154..b553dd4b139e 100644 --- a/include/asm-ppc/macio.h +++ b/include/asm-ppc/macio.h @@ -1,7 +1,6 @@ #ifndef __MACIO_ASIC_H__ #define __MACIO_ASIC_H__ -#include <linux/mod_devicetable.h> #include <asm/of_device.h> extern struct bus_type macio_bus_type; diff --git a/include/asm-ppc/of_device.h b/include/asm-ppc/of_device.h index 4b264cfd3998..575bce418f80 100644 --- a/include/asm-ppc/of_device.h +++ b/include/asm-ppc/of_device.h @@ -2,6 +2,7 @@ #define __OF_DEVICE_H__ #include <linux/device.h> +#include <linux/mod_devicetable.h> #include <asm/prom.h> /* @@ -55,7 +56,9 @@ extern int of_register_driver(struct of_platform_driver *drv); extern void of_unregister_driver(struct of_platform_driver *drv); extern int of_device_register(struct of_device *ofdev); extern void of_device_unregister(struct of_device *ofdev); -extern struct of_device *of_platform_device_create(struct device_node *np, const char *bus_id); +extern struct of_device *of_platform_device_create(struct device_node *np, + const char *bus_id, + struct device *parent); extern void of_release_dev(struct device *dev); #endif /* __OF_DEVICE_H__ */ diff --git a/include/asm-ppc64/smu.h b/include/asm-ppc64/smu.h index 10b4397af9aa..dee8eefe47bc 100644 --- a/include/asm-ppc64/smu.h +++ b/include/asm-ppc64/smu.h @@ -1,22 +1,379 @@ +#ifndef _SMU_H +#define _SMU_H + /* * Definitions for talking to the SMU chip in newer G5 PowerMacs */ #include <linux/config.h> +#include <linux/list.h> + +/* + * Known SMU commands + * + * Most of what is below comes from looking at the Open Firmware driver, + * though this is still incomplete and could use better documentation here + * or there... + */ + + +/* + * Partition info commands + * + * I do not know what those are for at this point + */ +#define SMU_CMD_PARTITION_COMMAND 0x3e + + +/* + * Fan control + * + * This is a "mux" for fan control commands, first byte is the + * "sub" command. + */ +#define SMU_CMD_FAN_COMMAND 0x4a + + +/* + * Battery access + * + * Same command number as the PMU, could it be same syntax ? + */ +#define SMU_CMD_BATTERY_COMMAND 0x6f +#define SMU_CMD_GET_BATTERY_INFO 0x00 + +/* + * Real time clock control + * + * This is a "mux", first data byte contains the "sub" command. + * The "RTC" part of the SMU controls the date, time, powerup + * timer, but also a PRAM + * + * Dates are in BCD format on 7 bytes: + * [sec] [min] [hour] [weekday] [month day] [month] [year] + * with month being 1 based and year minus 100 + */ +#define SMU_CMD_RTC_COMMAND 0x8e +#define SMU_CMD_RTC_SET_PWRUP_TIMER 0x00 /* i: 7 bytes date */ +#define SMU_CMD_RTC_GET_PWRUP_TIMER 0x01 /* o: 7 bytes date */ +#define SMU_CMD_RTC_STOP_PWRUP_TIMER 0x02 +#define SMU_CMD_RTC_SET_PRAM_BYTE_ACC 0x20 /* i: 1 byte (address?) */ +#define SMU_CMD_RTC_SET_PRAM_AUTOINC 0x21 /* i: 1 byte (data?) */ +#define SMU_CMD_RTC_SET_PRAM_LO_BYTES 0x22 /* i: 10 bytes */ +#define SMU_CMD_RTC_SET_PRAM_HI_BYTES 0x23 /* i: 10 bytes */ +#define SMU_CMD_RTC_GET_PRAM_BYTE 0x28 /* i: 1 bytes (address?) */ +#define SMU_CMD_RTC_GET_PRAM_LO_BYTES 0x29 /* o: 10 bytes */ +#define SMU_CMD_RTC_GET_PRAM_HI_BYTES 0x2a /* o: 10 bytes */ +#define SMU_CMD_RTC_SET_DATETIME 0x80 /* i: 7 bytes date */ +#define SMU_CMD_RTC_GET_DATETIME 0x81 /* o: 7 bytes date */ + + /* + * i2c commands + * + * To issue an i2c command, first is to send a parameter block to the + * the SMU. This is a command of type 0x9a with 9 bytes of header + * eventually followed by data for a write: + * + * 0: bus number (from device-tree usually, SMU has lots of busses !) + * 1: transfer type/format (see below) + * 2: device address. For combined and combined4 type transfers, this + * is the "write" version of the address (bit 0x01 cleared) + * 3: subaddress length (0..3) + * 4: subaddress byte 0 (or only byte for subaddress length 1) + * 5: subaddress byte 1 + * 6: subaddress byte 2 + * 7: combined address (device address for combined mode data phase) + * 8: data length + * + * The transfer types are the same good old Apple ones it seems, + * that is: + * - 0x00: Simple transfer + * - 0x01: Subaddress transfer (addr write + data tx, no restart) + * - 0x02: Combined transfer (addr write + restart + data tx) + * + * This is then followed by actual data for a write. + * + * At this point, the OF driver seems to have a limitation on transfer + * sizes of 0xd bytes on reads and 0x5 bytes on writes. I do not know + * wether this is just an OF limit due to some temporary buffer size + * or if this is an SMU imposed limit. This driver has the same limitation + * for now as I use a 0x10 bytes temporary buffer as well + * + * Once that is completed, a response is expected from the SMU. This is + * obtained via a command of type 0x9a with a length of 1 byte containing + * 0 as the data byte. OF also fills the rest of the data buffer with 0xff's + * though I can't tell yet if this is actually necessary. Once this command + * is complete, at this point, all I can tell is what OF does. OF tests + * byte 0 of the reply: + * - on read, 0xfe or 0xfc : bus is busy, wait (see below) or nak ? + * - on read, 0x00 or 0x01 : reply is in buffer (after the byte 0) + * - on write, < 0 -> failure (immediate exit) + * - else, OF just exists (without error, weird) + * + * So on read, there is this wait-for-busy thing when getting a 0xfc or + * 0xfe result. OF does a loop of up to 64 retries, waiting 20ms and + * doing the above again until either the retries expire or the result + * is no longer 0xfe or 0xfc + * + * The Darwin I2C driver is less subtle though. On any non-success status + * from the response command, it waits 5ms and tries again up to 20 times, + * it doesn't differenciate between fatal errors or "busy" status. + * + * This driver provides an asynchronous paramblock based i2c command + * interface to be used either directly by low level code or by a higher + * level driver interfacing to the linux i2c layer. The current + * implementation of this relies on working timers & timer interrupts + * though, so be careful of calling context for now. This may be "fixed" + * in the future by adding a polling facility. + */ +#define SMU_CMD_I2C_COMMAND 0x9a + /* transfer types */ +#define SMU_I2C_TRANSFER_SIMPLE 0x00 +#define SMU_I2C_TRANSFER_STDSUB 0x01 +#define SMU_I2C_TRANSFER_COMBINED 0x02 + +/* + * Power supply control + * + * The "sub" command is an ASCII string in the data, the + * data lenght is that of the string. + * + * The VSLEW command can be used to get or set the voltage slewing. + * - lenght 5 (only "VSLEW") : it returns "DONE" and 3 bytes of + * reply at data offset 6, 7 and 8. + * - lenght 8 ("VSLEWxyz") has 3 additional bytes appended, and is + * used to set the voltage slewing point. The SMU replies with "DONE" + * I yet have to figure out their exact meaning of those 3 bytes in + * both cases. + * + */ +#define SMU_CMD_POWER_COMMAND 0xaa +#define SMU_CMD_POWER_RESTART "RESTART" +#define SMU_CMD_POWER_SHUTDOWN "SHUTDOWN" +#define SMU_CMD_POWER_VOLTAGE_SLEW "VSLEW" + +/* Misc commands + * + * This command seem to be a grab bag of various things + */ +#define SMU_CMD_MISC_df_COMMAND 0xdf +#define SMU_CMD_MISC_df_SET_DISPLAY_LIT 0x02 /* i: 1 byte */ +#define SMU_CMD_MISC_df_NMI_OPTION 0x04 + +/* + * Version info commands + * + * I haven't quite tried to figure out how these work + */ +#define SMU_CMD_VERSION_COMMAND 0xea + + +/* + * Misc commands + * + * This command seem to be a grab bag of various things + */ +#define SMU_CMD_MISC_ee_COMMAND 0xee +#define SMU_CMD_MISC_ee_GET_DATABLOCK_REC 0x02 +#define SMU_CMD_MISC_ee_LEDS_CTRL 0x04 /* i: 00 (00,01) [00] */ +#define SMU_CMD_MISC_ee_GET_DATA 0x05 /* i: 00 , o: ?? */ + + + +/* + * - Kernel side interface - + */ + +#ifdef __KERNEL__ + +/* + * Asynchronous SMU commands + * + * Fill up this structure and submit it via smu_queue_command(), + * and get notified by the optional done() callback, or because + * status becomes != 1 + */ + +struct smu_cmd; + +struct smu_cmd +{ + /* public */ + u8 cmd; /* command */ + int data_len; /* data len */ + int reply_len; /* reply len */ + void *data_buf; /* data buffer */ + void *reply_buf; /* reply buffer */ + int status; /* command status */ + void (*done)(struct smu_cmd *cmd, void *misc); + void *misc; + + /* private */ + struct list_head link; +}; + +/* + * Queues an SMU command, all fields have to be initialized + */ +extern int smu_queue_cmd(struct smu_cmd *cmd); + +/* + * Simple command wrapper. This structure embeds a small buffer + * to ease sending simple SMU commands from the stack + */ +struct smu_simple_cmd +{ + struct smu_cmd cmd; + u8 buffer[16]; +}; + +/* + * Queues a simple command. All fields will be initialized by that + * function + */ +extern int smu_queue_simple(struct smu_simple_cmd *scmd, u8 command, + unsigned int data_len, + void (*done)(struct smu_cmd *cmd, void *misc), + void *misc, + ...); + +/* + * Completion helper. Pass it to smu_queue_simple or as 'done' + * member to smu_queue_cmd, it will call complete() on the struct + * completion passed in the "misc" argument + */ +extern void smu_done_complete(struct smu_cmd *cmd, void *misc); /* - * Basic routines for use by architecture. To be extended as - * we understand more of the chip + * Synchronous helpers. Will spin-wait for completion of a command + */ +extern void smu_spinwait_cmd(struct smu_cmd *cmd); + +static inline void smu_spinwait_simple(struct smu_simple_cmd *scmd) +{ + smu_spinwait_cmd(&scmd->cmd); +} + +/* + * Poll routine to call if blocked with irqs off + */ +extern void smu_poll(void); + + +/* + * Init routine, presence check.... */ extern int smu_init(void); extern int smu_present(void); +struct of_device; +extern struct of_device *smu_get_ofdev(void); + + +/* + * Common command wrappers + */ extern void smu_shutdown(void); extern void smu_restart(void); -extern int smu_get_rtc_time(struct rtc_time *time); -extern int smu_set_rtc_time(struct rtc_time *time); +struct rtc_time; +extern int smu_get_rtc_time(struct rtc_time *time, int spinwait); +extern int smu_set_rtc_time(struct rtc_time *time, int spinwait); /* * SMU command buffer absolute address, exported by pmac_setup, * this is allocated very early during boot. */ extern unsigned long smu_cmdbuf_abs; + + +/* + * Kenrel asynchronous i2c interface + */ + +/* SMU i2c header, exactly matches i2c header on wire */ +struct smu_i2c_param +{ + u8 bus; /* SMU bus ID (from device tree) */ + u8 type; /* i2c transfer type */ + u8 devaddr; /* device address (includes direction) */ + u8 sublen; /* subaddress length */ + u8 subaddr[3]; /* subaddress */ + u8 caddr; /* combined address, filled by SMU driver */ + u8 datalen; /* length of transfer */ + u8 data[7]; /* data */ +}; + +#define SMU_I2C_READ_MAX 0x0d +#define SMU_I2C_WRITE_MAX 0x05 + +struct smu_i2c_cmd +{ + /* public */ + struct smu_i2c_param info; + void (*done)(struct smu_i2c_cmd *cmd, void *misc); + void *misc; + int status; /* 1 = pending, 0 = ok, <0 = fail */ + + /* private */ + struct smu_cmd scmd; + int read; + int stage; + int retries; + u8 pdata[0x10]; + struct list_head link; +}; + +/* + * Call this to queue an i2c command to the SMU. You must fill info, + * including info.data for a write, done and misc. + * For now, no polling interface is provided so you have to use completion + * callback. + */ +extern int smu_queue_i2c(struct smu_i2c_cmd *cmd); + + +#endif /* __KERNEL__ */ + +/* + * - Userland interface - + */ + +/* + * A given instance of the device can be configured for 2 different + * things at the moment: + * + * - sending SMU commands (default at open() time) + * - receiving SMU events (not yet implemented) + * + * Commands are written with write() of a command block. They can be + * "driver" commands (for example to switch to event reception mode) + * or real SMU commands. They are made of a header followed by command + * data if any. + * + * For SMU commands (not for driver commands), you can then read() back + * a reply. The reader will be blocked or not depending on how the device + * file is opened. poll() isn't implemented yet. The reply will consist + * of a header as well, followed by the reply data if any. You should + * always provide a buffer large enough for the maximum reply data, I + * recommand one page. + * + * It is illegal to send SMU commands through a file descriptor configured + * for events reception + * + */ +struct smu_user_cmd_hdr +{ + __u32 cmdtype; +#define SMU_CMDTYPE_SMU 0 /* SMU command */ +#define SMU_CMDTYPE_WANTS_EVENTS 1 /* switch fd to events mode */ + + __u8 cmd; /* SMU command byte */ + __u32 data_len; /* Lenght of data following */ +}; + +struct smu_user_reply_hdr +{ + __u32 status; /* Command status */ + __u32 reply_len; /* Lenght of data follwing */ +}; + +#endif /* _SMU_H */ diff --git a/include/asm-s390/futex.h b/include/asm-s390/futex.h index 2cac5ecd9d00..9feff4ce1424 100644 --- a/include/asm-s390/futex.h +++ b/include/asm-s390/futex.h @@ -14,7 +14,7 @@ futex_atomic_op_inuser (int encoded_op, int __user *uaddr) int cmp = (encoded_op >> 24) & 15; int oparg = (encoded_op << 8) >> 20; int cmparg = (encoded_op << 20) >> 20; - int oldval = 0, ret, tem; + int oldval = 0, ret; if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) oparg = 1 << oparg; diff --git a/include/asm-sh/futex.h b/include/asm-sh/futex.h index 2cac5ecd9d00..9feff4ce1424 100644 --- a/include/asm-sh/futex.h +++ b/include/asm-sh/futex.h @@ -14,7 +14,7 @@ futex_atomic_op_inuser (int encoded_op, int __user *uaddr) int cmp = (encoded_op >> 24) & 15; int oparg = (encoded_op << 8) >> 20; int cmparg = (encoded_op << 20) >> 20; - int oldval = 0, ret, tem; + int oldval = 0, ret; if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) oparg = 1 << oparg; diff --git a/include/asm-sh64/futex.h b/include/asm-sh64/futex.h index 2cac5ecd9d00..9feff4ce1424 100644 --- a/include/asm-sh64/futex.h +++ b/include/asm-sh64/futex.h @@ -14,7 +14,7 @@ futex_atomic_op_inuser (int encoded_op, int __user *uaddr) int cmp = (encoded_op >> 24) & 15; int oparg = (encoded_op << 8) >> 20; int cmparg = (encoded_op << 20) >> 20; - int oldval = 0, ret, tem; + int oldval = 0, ret; if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) oparg = 1 << oparg; diff --git a/include/asm-sparc/futex.h b/include/asm-sparc/futex.h index 2cac5ecd9d00..9feff4ce1424 100644 --- a/include/asm-sparc/futex.h +++ b/include/asm-sparc/futex.h @@ -14,7 +14,7 @@ futex_atomic_op_inuser (int encoded_op, int __user *uaddr) int cmp = (encoded_op >> 24) & 15; int oparg = (encoded_op << 8) >> 20; int cmparg = (encoded_op << 20) >> 20; - int oldval = 0, ret, tem; + int oldval = 0, ret; if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) oparg = 1 << oparg; diff --git a/include/asm-sparc64/cacheflush.h b/include/asm-sparc64/cacheflush.h index 51b26e81d828..ededd2659eab 100644 --- a/include/asm-sparc64/cacheflush.h +++ b/include/asm-sparc64/cacheflush.h @@ -4,13 +4,6 @@ #include <linux/config.h> #include <asm/page.h> -/* Flushing for D-cache alias handling is only needed if - * the page size is smaller than 16K. - */ -#if PAGE_SHIFT < 14 -#define DCACHE_ALIASING_POSSIBLE -#endif - #ifndef __ASSEMBLY__ #include <linux/mm.h> diff --git a/include/asm-sparc64/futex.h b/include/asm-sparc64/futex.h index 2cac5ecd9d00..9feff4ce1424 100644 --- a/include/asm-sparc64/futex.h +++ b/include/asm-sparc64/futex.h @@ -14,7 +14,7 @@ futex_atomic_op_inuser (int encoded_op, int __user *uaddr) int cmp = (encoded_op >> 24) & 15; int oparg = (encoded_op << 8) >> 20; int cmparg = (encoded_op << 20) >> 20; - int oldval = 0, ret, tem; + int oldval = 0, ret; if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) oparg = 1 << oparg; diff --git a/include/asm-sparc64/ide.h b/include/asm-sparc64/ide.h index 4c1098474c73..c393f815b0be 100644 --- a/include/asm-sparc64/ide.h +++ b/include/asm-sparc64/ide.h @@ -15,6 +15,7 @@ #include <asm/io.h> #include <asm/spitfire.h> #include <asm/cacheflush.h> +#include <asm/page.h> #ifndef MAX_HWIFS # ifdef CONFIG_BLK_DEV_IDEPCI diff --git a/include/asm-sparc64/page.h b/include/asm-sparc64/page.h index c9f8ef208ea5..7f8d764abc47 100644 --- a/include/asm-sparc64/page.h +++ b/include/asm-sparc64/page.h @@ -21,6 +21,13 @@ #define PAGE_SIZE (_AC(1,UL) << PAGE_SHIFT) #define PAGE_MASK (~(PAGE_SIZE-1)) +/* Flushing for D-cache alias handling is only needed if + * the page size is smaller than 16K. + */ +#if PAGE_SHIFT < 14 +#define DCACHE_ALIASING_POSSIBLE +#endif + #ifdef __KERNEL__ #ifndef __ASSEMBLY__ diff --git a/include/asm-sparc64/pgalloc.h b/include/asm-sparc64/pgalloc.h index b9b1914aae63..a96067cca963 100644 --- a/include/asm-sparc64/pgalloc.h +++ b/include/asm-sparc64/pgalloc.h @@ -10,6 +10,7 @@ #include <asm/spitfire.h> #include <asm/cpudata.h> #include <asm/cacheflush.h> +#include <asm/page.h> /* Page table allocation/freeing. */ #ifdef CONFIG_SMP diff --git a/include/asm-sparc64/pgtable.h b/include/asm-sparc64/pgtable.h index a2b4f5ed4625..a297f6144f0f 100644 --- a/include/asm-sparc64/pgtable.h +++ b/include/asm-sparc64/pgtable.h @@ -24,21 +24,23 @@ #include <asm/processor.h> #include <asm/const.h> -/* The kernel image occupies 0x4000000 to 0x1000000 (4MB --> 16MB). - * The page copy blockops use 0x1000000 to 0x18000000 (16MB --> 24MB). +/* The kernel image occupies 0x4000000 to 0x1000000 (4MB --> 32MB). + * The page copy blockops can use 0x2000000 to 0x10000000. * The PROM resides in an area spanning 0xf0000000 to 0x100000000. - * The vmalloc area spans 0x140000000 to 0x200000000. + * The vmalloc area spans 0x100000000 to 0x200000000. + * Since modules need to be in the lowest 32-bits of the address space, + * we place them right before the OBP area from 0x10000000 to 0xf0000000. * There is a single static kernel PMD which maps from 0x0 to address * 0x400000000. */ -#define TLBTEMP_BASE _AC(0x0000000001000000,UL) -#define MODULES_VADDR _AC(0x0000000002000000,UL) -#define MODULES_LEN _AC(0x000000007e000000,UL) -#define MODULES_END _AC(0x0000000080000000,UL) -#define VMALLOC_START _AC(0x0000000140000000,UL) -#define VMALLOC_END _AC(0x0000000200000000,UL) +#define TLBTEMP_BASE _AC(0x0000000002000000,UL) +#define MODULES_VADDR _AC(0x0000000010000000,UL) +#define MODULES_LEN _AC(0x00000000e0000000,UL) +#define MODULES_END _AC(0x00000000f0000000,UL) #define LOW_OBP_ADDRESS _AC(0x00000000f0000000,UL) #define HI_OBP_ADDRESS _AC(0x0000000100000000,UL) +#define VMALLOC_START _AC(0x0000000100000000,UL) +#define VMALLOC_END _AC(0x0000000200000000,UL) /* XXX All of this needs to be rethought so we can take advantage * XXX cheetah's full 64-bit virtual address space, ie. no more hole diff --git a/include/asm-um/futex.h b/include/asm-um/futex.h index 2cac5ecd9d00..142ee2d8e0fd 100644 --- a/include/asm-um/futex.h +++ b/include/asm-um/futex.h @@ -1,53 +1,12 @@ -#ifndef _ASM_FUTEX_H -#define _ASM_FUTEX_H - -#ifdef __KERNEL__ +#ifndef __UM_FUTEX_H +#define __UM_FUTEX_H #include <linux/futex.h> #include <asm/errno.h> +#include <asm/system.h> +#include <asm/processor.h> #include <asm/uaccess.h> -static inline int -futex_atomic_op_inuser (int encoded_op, int __user *uaddr) -{ - int op = (encoded_op >> 28) & 7; - int cmp = (encoded_op >> 24) & 15; - int oparg = (encoded_op << 8) >> 20; - int cmparg = (encoded_op << 20) >> 20; - int oldval = 0, ret, tem; - if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) - oparg = 1 << oparg; - - if (! access_ok (VERIFY_WRITE, uaddr, sizeof(int))) - return -EFAULT; - - inc_preempt_count(); - - switch (op) { - case FUTEX_OP_SET: - case FUTEX_OP_ADD: - case FUTEX_OP_OR: - case FUTEX_OP_ANDN: - case FUTEX_OP_XOR: - default: - ret = -ENOSYS; - } +#include "asm/arch/futex.h" - dec_preempt_count(); - - if (!ret) { - switch (cmp) { - case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break; - case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break; - case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break; - case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break; - case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break; - case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break; - default: ret = -ENOSYS; - } - } - return ret; -} - -#endif #endif diff --git a/include/asm-um/pgtable.h b/include/asm-um/pgtable.h index ed06170e0edd..616d02b57ea9 100644 --- a/include/asm-um/pgtable.h +++ b/include/asm-um/pgtable.h @@ -346,7 +346,6 @@ static inline void set_pte(pte_t *pteptr, pte_t pteval) static inline pte_t pte_modify(pte_t pte, pgprot_t newprot) { pte_set_val(pte, (pte_val(pte) & _PAGE_CHG_MASK), newprot); - if(pte_present(pte)) pte = pte_mknewpage(pte_mknewprot(pte)); return pte; } diff --git a/include/asm-v850/futex.h b/include/asm-v850/futex.h index 2cac5ecd9d00..9feff4ce1424 100644 --- a/include/asm-v850/futex.h +++ b/include/asm-v850/futex.h @@ -14,7 +14,7 @@ futex_atomic_op_inuser (int encoded_op, int __user *uaddr) int cmp = (encoded_op >> 24) & 15; int oparg = (encoded_op << 8) >> 20; int cmparg = (encoded_op << 20) >> 20; - int oldval = 0, ret, tem; + int oldval = 0, ret; if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) oparg = 1 << oparg; diff --git a/include/asm-xtensa/atomic.h b/include/asm-xtensa/atomic.h index 24f86f0e43cf..12b5732dc6e5 100644 --- a/include/asm-xtensa/atomic.h +++ b/include/asm-xtensa/atomic.h @@ -22,7 +22,7 @@ typedef struct { volatile int counter; } atomic_t; #include <asm/processor.h> #include <asm/system.h> -#define ATOMIC_INIT(i) ( (atomic_t) { (i) } ) +#define ATOMIC_INIT(i) { (i) } /* * This Xtensa implementation assumes that the right mechanism diff --git a/include/asm-xtensa/bitops.h b/include/asm-xtensa/bitops.h index d395ef226c32..e76ee889e21d 100644 --- a/include/asm-xtensa/bitops.h +++ b/include/asm-xtensa/bitops.h @@ -174,7 +174,7 @@ static __inline__ int test_bit(int nr, const volatile void *addr) return 1UL & (((const volatile unsigned int *)addr)[nr>>5] >> (nr&31)); } -#if XCHAL_HAVE_NSAU +#if XCHAL_HAVE_NSA static __inline__ int __cntlz (unsigned long x) { diff --git a/include/asm-xtensa/hardirq.h b/include/asm-xtensa/hardirq.h index e07c76c36b95..aa9c1adf68d7 100644 --- a/include/asm-xtensa/hardirq.h +++ b/include/asm-xtensa/hardirq.h @@ -23,6 +23,7 @@ typedef struct { unsigned int __nmi_count; /* arch dependent */ } ____cacheline_aligned irq_cpustat_t; +void ack_bad_irq(unsigned int irq); #include <linux/irq_cpustat.h> /* Standard mappings for irq_cpustat_t above */ #endif /* _XTENSA_HARDIRQ_H */ diff --git a/include/asm-xtensa/semaphore.h b/include/asm-xtensa/semaphore.h index db740b8bc6f0..09e89ab3eb61 100644 --- a/include/asm-xtensa/semaphore.h +++ b/include/asm-xtensa/semaphore.h @@ -20,28 +20,19 @@ struct semaphore { atomic_t count; int sleepers; wait_queue_head_t wait; -#if WAITQUEUE_DEBUG - long __magic; -#endif }; -#if WAITQUEUE_DEBUG -# define __SEM_DEBUG_INIT(name) \ - , (int)&(name).__magic -#else -# define __SEM_DEBUG_INIT(name) -#endif - -#define __SEMAPHORE_INITIALIZER(name,count) \ - { ATOMIC_INIT(count), \ - 0, \ - __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ - __SEM_DEBUG_INIT(name) } +#define __SEMAPHORE_INITIALIZER(name,n) \ +{ \ + .count = ATOMIC_INIT(n), \ + .sleepers = 0, \ + .wait = __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ +} -#define __MUTEX_INITIALIZER(name) \ +#define __MUTEX_INITIALIZER(name) \ __SEMAPHORE_INITIALIZER(name, 1) -#define __DECLARE_SEMAPHORE_GENERIC(name,count) \ +#define __DECLARE_SEMAPHORE_GENERIC(name,count) \ struct semaphore name = __SEMAPHORE_INITIALIZER(name,count) #define DECLARE_MUTEX(name) __DECLARE_SEMAPHORE_GENERIC(name,1) @@ -49,17 +40,8 @@ struct semaphore { static inline void sema_init (struct semaphore *sem, int val) { -/* - * *sem = (struct semaphore)__SEMAPHORE_INITIALIZER((*sem),val); - * - * i'd rather use the more flexible initialization above, but sadly - * GCC 2.7.2.3 emits a bogus warning. EGCS doesnt. Oh well. - */ atomic_set(&sem->count, val); init_waitqueue_head(&sem->wait); -#if WAITQUEUE_DEBUG - sem->__magic = (int)&sem->__magic; -#endif } static inline void init_MUTEX (struct semaphore *sem) @@ -81,9 +63,7 @@ extern spinlock_t semaphore_wake_lock; static inline void down(struct semaphore * sem) { -#if WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif + might_sleep(); if (atomic_sub_return(1, &sem->count) < 0) __down(sem); @@ -92,9 +72,8 @@ static inline void down(struct semaphore * sem) static inline int down_interruptible(struct semaphore * sem) { int ret = 0; -#if WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif + + might_sleep(); if (atomic_sub_return(1, &sem->count) < 0) ret = __down_interruptible(sem); @@ -104,9 +83,6 @@ static inline int down_interruptible(struct semaphore * sem) static inline int down_trylock(struct semaphore * sem) { int ret = 0; -#if WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif if (atomic_sub_return(1, &sem->count) < 0) ret = __down_trylock(sem); @@ -119,9 +95,6 @@ static inline int down_trylock(struct semaphore * sem) */ static inline void up(struct semaphore * sem) { -#if WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif if (atomic_add_return(1, &sem->count) <= 0) __up(sem); } diff --git a/include/asm-xtensa/system.h b/include/asm-xtensa/system.h index f09393232e5e..9284867f1cb9 100644 --- a/include/asm-xtensa/system.h +++ b/include/asm-xtensa/system.h @@ -189,20 +189,6 @@ static inline unsigned long xchg_u32(volatile int * m, unsigned long val) #define tas(ptr) (xchg((ptr),1)) -#if ( __XCC__ == 1 ) - -/* xt-xcc processes __inline__ differently than xt-gcc and decides to - * insert an out-of-line copy of function __xchg. This presents the - * unresolved symbol at link time of __xchg_called_with_bad_pointer, - * even though such a function would never be called at run-time. - * xt-gcc always inlines __xchg, and optimizes away the undefined - * bad_pointer function. - */ - -#define xchg(ptr,x) xchg_u32(ptr,x) - -#else /* assume xt-gcc */ - #define xchg(ptr,x) ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr)))) /* @@ -224,8 +210,6 @@ __xchg(unsigned long x, volatile void * ptr, int size) return x; } -#endif - extern void set_except_vector(int n, void *addr); static inline void spill_registers(void) diff --git a/include/linux/byteorder/generic.h b/include/linux/byteorder/generic.h index 5fde6f4d6c1e..04bd756efc67 100644 --- a/include/linux/byteorder/generic.h +++ b/include/linux/byteorder/generic.h @@ -5,6 +5,10 @@ * linux/byteorder_generic.h * Generic Byte-reordering support * + * The "... p" macros, like le64_to_cpup, can be used with pointers + * to unaligned data, but there will be a performance penalty on + * some architectures. Use get_unaligned for unaligned data. + * * Francois-Rene Rideau <fare@tunes.org> 19970707 * gathered all the good ideas from all asm-foo/byteorder.h into one file, * cleaned them up. diff --git a/include/linux/device.h b/include/linux/device.h index 06e5d42f2c7b..95d607a48f06 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -317,6 +317,11 @@ dev_set_drvdata (struct device *dev, void *data) dev->driver_data = data; } +static inline int device_is_registered(struct device *dev) +{ + return klist_node_attached(&dev->knode_bus); +} + /* * High level routines for use by the bus drivers */ diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index 17d0c0d40b0e..eef0876d8307 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -42,8 +42,8 @@ struct hlist_node; struct vlan_ethhdr { unsigned char h_dest[ETH_ALEN]; /* destination eth addr */ unsigned char h_source[ETH_ALEN]; /* source ether addr */ - unsigned short h_vlan_proto; /* Should always be 0x8100 */ - unsigned short h_vlan_TCI; /* Encapsulates priority and VLAN ID */ + __be16 h_vlan_proto; /* Should always be 0x8100 */ + __be16 h_vlan_TCI; /* Encapsulates priority and VLAN ID */ unsigned short h_vlan_encapsulated_proto; /* packet type ID field (or len) */ }; @@ -55,8 +55,8 @@ static inline struct vlan_ethhdr *vlan_eth_hdr(const struct sk_buff *skb) } struct vlan_hdr { - unsigned short h_vlan_TCI; /* Encapsulates priority and VLAN ID */ - unsigned short h_vlan_encapsulated_proto; /* packet type ID field (or len) */ + __be16 h_vlan_TCI; /* Encapsulates priority and VLAN ID */ + __be16 h_vlan_encapsulated_proto; /* packet type ID field (or len) */ }; #define VLAN_VID_MASK 0xfff diff --git a/include/linux/libata.h b/include/linux/libata.h index 022105c745fc..ceee1fc42c60 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -393,6 +393,7 @@ extern int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_i extern void ata_pci_remove_one (struct pci_dev *pdev); #endif /* CONFIG_PCI */ extern int ata_device_add(struct ata_probe_ent *ent); +extern void ata_host_set_remove(struct ata_host_set *host_set); extern int ata_scsi_detect(Scsi_Host_Template *sht); extern int ata_scsi_ioctl(struct scsi_device *dev, int cmd, void __user *arg); extern int ata_scsi_queuecmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)); diff --git a/include/linux/mm.h b/include/linux/mm.h index 82d7024f0765..097b3a3c693d 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -136,6 +136,7 @@ extern unsigned int kobjsize(const void *objp); #define VM_EXEC 0x00000004 #define VM_SHARED 0x00000008 +/* mprotect() hardcodes VM_MAYREAD >> 4 == VM_READ, and so for r/w/x bits. */ #define VM_MAYREAD 0x00000010 /* limits for mprotect() etc */ #define VM_MAYWRITE 0x00000020 #define VM_MAYEXEC 0x00000040 @@ -350,7 +351,8 @@ static inline void put_page(struct page *page) * only one copy in memory, at most, normally. * * For the non-reserved pages, page_count(page) denotes a reference count. - * page_count() == 0 means the page is free. + * page_count() == 0 means the page is free. page->lru is then used for + * freelist management in the buddy allocator. * page_count() == 1 means the page is used for exactly one purpose * (e.g. a private data page of one process). * @@ -376,10 +378,8 @@ static inline void put_page(struct page *page) * attaches, plus 1 if `private' contains something, plus one for * the page cache itself. * - * All pages belonging to an inode are in these doubly linked lists: - * mapping->clean_pages, mapping->dirty_pages and mapping->locked_pages; - * using the page->list list_head. These fields are also used for - * freelist managemet (when page_count()==0). + * Instead of keeping dirty/clean pages in per address-space lists, we instead + * now tag pages as dirty/under writeback in the radix tree. * * There is also a per-mapping radix tree mapping index to the page * in memory if present. The tree is rooted at mapping->root. diff --git a/include/linux/netfilter_ipv4/ip_conntrack.h b/include/linux/netfilter_ipv4/ip_conntrack.h index 7e033e9271a8..4ced38736813 100644 --- a/include/linux/netfilter_ipv4/ip_conntrack.h +++ b/include/linux/netfilter_ipv4/ip_conntrack.h @@ -133,11 +133,13 @@ enum ip_conntrack_expect_events { #include <linux/netfilter_ipv4/ip_conntrack_tcp.h> #include <linux/netfilter_ipv4/ip_conntrack_icmp.h> +#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h> #include <linux/netfilter_ipv4/ip_conntrack_sctp.h> /* per conntrack: protocol private data */ union ip_conntrack_proto { /* insert conntrack proto private data here */ + struct ip_ct_gre gre; struct ip_ct_sctp sctp; struct ip_ct_tcp tcp; struct ip_ct_icmp icmp; @@ -148,6 +150,7 @@ union ip_conntrack_expect_proto { }; /* Add protocol helper include file here */ +#include <linux/netfilter_ipv4/ip_conntrack_pptp.h> #include <linux/netfilter_ipv4/ip_conntrack_amanda.h> #include <linux/netfilter_ipv4/ip_conntrack_ftp.h> #include <linux/netfilter_ipv4/ip_conntrack_irc.h> @@ -155,12 +158,20 @@ union ip_conntrack_expect_proto { /* per conntrack: application helper private data */ union ip_conntrack_help { /* insert conntrack helper private data (master) here */ + struct ip_ct_pptp_master ct_pptp_info; struct ip_ct_ftp_master ct_ftp_info; struct ip_ct_irc_master ct_irc_info; }; #ifdef CONFIG_IP_NF_NAT_NEEDED #include <linux/netfilter_ipv4/ip_nat.h> +#include <linux/netfilter_ipv4/ip_nat_pptp.h> + +/* per conntrack: nat application helper private data */ +union ip_conntrack_nat_help { + /* insert nat helper private data here */ + struct ip_nat_pptp nat_pptp_info; +}; #endif #include <linux/types.h> @@ -223,6 +234,7 @@ struct ip_conntrack #ifdef CONFIG_IP_NF_NAT_NEEDED struct { struct ip_nat_info info; + union ip_conntrack_nat_help help; #if defined(CONFIG_IP_NF_TARGET_MASQUERADE) || \ defined(CONFIG_IP_NF_TARGET_MASQUERADE_MODULE) int masq_index; @@ -320,11 +332,28 @@ extern void need_ip_conntrack(void); extern int invert_tuplepr(struct ip_conntrack_tuple *inverse, const struct ip_conntrack_tuple *orig); +extern void __ip_ct_refresh_acct(struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + const struct sk_buff *skb, + unsigned long extra_jiffies, + int do_acct); + +/* Refresh conntrack for this many jiffies and do accounting */ +static inline void ip_ct_refresh_acct(struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + const struct sk_buff *skb, + unsigned long extra_jiffies) +{ + __ip_ct_refresh_acct(ct, ctinfo, skb, extra_jiffies, 1); +} + /* Refresh conntrack for this many jiffies */ -extern void ip_ct_refresh_acct(struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - const struct sk_buff *skb, - unsigned long extra_jiffies); +static inline void ip_ct_refresh(struct ip_conntrack *ct, + const struct sk_buff *skb, + unsigned long extra_jiffies) +{ + __ip_ct_refresh_acct(ct, 0, skb, extra_jiffies, 0); +} /* These are for NAT. Icky. */ /* Update TCP window tracking data when NAT mangles the packet */ @@ -372,7 +401,7 @@ extern struct ip_conntrack_expect * __ip_conntrack_expect_find(const struct ip_conntrack_tuple *tuple); extern struct ip_conntrack_expect * -ip_conntrack_expect_find_get(const struct ip_conntrack_tuple *tuple); +ip_conntrack_expect_find(const struct ip_conntrack_tuple *tuple); extern struct ip_conntrack_tuple_hash * __ip_conntrack_find(const struct ip_conntrack_tuple *tuple, diff --git a/include/linux/netfilter_ipv4/ip_conntrack_pptp.h b/include/linux/netfilter_ipv4/ip_conntrack_pptp.h new file mode 100644 index 000000000000..816144c75de0 --- /dev/null +++ b/include/linux/netfilter_ipv4/ip_conntrack_pptp.h @@ -0,0 +1,325 @@ +/* PPTP constants and structs */ +#ifndef _CONNTRACK_PPTP_H +#define _CONNTRACK_PPTP_H + +/* state of the control session */ +enum pptp_ctrlsess_state { + PPTP_SESSION_NONE, /* no session present */ + PPTP_SESSION_ERROR, /* some session error */ + PPTP_SESSION_STOPREQ, /* stop_sess request seen */ + PPTP_SESSION_REQUESTED, /* start_sess request seen */ + PPTP_SESSION_CONFIRMED, /* session established */ +}; + +/* state of the call inside the control session */ +enum pptp_ctrlcall_state { + PPTP_CALL_NONE, + PPTP_CALL_ERROR, + PPTP_CALL_OUT_REQ, + PPTP_CALL_OUT_CONF, + PPTP_CALL_IN_REQ, + PPTP_CALL_IN_REP, + PPTP_CALL_IN_CONF, + PPTP_CALL_CLEAR_REQ, +}; + + +/* conntrack private data */ +struct ip_ct_pptp_master { + enum pptp_ctrlsess_state sstate; /* session state */ + + /* everything below is going to be per-expectation in newnat, + * since there could be more than one call within one session */ + enum pptp_ctrlcall_state cstate; /* call state */ + u_int16_t pac_call_id; /* call id of PAC, host byte order */ + u_int16_t pns_call_id; /* call id of PNS, host byte order */ + + /* in pre-2.6.11 this used to be per-expect. Now it is per-conntrack + * and therefore imposes a fixed limit on the number of maps */ + struct ip_ct_gre_keymap *keymap_orig, *keymap_reply; +}; + +/* conntrack_expect private member */ +struct ip_ct_pptp_expect { + enum pptp_ctrlcall_state cstate; /* call state */ + u_int16_t pac_call_id; /* call id of PAC */ + u_int16_t pns_call_id; /* call id of PNS */ +}; + + +#ifdef __KERNEL__ + +#define IP_CONNTR_PPTP PPTP_CONTROL_PORT + +#define PPTP_CONTROL_PORT 1723 + +#define PPTP_PACKET_CONTROL 1 +#define PPTP_PACKET_MGMT 2 + +#define PPTP_MAGIC_COOKIE 0x1a2b3c4d + +struct pptp_pkt_hdr { + __u16 packetLength; + __be16 packetType; + __be32 magicCookie; +}; + +/* PptpControlMessageType values */ +#define PPTP_START_SESSION_REQUEST 1 +#define PPTP_START_SESSION_REPLY 2 +#define PPTP_STOP_SESSION_REQUEST 3 +#define PPTP_STOP_SESSION_REPLY 4 +#define PPTP_ECHO_REQUEST 5 +#define PPTP_ECHO_REPLY 6 +#define PPTP_OUT_CALL_REQUEST 7 +#define PPTP_OUT_CALL_REPLY 8 +#define PPTP_IN_CALL_REQUEST 9 +#define PPTP_IN_CALL_REPLY 10 +#define PPTP_IN_CALL_CONNECT 11 +#define PPTP_CALL_CLEAR_REQUEST 12 +#define PPTP_CALL_DISCONNECT_NOTIFY 13 +#define PPTP_WAN_ERROR_NOTIFY 14 +#define PPTP_SET_LINK_INFO 15 + +#define PPTP_MSG_MAX 15 + +/* PptpGeneralError values */ +#define PPTP_ERROR_CODE_NONE 0 +#define PPTP_NOT_CONNECTED 1 +#define PPTP_BAD_FORMAT 2 +#define PPTP_BAD_VALUE 3 +#define PPTP_NO_RESOURCE 4 +#define PPTP_BAD_CALLID 5 +#define PPTP_REMOVE_DEVICE_ERROR 6 + +struct PptpControlHeader { + __be16 messageType; + __u16 reserved; +}; + +/* FramingCapability Bitmap Values */ +#define PPTP_FRAME_CAP_ASYNC 0x1 +#define PPTP_FRAME_CAP_SYNC 0x2 + +/* BearerCapability Bitmap Values */ +#define PPTP_BEARER_CAP_ANALOG 0x1 +#define PPTP_BEARER_CAP_DIGITAL 0x2 + +struct PptpStartSessionRequest { + __be16 protocolVersion; + __u8 reserved1; + __u8 reserved2; + __be32 framingCapability; + __be32 bearerCapability; + __be16 maxChannels; + __be16 firmwareRevision; + __u8 hostName[64]; + __u8 vendorString[64]; +}; + +/* PptpStartSessionResultCode Values */ +#define PPTP_START_OK 1 +#define PPTP_START_GENERAL_ERROR 2 +#define PPTP_START_ALREADY_CONNECTED 3 +#define PPTP_START_NOT_AUTHORIZED 4 +#define PPTP_START_UNKNOWN_PROTOCOL 5 + +struct PptpStartSessionReply { + __be16 protocolVersion; + __u8 resultCode; + __u8 generalErrorCode; + __be32 framingCapability; + __be32 bearerCapability; + __be16 maxChannels; + __be16 firmwareRevision; + __u8 hostName[64]; + __u8 vendorString[64]; +}; + +/* PptpStopReasons */ +#define PPTP_STOP_NONE 1 +#define PPTP_STOP_PROTOCOL 2 +#define PPTP_STOP_LOCAL_SHUTDOWN 3 + +struct PptpStopSessionRequest { + __u8 reason; +}; + +/* PptpStopSessionResultCode */ +#define PPTP_STOP_OK 1 +#define PPTP_STOP_GENERAL_ERROR 2 + +struct PptpStopSessionReply { + __u8 resultCode; + __u8 generalErrorCode; +}; + +struct PptpEchoRequest { + __be32 identNumber; +}; + +/* PptpEchoReplyResultCode */ +#define PPTP_ECHO_OK 1 +#define PPTP_ECHO_GENERAL_ERROR 2 + +struct PptpEchoReply { + __be32 identNumber; + __u8 resultCode; + __u8 generalErrorCode; + __u16 reserved; +}; + +/* PptpFramingType */ +#define PPTP_ASYNC_FRAMING 1 +#define PPTP_SYNC_FRAMING 2 +#define PPTP_DONT_CARE_FRAMING 3 + +/* PptpCallBearerType */ +#define PPTP_ANALOG_TYPE 1 +#define PPTP_DIGITAL_TYPE 2 +#define PPTP_DONT_CARE_BEARER_TYPE 3 + +struct PptpOutCallRequest { + __be16 callID; + __be16 callSerialNumber; + __be32 minBPS; + __be32 maxBPS; + __be32 bearerType; + __be32 framingType; + __be16 packetWindow; + __be16 packetProcDelay; + __u16 reserved1; + __be16 phoneNumberLength; + __u16 reserved2; + __u8 phoneNumber[64]; + __u8 subAddress[64]; +}; + +/* PptpCallResultCode */ +#define PPTP_OUTCALL_CONNECT 1 +#define PPTP_OUTCALL_GENERAL_ERROR 2 +#define PPTP_OUTCALL_NO_CARRIER 3 +#define PPTP_OUTCALL_BUSY 4 +#define PPTP_OUTCALL_NO_DIAL_TONE 5 +#define PPTP_OUTCALL_TIMEOUT 6 +#define PPTP_OUTCALL_DONT_ACCEPT 7 + +struct PptpOutCallReply { + __be16 callID; + __be16 peersCallID; + __u8 resultCode; + __u8 generalErrorCode; + __be16 causeCode; + __be32 connectSpeed; + __be16 packetWindow; + __be16 packetProcDelay; + __be32 physChannelID; +}; + +struct PptpInCallRequest { + __be16 callID; + __be16 callSerialNumber; + __be32 callBearerType; + __be32 physChannelID; + __be16 dialedNumberLength; + __be16 dialingNumberLength; + __u8 dialedNumber[64]; + __u8 dialingNumber[64]; + __u8 subAddress[64]; +}; + +/* PptpInCallResultCode */ +#define PPTP_INCALL_ACCEPT 1 +#define PPTP_INCALL_GENERAL_ERROR 2 +#define PPTP_INCALL_DONT_ACCEPT 3 + +struct PptpInCallReply { + __be16 callID; + __be16 peersCallID; + __u8 resultCode; + __u8 generalErrorCode; + __be16 packetWindow; + __be16 packetProcDelay; + __u16 reserved; +}; + +struct PptpInCallConnected { + __be16 peersCallID; + __u16 reserved; + __be32 connectSpeed; + __be16 packetWindow; + __be16 packetProcDelay; + __be32 callFramingType; +}; + +struct PptpClearCallRequest { + __be16 callID; + __u16 reserved; +}; + +struct PptpCallDisconnectNotify { + __be16 callID; + __u8 resultCode; + __u8 generalErrorCode; + __be16 causeCode; + __u16 reserved; + __u8 callStatistics[128]; +}; + +struct PptpWanErrorNotify { + __be16 peersCallID; + __u16 reserved; + __be32 crcErrors; + __be32 framingErrors; + __be32 hardwareOverRuns; + __be32 bufferOverRuns; + __be32 timeoutErrors; + __be32 alignmentErrors; +}; + +struct PptpSetLinkInfo { + __be16 peersCallID; + __u16 reserved; + __be32 sendAccm; + __be32 recvAccm; +}; + +union pptp_ctrl_union { + struct PptpStartSessionRequest sreq; + struct PptpStartSessionReply srep; + struct PptpStopSessionRequest streq; + struct PptpStopSessionReply strep; + struct PptpOutCallRequest ocreq; + struct PptpOutCallReply ocack; + struct PptpInCallRequest icreq; + struct PptpInCallReply icack; + struct PptpInCallConnected iccon; + struct PptpClearCallRequest clrreq; + struct PptpCallDisconnectNotify disc; + struct PptpWanErrorNotify wanerr; + struct PptpSetLinkInfo setlink; +}; + +extern int +(*ip_nat_pptp_hook_outbound)(struct sk_buff **pskb, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + struct PptpControlHeader *ctlh, + union pptp_ctrl_union *pptpReq); + +extern int +(*ip_nat_pptp_hook_inbound)(struct sk_buff **pskb, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + struct PptpControlHeader *ctlh, + union pptp_ctrl_union *pptpReq); + +extern int +(*ip_nat_pptp_hook_exp_gre)(struct ip_conntrack_expect *exp_orig, + struct ip_conntrack_expect *exp_reply); + +extern void +(*ip_nat_pptp_hook_expectfn)(struct ip_conntrack *ct, + struct ip_conntrack_expect *exp); +#endif /* __KERNEL__ */ +#endif /* _CONNTRACK_PPTP_H */ diff --git a/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h b/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h new file mode 100644 index 000000000000..8d090ef82f5f --- /dev/null +++ b/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h @@ -0,0 +1,114 @@ +#ifndef _CONNTRACK_PROTO_GRE_H +#define _CONNTRACK_PROTO_GRE_H +#include <asm/byteorder.h> + +/* GRE PROTOCOL HEADER */ + +/* GRE Version field */ +#define GRE_VERSION_1701 0x0 +#define GRE_VERSION_PPTP 0x1 + +/* GRE Protocol field */ +#define GRE_PROTOCOL_PPTP 0x880B + +/* GRE Flags */ +#define GRE_FLAG_C 0x80 +#define GRE_FLAG_R 0x40 +#define GRE_FLAG_K 0x20 +#define GRE_FLAG_S 0x10 +#define GRE_FLAG_A 0x80 + +#define GRE_IS_C(f) ((f)&GRE_FLAG_C) +#define GRE_IS_R(f) ((f)&GRE_FLAG_R) +#define GRE_IS_K(f) ((f)&GRE_FLAG_K) +#define GRE_IS_S(f) ((f)&GRE_FLAG_S) +#define GRE_IS_A(f) ((f)&GRE_FLAG_A) + +/* GRE is a mess: Four different standards */ +struct gre_hdr { +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u16 rec:3, + srr:1, + seq:1, + key:1, + routing:1, + csum:1, + version:3, + reserved:4, + ack:1; +#elif defined(__BIG_ENDIAN_BITFIELD) + __u16 csum:1, + routing:1, + key:1, + seq:1, + srr:1, + rec:3, + ack:1, + reserved:4, + version:3; +#else +#error "Adjust your <asm/byteorder.h> defines" +#endif + __u16 protocol; +}; + +/* modified GRE header for PPTP */ +struct gre_hdr_pptp { + __u8 flags; /* bitfield */ + __u8 version; /* should be GRE_VERSION_PPTP */ + __u16 protocol; /* should be GRE_PROTOCOL_PPTP */ + __u16 payload_len; /* size of ppp payload, not inc. gre header */ + __u16 call_id; /* peer's call_id for this session */ + __u32 seq; /* sequence number. Present if S==1 */ + __u32 ack; /* seq number of highest packet recieved by */ + /* sender in this session */ +}; + + +/* this is part of ip_conntrack */ +struct ip_ct_gre { + unsigned int stream_timeout; + unsigned int timeout; +}; + +#ifdef __KERNEL__ +struct ip_conntrack_expect; +struct ip_conntrack; + +/* structure for original <-> reply keymap */ +struct ip_ct_gre_keymap { + struct list_head list; + + struct ip_conntrack_tuple tuple; +}; + +/* add new tuple->key_reply pair to keymap */ +int ip_ct_gre_keymap_add(struct ip_conntrack *ct, + struct ip_conntrack_tuple *t, + int reply); + +/* delete keymap entries */ +void ip_ct_gre_keymap_destroy(struct ip_conntrack *ct); + + +/* get pointer to gre key, if present */ +static inline u_int32_t *gre_key(struct gre_hdr *greh) +{ + if (!greh->key) + return NULL; + if (greh->csum || greh->routing) + return (u_int32_t *) (greh+sizeof(*greh)+4); + return (u_int32_t *) (greh+sizeof(*greh)); +} + +/* get pointer ot gre csum, if present */ +static inline u_int16_t *gre_csum(struct gre_hdr *greh) +{ + if (!greh->csum) + return NULL; + return (u_int16_t *) (greh+sizeof(*greh)); +} + +#endif /* __KERNEL__ */ + +#endif /* _CONNTRACK_PROTO_GRE_H */ diff --git a/include/linux/netfilter_ipv4/ip_conntrack_tuple.h b/include/linux/netfilter_ipv4/ip_conntrack_tuple.h index c33f0b5e0d0a..20e43f018b7c 100644 --- a/include/linux/netfilter_ipv4/ip_conntrack_tuple.h +++ b/include/linux/netfilter_ipv4/ip_conntrack_tuple.h @@ -17,7 +17,7 @@ union ip_conntrack_manip_proto u_int16_t all; struct { - u_int16_t port; + __be16 port; } tcp; struct { u_int16_t port; @@ -28,6 +28,9 @@ union ip_conntrack_manip_proto struct { u_int16_t port; } sctp; + struct { + __be16 key; /* key is 32bit, pptp only uses 16 */ + } gre; }; /* The manipulable part of the tuple. */ @@ -61,6 +64,10 @@ struct ip_conntrack_tuple struct { u_int16_t port; } sctp; + struct { + __be16 key; /* key is 32bit, + * pptp only uses 16 */ + } gre; } u; /* The protocol. */ diff --git a/include/linux/netfilter_ipv4/ip_nat_pptp.h b/include/linux/netfilter_ipv4/ip_nat_pptp.h new file mode 100644 index 000000000000..eaf66c2e8f93 --- /dev/null +++ b/include/linux/netfilter_ipv4/ip_nat_pptp.h @@ -0,0 +1,11 @@ +/* PPTP constants and structs */ +#ifndef _NAT_PPTP_H +#define _NAT_PPTP_H + +/* conntrack private data */ +struct ip_nat_pptp { + u_int16_t pns_call_id; /* NAT'ed PNS call id */ + u_int16_t pac_call_id; /* NAT'ed PAC call id */ +}; + +#endif /* _NAT_PPTP_H */ diff --git a/include/linux/netfilter_ipv6/ip6_tables.h b/include/linux/netfilter_ipv6/ip6_tables.h index 58c72a52dc65..59f70b34e029 100644 --- a/include/linux/netfilter_ipv6/ip6_tables.h +++ b/include/linux/netfilter_ipv6/ip6_tables.h @@ -455,6 +455,9 @@ extern unsigned int ip6t_do_table(struct sk_buff **pskb, /* Check for an extension */ extern int ip6t_ext_hdr(u8 nexthdr); +/* find specified header and get offset to it */ +extern int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, + u8 target); #define IP6T_ALIGN(s) (((s) + (__alignof__(struct ip6t_entry)-1)) & ~(__alignof__(struct ip6t_entry)-1)) diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index c49d28eca561..b86a4b77007e 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1268,7 +1268,8 @@ #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA 0x0266 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA2 0x0267 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_IDE 0x036E -#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA 0x036F +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA 0x037E +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA2 0x037F #define PCI_DEVICE_ID_NVIDIA_NVENET_12 0x0268 #define PCI_DEVICE_ID_NVIDIA_NVENET_13 0x0269 #define PCI_DEVICE_ID_NVIDIA_MCP51_AUDIO 0x026B diff --git a/include/linux/reboot.h b/include/linux/reboot.h index 3b3266ff1a95..7ab2cdb83ef0 100644 --- a/include/linux/reboot.h +++ b/include/linux/reboot.h @@ -59,6 +59,10 @@ extern void machine_crash_shutdown(struct pt_regs *); * Architecture independent implemenations of sys_reboot commands. */ +extern void kernel_restart_prepare(char *cmd); +extern void kernel_halt_prepare(void); +extern void kernel_power_off_prepare(void); + extern void kernel_restart(char *cmd); extern void kernel_halt(void); extern void kernel_power_off(void); diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 425f58c8ea4a..a6f03e473737 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -508,5 +508,7 @@ asmlinkage long sys_keyctl(int cmd, unsigned long arg2, unsigned long arg3, asmlinkage long sys_ioprio_set(int which, int who, int ioprio); asmlinkage long sys_ioprio_get(int which, int who); +asmlinkage long sys_set_mempolicy(int mode, unsigned long __user *nmask, + unsigned long maxnode); #endif diff --git a/include/rdma/ib_mad.h b/include/rdma/ib_mad.h index 53184a38fdf6..0e293fe733b0 100644 --- a/include/rdma/ib_mad.h +++ b/include/rdma/ib_mad.h @@ -108,6 +108,13 @@ #define IB_QP1_QKEY 0x80010000 #define IB_QP_SET_QKEY 0x80000000 +enum { + IB_MGMT_MAD_DATA = 232, + IB_MGMT_RMPP_DATA = 220, + IB_MGMT_VENDOR_DATA = 216, + IB_MGMT_SA_DATA = 200 +}; + struct ib_mad_hdr { u8 base_version; u8 mgmt_class; @@ -149,20 +156,20 @@ struct ib_sa_hdr { struct ib_mad { struct ib_mad_hdr mad_hdr; - u8 data[232]; + u8 data[IB_MGMT_MAD_DATA]; }; struct ib_rmpp_mad { struct ib_mad_hdr mad_hdr; struct ib_rmpp_hdr rmpp_hdr; - u8 data[220]; + u8 data[IB_MGMT_RMPP_DATA]; }; struct ib_sa_mad { struct ib_mad_hdr mad_hdr; struct ib_rmpp_hdr rmpp_hdr; struct ib_sa_hdr sa_hdr; - u8 data[200]; + u8 data[IB_MGMT_SA_DATA]; } __attribute__ ((packed)); struct ib_vendor_mad { @@ -170,7 +177,7 @@ struct ib_vendor_mad { struct ib_rmpp_hdr rmpp_hdr; u8 reserved; u8 oui[3]; - u8 data[216]; + u8 data[IB_MGMT_VENDOR_DATA]; }; struct ib_class_port_info diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index 916144be208b..69313ba7505b 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -439,6 +439,8 @@ enum scsi_host_state { SHOST_CANCEL, SHOST_DEL, SHOST_RECOVERY, + SHOST_CANCEL_RECOVERY, + SHOST_DEL_RECOVERY, }; struct Scsi_Host { @@ -465,8 +467,6 @@ struct Scsi_Host { struct list_head eh_cmd_q; struct task_struct * ehandler; /* Error recovery thread. */ - struct semaphore * eh_wait; /* The error recovery thread waits - on this. */ struct semaphore * eh_action; /* Wait for specific actions on the host. */ unsigned int eh_active:1; /* Indicates the eh thread is awake and active if @@ -621,6 +621,13 @@ static inline struct Scsi_Host *dev_to_shost(struct device *dev) return container_of(dev, struct Scsi_Host, shost_gendev); } +static inline int scsi_host_in_recovery(struct Scsi_Host *shost) +{ + return shost->shost_state == SHOST_RECOVERY || + shost->shost_state == SHOST_CANCEL_RECOVERY || + shost->shost_state == SHOST_DEL_RECOVERY; +} + extern int scsi_queue_work(struct Scsi_Host *, struct work_struct *); extern void scsi_flush_work(struct Scsi_Host *); diff --git a/include/scsi/scsi_transport_fc.h b/include/scsi/scsi_transport_fc.h index 115db056dc6b..b0d445437372 100644 --- a/include/scsi/scsi_transport_fc.h +++ b/include/scsi/scsi_transport_fc.h @@ -103,8 +103,8 @@ enum fc_port_state { incapable of reporting */ #define FC_PORTSPEED_1GBIT 1 #define FC_PORTSPEED_2GBIT 2 -#define FC_PORTSPEED_10GBIT 4 -#define FC_PORTSPEED_4GBIT 8 +#define FC_PORTSPEED_4GBIT 4 +#define FC_PORTSPEED_10GBIT 8 #define FC_PORTSPEED_NOT_NEGOTIATED (1 << 15) /* Speed not established */ /* diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index 396c7873e804..46a5e5acff97 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -29,7 +29,7 @@ config PM_DEBUG config SOFTWARE_SUSPEND bool "Software Suspend" - depends on PM && SWAP && (X86 || ((FVR || PPC32) && !SMP)) + depends on PM && SWAP && (X86 && (!SMP || SUSPEND_SMP)) || ((FVR || PPC32) && !SMP) ---help--- Enable the possibility of suspending the machine. It doesn't need APM. diff --git a/kernel/power/disk.c b/kernel/power/disk.c index 2d8bf054d036..761956e813f5 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c @@ -17,12 +17,12 @@ #include <linux/delay.h> #include <linux/fs.h> #include <linux/mount.h> +#include <linux/pm.h> #include "power.h" extern suspend_disk_method_t pm_disk_mode; -extern struct pm_ops * pm_ops; extern int swsusp_suspend(void); extern int swsusp_write(void); @@ -49,13 +49,11 @@ dev_t swsusp_resume_device; static void power_down(suspend_disk_method_t mode) { - unsigned long flags; int error = 0; - local_irq_save(flags); switch(mode) { case PM_DISK_PLATFORM: - device_shutdown(); + kernel_power_off_prepare(); error = pm_ops->enter(PM_SUSPEND_DISK); break; case PM_DISK_SHUTDOWN: diff --git a/kernel/power/power.h b/kernel/power/power.h index cd6a3493cc0d..9c9167d910dd 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -1,7 +1,7 @@ #include <linux/suspend.h> #include <linux/utsname.h> -/* With SUSPEND_CONSOLE defined, it suspend looks *really* cool, but +/* With SUSPEND_CONSOLE defined suspend looks *really* cool, but we probably do not take enough locks for switching consoles, etc, so bad things might happen. */ diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index d967e875ee82..1cc9ff25e479 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -363,7 +363,7 @@ static void lock_swapdevices(void) } /** - * write_swap_page - Write one page to a fresh swap location. + * write_page - Write one page to a fresh swap location. * @addr: Address we're writing. * @loc: Place to store the entry we used. * @@ -863,6 +863,9 @@ static int alloc_image_pages(void) return 0; } +/* Free pages we allocated for suspend. Suspend pages are alocated + * before atomic copy, so we need to free them after resume. + */ void swsusp_free(void) { BUG_ON(PageNosave(virt_to_page(pagedir_save))); @@ -918,6 +921,7 @@ static int swsusp_alloc(void) pagedir_nosave = NULL; nr_copy_pages = calc_nr(nr_copy_pages); + nr_copy_pages_check = nr_copy_pages; pr_debug("suspend: (pages needed: %d + %d free: %d)\n", nr_copy_pages, PAGES_FOR_IO, nr_free_pages()); @@ -940,7 +944,6 @@ static int swsusp_alloc(void) return error; } - nr_copy_pages_check = nr_copy_pages; return 0; } @@ -1213,8 +1216,9 @@ static struct pbe * swsusp_pagedir_relocate(struct pbe *pblist) free_pagedir(pblist); free_eaten_memory(); pblist = NULL; - } - else + /* Is this even worth handling? It should never ever happen, and we + have just lost user's state, anyway... */ + } else printk("swsusp: Relocated %d pages\n", rel); return pblist; diff --git a/kernel/printk.c b/kernel/printk.c index a967605bc2e3..4b8f0f9230a4 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -488,6 +488,11 @@ static int __init printk_time_setup(char *str) __setup("time", printk_time_setup); +__attribute__((weak)) unsigned long long printk_clock(void) +{ + return sched_clock(); +} + /* * This is printk. It can be called from any context. We want it to work. * @@ -565,7 +570,7 @@ asmlinkage int vprintk(const char *fmt, va_list args) loglev_char = default_message_loglevel + '0'; } - t = sched_clock(); + t = printk_clock(); nanosec_rem = do_div(t, 1000000000); tlen = sprintf(tbuf, "<%c>[%5lu.%06lu] ", diff --git a/kernel/signal.c b/kernel/signal.c index b92c3c9f8b9a..5a274705ba19 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -936,34 +936,31 @@ force_sig_specific(int sig, struct task_struct *t) * as soon as they're available, so putting the signal on the shared queue * will be equivalent to sending it to one such thread. */ -#define wants_signal(sig, p, mask) \ - (!sigismember(&(p)->blocked, sig) \ - && !((p)->state & mask) \ - && !((p)->flags & PF_EXITING) \ - && (task_curr(p) || !signal_pending(p))) - +static inline int wants_signal(int sig, struct task_struct *p) +{ + if (sigismember(&p->blocked, sig)) + return 0; + if (p->flags & PF_EXITING) + return 0; + if (sig == SIGKILL) + return 1; + if (p->state & (TASK_STOPPED | TASK_TRACED)) + return 0; + return task_curr(p) || !signal_pending(p); +} static void __group_complete_signal(int sig, struct task_struct *p) { - unsigned int mask; struct task_struct *t; /* - * Don't bother traced and stopped tasks (but - * SIGKILL will punch through that). - */ - mask = TASK_STOPPED | TASK_TRACED; - if (sig == SIGKILL) - mask = 0; - - /* * Now find a thread we can wake up to take the signal off the queue. * * If the main thread wants the signal, it gets first crack. * Probably the least surprising to the average bear. */ - if (wants_signal(sig, p, mask)) + if (wants_signal(sig, p)) t = p; else if (thread_group_empty(p)) /* @@ -981,7 +978,7 @@ __group_complete_signal(int sig, struct task_struct *p) t = p->signal->curr_target = p; BUG_ON(t->tgid != p->tgid); - while (!wants_signal(sig, t, mask)) { + while (!wants_signal(sig, t)) { t = next_thread(t); if (t == p->signal->curr_target) /* diff --git a/kernel/sys.c b/kernel/sys.c index f723522e6986..2fa1ed18123c 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -361,17 +361,35 @@ out_unlock: return retval; } +/** + * emergency_restart - reboot the system + * + * Without shutting down any hardware or taking any locks + * reboot the system. This is called when we know we are in + * trouble so this is our best effort to reboot. This is + * safe to call in interrupt context. + */ void emergency_restart(void) { machine_emergency_restart(); } EXPORT_SYMBOL_GPL(emergency_restart); -void kernel_restart(char *cmd) +/** + * kernel_restart - reboot the system + * + * Shutdown everything and perform a clean reboot. + * This is not safe to call in interrupt context. + */ +void kernel_restart_prepare(char *cmd) { notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd); system_state = SYSTEM_RESTART; device_shutdown(); +} +void kernel_restart(char *cmd) +{ + kernel_restart_prepare(cmd); if (!cmd) { printk(KERN_EMERG "Restarting system.\n"); } else { @@ -382,6 +400,12 @@ void kernel_restart(char *cmd) } EXPORT_SYMBOL_GPL(kernel_restart); +/** + * kernel_kexec - reboot the system + * + * Move into place and start executing a preloaded standalone + * executable. If nothing was preloaded return an error. + */ void kernel_kexec(void) { #ifdef CONFIG_KEXEC @@ -390,9 +414,7 @@ void kernel_kexec(void) if (!image) { return; } - notifier_call_chain(&reboot_notifier_list, SYS_RESTART, NULL); - system_state = SYSTEM_RESTART; - device_shutdown(); + kernel_restart_prepare(NULL); printk(KERN_EMERG "Starting new kernel\n"); machine_shutdown(); machine_kexec(image); @@ -400,21 +422,39 @@ void kernel_kexec(void) } EXPORT_SYMBOL_GPL(kernel_kexec); -void kernel_halt(void) +/** + * kernel_halt - halt the system + * + * Shutdown everything and perform a clean system halt. + */ +void kernel_halt_prepare(void) { notifier_call_chain(&reboot_notifier_list, SYS_HALT, NULL); system_state = SYSTEM_HALT; device_shutdown(); +} +void kernel_halt(void) +{ + kernel_halt_prepare(); printk(KERN_EMERG "System halted.\n"); machine_halt(); } EXPORT_SYMBOL_GPL(kernel_halt); -void kernel_power_off(void) +/** + * kernel_power_off - power_off the system + * + * Shutdown everything and perform a clean system power_off. + */ +void kernel_power_off_prepare(void) { notifier_call_chain(&reboot_notifier_list, SYS_POWER_OFF, NULL); system_state = SYSTEM_POWER_OFF; device_shutdown(); +} +void kernel_power_off(void) +{ + kernel_power_off_prepare(); printk(KERN_EMERG "Power down.\n"); machine_power_off(); } diff --git a/mm/mmap.c b/mm/mmap.c index 8b8e05f07cdb..fa11d91242e8 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1640,7 +1640,7 @@ static void unmap_vma_list(struct mm_struct *mm, struct vm_area_struct *vma) /* * Get rid of page table information in the indicated region. * - * Called with the page table lock held. + * Called with the mm semaphore held. */ static void unmap_region(struct mm_struct *mm, struct vm_area_struct *vma, struct vm_area_struct *prev, diff --git a/mm/mprotect.c b/mm/mprotect.c index e9fbd013ad9a..57577f63b305 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -248,7 +248,8 @@ sys_mprotect(unsigned long start, size_t len, unsigned long prot) newflags = vm_flags | (vma->vm_flags & ~(VM_READ | VM_WRITE | VM_EXEC)); - if ((newflags & ~(newflags >> 4)) & 0xf) { + /* newflags >> 4 shift VM_MAY% in place of VM_% */ + if ((newflags & ~(newflags >> 4)) & (VM_READ | VM_WRITE | VM_EXEC)) { error = -EACCES; goto out; } diff --git a/mm/slab.c b/mm/slab.c index 437d3388054b..c9adfce00405 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -308,12 +308,12 @@ struct kmem_list3 __initdata initkmem_list3[NUM_INIT_LISTS]; #define SIZE_L3 (1 + MAX_NUMNODES) /* - * This function may be completely optimized away if + * This function must be completely optimized away if * a constant is passed to it. Mostly the same as * what is in linux/slab.h except it returns an * index. */ -static inline int index_of(const size_t size) +static __always_inline int index_of(const size_t size) { if (__builtin_constant_p(size)) { int i = 0; @@ -329,7 +329,8 @@ static inline int index_of(const size_t size) extern void __bad_size(void); __bad_size(); } - } + } else + BUG(); return 0; } @@ -639,7 +640,7 @@ static enum { static DEFINE_PER_CPU(struct work_struct, reap_work); -static void free_block(kmem_cache_t* cachep, void** objpp, int len); +static void free_block(kmem_cache_t* cachep, void** objpp, int len, int node); static void enable_cpucache (kmem_cache_t *cachep); static void cache_reap (void *unused); static int __node_shrink(kmem_cache_t *cachep, int node); @@ -804,7 +805,7 @@ static inline void __drain_alien_cache(kmem_cache_t *cachep, struct array_cache if (ac->avail) { spin_lock(&rl3->list_lock); - free_block(cachep, ac->entry, ac->avail); + free_block(cachep, ac->entry, ac->avail, node); ac->avail = 0; spin_unlock(&rl3->list_lock); } @@ -925,7 +926,7 @@ static int __devinit cpuup_callback(struct notifier_block *nfb, /* Free limit for this kmem_list3 */ l3->free_limit -= cachep->batchcount; if (nc) - free_block(cachep, nc->entry, nc->avail); + free_block(cachep, nc->entry, nc->avail, node); if (!cpus_empty(mask)) { spin_unlock(&l3->list_lock); @@ -934,7 +935,7 @@ static int __devinit cpuup_callback(struct notifier_block *nfb, if (l3->shared) { free_block(cachep, l3->shared->entry, - l3->shared->avail); + l3->shared->avail, node); kfree(l3->shared); l3->shared = NULL; } @@ -1882,12 +1883,13 @@ static void do_drain(void *arg) { kmem_cache_t *cachep = (kmem_cache_t*)arg; struct array_cache *ac; + int node = numa_node_id(); check_irq_off(); ac = ac_data(cachep); - spin_lock(&cachep->nodelists[numa_node_id()]->list_lock); - free_block(cachep, ac->entry, ac->avail); - spin_unlock(&cachep->nodelists[numa_node_id()]->list_lock); + spin_lock(&cachep->nodelists[node]->list_lock); + free_block(cachep, ac->entry, ac->avail, node); + spin_unlock(&cachep->nodelists[node]->list_lock); ac->avail = 0; } @@ -2608,7 +2610,7 @@ done: /* * Caller needs to acquire correct kmem_list's list_lock */ -static void free_block(kmem_cache_t *cachep, void **objpp, int nr_objects) +static void free_block(kmem_cache_t *cachep, void **objpp, int nr_objects, int node) { int i; struct kmem_list3 *l3; @@ -2617,14 +2619,12 @@ static void free_block(kmem_cache_t *cachep, void **objpp, int nr_objects) void *objp = objpp[i]; struct slab *slabp; unsigned int objnr; - int nodeid = 0; slabp = GET_PAGE_SLAB(virt_to_page(objp)); - nodeid = slabp->nodeid; - l3 = cachep->nodelists[nodeid]; + l3 = cachep->nodelists[node]; list_del(&slabp->list); objnr = (objp - slabp->s_mem) / cachep->objsize; - check_spinlock_acquired_node(cachep, nodeid); + check_spinlock_acquired_node(cachep, node); check_slabp(cachep, slabp); @@ -2664,13 +2664,14 @@ static void cache_flusharray(kmem_cache_t *cachep, struct array_cache *ac) { int batchcount; struct kmem_list3 *l3; + int node = numa_node_id(); batchcount = ac->batchcount; #if DEBUG BUG_ON(!batchcount || batchcount > ac->avail); #endif check_irq_off(); - l3 = cachep->nodelists[numa_node_id()]; + l3 = cachep->nodelists[node]; spin_lock(&l3->list_lock); if (l3->shared) { struct array_cache *shared_array = l3->shared; @@ -2686,7 +2687,7 @@ static void cache_flusharray(kmem_cache_t *cachep, struct array_cache *ac) } } - free_block(cachep, ac->entry, batchcount); + free_block(cachep, ac->entry, batchcount, node); free_done: #if STATS { @@ -2751,7 +2752,7 @@ static inline void __cache_free(kmem_cache_t *cachep, void *objp) } else { spin_lock(&(cachep->nodelists[nodeid])-> list_lock); - free_block(cachep, &objp, 1); + free_block(cachep, &objp, 1, nodeid); spin_unlock(&(cachep->nodelists[nodeid])-> list_lock); } @@ -2844,7 +2845,7 @@ void *kmem_cache_alloc_node(kmem_cache_t *cachep, unsigned int __nocast flags, i unsigned long save_flags; void *ptr; - if (nodeid == numa_node_id() || nodeid == -1) + if (nodeid == -1) return __cache_alloc(cachep, flags); if (unlikely(!cachep->nodelists[nodeid])) { @@ -3079,7 +3080,7 @@ static int alloc_kmemlist(kmem_cache_t *cachep) if ((nc = cachep->nodelists[node]->shared)) free_block(cachep, nc->entry, - nc->avail); + nc->avail, node); l3->shared = new; if (!cachep->nodelists[node]->alien) { @@ -3160,7 +3161,7 @@ static int do_tune_cpucache(kmem_cache_t *cachep, int limit, int batchcount, if (!ccold) continue; spin_lock_irq(&cachep->nodelists[cpu_to_node(i)]->list_lock); - free_block(cachep, ccold->entry, ccold->avail); + free_block(cachep, ccold->entry, ccold->avail, cpu_to_node(i)); spin_unlock_irq(&cachep->nodelists[cpu_to_node(i)]->list_lock); kfree(ccold); } @@ -3240,7 +3241,7 @@ static void drain_array_locked(kmem_cache_t *cachep, if (tofree > ac->avail) { tofree = (ac->avail+1)/2; } - free_block(cachep, ac->entry, tofree); + free_block(cachep, ac->entry, tofree, node); ac->avail -= tofree; memmove(ac->entry, &(ac->entry[tofree]), sizeof(void*)*ac->avail); diff --git a/mm/swapfile.c b/mm/swapfile.c index 0184f510aace..1dcaeda039f4 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -1381,6 +1381,7 @@ asmlinkage long sys_swapon(const char __user * specialfile, int swap_flags) error = bd_claim(bdev, sys_swapon); if (error < 0) { bdev = NULL; + error = -EINVAL; goto bad_swap; } p->old_block_size = block_size(bdev); diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 145f5cde96cf..b74864889670 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -120,7 +120,7 @@ int vlan_skb_recv(struct sk_buff *skb, struct net_device *dev, unsigned short vid; struct net_device_stats *stats; unsigned short vlan_TCI; - unsigned short proto; + __be16 proto; /* vlan_TCI = ntohs(get_unaligned(&vhdr->h_vlan_TCI)); */ vlan_TCI = ntohs(vhdr->h_vlan_TCI); diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index 069253f830c1..2d24fb400e0c 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -31,7 +31,8 @@ static inline int should_deliver(const struct net_bridge_port *p, int br_dev_queue_push_xmit(struct sk_buff *skb) { - if (skb->len > skb->dev->mtu) + /* drop mtu oversized packets except tso */ + if (skb->len > skb->dev->mtu && !skb_shinfo(skb)->tso_size) kfree_skb(skb); else { #ifdef CONFIG_BRIDGE_NETFILTER diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 1b63b4824164..50c0519cd70d 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -43,7 +43,7 @@ * 2 of the License, or (at your option) any later version. */ -#define VERSION "0.403" +#define VERSION "0.404" #include <linux/config.h> #include <asm/uaccess.h> @@ -224,7 +224,7 @@ static inline int tkey_mismatch(t_key a, int offset, t_key b) Consider a node 'n' and its parent 'tp'. If n is a leaf, every bit in its key is significant. Its presence is - necessitaded by path compression, since during a tree traversal (when + necessitated by path compression, since during a tree traversal (when searching for a leaf - unless we are doing an insertion) we will completely ignore all skipped bits we encounter. Thus we need to verify, at the end of a potentially successful search, that we have indeed been walking the @@ -836,11 +836,12 @@ static void trie_init(struct trie *t) #endif } -/* readside most use rcu_read_lock currently dump routines +/* readside must use rcu_read_lock currently dump routines via get_fa_head and dump */ -static struct leaf_info *find_leaf_info(struct hlist_head *head, int plen) +static struct leaf_info *find_leaf_info(struct leaf *l, int plen) { + struct hlist_head *head = &l->list; struct hlist_node *node; struct leaf_info *li; @@ -853,7 +854,7 @@ static struct leaf_info *find_leaf_info(struct hlist_head *head, int plen) static inline struct list_head * get_fa_head(struct leaf *l, int plen) { - struct leaf_info *li = find_leaf_info(&l->list, plen); + struct leaf_info *li = find_leaf_info(l, plen); if (!li) return NULL; @@ -1085,7 +1086,7 @@ fib_insert_node(struct trie *t, int *err, u32 key, int plen) } if (tp && tp->pos + tp->bits > 32) - printk("ERROR tp=%p pos=%d, bits=%d, key=%0x plen=%d\n", + printk(KERN_WARNING "fib_trie tp=%p pos=%d, bits=%d, key=%0x plen=%d\n", tp, tp->pos, tp->bits, key, plen); /* Rebalance the trie */ @@ -1248,7 +1249,7 @@ err: } -/* should be clalled with rcu_read_lock */ +/* should be called with rcu_read_lock */ static inline int check_leaf(struct trie *t, struct leaf *l, t_key key, int *plen, const struct flowi *flp, struct fib_result *res) @@ -1590,7 +1591,7 @@ fn_trie_delete(struct fib_table *tb, struct rtmsg *r, struct kern_rta *rta, rtmsg_fib(RTM_DELROUTE, htonl(key), fa, plen, tb->tb_id, nlhdr, req); l = fib_find_node(t, key); - li = find_leaf_info(&l->list, plen); + li = find_leaf_info(l, plen); list_del_rcu(&fa->fa_list); @@ -1714,7 +1715,6 @@ static int fn_trie_flush(struct fib_table *tb) t->revision++; - rcu_read_lock(); for (h = 0; (l = nextleaf(t, l)) != NULL; h++) { found += trie_flush_leaf(t, l); @@ -1722,7 +1722,6 @@ static int fn_trie_flush(struct fib_table *tb) trie_leaf_remove(t, ll->key); ll = l; } - rcu_read_unlock(); if (ll && hlist_empty(&ll->list)) trie_leaf_remove(t, ll->key); @@ -1833,16 +1832,7 @@ static int fn_trie_dump_fa(t_key key, int plen, struct list_head *fah, struct fi i++; continue; } - if (fa->fa_info->fib_nh == NULL) { - printk("Trie error _fib_nh=NULL in fa[%d] k=%08x plen=%d\n", i, key, plen); - i++; - continue; - } - if (fa->fa_info == NULL) { - printk("Trie error fa_info=NULL in fa[%d] k=%08x plen=%d\n", i, key, plen); - i++; - continue; - } + BUG_ON(!fa->fa_info); if (fib_dump_info(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, @@ -1965,7 +1955,7 @@ struct fib_table * __init fib_hash_init(int id) trie_main = t; if (id == RT_TABLE_LOCAL) - printk("IPv4 FIB: Using LC-trie version %s\n", VERSION); + printk(KERN_INFO "IPv4 FIB: Using LC-trie version %s\n", VERSION); return tb; } @@ -2029,7 +2019,7 @@ static struct node *fib_trie_get_first(struct fib_trie_iter *iter, iter->tnode = (struct tnode *) n; iter->trie = t; iter->index = 0; - iter->depth = 0; + iter->depth = 1; return n; } return NULL; @@ -2274,11 +2264,12 @@ static int fib_trie_seq_show(struct seq_file *seq, void *v) seq_puts(seq, "<local>:\n"); else seq_puts(seq, "<main>:\n"); - } else { - seq_indent(seq, iter->depth-1); - seq_printf(seq, " +-- %d.%d.%d.%d/%d\n", - NIPQUAD(prf), tn->pos); - } + } + seq_indent(seq, iter->depth-1); + seq_printf(seq, " +-- %d.%d.%d.%d/%d %d %d %d\n", + NIPQUAD(prf), tn->pos, tn->bits, tn->full_children, + tn->empty_children); + } else { struct leaf *l = (struct leaf *) n; int i; @@ -2287,7 +2278,7 @@ static int fib_trie_seq_show(struct seq_file *seq, void *v) seq_indent(seq, iter->depth); seq_printf(seq, " |-- %d.%d.%d.%d\n", NIPQUAD(val)); for (i = 32; i >= 0; i--) { - struct leaf_info *li = find_leaf_info(&l->list, i); + struct leaf_info *li = find_leaf_info(l, i); if (li) { struct fib_alias *fa; list_for_each_entry_rcu(fa, &li->falh, fa_list) { @@ -2383,7 +2374,7 @@ static int fib_route_seq_show(struct seq_file *seq, void *v) return 0; for (i=32; i>=0; i--) { - struct leaf_info *li = find_leaf_info(&l->list, i); + struct leaf_info *li = find_leaf_info(l, i); struct fib_alias *fa; u32 mask, prefix; diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig index e2162d270073..3cf9b451675c 100644 --- a/net/ipv4/netfilter/Kconfig +++ b/net/ipv4/netfilter/Kconfig @@ -137,6 +137,22 @@ config IP_NF_AMANDA To compile it as a module, choose M here. If unsure, say Y. +config IP_NF_PPTP + tristate 'PPTP protocol support' + help + This module adds support for PPTP (Point to Point Tunnelling + Protocol, RFC2637) conncection tracking and NAT. + + If you are running PPTP sessions over a stateful firewall or NAT + box, you may want to enable this feature. + + Please note that not all PPTP modes of operation are supported yet. + For more info, read top of the file + net/ipv4/netfilter/ip_conntrack_pptp.c + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + config IP_NF_QUEUE tristate "IP Userspace queueing via NETLINK (OBSOLETE)" help @@ -621,6 +637,12 @@ config IP_NF_NAT_AMANDA default IP_NF_NAT if IP_NF_AMANDA=y default m if IP_NF_AMANDA=m +config IP_NF_NAT_PPTP + tristate + depends on IP_NF_NAT!=n && IP_NF_PPTP!=n + default IP_NF_NAT if IP_NF_PPTP=y + default m if IP_NF_PPTP=m + # mangle + specific targets config IP_NF_MANGLE tristate "Packet mangling" diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile index 1ba0db746817..3d45d3c0283c 100644 --- a/net/ipv4/netfilter/Makefile +++ b/net/ipv4/netfilter/Makefile @@ -6,6 +6,9 @@ ip_conntrack-objs := ip_conntrack_standalone.o ip_conntrack_core.o ip_conntrack_proto_generic.o ip_conntrack_proto_tcp.o ip_conntrack_proto_udp.o ip_conntrack_proto_icmp.o iptable_nat-objs := ip_nat_standalone.o ip_nat_rule.o ip_nat_core.o ip_nat_helper.o ip_nat_proto_unknown.o ip_nat_proto_tcp.o ip_nat_proto_udp.o ip_nat_proto_icmp.o +ip_conntrack_pptp-objs := ip_conntrack_helper_pptp.o ip_conntrack_proto_gre.o +ip_nat_pptp-objs := ip_nat_helper_pptp.o ip_nat_proto_gre.o + # connection tracking obj-$(CONFIG_IP_NF_CONNTRACK) += ip_conntrack.o @@ -17,6 +20,7 @@ obj-$(CONFIG_IP_NF_CONNTRACK_NETLINK) += ip_conntrack_netlink.o obj-$(CONFIG_IP_NF_CT_PROTO_SCTP) += ip_conntrack_proto_sctp.o # connection tracking helpers +obj-$(CONFIG_IP_NF_PPTP) += ip_conntrack_pptp.o obj-$(CONFIG_IP_NF_AMANDA) += ip_conntrack_amanda.o obj-$(CONFIG_IP_NF_TFTP) += ip_conntrack_tftp.o obj-$(CONFIG_IP_NF_FTP) += ip_conntrack_ftp.o @@ -24,6 +28,7 @@ obj-$(CONFIG_IP_NF_IRC) += ip_conntrack_irc.o obj-$(CONFIG_IP_NF_NETBIOS_NS) += ip_conntrack_netbios_ns.o # NAT helpers +obj-$(CONFIG_IP_NF_NAT_PPTP) += ip_nat_pptp.o obj-$(CONFIG_IP_NF_NAT_AMANDA) += ip_nat_amanda.o obj-$(CONFIG_IP_NF_NAT_TFTP) += ip_nat_tftp.o obj-$(CONFIG_IP_NF_NAT_FTP) += ip_nat_ftp.o diff --git a/net/ipv4/netfilter/ip_conntrack_amanda.c b/net/ipv4/netfilter/ip_conntrack_amanda.c index dc20881004bc..fa3f914117ec 100644 --- a/net/ipv4/netfilter/ip_conntrack_amanda.c +++ b/net/ipv4/netfilter/ip_conntrack_amanda.c @@ -65,7 +65,7 @@ static int help(struct sk_buff **pskb, /* increase the UDP timeout of the master connection as replies from * Amanda clients to the server can be quite delayed */ - ip_ct_refresh_acct(ct, ctinfo, NULL, master_timeout * HZ); + ip_ct_refresh(ct, *pskb, master_timeout * HZ); /* No data? */ dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); diff --git a/net/ipv4/netfilter/ip_conntrack_core.c b/net/ipv4/netfilter/ip_conntrack_core.c index f8cd8e42961e..ea65dd3e517a 100644 --- a/net/ipv4/netfilter/ip_conntrack_core.c +++ b/net/ipv4/netfilter/ip_conntrack_core.c @@ -233,7 +233,7 @@ __ip_conntrack_expect_find(const struct ip_conntrack_tuple *tuple) /* Just find a expectation corresponding to a tuple. */ struct ip_conntrack_expect * -ip_conntrack_expect_find_get(const struct ip_conntrack_tuple *tuple) +ip_conntrack_expect_find(const struct ip_conntrack_tuple *tuple) { struct ip_conntrack_expect *i; @@ -1112,45 +1112,46 @@ void ip_conntrack_helper_unregister(struct ip_conntrack_helper *me) synchronize_net(); } -static inline void ct_add_counters(struct ip_conntrack *ct, - enum ip_conntrack_info ctinfo, - const struct sk_buff *skb) -{ -#ifdef CONFIG_IP_NF_CT_ACCT - if (skb) { - ct->counters[CTINFO2DIR(ctinfo)].packets++; - ct->counters[CTINFO2DIR(ctinfo)].bytes += - ntohs(skb->nh.iph->tot_len); - } -#endif -} - -/* Refresh conntrack for this many jiffies and do accounting (if skb != NULL) */ -void ip_ct_refresh_acct(struct ip_conntrack *ct, +/* Refresh conntrack for this many jiffies and do accounting if do_acct is 1 */ +void __ip_ct_refresh_acct(struct ip_conntrack *ct, enum ip_conntrack_info ctinfo, const struct sk_buff *skb, - unsigned long extra_jiffies) + unsigned long extra_jiffies, + int do_acct) { + int do_event = 0; + IP_NF_ASSERT(ct->timeout.data == (unsigned long)ct); + IP_NF_ASSERT(skb); + + write_lock_bh(&ip_conntrack_lock); /* If not in hash table, timer will not be active yet */ if (!is_confirmed(ct)) { ct->timeout.expires = extra_jiffies; - ct_add_counters(ct, ctinfo, skb); + do_event = 1; } else { - write_lock_bh(&ip_conntrack_lock); /* Need del_timer for race avoidance (may already be dying). */ if (del_timer(&ct->timeout)) { ct->timeout.expires = jiffies + extra_jiffies; add_timer(&ct->timeout); - /* FIXME: We loose some REFRESH events if this function - * is called without an skb. I'll fix this later -HW */ - if (skb) - ip_conntrack_event_cache(IPCT_REFRESH, skb); + do_event = 1; } - ct_add_counters(ct, ctinfo, skb); - write_unlock_bh(&ip_conntrack_lock); } + +#ifdef CONFIG_IP_NF_CT_ACCT + if (do_acct) { + ct->counters[CTINFO2DIR(ctinfo)].packets++; + ct->counters[CTINFO2DIR(ctinfo)].bytes += + ntohs(skb->nh.iph->tot_len); + } +#endif + + write_unlock_bh(&ip_conntrack_lock); + + /* must be unlocked when calling event cache */ + if (do_event) + ip_conntrack_event_cache(IPCT_REFRESH, skb); } #if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \ diff --git a/net/ipv4/netfilter/ip_conntrack_helper_pptp.c b/net/ipv4/netfilter/ip_conntrack_helper_pptp.c new file mode 100644 index 000000000000..926a6684643d --- /dev/null +++ b/net/ipv4/netfilter/ip_conntrack_helper_pptp.c @@ -0,0 +1,806 @@ +/* + * ip_conntrack_pptp.c - Version 3.0 + * + * Connection tracking support for PPTP (Point to Point Tunneling Protocol). + * PPTP is a a protocol for creating virtual private networks. + * It is a specification defined by Microsoft and some vendors + * working with Microsoft. PPTP is built on top of a modified + * version of the Internet Generic Routing Encapsulation Protocol. + * GRE is defined in RFC 1701 and RFC 1702. Documentation of + * PPTP can be found in RFC 2637 + * + * (C) 2000-2005 by Harald Welte <laforge@gnumonks.org> + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + * + * Limitations: + * - We blindly assume that control connections are always + * established in PNS->PAC direction. This is a violation + * of RFFC2673 + * - We can only support one single call within each session + * + * TODO: + * - testing of incoming PPTP calls + * + * Changes: + * 2002-02-05 - Version 1.3 + * - Call ip_conntrack_unexpect_related() from + * pptp_destroy_siblings() to destroy expectations in case + * CALL_DISCONNECT_NOTIFY or tcp fin packet was seen + * (Philip Craig <philipc@snapgear.com>) + * - Add Version information at module loadtime + * 2002-02-10 - Version 1.6 + * - move to C99 style initializers + * - remove second expectation if first arrives + * 2004-10-22 - Version 2.0 + * - merge Mandrake's 2.6.x port with recent 2.6.x API changes + * - fix lots of linear skb assumptions from Mandrake's port + * 2005-06-10 - Version 2.1 + * - use ip_conntrack_expect_free() instead of kfree() on the + * expect's (which are from the slab for quite some time) + * 2005-06-10 - Version 3.0 + * - port helper to post-2.6.11 API changes, + * funded by Oxcoda NetBox Blue (http://www.netboxblue.com/) + * 2005-07-30 - Version 3.1 + * - port helper to 2.6.13 API changes + * + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/netfilter.h> +#include <linux/ip.h> +#include <net/checksum.h> +#include <net/tcp.h> + +#include <linux/netfilter_ipv4/ip_conntrack.h> +#include <linux/netfilter_ipv4/ip_conntrack_core.h> +#include <linux/netfilter_ipv4/ip_conntrack_helper.h> +#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h> +#include <linux/netfilter_ipv4/ip_conntrack_pptp.h> + +#define IP_CT_PPTP_VERSION "3.1" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>"); +MODULE_DESCRIPTION("Netfilter connection tracking helper module for PPTP"); + +static DEFINE_SPINLOCK(ip_pptp_lock); + +int +(*ip_nat_pptp_hook_outbound)(struct sk_buff **pskb, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + struct PptpControlHeader *ctlh, + union pptp_ctrl_union *pptpReq); + +int +(*ip_nat_pptp_hook_inbound)(struct sk_buff **pskb, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + struct PptpControlHeader *ctlh, + union pptp_ctrl_union *pptpReq); + +int +(*ip_nat_pptp_hook_exp_gre)(struct ip_conntrack_expect *expect_orig, + struct ip_conntrack_expect *expect_reply); + +void +(*ip_nat_pptp_hook_expectfn)(struct ip_conntrack *ct, + struct ip_conntrack_expect *exp); + +#if 0 +/* PptpControlMessageType names */ +const char *pptp_msg_name[] = { + "UNKNOWN_MESSAGE", + "START_SESSION_REQUEST", + "START_SESSION_REPLY", + "STOP_SESSION_REQUEST", + "STOP_SESSION_REPLY", + "ECHO_REQUEST", + "ECHO_REPLY", + "OUT_CALL_REQUEST", + "OUT_CALL_REPLY", + "IN_CALL_REQUEST", + "IN_CALL_REPLY", + "IN_CALL_CONNECT", + "CALL_CLEAR_REQUEST", + "CALL_DISCONNECT_NOTIFY", + "WAN_ERROR_NOTIFY", + "SET_LINK_INFO" +}; +EXPORT_SYMBOL(pptp_msg_name); +#define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s: " format, __FILE__, __FUNCTION__, ## args) +#else +#define DEBUGP(format, args...) +#endif + +#define SECS *HZ +#define MINS * 60 SECS +#define HOURS * 60 MINS + +#define PPTP_GRE_TIMEOUT (10 MINS) +#define PPTP_GRE_STREAM_TIMEOUT (5 HOURS) + +static void pptp_expectfn(struct ip_conntrack *ct, + struct ip_conntrack_expect *exp) +{ + DEBUGP("increasing timeouts\n"); + + /* increase timeout of GRE data channel conntrack entry */ + ct->proto.gre.timeout = PPTP_GRE_TIMEOUT; + ct->proto.gre.stream_timeout = PPTP_GRE_STREAM_TIMEOUT; + + /* Can you see how rusty this code is, compared with the pre-2.6.11 + * one? That's what happened to my shiny newnat of 2002 ;( -HW */ + + if (!ip_nat_pptp_hook_expectfn) { + struct ip_conntrack_tuple inv_t; + struct ip_conntrack_expect *exp_other; + + /* obviously this tuple inversion only works until you do NAT */ + invert_tuplepr(&inv_t, &exp->tuple); + DEBUGP("trying to unexpect other dir: "); + DUMP_TUPLE(&inv_t); + + exp_other = ip_conntrack_expect_find(&inv_t); + if (exp_other) { + /* delete other expectation. */ + DEBUGP("found\n"); + ip_conntrack_unexpect_related(exp_other); + ip_conntrack_expect_put(exp_other); + } else { + DEBUGP("not found\n"); + } + } else { + /* we need more than simple inversion */ + ip_nat_pptp_hook_expectfn(ct, exp); + } +} + +static int destroy_sibling_or_exp(const struct ip_conntrack_tuple *t) +{ + struct ip_conntrack_tuple_hash *h; + struct ip_conntrack_expect *exp; + + DEBUGP("trying to timeout ct or exp for tuple "); + DUMP_TUPLE(t); + + h = ip_conntrack_find_get(t, NULL); + if (h) { + struct ip_conntrack *sibling = tuplehash_to_ctrack(h); + DEBUGP("setting timeout of conntrack %p to 0\n", sibling); + sibling->proto.gre.timeout = 0; + sibling->proto.gre.stream_timeout = 0; + if (del_timer(&sibling->timeout)) + sibling->timeout.function((unsigned long)sibling); + ip_conntrack_put(sibling); + return 1; + } else { + exp = ip_conntrack_expect_find(t); + if (exp) { + DEBUGP("unexpect_related of expect %p\n", exp); + ip_conntrack_unexpect_related(exp); + ip_conntrack_expect_put(exp); + return 1; + } + } + + return 0; +} + + +/* timeout GRE data connections */ +static void pptp_destroy_siblings(struct ip_conntrack *ct) +{ + struct ip_conntrack_tuple t; + + /* Since ct->sibling_list has literally rusted away in 2.6.11, + * we now need another way to find out about our sibling + * contrack and expects... -HW */ + + /* try original (pns->pac) tuple */ + memcpy(&t, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, sizeof(t)); + t.dst.protonum = IPPROTO_GRE; + t.src.u.gre.key = htons(ct->help.ct_pptp_info.pns_call_id); + t.dst.u.gre.key = htons(ct->help.ct_pptp_info.pac_call_id); + + if (!destroy_sibling_or_exp(&t)) + DEBUGP("failed to timeout original pns->pac ct/exp\n"); + + /* try reply (pac->pns) tuple */ + memcpy(&t, &ct->tuplehash[IP_CT_DIR_REPLY].tuple, sizeof(t)); + t.dst.protonum = IPPROTO_GRE; + t.src.u.gre.key = htons(ct->help.ct_pptp_info.pac_call_id); + t.dst.u.gre.key = htons(ct->help.ct_pptp_info.pns_call_id); + + if (!destroy_sibling_or_exp(&t)) + DEBUGP("failed to timeout reply pac->pns ct/exp\n"); +} + +/* expect GRE connections (PNS->PAC and PAC->PNS direction) */ +static inline int +exp_gre(struct ip_conntrack *master, + u_int32_t seq, + __be16 callid, + __be16 peer_callid) +{ + struct ip_conntrack_tuple inv_tuple; + struct ip_conntrack_tuple exp_tuples[] = { + /* tuple in original direction, PNS->PAC */ + { .src = { .ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip, + .u = { .gre = { .key = peer_callid } } + }, + .dst = { .ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip, + .u = { .gre = { .key = callid } }, + .protonum = IPPROTO_GRE + }, + }, + /* tuple in reply direction, PAC->PNS */ + { .src = { .ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip, + .u = { .gre = { .key = callid } } + }, + .dst = { .ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip, + .u = { .gre = { .key = peer_callid } }, + .protonum = IPPROTO_GRE + }, + } + }; + struct ip_conntrack_expect *exp_orig, *exp_reply; + int ret = 1; + + exp_orig = ip_conntrack_expect_alloc(master); + if (exp_orig == NULL) + goto out; + + exp_reply = ip_conntrack_expect_alloc(master); + if (exp_reply == NULL) + goto out_put_orig; + + memcpy(&exp_orig->tuple, &exp_tuples[0], sizeof(exp_orig->tuple)); + + exp_orig->mask.src.ip = 0xffffffff; + exp_orig->mask.src.u.all = 0; + exp_orig->mask.dst.u.all = 0; + exp_orig->mask.dst.u.gre.key = htons(0xffff); + exp_orig->mask.dst.ip = 0xffffffff; + exp_orig->mask.dst.protonum = 0xff; + + exp_orig->master = master; + exp_orig->expectfn = pptp_expectfn; + exp_orig->flags = 0; + + exp_orig->dir = IP_CT_DIR_ORIGINAL; + + /* both expectations are identical apart from tuple */ + memcpy(exp_reply, exp_orig, sizeof(*exp_reply)); + memcpy(&exp_reply->tuple, &exp_tuples[1], sizeof(exp_reply->tuple)); + + exp_reply->dir = !exp_orig->dir; + + if (ip_nat_pptp_hook_exp_gre) + ret = ip_nat_pptp_hook_exp_gre(exp_orig, exp_reply); + else { + + DEBUGP("calling expect_related PNS->PAC"); + DUMP_TUPLE(&exp_orig->tuple); + + if (ip_conntrack_expect_related(exp_orig) != 0) { + DEBUGP("cannot expect_related()\n"); + goto out_put_both; + } + + DEBUGP("calling expect_related PAC->PNS"); + DUMP_TUPLE(&exp_reply->tuple); + + if (ip_conntrack_expect_related(exp_reply) != 0) { + DEBUGP("cannot expect_related()\n"); + goto out_unexpect_orig; + } + + /* Add GRE keymap entries */ + if (ip_ct_gre_keymap_add(master, &exp_reply->tuple, 0) != 0) { + DEBUGP("cannot keymap_add() exp\n"); + goto out_unexpect_both; + } + + invert_tuplepr(&inv_tuple, &exp_reply->tuple); + if (ip_ct_gre_keymap_add(master, &inv_tuple, 1) != 0) { + ip_ct_gre_keymap_destroy(master); + DEBUGP("cannot keymap_add() exp_inv\n"); + goto out_unexpect_both; + } + ret = 0; + } + +out_put_both: + ip_conntrack_expect_put(exp_reply); +out_put_orig: + ip_conntrack_expect_put(exp_orig); +out: + return ret; + +out_unexpect_both: + ip_conntrack_unexpect_related(exp_reply); +out_unexpect_orig: + ip_conntrack_unexpect_related(exp_orig); + goto out_put_both; +} + +static inline int +pptp_inbound_pkt(struct sk_buff **pskb, + struct tcphdr *tcph, + unsigned int nexthdr_off, + unsigned int datalen, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo) +{ + struct PptpControlHeader _ctlh, *ctlh; + unsigned int reqlen; + union pptp_ctrl_union _pptpReq, *pptpReq; + struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info; + u_int16_t msg; + __be16 *cid, *pcid; + u_int32_t seq; + + ctlh = skb_header_pointer(*pskb, nexthdr_off, sizeof(_ctlh), &_ctlh); + if (!ctlh) { + DEBUGP("error during skb_header_pointer\n"); + return NF_ACCEPT; + } + nexthdr_off += sizeof(_ctlh); + datalen -= sizeof(_ctlh); + + reqlen = datalen; + if (reqlen > sizeof(*pptpReq)) + reqlen = sizeof(*pptpReq); + pptpReq = skb_header_pointer(*pskb, nexthdr_off, reqlen, &_pptpReq); + if (!pptpReq) { + DEBUGP("error during skb_header_pointer\n"); + return NF_ACCEPT; + } + + msg = ntohs(ctlh->messageType); + DEBUGP("inbound control message %s\n", pptp_msg_name[msg]); + + switch (msg) { + case PPTP_START_SESSION_REPLY: + if (reqlen < sizeof(_pptpReq.srep)) { + DEBUGP("%s: short packet\n", pptp_msg_name[msg]); + break; + } + + /* server confirms new control session */ + if (info->sstate < PPTP_SESSION_REQUESTED) { + DEBUGP("%s without START_SESS_REQUEST\n", + pptp_msg_name[msg]); + break; + } + if (pptpReq->srep.resultCode == PPTP_START_OK) + info->sstate = PPTP_SESSION_CONFIRMED; + else + info->sstate = PPTP_SESSION_ERROR; + break; + + case PPTP_STOP_SESSION_REPLY: + if (reqlen < sizeof(_pptpReq.strep)) { + DEBUGP("%s: short packet\n", pptp_msg_name[msg]); + break; + } + + /* server confirms end of control session */ + if (info->sstate > PPTP_SESSION_STOPREQ) { + DEBUGP("%s without STOP_SESS_REQUEST\n", + pptp_msg_name[msg]); + break; + } + if (pptpReq->strep.resultCode == PPTP_STOP_OK) + info->sstate = PPTP_SESSION_NONE; + else + info->sstate = PPTP_SESSION_ERROR; + break; + + case PPTP_OUT_CALL_REPLY: + if (reqlen < sizeof(_pptpReq.ocack)) { + DEBUGP("%s: short packet\n", pptp_msg_name[msg]); + break; + } + + /* server accepted call, we now expect GRE frames */ + if (info->sstate != PPTP_SESSION_CONFIRMED) { + DEBUGP("%s but no session\n", pptp_msg_name[msg]); + break; + } + if (info->cstate != PPTP_CALL_OUT_REQ && + info->cstate != PPTP_CALL_OUT_CONF) { + DEBUGP("%s without OUTCALL_REQ\n", pptp_msg_name[msg]); + break; + } + if (pptpReq->ocack.resultCode != PPTP_OUTCALL_CONNECT) { + info->cstate = PPTP_CALL_NONE; + break; + } + + cid = &pptpReq->ocack.callID; + pcid = &pptpReq->ocack.peersCallID; + + info->pac_call_id = ntohs(*cid); + + if (htons(info->pns_call_id) != *pcid) { + DEBUGP("%s for unknown callid %u\n", + pptp_msg_name[msg], ntohs(*pcid)); + break; + } + + DEBUGP("%s, CID=%X, PCID=%X\n", pptp_msg_name[msg], + ntohs(*cid), ntohs(*pcid)); + + info->cstate = PPTP_CALL_OUT_CONF; + + seq = ntohl(tcph->seq) + sizeof(struct pptp_pkt_hdr) + + sizeof(struct PptpControlHeader) + + ((void *)pcid - (void *)pptpReq); + + if (exp_gre(ct, seq, *cid, *pcid) != 0) + printk("ip_conntrack_pptp: error during exp_gre\n"); + break; + + case PPTP_IN_CALL_REQUEST: + if (reqlen < sizeof(_pptpReq.icack)) { + DEBUGP("%s: short packet\n", pptp_msg_name[msg]); + break; + } + + /* server tells us about incoming call request */ + if (info->sstate != PPTP_SESSION_CONFIRMED) { + DEBUGP("%s but no session\n", pptp_msg_name[msg]); + break; + } + pcid = &pptpReq->icack.peersCallID; + DEBUGP("%s, PCID=%X\n", pptp_msg_name[msg], ntohs(*pcid)); + info->cstate = PPTP_CALL_IN_REQ; + info->pac_call_id = ntohs(*pcid); + break; + + case PPTP_IN_CALL_CONNECT: + if (reqlen < sizeof(_pptpReq.iccon)) { + DEBUGP("%s: short packet\n", pptp_msg_name[msg]); + break; + } + + /* server tells us about incoming call established */ + if (info->sstate != PPTP_SESSION_CONFIRMED) { + DEBUGP("%s but no session\n", pptp_msg_name[msg]); + break; + } + if (info->sstate != PPTP_CALL_IN_REP + && info->sstate != PPTP_CALL_IN_CONF) { + DEBUGP("%s but never sent IN_CALL_REPLY\n", + pptp_msg_name[msg]); + break; + } + + pcid = &pptpReq->iccon.peersCallID; + cid = &info->pac_call_id; + + if (info->pns_call_id != ntohs(*pcid)) { + DEBUGP("%s for unknown CallID %u\n", + pptp_msg_name[msg], ntohs(*pcid)); + break; + } + + DEBUGP("%s, PCID=%X\n", pptp_msg_name[msg], ntohs(*pcid)); + info->cstate = PPTP_CALL_IN_CONF; + + /* we expect a GRE connection from PAC to PNS */ + seq = ntohl(tcph->seq) + sizeof(struct pptp_pkt_hdr) + + sizeof(struct PptpControlHeader) + + ((void *)pcid - (void *)pptpReq); + + if (exp_gre(ct, seq, *cid, *pcid) != 0) + printk("ip_conntrack_pptp: error during exp_gre\n"); + + break; + + case PPTP_CALL_DISCONNECT_NOTIFY: + if (reqlen < sizeof(_pptpReq.disc)) { + DEBUGP("%s: short packet\n", pptp_msg_name[msg]); + break; + } + + /* server confirms disconnect */ + cid = &pptpReq->disc.callID; + DEBUGP("%s, CID=%X\n", pptp_msg_name[msg], ntohs(*cid)); + info->cstate = PPTP_CALL_NONE; + + /* untrack this call id, unexpect GRE packets */ + pptp_destroy_siblings(ct); + break; + + case PPTP_WAN_ERROR_NOTIFY: + break; + + case PPTP_ECHO_REQUEST: + case PPTP_ECHO_REPLY: + /* I don't have to explain these ;) */ + break; + default: + DEBUGP("invalid %s (TY=%d)\n", (msg <= PPTP_MSG_MAX) + ? pptp_msg_name[msg]:pptp_msg_name[0], msg); + break; + } + + + if (ip_nat_pptp_hook_inbound) + return ip_nat_pptp_hook_inbound(pskb, ct, ctinfo, ctlh, + pptpReq); + + return NF_ACCEPT; + +} + +static inline int +pptp_outbound_pkt(struct sk_buff **pskb, + struct tcphdr *tcph, + unsigned int nexthdr_off, + unsigned int datalen, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo) +{ + struct PptpControlHeader _ctlh, *ctlh; + unsigned int reqlen; + union pptp_ctrl_union _pptpReq, *pptpReq; + struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info; + u_int16_t msg; + __be16 *cid, *pcid; + + ctlh = skb_header_pointer(*pskb, nexthdr_off, sizeof(_ctlh), &_ctlh); + if (!ctlh) + return NF_ACCEPT; + nexthdr_off += sizeof(_ctlh); + datalen -= sizeof(_ctlh); + + reqlen = datalen; + if (reqlen > sizeof(*pptpReq)) + reqlen = sizeof(*pptpReq); + pptpReq = skb_header_pointer(*pskb, nexthdr_off, reqlen, &_pptpReq); + if (!pptpReq) + return NF_ACCEPT; + + msg = ntohs(ctlh->messageType); + DEBUGP("outbound control message %s\n", pptp_msg_name[msg]); + + switch (msg) { + case PPTP_START_SESSION_REQUEST: + /* client requests for new control session */ + if (info->sstate != PPTP_SESSION_NONE) { + DEBUGP("%s but we already have one", + pptp_msg_name[msg]); + } + info->sstate = PPTP_SESSION_REQUESTED; + break; + case PPTP_STOP_SESSION_REQUEST: + /* client requests end of control session */ + info->sstate = PPTP_SESSION_STOPREQ; + break; + + case PPTP_OUT_CALL_REQUEST: + if (reqlen < sizeof(_pptpReq.ocreq)) { + DEBUGP("%s: short packet\n", pptp_msg_name[msg]); + /* FIXME: break; */ + } + + /* client initiating connection to server */ + if (info->sstate != PPTP_SESSION_CONFIRMED) { + DEBUGP("%s but no session\n", + pptp_msg_name[msg]); + break; + } + info->cstate = PPTP_CALL_OUT_REQ; + /* track PNS call id */ + cid = &pptpReq->ocreq.callID; + DEBUGP("%s, CID=%X\n", pptp_msg_name[msg], ntohs(*cid)); + info->pns_call_id = ntohs(*cid); + break; + case PPTP_IN_CALL_REPLY: + if (reqlen < sizeof(_pptpReq.icack)) { + DEBUGP("%s: short packet\n", pptp_msg_name[msg]); + break; + } + + /* client answers incoming call */ + if (info->cstate != PPTP_CALL_IN_REQ + && info->cstate != PPTP_CALL_IN_REP) { + DEBUGP("%s without incall_req\n", + pptp_msg_name[msg]); + break; + } + if (pptpReq->icack.resultCode != PPTP_INCALL_ACCEPT) { + info->cstate = PPTP_CALL_NONE; + break; + } + pcid = &pptpReq->icack.peersCallID; + if (info->pac_call_id != ntohs(*pcid)) { + DEBUGP("%s for unknown call %u\n", + pptp_msg_name[msg], ntohs(*pcid)); + break; + } + DEBUGP("%s, CID=%X\n", pptp_msg_name[msg], ntohs(*pcid)); + /* part two of the three-way handshake */ + info->cstate = PPTP_CALL_IN_REP; + info->pns_call_id = ntohs(pptpReq->icack.callID); + break; + + case PPTP_CALL_CLEAR_REQUEST: + /* client requests hangup of call */ + if (info->sstate != PPTP_SESSION_CONFIRMED) { + DEBUGP("CLEAR_CALL but no session\n"); + break; + } + /* FUTURE: iterate over all calls and check if + * call ID is valid. We don't do this without newnat, + * because we only know about last call */ + info->cstate = PPTP_CALL_CLEAR_REQ; + break; + case PPTP_SET_LINK_INFO: + break; + case PPTP_ECHO_REQUEST: + case PPTP_ECHO_REPLY: + /* I don't have to explain these ;) */ + break; + default: + DEBUGP("invalid %s (TY=%d)\n", (msg <= PPTP_MSG_MAX)? + pptp_msg_name[msg]:pptp_msg_name[0], msg); + /* unknown: no need to create GRE masq table entry */ + break; + } + + if (ip_nat_pptp_hook_outbound) + return ip_nat_pptp_hook_outbound(pskb, ct, ctinfo, ctlh, + pptpReq); + + return NF_ACCEPT; +} + + +/* track caller id inside control connection, call expect_related */ +static int +conntrack_pptp_help(struct sk_buff **pskb, + struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) + +{ + struct pptp_pkt_hdr _pptph, *pptph; + struct tcphdr _tcph, *tcph; + u_int32_t tcplen = (*pskb)->len - (*pskb)->nh.iph->ihl * 4; + u_int32_t datalen; + int dir = CTINFO2DIR(ctinfo); + struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info; + unsigned int nexthdr_off; + + int oldsstate, oldcstate; + int ret; + + /* don't do any tracking before tcp handshake complete */ + if (ctinfo != IP_CT_ESTABLISHED + && ctinfo != IP_CT_ESTABLISHED+IP_CT_IS_REPLY) { + DEBUGP("ctinfo = %u, skipping\n", ctinfo); + return NF_ACCEPT; + } + + nexthdr_off = (*pskb)->nh.iph->ihl*4; + tcph = skb_header_pointer(*pskb, nexthdr_off, sizeof(_tcph), &_tcph); + BUG_ON(!tcph); + nexthdr_off += tcph->doff * 4; + datalen = tcplen - tcph->doff * 4; + + if (tcph->fin || tcph->rst) { + DEBUGP("RST/FIN received, timeouting GRE\n"); + /* can't do this after real newnat */ + info->cstate = PPTP_CALL_NONE; + + /* untrack this call id, unexpect GRE packets */ + pptp_destroy_siblings(ct); + } + + pptph = skb_header_pointer(*pskb, nexthdr_off, sizeof(_pptph), &_pptph); + if (!pptph) { + DEBUGP("no full PPTP header, can't track\n"); + return NF_ACCEPT; + } + nexthdr_off += sizeof(_pptph); + datalen -= sizeof(_pptph); + + /* if it's not a control message we can't do anything with it */ + if (ntohs(pptph->packetType) != PPTP_PACKET_CONTROL || + ntohl(pptph->magicCookie) != PPTP_MAGIC_COOKIE) { + DEBUGP("not a control packet\n"); + return NF_ACCEPT; + } + + oldsstate = info->sstate; + oldcstate = info->cstate; + + spin_lock_bh(&ip_pptp_lock); + + /* FIXME: We just blindly assume that the control connection is always + * established from PNS->PAC. However, RFC makes no guarantee */ + if (dir == IP_CT_DIR_ORIGINAL) + /* client -> server (PNS -> PAC) */ + ret = pptp_outbound_pkt(pskb, tcph, nexthdr_off, datalen, ct, + ctinfo); + else + /* server -> client (PAC -> PNS) */ + ret = pptp_inbound_pkt(pskb, tcph, nexthdr_off, datalen, ct, + ctinfo); + DEBUGP("sstate: %d->%d, cstate: %d->%d\n", + oldsstate, info->sstate, oldcstate, info->cstate); + spin_unlock_bh(&ip_pptp_lock); + + return ret; +} + +/* control protocol helper */ +static struct ip_conntrack_helper pptp = { + .list = { NULL, NULL }, + .name = "pptp", + .me = THIS_MODULE, + .max_expected = 2, + .timeout = 5 * 60, + .tuple = { .src = { .ip = 0, + .u = { .tcp = { .port = + __constant_htons(PPTP_CONTROL_PORT) } } + }, + .dst = { .ip = 0, + .u = { .all = 0 }, + .protonum = IPPROTO_TCP + } + }, + .mask = { .src = { .ip = 0, + .u = { .tcp = { .port = __constant_htons(0xffff) } } + }, + .dst = { .ip = 0, + .u = { .all = 0 }, + .protonum = 0xff + } + }, + .help = conntrack_pptp_help +}; + +extern void __exit ip_ct_proto_gre_fini(void); +extern int __init ip_ct_proto_gre_init(void); + +/* ip_conntrack_pptp initialization */ +static int __init init(void) +{ + int retcode; + + retcode = ip_ct_proto_gre_init(); + if (retcode < 0) + return retcode; + + DEBUGP(" registering helper\n"); + if ((retcode = ip_conntrack_helper_register(&pptp))) { + printk(KERN_ERR "Unable to register conntrack application " + "helper for pptp: %d\n", retcode); + ip_ct_proto_gre_fini(); + return retcode; + } + + printk("ip_conntrack_pptp version %s loaded\n", IP_CT_PPTP_VERSION); + return 0; +} + +static void __exit fini(void) +{ + ip_conntrack_helper_unregister(&pptp); + ip_ct_proto_gre_fini(); + printk("ip_conntrack_pptp version %s unloaded\n", IP_CT_PPTP_VERSION); +} + +module_init(init); +module_exit(fini); + +EXPORT_SYMBOL(ip_nat_pptp_hook_outbound); +EXPORT_SYMBOL(ip_nat_pptp_hook_inbound); +EXPORT_SYMBOL(ip_nat_pptp_hook_exp_gre); +EXPORT_SYMBOL(ip_nat_pptp_hook_expectfn); diff --git a/net/ipv4/netfilter/ip_conntrack_netbios_ns.c b/net/ipv4/netfilter/ip_conntrack_netbios_ns.c index 71ef19d126d0..577bac22dcc6 100644 --- a/net/ipv4/netfilter/ip_conntrack_netbios_ns.c +++ b/net/ipv4/netfilter/ip_conntrack_netbios_ns.c @@ -91,7 +91,7 @@ static int help(struct sk_buff **pskb, ip_conntrack_expect_related(exp); ip_conntrack_expect_put(exp); - ip_ct_refresh_acct(ct, ctinfo, NULL, timeout * HZ); + ip_ct_refresh(ct, *pskb, timeout * HZ); out: return NF_ACCEPT; } diff --git a/net/ipv4/netfilter/ip_conntrack_netlink.c b/net/ipv4/netfilter/ip_conntrack_netlink.c index 15aef3564742..b08a432efcf8 100644 --- a/net/ipv4/netfilter/ip_conntrack_netlink.c +++ b/net/ipv4/netfilter/ip_conntrack_netlink.c @@ -1270,7 +1270,7 @@ ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb, if (err < 0) return err; - exp = ip_conntrack_expect_find_get(&tuple); + exp = ip_conntrack_expect_find(&tuple); if (!exp) return -ENOENT; @@ -1318,7 +1318,7 @@ ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb, return err; /* bump usage count to 2 */ - exp = ip_conntrack_expect_find_get(&tuple); + exp = ip_conntrack_expect_find(&tuple); if (!exp) return -ENOENT; diff --git a/net/ipv4/netfilter/ip_conntrack_proto_gre.c b/net/ipv4/netfilter/ip_conntrack_proto_gre.c new file mode 100644 index 000000000000..de3cb9db6f85 --- /dev/null +++ b/net/ipv4/netfilter/ip_conntrack_proto_gre.c @@ -0,0 +1,327 @@ +/* + * ip_conntrack_proto_gre.c - Version 3.0 + * + * Connection tracking protocol helper module for GRE. + * + * GRE is a generic encapsulation protocol, which is generally not very + * suited for NAT, as it has no protocol-specific part as port numbers. + * + * It has an optional key field, which may help us distinguishing two + * connections between the same two hosts. + * + * GRE is defined in RFC 1701 and RFC 1702, as well as RFC 2784 + * + * PPTP is built on top of a modified version of GRE, and has a mandatory + * field called "CallID", which serves us for the same purpose as the key + * field in plain GRE. + * + * Documentation about PPTP can be found in RFC 2637 + * + * (C) 2000-2005 by Harald Welte <laforge@gnumonks.org> + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + * + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/timer.h> +#include <linux/netfilter.h> +#include <linux/ip.h> +#include <linux/in.h> +#include <linux/list.h> + +static DEFINE_RWLOCK(ip_ct_gre_lock); +#define ASSERT_READ_LOCK(x) +#define ASSERT_WRITE_LOCK(x) + +#include <linux/netfilter_ipv4/listhelp.h> +#include <linux/netfilter_ipv4/ip_conntrack_protocol.h> +#include <linux/netfilter_ipv4/ip_conntrack_helper.h> +#include <linux/netfilter_ipv4/ip_conntrack_core.h> + +#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h> +#include <linux/netfilter_ipv4/ip_conntrack_pptp.h> + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>"); +MODULE_DESCRIPTION("netfilter connection tracking protocol helper for GRE"); + +/* shamelessly stolen from ip_conntrack_proto_udp.c */ +#define GRE_TIMEOUT (30*HZ) +#define GRE_STREAM_TIMEOUT (180*HZ) + +#if 0 +#define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s: " format, __FILE__, __FUNCTION__, ## args) +#define DUMP_TUPLE_GRE(x) printk("%u.%u.%u.%u:0x%x -> %u.%u.%u.%u:0x%x\n", \ + NIPQUAD((x)->src.ip), ntohs((x)->src.u.gre.key), \ + NIPQUAD((x)->dst.ip), ntohs((x)->dst.u.gre.key)) +#else +#define DEBUGP(x, args...) +#define DUMP_TUPLE_GRE(x) +#endif + +/* GRE KEYMAP HANDLING FUNCTIONS */ +static LIST_HEAD(gre_keymap_list); + +static inline int gre_key_cmpfn(const struct ip_ct_gre_keymap *km, + const struct ip_conntrack_tuple *t) +{ + return ((km->tuple.src.ip == t->src.ip) && + (km->tuple.dst.ip == t->dst.ip) && + (km->tuple.dst.protonum == t->dst.protonum) && + (km->tuple.dst.u.all == t->dst.u.all)); +} + +/* look up the source key for a given tuple */ +static u_int32_t gre_keymap_lookup(struct ip_conntrack_tuple *t) +{ + struct ip_ct_gre_keymap *km; + u_int32_t key = 0; + + read_lock_bh(&ip_ct_gre_lock); + km = LIST_FIND(&gre_keymap_list, gre_key_cmpfn, + struct ip_ct_gre_keymap *, t); + if (km) + key = km->tuple.src.u.gre.key; + read_unlock_bh(&ip_ct_gre_lock); + + DEBUGP("lookup src key 0x%x up key for ", key); + DUMP_TUPLE_GRE(t); + + return key; +} + +/* add a single keymap entry, associate with specified master ct */ +int +ip_ct_gre_keymap_add(struct ip_conntrack *ct, + struct ip_conntrack_tuple *t, int reply) +{ + struct ip_ct_gre_keymap **exist_km, *km, *old; + + if (!ct->helper || strcmp(ct->helper->name, "pptp")) { + DEBUGP("refusing to add GRE keymap to non-pptp session\n"); + return -1; + } + + if (!reply) + exist_km = &ct->help.ct_pptp_info.keymap_orig; + else + exist_km = &ct->help.ct_pptp_info.keymap_reply; + + if (*exist_km) { + /* check whether it's a retransmission */ + old = LIST_FIND(&gre_keymap_list, gre_key_cmpfn, + struct ip_ct_gre_keymap *, t); + if (old == *exist_km) { + DEBUGP("retransmission\n"); + return 0; + } + + DEBUGP("trying to override keymap_%s for ct %p\n", + reply? "reply":"orig", ct); + return -EEXIST; + } + + km = kmalloc(sizeof(*km), GFP_ATOMIC); + if (!km) + return -ENOMEM; + + memcpy(&km->tuple, t, sizeof(*t)); + *exist_km = km; + + DEBUGP("adding new entry %p: ", km); + DUMP_TUPLE_GRE(&km->tuple); + + write_lock_bh(&ip_ct_gre_lock); + list_append(&gre_keymap_list, km); + write_unlock_bh(&ip_ct_gre_lock); + + return 0; +} + +/* destroy the keymap entries associated with specified master ct */ +void ip_ct_gre_keymap_destroy(struct ip_conntrack *ct) +{ + DEBUGP("entering for ct %p\n", ct); + + if (!ct->helper || strcmp(ct->helper->name, "pptp")) { + DEBUGP("refusing to destroy GRE keymap to non-pptp session\n"); + return; + } + + write_lock_bh(&ip_ct_gre_lock); + if (ct->help.ct_pptp_info.keymap_orig) { + DEBUGP("removing %p from list\n", + ct->help.ct_pptp_info.keymap_orig); + list_del(&ct->help.ct_pptp_info.keymap_orig->list); + kfree(ct->help.ct_pptp_info.keymap_orig); + ct->help.ct_pptp_info.keymap_orig = NULL; + } + if (ct->help.ct_pptp_info.keymap_reply) { + DEBUGP("removing %p from list\n", + ct->help.ct_pptp_info.keymap_reply); + list_del(&ct->help.ct_pptp_info.keymap_reply->list); + kfree(ct->help.ct_pptp_info.keymap_reply); + ct->help.ct_pptp_info.keymap_reply = NULL; + } + write_unlock_bh(&ip_ct_gre_lock); +} + + +/* PUBLIC CONNTRACK PROTO HELPER FUNCTIONS */ + +/* invert gre part of tuple */ +static int gre_invert_tuple(struct ip_conntrack_tuple *tuple, + const struct ip_conntrack_tuple *orig) +{ + tuple->dst.u.gre.key = orig->src.u.gre.key; + tuple->src.u.gre.key = orig->dst.u.gre.key; + + return 1; +} + +/* gre hdr info to tuple */ +static int gre_pkt_to_tuple(const struct sk_buff *skb, + unsigned int dataoff, + struct ip_conntrack_tuple *tuple) +{ + struct gre_hdr_pptp _pgrehdr, *pgrehdr; + u_int32_t srckey; + struct gre_hdr _grehdr, *grehdr; + + /* first only delinearize old RFC1701 GRE header */ + grehdr = skb_header_pointer(skb, dataoff, sizeof(_grehdr), &_grehdr); + if (!grehdr || grehdr->version != GRE_VERSION_PPTP) { + /* try to behave like "ip_conntrack_proto_generic" */ + tuple->src.u.all = 0; + tuple->dst.u.all = 0; + return 1; + } + + /* PPTP header is variable length, only need up to the call_id field */ + pgrehdr = skb_header_pointer(skb, dataoff, 8, &_pgrehdr); + if (!pgrehdr) + return 1; + + if (ntohs(grehdr->protocol) != GRE_PROTOCOL_PPTP) { + DEBUGP("GRE_VERSION_PPTP but unknown proto\n"); + return 0; + } + + tuple->dst.u.gre.key = pgrehdr->call_id; + srckey = gre_keymap_lookup(tuple); + tuple->src.u.gre.key = srckey; + + return 1; +} + +/* print gre part of tuple */ +static int gre_print_tuple(struct seq_file *s, + const struct ip_conntrack_tuple *tuple) +{ + return seq_printf(s, "srckey=0x%x dstkey=0x%x ", + ntohs(tuple->src.u.gre.key), + ntohs(tuple->dst.u.gre.key)); +} + +/* print private data for conntrack */ +static int gre_print_conntrack(struct seq_file *s, + const struct ip_conntrack *ct) +{ + return seq_printf(s, "timeout=%u, stream_timeout=%u ", + (ct->proto.gre.timeout / HZ), + (ct->proto.gre.stream_timeout / HZ)); +} + +/* Returns verdict for packet, and may modify conntrack */ +static int gre_packet(struct ip_conntrack *ct, + const struct sk_buff *skb, + enum ip_conntrack_info conntrackinfo) +{ + /* If we've seen traffic both ways, this is a GRE connection. + * Extend timeout. */ + if (ct->status & IPS_SEEN_REPLY) { + ip_ct_refresh_acct(ct, conntrackinfo, skb, + ct->proto.gre.stream_timeout); + /* Also, more likely to be important, and not a probe. */ + set_bit(IPS_ASSURED_BIT, &ct->status); + } else + ip_ct_refresh_acct(ct, conntrackinfo, skb, + ct->proto.gre.timeout); + + return NF_ACCEPT; +} + +/* Called when a new connection for this protocol found. */ +static int gre_new(struct ip_conntrack *ct, + const struct sk_buff *skb) +{ + DEBUGP(": "); + DUMP_TUPLE_GRE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + + /* initialize to sane value. Ideally a conntrack helper + * (e.g. in case of pptp) is increasing them */ + ct->proto.gre.stream_timeout = GRE_STREAM_TIMEOUT; + ct->proto.gre.timeout = GRE_TIMEOUT; + + return 1; +} + +/* Called when a conntrack entry has already been removed from the hashes + * and is about to be deleted from memory */ +static void gre_destroy(struct ip_conntrack *ct) +{ + struct ip_conntrack *master = ct->master; + DEBUGP(" entering\n"); + + if (!master) + DEBUGP("no master !?!\n"); + else + ip_ct_gre_keymap_destroy(master); +} + +/* protocol helper struct */ +static struct ip_conntrack_protocol gre = { + .proto = IPPROTO_GRE, + .name = "gre", + .pkt_to_tuple = gre_pkt_to_tuple, + .invert_tuple = gre_invert_tuple, + .print_tuple = gre_print_tuple, + .print_conntrack = gre_print_conntrack, + .packet = gre_packet, + .new = gre_new, + .destroy = gre_destroy, + .me = THIS_MODULE, +#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \ + defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE) + .tuple_to_nfattr = ip_ct_port_tuple_to_nfattr, + .nfattr_to_tuple = ip_ct_port_nfattr_to_tuple, +#endif +}; + +/* ip_conntrack_proto_gre initialization */ +int __init ip_ct_proto_gre_init(void) +{ + return ip_conntrack_protocol_register(&gre); +} + +void __exit ip_ct_proto_gre_fini(void) +{ + struct list_head *pos, *n; + + /* delete all keymap entries */ + write_lock_bh(&ip_ct_gre_lock); + list_for_each_safe(pos, n, &gre_keymap_list) { + DEBUGP("deleting keymap %p at module unload time\n", pos); + list_del(pos); + kfree(pos); + } + write_unlock_bh(&ip_ct_gre_lock); + + ip_conntrack_protocol_unregister(&gre); +} + +EXPORT_SYMBOL(ip_ct_gre_keymap_add); +EXPORT_SYMBOL(ip_ct_gre_keymap_destroy); diff --git a/net/ipv4/netfilter/ip_conntrack_standalone.c b/net/ipv4/netfilter/ip_conntrack_standalone.c index ae3e3e655db5..dd476b191f4b 100644 --- a/net/ipv4/netfilter/ip_conntrack_standalone.c +++ b/net/ipv4/netfilter/ip_conntrack_standalone.c @@ -989,15 +989,15 @@ EXPORT_SYMBOL(need_ip_conntrack); EXPORT_SYMBOL(ip_conntrack_helper_register); EXPORT_SYMBOL(ip_conntrack_helper_unregister); EXPORT_SYMBOL(ip_ct_iterate_cleanup); -EXPORT_SYMBOL(ip_ct_refresh_acct); +EXPORT_SYMBOL(__ip_ct_refresh_acct); EXPORT_SYMBOL(ip_conntrack_expect_alloc); EXPORT_SYMBOL(ip_conntrack_expect_put); -EXPORT_SYMBOL_GPL(ip_conntrack_expect_find_get); +EXPORT_SYMBOL_GPL(__ip_conntrack_expect_find); +EXPORT_SYMBOL_GPL(ip_conntrack_expect_find); EXPORT_SYMBOL(ip_conntrack_expect_related); EXPORT_SYMBOL(ip_conntrack_unexpect_related); EXPORT_SYMBOL_GPL(ip_conntrack_expect_list); -EXPORT_SYMBOL_GPL(__ip_conntrack_expect_find); EXPORT_SYMBOL_GPL(ip_ct_unlink_expect); EXPORT_SYMBOL(ip_conntrack_tuple_taken); diff --git a/net/ipv4/netfilter/ip_nat_core.c b/net/ipv4/netfilter/ip_nat_core.c index 1adedb743f60..c3ea891d38e7 100644 --- a/net/ipv4/netfilter/ip_nat_core.c +++ b/net/ipv4/netfilter/ip_nat_core.c @@ -578,6 +578,8 @@ ip_nat_port_nfattr_to_range(struct nfattr *tb[], struct ip_nat_range *range) return ret; } +EXPORT_SYMBOL_GPL(ip_nat_port_nfattr_to_range); +EXPORT_SYMBOL_GPL(ip_nat_port_range_to_nfattr); #endif int __init ip_nat_init(void) diff --git a/net/ipv4/netfilter/ip_nat_helper_pptp.c b/net/ipv4/netfilter/ip_nat_helper_pptp.c new file mode 100644 index 000000000000..3cdd0684d30d --- /dev/null +++ b/net/ipv4/netfilter/ip_nat_helper_pptp.c @@ -0,0 +1,401 @@ +/* + * ip_nat_pptp.c - Version 3.0 + * + * NAT support for PPTP (Point to Point Tunneling Protocol). + * PPTP is a a protocol for creating virtual private networks. + * It is a specification defined by Microsoft and some vendors + * working with Microsoft. PPTP is built on top of a modified + * version of the Internet Generic Routing Encapsulation Protocol. + * GRE is defined in RFC 1701 and RFC 1702. Documentation of + * PPTP can be found in RFC 2637 + * + * (C) 2000-2005 by Harald Welte <laforge@gnumonks.org> + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + * + * TODO: - NAT to a unique tuple, not to TCP source port + * (needs netfilter tuple reservation) + * + * Changes: + * 2002-02-10 - Version 1.3 + * - Use ip_nat_mangle_tcp_packet() because of cloned skb's + * in local connections (Philip Craig <philipc@snapgear.com>) + * - add checks for magicCookie and pptp version + * - make argument list of pptp_{out,in}bound_packet() shorter + * - move to C99 style initializers + * - print version number at module loadtime + * 2003-09-22 - Version 1.5 + * - use SNATed tcp sourceport as callid, since we get called before + * TCP header is mangled (Philip Craig <philipc@snapgear.com>) + * 2004-10-22 - Version 2.0 + * - kernel 2.6.x version + * 2005-06-10 - Version 3.0 + * - kernel >= 2.6.11 version, + * funded by Oxcoda NetBox Blue (http://www.netboxblue.com/) + * + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/ip.h> +#include <linux/tcp.h> +#include <net/tcp.h> + +#include <linux/netfilter_ipv4/ip_nat.h> +#include <linux/netfilter_ipv4/ip_nat_rule.h> +#include <linux/netfilter_ipv4/ip_nat_helper.h> +#include <linux/netfilter_ipv4/ip_nat_pptp.h> +#include <linux/netfilter_ipv4/ip_conntrack_core.h> +#include <linux/netfilter_ipv4/ip_conntrack_helper.h> +#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h> +#include <linux/netfilter_ipv4/ip_conntrack_pptp.h> + +#define IP_NAT_PPTP_VERSION "3.0" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>"); +MODULE_DESCRIPTION("Netfilter NAT helper module for PPTP"); + + +#if 0 +extern const char *pptp_msg_name[]; +#define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s: " format, __FILE__, \ + __FUNCTION__, ## args) +#else +#define DEBUGP(format, args...) +#endif + +static void pptp_nat_expected(struct ip_conntrack *ct, + struct ip_conntrack_expect *exp) +{ + struct ip_conntrack *master = ct->master; + struct ip_conntrack_expect *other_exp; + struct ip_conntrack_tuple t; + struct ip_ct_pptp_master *ct_pptp_info; + struct ip_nat_pptp *nat_pptp_info; + + ct_pptp_info = &master->help.ct_pptp_info; + nat_pptp_info = &master->nat.help.nat_pptp_info; + + /* And here goes the grand finale of corrosion... */ + + if (exp->dir == IP_CT_DIR_ORIGINAL) { + DEBUGP("we are PNS->PAC\n"); + /* therefore, build tuple for PAC->PNS */ + t.src.ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; + t.src.u.gre.key = htons(master->help.ct_pptp_info.pac_call_id); + t.dst.ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; + t.dst.u.gre.key = htons(master->help.ct_pptp_info.pns_call_id); + t.dst.protonum = IPPROTO_GRE; + } else { + DEBUGP("we are PAC->PNS\n"); + /* build tuple for PNS->PAC */ + t.src.ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; + t.src.u.gre.key = + htons(master->nat.help.nat_pptp_info.pns_call_id); + t.dst.ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; + t.dst.u.gre.key = + htons(master->nat.help.nat_pptp_info.pac_call_id); + t.dst.protonum = IPPROTO_GRE; + } + + DEBUGP("trying to unexpect other dir: "); + DUMP_TUPLE(&t); + other_exp = ip_conntrack_expect_find(&t); + if (other_exp) { + ip_conntrack_unexpect_related(other_exp); + ip_conntrack_expect_put(other_exp); + DEBUGP("success\n"); + } else { + DEBUGP("not found!\n"); + } + + ip_nat_follow_master(ct, exp); +} + +/* outbound packets == from PNS to PAC */ +static int +pptp_outbound_pkt(struct sk_buff **pskb, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + struct PptpControlHeader *ctlh, + union pptp_ctrl_union *pptpReq) + +{ + struct ip_ct_pptp_master *ct_pptp_info = &ct->help.ct_pptp_info; + struct ip_nat_pptp *nat_pptp_info = &ct->nat.help.nat_pptp_info; + + u_int16_t msg, *cid = NULL, new_callid; + + new_callid = htons(ct_pptp_info->pns_call_id); + + switch (msg = ntohs(ctlh->messageType)) { + case PPTP_OUT_CALL_REQUEST: + cid = &pptpReq->ocreq.callID; + /* FIXME: ideally we would want to reserve a call ID + * here. current netfilter NAT core is not able to do + * this :( For now we use TCP source port. This breaks + * multiple calls within one control session */ + + /* save original call ID in nat_info */ + nat_pptp_info->pns_call_id = ct_pptp_info->pns_call_id; + + /* don't use tcph->source since we are at a DSTmanip + * hook (e.g. PREROUTING) and pkt is not mangled yet */ + new_callid = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.tcp.port; + + /* save new call ID in ct info */ + ct_pptp_info->pns_call_id = ntohs(new_callid); + break; + case PPTP_IN_CALL_REPLY: + cid = &pptpReq->icreq.callID; + break; + case PPTP_CALL_CLEAR_REQUEST: + cid = &pptpReq->clrreq.callID; + break; + default: + DEBUGP("unknown outbound packet 0x%04x:%s\n", msg, + (msg <= PPTP_MSG_MAX)? + pptp_msg_name[msg]:pptp_msg_name[0]); + /* fall through */ + + case PPTP_SET_LINK_INFO: + /* only need to NAT in case PAC is behind NAT box */ + case PPTP_START_SESSION_REQUEST: + case PPTP_START_SESSION_REPLY: + case PPTP_STOP_SESSION_REQUEST: + case PPTP_STOP_SESSION_REPLY: + case PPTP_ECHO_REQUEST: + case PPTP_ECHO_REPLY: + /* no need to alter packet */ + return NF_ACCEPT; + } + + /* only OUT_CALL_REQUEST, IN_CALL_REPLY, CALL_CLEAR_REQUEST pass + * down to here */ + + IP_NF_ASSERT(cid); + + DEBUGP("altering call id from 0x%04x to 0x%04x\n", + ntohs(*cid), ntohs(new_callid)); + + /* mangle packet */ + if (ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, + (void *)cid - ((void *)ctlh - sizeof(struct pptp_pkt_hdr)), + sizeof(new_callid), + (char *)&new_callid, + sizeof(new_callid)) == 0) + return NF_DROP; + + return NF_ACCEPT; +} + +static int +pptp_exp_gre(struct ip_conntrack_expect *expect_orig, + struct ip_conntrack_expect *expect_reply) +{ + struct ip_ct_pptp_master *ct_pptp_info = + &expect_orig->master->help.ct_pptp_info; + struct ip_nat_pptp *nat_pptp_info = + &expect_orig->master->nat.help.nat_pptp_info; + + struct ip_conntrack *ct = expect_orig->master; + + struct ip_conntrack_tuple inv_t; + struct ip_conntrack_tuple *orig_t, *reply_t; + + /* save original PAC call ID in nat_info */ + nat_pptp_info->pac_call_id = ct_pptp_info->pac_call_id; + + /* alter expectation */ + orig_t = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; + reply_t = &ct->tuplehash[IP_CT_DIR_REPLY].tuple; + + /* alter expectation for PNS->PAC direction */ + invert_tuplepr(&inv_t, &expect_orig->tuple); + expect_orig->saved_proto.gre.key = htons(nat_pptp_info->pac_call_id); + expect_orig->tuple.src.u.gre.key = htons(nat_pptp_info->pns_call_id); + expect_orig->tuple.dst.u.gre.key = htons(ct_pptp_info->pac_call_id); + inv_t.src.ip = reply_t->src.ip; + inv_t.dst.ip = reply_t->dst.ip; + inv_t.src.u.gre.key = htons(nat_pptp_info->pac_call_id); + inv_t.dst.u.gre.key = htons(ct_pptp_info->pns_call_id); + + if (!ip_conntrack_expect_related(expect_orig)) { + DEBUGP("successfully registered expect\n"); + } else { + DEBUGP("can't expect_related(expect_orig)\n"); + return 1; + } + + /* alter expectation for PAC->PNS direction */ + invert_tuplepr(&inv_t, &expect_reply->tuple); + expect_reply->saved_proto.gre.key = htons(nat_pptp_info->pns_call_id); + expect_reply->tuple.src.u.gre.key = htons(nat_pptp_info->pac_call_id); + expect_reply->tuple.dst.u.gre.key = htons(ct_pptp_info->pns_call_id); + inv_t.src.ip = orig_t->src.ip; + inv_t.dst.ip = orig_t->dst.ip; + inv_t.src.u.gre.key = htons(nat_pptp_info->pns_call_id); + inv_t.dst.u.gre.key = htons(ct_pptp_info->pac_call_id); + + if (!ip_conntrack_expect_related(expect_reply)) { + DEBUGP("successfully registered expect\n"); + } else { + DEBUGP("can't expect_related(expect_reply)\n"); + ip_conntrack_unexpect_related(expect_orig); + return 1; + } + + if (ip_ct_gre_keymap_add(ct, &expect_reply->tuple, 0) < 0) { + DEBUGP("can't register original keymap\n"); + ip_conntrack_unexpect_related(expect_orig); + ip_conntrack_unexpect_related(expect_reply); + return 1; + } + + if (ip_ct_gre_keymap_add(ct, &inv_t, 1) < 0) { + DEBUGP("can't register reply keymap\n"); + ip_conntrack_unexpect_related(expect_orig); + ip_conntrack_unexpect_related(expect_reply); + ip_ct_gre_keymap_destroy(ct); + return 1; + } + + return 0; +} + +/* inbound packets == from PAC to PNS */ +static int +pptp_inbound_pkt(struct sk_buff **pskb, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + struct PptpControlHeader *ctlh, + union pptp_ctrl_union *pptpReq) +{ + struct ip_nat_pptp *nat_pptp_info = &ct->nat.help.nat_pptp_info; + u_int16_t msg, new_cid = 0, new_pcid, *pcid = NULL, *cid = NULL; + + int ret = NF_ACCEPT, rv; + + new_pcid = htons(nat_pptp_info->pns_call_id); + + switch (msg = ntohs(ctlh->messageType)) { + case PPTP_OUT_CALL_REPLY: + pcid = &pptpReq->ocack.peersCallID; + cid = &pptpReq->ocack.callID; + break; + case PPTP_IN_CALL_CONNECT: + pcid = &pptpReq->iccon.peersCallID; + break; + case PPTP_IN_CALL_REQUEST: + /* only need to nat in case PAC is behind NAT box */ + break; + case PPTP_WAN_ERROR_NOTIFY: + pcid = &pptpReq->wanerr.peersCallID; + break; + case PPTP_CALL_DISCONNECT_NOTIFY: + pcid = &pptpReq->disc.callID; + break; + case PPTP_SET_LINK_INFO: + pcid = &pptpReq->setlink.peersCallID; + break; + + default: + DEBUGP("unknown inbound packet %s\n", (msg <= PPTP_MSG_MAX)? + pptp_msg_name[msg]:pptp_msg_name[0]); + /* fall through */ + + case PPTP_START_SESSION_REQUEST: + case PPTP_START_SESSION_REPLY: + case PPTP_STOP_SESSION_REQUEST: + case PPTP_STOP_SESSION_REPLY: + case PPTP_ECHO_REQUEST: + case PPTP_ECHO_REPLY: + /* no need to alter packet */ + return NF_ACCEPT; + } + + /* only OUT_CALL_REPLY, IN_CALL_CONNECT, IN_CALL_REQUEST, + * WAN_ERROR_NOTIFY, CALL_DISCONNECT_NOTIFY pass down here */ + + /* mangle packet */ + IP_NF_ASSERT(pcid); + DEBUGP("altering peer call id from 0x%04x to 0x%04x\n", + ntohs(*pcid), ntohs(new_pcid)); + + rv = ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, + (void *)pcid - ((void *)ctlh - sizeof(struct pptp_pkt_hdr)), + sizeof(new_pcid), (char *)&new_pcid, + sizeof(new_pcid)); + if (rv != NF_ACCEPT) + return rv; + + if (new_cid) { + IP_NF_ASSERT(cid); + DEBUGP("altering call id from 0x%04x to 0x%04x\n", + ntohs(*cid), ntohs(new_cid)); + rv = ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, + (void *)cid - ((void *)ctlh - sizeof(struct pptp_pkt_hdr)), + sizeof(new_cid), + (char *)&new_cid, + sizeof(new_cid)); + if (rv != NF_ACCEPT) + return rv; + } + + /* check for earlier return value of 'switch' above */ + if (ret != NF_ACCEPT) + return ret; + + /* great, at least we don't need to resize packets */ + return NF_ACCEPT; +} + + +extern int __init ip_nat_proto_gre_init(void); +extern void __exit ip_nat_proto_gre_fini(void); + +static int __init init(void) +{ + int ret; + + DEBUGP("%s: registering NAT helper\n", __FILE__); + + ret = ip_nat_proto_gre_init(); + if (ret < 0) + return ret; + + BUG_ON(ip_nat_pptp_hook_outbound); + ip_nat_pptp_hook_outbound = &pptp_outbound_pkt; + + BUG_ON(ip_nat_pptp_hook_inbound); + ip_nat_pptp_hook_inbound = &pptp_inbound_pkt; + + BUG_ON(ip_nat_pptp_hook_exp_gre); + ip_nat_pptp_hook_exp_gre = &pptp_exp_gre; + + BUG_ON(ip_nat_pptp_hook_expectfn); + ip_nat_pptp_hook_expectfn = &pptp_nat_expected; + + printk("ip_nat_pptp version %s loaded\n", IP_NAT_PPTP_VERSION); + return 0; +} + +static void __exit fini(void) +{ + DEBUGP("cleanup_module\n" ); + + ip_nat_pptp_hook_expectfn = NULL; + ip_nat_pptp_hook_exp_gre = NULL; + ip_nat_pptp_hook_inbound = NULL; + ip_nat_pptp_hook_outbound = NULL; + + ip_nat_proto_gre_fini(); + /* Make sure noone calls it, meanwhile */ + synchronize_net(); + + printk("ip_nat_pptp version %s unloaded\n", IP_NAT_PPTP_VERSION); +} + +module_init(init); +module_exit(fini); diff --git a/net/ipv4/netfilter/ip_nat_proto_gre.c b/net/ipv4/netfilter/ip_nat_proto_gre.c new file mode 100644 index 000000000000..7c1285401672 --- /dev/null +++ b/net/ipv4/netfilter/ip_nat_proto_gre.c @@ -0,0 +1,214 @@ +/* + * ip_nat_proto_gre.c - Version 2.0 + * + * NAT protocol helper module for GRE. + * + * GRE is a generic encapsulation protocol, which is generally not very + * suited for NAT, as it has no protocol-specific part as port numbers. + * + * It has an optional key field, which may help us distinguishing two + * connections between the same two hosts. + * + * GRE is defined in RFC 1701 and RFC 1702, as well as RFC 2784 + * + * PPTP is built on top of a modified version of GRE, and has a mandatory + * field called "CallID", which serves us for the same purpose as the key + * field in plain GRE. + * + * Documentation about PPTP can be found in RFC 2637 + * + * (C) 2000-2005 by Harald Welte <laforge@gnumonks.org> + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + * + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/ip.h> +#include <linux/netfilter_ipv4/ip_nat.h> +#include <linux/netfilter_ipv4/ip_nat_rule.h> +#include <linux/netfilter_ipv4/ip_nat_protocol.h> +#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h> + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>"); +MODULE_DESCRIPTION("Netfilter NAT protocol helper module for GRE"); + +#if 0 +#define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s: " format, __FILE__, \ + __FUNCTION__, ## args) +#else +#define DEBUGP(x, args...) +#endif + +/* is key in given range between min and max */ +static int +gre_in_range(const struct ip_conntrack_tuple *tuple, + enum ip_nat_manip_type maniptype, + const union ip_conntrack_manip_proto *min, + const union ip_conntrack_manip_proto *max) +{ + u_int32_t key; + + if (maniptype == IP_NAT_MANIP_SRC) + key = tuple->src.u.gre.key; + else + key = tuple->dst.u.gre.key; + + return ntohl(key) >= ntohl(min->gre.key) + && ntohl(key) <= ntohl(max->gre.key); +} + +/* generate unique tuple ... */ +static int +gre_unique_tuple(struct ip_conntrack_tuple *tuple, + const struct ip_nat_range *range, + enum ip_nat_manip_type maniptype, + const struct ip_conntrack *conntrack) +{ + static u_int16_t key; + u_int16_t *keyptr; + unsigned int min, i, range_size; + + if (maniptype == IP_NAT_MANIP_SRC) + keyptr = &tuple->src.u.gre.key; + else + keyptr = &tuple->dst.u.gre.key; + + if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED)) { + DEBUGP("%p: NATing GRE PPTP\n", conntrack); + min = 1; + range_size = 0xffff; + } else { + min = ntohl(range->min.gre.key); + range_size = ntohl(range->max.gre.key) - min + 1; + } + + DEBUGP("min = %u, range_size = %u\n", min, range_size); + + for (i = 0; i < range_size; i++, key++) { + *keyptr = htonl(min + key % range_size); + if (!ip_nat_used_tuple(tuple, conntrack)) + return 1; + } + + DEBUGP("%p: no NAT mapping\n", conntrack); + + return 0; +} + +/* manipulate a GRE packet according to maniptype */ +static int +gre_manip_pkt(struct sk_buff **pskb, + unsigned int iphdroff, + const struct ip_conntrack_tuple *tuple, + enum ip_nat_manip_type maniptype) +{ + struct gre_hdr *greh; + struct gre_hdr_pptp *pgreh; + struct iphdr *iph = (struct iphdr *)((*pskb)->data + iphdroff); + unsigned int hdroff = iphdroff + iph->ihl*4; + + /* pgreh includes two optional 32bit fields which are not required + * to be there. That's where the magic '8' comes from */ + if (!skb_make_writable(pskb, hdroff + sizeof(*pgreh)-8)) + return 0; + + greh = (void *)(*pskb)->data + hdroff; + pgreh = (struct gre_hdr_pptp *) greh; + + /* we only have destination manip of a packet, since 'source key' + * is not present in the packet itself */ + if (maniptype == IP_NAT_MANIP_DST) { + /* key manipulation is always dest */ + switch (greh->version) { + case 0: + if (!greh->key) { + DEBUGP("can't nat GRE w/o key\n"); + break; + } + if (greh->csum) { + /* FIXME: Never tested this code... */ + *(gre_csum(greh)) = + ip_nat_cheat_check(~*(gre_key(greh)), + tuple->dst.u.gre.key, + *(gre_csum(greh))); + } + *(gre_key(greh)) = tuple->dst.u.gre.key; + break; + case GRE_VERSION_PPTP: + DEBUGP("call_id -> 0x%04x\n", + ntohl(tuple->dst.u.gre.key)); + pgreh->call_id = htons(ntohl(tuple->dst.u.gre.key)); + break; + default: + DEBUGP("can't nat unknown GRE version\n"); + return 0; + break; + } + } + return 1; +} + +/* print out a nat tuple */ +static unsigned int +gre_print(char *buffer, + const struct ip_conntrack_tuple *match, + const struct ip_conntrack_tuple *mask) +{ + unsigned int len = 0; + + if (mask->src.u.gre.key) + len += sprintf(buffer + len, "srckey=0x%x ", + ntohl(match->src.u.gre.key)); + + if (mask->dst.u.gre.key) + len += sprintf(buffer + len, "dstkey=0x%x ", + ntohl(match->src.u.gre.key)); + + return len; +} + +/* print a range of keys */ +static unsigned int +gre_print_range(char *buffer, const struct ip_nat_range *range) +{ + if (range->min.gre.key != 0 + || range->max.gre.key != 0xFFFF) { + if (range->min.gre.key == range->max.gre.key) + return sprintf(buffer, "key 0x%x ", + ntohl(range->min.gre.key)); + else + return sprintf(buffer, "keys 0x%u-0x%u ", + ntohl(range->min.gre.key), + ntohl(range->max.gre.key)); + } else + return 0; +} + +/* nat helper struct */ +static struct ip_nat_protocol gre = { + .name = "GRE", + .protonum = IPPROTO_GRE, + .manip_pkt = gre_manip_pkt, + .in_range = gre_in_range, + .unique_tuple = gre_unique_tuple, + .print = gre_print, + .print_range = gre_print_range, +#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \ + defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE) + .range_to_nfattr = ip_nat_port_range_to_nfattr, + .nfattr_to_range = ip_nat_port_nfattr_to_range, +#endif +}; + +int __init ip_nat_proto_gre_init(void) +{ + return ip_nat_protocol_register(&gre); +} + +void __exit ip_nat_proto_gre_fini(void) +{ + ip_nat_protocol_unregister(&gre); +} diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 304bb0a1d4f0..4b0d7e4d6269 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -361,7 +361,7 @@ static void raw_probe_proto_opt(struct flowi *fl, struct msghdr *msg) if (type && code) { get_user(fl->fl_icmp_type, type); - __get_user(fl->fl_icmp_code, code); + get_user(fl->fl_icmp_code, code); probed = 1; } break; diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index a88db28b0af7..b1a63b2c6b4a 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -384,7 +384,7 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, newtp->frto_counter = 0; newtp->frto_highmark = 0; - newicsk->icsk_ca_ops = &tcp_reno; + newicsk->icsk_ca_ops = &tcp_init_congestion_ops; tcp_set_ca_state(newsk, TCP_CA_Open); tcp_init_xmit_timers(newsk); diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index b018e31b6530..d6e3d269e906 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -461,9 +461,7 @@ int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len, unsigned int mss flags = TCP_SKB_CB(skb)->flags; TCP_SKB_CB(skb)->flags = flags & ~(TCPCB_FLAG_FIN|TCPCB_FLAG_PSH); TCP_SKB_CB(buff)->flags = flags; - TCP_SKB_CB(buff)->sacked = - (TCP_SKB_CB(skb)->sacked & - (TCPCB_LOST | TCPCB_EVER_RETRANS | TCPCB_AT_TAIL)); + TCP_SKB_CB(buff)->sacked = TCP_SKB_CB(skb)->sacked; TCP_SKB_CB(skb)->sacked &= ~TCPCB_AT_TAIL; if (!skb_shinfo(skb)->nr_frags && skb->ip_summed != CHECKSUM_HW) { @@ -501,11 +499,26 @@ int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len, unsigned int mss tcp_skb_pcount(buff); tp->packets_out -= diff; + + if (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_ACKED) + tp->sacked_out -= diff; + if (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_RETRANS) + tp->retrans_out -= diff; + if (TCP_SKB_CB(skb)->sacked & TCPCB_LOST) { tp->lost_out -= diff; tp->left_out -= diff; } + if (diff > 0) { + /* Adjust Reno SACK estimate. */ + if (!tp->rx_opt.sack_ok) { + tp->sacked_out -= diff; + if ((int)tp->sacked_out < 0) + tp->sacked_out = 0; + tcp_sync_left_out(tp); + } + tp->fackets_out -= diff; if ((int)tp->fackets_out < 0) tp->fackets_out = 0; diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 1cb8adb2787f..2da514b16d95 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -1955,6 +1955,57 @@ static void __exit fini(void) #endif } +/* + * find specified header up to transport protocol header. + * If found target header, the offset to the header is set to *offset + * and return 0. otherwise, return -1. + * + * Notes: - non-1st Fragment Header isn't skipped. + * - ESP header isn't skipped. + * - The target header may be trancated. + */ +int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, u8 target) +{ + unsigned int start = (u8*)(skb->nh.ipv6h + 1) - skb->data; + u8 nexthdr = skb->nh.ipv6h->nexthdr; + unsigned int len = skb->len - start; + + while (nexthdr != target) { + struct ipv6_opt_hdr _hdr, *hp; + unsigned int hdrlen; + + if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) + return -1; + hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr); + if (hp == NULL) + return -1; + if (nexthdr == NEXTHDR_FRAGMENT) { + unsigned short _frag_off, *fp; + fp = skb_header_pointer(skb, + start+offsetof(struct frag_hdr, + frag_off), + sizeof(_frag_off), + &_frag_off); + if (fp == NULL) + return -1; + + if (ntohs(*fp) & ~0x7) + return -1; + hdrlen = 8; + } else if (nexthdr == NEXTHDR_AUTH) + hdrlen = (hp->hdrlen + 2) << 2; + else + hdrlen = ipv6_optlen(hp); + + nexthdr = hp->nexthdr; + len -= hdrlen; + start += hdrlen; + } + + *offset = start; + return 0; +} + EXPORT_SYMBOL(ip6t_register_table); EXPORT_SYMBOL(ip6t_unregister_table); EXPORT_SYMBOL(ip6t_do_table); @@ -1963,6 +2014,7 @@ EXPORT_SYMBOL(ip6t_unregister_match); EXPORT_SYMBOL(ip6t_register_target); EXPORT_SYMBOL(ip6t_unregister_target); EXPORT_SYMBOL(ip6t_ext_hdr); +EXPORT_SYMBOL(ipv6_find_hdr); module_init(init); module_exit(fini); diff --git a/net/ipv6/netfilter/ip6t_ah.c b/net/ipv6/netfilter/ip6t_ah.c index d5b94f142bba..dde37793d20b 100644 --- a/net/ipv6/netfilter/ip6t_ah.c +++ b/net/ipv6/netfilter/ip6t_ah.c @@ -48,92 +48,21 @@ match(const struct sk_buff *skb, unsigned int protoff, int *hotdrop) { - struct ip_auth_hdr *ah = NULL, _ah; + struct ip_auth_hdr *ah, _ah; const struct ip6t_ah *ahinfo = matchinfo; - unsigned int temp; - int len; - u8 nexthdr; unsigned int ptr; unsigned int hdrlen = 0; - /*DEBUGP("IPv6 AH entered\n");*/ - /* if (opt->auth == 0) return 0; - * It does not filled on output */ - - /* type of the 1st exthdr */ - nexthdr = skb->nh.ipv6h->nexthdr; - /* pointer to the 1st exthdr */ - ptr = sizeof(struct ipv6hdr); - /* available length */ - len = skb->len - ptr; - temp = 0; - - while (ip6t_ext_hdr(nexthdr)) { - struct ipv6_opt_hdr _hdr, *hp; - - DEBUGP("ipv6_ah header iteration \n"); - - /* Is there enough space for the next ext header? */ - if (len < sizeof(struct ipv6_opt_hdr)) - return 0; - /* No more exthdr -> evaluate */ - if (nexthdr == NEXTHDR_NONE) - break; - /* ESP -> evaluate */ - if (nexthdr == NEXTHDR_ESP) - break; - - hp = skb_header_pointer(skb, ptr, sizeof(_hdr), &_hdr); - BUG_ON(hp == NULL); - - /* Calculate the header length */ - if (nexthdr == NEXTHDR_FRAGMENT) - hdrlen = 8; - else if (nexthdr == NEXTHDR_AUTH) - hdrlen = (hp->hdrlen+2)<<2; - else - hdrlen = ipv6_optlen(hp); - - /* AH -> evaluate */ - if (nexthdr == NEXTHDR_AUTH) { - temp |= MASK_AH; - break; - } - - - /* set the flag */ - switch (nexthdr) { - case NEXTHDR_HOP: - case NEXTHDR_ROUTING: - case NEXTHDR_FRAGMENT: - case NEXTHDR_AUTH: - case NEXTHDR_DEST: - break; - default: - DEBUGP("ipv6_ah match: unknown nextheader %u\n",nexthdr); - return 0; - } - - nexthdr = hp->nexthdr; - len -= hdrlen; - ptr += hdrlen; - if (ptr > skb->len) { - DEBUGP("ipv6_ah: new pointer too large! \n"); - break; - } - } - - /* AH header not found */ - if (temp != MASK_AH) + if (ipv6_find_hdr(skb, &ptr, NEXTHDR_AUTH) < 0) return 0; - if (len < sizeof(struct ip_auth_hdr)){ + ah = skb_header_pointer(skb, ptr, sizeof(_ah), &_ah); + if (ah == NULL) { *hotdrop = 1; return 0; } - ah = skb_header_pointer(skb, ptr, sizeof(_ah), &_ah); - BUG_ON(ah == NULL); + hdrlen = (ah->hdrlen + 2) << 2; DEBUGP("IPv6 AH LEN %u %u ", hdrlen, ah->hdrlen); DEBUGP("RES %04X ", ah->reserved); diff --git a/net/ipv6/netfilter/ip6t_dst.c b/net/ipv6/netfilter/ip6t_dst.c index 540925e4a7a8..c450a635e54b 100644 --- a/net/ipv6/netfilter/ip6t_dst.c +++ b/net/ipv6/netfilter/ip6t_dst.c @@ -63,8 +63,6 @@ match(const struct sk_buff *skb, struct ipv6_opt_hdr _optsh, *oh; const struct ip6t_opts *optinfo = matchinfo; unsigned int temp; - unsigned int len; - u8 nexthdr; unsigned int ptr; unsigned int hdrlen = 0; unsigned int ret = 0; @@ -72,97 +70,25 @@ match(const struct sk_buff *skb, u8 _optlen, *lp = NULL; unsigned int optlen; - /* type of the 1st exthdr */ - nexthdr = skb->nh.ipv6h->nexthdr; - /* pointer to the 1st exthdr */ - ptr = sizeof(struct ipv6hdr); - /* available length */ - len = skb->len - ptr; - temp = 0; - - while (ip6t_ext_hdr(nexthdr)) { - struct ipv6_opt_hdr _hdr, *hp; - - DEBUGP("ipv6_opts header iteration \n"); - - /* Is there enough space for the next ext header? */ - if (len < (int)sizeof(struct ipv6_opt_hdr)) - return 0; - /* No more exthdr -> evaluate */ - if (nexthdr == NEXTHDR_NONE) { - break; - } - /* ESP -> evaluate */ - if (nexthdr == NEXTHDR_ESP) { - break; - } - - hp = skb_header_pointer(skb, ptr, sizeof(_hdr), &_hdr); - BUG_ON(hp == NULL); - - /* Calculate the header length */ - if (nexthdr == NEXTHDR_FRAGMENT) { - hdrlen = 8; - } else if (nexthdr == NEXTHDR_AUTH) - hdrlen = (hp->hdrlen+2)<<2; - else - hdrlen = ipv6_optlen(hp); - - /* OPTS -> evaluate */ #if HOPBYHOP - if (nexthdr == NEXTHDR_HOP) { - temp |= MASK_HOPOPTS; + if (ipv6_find_hdr(skb, &ptr, NEXTHDR_HOP) < 0) #else - if (nexthdr == NEXTHDR_DEST) { - temp |= MASK_DSTOPTS; + if (ipv6_find_hdr(skb, &ptr, NEXTHDR_DEST) < 0) #endif - break; - } - + return 0; - /* set the flag */ - switch (nexthdr){ - case NEXTHDR_HOP: - case NEXTHDR_ROUTING: - case NEXTHDR_FRAGMENT: - case NEXTHDR_AUTH: - case NEXTHDR_DEST: - break; - default: - DEBUGP("ipv6_opts match: unknown nextheader %u\n",nexthdr); - return 0; - break; - } - - nexthdr = hp->nexthdr; - len -= hdrlen; - ptr += hdrlen; - if ( ptr > skb->len ) { - DEBUGP("ipv6_opts: new pointer is too large! \n"); - break; - } - } - - /* OPTIONS header not found */ -#if HOPBYHOP - if ( temp != MASK_HOPOPTS ) return 0; -#else - if ( temp != MASK_DSTOPTS ) return 0; -#endif - - if (len < (int)sizeof(struct ipv6_opt_hdr)){ + oh = skb_header_pointer(skb, ptr, sizeof(_optsh), &_optsh); + if (oh == NULL){ *hotdrop = 1; return 0; } - if (len < hdrlen){ + hdrlen = ipv6_optlen(oh); + if (skb->len - ptr < hdrlen){ /* Packet smaller than it's length field */ return 0; } - oh = skb_header_pointer(skb, ptr, sizeof(_optsh), &_optsh); - BUG_ON(oh == NULL); - DEBUGP("IPv6 OPTS LEN %u %u ", hdrlen, oh->hdrlen); DEBUGP("len %02X %04X %02X ", diff --git a/net/ipv6/netfilter/ip6t_esp.c b/net/ipv6/netfilter/ip6t_esp.c index e39dd236fd8e..24bc0cde43a1 100644 --- a/net/ipv6/netfilter/ip6t_esp.c +++ b/net/ipv6/netfilter/ip6t_esp.c @@ -48,87 +48,22 @@ match(const struct sk_buff *skb, unsigned int protoff, int *hotdrop) { - struct ip_esp_hdr _esp, *eh = NULL; + struct ip_esp_hdr _esp, *eh; const struct ip6t_esp *espinfo = matchinfo; - unsigned int temp; - int len; - u8 nexthdr; unsigned int ptr; /* Make sure this isn't an evil packet */ /*DEBUGP("ipv6_esp entered \n");*/ - /* type of the 1st exthdr */ - nexthdr = skb->nh.ipv6h->nexthdr; - /* pointer to the 1st exthdr */ - ptr = sizeof(struct ipv6hdr); - /* available length */ - len = skb->len - ptr; - temp = 0; - - while (ip6t_ext_hdr(nexthdr)) { - struct ipv6_opt_hdr _hdr, *hp; - int hdrlen; - - DEBUGP("ipv6_esp header iteration \n"); - - /* Is there enough space for the next ext header? */ - if (len < sizeof(struct ipv6_opt_hdr)) - return 0; - /* No more exthdr -> evaluate */ - if (nexthdr == NEXTHDR_NONE) - break; - /* ESP -> evaluate */ - if (nexthdr == NEXTHDR_ESP) { - temp |= MASK_ESP; - break; - } - - hp = skb_header_pointer(skb, ptr, sizeof(_hdr), &_hdr); - BUG_ON(hp == NULL); - - /* Calculate the header length */ - if (nexthdr == NEXTHDR_FRAGMENT) - hdrlen = 8; - else if (nexthdr == NEXTHDR_AUTH) - hdrlen = (hp->hdrlen+2)<<2; - else - hdrlen = ipv6_optlen(hp); - - /* set the flag */ - switch (nexthdr) { - case NEXTHDR_HOP: - case NEXTHDR_ROUTING: - case NEXTHDR_FRAGMENT: - case NEXTHDR_AUTH: - case NEXTHDR_DEST: - break; - default: - DEBUGP("ipv6_esp match: unknown nextheader %u\n",nexthdr); - return 0; - } - - nexthdr = hp->nexthdr; - len -= hdrlen; - ptr += hdrlen; - if (ptr > skb->len) { - DEBUGP("ipv6_esp: new pointer too large! \n"); - break; - } - } - - /* ESP header not found */ - if (temp != MASK_ESP) + if (ipv6_find_hdr(skb, &ptr, NEXTHDR_ESP) < 0) return 0; - if (len < sizeof(struct ip_esp_hdr)) { + eh = skb_header_pointer(skb, ptr, sizeof(_esp), &_esp); + if (eh == NULL) { *hotdrop = 1; return 0; } - eh = skb_header_pointer(skb, ptr, sizeof(_esp), &_esp); - BUG_ON(eh == NULL); - DEBUGP("IPv6 ESP SPI %u %08X\n", ntohl(eh->spi), ntohl(eh->spi)); return (eh != NULL) diff --git a/net/ipv6/netfilter/ip6t_frag.c b/net/ipv6/netfilter/ip6t_frag.c index 4bfa30a9bc80..085d5f8eea29 100644 --- a/net/ipv6/netfilter/ip6t_frag.c +++ b/net/ipv6/netfilter/ip6t_frag.c @@ -48,90 +48,18 @@ match(const struct sk_buff *skb, unsigned int protoff, int *hotdrop) { - struct frag_hdr _frag, *fh = NULL; + struct frag_hdr _frag, *fh; const struct ip6t_frag *fraginfo = matchinfo; - unsigned int temp; - int len; - u8 nexthdr; unsigned int ptr; - unsigned int hdrlen = 0; - - /* type of the 1st exthdr */ - nexthdr = skb->nh.ipv6h->nexthdr; - /* pointer to the 1st exthdr */ - ptr = sizeof(struct ipv6hdr); - /* available length */ - len = skb->len - ptr; - temp = 0; - - while (ip6t_ext_hdr(nexthdr)) { - struct ipv6_opt_hdr _hdr, *hp; - - DEBUGP("ipv6_frag header iteration \n"); - - /* Is there enough space for the next ext header? */ - if (len < (int)sizeof(struct ipv6_opt_hdr)) - return 0; - /* No more exthdr -> evaluate */ - if (nexthdr == NEXTHDR_NONE) { - break; - } - /* ESP -> evaluate */ - if (nexthdr == NEXTHDR_ESP) { - break; - } - - hp = skb_header_pointer(skb, ptr, sizeof(_hdr), &_hdr); - BUG_ON(hp == NULL); - - /* Calculate the header length */ - if (nexthdr == NEXTHDR_FRAGMENT) { - hdrlen = 8; - } else if (nexthdr == NEXTHDR_AUTH) - hdrlen = (hp->hdrlen+2)<<2; - else - hdrlen = ipv6_optlen(hp); - - /* FRAG -> evaluate */ - if (nexthdr == NEXTHDR_FRAGMENT) { - temp |= MASK_FRAGMENT; - break; - } - - - /* set the flag */ - switch (nexthdr){ - case NEXTHDR_HOP: - case NEXTHDR_ROUTING: - case NEXTHDR_FRAGMENT: - case NEXTHDR_AUTH: - case NEXTHDR_DEST: - break; - default: - DEBUGP("ipv6_frag match: unknown nextheader %u\n",nexthdr); - return 0; - break; - } - - nexthdr = hp->nexthdr; - len -= hdrlen; - ptr += hdrlen; - if ( ptr > skb->len ) { - DEBUGP("ipv6_frag: new pointer too large! \n"); - break; - } - } - - /* FRAG header not found */ - if ( temp != MASK_FRAGMENT ) return 0; - - if (len < sizeof(struct frag_hdr)){ - *hotdrop = 1; - return 0; - } - fh = skb_header_pointer(skb, ptr, sizeof(_frag), &_frag); - BUG_ON(fh == NULL); + if (ipv6_find_hdr(skb, &ptr, NEXTHDR_FRAGMENT) < 0) + return 0; + + fh = skb_header_pointer(skb, ptr, sizeof(_frag), &_frag); + if (fh == NULL){ + *hotdrop = 1; + return 0; + } DEBUGP("INFO %04X ", fh->frag_off); DEBUGP("OFFSET %04X ", ntohs(fh->frag_off) & ~0x7); diff --git a/net/ipv6/netfilter/ip6t_hbh.c b/net/ipv6/netfilter/ip6t_hbh.c index 27f3650d127e..1d09485111d0 100644 --- a/net/ipv6/netfilter/ip6t_hbh.c +++ b/net/ipv6/netfilter/ip6t_hbh.c @@ -63,8 +63,6 @@ match(const struct sk_buff *skb, struct ipv6_opt_hdr _optsh, *oh; const struct ip6t_opts *optinfo = matchinfo; unsigned int temp; - unsigned int len; - u8 nexthdr; unsigned int ptr; unsigned int hdrlen = 0; unsigned int ret = 0; @@ -72,97 +70,25 @@ match(const struct sk_buff *skb, u8 _optlen, *lp = NULL; unsigned int optlen; - /* type of the 1st exthdr */ - nexthdr = skb->nh.ipv6h->nexthdr; - /* pointer to the 1st exthdr */ - ptr = sizeof(struct ipv6hdr); - /* available length */ - len = skb->len - ptr; - temp = 0; - - while (ip6t_ext_hdr(nexthdr)) { - struct ipv6_opt_hdr _hdr, *hp; - - DEBUGP("ipv6_opts header iteration \n"); - - /* Is there enough space for the next ext header? */ - if (len < (int)sizeof(struct ipv6_opt_hdr)) - return 0; - /* No more exthdr -> evaluate */ - if (nexthdr == NEXTHDR_NONE) { - break; - } - /* ESP -> evaluate */ - if (nexthdr == NEXTHDR_ESP) { - break; - } - - hp = skb_header_pointer(skb, ptr, sizeof(_hdr), &_hdr); - BUG_ON(hp == NULL); - - /* Calculate the header length */ - if (nexthdr == NEXTHDR_FRAGMENT) { - hdrlen = 8; - } else if (nexthdr == NEXTHDR_AUTH) - hdrlen = (hp->hdrlen+2)<<2; - else - hdrlen = ipv6_optlen(hp); - - /* OPTS -> evaluate */ #if HOPBYHOP - if (nexthdr == NEXTHDR_HOP) { - temp |= MASK_HOPOPTS; + if (ipv6_find_hdr(skb, &ptr, NEXTHDR_HOP) < 0) #else - if (nexthdr == NEXTHDR_DEST) { - temp |= MASK_DSTOPTS; + if (ipv6_find_hdr(skb, &ptr, NEXTHDR_DEST) < 0) #endif - break; - } - + return 0; - /* set the flag */ - switch (nexthdr){ - case NEXTHDR_HOP: - case NEXTHDR_ROUTING: - case NEXTHDR_FRAGMENT: - case NEXTHDR_AUTH: - case NEXTHDR_DEST: - break; - default: - DEBUGP("ipv6_opts match: unknown nextheader %u\n",nexthdr); - return 0; - break; - } - - nexthdr = hp->nexthdr; - len -= hdrlen; - ptr += hdrlen; - if ( ptr > skb->len ) { - DEBUGP("ipv6_opts: new pointer is too large! \n"); - break; - } - } - - /* OPTIONS header not found */ -#if HOPBYHOP - if ( temp != MASK_HOPOPTS ) return 0; -#else - if ( temp != MASK_DSTOPTS ) return 0; -#endif - - if (len < (int)sizeof(struct ipv6_opt_hdr)){ + oh = skb_header_pointer(skb, ptr, sizeof(_optsh), &_optsh); + if (oh == NULL){ *hotdrop = 1; return 0; } - if (len < hdrlen){ + hdrlen = ipv6_optlen(oh); + if (skb->len - ptr < hdrlen){ /* Packet smaller than it's length field */ return 0; } - oh = skb_header_pointer(skb, ptr, sizeof(_optsh), &_optsh); - BUG_ON(oh == NULL); - DEBUGP("IPv6 OPTS LEN %u %u ", hdrlen, oh->hdrlen); DEBUGP("len %02X %04X %02X ", diff --git a/net/ipv6/netfilter/ip6t_rt.c b/net/ipv6/netfilter/ip6t_rt.c index 2bb670037df3..beb2fd5cebbb 100644 --- a/net/ipv6/netfilter/ip6t_rt.c +++ b/net/ipv6/netfilter/ip6t_rt.c @@ -50,98 +50,29 @@ match(const struct sk_buff *skb, unsigned int protoff, int *hotdrop) { - struct ipv6_rt_hdr _route, *rh = NULL; + struct ipv6_rt_hdr _route, *rh; const struct ip6t_rt *rtinfo = matchinfo; unsigned int temp; - unsigned int len; - u8 nexthdr; unsigned int ptr; unsigned int hdrlen = 0; unsigned int ret = 0; struct in6_addr *ap, _addr; - /* type of the 1st exthdr */ - nexthdr = skb->nh.ipv6h->nexthdr; - /* pointer to the 1st exthdr */ - ptr = sizeof(struct ipv6hdr); - /* available length */ - len = skb->len - ptr; - temp = 0; + if (ipv6_find_hdr(skb, &ptr, NEXTHDR_ROUTING) < 0) + return 0; - while (ip6t_ext_hdr(nexthdr)) { - struct ipv6_opt_hdr _hdr, *hp; - - DEBUGP("ipv6_rt header iteration \n"); - - /* Is there enough space for the next ext header? */ - if (len < (int)sizeof(struct ipv6_opt_hdr)) - return 0; - /* No more exthdr -> evaluate */ - if (nexthdr == NEXTHDR_NONE) { - break; - } - /* ESP -> evaluate */ - if (nexthdr == NEXTHDR_ESP) { - break; - } - - hp = skb_header_pointer(skb, ptr, sizeof(_hdr), &_hdr); - BUG_ON(hp == NULL); - - /* Calculate the header length */ - if (nexthdr == NEXTHDR_FRAGMENT) { - hdrlen = 8; - } else if (nexthdr == NEXTHDR_AUTH) - hdrlen = (hp->hdrlen+2)<<2; - else - hdrlen = ipv6_optlen(hp); - - /* ROUTING -> evaluate */ - if (nexthdr == NEXTHDR_ROUTING) { - temp |= MASK_ROUTING; - break; - } - - - /* set the flag */ - switch (nexthdr){ - case NEXTHDR_HOP: - case NEXTHDR_ROUTING: - case NEXTHDR_FRAGMENT: - case NEXTHDR_AUTH: - case NEXTHDR_DEST: - break; - default: - DEBUGP("ipv6_rt match: unknown nextheader %u\n",nexthdr); - return 0; - break; - } - - nexthdr = hp->nexthdr; - len -= hdrlen; - ptr += hdrlen; - if ( ptr > skb->len ) { - DEBUGP("ipv6_rt: new pointer is too large! \n"); - break; - } - } - - /* ROUTING header not found */ - if ( temp != MASK_ROUTING ) return 0; - - if (len < (int)sizeof(struct ipv6_rt_hdr)){ + rh = skb_header_pointer(skb, ptr, sizeof(_route), &_route); + if (rh == NULL){ *hotdrop = 1; return 0; } - if (len < hdrlen){ + hdrlen = ipv6_optlen(rh); + if (skb->len - ptr < hdrlen){ /* Pcket smaller than its length field */ return 0; } - rh = skb_header_pointer(skb, ptr, sizeof(_route), &_route); - BUG_ON(rh == NULL); - DEBUGP("IPv6 RT LEN %u %u ", hdrlen, rh->hdrlen); DEBUGP("TYPE %04X ", rh->type); DEBUGP("SGS_LEFT %u %02X\n", rh->segments_left, rh->segments_left); diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 5aa3691c578d..a1265a320b11 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -627,7 +627,7 @@ static void rawv6_probe_proto_opt(struct flowi *fl, struct msghdr *msg) if (type && code) { get_user(fl->fl_icmp_type, type); - __get_user(fl->fl_icmp_code, code); + get_user(fl->fl_icmp_code, code); probed = 1; } break; diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 8690f171c1ef..ee865d88183b 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -36,6 +36,11 @@ * Michal Ostrowski : Module initialization cleanup. * Ulises Alonso : Frame number limit removal and * packet_set_ring memory leak. + * Eric Biederman : Allow for > 8 byte hardware addresses. + * The convention is that longer addresses + * will simply extend the hardware address + * byte arrays at the end of sockaddr_ll + * and packet_mreq. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -161,7 +166,17 @@ struct packet_mclist int count; unsigned short type; unsigned short alen; - unsigned char addr[8]; + unsigned char addr[MAX_ADDR_LEN]; +}; +/* identical to struct packet_mreq except it has + * a longer address field. + */ +struct packet_mreq_max +{ + int mr_ifindex; + unsigned short mr_type; + unsigned short mr_alen; + unsigned char mr_address[MAX_ADDR_LEN]; }; #endif #ifdef CONFIG_PACKET_MMAP @@ -716,6 +731,8 @@ static int packet_sendmsg(struct kiocb *iocb, struct socket *sock, err = -EINVAL; if (msg->msg_namelen < sizeof(struct sockaddr_ll)) goto out; + if (msg->msg_namelen < (saddr->sll_halen + offsetof(struct sockaddr_ll, sll_addr))) + goto out; ifindex = saddr->sll_ifindex; proto = saddr->sll_protocol; addr = saddr->sll_addr; @@ -744,6 +761,12 @@ static int packet_sendmsg(struct kiocb *iocb, struct socket *sock, if (dev->hard_header) { int res; err = -EINVAL; + if (saddr) { + if (saddr->sll_halen != dev->addr_len) + goto out_free; + if (saddr->sll_hatype != dev->type) + goto out_free; + } res = dev->hard_header(skb, dev, ntohs(proto), addr, NULL, len); if (sock->type != SOCK_DGRAM) { skb->tail = skb->data; @@ -1045,6 +1068,7 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock, struct sock *sk = sock->sk; struct sk_buff *skb; int copied, err; + struct sockaddr_ll *sll; err = -EINVAL; if (flags & ~(MSG_PEEK|MSG_DONTWAIT|MSG_TRUNC|MSG_CMSG_COMPAT)) @@ -1057,16 +1081,6 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock, #endif /* - * If the address length field is there to be filled in, we fill - * it in now. - */ - - if (sock->type == SOCK_PACKET) - msg->msg_namelen = sizeof(struct sockaddr_pkt); - else - msg->msg_namelen = sizeof(struct sockaddr_ll); - - /* * Call the generic datagram receiver. This handles all sorts * of horrible races and re-entrancy so we can forget about it * in the protocol layers. @@ -1087,6 +1101,17 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock, goto out; /* + * If the address length field is there to be filled in, we fill + * it in now. + */ + + sll = (struct sockaddr_ll*)skb->cb; + if (sock->type == SOCK_PACKET) + msg->msg_namelen = sizeof(struct sockaddr_pkt); + else + msg->msg_namelen = sll->sll_halen + offsetof(struct sockaddr_ll, sll_addr); + + /* * You lose any data beyond the buffer you gave. If it worries a * user program they can ask the device for its MTU anyway. */ @@ -1166,7 +1191,7 @@ static int packet_getname(struct socket *sock, struct sockaddr *uaddr, sll->sll_hatype = 0; /* Bad: we have no ARPHRD_UNSPEC */ sll->sll_halen = 0; } - *uaddr_len = sizeof(*sll); + *uaddr_len = offsetof(struct sockaddr_ll, sll_addr) + sll->sll_halen; return 0; } @@ -1199,7 +1224,7 @@ static void packet_dev_mclist(struct net_device *dev, struct packet_mclist *i, i } } -static int packet_mc_add(struct sock *sk, struct packet_mreq *mreq) +static int packet_mc_add(struct sock *sk, struct packet_mreq_max *mreq) { struct packet_sock *po = pkt_sk(sk); struct packet_mclist *ml, *i; @@ -1249,7 +1274,7 @@ done: return err; } -static int packet_mc_drop(struct sock *sk, struct packet_mreq *mreq) +static int packet_mc_drop(struct sock *sk, struct packet_mreq_max *mreq) { struct packet_mclist *ml, **mlp; @@ -1315,11 +1340,17 @@ packet_setsockopt(struct socket *sock, int level, int optname, char __user *optv case PACKET_ADD_MEMBERSHIP: case PACKET_DROP_MEMBERSHIP: { - struct packet_mreq mreq; - if (optlen<sizeof(mreq)) + struct packet_mreq_max mreq; + int len = optlen; + memset(&mreq, 0, sizeof(mreq)); + if (len < sizeof(struct packet_mreq)) return -EINVAL; - if (copy_from_user(&mreq,optval,sizeof(mreq))) + if (len > sizeof(mreq)) + len = sizeof(mreq); + if (copy_from_user(&mreq,optval,len)) return -EFAULT; + if (len < (mreq.mr_alen + offsetof(struct packet_mreq, mr_address))) + return -EINVAL; if (optname == PACKET_ADD_MEMBERSHIP) ret = packet_mc_add(sk, &mreq); else diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 86073df418f5..505c7de10c50 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -2414,6 +2414,17 @@ sctp_disposition_t sctp_sf_do_9_2_shutdown(const struct sctp_endpoint *ep, skb_pull(chunk->skb, sizeof(sctp_shutdownhdr_t)); chunk->subh.shutdown_hdr = sdh; + /* API 5.3.1.5 SCTP_SHUTDOWN_EVENT + * When a peer sends a SHUTDOWN, SCTP delivers this notification to + * inform the application that it should cease sending data. + */ + ev = sctp_ulpevent_make_shutdown_event(asoc, 0, GFP_ATOMIC); + if (!ev) { + disposition = SCTP_DISPOSITION_NOMEM; + goto out; + } + sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(ev)); + /* Upon the reception of the SHUTDOWN, the peer endpoint shall * - enter the SHUTDOWN-RECEIVED state, * - stop accepting new data from its SCTP user @@ -2439,17 +2450,6 @@ sctp_disposition_t sctp_sf_do_9_2_shutdown(const struct sctp_endpoint *ep, sctp_add_cmd_sf(commands, SCTP_CMD_PROCESS_CTSN, SCTP_U32(chunk->subh.shutdown_hdr->cum_tsn_ack)); - /* API 5.3.1.5 SCTP_SHUTDOWN_EVENT - * When a peer sends a SHUTDOWN, SCTP delivers this notification to - * inform the application that it should cease sending data. - */ - ev = sctp_ulpevent_make_shutdown_event(asoc, 0, GFP_ATOMIC); - if (!ev) { - disposition = SCTP_DISPOSITION_NOMEM; - goto out; - } - sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(ev)); - out: return disposition; } diff --git a/sound/oss/au1000.c b/sound/oss/au1000.c index 4491733c9e4e..2c2ae2ee01ac 100644 --- a/sound/oss/au1000.c +++ b/sound/oss/au1000.c @@ -1295,7 +1295,7 @@ static int au1000_mmap(struct file *file, struct vm_area_struct *vma) unsigned long size; int ret = 0; - dbg(__FUNCTION__); + dbg("%s", __FUNCTION__); lock_kernel(); down(&s->sem); diff --git a/sound/oss/ite8172.c b/sound/oss/ite8172.c index 58f879fda975..26e5944b6ba8 100644 --- a/sound/oss/ite8172.c +++ b/sound/oss/ite8172.c @@ -1859,7 +1859,7 @@ static int it8172_release(struct inode *inode, struct file *file) struct it8172_state *s = (struct it8172_state *)file->private_data; #ifdef IT8172_VERBOSE_DEBUG - dbg(__FUNCTION__); + dbg("%s", __FUNCTION__); #endif lock_kernel(); if (file->f_mode & FMODE_WRITE) diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c index 8a59598167f9..c1a239a4dac6 100644 --- a/sound/pci/atiixp_modem.c +++ b/sound/pci/atiixp_modem.c @@ -405,7 +405,7 @@ static int snd_atiixp_acquire_codec(atiixp_t *chip) while (atiixp_read(chip, PHYS_OUT_ADDR) & ATI_REG_PHYS_OUT_ADDR_EN) { if (! timeout--) { - snd_printk(KERN_WARNING "atiixp: codec acquire timeout\n"); + snd_printk(KERN_WARNING "atiixp-modem: codec acquire timeout\n"); return -EBUSY; } udelay(1); @@ -436,7 +436,7 @@ static unsigned short snd_atiixp_codec_read(atiixp_t *chip, unsigned short codec } while (--timeout); /* time out may happen during reset */ if (reg < 0x7c) - snd_printk(KERN_WARNING "atiixp: codec read timeout (reg %x)\n", reg); + snd_printk(KERN_WARNING "atiixp-modem: codec read timeout (reg %x)\n", reg); return 0xffff; } @@ -498,7 +498,7 @@ static int snd_atiixp_aclink_reset(atiixp_t *chip) do_delay(); atiixp_update(chip, CMD, ATI_REG_CMD_AC_RESET, ATI_REG_CMD_AC_RESET); if (--timeout) { - snd_printk(KERN_ERR "atiixp: codec reset timeout\n"); + snd_printk(KERN_ERR "atiixp-modem: codec reset timeout\n"); break; } } @@ -552,7 +552,7 @@ static int snd_atiixp_codec_detect(atiixp_t *chip) atiixp_write(chip, IER, 0); /* disable irqs */ if ((chip->codec_not_ready_bits & ALL_CODEC_NOT_READY) == ALL_CODEC_NOT_READY) { - snd_printk(KERN_ERR "atiixp: no codec detected!\n"); + snd_printk(KERN_ERR "atiixp-modem: no codec detected!\n"); return -ENXIO; } return 0; @@ -635,7 +635,7 @@ static void snd_atiixp_xrun_dma(atiixp_t *chip, atiixp_dma_t *dma) { if (! dma->substream || ! dma->running) return; - snd_printdd("atiixp: XRUN detected (DMA %d)\n", dma->ops->type); + snd_printdd("atiixp-modem: XRUN detected (DMA %d)\n", dma->ops->type); snd_pcm_stop(dma->substream, SNDRV_PCM_STATE_XRUN); } @@ -1081,14 +1081,14 @@ static int __devinit snd_atiixp_mixer_new(atiixp_t *chip, int clock) ac97.scaps = AC97_SCAP_SKIP_AUDIO; if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97[i])) < 0) { chip->ac97[i] = NULL; /* to be sure */ - snd_printdd("atiixp: codec %d not available for modem\n", i); + snd_printdd("atiixp-modem: codec %d not available for modem\n", i); continue; } codec_count++; } if (! codec_count) { - snd_printk(KERN_ERR "atiixp: no codec available\n"); + snd_printk(KERN_ERR "atiixp-modem: no codec available\n"); return -ENODEV; } @@ -1159,7 +1159,7 @@ static void __devinit snd_atiixp_proc_init(atiixp_t *chip) { snd_info_entry_t *entry; - if (! snd_card_proc_new(chip->card, "atiixp", &entry)) + if (! snd_card_proc_new(chip->card, "atiixp-modem", &entry)) snd_info_set_text_ops(entry, chip, 1024, snd_atiixp_proc_read); } diff --git a/sound/sparc/cs4231.c b/sound/sparc/cs4231.c index 2fb27c4e951f..f4361c518e46 100644 --- a/sound/sparc/cs4231.c +++ b/sound/sparc/cs4231.c @@ -173,7 +173,7 @@ static cs4231_t *cs4231_list; #define CS4231_GLOBALIRQ 0x01 /* IRQ is active */ -/* definitions for codec irq status */ +/* definitions for codec irq status - CS4231_IRQ_STATUS */ #define CS4231_PLAYBACK_IRQ 0x10 #define CS4231_RECORD_IRQ 0x20 @@ -402,7 +402,7 @@ static void snd_cs4231_outm(cs4231_t *chip, unsigned char reg, udelay(100); #ifdef CONFIG_SND_DEBUG if (__cs4231_readb(chip, CS4231P(chip, REGSEL)) & CS4231_INIT) - snd_printk("outm: auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value); + snd_printdd("outm: auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value); #endif if (chip->calibrate_mute) { chip->image[reg] &= mask; @@ -425,6 +425,10 @@ static void snd_cs4231_dout(cs4231_t *chip, unsigned char reg, unsigned char val timeout > 0 && (__cs4231_readb(chip, CS4231P(chip, REGSEL)) & CS4231_INIT); timeout--) udelay(100); +#ifdef CONFIG_SND_DEBUG + if (__cs4231_readb(chip, CS4231P(chip, REGSEL)) & CS4231_INIT) + snd_printdd("out: auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value); +#endif __cs4231_writeb(chip, chip->mce_bit | reg, CS4231P(chip, REGSEL)); __cs4231_writeb(chip, value, CS4231P(chip, REG)); mb(); @@ -440,15 +444,12 @@ static void snd_cs4231_out(cs4231_t *chip, unsigned char reg, unsigned char valu udelay(100); #ifdef CONFIG_SND_DEBUG if (__cs4231_readb(chip, CS4231P(chip, REGSEL)) & CS4231_INIT) - snd_printk("out: auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value); + snd_printdd("out: auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value); #endif __cs4231_writeb(chip, chip->mce_bit | reg, CS4231P(chip, REGSEL)); __cs4231_writeb(chip, value, CS4231P(chip, REG)); chip->image[reg] = value; mb(); -#if 0 - printk("codec out - reg 0x%x = 0x%x\n", chip->mce_bit | reg, value); -#endif } static unsigned char snd_cs4231_in(cs4231_t *chip, unsigned char reg) @@ -462,61 +463,14 @@ static unsigned char snd_cs4231_in(cs4231_t *chip, unsigned char reg) udelay(100); #ifdef CONFIG_SND_DEBUG if (__cs4231_readb(chip, CS4231P(chip, REGSEL)) & CS4231_INIT) - snd_printk("in: auto calibration time out - reg = 0x%x\n", reg); + snd_printdd("in: auto calibration time out - reg = 0x%x\n", reg); #endif __cs4231_writeb(chip, chip->mce_bit | reg, CS4231P(chip, REGSEL)); mb(); ret = __cs4231_readb(chip, CS4231P(chip, REG)); -#if 0 - printk("codec in - reg 0x%x = 0x%x\n", chip->mce_bit | reg, ret); -#endif return ret; } -#if 0 - -static void snd_cs4231_debug(cs4231_t *chip) -{ - printk("CS4231 REGS: INDEX = 0x%02x ", - __cs4231_readb(chip, CS4231P(chip, REGSEL))); - printk(" STATUS = 0x%02x\n", - __cs4231_readb(chip, CS4231P(chip, STATUS))); - printk(" 0x00: left input = 0x%02x ", snd_cs4231_in(chip, 0x00)); - printk(" 0x10: alt 1 (CFIG 2) = 0x%02x\n", snd_cs4231_in(chip, 0x10)); - printk(" 0x01: right input = 0x%02x ", snd_cs4231_in(chip, 0x01)); - printk(" 0x11: alt 2 (CFIG 3) = 0x%02x\n", snd_cs4231_in(chip, 0x11)); - printk(" 0x02: GF1 left input = 0x%02x ", snd_cs4231_in(chip, 0x02)); - printk(" 0x12: left line in = 0x%02x\n", snd_cs4231_in(chip, 0x12)); - printk(" 0x03: GF1 right input = 0x%02x ", snd_cs4231_in(chip, 0x03)); - printk(" 0x13: right line in = 0x%02x\n", snd_cs4231_in(chip, 0x13)); - printk(" 0x04: CD left input = 0x%02x ", snd_cs4231_in(chip, 0x04)); - printk(" 0x14: timer low = 0x%02x\n", snd_cs4231_in(chip, 0x14)); - printk(" 0x05: CD right input = 0x%02x ", snd_cs4231_in(chip, 0x05)); - printk(" 0x15: timer high = 0x%02x\n", snd_cs4231_in(chip, 0x15)); - printk(" 0x06: left output = 0x%02x ", snd_cs4231_in(chip, 0x06)); - printk(" 0x16: left MIC (PnP) = 0x%02x\n", snd_cs4231_in(chip, 0x16)); - printk(" 0x07: right output = 0x%02x ", snd_cs4231_in(chip, 0x07)); - printk(" 0x17: right MIC (PnP) = 0x%02x\n", snd_cs4231_in(chip, 0x17)); - printk(" 0x08: playback format = 0x%02x ", snd_cs4231_in(chip, 0x08)); - printk(" 0x18: IRQ status = 0x%02x\n", snd_cs4231_in(chip, 0x18)); - printk(" 0x09: iface (CFIG 1) = 0x%02x ", snd_cs4231_in(chip, 0x09)); - printk(" 0x19: left line out = 0x%02x\n", snd_cs4231_in(chip, 0x19)); - printk(" 0x0a: pin control = 0x%02x ", snd_cs4231_in(chip, 0x0a)); - printk(" 0x1a: mono control = 0x%02x\n", snd_cs4231_in(chip, 0x1a)); - printk(" 0x0b: init & status = 0x%02x ", snd_cs4231_in(chip, 0x0b)); - printk(" 0x1b: right line out = 0x%02x\n", snd_cs4231_in(chip, 0x1b)); - printk(" 0x0c: revision & mode = 0x%02x ", snd_cs4231_in(chip, 0x0c)); - printk(" 0x1c: record format = 0x%02x\n", snd_cs4231_in(chip, 0x1c)); - printk(" 0x0d: loopback = 0x%02x ", snd_cs4231_in(chip, 0x0d)); - printk(" 0x1d: var freq (PnP) = 0x%02x\n", snd_cs4231_in(chip, 0x1d)); - printk(" 0x0e: ply upr count = 0x%02x ", snd_cs4231_in(chip, 0x0e)); - printk(" 0x1e: rec upr count = 0x%02x\n", snd_cs4231_in(chip, 0x1e)); - printk(" 0x0f: ply lwr count = 0x%02x ", snd_cs4231_in(chip, 0x0f)); - printk(" 0x1f: rec lwr count = 0x%02x\n", snd_cs4231_in(chip, 0x1f)); -} - -#endif - /* * CS4231 detection / MCE routines */ @@ -528,11 +482,12 @@ static void snd_cs4231_busy_wait(cs4231_t *chip) /* huh.. looks like this sequence is proper for CS4231A chip (GUS MAX) */ for (timeout = 5; timeout > 0; timeout--) __cs4231_readb(chip, CS4231P(chip, REGSEL)); + /* end of cleanup sequence */ - for (timeout = 250; + for (timeout = 500; timeout > 0 && (__cs4231_readb(chip, CS4231P(chip, REGSEL)) & CS4231_INIT); timeout--) - udelay(100); + udelay(1000); } static void snd_cs4231_mce_up(cs4231_t *chip) @@ -545,12 +500,12 @@ static void snd_cs4231_mce_up(cs4231_t *chip) udelay(100); #ifdef CONFIG_SND_DEBUG if (__cs4231_readb(chip, CS4231P(chip, REGSEL)) & CS4231_INIT) - snd_printk("mce_up - auto calibration time out (0)\n"); + snd_printdd("mce_up - auto calibration time out (0)\n"); #endif chip->mce_bit |= CS4231_MCE; timeout = __cs4231_readb(chip, CS4231P(chip, REGSEL)); if (timeout == 0x80) - snd_printk("mce_up [%p]: serious init problem - codec still busy\n", chip->port); + snd_printdd("mce_up [%p]: serious init problem - codec still busy\n", chip->port); if (!(timeout & CS4231_MCE)) __cs4231_writeb(chip, chip->mce_bit | (timeout & 0x1f), CS4231P(chip, REGSEL)); spin_unlock_irqrestore(&chip->lock, flags); @@ -563,18 +518,15 @@ static void snd_cs4231_mce_down(cs4231_t *chip) spin_lock_irqsave(&chip->lock, flags); snd_cs4231_busy_wait(chip); -#if 0 - printk("(1) timeout = %i\n", timeout); -#endif #ifdef CONFIG_SND_DEBUG if (__cs4231_readb(chip, CS4231P(chip, REGSEL)) & CS4231_INIT) - snd_printk("mce_down [%p] - auto calibration time out (0)\n", CS4231P(chip, REGSEL)); + snd_printdd("mce_down [%p] - auto calibration time out (0)\n", CS4231P(chip, REGSEL)); #endif chip->mce_bit &= ~CS4231_MCE; timeout = __cs4231_readb(chip, CS4231P(chip, REGSEL)); __cs4231_writeb(chip, chip->mce_bit | (timeout & 0x1f), CS4231P(chip, REGSEL)); if (timeout == 0x80) - snd_printk("mce_down [%p]: serious init problem - codec still busy\n", chip->port); + snd_printdd("mce_down [%p]: serious init problem - codec still busy\n", chip->port); if ((timeout & CS4231_MCE) == 0) { spin_unlock_irqrestore(&chip->lock, flags); return; @@ -590,9 +542,7 @@ static void snd_cs4231_mce_down(cs4231_t *chip) spin_unlock_irqrestore(&chip->lock, flags); return; } -#if 0 - printk("(2) timeout = %i, jiffies = %li\n", timeout, jiffies); -#endif + /* in 10ms increments, check condition, up to 250ms */ timeout = 25; while (snd_cs4231_in(chip, CS4231_TEST_INIT) & CS4231_CALIB_IN_PROGRESS) { @@ -604,9 +554,7 @@ static void snd_cs4231_mce_down(cs4231_t *chip) msleep(10); spin_lock_irqsave(&chip->lock, flags); } -#if 0 - printk("(3) jiffies = %li\n", jiffies); -#endif + /* in 10ms increments, check condition, up to 100ms */ timeout = 10; while (__cs4231_readb(chip, CS4231P(chip, REGSEL)) & CS4231_INIT) { @@ -619,54 +567,58 @@ static void snd_cs4231_mce_down(cs4231_t *chip) spin_lock_irqsave(&chip->lock, flags); } spin_unlock_irqrestore(&chip->lock, flags); -#if 0 - printk("(4) jiffies = %li\n", jiffies); - snd_printk("mce_down - exit = 0x%x\n", __cs4231_readb(chip, CS4231P(chip, REGSEL))); -#endif } -#if 0 /* Unused for now... */ -static unsigned int snd_cs4231_get_count(unsigned char format, unsigned int size) -{ - switch (format & 0xe0) { - case CS4231_LINEAR_16: - case CS4231_LINEAR_16_BIG: - size >>= 1; - break; - case CS4231_ADPCM_16: - return size >> 2; - } - if (format & CS4231_STEREO) - size >>= 1; - return size; -} -#endif - #ifdef EBUS_SUPPORT static void snd_cs4231_ebus_advance_dma(struct ebus_dma_info *p, snd_pcm_substream_t *substream, unsigned int *periods_sent) { snd_pcm_runtime_t *runtime = substream->runtime; while (1) { - unsigned int dma_size = snd_pcm_lib_period_bytes(substream); - unsigned int offset = dma_size * (*periods_sent); + unsigned int period_size = snd_pcm_lib_period_bytes(substream); + unsigned int offset = period_size * (*periods_sent); - if (dma_size >= (1 << 24)) + if (period_size >= (1 << 24)) BUG(); - if (ebus_dma_request(p, runtime->dma_addr + offset, dma_size)) + if (ebus_dma_request(p, runtime->dma_addr + offset, period_size)) return; -#if 0 - printk("ebus_advance: Sent period %u (size[%x] offset[%x])\n", - (*periods_sent), dma_size, offset); -#endif (*periods_sent) = ((*periods_sent) + 1) % runtime->periods; } } #endif -static void cs4231_dma_trigger(cs4231_t *chip, unsigned int what, int on) +#ifdef SBUS_SUPPORT +static void snd_cs4231_sbus_advance_dma(snd_pcm_substream_t *substream, unsigned int *periods_sent) +{ + cs4231_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + unsigned int period_size = snd_pcm_lib_period_bytes(substream); + unsigned int offset = period_size * (*periods_sent % runtime->periods); + + if (runtime->period_size > 0xffff + 1) + BUG(); + + switch (substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + sbus_writel(runtime->dma_addr + offset, chip->port + APCPNVA); + sbus_writel(period_size, chip->port + APCPNC); + break; + case SNDRV_PCM_STREAM_CAPTURE: + sbus_writel(runtime->dma_addr + offset, chip->port + APCCNVA); + sbus_writel(period_size, chip->port + APCCNC); + break; + } + + (*periods_sent) = (*periods_sent + 1) % runtime->periods; +} +#endif + +static void cs4231_dma_trigger(snd_pcm_substream_t *substream, unsigned int what, int on) { + cs4231_t *chip = snd_pcm_substream_chip(substream); + #ifdef EBUS_SUPPORT if (chip->flags & CS4231_FLAG_EBUS) { if (what & CS4231_PLAYBACK_ENABLE) { @@ -694,6 +646,60 @@ static void cs4231_dma_trigger(cs4231_t *chip, unsigned int what, int on) } else { #endif #ifdef SBUS_SUPPORT + u32 csr = sbus_readl(chip->port + APCCSR); + /* I don't know why, but on sbus the period counter must + * only start counting after the first period is sent. + * Therefore this dummy thing. + */ + unsigned int dummy = 0; + + switch (what) { + case CS4231_PLAYBACK_ENABLE: + if (on) { + csr &= ~APC_XINT_PLAY; + sbus_writel(csr, chip->port + APCCSR); + + csr &= ~APC_PPAUSE; + sbus_writel(csr, chip->port + APCCSR); + + snd_cs4231_sbus_advance_dma(substream, &dummy); + + csr |= APC_GENL_INT | APC_PLAY_INT | APC_XINT_ENA | + APC_XINT_PLAY | APC_XINT_EMPT | APC_XINT_GENL | + APC_XINT_PENA | APC_PDMA_READY; + sbus_writel(csr, chip->port + APCCSR); + } else { + csr |= APC_PPAUSE; + sbus_writel(csr, chip->port + APCCSR); + + csr &= ~APC_PDMA_READY; + sbus_writel(csr, chip->port + APCCSR); + } + break; + case CS4231_RECORD_ENABLE: + if (on) { + csr &= ~APC_XINT_CAPT; + sbus_writel(csr, chip->port + APCCSR); + + csr &= ~APC_CPAUSE; + sbus_writel(csr, chip->port + APCCSR); + + snd_cs4231_sbus_advance_dma(substream, &dummy); + + csr |= APC_GENL_INT | APC_CAPT_INT | APC_XINT_ENA | + APC_XINT_CAPT | APC_XINT_CEMP | APC_XINT_GENL | + APC_CDMA_READY; + + sbus_writel(csr, chip->port + APCCSR); + } else { + csr |= APC_CPAUSE; + sbus_writel(csr, chip->port + APCCSR); + + csr &= ~APC_CDMA_READY; + sbus_writel(csr, chip->port + APCCSR); + } + break; + } #endif #ifdef EBUS_SUPPORT } @@ -725,25 +731,12 @@ static int snd_cs4231_trigger(snd_pcm_substream_t *substream, int cmd) } } -#if 0 - printk("TRIGGER: what[%x] on(%d)\n", - what, (cmd == SNDRV_PCM_TRIGGER_START)); -#endif - spin_lock_irqsave(&chip->lock, flags); if (cmd == SNDRV_PCM_TRIGGER_START) { - cs4231_dma_trigger(chip, what, 1); + cs4231_dma_trigger(substream, what, 1); chip->image[CS4231_IFACE_CTRL] |= what; - if (what & CS4231_PLAYBACK_ENABLE) { - snd_cs4231_out(chip, CS4231_PLY_LWR_CNT, 0xff); - snd_cs4231_out(chip, CS4231_PLY_UPR_CNT, 0xff); - } - if (what & CS4231_RECORD_ENABLE) { - snd_cs4231_out(chip, CS4231_REC_LWR_CNT, 0xff); - snd_cs4231_out(chip, CS4231_REC_UPR_CNT, 0xff); - } } else { - cs4231_dma_trigger(chip, what, 0); + cs4231_dma_trigger(substream, what, 0); chip->image[CS4231_IFACE_CTRL] &= ~what; } snd_cs4231_out(chip, CS4231_IFACE_CTRL, @@ -755,9 +748,7 @@ static int snd_cs4231_trigger(snd_pcm_substream_t *substream, int cmd) result = -EINVAL; break; } -#if 0 - snd_cs4231_debug(chip); -#endif + return result; } @@ -790,9 +781,6 @@ static unsigned char snd_cs4231_get_format(cs4231_t *chip, int format, int chann } if (channels > 1) rformat |= CS4231_STEREO; -#if 0 - snd_printk("get_format: 0x%x (mode=0x%x)\n", format, mode); -#endif return rformat; } @@ -944,7 +932,7 @@ static void snd_cs4231_init(cs4231_t *chip) snd_cs4231_mce_down(chip); #ifdef SNDRV_DEBUG_MCE - snd_printk("init: (1)\n"); + snd_printdd("init: (1)\n"); #endif snd_cs4231_mce_up(chip); spin_lock_irqsave(&chip->lock, flags); @@ -957,7 +945,7 @@ static void snd_cs4231_init(cs4231_t *chip) snd_cs4231_mce_down(chip); #ifdef SNDRV_DEBUG_MCE - snd_printk("init: (2)\n"); + snd_printdd("init: (2)\n"); #endif snd_cs4231_mce_up(chip); @@ -967,7 +955,7 @@ static void snd_cs4231_init(cs4231_t *chip) snd_cs4231_mce_down(chip); #ifdef SNDRV_DEBUG_MCE - snd_printk("init: (3) - afei = 0x%x\n", chip->image[CS4231_ALT_FEATURE_1]); + snd_printdd("init: (3) - afei = 0x%x\n", chip->image[CS4231_ALT_FEATURE_1]); #endif spin_lock_irqsave(&chip->lock, flags); @@ -981,7 +969,7 @@ static void snd_cs4231_init(cs4231_t *chip) snd_cs4231_mce_down(chip); #ifdef SNDRV_DEBUG_MCE - snd_printk("init: (4)\n"); + snd_printdd("init: (4)\n"); #endif snd_cs4231_mce_up(chip); @@ -991,7 +979,7 @@ static void snd_cs4231_init(cs4231_t *chip) snd_cs4231_mce_down(chip); #ifdef SNDRV_DEBUG_MCE - snd_printk("init: (5)\n"); + snd_printdd("init: (5)\n"); #endif } @@ -1022,6 +1010,7 @@ static int snd_cs4231_open(cs4231_t *chip, unsigned int mode) CS4231_RECORD_IRQ | CS4231_TIMER_IRQ); snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0); + spin_unlock_irqrestore(&chip->lock, flags); chip->mode = mode; @@ -1136,11 +1125,21 @@ static int snd_cs4231_playback_hw_free(snd_pcm_substream_t *substream) static int snd_cs4231_playback_prepare(snd_pcm_substream_t *substream) { cs4231_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; unsigned long flags; spin_lock_irqsave(&chip->lock, flags); + chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO); + + if (runtime->period_size > 0xffff + 1) + BUG(); + + snd_cs4231_out(chip, CS4231_PLY_LWR_CNT, (runtime->period_size - 1) & 0x00ff); + snd_cs4231_out(chip, CS4231_PLY_UPR_CNT, (runtime->period_size - 1) >> 8 & 0x00ff); + chip->p_periods_sent = 0; + spin_unlock_irqrestore(&chip->lock, flags); return 0; @@ -1172,12 +1171,16 @@ static int snd_cs4231_capture_hw_free(snd_pcm_substream_t *substream) static int snd_cs4231_capture_prepare(snd_pcm_substream_t *substream) { cs4231_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; unsigned long flags; spin_lock_irqsave(&chip->lock, flags); chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_RECORD_ENABLE | CS4231_RECORD_PIO); + snd_cs4231_out(chip, CS4231_REC_LWR_CNT, (runtime->period_size - 1) & 0x00ff); + snd_cs4231_out(chip, CS4231_REC_LWR_CNT, (runtime->period_size - 1) >> 8 & 0x00ff); + spin_unlock_irqrestore(&chip->lock, flags); return 0; @@ -1196,53 +1199,61 @@ static void snd_cs4231_overrange(cs4231_t *chip) chip->capture_substream->runtime->overrange++; } -static void snd_cs4231_generic_interrupt(cs4231_t *chip) +static irqreturn_t snd_cs4231_generic_interrupt(cs4231_t *chip) { unsigned long flags; unsigned char status; + /*This is IRQ is not raised by the cs4231*/ + if (!(__cs4231_readb(chip, CS4231P(chip, STATUS)) & CS4231_GLOBALIRQ)) + return IRQ_NONE; + status = snd_cs4231_in(chip, CS4231_IRQ_STATUS); - if (!status) - return; if (status & CS4231_TIMER_IRQ) { if (chip->timer) snd_timer_interrupt(chip->timer, chip->timer->sticks); } - if (status & CS4231_PLAYBACK_IRQ) - snd_pcm_period_elapsed(chip->playback_substream); - if (status & CS4231_RECORD_IRQ) { + + if (status & CS4231_RECORD_IRQ) snd_cs4231_overrange(chip); - snd_pcm_period_elapsed(chip->capture_substream); - } /* ACK the CS4231 interrupt. */ spin_lock_irqsave(&chip->lock, flags); snd_cs4231_outm(chip, CS4231_IRQ_STATUS, ~CS4231_ALL_IRQS | ~status, 0); spin_unlock_irqrestore(&chip->lock, flags); + + return 0; } #ifdef SBUS_SUPPORT static irqreturn_t snd_cs4231_sbus_interrupt(int irq, void *dev_id, struct pt_regs *regs) { cs4231_t *chip = dev_id; - u32 csr; - - csr = sbus_readl(chip->port + APCCSR); - if (!(csr & (APC_INT_PENDING | - APC_PLAY_INT | - APC_CAPT_INT | - APC_GENL_INT | - APC_XINT_PEMP | - APC_XINT_CEMP))) - return IRQ_NONE; /* ACK the APC interrupt. */ + u32 csr = sbus_readl(chip->port + APCCSR); + sbus_writel(csr, chip->port + APCCSR); - snd_cs4231_generic_interrupt(chip); + if ((chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE) && + (csr & APC_PLAY_INT) && + (csr & APC_XINT_PNVA) && + !(csr & APC_XINT_EMPT)) { + snd_cs4231_sbus_advance_dma(chip->playback_substream, + &chip->p_periods_sent); + snd_pcm_period_elapsed(chip->playback_substream); + } - return IRQ_HANDLED; + if ((chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE) && + (csr & APC_CAPT_INT) && + (csr & APC_XINT_CNVA)) { + snd_cs4231_sbus_advance_dma(chip->capture_substream, + &chip->c_periods_sent); + snd_pcm_period_elapsed(chip->capture_substream); + } + + return snd_cs4231_generic_interrupt(chip); } #endif @@ -1290,7 +1301,8 @@ static snd_pcm_uframes_t snd_cs4231_playback_pointer(snd_pcm_substream_t *substr #ifdef EBUS_SUPPORT } #endif - ptr += (period_bytes - residue); + ptr += period_bytes - residue; + return bytes_to_frames(substream->runtime, ptr); } @@ -1314,7 +1326,7 @@ static snd_pcm_uframes_t snd_cs4231_capture_pointer(snd_pcm_substream_t * substr #ifdef EBUS_SUPPORT } #endif - ptr += (period_bytes - residue); + ptr += period_bytes - residue; return bytes_to_frames(substream->runtime, ptr); } @@ -1328,9 +1340,6 @@ static int snd_cs4231_probe(cs4231_t *chip) int i, id, vers; unsigned char *ptr; -#if 0 - snd_cs4231_debug(chip); -#endif id = vers = 0; for (i = 0; i < 50; i++) { mb(); @@ -1985,13 +1994,13 @@ static int __init snd_cs4231_sbus_create(snd_card_t *card, chip->port = sbus_ioremap(&sdev->resource[0], 0, chip->regs_size, "cs4231"); if (!chip->port) { - snd_printk("cs4231-%d: Unable to map chip registers.\n", dev); + snd_printdd("cs4231-%d: Unable to map chip registers.\n", dev); return -EIO; } if (request_irq(sdev->irqs[0], snd_cs4231_sbus_interrupt, SA_SHIRQ, "cs4231", chip)) { - snd_printk("cs4231-%d: Unable to grab SBUS IRQ %s\n", + snd_printdd("cs4231-%d: Unable to grab SBUS IRQ %s\n", dev, __irq_itoa(sdev->irqs[0])); snd_cs4231_sbus_free(chip); @@ -2113,29 +2122,29 @@ static int __init snd_cs4231_ebus_create(snd_card_t *card, chip->eb2c.regs = ioremap(edev->resource[2].start, 0x10); if (!chip->port || !chip->eb2p.regs || !chip->eb2c.regs) { snd_cs4231_ebus_free(chip); - snd_printk("cs4231-%d: Unable to map chip registers.\n", dev); + snd_printdd("cs4231-%d: Unable to map chip registers.\n", dev); return -EIO; } if (ebus_dma_register(&chip->eb2c)) { snd_cs4231_ebus_free(chip); - snd_printk("cs4231-%d: Unable to register EBUS capture DMA\n", dev); + snd_printdd("cs4231-%d: Unable to register EBUS capture DMA\n", dev); return -EBUSY; } if (ebus_dma_irq_enable(&chip->eb2c, 1)) { snd_cs4231_ebus_free(chip); - snd_printk("cs4231-%d: Unable to enable EBUS capture IRQ\n", dev); + snd_printdd("cs4231-%d: Unable to enable EBUS capture IRQ\n", dev); return -EBUSY; } if (ebus_dma_register(&chip->eb2p)) { snd_cs4231_ebus_free(chip); - snd_printk("cs4231-%d: Unable to register EBUS play DMA\n", dev); + snd_printdd("cs4231-%d: Unable to register EBUS play DMA\n", dev); return -EBUSY; } if (ebus_dma_irq_enable(&chip->eb2p, 1)) { snd_cs4231_ebus_free(chip); - snd_printk("cs4231-%d: Unable to enable EBUS play IRQ\n", dev); + snd_printdd("cs4231-%d: Unable to enable EBUS play IRQ\n", dev); return -EBUSY; } |