--- linux-2.6.4-rc2/arch/alpha/kernel/alpha_ksyms.c 2004-03-03 23:12:42.000000000 -0800 +++ 25/arch/alpha/kernel/alpha_ksyms.c 2004-03-07 20:47:47.000000000 -0800 @@ -35,9 +35,6 @@ #include #include -#define __KERNEL_SYSCALLS__ -#include - extern struct hwrpb_struct *hwrpb; extern void dump_thread(struct pt_regs *, struct user *); extern spinlock_t rtc_lock; --- linux-2.6.4-rc2/arch/alpha/kernel/ptrace.c 2003-06-14 12:18:33.000000000 -0700 +++ 25/arch/alpha/kernel/ptrace.c 2004-03-07 20:46:45.000000000 -0800 @@ -369,8 +369,8 @@ do_sys_ptrace(long request, long pid, lo /* Mark single stepping. */ child->thread_info->bpt_nsaved = -1; clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - wake_up_process(child); child->exit_code = data; + wake_up_process(child); /* give it a chance to run. */ ret = 0; goto out; --- linux-2.6.4-rc2/arch/alpha/kernel/smp.c 2003-10-08 15:07:08.000000000 -0700 +++ 25/arch/alpha/kernel/smp.c 2004-03-07 20:47:47.000000000 -0800 @@ -39,9 +39,6 @@ #include #include -#define __KERNEL_SYSCALLS__ -#include - #include "proto.h" #include "irq_impl.h" --- linux-2.6.4-rc2/arch/arm26/Kconfig 2004-03-03 23:12:42.000000000 -0800 +++ 25/arch/arm26/Kconfig 2004-03-07 20:46:53.000000000 -0800 @@ -198,11 +198,6 @@ source "drivers/input/Kconfig" source "drivers/char/Kconfig" -config KBDMOUSE - bool - depends on ARCH_ACORN && BUSMOUSE=y - default y - source "drivers/media/Kconfig" source "fs/Kconfig" --- linux-2.6.4-rc2/arch/arm/common/sa1111-pcibuf.c 2004-03-03 23:12:42.000000000 -0800 +++ 25/arch/arm/common/sa1111-pcibuf.c 2004-03-07 20:46:58.000000000 -0800 @@ -457,8 +457,8 @@ void sa1111_unmap_sg(struct device *dev, local_irq_restore(flags); } -void sa1111_dma_sync_single(struct device *dev, dma_addr_t dma_addr, - size_t size, enum dma_data_direction dir) +void sa1111_dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_addr, + size_t size, enum dma_data_direction dir) { unsigned long flags; @@ -472,8 +472,44 @@ void sa1111_dma_sync_single(struct devic local_irq_restore(flags); } -void sa1111_dma_sync_sg(struct device *dev, struct scatterlist *sg, - int nents, enum dma_data_direction dir) +void sa1111_dma_sync_single_for_device(struct device *dev, dma_addr_t dma_addr, + size_t size, enum dma_data_direction dir) +{ + unsigned long flags; + + dev_dbg(dev, "%s(ptr=%08lx,size=%d,dir=%x)\n", + __func__, dma_addr, size, dir); + + local_irq_save(flags); + + sync_single(dev, dma_addr, size, dir); + + local_irq_restore(flags); +} + +void sa1111_dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, + int nents, enum dma_data_direction dir) +{ + unsigned long flags; + int i; + + dev_dbg(dev, "%s(sg=%p,nents=%d,dir=%x)\n", + __func__, sg, nents, dir); + + local_irq_save(flags); + + for (i = 0; i < nents; i++, sg++) { + dma_addr_t dma_addr = sg->dma_address; + unsigned int length = sg->length; + + sync_single(dev, dma_addr, length, dir); + } + + local_irq_restore(flags); +} + +void sa1111_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, + int nents, enum dma_data_direction dir) { unsigned long flags; int i; @@ -497,8 +533,10 @@ EXPORT_SYMBOL(sa1111_map_single); EXPORT_SYMBOL(sa1111_unmap_single); EXPORT_SYMBOL(sa1111_map_sg); EXPORT_SYMBOL(sa1111_unmap_sg); -EXPORT_SYMBOL(sa1111_dma_sync_single); -EXPORT_SYMBOL(sa1111_dma_sync_sg); +EXPORT_SYMBOL(sa1111_dma_sync_single_for_cpu); +EXPORT_SYMBOL(sa1111_dma_sync_single_for_device); +EXPORT_SYMBOL(sa1111_dma_sync_sg_for_cpu); +EXPORT_SYMBOL(sa1111_dma_sync_sg_for_device); /* **************************************** */ --- linux-2.6.4-rc2/arch/arm/kernel/armksyms.c 2004-03-03 23:12:42.000000000 -0800 +++ 25/arch/arm/kernel/armksyms.c 2004-03-07 20:46:45.000000000 -0800 @@ -187,6 +187,7 @@ EXPORT_SYMBOL(__arch_copy_from_user); EXPORT_SYMBOL(__arch_copy_to_user); EXPORT_SYMBOL(__arch_clear_user); EXPORT_SYMBOL(__arch_strnlen_user); +EXPORT_SYMBOL(__arch_strncpy_from_user); /* consistent area handling */ EXPORT_SYMBOL(consistent_alloc); --- linux-2.6.4-rc2/arch/arm/Makefile 2004-03-03 23:12:42.000000000 -0800 +++ 25/arch/arm/Makefile 2004-03-07 20:46:45.000000000 -0800 @@ -23,6 +23,11 @@ CFLAGS += -mbig-endian AS += -EB LD += -EB AFLAGS += -mbig-endian +else +CFLAGS += -mlittle-endian +AS += -EL +LD += -EL +AFLAGS += -mlittle-endian endif comma = , --- linux-2.6.4-rc2/arch/arm/mm/Kconfig 2004-03-03 23:12:42.000000000 -0800 +++ 25/arch/arm/mm/Kconfig 2004-03-07 20:46:45.000000000 -0800 @@ -347,7 +347,7 @@ config CPU_BIG_ENDIAN help Say Y if you plan on running a kernel in big-endian mode. Note that your board must be properly built and your board - port must properly enable and big-endian related features + port must properly enable any big-endian related features of your chipset/board/processor. config CPU_ICACHE_DISABLE --- linux-2.6.4-rc2/arch/cris/kernel/process.c 2003-10-08 15:07:08.000000000 -0700 +++ 25/arch/cris/kernel/process.c 2004-03-07 20:47:47.000000000 -0800 @@ -91,8 +91,6 @@ * This file handles the architecture-dependent parts of process handling.. */ -#define __KERNEL_SYSCALLS__ - #include #include #include --- linux-2.6.4-rc2/arch/h8300/kernel/syscalls.S 2003-08-22 19:23:40.000000000 -0700 +++ 25/arch/h8300/kernel/syscalls.S 2004-03-07 20:47:07.000000000 -0800 @@ -116,7 +116,7 @@ SYMBOL_NAME_LABEL(sys_call_table) .long SYMBOL_NAME(sys_ni_syscall) /* old profil syscall holder */ .long SYMBOL_NAME(sys_statfs) .long SYMBOL_NAME(sys_fstatfs) /* 100 */ - .long SYMBOL_NAME(sys_ioperm) + .long SYMBOL_NAME(sys_ni_syscall) /* ioperm for i386 */ .long SYMBOL_NAME(sys_socketcall) .long SYMBOL_NAME(sys_syslog) .long SYMBOL_NAME(sys_setitimer) --- linux-2.6.4-rc2/arch/h8300/kernel/sys_h8300.c 2004-03-03 23:12:42.000000000 -0800 +++ 25/arch/h8300/kernel/sys_h8300.c 2004-03-07 20:47:07.000000000 -0800 @@ -260,11 +260,6 @@ asmlinkage int sys_ipc (uint call, int f return -EINVAL; } -asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int on) -{ - return -ENOSYS; -} - /* sys_cacheflush -- no support. */ asmlinkage int sys_cacheflush (unsigned long addr, int scope, int cache, unsigned long len) --- linux-2.6.4-rc2/arch/i386/boot/setup.S 2004-03-03 23:12:42.000000000 -0800 +++ 25/arch/i386/boot/setup.S 2004-03-07 20:48:18.000000000 -0800 @@ -164,7 +164,7 @@ cmd_line_ptr: .long 0 # (Header versio # can be located anywhere in # low memory 0x10000 or higher. -ramdisk_max: .long MAXMEM-1 # (Header version 0x0203 or later) +ramdisk_max: .long __MAXMEM-1 # (Header version 0x0203 or later) # The highest safe address for # the contents of an initrd --- linux-2.6.4-rc2/arch/i386/boot/tools/build.c 2003-06-14 12:18:21.000000000 -0700 +++ 25/arch/i386/boot/tools/build.c 2004-03-07 20:47:46.000000000 -0800 @@ -150,10 +150,8 @@ int main(int argc, char ** argv) sz = sb.st_size; fprintf (stderr, "System is %d kB\n", sz/1024); sys_size = (sz + 15) / 16; - /* 0x40000*16 = 4.0 MB, reasonable estimate for the current maximum */ - if (sys_size > (is_big_kernel ? 0x40000 : DEF_SYSSIZE)) - die("System is too big. Try using %smodules.", - is_big_kernel ? "" : "bzImage or "); + if (!is_big_kernel && sys_size > DEF_SYSSIZE) + die("System is too big. Try using bzImage or modules."); while (sz > 0) { int l, n; --- linux-2.6.4-rc2/arch/i386/defconfig 2004-03-03 23:12:42.000000000 -0800 +++ 25/arch/i386/defconfig 2004-03-07 20:47:46.000000000 -0800 @@ -1212,5 +1212,4 @@ CONFIG_CRC32=y CONFIG_X86_SMP=y CONFIG_X86_HT=y CONFIG_X86_BIOS_REBOOT=y -CONFIG_X86_TRAMPOLINE=y CONFIG_PC=y --- linux-2.6.4-rc2/arch/i386/Kconfig 2004-03-03 23:12:42.000000000 -0800 +++ 25/arch/i386/Kconfig 2004-03-07 20:48:20.000000000 -0800 @@ -269,9 +269,6 @@ config MK8 use of some extended instructions, and passes appropriate optimization flags to GCC. -config MELAN - bool "Elan" - config MCRUSOE bool "Crusoe" help @@ -421,6 +418,54 @@ config X86_OOSTORE depends on (MWINCHIP3D || MWINCHIP2 || MWINCHIPC6) && MTRR default y +config X86_4G + bool "4 GB kernel-space and 4 GB user-space virtual memory support" + help + This option is only useful for systems that have more than 1 GB + of RAM. + + The default kernel VM layout leaves 1 GB of virtual memory for + kernel-space mappings, and 3 GB of VM for user-space applications. + This option ups both the kernel-space VM and the user-space VM to + 4 GB. + + The cost of this option is additional TLB flushes done at + system-entry points that transition from user-mode into kernel-mode. + I.e. system calls and page faults, and IRQs that interrupt user-mode + code. There's also additional overhead to kernel operations that copy + memory to/from user-space. The overhead from this is hard to tell and + depends on the workload - it can be anything from no visible overhead + to 20-30% overhead. A good rule of thumb is to count with a runtime + overhead of 20%. + + The upside is the much increased kernel-space VM, which more than + quadruples the maximum amount of RAM supported. Kernels compiled with + this option boot on 64GB of RAM and still have more than 3.1 GB of + 'lowmem' left. Another bonus is that highmem IO bouncing decreases, + if used with drivers that still use bounce-buffers. + + There's also a 33% increase in user-space VM size - database + applications might see a boost from this. + + But the cost of the TLB flushes and the runtime overhead has to be + weighed against the bonuses offered by the larger VM spaces. The + dividing line depends on the actual workload - there might be 4 GB + systems that benefit from this option. Systems with less than 4 GB + of RAM will rarely see a benefit from this option - but it's not + out of question, the exact circumstances have to be considered. + +config X86_SWITCH_PAGETABLES + def_bool X86_4G + +config X86_4G_VM_LAYOUT + def_bool X86_4G + +config X86_UACCESS_INDIRECT + def_bool X86_4G + +config X86_HIGH_ENTRY + def_bool X86_4G + config HPET_TIMER bool "HPET Timer Support" help @@ -478,6 +523,16 @@ config NR_CPUS This is purely to save memory - each supported CPU adds approximately eight kilobytes to the kernel image. +config SCHED_SMT + bool "SMT (Hyperthreading) scheduler support" + depends on SMP + default off + help + SMT scheduler support improves the CPU scheduler's decision making + when dealing with Intel Pentium 4 chips with HyperThreading at a + cost of slightly increased overhead in some places. If unsure say + N here. + config PREEMPT bool "Preemptible Kernel" help @@ -552,7 +607,7 @@ config X86_MCE the 386 and 486, so nearly everyone can say Y here. config X86_MCE_NONFATAL - bool "Check for non-fatal errors on AMD Athlon/Duron / Intel Pentium 4" + tristate "Check for non-fatal errors on AMD Athlon/Duron / Intel Pentium 4" depends on X86_MCE help Enabling this feature starts a timer that triggers every 5 seconds which @@ -1068,12 +1123,16 @@ config PCI_GOBIOS PCI-based systems don't have any BIOS at all. Linux can also try to detect the PCI hardware directly without using the BIOS. - With this option, you can specify how Linux should detect the PCI - devices. If you choose "BIOS", the BIOS will be used, if you choose - "Direct", the BIOS won't be used, and if you choose "Any", the - kernel will try the direct access method and falls back to the BIOS - if that doesn't work. If unsure, go with the default, which is - "Any". + With this option, you can specify how Linux should detect the + PCI devices. If you choose "BIOS", the BIOS will be used, + if you choose "Direct", the BIOS won't be used, and if you + choose "MMConfig", then PCI Express MMCONFIG will be used. + If you choose "Any", the kernel will try MMCONFIG, then the + direct access method and falls back to the BIOS if that doesn't + work. If unsure, go with the default, which is "Any". + +config PCI_GOMMCONFIG + bool "MMConfig" config PCI_GODIRECT bool "Direct" @@ -1093,6 +1152,12 @@ config PCI_DIRECT depends on PCI && ((PCI_GODIRECT || PCI_GOANY) || X86_VISWS) default y +config PCI_MMCONFIG + bool + depends on PCI && (PCI_GOMMCONFIG || PCI_GOANY) + select ACPI_BOOT + default y + config PCI_USE_VECTOR bool "Vector-based interrupt indexing (MSI)" depends on X86_LOCAL_APIC && X86_IO_APIC @@ -1231,17 +1296,6 @@ config DEBUG_SLAB allocation as well as poisoning memory on free to catch use of freed memory. -config DEBUG_IOVIRT - bool "Memory mapped I/O debugging" - depends on DEBUG_KERNEL - help - Say Y here to get warned whenever an attempt is made to do I/O on - obviously invalid addresses such as those generated when ioremap() - calls are forgotten. Memory mapped I/O will go through an extra - check to catch access to unmapped ISA addresses, an access method - that can still be used by old drivers that are being ported from - 2.0/2.2. - config MAGIC_SYSRQ bool "Magic SysRq key" depends on DEBUG_KERNEL @@ -1273,6 +1327,15 @@ config DEBUG_PAGEALLOC This results in a large slowdown, but helps to find certain types of memory corruptions. +config SPINLINE + bool "Spinlock inlining" + depends on DEBUG_KERNEL + help + This will change spinlocks from out of line to inline, making them + account cost to the callers in readprofile, rather than the lock + itself (as ".text.lock.filename"). This can be helpful for finding + the callers of locks. + config DEBUG_HIGHMEM bool "Highmem debugging" depends on DEBUG_KERNEL && HIGHMEM @@ -1289,20 +1352,217 @@ config DEBUG_INFO Say Y here only if you plan to use gdb to debug the kernel. If you don't debug the kernel, you can say N. +config LOCKMETER + bool "Kernel lock metering" + depends on SMP + help + Say Y to enable kernel lock metering, which adds overhead to SMP locks, + but allows you to see various statistics using the lockstat command. + config DEBUG_SPINLOCK_SLEEP bool "Sleep-inside-spinlock checking" help If you say Y here, various routines which may sleep will become very noisy if they are called with a spinlock held. +config KGDB + bool "Include kgdb kernel debugger" + depends on DEBUG_KERNEL + help + If you say Y here, the system will be compiled with the debug + option (-g) and a debugging stub will be included in the + kernel. This stub communicates with gdb on another (host) + computer via a serial port. The host computer should have + access to the kernel binary file (vmlinux) and a serial port + that is connected to the target machine. Gdb can be made to + configure the serial port or you can use stty and setserial to + do this. See the 'target' command in gdb. This option also + configures in the ability to request a breakpoint early in the + boot process. To request the breakpoint just include 'kgdb' + as a boot option when booting the target machine. The system + will then break as soon as it looks at the boot options. This + option also installs a breakpoint in panic and sends any + kernel faults to the debugger. For more information see the + Documentation/i386/kgdb/kgdb.txt file. + +choice + depends on KGDB + prompt "Debug serial port BAUD" + default KGDB_115200BAUD + help + Gdb and the kernel stub need to agree on the baud rate to be + used. Some systems (x86 family at this writing) allow this to + be configured. + +config KGDB_9600BAUD + bool "9600" + +config KGDB_19200BAUD + bool "19200" + +config KGDB_38400BAUD + bool "38400" + +config KGDB_57600BAUD + bool "57600" + +config KGDB_115200BAUD + bool "115200" +endchoice + +config KGDB_PORT + hex "hex I/O port address of the debug serial port" + depends on KGDB + default 3f8 + help + Some systems (x86 family at this writing) allow the port + address to be configured. The number entered is assumed to be + hex, don't put 0x in front of it. The standard address are: + COM1 3f8 , irq 4 and COM2 2f8 irq 3. Setserial /dev/ttySx + will tell you what you have. It is good to test the serial + connection with a live system before trying to debug. + +config KGDB_IRQ + int "IRQ of the debug serial port" + depends on KGDB + default 4 + help + This is the irq for the debug port. If everything is working + correctly and the kernel has interrupts on a control C to the + port should cause a break into the kernel debug stub. + +config DEBUG_INFO + bool + depends on KGDB + default y + +config KGDB_MORE + bool "Add any additional compile options" + depends on KGDB + default n + help + Saying yes here turns on the ability to enter additional + compile options. + + +config KGDB_OPTIONS + depends on KGDB_MORE + string "Additional compile arguments" + default "-O1" + help + This option allows you enter additional compile options for + the whole kernel compile. Each platform will have a default + that seems right for it. For example on PPC "-ggdb -O1", and + for i386 "-O1". Note that by configuring KGDB "-g" is already + turned on. In addition, on i386 platforms + "-fomit-frame-pointer" is deleted from the standard compile + options. + +config NO_KGDB_CPUS + int "Number of CPUs" + depends on KGDB && SMP + default NR_CPUS + help + + This option sets the number of cpus for kgdb ONLY. It is used + to prune some internal structures so they look "nice" when + displayed with gdb. This is to overcome possibly larger + numbers that may have been entered above. Enter the real + number to get nice clean kgdb_info displays. + +config KGDB_TS + bool "Enable kgdb time stamp macros?" + depends on KGDB + default n + help + Kgdb event macros allow you to instrument your code with calls + to the kgdb event recording function. The event log may be + examined with gdb at a break point. Turning on this + capability also allows you to choose how many events to + keep. Kgdb always keeps the lastest events. + +choice + depends on KGDB_TS + prompt "Max number of time stamps to save?" + default KGDB_TS_128 + +config KGDB_TS_64 + bool "64" + +config KGDB_TS_128 + bool "128" + +config KGDB_TS_256 + bool "256" + +config KGDB_TS_512 + bool "512" + +config KGDB_TS_1024 + bool "1024" + +endchoice + +config STACK_OVERFLOW_TEST + bool "Turn on kernel stack overflow testing?" + depends on KGDB + default n + help + This option enables code in the front line interrupt handlers + to check for kernel stack overflow on interrupts and system + calls. This is part of the kgdb code on x86 systems. + +config KGDB_CONSOLE + bool "Enable serial console thru kgdb port" + depends on KGDB + default n + help + This option enables the command line "console=kgdb" option. + When the system is booted with this option in the command line + all kernel printk output is sent to gdb (as well as to other + consoles). For this to work gdb must be connected. For this + reason, this command line option will generate a breakpoint if + gdb has not yet connected. After the gdb continue command is + given all pent up console output will be printed by gdb on the + host machine. Neither this option, nor KGDB require the + serial driver to be configured. + +config KGDB_SYSRQ + bool "Turn on SysRq 'G' command to do a break?" + depends on KGDB + default y + help + This option includes an option in the SysRq code that allows + you to enter SysRq G which generates a breakpoint to the KGDB + stub. This will work if the keyboard is alive and can + interrupt the system. Because of constraints on when the + serial port interrupt can be enabled, this code may allow you + to interrupt the system before the serial port control C is + available. Just say yes here. + config FRAME_POINTER bool "Compile the kernel with frame pointers" + default KGDB help If you say Y here the resulting kernel image will be slightly larger and slower, but it will give very useful debugging information. If you don't debug the kernel, you can say N, but we may not be able to solve problems without frame pointers. +config MAGIC_SYSRQ + bool + depends on KGDB_SYSRQ + default y + +config 4KSTACKS + bool "Use 4Kb for kernel stacks instead of 8Kb" + help + If you say Y here the kernel will use a 4Kb stacksize for the + kernel stack attached to each process/thread. This facilitates + running more threads on a system and also reduces the pressure + on the VM subsystem for higher order allocations. This option + will also use IRQ stacks to compensate for the reduced stackspace. + config X86_FIND_SMP_CONFIG bool depends on X86_LOCAL_APIC || X86_VOYAGER @@ -1336,11 +1596,6 @@ config X86_BIOS_REBOOT depends on !(X86_VISWS || X86_VOYAGER) default y -config X86_TRAMPOLINE - bool - depends on SMP || X86_VISWS - default y - config PC bool depends on X86 && !EMBEDDED --- linux-2.6.4-rc2/arch/i386/kernel/acpi/boot.c 2004-03-03 23:12:42.000000000 -0800 +++ 25/arch/i386/kernel/acpi/boot.c 2004-03-07 20:48:18.000000000 -0800 @@ -35,7 +35,7 @@ #include #include -#if defined (CONFIG_X86_LOCAL_APIC) +#ifdef CONFIG_X86_LOCAL_APIC #include #include #include @@ -43,17 +43,26 @@ #define PREFIX "ACPI: " -int acpi_noirq __initdata = 0; /* skip ACPI IRQ initialization */ +int acpi_noirq __initdata; /* skip ACPI IRQ initialization */ int acpi_ht __initdata = 1; /* enable HT */ int acpi_lapic; int acpi_ioapic; +int acpi_strict; + +#ifdef CONFIG_X86_LOCAL_APIC +static u64 acpi_lapic_addr __initdata = APIC_DEFAULT_PHYS_BASE; +#endif /* -------------------------------------------------------------------------- Boot-time Configuration -------------------------------------------------------------------------- */ -enum acpi_irq_model_id acpi_irq_model; +/* + * The default interrupt routing model is PIC (8259). This gets + * overriden if IOAPICs are enumerated (below). + */ +enum acpi_irq_model_id acpi_irq_model = ACPI_IRQ_MODEL_PIC; /* * Temporarily use the virtual area starting from FIX_IO_APIC_BASE_END, @@ -96,11 +105,32 @@ char *__acpi_map_table(unsigned long phy } -#ifdef CONFIG_X86_LOCAL_APIC +#ifdef CONFIG_PCI_MMCONFIG +static int __init acpi_parse_mcfg(unsigned long phys_addr, unsigned long size) +{ + struct acpi_table_mcfg *mcfg; -static u64 acpi_lapic_addr __initdata = APIC_DEFAULT_PHYS_BASE; + if (!phys_addr || !size) + return -EINVAL; + + mcfg = (struct acpi_table_mcfg *) __acpi_map_table(phys_addr, size); + if (!mcfg) { + printk(KERN_WARNING PREFIX "Unable to map MCFG\n"); + return -ENODEV; + } + if (mcfg->base_reserved) { + printk(KERN_ERR PREFIX "MMCONFIG not in low 4GB of memory\n"); + return -ENODEV; + } + + pci_mmcfg_base_addr = mcfg->base_address; + + return 0; +} +#endif /* CONFIG_PCI_MMCONFIG */ +#ifdef CONFIG_X86_LOCAL_APIC static int __init acpi_parse_madt ( unsigned long phys_addr, @@ -117,11 +147,12 @@ acpi_parse_madt ( return -ENODEV; } - if (madt->lapic_address) + if (madt->lapic_address) { acpi_lapic_addr = (u64) madt->lapic_address; - printk(KERN_INFO PREFIX "Local APIC address 0x%08x\n", - madt->lapic_address); + printk(KERN_DEBUG PREFIX "Local APIC address 0x%08x\n", + madt->lapic_address); + } acpi_madt_oem_check(madt->header.oem_id, madt->header.oem_table_id); @@ -251,7 +282,7 @@ acpi_parse_nmi_src ( return 0; } -#endif /*CONFIG_X86_IO_APIC*/ +#endif /* CONFIG_X86_IO_APIC */ #ifdef CONFIG_ACPI_BUS /* @@ -259,7 +290,7 @@ acpi_parse_nmi_src ( * programs the PIC-mode SCI to Level Trigger. * (NO-OP if the BIOS set Level Trigger already) * - * If a PIC-mode SCI is not recogznied or gives spurious IRQ7's + * If a PIC-mode SCI is not recognized or gives spurious IRQ7's * it may require Edge Trigger -- use "acpi_pic_sci=edge" * (NO-OP if the BIOS set Edge Trigger already) * @@ -339,7 +370,7 @@ acpi_scan_rsdp ( * RSDP signature. */ for (offset = 0; offset < length; offset += 16) { - if (strncmp((char *) (start + offset), "RSD PTR ", sig_len)) + if (strncmp((char *) __va(start + offset), "RSD PTR ", sig_len)) continue; return (start + offset); } @@ -347,6 +378,25 @@ acpi_scan_rsdp ( return 0; } +static int __init acpi_parse_sbf(unsigned long phys_addr, unsigned long size) +{ + struct acpi_table_sbf *sb; + + if (!phys_addr || !size) + return -EINVAL; + + sb = (struct acpi_table_sbf *) __acpi_map_table(phys_addr, size); + if (!sb) { + printk(KERN_WARNING PREFIX "Unable to map SBF\n"); + return -ENODEV; + } + + sbf_port = sb->sbf_cmos; /* Save CMOS port */ + + return 0; +} + + #ifdef CONFIG_HPET_TIMER extern unsigned long hpet_address; @@ -429,126 +479,61 @@ acpi_find_rsdp (void) return rsdp_phys; } +#ifdef CONFIG_X86_LOCAL_APIC /* - * acpi_boot_init() - * called from setup_arch(), always. - * 1. maps ACPI tables for later use - * 2. enumerates lapics - * 3. enumerates io-apics - * - * side effects: - * acpi_lapic = 1 if LAPIC found - * acpi_ioapic = 1 if IOAPIC found - * if (acpi_lapic && acpi_ioapic) smp_found_config = 1; - * if acpi_blacklisted() acpi_disabled = 1; - * acpi_irq_model=... - * ... - * - * return value: (currently ignored) - * 0: success - * !0: failure + * Parse LAPIC entries in MADT + * returns 0 on success, < 0 on error */ - -int __init -acpi_boot_init (void) +static int __init +acpi_parse_madt_lapic_entries(void) { - int result = 0; - - if (acpi_disabled && !acpi_ht) - return 1; - - /* - * The default interrupt routing model is PIC (8259). This gets - * overriden if IOAPICs are enumerated (below). - */ - acpi_irq_model = ACPI_IRQ_MODEL_PIC; - - /* - * Initialize the ACPI boot-time table parser. - */ - result = acpi_table_init(); - if (result) { - acpi_disabled = 1; - return result; - } - - result = acpi_blacklisted(); - if (result) { - printk(KERN_WARNING PREFIX "BIOS listed in blacklist, disabling ACPI support\n"); - acpi_disabled = 1; - return result; - } - -#ifdef CONFIG_X86_PM_TIMER - acpi_table_parse(ACPI_FADT, acpi_parse_fadt); -#endif - -#ifdef CONFIG_X86_LOCAL_APIC - - /* - * MADT - * ---- - * Parse the Multiple APIC Description Table (MADT), if exists. - * Note that this table provides platform SMP configuration - * information -- the successor to MPS tables. - */ - - result = acpi_table_parse(ACPI_APIC, acpi_parse_madt); - if (!result) { - return 0; - } - else if (result < 0) { - printk(KERN_ERR PREFIX "Error parsing MADT\n"); - return result; - } - else if (result > 1) - printk(KERN_WARNING PREFIX "Multiple MADT tables exist\n"); + int count; /* - * Local APIC - * ---------- * Note that the LAPIC address is obtained from the MADT (32-bit value) * and (optionally) overriden by a LAPIC_ADDR_OVR entry (64-bit value). */ - result = acpi_table_parse_madt(ACPI_MADT_LAPIC_ADDR_OVR, acpi_parse_lapic_addr_ovr, 0); - if (result < 0) { + count = acpi_table_parse_madt(ACPI_MADT_LAPIC_ADDR_OVR, acpi_parse_lapic_addr_ovr, 0); + if (count < 0) { printk(KERN_ERR PREFIX "Error parsing LAPIC address override entry\n"); - return result; + return count; } mp_register_lapic_address(acpi_lapic_addr); - result = acpi_table_parse_madt(ACPI_MADT_LAPIC, acpi_parse_lapic, + count = acpi_table_parse_madt(ACPI_MADT_LAPIC, acpi_parse_lapic, MAX_APICS); - if (!result) { + if (!count) { printk(KERN_ERR PREFIX "No LAPIC entries present\n"); /* TBD: Cleanup to allow fallback to MPS */ return -ENODEV; } - else if (result < 0) { + else if (count < 0) { printk(KERN_ERR PREFIX "Error parsing LAPIC entry\n"); /* TBD: Cleanup to allow fallback to MPS */ - return result; + return count; } - result = acpi_table_parse_madt(ACPI_MADT_LAPIC_NMI, acpi_parse_lapic_nmi, 0); - if (result < 0) { + count = acpi_table_parse_madt(ACPI_MADT_LAPIC_NMI, acpi_parse_lapic_nmi, 0); + if (count < 0) { printk(KERN_ERR PREFIX "Error parsing LAPIC NMI entry\n"); /* TBD: Cleanup to allow fallback to MPS */ - return result; + return count; } - - acpi_lapic = 1; - -#endif /*CONFIG_X86_LOCAL_APIC*/ + return 0; +} +#endif /* CONFIG_X86_LOCAL_APIC */ #if defined(CONFIG_X86_IO_APIC) && defined(CONFIG_ACPI_INTERPRETER) - - /* - * I/O APIC - * -------- - */ +/* + * Parse IOAPIC related entries in MADT + * returns 0 on success, < 0 on error + */ +static int __init +acpi_parse_madt_ioapic_entries(void) +{ + int count; /* * ACPI interpreter is required to complete interrupt setup, @@ -557,7 +542,7 @@ acpi_boot_init (void) * otherwise the system will stay in PIC mode */ if (acpi_disabled || acpi_noirq) { - return 1; + return -ENODEV; } /* @@ -566,54 +551,152 @@ acpi_boot_init (void) if (ioapic_setup_disabled()) { printk(KERN_INFO PREFIX "Skipping IOAPIC probe " "due to 'noapic' option.\n"); - return 1; + return -ENODEV; } - result = acpi_table_parse_madt(ACPI_MADT_IOAPIC, acpi_parse_ioapic, MAX_IO_APICS); - if (!result) { + count = acpi_table_parse_madt(ACPI_MADT_IOAPIC, acpi_parse_ioapic, MAX_IO_APICS); + if (!count) { printk(KERN_ERR PREFIX "No IOAPIC entries present\n"); return -ENODEV; } - else if (result < 0) { + else if (count < 0) { printk(KERN_ERR PREFIX "Error parsing IOAPIC entry\n"); - return result; + return count; } /* Build a default routing table for legacy (ISA) interrupts. */ mp_config_acpi_legacy_irqs(); - result = acpi_table_parse_madt(ACPI_MADT_INT_SRC_OVR, acpi_parse_int_src_ovr, NR_IRQ_VECTORS); - if (result < 0) { + count = acpi_table_parse_madt(ACPI_MADT_INT_SRC_OVR, acpi_parse_int_src_ovr, NR_IRQ_VECTORS); + if (count < 0) { printk(KERN_ERR PREFIX "Error parsing interrupt source overrides entry\n"); /* TBD: Cleanup to allow fallback to MPS */ - return result; + return count; } - result = acpi_table_parse_madt(ACPI_MADT_NMI_SRC, acpi_parse_nmi_src, NR_IRQ_VECTORS); - if (result < 0) { + count = acpi_table_parse_madt(ACPI_MADT_NMI_SRC, acpi_parse_nmi_src, NR_IRQ_VECTORS); + if (count < 0) { printk(KERN_ERR PREFIX "Error parsing NMI SRC entry\n"); /* TBD: Cleanup to allow fallback to MPS */ - return result; + return count; } - acpi_irq_model = ACPI_IRQ_MODEL_IOAPIC; + return 0; +} +#else +static inline int acpi_parse_madt_ioapic_entries(void) +{ + return -1; +} +#endif /* !(CONFIG_X86_IO_APIC && CONFIG_ACPI_INTERPRETER) */ - acpi_irq_balance_set(NULL); - acpi_ioapic = 1; +static void __init +acpi_process_madt(void) +{ +#ifdef CONFIG_X86_LOCAL_APIC + int count, error; -#endif /* CONFIG_X86_IO_APIC && CONFIG_ACPI_INTERPRETER */ + count = acpi_table_parse(ACPI_APIC, acpi_parse_madt); + if (count == 1) { -#ifdef CONFIG_X86_LOCAL_APIC - if (acpi_lapic && acpi_ioapic) { - smp_found_config = 1; - clustered_apic_check(); + /* + * Parse MADT LAPIC entries + */ + error = acpi_parse_madt_lapic_entries(); + if (!error) { + acpi_lapic = 1; + + /* + * Parse MADT IO-APIC entries + */ + error = acpi_parse_madt_ioapic_entries(); + if (!error) { + acpi_irq_model = ACPI_IRQ_MODEL_IOAPIC; + acpi_irq_balance_set(NULL); + acpi_ioapic = 1; + + smp_found_config = 1; + clustered_apic_check(); + } + } } #endif + return; +} + +/* + * acpi_boot_init() + * called from setup_arch(), always. + * 1. checksums all tables + * 2. enumerates lapics + * 3. enumerates io-apics + * + * side effects: + * acpi_lapic = 1 if LAPIC found + * acpi_ioapic = 1 if IOAPIC found + * if (acpi_lapic && acpi_ioapic) smp_found_config = 1; + * if acpi_blacklisted() acpi_disabled = 1; + * acpi_irq_model=... + * ... + * + * return value: (currently ignored) + * 0: success + * !0: failure + */ + +int __init +acpi_boot_init (void) +{ + int error; + + /* + * If acpi_disabled, bail out + * One exception: acpi=ht continues far enough to enumerate LAPICs + */ + if (acpi_disabled && !acpi_ht) + return 1; + + /* + * Initialize the ACPI boot-time table parser. + */ + error = acpi_table_init(); + if (error) { + acpi_disabled = 1; + return error; + } + + (void) acpi_table_parse(ACPI_BOOT, acpi_parse_sbf); + + /* + * blacklist may disable ACPI entirely + */ + error = acpi_blacklisted(); + if (error) { + printk(KERN_WARNING PREFIX "BIOS listed in blacklist, disabling ACPI support\n"); + acpi_disabled = 1; + return error; + } + + /* + * Process the Multiple APIC Description Table (MADT), if present + */ + acpi_process_madt(); + +#ifdef CONFIG_X86_PM_TIMER + acpi_table_parse(ACPI_FADT, acpi_parse_fadt); +#endif #ifdef CONFIG_HPET_TIMER - acpi_table_parse(ACPI_HPET, acpi_parse_hpet); + (void) acpi_table_parse(ACPI_HPET, acpi_parse_hpet); +#endif + +#ifdef CONFIG_PCI_MMCONFIG + error = acpi_table_parse(ACPI_MCFG, acpi_parse_mcfg); + if (error) + printk(KERN_ERR PREFIX "Error %d parsing MCFG\n", error); #endif return 0; } + --- linux-2.6.4-rc2/arch/i386/kernel/apic.c 2004-03-03 23:12:42.000000000 -0800 +++ 25/arch/i386/kernel/apic.c 2004-03-07 20:48:05.000000000 -0800 @@ -1182,9 +1182,6 @@ int __init APIC_init_uniprocessor (void) phys_cpu_present_map = physid_mask_of_physid(boot_cpu_physical_apicid); setup_local_APIC(); - - if (nmi_watchdog == NMI_LOCAL_APIC) - check_nmi_watchdog(); #ifdef CONFIG_X86_IO_APIC if (smp_found_config) if (!skip_ioapic_setup && nr_ioapics) --- linux-2.6.4-rc2/arch/i386/kernel/asm-offsets.c 2003-11-09 16:45:04.000000000 -0800 +++ 25/arch/i386/kernel/asm-offsets.c 2004-03-07 20:48:18.000000000 -0800 @@ -4,9 +4,11 @@ * to extract and format the required data. */ +#include #include #include #include "sigframe.h" +#include #define DEFINE(sym, val) \ asm volatile("\n->" #sym " %0 " #val : : "i" (val)) @@ -28,4 +30,20 @@ void foo(void) DEFINE(RT_SIGFRAME_sigcontext, offsetof (struct rt_sigframe, uc.uc_mcontext)); + + DEFINE(TI_task, offsetof (struct thread_info, task)); + DEFINE(TI_exec_domain, offsetof (struct thread_info, exec_domain)); + DEFINE(TI_flags, offsetof (struct thread_info, flags)); + DEFINE(TI_preempt_count, offsetof (struct thread_info, preempt_count)); + DEFINE(TI_addr_limit, offsetof (struct thread_info, addr_limit)); + DEFINE(TI_real_stack, offsetof (struct thread_info, real_stack)); + DEFINE(TI_virtual_stack, offsetof (struct thread_info, virtual_stack)); + DEFINE(TI_user_pgd, offsetof (struct thread_info, user_pgd)); + + DEFINE(FIX_ENTRY_TRAMPOLINE_0_addr, + __fix_to_virt(FIX_ENTRY_TRAMPOLINE_0)); + DEFINE(FIX_VSYSCALL_addr, __fix_to_virt(FIX_VSYSCALL)); + DEFINE(PAGE_SIZE_asm, PAGE_SIZE); + DEFINE(task_thread_db7, + offsetof (struct task_struct, thread.debugreg[7])); } --- linux-2.6.4-rc2/arch/i386/kernel/bootflag.c 2003-06-14 12:18:29.000000000 -0700 +++ 25/arch/i386/kernel/bootflag.c 2004-03-07 20:46:45.000000000 -0800 @@ -1,6 +1,5 @@ /* - * Implement 'Simple Boot Flag Specification 1.0' - * + * Implement 'Simple Boot Flag Specification 2.0' */ @@ -11,6 +10,7 @@ #include #include #include +#include #include #include @@ -23,56 +23,8 @@ #define SBF_PARITY (1<<7) -struct sbf_boot -{ - u8 sbf_signature[4]; - u32 sbf_len; - u8 sbf_revision __attribute((packed)); - u8 sbf_csum __attribute((packed)); - u8 sbf_oemid[6] __attribute((packed)); - u8 sbf_oemtable[8] __attribute((packed)); - u8 sbf_revdata[4] __attribute((packed)); - u8 sbf_creator[4] __attribute((packed)); - u8 sbf_crearev[4] __attribute((packed)); - u8 sbf_cmos __attribute((packed)); - u8 sbf_spare[3] __attribute((packed)); -}; - - -static int sbf_port __initdata = -1; - -static int __init sbf_struct_valid(unsigned long tptr) -{ - u8 *ap; - u8 v; - unsigned int i; - struct sbf_boot sb; - - memcpy_fromio(&sb, (void *)tptr, sizeof(sb)); - - if(sb.sbf_len != 40 && sb.sbf_len != 39) - // 39 on IBM ThinkPad A21m, BIOS version 1.02b (KXET24WW; 2000-12-19). - return 0; - - ap = (u8 *)&sb; - v= 0; - - for(i=0;i1) - rsdtlen = *(u32 *)(p+20); - else - rsdtlen = 36; - - if(rsdtlen < 36 || rsdtlen > 1024) - continue; - break; - } - if(i>0xFFFE0) - return 0; - - - rsdt = ioremap(rsdtbase, rsdtlen); - if(rsdt == 0) - return 0; - - i = readl(rsdt + 4); - - /* - * Remap if needed - */ - - if(i > rsdtlen) - { - rsdtlen = i; - iounmap(rsdt); - rsdt = ioremap(rsdtbase, rsdtlen); - if(rsdt == 0) - return 0; - } - - for(n = 0; n < i; n++) - sum += readb(rsdt + n); - - if(sum) - { - iounmap(rsdt); - return 0; - } - - /* Ok the RSDT checksums too */ - - for(n = 36; n+3 < i; n += 4) - { - unsigned long rp = readl(rsdt+n); - int len = 4096; - - if(rp > 0xFFFFFFFFUL - len) - len = 0xFFFFFFFFUL - rp; - - /* Too close to the end!! */ - if(len < 20) - continue; - rp = (unsigned long)ioremap(rp, 4096); - if(rp == 0) - continue; - if(sbf_struct_valid(rp)) - { - /* Found the BOOT table and processed it */ - printk(KERN_INFO "SBF: Simple Boot Flag extension found and enabled.\n"); - } - iounmap((void *)rp); - } - iounmap(rsdt); - sbf_bootup(); return 0; } --- linux-2.6.4-rc2/arch/i386/kernel/cpu/centaur.c 2003-06-14 12:18:24.000000000 -0700 +++ 25/arch/i386/kernel/cpu/centaur.c 2004-03-07 20:47:35.000000000 -0800 @@ -246,7 +246,15 @@ static void __init winchip2_protect_mcr( lo&=~0x1C0; /* blank bits 8-6 */ wrmsr(MSR_IDT_MCR_CTRL, lo, hi); } -#endif +#endif /* CONFIG_X86_OOSTORE */ + +#define ACE_PRESENT (1 << 6) +#define ACE_ENABLED (1 << 7) +#define ACE_FCR (1 << 28) /* MSR_VIA_FCR */ + +#define RNG_PRESENT (1 << 2) +#define RNG_ENABLED (1 << 3) +#define RNG_ENABLE (1 << 6) /* MSR_VIA_RNG */ static void __init init_c3(struct cpuinfo_x86 *c) { @@ -254,6 +262,24 @@ static void __init init_c3(struct cpuinf /* Test for Centaur Extended Feature Flags presence */ if (cpuid_eax(0xC0000000) >= 0xC0000001) { + u32 tmp = cpuid_edx(0xC0000001); + + /* enable ACE unit, if present and disabled */ + if ((tmp & (ACE_PRESENT | ACE_ENABLED)) == ACE_PRESENT) { + rdmsr (MSR_VIA_FCR, lo, hi); + lo |= ACE_FCR; /* enable ACE unit */ + wrmsr (MSR_VIA_FCR, lo, hi); + printk(KERN_INFO "CPU: Enabled ACE h/w crypto\n"); + } + + /* enable RNG unit, if present and disabled */ + if ((tmp & (RNG_PRESENT | RNG_ENABLED)) == RNG_PRESENT) { + rdmsr (MSR_VIA_RNG, lo, hi); + lo |= RNG_ENABLE; /* enable RNG unit */ + wrmsr (MSR_VIA_RNG, lo, hi); + printk(KERN_INFO "CPU: Enabled h/w RNG\n"); + } + /* store Centaur Extended Feature Flags as * word 5 of the CPU capability bit array */ --- linux-2.6.4-rc2/arch/i386/kernel/cpu/common.c 2004-01-09 00:04:30.000000000 -0800 +++ 25/arch/i386/kernel/cpu/common.c 2004-03-07 20:48:18.000000000 -0800 @@ -514,12 +514,16 @@ void __init cpu_init (void) set_tss_desc(cpu,t); cpu_gdt_table[cpu][GDT_ENTRY_TSS].b &= 0xfffffdff; load_TR_desc(); - load_LDT(&init_mm.context); + if (cpu) + load_LDT(&init_mm.context); /* Set up doublefault TSS pointer in the GDT */ __set_tss_desc(cpu, GDT_ENTRY_DOUBLEFAULT_TSS, &doublefault_tss); cpu_gdt_table[cpu][GDT_ENTRY_DOUBLEFAULT_TSS].b &= 0xfffffdff; + if (cpu) + trap_init_virtual_GDT(); + /* Clear %fs and %gs. */ asm volatile ("xorl %eax, %eax; movl %eax, %fs; movl %eax, %gs"); --- linux-2.6.4-rc2/arch/i386/kernel/cpu/cpufreq/p4-clockmod.c 2004-02-17 20:48:42.000000000 -0800 +++ 25/arch/i386/kernel/cpu/cpufreq/p4-clockmod.c 2004-03-07 20:47:15.000000000 -0800 @@ -57,8 +57,7 @@ static int cpufreq_p4_setdc(unsigned int u32 l, h; cpumask_t cpus_allowed, affected_cpu_map; struct cpufreq_freqs freqs; - int hyperthreading = 0; - int sibling = 0; + int j; if (!cpu_online(cpu) || (newstate > DC_DISABLE) || (newstate == DC_RESV)) @@ -68,13 +67,10 @@ static int cpufreq_p4_setdc(unsigned int cpus_allowed = current->cpus_allowed; /* only run on CPU to be set, or on its sibling */ - affected_cpu_map = cpumask_of_cpu(cpu); -#ifdef CONFIG_X86_HT - hyperthreading = ((cpu_has_ht) && (smp_num_siblings == 2)); - if (hyperthreading) { - sibling = cpu_sibling_map[cpu]; - cpu_set(sibling, affected_cpu_map); - } +#ifdef CONFIG_SMP + affected_cpu_map = cpu_sibling_map[cpu]; +#else + affected_cpu_map = cpumask_of_cpu(cpu); #endif set_cpus_allowed(current, affected_cpu_map); BUG_ON(!cpu_isset(smp_processor_id(), affected_cpu_map)); @@ -97,11 +93,11 @@ static int cpufreq_p4_setdc(unsigned int /* notifiers */ freqs.old = stock_freq * l / 8; freqs.new = stock_freq * newstate / 8; - freqs.cpu = cpu; - cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); - if (hyperthreading) { - freqs.cpu = sibling; - cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + for_each_cpu(j) { + if (cpu_isset(j, affected_cpu_map)) { + freqs.cpu = j; + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + } } rdmsr(MSR_IA32_THERM_STATUS, l, h); @@ -132,10 +128,11 @@ static int cpufreq_p4_setdc(unsigned int set_cpus_allowed(current, cpus_allowed); /* notifiers */ - cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); - if (hyperthreading) { - freqs.cpu = cpu; - cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + for_each_cpu(j) { + if (cpu_isset(j, affected_cpu_map)) { + freqs.cpu = j; + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + } } return 0; --- linux-2.6.4-rc2/arch/i386/kernel/cpu/intel.c 2004-03-03 23:12:42.000000000 -0800 +++ 25/arch/i386/kernel/cpu/intel.c 2004-03-07 20:48:18.000000000 -0800 @@ -10,6 +10,7 @@ #include #include #include +#include #include "cpu.h" @@ -19,8 +20,6 @@ #include #endif -extern int trap_init_f00f_bug(void); - #ifdef CONFIG_X86_INTEL_USERCOPY /* * Alignment at which movsl is preferred for bulk memory copies. @@ -165,7 +164,7 @@ static void __init init_intel(struct cpu c->f00f_bug = 1; if ( !f00f_workaround_enabled ) { - trap_init_f00f_bug(); + trap_init_virtual_IDT(); printk(KERN_NOTICE "Intel Pentium with F0 0F bug - workaround enabled.\n"); f00f_workaround_enabled = 1; } @@ -250,6 +249,12 @@ static void __init init_intel(struct cpu /* SEP CPUID bug: Pentium Pro reports SEP but doesn't have it until model 3 mask 3 */ if ((c->x86<<8 | c->x86_model<<4 | c->x86_mask) < 0x633) clear_bit(X86_FEATURE_SEP, c->x86_capability); + /* + * FIXME: SEP is disabled for 4G/4G for now: + */ +#ifdef CONFIG_X86_HIGH_ENTRY + clear_bit(X86_FEATURE_SEP, c->x86_capability); +#endif /* Names for the Pentium II/Celeron processors detectable only by also checking the cache size. --- linux-2.6.4-rc2/arch/i386/kernel/cpu/mcheck/mce.c 2004-02-03 20:42:34.000000000 -0800 +++ 25/arch/i386/kernel/cpu/mcheck/mce.c 2004-03-07 20:47:47.000000000 -0800 @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -18,6 +19,8 @@ int mce_disabled __initdata = 0; int nr_mce_banks; +EXPORT_SYMBOL_GPL(nr_mce_banks); /* non-fatal.o */ + /* Handle unconfigured int18 (should never happen) */ static asmlinkage void unexpected_machine_check(struct pt_regs * regs, long error_code) { --- linux-2.6.4-rc2/arch/i386/kernel/cpu/proc.c 2003-08-22 19:23:40.000000000 -0700 +++ 25/arch/i386/kernel/cpu/proc.c 2004-03-07 20:47:35.000000000 -0800 @@ -50,7 +50,7 @@ static int show_cpuinfo(struct seq_file NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* VIA/Cyrix/Centaur-defined */ - NULL, NULL, "xstore", NULL, NULL, NULL, NULL, NULL, + NULL, NULL, "rng", "rng_en", NULL, NULL, "ace", "ace_en", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, --- linux-2.6.4-rc2/arch/i386/kernel/doublefault.c 2003-11-09 16:45:04.000000000 -0800 +++ 25/arch/i386/kernel/doublefault.c 2004-03-07 20:48:18.000000000 -0800 @@ -7,12 +7,13 @@ #include #include #include +#include #define DOUBLEFAULT_STACKSIZE (1024) static unsigned long doublefault_stack[DOUBLEFAULT_STACKSIZE]; #define STACK_START (unsigned long)(doublefault_stack+DOUBLEFAULT_STACKSIZE) -#define ptr_ok(x) ((x) > 0xc0000000 && (x) < 0xc1000000) +#define ptr_ok(x) (((x) > __PAGE_OFFSET && (x) < (__PAGE_OFFSET + 0x01000000)) || ((x) >= FIXADDR_START)) static void doublefault_fn(void) { @@ -38,8 +39,8 @@ static void doublefault_fn(void) printk("eax = %08lx, ebx = %08lx, ecx = %08lx, edx = %08lx\n", t->eax, t->ebx, t->ecx, t->edx); - printk("esi = %08lx, edi = %08lx\n", - t->esi, t->edi); + printk("esi = %08lx, edi = %08lx, ebp = %08lx\n", + t->esi, t->edi, t->ebp); } } --- linux-2.6.4-rc2/arch/i386/kernel/entry.S 2004-03-03 23:12:42.000000000 -0800 +++ 25/arch/i386/kernel/entry.S 2004-03-07 20:48:19.000000000 -0800 @@ -43,11 +43,25 @@ #include #include #include +#include #include #include +#include #include #include #include "irq_vectors.h" + /* We do not recover from a stack overflow, but at least + * we know it happened and should be able to track it down. + */ +#ifdef CONFIG_STACK_OVERFLOW_TEST +#define STACK_OVERFLOW_TEST \ + testl $7680,%esp; \ + jnz 10f; \ + call stack_overflow; \ +10: +#else +#define STACK_OVERFLOW_TEST +#endif #define nr_syscalls ((syscall_table_size)/4) @@ -87,7 +101,102 @@ TSS_ESP0_OFFSET = (4 - 0x200) #define resume_kernel restore_all #endif -#define SAVE_ALL \ +#ifdef CONFIG_X86_HIGH_ENTRY + +#ifdef CONFIG_X86_SWITCH_PAGETABLES + +#if defined(CONFIG_PREEMPT) && defined(CONFIG_SMP) +/* + * If task is preempted in __SWITCH_KERNELSPACE, and moved to another cpu, + * __switch_to repoints %esp to the appropriate virtual stack; but %ebp is + * left stale, so we must check whether to repeat the real stack calculation. + */ +#define repeat_if_esp_changed \ + xorl %esp, %ebp; \ + testl $-THREAD_SIZE, %ebp; \ + jnz 0b +#else +#define repeat_if_esp_changed +#endif + +/* clobbers ebx, edx and ebp */ + +#define __SWITCH_KERNELSPACE \ + cmpl $0xff000000, %esp; \ + jb 1f; \ + \ + /* \ + * switch pagetables and load the real stack, \ + * keep the stack offset: \ + */ \ + \ + movl $swapper_pg_dir-__PAGE_OFFSET, %edx; \ + \ + /* GET_THREAD_INFO(%ebp) intermixed */ \ +0: \ + movl %esp, %ebp; \ + movl %esp, %ebx; \ + andl $(-THREAD_SIZE), %ebp; \ + andl $(THREAD_SIZE-1), %ebx; \ + orl TI_real_stack(%ebp), %ebx; \ + repeat_if_esp_changed; \ + \ + movl %edx, %cr3; \ + movl %ebx, %esp; \ +1: + +#endif + + +#define __SWITCH_USERSPACE \ + /* interrupted any of the user return paths? */ \ + \ + movl EIP(%esp), %eax; \ + \ + cmpl $int80_ret_start_marker, %eax; \ + jb 33f; /* nope - continue with sysexit check */\ + cmpl $int80_ret_end_marker, %eax; \ + jb 22f; /* yes - switch to virtual stack */ \ +33: \ + cmpl $sysexit_ret_start_marker, %eax; \ + jb 44f; /* nope - continue with user check */ \ + cmpl $sysexit_ret_end_marker, %eax; \ + jb 22f; /* yes - switch to virtual stack */ \ + /* return to userspace? */ \ +44: \ + movl EFLAGS(%esp),%ecx; \ + movb CS(%esp),%cl; \ + testl $(VM_MASK | 3),%ecx; \ + jz 2f; \ +22: \ + /* \ + * switch to the virtual stack, then switch to \ + * the userspace pagetables. \ + */ \ + \ + GET_THREAD_INFO(%ebp); \ + movl TI_virtual_stack(%ebp), %edx; \ + movl TI_user_pgd(%ebp), %ecx; \ + \ + movl %esp, %ebx; \ + andl $(THREAD_SIZE-1), %ebx; \ + orl %ebx, %edx; \ +int80_ret_start_marker: \ + movl %edx, %esp; \ + movl %ecx, %cr3; \ + \ + __RESTORE_ALL; \ +int80_ret_end_marker: \ +2: + +#else /* !CONFIG_X86_HIGH_ENTRY */ + +#define __SWITCH_KERNELSPACE +#define __SWITCH_USERSPACE + +#endif + +#define __SAVE_ALL \ cld; \ pushl %es; \ pushl %ds; \ @@ -102,7 +211,7 @@ TSS_ESP0_OFFSET = (4 - 0x200) movl %edx, %ds; \ movl %edx, %es; -#define RESTORE_INT_REGS \ +#define __RESTORE_INT_REGS \ popl %ebx; \ popl %ecx; \ popl %edx; \ @@ -111,29 +220,28 @@ TSS_ESP0_OFFSET = (4 - 0x200) popl %ebp; \ popl %eax -#define RESTORE_REGS \ - RESTORE_INT_REGS; \ -1: popl %ds; \ -2: popl %es; \ +#define __RESTORE_REGS \ + __RESTORE_INT_REGS; \ +111: popl %ds; \ +222: popl %es; \ .section .fixup,"ax"; \ -3: movl $0,(%esp); \ - jmp 1b; \ -4: movl $0,(%esp); \ - jmp 2b; \ +444: movl $0,(%esp); \ + jmp 111b; \ +555: movl $0,(%esp); \ + jmp 222b; \ .previous; \ .section __ex_table,"a";\ .align 4; \ - .long 1b,3b; \ - .long 2b,4b; \ + .long 111b,444b;\ + .long 222b,555b;\ .previous - -#define RESTORE_ALL \ - RESTORE_REGS \ +#define __RESTORE_ALL \ + __RESTORE_REGS \ addl $4, %esp; \ -1: iret; \ +333: iret; \ .section .fixup,"ax"; \ -2: sti; \ +666: sti; \ movl $(__USER_DS), %edx; \ movl %edx, %ds; \ movl %edx, %es; \ @@ -142,10 +250,19 @@ TSS_ESP0_OFFSET = (4 - 0x200) .previous; \ .section __ex_table,"a";\ .align 4; \ - .long 1b,2b; \ + .long 333b,666b;\ .previous +#define SAVE_ALL \ + __SAVE_ALL; \ + __SWITCH_KERNELSPACE; \ + STACK_OVERFLOW_TEST; + +#define RESTORE_ALL \ + __SWITCH_USERSPACE; \ + __RESTORE_ALL; +.section .entry.text,"ax" ENTRY(lcall7) pushfl # We get a different stack layout with call @@ -163,7 +280,7 @@ do_lcall: movl %edx,EIP(%ebp) # Now we move them to their "normal" places movl %ecx,CS(%ebp) # GET_THREAD_INFO_WITH_ESP(%ebp) # GET_THREAD_INFO - movl TI_EXEC_DOMAIN(%ebp), %edx # Get the execution domain + movl TI_exec_domain(%ebp), %edx # Get the execution domain call *4(%edx) # Call the lcall7 handler for the domain addl $4, %esp popl %eax @@ -208,7 +325,7 @@ ENTRY(resume_userspace) cli # make sure we don't miss an interrupt # setting need_resched or sigpending # between sampling and the iret - movl TI_FLAGS(%ebp), %ecx + movl TI_flags(%ebp), %ecx andl $_TIF_WORK_MASK, %ecx # is there any work to be done on # int/exception return? jne work_pending @@ -216,18 +333,18 @@ ENTRY(resume_userspace) #ifdef CONFIG_PREEMPT ENTRY(resume_kernel) - cmpl $0,TI_PRE_COUNT(%ebp) # non-zero preempt_count ? + cmpl $0,TI_preempt_count(%ebp) # non-zero preempt_count ? jnz restore_all need_resched: - movl TI_FLAGS(%ebp), %ecx # need_resched set ? + movl TI_flags(%ebp), %ecx # need_resched set ? testb $_TIF_NEED_RESCHED, %cl jz restore_all testl $IF_MASK,EFLAGS(%esp) # interrupts off (exception path) ? jz restore_all - movl $PREEMPT_ACTIVE,TI_PRE_COUNT(%ebp) + movl $PREEMPT_ACTIVE,TI_preempt_count(%ebp) sti call schedule - movl $0,TI_PRE_COUNT(%ebp) + movl $0,TI_preempt_count(%ebp) cli jmp need_resched #endif @@ -246,37 +363,50 @@ sysenter_past_esp: pushl $(__USER_CS) pushl $SYSENTER_RETURN -/* - * Load the potential sixth argument from user stack. - * Careful about security. - */ - cmpl $__PAGE_OFFSET-3,%ebp - jae syscall_fault -1: movl (%ebp),%ebp -.section __ex_table,"a" - .align 4 - .long 1b,syscall_fault -.previous - pushl %eax SAVE_ALL GET_THREAD_INFO(%ebp) cmpl $(nr_syscalls), %eax jae syscall_badsys - testb $_TIF_SYSCALL_TRACE,TI_FLAGS(%ebp) + testb $_TIF_SYSCALL_TRACE,TI_flags(%ebp) jnz syscall_trace_entry call *sys_call_table(,%eax,4) movl %eax,EAX(%esp) cli - movl TI_FLAGS(%ebp), %ecx + movl TI_flags(%ebp), %ecx testw $_TIF_ALLWORK_MASK, %cx jne syscall_exit_work + +#ifdef CONFIG_X86_SWITCH_PAGETABLES + + GET_THREAD_INFO(%ebp) + movl TI_virtual_stack(%ebp), %edx + movl TI_user_pgd(%ebp), %ecx + movl %esp, %ebx + andl $(THREAD_SIZE-1), %ebx + orl %ebx, %edx +sysexit_ret_start_marker: + movl %edx, %esp + movl %ecx, %cr3 +#endif + /* + * only ebx is not restored by the userspace sysenter vsyscall + * code, it assumes it to be callee-saved. + */ + movl EBX(%esp), %ebx + /* if something modifies registers it must also disable sysexit */ + movl EIP(%esp), %edx movl OLDESP(%esp), %ecx + sti sysexit +#ifdef CONFIG_X86_SWITCH_PAGETABLES +sysexit_ret_end_marker: + nop +#endif # system call handler stub @@ -287,7 +417,7 @@ ENTRY(system_call) cmpl $(nr_syscalls), %eax jae syscall_badsys # system call tracing in operation - testb $_TIF_SYSCALL_TRACE,TI_FLAGS(%ebp) + testb $_TIF_SYSCALL_TRACE,TI_flags(%ebp) jnz syscall_trace_entry syscall_call: call *sys_call_table(,%eax,4) @@ -296,10 +426,23 @@ syscall_exit: cli # make sure we don't miss an interrupt # setting need_resched or sigpending # between sampling and the iret - movl TI_FLAGS(%ebp), %ecx + movl TI_flags(%ebp), %ecx testw $_TIF_ALLWORK_MASK, %cx # current->work jne syscall_exit_work restore_all: +#ifdef CONFIG_TRAP_BAD_SYSCALL_EXITS + movl EFLAGS(%esp), %eax # mix EFLAGS and CS + movb CS(%esp), %al + testl $(VM_MASK | 3), %eax + jz resume_kernelX # returning to kernel or vm86-space + + cmpl $0,TI_preempt_count(%ebp) # non-zero preempt_count ? + jz resume_kernelX + + int $3 + +resume_kernelX: +#endif RESTORE_ALL # perform work that needs to be done immediately before resumption @@ -312,7 +455,7 @@ work_resched: cli # make sure we don't miss an interrupt # setting need_resched or sigpending # between sampling and the iret - movl TI_FLAGS(%ebp), %ecx + movl TI_flags(%ebp), %ecx andl $_TIF_WORK_MASK, %ecx # is there any work to be done other # than syscall tracing? jz restore_all @@ -327,6 +470,22 @@ work_notifysig: # deal with pending s # vm86-space xorl %edx, %edx call do_notify_resume + +#if CONFIG_X86_HIGH_ENTRY + /* + * Reload db7 if necessary: + */ + movl TI_flags(%ebp), %ecx + testb $_TIF_DB7, %cl + jnz work_db7 + + jmp restore_all + +work_db7: + movl TI_task(%ebp), %edx; + movl task_thread_db7(%edx), %edx; + movl %edx, %db7; +#endif jmp restore_all ALIGN @@ -382,7 +541,7 @@ syscall_badsys: */ .data ENTRY(interrupt) -.text +.previous vector=0 ENTRY(irq_entries_start) @@ -392,7 +551,7 @@ ENTRY(irq_entries_start) jmp common_interrupt .data .long 1b -.text +.previous vector=vector+1 .endr @@ -433,12 +592,17 @@ error_code: movl ES(%esp), %edi # get the function address movl %eax, ORIG_EAX(%esp) movl %ecx, ES(%esp) - movl %esp, %edx pushl %esi # push the error code - pushl %edx # push the pt_regs pointer movl $(__USER_DS), %edx movl %edx, %ds movl %edx, %es + +/* clobbers edx, ebx and ebp */ + __SWITCH_KERNELSPACE + + leal 4(%esp), %edx # prepare pt_regs + pushl %edx # push pt_regs + call *%edi addl $8, %esp jmp ret_from_exception @@ -529,7 +693,7 @@ nmi_stack_correct: pushl %edx call do_nmi addl $8, %esp - RESTORE_ALL + jmp restore_all nmi_stack_fixup: FIX_STACK(12,nmi_stack_correct, 1) @@ -606,6 +770,8 @@ ENTRY(spurious_interrupt_bug) pushl $do_spurious_interrupt_bug jmp error_code +.previous + .data ENTRY(sys_call_table) .long sys_restart_syscall /* 0 - old "setup()" system call, used for restarting */ @@ -865,7 +1031,7 @@ ENTRY(sys_call_table) .long sys_epoll_create .long sys_epoll_ctl /* 255 */ .long sys_epoll_wait - .long sys_remap_file_pages + .long old_remap_file_pages .long sys_set_tid_address .long sys_timer_create .long sys_timer_settime /* 260 */ @@ -882,5 +1048,12 @@ ENTRY(sys_call_table) .long sys_utimes .long sys_fadvise64_64 .long sys_ni_syscall /* sys_vserver */ + .long sys_mq_open + .long sys_mq_unlink /* 275 */ + .long sys_mq_timedsend + .long sys_mq_timedreceive + .long sys_mq_notify + .long sys_mq_getsetattr + .long sys_remap_file_pages /* 280 */ syscall_table_size=(.-sys_call_table) --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/arch/i386/kernel/entry_trampoline.c 2004-03-07 20:48:19.000000000 -0800 @@ -0,0 +1,73 @@ +/* + * linux/arch/i386/kernel/entry_trampoline.c + * + * (C) Copyright 2003 Ingo Molnar + * + * This file contains the needed support code for 4GB userspace + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern char __entry_tramp_start, __entry_tramp_end, __start___entry_text; + +void __init init_entry_mappings(void) +{ +#ifdef CONFIG_X86_HIGH_ENTRY + void *tramp; + + /* + * We need a high IDT and GDT for the 4G/4G split: + */ + trap_init_virtual_IDT(); + + __set_fixmap(FIX_ENTRY_TRAMPOLINE_0, __pa((unsigned long)&__entry_tramp_start), PAGE_KERNEL); + __set_fixmap(FIX_ENTRY_TRAMPOLINE_1, __pa((unsigned long)&__entry_tramp_start) + PAGE_SIZE, PAGE_KERNEL); + tramp = (void *)fix_to_virt(FIX_ENTRY_TRAMPOLINE_0); + + printk("mapped 4G/4G trampoline to %p.\n", tramp); + BUG_ON((void *)&__start___entry_text != tramp); + /* + * Virtual kernel stack: + */ + BUG_ON(__kmap_atomic_vaddr(KM_VSTACK0) & (THREAD_SIZE-1)); + BUG_ON(sizeof(struct desc_struct)*NR_CPUS*GDT_ENTRIES > 2*PAGE_SIZE); + BUG_ON((unsigned int)&__entry_tramp_end - (unsigned int)&__entry_tramp_start > 2*PAGE_SIZE); + + /* + * set up the initial thread's virtual stack related + * fields: + */ + current->thread.stack_page0 = virt_to_page((char *)current->thread_info); + current->thread.stack_page1 = virt_to_page((char *)current->thread_info + PAGE_SIZE); + current->thread_info->virtual_stack = (void *)__kmap_atomic_vaddr(KM_VSTACK0); + + __kunmap_atomic_type(KM_VSTACK0); + __kunmap_atomic_type(KM_VSTACK1); + __kmap_atomic(current->thread.stack_page0, KM_VSTACK0); + __kmap_atomic(current->thread.stack_page1, KM_VSTACK1); + +#endif + current->thread_info->real_stack = (void *)current->thread_info; + current->thread_info->user_pgd = NULL; + current->thread.esp0 = (unsigned long)current->thread_info->real_stack + THREAD_SIZE; +} + + + +void __init entry_trampoline_setup(void) +{ + /* + * old IRQ entries set up by the boot code will still hang + * around - they are a sign of hw trouble anyway, now they'll + * produce a double fault message. + */ + trap_init_virtual_GDT(); +} --- linux-2.6.4-rc2/arch/i386/kernel/head.S 2004-03-03 23:12:42.000000000 -0800 +++ 25/arch/i386/kernel/head.S 2004-03-07 20:47:46.000000000 -0800 @@ -17,7 +17,7 @@ #include #include #include - +#include #define OLD_CL_MAGIC_ADDR 0x90020 #define OLD_CL_MAGIC 0xA33F @@ -40,49 +40,89 @@ #define X86_VENDOR_ID CPU_PARAMS+36 /* offset dependent on NCAPINTS */ /* - * Initialize page tables + * This is how much memory *in addition to the memory covered up to + * and including _end* we need mapped initially. We need one bit for + * each possible page, but only in low memory, which means + * 2^32/4096/8 = 128K worst case (4G/4G split.) + * + * Modulo rounding, each megabyte assigned here requires a kilobyte of + * memory, which is currently unreclaimed. + * + * This should be a multiple of a page. */ -#define INIT_PAGE_TABLES \ - movl $pg0 - __PAGE_OFFSET, %edi; \ - /* "007" doesn't mean with license to kill, but PRESENT+RW+USER */ \ - movl $007, %eax; \ -2: stosl; \ - add $0x1000, %eax; \ - cmp $empty_zero_page - __PAGE_OFFSET, %edi; \ - jne 2b; +#define INIT_MAP_BEYOND_END (128*1024) + /* - * swapper_pg_dir is the main page directory, address 0x00101000 - * - * On entry, %esi points to the real-mode code as a 32-bit pointer. + * 32-bit kernel entrypoint; only used by the boot CPU. On entry, + * %esi points to the real-mode code as a 32-bit pointer. + * CS and DS must be 4 GB flat segments, but we don't depend on + * any particular GDT layout, because we load our own as soon as we + * can. */ ENTRY(startup_32) -#ifdef CONFIG_X86_VISWS /* - * On SGI Visual Workstations boot CPU starts in protected mode. + * Set segments to known values. */ - orw %bx, %bx - jnz 1f - INIT_PAGE_TABLES - movl $swapper_pg_dir - __PAGE_OFFSET, %eax - movl %eax, %cr3 - lgdt boot_gdt -1: -#endif + cld + lgdt boot_gdt_descr - __PAGE_OFFSET + movl $(__BOOT_DS),%eax + movl %eax,%ds + movl %eax,%es + movl %eax,%fs + movl %eax,%gs /* - * Set segments to known values + * Initialize page tables. This creates a PDE and a set of page + * tables, which are located immediately beyond _end. The variable + * init_pg_tables_end is set up to point to the first "safe" location. + * + * Warning: don't use %esi or the stack in this code. However, %esp + * can be used as a GPR if you really need it... */ +page_pde_offset = (__PAGE_OFFSET >> 20); + + movl $(pg0 - __PAGE_OFFSET), %edi + movl $(swapper_pg_dir - __PAGE_OFFSET), %edx + movl $0x007, %eax /* 0x007 = PRESENT+RW+USER */ +10: + leal 0x007(%edi),%ecx /* Create PDE entry */ + movl %ecx,(%edx) /* Store identity PDE entry */ + movl %ecx,page_pde_offset(%edx) /* Store kernel PDE entry */ + addl $4,%edx + movl $1024, %ecx +11: + stosl + addl $0x1000,%eax + loop 11b + /* End condition: we must map up to and including INIT_MAP_BEYOND_END */ + /* bytes beyond the end of our own page tables; the +0x007 is the attribute bits */ + leal (INIT_MAP_BEYOND_END+0x007)(%edi),%ebp + cmpl %ebp,%eax + jb 10b + movl %edi,(init_pg_tables_end - __PAGE_OFFSET) + +#ifdef CONFIG_SMP + xorl %ebx,%ebx /* This is the boot CPU (BSP) */ + jmp 3f + +/* + * Non-boot CPU entry point; entered from trampoline.S + * We can't lgdt here, because lgdt itself uses a data segment, but + * we know the trampoline has already loaded the boot_gdt_table GDT + * for us. + */ +ENTRY(startup_32_smp) cld movl $(__BOOT_DS),%eax movl %eax,%ds movl %eax,%es movl %eax,%fs movl %eax,%gs -#ifdef CONFIG_SMP - orw %bx,%bx - jz 1f + + xorl %ebx,%ebx + incl %ebx /* This is a secondary processor (AP) */ /* * New page tables may be in 4Mbyte page mode and may @@ -99,37 +139,40 @@ ENTRY(startup_32) * not yet offset PAGE_OFFSET.. */ #define cr4_bits mmu_cr4_features-__PAGE_OFFSET - cmpl $0,cr4_bits - je 3f + movl cr4_bits,%edx + andl %edx,%edx + jz 3f movl %cr4,%eax # Turn on paging options (PSE,PAE,..) - orl cr4_bits,%eax + orl %edx,%eax movl %eax,%cr4 - jmp 3f -1: -#endif - INIT_PAGE_TABLES + +3: +#endif /* CONFIG_SMP */ + /* * Enable paging */ -3: movl $swapper_pg_dir-__PAGE_OFFSET,%eax movl %eax,%cr3 /* set the page table pointer.. */ movl %cr0,%eax orl $0x80000000,%eax movl %eax,%cr0 /* ..and set paging (PG) bit */ - jmp 1f /* flush the prefetch-queue */ -1: - movl $1f,%eax - jmp *%eax /* make sure eip is relocated */ + ljmp $__BOOT_CS,$1f /* Clear prefetch and normalize %eip */ 1: /* Set up the stack pointer */ lss stack_start,%esp -#ifdef CONFIG_SMP - orw %bx,%bx - jz 1f /* Initial CPU cleans BSS */ +/* + * Initialize eflags. Some BIOS's leave bits like NT set. This would + * confuse the debugger if this code is traced. + * XXX - best to initialize before switching to protected mode. + */ pushl $0 popfl + +#ifdef CONFIG_SMP + andl %ebx,%ebx + jz 1f /* Initial CPU cleans BSS */ jmp checkCPUtype 1: #endif /* CONFIG_SMP */ @@ -142,21 +185,15 @@ ENTRY(startup_32) movl $__bss_start,%edi movl $__bss_stop,%ecx subl %edi,%ecx - rep - stosb + shrl $2,%ecx + rep ; stosl /* * start system 32-bit setup. We need to re-do some of the things done * in 16-bit mode for the "real" operations. */ call setup_idt -/* - * Initialize eflags. Some BIOS's leave bits like NT set. This would - * confuse the debugger if this code is traced. - * XXX - best to initialize before switching to protected mode. - */ - pushl $0 - popfl + /* * Copy bootup parameters out of the way. First 2kB of * _empty_zero_page is for boot parameters, second 2kB @@ -273,7 +310,7 @@ is386: movl $2,%ecx # set MP call initialize_secondary jmp L6 1: -#endif +#endif /* CONFIG_SMP */ call start_kernel L6: jmp L6 # main should never return here, but @@ -309,6 +346,8 @@ check_x87: * and the kernel moved to PAGE_OFFSET. Interrupts * are enabled elsewhere, when we can be relatively * sure everything is ok. + * + * Warning: %esi is live across this function. */ setup_idt: lea ignore_int,%edx @@ -332,7 +371,7 @@ ENTRY(stack_start) /* This is the default interrupt "handler" :-) */ int_msg: - .asciz "Unknown interrupt\n" + .asciz "Unknown interrupt or fault at EIP %p %p %p\n" ALIGN ignore_int: cld @@ -344,9 +383,13 @@ ignore_int: movl $(__KERNEL_DS),%eax movl %eax,%ds movl %eax,%es + pushl 16(%esp) + pushl 24(%esp) + pushl 32(%esp) + pushl 40(%esp) pushl $int_msg call printk - popl %eax + addl $(5*4),%esp popl %ds popl %es popl %edx @@ -361,10 +404,17 @@ ignore_int: * segment size, and 32-bit linear address value: */ +.globl boot_gdt_descr .globl idt_descr .globl cpu_gdt_descr ALIGN +# early boot GDT descriptor (must use 1:1 address mapping) + .word 0 # 32 bit align gdt_desc.address +boot_gdt_descr: + .word __BOOT_DS+7 + .long boot_gdt_table - __PAGE_OFFSET + .word 0 # 32-bit align idt_desc.address idt_descr: .word IDT_ENTRIES*8-1 # idt contains 256 entries @@ -379,41 +429,25 @@ cpu_gdt_descr: .fill NR_CPUS-1,8,0 # space for the other GDT descriptors /* - * This is initialized to create an identity-mapping at 0-8M (for bootup - * purposes) and another mapping of the 0-8M area at virtual address - * PAGE_OFFSET. + * swapper_pg_dir is the main page directory, address 0x00101000 + * + * This is initialized to create an identity-mapping at 0 (for bootup + * purposes) and another mapping at virtual address PAGE_OFFSET. The + * values put here should be all invalid (zero); the valid + * entries are created dynamically at boot time. + * + * The code creates enough page tables to map 0-_end, the page tables + * themselves, plus INIT_MAP_BEYOND_END bytes; see comment at beginning. */ .org 0x1000 ENTRY(swapper_pg_dir) - .long 0x00102007 - .long 0x00103007 - .fill BOOT_USER_PGD_PTRS-2,4,0 - /* default: 766 entries */ - .long 0x00102007 - .long 0x00103007 - /* default: 254 entries */ - .fill BOOT_KERNEL_PGD_PTRS-2,4,0 + .fill 1024,4,0 -/* - * The page tables are initialized to only 8MB here - the final page - * tables are set up later depending on memory size. - */ .org 0x2000 -ENTRY(pg0) - -.org 0x3000 -ENTRY(pg1) - -/* - * empty_zero_page must immediately follow the page tables ! (The - * initialization loop counts until empty_zero_page) - */ - -.org 0x4000 ENTRY(empty_zero_page) + .fill 4096,1,0 -.org 0x5000 - +.org 0x3000 /* * Real beginning of normal "text" segment */ @@ -428,20 +462,19 @@ ENTRY(_stext) .data /* - * The Global Descriptor Table contains 28 quadwords, per-CPU. - */ -#if defined(CONFIG_SMP) || defined(CONFIG_X86_VISWS) -/* * The boot_gdt_table must mirror the equivalent in setup.S and is - * used only by the trampoline for booting other CPUs + * used only for booting. */ .align L1_CACHE_BYTES ENTRY(boot_gdt_table) .fill GDT_ENTRY_BOOT_CS,8,0 .quad 0x00cf9a000000ffff /* kernel 4GB code at 0x00000000 */ .quad 0x00cf92000000ffff /* kernel 4GB data at 0x00000000 */ -#endif - .align L1_CACHE_BYTES + +/* + * The Global Descriptor Table contains 28 quadwords, per-CPU. + */ + .align PAGE_SIZE_asm ENTRY(cpu_gdt_table) .quad 0x0000000000000000 /* NULL descriptor */ .quad 0x0000000000000000 /* 0x0b reserved */ @@ -488,4 +521,3 @@ ENTRY(cpu_gdt_table) #ifdef CONFIG_SMP .fill (NR_CPUS-1)*GDT_ENTRIES,8,0 /* other CPU's GDT */ #endif - --- linux-2.6.4-rc2/arch/i386/kernel/i386_ksyms.c 2004-02-17 20:48:42.000000000 -0800 +++ 25/arch/i386/kernel/i386_ksyms.c 2004-03-07 20:48:18.000000000 -0800 @@ -88,16 +88,11 @@ EXPORT_SYMBOL(get_cmos_time); EXPORT_SYMBOL(cpu_khz); EXPORT_SYMBOL(apm_info); -#ifdef CONFIG_DEBUG_IOVIRT -EXPORT_SYMBOL(__io_virt_debug); -#endif - EXPORT_SYMBOL_NOVERS(__down_failed); EXPORT_SYMBOL_NOVERS(__down_failed_interruptible); EXPORT_SYMBOL_NOVERS(__down_failed_trylock); EXPORT_SYMBOL_NOVERS(__up_wakeup); /* Networking helper routines. */ -EXPORT_SYMBOL(csum_partial_copy_generic); /* Delay loops */ EXPORT_SYMBOL(__ndelay); EXPORT_SYMBOL(__udelay); @@ -111,13 +106,17 @@ EXPORT_SYMBOL_NOVERS(__get_user_4); EXPORT_SYMBOL(strpbrk); EXPORT_SYMBOL(strstr); +#if !defined(CONFIG_X86_UACCESS_INDIRECT) EXPORT_SYMBOL(strncpy_from_user); -EXPORT_SYMBOL(__strncpy_from_user); +EXPORT_SYMBOL(__direct_strncpy_from_user); EXPORT_SYMBOL(clear_user); EXPORT_SYMBOL(__clear_user); EXPORT_SYMBOL(__copy_from_user_ll); EXPORT_SYMBOL(__copy_to_user_ll); EXPORT_SYMBOL(strnlen_user); +#else /* CONFIG_X86_UACCESS_INDIRECT */ +EXPORT_SYMBOL(direct_csum_partial_copy_generic); +#endif EXPORT_SYMBOL(dma_alloc_coherent); EXPORT_SYMBOL(dma_free_coherent); --- linux-2.6.4-rc2/arch/i386/kernel/i387.c 2004-02-17 20:48:42.000000000 -0800 +++ 25/arch/i386/kernel/i387.c 2004-03-07 20:48:18.000000000 -0800 @@ -218,6 +218,7 @@ void set_fpu_mxcsr( struct task_struct * static int convert_fxsr_to_user( struct _fpstate __user *buf, struct i387_fxsave_struct *fxsave ) { + struct _fpreg tmp[8]; /* 80 bytes scratch area */ unsigned long env[7]; struct _fpreg __user *to; struct _fpxreg *from; @@ -234,23 +235,25 @@ static int convert_fxsr_to_user( struct if ( __copy_to_user( buf, env, 7 * sizeof(unsigned long) ) ) return 1; - to = &buf->_st[0]; + to = tmp; from = (struct _fpxreg *) &fxsave->st_space[0]; for ( i = 0 ; i < 8 ; i++, to++, from++ ) { unsigned long *t = (unsigned long *)to; unsigned long *f = (unsigned long *)from; - if (__put_user(*f, t) || - __put_user(*(f + 1), t + 1) || - __put_user(from->exponent, &to->exponent)) - return 1; + *t = *f; + *(t + 1) = *(f+1); + to->exponent = from->exponent; } + if (copy_to_user(buf->_st, tmp, sizeof(struct _fpreg [8]))) + return 1; return 0; } static int convert_fxsr_from_user( struct i387_fxsave_struct *fxsave, struct _fpstate __user *buf ) { + struct _fpreg tmp[8]; /* 80 bytes scratch area */ unsigned long env[7]; struct _fpxreg *to; struct _fpreg __user *from; @@ -258,6 +261,8 @@ static int convert_fxsr_from_user( struc if ( __copy_from_user( env, buf, 7 * sizeof(long) ) ) return 1; + if (copy_from_user(tmp, buf->_st, sizeof(struct _fpreg [8]))) + return 1; fxsave->cwd = (unsigned short)(env[0] & 0xffff); fxsave->swd = (unsigned short)(env[1] & 0xffff); @@ -269,15 +274,14 @@ static int convert_fxsr_from_user( struc fxsave->fos = env[6]; to = (struct _fpxreg *) &fxsave->st_space[0]; - from = &buf->_st[0]; + from = tmp; for ( i = 0 ; i < 8 ; i++, to++, from++ ) { unsigned long *t = (unsigned long *)to; unsigned long *f = (unsigned long *)from; - if (__get_user(*t, f) || - __get_user(*(t + 1), f + 1) || - __get_user(to->exponent, &from->exponent)) - return 1; + *t = *f; + *(t + 1) = *(f + 1); + to->exponent = from->exponent; } return 0; } --- linux-2.6.4-rc2/arch/i386/kernel/i8259.c 2004-03-03 23:12:42.000000000 -0800 +++ 25/arch/i386/kernel/i8259.c 2004-03-07 20:48:20.000000000 -0800 @@ -444,4 +444,7 @@ void __init init_IRQ(void) */ if (boot_cpu_data.hard_math && !cpu_has_fpu) setup_irq(FPU_IRQ, &fpu_irq); + + current_thread_info()->cpu = 0; + irq_ctx_init(0); } --- linux-2.6.4-rc2/arch/i386/kernel/init_task.c 2003-11-09 16:45:04.000000000 -0800 +++ 25/arch/i386/kernel/init_task.c 2004-03-07 20:48:18.000000000 -0800 @@ -26,7 +26,7 @@ EXPORT_SYMBOL(init_mm); */ union thread_union init_thread_union __attribute__((__section__(".data.init_task"))) = - { INIT_THREAD_INFO(init_task) }; + { INIT_THREAD_INFO(init_task, init_thread_union) }; /* * Initial task structure. @@ -44,5 +44,5 @@ EXPORT_SYMBOL(init_task); * section. Since TSS's are completely CPU-local, we want them * on exact cacheline boundaries, to eliminate cacheline ping-pong. */ -struct tss_struct init_tss[NR_CPUS] __cacheline_aligned = { [0 ... NR_CPUS-1] = INIT_TSS }; +struct tss_struct init_tss[NR_CPUS] __attribute__((__section__(".data.tss"))) = { [0 ... NR_CPUS-1] = INIT_TSS }; --- linux-2.6.4-rc2/arch/i386/kernel/io_apic.c 2004-03-03 23:12:42.000000000 -0800 +++ 25/arch/i386/kernel/io_apic.c 2004-03-07 20:47:15.000000000 -0800 @@ -317,8 +317,7 @@ struct irq_cpu_info { #define IRQ_ALLOWED(cpu, allowed_mask) cpu_isset(cpu, allowed_mask) -#define CPU_TO_PACKAGEINDEX(i) \ - ((physical_balance && i > cpu_sibling_map[i]) ? cpu_sibling_map[i] : i) +#define CPU_TO_PACKAGEINDEX(i) (first_cpu(cpu_sibling_map[i])) #define MAX_BALANCED_IRQ_INTERVAL (5*HZ) #define MIN_BALANCED_IRQ_INTERVAL (HZ/2) @@ -401,6 +400,7 @@ static void do_irq_balance(void) unsigned long max_cpu_irq = 0, min_cpu_irq = (~0); unsigned long move_this_load = 0; int max_loaded = 0, min_loaded = 0; + int load; unsigned long useful_load_threshold = balanced_irq_interval + 10; int selected_irq; int tmp_loaded, first_attempt = 1; @@ -452,7 +452,7 @@ static void do_irq_balance(void) for (i = 0; i < NR_CPUS; i++) { if (!cpu_online(i)) continue; - if (physical_balance && i > cpu_sibling_map[i]) + if (i != CPU_TO_PACKAGEINDEX(i)) continue; if (min_cpu_irq > CPU_IRQ(i)) { min_cpu_irq = CPU_IRQ(i); @@ -471,7 +471,7 @@ tryanothercpu: for (i = 0; i < NR_CPUS; i++) { if (!cpu_online(i)) continue; - if (physical_balance && i > cpu_sibling_map[i]) + if (i != CPU_TO_PACKAGEINDEX(i)) continue; if (max_cpu_irq <= CPU_IRQ(i)) continue; @@ -551,9 +551,14 @@ tryanotherirq: * We seek the least loaded sibling by making the comparison * (A+B)/2 vs B */ - if (physical_balance && (CPU_IRQ(min_loaded) >> 1) > - CPU_IRQ(cpu_sibling_map[min_loaded])) - min_loaded = cpu_sibling_map[min_loaded]; + load = CPU_IRQ(min_loaded) >> 1; + for_each_cpu_mask(j, cpu_sibling_map[min_loaded]) { + if (load > CPU_IRQ(j)) { + /* This won't change cpu_sibling_map[min_loaded] */ + load = CPU_IRQ(j); + min_loaded = j; + } + } cpus_and(allowed_mask, cpu_online_map, irq_affinity[selected_irq]); target_cpu_mask = cpumask_of_cpu(min_loaded); --- linux-2.6.4-rc2/arch/i386/kernel/irq.c 2004-03-03 23:12:42.000000000 -0800 +++ 25/arch/i386/kernel/irq.c 2004-03-07 20:48:20.000000000 -0800 @@ -75,6 +75,14 @@ irq_desc_t irq_desc[NR_IRQS] __cacheline static void register_irq_proc (unsigned int irq); /* + * per-CPU IRQ handling stacks + */ +#ifdef CONFIG_4KSTACKS +union irq_ctx *hardirq_ctx[NR_CPUS]; +union irq_ctx *softirq_ctx[NR_CPUS]; +#endif + +/* * Special irq handlers. */ @@ -213,7 +221,7 @@ inline void synchronize_irq(unsigned int * waste of time and is not what some drivers would * prefer. */ -int handle_IRQ_event(unsigned int irq, +asmlinkage int handle_IRQ_event(unsigned int irq, struct pt_regs *regs, struct irqaction *action) { int status = 1; /* Force the "do bottom halves" bit */ @@ -436,7 +444,7 @@ asmlinkage unsigned int do_IRQ(struct pt __asm__ __volatile__("andl %%esp,%0" : "=r" (esp) : "0" (THREAD_SIZE - 1)); - if (unlikely(esp < (sizeof(struct thread_info) + 1024))) { + if (unlikely(esp < (sizeof(struct thread_info) + STACK_WARN))) { printk("do_IRQ: stack overflow: %ld\n", esp - sizeof(struct thread_info)); dump_stack(); @@ -484,11 +492,70 @@ asmlinkage unsigned int do_IRQ(struct pt * useful for irq hardware that does not mask cleanly in an * SMP environment. */ +#ifdef CONFIG_4KSTACKS + + for (;;) { + irqreturn_t action_ret; + u32 *isp; + union irq_ctx * curctx; + union irq_ctx * irqctx; + + curctx = (union irq_ctx *) current_thread_info(); + irqctx = hardirq_ctx[smp_processor_id()]; + + spin_unlock(&desc->lock); + + /* + * this is where we switch to the IRQ stack. However, if we are already using + * the IRQ stack (because we interrupted a hardirq handler) we can't do that + * and just have to keep using the current stack (which is the irq stack already + * after all) + */ + + if (curctx == irqctx) + action_ret = handle_IRQ_event(irq, ®s, action); + else { + /* build the stack frame on the IRQ stack */ + isp = (u32*) ((char*)irqctx + sizeof(*irqctx)); + irqctx->tinfo.task = curctx->tinfo.task; + irqctx->tinfo.real_stack = curctx->tinfo.real_stack; + irqctx->tinfo.virtual_stack = curctx->tinfo.virtual_stack; + irqctx->tinfo.previous_esp = current_stack_pointer(); + + *--isp = (u32) action; + *--isp = (u32) ®s; + *--isp = (u32) irq; + + asm volatile( + " xchgl %%ebx,%%esp \n" + " call handle_IRQ_event \n" + " xchgl %%ebx,%%esp \n" + : "=a"(action_ret) + : "b"(isp) + : "memory", "cc", "edx", "ecx" + ); + + + } + spin_lock(&desc->lock); + if (!noirqdebug) + note_interrupt(irq, desc, action_ret); + if (curctx != irqctx) + irqctx->tinfo.task = NULL; + if (likely(!(desc->status & IRQ_PENDING))) + break; + desc->status &= ~IRQ_PENDING; + } + +#else + for (;;) { irqreturn_t action_ret; spin_unlock(&desc->lock); + action_ret = handle_IRQ_event(irq, ®s, action); + spin_lock(&desc->lock); if (!noirqdebug) note_interrupt(irq, desc, action_ret); @@ -496,6 +563,7 @@ asmlinkage unsigned int do_IRQ(struct pt break; desc->status &= ~IRQ_PENDING; } +#endif desc->status &= ~IRQ_INPROGRESS; out: @@ -508,6 +576,8 @@ out: irq_exit(); + kgdb_process_breakpoint(); + return 1; } @@ -1053,3 +1123,81 @@ void init_irq_proc (void) register_irq_proc(i); } + +#ifdef CONFIG_4KSTACKS +static char softirq_stack[NR_CPUS * THREAD_SIZE] __attribute__((__aligned__(THREAD_SIZE))); +static char hardirq_stack[NR_CPUS * THREAD_SIZE] __attribute__((__aligned__(THREAD_SIZE))); + +/* + * allocate per-cpu stacks for hardirq and for softirq processing + */ +void irq_ctx_init(int cpu) +{ + union irq_ctx *irqctx; + + if (hardirq_ctx[cpu]) + return; + + irqctx = (union irq_ctx*) &hardirq_stack[cpu*THREAD_SIZE]; + irqctx->tinfo.task = NULL; + irqctx->tinfo.exec_domain = NULL; + irqctx->tinfo.cpu = cpu; + irqctx->tinfo.preempt_count = HARDIRQ_OFFSET; + irqctx->tinfo.addr_limit = MAKE_MM_SEG(0); + + hardirq_ctx[cpu] = irqctx; + + irqctx = (union irq_ctx*) &softirq_stack[cpu*THREAD_SIZE]; + irqctx->tinfo.task = NULL; + irqctx->tinfo.exec_domain = NULL; + irqctx->tinfo.cpu = cpu; + irqctx->tinfo.preempt_count = SOFTIRQ_OFFSET; + irqctx->tinfo.addr_limit = MAKE_MM_SEG(0); + + softirq_ctx[cpu] = irqctx; + + printk("CPU %u irqstacks, hard=%p soft=%p\n", + cpu,hardirq_ctx[cpu],softirq_ctx[cpu]); +} + +extern asmlinkage void __do_softirq(void); + +asmlinkage void do_softirq(void) +{ + unsigned long flags; + struct thread_info *curctx; + union irq_ctx *irqctx; + u32 *isp; + + if (in_interrupt()) + return; + + local_irq_save(flags); + + if (local_softirq_pending()) { + curctx = current_thread_info(); + irqctx = softirq_ctx[smp_processor_id()]; + irqctx->tinfo.task = curctx->task; + irqctx->tinfo.real_stack = curctx->real_stack; + irqctx->tinfo.virtual_stack = curctx->virtual_stack; + irqctx->tinfo.previous_esp = current_stack_pointer(); + + /* build the stack frame on the softirq stack */ + isp = (u32*) ((char*)irqctx + sizeof(*irqctx)); + + + asm volatile( + " xchgl %%ebx,%%esp \n" + " call __do_softirq \n" + " movl %%ebx,%%esp \n" + : "=b"(isp) + : "0"(isp) + : "memory", "cc", "edx", "ecx", "eax" + ); + } + + local_irq_restore(flags); +} + +EXPORT_SYMBOL(do_softirq); +#endif --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/arch/i386/kernel/kgdb_stub.c 2004-03-07 20:47:04.000000000 -0800 @@ -0,0 +1,2458 @@ +/* + * + * 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. + * + */ + +/* + * Copyright (c) 2000 VERITAS Software Corporation. + * + */ +/**************************************************************************** + * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $ + * + * Module name: remcom.c $ + * Revision: 1.34 $ + * Date: 91/03/09 12:29:49 $ + * Contributor: Lake Stevens Instrument Division$ + * + * Description: low level support for gdb debugger. $ + * + * Considerations: only works on target hardware $ + * + * Written by: Glenn Engel $ + * Updated by: David Grothe + * Updated by: Robert Walsh + * Updated by: wangdi + * ModuleState: Experimental $ + * + * NOTES: See Below $ + * + * Modified for 386 by Jim Kingdon, Cygnus Support. + * Compatibility with 2.1.xx kernel by David Grothe + * + * Changes to allow auto initilization. All that is needed is that it + * be linked with the kernel and a break point (int 3) be executed. + * The header file defines BREAKPOINT to allow one to do + * this. It should also be possible, once the interrupt system is up, to + * call putDebugChar("+"). Once this is done, the remote debugger should + * get our attention by sending a ^C in a packet. George Anzinger + * + * Integrated into 2.2.5 kernel by Tigran Aivazian + * Added thread support, support for multiple processors, + * support for ia-32(x86) hardware debugging. + * Amit S. Kale ( akale@veritas.com ) + * + * Modified to support debugging over ethernet by Robert Walsh + * and wangdi , based on + * code by San Mehat. + * + * + * To enable debugger support, two things need to happen. One, a + * call to set_debug_traps() is necessary in order to allow any breakpoints + * or error conditions to be properly intercepted and reported to gdb. + * Two, a breakpoint needs to be generated to begin communication. This + * is most easily accomplished by a call to breakpoint(). Breakpoint() + * simulates a breakpoint by executing an int 3. + * + ************* + * + * The following gdb commands are supported: + * + * command function Return value + * + * g return the value of the CPU registers hex data or ENN + * G set the value of the CPU registers OK or ENN + * + * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN + * + * c Resume at current address SNN ( signal NN) + * cAA..AA Continue at address AA..AA SNN + * + * s Step one instruction SNN + * sAA..AA Step one instruction from AA..AA SNN + * + * k kill + * + * ? What was the last sigval ? SNN (signal NN) + * + * All commands and responses are sent with a packet which includes a + * checksum. A packet consists of + * + * $#. + * + * where + * :: + * :: < two hex digits computed as modulo 256 sum of > + * + * When a packet is received, it is first acknowledged with either '+' or '-'. + * '+' indicates a successful transfer. '-' indicates a failed transfer. + * + * Example: + * + * Host: Reply: + * $m0,10#2a +$00010203040506070809101112131415#42 + * + ****************************************************************************/ +#define KGDB_VERSION "<20030915.1651.33>" +#include +#include +#include /* for strcpy */ +#include +#include +#include +#include +#include /* for linux pt_regs struct */ +#include +#include +#include +#include +#include +#include +#include +#include + +/************************************************************************ + * + * external low-level support routines + */ +typedef void (*Function) (void); /* pointer to a function */ + +/* Thread reference */ +typedef unsigned char threadref[8]; + +extern int tty_putDebugChar(int); /* write a single character */ +extern int tty_getDebugChar(void); /* read and return a single char */ +extern void tty_flushDebugChar(void); /* flush pending characters */ +extern int eth_putDebugChar(int); /* write a single character */ +extern int eth_getDebugChar(void); /* read and return a single char */ +extern void eth_flushDebugChar(void); /* flush pending characters */ + +/************************************************************************/ +/* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/ +/* at least NUMREGBYTES*2 are needed for register packets */ +/* Longer buffer is needed to list all threads */ +#define BUFMAX 400 + +char *kgdb_version = KGDB_VERSION; + +/* debug > 0 prints ill-formed commands in valid packets & checksum errors */ +int debug_regs = 0; /* set to non-zero to print registers */ + +/* filled in by an external module */ +char *gdb_module_offsets; + +static const char hexchars[] = "0123456789abcdef"; + +/* Number of bytes of registers. */ +#define NUMREGBYTES 64 +/* + * Note that this register image is in a different order than + * the register image that Linux produces at interrupt time. + * + * Linux's register image is defined by struct pt_regs in ptrace.h. + * Just why GDB uses a different order is a historical mystery. + */ +enum regnames { _EAX, /* 0 */ + _ECX, /* 1 */ + _EDX, /* 2 */ + _EBX, /* 3 */ + _ESP, /* 4 */ + _EBP, /* 5 */ + _ESI, /* 6 */ + _EDI, /* 7 */ + _PC /* 8 also known as eip */ , + _PS /* 9 also known as eflags */ , + _CS, /* 10 */ + _SS, /* 11 */ + _DS, /* 12 */ + _ES, /* 13 */ + _FS, /* 14 */ + _GS /* 15 */ +}; + +/*************************** ASSEMBLY CODE MACROS *************************/ +/* + * Put the error code here just in case the user cares. + * Likewise, the vector number here (since GDB only gets the signal + * number through the usual means, and that's not very specific). + * The called_from is the return address so he can tell how we entered kgdb. + * This will allow him to seperate out the various possible entries. + */ +#define REMOTE_DEBUG 0 /* set != to turn on printing (also available in info) */ + +#define PID_MAX PID_MAX_DEFAULT + +#ifdef CONFIG_SMP +void smp_send_nmi_allbutself(void); +#define IF_SMP(x) x +#undef MAX_NO_CPUS +#ifndef CONFIG_NO_KGDB_CPUS +#define CONFIG_NO_KGDB_CPUS 2 +#endif +#if CONFIG_NO_KGDB_CPUS > NR_CPUS +#define MAX_NO_CPUS NR_CPUS +#else +#define MAX_NO_CPUS CONFIG_NO_KGDB_CPUS +#endif +#define hold_init hold_on_sstep: 1, +#define MAX_CPU_MASK (unsigned long)((1LL << MAX_NO_CPUS) - 1LL) +#define NUM_CPUS num_online_cpus() +#else +#define IF_SMP(x) +#define hold_init +#undef MAX_NO_CPUS +#define MAX_NO_CPUS 1 +#define NUM_CPUS 1 +#endif +#define NOCPU (struct task_struct *)0xbad1fbad +/* *INDENT-OFF* */ +struct kgdb_info { + int used_malloc; + void *called_from; + long long entry_tsc; + int errcode; + int vector; + int print_debug_info; +#ifdef CONFIG_SMP + int hold_on_sstep; + struct { + volatile struct task_struct *task; + int pid; + int hold; + struct pt_regs *regs; + } cpus_waiting[MAX_NO_CPUS]; +#endif +} kgdb_info = {hold_init print_debug_info:REMOTE_DEBUG, vector:-1}; + +/* *INDENT-ON* */ + +#define used_m kgdb_info.used_malloc +/* + * This is little area we set aside to contain the stack we + * need to build to allow gdb to call functions. We use one + * per cpu to avoid locking issues. We will do all this work + * with interrupts off so that should take care of the protection + * issues. + */ +#define LOOKASIDE_SIZE 200 /* should be more than enough */ +#define MALLOC_MAX 200 /* Max malloc size */ +struct { + unsigned int esp; + int array[LOOKASIDE_SIZE]; +} fn_call_lookaside[MAX_NO_CPUS]; + +static int trap_cpu; +static unsigned int OLD_esp; + +#define END_OF_LOOKASIDE &fn_call_lookaside[trap_cpu].array[LOOKASIDE_SIZE] +#define IF_BIT 0x200 +#define TF_BIT 0x100 + +#define MALLOC_ROUND 8-1 + +static char malloc_array[MALLOC_MAX]; +IF_SMP(static void to_gdb(const char *mess)); +void * +malloc(int size) +{ + + if (size <= (MALLOC_MAX - used_m)) { + int old_used = used_m; + used_m += ((size + MALLOC_ROUND) & (~MALLOC_ROUND)); + return &malloc_array[old_used]; + } else { + return NULL; + } +} + +/* + * I/O dispatch functions... + * Based upon kgdboe, either call the ethernet + * handler or the serial one.. + */ +void +putDebugChar(int c) +{ + if (!kgdboe) { + tty_putDebugChar(c); + } else { + eth_putDebugChar(c); + } +} + +int +getDebugChar(void) +{ + if (!kgdboe) { + return tty_getDebugChar(); + } else { + return eth_getDebugChar(); + } +} + +void +flushDebugChar(void) +{ + if (!kgdboe) { + tty_flushDebugChar(); + } else { + eth_flushDebugChar(); + } +} + +/* + * Gdb calls functions by pushing agruments, including a return address + * on the stack and the adjusting EIP to point to the function. The + * whole assumption in GDB is that we are on a different stack than the + * one the "user" i.e. code that hit the break point, is on. This, of + * course is not true in the kernel. Thus various dodges are needed to + * do the call without directly messing with EIP (which we can not change + * as it is just a location and not a register. To adjust it would then + * require that we move every thing below EIP up or down as needed. This + * will not work as we may well have stack relative pointer on the stack + * (such as the pointer to regs, for example). + + * So here is what we do: + * We detect gdb attempting to store into the stack area and instead, store + * into the fn_call_lookaside.array at the same relative location as if it + * were the area ESP pointed at. We also trap ESP modifications + * and uses these to adjust fn_call_lookaside.esp. On entry + * fn_call_lookaside.esp will be set to point at the last entry in + * fn_call_lookaside.array. This allows us to check if it has changed, and + * if so, on exit, we add the registers we will use to do the move and a + * trap/ interrupt return exit sequence. We then adjust the eflags in the + * regs array (remember we now have a copy in the fn_call_lookaside.array) to + * kill the interrupt bit, AND we change EIP to point at our set up stub. + * As part of the register set up we preset the registers to point at the + * begining and end of the fn_call_lookaside.array, so all the stub needs to + * do is move words from the array to the stack until ESP= the desired value + * then do the rti. This will then transfer to the desired function with + * all the correct registers. Nifty huh? + */ +extern asmlinkage void fn_call_stub(void); +extern asmlinkage void fn_rtn_stub(void); +/* *INDENT-OFF* */ +__asm__("fn_rtn_stub:\n\t" + "movl %eax,%esp\n\t" + "fn_call_stub:\n\t" + "1:\n\t" + "addl $-4,%ebx\n\t" + "movl (%ebx), %eax\n\t" + "pushl %eax\n\t" + "cmpl %esp,%ecx\n\t" + "jne 1b\n\t" + "popl %eax\n\t" + "popl %ebx\n\t" + "popl %ecx\n\t" + "iret \n\t"); +/* *INDENT-ON* */ +#define gdb_i386vector kgdb_info.vector +#define gdb_i386errcode kgdb_info.errcode +#define waiting_cpus kgdb_info.cpus_waiting +#define remote_debug kgdb_info.print_debug_info +#define hold_cpu(cpu) kgdb_info.cpus_waiting[cpu].hold +/* gdb locks */ + +#ifdef CONFIG_SMP +static int in_kgdb_called; +static spinlock_t waitlocks[MAX_NO_CPUS] = + {[0 ... MAX_NO_CPUS - 1] = SPIN_LOCK_UNLOCKED }; +/* + * The following array has the thread pointer of each of the "other" + * cpus. We make it global so it can be seen by gdb. + */ +volatile int in_kgdb_entry_log[MAX_NO_CPUS]; +volatile struct pt_regs *in_kgdb_here_log[MAX_NO_CPUS]; +/* +static spinlock_t continuelocks[MAX_NO_CPUS]; +*/ +spinlock_t kgdb_spinlock = SPIN_LOCK_UNLOCKED; +/* waiters on our spinlock plus us */ +static atomic_t spinlock_waiters = ATOMIC_INIT(1); +static int spinlock_count = 0; +static int spinlock_cpu = 0; +/* + * Note we use nested spin locks to account for the case where a break + * point is encountered when calling a function by user direction from + * kgdb. Also there is the memory exception recursion to account for. + * Well, yes, but this lets other cpus thru too. Lets add a + * cpu id to the lock. + */ +#define KGDB_SPIN_LOCK(x) if( spinlock_count == 0 || \ + spinlock_cpu != smp_processor_id()){\ + atomic_inc(&spinlock_waiters); \ + while (! spin_trylock(x)) {\ + in_kgdb(®s);\ + }\ + atomic_dec(&spinlock_waiters); \ + spinlock_count = 1; \ + spinlock_cpu = smp_processor_id(); \ + }else{ \ + spinlock_count++; \ + } +#define KGDB_SPIN_UNLOCK(x) if( --spinlock_count == 0) spin_unlock(x) +#else +unsigned kgdb_spinlock = 0; +#define KGDB_SPIN_LOCK(x) --*x +#define KGDB_SPIN_UNLOCK(x) ++*x +#endif + +int +hex(char ch) +{ + if ((ch >= 'a') && (ch <= 'f')) + return (ch - 'a' + 10); + if ((ch >= '0') && (ch <= '9')) + return (ch - '0'); + if ((ch >= 'A') && (ch <= 'F')) + return (ch - 'A' + 10); + return (-1); +} + +/* scan for the sequence $# */ +void +getpacket(char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + int i; + int count; + char ch; + + do { + /* wait around for the start character, ignore all other characters */ + while ((ch = (getDebugChar() & 0x7f)) != '$') ; + checksum = 0; + xmitcsum = -1; + + count = 0; + + /* now, read until a # or end of buffer is found */ + while (count < BUFMAX) { + ch = getDebugChar() & 0x7f; + if (ch == '#') + break; + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + buffer[count] = 0; + + if (ch == '#') { + xmitcsum = hex(getDebugChar() & 0x7f) << 4; + xmitcsum += hex(getDebugChar() & 0x7f); + if ((remote_debug) && (checksum != xmitcsum)) { + printk + ("bad checksum. My count = 0x%x, sent=0x%x. buf=%s\n", + checksum, xmitcsum, buffer); + } + + if (checksum != xmitcsum) + putDebugChar('-'); /* failed checksum */ + else { + putDebugChar('+'); /* successful transfer */ + /* if a sequence char is present, reply the sequence ID */ + if (buffer[2] == ':') { + putDebugChar(buffer[0]); + putDebugChar(buffer[1]); + /* remove sequence chars from buffer */ + count = strlen(buffer); + for (i = 3; i <= count; i++) + buffer[i - 3] = buffer[i]; + } + } + } + } while (checksum != xmitcsum); + + if (remote_debug) + printk("R:%s\n", buffer); + flushDebugChar(); +} + +/* send the packet in buffer. */ + +void +putpacket(char *buffer) +{ + unsigned char checksum; + int count; + char ch; + + /* $#. */ + + if (!kgdboe) { + do { + if (remote_debug) + printk("T:%s\n", buffer); + putDebugChar('$'); + checksum = 0; + count = 0; + + while ((ch = buffer[count])) { + putDebugChar(ch); + checksum += ch; + count += 1; + } + + putDebugChar('#'); + putDebugChar(hexchars[checksum >> 4]); + putDebugChar(hexchars[checksum % 16]); + flushDebugChar(); + + } while ((getDebugChar() & 0x7f) != '+'); + } else { + /* + * For udp, we can not transfer too much bytes once. + * We only transfer MAX_SEND_COUNT size bytes each time + */ + +#define MAX_SEND_COUNT 30 + + int send_count = 0, i = 0; + char send_buf[MAX_SEND_COUNT]; + + do { + if (remote_debug) + printk("T:%s\n", buffer); + putDebugChar('$'); + checksum = 0; + count = 0; + send_count = 0; + while ((ch = buffer[count])) { + if (send_count >= MAX_SEND_COUNT) { + for(i = 0; i < MAX_SEND_COUNT; i++) { + putDebugChar(send_buf[i]); + } + flushDebugChar(); + send_count = 0; + } else { + send_buf[send_count] = ch; + checksum += ch; + count ++; + send_count++; + } + } + for(i = 0; i < send_count; i++) + putDebugChar(send_buf[i]); + putDebugChar('#'); + putDebugChar(hexchars[checksum >> 4]); + putDebugChar(hexchars[checksum % 16]); + flushDebugChar(); + } while ((getDebugChar() & 0x7f) != '+'); + } +} + +static char remcomInBuffer[BUFMAX]; +static char remcomOutBuffer[BUFMAX]; +static short error; + +void +debug_error(char *format, char *parm) +{ + if (remote_debug) + printk(format, parm); +} + +static void +print_regs(struct pt_regs *regs) +{ + printk("EAX=%08lx ", regs->eax); + printk("EBX=%08lx ", regs->ebx); + printk("ECX=%08lx ", regs->ecx); + printk("EDX=%08lx ", regs->edx); + printk("\n"); + printk("ESI=%08lx ", regs->esi); + printk("EDI=%08lx ", regs->edi); + printk("EBP=%08lx ", regs->ebp); + printk("ESP=%08lx ", (long) ®s->esp); + printk("\n"); + printk(" DS=%08x ", regs->xds); + printk(" ES=%08x ", regs->xes); + printk(" SS=%08x ", __KERNEL_DS); + printk(" FL=%08lx ", regs->eflags); + printk("\n"); + printk(" CS=%08x ", regs->xcs); + printk(" IP=%08lx ", regs->eip); +#if 0 + printk(" FS=%08x ", regs->fs); + printk(" GS=%08x ", regs->gs); +#endif + printk("\n"); + +} /* print_regs */ + +#define NEW_esp fn_call_lookaside[trap_cpu].esp + +static void +regs_to_gdb_regs(int *gdb_regs, struct pt_regs *regs) +{ + gdb_regs[_EAX] = regs->eax; + gdb_regs[_EBX] = regs->ebx; + gdb_regs[_ECX] = regs->ecx; + gdb_regs[_EDX] = regs->edx; + gdb_regs[_ESI] = regs->esi; + gdb_regs[_EDI] = regs->edi; + gdb_regs[_EBP] = regs->ebp; + gdb_regs[_DS] = regs->xds; + gdb_regs[_ES] = regs->xes; + gdb_regs[_PS] = regs->eflags; + gdb_regs[_CS] = regs->xcs; + gdb_regs[_PC] = regs->eip; + /* Note, as we are a debugging the kernel, we will always + * trap in kernel code, this means no priviledge change, + * and so the pt_regs structure is not completely valid. In a non + * privilege change trap, only EFLAGS, CS and EIP are put on the stack, + * SS and ESP are not stacked, this means that the last 2 elements of + * pt_regs is not valid (they would normally refer to the user stack) + * also, using regs+1 is no good because you end up will a value that is + * 2 longs (8) too high. This used to cause stepping over functions + * to fail, so my fix is to use the address of regs->esp, which + * should point at the end of the stack frame. Note I have ignored + * completely exceptions that cause an error code to be stacked, such + * as double fault. Stuart Hughes, Zentropix. + * original code: gdb_regs[_ESP] = (int) (regs + 1) ; + + * this is now done on entry and moved to OLD_esp (as well as NEW_esp). + */ + gdb_regs[_ESP] = NEW_esp; + gdb_regs[_SS] = __KERNEL_DS; + gdb_regs[_FS] = 0xFFFF; + gdb_regs[_GS] = 0xFFFF; +} /* regs_to_gdb_regs */ + +static void +gdb_regs_to_regs(int *gdb_regs, struct pt_regs *regs) +{ + regs->eax = gdb_regs[_EAX]; + regs->ebx = gdb_regs[_EBX]; + regs->ecx = gdb_regs[_ECX]; + regs->edx = gdb_regs[_EDX]; + regs->esi = gdb_regs[_ESI]; + regs->edi = gdb_regs[_EDI]; + regs->ebp = gdb_regs[_EBP]; + regs->xds = gdb_regs[_DS]; + regs->xes = gdb_regs[_ES]; + regs->eflags = gdb_regs[_PS]; + regs->xcs = gdb_regs[_CS]; + regs->eip = gdb_regs[_PC]; + NEW_esp = gdb_regs[_ESP]; /* keep the value */ +#if 0 /* can't change these */ + regs->esp = gdb_regs[_ESP]; + regs->xss = gdb_regs[_SS]; + regs->fs = gdb_regs[_FS]; + regs->gs = gdb_regs[_GS]; +#endif + +} /* gdb_regs_to_regs */ +extern void scheduling_functions_start_here(void); +extern void scheduling_functions_end_here(void); +#define first_sched ((unsigned long) scheduling_functions_start_here) +#define last_sched ((unsigned long) scheduling_functions_end_here) + +int thread_list = 0; + +void +get_gdb_regs(struct task_struct *p, struct pt_regs *regs, int *gdb_regs) +{ + unsigned long stack_page; + int count = 0; + IF_SMP(int i); + if (!p || p == current) { + regs_to_gdb_regs(gdb_regs, regs); + return; + } +#ifdef CONFIG_SMP + for (i = 0; i < MAX_NO_CPUS; i++) { + if (p == kgdb_info.cpus_waiting[i].task) { + regs_to_gdb_regs(gdb_regs, + kgdb_info.cpus_waiting[i].regs); + gdb_regs[_ESP] = + (int) &kgdb_info.cpus_waiting[i].regs->esp; + + return; + } + } +#endif + memset(gdb_regs, 0, NUMREGBYTES); + gdb_regs[_ESP] = p->thread.esp; + gdb_regs[_PC] = p->thread.eip; + gdb_regs[_EBP] = *(int *) gdb_regs[_ESP]; + gdb_regs[_EDI] = *(int *) (gdb_regs[_ESP] + 4); + gdb_regs[_ESI] = *(int *) (gdb_regs[_ESP] + 8); + +/* + * This code is to give a more informative notion of where a process + * is waiting. It is used only when the user asks for a thread info + * list. If he then switches to the thread, s/he will find the task + * is in schedule, but a back trace should show the same info we come + * up with. This code was shamelessly purloined from process.c. It was + * then enhanced to provide more registers than simply the program + * counter. + */ + + if (!thread_list) { + return; + } + + if (p->state == TASK_RUNNING) + return; + stack_page = (unsigned long) p->thread_info; + if (gdb_regs[_ESP] < stack_page || gdb_regs[_ESP] > + THREAD_SIZE - sizeof(long) + stack_page) + return; + /* include/asm-i386/system.h:switch_to() pushes ebp last. */ + do { + if (gdb_regs[_EBP] < stack_page || + gdb_regs[_EBP] > THREAD_SIZE - 2*sizeof(long) + stack_page) + return; + gdb_regs[_PC] = *(unsigned long *) (gdb_regs[_EBP] + 4); + gdb_regs[_ESP] = gdb_regs[_EBP] + 8; + gdb_regs[_EBP] = *(unsigned long *) gdb_regs[_EBP]; + if (gdb_regs[_PC] < first_sched || gdb_regs[_PC] >= last_sched) + return; + } while (count++ < 16); + return; +} + +/* Indicate to caller of mem2hex or hex2mem that there has been an + error. */ +static volatile int mem_err = 0; +static volatile int mem_err_expected = 0; +static volatile int mem_err_cnt = 0; +static int garbage_loc = -1; + +int +get_char(char *addr) +{ + return *addr; +} + +void +set_char(char *addr, int val, int may_fault) +{ + /* + * This code traps references to the area mapped to the kernel + * stack as given by the regs and, instead, stores to the + * fn_call_lookaside[cpu].array + */ + if (may_fault && + (unsigned int) addr < OLD_esp && + ((unsigned int) addr > (OLD_esp - (unsigned int) LOOKASIDE_SIZE))) { + addr = (char *) END_OF_LOOKASIDE - ((char *) OLD_esp - addr); + } + *addr = val; +} + +/* convert the memory pointed to by mem into hex, placing result in buf */ +/* return a pointer to the last char put in buf (null) */ +/* If MAY_FAULT is non-zero, then we should set mem_err in response to + a fault; if zero treat a fault like any other fault in the stub. */ +char * +mem2hex(char *mem, char *buf, int count, int may_fault) +{ + int i; + unsigned char ch; + + if (may_fault) { + mem_err_expected = 1; + mem_err = 0; + } + for (i = 0; i < count; i++) { + /* printk("%lx = ", mem) ; */ + + ch = get_char(mem++); + + /* printk("%02x\n", ch & 0xFF) ; */ + if (may_fault && mem_err) { + if (remote_debug) + printk("Mem fault fetching from addr %lx\n", + (long) (mem - 1)); + *buf = 0; /* truncate buffer */ + return (buf); + } + *buf++ = hexchars[ch >> 4]; + *buf++ = hexchars[ch % 16]; + } + *buf = 0; + if (may_fault) + mem_err_expected = 0; + return (buf); +} + +/* convert the hex array pointed to by buf into binary to be placed in mem */ +/* return a pointer to the character AFTER the last byte written */ +/* NOTE: We use the may fault flag to also indicate if the write is to + * the registers (0) or "other" memory (!=0) + */ +char * +hex2mem(char *buf, char *mem, int count, int may_fault) +{ + int i; + unsigned char ch; + + if (may_fault) { + mem_err_expected = 1; + mem_err = 0; + } + for (i = 0; i < count; i++) { + ch = hex(*buf++) << 4; + ch = ch + hex(*buf++); + set_char(mem++, ch, may_fault); + + if (may_fault && mem_err) { + if (remote_debug) + printk("Mem fault storing to addr %lx\n", + (long) (mem - 1)); + return (mem); + } + } + if (may_fault) + mem_err_expected = 0; + return (mem); +} + +/**********************************************/ +/* WHILE WE FIND NICE HEX CHARS, BUILD AN INT */ +/* RETURN NUMBER OF CHARS PROCESSED */ +/**********************************************/ +int +hexToInt(char **ptr, int *intValue) +{ + int numChars = 0; + int hexValue; + + *intValue = 0; + + while (**ptr) { + hexValue = hex(**ptr); + if (hexValue >= 0) { + *intValue = (*intValue << 4) | hexValue; + numChars++; + } else + break; + + (*ptr)++; + } + + return (numChars); +} + +#define stubhex(h) hex(h) +#ifdef old_thread_list + +static int +stub_unpack_int(char *buff, int fieldlength) +{ + int nibble; + int retval = 0; + + while (fieldlength) { + nibble = stubhex(*buff++); + retval |= nibble; + fieldlength--; + if (fieldlength) + retval = retval << 4; + } + return retval; +} +#endif +static char * +pack_hex_byte(char *pkt, int byte) +{ + *pkt++ = hexchars[(byte >> 4) & 0xf]; + *pkt++ = hexchars[(byte & 0xf)]; + return pkt; +} + +#define BUF_THREAD_ID_SIZE 16 + +static char * +pack_threadid(char *pkt, threadref * id) +{ + char *limit; + unsigned char *altid; + + altid = (unsigned char *) id; + limit = pkt + BUF_THREAD_ID_SIZE; + while (pkt < limit) + pkt = pack_hex_byte(pkt, *altid++); + return pkt; +} + +#ifdef old_thread_list +static char * +unpack_byte(char *buf, int *value) +{ + *value = stub_unpack_int(buf, 2); + return buf + 2; +} + +static char * +unpack_threadid(char *inbuf, threadref * id) +{ + char *altref; + char *limit = inbuf + BUF_THREAD_ID_SIZE; + int x, y; + + altref = (char *) id; + + while (inbuf < limit) { + x = stubhex(*inbuf++); + y = stubhex(*inbuf++); + *altref++ = (x << 4) | y; + } + return inbuf; +} +#endif +void +int_to_threadref(threadref * id, int value) +{ + unsigned char *scan; + + scan = (unsigned char *) id; + { + int i = 4; + while (i--) + *scan++ = 0; + } + *scan++ = (value >> 24) & 0xff; + *scan++ = (value >> 16) & 0xff; + *scan++ = (value >> 8) & 0xff; + *scan++ = (value & 0xff); +} +int +int_to_hex_v(unsigned char * id, int value) +{ + unsigned char *start = id; + int shift; + int ch; + + for (shift = 28; shift >= 0; shift -= 4) { + if ((ch = (value >> shift) & 0xf) || (id != start)) { + *id = hexchars[ch]; + id++; + } + } + if (id == start) + *id++ = '0'; + return id - start; +} +#ifdef old_thread_list + +static int +threadref_to_int(threadref * ref) +{ + int i, value = 0; + unsigned char *scan; + + scan = (char *) ref; + scan += 4; + i = 4; + while (i-- > 0) + value = (value << 8) | ((*scan++) & 0xff); + return value; +} +#endif +static int +cmp_str(char *s1, char *s2, int count) +{ + while (count--) { + if (*s1++ != *s2++) + return 0; + } + return 1; +} + +#if 1 /* this is a hold over from 2.4 where O(1) was "sometimes" */ +extern struct task_struct *kgdb_get_idle(int cpu); +#define idle_task(cpu) kgdb_get_idle(cpu) +#else +#define idle_task(cpu) init_tasks[cpu] +#endif + +extern int kgdb_pid_init_done; + +struct task_struct * +getthread(int pid) +{ + struct task_struct *thread; + if (pid >= PID_MAX && pid <= (PID_MAX + MAX_NO_CPUS)) { + + return idle_task(pid - PID_MAX); + } else { + /* + * find_task_by_pid is relatively safe all the time + * Other pid functions require lock downs which imply + * that we may be interrupting them (as we get here + * in the middle of most any lock down). + * Still we don't want to call until the table exists! + */ + if (kgdb_pid_init_done){ + thread = find_task_by_pid(pid); + if (thread) { + return thread; + } + } + } + return NULL; +} +/* *INDENT-OFF* */ +struct hw_breakpoint { + unsigned enabled; + unsigned type; + unsigned len; + unsigned addr; +} breakinfo[4] = { {enabled:0}, + {enabled:0}, + {enabled:0}, + {enabled:0}}; +/* *INDENT-ON* */ +unsigned hw_breakpoint_status; +void +correct_hw_break(void) +{ + int breakno; + int correctit; + int breakbit; + unsigned dr7; + + asm volatile ("movl %%db7, %0\n":"=r" (dr7) + :); + /* *INDENT-OFF* */ + do { + unsigned addr0, addr1, addr2, addr3; + asm volatile ("movl %%db0, %0\n" + "movl %%db1, %1\n" + "movl %%db2, %2\n" + "movl %%db3, %3\n" + :"=r" (addr0), "=r"(addr1), + "=r"(addr2), "=r"(addr3) + :); + } while (0); + /* *INDENT-ON* */ + correctit = 0; + for (breakno = 0; breakno < 3; breakno++) { + breakbit = 2 << (breakno << 1); + if (!(dr7 & breakbit) && breakinfo[breakno].enabled) { + correctit = 1; + dr7 |= breakbit; + dr7 &= ~(0xf0000 << (breakno << 2)); + dr7 |= (((breakinfo[breakno].len << 2) | + breakinfo[breakno].type) << 16) << + (breakno << 2); + switch (breakno) { + case 0: + asm volatile ("movl %0, %%dr0\n"::"r" + (breakinfo[breakno].addr)); + break; + + case 1: + asm volatile ("movl %0, %%dr1\n"::"r" + (breakinfo[breakno].addr)); + break; + + case 2: + asm volatile ("movl %0, %%dr2\n"::"r" + (breakinfo[breakno].addr)); + break; + + case 3: + asm volatile ("movl %0, %%dr3\n"::"r" + (breakinfo[breakno].addr)); + break; + } + } else if ((dr7 & breakbit) && !breakinfo[breakno].enabled) { + correctit = 1; + dr7 &= ~breakbit; + dr7 &= ~(0xf0000 << (breakno << 2)); + } + } + if (correctit) { + asm volatile ("movl %0, %%db7\n"::"r" (dr7)); + } +} + +int +remove_hw_break(unsigned breakno) +{ + if (!breakinfo[breakno].enabled) { + return -1; + } + breakinfo[breakno].enabled = 0; + return 0; +} + +int +set_hw_break(unsigned breakno, unsigned type, unsigned len, unsigned addr) +{ + if (breakinfo[breakno].enabled) { + return -1; + } + breakinfo[breakno].enabled = 1; + breakinfo[breakno].type = type; + breakinfo[breakno].len = len; + breakinfo[breakno].addr = addr; + return 0; +} + +#ifdef CONFIG_SMP +static int in_kgdb_console = 0; + +int +in_kgdb(struct pt_regs *regs) +{ + unsigned flags; + int cpu = smp_processor_id(); + in_kgdb_called = 1; + if (!spin_is_locked(&kgdb_spinlock)) { + if (in_kgdb_here_log[cpu] || /* we are holding this cpu */ + in_kgdb_console) { /* or we are doing slow i/o */ + return 1; + } + return 0; + } + + /* As I see it the only reason not to let all cpus spin on + * the same spin_lock is to allow selected ones to proceed. + * This would be a good thing, so we leave it this way. + * Maybe someday.... Done ! + + * in_kgdb() is called from an NMI so we don't pretend + * to have any resources, like printk() for example. + */ + + kgdb_local_irq_save(flags); /* only local here, to avoid hanging */ + /* + * log arival of this cpu + * The NMI keeps on ticking. Protect against recurring more + * than once, and ignor the cpu that has the kgdb lock + */ + in_kgdb_entry_log[cpu]++; + in_kgdb_here_log[cpu] = regs; + if (cpu == spinlock_cpu || waiting_cpus[cpu].task) + goto exit_in_kgdb; + + /* + * For protection of the initilization of the spin locks by kgdb + * it locks the kgdb spinlock before it gets the wait locks set + * up. We wait here for the wait lock to be taken. If the + * kgdb lock goes away first?? Well, it could be a slow exit + * sequence where the wait lock is removed prior to the kgdb lock + * so if kgdb gets unlocked, we just exit. + */ + + while (spin_is_locked(&kgdb_spinlock) && + !spin_is_locked(waitlocks + cpu)) ; + if (!spin_is_locked(&kgdb_spinlock)) + goto exit_in_kgdb; + + waiting_cpus[cpu].task = current; + waiting_cpus[cpu].pid = (current->pid) ? : (PID_MAX + cpu); + waiting_cpus[cpu].regs = regs; + + spin_unlock_wait(waitlocks + cpu); + + /* + * log departure of this cpu + */ + waiting_cpus[cpu].task = 0; + waiting_cpus[cpu].pid = 0; + waiting_cpus[cpu].regs = 0; + correct_hw_break(); + exit_in_kgdb: + in_kgdb_here_log[cpu] = 0; + kgdb_local_irq_restore(flags); + return 1; + /* + spin_unlock(continuelocks + smp_processor_id()); + */ +} + +void +smp__in_kgdb(struct pt_regs regs) +{ + ack_APIC_irq(); + in_kgdb(®s); +} +#else +int +in_kgdb(struct pt_regs *regs) +{ + return (kgdb_spinlock); +} +#endif + +void +printexceptioninfo(int exceptionNo, int errorcode, char *buffer) +{ + unsigned dr6; + int i; + switch (exceptionNo) { + case 1: /* debug exception */ + break; + case 3: /* breakpoint */ + sprintf(buffer, "Software breakpoint"); + return; + default: + sprintf(buffer, "Details not available"); + return; + } + asm volatile ("movl %%db6, %0\n":"=r" (dr6) + :); + if (dr6 & 0x4000) { + sprintf(buffer, "Single step"); + return; + } + for (i = 0; i < 4; ++i) { + if (dr6 & (1 << i)) { + sprintf(buffer, "Hardware breakpoint %d", i); + return; + } + } + sprintf(buffer, "Unknown trap"); + return; +} + +/* + * This function does all command procesing for interfacing to gdb. + * + * NOTE: The INT nn instruction leaves the state of the interrupt + * enable flag UNCHANGED. That means that when this routine + * is entered via a breakpoint (INT 3) instruction from code + * that has interrupts enabled, then interrupts will STILL BE + * enabled when this routine is entered. The first thing that + * we do here is disable interrupts so as to prevent recursive + * entries and bothersome serial interrupts while we are + * trying to run the serial port in polled mode. + * + * For kernel version 2.1.xx the kgdb_cli() actually gets a spin lock so + * it is always necessary to do a restore_flags before returning + * so as to let go of that lock. + */ +int +kgdb_handle_exception(int exceptionVector, + int signo, int err_code, struct pt_regs *linux_regs) +{ + struct task_struct *usethread = NULL; + struct task_struct *thread_list_start = 0, *thread = NULL; + int addr, length; + int breakno, breaktype; + char *ptr; + int newPC; + threadref thref; + int threadid; + int thread_min = PID_MAX + MAX_NO_CPUS; +#ifdef old_thread_list + int maxthreads; +#endif + int nothreads; + unsigned long flags; + int gdb_regs[NUMREGBYTES / 4]; + int dr6; + IF_SMP(int entry_state = 0); /* 0, ok, 1, no nmi, 2 sync failed */ +#define NO_NMI 1 +#define NO_SYNC 2 +#define regs (*linux_regs) +#define NUMREGS NUMREGBYTES/4 + /* + * If the entry is not from the kernel then return to the Linux + * trap handler and let it process the interrupt normally. + */ + if ((linux_regs->eflags & VM_MASK) || (3 & linux_regs->xcs)) { + printk("ignoring non-kernel exception\n"); + print_regs(®s); + return (0); + } + /* + * If we're using eth mode, set the 'mode' in the netdevice. + */ + + if (kgdboe) + netpoll_set_trap(1); + + kgdb_local_irq_save(flags); + + /* Get kgdb spinlock */ + + KGDB_SPIN_LOCK(&kgdb_spinlock); + rdtscll(kgdb_info.entry_tsc); + /* + * We depend on this spinlock and the NMI watch dog to control the + * other cpus. They will arrive at "in_kgdb()" as a result of the + * NMI and will wait there for the following spin locks to be + * released. + */ +#ifdef CONFIG_SMP + +#if 0 + if (cpu_callout_map & ~MAX_CPU_MASK) { + printk("kgdb : too many cpus, possibly not mapped" + " in contiguous space, change MAX_NO_CPUS" + " in kgdb_stub and make new kernel.\n" + " cpu_callout_map is %lx\n", cpu_callout_map); + goto exit_just_unlock; + } +#endif + if (spinlock_count == 1) { + int time = 0, end_time, dum = 0; + int i; + int cpu_logged_in[MAX_NO_CPUS] = {[0 ... MAX_NO_CPUS - 1] = (0) + }; + if (remote_debug) { + printk("kgdb : cpu %d entry, syncing others\n", + smp_processor_id()); + } + for (i = 0; i < MAX_NO_CPUS; i++) { + /* + * Use trylock as we may already hold the lock if + * we are holding the cpu. Net result is all + * locked. + */ + spin_trylock(&waitlocks[i]); + } + for (i = 0; i < MAX_NO_CPUS; i++) + cpu_logged_in[i] = 0; + /* + * Wait for their arrival. We know the watch dog is active if + * in_kgdb() has ever been called, as it is always called on a + * watchdog tick. + */ + rdtsc(dum, time); + end_time = time + 2; /* Note: we use the High order bits! */ + i = 1; + if (num_online_cpus() > 1) { + int me_in_kgdb = in_kgdb_entry_log[smp_processor_id()]; + smp_send_nmi_allbutself(); + + while (i < num_online_cpus() && time != end_time) { + int j; + for (j = 0; j < MAX_NO_CPUS; j++) { + if (waiting_cpus[j].task && + waiting_cpus[j].task != NOCPU && + !cpu_logged_in[j]) { + i++; + cpu_logged_in[j] = 1; + if (remote_debug) { + printk + ("kgdb : cpu %d arrived at kgdb\n", + j); + } + break; + } else if (!waiting_cpus[j].task && + !cpu_online(j)) { + waiting_cpus[j].task = NOCPU; + cpu_logged_in[j] = 1; + waiting_cpus[j].hold = 1; + break; + } + if (!waiting_cpus[j].task && + in_kgdb_here_log[j]) { + + int wait = 100000; + while (wait--) ; + if (!waiting_cpus[j].task && + in_kgdb_here_log[j]) { + printk + ("kgdb : cpu %d stall" + " in in_kgdb\n", + j); + i++; + cpu_logged_in[j] = 1; + waiting_cpus[j].task = + (struct task_struct + *) 1; + } + } + } + + if (in_kgdb_entry_log[smp_processor_id()] > + (me_in_kgdb + 10)) { + break; + } + + rdtsc(dum, time); + } + if (i < num_online_cpus()) { + printk + ("kgdb : time out, proceeding without sync\n"); +#if 0 + printk("kgdb : Waiting_cpus: 0 = %d, 1 = %d\n", + waiting_cpus[0].task != 0, + waiting_cpus[1].task != 0); + printk("kgdb : Cpu_logged in: 0 = %d, 1 = %d\n", + cpu_logged_in[0], cpu_logged_in[1]); + printk + ("kgdb : in_kgdb_here_log in: 0 = %d, 1 = %d\n", + in_kgdb_here_log[0] != 0, + in_kgdb_here_log[1] != 0); +#endif + entry_state = NO_SYNC; + } else { +#if 0 + int ent = + in_kgdb_entry_log[smp_processor_id()] - + me_in_kgdb; + printk("kgdb : sync after %d entries\n", ent); +#endif + } + } else { + if (remote_debug) { + printk + ("kgdb : %d cpus, but watchdog not active\n" + "proceeding without locking down other cpus\n", + num_online_cpus()); + entry_state = NO_NMI; + } + } + } +#endif + + if (remote_debug) { + unsigned long *lp = (unsigned long *) &linux_regs; + + printk("handle_exception(exceptionVector=%d, " + "signo=%d, err_code=%d, linux_regs=%p)\n", + exceptionVector, signo, err_code, linux_regs); + if (debug_regs) { + print_regs(®s); + printk("Stk: %8lx %8lx %8lx %8lx" + " %8lx %8lx %8lx %8lx\n", + lp[0], lp[1], lp[2], lp[3], + lp[4], lp[5], lp[6], lp[7]); + printk(" %8lx %8lx %8lx %8lx" + " %8lx %8lx %8lx %8lx\n", + lp[8], lp[9], lp[10], lp[11], + lp[12], lp[13], lp[14], lp[15]); + printk(" %8lx %8lx %8lx %8lx " + "%8lx %8lx %8lx %8lx\n", + lp[16], lp[17], lp[18], lp[19], + lp[20], lp[21], lp[22], lp[23]); + printk(" %8lx %8lx %8lx %8lx " + "%8lx %8lx %8lx %8lx\n", + lp[24], lp[25], lp[26], lp[27], + lp[28], lp[29], lp[30], lp[31]); + } + } + + /* Disable hardware debugging while we are in kgdb */ + /* Get the debug register status register */ +/* *INDENT-OFF* */ + __asm__("movl %0,%%db7" + : /* no output */ + :"r"(0)); + + asm volatile ("movl %%db6, %0\n" + :"=r" (hw_breakpoint_status) + :); + +/* *INDENT-ON* */ + switch (exceptionVector) { + case 0: /* divide error */ + case 1: /* debug exception */ + case 2: /* NMI */ + case 3: /* breakpoint */ + case 4: /* overflow */ + case 5: /* bounds check */ + case 6: /* invalid opcode */ + case 7: /* device not available */ + case 8: /* double fault (errcode) */ + case 10: /* invalid TSS (errcode) */ + case 12: /* stack fault (errcode) */ + case 16: /* floating point error */ + case 17: /* alignment check (errcode) */ + default: /* any undocumented */ + break; + case 11: /* segment not present (errcode) */ + case 13: /* general protection (errcode) */ + case 14: /* page fault (special errcode) */ + case 19: /* cache flush denied */ + if (mem_err_expected) { + /* + * This fault occured because of the + * get_char or set_char routines. These + * two routines use either eax of edx to + * indirectly reference the location in + * memory that they are working with. + * For a page fault, when we return the + * instruction will be retried, so we + * have to make sure that these + * registers point to valid memory. + */ + mem_err = 1; /* set mem error flag */ + mem_err_expected = 0; + mem_err_cnt++; /* helps in debugging */ + /* make valid address */ + regs.eax = (long) &garbage_loc; + /* make valid address */ + regs.edx = (long) &garbage_loc; + if (remote_debug) + printk("Return after memory error: " + "mem_err_cnt=%d\n", mem_err_cnt); + if (debug_regs) + print_regs(®s); + goto exit_kgdb; + } + break; + } + if (remote_debug) + printk("kgdb : entered kgdb on cpu %d\n", smp_processor_id()); + + gdb_i386vector = exceptionVector; + gdb_i386errcode = err_code; + kgdb_info.called_from = __builtin_return_address(0); +#ifdef CONFIG_SMP + /* + * OK, we can now communicate, lets tell gdb about the sync. + * but only if we had a problem. + */ + switch (entry_state) { + case NO_NMI: + to_gdb("NMI not active, other cpus not stopped\n"); + break; + case NO_SYNC: + to_gdb("Some cpus not stopped, see 'kgdb_info' for details\n"); + default:; + } + +#endif +/* + * Set up the gdb function call area. + */ + trap_cpu = smp_processor_id(); + OLD_esp = NEW_esp = (int) (&linux_regs->esp); + + IF_SMP(once_again:) + /* reply to host that an exception has occurred */ + remcomOutBuffer[0] = 'S'; + remcomOutBuffer[1] = hexchars[signo >> 4]; + remcomOutBuffer[2] = hexchars[signo % 16]; + remcomOutBuffer[3] = 0; + + putpacket(remcomOutBuffer); + + while (1 == 1) { + error = 0; + remcomOutBuffer[0] = 0; + getpacket(remcomInBuffer); + switch (remcomInBuffer[0]) { + case '?': + remcomOutBuffer[0] = 'S'; + remcomOutBuffer[1] = hexchars[signo >> 4]; + remcomOutBuffer[2] = hexchars[signo % 16]; + remcomOutBuffer[3] = 0; + break; + case 'd': + remote_debug = !(remote_debug); /* toggle debug flag */ + printk("Remote debug %s\n", + remote_debug ? "on" : "off"); + break; + case 'g': /* return the value of the CPU registers */ + get_gdb_regs(usethread, ®s, gdb_regs); + mem2hex((char *) gdb_regs, + remcomOutBuffer, NUMREGBYTES, 0); + break; + case 'G': /* set the value of the CPU registers - return OK */ + hex2mem(&remcomInBuffer[1], + (char *) gdb_regs, NUMREGBYTES, 0); + if (!usethread || usethread == current) { + gdb_regs_to_regs(gdb_regs, ®s); + strcpy(remcomOutBuffer, "OK"); + } else { + strcpy(remcomOutBuffer, "E00"); + } + break; + + case 'P':{ /* set the value of a single CPU register - + return OK */ + /* + * For some reason, gdb wants to talk about psudo + * registers (greater than 15). These may have + * meaning for ptrace, but for us it is safe to + * ignor them. We do this by dumping them into + * _GS which we also ignor, but do have memory for. + */ + int regno; + + ptr = &remcomInBuffer[1]; + regs_to_gdb_regs(gdb_regs, ®s); + if ((!usethread || usethread == current) && + hexToInt(&ptr, ®no) && + *ptr++ == '=' && (regno >= 0)) { + regno = + (regno >= NUMREGS ? _GS : regno); + hex2mem(ptr, (char *) &gdb_regs[regno], + 4, 0); + gdb_regs_to_regs(gdb_regs, ®s); + strcpy(remcomOutBuffer, "OK"); + break; + } + strcpy(remcomOutBuffer, "E01"); + break; + } + + /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ + case 'm': + /* TRY TO READ %x,%x. IF SUCCEED, SET PTR = 0 */ + ptr = &remcomInBuffer[1]; + if (hexToInt(&ptr, &addr) && + (*(ptr++) == ',') && (hexToInt(&ptr, &length))) { + ptr = 0; + /* + * hex doubles the byte count + */ + if (length > (BUFMAX / 2)) + length = BUFMAX / 2; + mem2hex((char *) addr, + remcomOutBuffer, length, 1); + if (mem_err) { + strcpy(remcomOutBuffer, "E03"); + debug_error("memory fault\n", NULL); + } + } + + if (ptr) { + strcpy(remcomOutBuffer, "E01"); + debug_error + ("malformed read memory command: %s\n", + remcomInBuffer); + } + break; + + /* MAA..AA,LLLL: + Write LLLL bytes at address AA.AA return OK */ + case 'M': + /* TRY TO READ '%x,%x:'. IF SUCCEED, SET PTR = 0 */ + ptr = &remcomInBuffer[1]; + if (hexToInt(&ptr, &addr) && + (*(ptr++) == ',') && + (hexToInt(&ptr, &length)) && (*(ptr++) == ':')) { + hex2mem(ptr, (char *) addr, length, 1); + + if (mem_err) { + strcpy(remcomOutBuffer, "E03"); + debug_error("memory fault\n", NULL); + } else { + strcpy(remcomOutBuffer, "OK"); + } + + ptr = 0; + } + if (ptr) { + strcpy(remcomOutBuffer, "E02"); + debug_error + ("malformed write memory command: %s\n", + remcomInBuffer); + } + break; + case 'S': + remcomInBuffer[0] = 's'; + case 'C': + /* Csig;AA..AA where ;AA..AA is optional + * continue with signal + * Since signals are meaning less to us, delete that + * part and then fall into the 'c' code. + */ + ptr = &remcomInBuffer[1]; + length = 2; + while (*ptr && *ptr != ';') { + length++; + ptr++; + } + if (*ptr) { + do { + ptr++; + *(ptr - length++) = *ptr; + } while (*ptr); + } else { + remcomInBuffer[1] = 0; + } + + /* cAA..AA Continue at address AA..AA(optional) */ + /* sAA..AA Step one instruction from AA..AA(optional) */ + /* D detach, reply OK and then continue */ + case 'c': + case 's': + case 'D': + + /* try to read optional parameter, + pc unchanged if no parm */ + ptr = &remcomInBuffer[1]; + if (hexToInt(&ptr, &addr)) { + if (remote_debug) + printk("Changing EIP to 0x%x\n", addr); + + regs.eip = addr; + } + + newPC = regs.eip; + + /* clear the trace bit */ + regs.eflags &= 0xfffffeff; + + /* set the trace bit if we're stepping */ + if (remcomInBuffer[0] == 's') + regs.eflags |= 0x100; + + /* detach is a friendly version of continue. Note that + debugging is still enabled (e.g hit control C) + */ + if (remcomInBuffer[0] == 'D') { + strcpy(remcomOutBuffer, "OK"); + putpacket(remcomOutBuffer); + } + + if (remote_debug) { + printk("Resuming execution\n"); + print_regs(®s); + } + asm volatile ("movl %%db6, %0\n":"=r" (dr6) + :); + if (!(dr6 & 0x4000)) { + for (breakno = 0; breakno < 4; ++breakno) { + if (dr6 & (1 << breakno) && + (breakinfo[breakno].type == 0)) { + /* Set restore flag */ + regs.eflags |= 0x10000; + break; + } + } + } + + if (kgdboe) + netpoll_set_trap(0); + + correct_hw_break(); + asm volatile ("movl %0, %%db6\n"::"r" (0)); + goto exit_kgdb; + + /* kill the program */ + case 'k': /* do nothing */ + break; + + /* query */ + case 'q': + nothreads = 0; + switch (remcomInBuffer[1]) { + case 'f': + threadid = 1; + thread_list = 2; + thread_list_start = (usethread ? : current); + case 's': + if (!cmp_str(&remcomInBuffer[2], + "ThreadInfo", 10)) + break; + + remcomOutBuffer[nothreads++] = 'm'; + for (; threadid < PID_MAX + MAX_NO_CPUS; + threadid++) { + thread = getthread(threadid); + if (thread) { + nothreads += int_to_hex_v( + &remcomOutBuffer[ + nothreads], + threadid); + if (thread_min > threadid) + thread_min = threadid; + remcomOutBuffer[ + nothreads] = ','; + nothreads++; + if (nothreads > BUFMAX - 10) + break; + } + } + if (remcomOutBuffer[nothreads - 1] == 'm') { + remcomOutBuffer[nothreads - 1] = 'l'; + } else { + nothreads--; + } + remcomOutBuffer[nothreads] = 0; + break; + +#ifdef old_thread_list /* Old thread info request */ + case 'L': + /* List threads */ + thread_list = 2; + thread_list_start = (usethread ? : current); + unpack_byte(remcomInBuffer + 3, &maxthreads); + unpack_threadid(remcomInBuffer + 5, &thref); + do { + int buf_thread_limit = + (BUFMAX - 22) / BUF_THREAD_ID_SIZE; + if (maxthreads > buf_thread_limit) { + maxthreads = buf_thread_limit; + } + } while (0); + remcomOutBuffer[0] = 'q'; + remcomOutBuffer[1] = 'M'; + remcomOutBuffer[4] = '0'; + pack_threadid(remcomOutBuffer + 5, &thref); + + threadid = threadref_to_int(&thref); + for (nothreads = 0; + nothreads < maxthreads && + threadid < PID_MAX + MAX_NO_CPUS; + threadid++) { + thread = getthread(threadid); + if (thread) { + int_to_threadref(&thref, + threadid); + pack_threadid(remcomOutBuffer + + 21 + + nothreads * 16, + &thref); + nothreads++; + if (thread_min > threadid) + thread_min = threadid; + } + } + + if (threadid == PID_MAX + MAX_NO_CPUS) { + remcomOutBuffer[4] = '1'; + } + pack_hex_byte(remcomOutBuffer + 2, nothreads); + remcomOutBuffer[21 + nothreads * 16] = '\0'; + break; +#endif + case 'C': + /* Current thread id */ + remcomOutBuffer[0] = 'Q'; + remcomOutBuffer[1] = 'C'; + threadid = current->pid; + if (!threadid) { + /* + * idle thread + */ + for (threadid = PID_MAX; + threadid < PID_MAX + MAX_NO_CPUS; + threadid++) { + if (current == + idle_task(threadid - + PID_MAX)) + break; + } + } + int_to_threadref(&thref, threadid); + pack_threadid(remcomOutBuffer + 2, &thref); + remcomOutBuffer[18] = '\0'; + break; + + case 'E': + /* Print exception info */ + printexceptioninfo(exceptionVector, + err_code, remcomOutBuffer); + break; + case 'T':{ + char * nptr; + /* Thread extra info */ + if (!cmp_str(&remcomInBuffer[2], + "hreadExtraInfo,", 15)) { + break; + } + ptr = &remcomInBuffer[17]; + hexToInt(&ptr, &threadid); + thread = getthread(threadid); + nptr = &thread->comm[0]; + length = 0; + ptr = &remcomOutBuffer[0]; + do { + length++; + ptr = pack_hex_byte(ptr, *nptr++); + } while (*nptr && length < 16); + /* + * would like that 16 to be the size of + * task_struct.comm but don't know the + * syntax.. + */ + *ptr = 0; + } + } + break; + + /* task related */ + case 'H': + switch (remcomInBuffer[1]) { + case 'g': + ptr = &remcomInBuffer[2]; + hexToInt(&ptr, &threadid); + thread = getthread(threadid); + if (!thread) { + remcomOutBuffer[0] = 'E'; + remcomOutBuffer[1] = '\0'; + break; + } + /* + * Just in case I forget what this is all about, + * the "thread info" command to gdb causes it + * to ask for a thread list. It then switches + * to each thread and asks for the registers. + * For this (and only this) usage, we want to + * fudge the registers of tasks not on the run + * list (i.e. waiting) to show the routine that + * called schedule. Also, gdb, is a minimalist + * in that if the current thread is the last + * it will not re-read the info when done. + * This means that in this case we must show + * the real registers. So here is how we do it: + * Each entry we keep track of the min + * thread in the list (the last that gdb will) + * get info for. We also keep track of the + * starting thread. + * "thread_list" is cleared when switching back + * to the min thread if it is was current, or + * if it was not current, thread_list is set + * to 1. When the switch to current comes, + * if thread_list is 1, clear it, else do + * nothing. + */ + usethread = thread; + if ((thread_list == 1) && + (thread == thread_list_start)) { + thread_list = 0; + } + if (thread_list && (threadid == thread_min)) { + if (thread == thread_list_start) { + thread_list = 0; + } else { + thread_list = 1; + } + } + /* follow through */ + case 'c': + remcomOutBuffer[0] = 'O'; + remcomOutBuffer[1] = 'K'; + remcomOutBuffer[2] = '\0'; + break; + } + break; + + /* Query thread status */ + case 'T': + ptr = &remcomInBuffer[1]; + hexToInt(&ptr, &threadid); + thread = getthread(threadid); + if (thread) { + remcomOutBuffer[0] = 'O'; + remcomOutBuffer[1] = 'K'; + remcomOutBuffer[2] = '\0'; + if (thread_min > threadid) + thread_min = threadid; + } else { + remcomOutBuffer[0] = 'E'; + remcomOutBuffer[1] = '\0'; + } + break; + + case 'Y': /* set up a hardware breakpoint */ + ptr = &remcomInBuffer[1]; + hexToInt(&ptr, &breakno); + ptr++; + hexToInt(&ptr, &breaktype); + ptr++; + hexToInt(&ptr, &length); + ptr++; + hexToInt(&ptr, &addr); + if (set_hw_break(breakno & 0x3, + breaktype & 0x3, + length & 0x3, addr) == 0) { + strcpy(remcomOutBuffer, "OK"); + } else { + strcpy(remcomOutBuffer, "ERROR"); + } + break; + + /* Remove hardware breakpoint */ + case 'y': + ptr = &remcomInBuffer[1]; + hexToInt(&ptr, &breakno); + if (remove_hw_break(breakno & 0x3) == 0) { + strcpy(remcomOutBuffer, "OK"); + } else { + strcpy(remcomOutBuffer, "ERROR"); + } + break; + + case 'r': /* reboot */ + strcpy(remcomOutBuffer, "OK"); + putpacket(remcomOutBuffer); + /*to_gdb("Rebooting\n"); */ + /* triplefault no return from here */ + { + static long no_idt[2]; + __asm__ __volatile__("lidt %0"::"m"(no_idt[0])); + BREAKPOINT; + } + + } /* switch */ + + /* reply to the request */ + putpacket(remcomOutBuffer); + } /* while(1==1) */ + /* + * reached by goto only. + */ + exit_kgdb: + /* + * Here is where we set up to trap a gdb function call. NEW_esp + * will be changed if we are trying to do this. We handle both + * adding and subtracting, thus allowing gdb to put grung on + * the stack which it removes later. + */ + if (NEW_esp != OLD_esp) { + int *ptr = END_OF_LOOKASIDE; + if (NEW_esp < OLD_esp) + ptr -= (OLD_esp - NEW_esp) / sizeof (int); + *--ptr = linux_regs->eflags; + *--ptr = linux_regs->xcs; + *--ptr = linux_regs->eip; + *--ptr = linux_regs->ecx; + *--ptr = linux_regs->ebx; + *--ptr = linux_regs->eax; + linux_regs->ecx = NEW_esp - (sizeof (int) * 6); + linux_regs->ebx = (unsigned int) END_OF_LOOKASIDE; + if (NEW_esp < OLD_esp) { + linux_regs->eip = (unsigned int) fn_call_stub; + } else { + linux_regs->eip = (unsigned int) fn_rtn_stub; + linux_regs->eax = NEW_esp; + } + linux_regs->eflags &= ~(IF_BIT | TF_BIT); + } +#ifdef CONFIG_SMP + /* + * Release gdb wait locks + * Sanity check time. Must have at least one cpu to run. Also single + * step must not be done if the current cpu is on hold. + */ + if (spinlock_count == 1) { + int ss_hold = (regs.eflags & 0x100) && kgdb_info.hold_on_sstep; + int cpu_avail = 0; + int i; + + for (i = 0; i < MAX_NO_CPUS; i++) { + if (!cpu_online(i)) + break; + if (!hold_cpu(i)) { + cpu_avail = 1; + } + } + /* + * Early in the bring up there will be NO cpus on line... + */ + if (!cpu_avail && !cpus_empty(cpu_online_map)) { + to_gdb("No cpus unblocked, see 'kgdb_info.hold_cpu'\n"); + goto once_again; + } + if (hold_cpu(smp_processor_id()) && (regs.eflags & 0x100)) { + to_gdb + ("Current cpu must be unblocked to single step\n"); + goto once_again; + } + if (!(ss_hold)) { + int i; + for (i = 0; i < MAX_NO_CPUS; i++) { + if (!hold_cpu(i)) { + spin_unlock(&waitlocks[i]); + } + } + } else { + spin_unlock(&waitlocks[smp_processor_id()]); + } + /* Release kgdb spinlock */ + KGDB_SPIN_UNLOCK(&kgdb_spinlock); + /* + * If this cpu is on hold, this is where we + * do it. Note, the NMI will pull us out of here, + * but will return as the above lock is not held. + * We will stay here till another cpu releases the lock for us. + */ + spin_unlock_wait(waitlocks + smp_processor_id()); + kgdb_local_irq_restore(flags); + return (0); + } +#if 0 +exit_just_unlock: +#endif +#endif + /* Release kgdb spinlock */ + KGDB_SPIN_UNLOCK(&kgdb_spinlock); + kgdb_local_irq_restore(flags); + return (0); +} + +/* this function is used to set up exception handlers for tracing and + * breakpoints. + * This function is not needed as the above line does all that is needed. + * We leave it for backward compatitability... + */ +void +set_debug_traps(void) +{ + /* + * linux_debug_hook is defined in traps.c. We store a pointer + * to our own exception handler into it. + + * But really folks, every hear of labeled common, an old Fortran + * concept. Lots of folks can reference it and it is define if + * anyone does. Only one can initialize it at link time. We do + * this with the hook. See the statement above. No need for any + * executable code and it is ready as soon as the kernel is + * loaded. Very desirable in kernel debugging. + + linux_debug_hook = handle_exception ; + */ + + /* In case GDB is started before us, ack any packets (presumably + "$?#xx") sitting there. + putDebugChar ('+'); + + initialized = 1; + */ +} + +/* This function will generate a breakpoint exception. It is used at the + beginning of a program to sync up with a debugger and can be used + otherwise as a quick means to stop program execution and "break" into + the debugger. */ +/* But really, just use the BREAKPOINT macro. We will handle the int stuff + */ + +#ifdef later +/* + * possibly we should not go thru the traps.c code at all? Someday. + */ +void +do_kgdb_int3(struct pt_regs *regs, long error_code) +{ + kgdb_handle_exception(3, 5, error_code, regs); + return; +} +#endif +#undef regs +#ifdef CONFIG_TRAP_BAD_SYSCALL_EXITS +asmlinkage void +bad_sys_call_exit(int stuff) +{ + struct pt_regs *regs = (struct pt_regs *) &stuff; + printk("Sys call %d return with %x preempt_count\n", + (int) regs->orig_eax, preempt_count()); +} +#endif +#ifdef CONFIG_STACK_OVERFLOW_TEST +#include +asmlinkage void +stack_overflow(void) +{ +#ifdef BREAKPOINT + BREAKPOINT; +#else + printk("Kernel stack overflow, looping forever\n"); +#endif + while (1) { + } +} +#endif + +#if defined(CONFIG_SMP) || defined(CONFIG_KGDB_CONSOLE) +char gdbconbuf[BUFMAX]; + +static void +kgdb_gdb_message(const char *s, unsigned count) +{ + int i; + int wcount; + char *bufptr; + /* + * This takes care of NMI while spining out chars to gdb + */ + IF_SMP(in_kgdb_console = 1); + gdbconbuf[0] = 'O'; + bufptr = gdbconbuf + 1; + while (count > 0) { + if ((count << 1) > (BUFMAX - 2)) { + wcount = (BUFMAX - 2) >> 1; + } else { + wcount = count; + } + count -= wcount; + for (i = 0; i < wcount; i++) { + bufptr = pack_hex_byte(bufptr, s[i]); + } + *bufptr = '\0'; + s += wcount; + + putpacket(gdbconbuf); + + } + IF_SMP(in_kgdb_console = 0); +} +#endif +#ifdef CONFIG_SMP +static void +to_gdb(const char *s) +{ + int count = 0; + while (s[count] && (count++ < BUFMAX)) ; + kgdb_gdb_message(s, count); +} +#endif +#ifdef CONFIG_KGDB_CONSOLE +#include +#include +#include +#include +#include + +void +kgdb_console_write(struct console *co, const char *s, unsigned count) +{ + + if (gdb_i386vector == -1) { + /* + * We have not yet talked to gdb. What to do... + * lets break, on continue we can do the write. + * But first tell him whats up. Uh, well no can do, + * as this IS the console. Oh well... + * We do need to wait or the messages will be lost. + * Other option would be to tell the above code to + * ignore this breakpoint and do an auto return, + * but that might confuse gdb. Also this happens + * early enough in boot up that we don't have the traps + * set up yet, so... + */ + breakpoint(); + } + kgdb_gdb_message(s, count); +} + +/* + * ------------------------------------------------------------ + * Serial KGDB driver + * ------------------------------------------------------------ + */ + +static struct console kgdbcons = { + name:"kgdb", + write:kgdb_console_write, +#ifdef CONFIG_KGDB_USER_CONSOLE + device:kgdb_console_device, +#endif + flags:CON_PRINTBUFFER | CON_ENABLED, + index:-1, +}; + +/* + * The trick here is that this file gets linked before printk.o + * That means we get to peer at the console info in the command + * line before it does. If we are up, we register, otherwise, + * do nothing. By returning 0, we allow printk to look also. + */ +static int kgdb_console_enabled; + +int __init +kgdb_console_init(char *str) +{ + if ((strncmp(str, "kgdb", 4) == 0) || (strncmp(str, "gdb", 3) == 0)) { + register_console(&kgdbcons); + kgdb_console_enabled = 1; + } + return 0; /* let others look at the string */ +} + +__setup("console=", kgdb_console_init); + +#ifdef CONFIG_KGDB_USER_CONSOLE +static kdev_t kgdb_console_device(struct console *c); +/* This stuff sort of works, but it knocks out telnet devices + * we are leaving it here in case we (or you) find time to figure it out + * better.. + */ + +/* + * We need a real char device as well for when the console is opened for user + * space activities. + */ + +static int +kgdb_consdev_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static ssize_t +kgdb_consdev_write(struct file *file, const char *buf, + size_t count, loff_t * ppos) +{ + int size, ret = 0; + static char kbuf[128]; + static DECLARE_MUTEX(sem); + + /* We are not reentrant... */ + if (down_interruptible(&sem)) + return -ERESTARTSYS; + + while (count > 0) { + /* need to copy the data from user space */ + size = count; + if (size > sizeof (kbuf)) + size = sizeof (kbuf); + if (copy_from_user(kbuf, buf, size)) { + ret = -EFAULT; + break;; + } + kgdb_console_write(&kgdbcons, kbuf, size); + count -= size; + ret += size; + buf += size; + } + + up(&sem); + + return ret; +} + +struct file_operations kgdb_consdev_fops = { + open:kgdb_consdev_open, + write:kgdb_consdev_write +}; +static kdev_t +kgdb_console_device(struct console *c) +{ + return MKDEV(TTYAUX_MAJOR, 1); +} + +/* + * This routine gets called from the serial stub in the i386/lib + * This is so it is done late in bring up (just before the console open). + */ +void +kgdb_console_finit(void) +{ + if (kgdb_console_enabled) { + char *cptr = cdevname(MKDEV(TTYAUX_MAJOR, 1)); + char *cp = cptr; + while (*cptr && *cptr != '(') + cptr++; + *cptr = 0; + unregister_chrdev(TTYAUX_MAJOR, cp); + register_chrdev(TTYAUX_MAJOR, "kgdb", &kgdb_consdev_fops); + } +} +#endif +#endif +#ifdef CONFIG_KGDB_TS +#include /* time stamp code */ +#include /* in_interrupt */ +#ifdef CONFIG_KGDB_TS_64 +#define DATA_POINTS 64 +#endif +#ifdef CONFIG_KGDB_TS_128 +#define DATA_POINTS 128 +#endif +#ifdef CONFIG_KGDB_TS_256 +#define DATA_POINTS 256 +#endif +#ifdef CONFIG_KGDB_TS_512 +#define DATA_POINTS 512 +#endif +#ifdef CONFIG_KGDB_TS_1024 +#define DATA_POINTS 1024 +#endif +#ifndef DATA_POINTS +#define DATA_POINTS 128 /* must be a power of two */ +#endif +#define INDEX_MASK (DATA_POINTS - 1) +#if (INDEX_MASK & DATA_POINTS) +#error "CONFIG_KGDB_TS_COUNT must be a power of 2" +#endif +struct kgdb_and_then_struct { +#ifdef CONFIG_SMP + int on_cpu; +#endif + struct task_struct *task; + long long at_time; + int from_ln; + char *in_src; + void *from; + int *with_shpf; + int data0; + int data1; +}; +struct kgdb_and_then_struct2 { +#ifdef CONFIG_SMP + int on_cpu; +#endif + struct task_struct *task; + long long at_time; + int from_ln; + char *in_src; + void *from; + int *with_shpf; + struct task_struct *t1; + struct task_struct *t2; +}; +struct kgdb_and_then_struct kgdb_data[DATA_POINTS]; + +struct kgdb_and_then_struct *kgdb_and_then = &kgdb_data[0]; +int kgdb_and_then_count; + +void +kgdb_tstamp(int line, char *source, int data0, int data1) +{ + static spinlock_t ts_spin = SPIN_LOCK_UNLOCKED; + int flags; + kgdb_local_irq_save(flags); + spin_lock(&ts_spin); + rdtscll(kgdb_and_then->at_time); +#ifdef CONFIG_SMP + kgdb_and_then->on_cpu = smp_processor_id(); +#endif + kgdb_and_then->task = current; + kgdb_and_then->from_ln = line; + kgdb_and_then->in_src = source; + kgdb_and_then->from = __builtin_return_address(0); + kgdb_and_then->with_shpf = (int *) (((flags & IF_BIT) >> 9) | + (preempt_count() << 8)); + kgdb_and_then->data0 = data0; + kgdb_and_then->data1 = data1; + kgdb_and_then = &kgdb_data[++kgdb_and_then_count & INDEX_MASK]; + spin_unlock(&ts_spin); + kgdb_local_irq_restore(flags); +#ifdef CONFIG_PREEMPT + +#endif + return; +} +#endif +typedef int gdb_debug_hook(int exceptionVector, + int signo, int err_code, struct pt_regs *linux_regs); +gdb_debug_hook *linux_debug_hook = &kgdb_handle_exception; /* histerical reasons... */ + +static int kgdb_need_breakpoint[NR_CPUS]; + +void kgdb_schedule_breakpoint(void) +{ + kgdb_need_breakpoint[smp_processor_id()] = 1; +} + +void kgdb_process_breakpoint(void) +{ + /* + * Handle a breakpoint queued from inside network driver code + * to avoid reentrancy issues + */ + if (kgdb_need_breakpoint[smp_processor_id()]) { + kgdb_need_breakpoint[smp_processor_id()] = 0; + BREAKPOINT; + } +} + --- linux-2.6.4-rc2/arch/i386/kernel/ldt.c 2003-11-09 16:45:04.000000000 -0800 +++ 25/arch/i386/kernel/ldt.c 2004-03-07 20:48:18.000000000 -0800 @@ -2,7 +2,7 @@ * linux/kernel/ldt.c * * Copyright (C) 1992 Krishna Balasubramanian and Linus Torvalds - * Copyright (C) 1999 Ingo Molnar + * Copyright (C) 1999, 2003 Ingo Molnar */ #include @@ -18,6 +18,8 @@ #include #include #include +#include +#include #ifdef CONFIG_SMP /* avoids "defined but not used" warnig */ static void flush_ldt(void *null) @@ -29,34 +31,31 @@ static void flush_ldt(void *null) static int alloc_ldt(mm_context_t *pc, int mincount, int reload) { - void *oldldt; - void *newldt; - int oldsize; + int oldsize, newsize, i; if (mincount <= pc->size) return 0; + /* + * LDT got larger - reallocate if necessary. + */ oldsize = pc->size; mincount = (mincount+511)&(~511); - if (mincount*LDT_ENTRY_SIZE > PAGE_SIZE) - newldt = vmalloc(mincount*LDT_ENTRY_SIZE); - else - newldt = kmalloc(mincount*LDT_ENTRY_SIZE, GFP_KERNEL); - - if (!newldt) - return -ENOMEM; - - if (oldsize) - memcpy(newldt, pc->ldt, oldsize*LDT_ENTRY_SIZE); - oldldt = pc->ldt; - memset(newldt+oldsize*LDT_ENTRY_SIZE, 0, (mincount-oldsize)*LDT_ENTRY_SIZE); - pc->ldt = newldt; - wmb(); + newsize = mincount*LDT_ENTRY_SIZE; + for (i = 0; i < newsize; i += PAGE_SIZE) { + int nr = i/PAGE_SIZE; + BUG_ON(i >= 64*1024); + if (!pc->ldt_pages[nr]) { + pc->ldt_pages[nr] = alloc_page(GFP_HIGHUSER); + if (!pc->ldt_pages[nr]) + return -ENOMEM; + clear_highpage(pc->ldt_pages[nr]); + } + } pc->size = mincount; - wmb(); - if (reload) { #ifdef CONFIG_SMP cpumask_t mask; + preempt_disable(); load_LDT(pc); mask = cpumask_of_cpu(smp_processor_id()); @@ -67,21 +66,20 @@ static int alloc_ldt(mm_context_t *pc, i load_LDT(pc); #endif } - if (oldsize) { - if (oldsize*LDT_ENTRY_SIZE > PAGE_SIZE) - vfree(oldldt); - else - kfree(oldldt); - } return 0; } static inline int copy_ldt(mm_context_t *new, mm_context_t *old) { - int err = alloc_ldt(new, old->size, 0); - if (err < 0) + int i, err, size = old->size, nr_pages = (size*LDT_ENTRY_SIZE + PAGE_SIZE-1)/PAGE_SIZE; + + err = alloc_ldt(new, size, 0); + if (err < 0) { + new->size = 0; return err; - memcpy(new->ldt, old->ldt, old->size*LDT_ENTRY_SIZE); + } + for (i = 0; i < nr_pages; i++) + copy_user_highpage(new->ldt_pages[i], old->ldt_pages[i], 0); return 0; } @@ -96,6 +94,7 @@ int init_new_context(struct task_struct init_MUTEX(&mm->context.sem); mm->context.size = 0; + memset(mm->context.ldt_pages, 0, sizeof(struct page *) * MAX_LDT_PAGES); old_mm = current->mm; if (old_mm && old_mm->context.size > 0) { down(&old_mm->context.sem); @@ -107,23 +106,21 @@ int init_new_context(struct task_struct /* * No need to lock the MM as we are the last user + * Do not touch the ldt register, we are already + * in the next thread. */ void destroy_context(struct mm_struct *mm) { - if (mm->context.size) { - if (mm == current->active_mm) - clear_LDT(); - if (mm->context.size*LDT_ENTRY_SIZE > PAGE_SIZE) - vfree(mm->context.ldt); - else - kfree(mm->context.ldt); - mm->context.size = 0; - } + int i, nr_pages = (mm->context.size*LDT_ENTRY_SIZE + PAGE_SIZE-1) / PAGE_SIZE; + + for (i = 0; i < nr_pages; i++) + __free_page(mm->context.ldt_pages[i]); + mm->context.size = 0; } static int read_ldt(void __user * ptr, unsigned long bytecount) { - int err; + int err, i; unsigned long size; struct mm_struct * mm = current->mm; @@ -138,8 +135,25 @@ static int read_ldt(void __user * ptr, u size = bytecount; err = 0; - if (copy_to_user(ptr, mm->context.ldt, size)) - err = -EFAULT; + /* + * This is necessary just in case we got here straight from a + * context-switch where the ptes were set but no tlb flush + * was done yet. We rather avoid doing a TLB flush in the + * context-switch path and do it here instead. + */ + __flush_tlb_global(); + + for (i = 0; i < size; i += PAGE_SIZE) { + int nr = i / PAGE_SIZE, bytes; + char *kaddr = kmap(mm->context.ldt_pages[nr]); + + bytes = size - i; + if (bytes > PAGE_SIZE) + bytes = PAGE_SIZE; + if (copy_to_user(ptr + i, kaddr, size - i)) + err = -EFAULT; + kunmap(mm->context.ldt_pages[nr]); + } up(&mm->context.sem); if (err < 0) return err; @@ -158,7 +172,7 @@ static int read_default_ldt(void __user err = 0; address = &default_ldt[0]; - size = 5*sizeof(struct desc_struct); + size = 5*LDT_ENTRY_SIZE; if (size > bytecount) size = bytecount; @@ -200,7 +214,15 @@ static int write_ldt(void __user * ptr, goto out_unlock; } - lp = (__u32 *) ((ldt_info.entry_number << 3) + (char *) mm->context.ldt); + /* + * No rescheduling allowed from this point to the install. + * + * We do a TLB flush for the same reason as in the read_ldt() path. + */ + preempt_disable(); + __flush_tlb_global(); + lp = (__u32 *) ((ldt_info.entry_number << 3) + + (char *) __kmap_atomic_vaddr(KM_LDT_PAGE0)); /* Allow LDTs to be cleared by the user. */ if (ldt_info.base_addr == 0 && ldt_info.limit == 0) { @@ -221,6 +243,7 @@ install: *lp = entry_1; *(lp+1) = entry_2; error = 0; + preempt_enable(); out_unlock: up(&mm->context.sem); @@ -248,3 +271,26 @@ asmlinkage int sys_modify_ldt(int func, } return ret; } + +/* + * load one particular LDT into the current CPU + */ +void load_LDT_nolock(mm_context_t *pc, int cpu) +{ + struct page **pages = pc->ldt_pages; + int count = pc->size; + int nr_pages, i; + + if (likely(!count)) { + pages = &default_ldt_page; + count = 5; + } + nr_pages = (count*LDT_ENTRY_SIZE + PAGE_SIZE-1) / PAGE_SIZE; + + for (i = 0; i < nr_pages; i++) { + __kunmap_atomic_type(KM_LDT_PAGE0 - i); + __kmap_atomic(pages[i], KM_LDT_PAGE0 - i); + } + set_ldt_desc(cpu, (void *)__kmap_atomic_vaddr(KM_LDT_PAGE0), count); + load_LDT_desc(); +} --- linux-2.6.4-rc2/arch/i386/kernel/Makefile 2004-03-03 23:12:42.000000000 -0800 +++ 25/arch/i386/kernel/Makefile 2004-03-07 20:48:18.000000000 -0800 @@ -7,19 +7,19 @@ extra-y := head.o init_task.o vmlinux.ld obj-y := process.o semaphore.o signal.o entry.o traps.o irq.o vm86.o \ ptrace.o i8259.o ioport.o ldt.o setup.o time.o sys_i386.o \ pci-dma.o i386_ksyms.o i387.o dmi_scan.o bootflag.o \ - doublefault.o + doublefault.o entry_trampoline.o obj-y += cpu/ obj-y += timers/ obj-$(CONFIG_ACPI_BOOT) += acpi/ obj-$(CONFIG_X86_BIOS_REBOOT) += reboot.o obj-$(CONFIG_MCA) += mca.o +obj-$(CONFIG_KGDB) += kgdb_stub.o obj-$(CONFIG_X86_MSR) += msr.o obj-$(CONFIG_X86_CPUID) += cpuid.o obj-$(CONFIG_MICROCODE) += microcode.o obj-$(CONFIG_APM) += apm.o -obj-$(CONFIG_X86_SMP) += smp.o smpboot.o -obj-$(CONFIG_X86_TRAMPOLINE) += trampoline.o +obj-$(CONFIG_X86_SMP) += smp.o smpboot.o trampoline.o obj-$(CONFIG_X86_MPPARSE) += mpparse.o obj-$(CONFIG_X86_LOCAL_APIC) += apic.o nmi.o obj-$(CONFIG_X86_IO_APIC) += io_apic.o --- linux-2.6.4-rc2/arch/i386/kernel/mpparse.c 2004-02-17 20:48:42.000000000 -0800 +++ 25/arch/i386/kernel/mpparse.c 2004-03-07 20:48:18.000000000 -0800 @@ -668,7 +668,7 @@ void __init get_smp_config (void) * Read the physical hardware table. Anything here will * override the defaults. */ - if (!smp_read_mpc((void *)mpf->mpf_physptr)) { + if (!smp_read_mpc((void *)phys_to_virt(mpf->mpf_physptr))) { smp_found_config = 0; printk(KERN_ERR "BIOS bug, MP table errors detected!...\n"); printk(KERN_ERR "... disabling SMP support. (tell your hw vendor)\n"); @@ -1156,7 +1156,7 @@ void __init mp_parse_prt (void) continue; } if ((1<irq = acpi_irq_to_vector(irq); continue; --- linux-2.6.4-rc2/arch/i386/kernel/nmi.c 2004-03-03 23:12:42.000000000 -0800 +++ 25/arch/i386/kernel/nmi.c 2004-03-07 20:48:05.000000000 -0800 @@ -31,9 +31,19 @@ #include #include +#ifdef CONFIG_KGDB +#include +#ifdef CONFIG_SMP +unsigned int nmi_watchdog = NMI_IO_APIC; +#else +unsigned int nmi_watchdog = NMI_LOCAL_APIC; +#endif +#else unsigned int nmi_watchdog = NMI_NONE; +#endif static unsigned int nmi_hz = HZ; -unsigned int nmi_perfctr_msr; /* the MSR to reset in NMI handler */ +static unsigned int nmi_perfctr_msr; /* the MSR to reset in NMI handler */ +static unsigned int nmi_p4_cccr_val; extern void show_registers(struct pt_regs *regs); /* nmi_active: @@ -66,7 +76,8 @@ int nmi_active; #define P4_ESCR_EVENT_SELECT(N) ((N)<<25) #define P4_ESCR_OS (1<<3) #define P4_ESCR_USR (1<<2) -#define P4_CCCR_OVF_PMI (1<<26) +#define P4_CCCR_OVF_PMI0 (1<<26) +#define P4_CCCR_OVF_PMI1 (1<<27) #define P4_CCCR_THRESHOLD(N) ((N)<<20) #define P4_CCCR_COMPLEMENT (1<<19) #define P4_CCCR_COMPARE (1<<18) @@ -79,7 +90,7 @@ int nmi_active; #define MSR_P4_IQ_COUNTER0 0x30C #define P4_NMI_CRU_ESCR0 (P4_ESCR_EVENT_SELECT(0x3F)|P4_ESCR_OS|P4_ESCR_USR) #define P4_NMI_IQ_CCCR0 \ - (P4_CCCR_OVF_PMI|P4_CCCR_THRESHOLD(15)|P4_CCCR_COMPLEMENT| \ + (P4_CCCR_OVF_PMI0|P4_CCCR_THRESHOLD(15)|P4_CCCR_COMPLEMENT| \ P4_CCCR_COMPARE|P4_CCCR_REQUIRED|P4_CCCR_ESCR_SELECT(4)|P4_CCCR_ENABLE) int __init check_nmi_watchdog (void) @@ -107,11 +118,6 @@ int __init check_nmi_watchdog (void) } printk("OK.\n"); - /* now that we know it works we can reduce NMI frequency to - something more reasonable; makes a difference in some configs */ - if (nmi_watchdog == NMI_LOCAL_APIC) - nmi_hz = 1; - return 0; } @@ -322,6 +328,11 @@ static int setup_p4_watchdog(void) return 0; nmi_perfctr_msr = MSR_P4_IQ_COUNTER0; + nmi_p4_cccr_val = P4_NMI_IQ_CCCR0; +#ifdef CONFIG_SMP + if (smp_num_siblings == 2) + nmi_p4_cccr_val |= P4_CCCR_OVF_PMI1; +#endif if (!(misc_enable & MSR_P4_MISC_ENABLE_PEBS_UNAVAIL)) clear_msr_range(0x3F1, 2); @@ -339,12 +350,14 @@ static int setup_p4_watchdog(void) Dprintk("setting P4_IQ_COUNTER0 to 0x%08lx\n", -(cpu_khz/nmi_hz*1000)); wrmsr(MSR_P4_IQ_COUNTER0, -(cpu_khz/nmi_hz*1000), -1); apic_write(APIC_LVTPC, APIC_DM_NMI); - wrmsr(MSR_P4_IQ_CCCR0, P4_NMI_IQ_CCCR0, 0); + wrmsr(MSR_P4_IQ_CCCR0, nmi_p4_cccr_val, 0); return 1; } void setup_apic_nmi_watchdog (void) { + nmi_hz = 1; + switch (boot_cpu_data.x86_vendor) { case X86_VENDOR_AMD: if (boot_cpu_data.x86 != 6 && boot_cpu_data.x86 != 15) @@ -408,6 +421,9 @@ void touch_nmi_watchdog (void) for (i = 0; i < NR_CPUS; i++) alert_counter[i] = 0; } +#ifdef CONFIG_KGDB +int tune_watchdog = 5*HZ; +#endif void nmi_watchdog_tick (struct pt_regs * regs) { @@ -421,12 +437,24 @@ void nmi_watchdog_tick (struct pt_regs * sum = irq_stat[cpu].apic_timer_irqs; +#ifdef CONFIG_KGDB + if (! in_kgdb(regs) && last_irq_sums[cpu] == sum ) { + +#else if (last_irq_sums[cpu] == sum) { +#endif /* * Ayiee, looks like this CPU is stuck ... * wait a few IRQs (5 seconds) before doing the oops ... */ alert_counter[cpu]++; +#ifdef CONFIG_KGDB + if (alert_counter[cpu] == tune_watchdog) { + kgdb_handle_exception(2, SIGPWR, 0, regs); + last_irq_sums[cpu] = sum; + alert_counter[cpu] = 0; + } +#endif if (alert_counter[cpu] == 5*nmi_hz) { spin_lock(&nmi_print_lock); /* @@ -455,7 +483,7 @@ void nmi_watchdog_tick (struct pt_regs * * - LVTPC is masked on interrupt and must be * unmasked by the LVTPC handler. */ - wrmsr(MSR_P4_IQ_CCCR0, P4_NMI_IQ_CCCR0, 0); + wrmsr(MSR_P4_IQ_CCCR0, nmi_p4_cccr_val, 0); apic_write(APIC_LVTPC, APIC_DM_NMI); } wrmsr(nmi_perfctr_msr, -(cpu_khz/nmi_hz*1000), -1); --- linux-2.6.4-rc2/arch/i386/kernel/pci-dma.c 2003-06-14 12:18:02.000000000 -0700 +++ 25/arch/i386/kernel/pci-dma.c 2004-03-07 20:47:00.000000000 -0800 @@ -20,8 +20,9 @@ void *dma_alloc_coherent(struct device * /* ignore region specifiers */ gfp &= ~(__GFP_DMA | __GFP_HIGHMEM); - if (dev == NULL || (*dev->dma_mask < 0xffffffff)) + if (dev == NULL || (dev->coherent_dma_mask < 0xffffffff)) gfp |= GFP_DMA; + ret = (void *)__get_free_pages(gfp, get_order(size)); if (ret != NULL) { --- linux-2.6.4-rc2/arch/i386/kernel/process.c 2004-03-03 23:12:42.000000000 -0800 +++ 25/arch/i386/kernel/process.c 2004-03-07 20:48:18.000000000 -0800 @@ -45,6 +45,7 @@ #include #include #include +#include #ifdef CONFIG_MATH_EMULATION #include #endif @@ -302,6 +303,9 @@ void flush_thread(void) struct task_struct *tsk = current; memset(tsk->thread.debugreg, 0, sizeof(unsigned long)*8); +#ifdef CONFIG_X86_HIGH_ENTRY + clear_thread_flag(TIF_DB7); +#endif memset(tsk->thread.tls_array, 0, sizeof(tsk->thread.tls_array)); /* * Forget coprocessor state.. @@ -315,9 +319,8 @@ void release_thread(struct task_struct * if (dead_task->mm) { // temporary debugging check if (dead_task->mm->context.size) { - printk("WARNING: dead process %8s still has LDT? <%p/%d>\n", + printk("WARNING: dead process %8s still has LDT? <%d>\n", dead_task->comm, - dead_task->mm->context.ldt, dead_task->mm->context.size); BUG(); } @@ -352,7 +355,17 @@ int copy_thread(int nr, unsigned long cl p->thread.esp = (unsigned long) childregs; p->thread.esp0 = (unsigned long) (childregs+1); + /* + * get the two stack pages, for the virtual stack. + * + * IMPORTANT: this code relies on the fact that the task + * structure is an 8K aligned piece of physical memory. + */ + p->thread.stack_page0 = virt_to_page((unsigned long)p->thread_info); + p->thread.stack_page1 = virt_to_page((unsigned long)p->thread_info + PAGE_SIZE); + p->thread.eip = (unsigned long) ret_from_fork; + p->thread_info->real_stack = p->thread_info; savesegment(fs,p->thread.fs); savesegment(gs,p->thread.gs); @@ -493,7 +506,7 @@ int dump_task_regs(struct task_struct *t * the task-switch, and shows up in ret_from_fork in entry.S, * for example. */ -struct task_struct * __switch_to(struct task_struct *prev_p, struct task_struct *next_p) +struct task_struct fastcall * __switch_to(struct task_struct *prev_p, struct task_struct *next_p) { struct thread_struct *prev = &prev_p->thread, *next = &next_p->thread; @@ -504,10 +517,41 @@ struct task_struct * __switch_to(struct __unlazy_fpu(prev_p); +#ifdef CONFIG_X86_HIGH_ENTRY + /* + * Set the ptes of the virtual stack. (NOTE: a one-page TLB flush is + * needed because otherwise NMIs could interrupt the + * user-return code with a virtual stack and stale TLBs.) + */ + __kunmap_atomic_type(KM_VSTACK0); + __kunmap_atomic_type(KM_VSTACK1); + __kmap_atomic(next->stack_page0, KM_VSTACK0); + __kmap_atomic(next->stack_page1, KM_VSTACK1); + + /* + * NOTE: here we rely on the task being the stack as well + */ + next_p->thread_info->virtual_stack = + (void *)__kmap_atomic_vaddr(KM_VSTACK0); + +#if defined(CONFIG_PREEMPT) && defined(CONFIG_SMP) + /* + * If next was preempted on entry from userspace to kernel, + * and now it's on a different cpu, we need to adjust %esp. + * This assumes that entry.S does not copy %esp while on the + * virtual stack (with interrupts enabled): which is so, + * except within __SWITCH_KERNELSPACE itself. + */ + if (unlikely(next->esp >= TASK_SIZE)) { + next->esp &= THREAD_SIZE - 1; + next->esp |= (unsigned long) next_p->thread_info->virtual_stack; + } +#endif +#endif /* * Reload esp0, LDT and the page table pointer: */ - load_esp0(tss, next); + load_virtual_esp0(tss, next_p); /* * Load the per-thread Thread-Local Storage descriptor. --- linux-2.6.4-rc2/arch/i386/kernel/reboot.c 2004-01-09 00:04:30.000000000 -0800 +++ 25/arch/i386/kernel/reboot.c 2004-03-07 20:48:18.000000000 -0800 @@ -155,12 +155,11 @@ void machine_real_restart(unsigned char CMOS_WRITE(0x00, 0x8f); spin_unlock_irqrestore(&rtc_lock, flags); - /* Remap the kernel at virtual address zero, as well as offset zero - from the kernel segment. This assumes the kernel segment starts at - virtual address PAGE_OFFSET. */ - - memcpy (swapper_pg_dir, swapper_pg_dir + USER_PGD_PTRS, - sizeof (swapper_pg_dir [0]) * KERNEL_PGD_PTRS); + /* + * Remap the first 16 MB of RAM (which includes the kernel image) + * at virtual address zero: + */ + setup_identity_mappings(swapper_pg_dir, 0, 16*1024*1024); /* * Use `swapper_pg_dir' as our page directory. --- linux-2.6.4-rc2/arch/i386/kernel/setup.c 2004-03-03 23:12:42.000000000 -0800 +++ 25/arch/i386/kernel/setup.c 2004-03-07 20:48:08.000000000 -0800 @@ -50,6 +50,11 @@ #include "setup_arch_pre.h" #include "mach_resources.h" +/* This value is set up by the early boot code to point to the value + immediately after the boot time page tables. It contains a *physical* + address, and must not be in the .bss segment! */ +unsigned long init_pg_tables_end __initdata = ~0UL; + int disable_pse __initdata = 0; static inline char * __init machine_specific_memory_setup(void); @@ -115,7 +120,6 @@ extern void early_cpu_init(void); extern void dmi_scan_machine(void); extern void generic_apic_probe(char *); extern int root_mountflags; -extern char _end[]; unsigned long saved_videomode; @@ -569,6 +573,11 @@ static void __init parse_cmdline_early ( acpi_disabled = 0; } + /* acpi=strict disables out-of-spec workarounds */ + else if (!memcmp(from, "acpi=strict", 11)) { + acpi_strict = 1; + } + /* Limit ACPI just to boot-time to enable HT */ else if (!memcmp(from, "acpi=ht", 7)) { acpi_ht = 1; @@ -785,7 +794,7 @@ static unsigned long __init setup_memory * partially used pages are not usable - thus * we are rounding upwards: */ - start_pfn = PFN_UP(__pa(_end)); + start_pfn = PFN_UP(init_pg_tables_end); find_max_pfn(); @@ -823,6 +832,13 @@ static unsigned long __init setup_memory */ reserve_bootmem(0, PAGE_SIZE); + /* could be an AMD 768MPX chipset. Reserve a page before VGA to prevent + PCI prefetch into it (errata #56). Usually the page is reserved anyways, + unless you have no PS/2 mouse plugged in. */ + if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD && + boot_cpu_data.x86 == 6) + reserve_bootmem(0xa0000 - 4096, 4096); + #ifdef CONFIG_SMP /* * But first pinch a few for the stack/trampoline stuff @@ -1097,7 +1113,7 @@ void __init setup_arch(char **cmdline_p) init_mm.start_code = (unsigned long) _text; init_mm.end_code = (unsigned long) _etext; init_mm.end_data = (unsigned long) _edata; - init_mm.brk = (unsigned long) _end; + init_mm.brk = init_pg_tables_end + PAGE_OFFSET; code_resource.start = virt_to_phys(_text); code_resource.end = virt_to_phys(_etext)-1; --- linux-2.6.4-rc2/arch/i386/kernel/signal.c 2003-11-23 19:03:00.000000000 -0800 +++ 25/arch/i386/kernel/signal.c 2004-03-07 20:48:18.000000000 -0800 @@ -128,28 +128,29 @@ sys_sigaltstack(const stack_t __user *us */ static int -restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, int *peax) +restore_sigcontext(struct pt_regs *regs, + struct sigcontext __user *__sc, int *peax) { - unsigned int err = 0; + struct sigcontext scratch; /* 88 bytes of scratch area */ /* Always make any pending restarted system calls return -EINTR */ current_thread_info()->restart_block.fn = do_no_restart_syscall; -#define COPY(x) err |= __get_user(regs->x, &sc->x) + if (copy_from_user(&scratch, __sc, sizeof(scratch))) + return -EFAULT; + +#define COPY(x) regs->x = scratch.x #define COPY_SEG(seg) \ - { unsigned short tmp; \ - err |= __get_user(tmp, &sc->seg); \ + { unsigned short tmp = scratch.seg; \ regs->x##seg = tmp; } #define COPY_SEG_STRICT(seg) \ - { unsigned short tmp; \ - err |= __get_user(tmp, &sc->seg); \ + { unsigned short tmp = scratch.seg; \ regs->x##seg = tmp|3; } #define GET_SEG(seg) \ - { unsigned short tmp; \ - err |= __get_user(tmp, &sc->seg); \ + { unsigned short tmp = scratch.seg; \ loadsegment(seg,tmp); } GET_SEG(gs); @@ -168,27 +169,23 @@ restore_sigcontext(struct pt_regs *regs, COPY_SEG_STRICT(ss); { - unsigned int tmpflags; - err |= __get_user(tmpflags, &sc->eflags); + unsigned int tmpflags = scratch.eflags; regs->eflags = (regs->eflags & ~0x40DD5) | (tmpflags & 0x40DD5); regs->orig_eax = -1; /* disable syscall checks */ } { - struct _fpstate __user * buf; - err |= __get_user(buf, &sc->fpstate); + struct _fpstate * buf = scratch.fpstate; if (buf) { if (verify_area(VERIFY_READ, buf, sizeof(*buf))) - goto badframe; - err |= restore_i387(buf); + return -EFAULT; + if (restore_i387(buf)) + return -EFAULT; } } - err |= __get_user(*peax, &sc->eax); - return err; - -badframe: - return 1; + *peax = scratch.eax; + return 0; } asmlinkage int sys_sigreturn(unsigned long __unused) @@ -266,46 +263,47 @@ badframe: */ static int -setup_sigcontext(struct sigcontext __user *sc, struct _fpstate __user *fpstate, +setup_sigcontext(struct sigcontext __user *__sc, struct _fpstate __user *fpstate, struct pt_regs *regs, unsigned long mask) { - int tmp, err = 0; + struct sigcontext sc; /* 88 bytes of scratch area */ + int tmp; tmp = 0; __asm__("movl %%gs,%0" : "=r"(tmp): "0"(tmp)); - err |= __put_user(tmp, (unsigned int *)&sc->gs); + *(unsigned int *)&sc.gs = tmp; __asm__("movl %%fs,%0" : "=r"(tmp): "0"(tmp)); - err |= __put_user(tmp, (unsigned int *)&sc->fs); - - err |= __put_user(regs->xes, (unsigned int *)&sc->es); - err |= __put_user(regs->xds, (unsigned int *)&sc->ds); - err |= __put_user(regs->edi, &sc->edi); - err |= __put_user(regs->esi, &sc->esi); - err |= __put_user(regs->ebp, &sc->ebp); - err |= __put_user(regs->esp, &sc->esp); - err |= __put_user(regs->ebx, &sc->ebx); - err |= __put_user(regs->edx, &sc->edx); - err |= __put_user(regs->ecx, &sc->ecx); - err |= __put_user(regs->eax, &sc->eax); - err |= __put_user(current->thread.trap_no, &sc->trapno); - err |= __put_user(current->thread.error_code, &sc->err); - err |= __put_user(regs->eip, &sc->eip); - err |= __put_user(regs->xcs, (unsigned int *)&sc->cs); - err |= __put_user(regs->eflags, &sc->eflags); - err |= __put_user(regs->esp, &sc->esp_at_signal); - err |= __put_user(regs->xss, (unsigned int *)&sc->ss); + *(unsigned int *)&sc.fs = tmp; + *(unsigned int *)&sc.es = regs->xes; + *(unsigned int *)&sc.ds = regs->xds; + sc.edi = regs->edi; + sc.esi = regs->esi; + sc.ebp = regs->ebp; + sc.esp = regs->esp; + sc.ebx = regs->ebx; + sc.edx = regs->edx; + sc.ecx = regs->ecx; + sc.eax = regs->eax; + sc.trapno = current->thread.trap_no; + sc.err = current->thread.error_code; + sc.eip = regs->eip; + *(unsigned int *)&sc.cs = regs->xcs; + sc.eflags = regs->eflags; + sc.esp_at_signal = regs->esp; + *(unsigned int *)&sc.ss = regs->xss; tmp = save_i387(fpstate); if (tmp < 0) - err = 1; - else - err |= __put_user(tmp ? fpstate : NULL, &sc->fpstate); + return 1; + sc.fpstate = tmp ? fpstate : NULL; /* non-iBCS2 extensions.. */ - err |= __put_user(mask, &sc->oldmask); - err |= __put_user(current->thread.cr2, &sc->cr2); + sc.oldmask = mask; + sc.cr2 = current->thread.cr2; - return err; + if (copy_to_user(__sc, &sc, sizeof(sc))) + return 1; + return 0; } /* @@ -443,7 +441,7 @@ static void setup_rt_frame(int sig, stru /* Create the ucontext. */ err |= __put_user(0, &frame->uc.uc_flags); err |= __put_user(0, &frame->uc.uc_link); - err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp); + err |= __put_user(current->sas_ss_sp, (unsigned long *)&frame->uc.uc_stack.ss_sp); err |= __put_user(sas_ss_flags(regs->esp), &frame->uc.uc_stack.ss_flags); err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); @@ -551,7 +549,7 @@ handle_signal(unsigned long sig, siginfo * want to handle. Thus you cannot kill init even with a SIGKILL even by * mistake. */ -int do_signal(struct pt_regs *regs, sigset_t *oldset) +int fastcall do_signal(struct pt_regs *regs, sigset_t *oldset) { siginfo_t info; int signr; --- linux-2.6.4-rc2/arch/i386/kernel/smpboot.c 2004-03-03 23:12:42.000000000 -0800 +++ 25/arch/i386/kernel/smpboot.c 2004-03-07 20:48:20.000000000 -0800 @@ -39,6 +39,7 @@ #include #include +#include #include #include #include @@ -815,6 +816,8 @@ static int __init do_boot_cpu(int apicid /* Stack for startup_32 can be just as for start_secondary onwards */ stack_start.esp = (void *) idle->thread.esp; + irq_ctx_init(cpu); + /* * This grunge runs the startup process for * the targeted processor. @@ -934,7 +937,7 @@ static int boot_cpu_logical_apicid; /* Where the IO area was mapped on multiquad, always 0 otherwise */ void *xquad_portio; -int cpu_sibling_map[NR_CPUS] __cacheline_aligned; +cpumask_t cpu_sibling_map[NR_CPUS] __cacheline_aligned; static void __init smp_boot_cpus(unsigned int max_cpus) { @@ -953,6 +956,8 @@ static void __init smp_boot_cpus(unsigne current_thread_info()->cpu = 0; smp_tune_scheduling(); + cpus_clear(cpu_sibling_map[0]); + cpu_set(0, cpu_sibling_map[0]); /* * If we couldn't find an SMP configuration at boot time, @@ -1079,32 +1084,34 @@ static void __init smp_boot_cpus(unsigne Dprintk("Boot done.\n"); /* - * If Hyper-Threading is avaialble, construct cpu_sibling_map[], so - * that we can tell the sibling CPU efficiently. + * construct cpu_sibling_map[], so that we can tell sibling CPUs + * efficiently. */ - if (cpu_has_ht && smp_num_siblings > 1) { - for (cpu = 0; cpu < NR_CPUS; cpu++) - cpu_sibling_map[cpu] = NO_PROC_ID; - - for (cpu = 0; cpu < NR_CPUS; cpu++) { - int i; - if (!cpu_isset(cpu, cpu_callout_map)) - continue; + for (cpu = 0; cpu < NR_CPUS; cpu++) + cpus_clear(cpu_sibling_map[cpu]); + for (cpu = 0; cpu < NR_CPUS; cpu++) { + int siblings = 0; + int i; + if (!cpu_isset(cpu, cpu_callout_map)) + continue; + + if (smp_num_siblings > 1) { for (i = 0; i < NR_CPUS; i++) { - if (i == cpu || !cpu_isset(i, cpu_callout_map)) + if (!cpu_isset(i, cpu_callout_map)) continue; if (phys_proc_id[cpu] == phys_proc_id[i]) { - cpu_sibling_map[cpu] = i; - printk("cpu_sibling_map[%d] = %d\n", cpu, cpu_sibling_map[cpu]); - break; + siblings++; + cpu_set(i, cpu_sibling_map[cpu]); } } - if (cpu_sibling_map[cpu] == NO_PROC_ID) { - smp_num_siblings = 1; - printk(KERN_WARNING "WARNING: No sibling found for CPU %d.\n", cpu); - } + } else { + siblings++; + cpu_set(cpu, cpu_sibling_map[cpu]); } + + if (siblings != smp_num_siblings) + printk(KERN_WARNING "WARNING: %d siblings found for CPU%d, should be %d\n", siblings, cpu, smp_num_siblings); } smpboot_setup_io_apic(); @@ -1118,6 +1125,224 @@ static void __init smp_boot_cpus(unsigne synchronize_tsc_bp(); } +#ifdef CONFIG_SCHED_SMT +#ifdef CONFIG_NUMA +static struct sched_group sched_group_cpus[NR_CPUS]; +static struct sched_group sched_group_phys[NR_CPUS]; +static struct sched_group sched_group_nodes[MAX_NUMNODES]; +static DEFINE_PER_CPU(struct sched_domain, phys_domains); +static DEFINE_PER_CPU(struct sched_domain, node_domains); +__init void arch_init_sched_domains(void) +{ + int i; + struct sched_group *first_cpu = NULL, *last_cpu = NULL; + + /* Set up domains */ + for_each_cpu(i) { + struct sched_domain *cpu_domain = cpu_sched_domain(i); + struct sched_domain *phys_domain = &per_cpu(phys_domains, i); + struct sched_domain *node_domain = &per_cpu(node_domains, i); + int node = cpu_to_node(i); + cpumask_t nodemask = node_to_cpumask(node); + + *cpu_domain = SD_SIBLING_INIT; + cpu_domain->span = cpu_sibling_map[i]; + + *phys_domain = SD_CPU_INIT; + phys_domain->span = nodemask; + + *node_domain = SD_NODE_INIT; + node_domain->span = cpu_possible_map; + } + + /* Set up CPU (sibling) groups */ + for_each_cpu(i) { + struct sched_domain *cpu_domain = cpu_sched_domain(i); + int j; + first_cpu = last_cpu = NULL; + + if (i != first_cpu(cpu_domain->span)) { + cpu_sched_domain(i)->flags |= SD_FLAG_SHARE_CPUPOWER; + cpu_sched_domain(first_cpu(cpu_domain->span))->flags |= + SD_FLAG_SHARE_CPUPOWER; + continue; + } + + for_each_cpu_mask(j, cpu_domain->span) { + struct sched_group *cpu = &sched_group_cpus[j]; + + cpu->cpumask = CPU_MASK_NONE; + cpu_set(j, cpu->cpumask); + cpu->cpu_power = SCHED_LOAD_SCALE; + + if (!first_cpu) + first_cpu = cpu; + if (last_cpu) + last_cpu->next = cpu; + last_cpu = cpu; + } + last_cpu->next = first_cpu; + } + + for (i = 0; i < MAX_NUMNODES; i++) { + int j; + cpumask_t nodemask; + struct sched_group *node = &sched_group_nodes[i]; + cpus_and(nodemask, node_to_cpumask(i), cpu_possible_map); + + if (cpus_empty(nodemask)) + continue; + + first_cpu = last_cpu = NULL; + /* Set up physical groups */ + for_each_cpu_mask(j, nodemask) { + struct sched_domain *cpu_domain = cpu_sched_domain(j); + struct sched_group *cpu = &sched_group_phys[j]; + + if (j != first_cpu(cpu_domain->span)) + continue; + + cpu->cpumask = cpu_domain->span; + /* + * Make each extra sibling increase power by 10% of + * the basic CPU. This is very arbitrary. + */ + cpu->cpu_power = SCHED_LOAD_SCALE + SCHED_LOAD_SCALE*(cpus_weight(cpu->cpumask)-1) / 10; + node->cpu_power += cpu->cpu_power; + + if (!first_cpu) + first_cpu = cpu; + if (last_cpu) + last_cpu->next = cpu; + last_cpu = cpu; + } + last_cpu->next = first_cpu; + } + + /* Set up nodes */ + first_cpu = last_cpu = NULL; + for (i = 0; i < MAX_NUMNODES; i++) { + struct sched_group *cpu = &sched_group_nodes[i]; + cpumask_t nodemask; + cpus_and(nodemask, node_to_cpumask(i), cpu_possible_map); + + if (cpus_empty(nodemask)) + continue; + + cpu->cpumask = nodemask; + /* ->cpu_power already setup */ + + if (!first_cpu) + first_cpu = cpu; + if (last_cpu) + last_cpu->next = cpu; + last_cpu = cpu; + } + last_cpu->next = first_cpu; + + mb(); + for_each_cpu(i) { + int node = cpu_to_node(i); + struct sched_domain *cpu_domain = cpu_sched_domain(i); + struct sched_domain *phys_domain = &per_cpu(phys_domains, i); + struct sched_domain *node_domain = &per_cpu(node_domains, i); + struct sched_group *cpu_group = &sched_group_cpus[i]; + struct sched_group *phys_group = &sched_group_phys[first_cpu(cpu_domain->span)]; + struct sched_group *node_group = &sched_group_nodes[node]; + + cpu_domain->parent = phys_domain; + phys_domain->parent = node_domain; + + node_domain->groups = node_group; + phys_domain->groups = phys_group; + cpu_domain->groups = cpu_group; + } +} +#else /* CONFIG_NUMA */ +static struct sched_group sched_group_cpus[NR_CPUS]; +static struct sched_group sched_group_phys[NR_CPUS]; +static DEFINE_PER_CPU(struct sched_domain, phys_domains); +__init void arch_init_sched_domains(void) +{ + int i; + struct sched_group *first_cpu = NULL, *last_cpu = NULL; + + /* Set up domains */ + for_each_cpu(i) { + struct sched_domain *cpu_domain = cpu_sched_domain(i); + struct sched_domain *phys_domain = &per_cpu(phys_domains, i); + + *cpu_domain = SD_SIBLING_INIT; + cpu_domain->span = cpu_sibling_map[i]; + + *phys_domain = SD_CPU_INIT; + phys_domain->span = cpu_possible_map; + } + + /* Set up CPU (sibling) groups */ + for_each_cpu(i) { + struct sched_domain *cpu_domain = cpu_sched_domain(i); + int j; + first_cpu = last_cpu = NULL; + + if (i != first_cpu(cpu_domain->span)) { + cpu_sched_domain(i)->flags |= SD_FLAG_SHARE_CPUPOWER; + cpu_sched_domain(first_cpu(cpu_domain->span))->flags |= + SD_FLAG_SHARE_CPUPOWER; + continue; + } + + for_each_cpu_mask(j, cpu_domain->span) { + struct sched_group *cpu = &sched_group_cpus[j]; + + cpus_clear(cpu->cpumask); + cpu_set(j, cpu->cpumask); + cpu->cpu_power = SCHED_LOAD_SCALE; + + if (!first_cpu) + first_cpu = cpu; + if (last_cpu) + last_cpu->next = cpu; + last_cpu = cpu; + } + last_cpu->next = first_cpu; + } + + first_cpu = last_cpu = NULL; + /* Set up physical groups */ + for_each_cpu(i) { + struct sched_domain *cpu_domain = cpu_sched_domain(i); + struct sched_group *cpu = &sched_group_phys[i]; + + if (i != first_cpu(cpu_domain->span)) + continue; + + cpu->cpumask = cpu_domain->span; + /* See SMT+NUMA setup for comment */ + cpu->cpu_power = SCHED_LOAD_SCALE + SCHED_LOAD_SCALE*(cpus_weight(cpu->cpumask)-1) / 10; + + if (!first_cpu) + first_cpu = cpu; + if (last_cpu) + last_cpu->next = cpu; + last_cpu = cpu; + } + last_cpu->next = first_cpu; + + mb(); + for_each_cpu(i) { + struct sched_domain *cpu_domain = cpu_sched_domain(i); + struct sched_domain *phys_domain = &per_cpu(phys_domains, i); + struct sched_group *cpu_group = &sched_group_cpus[i]; + struct sched_group *phys_group = &sched_group_phys[first_cpu(cpu_domain->span)]; + cpu_domain->parent = phys_domain; + phys_domain->groups = phys_group; + cpu_domain->groups = cpu_group; + } +} +#endif /* CONFIG_NUMA */ +#endif /* CONFIG_SCHED_SMT */ + /* These are wrappers to interface to the new boot process. Someone who understands all this stuff should rewrite it properly. --RR 15/Jul/02 */ void __init smp_prepare_cpus(unsigned int max_cpus) --- linux-2.6.4-rc2/arch/i386/kernel/smp.c 2003-11-09 16:45:04.000000000 -0800 +++ 25/arch/i386/kernel/smp.c 2004-03-07 20:48:18.000000000 -0800 @@ -150,7 +150,7 @@ inline void __send_IPI_shortcut(unsigned apic_write_around(APIC_ICR, cfg); } -void send_IPI_self(int vector) +void fastcall send_IPI_self(int vector) { __send_IPI_shortcut(APIC_DEST_SELF, vector); } @@ -327,10 +327,12 @@ asmlinkage void smp_invalidate_interrupt if (flush_mm == cpu_tlbstate[cpu].active_mm) { if (cpu_tlbstate[cpu].state == TLBSTATE_OK) { +#ifndef CONFIG_X86_SWITCH_PAGETABLES if (flush_va == FLUSH_ALL) local_flush_tlb(); else __flush_tlb_one(flush_va); +#endif } else leave_mm(cpu); } @@ -396,21 +398,6 @@ static void flush_tlb_others(cpumask_t c spin_unlock(&tlbstate_lock); } -void flush_tlb_current_task(void) -{ - struct mm_struct *mm = current->mm; - cpumask_t cpu_mask; - - preempt_disable(); - cpu_mask = mm->cpu_vm_mask; - cpu_clear(smp_processor_id(), cpu_mask); - - local_flush_tlb(); - if (!cpus_empty(cpu_mask)) - flush_tlb_others(cpu_mask, mm, FLUSH_ALL); - preempt_enable(); -} - void flush_tlb_mm (struct mm_struct * mm) { cpumask_t cpu_mask; @@ -442,7 +429,10 @@ void flush_tlb_page(struct vm_area_struc if (current->active_mm == mm) { if(current->mm) - __flush_tlb_one(va); +#ifndef CONFIG_X86_SWITCH_PAGETABLES + __flush_tlb_one(va) +#endif + ; else leave_mm(smp_processor_id()); } @@ -466,7 +456,17 @@ void flush_tlb_all(void) { on_each_cpu(do_flush_tlb_all, 0, 1, 1); } - +#ifdef CONFIG_KGDB +/* + * By using the NMI code instead of a vector we just sneak thru the + * word generator coming out with just what we want. AND it does + * not matter if clustered_apic_mode is set or not. + */ +void smp_send_nmi_allbutself(void) +{ + send_IPI_allbutself(APIC_DM_NMI); +} +#endif /* * this function sends a 'reschedule' IPI to another CPU. * it goes straight through and wastes no time serializing --- linux-2.6.4-rc2/arch/i386/kernel/sysenter.c 2003-11-09 16:45:04.000000000 -0800 +++ 25/arch/i386/kernel/sysenter.c 2004-03-07 20:48:18.000000000 -0800 @@ -18,13 +18,18 @@ #include #include #include +#include extern asmlinkage void sysenter_entry(void); void enable_sep_cpu(void *info) { int cpu = get_cpu(); +#ifdef CONFIG_X86_HIGH_ENTRY + struct tss_struct *tss = (struct tss_struct *) __fix_to_virt(FIX_TSS_0) + cpu; +#else struct tss_struct *tss = init_tss + cpu; +#endif tss->ss1 = __KERNEL_CS; tss->esp1 = sizeof(struct tss_struct) + (unsigned long) tss; --- linux-2.6.4-rc2/arch/i386/kernel/trampoline.S 2003-06-14 12:18:07.000000000 -0700 +++ 25/arch/i386/kernel/trampoline.S 2004-03-07 20:47:46.000000000 -0800 @@ -23,9 +23,13 @@ * and IP is zero. Thus, data addresses need to be absolute * (no relocation) and are taken with regard to r_base. * - * If you work on this file, check the object module with objdump - * --full-contents --reloc to make sure there are no relocation - * entries except for the gdt one.. + * If you work on this file, check the object module with + * objdump --reloc to make sure there are no relocation + * entries except for: + * + * TYPE VALUE + * R_386_32 startup_32_smp + * R_386_32 boot_gdt_table */ #include @@ -42,7 +46,6 @@ r_base = . mov %cs, %ax # Code and data in the same place mov %ax, %ds - mov $1, %bx # Flag an SMP trampoline cli # We should be safe anyway movl $0xA5A5A5A5, trampoline_data - r_base @@ -54,22 +57,18 @@ r_base = . xor %ax, %ax inc %ax # protected mode (PE) bit lmsw %ax # into protected mode - jmp flush_instr -flush_instr: - ljmpl $__BOOT_CS, $0x00100000 - # jump to startup_32 in arch/i386/kernel/head.S - -boot_idt: - .word 0 # idt limit = 0 - .word 0, 0 # idt base = 0L + # flush prefetch and jump to startup_32_smp in arch/i386/kernel/head.S + ljmpl $__BOOT_CS, $(startup_32_smp-__PAGE_OFFSET) -# -# NOTE: here we actually use CPU#0's GDT - but that is OK, we reload -# the proper GDT shortly after booting up the secondary CPUs. -# -ENTRY(boot_gdt) + # These need to be in the same 64K segment as the above; + # hence we don't use the boot_gdt_descr defined in head.S +boot_gdt: .word __BOOT_DS + 7 # gdt limit - .long boot_gdt_table-__PAGE_OFFSET # gdt base = gdt (first SMP CPU) + .long boot_gdt_table-__PAGE_OFFSET # gdt base + +boot_idt: + .word 0 # idt limit = 0 + .long 0 # idt base = 0L .globl trampoline_end trampoline_end: --- linux-2.6.4-rc2/arch/i386/kernel/traps.c 2004-03-03 23:12:42.000000000 -0800 +++ 25/arch/i386/kernel/traps.c 2004-03-07 20:48:21.000000000 -0800 @@ -54,12 +54,8 @@ #include "mach_traps.h" -asmlinkage int system_call(void); -asmlinkage void lcall7(void); -asmlinkage void lcall27(void); - -struct desc_struct default_ldt[] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, - { 0, 0 }, { 0, 0 } }; +struct desc_struct default_ldt[] __attribute__((__section__(".data.default_ldt"))) = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }; +struct page *default_ldt_page; /* Do we ignore FPU interrupts ? */ char ignore_fpu_irq = 0; @@ -91,6 +87,41 @@ asmlinkage void alignment_check(void); asmlinkage void spurious_interrupt_bug(void); asmlinkage void machine_check(void); +#ifdef CONFIG_KGDB +extern void sysenter_entry(void); +#include +#include +void set_intr_gate(unsigned int n, void *addr); +static void set_intr_usr_gate(unsigned int n, void *addr); +/* + * Should be able to call this breakpoint() very early in + * bring up. Just hard code the call where needed. + * The breakpoint() code is here because set_?_gate() functions + * are local (static) to trap.c. They need be done only once, + * but it does not hurt to do them over. + */ +void breakpoint(void) +{ + init_entry_mappings(); + set_intr_usr_gate(3,&int3); /* disable ints on trap */ + set_intr_gate(1,&debug); + set_intr_gate(14,&page_fault); + + BREAKPOINT; +} +#define CHK_REMOTE_DEBUG(trapnr,signr,error_code,regs,after) \ + { \ + if (!user_mode(regs) ) \ + { \ + kgdb_handle_exception(trapnr, signr, error_code, regs); \ + after; \ + } else if ((trapnr == 3) && (regs->eflags &0x200)) local_irq_enable(); \ + } +#else +#define CHK_REMOTE_DEBUG(trapnr,signr,error_code,regs,after) +#endif + + static int kstack_depth_to_print = 24; void show_trace(struct task_struct *task, unsigned long * stack) @@ -104,12 +135,20 @@ void show_trace(struct task_struct *task #ifdef CONFIG_KALLSYMS printk("\n"); #endif - while (!kstack_end(stack)) { - addr = *stack++; - if (kernel_text_address(addr)) { - printk(" [<%08lx>] ", addr); - print_symbol("%s\n", addr); + while (1) { + struct thread_info *context; + context = (struct thread_info*) ((unsigned long)stack & (~(THREAD_SIZE - 1))); + while (!kstack_end(stack)) { + addr = *stack++; + if (kernel_text_address(addr)) { + printk(" [<%08lx>] ", addr); + print_symbol("%s\n", addr); + } } + stack = (unsigned long*)context->previous_esp; + if (!stack) + break; + printk(" =======================\n"); } printk("\n"); } @@ -175,8 +214,9 @@ void show_registers(struct pt_regs *regs ss = regs->xss & 0xffff; } print_modules(); - printk("CPU: %d\nEIP: %04x:[<%08lx>] %s\nEFLAGS: %08lx\n", - smp_processor_id(), 0xffff & regs->xcs, regs->eip, print_tainted(), regs->eflags); + printk("CPU: %d\nEIP: %04x:[<%08lx>] %s VLI\nEFLAGS: %08lx\n", + smp_processor_id(), 0xffff & regs->xcs, + regs->eip, print_tainted(), regs->eflags); print_symbol("EIP is at %s\n", regs->eip); printk("eax: %08lx ebx: %08lx ecx: %08lx edx: %08lx\n", @@ -192,23 +232,27 @@ void show_registers(struct pt_regs *regs * time of the fault.. */ if (in_kernel) { + u8 *eip; printk("\nStack: "); show_stack(NULL, (unsigned long*)esp); printk("Code: "); - if(regs->eip < PAGE_OFFSET) - goto bad; - for(i=0;i<20;i++) - { - unsigned char c; - if(__get_user(c, &((unsigned char*)regs->eip)[i])) { -bad: + eip = (u8 *)regs->eip - 43; + for (i = 0; i < 64; i++, eip++) { + unsigned char c = 0xff; + + if ((user_mode(regs) && get_user(c, eip)) || + (!user_mode(regs) && __direct_get_user(c, eip))) { + printk(" Bad EIP value."); break; } - printk("%02x ", c); + if (eip == (u8 *)regs->eip) + printk("<%02x> ", c); + else + printk("%02x ", c); } } printk("\n"); @@ -227,16 +271,14 @@ static void handle_BUG(struct pt_regs *r eip = regs->eip; - if (eip < PAGE_OFFSET) - goto no_bug; - if (__get_user(ud2, (unsigned short *)eip)) + if (__direct_get_user(ud2, (unsigned short *)eip)) goto no_bug; if (ud2 != 0x0b0f) goto no_bug; - if (__get_user(line, (unsigned short *)(eip + 2))) + if (__direct_get_user(line, (unsigned short *)(eip + 2))) goto bug; - if (__get_user(file, (char **)(eip + 4)) || - (unsigned long)file < PAGE_OFFSET || __get_user(c, file)) + if (__direct_get_user(file, (char **)(eip + 4)) || + __direct_get_user(c, file)) file = ""; printk("------------[ cut here ]------------\n"); @@ -276,6 +318,15 @@ void die(const char * str, struct pt_reg #endif if (nl) printk("\n"); +#ifdef CONFIG_KGDB + /* This is about the only place we want to go to kgdb even if in + * user mode. But we must go in via a trap so within kgdb we will + * always be in kernel mode. + */ + if (user_mode(regs)) + BREAKPOINT; +#endif + CHK_REMOTE_DEBUG(0,SIGTRAP,err,regs,) show_registers(regs); bust_spinlocks(0); spin_unlock_irq(&die_lock); @@ -345,6 +396,7 @@ static inline void do_trap(int trapnr, i #define DO_ERROR(trapnr, signr, str, name) \ asmlinkage void do_##name(struct pt_regs * regs, long error_code) \ { \ + CHK_REMOTE_DEBUG(trapnr,signr,error_code,regs,)\ do_trap(trapnr, signr, str, 0, regs, error_code, NULL); \ } @@ -362,7 +414,9 @@ asmlinkage void do_##name(struct pt_regs #define DO_VM86_ERROR(trapnr, signr, str, name) \ asmlinkage void do_##name(struct pt_regs * regs, long error_code) \ { \ + CHK_REMOTE_DEBUG(trapnr, signr, error_code,regs, return)\ do_trap(trapnr, signr, str, 1, regs, error_code, NULL); \ + return; \ } #define DO_VM86_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr) \ @@ -409,8 +463,10 @@ gp_in_vm86: return; gp_in_kernel: - if (!fixup_exception(regs)) + if (!fixup_exception(regs)){ + CHK_REMOTE_DEBUG(13,SIGSEGV,error_code,regs,) die("general protection fault", regs, error_code); + } } static void mem_parity_error(unsigned char reason, struct pt_regs * regs) @@ -549,10 +605,18 @@ asmlinkage void do_debug(struct pt_regs if (regs->eflags & X86_EFLAGS_IF) local_irq_enable(); - /* Mask out spurious debug traps due to lazy DR7 setting */ + /* + * Mask out spurious debug traps due to lazy DR7 setting or + * due to 4G/4G kernel mode: + */ if (condition & (DR_TRAP0|DR_TRAP1|DR_TRAP2|DR_TRAP3)) { if (!tsk->thread.debugreg[7]) goto clear_dr7; + if (!user_mode(regs)) { + // restore upon return-to-userspace: + set_thread_flag(TIF_DB7); + goto clear_dr7; + } } if (regs->eflags & VM_MASK) @@ -572,8 +636,18 @@ asmlinkage void do_debug(struct pt_regs * allowing programs to debug themselves without the ptrace() * interface. */ +#ifdef CONFIG_KGDB + /* + * I think this is the only "real" case of a TF in the kernel + * that really belongs to user space. Others are + * "Ours all ours!" + */ + if (((regs->xcs & 3) == 0) && ((void *)regs->eip == sysenter_entry)) + goto clear_TF_reenable; +#else if ((regs->xcs & 3) == 0) goto clear_TF_reenable; +#endif if ((tsk->ptrace & (PT_DTRACE|PT_PTRACED)) == PT_DTRACE) goto clear_TF; } @@ -585,6 +659,17 @@ asmlinkage void do_debug(struct pt_regs info.si_errno = 0; info.si_code = TRAP_BRKPT; +#ifdef CONFIG_KGDB + /* + * If this is a kernel mode trap, we need to reset db7 to allow us + * to continue sanely ALSO skip the signal delivery + */ + if ((regs->xcs & 3) == 0) + goto clear_dr7; + + /* if not kernel, allow ints but only if they were on */ + if ( regs->eflags & 0x200) local_irq_enable(); +#endif /* If this is a kernel mode trap, save the user PC on entry to * the kernel, that's what the debugger can make sense of. */ @@ -599,6 +684,7 @@ clear_dr7: __asm__("movl %0,%%db7" : /* no output */ : "r" (0)); + CHK_REMOTE_DEBUG(1,SIGTRAP,error_code,regs,) return; debug_vm86: @@ -794,19 +880,53 @@ asmlinkage void math_emulate(long arg) #endif /* CONFIG_MATH_EMULATION */ -#ifdef CONFIG_X86_F00F_BUG -void __init trap_init_f00f_bug(void) +void __init trap_init_virtual_IDT(void) { - __set_fixmap(FIX_F00F_IDT, __pa(&idt_table), PAGE_KERNEL_RO); - /* - * Update the IDT descriptor and reload the IDT so that - * it uses the read-only mapped virtual address. + * "idt" is magic - it overlaps the idt_descr + * variable so that updating idt will automatically + * update the idt descriptor.. */ - idt_descr.address = fix_to_virt(FIX_F00F_IDT); + __set_fixmap(FIX_IDT, __pa(&idt_table), PAGE_KERNEL_RO); + idt_descr.address = __fix_to_virt(FIX_IDT); + __asm__ __volatile__("lidt %0" : : "m" (idt_descr)); } -#endif + +void __init trap_init_virtual_GDT(void) +{ + int cpu = smp_processor_id(); + struct Xgt_desc_struct *gdt_desc = cpu_gdt_descr + cpu; + struct Xgt_desc_struct tmp_desc = {0, 0}; + struct tss_struct * t; + + __asm__ __volatile__("sgdt %0": "=m" (tmp_desc): :"memory"); + +#ifdef CONFIG_X86_HIGH_ENTRY + if (!cpu) { + __set_fixmap(FIX_GDT_0, __pa(cpu_gdt_table), PAGE_KERNEL); + __set_fixmap(FIX_GDT_1, __pa(cpu_gdt_table) + PAGE_SIZE, PAGE_KERNEL); + __set_fixmap(FIX_TSS_0, __pa(init_tss), PAGE_KERNEL); + __set_fixmap(FIX_TSS_1, __pa(init_tss) + 1*PAGE_SIZE, PAGE_KERNEL); + __set_fixmap(FIX_TSS_2, __pa(init_tss) + 2*PAGE_SIZE, PAGE_KERNEL); + __set_fixmap(FIX_TSS_3, __pa(init_tss) + 3*PAGE_SIZE, PAGE_KERNEL); + } + + gdt_desc->address = __fix_to_virt(FIX_GDT_0) + sizeof(cpu_gdt_table[0]) * cpu; +#else + gdt_desc->address = (unsigned long)cpu_gdt_table[cpu]; +#endif + __asm__ __volatile__("lgdt %0": "=m" (*gdt_desc)); + +#ifdef CONFIG_X86_HIGH_ENTRY + t = (struct tss_struct *) __fix_to_virt(FIX_TSS_0) + cpu; +#else + t = init_tss + cpu; +#endif + set_tss_desc(cpu, t); + cpu_gdt_table[cpu][GDT_ENTRY_TSS].b &= 0xfffffdff; + load_TR_desc(); +} #define _set_gate(gate_addr,type,dpl,addr,seg) \ do { \ @@ -833,20 +953,26 @@ void set_intr_gate(unsigned int n, void _set_gate(idt_table+n,14,0,addr,__KERNEL_CS); } -static void __init set_trap_gate(unsigned int n, void *addr) +void __init set_trap_gate(unsigned int n, void *addr) { _set_gate(idt_table+n,15,0,addr,__KERNEL_CS); } -static void __init set_system_gate(unsigned int n, void *addr) +void __init set_system_gate(unsigned int n, void *addr) { _set_gate(idt_table+n,15,3,addr,__KERNEL_CS); } -static void __init set_call_gate(void *a, void *addr) +void __init set_call_gate(void *a, void *addr) { _set_gate(a,12,3,addr,__KERNEL_CS); } +#ifdef CONFIG_KGDB +void set_intr_usr_gate(unsigned int n, void *addr) +{ + _set_gate(idt_table+n,14,3,addr,__KERNEL_CS); +} +#endif static void __init set_task_gate(unsigned int n, unsigned int gdt_entry) { @@ -865,11 +991,16 @@ void __init trap_init(void) #ifdef CONFIG_X86_LOCAL_APIC init_apic_mappings(); #endif + init_entry_mappings(); set_trap_gate(0,÷_error); set_intr_gate(1,&debug); set_intr_gate(2,&nmi); +#ifndef CONFIG_KGDB set_system_gate(3,&int3); /* int3-5 can be called from all */ +#else + set_intr_usr_gate(3,&int3); /* int3-5 can be called from all */ +#endif set_system_gate(4,&overflow); set_system_gate(5,&bounds); set_trap_gate(6,&invalid_op); --- linux-2.6.4-rc2/arch/i386/kernel/vm86.c 2004-01-09 00:04:30.000000000 -0800 +++ 25/arch/i386/kernel/vm86.c 2004-03-07 20:48:18.000000000 -0800 @@ -95,7 +95,7 @@ #define VM86_REGS_SIZE2 (sizeof(struct kernel_vm86_regs) - VM86_REGS_SIZE1) struct pt_regs * FASTCALL(save_v86_state(struct kernel_vm86_regs * regs)); -struct pt_regs * save_v86_state(struct kernel_vm86_regs * regs) +struct pt_regs * fastcall save_v86_state(struct kernel_vm86_regs * regs) { struct tss_struct *tss; struct pt_regs *ret; @@ -125,7 +125,7 @@ struct pt_regs * save_v86_state(struct k tss = init_tss + get_cpu(); current->thread.esp0 = current->thread.saved_esp0; current->thread.sysenter_cs = __KERNEL_CS; - load_esp0(tss, ¤t->thread); + load_virtual_esp0(tss, current); current->thread.saved_esp0 = 0; put_cpu(); @@ -305,7 +305,7 @@ static void do_sys_vm86(struct kernel_vm tsk->thread.esp0 = (unsigned long) &info->VM86_TSS_ESP0; if (cpu_has_sep) tsk->thread.sysenter_cs = 0; - load_esp0(tss, &tsk->thread); + load_virtual_esp0(tss, tsk); put_cpu(); tsk->thread.screen_bitmap = info->screen_bitmap; --- linux-2.6.4-rc2/arch/i386/kernel/vmlinux.lds.S 2003-11-09 16:45:04.000000000 -0800 +++ 25/arch/i386/kernel/vmlinux.lds.S 2004-03-07 20:48:18.000000000 -0800 @@ -3,6 +3,9 @@ */ #include +#include +#include +#include OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") OUTPUT_ARCH(i386) @@ -10,7 +13,7 @@ ENTRY(startup_32) jiffies = jiffies_64; SECTIONS { - . = 0xC0000000 + 0x100000; + . = __PAGE_OFFSET + 0x100000; /* read-only */ _text = .; /* Text and read-only data */ .text : { @@ -19,6 +22,19 @@ SECTIONS *(.gnu.warning) } = 0x9090 +#ifdef CONFIG_X86_4G + . = ALIGN(PAGE_SIZE_asm); + __entry_tramp_start = .; + . = FIX_ENTRY_TRAMPOLINE_0_addr; + __start___entry_text = .; + .entry.text : AT (__entry_tramp_start) { *(.entry.text) } + __entry_tramp_end = __entry_tramp_start + SIZEOF(.entry.text); + . = __entry_tramp_end; + . = ALIGN(PAGE_SIZE_asm); +#else + .entry.text : { *(.entry.text) } +#endif + _etext = .; /* End of text section */ . = ALIGN(16); /* Exception table */ @@ -34,15 +50,12 @@ SECTIONS CONSTRUCTORS } - . = ALIGN(4096); + . = ALIGN(PAGE_SIZE_asm); __nosave_begin = .; .data_nosave : { *(.data.nosave) } - . = ALIGN(4096); + . = ALIGN(PAGE_SIZE_asm); __nosave_end = .; - . = ALIGN(4096); - .data.page_aligned : { *(.data.idt) } - . = ALIGN(32); .data.cacheline_aligned : { *(.data.cacheline_aligned) } @@ -52,7 +65,7 @@ SECTIONS .data.init_task : { *(.data.init_task) } /* will be freed after init */ - . = ALIGN(4096); /* Init code and data */ + . = ALIGN(PAGE_SIZE_asm); /* Init code and data */ __init_begin = .; .init.text : { _sinittext = .; @@ -91,7 +104,7 @@ SECTIONS from .altinstructions and .eh_frame */ .exit.text : { *(.exit.text) } .exit.data : { *(.exit.data) } - . = ALIGN(4096); + . = ALIGN(PAGE_SIZE_asm); __initramfs_start = .; .init.ramfs : { *(.init.ramfs) } __initramfs_end = .; @@ -99,16 +112,33 @@ SECTIONS __per_cpu_start = .; .data.percpu : { *(.data.percpu) } __per_cpu_end = .; - . = ALIGN(4096); + . = ALIGN(PAGE_SIZE_asm); __init_end = .; /* freed after init ends here */ - + + . = ALIGN(PAGE_SIZE_asm); + .data.page_aligned_tss : { *(.data.tss) } + + . = ALIGN(PAGE_SIZE_asm); + .data.page_aligned_default_ldt : { *(.data.default_ldt) } + + . = ALIGN(PAGE_SIZE_asm); + .data.page_aligned_idt : { *(.data.idt) } + + . = ALIGN(PAGE_SIZE_asm); + .data.page_aligned_gdt : { *(.data.gdt) } + __bss_start = .; /* BSS */ .bss : { *(.bss) } + . = ALIGN(4); __bss_stop = .; _end = . ; + /* This is where the kernel creates the early boot page tables */ + . = ALIGN(4096); + pg0 = .; + /* Sections to be discarded */ /DISCARD/ : { *(.exitcall.exit) @@ -122,4 +152,6 @@ SECTIONS .stab.index 0 : { *(.stab.index) } .stab.indexstr 0 : { *(.stab.indexstr) } .comment 0 : { *(.comment) } + + } --- linux-2.6.4-rc2/arch/i386/kernel/vsyscall.lds 2003-11-09 16:45:04.000000000 -0800 +++ 25/arch/i386/kernel/vsyscall.lds 2004-03-07 20:48:18.000000000 -0800 @@ -5,7 +5,7 @@ */ /* This must match . */ -VSYSCALL_BASE = 0xffffe000; +VSYSCALL_BASE = 0xffffd000; SECTIONS { --- linux-2.6.4-rc2/arch/i386/kernel/vsyscall-sysenter.S 2003-11-09 16:45:04.000000000 -0800 +++ 25/arch/i386/kernel/vsyscall-sysenter.S 2004-03-07 20:48:18.000000000 -0800 @@ -7,6 +7,11 @@ .type __kernel_vsyscall,@function __kernel_vsyscall: .LSTART_vsyscall: + cmpl $192, %eax + jne 1f + int $0x80 + ret +1: push %ecx .Lpush_ecx: push %edx --- linux-2.6.4-rc2/arch/i386/lib/checksum.S 2003-11-09 16:45:04.000000000 -0800 +++ 25/arch/i386/lib/checksum.S 2004-03-07 20:48:18.000000000 -0800 @@ -280,14 +280,14 @@ unsigned int csum_partial_copy_generic ( .previous .align 4 -.globl csum_partial_copy_generic +.globl direct_csum_partial_copy_generic #ifndef CONFIG_X86_USE_PPRO_CHECKSUM #define ARGBASE 16 #define FP 12 -csum_partial_copy_generic: +direct_csum_partial_copy_generic: subl $4,%esp pushl %edi pushl %esi @@ -422,7 +422,7 @@ DST( movb %cl, (%edi) ) #define ARGBASE 12 -csum_partial_copy_generic: +direct_csum_partial_copy_generic: pushl %ebx pushl %edi pushl %esi --- linux-2.6.4-rc2/arch/i386/lib/dec_and_lock.c 2003-11-09 16:45:04.000000000 -0800 +++ 25/arch/i386/lib/dec_and_lock.c 2004-03-07 20:48:17.000000000 -0800 @@ -10,6 +10,7 @@ #include #include +#ifndef ATOMIC_DEC_AND_LOCK int atomic_dec_and_lock(atomic_t *atomic, spinlock_t *lock) { int counter; @@ -38,3 +39,5 @@ slow_path: spin_unlock(lock); return 0; } +#endif + --- linux-2.6.4-rc2/arch/i386/lib/getuser.S 2003-11-09 16:45:04.000000000 -0800 +++ 25/arch/i386/lib/getuser.S 2004-03-07 20:48:18.000000000 -0800 @@ -9,6 +9,7 @@ * return value. */ #include +#include /* @@ -28,7 +29,7 @@ .globl __get_user_1 __get_user_1: GET_THREAD_INFO(%edx) - cmpl TI_ADDR_LIMIT(%edx),%eax + cmpl TI_addr_limit(%edx),%eax jae bad_get_user 1: movzbl (%eax),%edx xorl %eax,%eax @@ -40,7 +41,7 @@ __get_user_2: addl $1,%eax jc bad_get_user GET_THREAD_INFO(%edx) - cmpl TI_ADDR_LIMIT(%edx),%eax + cmpl TI_addr_limit(%edx),%eax jae bad_get_user 2: movzwl -1(%eax),%edx xorl %eax,%eax @@ -52,7 +53,7 @@ __get_user_4: addl $3,%eax jc bad_get_user GET_THREAD_INFO(%edx) - cmpl TI_ADDR_LIMIT(%edx),%eax + cmpl TI_addr_limit(%edx),%eax jae bad_get_user 3: movl -3(%eax),%edx xorl %eax,%eax --- linux-2.6.4-rc2/arch/i386/lib/iodebug.c 2003-06-14 12:18:33.000000000 -0700 +++ /dev/null 2002-08-30 16:31:37.000000000 -0700 @@ -1,11 +0,0 @@ -#include - -void * __io_virt_debug(unsigned long x, const char *file, int line) -{ - if (x < PAGE_OFFSET) { - printk("io mapaddr 0x%05lx not valid at %s:%d!\n", x, file, line); - return __va(x); - } - return (void *)x; -} - --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/arch/i386/lib/kgdb_serial.c 2004-03-07 20:47:03.000000000 -0800 @@ -0,0 +1,499 @@ +/* + * Serial interface GDB stub + * + * Written (hacked together) by David Grothe (dave@gcom.com) + * Modified to allow invokation early in boot see also + * kgdb.h for instructions by George Anzinger(george@mvista.com) + * Modified to handle debugging over ethernet by Robert Walsh + * and wangdi , based on + * code by San Mehat. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_KGDB_USER_CONSOLE +extern void kgdb_console_finit(void); +#endif +#define PRNT_off +#define TEST_EXISTANCE +#ifdef PRNT +#define dbprintk(s) printk s +#else +#define dbprintk(s) +#endif +#define TEST_INTERRUPT_off +#ifdef TEST_INTERRUPT +#define intprintk(s) printk s +#else +#define intprintk(s) +#endif + +#define IRQ_T(info) ((info->flags & ASYNC_SHARE_IRQ) ? SA_SHIRQ : SA_INTERRUPT) + +#define GDB_BUF_SIZE 512 /* power of 2, please */ + +static char gdb_buf[GDB_BUF_SIZE]; +static int gdb_buf_in_inx; +static atomic_t gdb_buf_in_cnt; +static int gdb_buf_out_inx; + +struct async_struct *gdb_async_info; +static int gdb_async_irq; + +#define outb_px(a,b) outb_p(b,a) + +static void program_uart(struct async_struct *info); +static void write_char(struct async_struct *info, int chr); +/* + * Get a byte from the hardware data buffer and return it + */ +static int +read_data_bfr(struct async_struct *info) +{ + char it = inb_p(info->port + UART_LSR); + + if (it & UART_LSR_DR) + return (inb_p(info->port + UART_RX)); + /* + * If we have a framing error assume somebody messed with + * our uart. Reprogram it and send '-' both ways... + */ + if (it & 0xc) { + program_uart(info); + write_char(info, '-'); + return ('-'); + } + return (-1); + +} /* read_data_bfr */ + +/* + * Get a char if available, return -1 if nothing available. + * Empty the receive buffer first, then look at the interface hardware. + + * Locking here is a bit of a problem. We MUST not lock out communication + * if we are trying to talk to gdb about a kgdb entry. ON the other hand + * we can loose chars in the console pass thru if we don't lock. It is also + * possible that we could hold the lock or be waiting for it when kgdb + * NEEDS to talk. Since kgdb locks down the world, it does not need locks. + * We do, of course have possible issues with interrupting a uart operation, + * but we will just depend on the uart status to help keep that straight. + + */ +static spinlock_t uart_interrupt_lock = SPIN_LOCK_UNLOCKED; +#ifdef CONFIG_SMP +extern spinlock_t kgdb_spinlock; +#endif + +static int +read_char(struct async_struct *info) +{ + int chr; + unsigned long flags; + local_irq_save(flags); +#ifdef CONFIG_SMP + if (!spin_is_locked(&kgdb_spinlock)) { + spin_lock(&uart_interrupt_lock); + } +#endif + if (atomic_read(&gdb_buf_in_cnt) != 0) { /* intr routine has q'd chars */ + chr = gdb_buf[gdb_buf_out_inx++]; + gdb_buf_out_inx &= (GDB_BUF_SIZE - 1); + atomic_dec(&gdb_buf_in_cnt); + } else { + chr = read_data_bfr(info); + } +#ifdef CONFIG_SMP + if (!spin_is_locked(&kgdb_spinlock)) { + spin_unlock(&uart_interrupt_lock); + } +#endif + local_irq_restore(flags); + return (chr); +} + +/* + * Wait until the interface can accept a char, then write it. + */ +static void +write_char(struct async_struct *info, int chr) +{ + while (!(inb_p(info->port + UART_LSR) & UART_LSR_THRE)) ; + + outb_p(chr, info->port + UART_TX); + +} /* write_char */ + +/* + * Mostly we don't need a spinlock, but since the console goes + * thru here with interrutps on, well, we need to catch those + * chars. + */ +/* + * This is the receiver interrupt routine for the GDB stub. + * It will receive a limited number of characters of input + * from the gdb host machine and save them up in a buffer. + * + * When the gdb stub routine tty_getDebugChar() is called it + * draws characters out of the buffer until it is empty and + * then reads directly from the serial port. + * + * We do not attempt to write chars from the interrupt routine + * since the stubs do all of that via tty_putDebugChar() which + * writes one byte after waiting for the interface to become + * ready. + * + * The debug stubs like to run with interrupts disabled since, + * after all, they run as a consequence of a breakpoint in + * the kernel. + * + * Perhaps someone who knows more about the tty driver than I + * care to learn can make this work for any low level serial + * driver. + */ +static irqreturn_t +gdb_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct async_struct *info; + unsigned long flags; + + info = gdb_async_info; + if (!info || !info->tty || irq != gdb_async_irq) + return IRQ_NONE; + + local_irq_save(flags); + spin_lock(&uart_interrupt_lock); + do { + int chr = read_data_bfr(info); + intprintk(("Debug char on int: %x hex\n", chr)); + if (chr < 0) + continue; + + if (chr == 3) { /* Ctrl-C means remote interrupt */ + BREAKPOINT; + continue; + } + + if (atomic_read(&gdb_buf_in_cnt) >= GDB_BUF_SIZE) { + /* buffer overflow tosses early char */ + read_char(info); + } + gdb_buf[gdb_buf_in_inx++] = chr; + gdb_buf_in_inx &= (GDB_BUF_SIZE - 1); + } while (inb_p(info->port + UART_IIR) & UART_IIR_RDI); + spin_unlock(&uart_interrupt_lock); + local_irq_restore(flags); + return IRQ_HANDLED; +} /* gdb_interrupt */ + +/* + * Just a NULL routine for testing. + */ +void +gdb_null(void) +{ +} /* gdb_null */ + +/* These structure are filled in with values defined in asm/kgdb_local.h + */ +static struct serial_state state = SB_STATE; +static struct async_struct local_info = SB_INFO; +static int ok_to_enable_ints = 0; +static void kgdb_enable_ints_now(void); + +extern char *kgdb_version; +/* + * Hook an IRQ for KGDB. + * + * This routine is called from tty_putDebugChar, below. + */ +static int ints_disabled = 1; +int +gdb_hook_interrupt(struct async_struct *info, int verb) +{ + struct serial_state *state = info->state; + unsigned long flags; + int port; +#ifdef TEST_EXISTANCE + int scratch, scratch2; +#endif + + /* The above fails if memory managment is not set up yet. + * Rather than fail the set up, just keep track of the fact + * and pick up the interrupt thing later. + */ + gdb_async_info = info; + port = gdb_async_info->port; + gdb_async_irq = state->irq; + if (verb) { + printk("kgdb %s : port =%x, IRQ=%d, divisor =%d\n", + kgdb_version, + port, + gdb_async_irq, gdb_async_info->state->custom_divisor); + } + local_irq_save(flags); +#ifdef TEST_EXISTANCE + /* Existance test */ + /* Should not need all this, but just in case.... */ + + scratch = inb_p(port + UART_IER); + outb_px(port + UART_IER, 0); + outb_px(0xff, 0x080); + scratch2 = inb_p(port + UART_IER); + outb_px(port + UART_IER, scratch); + if (scratch2) { + printk + ("gdb_hook_interrupt: Could not clear IER, not a UART!\n"); + local_irq_restore(flags); + return 1; /* We failed; there's nothing here */ + } + scratch2 = inb_p(port + UART_LCR); + outb_px(port + UART_LCR, 0xBF); /* set up for StarTech test */ + outb_px(port + UART_EFR, 0); /* EFR is the same as FCR */ + outb_px(port + UART_LCR, 0); + outb_px(port + UART_FCR, UART_FCR_ENABLE_FIFO); + scratch = inb_p(port + UART_IIR) >> 6; + if (scratch == 1) { + printk("gdb_hook_interrupt: Undefined UART type!" + " Not a UART! \n"); + local_irq_restore(flags); + return 1; + } else { + dbprintk(("gdb_hook_interrupt: UART type " + "is %d where 0=16450, 2=16550 3=16550A\n", scratch)); + } + scratch = inb_p(port + UART_MCR); + outb_px(port + UART_MCR, UART_MCR_LOOP | scratch); + outb_px(port + UART_MCR, UART_MCR_LOOP | 0x0A); + scratch2 = inb_p(port + UART_MSR) & 0xF0; + outb_px(port + UART_MCR, scratch); + if (scratch2 != 0x90) { + printk("gdb_hook_interrupt: " + "Loop back test failed! Not a UART!\n"); + local_irq_restore(flags); + return scratch2 + 1000; /* force 0 to fail */ + } +#endif /* test existance */ + program_uart(info); + local_irq_restore(flags); + + return (0); + +} /* gdb_hook_interrupt */ + +static void +program_uart(struct async_struct *info) +{ + int port = info->port; + + (void) inb_p(port + UART_RX); + outb_px(port + UART_IER, 0); + + (void) inb_p(port + UART_RX); /* serial driver comments say */ + (void) inb_p(port + UART_IIR); /* this clears the interrupt regs */ + (void) inb_p(port + UART_MSR); + outb_px(port + UART_LCR, UART_LCR_WLEN8 | UART_LCR_DLAB); + outb_px(port + UART_DLL, info->state->custom_divisor & 0xff); /* LS */ + outb_px(port + UART_DLM, info->state->custom_divisor >> 8); /* MS */ + outb_px(port + UART_MCR, info->MCR); + + outb_px(port + UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1 | UART_FCR_CLEAR_XMIT | UART_FCR_CLEAR_RCVR); /* set fcr */ + outb_px(port + UART_LCR, UART_LCR_WLEN8); /* reset DLAB */ + outb_px(port + UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1); /* set fcr */ + if (!ints_disabled) { + intprintk(("KGDB: Sending %d to port %x offset %d\n", + gdb_async_info->IER, + (int) gdb_async_info->port, UART_IER)); + outb_px(gdb_async_info->port + UART_IER, gdb_async_info->IER); + } + return; +} + +/* + * tty_getDebugChar + * + * This is a GDB stub routine. It waits for a character from the + * serial interface and then returns it. If there is no serial + * interface connection then it returns a bogus value which will + * almost certainly cause the system to hang. In the + */ +int kgdb_in_isr = 0; +int kgdb_in_lsr = 0; +extern spinlock_t kgdb_spinlock; + +/* Caller takes needed protections */ + +int +tty_getDebugChar(void) +{ + volatile int chr, dum, time, end_time; + + dbprintk(("tty_getDebugChar(port %x): ", gdb_async_info->port)); + + if (gdb_async_info == NULL) { + gdb_hook_interrupt(&local_info, 0); + } + /* + * This trick says if we wait a very long time and get + * no char, return the -1 and let the upper level deal + * with it. + */ + rdtsc(dum, time); + end_time = time + 2; + while (((chr = read_char(gdb_async_info)) == -1) && + (end_time - time) > 0) { + rdtsc(dum, time); + }; + /* + * This covers our butts if some other code messes with + * our uart, hay, it happens :o) + */ + if (chr == -1) + program_uart(gdb_async_info); + + dbprintk(("%c\n", chr > ' ' && chr < 0x7F ? chr : ' ')); + return (chr); + +} /* tty_getDebugChar */ + +static int count = 3; +static spinlock_t one_at_atime = SPIN_LOCK_UNLOCKED; + +static int __init +kgdb_enable_ints(void) +{ + if (kgdboe) { + return 0; + } + if (gdb_async_info == NULL) { + gdb_hook_interrupt(&local_info, 1); + } + ok_to_enable_ints = 1; + kgdb_enable_ints_now(); +#ifdef CONFIG_KGDB_USER_CONSOLE + kgdb_console_finit(); +#endif + return 0; +} + +#ifdef CONFIG_SERIAL_8250 +void shutdown_for_kgdb(struct async_struct *gdb_async_info); +#endif + +#ifdef CONFIG_DISCONTIGMEM +static inline int kgdb_mem_init_done(void) +{ + return highmem_start_page != NULL; +} +#else +static inline int kgdb_mem_init_done(void) +{ + return max_mapnr != 0; +} +#endif + +static void +kgdb_enable_ints_now(void) +{ + if (!spin_trylock(&one_at_atime)) + return; + if (!ints_disabled) + goto exit; + if (kgdb_mem_init_done() && + ints_disabled) { /* don't try till mem init */ +#ifdef CONFIG_SERIAL_8250 + /* + * The ifdef here allows the system to be configured + * without the serial driver. + * Don't make it a module, however, it will steal the port + */ + shutdown_for_kgdb(gdb_async_info); +#endif + ints_disabled = request_irq(gdb_async_info->state->irq, + gdb_interrupt, + IRQ_T(gdb_async_info), + "KGDB-stub", NULL); + intprintk(("KGDB: request_irq returned %d\n", ints_disabled)); + } + if (!ints_disabled) { + intprintk(("KGDB: Sending %d to port %x offset %d\n", + gdb_async_info->IER, + (int) gdb_async_info->port, UART_IER)); + outb_px(gdb_async_info->port + UART_IER, gdb_async_info->IER); + } + exit: + spin_unlock(&one_at_atime); +} + +/* + * tty_putDebugChar + * + * This is a GDB stub routine. It waits until the interface is ready + * to transmit a char and then sends it. If there is no serial + * interface connection then it simply returns to its caller, having + * pretended to send the char. Caller takes needed protections. + */ +void +tty_putDebugChar(int chr) +{ + dbprintk(("tty_putDebugChar(port %x): chr=%02x '%c', ints_on=%d\n", + gdb_async_info->port, + chr, + chr > ' ' && chr < 0x7F ? chr : ' ', ints_disabled ? 0 : 1)); + + if (gdb_async_info == NULL) { + gdb_hook_interrupt(&local_info, 0); + } + + write_char(gdb_async_info, chr); /* this routine will wait */ + count = (chr == '#') ? 0 : count + 1; + if ((count == 2)) { /* try to enable after */ + if (ints_disabled & ok_to_enable_ints) + kgdb_enable_ints_now(); /* try to enable after */ + + /* We do this a lot because, well we really want to get these + * interrupts. The serial driver will clear these bits when it + * initializes the chip. Every thing else it does is ok, + * but this. + */ + if (!ints_disabled) { + outb_px(gdb_async_info->port + UART_IER, + gdb_async_info->IER); + } + } + +} /* tty_putDebugChar */ + +/* + * This does nothing for the serial port, since it doesn't buffer. + */ + +void tty_flushDebugChar(void) +{ +} + +module_init(kgdb_enable_ints); --- linux-2.6.4-rc2/arch/i386/lib/Makefile 2003-11-09 16:45:04.000000000 -0800 +++ 25/arch/i386/lib/Makefile 2004-03-07 20:47:45.000000000 -0800 @@ -9,4 +9,4 @@ lib-y = checksum.o delay.o \ lib-$(CONFIG_X86_USE_3DNOW) += mmx.o lib-$(CONFIG_HAVE_DEC_LOCK) += dec_and_lock.o -lib-$(CONFIG_DEBUG_IOVIRT) += iodebug.o +lib-$(CONFIG_KGDB) += kgdb_serial.o --- linux-2.6.4-rc2/arch/i386/lib/usercopy.c 2004-01-09 00:04:30.000000000 -0800 +++ 25/arch/i386/lib/usercopy.c 2004-03-07 20:48:18.000000000 -0800 @@ -76,7 +76,7 @@ do { \ * and returns @count. */ long -__strncpy_from_user(char *dst, const char __user *src, long count) +__direct_strncpy_from_user(char *dst, const char __user *src, long count) { long res; __do_strncpy_from_user(dst, src, count, res); @@ -102,7 +102,7 @@ __strncpy_from_user(char *dst, const cha * and returns @count. */ long -strncpy_from_user(char *dst, const char __user *src, long count) +direct_strncpy_from_user(char *dst, const char __user *src, long count) { long res = -EFAULT; if (access_ok(VERIFY_READ, src, 1)) @@ -147,7 +147,7 @@ do { \ * On success, this will be zero. */ unsigned long -clear_user(void __user *to, unsigned long n) +direct_clear_user(void __user *to, unsigned long n) { might_sleep(); if (access_ok(VERIFY_WRITE, to, n)) @@ -167,7 +167,7 @@ clear_user(void __user *to, unsigned lon * On success, this will be zero. */ unsigned long -__clear_user(void __user *to, unsigned long n) +__direct_clear_user(void __user *to, unsigned long n) { __do_clear_user(to, n); return n; @@ -184,7 +184,7 @@ __clear_user(void __user *to, unsigned l * On exception, returns 0. * If the string is too long, returns a value greater than @n. */ -long strnlen_user(const char __user *s, long n) +long direct_strnlen_user(const char __user *s, long n) { unsigned long mask = -__addr_ok(s); unsigned long res, tmp; @@ -575,3 +575,4 @@ unsigned long __copy_from_user_ll(void * n = __copy_user_zeroing_intel(to, (const void *) from, n); return n; } + --- linux-2.6.4-rc2/arch/i386/mach-default/Makefile 2003-06-14 12:18:06.000000000 -0700 +++ 25/arch/i386/mach-default/Makefile 2004-03-07 20:47:56.000000000 -0800 @@ -2,6 +2,4 @@ # Makefile for the linux kernel. # -EXTRA_CFLAGS += -I../kernel - obj-y := setup.o topology.o --- linux-2.6.4-rc2/arch/i386/mach-es7000/Makefile 2003-06-16 22:32:20.000000000 -0700 +++ 25/arch/i386/mach-es7000/Makefile 2004-03-07 20:47:56.000000000 -0800 @@ -2,6 +2,4 @@ # Makefile for the linux kernel. # -EXTRA_CFLAGS += -I../kernel - obj-y := setup.o topology.o es7000.o --- linux-2.6.4-rc2/arch/i386/mach-pc9800/Makefile 2003-06-14 12:18:30.000000000 -0700 +++ 25/arch/i386/mach-pc9800/Makefile 2004-03-07 20:47:56.000000000 -0800 @@ -2,6 +2,4 @@ # Makefile for the linux kernel. # -EXTRA_CFLAGS += -I../kernel - obj-y := setup.o topology.o --- linux-2.6.4-rc2/arch/i386/mach-visws/Makefile 2003-06-14 12:18:30.000000000 -0700 +++ 25/arch/i386/mach-visws/Makefile 2004-03-07 20:47:56.000000000 -0800 @@ -2,8 +2,6 @@ # Makefile for the linux kernel. # -EXTRA_CFLAGS += -I../kernel - obj-y := setup.o traps.o reboot.o obj-$(CONFIG_X86_VISWS_APIC) += visws_apic.o --- linux-2.6.4-rc2/arch/i386/Makefile 2004-03-03 23:12:42.000000000 -0800 +++ 25/arch/i386/Makefile 2004-03-07 20:48:20.000000000 -0800 @@ -19,7 +19,7 @@ LDFLAGS := -m elf_i386 OBJCOPYFLAGS := -O binary -R .note -R .comment -S LDFLAGS_vmlinux := -CFLAGS += -pipe +CFLAGS += -pipe -msoft-float # prevent gcc from keeping the stack 16 byte aligned CFLAGS += $(call check_gcc,-mpreferred-stack-boundary=2,) @@ -56,9 +56,9 @@ cflags-$(CONFIG_X86_ELAN) += -march=i486 GCC_VERSION := $(shell $(CONFIG_SHELL) $(srctree)/scripts/gcc-version.sh $(CC)) cflags-$(CONFIG_REGPARM) += $(shell if [ $(GCC_VERSION) -ge 0300 ] ; then echo "-mregparm=3"; fi ;) -# Enable unit-at-a-time mode when possible. It shrinks the -# kernel considerably. -CFLAGS += $(call check_gcc,-funit-at-a-time,) +# Disable unit-at-a-time mode, it makes gcc use a lot more stack +# due to the lack of sharing of stacklots. +CFLAGS += $(call check_gcc,-fno-unit-at-a-time,) CFLAGS += $(cflags-y) @@ -97,6 +97,9 @@ mcore-$(CONFIG_X86_ES7000) := mach-es700 # default subarch .h files mflags-y += -Iinclude/asm-i386/mach-default +mflags-$(CONFIG_KGDB) += -gdwarf-2 +mflags-$(CONFIG_KGDB_MORE) += $(shell echo $(CONFIG_KGDB_OPTIONS) | sed -e 's/"//g') + head-y := arch/i386/kernel/head.o arch/i386/kernel/init_task.o libs-y += arch/i386/lib/ --- linux-2.6.4-rc2/arch/i386/math-emu/fpu_system.h 2003-11-09 16:45:04.000000000 -0800 +++ 25/arch/i386/math-emu/fpu_system.h 2004-03-07 20:48:18.000000000 -0800 @@ -15,6 +15,7 @@ #include #include #include +#include /* This sets the pointer FPU_info to point to the argument part of the stack frame of math_emulate() */ @@ -22,7 +23,7 @@ /* s is always from a cpu register, and the cpu does bounds checking * during register load --> no further bounds checks needed */ -#define LDT_DESCRIPTOR(s) (((struct desc_struct *)current->mm->context.ldt)[(s) >> 3]) +#define LDT_DESCRIPTOR(s) (((struct desc_struct *)__kmap_atomic_vaddr(KM_LDT_PAGE0))[(s) >> 3]) #define SEG_D_SIZE(x) ((x).b & (3 << 21)) #define SEG_G_BIT(x) ((x).b & (1 << 23)) #define SEG_GRANULARITY(x) (((x).b & (1 << 23)) ? 4096 : 1) --- linux-2.6.4-rc2/arch/i386/mm/discontig.c 2003-09-27 18:57:43.000000000 -0700 +++ 25/arch/i386/mm/discontig.c 2004-03-07 20:47:46.000000000 -0800 @@ -66,7 +66,7 @@ extern void find_max_pfn(void); extern void one_highpage_init(struct page *, int, int); extern struct e820map e820; -extern char _end; +extern unsigned long init_pg_tables_end; extern unsigned long highend_pfn, highstart_pfn; extern unsigned long max_low_pfn; extern unsigned long totalram_pages; @@ -237,7 +237,7 @@ unsigned long __init setup_memory(void) reserve_pages = calculate_numa_remap_pages(); /* partially used pages are not usable - thus round upwards */ - system_start_pfn = min_low_pfn = PFN_UP(__pa(&_end)); + system_start_pfn = min_low_pfn = PFN_UP(init_pg_tables_end); find_max_pfn(); system_max_low_pfn = max_low_pfn = find_max_low_pfn(); --- linux-2.6.4-rc2/arch/i386/mm/extable.c 2004-02-03 20:42:34.000000000 -0800 +++ 25/arch/i386/mm/extable.c 2004-03-07 20:46:45.000000000 -0800 @@ -12,7 +12,7 @@ int fixup_exception(struct pt_regs *regs const struct exception_table_entry *fixup; #ifdef CONFIG_PNPBIOS - if (unlikely((regs->xcs | 8) == 0x88)) /* 0x80 or 0x88 */ + if (unlikely((regs->xcs & ~15) == (GDT_ENTRY_PNPBIOS_BASE << 3))) { extern u32 pnp_bios_fault_eip, pnp_bios_fault_esp; extern u32 pnp_bios_is_utter_crap; @@ -21,7 +21,7 @@ int fixup_exception(struct pt_regs *regs __asm__ volatile( "movl %0, %%esp\n\t" "jmp *%1\n\t" - : "=a" (pnp_bios_fault_esp), "=b" (pnp_bios_fault_eip)); + : : "g" (pnp_bios_fault_esp), "g" (pnp_bios_fault_eip)); panic("do_trap: can't hit this"); } #endif --- linux-2.6.4-rc2/arch/i386/mm/fault.c 2003-12-17 21:20:01.000000000 -0800 +++ 25/arch/i386/mm/fault.c 2004-03-07 20:48:18.000000000 -0800 @@ -27,6 +27,7 @@ #include #include #include +#include extern void die(const char *,struct pt_regs *,long); @@ -104,8 +105,17 @@ static inline unsigned long get_segment_ if (seg & (1<<2)) { /* Must lock the LDT while reading it. */ down(¤t->mm->context.sem); +#if 1 + /* horrible hack for 4/4 disabled kernels. + I'm not quite sure what the TLB flush is good for, + it's mindlessly copied from the read_ldt code */ + __flush_tlb_global(); + desc = kmap(current->mm->context.ldt_pages[(seg&~7)/PAGE_SIZE]); + desc = (void *)desc + ((seg & ~7) % PAGE_SIZE); +#else desc = current->mm->context.ldt; desc = (void *)desc + (seg & ~7); +#endif } else { /* Must disable preemption while reading the GDT. */ desc = (u32 *)&cpu_gdt_table[get_cpu()]; @@ -118,6 +128,9 @@ static inline unsigned long get_segment_ (desc[1] & 0xff000000); if (seg & (1<<2)) { +#if 1 + kunmap((void *)((unsigned long)desc & PAGE_MASK)); +#endif up(¤t->mm->context.sem); } else put_cpu(); @@ -243,6 +256,19 @@ asmlinkage void do_page_fault(struct pt_ * (error_code & 4) == 0, and that the fault was not a * protection error (error_code & 1) == 0. */ +#ifdef CONFIG_X86_4G + /* + * On 4/4 all kernels faults are either bugs, vmalloc or prefetch + */ + if (unlikely((regs->xcs & 3) == 0)) { + if (error_code & 3) + goto bad_area_nosemaphore; + + /* If it's vm86 fall through */ + if (!(regs->eflags & VM_MASK)) + goto vmalloc_fault; + } +#else if (unlikely(address >= TASK_SIZE)) { if (!(error_code & 5)) goto vmalloc_fault; @@ -252,6 +278,7 @@ asmlinkage void do_page_fault(struct pt_ */ goto bad_area_nosemaphore; } +#endif mm = tsk->mm; @@ -326,6 +353,8 @@ good_area: goto do_sigbus; case VM_FAULT_OOM: goto out_of_memory; + case VM_FAULT_SIGSEGV: + goto bad_area; default: BUG(); } @@ -403,6 +432,12 @@ no_context: * Oops. The kernel tried to access some bad page. We'll have to * terminate things with extreme prejudice. */ +#ifdef CONFIG_KGDB + if (!user_mode(regs)){ + kgdb_handle_exception(14,SIGBUS, error_code, regs); + return; + } +#endif bust_spinlocks(1); --- linux-2.6.4-rc2/arch/i386/mm/hugetlbpage.c 2004-01-09 00:04:30.000000000 -0800 +++ 25/arch/i386/mm/hugetlbpage.c 2004-03-07 20:47:37.000000000 -0800 @@ -61,6 +61,27 @@ static struct page *alloc_fresh_huge_pag static void free_huge_page(struct page *page); +#ifdef CONFIG_NUMA + +static inline void huge_inc_rss(struct mm_struct *mm, struct page *page) +{ + mm->rss += (HPAGE_SIZE / PAGE_SIZE); + mm->pernode_rss[page_nodenum(page)] += (HPAGE_SIZE / PAGE_SIZE); +} + +static inline void huge_dec_rss(struct mm_struct *mm, struct page *page) +{ + mm->rss -= (HPAGE_SIZE / PAGE_SIZE); + mm->pernode_rss[page_nodenum(page)] -= (HPAGE_SIZE / PAGE_SIZE); +} + +#else /* !CONFIG_NUMA */ + +#define huge_inc_rss(mm, page) ((mm)->rss += (HPAGE_SIZE / PAGE_SIZE)) +#define huge_dec_rss(mm, page) ((mm)->rss -= (HPAGE_SIZE / PAGE_SIZE)) + +#endif /* CONFIG_NUMA */ + static struct page *alloc_hugetlb_page(void) { int i; @@ -105,7 +126,7 @@ static void set_huge_pte(struct mm_struc { pte_t entry; - mm->rss += (HPAGE_SIZE / PAGE_SIZE); + huge_inc_rss(mm, page); if (write_access) { entry = pte_mkwrite(pte_mkdirty(mk_pte(page, vma->vm_page_prot))); @@ -145,7 +166,7 @@ int copy_hugetlb_page_range(struct mm_st ptepage = pte_page(entry); get_page(ptepage); set_pte(dst_pte, entry); - dst->rss += (HPAGE_SIZE / PAGE_SIZE); + huge_inc_rss(dst, ptepage); addr += HPAGE_SIZE; } return 0; @@ -314,8 +335,8 @@ void unmap_hugepage_range(struct vm_area page = pte_page(*pte); huge_page_release(page); pte_clear(pte); + huge_dec_rss(mm, page); } - mm->rss -= (end - start) >> PAGE_SHIFT; flush_tlb_range(vma, start, end); } --- linux-2.6.4-rc2/arch/i386/mm/init.c 2004-01-09 00:04:30.000000000 -0800 +++ 25/arch/i386/mm/init.c 2004-03-07 20:48:18.000000000 -0800 @@ -40,125 +40,13 @@ #include #include #include +#include DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); unsigned long highstart_pfn, highend_pfn; static int do_test_wp_bit(void); -/* - * Creates a middle page table and puts a pointer to it in the - * given global directory entry. This only returns the gd entry - * in non-PAE compilation mode, since the middle layer is folded. - */ -static pmd_t * __init one_md_table_init(pgd_t *pgd) -{ - pmd_t *pmd_table; - -#ifdef CONFIG_X86_PAE - pmd_table = (pmd_t *) alloc_bootmem_low_pages(PAGE_SIZE); - set_pgd(pgd, __pgd(__pa(pmd_table) | _PAGE_PRESENT)); - if (pmd_table != pmd_offset(pgd, 0)) - BUG(); -#else - pmd_table = pmd_offset(pgd, 0); -#endif - - return pmd_table; -} - -/* - * Create a page table and place a pointer to it in a middle page - * directory entry. - */ -static pte_t * __init one_page_table_init(pmd_t *pmd) -{ - if (pmd_none(*pmd)) { - pte_t *page_table = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE); - set_pmd(pmd, __pmd(__pa(page_table) | _PAGE_TABLE)); - if (page_table != pte_offset_kernel(pmd, 0)) - BUG(); - - return page_table; - } - - return pte_offset_kernel(pmd, 0); -} - -/* - * This function initializes a certain range of kernel virtual memory - * with new bootmem page tables, everywhere page tables are missing in - * the given range. - */ - -/* - * NOTE: The pagetables are allocated contiguous on the physical space - * so we can cache the place of the first one and move around without - * checking the pgd every time. - */ -static void __init page_table_range_init (unsigned long start, unsigned long end, pgd_t *pgd_base) -{ - pgd_t *pgd; - pmd_t *pmd; - int pgd_idx, pmd_idx; - unsigned long vaddr; - - vaddr = start; - pgd_idx = pgd_index(vaddr); - pmd_idx = pmd_index(vaddr); - pgd = pgd_base + pgd_idx; - - for ( ; (pgd_idx < PTRS_PER_PGD) && (vaddr != end); pgd++, pgd_idx++) { - if (pgd_none(*pgd)) - one_md_table_init(pgd); - - pmd = pmd_offset(pgd, vaddr); - for (; (pmd_idx < PTRS_PER_PMD) && (vaddr != end); pmd++, pmd_idx++) { - if (pmd_none(*pmd)) - one_page_table_init(pmd); - - vaddr += PMD_SIZE; - } - pmd_idx = 0; - } -} - -/* - * This maps the physical memory to kernel virtual address space, a total - * of max_low_pfn pages, by creating page tables starting from address - * PAGE_OFFSET. - */ -static void __init kernel_physical_mapping_init(pgd_t *pgd_base) -{ - unsigned long pfn; - pgd_t *pgd; - pmd_t *pmd; - pte_t *pte; - int pgd_idx, pmd_idx, pte_ofs; - - pgd_idx = pgd_index(PAGE_OFFSET); - pgd = pgd_base + pgd_idx; - pfn = 0; - - for (; pgd_idx < PTRS_PER_PGD; pgd++, pgd_idx++) { - pmd = one_md_table_init(pgd); - if (pfn >= max_low_pfn) - continue; - for (pmd_idx = 0; pmd_idx < PTRS_PER_PMD && pfn < max_low_pfn; pmd++, pmd_idx++) { - /* Map with big pages if possible, otherwise create normal page tables. */ - if (cpu_has_pse) { - set_pmd(pmd, pfn_pmd(pfn, PAGE_KERNEL_LARGE)); - pfn += PTRS_PER_PTE; - } else { - pte = one_page_table_init(pmd); - - for (pte_ofs = 0; pte_ofs < PTRS_PER_PTE && pfn < max_low_pfn; pte++, pfn++, pte_ofs++) - set_pte(pte, pfn_pte(pfn, PAGE_KERNEL)); - } - } - } -} - static inline int page_kills_ppro(unsigned long pagenr) { if (pagenr >= 0x70000 && pagenr <= 0x7003F) @@ -206,11 +94,8 @@ static inline int page_is_ram(unsigned l return 0; } -#ifdef CONFIG_HIGHMEM pte_t *kmap_pte; -pgprot_t kmap_prot; -EXPORT_SYMBOL(kmap_prot); EXPORT_SYMBOL(kmap_pte); #define kmap_get_fixmap_pte(vaddr) \ @@ -218,29 +103,7 @@ EXPORT_SYMBOL(kmap_pte); void __init kmap_init(void) { - unsigned long kmap_vstart; - - /* cache the first kmap pte */ - kmap_vstart = __fix_to_virt(FIX_KMAP_BEGIN); - kmap_pte = kmap_get_fixmap_pte(kmap_vstart); - - kmap_prot = PAGE_KERNEL; -} - -void __init permanent_kmaps_init(pgd_t *pgd_base) -{ - pgd_t *pgd; - pmd_t *pmd; - pte_t *pte; - unsigned long vaddr; - - vaddr = PKMAP_BASE; - page_table_range_init(vaddr, vaddr + PAGE_SIZE*LAST_PKMAP, pgd_base); - - pgd = swapper_pg_dir + pgd_index(vaddr); - pmd = pmd_offset(pgd, vaddr); - pte = pte_offset_kernel(pmd, vaddr); - pkmap_page_table = pte; + kmap_pte = kmap_get_fixmap_pte(__fix_to_virt(FIX_KMAP_BEGIN)); } void __init one_highpage_init(struct page *page, int pfn, int bad_ppro) @@ -255,6 +118,8 @@ void __init one_highpage_init(struct pag SetPageReserved(page); } +#ifdef CONFIG_HIGHMEM + #ifndef CONFIG_DISCONTIGMEM void __init set_highmem_pages_init(int bad_ppro) { @@ -266,12 +131,9 @@ void __init set_highmem_pages_init(int b #else extern void set_highmem_pages_init(int); #endif /* !CONFIG_DISCONTIGMEM */ - #else -#define kmap_init() do { } while (0) -#define permanent_kmaps_init(pgd_base) do { } while (0) -#define set_highmem_pages_init(bad_ppro) do { } while (0) -#endif /* CONFIG_HIGHMEM */ +# define set_highmem_pages_init(bad_ppro) do { } while (0) +#endif unsigned long __PAGE_KERNEL = _PAGE_KERNEL; @@ -281,30 +143,125 @@ unsigned long __PAGE_KERNEL = _PAGE_KERN extern void __init remap_numa_kva(void); #endif -static void __init pagetable_init (void) +static __init void prepare_pagetables(pgd_t *pgd_base, unsigned long address) +{ + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + + pgd = pgd_base + pgd_index(address); + pmd = pmd_offset(pgd, address); + if (!pmd_present(*pmd)) { + pte = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE); + set_pmd(pmd, __pmd(_PAGE_TABLE + __pa(pte))); + } +} + +static void __init fixrange_init (unsigned long start, unsigned long end, pgd_t *pgd_base) { unsigned long vaddr; - pgd_t *pgd_base = swapper_pg_dir; + for (vaddr = start; vaddr != end; vaddr += PAGE_SIZE) + prepare_pagetables(pgd_base, vaddr); +} + +void setup_identity_mappings(pgd_t *pgd_base, unsigned long start, unsigned long end) +{ + unsigned long vaddr; + pgd_t *pgd; + int i, j, k; + pmd_t *pmd; + pte_t *pte, *pte_base; + + pgd = pgd_base; + + for (i = 0; i < PTRS_PER_PGD; pgd++, i++) { + vaddr = i*PGDIR_SIZE; + if (end && (vaddr >= end)) + break; + pmd = pmd_offset(pgd, 0); + for (j = 0; j < PTRS_PER_PMD; pmd++, j++) { + vaddr = i*PGDIR_SIZE + j*PMD_SIZE; + if (end && (vaddr >= end)) + break; + if (vaddr < start) + continue; + if (cpu_has_pse) { + unsigned long __pe; + + set_in_cr4(X86_CR4_PSE); + boot_cpu_data.wp_works_ok = 1; + __pe = _KERNPG_TABLE + _PAGE_PSE + vaddr - start; + /* Make it "global" too if supported */ + if (cpu_has_pge) { + set_in_cr4(X86_CR4_PGE); +#if !defined(CONFIG_X86_SWITCH_PAGETABLES) + __pe += _PAGE_GLOBAL; + __PAGE_KERNEL |= _PAGE_GLOBAL; +#endif + } + set_pmd(pmd, __pmd(__pe)); + continue; + } + if (!pmd_present(*pmd)) + pte_base = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE); + else + pte_base = (pte_t *) page_address(pmd_page(*pmd)); + pte = pte_base; + for (k = 0; k < PTRS_PER_PTE; pte++, k++) { + vaddr = i*PGDIR_SIZE + j*PMD_SIZE + k*PAGE_SIZE; + if (end && (vaddr >= end)) + break; + if (vaddr < start) + continue; + *pte = mk_pte_phys(vaddr-start, PAGE_KERNEL); + } + set_pmd(pmd, __pmd(_KERNPG_TABLE + __pa(pte_base))); + } + } +} + +static void __init pagetable_init (void) +{ + unsigned long vaddr, end; + pgd_t *pgd_base; #ifdef CONFIG_X86_PAE int i; - /* Init entries of the first-level page table to the zero page */ - for (i = 0; i < PTRS_PER_PGD; i++) - set_pgd(pgd_base + i, __pgd(__pa(empty_zero_page) | _PAGE_PRESENT)); #endif - /* Enable PSE if available */ - if (cpu_has_pse) { - set_in_cr4(X86_CR4_PSE); - } + /* + * This can be zero as well - no problem, in that case we exit + * the loops anyway due to the PTRS_PER_* conditions. + */ + end = (unsigned long)__va(max_low_pfn*PAGE_SIZE); - /* Enable PGE if available */ - if (cpu_has_pge) { - set_in_cr4(X86_CR4_PGE); - __PAGE_KERNEL |= _PAGE_GLOBAL; + pgd_base = swapper_pg_dir; +#ifdef CONFIG_X86_PAE + /* + * It causes too many problems if there's no proper pmd set up + * for all 4 entries of the PGD - so we allocate all of them. + * PAE systems will not miss this extra 4-8K anyway ... + */ + for (i = 0; i < PTRS_PER_PGD; i++) { + pmd_t *pmd = (pmd_t *) alloc_bootmem_low_pages(PAGE_SIZE); + set_pgd(pgd_base + i, __pgd(__pa(pmd) + 0x1)); } +#endif + /* + * Set up lowmem-sized identity mappings at PAGE_OFFSET: + */ + setup_identity_mappings(pgd_base, PAGE_OFFSET, end); - kernel_physical_mapping_init(pgd_base); + /* + * Add flat-mode identity-mappings - SMP needs it when + * starting up on an AP from real-mode. (In the non-PAE + * case we already have these mappings through head.S.) + * All user-space mappings are explicitly cleared after + * SMP startup. + */ +#if defined(CONFIG_SMP) && defined(CONFIG_X86_PAE) + setup_identity_mappings(pgd_base, 0, 16*1024*1024); +#endif remap_numa_kva(); /* @@ -312,38 +269,64 @@ static void __init pagetable_init (void) * created - mappings will be set by set_fixmap(): */ vaddr = __fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK; - page_table_range_init(vaddr, 0, pgd_base); + fixrange_init(vaddr, 0, pgd_base); - permanent_kmaps_init(pgd_base); +#ifdef CONFIG_HIGHMEM + { + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; -#ifdef CONFIG_X86_PAE - /* - * Add low memory identity-mappings - SMP needs it when - * starting up on an AP from real-mode. In the non-PAE - * case we already have these mappings through head.S. - * All user-space mappings are explicitly cleared after - * SMP startup. - */ - pgd_base[0] = pgd_base[USER_PTRS_PER_PGD]; + /* + * Permanent kmaps: + */ + vaddr = PKMAP_BASE; + fixrange_init(vaddr, vaddr + PAGE_SIZE*LAST_PKMAP, pgd_base); + + pgd = swapper_pg_dir + pgd_index(vaddr); + pmd = pmd_offset(pgd, vaddr); + pte = pte_offset_kernel(pmd, vaddr); + pkmap_page_table = pte; + } #endif } -void zap_low_mappings (void) +/* + * Clear kernel pagetables in a PMD_SIZE-aligned range. + */ +static void clear_mappings(pgd_t *pgd_base, unsigned long start, unsigned long end) { - int i; + unsigned long vaddr; + pgd_t *pgd; + pmd_t *pmd; + int i, j; + + pgd = pgd_base; + + for (i = 0; i < PTRS_PER_PGD; pgd++, i++) { + vaddr = i*PGDIR_SIZE; + if (end && (vaddr >= end)) + break; + pmd = pmd_offset(pgd, 0); + for (j = 0; j < PTRS_PER_PMD; pmd++, j++) { + vaddr = i*PGDIR_SIZE + j*PMD_SIZE; + if (end && (vaddr >= end)) + break; + if (vaddr < start) + continue; + pmd_clear(pmd); + } + } + flush_tlb_all(); +} + +void zap_low_mappings(void) +{ + printk("zapping low mappings.\n"); /* * Zap initial low-memory mappings. - * - * Note that "pgd_clear()" doesn't do it for - * us, because pgd_clear() is a no-op on i386. */ - for (i = 0; i < USER_PTRS_PER_PGD; i++) -#ifdef CONFIG_X86_PAE - set_pgd(swapper_pg_dir+i, __pgd(1 + __pa(empty_zero_page))); -#else - set_pgd(swapper_pg_dir+i, __pgd(0)); -#endif - flush_tlb_all(); + clear_mappings(swapper_pg_dir, 0, 16*1024*1024); } #ifndef CONFIG_DISCONTIGMEM @@ -393,7 +376,15 @@ void __init paging_init(void) set_in_cr4(X86_CR4_PAE); #endif __flush_tlb_all(); - + /* + * Subtle. SMP is doing it's boot stuff late (because it has to + * fork idle threads) - but it also needs low mappings for the + * protected-mode entry to work. We zap these entries only after + * the WP-bit has been tested. + */ +#ifndef CONFIG_SMP + zap_low_mappings(); +#endif kmap_init(); zone_sizes_init(); } @@ -515,22 +506,18 @@ void __init mem_init(void) if (boot_cpu_data.wp_works_ok < 0) test_wp_bit(); - /* - * Subtle. SMP is doing it's boot stuff late (because it has to - * fork idle threads) - but it also needs low mappings for the - * protected-mode entry to work. We zap these entries only after - * the WP-bit has been tested. - */ -#ifndef CONFIG_SMP - zap_low_mappings(); -#endif + entry_trampoline_setup(); + default_ldt_page = virt_to_page(default_ldt); + load_LDT(&init_mm.context); } -kmem_cache_t *pgd_cache; -kmem_cache_t *pmd_cache; +kmem_cache_t *pgd_cache, *pmd_cache, *kpmd_cache; void __init pgtable_cache_init(void) { + void (*ctor)(void *, kmem_cache_t *, unsigned long); + void (*dtor)(void *, kmem_cache_t *, unsigned long); + if (PTRS_PER_PMD > 1) { pmd_cache = kmem_cache_create("pmd", PTRS_PER_PMD*sizeof(pmd_t), @@ -540,13 +527,36 @@ void __init pgtable_cache_init(void) NULL); if (!pmd_cache) panic("pgtable_cache_init(): cannot create pmd cache"); + + if (TASK_SIZE > PAGE_OFFSET) { + kpmd_cache = kmem_cache_create("kpmd", + PTRS_PER_PMD*sizeof(pmd_t), + 0, + SLAB_HWCACHE_ALIGN | SLAB_MUST_HWCACHE_ALIGN, + kpmd_ctor, + NULL); + if (!kpmd_cache) + panic("pgtable_cache_init(): " + "cannot create kpmd cache"); + } } + + if (PTRS_PER_PMD == 1 || TASK_SIZE <= PAGE_OFFSET) + ctor = pgd_ctor; + else + ctor = NULL; + + if (PTRS_PER_PMD == 1 && TASK_SIZE <= PAGE_OFFSET) + dtor = pgd_dtor; + else + dtor = NULL; + pgd_cache = kmem_cache_create("pgd", PTRS_PER_PGD*sizeof(pgd_t), 0, SLAB_HWCACHE_ALIGN | SLAB_MUST_HWCACHE_ALIGN, - pgd_ctor, - PTRS_PER_PMD == 1 ? pgd_dtor : NULL); + ctor, + dtor); if (!pgd_cache) panic("pgtable_cache_init(): Cannot create pgd cache"); } --- linux-2.6.4-rc2/arch/i386/mm/pgtable.c 2003-11-09 16:45:05.000000000 -0800 +++ 25/arch/i386/mm/pgtable.c 2004-03-07 20:48:18.000000000 -0800 @@ -21,6 +21,7 @@ #include #include #include +#include void show_mem(void) { @@ -157,11 +158,20 @@ void pmd_ctor(void *pmd, kmem_cache_t *c memset(pmd, 0, PTRS_PER_PMD*sizeof(pmd_t)); } +void kpmd_ctor(void *__pmd, kmem_cache_t *cache, unsigned long flags) +{ + pmd_t *kpmd, *pmd; + kpmd = pmd_offset(&swapper_pg_dir[PTRS_PER_PGD-1], + (PTRS_PER_PMD - NR_SHARED_PMDS)*PMD_SIZE); + pmd = (pmd_t *)__pmd + (PTRS_PER_PMD - NR_SHARED_PMDS); + + memset(__pmd, 0, (PTRS_PER_PMD - NR_SHARED_PMDS)*sizeof(pmd_t)); + memcpy(pmd, kpmd, NR_SHARED_PMDS*sizeof(pmd_t)); +} + /* - * List of all pgd's needed for non-PAE so it can invalidate entries - * in both cached and uncached pgd's; not needed for PAE since the - * kernel pmd is shared. If PAE were not to share the pmd a similar - * tactic would be needed. This is essentially codepath-based locking + * List of all pgd's needed so it can invalidate entries in both cached + * and uncached pgd's. This is essentially codepath-based locking * against pageattr.c; it is the unique case in which a valid change * of kernel pagetables can't be lazily synchronized by vmalloc faults. * vmalloc faults work because attached pagetables are never freed. @@ -170,30 +180,60 @@ void pmd_ctor(void *pmd, kmem_cache_t *c * could be used. The locking scheme was chosen on the basis of * manfred's recommendations and having no core impact whatsoever. * -- wli + * + * The entire issue goes away when XKVA is configured. */ spinlock_t pgd_lock = SPIN_LOCK_UNLOCKED; LIST_HEAD(pgd_list); -void pgd_ctor(void *pgd, kmem_cache_t *cache, unsigned long unused) +/* + * This is not that hard to figure out. + * (a) PTRS_PER_PMD == 1 means non-PAE. + * (b) PTRS_PER_PMD > 1 means PAE. + * (c) TASK_SIZE > PAGE_OFFSET means XKVA. + * (d) TASK_SIZE <= PAGE_OFFSET means non-XKVA. + * + * Do *NOT* back out the preconstruction like the patch I'm cleaning + * up after this very instant did, or at all, for that matter. + * This is never called when PTRS_PER_PMD > 1 && TASK_SIZE > PAGE_OFFSET. + * -- wli + */ +void pgd_ctor(void *__pgd, kmem_cache_t *cache, unsigned long unused) { + pgd_t *pgd = (pgd_t *)__pgd; unsigned long flags; - if (PTRS_PER_PMD == 1) - spin_lock_irqsave(&pgd_lock, flags); + if (PTRS_PER_PMD == 1) { + if (TASK_SIZE <= PAGE_OFFSET) + spin_lock_irqsave(&pgd_lock, flags); + else + memcpy(&pgd[PTRS_PER_PGD - NR_SHARED_PMDS], + &swapper_pg_dir[PTRS_PER_PGD - NR_SHARED_PMDS], + NR_SHARED_PMDS * sizeof(pgd_t)); + } - memcpy((pgd_t *)pgd + USER_PTRS_PER_PGD, - swapper_pg_dir + USER_PTRS_PER_PGD, - (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t)); + if (TASK_SIZE <= PAGE_OFFSET) + memcpy(pgd + USER_PTRS_PER_PGD, + swapper_pg_dir + USER_PTRS_PER_PGD, + (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t)); if (PTRS_PER_PMD > 1) return; - list_add(&virt_to_page(pgd)->lru, &pgd_list); - spin_unlock_irqrestore(&pgd_lock, flags); - memset(pgd, 0, USER_PTRS_PER_PGD*sizeof(pgd_t)); + if (TASK_SIZE > PAGE_OFFSET) + memset(pgd, 0, (PTRS_PER_PGD - NR_SHARED_PMDS)*sizeof(pgd_t)); + else { + list_add(&virt_to_page(pgd)->lru, &pgd_list); + spin_unlock_irqrestore(&pgd_lock, flags); + memset(pgd, 0, USER_PTRS_PER_PGD*sizeof(pgd_t)); + } } -/* never called when PTRS_PER_PMD > 1 */ +/* + * Never called when PTRS_PER_PMD > 1 || TASK_SIZE > PAGE_OFFSET + * for with PAE we would list_del() multiple times, and for non-PAE + * with XKVA all the AGP pgd shootdown code is unnecessary. + */ void pgd_dtor(void *pgd, kmem_cache_t *cache, unsigned long unused) { unsigned long flags; /* can be called from interrupt context */ @@ -203,6 +243,12 @@ void pgd_dtor(void *pgd, kmem_cache_t *c spin_unlock_irqrestore(&pgd_lock, flags); } +/* + * See the comments above pgd_ctor() wrt. preconstruction. + * Do *NOT* memcpy() here. If you do, you back out important + * anti- cache pollution code. + * + */ pgd_t *pgd_alloc(struct mm_struct *mm) { int i; @@ -211,15 +257,33 @@ pgd_t *pgd_alloc(struct mm_struct *mm) if (PTRS_PER_PMD == 1 || !pgd) return pgd; + /* + * In the 4G userspace case alias the top 16 MB virtual + * memory range into the user mappings as well (these + * include the trampoline and CPU data structures). + */ for (i = 0; i < USER_PTRS_PER_PGD; ++i) { - pmd_t *pmd = kmem_cache_alloc(pmd_cache, GFP_KERNEL); + kmem_cache_t *cache; + pmd_t *pmd; + + if (TASK_SIZE > PAGE_OFFSET && i == USER_PTRS_PER_PGD - 1) + cache = kpmd_cache; + else + cache = pmd_cache; + + pmd = kmem_cache_alloc(cache, GFP_KERNEL); if (!pmd) goto out_oom; set_pgd(&pgd[i], __pgd(1 + __pa((u64)((u32)pmd)))); } - return pgd; + return pgd; out_oom: + /* + * we don't have to handle the kpmd_cache here, since it's the + * last allocation, and has either nothing to free or when it + * succeeds the whole operation succeeds. + */ for (i--; i >= 0; i--) kmem_cache_free(pmd_cache, (void *)__va(pgd_val(pgd[i])-1)); kmem_cache_free(pgd_cache, pgd); @@ -230,10 +294,29 @@ void pgd_free(pgd_t *pgd) { int i; - /* in the PAE case user pgd entries are overwritten before usage */ - if (PTRS_PER_PMD > 1) - for (i = 0; i < USER_PTRS_PER_PGD; ++i) - kmem_cache_free(pmd_cache, (void *)__va(pgd_val(pgd[i])-1)); /* in the non-PAE case, clear_page_tables() clears user pgd entries */ + if (PTRS_PER_PMD == 1) + goto out_free; + + /* in the PAE case user pgd entries are overwritten before usage */ + for (i = 0; i < USER_PTRS_PER_PGD; ++i) { + kmem_cache_t *cache; + pmd_t *pmd = __va(pgd_val(pgd[i]) - 1); + + /* + * only userspace pmd's are cleared for us + * by mm/memory.c; it's a slab cache invariant + * that we must separate the kernel pmd slab + * all times, else we'll have bad pmd's. + */ + if (TASK_SIZE > PAGE_OFFSET && i == USER_PTRS_PER_PGD - 1) + cache = kpmd_cache; + else + cache = pmd_cache; + + kmem_cache_free(cache, pmd); + } +out_free: kmem_cache_free(pgd_cache, pgd); } + --- linux-2.6.4-rc2/arch/i386/oprofile/op_model_p4.c 2003-08-22 19:23:40.000000000 -0700 +++ 25/arch/i386/oprofile/op_model_p4.c 2004-03-07 20:47:15.000000000 -0800 @@ -382,11 +382,8 @@ static struct p4_event_binding p4_events static unsigned int get_stagger(void) { #ifdef CONFIG_SMP - int cpu; - if (smp_num_siblings > 1) { - cpu = smp_processor_id(); - return (cpu_sibling_map[cpu] > cpu) ? 0 : 1; - } + int cpu = smp_processor_id(); + return (cpu != first_cpu(cpu_sibling_map[cpu])); #endif return 0; } --- linux-2.6.4-rc2/arch/i386/pci/common.c 2004-02-03 20:42:34.000000000 -0800 +++ 25/arch/i386/pci/common.c 2004-03-07 20:46:45.000000000 -0800 @@ -20,7 +20,8 @@ extern void pcibios_sort(void); #endif -unsigned int pci_probe = PCI_PROBE_BIOS | PCI_PROBE_CONF1 | PCI_PROBE_CONF2; +unsigned int pci_probe = PCI_PROBE_BIOS | PCI_PROBE_CONF1 | PCI_PROBE_CONF2 | + PCI_PROBE_MMCONF; int pcibios_last_bus = -1; struct pci_bus *pci_root_bus = NULL; @@ -198,6 +199,12 @@ char * __devinit pcibios_setup(char *st return NULL; } #endif +#ifdef CONFIG_PCI_MMCONFIG + else if (!strcmp(str, "nommconf")) { + pci_probe &= ~PCI_PROBE_MMCONF; + return NULL; + } +#endif else if (!strcmp(str, "noacpi")) { acpi_noirq_set(); return NULL; --- linux-2.6.4-rc2/arch/i386/pci/Makefile 2003-07-02 14:53:12.000000000 -0700 +++ 25/arch/i386/pci/Makefile 2004-03-07 20:46:45.000000000 -0800 @@ -1,6 +1,7 @@ obj-y := i386.o obj-$(CONFIG_PCI_BIOS) += pcbios.o +obj-$(CONFIG_PCI_MMCONFIG) += mmconfig.o obj-$(CONFIG_PCI_DIRECT) += direct.o pci-y := fixup.o --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/arch/i386/pci/mmconfig.c 2004-03-07 20:46:45.000000000 -0800 @@ -0,0 +1,109 @@ +/* + * mmconfig.c - Low-level direct PCI config space access via MMCONFIG + */ + +#include +#include +#include "pci.h" + +/* The physical address of the MMCONFIG aperture. Set from ACPI tables. */ +u32 pci_mmcfg_base_addr; + +#define mmcfg_virt_addr (fix_to_virt(FIX_PCIE_MCFG)) + +/* The base address of the last MMCONFIG device accessed */ +static u32 mmcfg_last_accessed_device; + +/* + * Functions for accessing PCI configuration space with MMCONFIG accesses + */ + +static inline void pci_exp_set_dev_base(int bus, int devfn) +{ + u32 dev_base = pci_mmcfg_base_addr | (bus << 20) | (devfn << 12); + if (dev_base != mmcfg_last_accessed_device) { + mmcfg_last_accessed_device = dev_base; + set_fixmap(FIX_PCIE_MCFG, dev_base); + } +} + +static int pci_mmcfg_read(int seg, int bus, int devfn, int reg, int len, u32 *value) +{ + unsigned long flags; + + if (!value || (bus > 255) || (devfn > 255) || (reg > 4095)) + return -EINVAL; + + spin_lock_irqsave(&pci_config_lock, flags); + + pci_exp_set_dev_base(bus, devfn); + + switch (len) { + case 1: + *value = readb(mmcfg_virt_addr + reg); + break; + case 2: + *value = readw(mmcfg_virt_addr + reg); + break; + case 4: + *value = readl(mmcfg_virt_addr + reg); + break; + } + + spin_unlock_irqrestore(&pci_config_lock, flags); + + return 0; +} + +static int pci_mmcfg_write(int seg, int bus, int devfn, int reg, int len, u32 value) +{ + unsigned long flags; + + if ((bus > 255) || (devfn > 255) || (reg > 4095)) + return -EINVAL; + + spin_lock_irqsave(&pci_config_lock, flags); + + pci_exp_set_dev_base(bus, devfn); + + switch (len) { + case 1: + writeb(value, mmcfg_virt_addr + reg); + break; + case 2: + writew(value, mmcfg_virt_addr + reg); + break; + case 4: + writel(value, mmcfg_virt_addr + reg); + break; + } + + /* Dummy read to flush PCI write */ + readl(mmcfg_virt_addr); + + spin_unlock_irqrestore(&pci_config_lock, flags); + + return 0; +} + +static struct pci_raw_ops pci_mmcfg = { + .read = pci_mmcfg_read, + .write = pci_mmcfg_write, +}; + +static int __init pci_mmcfg_init(void) +{ + if ((pci_probe & PCI_PROBE_MMCONF) == 0) + goto out; + if (!pci_mmcfg_base_addr) + goto out; + + printk(KERN_INFO "PCI: Using MMCONFIG\n"); + raw_pci_ops = &pci_mmcfg; + pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF; + + out: + return 0; +} + +arch_initcall(pci_mmcfg_init); --- linux-2.6.4-rc2/arch/i386/pci/pci.h 2004-02-03 20:42:34.000000000 -0800 +++ 25/arch/i386/pci/pci.h 2004-03-07 20:46:45.000000000 -0800 @@ -15,6 +15,9 @@ #define PCI_PROBE_BIOS 0x0001 #define PCI_PROBE_CONF1 0x0002 #define PCI_PROBE_CONF2 0x0004 +#define PCI_PROBE_MMCONF 0x0008 +#define PCI_PROBE_MASK 0x000f + #define PCI_NO_SORT 0x0100 #define PCI_BIOS_SORT 0x0200 #define PCI_NO_CHECKS 0x0400 --- linux-2.6.4-rc2/arch/ia64/ia32/sys_ia32.c 2004-03-03 23:12:42.000000000 -0800 +++ 25/arch/ia64/ia32/sys_ia32.c 2004-03-07 20:47:07.000000000 -0800 @@ -1023,143 +1023,6 @@ sys32_writev (int fd, struct compat_iove return ret; } -/* - * sys32_ipc() is the de-multiplexer for the SysV IPC calls in 32bit emulation.. - * - * This is really horribly ugly. - */ - -struct msgbuf32 { s32 mtype; char mtext[1]; }; - -struct ipc_perm32 { - key_t key; - compat_uid_t uid; - compat_gid_t gid; - compat_uid_t cuid; - compat_gid_t cgid; - compat_mode_t mode; - unsigned short seq; -}; - -struct ipc64_perm32 { - key_t key; - compat_uid32_t uid; - compat_gid32_t gid; - compat_uid32_t cuid; - compat_gid32_t cgid; - compat_mode_t mode; - unsigned short __pad1; - unsigned short seq; - unsigned short __pad2; - unsigned int unused1; - unsigned int unused2; -}; - -struct semid_ds32 { - struct ipc_perm32 sem_perm; /* permissions .. see ipc.h */ - compat_time_t sem_otime; /* last semop time */ - compat_time_t sem_ctime; /* last change time */ - u32 sem_base; /* ptr to first semaphore in array */ - u32 sem_pending; /* pending operations to be processed */ - u32 sem_pending_last; /* last pending operation */ - u32 undo; /* undo requests on this array */ - unsigned short sem_nsems; /* no. of semaphores in array */ -}; - -struct semid64_ds32 { - struct ipc64_perm32 sem_perm; - compat_time_t sem_otime; - unsigned int __unused1; - compat_time_t sem_ctime; - unsigned int __unused2; - unsigned int sem_nsems; - unsigned int __unused3; - unsigned int __unused4; -}; - -struct msqid_ds32 { - struct ipc_perm32 msg_perm; - u32 msg_first; - u32 msg_last; - compat_time_t msg_stime; - compat_time_t msg_rtime; - compat_time_t msg_ctime; - u32 wwait; - u32 rwait; - unsigned short msg_cbytes; - unsigned short msg_qnum; - unsigned short msg_qbytes; - compat_ipc_pid_t msg_lspid; - compat_ipc_pid_t msg_lrpid; -}; - -struct msqid64_ds32 { - struct ipc64_perm32 msg_perm; - compat_time_t msg_stime; - unsigned int __unused1; - compat_time_t msg_rtime; - unsigned int __unused2; - compat_time_t msg_ctime; - unsigned int __unused3; - unsigned int msg_cbytes; - unsigned int msg_qnum; - unsigned int msg_qbytes; - compat_pid_t msg_lspid; - compat_pid_t msg_lrpid; - unsigned int __unused4; - unsigned int __unused5; -}; - -struct shmid_ds32 { - struct ipc_perm32 shm_perm; - int shm_segsz; - compat_time_t shm_atime; - compat_time_t shm_dtime; - compat_time_t shm_ctime; - compat_ipc_pid_t shm_cpid; - compat_ipc_pid_t shm_lpid; - unsigned short shm_nattch; -}; - -struct shmid64_ds32 { - struct ipc64_perm32 shm_perm; - compat_size_t shm_segsz; - compat_time_t shm_atime; - unsigned int __unused1; - compat_time_t shm_dtime; - unsigned int __unused2; - compat_time_t shm_ctime; - unsigned int __unused3; - compat_pid_t shm_cpid; - compat_pid_t shm_lpid; - unsigned int shm_nattch; - unsigned int __unused4; - unsigned int __unused5; -}; - -struct shminfo64_32 { - unsigned int shmmax; - unsigned int shmmin; - unsigned int shmmni; - unsigned int shmseg; - unsigned int shmall; - unsigned int __unused1; - unsigned int __unused2; - unsigned int __unused3; - unsigned int __unused4; -}; - -struct shm_info32 { - int used_ids; - u32 shm_tot, shm_rss, shm_swp; - u32 swap_attempts, swap_successes; -}; - -struct ipc_kludge { - u32 msgp; - s32 msgtyp; -}; - #define SEMOP 1 #define SEMGET 2 #define SEMCTL 3 @@ -1173,454 +1036,6 @@ struct ipc_kludge { #define SHMGET 23 #define SHMCTL 24 -#define IPCOP_MASK(__x) (1UL << (__x)) - -static int -ipc_parse_version32 (int *cmd) -{ - if (*cmd & IPC_64) { - *cmd ^= IPC_64; - return IPC_64; - } else { - return IPC_OLD; - } -} - -static int -semctl32 (int first, int second, int third, void *uptr) -{ - union semun fourth; - u32 pad; - int err = 0, err2; - struct semid64_ds s; - mm_segment_t old_fs; - int version = ipc_parse_version32(&third); - - if (!uptr) - return -EINVAL; - if (get_user(pad, (u32 *)uptr)) - return -EFAULT; - if (third == SETVAL) - fourth.val = (int)pad; - else - fourth.__pad = (void *)A(pad); - switch (third) { - default: - err = -EINVAL; - break; - - case IPC_INFO: - case IPC_RMID: - case IPC_SET: - case SEM_INFO: - case GETVAL: - case GETPID: - case GETNCNT: - case GETZCNT: - case GETALL: - case SETVAL: - case SETALL: - err = sys_semctl(first, second, third, fourth); - break; - - case IPC_STAT: - case SEM_STAT: - fourth.__pad = &s; - old_fs = get_fs(); - set_fs(KERNEL_DS); - err = sys_semctl(first, second, third, fourth); - set_fs(old_fs); - - if (version == IPC_64) { - struct semid64_ds32 *usp64 = (struct semid64_ds32 *) A(pad); - - if (!access_ok(VERIFY_WRITE, usp64, sizeof(*usp64))) { - err = -EFAULT; - break; - } - err2 = __put_user(s.sem_perm.key, &usp64->sem_perm.key); - err2 |= __put_user(s.sem_perm.uid, &usp64->sem_perm.uid); - err2 |= __put_user(s.sem_perm.gid, &usp64->sem_perm.gid); - err2 |= __put_user(s.sem_perm.cuid, &usp64->sem_perm.cuid); - err2 |= __put_user(s.sem_perm.cgid, &usp64->sem_perm.cgid); - err2 |= __put_user(s.sem_perm.mode, &usp64->sem_perm.mode); - err2 |= __put_user(s.sem_perm.seq, &usp64->sem_perm.seq); - err2 |= __put_user(s.sem_otime, &usp64->sem_otime); - err2 |= __put_user(s.sem_ctime, &usp64->sem_ctime); - err2 |= __put_user(s.sem_nsems, &usp64->sem_nsems); - } else { - struct semid_ds32 *usp32 = (struct semid_ds32 *) A(pad); - - if (!access_ok(VERIFY_WRITE, usp32, sizeof(*usp32))) { - err = -EFAULT; - break; - } - err2 = __put_user(s.sem_perm.key, &usp32->sem_perm.key); - err2 |= __put_user(s.sem_perm.uid, &usp32->sem_perm.uid); - err2 |= __put_user(s.sem_perm.gid, &usp32->sem_perm.gid); - err2 |= __put_user(s.sem_perm.cuid, &usp32->sem_perm.cuid); - err2 |= __put_user(s.sem_perm.cgid, &usp32->sem_perm.cgid); - err2 |= __put_user(s.sem_perm.mode, &usp32->sem_perm.mode); - err2 |= __put_user(s.sem_perm.seq, &usp32->sem_perm.seq); - err2 |= __put_user(s.sem_otime, &usp32->sem_otime); - err2 |= __put_user(s.sem_ctime, &usp32->sem_ctime); - err2 |= __put_user(s.sem_nsems, &usp32->sem_nsems); - } - if (err2) - err = -EFAULT; - break; - } - return err; -} - -static int -do_sys32_msgsnd (int first, int second, int third, void *uptr) -{ - struct msgbuf *p = kmalloc(second + sizeof(struct msgbuf), GFP_USER); - struct msgbuf32 *up = (struct msgbuf32 *)uptr; - mm_segment_t old_fs; - int err; - - if (!p) - return -ENOMEM; - err = get_user(p->mtype, &up->mtype); - err |= copy_from_user(p->mtext, &up->mtext, second); - if (err) - goto out; - old_fs = get_fs(); - set_fs(KERNEL_DS); - err = sys_msgsnd(first, p, second, third); - set_fs(old_fs); - out: - kfree(p); - return err; -} - -static int -do_sys32_msgrcv (int first, int second, int msgtyp, int third, int version, void *uptr) -{ - struct msgbuf32 *up; - struct msgbuf *p; - mm_segment_t old_fs; - int err; - - if (!version) { - struct ipc_kludge *uipck = (struct ipc_kludge *)uptr; - struct ipc_kludge ipck; - - err = -EINVAL; - if (!uptr) - goto out; - err = -EFAULT; - if (copy_from_user(&ipck, uipck, sizeof(struct ipc_kludge))) - goto out; - uptr = (void *)A(ipck.msgp); - msgtyp = ipck.msgtyp; - } - err = -ENOMEM; - p = kmalloc(second + sizeof(struct msgbuf), GFP_USER); - if (!p) - goto out; - old_fs = get_fs(); - set_fs(KERNEL_DS); - err = sys_msgrcv(first, p, second, msgtyp, third); - set_fs(old_fs); - if (err < 0) - goto free_then_out; - up = (struct msgbuf32 *)uptr; - if (put_user(p->mtype, &up->mtype) || copy_to_user(&up->mtext, p->mtext, err)) - err = -EFAULT; -free_then_out: - kfree(p); -out: - return err; -} - -static int -msgctl32 (int first, int second, void *uptr) -{ - int err = -EINVAL, err2; - struct msqid64_ds m64; - struct msqid_ds32 *up32 = (struct msqid_ds32 *)uptr; - struct msqid64_ds32 *up64 = (struct msqid64_ds32 *)uptr; - mm_segment_t old_fs; - int version = ipc_parse_version32(&second); - - switch (second) { - case IPC_INFO: - case IPC_RMID: - case MSG_INFO: - err = sys_msgctl(first, second, (struct msqid_ds *)uptr); - break; - - case IPC_SET: - if (version == IPC_64) { - err = get_user(m64.msg_perm.uid, &up64->msg_perm.uid); - err |= get_user(m64.msg_perm.gid, &up64->msg_perm.gid); - err |= get_user(m64.msg_perm.mode, &up64->msg_perm.mode); - err |= get_user(m64.msg_qbytes, &up64->msg_qbytes); - } else { - err = get_user(m64.msg_perm.uid, &up32->msg_perm.uid); - err |= get_user(m64.msg_perm.gid, &up32->msg_perm.gid); - err |= get_user(m64.msg_perm.mode, &up32->msg_perm.mode); - err |= get_user(m64.msg_qbytes, &up32->msg_qbytes); - } - if (err) - break; - old_fs = get_fs(); - set_fs(KERNEL_DS); - err = sys_msgctl(first, second, (struct msqid_ds *)&m64); - set_fs(old_fs); - break; - - case IPC_STAT: - case MSG_STAT: - old_fs = get_fs(); - set_fs(KERNEL_DS); - err = sys_msgctl(first, second, (struct msqid_ds *)&m64); - set_fs(old_fs); - - if (version == IPC_64) { - if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64))) { - err = -EFAULT; - break; - } - err2 = __put_user(m64.msg_perm.key, &up64->msg_perm.key); - err2 |= __put_user(m64.msg_perm.uid, &up64->msg_perm.uid); - err2 |= __put_user(m64.msg_perm.gid, &up64->msg_perm.gid); - err2 |= __put_user(m64.msg_perm.cuid, &up64->msg_perm.cuid); - err2 |= __put_user(m64.msg_perm.cgid, &up64->msg_perm.cgid); - err2 |= __put_user(m64.msg_perm.mode, &up64->msg_perm.mode); - err2 |= __put_user(m64.msg_perm.seq, &up64->msg_perm.seq); - err2 |= __put_user(m64.msg_stime, &up64->msg_stime); - err2 |= __put_user(m64.msg_rtime, &up64->msg_rtime); - err2 |= __put_user(m64.msg_ctime, &up64->msg_ctime); - err2 |= __put_user(m64.msg_cbytes, &up64->msg_cbytes); - err2 |= __put_user(m64.msg_qnum, &up64->msg_qnum); - err2 |= __put_user(m64.msg_qbytes, &up64->msg_qbytes); - err2 |= __put_user(m64.msg_lspid, &up64->msg_lspid); - err2 |= __put_user(m64.msg_lrpid, &up64->msg_lrpid); - if (err2) - err = -EFAULT; - } else { - if (!access_ok(VERIFY_WRITE, up32, sizeof(*up32))) { - err = -EFAULT; - break; - } - err2 = __put_user(m64.msg_perm.key, &up32->msg_perm.key); - err2 |= __put_user(m64.msg_perm.uid, &up32->msg_perm.uid); - err2 |= __put_user(m64.msg_perm.gid, &up32->msg_perm.gid); - err2 |= __put_user(m64.msg_perm.cuid, &up32->msg_perm.cuid); - err2 |= __put_user(m64.msg_perm.cgid, &up32->msg_perm.cgid); - err2 |= __put_user(m64.msg_perm.mode, &up32->msg_perm.mode); - err2 |= __put_user(m64.msg_perm.seq, &up32->msg_perm.seq); - err2 |= __put_user(m64.msg_stime, &up32->msg_stime); - err2 |= __put_user(m64.msg_rtime, &up32->msg_rtime); - err2 |= __put_user(m64.msg_ctime, &up32->msg_ctime); - err2 |= __put_user(m64.msg_cbytes, &up32->msg_cbytes); - err2 |= __put_user(m64.msg_qnum, &up32->msg_qnum); - err2 |= __put_user(m64.msg_qbytes, &up32->msg_qbytes); - err2 |= __put_user(m64.msg_lspid, &up32->msg_lspid); - err2 |= __put_user(m64.msg_lrpid, &up32->msg_lrpid); - if (err2) - err = -EFAULT; - } - break; - } - return err; -} - -static int -shmat32 (int first, int second, int third, int version, void *uptr) -{ - unsigned long raddr; - u32 *uaddr = (u32 *)A((u32)third); - int err; - - if (version == 1) - return -EINVAL; /* iBCS2 emulator entry point: unsupported */ - err = do_shmat(first, uptr, second, &raddr); - if (err) - return err; - return put_user(raddr, uaddr); -} - -static int -shmctl32 (int first, int second, void *uptr) -{ - int err = -EFAULT, err2; - - struct shmid64_ds s64; - struct shmid_ds32 *up32 = (struct shmid_ds32 *)uptr; - struct shmid64_ds32 *up64 = (struct shmid64_ds32 *)uptr; - mm_segment_t old_fs; - struct shm_info32 *uip = (struct shm_info32 *)uptr; - struct shm_info si; - int version = ipc_parse_version32(&second); - struct shminfo64 smi; - struct shminfo *usi32 = (struct shminfo *) uptr; - struct shminfo64_32 *usi64 = (struct shminfo64_32 *) uptr; - - switch (second) { - case IPC_INFO: - old_fs = get_fs(); - set_fs(KERNEL_DS); - err = sys_shmctl(first, second, (struct shmid_ds *)&smi); - set_fs(old_fs); - - if (version == IPC_64) { - if (!access_ok(VERIFY_WRITE, usi64, sizeof(*usi64))) { - err = -EFAULT; - break; - } - err2 = __put_user(smi.shmmax, &usi64->shmmax); - err2 |= __put_user(smi.shmmin, &usi64->shmmin); - err2 |= __put_user(smi.shmmni, &usi64->shmmni); - err2 |= __put_user(smi.shmseg, &usi64->shmseg); - err2 |= __put_user(smi.shmall, &usi64->shmall); - } else { - if (!access_ok(VERIFY_WRITE, usi32, sizeof(*usi32))) { - err = -EFAULT; - break; - } - err2 = __put_user(smi.shmmax, &usi32->shmmax); - err2 |= __put_user(smi.shmmin, &usi32->shmmin); - err2 |= __put_user(smi.shmmni, &usi32->shmmni); - err2 |= __put_user(smi.shmseg, &usi32->shmseg); - err2 |= __put_user(smi.shmall, &usi32->shmall); - } - if (err2) - err = -EFAULT; - break; - - case IPC_RMID: - case SHM_LOCK: - case SHM_UNLOCK: - err = sys_shmctl(first, second, (struct shmid_ds *)uptr); - break; - - case IPC_SET: - if (version == IPC_64) { - err = get_user(s64.shm_perm.uid, &up64->shm_perm.uid); - err |= get_user(s64.shm_perm.gid, &up64->shm_perm.gid); - err |= get_user(s64.shm_perm.mode, &up64->shm_perm.mode); - } else { - err = get_user(s64.shm_perm.uid, &up32->shm_perm.uid); - err |= get_user(s64.shm_perm.gid, &up32->shm_perm.gid); - err |= get_user(s64.shm_perm.mode, &up32->shm_perm.mode); - } - if (err) - break; - old_fs = get_fs(); - set_fs(KERNEL_DS); - err = sys_shmctl(first, second, (struct shmid_ds *)&s64); - set_fs(old_fs); - break; - - case IPC_STAT: - case SHM_STAT: - old_fs = get_fs(); - set_fs(KERNEL_DS); - err = sys_shmctl(first, second, (struct shmid_ds *)&s64); - set_fs(old_fs); - if (err < 0) - break; - if (version == IPC_64) { - if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64))) { - err = -EFAULT; - break; - } - err2 = __put_user(s64.shm_perm.key, &up64->shm_perm.key); - err2 |= __put_user(s64.shm_perm.uid, &up64->shm_perm.uid); - err2 |= __put_user(s64.shm_perm.gid, &up64->shm_perm.gid); - err2 |= __put_user(s64.shm_perm.cuid, &up64->shm_perm.cuid); - err2 |= __put_user(s64.shm_perm.cgid, &up64->shm_perm.cgid); - err2 |= __put_user(s64.shm_perm.mode, &up64->shm_perm.mode); - err2 |= __put_user(s64.shm_perm.seq, &up64->shm_perm.seq); - err2 |= __put_user(s64.shm_atime, &up64->shm_atime); - err2 |= __put_user(s64.shm_dtime, &up64->shm_dtime); - err2 |= __put_user(s64.shm_ctime, &up64->shm_ctime); - err2 |= __put_user(s64.shm_segsz, &up64->shm_segsz); - err2 |= __put_user(s64.shm_nattch, &up64->shm_nattch); - err2 |= __put_user(s64.shm_cpid, &up64->shm_cpid); - err2 |= __put_user(s64.shm_lpid, &up64->shm_lpid); - } else { - if (!access_ok(VERIFY_WRITE, up32, sizeof(*up32))) { - err = -EFAULT; - break; - } - err2 = __put_user(s64.shm_perm.key, &up32->shm_perm.key); - err2 |= __put_user(s64.shm_perm.uid, &up32->shm_perm.uid); - err2 |= __put_user(s64.shm_perm.gid, &up32->shm_perm.gid); - err2 |= __put_user(s64.shm_perm.cuid, &up32->shm_perm.cuid); - err2 |= __put_user(s64.shm_perm.cgid, &up32->shm_perm.cgid); - err2 |= __put_user(s64.shm_perm.mode, &up32->shm_perm.mode); - err2 |= __put_user(s64.shm_perm.seq, &up32->shm_perm.seq); - err2 |= __put_user(s64.shm_atime, &up32->shm_atime); - err2 |= __put_user(s64.shm_dtime, &up32->shm_dtime); - err2 |= __put_user(s64.shm_ctime, &up32->shm_ctime); - err2 |= __put_user(s64.shm_segsz, &up32->shm_segsz); - err2 |= __put_user(s64.shm_nattch, &up32->shm_nattch); - err2 |= __put_user(s64.shm_cpid, &up32->shm_cpid); - err2 |= __put_user(s64.shm_lpid, &up32->shm_lpid); - } - if (err2) - err = -EFAULT; - break; - - case SHM_INFO: - old_fs = get_fs(); - set_fs(KERNEL_DS); - err = sys_shmctl(first, second, (void *)&si); - set_fs(old_fs); - if (err < 0) - break; - - if (!access_ok(VERIFY_WRITE, uip, sizeof(*uip))) { - err = -EFAULT; - break; - } - err2 = __put_user(si.used_ids, &uip->used_ids); - err2 |= __put_user(si.shm_tot, &uip->shm_tot); - err2 |= __put_user(si.shm_rss, &uip->shm_rss); - err2 |= __put_user(si.shm_swp, &uip->shm_swp); - err2 |= __put_user(si.swap_attempts, &uip->swap_attempts); - err2 |= __put_user(si.swap_successes, &uip->swap_successes); - if (err2) - err = -EFAULT; - break; - - } - return err; -} - -extern int sem_ctls[]; -#define sc_semopm (sem_ctls[2]) - -static long -semtimedop32(int semid, struct sembuf *tsops, int nsops, - struct compat_timespec *timeout32) -{ - struct timespec t; - mm_segment_t oldfs; - long ret; - - /* parameter checking precedence should mirror sys_semtimedop() */ - if (nsops < 1 || semid < 0) - return -EINVAL; - if (nsops > sc_semopm) - return -E2BIG; - if (!access_ok(VERIFY_READ, tsops, nsops * sizeof(struct sembuf)) || - get_compat_timespec(&t, timeout32)) - return -EFAULT; - - oldfs = get_fs(); - set_fs(KERNEL_DS); - ret = sys_semtimedop(semid, tsops, nsops, &t); - set_fs(oldfs); - return ret; -} - asmlinkage long sys32_ipc(u32 call, int first, int second, int third, u32 ptr, u32 fifth) { @@ -1632,36 +1047,36 @@ sys32_ipc(u32 call, int first, int secon switch (call) { case SEMTIMEDOP: if (fifth) - return semtimedop32(first, (struct sembuf *)AA(ptr), - second, (struct compat_timespec *)AA(fifth)); + return compat_sys_semtimedop(first, compat_ptr(ptr), + second, compat_ptr(fifth)); /* else fall through for normal semop() */ case SEMOP: /* struct sembuf is the same on 32 and 64bit :)) */ - return sys_semtimedop(first, (struct sembuf *)AA(ptr), second, + return sys_semtimedop(first, compat_ptr(ptr), second, NULL); case SEMGET: return sys_semget(first, second, third); case SEMCTL: - return semctl32(first, second, third, (void *)AA(ptr)); + return compat_sys_semctl(first, second, third, compat_ptr(ptr)); case MSGSND: - return do_sys32_msgsnd(first, second, third, (void *)AA(ptr)); + return compat_sys_msgsnd(first, second, third, compat_ptr(ptr)); case MSGRCV: - return do_sys32_msgrcv(first, second, fifth, third, version, (void *)AA(ptr)); + return compat_sys_msgrcv(first, second, fifth, third, version, compat_ptr(ptr)); case MSGGET: return sys_msgget((key_t) first, second); case MSGCTL: - return msgctl32(first, second, (void *)AA(ptr)); + return compat_sys_msgctl(first, second, compat_ptr(ptr)); case SHMAT: - return shmat32(first, second, third, version, (void *)AA(ptr)); + return compat_sys_shmat(first, second, third, version, compat_ptr(ptr)); break; case SHMDT: - return sys_shmdt((char *)AA(ptr)); + return sys_shmdt(compat_ptr(ptr)); case SHMGET: return sys_shmget(first, second, third); case SHMCTL: - return shmctl32(first, second, (void *)AA(ptr)); + return compat_sys_shmctl(first, second, compat_ptr(ptr)); default: return -ENOSYS; --- linux-2.6.4-rc2/arch/ia64/Kconfig 2004-03-03 23:12:42.000000000 -0800 +++ 25/arch/ia64/Kconfig 2004-03-07 20:48:18.000000000 -0800 @@ -616,7 +616,18 @@ config DEBUG_INFO debugging info resulting in a larger kernel image. Say Y here only if you plan to use gdb to debug the kernel. If you don't debug the kernel, you can say N. - + +config LOCKMETER + bool "Kernel lock metering" + depends on SMP + help + Say Y to enable kernel lock metering, which adds overhead to SMP locks, + but allows you to see various statistics using the lockstat command. + +config SYSVIPC_COMPAT + bool + depends on COMPAT && SYSVIPC + default y endmenu source "security/Kconfig" --- linux-2.6.4-rc2/arch/ia64/lib/dec_and_lock.c 2004-01-09 00:04:31.000000000 -0800 +++ 25/arch/ia64/lib/dec_and_lock.c 2004-03-07 20:48:17.000000000 -0800 @@ -13,6 +13,7 @@ #include #include +#ifndef CONFIG_LOCKMETER /* * Decrement REFCOUNT and if the count reaches zero, acquire the spinlock. Both of these * operations have to be done atomically, so that the count doesn't drop to zero without @@ -40,3 +41,4 @@ atomic_dec_and_lock (atomic_t *refcount, } EXPORT_SYMBOL(atomic_dec_and_lock); +#endif --- linux-2.6.4-rc2/arch/ia64/lib/swiotlb.c 2003-06-14 12:18:29.000000000 -0700 +++ 25/arch/ia64/lib/swiotlb.c 2004-03-07 20:46:58.000000000 -0800 @@ -47,7 +47,7 @@ #define IO_TLB_SHIFT 11 /* - * Used to do a quick range check in swiotlb_unmap_single and swiotlb_sync_single, to see + * Used to do a quick range check in swiotlb_unmap_single and swiotlb_sync_single_*, to see * if the memory was in fact allocated by this API. */ static char *io_tlb_start, *io_tlb_end; @@ -381,11 +381,24 @@ swiotlb_unmap_single (struct device *hwd * * If you perform a swiotlb_map_single() but wish to interrogate the buffer using the cpu, * yet do not wish to teardown the PCI dma mapping, you must call this function before - * doing so. At the next point you give the PCI dma address back to the card, the device - * again owns the buffer. + * doing so. At the next point you give the PCI dma address back to the card, you must + * first perform a swiotlb_dma_sync_for_device, and then the device again owns the buffer */ void -swiotlb_sync_single (struct device *hwdev, dma_addr_t dev_addr, size_t size, int dir) +swiotlb_sync_single_for_cpu (struct device *hwdev, dma_addr_t dev_addr, size_t size, int dir) +{ + char *dma_addr = phys_to_virt(dev_addr); + + if (dir == DMA_NONE) + BUG(); + if (dma_addr >= io_tlb_start && dma_addr < io_tlb_end) + sync_single(hwdev, dma_addr, size, dir); + else if (dir == DMA_FROM_DEVICE) + mark_clean(dma_addr, size); +} + +void +swiotlb_sync_single_for_device (struct device *hwdev, dma_addr_t dev_addr, size_t size, int dir) { char *dma_addr = phys_to_virt(dev_addr); @@ -456,11 +469,24 @@ swiotlb_unmap_sg (struct device *hwdev, * Make physical memory consistent for a set of streaming mode DMA translations after a * transfer. * - * The same as swiotlb_dma_sync_single but for a scatter-gather list, same rules and + * The same as swiotlb_sync_single_* but for a scatter-gather list, same rules and * usage. */ void -swiotlb_sync_sg (struct device *hwdev, struct scatterlist *sg, int nelems, int dir) +swiotlb_sync_sg_for_cpu (struct device *hwdev, struct scatterlist *sg, int nelems, int dir) +{ + int i; + + if (dir == DMA_NONE) + BUG(); + + for (i = 0; i < nelems; i++, sg++) + if (sg->dma_address != SG_ENT_PHYS_ADDRESS(sg)) + sync_single(hwdev, (void *) sg->dma_address, sg->dma_length, dir); +} + +void +swiotlb_sync_sg_for_device (struct device *hwdev, struct scatterlist *sg, int nelems, int dir) { int i; @@ -488,8 +514,10 @@ EXPORT_SYMBOL(swiotlb_map_single); EXPORT_SYMBOL(swiotlb_unmap_single); EXPORT_SYMBOL(swiotlb_map_sg); EXPORT_SYMBOL(swiotlb_unmap_sg); -EXPORT_SYMBOL(swiotlb_sync_single); -EXPORT_SYMBOL(swiotlb_sync_sg); +EXPORT_SYMBOL(swiotlb_sync_single_for_cpu); +EXPORT_SYMBOL(swiotlb_sync_single_for_device); +EXPORT_SYMBOL(swiotlb_sync_sg_for_cpu); +EXPORT_SYMBOL(swiotlb_sync_sg_for_device); EXPORT_SYMBOL(swiotlb_alloc_coherent); EXPORT_SYMBOL(swiotlb_free_coherent); EXPORT_SYMBOL(swiotlb_dma_supported); --- linux-2.6.4-rc2/arch/ia64/sn/io/machvec/pci_dma.c 2004-03-03 23:12:42.000000000 -0800 +++ 25/arch/ia64/sn/io/machvec/pci_dma.c 2004-03-07 20:47:01.000000000 -0800 @@ -152,7 +152,7 @@ sn_pci_alloc_consistent(struct pci_dev * * pcibr_dmatrans_addr ignores a missing PCIIO_DMA_A64 flag on * PCI-X buses. */ - if (hwdev->consistent_dma_mask == ~0UL) + if (hwdev->dev.coherent_dma_mask == ~0UL) *dma_handle = pcibr_dmatrans_addr(vhdl, NULL, phys_addr, size, PCIIO_DMA_CMD | PCIIO_DMA_A64); else { @@ -169,7 +169,7 @@ sn_pci_alloc_consistent(struct pci_dev * } } - if (!*dma_handle || *dma_handle > hwdev->consistent_dma_mask) { + if (!*dma_handle || *dma_handle > hwdev->dev.coherent_dma_mask) { if (dma_map) { pcibr_dmamap_done(dma_map); pcibr_dmamap_free(dma_map); @@ -437,7 +437,8 @@ sn_pci_unmap_single(struct pci_dev *hwde } /** - * sn_pci_dma_sync_single - make sure all DMAs have completed + * sn_pci_dma_sync_single_* - make sure all DMAs or CPU accesses + * have completed * @hwdev: device to sync * @dma_handle: DMA address to sync * @size: size of region @@ -448,14 +449,19 @@ sn_pci_unmap_single(struct pci_dev *hwde * anything on our platform. */ void -sn_pci_dma_sync_single(struct pci_dev *hwdev, dma_addr_t dma_handle, size_t size, int direction) +sn_pci_dma_sync_single_for_cpu(struct pci_dev *hwdev, dma_addr_t dma_handle, size_t size, int direction) { return; +} +void +sn_pci_dma_sync_single_for_device(struct pci_dev *hwdev, dma_addr_t dma_handle, size_t size, int direction) +{ + return; } /** - * sn_pci_dma_sync_sg - make sure all DMAs have completed + * sn_pci_dma_sync_sg_* - make sure all DMAs or CPU accesses have completed * @hwdev: device to sync * @sg: scatterlist to sync * @nents: number of entries in the scatterlist @@ -466,10 +472,15 @@ sn_pci_dma_sync_single(struct pci_dev *h * on our platform. */ void -sn_pci_dma_sync_sg(struct pci_dev *hwdev, struct scatterlist *sg, int nents, int direction) +sn_pci_dma_sync_sg_for_cpu(struct pci_dev *hwdev, struct scatterlist *sg, int nents, int direction) { return; +} +void +sn_pci_dma_sync_sg_for_device(struct pci_dev *hwdev, struct scatterlist *sg, int nents, int direction) +{ + return; } /** @@ -602,28 +613,51 @@ sn_dma_unmap_sg(struct device *dev, stru EXPORT_SYMBOL(sn_dma_unmap_sg); void -sn_dma_sync_single(struct device *dev, dma_addr_t dma_handle, size_t size, +sn_dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, size_t size, + int direction) +{ + BUG_ON(dev->bus != &pci_bus_type); + + sn_pci_dma_sync_single_for_cpu(to_pci_dev(dev), dma_handle, size, (int)direction); +} +EXPORT_SYMBOL(sn_dma_sync_single_for_cpu); + +void +sn_dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, size_t size, int direction) { BUG_ON(dev->bus != &pci_bus_type); - sn_pci_dma_sync_single(to_pci_dev(dev), dma_handle, size, (int)direction); + sn_pci_dma_sync_single_for_device(to_pci_dev(dev), dma_handle, size, (int)direction); +} +EXPORT_SYMBOL(sn_dma_sync_single_for_device); + +void +sn_dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems, + int direction) +{ + BUG_ON(dev->bus != &pci_bus_type); + + sn_pci_dma_sync_sg_for_cpu(to_pci_dev(dev), sg, nelems, (int)direction); } -EXPORT_SYMBOL(sn_dma_sync_single); +EXPORT_SYMBOL(sn_dma_sync_sg_for_cpu); void -sn_dma_sync_sg(struct device *dev, struct scatterlist *sg, int nelems, +sn_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems, int direction) { BUG_ON(dev->bus != &pci_bus_type); - sn_pci_dma_sync_sg(to_pci_dev(dev), sg, nelems, (int)direction); + sn_pci_dma_sync_sg_for_device(to_pci_dev(dev), sg, nelems, (int)direction); } -EXPORT_SYMBOL(sn_dma_sync_sg); +EXPORT_SYMBOL(sn_dma_sync_sg_for_device); EXPORT_SYMBOL(sn_pci_unmap_single); EXPORT_SYMBOL(sn_pci_map_single); -EXPORT_SYMBOL(sn_pci_dma_sync_single); +EXPORT_SYMBOL(sn_pci_dma_sync_single_for_cpu); +EXPORT_SYMBOL(sn_pci_dma_sync_single_for_device); +EXPORT_SYMBOL(sn_pci_dma_sync_sg_for_cpu); +EXPORT_SYMBOL(sn_pci_dma_sync_sg_for_device); EXPORT_SYMBOL(sn_pci_map_sg); EXPORT_SYMBOL(sn_pci_unmap_sg); EXPORT_SYMBOL(sn_pci_alloc_consistent); --- linux-2.6.4-rc2/arch/m68k/amiga/amiints.c 2003-06-14 12:18:52.000000000 -0700 +++ 25/arch/m68k/amiga/amiints.c 2004-03-07 20:47:58.000000000 -0800 @@ -197,7 +197,7 @@ int amiga_request_irq(unsigned int irq, } if (irq >= IRQ_AMIGA_AUTO) - return sys_request_irq(irq - IRQ_AMIGA_AUTO, handler, + return cpu_request_irq(irq - IRQ_AMIGA_AUTO, handler, flags, devname, dev_id); if (irq >= IRQ_AMIGA_CIAB) @@ -244,7 +244,7 @@ void amiga_free_irq(unsigned int irq, vo } if (irq >= IRQ_AMIGA_AUTO) - sys_free_irq(irq - IRQ_AMIGA_AUTO, dev_id); + cpu_free_irq(irq - IRQ_AMIGA_AUTO, dev_id); if (irq >= IRQ_AMIGA_CIAB) { cia_free_irq(&ciab_base, irq - IRQ_AMIGA_CIAB, dev_id); --- linux-2.6.4-rc2/arch/m68k/bvme6000/bvmeints.c 2003-06-14 12:17:58.000000000 -0700 +++ 25/arch/m68k/bvme6000/bvmeints.c 2004-03-07 20:47:58.000000000 -0800 @@ -73,7 +73,7 @@ int bvme6000_request_irq(unsigned int ir */ if (irq >= VEC_INT1 && irq <= VEC_INT7) - return sys_request_irq(irq - VEC_SPUR, handler, flags, + return cpu_request_irq(irq - VEC_SPUR, handler, flags, devname, dev_id); #endif if (!(irq_tab[irq].flags & IRQ_FLG_STD)) { @@ -103,7 +103,7 @@ void bvme6000_free_irq(unsigned int irq, } #if 0 if (irq >= VEC_INT1 && irq <= VEC_INT7) { - sys_free_irq(irq - VEC_SPUR, dev_id); + cpu_free_irq(irq - VEC_SPUR, dev_id); return; } #endif --- linux-2.6.4-rc2/arch/m68k/hp300/time.c 2003-06-14 12:18:29.000000000 -0700 +++ 25/arch/m68k/hp300/time.c 2004-03-07 20:47:58.000000000 -0800 @@ -68,7 +68,7 @@ void __init hp300_sched_init(irqreturn_t asm volatile(" movpw %0,%1@(5)" : : "d" (INTVAL), "a" (CLOCKBASE)); - sys_request_irq(6, hp300_tick, IRQ_FLG_STD, "timer tick", vector); + cpu_request_irq(6, hp300_tick, IRQ_FLG_STD, "timer tick", vector); out_8(CLOCKBASE + CLKCR2, 0x1); /* select CR1 */ out_8(CLOCKBASE + CLKCR1, 0x40); /* enable irq */ --- linux-2.6.4-rc2/arch/m68k/kernel/entry.S 2004-03-03 23:12:42.000000000 -0800 +++ 25/arch/m68k/kernel/entry.S 2004-03-07 20:47:07.000000000 -0800 @@ -528,7 +528,7 @@ sys_call_table: .long sys_ni_syscall /* old profil syscall holder */ .long sys_statfs .long sys_fstatfs /* 100 */ - .long sys_ioperm + .long sys_ni_syscall /* ioperm for i386 */ .long sys_socketcall .long sys_syslog .long sys_setitimer --- linux-2.6.4-rc2/arch/m68k/kernel/ints.c 2004-01-09 00:04:31.000000000 -0800 +++ 25/arch/m68k/kernel/ints.c 2004-03-07 20:47:58.000000000 -0800 @@ -137,8 +137,8 @@ void free_irq(unsigned int irq, void *de EXPORT_SYMBOL(free_irq); -int sys_request_irq(unsigned int irq, - irqreturn_t (*handler)(int, void *, struct pt_regs *), +int cpu_request_irq(unsigned int irq, + irqreturn_t (*handler)(int, void *, struct pt_regs *), unsigned long flags, const char *devname, void *dev_id) { if (irq < IRQ1 || irq > IRQ7) { @@ -169,7 +169,7 @@ int sys_request_irq(unsigned int irq, return 0; } -void sys_free_irq(unsigned int irq, void *dev_id) +void cpu_free_irq(unsigned int irq, void *dev_id) { if (irq < IRQ1 || irq > IRQ7) { printk("%s: Incorrect IRQ %d\n", __FUNCTION__, irq); --- linux-2.6.4-rc2/arch/m68k/kernel/sys_m68k.c 2004-03-03 23:12:42.000000000 -0800 +++ 25/arch/m68k/kernel/sys_m68k.c 2004-03-07 20:47:07.000000000 -0800 @@ -261,12 +261,6 @@ asmlinkage int sys_ipc (uint call, int f return -EINVAL; } -asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int on) -{ - return -ENOSYS; -} - - /* Convert virtual (user) address VADDR to physical address PADDR */ #define virt_to_phys_040(vaddr) \ ({ \ --- linux-2.6.4-rc2/arch/m68k/mac/iop.c 2004-03-03 23:12:42.000000000 -0800 +++ 25/arch/m68k/mac/iop.c 2004-03-07 20:47:58.000000000 -0800 @@ -317,7 +317,7 @@ void __init iop_register_interrupts(void { if (iop_ism_present) { if (oss_present) { - sys_request_irq(OSS_IRQLEV_IOPISM, iop_ism_irq, + cpu_request_irq(OSS_IRQLEV_IOPISM, iop_ism_irq, IRQ_FLG_LOCK, "ISM IOP", (void *) IOP_NUM_ISM); oss_irq_enable(IRQ_MAC_ADB); --- linux-2.6.4-rc2/arch/m68k/mac/macints.c 2004-03-03 23:12:42.000000000 -0800 +++ 25/arch/m68k/mac/macints.c 2004-03-07 20:47:58.000000000 -0800 @@ -261,7 +261,8 @@ void mac_init_IRQ(void) if (psc_present) psc_register_interrupts(); if (baboon_present) baboon_register_interrupts(); iop_register_interrupts(); - sys_request_irq(7, mac_nmi_handler, IRQ_FLG_LOCK, "NMI", mac_nmi_handler); + cpu_request_irq(7, mac_nmi_handler, IRQ_FLG_LOCK, "NMI", + mac_nmi_handler); #ifdef DEBUG_MACINTS printk("mac_init_IRQ(): Done!\n"); #endif @@ -507,7 +508,7 @@ int mac_request_irq(unsigned int irq, #endif if (irq < VIA1_SOURCE_BASE) { - return sys_request_irq(irq, handler, flags, devname, dev_id); + return cpu_request_irq(irq, handler, flags, devname, dev_id); } if (irq >= NUM_MAC_SOURCES) { @@ -544,7 +545,7 @@ void mac_free_irq(unsigned int irq, void #endif if (irq < VIA1_SOURCE_BASE) { - return sys_free_irq(irq, dev_id); + return cpu_free_irq(irq, dev_id); } if (irq >= NUM_MAC_SOURCES) { --- linux-2.6.4-rc2/arch/m68k/mac/oss.c 2003-06-14 12:18:20.000000000 -0700 +++ 25/arch/m68k/mac/oss.c 2004-03-07 20:47:58.000000000 -0800 @@ -67,15 +67,15 @@ void __init oss_init(void) void __init oss_register_interrupts(void) { - sys_request_irq(OSS_IRQLEV_SCSI, oss_irq, IRQ_FLG_LOCK, + cpu_request_irq(OSS_IRQLEV_SCSI, oss_irq, IRQ_FLG_LOCK, "scsi", (void *) oss); - sys_request_irq(OSS_IRQLEV_IOPSCC, mac_scc_dispatch, IRQ_FLG_LOCK, + cpu_request_irq(OSS_IRQLEV_IOPSCC, mac_scc_dispatch, IRQ_FLG_LOCK, "scc", mac_scc_dispatch); - sys_request_irq(OSS_IRQLEV_NUBUS, oss_nubus_irq, IRQ_FLG_LOCK, + cpu_request_irq(OSS_IRQLEV_NUBUS, oss_nubus_irq, IRQ_FLG_LOCK, "nubus", (void *) oss); - sys_request_irq(OSS_IRQLEV_SOUND, oss_irq, IRQ_FLG_LOCK, + cpu_request_irq(OSS_IRQLEV_SOUND, oss_irq, IRQ_FLG_LOCK, "sound", (void *) oss); - sys_request_irq(OSS_IRQLEV_VIA1, via1_irq, IRQ_FLG_LOCK, + cpu_request_irq(OSS_IRQLEV_VIA1, via1_irq, IRQ_FLG_LOCK, "via1", (void *) via1); } --- linux-2.6.4-rc2/arch/m68k/mac/psc.c 2003-06-14 12:18:23.000000000 -0700 +++ 25/arch/m68k/mac/psc.c 2004-03-07 20:47:58.000000000 -0800 @@ -117,14 +117,10 @@ void __init psc_init(void) void __init psc_register_interrupts(void) { - sys_request_irq(3, psc_irq, IRQ_FLG_LOCK, "psc3", - (void *) 0x30); - sys_request_irq(4, psc_irq, IRQ_FLG_LOCK, "psc4", - (void *) 0x40); - sys_request_irq(5, psc_irq, IRQ_FLG_LOCK, "psc5", - (void *) 0x50); - sys_request_irq(6, psc_irq, IRQ_FLG_LOCK, "psc6", - (void *) 0x60); + cpu_request_irq(3, psc_irq, IRQ_FLG_LOCK, "psc3", (void *) 0x30); + cpu_request_irq(4, psc_irq, IRQ_FLG_LOCK, "psc4", (void *) 0x40); + cpu_request_irq(5, psc_irq, IRQ_FLG_LOCK, "psc5", (void *) 0x50); + cpu_request_irq(6, psc_irq, IRQ_FLG_LOCK, "psc6", (void *) 0x60); } /* --- linux-2.6.4-rc2/arch/m68k/mac/via.c 2004-03-03 23:12:42.000000000 -0800 +++ 25/arch/m68k/mac/via.c 2004-03-07 20:47:58.000000000 -0800 @@ -260,24 +260,27 @@ void __init via_init_clock(irqreturn_t ( void __init via_register_interrupts(void) { if (via_alt_mapping) { - sys_request_irq(IRQ_AUTO_1, via1_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST, - "software", (void *) via1); - sys_request_irq(IRQ_AUTO_6, via1_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST, - "via1", (void *) via1); + cpu_request_irq(IRQ_AUTO_1, via1_irq, + IRQ_FLG_LOCK|IRQ_FLG_FAST, "software", + (void *) via1); + cpu_request_irq(IRQ_AUTO_6, via1_irq, + IRQ_FLG_LOCK|IRQ_FLG_FAST, "via1", + (void *) via1); } else { - sys_request_irq(IRQ_AUTO_1, via1_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST, - "via1", (void *) via1); + cpu_request_irq(IRQ_AUTO_1, via1_irq, + IRQ_FLG_LOCK|IRQ_FLG_FAST, "via1", + (void *) via1); #if 0 /* interferes with serial on some machines */ if (!psc_present) { - sys_request_irq(IRQ_AUTO_6, mac_bang, IRQ_FLG_LOCK, + cpu_request_irq(IRQ_AUTO_6, mac_bang, IRQ_FLG_LOCK, "Off Switch", mac_bang); } #endif } - sys_request_irq(IRQ_AUTO_2, via2_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST, + cpu_request_irq(IRQ_AUTO_2, via2_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST, "via2", (void *) via2); if (!psc_present) { - sys_request_irq(IRQ_AUTO_4, mac_scc_dispatch, IRQ_FLG_LOCK, + cpu_request_irq(IRQ_AUTO_4, mac_scc_dispatch, IRQ_FLG_LOCK, "scc", mac_scc_dispatch); } request_irq(IRQ_MAC_NUBUS, via_nubus_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST, --- linux-2.6.4-rc2/arch/m68knommu/kernel/syscalltable.S 2003-06-14 12:17:55.000000000 -0700 +++ 25/arch/m68knommu/kernel/syscalltable.S 2004-03-07 20:47:07.000000000 -0800 @@ -120,7 +120,7 @@ ENTRY(sys_call_table) .long sys_ni_syscall /* old profil syscall holder */ .long sys_statfs .long sys_fstatfs /* 100 */ - .long sys_ioperm + .long sys_ni_syscall /* ioperm for i386 */ .long sys_socketcall .long sys_syslog .long sys_setitimer --- linux-2.6.4-rc2/arch/m68knommu/kernel/sys_m68k.c 2004-03-03 23:12:42.000000000 -0800 +++ 25/arch/m68knommu/kernel/sys_m68k.c 2004-03-07 20:47:07.000000000 -0800 @@ -193,12 +193,6 @@ asmlinkage int sys_ipc (uint call, int f return -EINVAL; } -asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int on) -{ - return -ENOSYS; -} - - /* sys_cacheflush -- flush (part of) the processor cache. */ asmlinkage int sys_cacheflush (unsigned long addr, int scope, int cache, unsigned long len) --- linux-2.6.4-rc2/arch/m68k/q40/config.c 2003-06-14 12:18:21.000000000 -0700 +++ 25/arch/m68k/q40/config.c 2004-03-07 20:47:58.000000000 -0800 @@ -40,7 +40,7 @@ extern void floppy_setup(char *str, int *ints); extern irqreturn_t q40_process_int (int level, struct pt_regs *regs); -extern irqreturn_t (*q40_sys_default_handler[]) (int, void *, struct pt_regs *); /* added just for debugging */ +extern irqreturn_t (*q40_default_handler[]) (int, void *, struct pt_regs *); /* added just for debugging */ extern void q40_init_IRQ (void); extern void q40_free_irq (unsigned int, void *); extern int show_q40_interrupts (struct seq_file *, void *); @@ -185,7 +185,7 @@ void __init config_q40(void) mach_request_irq = q40_request_irq; enable_irq = q40_enable_irq; disable_irq = q40_disable_irq; - mach_default_handler = &q40_sys_default_handler; + mach_default_handler = &q40_default_handler; mach_get_model = q40_get_model; mach_get_hardware_list = q40_get_hardware_list; --- linux-2.6.4-rc2/arch/m68k/q40/q40ints.c 2004-02-03 20:42:34.000000000 -0800 +++ 25/arch/m68k/q40/q40ints.c 2004-03-07 20:47:58.000000000 -0800 @@ -46,10 +46,8 @@ extern int ints_inited; irqreturn_t q40_irq2_handler (int, void *, struct pt_regs *fp); -extern irqreturn_t (*q40_sys_default_handler[]) (int, void *, struct pt_regs *); - static irqreturn_t q40_defhand (int irq, void *dev_id, struct pt_regs *fp); -static irqreturn_t sys_default_handler(int lev, void *dev_id, struct pt_regs *regs); +static irqreturn_t default_handler(int lev, void *dev_id, struct pt_regs *regs); #define DEVNAME_SIZE 24 @@ -96,7 +94,8 @@ void q40_init_IRQ (void) } /* setup handler for ISA ints */ - sys_request_irq(IRQ2,q40_irq2_handler, 0, "q40 ISA and master chip", NULL); + cpu_request_irq(IRQ2, q40_irq2_handler, 0, "q40 ISA and master chip", + NULL); /* now enable some ints.. */ master_outb(1,EXT_ENABLE_REG); /* ISA IRQ 5-15 */ @@ -153,8 +152,8 @@ int q40_request_irq(unsigned int irq, } else { /* Q40_IRQ_SAMPLE :somewhat special actions required here ..*/ - sys_request_irq(4,handler,flags,devname,dev_id); - sys_request_irq(6,handler,flags,devname,dev_id); + cpu_request_irq(4, handler, flags, devname, dev_id); + cpu_request_irq(6, handler, flags, devname, dev_id); return 0; } } @@ -192,8 +191,8 @@ void q40_free_irq(unsigned int irq, void } else { /* == Q40_IRQ_SAMPLE */ - sys_free_irq(4,dev_id); - sys_free_irq(6,dev_id); + cpu_free_irq(4, dev_id); + cpu_free_irq(6, dev_id); } } @@ -417,16 +416,16 @@ static irqreturn_t q40_defhand (int irq, else master_outb(-1,KEYBOARD_UNLOCK_REG); return IRQ_NONE; } -static irqreturn_t sys_default_handler(int lev, void *dev_id, struct pt_regs *regs) +static irqreturn_t default_handler(int lev, void *dev_id, struct pt_regs *regs) { printk ("Uninitialised interrupt level %d\n", lev); return IRQ_NONE; } - irqreturn_t (*q40_sys_default_handler[SYS_IRQS]) (int, void *, struct pt_regs *) = { - sys_default_handler,sys_default_handler,sys_default_handler,sys_default_handler, - sys_default_handler,sys_default_handler,sys_default_handler,sys_default_handler - }; +irqreturn_t (*q40_default_handler[SYS_IRQS])(int, void *, struct pt_regs *) = { + default_handler, default_handler, default_handler, default_handler, + default_handler, default_handler, default_handler, default_handler +}; void q40_enable_irq (unsigned int irq) --- linux-2.6.4-rc2/arch/m68k/sun3/sun3ints.c 2003-06-14 12:18:51.000000000 -0700 +++ 25/arch/m68k/sun3/sun3ints.c 2004-03-07 20:47:58.000000000 -0800 @@ -153,8 +153,8 @@ void sun3_init_IRQ(void) for(i = 0; i < SYS_IRQS; i++) { if(dev_names[i]) - sys_request_irq(i, sun3_default_handler[i], - 0, dev_names[i], NULL); + cpu_request_irq(i, sun3_default_handler[i], 0, + dev_names[i], NULL); } for(i = 0; i < 192; i++) @@ -178,7 +178,8 @@ int sun3_request_irq(unsigned int irq, i dev_names[irq] = devname; /* setting devname would be nice */ - sys_request_irq(irq, sun3_default_handler[irq], 0, devname, NULL); + cpu_request_irq(irq, sun3_default_handler[irq], 0, devname, + NULL); return 0; } else { --- linux-2.6.4-rc2/arch/mips/kernel/ioctl32.c 2004-03-03 23:12:42.000000000 -0800 +++ 25/arch/mips/kernel/ioctl32.c 2004-03-07 20:46:45.000000000 -0800 @@ -1277,20 +1277,6 @@ COMPATIBLE_IOCTL(SBPROF_ZBSTOP) COMPATIBLE_IOCTL(SBPROF_ZBWAITFULL) #endif /* CONFIG_SIBYTE_TBPROF */ -#if defined(CONFIG_BLK_DEV_DM) || defined(CONFIG_BLK_DEV_DM_MODULE) -COMPATIBLE_IOCTL(DM_VERSION) -COMPATIBLE_IOCTL(DM_REMOVE_ALL) -COMPATIBLE_IOCTL(DM_DEV_CREATE) -COMPATIBLE_IOCTL(DM_DEV_REMOVE) -COMPATIBLE_IOCTL(DM_DEV_RELOAD) -COMPATIBLE_IOCTL(DM_DEV_SUSPEND) -COMPATIBLE_IOCTL(DM_DEV_RENAME) -COMPATIBLE_IOCTL(DM_DEV_DEPS) -COMPATIBLE_IOCTL(DM_DEV_STATUS) -COMPATIBLE_IOCTL(DM_TARGET_STATUS) -COMPATIBLE_IOCTL(DM_TARGET_WAIT) -#endif /* CONFIG_BLK_DEV_DM */ - COMPATIBLE_IOCTL(MTIOCTOP) /* mtio.h ioctls */ HANDLE_IOCTL(MTIOCGET32, mt_ioctl_trans) HANDLE_IOCTL(MTIOCPOS32, mt_ioctl_trans) --- linux-2.6.4-rc2/arch/mips/mm/dma-coherent.c 2004-03-03 23:12:43.000000000 -0800 +++ 25/arch/mips/mm/dma-coherent.c 2004-03-07 20:46:58.000000000 -0800 @@ -119,30 +119,55 @@ void dma_unmap_sg(struct device *dev, st EXPORT_SYMBOL(dma_unmap_sg); -void dma_sync_single(struct device *dev, dma_addr_t dma_handle, size_t size, +void dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, size_t size, enum dma_data_direction direction) { BUG_ON(direction == DMA_NONE); } -EXPORT_SYMBOL(dma_sync_single); +EXPORT_SYMBOL(dma_sync_single_for_cpu); -void dma_sync_single_range(struct device *dev, dma_addr_t dma_handle, +void dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, size_t size, + enum dma_data_direction direction) +{ + BUG_ON(direction == DMA_NONE); +} + +EXPORT_SYMBOL(dma_sync_single_for_device); + +void dma_sync_single_range_for_cpu(struct device *dev, dma_addr_t dma_handle, + unsigned long offset, size_t size, + enum dma_data_direction direction) +{ + BUG_ON(direction == DMA_NONE); +} + +EXPORT_SYMBOL(dma_sync_single_range_for_cpu); + +void dma_sync_single_range_for_device(struct device *dev, dma_addr_t dma_handle, unsigned long offset, size_t size, enum dma_data_direction direction) { BUG_ON(direction == DMA_NONE); } -EXPORT_SYMBOL(dma_sync_single_range); +EXPORT_SYMBOL(dma_sync_single_range_for_device); -void dma_sync_sg(struct device *dev, struct scatterlist *sg, int nelems, +void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems, enum dma_data_direction direction) { BUG_ON(direction == DMA_NONE); } -EXPORT_SYMBOL(dma_sync_sg); +EXPORT_SYMBOL(dma_sync_sg_for_cpu); + +void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems, + enum dma_data_direction direction) +{ + BUG_ON(direction == DMA_NONE); +} + +EXPORT_SYMBOL(dma_sync_sg_for_device); int dma_supported(struct device *dev, u64 mask) { @@ -204,12 +229,20 @@ unsigned long pci_dac_dma_to_offset(stru EXPORT_SYMBOL(pci_dac_dma_to_offset); -void pci_dac_dma_sync_single(struct pci_dev *pdev, +void pci_dac_dma_sync_single_for_cpu(struct pci_dev *pdev, + dma64_addr_t dma_addr, size_t len, int direction) +{ + BUG_ON(direction == PCI_DMA_NONE); +} + +EXPORT_SYMBOL(pci_dac_dma_sync_single_for_cpu); + +void pci_dac_dma_sync_single_for_device(struct pci_dev *pdev, dma64_addr_t dma_addr, size_t len, int direction) { BUG_ON(direction == PCI_DMA_NONE); } -EXPORT_SYMBOL(pci_dac_dma_sync_single); +EXPORT_SYMBOL(pci_dac_dma_sync_single_for_device); #endif /* CONFIG_PCI */ --- linux-2.6.4-rc2/arch/mips/mm/dma-ip27.c 2004-03-03 23:12:43.000000000 -0800 +++ 25/arch/mips/mm/dma-ip27.c 2004-03-07 20:46:58.000000000 -0800 @@ -125,30 +125,55 @@ void dma_unmap_sg(struct device *dev, st EXPORT_SYMBOL(dma_unmap_sg); -void dma_sync_single(struct device *dev, dma_addr_t dma_handle, size_t size, +void dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, size_t size, enum dma_data_direction direction) { BUG_ON(direction == DMA_NONE); } -EXPORT_SYMBOL(dma_sync_single); +EXPORT_SYMBOL(dma_sync_single_for_cpu); -void dma_sync_single_range(struct device *dev, dma_addr_t dma_handle, +void dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, size_t size, + enum dma_data_direction direction) +{ + BUG_ON(direction == DMA_NONE); +} + +EXPORT_SYMBOL(dma_sync_single_for_device); + +void dma_sync_single_range_for_cpu(struct device *dev, dma_addr_t dma_handle, + unsigned long offset, size_t size, + enum dma_data_direction direction) +{ + BUG_ON(direction == DMA_NONE); +} + +EXPORT_SYMBOL(dma_sync_single_range_for_cpu); + +void dma_sync_single_range_for_device(struct device *dev, dma_addr_t dma_handle, unsigned long offset, size_t size, enum dma_data_direction direction) { BUG_ON(direction == DMA_NONE); } -EXPORT_SYMBOL(dma_sync_single_range); +EXPORT_SYMBOL(dma_sync_single_range_for_device); -void dma_sync_sg(struct device *dev, struct scatterlist *sg, int nelems, +void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems, enum dma_data_direction direction) { BUG_ON(direction == DMA_NONE); } -EXPORT_SYMBOL(dma_sync_sg); +EXPORT_SYMBOL(dma_sync_sg_for_cpu); + +void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems, + enum dma_data_direction direction) +{ + BUG_ON(direction == DMA_NONE); +} + +EXPORT_SYMBOL(dma_sync_sg_for_device); int dma_supported(struct device *dev, u64 mask) { @@ -208,10 +233,18 @@ unsigned long pci_dac_dma_to_offset(stru EXPORT_SYMBOL(pci_dac_dma_to_offset); -void pci_dac_dma_sync_single(struct pci_dev *pdev, +void pci_dac_dma_sync_single_for_cpu(struct pci_dev *pdev, + dma64_addr_t dma_addr, size_t len, int direction) +{ + BUG_ON(direction == PCI_DMA_NONE); +} + +EXPORT_SYMBOL(pci_dac_dma_sync_single_for_cpu); + +void pci_dac_dma_sync_single_for_device(struct pci_dev *pdev, dma64_addr_t dma_addr, size_t len, int direction) { BUG_ON(direction == PCI_DMA_NONE); } -EXPORT_SYMBOL(pci_dac_dma_sync_single); +EXPORT_SYMBOL(pci_dac_dma_sync_single_for_device); --- linux-2.6.4-rc2/arch/mips/mm/dma-noncoherent.c 2004-03-03 23:12:43.000000000 -0800 +++ 25/arch/mips/mm/dma-noncoherent.c 2004-03-07 20:46:58.000000000 -0800 @@ -226,7 +226,7 @@ void dma_unmap_sg(struct device *dev, st EXPORT_SYMBOL(dma_unmap_sg); -void dma_sync_single(struct device *dev, dma_addr_t dma_handle, size_t size, +void dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, size_t size, enum dma_data_direction direction) { unsigned long addr; @@ -237,9 +237,35 @@ void dma_sync_single(struct device *dev, __dma_sync(addr, size, direction); } -EXPORT_SYMBOL(dma_sync_single); +EXPORT_SYMBOL(dma_sync_single_for_cpu); -void dma_sync_single_range(struct device *dev, dma_addr_t dma_handle, +void dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, size_t size, + enum dma_data_direction direction) +{ + unsigned long addr; + + BUG_ON(direction == DMA_NONE); + + addr = dma_handle + PAGE_OFFSET; + __dma_sync(addr, size, direction); +} + +EXPORT_SYMBOL(dma_sync_single_for_device); + +void dma_sync_single_range_for_cpu(struct device *dev, dma_addr_t dma_handle, + unsigned long offset, size_t size, enum dma_data_direction direction) +{ + unsigned long addr; + + BUG_ON(direction == DMA_NONE); + + addr = dma_handle + offset + PAGE_OFFSET; + __dma_sync(addr, size, direction); +} + +EXPORT_SYMBOL(dma_sync_single_range_for_cpu); + +void dma_sync_single_range_for_device(struct device *dev, dma_addr_t dma_handle, unsigned long offset, size_t size, enum dma_data_direction direction) { unsigned long addr; @@ -250,9 +276,9 @@ void dma_sync_single_range(struct device __dma_sync(addr, size, direction); } -EXPORT_SYMBOL(dma_sync_single_range); +EXPORT_SYMBOL(dma_sync_single_range_for_device); -void dma_sync_sg(struct device *dev, struct scatterlist *sg, int nelems, +void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems, enum dma_data_direction direction) { int i; @@ -265,7 +291,22 @@ void dma_sync_sg(struct device *dev, str sg->length, direction); } -EXPORT_SYMBOL(dma_sync_sg); +EXPORT_SYMBOL(dma_sync_sg_for_cpu); + +void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems, + enum dma_data_direction direction) +{ + int i; + + BUG_ON(direction == DMA_NONE); + + /* Make sure that gcc doesn't leave the empty loop body. */ + for (i = 0; i < nelems; i++, sg++) + __dma_sync((unsigned long)page_address(sg->page), + sg->length, direction); +} + +EXPORT_SYMBOL(dma_sync_sg_for_device); int dma_supported(struct device *dev, u64 mask) { @@ -329,7 +370,17 @@ unsigned long pci_dac_dma_to_offset(stru EXPORT_SYMBOL(pci_dac_dma_to_offset); -void pci_dac_dma_sync_single(struct pci_dev *pdev, +void pci_dac_dma_sync_single_for_cpu(struct pci_dev *pdev, + dma64_addr_t dma_addr, size_t len, int direction) +{ + BUG_ON(direction == PCI_DMA_NONE); + + dma_cache_wback_inv(dma_addr + PAGE_OFFSET, len); +} + +EXPORT_SYMBOL(pci_dac_dma_sync_single_for_cpu); + +void pci_dac_dma_sync_single_for_device(struct pci_dev *pdev, dma64_addr_t dma_addr, size_t len, int direction) { BUG_ON(direction == PCI_DMA_NONE); @@ -337,6 +388,6 @@ void pci_dac_dma_sync_single(struct pci_ dma_cache_wback_inv(dma_addr + PAGE_OFFSET, len); } -EXPORT_SYMBOL(pci_dac_dma_sync_single); +EXPORT_SYMBOL(pci_dac_dma_sync_single_for_device); #endif /* CONFIG_PCI */ --- linux-2.6.4-rc2/arch/parisc/kernel/drivers.c 2004-02-17 20:48:42.000000000 -0800 +++ 25/arch/parisc/kernel/drivers.c 2004-03-07 20:47:00.000000000 -0800 @@ -618,6 +618,7 @@ static void parisc_generic_device_regist tmp1); /* make the generic dma mask a pointer to the parisc one */ dev->dev.dma_mask = &dev->dma_mask; + dev->dev.coherent_dma_mask = dev->dma_mask; pr_debug("device_register(%s)\n", dev->dev.bus_id); device_register(&dev->dev); } --- linux-2.6.4-rc2/arch/parisc/kernel/pci-dma.c 2004-02-17 20:48:42.000000000 -0800 +++ 25/arch/parisc/kernel/pci-dma.c 2004-03-07 20:47:00.000000000 -0800 @@ -372,7 +372,7 @@ static void * pa11_dma_alloc_consistent ** ISA cards will certainly only support 24-bit DMA addressing. ** Not clear if we can, want, or need to support ISA. */ - if (!dev || *dev->dma_mask != 0xffffffff) + if (!dev || *dev->coherent_dma_mask < 0xffffffff) gfp |= GFP_DMA; #endif return (void *)vaddr; @@ -413,7 +413,7 @@ static void pa11_dma_unmap_single(struct /* * For PCI_DMA_FROMDEVICE this flush is not necessary for the * simple map/unmap case. However, it IS necessary if if - * pci_dma_sync_single has been called and the buffer reused. + * pci_dma_sync_single_* has been called and the buffer reused. */ flush_kernel_dcache_range((unsigned long) phys_to_virt(dma_handle), size); @@ -453,7 +453,7 @@ static void pa11_dma_unmap_sg(struct dev return; } -static void pa11_dma_sync_single(struct device *dev, dma_addr_t dma_handle, unsigned long offset, size_t size, enum dma_data_direction direction) +static void pa11_dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, unsigned long offset, size_t size, enum dma_data_direction direction) { if (direction == DMA_NONE) BUG(); @@ -461,7 +461,25 @@ static void pa11_dma_sync_single(struct flush_kernel_dcache_range((unsigned long) phys_to_virt(dma_handle) + offset, size); } -static void pa11_dma_sync_sg(struct device *dev, struct scatterlist *sglist, int nents, enum dma_data_direction direction) +static void pa11_dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, unsigned long offset, size_t size, enum dma_data_direction direction) +{ + if (direction == DMA_NONE) + BUG(); + + flush_kernel_dcache_range((unsigned long) phys_to_virt(dma_handle) + offset, size); +} + +static void pa11_dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sglist, int nents, enum dma_data_direction direction) +{ + int i; + + /* once we do combining we'll need to use phys_to_virt(sg_dma_address(sglist)) */ + + for (i = 0; i < nents; i++, sglist++ ) + flush_kernel_dcache_range(sg_virt_addr(sglist), sglist->length); +} + +static void pa11_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sglist, int nents, enum dma_data_direction direction) { int i; @@ -480,8 +498,10 @@ struct hppa_dma_ops pcxl_dma_ops = { .unmap_single = pa11_dma_unmap_single, .map_sg = pa11_dma_map_sg, .unmap_sg = pa11_dma_unmap_sg, - .dma_sync_single = pa11_dma_sync_single, - .dma_sync_sg = pa11_dma_sync_sg, + .dma_sync_single_for_cpu = pa11_dma_sync_single_for_cpu, + .dma_sync_single_for_device = pa11_dma_sync_single_for_device, + .dma_sync_sg_for_cpu = pa11_dma_sync_sg_for_cpu, + .dma_sync_sg_for_device = pa11_dma_sync_sg_for_device, }; static void *fail_alloc_consistent(struct device *dev, size_t size, @@ -519,8 +539,10 @@ struct hppa_dma_ops pcx_dma_ops = { .unmap_single = pa11_dma_unmap_single, .map_sg = pa11_dma_map_sg, .unmap_sg = pa11_dma_unmap_sg, - .dma_sync_single = pa11_dma_sync_single, - .dma_sync_sg = pa11_dma_sync_sg, + .dma_sync_single_cpu = pa11_dma_sync_single_cpu, + .dma_sync_single_device = pa11_dma_sync_single_device, + .dma_sync_sg_cpu = pa11_dma_sync_sg_cpu, + .dma_sync_sg_device = pa11_dma_sync_sg_device, }; --- linux-2.6.4-rc2/arch/parisc/kernel/process.c 2004-02-17 20:48:42.000000000 -0800 +++ 25/arch/parisc/kernel/process.c 2004-03-07 20:47:47.000000000 -0800 @@ -32,7 +32,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#define __KERNEL_SYSCALLS__ #include #include --- linux-2.6.4-rc2/arch/parisc/kernel/smp.c 2004-01-09 00:04:31.000000000 -0800 +++ 25/arch/parisc/kernel/smp.c 2004-03-07 20:47:47.000000000 -0800 @@ -16,7 +16,6 @@ ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. */ -#define __KERNEL_SYSCALLS__ #undef ENTRY_SYS_CPUS /* syscall support for iCOD-like functionality */ #include --- linux-2.6.4-rc2/arch/parisc/kernel/sys_parisc.c 2004-03-03 23:12:43.000000000 -0800 +++ 25/arch/parisc/kernel/sys_parisc.c 2004-03-07 20:47:07.000000000 -0800 @@ -242,14 +242,6 @@ asmlinkage ssize_t parisc_readahead(int return sys_readahead(fd, (loff_t)high << 32 | low, count); } -/* - * This changes the io permissions bitmap in the current task. - */ -asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int turn_on) -{ - return -ENOSYS; -} - asmlinkage unsigned long sys_alloc_hugepages(int key, unsigned long addr, unsigned long len, int prot, int flag) { return -ENOMEM; --- linux-2.6.4-rc2/arch/ppc64/Kconfig 2004-03-03 23:12:43.000000000 -0800 +++ 25/arch/ppc64/Kconfig 2004-03-07 20:46:45.000000000 -0800 @@ -300,10 +300,6 @@ config VIOCD If you are running Linux on an IBM iSeries system and you want to read a CD drive owned by OS/400, say Y here. -config VIOCD_AZTECH - bool "iSeries Virtual CD Aztech emulation" - depends on VIOCD - config VIOTAPE tristate "iSeries Virtual Tape Support" help --- linux-2.6.4-rc2/arch/ppc64/kernel/iSeries_iommu.c 2004-03-03 23:12:43.000000000 -0800 +++ 25/arch/ppc64/kernel/iSeries_iommu.c 2004-03-07 20:46:45.000000000 -0800 @@ -43,6 +43,7 @@ #include #include #include +#include #include @@ -58,11 +59,6 @@ static struct iSeries_Device_Node vio_de static struct pci_dev _veth_dev = { .sysdata = &veth_dev_node }; static struct pci_dev _vio_dev = { .sysdata = &vio_dev_node, .dev.bus = &pci_bus_type }; -/* - * I wonder what the deal is with these. Nobody uses them. Why do they - * exist? Why do we export them to modules? Why is this comment here, and - * why didn't I just delete them? - */ struct pci_dev *iSeries_veth_dev = &_veth_dev; struct device *iSeries_vio_dev = &_vio_dev.dev; --- linux-2.6.4-rc2/arch/ppc64/kernel/mf.c 2004-02-03 20:42:34.000000000 -0800 +++ 25/arch/ppc64/kernel/mf.c 2004-03-07 20:46:45.000000000 -0800 @@ -38,10 +38,9 @@ #include #include #include -#include +#include #include - -extern struct pci_dev *iSeries_vio_dev; +#include /* * This is the structure layout for the Machine Facilites LPAR event @@ -791,7 +790,8 @@ void mf_setCmdLine(const char *cmdline, { struct VspCmdData myVspCmd; dma_addr_t dma_addr = 0; - char *page = pci_alloc_consistent(iSeries_vio_dev, size, &dma_addr); + char *page = dma_alloc_coherent(iSeries_vio_dev, size, &dma_addr, + GFP_ATOMIC); if (page == NULL) { printk(KERN_ERR "mf.c: couldn't allocate memory to set command line\n"); @@ -809,7 +809,7 @@ void mf_setCmdLine(const char *cmdline, mb(); (void)signal_vsp_instruction(&myVspCmd); - pci_free_consistent(iSeries_vio_dev, size, page, dma_addr); + dma_free_coherent(iSeries_vio_dev, size, page, dma_addr); } int mf_getCmdLine(char *cmdline, int *size, u64 side) @@ -819,8 +819,8 @@ int mf_getCmdLine(char *cmdline, int *si int len = *size; dma_addr_t dma_addr; - dma_addr = pci_map_single(iSeries_vio_dev, cmdline, len, - PCI_DMA_FROMDEVICE); + dma_addr = dma_map_single(iSeries_vio_dev, cmdline, len, + DMA_FROM_DEVICE); memset(cmdline, 0, len); memset(&myVspCmd, 0, sizeof(myVspCmd)); myVspCmd.cmd = 33; @@ -840,7 +840,7 @@ int mf_getCmdLine(char *cmdline, int *si #endif } - pci_unmap_single(iSeries_vio_dev, dma_addr, *size, PCI_DMA_FROMDEVICE); + dma_unmap_single(iSeries_vio_dev, dma_addr, *size, DMA_FROM_DEVICE); return len; } @@ -851,7 +851,8 @@ int mf_setVmlinuxChunk(const char *buffe struct VspCmdData myVspCmd; int rc; dma_addr_t dma_addr = 0; - char *page = pci_alloc_consistent(iSeries_vio_dev, size, &dma_addr); + char *page = dma_alloc_coherent(iSeries_vio_dev, size, &dma_addr, + GFP_ATOMIC); if (page == NULL) { printk(KERN_ERR "mf.c: couldn't allocate memory to set vmlinux chunk\n"); @@ -876,7 +877,7 @@ int mf_setVmlinuxChunk(const char *buffe rc = -ENOMEM; } - pci_free_consistent(iSeries_vio_dev, size, page, dma_addr); + dma_free_coherent(iSeries_vio_dev, size, page, dma_addr); return rc; } @@ -888,8 +889,8 @@ int mf_getVmlinuxChunk(char *buffer, int int len = *size; dma_addr_t dma_addr; - dma_addr = pci_map_single(iSeries_vio_dev, buffer, len, - PCI_DMA_FROMDEVICE); + dma_addr = dma_map_single(iSeries_vio_dev, buffer, len, + DMA_FROM_DEVICE); memset(buffer, 0, len); memset(&myVspCmd, 0, sizeof(myVspCmd)); myVspCmd.cmd = 32; @@ -907,7 +908,7 @@ int mf_getVmlinuxChunk(char *buffer, int rc = -ENOMEM; } - pci_unmap_single(iSeries_vio_dev, dma_addr, len, PCI_DMA_FROMDEVICE); + dma_unmap_single(iSeries_vio_dev, dma_addr, len, DMA_FROM_DEVICE); return rc; } --- linux-2.6.4-rc2/arch/ppc64/kernel/misc.S 2004-03-03 23:12:43.000000000 -0800 +++ 25/arch/ppc64/kernel/misc.S 2004-03-07 20:47:47.000000000 -0800 @@ -582,17 +582,7 @@ _GLOBAL(name) \ li r3,-1; \ blr -#define __NR__exit __NR_exit - -SYSCALL(setsid) -SYSCALL(open) -SYSCALL(read) -SYSCALL(write) -SYSCALL(lseek) -SYSCALL(close) -SYSCALL(dup) SYSCALL(execve) -SYSCALL(waitpid) #ifdef CONFIG_PPC_ISERIES /* hack hack hack */ #define ppc_rtas sys_ni_syscall --- linux-2.6.4-rc2/arch/ppc64/kernel/pmac_smp.c 2004-03-03 23:12:43.000000000 -0800 +++ 25/arch/ppc64/kernel/pmac_smp.c 2004-03-07 20:47:47.000000000 -0800 @@ -29,8 +29,6 @@ #include #include #include -#define __KERNEL_SYSCALLS__ -#include #include #include #include --- linux-2.6.4-rc2/arch/ppc64/kernel/stab.c 2004-03-03 23:12:43.000000000 -0800 +++ 25/arch/ppc64/kernel/stab.c 2004-03-07 20:46:45.000000000 -0800 @@ -184,13 +184,13 @@ int ste_allocate(unsigned long ea) /* Kernel or user address? */ if (REGION_ID(ea) >= KERNEL_REGION_ID) { vsid = get_kernel_vsid(ea); - context = REGION_ID(ea); + context = KERNEL_CONTEXT(ea); } else { if (!current->mm) return 1; context = current->mm->context; - vsid = get_vsid(context, ea); + vsid = get_vsid(context.id, ea); } esid = GET_ESID(ea); @@ -223,7 +223,7 @@ static void preload_stab(struct task_str if (!IS_VALID_EA(pc) || (REGION_ID(pc) >= KERNEL_REGION_ID)) return; - vsid = get_vsid(mm->context, pc); + vsid = get_vsid(mm->context.id, pc); __ste_allocate(pc_esid, vsid); if (pc_esid == stack_esid) @@ -231,7 +231,7 @@ static void preload_stab(struct task_str if (!IS_VALID_EA(stack) || (REGION_ID(stack) >= KERNEL_REGION_ID)) return; - vsid = get_vsid(mm->context, stack); + vsid = get_vsid(mm->context.id, stack); __ste_allocate(stack_esid, vsid); if (pc_esid == unmapped_base_esid || stack_esid == unmapped_base_esid) @@ -240,7 +240,7 @@ static void preload_stab(struct task_str if (!IS_VALID_EA(unmapped_base) || (REGION_ID(unmapped_base) >= KERNEL_REGION_ID)) return; - vsid = get_vsid(mm->context, unmapped_base); + vsid = get_vsid(mm->context.id, unmapped_base); __ste_allocate(unmapped_base_esid, vsid); /* Order update */ @@ -406,14 +406,14 @@ int slb_allocate(unsigned long ea) /* Kernel or user address? */ if (REGION_ID(ea) >= KERNEL_REGION_ID) { - context = REGION_ID(ea); + context = KERNEL_CONTEXT(ea); vsid = get_kernel_vsid(ea); } else { if (unlikely(!current->mm)) return 1; context = current->mm->context; - vsid = get_vsid(context, ea); + vsid = get_vsid(context.id, ea); } esid = GET_ESID(ea); @@ -444,7 +444,7 @@ static void preload_slb(struct task_stru if (!IS_VALID_EA(pc) || (REGION_ID(pc) >= KERNEL_REGION_ID)) return; - vsid = get_vsid(mm->context, pc); + vsid = get_vsid(mm->context.id, pc); __slb_allocate(pc_esid, vsid, mm->context); if (pc_esid == stack_esid) @@ -452,7 +452,7 @@ static void preload_slb(struct task_stru if (!IS_VALID_EA(stack) || (REGION_ID(stack) >= KERNEL_REGION_ID)) return; - vsid = get_vsid(mm->context, stack); + vsid = get_vsid(mm->context.id, stack); __slb_allocate(stack_esid, vsid, mm->context); if (pc_esid == unmapped_base_esid || stack_esid == unmapped_base_esid) @@ -461,7 +461,7 @@ static void preload_slb(struct task_stru if (!IS_VALID_EA(unmapped_base) || (REGION_ID(unmapped_base) >= KERNEL_REGION_ID)) return; - vsid = get_vsid(mm->context, unmapped_base); + vsid = get_vsid(mm->context.id, unmapped_base); __slb_allocate(unmapped_base_esid, vsid, mm->context); } --- linux-2.6.4-rc2/arch/ppc64/kernel/viopath.c 2004-03-03 23:12:43.000000000 -0800 +++ 25/arch/ppc64/kernel/viopath.c 2004-03-07 20:46:45.000000000 -0800 @@ -35,7 +35,6 @@ #include #include #include -#include #include #include @@ -49,8 +48,6 @@ #include #include -extern struct device *iSeries_vio_dev; - /* Status of the path to each other partition in the system. * This is overkill, since we will only ever establish connections * to our hosting partition and the primary partition on the system. --- linux-2.6.4-rc2/arch/ppc64/mm/hash_utils.c 2004-03-03 23:12:43.000000000 -0800 +++ 25/arch/ppc64/mm/hash_utils.c 2004-03-07 20:46:45.000000000 -0800 @@ -265,7 +265,7 @@ int hash_page(unsigned long ea, unsigned if (mm == NULL) return 1; - vsid = get_vsid(mm->context, ea); + vsid = get_vsid(mm->context.id, ea); break; case IO_REGION_ID: mm = &ioremap_mm; --- linux-2.6.4-rc2/arch/ppc64/mm/hugetlbpage.c 2004-02-03 20:42:35.000000000 -0800 +++ 25/arch/ppc64/mm/hugetlbpage.c 2004-03-07 20:46:45.000000000 -0800 @@ -244,7 +244,7 @@ static int open_32bit_htlbpage_range(str struct vm_area_struct *vma; unsigned long addr; - if (mm->context & CONTEXT_LOW_HPAGES) + if (mm->context.low_hpages) return 0; /* The window is already open */ /* Check no VMAs are in the region */ @@ -281,7 +281,7 @@ static int open_32bit_htlbpage_range(str /* FIXME: do we need to scan for PTEs too? */ - mm->context |= CONTEXT_LOW_HPAGES; + mm->context.low_hpages = 1; /* the context change must make it to memory before the slbia, * so that further SLB misses do the right thing. */ @@ -589,7 +589,6 @@ full_search: } } - unsigned long hugetlb_get_unmapped_area(struct file *file, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags) @@ -778,7 +777,7 @@ static void flush_hash_hugepage(mm_conte BUG_ON(hugepte_bad(pte)); BUG_ON(!in_hugepage_area(context, ea)); - vsid = get_vsid(context, ea); + vsid = get_vsid(context.id, ea); va = (vsid << 28) | (ea & 0x0fffffff); vpn = va >> LARGE_PAGE_SHIFT; --- linux-2.6.4-rc2/arch/ppc64/mm/init.c 2004-03-03 23:12:43.000000000 -0800 +++ 25/arch/ppc64/mm/init.c 2004-03-07 20:46:45.000000000 -0800 @@ -794,7 +794,7 @@ void update_mmu_cache(struct vm_area_str if (!ptep) return; - vsid = get_vsid(vma->vm_mm->context, ea); + vsid = get_vsid(vma->vm_mm->context.id, ea); tmp = cpumask_of_cpu(smp_processor_id()); if (cpus_equal(vma->vm_mm->cpu_vm_mask, tmp)) --- linux-2.6.4-rc2/arch/ppc64/mm/tlb.c 2004-03-03 23:12:43.000000000 -0800 +++ 25/arch/ppc64/mm/tlb.c 2004-03-07 20:46:45.000000000 -0800 @@ -62,7 +62,7 @@ void hpte_update(pte_t *ptep, unsigned l addr = ptep_to_address(ptep); if (REGION_ID(addr) == USER_REGION_ID) - context = mm->context; + context = mm->context.id; i = batch->index; /* --- linux-2.6.4-rc2/arch/ppc64/xmon/xmon.c 2004-03-03 23:12:43.000000000 -0800 +++ 25/arch/ppc64/xmon/xmon.c 2004-03-07 20:46:45.000000000 -0800 @@ -344,7 +344,7 @@ xmon(struct pt_regs *excp) #endif /* CONFIG_SMP */ set_msrd(msr); /* restore interrupt enable */ - return 0; + return 1; } int --- linux-2.6.4-rc2/arch/ppc/boot/ld.script 2003-11-09 16:45:05.000000000 -0800 +++ 25/arch/ppc/boot/ld.script 2004-03-07 20:48:22.000000000 -0800 @@ -82,6 +82,7 @@ SECTIONS *(__ksymtab) *(__ksymtab_strings) *(__bug_table) + *(__kcrctab) } } --- linux-2.6.4-rc2/arch/ppc/boot/openfirmware/Makefile 2003-09-08 13:58:56.000000000 -0700 +++ 25/arch/ppc/boot/openfirmware/Makefile 2004-03-07 20:48:22.000000000 -0800 @@ -104,10 +104,10 @@ quiet_cmd_gen-coff = COFF $@ $(HACKCOFF) $@ && \ ln -sf $(notdir $@) $(images)/zImage$(initrd).pmac -$(images)/vmlinux.coff: $(obj)/coffboot +$(images)/vmlinux.coff: $(obj)/coffboot $(boot)/ld.script $(call cmd,gen-coff) -$(images)/vmlinux.initrd.coff: $(obj)/coffboot.initrd +$(images)/vmlinux.initrd.coff: $(obj)/coffboot.initrd $(boot)/ld.script $(call cmd,gen-coff) quiet_cmd_gen-elf-pmac = ELF $@ @@ -116,19 +116,19 @@ quiet_cmd_gen-elf-pmac = ELF $@ $(OBJCOPY) $@ $@ --add-section=.note=$(obj)/note \ -R .comment $(del-ramdisk-sec) -$(images)/vmlinux.elf-pmac: $(obj)/image.o $(NEWWORLDOBJS) $(LIBS) $(obj)/note +$(images)/vmlinux.elf-pmac: $(obj)/image.o $(NEWWORLDOBJS) $(LIBS) $(obj)/note $(boot)/ld.script $(call cmd,gen-elf-pmac) $(images)/vmlinux.initrd.elf-pmac: $(obj)/image.initrd.o $(NEWWORLDOBJS) \ - $(LIBS) $(obj)/note + $(LIBS) $(obj)/note $(boot)/ld.script $(call cmd,gen-elf-pmac) quiet_cmd_gen-chrp = CHRP $@ cmd_gen-chrp = $(LD) $(CHRP_LD_ARGS) -o $@ $^ && \ $(OBJCOPY) $@ $@ -R .comment $(del-ramdisk-sec) -$(images)/zImage.chrp: $(CHRPOBJS) $(obj)/image.o $(LIBS) +$(images)/zImage.chrp: $(CHRPOBJS) $(obj)/image.o $(LIBS) $(boot)/ld.script $(call cmd,gen-chrp) -$(images)/zImage.initrd.chrp: $(CHRPOBJS) $(obj)/image.initrd.o $(LIBS) +$(images)/zImage.initrd.chrp: $(CHRPOBJS) $(obj)/image.initrd.o $(LIBS) $(boot)/ld.script $(call cmd,gen-chrp) quiet_cmd_addnote = ADDNOTE $@ --- linux-2.6.4-rc2/arch/ppc/kernel/head.S 2004-02-17 20:48:42.000000000 -0800 +++ 25/arch/ppc/kernel/head.S 2004-03-07 20:46:45.000000000 -0800 @@ -1492,22 +1492,22 @@ BEGIN_FTR_SECTION * seems that doesn't affect our ability to actually * write to these SPRs. */ - mtspr SPRN_DBAT4U,r20 - mtspr SPRN_DBAT4L,r20 - mtspr SPRN_DBAT5U,r20 - mtspr SPRN_DBAT5L,r20 - mtspr SPRN_DBAT6U,r20 - mtspr SPRN_DBAT6L,r20 - mtspr SPRN_DBAT7U,r20 - mtspr SPRN_DBAT7L,r20 - mtspr SPRN_IBAT4U,r20 - mtspr SPRN_IBAT4L,r20 - mtspr SPRN_IBAT5U,r20 - mtspr SPRN_IBAT5L,r20 - mtspr SPRN_IBAT6U,r20 - mtspr SPRN_IBAT6L,r20 - mtspr SPRN_IBAT7U,r20 - mtspr SPRN_IBAT7L,r20 + mtspr SPRN_DBAT4U,r10 + mtspr SPRN_DBAT4L,r10 + mtspr SPRN_DBAT5U,r10 + mtspr SPRN_DBAT5L,r10 + mtspr SPRN_DBAT6U,r10 + mtspr SPRN_DBAT6L,r10 + mtspr SPRN_DBAT7U,r10 + mtspr SPRN_DBAT7L,r10 + mtspr SPRN_IBAT4U,r10 + mtspr SPRN_IBAT4L,r10 + mtspr SPRN_IBAT5U,r10 + mtspr SPRN_IBAT5L,r10 + mtspr SPRN_IBAT6U,r10 + mtspr SPRN_IBAT6L,r10 + mtspr SPRN_IBAT7U,r10 + mtspr SPRN_IBAT7L,r10 END_FTR_SECTION_IFSET(CPU_FTR_HAS_HIGH_BATS) blr --- linux-2.6.4-rc2/arch/ppc/kernel/misc.S 2004-02-17 20:48:42.000000000 -0800 +++ 25/arch/ppc/kernel/misc.S 2004-03-07 20:47:47.000000000 -0800 @@ -1108,17 +1108,7 @@ _GLOBAL(name) \ li r3,-1; \ blr -#define __NR__exit __NR_exit - -SYSCALL(setsid) -SYSCALL(open) -SYSCALL(read) -SYSCALL(write) -SYSCALL(lseek) -SYSCALL(close) -SYSCALL(dup) SYSCALL(execve) -SYSCALL(waitpid) /* Why isn't this a) automatic, b) written in 'C'? */ .data --- linux-2.6.4-rc2/arch/ppc/kernel/ppc_ksyms.c 2004-02-17 20:48:42.000000000 -0800 +++ 25/arch/ppc/kernel/ppc_ksyms.c 2004-03-07 20:47:47.000000000 -0800 @@ -32,8 +32,6 @@ #include #include #include -#define __KERNEL_SYSCALLS__ -#include #include #include #include @@ -189,10 +187,6 @@ EXPORT_SYMBOL(consistent_sync); EXPORT_SYMBOL(flush_dcache_all); #endif -EXPORT_SYMBOL(open); -EXPORT_SYMBOL(read); -EXPORT_SYMBOL(lseek); -EXPORT_SYMBOL(close); EXPORT_SYMBOL(start_thread); EXPORT_SYMBOL(kernel_thread); --- linux-2.6.4-rc2/arch/ppc/kernel/smp.c 2004-03-03 23:12:43.000000000 -0800 +++ 25/arch/ppc/kernel/smp.c 2004-03-07 20:47:47.000000000 -0800 @@ -17,8 +17,6 @@ #include #include #include -#define __KERNEL_SYSCALLS__ -#include #include #include #include --- linux-2.6.4-rc2/arch/ppc/platforms/chrp_smp.c 2003-06-14 12:18:07.000000000 -0700 +++ 25/arch/ppc/platforms/chrp_smp.c 2004-03-07 20:47:47.000000000 -0800 @@ -16,8 +16,6 @@ #include #include #include -#define __KERNEL_SYSCALLS__ -#include #include #include --- linux-2.6.4-rc2/arch/ppc/platforms/pmac_smp.c 2004-02-17 20:48:42.000000000 -0800 +++ 25/arch/ppc/platforms/pmac_smp.c 2004-03-07 20:47:47.000000000 -0800 @@ -29,8 +29,6 @@ #include #include #include -#define __KERNEL_SYSCALLS__ -#include #include #include #include --- linux-2.6.4-rc2/arch/s390/Kconfig 2004-03-03 23:12:43.000000000 -0800 +++ 25/arch/s390/Kconfig 2004-03-07 20:47:07.000000000 -0800 @@ -143,6 +143,11 @@ config COMPAT depends on S390_SUPPORT default y +config SYSVIPC_COMPAT + bool + depends on COMPAT && SYSVIPC + default y + config BINFMT_ELF32 tristate "Kernel support for 31 bit ELF binaries" depends on S390_SUPPORT --- linux-2.6.4-rc2/arch/s390/kernel/compat_linux.c 2004-03-03 23:12:43.000000000 -0800 +++ 25/arch/s390/kernel/compat_linux.c 2004-03-07 20:47:07.000000000 -0800 @@ -293,541 +293,6 @@ static inline long put_tv32(struct compa __put_user(i->tv_usec, &o->tv_usec))); } -struct msgbuf32 { s32 mtype; char mtext[1]; }; - -struct ipc64_perm_ds32 -{ - __kernel_key_t key; - __kernel_uid32_t uid; - __kernel_gid32_t gid; - __kernel_uid32_t cuid; - __kernel_gid32_t cgid; - compat_mode_t mode; - unsigned short __pad1; - unsigned short seq; - unsigned short __pad2; - unsigned int __unused1; - unsigned int __unused2; -}; - -struct ipc_perm32 -{ - key_t key; - compat_uid_t uid; - compat_gid_t gid; - compat_uid_t cuid; - compat_gid_t cgid; - compat_mode_t mode; - unsigned short seq; -}; - -struct semid_ds32 { - struct ipc_perm32 sem_perm; /* permissions .. see ipc.h */ - compat_time_t sem_otime; /* last semop time */ - compat_time_t sem_ctime; /* last change time */ - u32 sem_base; /* ptr to first semaphore in array */ - u32 sem_pending; /* pending operations to be processed */ - u32 sem_pending_last; /* last pending operation */ - u32 undo; /* undo requests on this array */ - unsigned short sem_nsems; /* no. of semaphores in array */ -}; - -struct semid64_ds32 { - struct ipc64_perm_ds32 sem_perm; - unsigned int __pad1; - compat_time_t sem_otime; - unsigned int __pad2; - compat_time_t sem_ctime; - u32 sem_nsems; - u32 __unused1; - u32 __unused2; -}; - -struct msqid_ds32 -{ - struct ipc_perm32 msg_perm; - u32 msg_first; - u32 msg_last; - compat_time_t msg_stime; - compat_time_t msg_rtime; - compat_time_t msg_ctime; - u32 wwait; - u32 rwait; - unsigned short msg_cbytes; - unsigned short msg_qnum; - unsigned short msg_qbytes; - compat_ipc_pid_t msg_lspid; - compat_ipc_pid_t msg_lrpid; -}; - -struct msqid64_ds32 { - struct ipc64_perm_ds32 msg_perm; - unsigned int __pad1; - compat_time_t msg_stime; - unsigned int __pad2; - compat_time_t msg_rtime; - unsigned int __pad3; - compat_time_t msg_ctime; - unsigned int msg_cbytes; - unsigned int msg_qnum; - unsigned int msg_qbytes; - compat_pid_t msg_lspid; - compat_pid_t msg_lrpid; - unsigned int __unused1; - unsigned int __unused2; -}; - - -struct shmid_ds32 { - struct ipc_perm32 shm_perm; - int shm_segsz; - compat_time_t shm_atime; - compat_time_t shm_dtime; - compat_time_t shm_ctime; - compat_ipc_pid_t shm_cpid; - compat_ipc_pid_t shm_lpid; - unsigned short shm_nattch; -}; - -struct shmid64_ds32 { - struct ipc64_perm_ds32 shm_perm; - compat_size_t shm_segsz; - compat_time_t shm_atime; - unsigned int __unused1; - compat_time_t shm_dtime; - unsigned int __unused2; - compat_time_t shm_ctime; - unsigned int __unused3; - compat_pid_t shm_cpid; - compat_pid_t shm_lpid; - unsigned int shm_nattch; - unsigned int __unused4; - unsigned int __unused5; -}; - -extern int sem_ctls[]; -#define sc_semopm (sem_ctls[2]) -#define SEMOPM_FAST 64 /* ~ 372 bytes on stack */ - -static long -do_sys32_semtimedop (int semid, struct sembuf *tsops, int nsops, - struct compat_timespec *timeout32) -{ - struct sembuf *sops, fast_sops[SEMOPM_FAST]; - struct timespec t; - mm_segment_t oldfs; - long ret; - - /* parameter checking precedence should mirror sys_semtimedop() */ - if (nsops < 1 || semid < 0) - return -EINVAL; - if (nsops > sc_semopm) - return -E2BIG; - if (nsops <= SEMOPM_FAST) - sops = fast_sops; - else { - sops = kmalloc(nsops * sizeof(*sops), GFP_KERNEL); - if (sops == NULL) - return -ENOMEM; - } - if (copy_from_user(sops, tsops, nsops * sizeof(*tsops)) || - get_compat_timespec(&t, timeout32)) - ret = -EFAULT; - else { - oldfs = get_fs(); - set_fs(KERNEL_DS); - ret = sys_semtimedop(semid, sops, nsops, &t); - set_fs(oldfs); - } - if (sops != fast_sops) - kfree(sops); - return ret; -} - -#define IPCOP_MASK(__x) (1UL << (__x)) -static int do_sys32_semctl(int first, int second, int third, void *uptr) -{ - union semun fourth; - u32 pad; - int err = -EINVAL; - - if (!uptr) - goto out; - err = -EFAULT; - if (get_user (pad, (u32 *)uptr)) - goto out; - if(third == SETVAL) - fourth.val = (int)pad; - else - fourth.__pad = (void *)A(pad); - if (IPCOP_MASK (third) & - (IPCOP_MASK (IPC_INFO) | IPCOP_MASK (SEM_INFO) | IPCOP_MASK (GETVAL) | - IPCOP_MASK (GETPID) | IPCOP_MASK (GETNCNT) | IPCOP_MASK (GETZCNT) | - IPCOP_MASK (GETALL) | IPCOP_MASK (SETALL) | IPCOP_MASK (IPC_RMID))) { - err = sys_semctl (first, second, third, fourth); - } else if (third & IPC_64) { - struct semid64_ds s; - struct semid64_ds32 *usp = (struct semid64_ds32 *)A(pad); - mm_segment_t old_fs; - int need_back_translation; - - if (third == (IPC_SET|IPC_64)) { - err = get_user (s.sem_perm.uid, &usp->sem_perm.uid); - err |= __get_user (s.sem_perm.gid, &usp->sem_perm.gid); - err |= __get_user (s.sem_perm.mode, &usp->sem_perm.mode); - if (err) - goto out; - fourth.__pad = &s; - } - need_back_translation = - (IPCOP_MASK (third) & - (IPCOP_MASK (SEM_STAT) | IPCOP_MASK (IPC_STAT))) != 0; - if (need_back_translation) - fourth.__pad = &s; - old_fs = get_fs (); - set_fs (KERNEL_DS); - err = sys_semctl (first, second, third, fourth); - set_fs (old_fs); - if (need_back_translation) { - int err2 = put_user (s.sem_perm.key, &usp->sem_perm.key); - err2 |= __put_user (high2lowuid(s.sem_perm.uid), &usp->sem_perm.uid); - err2 |= __put_user (high2lowgid(s.sem_perm.gid), &usp->sem_perm.gid); - err2 |= __put_user (high2lowuid(s.sem_perm.cuid), &usp->sem_perm.cuid); - err2 |= __put_user (high2lowgid(s.sem_perm.cgid), &usp->sem_perm.cgid); - err2 |= __put_user (s.sem_perm.mode, &usp->sem_perm.mode); - err2 |= __put_user (s.sem_perm.seq, &usp->sem_perm.seq); - err2 |= __put_user (s.sem_otime, &usp->sem_otime); - err2 |= __put_user (s.sem_ctime, &usp->sem_ctime); - err2 |= __put_user (s.sem_nsems, &usp->sem_nsems); - if (err2) err = -EFAULT; - } - } else { - struct semid_ds s; - struct semid_ds32 *usp = (struct semid_ds32 *)A(pad); - mm_segment_t old_fs; - int need_back_translation; - - if (third == IPC_SET) { - err = get_user (s.sem_perm.uid, &usp->sem_perm.uid); - err |= __get_user (s.sem_perm.gid, &usp->sem_perm.gid); - err |= __get_user (s.sem_perm.mode, &usp->sem_perm.mode); - if (err) - goto out; - fourth.__pad = &s; - } - need_back_translation = - (IPCOP_MASK (third) & - (IPCOP_MASK (SEM_STAT) | IPCOP_MASK (IPC_STAT))) != 0; - if (need_back_translation) - fourth.__pad = &s; - old_fs = get_fs (); - set_fs (KERNEL_DS); - err = sys_semctl (first, second, third, fourth); - set_fs (old_fs); - if (need_back_translation) { - int err2 = put_user (s.sem_perm.key, &usp->sem_perm.key); - err2 |= __put_user (high2lowuid(s.sem_perm.uid), &usp->sem_perm.uid); - err2 |= __put_user (high2lowgid(s.sem_perm.gid), &usp->sem_perm.gid); - err2 |= __put_user (high2lowuid(s.sem_perm.cuid), &usp->sem_perm.cuid); - err2 |= __put_user (high2lowgid(s.sem_perm.cgid), &usp->sem_perm.cgid); - err2 |= __put_user (s.sem_perm.mode, &usp->sem_perm.mode); - err2 |= __put_user (s.sem_perm.seq, &usp->sem_perm.seq); - err2 |= __put_user (s.sem_otime, &usp->sem_otime); - err2 |= __put_user (s.sem_ctime, &usp->sem_ctime); - err2 |= __put_user (s.sem_nsems, &usp->sem_nsems); - if (err2) err = -EFAULT; - } - } -out: - return err; -} - -static int do_sys32_msgsnd (int first, int second, int third, void *uptr) -{ - struct msgbuf *p = kmalloc (second + sizeof (struct msgbuf), GFP_USER); - struct msgbuf32 *up = (struct msgbuf32 *)uptr; - mm_segment_t old_fs; - int err; - - if (!p) - return -ENOMEM; - - err = -EINVAL; - if (second > MSGMAX || first < 0 || second < 0) - goto out; - - err = -EFAULT; - if (!uptr) - goto out; - if (get_user (p->mtype, &up->mtype) || - __copy_from_user (p->mtext, &up->mtext, second)) - goto out; - old_fs = get_fs (); - set_fs (KERNEL_DS); - err = sys_msgsnd (first, p, second, third); - set_fs (old_fs); -out: - kfree (p); - return err; -} - -static int do_sys32_msgrcv (int first, int second, int msgtyp, int third, - int version, void *uptr) -{ - struct msgbuf32 *up; - struct msgbuf *p; - mm_segment_t old_fs; - int err; - - if (first < 0 || second < 0) - return -EINVAL; - - if (!version) { - struct ipc_kludge_32 *uipck = (struct ipc_kludge_32 *)uptr; - struct ipc_kludge_32 ipck; - - err = -EINVAL; - if (!uptr) - goto out; - err = -EFAULT; - if (copy_from_user (&ipck, uipck, sizeof (struct ipc_kludge_32))) - goto out; - uptr = (void *)A(ipck.msgp); - msgtyp = ipck.msgtyp; - } - err = -ENOMEM; - p = kmalloc (second + sizeof (struct msgbuf), GFP_USER); - if (!p) - goto out; - old_fs = get_fs (); - set_fs (KERNEL_DS); - err = sys_msgrcv (first, p, second, msgtyp, third); - set_fs (old_fs); - if (err < 0) - goto free_then_out; - up = (struct msgbuf32 *)uptr; - if (put_user (p->mtype, &up->mtype) || - __copy_to_user (&up->mtext, p->mtext, err)) - err = -EFAULT; -free_then_out: - kfree (p); -out: - return err; -} - -static int do_sys32_msgctl (int first, int second, void *uptr) -{ - int err; - - if (IPCOP_MASK (second) & - (IPCOP_MASK (IPC_INFO) | IPCOP_MASK (MSG_INFO) | - IPCOP_MASK (IPC_RMID))) { - err = sys_msgctl (first, second, (struct msqid_ds *)uptr); - } else if (second & IPC_64) { - struct msqid64_ds m; - struct msqid64_ds32 *up = (struct msqid64_ds32 *)uptr; - mm_segment_t old_fs; - - if (second == (IPC_SET|IPC_64)) { - err = get_user (m.msg_perm.uid, &up->msg_perm.uid); - err |= __get_user (m.msg_perm.gid, &up->msg_perm.gid); - err |= __get_user (m.msg_perm.mode, &up->msg_perm.mode); - err |= __get_user (m.msg_qbytes, &up->msg_qbytes); - if (err) - goto out; - } - old_fs = get_fs (); - set_fs (KERNEL_DS); - err = sys_msgctl (first, second, (struct msqid_ds *)&m); - set_fs (old_fs); - if (IPCOP_MASK (second) & - (IPCOP_MASK (MSG_STAT) | IPCOP_MASK (IPC_STAT))) { - int err2 = put_user (m.msg_perm.key, &up->msg_perm.key); - err2 |= __put_user (high2lowuid(m.msg_perm.uid), &up->msg_perm.uid); - err2 |= __put_user (high2lowgid(m.msg_perm.gid), &up->msg_perm.gid); - err2 |= __put_user (high2lowuid(m.msg_perm.cuid), &up->msg_perm.cuid); - err2 |= __put_user (high2lowgid(m.msg_perm.cgid), &up->msg_perm.cgid); - err2 |= __put_user (m.msg_perm.mode, &up->msg_perm.mode); - err2 |= __put_user (m.msg_perm.seq, &up->msg_perm.seq); - err2 |= __put_user (m.msg_stime, &up->msg_stime); - err2 |= __put_user (m.msg_rtime, &up->msg_rtime); - err2 |= __put_user (m.msg_ctime, &up->msg_ctime); - err2 |= __put_user (m.msg_cbytes, &up->msg_cbytes); - err2 |= __put_user (m.msg_qnum, &up->msg_qnum); - err2 |= __put_user (m.msg_qbytes, &up->msg_qbytes); - err2 |= __put_user (m.msg_lspid, &up->msg_lspid); - err2 |= __put_user (m.msg_lrpid, &up->msg_lrpid); - if (err2) - err = -EFAULT; - } - } else { - struct msqid_ds m; - struct msqid_ds32 *up = (struct msqid_ds32 *)uptr; - mm_segment_t old_fs; - - if (second == IPC_SET) { - err = get_user (m.msg_perm.uid, &up->msg_perm.uid); - err |= __get_user (m.msg_perm.gid, &up->msg_perm.gid); - err |= __get_user (m.msg_perm.mode, &up->msg_perm.mode); - err |= __get_user (m.msg_qbytes, &up->msg_qbytes); - if (err) - goto out; - } - old_fs = get_fs (); - set_fs (KERNEL_DS); - err = sys_msgctl (first, second, &m); - set_fs (old_fs); - if (IPCOP_MASK (second) & - (IPCOP_MASK (MSG_STAT) | IPCOP_MASK (IPC_STAT))) { - int err2 = put_user (m.msg_perm.key, &up->msg_perm.key); - err2 |= __put_user (high2lowuid(m.msg_perm.uid), &up->msg_perm.uid); - err2 |= __put_user (high2lowgid(m.msg_perm.gid), &up->msg_perm.gid); - err2 |= __put_user (high2lowuid(m.msg_perm.cuid), &up->msg_perm.cuid); - err2 |= __put_user (high2lowgid(m.msg_perm.cgid), &up->msg_perm.cgid); - err2 |= __put_user (m.msg_perm.mode, &up->msg_perm.mode); - err2 |= __put_user (m.msg_perm.seq, &up->msg_perm.seq); - err2 |= __put_user (m.msg_stime, &up->msg_stime); - err2 |= __put_user (m.msg_rtime, &up->msg_rtime); - err2 |= __put_user (m.msg_ctime, &up->msg_ctime); - err2 |= __put_user (m.msg_cbytes, &up->msg_cbytes); - err2 |= __put_user (m.msg_qnum, &up->msg_qnum); - err2 |= __put_user (m.msg_qbytes, &up->msg_qbytes); - err2 |= __put_user (m.msg_lspid, &up->msg_lspid); - err2 |= __put_user (m.msg_lrpid, &up->msg_lrpid); - if (err2) - err = -EFAULT; - } - } - -out: - return err; -} - -static int do_sys32_shmat (int first, int second, int third, int version, void *uptr) -{ - unsigned long raddr; - u32 *uaddr = (u32 *)A((u32)third); - int err = -EINVAL; - - if (version == 1) - goto out; - err = do_shmat (first, uptr, second, &raddr); - if (err) - goto out; - err = put_user (raddr, uaddr); -out: - return err; -} - -static int do_sys32_shmctl (int first, int second, void *uptr) -{ - int err; - - if (IPCOP_MASK (second) & - (IPCOP_MASK (IPC_INFO) | IPCOP_MASK (SHM_LOCK) | IPCOP_MASK (SHM_UNLOCK) | - IPCOP_MASK (IPC_RMID))) { - if (second == (IPC_INFO|IPC_64)) - second = IPC_INFO; /* So that we don't have to translate it */ - err = sys_shmctl (first, second, (struct shmid_ds *)uptr); - } else if ((second & IPC_64) && second != (SHM_INFO|IPC_64)) { - struct shmid64_ds s; - struct shmid64_ds32 *up = (struct shmid64_ds32 *)uptr; - mm_segment_t old_fs; - - if (second == (IPC_SET|IPC_64)) { - err = get_user (s.shm_perm.uid, &up->shm_perm.uid); - err |= __get_user (s.shm_perm.gid, &up->shm_perm.gid); - err |= __get_user (s.shm_perm.mode, &up->shm_perm.mode); - if (err) - goto out; - } - old_fs = get_fs (); - set_fs (KERNEL_DS); - err = sys_shmctl (first, second, (struct shmid_ds *)&s); - set_fs (old_fs); - if (err < 0) - goto out; - - /* Mask it even in this case so it becomes a CSE. */ - if (IPCOP_MASK (second) & - (IPCOP_MASK (SHM_STAT) | IPCOP_MASK (IPC_STAT))) { - int err2 = put_user (s.shm_perm.key, &up->shm_perm.key); - err2 |= __put_user (high2lowuid(s.shm_perm.uid), &up->shm_perm.uid); - err2 |= __put_user (high2lowgid(s.shm_perm.gid), &up->shm_perm.gid); - err2 |= __put_user (high2lowuid(s.shm_perm.cuid), &up->shm_perm.cuid); - err2 |= __put_user (high2lowgid(s.shm_perm.cgid), &up->shm_perm.cgid); - err2 |= __put_user (s.shm_perm.mode, &up->shm_perm.mode); - err2 |= __put_user (s.shm_perm.seq, &up->shm_perm.seq); - err2 |= __put_user (s.shm_atime, &up->shm_atime); - err2 |= __put_user (s.shm_dtime, &up->shm_dtime); - err2 |= __put_user (s.shm_ctime, &up->shm_ctime); - err2 |= __put_user (s.shm_segsz, &up->shm_segsz); - err2 |= __put_user (s.shm_nattch, &up->shm_nattch); - err2 |= __put_user (s.shm_cpid, &up->shm_cpid); - err2 |= __put_user (s.shm_lpid, &up->shm_lpid); - if (err2) - err = -EFAULT; - } - } else { - struct shmid_ds s; - struct shmid_ds32 *up = (struct shmid_ds32 *)uptr; - mm_segment_t old_fs; - - second &= ~IPC_64; - if (second == IPC_SET) { - err = get_user (s.shm_perm.uid, &up->shm_perm.uid); - err |= __get_user (s.shm_perm.gid, &up->shm_perm.gid); - err |= __get_user (s.shm_perm.mode, &up->shm_perm.mode); - if (err) - goto out; - } - old_fs = get_fs (); - set_fs (KERNEL_DS); - err = sys_shmctl (first, second, &s); - set_fs (old_fs); - if (err < 0) - goto out; - - /* Mask it even in this case so it becomes a CSE. */ - if (second == SHM_INFO) { - struct shm_info32 { - int used_ids; - u32 shm_tot, shm_rss, shm_swp; - u32 swap_attempts, swap_successes; - } *uip = (struct shm_info32 *)uptr; - struct shm_info *kp = (struct shm_info *)&s; - int err2 = put_user (kp->used_ids, &uip->used_ids); - err2 |= __put_user (kp->shm_tot, &uip->shm_tot); - err2 |= __put_user (kp->shm_rss, &uip->shm_rss); - err2 |= __put_user (kp->shm_swp, &uip->shm_swp); - err2 |= __put_user (kp->swap_attempts, &uip->swap_attempts); - err2 |= __put_user (kp->swap_successes, &uip->swap_successes); - if (err2) - err = -EFAULT; - } else if (IPCOP_MASK (second) & - (IPCOP_MASK (SHM_STAT) | IPCOP_MASK (IPC_STAT))) { - int err2 = put_user (s.shm_perm.key, &up->shm_perm.key); - err2 |= __put_user (high2lowuid(s.shm_perm.uid), &up->shm_perm.uid); - err2 |= __put_user (high2lowgid(s.shm_perm.gid), &up->shm_perm.gid); - err2 |= __put_user (high2lowuid(s.shm_perm.cuid), &up->shm_perm.cuid); - err2 |= __put_user (high2lowgid(s.shm_perm.cgid), &up->shm_perm.cgid); - err2 |= __put_user (s.shm_perm.mode, &up->shm_perm.mode); - err2 |= __put_user (s.shm_perm.seq, &up->shm_perm.seq); - err2 |= __put_user (s.shm_atime, &up->shm_atime); - err2 |= __put_user (s.shm_dtime, &up->shm_dtime); - err2 |= __put_user (s.shm_ctime, &up->shm_ctime); - err2 |= __put_user (s.shm_segsz, &up->shm_segsz); - err2 |= __put_user (s.shm_nattch, &up->shm_nattch); - err2 |= __put_user (s.shm_cpid, &up->shm_cpid); - err2 |= __put_user (s.shm_lpid, &up->shm_lpid); - if (err2) - err = -EFAULT; - } - } -out: - return err; -} - /* * sys32_ipc() is the de-multiplexer for the SysV IPC calls in 32bit emulation. * @@ -835,84 +300,64 @@ out: */ asmlinkage int sys32_ipc (u32 call, int first, int second, int third, u32 ptr) { - int version, err; + if(call >> 16) /* hack for backward compatibility */ + return -EINVAL; - version = call >> 16; /* hack for backward compatibility */ call &= 0xffff; - if(version) - return -EINVAL; - if (call <= SEMTIMEDOP) switch (call) { case SEMTIMEDOP: - if (third) { - err = do_sys32_semtimedop(first, - (struct sembuf *)AA(ptr), - second, - (struct compat_timespec *) - AA((u32)third)); - goto out; - } + if (third) + return compat_sys_semtimedop(first, + compat_ptr(ptr), second, + compat_ptr(third)); /* else fall through for normal semop() */ case SEMOP: /* struct sembuf is the same on 32 and 64bit :)) */ - err = sys_semtimedop (first, (struct sembuf *)AA(ptr), + return sys_semtimedop (first, compat_ptr(ptr), second, NULL); - goto out; case SEMGET: - err = sys_semget (first, second, third); - goto out; + return sys_semget (first, second, third); case SEMCTL: - err = do_sys32_semctl (first, second, third, (void *)AA(ptr)); - goto out; + return compat_sys_semctl (first, second, third, + compat_ptr(ptr)); default: - err = -EINVAL; - goto out; + return -EINVAL; }; if (call <= MSGCTL) switch (call) { case MSGSND: - err = do_sys32_msgsnd (first, second, third, (void *)AA(ptr)); - goto out; + return compat_sys_msgsnd (first, second, third, + compat_ptr(ptr)); case MSGRCV: - err = do_sys32_msgrcv (first, second, 0, third, - version, (void *)AA(ptr)); - goto out; + return compat_sys_msgrcv (first, second, 0, third, + 0, compat_ptr(ptr)); case MSGGET: - err = sys_msgget ((key_t) first, second); - goto out; + return sys_msgget ((key_t) first, second); case MSGCTL: - err = do_sys32_msgctl (first, second, (void *)AA(ptr)); - goto out; + return compat_sys_msgctl (first, second, + compat_ptr(ptr)); default: - err = -EINVAL; - goto out; + return -EINVAL; } if (call <= SHMCTL) switch (call) { case SHMAT: - err = do_sys32_shmat (first, second, third, - version, (void *)AA(ptr)); - goto out; + return compat_sys_shmat (first, second, third, + 0, compat_ptr(ptr)); case SHMDT: - err = sys_shmdt ((char *)AA(ptr)); - goto out; + return sys_shmdt(compat_ptr(ptr)); case SHMGET: - err = sys_shmget (first, second, third); - goto out; + return sys_shmget(first, second, third); case SHMCTL: - err = do_sys32_shmctl (first, second, (void *)AA(ptr)); - goto out; + return compat_sys_shmctl(first, second, + compat_ptr(ptr)); default: - err = -EINVAL; - goto out; + return -EINVAL; } - err = -EINVAL; - -out: - return err; + return -EINVAL; } asmlinkage int sys32_truncate64(const char * path, unsigned long high, unsigned long low) --- linux-2.6.4-rc2/arch/s390/kernel/syscalls.S 2004-02-03 20:42:35.000000000 -0800 +++ 25/arch/s390/kernel/syscalls.S 2004-03-07 20:47:07.000000000 -0800 @@ -109,7 +109,7 @@ SYSCALL(sys_setpriority,sys_setpriority, NI_SYSCALL /* old profil syscall */ SYSCALL(sys_statfs,sys_statfs,compat_sys_statfs_wrapper) SYSCALL(sys_fstatfs,sys_fstatfs,compat_sys_fstatfs_wrapper) /* 100 */ -SYSCALL(sys_ioperm,sys_ni_syscall,sys_ni_syscall) +NI_SYSCALL /* ioperm for i386 */ SYSCALL(sys_socketcall,sys_socketcall,compat_sys_socketcall_wrapper) SYSCALL(sys_syslog,sys_syslog,sys32_syslog_wrapper) SYSCALL(sys_setitimer,sys_setitimer,compat_sys_setitimer_wrapper) --- linux-2.6.4-rc2/arch/s390/kernel/sys_s390.c 2004-03-03 23:12:43.000000000 -0800 +++ 25/arch/s390/kernel/sys_s390.c 2004-03-07 20:47:07.000000000 -0800 @@ -289,11 +289,6 @@ asmlinkage int sys_olduname(struct oldol return error; } -asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int on) -{ - return -ENOSYS; -} - #else /* CONFIG_ARCH_S390X */ asmlinkage int s390x_newuname(struct new_utsname * name) --- linux-2.6.4-rc2/arch/sparc64/defconfig 2004-03-03 23:12:43.000000000 -0800 +++ 25/arch/sparc64/defconfig 2004-03-07 20:46:46.000000000 -0800 @@ -100,19 +100,19 @@ CONFIG_PARPORT_1284=y CONFIG_PRINTER=m CONFIG_ENVCTRL=m CONFIG_DISPLAY7SEG=m -CONFIG_WATCHDOG_CP1XXX=m -CONFIG_WATCHDOG_RIO=m # CONFIG_CMDLINE_BOOL is not set # # Generic Driver Options # CONFIG_FW_LOADER=m +# CONFIG_DEBUG_DRIVER is not set # # Graphics support # CONFIG_FB=y +# CONFIG_FB_PM2 is not set # CONFIG_FB_CYBER2000 is not set # CONFIG_FB_IMSTT is not set # CONFIG_FB_BW2 is not set @@ -211,7 +211,6 @@ CONFIG_BLK_DEV_CRYPTOLOOP=m CONFIG_BLK_DEV_NBD=m # CONFIG_BLK_DEV_RAM is not set # CONFIG_BLK_DEV_INITRD is not set -CONFIG_DCSSBLK=m # # ATA/ATAPI/MFM/RLL support @@ -407,6 +406,8 @@ CONFIG_IEEE1394=m # # CONFIG_IEEE1394_VERBOSEDEBUG is not set CONFIG_IEEE1394_OUI_DB=y +CONFIG_IEEE1394_EXTRA_CONFIG_ROMS=y +CONFIG_IEEE1394_CONFIG_ROM_IP1394=y # # Device Drivers @@ -1638,6 +1639,8 @@ CONFIG_WATCHDOG=y # Watchdog Device Drivers # CONFIG_SOFT_WATCHDOG=m +CONFIG_WATCHDOG_CP1XXX=m +CONFIG_WATCHDOG_RIO=m # # PCI-based Watchdog Cards @@ -1647,6 +1650,11 @@ CONFIG_WDTPCI=m CONFIG_WDT_501_PCI=y # +# USB-based Watchdog Cards +# +CONFIG_USBPCWATCHDOG=m + +# # Profiling support # CONFIG_PROFILING=y @@ -1691,6 +1699,7 @@ CONFIG_CRYPTO_SERPENT=m CONFIG_CRYPTO_AES=m CONFIG_CRYPTO_CAST5=m CONFIG_CRYPTO_CAST6=m +CONFIG_CRYPTO_ARC4=m CONFIG_CRYPTO_DEFLATE=y CONFIG_CRYPTO_TEST=m --- linux-2.6.4-rc2/arch/sparc64/Kconfig 2004-03-03 23:12:43.000000000 -0800 +++ 25/arch/sparc64/Kconfig 2004-03-07 20:48:17.000000000 -0800 @@ -687,12 +687,19 @@ config DEBUG_BOOTMEM depends on DEBUG_KERNEL bool "Debug BOOTMEM initialization" +config LOCKMETER + bool "Kernel lock metering" + depends on SMP && !PREEMPT + help + Say Y to enable kernel lock metering, which adds overhead to SMP locks, + but allows you to see various statistics using the lockstat command. + # We have a custom atomic_dec_and_lock() implementation but it's not # compatible with spinlock debugging so we need to fall back on # the generic version in that case. config HAVE_DEC_LOCK bool - depends on SMP && !DEBUG_SPINLOCK + depends on SMP && !DEBUG_SPINLOCK && !LOCKMETER default y config MCOUNT --- linux-2.6.4-rc2/arch/sparc64/kernel/ioctl32.c 2004-03-03 23:12:43.000000000 -0800 +++ 25/arch/sparc64/kernel/ioctl32.c 2004-03-07 20:46:46.000000000 -0800 @@ -1117,34 +1117,6 @@ COMPATIBLE_IOCTL(BNEPCONNADD) COMPATIBLE_IOCTL(BNEPCONNDEL) COMPATIBLE_IOCTL(BNEPGETCONNLIST) COMPATIBLE_IOCTL(BNEPGETCONNINFO) -/* device-mapper */ -#if defined(CONFIG_DM_IOCTL_V4) -COMPATIBLE_IOCTL(DM_VERSION) -COMPATIBLE_IOCTL(DM_REMOVE_ALL) -COMPATIBLE_IOCTL(DM_LIST_DEVICES) -COMPATIBLE_IOCTL(DM_DEV_CREATE) -COMPATIBLE_IOCTL(DM_DEV_REMOVE) -COMPATIBLE_IOCTL(DM_DEV_RENAME) -COMPATIBLE_IOCTL(DM_DEV_SUSPEND) -COMPATIBLE_IOCTL(DM_DEV_STATUS) -COMPATIBLE_IOCTL(DM_DEV_WAIT) -COMPATIBLE_IOCTL(DM_TABLE_LOAD) -COMPATIBLE_IOCTL(DM_TABLE_CLEAR) -COMPATIBLE_IOCTL(DM_TABLE_DEPS) -COMPATIBLE_IOCTL(DM_TABLE_STATUS) -#else -COMPATIBLE_IOCTL(DM_VERSION) -COMPATIBLE_IOCTL(DM_REMOVE_ALL) -COMPATIBLE_IOCTL(DM_DEV_CREATE) -COMPATIBLE_IOCTL(DM_DEV_REMOVE) -COMPATIBLE_IOCTL(DM_DEV_RELOAD) -COMPATIBLE_IOCTL(DM_DEV_SUSPEND) -COMPATIBLE_IOCTL(DM_DEV_RENAME) -COMPATIBLE_IOCTL(DM_DEV_DEPS) -COMPATIBLE_IOCTL(DM_DEV_STATUS) -COMPATIBLE_IOCTL(DM_TARGET_STATUS) -COMPATIBLE_IOCTL(DM_TARGET_WAIT) -#endif /* And these ioctls need translation */ /* NCPFS */ HANDLE_IOCTL(NCP_IOC_NCPREQUEST_32, do_ncp_ncprequest) --- linux-2.6.4-rc2/arch/sparc64/kernel/pci_iommu.c 2003-08-08 22:55:11.000000000 -0700 +++ 25/arch/sparc64/kernel/pci_iommu.c 2004-03-07 20:46:58.000000000 -0800 @@ -661,7 +661,7 @@ void pci_unmap_sg(struct pci_dev *pdev, /* Make physical memory consistent for a single * streaming mode DMA translation after a transfer. */ -void pci_dma_sync_single(struct pci_dev *pdev, dma_addr_t bus_addr, size_t sz, int direction) +void pci_dma_sync_single_for_cpu(struct pci_dev *pdev, dma_addr_t bus_addr, size_t sz, int direction) { struct pcidev_cookie *pcp; struct pci_iommu *iommu; @@ -722,7 +722,7 @@ void pci_dma_sync_single(struct pci_dev /* Make physical memory consistent for a set of streaming * mode DMA translations after a transfer. */ -void pci_dma_sync_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems, int direction) +void pci_dma_sync_sg_for_cpu(struct pci_dev *pdev, struct scatterlist *sglist, int nelems, int direction) { struct pcidev_cookie *pcp; struct pci_iommu *iommu; --- linux-2.6.4-rc2/arch/sparc64/kernel/process.c 2003-10-08 15:07:08.000000000 -0700 +++ 25/arch/sparc64/kernel/process.c 2004-03-07 20:47:47.000000000 -0800 @@ -10,7 +10,6 @@ * This file handles the architecture-dependent parts of process handling.. */ -#define __KERNEL_SYSCALLS__ #include #include @@ -22,7 +21,6 @@ #include #include #include -#include #include #include #include --- linux-2.6.4-rc2/arch/sparc64/kernel/sbus.c 2003-06-14 12:18:51.000000000 -0700 +++ 25/arch/sparc64/kernel/sbus.c 2004-03-07 20:46:58.000000000 -0800 @@ -540,7 +540,7 @@ void sbus_unmap_sg(struct sbus_dev *sdev spin_unlock_irqrestore(&iommu->lock, flags); } -void sbus_dma_sync_single(struct sbus_dev *sdev, dma_addr_t base, size_t size, int direction) +void sbus_dma_sync_single_for_cpu(struct sbus_dev *sdev, dma_addr_t base, size_t size, int direction) { struct sbus_iommu *iommu = sdev->bus->iommu; unsigned long flags; @@ -552,7 +552,11 @@ void sbus_dma_sync_single(struct sbus_de spin_unlock_irqrestore(&iommu->lock, flags); } -void sbus_dma_sync_sg(struct sbus_dev *sdev, struct scatterlist *sg, int nents, int direction) +void sbus_dma_sync_single_for_device(struct sbus_dev *sdev, dma_addr_t base, size_t size, int direction) +{ +} + +void sbus_dma_sync_sg_for_cpu(struct sbus_dev *sdev, struct scatterlist *sg, int nents, int direction) { struct sbus_iommu *iommu = sdev->bus->iommu; unsigned long flags, size; @@ -572,6 +576,10 @@ void sbus_dma_sync_sg(struct sbus_dev *s spin_unlock_irqrestore(&iommu->lock, flags); } +void sbus_dma_sync_sg_for_device(struct sbus_dev *sdev, struct scatterlist *sg, int nents, int direction) +{ +} + /* Enable 64-bit DVMA mode for the given device. */ void sbus_set_sbus64(struct sbus_dev *sdev, int bursts) { --- linux-2.6.4-rc2/arch/sparc64/kernel/setup.c 2004-03-03 23:12:43.000000000 -0800 +++ 25/arch/sparc64/kernel/setup.c 2004-03-07 20:47:07.000000000 -0800 @@ -603,11 +603,6 @@ static int __init set_preferred_console( } console_initcall(set_preferred_console); -asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int on) -{ - return -EIO; -} - /* BUFFER is PAGE_SIZE bytes long. */ extern char *sparc_cpu_type; --- linux-2.6.4-rc2/arch/sparc64/kernel/smp.c 2004-02-03 20:42:35.000000000 -0800 +++ 25/arch/sparc64/kernel/smp.c 2004-03-07 20:47:47.000000000 -0800 @@ -36,9 +36,6 @@ #include #include -#define __KERNEL_SYSCALLS__ -#include - extern int linux_num_cpus; extern void calibrate_delay(void); @@ -46,7 +43,6 @@ extern void calibrate_delay(void); static unsigned char boot_cpu_id; cpumask_t cpu_online_map = CPU_MASK_NONE; -atomic_t sparc64_num_cpus_possible = ATOMIC_INIT(0); cpumask_t phys_cpu_present_map = CPU_MASK_NONE; static cpumask_t smp_commenced_mask; static cpumask_t cpu_callout_map; @@ -1236,20 +1232,17 @@ void __init smp_prepare_cpus(unsigned in instance = 0; while (!cpu_find_by_instance(instance, NULL, &mid)) { - if (mid < max_cpus) { + if (mid < max_cpus) cpu_set(mid, phys_cpu_present_map); - atomic_inc(&sparc64_num_cpus_possible); - } instance++; } - if (atomic_read(&sparc64_num_cpus_possible) > max_cpus) { + if (num_possible_cpus() > max_cpus) { instance = 0; while (!cpu_find_by_instance(instance, NULL, &mid)) { if (mid != boot_cpu_id) { cpu_clear(mid, phys_cpu_present_map); - atomic_dec(&sparc64_num_cpus_possible); - if (atomic_read(&sparc64_num_cpus_possible) <= max_cpus) + if (num_possible_cpus() <= max_cpus) break; } instance++; --- linux-2.6.4-rc2/arch/sparc64/kernel/sparc64_ksyms.c 2004-03-03 23:12:43.000000000 -0800 +++ 25/arch/sparc64/kernel/sparc64_ksyms.c 2004-03-07 20:47:24.000000000 -0800 @@ -145,7 +145,6 @@ EXPORT_SYMBOL_NOVERS(mcount); /* CPU online map and active count. */ EXPORT_SYMBOL(cpu_online_map); EXPORT_SYMBOL(phys_cpu_present_map); -EXPORT_SYMBOL(sparc64_num_cpus_possible); /* Spinlock debugging library, optional. */ #ifdef CONFIG_DEBUG_SPINLOCK @@ -214,8 +213,8 @@ EXPORT_SYMBOL(sbus_map_single); EXPORT_SYMBOL(sbus_unmap_single); EXPORT_SYMBOL(sbus_map_sg); EXPORT_SYMBOL(sbus_unmap_sg); -EXPORT_SYMBOL(sbus_dma_sync_single); -EXPORT_SYMBOL(sbus_dma_sync_sg); +EXPORT_SYMBOL(sbus_dma_sync_single_for_cpu); +EXPORT_SYMBOL(sbus_dma_sync_sg_for_cpu); #endif EXPORT_SYMBOL(outsb); EXPORT_SYMBOL(outsw); @@ -233,8 +232,8 @@ EXPORT_SYMBOL(pci_map_single); EXPORT_SYMBOL(pci_unmap_single); EXPORT_SYMBOL(pci_map_sg); EXPORT_SYMBOL(pci_unmap_sg); -EXPORT_SYMBOL(pci_dma_sync_single); -EXPORT_SYMBOL(pci_dma_sync_sg); +EXPORT_SYMBOL(pci_dma_sync_single_for_cpu); +EXPORT_SYMBOL(pci_dma_sync_sg_for_cpu); EXPORT_SYMBOL(pci_dma_supported); #endif --- linux-2.6.4-rc2/arch/sparc64/kernel/sys_sparc32.c 2004-03-03 23:12:43.000000000 -0800 +++ 25/arch/sparc64/kernel/sys_sparc32.c 2004-03-07 20:47:07.000000000 -0800 @@ -282,11 +282,6 @@ static inline long put_tv32(struct compa __put_user(i->tv_usec, &o->tv_usec))); } -asmlinkage long sys32_ioperm(u32 from, u32 num, int on) -{ - return sys_ioperm((unsigned long)from, (unsigned long)num, on); -} - struct msgbuf32 { s32 mtype; char mtext[1]; }; struct ipc_perm32 --- linux-2.6.4-rc2/arch/sparc64/lib/rwlock.S 2003-11-23 19:03:00.000000000 -0800 +++ 25/arch/sparc64/lib/rwlock.S 2004-03-07 20:48:17.000000000 -0800 @@ -85,5 +85,20 @@ __write_trylock_succeed: __write_trylock_fail: retl mov 0, %o0 + + .globl __read_trylock +__read_trylock: /* %o0 = lock_ptr */ + ldsw [%o0], %g5 + brlz,pn %g5, 100f + add %g5, 1, %g7 + cas [%o0], %g5, %g7 + cmp %g5, %g7 + bne,pn %icc, __read_trylock + membar #StoreLoad | #StoreStore + retl + mov 1, %o0 +100: retl + mov 0, %o0 + rwlock_impl_end: --- linux-2.6.4-rc2/arch/sparc/Kconfig 2004-02-17 20:48:42.000000000 -0800 +++ 25/arch/sparc/Kconfig 2004-03-07 20:46:46.000000000 -0800 @@ -380,11 +380,32 @@ source "drivers/char/watchdog/Kconfig" menu "Kernel hacking" +config DEBUG_KERNEL + bool "Kernel debugging" + help + Say Y here if you are developing drivers or trying to debug and + identify kernel problems. + +config DEBUG_STACK_USAGE + bool "Enable stack utilization instrumentation" + depends on DEBUG_KERNEL + help + Enables the display of the minimum amount of free stack which each + task has ever had available in the sysrq-T and sysrq-P debug output. + + This option will slow down process creation somewhat. + config DEBUG_SLAB bool "Debug memory allocations" + depends on DEBUG_KERNEL + help + Say Y here to have the kernel do limited verification on memory + allocation as well as poisoning memory on free to catch use of freed + memory. config MAGIC_SYSRQ bool "Magic SysRq key" + depends on DEBUG_KERNEL help If you say Y here, you will have some control over the system even if the system crashes for example during kernel debugging (e.g., you @@ -398,22 +419,30 @@ config MAGIC_SYSRQ config DEBUG_SPINLOCK bool "Spinlock debugging" + depends on DEBUG_KERNEL + help + Say Y here and build SMP to catch missing spinlock initialization + and certain other kinds of spinlock errors commonly made. This is + best used in conjunction with the NMI watchdog so that spinlock + deadlocks are also debuggable. config DEBUG_HIGHMEM bool "Highmem debugging" depends on DEBUG_KERNEL && HIGHMEM help - This options enables addition error checking for high memory systems. - Disable for production systems. + This options enables additional error checking for high memory + systems. Disable for production systems. config DEBUG_SPINLOCK_SLEEP bool "Sleep-inside-spinlock checking" + depends on DEBUG_KERNEL help If you say Y here, various routines which may sleep will become very noisy if they are called with a spinlock held. config DEBUG_BUGVERBOSE bool "Verbose BUG() reporting (adds 70K)" + depends on DEBUG_KERNEL help Say Y here to make BUG() panics output the file name and line number of the BUG call as well as the EIP and oops trace. This aids --- linux-2.6.4-rc2/arch/sparc/kernel/ioport.c 2003-09-08 13:58:56.000000000 -0700 +++ 25/arch/sparc/kernel/ioport.c 2004-03-07 20:46:58.000000000 -0800 @@ -360,7 +360,7 @@ void sbus_unmap_sg(struct sbus_dev *sdev /* */ -void sbus_dma_sync_single(struct sbus_dev *sdev, dma_addr_t ba, size_t size, int direction) +void sbus_dma_sync_single_for_cpu(struct sbus_dev *sdev, dma_addr_t ba, size_t size, int direction) { #if 0 unsigned long va; @@ -380,9 +380,34 @@ void sbus_dma_sync_single(struct sbus_de #endif } -void sbus_dma_sync_sg(struct sbus_dev *sdev, struct scatterlist *sg, int n, int direction) +void sbus_dma_sync_single_for_device(struct sbus_dev *sdev, dma_addr_t ba, size_t size, int direction) { - printk("sbus_dma_sync_sg: not implemented yet\n"); +#if 0 + unsigned long va; + struct resource *res; + + /* We do not need the resource, just print a message if invalid. */ + res = _sparc_find_resource(&_sparc_dvma, ba); + if (res == NULL) + panic("sbus_dma_sync_single: 0x%x\n", ba); + + va = page_address(mmu_translate_dvma(ba)); /* XXX higmem */ + /* + * XXX This bogosity will be fixed with the iommu rewrite coming soon + * to a kernel near you. - Anton + */ + /* mmu_inval_dma_area(va, (size + PAGE_SIZE-1) & PAGE_MASK); */ +#endif +} + +void sbus_dma_sync_sg_for_cpu(struct sbus_dev *sdev, struct scatterlist *sg, int n, int direction) +{ + printk("sbus_dma_sync_sg_for_cpu: not implemented yet\n"); +} + +void sbus_dma_sync_sg_for_device(struct sbus_dev *sdev, struct scatterlist *sg, int n, int direction) +{ + printk("sbus_dma_sync_sg_for_device: not implemented yet\n"); } #endif /* CONFIG_SBUS */ @@ -482,7 +507,7 @@ void pci_free_consistent(struct pci_dev * The 32-bit bus address to use is returned. * * Once the device is given the dma address, the device owns this memory - * until either pci_unmap_single or pci_dma_sync_single is performed. + * until either pci_unmap_single or pci_dma_sync_single_* is performed. */ dma_addr_t pci_map_single(struct pci_dev *hwdev, void *ptr, size_t size, int direction) @@ -591,10 +616,21 @@ void pci_unmap_sg(struct pci_dev *hwdev, * If you perform a pci_map_single() but wish to interrogate the * buffer using the cpu, yet do not wish to teardown the PCI dma * mapping, you must call this function before doing so. At the - * next point you give the PCI dma address back to the card, the + * next point you give the PCI dma address back to the card, you + * must first perform a pci_dma_sync_for_device, and then the * device again owns the buffer. */ -void pci_dma_sync_single(struct pci_dev *hwdev, dma_addr_t ba, size_t size, int direction) +void pci_dma_sync_single_for_cpu(struct pci_dev *hwdev, dma_addr_t ba, size_t size, int direction) +{ + if (direction == PCI_DMA_NONE) + BUG(); + if (direction != PCI_DMA_TODEVICE) { + mmu_inval_dma_area((unsigned long)phys_to_virt(ba), + (size + PAGE_SIZE-1) & PAGE_MASK); + } +} + +void pci_dma_sync_single_for_device(struct pci_dev *hwdev, dma_addr_t ba, size_t size, int direction) { if (direction == PCI_DMA_NONE) BUG(); @@ -607,10 +643,27 @@ void pci_dma_sync_single(struct pci_dev /* Make physical memory consistent for a set of streaming * mode DMA translations after a transfer. * - * The same as pci_dma_sync_single but for a scatter-gather list, + * The same as pci_dma_sync_single_* but for a scatter-gather list, * same rules and usage. */ -void pci_dma_sync_sg(struct pci_dev *hwdev, struct scatterlist *sg, int nents, int direction) +void pci_dma_sync_sg_for_cpu(struct pci_dev *hwdev, struct scatterlist *sg, int nents, int direction) +{ + int n; + + if (direction == PCI_DMA_NONE) + BUG(); + if (direction != PCI_DMA_TODEVICE) { + for (n = 0; n < nents; n++) { + if (page_address(sg->page) == NULL) BUG(); + mmu_inval_dma_area( + (unsigned long) page_address(sg->page), + (sg->length + PAGE_SIZE-1) & PAGE_MASK); + sg++; + } + } +} + +void pci_dma_sync_sg_for_device(struct pci_dev *hwdev, struct scatterlist *sg, int nents, int direction) { int n; --- linux-2.6.4-rc2/arch/sparc/kernel/process.c 2004-03-03 23:12:43.000000000 -0800 +++ 25/arch/sparc/kernel/process.c 2004-03-07 20:47:47.000000000 -0800 @@ -9,7 +9,6 @@ * This file handles the architecture-dependent parts of process handling.. */ -#define __KERNEL_SYSCALLS__ #include #include @@ -19,7 +18,6 @@ #include #include #include -#include #include #include #include --- linux-2.6.4-rc2/arch/sparc/kernel/setup.c 2004-03-03 23:12:43.000000000 -0800 +++ 25/arch/sparc/kernel/setup.c 2004-03-07 20:47:07.000000000 -0800 @@ -389,11 +389,6 @@ static int __init set_preferred_console( } console_initcall(set_preferred_console); -asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int on) -{ - return -EIO; -} - extern char *sparc_cpu_type[]; extern char *sparc_fpu_type[]; --- linux-2.6.4-rc2/arch/sparc/kernel/smp.c 2004-03-03 23:12:43.000000000 -0800 +++ 25/arch/sparc/kernel/smp.c 2004-03-07 20:47:47.000000000 -0800 @@ -33,9 +33,6 @@ #include #include -#define __KERNEL_SYSCALLS__ -#include - #define IRQ_RESCHEDULE 13 #define IRQ_STOP_CPU 14 #define IRQ_CROSS_CALL 15 --- linux-2.6.4-rc2/arch/sparc/kernel/sparc_ksyms.c 2004-03-03 23:12:43.000000000 -0800 +++ 25/arch/sparc/kernel/sparc_ksyms.c 2004-03-07 20:46:58.000000000 -0800 @@ -157,7 +157,6 @@ EXPORT_SYMBOL(___change_bit); #ifdef CONFIG_SMP /* IRQ implementation. */ -EXPORT_SYMBOL(global_irq_holder); EXPORT_SYMBOL(synchronize_irq); /* Misc SMP information */ @@ -206,8 +205,10 @@ EXPORT_SYMBOL(sbus_map_single); EXPORT_SYMBOL(sbus_unmap_single); EXPORT_SYMBOL(sbus_map_sg); EXPORT_SYMBOL(sbus_unmap_sg); -EXPORT_SYMBOL(sbus_dma_sync_single); -EXPORT_SYMBOL(sbus_dma_sync_sg); +EXPORT_SYMBOL(sbus_dma_sync_single_for_cpu); +EXPORT_SYMBOL(sbus_dma_sync_single_for_device); +EXPORT_SYMBOL(sbus_dma_sync_sg_for_cpu); +EXPORT_SYMBOL(sbus_dma_sync_sg_for_device); EXPORT_SYMBOL(sbus_iounmap); EXPORT_SYMBOL(sbus_ioremap); #endif @@ -219,7 +220,10 @@ EXPORT_SYMBOL(pci_alloc_consistent); EXPORT_SYMBOL(pci_free_consistent); EXPORT_SYMBOL(pci_map_single); EXPORT_SYMBOL(pci_unmap_single); -EXPORT_SYMBOL(pci_dma_sync_single); +EXPORT_SYMBOL(pci_dma_sync_single_for_cpu); +EXPORT_SYMBOL(pci_dma_sync_single_for_device); +EXPORT_SYMBOL(pci_dma_sync_sg_for_cpu); +EXPORT_SYMBOL(pci_dma_sync_sg_for_device); /* Actually, ioremap/iounmap are not PCI specific. But it is ok for drivers. */ EXPORT_SYMBOL(ioremap); EXPORT_SYMBOL(iounmap); --- linux-2.6.4-rc2/arch/sparc/kernel/sun4d_smp.c 2003-09-27 18:57:44.000000000 -0700 +++ 25/arch/sparc/kernel/sun4d_smp.c 2004-03-07 20:47:47.000000000 -0800 @@ -32,9 +32,6 @@ #include #include -#define __KERNEL_SYSCALLS__ -#include - #define IRQ_CROSS_CALL 15 extern ctxd_t *srmmu_ctx_table_phys; --- linux-2.6.4-rc2/arch/sparc/kernel/sun4m_smp.c 2003-06-14 12:18:32.000000000 -0700 +++ 25/arch/sparc/kernel/sun4m_smp.c 2004-03-07 20:47:47.000000000 -0800 @@ -27,9 +27,6 @@ #include #include -#define __KERNEL_SYSCALLS__ -#include - #define IRQ_RESCHEDULE 13 #define IRQ_STOP_CPU 14 #define IRQ_CROSS_CALL 15 --- linux-2.6.4-rc2/arch/sparc/kernel/trampoline.S 2004-03-03 23:12:43.000000000 -0800 +++ 25/arch/sparc/kernel/trampoline.S 2004-03-07 20:46:46.000000000 -0800 @@ -13,6 +13,7 @@ #include #include #include +#include .globl sun4m_cpu_startup, __smp4m_processor_id .globl sun4d_cpu_startup, __smp4d_processor_id --- linux-2.6.4-rc2/arch/sparc/mm/nosun4c.c 2003-06-14 12:18:51.000000000 -0700 +++ 25/arch/sparc/mm/nosun4c.c 2004-03-07 20:46:46.000000000 -0800 @@ -57,6 +57,11 @@ pte_t *sun4c_pte_offset(pmd_t * dir, uns return NULL; } +pte_t *sun4c_pte_offset_kernel(pmd_t *dir, unsigned long address) +{ + return NULL; +} + void sun4c_update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t pte) { } --- linux-2.6.4-rc2/arch/sparc/mm/srmmu.c 2004-02-03 20:42:35.000000000 -0800 +++ 25/arch/sparc/mm/srmmu.c 2004-03-07 20:46:46.000000000 -0800 @@ -627,8 +627,15 @@ static void srmmu_unmapiorange(unsigned */ struct thread_info *srmmu_alloc_thread_info(void) { - return (struct thread_info *) - __get_free_pages(GFP_KERNEL, THREAD_INFO_ORDER); + struct thread_info *ret; + + ret = (struct thread_info *)__get_free_pages(GFP_KERNEL, + THREAD_INFO_ORDER); +#ifdef CONFIG_DEBUG_STACK_USAGE + memset(ret, 0, PAGE_SIZE << THREAD_INFO_ORDER); +#endif /* DEBUG_STACK_USAGE */ + + return ret; } static void srmmu_free_thread_info(struct thread_info *ti) --- linux-2.6.4-rc2/arch/sparc/mm/sun4c.c 2004-03-03 23:12:43.000000000 -0800 +++ 25/arch/sparc/mm/sun4c.c 2004-03-07 20:46:46.000000000 -0800 @@ -1058,6 +1058,11 @@ static struct thread_info *sun4c_alloc_t #ifndef CONFIG_SUN4 sun4c_put_pte(addr + PAGE_SIZE, BUCKET_PTE(pages + PAGE_SIZE)); #endif + +#ifdef CONFIG_DEBUG_STACK_USAGE + memset((void *)addr, 0, PAGE_SIZE << THREAD_INFO_ORDER); +#endif /* DEBUG_STACK_USAGE */ + return (struct thread_info *) addr; } --- linux-2.6.4-rc2/arch/v850/kernel/rte_mb_a_pci.c 2003-08-08 22:55:11.000000000 -0700 +++ 25/arch/v850/kernel/rte_mb_a_pci.c 2004-03-07 20:46:58.000000000 -0800 @@ -687,10 +687,11 @@ void pci_unmap_single (struct pci_dev *p If you perform a pci_map_single() but wish to interrogate the buffer using the cpu, yet do not wish to teardown the PCI dma mapping, you must call this function before doing so. At the next - point you give the PCI dma address back to the card, the device - again owns the buffer. */ + point you give the PCI dma address back to the card, you must first + perform a pci_dma_sync_for_device, and then the device again owns + the buffer. */ void -pci_dma_sync_single (struct pci_dev *pdev, dma_addr_t dma_addr, size_t size, +pci_dma_sync_single_for_cpu (struct pci_dev *pdev, dma_addr_t dma_addr, size_t size, int dir) { void *mb_sram_addr = PCI_TO_MB_SRAM (dma_addr); @@ -700,6 +701,22 @@ pci_dma_sync_single (struct pci_dev *pde if (dir == PCI_DMA_FROMDEVICE) memcpy (mapping->cpu_addr, mb_sram_addr, size); else if (dir == PCI_DMA_TODEVICE) + ; /* nothing to do */ + else + panic("pci_dma_sync_single: unsupported sync dir: %d", dir); +} + +void +pci_dma_sync_single_for_device (struct pci_dev *pdev, dma_addr_t dma_addr, size_t size, + int dir) +{ + void *mb_sram_addr = PCI_TO_MB_SRAM (dma_addr); + struct dma_mapping *mapping = find_dma_mapping (mb_sram_addr); + + /* Synchronize the DMA buffer with the CPU buffer if necessary. */ + if (dir == PCI_DMA_FROMDEVICE) + ; /* nothing to do */ + else if (dir == PCI_DMA_TODEVICE) memcpy (mb_sram_addr, mapping->cpu_addr, size); else panic("pci_dma_sync_single: unsupported sync dir: %d", dir); @@ -724,11 +741,18 @@ pci_unmap_sg (struct pci_dev *pdev, stru } /* Make physical memory consistent for a set of streaming mode DMA - translations after a transfer. The same as pci_dma_sync_single but + translations after a transfer. The same as pci_dma_sync_single_* but for a scatter-gather list, same rules and usage. */ void -pci_dma_sync_sg (struct pci_dev *dev, struct scatterlist *sg, int sg_len, +pci_dma_sync_sg_for_cpu (struct pci_dev *dev, struct scatterlist *sg, int sg_len, + int dir) +{ + BUG (); +} + +void +pci_dma_sync_sg_for_device (struct pci_dev *dev, struct scatterlist *sg, int sg_len, int dir) { BUG (); @@ -770,4 +794,5 @@ EXPORT_SYMBOL (pci_map_single); EXPORT_SYMBOL (pci_unmap_single); EXPORT_SYMBOL (pci_alloc_consistent); EXPORT_SYMBOL (pci_free_consistent); -EXPORT_SYMBOL (pci_dma_sync_single); +EXPORT_SYMBOL (pci_dma_sync_single_for_cpu); +EXPORT_SYMBOL (pci_dma_sync_single_for_device); --- linux-2.6.4-rc2/arch/x86_64/ia32/ia32entry.S 2004-02-03 20:42:35.000000000 -0800 +++ 25/arch/x86_64/ia32/ia32entry.S 2004-03-07 20:48:04.000000000 -0800 @@ -467,7 +467,7 @@ ia32_sys_call_table: .quad sys_epoll_create .quad sys_epoll_ctl .quad sys_epoll_wait - .quad sys_remap_file_pages + .quad old_remap_file_pages .quad sys_set_tid_address .quad sys32_timer_create .quad compat_timer_settime --- linux-2.6.4-rc2/arch/x86_64/ia32/ipc32.c 2004-03-03 23:12:43.000000000 -0800 +++ 25/arch/x86_64/ia32/ipc32.c 2004-03-07 20:47:07.000000000 -0800 @@ -1,656 +1,19 @@ #include -#include -#include -#include +#include +#include #include +#include #include #include -#include #include -#include #include #include -#include -#include -#include -#include -#include - -#include - -/* - * sys32_ipc() is the de-multiplexer for the SysV IPC calls in 32bit emulation.. - * - * This is really horribly ugly. - */ - -struct msgbuf32 { - s32 mtype; - char mtext[1]; -}; - -struct ipc_perm32 { - int key; - compat_uid_t uid; - compat_gid_t gid; - compat_uid_t cuid; - compat_gid_t cgid; - unsigned short mode; - unsigned short seq; -}; - -struct ipc64_perm32 { - unsigned key; - compat_uid32_t uid; - compat_gid32_t gid; - compat_uid32_t cuid; - compat_gid32_t cgid; - unsigned short mode; - unsigned short __pad1; - unsigned short seq; - unsigned short __pad2; - unsigned int unused1; - unsigned int unused2; -}; - -struct semid_ds32 { - struct ipc_perm32 sem_perm; /* permissions .. see ipc.h */ - compat_time_t sem_otime; /* last semop time */ - compat_time_t sem_ctime; /* last change time */ - u32 sem_base; /* ptr to first semaphore in array */ - u32 sem_pending; /* pending operations to be processed */ - u32 sem_pending_last; /* last pending operation */ - u32 undo; /* undo requests on this array */ - unsigned short sem_nsems; /* no. of semaphores in array */ -}; - -struct semid64_ds32 { - struct ipc64_perm32 sem_perm; - compat_time_t sem_otime; - unsigned int __unused1; - compat_time_t sem_ctime; - unsigned int __unused2; - unsigned int sem_nsems; - unsigned int __unused3; - unsigned int __unused4; -}; - -struct msqid_ds32 { - struct ipc_perm32 msg_perm; - u32 msg_first; - u32 msg_last; - compat_time_t msg_stime; - compat_time_t msg_rtime; - compat_time_t msg_ctime; - u32 wwait; - u32 rwait; - unsigned short msg_cbytes; - unsigned short msg_qnum; - unsigned short msg_qbytes; - compat_ipc_pid_t msg_lspid; - compat_ipc_pid_t msg_lrpid; -}; - -struct msqid64_ds32 { - struct ipc64_perm32 msg_perm; - compat_time_t msg_stime; - unsigned int __unused1; - compat_time_t msg_rtime; - unsigned int __unused2; - compat_time_t msg_ctime; - unsigned int __unused3; - unsigned int msg_cbytes; - unsigned int msg_qnum; - unsigned int msg_qbytes; - compat_pid_t msg_lspid; - compat_pid_t msg_lrpid; - unsigned int __unused4; - unsigned int __unused5; -}; - -struct shmid_ds32 { - struct ipc_perm32 shm_perm; - int shm_segsz; - compat_time_t shm_atime; - compat_time_t shm_dtime; - compat_time_t shm_ctime; - compat_ipc_pid_t shm_cpid; - compat_ipc_pid_t shm_lpid; - unsigned short shm_nattch; -}; - -struct shmid64_ds32 { - struct ipc64_perm32 shm_perm; - compat_size_t shm_segsz; - compat_time_t shm_atime; - unsigned int __unused1; - compat_time_t shm_dtime; - unsigned int __unused2; - compat_time_t shm_ctime; - unsigned int __unused3; - compat_pid_t shm_cpid; - compat_pid_t shm_lpid; - unsigned int shm_nattch; - unsigned int __unused4; - unsigned int __unused5; -}; - -struct shminfo64_32 { - unsigned int shmmax; - unsigned int shmmin; - unsigned int shmmni; - unsigned int shmseg; - unsigned int shmall; - unsigned int __unused1; - unsigned int __unused2; - unsigned int __unused3; - unsigned int __unused4; -}; - -struct shm_info32 { - int used_ids; - u32 shm_tot, shm_rss, shm_swp; - u32 swap_attempts, swap_successes; -}; - -struct ipc_kludge { - u32 msgp; - s32 msgtyp; -}; - - -#define A(__x) ((unsigned long)(__x)) -#define AA(__x) ((unsigned long)(__x)) - -#define SEMOP 1 -#define SEMGET 2 -#define SEMCTL 3 -#define TIMEDSEMOP 4 -#define MSGSND 11 -#define MSGRCV 12 -#define MSGGET 13 -#define MSGCTL 14 -#define SHMAT 21 -#define SHMDT 22 -#define SHMGET 23 -#define SHMCTL 24 - -#define IPCOP_MASK(__x) (1UL << (__x)) - -static int -ipc_parse_version32 (int *cmd) -{ - if (*cmd & IPC_64) { - *cmd ^= IPC_64; - return IPC_64; - } else { - return IPC_OLD; - } -} - -static int put_semid(void *user_semid, struct semid64_ds *s, int version) -{ - int err2; - switch (version) { - case IPC_64: { - struct semid64_ds32 *usp64 = (struct semid64_ds32 *) user_semid; - - if (!access_ok(VERIFY_WRITE, usp64, sizeof(*usp64))) { - err2 = -EFAULT; - break; - } - err2 = __put_user(s->sem_perm.key, &usp64->sem_perm.key); - err2 |= __put_user(s->sem_perm.uid, &usp64->sem_perm.uid); - err2 |= __put_user(s->sem_perm.gid, &usp64->sem_perm.gid); - err2 |= __put_user(s->sem_perm.cuid, &usp64->sem_perm.cuid); - err2 |= __put_user(s->sem_perm.cgid, &usp64->sem_perm.cgid); - err2 |= __put_user(s->sem_perm.mode, &usp64->sem_perm.mode); - err2 |= __put_user(s->sem_perm.seq, &usp64->sem_perm.seq); - err2 |= __put_user(s->sem_otime, &usp64->sem_otime); - err2 |= __put_user(s->sem_ctime, &usp64->sem_ctime); - err2 |= __put_user(s->sem_nsems, &usp64->sem_nsems); - break; - } - default: { - struct semid_ds32 *usp32 = (struct semid_ds32 *) user_semid; - - if (!access_ok(VERIFY_WRITE, usp32, sizeof(*usp32))) { - err2 = -EFAULT; - break; - } - err2 = __put_user(s->sem_perm.key, &usp32->sem_perm.key); - err2 |= __put_user(s->sem_perm.uid, &usp32->sem_perm.uid); - err2 |= __put_user(s->sem_perm.gid, &usp32->sem_perm.gid); - err2 |= __put_user(s->sem_perm.cuid, &usp32->sem_perm.cuid); - err2 |= __put_user(s->sem_perm.cgid, &usp32->sem_perm.cgid); - err2 |= __put_user(s->sem_perm.mode, &usp32->sem_perm.mode); - err2 |= __put_user(s->sem_perm.seq, &usp32->sem_perm.seq); - err2 |= __put_user(s->sem_otime, &usp32->sem_otime); - err2 |= __put_user(s->sem_ctime, &usp32->sem_ctime); - err2 |= __put_user(s->sem_nsems, &usp32->sem_nsems); - break; - } - } - return err2; -} - -static int -semctl32 (int first, int second, int third, void *uptr) -{ - union semun fourth; - u32 pad; - int err; - struct semid64_ds s; - mm_segment_t old_fs; - int version = ipc_parse_version32(&third); - - if (!uptr) - return -EINVAL; - if (get_user(pad, (u32 *)uptr)) - return -EFAULT; - if (third == SETVAL) - fourth.val = (int)pad; - else - fourth.__pad = (void *)A(pad); - switch (third) { - case IPC_INFO: - case IPC_RMID: - case IPC_SET: - case SEM_INFO: - case GETVAL: - case GETPID: - case GETNCNT: - case GETZCNT: - case GETALL: - case SETVAL: - case SETALL: - err = sys_semctl(first, second, third, fourth); - break; - - case IPC_STAT: - case SEM_STAT: - fourth.__pad = &s; - old_fs = get_fs(); - set_fs(KERNEL_DS); - err = sys_semctl(first, second, third, fourth); - set_fs(old_fs); - if (!err) - err = put_semid((void *)A(pad), &s, version); - break; - default: - err = -EINVAL; - break; - } - return err; -} - -#define MAXBUF (64*1024) - -static int -do_sys32_msgsnd (int first, int second, int third, void *uptr) -{ - struct msgbuf *p; - struct msgbuf32 *up = (struct msgbuf32 *)uptr; - mm_segment_t old_fs; - int err; - - if (second >= MAXBUF-sizeof(struct msgbuf)) - return -EINVAL; - p = kmalloc(second + sizeof(struct msgbuf), GFP_USER); - if (!p) - return -ENOMEM; - err = get_user(p->mtype, &up->mtype); - err |= (copy_from_user(p->mtext, &up->mtext, second) ? -EFAULT : 0); - if (err) - goto out; - old_fs = get_fs(); - set_fs(KERNEL_DS); - err = sys_msgsnd(first, p, second, third); - set_fs(old_fs); - out: - kfree(p); - return err; -} - -static int -do_sys32_msgrcv (int first, int second, int msgtyp, int third, int version, void *uptr) -{ - struct msgbuf32 *up; - struct msgbuf *p; - mm_segment_t old_fs; - int err; - - if (!version) { - struct ipc_kludge *uipck = (struct ipc_kludge *)uptr; - struct ipc_kludge ipck; - - err = -EINVAL; - if (!uptr) - goto out; - err = -EFAULT; - if (copy_from_user(&ipck, uipck, sizeof(struct ipc_kludge))) - goto out; - uptr = (void *)A(ipck.msgp); - msgtyp = ipck.msgtyp; - } - if (second >= MAXBUF-sizeof(struct msgbuf)) - return -EINVAL; - err = -ENOMEM; - p = kmalloc(second + sizeof(struct msgbuf), GFP_USER); - if (!p) - goto out; - old_fs = get_fs(); - set_fs(KERNEL_DS); - err = sys_msgrcv(first, p, second, msgtyp, third); - set_fs(old_fs); - if (err < 0) - goto free_then_out; - up = (struct msgbuf32 *)uptr; - if (put_user(p->mtype, &up->mtype) || copy_to_user(&up->mtext, p->mtext, err)) - err = -EFAULT; -free_then_out: - kfree(p); -out: - return err; -} - - -static int -msgctl32 (int first, int second, void *uptr) -{ - int err = -EINVAL, err2; - struct msqid_ds m; - struct msqid64_ds m64; - struct msqid_ds32 *up32 = (struct msqid_ds32 *)uptr; - struct msqid64_ds32 *up64 = (struct msqid64_ds32 *)uptr; - mm_segment_t old_fs; - int version = ipc_parse_version32(&second); - - switch (second) { - case IPC_INFO: - case IPC_RMID: - case MSG_INFO: - err = sys_msgctl(first, second, (struct msqid_ds *)uptr); - break; - - case IPC_SET: - if (version == IPC_64) { - err = get_user(m.msg_perm.uid, &up64->msg_perm.uid); - err |= get_user(m.msg_perm.gid, &up64->msg_perm.gid); - err |= get_user(m.msg_perm.mode, &up64->msg_perm.mode); - err |= get_user(m.msg_qbytes, &up64->msg_qbytes); - } else { - err = get_user(m.msg_perm.uid, &up32->msg_perm.uid); - err |= get_user(m.msg_perm.gid, &up32->msg_perm.gid); - err |= get_user(m.msg_perm.mode, &up32->msg_perm.mode); - err |= get_user(m.msg_qbytes, &up32->msg_qbytes); - } - if (err) - break; - old_fs = get_fs(); - set_fs(KERNEL_DS); - err = sys_msgctl(first, second, &m); - set_fs(old_fs); - break; - - case IPC_STAT: - case MSG_STAT: - old_fs = get_fs(); - set_fs(KERNEL_DS); - err = sys_msgctl(first, second, (void *) &m64); - set_fs(old_fs); - if (version == IPC_64) { - if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64))) { - err = -EFAULT; - break; - } - err2 = __put_user(m64.msg_perm.key, &up64->msg_perm.key); - err2 |= __put_user(m64.msg_perm.uid, &up64->msg_perm.uid); - err2 |= __put_user(m64.msg_perm.gid, &up64->msg_perm.gid); - err2 |= __put_user(m64.msg_perm.cuid, &up64->msg_perm.cuid); - err2 |= __put_user(m64.msg_perm.cgid, &up64->msg_perm.cgid); - err2 |= __put_user(m64.msg_perm.mode, &up64->msg_perm.mode); - err2 |= __put_user(m64.msg_perm.seq, &up64->msg_perm.seq); - err2 |= __put_user(m64.msg_stime, &up64->msg_stime); - err2 |= __put_user(m64.msg_rtime, &up64->msg_rtime); - err2 |= __put_user(m64.msg_ctime, &up64->msg_ctime); - err2 |= __put_user(m64.msg_cbytes, &up64->msg_cbytes); - err2 |= __put_user(m64.msg_qnum, &up64->msg_qnum); - err2 |= __put_user(m64.msg_qbytes, &up64->msg_qbytes); - err2 |= __put_user(m64.msg_lspid, &up64->msg_lspid); - err2 |= __put_user(m64.msg_lrpid, &up64->msg_lrpid); - if (err2) - err = -EFAULT; - } else { - if (!access_ok(VERIFY_WRITE, up32, sizeof(*up32))) { - err = -EFAULT; - break; - } - err2 = __put_user(m64.msg_perm.key, &up32->msg_perm.key); - err2 |= __put_user(m64.msg_perm.uid, &up32->msg_perm.uid); - err2 |= __put_user(m64.msg_perm.gid, &up32->msg_perm.gid); - err2 |= __put_user(m64.msg_perm.cuid, &up32->msg_perm.cuid); - err2 |= __put_user(m64.msg_perm.cgid, &up32->msg_perm.cgid); - err2 |= __put_user(m64.msg_perm.mode, &up32->msg_perm.mode); - err2 |= __put_user(m64.msg_perm.seq, &up32->msg_perm.seq); - err2 |= __put_user(m64.msg_stime, &up32->msg_stime); - err2 |= __put_user(m64.msg_rtime, &up32->msg_rtime); - err2 |= __put_user(m64.msg_ctime, &up32->msg_ctime); - err2 |= __put_user(m64.msg_cbytes, &up32->msg_cbytes); - err2 |= __put_user(m64.msg_qnum, &up32->msg_qnum); - err2 |= __put_user(m64.msg_qbytes, &up32->msg_qbytes); - err2 |= __put_user(m64.msg_lspid, &up32->msg_lspid); - err2 |= __put_user(m64.msg_lrpid, &up32->msg_lrpid); - if (err2) - err = -EFAULT; - } - break; - } - return err; -} - -static int -shmat32 (int first, int second, int third, int version, void *uptr) -{ - unsigned long raddr; - u32 *uaddr = (u32 *)A((u32)third); - int err; - - if (version == 1) - return -EINVAL; /* iBCS2 emulator entry point: unsupported */ - err = do_shmat(first, uptr, second, &raddr); - if (err) - return err; - return put_user(raddr, uaddr); -} - -static int put_shmid64(struct shmid64_ds *s64p, void *uptr, int version) -{ - int err2; -#define s64 (*s64p) - if (version == IPC_64) { - struct shmid64_ds32 *up64 = (struct shmid64_ds32 *)uptr; - - if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64))) - return -EFAULT; - - err2 = __put_user(s64.shm_perm.key, &up64->shm_perm.key); - err2 |= __put_user(s64.shm_perm.uid, &up64->shm_perm.uid); - err2 |= __put_user(s64.shm_perm.gid, &up64->shm_perm.gid); - err2 |= __put_user(s64.shm_perm.cuid, &up64->shm_perm.cuid); - err2 |= __put_user(s64.shm_perm.cgid, &up64->shm_perm.cgid); - err2 |= __put_user(s64.shm_perm.mode, &up64->shm_perm.mode); - err2 |= __put_user(s64.shm_perm.seq, &up64->shm_perm.seq); - err2 |= __put_user(s64.shm_atime, &up64->shm_atime); - err2 |= __put_user(s64.shm_dtime, &up64->shm_dtime); - err2 |= __put_user(s64.shm_ctime, &up64->shm_ctime); - err2 |= __put_user(s64.shm_segsz, &up64->shm_segsz); - err2 |= __put_user(s64.shm_nattch, &up64->shm_nattch); - err2 |= __put_user(s64.shm_cpid, &up64->shm_cpid); - err2 |= __put_user(s64.shm_lpid, &up64->shm_lpid); - } else { - struct shmid_ds32 *up32 = (struct shmid_ds32 *)uptr; - - if (!access_ok(VERIFY_WRITE, up32, sizeof(*up32))) - return -EFAULT; - - err2 = __put_user(s64.shm_perm.key, &up32->shm_perm.key); - err2 |= __put_user(s64.shm_perm.uid, &up32->shm_perm.uid); - err2 |= __put_user(s64.shm_perm.gid, &up32->shm_perm.gid); - err2 |= __put_user(s64.shm_perm.cuid, &up32->shm_perm.cuid); - err2 |= __put_user(s64.shm_perm.cgid, &up32->shm_perm.cgid); - err2 |= __put_user(s64.shm_perm.mode, &up32->shm_perm.mode); - err2 |= __put_user(s64.shm_perm.seq, &up32->shm_perm.seq); - err2 |= __put_user(s64.shm_atime, &up32->shm_atime); - err2 |= __put_user(s64.shm_dtime, &up32->shm_dtime); - err2 |= __put_user(s64.shm_ctime, &up32->shm_ctime); - err2 |= __put_user(s64.shm_segsz, &up32->shm_segsz); - err2 |= __put_user(s64.shm_nattch, &up32->shm_nattch); - err2 |= __put_user(s64.shm_cpid, &up32->shm_cpid); - err2 |= __put_user(s64.shm_lpid, &up32->shm_lpid); - } -#undef s64 - return err2 ? -EFAULT : 0; -} -static int -shmctl32 (int first, int second, void *uptr) -{ - int err = -EFAULT, err2; - struct shmid_ds s; - struct shmid64_ds s64; - mm_segment_t old_fs; - struct shm_info32 *uip = (struct shm_info32 *)uptr; - struct shm_info si; - int version = ipc_parse_version32(&second); - struct shminfo64 smi; - struct shminfo *usi32 = (struct shminfo *) uptr; - struct shminfo64_32 *usi64 = (struct shminfo64_32 *) uptr; - - switch (second) { - case IPC_INFO: - old_fs = get_fs(); - set_fs(KERNEL_DS); - err = sys_shmctl(first, second, (struct shmid_ds *)&smi); - set_fs(old_fs); - - if (version == IPC_64) { - if (!access_ok(VERIFY_WRITE, usi64, sizeof(*usi64))) { - err = -EFAULT; - break; - } - err2 = __put_user(smi.shmmax, &usi64->shmmax); - err2 |= __put_user(smi.shmmin, &usi64->shmmin); - err2 |= __put_user(smi.shmmni, &usi64->shmmni); - err2 |= __put_user(smi.shmseg, &usi64->shmseg); - err2 |= __put_user(smi.shmall, &usi64->shmall); - } else { - if (!access_ok(VERIFY_WRITE, usi32, sizeof(*usi32))) { - err = -EFAULT; - break; - } - err2 = __put_user(smi.shmmax, &usi32->shmmax); - err2 |= __put_user(smi.shmmin, &usi32->shmmin); - err2 |= __put_user(smi.shmmni, &usi32->shmmni); - err2 |= __put_user(smi.shmseg, &usi32->shmseg); - err2 |= __put_user(smi.shmall, &usi32->shmall); - } - if (err2) - err = -EFAULT; - break; - - case IPC_RMID: - case SHM_LOCK: - case SHM_UNLOCK: - err = sys_shmctl(first, second, (struct shmid_ds *)uptr); - break; - - case IPC_SET: - if (version == IPC_64) { - struct shmid64_ds32 *up64 = (struct shmid64_ds32 *)uptr; - err = get_user(s.shm_perm.uid, &up64->shm_perm.uid); - err |= get_user(s.shm_perm.gid, &up64->shm_perm.gid); - err |= get_user(s.shm_perm.mode, &up64->shm_perm.mode); - } else { - struct shmid_ds32 *up32 = (struct shmid_ds32 *)uptr; - err = get_user(s.shm_perm.uid, &up32->shm_perm.uid); - err |= get_user(s.shm_perm.gid, &up32->shm_perm.gid); - err |= get_user(s.shm_perm.mode, &up32->shm_perm.mode); - } - if (err) - break; - old_fs = get_fs(); - set_fs(KERNEL_DS); - err = sys_shmctl(first, second, &s); - set_fs(old_fs); - break; - - case IPC_STAT: - case SHM_STAT: - old_fs = get_fs(); - set_fs(KERNEL_DS); - err = sys_shmctl(first, second, (void *) &s64); - set_fs(old_fs); - - if (err < 0) - break; - err2 = put_shmid64(&s64, uptr, version); - if (err2) - err = err2; - break; - - case SHM_INFO: - old_fs = get_fs(); - set_fs(KERNEL_DS); - err = sys_shmctl(first, second, (void *)&si); - set_fs(old_fs); - if (err < 0) - break; - - if (!access_ok(VERIFY_WRITE, uip, sizeof(*uip))) { - err = -EFAULT; - break; - } - err2 = __put_user(si.used_ids, &uip->used_ids); - err2 |= __put_user(si.shm_tot, &uip->shm_tot); - err2 |= __put_user(si.shm_rss, &uip->shm_rss); - err2 |= __put_user(si.shm_swp, &uip->shm_swp); - err2 |= __put_user(si.swap_attempts, &uip->swap_attempts); - err2 |= __put_user(si.swap_successes, &uip->swap_successes); - if (err2) - err = -EFAULT; - break; - default: - err = -EINVAL; - break; - } - return err; -} - -extern int sem_ctls[]; - -static long semtimedop32(int semid, struct sembuf *sb, - unsigned nsops, struct compat_timespec *ts32) -{ - struct timespec ts; - mm_segment_t oldfs = get_fs(); - long ret; - - if (nsops > sem_ctls[2]) - return -E2BIG; - if (!access_ok(VERIFY_READ, sb, nsops * sizeof(struct sembuf))) - return -EFAULT; - if (ts32 && get_compat_timespec(&ts, ts32)) - return -EFAULT; - - set_fs(KERNEL_DS); - ret = sys_semtimedop(semid, sb, nsops, ts32 ? &ts : NULL); - set_fs(oldfs); - return ret; -} +#include asmlinkage long -sys32_ipc (u32 call, int first, int second, int third, u32 ptr, u32 fifth) +sys32_ipc(u32 call, int first, int second, int third, + compat_uptr_t ptr, u32 fifth) { int version; @@ -660,35 +23,35 @@ sys32_ipc (u32 call, int first, int seco switch (call) { case SEMOP: /* struct sembuf is the same on 32 and 64bit :)) */ - return sys_semtimedop(first, (struct sembuf *)AA(ptr), second, - NULL); - case TIMEDSEMOP: - return semtimedop32(first, (struct sembuf *)AA(ptr), second, - (struct compat_timespec *)AA(fifth)); + return sys_semtimedop(first, compat_ptr(ptr), second, NULL); + case SEMTIMEDOP: + return compat_sys_semtimedop(first, compat_ptr(ptr), second, + compat_ptr(fifth)); case SEMGET: return sys_semget(first, second, third); case SEMCTL: - return semctl32(first, second, third, (void *)AA(ptr)); + return compat_sys_semctl(first, second, third, compat_ptr(ptr)); case MSGSND: - return do_sys32_msgsnd(first, second, third, (void *)AA(ptr)); + return compat_sys_msgsnd(first, second, third, compat_ptr(ptr)); case MSGRCV: - return do_sys32_msgrcv(first, second, fifth, third, version, (void *)AA(ptr)); + return compat_sys_msgrcv(first, second, fifth, third, + version, compat_ptr(ptr)); case MSGGET: return sys_msgget((key_t) first, second); case MSGCTL: - return msgctl32(first, second, (void *)AA(ptr)); + return compat_sys_msgctl(first, second, compat_ptr(ptr)); case SHMAT: - return shmat32(first, second, third, version, (void *)AA(ptr)); + return compat_sys_shmat(first, second, third, version, + compat_ptr(ptr)); break; case SHMDT: - return sys_shmdt((char *)AA(ptr)); + return sys_shmdt(compat_ptr(ptr)); case SHMGET: return sys_shmget(first, second, third); case SHMCTL: - return shmctl32(first, second, (void *)AA(ptr)); + return compat_sys_shmctl(first, second, compat_ptr(ptr)); } return -ENOSYS; } - --- linux-2.6.4-rc2/arch/x86_64/Kconfig 2004-03-03 23:12:43.000000000 -0800 +++ 25/arch/x86_64/Kconfig 2004-03-07 20:47:07.000000000 -0800 @@ -381,6 +381,10 @@ config COMPAT depends on IA32_EMULATION default y +config SYSVIPC_COMPAT + bool + depends on COMPAT && SYSVIPC + default y config UID16 bool @@ -452,6 +456,7 @@ config INIT_DEBUG config DEBUG_INFO bool "Compile the kernel with debug info" depends on DEBUG_KERNEL + default n help If you say Y here the resulting kernel image will include debugging info resulting in a larger kernel image. @@ -483,9 +488,8 @@ config IOMMU_LEAK help Add a simple leak tracer to the IOMMU code. This is useful when you are debugging a buggy device driver that leaks IOMMU mappings. - -#config X86_REMOTE_DEBUG -# bool "kgdb debugging stub" + +source "arch/x86_64/Kconfig.kgdb" endmenu --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/arch/x86_64/Kconfig.kgdb 2004-03-07 20:47:04.000000000 -0800 @@ -0,0 +1,176 @@ +config KGDB + bool "Include kgdb kernel debugger" + depends on DEBUG_KERNEL + select DEBUG_INFO + help + If you say Y here, the system will be compiled with the debug + option (-g) and a debugging stub will be included in the + kernel. This stub communicates with gdb on another (host) + computer via a serial port. The host computer should have + access to the kernel binary file (vmlinux) and a serial port + that is connected to the target machine. Gdb can be made to + configure the serial port or you can use stty and setserial to + do this. See the 'target' command in gdb. This option also + configures in the ability to request a breakpoint early in the + boot process. To request the breakpoint just include 'kgdb' + as a boot option when booting the target machine. The system + will then break as soon as it looks at the boot options. This + option also installs a breakpoint in panic and sends any + kernel faults to the debugger. For more information see the + Documentation/i386/kgdb.txt file. + +choice + depends on KGDB + prompt "Debug serial port BAUD" + default KGDB_115200BAUD + help + Gdb and the kernel stub need to agree on the baud rate to be + used. Some systems (x86 family at this writing) allow this to + be configured. + +config KGDB_9600BAUD + bool "9600" + +config KGDB_19200BAUD + bool "19200" + +config KGDB_38400BAUD + bool "38400" + +config KGDB_57600BAUD + bool "57600" + +config KGDB_115200BAUD + bool "115200" +endchoice + +config KGDB_PORT + hex "hex I/O port address of the debug serial port" + depends on KGDB + default 3f8 + help + Some systems (x86 family at this writing) allow the port + address to be configured. The number entered is assumed to be + hex, don't put 0x in front of it. The standard address are: + COM1 3f8 , irq 4 and COM2 2f8 irq 3. Setserial /dev/ttySx + will tell you what you have. It is good to test the serial + connection with a live system before trying to debug. + +config KGDB_IRQ + int "IRQ of the debug serial port" + depends on KGDB + default 4 + help + This is the irq for the debug port. If everything is working + correctly and the kernel has interrupts on a control C to the + port should cause a break into the kernel debug stub. + +config DEBUG_INFO + bool + depends on KGDB + default y + +config KGDB_MORE + bool "Add any additional compile options" + depends on KGDB + default n + help + Saying yes here turns on the ability to enter additional + compile options. + + +config KGDB_OPTIONS + depends on KGDB_MORE + string "Additional compile arguments" + default "-O1" + help + This option allows you enter additional compile options for + the whole kernel compile. Each platform will have a default + that seems right for it. For example on PPC "-ggdb -O1", and + for i386 "-O1". Note that by configuring KGDB "-g" is already + turned on. In addition, on i386 platforms + "-fomit-frame-pointer" is deleted from the standard compile + options. + +config NO_KGDB_CPUS + int "Number of CPUs" + depends on KGDB && SMP + default NR_CPUS + help + + This option sets the number of cpus for kgdb ONLY. It is used + to prune some internal structures so they look "nice" when + displayed with gdb. This is to overcome possibly larger + numbers that may have been entered above. Enter the real + number to get nice clean kgdb_info displays. + +config KGDB_TS + bool "Enable kgdb time stamp macros?" + depends on KGDB + default n + help + Kgdb event macros allow you to instrument your code with calls + to the kgdb event recording function. The event log may be + examined with gdb at a break point. Turning on this + capability also allows you to choose how many events to + keep. Kgdb always keeps the lastest events. + +choice + depends on KGDB_TS + prompt "Max number of time stamps to save?" + default KGDB_TS_128 + +config KGDB_TS_64 + bool "64" + +config KGDB_TS_128 + bool "128" + +config KGDB_TS_256 + bool "256" + +config KGDB_TS_512 + bool "512" + +config KGDB_TS_1024 + bool "1024" + +endchoice + +config STACK_OVERFLOW_TEST + bool "Turn on kernel stack overflow testing?" + depends on KGDB + default n + help + This option enables code in the front line interrupt handlers + to check for kernel stack overflow on interrupts and system + calls. This is part of the kgdb code on x86 systems. + +config KGDB_CONSOLE + bool "Enable serial console thru kgdb port" + depends on KGDB + default n + help + This option enables the command line "console=kgdb" option. + When the system is booted with this option in the command line + all kernel printk output is sent to gdb (as well as to other + consoles). For this to work gdb must be connected. For this + reason, this command line option will generate a breakpoint if + gdb has not yet connected. After the gdb continue command is + given all pent up console output will be printed by gdb on the + host machine. Neither this option, nor KGDB require the + serial driver to be configured. + +config KGDB_SYSRQ + bool "Turn on SysRq 'G' command to do a break?" + depends on KGDB + default y + help + This option includes an option in the SysRq code that allows + you to enter SysRq G which generates a breakpoint to the KGDB + stub. This will work if the keyboard is alive and can + interrupt the system. Because of constraints on when the + serial port interrupt can be enabled, this code may allow you + to interrupt the system before the serial port control C is + available. Just say yes here. + --- linux-2.6.4-rc2/arch/x86_64/kernel/acpi/boot.c 2004-02-17 20:48:42.000000000 -0800 +++ 25/arch/x86_64/kernel/acpi/boot.c 2004-03-07 20:46:46.000000000 -0800 @@ -48,11 +48,12 @@ #define PREFIX "ACPI: " -int acpi_noirq __initdata = 0; /* skip ACPI IRQ initialization */ +int acpi_noirq __initdata; /* skip ACPI IRQ initialization */ int acpi_ht __initdata = 1; /* enable HT */ int acpi_lapic; int acpi_ioapic; +int acpi_strict; /* -------------------------------------------------------------------------- Boot-time Configuration @@ -264,7 +265,7 @@ acpi_parse_hpet ( * programs the PIC-mode SCI to Level Trigger. * (NO-OP if the BIOS set Level Trigger already) * - * If a PIC-mode SCI is not recogznied or gives spurious IRQ7's + * If a PIC-mode SCI is not recognized or gives spurious IRQ7's * it may require Edge Trigger -- use "acpi_pic_sci=edge" * (NO-OP if the BIOS set Edge Trigger already) * --- linux-2.6.4-rc2/arch/x86_64/kernel/irq.c 2004-03-03 23:12:43.000000000 -0800 +++ 25/arch/x86_64/kernel/irq.c 2004-03-07 20:47:04.000000000 -0800 @@ -405,6 +405,9 @@ out: spin_unlock(&desc->lock); irq_exit(); + + kgdb_process_breakpoint(); + return 1; } --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/arch/x86_64/kernel/kgdb_stub.c 2004-03-07 20:47:04.000000000 -0800 @@ -0,0 +1,2595 @@ +/* + * + * 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. + * + */ + +/* + * Copyright (c) 2000 VERITAS Software Corporation. + * + */ +/**************************************************************************** + * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $ + * + * Module name: remcom.c $ + * Revision: 1.34 $ + * Date: 91/03/09 12:29:49 $ + * Contributor: Lake Stevens Instrument Division$ + * + * Description: low level support for gdb debugger. $ + * + * Considerations: only works on target hardware $ + * + * Written by: Glenn Engel $ + * Updated by: David Grothe + * Updated by: Robert Walsh + * Updated by: wangdi + * ModuleState: Experimental $ + * + * NOTES: See Below $ + * + * Modified for 386 by Jim Kingdon, Cygnus Support. + * Compatibility with 2.1.xx kernel by David Grothe + * + * Changes to allow auto initilization. All that is needed is that it + * be linked with the kernel and a break point (int 3) be executed. + * The header file defines BREAKPOINT to allow one to do + * this. It should also be possible, once the interrupt system is up, to + * call putDebugChar("+"). Once this is done, the remote debugger should + * get our attention by sending a ^C in a packet. George Anzinger + * + * Integrated into 2.2.5 kernel by Tigran Aivazian + * Added thread support, support for multiple processors, + * support for ia-32(x86) hardware debugging. + * Amit S. Kale ( akale@veritas.com ) + * + * Modified to support debugging over ethernet by Robert Walsh + * and wangdi , based on + * code by San Mehat. + * + * X86_64 changes from Andi Kleen's patch merged by Jim Houston + * (jim.houston@ccur.com). If it works thank Andi if its broken + * blame me. + * + * To enable debugger support, two things need to happen. One, a + * call to set_debug_traps() is necessary in order to allow any breakpoints + * or error conditions to be properly intercepted and reported to gdb. + * Two, a breakpoint needs to be generated to begin communication. This + * is most easily accomplished by a call to breakpoint(). Breakpoint() + * simulates a breakpoint by executing an int 3. + * + ************* + * + * The following gdb commands are supported: + * + * command function Return value + * + * g return the value of the CPU registers hex data or ENN + * G set the value of the CPU registers OK or ENN + * + * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN + * + * c Resume at current address SNN ( signal NN) + * cAA..AA Continue at address AA..AA SNN + * + * s Step one instruction SNN + * sAA..AA Step one instruction from AA..AA SNN + * + * k kill + * + * ? What was the last sigval ? SNN (signal NN) + * + * All commands and responses are sent with a packet which includes a + * checksum. A packet consists of + * + * $#. + * + * where + * :: + * :: < two hex digits computed as modulo 256 sum of > + * + * When a packet is received, it is first acknowledged with either '+' or '-'. + * '+' indicates a successful transfer. '-' indicates a failed transfer. + * + * Example: + * + * Host: Reply: + * $m0,10#2a +$00010203040506070809101112131415#42 + * + ****************************************************************************/ +#define KGDB_VERSION "<20030915.1651.33>" +#include +#include +#include /* for strcpy */ +#include +#include +#include +#include /* for linux pt_regs struct */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define Dearly_printk(x...) +int kgdb_enabled = 0; + +/************************************************************************ + * + * external low-level support routines + */ +typedef void (*Function) (void); /* pointer to a function */ + +/* Thread reference */ +typedef unsigned char threadref[8]; + +extern int tty_putDebugChar(int); /* write a single character */ +extern int tty_getDebugChar(void); /* read and return a single char */ +extern void tty_flushDebugChar(void); /* flush pending characters */ +extern int eth_putDebugChar(int); /* write a single character */ +extern int eth_getDebugChar(void); /* read and return a single char */ +extern void eth_flushDebugChar(void); /* flush pending characters */ + +/************************************************************************/ +/* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/ +/* at least NUMREGBYTES*2 are needed for register packets */ +/* Longer buffer is needed to list all threads */ +#define BUFMAX 400 + +char *kgdb_version = KGDB_VERSION; + +/* debug > 0 prints ill-formed commands in valid packets & checksum errors */ +int debug_regs = 0; /* set to non-zero to print registers */ + +/* filled in by an external module */ +char *gdb_module_offsets; + +static const char hexchars[] = "0123456789abcdef"; + +/* Number of bytes of registers. */ +#define NUMREGBYTES (NUMREGS * sizeof(unsigned long)) +/* + * Note that this register image is in a different order than + * the register image that Linux produces at interrupt time. + * + * Linux's register image is defined by struct pt_regs in ptrace.h. + * Just why GDB uses a different order is a historical mystery. + * + * Could add XMM and segment registers here. + */ +enum regnames {_RAX, + _RBX, + _RCX, + _RDX, + _RSI, + _RDI, + _RBP, + _RSP, + _R8, + _R9, + _R10, + _R11, + _R12, + _R13, + _R14, + _R15, + _PC, + _PS, + NUMREGS }; + + +/*************************** ASSEMBLY CODE MACROS *************************/ +/* + * Put the error code here just in case the user cares. + * Likewise, the vector number here (since GDB only gets the signal + * number through the usual means, and that's not very specific). + * The called_from is the return address so he can tell how we entered kgdb. + * This will allow him to seperate out the various possible entries. + */ +#define REMOTE_DEBUG 0 /* set != to turn on printing (also available in info) */ + +#define PID_MAX PID_MAX_DEFAULT + +#ifdef CONFIG_SMP +void smp_send_nmi_allbutself(void); +#define IF_SMP(x) x +#undef MAX_NO_CPUS +#ifndef CONFIG_NO_KGDB_CPUS +#define CONFIG_NO_KGDB_CPUS 2 +#endif +#if CONFIG_NO_KGDB_CPUS > NR_CPUS +#define MAX_NO_CPUS NR_CPUS +#else +#define MAX_NO_CPUS CONFIG_NO_KGDB_CPUS +#endif +#define hold_init hold_on_sstep: 1, +#define MAX_CPU_MASK (unsigned long)((1LL << MAX_NO_CPUS) - 1LL) +#define NUM_CPUS num_online_cpus() +#else +#define IF_SMP(x) +#define hold_init +#undef MAX_NO_CPUS +#define MAX_NO_CPUS 1 +#define NUM_CPUS 1 +#endif +#define NOCPU (struct task_struct *)0xbad1fbad +/* *INDENT-OFF* */ +struct kgdb_info { + int used_malloc; + void *called_from; + long long entry_tsc; + int errcode; + int vector; + int print_debug_info; +#ifdef CONFIG_SMP + int hold_on_sstep; + struct { + volatile struct task_struct *task; + int pid; + int hold; + struct pt_regs *regs; + } cpus_waiting[MAX_NO_CPUS]; +#endif +} kgdb_info = {hold_init print_debug_info:REMOTE_DEBUG, vector:-1}; + +/* *INDENT-ON* */ + +#define used_m kgdb_info.used_malloc +/* + * This is little area we set aside to contain the stack we + * need to build to allow gdb to call functions. We use one + * per cpu to avoid locking issues. We will do all this work + * with interrupts off so that should take care of the protection + * issues. + */ +#define LOOKASIDE_SIZE 200 /* should be more than enough */ +#define MALLOC_MAX 200 /* Max malloc size */ +struct { + unsigned long rsp; + unsigned long array[LOOKASIDE_SIZE]; +} fn_call_lookaside[MAX_NO_CPUS]; + +static int trap_cpu; +static unsigned long OLD_esp; + +#define END_OF_LOOKASIDE &fn_call_lookaside[trap_cpu].array[LOOKASIDE_SIZE] +#define IF_BIT 0x200 +#define TF_BIT 0x100 + +#define MALLOC_ROUND 8-1 + +static char malloc_array[MALLOC_MAX]; +IF_SMP(static void to_gdb(const char *mess)); +void * +malloc(int size) +{ + + if (size <= (MALLOC_MAX - used_m)) { + int old_used = used_m; + used_m += ((size + MALLOC_ROUND) & (~MALLOC_ROUND)); + return &malloc_array[old_used]; + } else { + return NULL; + } +} + +/* + * I/O dispatch functions... + * Based upon kgdboe, either call the ethernet + * handler or the serial one.. + */ +void +putDebugChar(int c) +{ + if (!kgdboe) { + tty_putDebugChar(c); + } else { + eth_putDebugChar(c); + } +} + +int +getDebugChar(void) +{ + if (!kgdboe) { + return tty_getDebugChar(); + } else { + return eth_getDebugChar(); + } +} + +void +flushDebugChar(void) +{ + if (!kgdboe) { + tty_flushDebugChar(); + } else { + eth_flushDebugChar(); + } +} + +/* + * Gdb calls functions by pushing agruments, including a return address + * on the stack and the adjusting EIP to point to the function. The + * whole assumption in GDB is that we are on a different stack than the + * one the "user" i.e. code that hit the break point, is on. This, of + * course is not true in the kernel. Thus various dodges are needed to + * do the call without directly messing with EIP (which we can not change + * as it is just a location and not a register. To adjust it would then + * require that we move every thing below EIP up or down as needed. This + * will not work as we may well have stack relative pointer on the stack + * (such as the pointer to regs, for example). + + * So here is what we do: + * We detect gdb attempting to store into the stack area and instead, store + * into the fn_call_lookaside.array at the same relative location as if it + * were the area ESP pointed at. We also trap ESP modifications + * and uses these to adjust fn_call_lookaside.esp. On entry + * fn_call_lookaside.esp will be set to point at the last entry in + * fn_call_lookaside.array. This allows us to check if it has changed, and + * if so, on exit, we add the registers we will use to do the move and a + * trap/ interrupt return exit sequence. We then adjust the eflags in the + * regs array (remember we now have a copy in the fn_call_lookaside.array) to + * kill the interrupt bit, AND we change EIP to point at our set up stub. + * As part of the register set up we preset the registers to point at the + * begining and end of the fn_call_lookaside.array, so all the stub needs to + * do is move words from the array to the stack until ESP= the desired value + * then do the rti. This will then transfer to the desired function with + * all the correct registers. Nifty huh? + */ +extern asmlinkage void fn_call_stub(void); +extern asmlinkage void fn_rtn_stub(void); +/* *INDENT-OFF* */ +__asm__("fn_rtn_stub:\n\t" + "movq %rax,%rsp\n\t" + "fn_call_stub:\n\t" + "1:\n\t" + "addq $-8,%rbx\n\t" + "movq (%rbx), %rax\n\t" + "pushq %rax\n\t" + "cmpq %rsp,%rcx\n\t" + "jne 1b\n\t" + "popq %rax\n\t" + "popq %rbx\n\t" + "popq %rcx\n\t" + "iret \n\t"); +/* *INDENT-ON* */ +#define gdb_i386vector kgdb_info.vector +#define gdb_i386errcode kgdb_info.errcode +#define waiting_cpus kgdb_info.cpus_waiting +#define remote_debug kgdb_info.print_debug_info +#define hold_cpu(cpu) kgdb_info.cpus_waiting[cpu].hold +/* gdb locks */ + +#ifdef CONFIG_SMP +static int in_kgdb_called; +static spinlock_t waitlocks[MAX_NO_CPUS] = + {[0 ... MAX_NO_CPUS - 1] = SPIN_LOCK_UNLOCKED }; +/* + * The following array has the thread pointer of each of the "other" + * cpus. We make it global so it can be seen by gdb. + */ +volatile int in_kgdb_entry_log[MAX_NO_CPUS]; +volatile struct pt_regs *in_kgdb_here_log[MAX_NO_CPUS]; +/* +static spinlock_t continuelocks[MAX_NO_CPUS]; +*/ +spinlock_t kgdb_spinlock = SPIN_LOCK_UNLOCKED; +/* waiters on our spinlock plus us */ +static atomic_t spinlock_waiters = ATOMIC_INIT(1); +static int spinlock_count = 0; +static int spinlock_cpu = 0; +/* + * Note we use nested spin locks to account for the case where a break + * point is encountered when calling a function by user direction from + * kgdb. Also there is the memory exception recursion to account for. + * Well, yes, but this lets other cpus thru too. Lets add a + * cpu id to the lock. + */ +#define KGDB_SPIN_LOCK(x) if( spinlock_count == 0 || \ + spinlock_cpu != smp_processor_id()){\ + atomic_inc(&spinlock_waiters); \ + while (! spin_trylock(x)) {\ + in_kgdb(®s);\ + }\ + atomic_dec(&spinlock_waiters); \ + spinlock_count = 1; \ + spinlock_cpu = smp_processor_id(); \ + }else{ \ + spinlock_count++; \ + } +#define KGDB_SPIN_UNLOCK(x) if( --spinlock_count == 0) spin_unlock(x) +#else +unsigned kgdb_spinlock = 0; +#define KGDB_SPIN_LOCK(x) --*x +#define KGDB_SPIN_UNLOCK(x) ++*x +#endif + +int +hex(char ch) +{ + if ((ch >= 'a') && (ch <= 'f')) + return (ch - 'a' + 10); + if ((ch >= '0') && (ch <= '9')) + return (ch - '0'); + if ((ch >= 'A') && (ch <= 'F')) + return (ch - 'A' + 10); + return (-1); +} + +/* scan for the sequence $# */ +void +getpacket(char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + int i; + int count; + char ch; + + do { + /* wait around for the start character, ignore all other characters */ + while ((ch = (getDebugChar() & 0x7f)) != '$') ; + checksum = 0; + xmitcsum = -1; + + count = 0; + + /* now, read until a # or end of buffer is found */ + while (count < BUFMAX) { + ch = getDebugChar() & 0x7f; + if (ch == '#') + break; + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + buffer[count] = 0; + + if (ch == '#') { + xmitcsum = hex(getDebugChar() & 0x7f) << 4; + xmitcsum += hex(getDebugChar() & 0x7f); + if ((remote_debug) && (checksum != xmitcsum)) { + printk + ("bad checksum. My count = 0x%x, sent=0x%x. buf=%s\n", + checksum, xmitcsum, buffer); + } + + if (checksum != xmitcsum) + putDebugChar('-'); /* failed checksum */ + else { + putDebugChar('+'); /* successful transfer */ + /* if a sequence char is present, reply the sequence ID */ + if (buffer[2] == ':') { + putDebugChar(buffer[0]); + putDebugChar(buffer[1]); + /* remove sequence chars from buffer */ + count = strlen(buffer); + for (i = 3; i <= count; i++) + buffer[i - 3] = buffer[i]; + } + } + } + } while (checksum != xmitcsum); + + if (remote_debug) + printk("R:%s\n", buffer); + flushDebugChar(); +} + +/* send the packet in buffer. */ + +void +putpacket(char *buffer) +{ + unsigned char checksum; + int count; + char ch; + + /* $#. */ + + if (!kgdboe) { + do { + if (remote_debug) + printk("T:%s\n", buffer); + putDebugChar('$'); + checksum = 0; + count = 0; + + while ((ch = buffer[count])) { + putDebugChar(ch); + checksum += ch; + count += 1; + } + + putDebugChar('#'); + putDebugChar(hexchars[checksum >> 4]); + putDebugChar(hexchars[checksum % 16]); + flushDebugChar(); + + } while ((getDebugChar() & 0x7f) != '+'); + } else { + /* + * For udp, we can not transfer too much bytes once. + * We only transfer MAX_SEND_COUNT size bytes each time + */ + +#define MAX_SEND_COUNT 30 + + int send_count = 0, i = 0; + char send_buf[MAX_SEND_COUNT]; + + do { + if (remote_debug) + printk("T:%s\n", buffer); + putDebugChar('$'); + checksum = 0; + count = 0; + send_count = 0; + while ((ch = buffer[count])) { + if (send_count >= MAX_SEND_COUNT) { + for(i = 0; i < MAX_SEND_COUNT; i++) { + putDebugChar(send_buf[i]); + } + flushDebugChar(); + send_count = 0; + } else { + send_buf[send_count] = ch; + checksum += ch; + count ++; + send_count++; + } + } + for(i = 0; i < send_count; i++) + putDebugChar(send_buf[i]); + putDebugChar('#'); + putDebugChar(hexchars[checksum >> 4]); + putDebugChar(hexchars[checksum % 16]); + flushDebugChar(); + } while ((getDebugChar() & 0x7f) != '+'); + } +} + +static char remcomInBuffer[BUFMAX]; +static char remcomOutBuffer[BUFMAX]; +static char lbuf[BUFMAX]; +static short error; + +void +debug_error(char *format, char *parm) +{ + if (remote_debug) + printk(format, parm); +} + +static void +print_regs(struct pt_regs *regs) +{ + printk("RAX=%016lx RBX=%016lx RCX=%016lx\n", + regs->rax, regs->rbx, regs->rcx); + printk("RDX=%016lx RSI=%016lx RDI=%016lx\n", + regs->rdx, regs->rsi, regs->rdi); + printk("RBP=%016lx PS=%016lx PC=%016lx\n", + regs->rbp, regs->eflags, regs->rip); + printk("R8=%016lx R9=%016lx R10=%016lx\n", + regs->r8, regs->r9, regs->r10); + printk("R11=%016lx R12=%016lx R13=%016lx\n", + regs->r11, regs->r12, regs->r13); + printk("R14=%016lx R15=%016lx RSP=%016lx\n", + regs->r14, regs->r15, regs->rsp); +} + +#define NEW_esp fn_call_lookaside[trap_cpu].rsp + +static void +regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs) +{ + gdb_regs[_RAX] = regs->rax; + gdb_regs[_RBX] = regs->rbx; + gdb_regs[_RCX] = regs->rcx; + gdb_regs[_RDX] = regs->rdx; + gdb_regs[_RSI] = regs->rsi; + gdb_regs[_RDI] = regs->rdi; + gdb_regs[_RBP] = regs->rbp; + gdb_regs[ _PS] = regs->eflags; + gdb_regs[ _PC] = regs->rip; + gdb_regs[ _R8] = regs->r8; + gdb_regs[ _R9] = regs->r9; + gdb_regs[_R10] = regs->r10; + gdb_regs[_R11] = regs->r11; + gdb_regs[_R12] = regs->r12; + gdb_regs[_R13] = regs->r13; + gdb_regs[_R14] = regs->r14; + gdb_regs[_R15] = regs->r15; + gdb_regs[_RSP] = regs->rsp; + + /* Note, as we are a debugging the kernel, we will always + * trap in kernel code, this means no priviledge change, + * and so the pt_regs structure is not completely valid. In a non + * privilege change trap, only EFLAGS, CS and EIP are put on the stack, + * SS and ESP are not stacked, this means that the last 2 elements of + * pt_regs is not valid (they would normally refer to the user stack) + * also, using regs+1 is no good because you end up will a value that is + * 2 longs (8) too high. This used to cause stepping over functions + * to fail, so my fix is to use the address of regs->esp, which + * should point at the end of the stack frame. Note I have ignored + * completely exceptions that cause an error code to be stacked, such + * as double fault. Stuart Hughes, Zentropix. + * original code: gdb_regs[_ESP] = (int) (regs + 1) ; + + * this is now done on entry and moved to OLD_esp (as well as NEW_esp). + */ +} + +static void +gdb_regs_to_regs(unsigned long *gdb_regs, struct pt_regs *regs) +{ + regs->rax = gdb_regs[_RAX] ; + regs->rbx = gdb_regs[_RBX] ; + regs->rcx = gdb_regs[_RCX] ; + regs->rdx = gdb_regs[_RDX] ; + regs->rsi = gdb_regs[_RSI] ; + regs->rdi = gdb_regs[_RDI] ; + regs->rbp = gdb_regs[_RBP] ; + regs->eflags = gdb_regs[ _PS] ; + regs->rip = gdb_regs[ _PC] ; + regs->r8 = gdb_regs[ _R8] ; + regs->r9 = gdb_regs[ _R9] ; + regs->r10 = gdb_regs[ _R10] ; + regs->r11 = gdb_regs[ _R11] ; + regs->r12 = gdb_regs[ _R12] ; + regs->r13 = gdb_regs[ _R13] ; + regs->r14 = gdb_regs[ _R14] ; + regs->r15 = gdb_regs[ _R15] ; + #if 0 /* can't change these */ + regs->rsp = gdb_regs[_RSP] ; + regs->ss = gdb_regs[ _SS] ; + regs->fs = gdb_regs[_FS]; + regs->gs = gdb_regs[_GS]; +#endif +} /* gdb_regs_to_regs */ + +extern void scheduling_functions_start_here(void); +extern void scheduling_functions_end_here(void); +#define first_sched ((unsigned long) scheduling_functions_start_here) +#define last_sched ((unsigned long) scheduling_functions_end_here) + +int thread_list = 0; +extern void thread_return(void); + +void +get_gdb_regs(struct task_struct *p, struct pt_regs *regs, unsigned long *gdb_regs) +{ + unsigned long **rbp, *rsp, *rsp0, pc; + int count = 0; + IF_SMP(int i); + if (!p || p == current) { + regs_to_gdb_regs(gdb_regs, regs); + return; + } +#ifdef CONFIG_SMP + for (i = 0; i < MAX_NO_CPUS; i++) { + if (p == kgdb_info.cpus_waiting[i].task) { + regs_to_gdb_regs(gdb_regs, + kgdb_info.cpus_waiting[i].regs); + gdb_regs[_RSP] = + (unsigned long)&kgdb_info.cpus_waiting[i].regs->rsp; + + return; + } + } +#endif + memset(gdb_regs, 0, NUMREGBYTES); + rsp = (unsigned long *)p->thread.rsp; + rbp = (unsigned long **)rsp[0]; + rsp += 2; + gdb_regs[_PC] = (unsigned long)thread_return; + gdb_regs[_RBP] = (unsigned long)rbp; + gdb_regs[_RSP] = (unsigned long)rsp; + +/* + * This code is to give a more informative notion of where a process + * is waiting. It is used only when the user asks for a thread info + * list. If he then switches to the thread, s/he will find the task + * is in schedule, but a back trace should show the same info we come + * up with. This code was shamelessly purloined from process.c. It was + * then enhanced to provide more registers than simply the program + * counter. + */ + + if (!thread_list) { + return; + } + + if (p->state == TASK_RUNNING) + return; + rsp0 = (unsigned long *)p->thread.rsp0; + if (rsp < (unsigned long *) p->thread_info || rsp > rsp0) + return; + /* include/asm-i386/system.h:switch_to() pushes ebp last. */ + do { + if (*rbp < rsp || *rbp > rsp0) + break; + rbp = (unsigned long **)*rbp; + rsp = (unsigned long *)rbp; + pc = rsp[1]; + + if (pc < first_sched || pc >= last_sched) + break; + gdb_regs[_PC] = (unsigned long)pc; + gdb_regs[_RSP] = (unsigned long)rsp; + gdb_regs[_RBP] = (unsigned long)rbp; + } while (count++ < 16); + return; +} + +/* convert the memory pointed to by mem into hex, placing result in buf */ +/* returns nonzero if any memory access fails. */ +int mem2hex( char* mem, char* buf, int count) +{ + int i; + unsigned char ch; + int ret = 0; + + for (i=0;i> 4]; + *buf++ = hexchars[ch % 16]; + } + *buf = 0; + if (ret) { + Dearly_printk("mem2hex: fault at accessing %p\n", mem); + } + return(ret); +} + +/* convert the hex array pointed to by buf into binary to be placed in mem */ +/* return nonzero if any memory access fails. */ +int hex2mem( char* buf, char* mem, int count) +{ + int i; + unsigned char ch; + int ret = 0; + + for (i=0;i (OLD_esp - (unsigned int) LOOKASIDE_SIZE))) { + addr = (char *) END_OF_LOOKASIDE - ((char *) OLD_esp - addr); + } + *addr = val; +} + +/* convert the memory pointed to by mem into hex, placing result in buf */ +/* return a pointer to the last char put in buf (null) */ +/* If MAY_FAULT is non-zero, then we should set mem_err in response to + a fault; if zero treat a fault like any other fault in the stub. */ +char * +mem2hex(char *mem, char *buf, int count, int may_fault) +{ + int i; + unsigned char ch; + + if (may_fault) { + mem_err_expected = 1; + mem_err = 0; + } + for (i = 0; i < count; i++) { + /* printk("%lx = ", mem) ; */ + + ch = get_char(mem++); + + /* printk("%02x\n", ch & 0xFF) ; */ + if (may_fault && mem_err) { + if (remote_debug) + printk("Mem fault fetching from addr %lx\n", + (long) (mem - 1)); + *buf = 0; /* truncate buffer */ + return (buf); + } + *buf++ = hexchars[ch >> 4]; + *buf++ = hexchars[ch % 16]; + } + *buf = 0; + if (may_fault) + mem_err_expected = 0; + return (buf); +} + +/* convert the hex array pointed to by buf into binary to be placed in mem */ +/* return a pointer to the character AFTER the last byte written */ +/* NOTE: We use the may fault flag to also indicate if the write is to + * the registers (0) or "other" memory (!=0) + */ +char * +hex2mem(char *buf, char *mem, int count, int may_fault) +{ + int i; + unsigned char ch; + + if (may_fault) { + mem_err_expected = 1; + mem_err = 0; + } + for (i = 0; i < count; i++) { + ch = hex(*buf++) << 4; + ch = ch + hex(*buf++); + set_char(mem++, ch, may_fault); + + if (may_fault && mem_err) { + if (remote_debug) + printk("Mem fault storing to addr %lx\n", + (long) (mem - 1)); + return (mem); + } + } + if (may_fault) + mem_err_expected = 0; + return (mem); +} +#endif + +/**********************************************/ +/* WHILE WE FIND NICE HEX CHARS, BUILD AN INT */ +/* RETURN NUMBER OF CHARS PROCESSED */ +/**********************************************/ +int +hexToLong(char **ptr, unsigned long *value) +{ + int numChars = 0; + int hexValue; + + *value = 0; + + while (**ptr) { + hexValue = hex(**ptr); + if (hexValue >= 0) { + *value = (*value << 4) | hexValue; + numChars++; + } else + break; + + (*ptr)++; + } + + return (numChars); +} + +#define stubhex(h) hex(h) +#ifdef old_thread_list + +static int +stub_unpack_int(char *buff, int fieldlength) +{ + int nibble; + int retval = 0; + + while (fieldlength) { + nibble = stubhex(*buff++); + retval |= nibble; + fieldlength--; + if (fieldlength) + retval = retval << 4; + } + return retval; +} +#endif +static char * +pack_hex_byte(char *pkt, int byte) +{ + *pkt++ = hexchars[(byte >> 4) & 0xf]; + *pkt++ = hexchars[(byte & 0xf)]; + return pkt; +} + +#define BUF_THREAD_ID_SIZE 16 + +static char * +pack_threadid(char *pkt, threadref * id) +{ + char *limit; + unsigned char *altid; + + altid = (unsigned char *) id; + limit = pkt + BUF_THREAD_ID_SIZE; + while (pkt < limit) + pkt = pack_hex_byte(pkt, *altid++); + return pkt; +} + +#ifdef old_thread_list +static char * +unpack_byte(char *buf, int *value) +{ + *value = stub_unpack_int(buf, 2); + return buf + 2; +} + +static char * +unpack_threadid(char *inbuf, threadref * id) +{ + char *altref; + char *limit = inbuf + BUF_THREAD_ID_SIZE; + int x, y; + + altref = (char *) id; + + while (inbuf < limit) { + x = stubhex(*inbuf++); + y = stubhex(*inbuf++); + *altref++ = (x << 4) | y; + } + return inbuf; +} +#endif +void +int_to_threadref(threadref * id, int value) +{ + unsigned char *scan; + + scan = (unsigned char *) id; + { + int i = 4; + while (i--) + *scan++ = 0; + } + *scan++ = (value >> 24) & 0xff; + *scan++ = (value >> 16) & 0xff; + *scan++ = (value >> 8) & 0xff; + *scan++ = (value & 0xff); +} +int +int_to_hex_v(unsigned char * id, int value) +{ + unsigned char *start = id; + int shift; + int ch; + + for (shift = 28; shift >= 0; shift -= 4) { + if ((ch = (value >> shift) & 0xf) || (id != start)) { + *id = hexchars[ch]; + id++; + } + } + if (id == start) + *id++ = '0'; + return id - start; +} +#ifdef old_thread_list + +static int +threadref_to_int(threadref * ref) +{ + int i, value = 0; + unsigned char *scan; + + scan = (char *) ref; + scan += 4; + i = 4; + while (i-- > 0) + value = (value << 8) | ((*scan++) & 0xff); + return value; +} +#endif +static int +cmp_str(char *s1, char *s2, int count) +{ + while (count--) { + if (*s1++ != *s2++) + return 0; + } + return 1; +} + +#if 1 /* this is a hold over from 2.4 where O(1) was "sometimes" */ +extern struct task_struct *kgdb_get_idle(int cpu); +#define idle_task(cpu) kgdb_get_idle(cpu) +#else +#define idle_task(cpu) init_tasks[cpu] +#endif + +extern int kgdb_pid_init_done; + +struct task_struct * +getthread(int pid) +{ + struct task_struct *thread; + if (pid >= PID_MAX && pid <= (PID_MAX + MAX_NO_CPUS)) { + if (!cpu_online(pid - PID_MAX)) + return NULL; + + return idle_task(pid - PID_MAX); + } else { + /* + * find_task_by_pid is relatively safe all the time + * Other pid functions require lock downs which imply + * that we may be interrupting them (as we get here + * in the middle of most any lock down). + * Still we don't want to call until the table exists! + */ + if (kgdb_pid_init_done){ + thread = find_task_by_pid(pid); + if (thread) { + return thread; + } + } + } + return NULL; +} +/* *INDENT-OFF* */ +struct hw_breakpoint { + unsigned enabled; + unsigned type; + unsigned len; + unsigned long addr; +} breakinfo[4] = { {enabled:0}, + {enabled:0}, + {enabled:0}, + {enabled:0}}; +/* *INDENT-ON* */ +unsigned long hw_breakpoint_status; +void +correct_hw_break(void) +{ + int breakno; + int correctit; + int breakbit; + unsigned long dr7; + + asm volatile ("movq %%db7, %0\n":"=r" (dr7) + :); + /* *INDENT-OFF* */ + do { + unsigned long addr0, addr1, addr2, addr3; + asm volatile ("movq %%db0, %0\n" + "movq %%db1, %1\n" + "movq %%db2, %2\n" + "movq %%db3, %3\n" + :"=r" (addr0), "=r"(addr1), + "=r"(addr2), "=r"(addr3) + :); + } while (0); + /* *INDENT-ON* */ + correctit = 0; + for (breakno = 0; breakno < 3; breakno++) { + breakbit = 2 << (breakno << 1); + if (!(dr7 & breakbit) && breakinfo[breakno].enabled) { + correctit = 1; + dr7 |= breakbit; + dr7 &= ~(0xf0000 << (breakno << 2)); + dr7 |= (((breakinfo[breakno].len << 2) | + breakinfo[breakno].type) << 16) << + (breakno << 2); + switch (breakno) { + case 0: + asm volatile ("movq %0, %%dr0\n"::"r" + (breakinfo[breakno].addr)); + break; + + case 1: + asm volatile ("movq %0, %%dr1\n"::"r" + (breakinfo[breakno].addr)); + break; + + case 2: + asm volatile ("movq %0, %%dr2\n"::"r" + (breakinfo[breakno].addr)); + break; + + case 3: + asm volatile ("movq %0, %%dr3\n"::"r" + (breakinfo[breakno].addr)); + break; + } + } else if ((dr7 & breakbit) && !breakinfo[breakno].enabled) { + correctit = 1; + dr7 &= ~breakbit; + dr7 &= ~(0xf0000 << (breakno << 2)); + } + } + if (correctit) { + asm volatile ("movq %0, %%db7\n"::"r" (dr7)); + } +} + +int +remove_hw_break(unsigned breakno) +{ + if (!breakinfo[breakno].enabled) { + return -1; + } + breakinfo[breakno].enabled = 0; + return 0; +} + +int +set_hw_break(unsigned breakno, unsigned type, unsigned len, unsigned addr) +{ + if (breakinfo[breakno].enabled) { + return -1; + } + breakinfo[breakno].enabled = 1; + breakinfo[breakno].type = type; + breakinfo[breakno].len = len; + breakinfo[breakno].addr = addr; + return 0; +} + +#ifdef CONFIG_SMP +static int in_kgdb_console = 0; + +int +in_kgdb(struct pt_regs *regs) +{ + unsigned long flags; + int cpu; + if (!kgdb_enabled) + return 0; + cpu = smp_processor_id(); + in_kgdb_called = 1; + if (!spin_is_locked(&kgdb_spinlock)) { + if (in_kgdb_here_log[cpu] || /* we are holding this cpu */ + in_kgdb_console) { /* or we are doing slow i/o */ + return 1; + } + return 0; + } + + /* As I see it the only reason not to let all cpus spin on + * the same spin_lock is to allow selected ones to proceed. + * This would be a good thing, so we leave it this way. + * Maybe someday.... Done ! + + * in_kgdb() is called from an NMI so we don't pretend + * to have any resources, like printk() for example. + */ + + local_irq_save(flags); /* only local here, to avoid hanging */ + /* + * log arival of this cpu + * The NMI keeps on ticking. Protect against recurring more + * than once, and ignor the cpu that has the kgdb lock + */ + in_kgdb_entry_log[cpu]++; + in_kgdb_here_log[cpu] = regs; + if (cpu == spinlock_cpu || waiting_cpus[cpu].task) + goto exit_in_kgdb; + + /* + * For protection of the initilization of the spin locks by kgdb + * it locks the kgdb spinlock before it gets the wait locks set + * up. We wait here for the wait lock to be taken. If the + * kgdb lock goes away first?? Well, it could be a slow exit + * sequence where the wait lock is removed prior to the kgdb lock + * so if kgdb gets unlocked, we just exit. + */ + + while (spin_is_locked(&kgdb_spinlock) && + !spin_is_locked(waitlocks + cpu)) ; + if (!spin_is_locked(&kgdb_spinlock)) + goto exit_in_kgdb; + + waiting_cpus[cpu].task = current; + waiting_cpus[cpu].pid = (current->pid) ? : (PID_MAX + cpu); + waiting_cpus[cpu].regs = regs; + + spin_unlock_wait(waitlocks + cpu); + + /* + * log departure of this cpu + */ + waiting_cpus[cpu].task = 0; + waiting_cpus[cpu].pid = 0; + waiting_cpus[cpu].regs = 0; + correct_hw_break(); + exit_in_kgdb: + in_kgdb_here_log[cpu] = 0; + local_irq_restore(flags); + return 1; + /* + spin_unlock(continuelocks + smp_processor_id()); + */ +} + +void +smp__in_kgdb(struct pt_regs regs) +{ + ack_APIC_irq(); + in_kgdb(®s); +} +#else +int +in_kgdb(struct pt_regs *regs) +{ + return (kgdb_spinlock); +} +#endif + +void +printexceptioninfo(int exceptionNo, int errorcode, char *buffer) +{ + unsigned long dr6; + int i; + switch (exceptionNo) { + case 1: /* debug exception */ + break; + case 3: /* breakpoint */ + sprintf(buffer, "Software breakpoint"); + return; + default: + sprintf(buffer, "Details not available"); + return; + } + asm volatile ("movq %%db6, %0\n":"=r" (dr6) + :); + if (dr6 & 0x4000) { + sprintf(buffer, "Single step"); + return; + } + for (i = 0; i < 4; ++i) { + if (dr6 & (1 << i)) { + sprintf(buffer, "Hardware breakpoint %d", i); + return; + } + } + sprintf(buffer, "Unknown trap"); + return; +} + +/* + * The ThreadExtraInfo query allows us to pass an arbitrary string + * for display with the "info threads" command. + */ + +void +print_extra_info(task_t *p, char *buf) +{ + if (!p) { + sprintf(buf, "Invalid thread"); + return; + } + sprintf(buf, "0x%p %8d %4d %c %s", + (void *)p, p->parent->pid, + task_cpu(p), + (p->state == 0) ? (task_curr(p)?'R':'r') : + (p->state < 0) ? 'U' : + (p->state & TASK_UNINTERRUPTIBLE) ? 'D' : + (p->state & TASK_STOPPED || p->ptrace & PT_PTRACED) ? 'T' : + (p->state & (TASK_ZOMBIE | TASK_DEAD)) ? 'Z' : + (p->state & TASK_INTERRUPTIBLE) ? 'S' : '?', + p->comm); +} + +/* + * This function does all command procesing for interfacing to gdb. + * + * NOTE: The INT nn instruction leaves the state of the interrupt + * enable flag UNCHANGED. That means that when this routine + * is entered via a breakpoint (INT 3) instruction from code + * that has interrupts enabled, then interrupts will STILL BE + * enabled when this routine is entered. The first thing that + * we do here is disable interrupts so as to prevent recursive + * entries and bothersome serial interrupts while we are + * trying to run the serial port in polled mode. + * + * For kernel version 2.1.xx the kgdb_cli() actually gets a spin lock so + * it is always necessary to do a restore_flags before returning + * so as to let go of that lock. + */ +int +kgdb_handle_exception(int exceptionVector, + int signo, int err_code, struct pt_regs *linux_regs) +{ + struct task_struct *usethread = NULL; + struct task_struct *thread_list_start = 0, *thread = NULL; + struct task_struct *p; + unsigned long addr, length; + unsigned long breakno, breaktype; + char *ptr; + unsigned long newPC; + threadref thref; + unsigned long threadid, tmpid; + int thread_min = PID_MAX + MAX_NO_CPUS; +#ifdef old_thread_list + int maxthreads; +#endif + int nothreads; + unsigned long flags; + unsigned long gdb_regs[NUMREGS]; + unsigned long dr6; + IF_SMP(int entry_state = 0); /* 0, ok, 1, no nmi, 2 sync failed */ +#define NO_NMI 1 +#define NO_SYNC 2 +#define regs (*linux_regs) + /* + * If the entry is not from the kernel then return to the Linux + * trap handler and let it process the interrupt normally. + */ + if ((linux_regs->eflags & VM_MASK) || (3 & linux_regs->cs)) { + printk("ignoring non-kernel exception\n"); + print_regs(®s); + return (0); + } + /* + * If we're using eth mode, set the 'mode' in the netdevice. + */ + + if (kgdboe) + netpoll_set_trap(1); + + local_irq_save(flags); + + /* Get kgdb spinlock */ + + KGDB_SPIN_LOCK(&kgdb_spinlock); + rdtscll(kgdb_info.entry_tsc); + /* + * We depend on this spinlock and the NMI watch dog to control the + * other cpus. They will arrive at "in_kgdb()" as a result of the + * NMI and will wait there for the following spin locks to be + * released. + */ +#ifdef CONFIG_SMP + +#if 0 + if (cpu_callout_map & ~MAX_CPU_MASK) { + printk("kgdb : too many cpus, possibly not mapped" + " in contiguous space, change MAX_NO_CPUS" + " in kgdb_stub and make new kernel.\n" + " cpu_callout_map is %lx\n", cpu_callout_map); + goto exit_just_unlock; + } +#endif + if (spinlock_count == 1) { + int time, end_time, dum; + int i; + int cpu_logged_in[MAX_NO_CPUS] = {[0 ... MAX_NO_CPUS - 1] = (0) + }; + if (remote_debug) { + printk("kgdb : cpu %d entry, syncing others\n", + smp_processor_id()); + } + for (i = 0; i < MAX_NO_CPUS; i++) { + /* + * Use trylock as we may already hold the lock if + * we are holding the cpu. Net result is all + * locked. + */ + spin_trylock(&waitlocks[i]); + } + for (i = 0; i < MAX_NO_CPUS; i++) + cpu_logged_in[i] = 0; + /* + * Wait for their arrival. We know the watch dog is active if + * in_kgdb() has ever been called, as it is always called on a + * watchdog tick. + */ + rdtsc(dum, time); + end_time = time + 2; /* Note: we use the High order bits! */ + i = 1; + if (num_online_cpus() > 1) { + int me_in_kgdb = in_kgdb_entry_log[smp_processor_id()]; + smp_send_nmi_allbutself(); + + while (i < num_online_cpus() && time != end_time) { + int j; + for (j = 0; j < MAX_NO_CPUS; j++) { + if (waiting_cpus[j].task && + waiting_cpus[j].task != NOCPU && + !cpu_logged_in[j]) { + i++; + cpu_logged_in[j] = 1; + if (remote_debug) { + printk + ("kgdb : cpu %d arrived at kgdb\n", + j); + } + break; + } else if (!waiting_cpus[j].task && + !cpu_online(j)) { + waiting_cpus[j].task = NOCPU; + cpu_logged_in[j] = 1; + waiting_cpus[j].hold = 1; + break; + } + if (!waiting_cpus[j].task && + in_kgdb_here_log[j]) { + + int wait = 100000; + while (wait--) ; + if (!waiting_cpus[j].task && + in_kgdb_here_log[j]) { + printk + ("kgdb : cpu %d stall" + " in in_kgdb\n", + j); + i++; + cpu_logged_in[j] = 1; + waiting_cpus[j].task = + (struct task_struct + *) 1; + } + } + } + + if (in_kgdb_entry_log[smp_processor_id()] > + (me_in_kgdb + 10)) { + break; + } + + rdtsc(dum, time); + } + if (i < num_online_cpus()) { + printk + ("kgdb : time out, proceeding without sync\n"); +#if 0 + printk("kgdb : Waiting_cpus: 0 = %d, 1 = %d\n", + waiting_cpus[0].task != 0, + waiting_cpus[1].task != 0); + printk("kgdb : Cpu_logged in: 0 = %d, 1 = %d\n", + cpu_logged_in[0], cpu_logged_in[1]); + printk + ("kgdb : in_kgdb_here_log in: 0 = %d, 1 = %d\n", + in_kgdb_here_log[0] != 0, + in_kgdb_here_log[1] != 0); +#endif + entry_state = NO_SYNC; + } else { +#if 0 + int ent = + in_kgdb_entry_log[smp_processor_id()] - + me_in_kgdb; + printk("kgdb : sync after %d entries\n", ent); +#endif + } + } else { + if (remote_debug) { + printk + ("kgdb : %d cpus, but watchdog not active\n" + "proceeding without locking down other cpus\n", + num_online_cpus()); + entry_state = NO_NMI; + } + } + } +#endif + + if (remote_debug) { + unsigned long *lp = (unsigned long *) &linux_regs; + + printk("handle_exception(exceptionVector=%d, " + "signo=%d, err_code=%d, linux_regs=%p)\n", + exceptionVector, signo, err_code, linux_regs); + if (debug_regs) { + print_regs(®s); + printk("Stk: %8lx %8lx %8lx %8lx" + " %8lx %8lx %8lx %8lx\n", + lp[0], lp[1], lp[2], lp[3], + lp[4], lp[5], lp[6], lp[7]); + printk(" %8lx %8lx %8lx %8lx" + " %8lx %8lx %8lx %8lx\n", + lp[8], lp[9], lp[10], lp[11], + lp[12], lp[13], lp[14], lp[15]); + printk(" %8lx %8lx %8lx %8lx " + "%8lx %8lx %8lx %8lx\n", + lp[16], lp[17], lp[18], lp[19], + lp[20], lp[21], lp[22], lp[23]); + printk(" %8lx %8lx %8lx %8lx " + "%8lx %8lx %8lx %8lx\n", + lp[24], lp[25], lp[26], lp[27], + lp[28], lp[29], lp[30], lp[31]); + } + } + + /* Disable hardware debugging while we are in kgdb */ + /* Get the debug register status register */ +/* *INDENT-OFF* */ + __asm__("movq %0,%%db7" + : /* no output */ + :"r"(0UL)); + + asm volatile ("movq %%db6, %0\n" + :"=r" (hw_breakpoint_status) + :); + +#if 0 +/* *INDENT-ON* */ + switch (exceptionVector) { + case 0: /* divide error */ + case 1: /* debug exception */ + case 2: /* NMI */ + case 3: /* breakpoint */ + case 4: /* overflow */ + case 5: /* bounds check */ + case 6: /* invalid opcode */ + case 7: /* device not available */ + case 8: /* double fault (errcode) */ + case 10: /* invalid TSS (errcode) */ + case 12: /* stack fault (errcode) */ + case 16: /* floating point error */ + case 17: /* alignment check (errcode) */ + default: /* any undocumented */ + break; + case 11: /* segment not present (errcode) */ + case 13: /* general protection (errcode) */ + case 14: /* page fault (special errcode) */ + case 19: /* cache flush denied */ + if (mem_err_expected) { + /* + * This fault occured because of the + * get_char or set_char routines. These + * two routines use either eax of edx to + * indirectly reference the location in + * memory that they are working with. + * For a page fault, when we return the + * instruction will be retried, so we + * have to make sure that these + * registers point to valid memory. + */ + mem_err = 1; /* set mem error flag */ + mem_err_expected = 0; + mem_err_cnt++; /* helps in debugging */ + /* make valid address */ + regs.eax = (long) &garbage_loc; + /* make valid address */ + regs.edx = (long) &garbage_loc; + if (remote_debug) + printk("Return after memory error: " + "mem_err_cnt=%d\n", mem_err_cnt); + if (debug_regs) + print_regs(®s); + goto exit_kgdb; + } + break; + } +#endif + if (remote_debug) + printk("kgdb : entered kgdb on cpu %d\n", smp_processor_id()); + + gdb_i386vector = exceptionVector; + gdb_i386errcode = err_code; + kgdb_info.called_from = __builtin_return_address(0); +#ifdef CONFIG_SMP + /* + * OK, we can now communicate, lets tell gdb about the sync. + * but only if we had a problem. + */ + switch (entry_state) { + case NO_NMI: + to_gdb("NMI not active, other cpus not stopped\n"); + break; + case NO_SYNC: + to_gdb("Some cpus not stopped, see 'kgdb_info' for details\n"); + default:; + } + +#endif +/* + * Set up the gdb function call area. + */ + trap_cpu = smp_processor_id(); + OLD_esp = NEW_esp = (unsigned long) (&linux_regs->rsp); + + IF_SMP(once_again:) + /* reply to host that an exception has occurred */ + remcomOutBuffer[0] = 'S'; + remcomOutBuffer[1] = hexchars[signo >> 4]; + remcomOutBuffer[2] = hexchars[signo % 16]; + remcomOutBuffer[3] = 0; + + putpacket(remcomOutBuffer); + + while (1 == 1) { + error = 0; + remcomOutBuffer[0] = 0; + getpacket(remcomInBuffer); + switch (remcomInBuffer[0]) { + case '?': + remcomOutBuffer[0] = 'S'; + remcomOutBuffer[1] = hexchars[signo >> 4]; + remcomOutBuffer[2] = hexchars[signo % 16]; + remcomOutBuffer[3] = 0; + break; + case 'd': + remote_debug = !(remote_debug); /* toggle debug flag */ + printk("Remote debug %s\n", + remote_debug ? "on" : "off"); + break; + case 'g': /* return the value of the CPU registers */ + get_gdb_regs(usethread, ®s, gdb_regs); + mem2hex((char *) gdb_regs, + remcomOutBuffer, NUMREGBYTES); + break; + case 'G': /* set the value of the CPU registers - return OK */ + hex2mem(&remcomInBuffer[1], + (char *) gdb_regs, NUMREGBYTES); + if (!usethread || usethread == current) { + gdb_regs_to_regs(gdb_regs, ®s); + strcpy(remcomOutBuffer, "OK"); + } else { + strcpy(remcomOutBuffer, "E00"); + } + break; + + case 'P':{ /* set the value of a single CPU register - + return OK */ + /* + * For some reason, gdb wants to talk about psudo + * registers (greater than 15). + */ + unsigned long regno; + + ptr = &remcomInBuffer[1]; + regs_to_gdb_regs(gdb_regs, ®s); + if ((!usethread || usethread == current) && + hexToLong(&ptr, ®no) && + *ptr++ == '=' && (regno >= 0)) { + if (regno >= NUMREGS) + break; + hex2mem(ptr, (char *) &gdb_regs[regno], + 8); + gdb_regs_to_regs(gdb_regs, ®s); + strcpy(remcomOutBuffer, "OK"); + break; + } + strcpy(remcomOutBuffer, "E01"); + break; + } + + /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ + case 'm': + /* TRY TO READ %x,%x. IF SUCCEED, SET PTR = 0 */ + ptr = &remcomInBuffer[1]; + if (hexToLong(&ptr, &addr) && + (*(ptr++) == ',') && (hexToLong(&ptr, &length))) { + ptr = 0; + /* + * hex doubles the byte count + */ + if (length > (BUFMAX / 2)) + length = BUFMAX / 2; + if (mem2hex((char *) addr, + remcomOutBuffer, length)) { + strcpy(remcomOutBuffer, "E03"); + debug_error("memory fault\n", NULL); + } + } + + if (ptr) { + strcpy(remcomOutBuffer, "E01"); + debug_error + ("malformed read memory command: %s\n", + remcomInBuffer); + } + break; + + /* MAA..AA,LLLL: + Write LLLL bytes at address AA.AA return OK */ + case 'M': + /* TRY TO READ '%x,%x:'. IF SUCCEED, SET PTR = 0 */ + ptr = &remcomInBuffer[1]; + if (hexToLong(&ptr, &addr) && + (*(ptr++) == ',') && + (hexToLong(&ptr, &length)) && (*(ptr++) == ':')) { + if (hex2mem(ptr, (char *) addr, length)) { + strcpy(remcomOutBuffer, "E03"); + debug_error("memory fault\n", NULL); + } else { + strcpy(remcomOutBuffer, "OK"); + } + + ptr = 0; + } + if (ptr) { + strcpy(remcomOutBuffer, "E02"); + debug_error + ("malformed write memory command: %s\n", + remcomInBuffer); + } + break; + case 'S': + remcomInBuffer[0] = 's'; + case 'C': + /* Csig;AA..AA where ;AA..AA is optional + * continue with signal + * Since signals are meaning less to us, delete that + * part and then fall into the 'c' code. + */ + ptr = &remcomInBuffer[1]; + length = 2; + while (*ptr && *ptr != ';') { + length++; + ptr++; + } + if (*ptr) { + do { + ptr++; + *(ptr - length++) = *ptr; + } while (*ptr); + } else { + remcomInBuffer[1] = 0; + } + + /* cAA..AA Continue at address AA..AA(optional) */ + /* sAA..AA Step one instruction from AA..AA(optional) */ + /* D detach, reply OK and then continue */ + case 'c': + case 's': + case 'D': + + /* try to read optional parameter, + pc unchanged if no parm */ + ptr = &remcomInBuffer[1]; + if (hexToLong(&ptr, &addr)) { + if (remote_debug) + printk("Changing EIP to 0x%lx\n", addr); + + regs.rip = addr; + } + + newPC = regs.rip; + + /* clear the trace bit */ + regs.eflags &= 0xfffffeff; + + /* set the trace bit if we're stepping */ + if (remcomInBuffer[0] == 's') + regs.eflags |= 0x100; + + /* detach is a friendly version of continue. Note that + debugging is still enabled (e.g hit control C) + */ + if (remcomInBuffer[0] == 'D') { + strcpy(remcomOutBuffer, "OK"); + putpacket(remcomOutBuffer); + } + + if (remote_debug) { + printk("Resuming execution\n"); + print_regs(®s); + } + asm volatile ("movq %%db6, %0\n":"=r" (dr6) + :); + if (!(dr6 & 0x4000)) { + for (breakno = 0; breakno < 4; ++breakno) { + if (dr6 & (1 << breakno) && + (breakinfo[breakno].type == 0)) { + /* Set restore flag */ + regs.eflags |= 0x10000; + break; + } + } + } + + if (kgdboe) + netpoll_set_trap(0); + + correct_hw_break(); + asm volatile ("movq %0, %%db6\n"::"r" (0UL)); + goto exit_kgdb; + + /* kill the program */ + case 'k': /* do nothing */ + break; + + /* query */ + case 'q': + nothreads = 0; + switch (remcomInBuffer[1]) { + case 'f': + threadid = 1; + thread_list = 2; + thread_list_start = (usethread ? : current); + case 's': + if (!cmp_str(&remcomInBuffer[2], + "ThreadInfo", 10)) + break; + + remcomOutBuffer[nothreads++] = 'm'; + for (; threadid < PID_MAX + MAX_NO_CPUS; + threadid++) { + thread = getthread(threadid); + if (thread) { + nothreads += int_to_hex_v( + &remcomOutBuffer[ + nothreads], + threadid); + if (thread_min > threadid) + thread_min = threadid; + remcomOutBuffer[ + nothreads] = ','; + nothreads++; + if (nothreads > BUFMAX - 10) + break; + } + } + if (remcomOutBuffer[nothreads - 1] == 'm') { + remcomOutBuffer[nothreads - 1] = 'l'; + } else { + nothreads--; + } + remcomOutBuffer[nothreads] = 0; + break; + +#ifdef old_thread_list /* Old thread info request */ + case 'L': + /* List threads */ + thread_list = 2; + thread_list_start = (usethread ? : current); + unpack_byte(remcomInBuffer + 3, &maxthreads); + unpack_threadid(remcomInBuffer + 5, &thref); + do { + int buf_thread_limit = + (BUFMAX - 22) / BUF_THREAD_ID_SIZE; + if (maxthreads > buf_thread_limit) { + maxthreads = buf_thread_limit; + } + } while (0); + remcomOutBuffer[0] = 'q'; + remcomOutBuffer[1] = 'M'; + remcomOutBuffer[4] = '0'; + pack_threadid(remcomOutBuffer + 5, &thref); + + /* If start flag set start at 0. */ + if (remcomInBuffer[2] == '1') + threadid = 0; + else + threadid = threadref_to_int(&thref); + for (nothreads = 0; + nothreads < maxthreads && + threadid < PID_MAX + MAX_NO_CPUS; + threadid++) { + thread = getthread(threadid); + if (thread) { + int_to_threadref(&thref, + threadid); + pack_threadid(remcomOutBuffer + + 21 + + nothreads * 16, + &thref); + nothreads++; + if (thread_min > threadid) + thread_min = threadid; + } + } + + if (threadid == PID_MAX + MAX_NO_CPUS) { + remcomOutBuffer[4] = '1'; + } + pack_hex_byte(remcomOutBuffer + 2, nothreads); + remcomOutBuffer[21 + nothreads * 16] = '\0'; + break; +#endif + case 'C': + /* Current thread id */ + remcomOutBuffer[0] = 'Q'; + remcomOutBuffer[1] = 'C'; + threadid = current->pid; + if (!threadid) { + /* + * idle thread + */ + for (threadid = PID_MAX; + threadid < PID_MAX + MAX_NO_CPUS; + threadid++) { + if (current == + idle_task(threadid - + PID_MAX)) + break; + } + } + int_to_threadref(&thref, threadid); + pack_threadid(remcomOutBuffer + 2, &thref); + remcomOutBuffer[18] = '\0'; + break; + + case 'E': + /* Print exception info */ + printexceptioninfo(exceptionVector, + err_code, remcomOutBuffer); + break; + case 'T': + ptr = &remcomInBuffer[0]; + if (strncmp(ptr, "qThreadExtraInfo,", + strlen("qThreadExtraInfo,")) == 0) { + ptr += strlen("qThreadExtraInfo,"); + hexToLong(&ptr, &tmpid); + p = getthread(tmpid); + print_extra_info(p, lbuf); + mem2hex(lbuf, remcomOutBuffer, + strlen(lbuf)); + } + break; +#if 0 + case 'T':{ + char * nptr; + /* Thread extra info */ + if (!cmp_str(&remcomInBuffer[2], + "hreadExtraInfo,", 15)) { + break; + } + ptr = &remcomInBuffer[17]; + hexToLong(&ptr, &threadid); + thread = getthread(threadid); + nptr = &thread->comm[0]; + length = 0; + ptr = &remcomOutBuffer[0]; + do { + length++; + ptr = pack_hex_byte(ptr, *nptr++); + } while (*nptr && length < 16); + /* + * would like that 16 to be the size of + * task_struct.comm but don't know the + * syntax.. + */ + *ptr = 0; + } +#endif + } + break; + + /* task related */ + case 'H': + switch (remcomInBuffer[1]) { + case 'g': + ptr = &remcomInBuffer[2]; + hexToLong(&ptr, &threadid); + thread = getthread(threadid); + if (!thread) { + remcomOutBuffer[0] = 'E'; + remcomOutBuffer[1] = '\0'; + break; + } + /* + * Just in case I forget what this is all about, + * the "thread info" command to gdb causes it + * to ask for a thread list. It then switches + * to each thread and asks for the registers. + * For this (and only this) usage, we want to + * fudge the registers of tasks not on the run + * list (i.e. waiting) to show the routine that + * called schedule. Also, gdb, is a minimalist + * in that if the current thread is the last + * it will not re-read the info when done. + * This means that in this case we must show + * the real registers. So here is how we do it: + * Each entry we keep track of the min + * thread in the list (the last that gdb will) + * get info for. We also keep track of the + * starting thread. + * "thread_list" is cleared when switching back + * to the min thread if it is was current, or + * if it was not current, thread_list is set + * to 1. When the switch to current comes, + * if thread_list is 1, clear it, else do + * nothing. + */ + usethread = thread; + if ((thread_list == 1) && + (thread == thread_list_start)) { + thread_list = 0; + } + if (thread_list && (threadid == thread_min)) { + if (thread == thread_list_start) { + thread_list = 0; + } else { + thread_list = 1; + } + } + /* follow through */ + case 'c': + remcomOutBuffer[0] = 'O'; + remcomOutBuffer[1] = 'K'; + remcomOutBuffer[2] = '\0'; + break; + } + break; + + /* Query thread status */ + case 'T': + ptr = &remcomInBuffer[1]; + hexToLong(&ptr, &threadid); + thread = getthread(threadid); + if (thread) { + remcomOutBuffer[0] = 'O'; + remcomOutBuffer[1] = 'K'; + remcomOutBuffer[2] = '\0'; + if (thread_min > threadid) + thread_min = threadid; + } else { + remcomOutBuffer[0] = 'E'; + remcomOutBuffer[1] = '\0'; + } + break; + + case 'Y': /* set up a hardware breakpoint */ + ptr = &remcomInBuffer[1]; + hexToLong(&ptr, &breakno); + ptr++; + hexToLong(&ptr, &breaktype); + ptr++; + hexToLong(&ptr, &length); + ptr++; + hexToLong(&ptr, &addr); + if (set_hw_break(breakno & 0x3, + breaktype & 0x3, + length & 0x3, addr) == 0) { + strcpy(remcomOutBuffer, "OK"); + } else { + strcpy(remcomOutBuffer, "ERROR"); + } + break; + + /* Remove hardware breakpoint */ + case 'y': + ptr = &remcomInBuffer[1]; + hexToLong(&ptr, &breakno); + if (remove_hw_break(breakno & 0x3) == 0) { + strcpy(remcomOutBuffer, "OK"); + } else { + strcpy(remcomOutBuffer, "ERROR"); + } + break; + + case 'r': /* reboot */ + strcpy(remcomOutBuffer, "OK"); + putpacket(remcomOutBuffer); + /*to_gdb("Rebooting\n"); */ + /* triplefault no return from here */ + { + static long no_idt[2]; + __asm__ __volatile__("lidt %0"::"m"(no_idt[0])); + BREAKPOINT; + } + + } /* switch */ + + /* reply to the request */ + putpacket(remcomOutBuffer); + } /* while(1==1) */ + /* + * reached by goto only. + */ + exit_kgdb: + /* + * Here is where we set up to trap a gdb function call. NEW_esp + * will be changed if we are trying to do this. We handle both + * adding and subtracting, thus allowing gdb to put grung on + * the stack which it removes later. + */ + if (NEW_esp != OLD_esp) { + unsigned long *ptr = END_OF_LOOKASIDE; + if (NEW_esp < OLD_esp) + ptr -= (OLD_esp - NEW_esp) / sizeof (unsigned long); + *--ptr = linux_regs->eflags; + *--ptr = linux_regs->cs; + *--ptr = linux_regs->rip; + *--ptr = linux_regs->rcx; + *--ptr = linux_regs->rbx; + *--ptr = linux_regs->rax; + linux_regs->rcx = NEW_esp - (sizeof (unsigned long) * 6); + linux_regs->rbx = (unsigned long) END_OF_LOOKASIDE; + if (NEW_esp < OLD_esp) { + linux_regs->rip = (unsigned long) fn_call_stub; + } else { + linux_regs->rip = (unsigned long) fn_rtn_stub; + linux_regs->rax = NEW_esp; + } + linux_regs->eflags &= ~(IF_BIT | TF_BIT); + } +#ifdef CONFIG_SMP + /* + * Release gdb wait locks + * Sanity check time. Must have at least one cpu to run. Also single + * step must not be done if the current cpu is on hold. + */ + if (spinlock_count == 1) { + int ss_hold = (regs.eflags & 0x100) && kgdb_info.hold_on_sstep; + int cpu_avail = 0; + int i; + + for (i = 0; i < MAX_NO_CPUS; i++) { + if (!cpu_online(i)) + break; + if (!hold_cpu(i)) { + cpu_avail = 1; + } + } + /* + * Early in the bring up there will be NO cpus on line... + */ + if (!cpu_avail && !cpus_empty(cpu_online_map)) { + to_gdb("No cpus unblocked, see 'kgdb_info.hold_cpu'\n"); + goto once_again; + } + if (hold_cpu(smp_processor_id()) && (regs.eflags & 0x100)) { + to_gdb + ("Current cpu must be unblocked to single step\n"); + goto once_again; + } + if (!(ss_hold)) { + int i; + for (i = 0; i < MAX_NO_CPUS; i++) { + if (!hold_cpu(i)) { + spin_unlock(&waitlocks[i]); + } + } + } else { + spin_unlock(&waitlocks[smp_processor_id()]); + } + /* Release kgdb spinlock */ + KGDB_SPIN_UNLOCK(&kgdb_spinlock); + /* + * If this cpu is on hold, this is where we + * do it. Note, the NMI will pull us out of here, + * but will return as the above lock is not held. + * We will stay here till another cpu releases the lock for us. + */ + spin_unlock_wait(waitlocks + smp_processor_id()); + local_irq_restore(flags); + return (1); + } +#if 0 +exit_just_unlock: +#endif +#endif + /* Release kgdb spinlock */ + KGDB_SPIN_UNLOCK(&kgdb_spinlock); + local_irq_restore(flags); + return (1); +} + +#undef regs +static int kgdb_notify(struct notifier_block *self, unsigned long cmd, void *ptr) +{ + struct die_args *d = ptr; + + if (!kgdb_enabled || (cmd == DIE_DEBUG && user_mode(d->regs))) + return NOTIFY_DONE; + if (cmd == DIE_NMI_IPI) { + if (in_kgdb(d->regs)) + return NOTIFY_BAD; + } else if (kgdb_handle_exception(d->trapnr, d->signr, d->err, d->regs)) + return NOTIFY_BAD; /* skip */ + + return NOTIFY_DONE; +} + +static struct notifier_block kgdb_notifier = { + .notifier_call = kgdb_notify, + .priority = 0, +}; + +void set_debug_traps(void) +{ + static int initialized = 0; + + if (!initialized) { + initialized = 1; + notifier_chain_register(&die_chain, &kgdb_notifier); + } +} + +/* + * Provide the command line "gdb" initial break + */ +int __init kgdb_initial_break(char * str) +{ + if (*str == '\0'){ + breakpoint(); + return 1; + } + return 0; +} +__setup("gdb",kgdb_initial_break); + +/* This function will generate a breakpoint exception. It is used at the + beginning of a program to sync up with a debugger and can be used + otherwise as a quick means to stop program execution and "break" into + the debugger. */ +/* But really, just use the BREAKPOINT macro. We will handle the int stuff + */ + +void breakpoint(void) +{ + + set_debug_traps(); + kgdb_enabled = 1; +#if 0 + /* + * These calls were not enough to allow breakpoint to be + * called before trap_init(). I moved the argument parsing + * after trap_init() and it seems to work. + */ + set_intr_usr_gate(3,&int3); /* disable ints on trap */ + set_intr_gate(1,&debug); + set_intr_gate(14,&page_fault); +#endif + + BREAKPOINT; +} + +#ifdef later +/* + * possibly we should not go thru the traps.c code at all? Someday. + */ +void +do_kgdb_int3(struct pt_regs *regs, long error_code) +{ + kgdb_handle_exception(3, 5, error_code, regs); + return; +} +#endif +#undef regs +#ifdef CONFIG_TRAP_BAD_SYSCALL_EXITS +asmlinkage void +bad_sys_call_exit(int stuff) +{ + struct pt_regs *regs = (struct pt_regs *) &stuff; + printk("Sys call %d return with %x preempt_count\n", + (int) regs->orig_eax, preempt_count()); +} +#endif +#ifdef CONFIG_STACK_OVERFLOW_TEST +#include +asmlinkage void +stack_overflow(void) +{ +#ifdef BREAKPOINT + BREAKPOINT; +#else + printk("Kernel stack overflow, looping forever\n"); +#endif + while (1) { + } +} +#endif + +#if defined(CONFIG_SMP) || defined(CONFIG_KGDB_CONSOLE) +char gdbconbuf[BUFMAX]; + +static void +kgdb_gdb_message(const char *s, unsigned count) +{ + int i; + int wcount; + char *bufptr; + /* + * This takes care of NMI while spining out chars to gdb + */ + IF_SMP(in_kgdb_console = 1); + gdbconbuf[0] = 'O'; + bufptr = gdbconbuf + 1; + while (count > 0) { + if ((count << 1) > (BUFMAX - 2)) { + wcount = (BUFMAX - 2) >> 1; + } else { + wcount = count; + } + count -= wcount; + for (i = 0; i < wcount; i++) { + bufptr = pack_hex_byte(bufptr, s[i]); + } + *bufptr = '\0'; + s += wcount; + + putpacket(gdbconbuf); + + } + IF_SMP(in_kgdb_console = 0); +} +#endif +#ifdef CONFIG_SMP +static void +to_gdb(const char *s) +{ + int count = 0; + while (s[count] && (count++ < BUFMAX)) ; + kgdb_gdb_message(s, count); +} +#endif +#ifdef CONFIG_KGDB_CONSOLE +#include +#include +#include +#include + +void +kgdb_console_write(struct console *co, const char *s, unsigned count) +{ + + if (gdb_i386vector == -1) { + /* + * We have not yet talked to gdb. What to do... + * lets break, on continue we can do the write. + * But first tell him whats up. Uh, well no can do, + * as this IS the console. Oh well... + * We do need to wait or the messages will be lost. + * Other option would be to tell the above code to + * ignore this breakpoint and do an auto return, + * but that might confuse gdb. Also this happens + * early enough in boot up that we don't have the traps + * set up yet, so... + */ + breakpoint(); + } + kgdb_gdb_message(s, count); +} + +/* + * ------------------------------------------------------------ + * Serial KGDB driver + * ------------------------------------------------------------ + */ + +static struct console kgdbcons = { + name:"kgdb", + write:kgdb_console_write, +#ifdef CONFIG_KGDB_USER_CONSOLE + device:kgdb_console_device, +#endif + flags:CON_PRINTBUFFER | CON_ENABLED, + index:-1, +}; + +/* + * The trick here is that this file gets linked before printk.o + * That means we get to peer at the console info in the command + * line before it does. If we are up, we register, otherwise, + * do nothing. By returning 0, we allow printk to look also. + */ +static int kgdb_console_enabled; + +int __init +kgdb_console_init(char *str) +{ + if ((strncmp(str, "kgdb", 4) == 0) || (strncmp(str, "gdb", 3) == 0)) { + register_console(&kgdbcons); + kgdb_console_enabled = 1; + } + return 0; /* let others look at the string */ +} + +__setup("console=", kgdb_console_init); + +#ifdef CONFIG_KGDB_USER_CONSOLE +static kdev_t kgdb_console_device(struct console *c); +/* This stuff sort of works, but it knocks out telnet devices + * we are leaving it here in case we (or you) find time to figure it out + * better.. + */ + +/* + * We need a real char device as well for when the console is opened for user + * space activities. + */ + +static int +kgdb_consdev_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static ssize_t +kgdb_consdev_write(struct file *file, const char *buf, + size_t count, loff_t * ppos) +{ + int size, ret = 0; + static char kbuf[128]; + static DECLARE_MUTEX(sem); + + /* We are not reentrant... */ + if (down_interruptible(&sem)) + return -ERESTARTSYS; + + while (count > 0) { + /* need to copy the data from user space */ + size = count; + if (size > sizeof (kbuf)) + size = sizeof (kbuf); + if (copy_from_user(kbuf, buf, size)) { + ret = -EFAULT; + break;; + } + kgdb_console_write(&kgdbcons, kbuf, size); + count -= size; + ret += size; + buf += size; + } + + up(&sem); + + return ret; +} + +struct file_operations kgdb_consdev_fops = { + open:kgdb_consdev_open, + write:kgdb_consdev_write +}; +static kdev_t +kgdb_console_device(struct console *c) +{ + return MKDEV(TTYAUX_MAJOR, 1); +} + +/* + * This routine gets called from the serial stub in the i386/lib + * This is so it is done late in bring up (just before the console open). + */ +void +kgdb_console_finit(void) +{ + if (kgdb_console_enabled) { + char *cptr = cdevname(MKDEV(TTYAUX_MAJOR, 1)); + char *cp = cptr; + while (*cptr && *cptr != '(') + cptr++; + *cptr = 0; + unregister_chrdev(TTYAUX_MAJOR, cp); + register_chrdev(TTYAUX_MAJOR, "kgdb", &kgdb_consdev_fops); + } +} +#endif +#endif +#ifdef CONFIG_KGDB_TS +#include /* time stamp code */ +#include /* in_interrupt */ +#ifdef CONFIG_KGDB_TS_64 +#define DATA_POINTS 64 +#endif +#ifdef CONFIG_KGDB_TS_128 +#define DATA_POINTS 128 +#endif +#ifdef CONFIG_KGDB_TS_256 +#define DATA_POINTS 256 +#endif +#ifdef CONFIG_KGDB_TS_512 +#define DATA_POINTS 512 +#endif +#ifdef CONFIG_KGDB_TS_1024 +#define DATA_POINTS 1024 +#endif +#ifndef DATA_POINTS +#define DATA_POINTS 128 /* must be a power of two */ +#endif +#define INDEX_MASK (DATA_POINTS - 1) +#if (INDEX_MASK & DATA_POINTS) +#error "CONFIG_KGDB_TS_COUNT must be a power of 2" +#endif +struct kgdb_and_then_struct { +#ifdef CONFIG_SMP + int on_cpu; +#endif + struct task_struct *task; + long long at_time; + int from_ln; + char *in_src; + void *from; + int *with_shpf; + int data0; + int data1; +}; +struct kgdb_and_then_struct2 { +#ifdef CONFIG_SMP + int on_cpu; +#endif + struct task_struct *task; + long long at_time; + int from_ln; + char *in_src; + void *from; + int *with_shpf; + struct task_struct *t1; + struct task_struct *t2; +}; +struct kgdb_and_then_struct kgdb_data[DATA_POINTS]; + +struct kgdb_and_then_struct *kgdb_and_then = &kgdb_data[0]; +int kgdb_and_then_count; + +void +kgdb_tstamp(int line, char *source, int data0, int data1) +{ + static spinlock_t ts_spin = SPIN_LOCK_UNLOCKED; + int flags; + local_irq_save(flags); + spin_lock(&ts_spin); + rdtscll(kgdb_and_then->at_time); +#ifdef CONFIG_SMP + kgdb_and_then->on_cpu = smp_processor_id(); +#endif + kgdb_and_then->task = current; + kgdb_and_then->from_ln = line; + kgdb_and_then->in_src = source; + kgdb_and_then->from = __builtin_return_address(0); + kgdb_and_then->with_shpf = (int *) (((flags & IF_BIT) >> 9) | + (preempt_count() << 8)); + kgdb_and_then->data0 = data0; + kgdb_and_then->data1 = data1; + kgdb_and_then = &kgdb_data[++kgdb_and_then_count & INDEX_MASK]; + spin_unlock(&ts_spin); + local_irq_restore(flags); +#ifdef CONFIG_PREEMPT + +#endif + return; +} +#endif +typedef int gdb_debug_hook(int exceptionVector, + int signo, int err_code, struct pt_regs *linux_regs); +gdb_debug_hook *linux_debug_hook = &kgdb_handle_exception; /* histerical reasons... */ + +static int kgdb_need_breakpoint[NR_CPUS]; + +void kgdb_schedule_breakpoint(void) +{ + kgdb_need_breakpoint[smp_processor_id()] = 1; +} + +void kgdb_process_breakpoint(void) +{ + /* + * Handle a breakpoint queued from inside network driver code + * to avoid reentrancy issues + */ + if (kgdb_need_breakpoint[smp_processor_id()]) { + kgdb_need_breakpoint[smp_processor_id()] = 0; + kgdb_enabled = 1; + BREAKPOINT; + } +} + --- linux-2.6.4-rc2/arch/x86_64/kernel/Makefile 2004-03-03 23:12:43.000000000 -0800 +++ 25/arch/x86_64/kernel/Makefile 2004-03-07 20:47:04.000000000 -0800 @@ -27,6 +27,7 @@ obj-$(CONFIG_DUMMY_IOMMU) += pci-nommu.o obj-$(CONFIG_SWIOTLB) += swiotlb.o obj-$(CONFIG_MODULES) += module.o +obj-$(CONFIG_KGDB) += kgdb_stub.o obj-y += topology.o --- linux-2.6.4-rc2/arch/x86_64/kernel/mpparse.c 2004-03-03 23:12:43.000000000 -0800 +++ 25/arch/x86_64/kernel/mpparse.c 2004-03-07 20:46:46.000000000 -0800 @@ -996,7 +996,7 @@ void __init mp_parse_prt (void) continue; } if ((1<consistent_dma_mask; + dma_mask = hwdev->dev.coherent_dma_mask; } if (dma_mask == 0) --- linux-2.6.4-rc2/arch/x86_64/kernel/process.c 2004-03-03 23:12:43.000000000 -0800 +++ 25/arch/x86_64/kernel/process.c 2004-03-07 20:47:47.000000000 -0800 @@ -16,7 +16,6 @@ * This file handles the architecture-dependent parts of process handling.. */ -#define __KERNEL_SYSCALLS__ #include #include @@ -25,7 +24,6 @@ #include #include #include -#include #include #include #include --- linux-2.6.4-rc2/arch/x86_64/kernel/smp.c 2003-11-23 19:03:00.000000000 -0800 +++ 25/arch/x86_64/kernel/smp.c 2004-03-07 20:47:04.000000000 -0800 @@ -362,6 +362,18 @@ void smp_send_reschedule(int cpu) send_IPI_mask(cpumask_of_cpu(cpu), RESCHEDULE_VECTOR); } +#ifdef CONFIG_KGDB +/* + * By using the NMI code instead of a vector we just sneak thru the + * word generator coming out with just what we want. AND it does + * not matter if clustered_apic_mode is set or not. + */ +void smp_send_nmi_allbutself(void) +{ + send_IPI_allbutself(APIC_DM_NMI); +} +#endif + /* * Structure and data for smp_call_function(). This is designed to minimise * static memory requirements. It also looks cleaner. --- linux-2.6.4-rc2/arch/x86_64/kernel/traps.c 2004-03-03 23:12:43.000000000 -0800 +++ 25/arch/x86_64/kernel/traps.c 2004-03-07 20:47:04.000000000 -0800 @@ -45,6 +45,9 @@ #include #include +#ifdef CONFIG_KGDB +#include +#endif extern struct gate_struct idt_table[256]; --- linux-2.6.4-rc2/arch/x86_64/kernel/x8664_ksyms.c 2004-03-03 23:12:43.000000000 -0800 +++ 25/arch/x86_64/kernel/x8664_ksyms.c 2004-03-07 20:47:45.000000000 -0800 @@ -63,10 +63,6 @@ EXPORT_SYMBOL(pm_idle); EXPORT_SYMBOL(pm_power_off); EXPORT_SYMBOL(get_cmos_time); -#ifdef CONFIG_IO_DEBUG -EXPORT_SYMBOL(__io_virt_debug); -#endif - EXPORT_SYMBOL_NOVERS(__down_failed); EXPORT_SYMBOL_NOVERS(__down_failed_interruptible); EXPORT_SYMBOL_NOVERS(__down_failed_trylock); --- linux-2.6.4-rc2/arch/x86_64/lib/io.c 2003-06-14 12:17:57.000000000 -0700 +++ 25/arch/x86_64/lib/io.c 2004-03-07 20:47:45.000000000 -0800 @@ -4,12 +4,12 @@ void *memcpy_toio(void *dst,const void*src,unsigned len) { - return __inline_memcpy(__io_virt(dst),src,len); + return __inline_memcpy(dst,src,len); } void *memcpy_fromio(void *dst,const void*src,unsigned len) { - return __inline_memcpy(dst,__io_virt(src),len); + return __inline_memcpy(dst,src,len); } EXPORT_SYMBOL(memcpy_toio); --- linux-2.6.4-rc2/arch/x86_64/lib/iodebug.c 2003-06-14 12:18:23.000000000 -0700 +++ /dev/null 2002-08-30 16:31:37.000000000 -0700 @@ -1,11 +0,0 @@ -#include - -void * __io_virt_debug(unsigned long x, const char *file, int line) -{ - if (x < PAGE_OFFSET) { - printk("io mapaddr 0x%05lx not valid at %s:%d!\n", x, file, line); - return __va(x); - } - return (void *)x; -} - --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/arch/x86_64/lib/kgdb_serial.c 2004-03-07 20:47:04.000000000 -0800 @@ -0,0 +1,490 @@ +/* + * Serial interface GDB stub + * + * Written (hacked together) by David Grothe (dave@gcom.com) + * Modified to allow invokation early in boot see also + * kgdb.h for instructions by George Anzinger(george@mvista.com) + * Modified to handle debugging over ethernet by Robert Walsh + * and wangdi , based on + * code by San Mehat. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_KGDB_USER_CONSOLE +extern void kgdb_console_finit(void); +#endif +#define PRNT_off +#define TEST_EXISTANCE +#ifdef PRNT +#define dbprintk(s) printk s +#else +#define dbprintk(s) +#endif +#define TEST_INTERRUPT_off +#ifdef TEST_INTERRUPT +#define intprintk(s) printk s +#else +#define intprintk(s) +#endif + +#define IRQ_T(info) ((info->flags & ASYNC_SHARE_IRQ) ? SA_SHIRQ : SA_INTERRUPT) + +#define GDB_BUF_SIZE 512 /* power of 2, please */ + +static char gdb_buf[GDB_BUF_SIZE]; +static int gdb_buf_in_inx; +static atomic_t gdb_buf_in_cnt; +static int gdb_buf_out_inx; + +struct async_struct *gdb_async_info; +static int gdb_async_irq; + +#define outb_px(a,b) outb_p(b,a) + +static void program_uart(struct async_struct *info); +static void write_char(struct async_struct *info, int chr); +/* + * Get a byte from the hardware data buffer and return it + */ +static int +read_data_bfr(struct async_struct *info) +{ + char it = inb_p(info->port + UART_LSR); + + if (it & UART_LSR_DR) + return (inb_p(info->port + UART_RX)); + /* + * If we have a framing error assume somebody messed with + * our uart. Reprogram it and send '-' both ways... + */ + if (it & 0xc) { + program_uart(info); + write_char(info, '-'); + return ('-'); + } + return (-1); + +} /* read_data_bfr */ + +/* + * Get a char if available, return -1 if nothing available. + * Empty the receive buffer first, then look at the interface hardware. + + * Locking here is a bit of a problem. We MUST not lock out communication + * if we are trying to talk to gdb about a kgdb entry. ON the other hand + * we can loose chars in the console pass thru if we don't lock. It is also + * possible that we could hold the lock or be waiting for it when kgdb + * NEEDS to talk. Since kgdb locks down the world, it does not need locks. + * We do, of course have possible issues with interrupting a uart operation, + * but we will just depend on the uart status to help keep that straight. + + */ +static spinlock_t uart_interrupt_lock = SPIN_LOCK_UNLOCKED; +#ifdef CONFIG_SMP +extern spinlock_t kgdb_spinlock; +#endif + +static int +read_char(struct async_struct *info) +{ + int chr; + unsigned long flags; + local_irq_save(flags); +#ifdef CONFIG_SMP + if (!spin_is_locked(&kgdb_spinlock)) { + spin_lock(&uart_interrupt_lock); + } +#endif + if (atomic_read(&gdb_buf_in_cnt) != 0) { /* intr routine has q'd chars */ + chr = gdb_buf[gdb_buf_out_inx++]; + gdb_buf_out_inx &= (GDB_BUF_SIZE - 1); + atomic_dec(&gdb_buf_in_cnt); + } else { + chr = read_data_bfr(info); + } +#ifdef CONFIG_SMP + if (!spin_is_locked(&kgdb_spinlock)) { + spin_unlock(&uart_interrupt_lock); + } +#endif + local_irq_restore(flags); + return (chr); +} + +/* + * Wait until the interface can accept a char, then write it. + */ +static void +write_char(struct async_struct *info, int chr) +{ + while (!(inb_p(info->port + UART_LSR) & UART_LSR_THRE)) ; + + outb_p(chr, info->port + UART_TX); + +} /* write_char */ + +/* + * Mostly we don't need a spinlock, but since the console goes + * thru here with interrutps on, well, we need to catch those + * chars. + */ +/* + * This is the receiver interrupt routine for the GDB stub. + * It will receive a limited number of characters of input + * from the gdb host machine and save them up in a buffer. + * + * When the gdb stub routine tty_getDebugChar() is called it + * draws characters out of the buffer until it is empty and + * then reads directly from the serial port. + * + * We do not attempt to write chars from the interrupt routine + * since the stubs do all of that via tty_putDebugChar() which + * writes one byte after waiting for the interface to become + * ready. + * + * The debug stubs like to run with interrupts disabled since, + * after all, they run as a consequence of a breakpoint in + * the kernel. + * + * Perhaps someone who knows more about the tty driver than I + * care to learn can make this work for any low level serial + * driver. + */ +static irqreturn_t +gdb_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct async_struct *info; + unsigned long flags; + + info = gdb_async_info; + if (!info || !info->tty || irq != gdb_async_irq) + return IRQ_NONE; + + local_irq_save(flags); + spin_lock(&uart_interrupt_lock); + do { + int chr = read_data_bfr(info); + intprintk(("Debug char on int: %x hex\n", chr)); + if (chr < 0) + continue; + + if (chr == 3) { /* Ctrl-C means remote interrupt */ + BREAKPOINT; + continue; + } + + if (atomic_read(&gdb_buf_in_cnt) >= GDB_BUF_SIZE) { + /* buffer overflow tosses early char */ + read_char(info); + } + gdb_buf[gdb_buf_in_inx++] = chr; + gdb_buf_in_inx &= (GDB_BUF_SIZE - 1); + } while (inb_p(info->port + UART_IIR) & UART_IIR_RDI); + spin_unlock(&uart_interrupt_lock); + local_irq_restore(flags); + return IRQ_HANDLED; +} /* gdb_interrupt */ + +/* + * Just a NULL routine for testing. + */ +void +gdb_null(void) +{ +} /* gdb_null */ + +/* These structure are filled in with values defined in asm/kgdb_local.h + */ +static struct serial_state state = SB_STATE; +static struct async_struct local_info = SB_INFO; +static int ok_to_enable_ints = 0; +static void kgdb_enable_ints_now(void); + +extern char *kgdb_version; +/* + * Hook an IRQ for KGDB. + * + * This routine is called from tty_putDebugChar, below. + */ +static int ints_disabled = 1; +int +gdb_hook_interrupt(struct async_struct *info, int verb) +{ + struct serial_state *state = info->state; + unsigned long flags; + int port; +#ifdef TEST_EXISTANCE + int scratch, scratch2; +#endif + + /* The above fails if memory managment is not set up yet. + * Rather than fail the set up, just keep track of the fact + * and pick up the interrupt thing later. + */ + gdb_async_info = info; + port = gdb_async_info->port; + gdb_async_irq = state->irq; + if (verb) { + printk("kgdb %s : port =%x, IRQ=%d, divisor =%d\n", + kgdb_version, + port, + gdb_async_irq, gdb_async_info->state->custom_divisor); + } + local_irq_save(flags); +#ifdef TEST_EXISTANCE + /* Existance test */ + /* Should not need all this, but just in case.... */ + + scratch = inb_p(port + UART_IER); + outb_px(port + UART_IER, 0); + outb_px(0xff, 0x080); + scratch2 = inb_p(port + UART_IER); + outb_px(port + UART_IER, scratch); + if (scratch2) { + printk + ("gdb_hook_interrupt: Could not clear IER, not a UART!\n"); + local_irq_restore(flags); + return 1; /* We failed; there's nothing here */ + } + scratch2 = inb_p(port + UART_LCR); + outb_px(port + UART_LCR, 0xBF); /* set up for StarTech test */ + outb_px(port + UART_EFR, 0); /* EFR is the same as FCR */ + outb_px(port + UART_LCR, 0); + outb_px(port + UART_FCR, UART_FCR_ENABLE_FIFO); + scratch = inb_p(port + UART_IIR) >> 6; + if (scratch == 1) { + printk("gdb_hook_interrupt: Undefined UART type!" + " Not a UART! \n"); + local_irq_restore(flags); + return 1; + } else { + dbprintk(("gdb_hook_interrupt: UART type " + "is %d where 0=16450, 2=16550 3=16550A\n", scratch)); + } + scratch = inb_p(port + UART_MCR); + outb_px(port + UART_MCR, UART_MCR_LOOP | scratch); + outb_px(port + UART_MCR, UART_MCR_LOOP | 0x0A); + scratch2 = inb_p(port + UART_MSR) & 0xF0; + outb_px(port + UART_MCR, scratch); + if (scratch2 != 0x90) { + printk("gdb_hook_interrupt: " + "Loop back test failed! Not a UART!\n"); + local_irq_restore(flags); + return scratch2 + 1000; /* force 0 to fail */ + } +#endif /* test existance */ + program_uart(info); + local_irq_restore(flags); + + return (0); + +} /* gdb_hook_interrupt */ + +static void +program_uart(struct async_struct *info) +{ + int port = info->port; + + (void) inb_p(port + UART_RX); + outb_px(port + UART_IER, 0); + + (void) inb_p(port + UART_RX); /* serial driver comments say */ + (void) inb_p(port + UART_IIR); /* this clears the interrupt regs */ + (void) inb_p(port + UART_MSR); + outb_px(port + UART_LCR, UART_LCR_WLEN8 | UART_LCR_DLAB); + outb_px(port + UART_DLL, info->state->custom_divisor & 0xff); /* LS */ + outb_px(port + UART_DLM, info->state->custom_divisor >> 8); /* MS */ + outb_px(port + UART_MCR, info->MCR); + + outb_px(port + UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1 | UART_FCR_CLEAR_XMIT | UART_FCR_CLEAR_RCVR); /* set fcr */ + outb_px(port + UART_LCR, UART_LCR_WLEN8); /* reset DLAB */ + outb_px(port + UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1); /* set fcr */ + if (!ints_disabled) { + intprintk(("KGDB: Sending %d to port %x offset %d\n", + gdb_async_info->IER, + (int) gdb_async_info->port, UART_IER)); + outb_px(gdb_async_info->port + UART_IER, gdb_async_info->IER); + } + return; +} + +/* + * tty_getDebugChar + * + * This is a GDB stub routine. It waits for a character from the + * serial interface and then returns it. If there is no serial + * interface connection then it returns a bogus value which will + * almost certainly cause the system to hang. In the + */ +int kgdb_in_isr = 0; +int kgdb_in_lsr = 0; +extern spinlock_t kgdb_spinlock; + +/* Caller takes needed protections */ + +int +tty_getDebugChar(void) +{ + volatile int chr, dum, time, end_time; + + dbprintk(("tty_getDebugChar(port %x): ", gdb_async_info->port)); + + if (gdb_async_info == NULL) { + gdb_hook_interrupt(&local_info, 0); + } + /* + * This trick says if we wait a very long time and get + * no char, return the -1 and let the upper level deal + * with it. + */ + rdtsc(dum, time); + end_time = time + 2; + while (((chr = read_char(gdb_async_info)) == -1) && + (end_time - time) > 0) { + rdtsc(dum, time); + }; + /* + * This covers our butts if some other code messes with + * our uart, hay, it happens :o) + */ + if (chr == -1) + program_uart(gdb_async_info); + + dbprintk(("%c\n", chr > ' ' && chr < 0x7F ? chr : ' ')); + return (chr); + +} /* tty_getDebugChar */ + +static int count = 3; +static spinlock_t one_at_atime = SPIN_LOCK_UNLOCKED; + +static int __init +kgdb_enable_ints(void) +{ + set_debug_traps(); + if (kgdboe) { + return 0; + } + if (gdb_async_info == NULL) { + gdb_hook_interrupt(&local_info, 1); + } + ok_to_enable_ints = 1; + kgdb_enable_ints_now(); +#ifdef CONFIG_KGDB_USER_CONSOLE + kgdb_console_finit(); +#endif + return 0; +} + +#ifdef CONFIG_SERIAL_8250 +void shutdown_for_kgdb(struct async_struct *gdb_async_info); +#endif + +#define kgdb_mem_init_done() (1) + +static void +kgdb_enable_ints_now(void) +{ + if (!spin_trylock(&one_at_atime)) + return; + if (!ints_disabled) + goto exit; + if (kgdb_mem_init_done() && + ints_disabled) { /* don't try till mem init */ +#ifdef CONFIG_SERIAL_8250 + /* + * The ifdef here allows the system to be configured + * without the serial driver. + * Don't make it a module, however, it will steal the port + */ + shutdown_for_kgdb(gdb_async_info); +#endif + ints_disabled = request_irq(gdb_async_info->state->irq, + gdb_interrupt, + IRQ_T(gdb_async_info), + "KGDB-stub", NULL); + intprintk(("KGDB: request_irq returned %d\n", ints_disabled)); + } + if (!ints_disabled) { + intprintk(("KGDB: Sending %d to port %x offset %d\n", + gdb_async_info->IER, + (int) gdb_async_info->port, UART_IER)); + outb_px(gdb_async_info->port + UART_IER, gdb_async_info->IER); + } + exit: + spin_unlock(&one_at_atime); +} + +/* + * tty_putDebugChar + * + * This is a GDB stub routine. It waits until the interface is ready + * to transmit a char and then sends it. If there is no serial + * interface connection then it simply returns to its caller, having + * pretended to send the char. Caller takes needed protections. + */ +void +tty_putDebugChar(int chr) +{ + dbprintk(("tty_putDebugChar(port %x): chr=%02x '%c', ints_on=%d\n", + gdb_async_info->port, + chr, + chr > ' ' && chr < 0x7F ? chr : ' ', ints_disabled ? 0 : 1)); + + if (gdb_async_info == NULL) { + gdb_hook_interrupt(&local_info, 0); + } + + write_char(gdb_async_info, chr); /* this routine will wait */ + count = (chr == '#') ? 0 : count + 1; + if ((count == 2)) { /* try to enable after */ + if (ints_disabled & ok_to_enable_ints) + kgdb_enable_ints_now(); /* try to enable after */ + + /* We do this a lot because, well we really want to get these + * interrupts. The serial driver will clear these bits when it + * initializes the chip. Every thing else it does is ok, + * but this. + */ + if (!ints_disabled) { + outb_px(gdb_async_info->port + UART_IER, + gdb_async_info->IER); + } + } + +} /* tty_putDebugChar */ + +/* + * This does nothing for the serial port, since it doesn't buffer. + */ + +void tty_flushDebugChar(void) +{ +} + +module_init(kgdb_enable_ints); --- linux-2.6.4-rc2/arch/x86_64/lib/Makefile 2003-06-14 12:17:59.000000000 -0700 +++ 25/arch/x86_64/lib/Makefile 2004-03-07 20:47:45.000000000 -0800 @@ -9,5 +9,5 @@ lib-y := csum-partial.o csum-copy.o csum thunk.o io.o clear_page.o copy_page.o bitstr.o lib-y += memcpy.o memmove.o memset.o copy_user.o -lib-$(CONFIG_IO_DEBUG) += iodebug.o lib-$(CONFIG_HAVE_DEC_LOCK) += dec_and_lock.o +lib-$(CONFIG_KGDB) += kgdb_serial.o --- linux-2.6.4-rc2/CREDITS 2004-02-17 20:48:41.000000000 -0800 +++ 25/CREDITS 2004-03-07 20:47:56.000000000 -0800 @@ -289,6 +289,15 @@ S: Via Delle Palme, 9 S: Terni 05100 S: Italy +N: Krzysztof Benedyczak +E: golbi@mat.uni.torun.pl +W: http://www.mat.uni.torun.pl/~golbi +D: POSIX message queues fs (with M. Wronski) +S: ul. Podmiejska 52 +S: Radunica +S: 83-000 Pruszcz Gdanski +S: Poland + N: Randolph Bentson E: bentson@grieg.seaslug.org W: http://www.aa.net/~bentson/ @@ -975,8 +984,7 @@ N: Ben Fennema E: bfennema@falcon.csc.calpoly.edu W: http://www.csc.calpoly.edu/~bfennema D: UDF filesystem -S: 21760 Irma Lyle Drive -S: Los Gatos, CA 95033-8942 +S: (ask for current address) S: USA N: Jürgen Fischer @@ -3489,6 +3497,14 @@ S: 12725 SW Millikan Way, Suite 400 S: Beaverton, OR 97005 S: USA +N: Michal Wronski +E: wrona@mat.uni.torun.pl +W: http://www.mat.uni.torun.pl/~wrona +D: POSIX message queues fs (with K. Benedyczak) +S: ul. Teczowa 23/12 +S: 80-680 Gdansk-Sobieszewo +S: Poland + N: Frank Xia E: qx@math.columbia.edu D: Xiafs filesystem [defunct] --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/crypto/arc4.c 2004-03-07 20:46:46.000000000 -0800 @@ -0,0 +1,107 @@ +/* + * Cryptographic API + * + * ARC4 Cipher Algorithm + * + * Jon Oberheide + * + * 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. + * + */ +#include +#include +#include + +#define ARC4_MIN_KEY_SIZE 1 +#define ARC4_MAX_KEY_SIZE 256 +#define ARC4_BLOCK_SIZE 1 + +struct arc4_ctx { + u8 S[256]; + u8 x, y; +}; + +static int arc4_set_key(void *ctx_arg, const u8 *in_key, unsigned int key_len, u32 *flags) +{ + struct arc4_ctx *ctx = ctx_arg; + int i, j = 0, k = 0; + + ctx->x = 1; + ctx->y = 0; + + for(i = 0; i < 256; i++) + ctx->S[i] = i; + + for(i = 0; i < 256; i++) + { + u8 a = ctx->S[i]; + j = (j + in_key[k] + a) & 0xff; + ctx->S[i] = ctx->S[j]; + ctx->S[j] = a; + if(++k >= key_len) + k = 0; + } + + /* TODO: dump the first 768 bytes generated as recommended + by Ilya Mironov (http://eprint.iacr.org/2002/067/) to + increase the statistical strength of the state table */ + + return 0; +} + +static void arc4_crypt(void *ctx_arg, u8 *out, const u8 *in) +{ + struct arc4_ctx *ctx = ctx_arg; + + u8 *const S = ctx->S; + u8 x = ctx->x; + u8 y = ctx->y; + u8 a, b; + + a = S[x]; + y = (y + a) & 0xff; + b = S[y]; + S[x] = b; + S[y] = a; + x = (x + 1) & 0xff; + *out++ = *in ^ S[(a + b) & 0xff]; + + ctx->x = x; + ctx->y = y; +} + +static struct crypto_alg arc4_alg = { + .cra_name = "arc4", + .cra_flags = CRYPTO_ALG_TYPE_CIPHER, + .cra_blocksize = ARC4_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct arc4_ctx), + .cra_module = THIS_MODULE, + .cra_list = LIST_HEAD_INIT(arc4_alg.cra_list), + .cra_u = { .cipher = { + .cia_min_keysize = ARC4_MIN_KEY_SIZE, + .cia_max_keysize = ARC4_MAX_KEY_SIZE, + .cia_setkey = arc4_set_key, + .cia_encrypt = arc4_crypt, + .cia_decrypt = arc4_crypt } } +}; + +static int __init arc4_init(void) +{ + return crypto_register_alg(&arc4_alg); +} + + +static void __exit arc4_exit(void) +{ + crypto_unregister_alg(&arc4_alg); +} + +module_init(arc4_init); +module_exit(arc4_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ARC4 Cipher Algorithm"); +MODULE_AUTHOR("Jon Oberheide "); --- linux-2.6.4-rc2/crypto/Kconfig 2003-10-08 15:07:08.000000000 -0700 +++ 25/crypto/Kconfig 2004-03-07 20:46:46.000000000 -0800 @@ -140,6 +140,16 @@ config CRYPTO_CAST6 The CAST6 encryption algorithm (synonymous with CAST-256) is described in RFC2612. +config CRYPTO_ARC4 + tristate "ARC4 cipher algorithm" + depends on CRYPTO + help + ARC4 cipher algorithm. + + This is a stream cipher using keys ranging from 8 bits to 2048 + bits in length. ARC4 is commonly used in protocols such as WEP + and SSL. + config CRYPTO_DEFLATE tristate "Deflate compression algorithm" depends on CRYPTO --- linux-2.6.4-rc2/crypto/Makefile 2004-03-03 23:12:43.000000000 -0800 +++ 25/crypto/Makefile 2004-03-07 20:46:46.000000000 -0800 @@ -21,6 +21,7 @@ obj-$(CONFIG_CRYPTO_SERPENT) += serpent. obj-$(CONFIG_CRYPTO_AES) += aes.o obj-$(CONFIG_CRYPTO_CAST5) += cast5.o obj-$(CONFIG_CRYPTO_CAST6) += cast6.o +obj-$(CONFIG_CRYPTO_ARC4) += arc4.o obj-$(CONFIG_CRYPTO_DEFLATE) += deflate.o obj-$(CONFIG_CRYPTO_TEST) += tcrypt.o --- linux-2.6.4-rc2/crypto/tcrypt.c 2004-01-09 00:04:31.000000000 -0800 +++ 25/crypto/tcrypt.c 2004-03-07 20:46:46.000000000 -0800 @@ -61,7 +61,7 @@ static char *tvmem; static char *check[] = { "des", "md5", "des3_ede", "rot13", "sha1", "sha256", "blowfish", "twofish", "serpent", "sha384", "sha512", "md4", "aes", "cast6", - "deflate", NULL + "arc4", "deflate", NULL }; static void @@ -556,6 +556,10 @@ do_test(void) test_cipher ("cast6", MODE_ECB, ENCRYPT, cast6_enc_tv_template, CAST6_ENC_TEST_VECTORS); test_cipher ("cast6", MODE_ECB, DECRYPT, cast6_dec_tv_template, CAST6_DEC_TEST_VECTORS); + //ARC4 + test_cipher ("arc4", MODE_ECB, ENCRYPT, arc4_enc_tv_template, ARC4_ENC_TEST_VECTORS); + test_cipher ("arc4x", MODE_ECB, DECRYPT, arc4_dec_tv_template, ARC4_DEC_TEST_VECTORS); + test_hash("sha384", sha384_tv_template, SHA384_TEST_VECTORS); test_hash("sha512", sha512_tv_template, SHA512_TEST_VECTORS); test_deflate(); @@ -638,6 +642,11 @@ do_test(void) test_cipher ("cast6", MODE_ECB, DECRYPT, cast6_dec_tv_template, CAST6_DEC_TEST_VECTORS); break; + case 16: + test_cipher ("arc4", MODE_ECB, ENCRYPT, arc4_enc_tv_template, ARC4_ENC_TEST_VECTORS); + test_cipher ("arc4", MODE_ECB, DECRYPT, arc4_dec_tv_template, ARC4_DEC_TEST_VECTORS); + break; + #ifdef CONFIG_CRYPTO_HMAC case 100: test_hmac("md5", hmac_md5_tv_template, HMAC_MD5_TEST_VECTORS); --- linux-2.6.4-rc2/crypto/tcrypt.h 2004-01-09 00:04:31.000000000 -0800 +++ 25/crypto/tcrypt.h 2004-03-07 20:46:46.000000000 -0800 @@ -1488,6 +1488,147 @@ struct cipher_testvec cast5_dec_tv_templ }, }; +/* + * ARC4 test vectors from OpenSSL + */ +#define ARC4_ENC_TEST_VECTORS 7 +#define ARC4_DEC_TEST_VECTORS 7 + +struct cipher_testvec arc4_enc_tv_template[] = +{ + { + .key = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }, + .klen = 8, + .input = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }, + .ilen = 8, + .result = { 0x75, 0xb7, 0x87, 0x80, 0x99, 0xe0, 0xc5, 0x96 }, + .rlen = 8, + }, { + .key = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }, + .klen = 8, + .input = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + .ilen = 8, + .result = { 0x74, 0x94, 0xc2, 0xe7, 0x10, 0x4b, 0x08, 0x79 }, + .rlen = 8, + }, { + .key = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + .klen = 8, + .input = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + .ilen = 8, + .result = { 0xde, 0x18, 0x89, 0x41, 0xa3, 0x37, 0x5d, 0x3a }, + .rlen = 8, + }, { + .key = { 0xef, 0x01, 0x23, 0x45}, + .klen = 4, + .input = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 }, + .ilen = 20, + .result = { 0xd6, 0xa1, 0x41, 0xa7, 0xec, 0x3c, 0x38, 0xdf, + 0xbd, 0x61, 0x5a, 0x11, 0x62, 0xe1, 0xc7, 0xba, + 0x36, 0xb6, 0x78, 0x58 }, + .rlen = 20, + }, { + .key = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }, + .klen = 8, + .input = { 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, + 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, + 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, + 0x12, 0x34, 0x56, 0x78 }, + .ilen = 28, + .result = { 0x66, 0xa0, 0x94, 0x9f, 0x8a, 0xf7, 0xd6, 0x89, + 0x1f, 0x7f, 0x83, 0x2b, 0xa8, 0x33, 0xc0, 0x0c, + 0x89, 0x2e, 0xbe, 0x30, 0x14, 0x3c, 0xe2, 0x87, + 0x40, 0x01, 0x1e, 0xcf }, + .rlen = 28, + }, { + .key = { 0xef, 0x01, 0x23, 0x45 }, + .klen = 4, + .input = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 }, + .ilen = 10, + .result = { 0xd6, 0xa1, 0x41, 0xa7, 0xec, 0x3c, 0x38, 0xdf, + 0xbd, 0x61 }, + .rlen = 10, + }, { + .key = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + .klen = 16, + .input = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF }, + .ilen = 8, + .result = { 0x69, 0x72, 0x36, 0x59, 0x1B, 0x52, 0x42, 0xB1 }, + .rlen = 8, + }, +}; + +struct cipher_testvec arc4_dec_tv_template[] = +{ + { + .key = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }, + .klen = 8, + .input = { 0x75, 0xb7, 0x87, 0x80, 0x99, 0xe0, 0xc5, 0x96 }, + .ilen = 8, + .result = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }, + .rlen = 8, + }, { + .key = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }, + .klen = 8, + .input = { 0x74, 0x94, 0xc2, 0xe7, 0x10, 0x4b, 0x08, 0x79 }, + .ilen = 8, + .result = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + .rlen = 8, + }, { + .key = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + .klen = 8, + .input = { 0xde, 0x18, 0x89, 0x41, 0xa3, 0x37, 0x5d, 0x3a }, + .ilen = 8, + .result = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + .rlen = 8, + }, { + .key = { 0xef, 0x01, 0x23, 0x45}, + .klen = 4, + .input = { 0xd6, 0xa1, 0x41, 0xa7, 0xec, 0x3c, 0x38, 0xdf, + 0xbd, 0x61, 0x5a, 0x11, 0x62, 0xe1, 0xc7, 0xba, + 0x36, 0xb6, 0x78, 0x58 }, + .ilen = 20, + .result = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 }, + .rlen = 20, + }, { + .key = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }, + .klen = 8, + .input = { 0x66, 0xa0, 0x94, 0x9f, 0x8a, 0xf7, 0xd6, 0x89, + 0x1f, 0x7f, 0x83, 0x2b, 0xa8, 0x33, 0xc0, 0x0c, + 0x89, 0x2e, 0xbe, 0x30, 0x14, 0x3c, 0xe2, 0x87, + 0x40, 0x01, 0x1e, 0xcf }, + .ilen = 28, + .result = { 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, + 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, + 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, + 0x12, 0x34, 0x56, 0x78 }, + .rlen = 28, + }, { + .key = { 0xef, 0x01, 0x23, 0x45 }, + .klen = 4, + .input = { 0xd6, 0xa1, 0x41, 0xa7, 0xec, 0x3c, 0x38, 0xdf, + 0xbd, 0x61 }, + .ilen = 10, + .result = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 }, + .rlen = 10, + }, { + .key = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + .klen = 16, + .input = { 0x69, 0x72, 0x36, 0x59, 0x1B, 0x52, 0x42, 0xB1 }, + .ilen = 8, + .result = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF }, + .rlen = 8, + }, +}; + + /* * Compression stuff. */ --- linux-2.6.4-rc2/Documentation/binfmt_misc.txt 2003-10-08 15:07:08.000000000 -0700 +++ 25/Documentation/binfmt_misc.txt 2004-03-07 20:47:35.000000000 -0800 @@ -15,7 +15,7 @@ First you must mount binfmt_misc: mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc To actually register a new binary type, you have to set up a string looking like -:name:type:offset:magic:mask:interpreter: (where you can choose the ':' upon +:name:type:offset:magic:mask:interpreter:flags (where you can choose the ':' upon your needs) and echo it to /proc/sys/fs/binfmt_misc/register. Here is what the fields mean: - 'name' is an identifier string. A new /proc file will be created with this @@ -34,6 +34,28 @@ Here is what the fields mean: The mask is anded with the byte sequence of the file. - 'interpreter' is the program that should be invoked with the binary as first argument (specify the full path) + - 'flags' is an optional field that controls several aspects of the invocation + of the interpreter. It is a string of capital letters, each controls a certain + aspect. The following flags are supported - + 'P' - preserve-argv[0]. Legacy behavior of binfmt_misc is to overwrite the + original argv[0] with the full path to the binary. When this flag is + included, binfmt_misc will add an argument to the argument vector for + this purpose, thus preserving the original argv[0]. + 'O' - open-binary. Legacy behavior of binfmt_misc is to pass the full path + of the binary to the interpreter as an argument. When this flag is + included, binfmt_misc will open the file for reading and pass its + descriptor as an argument, instead of the full path, thus allowing + the interpreter to execute non-readable binaries. This feature should + be used with care - the interpreter has to be trusted not to emit + the contents of the non-readable binary. + 'C' - credentials. Currently, the behavior of binfmt_misc is to calculate + the credentials and security token of the new process according to + the interpreter. When this flag is included, these attributes are + calculated according to the binary. It also implies the 'O' flag. + This feature should be used with care as the interpreter + will run with root permissions when a setuid binary owned by root + is run with binfmt_misc. + There are some restrictions: - the whole register string may not exceed 255 characters @@ -83,9 +105,9 @@ If you want to pass special arguments to write a wrapper script for it. See Documentation/java.txt for an example. -Your interpreter should NOT look in the PATH for the filename; the -kernel passes it the full filename to use. Using the PATH can cause -unexpected behaviour and be a security hazard. +Your interpreter should NOT look in the PATH for the filename; the kernel +passes it the full filename (or the file descriptor) to use. Using $PATH can +cause unexpected behaviour and can be a security hazard. There is a web page about binfmt_misc at --- linux-2.6.4-rc2/Documentation/cdrom/ide-cd 2003-06-14 12:18:28.000000000 -0700 +++ 25/Documentation/cdrom/ide-cd 2004-03-07 20:46:45.000000000 -0800 @@ -74,7 +74,7 @@ This driver provides the following featu 3. The CDROM drive should be connected to the host on an IDE interface. Each interface on a system is defined by an I/O port address and an IRQ number, the standard assignments being - 0x170 and 14 for the primary interface and 0x1f0 and 15 for the + 0x1f0 and 14 for the primary interface and 0x170 and 15 for the secondary interface. Each interface can control up to two devices, where each device can be a hard drive, a CDROM drive, a floppy drive, or a tape drive. The two devices on an interface are called `master' @@ -268,8 +268,8 @@ b. Timeout/IRQ errors. - Double-check your hardware configuration to make sure that the IRQ number of your IDE interface matches what the driver expects. - (The usual assignments are 14 for the primary (0x170) interface - and 15 for the secondary (0x1f0) interface.) Also be sure that + (The usual assignments are 14 for the primary (0x1f0) interface + and 15 for the secondary (0x170) interface.) Also be sure that you don't have some other hardware which might be conflicting with the IRQ you're using. Also check the BIOS setup for your system; some have the ability to disable individual IRQ levels, and I've --- linux-2.6.4-rc2/Documentation/crypto/api-intro.txt 2004-03-03 23:12:42.000000000 -0800 +++ 25/Documentation/crypto/api-intro.txt 2004-03-07 20:46:45.000000000 -0800 @@ -186,6 +186,7 @@ Original developers of the crypto algori Dag Arne Osvik (Serpent) Brian Gladman (AES) Kartikey Mahendra Bhatt (CAST6) + Jon Oberheide (ARC4) SHA1 algorithm contributors: Jean-Francois Dive --- linux-2.6.4-rc2/Documentation/DMA-mapping.txt 2003-08-22 19:23:39.000000000 -0700 +++ 25/Documentation/DMA-mapping.txt 2004-03-07 20:46:58.000000000 -0800 @@ -283,7 +283,7 @@ There are two types of DMA mappings: in order to get correct behavior on all platforms. - Streaming DMA mappings which are usually mapped for one DMA transfer, - unmapped right after it (unless you use pci_dma_sync below) and for which + unmapped right after it (unless you use pci_dma_sync_* below) and for which hardware can optimize for sequential accesses. This of "streaming" as "asynchronous" or "outside the coherency @@ -543,14 +543,30 @@ same bus address space) and you could re all bus addresses. If you need to use the same streaming DMA region multiple times and touch -the data in between the DMA transfers, just map it with -pci_map_{single,sg}, and after each DMA transfer call either: +the data in between the DMA transfers, the buffer needs to be synced +properly in order for the cpu and device to see the most uptodate and +correct copy of the DMA buffer. - pci_dma_sync_single(dev, dma_handle, size, direction); +So, firstly, just map it with pci_map_{single,sg}, and after each DMA +transfer call either: + + pci_dma_sync_single_for_cpu(dev, dma_handle, size, direction); or: - pci_dma_sync_sg(dev, sglist, nents, direction); + pci_dma_sync_sg_for_cpu(dev, sglist, nents, direction); + +as appropriate. + +Then, if you wish to let the device get at the DMA area again, +finish accessing the data with the cpu, and then before actually +giving the buffer to the hardware call either: + + pci_dma_sync_single_for_device(dev, dma_handle, size, direction); + +or: + + pci_dma_sync_sg_for_device(dev, sglist, nents, direction); as appropriate. @@ -590,8 +606,9 @@ to use the pci_dma_sync_*() interfaces. * the DMA transfer with the CPU first * so that we see updated contents. */ - pci_dma_sync_single(cp->pdev, cp->rx_dma, cp->rx_len, - PCI_DMA_FROMDEVICE); + pci_dma_sync_single_for_cpu(cp->pdev, cp->rx_dma, + cp->rx_len, + PCI_DMA_FROMDEVICE); /* Now it is safe to examine the buffer. */ hp = (struct my_card_header *) cp->rx_buf; @@ -601,7 +618,13 @@ to use the pci_dma_sync_*() interfaces. pass_to_upper_layers(cp->rx_buf); make_and_setup_new_rx_buf(cp); } else { - /* Just give the buffer back to the card. */ + /* Just sync the buffer and give it back + * to the card. + */ + pci_dma_sync_single_for_device(cp->pdev, + cp->rx_dma, + cp->rx_len, + PCI_DMA_FROMDEVICE); give_rx_buf_to_card(cp); } } @@ -709,12 +732,21 @@ interfaces. To reiterate: When the DMA transfer is complete, invoke: - void pci_dac_dma_sync_single(struct pci_dev *pdev, - dma64_addr_t dma_addr, - size_t len, int direction); + void pci_dac_dma_sync_single_for_cpu(struct pci_dev *pdev, + dma64_addr_t dma_addr, + size_t len, int direction); This must be done before the CPU looks at the buffer again. -This interface behaves identically to pci_dma_sync_{single,sg}(). +This interface behaves identically to pci_dma_sync_{single,sg}_for_cpu(). + +And likewise, if you wish to let the device get back at the buffer after +the cpu has read/written it, invoke: + + void pci_dac_dma_sync_single_for_device(struct pci_dev *pdev, + dma64_addr_t dma_addr, + size_t len, int direction); + +before letting the device access the DMA area again. If you need to get back to the PAGE/OFFSET tuple from a dma64_addr_t the following interfaces are provided: --- linux-2.6.4-rc2/Documentation/DocBook/gadget.tmpl 2003-09-27 18:57:43.000000000 -0700 +++ 25/Documentation/DocBook/gadget.tmpl 2004-03-07 20:46:56.000000000 -0800 @@ -454,6 +454,7 @@ but some optional utilities are provided !Edrivers/usb/gadget/usbstring.c +!Edrivers/usb/gadget/config.c --- linux-2.6.4-rc2/Documentation/early-userspace/README 2003-08-22 19:23:39.000000000 -0700 +++ 25/Documentation/early-userspace/README 2004-03-07 20:47:35.000000000 -0800 @@ -71,5 +71,31 @@ custom initramfs images that meet your n For questions and help, you can sign up for the early userspace mailing list at http://www.zytor.com/mailman/listinfo/klibc +How does it work? +================= + +The kernel has currently 3 ways to mount the root filesystem: + +a) all required device and filesystem drivers compiled into the kernel, no + initrd. init/main.c:init() will call prepare_namespace() to mount the + final root filesystem, based on the root= option and optional init= to run + some other init binary than listed at the end of init/main.c:init(). + +b) some device and filesystem drivers built as modules and stored in an + initrd. The initrd must contain a binary '/linuxrc' which is supposed to + load these driver modules. It is also possible to mount the final root + filesystem via linuxrc and use the pivot_root syscall. The initrd is + mounted and executed via prepare_namespace(). + +c) using initramfs. The call to prepare_namespace() must be skipped. + This means that a binary must do all the work. Said binary can be stored + into initramfs either via modifying usr/gen_init_cpio.c or via the new + initrd format, an cpio archive. It must be called "/init". This binary + is responsible to do all the things prepare_namespace() would do. + + To remain backwards compatibility, the /init binary will only run if it + comes via an initramfs cpio archive. If this is not the case, + init/main.c:init() will run prepare_namespace() to mount the final root + and exec one of the predefined init binaries. Bryan O'Sullivan --- linux-2.6.4-rc2/Documentation/filesystems/proc.txt 2004-03-03 23:12:42.000000000 -0800 +++ 25/Documentation/filesystems/proc.txt 2004-03-07 20:47:49.000000000 -0800 @@ -38,6 +38,7 @@ Table of Contents 2.8 /proc/sys/net/ipv4 - IPV4 settings 2.9 Appletalk 2.10 IPX + 2.11 /proc/sys/fs/mqueue - POSIX message queues filesystem ------------------------------------------------------------------------------ Preface @@ -1814,6 +1815,30 @@ The /proc/net/ipx_route table holds a gives the destination network, the router node (or Directly) and the network address of the router (or Connected) for internal networks. +2.11 /proc/sys/fs/mqueue - POSIX message queues filesystem +---------------------------------------------------------- + +The "mqueue" filesystem provides the necessary kernel features to enable the +creation of a user space library that implements the POSIX message queues +API (as noted by the MSG tag in the POSIX 1003.1-2001 version of the System +Interfaces specification.) + +The "mqueue" filesystem contains values for determining/setting the amount of +resources used by the file system. + +/proc/sys/fs/mqueue/queues_max is a read/write file for setting/getting the +maximum number of message queues allowed on the system. + +/proc/sys/fs/mqueue/msg_max is a read/write file for setting/getting the +maximum number of messages in a queue value. In fact it is the limiting value +for another (user) limit which is set in mq_open invocation. This attribute of +a queue must be less or equal then msg_max. + +/proc/sys/fs/mqueue/msgsize_max is a read/write file for setting/getting the +maximum message size value (it is every message queue's attribute set during +its creation). + + ------------------------------------------------------------------------------ Summary ------------------------------------------------------------------------------ --- linux-2.6.4-rc2/Documentation/filesystems/udf.txt 2003-06-14 12:18:32.000000000 -0700 +++ 25/Documentation/filesystems/udf.txt 2004-03-07 20:47:56.000000000 -0800 @@ -1,7 +1,7 @@ * * ./Documentation/filesystems/udf.txt * -UDF Filesystem version 0.9.5 +UDF Filesystem version 0.9.8.1 If you encounter problems with reading UDF discs using this driver, please report them to linux_udf@hpesjro.fc.hp.com, which is the @@ -16,7 +16,7 @@ The following mount options are supporte gid= Set the default group. umask= Set the default umask. uid= Set the default user. - bs= Set the block size. + bs= Set the block size. unhide Show otherwise hidden files. undelete Show deleted files in lists. adinicb Embed data in the inode (default) @@ -47,15 +47,11 @@ The following expect a offset from the p ------------------------------------------------------------------------------- -For more information see: - http://www.trylinux.com/projects/udf/index.html - For the latest version and toolset see: - http://www.csc.calpoly.edu/~bfennema/udf.html http://linux-udf.sourceforge.net/ Documentation on UDF and ECMA 167 is available FREE from: - http://www.osta.org/ - http://www.ecma.ch/ + http://www.osta.org/ + http://www.ecma-international.org/ Ben Fennema --- linux-2.6.4-rc2/Documentation/filesystems/ufs.txt 2003-06-14 12:18:29.000000000 -0700 +++ 25/Documentation/filesystems/ufs.txt 2004-03-07 20:47:39.000000000 -0800 @@ -20,6 +20,9 @@ ufstype=type_of_ufs 44bsd used in FreeBSD, NetBSD, OpenBSD supported os read-write + ufs2 used in FreeBSD 5.x + supported os read-only + sun used in SunOS (Solaris) supported as read-write --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/Documentation/i386/kgdb/andthen 2004-03-07 20:47:02.000000000 -0800 @@ -0,0 +1,100 @@ + +define set_andthen + set var $thp=0 + set var $thp=(struct kgdb_and_then_struct *)&kgdb_data[0] + set var $at_size = (sizeof kgdb_data)/(sizeof *$thp) + set var $at_oc=kgdb_and_then_count + set var $at_cc=$at_oc +end + +define andthen_next + set var $at_cc=$arg0 +end + +define andthen + andthen_set_edge + if ($at_cc >= $at_oc) + printf "Outside window. Window size is %d\n",($at_oc-$at_low) + else + printf "%d: ",$at_cc + output *($thp+($at_cc++ % $at_size )) + printf "\n" + end +end +define andthen_set_edge + set var $at_oc=kgdb_and_then_count + set var $at_low = $at_oc - $at_size + if ($at_low < 0 ) + set var $at_low = 0 + end + if (( $at_cc > $at_oc) || ($at_cc < $at_low)) + printf "Count outside of window, setting count to " + if ($at_cc >= $at_oc) + set var $at_cc = $at_oc + else + set var $at_cc = $at_low + end + printf "%d\n",$at_cc + end +end + +define beforethat + andthen_set_edge + if ($at_cc <= $at_low) + printf "Outside window. Window size is %d\n",($at_oc-$at_low) + else + printf "%d: ",$at_cc-1 + output *($thp+(--$at_cc % $at_size )) + printf "\n" + end +end + +document andthen_next + andthen_next + . sets the number of the event to display next. If this event + . is not in the event pool, either andthen or beforethat will + . correct it to the nearest event pool edge. The event pool + . ends at the last event recorded and begins + . prior to that. If beforethat is used next, it will display + . event -1. +. + andthen commands are: set_andthen, andthen_next, andthen and beforethat +end + + +document andthen + andthen +. displays the next event in the list. sets up to display +. the oldest saved event first. +. (optional) count of the event to display. +. note the number of events saved is specified at configure time. +. if events are saved between calls to andthen the index will change +. but the displayed event will be the next one (unless the event buffer +. is overrun). +. +. andthen commands are: set_andthen, andthen_next, andthen and beforethat +end + +document set_andthen + set_andthen +. sets up to use the and commands. +. if you have defined your own struct, use the above and +. then enter the following: +. p $thp=(struct kgdb_and_then_structX *)&kgdb_data[0] +. where is the name of your structure. +. +. andthen commands are: set_andthen, andthen_next, andthen and beforethat +end + +document beforethat + beforethat +. displays the next prior event in the list. sets up to +. display the last occuring event first. +. +. note the number of events saved is specified at configure time. +. if events are saved between calls to beforethat the index will change +. but the displayed event will be the next one (unless the event buffer +. is overrun). +. +. andthen commands are: set_andthen, andthen_next, andthen and beforethat +end --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/Documentation/i386/kgdb/debug-nmi.txt 2004-03-07 20:47:02.000000000 -0800 @@ -0,0 +1,37 @@ +Subject: Debugging with NMI +Date: Mon, 12 Jul 1999 11:28:31 -0500 +From: David Grothe +Organization: Gcom, Inc +To: David Grothe + +Kernel hackers: + +Maybe this is old hat, but it is new to me -- + +On an ISA bus machine, if you short out the A1 and B1 pins of an ISA +slot you will generate an NMI to the CPU. This interrupts even a +machine that is hung in a loop with interrupts disabled. Used in +conjunction with kgdb < +ftp://ftp.gcom.com/pub/linux/src/kgdb-2.3.35/kgdb-2.3.35.tgz > you can +gain debugger control of a machine that is hung in the kernel! Even +without kgdb the kernel will print a stack trace so you can find out +where it was hung. + +The A1/B1 pins are directly opposite one another and the farthest pins +towards the bracket end of the ISA bus socket. You can stick a paper +clip or multi-meter probe between them to short them out. + +I had a spare ISA bus to PC104 bus adapter around. The PC104 end of the +board consists of two rows of wire wrap pins. So I wired a push button +between the A1/B1 pins and now have an ISA board that I can stick into +any ISA bus slot for debugger entry. + +Microsoft has a circuit diagram of a PCI card at +http://www.microsoft.com/hwdev/DEBUGGING/DMPSW.HTM. If you want to +build one you will have to mail them and ask for the PAL equations. +Nobody makes one comercially. + +[THIS TIP COMES WITH NO WARRANTY WHATSOEVER. It works for me, but if +your machine catches fire, it is your problem, not mine.] + +-- Dave (the kgdb guy) --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/Documentation/i386/kgdb/gdb-globals.txt 2004-03-07 20:47:02.000000000 -0800 @@ -0,0 +1,71 @@ +Sender: akale@veritas.com +Date: Fri, 23 Jun 2000 19:26:35 +0530 +From: "Amit S. Kale" +Organization: Veritas Software (India) +To: Dave Grothe , linux-kernel@vger.rutgers.edu +CC: David Milburn , + "Edouard G. Parmelan" , + ezannoni@cygnus.com, Keith Owens +Subject: Re: Module debugging using kgdb + +Dave Grothe wrote: +> +> Amit: +> +> There is a 2.4.0 version of kgdb on our ftp site: +> ftp://ftp.gcom.com/pub/linux/src/kgdb. I mirrored your version of gdb +> and loadmodule.sh there. +> +> Have a look at the README file and see if I go it right. If not, send +> me some corrections and I will update it. +> +> Does your version of gdb solve the global variable problem? + +Yes. +Thanks to Elena Zanoni, gdb (developement version) can now calculate +correctly addresses of dynamically loaded object files. I have not been +following gdb developement for sometime and am not sure when symbol +address calculation fix is going to appear in a gdb stable version. + +Elena, any idea when the fix will make it to a prebuilt gdb from a +redhat release? + +For the time being I have built a gdb developement version. It can be +used for module debugging with loadmodule.sh script. + +The problem with calculating of module addresses with previous versions +of gdb was as follows: +gdb did not use base address of a section while calculating address of +a symbol in the section in an object file loaded via 'add-symbol-file'. +It used address of .text segment instead. Due to this addresses of +symbols in .data, .bss etc. (e.g. global variables) were calculated incorrectly. + +Above mentioned fix allow gdb to use base address of a segment while +calculating address of a symbol in it. It adds a parameter '-s' to +'add-symbol-file' command for specifying base address of a segment. + +loadmodule.sh script works as follows. + +1. Copy a module file to target machine. +2. Load the module on the target machine using insmod with -m parameter. +insmod produces a module load map which contains base addresses of all +sections in the module and addresses of symbols in the module file. +3. Find all sections and their base addresses in the module from +the module map. +4. Generate a script that loads the module file. The script uses +'add-symbol-file' and specifies address of text segment followed by +addresses of all segments in the module. + +Here is an example gdb script produced by loadmodule.sh script. + +add-symbol-file foo 0xd082c060 -s .text.lock 0xd08cbfb5 +-s .fixup 0xd08cfbdf -s .rodata 0xd08cfde0 -s __ex_table 0xd08e3b38 +-s .data 0xd08e3d00 -s .bss 0xd08ec8c0 -s __ksymtab 0xd08ee838 + +With this command gdb can calculate addresses of symbols in ANY segment +in a module file. + +Regards. +-- +Amit Kale +Veritas Software ( http://www.veritas.com ) --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/Documentation/i386/kgdb/gdbinit 2004-03-07 20:47:02.000000000 -0800 @@ -0,0 +1,14 @@ +shell echo -e "\003" >/dev/ttyS0 +set remotebaud 38400 +target remote /dev/ttyS0 +define si +stepi +printf "EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n", $eax, $ebx, $ecx, $edx +printf "ESI=%08x EDI=%08x EBP=%08x ESP=%08x\n", $esi, $edi, $ebp, $esp +x/i $eip +end +define ni +nexti +printf "EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n", $eax, $ebx, $ecx, $edx +printf "ESI=%08x EDI=%08x EBP=%08x ESP=%08x\n", $esi, $edi, $ebp, $esp +x/i $eip --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/Documentation/i386/kgdb/gdbinit.hw 2004-03-07 20:47:02.000000000 -0800 @@ -0,0 +1,117 @@ + +#Using ia-32 hardware breakpoints. +# +#4 hardware breakpoints are available in ia-32 processors. These breakpoints +#do not need code modification. They are set using debug registers. +# +#Each hardware breakpoint can be of one of the +#three types: execution, write, access. +#1. An Execution breakpoint is triggered when code at the breakpoint address is +#executed. +#2. A write breakpoint ( aka watchpoints ) is triggered when memory location +#at the breakpoint address is written. +#3. An access breakpoint is triggered when memory location at the breakpoint +#address is either read or written. +# +#As hardware breakpoints are available in limited number, use software +#breakpoints ( br command in gdb ) instead of execution hardware breakpoints. +# +#Length of an access or a write breakpoint defines length of the datatype to +#be watched. Length is 1 for char, 2 short , 3 int. +# +#For placing execution, write and access breakpoints, use commands +#hwebrk, hwwbrk, hwabrk +#To remove a breakpoint use hwrmbrk command. +# +#These commands take following types of arguments. For arguments associated +#with each command, use help command. +#1. breakpointno: 0 to 3 +#2. length: 1 to 3 +#3. address: Memory location in hex ( without 0x ) e.g c015e9bc +# +#Use the command exinfo to find which hardware breakpoint occured. + +#hwebrk breakpointno address +define hwebrk + maintenance packet Y$arg0,0,0,$arg1 +end +document hwebrk + hwebrk
+ Places a hardware execution breakpoint + = 0 - 3 +
= Hex digits without leading "0x". +end + +#hwwbrk breakpointno length address +define hwwbrk + maintenance packet Y$arg0,1,$arg1,$arg2 +end +document hwwbrk + hwwbrk
+ Places a hardware write breakpoint + = 0 - 3 + = 1 (1 byte), 2 (2 byte), 3 (4 byte) +
= Hex digits without leading "0x". +end + +#hwabrk breakpointno length address +define hwabrk + maintenance packet Y$arg0,1,$arg1,$arg2 +end +document hwabrk + hwabrk
+ Places a hardware access breakpoint + = 0 - 3 + = 1 (1 byte), 2 (2 byte), 3 (4 byte) +
= Hex digits without leading "0x". +end + +#hwrmbrk breakpointno +define hwrmbrk + maintenance packet y$arg0 +end +document hwrmbrk + hwrmbrk + = 0 - 3 + Removes a hardware breakpoint +end + +define reboot + maintenance packet r +end +#exinfo +define exinfo + maintenance packet qE +end +document exinfo + exinfo + Gives information about a breakpoint. +end +define get_th + p $th=(struct thread_info *)((int)$esp & ~8191) +end +document get_th + get_tu + Gets and prints the current thread_info pointer, Defines th to be it. +end +define get_cu + p $cu=((struct thread_info *)((int)$esp & ~8191))->task +end +document get_cu + get_cu + Gets and print the "current" value. Defines $cu to be it. +end +define int_off + set var $flags=$eflags + set $eflags=$eflags&~0x200 + end +define int_on + set var $eflags|=$flags&0x200 + end +document int_off + saves the current interrupt state and clears the processor interrupt + flag. Use int_on to restore the saved flag. +end +document int_on + Restores the interrupt flag saved by int_off. +end --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/Documentation/i386/kgdb/gdbinit-modules 2004-03-07 20:47:02.000000000 -0800 @@ -0,0 +1,146 @@ +# +# Usefull GDB user-command to debug Linux Kernel Modules with gdbstub. +# +# This don't work for Linux-2.0 or older. +# +# Author Edouard G. Parmelan +# +# +# Fri Apr 30 20:33:29 CEST 1999 +# First public release. +# +# Major cleanup after experiment Linux-2.0 kernel without success. +# Symbols of a module are not in the correct order, I can't explain +# why :( +# +# Fri Mar 19 15:41:40 CET 1999 +# Initial version. +# +# Thu Jan 6 16:29:03 CST 2000 +# A little fixing by Dave Grothe +# +# Mon Jun 19 09:33:13 CDT 2000 +# Alignment changes from Edouard Parmelan +# +# The basic idea is to find where insmod load the module and inform +# GDB to load the symbol table of the module with the GDB command +# ``add-symbol-file
''. +# +# The Linux kernel holds the list of all loaded modules in module_list, +# this list end with &kernel_module (exactly with module->next == NULL, +# but the last module is not a real module). +# +# Insmod allocates the struct module before the object file. Since +# Linux-2.1, this structure contain his size. The real address of +# the object file is then (char*)module + module->size_of_struct. +# +# You can use three user functions ``mod-list'', ``mod-print-symbols'' +# and ``add-module-symbols''. +# +# mod-list list all loaded modules with the format: +# +# +# As soon as you have found the address of your module, you can +# print its exported symbols (mod-print-symbols) or inform GDB to add +# symbols from your module file (mod-add-symbols). +# +# The argument that you give to mod-print-symbols or mod-add-symbols +# is the from the mod-list command. +# +# When using the mod-add-symbols command you must also give the full +# pathname of the modules object code file. +# +# The command mod-add-lis is an example of how to make this easier. +# You can edit this macro to contain the path name of your own +# favorite module and then use it as a shorthand to load it. You +# still need the module-address, however. +# +# The internal function ``mod-validate'' set the GDB variable $mod +# as a ``struct module*'' if the kernel known the module otherwise +# $mod is set to NULL. This ensure to not add symbols for a wrong +# address. +# +# Have a nice hacking day ! +# +# +define mod-list + set $mod = (struct module*)module_list + # the last module is the kernel, ignore it + while $mod != &kernel_module + printf "%p\t%s\n", (long)$mod, ($mod)->name + set $mod = $mod->next + end +end +document mod-list +List all modules in the form: +Use the as the argument for the other +mod-commands: mod-print-symbols, mod-add-symbols. +end + +define mod-validate + set $mod = (struct module*)module_list + while ($mod != $arg0) && ($mod != &kernel_module) + set $mod = $mod->next + end + if $mod == &kernel_module + set $mod = 0 + printf "%p is not a module\n", $arg0 + end +end +document mod-validate +mod-validate +Internal user-command used to validate the module parameter. +If is a real loaded module, set $mod to it otherwise set $mod to 0. +end + + +define mod-print-symbols + mod-validate $arg0 + if $mod != 0 + set $i = 0 + while $i < $mod->nsyms + set $sym = $mod->syms[$i] + printf "%p\t%s\n", $sym->value, $sym->name + set $i = $i + 1 + end + end +end +document mod-print-symbols +mod-print-symbols +Print all exported symbols of the module. see mod-list +end + + +define mod-add-symbols-align + mod-validate $arg0 + if $mod != 0 + set $mod_base = ($mod->size_of_struct + (long)$mod) + if ($arg2 != 0) && (($mod_base & ($arg2 - 1)) != 0) + set $mod_base = ($mod_base | ($arg2 - 1)) + 1 + end + add-symbol-file $arg1 $mod_base + end +end +document mod-add-symbols-align +mod-add-symbols-align +Load the symbols table of the module from the object file where +first section aligment is . +To retreive alignment, use `objdump -h '. +end + +define mod-add-symbols + mod-add-symbols-align $arg0 $arg1 sizeof(long) +end +document mod-add-symbols +mod-add-symbols +Load the symbols table of the module from the object file. +Default alignment is 4. See mod-add-symbols-align. +end + +define mod-add-lis + mod-add-symbols-align $arg0 /usr/src/LiS/streams.o 16 +end +document mod-add-lis +mod-add-lis +Does mod-add-symbols /usr/src/LiS/streams.o +end --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/Documentation/i386/kgdb/kgdbeth.txt 2004-03-07 20:47:03.000000000 -0800 @@ -0,0 +1,92 @@ +KGDB over ethernet +================== + +Authors +------- + +Robert Walsh (2.6 port) +wangdi (2.6 port) +Matt Mackall (netpoll api) +San Mehat (original 2.4 code) + + +Introduction +------------ + +KGDB supports debugging over ethernet (kgdboe) via polling of a given +network interface. Most cards should be supported automatically. +Debugging facilities are available as soon as the network driver and +kgdboe have initialized. Unfortunately, this is too late in the boot +process for debugging some issues, but works quite well for many +others. This should not interfere with normal network usage and +doesn't require a dedicated NIC. + +Terminology +----------- + +This document uses the following terms: + + TARGET: the machine being debugged. + HOST: the machine running gdb. + + +Usage +----- + +You need to use the following command-line option on the TARGET kernel: + + kgdboe=[tgt-port]@/[dev],[host-port]@/[host-macaddr] + + where + tgt-port source for UDP packets (defaults to 6443) + tgt-ip source IP to use (interface address) + dev network interface (eth0) + host-port HOST UDP port (6442) (not really used) + host-ip IP address for HOST machine + host-macaddr ethernet MAC address for HOST (ff:ff:ff:ff:ff:ff) + + examples: + + kgdboe=7000@192.168.0.1/eth1,7001@192.168.0.2/00:05:3C:04:47:5D + this machine is 192.168.0.1 on eth1 + remote machine is 192.168.0.2 with MAC address 00:05:3C:04:47:5D + listen for gdb packets on port 7000 + send unsolicited gdb packets to port 7001 + + kgdboe=@192.168.0.1/,@192.168.0.2/ + this machine is 192.168.0.1 on default interface eth0 + remote machine is 192.168.0.2, use default broadcast MAC address + listen for gdb packets on default port 6443 + send unsolicited gdb packets to port 6442 + +Only packets originating from the configured HOST IP address will be +accepted by the debugger. + +On the HOST side, run gdb as normal and use a remote UDP host as the +target: + + % gdb ./vmlinux + GNU gdb Red Hat Linux (5.3post-0.20021129.18rh) + Copyright 2003 Free Software Foundation, Inc. + GDB is free software, covered by the GNU General Public License, and you are + welcome to change it and/or distribute copies of it under certain conditions. + Type "show copying" to see the conditions. + There is absolutely no warranty for GDB. Type "show warranty" for details. + This GDB was configured as "i386-redhat-linux-gnu"... + (gdb) target remote udp:HOSTNAME:6443 + +You can now continue as if you were debugging over a serial line. + +Limitations +----------- + +The current release of this code is exclusive of using kgdb on a +serial interface, so you must boot without the kgdboe option to use +serial debugging. Trying to debug the network driver while using it +will prove interesting. + +Bug reports +----------- + +Send bug reports to Robert Walsh and Matt +Mackall . --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/Documentation/i386/kgdb/kgdb.txt 2004-03-07 20:47:02.000000000 -0800 @@ -0,0 +1,775 @@ +Last edit: <20030806.1637.12> +This file has information specific to the i386 kgdb option. Other +platforms with the kgdb option may behave in a similar fashion. + +New features: +============ +20030806.1557.37 +This version was made against the 2.6.0-test2 kernel. We have made the +following changes: + +- The getthread() code in the stub calls find_task_by_pid(). It fails + if we are early in the bring up such that the pid arrays have yet to + be allocated. We have added a line to kernel/pid.c to make + "kgdb_pid_init_done" true once the arrays are allocated. This way the + getthread() code knows not to call. This is only used by the thread + debugging stuff and threads will not yet exist at this point in the + boot. + +- For some reason, gdb was not asking for a new thread list when the + "info thread" command was given. We changed to the newer version of + the thread info command and gdb now seems to ask when needed. Result, + we now get all threads in the thread list. + +- We now respond to the ThreadExtraInfo request from gdb with the thread + name from task_struct .comm. This then appears in the thread list. + Thoughts on additional options for this are welcome. Things such as + "has BKL" and "Preempted" come to mind. I think we could have a flag + word that could enable different bits of info here. + +- We now honor, sort of, the C and S commands. These are continue and + single set after delivering a signal. We ignore the signal and do the + requested action. This only happens when we told gdb that a signal + was the reason for entry, which is only done on memory faults. The + result is that you can now continue into the Oops. + +- We changed the -g to -gdwarf-2. This seems to be the same as -ggdb, + but it is more exact on what language to use. + +- We added two dwarf2 include files and a bit of code at the end of + entry.S. This does not yet work, so it is disabled. Still we want to + keep track of the code and "maybe" someone out there can fix it. + +- Randy Dunlap sent some fix ups for this file which are now merged. + +- Hugh Dickins sent a fix to a bit of code in traps.c that prevents a + compiler warning if CONFIG_KGDB is off (now who would do that :). + +- Andrew Morton sent a fix for the serial driver which is now merged. + +- Andrew also sent a change to the stub around the cpu managment code + which is also merged. + +- Andrew also sent a patch to make "f" as well as "g" work as SysRq + commands to enter kgdb, merged. + +- If CONFIG_KGDB and CONFIG_DEBUG_SPINLOCKS are both set we added a + "who" field to the spinlock data struct. This is filled with + "current" when ever the spinlock suceeds. Useful if you want to know + who has the lock. + +_ And last, but not least, we fixed the "get_cu" macro to properly get + the current value of "current". + +New features: +============ +20030505.1827.27 +We are starting to align with the sourceforge version, at least in +commands. To this end, the boot command string to start kgdb at +boot time has been changed from "kgdb" to "gdb". + +Andrew Morton sent a couple of patches which are now included as follows: +1.) We now return a flag to the interrupt handler. +2.) We no longer use smp_num_cpus (a conflict with the lock meter). +3.) And from William Lee Irwin III code to make + sure high-mem is set up before we attempt to register our interrupt + handler. +We now include asm/kgdb.h from config.h so you will most likely never +have to include it. It also 'NULLS' the kgdb macros you might have in +your code when CONFIG_KGDB is not defined. This allows you to just +turn off CONFIG_KGDB to turn off all the kgdb_ts() calls and such. +This include is conditioned on the machine being an x86 so as to not +mess with other archs. + +20020801.1129.03 +This is currently the version for the 2.4.18 (and beyond?) kernel. + +We have several new "features" beginning with this version: + +1.) Kgdb now syncs the "other" CPUs with a cross-CPU NMI. No more + waiting and it will pull that guy out of an IRQ off spin lock :) + +2.) We doctored up the code that tells where a task is waiting and + included it so that the "info thread" command will show a bit more + than "schedule()". Try it... + +3.) Added the ability to call a function from gdb. All the standard gdb + issues apply, i.e. if you hit a breakpoint in the function, you are + not allowed to call another (gdb limitation, not kgdb). To help + this capability we added a memory allocation function. Gdb does not + return this memory (it is used for strings that you pass to that function + you are calling from gdb) so we fixed up a way to allow you to + manually return the memory (see below). + +4.) Kgdb time stamps (kgdb_ts()) are enhanced to expand what was the + interrupt flag to now also include the preemption count and the + "in_interrupt" info. The flag is now called "with_pif" to indicate + the order, preempt_count, in_interrupt, flag. The preempt_count is + shifted left by 4 bits so you can read the count in hex by dropping + the low order digit. In_interrupt is in bit 1, and the flag is in + bit 0. + +5.) The command: "p kgdb_info" is now expanded and prints something + like: +(gdb) p kgdb_info +$2 = {used_malloc = 0, called_from = 0xc0107506, entry_tsc = 67468627259, + errcode = 0, vector = 3, print_debug_info = 0, hold_on_sstep = 1, + cpus_waiting = {{task = 0xc027a000, pid = 32768, hold = 0, + regs = 0xc027bf84}, {task = 0x0, pid = 0, hold = 0, regs = 0x0}}} + + Things to note here: a.) used_malloc is the amount of memory that + has been malloc'ed to do calls from gdb. You can reclaim this + memory like this: "p kgdb_info.used_malloc=0" Cool, huh? b.) + cpus_waiting is now "sized" by the number of CPUs you enter at + configure time in the kgdb configure section. This is NOT used + anywhere else in the system, but it is "nice" here. c.) The task's + "pid" is now in the structure. This is the pid you will need to use + to decode to the thread id to get gdb to look at that thread. + Remember that the "info thread" command prints a list of threads + wherein it numbers each thread with its reference number followed + by the thread's pid. Note that the per-CPU idle threads actually + have pids of 0 (yes, there is more than one pid 0 in an SMP system). + To avoid confusion, kgdb numbers these threads with numbers beyond + the MAX_PID. That is why you see 32768 and above. + +6.) A subtle change, we now provide the complete register set for tasks + that are active on the other CPUs. This allows better trace back on + those tasks. + + And, let's mention what we could not fix. Back-trace from all but the + thread that we trapped will, most likely, have a bogus entry in it. + The problem is that gdb does not recognize the entry code for + functions that use "current" near (at all?) the entry. The compiler + is putting the "current" decode as the first two instructions of the + function where gdb expects to find %ebp changing code. Back trace + also has trouble with interrupt frames. I am talking with Daniel + Jacobowitz about some way to fix this, but don't hold your breath. + +20011220.0050.35 +Major enhancement with this version is the ability to hold one or more +CPUs in an SMP system while allowing the others to continue. Also, by +default only the current CPU is enabled on single-step commands (please +note that gdb issues single-step commands at times other than when you +use the si command). + +Another change is to collect some useful information in +a global structure called "kgdb_info". You should be able to just: + +p kgdb_info + +although I have seen cases where the first time this is done gdb just +prints the first member but prints the whole structure if you then enter +CR (carriage return or enter). This also works: + +p *&kgdb_info + +Here is a sample: +(gdb) p kgdb_info +$4 = {called_from = 0xc010732c, entry_tsc = 32804123790856, errcode = 0, + vector = 3, print_debug_info = 0} + +"Called_from" is the return address from the current entry into kgdb. +Sometimes it is useful to know why you are in kgdb, for example, was +it an NMI or a real breakpoint? The simple way to interrogate this +return address is: + +l *0xc010732c + +which will print the surrounding few lines of source code. + +"Entry_tsc" is the CPU TSC on entry to kgdb (useful to compare to the +kgdb_ts entries). + +"errcode" and "vector" are other entry parameters which may be helpful on +some traps. + +"print_debug_info" is the internal debugging kgdb print enable flag. Yes, +you can modify it. + +In SMP systems kgdb_info also includes the "cpus_waiting" structure and +"hold_on_step": + +(gdb) p kgdb_info +$7 = {called_from = 0xc0112739, entry_tsc = 1034936624074, errcode = 0, + vector = 2, print_debug_info = 0, hold_on_sstep = 1, cpus_waiting = {{ + task = 0x0, hold = 0, regs = 0x0}, {task = 0xc71b8000, hold = 0, + regs = 0xc71b9f70}, {task = 0x0, hold = 0, regs = 0x0}, {task = 0x0, + hold = 0, regs = 0x0}, {task = 0x0, hold = 0, regs = 0x0}, {task = 0x0, + hold = 0, regs = 0x0}, {task = 0x0, hold = 0, regs = 0x0}, {task = 0x0, + hold = 0, regs = 0x0}}} + +"Cpus_waiting" has an entry for each CPU other than the current one that +has been stopped. Each entry contains the task_struct address for that +CPU, the address of the regs for that task and a hold flag. All these +have the proper typing so that, for example: + +p *kgdb_info.cpus_waiting[1].regs + +will print the registers for CPU 1. + +"Hold_on_sstep" is a new feature with this version and comes up set or +true. What this means is that whenever kgdb is asked to single-step all +other CPUs are held (i.e. not allowed to execute). The flag applies to +all but the current CPU and, again, can be changed: + +p kgdb_info.hold_on_sstep=0 + +restores the old behavior of letting all CPUs run during single-stepping. + +Likewise, each CPU has a "hold" flag, which if set, locks that CPU out +of execution. Note that this has some risk in cases where the CPUs need +to communicate with each other. If kgdb finds no CPU available on exit, +it will push a message thru gdb and stay in kgdb. Note that it is legal +to hold the current CPU as long as at least one CPU can execute. + +20010621.1117.09 +This version implements an event queue. Events are signaled by calling +a function in the kgdb stub and may be examined from gdb. See EVENTS +below for details. This version also tightens up the interrupt and SMP +handling to not allow interrupts on the way to kgdb from a breakpoint +trap. It is fine to allow these interrupts for user code, but not +system debugging. + +Version +======= + +This version of the kgdb package was developed and tested on +kernel version 2.4.16. It will not install on any earlier kernels. +It is possible that it will continue to work on later versions +of 2.4 and then versions of 2.5 (I hope). + + +Debugging Setup +=============== + +Designate one machine as the "development" machine. This is the +machine on which you run your compiles and which has your source +code for the kernel. Designate a second machine as the "target" +machine. This is the machine that will run your experimental +kernel. + +The two machines will be connected together via a serial line out +one or the other of the COM ports of the PC. You will need the +appropriate modem eliminator (null modem) cable(s) for this. + +Decide on which tty port you want the machines to communicate, then +connect them up back-to-back using the null modem cable. COM1 is +/dev/ttyS0 and COM2 is /dev/ttyS1. You should test this connection +with the two machines prior to trying to debug a kernel. Once you +have it working, on the TARGET machine, enter: + +setserial /dev/ttyS0 (or what ever tty you are using) + +and record the port address and the IRQ number. + +On the DEVELOPMENT machine you need to apply the patch for the kgdb +hooks. You have probably already done that if you are reading this +file. + +On your DEVELOPMENT machine, go to your kernel source directory and do +"make Xconfig" where X is one of "x", "menu", or "". If you are +configuring in the standard serial driver, it must not be a module. +Either yes or no is ok, but making the serial driver a module means it +will initialize after kgdb has set up the UART interrupt code and may +cause a failure of the control-C option discussed below. The configure +question for the serial driver is under the "Character devices" heading +and is: + +"Standard/generic (8250/16550 and compatible UARTs) serial support" + +Go down to the kernel debugging menu item and open it up. Enable the +kernel kgdb stub code by selecting that item. You can also choose to +turn on the "-ggdb -O1" compile options. The -ggdb causes the compiler +to put more debug info (like local symbols) in the object file. On the +i386 -g and -ggdb are the same so this option just reduces to "O1". The +-O1 reduces the optimization level. This may be helpful in some cases, +be aware, however, that this may also mask the problem you are looking +for. + +The baud rate. Default is 115200. What ever you choose be sure that +the host machine is set to the same speed. I recommend the default. + +The port. This is the I/O address of the serial UART that you should +have gotten using setserial as described above. The standard COM1 port +(3f8) using IRQ 4 is default. COM2 is 2f8 which by convention uses IRQ +3. + +The port IRQ (see above). + +Stack overflow test. This option makes a minor change in the trap, +system call and interrupt code to detect stack overflow and transfer +control to kgdb if it happens. (Some platforms have this in the +baseline code, but the i386 does not.) + +You can also configure the system to recognize the boot option +"console=kgdb" which if given will cause all console output during +booting to be put thru gdb as well as other consoles. This option +requires that gdb and kgdb be connected prior to sending console output +so, if they are not, a breakpoint is executed to force the connection. +This will happen before any kernel output (it is going thru gdb, right), +and will stall the boot until the connection is made. + +You can also configure in a patch to SysRq to enable the kGdb SysRq. +This request generates a breakpoint. Since the serial port IRQ line is +set up after any serial drivers, it is possible that this command will +work when the control-C will not. + +Save and exit the Xconfig program. Then do "make clean" , "make dep" +and "make bzImage" (or whatever target you want to make). This gets the +kernel compiled with the "-g" option set -- necessary for debugging. + +You have just built the kernel on your DEVELOPMENT machine that you +intend to run on your TARGET machine. + +To install this new kernel, use the following installation procedure. +Remember, you are on the DEVELOPMENT machine patching the kernel source +for the kernel that you intend to run on the TARGET machine. + +Copy this kernel to your target machine using your usual procedures. I +usually arrange to copy development: +/usr/src/linux/arch/i386/boot/bzImage to /vmlinuz on the TARGET machine +via a LAN based NFS access. That is, I run the cp command on the target +and copy from the development machine via the LAN. Run Lilo (see "man +lilo" for details on how to set this up) on the new kernel on the target +machine so that it will boot! Then boot the kernel on the target +machine. + +On the DEVELOPMENT machine, create a file called .gdbinit in the +directory /usr/src/linux. An example .gdbinit file looks like this: + +shell echo -e "\003" >/dev/ttyS0 +set remotebaud 38400 (or what ever speed you have chosen) +target remote /dev/ttyS0 + + +Change the "echo" and "target" definition so that it specifies the tty +port that you intend to use. Change the "remotebaud" definition to +match the data rate that you are going to use for the com line. + +You are now ready to try it out. + +Boot your target machine with "kgdb" in the boot command i.e. something +like: + +lilo> test kgdb + +or if you also want console output thru gdb: + +lilo> test kgdb console=kgdb + +You should see the lilo message saying it has loaded the kernel and then +all output stops. The kgdb stub is trying to connect with gdb. Start +gdb something like this: + + +On your DEVELOPMENT machine, cd /usr/src/linux and enter "gdb vmlinux". +When gdb gets the symbols loaded it will read your .gdbinit file and, if +everything is working correctly, you should see gdb print out a few +lines indicating that a breakpoint has been taken. It will actually +show a line of code in the target kernel inside the kgdb activation +code. + +The gdb interaction should look something like this: + + linux-dev:/usr/src/linux# gdb vmlinux + GDB is free software and you are welcome to distribute copies of it + under certain conditions; type "show copying" to see the conditions. + There is absolutely no warranty for GDB; type "show warranty" for details. + GDB 4.15.1 (i486-slackware-linux), + Copyright 1995 Free Software Foundation, Inc... + breakpoint () at i386-stub.c:750 + 750 } + (gdb) + +You can now use whatever gdb commands you like to set breakpoints. +Enter "continue" to start your target machine executing again. At this +point the target system will run at full speed until it encounters +your breakpoint or gets a segment violation in the kernel, or whatever. + +If you have the kgdb console enabled when you continue, gdb will print +out all the console messages. + +The above example caused a breakpoint relatively early in the boot +process. For the i386 kgdb it is possible to code a break instruction +as the first C-language point in init/main.c, i.e. as the first instruction +in start_kernel(). This could be done as follows: + +#include + breakpoint(); + +This breakpoint() is really a function that sets up the breakpoint and +single-step hardware trap cells and then executes a breakpoint. Any +early hard coded breakpoint will need to use this function. Once the +trap cells are set up they need not be set again, but doing it again +does not hurt anything, so you don't need to be concerned about which +breakpoint is hit first. Once the trap cells are set up (and the kernel +sets them up in due course even if breakpoint() is never called) the +macro: + +BREAKPOINT; + +will generate an inline breakpoint. This may be more useful as it stops +the processor at the instruction instead of in a function a step removed +from the location of interest. In either case must be +included to define both breakpoint() and BREAKPOINT. + +Triggering kgdbstub at other times +================================== + +Often you don't need to enter the debugger until much later in the boot +or even after the machine has been running for some time. Once the +kernel is booted and interrupts are on, you can force the system to +enter the debugger by sending a control-C to the debug port. This is +what the first line of the recommended .gdbinit file does. This allows +you to start gdb any time after the system is up as well as when the +system is already at a breakpoint. (In the case where the system is +already at a breakpoint the control-C is not needed, however, it will +be ignored by the target so no harm is done. Also note the the echo +command assumes that the port speed is already set. This will be true +once gdb has connected, but it is best to set the port speed before you +run gdb.) + +Another simple way to do this is to put the following file in you ~/bin +directory: + +#!/bin/bash +echo -e "\003" > /dev/ttyS0 + +Here, the ttyS0 should be replaced with what ever port you are using. +The "\003" is control-C. Once you are connected with gdb, you can enter +control-C at the command prompt. + +An alternative way to get control to the debugger is to enable the kGdb +SysRq command. Then you would enter Alt-SysRq-g (all three keys at the +same time, but push them down in the order given). To refresh your +memory of the available SysRq commands try Alt-SysRq-=. Actually any +undefined command could replace the "=", but I like to KNOW that what I +am pushing will never be defined. + +Debugging hints +=============== + +You can break into the target machine at any time from the development +machine by typing ^C (see above paragraph). If the target machine has +interrupts enabled this will stop it in the kernel and enter the +debugger. + +There is unfortunately no way of breaking into the kernel if it is +in a loop with interrupts disabled, so if this happens to you then +you need to place exploratory breakpoints or printk's into the kernel +to find out where it is looping. The exploratory breakpoints can be +entered either thru gdb or hard coded into the source. This is very +handy if you do something like: + +if () BREAKPOINT; + + +There is a copy of an e-mail in the Documentation/i386/kgdb/ directory +(debug-nmi.txt) which describes how to create an NMI on an ISA bus +machine using a paper clip. I have a sophisticated version of this made +by wiring a push button switch into a PC104/ISA bus adapter card. The +adapter card nicely furnishes wire wrap pins for all the ISA bus +signals. + +When you are done debugging the kernel on the target machine it is a +good idea to leave it in a running state. This makes reboots faster, +bypassing the fsck. So do a gdb "continue" as the last gdb command if +this is possible. To terminate gdb itself on the development machine +and leave the target machine running, first clear all breakpoints and +continue, then type ^Z to suspend gdb and then kill it with "kill %1" or +something similar. + +If gdbstub Does Not Work +======================== + +If it doesn't work, you will have to troubleshoot it. Do the easy +things first like double checking your cabling and data rates. You +might try some non-kernel based programs to see if the back-to-back +connection works properly. Just something simple like cat /etc/hosts +>/dev/ttyS0 on one machine and cat /dev/ttyS0 on the other will tell you +if you can send data from one machine to the other. Make sure it works +in both directions. There is no point in tearing out your hair in the +kernel if the line doesn't work. + +All of the real action takes place in the file +/usr/src/linux/arch/i386/kernel/kgdb_stub.c. That is the code on the target +machine that interacts with gdb on the development machine. In gdb you can +turn on a debug switch with the following command: + + set remotedebug + +This will print out the protocol messages that gdb is exchanging with +the target machine. + +Another place to look is /usr/src/arch/i386/lib/kgdb_serial.c. This is +the code that talks to the serial port on the target side. There might +be a problem there. In particular there is a section of this code that +tests the UART which will tell you what UART you have if you define +"PRNT" (just remove "_off" from the #define PRNT_off). To view this +report you will need to boot the system without any beakpoints. This +allows the kernel to run to the point where it calls kgdb to set up +interrupts. At this time kgdb will test the UART and print out the type +it finds. (You need to wait so that the printks are actually being +printed. Early in the boot they are cached, waiting for the console to +be enabled. Also, if kgdb is entered thru a breakpoint it is possible +to cause a dead lock by calling printk when the console is locked. The +stub thus avoids doing printks from breakpoints, especially in the +serial code.) At this time, if the UART fails to do the expected thing, +kgdb will print out (using printk) information on what failed. (These +messages will be buried in all the other boot up messages. Look for +lines that start with "gdb_hook_interrupt:". You may want to use dmesg +once the system is up to view the log. If this fails or if you still +don't connect, review your answers for the port address. Use: + +setserial /dev/ttyS0 + +to get the current port and IRQ information. This command will also +tell you what the system found for the UART type. The stub recognizes +the following UART types: + +16450, 16550, and 16550A + +If you are really desperate you can use printk debugging in the +kgdbstub code in the target kernel until you get it working. In particular, +there is a global variable in /usr/src/linux/arch/i386/kernel/kgdb_stub.c +named "remote_debug". Compile your kernel with this set to 1, rather +than 0 and the debug stub will print out lots of stuff as it does +what it does. Likewise there are debug printks in the kgdb_serial.c +code that can be turned on with simple changes in the macro defines. + + +Debugging Loadable Modules +========================== + +This technique comes courtesy of Edouard Parmelan + + +When you run gdb, enter the command + +source gdbinit-modules + +This will read in a file of gdb macros that was installed in your +kernel source directory when kgdb was installed. This file implements +the following commands: + +mod-list + Lists the loaded modules in the form + +mod-print-symbols + Prints all the symbols in the indicated module. + +mod-add-symbols + Loads the symbols from the object file and associates them + with the indicated module. + +After you have loaded the module that you want to debug, use the command +mod-list to find the of your module. Then use that +address in the mod-add-symbols command to load your module's symbols. +From that point onward you can debug your module as if it were a part +of the kernel. + +The file gdbinit-modules also contains a command named mod-add-lis as +an example of how to construct a command of your own to load your +favorite module. The idea is to "can" the pathname of the module +in the command so you don't have to type so much. + +Threads +======= + +Each process in a target machine is seen as a gdb thread. gdb thread +related commands (info threads, thread n) can be used. + +ia-32 hardware breakpoints +========================== + +kgdb stub contains support for hardware breakpoints using debugging features +of ia-32(x86) processors. These breakpoints do not need code modification. +They use debugging registers. 4 hardware breakpoints are available in ia-32 +processors. + +Each hardware breakpoint can be of one of the following three types. + +1. Execution breakpoint - An Execution breakpoint is triggered when code + at the breakpoint address is executed. + + As limited number of hardware breakpoints are available, it is + advisable to use software breakpoints ( break command ) instead + of execution hardware breakpoints, unless modification of code + is to be avoided. + +2. Write breakpoint - A write breakpoint is triggered when memory + location at the breakpoint address is written. + + A write or can be placed for data of variable length. Length of + a write breakpoint indicates length of the datatype to be + watched. Length is 1 for 1 byte data , 2 for 2 byte data, 3 for + 4 byte data. + +3. Access breakpoint - An access breakpoint is triggered when memory + location at the breakpoint address is either read or written. + + Access breakpoints also have lengths similar to write breakpoints. + +IO breakpoints in ia-32 are not supported. + +Since gdb stub at present does not use the protocol used by gdb for hardware +breakpoints, hardware breakpoints are accessed through gdb macros. gdb macros +for hardware breakpoints are described below. + +hwebrk - Places an execution breakpoint + hwebrk breakpointno address +hwwbrk - Places a write breakpoint + hwwbrk breakpointno length address +hwabrk - Places an access breakpoint + hwabrk breakpointno length address +hwrmbrk - Removes a breakpoint + hwrmbrk breakpointno +exinfo - Tells whether a software or hardware breakpoint has occurred. + Prints number of the hardware breakpoint if a hardware breakpoint has + occurred. + +Arguments required by these commands are as follows +breakpointno - 0 to 3 +length - 1 to 3 +address - Memory location in hex digits ( without 0x ) e.g c015e9bc + +SMP support +========== + +When a breakpoint occurs or user issues a break ( Ctrl + C ) to gdb +client, all the processors are forced to enter the debugger. Current +thread corresponds to the thread running on the processor where +breakpoint occurred. Threads running on other processor(s) appear +similar to other non-running threads in the 'info threads' output. +Within the kgdb stub there is a structure "waiting_cpus" in which kgdb +records the values of "current" and "regs" for each CPU other than the +one that hit the breakpoint. "current" is a pointer to the task +structure for the task that CPU is running, while "regs" points to the +saved registers for the task. This structure can be examined with the +gdb "p" command. + +ia-32 hardware debugging registers on all processors are set to same +values. Hence any hardware breakpoints may occur on any processor. + +gdb troubleshooting +=================== + +1. gdb hangs +Kill it. restart gdb. Connect to target machine. + +2. gdb cannot connect to target machine (after killing a gdb and +restarting another) If the target machine was not inside debugger when +you killed gdb, gdb cannot connect because the target machine won't +respond. In this case echo "Ctrl+C"(ASCII 3) to the serial line. +e.g. echo -e "\003" > /dev/ttyS1 +This forces that target machine into the debugger, after which you +can connect. + +3. gdb cannot connect even after echoing Ctrl+C into serial line +Try changing serial line settings min to 1 and time to 0 +e.g. stty min 1 time 0 < /dev/ttyS1 +Try echoing again + +Check serial line speed and set it to correct value if required +e.g. stty ispeed 115200 ospeed 115200 < /dev/ttyS1 + +EVENTS +====== + +Ever want to know the order of things happening? Which CPU did what and +when? How did the spinlock get the way it is? Then events are for +you. Events are defined by calls to an event collection interface and +saved for later examination. In this case, kgdb events are saved by a +very fast bit of code in kgdb which is fully SMP and interrupt protected +and they are examined by using gdb to display them. Kgdb keeps only +the last N events, where N must be a power of two and is defined at +configure time. + + +Events are signaled to kgdb by calling: + +kgdb_ts(data0,data1) + +For each call kgdb records each call in an array along with other info. +Here is the array definition: + +struct kgdb_and_then_struct { +#ifdef CONFIG_SMP + int on_cpu; +#endif + long long at_time; + int from_ln; + char * in_src; + void *from; + int with_if; + int data0; + int data1; +}; + +For SMP machines the CPU is recorded, for all machines the TSC is +recorded (gets a time stamp) as well as the line number and source file +the call was made from. The address of the (from), the "if" (interrupt +flag) and the two data items are also recorded. The macro kgdb_ts casts +the types to int, so you can put any 32-bit values here. There is a +configure option to select the number of events you want to keep. A +nice number might be 128, but you can keep up to 1024 if you want. The +number must be a power of two. An "andthen" macro library is provided +for gdb to help you look at these events. It is also possible to define +a different structure for the event storage and cast the data to this +structure. For example the following structure is defined in kgdb: + +struct kgdb_and_then_struct2 { +#ifdef CONFIG_SMP + int on_cpu; +#endif + long long at_time; + int from_ln; + char * in_src; + void *from; + int with_if; + struct task_struct *t1; + struct task_struct *t2; +}; + +If you use this for display, the data elements will be displayed as +pointers to task_struct entries. You may want to define your own +structure to use in casting. You should only change the last two items +and you must keep the structure size the same. Kgdb will handle these +as 32-bit ints, but within that constraint you can define a structure to +cast to any 32-bit quantity. This need only be available to gdb and is +only used for casting in the display code. + +Final Items +=========== + +I picked up this code from Amit S. Kale and enhanced it. + +If you make some really cool modification to this stuff, or if you +fix a bug, please let me know. + +George Anzinger + + +Amit S. Kale + + +(First kgdb by David Grothe ) + +(modified by Tigran Aivazian ) + Putting gdbstub into the kernel config menu. + +(modified by Scott Foehner ) + Hooks for entering gdbstub at boot time. + +(modified by Amit S. Kale ) + Threads, ia-32 hw debugging, mp support, console support, + nmi watchdog handling. + +(modified by George Anzinger ) + Extended threads to include the idle threads. + Enhancements to allow breakpoint() at first C code. + Use of module_init() and __setup() to automate the configure. + Enhanced the cpu "collection" code to work in early bring-up. + Added ability to call functions from gdb + Print info thread stuff without going back to schedule() + Now collect the "other" cpus with an IPI/ NMI. --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/Documentation/i386/kgdb/loadmodule.sh 2004-03-07 20:47:02.000000000 -0800 @@ -0,0 +1,78 @@ +#/bin/sh +# This script loads a module on a target machine and generates a gdb script. +# source generated gdb script to load the module file at appropriate addresses +# in gdb. +# +# Usage: +# Loading the module on target machine and generating gdb script) +# [foo]$ loadmodule.sh +# +# Loading the module file into gdb +# (gdb) source +# +# Modify following variables according to your setup. +# TESTMACHINE - Name of the target machine +# GDBSCRIPTS - The directory where a gdb script will be generated +# +# Author: Amit S. Kale (akale@veritas.com). +# +# If you run into problems, please check files pointed to by following +# variables. +# ERRFILE - /tmp/.errs contains stderr output of insmod +# MAPFILE - /tmp/.map contains stdout output of insmod +# GDBSCRIPT - $GDBSCRIPTS/load gdb script. + +TESTMACHINE=foo +GDBSCRIPTS=/home/bar + +if [ $# -lt 1 ] ; then { + echo Usage: $0 modulefile + exit +} ; fi + +MODULEFILE=$1 +MODULEFILEBASENAME=`basename $1` + +if [ $MODULEFILE = $MODULEFILEBASENAME ] ; then { + MODULEFILE=`pwd`/$MODULEFILE +} fi + +ERRFILE=/tmp/$MODULEFILEBASENAME.errs +MAPFILE=/tmp/$MODULEFILEBASENAME.map +GDBSCRIPT=$GDBSCRIPTS/load$MODULEFILEBASENAME + +function findaddr() { + local ADDR=0x$(echo "$SEGMENTS" | \ + grep "$1" | sed 's/^[^ ]*[ ]*[^ ]*[ ]*//' | \ + sed 's/[ ]*[^ ]*$//') + echo $ADDR +} + +function checkerrs() { + if [ "`cat $ERRFILE`" != "" ] ; then { + cat $ERRFILE + exit + } fi +} + +#load the module +echo Copying $MODULEFILE to $TESTMACHINE +rcp $MODULEFILE root@${TESTMACHINE}: + +echo Loading module $MODULEFILE +rsh -l root $TESTMACHINE /sbin/insmod -m ./`basename $MODULEFILE` \ + > $MAPFILE 2> $ERRFILE +checkerrs + +SEGMENTS=`head -n 11 $MAPFILE | tail -n 10` +TEXTADDR=$(findaddr "\\.text[^.]") +LOADSTRING="add-symbol-file $MODULEFILE $TEXTADDR" +SEGADDRS=`echo "$SEGMENTS" | awk '//{ + if ($1 != ".text" && $1 != ".this" && + $1 != ".kstrtab" && $1 != ".kmodtab") { + print " -s " $1 " 0x" $3 " " + } +}'` +LOADSTRING="$LOADSTRING $SEGADDRS" +echo Generating script $GDBSCRIPT +echo $LOADSTRING > $GDBSCRIPT --- linux-2.6.4-rc2/Documentation/input/joystick-parport.txt 2003-06-14 12:18:51.000000000 -0700 +++ 25/Documentation/input/joystick-parport.txt 2004-03-07 20:46:53.000000000 -0800 @@ -434,7 +434,7 @@ Here are described their command lines: Using gamecon.c you can connect up to five devices to one parallel port. It uses the following kernel/module command line: - gc=port,pad1,pad2,pad3,pad4,pad5 + gamecon.map=port,pad1,pad2,pad3,pad4,pad5 Where 'port' the number of the parport interface (eg. 0 for parport0). @@ -457,15 +457,15 @@ uses the following kernel/module command your controller plugged in before initializing. Should you want to use more than one of parallel ports at once, you can use -gc_2 and gc_3 as additional command line parameters for two more parallel -ports. +gamecon.map2 and gamecon.map3 as additional command line parameters for two +more parallel ports. 3.2 db9.c ~~~~~~~~~ Apart from making an interface, there is nothing difficult on using the db9.c driver. It uses the following kernel/module command line: - db9=port,type + db9.dev=port,type Where 'port' is the number of the parport interface (eg. 0 for parport0). @@ -489,14 +489,14 @@ Old parallel ports may not have this fea 10 | Amiga CD32 pad Should you want to use more than one of these joysticks/pads at once, you -can use db9_2 and db9_3 as additional command line parameters for two +can use db9.dev2 and db9.dev3 as additional command line parameters for two more joysticks/pads. 3.3 turbografx.c ~~~~~~~~~~~~~~~~ The turbografx.c driver uses a very simple kernel/module command line: - tgfx=port,js1,js2,js3,js4,js5,js6,js7 + turbografx.map=port,js1,js2,js3,js4,js5,js6,js7 Where 'port' is the number of the parport interface (eg. 0 for parport0). @@ -504,8 +504,8 @@ more joysticks/pads. interface ports 1-7 have. For a standard multisystem joystick, this is 1. Should you want to use more than one of these interfaces at once, you can -use tgfx_2 and tgfx_3 as additional command line parameters for two more -interfaces. +use turbografx.map2 and turbografx.map3 as additional command line parameters +for two more interfaces. 3.4 PC parallel port pinout ~~~~~~~~~~~~~~~~~~~~~~~~~~~ --- linux-2.6.4-rc2/Documentation/input/joystick.txt 2003-06-14 12:18:34.000000000 -0700 +++ 25/Documentation/input/joystick.txt 2004-03-07 20:46:53.000000000 -0800 @@ -111,7 +111,7 @@ your needs: alias tty-ldisc-2 serport alias char-major-13 input above input joydev ns558 analog - options analog js=gamepad + options analog map=gamepad,none,2btn 2.5 Verifying that it works ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -185,7 +185,7 @@ you'll need to specify the types either module command line, when inserting analog.o into the kernel. The parameters are: - js=type,type,type,.... + analog.map=,,,.... 'type' is type of the joystick from the table below, defining joysticks present on gameports in the system, starting with gameport0, second 'type' @@ -419,7 +419,7 @@ card. Amiga joysticks, connected to an Amiga, are supported by the amijoy.c driver. Since they can't be autodetected, the driver has a command line. - amijoy=a,b + amijoy.map=, a and b define the joysticks connected to the JOY0DAT and JOY1DAT ports of the Amiga. --- linux-2.6.4-rc2/Documentation/IPMI.txt 2003-06-14 12:17:56.000000000 -0700 +++ 25/Documentation/IPMI.txt 2004-03-07 20:48:02.000000000 -0800 @@ -22,6 +22,58 @@ are not familiar with IPMI itself, see t http://www.intel.com/design/servers/ipmi/index.htm. IPMI is a big subject and I can't cover it all here! +Configuration +------------- + +The LinuxIPMI driver is modular, which means you have to pick several +things to have it work right depending on your hardware. Most of +these are available in the 'Character Devices' menu. + +No matter what, you must pick 'IPMI top-level message handler' to use +IPMI. What you do beyond that depends on your needs and hardware. + +The message handler does not provide any user-level interfaces. +Kernel code (like the watchdog) can still use it. If you need access +from userland, you need to select 'Device interface for IPMI' if you +want access through a device driver. Another interface is also +available, you may select 'IPMI sockets' in the 'Networking Support' +main menu. This provides a socket interface to IPMI. You may select +both of these at the same time, they will both work together. + +The driver interface depends on your hardware. If you have a board +with a standard interface (These will generally be either "KCS", +"SMIC", or "BT", consult your hardware manual), choose the 'IPMI SI +handler' option. A driver also exists for direct I2C access to the +IPMI management controller. Some boards support this, but it is +unknown if it will work on every board. For this, choose 'IPMI SMBus +handler', but be ready to try to do some figuring to see if it will +work. + +There is also a KCS-only driver interface supplied, but it is +depracated in favor of the SI interface. + +You should generally enable ACPI on your system, as systems with IPMI +should have ACPI tables describing them. + +If you have a standard interface and the board manufacturer has done +their job correctly, the IPMI controller should be automatically +detect (via ACPI or SMBIOS tables) and should just work. Sadly, many +boards do not have this information. The driver attempts standard +defaults, but they may not work. If you fall into this situation, you +need to read the section below named 'The SI Driver' on how to +hand-configure your system. + +IPMI defines a standard watchdog timer. You can enable this with the +'IPMI Watchdog Timer' config option. If you compile the driver into +the kernel, then via a kernel command-line option you can have the +watchdog timer start as soon as it intitializes. It also have a lot +of other options, see the 'Watchdog' section below for more details. +Note that you can also have the watchdog continue to run if it is +closed (by default it is disabled on close). Go into the 'Watchdog +Cards' menu, enable 'Watchdog Timer Support', and enable the option +'Disable watchdog shutdown on close'. + + Basic Design ------------ @@ -41,18 +93,30 @@ ipmi_devintf - This provides a userland driver, each open file for this device ties in to the message handler as an IPMI user. -ipmi_kcs_drv - A driver for the KCS SMI. Most system have a KCS -interface for IPMI. +ipmi_si - A driver for various system interfaces. This supports +KCS, SMIC, and may support BT in the future. Unless you have your own +custom interface, you probably need to use this. + +ipmi_smb - A driver for accessing BMCs on the SMBus. It uses the +I2C kernel driver's SMBus interfaces to send and receive IPMI messages +over the SMBus. +af_ipmi - A network socket interface to IPMI. This doesn't take up +a character device in your system. + +Note that the KCS-only interface ahs been removed. Much documentation for the interface is in the include files. The IPMI include files are: -ipmi.h - Contains the user interface and IOCTL interface for IPMI. +net/af_ipmi.h - Contains the socket interface. + +linux/ipmi.h - Contains the user interface and IOCTL interface for IPMI. -ipmi_smi.h - Contains the interface for SMI drivers to use. +linux/ipmi_smi.h - Contains the interface for system management interfaces +(things that interface to IPMI controllers) to use. -ipmi_msgdefs.h - General definitions for base IPMI messaging. +linux/ipmi_msgdefs.h - General definitions for base IPMI messaging. Addressing @@ -260,70 +324,131 @@ they register with the message handler. in the order they register, although if an SMI unregisters and then another one registers, all bets are off. -The ipmi_smi.h defines the interface for SMIs, see that for more -details. +The ipmi_smi.h defines the interface for management interfaces, see +that for more details. -The KCS Driver --------------- +The SI Driver +------------- -The KCS driver allows up to 4 KCS interfaces to be configured in the -system. By default, the driver will register one KCS interface at the -spec-specified I/O port 0xca2 without interrupts. You can change this -at module load time (for a module) with: +The SI driver allows up to 4 KCS or SMIC interfaces to be configured +in the system. By default, scan the ACPI tables for interfaces, and +if it doesn't find any the driver will attempt to register one KCS +interface at the spec-specified I/O port 0xca2 without interrupts. +You can change this at module load time (for a module) with: - insmod ipmi_kcs_drv.o kcs_ports=,... kcs_addrs=, - kcs_irqs=,... kcs_trydefaults=[0|1] + modprobe ipmi_si.o type=,.... + ports=,... addrs=,... + irqs=,... trydefaults=[0|1] -The KCS driver supports two types of interfaces, ports (for I/O port -based KCS interfaces) and memory addresses (for KCS interfaces in -memory). The driver will support both of them simultaneously, setting -the port to zero (or just not specifying it) will allow the memory -address to be used. The port will override the memory address if it -is specified and non-zero. kcs_trydefaults sets whether the standard -IPMI interface at 0xca2 and any interfaces specified by ACPE are -tried. By default, the driver tries it, set this value to zero to -turn this off. +Each of these except si_trydefaults is a list, the first item for the +first interface, second item for the second interface, etc. + +The si_type may be either "kcs", "smic", or "bt". If you leave it blank, it +defaults to "kcs". + +If you specify si_addrs as non-zero for an interface, the driver will +use the memory address given as the address of the device. This +overrides si_ports. + +If you specify si_ports as non-zero for an interface, the driver will +use the I/O port given as the device address. + +If you specify si_irqs as non-zero for an interface, the driver will +attempt to use the given interrupt for the device. + +si_trydefaults sets whether the standard IPMI interface at 0xca2 and +any interfaces specified by ACPE are tried. By default, the driver +tries it, set this value to zero to turn this off. When compiled into the kernel, the addresses can be specified on the kernel command line as: - ipmi_kcs=:,:....,[nodefault] - -The values is either "p" or "m" for port or memory -addresses. So for instance, a KCS interface at port 0xca2 using -interrupt 9 and a memory interface at address 0xf9827341 with no -interrupt would be specified "ipmi_kcs=p0xca2:9,m0xf9827341". -If you specify zero for in irq or don't specify it, the driver will -run polled unless the software can detect the interrupt to use in the -ACPI tables. - -By default, the driver will attempt to detect a KCS device at the -spec-specified 0xca2 address and any address specified by ACPI. If -you want to turn this off, use the "nodefault" option. + ipmi_si.type=,... + ipmi_si.ports=,... ipmi_si.addrs=,... + ipmi_si.irqs=,... ipmi_si.trydefaults=[0|1] + +It works the same as the module parameters of the same names. + +By default, the driver will attempt to detect any device specified by +ACPI, and if none of those then a KCS device at the spec-specified +0xca2. If you want to turn this off, set the "trydefaults" option to +false. If you have high-res timers compiled into the kernel, the driver will use them to provide much better performance. Note that if you do not have high-res timers enabled in the kernel and you don't have interrupts enabled, the driver will run VERY slowly. Don't blame me, -the KCS interface sucks. +these interfaces suck. + + +The SMBus Driver +---------------- + +The SMBus driver allows up to 4 SMBus devices to be configured in the +system. By default, the driver will register any SMBus interfaces it finds +in the I2C address range of 0x20 to 0x4f on any adapter. You can change this +at module load time (for a module) with: + + modprobe ipmi_smb.o + addr=,[,,[,...]] + dbg=,... + [defaultprobe=0] [dbg_probe=1] + +The addresses are specified in pairs, the first is the adapter ID and the +second is the I2C address on that adapter. + +The debug flags are bit flags for each BMC found, they are: +IPMI messages: 1, driver state: 2, timing: 4, I2C probe: 8 + +Setting smb_defaultprobe to zero disabled the default probing of SMBus +interfaces at address range 0x20 to 0x4f. This means that only the +BMCs specified on the smb_addr line will be detected. + +Setting smb_dbg_probe to 1 will enable debugging of the probing and +detection process for BMCs on the SMBusses. + +Discovering the IPMI compilant BMC on the SMBus can cause devices +on the I2C bus to fail. The SMBus driver writes a "Get Device ID" IPMI +message as a block write to the I2C bus and waits for a response. +This action can be detrimental to some I2C devices. It is highly recommended +that the known I2c address be given to the SMBus driver in the smb_addr +parameter. The default adrress range will not be used when a smb_addr +parameter is provided. + +When compiled into the kernel, the addresses can be specified on the +kernel command line as: + + ipmb_smb.addr=,[,,[,...]] + ipmi_smb.dbg=,... + ipmi_smb.defaultprobe=0 ipmi_smb.dbg_probe=1 + +These are the same options as on the module command line. + +Note that you might need some I2C changes if CONFIG_IPMI_PANIC_EVENT +is enabled along with this, so the I2C driver knows to run to +completion during sending a panic event. Other Pieces ------------ Watchdog +-------- A watchdog timer is provided that implements the Linux-standard watchdog timer interface. It has three module parameters that can be used to control it: - insmod ipmi_watchdog timeout= pretimeout= action= - preaction= preop= + modprobe ipmi_watchdog timeout= pretimeout= action= + preaction= preop= start_now=x The timeout is the number of seconds to the action, and the pretimeout is the amount of seconds before the reset that the pre-timeout panic will -occur (if pretimeout is zero, then pretimeout will not be enabled). +occur (if pretimeout is zero, then pretimeout will not be enabled). Note +that the pretimeout is the time before the final timeout. So if the +timeout is 50 seconds and the pretimeout is 10 seconds, then the pretimeout +will occur in 40 second (10 seconds before the timeout). The action may be "reset", "power_cycle", or "power_off", and specifies what to do when the timer times out, and defaults to @@ -344,16 +469,19 @@ When preop is set to "preop_give_data", on the device when the pretimeout occurs. Select and fasync work on the device, as well. +If start_now is set to 1, the watchdog timer will start running as +soon as the driver is loaded. + When compiled into the kernel, the kernel command line is available for configuring the watchdog: - ipmi_wdog=[,[,, + See also Documentation/kernel/input/joystick.txt + + analog.map= [HW,JOY] Analog joystick and gamepad support + Specifies type or capabilities of an analog joystick + connected to one of 16 gameports + Format: ,,.. apc= [HW,SPARC] Power management functions (SPARCstation-4/5 + deriv.) Format: noidle @@ -174,11 +185,18 @@ running once the system is up. atascsi= [HW,SCSI] Atari SCSI - atkbd.set= [HW] Select keyboard code set - Format: + atkbd.extra= [HW] Enable extra LEDs and keys on IBM RapidAccess, EzKey + and similar keyboards + + atkbd.reset= [HW] Reset keyboard during initialization + + atkbd.set= [HW] Select keyboard code set + Format: (2 = AT (default) 3 = PS/2) + + atkbd.scroll= [HW] Enable scroll wheel on MS Office and similar keyboards + atkbd.softrepeat= [HW] Use software keyboard repeat - atkbd.reset= [HW] Reset keyboard during initialization autotest [IA64] @@ -280,10 +298,11 @@ running once the system is up. dasd= [HW,NET] See header of drivers/s390/block/dasd_devmap.c. - db9= [HW,JOY] - db9_2= - db9_3= - + db9.dev[2|3]= [HW,JOY] Multisystem joystick support via parallel port + (one device per port) + Format: , + See also Documentation/input/joystick-parport.txt + debug [KNL] Enable kernel debugging (events log level). decnet= [HW,NET] @@ -377,12 +396,14 @@ running once the system is up. ftape= [HW] Floppy Tape subsystem debugging options. See Documentation/ftape.txt. + gamecon.map[2|3]= + [HW,JOY] Multisystem joystick and NES/SNES/PSX pad + support via parallel port (up to 5 devices per port) + Format: ,,,,, + See also Documentation/input/joystick-parport.txt + gamma= [HW,DRM] - gc= [HW,JOY] - gc_2= See Documentation/input/joystick-parport.txt. - gc_3= - gdth= [HW,SCSI] See header of drivers/scsi/gdth.c. @@ -609,9 +630,9 @@ running once the system is up. mga= [HW,DRM] - mousedev.xres [MOUSE] Horizontal screen resolution, used for devices + mousedev.xres= [MOUSE] Horizontal screen resolution, used for devices reporting absolute coordinates, such as tablets - mousedev.yres [MOUSE] Vertical screen resolution, used for devices + mousedev.yres= [MOUSE] Vertical screen resolution, used for devices reporting absolute coordinates, such as tablets mpu401= [HW,OSS] @@ -1156,10 +1177,6 @@ running once the system is up. See header of drivers/scsi/t128.c. tdfx= [HW,DRM] - - tgfx= [HW,JOY] TurboGraFX parallel port interface - tgfx_2= See Documentation/input/joystick-parport.txt. - tgfx_3= thash_entries= [KNL,NET] Set number of hash buckets for TCP connection @@ -1182,8 +1199,13 @@ running once the system is up. trix= [HW,OSS] MediaTrix AudioTrix Pro Format: ,,,,,,,, - tsdev.xres [TS] Horizontal screen resolution. - tsdev.yres [TS] Vertical screen resolution. + tsdev.xres= [TS] Horizontal screen resolution. + tsdev.yres= [TS] Vertical screen resolution. + + turbografx.map[2|3]= + [HW,JOY] TurboGraFX parallel port interface + Format: ,,,,,,, + See also Documentation/input/joystick-parport.txt u14-34f= [HW,SCSI] UltraStor 14F/34F SCSI host adapter See header of drivers/scsi/u14-34f.c. --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/Documentation/laptop-mode.txt 2004-03-07 20:47:20.000000000 -0800 @@ -0,0 +1,480 @@ +How to conserve battery power using laptop-mode +----------------------------------------------- + +Document Author: Bart Samwel (bart@samwel.tk) +Date created: January 2, 2004 + +Introduction +------------ + +Laptopmode is used to minimize the time that the hard disk needs to be spun up, +to conserve battery power on laptops. It has been reported to cause significant +power savings. + +Contents +-------- + +* Introduction +* The short story +* Caveats +* The details +* Tips & Tricks +* Control script +* ACPI integration +* Monitoring tool + + +The short story +--------------- + +If you just want to use it, run the laptop_mode control script (which is included +at the end of this document) as follows: + +# laptop_mode start + +Then set your harddisk spindown time to a relatively low value with hdparm: + +hdparm -S 4 /dev/hda + +The value -S 4 means 20 seconds idle time before spindown. Your harddisk will +now only spin up when a disk cache miss occurs, or at least once every 10 +minutes to write back any pending changes. + +To stop laptop_mode, remount your filesystems with regular commit intervals +(e.g., 5 seconds), and run "laptop_mode stop". + + +Caveats +------- + +* The downside of laptop mode is that you have a chance of losing up + to 10 minutes of work. If you cannot afford this, don't use it! + +* Most desktop hard drives have a very limited lifetime measured in spindown + cycles, typically about 50.000 times (it's usually listed on the spec sheet). + Check your drive's rating, and don't wear down your drive's lifetime if you + don't need to. + +* If you mount some of your ext3/reiserfs filesystems with the -n option, then + the control script will not be able to remount them correctly. You must set + DO_REMOUNTS=0 in the control script, otherwise it will remount them with the + wrong options -- or it will fail because it cannot write to /etc/mtab. + +* If you have your filesystems listed as type "auto" in fstab, like I did, then + the control script will not recognize them as filesystems that need remounting. + +The details +----------- + +Laptop-mode is controlled by the flag /proc/sys/vm/laptop_mode. When this +flag is set, any physical disk read operation (that might have caused the +hard disk to spin up) causes Linux to flush all dirty blocks. The result +of this is that after a disk has spun down, it will not be spun up anymore +to write dirty blocks, because those blocks had already been written +immediately after the most recent read operation + +To increase the effectiveness of the laptop_mode strategy, the laptop_mode +control script increases dirty_expire_centisecs and dirty_writeback_centisecs in +/proc/sys/vm to about 10 minutes (by default), which means that pages that are +dirtied are not forced to be written to disk as often. The control script also +changes the dirty background ratio, so that background writeback of dirty pages +is not done anymore. Combined with a higher commit value (also 10 minutes) for +ext3 or ReiserFS filesystems (also done automatically by the control script), +this results in concentration of disk activity in a small time interval which +occurs only once every 10 minutes, or whenever the disk is forced to spin up by +a cache miss. The disk can then be spun down in the periods of inactivity. + +If you want to find out which process caused the disk to spin up, you can +gather information by setting the flag /proc/sys/vm/block_dump. When this flag +is set, Linux reports all disk read and write operations that take place, and +all block dirtyings done to files. This makes it possible to debug why a disk +needs to spin up, and to increase battery life even more. + +If 10 minutes is too much or too little downtime for you, you can configure +this downtime as follows. In the control script, set the MAX_AGE value to the +maximum number of seconds of disk downtime that you would like. You should +then set your filesystem's commit interval to the same value. The dirty ratio +is also configurable from the control script. + +If you don't like the idea of the control script remounting your filesystems +for you, you can change DO_REMOUNTS to 0 in the script. + +Thanks to Kiko Piris, the control script can be used to enable laptop mode on +both the Linux 2.4 and 2.6 series. + + +Tips & Tricks +------------- + +* Bartek Kania reports getting up to 50 minutes of extra battery life (on top + of his regular 3 to 3.5 hours) using very aggressive power management (hdparm + -B1) and a spindown time of 5 seconds (hdparm -S1). + +* You can spin down the disk while playing MP3, by setting the disk readahead + to 8MB (hdparm -a 16384). Effectively, the disk will read a complete MP3 at + once, and will then spin down while the MP3 is playing. (Thanks to Bartek + Kania.) + +* Drew Scott Daniels observed: "I don't know why, but when I decrease the number + of colours that my display uses it consumes less battery power. I've seen + this on powerbooks too. I hope that this is a piece of information that + might be useful to the Laptop Mode patch or it's users." + + +Control script +-------------- + +Please note that this control script works for the Linux 2.4 and 2.6 series. + +--------------------CONTROL SCRIPT BEGIN------------------------------------------ +#!/bin/sh + +# start or stop laptop_mode, best run by a power management daemon when +# ac gets connected/disconnected from a laptop +# +# install as /sbin/laptop_mode +# +# Contributors to this script: Kiko Piris +# Bart Samwel +# Dax Kelson +# Original Linux 2.4 version by: Jens Axboe + +parse_mount_opts () { + echo "$*" | \ + sed 's/commit=[0-9]*//g' | \ + sed 's/,,*/,/g' | \ + sed 's/^,//' | \ + sed 's/,$//' | \ + cat - +} + +KLEVEL="$(uname -r | cut -c1-3)" +case "$KLEVEL" in + "2.4") + true + ;; + "2.6") + true + ;; + *) + echo "Unhandled kernel level: $KLEVEL ('uname -r' = '$(uname -r)')" + exit 1 + ;; +esac + +# Shall we remount journaled fs. with appropiate commit interval? (1=yes) +DO_REMOUNTS=1 + +# age time, in seconds. should be put into a sysconfig file +MAX_AGE=600 + +# Allowed dirty ratio, in pct. should be put into a sysconfig file as well. +DIRTY_RATIO=40 + +# kernel default dirty buffer age +DEF_AGE=30 +DEF_UPDATE=5 +DEF_DIRTY_BACKGROUND_RATIO=10 +DEF_DIRTY_RATIO=40 + + +if [ ! -e /proc/sys/vm/laptop_mode ]; then + echo "Kernel is not patched with laptop_mode patch." + exit 1 +fi + +if [ ! -w /proc/sys/vm/laptop_mode ]; then + echo "You do not have enough privileges to enable laptop_mode." + exit 1 +fi + +case "$1" in + start) + AGE=$((100*$MAX_AGE)) + echo -n "Starting laptop_mode" + case "$KLEVEL" in + "2.4") + echo "1" > /proc/sys/vm/laptop_mode + echo "30 500 0 0 $AGE $AGE 60 20 0" > /proc/sys/vm/bdflush + ;; + "2.6") + echo "1" > /proc/sys/vm/laptop_mode + echo "$AGE" > /proc/sys/vm/dirty_writeback_centisecs + echo "$AGE" > /proc/sys/vm/dirty_expire_centisecs + echo "$DIRTY_RATIO" > /proc/sys/vm/dirty_ratio + echo "$DIRTY_RATIO" > /proc/sys/vm/dirty_background_ratio + ;; + esac + if [ $DO_REMOUNTS -eq 1 ]; then + cat /etc/mtab | while read DEV MP FST OPTS DUMP PASS ; do + PARSEDOPTS="$(parse_mount_opts "$OPTS")" + case "$FST" in + "ext3") mount $DEV -t $FST $MP -o remount,$PARSEDOPTS,commit=$MAX_AGE ;; + "reiserfs") mount $DEV -t $FST $MP -o remount,$PARSEDOPTS,commit=$MAX_AGE ;; + "xfs") mount $DEV -t $FST $MP -o remount,$PARSEDOPTS,commit=$MAX_AGE ;; + esac + done + fi + echo "." + ;; + stop) + U_AGE=$((100*$DEF_UPDATE)) + B_AGE=$((100*$DEF_AGE)) + echo -n "Stopping laptop_mode" + case "$KLEVEL" in + "2.4") + echo "0" > /proc/sys/vm/laptop_mode + echo "30 500 0 0 $U_AGE $B_AGE 60 20 0" > /proc/sys/vm/bdflush + ;; + "2.6") + echo "0" > /proc/sys/vm/laptop_mode + echo "$U_AGE" > /proc/sys/vm/dirty_writeback_centisecs + echo "$B_AGE" > /proc/sys/vm/dirty_expire_centisecs + echo "$DEF_DIRTY_RATIO" > /proc/sys/vm/dirty_ratio + echo "$DEF_DIRTY_BACKGROUND_RATIO" > /proc/sys/vm/dirty_background_ratio + ;; + esac + if [ $DO_REMOUNTS -eq 1 ]; then + cat /etc/mtab | while read DEV MP FST OPTS DUMP PASS ; do + PARSEDOPTS="$(parse_mount_opts "$OPTS")" + case "$FST" in + "ext3") mount $DEV -t $FST $MP -o remount,$PARSEDOPTS ;; + "reiserfs") mount $DEV -t $FST $MP -o remount,$PARSEDOPTS ;; + "xfs") mount $DEV -t $FST $MP -o remount,$PARSEDOPTS ;; + esac + done + fi + echo "." + ;; + *) + echo "$0 {start|stop}" + ;; + +esac + +exit 0 + +--------------------CONTROL SCRIPT END-------------------------------------------- + + +ACPI integration +---------------- + +Dax Kelson submitted this so that the ACPI acpid daemon will +kick off the laptop_mode script and run hdparm. + +---------------------------/etc/acpi/events/ac_adapter BEGIN------------------------------------------- +event=ac_adapter +action=/etc/acpi/actions/battery.sh +---------------------------/etc/acpi/events/ac_adapter END------------------------------------------- + +---------------------------/etc/acpi/actions/battery.sh BEGIN------------------------------------------- +#!/bin/sh + +# cpu throttling +# cat /proc/acpi/processor/CPU0/throttling for more info +ACAD_THR=0 +BATT_THR=2 + +# spindown time for HD (man hdparm for valid values) +# I prefer 2 hours for acad and 20 seconds for batt +ACAD_HD=244 +BATT_HD=4 + +# ac/battery event handler + +status=`awk '/^state: / { print $2 }' /proc/acpi/ac_adapter/AC/state` + +case $status in + "on-line") + echo "Setting HD spindown to 2 hours" + /sbin/laptop-mode stop + /sbin/hdparm -S $ACAD_HD /dev/hda > /dev/null 2>&1 + /sbin/hdparm -B 255 /dev/hda > /dev/null 2>&1 + #echo -n $ACAD_CPU:$ACAD_THR > /proc/acpi/processor/CPU0/limit + exit 0 + ;; + "off-line") + echo "Setting HD spindown to 20 seconds" + /sbin/laptop-mode start + /sbin/hdparm -S $BATT_HD /dev/hda > /dev/null 2>&1 + /sbin/hdparm -B 1 /dev/hda > /dev/null 2>&1 + #echo -n $BATT_CPU:$BATT_THR > /proc/acpi/processor/CPU0/limit + exit 0 + ;; +esac +---------------------------/etc/acpi/actions/battery.sh END------------------------------------------- + +Monitoring tool +--------------- + +Bartek Kania submitted this, it can be used to measure how much time your disk +spends spun up/down. + +---------------------------dslm.c BEGIN------------------------------------------- +/* + * Simple Disk SLeep Monitor + * by Bartek Kania + * Licenced under the GPL + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef DEBUG +#define D(x) x +#else +#define D(x) +#endif + +int endit = 0; + +/* Check if the disk is in powersave-mode + * Most of the code is stolen from hdparm. + * 1 = active, 0 = standby/sleep, -1 = unknown */ +int check_powermode(int fd) +{ + unsigned char args[4] = {WIN_CHECKPOWERMODE1,0,0,0}; + int state; + + if (ioctl(fd, HDIO_DRIVE_CMD, &args) + && (args[0] = WIN_CHECKPOWERMODE2) /* try again with 0x98 */ + && ioctl(fd, HDIO_DRIVE_CMD, &args)) { + if (errno != EIO || args[0] != 0 || args[1] != 0) { + state = -1; /* "unknown"; */ + } else + state = 0; /* "sleeping"; */ + } else { + state = (args[2] == 255) ? 1 : 0; + } + D(printf(" drive state is: %s\n", state)); + + return state; +} + +char *state_name(int i) +{ + if (i == -1) return "unknown"; + if (i == 0) return "sleeping"; + if (i == 1) return "active"; + + return "internal error"; +} + +char *myctime(time_t time) +{ + char *ts = ctime(&time); + ts[strlen(ts) - 1] = 0; + + return ts; +} + +void measure(int fd) +{ + time_t start_time; + int last_state; + time_t last_time; + int curr_state; + time_t curr_time = 0; + time_t time_diff; + time_t active_time = 0; + time_t sleep_time = 0; + time_t unknown_time = 0; + time_t total_time = 0; + int changes = 0; + float tmp; + + printf("Starting measurements\n"); + + last_state = check_powermode(fd); + start_time = last_time = time(0); + printf(" System is in state %s\n\n", state_name(last_state)); + + while(!endit) { + sleep(1); + curr_state = check_powermode(fd); + + if (curr_state != last_state || endit) { + changes++; + curr_time = time(0); + time_diff = curr_time - last_time; + + if (last_state == 1) active_time += time_diff; + else if (last_state == 0) sleep_time += time_diff; + else unknown_time += time_diff; + + last_state = curr_state; + last_time = curr_time; + + printf("%s: State-change to %s\n", myctime(curr_time), + state_name(curr_state)); + } + } + changes--; /* Compensate for SIGINT */ + + total_time = time(0) - start_time; + printf("\nTotal running time: %lus\n", curr_time - start_time); + printf(" State changed %d times\n", changes); + + tmp = (float)sleep_time / (float)total_time * 100; + printf(" Time in sleep state: %lus (%.2f%%)\n", sleep_time, tmp); + tmp = (float)active_time / (float)total_time * 100; + printf(" Time in active state: %lus (%.2f%%)\n", active_time, tmp); + tmp = (float)unknown_time / (float)total_time * 100; + printf(" Time in unknown state: %lus (%.2f%%)\n", unknown_time, tmp); +} + +void ender(int s) +{ + endit = 1; +} + +void usage() +{ + puts("usage: dslm [-w , (default is 0,1)"); + +__obsolete_setup("amijoy="); + static int amijoy_used[2] = { 0, 0 }; static struct input_dev amijoy_dev[2]; static char *amijoy_phys[2] = { "amijoy/input0", "amijoy/input1" }; @@ -101,17 +107,6 @@ static void amijoy_close(struct input_de free_irq(IRQ_AMIGA_VERTB, amijoy_interrupt); } -static int __init amijoy_setup(char *str) -{ - int i; - int ints[4]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - for (i = 0; i <= ints[0] && i < 2; i++) amijoy[i] = ints[i+1]; - return 1; -} -__setup("amijoy=", amijoy_setup); - static int __init amijoy_init(void) { int i, j; --- linux-2.6.4-rc2/drivers/input/joystick/analog.c 2003-10-08 15:07:08.000000000 -0700 +++ 25/drivers/input/joystick/analog.c 2004-03-07 20:46:53.000000000 -0800 @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -50,9 +51,12 @@ MODULE_LICENSE("GPL"); #define ANALOG_PORTS 16 static char *js[ANALOG_PORTS]; +static int js_nargs; static int analog_options[ANALOG_PORTS]; -MODULE_PARM(js, "1-" __MODULE_STRING(ANALOG_PORTS) "s"); -MODULE_PARM_DESC(js, "Analog joystick options"); +module_param_array_named(map, js, charp, js_nargs, 0); +MODULE_PARM_DESC(map, "Describes analog joysticks type/capabilities"); + +__obsolete_setup("js="); /* * Times, feature definitions. @@ -711,7 +715,7 @@ static void analog_parse_options(void) int i, j; char *end; - for (i = 0; i < ANALOG_PORTS && js[i]; i++) { + for (i = 0; i < js_nargs; i++) { for (j = 0; analog_types[j].name; j++) if (!strcmp(analog_types[j].name, js[i])) { @@ -742,24 +746,6 @@ static struct gameport_dev analog_dev = .disconnect = analog_disconnect, }; -#ifndef MODULE -static int __init analog_setup(char *str) -{ - char *s = str; - int i = 0; - - if (!str || !*str) return 0; - - while ((str = s) && (i < ANALOG_PORTS)) { - if ((s = strchr(str,','))) *s++ = 0; - js[i++] = str; - } - - return 1; -} -__setup("js=", analog_setup); -#endif - int __init analog_init(void) { analog_parse_options(); --- linux-2.6.4-rc2/drivers/input/joystick/db9.c 2004-02-17 20:48:43.000000000 -0800 +++ 25/drivers/input/joystick/db9.c 2004-03-07 20:46:53.000000000 -0800 @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -42,9 +43,24 @@ MODULE_AUTHOR("Vojtech Pavlik ,)"); + +static int db9_2[] __initdata = { -1, 0 }; +static int db9_nargs_2 __initdata = 0; +module_param_array_named(dev2, db9_2, int, db9_nargs_2, 0); +MODULE_PARM_DESC(dev2, "Describes second attached device (,)"); + +static int db9_3[] __initdata = { -1, 0 }; +static int db9_nargs_3 __initdata = 0; +module_param_array_named(dev3, db9_3, int, db9_nargs_3, 0); +MODULE_PARM_DESC(dev3, "Describes third attached device (,)"); + +__obsolete_setup("db9="); +__obsolete_setup("db9_2="); +__obsolete_setup("db9_3="); #define DB9_MULTI_STICK 0x01 #define DB9_MULTI2_STICK 0x02 @@ -76,10 +92,6 @@ MODULE_PARM(db9_3, "2i"); #define DB9_GENESIS6_DELAY 14 #define DB9_REFRESH_TIME HZ/100 -static int db9[] __initdata = { -1, 0 }; -static int db9_2[] __initdata = { -1, 0 }; -static int db9_3[] __initdata = { -1, 0 }; - struct db9 { struct input_dev dev[DB9_MAX_DEVICES]; struct timer_list timer; @@ -518,7 +530,7 @@ static void db9_close(struct input_dev * } } -static struct db9 __init *db9_probe(int *config) +static struct db9 __init *db9_probe(int *config, int nargs) { struct db9 *db9; struct parport *pp; @@ -526,6 +538,12 @@ static struct db9 __init *db9_probe(int if (config[0] < 0) return NULL; + + if (nargs < 2) { + printk(KERN_ERR "db9.c: Device type must be specified.\n"); + return NULL; + } + if (config[1] < 1 || config[1] >= DB9_MAX_PAD || !db9_buttons[config[1]]) { printk(KERN_ERR "db9.c: bad config\n"); return NULL; @@ -601,38 +619,11 @@ static struct db9 __init *db9_probe(int return db9; } -#ifndef MODULE -static int __init db9_setup(char *str) -{ - int i, ints[3]; - get_options(str, ARRAY_SIZE(ints), ints); - for (i = 0; i <= ints[0] && i < 2; i++) db9[i] = ints[i + 1]; - return 1; -} -static int __init db9_setup_2(char *str) -{ - int i, ints[3]; - get_options(str, ARRAY_SIZE(ints), ints); - for (i = 0; i <= ints[0] && i < 2; i++) db9_2[i] = ints[i + 1]; - return 1; -} -static int __init db9_setup_3(char *str) -{ - int i, ints[3]; - get_options(str, ARRAY_SIZE(ints), ints); - for (i = 0; i <= ints[0] && i < 2; i++) db9_3[i] = ints[i + 1]; - return 1; -} -__setup("db9=", db9_setup); -__setup("db9_2=", db9_setup_2); -__setup("db9_3=", db9_setup_3); -#endif - int __init db9_init(void) { - db9_base[0] = db9_probe(db9); - db9_base[1] = db9_probe(db9_2); - db9_base[2] = db9_probe(db9_3); + db9_base[0] = db9_probe(db9, db9_nargs); + db9_base[1] = db9_probe(db9_2, db9_nargs_2); + db9_base[2] = db9_probe(db9_3, db9_nargs_3); if (db9_base[0] || db9_base[1] || db9_base[2]) return 0; --- linux-2.6.4-rc2/drivers/input/joystick/gamecon.c 2004-02-17 20:48:43.000000000 -0800 +++ 25/drivers/input/joystick/gamecon.c 2004-03-07 20:46:53.000000000 -0800 @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -43,10 +44,26 @@ MODULE_AUTHOR("Vojtech Pavlik ,,,..)"); + +static int gc_2[] __initdata = { -1, 0, 0, 0, 0, 0 }; +static int gc_nargs_2 __initdata = 0; +module_param_array_named(map2, gc_2, int, gc_nargs_2, 0); +MODULE_PARM_DESC(map2, "Describers second set of devices"); + +static int gc_3[] __initdata = { -1, 0, 0, 0, 0, 0 }; +static int gc_nargs_3 __initdata = 0; +module_param_array_named(map3, gc_3, int, gc_nargs_3, 0); +MODULE_PARM_DESC(map3, "Describers third set of devices"); + +__obsolete_setup("gc="); +__obsolete_setup("gc_2="); +__obsolete_setup("gc_3="); + +/* see also gs_psx_delay parameter in PSX support section */ #define GC_SNES 1 #define GC_NES 2 @@ -71,10 +88,6 @@ struct gc { static struct gc *gc_base[3]; -static int gc[] __initdata = { -1, 0, 0, 0, 0, 0 }; -static int gc_2[] __initdata = { -1, 0, 0, 0, 0, 0 }; -static int gc_3[] __initdata = { -1, 0, 0, 0, 0, 0 }; - static int gc_status_bit[] = { 0x40, 0x80, 0x20, 0x10, 0x08 }; static char *gc_names[] = { NULL, "SNES pad", "NES pad", "NES FourPort", "Multisystem joystick", @@ -232,6 +245,11 @@ static void gc_multi_read_packet(struct #define GC_PSX_LEN(x) ((x) & 0xf) /* Low nibble is length in words */ static int gc_psx_delay = GC_PSX_DELAY; +module_param_named(psx_delay, gc_psx_delay, uint, 0); +MODULE_PARM_DESC(psx_delay, "Delay when accessing Sony PSX controller (usecs)"); + +__obsolete_setup("gc_psx_delay="); + static short gc_psx_abs[] = { ABS_X, ABS_Y, ABS_RX, ABS_RY, ABS_HAT0X, ABS_HAT0Y }; static short gc_psx_btn[] = { BTN_TL, BTN_TR, BTN_TL2, BTN_TR2, BTN_A, BTN_B, BTN_X, BTN_Y, BTN_START, BTN_SELECT, BTN_THUMBL, BTN_THUMBR }; @@ -468,7 +486,7 @@ static void gc_close(struct input_dev *d } } -static struct gc __init *gc_probe(int *config) +static struct gc __init *gc_probe(int *config, int nargs) { struct gc *gc; struct parport *pp; @@ -478,6 +496,11 @@ static struct gc __init *gc_probe(int *c if (config[0] < 0) return NULL; + if (nargs < 2) { + printk(KERN_ERR "gamecon.c: at least one device must be specified\n"); + return NULL; + } + pp = parport_find_number(config[0]); if (!pp) { @@ -507,7 +530,7 @@ static struct gc __init *gc_probe(int *c gc->timer.data = (long) gc; gc->timer.function = gc_timer; - for (i = 0; i < 5; i++) { + for (i = 0; i < nargs - 1; i++) { if (!config[i + 1]) continue; @@ -632,44 +655,11 @@ static struct gc __init *gc_probe(int *c return gc; } -#ifndef MODULE -static int __init gc_setup(char *str) -{ - int i, ints[7]; - get_options(str, ARRAY_SIZE(ints), ints); - for (i = 0; i <= ints[0] && i < 6; i++) gc[i] = ints[i + 1]; - return 1; -} -static int __init gc_setup_2(char *str) -{ - int i, ints[7]; - get_options(str, ARRAY_SIZE(ints), ints); - for (i = 0; i <= ints[0] && i < 6; i++) gc_2[i] = ints[i + 1]; - return 1; -} -static int __init gc_setup_3(char *str) -{ - int i, ints[7]; - get_options(str, ARRAY_SIZE(ints), ints); - for (i = 0; i <= ints[0] && i < 6; i++) gc_3[i] = ints[i + 1]; - return 1; -} -static int __init gc_psx_setup(char *str) -{ - get_option(&str, &gc_psx_delay); - return 1; -} -__setup("gc=", gc_setup); -__setup("gc_2=", gc_setup_2); -__setup("gc_3=", gc_setup_3); -__setup("gc_psx_delay=", gc_psx_setup); -#endif - int __init gc_init(void) { - gc_base[0] = gc_probe(gc); - gc_base[1] = gc_probe(gc_2); - gc_base[2] = gc_probe(gc_3); + gc_base[0] = gc_probe(gc, gc_nargs); + gc_base[1] = gc_probe(gc_2, gc_nargs_2); + gc_base[2] = gc_probe(gc_3, gc_nargs_3); if (gc_base[0] || gc_base[1] || gc_base[2]) return 0; --- linux-2.6.4-rc2/drivers/input/joystick/iforce/iforce-usb.c 2003-09-27 18:57:44.000000000 -0700 +++ 25/drivers/input/joystick/iforce/iforce-usb.c 2004-03-07 20:46:56.000000000 -0800 @@ -135,7 +135,7 @@ static int iforce_usb_probe(struct usb_i struct usb_endpoint_descriptor *epirq, *epout; struct iforce *iforce; - interface = &intf->altsetting[intf->act_altsetting]; + interface = intf->cur_altsetting; epirq = &interface->endpoint[0].desc; epout = &interface->endpoint[1].desc; --- linux-2.6.4-rc2/drivers/input/joystick/turbografx.c 2004-02-17 20:48:43.000000000 -0800 +++ 25/drivers/input/joystick/turbografx.c 2004-03-07 20:46:53.000000000 -0800 @@ -35,15 +35,31 @@ #include #include #include +#include #include MODULE_AUTHOR("Vojtech Pavlik "); MODULE_DESCRIPTION("TurboGraFX parallel port interface driver"); MODULE_LICENSE("GPL"); -MODULE_PARM(tgfx, "2-8i"); -MODULE_PARM(tgfx_2, "2-8i"); -MODULE_PARM(tgfx_3, "2-8i"); +static int tgfx[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 }; +static int tgfx_nargs __initdata = 0; +module_param_array_named(map, tgfx, int, tgfx_nargs, 0); +MODULE_PARM_DESC(map, "Describes first set of devices (,,,.."); + +static int tgfx_2[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 }; +static int tgfx_nargs_2 __initdata = 0; +module_param_array_named(map2, tgfx_2, int, tgfx_nargs_2, 0); +MODULE_PARM_DESC(map2, "Describes second set of devices"); + +static int tgfx_3[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 }; +static int tgfx_nargs_3 __initdata = 0; +module_param_array_named(map3, tgfx_3, int, tgfx_nargs_3, 0); +MODULE_PARM_DESC(map3, "Describes third set of devices"); + +__obsolete_setup("tgfx="); +__obsolete_setup("tgfx_2="); +__obsolete_setup("tgfx_3="); #define TGFX_REFRESH_TIME HZ/100 /* 10 ms */ @@ -58,10 +74,6 @@ MODULE_PARM(tgfx_3, "2-8i"); #define TGFX_TOP 0x01 #define TGFX_TOP2 0x08 -static int tgfx[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 }; -static int tgfx_2[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 }; -static int tgfx_3[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 }; - static int tgfx_buttons[] = { BTN_TRIGGER, BTN_THUMB, BTN_THUMB2, BTN_TOP, BTN_TOP2 }; static char *tgfx_name = "TurboGraFX Multisystem joystick"; @@ -133,7 +145,7 @@ static void tgfx_close(struct input_dev * tgfx_probe() probes for tg gamepads. */ -static struct tgfx __init *tgfx_probe(int *config) +static struct tgfx __init *tgfx_probe(int *config, int nargs) { struct tgfx *tgfx; struct parport *pp; @@ -142,6 +154,11 @@ static struct tgfx __init *tgfx_probe(in if (config[0] < 0) return NULL; + if (nargs < 2) { + printk(KERN_ERR "turbografx.c: at least one joystick must be specified\n"); + return NULL; + } + pp = parport_find_number(config[0]); if (!pp) { @@ -171,7 +188,7 @@ static struct tgfx __init *tgfx_probe(in tgfx->sticks = 0; - for (i = 0; i < 7; i++) + for (i = 0; i < nargs - 1; i++) if (config[i+1] > 0 && config[i+1] < 6) { tgfx->sticks |= (1 << i); @@ -212,38 +229,11 @@ static struct tgfx __init *tgfx_probe(in return tgfx; } -#ifndef MODULE -static int __init tgfx_setup(char *str) -{ - int i, ints[9]; - get_options(str, ARRAY_SIZE(ints), ints); - for (i = 0; i <= ints[0] && i < 8; i++) tgfx[i] = ints[i + 1]; - return 1; -} -static int __init tgfx_setup_2(char *str) -{ - int i, ints[9]; - get_options(str, ARRAY_SIZE(ints), ints); - for (i = 0; i <= ints[0] && i < 8; i++) tgfx_2[i] = ints[i + 1]; - return 1; -} -static int __init tgfx_setup_3(char *str) -{ - int i, ints[9]; - get_options(str, ARRAY_SIZE(ints), ints); - for (i = 0; i <= ints[0] && i < 8; i++) tgfx_3[i] = ints[i + 1]; - return 1; -} -__setup("tgfx=", tgfx_setup); -__setup("tgfx_2=", tgfx_setup_2); -__setup("tgfx_3=", tgfx_setup_3); -#endif - int __init tgfx_init(void) { - tgfx_base[0] = tgfx_probe(tgfx); - tgfx_base[1] = tgfx_probe(tgfx_2); - tgfx_base[2] = tgfx_probe(tgfx_3); + tgfx_base[0] = tgfx_probe(tgfx, tgfx_nargs); + tgfx_base[1] = tgfx_probe(tgfx_2, tgfx_nargs_2); + tgfx_base[2] = tgfx_probe(tgfx_3, tgfx_nargs_3); if (tgfx_base[0] || tgfx_base[1] || tgfx_base[2]) return 0; --- linux-2.6.4-rc2/drivers/input/keyboard/atkbd.c 2004-02-03 20:42:35.000000000 -0800 +++ 25/drivers/input/keyboard/atkbd.c 2004-03-07 20:46:53.000000000 -0800 @@ -30,21 +30,17 @@ MODULE_AUTHOR("Vojtech Pavlik "); MODULE_DESCRIPTION("AT and PS/2 keyboard driver"); -MODULE_PARM(atkbd_set, "1i"); -MODULE_PARM(atkbd_reset, "1i"); -MODULE_PARM(atkbd_softrepeat, "1i"); MODULE_LICENSE("GPL"); static int atkbd_set = 2; module_param_named(set, atkbd_set, int, 0); -MODULE_PARM_DESC(set, "Select keyboard code set (2 = default, 3, 4)"); +MODULE_PARM_DESC(set, "Select keyboard code set (2 = default, 3 = PS/2 native)"); + #if defined(__i386__) || defined(__x86_64__) || defined(__hppa__) static int atkbd_reset; #else static int atkbd_reset = 1; #endif -static int atkbd_softrepeat; - module_param_named(reset, atkbd_reset, bool, 0); MODULE_PARM_DESC(reset, "Reset keyboard during initialization"); @@ -52,6 +48,18 @@ static int atkbd_softrepeat; module_param_named(softrepeat, atkbd_softrepeat, bool, 0); MODULE_PARM_DESC(softrepeat, "Use software keyboard repeat"); +static int atkbd_scroll; +module_param_named(scroll, atkbd_scroll, bool, 0); +MODULE_PARM_DESC(scroll, "Enable scroll-wheel on MS Office and similar keyboards"); + +static int atkbd_extra; +module_param_named(extra, atkbd_extra, bool, 0); +MODULE_PARM_DESC(extra, "Enable extra LEDs and keys on IBM RapidAcces, EzKey and similar keyboards"); + +__obsolete_setup("atkbd_set="); +__obsolete_setup("atkbd_reset"); +__obsolete_setup("atkbd_softrepeat="); + /* * Scancode to keycode tables. These are just the default setting, and * are loadable via an userland utility. @@ -127,11 +135,11 @@ static unsigned char atkbd_unxlate_table #define ATKBD_CMD_EX_SETLEDS 0x20eb #define ATKBD_CMD_OK_GETID 0x02e8 + #define ATKBD_RET_ACK 0xfa #define ATKBD_RET_NAK 0xfe #define ATKBD_RET_BAT 0xaa #define ATKBD_RET_EMUL0 0xe0 -#define ATKBD_RET_EMULX 0x80 #define ATKBD_RET_EMUL1 0xe1 #define ATKBD_RET_RELEASE 0xf0 #define ATKBD_RET_HANGUEL 0xf1 @@ -141,6 +149,22 @@ static unsigned char atkbd_unxlate_table #define ATKBD_KEY_UNKNOWN 0 #define ATKBD_KEY_NULL 255 +#define ATKBD_SCR_1 254 +#define ATKBD_SCR_2 253 +#define ATKBD_SCR_4 252 +#define ATKBD_SCR_8 251 +#define ATKBD_SCR_CLICK 250 + +#define ATKBD_SPECIAL 250 + +static unsigned char atkbd_scroll_keys[5][2] = { + { ATKBD_SCR_1, 0x45 }, + { ATKBD_SCR_2, 0x29 }, + { ATKBD_SCR_4, 0x36 }, + { ATKBD_SCR_8, 0x27 }, + { ATKBD_SCR_CLICK, 0x60 }, +}; + /* * The atkbd control structure */ @@ -155,6 +179,7 @@ struct atkbd { unsigned char cmdbuf[4]; unsigned char cmdcnt; unsigned char set; + unsigned char extra; unsigned char release; int lastkey; volatile signed char ack; @@ -189,6 +214,7 @@ static irqreturn_t atkbd_interrupt(struc { struct atkbd *atkbd = serio->private; unsigned int code = data; + int scroll = 0, click = -1; int value; #ifdef ATKBD_DEBUG @@ -202,7 +228,7 @@ static irqreturn_t atkbd_interrupt(struc atkbd->resend = 1; goto out; } - + if (!flags && data == ATKBD_RET_ACK) atkbd->resend = 0; #endif @@ -276,7 +302,7 @@ static irqreturn_t atkbd_interrupt(struc case ATKBD_KEY_UNKNOWN: printk(KERN_WARNING "atkbd.c: Unknown key %s (%s set %d, code %#x on %s).\n", atkbd->release ? "released" : "pressed", - atkbd->translated ? "translated" : "raw", + atkbd->translated ? "translated" : "raw", atkbd->set, code, serio->phys); if (atkbd->translated && atkbd->set == 2 && code == 0x7a) printk(KERN_WARNING "atkbd.c: This is an XFree86 bug. It shouldn't access" @@ -284,6 +310,21 @@ static irqreturn_t atkbd_interrupt(struc else printk(KERN_WARNING "atkbd.c: Use 'setkeycodes %s%02x ' to make it known.\n", code & 0x80 ? "e0" : "", code & 0x7f); break; + case ATKBD_SCR_1: + scroll = 1 - atkbd->release * 2; + break; + case ATKBD_SCR_2: + scroll = 2 - atkbd->release * 4; + break; + case ATKBD_SCR_4: + scroll = 4 - atkbd->release * 8; + break; + case ATKBD_SCR_8: + scroll = 8 - atkbd->release * 16; + break; + case ATKBD_SCR_CLICK: + click = !atkbd->release; + break; default: value = atkbd->release ? 0 : (1 + (!atkbd_softrepeat && test_bit(atkbd->keycode[code], atkbd->dev.key))); @@ -305,6 +346,13 @@ static irqreturn_t atkbd_interrupt(struc atkbd_report_key(&atkbd->dev, regs, atkbd->keycode[code], value); } + if (scroll || click != -1) { + input_regs(&atkbd->dev, regs); + input_report_key(&atkbd->dev, BTN_MIDDLE, click); + input_report_rel(&atkbd->dev, REL_WHEEL, scroll); + input_sync(&atkbd->dev); + } + atkbd->release = 0; out: return IRQ_HANDLED; @@ -353,7 +401,7 @@ static int atkbd_command(struct atkbd *a if (receive && param) for (i = 0; i < receive; i++) atkbd->cmdbuf[(receive - 1) - i] = param[i]; - + if (command & 0xff) if (atkbd_sendbyte(atkbd, command & 0xff)) return (atkbd->cmdcnt = 0) - 1; @@ -373,7 +421,7 @@ static int atkbd_command(struct atkbd *a atkbd->cmdcnt = 0; break; } - + udelay(1); } @@ -420,7 +468,7 @@ static int atkbd_event(struct input_dev | (test_bit(LED_CAPSL, dev->led) ? 4 : 0); atkbd_command(atkbd, param, ATKBD_CMD_SETLEDS); - if (atkbd->set == 4) { + if (atkbd->extra) { param[0] = 0; param[1] = (test_bit(LED_COMPOSE, dev->led) ? 0x01 : 0) | (test_bit(LED_SLEEP, dev->led) ? 0x02 : 0) @@ -466,7 +514,7 @@ static int atkbd_probe(struct atkbd *atk */ if (atkbd_reset) - if (atkbd_command(atkbd, NULL, ATKBD_CMD_RESET_BAT)) + if (atkbd_command(atkbd, NULL, ATKBD_CMD_RESET_BAT)) printk(KERN_WARNING "atkbd.c: keyboard reset failed on %s\n", atkbd->serio->phys); /* @@ -529,21 +577,22 @@ static int atkbd_set_3(struct atkbd *atk return 3; } - if (atkbd_set != 2) - if (!atkbd_command(atkbd, param, ATKBD_CMD_OK_GETID)) { - atkbd->id = param[0] << 8 | param[1]; + if (atkbd_extra) { + param[0] = 0x71; + if (!atkbd_command(atkbd, param, ATKBD_CMD_EX_ENABLE)) { + atkbd->extra = 1; return 2; } - - if (atkbd_set == 4) { - param[0] = 0x71; - if (!atkbd_command(atkbd, param, ATKBD_CMD_EX_ENABLE)) - return 4; } - if (atkbd_set != 3) + if (atkbd_set != 3) return 2; + if (!atkbd_command(atkbd, param, ATKBD_CMD_OK_GETID)) { + atkbd->id = param[0] << 8 | param[1]; + return 2; + } + param[0] = 3; if (atkbd_command(atkbd, param, ATKBD_CMD_SSCANSET)) return 2; @@ -637,7 +686,7 @@ static void atkbd_connect(struct serio * switch (serio->type & SERIO_TYPE) { - case SERIO_8042_XL: + case SERIO_8042_XL: atkbd->translated = 1; case SERIO_8042: if (serio->write) @@ -650,7 +699,7 @@ static void atkbd_connect(struct serio * kfree(atkbd); return; } - + if (atkbd->write) { atkbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP); atkbd->dev.ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL); @@ -687,7 +736,7 @@ static void atkbd_connect(struct serio * kfree(atkbd); return; } - + atkbd->set = atkbd_set_3(atkbd); atkbd_enable(atkbd); @@ -696,24 +745,32 @@ static void atkbd_connect(struct serio * atkbd->id = 0xab00; } - if (atkbd->set == 4) { + if (atkbd->extra) { atkbd->dev.ledbit[0] |= BIT(LED_COMPOSE) | BIT(LED_SUSPEND) | BIT(LED_SLEEP) | BIT(LED_MUTE) | BIT(LED_MISC); - sprintf(atkbd->name, "AT Set 2 Extended keyboard"); + sprintf(atkbd->name, "AT Set 2 Extra keyboard"); } else sprintf(atkbd->name, "AT %s Set %d keyboard", atkbd->translated ? "Translated" : "Raw", atkbd->set); sprintf(atkbd->phys, "%s/input0", serio->phys); + if (atkbd_scroll) { + for (i = 0; i < 5; i++) + atkbd_set2_keycode[atkbd_scroll_keys[i][1]] = atkbd_scroll_keys[i][0]; + atkbd->dev.evbit[0] |= BIT(EV_REL); + atkbd->dev.relbit[0] = BIT(REL_WHEEL); + set_bit(BTN_MIDDLE, atkbd->dev.keybit); + } + if (atkbd->translated) { for (i = 0; i < 128; i++) { atkbd->keycode[i] = atkbd_set2_keycode[atkbd_unxlate_table[i]]; atkbd->keycode[i | 0x80] = atkbd_set2_keycode[atkbd_unxlate_table[i] | 0x80]; } - } else if (atkbd->set == 2) { - memcpy(atkbd->keycode, atkbd_set2_keycode, sizeof(atkbd->keycode)); - } else { + } else if (atkbd->set == 3) { memcpy(atkbd->keycode, atkbd_set3_keycode, sizeof(atkbd->keycode)); + } else { + memcpy(atkbd->keycode, atkbd_set2_keycode, sizeof(atkbd->keycode)); } atkbd->dev.name = atkbd->name; @@ -724,7 +781,7 @@ static void atkbd_connect(struct serio * atkbd->dev.id.version = atkbd->id; for (i = 0; i < 512; i++) - if (atkbd->keycode[i] && atkbd->keycode[i] < 255) + if (atkbd->keycode[i] && atkbd->keycode[i] < ATKBD_SPECIAL) set_bit(atkbd->keycode[i], atkbd->dev.keybit); input_register_device(&atkbd->dev); @@ -741,46 +798,20 @@ static int atkbd_reconnect(struct serio { struct atkbd *atkbd = serio->private; struct serio_dev *dev = serio->dev; - int i; - if (!dev) { - printk(KERN_DEBUG "atkbd: reconnect request, but serio is disconnected, ignoring...\n"); - return -1; - } + if (!dev) { + printk(KERN_DEBUG "atkbd: reconnect request, but serio is disconnected, ignoring...\n"); + return -1; + } if (atkbd->write) { if (atkbd_probe(atkbd)) return -1; - - atkbd->set = atkbd_set_3(atkbd); + if (atkbd->set != atkbd_set_3(atkbd)) + return -1; atkbd_enable(atkbd); - } else { - atkbd->set = 2; - atkbd->id = 0xab00; } - /* - * Here we probably should check if the keyboard has the same set that - * it had before and bail out if it's different. But this will most likely - * cause new keyboard device be created... and for the user it will look - * like keyboard is lost - */ - - if (atkbd->translated) { - for (i = 0; i < 128; i++) { - atkbd->keycode[i] = atkbd_set2_keycode[atkbd_unxlate_table[i]]; - atkbd->keycode[i | 0x80] = atkbd_set2_keycode[atkbd_unxlate_table[i] | 0x80]; - } - } else if (atkbd->set == 2) { - memcpy(atkbd->keycode, atkbd_set2_keycode, sizeof(atkbd->keycode)); - } else { - memcpy(atkbd->keycode, atkbd_set3_keycode, sizeof(atkbd->keycode)); - } - - for (i = 0; i < 512; i++) - if (atkbd->keycode[i] && atkbd->keycode[i] < 255) - set_bit(atkbd->keycode[i], atkbd->dev.keybit); - return 0; } --- linux-2.6.4-rc2/drivers/input/keyboard/hpps2atkbd.h 2004-02-03 20:42:35.000000000 -0800 +++ 25/drivers/input/keyboard/hpps2atkbd.h 2004-03-07 20:46:53.000000000 -0800 @@ -4,14 +4,9 @@ * Copyright (c) 2004 Helge Deller * Copyright (c) 2002 Laurent Canet * Copyright (c) 2002 Thibaut Varene + * Copyright (c) 2000 Xavier Debacker * - * based on linux-2.4's hp_mouse.c & hp_keyb.c - * Copyright (c) 1999 Alex deVries - * Copyright (c) 1999-2000 Philipp Rumpf - * Copyright (c) 2000 Xavier Debacker - * Copyright (c) 2000-2001 Thomas Marteau - * - * HP PS/2 AT-compatible Keyboard, found in PA/RISC Workstations + * HP PS/2 AT-compatible Keyboard, found in PA/RISC Workstations & Laptops * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive @@ -19,87 +14,100 @@ */ -#define KBD_UNKNOWN 0 - -/* Raw SET 2 scancode table */ +/* undefine if you have a RDI PRECISIONBOOK */ +#define STANDARD_KEYBOARD -#if 0 - /* conflicting keys between a RDI Precisionbook keyboard and a normal HP keyboard */ - keytable[0x07] = KEY_F1; /* KEY_F12 */ - keytable[0x11] = KEY_LEFTCTRL; /* KEY_LEFTALT */ - keytable[0x14] = KEY_CAPSLOCK; /* KEY_LEFTCTRL */ - keytable[0x61] = KEY_LEFT; /* KEY_102ND */ +#if defined(STANDARD_KEYBOARD) +# define CONFLICT(x,y) x +#else +# define CONFLICT(x,y) y #endif +/* sadly RDI (Tadpole) decided to ship a different keyboard layout + than HP for their PS/2 laptop keyboard which leads to conflicting + keycodes between a normal HP PS/2 keyboard and a RDI Precisionbook. + HP: RDI: */ +#define C_07 CONFLICT( KEY_F12, KEY_F1 ) +#define C_11 CONFLICT( KEY_LEFTALT, KEY_LEFTCTRL ) +#define C_14 CONFLICT( KEY_LEFTCTRL, KEY_CAPSLOCK ) +#define C_58 CONFLICT( KEY_CAPSLOCK, KEY_RIGHTCTRL ) +#define C_61 CONFLICT( KEY_102ND, KEY_LEFT ) -static unsigned char atkbd_set2_keycode[512] = { +/* Raw SET 2 scancode table */ - /* 00 */ KBD_UNKNOWN, KEY_F9, KBD_UNKNOWN, KEY_F5, KEY_F3, KEY_F1, KEY_F2, KEY_F1, - /* 08 */ KEY_ESC, KEY_F10, KEY_F8, KEY_F6, KEY_F4, KEY_TAB, KEY_GRAVE, KEY_F2, - /* 10 */ KBD_UNKNOWN, KEY_LEFTCTRL, KEY_LEFTSHIFT, KBD_UNKNOWN, KEY_CAPSLOCK, KEY_Q, KEY_1, KEY_F3, - /* 18 */ KBD_UNKNOWN, KEY_LEFTALT, KEY_Z, KEY_S, KEY_A, KEY_W, KEY_2, KEY_F4, - /* 20 */ KBD_UNKNOWN, KEY_C, KEY_X, KEY_D, KEY_E, KEY_4, KEY_3, KEY_F5, - /* 28 */ KBD_UNKNOWN, KEY_SPACE, KEY_V, KEY_F, KEY_T, KEY_R, KEY_5, KEY_F6, - /* 30 */ KBD_UNKNOWN, KEY_N, KEY_B, KEY_H, KEY_G, KEY_Y, KEY_6, KEY_F7, - /* 38 */ KBD_UNKNOWN, KEY_RIGHTALT, KEY_M, KEY_J, KEY_U, KEY_7, KEY_8, KEY_F8, - /* 40 */ KBD_UNKNOWN, KEY_COMMA, KEY_K, KEY_I, KEY_O, KEY_0, KEY_9, KEY_F9, - /* 48 */ KBD_UNKNOWN, KEY_DOT, KEY_SLASH, KEY_L, KEY_SEMICOLON, KEY_P, KEY_MINUS, KEY_F10, - /* 50 */ KBD_UNKNOWN, KBD_UNKNOWN, KEY_APOSTROPHE,KBD_UNKNOWN, KEY_LEFTBRACE, KEY_EQUAL, KEY_F11, KEY_SYSRQ, - /* 58 */ KEY_CAPSLOCK, KEY_RIGHTSHIFT,KEY_ENTER, KEY_RIGHTBRACE,KEY_BACKSLASH, KEY_BACKSLASH,KEY_F12, KEY_SCROLLLOCK, - /* 60 */ KEY_DOWN, KEY_LEFT, KEY_PAUSE, KEY_UP, KEY_DELETE, KEY_END, KEY_BACKSPACE, KEY_INSERT, - /* 68 */ KBD_UNKNOWN, KEY_KP1, KEY_RIGHT, KEY_KP4, KEY_KP7, KEY_PAGEDOWN, KEY_HOME, KEY_PAGEUP, - /* 70 */ KEY_KP0, KEY_KPDOT, KEY_KP2, KEY_KP5, KEY_KP6, KEY_KP8, KEY_ESC, KEY_NUMLOCK, - /* 78 */ KEY_F11, KEY_KPPLUS, KEY_KP3, KEY_KPMINUS, KEY_KPASTERISK,KEY_KP9, KEY_SCROLLLOCK,KEY_103RD, - /* 80 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 88 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 90 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 98 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* a0 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* a8 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* b0 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* b8 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* c0 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* c8 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* d0 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* d8 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* e0 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* e8 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* f0 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* f8 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - - /* These are offset for escaped keycodes: */ - - /* 00 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KEY_F7, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 08 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KEY_LEFTMETA, KEY_RIGHTMETA, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 10 */ KBD_UNKNOWN, KEY_RIGHTALT, KBD_UNKNOWN, KBD_UNKNOWN, KEY_RIGHTCTRL, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 18 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 20 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 28 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 30 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 38 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 40 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 48 */ KBD_UNKNOWN, KBD_UNKNOWN, KEY_KPSLASH, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 50 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 58 */ KBD_UNKNOWN, KBD_UNKNOWN, KEY_KPENTER, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 60 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 68 */ KBD_UNKNOWN, KEY_END, KBD_UNKNOWN, KEY_LEFT, KEY_HOME, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 70 */ KEY_INSERT, KEY_DELETE, KEY_DOWN, KBD_UNKNOWN, KEY_RIGHT, KEY_UP, KBD_UNKNOWN, KBD_UNKNOWN, - /* 78 */ KBD_UNKNOWN, KBD_UNKNOWN, KEY_PAGEDOWN, KBD_UNKNOWN, KEY_SYSRQ, KEY_PAGEUP, KBD_UNKNOWN, KBD_UNKNOWN, - /* 80 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 88 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 90 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 98 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* a0 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* a8 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* b0 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* b8 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* c0 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* c8 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* d0 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* d8 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* e0 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* e8 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* f0 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* f8 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN +/* 00 */ KEY_RESERVED, KEY_F9, KEY_RESERVED, KEY_F5, KEY_F3, KEY_F1, KEY_F2, C_07, +/* 08 */ KEY_ESC, KEY_F10, KEY_F8, KEY_F6, KEY_F4, KEY_TAB, KEY_GRAVE, KEY_F2, +/* 10 */ KEY_RESERVED, C_11, KEY_LEFTSHIFT, KEY_RESERVED, C_14, KEY_Q, KEY_1, KEY_F3, +/* 18 */ KEY_RESERVED, KEY_LEFTALT, KEY_Z, KEY_S, KEY_A, KEY_W, KEY_2, KEY_F4, +/* 20 */ KEY_RESERVED, KEY_C, KEY_X, KEY_D, KEY_E, KEY_4, KEY_3, KEY_F5, +/* 28 */ KEY_RESERVED, KEY_SPACE, KEY_V, KEY_F, KEY_T, KEY_R, KEY_5, KEY_F6, +/* 30 */ KEY_RESERVED, KEY_N, KEY_B, KEY_H, KEY_G, KEY_Y, KEY_6, KEY_F7, +/* 38 */ KEY_RESERVED, KEY_RIGHTALT, KEY_M, KEY_J, KEY_U, KEY_7, KEY_8, KEY_F8, +/* 40 */ KEY_RESERVED, KEY_COMMA, KEY_K, KEY_I, KEY_O, KEY_0, KEY_9, KEY_F9, +/* 48 */ KEY_RESERVED, KEY_DOT, KEY_SLASH, KEY_L, KEY_SEMICOLON, KEY_P, KEY_MINUS, KEY_F10, +/* 50 */ KEY_RESERVED, KEY_RESERVED, KEY_APOSTROPHE,KEY_RESERVED, KEY_LEFTBRACE, KEY_EQUAL, KEY_F11, KEY_SYSRQ, +/* 58 */ C_58, KEY_RIGHTSHIFT,KEY_ENTER, KEY_RIGHTBRACE,KEY_BACKSLASH, KEY_BACKSLASH,KEY_F12, KEY_SCROLLLOCK, +/* 60 */ KEY_DOWN, C_61, KEY_PAUSE, KEY_UP, KEY_DELETE, KEY_END, KEY_BACKSPACE, KEY_INSERT, +/* 68 */ KEY_RESERVED, KEY_KP1, KEY_RIGHT, KEY_KP4, KEY_KP7, KEY_PAGEDOWN, KEY_HOME, KEY_PAGEUP, +/* 70 */ KEY_KP0, KEY_KPDOT, KEY_KP2, KEY_KP5, KEY_KP6, KEY_KP8, KEY_ESC, KEY_NUMLOCK, +/* 78 */ KEY_F11, KEY_KPPLUS, KEY_KP3, KEY_KPMINUS, KEY_KPASTERISK,KEY_KP9, KEY_SCROLLLOCK,KEY_103RD, +/* 80 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 88 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 90 */ KEY_RESERVED, KEY_RIGHTALT, KEY_SYSRQ, KEY_RESERVED, KEY_RIGHTCTRL, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 98 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_CAPSLOCK, KEY_RESERVED, KEY_LEFTMETA, +/* a0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RIGHTMETA, +/* a8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_COMPOSE, +/* b0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* b8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* c0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* c8 */ KEY_RESERVED, KEY_RESERVED, KEY_KPSLASH, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* d0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* d8 */ KEY_RESERVED, KEY_RESERVED, KEY_KPENTER, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* e0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* e8 */ KEY_RESERVED, KEY_END, KEY_RESERVED, KEY_LEFT, KEY_HOME, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* f0 */ KEY_INSERT, KEY_DELETE, KEY_DOWN, KEY_RESERVED, KEY_RIGHT, KEY_UP, KEY_RESERVED, KEY_PAUSE, +/* f8 */ KEY_RESERVED, KEY_RESERVED, KEY_PAGEDOWN, KEY_RESERVED, KEY_SYSRQ, KEY_PAGEUP, KEY_RESERVED, KEY_RESERVED, + +/* These are offset for escaped keycodes: */ + +/* 00 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_F7, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 08 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_LEFTMETA, KEY_RIGHTMETA, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 10 */ KEY_RESERVED, KEY_RIGHTALT, KEY_RESERVED, KEY_RESERVED, KEY_RIGHTCTRL, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 18 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 20 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 28 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 30 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 38 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 40 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 48 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 50 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 58 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 60 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 68 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 70 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 78 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 80 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 88 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 90 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* 98 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* a0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* a8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* b0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* b8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* c0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* c8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* d0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* d8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* e0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* e8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* f0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, +/* f8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED + +#undef STANDARD_KEYBOARD +#undef CONFLICT +#undef C_07 +#undef C_11 +#undef C_14 +#undef C_58 +#undef C_61 -}; --- linux-2.6.4-rc2/drivers/input/keyboard/Kconfig 2003-10-08 15:07:08.000000000 -0700 +++ 25/drivers/input/keyboard/Kconfig 2004-03-07 20:46:53.000000000 -0800 @@ -17,6 +17,7 @@ config KEYBOARD_ATKBD depends on INPUT && INPUT_KEYBOARD select SERIO select SERIO_I8042 if PC + select SERIO_GSCPS2 if GSC help Say Y here if you want to use a standard AT or PS/2 keyboard. Usually you'll need this, unless you have a different type keyboard (USB, ADB @@ -40,6 +41,19 @@ config KEYBOARD_SUNKBD To compile this driver as a module, choose M here: the module will be called sunkbd. +config KEYBOARD_LKKBD + tristate "DECstation/VAXstation LK201/LK401 keyboard support" + depends on INPUT && INPUT_KEYBOARD + select SERIO + help + Say Y here if you want to use a LK201 or LK401 style serial + keyboard. This keyboard is also useable on PCs if you attach + it with the inputattach program. The connector pinout is + described within lkkbd.c. + + To compile this driver as a module, choose M here: the + module will be called lkkbd. + config KEYBOARD_XTKBD tristate "XT Keyboard support" depends on INPUT && INPUT_KEYBOARD --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/drivers/input/keyboard/lkkbd.c 2004-03-07 20:46:53.000000000 -0800 @@ -0,0 +1,625 @@ +/* + * Copyright (C) 2004 by Jan-Benedict Glaw + */ + +/* + * LK keyboard driver for Linux, based on sunkbd.c (C) by Vojtech Pavlik + */ + +/* + * DEC LK201 and LK401 keyboard driver for Linux (primary for DECstations + * and VAXstations, but can also be used on any standard RS232 with an + * adaptor). + * + * DISCLAUNER: This works for _me_. If you break anything by using the + * information given below, I will _not_ be lieable! + * + * RJ11 pinout: To DB9: Or DB25: + * 1 - RxD <----> Pin 3 (TxD) <-> Pin 2 (TxD) + * 2 - GND <----> Pin 5 (GND) <-> Pin 7 (GND) + * 4 - TxD <----> Pin 2 (RxD) <-> Pin 3 (RxD) + * 3 - +12V (from HDD drive connector), DON'T connect to DB9 or DB25!!! + * + * Pin numbers for DB9 and DB25 are noted on the plug (quite small:). For + * RJ11, it's like this: + * + * __=__ Hold the plug in front of you, cable downwards, + * /___/| nose is hidden behind the plug. Now, pin 1 is at + * |1234|| the left side, pin 4 at the right and 2 and 3 are + * |IIII|| in between, of course:) + * | || + * |____|/ + * || So the adaptor consists of three connected cables + * || for data transmission (RxD and TxD) and signal ground. + * Additionally, you have to get +12V from somewhere. + * Most easily, you'll get that from a floppy or HDD power connector. + * It's the yellow cable there (black is ground and red is +5V). + */ + +/* + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * email or by paper mail: + * Jan-Benedict Glaw, Lilienstraße 16, 33790 Hörste (near Halle/Westf.), + * Germany. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +MODULE_AUTHOR ("Jan-Benedict Glaw "); +MODULE_DESCRIPTION ("LK keyboard driver"); +MODULE_LICENSE ("GPL"); + +/* + * Known parameters: + * bell_volume + * keyclick_volume + * ctrlclick_volume + * + * Please notice that there's not yet an API to set these at runtime. + */ +static int bell_volume = 100; /* % */ +module_param (bell_volume, int, 0); +MODULE_PARM_DESC (bell_volume, "Bell volume (in %). default is 100%"); + +static int keyclick_volume = 100; /* % */ +module_param (keyclick_volume, int, 0); +MODULE_PARM_DESC (keyclick_volume, "Keyclick volume (in %), default is 100%"); + +static int ctrlclick_volume = 100; /* % */ +module_param (ctrlclick_volume, int, 0); +MODULE_PARM_DESC (ctrlclick_volume, "Ctrlclick volume (in %), default is 100%"); + + + +#undef LKKBD_DEBUG +#ifdef LKKBD_DEBUG +#define DBG(x...) printk (x) +#else +#define DBG(x...) do {} while (0) +#endif + +/* LED control */ +#define LK_LED_WAIT 0x81 +#define LK_LED_COMPOSE 0x82 +#define LK_LED_SHIFTLOCK 0x84 +#define LK_LED_SCROLLLOCK 0x88 +#define LK_CMD_LED_ON 0x13 +#define LK_CMD_LED_OFF 0x11 + +/* Mode control */ +#define LK_MODE_DOWN 0x80 +#define LK_MODE_AUTODOWN 0x82 +#define LK_MODE_UPDOWN 0x86 +#define LK_CMD_SET_MODE(mode,div) ((mode) | ((div) << 3)) + +/* Misc commands */ +#define LK_CMD_ENABLE_KEYCLICK 0x1b +#define LK_CMD_DISABLE_KEYCLICK 0x99 +#define LK_CMD_DISABLE_BELL 0xa1 +#define LK_CMD_SOUND_BELL 0xa7 +#define LK_CMD_ENABLE_BELL 0x23 +#define LK_CMD_DISABLE_CTRCLICK 0xb9 +#define LK_CMD_ENABLE_CTRCLICK 0xbb +#define LK_CMD_SET_DEFAULTS 0xd3 +#define LK_CMD_POWERCYCLE_RESET 0xfd +#define LK_CMD_ENABLE_LK401 0xe9 + +/* Misc responses from keyboard */ +#define LK_ALL_KEYS_UP 0xb3 +#define LK_METRONOME 0xb4 +#define LK_OUTPUT_ERROR 0xb5 +#define LK_INPUT_ERROR 0xb6 +#define LK_KBD_LOCKED 0xb7 +#define LK_KBD_TEST_MODE_ACK 0xb8 +#define LK_PREFIX_KEY_DOWN 0xb9 +#define LK_MODE_CHANGE_ACK 0xba +#define LK_RESPONSE_RESERVED 0xbb + +#define LK_NUM_KEYCODES 256 +typedef u_int16_t lk_keycode_t; + + + +static lk_keycode_t lkkbd_keycode[LK_NUM_KEYCODES] = { + [0x56] = KEY_F1, + [0x57] = KEY_F2, + [0x58] = KEY_F3, + [0x59] = KEY_F4, + [0x5a] = KEY_F5, + [0x64] = KEY_F6, + [0x65] = KEY_F7, + [0x66] = KEY_F8, + [0x67] = KEY_F9, + [0x68] = KEY_F10, + [0x71] = KEY_F11, + [0x72] = KEY_F12, + [0x73] = KEY_F13, + [0x74] = KEY_F14, + [0x7c] = KEY_F15, + [0x7d] = KEY_F16, + [0x80] = KEY_F17, + [0x81] = KEY_F18, + [0x82] = KEY_F19, + [0x83] = KEY_F20, + [0x8a] = KEY_FIND, + [0x8b] = KEY_INSERT, + [0x8c] = KEY_DELETE, + [0x8d] = KEY_SELECT, + [0x8e] = KEY_PAGEUP, + [0x8f] = KEY_PAGEDOWN, + [0x92] = KEY_KP0, + [0x94] = KEY_KPDOT, + [0x95] = KEY_KPENTER, + [0x96] = KEY_KP1, + [0x97] = KEY_KP2, + [0x98] = KEY_KP3, + [0x99] = KEY_KP4, + [0x9a] = KEY_KP5, + [0x9b] = KEY_KP6, + [0x9c] = KEY_KPCOMMA, + [0x9d] = KEY_KP7, + [0x9e] = KEY_KP8, + [0x9f] = KEY_KP9, + [0xa0] = KEY_KPMINUS, + [0xa1] = KEY_PROG1, + [0xa2] = KEY_PROG2, + [0xa3] = KEY_PROG3, + [0xa4] = KEY_PROG4, + [0xa7] = KEY_LEFT, + [0xa8] = KEY_RIGHT, + [0xa9] = KEY_DOWN, + [0xaa] = KEY_UP, + [0xab] = KEY_RIGHTSHIFT, + [0xac] = KEY_LEFTALT, + [0xad] = KEY_COMPOSE, /* Right Compose, that is. */ + [0xae] = KEY_LEFTSHIFT, /* Same as KEY_RIGHTSHIFT on LK201 */ + [0xaf] = KEY_LEFTCTRL, + [0xb0] = KEY_CAPSLOCK, + [0xb1] = KEY_COMPOSE, /* Left Compose, that is. */ + [0xb2] = KEY_RIGHTALT, + [0xbc] = KEY_BACKSPACE, + [0xbd] = KEY_ENTER, + [0xbe] = KEY_TAB, + [0xbf] = KEY_ESC, + [0xc0] = KEY_1, + [0xc1] = KEY_Q, + [0xc2] = KEY_A, + [0xc3] = KEY_Z, + [0xc5] = KEY_2, + [0xc6] = KEY_W, + [0xc7] = KEY_S, + [0xc8] = KEY_X, + [0xc9] = KEY_102ND, + [0xcb] = KEY_3, + [0xcc] = KEY_E, + [0xcd] = KEY_D, + [0xce] = KEY_C, + [0xd0] = KEY_4, + [0xd1] = KEY_R, + [0xd2] = KEY_F, + [0xd3] = KEY_V, + [0xd4] = KEY_SPACE, + [0xd6] = KEY_5, + [0xd7] = KEY_T, + [0xd8] = KEY_G, + [0xd9] = KEY_B, + [0xdb] = KEY_6, + [0xdc] = KEY_Y, + [0xdd] = KEY_H, + [0xde] = KEY_N, + [0xe0] = KEY_7, + [0xe1] = KEY_U, + [0xe2] = KEY_J, + [0xe3] = KEY_M, + [0xe5] = KEY_8, + [0xe6] = KEY_I, + [0xe7] = KEY_K, + [0xe8] = KEY_COMMA, + [0xea] = KEY_9, + [0xeb] = KEY_O, + [0xec] = KEY_L, + [0xed] = KEY_DOT, + [0xef] = KEY_0, + [0xf0] = KEY_P, + [0xf2] = KEY_SEMICOLON, + [0xf3] = KEY_SLASH, + [0xf5] = KEY_EQUAL, + [0xf6] = KEY_RIGHTBRACE, + [0xf7] = KEY_BACKSLASH, + [0xf9] = KEY_MINUS, + [0xfa] = KEY_LEFTBRACE, + [0xfb] = KEY_APOSTROPHE, +}; + +#define CHECK_LED(LED, BITS) do { \ + if (test_bit (LED, lk->dev.led)) \ + leds_on |= BITS; \ + else \ + leds_off |= BITS; \ + } while (0) + +/* + * Per-keyboard data + */ +struct lkkbd { + lk_keycode_t keycode[LK_NUM_KEYCODES]; + int ignore_bytes; + struct input_dev dev; + struct serio *serio; + struct work_struct tq; + char name[64]; + char phys[32]; + char type; + int bell_volume; + int keyclick_volume; + int ctrlclick_volume; +}; + +/* + * Calculate volume parameter byte for a given volume. + */ +static unsigned char +volume_to_hw (int volume_percent) +{ + unsigned char ret = 0; + + if (volume_percent < 0) + volume_percent = 0; + if (volume_percent > 100) + volume_percent = 100; + + if (volume_percent >= 0) + ret = 7; + if (volume_percent >= 13) /* 12.5 */ + ret = 6; + if (volume_percent >= 25) + ret = 5; + if (volume_percent >= 38) /* 37.5 */ + ret = 4; + if (volume_percent >= 50) + ret = 3; + if (volume_percent >= 63) /* 62.5 */ + ret = 2; /* This is the default volume */ + if (volume_percent >= 75) + ret = 1; + if (volume_percent >= 88) /* 87.5 */ + ret = 0; + + ret |= 0x80; + + return ret; +} + +/* + * lkkbd_interrupt() is called by the low level driver when a character + * is received. + */ +static irqreturn_t +lkkbd_interrupt (struct serio *serio, unsigned char data, unsigned int flags, + struct pt_regs *regs) +{ + struct lkkbd *lk = serio->private; + int i; + + DBG (KERN_INFO "Got byte 0x%02x\n", data); + + if (lk->ignore_bytes > 0) { + DBG (KERN_INFO "Ignoring a byte on %s\n", + lk->name); + lk->ignore_bytes--; + return IRQ_HANDLED; + } + + switch (data) { + case LK_ALL_KEYS_UP: + input_regs (&lk->dev, regs); + for (i = 0; i < ARRAY_SIZE (lkkbd_keycode); i++) + if (lk->keycode[i] != KEY_RESERVED) + input_report_key (&lk->dev, lk->keycode[i], 0); + input_sync (&lk->dev); + break; + case LK_METRONOME: + DBG (KERN_INFO "Got LK_METRONOME and don't " + "know how to handle...\n"); + break; + case LK_OUTPUT_ERROR: + DBG (KERN_INFO "Got LK_OUTPUT_ERROR and don't " + "know how to handle...\n"); + break; + case LK_INPUT_ERROR: + DBG (KERN_INFO "Got LK_INPUT_ERROR and don't " + "know how to handle...\n"); + break; + case LK_KBD_LOCKED: + DBG (KERN_INFO "Got LK_KBD_LOCKED and don't " + "know how to handle...\n"); + break; + case LK_KBD_TEST_MODE_ACK: + DBG (KERN_INFO "Got LK_KBD_TEST_MODE_ACK and don't " + "know how to handle...\n"); + break; + case LK_PREFIX_KEY_DOWN: + DBG (KERN_INFO "Got LK_PREFIX_KEY_DOWN and don't " + "know how to handle...\n"); + break; + case LK_MODE_CHANGE_ACK: + DBG (KERN_INFO "Got LK_MODE_CHANGE_ACK and ignored " + "it properly...\n"); + break; + case LK_RESPONSE_RESERVED: + DBG (KERN_INFO "Got LK_RESPONSE_RESERVED and don't " + "know how to handle...\n"); + break; + case 0x01: + DBG (KERN_INFO "Got 0x01, scheduling re-initialization\n"); + lk->ignore_bytes = 3; + schedule_work (&lk->tq); + break; + + default: + if (lk->keycode[data] != KEY_RESERVED) { + input_regs (&lk->dev, regs); + if (!test_bit (lk->keycode[data], lk->dev.key)) + input_report_key (&lk->dev, lk->keycode[data], 1); + else + input_report_key (&lk->dev, lk->keycode[data], 0); + input_sync (&lk->dev); + } else + printk (KERN_WARNING "%s: Unknown key with " + "scancode %02x on %s.\n", + __FILE__, data, lk->name); + } + + return IRQ_HANDLED; +} + +/* + * lkkbd_event() handles events from the input module. + */ +static int +lkkbd_event (struct input_dev *dev, unsigned int type, unsigned int code, + int value) +{ + struct lkkbd *lk = dev->private; + unsigned char leds_on = 0; + unsigned char leds_off = 0; + + switch (type) { + case EV_LED: + CHECK_LED (LED_CAPSL, LK_LED_SHIFTLOCK); + CHECK_LED (LED_COMPOSE, LK_LED_COMPOSE); + CHECK_LED (LED_SCROLLL, LK_LED_SCROLLLOCK); + CHECK_LED (LED_SLEEP, LK_LED_WAIT); + if (leds_on != 0) { + lk->serio->write (lk->serio, LK_CMD_LED_ON); + lk->serio->write (lk->serio, leds_on); + } + if (leds_off != 0) { + lk->serio->write (lk->serio, LK_CMD_LED_OFF); + lk->serio->write (lk->serio, leds_off); + } + return 0; + + case EV_SND: + switch (code) { + case SND_CLICK: + if (value == 0) { + DBG ("%s: Deactivating key clicks\n", __FUNCTION__); + lk->serio->write (lk->serio, LK_CMD_DISABLE_KEYCLICK); + lk->serio->write (lk->serio, LK_CMD_DISABLE_CTRCLICK); + } else { + DBG ("%s: Activating key clicks\n", __FUNCTION__); + lk->serio->write (lk->serio, LK_CMD_ENABLE_KEYCLICK); + lk->serio->write (lk->serio, volume_to_hw (lk->keyclick_volume)); + lk->serio->write (lk->serio, LK_CMD_ENABLE_CTRCLICK); + lk->serio->write (lk->serio, volume_to_hw (lk->ctrlclick_volume)); + } + return 0; + + case SND_BELL: + if (value != 0) + lk->serio->write (lk->serio, LK_CMD_SOUND_BELL); + + return 0; + } + break; + + default: + printk (KERN_ERR "%s (): Got unknown type %d, code %d, value %d\n", + __FUNCTION__, type, code, value); + } + + return -1; +} + +/* + * lkkbd_reinit() sets leds and beeps to a state the computer remembers they + * were in. + */ +static void +lkkbd_reinit (void *data) +{ + struct lkkbd *lk = data; + int division; + unsigned char leds_on = 0; + unsigned char leds_off = 0; + + /* Reset parameters */ + lk->serio->write (lk->serio, LK_CMD_SET_DEFAULTS); + + /* Set LEDs */ + CHECK_LED (LED_CAPSL, LK_LED_SHIFTLOCK); + CHECK_LED (LED_COMPOSE, LK_LED_COMPOSE); + CHECK_LED (LED_SCROLLL, LK_LED_SCROLLLOCK); + CHECK_LED (LED_SLEEP, LK_LED_WAIT); + if (leds_on != 0) { + lk->serio->write (lk->serio, LK_CMD_LED_ON); + lk->serio->write (lk->serio, leds_on); + } + if (leds_off != 0) { + lk->serio->write (lk->serio, LK_CMD_LED_OFF); + lk->serio->write (lk->serio, leds_off); + } + + /* + * Try to activate extended LK401 mode. This command will + * only work with a LK401 keyboard and grants access to + * LAlt, RAlt, RCompose and RShift. + */ + lk->serio->write (lk->serio, LK_CMD_ENABLE_LK401); + + /* Set all keys to UPDOWN mode */ + for (division = 1; division <= 14; division++) + lk->serio->write (lk->serio, LK_CMD_SET_MODE (LK_MODE_UPDOWN, + division)); + + /* Enable bell and set volume */ + lk->serio->write (lk->serio, LK_CMD_ENABLE_BELL); + lk->serio->write (lk->serio, volume_to_hw (lk->bell_volume)); + + /* Enable/disable keyclick (and possibly set volume) */ + if (test_bit (SND_CLICK, lk->dev.snd)) { + lk->serio->write (lk->serio, LK_CMD_ENABLE_KEYCLICK); + lk->serio->write (lk->serio, volume_to_hw (lk->keyclick_volume)); + lk->serio->write (lk->serio, LK_CMD_ENABLE_CTRCLICK); + lk->serio->write (lk->serio, volume_to_hw (lk->ctrlclick_volume)); + } else { + lk->serio->write (lk->serio, LK_CMD_DISABLE_KEYCLICK); + lk->serio->write (lk->serio, LK_CMD_DISABLE_CTRCLICK); + } + + /* Sound the bell if needed */ + if (test_bit (SND_BELL, lk->dev.snd)) + lk->serio->write (lk->serio, LK_CMD_SOUND_BELL); +} + +/* + * lkkbd_connect() probes for a LK keyboard and fills the necessary structures. + */ +static void +lkkbd_connect (struct serio *serio, struct serio_dev *dev) +{ + struct lkkbd *lk; + int i; + + if ((serio->type & SERIO_TYPE) != SERIO_RS232) + return; + if (!(serio->type & SERIO_PROTO)) + return; + if ((serio->type & SERIO_PROTO) && (serio->type & SERIO_PROTO) != SERIO_LKKBD) + return; + + if (!(lk = kmalloc (sizeof (struct lkkbd), GFP_KERNEL))) + return; + memset (lk, 0, sizeof (struct lkkbd)); + + init_input_dev (&lk->dev); + + lk->dev.evbit[0] = BIT (EV_KEY) | BIT (EV_LED) | BIT (EV_SND) | BIT (EV_REP); + lk->dev.ledbit[0] = BIT (LED_CAPSL) | BIT (LED_COMPOSE) | BIT (LED_SCROLLL) | BIT (LED_SLEEP); + lk->dev.sndbit[0] = BIT (SND_CLICK) | BIT (SND_BELL); + + lk->serio = serio; + + INIT_WORK (&lk->tq, lkkbd_reinit, lk); + + lk->bell_volume = bell_volume; + lk->keyclick_volume = keyclick_volume; + lk->ctrlclick_volume = ctrlclick_volume; + + lk->dev.keycode = lk->keycode; + lk->dev.keycodesize = sizeof (lk_keycode_t); + lk->dev.keycodemax = LK_NUM_KEYCODES; + + lk->dev.event = lkkbd_event; + lk->dev.private = lk; + + serio->private = lk; + + if (serio_open (serio, dev)) { + kfree (lk); + return; + } + + sprintf (lk->name, "LK keyboard"); + + memcpy (lk->keycode, lkkbd_keycode, sizeof (lk_keycode_t) * LK_NUM_KEYCODES); + for (i = 0; i < LK_NUM_KEYCODES; i++) + set_bit (lk->keycode[i], lk->dev.keybit); + + sprintf (lk->name, "%s/input0", serio->phys); + + lk->dev.name = lk->name; + lk->dev.phys = lk->phys; + lk->dev.id.bustype = BUS_RS232; + lk->dev.id.vendor = SERIO_LKKBD; + lk->dev.id.product = 0; + lk->dev.id.version = 0x0100; + + input_register_device (&lk->dev); + + printk (KERN_INFO "input: %s on %s, initiating reset\n", lk->name, serio->phys); + lk->serio->write (lk->serio, LK_CMD_POWERCYCLE_RESET); +} + +/* + * lkkbd_disconnect() unregisters and closes behind us. + */ +static void +lkkbd_disconnect (struct serio *serio) +{ + struct lkkbd *lk = serio->private; + + input_unregister_device (&lk->dev); + serio_close (serio); + kfree (lk); +} + +static struct serio_dev lkkbd_dev = { + .interrupt = lkkbd_interrupt, + .connect = lkkbd_connect, + .disconnect = lkkbd_disconnect, +}; + +/* + * The functions for insering/removing us as a module. + */ +int __init +lkkbd_init (void) +{ + serio_register_device (&lkkbd_dev); + return 0; +} + +void __exit +lkkbd_exit (void) +{ + serio_unregister_device (&lkkbd_dev); +} + +module_init (lkkbd_init); +module_exit (lkkbd_exit); + --- linux-2.6.4-rc2/drivers/input/keyboard/Makefile 2003-06-14 12:18:08.000000000 -0700 +++ 25/drivers/input/keyboard/Makefile 2004-03-07 20:46:53.000000000 -0800 @@ -7,6 +7,7 @@ obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o +obj-$(CONFIG_KEYBOARD_LKKBD) += lkkbd.o obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o --- linux-2.6.4-rc2/drivers/input/keyboard/sunkbd.c 2003-07-27 12:14:38.000000000 -0700 +++ 25/drivers/input/keyboard/sunkbd.c 2004-03-07 20:46:53.000000000 -0800 @@ -77,6 +77,7 @@ struct sunkbd { struct input_dev dev; struct serio *serio; struct work_struct tq; + wait_queue_head_t wait; char name[64]; char phys[32]; char type; @@ -96,11 +97,13 @@ static irqreturn_t sunkbd_interrupt(stru if (sunkbd->reset <= -1) { /* If cp[i] is 0xff, sunkbd->reset will stay -1. */ sunkbd->reset = data; /* The keyboard sends 0xff 0xff 0xID on powerup */ + wake_up_interruptible(&sunkbd->wait); goto out; } if (sunkbd->layout == -1) { sunkbd->layout = data; + wake_up_interruptible(&sunkbd->wait); goto out; } @@ -176,22 +179,19 @@ static int sunkbd_event(struct input_dev static int sunkbd_initialize(struct sunkbd *sunkbd) { - int t; - - t = 1000; sunkbd->reset = -2; sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_RESET); - while (sunkbd->reset < 0 && --t) mdelay(1); - if (!t) return -1; + wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ); + if (sunkbd->reset <0) + return -1; sunkbd->type = sunkbd->reset; if (sunkbd->type == 4) { /* Type 4 keyboard */ - t = 250; sunkbd->layout = -2; sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_LAYOUT); - while (sunkbd->layout < 0 && --t) mdelay(1); - if (!t) return -1; + wait_event_interruptible_timeout(sunkbd->wait, sunkbd->layout >= 0, HZ/4); + if (sunkbd->layout < 0) return -1; if (sunkbd->layout & SUNKBD_LAYOUT_5_MASK) sunkbd->type = 5; } @@ -206,9 +206,8 @@ static int sunkbd_initialize(struct sunk static void sunkbd_reinit(void *data) { struct sunkbd *sunkbd = data; - int t = 1000; - while (sunkbd->reset < 0 && --t) mdelay(1); + wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ); sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_SETLED); sunkbd->serio->write(sunkbd->serio, @@ -239,6 +238,7 @@ static void sunkbd_connect(struct serio memset(sunkbd, 0, sizeof(struct sunkbd)); init_input_dev(&sunkbd->dev); + init_waitqueue_head(&sunkbd->wait); sunkbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_SND) | BIT(EV_REP); sunkbd->dev.ledbit[0] = BIT(LED_CAPSL) | BIT(LED_COMPOSE) | BIT(LED_SCROLLL) | BIT(LED_NUML); @@ -275,7 +275,7 @@ static void sunkbd_connect(struct serio set_bit(sunkbd->keycode[i], sunkbd->dev.keybit); clear_bit(0, sunkbd->dev.keybit); - sprintf(sunkbd->name, "%s/input", serio->phys); + sprintf(sunkbd->phys, "%s/input0", serio->phys); sunkbd->dev.name = sunkbd->name; sunkbd->dev.phys = sunkbd->phys; --- linux-2.6.4-rc2/drivers/input/misc/gsc_ps2.c 2003-07-13 21:44:34.000000000 -0700 +++ /dev/null 2002-08-30 16:31:37.000000000 -0700 @@ -1,712 +0,0 @@ -/* - * drivers/input/misc/gsc_ps2.c - * - * Copyright (c) 2002 Laurent Canet - * Copyright (c) 2002 Thibaut Varene - * - * Pieces of code based on linux-2.4's hp_mouse.c & hp_keyb.c - * Copyright (c) 1999 Alex deVries - * Copyright (c) 1999-2000 Philipp Rumpf - * Copyright (c) 2000 Xavier Debacker - * Copyright (c) 2000-2001 Thomas Marteau - * - * HP PS/2 Keyboard, found in PA/RISC Workstations - * very similar to AT keyboards, but without i8042 - * - * 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. - * - * STATUS: - * 11/09: lc: Only basic keyboard is supported, mouse still needs to be done. - * 11/12: tv: switching iomapping; cleaning code; improving module stuff. - * 11/13: lc & tv: leds aren't working. auto_repeat/meta are. Generaly good behavior. - * 11/15: tv: 2AM: leds ARE working ! - * 11/16: tv: 3AM: escaped keycodes emulation *handled*, some keycodes are - * still deliberately ignored (18), what are they used for ? - * 11/21: lc: mouse is now working - * 11/29: tv: first try for error handling in init sequence - * - * TODO: - * Error handling in init sequence - * SysRq handling - * Pause key handling - * Intellimouse & other rodents handling (at least send an error when - * such a mouse is plugged : it will totally fault) - * Mouse: set scaling / Dino testing - * Bug chasing... - * - */ - -#include -#include -#include -#include /* interrupt.h wants struct pt_regs defined */ -#include -#include /* for request_irq/free_irq */ -#include -#include -#include -#include -#include - -#include -#include -#include - -/* Debugging stuff */ -#undef KBD_DEBUG -#ifdef KBD_DEBUG - #define DPRINTK(fmt,args...) printk(KERN_DEBUG __FILE__ ":" fmt, ##args) -#else - #define DPRINTK(x,...) -#endif - - -/* - * Driver constants - */ - -/* PS/2 keyboard and mouse constants */ -#define AUX_RECONNECT 0xAA /* PS/2 Mouse end of test successful */ -#define AUX_REPLY_ACK 0xFA -#define AUX_ENABLE_DEV 0xF4 /* Enables aux device */ - -/* Order of the mouse bytes coming to the host */ -#define PACKET_X 1 -#define PACKET_Y 2 -#define PACKET_CTRL 0 - -#define GSC_MOUSE_OFFSET 0x0100 /* offset from keyboard to mouse port */ -#define GSC_DINO_OFFSET 0x800 /* offset for DINO controller versus LASI one */ - -#define GSC_ID 0x00 /* ID and reset port offsets */ -#define GSC_RESET 0x00 -#define GSC_RCVDATA 0x04 /* receive and transmit port offsets */ -#define GSC_XMTDATA 0x04 -#define GSC_CONTROL 0x08 /* see: control register bits */ -#define GSC_STATUS 0x0C /* see: status register bits */ - -/* Control register bits */ -#define GSC_CTRL_ENBL 0x01 /* enable interface */ -#define GSC_CTRL_LPBXR 0x02 /* loopback operation */ -#define GSC_CTRL_DIAG 0x20 /* directly control clock/data line */ -#define GSC_CTRL_DATDIR 0x40 /* data line direct control */ -#define GSC_CTRL_CLKDIR 0x80 /* clock line direct control */ - -/* Status register bits */ -#define GSC_STAT_RBNE 0x01 /* Receive Buffer Not Empty */ -#define GSC_STAT_TBNE 0x02 /* Transmit Buffer Not Empty */ -#define GSC_STAT_TERR 0x04 /* Timeout Error */ -#define GSC_STAT_PERR 0x08 /* Parity Error */ -#define GSC_STAT_CMPINTR 0x10 /* Composite Interrupt */ -#define GSC_STAT_DATSHD 0x40 /* Data Line Shadow */ -#define GSC_STAT_CLKSHD 0x80 /* Clock Line Shadow */ - -/* Keycode map */ -#define KBD_ESCAPE0 0xe0 -#define KBD_ESCAPE1 0xe1 -#define KBD_RELEASE 0xf0 -#define KBD_ACK 0xfa -#define KBD_RESEND 0xfe -#define KBD_UNKNOWN 0 - -#define KBD_TBLSIZE 512 - -/* Mouse */ -#define MOUSE_LEFTBTN 0x1 -#define MOUSE_MIDBTN 0x4 -#define MOUSE_RIGHTBTN 0x2 -#define MOUSE_ALWAYS1 0x8 -#define MOUSE_XSIGN 0x10 -#define MOUSE_YSIGN 0x20 -#define MOUSE_XOVFLOW 0x40 -#define MOUSE_YOVFLOW 0x80 - -/* Remnant of pc_keyb.h */ -#define KBD_CMD_SET_LEDS 0xED /* Sets keyboard leds */ -#define KBD_CMD_SET_RATE 0xF3 /* Sets typematic rate */ -#define KBD_CMD_ENABLE 0xF4 /* Enables scanning */ -#define KBD_CMD_DISABLE 0xF5 -#define KBD_CMD_RESET 0xFF - -static unsigned char hpkeyb_keycode[KBD_TBLSIZE] = -{ - /* 00 */ KBD_UNKNOWN, KEY_F9, KBD_UNKNOWN, KEY_F5, KEY_F3, KEY_F1, KEY_F2, KEY_F12, - /* 08 */ KBD_UNKNOWN, KEY_F10, KEY_F8, KEY_F6, KEY_F4, KEY_TAB, KEY_GRAVE, KBD_UNKNOWN, - /* 10 */ KBD_UNKNOWN, KEY_LEFTALT, KEY_LEFTSHIFT, KBD_UNKNOWN, KEY_LEFTCTRL, KEY_Q, KEY_1, KBD_UNKNOWN, - /* 18 */ KBD_UNKNOWN, KBD_UNKNOWN, KEY_Z, KEY_S, KEY_A, KEY_W, KEY_2, KBD_UNKNOWN, - /* 20 */ KBD_UNKNOWN, KEY_C, KEY_X, KEY_D, KEY_E, KEY_4, KEY_3, KBD_UNKNOWN, - /* 28 */ KBD_UNKNOWN, KEY_SPACE, KEY_V, KEY_F, KEY_T, KEY_R, KEY_5, KBD_UNKNOWN, - /* 30 */ KBD_UNKNOWN, KEY_N, KEY_B, KEY_H, KEY_G, KEY_Y, KEY_6, KBD_UNKNOWN, - /* 38 */ KBD_UNKNOWN, KBD_UNKNOWN, KEY_M, KEY_J, KEY_U, KEY_7, KEY_8, KBD_UNKNOWN, - /* 40 */ KBD_UNKNOWN, KEY_COMMA, KEY_K, KEY_I, KEY_O, KEY_0, KEY_9, KBD_UNKNOWN, - /* 48 */ KBD_UNKNOWN, KEY_DOT, KEY_SLASH, KEY_L, KEY_SEMICOLON, KEY_P, KEY_MINUS, KBD_UNKNOWN, - /* 50 */ KBD_UNKNOWN, KBD_UNKNOWN, KEY_APOSTROPHE,KBD_UNKNOWN, KEY_LEFTBRACE, KEY_EQUAL, KBD_UNKNOWN, KBD_UNKNOWN, - /* 58 */ KEY_CAPSLOCK, KEY_RIGHTSHIFT,KEY_ENTER, KEY_RIGHTBRACE,KBD_UNKNOWN, KEY_BACKSLASH,KBD_UNKNOWN, KBD_UNKNOWN, - /* 60 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KEY_BACKSPACE, KBD_UNKNOWN, - /* 68 */ KBD_UNKNOWN, KEY_KP1, KBD_UNKNOWN, KEY_KP4, KEY_KP7, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 70 */ KEY_KP0, KEY_KPDOT, KEY_KP2, KEY_KP5, KEY_KP6, KEY_KP8, KEY_ESC, KEY_NUMLOCK, - /* 78 */ KEY_F11, KEY_KPPLUS, KEY_KP3, KEY_KPMINUS, KEY_KPASTERISK,KEY_KP9, KEY_SCROLLLOCK,KEY_103RD, - /* 80 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KEY_F7, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 88 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 90 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 98 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* a0 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* a8 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* b0 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* b8 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* c0 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* c8 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* d0 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* d8 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* e0 */ KBD_ESCAPE0, KBD_ESCAPE1, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* e8 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* f0 */ KBD_RELEASE, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* f8 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_ACK, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_RESEND, KBD_UNKNOWN, -/* These are offset for escaped keycodes */ - /* 00 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 08 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 10 */ KBD_UNKNOWN, KEY_RIGHTALT, KBD_UNKNOWN, KBD_UNKNOWN, KEY_RIGHTCTRL, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 18 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 20 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 28 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 30 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 38 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 40 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 48 */ KBD_UNKNOWN, KBD_UNKNOWN, KEY_KPSLASH, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 50 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 58 */ KBD_UNKNOWN, KBD_UNKNOWN, KEY_KPENTER, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 60 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 68 */ KBD_UNKNOWN, KEY_END, KBD_UNKNOWN, KEY_LEFT, KEY_HOME, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 70 */ KEY_INSERT, KEY_DELETE, KEY_DOWN, KBD_UNKNOWN, KEY_RIGHT, KEY_UP, KBD_UNKNOWN, KBD_UNKNOWN, - /* 78 */ KBD_UNKNOWN, KBD_UNKNOWN, KEY_PAGEDOWN, KBD_UNKNOWN, KEY_SYSRQ, KEY_PAGEUP, KBD_UNKNOWN, KBD_UNKNOWN, - /* 80 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 88 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 90 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* 98 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* a0 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* a8 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* b0 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* b8 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* c0 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* c8 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* d0 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* d8 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* e0 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* e8 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* f0 */ KBD_RELEASE, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, - /* f8 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN -}; - - -/* Keyboard struct */ -static struct { - struct input_dev dev; - char * addr; - unsigned int irq; - unsigned int scancode; - unsigned int escaped; - unsigned int released; - unsigned int initialized; -} -hpkeyb = { - .escaped = 0, - .released = 0, - .initialized = 0 -}; - -/* Mouse struct */ -static struct { - struct input_dev dev; - char * addr; - unsigned long irq; - unsigned long initialized; - int nbread; - unsigned char bytes[3]; - unsigned long last; -} -hpmouse = { - .initialized = 0, - .nbread = 0 -}; - -static spinlock_t gscps2_lock = SPIN_LOCK_UNLOCKED; - - -/* - * Various HW level routines - */ - -#define gscps2_readb_input(x) readb(x+GSC_RCVDATA) -#define gscps2_readb_control(x) readb(x+GSC_CONTROL) -#define gscps2_readb_status(x) readb(x+GSC_STATUS) -#define gscps2_writeb_control(x, y) writeb(x, y+GSC_CONTROL) - -static inline void gscps2_writeb_output(u8 val, char * addr) -{ - int wait = 250; /* Keyboard is expected to react within 250ms */ - - while (gscps2_readb_status(addr) & GSC_STAT_TBNE) { - if (!--wait) - return; /* This should not happen */ - mdelay(1); - } - writeb(val, addr+GSC_XMTDATA); -} - -static inline unsigned char gscps2_wait_input(char * addr) -{ - int wait = 250; /* Keyboard is expected to react within 250ms */ - - while (!(gscps2_readb_status(addr) & GSC_STAT_RBNE)) { - if (!--wait) - return 0; /* This should not happen */ - mdelay(1); - } - return gscps2_readb_input(addr); -} - -static int gscps2_writeb_safe_output(u8 val) -{ - /* This function waits for keyboard's ACK */ - u8 scanread = KBD_UNKNOWN; - int loop = 5; - - while (hpkeyb_keycode[scanread]!=KBD_ACK && --loop > 0) { - gscps2_writeb_output(val, hpkeyb.addr); - mdelay(5); - scanread = gscps2_wait_input(hpkeyb.addr); - } - - if (loop <= 0) - return -1; - - return 0; -} - -/* Reset the PS2 port */ -static void __init gscps2_reset(char * addr) -{ - /* reset the interface */ - writeb(0xff, addr+GSC_RESET); - writeb(0x0 , addr+GSC_RESET); - - /* enable it */ - gscps2_writeb_control(gscps2_readb_control(addr) | GSC_CTRL_ENBL, addr); -} - - -/** - * gscps2_kbd_docode() - PS2 Keyboard basic handler - * - * Receives a keyboard scancode, analyses it and sends it to the input layer. - */ - -static void gscps2_kbd_docode(struct pt_regs *regs) -{ - int scancode = gscps2_readb_input(hpkeyb.addr); - DPRINTK("rel=%d scancode=%d, esc=%d ", hpkeyb.released, scancode, hpkeyb.escaped); - - /* Handle previously escaped scancodes */ - if (hpkeyb.escaped == KBD_ESCAPE0) - scancode |= 0x100; /* jump to the next 256 chars of the table */ - - switch (hpkeyb_keycode[scancode]) { - case KBD_RELEASE: - DPRINTK("release\n"); - hpkeyb.released = 1; - break; - case KBD_RESEND: - DPRINTK("resend request\n"); - break; - case KBD_ACK: - DPRINTK("ACK\n"); - break; - case KBD_ESCAPE0: - case KBD_ESCAPE1: - DPRINTK("escape code %d\n", hpkeyb_keycode[scancode]); - hpkeyb.escaped = hpkeyb_keycode[scancode]; - break; - case KBD_UNKNOWN: - DPRINTK("received unknown scancode %d, escape %d.\n", - scancode, hpkeyb.escaped); /* This is a DPRINTK atm since we do not handle escaped scancodes cleanly */ - if (hpkeyb.escaped) - hpkeyb.escaped = 0; - if (hpkeyb.released) - hpkeyb.released = 0; - return; - default: - hpkeyb.scancode = scancode; - DPRINTK("sent=%d, rel=%d\n",hpkeyb.scancode, hpkeyb.released); - /*input_regs(regs);*/ - input_report_key(&hpkeyb.dev, hpkeyb_keycode[hpkeyb.scancode], !hpkeyb.released); - input_sync(&hpkeyb.dev); - if (hpkeyb.escaped) - hpkeyb.escaped = 0; - if (hpkeyb.released) - hpkeyb.released = 0; - break; - } -} - - -/** - * gscps2_mouse_docode() - PS2 Mouse basic handler - * - * Receives mouse codes, processes them by packets of three, and sends - * correct events to the input layer. - */ - -static void gscps2_mouse_docode(struct pt_regs *regs) -{ - int xrel, yrel; - - /* process BAT (end of basic tests) command */ - if ((hpmouse.nbread == 1) && (hpmouse.bytes[0] == AUX_RECONNECT)) - hpmouse.nbread--; - - /* stolen from psmouse.c */ - if (hpmouse.nbread && time_after(jiffies, hpmouse.last + HZ/2)) { - printk(KERN_DEBUG "%s:%d : Lost mouse synchronization, throwing %d bytes away.\n", __FILE__, __LINE__, - hpmouse.nbread); - hpmouse.nbread = 0; - } - - hpmouse.last = jiffies; - hpmouse.bytes[hpmouse.nbread++] = gscps2_readb_input(hpmouse.addr); - - /* process packet */ - if (hpmouse.nbread == 3) { - - if (!(hpmouse.bytes[PACKET_CTRL] & MOUSE_ALWAYS1)) - DPRINTK("Mouse: error on packet always1 bit checking\n"); - /* XXX should exit now, bad data on the line! */ - - if ((hpmouse.bytes[PACKET_CTRL] & (MOUSE_XOVFLOW | MOUSE_YOVFLOW))) - DPRINTK("Mouse: position overflow\n"); - - /*input_regs(regs);*/ - - input_report_key(&hpmouse.dev, BTN_LEFT, hpmouse.bytes[PACKET_CTRL] & MOUSE_LEFTBTN); - input_report_key(&hpmouse.dev, BTN_MIDDLE, hpmouse.bytes[PACKET_CTRL] & MOUSE_MIDBTN); - input_report_key(&hpmouse.dev, BTN_RIGHT, hpmouse.bytes[PACKET_CTRL] & MOUSE_RIGHTBTN); - - xrel = hpmouse.bytes[PACKET_X]; - yrel = hpmouse.bytes[PACKET_Y]; - - /* Data sent by mouse are 9-bit signed, the sign bit is in the control packet */ - if (xrel && (hpmouse.bytes[PACKET_CTRL] & MOUSE_XSIGN)) - xrel = xrel - 0x100; - if (yrel && (hpmouse.bytes[PACKET_CTRL] & MOUSE_YSIGN)) - yrel = yrel - 0x100; - - input_report_rel(&hpmouse.dev, REL_X, xrel); - input_report_rel(&hpmouse.dev, REL_Y, -yrel); /* Y axis is received upside-down */ - - input_sync(&hpmouse.dev); - - hpmouse.nbread = 0; - } -} - - -/** - * gscps2_interrupt() - Interruption service routine - * - * This processes the list of scancodes queued and sends appropriate - * key value to the system. - */ - -static irqreturn_t gscps2_interrupt(int irq, void *dev, struct pt_regs *reg) -{ - /* process mouse actions */ - while (gscps2_readb_status(hpmouse.addr) & GSC_STAT_RBNE) - gscps2_mouse_docode(reg); - - /* process keyboard scancode */ - while (gscps2_readb_status(hpkeyb.addr) & GSC_STAT_RBNE) - gscps2_kbd_docode(reg); - - return IRQ_HANDLED; -} - - -/** - * gscps2_hpkeyb_event() - Event handler - * @return: success/error report - * - * Currently only updates leds on keyboard - */ - -int gscps2_hpkeyb_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) -{ - DPRINTK("Calling %s, type=%d, code=%d, value=%d\n", - __FUNCTION__, type, code, value); - - if (!hpkeyb.initialized) - return -1; - - if (type == EV_LED) { - u8 leds[2]; - - if (gscps2_writeb_safe_output(KBD_CMD_SET_LEDS)) { - printk(KERN_ERR "gsckbd_leds: timeout\n"); - return -1; - } - DPRINTK("KBD_CMD_SET_LEDS\n"); - - *leds = (test_bit(LED_SCROLLL, dev->led) ? LED_SCR : 0) - | (test_bit(LED_NUML, dev->led) ? LED_NUM : 0) - | (test_bit(LED_CAPSL, dev->led) ? LED_CAP : 0); - DPRINTK("Sending leds=%x\n", *leds); - - if (gscps2_writeb_safe_output(*leds)) { - printk(KERN_ERR "gsckbd_leds: timeout\n"); - return -1; - } - DPRINTK("leds sent\n"); - - if (gscps2_writeb_safe_output(KBD_CMD_ENABLE)) { - printk(KERN_ERR "gsckbd_leds: timeout\n"); - return -1; - } - DPRINTK("End\n"); - - return 0; - - } - return -1; -} - - -/** - * gscps2_kbd_probe() - Probes keyboard device and init input_dev structure - * @return: number of device initialized (1, 0 on error) - */ - -static int __init gscps2_kbd_probe(void) -{ - int i, res = 0; - unsigned long flags; - - if (hpkeyb.initialized) { - printk(KERN_ERR "GSC PS/2 keyboard driver already registered\n"); - return 0; - } - - spin_lock_irqsave(&gscps2_lock, flags); - - if (!gscps2_writeb_safe_output(KBD_CMD_SET_LEDS) && - !gscps2_writeb_safe_output(0) && - !gscps2_writeb_safe_output(KBD_CMD_ENABLE)) - res = 1; - - spin_unlock_irqrestore(&gscps2_lock, flags); - - if (!res) - printk(KERN_ERR "Keyboard initialization sequence failled\n"); - - init_input_dev(&hpkeyb.dev); - - for (i = 0; i < KBD_TBLSIZE; i++) - if (hpkeyb_keycode[i] != KBD_UNKNOWN) - set_bit(hpkeyb_keycode[i], hpkeyb.dev.keybit); - - hpkeyb.dev.evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP); - hpkeyb.dev.ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL); - hpkeyb.dev.keycode = hpkeyb_keycode; - hpkeyb.dev.keycodesize = sizeof(unsigned char); - hpkeyb.dev.keycodemax = KBD_TBLSIZE; - hpkeyb.dev.name = "GSC Keyboard"; - hpkeyb.dev.phys = "hpkbd/input0"; - - hpkeyb.dev.event = gscps2_hpkeyb_event; - - /* TODO These need some adjustement, are they really useful ? */ - hpkeyb.dev.id.bustype = BUS_GSC; - hpkeyb.dev.id.vendor = PCI_VENDOR_ID_HP; - hpkeyb.dev.id.product = 0x0001; - hpkeyb.dev.id.version = 0x0010; - hpkeyb.initialized = 1; - - return 1; -} - - -/** - * gscps2_mouse_probe() - Probes mouse device and init input_dev structure - * @return: number of device initialized (1, 0 on error) - * - * Currently no check on initialization is performed - */ - -static int __init gscps2_mouse_probe(void) -{ - if (hpmouse.initialized) { - printk(KERN_ERR "GSC PS/2 Mouse driver already registered\n"); - return 0; - } - - init_input_dev(&hpmouse.dev); - - hpmouse.dev.name = "GSC Mouse"; - hpmouse.dev.phys = "hpmouse/input0"; - hpmouse.dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL); - hpmouse.dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT); - hpmouse.dev.relbit[0] = BIT(REL_X) | BIT(REL_Y); - hpmouse.last = 0; - - gscps2_writeb_output(AUX_ENABLE_DEV, hpmouse.addr); - /* Try it a second time, this will give status if the device is available */ - gscps2_writeb_output(AUX_ENABLE_DEV, hpmouse.addr); - - /* TODO These need some adjustement, are they really useful ? */ - hpmouse.dev.id.bustype = BUS_GSC; - hpmouse.dev.id.vendor = 0x0001; - hpmouse.dev.id.product = 0x0001; - hpmouse.dev.id.version = 0x0010; - hpmouse.initialized = 1; - return 1; /* XXX: we don't check if initialization failed */ -} - - -/** - * gscps2_probe() - Probes PS2 devices - * @return: success/error report - */ - -static int __init gscps2_probe(struct parisc_device *dev) -{ - u8 id; - char *addr, *name; - int ret = 0, device_found = 0; - unsigned long hpa = dev->hpa; - - if (!dev->irq) - goto fail_pitifully; - - /* Offset for DINO PS/2. Works with LASI even */ - if (dev->id.sversion == 0x96) - hpa += GSC_DINO_OFFSET; - - addr = ioremap(hpa, 256); - - if (!hpmouse.initialized || !hpkeyb.initialized) - gscps2_reset(addr); - - ret = -EINVAL; - id = readb(addr+GSC_ID) & 0x0f; - switch (id) { - case 0: /* keyboard */ - hpkeyb.addr = addr; - name = "keyboard"; - device_found = gscps2_kbd_probe(); - break; - case 1: /* mouse */ - hpmouse.addr = addr; - name = "mouse"; - device_found = gscps2_mouse_probe(); - break; - default: - printk(KERN_WARNING "%s: Unsupported PS/2 port (id=%d) ignored\n", - __FUNCTION__, id); - goto fail_miserably; - } - - /* No valid device found */ - ret = -ENODEV; - if (!device_found) - goto fail_miserably; - - /* Here we claim only if we have a device attached */ - /* Allocate the irq and memory region for that device */ - ret = -EBUSY; - if (request_irq(dev->irq, gscps2_interrupt, 0, name, NULL)) - goto fail_miserably; - - if (!request_mem_region(hpa, GSC_STATUS + 4, name)) - goto fail_request_mem; - - /* Finalize input struct and register it */ - switch (id) { - case 0: /* keyboard */ - hpkeyb.irq = dev->irq; - input_register_device(&hpkeyb.dev); - break; - case 1: /* mouse */ - hpmouse.irq = dev->irq; - input_register_device(&hpmouse.dev); - break; - default: - break; - } - - printk(KERN_INFO "input: PS/2 %s port at 0x%08lx (irq %d) found and attached\n", - name, hpa, dev->irq); - - return 0; - -fail_request_mem: free_irq(dev->irq, NULL); -fail_miserably: iounmap(addr); -fail_pitifully: return ret; -} - - - -static struct parisc_device_id gscps2_device_tbl[] = { - { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00084 }, /* LASI PS/2 */ -/* { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00096 }, DINO PS/2 (XXX Not yet tested) */ - { 0, } /* 0 terminated list */ -}; - -static struct parisc_driver gscps2_driver = { - .name = "GSC PS2", - .id_table = gscps2_device_tbl, - .probe = gscps2_probe, -}; - -static int __init gscps2_init(void) -{ - if (register_parisc_driver(&gscps2_driver)) - return -EBUSY; - return 0; -} - -static void __exit gscps2_exit(void) -{ - /* TODO this is probably not very good and needs to be checked */ - if (hpkeyb.initialized) { - free_irq(hpkeyb.irq, gscps2_interrupt); - iounmap(hpkeyb.addr); - hpkeyb.initialized = 0; - input_unregister_device(&hpkeyb.dev); - } - if (hpmouse.initialized) { - free_irq(hpmouse.irq, gscps2_interrupt); - iounmap(hpmouse.addr); - hpmouse.initialized = 0; - input_unregister_device(&hpmouse.dev); - } - unregister_parisc_driver(&gscps2_driver); -} - - -MODULE_AUTHOR("Laurent Canet , Thibaut Varene "); -MODULE_DESCRIPTION("GSC PS/2 keyboard/mouse driver"); -MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(parisc, gscps2_device_tbl); - - -module_init(gscps2_init); -module_exit(gscps2_exit); --- linux-2.6.4-rc2/drivers/input/misc/Kconfig 2003-09-27 18:57:44.000000000 -0700 +++ 25/drivers/input/misc/Kconfig 2004-03-07 20:46:53.000000000 -0800 @@ -54,12 +54,3 @@ config INPUT_UINPUT To compile this driver as a module, choose M here: the module will be called uinput. -config INPUT_GSC - tristate "PA-RISC GSC PS/2 keyboard/mouse support" - depends on GSC && INPUT && INPUT_MISC - help - Say Y here if you have a PS/2 keyboard and/or mouse attached - to your PA-RISC box. HP run the keyboard in AT mode rather than - XT mode like everyone else, so we need our own driver. - Furthermore, the GSC PS/2 controller shares IRQ between mouse and - keyboard. --- linux-2.6.4-rc2/drivers/input/misc/Makefile 2003-06-14 12:18:23.000000000 -0700 +++ 25/drivers/input/misc/Makefile 2004-03-07 20:46:53.000000000 -0800 @@ -9,4 +9,3 @@ obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o obj-$(CONFIG_INPUT_98SPKR) += 98spkr.o obj-$(CONFIG_INPUT_UINPUT) += uinput.o -obj-$(CONFIG_INPUT_GSC) += gsc_ps2.o --- linux-2.6.4-rc2/drivers/input/mouse/98busmouse.c 2004-02-03 20:42:35.000000000 -0800 +++ 25/drivers/input/mouse/98busmouse.c 2004-03-07 20:46:53.000000000 -0800 @@ -74,6 +74,8 @@ static int pc98bm_irq = PC98BM_IRQ; module_param_named(irq, pc98bm_irq, uint, 0); MODULE_PARM_DESC(irq, "IRQ number (13=default)"); +__obsolete_setup("pc98bm_irq="); + static int pc98bm_used = 0; static irqreturn_t pc98bm_interrupt(int irq, void *dev_id, struct pt_regs *regs); --- linux-2.6.4-rc2/drivers/input/mouse/inport.c 2004-02-03 20:42:35.000000000 -0800 +++ 25/drivers/input/mouse/inport.c 2004-03-07 20:46:53.000000000 -0800 @@ -85,6 +85,8 @@ static int inport_irq = INPORT_IRQ; module_param_named(irq, inport_irq, uint, 0); MODULE_PARM_DESC(irq, "IRQ number (5=default)"); +__obsolete_setup("inport_irq="); + static int inport_used; static irqreturn_t inport_interrupt(int irq, void *dev_id, struct pt_regs *regs); --- linux-2.6.4-rc2/drivers/input/mouse/Kconfig 2004-02-03 20:42:35.000000000 -0800 +++ 25/drivers/input/mouse/Kconfig 2004-03-07 20:46:53.000000000 -0800 @@ -17,6 +17,7 @@ config MOUSE_PS2 depends on INPUT && INPUT_MOUSE select SERIO select SERIO_I8042 if PC + select SERIO_GSCPS2 if GSC ---help--- Say Y here if you have a PS/2 mouse connected to your system. This includes the standard 2 or 3-button PS/2 mouse, as well as PS/2 @@ -117,6 +118,19 @@ config MOUSE_RISCPC To compile this driver as a module, choose M here: the module will be called rpcmouse. +config MOUSE_VSXXXAA + tristate "DEC VSXXX-AA/GA mouse and tablet" + depends on INPUT && INPUT_MOUSE + select SERIO + help + Say Y (or M) if you want to use a DEC VSXXX-AA (hockey + puck) or a VSXXX-GA (rectangular) mouse. Theses mice are + typically used on DECstations or VAXstations, but can also + be used on any box capable of RS232 (with some adaptor + described in the source file). This driver should, in theory, + also work with the digitizer DEC produced, but it isn't tested + with that (I don't have the hardware yet). + config MOUSE_PC9800 tristate "NEC PC-9800 busmouse" depends on X86_PC9800 && INPUT && INPUT_MOUSE && ISA --- linux-2.6.4-rc2/drivers/input/mouse/logibm.c 2004-02-03 20:42:35.000000000 -0800 +++ 25/drivers/input/mouse/logibm.c 2004-03-07 20:46:53.000000000 -0800 @@ -75,6 +75,8 @@ static int logibm_irq = LOGIBM_IRQ; module_param_named(irq, logibm_irq, uint, 0); MODULE_PARM_DESC(irq, "IRQ number (5=default)"); +__obsolete_setup("logibm_irq="); + static int logibm_used = 0; static irqreturn_t logibm_interrupt(int irq, void *dev_id, struct pt_regs *regs); --- linux-2.6.4-rc2/drivers/input/mouse/Makefile 2003-06-22 12:04:44.000000000 -0700 +++ 25/drivers/input/mouse/Makefile 2004-03-07 20:46:53.000000000 -0800 @@ -13,5 +13,6 @@ obj-$(CONFIG_MOUSE_PC110PAD) += pc110pad obj-$(CONFIG_MOUSE_PC9800) += 98busmouse.o obj-$(CONFIG_MOUSE_PS2) += psmouse.o obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o +obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o psmouse-objs := psmouse-base.o logips2pp.o synaptics.o --- linux-2.6.4-rc2/drivers/input/mouse/psmouse-base.c 2004-02-03 20:42:35.000000000 -0800 +++ 25/drivers/input/mouse/psmouse-base.c 2004-03-07 20:46:53.000000000 -0800 @@ -47,6 +47,12 @@ unsigned int psmouse_resetafter; module_param_named(resetafter, psmouse_resetafter, uint, 0); MODULE_PARM_DESC(resetafter, "Reset Synaptics Touchpad after so many bad packets (0 = never)."); +__obsolete_setup("psmouse_noext"); +__obsolete_setup("psmouse_resolution="); +__obsolete_setup("psmouse_smartscroll="); +__obsolete_setup("psmouse_resetafter="); +__obsolete_setup("psmouse_rate="); + static char *psmouse_protocols[] = { "None", "PS/2", "PS2++", "PS2T++", "GenPS/2", "ImPS/2", "ImExPS/2", "SynPS/2"}; /* @@ -163,14 +169,14 @@ static irqreturn_t psmouse_interrupt(str psmouse->name, psmouse->phys, psmouse->pktcnt); psmouse->pktcnt = 0; } - + psmouse->last = jiffies; psmouse->packet[psmouse->pktcnt++] = data; if (psmouse->packet[0] == PSMOUSE_RET_BAT) { if (psmouse->pktcnt == 1) goto out; - + if (psmouse->pktcnt == 2) { if (psmouse->packet[1] == PSMOUSE_RET_ID) { psmouse->state = PSMOUSE_IGNORE; @@ -258,7 +264,7 @@ int psmouse_command(struct psmouse *psmo return (psmouse->cmdcnt = 0) - 1; while (psmouse->cmdcnt && timeout--) { - + if (psmouse->cmdcnt == 1 && command == PSMOUSE_CMD_RESET_BAT && timeout > 100000) /* do not run in a endless loop */ timeout = 100000; /* 1 sec */ @@ -442,7 +448,7 @@ static int psmouse_probe(struct psmouse */ if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_RESET_DIS)) - return -1; + printk(KERN_WARNING "psmouse.c: Failed to reset mouse on %s\n", psmouse->serio->phys); /* * And here we try to determine if it has any extensions over the @@ -497,7 +503,7 @@ static void psmouse_set_rate(struct psmo static void psmouse_initialize(struct psmouse *psmouse) { unsigned char param[2]; - + /* * We set the mouse report rate, resolution and scaling. */ @@ -571,7 +577,7 @@ static void psmouse_disconnect(struct se static void psmouse_connect(struct serio *serio, struct serio_dev *dev) { struct psmouse *psmouse; - + if ((serio->type & SERIO_TYPE) != SERIO_8042 && (serio->type & SERIO_TYPE) != SERIO_PS_PSTHRU) return; @@ -603,7 +609,7 @@ static void psmouse_connect(struct serio serio->private = NULL; return; } - + sprintf(psmouse->devname, "%s %s %s", psmouse_protocols[psmouse->type], psmouse->vendor, psmouse->name); sprintf(psmouse->phys, "%s/input0", @@ -617,7 +623,7 @@ static void psmouse_connect(struct serio psmouse->dev.id.version = psmouse->model; input_register_device(&psmouse->dev); - + printk(KERN_INFO "input: %s on %s\n", psmouse->devname, serio->phys); psmouse_initialize(psmouse); --- linux-2.6.4-rc2/drivers/input/mouse/synaptics.c 2004-02-03 20:42:35.000000000 -0800 +++ 25/drivers/input/mouse/synaptics.c 2004-03-07 20:46:53.000000000 -0800 @@ -435,6 +435,8 @@ int synaptics_init(struct psmouse *psmou goto init_fail; } + priv->pkt_type = SYN_MODEL_NEWABS(priv->model_id) ? SYN_NEWABS : SYN_OLDABS; + if (SYN_CAP_EXTENDED(priv->capabilities) && SYN_CAP_PASS_THROUGH(priv->capabilities)) synaptics_pt_create(psmouse); @@ -602,19 +604,42 @@ static void synaptics_process_packet(str input_sync(dev); } -static int synaptics_validate_byte(struct psmouse *psmouse) +static int synaptics_validate_byte(unsigned char packet[], int idx, unsigned char pkt_type) { - static unsigned char newabs_mask[] = { 0xC0, 0x00, 0x00, 0xC0, 0x00 }; - static unsigned char newabs_rslt[] = { 0x80, 0x00, 0x00, 0xC0, 0x00 }; - static unsigned char oldabs_mask[] = { 0xC0, 0x60, 0x00, 0xC0, 0x60 }; - static unsigned char oldabs_rslt[] = { 0xC0, 0x00, 0x00, 0x80, 0x00 }; - struct synaptics_data *priv = psmouse->private; - int idx = psmouse->pktcnt - 1; + static unsigned char newabs_mask[] = { 0xC8, 0x00, 0x00, 0xC8, 0x00 }; + static unsigned char newabs_rel_mask[] = { 0xC0, 0x00, 0x00, 0xC0, 0x00 }; + static unsigned char newabs_rslt[] = { 0x80, 0x00, 0x00, 0xC0, 0x00 }; + static unsigned char oldabs_mask[] = { 0xC0, 0x60, 0x00, 0xC0, 0x60 }; + static unsigned char oldabs_rslt[] = { 0xC0, 0x00, 0x00, 0x80, 0x00 }; + + switch (pkt_type) { + case SYN_NEWABS: + case SYN_NEWABS_RELAXED: + return (packet[idx] & newabs_rel_mask[idx]) == newabs_rslt[idx]; + + case SYN_NEWABS_STRICT: + return (packet[idx] & newabs_mask[idx]) == newabs_rslt[idx]; + + case SYN_OLDABS: + return (packet[idx] & oldabs_mask[idx]) == oldabs_rslt[idx]; - if (SYN_MODEL_NEWABS(priv->model_id)) - return (psmouse->packet[idx] & newabs_mask[idx]) == newabs_rslt[idx]; - else - return (psmouse->packet[idx] & oldabs_mask[idx]) == oldabs_rslt[idx]; + default: + printk(KERN_ERR "synaptics: unknown packet type %d\n", pkt_type); + return 0; + } +} + +static unsigned char synaptics_detect_pkt_type(struct psmouse *psmouse) +{ + int i; + + for (i = 0; i < 5; i++) + if (!synaptics_validate_byte(psmouse->packet, i, SYN_NEWABS_STRICT)) { + printk(KERN_INFO "synaptics: using relaxed packet validation\n"); + return SYN_NEWABS_RELAXED; + } + + return SYN_NEWABS_STRICT; } void synaptics_process_byte(struct psmouse *psmouse, struct pt_regs *regs) @@ -630,13 +655,17 @@ void synaptics_process_byte(struct psmou printk(KERN_NOTICE "Synaptics driver resynced.\n"); } + if (unlikely(priv->pkt_type == SYN_NEWABS)) + priv->pkt_type = synaptics_detect_pkt_type(psmouse); + if (psmouse->ptport && psmouse->ptport->serio.dev && synaptics_is_pt_packet(psmouse->packet)) synaptics_pass_pt_packet(&psmouse->ptport->serio, psmouse->packet); else synaptics_process_packet(psmouse); psmouse->pktcnt = 0; - } else if (psmouse->pktcnt && !synaptics_validate_byte(psmouse)) { + } else if (psmouse->pktcnt && + !synaptics_validate_byte(psmouse->packet, psmouse->pktcnt - 1, priv->pkt_type)) { printk(KERN_WARNING "Synaptics driver lost sync at byte %d\n", psmouse->pktcnt); psmouse->pktcnt = 0; if (++priv->out_of_sync == psmouse_resetafter) { --- linux-2.6.4-rc2/drivers/input/mouse/synaptics.h 2004-01-09 00:04:31.000000000 -0800 +++ 25/drivers/input/mouse/synaptics.h 2004-03-07 20:46:53.000000000 -0800 @@ -70,6 +70,12 @@ extern int synaptics_init(struct psmouse #define SYN_PS_SET_MODE2 0x14 #define SYN_PS_CLIENT_CMD 0x28 +/* synaptics packet types */ +#define SYN_NEWABS 0 +#define SYN_NEWABS_STRICT 1 +#define SYN_NEWABS_RELAXED 2 +#define SYN_OLDABS 3 + /* * A structure to describe the state of the touchpad hardware (buttons and pad) */ @@ -103,6 +109,7 @@ struct synaptics_data { /* Data for normal processing */ unsigned int out_of_sync; /* # of packets out of sync */ int old_w; /* Previous w value */ + unsigned char pkt_type; /* packet type - old, new, etc */ }; #endif /* _SYNAPTICS_H */ --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/drivers/input/mouse/vsxxxaa.c 2004-03-07 20:46:53.000000000 -0800 @@ -0,0 +1,550 @@ +/* + * DEC VSXXX-AA and VSXXX-GA mouse driver. + * + * Copyright (C) 2003-2004 by Jan-Benedict Glaw + * + * The packet format was taken from a patch to GPM which is (C) 2001 + * by Karsten Merker + * and Maciej W. Rozycki + */ + +/* + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Building an adaptor to DB9 / DB25 RS232 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * DISCLAIMER: Use this description AT YOUR OWN RISK! I'll not pay for + * anything if you break your mouse, your computer or whatever! + * + * In theory, this mouse is a simple RS232 device. In practice, it has got + * a quite uncommon plug and the requirement to additionally get a power + * supply at +5V and -12V. + * + * If you look at the socket/jack (_not_ at the plug), we use this pin + * numbering: + * _______ + * / 7 6 5 \ + * | 4 --- 3 | + * \ 2 1 / + * ------- + * + * DEC socket DB9 DB25 Note + * 1 (GND) 5 7 - + * 2 (RxD) 3 3 - + * 3 (TxD) 2 2 - + * 4 (-12V) - - Somewhere from the PSU. At ATX, it's + * the blue wire at pin 12 of the ATX + * power connector. Please note that the + * docs say this should be +12V! However, + * I measured -12V... + * 5 (+5V) - - PSU (red wire of ATX power connector + * on pin 4, 6, 19 or 20) or HDD power + * connector (also red wire) + * 6 (not conn.) - - - + * 7 (dev. avail.) - - The mouse shorts this one to pin 1. + * This way, the host computer can detect + * the mouse. To use it with the adaptor, + * simply don't connect this pin. + * + * So to get a working adaptor, you need to connect the mouse with three + * wires to a RS232 port and two additional wires for +5V and -12V to the + * PSU. + * + * Flow specification for the link is 4800, 8o1. + */ + +/* + * TODO list: + * - Automatically attach to a given serial port (no need for inputattach). + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR ("Jan-Benedict Glaw "); +MODULE_DESCRIPTION ("Serial DEC VSXXX-AA/GA mouse / DEC tablet driver"); +MODULE_LICENSE ("GPL"); + +#undef VSXXXAA_DEBUG +#ifdef VSXXXAA_DEBUG +#define DBG(x...) printk (x) +#else +#define DBG(x...) do {} while (0) +#endif + +#define VSXXXAA_INTRO_MASK 0x80 +#define VSXXXAA_INTRO_HEAD 0x80 +#define IS_HDR_BYTE(x) (((x) & VSXXXAA_INTRO_MASK) \ + == VSXXXAA_INTRO_HEAD) + +#define VSXXXAA_PACKET_MASK 0xe0 +#define VSXXXAA_PACKET_REL 0x80 +#define VSXXXAA_PACKET_ABS 0xc0 +#define VSXXXAA_PACKET_POR 0xa0 +#define MATCH_PACKET_TYPE(data, type) (((data) & VSXXXAA_PACKET_MASK) == type) + + + +struct vsxxxaa { + struct input_dev dev; + struct serio *serio; +#define BUFLEN 15 /* At least 5 is needed for a full tablet packet */ + unsigned char buf[BUFLEN]; + unsigned char count; + unsigned char version; + unsigned char country; + unsigned char type; + char phys[32]; +}; + +static void +vsxxxaa_drop_bytes (struct vsxxxaa *mouse, int num) +{ + if (num >= mouse->count) + mouse->count = 0; + else { + memmove (mouse->buf, mouse->buf + num - 1, BUFLEN - num); + mouse->count -= num; + } +} + +static void +vsxxxaa_queue_byte (struct vsxxxaa *mouse, unsigned char byte) +{ + if (mouse->count == BUFLEN) { + printk (KERN_ERR "%s on %s: Dropping a byte of full buffer.\n", + mouse->dev.name, mouse->dev.phys); + vsxxxaa_drop_bytes (mouse, 1); + } + + mouse->buf[mouse->count++] = byte; +} + +static void +vsxxxaa_report_mouse (struct vsxxxaa *mouse) +{ + char *devtype; + + switch (mouse->type) { + case 0x02: devtype = "DEC mouse"; break; + case 0x04: devtype = "DEC tablet"; break; + default: devtype = "unknown DEC device"; break; + } + + printk (KERN_INFO "Found %s version 0x%x from country 0x%x " + "on port %s\n", devtype, mouse->version, + mouse->country, mouse->dev.phys); +} + +/* + * Returns number of bytes to be dropped, 0 if packet is okay. + */ +static int +vsxxxaa_check_packet (struct vsxxxaa *mouse, int packet_len) +{ + int i; + + /* First byte must be a header byte */ + if (!IS_HDR_BYTE (mouse->buf[0])) { + DBG ("vsck: len=%d, 1st=0x%02x\n", packet_len, mouse->buf[0]); + return 1; + } + + /* Check all following bytes */ + if (packet_len > 1) { + for (i = 1; i < packet_len; i++) { + if (IS_HDR_BYTE (mouse->buf[i])) { + printk (KERN_ERR "Need to drop %d bytes " + "of a broken packet.\n", + i - 1); + DBG (KERN_INFO "check: len=%d, b[%d]=0x%02x\n", + packet_len, i, mouse->buf[i]); + return i - 1; + } + } + } + + return 0; +} + +static __inline__ int +vsxxxaa_smells_like_packet (struct vsxxxaa *mouse, unsigned char type, size_t len) +{ + return (mouse->count >= len) && MATCH_PACKET_TYPE (mouse->buf[0], type); +} + +static void +vsxxxaa_handle_REL_packet (struct vsxxxaa *mouse, struct pt_regs *regs) +{ + struct input_dev *dev = &mouse->dev; + unsigned char *buf = mouse->buf; + int left, middle, right; + int dx, dy; + + /* + * Check for normal stream packets. This is three bytes, + * with the first byte's 3 MSB set to 100. + * + * [0]: 1 0 0 SignX SignY Left Middle Right + * [1]: 0 dx dx dx dx dx dx dx + * [2]: 0 dy dy dy dy dy dy dy + */ + + /* + * Low 7 bit of byte 1 are abs(dx), bit 7 is + * 0, bit 4 of byte 0 is direction. + */ + dx = buf[1] & 0x7f; + dx *= ((buf[0] >> 4) & 0x01)? -1: 1; + + /* + * Low 7 bit of byte 2 are abs(dy), bit 7 is + * 0, bit 3 of byte 0 is direction. + */ + dy = buf[2] & 0x7f; + dy *= ((buf[0] >> 3) & 0x01)? -1: 1; + + /* + * Get button state. It's the low three bits + * (for three buttons) of byte 0. + */ + left = (buf[0] & 0x04)? 1: 0; + middle = (buf[0] & 0x02)? 1: 0; + right = (buf[0] & 0x01)? 1: 0; + + vsxxxaa_drop_bytes (mouse, 3); + + DBG (KERN_INFO "%s on %s: dx=%d, dy=%d, buttons=%s%s%s\n", + mouse->dev.name, mouse->dev.phys, dx, dy, + left? "L": "l", middle? "M": "m", right? "R": "r"); + + /* + * Report what we've found so far... + */ + input_regs (dev, regs); + input_report_key (dev, BTN_LEFT, left); + input_report_key (dev, BTN_MIDDLE, middle); + input_report_key (dev, BTN_RIGHT, right); + input_report_rel (dev, REL_X, dx); + input_report_rel (dev, REL_Y, dy); + input_sync (dev); +} + +static void +vsxxxaa_handle_ABS_packet (struct vsxxxaa *mouse, struct pt_regs *regs) +{ + struct input_dev *dev = &mouse->dev; + unsigned char *buf = mouse->buf; + int left, middle, right, extra; + int x, y; + + /* + * Tablet position / button packet + * + * [0]: 1 1 0 B4 B3 B2 B1 Pr + * [1]: 0 0 X5 X4 X3 X2 X1 X0 + * [2]: 0 0 X11 X10 X9 X8 X7 X6 + * [3]: 0 0 Y5 Y4 Y3 Y2 Y1 Y0 + * [4]: 0 0 Y11 Y10 Y9 Y8 Y7 Y6 + */ + + /* + * Get X/Y position + */ + x = ((buf[2] & 0x3f) << 6) | (buf[1] & 0x3f); + y = ((buf[4] & 0x3f) << 6) | (buf[3] & 0x3f); + + /* + * Get button state. It's bits <4..1> of byte 0. + */ + left = (buf[0] & 0x02)? 1: 0; + middle = (buf[0] & 0x04)? 1: 0; + right = (buf[0] & 0x08)? 1: 0; + extra = (buf[0] & 0x10)? 1: 0; + + vsxxxaa_drop_bytes (mouse, 5); + + DBG (KERN_INFO "%s on %s: x=%d, y=%d, buttons=%s%s%s%s\n", + mouse->dev.name, mouse->dev.phys, x, y, + left? "L": "l", middle? "M": "m", + right? "R": "r", extra? "E": "e"); + + /* + * Report what we've found so far... + */ + input_regs (dev, regs); + input_report_key (dev, BTN_LEFT, left); + input_report_key (dev, BTN_MIDDLE, middle); + input_report_key (dev, BTN_RIGHT, right); + input_report_key (dev, BTN_EXTRA, extra); + input_report_abs (dev, ABS_X, x); + input_report_abs (dev, ABS_Y, y); + input_sync (dev); +} + +static void +vsxxxaa_handle_POR_packet (struct vsxxxaa *mouse, struct pt_regs *regs) +{ + struct input_dev *dev = &mouse->dev; + unsigned char *buf = mouse->buf; + int left, middle, right; + unsigned char error; + + /* + * Check for Power-On-Reset packets. These are sent out + * after plugging the mouse in, or when explicitely + * requested by sending 'T'. + * + * [0]: 1 0 1 0 R3 R2 R1 R0 + * [1]: 0 M2 M1 M0 D3 D2 D1 D0 + * [2]: 0 E6 E5 E4 E3 E2 E1 E0 + * [3]: 0 0 0 0 0 Left Middle Right + * + * M: manufacturer location code + * R: revision code + * E: Error code. I'm not sure about these, but gpm's sources, + * which support this mouse, too, tell about them: + * E = [0x00 .. 0x1f]: no error, byte #3 is button state + * E = 0x3d: button error, byte #3 tells which one. + * E = : other error + * D: <0010> == mouse, <0100> == tablet + * + */ + + mouse->version = buf[0] & 0x0f; + mouse->country = (buf[1] >> 4) & 0x07; + mouse->type = buf[1] & 0x07; + error = buf[2] & 0x7f; + + /* + * Get button state. It's the low three bits + * (for three buttons) of byte 0. Maybe even the bit <3> + * has some meaning if a tablet is attached. + */ + left = (buf[0] & 0x04)? 1: 0; + middle = (buf[0] & 0x02)? 1: 0; + right = (buf[0] & 0x01)? 1: 0; + + vsxxxaa_drop_bytes (mouse, 4); + vsxxxaa_report_mouse (mouse); + + if (error <= 0x1f) { + /* No error. Report buttons */ + input_regs (dev, regs); + input_report_key (dev, BTN_LEFT, left); + input_report_key (dev, BTN_MIDDLE, middle); + input_report_key (dev, BTN_RIGHT, right); + input_sync (dev); + } else { + printk (KERN_ERR "Your %s on %s reports an undefined error, " + "please check it...\n", mouse->dev.name, + mouse->dev.phys); + } + + /* + * If the mouse was hot-plugged, we need to + * force differential mode now... + */ + printk (KERN_NOTICE "%s on %s: Forceing standard packet format and " + "streaming mode\n", mouse->dev.name, mouse->dev.phys); + mouse->serio->write (mouse->serio, 'S'); + mouse->serio->write (mouse->serio, 'R'); +} + +static void +vsxxxaa_parse_buffer (struct vsxxxaa *mouse, struct pt_regs *regs) +{ + unsigned char *buf = mouse->buf; + int stray_bytes; + + /* + * Parse buffer to death... + */ + do { + /* + * Out of sync? Throw away what we don't understand. Each + * packet starts with a byte whose bit 7 is set. Unhandled + * packets (ie. which we don't know about or simply b0rk3d + * data...) will get shifted out of the buffer after some + * activity on the mouse. + */ + while (mouse->count > 0 && !IS_HDR_BYTE(buf[0])) { + printk (KERN_ERR "%s on %s: Dropping a byte to regain " + "sync with mouse data stream...\n", + mouse->dev.name, mouse->dev.phys); + vsxxxaa_drop_bytes (mouse, 1); + } + + /* + * Check for packets we know about. + */ + + if (vsxxxaa_smells_like_packet (mouse, VSXXXAA_PACKET_REL, 3)) { + /* Check for broken packet */ + stray_bytes = vsxxxaa_check_packet (mouse, 3); + if (stray_bytes > 0) { + printk (KERN_ERR "Dropping %d bytes now...\n", + stray_bytes); + vsxxxaa_drop_bytes (mouse, stray_bytes); + continue; + } + + vsxxxaa_handle_REL_packet (mouse, regs); + continue; /* More to parse? */ + } + + if (vsxxxaa_smells_like_packet (mouse, VSXXXAA_PACKET_ABS, 5)) { + /* Check for broken packet */ + stray_bytes = vsxxxaa_check_packet (mouse, 5); + if (stray_bytes > 0) { + printk (KERN_ERR "Dropping %d bytes now...\n", + stray_bytes); + vsxxxaa_drop_bytes (mouse, stray_bytes); + continue; + } + + vsxxxaa_handle_ABS_packet (mouse, regs); + continue; /* More to parse? */ + } + + if (vsxxxaa_smells_like_packet (mouse, VSXXXAA_PACKET_POR, 4)) { + /* Check for broken packet */ + stray_bytes = vsxxxaa_check_packet (mouse, 4); + if (stray_bytes > 0) { + printk (KERN_ERR "Dropping %d bytes now...\n", + stray_bytes); + vsxxxaa_drop_bytes (mouse, stray_bytes); + continue; + } + + vsxxxaa_handle_POR_packet (mouse, regs); + continue; /* More to parse? */ + } + + break; /* No REL, ABS or POR packet found */ + } while (1); +} + +static irqreturn_t +vsxxxaa_interrupt (struct serio *serio, unsigned char data, unsigned int flags, + struct pt_regs *regs) +{ + struct vsxxxaa *mouse = serio->private; + + vsxxxaa_queue_byte (mouse, data); + vsxxxaa_parse_buffer (mouse, regs); + + return IRQ_HANDLED; +} + +static void +vsxxxaa_disconnect (struct serio *serio) +{ + struct vsxxxaa *mouse = serio->private; + + input_unregister_device (&mouse->dev); + serio_close (serio); + kfree (mouse); +} + +static void +vsxxxaa_connect (struct serio *serio, struct serio_dev *dev) +{ + struct vsxxxaa *mouse; + + if ((serio->type & SERIO_TYPE) != SERIO_RS232) + return; + + if ((serio->type & SERIO_PROTO) != SERIO_VSXXXAA) + return; + + if (!(mouse = kmalloc (sizeof (struct vsxxxaa), GFP_KERNEL))) + return; + + memset (mouse, 0, sizeof (struct vsxxxaa)); + + init_input_dev (&mouse->dev); + set_bit (EV_KEY, mouse->dev.evbit); /* We have buttons */ + set_bit (EV_REL, mouse->dev.evbit); /* We can move */ + set_bit (BTN_LEFT, mouse->dev.keybit); /* We have 3 buttons */ + set_bit (BTN_MIDDLE, mouse->dev.keybit); + set_bit (BTN_RIGHT, mouse->dev.keybit); + set_bit (BTN_EXTRA, mouse->dev.keybit); /* ...and Tablet */ + set_bit (REL_X, mouse->dev.relbit); /* We can move in */ + set_bit (REL_Y, mouse->dev.relbit); /* two dimensions */ + set_bit (ABS_X, mouse->dev.absbit); /* DEC tablet support */ + set_bit (ABS_Y, mouse->dev.absbit); + + mouse->dev.absmin[ABS_X] = 0; + mouse->dev.absmax[ABS_X] = 1023; + mouse->dev.absmin[ABS_Y] = 0; + mouse->dev.absmax[ABS_Y] = 1023; + + mouse->dev.private = mouse; + serio->private = mouse; + + sprintf (mouse->phys, "%s/input0", serio->phys); + mouse->dev.phys = mouse->phys; + mouse->dev.name = "DEC VSXXX-AA/GA mouse or DEC tablet"; + mouse->dev.id.bustype = BUS_RS232; + mouse->serio = serio; + + if (serio_open (serio, dev)) { + kfree (mouse); + return; + } + + /* + * Request selftest and differential stream mode. + */ + mouse->serio->write (mouse->serio, 'T'); /* Test */ + mouse->serio->write (mouse->serio, 'R'); /* Differential stream */ + + input_register_device (&mouse->dev); + + printk (KERN_INFO "input: %s on %s\n", mouse->dev.name, serio->phys); +} + +static struct serio_dev vsxxxaa_dev = { + .interrupt = vsxxxaa_interrupt, + .connect = vsxxxaa_connect, + .disconnect = vsxxxaa_disconnect +}; + +int __init +vsxxxaa_init (void) +{ + serio_register_device (&vsxxxaa_dev); + return 0; +} + +void __exit +vsxxxaa_exit (void) +{ + serio_unregister_device (&vsxxxaa_dev); +} + +module_init (vsxxxaa_init); +module_exit (vsxxxaa_exit); + --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/drivers/input/serio/gscps2.c 2004-03-07 20:46:53.000000000 -0800 @@ -0,0 +1,470 @@ +/* + * drivers/input/serio/gscps2.c + * + * Copyright (c) 2004 Helge Deller + * Copyright (c) 2002 Laurent Canet + * Copyright (c) 2002 Thibaut Varene + * + * Pieces of code based on linux-2.4's hp_mouse.c & hp_keyb.c + * Copyright (c) 1999 Alex deVries + * Copyright (c) 1999-2000 Philipp Rumpf + * Copyright (c) 2000 Xavier Debacker + * Copyright (c) 2000-2001 Thomas Marteau + * + * HP GSC PS/2 port driver, found in PA/RISC Workstations + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * TODO: + * - Dino testing (did HP ever shipped a machine on which this port + * was usable/enabled ?) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +MODULE_AUTHOR("Laurent Canet , Thibaut Varene , Helge Deller "); +MODULE_DESCRIPTION("HP GSC PS/2 port driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(parisc, gscps2_device_tbl); + +#define PFX "gscps2.c: " + +/* + * Driver constants + */ + +/* various constants */ +#define ENABLE 1 +#define DISABLE 0 + +#define GSC_DINO_OFFSET 0x0800 /* offset for DINO controller versus LASI one */ + +/* PS/2 IO port offsets */ +#define GSC_ID 0x00 /* device ID offset (see: GSC_ID_XXX) */ +#define GSC_RESET 0x00 /* reset port offset */ +#define GSC_RCVDATA 0x04 /* receive port offset */ +#define GSC_XMTDATA 0x04 /* transmit port offset */ +#define GSC_CONTROL 0x08 /* see: Control register bits */ +#define GSC_STATUS 0x0C /* see: Status register bits */ + +/* Control register bits */ +#define GSC_CTRL_ENBL 0x01 /* enable interface */ +#define GSC_CTRL_LPBXR 0x02 /* loopback operation */ +#define GSC_CTRL_DIAG 0x20 /* directly control clock/data line */ +#define GSC_CTRL_DATDIR 0x40 /* data line direct control */ +#define GSC_CTRL_CLKDIR 0x80 /* clock line direct control */ + +/* Status register bits */ +#define GSC_STAT_RBNE 0x01 /* Receive Buffer Not Empty */ +#define GSC_STAT_TBNE 0x02 /* Transmit Buffer Not Empty */ +#define GSC_STAT_TERR 0x04 /* Timeout Error */ +#define GSC_STAT_PERR 0x08 /* Parity Error */ +#define GSC_STAT_CMPINTR 0x10 /* Composite Interrupt = irq on any port */ +#define GSC_STAT_DATSHD 0x40 /* Data Line Shadow */ +#define GSC_STAT_CLKSHD 0x80 /* Clock Line Shadow */ + +/* IDs returned by GSC_ID port register */ +#define GSC_ID_KEYBOARD 0 /* device ID values */ +#define GSC_ID_MOUSE 1 + + +static irqreturn_t gscps2_interrupt(int irq, void *dev, struct pt_regs *regs); + +#define BUFFER_SIZE 0x0f + +/* GSC PS/2 port device struct */ +struct gscps2port { + struct list_head node; + struct parisc_device *padev; + struct serio port; + spinlock_t lock; + char *addr; + u8 act, append; /* position in buffer[] */ + struct { + u8 data; + u8 str; + } buffer[BUFFER_SIZE+1]; + int id; + char name[32]; +}; + +/* + * Various HW level routines + */ + +#define gscps2_readb_input(x) readb((x)+GSC_RCVDATA) +#define gscps2_readb_control(x) readb((x)+GSC_CONTROL) +#define gscps2_readb_status(x) readb((x)+GSC_STATUS) +#define gscps2_writeb_control(x, y) writeb((x), (y)+GSC_CONTROL) + + +/* + * wait_TBE() - wait for Transmit Buffer Empty + */ + +static int wait_TBE(char *addr) +{ + int timeout = 25000; /* device is expected to react within 250 msec */ + while (gscps2_readb_status(addr) & GSC_STAT_TBNE) { + if (!--timeout) + return 0; /* This should not happen */ + udelay(10); + } + return 1; +} + + +/* + * gscps2_flush() - flush the receive buffer + */ + +static void gscps2_flush(struct gscps2port *ps2port) +{ + while (gscps2_readb_status(ps2port->addr) & GSC_STAT_RBNE) + gscps2_readb_input(ps2port->addr); + ps2port->act = ps2port->append = 0; +} + +/* + * gscps2_writeb_output() - write a byte to the port + * + * returns 1 on sucess, 0 on error + */ + +static inline int gscps2_writeb_output(struct gscps2port *ps2port, u8 data) +{ + unsigned long flags; + char *addr = ps2port->addr; + + if (!wait_TBE(addr)) { + printk(KERN_DEBUG PFX "timeout - could not write byte %#x\n", data); + return 0; + } + + while (gscps2_readb_status(ps2port->addr) & GSC_STAT_RBNE) + /* wait */; + + spin_lock_irqsave(&ps2port->lock, flags); + writeb(data, addr+GSC_XMTDATA); + spin_unlock_irqrestore(&ps2port->lock, flags); + + /* this is ugly, but due to timing of the port it seems to be necessary. */ + mdelay(6); + + /* make sure any received data is returned as fast as possible */ + /* this is important e.g. when we set the LEDs on the keyboard */ + gscps2_interrupt(0, NULL, NULL); + + return 1; +} + + +/* + * gscps2_enable() - enables or disables the port + */ + +static void gscps2_enable(struct gscps2port *ps2port, int enable) +{ + unsigned long flags; + u8 data; + + /* now enable/disable the port */ + spin_lock_irqsave(&ps2port->lock, flags); + gscps2_flush(ps2port); + data = gscps2_readb_control(ps2port->addr); + if (enable) + data |= GSC_CTRL_ENBL; + else + data &= ~GSC_CTRL_ENBL; + gscps2_writeb_control(data, ps2port->addr); + spin_unlock_irqrestore(&ps2port->lock, flags); + wait_TBE(ps2port->addr); + gscps2_flush(ps2port); +} + +/* + * gscps2_reset() - resets the PS/2 port + */ + +static void gscps2_reset(struct gscps2port *ps2port) +{ + char *addr = ps2port->addr; + unsigned long flags; + + /* reset the interface */ + spin_lock_irqsave(&ps2port->lock, flags); + gscps2_flush(ps2port); + writeb(0xff, addr+GSC_RESET); + gscps2_flush(ps2port); + spin_unlock_irqrestore(&ps2port->lock, flags); + + /* enable it */ + gscps2_enable(ps2port, ENABLE); +} + +static LIST_HEAD(ps2port_list); + +/** + * gscps2_interrupt() - Interruption service routine + * + * This function reads received PS/2 bytes and processes them on + * all interfaces. + * The problematic part here is, that the keyboard and mouse PS/2 port + * share the same interrupt and it's not possible to send data if any + * one of them holds input data. To solve this problem we try to receive + * the data as fast as possible and handle the reporting to the upper layer + * later. + */ + +static irqreturn_t gscps2_interrupt(int irq, void *dev, struct pt_regs *regs) +{ + struct gscps2port *ps2port; + + list_for_each_entry(ps2port, &ps2port_list, node) { + + unsigned long flags; + spin_lock_irqsave(&ps2port->lock, flags); + + while ( (ps2port->buffer[ps2port->append].str = + gscps2_readb_status(ps2port->addr)) & GSC_STAT_RBNE ) { + ps2port->buffer[ps2port->append].data = + gscps2_readb_input(ps2port->addr); + ps2port->append = ((ps2port->append+1) & BUFFER_SIZE); + } + + spin_unlock_irqrestore(&ps2port->lock, flags); + + } /* list_for_each_entry */ + + /* all data was read from the ports - now report the data to upper layer */ + + list_for_each_entry(ps2port, &ps2port_list, node) { + + while (ps2port->act != ps2port->append) { + + unsigned int rxflags; + u8 data, status; + + /* Did new data arrived while we read existing data ? + If yes, exit now and let the new irq handler start over again */ + if (gscps2_readb_status(ps2port->addr) & GSC_STAT_CMPINTR) + return IRQ_HANDLED; + + status = ps2port->buffer[ps2port->act].str; + data = ps2port->buffer[ps2port->act].data; + + ps2port->act = ((ps2port->act+1) & BUFFER_SIZE); + rxflags = ((status & GSC_STAT_TERR) ? SERIO_TIMEOUT : 0 ) | + ((status & GSC_STAT_PERR) ? SERIO_PARITY : 0 ); + + serio_interrupt(&ps2port->port, data, rxflags, regs); + + } /* while() */ + + } /* list_for_each_entry */ + + return IRQ_HANDLED; +} + + +/* + * gscps2_write() - send a byte out through the aux interface. + */ + +static int gscps2_write(struct serio *port, unsigned char data) +{ + struct gscps2port *ps2port = port->driver; + + if (!gscps2_writeb_output(ps2port, data)) { + printk(KERN_DEBUG PFX "sending byte %#x failed.\n", data); + return -1; + } + return 0; +} + +/* + * gscps2_open() is called when a port is opened by the higher layer. + * It resets and enables the port. + */ + +static int gscps2_open(struct serio *port) +{ + struct gscps2port *ps2port = port->driver; + + gscps2_reset(ps2port); + + gscps2_interrupt(0, NULL, NULL); + + return 0; +} + +/* + * gscps2_close() disables the port + */ + +static void gscps2_close(struct serio *port) +{ + struct gscps2port *ps2port = port->driver; + gscps2_enable(ps2port, DISABLE); +} + +static struct serio gscps2_serio_port = +{ + .name = "GSC PS/2", + .idbus = BUS_GSC, + .idvendor = PCI_VENDOR_ID_HP, + .idproduct = 0x0001, + .idversion = 0x0010, + .type = SERIO_8042, + .write = gscps2_write, + .open = gscps2_open, + .close = gscps2_close, +}; + +/** + * gscps2_probe() - Probes PS2 devices + * @return: success/error report + */ + +static int __init gscps2_probe(struct parisc_device *dev) +{ + struct gscps2port *ps2port; + unsigned long hpa = dev->hpa; + int ret; + + if (!dev->irq) + return -ENODEV; + + /* Offset for DINO PS/2. Works with LASI even */ + if (dev->id.sversion == 0x96) + hpa += GSC_DINO_OFFSET; + + ps2port = kmalloc(sizeof(struct gscps2port), GFP_KERNEL); + if (!ps2port) + return -ENOMEM; + + dev_set_drvdata(&dev->dev, ps2port); + + memset(ps2port, 0, sizeof(struct gscps2port)); + ps2port->padev = dev; + ps2port->addr = ioremap(hpa, GSC_STATUS + 4); + spin_lock_init(&ps2port->lock); + + gscps2_reset(ps2port); + ps2port->id = readb(ps2port->addr+GSC_ID) & 0x0f; + snprintf(ps2port->name, sizeof(ps2port->name)-1, "%s %s", + gscps2_serio_port.name, + (ps2port->id == GSC_ID_KEYBOARD) ? "keyboard" : "mouse" ); + + memcpy(&ps2port->port, &gscps2_serio_port, sizeof(gscps2_serio_port)); + ps2port->port.driver = ps2port; + ps2port->port.name = ps2port->name; + ps2port->port.phys = dev->dev.bus_id; + + list_add_tail(&ps2port->node, &ps2port_list); + + ret = -EBUSY; + if (request_irq(dev->irq, gscps2_interrupt, SA_SHIRQ, ps2port->name, ps2port)) + goto fail_miserably; + + if ( (ps2port->id != GSC_ID_KEYBOARD) && (ps2port->id != GSC_ID_MOUSE) ) { + printk(KERN_WARNING PFX "Unsupported PS/2 port at 0x%08lx (id=%d) ignored\n", + hpa, ps2port->id); + ret = -ENODEV; + goto fail; + } + +#if 0 + if (!request_mem_region(hpa, GSC_STATUS + 4, ps2port->port.name)) + goto fail; +#endif + + printk(KERN_INFO "serio: %s port at 0x%p irq %d @ %s\n", + ps2port->name, + ps2port->addr, + ps2port->padev->irq, + ps2port->port.phys); + + serio_register_port(&ps2port->port); + + return 0; + +fail: + free_irq(dev->irq, ps2port); + +fail_miserably: + list_del(&ps2port->node); + iounmap(ps2port->addr); + release_mem_region(dev->hpa, GSC_STATUS + 4); + kfree(ps2port); + return ret; +} + +/** + * gscps2_remove() - Removes PS2 devices + * @return: success/error report + */ + +static int __devexit gscps2_remove(struct parisc_device *dev) +{ + struct gscps2port *ps2port = dev_get_drvdata(&dev->dev); + + serio_unregister_port(&ps2port->port); + free_irq(dev->irq, ps2port); + gscps2_flush(ps2port); + list_del(&ps2port->node); + iounmap(ps2port->addr); +#if 0 + release_mem_region(dev->hpa, GSC_STATUS + 4); +#endif + dev_set_drvdata(&dev->dev, NULL); + kfree(ps2port); + return 0; +} + + +static struct parisc_device_id gscps2_device_tbl[] = { + { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00084 }, /* LASI PS/2 */ +#ifdef DINO_TESTED + { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00096 }, /* DINO PS/2 */ +#endif + { 0, } /* 0 terminated list */ +}; + +static struct parisc_driver parisc_ps2_driver = { + .name = "GSC PS/2", + .id_table = gscps2_device_tbl, + .probe = gscps2_probe, + .remove = gscps2_remove, +}; + +static int __init gscps2_init(void) +{ + register_parisc_driver(&parisc_ps2_driver); + return 0; +} + +static void __exit gscps2_exit(void) +{ + unregister_parisc_driver(&parisc_ps2_driver); +} + + +module_init(gscps2_init); +module_exit(gscps2_exit); + --- linux-2.6.4-rc2/drivers/input/serio/i8042.c 2004-03-03 23:12:44.000000000 -0800 +++ 25/drivers/input/serio/i8042.c 2004-03-07 20:46:53.000000000 -0800 @@ -52,6 +52,13 @@ static unsigned int i8042_dumbkbd; module_param_named(dumbkbd, i8042_dumbkbd, bool, 0); MODULE_PARM_DESC(dumbkbd, "Pretend that controller can only read data from keyboard"); +__obsolete_setup("i8042_noaux"); +__obsolete_setup("i8042_nomux"); +__obsolete_setup("i8042_unlock"); +__obsolete_setup("i8042_reset"); +__obsolete_setup("i8042_direct"); +__obsolete_setup("i8042_dumbkbd"); + #undef DEBUG #include "i8042.h" @@ -379,6 +386,8 @@ static irqreturn_t i8042_interrupt(int i unsigned int dfl; int ret; + mod_timer(&i8042_timer, jiffies + I8042_POLL_PERIOD); + spin_lock_irqsave(&i8042_lock, flags); str = i8042_read_status(); if (str & I8042_STR_OBF) @@ -433,7 +442,6 @@ static irqreturn_t i8042_interrupt(int i irq_ret: ret = 1; out: - mod_timer(&i8042_timer, jiffies + I8042_POLL_PERIOD); return IRQ_RETVAL(ret); } @@ -522,6 +530,11 @@ static int __init i8042_check_mux(struct if (i8042_enable_mux_mode(values, &mux_version)) return -1; + + /* Workaround for broken chips which seem to support MUX, but in reality don't. */ + /* They all report version 12.10 */ + if (mux_version == 0xCA) + return -1; printk(KERN_INFO "i8042.c: Detected active multiplexing controller, rev %d.%d.\n", (mux_version >> 4) & 0xf, mux_version & 0xf); @@ -709,14 +722,6 @@ static int i8042_controller_init(void) } /* - * If the chip is configured into nontranslated mode by the BIOS, don't - * bother enabling translating and be happy. - */ - - if (~i8042_ctr & I8042_CTR_XLATE) - i8042_direct = 1; - -/* * Set nontranslated mode for the kbd interface if requested by an option. * After this the kbd interface becomes a simple serial in/out, like the aux * interface is. We don't do this by default, since it can confuse notebook --- linux-2.6.4-rc2/drivers/input/serio/Kconfig 2003-09-27 18:57:44.000000000 -0700 +++ 25/drivers/input/serio/Kconfig 2004-03-07 20:46:53.000000000 -0800 @@ -20,6 +20,7 @@ config SERIO_I8042 tristate "i8042 PC Keyboard controller" if EMBEDDED || !X86 default y select SERIO + depends on !PARISC ---help--- i8042 is the chip over which the standard AT keyboard and PS/2 mouse are connected to the computer. If you use these devices, @@ -48,6 +49,7 @@ config SERIO_SERPORT config SERIO_CT82C710 tristate "ct82c710 Aux port controller" depends on SERIO + depends on !PARISC ---help--- Say Y here if you have a Texas Instruments TravelMate notebook equipped with the ct82c710 chip and want to use a mouse connected @@ -105,6 +107,20 @@ config SERIO_98KBD To compile this driver as a module, choose M here: the module will be called 98kbd-io. +config SERIO_GSCPS2 + tristate "HP GSC PS/2 keyboard and PS/2 mouse controller" + depends on GSC && SERIO + default y + help + This driver provides support for the PS/2 ports on PA-RISC machines + over which HP PS/2 keyboards and PS/2 mice may be connected. + If you use these devices, you'll need to say Y here. + + It's safe to enable this driver, so if unsure, say Y. + + To compile this driver as a module, choose M here: the + module will be called gscps2. + config SERIO_PCIPS2 tristate "PCI PS/2 keyboard and PS/2 mouse controller" depends on PCI && SERIO --- linux-2.6.4-rc2/drivers/input/serio/Makefile 2003-06-16 22:32:21.000000000 -0700 +++ 25/drivers/input/serio/Makefile 2004-03-07 20:46:53.000000000 -0800 @@ -14,4 +14,5 @@ obj-$(CONFIG_SERIO_SA1111) += sa1111ps2. obj-$(CONFIG_SERIO_AMBAKMI) += ambakmi.o obj-$(CONFIG_SERIO_Q40KBD) += q40kbd.o obj-$(CONFIG_SERIO_98KBD) += 98kbd-io.o +obj-$(CONFIG_SERIO_GSCPS2) += gscps2.o obj-$(CONFIG_SERIO_PCIPS2) += pcips2.o --- linux-2.6.4-rc2/drivers/input/serio/serio.c 2004-01-09 00:04:31.000000000 -0800 +++ 25/drivers/input/serio/serio.c 2004-03-07 20:46:53.000000000 -0800 @@ -195,6 +195,9 @@ irqreturn_t serio_interrupt(struct serio ret = serio->dev->interrupt(serio, data, flags, regs); } else { if (!flags) { + if ((serio->type == SERIO_8042 || + serio->type == SERIO_8042_XL) && (data != 0xaa)) + return ret; serio_rescan(serio); ret = IRQ_HANDLED; } --- linux-2.6.4-rc2/drivers/macintosh/mediabay.c 2004-02-17 20:48:43.000000000 -0800 +++ 25/drivers/macintosh/mediabay.c 2004-03-07 20:47:47.000000000 -0800 @@ -10,8 +10,6 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ -#define __KERNEL_SYSCALLS__ - #include #include #include @@ -21,7 +19,6 @@ #include #include #include -#include #include #include #include --- linux-2.6.4-rc2/drivers/mca/mca-bus.c 2003-08-22 19:23:40.000000000 -0700 +++ 25/drivers/mca/mca-bus.c 2004-03-07 20:47:00.000000000 -0800 @@ -106,6 +106,7 @@ int __init mca_register_device(int bus, sprintf (mca_dev->dev.bus_id, "%02d:%02X", bus, mca_dev->slot); mca_dev->dma_mask = mca_bus->default_dma_mask; mca_dev->dev.dma_mask = &mca_dev->dma_mask; + mca_dev->dev.coherent_dma_mask = mca_dev->dma_mask; if (device_register(&mca_dev->dev)) return 0; --- linux-2.6.4-rc2/drivers/md/dm.c 2004-03-03 23:12:44.000000000 -0800 +++ 25/drivers/md/dm.c 2004-03-07 20:47:54.000000000 -0800 @@ -21,6 +21,9 @@ static const char *_name = DM_NAME; static unsigned int major = 0; static unsigned int _major = 0; +/* + * One of these is allocated per bio. + */ struct dm_io { struct mapped_device *md; int error; @@ -29,6 +32,16 @@ struct dm_io { }; /* + * One of these is allocated per target within a bio. Hopefully + * this will be simplified out one day. + */ +struct target_io { + struct dm_io *io; + struct dm_target *ti; + union map_info info; +}; + +/* * Bits for the md->flags field. */ #define DMF_BLOCK_IO 0 @@ -36,6 +49,7 @@ struct dm_io { struct mapped_device { struct rw_semaphore lock; + rwlock_t maplock; atomic_t holders; unsigned long flags; @@ -59,6 +73,7 @@ struct mapped_device { * io objects are allocated from here. */ mempool_t *io_pool; + mempool_t *tio_pool; /* * Event handling. @@ -69,6 +84,7 @@ struct mapped_device { #define MIN_IOS 256 static kmem_cache_t *_io_cache; +static kmem_cache_t *_tio_cache; static __init int local_init(void) { @@ -80,9 +96,18 @@ static __init int local_init(void) if (!_io_cache) return -ENOMEM; + /* allocate a slab for the target ios */ + _tio_cache = kmem_cache_create("dm_tio", sizeof(struct target_io), + 0, 0, NULL, NULL); + if (!_tio_cache) { + kmem_cache_destroy(_io_cache); + return -ENOMEM; + } + _major = major; r = register_blkdev(_major, _name); if (r < 0) { + kmem_cache_destroy(_tio_cache); kmem_cache_destroy(_io_cache); return r; } @@ -95,6 +120,7 @@ static __init int local_init(void) static void local_exit(void) { + kmem_cache_destroy(_tio_cache); kmem_cache_destroy(_io_cache); if (unregister_blkdev(_major, _name) < 0) @@ -184,6 +210,16 @@ static inline void free_io(struct mapped mempool_free(io, md->io_pool); } +static inline struct target_io *alloc_tio(struct mapped_device *md) +{ + return mempool_alloc(md->tio_pool, GFP_NOIO); +} + +static inline void free_tio(struct mapped_device *md, struct target_io *tio) +{ + mempool_free(tio, md->tio_pool); +} + /* * Add the bio to the list of deferred io. */ @@ -232,17 +268,30 @@ static inline void dec_pending(struct dm static int clone_endio(struct bio *bio, unsigned int done, int error) { - struct dm_io *io = bio->bi_private; + int r = 0; + struct target_io *tio = bio->bi_private; + struct dm_io *io = tio->io; + dm_endio_fn endio = tio->ti->type->end_io; if (bio->bi_size) return 1; + if (endio) { + r = endio(tio->ti, bio, error, &tio->info); + if (r < 0) + error = r; + + else if (r > 0) + /* the target wants another shot at the io */ + return 1; + } + + free_tio(io->md, tio); dec_pending(io, error); bio_put(bio); - return 0; + return r; } - static sector_t max_io_len(struct mapped_device *md, sector_t sector, struct dm_target *ti) { @@ -263,7 +312,8 @@ static sector_t max_io_len(struct mapped return len; } -static void __map_bio(struct dm_target *ti, struct bio *clone, struct dm_io *io) +static void __map_bio(struct dm_target *ti, struct bio *clone, + struct target_io *tio) { int r; @@ -273,22 +323,25 @@ static void __map_bio(struct dm_target * BUG_ON(!clone->bi_size); clone->bi_end_io = clone_endio; - clone->bi_private = io; + clone->bi_private = tio; /* * Map the clone. If r == 0 we don't need to do * anything, the target has assumed ownership of * this io. */ - atomic_inc(&io->io_count); - r = ti->type->map(ti, clone); + atomic_inc(&tio->io->io_count); + r = ti->type->map(ti, clone, &tio->info); if (r > 0) /* the bio has been remapped so dispatch it */ generic_make_request(clone); - else if (r < 0) + else if (r < 0) { /* error the io and bail out */ + struct dm_io *io = tio->io; + free_tio(tio->io->md, tio); dec_pending(io, -EIO); + } } struct clone_info { @@ -348,6 +401,15 @@ static void __clone_and_map(struct clone struct bio *clone, *bio = ci->bio; struct dm_target *ti = dm_table_find_target(ci->md->map, ci->sector); sector_t len = 0, max = max_io_len(ci->md, ci->sector, ti); + struct target_io *tio; + + /* + * Allocate a target io object. + */ + tio = alloc_tio(ci->md); + tio->io = ci->io; + tio->ti = ti; + memset(&tio->info, 0, sizeof(tio->info)); if (ci->sector_count <= max) { /* @@ -356,7 +418,7 @@ static void __clone_and_map(struct clone */ clone = clone_bio(bio, ci->sector, ci->idx, bio->bi_vcnt - ci->idx, ci->sector_count); - __map_bio(ti, clone, ci->io); + __map_bio(ti, clone, tio); ci->sector_count = 0; } else if (to_sector(bio->bi_io_vec[ci->idx].bv_len) <= max) { @@ -379,7 +441,7 @@ static void __clone_and_map(struct clone } clone = clone_bio(bio, ci->sector, ci->idx, i - ci->idx, len); - __map_bio(ti, clone, ci->io); + __map_bio(ti, clone, tio); ci->sector += len; ci->sector_count -= len; @@ -394,7 +456,7 @@ static void __clone_and_map(struct clone clone = split_bvec(bio, ci->sector, ci->idx, bv->bv_offset, max); - __map_bio(ti, clone, ci->io); + __map_bio(ti, clone, tio); ci->sector += max; ci->sector_count -= max; @@ -403,7 +465,11 @@ static void __clone_and_map(struct clone len = to_sector(bv->bv_len) - max; clone = split_bvec(bio, ci->sector, ci->idx, bv->bv_offset + to_bytes(max), len); - __map_bio(ti, clone, ci->io); + tio = alloc_tio(ci->md); + tio->io = ci->io; + tio->ti = ti; + memset(&tio->info, 0, sizeof(tio->info)); + __map_bio(ti, clone, tio); ci->sector += len; ci->sector_count -= len; @@ -441,6 +507,16 @@ static void __split_bio(struct mapped_de *---------------------------------------------------------------*/ +static inline void __dm_request(struct mapped_device *md, struct bio *bio) +{ + if (!md->map) { + bio_io_error(bio, bio->bi_size); + return; + } + + __split_bio(md, bio); +} + /* * The request function that just remaps the bio built up by * dm_merge_bvec. @@ -479,16 +555,26 @@ static int dm_request(request_queue_t *q down_read(&md->lock); } - if (!md->map) { - bio_io_error(bio, bio->bi_size); - return 0; - } - - __split_bio(md, bio); + __dm_request(md, bio); up_read(&md->lock); return 0; } +static int dm_any_congested(void *congested_data, int bdi_bits) +{ + int r; + struct mapped_device *md = congested_data; + + read_lock(&md->maplock); + if (md->map == NULL || test_bit(DMF_BLOCK_IO, &md->flags)) + r = bdi_bits; + else + r = dm_table_any_congested(md->map, bdi_bits); + read_unlock(&md->maplock); + + return r; +} + /*----------------------------------------------------------------- * A bitset is used to keep track of allocated minor numbers. *---------------------------------------------------------------*/ @@ -560,6 +646,7 @@ static struct mapped_device *alloc_dev(u memset(md, 0, sizeof(*md)); init_rwsem(&md->lock); + rwlock_init(&md->maplock); atomic_set(&md->holders, 1); md->queue = blk_alloc_queue(GFP_KERNEL); @@ -567,6 +654,8 @@ static struct mapped_device *alloc_dev(u goto bad1; md->queue->queuedata = md; + md->queue->backing_dev_info.congested_fn = dm_any_congested; + md->queue->backing_dev_info.congested_data = md; blk_queue_make_request(md->queue, dm_request); md->io_pool = mempool_create(MIN_IOS, mempool_alloc_slab, @@ -574,9 +663,14 @@ static struct mapped_device *alloc_dev(u if (!md->io_pool) goto bad2; + md->tio_pool = mempool_create(MIN_IOS, mempool_alloc_slab, + mempool_free_slab, _tio_cache); + if (!md->tio_pool) + goto bad3; + md->disk = alloc_disk(1); if (!md->disk) - goto bad3; + goto bad4; md->disk->major = _major; md->disk->first_minor = minor; @@ -592,7 +686,8 @@ static struct mapped_device *alloc_dev(u return md; - + bad4: + mempool_destroy(md->tio_pool); bad3: mempool_destroy(md->io_pool); bad2: @@ -606,6 +701,7 @@ static struct mapped_device *alloc_dev(u static void free_dev(struct mapped_device *md) { free_minor(md->disk->first_minor); + mempool_destroy(md->tio_pool); mempool_destroy(md->io_pool); del_gendisk(md->disk); put_disk(md->disk); @@ -644,13 +740,15 @@ static int __bind(struct mapped_device * { request_queue_t *q = md->queue; sector_t size; - md->map = t; size = dm_table_get_size(t); __set_size(md->disk, size); if (size == 0) return 0; + write_lock(&md->maplock); + md->map = t; + write_unlock(&md->maplock); dm_table_event_callback(md->map, event_callback, md); dm_table_get(t); @@ -660,12 +758,16 @@ static int __bind(struct mapped_device * static void __unbind(struct mapped_device *md) { - if (!md->map) + struct dm_table *map = md->map; + + if (!map) return; - dm_table_event_callback(md->map, NULL, NULL); - dm_table_put(md->map); + dm_table_event_callback(map, NULL, NULL); + write_lock(&md->maplock); md->map = NULL; + write_unlock(&md->maplock); + dm_table_put(map); } /* @@ -710,16 +812,16 @@ void dm_put(struct mapped_device *md) } /* - * Requeue the deferred bios by calling generic_make_request. + * Process the deferred bios */ -static void flush_deferred_io(struct bio *c) +static void __flush_deferred_io(struct mapped_device *md, struct bio *c) { struct bio *n; while (c) { n = c->bi_next; c->bi_next = NULL; - generic_make_request(c); + __dm_request(md, c); c = n; } } @@ -814,10 +916,11 @@ int dm_resume(struct mapped_device *md) dm_table_resume_targets(md->map); clear_bit(DMF_SUSPENDED, &md->flags); clear_bit(DMF_BLOCK_IO, &md->flags); + def = bio_list_get(&md->deferred); + __flush_deferred_io(md, def); up_write(&md->lock); - flush_deferred_io(def); blk_run_queues(); return 0; --- linux-2.6.4-rc2/drivers/md/dm-crypt.c 2004-03-03 23:12:44.000000000 -0800 +++ 25/drivers/md/dm-crypt.c 2004-03-07 20:47:52.000000000 -0800 @@ -621,7 +621,8 @@ crypt_clone(struct crypt_config *cc, str return clone; } -static int crypt_map(struct dm_target *ti, struct bio *bio) +static int crypt_map(struct dm_target *ti, struct bio *bio, + union map_info *map_context) { struct crypt_config *cc = (struct crypt_config *) ti->private; struct crypt_io *io = mempool_alloc(cc->io_pool, GFP_NOIO); @@ -739,6 +740,7 @@ static int crypt_status(struct dm_target static struct target_type crypt_target = { .name = "crypt", + .version= {1, 0, 0}, .module = THIS_MODULE, .ctr = crypt_ctr, .dtr = crypt_dtr, --- linux-2.6.4-rc2/drivers/md/dm.h 2004-03-03 23:12:44.000000000 -0800 +++ 25/drivers/md/dm.h 2004-03-07 20:47:53.000000000 -0800 @@ -115,6 +115,7 @@ struct list_head *dm_table_get_devices(s int dm_table_get_mode(struct dm_table *t); void dm_table_suspend_targets(struct dm_table *t); void dm_table_resume_targets(struct dm_table *t); +int dm_table_any_congested(struct dm_table *t, int bdi_bits); /*----------------------------------------------------------------- * A registry of target types. @@ -123,6 +124,8 @@ int dm_target_init(void); void dm_target_exit(void); struct target_type *dm_get_target_type(const char *name); void dm_put_target_type(struct target_type *t); +int dm_target_iterate(void (*iter_func)(struct target_type *tt, + void *param), void *param); /*----------------------------------------------------------------- --- linux-2.6.4-rc2/drivers/md/dm-ioctl.c 2003-07-27 12:14:39.000000000 -0700 +++ 25/drivers/md/dm-ioctl.c 2004-03-07 20:47:52.000000000 -0800 @@ -1,13 +1,1343 @@ /* - * Copyright (C) 2003 Sistina Software (UK) Limited. + * Copyright (C) 2001, 2002 Sistina Software (UK) Limited. * * This file is released under the GPL. */ +#include "dm.h" + +#include +#include +#include +#include +#include +#include +#include #include -#ifdef CONFIG_DM_IOCTL_V4 -#include "dm-ioctl-v4.c" -#else -#include "dm-ioctl-v1.c" -#endif +#include + +#define DM_DRIVER_EMAIL "dm@uk.sistina.com" + +/*----------------------------------------------------------------- + * The ioctl interface needs to be able to look up devices by + * name or uuid. + *---------------------------------------------------------------*/ +struct hash_cell { + struct list_head name_list; + struct list_head uuid_list; + + char *name; + char *uuid; + struct mapped_device *md; + struct dm_table *new_map; +}; + +struct vers_iter { + size_t param_size; + struct dm_target_versions *vers, *old_vers; + char *end; + uint32_t flags; +}; + + +#define NUM_BUCKETS 64 +#define MASK_BUCKETS (NUM_BUCKETS - 1) +static struct list_head _name_buckets[NUM_BUCKETS]; +static struct list_head _uuid_buckets[NUM_BUCKETS]; + +void dm_hash_remove_all(void); + +/* + * Guards access to both hash tables. + */ +static DECLARE_RWSEM(_hash_lock); + +static void init_buckets(struct list_head *buckets) +{ + unsigned int i; + + for (i = 0; i < NUM_BUCKETS; i++) + INIT_LIST_HEAD(buckets + i); +} + +int dm_hash_init(void) +{ + init_buckets(_name_buckets); + init_buckets(_uuid_buckets); + devfs_mk_dir(DM_DIR); + return 0; +} + +void dm_hash_exit(void) +{ + dm_hash_remove_all(); + devfs_remove(DM_DIR); +} + +/*----------------------------------------------------------------- + * Hash function: + * We're not really concerned with the str hash function being + * fast since it's only used by the ioctl interface. + *---------------------------------------------------------------*/ +static unsigned int hash_str(const char *str) +{ + const unsigned int hash_mult = 2654435387U; + unsigned int h = 0; + + while (*str) + h = (h + (unsigned int) *str++) * hash_mult; + + return h & MASK_BUCKETS; +} + +/*----------------------------------------------------------------- + * Code for looking up a device by name + *---------------------------------------------------------------*/ +static struct hash_cell *__get_name_cell(const char *str) +{ + struct hash_cell *hc; + unsigned int h = hash_str(str); + + list_for_each_entry (hc, _name_buckets + h, name_list) + if (!strcmp(hc->name, str)) + return hc; + + return NULL; +} + +static struct hash_cell *__get_uuid_cell(const char *str) +{ + struct hash_cell *hc; + unsigned int h = hash_str(str); + + list_for_each_entry (hc, _uuid_buckets + h, uuid_list) + if (!strcmp(hc->uuid, str)) + return hc; + + return NULL; +} + +/*----------------------------------------------------------------- + * Inserting, removing and renaming a device. + *---------------------------------------------------------------*/ +static inline char *kstrdup(const char *str) +{ + char *r = kmalloc(strlen(str) + 1, GFP_KERNEL); + if (r) + strcpy(r, str); + return r; +} + +static struct hash_cell *alloc_cell(const char *name, const char *uuid, + struct mapped_device *md) +{ + struct hash_cell *hc; + + hc = kmalloc(sizeof(*hc), GFP_KERNEL); + if (!hc) + return NULL; + + hc->name = kstrdup(name); + if (!hc->name) { + kfree(hc); + return NULL; + } + + if (!uuid) + hc->uuid = NULL; + + else { + hc->uuid = kstrdup(uuid); + if (!hc->uuid) { + kfree(hc->name); + kfree(hc); + return NULL; + } + } + + INIT_LIST_HEAD(&hc->name_list); + INIT_LIST_HEAD(&hc->uuid_list); + hc->md = md; + hc->new_map = NULL; + return hc; +} + +static void free_cell(struct hash_cell *hc) +{ + if (hc) { + kfree(hc->name); + kfree(hc->uuid); + kfree(hc); + } +} + +/* + * devfs stuff. + */ +static int register_with_devfs(struct hash_cell *hc) +{ + struct gendisk *disk = dm_disk(hc->md); + + devfs_mk_bdev(MKDEV(disk->major, disk->first_minor), + S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP, + DM_DIR "/%s", hc->name); + return 0; +} + +static int unregister_with_devfs(struct hash_cell *hc) +{ + devfs_remove(DM_DIR"/%s", hc->name); + return 0; +} + +/* + * The kdev_t and uuid of a device can never change once it is + * initially inserted. + */ +int dm_hash_insert(const char *name, const char *uuid, struct mapped_device *md) +{ + struct hash_cell *cell; + + /* + * Allocate the new cells. + */ + cell = alloc_cell(name, uuid, md); + if (!cell) + return -ENOMEM; + + /* + * Insert the cell into both hash tables. + */ + down_write(&_hash_lock); + if (__get_name_cell(name)) + goto bad; + + list_add(&cell->name_list, _name_buckets + hash_str(name)); + + if (uuid) { + if (__get_uuid_cell(uuid)) { + list_del(&cell->name_list); + goto bad; + } + list_add(&cell->uuid_list, _uuid_buckets + hash_str(uuid)); + } + register_with_devfs(cell); + dm_get(md); + up_write(&_hash_lock); + + return 0; + + bad: + up_write(&_hash_lock); + free_cell(cell); + return -EBUSY; +} + +void __hash_remove(struct hash_cell *hc) +{ + /* remove from the dev hash */ + list_del(&hc->uuid_list); + list_del(&hc->name_list); + unregister_with_devfs(hc); + dm_put(hc->md); + if (hc->new_map) + dm_table_put(hc->new_map); + free_cell(hc); +} + +void dm_hash_remove_all(void) +{ + int i; + struct hash_cell *hc; + struct list_head *tmp, *n; + + down_write(&_hash_lock); + for (i = 0; i < NUM_BUCKETS; i++) { + list_for_each_safe (tmp, n, _name_buckets + i) { + hc = list_entry(tmp, struct hash_cell, name_list); + __hash_remove(hc); + } + } + up_write(&_hash_lock); +} + +int dm_hash_rename(const char *old, const char *new) +{ + char *new_name, *old_name; + struct hash_cell *hc; + + /* + * duplicate new. + */ + new_name = kstrdup(new); + if (!new_name) + return -ENOMEM; + + down_write(&_hash_lock); + + /* + * Is new free ? + */ + hc = __get_name_cell(new); + if (hc) { + DMWARN("asked to rename to an already existing name %s -> %s", + old, new); + up_write(&_hash_lock); + kfree(new_name); + return -EBUSY; + } + + /* + * Is there such a device as 'old' ? + */ + hc = __get_name_cell(old); + if (!hc) { + DMWARN("asked to rename a non existent device %s -> %s", + old, new); + up_write(&_hash_lock); + kfree(new_name); + return -ENXIO; + } + + /* + * rename and move the name cell. + */ + unregister_with_devfs(hc); + + list_del(&hc->name_list); + old_name = hc->name; + hc->name = new_name; + list_add(&hc->name_list, _name_buckets + hash_str(new_name)); + + /* rename the device node in devfs */ + register_with_devfs(hc); + + up_write(&_hash_lock); + kfree(old_name); + return 0; +} + +/*----------------------------------------------------------------- + * Implementation of the ioctl commands + *---------------------------------------------------------------*/ +/* + * All the ioctl commands get dispatched to functions with this + * prototype. + */ +typedef int (*ioctl_fn)(struct dm_ioctl *param, size_t param_size); + +static int remove_all(struct dm_ioctl *param, size_t param_size) +{ + dm_hash_remove_all(); + param->data_size = 0; + return 0; +} + +/* + * Round up the ptr to an 8-byte boundary. + */ +#define ALIGN_MASK 7 +static inline void *align_ptr(void *ptr) +{ + return (void *) (((size_t) (ptr + ALIGN_MASK)) & ~ALIGN_MASK); +} + +/* + * Retrieves the data payload buffer from an already allocated + * struct dm_ioctl. + */ +static void *get_result_buffer(struct dm_ioctl *param, size_t param_size, + size_t *len) +{ + param->data_start = align_ptr(param + 1) - (void *) param; + + if (param->data_start < param_size) + *len = param_size - param->data_start; + else + *len = 0; + + return ((void *) param) + param->data_start; +} + +static int list_devices(struct dm_ioctl *param, size_t param_size) +{ + unsigned int i; + struct hash_cell *hc; + size_t len, needed = 0; + struct gendisk *disk; + struct dm_name_list *nl, *old_nl = NULL; + + down_write(&_hash_lock); + + /* + * Loop through all the devices working out how much + * space we need. + */ + for (i = 0; i < NUM_BUCKETS; i++) { + list_for_each_entry (hc, _name_buckets + i, name_list) { + needed += sizeof(struct dm_name_list); + needed += strlen(hc->name); + needed += ALIGN_MASK; + } + } + + /* + * Grab our output buffer. + */ + nl = get_result_buffer(param, param_size, &len); + if (len < needed) { + param->flags |= DM_BUFFER_FULL_FLAG; + goto out; + } + param->data_size = param->data_start + needed; + + nl->dev = 0; /* Flags no data */ + + /* + * Now loop through filling out the names. + */ + for (i = 0; i < NUM_BUCKETS; i++) { + list_for_each_entry (hc, _name_buckets + i, name_list) { + if (old_nl) + old_nl->next = (uint32_t) ((void *) nl - + (void *) old_nl); + disk = dm_disk(hc->md); + nl->dev = huge_encode_dev(MKDEV(disk->major, disk->first_minor)); + nl->next = 0; + strcpy(nl->name, hc->name); + + old_nl = nl; + nl = align_ptr(((void *) ++nl) + strlen(hc->name) + 1); + } + } + + out: + up_write(&_hash_lock); + return 0; +} + +static void list_version_get_needed(struct target_type *tt, void *param) +{ + int *needed = param; + + *needed += strlen(tt->name); + *needed += sizeof(tt->version); + *needed += ALIGN_MASK; +} + +static void list_version_get_info(struct target_type *tt, void *param) +{ + struct vers_iter *info = param; + + /* Check space - it might have changed since the first iteration */ + if ((char *)info->vers + sizeof(tt->version) + strlen(tt->name) + 1 > + info->end) { + + info->flags = DM_BUFFER_FULL_FLAG; + return; + } + + if (info->old_vers) + info->old_vers->next = (uint32_t) ((void *)info->vers - + (void *)info->old_vers); + info->vers->version[0] = tt->version[0]; + info->vers->version[1] = tt->version[1]; + info->vers->version[2] = tt->version[2]; + info->vers->next = 0; + strcpy(info->vers->name, tt->name); + + info->old_vers = info->vers; + info->vers = align_ptr(((void *) ++info->vers) + strlen(tt->name) + 1); +} + +static int list_versions(struct dm_ioctl *param, size_t param_size) +{ + size_t len, needed = 0; + struct dm_target_versions *vers; + struct vers_iter iter_info; + + /* + * Loop through all the devices working out how much + * space we need. + */ + dm_target_iterate(list_version_get_needed, &needed); + + /* + * Grab our output buffer. + */ + vers = get_result_buffer(param, param_size, &len); + if (len < needed) { + param->flags |= DM_BUFFER_FULL_FLAG; + goto out; + } + param->data_size = param->data_start + needed; + + iter_info.param_size = param_size; + iter_info.old_vers = NULL; + iter_info.vers = vers; + iter_info.flags = 0; + iter_info.end = (char *)vers+len; + + /* + * Now loop through filling out the names & versions. + */ + dm_target_iterate(list_version_get_info, &iter_info); + param->flags |= iter_info.flags; + + out: + return 0; +} + + + +static int check_name(const char *name) +{ + if (strchr(name, '/')) { + DMWARN("invalid device name"); + return -EINVAL; + } + + return 0; +} + +/* + * Fills in a dm_ioctl structure, ready for sending back to + * userland. + */ +static int __dev_status(struct mapped_device *md, struct dm_ioctl *param) +{ + struct gendisk *disk = dm_disk(md); + struct dm_table *table; + struct block_device *bdev; + + param->flags &= ~(DM_SUSPEND_FLAG | DM_READONLY_FLAG | + DM_ACTIVE_PRESENT_FLAG); + + if (dm_suspended(md)) + param->flags |= DM_SUSPEND_FLAG; + + bdev = bdget_disk(disk, 0); + if (!bdev) + return -ENXIO; + + param->dev = huge_encode_dev(MKDEV(disk->major, disk->first_minor)); + + /* + * Yes, this will be out of date by the time it gets back + * to userland, but it is still very useful ofr + * debugging. + */ + param->open_count = bdev->bd_openers; + bdput(bdev); + + if (disk->policy) + param->flags |= DM_READONLY_FLAG; + + param->event_nr = dm_get_event_nr(md); + + table = dm_get_table(md); + if (table) { + param->flags |= DM_ACTIVE_PRESENT_FLAG; + param->target_count = dm_table_get_num_targets(table); + dm_table_put(table); + } else + param->target_count = 0; + + return 0; +} + +static int dev_create(struct dm_ioctl *param, size_t param_size) +{ + int r; + struct mapped_device *md; + + r = check_name(param->name); + if (r) + return r; + + if (param->flags & DM_PERSISTENT_DEV_FLAG) + r = dm_create_with_minor(MINOR(huge_decode_dev(param->dev)), &md); + else + r = dm_create(&md); + + if (r) + return r; + + r = dm_hash_insert(param->name, *param->uuid ? param->uuid : NULL, md); + if (r) { + dm_put(md); + return r; + } + + param->flags &= ~DM_INACTIVE_PRESENT_FLAG; + + r = __dev_status(md, param); + dm_put(md); + + return r; +} + +/* + * Always use UUID for lookups if it's present, otherwise use name. + */ +static inline struct hash_cell *__find_device_hash_cell(struct dm_ioctl *param) +{ + return *param->uuid ? + __get_uuid_cell(param->uuid) : __get_name_cell(param->name); +} + +static inline struct mapped_device *find_device(struct dm_ioctl *param) +{ + struct hash_cell *hc; + struct mapped_device *md = NULL; + + down_read(&_hash_lock); + hc = __find_device_hash_cell(param); + if (hc) { + md = hc->md; + + /* + * Sneakily write in both the name and the uuid + * while we have the cell. + */ + strncpy(param->name, hc->name, sizeof(param->name)); + if (hc->uuid) + strncpy(param->uuid, hc->uuid, sizeof(param->uuid)-1); + else + param->uuid[0] = '\0'; + + if (hc->new_map) + param->flags |= DM_INACTIVE_PRESENT_FLAG; + else + param->flags &= ~DM_INACTIVE_PRESENT_FLAG; + + dm_get(md); + } + up_read(&_hash_lock); + + return md; +} + +static int dev_remove(struct dm_ioctl *param, size_t param_size) +{ + struct hash_cell *hc; + + down_write(&_hash_lock); + hc = __find_device_hash_cell(param); + + if (!hc) { + DMWARN("device doesn't appear to be in the dev hash table."); + up_write(&_hash_lock); + return -ENXIO; + } + + __hash_remove(hc); + up_write(&_hash_lock); + param->data_size = 0; + return 0; +} + +/* + * Check a string doesn't overrun the chunk of + * memory we copied from userland. + */ +static int invalid_str(char *str, void *end) +{ + while ((void *) str < end) + if (!*str++) + return 0; + + return -EINVAL; +} + +static int dev_rename(struct dm_ioctl *param, size_t param_size) +{ + int r; + char *new_name = (char *) param + param->data_start; + + if (new_name < (char *) (param + 1) || + invalid_str(new_name, (void *) param + param_size)) { + DMWARN("Invalid new logical volume name supplied."); + return -EINVAL; + } + + r = check_name(new_name); + if (r) + return r; + + param->data_size = 0; + return dm_hash_rename(param->name, new_name); +} + +static int do_suspend(struct dm_ioctl *param) +{ + int r = 0; + struct mapped_device *md; + + md = find_device(param); + if (!md) + return -ENXIO; + + if (!dm_suspended(md)) + r = dm_suspend(md); + + if (!r) + r = __dev_status(md, param); + + dm_put(md); + return r; +} + +static int do_resume(struct dm_ioctl *param) +{ + int r = 0; + struct hash_cell *hc; + struct mapped_device *md; + struct dm_table *new_map; + + down_write(&_hash_lock); + + hc = __find_device_hash_cell(param); + if (!hc) { + DMWARN("device doesn't appear to be in the dev hash table."); + up_write(&_hash_lock); + return -ENXIO; + } + + md = hc->md; + dm_get(md); + + new_map = hc->new_map; + hc->new_map = NULL; + param->flags &= ~DM_INACTIVE_PRESENT_FLAG; + + up_write(&_hash_lock); + + /* Do we need to load a new map ? */ + if (new_map) { + /* Suspend if it isn't already suspended */ + if (!dm_suspended(md)) + dm_suspend(md); + + r = dm_swap_table(md, new_map); + if (r) { + dm_put(md); + dm_table_put(new_map); + return r; + } + + if (dm_table_get_mode(new_map) & FMODE_WRITE) + set_disk_ro(dm_disk(md), 0); + else + set_disk_ro(dm_disk(md), 1); + + dm_table_put(new_map); + } + + if (dm_suspended(md)) + r = dm_resume(md); + + if (!r) + r = __dev_status(md, param); + + dm_put(md); + return r; +} + +/* + * Set or unset the suspension state of a device. + * If the device already is in the requested state we just return its status. + */ +static int dev_suspend(struct dm_ioctl *param, size_t param_size) +{ + if (param->flags & DM_SUSPEND_FLAG) + return do_suspend(param); + + return do_resume(param); +} + +/* + * Copies device info back to user space, used by + * the create and info ioctls. + */ +static int dev_status(struct dm_ioctl *param, size_t param_size) +{ + int r; + struct mapped_device *md; + + md = find_device(param); + if (!md) + return -ENXIO; + + r = __dev_status(md, param); + dm_put(md); + return r; +} + +/* + * Build up the status struct for each target + */ +static void retrieve_status(struct dm_table *table, + struct dm_ioctl *param, size_t param_size) +{ + unsigned int i, num_targets; + struct dm_target_spec *spec; + char *outbuf, *outptr; + status_type_t type; + size_t remaining, len, used = 0; + + outptr = outbuf = get_result_buffer(param, param_size, &len); + + if (param->flags & DM_STATUS_TABLE_FLAG) + type = STATUSTYPE_TABLE; + else + type = STATUSTYPE_INFO; + + /* Get all the target info */ + num_targets = dm_table_get_num_targets(table); + for (i = 0; i < num_targets; i++) { + struct dm_target *ti = dm_table_get_target(table, i); + + remaining = len - (outptr - outbuf); + if (remaining < sizeof(struct dm_target_spec)) { + param->flags |= DM_BUFFER_FULL_FLAG; + break; + } + + spec = (struct dm_target_spec *) outptr; + + spec->status = 0; + spec->sector_start = ti->begin; + spec->length = ti->len; + strncpy(spec->target_type, ti->type->name, + sizeof(spec->target_type)); + + outptr += sizeof(struct dm_target_spec); + remaining = len - (outptr - outbuf); + + /* Get the status/table string from the target driver */ + if (ti->type->status) { + if (ti->type->status(ti, type, outptr, remaining)) { + param->flags |= DM_BUFFER_FULL_FLAG; + break; + } + } else + outptr[0] = '\0'; + + outptr += strlen(outptr) + 1; + used = param->data_start + (outptr - outbuf); + + align_ptr(outptr); + spec->next = outptr - outbuf; + } + + if (used) + param->data_size = used; + + param->target_count = num_targets; +} + +/* + * Wait for a device to report an event + */ +static int dev_wait(struct dm_ioctl *param, size_t param_size) +{ + int r; + struct mapped_device *md; + struct dm_table *table; + DECLARE_WAITQUEUE(wq, current); + + md = find_device(param); + if (!md) + return -ENXIO; + + /* + * Wait for a notification event + */ + set_current_state(TASK_INTERRUPTIBLE); + if (!dm_add_wait_queue(md, &wq, param->event_nr)) { + schedule(); + dm_remove_wait_queue(md, &wq); + } + set_current_state(TASK_RUNNING); + + /* + * The userland program is going to want to know what + * changed to trigger the event, so we may as well tell + * him and save an ioctl. + */ + r = __dev_status(md, param); + if (r) + goto out; + + table = dm_get_table(md); + if (table) { + retrieve_status(table, param, param_size); + dm_table_put(table); + } + + out: + dm_put(md); + return r; +} + +static inline int get_mode(struct dm_ioctl *param) +{ + int mode = FMODE_READ | FMODE_WRITE; + + if (param->flags & DM_READONLY_FLAG) + mode = FMODE_READ; + + return mode; +} + +static int next_target(struct dm_target_spec *last, uint32_t next, void *end, + struct dm_target_spec **spec, char **target_params) +{ + *spec = (struct dm_target_spec *) ((unsigned char *) last + next); + *target_params = (char *) (*spec + 1); + + if (*spec < (last + 1)) + return -EINVAL; + + return invalid_str(*target_params, end); +} + +static int populate_table(struct dm_table *table, + struct dm_ioctl *param, size_t param_size) +{ + int r; + unsigned int i = 0; + struct dm_target_spec *spec = (struct dm_target_spec *) param; + uint32_t next = param->data_start; + void *end = (void *) param + param_size; + char *target_params; + + if (!param->target_count) { + DMWARN("populate_table: no targets specified"); + return -EINVAL; + } + + for (i = 0; i < param->target_count; i++) { + + r = next_target(spec, next, end, &spec, &target_params); + if (r) { + DMWARN("unable to find target"); + return r; + } + + r = dm_table_add_target(table, spec->target_type, + (sector_t) spec->sector_start, + (sector_t) spec->length, + target_params); + if (r) { + DMWARN("error adding target to table"); + return r; + } + + next = spec->next; + } + + return dm_table_complete(table); +} + +static int table_load(struct dm_ioctl *param, size_t param_size) +{ + int r; + struct hash_cell *hc; + struct dm_table *t; + + r = dm_table_create(&t, get_mode(param), param->target_count); + if (r) + return r; + + r = populate_table(t, param, param_size); + if (r) { + dm_table_put(t); + return r; + } + + down_write(&_hash_lock); + hc = __find_device_hash_cell(param); + if (!hc) { + DMWARN("device doesn't appear to be in the dev hash table."); + up_write(&_hash_lock); + return -ENXIO; + } + + if (hc->new_map) + dm_table_put(hc->new_map); + hc->new_map = t; + param->flags |= DM_INACTIVE_PRESENT_FLAG; + + r = __dev_status(hc->md, param); + up_write(&_hash_lock); + return r; +} + +static int table_clear(struct dm_ioctl *param, size_t param_size) +{ + int r; + struct hash_cell *hc; + + down_write(&_hash_lock); + + hc = __find_device_hash_cell(param); + if (!hc) { + DMWARN("device doesn't appear to be in the dev hash table."); + up_write(&_hash_lock); + return -ENXIO; + } + + if (hc->new_map) { + dm_table_put(hc->new_map); + hc->new_map = NULL; + } + + param->flags &= ~DM_INACTIVE_PRESENT_FLAG; + + r = __dev_status(hc->md, param); + up_write(&_hash_lock); + return r; +} + +/* + * Retrieves a list of devices used by a particular dm device. + */ +static void retrieve_deps(struct dm_table *table, + struct dm_ioctl *param, size_t param_size) +{ + unsigned int count = 0; + struct list_head *tmp; + size_t len, needed; + struct dm_dev *dd; + struct dm_target_deps *deps; + + deps = get_result_buffer(param, param_size, &len); + + /* + * Count the devices. + */ + list_for_each (tmp, dm_table_get_devices(table)) + count++; + + /* + * Check we have enough space. + */ + needed = sizeof(*deps) + (sizeof(*deps->dev) * count); + if (len < needed) { + param->flags |= DM_BUFFER_FULL_FLAG; + return; + } + + /* + * Fill in the devices. + */ + deps->count = count; + count = 0; + list_for_each_entry (dd, dm_table_get_devices(table), list) + deps->dev[count++] = huge_encode_dev(dd->bdev->bd_dev); + + param->data_size = param->data_start + needed; +} + +static int table_deps(struct dm_ioctl *param, size_t param_size) +{ + int r = 0; + struct mapped_device *md; + struct dm_table *table; + + md = find_device(param); + if (!md) + return -ENXIO; + + r = __dev_status(md, param); + if (r) + goto out; + + table = dm_get_table(md); + if (table) { + retrieve_deps(table, param, param_size); + dm_table_put(table); + } + + out: + dm_put(md); + return r; +} + +/* + * Return the status of a device as a text string for each + * target. + */ +static int table_status(struct dm_ioctl *param, size_t param_size) +{ + int r; + struct mapped_device *md; + struct dm_table *table; + + md = find_device(param); + if (!md) + return -ENXIO; + + r = __dev_status(md, param); + if (r) + goto out; + + table = dm_get_table(md); + if (table) { + retrieve_status(table, param, param_size); + dm_table_put(table); + } + + out: + dm_put(md); + return r; +} + +/*----------------------------------------------------------------- + * Implementation of open/close/ioctl on the special char + * device. + *---------------------------------------------------------------*/ +static ioctl_fn lookup_ioctl(unsigned int cmd) +{ + static struct { + int cmd; + ioctl_fn fn; + } _ioctls[] = { + {DM_VERSION_CMD, NULL}, /* version is dealt with elsewhere */ + {DM_REMOVE_ALL_CMD, remove_all}, + {DM_LIST_DEVICES_CMD, list_devices}, + + {DM_DEV_CREATE_CMD, dev_create}, + {DM_DEV_REMOVE_CMD, dev_remove}, + {DM_DEV_RENAME_CMD, dev_rename}, + {DM_DEV_SUSPEND_CMD, dev_suspend}, + {DM_DEV_STATUS_CMD, dev_status}, + {DM_DEV_WAIT_CMD, dev_wait}, + + {DM_TABLE_LOAD_CMD, table_load}, + {DM_TABLE_CLEAR_CMD, table_clear}, + {DM_TABLE_DEPS_CMD, table_deps}, + {DM_TABLE_STATUS_CMD, table_status}, + + {DM_LIST_VERSIONS_CMD, list_versions} + }; + + return (cmd >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[cmd].fn; +} + +/* + * As well as checking the version compatibility this always + * copies the kernel interface version out. + */ +static int check_version(unsigned int cmd, struct dm_ioctl *user) +{ + uint32_t version[3]; + int r = 0; + + if (copy_from_user(version, user->version, sizeof(version))) + return -EFAULT; + + if ((DM_VERSION_MAJOR != version[0]) || + (DM_VERSION_MINOR < version[1])) { + DMWARN("ioctl interface mismatch: " + "kernel(%u.%u.%u), user(%u.%u.%u), cmd(%d)", + DM_VERSION_MAJOR, DM_VERSION_MINOR, + DM_VERSION_PATCHLEVEL, + version[0], version[1], version[2], cmd); + r = -EINVAL; + } + + /* + * Fill in the kernel version. + */ + version[0] = DM_VERSION_MAJOR; + version[1] = DM_VERSION_MINOR; + version[2] = DM_VERSION_PATCHLEVEL; + if (copy_to_user(user->version, version, sizeof(version))) + return -EFAULT; + + return r; +} + +static void free_params(struct dm_ioctl *param) +{ + vfree(param); +} + +static int copy_params(struct dm_ioctl *user, struct dm_ioctl **param) +{ + struct dm_ioctl tmp, *dmi; + + if (copy_from_user(&tmp, user, sizeof(tmp))) + return -EFAULT; + + if (tmp.data_size < sizeof(tmp)) + return -EINVAL; + + dmi = (struct dm_ioctl *) vmalloc(tmp.data_size); + if (!dmi) + return -ENOMEM; + + if (copy_from_user(dmi, user, tmp.data_size)) { + vfree(dmi); + return -EFAULT; + } + + *param = dmi; + return 0; +} + +static int validate_params(uint cmd, struct dm_ioctl *param) +{ + /* Always clear this flag */ + param->flags &= ~DM_BUFFER_FULL_FLAG; + + /* Ignores parameters */ + if (cmd == DM_REMOVE_ALL_CMD || + cmd == DM_LIST_DEVICES_CMD || + cmd == DM_LIST_VERSIONS_CMD) + return 0; + + /* Unless creating, either name or uuid but not both */ + if (cmd != DM_DEV_CREATE_CMD) { + if ((!*param->uuid && !*param->name) || + (*param->uuid && *param->name)) { + DMWARN("one of name or uuid must be supplied, cmd(%u)", + cmd); + return -EINVAL; + } + } + + /* Ensure strings are terminated */ + param->name[DM_NAME_LEN - 1] = '\0'; + param->uuid[DM_UUID_LEN - 1] = '\0'; + + return 0; +} + +static int ctl_ioctl(struct inode *inode, struct file *file, + uint command, ulong u) +{ + int r = 0; + unsigned int cmd; + struct dm_ioctl *param; + struct dm_ioctl *user = (struct dm_ioctl *) u; + ioctl_fn fn = NULL; + size_t param_size; + + /* only root can play with this */ + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + if (_IOC_TYPE(command) != DM_IOCTL) + return -ENOTTY; + + cmd = _IOC_NR(command); + + /* + * Check the interface version passed in. This also + * writes out the kernel's interface version. + */ + r = check_version(cmd, user); + if (r) + return r; + + /* + * Nothing more to do for the version command. + */ + if (cmd == DM_VERSION_CMD) + return 0; + + fn = lookup_ioctl(cmd); + if (!fn) { + DMWARN("dm_ctl_ioctl: unknown command 0x%x", command); + return -ENOTTY; + } + + /* + * Trying to avoid low memory issues when a device is + * suspended. + */ + current->flags |= PF_MEMALLOC; + + /* + * Copy the parameters into kernel space. + */ + r = copy_params(user, ¶m); + if (r) { + current->flags &= ~PF_MEMALLOC; + return r; + } + + /* + * FIXME: eventually we will remove the PF_MEMALLOC flag + * here. However the tools still do nasty things like + * 'load' while a device is suspended. + */ + + r = validate_params(cmd, param); + if (r) + goto out; + + param_size = param->data_size; + param->data_size = sizeof(*param); + r = fn(param, param_size); + + /* + * Copy the results back to userland. + */ + if (!r && copy_to_user(user, param, param->data_size)) + r = -EFAULT; + + out: + free_params(param); + current->flags &= ~PF_MEMALLOC; + return r; +} + +static struct file_operations _ctl_fops = { + .ioctl = ctl_ioctl, + .owner = THIS_MODULE, +}; + +static struct miscdevice _dm_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = DM_NAME, + .devfs_name = "mapper/control", + .fops = &_ctl_fops +}; + +/* + * Create misc character device and link to DM_DIR/control. + */ +int __init dm_interface_init(void) +{ + int r; + + r = dm_hash_init(); + if (r) + return r; + + r = misc_register(&_dm_misc); + if (r) { + DMERR("misc_register failed for control device"); + dm_hash_exit(); + return r; + } + + DMINFO("%d.%d.%d%s initialised: %s", DM_VERSION_MAJOR, + DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL, DM_VERSION_EXTRA, + DM_DRIVER_EMAIL); + return 0; +} + +void dm_interface_exit(void) +{ + if (misc_deregister(&_dm_misc) < 0) + DMERR("misc_deregister failed for control device"); + + dm_hash_exit(); +} --- linux-2.6.4-rc2/drivers/md/dm-ioctl-v1.c 2004-01-09 00:04:31.000000000 -0800 +++ /dev/null 2002-08-30 16:31:37.000000000 -0700 @@ -1,1159 +0,0 @@ -/* - * Copyright (C) 2001, 2002 Sistina Software (UK) Limited. - * - * This file is released under the GPL. - */ - -#include "dm.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define DM_DRIVER_EMAIL "dm@uk.sistina.com" - -/*----------------------------------------------------------------- - * The ioctl interface needs to be able to look up devices by - * name or uuid. - *---------------------------------------------------------------*/ -struct hash_cell { - struct list_head name_list; - struct list_head uuid_list; - - char *name; - char *uuid; - struct mapped_device *md; -}; - -#define NUM_BUCKETS 64 -#define MASK_BUCKETS (NUM_BUCKETS - 1) -static struct list_head _name_buckets[NUM_BUCKETS]; -static struct list_head _uuid_buckets[NUM_BUCKETS]; - -void dm_hash_remove_all(void); - -/* - * Guards access to all three tables. - */ -static DECLARE_RWSEM(_hash_lock); - -static void init_buckets(struct list_head *buckets) -{ - unsigned int i; - - for (i = 0; i < NUM_BUCKETS; i++) - INIT_LIST_HEAD(buckets + i); -} - -int dm_hash_init(void) -{ - init_buckets(_name_buckets); - init_buckets(_uuid_buckets); - devfs_mk_dir(DM_DIR); - return 0; -} - -void dm_hash_exit(void) -{ - dm_hash_remove_all(); - devfs_remove(DM_DIR); -} - -/*----------------------------------------------------------------- - * Hash function: - * We're not really concerned with the str hash function being - * fast since it's only used by the ioctl interface. - *---------------------------------------------------------------*/ -static unsigned int hash_str(const char *str) -{ - const unsigned int hash_mult = 2654435387U; - unsigned int h = 0; - - while (*str) - h = (h + (unsigned int) *str++) * hash_mult; - - return h & MASK_BUCKETS; -} - -/*----------------------------------------------------------------- - * Code for looking up a device by name - *---------------------------------------------------------------*/ -static struct hash_cell *__get_name_cell(const char *str) -{ - struct list_head *tmp; - struct hash_cell *hc; - unsigned int h = hash_str(str); - - list_for_each (tmp, _name_buckets + h) { - hc = list_entry(tmp, struct hash_cell, name_list); - if (!strcmp(hc->name, str)) - return hc; - } - - return NULL; -} - -static struct hash_cell *__get_uuid_cell(const char *str) -{ - struct list_head *tmp; - struct hash_cell *hc; - unsigned int h = hash_str(str); - - list_for_each (tmp, _uuid_buckets + h) { - hc = list_entry(tmp, struct hash_cell, uuid_list); - if (!strcmp(hc->uuid, str)) - return hc; - } - - return NULL; -} - -/*----------------------------------------------------------------- - * Inserting, removing and renaming a device. - *---------------------------------------------------------------*/ -static inline char *kstrdup(const char *str) -{ - char *r = kmalloc(strlen(str) + 1, GFP_KERNEL); - if (r) - strcpy(r, str); - return r; -} - -static struct hash_cell *alloc_cell(const char *name, const char *uuid, - struct mapped_device *md) -{ - struct hash_cell *hc; - - hc = kmalloc(sizeof(*hc), GFP_KERNEL); - if (!hc) - return NULL; - - hc->name = kstrdup(name); - if (!hc->name) { - kfree(hc); - return NULL; - } - - if (!uuid) - hc->uuid = NULL; - - else { - hc->uuid = kstrdup(uuid); - if (!hc->uuid) { - kfree(hc->name); - kfree(hc); - return NULL; - } - } - - INIT_LIST_HEAD(&hc->name_list); - INIT_LIST_HEAD(&hc->uuid_list); - hc->md = md; - return hc; -} - -static void free_cell(struct hash_cell *hc) -{ - if (hc) { - kfree(hc->name); - kfree(hc->uuid); - kfree(hc); - } -} - -/* - * devfs stuff. - */ -static int register_with_devfs(struct hash_cell *hc) -{ - struct gendisk *disk = dm_disk(hc->md); - - devfs_mk_bdev(MKDEV(disk->major, disk->first_minor), - S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP, - DM_DIR "/%s", hc->name); - return 0; -} - -static int unregister_with_devfs(struct hash_cell *hc) -{ - devfs_remove(DM_DIR"/%s", hc->name); - return 0; -} - -/* - * The kdev_t and uuid of a device can never change once it is - * initially inserted. - */ -int dm_hash_insert(const char *name, const char *uuid, struct mapped_device *md) -{ - struct hash_cell *cell; - - /* - * Allocate the new cells. - */ - cell = alloc_cell(name, uuid, md); - if (!cell) - return -ENOMEM; - - /* - * Insert the cell into all three hash tables. - */ - down_write(&_hash_lock); - if (__get_name_cell(name)) - goto bad; - - list_add(&cell->name_list, _name_buckets + hash_str(name)); - - if (uuid) { - if (__get_uuid_cell(uuid)) { - list_del(&cell->name_list); - goto bad; - } - list_add(&cell->uuid_list, _uuid_buckets + hash_str(uuid)); - } - register_with_devfs(cell); - dm_get(md); - up_write(&_hash_lock); - - return 0; - - bad: - up_write(&_hash_lock); - free_cell(cell); - return -EBUSY; -} - -void __hash_remove(struct hash_cell *hc) -{ - /* remove from the dev hash */ - list_del(&hc->uuid_list); - list_del(&hc->name_list); - unregister_with_devfs(hc); - dm_put(hc->md); - free_cell(hc); -} - -void dm_hash_remove_all(void) -{ - int i; - struct hash_cell *hc; - struct list_head *tmp, *n; - - down_write(&_hash_lock); - for (i = 0; i < NUM_BUCKETS; i++) { - list_for_each_safe (tmp, n, _name_buckets + i) { - hc = list_entry(tmp, struct hash_cell, name_list); - __hash_remove(hc); - } - } - up_write(&_hash_lock); -} - -int dm_hash_rename(const char *old, const char *new) -{ - char *new_name, *old_name; - struct hash_cell *hc; - - /* - * duplicate new. - */ - new_name = kstrdup(new); - if (!new_name) - return -ENOMEM; - - down_write(&_hash_lock); - - /* - * Is new free ? - */ - hc = __get_name_cell(new); - if (hc) { - DMWARN("asked to rename to an already existing name %s -> %s", - old, new); - up_write(&_hash_lock); - kfree(new_name); - return -EBUSY; - } - - /* - * Is there such a device as 'old' ? - */ - hc = __get_name_cell(old); - if (!hc) { - DMWARN("asked to rename a non existent device %s -> %s", - old, new); - up_write(&_hash_lock); - kfree(new_name); - return -ENXIO; - } - - /* - * rename and move the name cell. - */ - unregister_with_devfs(hc); - - list_del(&hc->name_list); - old_name = hc->name; - hc->name = new_name; - list_add(&hc->name_list, _name_buckets + hash_str(new_name)); - - /* rename the device node in devfs */ - register_with_devfs(hc); - - up_write(&_hash_lock); - kfree(old_name); - return 0; -} - - -/*----------------------------------------------------------------- - * Implementation of the ioctl commands - *---------------------------------------------------------------*/ - -/* - * All the ioctl commands get dispatched to functions with this - * prototype. - */ -typedef int (*ioctl_fn)(struct dm_ioctl *param, struct dm_ioctl *user); - -/* - * Check a string doesn't overrun the chunk of - * memory we copied from userland. - */ -static int valid_str(char *str, void *begin, void *end) -{ - while (((void *) str >= begin) && ((void *) str < end)) - if (!*str++) - return 0; - - return -EINVAL; -} - -static int next_target(struct dm_target_spec *last, uint32_t next, - void *begin, void *end, - struct dm_target_spec **spec, char **params) -{ - *spec = (struct dm_target_spec *) - ((unsigned char *) last + next); - *params = (char *) (*spec + 1); - - if (*spec < (last + 1) || ((void *) *spec > end)) - return -EINVAL; - - return valid_str(*params, begin, end); -} - -static int populate_table(struct dm_table *table, struct dm_ioctl *args) -{ - int r, first = 1; - unsigned int i = 0; - struct dm_target_spec *spec; - char *params; - void *begin, *end; - - if (!args->target_count) { - DMWARN("populate_table: no targets specified"); - return -EINVAL; - } - - begin = (void *) args; - end = begin + args->data_size; - - for (i = 0; i < args->target_count; i++) { - - if (first) - r = next_target((struct dm_target_spec *) args, - args->data_start, - begin, end, &spec, ¶ms); - else - r = next_target(spec, spec->next, begin, end, - &spec, ¶ms); - - if (r) { - DMWARN("unable to find target"); - return -EINVAL; - } - - r = dm_table_add_target(table, spec->target_type, - (sector_t) spec->sector_start, - (sector_t) spec->length, - params); - if (r) { - DMWARN("internal error adding target to table"); - return -EINVAL; - } - - first = 0; - } - - return dm_table_complete(table); -} - -/* - * Round up the ptr to the next 'align' boundary. Obviously - * 'align' must be a power of 2. - */ -static inline void *align_ptr(void *ptr, unsigned int align) -{ - align--; - return (void *) (((unsigned long) (ptr + align)) & ~align); -} - -/* - * Copies a dm_ioctl and an optional additional payload to - * userland. - */ -static int results_to_user(struct dm_ioctl *user, struct dm_ioctl *param, - void *data, uint32_t len) -{ - int r; - void *ptr = NULL; - - if (data) { - ptr = align_ptr(user + 1, sizeof(unsigned long)); - param->data_start = ptr - (void *) user; - } - - /* - * The version number has already been filled in, so we - * just copy later fields. - */ - r = copy_to_user(&user->data_size, ¶m->data_size, - sizeof(*param) - sizeof(param->version)); - if (r) - return -EFAULT; - - if (data) { - if (param->data_start + len > param->data_size) - return -ENOSPC; - - if (copy_to_user(ptr, data, len)) - r = -EFAULT; - } - - return r; -} - -/* - * Fills in a dm_ioctl structure, ready for sending back to - * userland. - */ -static int __info(struct mapped_device *md, struct dm_ioctl *param) -{ - struct dm_table *table; - struct block_device *bdev; - struct gendisk *disk = dm_disk(md); - - param->flags = DM_EXISTS_FLAG; - if (dm_suspended(md)) - param->flags |= DM_SUSPEND_FLAG; - - bdev = bdget_disk(disk, 0); - if (!bdev) - return -ENXIO; - - param->dev = old_encode_dev(bdev->bd_dev); - param->open_count = bdev->bd_openers; - bdput(bdev); - - if (disk->policy) - param->flags |= DM_READONLY_FLAG; - - table = dm_get_table(md); - param->target_count = dm_table_get_num_targets(table); - dm_table_put(table); - - return 0; -} - -/* - * Always use UUID for lookups if it's present, otherwise use name. - */ -static inline struct mapped_device *find_device(struct dm_ioctl *param) -{ - struct hash_cell *hc; - struct mapped_device *md = NULL; - - down_read(&_hash_lock); - hc = *param->uuid ? __get_uuid_cell(param->uuid) : - __get_name_cell(param->name); - if (hc) { - md = hc->md; - - /* - * Sneakily write in both the name and the uuid - * while we have the cell. - */ - strlcpy(param->name, hc->name, sizeof(param->name)); - if (hc->uuid) - strlcpy(param->uuid, hc->uuid, sizeof(param->uuid)); - else - param->uuid[0] = '\0'; - - dm_get(md); - } - up_read(&_hash_lock); - - return md; -} - -#define ALIGNMENT sizeof(int) -static void *_align(void *ptr, unsigned int a) -{ - register unsigned long align = --a; - - return (void *) (((unsigned long) ptr + align) & ~align); -} - -/* - * Copies device info back to user space, used by - * the create and info ioctls. - */ -static int info(struct dm_ioctl *param, struct dm_ioctl *user) -{ - struct mapped_device *md; - - param->flags = 0; - - md = find_device(param); - if (!md) - /* - * Device not found - returns cleared exists flag. - */ - goto out; - - __info(md, param); - dm_put(md); - - out: - return results_to_user(user, param, NULL, 0); -} - -static inline int get_mode(struct dm_ioctl *param) -{ - int mode = FMODE_READ | FMODE_WRITE; - - if (param->flags & DM_READONLY_FLAG) - mode = FMODE_READ; - - return mode; -} - -static int check_name(const char *name) -{ - if (name[0] == '/') { - DMWARN("invalid device name"); - return -EINVAL; - } - - return 0; -} - -static int create(struct dm_ioctl *param, struct dm_ioctl *user) -{ - int r; - struct dm_table *t; - struct mapped_device *md; - - r = check_name(param->name); - if (r) - return r; - - r = dm_table_create(&t, get_mode(param), param->target_count); - if (r) - return r; - - r = populate_table(t, param); - if (r) { - dm_table_put(t); - return r; - } - - if (param->flags & DM_PERSISTENT_DEV_FLAG) - r = dm_create_with_minor(MINOR(old_decode_dev(param->dev)), &md); - else - r = dm_create(&md); - - if (r) { - dm_table_put(t); - return r; - } - - /* suspend the device */ - r = dm_suspend(md); - if (r) { - DMWARN("suspend failed"); - dm_table_put(t); - dm_put(md); - return r; - } - /* swap in the table */ - r = dm_swap_table(md, t); - if (r) { - DMWARN("table swap failed"); - dm_table_put(t); - dm_put(md); - return r; - } - - /* resume the device */ - r = dm_resume(md); - if (r) { - DMWARN("resume failed"); - dm_table_put(t); - dm_put(md); - return r; - } - - dm_table_put(t); /* md will have grabbed its own reference */ - - set_disk_ro(dm_disk(md), (param->flags & DM_READONLY_FLAG) ? 1 : 0); - r = dm_hash_insert(param->name, *param->uuid ? param->uuid : NULL, md); - dm_put(md); - - return r ? r : info(param, user); -} - -/* - * Build up the status struct for each target - */ -static int __status(struct mapped_device *md, struct dm_ioctl *param, - char *outbuf, size_t *len) -{ - unsigned int i, num_targets; - struct dm_target_spec *spec; - char *outptr; - status_type_t type; - struct dm_table *table = dm_get_table(md); - - if (param->flags & DM_STATUS_TABLE_FLAG) - type = STATUSTYPE_TABLE; - else - type = STATUSTYPE_INFO; - - outptr = outbuf; - - /* Get all the target info */ - num_targets = dm_table_get_num_targets(table); - for (i = 0; i < num_targets; i++) { - struct dm_target *ti = dm_table_get_target(table, i); - - if (outptr - outbuf + - sizeof(struct dm_target_spec) > param->data_size) { - dm_table_put(table); - return -ENOMEM; - } - - spec = (struct dm_target_spec *) outptr; - - spec->status = 0; - spec->sector_start = ti->begin; - spec->length = ti->len; - strlcpy(spec->target_type, ti->type->name, - sizeof(spec->target_type)); - - outptr += sizeof(struct dm_target_spec); - - /* Get the status/table string from the target driver */ - if (ti->type->status) - ti->type->status(ti, type, outptr, - outbuf + param->data_size - outptr); - else - outptr[0] = '\0'; - - outptr += strlen(outptr) + 1; - _align(outptr, ALIGNMENT); - spec->next = outptr - outbuf; - } - - param->target_count = num_targets; - *len = outptr - outbuf; - dm_table_put(table); - - return 0; -} - -/* - * Return the status of a device as a text string for each - * target. - */ -static int get_status(struct dm_ioctl *param, struct dm_ioctl *user) -{ - struct mapped_device *md; - size_t len = 0; - int ret; - char *outbuf = NULL; - - md = find_device(param); - if (!md) - /* - * Device not found - returns cleared exists flag. - */ - goto out; - - /* We haven't a clue how long the resultant data will be so - just allocate as much as userland has allowed us and make sure - we don't overun it */ - outbuf = kmalloc(param->data_size, GFP_KERNEL); - if (!outbuf) - goto out; - /* - * Get the status of all targets - */ - __status(md, param, outbuf, &len); - - /* - * Setup the basic dm_ioctl structure. - */ - __info(md, param); - - out: - if (md) - dm_put(md); - - ret = results_to_user(user, param, outbuf, len); - - if (outbuf) - kfree(outbuf); - - return ret; -} - -/* - * Wait for a device to report an event - */ -static int wait_device_event(struct dm_ioctl *param, struct dm_ioctl *user) -{ - struct mapped_device *md; - DECLARE_WAITQUEUE(wq, current); - - md = find_device(param); - if (!md) - /* - * Device not found - returns cleared exists flag. - */ - goto out; - - /* - * Setup the basic dm_ioctl structure. - */ - __info(md, param); - - /* - * Wait for a notification event - */ - set_current_state(TASK_INTERRUPTIBLE); - if (!dm_add_wait_queue(md, &wq, dm_get_event_nr(md))) { - schedule(); - dm_remove_wait_queue(md, &wq); - } - set_current_state(TASK_RUNNING); - dm_put(md); - - out: - return results_to_user(user, param, NULL, 0); -} - -/* - * Retrieves a list of devices used by a particular dm device. - */ -static int dep(struct dm_ioctl *param, struct dm_ioctl *user) -{ - int r; - unsigned int count; - struct mapped_device *md; - struct list_head *tmp; - size_t len = 0; - struct dm_target_deps *deps = NULL; - struct dm_table *table; - - md = find_device(param); - if (!md) - goto out; - table = dm_get_table(md); - - /* - * Setup the basic dm_ioctl structure. - */ - __info(md, param); - - /* - * Count the devices. - */ - count = 0; - list_for_each(tmp, dm_table_get_devices(table)) - count++; - - /* - * Allocate a kernel space version of the dm_target_status - * struct. - */ - if (array_too_big(sizeof(*deps), sizeof(*deps->dev), count)) { - dm_table_put(table); - dm_put(md); - return -ENOMEM; - } - - len = sizeof(*deps) + (sizeof(*deps->dev) * count); - deps = kmalloc(len, GFP_KERNEL); - if (!deps) { - dm_table_put(table); - dm_put(md); - return -ENOMEM; - } - - /* - * Fill in the devices. - */ - deps->count = count; - count = 0; - list_for_each(tmp, dm_table_get_devices(table)) { - struct dm_dev *dd = list_entry(tmp, struct dm_dev, list); - deps->dev[count++] = old_encode_dev(dd->bdev->bd_dev); - } - dm_table_put(table); - dm_put(md); - - out: - r = results_to_user(user, param, deps, len); - - kfree(deps); - return r; -} - -static int remove(struct dm_ioctl *param, struct dm_ioctl *user) -{ - struct hash_cell *hc; - - down_write(&_hash_lock); - hc = *param->uuid ? __get_uuid_cell(param->uuid) : - __get_name_cell(param->name); - if (!hc) { - DMWARN("device doesn't appear to be in the dev hash table."); - up_write(&_hash_lock); - return -EINVAL; - } - - /* - * You may ask the interface to drop its reference to an - * in use device. This is no different to unlinking a - * file that someone still has open. The device will not - * actually be destroyed until the last opener closes it. - * The name and uuid of the device (both are interface - * properties) will be available for reuse immediately. - * - * You don't want to drop a _suspended_ device from the - * interface, since that will leave you with no way of - * resuming it. - */ - if (dm_suspended(hc->md)) { - DMWARN("refusing to remove a suspended device."); - up_write(&_hash_lock); - return -EPERM; - } - - __hash_remove(hc); - up_write(&_hash_lock); - return 0; -} - -static int remove_all(struct dm_ioctl *param, struct dm_ioctl *user) -{ - dm_hash_remove_all(); - return 0; -} - -static int suspend(struct dm_ioctl *param, struct dm_ioctl *user) -{ - int r; - struct mapped_device *md; - - md = find_device(param); - if (!md) - return -ENXIO; - - if (param->flags & DM_SUSPEND_FLAG) - r = dm_suspend(md); - else - r = dm_resume(md); - - dm_put(md); - return r; -} - -static int reload(struct dm_ioctl *param, struct dm_ioctl *user) -{ - int r; - struct mapped_device *md; - struct dm_table *t; - - r = dm_table_create(&t, get_mode(param), param->target_count); - if (r) - return r; - - r = populate_table(t, param); - if (r) { - dm_table_put(t); - return r; - } - - md = find_device(param); - if (!md) { - dm_table_put(t); - return -ENXIO; - } - - r = dm_swap_table(md, t); - if (r) { - dm_put(md); - dm_table_put(t); - return r; - } - dm_table_put(t); /* md will have taken its own reference */ - - set_disk_ro(dm_disk(md), (param->flags & DM_READONLY_FLAG) ? 1 : 0); - dm_put(md); - - r = info(param, user); - return r; -} - -static int rename(struct dm_ioctl *param, struct dm_ioctl *user) -{ - int r; - char *new_name = (char *) param + param->data_start; - - if (valid_str(new_name, (void *) param, - (void *) param + param->data_size)) { - DMWARN("Invalid new logical volume name supplied."); - return -EINVAL; - } - - r = check_name(new_name); - if (r) - return r; - - return dm_hash_rename(param->name, new_name); -} - - -/*----------------------------------------------------------------- - * Implementation of open/close/ioctl on the special char - * device. - *---------------------------------------------------------------*/ -static ioctl_fn lookup_ioctl(unsigned int cmd) -{ - static struct { - int cmd; - ioctl_fn fn; - } _ioctls[] = { - {DM_VERSION_CMD, NULL}, /* version is dealt with elsewhere */ - {DM_REMOVE_ALL_CMD, remove_all}, - {DM_DEV_CREATE_CMD, create}, - {DM_DEV_REMOVE_CMD, remove}, - {DM_DEV_RELOAD_CMD, reload}, - {DM_DEV_RENAME_CMD, rename}, - {DM_DEV_SUSPEND_CMD, suspend}, - {DM_DEV_DEPS_CMD, dep}, - {DM_DEV_STATUS_CMD, info}, - {DM_TARGET_STATUS_CMD, get_status}, - {DM_TARGET_WAIT_CMD, wait_device_event}, - }; - - return (cmd >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[cmd].fn; -} - -/* - * As well as checking the version compatibility this always - * copies the kernel interface version out. - */ -static int check_version(unsigned int cmd, struct dm_ioctl *user) -{ - uint32_t version[3]; - int r = 0; - - if (copy_from_user(version, user->version, sizeof(version))) - return -EFAULT; - - if ((DM_VERSION_MAJOR != version[0]) || - (DM_VERSION_MINOR < version[1])) { - DMWARN("ioctl interface mismatch: " - "kernel(%u.%u.%u), user(%u.%u.%u), cmd(%d)", - DM_VERSION_MAJOR, DM_VERSION_MINOR, - DM_VERSION_PATCHLEVEL, - version[0], version[1], version[2], cmd); - r = -EINVAL; - } - - /* - * Fill in the kernel version. - */ - version[0] = DM_VERSION_MAJOR; - version[1] = DM_VERSION_MINOR; - version[2] = DM_VERSION_PATCHLEVEL; - if (copy_to_user(user->version, version, sizeof(version))) - return -EFAULT; - - return r; -} - -static void free_params(struct dm_ioctl *param) -{ - vfree(param); -} - -static int copy_params(struct dm_ioctl *user, struct dm_ioctl **param) -{ - struct dm_ioctl tmp, *dmi; - - if (copy_from_user(&tmp, user, sizeof(tmp))) - return -EFAULT; - - if (tmp.data_size < sizeof(tmp)) - return -EINVAL; - - dmi = (struct dm_ioctl *) vmalloc(tmp.data_size); - if (!dmi) - return -ENOMEM; - - if (copy_from_user(dmi, user, tmp.data_size)) { - vfree(dmi); - return -EFAULT; - } - - *param = dmi; - return 0; -} - -static int validate_params(uint cmd, struct dm_ioctl *param) -{ - /* Ignores parameters */ - if (cmd == DM_REMOVE_ALL_CMD) - return 0; - - /* Unless creating, either name of uuid but not both */ - if (cmd != DM_DEV_CREATE_CMD) { - if ((!*param->uuid && !*param->name) || - (*param->uuid && *param->name)) { - DMWARN("one of name or uuid must be supplied"); - return -EINVAL; - } - } - - /* Ensure strings are terminated */ - param->name[DM_NAME_LEN - 1] = '\0'; - param->uuid[DM_UUID_LEN - 1] = '\0'; - - return 0; -} - -static int ctl_ioctl(struct inode *inode, struct file *file, - uint command, ulong u) -{ - int r = 0; - unsigned int cmd; - struct dm_ioctl *param; - struct dm_ioctl *user = (struct dm_ioctl *) u; - ioctl_fn fn = NULL; - - /* only root can play with this */ - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - - if (_IOC_TYPE(command) != DM_IOCTL) - return -ENOTTY; - - cmd = _IOC_NR(command); - - /* - * Check the interface version passed in. This also - * writes out the kernels interface version. - */ - r = check_version(cmd, user); - if (r) - return r; - - /* - * Nothing more to do for the version command. - */ - if (cmd == DM_VERSION_CMD) - return 0; - - fn = lookup_ioctl(cmd); - if (!fn) { - DMWARN("dm_ctl_ioctl: unknown command 0x%x", command); - return -ENOTTY; - } - - /* - * Copy the parameters into kernel space. - */ - r = copy_params(user, ¶m); - if (r) - return r; - - r = validate_params(cmd, param); - if (r) { - free_params(param); - return r; - } - - r = fn(param, user); - free_params(param); - return r; -} - -static struct file_operations _ctl_fops = { - .ioctl = ctl_ioctl, - .owner = THIS_MODULE, -}; - -static struct miscdevice _dm_misc = { - .minor = MISC_DYNAMIC_MINOR, - .name = DM_NAME, - .devfs_name = "mapper/control", - .fops = &_ctl_fops -}; - -/* - * Create misc character device and link to DM_DIR/control. - */ -int __init dm_interface_init(void) -{ - int r; - - r = dm_hash_init(); - if (r) - return r; - - r = misc_register(&_dm_misc); - if (r) { - DMERR("misc_register failed for control device"); - dm_hash_exit(); - return r; - } - - DMINFO("%d.%d.%d%s initialised: %s", DM_VERSION_MAJOR, - DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL, DM_VERSION_EXTRA, - DM_DRIVER_EMAIL); - return 0; - - if (misc_deregister(&_dm_misc) < 0) - DMERR("misc_deregister failed for control device"); - dm_hash_exit(); - return r; -} - -void dm_interface_exit(void) -{ - if (misc_deregister(&_dm_misc) < 0) - DMERR("misc_deregister failed for control device"); - dm_hash_exit(); -} --- linux-2.6.4-rc2/drivers/md/dm-ioctl-v4.c 2004-01-09 00:04:31.000000000 -0800 +++ /dev/null 2002-08-30 16:31:37.000000000 -0700 @@ -1,1264 +0,0 @@ -/* - * Copyright (C) 2001, 2002 Sistina Software (UK) Limited. - * - * This file is released under the GPL. - */ - -#include "dm.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define DM_DRIVER_EMAIL "dm@uk.sistina.com" - -/*----------------------------------------------------------------- - * The ioctl interface needs to be able to look up devices by - * name or uuid. - *---------------------------------------------------------------*/ -struct hash_cell { - struct list_head name_list; - struct list_head uuid_list; - - char *name; - char *uuid; - struct mapped_device *md; - struct dm_table *new_map; -}; - -#define NUM_BUCKETS 64 -#define MASK_BUCKETS (NUM_BUCKETS - 1) -static struct list_head _name_buckets[NUM_BUCKETS]; -static struct list_head _uuid_buckets[NUM_BUCKETS]; - -void dm_hash_remove_all(void); - -/* - * Guards access to both hash tables. - */ -static DECLARE_RWSEM(_hash_lock); - -static void init_buckets(struct list_head *buckets) -{ - unsigned int i; - - for (i = 0; i < NUM_BUCKETS; i++) - INIT_LIST_HEAD(buckets + i); -} - -int dm_hash_init(void) -{ - init_buckets(_name_buckets); - init_buckets(_uuid_buckets); - devfs_mk_dir(DM_DIR); - return 0; -} - -void dm_hash_exit(void) -{ - dm_hash_remove_all(); - devfs_remove(DM_DIR); -} - -/*----------------------------------------------------------------- - * Hash function: - * We're not really concerned with the str hash function being - * fast since it's only used by the ioctl interface. - *---------------------------------------------------------------*/ -static unsigned int hash_str(const char *str) -{ - const unsigned int hash_mult = 2654435387U; - unsigned int h = 0; - - while (*str) - h = (h + (unsigned int) *str++) * hash_mult; - - return h & MASK_BUCKETS; -} - -/*----------------------------------------------------------------- - * Code for looking up a device by name - *---------------------------------------------------------------*/ -static struct hash_cell *__get_name_cell(const char *str) -{ - struct list_head *tmp; - struct hash_cell *hc; - unsigned int h = hash_str(str); - - list_for_each (tmp, _name_buckets + h) { - hc = list_entry(tmp, struct hash_cell, name_list); - if (!strcmp(hc->name, str)) - return hc; - } - - return NULL; -} - -static struct hash_cell *__get_uuid_cell(const char *str) -{ - struct list_head *tmp; - struct hash_cell *hc; - unsigned int h = hash_str(str); - - list_for_each (tmp, _uuid_buckets + h) { - hc = list_entry(tmp, struct hash_cell, uuid_list); - if (!strcmp(hc->uuid, str)) - return hc; - } - - return NULL; -} - -/*----------------------------------------------------------------- - * Inserting, removing and renaming a device. - *---------------------------------------------------------------*/ -static inline char *kstrdup(const char *str) -{ - char *r = kmalloc(strlen(str) + 1, GFP_KERNEL); - if (r) - strcpy(r, str); - return r; -} - -static struct hash_cell *alloc_cell(const char *name, const char *uuid, - struct mapped_device *md) -{ - struct hash_cell *hc; - - hc = kmalloc(sizeof(*hc), GFP_KERNEL); - if (!hc) - return NULL; - - hc->name = kstrdup(name); - if (!hc->name) { - kfree(hc); - return NULL; - } - - if (!uuid) - hc->uuid = NULL; - - else { - hc->uuid = kstrdup(uuid); - if (!hc->uuid) { - kfree(hc->name); - kfree(hc); - return NULL; - } - } - - INIT_LIST_HEAD(&hc->name_list); - INIT_LIST_HEAD(&hc->uuid_list); - hc->md = md; - hc->new_map = NULL; - return hc; -} - -static void free_cell(struct hash_cell *hc) -{ - if (hc) { - kfree(hc->name); - kfree(hc->uuid); - kfree(hc); - } -} - -/* - * devfs stuff. - */ -static int register_with_devfs(struct hash_cell *hc) -{ - struct gendisk *disk = dm_disk(hc->md); - - devfs_mk_bdev(MKDEV(disk->major, disk->first_minor), - S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP, - DM_DIR "/%s", hc->name); - return 0; -} - -static int unregister_with_devfs(struct hash_cell *hc) -{ - devfs_remove(DM_DIR"/%s", hc->name); - return 0; -} - -/* - * The kdev_t and uuid of a device can never change once it is - * initially inserted. - */ -int dm_hash_insert(const char *name, const char *uuid, struct mapped_device *md) -{ - struct hash_cell *cell; - - /* - * Allocate the new cells. - */ - cell = alloc_cell(name, uuid, md); - if (!cell) - return -ENOMEM; - - /* - * Insert the cell into both hash tables. - */ - down_write(&_hash_lock); - if (__get_name_cell(name)) - goto bad; - - list_add(&cell->name_list, _name_buckets + hash_str(name)); - - if (uuid) { - if (__get_uuid_cell(uuid)) { - list_del(&cell->name_list); - goto bad; - } - list_add(&cell->uuid_list, _uuid_buckets + hash_str(uuid)); - } - register_with_devfs(cell); - dm_get(md); - up_write(&_hash_lock); - - return 0; - - bad: - up_write(&_hash_lock); - free_cell(cell); - return -EBUSY; -} - -void __hash_remove(struct hash_cell *hc) -{ - /* remove from the dev hash */ - list_del(&hc->uuid_list); - list_del(&hc->name_list); - unregister_with_devfs(hc); - dm_put(hc->md); - if (hc->new_map) - dm_table_put(hc->new_map); - free_cell(hc); -} - -void dm_hash_remove_all(void) -{ - int i; - struct hash_cell *hc; - struct list_head *tmp, *n; - - down_write(&_hash_lock); - for (i = 0; i < NUM_BUCKETS; i++) { - list_for_each_safe (tmp, n, _name_buckets + i) { - hc = list_entry(tmp, struct hash_cell, name_list); - __hash_remove(hc); - } - } - up_write(&_hash_lock); -} - -int dm_hash_rename(const char *old, const char *new) -{ - char *new_name, *old_name; - struct hash_cell *hc; - - /* - * duplicate new. - */ - new_name = kstrdup(new); - if (!new_name) - return -ENOMEM; - - down_write(&_hash_lock); - - /* - * Is new free ? - */ - hc = __get_name_cell(new); - if (hc) { - DMWARN("asked to rename to an already existing name %s -> %s", - old, new); - up_write(&_hash_lock); - kfree(new_name); - return -EBUSY; - } - - /* - * Is there such a device as 'old' ? - */ - hc = __get_name_cell(old); - if (!hc) { - DMWARN("asked to rename a non existent device %s -> %s", - old, new); - up_write(&_hash_lock); - kfree(new_name); - return -ENXIO; - } - - /* - * rename and move the name cell. - */ - unregister_with_devfs(hc); - - list_del(&hc->name_list); - old_name = hc->name; - hc->name = new_name; - list_add(&hc->name_list, _name_buckets + hash_str(new_name)); - - /* rename the device node in devfs */ - register_with_devfs(hc); - - up_write(&_hash_lock); - kfree(old_name); - return 0; -} - -/*----------------------------------------------------------------- - * Implementation of the ioctl commands - *---------------------------------------------------------------*/ -/* - * All the ioctl commands get dispatched to functions with this - * prototype. - */ -typedef int (*ioctl_fn)(struct dm_ioctl *param, size_t param_size); - -static int remove_all(struct dm_ioctl *param, size_t param_size) -{ - dm_hash_remove_all(); - param->data_size = 0; - return 0; -} - -/* - * Round up the ptr to an 8-byte boundary. - */ -#define ALIGN_MASK 7 -static inline void *align_ptr(void *ptr) -{ - return (void *) (((size_t) (ptr + ALIGN_MASK)) & ~ALIGN_MASK); -} - -/* - * Retrieves the data payload buffer from an already allocated - * struct dm_ioctl. - */ -static void *get_result_buffer(struct dm_ioctl *param, size_t param_size, - size_t *len) -{ - param->data_start = align_ptr(param + 1) - (void *) param; - - if (param->data_start < param_size) - *len = param_size - param->data_start; - else - *len = 0; - - return ((void *) param) + param->data_start; -} - -static int list_devices(struct dm_ioctl *param, size_t param_size) -{ - unsigned int i; - struct hash_cell *hc; - size_t len, needed = 0; - struct gendisk *disk; - struct dm_name_list *nl, *old_nl = NULL; - - down_write(&_hash_lock); - - /* - * Loop through all the devices working out how much - * space we need. - */ - for (i = 0; i < NUM_BUCKETS; i++) { - list_for_each_entry (hc, _name_buckets + i, name_list) { - needed += sizeof(struct dm_name_list); - needed += strlen(hc->name); - needed += ALIGN_MASK; - } - } - - /* - * Grab our output buffer. - */ - nl = get_result_buffer(param, param_size, &len); - if (len < needed) { - param->flags |= DM_BUFFER_FULL_FLAG; - goto out; - } - param->data_size = param->data_start + needed; - - nl->dev = 0; /* Flags no data */ - - /* - * Now loop through filling out the names. - */ - for (i = 0; i < NUM_BUCKETS; i++) { - list_for_each_entry (hc, _name_buckets + i, name_list) { - if (old_nl) - old_nl->next = (uint32_t) ((void *) nl - - (void *) old_nl); - disk = dm_disk(hc->md); - nl->dev = huge_encode_dev(MKDEV(disk->major, disk->first_minor)); - nl->next = 0; - strcpy(nl->name, hc->name); - - old_nl = nl; - nl = align_ptr(((void *) ++nl) + strlen(hc->name) + 1); - } - } - - out: - up_write(&_hash_lock); - return 0; -} - -static int check_name(const char *name) -{ - if (strchr(name, '/')) { - DMWARN("invalid device name"); - return -EINVAL; - } - - return 0; -} - -/* - * Fills in a dm_ioctl structure, ready for sending back to - * userland. - */ -static int __dev_status(struct mapped_device *md, struct dm_ioctl *param) -{ - struct gendisk *disk = dm_disk(md); - struct dm_table *table; - struct block_device *bdev; - - param->flags &= ~(DM_SUSPEND_FLAG | DM_READONLY_FLAG | - DM_ACTIVE_PRESENT_FLAG); - - if (dm_suspended(md)) - param->flags |= DM_SUSPEND_FLAG; - - bdev = bdget_disk(disk, 0); - if (!bdev) - return -ENXIO; - - param->dev = huge_encode_dev(MKDEV(disk->major, disk->first_minor)); - - /* - * Yes, this will be out of date by the time it gets back - * to userland, but it is still very useful ofr - * debugging. - */ - param->open_count = bdev->bd_openers; - bdput(bdev); - - if (disk->policy) - param->flags |= DM_READONLY_FLAG; - - param->event_nr = dm_get_event_nr(md); - - table = dm_get_table(md); - if (table) { - param->flags |= DM_ACTIVE_PRESENT_FLAG; - param->target_count = dm_table_get_num_targets(table); - dm_table_put(table); - } else - param->target_count = 0; - - return 0; -} - -static int dev_create(struct dm_ioctl *param, size_t param_size) -{ - int r; - struct mapped_device *md; - - r = check_name(param->name); - if (r) - return r; - - if (param->flags & DM_PERSISTENT_DEV_FLAG) - r = dm_create_with_minor(MINOR(huge_decode_dev(param->dev)), &md); - else - r = dm_create(&md); - - if (r) - return r; - - r = dm_hash_insert(param->name, *param->uuid ? param->uuid : NULL, md); - if (r) { - dm_put(md); - return r; - } - - param->flags &= ~DM_INACTIVE_PRESENT_FLAG; - - r = __dev_status(md, param); - dm_put(md); - - return r; -} - -/* - * Always use UUID for lookups if it's present, otherwise use name. - */ -static inline struct hash_cell *__find_device_hash_cell(struct dm_ioctl *param) -{ - return *param->uuid ? - __get_uuid_cell(param->uuid) : __get_name_cell(param->name); -} - -static inline struct mapped_device *find_device(struct dm_ioctl *param) -{ - struct hash_cell *hc; - struct mapped_device *md = NULL; - - down_read(&_hash_lock); - hc = __find_device_hash_cell(param); - if (hc) { - md = hc->md; - - /* - * Sneakily write in both the name and the uuid - * while we have the cell. - */ - strncpy(param->name, hc->name, sizeof(param->name)); - if (hc->uuid) - strncpy(param->uuid, hc->uuid, sizeof(param->uuid)-1); - else - param->uuid[0] = '\0'; - - if (hc->new_map) - param->flags |= DM_INACTIVE_PRESENT_FLAG; - else - param->flags &= ~DM_INACTIVE_PRESENT_FLAG; - - dm_get(md); - } - up_read(&_hash_lock); - - return md; -} - -static int dev_remove(struct dm_ioctl *param, size_t param_size) -{ - struct hash_cell *hc; - - down_write(&_hash_lock); - hc = __find_device_hash_cell(param); - - if (!hc) { - DMWARN("device doesn't appear to be in the dev hash table."); - up_write(&_hash_lock); - return -ENXIO; - } - - __hash_remove(hc); - up_write(&_hash_lock); - param->data_size = 0; - return 0; -} - -/* - * Check a string doesn't overrun the chunk of - * memory we copied from userland. - */ -static int invalid_str(char *str, void *end) -{ - while ((void *) str < end) - if (!*str++) - return 0; - - return -EINVAL; -} - -static int dev_rename(struct dm_ioctl *param, size_t param_size) -{ - int r; - char *new_name = (char *) param + param->data_start; - - if (new_name < (char *) (param + 1) || - invalid_str(new_name, (void *) param + param_size)) { - DMWARN("Invalid new logical volume name supplied."); - return -EINVAL; - } - - r = check_name(new_name); - if (r) - return r; - - param->data_size = 0; - return dm_hash_rename(param->name, new_name); -} - -static int do_suspend(struct dm_ioctl *param) -{ - int r = 0; - struct mapped_device *md; - - md = find_device(param); - if (!md) - return -ENXIO; - - if (!dm_suspended(md)) - r = dm_suspend(md); - - if (!r) - r = __dev_status(md, param); - - dm_put(md); - return r; -} - -static int do_resume(struct dm_ioctl *param) -{ - int r = 0; - struct hash_cell *hc; - struct mapped_device *md; - struct dm_table *new_map; - - down_write(&_hash_lock); - - hc = __find_device_hash_cell(param); - if (!hc) { - DMWARN("device doesn't appear to be in the dev hash table."); - up_write(&_hash_lock); - return -ENXIO; - } - - md = hc->md; - dm_get(md); - - new_map = hc->new_map; - hc->new_map = NULL; - param->flags &= ~DM_INACTIVE_PRESENT_FLAG; - - up_write(&_hash_lock); - - /* Do we need to load a new map ? */ - if (new_map) { - /* Suspend if it isn't already suspended */ - if (!dm_suspended(md)) - dm_suspend(md); - - r = dm_swap_table(md, new_map); - if (r) { - dm_put(md); - dm_table_put(new_map); - return r; - } - - if (dm_table_get_mode(new_map) & FMODE_WRITE) - set_disk_ro(dm_disk(md), 0); - else - set_disk_ro(dm_disk(md), 1); - - dm_table_put(new_map); - } - - if (dm_suspended(md)) - r = dm_resume(md); - - if (!r) - r = __dev_status(md, param); - - dm_put(md); - return r; -} - -/* - * Set or unset the suspension state of a device. - * If the device already is in the requested state we just return its status. - */ -static int dev_suspend(struct dm_ioctl *param, size_t param_size) -{ - if (param->flags & DM_SUSPEND_FLAG) - return do_suspend(param); - - return do_resume(param); -} - -/* - * Copies device info back to user space, used by - * the create and info ioctls. - */ -static int dev_status(struct dm_ioctl *param, size_t param_size) -{ - int r; - struct mapped_device *md; - - md = find_device(param); - if (!md) - return -ENXIO; - - r = __dev_status(md, param); - dm_put(md); - return r; -} - -/* - * Build up the status struct for each target - */ -static void retrieve_status(struct dm_table *table, - struct dm_ioctl *param, size_t param_size) -{ - unsigned int i, num_targets; - struct dm_target_spec *spec; - char *outbuf, *outptr; - status_type_t type; - size_t remaining, len, used = 0; - - outptr = outbuf = get_result_buffer(param, param_size, &len); - - if (param->flags & DM_STATUS_TABLE_FLAG) - type = STATUSTYPE_TABLE; - else - type = STATUSTYPE_INFO; - - /* Get all the target info */ - num_targets = dm_table_get_num_targets(table); - for (i = 0; i < num_targets; i++) { - struct dm_target *ti = dm_table_get_target(table, i); - - remaining = len - (outptr - outbuf); - if (remaining < sizeof(struct dm_target_spec)) { - param->flags |= DM_BUFFER_FULL_FLAG; - break; - } - - spec = (struct dm_target_spec *) outptr; - - spec->status = 0; - spec->sector_start = ti->begin; - spec->length = ti->len; - strncpy(spec->target_type, ti->type->name, - sizeof(spec->target_type)); - - outptr += sizeof(struct dm_target_spec); - remaining = len - (outptr - outbuf); - - /* Get the status/table string from the target driver */ - if (ti->type->status) { - if (ti->type->status(ti, type, outptr, remaining)) { - param->flags |= DM_BUFFER_FULL_FLAG; - break; - } - } else - outptr[0] = '\0'; - - outptr += strlen(outptr) + 1; - used = param->data_start + (outptr - outbuf); - - align_ptr(outptr); - spec->next = outptr - outbuf; - } - - if (used) - param->data_size = used; - - param->target_count = num_targets; -} - -/* - * Wait for a device to report an event - */ -static int dev_wait(struct dm_ioctl *param, size_t param_size) -{ - int r; - struct mapped_device *md; - struct dm_table *table; - DECLARE_WAITQUEUE(wq, current); - - md = find_device(param); - if (!md) - return -ENXIO; - - /* - * Wait for a notification event - */ - set_current_state(TASK_INTERRUPTIBLE); - if (!dm_add_wait_queue(md, &wq, param->event_nr)) { - schedule(); - dm_remove_wait_queue(md, &wq); - } - set_current_state(TASK_RUNNING); - - /* - * The userland program is going to want to know what - * changed to trigger the event, so we may as well tell - * him and save an ioctl. - */ - r = __dev_status(md, param); - if (r) - goto out; - - table = dm_get_table(md); - if (table) { - retrieve_status(table, param, param_size); - dm_table_put(table); - } - - out: - dm_put(md); - return r; -} - -static inline int get_mode(struct dm_ioctl *param) -{ - int mode = FMODE_READ | FMODE_WRITE; - - if (param->flags & DM_READONLY_FLAG) - mode = FMODE_READ; - - return mode; -} - -static int next_target(struct dm_target_spec *last, uint32_t next, void *end, - struct dm_target_spec **spec, char **target_params) -{ - *spec = (struct dm_target_spec *) ((unsigned char *) last + next); - *target_params = (char *) (*spec + 1); - - if (*spec < (last + 1)) - return -EINVAL; - - return invalid_str(*target_params, end); -} - -static int populate_table(struct dm_table *table, - struct dm_ioctl *param, size_t param_size) -{ - int r; - unsigned int i = 0; - struct dm_target_spec *spec = (struct dm_target_spec *) param; - uint32_t next = param->data_start; - void *end = (void *) param + param_size; - char *target_params; - - if (!param->target_count) { - DMWARN("populate_table: no targets specified"); - return -EINVAL; - } - - for (i = 0; i < param->target_count; i++) { - - r = next_target(spec, next, end, &spec, &target_params); - if (r) { - DMWARN("unable to find target"); - return r; - } - - r = dm_table_add_target(table, spec->target_type, - (sector_t) spec->sector_start, - (sector_t) spec->length, - target_params); - if (r) { - DMWARN("error adding target to table"); - return r; - } - - next = spec->next; - } - - return dm_table_complete(table); -} - -static int table_load(struct dm_ioctl *param, size_t param_size) -{ - int r; - struct hash_cell *hc; - struct dm_table *t; - - r = dm_table_create(&t, get_mode(param), param->target_count); - if (r) - return r; - - r = populate_table(t, param, param_size); - if (r) { - dm_table_put(t); - return r; - } - - down_write(&_hash_lock); - hc = __find_device_hash_cell(param); - if (!hc) { - DMWARN("device doesn't appear to be in the dev hash table."); - up_write(&_hash_lock); - return -ENXIO; - } - - if (hc->new_map) - dm_table_put(hc->new_map); - hc->new_map = t; - param->flags |= DM_INACTIVE_PRESENT_FLAG; - - r = __dev_status(hc->md, param); - up_write(&_hash_lock); - return r; -} - -static int table_clear(struct dm_ioctl *param, size_t param_size) -{ - int r; - struct hash_cell *hc; - - down_write(&_hash_lock); - - hc = __find_device_hash_cell(param); - if (!hc) { - DMWARN("device doesn't appear to be in the dev hash table."); - up_write(&_hash_lock); - return -ENXIO; - } - - if (hc->new_map) { - dm_table_put(hc->new_map); - hc->new_map = NULL; - } - - param->flags &= ~DM_INACTIVE_PRESENT_FLAG; - - r = __dev_status(hc->md, param); - up_write(&_hash_lock); - return r; -} - -/* - * Retrieves a list of devices used by a particular dm device. - */ -static void retrieve_deps(struct dm_table *table, - struct dm_ioctl *param, size_t param_size) -{ - unsigned int count = 0; - struct list_head *tmp; - size_t len, needed; - struct dm_target_deps *deps; - - deps = get_result_buffer(param, param_size, &len); - - /* - * Count the devices. - */ - list_for_each(tmp, dm_table_get_devices(table)) - count++; - - /* - * Check we have enough space. - */ - needed = sizeof(*deps) + (sizeof(*deps->dev) * count); - if (len < needed) { - param->flags |= DM_BUFFER_FULL_FLAG; - return; - } - - /* - * Fill in the devices. - */ - deps->count = count; - count = 0; - list_for_each(tmp, dm_table_get_devices(table)) { - struct dm_dev *dd = list_entry(tmp, struct dm_dev, list); - deps->dev[count++] = huge_encode_dev(dd->bdev->bd_dev); - } - - param->data_size = param->data_start + needed; -} - -static int table_deps(struct dm_ioctl *param, size_t param_size) -{ - int r = 0; - struct mapped_device *md; - struct dm_table *table; - - md = find_device(param); - if (!md) - return -ENXIO; - - r = __dev_status(md, param); - if (r) - goto out; - - table = dm_get_table(md); - if (table) { - retrieve_deps(table, param, param_size); - dm_table_put(table); - } - - out: - dm_put(md); - return r; -} - -/* - * Return the status of a device as a text string for each - * target. - */ -static int table_status(struct dm_ioctl *param, size_t param_size) -{ - int r; - struct mapped_device *md; - struct dm_table *table; - - md = find_device(param); - if (!md) - return -ENXIO; - - r = __dev_status(md, param); - if (r) - goto out; - - table = dm_get_table(md); - if (table) { - retrieve_status(table, param, param_size); - dm_table_put(table); - } - - out: - dm_put(md); - return r; -} - -/*----------------------------------------------------------------- - * Implementation of open/close/ioctl on the special char - * device. - *---------------------------------------------------------------*/ -static ioctl_fn lookup_ioctl(unsigned int cmd) -{ - static struct { - int cmd; - ioctl_fn fn; - } _ioctls[] = { - {DM_VERSION_CMD, NULL}, /* version is dealt with elsewhere */ - {DM_REMOVE_ALL_CMD, remove_all}, - {DM_LIST_DEVICES_CMD, list_devices}, - - {DM_DEV_CREATE_CMD, dev_create}, - {DM_DEV_REMOVE_CMD, dev_remove}, - {DM_DEV_RENAME_CMD, dev_rename}, - {DM_DEV_SUSPEND_CMD, dev_suspend}, - {DM_DEV_STATUS_CMD, dev_status}, - {DM_DEV_WAIT_CMD, dev_wait}, - - {DM_TABLE_LOAD_CMD, table_load}, - {DM_TABLE_CLEAR_CMD, table_clear}, - {DM_TABLE_DEPS_CMD, table_deps}, - {DM_TABLE_STATUS_CMD, table_status} - }; - - return (cmd >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[cmd].fn; -} - -/* - * As well as checking the version compatibility this always - * copies the kernel interface version out. - */ -static int check_version(unsigned int cmd, struct dm_ioctl *user) -{ - uint32_t version[3]; - int r = 0; - - if (copy_from_user(version, user->version, sizeof(version))) - return -EFAULT; - - if ((DM_VERSION_MAJOR != version[0]) || - (DM_VERSION_MINOR < version[1])) { - DMWARN("ioctl interface mismatch: " - "kernel(%u.%u.%u), user(%u.%u.%u), cmd(%d)", - DM_VERSION_MAJOR, DM_VERSION_MINOR, - DM_VERSION_PATCHLEVEL, - version[0], version[1], version[2], cmd); - r = -EINVAL; - } - - /* - * Fill in the kernel version. - */ - version[0] = DM_VERSION_MAJOR; - version[1] = DM_VERSION_MINOR; - version[2] = DM_VERSION_PATCHLEVEL; - if (copy_to_user(user->version, version, sizeof(version))) - return -EFAULT; - - return r; -} - -static void free_params(struct dm_ioctl *param) -{ - vfree(param); -} - -static int copy_params(struct dm_ioctl *user, struct dm_ioctl **param) -{ - struct dm_ioctl tmp, *dmi; - - if (copy_from_user(&tmp, user, sizeof(tmp))) - return -EFAULT; - - if (tmp.data_size < sizeof(tmp)) - return -EINVAL; - - dmi = (struct dm_ioctl *) vmalloc(tmp.data_size); - if (!dmi) - return -ENOMEM; - - if (copy_from_user(dmi, user, tmp.data_size)) { - vfree(dmi); - return -EFAULT; - } - - *param = dmi; - return 0; -} - -static int validate_params(uint cmd, struct dm_ioctl *param) -{ - /* Always clear this flag */ - param->flags &= ~DM_BUFFER_FULL_FLAG; - - /* Ignores parameters */ - if (cmd == DM_REMOVE_ALL_CMD || cmd == DM_LIST_DEVICES_CMD) - return 0; - - /* Unless creating, either name or uuid but not both */ - if (cmd != DM_DEV_CREATE_CMD) { - if ((!*param->uuid && !*param->name) || - (*param->uuid && *param->name)) { - DMWARN("one of name or uuid must be supplied, cmd(%u)", - cmd); - return -EINVAL; - } - } - - /* Ensure strings are terminated */ - param->name[DM_NAME_LEN - 1] = '\0'; - param->uuid[DM_UUID_LEN - 1] = '\0'; - - return 0; -} - -static int ctl_ioctl(struct inode *inode, struct file *file, - uint command, ulong u) -{ - int r = 0; - unsigned int cmd; - struct dm_ioctl *param; - struct dm_ioctl *user = (struct dm_ioctl *) u; - ioctl_fn fn = NULL; - size_t param_size; - - /* only root can play with this */ - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - - if (_IOC_TYPE(command) != DM_IOCTL) - return -ENOTTY; - - cmd = _IOC_NR(command); - - /* - * Check the interface version passed in. This also - * writes out the kernel's interface version. - */ - r = check_version(cmd, user); - if (r) - return r; - - /* - * Nothing more to do for the version command. - */ - if (cmd == DM_VERSION_CMD) - return 0; - - fn = lookup_ioctl(cmd); - if (!fn) { - DMWARN("dm_ctl_ioctl: unknown command 0x%x", command); - return -ENOTTY; - } - - /* - * Trying to avoid low memory issues when a device is - * suspended. - */ - current->flags |= PF_MEMALLOC; - - /* - * Copy the parameters into kernel space. - */ - r = copy_params(user, ¶m); - if (r) { - current->flags &= ~PF_MEMALLOC; - return r; - } - - /* - * FIXME: eventually we will remove the PF_MEMALLOC flag - * here. However the tools still do nasty things like - * 'load' while a device is suspended. - */ - - r = validate_params(cmd, param); - if (r) - goto out; - - param_size = param->data_size; - param->data_size = sizeof(*param); - r = fn(param, param_size); - - /* - * Copy the results back to userland. - */ - if (!r && copy_to_user(user, param, param->data_size)) - r = -EFAULT; - - out: - free_params(param); - current->flags &= ~PF_MEMALLOC; - return r; -} - -static struct file_operations _ctl_fops = { - .ioctl = ctl_ioctl, - .owner = THIS_MODULE, -}; - -static struct miscdevice _dm_misc = { - .minor = MISC_DYNAMIC_MINOR, - .name = DM_NAME, - .devfs_name = "mapper/control", - .fops = &_ctl_fops -}; - -/* - * Create misc character device and link to DM_DIR/control. - */ -int __init dm_interface_init(void) -{ - int r; - - r = dm_hash_init(); - if (r) - return r; - - r = misc_register(&_dm_misc); - if (r) { - DMERR("misc_register failed for control device"); - dm_hash_exit(); - return r; - } - - DMINFO("%d.%d.%d%s initialised: %s", DM_VERSION_MAJOR, - DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL, DM_VERSION_EXTRA, - DM_DRIVER_EMAIL); - return 0; -} - -void dm_interface_exit(void) -{ - if (misc_deregister(&_dm_misc) < 0) - DMERR("misc_deregister failed for control device"); - - dm_hash_exit(); -} --- linux-2.6.4-rc2/drivers/md/dm-linear.c 2003-09-27 18:57:44.000000000 -0700 +++ 25/drivers/md/dm-linear.c 2004-03-07 20:47:52.000000000 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2001 Sistina Software (UK) Limited. + * Copyright (C) 2001-2003 Sistina Software (UK) Limited. * * This file is released under the GPL. */ @@ -65,7 +65,8 @@ static void linear_dtr(struct dm_target kfree(lc); } -static int linear_map(struct dm_target *ti, struct bio *bio) +static int linear_map(struct dm_target *ti, struct bio *bio, + union map_info *map_context) { struct linear_c *lc = (struct linear_c *) ti->private; @@ -96,6 +97,7 @@ static int linear_status(struct dm_targe static struct target_type linear_target = { .name = "linear", + .version= {1, 0, 1}, .module = THIS_MODULE, .ctr = linear_ctr, .dtr = linear_dtr, --- linux-2.6.4-rc2/drivers/md/dm-stripe.c 2004-03-03 23:12:44.000000000 -0800 +++ 25/drivers/md/dm-stripe.c 2004-03-07 20:47:53.000000000 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2001 Sistina Software (UK) Limited. + * Copyright (C) 2001-2003 Sistina Software (UK) Limited. * * This file is released under the GPL. */ @@ -97,7 +97,8 @@ static int stripe_ctr(struct dm_target * /* * chunk_size is a power of two */ - if (!chunk_size || (chunk_size & (chunk_size - 1))) { + if (!chunk_size || (chunk_size & (chunk_size - 1)) || + (chunk_size < (PAGE_SIZE >> SECTOR_SHIFT))) { ti->error = "dm-stripe: Invalid chunk size"; return -EINVAL; } @@ -166,7 +167,8 @@ static void stripe_dtr(struct dm_target kfree(sc); } -static int stripe_map(struct dm_target *ti, struct bio *bio) +static int stripe_map(struct dm_target *ti, struct bio *bio, + union map_info *map_context) { struct stripe_c *sc = (struct stripe_c *) ti->private; @@ -211,6 +213,7 @@ static int stripe_status(struct dm_targe static struct target_type stripe_target = { .name = "striped", + .version= {1, 0, 1}, .module = THIS_MODULE, .ctr = stripe_ctr, .dtr = stripe_dtr, --- linux-2.6.4-rc2/drivers/md/dm-table.c 2004-03-03 23:12:44.000000000 -0800 +++ 25/drivers/md/dm-table.c 2004-03-07 20:47:53.000000000 -0800 @@ -329,13 +329,11 @@ static int lookup_device(const char *pat */ static struct dm_dev *find_device(struct list_head *l, dev_t dev) { - struct list_head *tmp; + struct dm_dev *dd; - list_for_each(tmp, l) { - struct dm_dev *dd = list_entry(tmp, struct dm_dev, list); + list_for_each_entry (dd, l, list) if (dd->bdev->bd_dev == dev) return dd; - } return NULL; } @@ -631,14 +629,20 @@ static int split_args(int *argc, char ** return 0; } -static void set_default_limits(struct io_restrictions *rs) +static void check_for_valid_limits(struct io_restrictions *rs) { - rs->max_sectors = MAX_SECTORS; - rs->max_phys_segments = MAX_PHYS_SEGMENTS; - rs->max_hw_segments = MAX_HW_SEGMENTS; - rs->hardsect_size = 1 << SECTOR_SHIFT; - rs->max_segment_size = MAX_SEGMENT_SIZE; - rs->seg_boundary_mask = -1; + if (!rs->max_sectors) + rs->max_sectors = MAX_SECTORS; + if (!rs->max_phys_segments) + rs->max_phys_segments = MAX_PHYS_SEGMENTS; + if (!rs->max_hw_segments) + rs->max_hw_segments = MAX_HW_SEGMENTS; + if (!rs->hardsect_size) + rs->hardsect_size = 1 << SECTOR_SHIFT; + if (!rs->max_segment_size) + rs->max_segment_size = MAX_SEGMENT_SIZE; + if (!rs->seg_boundary_mask) + rs->seg_boundary_mask = -1; } int dm_table_add_target(struct dm_table *t, const char *type, @@ -653,7 +657,6 @@ int dm_table_add_target(struct dm_table tgt = t->targets + t->num_targets; memset(tgt, 0, sizeof(*tgt)); - set_default_limits(&tgt->limits); if (!len) { tgt->error = "zero-length target"; @@ -738,6 +741,8 @@ int dm_table_complete(struct dm_table *t int r = 0; unsigned int leaf_nodes; + check_for_valid_limits(&t->limits); + /* how many indexes will the btree have ? */ leaf_nodes = dm_div_up(t->num_targets, KEYS_PER_NODE); t->depth = 1 + int_log(leaf_nodes, CHILDREN_PER_NODE); @@ -862,6 +867,20 @@ void dm_table_resume_targets(struct dm_t } } +int dm_table_any_congested(struct dm_table *t, int bdi_bits) +{ + struct list_head *d, *devices; + int r = 0; + + devices = dm_table_get_devices(t); + for (d = devices->next; d != devices; d = d->next) { + struct dm_dev *dd = list_entry(d, struct dm_dev, list); + request_queue_t *q = bdev_get_queue(dd->bdev); + r |= bdi_congested(&q->backing_dev_info, bdi_bits); + } + + return r; +} EXPORT_SYMBOL(dm_vcalloc); EXPORT_SYMBOL(dm_get_device); --- linux-2.6.4-rc2/drivers/md/dm-target.c 2003-06-14 12:18:47.000000000 -0700 +++ 25/drivers/md/dm-target.c 2004-03-07 20:47:52.000000000 -0800 @@ -25,15 +25,11 @@ static DECLARE_RWSEM(_lock); static inline struct tt_internal *__find_target_type(const char *name) { - struct list_head *tih; struct tt_internal *ti; - list_for_each(tih, &_targets) { - ti = list_entry(tih, struct tt_internal, list); - + list_for_each_entry (ti, &_targets, list) if (!strcmp(name, ti->tt.name)) return ti; - } return NULL; } @@ -100,6 +96,20 @@ static struct tt_internal *alloc_target( return ti; } + +int dm_target_iterate(void (*iter_func)(struct target_type *tt, + void *param), void *param) +{ + struct tt_internal *ti; + + down_read(&_lock); + list_for_each_entry (ti, &_targets, list) + iter_func(&ti->tt, param); + up_read(&_lock); + + return 0; +} + int dm_register_target(struct target_type *t) { int rv = 0; @@ -157,13 +167,15 @@ static void io_err_dtr(struct dm_target /* empty */ } -static int io_err_map(struct dm_target *ti, struct bio *bio) +static int io_err_map(struct dm_target *ti, struct bio *bio, + union map_info *map_context) { return -EIO; } static struct target_type error_target = { .name = "error", + .version = {1, 0, 1}, .ctr = io_err_ctr, .dtr = io_err_dtr, .map = io_err_map, --- linux-2.6.4-rc2/drivers/md/Kconfig 2004-03-03 23:12:44.000000000 -0800 +++ 25/drivers/md/Kconfig 2004-03-07 20:46:46.000000000 -0800 @@ -162,14 +162,6 @@ config BLK_DEV_DM If unsure, say N. -config DM_IOCTL_V4 - bool "ioctl interface version 4" - depends on BLK_DEV_DM - default y - ---help--- - Recent tools use a new version of the ioctl interface, only - select this option if you intend using such tools. - config DM_CRYPT tristate "Crypt target support" depends on BLK_DEV_DM && EXPERIMENTAL --- linux-2.6.4-rc2/drivers/md/md.c 2004-03-03 23:12:44.000000000 -0800 +++ 25/drivers/md/md.c 2004-03-07 20:48:03.000000000 -0800 @@ -57,7 +57,7 @@ #ifndef MODULE -static void autostart_arrays (void); +static void autostart_arrays (int part); #endif static mdk_personality_t *pers[MAX_PERSONALITY]; @@ -1792,7 +1792,7 @@ static void autorun_array(mddev_t *mddev * * If "unit" is allocated, then bump its reference count */ -static void autorun_devices(void) +static void autorun_devices(int part) { struct list_head candidates; struct list_head *tmp; @@ -1825,7 +1825,12 @@ static void autorun_devices(void) bdevname(rdev0->bdev, b), rdev0->preferred_minor); break; } - dev = MKDEV(MD_MAJOR, rdev0->preferred_minor); + if (part) + dev = MKDEV(mdp_major, + rdev0->preferred_minor << MdpMinorShift); + else + dev = MKDEV(MD_MAJOR, rdev0->preferred_minor); + md_probe(dev, NULL, NULL); mddev = mddev_find(dev); if (!mddev) { @@ -1922,7 +1927,7 @@ static int autostart_array(dev_t startde /* * possibly return codes */ - autorun_devices(); + autorun_devices(0); return 0; } @@ -2407,7 +2412,7 @@ static int md_ioctl(struct inode *inode, #ifndef MODULE case RAID_AUTORUN: err = 0; - autostart_arrays(); + autostart_arrays(arg); goto done; #endif default:; @@ -3577,7 +3582,7 @@ void md_autodetect_dev(dev_t dev) } -static void autostart_arrays(void) +static void autostart_arrays(int part) { char b[BDEVNAME_SIZE]; mdk_rdev_t *rdev; @@ -3602,7 +3607,7 @@ static void autostart_arrays(void) } dev_cnt = 0; - autorun_devices(); + autorun_devices(part); } #endif --- linux-2.6.4-rc2/drivers/md/raid5.c 2004-03-03 23:12:44.000000000 -0800 +++ 25/drivers/md/raid5.c 2004-03-07 20:48:03.000000000 -0800 @@ -1409,7 +1409,8 @@ static int sync_request (mddev_t *mddev, /* make sure we don't swamp the stripe cache if someone else * is trying to get access */ - yield(); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(max(HZ/500, 1)); } spin_lock(&sh->lock); set_bit(STRIPE_SYNCING, &sh->state); --- linux-2.6.4-rc2/drivers/md/raid6main.c 2004-03-03 23:12:44.000000000 -0800 +++ 25/drivers/md/raid6main.c 2004-03-07 20:48:03.000000000 -0800 @@ -1571,7 +1571,8 @@ static int sync_request (mddev_t *mddev, /* make sure we don't swamp the stripe cache if someone else * is trying to get access */ - yield(); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(max(HZ/500, 1)); } spin_lock(&sh->lock); set_bit(STRIPE_SYNCING, &sh->state); --- linux-2.6.4-rc2/drivers/media/dvb/frontends/alps_tdlb7.c 2004-03-03 23:12:44.000000000 -0800 +++ 25/drivers/media/dvb/frontends/alps_tdlb7.c 2004-03-07 20:47:47.000000000 -0800 @@ -29,8 +29,6 @@ */ - -#define __KERNEL_SYSCALLS__ #include #include #include @@ -58,8 +56,6 @@ static int debug = 0; #define SP8870_FIRMWARE_OFFSET 0x0A -static int errno; - static struct dvb_frontend_info tdlb7_info = { .name = "Alps TDLB7", .type = FE_OFDM, @@ -174,13 +170,13 @@ static int sp8870_read_firmware_file (co loff_t filesize; char *dp; - fd = open(fn, 0, 0); + fd = sys_open(fn, 0, 0); if (fd == -1) { printk("%s: unable to open '%s'.\n", __FUNCTION__, fn); return -EIO; } - filesize = lseek(fd, 0L, 2); + filesize = sys_lseek(fd, 0L, 2); if (filesize <= 0 || filesize < SP8870_FIRMWARE_OFFSET + SP8870_FIRMWARE_SIZE) { printk("%s: firmware filesize to small '%s'\n", __FUNCTION__, fn); sys_close(fd); @@ -194,8 +190,8 @@ static int sp8870_read_firmware_file (co return -EIO; } - lseek(fd, SP8870_FIRMWARE_OFFSET, 0); - if (read(fd, dp, SP8870_FIRMWARE_SIZE) != SP8870_FIRMWARE_SIZE) { + sys_lseek(fd, SP8870_FIRMWARE_OFFSET, 0); + if (sys_read(fd, dp, SP8870_FIRMWARE_SIZE) != SP8870_FIRMWARE_SIZE) { printk("%s: failed to read '%s'.\n",__FUNCTION__, fn); vfree(dp); sys_close(fd); --- linux-2.6.4-rc2/drivers/media/dvb/frontends/sp887x.c 2004-03-03 23:12:44.000000000 -0800 +++ 25/drivers/media/dvb/frontends/sp887x.c 2004-03-07 20:47:47.000000000 -0800 @@ -12,7 +12,6 @@ next 0x4000 loaded. This may change in future versions. */ -#define __KERNEL_SYSCALLS__ #include #include #include @@ -68,8 +67,6 @@ struct dvb_frontend_info sp887x_info = { FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_RECOVER }; -static int errno; - static int i2c_writebytes (struct dvb_frontend *fe, u8 addr, u8 *buf, u8 len) { @@ -216,13 +213,13 @@ int sp887x_initial_setup (struct dvb_fro // Load the firmware set_fs(get_ds()); - fd = open(sp887x_firmware, 0, 0); + fd = sys_open(sp887x_firmware, 0, 0); if (fd < 0) { printk(KERN_WARNING "%s: Unable to open firmware %s\n", __FUNCTION__, sp887x_firmware); return -EIO; } - filesize = lseek(fd, 0L, 2); + filesize = sys_lseek(fd, 0L, 2); if (filesize <= 0) { printk(KERN_WARNING "%s: Firmware %s is empty\n", __FUNCTION__, sp887x_firmware); @@ -244,8 +241,8 @@ int sp887x_initial_setup (struct dvb_fro // read it! // read the first 16384 bytes from the file // ignore the first 10 bytes - lseek(fd, 10, 0); - if (read(fd, firmware, fw_size) != fw_size) { + sys_lseek(fd, 10, 0); + if (sys_read(fd, firmware, fw_size) != fw_size) { printk(KERN_WARNING "%s: Failed to read firmware\n", __FUNCTION__); vfree(firmware); sys_close(fd); --- linux-2.6.4-rc2/drivers/media/dvb/frontends/tda1004x.c 2004-03-03 23:12:44.000000000 -0800 +++ 25/drivers/media/dvb/frontends/tda1004x.c 2004-03-07 20:47:47.000000000 -0800 @@ -32,7 +32,6 @@ */ -#define __KERNEL_SYSCALLS__ #include #include #include @@ -41,7 +40,6 @@ #include #include #include -#include #include #include #include "dvb_frontend.h" @@ -399,13 +397,13 @@ static int tda1004x_fwupload(struct dvb_ // Load the firmware set_fs(get_ds()); - fd = open(tda1004x_firmware, 0, 0); + fd = sys_open(tda1004x_firmware, 0, 0); if (fd < 0) { printk("%s: Unable to open firmware %s\n", __FUNCTION__, tda1004x_firmware); return -EIO; } - filesize = lseek(fd, 0L, 2); + filesize = sys_lseek(fd, 0L, 2); if (filesize <= 0) { printk("%s: Firmware %s is empty\n", __FUNCTION__, tda1004x_firmware); @@ -436,8 +434,8 @@ static int tda1004x_fwupload(struct dvb_ } // read it! - lseek(fd, fw_offset, 0); - if (read(fd, firmware, fw_size) != fw_size) { + sys_lseek(fd, fw_offset, 0); + if (sys_read(fd, firmware, fw_size) != fw_size) { printk("%s: Failed to read firmware\n", __FUNCTION__); vfree(firmware); sys_close(fd); --- linux-2.6.4-rc2/drivers/media/video/v4l1-compat.c 2004-02-03 20:42:36.000000000 -0800 +++ 25/drivers/media/video/v4l1-compat.c 2004-03-07 20:46:46.000000000 -0800 @@ -503,10 +503,11 @@ v4l_compat_translate_ioctl(struct inode int *on = arg; if (0 == *on) { + enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; /* dirty hack time. But v4l1 has no STREAMOFF * equivalent in the API, and this one at * least comes close ... */ - drv(inode, file, VIDIOC_STREAMOFF, NULL); + drv(inode, file, VIDIOC_STREAMOFF, &type); } err = drv(inode, file, VIDIOC_OVERLAY, arg); if (err < 0) @@ -857,6 +858,7 @@ v4l_compat_translate_ioctl(struct inode case VIDIOCMCAPTURE: /* capture a frame */ { struct video_mmap *mm = arg; + enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt2 = kmalloc(sizeof(*fmt2),GFP_KERNEL); memset(&buf2,0,sizeof(buf2)); @@ -897,7 +899,7 @@ v4l_compat_translate_ioctl(struct inode dprintk("VIDIOCMCAPTURE / VIDIOC_QBUF: %d\n",err); break; } - err = drv(inode, file, VIDIOC_STREAMON, NULL); + err = drv(inode, file, VIDIOC_STREAMON, &type); if (err < 0) dprintk("VIDIOCMCAPTURE / VIDIOC_STREAMON: %d\n",err); break; --- linux-2.6.4-rc2/drivers/media/video/video-buf.c 2004-02-03 20:42:36.000000000 -0800 +++ 25/drivers/media/video/video-buf.c 2004-03-07 20:46:58.000000000 -0800 @@ -215,7 +215,7 @@ int videobuf_dma_pci_sync(struct pci_dev BUG(); if (!dma->bus_addr) - pci_dma_sync_sg(dev,dma->sglist,dma->nr_pages,dma->direction); + pci_dma_sync_sg_for_cpu(dev,dma->sglist,dma->nr_pages,dma->direction); return 0; } --- linux-2.6.4-rc2/drivers/message/fusion/Kconfig 2004-03-03 23:12:44.000000000 -0800 +++ 25/drivers/message/fusion/Kconfig 2004-03-07 20:46:55.000000000 -0800 @@ -14,41 +14,6 @@ config FUSION [1] LAN is not supported on parallel SCSI medium. - These drivers require a Fusion MPT compatible PCI adapter installed - in the host system. MPT adapters contain specialized I/O processors - to handle I/O workload, and more importantly to offload this work - from the host CPU(s). - - If you have Fusion MPT hardware and want to use it, you can say - Y or M here to add MPT (base + ScsiHost) drivers. - = build lib (fusion), and link [static] into the kernel [2] - proper - = compiled as [dynamic] modules [3] named: (mptbase, - mptscsih) - - [2] In order enable capability to boot the linux kernel - natively from a Fusion MPT target device, you MUST - answer Y here! (currently requires CONFIG_BLK_DEV_SD) - [3] To compile this support as modules, choose M here. - - If unsure, say N. - - If you say Y or M here you will get a choice of these - additional protocol and support module options: Module Name: - Enhanced SCSI error reporting (isense) - Fusion MPT misc device (ioctl) driver (mptctl) - Fusion MPT LAN driver (mptlan) - - --- - Fusion MPT is trademark of LSI Logic Corporation, and its - architecture is based on LSI Logic's Message Passing Interface (MPI) - specification. - -config FUSION_BOOT - bool - depends on FUSION=y - default y - config FUSION_MAX_SGE int "Maximum number of scatter gather entries" depends on FUSION @@ -62,7 +27,6 @@ config FUSION_MAX_SGE necessary (or recommended) unless the user will be running large I/O's via the raw interface. -# How can we force these options to module or nothing? config FUSION_ISENSE tristate "Enhanced SCSI error reporting" depends on MODULES && FUSION && m @@ -132,17 +96,4 @@ config FUSION_LAN If unsure whether you really want or need this, say N. - NOTES: This feature is NOT available nor supported for linux-2.2.x - kernels. You must be building a linux-2.3.x or linux-2.4.x kernel - in order to configure this option. - Support for building this feature into the linux kernel is not - yet available. - -# if [ "$CONFIG_FUSION_LAN" != "n" ]; then -# define_bool CONFIG_NET_FC y -# fi -# These be define_tristate, but we leave them define_bool -# for backward compatibility with pre-linux-2.2.15 kernels. -# (Bugzilla:fibrebugs, #384) endmenu - --- linux-2.6.4-rc2/drivers/message/fusion/mptbase.c 2004-03-03 23:12:44.000000000 -0800 +++ 25/drivers/message/fusion/mptbase.c 2004-03-07 20:47:46.000000000 -0800 @@ -1515,17 +1515,18 @@ mptbase_probe(struct pci_dev *pdev, cons || (ioc->chip_type == C1035) || (ioc->chip_type == FC929X)) mpt_detect_bound_ports(ioc, pdev); - if ((r = mpt_do_ioc_recovery(ioc, MPT_HOSTEVENT_IOC_BRINGUP, CAN_SLEEP)) != 0) { - printk(KERN_WARNING MYNAM ": WARNING - %s did not initialize properly! (%d)\n", - ioc->name, r); - } + if ((r = mpt_do_ioc_recovery(ioc, + MPT_HOSTEVENT_IOC_BRINGUP, CAN_SLEEP)) != 0) { + printk(KERN_WARNING MYNAM + ": WARNING - %s did not initialize properly! (%d)\n", + ioc->name, r); - if(r != 0 ) { Q_DEL_ITEM(ioc); mpt_adapters[ioc->id] = NULL; free_irq(ioc->pci_irq, ioc); iounmap(mem); kfree(ioc); + pci_set_drvdata(pdev, NULL); return r; } @@ -2508,10 +2509,10 @@ GetIocFacts(MPT_ADAPTER *ioc, int sleepF * Set values for this IOC's request & reply frame sizes, * and request & reply queue depths... */ - ioc->req_sz = MIN(MPT_DEFAULT_FRAME_SIZE, facts->RequestFrameSize * 4); - ioc->req_depth = MIN(MPT_MAX_REQ_DEPTH, facts->GlobalCredits); + ioc->req_sz = min(MPT_DEFAULT_FRAME_SIZE, facts->RequestFrameSize * 4); + ioc->req_depth = min_t(int, MPT_MAX_REQ_DEPTH, facts->GlobalCredits); ioc->reply_sz = ioc->req_sz; - ioc->reply_depth = MIN(MPT_DEFAULT_REPLY_DEPTH, facts->ReplyQueueDepth); + ioc->reply_depth = min_t(int, MPT_DEFAULT_REPLY_DEPTH, facts->ReplyQueueDepth); dprintk((MYIOC_s_INFO_FMT "reply_sz=%3d, reply_depth=%4d\n", ioc->name, ioc->reply_sz, ioc->reply_depth)); @@ -3992,7 +3993,7 @@ mpt_handshake_req_reply_wait(MPT_ADAPTER /* * Copy out the cached reply... */ - for (ii=0; ii < MIN(replyBytes/2,mptReply->MsgLength*2); ii++) + for (ii=0; ii < min(replyBytes/2, mptReply->MsgLength*2); ii++) u16reply[ii] = ioc->hs_reply[ii]; } else { return -99; @@ -4239,7 +4240,7 @@ GetLanConfigPages(MPT_ADAPTER *ioc) if ((rc = mpt_config(ioc, &cfg)) == 0) { /* save the data */ - copy_sz = MIN(sizeof(LANPage0_t), data_sz); + copy_sz = min_t(int, sizeof(LANPage0_t), data_sz); memcpy(&ioc->lan_cnfg_page0, ppage0_alloc, copy_sz); } @@ -4284,7 +4285,7 @@ GetLanConfigPages(MPT_ADAPTER *ioc) if ((rc = mpt_config(ioc, &cfg)) == 0) { /* save the data */ - copy_sz = MIN(sizeof(LANPage1_t), data_sz); + copy_sz = min_t(int, sizeof(LANPage1_t), data_sz); memcpy(&ioc->lan_cnfg_page1, ppage1_alloc, copy_sz); } @@ -4353,7 +4354,7 @@ GetFcPortPage0(MPT_ADAPTER *ioc, int por if ((rc = mpt_config(ioc, &cfg)) == 0) { /* save the data */ pp0dest = &ioc->fc_port_page0[portnum]; - copy_sz = MIN(sizeof(FCPortPage0_t), data_sz); + copy_sz = min_t(int, sizeof(FCPortPage0_t), data_sz); memcpy(pp0dest, ppage0_alloc, copy_sz); /* --- linux-2.6.4-rc2/drivers/message/fusion/mptbase.h 2004-03-03 23:12:44.000000000 -0800 +++ 25/drivers/message/fusion/mptbase.h 2004-03-07 20:47:46.000000000 -0800 @@ -80,8 +80,8 @@ #define COPYRIGHT "Copyright (c) 1999-2003 " MODULEAUTHOR #endif -#define MPT_LINUX_VERSION_COMMON "3.00.03" -#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-3.00.03" +#define MPT_LINUX_VERSION_COMMON "3.00.04" +#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-3.00.04" #define WHAT_MAGIC_STRING "@" "(" "#" ")" #define show_mptmod_ver(s,ver) \ @@ -1057,13 +1057,6 @@ extern int mpt_ASCQ_TableSz; /* * More (public) macros... */ -#ifndef MIN -#define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#endif -#ifndef MAX -#define MAX(a, b) (((a) > (b)) ? (a) : (b)) -#endif - #ifndef offsetof #define offsetof(t, m) ((size_t) (&((t *)0)->m)) #endif --- linux-2.6.4-rc2/drivers/message/fusion/mptctl.c 2004-01-09 00:04:31.000000000 -0800 +++ 25/drivers/message/fusion/mptctl.c 2004-03-07 20:47:46.000000000 -0800 @@ -283,7 +283,7 @@ mptctl_reply(MPT_ADAPTER *ioc, MPT_FRAME dctlprintk((MYIOC_s_INFO_FMT ": Copying Reply Frame @%p to IOC!\n", ioc->name, reply)); memcpy(ioc->ioctl->ReplyFrame, reply, - MIN(ioc->reply_sz, 4*reply->u.reply.MsgLength)); + min(ioc->reply_sz, 4*reply->u.reply.MsgLength)); ioc->ioctl->status |= MPT_IOCTL_STATUS_RF_VALID; /* Set the command status to GOOD if IOC Status is GOOD @@ -334,7 +334,7 @@ mptctl_reply(MPT_ADAPTER *ioc, MPT_FRAME // NOTE: Expects/requires non-Turbo reply! dctlprintk((MYIOC_s_INFO_FMT ":Caching MPI_FUNCTION_FW_DOWNLOAD reply!\n", ioc->name)); - memcpy(fwReplyBuffer, reply, MIN(sizeof(fwReplyBuffer), 4*reply->u.reply.MsgLength)); + memcpy(fwReplyBuffer, reply, min_t(int, sizeof(fwReplyBuffer), 4*reply->u.reply.MsgLength)); ReplyMsg = (pMPIDefaultReply_t) fwReplyBuffer; } } @@ -992,7 +992,7 @@ kbuf_alloc_2_sgl(int bytes, u32 sgdir, i MptSge_t *sgl; int numfrags = 0; int fragcnt = 0; - int alloc_sz = MIN(bytes,MAX_KMALLOC_SZ); // avoid kernel warning msg! + int alloc_sz = min(bytes, MAX_KMALLOC_SZ); // avoid kernel warning msg! int bytes_allocd = 0; int this_alloc; dma_addr_t pa; // phys addr @@ -1037,7 +1037,7 @@ kbuf_alloc_2_sgl(int bytes, u32 sgdir, i sgl = sglbuf; sg_spill = ((ioc->req_sz - sge_offset)/(sizeof(dma_addr_t) + sizeof(u32))) - 1; while (bytes_allocd < bytes) { - this_alloc = MIN(alloc_sz, bytes-bytes_allocd); + this_alloc = min(alloc_sz, bytes-bytes_allocd); buflist[buflist_ent].len = this_alloc; buflist[buflist_ent].kptr = pci_alloc_consistent(ioc->pcidev, this_alloc, @@ -2280,9 +2280,9 @@ mptctl_do_mpt_command (struct mpt_ioctl_ */ if (ioc->ioctl->status & MPT_IOCTL_STATUS_RF_VALID) { if (karg.maxReplyBytes < ioc->reply_sz) { - sz = MIN(karg.maxReplyBytes, 4*ioc->ioctl->ReplyFrame[2]); + sz = min(karg.maxReplyBytes, 4*ioc->ioctl->ReplyFrame[2]); } else { - sz = MIN(ioc->reply_sz, 4*ioc->ioctl->ReplyFrame[2]); + sz = min(ioc->reply_sz, 4*ioc->ioctl->ReplyFrame[2]); } if (sz > 0) { @@ -2301,7 +2301,7 @@ mptctl_do_mpt_command (struct mpt_ioctl_ /* If valid sense data, copy to user. */ if (ioc->ioctl->status & MPT_IOCTL_STATUS_SENSE_VALID) { - sz = MIN(karg.maxSenseBytes, MPT_SENSE_BUFFER_SIZE); + sz = min(karg.maxSenseBytes, MPT_SENSE_BUFFER_SIZE); if (sz > 0) { if (copy_to_user((char *)karg.senseDataPtr, ioc->ioctl->sense, sz)) { printk(KERN_ERR "%s@%d::mptctl_do_mpt_command - " --- linux-2.6.4-rc2/drivers/message/fusion/mptlan.c 2004-03-03 23:12:44.000000000 -0800 +++ 25/drivers/message/fusion/mptlan.c 2004-03-07 20:46:58.000000000 -0800 @@ -955,11 +955,13 @@ mpt_lan_receive_post_turbo(struct net_de return -ENOMEM; } - pci_dma_sync_single(mpt_dev->pcidev, priv->RcvCtl[ctx].dma, - priv->RcvCtl[ctx].len, PCI_DMA_FROMDEVICE); + pci_dma_sync_single_for_cpu(mpt_dev->pcidev, priv->RcvCtl[ctx].dma, + priv->RcvCtl[ctx].len, PCI_DMA_FROMDEVICE); memcpy(skb_put(skb, len), old_skb->data, len); + pci_dma_sync_single_for_device(mpt_dev->pcidev, priv->RcvCtl[ctx].dma, + priv->RcvCtl[ctx].len, PCI_DMA_FROMDEVICE); goto out; } @@ -1113,12 +1115,17 @@ mpt_lan_receive_post_reply(struct net_de // IOC_AND_NETDEV_NAMES_s_s(dev), // i, l)); - pci_dma_sync_single(mpt_dev->pcidev, - priv->RcvCtl[ctx].dma, - priv->RcvCtl[ctx].len, - PCI_DMA_FROMDEVICE); + pci_dma_sync_single_for_cpu(mpt_dev->pcidev, + priv->RcvCtl[ctx].dma, + priv->RcvCtl[ctx].len, + PCI_DMA_FROMDEVICE); memcpy(skb_put(skb, l), old_skb->data, l); + pci_dma_sync_single_for_device(mpt_dev->pcidev, + priv->RcvCtl[ctx].dma, + priv->RcvCtl[ctx].len, + PCI_DMA_FROMDEVICE); + priv->mpt_rxfidx[++priv->mpt_rxfidx_tail] = ctx; szrem -= l; } @@ -1136,11 +1143,18 @@ mpt_lan_receive_post_reply(struct net_de return -ENOMEM; } - pci_dma_sync_single(mpt_dev->pcidev, priv->RcvCtl[ctx].dma, - priv->RcvCtl[ctx].len, PCI_DMA_FROMDEVICE); + pci_dma_sync_single_for_cpu(mpt_dev->pcidev, + priv->RcvCtl[ctx].dma, + priv->RcvCtl[ctx].len, + PCI_DMA_FROMDEVICE); memcpy(skb_put(skb, len), old_skb->data, len); + pci_dma_sync_single_for_device(mpt_dev->pcidev, + priv->RcvCtl[ctx].dma, + priv->RcvCtl[ctx].len, + PCI_DMA_FROMDEVICE); + spin_lock_irqsave(&priv->rxfidx_lock, flags); priv->mpt_rxfidx[++priv->mpt_rxfidx_tail] = ctx; spin_unlock_irqrestore(&priv->rxfidx_lock, flags); --- linux-2.6.4-rc2/drivers/message/fusion/mptscsih.c 2004-03-03 23:12:44.000000000 -0800 +++ 25/drivers/message/fusion/mptscsih.c 2004-03-07 20:47:46.000000000 -0800 @@ -200,8 +200,8 @@ static int mptscsih_setup(char *str); static int __init mptscsih_init (void); static void __exit mptscsih_exit (void); -static int __devinit mptscsih_probe (struct pci_dev *, const struct pci_device_id *); -static void __devexit mptscsih_remove(struct pci_dev *); +static int mptscsih_probe (struct pci_dev *, const struct pci_device_id *); +static void mptscsih_remove(struct pci_dev *); static void mptscsih_shutdown(struct device *); #ifdef CONFIG_PM static int mptscsih_suspend(struct pci_dev *pdev, u32 state); @@ -1405,7 +1405,7 @@ static char *info_kbuf = NULL; * */ -static int __devinit +static int mptscsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct Scsi_Host *sh = NULL; @@ -1718,7 +1718,7 @@ mptscsih_probe_failed: * * */ -static void __devexit +static void mptscsih_remove(struct pci_dev *pdev) { MPT_ADAPTER *ioc = pci_get_drvdata(pdev); @@ -1920,7 +1920,7 @@ mptscsih_resume(struct pci_dev *pdev) static struct mpt_pci_driver mptscsih_driver = { .probe = mptscsih_probe, - .remove = __devexit_p(mptscsih_remove), + .remove = mptscsih_remove, .shutdown = mptscsih_shutdown, #ifdef CONFIG_PM .suspend = mptscsih_suspend, @@ -3658,7 +3658,7 @@ mptscsih_target_settings(MPT_SCSI_HOST * if (nfactor < pspi_data->minSyncFactor ) nfactor = pspi_data->minSyncFactor; - factor = MAX (factor, nfactor); + factor = max(factor, nfactor); if (factor == MPT_ASYNC) offset = 0; } else { @@ -4716,7 +4716,7 @@ mptscsih_writeSDP1(MPT_SCSI_HOST *hd, in maxid = ioc->sh->max_id - 1; } else if (ioc->sh) { id = target_id; - maxid = MIN(id, ioc->sh->max_id - 1); + maxid = min_t(int, id, ioc->sh->max_id - 1); } for (; id <= maxid; id++) { @@ -5049,7 +5049,7 @@ mptscsih_scandv_complete(MPT_ADAPTER *io sense_data = ((u8 *)hd->ioc->sense_buf_pool + (req_idx * MPT_SENSE_BUFFER_ALLOC)); - sz = MIN (pReq->SenseBufferLength, + sz = min_t(int, pReq->SenseBufferLength, SCSI_STD_SENSE_BYTES); memcpy(hd->pLocal->sense, sense_data, sz); @@ -5698,7 +5698,7 @@ mptscsih_domainValidation(void *arg) ioc->spi_data.forceDv &= ~MPT_SCSICFG_RELOAD_IOC_PG3; } - maxid = MIN (ioc->sh->max_id, MPT_MAX_SCSI_DEVICES); + maxid = min_t(int, ioc->sh->max_id, MPT_MAX_SCSI_DEVICES); for (id = 0; id < maxid; id++) { spin_lock_irqsave(&dvtaskQ_lock, flags); @@ -6374,7 +6374,7 @@ mptscsih_doDv(MPT_SCSI_HOST *hd, int por if (echoBufSize > 0) { iocmd.flags |= MPT_ICFLAG_ECHO; if (dataBufSize > 0) - bufsize = MIN(echoBufSize, dataBufSize); + bufsize = min(echoBufSize, dataBufSize); else bufsize = echoBufSize; } else if (dataBufSize == 0) @@ -6385,7 +6385,7 @@ mptscsih_doDv(MPT_SCSI_HOST *hd, int por /* Data buffers for write-read-compare test max 1K. */ - sz = MIN(bufsize, 1024); + sz = min(bufsize, 1024); /* --- loop ---- * On first pass, always issue a reserve. @@ -6740,9 +6740,9 @@ mptscsih_dv_parms(MPT_SCSI_HOST *hd, DVP } /* limit by adapter capabilities */ - width = MIN(width, hd->ioc->spi_data.maxBusWidth); - offset = MIN(offset, hd->ioc->spi_data.maxSyncOffset); - factor = MAX(factor, hd->ioc->spi_data.minSyncFactor); + width = min(width, hd->ioc->spi_data.maxBusWidth); + offset = min(offset, hd->ioc->spi_data.maxSyncOffset); + factor = max(factor, hd->ioc->spi_data.minSyncFactor); /* Check Consistency */ if (offset && (factor < MPT_ULTRA2) && !width) --- linux-2.6.4-rc2/drivers/message/i2o/i2o_block.c 2003-11-09 16:45:05.000000000 -0800 +++ 25/drivers/message/i2o/i2o_block.c 2004-03-07 20:46:46.000000000 -0800 @@ -50,9 +50,11 @@ * Properly attach/detach I2O gendisk structure from the system * gendisk list. The I2O block devices now appear in * /proc/partitions. + * Markus Lidel : + * Minor bugfixes for 2.6. * - * To do: - * Serial number scanning to find duplicates for FC multipathing + * To do: + * Serial number scanning to find duplicates for FC multipathing */ #include @@ -109,25 +111,6 @@ I2O_EVT_IND_BSA_SCSI_SMART ) -/* - * I2O Block Error Codes - should be in a header file really... - */ -#define I2O_BSA_DSC_SUCCESS 0x0000 -#define I2O_BSA_DSC_MEDIA_ERROR 0x0001 -#define I2O_BSA_DSC_ACCESS_ERROR 0x0002 -#define I2O_BSA_DSC_DEVICE_FAILURE 0x0003 -#define I2O_BSA_DSC_DEVICE_NOT_READY 0x0004 -#define I2O_BSA_DSC_MEDIA_NOT_PRESENT 0x0005 -#define I2O_BSA_DSC_MEDIA_LOCKED 0x0006 -#define I2O_BSA_DSC_MEDIA_FAILURE 0x0007 -#define I2O_BSA_DSC_PROTOCOL_FAILURE 0x0008 -#define I2O_BSA_DSC_BUS_FAILURE 0x0009 -#define I2O_BSA_DSC_ACCESS_VIOLATION 0x000A -#define I2O_BSA_DSC_WRITE_PROTECTED 0x000B -#define I2O_BSA_DSC_DEVICE_RESET 0x000C -#define I2O_BSA_DSC_VOLUME_CHANGED 0x000D -#define I2O_BSA_DSC_TIMEOUT 0x000E - #define I2O_LOCK(unit) (i2ob_dev[(unit)].req_queue->queue_lock) /* @@ -1091,6 +1074,28 @@ static int i2ob_install_device(struct i2 d->lct_data.tid, unit); /* + * If this is the first I2O block device found on this IOP, + * we need to initialize all the queue data structures + * before any I/O can be performed. If it fails, this + * device is useless. + */ + if(!i2ob_queues[unit]) { + if(i2ob_init_iop(unit)) + return 1; + } + + /* + * This will save one level of lookup/indirection in critical + * code so that we can directly get the queue ptr from the + * device instead of having to go the IOP data structure. + */ + dev->req_queue = i2ob_queues[unit]->req_queue; + + /* initialize gendik structure */ + i2ob_disk[unit>>4]->private_data = dev; + i2ob_disk[unit>>4]->queue = dev->req_queue; + + /* * Ask for the current media data. If that isn't supported * then we ask for the device capacity data */ @@ -1148,6 +1153,7 @@ static int i2ob_install_device(struct i2 } strcpy(d->dev_name, i2ob_disk[unit>>4]->disk_name); + strcpy(i2ob_disk[unit>>4]->devfs_name, i2ob_disk[unit>>4]->disk_name); printk(KERN_INFO "%s: Max segments %d, queue depth %d, byte limit %d.\n", d->dev_name, i2ob_dev[unit].max_segments, i2ob_dev[unit].depth, i2ob_max_sectors[unit]<<9); @@ -1193,28 +1199,6 @@ static int i2ob_install_device(struct i2 printk(KERN_INFO "%s: Maximum sectors/read set to %d.\n", d->dev_name, i2ob_max_sectors[unit]); - /* - * If this is the first I2O block device found on this IOP, - * we need to initialize all the queue data structures - * before any I/O can be performed. If it fails, this - * device is useless. - */ - if(!i2ob_queues[c->unit]) { - if(i2ob_init_iop(c->unit)) - return 1; - } - - /* - * This will save one level of lookup/indirection in critical - * code so that we can directly get the queue ptr from the - * device instead of having to go the IOP data structure. - */ - dev->req_queue = i2ob_queues[c->unit]->req_queue; - - /* Register a size before we register for events - otherwise we - might miss and overwrite an event */ - set_capacity(i2ob_disk[unit>>4], size>>9); - /* * Register for the events we're interested in and that the * device actually supports. @@ -1251,6 +1235,7 @@ static int i2ob_init_iop(unsigned int un i2ob_queues[unit]->i2ob_qhead = &i2ob_queues[unit]->request_queue[0]; atomic_set(&i2ob_queues[unit]->queue_depth, 0); + i2ob_queues[unit]->lock = SPIN_LOCK_UNLOCKED; i2ob_queues[unit]->req_queue = blk_init_queue(i2ob_request, &i2ob_queues[unit]->lock); if (!i2ob_queues[unit]->req_queue) { kfree(i2ob_queues[unit]); @@ -1336,6 +1321,8 @@ static void i2ob_scan(int bios) continue; } + i2o_release_device(d, &i2o_block_handler); + if(scan_unit>4); } - i2o_release_device(d, &i2o_block_handler); } i2o_unlock_controller(c); } @@ -1699,9 +1685,9 @@ static void i2o_block_exit(void) if(evt_running) { printk(KERN_INFO "Killing I2O block threads..."); - i = kill_proc(evt_pid, SIGTERM, 1); + i = kill_proc(evt_pid, SIGKILL, 1); if(!i) { - printk("waiting..."); + printk("waiting...\n"); } /* Be sure it died */ wait_for_completion(&i2ob_thread_dead); --- linux-2.6.4-rc2/drivers/message/i2o/i2o_core.c 2003-06-14 12:18:24.000000000 -0700 +++ 25/drivers/message/i2o/i2o_core.c 2004-03-07 20:46:58.000000000 -0800 @@ -13,15 +13,16 @@ * A lot of the I2O message side code from this is taken from the * Red Creek RCPCI45 adapter driver by Red Creek Communications * - * Fixes by: - * Philipp Rumpf - * Juha Sievänen - * Auvo Häkkinen - * Deepak Saxena - * Boji T Kannanthanam - * - * Ported to Linux 2.5 by - * Alan Cox + * Fixes/additions: + * Philipp Rumpf + * Juha Sievänen + * Auvo Häkkinen + * Deepak Saxena + * Boji T Kannanthanam + * Alan Cox : + * Ported to Linux 2.5. + * Markus Lidel : + * Minor fixes for 2.6. * */ @@ -502,6 +503,7 @@ int i2o_install_controller(struct i2o_co c->unit = i; c->page_frame = NULL; c->hrt = NULL; + c->hrt_len = 0; c->lct = NULL; c->status_block = NULL; sprintf(c->name, "i2o/iop%d", i); @@ -564,7 +566,7 @@ int i2o_delete_controller(struct i2o_con * If this is shutdown time, the thread's already been killed */ if(c->lct_running) { - stat = kill_proc(c->lct_pid, SIGTERM, 1); + stat = kill_proc(c->lct_pid, SIGKILL, 1); if(!stat) { int count = 10 * 100; while(c->lct_running && --count) { @@ -1177,7 +1179,7 @@ void i2o_run_queue(struct i2o_controller * the processor */ - pci_dma_sync_single(c->pdev, c->page_frame_map, MSG_FRAME_SIZE, PCI_DMA_FROMDEVICE); + pci_dma_sync_single_for_cpu(c->pdev, c->page_frame_map, MSG_FRAME_SIZE, PCI_DMA_FROMDEVICE); /* * Despatch it @@ -1861,31 +1863,36 @@ int i2o_hrt_get(struct i2o_controller *c { u32 msg[6]; int ret, size = sizeof(i2o_hrt); + int loops = 3; /* we only try 3 times to get the HRT, this should be + more then enough. Worst case should be 2 times.*/ /* First read just the header to figure out the real size */ do { + /* first we allocate the memory for the HRT */ if (c->hrt == NULL) { c->hrt=pci_alloc_consistent(c->pdev, size, &c->hrt_phys); if (c->hrt == NULL) { printk(KERN_CRIT "%s: Hrt Get failed; Out of memory.\n", c->name); return -ENOMEM; } + c->hrt_len = size; } msg[0]= SIX_WORD_MSG_SIZE| SGL_OFFSET_4; msg[1]= I2O_CMD_HRT_GET<<24 | HOST_TID<<12 | ADAPTER_TID; msg[3]= 0; - msg[4]= (0xD0000000 | size); /* Simple transaction */ + msg[4]= (0xD0000000 | c->hrt_len); /* Simple transaction */ msg[5]= c->hrt_phys; /* Dump it here */ - ret = i2o_post_wait_mem(c, msg, sizeof(msg), 20, c->hrt, NULL, c->hrt_phys, 0, size, 0); + ret = i2o_post_wait_mem(c, msg, sizeof(msg), 20, c->hrt, NULL, c->hrt_phys, 0, c->hrt_len, 0); if(ret == -ETIMEDOUT) { /* The HRT block we used is in limbo somewhere. When the iop wakes up we will recover it */ c->hrt = NULL; + c->hrt_len = 0; return ret; } @@ -1896,13 +1903,20 @@ int i2o_hrt_get(struct i2o_controller *c return ret; } - if (c->hrt->num_entries * c->hrt->entry_len << 2 > size) { - int new_size = c->hrt->num_entries * c->hrt->entry_len << 2; - pci_free_consistent(c->pdev, size, c->hrt, c->hrt_phys); - size = new_size; + if (c->hrt->num_entries * c->hrt->entry_len << 2 > c->hrt_len) { + size = c->hrt->num_entries * c->hrt->entry_len << 2; + pci_free_consistent(c->pdev, c->hrt_len, c->hrt, c->hrt_phys); + c->hrt_len = 0; c->hrt = NULL; } - } while (c->hrt == NULL); + loops --; + } while (c->hrt == NULL && loops > 0); + + if(c->hrt == NULL) + { + printk(KERN_ERR "%s: Unable to get HRT after three tries, giving up\n", c->name); + return -1; + } i2o_parse_hrt(c); // just for debugging @@ -3628,8 +3642,6 @@ int __init i2o_pci_install(struct pci_de return 0; } -static int dpt; - /** * i2o_pci_scan - Scan the pci bus for controllers * @@ -3654,14 +3666,7 @@ int __init i2o_pci_scan(void) { if((dev->class>>8)!=PCI_CLASS_INTELLIGENT_I2O) continue; - if(dev->vendor == PCI_VENDOR_ID_DPT && !dpt) - { - if(dev->device == 0xA501 || dev->device == 0xA511) - { - printk(KERN_INFO "i2o: Skipping Adaptec/DPT I2O raid with preferred native driver.\n"); - continue; - } - } + if((dev->class&0xFF)>1) { printk(KERN_INFO "i2o: I2O Controller found but does not support I2O 1.5 (skipping).\n"); @@ -3735,22 +3740,19 @@ static void i2o_core_exit(void) */ if(evt_running) { printk("Terminating i2o threads..."); - stat = kill_proc(evt_pid, SIGTERM, 1); + stat = kill_proc(evt_pid, SIGKILL, 1); if(!stat) { - printk("waiting..."); + printk("waiting...\n"); wait_for_completion(&evt_dead); } printk("done.\n"); } i2o_remove_handler(&i2o_core_handler); - unregister_reboot_notifier(&i2o_reboot_notifier); } module_init(i2o_core_init); module_exit(i2o_core_exit); -MODULE_PARM(dpt, "i"); -MODULE_PARM_DESC(dpt, "Set this if you want to drive DPT cards normally handled by dpt_i2o"); MODULE_PARM(verbose, "i"); MODULE_PARM_DESC(verbose, "Verbose diagnostics"); --- linux-2.6.4-rc2/drivers/message/i2o/i2o_scsi.c 2003-07-27 12:14:39.000000000 -0700 +++ 25/drivers/message/i2o/i2o_scsi.c 2004-03-07 20:46:46.000000000 -0800 @@ -29,12 +29,15 @@ * In general the firmware wants to help. Where its help isn't performance * useful we just ignore the aid. Its not worth the code in truth. * - * Fixes: - * Steve Ralston : Scatter gather now works + * Fixes/additions: + * Steve Ralston: + * Scatter gather now works + * Markus Lidel : + * Minor fixes for 2.6. * - * To Do - * 64bit cleanups - * Fix the resource management problems. + * To Do: + * 64bit cleanups + * Fix the resource management problems. */ @@ -66,7 +69,13 @@ #define VERSION_STRING "Version 0.1.2" -#define dprintk(x) +//#define DRIVERDEBUG + +#ifdef DRIVERDEBUG +#define dprintk(s, args...) printk(s, ## args) +#else +#define dprintk(s, args...) +#endif #define I2O_SCSI_CAN_QUEUE 4 #define MAXHOSTS 32 @@ -252,15 +261,15 @@ static void i2o_scsi_reply(struct i2o_ha as=(u8)le32_to_cpu(m[4]>>8); st=(u8)le32_to_cpu(m[4]>>24); - dprintk(("i2o got a scsi reply %08X: ", m[0])); - dprintk(("m[2]=%08X: ", m[2])); - dprintk(("m[4]=%08X\n", m[4])); + dprintk(KERN_INFO "i2o got a scsi reply %08X: ", m[0]); + dprintk(KERN_INFO "m[2]=%08X: ", m[2]); + dprintk(KERN_INFO "m[4]=%08X\n", m[4]); if(m[2]&0x80000000) { if(m[2]&0x40000000) { - dprintk(("Event.\n")); + dprintk(KERN_INFO "Event.\n"); lun_done=1; return; } @@ -280,12 +289,12 @@ static void i2o_scsi_reply(struct i2o_ha if(current_command==NULL) { if(st) - dprintk(("SCSI abort: %08X", m[4])); - dprintk(("SCSI abort completed.\n")); + dprintk(KERN_WARNING "SCSI abort: %08X", m[4]); + dprintk(KERN_INFO "SCSI abort completed.\n"); return; } - dprintk(("Completed %ld\n", current_command->serial_number)); + dprintk(KERN_INFO "Completed %ld\n", current_command->serial_number); atomic_dec(&queue_depth); @@ -308,7 +317,7 @@ static void i2o_scsi_reply(struct i2o_ha { /* An error has occurred */ - dprintk((KERN_DEBUG "SCSI error %08X", m[4])); + dprintk(KERN_WARNING "SCSI error %08X", m[4]); if (as == 0x0E) /* SCSI Reset */ @@ -368,7 +377,7 @@ static int i2o_find_lun(struct i2o_contr *lun=reply[1]; - dprintk(("SCSI (%d,%d)\n", *target, *lun)); + dprintk(KERN_INFO "SCSI (%d,%d)\n", *target, *lun); return 0; } @@ -401,8 +410,8 @@ static void i2o_scsi_init(struct i2o_con for(unit=c->devices;unit!=NULL;unit=unit->next) { - dprintk(("Class %03X, parent %d, want %d.\n", - unit->lct_data.class_id, unit->lct_data.parent_tid, d->lct_data.tid)); + dprintk(KERN_INFO "Class %03X, parent %d, want %d.\n", + unit->lct_data.class_id, unit->lct_data.parent_tid, d->lct_data.tid); /* Only look at scsi and fc devices */ if ( (unit->lct_data.class_id != I2O_CLASS_SCSI_PERIPHERAL) @@ -411,19 +420,19 @@ static void i2o_scsi_init(struct i2o_con continue; /* On our bus ? */ - dprintk(("Found a disk (%d).\n", unit->lct_data.tid)); + dprintk(KERN_INFO "Found a disk (%d).\n", unit->lct_data.tid); if ((unit->lct_data.parent_tid == d->lct_data.tid) || (unit->lct_data.parent_tid == d->lct_data.parent_tid) ) { u16 limit; - dprintk(("Its ours.\n")); + dprintk(KERN_INFO "Its ours.\n"); if(i2o_find_lun(c, unit, &target, &lun)==-1) { printk(KERN_ERR "i2o_scsi: Unable to get lun for tid %d.\n", unit->lct_data.tid); continue; } - dprintk(("Found disk %d %d.\n", target, lun)); + dprintk(KERN_INFO "Found disk %d %d.\n", target, lun); h->task[target][lun]=unit->lct_data.tid; h->tagclock[target][lun]=jiffies; @@ -439,8 +448,8 @@ static void i2o_scsi_init(struct i2o_con shpnt->sg_tablesize = limit; - dprintk(("i2o_scsi: set scatter-gather to %d.\n", - shpnt->sg_tablesize)); + dprintk(KERN_INFO "i2o_scsi: set scatter-gather to %d.\n", + shpnt->sg_tablesize); } } } @@ -558,6 +567,9 @@ static int i2o_scsi_release(struct Scsi_ del_timer(&retry_timer); i2o_remove_handler(&i2o_scsi_handler); } + + scsi_unregister(host); + return 0; } @@ -624,7 +636,7 @@ static int i2o_scsi_queuecommand(Scsi_Cm tid = hostdata->task[SCpnt->device->id][SCpnt->device->lun]; - dprintk(("qcmd: Tid = %d\n", tid)); + dprintk(KERN_INFO "qcmd: Tid = %d\n", tid); current_command = SCpnt; /* set current command */ current_command->scsi_done = done; /* set ptr to done function */ @@ -641,7 +653,7 @@ static int i2o_scsi_queuecommand(Scsi_Cm return 0; } - dprintk(("Real scsi messages.\n")); + dprintk(KERN_INFO "Real scsi messages.\n"); /* * Obtain an I2O message. If there are none free then @@ -821,8 +833,8 @@ static int i2o_scsi_queuecommand(Scsi_Cm } else { - dprintk(("non sg for %p, %d\n", SCpnt->request_buffer, - SCpnt->request_bufflen)); + dprintk(KERN_INFO "non sg for %p, %d\n", SCpnt->request_buffer, + SCpnt->request_bufflen); i2o_raw_writel(len = SCpnt->request_bufflen, lenptr); if(len == 0) { @@ -861,7 +873,7 @@ static int i2o_scsi_queuecommand(Scsi_Cm } mb(); - dprintk(("Issued %ld\n", current_command->serial_number)); + dprintk(KERN_INFO "Issued %ld\n", current_command->serial_number); return 0; } --- linux-2.6.4-rc2/drivers/message/i2o/Kconfig 2004-02-03 20:42:36.000000000 -0800 +++ 25/drivers/message/i2o/Kconfig 2004-03-07 20:46:46.000000000 -0800 @@ -39,7 +39,10 @@ config I2O_BLOCK depends on I2O help Include support for the I2O Block OSM. The Block OSM presents disk - and other structured block devices to the operating system. + and other structured block devices to the operating system. If you + are using an RAID controller, you could access the array only by + the Block OSM driver. But it is possible to access the single disks + by the SCSI OSM driver, for example to monitor the disks. To compile this support as a module, choose M here: the module will be called i2o_block. @@ -50,7 +53,8 @@ config I2O_SCSI help Allows direct SCSI access to SCSI devices on a SCSI or FibreChannel I2O controller. You can use both the SCSI and Block OSM together if - you wish. + you wish. To access a RAID array, you must use the Block OSM driver. + But you could use the SCSI OSM driver to monitor the single disks. To compile this support as a module, choose M here: the module will be called i2o_scsi. --- linux-2.6.4-rc2/drivers/mtd/chips/cfi_cmdset_0020.c 2004-02-17 20:48:43.000000000 -0800 +++ 25/drivers/mtd/chips/cfi_cmdset_0020.c 2004-03-07 20:46:46.000000000 -0800 @@ -1460,3 +1460,5 @@ static void __exit cfi_staa_exit(void) module_init(cfi_staa_init); module_exit(cfi_staa_exit); + +MODULE_LICENSE("GPL"); --- linux-2.6.4-rc2/drivers/mtd/maps/map_funcs.c 2003-06-14 12:17:56.000000000 -0700 +++ 25/drivers/mtd/maps/map_funcs.c 2004-03-07 20:46:46.000000000 -0800 @@ -93,3 +93,4 @@ void simple_map_init(struct map_info *ma } EXPORT_SYMBOL(simple_map_init); +MODULE_LICENSE("GPL"); --- linux-2.6.4-rc2/drivers/net/3c503.c 2004-02-17 20:48:43.000000000 -0800 +++ 25/drivers/net/3c503.c 2004-03-07 20:46:54.000000000 -0800 @@ -337,6 +337,9 @@ el2_probe1(struct net_device *dev, int i dev->open = &el2_open; dev->stop = &el2_close; dev->ethtool_ops = &netdev_ethtool_ops; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = ei_poll; +#endif if (dev->mem_start) printk("%s: %s - %dkB RAM, 8kB shared mem window at %#6lx-%#6lx.\n", --- linux-2.6.4-rc2/drivers/net/3c59x.c 2004-03-03 23:12:45.000000000 -0800 +++ 25/drivers/net/3c59x.c 2004-03-07 20:46:58.000000000 -0800 @@ -927,6 +927,18 @@ static struct net_device *compaq_net_dev static int vortex_cards_found; +#ifdef CONFIG_NET_POLL_CONTROLLER +static void poll_vortex(struct net_device *dev) +{ + struct vortex_private *vp = (struct vortex_private *)dev->priv; + unsigned long flags; + local_save_flags(flags); + local_irq_disable(); + (vp->full_bus_master_rx ? boomerang_interrupt:vortex_interrupt)(dev->irq,dev,NULL); + local_irq_restore(flags); +} +#endif + #ifdef CONFIG_PM static int vortex_suspend (struct pci_dev *pdev, u32 state) @@ -1463,6 +1475,9 @@ static int __devinit vortex_probe1(struc dev->set_multicast_list = set_rx_mode; dev->tx_timeout = vortex_tx_timeout; dev->watchdog_timeo = (watchdog * HZ) / 1000; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = poll_vortex; +#endif if (pdev && vp->enable_wol) { vp->pm_state_valid = 1; pci_save_state(VORTEX_PCI(vp), vp->power_state); @@ -2562,11 +2577,12 @@ boomerang_rx(struct net_device *dev) if (pkt_len < rx_copybreak && (skb = dev_alloc_skb(pkt_len + 2)) != 0) { skb->dev = dev; skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ - pci_dma_sync_single(VORTEX_PCI(vp), dma, PKT_BUF_SZ, PCI_DMA_FROMDEVICE); + pci_dma_sync_single_for_cpu(VORTEX_PCI(vp), dma, PKT_BUF_SZ, PCI_DMA_FROMDEVICE); /* 'skb_put()' points to the start of sk_buff data area. */ memcpy(skb_put(skb, pkt_len), vp->rx_skbuff[entry]->tail, pkt_len); + pci_dma_sync_single_for_device(VORTEX_PCI(vp), dma, PKT_BUF_SZ, PCI_DMA_FROMDEVICE); vp->rx_copy++; } else { /* Pass up the skbuff already on the Rx ring. */ --- linux-2.6.4-rc2/drivers/net/8390.c 2004-03-03 23:12:45.000000000 -0800 +++ 25/drivers/net/8390.c 2004-03-07 20:46:54.000000000 -0800 @@ -516,6 +516,15 @@ irqreturn_t ei_interrupt(int irq, void * return IRQ_RETVAL(nr_serviced > 0); } +#ifdef CONFIG_NET_POLL_CONTROLLER +void ei_poll(struct net_device *dev) +{ + disable_irq(dev->irq); + ei_interrupt(dev->irq, dev, NULL); + enable_irq(dev->irq); +} +#endif + /** * ei_tx_err - handle transmitter error * @dev: network device which threw the exception @@ -1124,6 +1133,9 @@ static void NS8390_trigger_send(struct n EXPORT_SYMBOL(ei_open); EXPORT_SYMBOL(ei_close); EXPORT_SYMBOL(ei_interrupt); +#ifdef CONFIG_NET_POLL_CONTROLLER +EXPORT_SYMBOL(ei_poll); +#endif EXPORT_SYMBOL(ei_tx_timeout); EXPORT_SYMBOL(NS8390_init); EXPORT_SYMBOL(__alloc_ei_netdev); --- linux-2.6.4-rc2/drivers/net/8390.h 2004-02-17 20:48:43.000000000 -0800 +++ 25/drivers/net/8390.h 2004-03-07 20:46:54.000000000 -0800 @@ -39,6 +39,10 @@ extern int ei_debug; #define ei_debug 1 #endif +#ifdef CONFIG_NET_POLL_CONTROLLER +extern void ei_poll(struct net_device *dev); +#endif + extern void NS8390_init(struct net_device *dev, int startp); extern int ei_open(struct net_device *dev); extern int ei_close(struct net_device *dev); --- linux-2.6.4-rc2/drivers/net/ac3200.c 2004-02-17 20:48:43.000000000 -0800 +++ 25/drivers/net/ac3200.c 2004-03-07 20:46:54.000000000 -0800 @@ -276,6 +276,9 @@ static int __init ac_probe1(int ioaddr, dev->open = &ac_open; dev->stop = &ac_close_card; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = ei_poll; +#endif NS8390_init(dev, 0); return 0; out1: --- linux-2.6.4-rc2/drivers/net/acenic.c 2004-03-03 23:12:45.000000000 -0800 +++ 25/drivers/net/acenic.c 2004-03-07 20:46:54.000000000 -0800 @@ -131,7 +131,6 @@ #define PCI_DEVICE_ID_SGI_ACENIC 0x0009 #endif -#if LINUX_VERSION_CODE >= 0x20400 static struct pci_device_id acenic_pci_tbl[] = { { PCI_VENDOR_ID_ALTEON, PCI_DEVICE_ID_ALTEON_ACENIC_FIBRE, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, @@ -156,37 +155,6 @@ static struct pci_device_id acenic_pci_t { } }; MODULE_DEVICE_TABLE(pci, acenic_pci_tbl); -#endif - - -#ifndef MODULE_LICENSE -#define MODULE_LICENSE(a) -#endif - -#ifndef wmb -#define wmb() mb() -#endif - -#ifndef __exit -#define __exit -#endif - -#ifndef __devinit -#define __devinit __init -#endif - -#ifndef SMP_CACHE_BYTES -#define SMP_CACHE_BYTES L1_CACHE_BYTES -#endif - -#ifndef SET_MODULE_OWNER -#define SET_MODULE_OWNER(dev) do{} while(0) -#define ACE_MOD_INC_USE_COUNT MOD_INC_USE_COUNT -#define ACE_MOD_DEC_USE_COUNT MOD_DEC_USE_COUNT -#else -#define ACE_MOD_INC_USE_COUNT do{} while(0) -#define ACE_MOD_DEC_USE_COUNT do{} while(0) -#endif #ifndef SET_NETDEV_DEV #define SET_NETDEV_DEV(net, pdev) do{} while(0) @@ -198,151 +166,8 @@ MODULE_DEVICE_TABLE(pci, acenic_pci_tbl) #define ace_sync_irq(irq) synchronize_irq() #endif -#if LINUX_VERSION_CODE < 0x2051e -#define local_irq_save(flags) do{__save_flags(flags) ; \ - __cli();} while(0) -#define local_irq_restore(flags) __restore_flags(flags) -#endif - -#if (LINUX_VERSION_CODE < 0x02030d) -#define pci_resource_start(dev, bar) dev->base_address[bar] -#elif (LINUX_VERSION_CODE < 0x02032c) -#define pci_resource_start(dev, bar) dev->resource[bar].start -#endif - -#if (LINUX_VERSION_CODE < 0x02030e) -#define net_device device -#endif - - -#if (LINUX_VERSION_CODE < 0x02032a) -typedef u32 dma_addr_t; - -static inline void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, - dma_addr_t *dma_handle) -{ - void *virt_ptr; - - virt_ptr = kmalloc(size, GFP_KERNEL); - if (!virt_ptr) - return NULL; - *dma_handle = virt_to_bus(virt_ptr); - return virt_ptr; -} - -#define pci_free_consistent(cookie, size, ptr, dma_ptr) kfree(ptr) -#define pci_map_page(cookie, page, off, size, dir) \ - virt_to_bus(page_address(page)+(off)) -#define pci_unmap_page(cookie, address, size, dir) -#define pci_set_dma_mask(dev, mask) \ - (((u64)(mask) & 0xffffffff00000000) == 0 ? 0 : -EIO) -#define pci_dma_supported(dev, mask) \ - (((u64)(mask) & 0xffffffff00000000) == 0 ? 1 : 0) - -#elif (LINUX_VERSION_CODE < 0x02040d) - -/* - * 2.4.13 introduced pci_map_page()/pci_unmap_page() - for 2.4.12 and prior, - * fall back on pci_map_single()/pci_unnmap_single(). - * - * We are guaranteed that the page is mapped at this point since - * pci_map_page() is only used upon valid struct skb's. - */ -static inline dma_addr_t -pci_map_page(struct pci_dev *cookie, struct page *page, unsigned long off, - size_t size, int dir) -{ - void *page_virt; - - page_virt = page_address(page); - if (!page_virt) - BUG(); - return pci_map_single(cookie, (page_virt + off), size, dir); -} -#define pci_unmap_page(cookie, dma_addr, size, dir) \ - pci_unmap_single(cookie, dma_addr, size, dir) -#endif - -#if (LINUX_VERSION_CODE < 0x020412) -#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME) -#define DECLARE_PCI_UNMAP_LEN(LEN_NAME) -#define pci_unmap_addr(PTR, ADDR_NAME) 0 -#define pci_unmap_addr_set(PTR, ADDR_NAME, VAL) do{} while(0) -#define pci_unmap_len(PTR, LEN_NAME) 0 -#define pci_unmap_len_set(PTR, LEN_NAME, VAL) do{} while(0) -#endif - - -#if (LINUX_VERSION_CODE < 0x02032b) -/* - * SoftNet - * - * For pre-softnet kernels we need to tell the upper layer not to - * re-enter start_xmit() while we are in there. However softnet - * guarantees not to enter while we are in there so there is no need - * to do the netif_stop_queue() dance unless the transmit queue really - * gets stuck. This should also improve performance according to tests - * done by Aman Singla. - */ -#define dev_kfree_skb_irq(a) dev_kfree_skb(a) -#define netif_wake_queue(dev) clear_bit(0, &dev->tbusy) -#define netif_stop_queue(dev) set_bit(0, &dev->tbusy) -#define late_stop_netif_stop_queue(dev) do{} while(0) -#define early_stop_netif_stop_queue(dev) test_and_set_bit(0,&dev->tbusy) -#define early_stop_netif_wake_queue(dev) netif_wake_queue(dev) - -static inline void netif_start_queue(struct net_device *dev) -{ - dev->tbusy = 0; - dev->interrupt = 0; - dev->start = 1; -} - -#define ace_mark_net_bh() mark_bh(NET_BH) -#define netif_queue_stopped(dev) dev->tbusy -#define netif_running(dev) dev->start -#define ace_if_down(dev) do{dev->start = 0;} while(0) - -#define tasklet_struct tq_struct -static inline void tasklet_schedule(struct tasklet_struct *tasklet) -{ - queue_task(tasklet, &tq_immediate); - mark_bh(IMMEDIATE_BH); -} - -static inline void tasklet_init(struct tasklet_struct *tasklet, - void (*func)(unsigned long), - unsigned long data) -{ - tasklet->next = NULL; - tasklet->sync = 0; - tasklet->routine = (void (*)(void *))func; - tasklet->data = (void *)data; -} -#define tasklet_kill(tasklet) do{} while(0) -#else -#define late_stop_netif_stop_queue(dev) netif_stop_queue(dev) -#define early_stop_netif_stop_queue(dev) 0 -#define early_stop_netif_wake_queue(dev) do{} while(0) -#define ace_mark_net_bh() do{} while(0) -#define ace_if_down(dev) do{} while(0) -#endif - -#if (LINUX_VERSION_CODE >= 0x02031b) -#define NEW_NETINIT -#define ACE_PROBE_ARG void -#else -#define ACE_PROBE_ARG struct net_device *dev -#endif - -#ifndef min_t -#define min_t(type,a,b) (((a)<(b))?(a):(b)) -#endif - -#ifndef ARCH_HAS_PREFETCHW -#ifndef prefetchw -#define prefetchw(x) do{} while(0) -#endif +#ifndef offset_in_page +#define offset_in_page(ptr) ((unsigned long)(ptr) & ~PAGE_MASK) #endif #define ACE_MAX_MOD_PARMS 8 @@ -595,407 +420,323 @@ static int max_rx_desc[ACE_MAX_MOD_PARMS static int tx_ratio[ACE_MAX_MOD_PARMS]; static int dis_pci_mem_inval[ACE_MAX_MOD_PARMS] = {1, 1, 1, 1, 1, 1, 1, 1}; +MODULE_AUTHOR("Jes Sorensen "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("AceNIC/3C985/GA620 Gigabit Ethernet driver"); +MODULE_PARM(link, "1-" __MODULE_STRING(8) "i"); +MODULE_PARM(trace, "1-" __MODULE_STRING(8) "i"); +MODULE_PARM(tx_coal_tick, "1-" __MODULE_STRING(8) "i"); +MODULE_PARM(max_tx_desc, "1-" __MODULE_STRING(8) "i"); +MODULE_PARM(rx_coal_tick, "1-" __MODULE_STRING(8) "i"); +MODULE_PARM(max_rx_desc, "1-" __MODULE_STRING(8) "i"); +MODULE_PARM(tx_ratio, "1-" __MODULE_STRING(8) "i"); +MODULE_PARM_DESC(link, "AceNIC/3C985/NetGear link state"); +MODULE_PARM_DESC(trace, "AceNIC/3C985/NetGear firmware trace level"); +MODULE_PARM_DESC(tx_coal_tick, "AceNIC/3C985/GA620 max clock ticks to wait from first tx descriptor arrives"); +MODULE_PARM_DESC(max_tx_desc, "AceNIC/3C985/GA620 max number of transmit descriptors to wait"); +MODULE_PARM_DESC(rx_coal_tick, "AceNIC/3C985/GA620 max clock ticks to wait from first rx descriptor arrives"); +MODULE_PARM_DESC(max_rx_desc, "AceNIC/3C985/GA620 max number of receive descriptors to wait"); +MODULE_PARM_DESC(tx_ratio, "AceNIC/3C985/GA620 ratio of NIC memory used for TX/RX descriptors (range 0-63)"); + + static char version[] __initdata = "acenic.c: v0.92 08/05/2002 Jes Sorensen, linux-acenic@SunSITE.dk\n" " http://home.cern.ch/~jes/gige/acenic.html\n"; -static struct net_device *root_dev; - -static int probed __initdata = 0; - - -int __devinit acenic_probe (ACE_PROBE_ARG) +static int __devinit acenic_probe_one(struct pci_dev *pdev, + const struct pci_device_id *id) { -#ifdef NEW_NETINIT struct net_device *dev; -#endif struct ace_private *ap; - struct pci_dev *pdev = NULL; - int boards_found = 0; - int version_disp; - - if (probed) - return -ENODEV; - probed++; - - version_disp = 0; - - while ((pdev = pci_find_class(PCI_CLASS_NETWORK_ETHERNET<<8, pdev))) { - - if (!((pdev->vendor == PCI_VENDOR_ID_ALTEON) && - ((pdev->device == PCI_DEVICE_ID_ALTEON_ACENIC_FIBRE) || - (pdev->device == PCI_DEVICE_ID_ALTEON_ACENIC_COPPER)))&& - !((pdev->vendor == PCI_VENDOR_ID_3COM) && - (pdev->device == PCI_DEVICE_ID_3COM_3C985)) && - !((pdev->vendor == PCI_VENDOR_ID_NETGEAR) && - ((pdev->device == PCI_DEVICE_ID_NETGEAR_GA620) || - (pdev->device == PCI_DEVICE_ID_NETGEAR_GA620T))) && - /* - * Farallon used the DEC vendor ID on their cards by - * mistake for a while - */ - !((pdev->vendor == PCI_VENDOR_ID_DEC) && - (pdev->device == PCI_DEVICE_ID_FARALLON_PN9000SX)) && - !((pdev->vendor == PCI_VENDOR_ID_ALTEON) && - (pdev->device == PCI_DEVICE_ID_FARALLON_PN9100T)) && - !((pdev->vendor == PCI_VENDOR_ID_SGI) && - (pdev->device == PCI_DEVICE_ID_SGI_ACENIC))) - continue; - - dev = alloc_etherdev(sizeof(struct ace_private)); - if (dev == NULL) { - printk(KERN_ERR "acenic: Unable to allocate " - "net_device structure!\n"); - break; - } + static int boards_found; - SET_MODULE_OWNER(dev); - SET_NETDEV_DEV(dev, &pdev->dev); + dev = alloc_etherdev(sizeof(struct ace_private)); + if (dev == NULL) { + printk(KERN_ERR "acenic: Unable to allocate " + "net_device structure!\n"); + return -ENOMEM; + } - ap = dev->priv; - ap->pdev = pdev; + SET_MODULE_OWNER(dev); + SET_NETDEV_DEV(dev, &pdev->dev); - dev->open = &ace_open; - dev->hard_start_xmit = &ace_start_xmit; - dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM; + ap = dev->priv; + ap->pdev = pdev; + + dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM; #if ACENIC_DO_VLAN - dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; - dev->vlan_rx_register = ace_vlan_rx_register; - dev->vlan_rx_kill_vid = ace_vlan_rx_kill_vid; -#endif - if (1) { - static void ace_watchdog(struct net_device *dev); - dev->tx_timeout = &ace_watchdog; - dev->watchdog_timeo = 5*HZ; - } - dev->stop = &ace_close; - dev->get_stats = &ace_get_stats; - dev->set_multicast_list = &ace_set_multicast_list; - dev->do_ioctl = &ace_ioctl; - dev->set_mac_address = &ace_set_mac_addr; - dev->change_mtu = &ace_change_mtu; + dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; + dev->vlan_rx_register = ace_vlan_rx_register; + dev->vlan_rx_kill_vid = ace_vlan_rx_kill_vid; +#endif + if (1) { + static void ace_watchdog(struct net_device *dev); + dev->tx_timeout = &ace_watchdog; + dev->watchdog_timeo = 5*HZ; + } - /* display version info if adapter is found */ - if (!version_disp) - { - /* set display flag to TRUE so that */ - /* we only display this string ONCE */ - version_disp = 1; - printk(version); - } + dev->open = &ace_open; + dev->stop = &ace_close; + dev->hard_start_xmit = &ace_start_xmit; + dev->get_stats = &ace_get_stats; + dev->set_multicast_list = &ace_set_multicast_list; + dev->do_ioctl = &ace_ioctl; + dev->set_mac_address = &ace_set_mac_addr; + dev->change_mtu = &ace_change_mtu; - if (pci_enable_device(pdev)) { - free_netdev(dev); - continue; - } + /* we only display this string ONCE */ + if (!boards_found) + printk(version); - /* - * Enable master mode before we start playing with the - * pci_command word since pci_set_master() will modify - * it. - */ - pci_set_master(pdev); + if (pci_enable_device(pdev)) + goto fail_free_netdev; - pci_read_config_word(pdev, PCI_COMMAND, &ap->pci_command); + /* + * Enable master mode before we start playing with the + * pci_command word since pci_set_master() will modify + * it. + */ + pci_set_master(pdev); - /* OpenFirmware on Mac's does not set this - DOH.. */ - if (!(ap->pci_command & PCI_COMMAND_MEMORY)) { - printk(KERN_INFO "%s: Enabling PCI Memory Mapped " - "access - was not enabled by BIOS/Firmware\n", - dev->name); - ap->pci_command = ap->pci_command | PCI_COMMAND_MEMORY; - pci_write_config_word(ap->pdev, PCI_COMMAND, - ap->pci_command); - wmb(); - } + pci_read_config_word(pdev, PCI_COMMAND, &ap->pci_command); - pci_read_config_byte(pdev, PCI_LATENCY_TIMER, - &ap->pci_latency); - if (ap->pci_latency <= 0x40) { - ap->pci_latency = 0x40; - pci_write_config_byte(pdev, PCI_LATENCY_TIMER, - ap->pci_latency); - } + /* OpenFirmware on Mac's does not set this - DOH.. */ + if (!(ap->pci_command & PCI_COMMAND_MEMORY)) { + printk(KERN_INFO "%s: Enabling PCI Memory Mapped " + "access - was not enabled by BIOS/Firmware\n", + dev->name); + ap->pci_command = ap->pci_command | PCI_COMMAND_MEMORY; + pci_write_config_word(ap->pdev, PCI_COMMAND, + ap->pci_command); + wmb(); + } - /* - * Remap the regs into kernel space - this is abuse of - * dev->base_addr since it was means for I/O port - * addresses but who gives a damn. - */ - dev->base_addr = pci_resource_start(pdev, 0); - ap->regs = (struct ace_regs *)ioremap(dev->base_addr, 0x4000); - if (!ap->regs) { - printk(KERN_ERR "%s: Unable to map I/O register, " - "AceNIC %i will be disabled.\n", - dev->name, boards_found); - break; - } + pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &ap->pci_latency); + if (ap->pci_latency <= 0x40) { + ap->pci_latency = 0x40; + pci_write_config_byte(pdev, PCI_LATENCY_TIMER, ap->pci_latency); + } - switch(pdev->vendor) { - case PCI_VENDOR_ID_ALTEON: - if (pdev->device == PCI_DEVICE_ID_FARALLON_PN9100T) { - strncpy(ap->name, "Farallon PN9100-T " - "Gigabit Ethernet", sizeof (ap->name)); - printk(KERN_INFO "%s: Farallon PN9100-T ", - dev->name); - } else { - strncpy(ap->name, "AceNIC Gigabit Ethernet", - sizeof (ap->name)); - printk(KERN_INFO "%s: Alteon AceNIC ", - dev->name); - } - break; - case PCI_VENDOR_ID_3COM: - strncpy(ap->name, "3Com 3C985 Gigabit Ethernet", - sizeof (ap->name)); - printk(KERN_INFO "%s: 3Com 3C985 ", dev->name); - break; - case PCI_VENDOR_ID_NETGEAR: - strncpy(ap->name, "NetGear GA620 Gigabit Ethernet", - sizeof (ap->name)); - printk(KERN_INFO "%s: NetGear GA620 ", dev->name); - break; - case PCI_VENDOR_ID_DEC: - if (pdev->device == PCI_DEVICE_ID_FARALLON_PN9000SX) { - strncpy(ap->name, "Farallon PN9000-SX " - "Gigabit Ethernet", sizeof (ap->name)); - printk(KERN_INFO "%s: Farallon PN9000-SX ", - dev->name); - break; - } - case PCI_VENDOR_ID_SGI: - strncpy(ap->name, "SGI AceNIC Gigabit Ethernet", + /* + * Remap the regs into kernel space - this is abuse of + * dev->base_addr since it was means for I/O port + * addresses but who gives a damn. + */ + dev->base_addr = pci_resource_start(pdev, 0); + ap->regs = (struct ace_regs *)ioremap(dev->base_addr, 0x4000); + if (!ap->regs) { + printk(KERN_ERR "%s: Unable to map I/O register, " + "AceNIC %i will be disabled.\n", + dev->name, boards_found); + goto fail_free_netdev; + } + + switch(pdev->vendor) { + case PCI_VENDOR_ID_ALTEON: + if (pdev->device == PCI_DEVICE_ID_FARALLON_PN9100T) { + strncpy(ap->name, "Farallon PN9100-T " + "Gigabit Ethernet", sizeof (ap->name)); + printk(KERN_INFO "%s: Farallon PN9100-T ", + dev->name); + } else { + strncpy(ap->name, "AceNIC Gigabit Ethernet", sizeof (ap->name)); - printk(KERN_INFO "%s: SGI AceNIC ", dev->name); - break; - default: - strncpy(ap->name, "Unknown AceNIC based Gigabit " - "Ethernet", sizeof (ap->name)); - printk(KERN_INFO "%s: Unknown AceNIC ", dev->name); + printk(KERN_INFO "%s: Alteon AceNIC ", + dev->name); + } + break; + case PCI_VENDOR_ID_3COM: + strncpy(ap->name, "3Com 3C985 Gigabit Ethernet", + sizeof (ap->name)); + printk(KERN_INFO "%s: 3Com 3C985 ", dev->name); + break; + case PCI_VENDOR_ID_NETGEAR: + strncpy(ap->name, "NetGear GA620 Gigabit Ethernet", + sizeof (ap->name)); + printk(KERN_INFO "%s: NetGear GA620 ", dev->name); + break; + case PCI_VENDOR_ID_DEC: + if (pdev->device == PCI_DEVICE_ID_FARALLON_PN9000SX) { + strncpy(ap->name, "Farallon PN9000-SX " + "Gigabit Ethernet", sizeof (ap->name)); + printk(KERN_INFO "%s: Farallon PN9000-SX ", + dev->name); break; } - ap->name [sizeof (ap->name) - 1] = '\0'; - printk("Gigabit Ethernet at 0x%08lx, ", dev->base_addr); + case PCI_VENDOR_ID_SGI: + strncpy(ap->name, "SGI AceNIC Gigabit Ethernet", + sizeof (ap->name)); + printk(KERN_INFO "%s: SGI AceNIC ", dev->name); + break; + default: + strncpy(ap->name, "Unknown AceNIC based Gigabit " + "Ethernet", sizeof (ap->name)); + printk(KERN_INFO "%s: Unknown AceNIC ", dev->name); + break; + } + + ap->name [sizeof (ap->name) - 1] = '\0'; + printk("Gigabit Ethernet at 0x%08lx, ", dev->base_addr); #ifdef __sparc__ - printk("irq %s\n", __irq_itoa(pdev->irq)); + printk("irq %s\n", __irq_itoa(pdev->irq)); #else - printk("irq %i\n", pdev->irq); + printk("irq %i\n", pdev->irq); #endif #ifdef CONFIG_ACENIC_OMIT_TIGON_I - if ((readl(&ap->regs->HostCtrl) >> 28) == 4) { - printk(KERN_ERR "%s: Driver compiled without Tigon I" - " support - NIC disabled\n", dev->name); - ace_init_cleanup(dev); - free_netdev(dev); - continue; - } + if ((readl(&ap->regs->HostCtrl) >> 28) == 4) { + printk(KERN_ERR "%s: Driver compiled without Tigon I" + " support - NIC disabled\n", dev->name); + goto fail_uninit; + } #endif - if (ace_allocate_descriptors(dev)) { - /* - * ace_allocate_descriptors() calls - * ace_init_cleanup() on error. - */ - free_netdev(dev); - continue; - } + if (ace_allocate_descriptors(dev)) + goto fail_free_netdev; #ifdef MODULE - if (boards_found >= ACE_MAX_MOD_PARMS) - ap->board_idx = BOARD_IDX_OVERFLOW; - else - ap->board_idx = boards_found; + if (boards_found >= ACE_MAX_MOD_PARMS) + ap->board_idx = BOARD_IDX_OVERFLOW; + else + ap->board_idx = boards_found; #else - ap->board_idx = BOARD_IDX_STATIC; + ap->board_idx = BOARD_IDX_STATIC; #endif - if (ace_init(dev)) { - /* - * ace_init() calls ace_init_cleanup() on error. - */ - free_netdev(dev); - continue; - } + if (ace_init(dev)) + goto fail_free_netdev; - if (register_netdev(dev)) { - printk(KERN_ERR "acenic: device registration failed\n"); - ace_init_cleanup(dev); - free_netdev(dev); - continue; - } - - if (ap->pci_using_dac) - dev->features |= NETIF_F_HIGHDMA; - - boards_found++; + if (register_netdev(dev)) { + printk(KERN_ERR "acenic: device registration failed\n"); + goto fail_uninit; } - /* - * If we're at this point we're going through ace_probe() for - * the first time. Return success (0) if we've initialized 1 - * or more boards. Otherwise, return failure (-ENODEV). - */ - - if (boards_found > 0) - return 0; - else - return -ENODEV; -} + if (ap->pci_using_dac) + dev->features |= NETIF_F_HIGHDMA; + pci_set_drvdata(pdev, dev); -#ifdef MODULE -MODULE_AUTHOR("Jes Sorensen "); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("AceNIC/3C985/GA620 Gigabit Ethernet driver"); -MODULE_PARM(link, "1-" __MODULE_STRING(8) "i"); -MODULE_PARM(trace, "1-" __MODULE_STRING(8) "i"); -MODULE_PARM(tx_coal_tick, "1-" __MODULE_STRING(8) "i"); -MODULE_PARM(max_tx_desc, "1-" __MODULE_STRING(8) "i"); -MODULE_PARM(rx_coal_tick, "1-" __MODULE_STRING(8) "i"); -MODULE_PARM(max_rx_desc, "1-" __MODULE_STRING(8) "i"); -MODULE_PARM(tx_ratio, "1-" __MODULE_STRING(8) "i"); -MODULE_PARM_DESC(link, "AceNIC/3C985/NetGear link state"); -MODULE_PARM_DESC(trace, "AceNIC/3C985/NetGear firmware trace level"); -MODULE_PARM_DESC(tx_coal_tick, "AceNIC/3C985/GA620 max clock ticks to wait from first tx descriptor arrives"); -MODULE_PARM_DESC(max_tx_desc, "AceNIC/3C985/GA620 max number of transmit descriptors to wait"); -MODULE_PARM_DESC(rx_coal_tick, "AceNIC/3C985/GA620 max clock ticks to wait from first rx descriptor arrives"); -MODULE_PARM_DESC(max_rx_desc, "AceNIC/3C985/GA620 max number of receive descriptors to wait"); -MODULE_PARM_DESC(tx_ratio, "AceNIC/3C985/GA620 ratio of NIC memory used for TX/RX descriptors (range 0-63)"); -#endif + boards_found++; + return 0; + fail_uninit: + ace_init_cleanup(dev); + fail_free_netdev: + free_netdev(dev); + return -ENODEV; +} -static void __exit ace_module_cleanup(void) +static void __devexit acenic_remove_one(struct pci_dev *pdev) { - struct ace_private *ap; - struct ace_regs *regs; - struct net_device *next; + struct net_device *dev = pci_get_drvdata(pdev); + struct ace_private *ap = dev->priv; + struct ace_regs *regs = ap->regs; short i; - while (root_dev) { - ap = root_dev->priv; - next = ap->next; - unregister_netdev(root_dev); + unregister_netdev(dev); - regs = ap->regs; - - writel(readl(®s->CpuCtrl) | CPU_HALT, ®s->CpuCtrl); - if (ap->version >= 2) - writel(readl(®s->CpuBCtrl) | CPU_HALT, - ®s->CpuBCtrl); - /* - * This clears any pending interrupts - */ - writel(1, ®s->Mb0Lo); - readl(®s->CpuCtrl); /* flush */ + writel(readl(®s->CpuCtrl) | CPU_HALT, ®s->CpuCtrl); + if (ap->version >= 2) + writel(readl(®s->CpuBCtrl) | CPU_HALT, ®s->CpuBCtrl); + + /* + * This clears any pending interrupts + */ + writel(1, ®s->Mb0Lo); + readl(®s->CpuCtrl); /* flush */ - /* - * Make sure no other CPUs are processing interrupts - * on the card before the buffers are being released. - * Otherwise one might experience some `interesting' - * effects. - * - * Then release the RX buffers - jumbo buffers were - * already released in ace_close(). - */ - ace_sync_irq(root_dev->irq); + /* + * Make sure no other CPUs are processing interrupts + * on the card before the buffers are being released. + * Otherwise one might experience some `interesting' + * effects. + * + * Then release the RX buffers - jumbo buffers were + * already released in ace_close(). + */ + ace_sync_irq(dev->irq); - for (i = 0; i < RX_STD_RING_ENTRIES; i++) { - struct sk_buff *skb = ap->skb->rx_std_skbuff[i].skb; + for (i = 0; i < RX_STD_RING_ENTRIES; i++) { + struct sk_buff *skb = ap->skb->rx_std_skbuff[i].skb; - if (skb) { - struct ring_info *ringp; - dma_addr_t mapping; + if (skb) { + struct ring_info *ringp; + dma_addr_t mapping; - ringp = &ap->skb->rx_std_skbuff[i]; - mapping = pci_unmap_addr(ringp, mapping); - pci_unmap_page(ap->pdev, mapping, - ACE_STD_BUFSIZE - (2 + 16), - PCI_DMA_FROMDEVICE); + ringp = &ap->skb->rx_std_skbuff[i]; + mapping = pci_unmap_addr(ringp, mapping); + pci_unmap_page(ap->pdev, mapping, + ACE_STD_BUFSIZE - (2 + 16), + PCI_DMA_FROMDEVICE); - ap->rx_std_ring[i].size = 0; - ap->skb->rx_std_skbuff[i].skb = NULL; - dev_kfree_skb(skb); - } - } - if (ap->version >= 2) { - for (i = 0; i < RX_MINI_RING_ENTRIES; i++) { - struct sk_buff *skb = ap->skb->rx_mini_skbuff[i].skb; - - if (skb) { - struct ring_info *ringp; - dma_addr_t mapping; - - ringp = &ap->skb->rx_mini_skbuff[i]; - mapping = pci_unmap_addr(ringp,mapping); - pci_unmap_page(ap->pdev, mapping, - ACE_MINI_BUFSIZE - (2 + 16), - PCI_DMA_FROMDEVICE); - - ap->rx_mini_ring[i].size = 0; - ap->skb->rx_mini_skbuff[i].skb = NULL; - dev_kfree_skb(skb); - } - } + ap->rx_std_ring[i].size = 0; + ap->skb->rx_std_skbuff[i].skb = NULL; + dev_kfree_skb(skb); } - for (i = 0; i < RX_JUMBO_RING_ENTRIES; i++) { - struct sk_buff *skb = ap->skb->rx_jumbo_skbuff[i].skb; + } + + if (ap->version >= 2) { + for (i = 0; i < RX_MINI_RING_ENTRIES; i++) { + struct sk_buff *skb = ap->skb->rx_mini_skbuff[i].skb; + if (skb) { struct ring_info *ringp; dma_addr_t mapping; - ringp = &ap->skb->rx_jumbo_skbuff[i]; - mapping = pci_unmap_addr(ringp, mapping); + ringp = &ap->skb->rx_mini_skbuff[i]; + mapping = pci_unmap_addr(ringp,mapping); pci_unmap_page(ap->pdev, mapping, - ACE_JUMBO_BUFSIZE - (2 + 16), + ACE_MINI_BUFSIZE - (2 + 16), PCI_DMA_FROMDEVICE); - ap->rx_jumbo_ring[i].size = 0; - ap->skb->rx_jumbo_skbuff[i].skb = NULL; + ap->rx_mini_ring[i].size = 0; + ap->skb->rx_mini_skbuff[i].skb = NULL; dev_kfree_skb(skb); } } - - ace_init_cleanup(root_dev); - free_netdev(root_dev); - root_dev = next; } -} + for (i = 0; i < RX_JUMBO_RING_ENTRIES; i++) { + struct sk_buff *skb = ap->skb->rx_jumbo_skbuff[i].skb; + if (skb) { + struct ring_info *ringp; + dma_addr_t mapping; -int __init ace_module_init(void) -{ - int status; + ringp = &ap->skb->rx_jumbo_skbuff[i]; + mapping = pci_unmap_addr(ringp, mapping); + pci_unmap_page(ap->pdev, mapping, + ACE_JUMBO_BUFSIZE - (2 + 16), + PCI_DMA_FROMDEVICE); - root_dev = NULL; + ap->rx_jumbo_ring[i].size = 0; + ap->skb->rx_jumbo_skbuff[i].skb = NULL; + dev_kfree_skb(skb); + } + } -#ifdef NEW_NETINIT - status = acenic_probe(); -#else - status = acenic_probe(NULL); -#endif - return status; + ace_init_cleanup(dev); + free_netdev(dev); } +static struct pci_driver acenic_pci_driver = { + .name = "acenic", + .id_table = acenic_pci_tbl, + .probe = acenic_probe_one, + .remove = __devexit_p(acenic_remove_one), +}; -#if (LINUX_VERSION_CODE < 0x02032a) -#ifdef MODULE -int init_module(void) +static int __init acenic_init(void) { - return ace_module_init(); + return pci_module_init(&acenic_pci_driver); } - -void cleanup_module(void) +static void __exit acenic_exit(void) { - ace_module_cleanup(); + pci_unregister_driver(&acenic_pci_driver); } -#endif -#else -module_init(ace_module_init); -module_exit(ace_module_cleanup); -#endif +module_init(acenic_init); +module_exit(acenic_exit); static void ace_free_descriptors(struct net_device *dev) { @@ -1462,13 +1203,6 @@ static int __init ace_init(struct net_de } else dev->irq = pdev->irq; - /* - * Register the device here to be able to catch allocated - * interrupt handlers in case the firmware doesn't come up. - */ - ap->next = root_dev; - root_dev = dev; - #ifdef INDEX_DEBUG spin_lock_init(&ap->debug_lock); ap->last_tx = ACE_TX_RING_ENTRIES(ap) - 1; @@ -2642,8 +2376,6 @@ static int ace_open(struct net_device *d netif_start_queue(dev); - ACE_MOD_INC_USE_COUNT; - /* * Setup the bottom half rx ring refill handler */ @@ -2660,8 +2392,6 @@ static int ace_close(struct net_device * unsigned long flags; short i; - ace_if_down(dev); - /* * Without (or before) releasing irq and stopping hardware, this * is an absolute non-sense, by the way. It will be reset instantly @@ -2733,7 +2463,6 @@ static int ace_close(struct net_device * ace_unmask_irq(dev); local_irq_restore(flags); - ACE_MOD_DEC_USE_COUNT; return 0; } @@ -2790,12 +2519,6 @@ static int ace_start_xmit(struct sk_buff struct tx_desc *desc; u32 idx, flagsize; - /* - * This only happens with pre-softnet, ie. 2.2.x kernels. - */ - if (early_stop_netif_stop_queue(dev)) - return 1; - restart: idx = ap->tx_prd; --- linux-2.6.4-rc2/drivers/net/amd8111e.c 2004-03-03 23:12:45.000000000 -0800 +++ 25/drivers/net/amd8111e.c 2004-03-07 20:46:54.000000000 -0800 @@ -1153,6 +1153,17 @@ err_no_interrupt: return IRQ_RETVAL(handled); } +#ifdef CONFIG_NET_POLL_CONTROLLER +static void amd8111e_poll(struct net_device *dev) +{ + unsigned long flags; + local_save_flags(flags); + local_irq_disable(); + amd8111e_interrupt(0, dev, NULL); + local_irq_restore(flags); +} +#endif + /* This function closes the network interface and updates the statistics so that most recent statistics will be available after the interface is down. */ @@ -1884,6 +1895,9 @@ static int __devinit amd8111e_probe_one( dev->irq =pdev->irq; dev->tx_timeout = amd8111e_tx_timeout; dev->watchdog_timeo = AMD8111E_TX_TIMEOUT; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = amd8111e_poll; +#endif #if AMD8111E_VLAN_TAG_USED dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; --- linux-2.6.4-rc2/drivers/net/apne.c 2004-03-03 23:12:45.000000000 -0800 +++ 25/drivers/net/apne.c 2004-03-07 20:46:54.000000000 -0800 @@ -333,6 +333,9 @@ static int __init apne_probe1(struct net ei_status.get_8390_hdr = &apne_get_8390_hdr; dev->open = &apne_open; dev->stop = &apne_close; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = ei_poll; +#endif NS8390_init(dev, 0); pcmcia_ack_int(pcmcia_get_intreq()); /* ack PCMCIA int req */ --- linux-2.6.4-rc2/drivers/net/b44.c 2003-11-23 19:03:00.000000000 -0800 +++ 25/drivers/net/b44.c 2004-03-07 20:46:58.000000000 -0800 @@ -667,6 +667,10 @@ static void b44_recycle_rx(struct b44 *b dest_desc->ctrl = ctrl; dest_desc->addr = src_desc->addr; src_map->skb = NULL; + + pci_dma_sync_single_for_device(bp->pdev, src_desc->addr, + RX_PKT_BUF_SZ, + PCI_DMA_FROMDEVICE); } static int b44_rx(struct b44 *bp, int budget) @@ -686,9 +690,9 @@ static int b44_rx(struct b44 *bp, int bu struct rx_header *rh; u16 len; - pci_dma_sync_single(bp->pdev, map, - RX_PKT_BUF_SZ, - PCI_DMA_FROMDEVICE); + pci_dma_sync_single_for_cpu(bp->pdev, map, + RX_PKT_BUF_SZ, + PCI_DMA_FROMDEVICE); rh = (struct rx_header *) skb->data; len = cpu_to_le16(rh->len); if ((len > (RX_PKT_BUF_SZ - bp->rx_offset)) || --- linux-2.6.4-rc2/drivers/net/dl2k.c 2004-02-17 20:48:43.000000000 -0800 +++ 25/drivers/net/dl2k.c 2004-03-07 20:46:58.000000000 -0800 @@ -874,8 +874,6 @@ receive_packet (struct net_device *dev) frame_status = le64_to_cpu (desc->status); if (--cnt < 0) break; - pci_dma_sync_single (np->pdev, desc->fraginfo, np->rx_buf_sz, - PCI_DMA_FROMDEVICE); /* Update rx error statistics, drop packet. */ if (frame_status & RFS_Errors) { np->stats.rx_errors++; @@ -898,6 +896,10 @@ receive_packet (struct net_device *dev) skb_put (skb = np->rx_skbuff[entry], pkt_len); np->rx_skbuff[entry] = NULL; } else if ((skb = dev_alloc_skb (pkt_len + 2)) != NULL) { + pci_dma_sync_single_for_cpu(np->pdev, + desc->fraginfo, + np->rx_buf_sz, + PCI_DMA_FROMDEVICE); skb->dev = dev; /* 16 byte align the IP header */ skb_reserve (skb, 2); @@ -905,6 +907,10 @@ receive_packet (struct net_device *dev) np->rx_skbuff[entry]->tail, pkt_len, 0); skb_put (skb, pkt_len); + pci_dma_sync_single_for_device(np->pdev, + desc->fraginfo, + np->rx_buf_sz, + PCI_DMA_FROMDEVICE); } skb->protocol = eth_type_trans (skb, dev); #if 0 --- linux-2.6.4-rc2/drivers/net/e1000/e1000_ethtool.c 2004-02-17 20:48:43.000000000 -0800 +++ 25/drivers/net/e1000/e1000_ethtool.c 2004-03-07 20:46:58.000000000 -0800 @@ -1191,16 +1191,16 @@ e1000_run_loopback_test(struct e1000_ada for(i = 0; i < 64; i++) { e1000_create_lbtest_frame(txdr->buffer_info[i].skb, 1024); - pci_dma_sync_single(pdev, txdr->buffer_info[i].dma, - txdr->buffer_info[i].length, - PCI_DMA_TODEVICE); + pci_dma_sync_single_for_device(pdev, txdr->buffer_info[i].dma, + txdr->buffer_info[i].length, + PCI_DMA_TODEVICE); } E1000_WRITE_REG(&adapter->hw, TDT, i); msec_delay(200); - pci_dma_sync_single(pdev, rxdr->buffer_info[0].dma, - rxdr->buffer_info[0].length, PCI_DMA_FROMDEVICE); + pci_dma_sync_single_for_cpu(pdev, rxdr->buffer_info[0].dma, + rxdr->buffer_info[0].length, PCI_DMA_FROMDEVICE); return e1000_check_lbtest_frame(rxdr->buffer_info[0].skb, 1024); } --- linux-2.6.4-rc2/drivers/net/e100.c 2004-03-03 23:12:45.000000000 -0800 +++ 25/drivers/net/e100.c 2004-03-07 20:46:58.000000000 -0800 @@ -158,7 +158,7 @@ #define DRV_NAME "e100" -#define DRV_VERSION "3.0.15" +#define DRV_VERSION "3.0.16" #define DRV_DESCRIPTION "Intel(R) PRO/100 Network Driver" #define DRV_COPYRIGHT "Copyright(c) 1999-2004 Intel Corporation" #define PFX DRV_NAME ": " @@ -1032,8 +1032,9 @@ static int e100_phy_init(struct nic *nic nic->phy = (u32)id_hi << 16 | (u32)id_lo; DPRINTK(HW, DEBUG, "phy ID = 0x%08X\n", nic->phy); - /* Handle National tx phy */ - if(nic->phy == phy_nsc_tx) { + /* Handle National tx phys */ +#define NCS_PHY_MODEL_MASK 0xFFF0FFFF + if((nic->phy & NCS_PHY_MODEL_MASK) == phy_nsc_tx) { /* Disable congestion control */ cong = mdio_read(netdev, nic->mii.phy_id, MII_NSC_CONG); cong |= NSC_CONG_TXREADY; @@ -1386,8 +1387,8 @@ static inline int e100_rx_alloc_skb(stru (u32 *)&prev_rfd->link); wmb(); prev_rfd->command &= ~cpu_to_le16(cb_el); - pci_dma_sync_single(nic->pdev, rx->prev->dma_addr, - sizeof(struct rfd), PCI_DMA_TODEVICE); + pci_dma_sync_single_for_device(nic->pdev, rx->prev->dma_addr, + sizeof(struct rfd), PCI_DMA_TODEVICE); } return 0; @@ -1404,8 +1405,8 @@ static inline int e100_rx_indicate(struc return -EAGAIN; /* Need to sync before taking a peek at cb_complete bit */ - pci_dma_sync_single(nic->pdev, rx->dma_addr, - sizeof(struct rfd), PCI_DMA_FROMDEVICE); + pci_dma_sync_single_for_cpu(nic->pdev, rx->dma_addr, + sizeof(struct rfd), PCI_DMA_FROMDEVICE); rfd_status = le16_to_cpu(rfd->status); DPRINTK(RX_STATUS, DEBUG, "status=0x%04X\n", rfd_status); @@ -1420,11 +1421,8 @@ static inline int e100_rx_indicate(struc actual_size = RFD_BUF_LEN - sizeof(struct rfd); /* Get data */ - pci_dma_sync_single(nic->pdev, rx->dma_addr, - sizeof(struct rfd) + actual_size, - PCI_DMA_FROMDEVICE); pci_unmap_single(nic->pdev, rx->dma_addr, - RFD_BUF_LEN, PCI_DMA_FROMDEVICE); + RFD_BUF_LEN, PCI_DMA_FROMDEVICE); /* Pull off the RFD and put the actual data (minus eth hdr) */ skb_reserve(skb, sizeof(struct rfd)); --- linux-2.6.4-rc2/drivers/net/e2100.c 2004-02-17 20:48:43.000000000 -0800 +++ 25/drivers/net/e2100.c 2004-03-07 20:46:54.000000000 -0800 @@ -269,6 +269,9 @@ static int __init e21_probe1(struct net_ ei_status.get_8390_hdr = &e21_get_8390_hdr; dev->open = &e21_open; dev->stop = &e21_close; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = ei_poll; +#endif NS8390_init(dev, 0); return 0; --- linux-2.6.4-rc2/drivers/net/eepro100.c 2003-11-09 16:45:05.000000000 -0800 +++ 25/drivers/net/eepro100.c 2004-03-07 20:46:58.000000000 -0800 @@ -654,6 +654,23 @@ err_out_none: return -ENODEV; } +#ifdef CONFIG_NET_POLL_CONTROLLER +/* + * Polling 'interrupt' - used by things like netconsole to send skbs + * without having to re-enable interrupts. It's not called while + * the interrupt routine is executing. + */ + +static void poll_speedo (struct net_device *dev) +{ + /* disable_irq is not very nice, but with the funny lockless design + we have no other choice. */ + disable_irq(dev->irq); + speedo_interrupt (dev->irq, dev, NULL); + enable_irq(dev->irq); +} +#endif + static int __devinit speedo_found1(struct pci_dev *pdev, long ioaddr, int card_idx, int acpi_idle_state) { @@ -885,6 +902,9 @@ static int __devinit speedo_found1(struc dev->get_stats = &speedo_get_stats; dev->set_multicast_list = &set_rx_mode; dev->do_ioctl = &speedo_ioctl; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = &poll_speedo; +#endif if (register_netdevice(dev)) goto err_free_unlock; @@ -1306,8 +1326,8 @@ speedo_init_rx_ring(struct net_device *d skb_reserve(skb, sizeof(struct RxFD)); if (last_rxf) { last_rxf->link = cpu_to_le32(sp->rx_ring_dma[i]); - pci_dma_sync_single(sp->pdev, last_rxf_dma, - sizeof(struct RxFD), PCI_DMA_TODEVICE); + pci_dma_sync_single_for_device(sp->pdev, last_rxf_dma, + sizeof(struct RxFD), PCI_DMA_TODEVICE); } last_rxf = rxf; last_rxf_dma = sp->rx_ring_dma[i]; @@ -1316,14 +1336,14 @@ speedo_init_rx_ring(struct net_device *d /* This field unused by i82557. */ rxf->rx_buf_addr = 0xffffffff; rxf->count = cpu_to_le32(PKT_BUF_SZ << 16); - pci_dma_sync_single(sp->pdev, sp->rx_ring_dma[i], - sizeof(struct RxFD), PCI_DMA_TODEVICE); + pci_dma_sync_single_for_device(sp->pdev, sp->rx_ring_dma[i], + sizeof(struct RxFD), PCI_DMA_TODEVICE); } sp->dirty_rx = (unsigned int)(i - RX_RING_SIZE); /* Mark the last entry as end-of-list. */ last_rxf->status = cpu_to_le32(0xC0000002); /* '2' is flag value only. */ - pci_dma_sync_single(sp->pdev, sp->rx_ring_dma[RX_RING_SIZE-1], - sizeof(struct RxFD), PCI_DMA_TODEVICE); + pci_dma_sync_single_for_device(sp->pdev, sp->rx_ring_dma[RX_RING_SIZE-1], + sizeof(struct RxFD), PCI_DMA_TODEVICE); sp->last_rxf = last_rxf; sp->last_rxf_dma = last_rxf_dma; } @@ -1696,8 +1716,8 @@ static inline struct RxFD *speedo_rx_all skb->dev = dev; skb_reserve(skb, sizeof(struct RxFD)); rxf->rx_buf_addr = 0xffffffff; - pci_dma_sync_single(sp->pdev, sp->rx_ring_dma[entry], - sizeof(struct RxFD), PCI_DMA_TODEVICE); + pci_dma_sync_single_for_device(sp->pdev, sp->rx_ring_dma[entry], + sizeof(struct RxFD), PCI_DMA_TODEVICE); return rxf; } @@ -1710,8 +1730,8 @@ static inline void speedo_rx_link(struct rxf->count = cpu_to_le32(PKT_BUF_SZ << 16); sp->last_rxf->link = cpu_to_le32(rxf_dma); sp->last_rxf->status &= cpu_to_le32(~0xC0000000); - pci_dma_sync_single(sp->pdev, sp->last_rxf_dma, - sizeof(struct RxFD), PCI_DMA_TODEVICE); + pci_dma_sync_single_for_device(sp->pdev, sp->last_rxf_dma, + sizeof(struct RxFD), PCI_DMA_TODEVICE); sp->last_rxf = rxf; sp->last_rxf_dma = rxf_dma; } @@ -1783,8 +1803,8 @@ speedo_rx(struct net_device *dev) int status; int pkt_len; - pci_dma_sync_single(sp->pdev, sp->rx_ring_dma[entry], - sizeof(struct RxFD), PCI_DMA_FROMDEVICE); + pci_dma_sync_single_for_cpu(sp->pdev, sp->rx_ring_dma[entry], + sizeof(struct RxFD), PCI_DMA_FROMDEVICE); status = le32_to_cpu(sp->rx_ringp[entry]->status); pkt_len = le32_to_cpu(sp->rx_ringp[entry]->count) & 0x3fff; @@ -1830,8 +1850,9 @@ speedo_rx(struct net_device *dev) skb->dev = dev; skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ /* 'skb_put()' points to the start of sk_buff data area. */ - pci_dma_sync_single(sp->pdev, sp->rx_ring_dma[entry], - sizeof(struct RxFD) + pkt_len, PCI_DMA_FROMDEVICE); + pci_dma_sync_single_for_cpu(sp->pdev, sp->rx_ring_dma[entry], + sizeof(struct RxFD) + pkt_len, + PCI_DMA_FROMDEVICE); #if 1 || USE_IP_CSUM /* Packet is in one chunk -- we can copy + cksum. */ @@ -1841,6 +1862,9 @@ speedo_rx(struct net_device *dev) memcpy(skb_put(skb, pkt_len), sp->rx_skbuff[entry]->tail, pkt_len); #endif + pci_dma_sync_single_for_device(sp->pdev, sp->rx_ring_dma[entry], + sizeof(struct RxFD) + pkt_len, + PCI_DMA_FROMDEVICE); npkts++; } else { /* Pass up the already-filled skbuff. */ @@ -1855,7 +1879,8 @@ speedo_rx(struct net_device *dev) npkts++; sp->rx_ringp[entry] = NULL; pci_unmap_single(sp->pdev, sp->rx_ring_dma[entry], - PKT_BUF_SZ + sizeof(struct RxFD), PCI_DMA_FROMDEVICE); + PKT_BUF_SZ + sizeof(struct RxFD), + PCI_DMA_FROMDEVICE); } skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); @@ -2287,8 +2312,8 @@ static void set_rx_mode(struct net_devic mc_setup_frm->link = cpu_to_le32(TX_RING_ELEM_DMA(sp, (entry + 1) % TX_RING_SIZE)); - pci_dma_sync_single(sp->pdev, mc_blk->frame_dma, - mc_blk->len, PCI_DMA_TODEVICE); + pci_dma_sync_single_for_device(sp->pdev, mc_blk->frame_dma, + mc_blk->len, PCI_DMA_TODEVICE); wait_for_cmd_done(dev); clear_suspend(last_cmd); --- linux-2.6.4-rc2/drivers/net/epic100.c 2003-09-08 13:58:57.000000000 -0700 +++ 25/drivers/net/epic100.c 2004-03-07 20:46:58.000000000 -0800 @@ -1199,8 +1199,6 @@ static int epic_rx(struct net_device *de short pkt_len = (status >> 16) - 4; struct sk_buff *skb; - pci_dma_sync_single(ep->pci_dev, ep->rx_ring[entry].bufaddr, - ep->rx_buf_sz, PCI_DMA_FROMDEVICE); if (pkt_len > PKT_BUF_SZ - 4) { printk(KERN_ERR "%s: Oversized Ethernet frame, status %x " "%d bytes.\n", @@ -1213,6 +1211,10 @@ static int epic_rx(struct net_device *de && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ + pci_dma_sync_single_for_cpu(ep->pci_dev, + ep->rx_ring[entry].bufaddr, + ep->rx_buf_sz, + PCI_DMA_FROMDEVICE); #if 1 /* HAS_IP_COPYSUM */ eth_copy_and_sum(skb, ep->rx_skbuff[entry]->tail, pkt_len, 0); skb_put(skb, pkt_len); @@ -1220,6 +1222,10 @@ static int epic_rx(struct net_device *de memcpy(skb_put(skb, pkt_len), ep->rx_skbuff[entry]->tail, pkt_len); #endif + pci_dma_sync_single_for_device(ep->pci_dev, + ep->rx_ring[entry].bufaddr, + ep->rx_buf_sz, + PCI_DMA_FROMDEVICE); } else { pci_unmap_single(ep->pci_dev, ep->rx_ring[entry].bufaddr, --- linux-2.6.4-rc2/drivers/net/es3210.c 2004-02-17 20:48:43.000000000 -0800 +++ 25/drivers/net/es3210.c 2004-03-07 20:46:54.000000000 -0800 @@ -298,6 +298,9 @@ static int __init es_probe1(struct net_d dev->open = &es_open; dev->stop = &es_close; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = ei_poll; +#endif NS8390_init(dev, 0); return 0; out1: --- linux-2.6.4-rc2/drivers/net/fealnx.c 2004-02-17 20:48:43.000000000 -0800 +++ 25/drivers/net/fealnx.c 2004-03-07 20:46:58.000000000 -0800 @@ -1647,10 +1647,6 @@ static int netdev_rx(struct net_device * printk(KERN_DEBUG " netdev_rx() normal Rx pkt length %d" " status %x.\n", pkt_len, rx_status); #endif - pci_dma_sync_single(np->pci_dev, np->cur_rx->buffer, - np->rx_buf_sz, PCI_DMA_FROMDEVICE); - pci_unmap_single(np->pci_dev, np->cur_rx->buffer, - np->rx_buf_sz, PCI_DMA_FROMDEVICE); /* Check if the packet is long enough to accept without copying to a minimally-sized skbuff. */ @@ -1658,6 +1654,10 @@ static int netdev_rx(struct net_device * (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ + pci_dma_sync_single_for_cpu(np->pci_dev, + np->cur_rx->buffer, + np->rx_buf_sz, + PCI_DMA_FROMDEVICE); /* Call copy + cksum if available. */ #if ! defined(__alpha__) @@ -1668,7 +1668,15 @@ static int netdev_rx(struct net_device * memcpy(skb_put(skb, pkt_len), np->cur_rx->skbuff->tail, pkt_len); #endif + pci_dma_sync_single_for_device(np->pci_dev, + np->cur_rx->buffer, + np->rx_buf_sz, + PCI_DMA_FROMDEVICE); } else { + pci_unmap_single(np->pci_dev, + np->cur_rx->buffer, + np->rx_buf_sz, + PCI_DMA_FROMDEVICE); skb_put(skb = np->cur_rx->skbuff, pkt_len); np->cur_rx->skbuff = NULL; if (np->really_rx_count == RX_RING_SIZE) @@ -1689,8 +1697,10 @@ static int netdev_rx(struct net_device * if (skb != NULL) { skb->dev = dev; /* Mark as being used by this device. */ - np->cur_rx->buffer = pci_map_single(np->pci_dev, skb->tail, - np->rx_buf_sz, PCI_DMA_FROMDEVICE); + np->cur_rx->buffer = pci_map_single(np->pci_dev, + skb->tail, + np->rx_buf_sz, + PCI_DMA_FROMDEVICE); np->cur_rx->skbuff = skb; ++np->really_rx_count; } --- linux-2.6.4-rc2/drivers/net/hamachi.c 2003-09-27 18:57:45.000000000 -0700 +++ 25/drivers/net/hamachi.c 2004-03-07 20:46:58.000000000 -0800 @@ -1498,8 +1498,10 @@ static int hamachi_rx(struct net_device if (desc_status & DescOwn) break; - pci_dma_sync_single(hmp->pci_dev, desc->addr, hmp->rx_buf_sz, - PCI_DMA_FROMDEVICE); + pci_dma_sync_single_for_cpu(hmp->pci_dev, + desc->addr, + hmp->rx_buf_sz, + PCI_DMA_FROMDEVICE); buf_addr = desc_to_virt(desc->addr); frame_status = le32_to_cpu(get_unaligned((s32*)&(buf_addr[data_size - 12]))); if (hamachi_debug > 4) @@ -1563,6 +1565,10 @@ static int hamachi_rx(struct net_device #endif skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ + pci_dma_sync_single_for_cpu(hmp->pci_dev, + hmp->rx_ring[entry].addr, + hmp->rx_buf_sz, + PCI_DMA_FROMDEVICE); /* Call copy + cksum if available. */ #if 1 || USE_IP_COPYSUM eth_copy_and_sum(skb, @@ -1572,10 +1578,14 @@ static int hamachi_rx(struct net_device memcpy(skb_put(skb, pkt_len), hmp->rx_ring_dma + entry*sizeof(*desc), pkt_len); #endif + pci_dma_sync_single_for_device(hmp->pci_dev, + hmp->rx_ring[entry].addr, + hmp->rx_buf_sz, + PCI_DMA_FROMDEVICE); } else { pci_unmap_single(hmp->pci_dev, - hmp->rx_ring[entry].addr, - hmp->rx_buf_sz, PCI_DMA_FROMDEVICE); + hmp->rx_ring[entry].addr, + hmp->rx_buf_sz, PCI_DMA_FROMDEVICE); skb_put(skb = hmp->rx_skbuff[entry], pkt_len); hmp->rx_skbuff[entry] = NULL; } --- linux-2.6.4-rc2/drivers/net/hp.c 2004-02-17 20:48:43.000000000 -0800 +++ 25/drivers/net/hp.c 2004-03-07 20:46:54.000000000 -0800 @@ -207,6 +207,9 @@ static int __init hp_probe1(struct net_d dev->base_addr = ioaddr + NIC_OFFSET; dev->open = &hp_open; dev->stop = &hp_close; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = ei_poll; +#endif ei_status.name = name; ei_status.word16 = wordmode; --- linux-2.6.4-rc2/drivers/net/hp-plus.c 2004-02-17 20:48:43.000000000 -0800 +++ 25/drivers/net/hp-plus.c 2004-03-07 20:46:54.000000000 -0800 @@ -236,6 +236,9 @@ static int __init hpp_probe1(struct net_ dev->open = &hpp_open; dev->stop = &hpp_close; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = ei_poll; +#endif ei_status.name = name; ei_status.word16 = 0; /* Agggghhhhh! Debug time: 2 days! */ --- linux-2.6.4-rc2/drivers/net/hydra.c 2004-03-03 23:12:45.000000000 -0800 +++ 25/drivers/net/hydra.c 2004-03-07 20:46:54.000000000 -0800 @@ -142,6 +142,10 @@ static int __devinit hydra_init(struct z ei_status.reg_offset = hydra_offsets; dev->open = &hydra_open; dev->stop = &hydra_close; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = ei_poll; +#endif + NS8390_init(dev, 0); err = register_netdev(dev); --- linux-2.6.4-rc2/drivers/net/irda/stir4200.c 2004-03-03 23:12:45.000000000 -0800 +++ 25/drivers/net/irda/stir4200.c 2004-03-07 20:46:46.000000000 -0800 @@ -49,12 +49,14 @@ #include #include #include +#include #include #include #include #include #include -#include +#include +#include MODULE_AUTHOR("Stephen Hemminger "); MODULE_DESCRIPTION("IrDA-USB Dongle Driver for SigmaTel STIr4200"); @@ -72,15 +74,11 @@ static int tx_power = 0; /* 0 = highest module_param(tx_power, int, 0); MODULE_PARM_DESC(tx_power, "Set Transmitter power (0-3, 0 is highest power)"); -static int rx_interval = 5; /* milliseconds */ -module_param(rx_interval, int, 0); -MODULE_PARM_DESC(rx_interval, "Receive polling interval (ms)"); - #define STIR_IRDA_HEADER 4 #define CTRL_TIMEOUT 100 /* milliseconds */ #define TRANSMIT_TIMEOUT 200 /* milliseconds */ #define STIR_FIFO_SIZE 4096 -#define NUM_RX_URBS 2 +#define FIFO_REGS_SIZE 3 enum FirChars { FIR_CE = 0x7d, @@ -167,36 +165,26 @@ enum StirTestMask { TEST_TSTOSC = 0x0F, }; -enum StirState { - STIR_STATE_RECEIVING=0, - STIR_STATE_TXREADY, -}; - struct stir_cb { struct usb_device *usbdev; /* init: probe_irda */ struct net_device *netdev; /* network layer */ struct irlap_cb *irlap; /* The link layer we are binded to */ struct net_device_stats stats; /* network statistics */ struct qos_info qos; - unsigned long state; unsigned speed; /* Current speed */ wait_queue_head_t thr_wait; /* transmit thread wakeup */ struct completion thr_exited; pid_t thr_pid; - unsigned int tx_bulkpipe; - void *tx_data; /* wrapped data out */ - unsigned tx_len; - unsigned tx_newspeed; - unsigned tx_mtt; + struct sk_buff *tx_pending; + void *io_buf; /* transmit/receive buffer */ + __u8 *fifo_status; - unsigned int rx_intpipe; iobuff_t rx_buff; /* receive unwrap state machine */ - struct timespec rx_time; - - struct urb *rx_urbs[NUM_RX_URBS]; - void *rx_data[NUM_RX_URBS]; + struct timeval rx_time; + int receiving; + struct urb *rx_urb; }; @@ -209,9 +197,6 @@ static struct usb_device_id dongles[] = MODULE_DEVICE_TABLE(usb, dongles); -static int fifo_txwait(struct stir_cb *stir, unsigned space); -static void stir_usb_receive(struct urb *urb, struct pt_regs *regs); - /* Send control message to set dongle register */ static int write_reg(struct stir_cb *stir, __u16 reg, __u8 value) { @@ -239,6 +224,11 @@ static inline int read_reg(struct stir_c MSECS_TO_JIFFIES(CTRL_TIMEOUT)); } +static inline int isfir(u32 speed) +{ + return (speed == 4000000); +} + /* * Prepare a FIR IrDA frame for transmission to the USB dongle. The * FIR transmit frame is documented in the datasheet. It consists of @@ -333,8 +323,8 @@ static void fir_eof(struct stir_cb *stir { iobuff_t *rx_buff = &stir->rx_buff; int len = rx_buff->len - 4; + struct sk_buff *skb, *nskb; __u32 fcs; - struct sk_buff *nskb; if (unlikely(len <= 0)) { pr_debug("%s: short frame len %d\n", @@ -345,41 +335,46 @@ static void fir_eof(struct stir_cb *stir return; } - fcs = rx_buff->data[len] | - rx_buff->data[len+1] << 8 | - rx_buff->data[len+2] << 16 | - rx_buff->data[len+3] << 24; - - if (unlikely(fcs != ~(crc32_le(~0, rx_buff->data, len)))) { - pr_debug("%s: crc error\n", stir->netdev->name); - irda_device_set_media_busy(stir->netdev, TRUE); + fcs = ~(crc32_le(~0, rx_buff->data, len)); + if (fcs != le32_to_cpu(get_unaligned((u32 *)(rx_buff->data+len)))) { + pr_debug("crc error calc 0x%x len %d\n", fcs, len); stir->stats.rx_errors++; stir->stats.rx_crc_errors++; return; } - /* If can't get new buffer, just drop and reuse */ - nskb = dev_alloc_skb(IRDA_SKB_MAX_MTU); - if (unlikely(!nskb)) - ++stir->stats.rx_dropped; - else { - struct sk_buff *oskb = rx_buff->skb; + /* if frame is short then just copy it */ + if (len < IRDA_RX_COPY_THRESHOLD) { + nskb = dev_alloc_skb(len + 1); + if (unlikely(!nskb)) { + ++stir->stats.rx_dropped; + return; + } + skb_reserve(nskb, 1); + skb = nskb; + memcpy(nskb->data, rx_buff->data, len); + } else { + nskb = dev_alloc_skb(rx_buff->truesize); + if (unlikely(!nskb)) { + ++stir->stats.rx_dropped; + return; + } skb_reserve(nskb, 1); + skb = rx_buff->skb; + rx_buff->skb = nskb; + rx_buff->head = nskb->data; + } - /* Set correct length in socket buffer */ - skb_put(oskb, len); + skb_put(skb, len); - oskb->mac.raw = oskb->data; - oskb->protocol = htons(ETH_P_IRDA); - oskb->dev = stir->netdev; + skb->mac.raw = skb->data; + skb->protocol = htons(ETH_P_IRDA); + skb->dev = stir->netdev; - netif_rx(oskb); + netif_rx(skb); - stir->stats.rx_packets++; - stir->stats.rx_bytes += len; - rx_buff->skb = nskb; - rx_buff->head = nskb->data; - } + stir->stats.rx_packets++; + stir->stats.rx_bytes += len; rx_buff->data = rx_buff->head; rx_buff->len = 0; @@ -402,7 +397,6 @@ static void stir_fir_chars(struct stir_c continue; /* Now receiving frame */ rx_buff->state = BEGIN_FRAME; - rx_buff->in_frame = TRUE; /* Time to initialize receive buffer */ rx_buff->data = rx_buff->head; @@ -424,6 +418,7 @@ static void stir_fir_chars(struct stir_c if (byte == FIR_EOF) continue; rx_buff->state = INSIDE_FRAME; + rx_buff->in_frame = TRUE; /* fall through */ case INSIDE_FRAME: @@ -461,7 +456,6 @@ static void stir_fir_chars(struct stir_c error_recovery: ++stir->stats.rx_errors; - irda_device_set_media_busy(stir->netdev, TRUE); rx_buff->state = OUTSIDE_FRAME; rx_buff->in_frame = FALSE; } @@ -478,11 +472,6 @@ static void stir_sir_chars(struct stir_c &stir->rx_buff, bytes[i]); } -static inline int isfir(u32 speed) -{ - return (speed == 4000000); -} - static inline void unwrap_chars(struct stir_cb *stir, const __u8 *bytes, int length) { @@ -519,25 +508,31 @@ static int change_speed(struct stir_cb * int i, err; __u8 mode; - pr_debug("%s: change speed %d\n", stir->netdev->name, speed); for (i = 0; i < ARRAY_SIZE(stir_modes); ++i) { if (speed == stir_modes[i].speed) goto found; } - ERROR("%s: invalid speed %d\n", stir->netdev->name, speed); + warn("%s: invalid speed %d", stir->netdev->name, speed); return -EINVAL; found: - pr_debug("%s: speed change from %d to %d\n", - stir->netdev->name, stir->speed, speed); + pr_debug("speed change from %d to %d\n", stir->speed, speed); + + /* sometimes needed to get chip out of stuck state */ + err = usb_reset_device(stir->usbdev); + if (err) + goto out; + + /* Reset modulator */ + err = write_reg(stir, REG_CTRL1, CTRL1_SRESET); + if (err) + goto out; - /* Make sure any previous Tx is really finished. This happens - * when we answer an incomming request ; the ua:rsp and the - * speed change are bundled together, so we need to wait until - * the packet we just submitted has been sent. Jean II */ - if (fifo_txwait(stir, 0)) - return -EIO; + /* Undocumented magic to tweak the DPLL */ + err = write_reg(stir, REG_DPLL, 0x15); + if (err) + goto out; /* Set clock */ err = write_reg(stir, REG_PDCLK, stir_modes[i].pdclk); @@ -564,33 +559,13 @@ static int change_speed(struct stir_cb * goto out; err = write_reg(stir, REG_CTRL1, (tx_power & 3) << 1); - - out: - stir->speed = speed; - return err; -} - -static int stir_reset(struct stir_cb *stir) -{ - int err; - - /* reset state */ - stir->rx_buff.in_frame = FALSE; - stir->rx_buff.state = OUTSIDE_FRAME; - stir->speed = -1; - - /* Undocumented magic to tweak the DPLL */ - err = write_reg(stir, REG_DPLL, 0x15); if (err) goto out; /* Reset sensitivity */ err = write_reg(stir, REG_CTRL2, (rx_sensitivity & 7) << 5); - if (err) - goto out; - - err = change_speed(stir, 9600); out: + stir->speed = speed; return err; } @@ -606,48 +581,62 @@ static int stir_hard_xmit(struct sk_buff /* the IRDA wrapping routines don't deal with non linear skb */ SKB_LINEAR_ASSERT(skb); - if (unlikely(skb->len) == 0) /* speed change only */ - stir->tx_len = 0; - else if (isfir(stir->speed)) - stir->tx_len = wrap_fir_skb(skb, stir->tx_data); - else - stir->tx_len = wrap_sir_skb(skb, stir->tx_data); - - stir->stats.tx_packets++; - stir->stats.tx_bytes += skb->len; - - stir->tx_mtt = irda_get_mtt(skb); - stir->tx_newspeed = irda_get_next_speed(skb); - - if (!test_and_set_bit(STIR_STATE_TXREADY, &stir->state)) - wake_up(&stir->thr_wait); + skb = xchg(&stir->tx_pending, skb); + wake_up(&stir->thr_wait); + + /* this should never happen unless stop/wakeup problem */ + if (unlikely(skb)) { + WARN_ON(1); + dev_kfree_skb(skb); + } - dev_kfree_skb(skb); return 0; } /* * Wait for the transmit FIFO to have space for next data + * + * If space < 0 then wait till FIFO completely drains. + * FYI: can take up to 13 seconds at 2400baud. */ -static int fifo_txwait(struct stir_cb *stir, unsigned space) +static int fifo_txwait(struct stir_cb *stir, int space) { int err; - unsigned count; - __u8 regs[3]; - unsigned long timeout = jiffies + HZ/10; + unsigned long count, status; + /* Read FIFO status and count */ for(;;) { - /* Read FIFO status and count */ - err = read_reg(stir, REG_FIFOCTL, regs, 3); - if (unlikely(err != 3)) { - WARNING("%s: FIFO register read error: %d\n", - stir->netdev->name, err); + err = read_reg(stir, REG_FIFOCTL, stir->fifo_status, + FIFO_REGS_SIZE); + if (unlikely(err != FIFO_REGS_SIZE)) { + warn("%s: FIFO register read error: %d", + stir->netdev->name, err); + return err; } + status = stir->fifo_status[0]; + count = (unsigned)(stir->fifo_status[2] & 0x1f) << 8 + | stir->fifo_status[1]; + + pr_debug("fifo status 0x%lx count %lu\n", status, count); + + /* error when receive/transmit fifo gets confused */ + if (status & FIFOCTL_RXERR) { + stir->stats.rx_fifo_errors++; + stir->stats.rx_errors++; + break; + } + + if (status & FIFOCTL_TXERR) { + stir->stats.tx_fifo_errors++; + stir->stats.tx_errors++; + break; + } + /* is fifo receiving already, or empty */ - if (!(regs[0] & FIFOCTL_DIR) - || (regs[0] & FIFOCTL_EMPTY)) + if (!(status & FIFOCTL_DIR) + || (status & FIFOCTL_EMPTY)) return 0; if (signal_pending(current)) @@ -658,40 +647,37 @@ static int fifo_txwait(struct stir_cb *s || !netif_device_present(stir->netdev)) return -ESHUTDOWN; - count = (unsigned)(regs[2] & 0x1f) << 8 | regs[1]; - - pr_debug("%s: fifo status 0x%x count %u\n", - stir->netdev->name, regs[0], count); - /* only waiting for some space */ - if (space && STIR_FIFO_SIZE - 4 > space + count) + if (space >= 0 && STIR_FIFO_SIZE - 4 > space + count) return 0; - if (time_after(jiffies, timeout)) { - WARNING("%s: transmit fifo timeout status=0x%x count=%d\n", - stir->netdev->name, regs[0], count); - ++stir->stats.tx_errors; - irda_device_set_media_busy(stir->netdev, TRUE); - return -ETIMEDOUT; - } - /* estimate transfer time for remaining chars */ wait_ms((count * 8000) / stir->speed); } + + err = write_reg(stir, REG_FIFOCTL, FIFOCTL_CLR); + if (err) + return err; + err = write_reg(stir, REG_FIFOCTL, 0); + if (err) + return err; + + return 0; } /* Wait for turnaround delay before starting transmit. */ -static void turnaround_delay(long us, const struct timespec *last) +static void turnaround_delay(const struct stir_cb *stir, long us) { long ticks; - struct timespec now = CURRENT_TIME; + struct timeval now; if (us <= 0) return; - us -= (now.tv_sec - last->tv_sec) * USEC_PER_SEC; - us -= (now.tv_nsec - last->tv_nsec) / NSEC_PER_USEC; + do_gettimeofday(&now); + us -= (now.tv_sec - stir->rx_time.tv_sec) * USEC_PER_SEC; + us -= now.tv_usec - stir->rx_time.tv_usec; if (us < 10) return; @@ -707,77 +693,60 @@ static void turnaround_delay(long us, co * Start receiver by submitting a request to the receive pipe. * If nothing is available it will return after rx_interval. */ -static void receive_start(struct stir_cb *stir) +static int receive_start(struct stir_cb *stir) { - int i; - - if (test_and_set_bit(STIR_STATE_RECEIVING, &stir->state)) - return; - - if (fifo_txwait(stir, 0)) - return; - - for (i = 0; i < NUM_RX_URBS; i++) { - struct urb *urb = stir->rx_urbs[i]; - - usb_fill_int_urb(urb, stir->usbdev, stir->rx_intpipe, - stir->rx_data[i], STIR_FIFO_SIZE, - stir_usb_receive, stir, rx_interval); - - if (usb_submit_urb(urb, GFP_KERNEL)) - urb->status = -EINVAL; - } + /* reset state */ + stir->receiving = 1; - if (i == 0) { - /* if nothing got queued, then just retry next time */ - if (net_ratelimit()) - WARNING("%s: no receive buffers avaiable\n", - stir->netdev->name); + stir->rx_buff.in_frame = FALSE; + stir->rx_buff.state = OUTSIDE_FRAME; - clear_bit(STIR_STATE_RECEIVING, &stir->state); - } + stir->rx_urb->status = 0; + return usb_submit_urb(stir->rx_urb, GFP_KERNEL); } /* Stop all pending receive Urb's */ static void receive_stop(struct stir_cb *stir) { - int i; + stir->receiving = 0; + usb_unlink_urb(stir->rx_urb); - for (i = 0; i < NUM_RX_URBS; i++) { - struct urb *urb = stir->rx_urbs[i]; - usb_unlink_urb(urb); - } + if (stir->rx_buff.in_frame) + stir->stats.collisions++; } - -/* Send wrapped data (in tx_data) to device */ -static void stir_send(struct stir_cb *stir) +/* + * Wrap data in socket buffer and send it. + */ +static void stir_send(struct stir_cb *stir, struct sk_buff *skb) { - int rc; + unsigned wraplen; + int first_frame = 0; - if (test_and_clear_bit(STIR_STATE_RECEIVING, &stir->state)) { + /* if receiving, need to turnaround */ + if (stir->receiving) { receive_stop(stir); - - turnaround_delay(stir->tx_mtt, &stir->rx_time); - - if (stir->rx_buff.in_frame) - ++stir->stats.collisions; + turnaround_delay(stir, irda_get_mtt(skb)); + first_frame = 1; } - else if (fifo_txwait(stir, stir->tx_len)) - return; /* shutdown or major errors */ + if (isfir(stir->speed)) + wraplen = wrap_fir_skb(skb, stir->io_buf); + else + wraplen = wrap_sir_skb(skb, stir->io_buf); + + /* check for space available in fifo */ + if (!first_frame) + fifo_txwait(stir, wraplen); + + stir->stats.tx_packets++; + stir->stats.tx_bytes += skb->len; stir->netdev->trans_start = jiffies; + pr_debug("send %d (%d)\n", skb->len, wraplen); - pr_debug("%s: send %d\n", stir->netdev->name, stir->tx_len); - rc = usb_bulk_msg(stir->usbdev, - stir->tx_bulkpipe, - stir->tx_data, stir->tx_len, - NULL, MSECS_TO_JIFFIES(TRANSMIT_TIMEOUT)); - - if (unlikely(rc)) { - WARNING("%s: usb bulk message failed %d\n", - stir->netdev->name, rc); + if (usb_bulk_msg(stir->usbdev, usb_sndbulkpipe(stir->usbdev, 1), + stir->io_buf, wraplen, + NULL, MSECS_TO_JIFFIES(TRANSMIT_TIMEOUT))) stir->stats.tx_errors++; - } } /* @@ -787,7 +756,7 @@ static int stir_transmit_thread(void *ar { struct stir_cb *stir = arg; struct net_device *dev = stir->netdev; - DECLARE_WAITQUEUE(wait, current); + struct sk_buff *skb; daemonize("%s", dev->name); allow_signal(SIGTERM); @@ -796,44 +765,58 @@ static int stir_transmit_thread(void *ar && netif_device_present(dev) && !signal_pending(current)) { - /* make swsusp happy with our thread */ + /* if suspending, then power off and wait */ if (current->flags & PF_FREEZE) { - receive_stop(stir); + if (stir->receiving) + receive_stop(stir); + else + fifo_txwait(stir, -1); write_reg(stir, REG_CTRL1, CTRL1_TXPWD|CTRL1_RXPWD); refrigerator(PF_IOTHREAD); - stir_reset(stir); + if (change_speed(stir, stir->speed)) + break; } /* if something to send? */ - if (test_and_clear_bit(STIR_STATE_TXREADY, &stir->state)) { - unsigned new_speed = stir->tx_newspeed; - - /* Note that we may both send a packet and - * change speed in some cases. Jean II */ - - if (stir->tx_len != 0) - stir_send(stir); - - if (stir->speed != new_speed) - change_speed(stir, new_speed); - - netif_wake_queue(stir->netdev); + skb = xchg(&stir->tx_pending, NULL); + if (skb) { + unsigned new_speed = irda_get_next_speed(skb); + netif_wake_queue(dev); + + if (skb->len > 0) + stir_send(stir, skb); + dev_kfree_skb(skb); + + if (stir->speed != new_speed) { + if (fifo_txwait(stir, -1) || + change_speed(stir, new_speed)) + break; + } continue; } - if (irda_device_txqueue_empty(dev)) - receive_start(stir); + /* nothing to send? start receiving */ + if (!stir->receiving + && irda_device_txqueue_empty(dev)) { + /* Wait otherwise chip gets confused. */ + if (fifo_txwait(stir, -1)) + break; + + if (unlikely(receive_start(stir))) { + if (net_ratelimit()) + info("%s: receive usb submit failed", + stir->netdev->name); + stir->receiving = 0; + wait_ms(10); + continue; + } + } - set_task_state(current, TASK_INTERRUPTIBLE); - add_wait_queue(&stir->thr_wait, &wait); - if (test_bit(STIR_STATE_TXREADY, &stir->state)) - __set_task_state(current, TASK_RUNNING); - else - schedule_timeout(HZ/10); - remove_wait_queue(&stir->thr_wait, &wait); + /* sleep if nothing to send */ + wait_event_interruptible(stir->thr_wait, stir->tx_pending); } complete_and_exit (&stir->thr_exited, 0); @@ -841,48 +824,34 @@ static int stir_transmit_thread(void *ar /* - * Receive wrapped data into rx_data buffer. - * This chip doesn't block until data is available, we just have - * to read the FIFO perodically (ugh). + * USB bulk receive completion callback. + * Wakes up every ms (usb round trip) with wrapped + * data. */ -static void stir_usb_receive(struct urb *urb, struct pt_regs *regs) +static void stir_rcv_irq(struct urb *urb, struct pt_regs *regs) { struct stir_cb *stir = urb->context; int err; + /* in process of stopping, just drop data */ if (!netif_running(stir->netdev)) return; - switch (urb->status) { - case 0: - if(urb->actual_length > 0) { - pr_debug("%s: receive %d\n", - stir->netdev->name, urb->actual_length); - unwrap_chars(stir, urb->transfer_buffer, - urb->actual_length); - - stir->netdev->last_rx = jiffies; - stir->rx_time = CURRENT_TIME; - } - break; - - case -ECONNRESET: /* killed but pending */ - case -ENOENT: /* killed but not in use */ - case -ESHUTDOWN: - /* These are normal errors when URB is cancelled */ - stir->rx_buff.in_frame = FALSE; - stir->rx_buff.state = OUTSIDE_FRAME; + /* unlink, shutdown, unplug, other nasties */ + if (urb->status != 0) return; - default: - WARNING("%s: received status %d\n", stir->netdev->name, - urb->status); - stir->stats.rx_errors++; - urb->status = 0; + if (urb->actual_length > 0) { + pr_debug("receive %d\n", urb->actual_length); + unwrap_chars(stir, urb->transfer_buffer, + urb->actual_length); + + stir->netdev->last_rx = jiffies; + do_gettimeofday(&stir->rx_time); } /* kernel thread is stopping receiver don't resubmit */ - if (!test_bit(STIR_STATE_RECEIVING, &stir->state)) + if (!stir->receiving) return; /* resubmit existing urb */ @@ -890,14 +859,13 @@ static void stir_usb_receive(struct urb /* in case of error, the kernel thread will restart us */ if (err) { - WARNING("%s: usb receive submit error: %d\n", + warn("%s: usb receive submit error: %d", stir->netdev->name, err); - urb->status = -ENOENT; + stir->receiving = 0; wake_up(&stir->thr_wait); } } - /* * Function stir_net_open (dev) * @@ -906,50 +874,50 @@ static void stir_usb_receive(struct urb static int stir_net_open(struct net_device *netdev) { struct stir_cb *stir = netdev->priv; - int i, err; - char hwname[16]; + int err; + char hwname[16]; - err = stir_reset(stir); + err = usb_clear_halt(stir->usbdev, usb_sndbulkpipe(stir->usbdev, 1)); + if (err) + goto err_out1; + err = usb_clear_halt(stir->usbdev, usb_rcvbulkpipe(stir->usbdev, 2)); if (err) goto err_out1; - err = -ENOMEM; - - /* Note: Max SIR frame possible is 4273 */ - stir->tx_data = kmalloc(STIR_FIFO_SIZE, GFP_KERNEL); - if (!stir->tx_data) { - ERROR("%s(), alloc failed for rxbuf!\n", __FUNCTION__); + err = change_speed(stir, 9600); + if (err) goto err_out1; - } + + err = -ENOMEM; /* Initialize for SIR/FIR to copy data directly into skb. */ + stir->receiving = 0; stir->rx_buff.truesize = IRDA_SKB_MAX_MTU; stir->rx_buff.skb = dev_alloc_skb(IRDA_SKB_MAX_MTU); - if (!stir->rx_buff.skb) { - ERROR("%s(), dev_alloc_skb() failed for rxbuf!\n", - __FUNCTION__); - goto err_out2; - } + if (!stir->rx_buff.skb) + goto err_out1; + skb_reserve(stir->rx_buff.skb, 1); stir->rx_buff.head = stir->rx_buff.skb->data; - stir->rx_time = CURRENT_TIME; + do_gettimeofday(&stir->rx_time); - /* Allocate N receive buffer's and urbs */ - for (i = 0; i < NUM_RX_URBS; i++) { - stir->rx_urbs[i] = usb_alloc_urb(0, GFP_KERNEL); - if (!stir->rx_urbs[i]){ - ERROR("%s(), usb_alloc_urb failed\n", __FUNCTION__); - goto err_out3; - } + stir->rx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!stir->rx_urb) + goto err_out2; - stir->rx_data[i] = kmalloc(STIR_FIFO_SIZE, GFP_KERNEL); - if (!stir->rx_data) { - usb_free_urb(stir->rx_urbs[i]); - ERROR("%s(), alloc failed for rxbuf!\n", __FUNCTION__); - goto err_out3; - } - } + stir->io_buf = kmalloc(STIR_FIFO_SIZE, GFP_KERNEL); + if (!stir->io_buf) + goto err_out3; + usb_fill_bulk_urb(stir->rx_urb, stir->usbdev, + usb_rcvbulkpipe(stir->usbdev, 2), + stir->io_buf, STIR_FIFO_SIZE, + stir_rcv_irq, stir); + + stir->fifo_status = kmalloc(FIFO_REGS_SIZE, GFP_KERNEL); + if (!stir->fifo_status) + goto err_out4; + /* * Now that everything should be initialized properly, * Open new IrLAP layer instance to take care of us... @@ -958,8 +926,8 @@ static int stir_net_open(struct net_devi sprintf(hwname, "usb#%d", stir->usbdev->devnum); stir->irlap = irlap_open(netdev, &stir->qos, hwname); if (!stir->irlap) { - ERROR("%s(): irlap_open failed\n", __FUNCTION__); - goto err_out3; + err("irlap_open failed"); + goto err_out5; } /** Start kernel thread for transmit. */ @@ -967,25 +935,24 @@ static int stir_net_open(struct net_devi CLONE_FS|CLONE_FILES); if (stir->thr_pid < 0) { err = stir->thr_pid; - WARNING("%s: unable to start kernel thread\n", - stir->netdev->name); - goto err_out4; + err("unable to start kernel thread"); + goto err_out6; } netif_start_queue(netdev); return 0; - err_out4: + err_out6: irlap_close(stir->irlap); + err_out5: + kfree(stir->fifo_status); + err_out4: + kfree(stir->io_buf); err_out3: - while(--i >= 0) { - usb_free_urb(stir->rx_urbs[i]); - kfree(stir->rx_data[i]); - } - kfree_skb(stir->rx_buff.skb); + usb_free_urb(stir->rx_urb); err_out2: - kfree(stir->tx_data); + kfree_skb(stir->rx_buff.skb); err_out1: return err; } @@ -999,7 +966,6 @@ static int stir_net_open(struct net_devi static int stir_net_close(struct net_device *netdev) { struct stir_cb *stir = netdev->priv; - int i; /* Stop transmit processing */ netif_stop_queue(netdev); @@ -1007,15 +973,13 @@ static int stir_net_close(struct net_dev /* Kill transmit thread */ kill_proc(stir->thr_pid, SIGTERM, 1); wait_for_completion(&stir->thr_exited); - kfree(stir->tx_data); - - clear_bit(STIR_STATE_RECEIVING, &stir->state); - receive_stop(stir); + kfree(stir->fifo_status); - for (i = 0; i < NUM_RX_URBS; i++) { - usb_free_urb(stir->rx_urbs[i]); - kfree(stir->rx_data[i]); - } + /* Mop up receive urb's */ + usb_unlink_urb(stir->rx_urb); + + kfree(stir->io_buf); + usb_free_urb(stir->rx_urb); kfree_skb(stir->rx_buff.skb); /* Stop and remove instance of IrLAP */ @@ -1057,7 +1021,7 @@ static int stir_net_ioctl(struct net_dev case SIOCGRECEIVING: /* Only approximately true */ - irq->ifr_receiving = test_bit(STIR_STATE_RECEIVING, &stir->state); + irq->ifr_receiving = stir->receiving; break; default: @@ -1077,53 +1041,6 @@ static struct net_device_stats *stir_net } /* - * Parse the various endpoints and find the one we need. - * - * The endpoint are the pipes used to communicate with the USB device. - * The spec defines 2 endpoints of type bulk transfer, one in, and one out. - * These are used to pass frames back and forth with the dongle. - */ -static int stir_setup_usb(struct stir_cb *stir, struct usb_interface *intf) -{ - struct usb_device *usbdev = interface_to_usbdev(intf); - const struct usb_host_interface *interface - = &intf->altsetting[intf->act_altsetting]; - const struct usb_endpoint_descriptor *ep_in = NULL; - const struct usb_endpoint_descriptor *ep_out = NULL; - int i; - - if (interface->desc.bNumEndpoints != 2) { - WARNING("%s: expected two endpoints\n", __FUNCTION__); - return -ENODEV; - } - - for(i = 0; i < interface->desc.bNumEndpoints; i++) { - const struct usb_endpoint_descriptor *ep - = &interface->endpoint[i].desc; - - if ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) - == USB_ENDPOINT_XFER_BULK) { - /* We need to find an IN and an OUT */ - if ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) - ep_in = ep; - else - ep_out = ep; - } else - WARNING("%s: unknown endpoint type 0x%x\n", - __FUNCTION__, ep->bmAttributes); - } - - if (!ep_in || !ep_out) - return -EIO; - - stir->tx_bulkpipe = usb_sndbulkpipe(usbdev, - ep_out->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); - stir->rx_intpipe = usb_rcvintpipe(usbdev, - ep_in->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); - return 0; -} - -/* * This routine is called by the USB subsystem for each new device * in the system. We need to check if the device is ours, and in * this case start handling it. @@ -1149,9 +1066,9 @@ static int stir_probe(struct usb_interfa stir->netdev = net; stir->usbdev = dev; - ret = stir_setup_usb(stir, intf); + ret = usb_reset_configuration(dev); if (ret != 0) { - ERROR("%s(), Bogus endpoints...\n", __FUNCTION__); + err("usb reset configuration failed"); goto err_out2; } @@ -1180,10 +1097,6 @@ static int stir_probe(struct usb_interfa net->get_stats = stir_net_get_stats; net->do_ioctl = stir_net_ioctl; - ret = stir_reset(stir); - if (ret) - goto err_out2; - ret = register_netdev(net); if (ret != 0) goto err_out2; @@ -1206,23 +1119,14 @@ err_out1: static void stir_disconnect(struct usb_interface *intf) { struct stir_cb *stir = usb_get_intfdata(intf); - struct net_device *net; - usb_set_intfdata(intf, NULL); if (!stir) return; - /* Stop transmitter */ - net = stir->netdev; - netif_device_detach(net); - - /* Remove netdevice */ - unregister_netdev(net); - - /* No longer attached to USB bus */ - stir->usbdev = NULL; + unregister_netdev(stir->netdev); + free_netdev(stir->netdev); - free_netdev(net); + usb_set_intfdata(intf, NULL); } --- linux-2.6.4-rc2/drivers/net/irda/vlsi_ir.c 2004-03-03 23:12:45.000000000 -0800 +++ 25/drivers/net/irda/vlsi_ir.c 2004-03-07 20:46:58.000000000 -0800 @@ -173,7 +173,7 @@ static int vlsi_proc_pdev(struct pci_dev PCIDEV_NAME(pdev), (int)pdev->vendor, (int)pdev->device); out += sprintf(out, "pci-power-state: %u\n", (unsigned) pdev->current_state); out += sprintf(out, "resources: irq=%u / io=0x%04x / dma_mask=0x%016Lx\n", - pdev->irq, (unsigned)pci_resource_start(pdev, 0), (u64)pdev->dma_mask); + pdev->irq, (unsigned)pci_resource_start(pdev, 0), (unsigned long long)pdev->dma_mask); out += sprintf(out, "hw registers: "); for (i = 0; i < 0x20; i++) out += sprintf(out, "%02x", (unsigned)inb((iobase+i))); @@ -566,7 +566,6 @@ static struct vlsi_ring *vlsi_alloc_ring return NULL; } rd_set_addr_status(rd, busaddr, 0); - pci_dma_sync_single(pdev, busaddr, len, dir); /* initially, the dma buffer is owned by the CPU */ rd->skb = NULL; } @@ -660,7 +659,7 @@ static int vlsi_process_rx(struct vlsi_r struct net_device *ndev = (struct net_device *)pci_get_drvdata(r->pdev); vlsi_irda_dev_t *idev = ndev->priv; - pci_dma_sync_single(r->pdev, rd_get_addr(rd), r->len, r->dir); + pci_dma_sync_single_for_cpu(r->pdev, rd_get_addr(rd), r->len, r->dir); /* dma buffer now owned by the CPU */ status = rd_get_status(rd); if (status & RD_RX_ERROR) { @@ -746,7 +745,7 @@ static void vlsi_fill_rx(struct vlsi_rin break; /* probably not worth logging? */ } /* give dma buffer back to busmaster */ - pci_dma_prep_single(r->pdev, rd_get_addr(rd), r->len, r->dir); + pci_dma_sync_single_for_device(r->pdev, rd_get_addr(rd), r->len, r->dir); rd_activate(rd); } } @@ -816,7 +815,7 @@ static void vlsi_unarm_rx(vlsi_irda_dev_ ret = -VLSI_RX_DROP; } rd_set_count(rd, 0); - pci_dma_sync_single(r->pdev, rd_get_addr(rd), r->len, r->dir); + pci_dma_sync_single_for_cpu(r->pdev, rd_get_addr(rd), r->len, r->dir); if (rd->skb) { dev_kfree_skb_any(rd->skb); rd->skb = NULL; @@ -854,7 +853,7 @@ static int vlsi_process_tx(struct vlsi_r int len; int ret; - pci_dma_sync_single(r->pdev, rd_get_addr(rd), r->len, r->dir); + pci_dma_sync_single_for_cpu(r->pdev, rd_get_addr(rd), r->len, r->dir); /* dma buffer now owned by the CPU */ status = rd_get_status(rd); if (status & RD_TX_UNDRN) @@ -1077,8 +1076,8 @@ static int vlsi_hard_start_xmit(struct s } } - /* tx buffer already owned by CPU due to pci_dma_sync_single() either - * after initial pci_map_single or after subsequent tx-completion + /* tx buffer already owned by CPU due to pci_dma_sync_single_for_cpu() + * after subsequent tx-completion */ if (idev->mode == IFF_SIR) { @@ -1120,7 +1119,7 @@ static int vlsi_hard_start_xmit(struct s * CPU-driven changes visible from the pci bus). */ - pci_dma_prep_single(r->pdev, rd_get_addr(rd), r->len, r->dir); + pci_dma_sync_single_for_device(r->pdev, rd_get_addr(rd), r->len, r->dir); /* Switching to TX mode here races with the controller * which may stop TX at any time when fetching an inactive descriptor @@ -1248,7 +1247,7 @@ static void vlsi_unarm_tx(vlsi_irda_dev_ if (rd_is_active(rd)) { rd_set_status(rd, 0); rd_set_count(rd, 0); - pci_dma_sync_single(r->pdev, rd_get_addr(rd), r->len, r->dir); + pci_dma_sync_single_for_cpu(r->pdev, rd_get_addr(rd), r->len, r->dir); if (rd->skb) { dev_kfree_skb_any(rd->skb); rd->skb = NULL; --- linux-2.6.4-rc2/drivers/net/Kconfig 2004-03-03 23:12:45.000000000 -0800 +++ 25/drivers/net/Kconfig 2004-03-07 20:46:54.000000000 -0800 @@ -2495,6 +2495,13 @@ config SHAPER To compile this driver as a module, choose M here: the module will be called shaper. If unsure, say N. +config NETCONSOLE + tristate "Network console logging support (EXPERIMENTAL)" + depends on NETDEVICES && EXPERIMENTAL + ---help--- + If you want to log kernel messages over the network, enable this. + See Documentation/networking/netconsole.txt for details. + source "drivers/net/wan/Kconfig" source "drivers/net/pcmcia/Kconfig" --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/drivers/net/kgdb_eth.c 2004-03-07 20:47:03.000000000 -0800 @@ -0,0 +1,131 @@ +/* + * Network interface GDB stub + * + * Written by San Mehat (nettwerk@biodome.org) + * Based upon 'gdbserial' by David Grothe (dave@gcom.com) + * and Scott Foehner (sfoehner@engr.sgi.com) + * + * Twiddled for 2.6 by Robert Walsh + * and wangdi . + * + * Refactored for netpoll API by Matt Mackall + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define IN_BUF_SIZE 512 /* power of 2, please */ +#define OUT_BUF_SIZE 256 + +static char in_buf[IN_BUF_SIZE], out_buf[OUT_BUF_SIZE]; +static int in_head, in_tail, out_count; +static atomic_t in_count; +int kgdboe = 0; /* Default to tty mode */ + +extern void set_debug_traps(void); +extern void breakpoint(void); +static void rx_hook(struct netpoll *np, int port, char *msg, int len); + +static struct netpoll np = { + .name = "kgdboe", + .dev_name = "eth0", + .rx_hook = rx_hook, + .local_port = 6443, + .remote_port = 6442, + .remote_mac = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, +}; + +int eth_getDebugChar(void) +{ + int chr; + + while (atomic_read(&in_count) == 0) + netpoll_poll(&np); + + chr = in_buf[in_tail++]; + in_tail &= (IN_BUF_SIZE - 1); + atomic_dec(&in_count); + return chr; +} + +void eth_flushDebugChar(void) +{ + if(out_count && np.dev) { + netpoll_send_udp(&np, out_buf, out_count); + out_count = 0; + } +} + +void eth_putDebugChar(int chr) +{ + out_buf[out_count++] = chr; + if(out_count == OUT_BUF_SIZE) + eth_flushDebugChar(); +} + +static void rx_hook(struct netpoll *np, int port, char *msg, int len) +{ + int i; + + np->remote_port = port; + + /* Is this gdb trying to attach? */ + if (!netpoll_trap() && len == 8 && !strncmp(msg, "$Hc-1#09", 8)) + kgdb_schedule_breakpoint(); + + for (i = 0; i < len; i++) { + if (msg[i] == 3) + kgdb_schedule_breakpoint(); + + if (atomic_read(&in_count) >= IN_BUF_SIZE) { + /* buffer overflow, clear it */ + in_head = in_tail = 0; + atomic_set(&in_count, 0); + break; + } + in_buf[in_head++] = msg[i]; + in_head &= (IN_BUF_SIZE - 1); + atomic_inc(&in_count); + } +} + +static int option_setup(char *opt) +{ + return netpoll_parse_options(&np, opt); +} + +__setup("kgdboe=", option_setup); + +static int init_kgdboe(void) +{ +#ifdef CONFIG_SMP + if (num_online_cpus() > CONFIG_NO_KGDB_CPUS) { + printk("kgdb: too manu cpus. Cannot enable debugger with more than %d cpus\n", CONFIG_NO_KGDB_CPUS); + return -1; + } +#endif + + set_debug_traps(); + + if(!np.remote_ip || netpoll_setup(&np)) + return 1; + + kgdboe = 1; + printk(KERN_INFO "kgdb: debugging over ethernet enabled\n"); + + return 0; +} + +module_init(init_kgdboe); --- linux-2.6.4-rc2/drivers/net/lasi_82596.c 2004-03-03 23:12:45.000000000 -0800 +++ 25/drivers/net/lasi_82596.c 2004-03-07 20:46:58.000000000 -0800 @@ -802,9 +802,10 @@ memory_squeeze: skb->dev = dev; if (!rx_in_place) { /* 16 byte align the data fields */ - dma_sync_single(lp->dev, (dma_addr_t)WSWAPchar(rbd->b_data), PKT_BUF_SZ, DMA_FROM_DEVICE); + dma_sync_single_for_cpu(lp->dev, (dma_addr_t)WSWAPchar(rbd->b_data), PKT_BUF_SZ, DMA_FROM_DEVICE); skb_reserve(skb, 2); memcpy(skb_put(skb,pkt_len), rbd->v_data, pkt_len); + dma_sync_single_for_device(lp->dev, (dma_addr_t)WSWAPchar(rbd->b_data), PKT_BUF_SZ, DMA_FROM_DEVICE); } skb->len = pkt_len; skb->protocol=eth_type_trans(skb,dev); --- linux-2.6.4-rc2/drivers/net/lne390.c 2004-02-17 20:48:43.000000000 -0800 +++ 25/drivers/net/lne390.c 2004-03-07 20:46:54.000000000 -0800 @@ -299,6 +299,9 @@ static int __init lne390_probe1(struct n dev->open = &lne390_open; dev->stop = &lne390_close; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = ei_poll; +#endif NS8390_init(dev, 0); return 0; cleanup: --- linux-2.6.4-rc2/drivers/net/mac8390.c 2004-02-17 20:48:43.000000000 -0800 +++ 25/drivers/net/mac8390.c 2004-03-07 20:46:54.000000000 -0800 @@ -442,6 +442,9 @@ static int __init mac8390_initdev(struct /* Now fill in our stuff */ dev->open = &mac8390_open; dev->stop = &mac8390_close; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = ei_poll; +#endif /* GAR, ei_status is actually a macro even though it looks global */ ei_status.name = cardname[type]; --- linux-2.6.4-rc2/drivers/net/Makefile 2004-03-03 23:12:45.000000000 -0800 +++ 25/drivers/net/Makefile 2004-03-07 20:47:03.000000000 -0800 @@ -188,3 +188,6 @@ obj-$(CONFIG_NET_TULIP) += tulip/ obj-$(CONFIG_HAMRADIO) += hamradio/ obj-$(CONFIG_IRDA) += irda/ +# Must come after all NICs that might use them +obj-$(CONFIG_NETCONSOLE) += netconsole.o +obj-$(CONFIG_KGDB) += kgdb_eth.o --- linux-2.6.4-rc2/drivers/net/myri_sbus.c 2003-10-17 15:58:03.000000000 -0700 +++ 25/drivers/net/myri_sbus.c 2004-03-07 20:46:58.000000000 -0800 @@ -435,9 +435,9 @@ static void myri_rx(struct myri_eth *mp, /* Check for errors. */ DRX(("rxd[%d]: %p len[%d] csum[%08x] ", entry, rxd, len, csum)); - sbus_dma_sync_single(mp->myri_sdev, - sbus_readl(&rxd->myri_scatters[0].addr), - RX_ALLOC_SIZE, SBUS_DMA_FROMDEVICE); + sbus_dma_sync_single_for_cpu(mp->myri_sdev, + sbus_readl(&rxd->myri_scatters[0].addr), + RX_ALLOC_SIZE, SBUS_DMA_FROMDEVICE); if (len < (ETH_HLEN + MYRI_PAD_LEN) || (skb->data[0] != MYRI_PAD_LEN)) { DRX(("ERROR[")); mp->enet_stats.rx_errors++; @@ -454,6 +454,10 @@ static void myri_rx(struct myri_eth *mp, drops++; DRX(("DROP ")); mp->enet_stats.rx_dropped++; + sbus_dma_sync_single_for_device(mp->myri_sdev, + sbus_readl(&rxd->myri_scatters[0].addr), + RX_ALLOC_SIZE, + SBUS_DMA_FROMDEVICE); sbus_writel(RX_ALLOC_SIZE, &rxd->myri_scatters[0].len); sbus_writel(index, &rxd->ctx); sbus_writel(1, &rxd->num_sg); @@ -508,6 +512,10 @@ static void myri_rx(struct myri_eth *mp, /* Reuse original ring buffer. */ DRX(("reuse ")); + sbus_dma_sync_single_for_device(mp->myri_sdev, + sbus_readl(&rxd->myri_scatters[0].addr), + RX_ALLOC_SIZE, + SBUS_DMA_FROMDEVICE); sbus_writel(RX_ALLOC_SIZE, &rxd->myri_scatters[0].len); sbus_writel(index, &rxd->ctx); sbus_writel(1, &rxd->num_sg); --- linux-2.6.4-rc2/drivers/net/natsemi.c 2004-02-03 20:42:36.000000000 -0800 +++ 25/drivers/net/natsemi.c 2004-03-07 20:46:58.000000000 -0800 @@ -1789,7 +1789,7 @@ static void netdev_rx(struct net_device skb->dev = dev; /* 16 byte align the IP header */ skb_reserve(skb, 2); - pci_dma_sync_single(np->pci_dev, + pci_dma_sync_single_for_cpu(np->pci_dev, np->rx_dma[entry], np->rx_skbuff[entry]->len, PCI_DMA_FROMDEVICE); @@ -1801,6 +1801,10 @@ static void netdev_rx(struct net_device memcpy(skb_put(skb, pkt_len), np->rx_skbuff[entry]->tail, pkt_len); #endif + pci_dma_sync_single_for_device(np->pci_dev, + np->rx_dma[entry], + np->rx_skbuff[entry]->len, + PCI_DMA_FROMDEVICE); } else { pci_unmap_single(np->pci_dev, np->rx_dma[entry], np->rx_skbuff[entry]->len, --- linux-2.6.4-rc2/drivers/net/ne2.c 2004-02-17 20:48:43.000000000 -0800 +++ 25/drivers/net/ne2.c 2004-03-07 20:46:54.000000000 -0800 @@ -509,6 +509,9 @@ static int __init ne2_probe1(struct net_ dev->open = &ne_open; dev->stop = &ne_close; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = ei_poll; +#endif NS8390_init(dev, 0); return 0; out: --- linux-2.6.4-rc2/drivers/net/ne2k_cbus.c 2004-02-17 20:48:43.000000000 -0800 +++ 25/drivers/net/ne2k_cbus.c 2004-03-07 20:46:54.000000000 -0800 @@ -534,6 +534,9 @@ static int __init ne_probe1(struct net_d ei_status.priv = 0; dev->open = &ne_open; dev->stop = &ne_close; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = ei_poll; +#endif NS8390_init(dev, 0); return 0; --- linux-2.6.4-rc2/drivers/net/ne2k-pci.c 2004-02-17 20:48:43.000000000 -0800 +++ 25/drivers/net/ne2k-pci.c 2004-03-07 20:46:54.000000000 -0800 @@ -359,6 +359,9 @@ static int __devinit ne2k_pci_init_one ( dev->open = &ne2k_pci_open; dev->stop = &ne2k_pci_close; dev->ethtool_ops = &ne2k_pci_ethtool_ops; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = ei_poll; +#endif NS8390_init(dev, 0); i = register_netdev(dev); --- linux-2.6.4-rc2/drivers/net/ne3210.c 2004-02-17 20:48:43.000000000 -0800 +++ 25/drivers/net/ne3210.c 2004-03-07 20:46:54.000000000 -0800 @@ -205,6 +205,9 @@ static int __init ne3210_eisa_probe (str dev->open = &ne3210_open; dev->stop = &ne3210_close; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = ei_poll; +#endif dev->if_port = ifmap_val[port_index]; if ((retval = register_netdev (dev))) --- linux-2.6.4-rc2/drivers/net/ne.c 2004-03-03 23:12:45.000000000 -0800 +++ 25/drivers/net/ne.c 2004-03-07 20:46:54.000000000 -0800 @@ -498,6 +498,9 @@ static int __init ne_probe1(struct net_d ei_status.priv = 0; dev->open = &ne_open; dev->stop = &ne_close; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = ei_poll; +#endif NS8390_init(dev, 0); return 0; --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/drivers/net/netconsole.c 2004-03-07 20:46:54.000000000 -0800 @@ -0,0 +1,127 @@ +/* + * linux/drivers/net/netconsole.c + * + * Copyright (C) 2001 Ingo Molnar + * + * This file contains the implementation of an IRQ-safe, crash-safe + * kernel console implementation that outputs kernel messages to the + * network. + * + * Modification history: + * + * 2001-09-17 started by Ingo Molnar. + * 2003-08-11 2.6 port by Matt Mackall + * simplified options + * generic card hooks + * works non-modular + * 2003-09-07 rewritten with netpoll api + */ + +/**************************************************************** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ****************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Maintainer: Matt Mackall "); +MODULE_DESCRIPTION("Console driver for network interfaces"); +MODULE_LICENSE("GPL"); + +static char config[256]; +module_param_string(netconsole, config, 256, 0); +MODULE_PARM_DESC(netconsole, " netconsole=[src-port]@[src-ip]/[dev],[tgt-port]@/[tgt-macaddr]\n"); + +static struct netpoll np = { + .name = "netconsole", + .dev_name = "eth0", + .local_port = 6665, + .remote_port = 6666, + .remote_mac = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, +}; +static int configured = 0; + +#define MAX_PRINT_CHUNK 1000 + +static void write_msg(struct console *con, const char *msg, unsigned int len) +{ + int frag, left; + unsigned long flags; + + if (!np.dev) + return; + + local_irq_save(flags); + + for(left = len; left; ) { + frag = min(left, MAX_PRINT_CHUNK); + netpoll_send_udp(&np, msg, frag); + msg += frag; + left -= frag; + } + + local_irq_restore(flags); +} + +static struct console netconsole = { + .flags = CON_ENABLED | CON_PRINTBUFFER, + .write = write_msg +}; + +static int option_setup(char *opt) +{ + configured = !netpoll_parse_options(&np, opt); + return 0; +} + +__setup("netconsole=", option_setup); + +static int init_netconsole(void) +{ + if(strlen(config)) + option_setup(config); + + if(!configured) { + printk("netconsole: not configured, aborting\n"); + return -EINVAL; + } + + if(netpoll_setup(&np)) + return -EINVAL; + + register_console(&netconsole); + printk(KERN_INFO "netconsole: network logging started\n"); + return 0; +} + +static void cleanup_netconsole(void) +{ + unregister_console(&netconsole); + netpoll_cleanup(&np); +} + +module_init(init_netconsole); +module_exit(cleanup_netconsole); --- linux-2.6.4-rc2/drivers/net/ns83820.c 2004-02-17 20:48:43.000000000 -0800 +++ 25/drivers/net/ns83820.c 2004-03-07 20:46:46.000000000 -0800 @@ -598,7 +598,7 @@ static inline int rx_refill(struct net_d } static void FASTCALL(rx_refill_atomic(struct net_device *ndev)); -static void rx_refill_atomic(struct net_device *ndev) +static void fastcall rx_refill_atomic(struct net_device *ndev) { rx_refill(ndev, GFP_ATOMIC); } @@ -620,7 +620,7 @@ static inline void clear_rx_desc(struct } static void FASTCALL(phy_intr(struct net_device *ndev)); -static void phy_intr(struct net_device *ndev) +static void fastcall phy_intr(struct net_device *ndev) { struct ns83820 *dev = PRIV(ndev); static char *speeds[] = { "10", "100", "1000", "1000(?)", "1000F" }; @@ -807,7 +807,7 @@ static void ns83820_cleanup_rx(struct ns } static void FASTCALL(ns83820_rx_kick(struct net_device *ndev)); -static void ns83820_rx_kick(struct net_device *ndev) +static void fastcall ns83820_rx_kick(struct net_device *ndev) { struct ns83820 *dev = PRIV(ndev); /*if (nr_rx_empty(dev) >= NR_RX_DESC/4)*/ { @@ -829,7 +829,7 @@ static void ns83820_rx_kick(struct net_d * */ static void FASTCALL(rx_irq(struct net_device *ndev)); -static void rx_irq(struct net_device *ndev) +static void fastcall rx_irq(struct net_device *ndev) { struct ns83820 *dev = PRIV(ndev); struct rx_info *info = &dev->rx_info; --- linux-2.6.4-rc2/drivers/net/oaknet.c 2004-02-17 20:48:43.000000000 -0800 +++ 25/drivers/net/oaknet.c 2004-03-07 20:46:54.000000000 -0800 @@ -192,6 +192,9 @@ static int __init oaknet_init(void) dev->open = oaknet_open; dev->stop = oaknet_close; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = ei_poll; +#endif NS8390_init(dev, FALSE); ret = register_netdev(dev); --- linux-2.6.4-rc2/drivers/net/pcmcia/3c574_cs.c 2004-02-17 20:48:43.000000000 -0800 +++ 25/drivers/net/pcmcia/3c574_cs.c 2004-03-07 20:48:10.000000000 -0800 @@ -384,6 +384,8 @@ static void tc574_detach(dev_link_t *lin #define CS_CHECK(fn, ret) \ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) +static char *ram_split[] = {"5:3", "3:1", "1:1", "3:5"}; + static void tc574_config(dev_link_t *link) { client_handle_t handle = link->handle; @@ -396,6 +398,7 @@ static void tc574_config(dev_link_t *lin ioaddr_t ioaddr; u16 *phys_addr; char *cardname; + union wn3_config config; phys_addr = (u16 *)dev->dev_addr; @@ -431,15 +434,7 @@ static void tc574_config(dev_link_t *lin dev->irq = link->irq.AssignedIRQ; dev->base_addr = link->io.BasePort1; - if (register_netdev(dev) != 0) { - printk(KERN_NOTICE "3c574_cs: register_netdev() failed\n"); - goto failed; - } - ioaddr = dev->base_addr; - strcpy(lp->node.dev_name, dev->name); - link->dev = &lp->node; - link->state &= ~DEV_CONFIG_PENDING; /* The 3c574 normally uses an EEPROM for configuration info, including the hardware address. The future products may include a modem chip @@ -467,24 +462,14 @@ static void tc574_config(dev_link_t *lin } else cardname = "3Com 3c574"; - printk(KERN_INFO "%s: %s at io %#3lx, irq %d, hw_addr ", - dev->name, cardname, dev->base_addr, dev->irq); - - for (i = 0; i < 6; i++) - printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : ".\n")); - { - u_char mcr, *ram_split[] = {"5:3", "3:1", "1:1", "3:5"}; - union wn3_config config; + u_char mcr; outw(2<<11, ioaddr + RunnerRdCtrl); mcr = inb(ioaddr + 2); outw(0<<11, ioaddr + RunnerRdCtrl); printk(KERN_INFO " ASIC rev %d,", mcr>>3); EL3WINDOW(3); config.i = inl(ioaddr + Wn3_Config); - printk(" %dK FIFO split %s Rx:Tx, %sMII interface.\n", - 8 << config.u.ram_size, ram_split[config.u.ram_split], - config.u.autoselect ? "autoselect " : ""); lp->default_media = config.u.xcvr; lp->autoselect = config.u.autoselect; } @@ -531,6 +516,25 @@ static void tc574_config(dev_link_t *lin } } + link->state &= ~DEV_CONFIG_PENDING; + link->dev = &lp->node; + + if (register_netdev(dev) != 0) { + printk(KERN_NOTICE "3c574_cs: register_netdev() failed\n"); + link->dev = NULL; + goto failed; + } + + strcpy(lp->node.dev_name, dev->name); + + printk(KERN_INFO "%s: %s at io %#3lx, irq %d, hw_addr ", + dev->name, cardname, dev->base_addr, dev->irq); + for (i = 0; i < 6; i++) + printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : ".\n")); + printk(" %dK FIFO split %s Rx:Tx, %sMII interface.\n", + 8 << config.u.ram_size, ram_split[config.u.ram_split], + config.u.autoselect ? "autoselect " : ""); + return; cs_failed: --- linux-2.6.4-rc2/drivers/net/pcmcia/3c589_cs.c 2004-02-17 20:48:43.000000000 -0800 +++ 25/drivers/net/pcmcia/3c589_cs.c 2004-03-07 20:48:10.000000000 -0800 @@ -308,7 +308,7 @@ static void tc589_config(dev_link_t *lin tuple_t tuple; cisparse_t parse; u16 buf[32], *phys_addr; - int last_fn, last_ret, i, j, multi = 0; + int last_fn, last_ret, i, j, multi = 0, fifo; ioaddr_t ioaddr; char *ram_split[] = {"5:3", "3:1", "1:1", "3:5"}; @@ -357,11 +357,6 @@ static void tc589_config(dev_link_t *lin dev->irq = link->irq.AssignedIRQ; dev->base_addr = link->io.BasePort1; - if (register_netdev(dev) != 0) { - printk(KERN_ERR "3c589_cs: register_netdev() failed\n"); - goto failed; - } - ioaddr = dev->base_addr; EL3WINDOW(0); @@ -382,13 +377,10 @@ static void tc589_config(dev_link_t *lin } } - strcpy(lp->node.dev_name, dev->name); - link->dev = &lp->node; - link->state &= ~DEV_CONFIG_PENDING; - /* The address and resource configuration register aren't loaded from the EEPROM and *must* be set to 0 and IRQ3 for the PCMCIA version. */ outw(0x3f00, ioaddr + 8); + fifo = inl(ioaddr); /* The if_port symbol can be set when the module is loaded */ if ((if_port >= 0) && (if_port <= 3)) @@ -396,14 +388,24 @@ static void tc589_config(dev_link_t *lin else printk(KERN_ERR "3c589_cs: invalid if_port requested\n"); + link->dev = &lp->node; + link->state &= ~DEV_CONFIG_PENDING; + + if (register_netdev(dev) != 0) { + printk(KERN_ERR "3c589_cs: register_netdev() failed\n"); + link->dev = NULL; + goto failed; + } + + strcpy(lp->node.dev_name, dev->name); + printk(KERN_INFO "%s: 3Com 3c%s, io %#3lx, irq %d, hw_addr ", dev->name, (multi ? "562" : "589"), dev->base_addr, dev->irq); for (i = 0; i < 6; i++) printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n")); - i = inl(ioaddr); printk(KERN_INFO " %dK FIFO split %s Rx:Tx, %s xcvr\n", - (i & 7) ? 32 : 8, ram_split[(i >> 16) & 3], + (fifo & 7) ? 32 : 8, ram_split[(fifo >> 16) & 3], if_names[dev->if_port]); return; --- linux-2.6.4-rc2/drivers/net/pcmcia/axnet_cs.c 2004-02-17 20:48:43.000000000 -0800 +++ 25/drivers/net/pcmcia/axnet_cs.c 2004-03-07 20:48:10.000000000 -0800 @@ -430,19 +430,11 @@ static void axnet_config(dev_link_t *lin ei_status.block_input = &block_input; ei_status.block_output = &block_output; - strcpy(info->node.dev_name, dev->name); - if (inb(dev->base_addr + AXNET_TEST) != 0) info->flags |= IS_AX88790; else info->flags |= IS_AX88190; - printk(KERN_INFO "%s: Asix AX88%d90: io %#3lx, irq %d, hw_addr ", - dev->name, ((info->flags & IS_AX88790) ? 7 : 1), - dev->base_addr, dev->irq); - for (i = 0; i < 6; i++) - printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n")); - if (info->flags & IS_AX88790) outb(0x10, dev->base_addr + AXNET_GPIO); /* select Internal PHY */ @@ -463,19 +455,27 @@ static void axnet_config(dev_link_t *lin } info->phy_id = (i < 32) ? i : -1; - if (i < 32) { - DEBUG(0, " MII transceiver at index %d, status %x.\n", i, j); - } else { - printk(KERN_NOTICE " No MII transceivers found!\n"); - } + link->dev = &info->node; + link->state &= ~DEV_CONFIG_PENDING; if (register_netdev(dev) != 0) { printk(KERN_NOTICE "axnet_cs: register_netdev() failed\n"); + link->dev = NULL; goto failed; } - link->dev = &info->node; - link->state &= ~DEV_CONFIG_PENDING; + strcpy(info->node.dev_name, dev->name); + + printk(KERN_INFO "%s: Asix AX88%d90: io %#3lx, irq %d, hw_addr ", + dev->name, ((info->flags & IS_AX88790) ? 7 : 1), + dev->base_addr, dev->irq); + for (i = 0; i < 6; i++) + printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n")); + if (info->phy_id != -1) { + DEBUG(0, " MII transceiver at index %d, status %x.\n", info->phy_id, j); + } else { + printk(KERN_NOTICE " No MII transceivers found!\n"); + } return; cs_failed: --- linux-2.6.4-rc2/drivers/net/pcmcia/com20020_cs.c 2004-02-17 20:48:43.000000000 -0800 +++ 25/drivers/net/pcmcia/com20020_cs.c 2004-03-07 20:48:10.000000000 -0800 @@ -141,7 +141,6 @@ static dev_link_t *dev_list; typedef struct com20020_dev_t { struct net_device *dev; - int dev_configured; dev_node_t node; } com20020_dev_t; @@ -277,13 +276,10 @@ static void com20020_detach(dev_link_t * dev = info->dev; if (dev) { - if (info->dev_configured) + if (link->dev) { DEBUG(1,"unregister...\n"); - if (netif_running(dev)) - dev->stop(dev); - unregister_netdev(dev); /* @@ -398,17 +394,18 @@ static void com20020_config(dev_link_t * lp->card_name = "PCMCIA COM20020"; lp->card_flags = ARC_CAN_10MBIT; /* pretend all of them can 10Mbit */ + link->dev = &info->node; + link->state &= ~DEV_CONFIG_PENDING; + i = com20020_found(dev, 0); /* calls register_netdev */ if (i != 0) { DEBUG(1,KERN_NOTICE "com20020_cs: com20020_found() failed\n"); + link->dev = NULL; goto failed; } - info->dev_configured = 1; strcpy(info->node.dev_name, dev->name); - link->dev = &info->node; - link->state &= ~DEV_CONFIG_PENDING; DEBUG(1,KERN_INFO "%s: port %#3lx, irq %d\n", dev->name, dev->base_addr, dev->irq); --- linux-2.6.4-rc2/drivers/net/pcmcia/fmvj18x_cs.c 2004-02-17 20:48:43.000000000 -0800 +++ 25/drivers/net/pcmcia/fmvj18x_cs.c 2004-03-07 20:48:10.000000000 -0800 @@ -510,10 +510,6 @@ static void fmvj18x_config(dev_link_t *l CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf)); dev->irq = link->irq.AssignedIRQ; dev->base_addr = link->io.BasePort1; - if (register_netdev(dev) != 0) { - printk(KERN_NOTICE "fmvj18x_cs: register_netdev() failed\n"); - goto failed; - } if (link->io.BasePort2 != 0) fmvj18x_setup_mfc(link); @@ -575,7 +571,6 @@ static void fmvj18x_config(dev_link_t *l /* Read MACID from Buggy CIS */ if (fmvj18x_get_hwinfo(link, tuple.TupleData) == -1) { printk(KERN_NOTICE "fmvj18x_cs: unable to read hardware net address.\n"); - unregister_netdev(dev); goto failed; } for (i = 0 ; i < 6; i++) { @@ -592,10 +587,18 @@ static void fmvj18x_config(dev_link_t *l break; } - strcpy(lp->node.dev_name, dev->name); + lp->cardtype = cardtype; link->dev = &lp->node; + link->state &= ~DEV_CONFIG_PENDING; + + if (register_netdev(dev) != 0) { + printk(KERN_NOTICE "fmvj18x_cs: register_netdev() failed\n"); + link->dev = NULL; + goto failed; + } + + strcpy(lp->node.dev_name, dev->name); - lp->cardtype = cardtype; /* print current configuration */ printk(KERN_INFO "%s: %s, sram %s, port %#3lx, irq %d, hw_addr ", dev->name, card_name, sram_config == 0 ? "4K TX*2" : "8K TX*2", @@ -603,7 +606,6 @@ static void fmvj18x_config(dev_link_t *l for (i = 0; i < 6; i++) printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n")); - link->state &= ~DEV_CONFIG_PENDING; return; cs_failed: --- linux-2.6.4-rc2/drivers/net/pcmcia/ibmtr_cs.c 2004-02-17 20:48:43.000000000 -0800 +++ 25/drivers/net/pcmcia/ibmtr_cs.c 2004-03-07 20:48:10.000000000 -0800 @@ -317,13 +317,10 @@ static void ibmtr_config(dev_link_t *lin /* Try PRIMARY card at 0xA20-0xA23 */ link->io.BasePort1 = 0xA20; i = pcmcia_request_io(link->handle, &link->io); - if (i == CS_SUCCESS) { - memcpy(info->node.dev_name, "tr0\0", 4); - } else { + if (i != CS_SUCCESS) { /* Couldn't get 0xA20-0xA23. Try ALTERNATE at 0xA24-0xA27. */ link->io.BasePort1 = 0xA24; CS_CHECK(RequestIO, pcmcia_request_io(link->handle, &link->io)); - memcpy(info->node.dev_name, "tr1\0", 4); } dev->base_addr = link->io.BasePort1; @@ -367,15 +364,17 @@ static void ibmtr_config(dev_link_t *lin Adapters Technical Reference" SC30-3585 for this info. */ ibmtr_hw_setup(dev, mmiobase); + link->dev = &info->node; + link->state &= ~DEV_CONFIG_PENDING; + i = ibmtr_probe_card(dev); - if (i != 0) { printk(KERN_NOTICE "ibmtr_cs: register_netdev() failed\n"); + link->dev = NULL; goto failed; } - link->dev = &info->node; - link->state &= ~DEV_CONFIG_PENDING; + strcpy(info->node.dev_name, dev->name); printk(KERN_INFO "%s: port %#3lx, irq %d,", dev->name, dev->base_addr, dev->irq); --- linux-2.6.4-rc2/drivers/net/pcmcia/nmclan_cs.c 2004-02-17 20:48:43.000000000 -0800 +++ 25/drivers/net/pcmcia/nmclan_cs.c 2004-03-07 20:48:10.000000000 -0800 @@ -734,11 +734,6 @@ static void nmclan_config(dev_link_t *li CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf)); dev->irq = link->irq.AssignedIRQ; dev->base_addr = link->io.BasePort1; - i = register_netdev(dev); - if (i != 0) { - printk(KERN_NOTICE "nmclan_cs: register_netdev() failed\n"); - goto failed; - } ioaddr = dev->base_addr; @@ -777,10 +772,18 @@ static void nmclan_config(dev_link_t *li else printk(KERN_NOTICE "nmclan_cs: invalid if_port requested\n"); - strcpy(lp->node.dev_name, dev->name); link->dev = &lp->node; link->state &= ~DEV_CONFIG_PENDING; + i = register_netdev(dev); + if (i != 0) { + printk(KERN_NOTICE "nmclan_cs: register_netdev() failed\n"); + link->dev = NULL; + goto failed; + } + + strcpy(lp->node.dev_name, dev->name); + printk(KERN_INFO "%s: nmclan: port %#3lx, irq %d, %s port, hw_addr ", dev->name, dev->base_addr, dev->irq, if_names[dev->if_port]); for (i = 0; i < 6; i++) --- linux-2.6.4-rc2/drivers/net/pcmcia/smc91c92_cs.c 2004-02-17 20:48:44.000000000 -0800 +++ 25/drivers/net/pcmcia/smc91c92_cs.c 2004-03-07 20:48:10.000000000 -0800 @@ -901,6 +901,7 @@ static void smc91c92_config(dev_link_t * char *name; int i, j, rev; ioaddr_t ioaddr; + u_long mir; DEBUG(0, "smc91c92_config(0x%p)\n", link); @@ -952,11 +953,6 @@ static void smc91c92_config(dev_link_t * else printk(KERN_NOTICE "smc91c92_cs: invalid if_port requested\n"); - if (register_netdev(dev) != 0) { - printk(KERN_ERR "smc91c92_cs: register_netdev() failed\n"); - goto config_undo; - } - switch (smc->manfid) { case MANFID_OSITECH: case MANFID_PSION: @@ -977,8 +973,6 @@ static void smc91c92_config(dev_link_t * goto config_undo; } - strcpy(smc->node.dev_name, dev->name); - link->dev = &smc->node; smc->duplex = 0; smc->rx_ovrn = 0; @@ -993,25 +987,16 @@ static void smc91c92_config(dev_link_t * case 8: name = "100-FD"; break; case 9: name = "110"; break; } - printk(KERN_INFO "%s: smc91c%s rev %d: io %#3lx, irq %d, " - "hw_addr ", dev->name, name, (rev & 0x0f), dev->base_addr, - dev->irq); - for (i = 0; i < 6; i++) - printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n")); ioaddr = dev->base_addr; if (rev > 0) { - u_long mir, mcr; + u_long mcr; SMC_SELECT_BANK(0); mir = inw(ioaddr + MEMINFO) & 0xff; if (mir == 0xff) mir++; /* Get scale factor for memory size */ mcr = ((rev >> 4) > 3) ? inw(ioaddr + MEMCFG) : 0x0200; mir *= 128 * (1<<((mcr >> 9) & 7)); - if (mir & 0x3ff) - printk(KERN_INFO " %lu byte", mir); - else - printk(KERN_INFO " %lu kb", mir>>10); SMC_SELECT_BANK(1); smc->cfg = inw(ioaddr + CONFIG) & ~CFG_AUI_SELECT; smc->cfg |= CFG_NO_WAIT | CFG_16BIT | CFG_STATIC; @@ -1019,9 +1004,8 @@ static void smc91c92_config(dev_link_t * smc->cfg |= CFG_IRQ_SEL_1 | CFG_IRQ_SEL_0; if ((rev >> 4) >= 7) smc->cfg |= CFG_MII_SELECT; - printk(" buffer, %s xcvr\n", (smc->cfg & CFG_MII_SELECT) ? - "MII" : if_names[dev->if_port]); - } + } else + mir = 0; if (smc->cfg & CFG_MII_SELECT) { SMC_SELECT_BANK(3); @@ -1031,16 +1015,45 @@ static void smc91c92_config(dev_link_t * if ((j != 0) && (j != 0xffff)) break; } smc->mii_if.phy_id = (i < 32) ? i : -1; - if (i < 32) { - DEBUG(0, " MII transceiver at index %d, status %x.\n", i, j); - } else { - printk(KERN_NOTICE " No MII transceivers found!\n"); - } SMC_SELECT_BANK(0); } + link->dev = &smc->node; link->state &= ~DEV_CONFIG_PENDING; + + if (register_netdev(dev) != 0) { + printk(KERN_ERR "smc91c92_cs: register_netdev() failed\n"); + link->dev = NULL; + goto config_undo; + } + + strcpy(smc->node.dev_name, dev->name); + + printk(KERN_INFO "%s: smc91c%s rev %d: io %#3lx, irq %d, " + "hw_addr ", dev->name, name, (rev & 0x0f), dev->base_addr, + dev->irq); + for (i = 0; i < 6; i++) + printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n")); + + if (rev > 0) { + if (mir & 0x3ff) + printk(KERN_INFO " %lu byte", mir); + else + printk(KERN_INFO " %lu kb", mir>>10); + printk(" buffer, %s xcvr\n", (smc->cfg & CFG_MII_SELECT) ? + "MII" : if_names[dev->if_port]); + } + + if (smc->cfg & CFG_MII_SELECT) { + if (smc->mii_if.phy_id != -1) { + DEBUG(0, " MII transceiver at index %d, status %x.\n", + smc->mii_if.phy_id, j); + } else { + printk(KERN_NOTICE " No MII transceivers found!\n"); + } + } + return; config_undo: --- linux-2.6.4-rc2/drivers/net/pcmcia/xirc2ps_cs.c 2004-02-17 20:48:44.000000000 -0800 +++ 25/drivers/net/pcmcia/xirc2ps_cs.c 2004-03-07 20:48:10.000000000 -0800 @@ -1114,17 +1114,20 @@ xirc2ps_config(dev_link_t * link) /* we can now register the device with the net subsystem */ dev->irq = link->irq.AssignedIRQ; dev->base_addr = link->io.BasePort1; + + if (local->dingo) + do_reset(dev, 1); /* a kludge to make the cem56 work */ + + link->dev = &local->node; + link->state &= ~DEV_CONFIG_PENDING; + if ((err=register_netdev(dev))) { printk(KNOT_XIRC "register_netdev() failed\n"); + link->dev = NULL; goto config_error; } strcpy(local->node.dev_name, dev->name); - link->dev = &local->node; - link->state &= ~DEV_CONFIG_PENDING; - - if (local->dingo) - do_reset(dev, 1); /* a kludge to make the cem56 work */ /* give some infos about the hardware */ printk(KERN_INFO "%s: %s: port %#3lx, irq %d, hwaddr", --- linux-2.6.4-rc2/drivers/net/pcnet32.c 2004-03-03 23:12:45.000000000 -0800 +++ 25/drivers/net/pcnet32.c 2004-03-07 20:46:58.000000000 -0800 @@ -479,6 +479,14 @@ static struct pcnet32_access pcnet32_dwi .reset = pcnet32_dwio_reset }; +#ifdef CONFIG_NET_POLL_CONTROLLER +static void pcnet32_poll_controller(struct net_device *dev) +{ + disable_irq(dev->irq); + pcnet32_interrupt(0, dev, NULL); + enable_irq(dev->irq); +} +#endif static int pcnet32_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) @@ -1106,6 +1114,10 @@ pcnet32_probe1(unsigned long ioaddr, uns dev->tx_timeout = pcnet32_tx_timeout; dev->watchdog_timeo = (5*HZ); +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = pcnet32_poll_controller; +#endif + /* Fill in the generic fields of the device structure. */ if (register_netdev(dev)) goto err_free_consistent; @@ -1732,13 +1744,17 @@ pcnet32_rx(struct net_device *dev) if (!rx_in_place) { skb_reserve(skb,2); /* 16 byte align */ skb_put(skb,pkt_len); /* Make room */ - pci_dma_sync_single(lp->pci_dev, - lp->rx_dma_addr[entry], - PKT_BUF_SZ-2, - PCI_DMA_FROMDEVICE); + pci_dma_sync_single_for_cpu(lp->pci_dev, + lp->rx_dma_addr[entry], + PKT_BUF_SZ-2, + PCI_DMA_FROMDEVICE); eth_copy_and_sum(skb, (unsigned char *)(lp->rx_skbuff[entry]->tail), pkt_len,0); + pci_dma_sync_single_for_device(lp->pci_dev, + lp->rx_dma_addr[entry], + PKT_BUF_SZ-2, + PCI_DMA_FROMDEVICE); } lp->stats.rx_bytes += skb->len; skb->protocol=eth_type_trans(skb,dev); --- linux-2.6.4-rc2/drivers/net/r8169.c 2004-03-03 23:12:45.000000000 -0800 +++ 25/drivers/net/r8169.c 2004-03-07 20:46:54.000000000 -0800 @@ -56,9 +56,11 @@ VERSION 1.2 <2002/11/30> printk( "Assertion failed! %s,%s,%s,line=%d\n", \ #expr,__FILE__,__FUNCTION__,__LINE__); \ } +#define dprintk(fmt, args...) do { printk(PFX fmt, ## args) } while (0) #else #define assert(expr) do {} while (0) -#endif +#define dprintk(fmt, args...) do {} while (0) +#endif /* RTL8169_DEBUG */ /* media options */ #define MAX_UNITS 8 @@ -89,9 +91,12 @@ static int multicast_filter_limit = 32; #define NUM_TX_DESC 64 /* Number of Tx descriptor registers */ #define NUM_RX_DESC 64 /* Number of Rx descriptor registers */ #define RX_BUF_SIZE 1536 /* Rx Buffer size */ +#define R8169_TX_RING_BYTES (NUM_TX_DESC * sizeof(struct TxDesc)) +#define R8169_RX_RING_BYTES (NUM_RX_DESC * sizeof(struct RxDesc)) #define RTL_MIN_IO_SIZE 0x80 -#define TX_TIMEOUT (6*HZ) +#define RTL8169_TX_TIMEOUT (6*HZ) +#define RTL8169_PHY_TIMEOUT (HZ) /* write/read MMIO register */ #define RTL_W8(reg, val8) writeb ((val8), ioaddr + (reg)) @@ -101,11 +106,35 @@ static int multicast_filter_limit = 32; #define RTL_R16(reg) readw (ioaddr + (reg)) #define RTL_R32(reg) ((unsigned long) readl (ioaddr + (reg))) -static struct { +enum mac_version { + RTL_GIGA_MAC_VER_B = 0x00, + /* RTL_GIGA_MAC_VER_C = 0x03, */ + RTL_GIGA_MAC_VER_D = 0x01, + RTL_GIGA_MAC_VER_E = 0x02 +}; + +enum phy_version { + RTL_GIGA_PHY_VER_C = 0x03, /* PHY Reg 0x03 bit0-3 == 0x0000 */ + RTL_GIGA_PHY_VER_D = 0x04, /* PHY Reg 0x03 bit0-3 == 0x0000 */ + RTL_GIGA_PHY_VER_E = 0x05, /* PHY Reg 0x03 bit0-3 == 0x0000 */ + RTL_GIGA_PHY_VER_F = 0x06, /* PHY Reg 0x03 bit0-3 == 0x0001 */ + RTL_GIGA_PHY_VER_G = 0x07, /* PHY Reg 0x03 bit0-3 == 0x0002 */ +}; + + +#define _R(NAME,MAC,MASK) \ + { .name = NAME, .mac_version = MAC, .RxConfigMask = MASK } + +const static struct { const char *name; -} board_info[] __devinitdata = { - { -"RealTek RTL8169 Gigabit Ethernet"},}; + u8 mac_version; + u32 RxConfigMask; /* Clears the bits supported by this chip */ +} rtl_chip_info[] = { + _R("RTL8169", RTL_GIGA_MAC_VER_B, 0xff7e1880), + _R("RTL8169s/8110s", RTL_GIGA_MAC_VER_D, 0xff7e1880), + _R("RTL8169s/8110s", RTL_GIGA_MAC_VER_E, 0xff7e1880) +}; +#undef _R static struct pci_device_id rtl8169_pci_tbl[] = { {0x10ec, 0x8169, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, @@ -114,6 +143,8 @@ static struct pci_device_id rtl8169_pci_ MODULE_DEVICE_TABLE(pci, rtl8169_pci_tbl); +static int rx_copybreak = 200; + enum RTL8169_registers { MAC0 = 0, /* Ethernet hardware address. */ MAR0 = 8, /* Multicast filter. */ @@ -242,14 +273,6 @@ enum RTL8169_register_content { TBILinkOK = 0x02000000, }; -const static struct { - const char *name; - u8 version; /* depend on RTL8169 docs */ - u32 RxConfigMask; /* should clear the bits supported by this chip */ -} rtl_chip_info[] = { - { -"RTL-8169", 0x00, 0xff7e1880,},}; - enum _DescStatusBit { OWNbit = 0x80000000, EORbit = 0x40000000, @@ -257,6 +280,8 @@ enum _DescStatusBit { LSbit = 0x10000000, }; +#define RsvdMask 0x3fffc000 + struct TxDesc { u32 status; u32 vlan_tag; @@ -277,28 +302,33 @@ struct rtl8169_private { struct net_device_stats stats; /* statistics of net device */ spinlock_t lock; /* spin lock flag */ int chipset; - unsigned long cur_rx; /* Index into the Rx descriptor buffer of next Rx pkt. */ - unsigned long cur_tx; /* Index into the Tx descriptor buffer of next Rx pkt. */ - unsigned long dirty_tx; - unsigned char *TxDescArrays; /* Index of Tx Descriptor buffer */ - unsigned char *RxDescArrays; /* Index of Rx Descriptor buffer */ + int mac_version; + int phy_version; + u32 cur_rx; /* Index into the Rx descriptor buffer of next Rx pkt. */ + u32 cur_tx; /* Index into the Tx descriptor buffer of next Rx pkt. */ + u32 dirty_rx; + u32 dirty_tx; struct TxDesc *TxDescArray; /* Index of 256-alignment Tx Descriptor buffer */ struct RxDesc *RxDescArray; /* Index of 256-alignment Rx Descriptor buffer */ - unsigned char *RxBufferRings; /* Index of Rx Buffer */ - unsigned char *RxBufferRing[NUM_RX_DESC]; /* Index of Rx Buffer array */ + dma_addr_t TxPhyAddr; + dma_addr_t RxPhyAddr; + struct sk_buff *Rx_skbuff[NUM_RX_DESC]; /* Rx data buffers */ struct sk_buff *Tx_skbuff[NUM_TX_DESC]; /* Index of Transmit data buffer */ + struct timer_list timer; + unsigned long phy_link_down_cnt; }; MODULE_AUTHOR("Realtek"); MODULE_DESCRIPTION("RealTek RTL-8169 Gigabit Ethernet driver"); MODULE_PARM(media, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(rx_copybreak, "i"); MODULE_LICENSE("GPL"); static int rtl8169_open(struct net_device *dev); static int rtl8169_start_xmit(struct sk_buff *skb, struct net_device *dev); static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance, struct pt_regs *regs); -static void rtl8169_init_ring(struct net_device *dev); +static int rtl8169_init_ring(struct net_device *dev); static void rtl8169_hw_start(struct net_device *dev); static int rtl8169_close(struct net_device *dev); static void rtl8169_set_rx_mode(struct net_device *dev); @@ -306,11 +336,15 @@ static void rtl8169_tx_timeout(struct ne static struct net_device_stats *rtl8169_get_stats(struct net_device *netdev); static const u16 rtl8169_intr_mask = - SYSErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver | TxErr | TxOK | - RxErr | RxOK; + RxUnderrun | RxOverflow | RxFIFOOver | TxErr | TxOK | RxErr | RxOK; static const unsigned int rtl8169_rx_config = (RX_FIFO_THRESH << RxCfgFIFOShift) | (RX_DMA_BURST << RxCfgDMAShift); +#define PHY_Cap_10_Half_Or_Less PHY_Cap_10_Half +#define PHY_Cap_10_Full_Or_Less PHY_Cap_10_Full | PHY_Cap_10_Half_Or_Less +#define PHY_Cap_100_Half_Or_Less PHY_Cap_100_Half | PHY_Cap_10_Full_Or_Less +#define PHY_Cap_100_Full_Or_Less PHY_Cap_100_Full | PHY_Cap_100_Half_Or_Less + void mdio_write(void *ioaddr, int RegAddr, int value) { @@ -342,13 +376,258 @@ mdio_read(void *ioaddr, int RegAddr) if (RTL_R32(PHYAR) & 0x80000000) { value = (int) (RTL_R32(PHYAR) & 0xFFFF); break; - } else { - udelay(100); } + udelay(100); } return value; } +static void rtl8169_write_gmii_reg_bit(void *ioaddr, int reg, int bitnum, + int bitval) +{ + int val; + + val = mdio_read(ioaddr, reg); + val = (bitval == 1) ? + val | (bitval << bitnum) : val & ~(0x0001 << bitnum); + mdio_write(ioaddr, reg, val & 0xffff); +} + +static void rtl8169_get_mac_version(struct rtl8169_private *tp, void *ioaddr) +{ + const struct { + u32 mask; + int mac_version; + } mac_info[] = { + { 0x1 << 26, RTL_GIGA_MAC_VER_E }, + { 0x1 << 23, RTL_GIGA_MAC_VER_D }, + { 0x00000000, RTL_GIGA_MAC_VER_B } /* Catch-all */ + }, *p = mac_info; + u32 reg; + + reg = RTL_R32(TxConfig) & 0x7c800000; + while ((reg & p->mask) != p->mask) + p++; + tp->mac_version = p->mac_version; +} + +static void rtl8169_print_mac_version(struct rtl8169_private *tp) +{ + struct { + int version; + char *msg; + } mac_print[] = { + { RTL_GIGA_MAC_VER_E, "RTL_GIGA_MAC_VER_E" }, + { RTL_GIGA_MAC_VER_D, "RTL_GIGA_MAC_VER_D" }, + { RTL_GIGA_MAC_VER_B, "RTL_GIGA_MAC_VER_B" }, + { 0, NULL } + }, *p; + + for (p = mac_print; p->msg; p++) { + if (tp->mac_version == p->version) { + dprintk("mac_version == %s (%04d)\n", p->msg, + p->version); + return; + } + } + dprintk("mac_version == Unknown\n"); +} + +static void rtl8169_get_phy_version(struct rtl8169_private *tp, void *ioaddr) +{ + const struct { + u16 mask; + u16 set; + int phy_version; + } phy_info[] = { + { 0x000f, 0x0002, RTL_GIGA_PHY_VER_G }, + { 0x000f, 0x0001, RTL_GIGA_PHY_VER_F }, + { 0x000f, 0x0000, RTL_GIGA_PHY_VER_E }, + { 0x0000, 0x0000, RTL_GIGA_PHY_VER_D } /* Catch-all */ + }, *p = phy_info; + u16 reg; + + reg = mdio_read(ioaddr, 3) & 0xffff; + while ((reg & p->mask) != p->set) + p++; + tp->phy_version = p->phy_version; +} + +static void rtl8169_print_phy_version(struct rtl8169_private *tp) +{ + struct { + int version; + char *msg; + u32 reg; + } phy_print[] = { + { RTL_GIGA_PHY_VER_G, "RTL_GIGA_PHY_VER_G", 0x0002 }, + { RTL_GIGA_PHY_VER_F, "RTL_GIGA_PHY_VER_F", 0x0001 }, + { RTL_GIGA_PHY_VER_E, "RTL_GIGA_PHY_VER_E", 0x0000 }, + { RTL_GIGA_PHY_VER_D, "RTL_GIGA_PHY_VER_D", 0x0000 }, + { 0, NULL, 0x0000 } + }, *p; + + for (p = phy_print; p->msg; p++) { + if (tp->phy_version == p->version) { + dprintk("phy_version == %s (%04x)\n", p->msg, p->reg); + return; + } + } + dprintk("phy_version == Unknown\n"); +} + +static void rtl8169_hw_phy_config(struct net_device *dev) +{ + struct rtl8169_private *tp = dev->priv; + void *ioaddr = tp->mmio_addr; + struct { + u16 regs[5]; /* Beware of bit-sign propagation */ + } phy_magic[5] = { { + { 0x0000, //w 4 15 12 0 + 0x00a1, //w 3 15 0 00a1 + 0x0008, //w 2 15 0 0008 + 0x1020, //w 1 15 0 1020 + 0x1000 } },{ //w 0 15 0 1000 + { 0x7000, //w 4 15 12 7 + 0xff41, //w 3 15 0 ff41 + 0xde60, //w 2 15 0 de60 + 0x0140, //w 1 15 0 0140 + 0x0077 } },{ //w 0 15 0 0077 + { 0xa000, //w 4 15 12 a + 0xdf01, //w 3 15 0 df01 + 0xdf20, //w 2 15 0 df20 + 0xff95, //w 1 15 0 ff95 + 0xfa00 } },{ //w 0 15 0 fa00 + { 0xb000, //w 4 15 12 b + 0xff41, //w 3 15 0 ff41 + 0xde20, //w 2 15 0 de20 + 0x0140, //w 1 15 0 0140 + 0x00bb } },{ //w 0 15 0 00bb + { 0xf000, //w 4 15 12 f + 0xdf01, //w 3 15 0 df01 + 0xdf20, //w 2 15 0 df20 + 0xff95, //w 1 15 0 ff95 + 0xbf00 } //w 0 15 0 bf00 + } + }, *p = phy_magic; + int i; + + rtl8169_print_mac_version(tp); + rtl8169_print_phy_version(tp); + + if (tp->mac_version <= RTL_GIGA_MAC_VER_B) + return; + if (tp->phy_version >= RTL_GIGA_PHY_VER_F) + return; + + dprintk("MAC version != 0 && PHY version == 0 or 1\n"); + dprintk("Do final_reg2.cfg\n"); + + /* Shazam ! */ + + // phy config for RTL8169s mac_version C chip + mdio_write(ioaddr, 31, 0x0001); //w 31 2 0 1 + mdio_write(ioaddr, 21, 0x1000); //w 21 15 0 1000 + mdio_write(ioaddr, 24, 0x65c7); //w 24 15 0 65c7 + rtl8169_write_gmii_reg_bit(ioaddr, 4, 11, 0); //w 4 11 11 0 + + for (i = 0; i < ARRAY_SIZE(phy_magic); i++, p++) { + int val, pos = 4; + + val = (mdio_read(ioaddr, pos) & 0x0fff) | (p->regs[0] & 0xffff); + mdio_write(ioaddr, pos, val); + while (--pos >= 0) + mdio_write(ioaddr, pos, p->regs[4 - pos] & 0xffff); + rtl8169_write_gmii_reg_bit(ioaddr, 4, 11, 1); //w 4 11 11 1 + rtl8169_write_gmii_reg_bit(ioaddr, 4, 11, 0); //w 4 11 11 0 + } + mdio_write(ioaddr, 31, 0x0000); //w 31 2 0 0 +} + +static void rtl8169_hw_phy_reset(struct net_device *dev) +{ + struct rtl8169_private *tp = dev->priv; + void *ioaddr = tp->mmio_addr; + int i, val; + + printk(KERN_WARNING PFX "%s: Reset RTL8169s PHY\n", dev->name); + + val = (mdio_read(ioaddr, 0) | 0x8000) & 0xffff; + mdio_write(ioaddr, 0, val); + + for (i = 50; i >= 0; i--) { + if (!(mdio_read(ioaddr, 0) & 0x8000)) + break; + udelay(100); /* Gross */ + } + + if (i < 0) { + printk(KERN_WARNING PFX "%s: no PHY Reset ack. Giving up.\n", + dev->name); + } +} + +static void rtl8169_phy_timer(unsigned long __opaque) +{ + struct net_device *dev = (struct net_device *)__opaque; + struct rtl8169_private *tp = dev->priv; + struct timer_list *timer = &tp->timer; + void *ioaddr = tp->mmio_addr; + + assert(tp->mac_version > RTL_GIGA_MAC_VER_B); + assert(tp->phy_version < RTL_GIGA_PHY_VER_G); + + if (RTL_R8(PHYstatus) & LinkStatus) + tp->phy_link_down_cnt = 0; + else { + tp->phy_link_down_cnt++; + if (tp->phy_link_down_cnt >= 12) { + int reg; + + // If link on 1000, perform phy reset. + reg = mdio_read(ioaddr, PHY_1000_CTRL_REG); + if (reg & PHY_Cap_1000_Full) + rtl8169_hw_phy_reset(dev); + + tp->phy_link_down_cnt = 0; + } + } + + mod_timer(timer, RTL8169_PHY_TIMEOUT); +} + +static inline void rtl8169_delete_timer(struct net_device *dev) +{ + struct rtl8169_private *tp = dev->priv; + struct timer_list *timer = &tp->timer; + + if ((tp->mac_version <= RTL_GIGA_MAC_VER_B) || + (tp->phy_version >= RTL_GIGA_PHY_VER_G)) + return; + + del_timer_sync(timer); + + tp->phy_link_down_cnt = 0; +} + +static inline void rtl8169_request_timer(struct net_device *dev) +{ + struct rtl8169_private *tp = dev->priv; + struct timer_list *timer = &tp->timer; + + if ((tp->mac_version <= RTL_GIGA_MAC_VER_B) || + (tp->phy_version >= RTL_GIGA_PHY_VER_G)) + return; + + tp->phy_link_down_cnt = 0; + + init_timer(timer); + timer->expires = jiffies + RTL8169_PHY_TIMEOUT; + timer->data = (unsigned long)(dev); + timer->function = rtl8169_phy_timer; + add_timer(timer); +} + static int __devinit rtl8169_init_board(struct pci_dev *pdev, struct net_device **dev_out, void **ioaddr_out) @@ -356,9 +635,9 @@ rtl8169_init_board(struct pci_dev *pdev, void *ioaddr = NULL; struct net_device *dev; struct rtl8169_private *tp; - int rc, i; unsigned long mmio_start, mmio_end, mmio_flags, mmio_len; - u32 tmp; + int rc, i, acpi_idle_state = 0, pm_cap; + assert(pdev != NULL); assert(ioaddr_out != NULL); @@ -379,8 +658,22 @@ rtl8169_init_board(struct pci_dev *pdev, // enable device (incl. PCI PM wakeup and hotplug setup) rc = pci_enable_device(pdev); - if (rc) + if (rc) { + printk(KERN_ERR PFX "%s: unable to enable device\n", pdev->slot_name); goto err_out; + } + + /* save power state before pci_enable_device overwrites it */ + pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM); + if (pm_cap) { + u16 pwr_command; + + pci_read_config_word(pdev, pm_cap + PCI_PM_CTRL, &pwr_command); + acpi_idle_state = pwr_command & PCI_PM_CTRL_STATE_MASK; + } else { + printk(KERN_ERR PFX "Cannot find PowerManagement capability, aborting.\n"); + goto err_out_free_res; + } mmio_start = pci_resource_start(pdev, 1); mmio_end = pci_resource_end(pdev, 1); @@ -402,8 +695,10 @@ rtl8169_init_board(struct pci_dev *pdev, } rc = pci_request_regions(pdev, dev->name); - if (rc) + if (rc) { + printk(KERN_ERR PFX "%s: Could not request regions.\n", pdev->slot_name); goto err_out_disable; + } // enable PCI bus-mastering pci_set_master(pdev); @@ -420,30 +715,32 @@ rtl8169_init_board(struct pci_dev *pdev, RTL_W8(ChipCmd, CmdReset); // Check that the chip has finished the reset. - for (i = 1000; i > 0; i--) + for (i = 1000; i > 0; i--) { if ((RTL_R8(ChipCmd) & CmdReset) == 0) break; - else - udelay(10); + udelay(10); + } - // identify chip attached to board - tmp = RTL_R32(TxConfig); - tmp = ((tmp & 0x7c000000) + ((tmp & 0x00800000) << 2)) >> 24; - - for (i = ARRAY_SIZE(rtl_chip_info) - 1; i >= 0; i--) - if (tmp == rtl_chip_info[i].version) { - tp->chipset = i; - goto match; - } - //if unknown chip, assume array element #0, original RTL-8169 in this case - printk(KERN_DEBUG PFX - "PCI device %s: unknown chip version, assuming RTL-8169\n", - pci_name(pdev)); - printk(KERN_DEBUG PFX "PCI device %s: TxConfig = 0x%lx\n", - pci_name(pdev), (unsigned long) RTL_R32(TxConfig)); - tp->chipset = 0; + // Identify chip attached to board + rtl8169_get_mac_version(tp, ioaddr); + rtl8169_get_phy_version(tp, ioaddr); + + rtl8169_print_mac_version(tp); + rtl8169_print_phy_version(tp); + + for (i = ARRAY_SIZE(rtl_chip_info) - 1; i >= 0; i--) { + if (tp->mac_version == rtl_chip_info[i].mac_version) + break; + } + if (i < 0) { + /* Unknown chip: assume array element #0, original RTL-8169 */ + printk(KERN_DEBUG PFX + "PCI device %s: unknown chip version, assuming %s\n", + pci_name(pdev), rtl_chip_info[0].name); + i++; + } + tp->chipset = i; -match: *ioaddr_out = ioaddr; *dev_out = dev; return 0; @@ -499,7 +796,7 @@ rtl8169_init_one(struct pci_dev *pdev, c dev->stop = rtl8169_close; dev->tx_timeout = rtl8169_tx_timeout; dev->set_multicast_list = rtl8169_set_rx_mode; - dev->watchdog_timeo = TX_TIMEOUT; + dev->watchdog_timeo = RTL8169_TX_TIMEOUT; dev->irq = pdev->irq; dev->base_addr = (unsigned long) ioaddr; // dev->do_ioctl = mii_ioctl; @@ -528,12 +825,29 @@ rtl8169_init_one(struct pci_dev *pdev, c "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, " "IRQ %d\n", dev->name, - board_info[ent->driver_data].name, + rtl_chip_info[ent->driver_data].name, dev->base_addr, dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5], dev->irq); + rtl8169_hw_phy_config(dev); + + dprintk("Set MAC Reg C+CR Offset 0x82h = 0x01h\n"); + RTL_W8(0x82, 0x01); + + if (tp->mac_version < RTL_GIGA_MAC_VER_E) { + dprintk("Set PCI Latency=0x40\n"); + pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0x40); + } + + if (tp->mac_version == RTL_GIGA_MAC_VER_D) { + dprintk("Set MAC Reg C+CR Offset 0x82h = 0x01h\n"); + RTL_W8(0x82, 0x01); + dprintk("Set PHY Reg 0x0bh = 0x00h\n"); + mdio_write(ioaddr, 0x0b, 0x0000); //w 0x0b 15 0 0 + } + // if TBI is not endbled if (!(RTL_R8(PHYstatus) & TBI_Enable)) { int val = mdio_read(ioaddr, PHY_AUTO_NEGO_REG); @@ -546,23 +860,23 @@ rtl8169_init_one(struct pci_dev *pdev, c Cap10_100 = 0, Cap1000 = 0; switch (option) { case _10_Half: - Cap10_100 = PHY_Cap_10_Half; + Cap10_100 = PHY_Cap_10_Half_Or_Less; Cap1000 = PHY_Cap_Null; break; case _10_Full: - Cap10_100 = PHY_Cap_10_Full; + Cap10_100 = PHY_Cap_10_Full_Or_Less; Cap1000 = PHY_Cap_Null; break; case _100_Half: - Cap10_100 = PHY_Cap_100_Half; + Cap10_100 = PHY_Cap_100_Half_Or_Less; Cap1000 = PHY_Cap_Null; break; case _100_Full: - Cap10_100 = PHY_Cap_100_Full; + Cap10_100 = PHY_Cap_100_Full_Or_Less; Cap1000 = PHY_Cap_Null; break; case _1000_Full: - Cap10_100 = PHY_Cap_Null; + Cap10_100 = PHY_Cap_100_Full_Or_Less; Cap1000 = PHY_Cap_1000_Full; break; default: @@ -576,9 +890,7 @@ rtl8169_init_one(struct pci_dev *pdev, c // enable 10/100 Full/Half Mode, leave PHY_AUTO_NEGO_REG bit4:0 unchanged mdio_write(ioaddr, PHY_AUTO_NEGO_REG, - PHY_Cap_10_Half | PHY_Cap_10_Full | - PHY_Cap_100_Half | PHY_Cap_100_Full | (val & - 0x1F)); + PHY_Cap_100_Full_Or_Less | (val & 0x1f)); // enable 1000 Full Mode mdio_write(ioaddr, PHY_1000_CTRL_REG, @@ -647,56 +959,96 @@ rtl8169_remove_one(struct pci_dev *pdev) pci_set_drvdata(pdev, NULL); } +#ifdef CONFIG_PM + +static int rtl8169_suspend(struct pci_dev *pdev, u32 state) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct rtl8169_private *tp = dev->priv; + void *ioaddr = tp->mmio_addr; + unsigned long flags; + + if (!netif_running(dev)) + return 0; + + netif_device_detach(dev); + netif_stop_queue(dev); + spin_lock_irqsave(&tp->lock, flags); + + /* Disable interrupts, stop Rx and Tx */ + RTL_W16(IntrMask, 0); + RTL_W8(ChipCmd, 0); + + /* Update the error counts. */ + tp->stats.rx_missed_errors += RTL_R32(RxMissed); + RTL_W32(RxMissed, 0); + spin_unlock_irqrestore(&tp->lock, flags); + + return 0; +} + +static int rtl8169_resume(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + + if (!netif_running(dev)) + return 0; + + netif_device_attach(dev); + rtl8169_hw_start(dev); + + return 0; +} + +#endif /* CONFIG_PM */ + static int rtl8169_open(struct net_device *dev) { struct rtl8169_private *tp = dev->priv; + struct pci_dev *pdev = tp->pci_dev; int retval; - u8 diff; - u32 TxPhyAddr, RxPhyAddr; retval = request_irq(dev->irq, rtl8169_interrupt, SA_SHIRQ, dev->name, dev); - if (retval) { - return retval; - } + if (retval < 0) + goto out; - tp->TxDescArrays = - kmalloc(NUM_TX_DESC * sizeof (struct TxDesc) + 256, GFP_KERNEL); - // Tx Desscriptor needs 256 bytes alignment; - TxPhyAddr = virt_to_bus(tp->TxDescArrays); - diff = 256 - (TxPhyAddr - ((TxPhyAddr >> 8) << 8)); - TxPhyAddr += diff; - tp->TxDescArray = (struct TxDesc *) (tp->TxDescArrays + diff); - - tp->RxDescArrays = - kmalloc(NUM_RX_DESC * sizeof (struct RxDesc) + 256, GFP_KERNEL); - // Rx Desscriptor needs 256 bytes alignment; - RxPhyAddr = virt_to_bus(tp->RxDescArrays); - diff = 256 - (RxPhyAddr - ((RxPhyAddr >> 8) << 8)); - RxPhyAddr += diff; - tp->RxDescArray = (struct RxDesc *) (tp->RxDescArrays + diff); + retval = -ENOMEM; - if (tp->TxDescArrays == NULL || tp->RxDescArrays == NULL) { - printk(KERN_INFO - "Allocate RxDescArray or TxDescArray failed\n"); - free_irq(dev->irq, dev); - if (tp->TxDescArrays) - kfree(tp->TxDescArrays); - if (tp->RxDescArrays) - kfree(tp->RxDescArrays); - return -ENOMEM; - } - tp->RxBufferRings = kmalloc(RX_BUF_SIZE * NUM_RX_DESC, GFP_KERNEL); - if (tp->RxBufferRings == NULL) { - printk(KERN_INFO "Allocate RxBufferRing failed\n"); - } + /* + * Rx and Tx desscriptors needs 256 bytes alignment. + * pci_alloc_consistent provides more. + */ + tp->TxDescArray = pci_alloc_consistent(pdev, R8169_TX_RING_BYTES, + &tp->TxPhyAddr); + if (!tp->TxDescArray) + goto err_free_irq; + + tp->RxDescArray = pci_alloc_consistent(pdev, R8169_RX_RING_BYTES, + &tp->RxPhyAddr); + if (!tp->RxDescArray) + goto err_free_tx; + + retval = rtl8169_init_ring(dev); + if (retval < 0) + goto err_free_rx; - rtl8169_init_ring(dev); rtl8169_hw_start(dev); - return 0; - + rtl8169_request_timer(dev); +out: + return retval; + +err_free_rx: + pci_free_consistent(pdev, R8169_RX_RING_BYTES, tp->RxDescArray, + tp->RxPhyAddr); +err_free_tx: + pci_free_consistent(pdev, R8169_TX_RING_BYTES, tp->TxDescArray, + tp->TxPhyAddr); +err_free_irq: + free_irq(dev->irq, dev); + goto out; } static void @@ -733,11 +1085,17 @@ rtl8169_hw_start(struct net_device *dev) RTL_W32(TxConfig, (TX_DMA_BURST << TxDMAShift) | (InterFrameGap << TxInterFrameGapShift)); + RTL_W16(CPlusCmd, RTL_R16(CPlusCmd)); + + if (tp->mac_version == RTL_GIGA_MAC_VER_D) { + dprintk(KERN_INFO PFX "Set MAC Reg C+CR Offset 0xE0: bit-3 and bit-14 MUST be 1\n"); + RTL_W16(CPlusCmd, RTL_R16(CPlusCmd) | (1 << 14) | (1 << 3)); + } tp->cur_rx = 0; - RTL_W32(TxDescStartAddr, virt_to_bus(tp->TxDescArray)); - RTL_W32(RxDescStartAddr, virt_to_bus(tp->RxDescArray)); + RTL_W32(TxDescStartAddr, tp->TxPhyAddr); + RTL_W32(RxDescStartAddr, tp->RxPhyAddr); RTL_W8(Cfg9346, Cfg9346_Lock); udelay(10); @@ -755,31 +1113,131 @@ rtl8169_hw_start(struct net_device *dev) } -static void -rtl8169_init_ring(struct net_device *dev) +static inline void rtl8169_make_unusable_by_asic(struct RxDesc *desc) +{ + desc->buf_addr = 0xdeadbeef; + desc->status &= ~cpu_to_le32(OWNbit | RsvdMask); +} + +static void rtl8169_free_rx_skb(struct pci_dev *pdev, struct sk_buff **sk_buff, + struct RxDesc *desc) +{ + pci_unmap_single(pdev, le32_to_cpu(desc->buf_addr), RX_BUF_SIZE, + PCI_DMA_FROMDEVICE); + dev_kfree_skb(*sk_buff); + *sk_buff = NULL; + rtl8169_make_unusable_by_asic(desc); +} + +static inline void rtl8169_return_to_asic(struct RxDesc *desc) +{ + desc->status |= cpu_to_le32(OWNbit + RX_BUF_SIZE); +} + +static inline void rtl8169_give_to_asic(struct RxDesc *desc, dma_addr_t mapping) +{ + desc->buf_addr = cpu_to_le32(mapping); + desc->status |= cpu_to_le32(OWNbit + RX_BUF_SIZE); +} + +static int rtl8169_alloc_rx_skb(struct pci_dev *pdev, struct net_device *dev, + struct sk_buff **sk_buff, struct RxDesc *desc) +{ + struct sk_buff *skb; + dma_addr_t mapping; + int ret = 0; + + skb = dev_alloc_skb(RX_BUF_SIZE); + if (!skb) + goto err_out; + + skb->dev = dev; + skb_reserve(skb, 2); + *sk_buff = skb; + + mapping = pci_map_single(pdev, skb->tail, RX_BUF_SIZE, + PCI_DMA_FROMDEVICE); + + rtl8169_give_to_asic(desc, mapping); + +out: + return ret; + +err_out: + ret = -ENOMEM; + rtl8169_make_unusable_by_asic(desc); + goto out; +} + +static void rtl8169_rx_clear(struct rtl8169_private *tp) { - struct rtl8169_private *tp = dev->priv; int i; - tp->cur_rx = 0; - tp->cur_tx = 0; - tp->dirty_tx = 0; + for (i = 0; i < NUM_RX_DESC; i++) { + if (tp->Rx_skbuff[i]) { + rtl8169_free_rx_skb(tp->pci_dev, tp->Rx_skbuff + i, + tp->RxDescArray + i); + } + } +} + +static u32 rtl8169_rx_fill(struct rtl8169_private *tp, struct net_device *dev, + u32 start, u32 end) +{ + u32 cur; + + for (cur = start; end - cur > 0; cur++) { + int ret, i = cur % NUM_RX_DESC; + + if (tp->Rx_skbuff[i]) + continue; + + ret = rtl8169_alloc_rx_skb(tp->pci_dev, dev, tp->Rx_skbuff + i, + tp->RxDescArray + i); + if (ret < 0) + break; + } + return cur - start; +} + +static inline void rtl8169_mark_as_last_descriptor(struct RxDesc *desc) +{ + desc->status |= cpu_to_le32(EORbit); +} + +static int rtl8169_init_ring(struct net_device *dev) +{ + struct rtl8169_private *tp = dev->priv; + + tp->cur_rx = tp->dirty_rx = 0; + tp->cur_tx = tp->dirty_tx = 0; memset(tp->TxDescArray, 0x0, NUM_TX_DESC * sizeof (struct TxDesc)); memset(tp->RxDescArray, 0x0, NUM_RX_DESC * sizeof (struct RxDesc)); - for (i = 0; i < NUM_TX_DESC; i++) { - tp->Tx_skbuff[i] = NULL; - } - for (i = 0; i < NUM_RX_DESC; i++) { - if (i == (NUM_RX_DESC - 1)) - tp->RxDescArray[i].status = - (OWNbit | EORbit) + RX_BUF_SIZE; - else - tp->RxDescArray[i].status = OWNbit + RX_BUF_SIZE; + memset(tp->Tx_skbuff, 0x0, NUM_TX_DESC * sizeof(struct sk_buff *)); + memset(tp->Rx_skbuff, 0x0, NUM_RX_DESC * sizeof(struct sk_buff *)); - tp->RxBufferRing[i] = &(tp->RxBufferRings[i * RX_BUF_SIZE]); - tp->RxDescArray[i].buf_addr = virt_to_bus(tp->RxBufferRing[i]); - } + if (rtl8169_rx_fill(tp, dev, 0, NUM_RX_DESC) != NUM_RX_DESC) + goto err_out; + + rtl8169_mark_as_last_descriptor(tp->RxDescArray + NUM_RX_DESC - 1); + + return 0; + +err_out: + rtl8169_rx_clear(tp); + return -ENOMEM; +} + +static void rtl8169_unmap_tx_skb(struct pci_dev *pdev, struct sk_buff **sk_buff, + struct TxDesc *desc) +{ + u32 len = sk_buff[0]->len; + + pci_unmap_single(pdev, le32_to_cpu(desc->buf_addr), + len < ETH_ZLEN ? ETH_ZLEN : len, PCI_DMA_TODEVICE); + desc->buf_addr = 0x00; + *sk_buff = NULL; } static void @@ -789,9 +1247,12 @@ rtl8169_tx_clear(struct rtl8169_private tp->cur_tx = 0; for (i = 0; i < NUM_TX_DESC; i++) { - if (tp->Tx_skbuff[i] != NULL) { - dev_kfree_skb(tp->Tx_skbuff[i]); - tp->Tx_skbuff[i] = NULL; + struct sk_buff *skb = tp->Tx_skbuff[i]; + + if (skb) { + rtl8169_unmap_tx_skb(tp->pci_dev, tp->Tx_skbuff + i, + tp->TxDescArray + i); + dev_kfree_skb(skb); tp->stats.tx_dropped++; } } @@ -829,48 +1290,58 @@ rtl8169_start_xmit(struct sk_buff *skb, struct rtl8169_private *tp = dev->priv; void *ioaddr = tp->mmio_addr; int entry = tp->cur_tx % NUM_TX_DESC; + u32 len = skb->len; - if (skb->len < ETH_ZLEN) { + if (unlikely(skb->len < ETH_ZLEN)) { skb = skb_padto(skb, ETH_ZLEN); - if (skb == NULL) - return 0; + if (!skb) + goto err_update_stats; + len = ETH_ZLEN; } spin_lock_irq(&tp->lock); - if ((tp->TxDescArray[entry].status & OWNbit) == 0) { + if (!(le32_to_cpu(tp->TxDescArray[entry].status) & OWNbit)) { + dma_addr_t mapping; + + mapping = pci_map_single(tp->pci_dev, skb->data, len, + PCI_DMA_TODEVICE); + tp->Tx_skbuff[entry] = skb; - tp->TxDescArray[entry].buf_addr = virt_to_bus(skb->data); - if (entry != (NUM_TX_DESC - 1)) - tp->TxDescArray[entry].status = - (OWNbit | FSbit | LSbit) | ((skb->len > ETH_ZLEN) ? - skb->len : ETH_ZLEN); - else - tp->TxDescArray[entry].status = - (OWNbit | EORbit | FSbit | LSbit) | - ((skb->len > ETH_ZLEN) ? skb->len : ETH_ZLEN); + tp->TxDescArray[entry].buf_addr = cpu_to_le32(mapping); + tp->TxDescArray[entry].status = cpu_to_le32(OWNbit | FSbit | + LSbit | len | (EORbit * !((entry + 1) % NUM_TX_DESC))); + RTL_W8(TxPoll, 0x40); //set polling bit dev->trans_start = jiffies; tp->cur_tx++; - } + } else + goto err_drop; - spin_unlock_irq(&tp->lock); if ((tp->cur_tx - NUM_TX_DESC) == tp->dirty_tx) { netif_stop_queue(dev); } +out: + spin_unlock_irq(&tp->lock); return 0; + +err_drop: + dev_kfree_skb(skb); +err_update_stats: + tp->stats.tx_dropped++; + goto out; } static void rtl8169_tx_interrupt(struct net_device *dev, struct rtl8169_private *tp, void *ioaddr) { - unsigned long dirty_tx, tx_left = 0; + unsigned long dirty_tx, tx_left; assert(dev != NULL); assert(tp != NULL); @@ -882,12 +1353,15 @@ rtl8169_tx_interrupt(struct net_device * while (tx_left > 0) { int entry = dirty_tx % NUM_TX_DESC; - if ((tp->TxDescArray[entry].status & OWNbit) == 0) { + if (!(le32_to_cpu(tp->TxDescArray[entry].status) & OWNbit)) { struct sk_buff *skb = tp->Tx_skbuff[entry]; + /* FIXME: is it really accurate for TxErr ? */ tp->stats.tx_bytes += skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN; tp->stats.tx_packets++; + rtl8169_unmap_tx_skb(tp->pci_dev, tp->Tx_skbuff + entry, + tp->TxDescArray + entry); dev_kfree_skb_irq(skb); tp->Tx_skbuff[entry] = NULL; dirty_tx++; @@ -902,70 +1376,102 @@ rtl8169_tx_interrupt(struct net_device * } } +static inline int rtl8169_try_rx_copy(struct sk_buff **sk_buff, int pkt_size, + struct RxDesc *desc, + struct net_device *dev) +{ + int ret = -1; + + if (pkt_size < rx_copybreak) { + struct sk_buff *skb; + + skb = dev_alloc_skb(pkt_size + 2); + if (skb) { + skb->dev = dev; + skb_reserve(skb, 2); + eth_copy_and_sum(skb, sk_buff[0]->tail, pkt_size, 0); + *sk_buff = skb; + rtl8169_return_to_asic(desc); + ret = 0; + } + } + return ret; +} + static void rtl8169_rx_interrupt(struct net_device *dev, struct rtl8169_private *tp, void *ioaddr) { - int cur_rx; - struct sk_buff *skb; - int pkt_size = 0; + unsigned long cur_rx, rx_left; + int delta; assert(dev != NULL); assert(tp != NULL); assert(ioaddr != NULL); cur_rx = tp->cur_rx; + rx_left = NUM_RX_DESC + tp->dirty_rx - cur_rx; - while ((tp->RxDescArray[cur_rx].status & OWNbit) == 0) { + while (rx_left > 0) { + int entry = cur_rx % NUM_RX_DESC; + u32 status = le32_to_cpu(tp->RxDescArray[entry].status); - if (tp->RxDescArray[cur_rx].status & RxRES) { + if (status & OWNbit) + break; + if (status & RxRES) { printk(KERN_INFO "%s: Rx ERROR!!!\n", dev->name); tp->stats.rx_errors++; - if (tp->RxDescArray[cur_rx].status & (RxRWT | RxRUNT)) + if (status & (RxRWT | RxRUNT)) tp->stats.rx_length_errors++; - if (tp->RxDescArray[cur_rx].status & RxCRC) + if (status & RxCRC) tp->stats.rx_crc_errors++; } else { - pkt_size = - (int) (tp->RxDescArray[cur_rx]. - status & 0x00001FFF) - 4; - skb = dev_alloc_skb(pkt_size + 2); - if (skb != NULL) { - skb->dev = dev; - skb_reserve(skb, 2); // 16 byte align the IP fields. // - eth_copy_and_sum(skb, tp->RxBufferRing[cur_rx], - pkt_size, 0); - skb_put(skb, pkt_size); - skb->protocol = eth_type_trans(skb, dev); - netif_rx(skb); - - if (cur_rx == (NUM_RX_DESC - 1)) - tp->RxDescArray[cur_rx].status = - (OWNbit | EORbit) + RX_BUF_SIZE; - else - tp->RxDescArray[cur_rx].status = - OWNbit + RX_BUF_SIZE; - - tp->RxDescArray[cur_rx].buf_addr = - virt_to_bus(tp->RxBufferRing[cur_rx]); - dev->last_rx = jiffies; - tp->stats.rx_bytes += pkt_size; - tp->stats.rx_packets++; - } else { - printk(KERN_WARNING - "%s: Memory squeeze, deferring packet.\n", - dev->name); - /* We should check that some rx space is free. - If not, free one and mark stats->rx_dropped++. */ - tp->stats.rx_dropped++; + struct RxDesc *desc = tp->RxDescArray + entry; + struct sk_buff *skb = tp->Rx_skbuff[entry]; + int pkt_size = (status & 0x00001FFF) - 4; + + pci_dma_sync_single(tp->pci_dev, + le32_to_cpu(desc->buf_addr), + RX_BUF_SIZE, PCI_DMA_FROMDEVICE); + + if (rtl8169_try_rx_copy(&skb, pkt_size, desc, dev)) { + pci_unmap_single(tp->pci_dev, + le32_to_cpu(desc->buf_addr), + RX_BUF_SIZE, + PCI_DMA_FROMDEVICE); + tp->Rx_skbuff[entry] = NULL; } - } - - cur_rx = (cur_rx + 1) % NUM_RX_DESC; + skb_put(skb, pkt_size); + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + + dev->last_rx = jiffies; + tp->stats.rx_bytes += pkt_size; + tp->stats.rx_packets++; + } + + cur_rx++; + rx_left--; } tp->cur_rx = cur_rx; + + delta = rtl8169_rx_fill(tp, dev, tp->dirty_rx, tp->cur_rx); + if (delta > 0) + tp->dirty_rx += delta; + else if (delta < 0) + printk(KERN_INFO "%s: no Rx buffer allocated\n", dev->name); + + /* + * FIXME: until there is periodic timer to try and refill the ring, + * a temporary shortage may definitely kill the Rx process. + * - disable the asic to try and avoid an overflow and kick it again + * after refill ? + * - how do others driver handle this condition (Uh oh...). + */ + if (tp->dirty_rx + NUM_RX_DESC == tp->cur_rx) + printk(KERN_EMERG "%s: Rx buffers exhausted\n", dev->name); } /* The interrupt handler does all of the Rx thread work and cleans up after the Tx thread. */ @@ -994,9 +1500,7 @@ rtl8169_interrupt(int irq, void *dev_ins RTL_W16(IntrStatus, (status & RxFIFOOver) ? (status | RxOverflow) : status); - if ((status & - (SYSErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver - | TxErr | TxOK | RxErr | RxOK)) == 0) + if (!(status & rtl8169_intr_mask)) break; // Rx interrupt @@ -1026,11 +1530,13 @@ static int rtl8169_close(struct net_device *dev) { struct rtl8169_private *tp = dev->priv; + struct pci_dev *pdev = tp->pci_dev; void *ioaddr = tp->mmio_addr; - int i; netif_stop_queue(dev); + rtl8169_delete_timer(dev); + spin_lock_irq(&tp->lock); /* Stop the chip's Tx and Rx DMA processes. */ @@ -1049,16 +1555,15 @@ rtl8169_close(struct net_device *dev) free_irq(dev->irq, dev); rtl8169_tx_clear(tp); - kfree(tp->TxDescArrays); - kfree(tp->RxDescArrays); - tp->TxDescArrays = NULL; - tp->RxDescArrays = NULL; + + rtl8169_rx_clear(tp); + + pci_free_consistent(pdev, R8169_RX_RING_BYTES, tp->RxDescArray, + tp->RxPhyAddr); + pci_free_consistent(pdev, R8169_TX_RING_BYTES, tp->TxDescArray, + tp->TxPhyAddr); tp->TxDescArray = NULL; tp->RxDescArray = NULL; - kfree(tp->RxBufferRings); - for (i = 0; i < NUM_RX_DESC; i++) { - tp->RxBufferRing[i] = NULL; - } return 0; } @@ -1112,11 +1617,26 @@ rtl8169_set_rx_mode(struct net_device *d spin_unlock_irqrestore(&tp->lock, flags); } +/** + * rtl8169_get_stats - Get rtl8169 read/write statistics + * @dev: The Ethernet Device to get statistics for + * + * Get TX/RX statistics for rtl8169 + */ struct net_device_stats * rtl8169_get_stats(struct net_device *dev) { struct rtl8169_private *tp = dev->priv; + void *ioaddr = tp->mmio_addr; + unsigned long flags; + if (netif_running(dev)) { + spin_lock_irqsave(&tp->lock, flags); + tp->stats.rx_missed_errors += RTL_R32(RxMissed); + RTL_W32(RxMissed, 0); + spin_unlock_irqrestore(&tp->lock, flags); + } + return &tp->stats; } @@ -1125,8 +1645,10 @@ static struct pci_driver rtl8169_pci_dri .id_table = rtl8169_pci_tbl, .probe = rtl8169_init_one, .remove = __devexit_p(rtl8169_remove_one), - .suspend = NULL, - .resume = NULL, +#ifdef CONFIG_PM + .suspend = rtl8169_suspend, + .resume = rtl8169_resume, +#endif }; static int __init --- linux-2.6.4-rc2/drivers/net/rrunner.c 2003-09-27 18:57:45.000000000 -0700 +++ 25/drivers/net/rrunner.c 2004-03-07 20:46:58.000000000 -0800 @@ -983,18 +983,26 @@ static void rx_int(struct net_device *de rx_skb = rrpriv->rx_skbuff[index]; - pci_dma_sync_single(rrpriv->pci_dev, desc->addr.addrlo, - pkt_len, PCI_DMA_FROMDEVICE); - if (pkt_len < PKT_COPY_THRESHOLD) { skb = alloc_skb(pkt_len, GFP_ATOMIC); if (skb == NULL){ printk(KERN_WARNING "%s: Unable to allocate skb (%i bytes), deferring packet\n", dev->name, pkt_len); rrpriv->stats.rx_dropped++; goto defer; - }else + } else { + pci_dma_sync_single_for_cpu(rrpriv->pci_dev, + desc->addr.addrlo, + pkt_len, + PCI_DMA_FROMDEVICE); + memcpy(skb_put(skb, pkt_len), rx_skb->data, pkt_len); + + pci_dma_sync_single_for_device(rrpriv->pci_dev, + desc->addr.addrlo, + pkt_len, + PCI_DMA_FROMDEVICE); + } }else{ struct sk_buff *newskb; --- linux-2.6.4-rc2/drivers/net/sis190.c 2004-03-03 23:12:45.000000000 -0800 +++ 25/drivers/net/sis190.c 2004-03-07 20:46:58.000000000 -0800 @@ -1016,14 +1016,20 @@ SiS190_rx_interrupt(struct net_device *d int pkt_size; pkt_size = (int) (desc->PSize & 0x0000FFFF) - 4; - pci_dma_sync_single(tp->pci_dev, desc->buf_addr, - RX_BUF_SIZE, PCI_DMA_FROMDEVICE); skb = dev_alloc_skb(pkt_size + 2); if (skb != NULL) { skb->dev = dev; skb_reserve(skb, 2); // 16 byte align the IP fields. // + pci_dma_sync_single_for_cpu(tp->pci_dev, + desc->buf_addr, + RX_BUF_SIZE, + PCI_DMA_FROMDEVICE); eth_copy_and_sum(skb, tp->RxBufferRing[cur_rx], pkt_size, 0); + pci_dma_sync_single_for_device(tp->pci_dev, + desc->buf_addr, + RX_BUF_SIZE, + PCI_DMA_FROMDEVICE); skb_put(skb, pkt_size); skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); --- linux-2.6.4-rc2/drivers/net/sis900.c 2004-03-03 23:12:45.000000000 -0800 +++ 25/drivers/net/sis900.c 2004-03-07 20:46:59.000000000 -0800 @@ -1650,9 +1650,6 @@ static int sis900_rx(struct net_device * break; } - pci_dma_sync_single(sis_priv->pci_dev, - sis_priv->rx_ring[entry].bufptr, RX_BUF_SIZE, - PCI_DMA_FROMDEVICE); pci_unmap_single(sis_priv->pci_dev, sis_priv->rx_ring[entry].bufptr, RX_BUF_SIZE, PCI_DMA_FROMDEVICE); --- linux-2.6.4-rc2/drivers/net/sk98lin/skge.c 2004-03-03 23:12:45.000000000 -0800 +++ 25/drivers/net/sk98lin/skge.c 2004-03-07 20:46:59.000000000 -0800 @@ -2533,12 +2533,6 @@ rx_start: "Control: %x\nRxStat: %x\n", Control, FrameStat)); - PhysAddr = ((SK_U64) pRxd->VDataHigh) << (SK_U64)32; - PhysAddr |= (SK_U64) pRxd->VDataLow; - pci_dma_sync_single(pAC->PciDev, - (dma_addr_t) PhysAddr, - FrameLength, - PCI_DMA_FROMDEVICE); ReQueueRxBuffer(pAC, pRxPort, pMsg, pRxd->VDataHigh, pRxd->VDataLow); @@ -2559,12 +2553,16 @@ rx_start: PhysAddr = ((SK_U64) pRxd->VDataHigh) << (SK_U64)32; PhysAddr |= (SK_U64) pRxd->VDataLow; - pci_dma_sync_single(pAC->PciDev, - (dma_addr_t) PhysAddr, - FrameLength, - PCI_DMA_FROMDEVICE); + pci_dma_sync_single_for_cpu(pAC->PciDev, + (dma_addr_t) PhysAddr, + FrameLength, + PCI_DMA_FROMDEVICE); eth_copy_and_sum(pNewMsg, pMsg->data, FrameLength, 0); + pci_dma_sync_single_for_device(pAC->PciDev, + (dma_addr_t) PhysAddr, + FrameLength, + PCI_DMA_FROMDEVICE); ReQueueRxBuffer(pAC, pRxPort, pMsg, pRxd->VDataHigh, pRxd->VDataLow); --- linux-2.6.4-rc2/drivers/net/smc-mca.c 2004-02-17 20:48:44.000000000 -0800 +++ 25/drivers/net/smc-mca.c 2004-03-07 20:46:54.000000000 -0800 @@ -324,6 +324,9 @@ int __init ultramca_probe(struct device dev->open = &ultramca_open; dev->stop = &ultramca_close_card; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = ei_poll; +#endif NS8390_init(dev, 0); --- linux-2.6.4-rc2/drivers/net/smc-ultra32.c 2004-02-17 20:48:44.000000000 -0800 +++ 25/drivers/net/smc-ultra32.c 2004-03-07 20:46:54.000000000 -0800 @@ -268,6 +268,9 @@ static int __init ultra32_probe1(struct ei_status.reset_8390 = &ultra32_reset_8390; dev->open = &ultra32_open; dev->stop = &ultra32_close; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = ei_poll; +#endif NS8390_init(dev, 0); return 0; --- linux-2.6.4-rc2/drivers/net/smc-ultra.c 2004-02-17 20:48:44.000000000 -0800 +++ 25/drivers/net/smc-ultra.c 2004-03-07 20:46:54.000000000 -0800 @@ -121,6 +121,14 @@ MODULE_DEVICE_TABLE(isapnp, ultra_device #define ULTRA_IO_EXTENT 32 #define EN0_ERWCNT 0x08 /* Early receive warning count. */ +#ifdef CONFIG_NET_POLL_CONTROLLER +static void ultra_poll(struct net_device *dev) +{ + disable_irq(dev->irq); + ei_interrupt(dev->irq, dev, NULL); + enable_irq(dev->irq); +} +#endif /* Probe for the Ultra. This looks like a 8013 with the station address PROM at I/O ports +8 to +13, with a checksum following. @@ -134,6 +142,9 @@ static int __init do_ultra_probe(struct SET_MODULE_OWNER(dev); +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = &ultra_poll; +#endif if (base_addr > 0x1ff) /* Check a single specified location. */ return ultra_probe1(dev, base_addr); else if (base_addr != 0) /* Don't probe at all. */ @@ -301,6 +312,9 @@ static int __init ultra_probe1(struct ne ei_status.reset_8390 = &ultra_reset_8390; dev->open = &ultra_open; dev->stop = &ultra_close_card; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = ei_poll; +#endif NS8390_init(dev, 0); return 0; --- linux-2.6.4-rc2/drivers/net/starfire.c 2004-02-03 20:42:36.000000000 -0800 +++ 25/drivers/net/starfire.c 2004-03-07 20:46:59.000000000 -0800 @@ -1637,10 +1637,13 @@ static int __netdev_rx(struct net_device && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ - pci_dma_sync_single(np->pci_dev, - np->rx_info[entry].mapping, - pkt_len, PCI_DMA_FROMDEVICE); + pci_dma_sync_single_for_cpu(np->pci_dev, + np->rx_info[entry].mapping, + pkt_len, PCI_DMA_FROMDEVICE); eth_copy_and_sum(skb, np->rx_info[entry].skb->tail, pkt_len, 0); + pci_dma_sync_single_for_device(np->pci_dev, + np->rx_info[entry].mapping, + pkt_len, PCI_DMA_FROMDEVICE); skb_put(skb, pkt_len); } else { pci_unmap_single(np->pci_dev, np->rx_info[entry].mapping, np->rx_buf_sz, PCI_DMA_FROMDEVICE); --- linux-2.6.4-rc2/drivers/net/stnic.c 2004-02-17 20:48:44.000000000 -0800 +++ 25/drivers/net/stnic.c 2004-03-07 20:46:54.000000000 -0800 @@ -124,6 +124,9 @@ static int __init stnic_probe(void) dev->irq = IRQ_STNIC; dev->open = &stnic_open; dev->stop = &stnic_close; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = ei_poll; +#endif /* Snarf the interrupt now. There's no point in waiting since we cannot share and the board will usually be enabled. */ --- linux-2.6.4-rc2/drivers/net/sunbmac.c 2003-10-17 15:58:03.000000000 -0700 +++ 25/drivers/net/sunbmac.c 2004-03-07 20:46:59.000000000 -0800 @@ -849,9 +849,13 @@ static void bigmac_rx(struct bigmac *bp) copy_skb->dev = bp->dev; skb_reserve(copy_skb, 2); skb_put(copy_skb, len); - sbus_dma_sync_single(bp->bigmac_sdev, - this->rx_addr, len, SBUS_DMA_FROMDEVICE); + sbus_dma_sync_single_for_cpu(bp->bigmac_sdev, + this->rx_addr, len, + SBUS_DMA_FROMDEVICE); eth_copy_and_sum(copy_skb, (unsigned char *)skb->data, len, 0); + sbus_dma_sync_single_for_device(bp->bigmac_sdev, + this->rx_addr, len, + SBUS_DMA_FROMDEVICE); /* Reuse original ring buffer. */ this->rx_flags = --- linux-2.6.4-rc2/drivers/net/sundance.c 2004-03-03 23:12:45.000000000 -0800 +++ 25/drivers/net/sundance.c 2004-03-07 20:46:59.000000000 -0800 @@ -1331,9 +1331,6 @@ static void rx_poll(unsigned long data) if (netif_msg_rx_status(np)) printk(KERN_DEBUG " netdev_rx() status was %8.8x.\n", frame_status); - pci_dma_sync_single(np->pci_dev, desc->frag[0].addr, - np->rx_buf_sz, PCI_DMA_FROMDEVICE); - if (frame_status & 0x001f4000) { /* There was a error. */ if (netif_msg_rx_err(np)) @@ -1363,7 +1360,16 @@ static void rx_poll(unsigned long data) && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ + pci_dma_sync_single_for_cpu(np->pci_dev, + desc->frag[0].addr, + np->rx_buf_sz, + PCI_DMA_FROMDEVICE); + eth_copy_and_sum(skb, np->rx_skbuff[entry]->tail, pkt_len, 0); + pci_dma_sync_single_for_device(np->pci_dev, + desc->frag[0].addr, + np->rx_buf_sz, + PCI_DMA_FROMDEVICE); skb_put(skb, pkt_len); } else { pci_unmap_single(np->pci_dev, --- linux-2.6.4-rc2/drivers/net/sungem.c 2004-03-03 23:12:45.000000000 -0800 +++ 25/drivers/net/sungem.c 2004-03-07 20:46:59.000000000 -0800 @@ -763,8 +763,9 @@ static void gem_rx(struct gem *gp) copy_skb->dev = gp->dev; skb_reserve(copy_skb, 2); skb_put(copy_skb, len); - pci_dma_sync_single(gp->pdev, dma_addr, len, PCI_DMA_FROMDEVICE); + pci_dma_sync_single_for_cpu(gp->pdev, dma_addr, len, PCI_DMA_FROMDEVICE); memcpy(copy_skb->data, skb->data, len); + pci_dma_sync_single_for_device(gp->pdev, dma_addr, len, PCI_DMA_FROMDEVICE); /* We'll reuse the original ring buffer. */ skb = copy_skb; --- linux-2.6.4-rc2/drivers/net/sunhme.c 2003-10-17 15:58:03.000000000 -0700 +++ 25/drivers/net/sunhme.c 2004-03-07 20:46:59.000000000 -0800 @@ -273,8 +273,10 @@ static u32 pci_hme_read_desc32(u32 *p) ((__hp)->dma_map((__hp)->happy_dev, (__ptr), (__size), (__dir))) #define hme_dma_unmap(__hp, __addr, __size, __dir) \ ((__hp)->dma_unmap((__hp)->happy_dev, (__addr), (__size), (__dir))) -#define hme_dma_sync(__hp, __addr, __size, __dir) \ - ((__hp)->dma_sync((__hp)->happy_dev, (__addr), (__size), (__dir))) +#define hme_dma_sync_for_cpu(__hp, __addr, __size, __dir) \ + ((__hp)->dma_sync_for_cpu((__hp)->happy_dev, (__addr), (__size), (__dir))) +#define hme_dma_sync_for_device(__hp, __addr, __size, __dir) \ + ((__hp)->dma_sync_for_device((__hp)->happy_dev, (__addr), (__size), (__dir))) #else #ifdef CONFIG_SBUS /* SBUS only compilation */ @@ -297,8 +299,10 @@ do { (__txd)->tx_addr = (__addr); \ sbus_map_single((__hp)->happy_dev, (__ptr), (__size), (__dir)) #define hme_dma_unmap(__hp, __addr, __size, __dir) \ sbus_unmap_single((__hp)->happy_dev, (__addr), (__size), (__dir)) -#define hme_dma_sync(__hp, __addr, __size, __dir) \ - sbus_dma_sync_single((__hp)->happy_dev, (__addr), (__size), (__dir)) +#define hme_dma_sync_for_cpu(__hp, __addr, __size, __dir) \ + sbus_dma_sync_single_for_cpu((__hp)->happy_dev, (__addr), (__size), (__dir)) +#define hme_dma_sync_for_device(__hp, __addr, __size, __dir) \ + sbus_dma_sync_single_for_device((__hp)->happy_dev, (__addr), (__size), (__dir)) #else /* PCI only compilation */ #define hme_write32(__hp, __reg, __val) \ @@ -320,8 +324,10 @@ do { (__txd)->tx_addr = cpu_to_le32(__ad pci_map_single((__hp)->happy_dev, (__ptr), (__size), (__dir)) #define hme_dma_unmap(__hp, __addr, __size, __dir) \ pci_unmap_single((__hp)->happy_dev, (__addr), (__size), (__dir)) -#define hme_dma_sync(__hp, __addr, __size, __dir) \ - pci_dma_sync_single((__hp)->happy_dev, (__addr), (__size), (__dir)) +#define hme_dma_sync_for_cpu(__hp, __addr, __size, __dir) \ + pci_dma_sync_single_for_cpu((__hp)->happy_dev, (__addr), (__size), (__dir)) +#define hme_dma_sync_for_device(__hp, __addr, __size, __dir) \ + pci_dma_sync_single_for_device((__hp)->happy_dev, (__addr), (__size), (__dir)) #endif #endif @@ -2069,8 +2075,9 @@ static void happy_meal_rx(struct happy_m copy_skb->dev = dev; skb_reserve(copy_skb, 2); skb_put(copy_skb, len); - hme_dma_sync(hp, dma_addr, len, DMA_FROMDEVICE); + hme_dma_sync_for_cpu(hp, dma_addr, len, DMA_FROMDEVICE); memcpy(copy_skb->data, skb->data, len); + hme_dma_sync_for_device(hp, dma_addr, len, DMA_FROMDEVICE); /* Reuse original ring buffer. */ hme_write_rxd(hp, this, @@ -2838,7 +2845,10 @@ static int __init happy_meal_sbus_init(s hp->write_rxd = sbus_hme_write_rxd; hp->dma_map = (u32 (*)(void *, void *, long, int))sbus_map_single; hp->dma_unmap = (void (*)(void *, u32, long, int))sbus_unmap_single; - hp->dma_sync = (void (*)(void *, u32, long, int))sbus_dma_sync_single; + hp->dma_sync_for_cpu = (void (*)(void *, u32, long, int)) + sbus_dma_sync_single_for_cpu; + hp->dma_sync_for_device = (void (*)(void *, u32, long, int)) + sbus_dma_sync_single_for_device; hp->read32 = sbus_hme_read32; hp->write32 = sbus_hme_write32; #endif @@ -3182,7 +3192,10 @@ static int __init happy_meal_pci_init(st hp->write_rxd = pci_hme_write_rxd; hp->dma_map = (u32 (*)(void *, void *, long, int))pci_map_single; hp->dma_unmap = (void (*)(void *, u32, long, int))pci_unmap_single; - hp->dma_sync = (void (*)(void *, u32, long, int))pci_dma_sync_single; + hp->dma_sync_for_cpu = (void (*)(void *, u32, long, int)) + pci_dma_sync_single_for_cpu; + hp->dma_sync_for_device = (void (*)(void *, u32, long, int)) + pci_dma_sync_single_for_device; hp->read32 = pci_hme_read32; hp->write32 = pci_hme_write32; #endif --- linux-2.6.4-rc2/drivers/net/sunhme.h 2003-06-14 12:18:08.000000000 -0700 +++ 25/drivers/net/sunhme.h 2004-03-07 20:46:59.000000000 -0800 @@ -406,7 +406,8 @@ struct happy_meal { void (*write_rxd)(struct happy_meal_rxd *, u32, u32); u32 (*dma_map)(void *, void *, long, int); void (*dma_unmap)(void *, u32, long, int); - void (*dma_sync)(void *, u32, long, int); + void (*dma_sync_for_cpu)(void *, u32, long, int); + void (*dma_sync_for_device)(void *, u32, long, int); #endif /* This is either a sbus_dev or a pci_dev. */ --- linux-2.6.4-rc2/drivers/net/tg3.c 2004-03-03 23:12:45.000000000 -0800 +++ 25/drivers/net/tg3.c 2004-03-07 20:46:59.000000000 -0800 @@ -2327,8 +2327,9 @@ static int tg3_rx(struct tg3 *tp, int bu copy_skb->dev = tp->dev; skb_reserve(copy_skb, 2); skb_put(copy_skb, len); - pci_dma_sync_single(tp->pdev, dma_addr, len, PCI_DMA_FROMDEVICE); + pci_dma_sync_single_for_cpu(tp->pdev, dma_addr, len, PCI_DMA_FROMDEVICE); memcpy(copy_skb->data, skb->data, len); + pci_dma_sync_single_for_device(tp->pdev, dma_addr, len, PCI_DMA_FROMDEVICE); /* We'll reuse the original ring buffer. */ skb = copy_skb; @@ -2513,6 +2514,13 @@ static irqreturn_t tg3_interrupt(int irq static int tg3_init_hw(struct tg3 *); static int tg3_halt(struct tg3 *); +#ifdef CONFIG_NET_POLL_CONTROLLER +static void tg3_poll_controller(struct net_device *dev) +{ + tg3_interrupt(dev->irq, dev, NULL); +} +#endif + static void tg3_reset_task(void *_data) { struct tg3 *tp = _data; @@ -7708,6 +7716,9 @@ static int __devinit tg3_init_one(struct dev->watchdog_timeo = TG3_TX_TIMEOUT; dev->change_mtu = tg3_change_mtu; dev->irq = pdev->irq; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = tg3_poll_controller; +#endif err = tg3_get_invariants(tp); if (err) { --- linux-2.6.4-rc2/drivers/net/tlan.c 2003-11-23 19:03:00.000000000 -0800 +++ 25/drivers/net/tlan.c 2004-03-07 20:46:54.000000000 -0800 @@ -814,6 +814,14 @@ static void __init TLan_EisaProbe (void } /* TLan_EisaProbe */ +#ifdef CONFIG_NET_POLL_CONTROLLER +static void TLan_Poll(struct net_device *dev) +{ + disable_irq(dev->irq); + TLan_HandleInterrupt(dev->irq, dev, NULL); + enable_irq(dev->irq); +} +#endif @@ -893,6 +901,9 @@ static int TLan_Init( struct net_device dev->get_stats = &TLan_GetStats; dev->set_multicast_list = &TLan_SetMulticastList; dev->do_ioctl = &TLan_ioctl; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = &TLan_Poll; +#endif dev->tx_timeout = &TLan_tx_timeout; dev->watchdog_timeo = TX_TIMEOUT; --- linux-2.6.4-rc2/drivers/net/tokenring/3c359.c 2004-03-03 23:12:45.000000000 -0800 +++ 25/drivers/net/tokenring/3c359.c 2004-03-07 20:46:59.000000000 -0800 @@ -937,15 +937,17 @@ static void xl_rx(struct net_device *dev while (xl_priv->rx_ring_tail != temp_ring_loc) { copy_len = xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfraglen & 0x7FFF ; frame_length -= copy_len ; - pci_dma_sync_single(xl_priv->pdev,xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfragaddr,xl_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ; + pci_dma_sync_single_for_cpu(xl_priv->pdev,xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfragaddr,xl_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ; memcpy(skb_put(skb,copy_len), xl_priv->rx_ring_skb[xl_priv->rx_ring_tail]->data, copy_len) ; + pci_dma_sync_single_for_device(xl_priv->pdev,xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfragaddr,xl_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ; adv_rx_ring(dev) ; } /* Now we have found the last fragment */ - pci_dma_sync_single(xl_priv->pdev,xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfragaddr,xl_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ; + pci_dma_sync_single_for_cpu(xl_priv->pdev,xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfragaddr,xl_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ; memcpy(skb_put(skb,copy_len), xl_priv->rx_ring_skb[xl_priv->rx_ring_tail]->data, frame_length) ; /* memcpy(skb_put(skb,frame_length), bus_to_virt(xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfragaddr), frame_length) ; */ + pci_dma_sync_single_for_device(xl_priv->pdev,xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfragaddr,xl_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ; adv_rx_ring(dev) ; skb->protocol = tr_type_trans(skb,dev) ; netif_rx(skb) ; --- linux-2.6.4-rc2/drivers/net/tokenring/olympic.c 2004-02-17 20:48:44.000000000 -0800 +++ 25/drivers/net/tokenring/olympic.c 2004-03-07 20:46:59.000000000 -0800 @@ -842,10 +842,13 @@ static void olympic_rx(struct net_device olympic_priv->rx_ring_skb[rx_ring_last_received] = skb ; netif_rx(skb2) ; } else { - pci_dma_sync_single(olympic_priv->pdev, + pci_dma_sync_single_for_cpu(olympic_priv->pdev, le32_to_cpu(olympic_priv->olympic_rx_ring[rx_ring_last_received].buffer), olympic_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ; memcpy(skb_put(skb,length-4),olympic_priv->rx_ring_skb[rx_ring_last_received]->data,length-4) ; + pci_dma_sync_single_for_device(olympic_priv->pdev, + le32_to_cpu(olympic_priv->olympic_rx_ring[rx_ring_last_received].buffer), + olympic_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ; skb->protocol = tr_type_trans(skb,dev) ; netif_rx(skb) ; } @@ -854,12 +857,15 @@ static void olympic_rx(struct net_device olympic_priv->rx_ring_last_received++ ; olympic_priv->rx_ring_last_received &= (OLYMPIC_RX_RING_SIZE -1); rx_ring_last_received = olympic_priv->rx_ring_last_received ; - pci_dma_sync_single(olympic_priv->pdev, + pci_dma_sync_single_for_cpu(olympic_priv->pdev, le32_to_cpu(olympic_priv->olympic_rx_ring[rx_ring_last_received].buffer), olympic_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ; rx_desc = &(olympic_priv->olympic_rx_ring[rx_ring_last_received]); cpy_length = (i == 1 ? frag_len : le32_to_cpu(rx_desc->res_length)); memcpy(skb_put(skb, cpy_length), olympic_priv->rx_ring_skb[rx_ring_last_received]->data, cpy_length) ; + pci_dma_sync_single_for_device(olympic_priv->pdev, + le32_to_cpu(olympic_priv->olympic_rx_ring[rx_ring_last_received].buffer), + olympic_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ; } while (--i) ; skb_trim(skb,skb->len-4) ; skb->protocol = tr_type_trans(skb,dev); --- linux-2.6.4-rc2/drivers/net/tulip/de2104x.c 2004-02-17 20:48:44.000000000 -0800 +++ 25/drivers/net/tulip/de2104x.c 2004-03-07 20:46:59.000000000 -0800 @@ -457,10 +457,12 @@ static void de_rx (struct de_private *de buflen, PCI_DMA_FROMDEVICE); de->rx_skb[rx_tail].skb = copy_skb; } else { - pci_dma_sync_single(de->pdev, mapping, len, PCI_DMA_FROMDEVICE); + pci_dma_sync_single_for_cpu(de->pdev, mapping, len, PCI_DMA_FROMDEVICE); skb_reserve(copy_skb, RX_OFFSET); memcpy(skb_put(copy_skb, len), skb->tail, len); + pci_dma_sync_single_for_device(de->pdev, mapping, len, PCI_DMA_FROMDEVICE); + /* We'll reuse the original ring buffer. */ skb = copy_skb; } --- linux-2.6.4-rc2/drivers/net/tulip/interrupt.c 2004-03-03 23:12:45.000000000 -0800 +++ 25/drivers/net/tulip/interrupt.c 2004-03-07 20:46:59.000000000 -0800 @@ -191,9 +191,9 @@ int tulip_poll(struct net_device *dev, i && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ - pci_dma_sync_single(tp->pdev, - tp->rx_buffers[entry].mapping, - pkt_len, PCI_DMA_FROMDEVICE); + pci_dma_sync_single_for_cpu(tp->pdev, + tp->rx_buffers[entry].mapping, + pkt_len, PCI_DMA_FROMDEVICE); #if ! defined(__alpha__) eth_copy_and_sum(skb, tp->rx_buffers[entry].skb->tail, pkt_len, 0); @@ -203,6 +203,9 @@ int tulip_poll(struct net_device *dev, i tp->rx_buffers[entry].skb->tail, pkt_len); #endif + pci_dma_sync_single_for_device(tp->pdev, + tp->rx_buffers[entry].mapping, + pkt_len, PCI_DMA_FROMDEVICE); } else { /* Pass up the skb already on the Rx ring. */ char *temp = skb_put(skb = tp->rx_buffers[entry].skb, pkt_len); @@ -211,7 +214,7 @@ int tulip_poll(struct net_device *dev, i if (tp->rx_buffers[entry].mapping != le32_to_cpu(tp->rx_ring[entry].buffer1)) { printk(KERN_ERR "%s: Internal fault: The skbuff addresses " - "do not match in tulip_rx: %08x vs. %llx %p / %p.\n", + "do not match in tulip_rx: %08x vs. %08llx %p / %p.\n", dev->name, le32_to_cpu(tp->rx_ring[entry].buffer1), (unsigned long long)tp->rx_buffers[entry].mapping, @@ -412,9 +415,9 @@ static int tulip_rx(struct net_device *d && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ - pci_dma_sync_single(tp->pdev, - tp->rx_buffers[entry].mapping, - pkt_len, PCI_DMA_FROMDEVICE); + pci_dma_sync_single_for_cpu(tp->pdev, + tp->rx_buffers[entry].mapping, + pkt_len, PCI_DMA_FROMDEVICE); #if ! defined(__alpha__) eth_copy_and_sum(skb, tp->rx_buffers[entry].skb->tail, pkt_len, 0); @@ -424,6 +427,9 @@ static int tulip_rx(struct net_device *d tp->rx_buffers[entry].skb->tail, pkt_len); #endif + pci_dma_sync_single_for_device(tp->pdev, + tp->rx_buffers[entry].mapping, + pkt_len, PCI_DMA_FROMDEVICE); } else { /* Pass up the skb already on the Rx ring. */ char *temp = skb_put(skb = tp->rx_buffers[entry].skb, pkt_len); --- linux-2.6.4-rc2/drivers/net/tulip/tulip_core.c 2004-02-17 20:48:44.000000000 -0800 +++ 25/drivers/net/tulip/tulip_core.c 2004-03-07 20:47:19.000000000 -0800 @@ -253,7 +253,7 @@ static void tulip_down(struct net_device static struct net_device_stats *tulip_get_stats(struct net_device *dev); static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); static void set_rx_mode(struct net_device *dev); - +static void poll_tulip(struct net_device *dev); static void tulip_set_power_state (struct tulip_private *tp, @@ -1512,7 +1512,7 @@ static int __devinit tulip_init_one (str } } /* Lite-On boards have the address byte-swapped. */ - if ((dev->dev_addr[0] == 0xA0 || dev->dev_addr[0] == 0xC0) + if ((dev->dev_addr[0] == 0xA0 || dev->dev_addr[0] == 0xC0 || dev->dev_addr[0] == 0x02) && dev->dev_addr[1] == 0x00) for (i = 0; i < 6; i+=2) { char tmp = dev->dev_addr[i]; @@ -1618,6 +1618,9 @@ static int __devinit tulip_init_one (str dev->get_stats = tulip_get_stats; dev->do_ioctl = private_ioctl; dev->set_multicast_list = set_rx_mode; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = &poll_tulip; +#endif if (register_netdev(dev)) goto err_out_free_ring; @@ -1774,6 +1777,22 @@ static void __devexit tulip_remove_one ( /* pci_power_off (pdev, -1); */ } +#ifdef CONFIG_NET_POLL_CONTROLLER +/* + * Polling 'interrupt' - used by things like netconsole to send skbs + * without having to re-enable interrupts. It's not called while + * the interrupt routine is executing. + */ + +static void poll_tulip (struct net_device *dev) +{ + /* disable_irq here is not very nice, but with the lockless + interrupt handler we have no other choice. */ + disable_irq(dev->irq); + tulip_interrupt (dev->irq, dev, NULL); + enable_irq(dev->irq); +} +#endif static struct pci_driver tulip_driver = { .name = DRV_NAME, --- linux-2.6.4-rc2/drivers/net/tulip/winbond-840.c 2004-02-17 20:48:44.000000000 -0800 +++ 25/drivers/net/tulip/winbond-840.c 2004-03-07 20:46:59.000000000 -0800 @@ -1289,9 +1289,9 @@ static int netdev_rx(struct net_device * && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ - pci_dma_sync_single(np->pci_dev,np->rx_addr[entry], - np->rx_skbuff[entry]->len, - PCI_DMA_FROMDEVICE); + pci_dma_sync_single_for_cpu(np->pci_dev,np->rx_addr[entry], + np->rx_skbuff[entry]->len, + PCI_DMA_FROMDEVICE); /* Call copy + cksum if available. */ #if HAS_IP_COPYSUM eth_copy_and_sum(skb, np->rx_skbuff[entry]->tail, pkt_len, 0); @@ -1300,6 +1300,9 @@ static int netdev_rx(struct net_device * memcpy(skb_put(skb, pkt_len), np->rx_skbuff[entry]->tail, pkt_len); #endif + pci_dma_sync_single_for_device(np->pci_dev,np->rx_addr[entry], + np->rx_skbuff[entry]->len, + PCI_DMA_FROMDEVICE); } else { pci_unmap_single(np->pci_dev,np->rx_addr[entry], np->rx_skbuff[entry]->len, --- linux-2.6.4-rc2/drivers/net/typhoon.c 2004-02-17 20:48:44.000000000 -0800 +++ 25/drivers/net/typhoon.c 2004-03-07 20:46:59.000000000 -0800 @@ -1701,9 +1701,13 @@ typhoon_rx(struct typhoon *tp, struct ba (new_skb = dev_alloc_skb(pkt_len + 2)) != NULL) { new_skb->dev = tp->dev; skb_reserve(new_skb, 2); - pci_dma_sync_single(tp->pdev, dma_addr, PKT_BUF_SZ, - PCI_DMA_FROMDEVICE); + pci_dma_sync_single_for_cpu(tp->pdev, dma_addr, + PKT_BUF_SZ, + PCI_DMA_FROMDEVICE); eth_copy_and_sum(new_skb, skb->tail, pkt_len, 0); + pci_dma_sync_single_for_device(tp->pdev, dma_addr, + PKT_BUF_SZ, + PCI_DMA_FROMDEVICE); skb_put(new_skb, pkt_len); typhoon_recycle_rx_skb(tp, idx); } else { --- linux-2.6.4-rc2/drivers/net/via-rhine.c 2003-09-08 13:58:58.000000000 -0700 +++ 25/drivers/net/via-rhine.c 2004-03-07 20:46:59.000000000 -0800 @@ -615,6 +615,15 @@ static void __devinit reload_eeprom(long break; } +#ifdef CONFIG_NET_POLL_CONTROLLER +static void via_rhine_poll(struct net_device *dev) +{ + disable_irq(dev->irq); + via_rhine_interrupt(dev->irq, (void *)dev, NULL); + enable_irq(dev->irq); +} +#endif + static int __devinit via_rhine_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -784,6 +793,9 @@ static int __devinit via_rhine_init_one dev->ethtool_ops = &netdev_ethtool_ops; dev->tx_timeout = via_rhine_tx_timeout; dev->watchdog_timeo = TX_TIMEOUT; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = via_rhine_poll; +#endif if (np->drv_flags & ReqTxAlign) dev->features |= NETIF_F_SG|NETIF_F_HW_CSUM; @@ -1524,7 +1536,7 @@ static void via_rhine_rx(struct net_devi (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ - pci_dma_sync_single(np->pdev, np->rx_skbuff_dma[entry], + pci_dma_sync_single_for_cpu(np->pdev, np->rx_skbuff_dma[entry], np->rx_buf_sz, PCI_DMA_FROMDEVICE); /* *_IP_COPYSUM isn't defined anywhere and eth_copy_and_sum @@ -1537,6 +1549,8 @@ static void via_rhine_rx(struct net_devi memcpy(skb_put(skb, pkt_len), np->rx_skbuff[entry]->tail, pkt_len); #endif + pci_dma_sync_single_for_device(np->pdev, np->rx_skbuff_dma[entry], + np->rx_buf_sz, PCI_DMA_FROMDEVICE); } else { skb = np->rx_skbuff[entry]; if (skb == NULL) { --- linux-2.6.4-rc2/drivers/net/wan/dscc4.c 2004-03-03 23:12:45.000000000 -0800 +++ 25/drivers/net/wan/dscc4.c 2004-03-07 20:46:59.000000000 -0800 @@ -652,7 +652,6 @@ static inline void dscc4_rx_skb(struct d goto refill; } pkt_len = TO_SIZE(rx_fd->state2); - pci_dma_sync_single(pdev, rx_fd->data, pkt_len, PCI_DMA_FROMDEVICE); pci_unmap_single(pdev, rx_fd->data, RX_MAX(HDLC_MAX_MRU), PCI_DMA_FROMDEVICE); if ((skb->data[--pkt_len] & FrameOk) == FrameOk) { stats->rx_packets++; --- linux-2.6.4-rc2/drivers/net/wan/lmc/lmc_ver.h 2003-09-27 18:57:45.000000000 -0700 +++ /dev/null 2002-08-30 16:31:37.000000000 -0700 @@ -1,123 +0,0 @@ -#include - -#ifndef _IF_LMC_LINUXVER_ -#define _IF_LMC_LINUXVER_ - - /* - * Copyright (c) 1997-2000 LAN Media Corporation (LMC) - * All rights reserved. www.lanmedia.com - * - * This code is written by: - * Andrew Stanley-Jones (asj@cban.com) - * Rob Braun (bbraun@vix.com), - * Michael Graff (explorer@vix.com) and - * Matt Thomas (matt@3am-software.com). - * - * This software may be used and distributed according to the terms - * of the GNU General Public License version 2, incorporated herein by reference. - */ - - /* - * This file defines and controls all linux version - * differences. - * - * This is being done to keep 1 central location where all linux - * version differences can be kept and maintained. as this code was - * found version issues where pepered throughout the source code and - * made the souce code not only hard to read but version problems hard - * to track down. If I'm overiding a function/etc with something in - * this file it will be prefixed by "LMC_" which will mean look - * here for the version dependent change that's been done. - * - */ - -#if LINUX_VERSION_CODE < 0x20363 -#define net_device device -#endif - -#if LINUX_VERSION_CODE < 0x20363 -#define LMC_XMITTER_BUSY(x) (x)->tbusy = 1 -#define LMC_XMITTER_FREE(x) (x)->tbusy = 0 -#define LMC_XMITTER_INIT(x) (x)->tbusy = 0 -#else -#define LMC_XMITTER_BUSY(x) netif_stop_queue(x) -#define LMC_XMITTER_FREE(x) netif_wake_queue(x) -#define LMC_XMITTER_INIT(x) netif_start_queue(x) - -#endif - - -#if LINUX_VERSION_CODE < 0x20100 -//typedef unsigned int u_int32_t; - -#define LMC_SETUP_20_DEV {\ - int indx; \ - for (indx = 0; indx < DEV_NUMBUFFS; indx++) \ - skb_queue_head_init (&dev->buffs[indx]); \ - } \ - dev->family = AF_INET; \ - dev->pa_addr = 0; \ - dev->pa_brdaddr = 0; \ - dev->pa_mask = 0xFCFFFFFF; \ - dev->pa_alen = 4; /* IP addr. sizeof(u32) */ - -#else - -#define LMC_SETUP_20_DEV - -#endif - - -#if LINUX_VERSION_CODE < 0x20155 /* basically 2.2 plus */ - -#define LMC_DEV_KFREE_SKB(skb) dev_kfree_skb((skb), FREE_WRITE) - -#else /* Mostly 2.0 kernels */ - -#define LMC_DEV_KFREE_SKB(skb) dev_kfree_skb(skb) - -#endif - -#if LINUX_VERSION_CODE < 0x20200 -#else - -#endif - -#if LINUX_VERSION_CODE < 0x20100 -#define LMC_SKB_FREE(skb, val) (skb->free = val) -#else -#define LMC_SKB_FREE(skb, val) -#endif - - -#if (LINUX_VERSION_CODE >= 0x20200) - -#define LMC_SPIN_FLAGS unsigned long flags; -#define LMC_SPIN_LOCK_INIT(x) spin_lock_init(&(x)->lmc_lock); -#define LMC_SPIN_UNLOCK(x) ((x)->lmc_lock = SPIN_LOCK_UNLOCKED) -#define LMC_SPIN_LOCK_IRQSAVE(x) spin_lock_irqsave (&(x)->lmc_lock, flags); -#define LMC_SPIN_UNLOCK_IRQRESTORE(x) spin_unlock_irqrestore (&(x)->lmc_lock, flags); -#else -#define LMC_SPIN_FLAGS -#define LMC_SPIN_LOCK_INIT(x) -#define LMC_SPIN_UNLOCK(x) -#define LMC_SPIN_LOCK_IRQSAVE(x) -#define LMC_SPIN_UNLOCK_IRQRESTORE(x) -#endif - - -#if LINUX_VERSION_CODE >= 0x20100 -#define LMC_COPY_FROM_USER(x, y, z) if(copy_from_user ((x), (y), (z))) return -EFAULT -#define LMC_COPY_TO_USER(x, y, z) if(copy_to_user ((x), (y), (z))) return -EFAULT -#else -#define LMC_COPY_FROM_USER(x, y, z) if(verify_area(VERIFY_READ, (y), (z))) \ - return -EFAULT; \ - memcpy_fromfs ((x), (y), (z)) - -#define LMC_COPY_TO_USER(x, y, z) if(verify_area(VERIFY_WRITE, (x), (z))) \ - return -EFAULT; \ - memcpy_tofs ((x), (y), (z)) -#endif - - -#endif --- linux-2.6.4-rc2/drivers/net/wan/wanxl.c 2004-03-03 23:12:45.000000000 -0800 +++ 25/drivers/net/wan/wanxl.c 2004-03-07 20:46:46.000000000 -0800 @@ -31,7 +31,7 @@ #include "wanxl.h" -static const char* version = "wanXL serial card driver version: 0.47"; +static const char* version = "wanXL serial card driver version: 0.48"; #define PLX_CTL_RESET 0x40000000 /* adapter reset */ @@ -73,12 +73,11 @@ typedef struct card_t { u8 *plx; /* PLX PCI9060 virtual base address */ struct pci_dev *pdev; /* for pdev->slot_name */ - port_t *ports[4]; int rx_in; struct sk_buff *rx_skbs[RX_QUEUE_LENGTH]; card_status_t *status; /* shared between host and card */ dma_addr_t status_address; - port_t __ports[0]; + port_t ports[0]; /* 1 - 4 port_t structures follow */ }card_t; @@ -89,18 +88,6 @@ static inline port_t* dev_to_port(struct } -static inline struct net_device *port_to_dev(port_t* port) -{ - return port->dev; -} - - -static inline const char* port_name(port_t *port) -{ - return port_to_dev(port)->name; -} - - static inline const char* card_name(struct pci_dev *pdev) { return pdev->slot_name; @@ -165,9 +152,9 @@ static inline void wanxl_cable_intr(port dte = (value & STATUS_CABLE_DCE) ? " DCE" : " DTE"; } printk(KERN_INFO "%s: %s%s module, %s cable%s%s\n", - port_name(port), pm, dte, cable, dsr, dcd); + port->dev->name, pm, dte, cable, dsr, dcd); - hdlc_set_carrier(value & STATUS_CABLE_DCD, port_to_dev(port)); + hdlc_set_carrier(value & STATUS_CABLE_DCD, port->dev); } @@ -175,7 +162,7 @@ static inline void wanxl_cable_intr(port /* Transmit complete interrupt service */ static inline void wanxl_tx_intr(port_t *port) { - struct net_device *dev = port_to_dev(port); + struct net_device *dev = port->dev; struct net_device_stats *stats = hdlc_stats(dev); while (1) { desc_t *desc = &get_status(port)->tx_descs[port->tx_in]; @@ -210,47 +197,49 @@ static inline void wanxl_tx_intr(port_t static inline void wanxl_rx_intr(card_t *card) { desc_t *desc; - while(desc = &card->status->rx_descs[card->rx_in], - desc->stat != PACKET_EMPTY) { - struct sk_buff *skb = card->rx_skbs[card->rx_in]; - port_t *port = card->ports[desc->stat & PACKET_PORT_MASK]; - struct net_device *dev = port_to_dev(port); - struct net_device_stats *stats = hdlc_stats(dev); - + while (desc = &card->status->rx_descs[card->rx_in], + desc->stat != PACKET_EMPTY) { if ((desc->stat & PACKET_PORT_MASK) > card->n_ports) printk(KERN_CRIT "wanXL %s: received packet for" " nonexistent port\n", card_name(card->pdev)); - - else if (!skb) - stats->rx_dropped++; - else { - pci_unmap_single(card->pdev, desc->address, - BUFFER_LENGTH, PCI_DMA_FROMDEVICE); - skb_put(skb, desc->length); + struct sk_buff *skb = card->rx_skbs[card->rx_in]; + port_t *port = &card->ports[desc->stat & + PACKET_PORT_MASK]; + struct net_device *dev = port->dev; + struct net_device_stats *stats = hdlc_stats(dev); + + if (!skb) + stats->rx_dropped++; + else { + pci_unmap_single(card->pdev, desc->address, + BUFFER_LENGTH, + PCI_DMA_FROMDEVICE); + skb_put(skb, desc->length); #ifdef DEBUG_PKT - printk(KERN_DEBUG "%s RX(%i):", port_name(port), - skb->len); - debug_frame(skb); -#endif - stats->rx_packets++; - stats->rx_bytes += skb->len; - skb->mac.raw = skb->data; - skb->dev = dev; - dev->last_rx = jiffies; - skb->protocol = hdlc_type_trans(skb, dev); - netif_rx(skb); - skb = NULL; - } - - if (!skb) { - skb = dev_alloc_skb(BUFFER_LENGTH); - desc->address = skb ? - pci_map_single(card->pdev, skb->data, - BUFFER_LENGTH, - PCI_DMA_FROMDEVICE) : 0; - card->rx_skbs[card->rx_in] = skb; + printk(KERN_DEBUG "%s RX(%i):", dev->name, + skb->len); + debug_frame(skb); +#endif + stats->rx_packets++; + stats->rx_bytes += skb->len; + skb->mac.raw = skb->data; + skb->dev = dev; + dev->last_rx = jiffies; + skb->protocol = hdlc_type_trans(skb, dev); + netif_rx(skb); + skb = NULL; + } + + if (!skb) { + skb = dev_alloc_skb(BUFFER_LENGTH); + desc->address = skb ? + pci_map_single(card->pdev, skb->data, + BUFFER_LENGTH, + PCI_DMA_FROMDEVICE) : 0; + card->rx_skbs[card->rx_in] = skb; + } } desc->stat = PACKET_EMPTY; /* Free descriptor */ card->rx_in = (card->rx_in + 1) % RX_QUEUE_LENGTH; @@ -273,9 +262,9 @@ static irqreturn_t wanxl_intr(int irq, v for (i = 0; i < card->n_ports; i++) { if (stat & (1 << (DOORBELL_FROM_CARD_TX_0 + i))) - wanxl_tx_intr(card->ports[i]); + wanxl_tx_intr(&card->ports[i]); if (stat & (1 << (DOORBELL_FROM_CARD_CABLE_0 + i))) - wanxl_cable_intr(card->ports[i]); + wanxl_cable_intr(&card->ports[i]); } if (stat & (1 << DOORBELL_FROM_CARD_RX)) wanxl_rx_intr(card); @@ -297,8 +286,7 @@ static int wanxl_xmit(struct sk_buff *sk if (desc->stat != PACKET_EMPTY) { /* should never happen - previous xmit should stop queue */ #ifdef DEBUG_PKT - printk(KERN_DEBUG "%s: transmitter buffer full\n", - port_name(port)); + printk(KERN_DEBUG "%s: transmitter buffer full\n", dev->name); #endif netif_stop_queue(dev); spin_unlock_irq(&port->lock); @@ -306,7 +294,7 @@ static int wanxl_xmit(struct sk_buff *sk } #ifdef DEBUG_PKT - printk(KERN_DEBUG "%s TX(%i):", port_name(port), skb->len); + printk(KERN_DEBUG "%s TX(%i):", dev->name, skb->len); debug_frame(skb); #endif @@ -324,8 +312,7 @@ static int wanxl_xmit(struct sk_buff *sk if (get_status(port)->tx_descs[port->tx_out].stat != PACKET_EMPTY) { netif_stop_queue(dev); #ifdef DEBUG_PKT - printk(KERN_DEBUG "%s: transmitter buffer full\n", - port_name(port)); + printk(KERN_DEBUG "%s: transmitter buffer full\n", dev->name); #endif } @@ -417,7 +404,7 @@ static int wanxl_open(struct net_device int i; if (get_status(port)->open) { - printk(KERN_ERR "%s: port already open\n", port_name(port)); + printk(KERN_ERR "%s: port already open\n", dev->name); return -EIO; } if ((i = hdlc_open(dev)) != 0) @@ -435,7 +422,7 @@ static int wanxl_open(struct net_device return 0; while (time_after(timeout, jiffies)); - printk(KERN_ERR "%s: unable to open port\n", port_name(port)); + printk(KERN_ERR "%s: unable to open port\n", dev->name); /* ask the card to close the port, should it be still alive */ writel(1 << (DOORBELL_TO_CARD_CLOSE_0 + port->node), dbr); return -EFAULT; @@ -461,7 +448,7 @@ static int wanxl_close(struct net_device while (time_after(timeout, jiffies)); if (get_status(port)->open) - printk(KERN_ERR "%s: unable to close port\n", port_name(port)); + printk(KERN_ERR "%s: unable to close port\n", dev->name); for (i = 0; i < TX_BUFFERS; i++) { desc_t *desc = &get_status(port)->tx_descs[i]; @@ -528,11 +515,10 @@ static void wanxl_pci_remove_one(struct card_t *card = pci_get_drvdata(pdev); int i; - for (i = 0; i < 4; i++) - if (card->ports[i]) { - struct net_device *dev = port_to_dev(card->ports[i]); - unregister_hdlc_device(dev); - } + for (i = 0; i < card->n_ports; i++) { + unregister_hdlc_device(card->ports[i].dev); + free_netdev(card->ports[i].dev); + } /* unregister and free all host resources */ if (card->irq) @@ -555,13 +541,10 @@ static void wanxl_pci_remove_one(struct pci_free_consistent(pdev, sizeof(card_status_t), card->status, card->status_address); - for (i = 0; i < card->n_ports; i++) - if (card->__ports[i].dev) - free_netdev(card->__ports[i].dev); - + pci_release_regions(pdev); + pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); kfree(card); - pci_release_regions(pdev); } @@ -599,13 +582,15 @@ static int __devinit wanxl_pci_init_one( work on most platforms */ if (pci_set_consistent_dma_mask(pdev, 0x0FFFFFFF) || pci_set_dma_mask(pdev, 0x0FFFFFFF)) { - printk(KERN_ERR "No usable DMA configuration\n"); + printk(KERN_ERR "wanXL: No usable DMA configuration\n"); return -EIO; } i = pci_request_regions(pdev, "wanXL"); - if (i) + if (i) { + pci_disable_device(pdev); return i; + } switch (pdev->device) { case PCI_DEVICE_ID_SBE_WANXL100: ports = 1; break; @@ -619,23 +604,13 @@ static int __devinit wanxl_pci_init_one( printk(KERN_ERR "wanXL %s: unable to allocate memory\n", card_name(pdev)); pci_release_regions(pdev); + pci_disable_device(pdev); return -ENOBUFS; } memset(card, 0, alloc_size); pci_set_drvdata(pdev, card); card->pdev = pdev; - card->n_ports = ports; - - for (i = 0; i < ports; i++) { - card->__ports[i].dev = alloc_hdlcdev(&card->__ports[i]); - if (!card->__ports[i].dev) { - printk(KERN_ERR "wanXL %s: unable to allocate memory\n", - card_name(pdev)); - wanxl_pci_remove_one(pdev); - return -ENOMEM; - } - } card->status = pci_alloc_consistent(pdev, sizeof(card_status_t), &card->status_address); @@ -655,7 +630,7 @@ static int __devinit wanxl_pci_init_one( to indicate the card can do 32-bit DMA addressing */ if (pci_set_consistent_dma_mask(pdev, 0xFFFFFFFF) || pci_set_dma_mask(pdev, 0xFFFFFFFF)) { - printk(KERN_ERR "No usable DMA configuration\n"); + printk(KERN_ERR "wanXL: No usable DMA configuration\n"); wanxl_pci_remove_one(pdev); return -EIO; } @@ -767,17 +742,11 @@ static int __devinit wanxl_pci_init_one( ramsize = stat; #endif - printk(KERN_INFO "wanXL %s: at 0x%X, %u KB of RAM at 0x%X, irq" - " %u\n" KERN_INFO "wanXL %s: port", card_name(pdev), - plx_phy, ramsize / 1024, mem_phy, pdev->irq, card_name(pdev)); - - for (i = 0; i < ports; i++) - printk("%s #%i: %s", i ? "," : "", i, - port_name(card->ports[i])); - printk("\n"); + printk(KERN_INFO "wanXL %s: at 0x%X, %u KB of RAM at 0x%X, irq %u\n", + card_name(pdev), plx_phy, ramsize / 1024, mem_phy, pdev->irq); /* Allocate IRQ */ - if(request_irq(pdev->irq, wanxl_intr, SA_SHIRQ, "wanXL", card)) { + if (request_irq(pdev->irq, wanxl_intr, SA_SHIRQ, "wanXL", card)) { printk(KERN_WARNING "wanXL %s: could not allocate IRQ%i.\n", card_name(pdev), pdev->irq); wanxl_pci_remove_one(pdev); @@ -786,9 +755,18 @@ static int __devinit wanxl_pci_init_one( card->irq = pdev->irq; for (i = 0; i < ports; i++) { - port_t *port = &card->__ports[i]; - struct net_device *dev = port_to_dev(port); - hdlc_device *hdlc = dev_to_hdlc(dev); + hdlc_device *hdlc; + port_t *port = &card->ports[i]; + struct net_device *dev = alloc_hdlcdev(port); + if (!dev) { + printk(KERN_ERR "wanXL %s: unable to allocate" + " memory\n", card_name(pdev)); + wanxl_pci_remove_one(pdev); + return -ENOMEM; + } + + port->dev = dev; + hdlc = dev_to_hdlc(dev); spin_lock_init(&port->lock); SET_MODULE_OWNER(dev); dev->tx_queue_len = 50; @@ -797,7 +775,6 @@ static int __devinit wanxl_pci_init_one( dev->stop = wanxl_close; hdlc->attach = wanxl_attach; hdlc->xmit = wanxl_xmit; - card->ports[i] = port; dev->get_stats = wanxl_get_stats; port->card = card; port->node = i; @@ -805,12 +782,22 @@ static int __devinit wanxl_pci_init_one( if (register_hdlc_device(dev)) { printk(KERN_ERR "wanXL %s: unable to register hdlc" " device\n", card_name(pdev)); - card->ports[i] = NULL; + free_netdev(dev); wanxl_pci_remove_one(pdev); return -ENOBUFS; } + card->n_ports++; } + printk(KERN_INFO "wanXL %s: port", card_name(pdev)); + for (i = 0; i < ports; i++) + printk("%s #%i: %s", i ? "," : "", i, + card->ports[i].dev->name); + printk("\n"); + + for (i = 0; i < ports; i++) + wanxl_cable_intr(&card->ports[i]); /* get carrier status etc.*/ + return 0; } --- linux-2.6.4-rc2/drivers/net/wd.c 2004-02-17 20:48:44.000000000 -0800 +++ 25/drivers/net/wd.c 2004-03-07 20:46:54.000000000 -0800 @@ -333,6 +333,9 @@ static int __init wd_probe1(struct net_d ei_status.get_8390_hdr = &wd_get_8390_hdr; dev->open = &wd_open; dev->stop = &wd_close; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = ei_poll; +#endif NS8390_init(dev, 0); #if 1 --- linux-2.6.4-rc2/drivers/net/yellowfin.c 2004-02-17 20:48:44.000000000 -0800 +++ 25/drivers/net/yellowfin.c 2004-03-07 20:46:59.000000000 -0800 @@ -1124,7 +1124,7 @@ static int yellowfin_rx(struct net_devic if(!desc->result_status) break; - pci_dma_sync_single(yp->pci_dev, desc->addr, + pci_dma_sync_single_for_cpu(yp->pci_dev, desc->addr, yp->rx_buf_sz, PCI_DMA_FROMDEVICE); desc_status = le32_to_cpu(desc->result_status) >> 16; buf_addr = rx_skb->tail; @@ -1208,6 +1208,9 @@ static int yellowfin_rx(struct net_devic memcpy(skb_put(skb, pkt_len), rx_skb->tail, pkt_len); #endif + pci_dma_sync_single_for_device(yp->pci_dev, desc->addr, + yp->rx_buf_sz, + PCI_DMA_FROMDEVICE); } skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); --- linux-2.6.4-rc2/drivers/net/zorro8390.c 2004-03-03 23:12:45.000000000 -0800 +++ 25/drivers/net/zorro8390.c 2004-03-07 20:46:54.000000000 -0800 @@ -227,6 +227,10 @@ static int __devinit zorro8390_init(stru ei_status.reg_offset = zorro8390_offsets; dev->open = &zorro8390_open; dev->stop = &zorro8390_close; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = ei_poll; +#endif + NS8390_init(dev, 0); err = register_netdev(dev); if (err) --- linux-2.6.4-rc2/drivers/parisc/ccio-dma.c 2004-02-17 20:48:44.000000000 -0800 +++ 25/drivers/parisc/ccio-dma.c 2004-03-07 20:46:59.000000000 -0800 @@ -1149,8 +1149,10 @@ static struct hppa_dma_ops ccio_ops = { .unmap_single = ccio_unmap_single, .map_sg = ccio_map_sg, .unmap_sg = ccio_unmap_sg, - .dma_sync_single = NULL, /* NOP for U2/Uturn */ - .dma_sync_sg = NULL, /* ditto */ + .dma_sync_single_for_cpu = NULL, /* NOP for U2/Uturn */ + .dma_sync_single_for_device = NULL, /* NOP for U2/Uturn */ + .dma_sync_sg_for_cpu = NULL, /* ditto */ + .dma_sync_sg_for_device = NULL, /* ditto */ }; #ifdef CONFIG_PROC_FS --- linux-2.6.4-rc2/drivers/parisc/ccio-rm-dma.c 2003-06-14 12:18:03.000000000 -0700 +++ 25/drivers/parisc/ccio-rm-dma.c 2004-03-07 20:46:59.000000000 -0800 @@ -151,8 +151,10 @@ static struct pci_dma_ops ccio_ops = { ccio_unmap_single, ccio_map_sg, ccio_unmap_sg, - NULL, /* dma_sync_single : NOP for U2 */ - NULL, /* dma_sync_sg : ditto */ + NULL, /* dma_sync_single_for_cpu : NOP for U2 */ + NULL, /* dma_sync_single_for_device : NOP for U2 */ + NULL, /* dma_sync_sg_for_cpu : ditto */ + NULL, /* dma_sync_sg_for_device : ditto */ }; --- linux-2.6.4-rc2/drivers/parisc/sba_iommu.c 2004-02-17 20:48:44.000000000 -0800 +++ 25/drivers/parisc/sba_iommu.c 2004-03-07 20:46:59.000000000 -0800 @@ -1410,8 +1410,10 @@ static struct hppa_dma_ops sba_ops = { .unmap_single = sba_unmap_single, .map_sg = sba_map_sg, .unmap_sg = sba_unmap_sg, - .dma_sync_single = NULL, - .dma_sync_sg = NULL, + .dma_sync_single_for_cpu = NULL, + .dma_sync_single_for_device = NULL, + .dma_sync_sg_for_cpu = NULL, + .dma_sync_sg_for_device = NULL, }; --- linux-2.6.4-rc2/drivers/parport/parport_sunbpp.c 2004-03-03 23:12:45.000000000 -0800 +++ 25/drivers/parport/parport_sunbpp.c 2004-03-07 20:46:46.000000000 -0800 @@ -44,9 +44,10 @@ #define dprintk(x) #endif -static void parport_sunbpp_interrupt(int irq, void *dev_id, struct pt_regs *regs) +static irqreturn_t parport_sunbpp_interrupt(int irq, void *dev_id, struct pt_regs *regs) { parport_generic_irq(irq, (struct parport *) dev_id, regs); + return IRQ_HANDLED; } static void parport_sunbpp_disable_irq(struct parport *p) @@ -384,7 +385,7 @@ static int __init parport_sunbpp_init(vo static void __exit parport_sunbpp_exit(void) { - while (!list_empty(port_list)) { + while (!list_empty(&port_list)) { Node *node = list_entry(port_list.next, Node, list); struct parport *p = node->port; struct parport_operations *ops = p->ops; --- linux-2.6.4-rc2/drivers/pci/pci.c 2004-03-03 23:12:45.000000000 -0800 +++ 25/drivers/pci/pci.c 2004-03-07 20:47:11.000000000 -0800 @@ -205,6 +205,8 @@ pci_set_power_state(struct pci_dev *dev, int pm; u16 pmcsr; + might_sleep(); + /* bound the state we're entering */ if (state > 3) state = 3; @@ -686,7 +688,7 @@ pci_set_consistent_dma_mask(struct pci_d if (!pci_dma_supported(dev, mask)) return -EIO; - dev->consistent_dma_mask = mask; + dev->dev.coherent_dma_mask = mask; return 0; } --- linux-2.6.4-rc2/drivers/pci/pci.ids 2004-02-17 20:48:44.000000000 -0800 +++ 25/drivers/pci/pci.ids 2004-03-07 20:46:46.000000000 -0800 @@ -5601,6 +5601,8 @@ 14da National Aerospace Laboratories 14db AFAVLAB Technology Inc 2120 TK9902 + 2180 P028 + 2182 P030 14dc Amplicon Liveline Ltd 0000 PCI230 0001 PCI242 --- linux-2.6.4-rc2/drivers/pci/probe.c 2004-03-03 23:12:45.000000000 -0800 +++ 25/drivers/pci/probe.c 2004-03-07 20:47:00.000000000 -0800 @@ -570,7 +570,6 @@ pci_scan_device(struct pci_bus *bus, int /* Assume 32-bit PCI; let 64-bit PCI cards (which are far rarer) set this higher, assuming the system even supports it. */ dev->dma_mask = 0xffffffff; - dev->consistent_dma_mask = 0xffffffff; if (pci_setup_device(dev) < 0) { kfree(dev); return NULL; @@ -582,6 +581,7 @@ pci_scan_device(struct pci_bus *bus, int pci_name_device(dev); dev->dev.dma_mask = &dev->dma_mask; + dev->dev.coherent_dma_mask = 0xffffffffull; return dev; } --- linux-2.6.4-rc2/drivers/pcmcia/au1000_generic.c 2003-10-08 15:07:09.000000000 -0700 +++ 25/drivers/pcmcia/au1000_generic.c 2004-03-07 20:47:43.000000000 -0800 @@ -26,6 +26,7 @@ * */ #include +#include #include #include #include @@ -54,8 +55,17 @@ #include #include -#ifdef PCMCIA_DEBUG +#ifdef DEBUG static int pc_debug; + +module_param(pc_debug, int, 0644); + +#define debug(lvl,fmt) do { \ + if (pc_debug > (lvl)) \ + printk(KERN_DEBUG fmt); \ +} while (0) +#else +#define debug(lvl,fmt) do { } while (0) #endif MODULE_LICENSE("GPL"); @@ -209,7 +219,7 @@ static int __init au1000_pcmcia_driver_i */ au1000_pcmcia_poll_event(0); - DEBUG(1, "au1000: initialization complete\n"); + debug(1, "au1000: initialization complete\n"); return 0; } /* au1000_pcmcia_driver_init() */ @@ -228,7 +238,7 @@ static void __exit au1000_pcmcia_driver_ if (pcmcia_socket[i].virt_io) iounmap((void *)pcmcia_socket[i].virt_io); } - DEBUG(1, "au1000: shutdown complete\n"); + debug(1, "au1000: shutdown complete\n"); } module_exit(au1000_pcmcia_driver_shutdown); @@ -249,14 +259,14 @@ au1000_pcmcia_events(struct pcmcia_state unsigned int events=0; if(state->detect!=prev_state->detect){ - DEBUG(2, "%s(): card detect value %u\n", + debug(2, "%s(): card detect value %u\n", __FUNCTION__, state->detect); events |= mask&SS_DETECT; } if(state->ready!=prev_state->ready){ - DEBUG(2, "%s(): card ready value %u\n", + debug(2, "%s(): card ready value %u\n", __FUNCTION__, state->ready); events |= mask&((flags&SS_IOCARD)?0:SS_READY); } @@ -429,7 +439,7 @@ au1000_pcmcia_get_status(unsigned int so *status|=state.vs_Xv?SS_XVCARD:0; - DEBUG(2, "\tstatus: %s%s%s%s%s%s%s%s\n", + debug(2, "\tstatus: %s%s%s%s%s%s%s%s\n", (*status&SS_DETECT)?"DETECT ":"", (*status&SS_READY)?"READY ":"", (*status&SS_BATDEAD)?"BATDEAD ":"", @@ -457,7 +467,7 @@ au1000_pcmcia_set_socket(unsigned int so { struct pcmcia_configure configure; - DEBUG(2, "\tmask: %s%s%s%s%s%s\n\tflags: %s%s%s%s%s%s\n" + debug(2, "\tmask: %s%s%s%s%s%s\n\tflags: %s%s%s%s%s%s\n" "\tVcc %d Vpp %d irq %d\n", (state->csc_mask==0)?"":"", (state->csc_mask&SS_DETECT)?"DETECT ":"", @@ -494,7 +504,7 @@ au1000_pcmcia_set_socket(unsigned int so static int au1000_pcmcia_get_io_map(unsigned int sock, struct pccard_io_map *map) { - DEBUG(1, "au1000_pcmcia_get_io_map: sock %d\n", sock); + debug(1, "au1000_pcmcia_get_io_map: sock %d\n", sock); if(map->map>=MAX_IO_WIN){ printk(KERN_ERR "%s(): map (%d) out of range\n", __FUNCTION__, map->map); @@ -531,7 +541,7 @@ au1000_pcmcia_set_io_map(unsigned int so map->start=pcmcia_socket[sock].virt_io; map->stop=map->start+(map->stop-start); pcmcia_socket[sock].io_map[map->map]=*map; - DEBUG(3, "set_io_map %d start %x stop %x\n", + debug(3, "set_io_map %d start %x stop %x\n", map->map, map->start, map->stop); return 0; @@ -595,7 +605,7 @@ au1000_pcmcia_set_mem_map(unsigned int s map->sys_stop=map->sys_start+(map->sys_stop-start); pcmcia_socket[sock].mem_map[map->map]=*map; spin_unlock_irqrestore(&pcmcia_lock, flags); - DEBUG(3, "set_mem_map %d start %x stop %x card_start %x\n", + debug(3, "set_mem_map %d start %x stop %x card_start %x\n", map->map, map->sys_start, map->sys_stop, map->card_start); return 0; --- linux-2.6.4-rc2/drivers/pcmcia/au1000_pb1x00.c 2003-10-08 15:07:09.000000000 -0700 +++ 25/drivers/pcmcia/au1000_pb1x00.c 2004-03-07 20:47:43.000000000 -0800 @@ -48,6 +48,8 @@ #include #include +#define debug(fmt, arg...) do { } while (0) + #ifdef CONFIG_MIPS_PB1000 #include #define PCMCIA_IRQ AU1000_GPIO_15 @@ -213,7 +215,7 @@ pb1x00_pcmcia_configure_socket(const str } pcr &= ~PCR_SLOT_0_RST; - DEBUG(KERN_INFO "Vcc %dV Vpp %dV, pcr %x\n", + debug("Vcc %dV Vpp %dV, pcr %x\n", configure->vcc, configure->vpp, pcr); switch(configure->vcc){ case 0: /* Vcc 0 */ @@ -324,7 +326,7 @@ pb1x00_pcmcia_configure_socket(const str pcr = au_readw(PB1100_MEM_PCMCIA) & ~0xf; - DEBUG(KERN_INFO "Vcc %dV Vpp %dV, pcr %x, reset %d\n", + debug("Vcc %dV Vpp %dV, pcr %x, reset %d\n", configure->vcc, configure->vpp, pcr, configure->reset); --- linux-2.6.4-rc2/drivers/pcmcia/bulkmem.c 2004-02-17 20:48:44.000000000 -0800 +++ 25/drivers/pcmcia/bulkmem.c 2004-03-07 20:47:42.000000000 -0800 @@ -48,6 +48,8 @@ #include #include "cs_internal.h" +static void retry_erase_list(erase_busy_t *list, u_int cause); + /*====================================================================== This function handles submitting an MTD request, and retrying @@ -108,18 +110,18 @@ static int do_mtd_request(memory_handle_ ======================================================================*/ -static void insert_queue(erase_busy_t *head, erase_busy_t *entry) +static void insert_queue(struct pcmcia_socket *s, erase_busy_t *head, erase_busy_t *entry) { - DEBUG(2, "cs: adding 0x%p to queue 0x%p\n", entry, head); + cs_dbg(s, 2, "adding 0x%p to queue 0x%p\n", entry, head); entry->next = head; entry->prev = head->prev; head->prev->next = entry; head->prev = entry; } -static void remove_queue(erase_busy_t *entry) +static void remove_queue(struct pcmcia_socket *s, erase_busy_t *entry) { - DEBUG(2, "cs: unqueueing 0x%p\n", entry); + cs_dbg(s, 2, "unqueueing 0x%p\n", entry); entry->next->prev = entry->prev; entry->prev->next = entry->next; } @@ -132,34 +134,35 @@ static void retry_erase(erase_busy_t *bu struct pcmcia_socket *s; int ret; - DEBUG(2, "cs: trying erase request 0x%p...\n", busy); + mtd = erase->Handle->mtd; + s = SOCKET(mtd); + + cs_dbg(s, 2, "trying erase request 0x%p...\n", busy); if (busy->next) - remove_queue(busy); + remove_queue(s, busy); req.Function = MTD_REQ_ERASE | cause; req.TransferLength = erase->Size; req.DestCardOffset = erase->Offset + erase->Handle->info.CardOffset; req.MediaID = erase->Handle->MediaID; - mtd = erase->Handle->mtd; - s = SOCKET(mtd); mtd->event_callback_args.mtdrequest = &req; ret = EVENT(mtd, CS_EVENT_MTD_REQUEST, CS_EVENT_PRI_LOW); if (ret == CS_BUSY) { - DEBUG(2, " Status = %d, requeueing.\n", req.Status); + cs_dbg(s, 2, " Status = %d, requeueing.\n", req.Status); switch (req.Status) { case MTD_WAITREQ: case MTD_WAITPOWER: - insert_queue(&mtd->erase_busy, busy); + insert_queue(s, &mtd->erase_busy, busy); break; case MTD_WAITTIMER: case MTD_WAITRDY: if (req.Status == MTD_WAITRDY) - insert_queue(&s->erase_busy, busy); + insert_queue(s, &s->erase_busy, busy); mod_timer(&busy->timeout, jiffies + req.Timeout*HZ/1000); break; } } else { /* update erase queue status */ - DEBUG(2, " Ret = %d\n", ret); + cs_dbg(s, 2, " Ret = %d\n", ret); switch (ret) { case CS_SUCCESS: erase->State = ERASE_PASSED; break; @@ -183,11 +186,11 @@ static void retry_erase(erase_busy_t *bu } } /* retry_erase */ -void retry_erase_list(erase_busy_t *list, u_int cause) +static void retry_erase_list(erase_busy_t *list, u_int cause) { erase_busy_t tmp = *list; - DEBUG(2, "cs: rescanning erase queue list 0x%p\n", list); + cs_dbg(SOCKET(list->client), 2, "rescanning erase queue list 0x%p\n", list); if (list->next == list) return; /* First, truncate the original list */ @@ -204,8 +207,9 @@ void retry_erase_list(erase_busy_t *list static void handle_erase_timeout(u_long arg) { - DEBUG(0, "cs: erase timeout for entry 0x%lx\n", arg); - retry_erase((erase_busy_t *)arg, MTD_REQ_TIMEOUT); + erase_busy_t *busy = (erase_busy_t *)arg; + cs_dbg(SOCKET(busy->client), 0, "erase timeout for entry 0x%lx\n", arg); + retry_erase(busy, MTD_REQ_TIMEOUT); } static void setup_erase_request(client_handle_t handle, eraseq_entry_t *erase) @@ -333,8 +337,8 @@ static void setup_regions(client_handle_ cistpl_device_geo_t geo; memory_handle_t r; - DEBUG(1, "cs: setup_regions(0x%p, %d, 0x%p)\n", - handle, attr, list); + cs_dbg(SOCKET(handle), 1, "setup_regions(0x%p, %d, 0x%p)\n", + handle, attr, list); code = (attr) ? CISTPL_DEVICE_A : CISTPL_DEVICE; if (read_tuple(handle, code, &device) != CS_SUCCESS) @@ -342,17 +346,13 @@ static void setup_regions(client_handle_ code = (attr) ? CISTPL_JEDEC_A : CISTPL_JEDEC_C; has_jedec = (read_tuple(handle, code, &jedec) == CS_SUCCESS); if (has_jedec && (device.ndev != jedec.nid)) { -#ifdef PCMCIA_DEBUG - printk(KERN_DEBUG "cs: Device info does not match JEDEC info.\n"); -#endif + cs_dbg(SOCKET(handle), 0, "Device info does not match JEDEC info.\n"); has_jedec = 0; } code = (attr) ? CISTPL_DEVICE_GEO_A : CISTPL_DEVICE_GEO; has_geo = (read_tuple(handle, code, &geo) == CS_SUCCESS); if (has_geo && (device.ndev != geo.ngeo)) { -#ifdef PCMCIA_DEBUG - printk(KERN_DEBUG "cs: Device info does not match geometry tuple.\n"); -#endif + cs_dbg(SOCKET(handle), 0, "Device info does not match geometry tuple.\n"); has_geo = 0; } @@ -458,7 +458,7 @@ int pcmcia_register_mtd(client_handle_t list = s->a_region; else list = s->c_region; - DEBUG(1, "cs: register_mtd(0x%p, '%s', 0x%x)\n", + cs_dbg(s, 1, "register_mtd(0x%p, '%s', 0x%x)\n", handle, handle->dev_info, reg->Offset); while (list) { if (list->info.CardOffset == reg->Offset) break; @@ -548,8 +548,8 @@ int pcmcia_open_memory(client_handle_t * } if (region && region->mtd) { *mh = region; - DEBUG(1, "cs: open_memory(0x%p, 0x%x) = 0x%p\n", - handle, open->Offset, region); + cs_dbg(s, 1, "open_memory(0x%p, 0x%x) = 0x%p\n", + handle, open->Offset, region); return CS_SUCCESS; } else return CS_BAD_OFFSET; @@ -565,7 +565,7 @@ int pcmcia_open_memory(client_handle_t * int pcmcia_close_memory(memory_handle_t handle) { - DEBUG(1, "cs: close_memory(0x%p)\n", handle); + cs_dbg(SOCKET(handle->mtd), 1, "cs: close_memory(0x%p)\n", handle); if (CHECK_REGION(handle)) return CS_BAD_HANDLE; return CS_SUCCESS; --- linux-2.6.4-rc2/drivers/pcmcia/cardbus.c 2003-09-27 18:57:45.000000000 -0700 +++ 25/drivers/pcmcia/cardbus.c 2004-03-07 20:47:42.000000000 -0800 @@ -58,10 +58,6 @@ #include #include "cs_internal.h" -#ifdef PCMCIA_DEBUG -static int pc_debug = PCMCIA_DEBUG; -#endif - /*====================================================================*/ #define FIND_FIRST_BIT(n) ((n) - ((n) & ((n)-1))) @@ -119,7 +115,7 @@ static u_int xlate_rom_addr(u_char * b, static void cb_release_cis_mem(struct pcmcia_socket * s) { if (s->cb_cis_virt) { - DEBUG(1, "cs: cb_release_cis_mem()\n"); + cs_dbg(s, 1, "cb_release_cis_mem()\n"); iounmap(s->cb_cis_virt); s->cb_cis_virt = NULL; s->cb_cis_res = 0; @@ -160,7 +156,7 @@ int read_cb_mem(struct pcmcia_socket * s struct pci_dev *dev; struct resource *res; - DEBUG(3, "cs: read_cb_mem(%d, %#x, %u)\n", space, addr, len); + cs_dbg(s, 3, "read_cb_mem(%d, %#x, %u)\n", space, addr, len); dev = pci_find_slot(s->cb_dev->subordinate->number, 0); if (!dev) --- linux-2.6.4-rc2/drivers/pcmcia/cistpl.c 2004-01-09 00:04:32.000000000 -0800 +++ 25/drivers/pcmcia/cistpl.c 2004-03-07 20:47:42.000000000 -0800 @@ -143,7 +143,7 @@ int read_cis_mem(struct pcmcia_socket *s { u_char *sys, *end, *buf = ptr; - DEBUG(3, "cs: read_cis_mem(%d, %#x, %u)\n", attr, addr, len); + cs_dbg(s, 3, "read_cis_mem(%d, %#x, %u)\n", attr, addr, len); if (attr & IS_INDIRECT) { /* Indirect accesses use a bunch of special registers at fixed @@ -195,7 +195,7 @@ int read_cis_mem(struct pcmcia_socket *s addr = 0; } } - DEBUG(3, "cs: %#2.2x %#2.2x %#2.2x %#2.2x ...\n", + cs_dbg(s, 3, " %#2.2x %#2.2x %#2.2x %#2.2x ...\n", *(u_char *)(ptr+0), *(u_char *)(ptr+1), *(u_char *)(ptr+2), *(u_char *)(ptr+3)); return 0; @@ -206,7 +206,7 @@ void write_cis_mem(struct pcmcia_socket { u_char *sys, *end, *buf = ptr; - DEBUG(3, "cs: write_cis_mem(%d, %#x, %u)\n", attr, addr, len); + cs_dbg(s, 3, "write_cis_mem(%d, %#x, %u)\n", attr, addr, len); if (attr & IS_INDIRECT) { /* Indirect accesses use a bunch of special registers at fixed @@ -578,8 +578,7 @@ int pcmcia_get_next_tuple(client_handle_ ofs += link[1] + 2; } if (i == MAX_TUPLES) { - DEBUG(1, "cs: overrun in pcmcia_get_next_tuple for socket %d\n", - handle->Socket); + cs_dbg(s, 1, "cs: overrun in pcmcia_get_next_tuple\n"); return CS_NO_MORE_ITEMS; } --- linux-2.6.4-rc2/drivers/pcmcia/cs.c 2004-02-03 20:42:36.000000000 -0800 +++ 25/drivers/pcmcia/cs.c 2004-03-07 20:47:42.000000000 -0800 @@ -32,6 +32,7 @@ ======================================================================*/ #include +#include #include #include #include @@ -110,12 +111,17 @@ INT_MODULE_PARM(cis_speed, 300); /* ns /* Access speed for IO windows */ INT_MODULE_PARM(io_speed, 0); /* ns */ -#ifdef PCMCIA_DEBUG -INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG); -static const char *version = -"cs.c 1.279 2001/10/13 00:08:28 (David Hinds)"; +#ifdef DEBUG +static int pc_debug; + +module_param(pc_debug, int, 0644); + +int cs_debug_level(int level) +{ + return pc_debug > level; +} #endif - + /*====================================================================*/ socket_state_t dead_socket = { @@ -306,7 +312,7 @@ int pcmcia_register_socket(struct pcmcia if (!socket || !socket->ops || !socket->dev.dev) return -EINVAL; - DEBUG(0, "cs: pcmcia_register_socket(0x%p)\n", socket->ops); + cs_dbg(socket, 0, "pcmcia_register_socket(0x%p)\n", socket->ops); /* try to obtain a socket number [yes, it gets ugly if we * register more than 2^sizeof(unsigned int) pcmcia @@ -377,7 +383,7 @@ void pcmcia_unregister_socket(struct pcm if (!socket) return; - DEBUG(0, "cs: pcmcia_unregister_socket(0x%p)\n", socket->ops); + cs_dbg(socket, 0, "pcmcia_unregister_socket(0x%p)\n", socket->ops); if (socket->thread) { init_completion(&socket->thread_done); @@ -443,7 +449,7 @@ static void shutdown_socket(struct pcmci { client_t **c; - DEBUG(1, "cs: shutdown_socket(%p)\n", s); + cs_dbg(s, 1, "shutdown_socket\n"); /* Blank out the socket state */ s->state &= SOCKET_PRESENT|SOCKET_INUSE; @@ -499,8 +505,8 @@ static int send_event(struct pcmcia_sock { client_t *client = s->clients; int ret; - DEBUG(1, "cs: send_event(sock %d, event %d, pri %d)\n", - s->sock, event, priority); + cs_dbg(s, 1, "send_event(event %d, pri %d)\n", + event, priority); ret = 0; if (s->state & SOCKET_CARDBUS) return 0; @@ -516,26 +522,14 @@ static int send_event(struct pcmcia_sock return ret; } /* send_event */ -static void pcmcia_error(struct pcmcia_socket *skt, const char *fmt, ...) -{ - static char buf[128]; - va_list ap; - int len; - - va_start(ap, fmt); - len = vsnprintf(buf, sizeof(buf), fmt, ap); - va_end(ap); - buf[len] = '\0'; - - printk(KERN_ERR "PCMCIA: socket %p: %s", skt, buf); -} - #define cs_to_timeout(cs) (((cs) * HZ + 99) / 100) static void socket_remove_drivers(struct pcmcia_socket *skt) { client_t *client; + cs_dbg(skt, 4, "remove_drivers\n"); + send_event(skt, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH); for (client = skt->clients; client; client = client->next) @@ -545,6 +539,8 @@ static void socket_remove_drivers(struct static void socket_shutdown(struct pcmcia_socket *skt) { + cs_dbg(skt, 4, "shutdown\n"); + socket_remove_drivers(skt); set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(cs_to_timeout(shutdown_delay)); @@ -556,6 +552,8 @@ static int socket_reset(struct pcmcia_so { int status, i; + cs_dbg(skt, 4, "reset\n"); + skt->socket.flags |= SS_OUTPUT_ENA | SS_RESET; skt->ops->set_socket(skt, &skt->socket); udelay((long)reset_time); @@ -578,7 +576,7 @@ static int socket_reset(struct pcmcia_so schedule_timeout(cs_to_timeout(unreset_check)); } - pcmcia_error(skt, "time out after reset.\n"); + cs_err(skt, "time out after reset.\n"); return CS_GENERAL_FAILURE; } @@ -586,6 +584,8 @@ static int socket_setup(struct pcmcia_so { int status, i; + cs_dbg(skt, 4, "setup\n"); + skt->ops->get_status(skt, &status); if (!(status & SS_DETECT)) return CS_NO_CARD; @@ -606,14 +606,14 @@ static int socket_setup(struct pcmcia_so } if (status & SS_PENDING) { - pcmcia_error(skt, "voltage interrogation timed out.\n"); + cs_err(skt, "voltage interrogation timed out.\n"); return CS_GENERAL_FAILURE; } if (status & SS_CARDBUS) { skt->state |= SOCKET_CARDBUS; #ifndef CONFIG_CARDBUS - pcmcia_error(skt, "cardbus cards are not supported.\n"); + cs_err(skt, "cardbus cards are not supported.\n"); return CS_BAD_TYPE; #endif } @@ -626,7 +626,7 @@ static int socket_setup(struct pcmcia_so else if (!(status & SS_XVCARD)) skt->socket.Vcc = skt->socket.Vpp = 50; else { - pcmcia_error(skt, "unsupported voltage key.\n"); + cs_err(skt, "unsupported voltage key.\n"); return CS_BAD_TYPE; } skt->socket.flags = 0; @@ -640,7 +640,7 @@ static int socket_setup(struct pcmcia_so skt->ops->get_status(skt, &status); if (!(status & SS_POWERON)) { - pcmcia_error(skt, "unable to apply power.\n"); + cs_err(skt, "unable to apply power.\n"); return CS_BAD_TYPE; } @@ -655,6 +655,8 @@ static int socket_insert(struct pcmcia_s { int ret; + cs_dbg(skt, 4, "insert\n"); + if (!cs_socket_get(skt)) return CS_NO_CARD; @@ -667,6 +669,8 @@ static int socket_insert(struct pcmcia_s skt->state |= SOCKET_CARDBUS_CONFIG; } #endif + cs_dbg(skt, 4, "insert done\n"); + send_event(skt, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW); } else { socket_shutdown(skt); @@ -832,6 +836,7 @@ static int pccardd(void *__skt) */ void pcmcia_parse_events(struct pcmcia_socket *s, u_int events) { + cs_dbg(s, 4, "parse_events: events %08x\n", events); if (s->thread) { spin_lock(&s->thread_lock); s->thread_events |= events; @@ -857,15 +862,15 @@ static int alloc_io_space(struct pcmcia_ align = (*base) ? (lines ? 1<features & SS_CAP_STATIC_MAP) && s->io_offset) { @@ -1010,8 +1015,8 @@ int pcmcia_bind_device(bind_req_t *req) init_waitqueue_head(&client->mtd_req); client->next = s->clients; s->clients = client; - DEBUG(1, "cs: bind_device(): client 0x%p, sock %p, dev %s\n", - client, client->Socket, client->dev_info); + cs_dbg(s, 1, "bind_device(): client 0x%p, dev %s\n", + client, client->dev_info); return CS_SUCCESS; } /* bind_device */ @@ -1046,8 +1051,8 @@ int pcmcia_bind_mtd(mtd_bind_t *req) return CS_BAD_OFFSET; strlcpy(region->dev_info, (char *)req->dev_info, DEV_NAME_LEN); - DEBUG(1, "cs: bind_mtd(): attr 0x%x, offset 0x%x, dev %s\n", - req->Attributes, req->CardOffset, (char *)req->dev_info); + cs_dbg(s, 1, "bind_mtd(): attr 0x%x, offset 0x%x, dev %s\n", + req->Attributes, req->CardOffset, (char *)req->dev_info); return CS_SUCCESS; } /* bind_mtd */ @@ -1061,9 +1066,12 @@ int pcmcia_deregister_client(client_hand u_long flags; int i; - DEBUG(1, "cs: deregister_client(%p)\n", handle); if (CHECK_HANDLE(handle)) return CS_BAD_HANDLE; + + s = SOCKET(handle); + cs_dbg(s, 1, "deregister_client(%p)\n", handle); + if (handle->state & (CLIENT_IRQ_REQ|CLIENT_IO_REQ|CLIENT_CONFIG_LOCKED)) return CS_IN_USE; @@ -1072,7 +1080,6 @@ int pcmcia_deregister_client(client_hand return CS_IN_USE; /* Disconnect all MTD links */ - s = SOCKET(handle); if (handle->mtd_count) { for (region = s->a_region; region; region = region->info.next) if (region->mtd == handle) region->mtd = NULL; @@ -1543,8 +1550,8 @@ int pcmcia_register_client(client_handle memset(s->config, 0, sizeof(config_t) * s->functions); } - DEBUG(1, "cs: register_client(): client 0x%p, sock %p, dev %s\n", - client, client->Socket, client->dev_info); + cs_dbg(s, 1, "register_client(): client 0x%p, dev %s\n", + client, client->dev_info); if (client->EventMask & CS_EVENT_REGISTRATION_COMPLETE) EVENT(client, CS_EVENT_REGISTRATION_COMPLETE, CS_EVENT_PRI_LOW); @@ -2077,8 +2084,8 @@ int pcmcia_reset_card(client_handle_t ha if (CHECK_HANDLE(handle)) return CS_BAD_HANDLE; - DEBUG(1, "cs: resetting socket %p\n", handle->Socket); skt = SOCKET(handle); + cs_dbg(skt, 1, "resetting socket\n"); down(&skt->skt_sem); do { @@ -2126,8 +2133,8 @@ int pcmcia_suspend_card(client_handle_t if (CHECK_HANDLE(handle)) return CS_BAD_HANDLE; - DEBUG(1, "cs: suspending socket %p\n", handle->Socket); skt = SOCKET(handle); + cs_dbg(skt, 1, "suspending socket\n"); down(&skt->skt_sem); do { @@ -2153,8 +2160,8 @@ int pcmcia_resume_card(client_handle_t h if (CHECK_HANDLE(handle)) return CS_BAD_HANDLE; - DEBUG(1, "cs: waking up socket %p\n", handle->Socket); skt = SOCKET(handle); + cs_dbg(skt, 1, "waking up socket\n"); down(&skt->skt_sem); do { @@ -2186,8 +2193,8 @@ int pcmcia_eject_card(client_handle_t ha if (CHECK_HANDLE(handle)) return CS_BAD_HANDLE; - DEBUG(1, "cs: user eject request on socket %p\n", handle->Socket); skt = SOCKET(handle); + cs_dbg(skt, 1, "user eject request\n"); down(&skt->skt_sem); do { @@ -2215,8 +2222,8 @@ int pcmcia_insert_card(client_handle_t h if (CHECK_HANDLE(handle)) return CS_BAD_HANDLE; - DEBUG(1, "cs: user insert request on socket %p\n", handle->Socket); skt = SOCKET(handle); + cs_dbg(skt, 1, "user insert request\n"); down(&skt->skt_sem); do { @@ -2360,7 +2367,6 @@ static int __init init_pcmcia_cs(void) { printk(KERN_INFO "%s\n", release); printk(KERN_INFO " %s\n", options); - DEBUG(0, "%s\n", version); class_register(&pcmcia_socket_class); return 0; --- linux-2.6.4-rc2/drivers/pcmcia/cs_internal.h 2003-09-27 18:57:45.000000000 -0700 +++ 25/drivers/pcmcia/cs_internal.h 2004-03-07 20:47:42.000000000 -0800 @@ -167,7 +167,6 @@ int replace_cis(client_handle_t handle, int read_tuple(client_handle_t handle, cisdata_t code, void *parse); /* In bulkmem.c */ -void retry_erase_list(struct erase_busy_t *list, u_int cause); int get_first_region(client_handle_t handle, region_info_t *rgn); int get_next_region(client_handle_t handle, region_info_t *rgn); int register_mtd(client_handle_t handle, mtd_reg_t *reg); @@ -195,10 +194,19 @@ extern struct rw_semaphore pcmcia_socket extern struct list_head pcmcia_socket_list; #ifdef PCMCIA_DEBUG -extern int pc_debug; -#define DEBUG(n, args...) do { if (pc_debug>(n)) printk(KERN_DEBUG args); } while (0) +extern int cs_debug_level(int); + +#define cs_dbg(skt, lvl, fmt, arg...) do { \ + if (cs_debug_level(lvl)) \ + printk(KERN_DEBUG "cs: %s: " fmt, \ + (skt)->dev.class_id, ## arg); \ +} while (0) + #else -#define DEBUG(n, args...) do { } while (0) +#define cs_dbg(skt, lvl, fmt, arg...) do { } while (0) #endif +#define cs_err(skt, fmt, arg...) \ + printk(KERN_ERR "cs: %s: " fmt, (skt)->dev.class_id , ## arg) + #endif /* _LINUX_CS_INTERNAL_H */ --- linux-2.6.4-rc2/drivers/pcmcia/ds.c 2004-02-03 20:42:36.000000000 -0800 +++ 25/drivers/pcmcia/ds.c 2004-03-07 20:47:42.000000000 -0800 @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -69,13 +70,17 @@ MODULE_AUTHOR("David Hinds (n)) printk(KERN_DEBUG args) +module_param(pc_debug, int, 0644); + +#define debug(lvl, fmt, arg...) do { \ + if (pc_debug > (lvl)) \ + printk(KERN_DEBUG "ds: " fmt, ## arg); \ +} while (0) #else -#define DEBUG(n, args...) +#define debug(lvl, fmt, arg...) do { } while (0) #endif /*====================================================================*/ @@ -277,7 +282,7 @@ static int ds_event(event_t event, int p { struct pcmcia_bus_socket *s; - DEBUG(1, "ds: ds_event(0x%06x, %d, 0x%p)\n", + debug(1, "ds_event(0x%06x, %d, 0x%p)\n", event, priority, args->client_handle); s = args->client_data; @@ -353,7 +358,7 @@ static int bind_request(struct pcmcia_bu if (!s) return -EINVAL; - DEBUG(2, "bind_request(%d, '%s')\n", s->parent->sock, + debug(2, "bind_request(%d, '%s')\n", s->parent->sock, (char *)bind_info->dev_info); driver = get_pcmcia_driver(&bind_info->dev_info); if (!driver) @@ -484,7 +489,7 @@ static int unbind_request(struct pcmcia_ { socket_bind_t **b, *c; - DEBUG(2, "unbind_request(%d, '%s')\n", s->parent->sock, + debug(2, "unbind_request(%d, '%s')\n", s->parent->sock, (char *)bind_info->dev_info); for (b = &s->bind; *b; b = &(*b)->next) if ((strcmp((char *)(*b)->driver->drv.name, @@ -518,7 +523,7 @@ static int ds_open(struct inode *inode, struct pcmcia_bus_socket *s; user_info_t *user; - DEBUG(0, "ds_open(socket %d)\n", i); + debug(0, "ds_open(socket %d)\n", i); s = pcmcia_get_bus_socket(i); if (!s) @@ -552,7 +557,7 @@ static int ds_release(struct inode *inod struct pcmcia_bus_socket *s; user_info_t *user, **link; - DEBUG(0, "ds_release(socket %d)\n", iminor(inode)); + debug(0, "ds_release(socket %d)\n", iminor(inode)); user = file->private_data; if (CHECK_USER(user)) @@ -588,7 +593,7 @@ static ssize_t ds_read(struct file *file user_info_t *user; int ret; - DEBUG(2, "ds_read(socket %d)\n", iminor(inode)); + debug(2, "ds_read(socket %d)\n", iminor(file->f_dentry->d_inode)); if (count < 4) return -EINVAL; @@ -616,7 +621,7 @@ static ssize_t ds_write(struct file *fil struct pcmcia_bus_socket *s; user_info_t *user; - DEBUG(2, "ds_write(socket %d)\n", iminor(inode)); + debug(2, "ds_write(socket %d)\n", iminor(file->f_dentry->d_inode)); if (count != 4) return -EINVAL; @@ -650,7 +655,7 @@ static u_int ds_poll(struct file *file, struct pcmcia_bus_socket *s; user_info_t *user; - DEBUG(2, "ds_poll(socket %d)\n", iminor(inode)); + debug(2, "ds_poll(socket %d)\n", iminor(file->f_dentry->d_inode)); user = file->private_data; if (CHECK_USER(user)) @@ -677,7 +682,7 @@ static int ds_ioctl(struct inode * inode ds_ioctl_arg_t buf; user_info_t *user; - DEBUG(2, "ds_ioctl(socket %d, %#x, %#lx)\n", iminor(inode), cmd, arg); + debug(2, "ds_ioctl(socket %d, %#x, %#lx)\n", iminor(inode), cmd, arg); user = file->private_data; if (CHECK_USER(user)) @@ -697,14 +702,14 @@ static int ds_ioctl(struct inode * inode if (cmd & IOC_IN) { err = verify_area(VERIFY_READ, (char *)arg, size); if (err) { - DEBUG(3, "ds_ioctl(): verify_read = %d\n", err); + debug(3, "ds_ioctl(): verify_read = %d\n", err); return err; } } if (cmd & IOC_OUT) { err = verify_area(VERIFY_WRITE, (char *)arg, size); if (err) { - DEBUG(3, "ds_ioctl(): verify_write = %d\n", err); + debug(3, "ds_ioctl(): verify_write = %d\n", err); return err; } } @@ -806,7 +811,7 @@ static int ds_ioctl(struct inode * inode } if ((err == 0) && (ret != CS_SUCCESS)) { - DEBUG(2, "ds_ioctl: ret = %d\n", ret); + debug(2, "ds_ioctl: ret = %d\n", ret); switch (ret) { case CS_BAD_SOCKET: case CS_NO_CARD: err = -ENODEV; break; --- linux-2.6.4-rc2/drivers/pcmcia/i82365.c 2004-01-09 00:04:32.000000000 -0800 +++ 25/drivers/pcmcia/i82365.c 2004-03-07 20:47:43.000000000 -0800 @@ -32,6 +32,7 @@ ======================================================================*/ #include +#include #include #include #include @@ -66,14 +67,20 @@ #include "ricoh.h" #include "o2micro.h" -#ifdef PCMCIA_DEBUG -static int pc_debug = PCMCIA_DEBUG; -MODULE_PARM(pc_debug, "i"); -#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) +#ifdef DEBUG static const char *version = "i82365.c 1.265 1999/11/10 18:36:21 (David Hinds)"; + +static int pc_debug; + +module_param(pc_debug, int, 0644); + +#define debug(lvl, fmt, arg...) do { \ + if (pc_debug > (lvl)) \ + printk(KERN_DEBUG "i82365: " fmt, ## arg); \ +} while (0) #else -#define DEBUG(n, args...) do { } while (0) +#define debug(lvl, fmt, arg...) do { } while (0) #endif static irqreturn_t i365_count_irq(int, void *, struct pt_regs *); @@ -496,13 +503,13 @@ static irqreturn_t i365_count_irq(int ir { i365_get(irq_sock, I365_CSC); irq_hits++; - DEBUG(2, "-> hit on irq %d\n", irq); + debug(2, "-> hit on irq %d\n", irq); return IRQ_HANDLED; } static u_int __init test_irq(u_short sock, int irq) { - DEBUG(2, " testing ISA irq %d\n", irq); + debug(2, " testing ISA irq %d\n", irq); if (request_irq(irq, i365_count_irq, 0, "scan", i365_count_irq) != 0) return 1; irq_hits = 0; irq_sock = sock; @@ -510,7 +517,7 @@ static u_int __init test_irq(u_short soc schedule_timeout(HZ/100); if (irq_hits) { free_irq(irq, i365_count_irq); - DEBUG(2, " spurious hit!\n"); + debug(2, " spurious hit!\n"); return 1; } @@ -523,7 +530,7 @@ static u_int __init test_irq(u_short soc /* mask all interrupts */ i365_set(sock, I365_CSCINT, 0); - DEBUG(2, " hits = %d\n", irq_hits); + debug(2, " hits = %d\n", irq_hits); return (irq_hits != 1); } @@ -850,7 +857,7 @@ static irqreturn_t pcic_interrupt(int ir u_long flags = 0; int handled = 0; - DEBUG(4, "i82365: pcic_interrupt(%d)\n", irq); + debug(4, "pcic_interrupt(%d)\n", irq); for (j = 0; j < 20; j++) { active = 0; @@ -874,7 +881,7 @@ static irqreturn_t pcic_interrupt(int ir events |= (csc & I365_CSC_READY) ? SS_READY : 0; } ISA_UNLOCK(i, flags); - DEBUG(2, "i82365: socket %d event 0x%02x\n", i, events); + debug(2, "socket %d event 0x%02x\n", i, events); if (events) pcmcia_parse_events(&socket[i].socket, events); @@ -886,7 +893,7 @@ static irqreturn_t pcic_interrupt(int ir if (j == 20) printk(KERN_NOTICE "i82365: infinite loop in interrupt handler\n"); - DEBUG(4, "i82365: interrupt done\n"); + debug(4, "interrupt done\n"); return IRQ_RETVAL(handled); } /* pcic_interrupt */ @@ -928,7 +935,7 @@ static int i365_get_status(u_short sock, } } - DEBUG(1, "i82365: GetStatus(%d) = %#4.4x\n", sock, *value); + debug(1, "GetStatus(%d) = %#4.4x\n", sock, *value); return 0; } /* i365_get_status */ @@ -998,7 +1005,7 @@ static int i365_get_socket(u_short sock, state->csc_mask |= (reg & I365_CSC_READY) ? SS_READY : 0; } - DEBUG(1, "i82365: GetSocket(%d) = flags %#3.3x, Vcc %d, Vpp %d, " + debug(1, "GetSocket(%d) = flags %#3.3x, Vcc %d, Vpp %d, " "io_irq %d, csc_mask %#2.2x\n", sock, state->flags, state->Vcc, state->Vpp, state->io_irq, state->csc_mask); return 0; @@ -1011,7 +1018,7 @@ static int i365_set_socket(u_short sock, struct i82365_socket *t = &socket[sock]; u_char reg; - DEBUG(1, "i82365: SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, " + debug(1, "SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, " "io_irq %d, csc_mask %#2.2x)\n", sock, state->flags, state->Vcc, state->Vpp, state->io_irq, state->csc_mask); @@ -1120,7 +1127,7 @@ static int i365_set_io_map(u_short sock, { u_char map, ioctl; - DEBUG(1, "i82365: SetIOMap(%d, %d, %#2.2x, %d ns, " + debug(1, "SetIOMap(%d, %d, %#2.2x, %d ns, " "%#4.4x-%#4.4x)\n", sock, io->map, io->flags, io->speed, io->start, io->stop); map = io->map; @@ -1150,7 +1157,7 @@ static int i365_set_mem_map(u_short sock u_short base, i; u_char map; - DEBUG(1, "i82365: SetMemMap(%d, %d, %#2.2x, %d ns, %#5.5lx-%#5.5" + debug(1, "SetMemMap(%d, %d, %#2.2x, %d ns, %#5.5lx-%#5.5" "lx, %#5.5x)\n", sock, mem->map, mem->flags, mem->speed, mem->sys_start, mem->sys_stop, mem->card_start); --- linux-2.6.4-rc2/drivers/pcmcia/sa11xx_core.c 2004-03-03 23:12:45.000000000 -0800 +++ 25/drivers/pcmcia/sa11xx_core.c 2004-03-07 20:47:43.000000000 -0800 @@ -35,6 +35,7 @@ */ #include +#include #include #include #include @@ -54,8 +55,19 @@ #include "sa11xx_core.h" #include "sa1100.h" -#ifdef PCMCIA_DEBUG +#ifdef DEBUG static int pc_debug; + +module_param(pc_debug, int, 0644); + +#define debug(skt, lvl, fmt, arg...) do { \ + if (pc_debug > (lvl)) \ + printk(KERN_DEBUG "skt%u: %s: " fmt, \ + (skt)->nr, __func__, ## arg); \ +} while (0) + +#else +#define debug(skt, lvl, fmt, arg...) do { } while (0) #endif #define to_sa1100_socket(x) container_of(x, struct sa1100_pcmcia_socket, socket) @@ -133,8 +145,8 @@ sa1100_pcmcia_set_mecr(struct sa1100_pcm local_irq_restore(flags); - DEBUG(4, "%s(): sock %u FAST %X BSM %X BSA %X BSIO %X\n", - __FUNCTION__, skt->nr, MECR_FAST_GET(mecr, skt->nr), + debug(skt, 4, "FAST %X BSM %X BSA %X BSIO %X\n", + MECR_FAST_GET(mecr, skt->nr), MECR_BSM_GET(mecr, skt->nr), MECR_BSA_GET(mecr, skt->nr), MECR_BSIO_GET(mecr, skt->nr)); @@ -221,7 +233,7 @@ static int sa1100_pcmcia_sock_init(struc { struct sa1100_pcmcia_socket *skt = to_sa1100_socket(sock); - DEBUG(2, "%s(): initializing socket %u\n", __FUNCTION__, skt->nr); + debug(skt, 2, "initializing socket\n"); skt->ops->socket_init(skt); return 0; @@ -242,7 +254,7 @@ static int sa1100_pcmcia_suspend(struct struct sa1100_pcmcia_socket *skt = to_sa1100_socket(sock); int ret; - DEBUG(2, "%s(): suspending socket %u\n", __FUNCTION__, skt->nr); + debug(skt, 2, "suspending socket\n"); ret = sa1100_pcmcia_config_skt(skt, &dead_socket); if (ret == 0) @@ -260,7 +272,7 @@ static void sa1100_check_status(struct s { unsigned int events; - DEBUG(4, "%s(): entering PCMCIA monitoring thread\n", __FUNCTION__); + debug(skt, 4, "entering PCMCIA monitoring thread\n"); do { unsigned int status; @@ -273,7 +285,7 @@ static void sa1100_check_status(struct s skt->status = status; spin_unlock_irqrestore(&status_lock, flags); - DEBUG(2, "events: %s%s%s%s%s%s\n", + debug(skt, 4, "events: %s%s%s%s%s%s\n", events == 0 ? "" : "", events & SS_DETECT ? "DETECT " : "", events & SS_READY ? "READY " : "", @@ -293,7 +305,7 @@ static void sa1100_check_status(struct s static void sa1100_pcmcia_poll_event(unsigned long dummy) { struct sa1100_pcmcia_socket *skt = (struct sa1100_pcmcia_socket *)dummy; - DEBUG(4, "%s(): polling for events\n", __FUNCTION__); + debug(skt, 4, "polling for events\n"); mod_timer(&skt->poll_timer, jiffies + SA1100_PCMCIA_POLL_PERIOD); @@ -314,7 +326,7 @@ static irqreturn_t sa1100_pcmcia_interru { struct sa1100_pcmcia_socket *skt = dev; - DEBUG(3, "%s(): servicing IRQ %d\n", __FUNCTION__, irq); + debug(skt, 3, "servicing IRQ %d\n", irq); sa1100_check_status(skt); @@ -363,7 +375,7 @@ sa1100_pcmcia_get_socket(struct pcmcia_s { struct sa1100_pcmcia_socket *skt = to_sa1100_socket(sock); - DEBUG(2, "%s() for sock %u\n", __FUNCTION__, skt->nr); + debug(skt, 2, "\n"); *state = skt->cs_state; @@ -385,22 +397,19 @@ sa1100_pcmcia_set_socket(struct pcmcia_s { struct sa1100_pcmcia_socket *skt = to_sa1100_socket(sock); - DEBUG(2, "%s() for sock %u\n", __FUNCTION__, skt->nr); - - DEBUG(3, "\tmask: %s%s%s%s%s%s\n\tflags: %s%s%s%s%s%s\n", - (state->csc_mask==0)?"":"", + debug(skt, 2, "mask: %s%s%s%s%s%sflags: %s%s%s%s%s%sVcc %d Vpp %d irq %d\n", + (state->csc_mask==0)?" ":"", (state->csc_mask&SS_DETECT)?"DETECT ":"", (state->csc_mask&SS_READY)?"READY ":"", (state->csc_mask&SS_BATDEAD)?"BATDEAD ":"", (state->csc_mask&SS_BATWARN)?"BATWARN ":"", (state->csc_mask&SS_STSCHG)?"STSCHG ":"", - (state->flags==0)?"":"", + (state->flags==0)?" ":"", (state->flags&SS_PWR_AUTO)?"PWR_AUTO ":"", (state->flags&SS_IOCARD)?"IOCARD ":"", (state->flags&SS_RESET)?"RESET ":"", (state->flags&SS_SPKR_ENA)?"SPKR_ENA ":"", - (state->flags&SS_OUTPUT_ENA)?"OUTPUT_ENA ":""); - DEBUG(3, "\tVcc %d Vpp %d irq %d\n", + (state->flags&SS_OUTPUT_ENA)?"OUTPUT_ENA ":"", state->Vcc, state->Vpp, state->io_irq); return sa1100_pcmcia_config_skt(skt, state); @@ -422,11 +431,9 @@ sa1100_pcmcia_set_io_map(struct pcmcia_s struct sa1100_pcmcia_socket *skt = to_sa1100_socket(sock); unsigned short speed = map->speed; - DEBUG(2, "%s() for sock %u\n", __FUNCTION__, skt->nr); - - DEBUG(3, "\tmap %u speed %u\n\tstart 0x%08x stop 0x%08x\n", + debug(skt, 2, "map %u speed %u start 0x%08x stop 0x%08x\n", map->map, map->speed, map->start, map->stop); - DEBUG(3, "\tflags: %s%s%s%s%s%s%s%s\n", + debug(skt, 2, "flags: %s%s%s%s%s%s%s%s\n", (map->flags==0)?"":"", (map->flags&MAP_ACTIVE)?"ACTIVE ":"", (map->flags&MAP_16BIT)?"16BIT ":"", @@ -479,11 +486,9 @@ sa1100_pcmcia_set_mem_map(struct pcmcia_ struct resource *res; unsigned short speed = map->speed; - DEBUG(2, "%s() for sock %u\n", __FUNCTION__, skt->nr); - - DEBUG(3, "\tmap %u speed %u card_start %08x\n", + debug(skt, 2, "map %u speed %u card_start %08x\n", map->map, map->speed, map->card_start); - DEBUG(3, "\tflags: %s%s%s%s%s%s%s%s\n", + debug(skt, 2, "flags: %s%s%s%s%s%s%s%s\n", (map->flags==0)?"":"", (map->flags&MAP_ACTIVE)?"ACTIVE ":"", (map->flags&MAP_16BIT)?"16BIT ":"", @@ -920,23 +925,13 @@ sa1100_pcmcia_notifier(struct notifier_b switch (val) { case CPUFREQ_PRECHANGE: - if (freqs->new > freqs->old) { - DEBUG(2, "%s(): new frequency %u.%uMHz > %u.%uMHz, " - "pre-updating\n", __FUNCTION__, - freqs->new / 1000, (freqs->new / 100) % 10, - freqs->old / 1000, (freqs->old / 100) % 10); + if (freqs->new > freqs->old) sa1100_pcmcia_update_mecr(freqs->new); - } break; case CPUFREQ_POSTCHANGE: - if (freqs->new < freqs->old) { - DEBUG(2, "%s(): new frequency %u.%uMHz < %u.%uMHz, " - "post-updating\n", __FUNCTION__, - freqs->new / 1000, (freqs->new / 100) % 10, - freqs->old / 1000, (freqs->old / 100) % 10); + if (freqs->new < freqs->old) sa1100_pcmcia_update_mecr(freqs->new); - } break; } --- linux-2.6.4-rc2/drivers/pcmcia/tcic.c 2003-10-17 15:58:03.000000000 -0700 +++ 25/drivers/pcmcia/tcic.c 2004-03-07 20:47:43.000000000 -0800 @@ -32,6 +32,7 @@ ======================================================================*/ #include +#include #include #include #include @@ -55,14 +56,20 @@ #include #include "tcic.h" -#ifdef PCMCIA_DEBUG -static int pc_debug = PCMCIA_DEBUG; +#ifdef DEBUG +static int pc_debug; + +module_param(pc_debug, int, 0644); MODULE_PARM(pc_debug, "i"); static const char *version = "tcic.c 1.111 2000/02/15 04:13:12 (David Hinds)"; -#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) + +#define debug(lvl, fmt, arg...) do { \ + if (pc_debug > (lvl)) \ + printk(KERN_DEBUG "tcic: " fmt, ## arg); \ +} while (0) #else -#define DEBUG(n, args...) +#define debug(lvl, fmt, arg...) do { } while (0) #endif MODULE_AUTHOR("David Hinds "); @@ -133,7 +140,7 @@ static struct tcic_socket socket_table[2 to map to irq 11, but is coded as 0 or 1 in the irq registers. */ #define TCIC_IRQ(x) ((x) ? (((x) == 11) ? 1 : (x)) : 15) -#ifdef PCMCIA_DEBUG_X +#ifdef DEBUG_X static u_char tcic_getb(u_char reg) { u_char val = inb(tcic_base+reg); @@ -168,7 +175,7 @@ static void tcic_setw(u_char reg, u_shor static void tcic_setl(u_char reg, u_int data) { -#ifdef PCMCIA_DEBUG_X +#ifdef DEBUG_X printk(KERN_DEBUG "tcic_setl(%#x, %#lx)\n", tcic_base+reg, data); #endif outw(data & 0xffff, tcic_base+reg); @@ -573,7 +580,7 @@ static irqreturn_t tcic_interrupt(int ir } else active = 1; - DEBUG(2, "tcic: tcic_interrupt()\n"); + debug(2, "tcic_interrupt()\n"); for (i = 0; i < sockets; i++) { psock = socket_table[i].psock; @@ -610,13 +617,13 @@ static irqreturn_t tcic_interrupt(int ir } active = 0; - DEBUG(2, "tcic: interrupt done\n"); + debug(2, "interrupt done\n"); return IRQ_HANDLED; } /* tcic_interrupt */ static void tcic_timer(u_long data) { - DEBUG(2, "tcic: tcic_timer()\n"); + debug(2, "tcic_timer()\n"); tcic_timer_pending = 0; tcic_interrupt(0, NULL, NULL); } /* tcic_timer */ @@ -643,7 +650,7 @@ static int tcic_get_status(struct pcmcia reg = tcic_getb(TCIC_PWR); if (reg & (TCIC_PWR_VCC(psock)|TCIC_PWR_VPP(psock))) *value |= SS_POWERON; - DEBUG(1, "tcic: GetStatus(%d) = %#2.2x\n", psock, *value); + debug(1, "GetStatus(%d) = %#2.2x\n", psock, *value); return 0; } /* tcic_get_status */ @@ -694,7 +701,7 @@ static int tcic_get_socket(struct pcmcia state->csc_mask |= (scf2 & TCIC_SCF2_MRDY) ? 0 : SS_READY; } - DEBUG(1, "tcic: GetSocket(%d) = flags %#3.3x, Vcc %d, Vpp %d, " + debug(1, "GetSocket(%d) = flags %#3.3x, Vcc %d, Vpp %d, " "io_irq %d, csc_mask %#2.2x\n", psock, state->flags, state->Vcc, state->Vpp, state->io_irq, state->csc_mask); return 0; @@ -708,7 +715,7 @@ static int tcic_set_socket(struct pcmcia u_char reg; u_short scf1, scf2; - DEBUG(1, "tcic: SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, " + debug(1, "SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, " "io_irq %d, csc_mask %#2.2x)\n", psock, state->flags, state->Vcc, state->Vpp, state->io_irq, state->csc_mask); tcic_setw(TCIC_ADDR+2, (psock << TCIC_SS_SHFT) | TCIC_ADR2_INDREG); @@ -783,7 +790,7 @@ static int tcic_set_io_map(struct pcmcia u_int addr; u_short base, len, ioctl; - DEBUG(1, "tcic: SetIOMap(%d, %d, %#2.2x, %d ns, " + debug(1, "SetIOMap(%d, %d, %#2.2x, %d ns, " "%#4.4x-%#4.4x)\n", psock, io->map, io->flags, io->speed, io->start, io->stop); if ((io->map > 1) || (io->start > 0xffff) || (io->stop > 0xffff) || @@ -820,7 +827,7 @@ static int tcic_set_mem_map(struct pcmci u_short addr, ctl; u_long base, len, mmap; - DEBUG(1, "tcic: SetMemMap(%d, %d, %#2.2x, %d ns, " + debug(1, "SetMemMap(%d, %d, %#2.2x, %d ns, " "%#5.5lx-%#5.5lx, %#5.5x)\n", psock, mem->map, mem->flags, mem->speed, mem->sys_start, mem->sys_stop, mem->card_start); if ((mem->map > 3) || (mem->card_start > 0x3ffffff) || --- linux-2.6.4-rc2/drivers/pcmcia/yenta_socket.c 2004-01-09 00:04:32.000000000 -0800 +++ 25/drivers/pcmcia/yenta_socket.c 2004-03-07 20:47:43.000000000 -0800 @@ -30,9 +30,9 @@ #if 0 -#define DEBUG(x,args...) printk(KERN_DEBUG "%s: " x, __FUNCTION__, ##args) +#define debug(x,args...) printk(KERN_DEBUG "%s: " x, __FUNCTION__, ##args) #else -#define DEBUG(x,args...) +#define debug(x,args...) #endif /* Don't ask.. */ @@ -47,13 +47,13 @@ static inline u32 cb_readl(struct yenta_socket *socket, unsigned reg) { u32 val = readl(socket->base + reg); - DEBUG("%p %04x %08x\n", socket, reg, val); + debug("%p %04x %08x\n", socket, reg, val); return val; } static inline void cb_writel(struct yenta_socket *socket, unsigned reg, u32 val) { - DEBUG("%p %04x %08x\n", socket, reg, val); + debug("%p %04x %08x\n", socket, reg, val); writel(val, socket->base + reg); } @@ -61,7 +61,7 @@ static inline u8 config_readb(struct yen { u8 val; pci_read_config_byte(socket->dev, offset, &val); - DEBUG("%p %04x %02x\n", socket, offset, val); + debug("%p %04x %02x\n", socket, offset, val); return val; } @@ -69,7 +69,7 @@ static inline u16 config_readw(struct ye { u16 val; pci_read_config_word(socket->dev, offset, &val); - DEBUG("%p %04x %04x\n", socket, offset, val); + debug("%p %04x %04x\n", socket, offset, val); return val; } @@ -77,32 +77,32 @@ static inline u32 config_readl(struct ye { u32 val; pci_read_config_dword(socket->dev, offset, &val); - DEBUG("%p %04x %08x\n", socket, offset, val); + debug("%p %04x %08x\n", socket, offset, val); return val; } static inline void config_writeb(struct yenta_socket *socket, unsigned offset, u8 val) { - DEBUG("%p %04x %02x\n", socket, offset, val); + debug("%p %04x %02x\n", socket, offset, val); pci_write_config_byte(socket->dev, offset, val); } static inline void config_writew(struct yenta_socket *socket, unsigned offset, u16 val) { - DEBUG("%p %04x %04x\n", socket, offset, val); + debug("%p %04x %04x\n", socket, offset, val); pci_write_config_word(socket->dev, offset, val); } static inline void config_writel(struct yenta_socket *socket, unsigned offset, u32 val) { - DEBUG("%p %04x %08x\n", socket, offset, val); + debug("%p %04x %08x\n", socket, offset, val); pci_write_config_dword(socket->dev, offset, val); } static inline u8 exca_readb(struct yenta_socket *socket, unsigned reg) { u8 val = readb(socket->base + 0x800 + reg); - DEBUG("%p %04x %02x\n", socket, reg, val); + debug("%p %04x %02x\n", socket, reg, val); return val; } @@ -111,19 +111,19 @@ static inline u8 exca_readw(struct yenta u16 val; val = readb(socket->base + 0x800 + reg); val |= readb(socket->base + 0x800 + reg + 1) << 8; - DEBUG("%p %04x %04x\n", socket, reg, val); + debug("%p %04x %04x\n", socket, reg, val); return val; } static inline void exca_writeb(struct yenta_socket *socket, unsigned reg, u8 val) { - DEBUG("%p %04x %02x\n", socket, reg, val); + debug("%p %04x %02x\n", socket, reg, val); writeb(val, socket->base + 0x800 + reg); } static void exca_writew(struct yenta_socket *socket, unsigned reg, u16 val) { - DEBUG("%p %04x %04x\n", socket, reg, val); + debug("%p %04x %04x\n", socket, reg, val); writeb(val, socket->base + 0x800 + reg); writeb(val >> 8, socket->base + 0x800 + reg + 1); } --- linux-2.6.4-rc2/drivers/sbus/char/vfc_dev.c 2004-01-09 00:04:32.000000000 -0800 +++ 25/drivers/sbus/char/vfc_dev.c 2004-03-07 20:46:46.000000000 -0800 @@ -24,6 +24,7 @@ #include #include #include +#include #include #include --- linux-2.6.4-rc2/drivers/scsi/53c700.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/drivers/scsi/53c700.c 2004-03-07 20:46:59.000000000 -0800 @@ -321,7 +321,7 @@ NCR_700_detect(Scsi_Host_Template *tpnt, hostdata->script = script; hostdata->pScript = pScript; - dma_sync_single(hostdata->dev, pScript, sizeof(SCRIPT), DMA_TO_DEVICE); + dma_sync_single_for_device(hostdata->dev, pScript, sizeof(SCRIPT), DMA_TO_DEVICE); hostdata->state = NCR_700_HOST_FREE; hostdata->cmd = NULL; host->max_id = 7; @@ -982,8 +982,8 @@ process_script_interrupt(__u32 dsps, __u SCp->cmnd[7] = hostdata->status[0]; SCp->use_sg = 0; SCp->sc_data_direction = SCSI_DATA_READ; - dma_sync_single(hostdata->dev, slot->pCmd, - SCp->cmd_len, DMA_TO_DEVICE); + dma_sync_single_for_device(hostdata->dev, slot->pCmd, + SCp->cmd_len, DMA_TO_DEVICE); SCp->request_bufflen = sizeof(SCp->sense_buffer); slot->dma_handle = dma_map_single(hostdata->dev, SCp->sense_buffer, sizeof(SCp->sense_buffer), DMA_FROM_DEVICE); slot->SG[0].ins = bS_to_host(SCRIPT_MOVE_DATA_IN | sizeof(SCp->sense_buffer)); @@ -1007,7 +1007,7 @@ process_script_interrupt(__u32 dsps, __u // SCp->cmnd[0] == INQUIRY && SCp->use_sg == 0) { // /* Piggy back the tag queueing support // * on this command */ - // dma_sync_single(hostdata->dev, + // dma_sync_single_for_cpu(hostdata->dev, // slot->dma_handle, // SCp->request_bufflen, // DMA_FROM_DEVICE); --- linux-2.6.4-rc2/drivers/scsi/aic7xxx/aic7xxx_osm.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/drivers/scsi/aic7xxx/aic7xxx_osm.c 2004-03-07 20:47:37.000000000 -0800 @@ -3969,11 +3969,10 @@ ahc_linux_alloc_device(struct ahc_softc } static void -ahc_linux_free_device(struct ahc_softc *ahc, struct ahc_linux_device *dev) +__ahc_linux_free_device(struct ahc_softc *ahc, struct ahc_linux_device *dev) { struct ahc_linux_target *targ; - del_timer_sync(&dev->timer); targ = dev->target; targ->devices[dev->lun] = NULL; free(dev, M_DEVBUF); @@ -3983,6 +3982,13 @@ ahc_linux_free_device(struct ahc_softc * ahc_linux_free_target(ahc, targ); } +static void +ahc_linux_free_device(struct ahc_softc *ahc, struct ahc_linux_device *dev) +{ + del_timer_sync(&dev->timer); + __ahc_linux_free_device(ahc, dev); +} + void ahc_send_async(struct ahc_softc *ahc, char channel, u_int target, u_int lun, ac_code code, void *arg) @@ -4693,7 +4699,7 @@ ahc_linux_dev_timed_unfreeze(u_long arg) ahc_linux_run_device_queue(ahc, dev); if (TAILQ_EMPTY(&dev->busyq) && dev->active == 0) - ahc_linux_free_device(ahc, dev); + __ahc_linux_free_device(ahc, dev); ahc_unlock(ahc, &s); } --- linux-2.6.4-rc2/drivers/scsi/cpqfcTSinit.c 2004-01-09 00:04:32.000000000 -0800 +++ 25/drivers/scsi/cpqfcTSinit.c 2004-03-07 20:47:47.000000000 -0800 @@ -46,10 +46,6 @@ #include // request_region() prototype #include -#ifdef __alpha__ -#define __KERNEL_SYSCALLS__ -#endif -#include #include #include // ioctl related #include --- linux-2.6.4-rc2/drivers/scsi/dc395x.c 2003-09-08 13:58:58.000000000 -0700 +++ 25/drivers/scsi/dc395x.c 2004-03-07 20:46:59.000000000 -0800 @@ -2419,13 +2419,13 @@ static void update_sg_list(struct ScsiRe psge->length -= xferred; /* residue data length */ psge->address += xferred; /* residue data pointer */ srb->sg_index = idx; - pci_dma_sync_single(srb->dcb-> - acb->dev, - srb->sg_bus_addr, - sizeof(struct SGentry) - * - DC395x_MAX_SG_LISTENTRY, - PCI_DMA_TODEVICE); + pci_dma_sync_single_for_device(srb->dcb-> + acb->dev, + srb->sg_bus_addr, + sizeof(struct SGentry) + * + DC395x_MAX_SG_LISTENTRY, + PCI_DMA_TODEVICE); break; } psge++; @@ -4298,11 +4298,11 @@ void srb_done(struct AdapterCtlBlk *acb, if (dir != PCI_DMA_NONE) { if (cmd->use_sg) - pci_dma_sync_sg(acb->dev, + pci_dma_sync_sg_for_cpu(acb->dev, (struct scatterlist *) cmd-> request_buffer, cmd->use_sg, dir); else if (cmd->request_buffer) - pci_dma_sync_single(acb->dev, + pci_dma_sync_single_for_cpu(acb->dev, srb->segment_x[0].address, cmd->request_bufflen, dir); } --- linux-2.6.4-rc2/drivers/scsi/eata.c 2003-07-27 12:14:39.000000000 -0700 +++ 25/drivers/scsi/eata.c 2004-03-07 20:46:59.000000000 -0800 @@ -1598,17 +1598,17 @@ static void sync_dma(unsigned int i, uns pci_dir = scsi_to_pci_dma_dir(SCpnt->sc_data_direction); if (DEV2H(cpp->sense_addr)) - pci_dma_sync_single(HD(j)->pdev, DEV2H(cpp->sense_addr), + pci_dma_sync_single_for_cpu(HD(j)->pdev, DEV2H(cpp->sense_addr), DEV2H(cpp->sense_len), PCI_DMA_FROMDEVICE); if (SCpnt->use_sg) - pci_dma_sync_sg(HD(j)->pdev, SCpnt->request_buffer, + pci_dma_sync_sg_for_cpu(HD(j)->pdev, SCpnt->request_buffer, SCpnt->use_sg, pci_dir); if (!DEV2H(cpp->data_len)) pci_dir = PCI_DMA_BIDIRECTIONAL; if (DEV2H(cpp->data_address)) - pci_dma_sync_single(HD(j)->pdev, DEV2H(cpp->data_address), + pci_dma_sync_single_for_cpu(HD(j)->pdev, DEV2H(cpp->data_address), DEV2H(cpp->data_len), pci_dir); } --- linux-2.6.4-rc2/drivers/scsi/hosts.c 2004-01-09 00:04:32.000000000 -0800 +++ 25/drivers/scsi/hosts.c 2004-03-07 20:46:55.000000000 -0800 @@ -32,6 +32,7 @@ #include #include +#include #include "scsi.h" #include "scsi_priv.h" @@ -222,6 +223,11 @@ struct Scsi_Host *scsi_host_alloc(struct shost->max_id = 8; shost->max_lun = 8; + /* Give each shost a default transportt if the driver + * doesn't yet support Transport Attributes */ + if (!shost->transportt) + shost->transportt = &blank_transport_template; + /* * All drivers right now should be able to handle 12 byte * commands. Every so often there are requests for 16 byte --- linux-2.6.4-rc2/drivers/scsi/ide-scsi.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/drivers/scsi/ide-scsi.c 2004-03-07 20:47:40.000000000 -0800 @@ -78,6 +78,7 @@ typedef struct idescsi_pc_s { #define PC_DMA_IN_PROGRESS 0 /* 1 while DMA in progress */ #define PC_WRITING 1 /* Data direction */ #define PC_TRANSFORM 2 /* transform SCSI commands */ +#define PC_TIMEDOUT 3 /* command timed out */ #define PC_DMA_OK 4 /* Use DMA */ /* @@ -307,6 +308,41 @@ static int idescsi_check_condition(ide_d return ide_do_drive_cmd(drive, rq, ide_preempt); } +ide_startstop_t idescsi_atapi_error (ide_drive_t *drive, const char *msg, byte stat) +{ + struct request *rq; + byte err; + + err = ide_dump_atapi_status(drive, msg, stat); + + if (drive == NULL || (rq = HWGROUP(drive)->rq) == NULL) + return ide_stopped; + + if (HWIF(drive)->INB(IDE_STATUS_REG) & (BUSY_STAT|DRQ_STAT)) + /* force an abort */ + HWIF(drive)->OUTB(WIN_IDLEIMMEDIATE,IDE_COMMAND_REG); + + rq->errors++; + DRIVER(drive)->end_request(drive, 0, 0); + return ide_stopped; +} + +ide_startstop_t idescsi_atapi_abort (ide_drive_t *drive, const char *msg) +{ + struct request *rq; + + if (drive == NULL || (rq = HWGROUP(drive)->rq) == NULL) + return ide_stopped; + +#if IDESCSI_DEBUG_LOG + printk(KERN_WARNING "idescsi_atapi_abort called for %lu\n", + ((idescsi_pc_t *) rq->special)->scsi_cmd->serial_number); +#endif + rq->errors |= ERROR_MAX; + DRIVER(drive)->end_request(drive, 0, 0); + return ide_stopped; +} + static int idescsi_end_request (ide_drive_t *drive, int uptodate, int nrsecs) { idescsi_scsi_t *scsi = drive_to_idescsi(drive); @@ -334,7 +370,13 @@ static int idescsi_end_request (ide_driv kfree(rq); pc = opc; rq = pc->rq; - pc->scsi_cmd->result = (CHECK_CONDITION << 1) | (DID_OK << 16); + pc->scsi_cmd->result = (CHECK_CONDITION << 1) | + ((test_bit(PC_TIMEDOUT, &pc->flags)?DID_TIME_OUT:DID_OK) << 16); + } else if (test_bit(PC_TIMEDOUT, &pc->flags)) { + if (log) + printk (KERN_WARNING "ide-scsi: %s: timed out for %lu\n", + drive->name, pc->scsi_cmd->serial_number); + pc->scsi_cmd->result = DID_TIME_OUT << 16; } else if (rq->errors >= ERROR_MAX) { pc->scsi_cmd->result = DID_ERROR << 16; if (log) @@ -374,6 +416,19 @@ static inline unsigned long get_timeout( return IDE_MAX(WAIT_CMD, pc->timeout - jiffies); } +static int idescsi_expiry(ide_drive_t *drive) +{ + idescsi_scsi_t *scsi = drive->driver_data; + idescsi_pc_t *pc = scsi->pc; + +#if IDESCSI_DEBUG_LOG + printk(KERN_WARNING "idescsi_expiry called for %lu at %lu\n", pc->scsi_cmd->serial_number, jiffies); +#endif + set_bit(PC_TIMEDOUT, &pc->flags); + + return 0; /* we do not want the ide subsystem to retry */ +} + /* * Our interrupt handler. */ @@ -393,6 +448,15 @@ static ide_startstop_t idescsi_pc_intr ( printk (KERN_INFO "ide-scsi: Reached idescsi_pc_intr interrupt handler\n"); #endif /* IDESCSI_DEBUG_LOG */ + if (test_bit(PC_TIMEDOUT, &pc->flags)){ +#if IDESCSI_DEBUG_LOG + printk(KERN_WARNING "idescsi_pc_intr: got timed out packet %lu at %lu\n", + pc->scsi_cmd->serial_number, jiffies); +#endif + /* end this request now - scsi should retry it*/ + idescsi_end_request (drive, 1, 0); + return ide_stopped; + } if (test_and_clear_bit (PC_DMA_IN_PROGRESS, &pc->flags)) { #if IDESCSI_DEBUG_LOG printk ("ide-scsi: %s: DMA complete\n", drive->name); @@ -442,7 +506,7 @@ static ide_startstop_t idescsi_pc_intr ( pc->actually_transferred += temp; pc->current_position += temp; idescsi_discard_data(drive, bcount.all - temp); - ide_set_handler(drive, &idescsi_pc_intr, get_timeout(pc), NULL); + ide_set_handler(drive, &idescsi_pc_intr, get_timeout(pc), idescsi_expiry); return ide_started; } #if IDESCSI_DEBUG_LOG @@ -467,7 +531,8 @@ static ide_startstop_t idescsi_pc_intr ( pc->actually_transferred += bcount.all; pc->current_position += bcount.all; - ide_set_handler(drive, &idescsi_pc_intr, get_timeout(pc), NULL); /* And set the interrupt handler again */ + /* And set the interrupt handler again */ + ide_set_handler(drive, &idescsi_pc_intr, get_timeout(pc), idescsi_expiry); return ide_started; } @@ -492,7 +557,7 @@ static ide_startstop_t idescsi_transfer_ if (HWGROUP(drive)->handler != NULL) BUG(); /* Set the interrupt routine */ - ide_set_handler(drive, &idescsi_pc_intr, get_timeout(pc), NULL); + ide_set_handler(drive, &idescsi_pc_intr, get_timeout(pc), idescsi_expiry); /* Send the actual packet */ atapi_output_bytes(drive, scsi->pc->c, 12); if (test_bit (PC_DMA_OK, &pc->flags)) { @@ -540,7 +605,7 @@ static ide_startstop_t idescsi_issue_pc if (HWGROUP(drive)->handler != NULL) BUG(); ide_set_handler(drive, &idescsi_transfer_pc, - get_timeout(pc), NULL); + get_timeout(pc), idescsi_expiry); /* Issue the packet command */ HWIF(drive)->OUTB(WIN_PACKETCMD, IDE_COMMAND_REG); return ide_started; @@ -633,6 +698,8 @@ static ide_driver_t idescsi_driver = { .cleanup = idescsi_cleanup, .do_request = idescsi_do_request, .end_request = idescsi_end_request, + .error = idescsi_atapi_error, + .abort = idescsi_atapi_abort, .drives = LIST_HEAD_INIT(idescsi_driver.drives), }; @@ -852,66 +919,132 @@ abort: return 1; } -static int idescsi_abort (Scsi_Cmnd *cmd) +static int idescsi_eh_abort (Scsi_Cmnd *cmd) { - int countdown = 8; - unsigned long flags; - idescsi_scsi_t *scsi = scsihost_to_idescsi(cmd->device->host); - ide_drive_t *drive = scsi->drive; + idescsi_scsi_t *scsi = scsihost_to_idescsi(cmd->device->host); + ide_drive_t *drive = scsi->drive; + int busy; + int ret = FAILED; - printk (KERN_ERR "ide-scsi: abort called for %lu\n", cmd->serial_number); - while (countdown--) { - /* is cmd active? - * need to lock so this stuff doesn't change under us */ - spin_lock_irqsave(&ide_lock, flags); - if (scsi->pc && scsi->pc->scsi_cmd && - scsi->pc->scsi_cmd->serial_number == cmd->serial_number) { - /* yep - let's give it some more time - - * we can do that, we're in _our_ error kernel thread */ - spin_unlock_irqrestore(&ide_lock, flags); - scsi_sleep(HZ); - continue; - } - /* no, but is it queued in the ide subsystem? */ - if (elv_queue_empty(drive->queue)) { - spin_unlock_irqrestore(&ide_lock, flags); - return SUCCESS; - } - spin_unlock_irqrestore(&ide_lock, flags); - schedule_timeout(HZ/10); + /* In idescsi_eh_abort we try to gently pry our command from the ide subsystem */ + + if (test_bit(IDESCSI_LOG_CMD, &scsi->log)) + printk (KERN_WARNING "ide-scsi: abort called for %lu\n", cmd->serial_number); + + if (!drive) { + printk (KERN_WARNING "ide-scsi: Drive not set in idescsi_eh_abort\n"); + WARN_ON(1); + goto no_drive; + } + + /* First give it some more time, how much is "right" is hard to say :-( */ + + busy = ide_wait_not_busy(HWIF(drive), 100); /* FIXME - uses mdelay which causes latency? */ + if (test_bit(IDESCSI_LOG_CMD, &scsi->log)) + printk (KERN_WARNING "ide-scsi: drive did%s become ready\n", busy?" not":""); + + spin_lock_irq(&ide_lock); + + /* If there is no pc running we're done (our interrupt took care of it) */ + if (!scsi->pc) { + ret = SUCCESS; + goto ide_unlock; + } + + /* It's somewhere in flight. Does ide subsystem agree? */ + if (scsi->pc->scsi_cmd->serial_number == cmd->serial_number && !busy && + elv_queue_empty(drive->queue) && HWGROUP(drive)->rq != scsi->pc->rq) { + /* + * FIXME - not sure this condition can ever occur + */ + printk (KERN_ERR "ide-scsi: cmd aborted!\n"); + + idescsi_free_bio(scsi->pc->rq->bio); + if (scsi->pc->rq->flags & REQ_SENSE) + kfree(scsi->pc->buffer); + kfree(scsi->pc->rq); + kfree(scsi->pc); + scsi->pc = NULL; + + ret = SUCCESS; } - return FAILED; + +ide_unlock: + spin_unlock_irq(&ide_lock); +no_drive: + if (test_bit(IDESCSI_LOG_CMD, &scsi->log)) + printk (KERN_WARNING "ide-scsi: abort returns %s\n", ret == SUCCESS?"success":"failed"); + + return ret; } -static int idescsi_reset (Scsi_Cmnd *cmd) +static int idescsi_eh_reset (Scsi_Cmnd *cmd) { - unsigned long flags; struct request *req; - idescsi_scsi_t *idescsi = scsihost_to_idescsi(cmd->device->host); - ide_drive_t *drive = idescsi->drive; + idescsi_scsi_t *scsi = scsihost_to_idescsi(cmd->device->host); + ide_drive_t *drive = scsi->drive; + int ready = 0; + int ret = SUCCESS; + + /* In idescsi_eh_reset we forcefully remove the command from the ide subsystem and reset the device. */ + + if (test_bit(IDESCSI_LOG_CMD, &scsi->log)) + printk (KERN_WARNING "ide-scsi: reset called for %lu\n", cmd->serial_number); + + if (!drive) { + printk (KERN_WARNING "ide-scsi: Drive not set in idescsi_eh_reset\n"); + WARN_ON(1); + return FAILED; + } + + spin_lock_irq(&ide_lock); + + if (!scsi->pc || (req = scsi->pc->rq) != HWGROUP(drive)->rq || !HWGROUP(drive)->handler) { + printk (KERN_WARNING "ide-scsi: No active request in idescsi_eh_reset\n"); + spin_unlock(&ide_lock); + return FAILED; + } + + /* kill current request */ + blkdev_dequeue_request(req); + end_that_request_last(req); + idescsi_free_bio(req->bio); + if (req->flags & REQ_SENSE) + kfree(scsi->pc->buffer); + kfree(scsi->pc); + scsi->pc = NULL; + kfree(req); - printk (KERN_ERR "ide-scsi: reset called for %lu\n", cmd->serial_number); - /* first null the handler for the drive and let any process - * doing IO (on another CPU) run to (partial) completion - * the lock prevents processing new requests */ - spin_lock_irqsave(&ide_lock, flags); - while (HWGROUP(drive)->handler) { - HWGROUP(drive)->handler = NULL; - schedule_timeout(1); - } /* now nuke the drive queue */ while ((req = elv_next_request(drive->queue))) { blkdev_dequeue_request(req); end_that_request_last(req); } - /* FIXME - this will probably leak memory */ + HWGROUP(drive)->rq = NULL; - if (drive_to_idescsi(drive)) - drive_to_idescsi(drive)->pc = NULL; - spin_unlock_irqrestore(&ide_lock, flags); - /* finally, reset the drive (and its partner on the bus...) */ - ide_do_reset (drive); - return SUCCESS; + HWGROUP(drive)->handler = NULL; + HWGROUP(drive)->busy = 1; /* will set this to zero when ide reset finished */ + spin_unlock_irq(&ide_lock); + + ide_do_reset(drive); + + /* ide_do_reset starts a polling handler which restarts itself every 50ms until the reset finishes */ + + do { + set_current_state(TASK_UNINTERRUPTIBLE); + spin_unlock_irq(cmd->device->host->host_lock); + schedule_timeout(HZ/20); + spin_lock_irq(cmd->device->host->host_lock); + } while ( HWGROUP(drive)->handler ); + + ready = drive_is_ready(drive); + HWGROUP(drive)->busy--; + if (!ready) { + printk (KERN_ERR "ide-scsi: reset failed!\n"); + ret = FAILED; + } + + return ret; } static int idescsi_bios(struct scsi_device *sdev, struct block_device *bdev, @@ -935,8 +1068,8 @@ static Scsi_Host_Template idescsi_templa .slave_configure = idescsi_slave_configure, .ioctl = idescsi_ioctl, .queuecommand = idescsi_queue, - .eh_abort_handler = idescsi_abort, - .eh_device_reset_handler = idescsi_reset, + .eh_abort_handler = idescsi_eh_abort, + .eh_host_reset_handler = idescsi_eh_reset, .bios_param = idescsi_bios, .can_queue = 40, .this_id = -1, --- linux-2.6.4-rc2/drivers/scsi/ini9100u.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/drivers/scsi/ini9100u.c 2004-03-07 20:46:55.000000000 -0800 @@ -180,15 +180,6 @@ static int setup_debug = 0; static char *setup_str = (char *) NULL; -static irqreturn_t i91u_intr0(int irq, void *dev_id, struct pt_regs *); -static irqreturn_t i91u_intr1(int irq, void *dev_id, struct pt_regs *); -static irqreturn_t i91u_intr2(int irq, void *dev_id, struct pt_regs *); -static irqreturn_t i91u_intr3(int irq, void *dev_id, struct pt_regs *); -static irqreturn_t i91u_intr4(int irq, void *dev_id, struct pt_regs *); -static irqreturn_t i91u_intr5(int irq, void *dev_id, struct pt_regs *); -static irqreturn_t i91u_intr6(int irq, void *dev_id, struct pt_regs *); -static irqreturn_t i91u_intr7(int irq, void *dev_id, struct pt_regs *); - static void i91u_panic(char *msg); static void i91uSCBPost(BYTE * pHcb, BYTE * pScb); @@ -278,7 +269,7 @@ static irqreturn_t i91u_intr(int irqno, unsigned long flags; spin_lock_irqsave(dev->host_lock, flags); - tul_isr((HCS *)hreg->base); + tul_isr((HCS *)dev->base); spin_unlock_irqrestore(dev->host_lock, flags); return IRQ_HANDLED; } --- linux-2.6.4-rc2/drivers/scsi/Kconfig 2004-03-03 23:12:46.000000000 -0800 +++ 25/drivers/scsi/Kconfig 2004-03-07 20:46:55.000000000 -0800 @@ -196,6 +196,25 @@ config SCSI_LOGGING there should be no noticeable performance impact as long as you have logging turned off. +menu "SCSI Transport Attributes" + depends on SCSI + +config SCSI_SPI_ATTRS + tristate "Parallel SCSI (SPI) Transport Attributes" + depends on SCSI + help + If you wish to export transport-specific information about + each attached SCSI device to sysfs, say Y. Otherwise, say N. + +config SCSI_FC_ATTRS + tristate "FiberChannel Transport Attributes" + depends on SCSI + help + If you wish to export transport-specific information about + each attached FiberChannel device to sysfs, say Y. + Otherwise, say N. + +endmenu menu "SCSI low-level drivers" depends on SCSI!=n --- linux-2.6.4-rc2/drivers/scsi/Makefile 2004-02-17 20:48:44.000000000 -0800 +++ 25/drivers/scsi/Makefile 2004-03-07 20:46:55.000000000 -0800 @@ -22,6 +22,14 @@ subdir-$(CONFIG_PCMCIA) += pcmcia obj-$(CONFIG_SCSI) += scsi_mod.o +# --- NOTE ORDERING HERE --- +# For kernel non-modular link, transport attributes need to +# be initialised before drivers +# -------------------------- +obj-$(CONFIG_SCSI_SPI_ATTRS) += scsi_transport_spi.o +obj-$(CONFIG_SCSI_FC_ATTRS) += scsi_transport_fc.o + + obj-$(CONFIG_SCSI_AMIGA7XX) += amiga7xx.o 53c7xx.o obj-$(CONFIG_A3000_SCSI) += a3000.o wd33c93.o obj-$(CONFIG_A2091_SCSI) += a2091.o wd33c93.o @@ -130,7 +138,7 @@ scsi_mod-y += scsi.o hosts.o scsi_ioct scsi_mod-$(CONFIG_SYSCTL) += scsi_sysctl.o scsi_mod-$(CONFIG_SCSI_PROC_FS) += scsi_proc.o scsi_mod-$(CONFIG_X86_PC9800) += scsi_pc98.o - + sd_mod-objs := sd.o sr_mod-objs := sr.o sr_ioctl.o sr_vendor.o initio-objs := ini9100u.o i91uscsi.o --- linux-2.6.4-rc2/drivers/scsi/megaraid.c 2004-02-03 20:42:37.000000000 -0800 +++ 25/drivers/scsi/megaraid.c 2004-03-07 20:46:59.000000000 -0800 @@ -261,10 +261,6 @@ mega_query_adapter(adapter_t *adapter) "megaraid: Product_info cmd failed with error: %d\n", retval); - pci_dma_sync_single(adapter->dev, prod_info_dma_handle, - sizeof(mega_product_info), - PCI_DMA_FROMDEVICE); - pci_unmap_single(adapter->dev, prod_info_dma_handle, sizeof(mega_product_info), PCI_DMA_FROMDEVICE); } @@ -1651,26 +1647,11 @@ mega_free_scb(adapter_t *adapter, scb_t case MEGA_BULK_DATA: pci_unmap_page(adapter->dev, scb->dma_h_bulkdata, scb->cmd->request_bufflen, scb->dma_direction); - - if( scb->dma_direction == PCI_DMA_FROMDEVICE ) { - pci_dma_sync_single(adapter->dev, - scb->dma_h_bulkdata, - scb->cmd->request_bufflen, - PCI_DMA_FROMDEVICE); - } - break; case MEGA_SGLIST: pci_unmap_sg(adapter->dev, scb->cmd->request_buffer, scb->cmd->use_sg, scb->dma_direction); - - if( scb->dma_direction == PCI_DMA_FROMDEVICE ) { - pci_dma_sync_sg(adapter->dev, - scb->cmd->request_buffer, - scb->cmd->use_sg, PCI_DMA_FROMDEVICE); - } - break; default: @@ -1758,14 +1739,6 @@ mega_build_sglist(adapter_t *adapter, sc *buf = (u32)scb->dma_h_bulkdata; *len = (u32)cmd->request_bufflen; } - - if( scb->dma_direction == PCI_DMA_TODEVICE ) { - pci_dma_sync_single(adapter->dev, - scb->dma_h_bulkdata, - cmd->request_bufflen, - PCI_DMA_TODEVICE); - } - return 0; } @@ -1804,11 +1777,6 @@ mega_build_sglist(adapter_t *adapter, sc */ *len = (u32)cmd->request_bufflen; - if( scb->dma_direction == PCI_DMA_TODEVICE ) { - pci_dma_sync_sg(adapter->dev, sgl, cmd->use_sg, - PCI_DMA_TODEVICE); - } - /* Return count of SG requests */ return sgcnt; } --- linux-2.6.4-rc2/drivers/scsi/ncr53c8xx.c 2003-10-08 15:07:09.000000000 -0700 +++ 25/drivers/scsi/ncr53c8xx.c 2004-03-07 20:46:59.000000000 -0800 @@ -5140,9 +5140,10 @@ void ncr_complete (ncb_p np, ccb_p cp) */ if (cmd->cmnd[0] == 0x12 && !(cmd->cmnd[1] & 0x3) && cmd->cmnd[4] >= 7 && !cmd->use_sg) { - sync_scsi_data(np, cmd); /* SYNC the data */ + sync_scsi_data_for_cpu(np, cmd); /* SYNC the data */ ncr_setup_lcb (np, cmd->device->id, cmd->device->lun, (char *) cmd->request_buffer); + sync_scsi_data_for_device(np, cmd); /* SYNC the data */ } tp->bytes += cp->data_len; --- linux-2.6.4-rc2/drivers/scsi/oktagon_esp.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/drivers/scsi/oktagon_esp.c 2004-03-07 20:47:47.000000000 -0800 @@ -12,7 +12,6 @@ #define USE_BOTTOM_HALF #endif -#define __KERNEL_SYSCALLS__ #include #include @@ -43,8 +42,6 @@ #include #endif -#include - /* The controller registers can be found in the Z2 config area at these * offsets: */ --- linux-2.6.4-rc2/drivers/scsi/qlogicfc.c 2003-10-08 15:07:09.000000000 -0700 +++ 25/drivers/scsi/qlogicfc.c 2004-03-07 20:46:46.000000000 -0800 @@ -411,7 +411,7 @@ static unsigned short risc_code_addr01 = if that mbox should be copied as input. For example 0x2 would mean only copy mbox1. */ -const u_char mbox_param[] = +static const u_char mbox_param[] = { 0x01, /* MBOX_NO_OP */ 0x1f, /* MBOX_LOAD_RAM */ --- linux-2.6.4-rc2/drivers/scsi/qlogicpti.c 2004-02-03 20:42:37.000000000 -0800 +++ 25/drivers/scsi/qlogicpti.c 2004-03-07 20:46:46.000000000 -0800 @@ -54,7 +54,7 @@ static int qptis_running = 0; #define PACKB(a, b) (((a)<<4)|(b)) -const u_char mbox_param[] = { +static const u_char mbox_param[] = { PACKB(1, 1), /* MBOX_NO_OP */ PACKB(5, 5), /* MBOX_LOAD_RAM */ PACKB(2, 0), /* MBOX_EXEC_FIRMWARE */ --- linux-2.6.4-rc2/drivers/scsi/scsi.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/drivers/scsi/scsi.c 2004-03-07 20:47:23.000000000 -0800 @@ -53,6 +53,8 @@ #include #include #include +#include +#include #include #include "scsi.h" @@ -104,7 +106,7 @@ const char *const scsi_device_types[MAX_ "Communications ", "Unknown ", "Unknown ", - "Unknown ", + "RAID ", "Enclosure ", }; @@ -1130,6 +1132,38 @@ int scsi_device_cancel(struct scsi_devic return 0; } +#ifdef CONFIG_HOTPLUG_CPU +static int scsi_cpu_notify(struct notifier_block *self, + unsigned long action, void *hcpu) +{ + int cpu = (unsigned long)hcpu; + + switch(action) { + case CPU_DEAD: + /* Drain scsi_done_q. */ + local_irq_disable(); + list_splice_init(&per_cpu(scsi_done_q, cpu), + &__get_cpu_var(scsi_done_q)); + raise_softirq_irqoff(SCSI_SOFTIRQ); + local_irq_enable(); + break; + default: + break; + } + return NOTIFY_OK; +} + +static struct notifier_block __devinitdata scsi_cpu_nb = { + .notifier_call = scsi_cpu_notify, +}; + +#define register_scsi_cpu() register_cpu_notifier(&scsi_cpu_nb) +#define unregister_scsi_cpu() unregister_cpu_notifier(&scsi_cpu_nb) +#else +#define register_scsi_cpu() +#define unregister_scsi_cpu() +#endif /* CONFIG_HOTPLUG_CPU */ + MODULE_DESCRIPTION("SCSI core"); MODULE_LICENSE("GPL"); @@ -1164,6 +1198,7 @@ static int __init init_scsi(void) devfs_mk_dir("scsi"); open_softirq(SCSI_SOFTIRQ, scsi_softirq, NULL); + register_scsi_cpu(); printk(KERN_NOTICE "SCSI subsystem initialized\n"); return 0; @@ -1191,6 +1226,7 @@ static void __exit exit_scsi(void) devfs_remove("scsi"); scsi_exit_procfs(); scsi_exit_queue(); + unregister_scsi_cpu(); } subsys_initcall(init_scsi); --- linux-2.6.4-rc2/drivers/scsi/scsi_devinfo.c 2003-10-25 14:45:46.000000000 -0700 +++ 25/drivers/scsi/scsi_devinfo.c 2004-03-07 21:57:41.000000000 -0800 @@ -94,95 +94,93 @@ static struct { * The following causes a failed REQUEST SENSE on lun 1 for * seagate controller, which causes SCSI code to reset bus. */ - {"TEXEL", "CD-ROM", "1.06", BLIST_NOLUN}, + {"HP", "C1750A", "3226", BLIST_NOLUN}, /* scanjet iic */ + {"HP", "C1790A", "", BLIST_NOLUN}, /* scanjet iip */ + {"HP", "C2500A", "", BLIST_NOLUN}, /* scanjet iicx */ + {"MEDIAVIS", "CDR-H93MV", "1.31", BLIST_NOLUN}, /* locks up */ + {"MICROTEK", "ScanMaker II", "5.61", BLIST_NOLUN}, /* responds to all lun */ + {"MITSUMI", "CD-R CR-2201CS", "6119", BLIST_NOLUN}, /* locks up */ + {"NEC", "D3856", "0009", BLIST_NOLUN}, {"QUANTUM", "LPS525S", "3110", BLIST_NOLUN}, /* locks up */ {"QUANTUM", "PD1225S", "3110", BLIST_NOLUN}, /* locks up */ {"QUANTUM", "FIREBALL ST4.3S", "0F0C", BLIST_NOLUN}, /* locks up */ - {"MEDIAVIS", "CDR-H93MV", "1.31", BLIST_NOLUN}, /* locks up */ + {"RELISYS", "Scorpio", NULL, BLIST_NOLUN}, /* responds to all lun */ {"SANKYO", "CP525", "6.64", BLIST_NOLUN}, /* causes failed REQ SENSE, extra reset */ - {"HP", "C1750A", "3226", BLIST_NOLUN}, /* scanjet iic */ - {"HP", "C1790A", "", BLIST_NOLUN}, /* scanjet iip */ - {"HP", "C2500A", "", BLIST_NOLUN}, /* scanjet iicx */ + {"TEXEL", "CD-ROM", "1.06", BLIST_NOLUN}, {"YAMAHA", "CDR100", "1.00", BLIST_NOLUN}, /* locks up */ {"YAMAHA", "CDR102", "1.00", BLIST_NOLUN}, /* locks up */ {"YAMAHA", "CRW8424S", "1.0", BLIST_NOLUN}, /* locks up */ {"YAMAHA", "CRW6416S", "1.0c", BLIST_NOLUN}, /* locks up */ - {"MITSUMI", "CD-R CR-2201CS", "6119", BLIST_NOLUN}, /* locks up */ - {"RELISYS", "Scorpio", NULL, BLIST_NOLUN}, /* responds to all lun */ - {"MICROTEK", "ScanMaker II", "5.61", BLIST_NOLUN}, /* responds to all lun */ - {"NEC", "D3856", "0009", BLIST_NOLUN}, /* * Other types of devices that have special flags. */ - {"SONY", "CD-ROM CDU-8001", NULL, BLIST_BORKEN}, - {"TEXEL", "CD-ROM", "1.06", BLIST_BORKEN}, - {"IOMEGA", "Io20S *F", NULL, BLIST_KEY}, - {"INSITE", "Floptical F*8I", NULL, BLIST_KEY}, - {"INSITE", "I325VM", NULL, BLIST_KEY}, - {"LASOUND", "CDX7405", "3.10", BLIST_MAX5LUN | BLIST_SINGLELUN}, - {"MICROP", "4110", NULL, BLIST_NOTQ}, - {"NRC", "MBR-7", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, - {"NRC", "MBR-7.4", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, - {"REGAL", "CDC-4X", NULL, BLIST_MAX5LUN | BLIST_SINGLELUN}, - {"NAKAMICH", "MJ-4.8S", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, - {"NAKAMICH", "MJ-5.16S", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, - {"PIONEER", "CD-ROM DRM-600", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, - {"PIONEER", "CD-ROM DRM-602X", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, - {"PIONEER", "CD-ROM DRM-604X", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, - {"EMULEX", "MD21/S2 ESDI", NULL, BLIST_SINGLELUN}, + {"ADAPTEC", "AACRAID", NULL, BLIST_FORCELUN}, + {"ADAPTEC", "Adaptec 5400S", NULL, BLIST_FORCELUN}, {"CANON", "IPUBJD", NULL, BLIST_SPARSELUN}, - {"nCipher", "Fastness Crypto", NULL, BLIST_FORCELUN}, - {"DEC", "HSG80", NULL, BLIST_SPARSELUN | BLIST_NOSTARTONADD}, + {"CMD", "CRA-7280", NULL, BLIST_SPARSELUN}, /* CMD RAID Controller */ + {"CNSI", "G7324", NULL, BLIST_SPARSELUN}, /* Chaparral G7324 RAID */ + {"CNSi", "G8324", NULL, BLIST_SPARSELUN}, /* Chaparral G8324 RAID */ {"COMPAQ", "LOGICAL VOLUME", NULL, BLIST_FORCELUN}, {"COMPAQ", "CR3500", NULL, BLIST_FORCELUN}, - {"NEC", "PD-1 ODX654P", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, - {"MATSHITA", "PD-1", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, - {"iomega", "jaz 1GB", "J.86", BLIST_NOTQ | BLIST_NOLUN}, - {"TOSHIBA", "CDROM", NULL, BLIST_ISROM}, - {"TOSHIBA", "CD-ROM", NULL, BLIST_ISROM}, - {"MegaRAID", "LD", NULL, BLIST_FORCELUN}, - {"DGC", "RAID", NULL, BLIST_SPARSELUN}, /* Dell PV 650F, storage on LUN 0 */ - {"DGC", "DISK", NULL, BLIST_SPARSELUN}, /* Dell PV 650F, no storage on LUN 0 */ + {"COMPAQ", "MSA1000", NULL, BLIST_SPARSELUN | BLIST_NOSTARTONADD}, + {"COMPAQ", "MSA1000 VOLUME", NULL, BLIST_SPARSELUN | BLIST_NOSTARTONADD}, + {"COMPAQ", "HSV110", NULL, BLIST_SPARSELUN | BLIST_NOSTARTONADD}, + {"DDN", "SAN DataDirector", "*", BLIST_SPARSELUN}, + {"DEC", "HSG80", NULL, BLIST_SPARSELUN | BLIST_NOSTARTONADD}, {"DELL", "PV660F", NULL, BLIST_SPARSELUN}, {"DELL", "PV660F PSEUDO", NULL, BLIST_SPARSELUN}, {"DELL", "PSEUDO DEVICE .", NULL, BLIST_SPARSELUN}, /* Dell PV 530F */ {"DELL", "PV530F", NULL, BLIST_SPARSELUN}, + {"DELL", "PERCRAID", NULL, BLIST_FORCELUN}, + {"DGC", "RAID", NULL, BLIST_SPARSELUN}, /* Dell PV 650F, storage on LUN 0 */ + {"DGC", "DISK", NULL, BLIST_SPARSELUN}, /* Dell PV 650F, no storage on LUN 0 */ {"EMC", "SYMMETRIX", NULL, BLIST_SPARSELUN | BLIST_LARGELUN | BLIST_FORCELUN}, + {"EMULEX", "MD21/S2 ESDI", NULL, BLIST_SINGLELUN}, + {"FSC", "CentricStor", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, + {"Generic", "USB Storage-SMC", "0207", BLIST_FORCELUN}, + {"HITACHI", "DF400", "*", BLIST_SPARSELUN}, + {"HITACHI", "DF500", "*", BLIST_SPARSELUN}, + {"HITACHI", "DF600", "*", BLIST_SPARSELUN}, {"HP", "A6189A", NULL, BLIST_SPARSELUN | BLIST_LARGELUN}, /* HP VA7400 */ {"HP", "OPEN-", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, /* HP XP Arrays */ - {"CMD", "CRA-7280", NULL, BLIST_SPARSELUN}, /* CMD RAID Controller */ - {"CNSI", "G7324", NULL, BLIST_SPARSELUN}, /* Chaparral G7324 RAID */ - {"CNSi", "G8324", NULL, BLIST_SPARSELUN}, /* Chaparral G8324 RAID */ - {"Zzyzx", "RocketStor 500S", NULL, BLIST_SPARSELUN}, - {"Zzyzx", "RocketStor 2000", NULL, BLIST_SPARSELUN}, - {"SONY", "TSL", NULL, BLIST_FORCELUN}, /* DDS3 & DDS4 autoloaders */ - {"DELL", "PERCRAID", NULL, BLIST_FORCELUN}, {"HP", "NetRAID-4M", NULL, BLIST_FORCELUN}, - {"ADAPTEC", "AACRAID", NULL, BLIST_FORCELUN}, - {"ADAPTEC", "Adaptec 5400S", NULL, BLIST_FORCELUN}, - {"COMPAQ", "MSA1000", NULL, BLIST_SPARSELUN | BLIST_NOSTARTONADD}, - {"COMPAQ", "MSA1000 VOLUME", NULL, BLIST_SPARSELUN | BLIST_NOSTARTONADD}, - {"COMPAQ", "HSV110", NULL, BLIST_SPARSELUN | BLIST_NOSTARTONADD}, {"HP", "HSV100", NULL, BLIST_SPARSELUN | BLIST_NOSTARTONADD}, {"HP", "C1557A", NULL, BLIST_FORCELUN}, {"IBM", "AuSaV1S2", NULL, BLIST_FORCELUN}, - {"FSC", "CentricStor", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, - {"DDN", "SAN DataDirector", "*", BLIST_SPARSELUN}, - {"HITACHI", "DF400", "*", BLIST_SPARSELUN}, - {"HITACHI", "DF500", "*", BLIST_SPARSELUN}, - {"HITACHI", "DF600", "*", BLIST_SPARSELUN}, {"IBM", "ProFibre 4000R", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, - {"SUN", "T300", "*", BLIST_SPARSELUN}, - {"SUN", "T4", "*", BLIST_SPARSELUN}, + {"iomega", "jaz 1GB", "J.86", BLIST_NOTQ | BLIST_NOLUN}, + {"IOMEGA", "Io20S *F", NULL, BLIST_KEY}, + {"INSITE", "Floptical F*8I", NULL, BLIST_KEY}, + {"INSITE", "I325VM", NULL, BLIST_KEY}, + {"LASOUND", "CDX7405", "3.10", BLIST_MAX5LUN | BLIST_SINGLELUN}, + {"MATSHITA", "PD-1", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, + {"MegaRAID", "LD", NULL, BLIST_FORCELUN}, + {"MICROP", "4110", NULL, BLIST_NOTQ}, + {"MYLEX", "DACARMRB", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, + {"nCipher", "Fastness Crypto", NULL, BLIST_FORCELUN}, + {"NAKAMICH", "MJ-4.8S", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, + {"NAKAMICH", "MJ-5.16S", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, + {"NEC", "PD-1 ODX654P", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, + {"NRC", "MBR-7", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, + {"NRC", "MBR-7.4", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, + {"PIONEER", "CD-ROM DRM-600", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, + {"PIONEER", "CD-ROM DRM-602X", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, + {"PIONEER", "CD-ROM DRM-604X", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, + {"REGAL", "CDC-4X", NULL, BLIST_MAX5LUN | BLIST_SINGLELUN}, {"SGI", "RAID3", "*", BLIST_SPARSELUN}, {"SGI", "RAID5", "*", BLIST_SPARSELUN}, {"SGI", "TP9100", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, - {"SGI", "TP9300", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, - {"SGI", "TP9400", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, - {"SGI", "TP9500", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, - {"MYLEX", "DACARMRB", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, + {"SONY", "CD-ROM CDU-8001", NULL, BLIST_BORKEN}, + {"SONY", "TSL", NULL, BLIST_FORCELUN}, /* DDS3 & DDS4 autoloaders */ + {"SUN", "T300", "*", BLIST_SPARSELUN}, + {"SUN", "T4", "*", BLIST_SPARSELUN}, + {"TEXEL", "CD-ROM", "1.06", BLIST_BORKEN}, + {"TOSHIBA", "CDROM", NULL, BLIST_ISROM}, + {"TOSHIBA", "CD-ROM", NULL, BLIST_ISROM}, {"XYRATEX", "RS", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, + {"Zzyzx", "RocketStor 500S", NULL, BLIST_SPARSELUN}, + {"Zzyzx", "RocketStor 2000", NULL, BLIST_SPARSELUN}, { NULL, NULL, NULL, 0 }, }; --- linux-2.6.4-rc2/drivers/scsi/scsi_error.c 2004-02-03 20:42:37.000000000 -0800 +++ 25/drivers/scsi/scsi_error.c 2004-03-07 20:46:55.000000000 -0800 @@ -37,6 +37,8 @@ #define SENSE_TIMEOUT (10*HZ) #endif +#define START_UNIT_TIMEOUT (30*HZ) + /* * These should *probably* be handled by the host itself. * Since it is allowed to sleep, it probably should. @@ -282,6 +284,15 @@ static int scsi_check_sense(struct scsi_ (scmd->sense_buffer[13] == 0x01)) { return NEEDS_RETRY; } + /* + * if the device is not started, we need to wake + * the error handler to start the motor + */ + if (scmd->device->allow_restart && + (scmd->sense_buffer[12] == 0x04) && + (scmd->sense_buffer[13] == 0x02)) { + return FAILED; + } return SUCCESS; /* these three are not supported */ @@ -829,6 +840,105 @@ static int scsi_try_bus_device_reset(str } /** + * scsi_eh_try_stu - Send START_UNIT to device. + * @scmd: Scsi cmd to send START_UNIT + * + * Return value: + * 0 - Device is ready. 1 - Device NOT ready. + **/ +static int scsi_eh_try_stu(struct scsi_cmnd *scmd) +{ + static unsigned char stu_command[6] = {START_STOP, 0, 0, 0, 1, 0}; + int rtn; + + if (!scmd->device->allow_restart) + return 1; + + memcpy(scmd->cmnd, stu_command, sizeof(stu_command)); + + /* + * zero the sense buffer. the scsi spec mandates that any + * untransferred sense data should be interpreted as being zero. + */ + memset(scmd->sense_buffer, 0, sizeof(scmd->sense_buffer)); + + scmd->request_buffer = NULL; + scmd->request_bufflen = 0; + scmd->use_sg = 0; + scmd->cmd_len = COMMAND_SIZE(scmd->cmnd[0]); + scmd->underflow = 0; + scmd->sc_data_direction = DMA_NONE; + + rtn = scsi_send_eh_cmnd(scmd, START_UNIT_TIMEOUT); + + /* + * when we eventually call scsi_finish, we really wish to complete + * the original request, so let's restore the original data. (db) + */ + scsi_setup_cmd_retry(scmd); + + /* + * hey, we are done. let's look to see what happened. + */ + SCSI_LOG_ERROR_RECOVERY(3, printk("%s: scmd %p rtn %x\n", + __FUNCTION__, scmd, rtn)); + if (rtn == SUCCESS) + return 0; + return 1; +} + + /** + * scsi_eh_stu - send START_UNIT if needed + * @shost: scsi host being recovered. + * @eh_done_q: list_head for processed commands. + * + * Notes: + * If commands are failing due to not ready, initializing command required, + * try revalidating the device, which will end up sending a start unit. + **/ +static int scsi_eh_stu(struct Scsi_Host *shost, + struct list_head *work_q, + struct list_head *done_q) +{ + struct list_head *lh, *lh_sf; + struct scsi_cmnd *scmd, *stu_scmd; + struct scsi_device *sdev; + + shost_for_each_device(sdev, shost) { + stu_scmd = NULL; + list_for_each_entry(scmd, work_q, eh_entry) + if (scmd->device == sdev && SCSI_SENSE_VALID(scmd) && + scsi_check_sense(scmd) == FAILED ) { + stu_scmd = scmd; + break; + } + + if (!stu_scmd) + continue; + + SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Sending START_UNIT to sdev:" + " 0x%p\n", current->comm, sdev)); + + if (!scsi_eh_try_stu(stu_scmd)) { + if (!sdev->online || !scsi_eh_tur(stu_scmd)) { + list_for_each_safe(lh, lh_sf, work_q) { + scmd = list_entry(lh, struct scsi_cmnd, eh_entry); + if (scmd->device == sdev) + scsi_eh_finish_cmd(scmd, done_q); + } + } + } else { + SCSI_LOG_ERROR_RECOVERY(3, + printk("%s: START_UNIT failed to sdev:" + " 0x%p\n", current->comm, sdev)); + } + } + + return list_empty(work_q); +} + + +/** * scsi_eh_bus_device_reset - send bdr if needed * @shost: scsi host being recovered. * @eh_done_q: list_head for processed commands. @@ -1033,7 +1143,9 @@ static int scsi_eh_host_reset(struct lis if (rtn == SUCCESS) { list_for_each_safe(lh, lh_sf, work_q) { scmd = list_entry(lh, struct scsi_cmnd, eh_entry); - if (!scmd->device->online || !scsi_eh_tur(scmd)) + if (!scmd->device->online || + (!scsi_eh_try_stu(scmd) && !scsi_eh_tur(scmd)) || + !scsi_eh_tur(scmd)) scsi_eh_finish_cmd(scmd, done_q); } } else { @@ -1401,10 +1513,11 @@ static void scsi_eh_ready_devs(struct Sc struct list_head *work_q, struct list_head *done_q) { - if (!scsi_eh_bus_device_reset(shost, work_q, done_q)) - if (!scsi_eh_bus_reset(shost, work_q, done_q)) - if (!scsi_eh_host_reset(work_q, done_q)) - scsi_eh_offline_sdevs(work_q, done_q); + if (!scsi_eh_stu(shost, work_q, done_q)) + if (!scsi_eh_bus_device_reset(shost, work_q, done_q)) + if (!scsi_eh_bus_reset(shost, work_q, done_q)) + if (!scsi_eh_host_reset(work_q, done_q)) + scsi_eh_offline_sdevs(work_q, done_q); } /** --- linux-2.6.4-rc2/drivers/scsi/scsi_lib.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/drivers/scsi/scsi_lib.c 2004-03-07 20:46:55.000000000 -0800 @@ -917,6 +917,7 @@ static int scsi_init_io(struct scsi_cmnd req->current_nr_sectors); /* release the command and kill it */ + scsi_release_buffers(cmd); scsi_put_command(cmd); return BLKPREP_KILL; } --- linux-2.6.4-rc2/drivers/scsi/scsi_priv.h 2004-03-03 23:12:46.000000000 -0800 +++ 25/drivers/scsi/scsi_priv.h 2004-03-07 20:46:55.000000000 -0800 @@ -155,6 +155,7 @@ extern int scsi_sysfs_add_sdev(struct sc extern int scsi_sysfs_add_host(struct Scsi_Host *); extern int scsi_sysfs_register(void); extern void scsi_sysfs_unregister(void); +extern struct scsi_transport_template blank_transport_template; extern struct class sdev_class; extern struct bus_type scsi_bus_type; --- linux-2.6.4-rc2/drivers/scsi/scsi_scan.c 2004-02-03 20:42:37.000000000 -0800 +++ 25/drivers/scsi/scsi_scan.c 2004-03-07 20:46:55.000000000 -0800 @@ -35,6 +35,7 @@ #include #include #include +#include #include "scsi.h" #include "scsi_priv.h" @@ -192,7 +193,7 @@ static struct scsi_device *scsi_alloc_sd struct scsi_device *sdev, *device; unsigned long flags; - sdev = kmalloc(sizeof(*sdev), GFP_ATOMIC); + sdev = kmalloc(sizeof(*sdev) + shost->transportt->size, GFP_ATOMIC); if (!sdev) goto out; @@ -237,6 +238,11 @@ static struct scsi_device *scsi_alloc_sd goto out_free_queue; } + if (shost->transportt->setup) { + if (shost->transportt->setup(sdev)) + goto out_cleanup_slave; + } + if (get_device(&sdev->host->shost_gendev)) { device_initialize(&sdev->sdev_gendev); @@ -253,8 +259,15 @@ static struct scsi_device *scsi_alloc_sd snprintf(sdev->sdev_classdev.class_id, BUS_ID_SIZE, "%d:%d:%d:%d", sdev->host->host_no, sdev->channel, sdev->id, sdev->lun); + + class_device_initialize(&sdev->transport_classdev); + sdev->transport_classdev.dev = &sdev->sdev_gendev; + sdev->transport_classdev.class = sdev->host->transportt->class; + snprintf(sdev->transport_classdev.class_id, BUS_ID_SIZE, + "%d:%d:%d:%d", sdev->host->host_no, + sdev->channel, sdev->id, sdev->lun); } else - goto out_cleanup_slave; + goto out_cleanup_transport; /* * If there are any same target siblings, add this to the @@ -283,6 +296,9 @@ static struct scsi_device *scsi_alloc_sd spin_unlock_irqrestore(shost->host_lock, flags); return sdev; +out_cleanup_transport: + if (shost->transportt->cleanup) + shost->transportt->cleanup(sdev); out_cleanup_slave: if (shost->hostt->slave_destroy) shost->hostt->slave_destroy(sdev); @@ -744,6 +760,8 @@ static int scsi_probe_and_add_lun(struct } else { if (sdev->host->hostt->slave_destroy) sdev->host->hostt->slave_destroy(sdev); + if (sdev->host->transportt->cleanup) + sdev->host->transportt->cleanup(sdev); put_device(&sdev->sdev_gendev); } out: @@ -1300,5 +1318,7 @@ void scsi_free_host_dev(struct scsi_devi if (sdev->host->hostt->slave_destroy) sdev->host->hostt->slave_destroy(sdev); + if (sdev->host->transportt->cleanup) + sdev->host->transportt->cleanup(sdev); put_device(&sdev->sdev_gendev); } --- linux-2.6.4-rc2/drivers/scsi/scsi_sysfs.c 2004-02-03 20:42:37.000000000 -0800 +++ 25/drivers/scsi/scsi_sysfs.c 2004-03-07 20:46:55.000000000 -0800 @@ -13,6 +13,7 @@ #include #include +#include #include "scsi.h" #include "scsi_priv.h" @@ -58,21 +59,24 @@ static int scsi_scan(struct Scsi_Host *s * shost_show_function: macro to create an attr function that can be used to * show a non-bit field. */ -#define shost_show_function(field, format_string) \ +#define shost_show_function(name, field, format_string) \ static ssize_t \ -show_##field (struct class_device *class_dev, char *buf) \ +show_##name (struct class_device *class_dev, char *buf) \ { \ struct Scsi_Host *shost = class_to_shost(class_dev); \ - return snprintf (buf, 20, format_string, shost->field); \ + return snprintf (buf, 20, format_string, shost->field); \ } /* * shost_rd_attr: macro to create a function and attribute variable for a * read only field. */ -#define shost_rd_attr(field, format_string) \ - shost_show_function(field, format_string) \ -static CLASS_DEVICE_ATTR(field, S_IRUGO, show_##field, NULL) +#define shost_rd_attr2(name, field, format_string) \ + shost_show_function(name, field, format_string) \ +static CLASS_DEVICE_ATTR(name, S_IRUGO, show_##name, NULL) + +#define shost_rd_attr(field, format_string) \ +shost_rd_attr2(field, field, format_string) /* * Create the actual show/store functions and data structures. @@ -96,6 +100,7 @@ shost_rd_attr(host_busy, "%hu\n"); shost_rd_attr(cmd_per_lun, "%hd\n"); shost_rd_attr(sg_tablesize, "%hu\n"); shost_rd_attr(unchecked_isa_dma, "%d\n"); +shost_rd_attr2(proc_name, hostt->proc_name, "%s\n"); static struct class_device_attribute *scsi_sysfs_shost_attrs[] = { &class_device_attr_unique_id, @@ -103,6 +108,7 @@ static struct class_device_attribute *sc &class_device_attr_cmd_per_lun, &class_device_attr_sg_tablesize, &class_device_attr_unchecked_isa_dma, + &class_device_attr_proc_name, &class_device_attr_scan, NULL }; @@ -344,6 +350,7 @@ static int attr_add(struct device *dev, **/ int scsi_sysfs_add_sdev(struct scsi_device *sdev) { + struct class_device_attribute **attrs; int error = -EINVAL, i; if (sdev->sdev_state != SDEV_CREATED) @@ -363,6 +370,12 @@ int scsi_sysfs_add_sdev(struct scsi_devi goto clean_device; } + if (sdev->transport_classdev.class) { + error = class_device_add(&sdev->transport_classdev); + if (error) + goto clean_device2; + } + get_device(&sdev->sdev_gendev); if (sdev->host->hostt->sdev_attrs) { @@ -388,10 +401,24 @@ int scsi_sysfs_add_sdev(struct scsi_devi } } + if (sdev->transport_classdev.class) { + attrs = sdev->host->transportt->attrs; + for (i = 0; attrs[i]; i++) { + error = class_device_create_file(&sdev->transport_classdev, + attrs[i]); + if (error) { + scsi_remove_device(sdev); + goto out; + } + } + } + out: return error; -clean_device: + clean_device2: + class_device_del(&sdev->sdev_classdev); + clean_device: sdev->sdev_state = SDEV_CANCEL; device_del(&sdev->sdev_gendev); @@ -409,9 +436,12 @@ void scsi_remove_device(struct scsi_devi if (sdev->sdev_state == SDEV_RUNNING || sdev->sdev_state == SDEV_CANCEL) { sdev->sdev_state = SDEV_DEL; class_device_unregister(&sdev->sdev_classdev); + class_device_unregister(&sdev->transport_classdev); device_del(&sdev->sdev_gendev); if (sdev->host->hostt->slave_destroy) sdev->host->hostt->slave_destroy(sdev); + if (sdev->host->transportt->cleanup) + sdev->host->transportt->cleanup(sdev); put_device(&sdev->sdev_gendev); } } @@ -498,3 +528,7 @@ int scsi_sysfs_add_host(struct Scsi_Host return 0; } + +/* A blank transport template that is used in drivers that don't + * yet implement Transport Attributes */ +struct scsi_transport_template blank_transport_template = { 0, }; --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/drivers/scsi/scsi_transport_fc.c 2004-03-07 20:46:55.000000000 -0800 @@ -0,0 +1,104 @@ +/* + * FiberChannel transport specific attributes exported to sysfs. + * + * Copyright (c) 2003 Silicon Graphics, Inc. All rights reserved. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include + +static void transport_class_release(struct class_device *class_dev); + +struct class fc_transport_class = { + .name = "fc_transport", + .release = transport_class_release, +}; + +static __init int fc_transport_init(void) +{ + return class_register(&fc_transport_class); +} + +static void __exit fc_transport_exit(void) +{ + class_unregister(&fc_transport_class); +} + +static int fc_setup_transport_attrs(struct scsi_device *sdev) +{ + /* FIXME: Callback into the driver */ + fc_node_name(sdev) = -1; + fc_port_name(sdev) = -1; + fc_port_id(sdev) = -1; + + return 0; +} + +static void transport_class_release(struct class_device *class_dev) +{ + struct scsi_device *sdev = transport_class_to_sdev(class_dev); + put_device(&sdev->sdev_gendev); +} + +#define fc_transport_show_function(field, format_string, cast) \ +static ssize_t \ +show_fc_transport_##field (struct class_device *cdev, char *buf) \ +{ \ + struct scsi_device *sdev = transport_class_to_sdev(cdev); \ + struct fc_transport_attrs *tp; \ + tp = (struct fc_transport_attrs *)&sdev->transport_data; \ + return snprintf(buf, 20, format_string, cast tp->field); \ +} + +#define fc_transport_rd_attr(field, format_string) \ + fc_transport_show_function(field, format_string, ) \ +static CLASS_DEVICE_ATTR( field, S_IRUGO, show_fc_transport_##field, NULL) + +#define fc_transport_rd_attr_cast(field, format_string, cast) \ + fc_transport_show_function(field, format_string, (cast)) \ +static CLASS_DEVICE_ATTR( field, S_IRUGO, show_fc_transport_##field, NULL) + +/* the FiberChannel Tranport Attributes: */ +fc_transport_rd_attr_cast(node_name, "0x%llx\n", unsigned long long); +fc_transport_rd_attr_cast(port_name, "0x%llx\n", unsigned long long); +fc_transport_rd_attr(port_id, "0x%06x\n"); + +struct class_device_attribute *fc_transport_attrs[] = { + &class_device_attr_node_name, + &class_device_attr_port_name, + &class_device_attr_port_id, + NULL +}; + +struct scsi_transport_template fc_transport_template = { + .attrs = fc_transport_attrs, + .class = &fc_transport_class, + .setup = &fc_setup_transport_attrs, + .cleanup = NULL, + .size = sizeof(struct fc_transport_attrs) - sizeof(unsigned long), +}; +EXPORT_SYMBOL(fc_transport_template); + +MODULE_AUTHOR("Martin Hicks"); +MODULE_DESCRIPTION("FC Transport Attributes"); +MODULE_LICENSE("GPL"); + +module_init(fc_transport_init); +module_exit(fc_transport_exit); --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/drivers/scsi/scsi_transport_spi.c 2004-03-07 20:46:55.000000000 -0800 @@ -0,0 +1,97 @@ +/* + * Parallel SCSI (SPI) transport specific attributes exported to sysfs. + * + * Copyright (c) 2003 Silicon Graphics, Inc. All rights reserved. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include + +static void transport_class_release(struct class_device *class_dev); + +struct class spi_transport_class = { + .name = "spi_transport", + .release = transport_class_release, +}; + +static __init int spi_transport_init(void) +{ + return class_register(&spi_transport_class); +} + +static void __exit spi_transport_exit(void) +{ + class_unregister(&spi_transport_class); +} + +static int spi_setup_transport_attrs(struct scsi_device *sdev) +{ + /* FIXME: should callback into the driver to get these values */ + spi_period(sdev) = -1; + spi_offset(sdev) = -1; + + return 0; +} + +static void transport_class_release(struct class_device *class_dev) +{ + struct scsi_device *sdev = transport_class_to_sdev(class_dev); + put_device(&sdev->sdev_gendev); +} + +#define spi_transport_show_function(field, format_string) \ +static ssize_t \ +show_spi_transport_##field (struct class_device *cdev, char *buf) \ +{ \ + struct scsi_device *sdev = transport_class_to_sdev(cdev); \ + struct spi_transport_attrs *tp; \ + tp = (struct spi_transport_attrs *)&sdev->transport_data; \ + return snprintf(buf, 20, format_string, tp->field); \ +} + +#define spi_transport_rd_attr(field, format_string) \ + spi_transport_show_function(field, format_string) \ +static CLASS_DEVICE_ATTR( field, S_IRUGO, show_spi_transport_##field, NULL) + +/* The Parallel SCSI Tranport Attributes: */ +spi_transport_rd_attr(period, "%d\n"); +spi_transport_rd_attr(offset, "%d\n"); + +struct class_device_attribute *spi_transport_attrs[] = { + &class_device_attr_period, + &class_device_attr_offset, + NULL +}; + +struct scsi_transport_template spi_transport_template = { + .attrs = spi_transport_attrs, + .class = &spi_transport_class, + .setup = &spi_setup_transport_attrs, + .cleanup = NULL, + .size = sizeof(struct spi_transport_attrs) - sizeof(unsigned long), +}; +EXPORT_SYMBOL(spi_transport_template); + +MODULE_AUTHOR("Martin Hicks"); +MODULE_DESCRIPTION("SPI Transport Attributes"); +MODULE_LICENSE("GPL"); + +module_init(spi_transport_init); +module_exit(spi_transport_exit); --- linux-2.6.4-rc2/drivers/scsi/sd.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/drivers/scsi/sd.c 2004-03-07 20:46:55.000000000 -0800 @@ -19,6 +19,9 @@ * not being read in sd_open. Fix problem where removable media * could be ejected after sd_open. * - Douglas Gilbert cleanup for lk 2.5.x + * - Badari Pulavarty , Matthew Wilcox + * , Kurt Garloff : + * Support 32k/1M disks. * * Logging policy (needs CONFIG_SCSI_LOGGING defined): * - setting up transfer: SCSI_LOG_HLQUEUE levels 1 and 2 @@ -61,7 +64,7 @@ * Remaining dev_t-handling stuff */ #define SD_MAJORS 16 -#define SD_DISKS (SD_MAJORS << 4) +#define SD_DISKS 32768 /* anything between 256 and 262144 */ /* * Time out in seconds for disks and Magneto-opticals (which are slower). @@ -121,6 +124,20 @@ static struct scsi_driver sd_template = .init_command = sd_init_command, }; +/* Device no to disk mapping: + * + * major disc2 disc p1 + * |............|.............|....|....| <- dev_t + * 31 20 19 8 7 4 3 0 + * + * Inside a major, we have 16k disks, however mapped non- + * contiguously. The first 16 disks are for major0, the next + * ones with major1, ... Disk 256 is for major0 again, disk 272 + * for major1, ... + * As we stay compatible with our numbering scheme, we can reuse + * the well-know SCSI majors 8, 65--71, 136--143. + */ + static int sd_major(int major_idx) { switch (major_idx) { @@ -136,6 +153,14 @@ static int sd_major(int major_idx) } } +static unsigned int make_sd_dev(unsigned int sd_nr, unsigned int part) +{ + return (part & 0xf) | ((sd_nr & 0xf) << 4) | + (sd_major((sd_nr & 0xf0) >> 4) << 20) | (sd_nr & 0xfff00); +} + +/* reverse mapping dev -> (sd_nr, part) not currently needed */ + #define to_scsi_disk(obj) container_of(obj,struct scsi_disk,kobj); static inline struct scsi_disk *scsi_disk(struct gendisk *disk) @@ -1301,7 +1326,7 @@ static int sd_probe(struct device *dev) struct scsi_disk *sdkp; struct gendisk *gd; u32 index; - int error; + int error, devno; error = -ENODEV; if ((sdp->type != TYPE_DISK) && (sdp->type != TYPE_MOD)) @@ -1319,6 +1344,12 @@ static int sd_probe(struct device *dev) kobject_init(&sdkp->kobj); sdkp->kobj.ktype = &scsi_disk_kobj_type; + /* Note: We can accomodate 64 partitions, but the genhd code + * assumes partitions allocate consecutive minors, which they don't. + * So for now stay with max 16 partitions and leave two spare bits. + * Later, we may change the genhd code and the alloc_disk() call + * and the ->minors assignment here. KG, 2004-02-10 + */ gd = alloc_disk(16); if (!gd) goto out_free; @@ -1339,16 +1370,23 @@ static int sd_probe(struct device *dev) sdkp->index = index; sdkp->openers = 0; - gd->major = sd_major(index >> 4); - gd->first_minor = (index & 15) << 4; + devno = make_sd_dev(index, 0); + gd->major = MAJOR(devno); + gd->first_minor = MINOR(devno); gd->minors = 16; gd->fops = &sd_fops; - if (index >= 26) { + if (index < 26) { + sprintf(gd->disk_name, "sd%c", 'a' + index % 26); + } else if (index < (26*27)) { sprintf(gd->disk_name, "sd%c%c", - 'a' + index/26-1,'a' + index % 26); + 'a' + index / 26 - 1,'a' + index % 26); } else { - sprintf(gd->disk_name, "sd%c", 'a' + index % 26); + const unsigned int m1 = (index / 26 - 1) / 26 - 1; + const unsigned int m2 = (index / 26 - 1) % 26; + const unsigned int m3 = index % 26; + sprintf(gd->disk_name, "sd%c%c%c", + 'a' + m1, 'a' + m2, 'a' + m3); } strcpy(gd->devfs_name, sdp->devfs_name); --- linux-2.6.4-rc2/drivers/scsi/sr.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/drivers/scsi/sr.c 2004-03-07 20:48:00.000000000 -0800 @@ -566,14 +566,17 @@ static int sr_probe(struct device *dev) snprintf(disk->devfs_name, sizeof(disk->devfs_name), "%s/cd", sdev->devfs_name); disk->driverfs_dev = &sdev->sdev_gendev; - register_cdrom(&cd->cdi); set_capacity(disk, cd->capacity); disk->private_data = &cd->driver; disk->queue = sdev->request_queue; + cd->cdi.disk = disk; dev_set_drvdata(dev, cd); add_disk(disk); + if (register_cdrom(&cd->cdi)) + goto fail_put; + printk(KERN_DEBUG "Attached scsi CD-ROM %s at scsi%d, channel %d, id %d, lun %d\n", cd->cdi.name, sdev->host->host_no, sdev->channel, --- linux-2.6.4-rc2/drivers/scsi/st.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/drivers/scsi/st.c 2004-03-07 20:46:55.000000000 -0800 @@ -17,7 +17,7 @@ Last modified: 18-JAN-1998 Richard Gooch Devfs support */ -static char *verstr = "20040213"; +static char *verstr = "20040226"; #include @@ -121,7 +121,15 @@ static struct st_dev_parm { }; #endif -static char *st_formats[ST_NBR_MODES] ={"", "l", "m", "a"}; +/* Restrict the number of modes so that names for all are assigned */ +#if ST_NBR_MODES > 16 +#error "Maximum number of modes is 16" +#endif +/* Bit reversed order to get same names for same minors with all + mode counts */ +static char *st_formats[] = { + "", "r", "k", "s", "l", "t", "o", "u", + "m", "v", "p", "x", "a", "y", "q", "z"}; /* The default definitions have been moved to st_options.h */ @@ -3888,8 +3896,11 @@ static int st_probe(struct device *dev) dev_num); goto out_free_tape; } - snprintf(cdev->kobj.name, KOBJ_NAME_LEN, "%sm%d%s", disk->disk_name, - mode, j ? "n" : ""); + /* Make sure that the minor numbers corresponding to the four + first modes always get the same names */ + i = mode << (4 - ST_NBR_MODE_BITS); + snprintf(cdev->kobj.name, KOBJ_NAME_LEN, "%s%s%s", j ? "n" : "", + disk->disk_name, st_formats[i]); cdev->owner = THIS_MODULE; cdev->ops = &st_fops; @@ -3909,22 +3920,26 @@ static int st_probe(struct device *dev) } for (mode = 0; mode < ST_NBR_MODES; ++mode) { + /* Make sure that the minor numbers corresponding to the four + first modes always get the same names */ + i = mode << (4 - ST_NBR_MODE_BITS); /* Rewind entry */ - devfs_mk_cdev(MKDEV(SCSI_TAPE_MAJOR, dev_num + (mode << 5)), + devfs_mk_cdev(MKDEV(SCSI_TAPE_MAJOR, TAPE_MINOR(dev_num, mode, 0)), S_IFCHR | S_IRUGO | S_IWUGO, - "%s/mt%s", SDp->devfs_name, st_formats[mode]); + "%s/mt%s", SDp->devfs_name, st_formats[i]); /* No-rewind entry */ - devfs_mk_cdev(MKDEV(SCSI_TAPE_MAJOR, dev_num + (mode << 5) + 128), + devfs_mk_cdev(MKDEV(SCSI_TAPE_MAJOR, TAPE_MINOR(dev_num, mode, 1)), S_IFCHR | S_IRUGO | S_IWUGO, - "%s/mt%sn", SDp->devfs_name, st_formats[mode]); + "%s/mt%sn", SDp->devfs_name, st_formats[i]); } disk->number = devfs_register_tape(SDp->devfs_name); printk(KERN_WARNING "Attached scsi tape %s at scsi%d, channel %d, id %d, lun %d\n", tape_name(tpnt), SDp->host->host_no, SDp->channel, SDp->id, SDp->lun); - printk(KERN_WARNING "%s: try direct i/o: %s, max page reachable by HBA %lu\n", - tape_name(tpnt), tpnt->try_dio ? "yes" : "no", tpnt->max_pfn); + printk(KERN_WARNING "%s: try direct i/o: %s (alignment %d B), max page reachable by HBA %lu\n", + tape_name(tpnt), tpnt->try_dio ? "yes" : "no", + queue_dma_alignment(SDp->request_queue) + 1, tpnt->max_pfn); return 0; @@ -3977,8 +3992,9 @@ static int st_remove(struct device *dev) sysfs_remove_link(&tpnt->device->sdev_gendev.kobj, "tape"); for (mode = 0; mode < ST_NBR_MODES; ++mode) { - devfs_remove("%s/mt%s", SDp->devfs_name, st_formats[mode]); - devfs_remove("%s/mt%sn", SDp->devfs_name, st_formats[mode]); + j = mode << (4 - ST_NBR_MODE_BITS); + devfs_remove("%s/mt%s", SDp->devfs_name, st_formats[j]); + devfs_remove("%s/mt%sn", SDp->devfs_name, st_formats[j]); for (j=0; j < 2; j++) { class_simple_device_remove(MKDEV(SCSI_TAPE_MAJOR, TAPE_MINOR(i, mode, j))); --- linux-2.6.4-rc2/drivers/scsi/st.h 2004-01-09 00:04:32.000000000 -0800 +++ 25/drivers/scsi/st.h 2004-03-07 20:46:55.000000000 -0800 @@ -50,6 +50,8 @@ typedef struct { struct cdev *cdevs[2]; /* Auto-rewind and non-rewind devices */ } ST_mode; +/* Number of modes can be changed by changing ST_NBR_MODE_BITS. The maximum + number of modes is 16 (ST_NBR_MODE_BITS 4) */ #define ST_NBR_MODE_BITS 2 #define ST_NBR_MODES (1 << ST_NBR_MODE_BITS) #define ST_MODE_SHIFT (7 - ST_NBR_MODE_BITS) --- linux-2.6.4-rc2/drivers/scsi/sym53c8xx_2/sym_glue.c 2004-02-03 20:42:37.000000000 -0800 +++ 25/drivers/scsi/sym53c8xx_2/sym_glue.c 2004-03-07 20:46:59.000000000 -0800 @@ -212,17 +212,32 @@ static int __map_scsi_sg_data(struct pci return use_sg; } -static void __sync_scsi_data(struct pci_dev *pdev, struct scsi_cmnd *cmd) +static void __sync_scsi_data_for_cpu(struct pci_dev *pdev, struct scsi_cmnd *cmd) { int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction); switch(SYM_UCMD_PTR(cmd)->data_mapped) { case 2: - pci_dma_sync_sg(pdev, cmd->buffer, cmd->use_sg, dma_dir); + pci_dma_sync_sg_for_cpu(pdev, cmd->buffer, cmd->use_sg, dma_dir); break; case 1: - pci_dma_sync_single(pdev, SYM_UCMD_PTR(cmd)->data_mapping, - cmd->request_bufflen, dma_dir); + pci_dma_sync_single_for_cpu(pdev, SYM_UCMD_PTR(cmd)->data_mapping, + cmd->request_bufflen, dma_dir); + break; + } +} + +static void __sync_scsi_data_for_device(struct pci_dev *pdev, struct scsi_cmnd *cmd) +{ + int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction); + + switch(SYM_UCMD_PTR(cmd)->data_mapped) { + case 2: + pci_dma_sync_sg_for_device(pdev, cmd->buffer, cmd->use_sg, dma_dir); + break; + case 1: + pci_dma_sync_single_for_device(pdev, SYM_UCMD_PTR(cmd)->data_mapping, + cmd->request_bufflen, dma_dir); break; } } @@ -233,8 +248,10 @@ static void __sync_scsi_data(struct pci_ __map_scsi_single_data(np->s.device, cmd) #define map_scsi_sg_data(np, cmd) \ __map_scsi_sg_data(np->s.device, cmd) -#define sync_scsi_data(np, cmd) \ - __sync_scsi_data(np->s.device, cmd) +#define sync_scsi_data_for_cpu(np, cmd) \ + __sync_scsi_data_for_cpu(np->s.device, cmd) +#define sync_scsi_data_for_device(np, cmd) \ + __sync_scsi_data_for_device(np->s.device, cmd) /* * Complete a pending CAM CCB. @@ -394,10 +411,11 @@ void sym_sniff_inquiry(struct sym_hcb *n if (!cmd || cmd->use_sg) return; - sync_scsi_data(np, cmd); + sync_scsi_data_for_cpu(np, cmd); retv = __sym_sniff_inquiry(np, cmd->device->id, cmd->device->lun, (u_char *) cmd->request_buffer, cmd->request_bufflen - resid); + sync_scsi_data_for_device(np, cmd); if (retv < 0) return; else if (retv) --- linux-2.6.4-rc2/drivers/scsi/sym53c8xx_comm.h 2003-10-08 15:07:09.000000000 -0700 +++ 25/drivers/scsi/sym53c8xx_comm.h 2004-03-07 20:46:59.000000000 -0800 @@ -703,7 +703,8 @@ static m_addr_t __vtobus(m_bush_t bush, #define __unmap_scsi_data(dev, cmd) do {; } while (0) #define __map_scsi_single_data(dev, cmd) (__vtobus(dev,(cmd)->request_buffer)) #define __map_scsi_sg_data(dev, cmd) ((cmd)->use_sg) -#define __sync_scsi_data(dev, cmd) do {; } while (0) +#define __sync_scsi_data_for_cpu(dev, cmd) do {; } while (0) +#define __sync_scsi_data_for_device(dev, cmd) do {; } while (0) #define scsi_sg_dma_address(sc) vtobus((sc)->address) #define scsi_sg_dma_len(sc) ((sc)->length) @@ -767,18 +768,34 @@ static int __map_scsi_sg_data(struct dev return use_sg; } -static void __sync_scsi_data(struct device *dev, Scsi_Cmnd *cmd) +static void __sync_scsi_data_for_cpu(struct device *dev, Scsi_Cmnd *cmd) { enum dma_data_direction dma_dir = (enum dma_data_direction)scsi_to_pci_dma_dir(cmd->sc_data_direction); switch(cmd->__data_mapped) { case 2: - dma_sync_sg(dev, cmd->buffer, cmd->use_sg, dma_dir); + dma_sync_sg_for_cpu(dev, cmd->buffer, cmd->use_sg, dma_dir); break; case 1: - dma_sync_single(dev, cmd->__data_mapping, - cmd->request_bufflen, dma_dir); + dma_sync_single_for_cpu(dev, cmd->__data_mapping, + cmd->request_bufflen, dma_dir); + break; + } +} + +static void __sync_scsi_data_for_device(struct device *dev, Scsi_Cmnd *cmd) +{ + enum dma_data_direction dma_dir = + (enum dma_data_direction)scsi_to_pci_dma_dir(cmd->sc_data_direction); + + switch(cmd->__data_mapped) { + case 2: + dma_sync_sg_for_device(dev, cmd->buffer, cmd->use_sg, dma_dir); + break; + case 1: + dma_sync_single_for_device(dev, cmd->__data_mapping, + cmd->request_bufflen, dma_dir); break; } } @@ -791,7 +808,8 @@ static void __sync_scsi_data(struct devi #define unmap_scsi_data(np, cmd) __unmap_scsi_data(np->dev, cmd) #define map_scsi_single_data(np, cmd) __map_scsi_single_data(np->dev, cmd) #define map_scsi_sg_data(np, cmd) __map_scsi_sg_data(np->dev, cmd) -#define sync_scsi_data(np, cmd) __sync_scsi_data(np->dev, cmd) +#define sync_scsi_data_for_cpu(np, cmd) __sync_scsi_data_for_cpu(np->dev, cmd) +#define sync_scsi_data_for_device(np, cmd) __sync_scsi_data_for_device(np->dev, cmd) /*========================================================== ** --- linux-2.6.4-rc2/drivers/scsi/u14-34f.c 2003-07-27 12:14:40.000000000 -0700 +++ 25/drivers/scsi/u14-34f.c 2004-03-07 20:46:59.000000000 -0800 @@ -1184,17 +1184,17 @@ static void sync_dma(unsigned int i, uns pci_dir = scsi_to_pci_dma_dir(SCpnt->sc_data_direction); if (DEV2H(cpp->sense_addr)) - pci_dma_sync_single(HD(j)->pdev, DEV2H(cpp->sense_addr), + pci_dma_sync_single_for_cpu(HD(j)->pdev, DEV2H(cpp->sense_addr), DEV2H(cpp->sense_len), PCI_DMA_FROMDEVICE); if (SCpnt->use_sg) - pci_dma_sync_sg(HD(j)->pdev, SCpnt->request_buffer, + pci_dma_sync_sg_for_cpu(HD(j)->pdev, SCpnt->request_buffer, SCpnt->use_sg, pci_dir); if (!DEV2H(cpp->data_len)) pci_dir = PCI_DMA_BIDIRECTIONAL; if (DEV2H(cpp->data_address)) - pci_dma_sync_single(HD(j)->pdev, DEV2H(cpp->data_address), + pci_dma_sync_single_for_cpu(HD(j)->pdev, DEV2H(cpp->data_address), DEV2H(cpp->data_len), pci_dir); } --- linux-2.6.4-rc2/drivers/serial/8250.c 2004-01-09 00:04:32.000000000 -0800 +++ 25/drivers/serial/8250.c 2004-03-07 20:47:02.000000000 -0800 @@ -16,12 +16,8 @@ * * A note about mapbase / membase * - * mapbase is the physical address of the IO port. Currently, we don't - * support this very well, and it may well be dropped from this driver - * in future. As such, mapbase should be NULL. - * - * membase is an 'ioremapped' cookie. This is compatible with the old - * serial.c driver, and is currently the preferred form. + * mapbase is the physical address of the IO port. + * membase is an 'ioremapped' cookie. */ #include #include @@ -837,7 +833,7 @@ receive_chars(struct uart_8250_port *up, if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) { tty->flip.work.func((void *)tty); if (tty->flip.count >= TTY_FLIPBUF_SIZE) - return; // if TTY_DONT_FLIP is set + return; /* if TTY_DONT_FLIP is set */ } ch = serial_inp(up, UART_RX); *tty->flip.char_buf_ptr = ch; @@ -1198,12 +1194,21 @@ static void serial8250_break_ctl(struct spin_unlock_irqrestore(&up->port.lock, flags); } +#ifdef CONFIG_KGDB +static int kgdb_irq = -1; +#endif + static int serial8250_startup(struct uart_port *port) { struct uart_8250_port *up = (struct uart_8250_port *)port; unsigned long flags; int retval; +#ifdef CONFIG_KGDB + if (up->port.irq == kgdb_irq) + return -EBUSY; +#endif + up->capabilities = uart_config[up->port.type].flags; if (up->port.type == PORT_16C950) { @@ -1869,6 +1874,10 @@ static void __init serial8250_register_p for (i = 0; i < UART_NR; i++) { struct uart_8250_port *up = &serial8250_ports[i]; +#ifdef CONFIG_KGDB + if (up->port.irq == kgdb_irq) + up->port.kgdb = 1; +#endif up->port.line = i; up->port.ops = &serial8250_pops; init_timer(&up->timer); @@ -1976,6 +1985,8 @@ static int __init serial8250_console_set if (co->index >= UART_NR) co->index = 0; port = &serial8250_ports[co->index].port; + if (!port->ops) + return -ENODEV; /* * Temporary fix. @@ -2007,6 +2018,14 @@ static int __init serial8250_console_ini } console_initcall(serial8250_console_init); +static int __init serial8250_late_console_init(void) +{ + if (!(serial8250_console.flags & CON_ENABLED)) + register_console(&serial8250_console); + return 0; +} +late_initcall(serial8250_late_console_init); + #define SERIAL8250_CONSOLE &serial8250_console #else #define SERIAL8250_CONSOLE NULL @@ -2138,6 +2157,31 @@ void serial8250_resume_port(int line) uart_resume_port(&serial8250_reg, &serial8250_ports[line].port); } +#ifdef CONFIG_KGDB +/* + * Find all the ports using the given irq and shut them down. + * Result should be that the irq will be released. + */ +void shutdown_for_kgdb(struct async_struct * info) +{ + int irq = info->state->irq; + struct uart_8250_port *up; + int ttyS; + + kgdb_irq = irq; /* save for later init */ + for (ttyS = 0; ttyS < UART_NR; ttyS++){ + up = &serial8250_ports[ttyS]; + if (up->port.irq == irq && (irq_lists + irq)->head) { +#ifdef CONFIG_DEBUG_SPINLOCK /* ugly business... */ + if(up->port.lock.magic != SPINLOCK_MAGIC) + spin_lock_init(&up->port.lock); +#endif + serial8250_shutdown(&up->port); + } + } +} +#endif /* CONFIG_KGDB */ + static int __init serial8250_init(void) { int ret, i; --- linux-2.6.4-rc2/drivers/serial/8250_pci.c 2004-02-17 20:48:45.000000000 -0800 +++ 25/drivers/serial/8250_pci.c 2004-03-07 20:46:46.000000000 -0800 @@ -145,8 +145,10 @@ afavlab_setup(struct pci_dev *dev, struc bar = FL_GET_BASE(board->flags); if (idx < 4) bar += idx; - else + else { + bar = 4; offset += (idx - 4) * board->uart_offset; + } return setup_port(dev, req, bar, offset, board->reg_shift); } @@ -1772,7 +1774,7 @@ static struct pci_device_id serial_pci_t pbn_b0_4_115200 }, { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_2_115200 }, + pbn_b0_bt_2_921600 }, /* * Digitan DS560-558, from jimd@esoft.com @@ -1891,6 +1893,9 @@ static struct pci_device_id serial_pci_t { PCI_VENDOR_ID_AFAVLAB, PCI_DEVICE_ID_AFAVLAB_P028, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_bt_8_115200 }, + { PCI_VENDOR_ID_AFAVLAB, PCI_DEVICE_ID_AFAVLAB_P030, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_8_115200 }, { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DSERIAL, PCI_ANY_ID, PCI_ANY_ID, 0, 0, --- linux-2.6.4-rc2/drivers/serial/clps711x.c 2003-09-27 18:57:46.000000000 -0700 +++ 25/drivers/serial/clps711x.c 2004-03-07 20:46:46.000000000 -0800 @@ -49,19 +49,10 @@ #define UART_NR 2 -#ifndef CONFIG_SERIAL_CLPS711X_OLD_NAME #define SERIAL_CLPS711X_MAJOR 204 #define SERIAL_CLPS711X_MINOR 40 #define SERIAL_CLPS711X_NR UART_NR -#else -#warning The old names/device number for this driver if compatabity is needed -#define SERIAL_CLPS711X_MAJOR 204 -#define SERIAL_CLPS711X_MINOR 16 -#define SERIAL_CLPS711X_NR UART_NR - -#endif - /* * We use the relevant SYSCON register as a base address for these ports. */ --- linux-2.6.4-rc2/drivers/serial/Kconfig 2004-03-03 23:12:46.000000000 -0800 +++ 25/drivers/serial/Kconfig 2004-03-07 20:46:46.000000000 -0800 @@ -250,12 +250,6 @@ config SERIAL_CLPS711X_CONSOLE your boot loader (lilo or loadlin) about how to pass options to the kernel at boot time.) -config SERIAL_CLPS711X_OLD_NAME - bool "Use the old 2.4 names for CLPS711X serial port" - depends on SERIAL_CLPS711X=y - help - ::: To be written ::: - config SERIAL_DZ bool "DECstation DZ serial driver" depends on DECSTATION --- linux-2.6.4-rc2/drivers/serial/serial_core.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/drivers/serial/serial_core.c 2004-03-07 20:47:02.000000000 -0800 @@ -175,8 +175,6 @@ static int uart_startup(struct uart_stat uart_circ_clear(&info->xmit); } - port->mctrl = 0; - retval = port->ops->startup(port); if (retval == 0) { if (init_hw) { @@ -1874,9 +1872,6 @@ uart_set_options(struct uart_port *port, if (flow == 'r') termios.c_cflag |= CRTSCTS; - if (!port->ops) - return 0; /* "console=" on ia64 */ - port->ops->set_termios(port, &termios, NULL); co->cflag = termios.c_cflag; @@ -1990,6 +1985,11 @@ uart_configure_port(struct uart_driver * { unsigned int flags; +#ifdef CONFIG_KGDB + if (port->kgdb) + return; +#endif + /* * If there isn't a port here, don't do anything further. */ --- linux-2.6.4-rc2/drivers/usb/class/audio.c 2004-02-17 20:48:45.000000000 -0800 +++ 25/drivers/usb/class/audio.c 2004-03-07 20:46:56.000000000 -0800 @@ -3,7 +3,7 @@ /* * audio.c -- USB Audio Class driver * - * Copyright (C) 1999, 2000, 2001 + * Copyright (C) 1999, 2000, 2001, 2003, 2004 * Alan Cox (alan@lxorguk.ukuu.org.uk) * Thomas Sailer (sailer@ife.ee.ethz.ch) * @@ -101,6 +101,8 @@ * Fix SNDCTL_DSP_STEREO API violation * 2003-04-08: Oliver Neukum (oliver@neukum.name): * Setting a configuration is done by usbcore and must not be overridden + * 2004-02-27: Workaround for broken synch descriptors + * */ /* @@ -1542,12 +1544,13 @@ static int set_format_in(struct usb_audi alts->endpoint[1].desc.bmAttributes != 0x01 || alts->endpoint[1].desc.bSynchAddress != 0 || alts->endpoint[1].desc.bEndpointAddress != (alts->endpoint[0].desc.bSynchAddress & 0x7f)) { - printk(KERN_ERR "usbaudio: device %d interface %d altsetting %d invalid synch pipe\n", + printk(KERN_WARNING "usbaudio: device %d interface %d altsetting %d claims adaptive in " + "but has invalid synch pipe; treating as asynchronous in\n", dev->devnum, u->interface, fmt->altsetting); - return -1; + } else { + u->syncpipe = usb_sndisocpipe(dev, alts->endpoint[1].desc.bEndpointAddress & 0xf); + u->syncinterval = alts->endpoint[1].desc.bRefresh; } - u->syncpipe = usb_sndisocpipe(dev, alts->endpoint[1].desc.bEndpointAddress & 0xf); - u->syncinterval = alts->endpoint[1].desc.bRefresh; } if (d->srate < fmt->sratelo) d->srate = fmt->sratelo; @@ -1637,12 +1640,13 @@ static int set_format_out(struct usb_aud alts->endpoint[1].desc.bmAttributes != 0x01 || alts->endpoint[1].desc.bSynchAddress != 0 || alts->endpoint[1].desc.bEndpointAddress != (alts->endpoint[0].desc.bSynchAddress | 0x80)) { - printk(KERN_ERR "usbaudio: device %d interface %d altsetting %d invalid synch pipe\n", + printk(KERN_WARNING "usbaudio: device %d interface %d altsetting %d claims asynch out " + "but has invalid synch pipe; treating as adaptive out\n", dev->devnum, u->interface, fmt->altsetting); - return -1; + } else { + u->syncpipe = usb_rcvisocpipe(dev, alts->endpoint[1].desc.bEndpointAddress & 0xf); + u->syncinterval = alts->endpoint[1].desc.bRefresh; } - u->syncpipe = usb_rcvisocpipe(dev, alts->endpoint[1].desc.bEndpointAddress & 0xf); - u->syncinterval = alts->endpoint[1].desc.bRefresh; } if (d->srate < fmt->sratelo) d->srate = fmt->sratelo; --- linux-2.6.4-rc2/drivers/usb/core/config.c 2003-09-27 18:57:46.000000000 -0700 +++ 25/drivers/usb/core/config.c 2004-03-07 20:46:56.000000000 -0800 @@ -72,13 +72,10 @@ static int usb_parse_endpoint(struct usb return buffer - buffer0; } -static void usb_release_intf(struct device *dev) +static void usb_free_intf(struct usb_interface *intf) { - struct usb_interface *intf; int j; - intf = to_usb_interface(dev); - if (intf->altsetting) { for (j = 0; j < intf->num_altsetting; j++) { struct usb_host_interface *as = &intf->altsetting[j]; @@ -235,8 +232,6 @@ int usb_parse_configuration(struct usb_h return -ENOMEM; } memset(interface, 0, sizeof(struct usb_interface)); - interface->dev.release = usb_release_intf; - device_initialize(&interface->dev); } /* Go through the descriptors, checking their length and counting the @@ -374,7 +369,7 @@ void usb_destroy_configuration(struct us struct usb_interface *ifp = cf->interface[i]; if (ifp) - put_device(&ifp->dev); + usb_free_intf(ifp); } } kfree(dev->config); --- linux-2.6.4-rc2/drivers/usb/core/devio.c 2003-12-17 21:20:02.000000000 -0800 +++ 25/drivers/usb/core/devio.c 2004-03-07 20:46:56.000000000 -0800 @@ -430,19 +430,14 @@ static int findintfep(struct usb_device static int findintfif(struct usb_device *dev, unsigned int ifn) { - unsigned int i, j; - struct usb_interface *iface; - struct usb_host_interface *alts; + unsigned int i; if (ifn & ~0xff) return -EINVAL; for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) { - iface = dev->actconfig->interface[i]; - for (j = 0; j < iface->num_altsetting; j++) { - alts = &iface->altsetting[j]; - if (alts->desc.bInterfaceNumber == ifn) - return i; - } + if (dev->actconfig->interface[i]-> + altsetting[0].desc.bInterfaceNumber == ifn) + return i; } return -ENOENT; } @@ -688,9 +683,7 @@ static int proc_getdriver(struct dev_sta return -EFAULT; if ((ret = findintfif(ps->dev, gd.interface)) < 0) return ret; - interface = usb_ifnum_to_if(ps->dev, gd.interface); - if (!interface) - return -EINVAL; + interface = ps->dev->actconfig->interface[ret]; if (!interface->driver) return -ENODATA; strcpy(gd.driver, interface->driver->name); @@ -744,9 +737,7 @@ static int proc_setintf(struct dev_state return -EFAULT; if ((ret = findintfif(ps->dev, setintf.interface)) < 0) return ret; - interface = usb_ifnum_to_if(ps->dev, setintf.interface); - if (!interface) - return -EINVAL; + interface = ps->dev->actconfig->interface[ret]; if (interface->driver) { if ((ret = checkintf(ps, ret))) return ret; --- linux-2.6.4-rc2/drivers/usb/core/driverfs.c 2004-02-03 20:42:37.000000000 -0800 +++ 25/drivers/usb/core/driverfs.c 2004-03-07 20:46:56.000000000 -0800 @@ -166,13 +166,9 @@ void usb_create_driverfs_dev_files (stru static ssize_t \ show_##field (struct device *dev, char *buf) \ { \ - struct usb_interface *intf; \ - int alt; \ + struct usb_interface *intf = to_usb_interface (dev); \ \ - intf = to_usb_interface (dev); \ - alt = intf->act_altsetting; \ - \ - return sprintf (buf, format_string, intf->altsetting[alt].desc.field); \ + return sprintf (buf, format_string, intf->cur_altsetting->desc.field); \ } \ static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL); --- linux-2.6.4-rc2/drivers/usb/core/hcd-pci.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/drivers/usb/core/hcd-pci.c 2004-03-07 20:46:56.000000000 -0800 @@ -147,8 +147,12 @@ clean_2: hcd->driver = driver; hcd->description = driver->description; hcd->self.bus_name = pci_name(dev); +#ifdef CONFIG_PCI_NAMES + hcd->product_desc = dev->pretty_name; +#else if (hcd->product_desc == NULL) hcd->product_desc = "USB Host Controller"; +#endif hcd->self.controller = &dev->dev; if ((retval = hcd_buffer_create (hcd)) != 0) { --- linux-2.6.4-rc2/drivers/usb/core/hub.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/drivers/usb/core/hub.c 2004-03-07 20:46:56.000000000 -0800 @@ -560,7 +560,7 @@ static int hub_probe(struct usb_interfac struct usb_hub *hub; unsigned long flags; - desc = intf->altsetting + intf->act_altsetting; + desc = intf->cur_altsetting; dev = interface_to_usbdev(intf); /* Some hubs have a subclass of 1, which AFAICT according to the */ @@ -1344,15 +1344,15 @@ int usb_physical_reset_device(struct usb for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) { struct usb_interface *intf = dev->actconfig->interface[i]; - struct usb_interface_descriptor *as; + struct usb_interface_descriptor *desc; - as = &intf->altsetting[intf->act_altsetting].desc; - ret = usb_set_interface(dev, as->bInterfaceNumber, - as->bAlternateSetting); + desc = &intf->cur_altsetting->desc; + ret = usb_set_interface(dev, desc->bInterfaceNumber, + desc->bAlternateSetting); if (ret < 0) { err("failed to set active alternate setting " "for dev %s interface %d (error=%d)", - dev->devpath, i, ret); + dev->devpath, desc->bInterfaceNumber, ret); return ret; } } --- linux-2.6.4-rc2/drivers/usb/core/message.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/drivers/usb/core/message.c 2004-03-07 20:46:56.000000000 -0800 @@ -783,16 +783,22 @@ void usb_disable_endpoint(struct usb_dev */ void usb_disable_interface(struct usb_device *dev, struct usb_interface *intf) { - struct usb_host_interface *hintf = - &intf->altsetting[intf->act_altsetting]; + struct usb_host_interface *alt = intf->cur_altsetting; int i; - for (i = 0; i < hintf->desc.bNumEndpoints; ++i) { + for (i = 0; i < alt->desc.bNumEndpoints; ++i) { usb_disable_endpoint(dev, - hintf->endpoint[i].desc.bEndpointAddress); + alt->endpoint[i].desc.bEndpointAddress); } } +static void release_interface(struct device *dev) +{ + struct usb_interface *interface = to_usb_interface(dev); + + complete(interface->released); +} + /* * usb_disable_device - Disable all the endpoints for a USB device * @dev: the device whose endpoints are being disabled @@ -822,12 +828,16 @@ void usb_disable_device(struct usb_devic if (dev->actconfig) { for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) { struct usb_interface *interface; + struct completion intf_completion; /* remove this interface */ interface = dev->actconfig->interface[i]; dev_dbg (&dev->dev, "unregistering interface %s\n", interface->dev.bus_id); - device_del(&interface->dev); + init_completion (&intf_completion); + interface->released = &intf_completion; + device_unregister (&interface->dev); + wait_for_completion (&intf_completion); } dev->actconfig = 0; if (dev->state == USB_STATE_CONFIGURED) @@ -876,12 +886,11 @@ void usb_enable_endpoint(struct usb_devi void usb_enable_interface(struct usb_device *dev, struct usb_interface *intf) { - struct usb_host_interface *hintf = - &intf->altsetting[intf->act_altsetting]; + struct usb_host_interface *alt = intf->cur_altsetting; int i; - for (i = 0; i < hintf->desc.bNumEndpoints; ++i) - usb_enable_endpoint(dev, &hintf->endpoint[i].desc); + for (i = 0; i < alt->desc.bNumEndpoints; ++i) + usb_enable_endpoint(dev, &alt->endpoint[i].desc); } /** @@ -920,6 +929,7 @@ void usb_enable_interface(struct usb_dev int usb_set_interface(struct usb_device *dev, int interface, int alternate) { struct usb_interface *iface; + struct usb_host_interface *alt; int ret; int manual = 0; @@ -929,14 +939,15 @@ int usb_set_interface(struct usb_device return -EINVAL; } - if (alternate < 0 || alternate >= iface->num_altsetting) + alt = usb_altnum_to_altsetting(iface, alternate); + if (!alt) { + warn("selecting invalid altsetting %d", alternate); return -EINVAL; + } ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_INTERFACE, USB_RECIP_INTERFACE, - iface->altsetting[alternate] - .desc.bAlternateSetting, - interface, NULL, 0, HZ * 5); + alternate, interface, NULL, 0, HZ * 5); /* 9.4.10 says devices don't need this and are free to STALL the * request if the interface only has one alternate setting. @@ -957,7 +968,7 @@ int usb_set_interface(struct usb_device /* prevent submissions using previous endpoint settings */ usb_disable_interface(dev, iface); - iface->act_altsetting = alternate; + iface->cur_altsetting = alt; /* If the interface only has one altsetting and the device didn't * accept the request, we attempt to carry out the equivalent action @@ -965,13 +976,11 @@ int usb_set_interface(struct usb_device * new altsetting. */ if (manual) { - struct usb_host_interface *iface_as = - &iface->altsetting[alternate]; int i; - for (i = 0; i < iface_as->desc.bNumEndpoints; i++) { + for (i = 0; i < alt->desc.bNumEndpoints; i++) { unsigned int epaddr = - iface_as->endpoint[i].desc.bEndpointAddress; + alt->endpoint[i].desc.bEndpointAddress; unsigned int pipe = __create_pipe(dev, USB_ENDPOINT_NUMBER_MASK & epaddr) | (usb_endpoint_out(epaddr) ? USB_DIR_OUT : USB_DIR_IN); @@ -1045,8 +1054,19 @@ int usb_reset_configuration(struct usb_d /* re-init hc/hcd interface/endpoint state */ for (i = 0; i < config->desc.bNumInterfaces; i++) { struct usb_interface *intf = config->interface[i]; + struct usb_host_interface *alt; + + alt = usb_altnum_to_altsetting(intf, 0); - intf->act_altsetting = 0; + /* No altsetting 0? We'll assume the first altsetting. + * We could use a GetInterface call, but if a device is + * so non-compliant that it doesn't have altsetting 0 + * then I wouldn't trust its reply anyway. + */ + if (!alt) + alt = &intf->altsetting[0]; + + intf->cur_altsetting = alt; usb_enable_interface(dev, intf); } return 0; @@ -1135,25 +1155,34 @@ int usb_set_configuration(struct usb_dev */ for (i = 0; i < cp->desc.bNumInterfaces; ++i) { struct usb_interface *intf = cp->interface[i]; - struct usb_interface_descriptor *desc; + struct usb_host_interface *alt; - intf->act_altsetting = 0; - desc = &intf->altsetting [0].desc; - usb_enable_interface(dev, intf); + alt = usb_altnum_to_altsetting(intf, 0); + /* No altsetting 0? We'll assume the first altsetting. + * We could use a GetInterface call, but if a device is + * so non-compliant that it doesn't have altsetting 0 + * then I wouldn't trust its reply anyway. + */ + if (!alt) + alt = &intf->altsetting[0]; + + intf->cur_altsetting = alt; + usb_enable_interface(dev, intf); intf->dev.parent = &dev->dev; intf->dev.driver = NULL; intf->dev.bus = &usb_bus_type; intf->dev.dma_mask = dev->dev.dma_mask; + intf->dev.release = release_interface; sprintf (&intf->dev.bus_id[0], "%d-%s:%d.%d", dev->bus->busnum, dev->devpath, configuration, - desc->bInterfaceNumber); + alt->desc.bInterfaceNumber); dev_dbg (&dev->dev, "registering %s (config #%d, interface %d)\n", intf->dev.bus_id, configuration, - desc->bInterfaceNumber); - device_add (&intf->dev); + alt->desc.bInterfaceNumber); + device_register (&intf->dev); usb_create_driverfs_intf_files (intf); } } --- linux-2.6.4-rc2/drivers/usb/core/usb.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/drivers/usb/core/usb.c 2004-03-07 20:46:59.000000000 -0800 @@ -189,7 +189,7 @@ void usb_deregister(struct usb_driver *d } /** - * usb_ifnum_to_if - get the interface object with a given interface number (usbcore-internal) + * usb_ifnum_to_if - get the interface object with a given interface number * @dev: the device whose current configuration is considered * @ifnum: the desired interface * @@ -220,6 +220,33 @@ struct usb_interface *usb_ifnum_to_if(st } /** + * usb_altnum_to_altsetting - get the altsetting structure with a given + * alternate setting number. + * @intf: the interface containing the altsetting in question + * @altnum: the desired alternate setting number + * + * This searches the altsetting array of the specified interface for + * an entry with the correct bAlternateSetting value and returns a pointer + * to that entry, or null. + * + * Note that altsettings need not be stored sequentially by number, so + * it would be incorrect to assume that the first altsetting entry in + * the array corresponds to altsetting zero. This routine helps device + * drivers avoid such mistakes. + */ +struct usb_host_interface *usb_altnum_to_altsetting(struct usb_interface *intf, + unsigned int altnum) +{ + int i; + + for (i = 0; i < intf->num_altsetting; i++) { + if (intf->altsetting[i].desc.bAlternateSetting == altnum) + return &intf->altsetting[i]; + } + return NULL; +} + +/** * usb_epnum_to_ep_desc - get the endpoint object with a given endpoint number * @dev: the device whose current configuration+altsettings is considered * @epnum: the desired endpoint, masked with USB_DIR_IN as appropriate. @@ -247,7 +274,7 @@ usb_epnum_to_ep_desc(struct usb_device * /* only endpoints in current altsetting are active */ intf = config->interface[i]; - alt = intf->altsetting + intf->act_altsetting; + alt = intf->cur_altsetting; for (k = 0; k < alt->desc.bNumEndpoints; k++) if (epnum == alt->endpoint[k].desc.bEndpointAddress) @@ -421,7 +448,7 @@ usb_match_id(struct usb_interface *inter if (id == NULL) return NULL; - intf = &interface->altsetting [interface->act_altsetting]; + intf = interface->cur_altsetting; dev = interface_to_usbdev(interface); /* It is important to check that id->driver_info is nonzero, @@ -624,7 +651,7 @@ static int usb_hotplug (struct device *d scratch += length; if (usb_dev->descriptor.bDeviceClass == 0) { - int alt = intf->act_altsetting; + struct usb_host_interface *alt = intf->cur_altsetting; /* 2.4 only exposed interface zero. in 2.5, hotplug * agents are called for all interfaces, and can use @@ -633,9 +660,9 @@ static int usb_hotplug (struct device *d envp [i++] = scratch; length += snprintf (scratch, buffer_size - length, "INTERFACE=%d/%d/%d", - intf->altsetting[alt].desc.bInterfaceClass, - intf->altsetting[alt].desc.bInterfaceSubClass, - intf->altsetting[alt].desc.bInterfaceProtocol); + alt->desc.bInterfaceClass, + alt->desc.bInterfaceSubClass, + alt->desc.bInterfaceProtocol); if ((buffer_size - length <= 0) || (i >= num_envp)) return -ENOMEM; ++length; @@ -1297,6 +1324,13 @@ struct urb *usb_buffer_map (struct urb * return urb; } +/* XXX DISABLED, no users currently. If you wish to re-enable this + * XXX please determine whether the sync is to transfer ownership of + * XXX the buffer from device to cpu or vice verse, and thusly use the + * XXX appropriate _for_{cpu,device}() method. -DaveM + */ +#if 0 + /** * usb_buffer_dmasync - synchronize DMA and CPU view of buffer(s) * @urb: urb whose transfer_buffer/setup_packet will be synchronized @@ -1325,6 +1359,7 @@ void usb_buffer_dmasync (struct urb *urb DMA_TO_DEVICE); } } +#endif /** * usb_buffer_unmap - free DMA mapping(s) for an urb @@ -1403,6 +1438,13 @@ int usb_buffer_map_sg (struct usb_device usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE); } +/* XXX DISABLED, no users currently. If you wish to re-enable this + * XXX please determine whether the sync is to transfer ownership of + * XXX the buffer from device to cpu or vice verse, and thusly use the + * XXX appropriate _for_{cpu,device}() method. -DaveM + */ +#if 0 + /** * usb_buffer_dmasync_sg - synchronize DMA and CPU view of scatterlist buffer(s) * @dev: device to which the scatterlist will be mapped @@ -1428,6 +1470,7 @@ void usb_buffer_dmasync_sg (struct usb_d dma_sync_sg (controller, sg, n_hw_ents, usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE); } +#endif /** * usb_buffer_unmap_sg - free DMA mapping(s) for a scatterlist @@ -1582,6 +1625,7 @@ EXPORT_SYMBOL(usb_driver_release_interfa EXPORT_SYMBOL(usb_match_id); EXPORT_SYMBOL(usb_find_interface); EXPORT_SYMBOL(usb_ifnum_to_if); +EXPORT_SYMBOL(usb_altnum_to_altsetting); EXPORT_SYMBOL(usb_reset_device); EXPORT_SYMBOL(usb_disconnect); @@ -1595,11 +1639,15 @@ EXPORT_SYMBOL (usb_buffer_alloc); EXPORT_SYMBOL (usb_buffer_free); EXPORT_SYMBOL (usb_buffer_map); +#if 0 EXPORT_SYMBOL (usb_buffer_dmasync); +#endif EXPORT_SYMBOL (usb_buffer_unmap); EXPORT_SYMBOL (usb_buffer_map_sg); +#if 0 EXPORT_SYMBOL (usb_buffer_dmasync_sg); +#endif EXPORT_SYMBOL (usb_buffer_unmap_sg); MODULE_LICENSE("GPL"); --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/drivers/usb/gadget/config.c 2004-03-07 20:46:56.000000000 -0800 @@ -0,0 +1,116 @@ +/* + * usb/gadget/config.c -- simplify building config descriptors + * + * Copyright (C) 2003 David Brownell + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +#include + + +/** + * usb_descriptor_fillbuf - fill buffer with descriptors + * @buf: Buffer to be filled + * @buflen: Size of buf + * @src: Array of descriptor pointers, terminated by null pointer. + * + * Copies descriptors into the buffer, returning the length or a + * negative error code if they can't all be copied. Useful when + * assembling descriptors for an associated set of interfaces used + * as part of configuring a composite device; or in other cases where + * sets of descriptors need to be marshaled. + */ +int +usb_descriptor_fillbuf(void *buf, unsigned buflen, + const struct usb_descriptor_header **src) +{ + u8 *dest = buf; + + if (!src) + return -EINVAL; + + /* fill buffer from src[] until null descriptor ptr */ + for (; 0 != *src; src++) { + unsigned len = (*src)->bLength; + + if (len > buflen); + return -EINVAL; + memcpy(dest, *src, len); + buflen -= len; + dest += len; + } + return dest - (u8 *)buf; +} + + +/** + * usb_gadget_config_buf - builts a complete configuration descriptor + * @config: Header for the descriptor, including characteristics such + * as power requirements and number of interfaces. + * @desc: Null-terminated vector of pointers to the descriptors (interface, + * endpoint, etc) defining all functions in this device configuration. + * @buf: Buffer for the resulting configuration descriptor. + * @length: Length of buffer. If this is not big enough to hold the + * entire configuration descriptor, an error code will be returned. + * + * This copies descriptors into the response buffer, building a descriptor + * for that configuration. It returns the buffer length or a negative + * status code. The config.wTotalLength field is set to match the length + * of the result, but other descriptor fields (including power usage and + * interface count) must be set by the caller. + * + * Gadget drivers could use this when constructing a config descriptor + * in response to USB_REQ_GET_DESCRIPTOR. They will need to patch the + * resulting bDescriptorType value if USB_DT_OTHER_SPEED_CONFIG is needed. + */ +int usb_gadget_config_buf( + const struct usb_config_descriptor *config, + void *buf, + unsigned length, + const struct usb_descriptor_header **desc +) +{ + struct usb_config_descriptor *cp = buf; + int len; + + /* config descriptor first */ + if (length < USB_DT_CONFIG_SIZE || !desc) + return -EINVAL; + *cp = *config; + + /* then interface/endpoint/class/vendor/... */ + len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8*)buf, + length - USB_DT_CONFIG_SIZE, desc); + if (len < 0) + return len; + len += USB_DT_CONFIG_SIZE; + if (len > 0xffff) + return -EINVAL; + + /* patch up the config descriptor */ + cp->bLength = USB_DT_CONFIG_SIZE; + cp->bDescriptorType = USB_DT_CONFIG; + cp->wTotalLength = cpu_to_le16(len); + cp->bmAttributes |= USB_CONFIG_ATT_ONE; + return len; +} + --- linux-2.6.4-rc2/drivers/usb/gadget/ether.c 2004-02-17 20:48:45.000000000 -0800 +++ 25/drivers/usb/gadget/ether.c 2004-03-07 20:46:56.000000000 -0800 @@ -607,6 +607,25 @@ fs_sink_desc = { .wMaxPacketSize = __constant_cpu_to_le16 (64), }; +static const struct usb_descriptor_header *fs_function [] = { +#ifdef DEV_CONFIG_CDC + /* "cdc" mode descriptors */ + (struct usb_descriptor_header *) &control_intf, + (struct usb_descriptor_header *) &header_desc, + (struct usb_descriptor_header *) &union_desc, + (struct usb_descriptor_header *) ðer_desc, +#ifdef EP_STATUS_NUM + (struct usb_descriptor_header *) &fs_status_desc, +#endif + (struct usb_descriptor_header *) &data_nop_intf, +#endif /* DEV_CONFIG_CDC */ + /* minimalist core */ + (struct usb_descriptor_header *) &data_intf, + (struct usb_descriptor_header *) &fs_source_desc, + (struct usb_descriptor_header *) &fs_sink_desc, + 0, +}; + #ifdef HIGHSPEED /* @@ -660,6 +679,25 @@ dev_qualifier = { .bNumConfigurations = 1, }; +static const struct usb_descriptor_header *hs_function [] = { +#ifdef DEV_CONFIG_CDC + /* "cdc" mode descriptors */ + (struct usb_descriptor_header *) &control_intf, + (struct usb_descriptor_header *) &header_desc, + (struct usb_descriptor_header *) &union_desc, + (struct usb_descriptor_header *) ðer_desc, +#ifdef EP_STATUS_NUM + (struct usb_descriptor_header *) &hs_status_desc, +#endif + (struct usb_descriptor_header *) &data_nop_intf, +#endif /* DEV_CONFIG_CDC */ + /* minimalist core */ + (struct usb_descriptor_header *) &data_intf, + (struct usb_descriptor_header *) &hs_source_desc, + (struct usb_descriptor_header *) &hs_sink_desc, + 0, +}; + /* maxpacket and other transfer characteristics vary by speed. */ #define ep_desc(g,hs,fs) (((g)->speed==USB_SPEED_HIGH)?(hs):(fs)) @@ -704,86 +742,25 @@ static struct usb_gadget_strings stringt static int config_buf (enum usb_device_speed speed, u8 *buf, u8 type, unsigned index) { - const unsigned config_len = USB_DT_CONFIG_SIZE -#ifdef DEV_CONFIG_CDC - + 2 * USB_DT_INTERFACE_SIZE - + sizeof header_desc - + sizeof union_desc - + sizeof ether_desc -#ifdef EP_STATUS_NUM - + USB_DT_ENDPOINT_SIZE -#endif -#endif /* DEV_CONFIG_CDC */ - + USB_DT_INTERFACE_SIZE - + 2 * USB_DT_ENDPOINT_SIZE; - + int len; + const struct usb_descriptor_header **function = fs_function; #ifdef HIGHSPEED - int hs; -#endif - /* a single configuration must always be index 0 */ - if (index > 0) - return -EINVAL; - if (config_len > USB_BUFSIZ) - return -EDOM; + int hs = (speed == USB_SPEED_HIGH); - /* config (or other speed config) */ - memcpy (buf, ð_config, USB_DT_CONFIG_SIZE); - buf [1] = type; - ((struct usb_config_descriptor *) buf)->wTotalLength - = __constant_cpu_to_le16 (config_len); - buf += USB_DT_CONFIG_SIZE; -#ifdef HIGHSPEED - hs = (speed == USB_SPEED_HIGH); if (type == USB_DT_OTHER_SPEED_CONFIG) hs = !hs; -#endif - -#ifdef DEV_CONFIG_CDC - /* control interface, class descriptors, optional status endpoint */ - memcpy (buf, &control_intf, USB_DT_INTERFACE_SIZE); - buf += USB_DT_INTERFACE_SIZE; - - memcpy (buf, &header_desc, sizeof header_desc); - buf += sizeof header_desc; - memcpy (buf, &union_desc, sizeof union_desc); - buf += sizeof union_desc; - memcpy (buf, ðer_desc, sizeof ether_desc); - buf += sizeof ether_desc; - -#ifdef EP_STATUS_NUM -#ifdef HIGHSPEED if (hs) - memcpy (buf, &hs_status_desc, USB_DT_ENDPOINT_SIZE); - else -#endif /* HIGHSPEED */ - memcpy (buf, &fs_status_desc, USB_DT_ENDPOINT_SIZE); - buf += USB_DT_ENDPOINT_SIZE; -#endif /* EP_STATUS_NUM */ - - /* default data altsetting has no endpoints */ - memcpy (buf, &data_nop_intf, USB_DT_INTERFACE_SIZE); - buf += USB_DT_INTERFACE_SIZE; -#endif /* DEV_CONFIG_CDC */ - - /* the "real" data interface has two endpoints */ - memcpy (buf, &data_intf, USB_DT_INTERFACE_SIZE); - buf += USB_DT_INTERFACE_SIZE; -#ifdef HIGHSPEED - if (hs) { - memcpy (buf, &hs_source_desc, USB_DT_ENDPOINT_SIZE); - buf += USB_DT_ENDPOINT_SIZE; - memcpy (buf, &hs_sink_desc, USB_DT_ENDPOINT_SIZE); - buf += USB_DT_ENDPOINT_SIZE; - } else + function = hs_function; #endif - { - memcpy (buf, &fs_source_desc, USB_DT_ENDPOINT_SIZE); - buf += USB_DT_ENDPOINT_SIZE; - memcpy (buf, &fs_sink_desc, USB_DT_ENDPOINT_SIZE); - buf += USB_DT_ENDPOINT_SIZE; - } - return config_len; + /* a single configuration must always be index 0 */ + if (index > 0) + return -EINVAL; + len = usb_gadget_config_buf (ð_config, buf, USB_BUFSIZ, function); + if (len < 0) + return len; + ((struct usb_config_descriptor *) buf)->bDescriptorType = type; + return len; } /*-------------------------------------------------------------------------*/ --- linux-2.6.4-rc2/drivers/usb/gadget/inode.c 2004-02-03 20:42:37.000000000 -0800 +++ 25/drivers/usb/gadget/inode.c 2004-03-07 20:46:46.000000000 -0800 @@ -1812,7 +1812,6 @@ gadgetfs_fill_super (struct super_block return -ENOMEM; inode->i_op = &simple_dir_inode_operations; if (!(d = d_alloc_root (inode))) { -enomem: iput (inode); return -ENOMEM; } @@ -1823,12 +1822,15 @@ enomem: */ dev = dev_new (); if (!dev) - goto enomem; + return -ENOMEM; + dev->sb = sb; if (!(inode = gadgetfs_create_file (sb, CHIP, dev, &dev_init_operations, - &dev->dentry))) - goto enomem; + &dev->dentry))) { + put_dev(dev); + return -ENOMEM; + } /* other endpoint files are available after hardware setup, * from binding to a controller. @@ -1849,8 +1851,10 @@ static void gadgetfs_kill_sb (struct super_block *sb) { kill_litter_super (sb); - put_dev (the_device); - the_device = 0; + if (the_device) { + put_dev (the_device); + the_device = 0; + } } /*----------------------------------------------------------------------*/ --- linux-2.6.4-rc2/drivers/usb/gadget/Makefile 2004-02-03 20:42:37.000000000 -0800 +++ 25/drivers/usb/gadget/Makefile 2004-03-07 20:46:56.000000000 -0800 @@ -9,7 +9,7 @@ obj-$(CONFIG_USB_GOKU) += goku_udc.o # USB gadget drivers # g_zero-objs := zero.o usbstring.o -g_ether-objs := ether.o usbstring.o +g_ether-objs := ether.o usbstring.o config.o g_serial-objs := serial.o usbstring.o gadgetfs-objs := inode.o usbstring.o g_file_storage-objs := file_storage.o usbstring.o --- linux-2.6.4-rc2/drivers/usb/gadget/usbstring.c 2003-06-14 12:18:25.000000000 -0700 +++ 25/drivers/usb/gadget/usbstring.c 2004-03-07 20:46:56.000000000 -0800 @@ -16,24 +16,89 @@ #include #include +#include + + +static int utf8_to_utf16le(const char *s, u16 *cp, unsigned len) +{ + int count = 0; + u8 c; + u16 uchar; + + /* this insists on correct encodings, though not minimal ones. + * BUT it currently rejects legit 4-byte UTF-8 code points, + * which need surrogate pairs. (Unicode 3.1 can use them.) + */ + while (len != 0 && (c = (u8) *s++) != 0) { + if (unlikely(c & 0x80)) { + // 2-byte sequence: + // 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx + if ((c & 0xe0) == 0xc0) { + uchar = (c & 0x1f) << 6; + + c = (u8) *s++; + if ((c & 0xc0) != 0xc0) + goto fail; + c &= 0x3f; + uchar |= c; + + // 3-byte sequence (most CJKV characters): + // zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx + } else if ((c & 0xf0) == 0xe0) { + uchar = (c & 0x0f) << 12; + + c = (u8) *s++; + if ((c & 0xc0) != 0xc0) + goto fail; + c &= 0x3f; + uchar |= c << 6; + + c = (u8) *s++; + if ((c & 0xc0) != 0xc0) + goto fail; + c &= 0x3f; + uchar |= c; + + /* no bogus surrogates */ + if (0xd800 <= uchar && uchar <= 0xdfff) + goto fail; + + // 4-byte sequence (surrogate pairs, currently rare): + // 11101110wwwwzzzzyy + 110111yyyyxxxxxx + // = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx + // (uuuuu = wwww + 1) + // FIXME accept the surrogate code points (only) + + } else + goto fail; + } else + uchar = c; + put_unaligned (cpu_to_le16 (uchar), cp++); + count++; + len--; + } + return count; +fail: + return -1; +} + /** * usb_gadget_get_string - fill out a string descriptor - * @table: of c strings using iso latin/1 characters + * @table: of c strings encoded using UTF-8 * @id: string id, from low byte of wValue in get string descriptor * @buf: at least 256 bytes * - * Finds the iso latin/1 string matching the ID, and converts it into a + * Finds the UTF-8 string matching the ID, and converts it into a * string descriptor in utf16-le. * Returns length of descriptor (always even) or negative errno * - * If your driver needs stings in multiple languages, you'll need to - * to use some alternate solution for languages where the ISO 8859/1 - * (latin/1) character set can't be used. For example, they can't be - * used with Chinese (Big5, GB2312, etc), Japanese, Korean, or many other - * languages. You'd likely "switch (wIndex) { ... }" in your ep0 - * string descriptor logic, using this routine in cases where "western - * european" characters suffice for the strings being returned. + * If your driver needs stings in multiple languages, you'll probably + * "switch (wIndex) { ... }" in your ep0 string descriptor logic, + * using this routine after choosing which set of UTF-8 strings to use. + * Note that US-ASCII is a strict subset of UTF-8; any string bytes with + * the eighth bit set will be multibyte UTF-8 characters, not ISO-8859/1 + * characters (which are also widely used in C strings). */ int usb_gadget_get_string (struct usb_gadget_strings *table, int id, u8 *buf) @@ -59,13 +124,12 @@ usb_gadget_get_string (struct usb_gadget /* string descriptors have length, tag, then UTF16-LE text */ len = min ((size_t) 126, strlen (s->s)); + memset (buf + 2, 0, 2 * len); /* zero all the bytes */ + len = utf8_to_utf16le(s->s, (u16 *)&buf[2], len); + if (len < 0) + return -EINVAL; buf [0] = (len + 1) * 2; buf [1] = USB_DT_STRING; - memset (buf + 2, 0, 2 * len); /* zero all the high bytes */ - while (len) { - buf [2 * len] = s->s [len - 1]; - len--; - } return buf [0]; } --- linux-2.6.4-rc2/drivers/usb/host/ehci-dbg.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/drivers/usb/host/ehci-dbg.c 2004-03-07 20:46:56.000000000 -0800 @@ -579,7 +579,11 @@ show_periodic (struct class_device *clas break; case Q_TYPE_SITD: temp = scnprintf (next, size, - " sitd/%p", p.sitd); + " sitd%d-%04x/%p", + p.sitd->stream->interval, + le32_to_cpup (&p.sitd->hw_uframe) + & 0x0000ffff, + p.sitd); tag = Q_NEXT_TYPE (p.sitd->hw_next); p = p.sitd->sitd_next; break; --- linux-2.6.4-rc2/drivers/usb/host/ehci.h 2004-03-03 23:12:46.000000000 -0800 +++ 25/drivers/usb/host/ehci.h 2004-03-07 20:46:56.000000000 -0800 @@ -492,16 +492,16 @@ struct ehci_itd { /* * EHCI Specification 0.95 Section 3.4 * siTD, aka split-transaction isochronous Transfer Descriptor - * ... describe low/full speed iso xfers through TT in hubs + * ... describe full speed iso xfers through TT in hubs * see Figure 3-5 "Split-transaction Isochronous Transaction Descriptor (siTD) */ struct ehci_sitd { /* first part defined by EHCI spec */ u32 hw_next; /* uses bit field macros above - see EHCI 0.95 Table 3-8 */ - u32 hw_fullspeed_ep; /* see EHCI table 3-9 */ - u32 hw_uframe; /* see EHCI table 3-10 */ - u32 hw_results; /* see EHCI table 3-11 */ + u32 hw_fullspeed_ep; /* EHCI table 3-9 */ + u32 hw_uframe; /* EHCI table 3-10 */ + u32 hw_results; /* EHCI table 3-11 */ #define SITD_IOC (1 << 31) /* interrupt on completion */ #define SITD_PAGE (1 << 30) /* buffer 0/1 */ #define SITD_LENGTH(x) (0x3ff & ((x)>>16)) @@ -515,8 +515,8 @@ struct ehci_sitd { #define SITD_ACTIVE __constant_cpu_to_le32(SITD_STS_ACTIVE) - u32 hw_buf [2]; /* see EHCI table 3-12 */ - u32 hw_backpointer; /* see EHCI table 3-13 */ + u32 hw_buf [2]; /* EHCI table 3-12 */ + u32 hw_backpointer; /* EHCI table 3-13 */ u32 hw_buf_hi [2]; /* Appendix B */ /* the rest is HCD-private */ @@ -552,8 +552,6 @@ struct ehci_fstn { /*-------------------------------------------------------------------------*/ -#define SUBMIT_URB(urb,mem_flags) usb_submit_urb(urb,mem_flags) - #ifndef DEBUG #define STUB_DEBUG_FILES #endif /* DEBUG */ --- linux-2.6.4-rc2/drivers/usb/host/ehci-hcd.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/drivers/usb/host/ehci-hcd.c 2004-03-07 20:46:56.000000000 -0800 @@ -106,8 +106,6 @@ static const char hcd_name [] = "ehci_hc #undef EHCI_VERBOSE_DEBUG #undef EHCI_URB_TRACE -// #define have_split_iso - #ifdef DEBUG #define EHCI_STATS #endif @@ -676,6 +674,7 @@ static void ehci_work (struct ehci_hcd * /* the IO watchdog guards against hardware or driver bugs that * misplace IRQs, and should let us run completely without IRQs. + * such lossage has been observed on both VT6202 and VT8235. */ if ((ehci->async->qh_next.ptr != 0) || (ehci->periodic_sched != 0)) timer_action (ehci, TIMER_IO_WATCHDOG); @@ -796,13 +795,8 @@ static int ehci_urb_enqueue ( case PIPE_ISOCHRONOUS: if (urb->dev->speed == USB_SPEED_HIGH) return itd_submit (ehci, urb, mem_flags); -#ifdef have_split_iso else return sitd_submit (ehci, urb, mem_flags); -#else - dbg ("no split iso support yet"); - return -ENOSYS; -#endif /* have_split_iso */ } } --- linux-2.6.4-rc2/drivers/usb/host/ehci-sched.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/drivers/usb/host/ehci-sched.c 2004-03-07 20:46:56.000000000 -0800 @@ -53,14 +53,10 @@ periodic_next_shadow (union ehci_shadow return &periodic->fstn->fstn_next; case Q_TYPE_ITD: return &periodic->itd->itd_next; -#ifdef have_split_iso - case Q_TYPE_SITD: + // case Q_TYPE_SITD: + default: return &periodic->sitd->sitd_next; -#endif /* have_split_iso */ } - dbg ("BAD shadow %p tag %d", periodic->ptr, tag); - // BUG (); - return 0; } /* returns true after successful unlink */ @@ -133,7 +129,6 @@ periodic_usecs (struct ehci_hcd *ehci, u hw_p = &q->itd->hw_next; q = &q->itd->itd_next; break; -#ifdef have_split_iso case Q_TYPE_SITD: /* is it in the S-mask? (count SPLIT, DATA) */ if (q->sitd->hw_uframe & cpu_to_le32 (1 << uframe)) { @@ -154,7 +149,6 @@ periodic_usecs (struct ehci_hcd *ehci, u hw_p = &q->sitd->hw_next; q = &q->sitd->sitd_next; break; -#endif /* have_split_iso */ default: BUG (); } @@ -229,7 +223,8 @@ static int tt_no_collision ( if (same_tt (dev, here.itd->urb->dev)) { u16 mask; - mask = le32_to_cpu (here.sitd->hw_uframe); + mask = le32_to_cpu (here.sitd + ->hw_uframe); /* FIXME assumes no gap for IN! */ mask |= mask >> 8; if (mask & uf_mask) @@ -237,7 +232,7 @@ static int tt_no_collision ( } type = Q_NEXT_TYPE (here.qh->hw_next); here = here.sitd->sitd_next; - break; + continue; // case Q_TYPE_FSTN: default: ehci_dbg (ehci, @@ -698,12 +693,27 @@ iso_stream_put(struct ehci_hcd *ehci, st // BUG_ON (!list_empty(&stream->td_list)); while (!list_empty (&stream->free_list)) { - struct ehci_itd *itd; + struct list_head *entry; - itd = list_entry (stream->free_list.next, - struct ehci_itd, itd_list); - list_del (&itd->itd_list); - dma_pool_free (ehci->itd_pool, itd, itd->itd_dma); + entry = stream->free_list.next; + list_del (entry); + + /* knows about ITD vs SITD */ + if (stream->highspeed) { + struct ehci_itd *itd; + + itd = list_entry (entry, struct ehci_itd, + itd_list); + dma_pool_free (ehci->itd_pool, itd, + itd->itd_dma); + } else { + struct ehci_sitd *sitd; + + sitd = list_entry (entry, struct ehci_sitd, + sitd_list); + dma_pool_free (ehci->sitd_pool, sitd, + sitd->sitd_dma); + } } is_in = (stream->bEndpointAddress & USB_DIR_IN) ? 0x10 : 0; @@ -858,6 +868,7 @@ itd_urb_transaction ( int i; unsigned num_itds; struct ehci_iso_sched *sched; + unsigned long flags; sched = iso_sched_alloc (urb->number_of_packets, mem_flags); if (unlikely (sched == 0)) @@ -871,6 +882,7 @@ itd_urb_transaction ( num_itds = urb->number_of_packets; /* allocate/init ITDs */ + spin_lock_irqsave (&ehci->lock, flags); for (i = 0; i < num_itds; i++) { /* free_list.next might be cache-hot ... but maybe @@ -884,8 +896,14 @@ itd_urb_transaction ( list_del (&itd->itd_list); itd_dma = itd->itd_dma; } else + itd = 0; + + if (!itd) { + spin_unlock_irqrestore (&ehci->lock, flags); itd = dma_pool_alloc (ehci->itd_pool, mem_flags, &itd_dma); + spin_lock_irqsave (&ehci->lock, flags); + } if (unlikely (0 == itd)) { iso_sched_free (stream, sched); @@ -895,6 +913,7 @@ itd_urb_transaction ( itd->itd_dma = itd_dma; list_add (&itd->itd_list, &sched->td_list); } + spin_unlock_irqrestore (&ehci->lock, flags); /* temporarily store schedule info in hcpriv */ urb->hcpriv = sched; @@ -909,11 +928,11 @@ itd_slot_ok ( struct ehci_hcd *ehci, u32 mod, u32 uframe, - u32 end, u8 usecs, u32 period ) { + uframe %= period; do { /* can't commit more than 80% periodic == 100 usec */ if (periodic_usecs (ehci, uframe >> 3, uframe & 0x7) @@ -922,8 +941,7 @@ itd_slot_ok ( /* we know urb->interval is 2^N uframes */ uframe += period; - uframe %= mod; - } while (uframe != end); + } while (uframe < mod); return 1; } @@ -933,7 +951,6 @@ sitd_slot_ok ( u32 mod, struct ehci_iso_stream *stream, u32 uframe, - u32 end, struct ehci_iso_sched *sched, u32 period_uframes ) @@ -952,12 +969,20 @@ sitd_slot_ok ( */ /* check bandwidth */ + uframe %= period_uframes; do { u32 max_used; frame = uframe >> 3; uf = uframe & 7; + /* tt must be idle for start(s), any gap, and csplit. + * assume scheduling slop leaves 10+% for control/bulk. + */ + if (!tt_no_collision (ehci, period_uframes << 3, + stream->udev, frame, mask)) + return 0; + /* check starts (OUT uses more than one) */ max_used = 100 - stream->usecs; for (tmp = stream->raw_mask & 0xff; tmp; tmp >>= 1, uf++) { @@ -969,25 +994,19 @@ sitd_slot_ok ( if (stream->c_usecs) { max_used = 100 - stream->c_usecs; do { - /* tt is busy in the gap before CSPLIT */ tmp = 1 << uf; - mask |= tmp; tmp <<= 8; - if (stream->raw_mask & tmp) - break; + if ((stream->raw_mask & tmp) == 0) + continue; + if (periodic_usecs (ehci, frame, uf) + > max_used) + return 0; } while (++uf < 8); - if (periodic_usecs (ehci, frame, uf) > max_used) - return 0; } /* we know urb->interval is 2^N uframes */ uframe += period_uframes; - uframe %= mod; - } while (uframe != end); - - /* tt must be idle for start(s), any gap, and csplit */ - if (!tt_no_collision (ehci, period_uframes, stream->udev, frame, mask)) - return 0; + } while (uframe < mod); stream->splits = stream->raw_mask << (uframe & 7); cpu_to_le32s (&stream->splits); @@ -1014,7 +1033,7 @@ iso_stream_schedule ( struct ehci_iso_stream *stream ) { - u32 now, start, end, max, period; + u32 now, start, max, period; int status; unsigned mod = ehci->periodic_size << 3; struct ehci_iso_sched *sched = urb->hcpriv; @@ -1036,8 +1055,6 @@ iso_stream_schedule ( /* when's the last uframe this urb could start? */ max = now + mod; - max -= sched->span; - max -= 8 * SCHEDULE_SLOP; /* typical case: reuse current schedule. stream is still active, * and no gaps from host falling behind (irq delays etc) @@ -1046,9 +1063,11 @@ iso_stream_schedule ( start = stream->next_uframe; if (start < now) start += mod; - if (likely (start < max)) + if (likely ((start + sched->span) < max)) goto ready; - /* else fell behind; try to reschedule */ + /* else fell behind; someday, try to reschedule */ + status = -EL2NSYNC; + goto fail; } /* need to schedule; when's the next (u)frame we could start? @@ -1059,63 +1078,40 @@ iso_stream_schedule ( */ start = SCHEDULE_SLOP * 8 + (now & ~0x07); start %= mod; - end = start; + stream->next_uframe = start; /* NOTE: assumes URB_ISO_ASAP, to limit complexity/bugs */ period = urb->interval; if (!stream->highspeed) period <<= 3; - if (max > (start + period)) - max = start + period; - /* hack: account for itds already scheduled to this endpoint */ - if (list_empty (&stream->td_list)) - end = max; - - /* within [start..max] find a uframe slot with enough bandwidth */ - end %= mod; - do { + /* find a uframe slot with enough bandwidth */ + for (; start < (stream->next_uframe + period); start++) { int enough_space; /* check schedule: enough space? */ if (stream->highspeed) - enough_space = itd_slot_ok (ehci, mod, start, end, + enough_space = itd_slot_ok (ehci, mod, start, stream->usecs, period); else { if ((start % 8) >= 6) continue; enough_space = sitd_slot_ok (ehci, mod, stream, - start, end, sched, period); + start, sched, period); } - /* (re)schedule it here if there's enough bandwidth */ + /* schedule it here if there's enough bandwidth */ if (enough_space) { - start %= mod; - if (unlikely (!list_empty (&stream->td_list))) { - /* host fell behind ... maybe irq latencies - * delayed this request queue for too long. - */ - stream->rescheduled++; - dev_dbg (&urb->dev->dev, - "iso%d%s %d.%d skip %d.%d\n", - stream->bEndpointAddress & 0x0f, - (stream->bEndpointAddress & USB_DIR_IN) - ? "in" : "out", - stream->next_uframe >> 3, - stream->next_uframe & 0x7, - start >> 3, start & 0x7); - } - stream->next_uframe = start; + stream->next_uframe = start % mod; goto ready; } - - } while (++start < max); + } /* no room in the schedule */ - ehci_dbg (ehci, "iso %ssched full %p (now %d end %d max %d)\n", + ehci_dbg (ehci, "iso %ssched full %p (now %d max %d)\n", list_empty (&stream->td_list) ? "" : "re", - urb, now, end, max); + urb, now, max); status = -ENOSPC; fail: @@ -1260,6 +1256,7 @@ itd_link_urb ( iso_sched_free (stream, iso_sched); urb->hcpriv = 0; + timer_action (ehci, TIMER_IO_WATCHDOG); if (unlikely (!ehci->periodic_sched++)) return enable_periodic (ehci); return 0; @@ -1404,18 +1401,392 @@ done: return status; } -#ifdef have_split_iso +#ifdef CONFIG_USB_EHCI_SPLIT_ISO /*-------------------------------------------------------------------------*/ /* - * "Split ISO TDs" ... used for USB 1.1 devices going through - * the TTs in USB 2.0 hubs. - * - * FIXME not yet implemented + * "Split ISO TDs" ... used for USB 1.1 devices going through the + * TTs in USB 2.0 hubs. These need microframe scheduling. */ -#endif /* have_split_iso */ +static inline void +sitd_sched_init ( + struct ehci_iso_sched *iso_sched, + struct ehci_iso_stream *stream, + struct urb *urb +) +{ + unsigned i; + dma_addr_t dma = urb->transfer_dma; + + /* how many frames are needed for these transfers */ + iso_sched->span = urb->number_of_packets * stream->interval; + + /* figure out per-frame sitd fields that we'll need later + * when we fit new sitds into the schedule. + */ + for (i = 0; i < urb->number_of_packets; i++) { + struct ehci_iso_packet *packet = &iso_sched->packet [i]; + unsigned length; + dma_addr_t buf; + u32 trans; + + length = urb->iso_frame_desc [i].length & 0x03ff; + buf = dma + urb->iso_frame_desc [i].offset; + + trans = SITD_STS_ACTIVE; + if (((i + 1) == urb->number_of_packets) + && !(urb->transfer_flags & URB_NO_INTERRUPT)) + trans |= SITD_IOC; + trans |= length << 16; + packet->transaction = cpu_to_le32 (trans); + + /* might need to cross a buffer page within a td */ + packet->bufp = buf; + buf += length; + packet->buf1 = buf & ~0x0fff; + if (packet->buf1 != (buf & ~(u64)0x0fff)) + packet->cross = 1; + + /* OUT uses multiple start-splits */ + if (stream->bEndpointAddress & USB_DIR_IN) + continue; + length = 1 + (length / 188); + packet->buf1 |= length; + if (length > 1) /* BEGIN vs ALL */ + packet->buf1 |= 1 << 3; + } +} + +static int +sitd_urb_transaction ( + struct ehci_iso_stream *stream, + struct ehci_hcd *ehci, + struct urb *urb, + int mem_flags +) +{ + struct ehci_sitd *sitd; + dma_addr_t sitd_dma; + int i; + struct ehci_iso_sched *iso_sched; + unsigned long flags; + + iso_sched = iso_sched_alloc (urb->number_of_packets, mem_flags); + if (iso_sched == 0) + return -ENOMEM; + + sitd_sched_init (iso_sched, stream, urb); + + /* allocate/init sITDs */ + spin_lock_irqsave (&ehci->lock, flags); + for (i = 0; i < urb->number_of_packets; i++) { + + /* NOTE: for now, we don't try to handle wraparound cases + * for IN (using sitd->hw_backpointer, like a FSTN), which + * means we never need two sitds for full speed packets. + */ + + /* free_list.next might be cache-hot ... but maybe + * the HC caches it too. avoid that issue for now. + */ + + /* prefer previously-allocated sitds */ + if (!list_empty(&stream->free_list)) { + sitd = list_entry (stream->free_list.prev, + struct ehci_sitd, sitd_list); + list_del (&sitd->sitd_list); + sitd_dma = sitd->sitd_dma; + } else + sitd = 0; + + if (!sitd) { + spin_unlock_irqrestore (&ehci->lock, flags); + sitd = dma_pool_alloc (ehci->sitd_pool, mem_flags, + &sitd_dma); + spin_lock_irqsave (&ehci->lock, flags); + } + + if (!sitd) { + iso_sched_free (stream, iso_sched); + spin_unlock_irqrestore (&ehci->lock, flags); + return -ENOMEM; + } + memset (sitd, 0, sizeof *sitd); + sitd->sitd_dma = sitd_dma; + list_add (&sitd->sitd_list, &iso_sched->td_list); + } + + /* temporarily store schedule info in hcpriv */ + urb->hcpriv = iso_sched; + urb->error_count = 0; + + spin_unlock_irqrestore (&ehci->lock, flags); + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static inline void +sitd_patch ( + struct ehci_iso_stream *stream, + struct ehci_sitd *sitd, + struct ehci_iso_sched *iso_sched, + unsigned index +) +{ + struct ehci_iso_packet *uf = &iso_sched->packet [index]; + u64 bufp = uf->bufp; + + sitd->hw_next = EHCI_LIST_END; + sitd->hw_fullspeed_ep = stream->address; + sitd->hw_uframe = stream->splits; + sitd->hw_results = uf->transaction; + sitd->hw_backpointer = EHCI_LIST_END; + + bufp = uf->bufp; + sitd->hw_buf [0] = cpu_to_le32 (bufp); + sitd->hw_buf_hi [0] = cpu_to_le32 (bufp >> 32); + + sitd->hw_buf [1] = cpu_to_le32 (uf->buf1); + if (uf->cross) { + bufp += 4096; + sitd->hw_buf_hi [1] = cpu_to_le32 (bufp >> 32); + } + sitd->index = index; +} + +static inline void +sitd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_sitd *sitd) +{ + /* note: sitd ordering could matter (CSPLIT then SSPLIT) */ + sitd->sitd_next = ehci->pshadow [frame]; + sitd->hw_next = ehci->periodic [frame]; + ehci->pshadow [frame].sitd = sitd; + sitd->frame = frame; + wmb (); + ehci->periodic [frame] = cpu_to_le32 (sitd->sitd_dma) | Q_TYPE_SITD; +} + +/* fit urb's sitds into the selected schedule slot; activate as needed */ +static int +sitd_link_urb ( + struct ehci_hcd *ehci, + struct urb *urb, + unsigned mod, + struct ehci_iso_stream *stream +) +{ + int packet; + unsigned next_uframe; + struct ehci_iso_sched *sched = urb->hcpriv; + struct ehci_sitd *sitd; + + next_uframe = stream->next_uframe; + + if (list_empty(&stream->td_list)) { + /* usbfs ignores TT bandwidth */ + hcd_to_bus (&ehci->hcd)->bandwidth_allocated + += stream->bandwidth; + ehci_vdbg (ehci, + "sched dev%s ep%d%s-iso [%d] %dms/%04x\n", + urb->dev->devpath, stream->bEndpointAddress & 0x0f, + (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out", + (next_uframe >> 3) % ehci->periodic_size, + stream->interval, le32_to_cpu (stream->splits)); + stream->start = jiffies; + } + hcd_to_bus (&ehci->hcd)->bandwidth_isoc_reqs++; + + /* fill sITDs frame by frame */ + for (packet = 0, sitd = 0; + packet < urb->number_of_packets; + packet++) { + + /* ASSERT: we have all necessary sitds */ + BUG_ON (list_empty (&sched->td_list)); + + /* ASSERT: no itds for this endpoint in this frame */ + + sitd = list_entry (sched->td_list.next, + struct ehci_sitd, sitd_list); + list_move_tail (&sitd->sitd_list, &stream->td_list); + sitd->stream = iso_stream_get (stream); + sitd->urb = usb_get_urb (urb); + + sitd_patch (stream, sitd, sched, packet); + sitd_link (ehci, (next_uframe >> 3) % ehci->periodic_size, + sitd); + + next_uframe += stream->interval << 3; + stream->depth += stream->interval << 3; + } + stream->next_uframe = next_uframe % mod; + + /* don't need that schedule data any more */ + iso_sched_free (stream, sched); + urb->hcpriv = 0; + + timer_action (ehci, TIMER_IO_WATCHDOG); + if (!ehci->periodic_sched++) + return enable_periodic (ehci); + return 0; +} + +/*-------------------------------------------------------------------------*/ + +#define SITD_ERRS (SITD_STS_ERR | SITD_STS_DBE | SITD_STS_BABBLE \ + | SITD_STS_XACT | SITD_STS_MMF | SITD_STS_STS) + +static unsigned +sitd_complete ( + struct ehci_hcd *ehci, + struct ehci_sitd *sitd, + struct pt_regs *regs +) { + struct urb *urb = sitd->urb; + struct usb_iso_packet_descriptor *desc; + u32 t; + int urb_index = -1; + struct ehci_iso_stream *stream = sitd->stream; + struct usb_device *dev; + + urb_index = sitd->index; + desc = &urb->iso_frame_desc [urb_index]; + t = le32_to_cpup (&sitd->hw_results); + + /* report transfer status */ + if (t & SITD_ERRS) { + urb->error_count++; + if (t & SITD_STS_DBE) + desc->status = usb_pipein (urb->pipe) + ? -ENOSR /* hc couldn't read */ + : -ECOMM; /* hc couldn't write */ + else if (t & SITD_STS_BABBLE) + desc->status = -EOVERFLOW; + else /* XACT, MMF, etc */ + desc->status = -EPROTO; + } else { + desc->status = 0; + desc->actual_length = desc->length - SITD_LENGTH (t); + } + + usb_put_urb (urb); + sitd->urb = 0; + sitd->stream = 0; + list_move (&sitd->sitd_list, &stream->free_list); + stream->depth -= stream->interval << 3; + iso_stream_put (ehci, stream); + + /* handle completion now? */ + if ((urb_index + 1) != urb->number_of_packets) + return 0; + + /* ASSERT: it's really the last sitd for this urb + list_for_each_entry (sitd, &stream->td_list, sitd_list) + BUG_ON (sitd->urb == urb); + */ + + /* give urb back to the driver */ + dev = usb_get_dev (urb->dev); + ehci_urb_done (ehci, urb, regs); + urb = 0; + + /* defer stopping schedule; completion can submit */ + ehci->periodic_sched--; + if (!ehci->periodic_sched) + (void) disable_periodic (ehci); + hcd_to_bus (&ehci->hcd)->bandwidth_isoc_reqs--; + + if (list_empty (&stream->td_list)) { + hcd_to_bus (&ehci->hcd)->bandwidth_allocated + -= stream->bandwidth; + ehci_vdbg (ehci, + "deschedule devp %s ep%d%s-iso\n", + dev->devpath, stream->bEndpointAddress & 0x0f, + (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); + } + iso_stream_put (ehci, stream); + usb_put_dev (dev); + + return 1; +} + + +static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags) +{ + int status = -EINVAL; + unsigned long flags; + struct ehci_iso_stream *stream; + + // FIXME remove when csplits behave + if (usb_pipein(urb->pipe)) { + ehci_dbg (ehci, "no iso-IN split transactions yet\n"); + return -ENOMEM; + } + + /* Get iso_stream head */ + stream = iso_stream_find (ehci, urb); + if (stream == 0) { + ehci_dbg (ehci, "can't get iso stream\n"); + return -ENOMEM; + } + if (urb->interval != stream->interval) { + ehci_dbg (ehci, "can't change iso interval %d --> %d\n", + stream->interval, urb->interval); + goto done; + } + +#ifdef EHCI_URB_TRACE + ehci_dbg (ehci, + "submit %p dev%s ep%d%s-iso len %d\n", + urb, urb->dev->devpath, + usb_pipeendpoint (urb->pipe), + usb_pipein (urb->pipe) ? "in" : "out", + urb->transfer_buffer_length); +#endif + + /* allocate SITDs */ + status = sitd_urb_transaction (stream, ehci, urb, mem_flags); + if (status < 0) { + ehci_dbg (ehci, "can't init sitds\n"); + goto done; + } + + /* schedule ... need to lock */ + spin_lock_irqsave (&ehci->lock, flags); + status = iso_stream_schedule (ehci, urb, stream); + if (status == 0) + sitd_link_urb (ehci, urb, ehci->periodic_size << 3, stream); + spin_unlock_irqrestore (&ehci->lock, flags); + +done: + if (status < 0) + iso_stream_put (ehci, stream); + return status; +} + +#else + +static inline int +sitd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags) +{ + ehci_dbg (ehci, "split iso support is disabled\n"); + return -ENOSYS; +} + +static inline unsigned +sitd_complete ( + struct ehci_hcd *ehci, + struct ehci_sitd *sitd, + struct pt_regs *regs +) { + ehci_err (ehci, "sitd_complete %p?\n", sitd); + return 0; +} + +#endif /* USB_EHCI_SPLIT_ISO */ /*-------------------------------------------------------------------------*/ @@ -1513,7 +1884,6 @@ restart: modified = itd_complete (ehci, q.itd, regs); q = *q_p; break; -#ifdef have_split_iso case Q_TYPE_SITD: if (q.sitd->hw_results & SITD_ACTIVE) { q_p = &q.sitd->sitd_next; @@ -1529,7 +1899,6 @@ restart: modified = sitd_complete (ehci, q.sitd, regs); q = *q_p; break; -#endif /* have_split_iso */ default: dbg ("corrupt type %d frame %d shadow %p", type, frame, q.ptr); --- linux-2.6.4-rc2/drivers/usb/host/Kconfig 2003-09-27 18:57:46.000000000 -0700 +++ 25/drivers/usb/host/Kconfig 2004-03-07 20:46:56.000000000 -0800 @@ -29,6 +29,15 @@ config USB_EHCI_HCD To compile this driver as a module, choose M here: the module will be called ehci-hcd. +config USB_EHCI_SPLIT_ISO + bool "Full speed ISO transactions (EXPERIMENTAL)" + depends on USB_EHCI_HCD && EXPERIMENTAL + default n + ---help--- + This code is new and hasn't been used with many different + EHCI or USB 2.0 transaction translator implementations. + It should work for ISO-OUT transfers, like audio. + config USB_OHCI_HCD tristate "OHCI HCD support" depends on USB --- linux-2.6.4-rc2/drivers/usb/host/uhci-hcd.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/drivers/usb/host/uhci-hcd.c 2004-03-07 20:46:56.000000000 -0800 @@ -781,7 +781,8 @@ static void uhci_dec_fsbr(struct uhci_hc /* * Map status to standard result codes * - * is (td->status & 0xFE0000) [a.k.a. uhci_status_bits(td->status)] + * is (td->status & 0xF60000) [a.k.a. uhci_status_bits(td->status)] + * Note: status does not include the TD_CTRL_NAK bit. * is True for output TDs and False for input TDs. */ static int uhci_map_status(int status, int dir_out) @@ -792,22 +793,18 @@ static int uhci_map_status(int status, i return -EPROTO; if (status & TD_CTRL_CRCTIMEO) { /* CRC/Timeout */ if (dir_out) - return -ETIMEDOUT; + return -EPROTO; else return -EILSEQ; } - if (status & TD_CTRL_NAK) /* NAK */ - return -ETIMEDOUT; if (status & TD_CTRL_BABBLE) /* Babble */ return -EOVERFLOW; if (status & TD_CTRL_DBUFERR) /* Buffer error */ return -ENOSR; if (status & TD_CTRL_STALLED) /* Stalled */ return -EPIPE; - if (status & TD_CTRL_ACTIVE) /* Active */ - return 0; - - return -EINVAL; + WARN_ON(status & TD_CTRL_ACTIVE); /* Active */ + return 0; } /* @@ -832,7 +829,7 @@ static int uhci_submit_control(struct uh status |= TD_CTRL_LS; /* - * Build the TD for the control request + * Build the TD for the control request setup packet */ td = uhci_alloc_td(uhci, urb->dev); if (!td) @@ -990,13 +987,13 @@ static int uhci_result_control(struct uh if (urbp->short_control_packet) { tmp = head->prev; - goto status_phase; + goto status_stage; } tmp = head->next; td = list_entry(tmp, struct uhci_td, list); - /* The first TD is the SETUP phase, check the status, but skip */ + /* The first TD is the SETUP stage, check the status, but skip */ /* the count */ status = uhci_status_bits(td_status(td)); if (status & TD_CTRL_ACTIVE) @@ -1037,10 +1034,10 @@ static int uhci_result_control(struct uh } } -status_phase: +status_stage: td = list_entry(tmp, struct uhci_td, list); - /* Control status phase */ + /* Control status stage */ status = td_status(td); #ifdef I_HAVE_BUGGY_APC_BACKUPS @@ -1053,10 +1050,11 @@ status_phase: return 0; #endif + status = uhci_status_bits(status); if (status & TD_CTRL_ACTIVE) return -EINPROGRESS; - if (uhci_status_bits(status)) + if (status) goto td_error; return 0; @@ -1273,12 +1271,6 @@ static inline int uhci_submit_interrupt( } /* - * Bulk and interrupt use common result - */ -#define uhci_result_bulk uhci_result_common -#define uhci_result_interrupt uhci_result_common - -/* * Isochronous transfers */ static int isochronous_find_limits(struct uhci_hcd *uhci, struct urb *urb, unsigned int *start, unsigned int *end) @@ -1403,7 +1395,8 @@ static int uhci_result_isochronous(struc urb->iso_frame_desc[i].actual_length = actlength; urb->actual_length += actlength; - status = uhci_map_status(uhci_status_bits(td_status(td)), usb_pipeout(urb->pipe)); + status = uhci_map_status(uhci_status_bits(td_status(td)), + usb_pipeout(urb->pipe)); urb->iso_frame_desc[i].status = status; if (status) { urb->error_count++; @@ -1508,12 +1501,9 @@ static int uhci_urb_enqueue(struct usb_h struct urb_priv *urbp = urb->hcpriv; list_del_init(&urbp->urb_list); - spin_unlock_irqrestore(&uhci->urb_list_lock, flags); - uhci_destroy_urb_priv (uhci, urb); - - return ret; - } - ret = 0; + uhci_destroy_urb_priv(uhci, urb); + } else + ret = 0; out: spin_unlock_irqrestore(&uhci->urb_list_lock, flags); @@ -1541,11 +1531,9 @@ static void uhci_transfer_result(struct case PIPE_CONTROL: ret = uhci_result_control(uhci, urb); break; - case PIPE_INTERRUPT: - ret = uhci_result_interrupt(uhci, urb); - break; case PIPE_BULK: - ret = uhci_result_bulk(uhci, urb); + case PIPE_INTERRUPT: + ret = uhci_result_common(uhci, urb); break; case PIPE_ISOCHRONOUS: ret = uhci_result_isochronous(uhci, urb); @@ -1649,10 +1637,12 @@ static int uhci_urb_dequeue(struct usb_h { struct uhci_hcd *uhci = hcd_to_uhci(hcd); unsigned long flags; - struct urb_priv *urbp = urb->hcpriv; + struct urb_priv *urbp; spin_lock_irqsave(&uhci->urb_list_lock, flags); - + urbp = urb->hcpriv; + if (!urbp) /* URB was never linked! */ + goto done; list_del_init(&urbp->urb_list); uhci_unlink_generic(uhci, urb); @@ -1665,6 +1655,7 @@ static int uhci_urb_dequeue(struct usb_h list_add_tail(&urbp->urb_list, &uhci->urb_remove_list); spin_unlock(&uhci->urb_remove_list_lock); +done: spin_unlock_irqrestore(&uhci->urb_list_lock, flags); return 0; } @@ -1861,17 +1852,12 @@ static void uhci_finish_completion(struc static void uhci_remove_pending_urbps(struct uhci_hcd *uhci) { - struct list_head *tmp, *head; - spin_lock(&uhci->urb_remove_list_lock); - head = &uhci->urb_remove_list; - tmp = head->next; - while (tmp != head) { - struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list); + spin_lock(&uhci->complete_list_lock); - tmp = tmp->next; - uhci_moveto_complete(uhci, urbp); - } + /* Splice the urb_remove_list onto the end of the complete_list */ + list_splice_init(&uhci->urb_remove_list, uhci->complete_list.prev); + spin_unlock(&uhci->complete_list_lock); spin_unlock(&uhci->urb_remove_list_lock); } @@ -2471,9 +2457,16 @@ static int uhci_resume(struct usb_hcd *h pci_set_master(to_pci_dev(uhci_dev(uhci))); - if (uhci->state == UHCI_SUSPENDED) + if (uhci->state == UHCI_SUSPENDED) { + + /* + * Some systems clear the Interrupt Enable register during + * PM suspend/resume, so reinitialize it. + */ + outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | + USBINTR_SP, uhci->io_addr + USBINTR); uhci->resume_detect = 1; - else { + } else { reset_hc(uhci); start_hc(uhci); } --- linux-2.6.4-rc2/drivers/usb/host/uhci-hcd.h 2004-03-03 23:12:46.000000000 -0800 +++ 25/drivers/usb/host/uhci-hcd.h 2004-03-07 20:46:56.000000000 -0800 @@ -141,7 +141,7 @@ struct uhci_qh { TD_CTRL_BABBLE | TD_CTRL_CRCTIME | TD_CTRL_BITSTUFF) #define uhci_maxerr(err) ((err) << TD_CTRL_C_ERR_SHIFT) -#define uhci_status_bits(ctrl_sts) ((ctrl_sts) & 0xFE0000) +#define uhci_status_bits(ctrl_sts) ((ctrl_sts) & 0xF60000) #define uhci_actual_length(ctrl_sts) (((ctrl_sts) + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */ /* --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/drivers/usb/input/ati_remote.c 2004-03-07 20:46:56.000000000 -0800 @@ -0,0 +1,851 @@ +/* + * USB ATI Remote support + * + * Version 2.2.0 Copyright (c) 2004 Torrey Hoffman + * Version 2.1.1 Copyright (c) 2002 Vladimir Dergachev + * + * This 2.2.0 version is a rewrite / cleanup of the 2.1.1 driver, including + * porting to the 2.6 kernel interfaces, along with other modification + * to better match the style of the existing usb/input drivers. However, the + * protocol and hardware handling is essentially unchanged from 2.1.1. + * + * The 2.1.1 driver was derived from the usbati_remote and usbkbd drivers by + * Vojtech Pavlik. + * + * Changes: + * + * Feb 2004: Torrey Hoffman + * Version 2.2.0 + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * + * Hardware & software notes + * + * These remote controls are distributed by ATI as part of their + * "All-In-Wonder" video card packages. The receiver self-identifies as a + * "USB Receiver" with manufacturer "X10 Wireless Technology Inc". + * + * It is possible to use multiple receivers and remotes on multiple computers + * simultaneously by configuring them to use specific channels. + * + * The RF protocol used by the remote supports 16 distinct channels, 1 to 16. + * Actually, it may even support more, at least in some revisions of the + * hardware. + * + * Each remote can be configured to transmit on one channel as follows: + * - Press and hold the "hand icon" button. + * - When the red LED starts to blink, let go of the "hand icon" button. + * - When it stops blinking, input the channel code as two digits, from 01 + * to 16, and press the hand icon again. + * + * The timing can be a little tricky. Try loading the module with debug=1 + * to have the kernel print out messages about the remote control number + * and mask. Note: debugging prints remote numbers as zero-based hexadecimal. + * + * The driver has a "channel_mask" parameter. This bitmask specifies which + * channels will be ignored by the module. To mask out channels, just add + * all the 2^channel_number values together. + * + * For instance, set channel_mask = 2^4 = 16 (binary 10000) to make ati_remote + * ignore signals coming from remote controls transmitting on channel 4, but + * accept all other channels. + * + * Or, set channel_mask = 65533, (0xFFFD), and all channels except 1 will be + * ignored. + * + * The default is 0 (respond to all channels). Bit 0 and bits 17-32 of this + * parameter are unused. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Module and Version Information, Module Parameters + */ + +#define ATI_REMOTE_VENDOR_ID 0x0bc7 +#define ATI_REMOTE_PRODUCT_ID 0x004 + +#define DRIVER_VERSION "2.2.0" +#define DRIVER_AUTHOR "Torrey Hoffman " +#define DRIVER_DESC "ATI/X10 RF USB Remote Control" + +#define NAME_BUFSIZE 80 /* size of product name, path buffers */ +#define DATA_BUFSIZE 63 /* size of URB data buffers */ +#define ATI_INPUTNUM 1 /* Which input device to register as */ + +unsigned long channel_mask = 0; +module_param(channel_mask, ulong, 444); +MODULE_PARM_DESC(channel_mask, "Bitmask of remote control channels to ignore"); + +static int debug = 0; +module_param(debug, int, 444); +MODULE_PARM_DESC(debug, "Enable extra debug messages and information"); + +#define dbginfo(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0) +#undef err +#define err(format, arg...) printk(KERN_ERR format , ## arg) + +static struct usb_device_id ati_remote_table[] = { + { USB_DEVICE(ATI_REMOTE_VENDOR_ID, ATI_REMOTE_PRODUCT_ID) }, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, ati_remote_table); + +/* Get hi and low bytes of a 16-bits int */ +#define HI(a) ((unsigned char)((a) >> 8)) +#define LO(a) ((unsigned char)((a) & 0xff)) + +#define SEND_FLAG_IN_PROGRESS 1 +#define SEND_FLAG_COMPLETE 2 + +/* Device initialization strings */ +static char init1[] = { 0x01, 0x00, 0x20, 0x14 }; +static char init2[] = { 0x01, 0x00, 0x20, 0x14, 0x20, 0x20, 0x20 }; + +/* Acceleration curve for directional control pad */ +static char accel[] = { 1, 2, 4, 6, 9, 13, 20 }; + +/* Duplicate event filtering time. + * Sequential, identical KIND_FILTERED inputs with less than + * FILTER_TIME jiffies between them are dropped. + * (HZ >> 4) == 1/16th of a second and works well for me. + */ +#define FILTER_TIME (HZ >> 4) + +struct ati_remote { + struct input_dev idev; + struct usb_device *udev; + struct usb_interface *interface; + + struct urb *irq_urb; + struct urb *out_urb; + struct usb_endpoint_descriptor *endpoint_in; + struct usb_endpoint_descriptor *endpoint_out; + unsigned char *inbuf; + unsigned char *outbuf; + dma_addr_t inbuf_dma; + dma_addr_t outbuf_dma; + + int open; /* open counter */ + int present; /* device plugged in? */ + + unsigned char old_data[2]; /* Detect duplicate events */ + unsigned long old_jiffies; + unsigned long acc_jiffies; /* handle acceleration */ + + char name[NAME_BUFSIZE]; + char phys[NAME_BUFSIZE]; + + wait_queue_head_t wait; + int send_flags; +}; + +/* "Kinds" of messages sent from the hardware to the driver. */ +#define KIND_END 0 +#define KIND_LITERAL 1 /* Simply pass to input system */ +#define KIND_FILTERED 2 /* Add artificial key-up events, drop keyrepeats */ +#define KIND_LU 3 /* Directional keypad diagonals - left up, */ +#define KIND_RU 4 /* right up, */ +#define KIND_LD 5 /* left down, */ +#define KIND_RD 6 /* right down */ +#define KIND_ACCEL 7 /* Directional keypad - left, right, up, down.*/ + +/* Translation table from hardware messages to input events. */ +static struct +{ + short kind; + unsigned char data1, data2; + int type; + unsigned int code; + int value; +} ati_remote_tbl[] = +{ + /* Directional control pad axes */ + {KIND_ACCEL, 0x35, 0x70, EV_REL, REL_X, -1}, /* left */ + {KIND_ACCEL, 0x36, 0x71, EV_REL, REL_X, 1}, /* right */ + {KIND_ACCEL, 0x37, 0x72, EV_REL, REL_Y, -1}, /* up */ + {KIND_ACCEL, 0x38, 0x73, EV_REL, REL_Y, 1}, /* down */ + /* Directional control pad diagonals */ + {KIND_LU, 0x39, 0x74, EV_REL, 0, 0}, /* left up */ + {KIND_RU, 0x3a, 0x75, EV_REL, 0, 0}, /* right up */ + {KIND_LD, 0x3c, 0x77, EV_REL, 0, 0}, /* left down */ + {KIND_RD, 0x3b, 0x76, EV_REL, 0, 0}, /* right down */ + + /* "Mouse button" buttons */ + {KIND_LITERAL, 0x3d, 0x78, EV_KEY, BTN_LEFT, 1}, /* left btn down */ + {KIND_LITERAL, 0x3e, 0x79, EV_KEY, BTN_LEFT, 0}, /* left btn up */ + {KIND_LITERAL, 0x41, 0x7c, EV_KEY, BTN_RIGHT, 1},/* right btn down */ + {KIND_LITERAL, 0x42, 0x7d, EV_KEY, BTN_RIGHT, 0},/* right btn up */ + + /* Artificial "doubleclick" events are generated by the hardware. + * They are mapped to the "side" and "extra" mouse buttons here. */ + {KIND_FILTERED, 0x3f, 0x7a, EV_KEY, BTN_SIDE, 1}, /* left dblclick */ + {KIND_FILTERED, 0x43, 0x7e, EV_KEY, BTN_EXTRA, 1},/* right dblclick */ + + /* keyboard. */ + {KIND_FILTERED, 0xd2, 0x0d, EV_KEY, KEY_1, 1}, + {KIND_FILTERED, 0xd3, 0x0e, EV_KEY, KEY_2, 1}, + {KIND_FILTERED, 0xd4, 0x0f, EV_KEY, KEY_3, 1}, + {KIND_FILTERED, 0xd5, 0x10, EV_KEY, KEY_4, 1}, + {KIND_FILTERED, 0xd6, 0x11, EV_KEY, KEY_5, 1}, + {KIND_FILTERED, 0xd7, 0x12, EV_KEY, KEY_6, 1}, + {KIND_FILTERED, 0xd8, 0x13, EV_KEY, KEY_7, 1}, + {KIND_FILTERED, 0xd9, 0x14, EV_KEY, KEY_8, 1}, + {KIND_FILTERED, 0xda, 0x15, EV_KEY, KEY_9, 1}, + {KIND_FILTERED, 0xdc, 0x17, EV_KEY, KEY_0, 1}, + {KIND_FILTERED, 0xc5, 0x00, EV_KEY, KEY_A, 1}, + {KIND_FILTERED, 0xc6, 0x01, EV_KEY, KEY_B, 1}, + {KIND_FILTERED, 0xde, 0x19, EV_KEY, KEY_C, 1}, + {KIND_FILTERED, 0xe0, 0x1b, EV_KEY, KEY_D, 1}, + {KIND_FILTERED, 0xe6, 0x21, EV_KEY, KEY_E, 1}, + {KIND_FILTERED, 0xe8, 0x23, EV_KEY, KEY_F, 1}, + + /* "special" keys */ + {KIND_FILTERED, 0xdd, 0x18, EV_KEY, KEY_KPENTER, 1}, /* "check" */ + {KIND_FILTERED, 0xdb, 0x16, EV_KEY, KEY_MENU, 1}, /* "menu" */ + {KIND_FILTERED, 0xc7, 0x02, EV_KEY, KEY_POWER, 1}, /* Power */ + {KIND_FILTERED, 0xc8, 0x03, EV_KEY, KEY_PROG1, 1}, /* TV */ + {KIND_FILTERED, 0xc9, 0x04, EV_KEY, KEY_PROG2, 1}, /* DVD */ + {KIND_FILTERED, 0xca, 0x05, EV_KEY, KEY_WWW, 1}, /* WEB */ + {KIND_FILTERED, 0xcb, 0x06, EV_KEY, KEY_BOOKMARKS, 1}, /* "book" */ + {KIND_FILTERED, 0xcc, 0x07, EV_KEY, KEY_EDIT, 1}, /* "hand" */ + {KIND_FILTERED, 0xe1, 0x1c, EV_KEY, KEY_COFFEE, 1}, /* "timer" */ + {KIND_FILTERED, 0xe5, 0x20, EV_KEY, KEY_FRONT, 1}, /* "max" */ + {KIND_FILTERED, 0xe2, 0x1d, EV_KEY, KEY_LEFT, 1}, /* left */ + {KIND_FILTERED, 0xe4, 0x1f, EV_KEY, KEY_RIGHT, 1}, /* right */ + {KIND_FILTERED, 0xe7, 0x22, EV_KEY, KEY_DOWN, 1}, /* down */ + {KIND_FILTERED, 0xdf, 0x1a, EV_KEY, KEY_UP, 1}, /* up */ + {KIND_FILTERED, 0xe3, 0x1e, EV_KEY, KEY_ENTER, 1}, /* "OK" */ + {KIND_FILTERED, 0xce, 0x09, EV_KEY, KEY_VOLUMEDOWN, 1}, /* VOL + */ + {KIND_FILTERED, 0xcd, 0x08, EV_KEY, KEY_VOLUMEUP, 1}, /* VOL - */ + {KIND_FILTERED, 0xcf, 0x0a, EV_KEY, KEY_MUTE, 1}, /* MUTE */ + {KIND_FILTERED, 0xd1, 0x0c, EV_KEY, KEY_CHANNELUP, 1}, /* CH + */ + {KIND_FILTERED, 0xd0, 0x0b, EV_KEY, KEY_CHANNELDOWN, 1},/* CH - */ + {KIND_FILTERED, 0xec, 0x27, EV_KEY, KEY_RECORD, 1}, /* ( o) red */ + {KIND_FILTERED, 0xea, 0x25, EV_KEY, KEY_PLAYCD, 1}, /* ( >) */ + {KIND_FILTERED, 0xe9, 0x24, EV_KEY, KEY_REWIND, 1}, /* (<<) */ + {KIND_FILTERED, 0xeb, 0x26, EV_KEY, KEY_FORWARD, 1}, /* (>>) */ + {KIND_FILTERED, 0xed, 0x28, EV_KEY, KEY_STOP, 1}, /* ([]) */ + {KIND_FILTERED, 0xee, 0x29, EV_KEY, KEY_PAUSE, 1}, /* ('') */ + + {KIND_END, 0x00, 0x00, EV_MAX + 1, 0, 0} +}; + +/* Local function prototypes */ +static void ati_remote_dump (unsigned char *data, unsigned int actual_length); +static void ati_remote_delete (struct ati_remote *dev); +static int ati_remote_open (struct input_dev *inputdev); +static void ati_remote_close (struct input_dev *inputdev); +static int ati_remote_sendpacket (struct ati_remote *ati_remote, u16 cmd, unsigned char *data); +static void ati_remote_irq_out (struct urb *urb, struct pt_regs *regs); +static void ati_remote_irq_in (struct urb *urb, struct pt_regs *regs); +static void ati_remote_input_report (struct urb *urb, struct pt_regs *regs); +static int ati_remote_initialize (struct ati_remote *ati_remote); +static int ati_remote_probe (struct usb_interface *interface, const struct usb_device_id *id); +static void ati_remote_disconnect (struct usb_interface *interface); + +/* usb specific object to register with the usb subsystem */ +static struct usb_driver ati_remote_driver = { + .owner = THIS_MODULE, + .name = "ati_remote", + .probe = ati_remote_probe, + .disconnect = ati_remote_disconnect, + .id_table = ati_remote_table, +}; + +/* + * ati_remote_dump_input + */ +static void ati_remote_dump(unsigned char *data, unsigned int len) +{ + if ((len == 1) && (data[0] != (unsigned char)0xff) && (data[0] != 0x00)) + warn("Weird byte 0x%02x\n", data[0]); + else if (len == 4) + warn("Weird key %02x %02x %02x %02x\n", + data[0], data[1], data[2], data[3]); + else + warn("Weird data, len=%d %02x %02x %02x %02x %02x %02x ...\n", + len, data[0], data[1], data[2], data[3], data[4], data[5]); +} + +/* + * ati_remote_open + */ +static int ati_remote_open(struct input_dev *inputdev) +{ + struct ati_remote *ati_remote = inputdev->private; + + if (ati_remote->open++) + return 0; + + /* On first open, submit the read urb which was set up previously. */ + ati_remote->irq_urb->dev = ati_remote->udev; + if (usb_submit_urb(ati_remote->irq_urb, GFP_KERNEL)) { + dev_err(&ati_remote->interface->dev, + "%s: usb_submit_urb failed!\n", __FUNCTION__); + ati_remote->open--; + return -EIO; + } + + return 0; +} + +/* + * ati_remote_close + */ +static void ati_remote_close(struct input_dev *inputdev) +{ + struct ati_remote *ati_remote = inputdev->private; + + if (ati_remote == NULL) { + err("ati_remote: %s: object is NULL!\n", __FUNCTION__); + return; + } + + if (ati_remote->open <= 0) + dev_dbg(&ati_remote->interface->dev, "%s: Not open.\n", __FUNCTION__); + else + --ati_remote->open; + + /* If still present, disconnect will call delete. */ + if (!ati_remote->present && !ati_remote->open) + ati_remote_delete(ati_remote); +} + +/* + * ati_remote_irq_out + */ +static void ati_remote_irq_out(struct urb *urb, struct pt_regs *regs) +{ + struct ati_remote *ati_remote = urb->context; + + if (urb->status) { + dev_dbg(&ati_remote->interface->dev, "%s: status %d\n", + __FUNCTION__, urb->status); + return; + } + + ati_remote->send_flags |= SEND_FLAG_COMPLETE; + wmb(); + if (waitqueue_active(&ati_remote->wait)) + wake_up(&ati_remote->wait); +} + +/* + * ati_remote_sendpacket + * + * Used to send device initialization strings + */ +static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd, unsigned char *data) +{ + DECLARE_WAITQUEUE(wait, current); + int timeout = HZ; /* 1 second */ + int retval = 0; + + /* Set up out_urb */ + memcpy(ati_remote->out_urb->transfer_buffer + 1, data, LO(cmd)); + ((char *) ati_remote->out_urb->transfer_buffer)[0] = HI(cmd); + + ati_remote->out_urb->transfer_buffer_length = LO(cmd) + 1; + ati_remote->out_urb->dev = ati_remote->udev; + ati_remote->send_flags = SEND_FLAG_IN_PROGRESS; + + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&ati_remote->wait, &wait); + + retval = usb_submit_urb(ati_remote->out_urb, GFP_KERNEL); + if (retval) { + set_current_state(TASK_RUNNING); + remove_wait_queue(&ati_remote->wait, &wait); + dev_dbg(&ati_remote->interface->dev, + "sendpacket: usb_submit_urb failed: %d\n", retval); + return retval; + } + + while (timeout && (ati_remote->out_urb->status == -EINPROGRESS) + && !(ati_remote->send_flags & SEND_FLAG_COMPLETE)) { + timeout = schedule_timeout(timeout); + rmb(); + } + + set_current_state(TASK_RUNNING); + remove_wait_queue(&ati_remote->wait, &wait); + usb_unlink_urb(ati_remote->out_urb); + + return retval; +} + +/* + * ati_remote_event_lookup + */ +static int ati_remote_event_lookup(int rem, unsigned char d1, unsigned char d2) +{ + int i; + + for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++) { + /* + * Decide if the table entry matches the remote input. + */ + if ((((ati_remote_tbl[i].data1 & 0x0f) == (d1 & 0x0f))) && + ((((ati_remote_tbl[i].data1 >> 4) - + (d1 >> 4) + rem) & 0x0f) == 0x0f) && + (ati_remote_tbl[i].data2 == d2)) + return i; + + } + return -1; +} + +/* + * ati_remote_report_input + */ +static void ati_remote_input_report(struct urb *urb, struct pt_regs *regs) +{ + struct ati_remote *ati_remote = urb->context; + unsigned char *data= ati_remote->inbuf; + struct input_dev *dev = &ati_remote->idev; + int index, acc; + int remote_num; + + /* Deal with strange looking inputs */ + if ( (urb->actual_length != 4) || (data[0] != 0x14) || + ((data[3] & 0x0f) != 0x00) ) { + ati_remote_dump(data, urb->actual_length); + return; + } + + /* Mask unwanted remote channels. */ + /* note: remote_num is 0-based, channel 1 on remote == 0 here */ + remote_num = (data[3] >> 4) & 0x0f; + if (channel_mask & (1 << (remote_num + 1))) { + dbginfo(&ati_remote->interface->dev, + "Masked input from channel 0x%02x: data %02x,%02x, mask= 0x%02lx\n", + remote_num, data[1], data[2], channel_mask); + return; + } + + /* Look up event code index in translation table */ + index = ati_remote_event_lookup(remote_num, data[1], data[2]); + if (index < 0) { + dev_warn(&ati_remote->interface->dev, + "Unknown input from channel 0x%02x: data %02x,%02x\n", + remote_num, data[1], data[2]); + return; + } + dbginfo(&ati_remote->interface->dev, + "channel 0x%02x; data %02x,%02x; index %d; keycode %d\n", + remote_num, data[1], data[2], index, ati_remote_tbl[index].code); + + if (ati_remote_tbl[index].kind == KIND_LITERAL) { + input_regs(dev, regs); + input_event(dev, ati_remote_tbl[index].type, + ati_remote_tbl[index].code, + ati_remote_tbl[index].value); + input_sync(dev); + + ati_remote->old_jiffies = jiffies; + return; + } + + if (ati_remote_tbl[index].kind == KIND_FILTERED) { + /* Filter duplicate events which happen "too close" together. */ + if ((ati_remote->old_data[0] == data[1]) && + (ati_remote->old_data[1] == data[2]) && + ((ati_remote->old_jiffies + FILTER_TIME) > jiffies)) { + ati_remote->old_jiffies = jiffies; + return; + } + + input_regs(dev, regs); + input_event(dev, ati_remote_tbl[index].type, + ati_remote_tbl[index].code, 1); + input_event(dev, ati_remote_tbl[index].type, + ati_remote_tbl[index].code, 0); + input_sync(dev); + + ati_remote->old_data[0] = data[1]; + ati_remote->old_data[1] = data[2]; + ati_remote->old_jiffies = jiffies; + return; + } + + /* + * Other event kinds are from the directional control pad, and have an + * acceleration factor applied to them. Without this acceleration, the + * control pad is mostly unusable. + * + * If elapsed time since last event is > 1/4 second, user "stopped", + * so reset acceleration. Otherwise, user is probably holding the control + * pad down, so we increase acceleration, ramping up over two seconds to + * a maximum speed. The acceleration curve is #defined above. + */ + if ((jiffies - ati_remote->old_jiffies) > (HZ >> 2)) { + acc = 1; + ati_remote->acc_jiffies = jiffies; + } + else if ((jiffies - ati_remote->acc_jiffies) < (HZ >> 3)) acc = accel[0]; + else if ((jiffies - ati_remote->acc_jiffies) < (HZ >> 2)) acc = accel[1]; + else if ((jiffies - ati_remote->acc_jiffies) < (HZ >> 1)) acc = accel[2]; + else if ((jiffies - ati_remote->acc_jiffies) < HZ ) acc = accel[3]; + else if ((jiffies - ati_remote->acc_jiffies) < HZ+(HZ>>1)) acc = accel[4]; + else if ((jiffies - ati_remote->acc_jiffies) < (HZ << 1)) acc = accel[5]; + else acc = accel[6]; + + input_regs(dev, regs); + switch (ati_remote_tbl[index].kind) { + case KIND_ACCEL: + input_event(dev, ati_remote_tbl[index].type, + ati_remote_tbl[index].code, + ati_remote_tbl[index].value * acc); + break; + case KIND_LU: + input_report_rel(dev, REL_X, -acc); + input_report_rel(dev, REL_Y, -acc); + break; + case KIND_RU: + input_report_rel(dev, REL_X, acc); + input_report_rel(dev, REL_Y, -acc); + break; + case KIND_LD: + input_report_rel(dev, REL_X, -acc); + input_report_rel(dev, REL_Y, acc); + break; + case KIND_RD: + input_report_rel(dev, REL_X, acc); + input_report_rel(dev, REL_Y, acc); + break; + default: + dev_dbg(&ati_remote->interface->dev, "ati_remote kind=%d\n", + ati_remote_tbl[index].kind); + } + input_sync(dev); + + ati_remote->old_jiffies = jiffies; + ati_remote->old_data[0] = data[1]; + ati_remote->old_data[1] = data[2]; +} + +/* + * ati_remote_irq_in + */ +static void ati_remote_irq_in(struct urb *urb, struct pt_regs *regs) +{ + struct ati_remote *ati_remote = urb->context; + int retval; + + switch (urb->status) { + case 0: /* success */ + ati_remote_input_report(urb, regs); + break; + case -ECONNRESET: /* unlink */ + case -ENOENT: + case -ESHUTDOWN: + dev_dbg(&ati_remote->interface->dev, "%s: urb error status, unlink? \n", + __FUNCTION__); + return; + default: /* error */ + dev_dbg(&ati_remote->interface->dev, "%s: Nonzero urb status %d\n", + __FUNCTION__, urb->status); + } + + retval = usb_submit_urb(urb, SLAB_ATOMIC); + if (retval) + dev_err(&ati_remote->interface->dev, "%s: usb_submit_urb()=%d\n", + __FUNCTION__, retval); +} + +/* + * ati_remote_delete + */ +static void ati_remote_delete(struct ati_remote *ati_remote) +{ + if (!ati_remote) return; + + if (ati_remote->irq_urb) + usb_unlink_urb(ati_remote->irq_urb); + + if (ati_remote->out_urb) + usb_unlink_urb(ati_remote->out_urb); + + if (ati_remote->irq_urb) + usb_free_urb(ati_remote->irq_urb); + + if (ati_remote->out_urb) + usb_free_urb(ati_remote->out_urb); + + if (ati_remote->inbuf) + usb_buffer_free(ati_remote->udev, DATA_BUFSIZE, + ati_remote->inbuf, ati_remote->inbuf_dma); + + if (ati_remote->outbuf) + usb_buffer_free(ati_remote->udev, DATA_BUFSIZE, + ati_remote->inbuf, ati_remote->outbuf_dma); + + kfree(ati_remote); +} + +static void ati_remote_input_init(struct ati_remote *ati_remote) +{ + struct input_dev *idev = &(ati_remote->idev); + int i; + + idev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL); + idev->keybit[LONG(BTN_MOUSE)] = ( BIT(BTN_LEFT) | BIT(BTN_RIGHT) | + BIT(BTN_SIDE) | BIT(BTN_EXTRA) ); + idev->relbit[0] = BIT(REL_X) | BIT(REL_Y); + for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++) + if (ati_remote_tbl[i].type == EV_KEY) + set_bit(ati_remote_tbl[i].code, idev->keybit); + + idev->private = ati_remote; + idev->open = ati_remote_open; + idev->close = ati_remote_close; + + idev->name = ati_remote->name; + idev->phys = ati_remote->phys; + + idev->id.bustype = BUS_USB; + idev->id.vendor = ati_remote->udev->descriptor.idVendor; + idev->id.product = ati_remote->udev->descriptor.idProduct; + idev->id.version = ati_remote->udev->descriptor.bcdDevice; +} + +static int ati_remote_initialize(struct ati_remote *ati_remote) +{ + struct usb_device *udev = ati_remote->udev; + int pipe, maxp; + + init_waitqueue_head(&ati_remote->wait); + + /* Set up irq_urb */ + pipe = usb_rcvintpipe(udev, ati_remote->endpoint_in->bEndpointAddress); + maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); + maxp = (maxp > DATA_BUFSIZE) ? DATA_BUFSIZE : maxp; + + usb_fill_int_urb(ati_remote->irq_urb, udev, pipe, ati_remote->inbuf, + maxp, ati_remote_irq_in, ati_remote, + ati_remote->endpoint_in->bInterval); + ati_remote->irq_urb->transfer_dma = ati_remote->inbuf_dma; + ati_remote->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + /* Set up out_urb */ + pipe = usb_sndintpipe(udev, ati_remote->endpoint_out->bEndpointAddress); + maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); + maxp = (maxp > DATA_BUFSIZE) ? DATA_BUFSIZE : maxp; + + usb_fill_int_urb(ati_remote->out_urb, udev, pipe, ati_remote->outbuf, + maxp, ati_remote_irq_out, ati_remote, + ati_remote->endpoint_out->bInterval); + ati_remote->out_urb->transfer_dma = ati_remote->outbuf_dma; + ati_remote->out_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + /* send initialization strings */ + if ((ati_remote_sendpacket(ati_remote, 0x8004, init1)) || + (ati_remote_sendpacket(ati_remote, 0x8007, init2))) { + dev_err(&ati_remote->interface->dev, + "Initializing ati_remote hardware failed.\n"); + return 1; + } + + return 0; +} + +/* + * ati_remote_probe + */ +static int ati_remote_probe(struct usb_interface *interface, const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(interface); + struct ati_remote *ati_remote = NULL; + struct usb_host_interface *iface_host; + int retval = -ENOMEM; + char path[64]; + char *buf = NULL; + + /* See if the offered device matches what we can accept */ + if ((udev->descriptor.idVendor != ATI_REMOTE_VENDOR_ID) || + (udev->descriptor.idProduct != ATI_REMOTE_PRODUCT_ID)) { + return -ENODEV; + } + + /* Allocate and clear an ati_remote struct */ + if (!(ati_remote = kmalloc(sizeof (struct ati_remote), GFP_KERNEL))) + return -ENOMEM; + memset(ati_remote, 0x00, sizeof (struct ati_remote)); + + iface_host = interface->cur_altsetting; + if (iface_host->desc.bNumEndpoints != 2) { + err("%s: Unexpected desc.bNumEndpoints\n", __FUNCTION__); + retval = -ENODEV; + goto error; + } + + ati_remote->endpoint_in = &(iface_host->endpoint[0].desc); + ati_remote->endpoint_out = &(iface_host->endpoint[1].desc); + ati_remote->udev = udev; + ati_remote->interface = interface; + + if (!(ati_remote->endpoint_in->bEndpointAddress & 0x80)) { + err("%s: Unexpected endpoint_in->bEndpointAddress\n", __FUNCTION__); + retval = -ENODEV; + goto error; + } + if ((ati_remote->endpoint_in->bmAttributes & 3) != 3) { + err("%s: Unexpected endpoint_in->bmAttributes\n", __FUNCTION__); + retval = -ENODEV; + goto error; + } + if (ati_remote->endpoint_in->wMaxPacketSize == 0) { + err("%s: endpoint_in message size==0? \n", __FUNCTION__); + retval = -ENODEV; + goto error; + } + if (!(buf = kmalloc(NAME_BUFSIZE, GFP_KERNEL))) + goto error; + + /* Allocate URB buffers, URBs */ + ati_remote->inbuf = usb_buffer_alloc(udev, DATA_BUFSIZE, SLAB_ATOMIC, + &ati_remote->inbuf_dma); + if (!ati_remote->inbuf) + goto error; + + ati_remote->outbuf = usb_buffer_alloc(udev, DATA_BUFSIZE, SLAB_ATOMIC, + &ati_remote->outbuf_dma); + if (!ati_remote->outbuf) + goto error; + + ati_remote->irq_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!ati_remote->irq_urb) + goto error; + + ati_remote->out_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!ati_remote->out_urb) + goto error; + + usb_make_path(udev, path, NAME_BUFSIZE); + sprintf(ati_remote->phys, "%s/input%d", path, ATI_INPUTNUM); + if (udev->descriptor.iManufacturer && + (usb_string(udev, udev->descriptor.iManufacturer, buf, + NAME_BUFSIZE) > 0)) + strcat(ati_remote->name, buf); + + if (udev->descriptor.iProduct && + (usb_string(udev, udev->descriptor.iProduct, buf, NAME_BUFSIZE) > 0)) + sprintf(ati_remote->name, "%s %s", ati_remote->name, buf); + + if (!strlen(ati_remote->name)) + sprintf(ati_remote->name, DRIVER_DESC "(%04x,%04x)", + ati_remote->udev->descriptor.idVendor, + ati_remote->udev->descriptor.idProduct); + + /* Device Hardware Initialization - fills in ati_remote->idev from udev. */ + retval = ati_remote_initialize(ati_remote); + if (retval) + goto error; + + /* Set up and register input device */ + ati_remote_input_init(ati_remote); + input_register_device(&ati_remote->idev); + + dev_info(&ati_remote->interface->dev, "Input registered: %s on %s\n", + ati_remote->name, path); + + usb_set_intfdata(interface, ati_remote); + ati_remote->present = 1; + kfree(buf); + return 0; + +error: + if (buf) + kfree(buf); + + ati_remote_delete(ati_remote); + return retval; +} + +/* + * ati_remote_disconnect + */ +static void ati_remote_disconnect(struct usb_interface *interface) +{ + struct ati_remote *ati_remote; + + ati_remote = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + if (!ati_remote) { + warn("%s - null device?\n", __FUNCTION__); + return; + } + + input_unregister_device(&ati_remote->idev); + + /* Mark device as unplugged */ + ati_remote->present = 0; + + /* If device is still open, ati_remote_close will call delete. */ + if (!ati_remote->open) + ati_remote_delete(ati_remote); +} + +/* + * ati_remote_init + */ +static int __init ati_remote_init(void) +{ + int result; + + result = usb_register(&ati_remote_driver); + if (result) + err("usb_register error #%d\n", result); + else + info("Registered USB driver " DRIVER_DESC " v. " DRIVER_VERSION); + + return result; +} + +/* + * ati_remote_exit + */ +static void __exit ati_remote_exit(void) +{ + usb_deregister(&ati_remote_driver); +} + +/* + * module specification + */ + +module_init(ati_remote_init); +module_exit(ati_remote_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + --- linux-2.6.4-rc2/drivers/usb/input/hid-core.c 2004-02-17 20:48:45.000000000 -0800 +++ 25/drivers/usb/input/hid-core.c 2004-03-07 20:46:56.000000000 -0800 @@ -224,6 +224,9 @@ static int hid_add_field(struct hid_pars offset = report->size; report->size += parser->global.report_size * parser->global.report_count; + if (usages < parser->global.report_count) + usages = parser->global.report_count; + if (usages == 0) return 0; /* ignore padding fields */ @@ -235,9 +238,13 @@ static int hid_add_field(struct hid_pars field->application = hid_lookup_collection(parser, HID_COLLECTION_APPLICATION); for (i = 0; i < usages; i++) { - field->usage[i].hid = parser->local.usage[i]; + int j = i; + /* Duplicate the last usage we parsed if we have excess values */ + if (i >= parser->local.usage_index) + j = parser->local.usage_index - 1; + field->usage[i].hid = parser->local.usage[j]; field->usage[i].collection_index = - parser->local.collection_index[i]; + parser->local.collection_index[j]; } field->maxusage = usages; @@ -1317,7 +1324,6 @@ void hid_init_reports(struct hid_device #define USB_VENDOR_ID_KBGEAR 0x084e #define USB_DEVICE_ID_KBGEAR_JAMSTUDIO 0x1001 - #define USB_VENDOR_ID_AIPTEK 0x08ca #define USB_DEVICE_ID_AIPTEK_6000 0x0020 @@ -1356,17 +1362,43 @@ void hid_init_reports(struct hid_device #define USB_VENDOR_ID_A4TECH 0x09DA #define USB_DEVICE_ID_A4TECH_WCP32PU 0x0006 +#define USB_VENDOR_ID_CYPRESS 0x04b4 +#define USB_DEVICE_ID_CYPRESS_MOUSE 0x0001 + #define USB_VENDOR_ID_BERKSHIRE 0x0c98 #define USB_DEVICE_ID_BERKSHIRE_PCWD 0x1140 #define USB_VENDOR_ID_ALPS 0x0433 #define USB_DEVICE_ID_IBM_GAMEPAD 0x1101 +#define USB_VENDOR_ID_SAITEK 0x06a3 +#define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17 + +#define USB_VENDOR_ID_NEC 0x073e +#define USB_DEVICE_ID_NEC_USB_GAME_PAD 0x0301 + struct hid_blacklist { __u16 idVendor; __u16 idProduct; unsigned quirks; } hid_blacklist[] = { + + { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_6000, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS1, HID_QUIRK_IGNORE }, + + { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 100, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 200, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 300, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 400, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 500, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_PENPARTNER, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE + 1, HID_QUIRK_IGNORE }, @@ -1388,32 +1420,25 @@ struct hid_blacklist { { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS2 + 2, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS2 + 3, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS2 + 4, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_6000, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET }, - { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS1, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD }, - { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_DRIVING, HID_QUIRK_BADPAD|HID_QUIRK_MULTI_INPUT }, - { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FLYING, HID_QUIRK_BADPAD|HID_QUIRK_MULTI_INPUT }, - { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FIGHTING, HID_QUIRK_BADPAD|HID_QUIRK_MULTI_INPUT }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 100, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 200, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 300, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 400, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 500, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_TANGTOP, USB_DEVICE_ID_TANGTOP_USBPS2, HID_QUIRK_NOGET }, - { USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU, HID_QUIRK_2WHEEL_MOUSE_HACK }, - { USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD, HID_QUIRK_IGNORE }, + + { USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU, HID_QUIRK_2WHEEL_MOUSE_HACK_BACK }, + { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE, HID_QUIRK_2WHEEL_MOUSE_HACK_EXTRA }, + { USB_VENDOR_ID_ALPS, USB_DEVICE_ID_IBM_GAMEPAD, HID_QUIRK_BADPAD }, + { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_DRIVING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT }, + { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FLYING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT }, + { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FIGHTING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT }, + { USB_VENDOR_ID_NEC, USB_DEVICE_ID_NEC_USB_GAME_PAD, HID_QUIRK_BADPAD }, + { USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD }, + { USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD }, + { 0, 0 } }; @@ -1445,7 +1470,7 @@ static void hid_free_buffers(struct usb_ static struct hid_device *usb_hid_configure(struct usb_interface *intf) { - struct usb_host_interface *interface = intf->altsetting + intf->act_altsetting; + struct usb_host_interface *interface = intf->cur_altsetting; struct usb_device *dev = interface_to_usbdev (intf); struct hid_descriptor *hdesc; struct hid_device *hid; --- linux-2.6.4-rc2/drivers/usb/input/hiddev.c 2004-02-03 20:42:37.000000000 -0800 +++ 25/drivers/usb/input/hiddev.c 2004-03-07 20:46:53.000000000 -0800 @@ -403,7 +403,8 @@ static int hiddev_ioctl(struct inode *in struct hiddev_collection_info cinfo; struct hiddev_report_info rinfo; struct hiddev_field_info finfo; - struct hiddev_usage_ref uref; + struct hiddev_usage_ref_multi uref_multi; + struct hiddev_usage_ref *uref = &uref_multi.uref; struct hiddev_devinfo dinfo; struct hid_report *report; struct hid_field *field; @@ -575,68 +576,98 @@ static int hiddev_ioctl(struct inode *in return 0; case HIDIOCGUCODE: - if (copy_from_user(&uref, (void *) arg, sizeof(uref))) + if (copy_from_user(uref, (void *) arg, sizeof(*uref))) return -EFAULT; - rinfo.report_type = uref.report_type; - rinfo.report_id = uref.report_id; + rinfo.report_type = uref->report_type; + rinfo.report_id = uref->report_id; if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) return -EINVAL; - if (uref.field_index >= report->maxfield) + if (uref->field_index >= report->maxfield) return -EINVAL; - field = report->field[uref.field_index]; - if (uref.usage_index >= field->maxusage) + field = report->field[uref->field_index]; + if (uref->usage_index >= field->maxusage) return -EINVAL; - uref.usage_code = field->usage[uref.usage_index].hid; + uref->usage_code = field->usage[uref->usage_index].hid; - if (copy_to_user((void *) arg, &uref, sizeof(uref))) + if (copy_to_user((void *) arg, uref, sizeof(*uref))) return -EFAULT; return 0; case HIDIOCGUSAGE: case HIDIOCSUSAGE: + case HIDIOCGUSAGES: + case HIDIOCSUSAGES: case HIDIOCGCOLLECTIONINDEX: - if (copy_from_user(&uref, (void *) arg, sizeof(uref))) - return -EFAULT; + if (cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) { + if (copy_from_user(&uref_multi, (void *) arg, + sizeof(uref_multi))) + return -EFAULT; + } else { + if (copy_from_user(uref, (void *) arg, sizeof(*uref))) + return -EFAULT; + } - if (cmd != HIDIOCGUSAGE && uref.report_type == HID_REPORT_TYPE_INPUT) - return -EINVAL; + if (cmd != HIDIOCGUSAGE && + cmd != HIDIOCGUSAGES && + uref->report_type == HID_REPORT_TYPE_INPUT) + return -EINVAL; - if (uref.report_id == HID_REPORT_ID_UNKNOWN) { - field = hiddev_lookup_usage(hid, &uref); + if (uref->report_id == HID_REPORT_ID_UNKNOWN) { + field = hiddev_lookup_usage(hid, uref); if (field == NULL) return -EINVAL; } else { - rinfo.report_type = uref.report_type; - rinfo.report_id = uref.report_id; + rinfo.report_type = uref->report_type; + rinfo.report_id = uref->report_id; if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) return -EINVAL; - if (uref.field_index >= report->maxfield) + if (uref->field_index >= report->maxfield) return -EINVAL; - field = report->field[uref.field_index]; - if (uref.usage_index >= field->maxusage) + field = report->field[uref->field_index]; + if (uref->usage_index >= field->maxusage) return -EINVAL; + + if (cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) { + if (uref_multi.num_values >= HID_MAX_USAGES || + uref->usage_index >= field->maxusage || + (uref->usage_index + uref_multi.num_values) >= field->maxusage) + return -EINVAL; + } } switch (cmd) { case HIDIOCGUSAGE: - uref.value = field->value[uref.usage_index]; - if (copy_to_user((void *) arg, &uref, sizeof(uref))) + uref->value = field->value[uref->usage_index]; + if (copy_to_user((void *) arg, uref, sizeof(*uref))) return -EFAULT; return 0; case HIDIOCSUSAGE: - field->value[uref.usage_index] = uref.value; + field->value[uref->usage_index] = uref->value; return 0; case HIDIOCGCOLLECTIONINDEX: - return field->usage[uref.usage_index].collection_index; + return field->usage[uref->usage_index].collection_index; + case HIDIOCGUSAGES: + for (i = 0; i < uref_multi.num_values; i++) + uref_multi.values[i] = + field->value[uref->usage_index + i]; + if (copy_to_user((void *) arg, &uref_multi, + sizeof(uref_multi))) + return -EFAULT; + return 0; + case HIDIOCSUSAGES: + for (i = 0; i < uref_multi.num_values; i++) + field->value[uref->usage_index + i] = + uref_multi.values[i]; + return 0; } return 0; --- linux-2.6.4-rc2/drivers/usb/input/hid.h 2004-02-03 20:42:37.000000000 -0800 +++ 25/drivers/usb/input/hid.h 2004-03-07 20:46:53.000000000 -0800 @@ -201,15 +201,16 @@ struct hid_item { * HID device quirks. */ -#define HID_QUIRK_INVERT 0x001 -#define HID_QUIRK_NOTOUCH 0x002 -#define HID_QUIRK_IGNORE 0x004 -#define HID_QUIRK_NOGET 0x008 -#define HID_QUIRK_HIDDEV 0x010 -#define HID_QUIRK_BADPAD 0x020 -#define HID_QUIRK_MULTI_INPUT 0x040 -#define HID_QUIRK_2WHEEL_MOUSE_HACK 0x080 -#define HID_QUIRK_2WHEEL_MOUSE_HACK_ON 0x100 +#define HID_QUIRK_INVERT 0x001 +#define HID_QUIRK_NOTOUCH 0x002 +#define HID_QUIRK_IGNORE 0x004 +#define HID_QUIRK_NOGET 0x008 +#define HID_QUIRK_HIDDEV 0x010 +#define HID_QUIRK_BADPAD 0x020 +#define HID_QUIRK_MULTI_INPUT 0x040 +#define HID_QUIRK_2WHEEL_MOUSE_HACK_BACK 0x080 +#define HID_QUIRK_2WHEEL_MOUSE_HACK_EXTRA 0x100 +#define HID_QUIRK_2WHEEL_MOUSE_HACK_ON 0x200 /* * This is the global environment of the parser. This information is --- linux-2.6.4-rc2/drivers/usb/input/hid-input.c 2004-02-03 20:42:37.000000000 -0800 +++ 25/drivers/usb/input/hid-input.c 2004-03-07 20:46:53.000000000 -0800 @@ -377,7 +377,8 @@ static void hidinput_configure_usage(str set_bit(usage->type, input->evbit); if ((usage->type == EV_REL) - && (device->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK) + && (device->quirks & (HID_QUIRK_2WHEEL_MOUSE_HACK_BACK + | HID_QUIRK_2WHEEL_MOUSE_HACK_EXTRA)) && (usage->code == REL_WHEEL)) { set_bit(REL_HWHEEL, bit); } @@ -431,21 +432,22 @@ void hidinput_hid_event(struct hid_devic input_regs(input, regs); - if ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK) - && (usage->code == BTN_BACK)) { + if (((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_EXTRA) && (usage->code == BTN_EXTRA)) + || ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_BACK) && (usage->code == BTN_BACK))) { if (value) hid->quirks |= HID_QUIRK_2WHEEL_MOUSE_HACK_ON; else hid->quirks &= ~HID_QUIRK_2WHEEL_MOUSE_HACK_ON; return; } + if ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_ON) && (usage->code == REL_WHEEL)) { input_event(input, usage->type, REL_HWHEEL, value); return; } - if (usage->hat_min != usage->hat_max) { + if (usage->hat_min != usage->hat_max ) { /* FIXME: hat_max can be 0 and hat_min 1 */ value = (value - usage->hat_min) * 8 / (usage->hat_max - usage->hat_min + 1) + 1; if (value < 0 || value > 8) value = 0; input_event(input, usage->type, usage->code , hid_hat_to_axis[value].x); @@ -484,7 +486,7 @@ void hidinput_hid_event(struct hid_devic return; } - if((usage->type == EV_KEY) && (usage->code == 0)) /* Key 0 is "unassigned", not KEY_UKNOWN */ + if((usage->type == EV_KEY) && (usage->code == 0)) /* Key 0 is "unassigned", not KEY_UNKNOWN */ return; input_event(input, usage->type, usage->code, value); --- linux-2.6.4-rc2/drivers/usb/input/kbtab.c 2004-02-03 20:42:37.000000000 -0800 +++ 25/drivers/usb/input/kbtab.c 2004-03-07 20:46:56.000000000 -0800 @@ -74,12 +74,15 @@ static void kbtab_irq(struct urb *urb, s input_report_abs(dev, ABS_X, kbtab->x); input_report_abs(dev, ABS_Y, kbtab->y); - /*input_report_abs(dev, ABS_PRESSURE, kbtab->pressure);*/ /*input_report_key(dev, BTN_TOUCH , data[0] & 0x01);*/ input_report_key(dev, BTN_RIGHT, data[0] & 0x02); - - input_report_key(dev, BTN_LEFT, (kbtab->pressure > kb_pressure_click) ? 1 : 0); + + if( -1 == kb_pressure_click){ + input_report_abs(dev, ABS_PRESSURE, kbtab->pressure); + } else { + input_report_key(dev, BTN_LEFT, (kbtab->pressure > kb_pressure_click) ? 1 : 0); + }; input_sync(dev); --- linux-2.6.4-rc2/drivers/usb/input/Kconfig 2004-02-17 20:48:45.000000000 -0800 +++ 25/drivers/usb/input/Kconfig 2004-03-07 20:46:56.000000000 -0800 @@ -179,6 +179,18 @@ config USB_POWERMATE To compile this driver as a module, choose M here: the module will be called powermate. +config USB_MTOUCH + tristate "MicroTouch USB Touchscreen Driver" + depends on USB && INPUT + ---help--- + Say Y here if you want to use a MicroTouch (Now 3M) USB + Touchscreen controller. + + See for additional information. + + To compile this driver as a module, choose M here: the + module will be called mtouchusb. + config USB_XPAD tristate "X-Box gamepad support" depends on USB && INPUT @@ -192,3 +204,17 @@ config USB_XPAD To compile this driver as a module, choose M here: the module will be called xpad. + +config USB_ATI_REMOTE + tristate "ATI USB RF remote control" + depends on USB && INPUT + ---help--- + Say Y here if you want to use one of ATI's USB remote controls. + These are RF remotes with USB receivers. They come with many of ATI's + All-In-Wonder video cards. This driver provides mouse pointer, left + and right mouse buttons, and maps all the other remote buttons to + keypress events. + + To compile this driver as a module, choose M here: the module will be + called ati_remote. + --- linux-2.6.4-rc2/drivers/usb/input/Makefile 2003-06-14 12:18:24.000000000 -0700 +++ 25/drivers/usb/input/Makefile 2004-03-07 20:46:56.000000000 -0800 @@ -27,10 +27,12 @@ ifeq ($(CONFIG_HID_FF),y) endif obj-$(CONFIG_USB_AIPTEK) += aiptek.o +obj-$(CONFIG_USB_ATI_REMOTE) += ati_remote.o obj-$(CONFIG_USB_HID) += hid.o obj-$(CONFIG_USB_KBD) += usbkbd.o -obj-$(CONFIG_USB_MOUSE) += usbmouse.o -obj-$(CONFIG_USB_WACOM) += wacom.o obj-$(CONFIG_USB_KBTAB) += kbtab.o +obj-$(CONFIG_USB_MOUSE) += usbmouse.o +obj-$(CONFIG_USB_MTOUCH) += mtouchusb.o obj-$(CONFIG_USB_POWERMATE) += powermate.o +obj-$(CONFIG_USB_WACOM) += wacom.o obj-$(CONFIG_USB_XPAD) += xpad.o --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/drivers/usb/input/mtouchusb.c 2004-03-07 20:46:56.000000000 -0800 @@ -0,0 +1,391 @@ +/****************************************************************************** + * mtouchusb.c -- Driver for Microtouch (Now 3M) USB Touchscreens + * + * 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. + * + * Based upon original work by Radoslaw Garbacz (usb-support@ite.pl) + * (http://freshmeat.net/projects/3mtouchscreendriver) + * + * History + * + * 0.3 & 0.4 2002 (TEJ) tejohnson@yahoo.com + * Updated to 2.4.18, then 2.4.19 + * Old version still relied on stealing a minor + * + * 0.5 02/26/2004 (TEJ) tejohnson@yahoo.com + * Complete rewrite using Linux Input in 2.6.3 + * Unfortunately no calibration support at this time + * + *****************************************************************************/ + +#include + +#ifdef CONFIG_USB_DEBUG + #define DEBUG +#else + #undef DEBUG +#endif + +#include +#include +#include +#include +#include +#include + +#define MTOUCHUSB_MIN_XC 0xc8 +#define MTOUCHUSB_MAX_XC 0xff78 +#define MTOUCHUSB_XC_FUZZ 0x0 +#define MTOUCHUSB_XC_FLAT 0x0 +#define MTOUCHUSB_MIN_YC 0x0 +#define MTOUCHUSB_MAX_YC 0xff78 +#define MTOUCHUSB_YC_FUZZ 0x0 +#define MTOUCHUSB_YC_FLAT 0x0 +#define MTOUCHUSB_ASYC_REPORT 1 +#define MTOUCHUSB_REPORT_SIZE_DATA 11 +#define MTOUCHUSB_REQ_CTRLLR_ID 10 + +#define MTOUCHUSB_GET_XC(data) (data[4]<<8 | data[3]) +#define MTOUCHUSB_GET_YC(data) (data[6]<<8 | data[5]) +#define MTOUCHUSB_GET_TOUCHED(data) ((data[2] & 0x40) ? 1:0) + +#define DRIVER_VERSION "v0.1" +#define DRIVER_AUTHOR "Todd E. Johnson, tejohnson@yahoo.com" +#define DRIVER_DESC "Microtouch USB HID Touchscreen Driver" + +struct mtouch_usb { + unsigned char *data; + dma_addr_t data_dma; + struct urb *irq; + struct usb_device *udev; + struct input_dev input; + int open; + char name[128]; + char phys[64]; +}; + +static __s32 vendor=-1, product=-1; + +static struct usb_device_id mtouchusb_devices [] = { + { USB_DEVICE(0x0596, 0x0001) }, /* 3M (Formerly MicroTouch) 14-206 */ + { } /* Terminating entry */ +}; + +static void mtouchusb_irq(struct urb *urb, struct pt_regs *regs) +{ + struct mtouch_usb *mtouch = urb->context; + int retval; + + switch (urb->status) { + case 0: + /* success */ + break; + case -ETIMEDOUT: + /* this urb is timing out */ + dbg("%s - urb timed out - was the device unplugged?", + __FUNCTION__); + return; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", + __FUNCTION__, urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", + __FUNCTION__, urb->status); + goto exit; + } + + input_regs(&mtouch->input, regs); + input_report_key(&mtouch->input, BTN_TOUCH, + MTOUCHUSB_GET_TOUCHED(mtouch->data)); + input_report_abs(&mtouch->input, ABS_X, + MTOUCHUSB_GET_XC(mtouch->data)); + input_report_abs(&mtouch->input, ABS_Y, + MTOUCHUSB_GET_YC(mtouch->data)); + input_sync(&mtouch->input); + +exit: + retval = usb_submit_urb (urb, GFP_ATOMIC); + if (retval) + err ("%s - usb_submit_urb failed with result: %d", + __FUNCTION__, retval); +} + +static int mtouchusb_open (struct input_dev *input) +{ + struct mtouch_usb *mtouch = input->private; + + if (mtouch->open++) + return 0; + + mtouch->irq->dev = mtouch->udev; + + if (usb_submit_urb (mtouch->irq, GFP_ATOMIC)) + return -EIO; + + return 0; +} + +static void mtouchusb_close (struct input_dev *input) +{ + struct mtouch_usb *mtouch = input->private; + + if (!--mtouch->open) + usb_unlink_urb (mtouch->irq); +} + +static int mtouchusb_alloc_buffers(struct usb_device *udev, struct mtouch_usb *mtouch) +{ + dbg("%s - called", __FUNCTION__); + + mtouch->data = usb_buffer_alloc(udev, MTOUCHUSB_REPORT_SIZE_DATA, + SLAB_ATOMIC, &mtouch->data_dma); + + if (!mtouch->data) + return -1; + + return 0; +} + +static void mtouchusb_free_buffers(struct usb_device *udev, struct mtouch_usb *mtouch) +{ + dbg("%s - called", __FUNCTION__); + + if (mtouch->data) + usb_buffer_free(udev, MTOUCHUSB_REPORT_SIZE_DATA, + mtouch->data, mtouch->data_dma); +} + +static int mtouchusb_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct mtouch_usb *mtouch; + struct usb_host_interface *interface; + struct usb_endpoint_descriptor *endpoint; + struct usb_device *udev = interface_to_usbdev (intf); + char path[64]; + char *buf; + int nRet; + int ix; + char valid_device = 0; + + dbg("%s - called", __FUNCTION__); + if (vendor != -1 && product != -1) { + info("%s - User specified USB Touch -- Vend:Prod - %x:%x", + __FUNCTION__, vendor, product); + } + + for (ix = 0; ix < sizeof (mtouchusb_devices) / + sizeof (struct usb_device_id); ix++) { + if ((udev->descriptor.idVendor == + mtouchusb_devices [ix].idVendor) && + (udev->descriptor.idProduct == + mtouchusb_devices [ix].idProduct)) { + valid_device = 1; + break; + } + } + + if (udev->descriptor.idVendor == vendor && + udev->descriptor.idProduct == product) { /* User specified */ + valid_device = 1; + } + + if (!valid_device) { + err("%s - No valid device!", __FUNCTION__); + return -EIO; + } + + if (udev->descriptor.bNumConfigurations != 1) { + err("%s - Only one device configuration is supported.", + __FUNCTION__); + return -EIO; + } + + dbg("%s - setting interface", __FUNCTION__); + interface = intf->cur_altsetting; + + dbg("%s - setting endpoint", __FUNCTION__); + endpoint = &interface->endpoint[0].desc; + + if (interface->desc.bNumEndpoints != 1) { + err("%s - Only one endpoint is supported.", __FUNCTION__); + return -EIO; + } + + if (!(mtouch = kmalloc (sizeof (struct mtouch_usb), GFP_KERNEL))) { + err("%s - Out of memory.", __FUNCTION__); + return -ENOMEM; + } + + memset(mtouch, 0, sizeof(struct mtouch_usb)); + mtouch->udev = udev; + + dbg("%s - allocating buffers", __FUNCTION__); + if (mtouchusb_alloc_buffers(udev, mtouch)) { + mtouchusb_free_buffers(udev, mtouch); + kfree(mtouch); + return -ENOMEM; + } + + mtouch->input.private = mtouch; + mtouch->input.open = mtouchusb_open; + mtouch->input.close = mtouchusb_close; + + usb_make_path(udev, path, 64); + sprintf(mtouch->phys, "%s/input0", path); + + mtouch->input.name = mtouch->name; + mtouch->input.phys = mtouch->phys; + mtouch->input.id.bustype = BUS_USB; + mtouch->input.id.vendor = udev->descriptor.idVendor; + mtouch->input.id.product = udev->descriptor.idProduct; + mtouch->input.id.version = udev->descriptor.bcdDevice; + mtouch->input.dev = &intf->dev; + + mtouch->input.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + mtouch->input.absbit[0] = BIT(ABS_X) | BIT(ABS_Y); + mtouch->input.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); + + /* Used to Scale Compensated Data and Flip Y */ + mtouch->input.absmin[ABS_X] = MTOUCHUSB_MIN_XC; + mtouch->input.absmax[ABS_X] = MTOUCHUSB_MAX_XC; + mtouch->input.absfuzz[ABS_X] = MTOUCHUSB_XC_FUZZ; + mtouch->input.absflat[ABS_X] = MTOUCHUSB_XC_FLAT; + mtouch->input.absmin[ABS_Y] = MTOUCHUSB_MAX_YC; + mtouch->input.absmax[ABS_Y] = MTOUCHUSB_MIN_YC; + mtouch->input.absfuzz[ABS_Y] = MTOUCHUSB_YC_FUZZ; + mtouch->input.absflat[ABS_Y] = MTOUCHUSB_YC_FLAT; + + if (!(buf = kmalloc(63, GFP_KERNEL))) { + kfree(mtouch); + return -ENOMEM; + } + + if (udev->descriptor.iManufacturer && + usb_string(udev, udev->descriptor.iManufacturer, buf, 63) > 0) + strcat(mtouch->name, buf); + if (udev->descriptor.iProduct && + usb_string(udev, udev->descriptor.iProduct, buf, 63) > 0) + sprintf(mtouch->name, "%s %s", mtouch->name, buf); + + if (!strlen(mtouch->name)) + sprintf(mtouch->name, "USB Touchscreen %04x:%04x", + mtouch->input.id.vendor, mtouch->input.id.product); + + kfree(buf); + + nRet = usb_control_msg(mtouch->udev, + usb_rcvctrlpipe(udev, 0x80), + USB_REQ_GET_CONFIGURATION, + USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE, + 0, + 0x81, + NULL, + 0, + HZ * USB_CTRL_SET_TIMEOUT); + dbg("%s - usb_control_msg - USB_REQ_GET_CONFIGURATION - bytes|err: %d", + __FUNCTION__, nRet); + + dbg("%s - usb_alloc_urb: mtouch->irq", __FUNCTION__); + mtouch->irq = usb_alloc_urb(0, GFP_KERNEL); + if (!mtouch->irq) { + dbg("%s - usb_alloc_urb failed: mtouch->irq", __FUNCTION__); + mtouchusb_free_buffers(udev, mtouch); + kfree(mtouch); + return -ENOMEM; + } + + dbg("%s - usb_fill_int_urb", __FUNCTION__); + usb_fill_int_urb(mtouch->irq, + mtouch->udev, + usb_rcvintpipe(mtouch->udev, 0x81), + mtouch->data, + MTOUCHUSB_REPORT_SIZE_DATA, + mtouchusb_irq, + mtouch, + endpoint->bInterval); + + dbg("%s - input_register_device", __FUNCTION__); + input_register_device(&mtouch->input); + + nRet = usb_control_msg(mtouch->udev, + usb_rcvctrlpipe(udev, 0x80), + MTOUCHUSB_ASYC_REPORT, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + MTOUCHUSB_ASYC_REPORT, + MTOUCHUSB_ASYC_REPORT, + NULL, + 0, + HZ * USB_CTRL_SET_TIMEOUT); + dbg("%s - usb_control_msg - MTOUCHUSB_ASYC_REPORT - bytes|err: %d", + __FUNCTION__, nRet); + + printk(KERN_INFO "input: %s on %s\n", mtouch->name, path); + usb_set_intfdata(intf, mtouch); + + return 0; +} + +static void mtouchusb_disconnect(struct usb_interface *intf) +{ + struct mtouch_usb *mtouch = usb_get_intfdata (intf); + + dbg("%s - called", __FUNCTION__); + usb_set_intfdata(intf, NULL); + if (mtouch) { + dbg("%s - mtouch is initialized, cleaning up", __FUNCTION__); + usb_unlink_urb(mtouch->irq); + input_unregister_device(&mtouch->input); + usb_free_urb(mtouch->irq); + mtouchusb_free_buffers(interface_to_usbdev(intf), mtouch); + kfree(mtouch); + } +} + +MODULE_DEVICE_TABLE (usb, mtouchusb_devices); + +static struct usb_driver mtouchusb_driver = { + .owner = THIS_MODULE, + .name = "mtouchusb", + .probe = mtouchusb_probe, + .disconnect = mtouchusb_disconnect, + .id_table = mtouchusb_devices, +}; + +static int __init mtouchusb_init(void) { + dbg("%s - called", __FUNCTION__); + return usb_register(&mtouchusb_driver); +} + +static void __exit mtouchusb_cleanup(void) { + dbg("%s - called", __FUNCTION__); + usb_deregister(&mtouchusb_driver); +} + +module_init(mtouchusb_init); +module_exit(mtouchusb_cleanup); + +MODULE_AUTHOR( DRIVER_AUTHOR ); +MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_LICENSE("GPL"); +MODULE_PARM(vendor, "i"); +MODULE_PARM_DESC(vendor, "User specified USB idVendor"); +MODULE_PARM(product, "i"); +MODULE_PARM_DESC(product, "User specified USB idProduct"); + + --- linux-2.6.4-rc2/drivers/usb/input/usbkbd.c 2004-02-03 20:42:37.000000000 -0800 +++ 25/drivers/usb/input/usbkbd.c 2004-03-07 20:46:56.000000000 -0800 @@ -240,7 +240,7 @@ static int usb_kbd_probe(struct usb_inte char path[64]; char *buf; - interface = &iface->altsetting[iface->act_altsetting]; + interface = iface->cur_altsetting; if (interface->desc.bNumEndpoints != 1) return -ENODEV; --- linux-2.6.4-rc2/drivers/usb/input/usbmouse.c 2004-02-03 20:42:37.000000000 -0800 +++ 25/drivers/usb/input/usbmouse.c 2004-03-07 20:46:56.000000000 -0800 @@ -131,7 +131,7 @@ static int usb_mouse_probe(struct usb_in char path[64]; char *buf; - interface = &intf->altsetting[intf->act_altsetting]; + interface = intf->cur_altsetting; if (interface->desc.bNumEndpoints != 1) return -ENODEV; --- linux-2.6.4-rc2/drivers/usb/input/wacom.c 2004-02-03 20:42:37.000000000 -0800 +++ 25/drivers/usb/input/wacom.c 2004-03-07 20:46:53.000000000 -0800 @@ -1,7 +1,7 @@ /* * USB Wacom Graphire and Wacom Intuos tablet support * - * Copyright (c) 2000-2002 Vojtech Pavlik + * Copyright (c) 2000-2004 Vojtech Pavlik * Copyright (c) 2000 Andreas Bach Aaen * Copyright (c) 2000 Clifford Wolf * Copyright (c) 2000 Sam Mosel @@ -9,6 +9,7 @@ * Copyright (c) 2000 Daniel Egger * Copyright (c) 2001 Frederic Lepied * Copyright (c) 2002 Ping Cheng + * Copyright (c) 2004 Panagiotis Issaris * * ChangeLog: * v0.1 (vp) - Initial release @@ -48,6 +49,7 @@ * v1.30 (vp) - Merge 2.4 and 2.5 drivers * - Since 2.5 now has input_sync(), remove MSC_SERIAL abuse * - Cleanups here and there + * v1.30.1 (pi) - Added Graphire3 support */ /* --- linux-2.6.4-rc2/drivers/usb/Makefile 2004-02-17 20:48:45.000000000 -0800 +++ 25/drivers/usb/Makefile 2004-03-07 20:46:56.000000000 -0800 @@ -20,10 +20,15 @@ obj-$(CONFIG_USB_PRINTER) += class/ obj-$(CONFIG_USB_STORAGE) += storage/ obj-$(CONFIG_USB_AIPTEK) += input/ +obj-$(CONFIG_USB_ATI_REMOTE) += input/ obj-$(CONFIG_USB_HID) += input/ obj-$(CONFIG_USB_KBD) += input/ +obj-$(CONFIG_USB_KBTAB) += input/ obj-$(CONFIG_USB_MOUSE) += input/ +obj-$(CONFIG_USB_MTOUCH) += input/ +obj-$(CONFIG_USB_POWERMATE) += input/ obj-$(CONFIG_USB_WACOM) += input/ +obj-$(CONFIG_USB_XPAD) += input/ obj-$(CONFIG_USB_DABUSB) += media/ obj-$(CONFIG_USB_DSBR) += media/ --- linux-2.6.4-rc2/drivers/usb/misc/usbtest.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/drivers/usb/misc/usbtest.c 2004-03-07 20:46:56.000000000 -0800 @@ -490,7 +490,7 @@ static int set_altsetting (struct usbtes struct usb_interface *iface = dev->intf; struct usb_device *udev; - if (alternate < 0 || alternate >= iface->num_altsetting) + if (alternate < 0 || alternate >= 256) return -EINVAL; udev = interface_to_usbdev (iface); @@ -556,23 +556,19 @@ static int ch9_postconfig (struct usbtes { struct usb_interface *iface = dev->intf; struct usb_device *udev = interface_to_usbdev (iface); - int i, retval; + int i, alt, retval; /* [9.2.3] if there's more than one altsetting, we need to be able to * set and get each one. mostly trusts the descriptors from usbcore. */ for (i = 0; i < iface->num_altsetting; i++) { - /* 9.2.3 constrains the range here, and Linux ensures - * they're ordered meaningfully in this array - */ - if (iface->altsetting [i].desc.bAlternateSetting != i) { + /* 9.2.3 constrains the range here */ + alt = iface->altsetting [i].desc.bAlternateSetting; + if (alt < 0 || alt >= iface->num_altsetting) { dev_dbg (&iface->dev, "invalid alt [%d].bAltSetting = %d\n", - i, - iface->altsetting [i].desc - .bAlternateSetting); - return -EDOM; + i, alt); } /* [real world] get/set unimplemented if there's only one */ @@ -580,18 +576,18 @@ static int ch9_postconfig (struct usbtes continue; /* [9.4.10] set_interface */ - retval = set_altsetting (dev, i); + retval = set_altsetting (dev, alt); if (retval) { dev_dbg (&iface->dev, "can't set_interface = %d, %d\n", - i, retval); + alt, retval); return retval; } /* [9.4.4] get_interface always works */ retval = get_altsetting (dev); - if (retval != i) { + if (retval != alt) { dev_dbg (&iface->dev, "get alt should be %d, was %d\n", - i, retval); + alt, retval); return (retval < 0) ? retval : -EDOM; } --- linux-2.6.4-rc2/drivers/usb/net/usbnet.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/drivers/usb/net/usbnet.c 2004-03-07 20:46:56.000000000 -0800 @@ -3009,7 +3009,7 @@ usbnet_probe (struct usb_interface *udev return -ENODEV; } xdev = interface_to_usbdev (udev); - interface = &udev->altsetting [udev->act_altsetting]; + interface = udev->cur_altsetting; usb_get_dev (xdev); @@ -3314,6 +3314,15 @@ static const struct usb_device_id produc .bInterfaceSubClass = 0x0a, .bInterfaceProtocol = 0x00, .driver_info = (unsigned long) &zaurus_pxa_info, +}, { + .match_flags = USB_DEVICE_ID_MATCH_INT_INFO + | USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = 0x04DD, + .idProduct = 0x9050, /* C-860 */ + .bInterfaceClass = 0x02, + .bInterfaceSubClass = 0x0a, + .bInterfaceProtocol = 0x00, + .driver_info = (unsigned long) &zaurus_pxa_info, }, #endif --- linux-2.6.4-rc2/drivers/usb/serial/ftdi_sio.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/drivers/usb/serial/ftdi_sio.c 2004-03-07 20:46:56.000000000 -0800 @@ -286,6 +286,7 @@ static struct usb_device_id id_table_sio static struct usb_device_id id_table_8U232AM [] = { + { USB_DEVICE_VER(FTDI_VID, FTDI_IRTRANS_PID, 0, 0x3ff) }, { USB_DEVICE_VER(FTDI_VID, FTDI_8U232AM_PID, 0, 0x3ff) }, { USB_DEVICE_VER(FTDI_VID, FTDI_8U232AM_ALT_PID, 0, 0x3ff) }, { USB_DEVICE_VER(FTDI_VID, FTDI_RELAIS_PID, 0, 0x3ff) }, @@ -358,6 +359,7 @@ static struct usb_device_id id_table_8U2 static struct usb_device_id id_table_FT232BM [] = { + { USB_DEVICE_VER(FTDI_VID, FTDI_IRTRANS_PID, 0x400, 0xffff) }, { USB_DEVICE_VER(FTDI_VID, FTDI_8U232AM_PID, 0x400, 0xffff) }, { USB_DEVICE_VER(FTDI_VID, FTDI_8U232AM_ALT_PID, 0x400, 0xffff) }, { USB_DEVICE_VER(FTDI_VID, FTDI_RELAIS_PID, 0x400, 0xffff) }, @@ -451,6 +453,7 @@ static struct usb_device_id id_table_HE_ static struct usb_device_id id_table_combined [] = { + { USB_DEVICE(FTDI_VID, FTDI_IRTRANS_PID) }, { USB_DEVICE(FTDI_VID, FTDI_SIO_PID) }, { USB_DEVICE(FTDI_VID, FTDI_8U232AM_PID) }, { USB_DEVICE(FTDI_VID, FTDI_8U232AM_ALT_PID) }, --- linux-2.6.4-rc2/drivers/usb/serial/ftdi_sio.h 2004-03-03 23:12:46.000000000 -0800 +++ 25/drivers/usb/serial/ftdi_sio.h 2004-03-07 20:46:56.000000000 -0800 @@ -30,6 +30,8 @@ #define FTDI_NF_RIC_VID 0x0DCD /* Vendor Id */ #define FTDI_NF_RIC_PID 0x0001 /* Product Id */ +/* www.irtrans.de device */ +#define FTDI_IRTRANS_PID 0xFC60 /* Product Id */ /* www.crystalfontz.com devices - thanx for providing free devices for evaluation ! */ /* they use the ftdi chipset for the USB interface and the vendor id is the same */ --- linux-2.6.4-rc2/drivers/usb/serial/kl5kusb105.c 2003-09-08 13:58:58.000000000 -0700 +++ 25/drivers/usb/serial/kl5kusb105.c 2004-03-07 20:46:56.000000000 -0800 @@ -273,6 +273,7 @@ static int klsi_105_startup (struct usb_ /* allocate the private data structure */ for (i=0; inum_ports; i++) { + int j; priv = kmalloc(sizeof(struct klsi_105_private), GFP_KERNEL); if (!priv) { @@ -293,10 +294,10 @@ static int klsi_105_startup (struct usb_ usb_set_serial_port_data(serial->port[i], priv); spin_lock_init (&priv->lock); - for (i=0; iwrite_urb_pool[i] = urb; + priv->write_urb_pool[j] = urb; if (urb == NULL) { err("No more urbs???"); continue; --- linux-2.6.4-rc2/drivers/usb/serial/safe_serial.c 2003-09-08 13:58:58.000000000 -0700 +++ 25/drivers/usb/serial/safe_serial.c 2004-03-07 20:46:46.000000000 -0800 @@ -93,6 +93,7 @@ static int padded = CONFIG_USB_SAFE_PADD MODULE_AUTHOR (DRIVER_AUTHOR); MODULE_DESCRIPTION (DRIVER_DESC); +MODULE_LICENSE("GPL"); #if defined(CONFIG_USBD_SAFE_SERIAL_VENDOR) && !defined(CONFIG_USBD_SAFE_SERIAL_PRODUCT) #abort "SAFE_SERIAL_VENDOR defined without SAFE_SERIAL_PRODUCT" --- linux-2.6.4-rc2/drivers/usb/serial/visor.c 2004-02-17 20:48:45.000000000 -0800 +++ 25/drivers/usb/serial/visor.c 2004-03-07 20:46:56.000000000 -0800 @@ -239,6 +239,8 @@ static struct usb_device_id id_table [] .driver_info = (kernel_ulong_t)&palm_os_4_probe }, { USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_SCH_I330_ID), .driver_info = (kernel_ulong_t)&palm_os_4_probe }, + { USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_SPH_I500_ID), + .driver_info = (kernel_ulong_t)&palm_os_4_probe }, { USB_DEVICE(GARMIN_VENDOR_ID, GARMIN_IQUE_3600_ID), .driver_info = (kernel_ulong_t)&palm_os_4_probe }, { USB_DEVICE(ACEECA_VENDOR_ID, ACEECA_MEZ1000_ID), @@ -275,6 +277,7 @@ static struct usb_device_id id_table_com { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_UX50_ID) }, { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_TJ25_ID) }, { USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_SCH_I330_ID) }, + { USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_SPH_I500_ID) }, { USB_DEVICE(GARMIN_VENDOR_ID, GARMIN_IQUE_3600_ID) }, { USB_DEVICE(ACEECA_VENDOR_ID, ACEECA_MEZ1000_ID) }, { }, /* optional parameter entry */ --- linux-2.6.4-rc2/drivers/usb/serial/visor.h 2004-02-17 20:48:45.000000000 -0800 +++ 25/drivers/usb/serial/visor.h 2004-03-07 20:46:56.000000000 -0800 @@ -46,6 +46,7 @@ #define SAMSUNG_VENDOR_ID 0x04E8 #define SAMSUNG_SCH_I330_ID 0x8001 +#define SAMSUNG_SPH_I500_ID 0x6601 #define GARMIN_VENDOR_ID 0x091E #define GARMIN_IQUE_3600_ID 0x0004 --- linux-2.6.4-rc2/drivers/usb/storage/scsiglue.c 2004-02-03 20:42:37.000000000 -0800 +++ 25/drivers/usb/storage/scsiglue.c 2004-03-07 20:46:56.000000000 -0800 @@ -64,8 +64,10 @@ static const char* host_info(struct Scsi return "SCSI emulation for USB Mass Storage devices"; } -static int slave_configure (struct scsi_device *sdev) +static int slave_configure(struct scsi_device *sdev) { + struct us_data *us = (struct us_data *) sdev->host->hostdata[0]; + /* Scatter-gather buffers (all but the last) must have a length * divisible by the bulk maxpacket size. Otherwise a data packet * would end up being short, causing a premature end to the data @@ -76,6 +78,16 @@ static int slave_configure (struct scsi_ * the end, scatter-gather buffers follow page boundaries. */ blk_queue_dma_alignment(sdev->request_queue, (512 - 1)); + /* Devices using Genesys Logic chips cause a lot of trouble for + * high-speed transfers; they die unpredictably when given more + * than 64 KB of data at a time. If we detect such a device, + * reduce the maximum transfer size to 64 KB = 128 sectors. */ + +#define USB_VENDOR_ID_GENESYS 0x05e3 // Needs a standard location + if (us->pusb_dev->descriptor.idVendor == USB_VENDOR_ID_GENESYS && + us->pusb_dev->speed == USB_SPEED_HIGH) + blk_queue_max_sectors(sdev->request_queue, 128); + /* this is to satisify the compiler, tho I don't think the * return code is ever checked anywhere. */ return 0; --- linux-2.6.4-rc2/drivers/usb/storage/transport.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/drivers/usb/storage/transport.c 2004-03-07 20:46:56.000000000 -0800 @@ -563,9 +563,9 @@ void usb_stor_invoke_transport(Scsi_Cmnd /* * If we're running the CB transport, which is incapable - * of determining status on its own, we need to auto-sense + * of determining status on its own, we will auto-sense * unless the operation involved a data-in transfer. Devices - * can signal data-in errors by stalling the bulk-in pipe. + * can signal most data-in errors by stalling the bulk-in pipe. */ if ((us->protocol == US_PR_CB || us->protocol == US_PR_DPCM_USB) && srb->sc_data_direction != SCSI_DATA_READ) { @@ -698,7 +698,11 @@ void usb_stor_invoke_transport(Scsi_Cmnd * out the sense buffer so the higher layers won't realize * we did an unsolicited auto-sense. */ if (result == USB_STOR_TRANSPORT_GOOD && - (srb->sense_buffer[2] & 0xf) == 0x0) { + /* Filemark 0, ignore EOM, ILI 0, no sense */ + (srb->sense_buffer[2] & 0xaf) == 0 && + /* No ASC or ASCQ */ + srb->sense_buffer[12] == 0 && + srb->sense_buffer[13] == 0) { srb->result = SAM_STAT_GOOD; srb->sense_buffer[0] = 0x0; } @@ -809,15 +813,19 @@ int usb_stor_CBI_transport(Scsi_Cmnd *sr } /* If not UFI, we interpret the data as a result code - * The first byte should always be a 0x0 - * The second byte & 0x0F should be 0x0 for good, otherwise error + * The first byte should always be a 0x0. + * + * Some bogus devices don't follow that rule. They stuff the ASC + * into the first byte -- so if it's non-zero, call it a failure. */ if (us->iobuf[0]) { - US_DEBUGP("CBI IRQ data showed reserved bType %d\n", + US_DEBUGP("CBI IRQ data showed reserved bType 0x%x\n", us->iobuf[0]); - return USB_STOR_TRANSPORT_ERROR; + goto Failed; + } + /* The second byte & 0x0F should be 0x0 for good, otherwise error */ switch (us->iobuf[1] & 0x0F) { case 0x00: return USB_STOR_TRANSPORT_GOOD; --- linux-2.6.4-rc2/drivers/usb/storage/unusual_devs.h 2004-03-03 23:12:46.000000000 -0800 +++ 25/drivers/usb/storage/unusual_devs.h 2004-03-07 20:46:56.000000000 -0800 @@ -261,6 +261,14 @@ UNUSUAL_DEV( 0x054c, 0x0010, 0x0106, 0x US_SC_SCSI, US_PR_DEVICE, NULL, US_FL_SINGLE_LUN | US_FL_MODE_XLATE ), +/* This entry is needed because the device reports Sub=ff */ +UNUSUAL_DEV( 0x054c, 0x0010, 0x0500, 0x0500, + "Sony", + "DSC-T1", + US_SC_8070, US_PR_DEVICE, NULL, + US_FL_SINGLE_LUN | US_FL_MODE_XLATE ), + + /* Reported by wim@geeks.nl */ UNUSUAL_DEV( 0x054c, 0x0025, 0x0100, 0x0100, "Sony", @@ -368,7 +376,7 @@ UNUSUAL_DEV( 0x05ab, 0x5701, 0x0100, 0x UNUSUAL_DEV( 0x05dc, 0x0001, 0x0000, 0x0001, "Lexar", "Jumpshot USB CF Reader", - US_SC_SCSI, US_PR_JUMPSHOT, NULL, + US_SC_DEVICE, US_PR_JUMPSHOT, NULL, US_FL_MODE_XLATE ), #endif @@ -440,12 +448,6 @@ UNUSUAL_DEV( 0x0686, 0x4006, 0x0001, 0x0 US_SC_SCSI, US_PR_DEVICE, NULL, 0 ), -UNUSUAL_DEV( 0x0686, 0x400b, 0x0001, 0x0001, - "Minolta", - "DiMAGE 7i", - US_SC_SCSI, US_PR_DEVICE, NULL, - 0 ), - UNUSUAL_DEV( 0x0686, 0x400f, 0x0001, 0x0001, "Minolta", "DiMAGE 7Hi", @@ -619,6 +621,9 @@ UNUSUAL_DEV( 0x07c4, 0xa400, 0x0000, 0x * are using transport protocol CB. * - They don't like the INQUIRY command. So we must handle this command * of the SCSI layer ourselves. + * - Some cameras with idProduct=0x1001 and bcdDevice=0x1000 have + * bInterfaceProtocol=0x00 (US_PR_CBI) while others have 0x01 (US_PR_CB). + * So don't remove the US_PR_CB override! */ UNUSUAL_DEV( 0x07cf, 0x1001, 0x1000, 0x9009, "Casio", @@ -649,6 +654,17 @@ UNUSUAL_DEV( 0x08ca, 0x2011, 0x0000, 0x US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_MODE_XLATE ), +/* Entry needed for flags. Moreover, all devices with this ID use + * bulk-only transport, but _some_ falsely report Control/Bulk instead. + * One example is "Trumpion Digital Research MYMP3". + * Submitted by Bjoern Brill + */ +UNUSUAL_DEV( 0x090a, 0x1001, 0x0100, 0x0100, + "Trumpion", + "t33520 USB Flash Card Controller", + US_SC_DEVICE, US_PR_BULK, NULL, + US_FL_MODE_XLATE), + /* Trumpion Microelectronics MP3 player (felipe_alfaro@linuxmail.org) */ UNUSUAL_DEV( 0x090a, 0x1200, 0x0000, 0x9999, "Trumpion", @@ -688,15 +704,9 @@ UNUSUAL_DEV( 0x0a17, 0x0004, 0x1000, 0x1 US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY ), -/* This entry from in the Debian mailing list */ -UNUSUAL_DEV( 0x0a17, 0x0006, 0x0000, 0xffff, - "Pentax", - "Optio 330GS", - US_SC_8070, US_PR_CB, NULL, - US_FL_MODE_XLATE | US_FL_FIX_INQUIRY ), /* Submitted by Per Winkvist */ -UNUSUAL_DEV( 0x0a17, 0x006, 0x1000, 0x9009, +UNUSUAL_DEV( 0x0a17, 0x006, 0x0000, 0xffff, "Pentax", "Optio S/S4", US_SC_DEVICE, US_PR_DEVICE, NULL, --- linux-2.6.4-rc2/drivers/usb/storage/usb.c 2004-02-17 20:48:45.000000000 -0800 +++ 25/drivers/usb/storage/usb.c 2004-03-07 20:46:56.000000000 -0800 @@ -423,7 +423,7 @@ static int associate_dev(struct us_data /* Fill in the device-related fields */ us->pusb_dev = interface_to_usbdev(intf); us->pusb_intf = intf; - us->ifnum = intf->altsetting->desc.bInterfaceNumber; + us->ifnum = intf->cur_altsetting->desc.bInterfaceNumber; /* Store our private data in the interface and increment the * device's reference count */ @@ -452,7 +452,7 @@ static void get_device_info(struct us_da { struct usb_device *dev = us->pusb_dev; struct usb_interface_descriptor *idesc = - &us->pusb_intf->altsetting[us->pusb_intf->act_altsetting].desc; + &us->pusb_intf->cur_altsetting->desc; struct us_unusual_dev *unusual_dev = &us_unusual_dev_list[id_index]; struct usb_device_id *id = &storage_usb_ids[id_index]; @@ -686,7 +686,7 @@ static int get_protocol(struct us_data * static int get_pipes(struct us_data *us) { struct usb_host_interface *altsetting = - &us->pusb_intf->altsetting[us->pusb_intf->act_altsetting]; + us->pusb_intf->cur_altsetting; int i; struct usb_endpoint_descriptor *ep; struct usb_endpoint_descriptor *ep_in = NULL; @@ -877,8 +877,9 @@ static int storage_probe(struct usb_inte int result; US_DEBUGP("USB Mass Storage device detected\n"); - US_DEBUGP("act_altsetting is %d, id_index is %d\n", - intf->act_altsetting, id_index); + US_DEBUGP("altsetting is %d, id_index is %d\n", + intf->cur_altsetting->desc.bAlternateSetting, + id_index); /* Allocate the us_data structure and initialize the mutexes */ us = (struct us_data *) kmalloc(sizeof(*us), GFP_KERNEL); @@ -954,8 +955,6 @@ static int storage_probe(struct usb_inte scsi_scan_host(us->host); printk(KERN_DEBUG - "WARNING: USB Mass Storage data integrity not assured\n"); - printk(KERN_DEBUG "USB Mass Storage device found at %d\n", us->pusb_dev->devnum); return 0; --- linux-2.6.4-rc2/drivers/usb/storage/usb.h 2003-09-08 13:58:58.000000000 -0700 +++ 25/drivers/usb/storage/usb.h 2004-03-07 20:46:56.000000000 -0800 @@ -176,6 +176,5 @@ extern void fill_inquiry_response(struct * single queue element srb for write access */ #define scsi_unlock(host) spin_unlock_irq(host->host_lock) #define scsi_lock(host) spin_lock_irq(host->host_lock) -#define sg_address(psg) (page_address((psg).page) + (psg).offset) #endif --- linux-2.6.4-rc2/drivers/video/aty/radeon_base.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/drivers/video/aty/radeon_base.c 2004-03-07 20:46:46.000000000 -0800 @@ -135,7 +135,7 @@ static struct pci_device_id radeonfb_pci CHIP_DEF(PCI_CHIP_R200_QM, R200, CHIP_HAS_CRTC2), /* Mobility M7 */ CHIP_DEF(PCI_CHIP_RADEON_LW, RV200, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY), - CHIP_DEF(PCI_CHIP_RADEON_LW, RV200, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY), + CHIP_DEF(PCI_CHIP_RADEON_LX, RV200, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY), /* 7500 */ CHIP_DEF(PCI_CHIP_RV200_QW, RV200, CHIP_HAS_CRTC2), CHIP_DEF(PCI_CHIP_RV200_QX, RV200, CHIP_HAS_CRTC2), --- linux-2.6.4-rc2/drivers/video/console/fbcon.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/drivers/video/console/fbcon.c 2004-03-07 20:47:40.000000000 -0800 @@ -2345,6 +2345,7 @@ int __init fb_console_init(void) { if (!num_registered_fb) return -ENODEV; + take_over_console(&fb_con, first_fb_vc, last_fb_vc, fbcon_is_default); acquire_console_sem(); if (!fbcon_event_notifier_registered) { @@ -2352,10 +2353,11 @@ int __init fb_console_init(void) fbcon_event_notifier_registered = 1; } release_console_sem(); - return 0; } +#ifdef MODULE + void __exit fb_console_exit(void) { acquire_console_sem(); @@ -2370,6 +2372,8 @@ void __exit fb_console_exit(void) module_init(fb_console_init); module_exit(fb_console_exit); +#endif + /* * Visible symbols for modules */ --- linux-2.6.4-rc2/drivers/video/ffb.c 2003-08-22 19:23:42.000000000 -0700 +++ 25/drivers/video/ffb.c 2004-03-07 20:46:46.000000000 -0800 @@ -466,6 +466,7 @@ static __inline__ void ffb_rop(struct ff static void ffb_switch_from_graph(struct ffb_par *par) { struct ffb_fbc *fbc = par->fbc; + struct ffb_dac *dac = par->dac; unsigned long flags; spin_lock_irqsave(&par->lock, flags); @@ -482,6 +483,14 @@ static void ffb_switch_from_graph(struct upa_writel(par->fg_cache, &fbc->fg); upa_writel(par->bg_cache, &fbc->bg); FFBWait(par); + + /* Disable cursor. */ + upa_writel(0x100, &dac->type2); + if (par->dac_rev <= 2) + upa_writel(0, &dac->value2); + else + upa_writel(3, &dac->value2); + spin_unlock_irqrestore(&par->lock, flags); } --- linux-2.6.4-rc2/drivers/video/i810/i810_main.h 2003-09-27 18:57:46.000000000 -0700 +++ 25/drivers/video/i810/i810_main.h 2004-03-07 20:48:08.000000000 -0800 @@ -84,7 +84,7 @@ extern void i810fb_init_ringbuffer(struc extern void i810fb_load_front (u32 offset, struct fb_info *info); /* Conditionals */ -#if defined(__i386__) +#ifdef CONFIG_X86 inline void flush_cache(void) { asm volatile ("wbinvd":::"memory"); --- linux-2.6.4-rc2/fs/adfs/super.c 2003-10-08 15:07:09.000000000 -0700 +++ 25/fs/adfs/super.c 2004-03-07 20:47:08.000000000 -0800 @@ -333,6 +333,9 @@ static int adfs_fill_super(struct super_ struct object_info root_obj; unsigned char *b_data; struct adfs_sb_info *asb; + struct inode *root; + + sb->s_flags |= MS_NODIRATIME; asb = kmalloc(sizeof(*asb), GFP_KERNEL); if (!asb) @@ -443,10 +446,11 @@ static int adfs_fill_super(struct super_ asb->s_namelen = ADFS_F_NAME_LEN; } - sb->s_root = d_alloc_root(adfs_iget(sb, &root_obj)); + root = adfs_iget(sb, &root_obj); + sb->s_root = d_alloc_root(root); if (!sb->s_root) { int i; - + iput(root); for (i = 0; i < asb->s_map_size; i++) brelse(asb->s_map[i].dm_bh); kfree(asb->s_map); --- linux-2.6.4-rc2/fs/affs/super.c 2003-10-08 15:07:09.000000000 -0700 +++ 25/fs/affs/super.c 2004-03-07 20:47:08.000000000 -0800 @@ -293,6 +293,7 @@ static int affs_fill_super(struct super_ sb->s_magic = AFFS_SUPER_MAGIC; sb->s_op = &affs_sops; + sb->s_flags |= MS_NODIRATIME; sbi = kmalloc(sizeof(struct affs_sb_info), GFP_KERNEL); if (!sbi) --- linux-2.6.4-rc2/fs/afs/inode.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/fs/afs/inode.c 2004-03-07 20:47:08.000000000 -0800 @@ -188,6 +188,7 @@ inline int afs_iget(struct super_block * #endif /* okay... it's a new inode */ + inode->i_flags |= S_NOATIME; vnode->flags |= AFS_VNODE_CHANGED; ret = afs_inode_fetch_status(inode); if (ret<0) --- linux-2.6.4-rc2/fs/afs/super.c 2004-02-03 20:42:38.000000000 -0800 +++ 25/fs/afs/super.c 2004-03-07 20:47:55.000000000 -0800 @@ -53,6 +53,7 @@ static struct file_system_type afs_fs_ty .name = "afs", .get_sb = afs_get_sb, .kill_sb = kill_anon_super, + .fs_flags = FS_BINARY_MOUNTDATA, }; static struct super_operations afs_super_ops = { @@ -280,7 +281,6 @@ static int afs_fill_super(struct super_b return 0; error: - dput(root); iput(inode); afs_put_volume(as->volume); kfree(as); --- linux-2.6.4-rc2/fs/aio.c 2003-11-23 19:03:01.000000000 -0800 +++ 25/fs/aio.c 2004-03-07 20:46:46.000000000 -0800 @@ -312,7 +312,7 @@ void wait_for_all_aios(struct kioctx *ct /* wait_on_sync_kiocb: * Waits on the given sync kiocb to complete. */ -ssize_t wait_on_sync_kiocb(struct kiocb *iocb) +ssize_t fastcall wait_on_sync_kiocb(struct kiocb *iocb) { while (iocb->ki_users) { set_current_state(TASK_UNINTERRUPTIBLE); @@ -331,7 +331,7 @@ ssize_t wait_on_sync_kiocb(struct kiocb * go away, they will call put_ioctx and release any pinned memory * associated with the request (held via struct page * references). */ -void exit_aio(struct mm_struct *mm) +void fastcall exit_aio(struct mm_struct *mm) { struct kioctx *ctx = mm->ioctx_list; mm->ioctx_list = NULL; @@ -356,7 +356,7 @@ void exit_aio(struct mm_struct *mm) * Called when the last user of an aio context has gone away, * and the struct needs to be freed. */ -void __put_ioctx(struct kioctx *ctx) +void fastcall __put_ioctx(struct kioctx *ctx) { unsigned nr_events = ctx->max_reqs; @@ -383,7 +383,7 @@ void __put_ioctx(struct kioctx *ctx) * req (after submitting it) and aio_complete() freeing the req. */ static struct kiocb *FASTCALL(__aio_get_req(struct kioctx *ctx)); -static struct kiocb *__aio_get_req(struct kioctx *ctx) +static struct kiocb fastcall *__aio_get_req(struct kioctx *ctx) { struct kiocb *req = NULL; struct aio_ring *ring; @@ -509,7 +509,7 @@ static int __aio_put_req(struct kioctx * * Returns true if this put was the last user of the kiocb, * false if the request is still in use. */ -int aio_put_req(struct kiocb *req) +int fastcall aio_put_req(struct kiocb *req) { struct kioctx *ctx = req->ki_ctx; int ret; @@ -596,7 +596,7 @@ static void aio_kick_handler(void *data) unuse_mm(ctx->mm); } -void kick_iocb(struct kiocb *iocb) +void fastcall kick_iocb(struct kiocb *iocb) { struct kioctx *ctx = iocb->ki_ctx; @@ -622,7 +622,7 @@ void kick_iocb(struct kiocb *iocb) * Returns true if this is the last user of the request. The * only other user of the request can be the cancellation code. */ -int aio_complete(struct kiocb *iocb, long res, long res2) +int fastcall aio_complete(struct kiocb *iocb, long res, long res2) { struct kioctx *ctx = iocb->ki_ctx; struct aio_ring_info *info; @@ -985,7 +985,7 @@ asmlinkage long sys_io_destroy(aio_conte return -EINVAL; } -int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, +int fastcall io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, struct iocb *iocb) { struct kiocb *req; --- linux-2.6.4-rc2/fs/autofs4/inode.c 2003-10-08 15:07:09.000000000 -0700 +++ 25/fs/autofs4/inode.c 2004-03-07 20:46:46.000000000 -0800 @@ -213,6 +213,9 @@ int autofs4_fill_super(struct super_bloc * Get the root inode and dentry, but defer checking for errors. */ root_inode = autofs4_get_inode(s, autofs4_mkroot(sbi)); + if (!root_inode) + goto fail_free; + root_inode->i_op = &autofs4_root_inode_operations; root_inode->i_fop = &autofs4_root_operations; root = d_alloc_root(root_inode); @@ -264,22 +267,13 @@ int autofs4_fill_super(struct super_bloc */ fail_fput: printk("autofs: pipe file descriptor does not contain proper ops\n"); - /* - * fput() can block, so we clear the super block first. - */ fput(pipe); /* fall through */ fail_dput: - /* - * dput() can block, so we clear the super block first. - */ dput(root); goto fail_free; fail_iput: printk("autofs: get root dentry failed\n"); - /* - * iput() can block, so we clear the super block first. - */ iput(root_inode); fail_free: kfree(sbi); --- linux-2.6.4-rc2/fs/befs/linuxvfs.c 2003-10-25 14:45:46.000000000 -0700 +++ 25/fs/befs/linuxvfs.c 2004-03-07 20:46:46.000000000 -0800 @@ -789,6 +789,7 @@ befs_fill_super(struct super_block *sb, struct buffer_head *bh; befs_sb_info *befs_sb; befs_super_block *disk_sb; + struct inode *root; const unsigned long sb_block = 0; const off_t x86_sb_off = 512; @@ -863,9 +864,10 @@ befs_fill_super(struct super_block *sb, /* Set real blocksize of fs */ sb_set_blocksize(sb, (ulong) befs_sb->block_size); sb->s_op = (struct super_operations *) &befs_sops; - sb->s_root = - d_alloc_root(iget(sb, iaddr2blockno(sb, &(befs_sb->root_dir)))); + root = iget(sb, iaddr2blockno(sb, &(befs_sb->root_dir))); + sb->s_root = d_alloc_root(root); if (!sb->s_root) { + iput(root); befs_error(sb, "get root inode failed"); goto unaquire_priv_sbp; } --- linux-2.6.4-rc2/fs/bfs/dir.c 2003-07-10 18:50:31.000000000 -0700 +++ 25/fs/bfs/dir.c 2004-03-07 20:47:08.000000000 -0800 @@ -65,7 +65,6 @@ static int bfs_readdir(struct file * f, brelse(bh); } - update_atime(dir); unlock_kernel(); return 0; } --- linux-2.6.4-rc2/fs/binfmt_aout.c 2003-09-27 18:57:46.000000000 -0700 +++ 25/fs/binfmt_aout.c 2004-03-07 20:47:37.000000000 -0800 @@ -309,7 +309,7 @@ static int load_aout_binary(struct linux (current->mm->start_brk = N_BSSADDR(ex)); current->mm->free_area_cache = TASK_UNMAPPED_BASE; - current->mm->rss = 0; + zero_rss(current->mm); current->mm->mmap = NULL; compute_creds(bprm); current->flags &= ~PF_FORKNOEXEC; --- linux-2.6.4-rc2/fs/binfmt_elf.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/fs/binfmt_elf.c 2004-03-07 20:48:05.000000000 -0800 @@ -672,7 +672,7 @@ static int load_elf_binary(struct linux_ /* Do this so that we can load the interpreter, if need be. We will change some of these later */ - current->mm->rss = 0; + zero_rss(current->mm); current->mm->free_area_cache = TASK_UNMAPPED_BASE; retval = setup_arg_pages(bprm); if (retval < 0) { @@ -830,9 +830,8 @@ static int load_elf_binary(struct linux_ and some applications "depend" upon this behavior. Since we do not have the power to recompile these, we emulate the SVr4 behavior. Sigh. */ - /* N.B. Shouldn't the size here be PAGE_SIZE?? */ down_write(¤t->mm->mmap_sem); - error = do_mmap(NULL, 0, 4096, PROT_READ | PROT_EXEC, + error = do_mmap(NULL, 0, PAGE_SIZE, PROT_READ | PROT_EXEC, MAP_FIXED | MAP_PRIVATE, 0); up_write(¤t->mm->mmap_sem); } --- linux-2.6.4-rc2/fs/binfmt_flat.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/fs/binfmt_flat.c 2004-03-07 20:47:37.000000000 -0800 @@ -651,7 +651,7 @@ static int load_flat_file(struct linux_b current->mm->start_brk = datapos + data_len + bss_len; current->mm->brk = (current->mm->start_brk + 3) & ~3; current->mm->context.end_brk = memp + ksize((void *) memp) - stack_len; - current->mm->rss = 0; + zero_rss(current->mm); } if (flags & FLAT_FLAG_KTRACE) --- linux-2.6.4-rc2/fs/binfmt_misc.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/fs/binfmt_misc.c 2004-03-07 20:47:35.000000000 -0800 @@ -39,6 +39,8 @@ static int enabled = 1; enum {Enabled, Magic}; #define MISC_FMT_PRESERVE_ARGV0 (1<<31) +#define MISC_FMT_OPEN_BINARY (1<<30) +#define MISC_FMT_CREDENTIALS (1<<29) typedef struct { struct list_head list; @@ -102,10 +104,15 @@ static Node *check_file(struct linux_bin static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs) { Node *fmt; - struct file * file; + struct file * interp_file = NULL; + struct file * binary_file = NULL; char iname[BINPRM_BUF_SIZE]; char *iname_addr = iname; int retval; + int fd_binary = -1; + char fd_str[32]; + char * fdsp = fd_str; + int is_open_bin; retval = -ENOEXEC; if (!enabled) @@ -120,33 +127,105 @@ static int load_misc_binary(struct linux if (!fmt) goto _ret; - allow_write_access(bprm->file); - fput(bprm->file); - bprm->file = NULL; + is_open_bin = (fmt->flags & MISC_FMT_OPEN_BINARY) ? 1 : 0; + + if (is_open_bin) { + /* if the binary should be opened on behalf of the + * interpreter than keep it open and assign descriptor + * to it */ + fd_binary = get_unused_fd (); + if (fd_binary < 0) { + retval = fd_binary; + goto _ret; + } + snprintf (fd_str, sizeof(fd_str) - 1, "%d", fd_binary); + } else { + allow_write_access (bprm->file); + fput (bprm->file); + bprm->file = NULL; + } /* Build args for interpreter */ if (!(fmt->flags & MISC_FMT_PRESERVE_ARGV0)) { remove_arg_zero(bprm); } - retval = copy_strings_kernel(1, &bprm->interp, bprm); - if (retval < 0) goto _ret; - bprm->argc++; - retval = copy_strings_kernel(1, &iname_addr, bprm); - if (retval < 0) goto _ret; - bprm->argc++; + + if (is_open_bin) { + /* make argv[1] be the file descriptor of the binary */ + retval = copy_strings_kernel (1, &fdsp, bprm); + } else { + /* make argv[1] be the path to the binary */ + retval = copy_strings_kernel (1, &bprm->interp, bprm); + } + if (retval < 0) + goto _error; + bprm->argc ++; + retval = copy_strings_kernel (1, &iname_addr, bprm); + if (retval < 0) + goto _error; + bprm->argc ++; bprm->interp = iname; /* for binfmt_script */ - file = open_exec(iname); - retval = PTR_ERR(file); - if (IS_ERR(file)) - goto _ret; - bprm->file = file; + interp_file = open_exec (iname); + retval = PTR_ERR (interp_file); + if (IS_ERR (interp_file)) + goto _error; + + + binary_file = bprm->file; + if (fmt->flags & MISC_FMT_CREDENTIALS) { + /* + * Call prepare_binprm before switching to interpreter's file + * so that all security calculation will be done according to + * binary and not interpreter + */ + retval = prepare_binprm(bprm); + if (retval < 0) + goto _error; + bprm->file = interp_file; + memset(bprm->buf, 0, BINPRM_BUF_SIZE); + retval = kernel_read(bprm->file, 0, bprm->buf, BINPRM_BUF_SIZE); + } else { + bprm->file = interp_file; + retval = prepare_binprm (bprm); + } + + if (retval < 0) + goto _error; + + if (is_open_bin) { + /* if the binary is not readable than enforce mm->dumpable=0 + regardless of the interpreter's permissions */ + if (permission (binary_file->f_dentry->d_inode, MAY_READ, NULL)) { + bprm->interp_flags |= BINPRM_FLAGS_ENFORCE_NONDUMP; + } + /* install the binary's fd. it is done at the latest possible point + * because once it is installed it will need to be sys_close()ed + * in case of error. + */ + fd_install (fd_binary, binary_file); + } + + retval = search_binary_handler (bprm, regs); + + if (retval < 0) + goto _error_close_file; - retval = prepare_binprm(bprm); - if (retval >= 0) - retval = search_binary_handler(bprm, regs); _ret: return retval; + +_error_close_file: + if (fd_binary > 0) { + sys_close (fd_binary); + fd_binary = -1; + bprm->file = NULL; + } +_error: + if (fd_binary > 0) + put_unused_fd (fd_binary); + bprm->interp_flags = 0; + goto _ret; + } /* Command parsers */ @@ -191,6 +270,36 @@ static int unquote(char *from) return p - from; } +static inline char * check_special_flags (char * sfs, Node * e) +{ + char * p = sfs; + int cont = 1; + + /* special flags */ + while (cont) { + switch (*p) { + case 'P': + p++; + e->flags |= MISC_FMT_PRESERVE_ARGV0; + break; + case 'O': + p++; + e->flags |= MISC_FMT_OPEN_BINARY; + break; + case 'C': + p++; + /* this flags also implies the + open-binary flag */ + e->flags |= (MISC_FMT_CREDENTIALS | + MISC_FMT_OPEN_BINARY); + break; + default: + cont = 0; + } + } + + return p; +} /* * This registers a new binary format, it recognises the syntax * ':name:type:offset:magic:mask:interpreter:' @@ -293,10 +402,8 @@ static Node *create_entry(const char *bu if (!e->interpreter[0]) goto Einval; - if (*p == 'P') { - p++; - e->flags |= MISC_FMT_PRESERVE_ARGV0; - } + + p = check_special_flags (p, e); if (*p == '\n') p++; @@ -346,6 +453,7 @@ static void entry_status(Node *e, char * { char *dp; char *status = "disabled"; + const char * flags = "flags: "; if (test_bit(Enabled, &e->flags)) status = "enabled"; @@ -357,6 +465,22 @@ static void entry_status(Node *e, char * sprintf(page, "%s\ninterpreter %s\n", status, e->interpreter); dp = page + strlen(page); + + /* print the special flags */ + sprintf (dp, "%s", flags); + dp += strlen (flags); + if (e->flags & MISC_FMT_PRESERVE_ARGV0) { + *dp ++ = 'P'; + } + if (e->flags & MISC_FMT_OPEN_BINARY) { + *dp ++ = 'O'; + } + if (e->flags & MISC_FMT_CREDENTIALS) { + *dp ++ = 'C'; + } + *dp ++ = '\n'; + + if (!test_bit(Magic, &e->flags)) { sprintf(dp, "extension .%s\n", e->magic); } else { --- linux-2.6.4-rc2/fs/binfmt_som.c 2004-01-09 00:04:32.000000000 -0800 +++ 25/fs/binfmt_som.c 2004-03-07 20:47:37.000000000 -0800 @@ -259,7 +259,7 @@ load_som_binary(struct linux_binprm * bp create_som_tables(bprm); current->mm->start_stack = bprm->p; - current->mm->rss = 0; + zero_rss(current->mm); #if 0 printk("(start_brk) %08lx\n" , (unsigned long) current->mm->start_brk); --- linux-2.6.4-rc2/fs/block_dev.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/fs/block_dev.c 2004-03-07 20:48:23.000000000 -0800 @@ -146,8 +146,8 @@ blkdev_direct_IO(int rw, struct kiocb *i struct file *file = iocb->ki_filp; struct inode *inode = file->f_mapping->host; - return blockdev_direct_IO(rw, iocb, inode, I_BDEV(inode), iov, offset, - nr_segs, blkdev_get_blocks, NULL); + return blockdev_direct_IO_no_locking(rw, iocb, inode, I_BDEV(inode), + iov, offset, nr_segs, blkdev_get_blocks, NULL); } static int blkdev_writepage(struct page *page, struct writeback_control *wbc) @@ -522,7 +522,7 @@ int check_disk_change(struct block_devic EXPORT_SYMBOL(check_disk_change); -static void bd_set_size(struct block_device *bdev, loff_t size) +void bd_set_size(struct block_device *bdev, loff_t size) { unsigned bsize = bdev_hardsect_size(bdev); @@ -535,6 +535,7 @@ static void bd_set_size(struct block_dev bdev->bd_block_size = bsize; bdev->bd_inode->i_blkbits = blksize_bits(bsize); } +EXPORT_SYMBOL(bd_set_size); static int do_open(struct block_device *bdev, struct file *file) { @@ -786,7 +787,7 @@ struct file_operations def_blk_fops = { .fsync = block_fsync, .ioctl = block_ioctl, .readv = generic_file_readv, - .writev = generic_file_writev, + .writev = generic_file_write_nolock, .sendfile = generic_file_sendfile, }; --- linux-2.6.4-rc2/fs/buffer.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/fs/buffer.c 2004-03-07 20:48:23.000000000 -0800 @@ -97,7 +97,7 @@ void wake_up_buffer(struct buffer_head * } EXPORT_SYMBOL(wake_up_buffer); -void unlock_buffer(struct buffer_head *bh) +void fastcall unlock_buffer(struct buffer_head *bh) { /* * unlock_buffer against a zero-count bh is a bug, if the page @@ -404,7 +404,7 @@ __find_get_block_slow(struct block_devic struct inode *bd_inode = bdev->bd_inode; struct address_space *bd_mapping = bd_inode->i_mapping; struct buffer_head *ret = NULL; - unsigned long index; + pgoff_t index; struct buffer_head *bh; struct buffer_head *head; struct page *page; @@ -431,6 +431,7 @@ __find_get_block_slow(struct block_devic printk("block=%llu, b_blocknr=%llu\n", (unsigned long long)block, (unsigned long long)bh->b_blocknr); printk("b_state=0x%08lx, b_size=%u\n", bh->b_state, bh->b_size); + printk("device blocksize: %d\n", 1 << bd_inode->i_blkbits); out_unlock: spin_unlock(&bd_mapping->private_lock); page_cache_release(page); @@ -857,10 +858,13 @@ int __set_page_dirty_buffers(struct page struct buffer_head *bh = head; do { - if (buffer_uptodate(bh)) + if (buffer_uptodate(bh)) { set_buffer_dirty(bh); - else + if (unlikely(block_dump)) + printk("%s(%d): dirtied buffer\n", current->comm, current->pid); + } else { buffer_error(); + } bh = bh->b_this_page; } while (bh != head); } @@ -1093,7 +1097,7 @@ link_dev_buffers(struct page *page, stru */ static void init_page_buffers(struct page *page, struct block_device *bdev, - int block, int size) + sector_t block, int size) { struct buffer_head *head = page_buffers(page); struct buffer_head *bh = head; @@ -1121,8 +1125,8 @@ init_page_buffers(struct page *page, str * This is user purely for blockdev mappings. */ static struct page * -grow_dev_page(struct block_device *bdev, unsigned long block, - unsigned long index, int size) +grow_dev_page(struct block_device *bdev, sector_t block, + pgoff_t index, int size) { struct inode *inode = bdev->bd_inode; struct page *page; @@ -1178,10 +1182,10 @@ failed: * grow_dev_page() will go BUG() if this happens. */ static inline int -grow_buffers(struct block_device *bdev, unsigned long block, int size) +grow_buffers(struct block_device *bdev, sector_t block, int size) { struct page *page; - unsigned long index; + pgoff_t index; int sizebits; /* Size must be multiple of hard sectorsize */ @@ -1256,7 +1260,7 @@ __getblk_slow(struct block_device *bdev, * mark_buffer_dirty() is atomic. It takes bh->b_page->mapping->private_lock, * mapping->page_lock and the global inode_lock. */ -void mark_buffer_dirty(struct buffer_head *bh) +void fastcall mark_buffer_dirty(struct buffer_head *bh) { if (!buffer_uptodate(bh)) buffer_error(); @@ -1738,8 +1742,8 @@ static int __block_write_full_page(struc get_block_t *get_block, struct writeback_control *wbc) { int err; - unsigned long block; - unsigned long last_block; + sector_t block; + sector_t last_block; struct buffer_head *bh, *head; int nr_underway = 0; @@ -1806,23 +1810,23 @@ static int __block_write_full_page(struc do { get_bh(bh); - if (buffer_mapped(bh) && buffer_dirty(bh)) { - if (wbc->sync_mode != WB_SYNC_NONE) { - lock_buffer(bh); - } else { - if (test_set_buffer_locked(bh)) { - __set_page_dirty_nobuffers(page); - continue; - } - } - if (test_clear_buffer_dirty(bh)) { - if (!buffer_uptodate(bh)) - buffer_error(); - mark_buffer_async_write(bh); - } else { - unlock_buffer(bh); + if (!buffer_mapped(bh)) + continue; + if (wbc->sync_mode != WB_SYNC_NONE) { + lock_buffer(bh); + } else { + if (test_set_buffer_locked(bh)) { + __set_page_dirty_nobuffers(page); + continue; } } + if (test_clear_buffer_dirty(bh)) { + if (!buffer_uptodate(bh)) + buffer_error(); + mark_buffer_async_write(bh); + } else { + unlock_buffer(bh); + } } while ((bh = bh->b_this_page) != head); BUG_ON(PageWriteback(page)); @@ -2207,7 +2211,7 @@ int cont_prepare_write(struct page *page struct address_space *mapping = page->mapping; struct inode *inode = mapping->host; struct page *new_page; - unsigned long pgpos; + pgoff_t pgpos; long status; unsigned zerofrom; unsigned blocksize = 1 << inode->i_blkbits; @@ -2317,6 +2321,28 @@ int generic_commit_write(struct file *fi return 0; } + +/* + * nobh_prepare_write()'s prereads are special: the buffer_heads are freed + * immediately, while under the page lock. So it needs a special end_io + * handler which does not touch the bh after unlocking it. + * + * Note: unlock_buffer() sort-of does touch the bh after unlocking it, but + * a race there is benign: unlock_buffer() only use the bh's address for + * hashing after unlocking the buffer, so it doesn't actually touch the bh + * itself. + */ +static void end_buffer_read_nobh(struct buffer_head *bh, int uptodate) +{ + if (uptodate) { + set_buffer_uptodate(bh); + } else { + /* This happens, due to failed READA attempts. */ + clear_buffer_uptodate(bh); + } + unlock_buffer(bh); +} + /* * On entry, the page is fully not uptodate. * On exit the page is fully uptodate in the areas outside (from,to) @@ -2408,12 +2434,25 @@ int nobh_prepare_write(struct page *page } if (nr_reads) { - ll_rw_block(READ, nr_reads, read_bh); + struct buffer_head *bh; + + /* + * The page is locked, so these buffers are protected from + * any VM or truncate activity. Hence we don't need to care + * for the buffer_head refcounts. + */ for (i = 0; i < nr_reads; i++) { - wait_on_buffer(read_bh[i]); - if (!buffer_uptodate(read_bh[i])) + bh = read_bh[i]; + lock_buffer(bh); + bh->b_end_io = end_buffer_read_nobh; + submit_bh(READ, bh); + } + for (i = 0; i < nr_reads; i++) { + bh = read_bh[i]; + wait_on_buffer(bh); + if (!buffer_uptodate(bh)) ret = -EIO; - free_buffer_head(read_bh[i]); + free_buffer_head(bh); read_bh[i] = NULL; } if (ret) @@ -2512,9 +2551,11 @@ EXPORT_SYMBOL(nobh_truncate_page); int block_truncate_page(struct address_space *mapping, loff_t from, get_block_t *get_block) { - unsigned long index = from >> PAGE_CACHE_SHIFT; + pgoff_t index = from >> PAGE_CACHE_SHIFT; unsigned offset = from & (PAGE_CACHE_SIZE-1); - unsigned blocksize, iblock, length, pos; + unsigned blocksize; + pgoff_t iblock; + unsigned length, pos; struct inode *inode = mapping->host; struct page *page; struct buffer_head *bh; @@ -2594,7 +2635,7 @@ int block_write_full_page(struct page *p { struct inode * const inode = page->mapping->host; loff_t i_size = i_size_read(inode); - const unsigned long end_index = i_size >> PAGE_CACHE_SHIFT; + const pgoff_t end_index = i_size >> PAGE_CACHE_SHIFT; unsigned offset; void *kaddr; @@ -2987,6 +3028,26 @@ init_buffer_head(void *data, kmem_cache_ } } +#ifdef CONFIG_HOTPLUG_CPU +static void buffer_exit_cpu(int cpu) +{ + int i; + struct bh_lru *b = &per_cpu(bh_lrus, cpu); + + for (i = 0; i < BH_LRU_SIZE; i++) { + brelse(b->bhs[i]); + b->bhs[i] = NULL; + } +} + +static int buffer_cpu_notify(struct notifier_block *self, + unsigned long action, void *hcpu) +{ + if (action == CPU_DEAD) + buffer_exit_cpu((unsigned long)hcpu); + return NOTIFY_OK; +} +#endif /* CONFIG_HOTPLUG_CPU */ void __init buffer_init(void) { @@ -3004,6 +3065,7 @@ void __init buffer_init(void) */ nrpages = (nr_free_buffer_pages() * 10) / 100; max_buffer_heads = nrpages * (PAGE_SIZE / sizeof(struct buffer_head)); + hotcpu_notifier(buffer_cpu_notify, 0); } EXPORT_SYMBOL(__bforget); --- linux-2.6.4-rc2/fs/cifs/cifsfs.c 2003-10-25 14:45:46.000000000 -0700 +++ 25/fs/cifs/cifsfs.c 2004-03-07 20:47:08.000000000 -0800 @@ -77,6 +77,7 @@ cifs_read_super(struct super_block *sb, struct cifs_sb_info *cifs_sb; int rc = 0; + sb->s_flags |= MS_NODIRATIME; /* and probably even noatime */ sb->s_fs_info = kmalloc(sizeof(struct cifs_sb_info),GFP_KERNEL); cifs_sb = CIFS_SB(sb); if(cifs_sb == NULL) --- linux-2.6.4-rc2/fs/coda/dir.c 2003-09-27 18:57:46.000000000 -0700 +++ 25/fs/coda/dir.c 2004-03-07 20:47:08.000000000 -0800 @@ -510,8 +510,10 @@ int coda_readdir(struct file *coda_file, goto out; ret = -ENOENT; - if (!IS_DEADDIR(host_inode)) + if (!IS_DEADDIR(host_inode)) { ret = host_file->f_op->readdir(host_file, filldir, dirent); + update_atime(host_inode); + } } out: coda_file->f_pos = host_file->f_pos; --- linux-2.6.4-rc2/fs/coda/inode.c 2003-09-27 18:57:46.000000000 -0700 +++ 25/fs/coda/inode.c 2004-03-07 20:47:55.000000000 -0800 @@ -171,6 +171,7 @@ static int coda_fill_super(struct super_ sbi->sbi_vcomm = vc; sb->s_fs_info = sbi; + sb->s_flags |= MS_NODIRATIME; /* probably even noatime */ sb->s_blocksize = 1024; /* XXXXX what do we put here?? */ sb->s_blocksize_bits = 10; sb->s_magic = CODA_SUPER_MAGIC; @@ -195,6 +196,8 @@ static int coda_fill_super(struct super_ printk("coda_read_super: rootinode is %ld dev %s\n", root->i_ino, root->i_sb->s_id); sb->s_root = d_alloc_root(root); + if (!sb->s_root) + goto error; return 0; error: @@ -306,5 +309,6 @@ struct file_system_type coda_fs_type = { .name = "coda", .get_sb = coda_get_sb, .kill_sb = kill_anon_super, + .fs_flags = FS_BINARY_MOUNTDATA, }; --- linux-2.6.4-rc2/fs/cramfs/inode.c 2004-02-03 20:42:38.000000000 -0800 +++ 25/fs/cramfs/inode.c 2004-03-07 20:47:08.000000000 -0800 @@ -199,6 +199,9 @@ static int cramfs_fill_super(struct supe struct cramfs_super super; unsigned long root_offset; struct cramfs_sb_info *sbi; + struct inode *root; + + sb->s_flags |= MS_RDONLY; sbi = kmalloc(sizeof(struct cramfs_sb_info), GFP_KERNEL); if (!sbi) @@ -263,7 +266,14 @@ static int cramfs_fill_super(struct supe /* Set it all up.. */ sb->s_op = &cramfs_ops; - sb->s_root = d_alloc_root(get_cramfs_inode(sb, &super.root)); + root = get_cramfs_inode(sb, &super.root); + if (!root) + goto out; + sb->s_root = d_alloc_root(root); + if (!sb->s_root) { + iput(root); + goto out; + } return 0; out: kfree(sbi); --- linux-2.6.4-rc2/fs/direct-io.c 2004-01-09 00:04:32.000000000 -0800 +++ 25/fs/direct-io.c 2004-03-07 22:11:31.000000000 -0800 @@ -52,6 +52,10 @@ * * If blkfactor is zero then the user's request was aligned to the filesystem's * blocksize. + * + * needs_locking is set for regular files on direct-IO-naive filesystems. It + * determines whether we need to do the fancy locking which prevents direct-IO + * from being able to read uninitialised disk blocks. */ struct dio { @@ -59,6 +63,7 @@ struct dio { struct bio *bio; /* bio under assembly */ struct inode *inode; int rw; + int needs_locking; /* doesn't change */ unsigned blkbits; /* doesn't change */ unsigned blkfactor; /* When we're using an alignment which is finer than the filesystem's soft @@ -69,6 +74,7 @@ struct dio { been performed at the start of a write */ int pages_in_io; /* approximate total IO pages */ + size_t size; /* total request size (doesn't change)*/ sector_t block_in_file; /* Current offset into the underlying file in dio_block units. */ unsigned blocks_available; /* At block_in_file. changes */ @@ -110,9 +116,9 @@ struct dio { int page_errors; /* errno from get_user_pages() */ /* BIO completion state */ - atomic_t bio_count; /* nr bios to be completed */ - atomic_t bios_in_flight; /* nr bios in flight */ - spinlock_t bio_list_lock; /* protects bio_list */ + spinlock_t bio_lock; /* protects BIO fields below */ + int bio_count; /* nr bios to be completed */ + int bios_in_flight; /* nr bios in flight */ struct bio *bio_list; /* singly linked via bi_private */ struct task_struct *waiter; /* waiting task (NULL if none) */ @@ -204,8 +210,10 @@ static struct page *dio_get_page(struct */ static void dio_complete(struct dio *dio, loff_t offset, ssize_t bytes) { - if (dio->end_io) + if (dio->end_io && dio->result) dio->end_io(dio->inode, offset, bytes, dio->map_bh.b_private); + if (dio->needs_locking) + up_read(&dio->inode->i_alloc_sem); } /* @@ -214,14 +222,38 @@ static void dio_complete(struct dio *dio */ static void finished_one_bio(struct dio *dio) { - if (atomic_dec_and_test(&dio->bio_count)) { + unsigned long flags; + + spin_lock_irqsave(&dio->bio_lock, flags); + if (dio->bio_count == 1) { if (dio->is_async) { + /* + * Last reference to the dio is going away. + * Drop spinlock and complete the DIO. + */ + spin_unlock_irqrestore(&dio->bio_lock, flags); dio_complete(dio, dio->block_in_file << dio->blkbits, dio->result); - aio_complete(dio->iocb, dio->result, 0); - kfree(dio); + /* Complete AIO later if falling back to buffered i/o */ + if (dio->result == dio->size || dio->rw == READ) { + aio_complete(dio->iocb, dio->result, 0); + kfree(dio); + return; + } else { + /* + * Falling back to buffered + */ + spin_lock_irqsave(&dio->bio_lock, flags); + dio->bio_count--; + if (dio->waiter) + wake_up_process(dio->waiter); + spin_unlock_irqrestore(&dio->bio_lock, flags); + return; + } } } + dio->bio_count--; + spin_unlock_irqrestore(&dio->bio_lock, flags); } static int dio_bio_complete(struct dio *dio, struct bio *bio); @@ -255,13 +287,13 @@ static int dio_bio_end_io(struct bio *bi if (bio->bi_size) return 1; - spin_lock_irqsave(&dio->bio_list_lock, flags); + spin_lock_irqsave(&dio->bio_lock, flags); bio->bi_private = dio->bio_list; dio->bio_list = bio; - atomic_dec(&dio->bios_in_flight); - if (dio->waiter && atomic_read(&dio->bios_in_flight) == 0) + dio->bios_in_flight--; + if (dio->waiter && dio->bios_in_flight == 0) wake_up_process(dio->waiter); - spin_unlock_irqrestore(&dio->bio_list_lock, flags); + spin_unlock_irqrestore(&dio->bio_lock, flags); return 0; } @@ -294,10 +326,13 @@ dio_bio_alloc(struct dio *dio, struct bl static void dio_bio_submit(struct dio *dio) { struct bio *bio = dio->bio; + unsigned long flags; bio->bi_private = dio; - atomic_inc(&dio->bio_count); - atomic_inc(&dio->bios_in_flight); + spin_lock_irqsave(&dio->bio_lock, flags); + dio->bio_count++; + dio->bios_in_flight++; + spin_unlock_irqrestore(&dio->bio_lock, flags); if (dio->is_async && dio->rw == READ) bio_set_pages_dirty(bio); submit_bio(dio->rw, bio); @@ -323,22 +358,22 @@ static struct bio *dio_await_one(struct unsigned long flags; struct bio *bio; - spin_lock_irqsave(&dio->bio_list_lock, flags); + spin_lock_irqsave(&dio->bio_lock, flags); while (dio->bio_list == NULL) { set_current_state(TASK_UNINTERRUPTIBLE); if (dio->bio_list == NULL) { dio->waiter = current; - spin_unlock_irqrestore(&dio->bio_list_lock, flags); + spin_unlock_irqrestore(&dio->bio_lock, flags); blk_run_queues(); io_schedule(); - spin_lock_irqsave(&dio->bio_list_lock, flags); + spin_lock_irqsave(&dio->bio_lock, flags); dio->waiter = NULL; } set_current_state(TASK_RUNNING); } bio = dio->bio_list; dio->bio_list = bio->bi_private; - spin_unlock_irqrestore(&dio->bio_list_lock, flags); + spin_unlock_irqrestore(&dio->bio_lock, flags); return bio; } @@ -380,7 +415,12 @@ static int dio_await_completion(struct d if (dio->bio) dio_bio_submit(dio); - while (atomic_read(&dio->bio_count)) { + /* + * The bio_lock is not held for the read of bio_count. + * This is ok since it is the dio_bio_complete() that changes + * bio_count. + */ + while (dio->bio_count) { struct bio *bio = dio_await_one(dio); int ret2; @@ -407,10 +447,10 @@ static int dio_bio_reap(struct dio *dio) unsigned long flags; struct bio *bio; - spin_lock_irqsave(&dio->bio_list_lock, flags); + spin_lock_irqsave(&dio->bio_lock, flags); bio = dio->bio_list; dio->bio_list = bio->bi_private; - spin_unlock_irqrestore(&dio->bio_list_lock, flags); + spin_unlock_irqrestore(&dio->bio_lock, flags); ret = dio_bio_complete(dio, bio); } dio->reap_counter = 0; @@ -449,6 +489,7 @@ static int get_more_blocks(struct dio *d unsigned long fs_count; /* Number of filesystem-sized blocks */ unsigned long dio_count;/* Number of dio_block-sized blocks */ unsigned long blkmask; + int beyond_eof = 0; /* * If there was a memory error and we've overwritten all the @@ -466,8 +507,19 @@ static int get_more_blocks(struct dio *d if (dio_count & blkmask) fs_count++; + if (dio->needs_locking) { + if (dio->block_in_file >= (i_size_read(dio->inode) >> + dio->blkbits)) + beyond_eof = 1; + } + /* + * For writes inside i_size we forbid block creations: only + * overwrites are permitted. We fall back to buffered writes + * at a higher level for inside-i_size block-instantiating + * writes. + */ ret = (*dio->get_blocks)(dio->inode, fs_startblk, fs_count, - map_bh, dio->rw == WRITE); + map_bh, (dio->rw == WRITE) && beyond_eof); } return ret; } @@ -774,6 +826,10 @@ do_holes: if (!buffer_mapped(map_bh)) { char *kaddr; + /* AKPM: eargh, -ENOTBLK is a hack */ + if (dio->rw == WRITE) + return -ENOTBLK; + if (dio->block_in_file >= i_size_read(dio->inode)>>blkbits) { /* We hit eof */ @@ -839,32 +895,30 @@ out: return ret; } +/* + * Releases both i_sem and i_alloc_sem + */ static int direct_io_worker(int rw, struct kiocb *iocb, struct inode *inode, const struct iovec *iov, loff_t offset, unsigned long nr_segs, - unsigned blkbits, get_blocks_t get_blocks, dio_iodone_t end_io) + unsigned blkbits, get_blocks_t get_blocks, dio_iodone_t end_io, + struct dio *dio) { unsigned long user_addr; int seg; int ret = 0; int ret2; - struct dio *dio; size_t bytes; - dio = kmalloc(sizeof(*dio), GFP_KERNEL); - if (!dio) - return -ENOMEM; - dio->is_async = !is_sync_kiocb(iocb); - dio->bio = NULL; dio->inode = inode; dio->rw = rw; dio->blkbits = blkbits; dio->blkfactor = inode->i_blkbits - blkbits; dio->start_zero_done = 0; + dio->size = 0; dio->block_in_file = offset >> blkbits; dio->blocks_available = 0; - dio->cur_page = NULL; dio->boundary = 0; @@ -887,9 +941,9 @@ direct_io_worker(int rw, struct kiocb *i * (or synchronous) device could take the count to zero while we're * still submitting BIOs. */ - atomic_set(&dio->bio_count, 1); - atomic_set(&dio->bios_in_flight, 0); - spin_lock_init(&dio->bio_list_lock); + dio->bio_count = 1; + dio->bios_in_flight = 0; + spin_lock_init(&dio->bio_lock); dio->bio_list = NULL; dio->waiter = NULL; @@ -899,7 +953,7 @@ direct_io_worker(int rw, struct kiocb *i for (seg = 0; seg < nr_segs; seg++) { user_addr = (unsigned long)iov[seg].iov_base; - bytes = iov[seg].iov_len; + dio->size += bytes = iov[seg].iov_len; /* Index into the first page of the first block */ dio->first_block_in_page = (user_addr & ~PAGE_MASK) >> blkbits; @@ -930,6 +984,13 @@ direct_io_worker(int rw, struct kiocb *i } } /* end iovec loop */ + if (ret == -ENOTBLK && rw == WRITE) { + /* + * The remaining part of the request will be + * be handled by buffered I/O when we return + */ + ret = 0; + } /* * There may be some unwritten disk at the end of a part-written * fs-block-sized block. Go zero that now. @@ -953,14 +1014,48 @@ direct_io_worker(int rw, struct kiocb *i dio_cleanup(dio); /* + * All block lookups have been performed. For READ requests + * we can let i_sem go now that its achieved its purpose + * of protecting us from looking up uninitialized blocks. + */ + if ((rw == READ) && dio->needs_locking) + up(&dio->inode->i_sem); + + /* * OK, all BIOs are submitted, so we can decrement bio_count to truly * reflect the number of to-be-processed BIOs. */ if (dio->is_async) { + int should_wait = 0; + + if (dio->result < dio->size && rw == WRITE) { + dio->waiter = current; + should_wait = 1; + } if (ret == 0) - ret = dio->result; /* Bytes written */ + ret = dio->result; finished_one_bio(dio); /* This can free the dio */ blk_run_queues(); + if (should_wait) { + unsigned long flags; + /* + * Wait for already issued I/O to drain out and + * release its references to user-space pages + * before returning to fallback on buffered I/O + */ + + spin_lock_irqsave(&dio->bio_lock, flags); + set_current_state(TASK_UNINTERRUPTIBLE); + while (dio->bio_count) { + spin_unlock_irqrestore(&dio->bio_lock, flags); + io_schedule(); + spin_lock_irqsave(&dio->bio_lock, flags); + set_current_state(TASK_UNINTERRUPTIBLE); + } + spin_unlock_irqrestore(&dio->bio_lock, flags); + set_current_state(TASK_RUNNING); + kfree(dio); + } } else { finished_one_bio(dio); ret2 = dio_await_completion(dio); @@ -980,6 +1075,14 @@ direct_io_worker(int rw, struct kiocb *i ret = i_size - offset; } dio_complete(dio, offset, ret); + /* We could have also come here on an AIO file extend */ + if (!is_sync_kiocb(iocb) && rw == WRITE && + ret >= 0 && dio->result == dio->size) + /* + * For AIO writes where we have completed the + * i/o, we have to mark the the aio complete. + */ + aio_complete(iocb, ret, 0); kfree(dio); } return ret; @@ -987,11 +1090,17 @@ direct_io_worker(int rw, struct kiocb *i /* * This is a library function for use by filesystem drivers. + * + * For writes to S_ISREG files, we are called under i_sem and return with i_sem + * held, even though it is internally dropped. + * + * For writes to S_ISBLK files, i_sem is not held on entry; it is never taken. */ int -blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode, +__blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode, struct block_device *bdev, const struct iovec *iov, loff_t offset, - unsigned long nr_segs, get_blocks_t get_blocks, dio_iodone_t end_io) + unsigned long nr_segs, get_blocks_t get_blocks, dio_iodone_t end_io, + int needs_special_locking) { int seg; size_t size; @@ -1000,6 +1109,9 @@ blockdev_direct_IO(int rw, struct kiocb unsigned bdev_blkbits = 0; unsigned blocksize_mask = (1 << blkbits) - 1; ssize_t retval = -EINVAL; + loff_t end = offset; + struct dio *dio; + int needs_locking; if (bdev) bdev_blkbits = blksize_bits(bdev_hardsect_size(bdev)); @@ -1016,6 +1128,7 @@ blockdev_direct_IO(int rw, struct kiocb for (seg = 0; seg < nr_segs; seg++) { addr = (unsigned long)iov[seg].iov_base; size = iov[seg].iov_len; + end += size; if ((addr & blocksize_mask) || (size & blocksize_mask)) { if (bdev) blkbits = bdev_blkbits; @@ -1025,10 +1138,46 @@ blockdev_direct_IO(int rw, struct kiocb } } - retval = direct_io_worker(rw, iocb, inode, iov, offset, - nr_segs, blkbits, get_blocks, end_io); + dio = kmalloc(sizeof(*dio), GFP_KERNEL); + retval = -ENOMEM; + if (!dio) + goto out; + + /* + * For regular files, + * readers need to grab i_sem and i_alloc_sem + * writers need to grab i_alloc_sem only (i_sem is already held) + */ + needs_locking = 0; + if (S_ISREG(inode->i_mode) && needs_special_locking) { + needs_locking = 1; + if (rw == READ) { + struct address_space *mapping; + + mapping = iocb->ki_filp->f_mapping; + down(&inode->i_sem); + retval = filemap_write_and_wait(mapping); + if (retval) { + up(&inode->i_sem); + kfree(dio); + goto out; + } + } + down_read(&inode->i_alloc_sem); + } + dio->needs_locking = needs_locking; + /* + * For file extending writes updating i_size before data + * writeouts complete can expose uninitialized blocks. So + * even for AIO, we need to wait for i/o to complete before + * returning in this case. + */ + dio->is_async = !is_sync_kiocb(iocb) && !((rw == WRITE) && + (end > i_size_read(inode))); + + retval = direct_io_worker(rw, iocb, inode, iov, offset, + nr_segs, blkbits, get_blocks, end_io, dio); out: return retval; } - -EXPORT_SYMBOL(blockdev_direct_IO); +EXPORT_SYMBOL(__blockdev_direct_IO); --- linux-2.6.4-rc2/fs/dquot.c 2004-02-03 20:42:38.000000000 -0800 +++ 25/fs/dquot.c 2004-03-07 20:47:09.000000000 -0800 @@ -1,16 +1,13 @@ /* - * Implementation of the diskquota system for the LINUX operating - * system. QUOTA is implemented using the BSD system call interface as - * the means of communication with the user level. Currently only the - * ext2 filesystem has support for disk quotas. Other filesystems may - * be added in the future. This file contains the generic routines - * called by the different filesystems on allocation of an inode or - * block. These routines take care of the administration needed to - * have a consistent diskquota tracking system. The ideas of both - * user and group quotas are based on the Melbourne quota system as - * used on BSD derived systems. The internal implementation is - * based on one of the several variants of the LINUX inode-subsystem - * with added complexity of the diskquota system. + * Implementation of the diskquota system for the LINUX operating system. QUOTA + * is implemented using the BSD system call interface as the means of + * communication with the user level. This file contains the generic routines + * called by the different filesystems on allocation of an inode or block. + * These routines take care of the administration needed to have a consistent + * diskquota tracking system. The ideas of both user and group quotas are based + * on the Melbourne quota system as used on BSD derived systems. The internal + * implementation is based on one of the several variants of the LINUX + * inode-subsystem with added complexity of the diskquota system. * * Version: $Id: dquot.c,v 6.3 1996/11/17 18:35:34 mvw Exp mvw $ * @@ -52,6 +49,9 @@ * New SMP locking. * Jan Kara, , 10/2002 * + * Added journalled quota support + * Jan Kara, , 2003,2004 + * * (C) Copyright 1994 - 1997 Marco van Wieringen */ @@ -85,13 +85,36 @@ * and quota formats and also dqstats structure containing statistics about the * lists. dq_data_lock protects data from dq_dqb and also mem_dqinfo structures * and also guards consistency of dquot->dq_dqb with inode->i_blocks, i_bytes. - * Note that we don't have to do the locking of i_blocks and i_bytes when the - * quota is disabled - i_sem should serialize the access. dq_data_lock should - * be always grabbed before dq_list_lock. + * i_blocks and i_bytes updates itself are guarded by i_lock acquired directly + * in inode_add_bytes() and inode_sub_bytes(). + * + * The spinlock ordering is hence: dq_data_lock > dq_list_lock > i_lock * * Note that some things (eg. sb pointer, type, id) doesn't change during * the life of the dquot structure and so needn't to be protected by a lock + * + * Any operation working on dquots via inode pointers must hold dqptr_sem. If + * operation is just reading pointers from inode (or not using them at all) the + * read lock is enough. If pointers are altered function must hold write lock. + * If operation is holding reference to dquot in other way (e.g. quotactl ops) + * it must be guarded by dqonoff_sem. + * This locking assures that: + * a) update/access to dquot pointers in inode is serialized + * b) everyone is guarded against invalidate_dquots() + * + * Each dquot has its dq_lock semaphore. Locked dquots might not be referenced + * from inodes (dquot_alloc_space() and such don't check the dq_lock). + * Currently dquot is locked only when it is being read to memory (or space for + * it is being allocated) on the first dqget() and when it is being released on + * the last dqput(). The allocation and release oparations are serialized by + * the dq_lock and by checking the use count in dquot_release(). Write + * operations on dquots don't hold dq_lock as they copy data under dq_data_lock + * spinlock to internal buffers before writing. + * + * Lock ordering (including journal_lock) is following: + * dqonoff_sem > journal_lock > dqptr_sem > dquot->dq_lock > dqio_sem */ + spinlock_t dq_list_lock = SPIN_LOCK_UNLOCKED; spinlock_t dq_data_lock = SPIN_LOCK_UNLOCKED; @@ -169,23 +192,6 @@ static void put_quota_format(struct quot * mechanism to locate a specific dquot. */ -/* - * Note that any operation which operates on dquot data (ie. dq_dqb) must - * hold dq_data_lock. - * - * Any operation working with dquots must hold dqptr_sem. If operation is - * just reading pointers from inodes than read lock is enough. If pointers - * are altered function must hold write lock. - * - * Locked dquots might not be referenced in inodes. Currently dquot it locked - * only once in its existence - when it's being read to memory on first dqget() - * and at that time it can't be referenced from inode. Write operations on - * dquots don't hold dquot lock as they copy data to internal buffers before - * writing anyway and copying as well as any data update should be atomic. Also - * nobody can change used entries in dquot structure as this is done only when - * quota is destroyed and invalidate_dquots() is called only when dq_count == 0. - */ - static LIST_HEAD(inuse_list); static LIST_HEAD(free_dquots); static struct list_head dquot_hash[NR_DQHASH]; @@ -254,6 +260,9 @@ static inline void remove_inuse(struct d dqstats.allocated_dquots--; list_del(&dquot->dq_inuse); } +/* + * End of list functions needing dq_list_lock + */ static void wait_on_dquot(struct dquot *dquot) { @@ -261,34 +270,98 @@ static void wait_on_dquot(struct dquot * up(&dquot->dq_lock); } -static int read_dqblk(struct dquot *dquot) +#define mark_dquot_dirty(dquot) ((dquot)->dq_sb->dq_op->mark_dirty(dquot)) + +/* No locks needed here as ANY_DQUOT_DIRTY is used just by sync and so the + * worst what can happen is that dquot is not written by concurrent sync... */ +int dquot_mark_dquot_dirty(struct dquot *dquot) +{ + set_bit(DQ_MOD_B, &(dquot)->dq_flags); + set_bit(DQF_ANY_DQUOT_DIRTY_B, &(sb_dqopt((dquot)->dq_sb)-> + info[(dquot)->dq_type].dqi_flags)); + return 0; +} + +void mark_info_dirty(struct super_block *sb, int type) { - int ret; + set_bit(DQF_INFO_DIRTY_B, &sb_dqopt(sb)->info[type].dqi_flags); +} + + +/* + * Read dquot from disk and alloc space for it + */ + +int dquot_acquire(struct dquot *dquot) +{ + int ret = 0; struct quota_info *dqopt = sb_dqopt(dquot->dq_sb); down(&dquot->dq_lock); down(&dqopt->dqio_sem); - ret = dqopt->ops[dquot->dq_type]->read_dqblk(dquot); + if (!test_bit(DQ_READ_B, &dquot->dq_flags)) + ret = dqopt->ops[dquot->dq_type]->read_dqblk(dquot); + if (ret < 0) + goto out_iolock; + set_bit(DQ_READ_B, &dquot->dq_flags); + /* Instantiate dquot if needed */ + if (!test_bit(DQ_ACTIVE_B, &dquot->dq_flags) && !dquot->dq_off) { + ret = dqopt->ops[dquot->dq_type]->commit_dqblk(dquot); + if (ret < 0) + goto out_iolock; + } + set_bit(DQ_ACTIVE_B, &dquot->dq_flags); +out_iolock: up(&dqopt->dqio_sem); up(&dquot->dq_lock); return ret; } -static int commit_dqblk(struct dquot *dquot) +/* + * Write dquot to disk + */ +int dquot_commit(struct dquot *dquot) { - int ret; + int ret = 0; struct quota_info *dqopt = sb_dqopt(dquot->dq_sb); down(&dqopt->dqio_sem); - ret = dqopt->ops[dquot->dq_type]->commit_dqblk(dquot); + clear_bit(DQ_MOD_B, &dquot->dq_flags); + /* Inactive dquot can be only if there was error during read/init + * => we have better not writing it */ + if (test_bit(DQ_ACTIVE_B, &dquot->dq_flags)) + ret = dqopt->ops[dquot->dq_type]->commit_dqblk(dquot); + up(&dqopt->dqio_sem); + if (info_dirty(&dqopt->info[dquot->dq_type])) + dquot->dq_sb->dq_op->write_info(dquot->dq_sb, dquot->dq_type); + return ret; +} + +/* + * Release dquot + */ +int dquot_release(struct dquot *dquot) +{ + int ret = 0; + struct quota_info *dqopt = sb_dqopt(dquot->dq_sb); + + down(&dquot->dq_lock); + /* Check whether we are not racing with some other dqget() */ + if (atomic_read(&dquot->dq_count) > 1) + goto out_dqlock; + down(&dqopt->dqio_sem); + ret = dqopt->ops[dquot->dq_type]->release_dqblk(dquot); + clear_bit(DQ_ACTIVE_B, &dquot->dq_flags); up(&dqopt->dqio_sem); +out_dqlock: + up(&dquot->dq_lock); return ret; } /* Invalidate all dquots on the list. Note that this function is called after - * quota is disabled so no new quota might be created. Because we hold dqptr_sem - * for writing and pointers were already removed from inodes we actually know that - * no quota for this sb+type should be held. */ + * quota is disabled and pointers from inodes removed so there cannot be new + * quota users. Also because we hold dqonoff_sem there can be no quota users + * for this sb+type at all. */ static void invalidate_dquots(struct super_block *sb, int type) { struct dquot *dquot; @@ -302,12 +375,11 @@ static void invalidate_dquots(struct sup continue; if (dquot->dq_type != type) continue; -#ifdef __DQUOT_PARANOIA - /* There should be no users of quota - we hold dqptr_sem for writing */ +#ifdef __DQUOT_PARANOIA if (atomic_read(&dquot->dq_count)) BUG(); #endif - /* Quota now have no users and it has been written on last dqput() */ + /* Quota now has no users and it has been written on last dqput() */ remove_dquot_hash(dquot); remove_free_dquot(dquot); remove_inuse(dquot); @@ -316,20 +388,22 @@ static void invalidate_dquots(struct sup spin_unlock(&dq_list_lock); } -static int vfs_quota_sync(struct super_block *sb, int type) +int vfs_quota_sync(struct super_block *sb, int type) { struct list_head *head; struct dquot *dquot; struct quota_info *dqopt = sb_dqopt(sb); int cnt; - down_read(&dqopt->dqptr_sem); + down(&dqopt->dqonoff_sem); restart: /* At this point any dirty dquot will definitely be written so we can clear dirty flag from info */ + spin_lock(&dq_data_lock); for (cnt = 0; cnt < MAXQUOTAS; cnt++) if ((cnt == type || type == -1) && sb_has_quota_enabled(sb, cnt)) clear_bit(DQF_ANY_DQUOT_DIRTY_B, &dqopt->info[cnt].dqi_flags); + spin_unlock(&dq_data_lock); spin_lock(&dq_list_lock); list_for_each(head, &inuse_list) { dquot = list_entry(head, struct dquot, dq_inuse); @@ -337,10 +411,13 @@ restart: continue; if (type != -1 && dquot->dq_type != type) continue; - if (!dquot->dq_sb) /* Invalidated? */ - continue; if (!dquot_dirty(dquot)) continue; + /* Dirty and inactive can be only bad dquot... */ + if (!test_bit(DQ_ACTIVE_B, &dquot->dq_flags)) + continue; + /* Now we have active dquot from which someone is holding reference so we + * can safely just increase use count */ atomic_inc(&dquot->dq_count); dqstats.lookups++; spin_unlock(&dq_list_lock); @@ -351,15 +428,13 @@ restart: spin_unlock(&dq_list_lock); for (cnt = 0; cnt < MAXQUOTAS; cnt++) - if ((cnt == type || type == -1) && sb_has_quota_enabled(sb, cnt) && info_dirty(&dqopt->info[cnt])) { - down(&dqopt->dqio_sem); - dqopt->ops[cnt]->write_file_info(sb, cnt); - up(&dqopt->dqio_sem); - } + if ((cnt == type || type == -1) && sb_has_quota_enabled(sb, cnt) + && info_dirty(&dqopt->info[cnt])) + sb->dq_op->write_info(sb, cnt); spin_lock(&dq_list_lock); dqstats.syncs++; spin_unlock(&dq_list_lock); - up_read(&dqopt->dqptr_sem); + up(&dqopt->dqonoff_sem); return 0; } @@ -402,7 +477,7 @@ static int shrink_dqcache_memory(int nr, /* * Put reference to dquot * NOTE: If you change this function please check whether dqput_blocks() works right... - * MUST be called with dqptr_sem held + * MUST be called with either dqptr_sem or dqonoff_sem */ static void dqput(struct dquot *dquot) { @@ -430,11 +505,20 @@ we_slept: spin_unlock(&dq_list_lock); return; } - if (dquot_dirty(dquot)) { + /* Need to release dquot? */ + if (test_bit(DQ_ACTIVE_B, &dquot->dq_flags) && dquot_dirty(dquot)) { spin_unlock(&dq_list_lock); + /* Commit dquot before releasing */ dquot->dq_sb->dq_op->write_dquot(dquot); goto we_slept; } + /* Clear flag in case dquot was inactive (something bad happened) */ + clear_bit(DQ_MOD_B, &dquot->dq_flags); + if (test_bit(DQ_ACTIVE_B, &dquot->dq_flags)) { + spin_unlock(&dq_list_lock); + dquot_release(dquot); + goto we_slept; + } atomic_dec(&dquot->dq_count); #ifdef __DQUOT_PARANOIA /* sanity check */ @@ -467,7 +551,7 @@ static struct dquot *get_empty_dquot(str /* * Get reference to dquot - * MUST be called with dqptr_sem held + * MUST be called with dqptr_sem or dqonoff_sem held */ static struct dquot *dqget(struct super_block *sb, unsigned int id, int type) { @@ -493,7 +577,6 @@ we_slept: insert_dquot_hash(dquot); dqstats.lookups++; spin_unlock(&dq_list_lock); - read_dqblk(dquot); } else { if (!atomic_read(&dquot->dq_count)) remove_free_dquot(dquot); @@ -501,11 +584,17 @@ we_slept: dqstats.cache_hits++; dqstats.lookups++; spin_unlock(&dq_list_lock); - wait_on_dquot(dquot); if (empty) kmem_cache_free(dquot_cachep, empty); } - + /* Wait for dq_lock - after this we know that either dquot_release() is already + * finished or it will be canceled due to dq_count > 1 test */ + wait_on_dquot(dquot); + /* Read the dquot and instantiate it (everything done only if needed) */ + if (!test_bit(DQ_ACTIVE_B, &dquot->dq_flags) && dquot_acquire(dquot) < 0) { + dqput(dquot); + return NODQUOT; + } #ifdef __DQUOT_PARANOIA if (!dquot->dq_sb) /* Has somebody invalidated entry under us? */ BUG(); @@ -528,7 +617,7 @@ static int dqinit_needed(struct inode *i return 0; } -/* This routine is guarded by dqptr_sem semaphore */ +/* This routine is guarded by dqonoff_sem semaphore */ static void add_dquot_ref(struct super_block *sb, int type) { struct list_head *p; @@ -539,12 +628,10 @@ restart: struct file *filp = list_entry(p, struct file, f_list); struct inode *inode = filp->f_dentry->d_inode; if (filp->f_mode & FMODE_WRITE && dqinit_needed(inode, type)) { - struct vfsmount *mnt = mntget(filp->f_vfsmnt); struct dentry *dentry = dget(filp->f_dentry); file_list_unlock(); sb->dq_op->initialize(inode, type); dput(dentry); - mntput(mnt); /* As we may have blocked we had better restart... */ goto restart; } @@ -594,7 +681,7 @@ put_it: /* Free list of dquots - called from inode.c */ /* dquots are removed from inodes, no new references can be got so we are the only ones holding reference */ -void put_dquot_list(struct list_head *tofree_head) +static void put_dquot_list(struct list_head *tofree_head) { struct list_head *act_head; struct dquot *dquot; @@ -609,16 +696,28 @@ void put_dquot_list(struct list_head *to } } +/* Function in inode.c - remove pointers to dquots in icache */ +extern void remove_dquot_ref(struct super_block *, int, struct list_head *); + +/* Gather all references from inodes and drop them */ +static void drop_dquot_ref(struct super_block *sb, int type) +{ + LIST_HEAD(tofree_head); + + down_write(&sb_dqopt(sb)->dqptr_sem); + remove_dquot_ref(sb, type, &tofree_head); + up_write(&sb_dqopt(sb)->dqptr_sem); + put_dquot_list(&tofree_head); +} + static inline void dquot_incr_inodes(struct dquot *dquot, unsigned long number) { dquot->dq_dqb.dqb_curinodes += number; - mark_dquot_dirty(dquot); } static inline void dquot_incr_space(struct dquot *dquot, qsize_t number) { dquot->dq_dqb.dqb_curspace += number; - mark_dquot_dirty(dquot); } static inline void dquot_decr_inodes(struct dquot *dquot, unsigned long number) @@ -630,7 +729,6 @@ static inline void dquot_decr_inodes(str if (dquot->dq_dqb.dqb_curinodes < dquot->dq_dqb.dqb_isoftlimit) dquot->dq_dqb.dqb_itime = (time_t) 0; clear_bit(DQ_INODES_B, &dquot->dq_flags); - mark_dquot_dirty(dquot); } static inline void dquot_decr_space(struct dquot *dquot, qsize_t number) @@ -642,7 +740,6 @@ static inline void dquot_decr_space(stru if (toqb(dquot->dq_dqb.dqb_curspace) < dquot->dq_dqb.dqb_bsoftlimit) dquot->dq_dqb.dqb_btime = (time_t) 0; clear_bit(DQ_BLKS_B, &dquot->dq_flags); - mark_dquot_dirty(dquot); } static inline int need_print_warning(struct dquot *dquot) @@ -795,22 +892,22 @@ static int check_bdq(struct dquot *dquot } /* - * Externally referenced functions through dquot_operations in inode. - * - * Note: this is a blocking operation. + * Initialize quota pointers in inode + * Transaction must be started at entry */ -void dquot_initialize(struct inode *inode, int type) +int dquot_initialize(struct inode *inode, int type) { unsigned int id = 0; - int cnt; + int cnt, ret = 0; + /* First test before acquiring semaphore - solves deadlocks when we + * re-enter the quota code and are already holding the semaphore */ + if (IS_NOQUOTA(inode)) + return 0; down_write(&sb_dqopt(inode->i_sb)->dqptr_sem); /* Having dqptr_sem we know NOQUOTA flags can't be altered... */ - if (IS_NOQUOTA(inode)) { - up_write(&sb_dqopt(inode->i_sb)->dqptr_sem); - return; - } - /* Build list of quotas to initialize... */ + if (IS_NOQUOTA(inode)) + goto out_err; for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (type != -1 && cnt != type) continue; @@ -828,54 +925,39 @@ void dquot_initialize(struct inode *inod inode->i_flags |= S_QUOTA; } } +out_err: up_write(&sb_dqopt(inode->i_sb)->dqptr_sem); + return ret; } /* - * Remove references to quota from inode - * This function needs dqptr_sem for writing + * Release all quotas referenced by inode + * Transaction must be started at entry */ -static void dquot_drop_iupdate(struct inode *inode, struct dquot **to_drop) +int dquot_drop(struct inode *inode) { int cnt; + down_write(&sb_dqopt(inode->i_sb)->dqptr_sem); inode->i_flags &= ~S_QUOTA; for (cnt = 0; cnt < MAXQUOTAS; cnt++) { - to_drop[cnt] = inode->i_dquot[cnt]; - inode->i_dquot[cnt] = NODQUOT; + if (inode->i_dquot[cnt]) { + dqput(inode->i_dquot[cnt]); + inode->i_dquot[cnt] = NODQUOT; + } } -} - -/* - * Release all quotas referenced by inode - */ -void dquot_drop(struct inode *inode) -{ - struct dquot *to_drop[MAXQUOTAS]; - int cnt; - - down_write(&sb_dqopt(inode->i_sb)->dqptr_sem); - dquot_drop_iupdate(inode, to_drop); up_write(&sb_dqopt(inode->i_sb)->dqptr_sem); - for (cnt = 0; cnt < MAXQUOTAS; cnt++) - if (to_drop[cnt] != NODQUOT) - dqput(to_drop[cnt]); + return 0; } /* - * Release all quotas referenced by inode. - * This function assumes dqptr_sem for writing + * Following four functions update i_blocks+i_bytes fields and + * quota information (together with appropriate checks) + * NOTE: We absolutely rely on the fact that caller dirties + * the inode (usually macros in quotaops.h care about this) and + * holds a handle for the current transaction so that dquot write and + * inode write go into the same transaction. */ -void dquot_drop_nolock(struct inode *inode) -{ - struct dquot *to_drop[MAXQUOTAS]; - int cnt; - - dquot_drop_iupdate(inode, to_drop); - for (cnt = 0; cnt < MAXQUOTAS; cnt++) - if (to_drop[cnt] != NODQUOT) - dqput(to_drop[cnt]); -} /* * This operation can block, but only after everything is updated @@ -885,13 +967,22 @@ int dquot_alloc_space(struct inode *inod int cnt, ret = NO_QUOTA; char warntype[MAXQUOTAS]; + /* First test before acquiring semaphore - solves deadlocks when we + * re-enter the quota code and are already holding the semaphore */ + if (IS_NOQUOTA(inode)) { +out_add: + inode_add_bytes(inode, number); + return QUOTA_OK; + } for (cnt = 0; cnt < MAXQUOTAS; cnt++) warntype[cnt] = NOWARN; down_read(&sb_dqopt(inode->i_sb)->dqptr_sem); + if (IS_NOQUOTA(inode)) { /* Now we can do reliable test... */ + up_read(&sb_dqopt(inode->i_sb)->dqptr_sem); + goto out_add; + } spin_lock(&dq_data_lock); - if (IS_NOQUOTA(inode)) - goto add_bytes; for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (inode->i_dquot[cnt] == NODQUOT) continue; @@ -903,11 +994,15 @@ int dquot_alloc_space(struct inode *inod continue; dquot_incr_space(inode->i_dquot[cnt], number); } -add_bytes: inode_add_bytes(inode, number); ret = QUOTA_OK; warn_put_all: spin_unlock(&dq_data_lock); + if (ret == QUOTA_OK) + /* Dirtify all the dquots - this can block when journalling */ + for (cnt = 0; cnt < MAXQUOTAS; cnt++) + if (inode->i_dquot[cnt]) + mark_dquot_dirty(inode->i_dquot[cnt]); flush_warnings(inode->i_dquot, warntype); up_read(&sb_dqopt(inode->i_sb)->dqptr_sem); return ret; @@ -921,6 +1016,10 @@ int dquot_alloc_inode(const struct inode int cnt, ret = NO_QUOTA; char warntype[MAXQUOTAS]; + /* First test before acquiring semaphore - solves deadlocks when we + * re-enter the quota code and are already holding the semaphore */ + if (IS_NOQUOTA(inode)) + return QUOTA_OK; for (cnt = 0; cnt < MAXQUOTAS; cnt++) warntype[cnt] = NOWARN; down_read(&sb_dqopt(inode->i_sb)->dqptr_sem); @@ -944,6 +1043,11 @@ int dquot_alloc_inode(const struct inode ret = QUOTA_OK; warn_put_all: spin_unlock(&dq_data_lock); + if (ret == QUOTA_OK) + /* Dirtify all the dquots - this can block when journalling */ + for (cnt = 0; cnt < MAXQUOTAS; cnt++) + if (inode->i_dquot[cnt]) + mark_dquot_dirty(inode->i_dquot[cnt]); flush_warnings((struct dquot **)inode->i_dquot, warntype); up_read(&sb_dqopt(inode->i_sb)->dqptr_sem); return ret; @@ -952,36 +1056,53 @@ warn_put_all: /* * This is a non-blocking operation. */ -void dquot_free_space(struct inode *inode, qsize_t number) +int dquot_free_space(struct inode *inode, qsize_t number) { unsigned int cnt; + /* First test before acquiring semaphore - solves deadlocks when we + * re-enter the quota code and are already holding the semaphore */ + if (IS_NOQUOTA(inode)) { +out_sub: + inode_sub_bytes(inode, number); + return QUOTA_OK; + } down_read(&sb_dqopt(inode->i_sb)->dqptr_sem); + if (IS_NOQUOTA(inode)) { + up_read(&sb_dqopt(inode->i_sb)->dqptr_sem); + goto out_sub; + } spin_lock(&dq_data_lock); - if (IS_NOQUOTA(inode)) - goto sub_bytes; for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (inode->i_dquot[cnt] == NODQUOT) continue; dquot_decr_space(inode->i_dquot[cnt], number); } -sub_bytes: inode_sub_bytes(inode, number); spin_unlock(&dq_data_lock); + /* Dirtify all the dquots - this can block when journalling */ + for (cnt = 0; cnt < MAXQUOTAS; cnt++) + if (inode->i_dquot[cnt]) + mark_dquot_dirty(inode->i_dquot[cnt]); up_read(&sb_dqopt(inode->i_sb)->dqptr_sem); + return QUOTA_OK; } /* * This is a non-blocking operation. */ -void dquot_free_inode(const struct inode *inode, unsigned long number) +int dquot_free_inode(const struct inode *inode, unsigned long number) { unsigned int cnt; + /* First test before acquiring semaphore - solves deadlocks when we + * re-enter the quota code and are already holding the semaphore */ + if (IS_NOQUOTA(inode)) + return QUOTA_OK; down_read(&sb_dqopt(inode->i_sb)->dqptr_sem); if (IS_NOQUOTA(inode)) { up_read(&sb_dqopt(inode->i_sb)->dqptr_sem); - return; + return QUOTA_OK; } spin_lock(&dq_data_lock); for (cnt = 0; cnt < MAXQUOTAS; cnt++) { @@ -990,7 +1111,12 @@ void dquot_free_inode(const struct inode dquot_decr_inodes(inode->i_dquot[cnt], number); } spin_unlock(&dq_data_lock); + /* Dirtify all the dquots - this can block when journalling */ + for (cnt = 0; cnt < MAXQUOTAS; cnt++) + if (inode->i_dquot[cnt]) + mark_dquot_dirty(inode->i_dquot[cnt]); up_read(&sb_dqopt(inode->i_sb)->dqptr_sem); + return QUOTA_OK; } /* @@ -1007,6 +1133,10 @@ int dquot_transfer(struct inode *inode, chgid = (iattr->ia_valid & ATTR_GID) && inode->i_gid != iattr->ia_gid; char warntype[MAXQUOTAS]; + /* First test before acquiring semaphore - solves deadlocks when we + * re-enter the quota code and are already holding the semaphore */ + if (IS_NOQUOTA(inode)) + return QUOTA_OK; /* Clear the arrays */ for (cnt = 0; cnt < MAXQUOTAS; cnt++) { transfer_to[cnt] = transfer_from[cnt] = NODQUOT; @@ -1017,7 +1147,9 @@ int dquot_transfer(struct inode *inode, up_write(&sb_dqopt(inode->i_sb)->dqptr_sem); return QUOTA_OK; } - /* First build the transfer_to list - here we can block on reading of dquots... */ + /* First build the transfer_to list - here we can block on + * reading/instantiating of dquots. We know that the transaction for + * us was already started so we don't violate lock ranking here */ for (cnt = 0; cnt < MAXQUOTAS; cnt++) { switch (cnt) { case USRQUOTA: @@ -1065,6 +1197,13 @@ int dquot_transfer(struct inode *inode, ret = QUOTA_OK; warn_put_all: spin_unlock(&dq_data_lock); + /* Dirtify all the dquots - this can block when journalling */ + for (cnt = 0; cnt < MAXQUOTAS; cnt++) { + if (transfer_from[cnt]) + mark_dquot_dirty(transfer_from[cnt]); + if (transfer_to[cnt]) + mark_dquot_dirty(transfer_to[cnt]); + } flush_warnings(transfer_to, warntype); for (cnt = 0; cnt < MAXQUOTAS; cnt++) { @@ -1078,25 +1217,35 @@ warn_put_all: } /* + * Write info of quota file to disk + */ +int dquot_commit_info(struct super_block *sb, int type) +{ + int ret; + struct quota_info *dqopt = sb_dqopt(sb); + + down(&dqopt->dqio_sem); + ret = dqopt->ops[type]->write_file_info(sb, type); + up(&dqopt->dqio_sem); + return ret; +} + +/* * Definitions of diskquota operations. */ struct dquot_operations dquot_operations = { - .initialize = dquot_initialize, /* mandatory */ - .drop = dquot_drop, /* mandatory */ + .initialize = dquot_initialize, + .drop = dquot_drop, .alloc_space = dquot_alloc_space, .alloc_inode = dquot_alloc_inode, .free_space = dquot_free_space, .free_inode = dquot_free_inode, .transfer = dquot_transfer, - .write_dquot = commit_dqblk + .write_dquot = dquot_commit, + .mark_dirty = dquot_mark_dquot_dirty, + .write_info = dquot_commit_info }; -/* Function used by filesystems for initializing the dquot_operations structure */ -void init_dquot_operations(struct dquot_operations *fsdqops) -{ - memcpy(fsdqops, &dquot_operations, sizeof(dquot_operations)); -} - static inline void set_enable_flags(struct quota_info *dqopt, int type) { switch (type) { @@ -1121,9 +1270,6 @@ static inline void reset_enable_flags(st } } -/* Function in inode.c - remove pointers to dquots in icache */ -extern void remove_dquot_ref(struct super_block *, int); - /* * Turn quota off on a device. type == -1 ==> quotaoff for all types (umount) */ @@ -1137,7 +1283,6 @@ int vfs_quota_off(struct super_block *sb /* We need to serialize quota_off() for device */ down(&dqopt->dqonoff_sem); - down_write(&dqopt->dqptr_sem); for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (type != -1 && cnt != type) continue; @@ -1146,63 +1291,56 @@ int vfs_quota_off(struct super_block *sb reset_enable_flags(dqopt, cnt); /* Note: these are blocking operations */ - remove_dquot_ref(sb, cnt); + drop_dquot_ref(sb, cnt); invalidate_dquots(sb, cnt); /* * Now all dquots should be invalidated, all writes done so we should be only * users of the info. No locks needed. */ - if (info_dirty(&dqopt->info[cnt])) { - down(&dqopt->dqio_sem); - dqopt->ops[cnt]->write_file_info(sb, cnt); - up(&dqopt->dqio_sem); - } + if (info_dirty(&dqopt->info[cnt])) + sb->dq_op->write_info(sb, cnt); if (dqopt->ops[cnt]->free_file_info) dqopt->ops[cnt]->free_file_info(sb, cnt); put_quota_format(dqopt->info[cnt].dqi_format); fput(dqopt->files[cnt]); - dqopt->files[cnt] = (struct file *)NULL; + dqopt->files[cnt] = NULL; dqopt->info[cnt].dqi_flags = 0; dqopt->info[cnt].dqi_igrace = 0; dqopt->info[cnt].dqi_bgrace = 0; dqopt->ops[cnt] = NULL; } - up_write(&dqopt->dqptr_sem); up(&dqopt->dqonoff_sem); out: return 0; } -int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path) +/* + * Turn quotas on on a device + */ + +/* Helper function when we already have file open */ +static int vfs_quota_on_file(struct file *f, int type, int format_id) { - struct file *f; + struct quota_format_type *fmt = find_quota_format(format_id); struct inode *inode; + struct super_block *sb = f->f_dentry->d_sb; struct quota_info *dqopt = sb_dqopt(sb); - struct quota_format_type *fmt = find_quota_format(format_id); - int error; + struct dquot *to_drop[MAXQUOTAS]; + int error, cnt; unsigned int oldflags; if (!fmt) return -ESRCH; - f = filp_open(path, O_RDWR, 0600); - if (IS_ERR(f)) { - error = PTR_ERR(f); - goto out_fmt; - } error = -EIO; if (!f->f_op || !f->f_op->read || !f->f_op->write) - goto out_f; - error = security_quota_on(f); - if (error) - goto out_f; + goto out_fmt; inode = f->f_dentry->d_inode; error = -EACCES; if (!S_ISREG(inode->i_mode)) - goto out_f; + goto out_fmt; down(&dqopt->dqonoff_sem); - down_write(&dqopt->dqptr_sem); if (sb_has_quota_enabled(sb, type)) { error = -EBUSY; goto out_lock; @@ -1213,8 +1351,20 @@ int vfs_quota_on(struct super_block *sb, if (!fmt->qf_ops->check_quota_file(sb, type)) goto out_file_init; /* We don't want quota and atime on quota files (deadlocks possible) */ - dquot_drop_nolock(inode); + down_write(&dqopt->dqptr_sem); inode->i_flags |= S_NOQUOTA | S_NOATIME; + for (cnt = 0; cnt < MAXQUOTAS; cnt++) { + to_drop[cnt] = inode->i_dquot[cnt]; + inode->i_dquot[cnt] = NODQUOT; + } + inode->i_flags &= ~S_QUOTA; + up_write(&dqopt->dqptr_sem); + /* We must put dquots outside of dqptr_sem because we may need to + * start transaction for dquot_release() */ + for (cnt = 0; cnt < MAXQUOTAS; cnt++) { + if (to_drop[cnt]) + dqput(to_drop[cnt]); + } dqopt->ops[type] = fmt->qf_ops; dqopt->info[type].dqi_format = fmt; @@ -1225,7 +1375,6 @@ int vfs_quota_on(struct super_block *sb, } up(&dqopt->dqio_sem); set_enable_flags(dqopt, type); - up_write(&dqopt->dqptr_sem); add_dquot_ref(sb, type); up(&dqopt->dqonoff_sem); @@ -1238,14 +1387,58 @@ out_file_init: out_lock: up_write(&dqopt->dqptr_sem); up(&dqopt->dqonoff_sem); -out_f: - filp_close(f, NULL); out_fmt: put_quota_format(fmt); return error; } +/* Actual function called from quotactl() */ +int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path) +{ + struct file *f; + int error; + + f = filp_open(path, O_RDWR, 0600); + if (IS_ERR(f)) + return PTR_ERR(f); + error = security_quota_on(f); + if (error) + goto out_f; + error = vfs_quota_on_file(f, type, format_id); + if (!error) + return 0; +out_f: + filp_close(f, NULL); + return error; +} + +/* + * Function used by filesystems when filp_open() would fail (filesystem is + * being mounted now). We will use a private file structure. Caller is + * responsible that it's IO functions won't need vfsmnt structure or + * some dentry tricks... + */ +int vfs_quota_on_mount(int type, int format_id, struct dentry *dentry) +{ + struct file *f; + int error; + + dget(dentry); /* Get a reference for struct file */ + f = dentry_open(dentry, NULL, O_RDWR); + if (IS_ERR(f)) { + error = PTR_ERR(f); + goto out_dentry; + } + error = vfs_quota_on_file(f, type, format_id); + if (!error) + return 0; + fput(f); +out_dentry: + dput(dentry); + return error; +} + /* Generic routine for getting common part of quota structure */ static void do_get_dqblk(struct dquot *dquot, struct if_dqblk *di) { @@ -1268,14 +1461,14 @@ int vfs_get_dqblk(struct super_block *sb { struct dquot *dquot; - down_read(&sb_dqopt(sb)->dqptr_sem); + down(&sb_dqopt(sb)->dqonoff_sem); if (!(dquot = dqget(sb, id, type))) { - up_read(&sb_dqopt(sb)->dqptr_sem); + up(&sb_dqopt(sb)->dqonoff_sem); return -ESRCH; } do_get_dqblk(dquot, di); dqput(dquot); - up_read(&sb_dqopt(sb)->dqptr_sem); + up(&sb_dqopt(sb)->dqonoff_sem); return 0; } @@ -1329,22 +1522,22 @@ static void do_set_dqblk(struct dquot *d clear_bit(DQ_FAKE_B, &dquot->dq_flags); else set_bit(DQ_FAKE_B, &dquot->dq_flags); - mark_dquot_dirty(dquot); spin_unlock(&dq_data_lock); + mark_dquot_dirty(dquot); } int vfs_set_dqblk(struct super_block *sb, int type, qid_t id, struct if_dqblk *di) { struct dquot *dquot; - down_read(&sb_dqopt(sb)->dqptr_sem); + down(&sb_dqopt(sb)->dqonoff_sem); if (!(dquot = dqget(sb, id, type))) { - up_read(&sb_dqopt(sb)->dqptr_sem); + up(&sb_dqopt(sb)->dqonoff_sem); return -ESRCH; } do_set_dqblk(dquot, di); dqput(dquot); - up_read(&sb_dqopt(sb)->dqptr_sem); + up(&sb_dqopt(sb)->dqonoff_sem); return 0; } @@ -1353,9 +1546,9 @@ int vfs_get_dqinfo(struct super_block *s { struct mem_dqinfo *mi; - down_read(&sb_dqopt(sb)->dqptr_sem); + down(&sb_dqopt(sb)->dqonoff_sem); if (!sb_has_quota_enabled(sb, type)) { - up_read(&sb_dqopt(sb)->dqptr_sem); + up(&sb_dqopt(sb)->dqonoff_sem); return -ESRCH; } mi = sb_dqopt(sb)->info + type; @@ -1365,7 +1558,7 @@ int vfs_get_dqinfo(struct super_block *s ii->dqi_flags = mi->dqi_flags & DQF_MASK; ii->dqi_valid = IIF_ALL; spin_unlock(&dq_data_lock); - up_read(&sb_dqopt(sb)->dqptr_sem); + up(&sb_dqopt(sb)->dqonoff_sem); return 0; } @@ -1374,9 +1567,9 @@ int vfs_set_dqinfo(struct super_block *s { struct mem_dqinfo *mi; - down_read(&sb_dqopt(sb)->dqptr_sem); + down(&sb_dqopt(sb)->dqonoff_sem); if (!sb_has_quota_enabled(sb, type)) { - up_read(&sb_dqopt(sb)->dqptr_sem); + up(&sb_dqopt(sb)->dqonoff_sem); return -ESRCH; } mi = sb_dqopt(sb)->info + type; @@ -1387,9 +1580,11 @@ int vfs_set_dqinfo(struct super_block *s mi->dqi_igrace = ii->dqi_igrace; if (ii->dqi_valid & IIF_FLAGS) mi->dqi_flags = (mi->dqi_flags & ~DQF_MASK) | (ii->dqi_flags & DQF_MASK); - mark_info_dirty(mi); spin_unlock(&dq_data_lock); - up_read(&sb_dqopt(sb)->dqptr_sem); + mark_info_dirty(sb, type); + /* Force write to disk */ + sb->dq_op->write_info(sb, type); + up(&sb_dqopt(sb)->dqonoff_sem); return 0; } @@ -1520,4 +1715,21 @@ EXPORT_SYMBOL(unregister_quota_format); EXPORT_SYMBOL(dqstats); EXPORT_SYMBOL(dq_list_lock); EXPORT_SYMBOL(dq_data_lock); -EXPORT_SYMBOL(init_dquot_operations); +EXPORT_SYMBOL(vfs_quota_on); +EXPORT_SYMBOL(vfs_quota_on_mount); +EXPORT_SYMBOL(vfs_quota_off); +EXPORT_SYMBOL(vfs_quota_sync); +EXPORT_SYMBOL(vfs_get_dqinfo); +EXPORT_SYMBOL(vfs_set_dqinfo); +EXPORT_SYMBOL(vfs_get_dqblk); +EXPORT_SYMBOL(vfs_set_dqblk); +EXPORT_SYMBOL(dquot_commit); +EXPORT_SYMBOL(dquot_commit_info); +EXPORT_SYMBOL(dquot_mark_dquot_dirty); +EXPORT_SYMBOL(dquot_initialize); +EXPORT_SYMBOL(dquot_drop); +EXPORT_SYMBOL(dquot_alloc_space); +EXPORT_SYMBOL(dquot_alloc_inode); +EXPORT_SYMBOL(dquot_free_space); +EXPORT_SYMBOL(dquot_free_inode); +EXPORT_SYMBOL(dquot_transfer); --- linux-2.6.4-rc2/fs/efs/super.c 2003-10-08 15:07:09.000000000 -0700 +++ 25/fs/efs/super.c 2004-03-07 20:46:46.000000000 -0800 @@ -210,6 +210,7 @@ int efs_fill_super(struct super_block *s { struct efs_sb_info *sb; struct buffer_head *bh; + struct inode *root; sb = kmalloc(sizeof(struct efs_sb_info), GFP_KERNEL); if (!sb) @@ -266,10 +267,12 @@ int efs_fill_super(struct super_block *s s->s_flags |= MS_RDONLY; } s->s_op = &efs_superblock_operations; - s->s_root = d_alloc_root(iget(s, EFS_ROOTINODE)); + root = iget(s, EFS_ROOTINODE); + s->s_root = d_alloc_root(root); if (!(s->s_root)) { printk(KERN_ERR "EFS: get root inode failed\n"); + iput(root); goto out_no_fs; } --- linux-2.6.4-rc2/fs/exec.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/fs/exec.c 2004-03-07 20:47:37.000000000 -0800 @@ -327,7 +327,7 @@ void put_dirty_page(struct task_struct * set_pte(pte, pte_mkdirty(pte_mkwrite(mk_pte(page, prot)))); pte_chain = page_add_rmap(page, pte, pte_chain); pte_unmap(pte); - tsk->mm->rss++; + inc_rss(tsk->mm, page); spin_unlock(&tsk->mm->page_table_lock); /* no need for flush_tlb */ @@ -833,7 +833,8 @@ int flush_old_exec(struct linux_binprm * flush_thread(); if (bprm->e_uid != current->euid || bprm->e_gid != current->egid || - permission(bprm->file->f_dentry->d_inode,MAY_READ, NULL)) + permission(bprm->file->f_dentry->d_inode,MAY_READ, NULL) || + (bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP)) current->mm->dumpable = 0; /* An exec changes our domain. We are no longer part of the thread @@ -1106,6 +1107,7 @@ int do_execve(char * filename, bprm.file = file; bprm.filename = filename; bprm.interp = filename; + bprm.interp_flags = 0; bprm.sh_bang = 0; bprm.loader = 0; bprm.exec = 0; --- linux-2.6.4-rc2/fs/ext2/acl.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/fs/ext2/acl.c 2004-03-07 20:48:08.000000000 -0800 @@ -154,10 +154,9 @@ ext2_iset_acl(struct inode *inode, struc static struct posix_acl * ext2_get_acl(struct inode *inode, int type) { - const size_t max_size = ext2_acl_size(EXT2_ACL_MAX_ENTRIES); struct ext2_inode_info *ei = EXT2_I(inode); int name_index; - char *value; + char *value = NULL; struct posix_acl *acl; int retval; @@ -182,17 +181,21 @@ ext2_get_acl(struct inode *inode, int ty default: return ERR_PTR(-EINVAL); } - value = kmalloc(max_size, GFP_KERNEL); - if (!value) - return ERR_PTR(-ENOMEM); - - retval = ext2_xattr_get(inode, name_index, "", value, max_size); - acl = ERR_PTR(retval); - if (retval >= 0) + retval = ext2_xattr_get(inode, name_index, "", NULL, 0); + if (retval > 0) { + value = kmalloc(retval, GFP_KERNEL); + if (!value) + return ERR_PTR(-ENOMEM); + retval = ext2_xattr_get(inode, name_index, "", value, retval); + } + if (retval > 0) acl = ext2_acl_from_disk(value, retval); else if (retval == -ENODATA || retval == -ENOSYS) acl = NULL; - kfree(value); + else + acl = ERR_PTR(retval); + if (value) + kfree(value); if (!IS_ERR(acl)) { switch(type) { --- linux-2.6.4-rc2/fs/ext2/dir.c 2003-06-14 12:18:22.000000000 -0700 +++ 25/fs/ext2/dir.c 2004-03-07 20:47:08.000000000 -0800 @@ -310,7 +310,6 @@ ext2_readdir (struct file * filp, void * done: filp->f_pos = (n << PAGE_CACHE_SHIFT) | offset; filp->f_version = inode->i_version; - update_atime(inode); return 0; } --- linux-2.6.4-rc2/fs/ext2/ialloc.c 2004-02-03 20:42:38.000000000 -0800 +++ 25/fs/ext2/ialloc.c 2004-03-07 20:46:46.000000000 -0800 @@ -431,8 +431,8 @@ static int find_group_other(struct super * That failed: try linear search for a free inode, even if that group * has no free blocks. */ - group = parent_group + 1; - for (i = 2; i < ngroups; i++) { + group = parent_group; + for (i = 0; i < ngroups; i++) { if (++group >= ngroups) group = 0; desc = ext2_get_group_desc (sb, group, &bh); --- linux-2.6.4-rc2/fs/ext2/super.c 2004-02-03 20:42:38.000000000 -0800 +++ 25/fs/ext2/super.c 2004-03-07 20:46:46.000000000 -0800 @@ -563,6 +563,7 @@ static int ext2_fill_super(struct super_ struct buffer_head * bh; struct ext2_sb_info * sbi; struct ext2_super_block * es; + struct inode *root; unsigned long block, sb_block = 1; unsigned long logic_sb_block = get_sb_block(&data); unsigned long offset = 0; @@ -815,15 +816,17 @@ static int ext2_fill_super(struct super_ */ sb->s_op = &ext2_sops; sb->s_export_op = &ext2_export_ops; - sb->s_root = d_alloc_root(iget(sb, EXT2_ROOT_INO)); - if (!sb->s_root || !S_ISDIR(sb->s_root->d_inode->i_mode) || - !sb->s_root->d_inode->i_blocks || !sb->s_root->d_inode->i_size) { - if (sb->s_root) { - dput(sb->s_root); - sb->s_root = NULL; - printk(KERN_ERR "EXT2-fs: corrupt root inode, run e2fsck\n"); - } else - printk(KERN_ERR "EXT2-fs: get root inode failed\n"); + root = iget(sb, EXT2_ROOT_INO); + sb->s_root = d_alloc_root(root); + if (!sb->s_root) { + iput(root); + printk(KERN_ERR "EXT2-fs: get root inode failed\n"); + goto failed_mount2; + } + if (!S_ISDIR(root->i_mode) || !root->i_blocks || !root->i_size) { + dput(sb->s_root); + sb->s_root = NULL; + printk(KERN_ERR "EXT2-fs: corrupt root inode, run e2fsck\n"); goto failed_mount2; } if (EXT2_HAS_COMPAT_FEATURE(sb, EXT3_FEATURE_COMPAT_HAS_JOURNAL)) --- linux-2.6.4-rc2/fs/ext3/acl.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/fs/ext3/acl.c 2004-03-07 20:48:08.000000000 -0800 @@ -157,10 +157,9 @@ ext3_iset_acl(struct inode *inode, struc static struct posix_acl * ext3_get_acl(struct inode *inode, int type) { - const size_t max_size = ext3_acl_size(EXT3_ACL_MAX_ENTRIES); struct ext3_inode_info *ei = EXT3_I(inode); int name_index; - char *value; + char *value = NULL; struct posix_acl *acl; int retval; @@ -185,17 +184,21 @@ ext3_get_acl(struct inode *inode, int ty default: return ERR_PTR(-EINVAL); } - value = kmalloc(max_size, GFP_KERNEL); - if (!value) - return ERR_PTR(-ENOMEM); - - retval = ext3_xattr_get(inode, name_index, "", value, max_size); - acl = ERR_PTR(retval); + retval = ext3_xattr_get(inode, name_index, "", NULL, 0); + if (retval > 0) { + value = kmalloc(retval, GFP_KERNEL); + if (!value) + return ERR_PTR(-ENOMEM); + retval = ext3_xattr_get(inode, name_index, "", value, retval); + } if (retval > 0) acl = ext3_acl_from_disk(value, retval); else if (retval == -ENODATA || retval == -ENOSYS) acl = NULL; - kfree(value); + else + acl = ERR_PTR(retval); + if (value) + kfree(value); if (!IS_ERR(acl)) { switch(type) { --- linux-2.6.4-rc2/fs/ext3/dir.c 2003-06-22 12:04:44.000000000 -0700 +++ 25/fs/ext3/dir.c 2004-03-07 20:47:08.000000000 -0800 @@ -224,7 +224,6 @@ revalidate: offset = 0; brelse (bh); } - update_atime(inode); out: return ret; } @@ -506,7 +505,6 @@ static int ext3_dx_readdir(struct file * } finished: info->last_pos = filp->f_pos; - update_atime(inode); return 0; } --- linux-2.6.4-rc2/fs/ext3/ialloc.c 2004-02-03 20:42:38.000000000 -0800 +++ 25/fs/ext3/ialloc.c 2004-03-07 20:46:46.000000000 -0800 @@ -398,8 +398,8 @@ static int find_group_other(struct super * That failed: try linear search for a free inode, even if that group * has no free blocks. */ - group = parent_group + 1; - for (i = 2; i < ngroups; i++) { + group = parent_group; + for (i = 0; i < ngroups; i++) { if (++group >= ngroups) group = 0; desc = ext3_get_group_desc (sb, group, &bh); --- linux-2.6.4-rc2/fs/ext3/inode.c 2004-02-03 20:42:38.000000000 -0800 +++ 25/fs/ext3/inode.c 2004-03-07 20:47:09.000000000 -0800 @@ -2772,9 +2772,28 @@ int ext3_setattr(struct dentry *dentry, if ((ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) || (ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) { + handle_t *handle; + + /* (user+group)*(old+new) structure, inode write (sb, + * inode block, ? - but truncate inode update has it) */ + handle = ext3_journal_start(inode, 4*EXT3_QUOTA_INIT_BLOCKS+3); + if (IS_ERR(handle)) { + error = PTR_ERR(handle); + goto err_out; + } error = DQUOT_TRANSFER(inode, attr) ? -EDQUOT : 0; - if (error) + if (error) { + ext3_journal_stop(handle); return error; + } + /* Update corresponding info in inode so that everything is in + * one transaction */ + if (attr->ia_valid & ATTR_UID) + inode->i_uid = attr->ia_uid; + if (attr->ia_valid & ATTR_GID) + inode->i_gid = attr->ia_gid; + error = ext3_mark_inode_dirty(handle, inode); + ext3_journal_stop(handle); } if (S_ISREG(inode->i_mode) && @@ -2853,7 +2872,9 @@ int ext3_writepage_trans_blocks(struct i ret = 2 * (bpp + indirects) + 2; #ifdef CONFIG_QUOTA - ret += 2 * EXT3_SINGLEDATA_TRANS_BLOCKS; + /* We know that structure was already allocated during DQUOT_INIT so + * we will be updating only the data blocks + inodes */ + ret += 2*EXT3_QUOTA_TRANS_BLOCKS; #endif return ret; --- linux-2.6.4-rc2/fs/ext3/namei.c 2004-02-17 20:48:45.000000000 -0800 +++ 25/fs/ext3/namei.c 2004-03-07 20:47:09.000000000 -0800 @@ -1633,7 +1633,8 @@ static int ext3_create (struct inode * d int err; handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + - EXT3_INDEX_EXTRA_TRANS_BLOCKS + 3); + EXT3_INDEX_EXTRA_TRANS_BLOCKS + 3 + + 2*EXT3_QUOTA_INIT_BLOCKS); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -1663,7 +1664,8 @@ static int ext3_mknod (struct inode * di return -EINVAL; handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + - EXT3_INDEX_EXTRA_TRANS_BLOCKS + 3); + EXT3_INDEX_EXTRA_TRANS_BLOCKS + 3 + + 2*EXT3_QUOTA_INIT_BLOCKS); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -1695,7 +1697,8 @@ static int ext3_mkdir(struct inode * dir return -EMLINK; handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + - EXT3_INDEX_EXTRA_TRANS_BLOCKS + 3); + EXT3_INDEX_EXTRA_TRANS_BLOCKS + 3 + + 2*EXT3_QUOTA_INIT_BLOCKS); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -1974,6 +1977,9 @@ static int ext3_rmdir (struct inode * di struct ext3_dir_entry_2 * de; handle_t *handle; + /* Initialize quotas before so that eventual writes go in + * separate transaction */ + DQUOT_INIT(dentry->d_inode); handle = ext3_journal_start(dir, EXT3_DELETE_TRANS_BLOCKS); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -1987,7 +1993,6 @@ static int ext3_rmdir (struct inode * di handle->h_sync = 1; inode = dentry->d_inode; - DQUOT_INIT(inode); retval = -EIO; if (le32_to_cpu(de->inode) != inode->i_ino) @@ -2031,6 +2036,9 @@ static int ext3_unlink(struct inode * di struct ext3_dir_entry_2 * de; handle_t *handle; + /* Initialize quotas before so that eventual writes go + * in separate transaction */ + DQUOT_INIT(dentry->d_inode); handle = ext3_journal_start(dir, EXT3_DELETE_TRANS_BLOCKS); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -2044,7 +2052,6 @@ static int ext3_unlink(struct inode * di goto end_unlink; inode = dentry->d_inode; - DQUOT_INIT(inode); retval = -EIO; if (le32_to_cpu(de->inode) != inode->i_ino) @@ -2087,7 +2094,8 @@ static int ext3_symlink (struct inode * return -ENAMETOOLONG; handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + - EXT3_INDEX_EXTRA_TRANS_BLOCKS + 5); + EXT3_INDEX_EXTRA_TRANS_BLOCKS + 5 + + 2*EXT3_QUOTA_INIT_BLOCKS); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -2172,6 +2180,10 @@ static int ext3_rename (struct inode * o old_bh = new_bh = dir_bh = NULL; + /* Initialize quotas before so that eventual writes go + * in separate transaction */ + if (new_dentry->d_inode) + DQUOT_INIT(new_dentry->d_inode); handle = ext3_journal_start(old_dir, 2 * EXT3_DATA_TRANS_BLOCKS + EXT3_INDEX_EXTRA_TRANS_BLOCKS + 2); if (IS_ERR(handle)) @@ -2198,8 +2210,6 @@ static int ext3_rename (struct inode * o if (!new_inode) { brelse (new_bh); new_bh = NULL; - } else { - DQUOT_INIT(new_inode); } } if (S_ISDIR(old_inode->i_mode)) { --- linux-2.6.4-rc2/fs/ext3/super.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/fs/ext3/super.c 2004-03-07 20:47:09.000000000 -0800 @@ -32,6 +32,9 @@ #include #include #include +#include +#include +#include #include #include "xattr.h" #include "acl.h" @@ -504,7 +507,43 @@ static void ext3_clear_inode(struct inod # define ext3_clear_inode NULL #endif -static struct dquot_operations ext3_qops; +#ifdef CONFIG_QUOTA + +#define QTYPE2NAME(t) ((t)==USRQUOTA?"user":"group") +#define QTYPE2MOPT(on, t) ((t)==USRQUOTA?((on)##USRJQUOTA):((on)##GRPJQUOTA)) + +static int ext3_dquot_initialize(struct inode *inode, int type); +static int ext3_dquot_drop(struct inode *inode); +static int ext3_write_dquot(struct dquot *dquot); +static int ext3_mark_dquot_dirty(struct dquot *dquot); +static int ext3_write_info(struct super_block *sb, int type); +static int ext3_quota_on(struct super_block *sb, int type, int format_id, char *path); +static int ext3_quota_on_mount(struct super_block *sb, int type); +static int ext3_quota_off_mount(struct super_block *sb, int type); + +static struct dquot_operations ext3_quota_operations = { + .initialize = ext3_dquot_initialize, + .drop = ext3_dquot_drop, + .alloc_space = dquot_alloc_space, + .alloc_inode = dquot_alloc_inode, + .free_space = dquot_free_space, + .free_inode = dquot_free_inode, + .transfer = dquot_transfer, + .write_dquot = ext3_write_dquot, + .mark_dirty = ext3_mark_dquot_dirty, + .write_info = ext3_write_info +}; + +static struct quotactl_ops ext3_qctl_operations = { + .quota_on = ext3_quota_on, + .quota_off = vfs_quota_off, + .quota_sync = vfs_quota_sync, + .get_info = vfs_get_dqinfo, + .set_info = vfs_set_dqinfo, + .get_dqblk = vfs_get_dqblk, + .set_dqblk = vfs_set_dqblk +}; +#endif static struct super_operations ext3_sops = { .alloc_inode = ext3_alloc_inode, @@ -536,6 +575,8 @@ enum { Opt_user_xattr, Opt_nouser_xattr, Opt_acl, Opt_noacl, Opt_noload, Opt_commit, Opt_journal_update, Opt_journal_inum, Opt_abort, Opt_data_journal, Opt_data_ordered, Opt_data_writeback, + Opt_usrjquota, Opt_grpjquota, Opt_offusrjquota, Opt_offgrpjquota, + Opt_jqfmt_vfsold, Opt_jqfmt_vfsv0, Opt_ignore, Opt_err, }; @@ -571,6 +612,12 @@ static match_table_t tokens = { {Opt_data_journal, "data=journal"}, {Opt_data_ordered, "data=ordered"}, {Opt_data_writeback, "data=writeback"}, + {Opt_offusrjquota, "usrjquota="}, + {Opt_usrjquota, "usrjquota=%s"}, + {Opt_offgrpjquota, "grpjquota="}, + {Opt_grpjquota, "grpjquota=%s"}, + {Opt_jqfmt_vfsold, "jqfmt=vfsold"}, + {Opt_jqfmt_vfsv0, "jqfmt=vfsv0"}, {Opt_ignore, "grpquota"}, {Opt_ignore, "noquota"}, {Opt_ignore, "quota"}, @@ -598,13 +645,17 @@ static unsigned long get_sb_block(void * return sb_block; } -static int parse_options (char * options, struct ext3_sb_info *sbi, +static int parse_options (char * options, struct super_block *sb, unsigned long * inum, int is_remount) { + struct ext3_sb_info *sbi = EXT3_SB(sb); char * p; substring_t args[MAX_OPT_ARGS]; int data_opt = 0; int option; +#ifdef CONFIG_QUOTA + int qtype; +#endif if (!options) return 1; @@ -759,6 +810,76 @@ static int parse_options (char * options sbi->s_mount_opt |= data_opt; } break; +#ifdef CONFIG_QUOTA + case Opt_usrjquota: + qtype = USRQUOTA; + goto set_qf_name; + case Opt_grpjquota: + qtype = GRPQUOTA; +set_qf_name: + if (sb_any_quota_enabled(sb)) { + printk(KERN_ERR + "EXT3-fs: Cannot change journalled " + "quota options when quota turned on.\n"); + return 0; + } + if (sbi->s_qf_names[qtype]) { + printk(KERN_ERR + "EXT3-fs: %s quota file already " + "specified.\n", QTYPE2NAME(qtype)); + return 0; + } + sbi->s_qf_names[qtype] = match_strdup(&args[0]); + if (!sbi->s_qf_names[qtype]) { + printk(KERN_ERR + "EXT3-fs: not enough memory for " + "storing quotafile name.\n"); + return 0; + } + if (strchr(sbi->s_qf_names[qtype], '/')) { + printk(KERN_ERR + "EXT3-fs: quotafile must be on " + "filesystem root.\n"); + kfree(sbi->s_qf_names[qtype]); + sbi->s_qf_names[qtype] = NULL; + return 0; + } + break; + case Opt_offusrjquota: + qtype = USRQUOTA; + goto clear_qf_name; + case Opt_offgrpjquota: + qtype = GRPQUOTA; +clear_qf_name: + if (sb_any_quota_enabled(sb)) { + printk(KERN_ERR "EXT3-fs: Cannot change " + "journalled quota options when " + "quota turned on.\n"); + return 0; + } + if (sbi->s_qf_names[qtype]) { + kfree(sbi->s_qf_names[qtype]); + sbi->s_qf_names[qtype] = NULL; + } + break; + case Opt_jqfmt_vfsold: + sbi->s_jquota_fmt = QFMT_VFS_OLD; + break; + case Opt_jqfmt_vfsv0: + sbi->s_jquota_fmt = QFMT_VFS_V0; + break; +#else + case Opt_usrjquota: + case Opt_grpjquota: + case Opt_offusrjquota: + case Opt_offgrpjquota: + case Opt_jqfmt_vfsold: + case Opt_jqfmt_vfsv0: + printk(KERN_ERR + "EXT3-fs: journalled quota options not " + "supported.\n"); + break; +#endif case Opt_abort: set_opt(sbi->s_mount_opt, ABORT); break; @@ -771,6 +892,13 @@ static int parse_options (char * options return 0; } } +#ifdef CONFIG_QUOTA + if (!sbi->s_jquota_fmt && (sbi->s_qf_names[0] || sbi->s_qf_names[1])) { + printk(KERN_ERR + "EXT3-fs: journalled quota format not specified.\n"); + return 0; + } +#endif return 1; } @@ -930,6 +1058,9 @@ static void ext3_orphan_cleanup (struct { unsigned int s_flags = sb->s_flags; int nr_orphans = 0, nr_truncates = 0; +#ifdef CONFIG_QUOTA + int i; +#endif if (!es->s_last_orphan) { jbd_debug(4, "no orphan inodes to clean up\n"); return; @@ -949,6 +1080,20 @@ static void ext3_orphan_cleanup (struct sb->s_id); sb->s_flags &= ~MS_RDONLY; } +#ifdef CONFIG_QUOTA + /* Needed for iput() to work correctly and not trash data */ + sb->s_flags |= MS_ACTIVE; + /* Turn on quotas so that they are updated correctly */ + for (i = 0; i < MAXQUOTAS; i++) { + if (EXT3_SB(sb)->s_qf_names[i]) { + int ret = ext3_quota_on_mount(sb, i); + if (ret < 0) + printk(KERN_ERR + "EXT3-fs: Cannot turn on journalled " + "quota: error %d\n", ret); + } + } +#endif while (es->s_last_orphan) { struct inode *inode; @@ -960,6 +1105,7 @@ static void ext3_orphan_cleanup (struct } list_add(&EXT3_I(inode)->i_orphan, &EXT3_SB(sb)->s_orphan); + DQUOT_INIT(inode); if (inode->i_nlink) { printk(KERN_DEBUG "%s: truncating inode %ld to %Ld bytes\n", @@ -987,6 +1133,13 @@ static void ext3_orphan_cleanup (struct if (nr_truncates) printk(KERN_INFO "EXT3-fs: %s: %d truncate%s cleaned up\n", sb->s_id, PLURAL(nr_truncates)); +#ifdef CONFIG_QUOTA + /* Turn quotas off */ + for (i = 0; i < MAXQUOTAS; i++) { + if (sb_dqopt(sb)->files[i]) + ext3_quota_off_mount(sb, i); + } +#endif sb->s_flags = s_flags; /* Restore MS_RDONLY status */ } @@ -1040,6 +1193,7 @@ static int ext3_fill_super (struct super unsigned long offset = 0; unsigned long journal_inum = 0; unsigned long def_mount_opts; + struct inode *root; int blocksize; int hblock; int db_count; @@ -1116,7 +1270,7 @@ static int ext3_fill_super (struct super sbi->s_resuid = le16_to_cpu(es->s_def_resuid); sbi->s_resgid = le16_to_cpu(es->s_def_resgid); - if (!parse_options ((char *) data, sbi, &journal_inum, 0)) + if (!parse_options ((char *) data, sb, &journal_inum, 0)) goto failed_mount; sb->s_flags |= MS_ONE_SECOND; @@ -1295,7 +1449,10 @@ static int ext3_fill_super (struct super */ sb->s_op = &ext3_sops; sb->s_export_op = &ext3_export_ops; - sb->dq_op = &ext3_qops; +#ifdef CONFIG_QUOTA + sb->s_qcop = &ext3_qctl_operations; + sb->dq_op = &ext3_quota_operations; +#endif INIT_LIST_HEAD(&sbi->s_orphan); /* unlinked but open files */ sb->s_root = 0; @@ -1354,16 +1511,17 @@ static int ext3_fill_super (struct super * so we can safely mount the rest of the filesystem now. */ - sb->s_root = d_alloc_root(iget(sb, EXT3_ROOT_INO)); - if (!sb->s_root || !S_ISDIR(sb->s_root->d_inode->i_mode) || - !sb->s_root->d_inode->i_blocks || !sb->s_root->d_inode->i_size) { - if (sb->s_root) { - dput(sb->s_root); - sb->s_root = NULL; - printk(KERN_ERR - "EXT3-fs: corrupt root inode, run e2fsck\n"); - } else - printk(KERN_ERR "EXT3-fs: get root inode failed\n"); + root = iget(sb, EXT3_ROOT_INO); + sb->s_root = d_alloc_root(root); + if (!sb->s_root) { + printk(KERN_ERR "EXT3-fs: get root inode failed\n"); + iput(root); + goto failed_mount3; + } + if (!S_ISDIR(root->i_mode) || !root->i_blocks || !root->i_size) { + dput(sb->s_root); + sb->s_root = NULL; + printk(KERN_ERR "EXT3-fs: corrupt root inode, run e2fsck\n"); goto failed_mount3; } @@ -1404,6 +1562,12 @@ failed_mount2: brelse(sbi->s_group_desc[i]); kfree(sbi->s_group_desc); failed_mount: +#ifdef CONFIG_QUOTA + for (i = 0; i < MAXQUOTAS; i++) { + if (sbi->s_qf_names[i]) + kfree(sbi->s_qf_names[i]); + } +#endif ext3_blkdev_remove(sbi); brelse(bh); out_fail: @@ -1830,7 +1994,7 @@ int ext3_remount (struct super_block * s /* * Allow the "check" option to be passed as a remount option. */ - if (!parse_options(data, sbi, &tmp, 1)) + if (!parse_options(data, sb, &tmp, 1)) return -EINVAL; if (sbi->s_mount_opt & EXT3_MOUNT_ABORT) @@ -1950,45 +2114,152 @@ int ext3_statfs (struct super_block * sb #ifdef CONFIG_QUOTA -/* Blocks: (2 data blocks) * (3 indirect + 1 descriptor + 1 bitmap) + superblock */ -#define EXT3_OLD_QFMT_BLOCKS 11 -/* Blocks: quota info + (4 pointer blocks + 1 entry block) * (3 indirect + 1 descriptor + 1 bitmap) + superblock */ -#define EXT3_V0_QFMT_BLOCKS 27 +static inline struct inode *dquot_to_inode(struct dquot *dquot) +{ + return sb_dqopt(dquot->dq_sb)->files[dquot->dq_type]->f_dentry->d_inode; +} + +static int ext3_dquot_initialize(struct inode *inode, int type) +{ + handle_t *handle; + int ret, err; + + /* We may create quota structure so we need to reserve enough blocks */ + handle = ext3_journal_start(inode, 2*EXT3_QUOTA_INIT_BLOCKS); + if (IS_ERR(handle)) + return PTR_ERR(handle); + ret = dquot_initialize(inode, type); + err = ext3_journal_stop(handle); + if (!ret) + ret = err; + return ret; +} -static int (*old_write_dquot)(struct dquot *dquot); +static int ext3_dquot_drop(struct inode *inode) +{ + handle_t *handle; + int ret, err; + + /* We may delete quota structure so we need to reserve enough blocks */ + handle = ext3_journal_start(inode, 2*EXT3_QUOTA_INIT_BLOCKS); + if (IS_ERR(handle)) + return PTR_ERR(handle); + ret = dquot_drop(inode); + err = ext3_journal_stop(handle); + if (!ret) + ret = err; + return ret; +} static int ext3_write_dquot(struct dquot *dquot) { - int nblocks; - int ret; - int err; + int ret, err; handle_t *handle; - struct quota_info *dqops = sb_dqopt(dquot->dq_sb); - struct inode *qinode; - switch (dqops->info[dquot->dq_type].dqi_format->qf_fmt_id) { - case QFMT_VFS_OLD: - nblocks = EXT3_OLD_QFMT_BLOCKS; - break; - case QFMT_VFS_V0: - nblocks = EXT3_V0_QFMT_BLOCKS; - break; - default: - nblocks = EXT3_MAX_TRANS_DATA; - } - qinode = dqops->files[dquot->dq_type]->f_dentry->d_inode; - handle = ext3_journal_start(qinode, nblocks); - if (IS_ERR(handle)) { - ret = PTR_ERR(handle); - goto out; - } - ret = old_write_dquot(dquot); + handle = ext3_journal_start(dquot_to_inode(dquot), + EXT3_QUOTA_TRANS_BLOCKS); + if (IS_ERR(handle)) + return PTR_ERR(handle); + ret = dquot_commit(dquot); err = ext3_journal_stop(handle); - if (ret == 0) + if (!ret) + ret = err; + return ret; +} + +static int ext3_mark_dquot_dirty(struct dquot * dquot) +{ + /* Are we journalling quotas? */ + if (EXT3_SB(dquot->dq_sb)->s_qf_names[0] || + EXT3_SB(dquot->dq_sb)->s_qf_names[1]) + return ext3_write_dquot(dquot); + else + return dquot_mark_dquot_dirty(dquot); +} + +static int ext3_write_info(struct super_block *sb, int type) +{ + int ret, err; + handle_t *handle; + + /* Data block + inode block */ + handle = ext3_journal_start(sb->s_root->d_inode, 2); + if (IS_ERR(handle)) + return PTR_ERR(handle); + ret = dquot_commit_info(sb, type); + err = ext3_journal_stop(handle); + if (!ret) ret = err; -out: return ret; } + +/* + * Turn on quotas during mount time - we need to find + * the quota file and such... + */ +static int ext3_quota_on_mount(struct super_block *sb, int type) +{ + int err; + struct dentry *dentry; + struct qstr name = { .name = EXT3_SB(sb)->s_qf_names[type], + .hash = 0, + .len = strlen(EXT3_SB(sb)->s_qf_names[type])}; + + dentry = lookup_hash(&name, sb->s_root); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + err = vfs_quota_on_mount(type, EXT3_SB(sb)->s_jquota_fmt, dentry); + if (err) + dput(dentry); + /* We keep the dentry reference if everything went ok - we drop it + * on quota_off time */ + return err; +} + +/* Turn quotas off during mount time */ +static int ext3_quota_off_mount(struct super_block *sb, int type) +{ + int err; + struct dentry *dentry; + + dentry = sb_dqopt(sb)->files[type]->f_dentry; + err = vfs_quota_off_mount(sb, type); + /* We invalidate dentry - it has at least wrong hash... */ + d_invalidate(dentry); + dput(dentry); + return err; +} + +/* + * Standard function to be called on quota_on + */ +static int ext3_quota_on(struct super_block *sb, int type, int format_id, + char *path) +{ + int err; + struct nameidata nd; + + /* Not journalling quota? */ + if (!EXT3_SB(sb)->s_qf_names[0] && !EXT3_SB(sb)->s_qf_names[1]) + return vfs_quota_on(sb, type, format_id, path); + err = path_lookup(path, LOOKUP_FOLLOW, &nd); + if (err) + return err; + /* Quotafile not on the same filesystem? */ + if (nd.mnt->mnt_sb != sb) + return -EXDEV; + /* Quotafile not of fs root? */ + if (nd.dentry->d_parent->d_inode != sb->s_root->d_inode) + printk(KERN_WARNING + "EXT3-fs: Quota file not on filesystem root. " + "Journalled quota will not work.\n"); + if (!ext3_should_journal_data(nd.dentry->d_inode)) + printk(KERN_WARNING "EXT3-fs: Quota file does not have " + "data-journalling. Journalled quota will not work.\n"); + path_release(&nd); + return vfs_quota_on(sb, type, format_id, path); +} + #endif static struct super_block *ext3_get_sb(struct file_system_type *fs_type, @@ -2013,11 +2284,6 @@ static int __init init_ext3_fs(void) err = init_inodecache(); if (err) goto out1; -#ifdef CONFIG_QUOTA - init_dquot_operations(&ext3_qops); - old_write_dquot = ext3_qops.write_dquot; - ext3_qops.write_dquot = ext3_write_dquot; -#endif err = register_filesystem(&ext3_fs_type); if (err) goto out; --- linux-2.6.4-rc2/fs/fat/inode.c 2004-01-09 00:04:32.000000000 -0800 +++ 25/fs/fat/inode.c 2004-03-07 20:47:08.000000000 -0800 @@ -778,6 +778,7 @@ int fat_fill_super(struct super_block *s sb->s_fs_info = sbi; memset(sbi, 0, sizeof(struct msdos_sb_info)); + sb->s_flags |= MS_NODIRATIME; sb->s_magic = MSDOS_SUPER_MAGIC; sb->s_op = &fat_sops; sb->s_export_op = &fat_export_ops; --- linux-2.6.4-rc2/fs/fcntl.c 2004-02-03 20:42:38.000000000 -0800 +++ 25/fs/fcntl.c 2004-03-07 20:46:46.000000000 -0800 @@ -19,7 +19,7 @@ #include #include -void set_close_on_exec(unsigned int fd, int flag) +void fastcall set_close_on_exec(unsigned int fd, int flag) { struct files_struct *files = current->files; spin_lock(&files->file_lock); --- linux-2.6.4-rc2/fs/file_table.c 2004-02-03 20:42:38.000000000 -0800 +++ 25/fs/file_table.c 2004-03-07 20:46:46.000000000 -0800 @@ -152,7 +152,7 @@ void close_private_file(struct file *fil EXPORT_SYMBOL(close_private_file); -void fput(struct file *file) +void fastcall fput(struct file *file) { if (atomic_dec_and_test(&file->f_count)) __fput(file); @@ -163,7 +163,7 @@ EXPORT_SYMBOL(fput); /* __fput is called from task context when aio completion releases the last * last use of a struct file *. Do not use otherwise. */ -void __fput(struct file *file) +void fastcall __fput(struct file *file) { struct dentry *dentry = file->f_dentry; struct vfsmount *mnt = file->f_vfsmnt; @@ -192,7 +192,7 @@ void __fput(struct file *file) mntput(mnt); } -struct file *fget(unsigned int fd) +struct file fastcall *fget(unsigned int fd) { struct file *file; struct files_struct *files = current->files; @@ -214,7 +214,7 @@ EXPORT_SYMBOL(fget); * and a flag is returned to be passed to the corresponding fput_light(). * There must not be a cloning between an fget_light/fput_light pair. */ -struct file *fget_light(unsigned int fd, int *fput_needed) +struct file fastcall *fget_light(unsigned int fd, int *fput_needed) { struct file *file; struct files_struct *files = current->files; --- linux-2.6.4-rc2/fs/freevxfs/vxfs_super.c 2004-02-17 20:48:45.000000000 -0800 +++ 25/fs/freevxfs/vxfs_super.c 2004-03-07 20:47:08.000000000 -0800 @@ -143,6 +143,9 @@ static int vxfs_fill_super(struct super_ struct vxfs_sb *rsbp; struct buffer_head *bp = NULL; u_long bsize; + struct inode *root; + + sbp->s_flags |= MS_RDONLY; infp = kmalloc(sizeof(*infp), GFP_KERNEL); if (!infp) { @@ -208,8 +211,10 @@ static int vxfs_fill_super(struct super_ } sbp->s_op = &vxfs_super_ops; - sbp->s_root = d_alloc_root(iget(sbp, VXFS_ROOT_INO)); + root = iget(sbp, VXFS_ROOT_INO); + sbp->s_root = d_alloc_root(root); if (!sbp->s_root) { + iput(root); printk(KERN_WARNING "vxfs: unable to get root dentry.\n"); goto out_free_ilist; } --- linux-2.6.4-rc2/fs/fs-writeback.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/fs/fs-writeback.c 2004-03-07 20:48:24.000000000 -0800 @@ -22,6 +22,7 @@ #include #include #include +#include extern struct super_block *blockdev_superblock; @@ -138,6 +139,7 @@ __sync_single_inode(struct inode *inode, struct address_space *mapping = inode->i_mapping; struct super_block *sb = inode->i_sb; int wait = wbc->sync_mode == WB_SYNC_ALL; + int blkdev = inode->i_sb == blockdev_superblock; BUG_ON(inode->i_state & I_LOCK); @@ -145,6 +147,26 @@ __sync_single_inode(struct inode *inode, dirty = inode->i_state & I_DIRTY; inode->i_state |= I_LOCK; inode->i_state &= ~I_DIRTY; + spin_unlock(&inode_lock); + + /* + * Serialize writebacks except for blockdevs + */ + if (!blkdev) { + /* + * Only allow 1 SYNC writeback at a time, to be able to wait + * for all i/o without worrying about racing WB_SYNC_NONE + * writers. + */ + if (wait) { + down_write(&mapping->wb_rwsema); + } else { + if (!down_read_trylock(&mapping->wb_rwsema)) { + wbc->encountered_congestion = 1; + goto skip_writeback; + } + } + } /* * smp_rmb(); note: if you remove write_lock below, you must add this. @@ -156,10 +178,17 @@ __sync_single_inode(struct inode *inode, if (wait || !wbc->for_kupdate || list_empty(&mapping->io_pages)) list_splice_init(&mapping->dirty_pages, &mapping->io_pages); spin_unlock(&mapping->page_lock); - spin_unlock(&inode_lock); do_writepages(mapping, wbc); + if (!blkdev) { + if (wait) + up_write(&mapping->wb_rwsema); + else + up_read(&mapping->wb_rwsema); + } + +skip_writeback: /* Don't write the inode if only I_DIRTY_PAGES was set */ if (dirty & (I_DIRTY_SYNC | I_DIRTY_DATASYNC)) write_inode(inode, wait); @@ -185,6 +214,14 @@ __sync_single_inode(struct inode *inode, } else if (atomic_read(&inode->i_count)) { list_move(&inode->i_list, &inode_in_use); } else { + if (!strcmp(inode->i_sb->s_type->name, "ext3")) { + struct ext3_inode_info *ext3_i; + ext3_i = container_of(inode, + struct ext3_inode_info, + vfs_inode); + if (!list_empty(&ext3_i->i_orphan)) + WARN_ON(1); + } list_move(&inode->i_list, &inode_unused); } } @@ -276,7 +313,12 @@ sync_sb_inodes(struct super_block *sb, s break; } - if (wbc->nonblocking && bdi_write_congested(bdi)) { + /* + * wbc->encountered_congestion is set if we cannot get + * the wb_rwsema. + */ + if (wbc->nonblocking && + (bdi_write_congested(bdi) || wbc->encountered_congestion)) { wbc->encountered_congestion = 1; if (sb != blockdev_superblock) break; /* Skip a congested fs */ --- linux-2.6.4-rc2/fs/hfsplus/dir.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/fs/hfsplus/dir.c 2004-03-07 20:46:46.000000000 -0800 @@ -399,6 +399,7 @@ int hfsplus_symlink(struct inode *dir, s res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode); if (!res) { + dentry->d_fsdata = (void *)inode->i_ino; d_instantiate(dentry, inode); mark_inode_dirty(inode); } @@ -424,6 +425,7 @@ int hfsplus_mknod(struct inode *dir, str return res; } init_special_inode(inode, mode, rdev); + dentry->d_fsdata = (void *)inode->i_ino; d_instantiate(dentry, inode); mark_inode_dirty(inode); --- linux-2.6.4-rc2/fs/hfsplus/inode.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/fs/hfsplus/inode.c 2004-03-07 20:46:46.000000000 -0800 @@ -335,6 +335,14 @@ struct inode *hfsplus_new_inode(struct s init_MUTEX(&HFSPLUS_I(inode).extents_lock); atomic_set(&HFSPLUS_I(inode).opencnt, 0); HFSPLUS_I(inode).flags = 0; + memset(HFSPLUS_I(inode).first_extents, 0, sizeof(hfsplus_extent_rec)); + memset(HFSPLUS_I(inode).cached_extents, 0, sizeof(hfsplus_extent_rec)); + HFSPLUS_I(inode).alloc_blocks = 0; + HFSPLUS_I(inode).first_blocks = 0; + HFSPLUS_I(inode).cached_start = 0; + HFSPLUS_I(inode).cached_blocks = 0; + HFSPLUS_I(inode).phys_size = 0; + HFSPLUS_I(inode).rsrc_inode = 0; if (S_ISDIR(inode->i_mode)) { inode->i_size = 2; HFSPLUS_SB(sb).folder_count++; @@ -346,14 +354,6 @@ struct inode *hfsplus_new_inode(struct s inode->i_fop = &hfsplus_file_operations; inode->i_mapping->a_ops = &hfsplus_aops; HFSPLUS_I(inode).clump_blocks = HFSPLUS_SB(sb).data_clump_blocks; - memset(HFSPLUS_I(inode).first_extents, 0, sizeof(hfsplus_extent_rec)); - memset(HFSPLUS_I(inode).cached_extents, 0, sizeof(hfsplus_extent_rec)); - HFSPLUS_I(inode).alloc_blocks = 0; - HFSPLUS_I(inode).first_blocks = 0; - HFSPLUS_I(inode).cached_start = 0; - HFSPLUS_I(inode).cached_blocks = 0; - HFSPLUS_I(inode).phys_size = 0; - HFSPLUS_I(inode).rsrc_inode = 0; } else if (S_ISLNK(inode->i_mode)) { HFSPLUS_SB(sb).file_count++; inode->i_op = &page_symlink_inode_operations; --- linux-2.6.4-rc2/fs/hfsplus/super.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/fs/hfsplus/super.c 2004-03-07 20:46:46.000000000 -0800 @@ -278,6 +278,7 @@ static int hfsplus_fill_super(struct sup struct hfsplus_sb_info *sbi; hfsplus_cat_entry entry; struct hfs_find_data fd; + struct inode *root; struct qstr str; int err = -EINVAL; @@ -364,10 +365,12 @@ static int hfsplus_fill_super(struct sup } /* Load the root directory */ - sb->s_root = d_alloc_root(iget(sb, HFSPLUS_ROOT_CNID)); + root = iget(sb, HFSPLUS_ROOT_CNID); + sb->s_root = d_alloc_root(root); if (!sb->s_root) { if (!silent) printk("HFS+-fs: failed to load root directory\n"); + iput(root); goto cleanup; } --- linux-2.6.4-rc2/fs/hfs/super.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/fs/hfs/super.c 2004-03-07 20:47:08.000000000 -0800 @@ -268,6 +268,7 @@ static int hfs_fill_super(struct super_b } sb->s_op = &hfs_super_operations; + sb->s_flags |= MS_NODIRATIME; init_MUTEX(&sbi->bitmap_lock); res = hfs_mdb_get(sb); @@ -294,13 +295,15 @@ static int hfs_fill_super(struct super_b sb->s_root = d_alloc_root(root_inode); if (!sb->s_root) - goto bail_no_root; + goto bail_iput; sb->s_root->d_op = &hfs_dentry_operations; /* everything's okay */ return 0; +bail_iput: + iput(root_inode); bail_no_root: hfs_warn("hfs_fs: get root inode failed.\n"); hfs_mdb_put(sb); --- linux-2.6.4-rc2/fs/hpfs/buffer.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/fs/hpfs/buffer.c 2004-03-07 20:47:54.000000000 -0800 @@ -60,74 +60,6 @@ void hpfs_unlock_inode(struct inode *i) } } -void hpfs_lock_2inodes(struct inode *i1, struct inode *i2) -{ - if (!i2 || i1 == i2) { - hpfs_lock_inode(i1); - } else if (!i1) { - hpfs_lock_inode(i2); - } else { - struct hpfs_inode_info *hpfs_i1 = hpfs_i(i1); - struct hpfs_inode_info *hpfs_i2 = hpfs_i(i2); - if (i1->i_ino < i2->i_ino) { - down(&hpfs_i1->i_sem); - down(&hpfs_i2->i_sem); - } else { - down(&hpfs_i2->i_sem); - down(&hpfs_i1->i_sem); - } - } -} - -void hpfs_unlock_2inodes(struct inode *i1, struct inode *i2) -{ - /* order of up() doesn't matter here */ - hpfs_unlock_inode(i1); - hpfs_unlock_inode(i2); -} - -void hpfs_lock_3inodes(struct inode *i1, struct inode *i2, struct inode *i3) -{ - if (!i1) { hpfs_lock_2inodes(i2, i3); return; } - if (!i2) { hpfs_lock_2inodes(i1, i3); return; } - if (!i3) { hpfs_lock_2inodes(i1, i2); return; } - if (i1->i_ino < i2->i_ino && i1->i_ino < i3->i_ino) { - struct hpfs_inode_info *hpfs_i1 = hpfs_i(i1); - down(&hpfs_i1->i_sem); - hpfs_lock_2inodes(i2, i3); - } else if (i2->i_ino < i1->i_ino && i2->i_ino < i3->i_ino) { - struct hpfs_inode_info *hpfs_i2 = hpfs_i(i2); - down(&hpfs_i2->i_sem); - hpfs_lock_2inodes(i1, i3); - } else if (i3->i_ino < i1->i_ino && i3->i_ino < i2->i_ino) { - struct hpfs_inode_info *hpfs_i3 = hpfs_i(i3); - down(&hpfs_i3->i_sem); - hpfs_lock_2inodes(i1, i2); - } else if (i1->i_ino != i2->i_ino) hpfs_lock_2inodes(i1, i2); - else hpfs_lock_2inodes(i1, i3); -} - -void hpfs_unlock_3inodes(struct inode *i1, struct inode *i2, struct inode *i3) -{ - if (!i1) { hpfs_unlock_2inodes(i2, i3); return; } - if (!i2) { hpfs_unlock_2inodes(i1, i3); return; } - if (!i3) { hpfs_unlock_2inodes(i1, i2); return; } - if (i1->i_ino < i2->i_ino && i1->i_ino < i3->i_ino) { - struct hpfs_inode_info *hpfs_i1 = hpfs_i(i1); - hpfs_unlock_2inodes(i2, i3); - up(&hpfs_i1->i_sem); - } else if (i2->i_ino < i1->i_ino && i2->i_ino < i3->i_ino) { - struct hpfs_inode_info *hpfs_i2 = hpfs_i(i2); - hpfs_unlock_2inodes(i1, i3); - up(&hpfs_i2->i_sem); - } else if (i3->i_ino < i1->i_ino && i3->i_ino < i2->i_ino) { - struct hpfs_inode_info *hpfs_i3 = hpfs_i(i3); - hpfs_unlock_2inodes(i1, i2); - up(&hpfs_i3->i_sem); - } else if (i1->i_ino != i2->i_ino) hpfs_unlock_2inodes(i1, i2); - else hpfs_unlock_2inodes(i1, i3); -} - /* Map a sector into a buffer and return pointers to it and to the buffer. */ void *hpfs_map_sector(struct super_block *s, unsigned secno, struct buffer_head **bhp, --- linux-2.6.4-rc2/fs/hpfs/hpfs_fn.h 2003-10-08 15:07:09.000000000 -0700 +++ 25/fs/hpfs/hpfs_fn.h 2004-03-07 20:47:54.000000000 -0800 @@ -196,10 +196,6 @@ void hpfs_lock_iget(struct super_block * void hpfs_unlock_iget(struct super_block *); void hpfs_lock_inode(struct inode *); void hpfs_unlock_inode(struct inode *); -void hpfs_lock_2inodes(struct inode *, struct inode *); -void hpfs_unlock_2inodes(struct inode *, struct inode *); -void hpfs_lock_3inodes(struct inode *, struct inode *, struct inode *); -void hpfs_unlock_3inodes(struct inode *, struct inode *, struct inode *); void *hpfs_map_sector(struct super_block *, unsigned, struct buffer_head **, int); void *hpfs_get_sector(struct super_block *, unsigned, struct buffer_head **); void *hpfs_map_4sectors(struct super_block *, unsigned, struct quad_buffer_head *, int); --- linux-2.6.4-rc2/fs/hpfs/namei.c 2003-09-27 18:57:46.000000000 -0700 +++ 25/fs/hpfs/namei.c 2004-03-07 20:47:55.000000000 -0800 @@ -28,8 +28,13 @@ int hpfs_mkdir(struct inode *dir, struct int err; if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err; lock_kernel(); - if (!(fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh))) goto bail; - if (!(dnode = hpfs_alloc_dnode(dir->i_sb, fno, &dno, &qbh0, 1))) goto bail1; + err = -ENOSPC; + fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh); + if (!fnode) + goto bail; + dnode = hpfs_alloc_dnode(dir->i_sb, fno, &dno, &qbh0, 1); + if (!dnode) + goto bail1; memset(&dee, 0, sizeof dee); dee.directory = 1; if (!(mode & 0222)) dee.read_only = 1; @@ -39,15 +44,11 @@ int hpfs_mkdir(struct inode *dir, struct dee.creation_date = dee.write_date = dee.read_date = gmt_to_local(dir->i_sb, get_seconds()); hpfs_lock_inode(dir); r = hpfs_add_dirent(dir, (char *)name, len, &dee, 0); - if (r == 1) goto bail2; + if (r == 1) + goto bail2; if (r == -1) { - brelse(bh); - hpfs_brelse4(&qbh0); - hpfs_free_sectors(dir->i_sb, fno, 1); - hpfs_free_dnode(dir->i_sb, dno); - hpfs_unlock_inode(dir); - unlock_kernel(); - return -EEXIST; + err = -EEXIST; + goto bail2; } fnode->len = len; memcpy(fnode->name, name, len > 15 ? 15 : len); @@ -103,7 +104,7 @@ bail1: hpfs_free_sectors(dir->i_sb, fno, 1); bail: unlock_kernel(); - return -ENOSPC; + return err; } int hpfs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd) @@ -120,7 +121,10 @@ int hpfs_create(struct inode *dir, struc if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err; lock_kernel(); - if (!(fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh))) goto bail; + err = -ENOSPC; + fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh); + if (!fnode) + goto bail; memset(&dee, 0, sizeof dee); if (!(mode & 0222)) dee.read_only = 1; dee.archive = 1; @@ -129,13 +133,11 @@ int hpfs_create(struct inode *dir, struc dee.creation_date = dee.write_date = dee.read_date = gmt_to_local(dir->i_sb, get_seconds()); hpfs_lock_inode(dir); r = hpfs_add_dirent(dir, (char *)name, len, &dee, 0); - if (r == 1) goto bail1; + if (r == 1) + goto bail1; if (r == -1) { - brelse(bh); - hpfs_free_sectors(dir->i_sb, fno, 1); - hpfs_unlock_inode(dir); - unlock_kernel(); - return -EEXIST; + err = -EEXIST; + goto bail1; } fnode->len = len; memcpy(fnode->name, name, len > 15 ? 15 : len); @@ -178,7 +180,7 @@ bail1: hpfs_unlock_inode(dir); bail: unlock_kernel(); - return -ENOSPC; + return err; } int hpfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev) @@ -197,7 +199,10 @@ int hpfs_mknod(struct inode *dir, struct if (!new_valid_dev(rdev)) return -EINVAL; lock_kernel(); - if (!(fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh))) goto bail; + err = -ENOSPC; + fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh); + if (!fnode) + goto bail; memset(&dee, 0, sizeof dee); if (!(mode & 0222)) dee.read_only = 1; dee.archive = 1; @@ -206,13 +211,11 @@ int hpfs_mknod(struct inode *dir, struct dee.creation_date = dee.write_date = dee.read_date = gmt_to_local(dir->i_sb, get_seconds()); hpfs_lock_inode(dir); r = hpfs_add_dirent(dir, (char *)name, len, &dee, 0); - if (r == 1) goto bail1; + if (r == 1) + goto bail1; if (r == -1) { - brelse(bh); - hpfs_free_sectors(dir->i_sb, fno, 1); - hpfs_unlock_inode(dir); - unlock_kernel(); - return -EEXIST; + err = -EEXIST; + goto bail1; } fnode->len = len; memcpy(fnode->name, name, len > 15 ? 15 : len); @@ -248,7 +251,7 @@ bail1: hpfs_unlock_inode(dir); bail: unlock_kernel(); - return -ENOSPC; + return err; } extern struct address_space_operations hpfs_symlink_aops; @@ -270,7 +273,10 @@ int hpfs_symlink(struct inode *dir, stru unlock_kernel(); return -EPERM; } - if (!(fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh))) goto bail; + err = -ENOSPC; + fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh); + if (!fnode) + goto bail; memset(&dee, 0, sizeof dee); dee.archive = 1; dee.hidden = name[0] == '.'; @@ -278,13 +284,11 @@ int hpfs_symlink(struct inode *dir, stru dee.creation_date = dee.write_date = dee.read_date = gmt_to_local(dir->i_sb, get_seconds()); hpfs_lock_inode(dir); r = hpfs_add_dirent(dir, (char *)name, len, &dee, 0); - if (r == 1) goto bail1; + if (r == 1) + goto bail1; if (r == -1) { - brelse(bh); - hpfs_free_sectors(dir->i_sb, fno, 1); - hpfs_unlock_inode(dir); - unlock_kernel(); - return -EEXIST; + err = -EEXIST; + goto bail1; } fnode->len = len; memcpy(fnode->name, name, len > 15 ? 15 : len); @@ -326,7 +330,7 @@ bail1: hpfs_unlock_inode(dir); bail: unlock_kernel(); - return -ENOSPC; + return err; } int hpfs_unlink(struct inode *dir, struct dentry *dentry) @@ -340,39 +344,41 @@ int hpfs_unlink(struct inode *dir, struc fnode_secno fno; int r; int rep = 0; + int err; lock_kernel(); hpfs_adjust_length((char *)name, &len); again: - hpfs_lock_2inodes(dir, inode); - if (!(de = map_dirent(dir, hpfs_i(dir)->i_dno, (char *)name, len, &dno, &qbh))) { - hpfs_unlock_2inodes(dir, inode); - unlock_kernel(); - return -ENOENT; - } - if (de->first) { - hpfs_brelse4(&qbh); - hpfs_unlock_2inodes(dir, inode); - unlock_kernel(); - return -EPERM; - } - if (de->directory) { - hpfs_brelse4(&qbh); - hpfs_unlock_2inodes(dir, inode); - unlock_kernel(); - return -EISDIR; - } + hpfs_lock_inode(dir); + hpfs_lock_inode(inode); + de = map_dirent(dir, hpfs_i(dir)->i_dno, (char *)name, len, &dno, &qbh); + err = -ENOENT; + if (!de) + goto out; + + err = -EPERM; + if (de->first) + goto out1; + + err = -EISDIR; + if (de->directory) + goto out1; + fno = de->fnode; - if ((r = hpfs_remove_dirent(dir, dno, de, &qbh, 1)) == 1) hpfs_error(dir->i_sb, "there was error when removing dirent"); - if (r != 2) { - inode->i_nlink--; - hpfs_unlock_2inodes(dir, inode); - } else { /* no space for deleting, try to truncate file */ - struct iattr newattrs; - int err; - hpfs_unlock_2inodes(dir, inode); - if (rep) - goto ret; + r = hpfs_remove_dirent(dir, dno, de, &qbh, 1); + switch (r) { + case 1: + hpfs_error(dir->i_sb, "there was error when removing dirent"); + err = -EFSERROR; + break; + case 2: /* no space for deleting, try to truncate file */ + + err = -ENOSPC; + if (rep++) + break; + + hpfs_unlock_inode(inode); + hpfs_unlock_inode(dir); d_drop(dentry); spin_lock(&dentry->d_lock); if (atomic_read(&dentry->d_count) > 1 || @@ -381,22 +387,32 @@ again: get_write_access(inode)) { spin_unlock(&dentry->d_lock); d_rehash(dentry); - goto ret; + } else { + struct iattr newattrs; + spin_unlock(&dentry->d_lock); + /*printk("HPFS: truncating file before delete.\n");*/ + newattrs.ia_size = 0; + newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME; + err = notify_change(dentry, &newattrs); + put_write_access(inode); + if (!err) + goto again; } - spin_unlock(&dentry->d_lock); - /*printk("HPFS: truncating file before delete.\n");*/ - newattrs.ia_size = 0; - newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME; - err = notify_change(dentry, &newattrs); - put_write_access(inode); - if (err) - goto ret; - rep = 1; - goto again; + unlock_kernel(); + return -ENOSPC; + default: + inode->i_nlink--; + err = 0; } -ret: + goto out; + +out1: + hpfs_brelse4(&qbh); +out: + hpfs_unlock_inode(inode); + hpfs_unlock_inode(dir); unlock_kernel(); - return r == 2 ? -ENOSPC : r == 1 ? -EFSERROR : 0; + return err; } int hpfs_rmdir(struct inode *dir, struct dentry *dentry) @@ -409,44 +425,54 @@ int hpfs_rmdir(struct inode *dir, struct dnode_secno dno; fnode_secno fno; int n_items = 0; + int err; int r; + hpfs_adjust_length((char *)name, &len); lock_kernel(); - hpfs_lock_2inodes(dir, inode); - if (!(de = map_dirent(dir, hpfs_i(dir)->i_dno, (char *)name, len, &dno, &qbh))) { - hpfs_unlock_2inodes(dir, inode); - unlock_kernel(); - return -ENOENT; - } - if (de->first) { - hpfs_brelse4(&qbh); - hpfs_unlock_2inodes(dir, inode); - unlock_kernel(); - return -EPERM; - } - if (!de->directory) { - hpfs_brelse4(&qbh); - hpfs_unlock_2inodes(dir, inode); - unlock_kernel(); - return -ENOTDIR; - } + hpfs_lock_inode(dir); + hpfs_lock_inode(inode); + err = -ENOENT; + de = map_dirent(dir, hpfs_i(dir)->i_dno, (char *)name, len, &dno, &qbh); + if (!de) + goto out; + + err = -EPERM; + if (de->first) + goto out1; + + err = -ENOTDIR; + if (!de->directory) + goto out1; + hpfs_count_dnodes(dir->i_sb, hpfs_i(inode)->i_dno, NULL, NULL, &n_items); - if (n_items) { - hpfs_brelse4(&qbh); - hpfs_unlock_2inodes(dir, inode); - unlock_kernel(); - return -ENOTEMPTY; - } + err = -ENOTEMPTY; + if (n_items) + goto out1; + fno = de->fnode; - if ((r = hpfs_remove_dirent(dir, dno, de, &qbh, 1)) == 1) + r = hpfs_remove_dirent(dir, dno, de, &qbh, 1); + switch (r) { + case 1: hpfs_error(dir->i_sb, "there was error when removing dirent"); - if (r != 2) { + err = -EFSERROR; + break; + case 2: + err = -ENOSPC; + break; + default: dir->i_nlink--; inode->i_nlink = 0; - hpfs_unlock_2inodes(dir, inode); - } else hpfs_unlock_2inodes(dir, inode); + err = 0; + } + goto out; +out1: + hpfs_brelse4(&qbh); +out: + hpfs_unlock_inode(inode); + hpfs_unlock_inode(dir); unlock_kernel(); - return r == 2 ? -ENOSPC : r == 1 ? -EFSERROR : 0; + return err; } int hpfs_symlink_readpage(struct file *file, struct page *page) @@ -501,7 +527,10 @@ int hpfs_rename(struct inode *old_dir, s hpfs_adjust_length((char *)old_name, &old_len); lock_kernel(); - hpfs_lock_3inodes(old_dir, new_dir, i); + hpfs_lock_inode(old_dir); + if (new_dir != old_dir) + hpfs_lock_inode(new_dir); + hpfs_lock_inode(i); /* Erm? Moving over the empty non-busy directory is perfectly legal */ if (new_inode && S_ISDIR(new_inode->i_mode)) { @@ -580,7 +609,10 @@ int hpfs_rename(struct inode *old_dir, s hpfs_i(i)->i_conv = hpfs_sb(i->i_sb)->sb_conv; hpfs_decide_conv(i, (char *)new_name, new_len); end1: - hpfs_unlock_3inodes(old_dir, new_dir, i); + hpfs_unlock_inode(i); + if (old_dir != new_dir) + hpfs_unlock_inode(new_dir); + hpfs_unlock_inode(old_dir); unlock_kernel(); return err; } --- linux-2.6.4-rc2/fs/hpfs/super.c 2003-10-08 15:07:09.000000000 -0700 +++ 25/fs/hpfs/super.c 2004-03-07 20:46:46.000000000 -0800 @@ -448,6 +448,7 @@ static int hpfs_fill_super(struct super_ struct hpfs_super_block *superblock; struct hpfs_spare_block *spareblock; struct hpfs_sb_info *sbi; + struct inode *root; uid_t uid; gid_t gid; @@ -613,10 +614,11 @@ static int hpfs_fill_super(struct super_ brelse(bh0); hpfs_lock_iget(s, 1); - s->s_root = d_alloc_root(iget(s, sbi->sb_root)); + root = iget(s, sbi->sb_root); hpfs_unlock_iget(s); - if (!s->s_root || !s->s_root->d_inode) { - printk("HPFS: iget failed. Why???\n"); + s->s_root = d_alloc_root(root); + if (!s->s_root) { + iput(root); goto bail0; } hpfs_set_dentry_operations(s->s_root); @@ -627,22 +629,24 @@ static int hpfs_fill_super(struct super_ root_dno = hpfs_fnode_dno(s, sbi->sb_root); if (root_dno) - de = map_dirent(s->s_root->d_inode, root_dno, "\001\001", 2, NULL, &qbh); - if (!root_dno || !de) hpfs_error(s, "unable to find root dir"); + de = map_dirent(root, root_dno, "\001\001", 2, NULL, &qbh); + if (!de) + hpfs_error(s, "unable to find root dir"); else { - s->s_root->d_inode->i_atime.tv_sec = local_to_gmt(s, de->read_date); - s->s_root->d_inode->i_atime.tv_nsec = 0; - s->s_root->d_inode->i_mtime.tv_sec = local_to_gmt(s, de->write_date); - s->s_root->d_inode->i_mtime.tv_nsec = 0; - s->s_root->d_inode->i_ctime.tv_sec = local_to_gmt(s, de->creation_date); - s->s_root->d_inode->i_ctime.tv_nsec = 0; - hpfs_i(s->s_root->d_inode)->i_ea_size = de->ea_size; - hpfs_i(s->s_root->d_inode)->i_parent_dir = s->s_root->d_inode->i_ino; - if (s->s_root->d_inode->i_size == -1) s->s_root->d_inode->i_size = 2048; - if (s->s_root->d_inode->i_blocks == -1) s->s_root->d_inode->i_blocks = 5; + root->i_atime.tv_sec = local_to_gmt(s, de->read_date); + root->i_atime.tv_nsec = 0; + root->i_mtime.tv_sec = local_to_gmt(s, de->write_date); + root->i_mtime.tv_nsec = 0; + root->i_ctime.tv_sec = local_to_gmt(s, de->creation_date); + root->i_ctime.tv_nsec = 0; + hpfs_i(root)->i_ea_size = de->ea_size; + hpfs_i(root)->i_parent_dir = root->i_ino; + if (root->i_size == -1) + root->i_size = 2048; + if (root->i_blocks == -1) + root->i_blocks = 5; + hpfs_brelse4(&qbh); } - if (de) hpfs_brelse4(&qbh); - return 0; bail4: brelse(bh2); --- linux-2.6.4-rc2/fs/hugetlbfs/inode.c 2004-02-17 20:48:45.000000000 -0800 +++ 25/fs/hugetlbfs/inode.c 2004-03-07 20:47:09.000000000 -0800 @@ -194,6 +194,7 @@ static void hugetlbfs_delete_inode(struc hlist_del_init(&inode->i_hash); list_del_init(&inode->i_list); + list_del_init(&inode->i_sb_list); inode->i_state |= I_FREEING; inodes_stat.nr_inodes--; spin_unlock(&inode_lock); @@ -236,6 +237,7 @@ static void hugetlbfs_forget_inode(struc hlist_del_init(&inode->i_hash); out_truncate: list_del_init(&inode->i_list); + list_del_init(&inode->i_sb_list); inode->i_state |= I_FREEING; inodes_stat.nr_inodes--; spin_unlock(&inode_lock); --- linux-2.6.4-rc2/fs/inode.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/fs/inode.c 2004-03-07 20:48:23.000000000 -0800 @@ -20,6 +20,7 @@ #include #include #include +#include /* * This is needed for the following functions: @@ -183,6 +184,7 @@ void inode_init_once(struct inode *inode INIT_LIST_HEAD(&inode->i_dentry); INIT_LIST_HEAD(&inode->i_devices); sema_init(&inode->i_sem, 1); + init_rwsem(&inode->i_alloc_sem); INIT_RADIX_TREE(&inode->i_data.page_tree, GFP_ATOMIC); spin_lock_init(&inode->i_data.page_lock); init_MUTEX(&inode->i_data.i_shared_sem); @@ -193,6 +195,7 @@ void inode_init_once(struct inode *inode INIT_LIST_HEAD(&inode->i_data.i_mmap_shared); spin_lock_init(&inode->i_lock); i_size_ordered_init(inode); + init_rwsem(&inode->i_data.wb_rwsema); } EXPORT_SYMBOL(inode_init_once); @@ -285,7 +288,7 @@ static void dispose_list(struct list_hea /* * Invalidate all inodes for a device. */ -static int invalidate_list(struct list_head *head, struct super_block * sb, struct list_head * dispose) +static int invalidate_list(struct list_head *head, struct list_head *dispose) { struct list_head *next; int busy = 0, count = 0; @@ -298,13 +301,12 @@ static int invalidate_list(struct list_h next = next->next; if (tmp == head) break; - inode = list_entry(tmp, struct inode, i_list); - if (inode->i_sb != sb) - continue; + inode = list_entry(tmp, struct inode, i_sb_list); invalidate_inode_buffers(inode); if (!atomic_read(&inode->i_count)) { hlist_del_init(&inode->i_hash); list_del(&inode->i_list); + list_del(&inode->i_sb_list); list_add(&inode->i_list, dispose); inode->i_state |= I_FREEING; count++; @@ -340,10 +342,7 @@ int invalidate_inodes(struct super_block down(&iprune_sem); spin_lock(&inode_lock); - busy = invalidate_list(&inode_in_use, sb, &throw_away); - busy |= invalidate_list(&inode_unused, sb, &throw_away); - busy |= invalidate_list(&sb->s_dirty, sb, &throw_away); - busy |= invalidate_list(&sb->s_io, sb, &throw_away); + busy = invalidate_list(&sb->s_inodes, &throw_away); spin_unlock(&inode_lock); dispose_list(&throw_away); @@ -443,6 +442,7 @@ static void prune_icache(int nr_to_scan) continue; } hlist_del_init(&inode->i_hash); + list_del_init(&inode->i_sb_list); list_move(&inode->i_list, &freeable); inode->i_state |= I_FREEING; nr_pruned++; @@ -553,6 +553,7 @@ struct inode *new_inode(struct super_blo spin_lock(&inode_lock); inodes_stat.nr_inodes++; list_add(&inode->i_list, &inode_in_use); + list_add(&inode->i_sb_list, &sb->s_inodes); inode->i_ino = ++last_ino; inode->i_state = 0; spin_unlock(&inode_lock); @@ -601,6 +602,7 @@ static struct inode * get_new_inode(stru inodes_stat.nr_inodes++; list_add(&inode->i_list, &inode_in_use); + list_add(&inode->i_sb_list, &sb->s_inodes); hlist_add_head(&inode->i_hash, head); inode->i_state = I_LOCK|I_NEW; spin_unlock(&inode_lock); @@ -649,6 +651,7 @@ static struct inode * get_new_inode_fast inode->i_ino = ino; inodes_stat.nr_inodes++; list_add(&inode->i_list, &inode_in_use); + list_add(&inode->i_sb_list, &sb->s_inodes); hlist_add_head(&inode->i_hash, head); inode->i_state = I_LOCK|I_NEW; spin_unlock(&inode_lock); @@ -984,6 +987,7 @@ void generic_delete_inode(struct inode * struct super_operations *op = inode->i_sb->s_op; list_del_init(&inode->i_list); + list_del_init(&inode->i_sb_list); inode->i_state|=I_FREEING; inodes_stat.nr_inodes--; spin_unlock(&inode_lock); @@ -1018,6 +1022,14 @@ static void generic_forget_inode(struct if (!hlist_unhashed(&inode->i_hash)) { if (!(inode->i_state & (I_DIRTY|I_LOCK))) { + if (!strcmp(inode->i_sb->s_type->name, "ext3")) { + struct ext3_inode_info *ext3_i; + ext3_i = container_of(inode, + struct ext3_inode_info, + vfs_inode); + if (!list_empty(&ext3_i->i_orphan)) + WARN_ON(1); + } list_del(&inode->i_list); list_add(&inode->i_list, &inode_unused); } @@ -1031,6 +1043,7 @@ static void generic_forget_inode(struct hlist_del_init(&inode->i_hash); } list_del_init(&inode->i_list); + list_del_init(&inode->i_sb_list); inode->i_state|=I_FREEING; inodes_stat.nr_inodes--; spin_unlock(&inode_lock); @@ -1064,7 +1077,7 @@ static void generic_drop_inode(struct in * held, and the drop function is supposed to release * the lock! */ -static inline void iput_final(struct inode *inode) +static void iput_final(struct inode *inode) { struct super_operations *op = inode->i_sb->s_op; void (*drop)(struct inode *) = generic_drop_inode; @@ -1178,6 +1191,8 @@ void inode_update_time(struct inode *ino struct timespec now; int sync_it = 0; + if (IS_NOCMTIME(inode)) + return; if (IS_RDONLY(inode)) return; @@ -1214,44 +1229,27 @@ EXPORT_SYMBOL(inode_needs_sync); */ #ifdef CONFIG_QUOTA -/* Functions back in dquot.c */ -void put_dquot_list(struct list_head *); +/* Function back in dquot.c */ int remove_inode_dquot_ref(struct inode *, int, struct list_head *); -void remove_dquot_ref(struct super_block *sb, int type) +void remove_dquot_ref(struct super_block *sb, int type, + struct list_head *tofree_head) { struct inode *inode; - struct list_head *act_head; - LIST_HEAD(tofree_head); if (!sb->dq_op) return; /* nothing to do */ spin_lock(&inode_lock); /* This lock is for inodes code */ - /* We don't have to lock against quota code - test IS_QUOTAINIT is just for speedup... */ - - list_for_each(act_head, &inode_in_use) { - inode = list_entry(act_head, struct inode, i_list); - if (inode->i_sb == sb && IS_QUOTAINIT(inode)) - remove_inode_dquot_ref(inode, type, &tofree_head); - } - list_for_each(act_head, &inode_unused) { - inode = list_entry(act_head, struct inode, i_list); - if (inode->i_sb == sb && IS_QUOTAINIT(inode)) - remove_inode_dquot_ref(inode, type, &tofree_head); - } - list_for_each(act_head, &sb->s_dirty) { - inode = list_entry(act_head, struct inode, i_list); - if (IS_QUOTAINIT(inode)) - remove_inode_dquot_ref(inode, type, &tofree_head); - } - list_for_each(act_head, &sb->s_io) { - inode = list_entry(act_head, struct inode, i_list); + /* + * We don't have to lock against quota code - test IS_QUOTAINIT is + * just for speedup... + */ + + list_for_each_entry(inode, &sb->s_inodes, i_sb_list) if (IS_QUOTAINIT(inode)) - remove_inode_dquot_ref(inode, type, &tofree_head); - } - spin_unlock(&inode_lock); + remove_inode_dquot_ref(inode, type, tofree_head); - put_dquot_list(&tofree_head); + spin_unlock(&inode_lock); } #endif --- linux-2.6.4-rc2/fs/jffs2/super.c 2003-10-17 15:58:04.000000000 -0700 +++ 25/fs/jffs2/super.c 2004-03-07 20:47:08.000000000 -0800 @@ -129,6 +129,7 @@ static struct super_block *jffs2_get_sb_ mtd->index, mtd->name)); sb->s_op = &jffs2_super_operations; + sb->s_flags |= MS_NODIRATIME; ret = jffs2_do_fill_super(sb, data, (flags&MS_VERBOSE)?1:0); --- linux-2.6.4-rc2/fs/jffs/inode-v23.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/fs/jffs/inode-v23.c 2004-03-07 20:47:08.000000000 -0800 @@ -70,6 +70,8 @@ static int jffs_fill_super(struct super_ struct inode *root_inode; struct jffs_control *c; + sb->s_flags |= MS_NODIRATIME; + D1(printk(KERN_NOTICE "JFFS: Trying to mount device %s.\n", sb->s_id)); --- linux-2.6.4-rc2/fs/Kconfig 2004-03-03 23:12:46.000000000 -0800 +++ 25/fs/Kconfig 2004-03-07 20:47:39.000000000 -0800 @@ -780,6 +780,30 @@ config PROC_KCORE bool default y if !ARM +config SYSFS + bool "sysfs file system support" if EMBEDDED + default y + help + The sysfs filesystem is a virtual filesystem that the kernel uses to export + internal kernel objects, their attributes, and their relationships to one + another. + + Users can use sysfs to ascertain useful information about the running kernel, + such as the devices the kernel has discovered on each bus and which driver + each is bound to. sysfs can also be used to tune devices and other kernel + subsystems. + + Some system agents rely on the information in sysfs to operate. /sbin/hotplug + uses device and object attributes in sysfs to assist in delegating policy + decisions, like persistantly naming devices. + + sysfs is currently needed by the block subsystem to mount the root partition. + Therefore, you MUST say Y if you're booting from a hard drive. If you use any + type of hotpluggable device, you'll also need sysfs for /sbin/hotplug support. + + However, designers of embedded systems may want to say N here to conserve + space. + config DEVFS_FS bool "/dev file system support (OBSOLETE)" depends on EXPERIMENTAL @@ -1227,6 +1251,9 @@ config UFS_FS experimental "UFS file system write support", below. Please read the file for more information. + The recently released UFS2 variant (used in FreeBSD 5.x) is + READ-ONLY supported. + If you only intend to mount files from some other Unix over the network using NFS, you don't need the UFS file system support (but you need NFS file system support obviously). @@ -1302,15 +1329,18 @@ config NFS_V3 Say Y here if you want your NFS client to be able to speak the newer version 3 of the NFS protocol. - If unsure, say N. + If unsure, say Y. config NFS_V4 bool "Provide NFSv4 client support (EXPERIMENTAL)" depends on NFS_FS && EXPERIMENTAL + select RPCSEC_GSS_KRB5 help Say Y here if you want your NFS client to be able to speak the newer - version 4 of the NFS protocol. This feature is experimental, and - should only be used if you are interested in helping to test NFSv4. + version 4 of the NFS protocol. + + Note: Requires auxiliary userspace daemons which may be found on + http://www.citi.umich.edu/projects/nfsv4/ If unsure, say N. @@ -1419,28 +1449,24 @@ config SUNRPC tristate config SUNRPC_GSS - tristate "Provide RPCSEC_GSS authentication (EXPERIMENTAL)" + tristate + +config RPCSEC_GSS_KRB5 + tristate "Secure RPC: Kerberos V mechanism (EXPERIMENTAL)" depends on SUNRPC && EXPERIMENTAL - default SUNRPC if NFS_V4=y + select SUNRPC_GSS + select CRYPTO + select CRYPTO_MD5 + select CRYPTO_DES help - Provides cryptographic authentication for NFS rpc requests. To - make this useful, you must also select at least one rpcsec_gss - mechanism. - Note: You should always select this option if you wish to use + Provides for secure RPC calls by means of a gss-api + mechanism based on Kerberos V5. This is required for NFSv4. -config RPCSEC_GSS_KRB5 - tristate "Kerberos V mechanism for RPCSEC_GSS (EXPERIMENTAL)" - depends on SUNRPC_GSS && CRYPTO_DES && CRYPTO_MD5 - default SUNRPC_GSS if NFS_V4=y - help - Provides a gss-api mechanism based on Kerberos V5 (this is - mandatory for RFC3010-compliant NFSv4 implementations). - Requires a userspace daemon; - see http://www.citi.umich.edu/projects/nfsv4/. + Note: Requires an auxiliary userspace daemon which may be found on + http://www.citi.umich.edu/projects/nfsv4/ - Note: If you select this option, please ensure that you also - enable the MD5 and DES crypto ciphers. + If unsure, say N. config SMB_FS tristate "SMB file system support (to mount Windows shares etc.)" --- linux-2.6.4-rc2/fs/libfs.c 2004-02-17 20:48:46.000000000 -0800 +++ 25/fs/libfs.c 2004-03-07 20:47:08.000000000 -0800 @@ -155,7 +155,6 @@ int dcache_readdir(struct file * filp, v } spin_unlock(&dcache_lock); } - update_atime(dentry->d_inode); return 0; } --- linux-2.6.4-rc2/fs/lockd/clntproc.c 2003-06-14 12:17:56.000000000 -0700 +++ 25/fs/lockd/clntproc.c 2004-03-07 20:47:32.000000000 -0800 @@ -195,19 +195,6 @@ done: } /* - * Wait while server is in grace period - */ -static inline int -nlmclnt_grace_wait(struct nlm_host *host) -{ - if (!host->h_reclaiming) - interruptible_sleep_on_timeout(&host->h_gracewait, 10*HZ); - else - interruptible_sleep_on(&host->h_gracewait); - return signalled()? -ERESTARTSYS : 0; -} - -/* * Allocate an NLM RPC call struct */ struct nlm_rqst * @@ -456,7 +443,7 @@ nlmclnt_lock(struct nlm_rqst *req, struc } if (status < 0) return status; - } while (resp->status == NLM_LCK_BLOCKED); + } while (resp->status == NLM_LCK_BLOCKED && req->a_args.block); if (resp->status == NLM_LCK_GRANTED) { fl->fl_u.nfs_fl.state = host->h_state; --- linux-2.6.4-rc2/fs/lockd/host.c 2003-06-14 12:18:22.000000000 -0700 +++ 25/fs/lockd/host.c 2004-03-07 20:47:30.000000000 -0800 @@ -188,14 +188,14 @@ nlm_bind_host(struct nlm_host *host) } } else { xprt = xprt_create_proto(host->h_proto, &host->h_addr, NULL); - if (xprt == NULL) + if (IS_ERR(xprt)) goto forgetit; xprt_set_timeout(&xprt->timeout, 5, nlmsvc_timeout); clnt = rpc_create_client(xprt, host->h_name, &nlm_program, host->h_version, host->h_authflavor); - if (clnt == NULL) { + if (IS_ERR(clnt)) { xprt_destroy(xprt); goto forgetit; } --- linux-2.6.4-rc2/fs/lockd/mon.c 2003-06-14 12:18:28.000000000 -0700 +++ 25/fs/lockd/mon.c 2004-03-07 20:47:30.000000000 -0800 @@ -36,10 +36,11 @@ nsm_mon_unmon(struct nlm_host *host, u32 int status; struct nsm_args args; - status = -EACCES; clnt = nsm_create(); - if (!clnt) + if (IS_ERR(clnt)) { + status = PTR_ERR(clnt); goto out; + } args.addr = host->h_addr.sin_addr.s_addr; args.proto= (host->h_proto<<1) | host->h_server; @@ -104,7 +105,7 @@ static struct rpc_clnt * nsm_create(void) { struct rpc_xprt *xprt; - struct rpc_clnt *clnt = NULL; + struct rpc_clnt *clnt; struct sockaddr_in sin; sin.sin_family = AF_INET; @@ -112,24 +113,23 @@ nsm_create(void) sin.sin_port = 0; xprt = xprt_create_proto(IPPROTO_UDP, &sin, NULL); - if (!xprt) - goto out; + if (IS_ERR(xprt)) + return (struct rpc_clnt *)xprt; clnt = rpc_create_client(xprt, "localhost", &nsm_program, SM_VERSION, RPC_AUTH_NULL); - if (!clnt) + if (IS_ERR(clnt)) goto out_destroy; clnt->cl_softrtry = 1; clnt->cl_chatty = 1; clnt->cl_oneshot = 1; xprt->resvport = 1; /* NSM requires a reserved port */ -out: return clnt; out_destroy: xprt_destroy(xprt); - goto out; + return clnt; } /* --- linux-2.6.4-rc2/fs/lockd/svc4proc.c 2003-06-14 12:18:07.000000000 -0700 +++ 25/fs/lockd/svc4proc.c 2004-03-07 20:47:32.000000000 -0800 @@ -453,6 +453,24 @@ nlm4svc_proc_sm_notify(struct svc_rqst * } /* + * client sent a GRANTED_RES, let's remove the associated block + */ +static int +nlm4svc_proc_granted_res(struct svc_rqst *rqstp, struct nlm_res *argp, + void *resp) +{ + if (!nlmsvc_ops) + return rpc_success; + + dprintk("lockd: GRANTED_RES called\n"); + + nlmsvc_grant_reply(rqstp, &argp->cookie, argp->status); + return rpc_success; +} + + + +/* * This is the generic lockd callback for async RPC calls */ static u32 @@ -515,7 +533,6 @@ nlm4svc_callback_exit(struct rpc_task *t #define nlm4svc_proc_lock_res nlm4svc_proc_null #define nlm4svc_proc_cancel_res nlm4svc_proc_null #define nlm4svc_proc_unlock_res nlm4svc_proc_null -#define nlm4svc_proc_granted_res nlm4svc_proc_null struct nlm_void { int dummy; }; @@ -548,7 +565,7 @@ struct svc_procedure nlmsvc_procedures4 PROC(lock_res, lockres, norep, res, void, 1), PROC(cancel_res, cancelres, norep, res, void, 1), PROC(unlock_res, unlockres, norep, res, void, 1), - PROC(granted_res, grantedres, norep, res, void, 1), + PROC(granted_res, res, norep, res, void, 1), /* statd callback */ PROC(sm_notify, reboot, void, reboot, void, 1), PROC(none, void, void, void, void, 0), --- linux-2.6.4-rc2/fs/lockd/svclock.c 2003-06-14 12:18:05.000000000 -0700 +++ 25/fs/lockd/svclock.c 2004-03-07 20:47:32.000000000 -0800 @@ -64,7 +64,7 @@ nlmsvc_insert_block(struct nlm_block *bl if (when != NLM_NEVER) { if ((when += jiffies) == NLM_NEVER) when ++; - while ((b = *bp) && time_before_eq(b->b_when,when)) + while ((b = *bp) && time_before_eq(b->b_when,when) && b->b_when != NLM_NEVER) bp = &b->b_next; } else while ((b = *bp)) @@ -143,14 +143,15 @@ static inline int nlm_cookie_match(struc * Find a block with a given NLM cookie. */ static inline struct nlm_block * -nlmsvc_find_block(struct nlm_cookie *cookie) +nlmsvc_find_block(struct nlm_cookie *cookie, struct sockaddr_in *sin) { struct nlm_block *block; for (block = nlm_blocked; block; block = block->b_next) { dprintk("cookie: head of blocked queue %p, block %p\n", nlm_blocked, block); - if (nlm_cookie_match(&block->b_call.a_args.cookie,cookie)) + if (nlm_cookie_match(&block->b_call.a_args.cookie,cookie) + && nlm_cmp_addr(sin, &block->b_host->h_addr)) break; } @@ -566,12 +567,16 @@ nlmsvc_grant_callback(struct rpc_task *t struct nlm_rqst *call = (struct nlm_rqst *) task->tk_calldata; struct nlm_block *block; unsigned long timeout; + struct sockaddr_in *peer_addr = RPC_PEERADDR(task->tk_client); dprintk("lockd: GRANT_MSG RPC callback\n"); - dprintk("callback: looking for cookie %x \n", - *(unsigned int *)(call->a_args.cookie.data)); - if (!(block = nlmsvc_find_block(&call->a_args.cookie))) { - dprintk("lockd: no block for cookie %x\n", *(u32 *)(call->a_args.cookie.data)); + dprintk("callback: looking for cookie %x, host (%08x)\n", + *(unsigned int *)(call->a_args.cookie.data), + ntohl(peer_addr->sin_addr.s_addr)); + if (!(block = nlmsvc_find_block(&call->a_args.cookie, peer_addr))) { + dprintk("lockd: no block for cookie %x, host (%08x)\n", + *(u32 *)(call->a_args.cookie.data), + ntohl(peer_addr->sin_addr.s_addr)); return; } @@ -600,18 +605,21 @@ nlmsvc_grant_callback(struct rpc_task *t * block. */ void -nlmsvc_grant_reply(struct nlm_cookie *cookie, u32 status) +nlmsvc_grant_reply(struct svc_rqst *rqstp, struct nlm_cookie *cookie, u32 status) { struct nlm_block *block; struct nlm_file *file; - if (!(block = nlmsvc_find_block(cookie))) + dprintk("grant_reply: looking for cookie %x, host (%08x), s=%d \n", + *(unsigned int *)(cookie->data), + ntohl(rqstp->rq_addr.sin_addr.s_addr), status); + if (!(block = nlmsvc_find_block(cookie, &rqstp->rq_addr))) return; file = block->b_file; file->f_count++; down(&file->f_sema); - if ((block = nlmsvc_find_block(cookie)) != NULL) { + if ((block = nlmsvc_find_block(cookie,&rqstp->rq_addr)) != NULL) { if (status == NLM_LCK_DENIED_GRACE_PERIOD) { /* Try again in a couple of seconds */ nlmsvc_insert_block(block, 10 * HZ); --- linux-2.6.4-rc2/fs/lockd/svcproc.c 2003-06-14 12:18:25.000000000 -0700 +++ 25/fs/lockd/svcproc.c 2004-03-07 20:47:32.000000000 -0800 @@ -479,6 +479,22 @@ nlmsvc_proc_sm_notify(struct svc_rqst *r } /* + * client sent a GRANTED_RES, let's remove the associated block + */ +static int +nlmsvc_proc_granted_res(struct svc_rqst *rqstp, struct nlm_res *argp, + void *resp) +{ + if (!nlmsvc_ops) + return rpc_success; + + dprintk("lockd: GRANTED_RES called\n"); + + nlmsvc_grant_reply(rqstp, &argp->cookie, argp->status); + return rpc_success; +} + +/* * This is the generic lockd callback for async RPC calls */ static u32 @@ -541,7 +557,6 @@ nlmsvc_callback_exit(struct rpc_task *ta #define nlmsvc_proc_lock_res nlmsvc_proc_null #define nlmsvc_proc_cancel_res nlmsvc_proc_null #define nlmsvc_proc_unlock_res nlmsvc_proc_null -#define nlmsvc_proc_granted_res nlmsvc_proc_null struct nlm_void { int dummy; }; @@ -576,7 +591,7 @@ struct svc_procedure nlmsvc_procedures[ PROC(lock_res, lockres, norep, res, void, 1), PROC(cancel_res, cancelres, norep, res, void, 1), PROC(unlock_res, unlockres, norep, res, void, 1), - PROC(granted_res, grantedres, norep, res, void, 1), + PROC(granted_res, res, norep, res, void, 1), /* statd callback */ PROC(sm_notify, reboot, void, reboot, void, 1), PROC(none, void, void, void, void, 1), --- linux-2.6.4-rc2/fs/Makefile 2004-03-03 23:12:46.000000000 -0800 +++ 25/fs/Makefile 2004-03-07 20:47:12.000000000 -0800 @@ -39,7 +39,7 @@ obj-$(CONFIG_QUOTACTL) += quota.o obj-$(CONFIG_PROC_FS) += proc/ obj-y += partitions/ -obj-y += sysfs/ +obj-$(CONFIG_SYSFS) += sysfs/ obj-y += devpts/ obj-$(CONFIG_PROFILING) += dcookies.o --- linux-2.6.4-rc2/fs/minix/dir.c 2003-06-14 12:18:29.000000000 -0700 +++ 25/fs/minix/dir.c 2004-03-07 20:47:08.000000000 -0800 @@ -127,7 +127,6 @@ static int minix_readdir(struct file * f done: filp->f_pos = (n << PAGE_CACHE_SHIFT) | offset; - update_atime(inode); unlock_kernel(); return 0; } --- linux-2.6.4-rc2/fs/namei.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/fs/namei.c 2004-03-07 20:46:46.000000000 -0800 @@ -571,7 +571,7 @@ fail: * * We expect 'base' to be positive and a directory. */ -int link_path_walk(const char * name, struct nameidata *nd) +int fastcall link_path_walk(const char * name, struct nameidata *nd) { struct path next; struct inode *inode; @@ -771,7 +771,7 @@ return_err: return err; } -int path_walk(const char * name, struct nameidata *nd) +int fastcall path_walk(const char * name, struct nameidata *nd) { current->total_link_count = 0; return link_path_walk(name, nd); @@ -858,7 +858,7 @@ walk_init_root(const char *name, struct return 1; } -int path_lookup(const char *name, unsigned int flags, struct nameidata *nd) +int fastcall path_lookup(const char *name, unsigned int flags, struct nameidata *nd) { nd->last_type = LAST_ROOT; /* if there are only slashes... */ nd->flags = flags; @@ -971,7 +971,7 @@ access: * that namei follows links, while lnamei does not. * SMP-safe */ -int __user_walk(const char __user *name, unsigned flags, struct nameidata *nd) +int fastcall __user_walk(const char __user *name, unsigned flags, struct nameidata *nd) { char *tmp = getname(name); int err = PTR_ERR(tmp); --- linux-2.6.4-rc2/fs/namespace.c 2004-02-17 20:48:46.000000000 -0800 +++ 25/fs/namespace.c 2004-03-07 20:47:12.000000000 -0800 @@ -24,7 +24,15 @@ #include extern int __init init_rootfs(void); + +#ifdef CONFIG_SYSFS extern int __init sysfs_init(void); +#else +static inline int sysfs_init(void) +{ + return 0; +} +#endif /* spinlock for vfsmount related operations, inplace of dcache_lock */ spinlock_t vfsmount_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED; --- linux-2.6.4-rc2/fs/ncpfs/inode.c 2004-02-17 20:48:46.000000000 -0800 +++ 25/fs/ncpfs/inode.c 2004-03-07 20:47:08.000000000 -0800 @@ -479,6 +479,7 @@ static int ncp_fill_super(struct super_b else default_bufsize = 1024; + sb->s_flags |= MS_NODIRATIME; /* probably even noatime */ sb->s_maxbytes = 0xFFFFFFFFU; sb->s_blocksize = 1024; /* Eh... Is this correct? */ sb->s_blocksize_bits = 10; --- linux-2.6.4-rc2/fs/nfsd/export.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/fs/nfsd/export.c 2004-03-07 20:46:46.000000000 -0800 @@ -56,11 +56,6 @@ static int exp_verify_string(char *cp, #define EXPKEY_HASHMASK (EXPKEY_HASHMAX -1) static struct cache_head *expkey_table[EXPKEY_HASHMAX]; -static inline int key_len(int type) -{ - return type == 0 ? 8 : type == 1 ? 4 : 12; -} - static inline int svc_expkey_hash(struct svc_expkey *item) { int hash = item->ek_fsidtype; @@ -547,8 +542,8 @@ exp_get_key(svc_client *clp, dev_t dev, mk_fsid_v0(fsidv, dev, ino); return exp_find_key(clp, 0, fsidv, NULL); } - mk_fsid_v2(fsidv, dev, ino); - return exp_find_key(clp, 2, fsidv, NULL); + mk_fsid_v3(fsidv, dev, ino); + return exp_find_key(clp, 3, fsidv, NULL); } /* @@ -684,8 +679,8 @@ static int exp_hash(struct auth_domain * mk_fsid_v0(fsid, dev, inode->i_ino); return exp_set_key(clp, 0, fsid, exp); } - mk_fsid_v2(fsid, dev, inode->i_ino); - return exp_set_key(clp, 2, fsid, exp); + mk_fsid_v3(fsid, dev, inode->i_ino); + return exp_set_key(clp, 3, fsid, exp); } static void exp_unhash(struct svc_export *exp) --- linux-2.6.4-rc2/fs/nfs/dir.c 2004-02-17 20:48:46.000000000 -0800 +++ 25/fs/nfs/dir.c 2004-03-07 20:47:28.000000000 -0800 @@ -139,11 +139,13 @@ int nfs_readdir_filler(nfs_readdir_descr struct file *file = desc->file; struct inode *inode = file->f_dentry->d_inode; struct rpc_cred *cred = nfs_file_cred(file); + unsigned long timestamp; int error; dfprintk(VFS, "NFS: nfs_readdir_filler() reading cookie %Lu into page %lu.\n", (long long)desc->entry->cookie, page->index); again: + timestamp = jiffies; error = NFS_PROTO(inode)->readdir(file->f_dentry, cred, desc->entry->cookie, page, NFS_SERVER(inode)->dtsize, desc->plus); if (error < 0) { @@ -157,18 +159,21 @@ int nfs_readdir_filler(nfs_readdir_descr goto error; } SetPageUptodate(page); + NFS_FLAGS(inode) |= NFS_INO_INVALID_ATIME; /* Ensure consistent page alignment of the data. * Note: assumes we have exclusive access to this mapping either * throught inode->i_sem or some other mechanism. */ - if (page->index == 0) + if (page->index == 0) { invalidate_inode_pages(inode->i_mapping); + NFS_I(inode)->readdir_timestamp = timestamp; + } unlock_page(page); return 0; error: SetPageError(page); unlock_page(page); - invalidate_inode_pages(inode->i_mapping); + nfs_zap_caches(inode); desc->error = error; return -EIO; } @@ -381,6 +386,7 @@ int uncached_readdir(nfs_readdir_descrip page, NFS_SERVER(inode)->dtsize, desc->plus); + NFS_FLAGS(inode) |= NFS_INO_INVALID_ATIME; desc->page = page; desc->ptr = kmap(page); /* matching kunmap in nfs_do_filldir */ if (desc->error >= 0) { @@ -459,7 +465,15 @@ static int nfs_readdir(struct file *filp } res = 0; break; - } else if (res < 0) + } + if (res == -ETOOSMALL && desc->plus) { + NFS_FLAGS(inode) &= ~NFS_INO_ADVISE_RDPLUS; + nfs_zap_caches(inode); + desc->plus = 0; + desc->entry->eof = 0; + continue; + } + if (res < 0) break; res = nfs_do_filldir(desc, dirent, filldir); @@ -481,14 +495,19 @@ static int nfs_readdir(struct file *filp * In the case it has, we assume that the dentries are untrustworthy * and may need to be looked up again. */ -static inline -int nfs_check_verifier(struct inode *dir, struct dentry *dentry) +static inline int nfs_check_verifier(struct inode *dir, struct dentry *dentry) { if (IS_ROOT(dentry)) return 1; - if (nfs_revalidate_inode(NFS_SERVER(dir), dir)) + if ((NFS_FLAGS(dir) & NFS_INO_INVALID_ATTR) != 0 + || nfs_attribute_timeout(dir)) return 0; - return time_after(dentry->d_time, NFS_MTIME_UPDATE(dir)); + return nfs_verify_change_attribute(dir, (unsigned long)dentry->d_fsdata); +} + +static inline void nfs_set_verifier(struct dentry * dentry, unsigned long verf) +{ + dentry->d_fsdata = (void *)verf; } /* @@ -528,9 +547,7 @@ int nfs_neg_need_reval(struct inode *dir /* Don't revalidate a negative dentry if we're creating a new file */ if ((ndflags & LOOKUP_CREATE) && !(ndflags & LOOKUP_CONTINUE)) return 0; - if (!nfs_check_verifier(dir, dentry)) - return 1; - return time_after(jiffies, dentry->d_time + NFS_ATTRTIMEO(dir)); + return !nfs_check_verifier(dir, dentry); } /* @@ -552,6 +569,7 @@ static int nfs_lookup_revalidate(struct int error; struct nfs_fh fhandle; struct nfs_fattr fattr; + unsigned long verifier; int isopen = 0; parent = dget_parent(dentry); @@ -574,6 +592,9 @@ static int nfs_lookup_revalidate(struct goto out_bad; } + /* Revalidate parent directory attribute cache */ + nfs_revalidate_inode(NFS_SERVER(dir), dir); + /* Force a full look up iff the parent directory has changed */ if (nfs_check_verifier(dir, dentry)) { if (nfs_lookup_verify_inode(inode, isopen)) @@ -581,6 +602,12 @@ static int nfs_lookup_revalidate(struct goto out_valid; } + /* + * Note: we're not holding inode->i_sem and so may be racing with + * operations that change the directory. We therefore save the + * change attribute *before* we do the RPC call. + */ + verifier = nfs_save_change_attribute(dir); error = nfs_cached_lookup(dir, dentry, &fhandle, &fattr); if (!error) { if (memcmp(NFS_FH(inode), &fhandle, sizeof(struct nfs_fh))!= 0) @@ -603,6 +630,7 @@ static int nfs_lookup_revalidate(struct out_valid_renew: nfs_renew_times(dentry); + nfs_set_verifier(dentry, verifier); out_valid: unlock_kernel(); dput(parent); @@ -638,6 +666,11 @@ static int nfs_dentry_delete(struct dent /* Unhash it, so that ->d_iput() would be called */ return 1; } + if (!(dentry->d_sb->s_flags & MS_ACTIVE)) { + /* Unhash it, so that ancestors of killed async unlink + * files will be cleaned up during umount */ + return 1; + } return 0; } @@ -693,6 +726,8 @@ static struct dentry *nfs_lookup(struct dentry->d_op = NFS_PROTO(dir)->dentry_ops; lock_kernel(); + /* Revalidate parent directory attribute cache */ + nfs_revalidate_inode(NFS_SERVER(dir), dir); /* If we're doing an exclusive create, optimize away the lookup */ if (nfs_is_exclusive_create(dir, nd)) @@ -715,6 +750,7 @@ no_entry: error = 0; d_add(dentry, inode); nfs_renew_times(dentry); + nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); out_unlock: unlock_kernel(); out: @@ -768,7 +804,15 @@ static struct dentry *nfs_atomic_lookup( /* Open the file on the server */ lock_kernel(); - inode = nfs4_atomic_open(dir, dentry, nd); + /* Revalidate parent directory attribute cache */ + nfs_revalidate_inode(NFS_SERVER(dir), dir); + + if (nd->intent.open.flags & O_CREAT) { + nfs_begin_data_update(dir); + inode = nfs4_atomic_open(dir, dentry, nd); + nfs_end_data_update(dir); + } else + inode = nfs4_atomic_open(dir, dentry, nd); unlock_kernel(); if (IS_ERR(inode)) { error = PTR_ERR(inode); @@ -790,6 +834,7 @@ static struct dentry *nfs_atomic_lookup( no_entry: d_add(dentry, inode); nfs_renew_times(dentry); + nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); out: BUG_ON(error > 0); return ERR_PTR(error); @@ -801,13 +846,16 @@ static int nfs_open_revalidate(struct de { struct dentry *parent = NULL; struct inode *inode = dentry->d_inode; + struct inode *dir; + unsigned long verifier; int openflags, ret = 0; /* NFS only supports OPEN for regular files */ if (inode && !S_ISREG(inode->i_mode)) goto no_open; parent = dget_parent(dentry); - if (!is_atomic_open(parent->d_inode, nd)) + dir = parent->d_inode; + if (!is_atomic_open(dir, nd)) goto no_open; openflags = nd->intent.open.flags; if (openflags & O_CREAT) { @@ -821,8 +869,16 @@ static int nfs_open_revalidate(struct de /* We can't create new files, or truncate existing ones here */ openflags &= ~(O_CREAT|O_TRUNC); + /* + * Note: we're not holding inode->i_sem and so may be racing with + * operations that change the directory. We therefore save the + * change attribute *before* we do the RPC call. + */ lock_kernel(); - ret = nfs4_open_revalidate(parent->d_inode, dentry, openflags); + verifier = nfs_save_change_attribute(dir); + ret = nfs4_open_revalidate(dir, dentry, openflags); + if (!ret) + nfs_set_verifier(dentry, verifier); unlock_kernel(); out: dput(parent); @@ -869,15 +925,20 @@ int nfs_cached_lookup(struct inode *dir, struct nfs_server *server; struct nfs_entry entry; struct page *page; - unsigned long timestamp = NFS_MTIME_UPDATE(dir); + unsigned long timestamp; int res; if (!NFS_USE_READDIRPLUS(dir)) return -ENOENT; server = NFS_SERVER(dir); - if (server->flags & NFS_MOUNT_NOAC) + /* Don't use readdirplus unless the cache is stable */ + if ((server->flags & NFS_MOUNT_NOAC) != 0 + || nfs_caches_unstable(dir) + || nfs_attribute_timeout(dir)) + return -ENOENT; + if ((NFS_FLAGS(dir) & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA)) != 0) return -ENOENT; - nfs_revalidate_inode(server, dir); + timestamp = NFS_I(dir)->readdir_timestamp; entry.fh = fh; entry.fattr = fattr; @@ -931,9 +992,10 @@ static int nfs_instantiate(struct dentry if (inode) { d_instantiate(dentry, inode); nfs_renew_times(dentry); - error = 0; + nfs_set_verifier(dentry, nfs_save_change_attribute(dentry->d_parent->d_inode)); + return 0; } - return error; + error = -ENOMEM; out_err: d_drop(dentry); return error; @@ -969,11 +1031,13 @@ static int nfs_create(struct inode *dir, * does not pass the create flags. */ lock_kernel(); - nfs_zap_caches(dir); + nfs_begin_data_update(dir); inode = NFS_PROTO(dir)->create(dir, &dentry->d_name, &attr, open_flags); + nfs_end_data_update(dir); if (!IS_ERR(inode)) { d_instantiate(dentry, inode); nfs_renew_times(dentry); + nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); error = 0; } else { error = PTR_ERR(inode); @@ -1004,9 +1068,10 @@ nfs_mknod(struct inode *dir, struct dent attr.ia_valid = ATTR_MODE; lock_kernel(); - nfs_zap_caches(dir); + nfs_begin_data_update(dir); error = NFS_PROTO(dir)->mknod(dir, &dentry->d_name, &attr, rdev, &fhandle, &fattr); + nfs_end_data_update(dir); if (!error) error = nfs_instantiate(dentry, &fhandle, &fattr); else @@ -1041,9 +1106,10 @@ static int nfs_mkdir(struct inode *dir, */ d_drop(dentry); #endif - nfs_zap_caches(dir); + nfs_begin_data_update(dir); error = NFS_PROTO(dir)->mkdir(dir, &dentry->d_name, &attr, &fhandle, &fattr); + nfs_end_data_update(dir); if (!error) error = nfs_instantiate(dentry, &fhandle, &fattr); else @@ -1060,10 +1126,12 @@ static int nfs_rmdir(struct inode *dir, dir->i_ino, dentry->d_name.name); lock_kernel(); - nfs_zap_caches(dir); + nfs_begin_data_update(dir); error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name); - if (!error) + /* Ensure the VFS deletes this inode */ + if (error == 0 && dentry->d_inode != NULL) dentry->d_inode->i_nlink = 0; + nfs_end_data_update(dir); unlock_kernel(); return error; @@ -1119,12 +1187,21 @@ dentry->d_parent->d_name.name, dentry->d goto out; } while(sdentry->d_inode != NULL); /* need negative lookup */ - nfs_zap_caches(dir); qsilly.name = silly; qsilly.len = strlen(silly); - error = NFS_PROTO(dir)->rename(dir, &dentry->d_name, dir, &qsilly); + nfs_begin_data_update(dir); + if (dentry->d_inode) { + nfs_begin_data_update(dentry->d_inode); + error = NFS_PROTO(dir)->rename(dir, &dentry->d_name, + dir, &qsilly); + nfs_end_data_update(dentry->d_inode); + } else + error = NFS_PROTO(dir)->rename(dir, &dentry->d_name, + dir, &qsilly); + nfs_end_data_update(dir); if (!error) { nfs_renew_times(dentry); + nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); d_move(dentry, sdentry); error = nfs_async_unlink(dentry); /* If we return 0 we don't unlink */ @@ -1156,14 +1233,17 @@ static int nfs_safe_remove(struct dentry goto out; } - nfs_zap_caches(dir); - if (inode) - NFS_CACHEINV(inode); - error = NFS_PROTO(dir)->remove(dir, &dentry->d_name); - if (error < 0) - goto out; - if (inode) - inode->i_nlink--; + nfs_begin_data_update(dir); + if (inode != NULL) { + nfs_begin_data_update(inode); + error = NFS_PROTO(dir)->remove(dir, &dentry->d_name); + /* The VFS may want to delete this inode */ + if (error == 0) + inode->i_nlink--; + nfs_end_data_update(inode); + } else + error = NFS_PROTO(dir)->remove(dir, &dentry->d_name); + nfs_end_data_update(dir); out: return error; } @@ -1198,9 +1278,10 @@ static int nfs_unlink(struct inode *dir, spin_unlock(&dentry->d_lock); spin_unlock(&dcache_lock); error = nfs_safe_remove(dentry); - if (!error) + if (!error) { nfs_renew_times(dentry); - else if (need_rehash) + nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); + } else if (need_rehash) d_rehash(dentry); unlock_kernel(); return error; @@ -1247,9 +1328,10 @@ dentry->d_parent->d_name.name, dentry->d qsymname.len = strlen(symname); lock_kernel(); - nfs_zap_caches(dir); + nfs_begin_data_update(dir); error = NFS_PROTO(dir)->symlink(dir, &dentry->d_name, &qsymname, &attr, &sym_fh, &sym_attr); + nfs_end_data_update(dir); if (!error) { error = nfs_instantiate(dentry, &sym_fh, &sym_attr); } else { @@ -1281,9 +1363,12 @@ nfs_link(struct dentry *old_dentry, stru */ lock_kernel(); d_drop(dentry); - nfs_zap_caches(dir); - NFS_CACHEINV(inode); + + nfs_begin_data_update(dir); + nfs_begin_data_update(inode); error = NFS_PROTO(dir)->link(inode, dir, &dentry->d_name); + nfs_end_data_update(inode); + nfs_end_data_update(dir); unlock_kernel(); return error; } @@ -1388,16 +1473,23 @@ go_ahead: if (new_inode) d_delete(new_dentry); - nfs_zap_caches(new_dir); - nfs_zap_caches(old_dir); + nfs_begin_data_update(old_dir); + nfs_begin_data_update(new_dir); + nfs_begin_data_update(old_inode); error = NFS_PROTO(old_dir)->rename(old_dir, &old_dentry->d_name, new_dir, &new_dentry->d_name); + nfs_end_data_update(old_inode); + nfs_end_data_update(new_dir); + nfs_end_data_update(old_dir); out: if (rehash) d_rehash(rehash); - if (!error && !S_ISDIR(old_inode->i_mode)) - d_move(old_dentry, new_dentry); - nfs_renew_times(new_dentry); + if (!error) { + if (!S_ISDIR(old_inode->i_mode)) + d_move(old_dentry, new_dentry); + nfs_renew_times(new_dentry); + nfs_set_verifier(new_dentry, nfs_save_change_attribute(new_dir)); + } /* new dentry created? */ if (dentry) @@ -1451,7 +1543,8 @@ nfs_permission(struct inode *inode, int cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0); if (cache->cred == cred - && time_before(jiffies, cache->jiffies + NFS_ATTRTIMEO(inode))) { + && time_before(jiffies, cache->jiffies + NFS_ATTRTIMEO(inode)) + && !(NFS_FLAGS(inode) & NFS_INO_INVALID_ATTR)) { if (!(res = cache->err)) { /* Is the mask a subset of an accepted mask? */ if ((cache->mask & mask) == mask) --- linux-2.6.4-rc2/fs/nfs/direct.c 2003-10-17 15:58:04.000000000 -0700 +++ 25/fs/nfs/direct.c 2004-03-07 20:47:26.000000000 -0800 @@ -269,6 +269,7 @@ nfs_direct_write_seg(struct inode *inode if (IS_SYNC(inode) || NFS_PROTO(inode)->version == 2 || count <= wsize) wdata.args.stable = NFS_FILE_SYNC; + nfs_begin_data_update(inode); retry: need_commit = 0; tot_bytes = 0; @@ -334,6 +335,8 @@ retry: VERF_SIZE) != 0) goto sync_retry; } + nfs_end_data_update(inode); + NFS_FLAGS(inode) |= NFS_INO_INVALID_DATA; return tot_bytes; --- linux-2.6.4-rc2/fs/nfsd/nfsfh.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/fs/nfsd/nfsfh.c 2004-03-07 20:46:46.000000000 -0800 @@ -117,19 +117,14 @@ fh_verify(struct svc_rqst *rqstp, struct case 0: break; default: goto out; } - - switch (fh->fh_fsid_type) { - case 0: - len = 2; - break; - case 1: - len = 1; - break; - case 2: + len = key_len(fh->fh_fsid_type) / 4; + if (len == 0) goto out; + if (fh->fh_fsid_type == 2) { + /* deprecated, convert to type 3 */ len = 3; - break; - default: - goto out; + fh->fh_fsid_type = 3; + fh->fh_fsid[0] = new_encode_dev(MKDEV(ntohl(fh->fh_fsid[0]), ntohl(fh->fh_fsid[1]))); + fh->fh_fsid[1] = fh->fh_fsid[2]; } if ((data_left -= len)<0) goto out; exp = exp_find(rqstp->rq_client, fh->fh_fsid_type, datap, &rqstp->rq_chandle); @@ -336,19 +331,31 @@ fh_compose(struct svc_fh *fhp, struct sv parent->d_name.name, dentry->d_name.name, (inode ? inode->i_ino : 0)); - /* for large devnums rules are simple */ - if (!old_valid_dev(ex_dev)) { - ref_fh_version = 1; - if (exp->ex_flags & NFSEXP_FSID) - ref_fh_fsid_type = 1; - else - ref_fh_fsid_type = 2; - } else if (ref_fh) { + if (ref_fh) { ref_fh_version = ref_fh->fh_handle.fh_version; - ref_fh_fsid_type = ref_fh->fh_handle.fh_fsid_type; - if (!(exp->ex_flags & NFSEXP_FSID) || ref_fh_fsid_type == 2) + if (ref_fh_version == 0xca) + ref_fh_fsid_type = 0; + else + ref_fh_fsid_type = ref_fh->fh_handle.fh_fsid_type; + if (ref_fh_fsid_type > 3) ref_fh_fsid_type = 0; } + /* make sure ref_fh type works for given export */ + if (ref_fh_fsid_type == 1 && + !(exp->ex_flags & NFSEXP_FSID)) { + /* if we don't have an fsid, we cannot provide one... */ + ref_fh_fsid_type = 0; + } + if (!old_valid_dev(ex_dev) && ref_fh_fsid_type == 0) { + /* for newer device numbers, we must use a newer fsid format */ + ref_fh_version = 1; + ref_fh_fsid_type = 3; + } + if (old_valid_dev(ex_dev) && + (ref_fh_fsid_type == 2 || ref_fh_fsid_type == 3)) + /* must use type1 for smaller device numbers */ + ref_fh_fsid_type = 0; + if (ref_fh == fhp) fh_put(ref_fh); @@ -376,16 +383,23 @@ fh_compose(struct svc_fh *fhp, struct sv if (inode) _fh_update_old(dentry, exp, &fhp->fh_handle); } else { + int len; fhp->fh_handle.fh_version = 1; fhp->fh_handle.fh_auth_type = 0; datap = fhp->fh_handle.fh_auth+0; fhp->fh_handle.fh_fsid_type = ref_fh_fsid_type; switch (ref_fh_fsid_type) { + case 0: + /* + * fsid_type 0: + * 2byte major, 2byte minor, 4byte inode + */ + mk_fsid_v0(datap, ex_dev, + exp->ex_dentry->d_inode->i_ino); + break; case 1: /* fsid_type 1 == 4 bytes filesystem id */ mk_fsid_v1(datap, exp->ex_fsid); - datap += 1; - fhp->fh_handle.fh_size = 2*4; break; case 2: /* @@ -394,21 +408,22 @@ fh_compose(struct svc_fh *fhp, struct sv */ mk_fsid_v2(datap, ex_dev, exp->ex_dentry->d_inode->i_ino); - datap += 3; - fhp->fh_handle.fh_size = 4*4; break; - default: + case 3: /* - * fsid_type 0: - * 2byte major, 2byte minor, 4byte inode + * fsid_type 3: + * 4byte devicenumber, 4byte inode */ - mk_fsid_v0(datap, ex_dev, + mk_fsid_v3(datap, ex_dev, exp->ex_dentry->d_inode->i_ino); - datap += 2; - fhp->fh_handle.fh_size = 3*4; + break; } + len = key_len(ref_fh_fsid_type); + datap += len/4; + fhp->fh_handle.fh_size = 4 + len; + if (inode) { - int size = fhp->fh_maxsize/4 - 3; + int size = (fhp->fh_maxsize-len-4)/4; fhp->fh_handle.fh_fileid_type = _fh_update(dentry, exp, datap, &size); fhp->fh_handle.fh_size += size*4; --- linux-2.6.4-rc2/fs/nfs/file.c 2004-02-17 20:48:46.000000000 -0800 +++ 25/fs/nfs/file.c 2004-03-07 20:47:30.000000000 -0800 @@ -104,11 +104,16 @@ nfs_file_flush(struct file *file) dfprintk(VFS, "nfs: flush(%s/%ld)\n", inode->i_sb->s_id, inode->i_ino); + if ((file->f_mode & FMODE_WRITE) == 0) + return 0; lock_kernel(); - status = nfs_wb_file(inode, file); + /* Ensure that data+attribute caches are up to date after close() */ + status = nfs_wb_all(inode); if (!status) { status = file->f_error; file->f_error = 0; + if (!status) + __nfs_revalidate_inode(NFS_SERVER(inode), inode); } unlock_kernel(); return status; @@ -179,7 +184,7 @@ nfs_fsync(struct file *file, struct dent dfprintk(VFS, "nfs: fsync(%s/%ld)\n", inode->i_sb->s_id, inode->i_ino); lock_kernel(); - status = nfs_wb_file(inode, file); + status = nfs_wb_all(inode); if (!status) { status = file->f_error; file->f_error = 0; --- linux-2.6.4-rc2/fs/nfs/inode.c 2004-02-17 20:48:46.000000000 -0800 +++ 25/fs/nfs/inode.c 2004-03-07 20:47:55.000000000 -0800 @@ -47,14 +47,11 @@ * their needs. People that do NFS over a slow network, might for * instance want to reduce it to something closer to 1 for improved * interactive response. - * - * For the moment, though, we instead set it to RPC_MAXREQS, which - * is the maximum number of simultaneous RPC requests on the wire. */ -#define NFS_MAX_READAHEAD RPC_MAXREQS +#define NFS_MAX_READAHEAD (RPC_DEF_SLOT_TABLE - 1) -void nfs_zap_caches(struct inode *); static void nfs_invalidate_inode(struct inode *); +static int nfs_update_inode(struct inode *, struct nfs_fattr *, unsigned long); static struct inode *nfs_alloc_inode(struct super_block *sb); static void nfs_destroy_inode(struct inode *); @@ -118,7 +115,7 @@ nfs_write_inode(struct inode *inode, int { int flags = sync ? FLUSH_WAIT : 0; - nfs_commit_file(inode, NULL, 0, 0, flags); + nfs_commit_inode(inode, 0, 0, flags); } static void @@ -151,6 +148,7 @@ nfs_clear_inode(struct inode *inode) cred = nfsi->cache_access.cred; if (cred) put_rpccred(cred); + BUG_ON(atomic_read(&nfsi->data_updates) != 0); } void @@ -230,50 +228,23 @@ nfs_block_size(unsigned long bsize, unsi /* * Obtain the root inode of the file system. */ -static int -nfs_get_root(struct inode **rooti, rpc_authflavor_t authflavor, struct super_block *sb, struct nfs_fh *rootfh) +static struct inode * +nfs_get_root(struct super_block *sb, struct nfs_fh *rootfh, struct nfs_fsinfo *fsinfo) { struct nfs_server *server = NFS_SB(sb); - struct nfs_fattr fattr = { }; + struct inode *rooti; int error; - error = server->rpc_ops->getroot(server, rootfh, &fattr); - if (error == -EACCES && authflavor > RPC_AUTH_MAXFLAVOR) { - /* - * Some authentication types (gss/krb5, most notably) - * are such that root won't be able to present a - * credential for GETATTR (ie, getroot()). - * - * We still want the mount to succeed. - * - * So we fake the attr values and mark the inode as such. - * On the first succesful traversal, we fix everything. - * The auth type test isn't quite correct, but whatever. - */ - dfprintk(VFS, "NFS: faking root inode\n"); - - fattr.fileid = 1; - fattr.nlink = 2; /* minimum for a dir */ - fattr.type = NFDIR; - fattr.mode = S_IFDIR|S_IRUGO|S_IXUGO; - fattr.size = 4096; - fattr.du.nfs3.used = 1; - fattr.valid = NFS_ATTR_FATTR|NFS_ATTR_FATTR_V3; - } else if (error < 0) { + error = server->rpc_ops->getroot(server, rootfh, fsinfo); + if (error < 0) { printk(KERN_NOTICE "nfs_get_root: getattr error = %d\n", -error); - *rooti = NULL; /* superfluous ... but safe */ - return error; + return ERR_PTR(error); } - *rooti = nfs_fhget(sb, rootfh, &fattr); - if (error == -EACCES && authflavor > RPC_AUTH_MAXFLAVOR) { - if (*rooti) { - NFS_FLAGS(*rooti) |= NFS_INO_FAKE_ROOT; - NFS_CACHEINV((*rooti)); - error = 0; - } - } - return error; + rooti = nfs_fhget(sb, rootfh, fsinfo->fattr); + if (!rooti) + return ERR_PTR(-ENOMEM); + return rooti; } /* @@ -283,7 +254,7 @@ static int nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor) { struct nfs_server *server; - struct inode *root_inode = NULL; + struct inode *root_inode; struct nfs_fattr fattr; struct nfs_fsinfo fsinfo = { .fattr = &fattr, @@ -299,8 +270,9 @@ nfs_sb_init(struct super_block *sb, rpc_ sb->s_magic = NFS_SUPER_MAGIC; + root_inode = nfs_get_root(sb, &server->fh, &fsinfo); /* Did getting the root inode fail? */ - if (nfs_get_root(&root_inode, authflavor, sb, &server->fh) < 0) + if (IS_ERR(root_inode)) goto out_no_root; sb->s_root = d_alloc_root(root_inode); if (!sb->s_root) @@ -309,10 +281,6 @@ nfs_sb_init(struct super_block *sb, rpc_ sb->s_root->d_op = server->rpc_ops->dentry_ops; /* Get some general file system info */ - if (server->rpc_ops->fsinfo(server, &server->fh, &fsinfo) < 0) { - printk(KERN_NOTICE "NFS: cannot retrieve file system info.\n"); - goto out_no_root; - } if (server->namelen == 0 && server->rpc_ops->pathconf(server, &server->fh, &pathinfo) >= 0) server->namelen = pathinfo.max_namelen; @@ -368,13 +336,11 @@ nfs_sb_init(struct super_block *sb, rpc_ rpc_setbufsize(server->client, server->wsize + 100, server->rsize + 100); return 0; /* Yargs. It didn't work out. */ -out_free_all: - if (root_inode) - iput(root_inode); - return -EINVAL; out_no_root: printk("nfs_read_super: get root inode failed\n"); - goto out_free_all; + if (!IS_ERR(root_inode)) + iput(root_inode); + return -EINVAL; } /* @@ -402,13 +368,13 @@ nfs_create_client(struct nfs_server *ser /* create transport and client */ xprt = xprt_create_proto(tcp ? IPPROTO_TCP : IPPROTO_UDP, &server->addr, &timeparms); - if (xprt == NULL) { + if (IS_ERR(xprt)) { printk(KERN_WARNING "NFS: cannot create RPC transport.\n"); - goto out_fail; + return (struct rpc_clnt *)xprt; } clnt = rpc_create_client(xprt, server->hostname, &nfs_program, server->rpc_ops->version, data->pseudoflavor); - if (clnt == NULL) { + if (IS_ERR(clnt)) { printk(KERN_WARNING "NFS: cannot create RPC client.\n"); goto out_fail; } @@ -421,9 +387,8 @@ nfs_create_client(struct nfs_server *ser return clnt; out_fail: - if (xprt) - xprt_destroy(xprt); - return NULL; + xprt_destroy(xprt); + return clnt; } /* @@ -627,13 +592,17 @@ static int nfs_show_options(struct seq_f void nfs_zap_caches(struct inode *inode) { + struct nfs_inode *nfsi = NFS_I(inode); + int mode = inode->i_mode; + NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode); NFS_ATTRTIMEO_UPDATE(inode) = jiffies; - invalidate_remote_inode(inode); - memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode))); - NFS_CACHEINV(inode); + if (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)) + nfsi->flags |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA; + else + nfsi->flags |= NFS_INO_INVALID_ATTR; } /* @@ -673,9 +642,6 @@ nfs_find_actor(struct inode *inode, void return 0; if (is_bad_inode(inode)) return 0; - /* Force an attribute cache update if inode->i_count == 0 */ - if (!atomic_read(&inode->i_count)) - NFS_CACHEINV(inode); return 1; } @@ -729,7 +695,7 @@ nfs_fhget(struct super_block *sb, struct inode->i_ino = hash; /* We can't support update_atime(), since the server will reset it */ - inode->i_flags |= S_NOATIME; + inode->i_flags |= S_NOATIME|S_NOCMTIME; inode->i_mode = fattr->mode; /* Why so? Because we want revalidate for devices/FIFOs, and * that's precisely what we have in nfs_file_inode_operations. @@ -754,10 +720,6 @@ nfs_fhget(struct super_block *sb, struct inode->i_atime = fattr->atime; inode->i_mtime = fattr->mtime; inode->i_ctime = fattr->ctime; - nfsi->read_cache_ctime = fattr->ctime; - nfsi->read_cache_mtime = fattr->mtime; - nfsi->cache_mtime_jiffies = fattr->timestamp; - nfsi->read_cache_isize = fattr->size; if (fattr->valid & NFS_ATTR_FATTR_V4) nfsi->change_attr = fattr->change_attr; inode->i_size = nfs_size_to_loff_t(fattr->size); @@ -804,70 +766,50 @@ nfs_setattr(struct dentry *dentry, struc struct nfs_fattr fattr; int error; + if (attr->ia_valid & ATTR_SIZE) { + if (!S_ISREG(inode->i_mode) || attr->ia_size == i_size_read(inode)) + attr->ia_valid &= ~ATTR_SIZE; + } + /* Optimization: if the end result is no change, don't RPC */ attr->ia_valid &= NFS_VALID_ATTRS; if (attr->ia_valid == 0) return 0; lock_kernel(); - - /* - * Make sure the inode is up-to-date. - */ - error = nfs_revalidate_inode(NFS_SERVER(inode),inode); - if (error) { -#ifdef NFS_PARANOIA -printk("nfs_setattr: revalidate failed, error=%d\n", error); -#endif - goto out; + nfs_begin_data_update(inode); + /* Write all dirty data if we're changing file permissions or size */ + if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID|ATTR_SIZE)) != 0) { + if (filemap_fdatawrite(inode->i_mapping) == 0) + filemap_fdatawait(inode->i_mapping); + nfs_wb_all(inode); } - - if (!S_ISREG(inode->i_mode)) { - attr->ia_valid &= ~ATTR_SIZE; - if (attr->ia_valid == 0) - goto out; - } else { - filemap_fdatawrite(inode->i_mapping); - error = nfs_wb_all(inode); - filemap_fdatawait(inode->i_mapping); - if (error) - goto out; - /* Optimize away unnecessary truncates */ - if ((attr->ia_valid & ATTR_SIZE) && i_size_read(inode) == attr->ia_size) - attr->ia_valid &= ~ATTR_SIZE; - } - if (!attr->ia_valid) - goto out; - error = NFS_PROTO(inode)->setattr(dentry, &fattr, attr); - if (error) - goto out; - /* - * If we changed the size or mtime, update the inode - * now to avoid invalidating the page cache. - */ - if (attr->ia_valid & ATTR_SIZE) { - if (attr->ia_size != fattr.size) - printk("nfs_setattr: attr=%Ld, fattr=%Ld??\n", - (long long) attr->ia_size, (long long)fattr.size); - vmtruncate(inode, attr->ia_size); + if (error == 0) { + nfs_refresh_inode(inode, &fattr); + if ((attr->ia_valid & ATTR_MODE) != 0) { + int mode; + mode = inode->i_mode & ~S_IALLUGO; + mode |= attr->ia_mode & S_IALLUGO; + inode->i_mode = mode; + } + if ((attr->ia_valid & ATTR_UID) != 0) + inode->i_uid = attr->ia_uid; + if ((attr->ia_valid & ATTR_GID) != 0) + inode->i_gid = attr->ia_gid; + if ((attr->ia_valid & ATTR_SIZE) != 0) { + inode->i_size = attr->ia_size; + vmtruncate(inode, attr->ia_size); + } } - - /* - * If we changed the size or mtime, update the inode - * now to avoid invalidating the page cache. - */ - if (!(fattr.valid & NFS_ATTR_WCC)) { - struct nfs_inode *nfsi = NFS_I(inode); - fattr.pre_size = nfsi->read_cache_isize; - fattr.pre_mtime = nfsi->read_cache_mtime; - fattr.pre_ctime = nfsi->read_cache_ctime; - fattr.valid |= NFS_ATTR_WCC; - } - /* Force an attribute cache update */ - NFS_CACHEINV(inode); - error = nfs_refresh_inode(inode, &fattr); -out: + if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0) { + struct rpc_cred **cred = &NFS_I(inode)->cache_access.cred; + if (*cred) { + put_rpccred(*cred); + *cred = NULL; + } + } + nfs_end_data_update(inode); unlock_kernel(); return error; } @@ -895,7 +837,19 @@ nfs_wait_on_inode(struct inode *inode, i int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) { struct inode *inode = dentry->d_inode; - int err = nfs_revalidate_inode(NFS_SERVER(inode), inode); + struct nfs_inode *nfsi = NFS_I(inode); + int need_atime = nfsi->flags & NFS_INO_INVALID_ATIME; + int err; + + if (__IS_FLG(inode, MS_NOATIME)) + need_atime = 0; + else if (__IS_FLG(inode, MS_NODIRATIME) && S_ISDIR(inode->i_mode)) + need_atime = 0; + /* We may force a getattr if the user cares about atime */ + if (need_atime) + err = __nfs_revalidate_inode(NFS_SERVER(inode), inode); + else + err = nfs_revalidate_inode(NFS_SERVER(inode), inode); if (!err) generic_fillattr(inode, stat); return err; @@ -930,8 +884,10 @@ int nfs_open(struct inode *inode, struct auth = NFS_CLIENT(inode)->cl_auth; cred = rpcauth_lookupcred(auth, 0); filp->private_data = cred; - if (filp->f_mode & FMODE_WRITE) + if ((filp->f_mode & FMODE_WRITE) != 0) { nfs_set_mmcred(inode, cred); + nfs_begin_data_update(inode); + } return 0; } @@ -940,6 +896,8 @@ int nfs_release(struct inode *inode, str struct rpc_cred *cred; lock_kernel(); + if ((filp->f_mode & FMODE_WRITE) != 0) + nfs_end_data_update(inode); cred = nfs_file_cred(filp); if (cred) put_rpccred(cred); @@ -956,6 +914,9 @@ __nfs_revalidate_inode(struct nfs_server { int status = -ESTALE; struct nfs_fattr fattr; + struct nfs_inode *nfsi = NFS_I(inode); + unsigned long verifier; + unsigned int flags; dfprintk(PAGECACHE, "NFS: revalidating (%s/%Ld)\n", inode->i_sb->s_id, (long long)NFS_FILEID(inode)); @@ -965,23 +926,22 @@ __nfs_revalidate_inode(struct nfs_server goto out_nowait; if (NFS_STALE(inode) && inode != inode->i_sb->s_root->d_inode) goto out_nowait; - if (NFS_FAKE_ROOT(inode)) { - dfprintk(VFS, "NFS: not revalidating fake root\n"); - status = 0; - goto out_nowait; - } while (NFS_REVALIDATING(inode)) { status = nfs_wait_on_inode(inode, NFS_INO_REVALIDATING); if (status < 0) goto out_nowait; - if (time_before(jiffies,NFS_READTIME(inode)+NFS_ATTRTIMEO(inode))) { - status = NFS_STALE(inode) ? -ESTALE : 0; - goto out_nowait; - } + if (NFS_SERVER(inode)->flags & NFS_MOUNT_NOAC) + continue; + if (NFS_FLAGS(inode) & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ATIME)) + continue; + status = NFS_STALE(inode) ? -ESTALE : 0; + goto out_nowait; } NFS_FLAGS(inode) |= NFS_INO_REVALIDATING; + /* Protect against RPC races by saving the change attribute */ + verifier = nfs_save_change_attribute(inode); status = NFS_PROTO(inode)->getattr(inode, &fattr); if (status) { dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) getattr failed, error=%d\n", @@ -995,13 +955,36 @@ __nfs_revalidate_inode(struct nfs_server goto out; } - status = nfs_refresh_inode(inode, &fattr); + status = nfs_update_inode(inode, &fattr, verifier); if (status) { dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) refresh failed, error=%d\n", inode->i_sb->s_id, (long long)NFS_FILEID(inode), status); goto out; } + flags = nfsi->flags; + /* + * We may need to keep the attributes marked as invalid if + * we raced with nfs_end_attr_update(). + */ + if (verifier == nfsi->cache_change_attribute) + nfsi->flags &= ~(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME); + /* Do the page cache invalidation */ + if (flags & NFS_INO_INVALID_DATA) { + if (S_ISREG(inode->i_mode)) { + if (filemap_fdatawrite(inode->i_mapping) == 0) + filemap_fdatawait(inode->i_mapping); + nfs_wb_all(inode); + } + nfsi->flags &= ~NFS_INO_INVALID_DATA; + invalidate_inode_pages2(inode->i_mapping); + memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode))); + dfprintk(PAGECACHE, "NFS: (%s/%Ld) data cache invalidated\n", + inode->i_sb->s_id, + (long long)NFS_FILEID(inode)); + /* This ensures we revalidate dentries */ + nfsi->cache_change_attribute++; + } dfprintk(PAGECACHE, "NFS: (%s/%Ld) revalidation complete\n", inode->i_sb->s_id, (long long)NFS_FILEID(inode)); @@ -1009,41 +992,104 @@ __nfs_revalidate_inode(struct nfs_server NFS_FLAGS(inode) &= ~NFS_INO_STALE; out: NFS_FLAGS(inode) &= ~NFS_INO_REVALIDATING; - wake_up(&NFS_I(inode)->nfs_i_wait); + wake_up(&nfsi->nfs_i_wait); out_nowait: unlock_kernel(); return status; } -/* - * nfs_fattr_obsolete - Test if attribute data is newer than cached data - * @inode: inode - * @fattr: attributes to test +/** + * nfs_begin_data_update + * @inode - pointer to inode + * Declare that a set of operations will update file data on the server + */ +void nfs_begin_data_update(struct inode *inode) +{ + atomic_inc(&NFS_I(inode)->data_updates); +} + +/** + * nfs_end_data_update + * @inode - pointer to inode + * Declare end of the operations that will update file data + */ +void nfs_end_data_update(struct inode *inode) +{ + struct nfs_inode *nfsi = NFS_I(inode); + + if (atomic_dec_and_test(&nfsi->data_updates)) { + nfsi->cache_change_attribute ++; + /* Mark the attribute cache for revalidation */ + nfsi->flags |= NFS_INO_INVALID_ATTR; + /* Directories and symlinks: invalidate page cache too */ + if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) + nfsi->flags |= NFS_INO_INVALID_DATA; + } +} + +/** + * nfs_refresh_inode - verify consistency of the inode attribute cache + * @inode - pointer to inode + * @fattr - updated attributes * - * Avoid stuffing the attribute cache with obsolete information. - * We always accept updates if the attribute cache timed out, or if - * fattr->ctime is newer than our cached value. - * If fattr->ctime matches the cached value, we still accept the update - * if it increases the file size. + * Verifies the attribute cache. If we have just changed the attributes, + * so that fattr carries weak cache consistency data, then it may + * also update the ctime/mtime/change_attribute. */ -static inline -int nfs_fattr_obsolete(struct inode *inode, struct nfs_fattr *fattr) +int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) { struct nfs_inode *nfsi = NFS_I(inode); - long cdif; + loff_t cur_size, new_isize; + int data_unstable; + + /* Are we in the process of updating data on the server? */ + data_unstable = nfs_caches_unstable(inode); - if (time_after(jiffies, nfsi->read_cache_jiffies + nfsi->attrtimeo)) - goto out_valid; - cdif = fattr->ctime.tv_sec - nfsi->read_cache_ctime.tv_sec; - if (cdif == 0) - cdif = fattr->ctime.tv_nsec - nfsi->read_cache_ctime.tv_nsec; - if (cdif > 0) - goto out_valid; - /* Ugh... */ - if (cdif == 0 && fattr->size > nfsi->read_cache_isize) - goto out_valid; - return -1; - out_valid: + if (fattr->valid & NFS_ATTR_FATTR_V4) { + if ((fattr->valid & NFS_ATTR_PRE_CHANGE) != 0 + && nfsi->change_attr == fattr->pre_change_attr) + nfsi->change_attr = fattr->change_attr; + if (!data_unstable && nfsi->change_attr != fattr->change_attr) + nfsi->flags |= NFS_INO_INVALID_ATTR; + } + + if ((fattr->valid & NFS_ATTR_FATTR) == 0) + return 0; + + /* Has the inode gone and changed behind our back? */ + if (nfsi->fileid != fattr->fileid + || (inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT)) + return -EIO; + + cur_size = i_size_read(inode); + new_isize = nfs_size_to_loff_t(fattr->size); + + /* If we have atomic WCC data, we may update some attributes */ + if ((fattr->valid & NFS_ATTR_WCC) != 0) { + if (timespec_equal(&inode->i_ctime, &fattr->pre_ctime)) + memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime)); + if (timespec_equal(&inode->i_mtime, &fattr->pre_mtime)) + memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime)); + } + + /* Verify a few of the more important attributes */ + if (!data_unstable) { + if (!timespec_equal(&inode->i_mtime, &fattr->mtime) + || cur_size != new_isize) + nfsi->flags |= NFS_INO_INVALID_ATTR; + } else if (S_ISREG(inode->i_mode) && new_isize > cur_size) + nfsi->flags |= NFS_INO_INVALID_ATTR; + + /* Have any file permissions changed? */ + if ((inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO) + || inode->i_uid != fattr->uid + || inode->i_gid != fattr->gid) + nfsi->flags |= NFS_INO_INVALID_ATTR; + + if (!timespec_equal(&inode->i_atime, &fattr->atime)) + nfsi->flags |= NFS_INO_INVALID_ATIME; + + nfsi->read_cache_jiffies = fattr->timestamp; return 0; } @@ -1059,65 +1105,66 @@ int nfs_fattr_obsolete(struct inode *ino * * A very similar scenario holds for the dir cache. */ -int -__nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) +static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr, unsigned long verifier) { struct nfs_inode *nfsi = NFS_I(inode); __u64 new_size; loff_t new_isize; - int invalid = 0; - int mtime_update = 0; + unsigned int invalid = 0; loff_t cur_isize; + int data_unstable; - dfprintk(VFS, "NFS: refresh_inode(%s/%ld ct=%d info=0x%x)\n", - inode->i_sb->s_id, inode->i_ino, + dfprintk(VFS, "NFS: %s(%s/%ld ct=%d info=0x%x)\n", + __FUNCTION__, inode->i_sb->s_id, inode->i_ino, atomic_read(&inode->i_count), fattr->valid); - /* First successful call after mount, fill real data. */ - if (NFS_FAKE_ROOT(inode)) { - dfprintk(VFS, "NFS: updating fake root\n"); - nfsi->fileid = fattr->fileid; - NFS_FLAGS(inode) &= ~NFS_INO_FAKE_ROOT; - } + if ((fattr->valid & NFS_ATTR_FATTR) == 0) + return 0; if (nfsi->fileid != fattr->fileid) { - printk(KERN_ERR "nfs_refresh_inode: inode number mismatch\n" + printk(KERN_ERR "%s: inode number mismatch\n" "expected (%s/0x%Lx), got (%s/0x%Lx)\n", + __FUNCTION__, inode->i_sb->s_id, (long long)nfsi->fileid, inode->i_sb->s_id, (long long)fattr->fileid); goto out_err; } - /* Throw out obsolete READDIRPLUS attributes */ - if (time_before(fattr->timestamp, NFS_READTIME(inode))) - return 0; /* * Make sure the inode's type hasn't changed. */ if ((inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT)) goto out_changed; - new_size = fattr->size; - new_isize = nfs_size_to_loff_t(fattr->size); - - /* Avoid races */ - if (nfs_fattr_obsolete(inode, fattr)) - goto out_nochange; - /* * Update the read time so we don't revalidate too often. */ nfsi->read_cache_jiffies = fattr->timestamp; - /* - * Note: NFS_CACHE_ISIZE(inode) reflects the state of the cache. - * NOT inode->i_size!!! - */ - if (nfsi->read_cache_isize != new_size) { + /* Are we racing with known updates of the metadata on the server? */ + data_unstable = ! nfs_verify_change_attribute(inode, verifier); + + /* Check if the file size agrees */ + new_size = fattr->size; + new_isize = nfs_size_to_loff_t(fattr->size); + cur_isize = i_size_read(inode); + if (cur_isize != new_size) { #ifdef NFS_DEBUG_VERBOSE printk(KERN_DEBUG "NFS: isize change on %s/%ld\n", inode->i_sb->s_id, inode->i_ino); #endif - invalid = 1; + /* + * If we have pending writebacks, things can get + * messy. + */ + if (S_ISREG(inode->i_mode) && data_unstable) { + if (new_isize > cur_isize) { + inode->i_size = new_isize; + invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA; + } + } else { + inode->i_size = new_isize; + invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA; + } } /* @@ -1125,12 +1172,13 @@ __nfs_refresh_inode(struct inode *inode, * can change this value in VFS without requiring a * cache revalidation. */ - if (!timespec_equal(&nfsi->read_cache_mtime, &fattr->mtime)) { + if (!timespec_equal(&inode->i_mtime, &fattr->mtime)) { + memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime)); #ifdef NFS_DEBUG_VERBOSE printk(KERN_DEBUG "NFS: mtime change on %s/%ld\n", inode->i_sb->s_id, inode->i_ino); #endif - invalid = 1; - mtime_update = 1; + if (!data_unstable) + invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA; } if ((fattr->valid & NFS_ATTR_FATTR_V4) @@ -1139,47 +1187,15 @@ __nfs_refresh_inode(struct inode *inode, printk(KERN_DEBUG "NFS: change_attr change on %s/%ld\n", inode->i_sb->s_id, inode->i_ino); #endif - invalid = 1; - } - - /* Check Weak Cache Consistency data. - * If size and mtime match the pre-operation values, we can - * assume that any attribute changes were caused by our NFS - * operation, so there's no need to invalidate the caches. - */ - if ((fattr->valid & NFS_ATTR_PRE_CHANGE) - && nfsi->change_attr == fattr->pre_change_attr) { - invalid = 0; - } - else if ((fattr->valid & NFS_ATTR_WCC) - && nfsi->read_cache_isize == fattr->pre_size - && timespec_equal(&nfsi->read_cache_mtime, &fattr->pre_mtime)) { - invalid = 0; - } - - /* - * If we have pending writebacks, things can get - * messy. - */ - cur_isize = i_size_read(inode); - if (nfs_have_writebacks(inode) && new_isize < cur_isize) - new_isize = cur_isize; - - nfsi->read_cache_ctime = fattr->ctime; - inode->i_ctime = fattr->ctime; - inode->i_atime = fattr->atime; - - if (mtime_update) { - if (invalid) - nfsi->cache_mtime_jiffies = fattr->timestamp; - nfsi->read_cache_mtime = fattr->mtime; - inode->i_mtime = fattr->mtime; + nfsi->change_attr = fattr->change_attr; + if (!data_unstable) + invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA; } - nfsi->read_cache_isize = new_size; - i_size_write(inode, new_isize); + memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime)); + memcpy(&inode->i_atime, &fattr->atime, sizeof(inode->i_atime)); - if (inode->i_mode != fattr->mode || + if ((inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO) || inode->i_uid != fattr->uid || inode->i_gid != fattr->gid) { struct rpc_cred **cred = &NFS_I(inode)->cache_access.cred; @@ -1187,11 +1203,9 @@ __nfs_refresh_inode(struct inode *inode, put_rpccred(*cred); *cred = NULL; } + invalid |= NFS_INO_INVALID_ATTR; } - if (fattr->valid & NFS_ATTR_FATTR_V4) - nfsi->change_attr = fattr->change_attr; - inode->i_mode = fattr->mode; inode->i_nlink = fattr->nlink; inode->i_uid = fattr->uid; @@ -1207,31 +1221,30 @@ __nfs_refresh_inode(struct inode *inode, inode->i_blocks = fattr->du.nfs2.blocks; inode->i_blksize = fattr->du.nfs2.blocksize; } - - /* Update attrtimeo value */ - if (invalid) { + + /* Update attrtimeo value if we're out of the unstable period */ + if (invalid & NFS_INO_INVALID_ATTR) { nfsi->attrtimeo = NFS_MINATTRTIMEO(inode); nfsi->attrtimeo_timestamp = jiffies; - invalidate_remote_inode(inode); - memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode))); } else if (time_after(jiffies, nfsi->attrtimeo_timestamp+nfsi->attrtimeo)) { if ((nfsi->attrtimeo <<= 1) > NFS_MAXATTRTIMEO(inode)) nfsi->attrtimeo = NFS_MAXATTRTIMEO(inode); nfsi->attrtimeo_timestamp = jiffies; } + /* Don't invalidate the data if we were to blame */ + if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) + || S_ISLNK(inode->i_mode))) + invalid &= ~NFS_INO_INVALID_DATA; + nfsi->flags |= invalid; return 0; - out_nochange: - if (!timespec_equal(&fattr->atime, &inode->i_atime)) - inode->i_atime = fattr->atime; - return 0; out_changed: /* * Big trouble! The inode has become a different object. */ #ifdef NFS_PARANOIA - printk(KERN_DEBUG "nfs_refresh_inode: inode %ld mode changed, %07o to %07o\n", - inode->i_ino, inode->i_mode, fattr->mode); + printk(KERN_DEBUG "%s: inode %ld mode changed, %07o to %07o\n", + __FUNCTION__, inode->i_ino, inode->i_mode, fattr->mode); #endif /* * No need to worry about unhashing the dentry, as the @@ -1352,7 +1365,7 @@ static struct file_system_type nfs_fs_ty .name = "nfs", .get_sb = nfs_get_sb, .kill_sb = nfs_kill_super, - .fs_flags = FS_ODD_RENAME|FS_REVAL_DOT, + .fs_flags = FS_ODD_RENAME|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, }; #ifdef CONFIG_NFS_V4 @@ -1472,17 +1485,19 @@ static int nfs4_fill_super(struct super_ down_write(&clp->cl_sem); if (clp->cl_rpcclient == NULL) { xprt = xprt_create_proto(proto, &server->addr, &timeparms); - if (xprt == NULL) { + if (IS_ERR(xprt)) { up_write(&clp->cl_sem); printk(KERN_WARNING "NFS: cannot create RPC transport.\n"); + err = PTR_ERR(xprt); goto out_fail; } clnt = rpc_create_client(xprt, server->hostname, &nfs_program, server->rpc_ops->version, authflavour); - if (clnt == NULL) { + if (IS_ERR(clnt)) { up_write(&clp->cl_sem); printk(KERN_WARNING "NFS: cannot create RPC client.\n"); xprt_destroy(xprt); + err = PTR_ERR(clnt); goto out_fail; } clnt->cl_chatty = 1; @@ -1495,14 +1510,17 @@ static int nfs4_fill_super(struct super_ clear_bit(NFS4CLNT_OK, &clp->cl_state); list_add_tail(&server->nfs4_siblings, &clp->cl_superblocks); clnt = rpc_clone_client(clp->cl_rpcclient); - server->nfs4_state = clp; + if (!IS_ERR(clnt)) + server->nfs4_state = clp; up_write(&clp->cl_sem); clp = NULL; - if (clnt == NULL) { + if (IS_ERR(clnt)) { printk(KERN_WARNING "NFS: cannot create RPC client.\n"); + err = PTR_ERR(clnt); goto out_remove_list; } + err = -ENOMEM; if (server->nfs4_state->cl_idmap == NULL) { printk(KERN_WARNING "NFS: failed to create idmapper.\n"); goto out_shutdown; @@ -1601,7 +1619,7 @@ static struct super_block *nfs4_get_sb(s if (data->version != NFS4_MOUNT_VERSION) { printk("nfs warning: mount version %s than kernel\n", - data->version < NFS_MOUNT_VERSION ? "older" : "newer"); + data->version < NFS4_MOUNT_VERSION ? "older" : "newer"); } p = nfs_copy_user_string(NULL, &data->hostname, 256); @@ -1666,7 +1684,7 @@ static struct file_system_type nfs4_fs_t .name = "nfs4", .get_sb = nfs4_get_sb, .kill_sb = nfs_kill_super, - .fs_flags = FS_ODD_RENAME|FS_REVAL_DOT, + .fs_flags = FS_ODD_RENAME|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, }; #define nfs4_zero_state(nfsi) \ @@ -1718,6 +1736,7 @@ static void init_once(void * foo, kmem_c INIT_LIST_HEAD(&nfsi->dirty); INIT_LIST_HEAD(&nfsi->commit); INIT_RADIX_TREE(&nfsi->nfs_page_tree, GFP_ATOMIC); + atomic_set(&nfsi->data_updates, 0); nfsi->ndirty = 0; nfsi->ncommit = 0; nfsi->npages = 0; --- linux-2.6.4-rc2/fs/nfs/mount_clnt.c 2003-06-14 12:17:57.000000000 -0700 +++ 25/fs/nfs/mount_clnt.c 2004-03-07 20:47:30.000000000 -0800 @@ -57,8 +57,9 @@ nfsroot_mount(struct sockaddr_in *addr, (unsigned)ntohl(addr->sin_addr.s_addr), path); sprintf(hostname, "%u.%u.%u.%u", NIPQUAD(addr->sin_addr.s_addr)); - if (!(mnt_clnt = mnt_create(hostname, addr, version, protocol))) - return -EACCES; + mnt_clnt = mnt_create(hostname, addr, version, protocol); + if (IS_ERR(mnt_clnt)) + return PTR_ERR(mnt_clnt); call = (version == NFS_MNT3_VERSION) ? MOUNTPROC3_MNT : MNTPROC_MNT; status = rpc_call(mnt_clnt, call, path, &result, 0); @@ -72,13 +73,14 @@ mnt_create(char *hostname, struct sockad struct rpc_xprt *xprt; struct rpc_clnt *clnt; - if (!(xprt = xprt_create_proto(protocol, srvaddr, NULL))) - return NULL; + xprt = xprt_create_proto(protocol, srvaddr, NULL); + if (IS_ERR(xprt)) + return (struct rpc_clnt *)xprt; clnt = rpc_create_client(xprt, hostname, &mnt_program, version, - RPC_AUTH_NULL); - if (!clnt) { + RPC_AUTH_UNIX); + if (IS_ERR(clnt)) { xprt_destroy(xprt); } else { clnt->cl_softrtry = 1; --- linux-2.6.4-rc2/fs/nfs/nfs2xdr.c 2003-09-27 18:57:46.000000000 -0700 +++ 25/fs/nfs/nfs2xdr.c 2004-03-07 20:47:31.000000000 -0800 @@ -36,33 +36,33 @@ extern int nfs_stat_to_errno(int stat) * Declare the space requirements for NFS arguments and replies as * number of 32bit-words */ -#define NFS_fhandle_sz 8 -#define NFS_sattr_sz 8 -#define NFS_filename_sz 1+(NFS2_MAXNAMLEN>>2) -#define NFS_path_sz 1+(NFS2_MAXPATHLEN>>2) -#define NFS_fattr_sz 17 -#define NFS_info_sz 5 -#define NFS_entry_sz NFS_filename_sz+3 - -#define NFS_diropargs_sz NFS_fhandle_sz+NFS_filename_sz -#define NFS_sattrargs_sz NFS_fhandle_sz+NFS_sattr_sz -#define NFS_readlinkargs_sz NFS_fhandle_sz -#define NFS_readargs_sz NFS_fhandle_sz+3 -#define NFS_writeargs_sz NFS_fhandle_sz+4 -#define NFS_createargs_sz NFS_diropargs_sz+NFS_sattr_sz -#define NFS_renameargs_sz NFS_diropargs_sz+NFS_diropargs_sz -#define NFS_linkargs_sz NFS_fhandle_sz+NFS_diropargs_sz -#define NFS_symlinkargs_sz NFS_diropargs_sz+NFS_path_sz+NFS_sattr_sz -#define NFS_readdirargs_sz NFS_fhandle_sz+2 - -#define NFS_attrstat_sz 1+NFS_fattr_sz -#define NFS_diropres_sz 1+NFS_fhandle_sz+NFS_fattr_sz -#define NFS_readlinkres_sz 1 -#define NFS_readres_sz 1+NFS_fattr_sz+1 -#define NFS_writeres_sz NFS_attrstat_sz -#define NFS_stat_sz 1 -#define NFS_readdirres_sz 1 -#define NFS_statfsres_sz 1+NFS_info_sz +#define NFS_fhandle_sz (8) +#define NFS_sattr_sz (8) +#define NFS_filename_sz (1+(NFS2_MAXNAMLEN>>2)) +#define NFS_path_sz (1+(NFS2_MAXPATHLEN>>2)) +#define NFS_fattr_sz (17) +#define NFS_info_sz (5) +#define NFS_entry_sz (NFS_filename_sz+3) + +#define NFS_diropargs_sz (NFS_fhandle_sz+NFS_filename_sz) +#define NFS_sattrargs_sz (NFS_fhandle_sz+NFS_sattr_sz) +#define NFS_readlinkargs_sz (NFS_fhandle_sz) +#define NFS_readargs_sz (NFS_fhandle_sz+3) +#define NFS_writeargs_sz (NFS_fhandle_sz+4) +#define NFS_createargs_sz (NFS_diropargs_sz+NFS_sattr_sz) +#define NFS_renameargs_sz (NFS_diropargs_sz+NFS_diropargs_sz) +#define NFS_linkargs_sz (NFS_fhandle_sz+NFS_diropargs_sz) +#define NFS_symlinkargs_sz (NFS_diropargs_sz+NFS_path_sz+NFS_sattr_sz) +#define NFS_readdirargs_sz (NFS_fhandle_sz+2) + +#define NFS_attrstat_sz (1+NFS_fattr_sz) +#define NFS_diropres_sz (1+NFS_fhandle_sz+NFS_fattr_sz) +#define NFS_readlinkres_sz (1) +#define NFS_readres_sz (1+NFS_fattr_sz+1) +#define NFS_writeres_sz (NFS_attrstat_sz) +#define NFS_stat_sz (1) +#define NFS_readdirres_sz (1) +#define NFS_statfsres_sz (1+NFS_info_sz) /* * Common NFS XDR functions as inlines --- linux-2.6.4-rc2/fs/nfs/nfs3proc.c 2004-02-17 20:48:46.000000000 -0800 +++ 25/fs/nfs/nfs3proc.c 2004-03-07 20:47:27.000000000 -0800 @@ -68,20 +68,6 @@ nfs3_async_handle_jukebox(struct rpc_tas return 1; } -static void -nfs3_write_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) -{ - if (fattr->valid & NFS_ATTR_FATTR) { - if (!(fattr->valid & NFS_ATTR_WCC)) { - fattr->pre_size = NFS_CACHE_ISIZE(inode); - fattr->pre_mtime = NFS_CACHE_MTIME(inode); - fattr->pre_ctime = NFS_CACHE_CTIME(inode); - fattr->valid |= NFS_ATTR_WCC; - } - nfs_refresh_inode(inode, fattr); - } -} - static struct rpc_cred * nfs_cred(struct inode *inode, struct file *filp) { @@ -99,14 +85,18 @@ nfs_cred(struct inode *inode, struct fil */ static int nfs3_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_fattr *fattr) + struct nfs_fsinfo *info) { int status; - dprintk("NFS call getroot\n"); - fattr->valid = 0; - status = rpc_call(server->client, NFS3PROC_GETATTR, fhandle, fattr, 0); - dprintk("NFS reply getroot\n"); + dprintk("%s: call fsinfo\n", __FUNCTION__); + info->fattr->valid = 0; + status = rpc_call(server->client_sys, NFS3PROC_FSINFO, fhandle, info, 0); + dprintk("%s: reply fsinfo %d\n", __FUNCTION__, status); + if (!(info->fattr->valid & NFS_ATTR_FATTR)) { + status = rpc_call(server->client_sys, NFS3PROC_GETATTR, fhandle, info->fattr, 0); + dprintk("%s: reply getattr %d\n", __FUNCTION__, status); + } return status; } @@ -280,7 +270,7 @@ nfs3_proc_write(struct nfs_write_data *w msg.rpc_cred = nfs_cred(inode, filp); status = rpc_call_sync(NFS_CLIENT(inode), &msg, rpcflags); if (status >= 0) - nfs3_write_refresh_inode(inode, fattr); + nfs_refresh_inode(inode, fattr); dprintk("NFS reply write: %d\n", status); return status < 0? status : wdata->res.count; } @@ -303,7 +293,7 @@ nfs3_proc_commit(struct nfs_write_data * msg.rpc_cred = nfs_cred(inode, filp); status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); if (status >= 0) - nfs3_write_refresh_inode(inode, fattr); + nfs_refresh_inode(inode, fattr); dprintk("NFS reply commit: %d\n", status); return status; } @@ -777,12 +767,13 @@ nfs3_proc_read_setup(struct nfs_read_dat static void nfs3_write_done(struct rpc_task *task) { - struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata; + struct nfs_write_data *data; if (nfs3_async_handle_jukebox(task)) return; + data = (struct nfs_write_data *)task->tk_calldata; if (task->tk_status >= 0) - nfs3_write_refresh_inode(data->inode, data->res.fattr); + nfs_refresh_inode(data->inode, data->res.fattr); nfs_writeback_done(task); } @@ -835,12 +826,13 @@ nfs3_proc_write_setup(struct nfs_write_d static void nfs3_commit_done(struct rpc_task *task) { - struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata; + struct nfs_write_data *data; if (nfs3_async_handle_jukebox(task)) return; + data = (struct nfs_write_data *)task->tk_calldata; if (task->tk_status >= 0) - nfs3_write_refresh_inode(data->inode, data->res.fattr); + nfs_refresh_inode(data->inode, data->res.fattr); nfs_commit_done(task); } --- linux-2.6.4-rc2/fs/nfs/nfs3xdr.c 2003-09-27 18:57:46.000000000 -0700 +++ 25/fs/nfs/nfs3xdr.c 2004-03-07 20:47:33.000000000 -0800 @@ -33,51 +33,51 @@ extern int nfs_stat_to_errno(int); * Declare the space requirements for NFS arguments and replies as * number of 32bit-words */ -#define NFS3_fhandle_sz 1+16 -#define NFS3_fh_sz NFS3_fhandle_sz /* shorthand */ -#define NFS3_sattr_sz 15 -#define NFS3_filename_sz 1+(NFS3_MAXNAMLEN>>2) -#define NFS3_path_sz 1+(NFS3_MAXPATHLEN>>2) -#define NFS3_fattr_sz 21 -#define NFS3_wcc_attr_sz 6 -#define NFS3_pre_op_attr_sz 1+NFS3_wcc_attr_sz -#define NFS3_post_op_attr_sz 1+NFS3_fattr_sz -#define NFS3_wcc_data_sz NFS3_pre_op_attr_sz+NFS3_post_op_attr_sz +#define NFS3_fhandle_sz (1+16) +#define NFS3_fh_sz (NFS3_fhandle_sz) /* shorthand */ +#define NFS3_sattr_sz (15) +#define NFS3_filename_sz (1+(NFS3_MAXNAMLEN>>2)) +#define NFS3_path_sz (1+(NFS3_MAXPATHLEN>>2)) +#define NFS3_fattr_sz (21) +#define NFS3_wcc_attr_sz (6) +#define NFS3_pre_op_attr_sz (1+NFS3_wcc_attr_sz) +#define NFS3_post_op_attr_sz (1+NFS3_fattr_sz) +#define NFS3_wcc_data_sz (NFS3_pre_op_attr_sz+NFS3_post_op_attr_sz) #define NFS3_fsstat_sz #define NFS3_fsinfo_sz #define NFS3_pathconf_sz -#define NFS3_entry_sz NFS3_filename_sz+3 +#define NFS3_entry_sz (NFS3_filename_sz+3) -#define NFS3_sattrargs_sz NFS3_fh_sz+NFS3_sattr_sz+3 -#define NFS3_diropargs_sz NFS3_fh_sz+NFS3_filename_sz -#define NFS3_accessargs_sz NFS3_fh_sz+1 -#define NFS3_readlinkargs_sz NFS3_fh_sz -#define NFS3_readargs_sz NFS3_fh_sz+3 -#define NFS3_writeargs_sz NFS3_fh_sz+5 -#define NFS3_createargs_sz NFS3_diropargs_sz+NFS3_sattr_sz -#define NFS3_mkdirargs_sz NFS3_diropargs_sz+NFS3_sattr_sz -#define NFS3_symlinkargs_sz NFS3_diropargs_sz+NFS3_path_sz+NFS3_sattr_sz -#define NFS3_mknodargs_sz NFS3_diropargs_sz+2+NFS3_sattr_sz -#define NFS3_renameargs_sz NFS3_diropargs_sz+NFS3_diropargs_sz -#define NFS3_linkargs_sz NFS3_fh_sz+NFS3_diropargs_sz -#define NFS3_readdirargs_sz NFS3_fh_sz+2 -#define NFS3_commitargs_sz NFS3_fh_sz+3 - -#define NFS3_attrstat_sz 1+NFS3_fattr_sz -#define NFS3_wccstat_sz 1+NFS3_wcc_data_sz -#define NFS3_lookupres_sz 1+NFS3_fh_sz+(2 * NFS3_post_op_attr_sz) -#define NFS3_accessres_sz 1+NFS3_post_op_attr_sz+1 -#define NFS3_readlinkres_sz 1+NFS3_post_op_attr_sz -#define NFS3_readres_sz 1+NFS3_post_op_attr_sz+3 -#define NFS3_writeres_sz 1+NFS3_wcc_data_sz+4 -#define NFS3_createres_sz 1+NFS3_fh_sz+NFS3_post_op_attr_sz+NFS3_wcc_data_sz -#define NFS3_renameres_sz 1+(2 * NFS3_wcc_data_sz) -#define NFS3_linkres_sz 1+NFS3_post_op_attr_sz+NFS3_wcc_data_sz -#define NFS3_readdirres_sz 1+NFS3_post_op_attr_sz+2 -#define NFS3_fsstatres_sz 1+NFS3_post_op_attr_sz+13 -#define NFS3_fsinfores_sz 1+NFS3_post_op_attr_sz+12 -#define NFS3_pathconfres_sz 1+NFS3_post_op_attr_sz+6 -#define NFS3_commitres_sz 1+NFS3_wcc_data_sz+2 +#define NFS3_sattrargs_sz (NFS3_fh_sz+NFS3_sattr_sz+3) +#define NFS3_diropargs_sz (NFS3_fh_sz+NFS3_filename_sz) +#define NFS3_accessargs_sz (NFS3_fh_sz+1) +#define NFS3_readlinkargs_sz (NFS3_fh_sz) +#define NFS3_readargs_sz (NFS3_fh_sz+3) +#define NFS3_writeargs_sz (NFS3_fh_sz+5) +#define NFS3_createargs_sz (NFS3_diropargs_sz+NFS3_sattr_sz) +#define NFS3_mkdirargs_sz (NFS3_diropargs_sz+NFS3_sattr_sz) +#define NFS3_symlinkargs_sz (NFS3_diropargs_sz+NFS3_path_sz+NFS3_sattr_sz) +#define NFS3_mknodargs_sz (NFS3_diropargs_sz+2+NFS3_sattr_sz) +#define NFS3_renameargs_sz (NFS3_diropargs_sz+NFS3_diropargs_sz) +#define NFS3_linkargs_sz (NFS3_fh_sz+NFS3_diropargs_sz) +#define NFS3_readdirargs_sz (NFS3_fh_sz+2) +#define NFS3_commitargs_sz (NFS3_fh_sz+3) + +#define NFS3_attrstat_sz (1+NFS3_fattr_sz) +#define NFS3_wccstat_sz (1+NFS3_wcc_data_sz) +#define NFS3_lookupres_sz (1+NFS3_fh_sz+(2 * NFS3_post_op_attr_sz)) +#define NFS3_accessres_sz (1+NFS3_post_op_attr_sz+1) +#define NFS3_readlinkres_sz (1+NFS3_post_op_attr_sz) +#define NFS3_readres_sz (1+NFS3_post_op_attr_sz+3) +#define NFS3_writeres_sz (1+NFS3_wcc_data_sz+4) +#define NFS3_createres_sz (1+NFS3_fh_sz+NFS3_post_op_attr_sz+NFS3_wcc_data_sz) +#define NFS3_renameres_sz (1+(2 * NFS3_wcc_data_sz)) +#define NFS3_linkres_sz (1+NFS3_post_op_attr_sz+NFS3_wcc_data_sz) +#define NFS3_readdirres_sz (1+NFS3_post_op_attr_sz+2) +#define NFS3_fsstatres_sz (1+NFS3_post_op_attr_sz+13) +#define NFS3_fsinfores_sz (1+NFS3_post_op_attr_sz+12) +#define NFS3_pathconfres_sz (1+NFS3_post_op_attr_sz+6) +#define NFS3_commitres_sz (1+NFS3_wcc_data_sz+2) /* * Map file type to S_IFMT bits @@ -103,9 +103,7 @@ static struct { static inline u32 * xdr_encode_fhandle(u32 *p, struct nfs_fh *fh) { - *p++ = htonl(fh->size); - memcpy(p, fh->data, fh->size); - return p + XDR_QUADLEN(fh->size); + return xdr_encode_array(p, fh->data, fh->size); } static inline u32 * --- linux-2.6.4-rc2/fs/nfs/nfs4proc.c 2004-02-17 20:48:46.000000000 -0800 +++ 25/fs/nfs/nfs4proc.c 2004-03-07 20:47:31.000000000 -0800 @@ -54,6 +54,7 @@ #define GET_OP(cp,name) &cp->ops[cp->req_nops].u.name #define OPNUM(cp) cp->ops[cp->req_nops].opnum +static int nfs4_proc_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *); static int nfs4_async_handle_error(struct rpc_task *, struct nfs_server *); extern u32 *nfs4_decode_dirent(u32 *p, struct nfs_entry *entry, int plus); extern struct rpc_procinfo nfs4_procedures[]; @@ -505,6 +506,8 @@ nfs4_open_reclaim(struct nfs4_state_owne status = rpc_call_sync(server->client, &msg, 0); nfs4_increment_seqid(status, sp); + if (status == 0) + memcpy(&state->stateid, &o_res.stateid, sizeof(state->stateid)); /* Update the inode attributes */ nfs_refresh_inode(inode, &fattr); return status; @@ -689,12 +692,12 @@ nfs4_do_setattr(struct nfs_server *serve retry: fattr->valid = 0; - if (state) + if (sattr->ia_valid & ATTR_SIZE) nfs4_copy_stateid(&arg.stateid, state, 0); - else + else memcpy(&arg.stateid, &zero_stateid, sizeof(arg.stateid)); - status = rpc_call_sync(server->client, &msg, 0); + status = rpc_call_sync(server->client, &msg, 0); if (status) { status = nfs4_handle_error(server, status); if (!status) @@ -822,10 +825,11 @@ nfs4_open_revalidate(struct inode *dir, static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_fattr *fattr) + struct nfs_fsinfo *info) { struct nfs4_compound compound; struct nfs4_op ops[4]; + struct nfs_fattr * fattr = info->fattr; unsigned char * p; struct qstr q; int status; @@ -869,7 +873,9 @@ nfs4_proc_get_root(struct nfs_server *se break; } out: - return status; + if (status) + return status; + return nfs4_proc_fsinfo(server, fhandle, info); } static int @@ -1088,12 +1094,8 @@ nfs4_proc_read(struct nfs_read_data *rda fattr->valid = 0; status = rpc_call_sync(server->client, &msg, flags); - if (!status) { + if (!status) renew_lease(server, timestamp); - /* Check cache consistency */ - if (fattr->change_attr != NFS_CHANGE_ATTR(inode)) - nfs_zap_caches(inode); - } dprintk("NFS reply read: %d\n", status); return status; } @@ -1130,7 +1132,6 @@ nfs4_proc_write(struct nfs_write_data *w fattr->valid = 0; status = rpc_call_sync(server->client, &msg, rpcflags); - NFS_CACHEINV(inode); dprintk("NFS reply write: %d\n", status); return status; } @@ -1421,6 +1422,8 @@ nfs4_proc_readdir(struct dentry *dentry, nfs4_setup_putfh(&compound, NFS_FH(dir)); nfs4_setup_readdir(&compound, cookie, NFS_COOKIEVERF(dir), &page, count, dentry); status = nfs4_call_compound(&compound, cred, 0); + if (status == 0) + memcpy(NFS_COOKIEVERF(dir), ops[1].u.readdir.rd_resp_verifier.data, NFS4_VERIFIER_SIZE); unlock_kernel(); return status; @@ -1463,7 +1466,6 @@ nfs4_proc_statfs(struct nfs_server *serv struct nfs4_compound compound; struct nfs4_op ops[2]; - memset(fsstat, 0, sizeof(*fsstat)); nfs4_setup_compound(&compound, ops, server, "statfs"); nfs4_setup_putfh(&compound, fhandle); nfs4_setup_statfs(&compound, fsstat); @@ -1480,7 +1482,6 @@ nfs4_proc_fsinfo(struct nfs_server *serv .rpc_resp = fsinfo, }; - memset(fsinfo, 0, sizeof(*fsinfo)); return rpc_call_sync(server->client, &msg, 0); } @@ -1491,7 +1492,6 @@ nfs4_proc_pathconf(struct nfs_server *se struct nfs4_compound compound; struct nfs4_op ops[2]; - memset(pathconf, 0, sizeof(*pathconf)); nfs4_setup_compound(&compound, ops, server, "statfs"); nfs4_setup_putfh(&compound, fhandle); nfs4_setup_pathconf(&compound, pathconf); @@ -1517,7 +1517,6 @@ nfs4_read_done(struct rpc_task *task) { struct nfs_read_data *data = (struct nfs_read_data *) task->tk_calldata; struct inode *inode = data->inode; - struct nfs_fattr *fattr = data->res.fattr; if (nfs4_async_handle_error(task, NFS_SERVER(inode)) == -EAGAIN) { task->tk_action = nfs4_restart_read; @@ -1525,11 +1524,6 @@ nfs4_read_done(struct rpc_task *task) } if (task->tk_status > 0) renew_lease(NFS_SERVER(inode), data->timestamp); - /* Check cache consistency */ - if (fattr->change_attr != NFS_CHANGE_ATTR(inode)) - nfs_zap_caches(inode); - if (fattr->bitmap[1] & FATTR4_WORD1_TIME_ACCESS) - inode->i_atime = fattr->atime; /* Call back common NFS readpage processing */ nfs_readpage_result(task); } @@ -1577,21 +1571,6 @@ nfs4_proc_read_setup(struct nfs_read_dat } static void -nfs4_write_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) -{ - /* Check cache consistency */ - if (fattr->pre_change_attr != NFS_CHANGE_ATTR(inode)) - nfs_zap_caches(inode); - NFS_CHANGE_ATTR(inode) = fattr->change_attr; - if (fattr->bitmap[1] & FATTR4_WORD1_SPACE_USED) - inode->i_blocks = (fattr->du.nfs3.used + 511) >> 9; - if (fattr->bitmap[1] & FATTR4_WORD1_TIME_METADATA) - inode->i_ctime = fattr->ctime; - if (fattr->bitmap[1] & FATTR4_WORD1_TIME_MODIFY) - inode->i_mtime = fattr->mtime; -} - -static void nfs4_restart_write(struct rpc_task *task) { struct nfs_write_data *data = (struct nfs_write_data *)task->tk_calldata; @@ -1617,7 +1596,6 @@ nfs4_write_done(struct rpc_task *task) } if (task->tk_status >= 0) renew_lease(NFS_SERVER(inode), data->timestamp); - nfs4_write_refresh_inode(inode, data->res.fattr); /* Call back common NFS writeback processing */ nfs_writeback_done(task); } @@ -1684,7 +1662,6 @@ nfs4_commit_done(struct rpc_task *task) task->tk_action = nfs4_restart_write; return; } - nfs4_write_refresh_inode(inode, data->res.fattr); /* Call back common NFS writeback processing */ nfs_commit_done(task); } @@ -1807,6 +1784,7 @@ nfs4_proc_file_open(struct inode *inode, if (filp->f_mode & FMODE_WRITE) { lock_kernel(); nfs_set_mmcred(inode, state->owner->so_cred); + nfs_begin_data_update(inode); unlock_kernel(); } filp->private_data = state; @@ -1823,6 +1801,11 @@ nfs4_proc_file_release(struct inode *ino if (state) nfs4_close_state(state, filp->f_mode); + if (filp->f_mode & FMODE_WRITE) { + lock_kernel(); + nfs_end_data_update(inode); + unlock_kernel(); + } return 0; } --- linux-2.6.4-rc2/fs/nfs/nfs4state.c 2004-02-17 20:48:46.000000000 -0800 +++ 25/fs/nfs/nfs4state.c 2004-03-07 20:47:31.000000000 -0800 @@ -790,7 +790,7 @@ reclaimer(void *ptr) restart_loop: spin_lock(&clp->cl_lock); list_for_each_entry(sp, &clp->cl_state_owners, so_list) { - if (sp->so_generation - generation <= 0) + if (sp->so_generation - generation >= 0) continue; atomic_inc(&sp->so_count); spin_unlock(&clp->cl_lock); --- linux-2.6.4-rc2/fs/nfs/nfs4xdr.c 2004-02-17 20:48:46.000000000 -0800 +++ 25/fs/nfs/nfs4xdr.c 2004-03-07 20:47:33.000000000 -0800 @@ -69,84 +69,84 @@ static int nfs_stat_to_errno(int); /* lock,open owner id: * we currently use size 1 (u32) out of (NFS4_OPAQUE_LIMIT >> 2) */ -#define owner_id_maxsz 1 + 1 -#define compound_encode_hdr_maxsz 3 + (NFS4_MAXTAGLEN >> 2) -#define compound_decode_hdr_maxsz 2 + (NFS4_MAXTAGLEN >> 2) -#define op_encode_hdr_maxsz 1 -#define op_decode_hdr_maxsz 2 -#define encode_putfh_maxsz op_encode_hdr_maxsz + 1 + \ - (NFS4_FHSIZE >> 2) -#define decode_putfh_maxsz op_decode_hdr_maxsz -#define encode_putrootfh_maxsz op_encode_hdr_maxsz -#define decode_putrootfh_maxsz op_decode_hdr_maxsz -#define encode_getfh_maxsz op_encode_hdr_maxsz -#define decode_getfh_maxsz op_decode_hdr_maxsz + 1 + \ - (NFS4_FHSIZE >> 2) -#define encode_getattr_maxsz op_encode_hdr_maxsz + 3 -#define nfs4_fattr_bitmap_maxsz 26 + 2 * ((NFS4_MAXNAMLEN +1) >> 2) -#define decode_getattr_maxsz op_decode_hdr_maxsz + 3 + \ - nfs4_fattr_bitmap_maxsz -#define encode_savefh_maxsz op_encode_hdr_maxsz -#define decode_savefh_maxsz op_decode_hdr_maxsz -#define encode_restorefh_maxsz op_encode_hdr_maxsz -#define decode_restorefh_maxsz op_decode_hdr_maxsz -#define encode_read_getattr_maxsz op_encode_hdr_maxsz + 2 -#define decode_read_getattr_maxsz op_decode_hdr_maxsz + 8 -#define encode_pre_write_getattr_maxsz op_encode_hdr_maxsz + 2 -#define decode_pre_write_getattr_maxsz op_decode_hdr_maxsz + 5 -#define encode_post_write_getattr_maxsz op_encode_hdr_maxsz + 2 -#define decode_post_write_getattr_maxsz op_decode_hdr_maxsz + 13 -#define encode_fsinfo_maxsz op_encode_hdr_maxsz + 2 -#define decode_fsinfo_maxsz op_decode_hdr_maxsz + 11 -#define encode_renew_maxsz op_encode_hdr_maxsz + 3 -#define decode_renew_maxsz op_decode_hdr_maxsz +#define owner_id_maxsz (1 + 1) +#define compound_encode_hdr_maxsz (3 + (NFS4_MAXTAGLEN >> 2)) +#define compound_decode_hdr_maxsz (3 + (NFS4_MAXTAGLEN >> 2)) +#define op_encode_hdr_maxsz (1) +#define op_decode_hdr_maxsz (2) +#define encode_putfh_maxsz (op_encode_hdr_maxsz + 1 + \ + (NFS4_FHSIZE >> 2)) +#define decode_putfh_maxsz (op_decode_hdr_maxsz) +#define encode_putrootfh_maxsz (op_encode_hdr_maxsz) +#define decode_putrootfh_maxsz (op_decode_hdr_maxsz) +#define encode_getfh_maxsz (op_encode_hdr_maxsz) +#define decode_getfh_maxsz (op_decode_hdr_maxsz + 1 + \ + (NFS4_FHSIZE >> 2)) +#define encode_getattr_maxsz (op_encode_hdr_maxsz + 3) +#define nfs4_fattr_bitmap_maxsz (26 + 2 * ((NFS4_MAXNAMLEN +1) >> 2)) +#define decode_getattr_maxsz (op_decode_hdr_maxsz + 3 + \ + nfs4_fattr_bitmap_maxsz) +#define encode_savefh_maxsz (op_encode_hdr_maxsz) +#define decode_savefh_maxsz (op_decode_hdr_maxsz) +#define encode_restorefh_maxsz (op_encode_hdr_maxsz) +#define decode_restorefh_maxsz (op_decode_hdr_maxsz) +#define encode_read_getattr_maxsz (op_encode_hdr_maxsz + 2) +#define decode_read_getattr_maxsz (op_decode_hdr_maxsz + 8) +#define encode_pre_write_getattr_maxsz (op_encode_hdr_maxsz + 2) +#define decode_pre_write_getattr_maxsz (op_decode_hdr_maxsz + 5) +#define encode_post_write_getattr_maxsz (op_encode_hdr_maxsz + 2) +#define decode_post_write_getattr_maxsz (op_decode_hdr_maxsz + 13) +#define encode_fsinfo_maxsz (op_encode_hdr_maxsz + 2) +#define decode_fsinfo_maxsz (op_decode_hdr_maxsz + 11) +#define encode_renew_maxsz (op_encode_hdr_maxsz + 3) +#define decode_renew_maxsz (op_decode_hdr_maxsz) #define encode_setclientid_maxsz \ - op_encode_hdr_maxsz + \ + (op_encode_hdr_maxsz + \ 4 /*server->ip_addr*/ + \ 1 /*Netid*/ + \ 6 /*uaddr*/ + \ - 6 + (NFS4_VERIFIER_SIZE >> 2) + 6 + (NFS4_VERIFIER_SIZE >> 2)) #define decode_setclientid_maxsz \ - op_decode_hdr_maxsz + \ + (op_decode_hdr_maxsz + \ 2 + \ - 1024 /* large value for CLID_INUSE */ + 1024) /* large value for CLID_INUSE */ #define encode_setclientid_confirm_maxsz \ - op_encode_hdr_maxsz + \ - 3 + (NFS4_VERIFIER_SIZE >> 2) + (op_encode_hdr_maxsz + \ + 3 + (NFS4_VERIFIER_SIZE >> 2)) #define decode_setclientid_confirm_maxsz \ - op_decode_hdr_maxsz + (op_decode_hdr_maxsz) -#define NFS4_enc_compound_sz 1024 /* XXX: large enough? */ -#define NFS4_dec_compound_sz 1024 /* XXX: large enough? */ -#define NFS4_enc_read_sz compound_encode_hdr_maxsz + \ +#define NFS4_enc_compound_sz (1024) /* XXX: large enough? */ +#define NFS4_dec_compound_sz (1024) /* XXX: large enough? */ +#define NFS4_enc_read_sz (compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ encode_read_getattr_maxsz + \ - op_encode_hdr_maxsz + 7 -#define NFS4_dec_read_sz compound_decode_hdr_maxsz + \ + op_encode_hdr_maxsz + 7) +#define NFS4_dec_read_sz (compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ decode_read_getattr_maxsz + \ - op_decode_hdr_maxsz + 2 -#define NFS4_enc_write_sz compound_encode_hdr_maxsz + \ + op_decode_hdr_maxsz + 2) +#define NFS4_enc_write_sz (compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ encode_pre_write_getattr_maxsz + \ op_encode_hdr_maxsz + 8 + \ - encode_post_write_getattr_maxsz -#define NFS4_dec_write_sz compound_decode_hdr_maxsz + \ + encode_post_write_getattr_maxsz) +#define NFS4_dec_write_sz (compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ decode_pre_write_getattr_maxsz + \ op_decode_hdr_maxsz + 4 + \ - decode_post_write_getattr_maxsz -#define NFS4_enc_commit_sz compound_encode_hdr_maxsz + \ + decode_post_write_getattr_maxsz) +#define NFS4_enc_commit_sz (compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ encode_pre_write_getattr_maxsz + \ op_encode_hdr_maxsz + 3 + \ - encode_post_write_getattr_maxsz -#define NFS4_dec_commit_sz compound_decode_hdr_maxsz + \ + encode_post_write_getattr_maxsz) +#define NFS4_dec_commit_sz (compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ decode_pre_write_getattr_maxsz + \ op_decode_hdr_maxsz + 2 + \ - decode_post_write_getattr_maxsz -#define NFS4_enc_open_sz compound_encode_hdr_maxsz + \ + decode_post_write_getattr_maxsz) +#define NFS4_enc_open_sz (compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ encode_savefh_maxsz + \ op_encode_hdr_maxsz + \ @@ -154,107 +154,107 @@ static int nfs_stat_to_errno(int); encode_getattr_maxsz + \ encode_getfh_maxsz + \ encode_restorefh_maxsz + \ - encode_getattr_maxsz -#define NFS4_dec_open_sz compound_decode_hdr_maxsz + \ + encode_getattr_maxsz) +#define NFS4_dec_open_sz (compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ decode_savefh_maxsz + \ op_decode_hdr_maxsz + 4 + 5 + 2 + 3 + \ decode_getattr_maxsz + \ decode_getfh_maxsz + \ decode_restorefh_maxsz + \ - decode_getattr_maxsz + decode_getattr_maxsz) #define NFS4_enc_open_confirm_sz \ - compound_encode_hdr_maxsz + \ + (compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ - op_encode_hdr_maxsz + 5 -#define NFS4_dec_open_confirm_sz compound_decode_hdr_maxsz + \ + op_encode_hdr_maxsz + 5) +#define NFS4_dec_open_confirm_sz (compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ - op_decode_hdr_maxsz + 4 -#define NFS4_enc_open_reclaim_sz compound_encode_hdr_maxsz + \ + op_decode_hdr_maxsz + 4) +#define NFS4_enc_open_reclaim_sz (compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ op_encode_hdr_maxsz + \ 11 + \ - encode_getattr_maxsz -#define NFS4_dec_open_reclaim_sz compound_decode_hdr_maxsz + \ + encode_getattr_maxsz) +#define NFS4_dec_open_reclaim_sz (compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ op_decode_hdr_maxsz + \ 4 + 5 + 2 + 3 + \ - decode_getattr_maxsz + decode_getattr_maxsz) #define NFS4_enc_open_downgrade_sz \ - compound_encode_hdr_maxsz + \ + (compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ - op_encode_hdr_maxsz + 7 + op_encode_hdr_maxsz + 7) #define NFS4_dec_open_downgrade_sz \ - compound_decode_hdr_maxsz + \ + (compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ - op_decode_hdr_maxsz + 4 -#define NFS4_enc_close_sz compound_encode_hdr_maxsz + \ + op_decode_hdr_maxsz + 4) +#define NFS4_enc_close_sz (compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ - op_encode_hdr_maxsz + 5 -#define NFS4_dec_close_sz compound_decode_hdr_maxsz + \ + op_encode_hdr_maxsz + 5) +#define NFS4_dec_close_sz (compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ - op_decode_hdr_maxsz + 4 -#define NFS4_enc_setattr_sz compound_encode_hdr_maxsz + \ + op_decode_hdr_maxsz + 4) +#define NFS4_enc_setattr_sz (compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ op_encode_hdr_maxsz + 4 + \ nfs4_fattr_bitmap_maxsz + \ - encode_getattr_maxsz -#define NFS4_dec_setattr_sz compound_decode_hdr_maxsz + \ + encode_getattr_maxsz) +#define NFS4_dec_setattr_sz (compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ - op_decode_hdr_maxsz + 3 -#define NFS4_enc_fsinfo_sz compound_encode_hdr_maxsz + \ + op_decode_hdr_maxsz + 3) +#define NFS4_enc_fsinfo_sz (compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ - encode_fsinfo_maxsz -#define NFS4_dec_fsinfo_sz compound_decode_hdr_maxsz + \ + encode_fsinfo_maxsz) +#define NFS4_dec_fsinfo_sz (compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ - decode_fsinfo_maxsz -#define NFS4_enc_renew_sz compound_encode_hdr_maxsz + \ - encode_renew_maxsz -#define NFS4_dec_renew_sz compound_decode_hdr_maxsz + \ - decode_renew_maxsz -#define NFS4_enc_setclientid_sz compound_encode_hdr_maxsz + \ - encode_setclientid_maxsz -#define NFS4_dec_setclientid_sz compound_decode_hdr_maxsz + \ - decode_setclientid_maxsz + decode_fsinfo_maxsz) +#define NFS4_enc_renew_sz (compound_encode_hdr_maxsz + \ + encode_renew_maxsz) +#define NFS4_dec_renew_sz (compound_decode_hdr_maxsz + \ + decode_renew_maxsz) +#define NFS4_enc_setclientid_sz (compound_encode_hdr_maxsz + \ + encode_setclientid_maxsz) +#define NFS4_dec_setclientid_sz (compound_decode_hdr_maxsz + \ + decode_setclientid_maxsz) #define NFS4_enc_setclientid_confirm_sz \ - compound_encode_hdr_maxsz + \ + (compound_encode_hdr_maxsz + \ encode_setclientid_confirm_maxsz + \ encode_putrootfh_maxsz + \ - encode_fsinfo_maxsz + encode_fsinfo_maxsz) #define NFS4_dec_setclientid_confirm_sz \ - compound_decode_hdr_maxsz + \ + (compound_decode_hdr_maxsz + \ decode_setclientid_confirm_maxsz + \ decode_putrootfh_maxsz + \ - decode_fsinfo_maxsz -#define NFS4_enc_lock_sz compound_encode_hdr_maxsz + \ + decode_fsinfo_maxsz) +#define NFS4_enc_lock_sz (compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ encode_getattr_maxsz + \ op_encode_hdr_maxsz + \ 1 + 1 + 2 + 2 + \ 1 + 4 + 1 + 2 + \ - owner_id_maxsz -#define NFS4_dec_lock_sz compound_decode_hdr_maxsz + \ + owner_id_maxsz) +#define NFS4_dec_lock_sz (compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ decode_getattr_maxsz + \ op_decode_hdr_maxsz + \ 2 + 2 + 1 + 2 + \ - owner_id_maxsz -#define NFS4_enc_lockt_sz compound_encode_hdr_maxsz + \ + owner_id_maxsz) +#define NFS4_enc_lockt_sz (compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ encode_getattr_maxsz + \ op_encode_hdr_maxsz + \ 1 + 2 + 2 + 2 + \ - owner_id_maxsz -#define NFS4_dec_lockt_sz NFS4_dec_lock_sz -#define NFS4_enc_locku_sz compound_encode_hdr_maxsz + \ + owner_id_maxsz) +#define NFS4_dec_lockt_sz (NFS4_dec_lock_sz) +#define NFS4_enc_locku_sz (compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ encode_getattr_maxsz + \ op_encode_hdr_maxsz + \ - 1 + 1 + 4 + 2 + 2 -#define NFS4_dec_locku_sz compound_decode_hdr_maxsz + \ + 1 + 1 + 4 + 2 + 2) +#define NFS4_dec_locku_sz (compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ decode_getattr_maxsz + \ - op_decode_hdr_maxsz + 4 + op_decode_hdr_maxsz + 4) @@ -324,7 +324,7 @@ encode_compound_hdr(struct xdr_stream *x dprintk("encode_compound: tag=%.*s\n", (int)hdr->taglen, hdr->tag); BUG_ON(hdr->taglen > NFS4_MAXTAGLEN); - RESERVE_SPACE(12+XDR_QUADLEN(hdr->taglen)); + RESERVE_SPACE(12+(XDR_QUADLEN(hdr->taglen)<<2)); WRITE32(hdr->taglen); WRITEMEM(hdr->tag, hdr->taglen); WRITE32(NFS4_MINOR_VERSION); @@ -3165,6 +3165,10 @@ static struct { { NFS4ERR_SYMLINK, ELOOP }, { NFS4ERR_OP_ILLEGAL, EOPNOTSUPP }, { NFS4ERR_DEADLOCK, EDEADLK }, + { NFS4ERR_WRONGSEC, EPERM }, /* FIXME: this needs + * to be handled by a + * middle-layer. + */ { -1, EIO } }; --- linux-2.6.4-rc2/fs/nfs/nfsroot.c 2003-08-22 19:23:42.000000000 -0700 +++ 25/fs/nfs/nfsroot.c 2004-03-07 20:47:34.000000000 -0800 @@ -166,37 +166,6 @@ static struct nfs_bool_opts { /* - * Extract IP address from the parameter string if needed. Note that we - * need to have root_server_addr set _before_ IPConfig gets called as it - * can override it. - */ -static void __init root_nfs_parse_addr(char *name) -{ - int octets = 0; - char *cp, *cq; - - cp = cq = name; - while (octets < 4) { - while (*cp >= '0' && *cp <= '9') - cp++; - if (cp == cq || cp - cq > 3) - break; - if (*cp == '.' || octets == 3) - octets++; - if (octets < 4) - cp++; - cq = cp; - } - if (octets == 4 && (*cp == ':' || *cp == '\0')) { - if (*cp == ':') - *cp++ = '\0'; - root_server_addr = in_aton(name); - strcpy(name, cp); - } -} - - -/* * Parse option string. */ static void __init root_nfs_parse(char *name, char *buf) @@ -345,7 +314,7 @@ int __init nfs_root_setup(char *line) line[sizeof(nfs_root_name) - strlen(NFS_ROOT) - 1] = '\0'; sprintf(nfs_root_name, NFS_ROOT, line); } - root_nfs_parse_addr(nfs_root_name); + root_server_addr = root_nfs_parse_addr(nfs_root_name); return 1; } --- linux-2.6.4-rc2/fs/nfs/pagelist.c 2003-10-17 15:58:04.000000000 -0700 +++ 25/fs/nfs/pagelist.c 2004-03-07 20:47:30.000000000 -0800 @@ -246,7 +246,6 @@ nfs_coalesce_requests(struct list_head * * nfs_scan_list - Scan a list for matching requests * @head: One of the NFS inode request lists * @dst: Destination list - * @file: if set, ensure we match requests from this file * @idx_start: lower bound of page->index to scan * @npages: idx_start + npages sets the upper bound to scan. * @@ -258,7 +257,6 @@ nfs_coalesce_requests(struct list_head * */ int nfs_scan_list(struct list_head *head, struct list_head *dst, - struct file *file, unsigned long idx_start, unsigned int npages) { struct list_head *pos, *tmp; @@ -276,9 +274,6 @@ nfs_scan_list(struct list_head *head, st req = nfs_list_entry(pos); - if (file && req->wb_file != file) - continue; - if (req->wb_index < idx_start) continue; if (req->wb_index > idx_end) --- linux-2.6.4-rc2/fs/nfs/proc.c 2004-02-17 20:48:46.000000000 -0800 +++ 25/fs/nfs/proc.c 2004-03-07 20:47:34.000000000 -0800 @@ -49,18 +49,6 @@ extern struct rpc_procinfo nfs_procedures[]; -static void -nfs_write_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) -{ - if (!(fattr->valid & NFS_ATTR_WCC)) { - fattr->pre_size = NFS_CACHE_ISIZE(inode); - fattr->pre_mtime = NFS_CACHE_MTIME(inode); - fattr->pre_ctime = NFS_CACHE_CTIME(inode); - fattr->valid |= NFS_ATTR_WCC; - } - nfs_refresh_inode(inode, fattr); -} - static struct rpc_cred * nfs_cred(struct inode *inode, struct file *filp) { @@ -78,15 +66,33 @@ nfs_cred(struct inode *inode, struct fil */ static int nfs_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_fattr *fattr) + struct nfs_fsinfo *info) { - int status; + struct nfs_fattr *fattr = info->fattr; + struct nfs2_fsstat fsinfo; + int status; - dprintk("NFS call getroot\n"); + dprintk("%s: call getattr\n", __FUNCTION__); fattr->valid = 0; - status = rpc_call(server->client, NFSPROC_GETATTR, fhandle, fattr, 0); - dprintk("NFS reply getroot\n"); - return status; + status = rpc_call(server->client_sys, NFSPROC_GETATTR, fhandle, fattr, 0); + dprintk("%s: reply getattr %d\n", __FUNCTION__, status); + if (status) + return status; + dprintk("%s: call statfs\n", __FUNCTION__); + status = rpc_call(server->client_sys, NFSPROC_STATFS, fhandle, &fsinfo, 0); + dprintk("%s: reply statfs %d\n", __FUNCTION__, status); + if (status) + return status; + info->rtmax = NFS_MAXDATA; + info->rtpref = fsinfo.tsize; + info->rtmult = fsinfo.bsize; + info->wtmax = NFS_MAXDATA; + info->wtpref = fsinfo.tsize; + info->wtmult = fsinfo.bsize; + info->dtpref = fsinfo.tsize; + info->maxfilesize = 0x7FFFFFFF; + info->lease_time = 0; + return 0; } /* @@ -180,8 +186,14 @@ nfs_proc_read(struct nfs_read_data *rdat msg.rpc_cred = nfs_cred(inode, filp); status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags); - if (status >= 0) + if (status >= 0) { nfs_refresh_inode(inode, fattr); + /* Emulate the eof flag, which isn't normally needed in NFSv2 + * as it is guaranteed to always return the file attributes + */ + if (rdata->args.offset + rdata->args.count >= fattr->size) + rdata->res.eof = 1; + } dprintk("NFS reply read: %d\n", status); return status; } @@ -205,7 +217,7 @@ nfs_proc_write(struct nfs_write_data *wd msg.rpc_cred = nfs_cred(inode, filp); status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags); if (status >= 0) { - nfs_write_refresh_inode(inode, fattr); + nfs_refresh_inode(inode, fattr); wdata->res.count = wdata->args.count; wdata->verf.committed = NFS_FILE_SYNC; } @@ -331,10 +343,8 @@ nfs_proc_unlink_done(struct dentry *dir, { struct rpc_message *msg = &task->tk_msg; - if (msg->rpc_argp) { - NFS_CACHEINV(dir->d_inode); + if (msg->rpc_argp) kfree(msg->rpc_argp); - } return 0; } @@ -537,8 +547,14 @@ nfs_read_done(struct rpc_task *task) { struct nfs_read_data *data = (struct nfs_read_data *) task->tk_calldata; - if (task->tk_status >= 0) + if (task->tk_status >= 0) { nfs_refresh_inode(data->inode, data->res.fattr); + /* Emulate the eof flag, which isn't normally needed in NFSv2 + * as it is guaranteed to always return the file attributes + */ + if (data->args.offset + data->args.count >= data->res.fattr->size) + data->res.eof = 1; + } nfs_readpage_result(task); } @@ -584,7 +600,7 @@ nfs_write_done(struct rpc_task *task) struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata; if (task->tk_status >= 0) - nfs_write_refresh_inode(data->inode, data->res.fattr); + nfs_refresh_inode(data->inode, data->res.fattr); nfs_writeback_done(task); } --- linux-2.6.4-rc2/fs/nfs/read.c 2003-10-08 15:07:10.000000000 -0700 +++ 25/fs/nfs/read.c 2004-03-07 20:47:34.000000000 -0800 @@ -121,9 +121,13 @@ nfs_readpage_sync(struct file *file, str } count -= result; rdata.args.pgbase += result; - if (result < rdata.args.count) /* NFSv2ism */ + /* Note: result == 0 should only happen if we're caching + * a write that extends the file and punches a hole. + */ + if (rdata.res.eof != 0 || result == 0) break; } while (count); + NFS_FLAGS(inode) |= NFS_INO_INVALID_ATIME; if (count) memclear_highpage_flush(page, rdata.args.pgbase, count); @@ -266,6 +270,7 @@ nfs_readpage_result(struct rpc_task *tas dprintk("NFS: %4d nfs_readpage_result, (status %d)\n", task->tk_pid, task->tk_status); + NFS_FLAGS(data->inode) |= NFS_INO_INVALID_ATIME; while (!list_empty(&data->pages)) { struct nfs_page *req = nfs_list_entry(data->pages.next); struct page *page = req->wb_page; --- linux-2.6.4-rc2/fs/nfs/unlink.c 2003-06-14 12:18:34.000000000 -0700 +++ 25/fs/nfs/unlink.c 2004-03-07 20:47:26.000000000 -0800 @@ -104,6 +104,7 @@ nfs_async_unlink_init(struct rpc_task *t status = NFS_PROTO(dir->d_inode)->unlink_setup(&msg, dir, &data->name); if (status < 0) goto out_err; + nfs_begin_data_update(dir->d_inode); rpc_call_setup(task, &msg, 0); return; out_err: @@ -126,7 +127,7 @@ nfs_async_unlink_done(struct rpc_task *t if (!dir) return; dir_i = dir->d_inode; - nfs_zap_caches(dir_i); + nfs_end_data_update(dir_i); if (NFS_PROTO(dir_i)->unlink_done(dir, task)) return; put_rpccred(data->cred); --- linux-2.6.4-rc2/fs/nfs/write.c 2004-02-03 20:42:38.000000000 -0800 +++ 25/fs/nfs/write.c 2004-03-07 20:47:30.000000000 -0800 @@ -74,7 +74,6 @@ static struct nfs_page * nfs_update_request(struct file*, struct inode *, struct page *, unsigned int, unsigned int); -static void nfs_strategy(struct inode *inode); static kmem_cache_t *nfs_wdata_cachep; static mempool_t *nfs_wdata_mempool; @@ -124,6 +123,52 @@ void nfs_commit_release(struct rpc_task nfs_commit_free(wdata); } +/* Adjust the file length if we're writing beyond the end */ +static void nfs_grow_file(struct page *page, unsigned int offset, unsigned int count) +{ + struct inode *inode = page->mapping->host; + loff_t end, i_size = i_size_read(inode); + unsigned long end_index = (i_size - 1) >> PAGE_CACHE_SHIFT; + + if (i_size > 0 && page->index < end_index) + return; + end = ((loff_t)page->index << PAGE_CACHE_SHIFT) + ((loff_t)offset+count); + if (i_size >= end) + return; + i_size_write(inode, end); +} + +/* We can set the PG_uptodate flag if we see that a write request + * covers the full page. + */ +static void nfs_mark_uptodate(struct page *page, unsigned int base, unsigned int count) +{ + loff_t end_offs; + + if (PageUptodate(page)) + return; + if (base != 0) + return; + if (count == PAGE_CACHE_SIZE) { + SetPageUptodate(page); + return; + } + + end_offs = i_size_read(page->mapping->host) - 1; + if (end_offs < 0) + return; + /* Is this the last page? */ + if (page->index != (unsigned long)(end_offs >> PAGE_CACHE_SHIFT)) + return; + /* This is the last page: set PG_uptodate if we cover the entire + * extent of the data, then zero the rest of the page. + */ + if (count == (unsigned int)(end_offs & (PAGE_CACHE_SIZE - 1)) + 1) { + memclear_highpage_flush(page, count, PAGE_CACHE_SIZE - count); + SetPageUptodate(page); + } +} + /* * Write a page synchronously. * Offset is the data offset within the page. @@ -157,6 +202,7 @@ nfs_writepage_sync(struct file *file, st (long long)NFS_FILEID(inode), count, (long long)(page_offset(page) + offset)); + nfs_begin_data_update(inode); do { if (count < wsize && !swapfile) wdata.args.count = count; @@ -177,14 +223,12 @@ nfs_writepage_sync(struct file *file, st wdata.args.pgbase += result; written += result; count -= result; - - /* - * If we've extended the file, update the inode - * now so we don't invalidate the cache. - */ - if (wdata.args.offset > i_size_read(inode)) - i_size_write(inode, wdata.args.offset); } while (count); + /* Update file length */ + nfs_grow_file(page, offset, written); + /* Set the PG_uptodate flag? */ + nfs_mark_uptodate(page, offset, written); + nfs_end_data_update(inode); if (PageError(page)) ClearPageError(page); @@ -201,18 +245,19 @@ nfs_writepage_async(struct file *file, s unsigned int offset, unsigned int count) { struct nfs_page *req; - loff_t end; int status; + nfs_begin_data_update(inode); req = nfs_update_request(file, inode, page, offset, count); status = (IS_ERR(req)) ? PTR_ERR(req) : 0; if (status < 0) goto out; + /* Update file length */ + nfs_grow_file(page, offset, count); + /* Set the PG_uptodate flag? */ + nfs_mark_uptodate(page, offset, count); nfs_unlock_request(req); - nfs_strategy(inode); - end = ((loff_t)page->index<sync_mode == WB_SYNC_HOLD) @@ -294,7 +339,7 @@ nfs_writepages(struct address_space *map if (is_sync && wbc->sync_mode == WB_SYNC_ALL) { err = nfs_wb_all(inode); } else - nfs_commit_file(inode, NULL, 0, 0, 0); + nfs_commit_inode(inode, 0, 0, 0); out: return err; } @@ -312,8 +357,10 @@ nfs_inode_add_request(struct inode *inod BUG_ON(error == -EEXIST); if (error) return error; - if (!nfsi->npages) + if (!nfsi->npages) { igrab(inode); + nfs_begin_data_update(inode); + } nfsi->npages++; req->wb_count++; return 0; @@ -336,6 +383,7 @@ nfs_inode_remove_request(struct nfs_page nfsi->npages--; if (!nfsi->npages) { spin_unlock(&nfs_wreq_lock); + nfs_end_data_update(inode); iput(inode); } else spin_unlock(&nfs_wreq_lock); @@ -421,7 +469,7 @@ nfs_mark_request_commit(struct nfs_page * Interruptible by signals only if mounted with intr flag. */ static int -nfs_wait_on_requests(struct inode *inode, struct file *file, unsigned long idx_start, unsigned int npages) +nfs_wait_on_requests(struct inode *inode, unsigned long idx_start, unsigned int npages) { struct nfs_inode *nfsi = NFS_I(inode); struct nfs_page *req; @@ -441,8 +489,6 @@ nfs_wait_on_requests(struct inode *inode break; next = req->wb_index + 1; - if (file && req->wb_file != file) - continue; if (!NFS_WBACK_BUSY(req)) continue; @@ -453,7 +499,6 @@ nfs_wait_on_requests(struct inode *inode if (error < 0) return error; spin_lock(&nfs_wreq_lock); - next = idx_start; res++; } spin_unlock(&nfs_wreq_lock); @@ -464,7 +509,6 @@ nfs_wait_on_requests(struct inode *inode * nfs_scan_dirty - Scan an inode for dirty requests * @inode: NFS inode to scan * @dst: destination list - * @file: if set, ensure we match requests from this file * @idx_start: lower bound of page->index to scan. * @npages: idx_start + npages sets the upper bound to scan. * @@ -472,11 +516,11 @@ nfs_wait_on_requests(struct inode *inode * The requests are *not* checked to ensure that they form a contiguous set. */ static int -nfs_scan_dirty(struct inode *inode, struct list_head *dst, struct file *file, unsigned long idx_start, unsigned int npages) +nfs_scan_dirty(struct inode *inode, struct list_head *dst, unsigned long idx_start, unsigned int npages) { struct nfs_inode *nfsi = NFS_I(inode); int res; - res = nfs_scan_list(&nfsi->dirty, dst, file, idx_start, npages); + res = nfs_scan_list(&nfsi->dirty, dst, idx_start, npages); nfsi->ndirty -= res; sub_page_state(nr_dirty,res); if ((nfsi->ndirty == 0) != list_empty(&nfsi->dirty)) @@ -489,7 +533,6 @@ nfs_scan_dirty(struct inode *inode, stru * nfs_scan_commit - Scan an inode for commit requests * @inode: NFS inode to scan * @dst: destination list - * @file: if set, ensure we collect requests from this file only. * @idx_start: lower bound of page->index to scan. * @npages: idx_start + npages sets the upper bound to scan. * @@ -497,11 +540,11 @@ nfs_scan_dirty(struct inode *inode, stru * The requests are *not* checked to ensure that they form a contiguous set. */ static int -nfs_scan_commit(struct inode *inode, struct list_head *dst, struct file *file, unsigned long idx_start, unsigned int npages) +nfs_scan_commit(struct inode *inode, struct list_head *dst, unsigned long idx_start, unsigned int npages) { struct nfs_inode *nfsi = NFS_I(inode); int res; - res = nfs_scan_list(&nfsi->commit, dst, file, idx_start, npages); + res = nfs_scan_list(&nfsi->commit, dst, idx_start, npages); nfsi->ncommit -= res; if ((nfsi->ncommit == 0) != list_empty(&nfsi->commit)) printk(KERN_ERR "NFS: desynchronized value of nfs_i.ncommit.\n"); @@ -600,46 +643,6 @@ nfs_update_request(struct file* file, st return req; } -/* - * This is the strategy routine for NFS. - * It is called by nfs_updatepage whenever the user wrote up to the end - * of a page. - * - * We always try to submit a set of requests in parallel so that the - * server's write code can gather writes. This is mainly for the benefit - * of NFSv2. - * - * We never submit more requests than we think the remote can handle. - * For UDP sockets, we make sure we don't exceed the congestion window; - * for TCP, we limit the number of requests to 8. - * - * NFS_STRATEGY_PAGES gives the minimum number of requests for NFSv2 that - * should be sent out in one go. This is for the benefit of NFSv2 servers - * that perform write gathering. - * - * FIXME: Different servers may have different sweet spots. - * Record the average congestion window in server struct? - */ -#define NFS_STRATEGY_PAGES 8 -static void -nfs_strategy(struct inode *inode) -{ - unsigned int dirty, wpages; - - dirty = NFS_I(inode)->ndirty; - wpages = NFS_SERVER(inode)->wpages; -#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) - if (NFS_PROTO(inode)->version == 2) { - if (dirty >= NFS_STRATEGY_PAGES * wpages) - nfs_flush_file(inode, NULL, 0, 0, 0); - } else if (dirty >= wpages) - nfs_flush_file(inode, NULL, 0, 0, 0); -#else - if (dirty >= NFS_STRATEGY_PAGES * wpages) - nfs_flush_file(inode, NULL, 0, 0, 0); -#endif -} - int nfs_flush_incompatible(struct file *file, struct page *page) { @@ -675,7 +678,6 @@ nfs_updatepage(struct file *file, struct struct dentry *dentry = file->f_dentry; struct inode *inode = page->mapping->host; struct nfs_page *req; - loff_t end; int status = 0; dprintk("NFS: nfs_updatepage(%s/%s %d@%Ld)\n", @@ -696,6 +698,30 @@ nfs_updatepage(struct file *file, struct return status; } + nfs_begin_data_update(inode); + + + /* If we're not using byte range locks, and we know the page + * is entirely in cache, it may be more efficient to avoid + * fragmenting write requests. + */ + if (PageUptodate(page) && inode->i_flock == NULL) { + loff_t end_offs = i_size_read(inode) - 1; + unsigned long end_index = end_offs >> PAGE_CACHE_SHIFT; + + count += offset; + offset = 0; + if (end_offs < 0) { + /* Do nothing */ + } else if (page->index == end_index) { + unsigned int pglen; + pglen = (unsigned int)(end_offs & (PAGE_CACHE_SIZE-1)) + 1; + if (count < pglen) + count = pglen; + } else if (page->index < end_index) + count = PAGE_CACHE_SIZE; + } + /* * Try to find an NFS request corresponding to this page * and update it. @@ -714,21 +740,14 @@ nfs_updatepage(struct file *file, struct goto done; status = 0; - end = ((loff_t)page->index<wb_pgbase == 0 && req->wb_bytes == PAGE_CACHE_SIZE) { - SetPageUptodate(page); - nfs_unlock_request(req); - nfs_strategy(inode); - } else - nfs_unlock_request(req); + + /* Update file length */ + nfs_grow_file(page, offset, count); + /* Set the PG_uptodate flag? */ + nfs_mark_uptodate(page, req->wb_pgbase, req->wb_bytes); + nfs_unlock_request(req); done: + nfs_end_data_update(inode); dprintk("NFS: nfs_updatepage returns %d (isize %Ld)\n", status, (long long)i_size_read(inode)); if (status < 0) @@ -891,10 +910,7 @@ nfs_writeback_done(struct rpc_task *task #endif /* - * Update attributes as result of writeback. - * FIXME: There is an inherent race with invalidate_inode_pages and - * writebacks since the page->count is kept > 1 for as long - * as the page has a write request pending. + * Process the nfs_page list */ while (!list_empty(&data->pages)) { req = nfs_list_entry(data->pages.next); @@ -1061,7 +1077,7 @@ nfs_commit_done(struct rpc_task *task) } #endif -int nfs_flush_file(struct inode *inode, struct file *file, unsigned long idx_start, +int nfs_flush_inode(struct inode *inode, unsigned long idx_start, unsigned int npages, int how) { LIST_HEAD(head); @@ -1069,7 +1085,7 @@ int nfs_flush_file(struct inode *inode, error = 0; spin_lock(&nfs_wreq_lock); - res = nfs_scan_dirty(inode, &head, file, idx_start, npages); + res = nfs_scan_dirty(inode, &head, idx_start, npages); spin_unlock(&nfs_wreq_lock); if (res) error = nfs_flush_list(&head, NFS_SERVER(inode)->wpages, how); @@ -1079,7 +1095,7 @@ int nfs_flush_file(struct inode *inode, } #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) -int nfs_commit_file(struct inode *inode, struct file *file, unsigned long idx_start, +int nfs_commit_inode(struct inode *inode, unsigned long idx_start, unsigned int npages, int how) { LIST_HEAD(head); @@ -1087,9 +1103,9 @@ int nfs_commit_file(struct inode *inode, error = 0; spin_lock(&nfs_wreq_lock); - res = nfs_scan_commit(inode, &head, file, idx_start, npages); + res = nfs_scan_commit(inode, &head, idx_start, npages); if (res) { - res += nfs_scan_commit(inode, &head, NULL, 0, 0); + res += nfs_scan_commit(inode, &head, 0, 0); spin_unlock(&nfs_wreq_lock); error = nfs_commit_list(&head, how); } else @@ -1100,7 +1116,7 @@ int nfs_commit_file(struct inode *inode, } #endif -int nfs_sync_file(struct inode *inode, struct file *file, unsigned long idx_start, +int nfs_sync_inode(struct inode *inode, unsigned long idx_start, unsigned int npages, int how) { int error, @@ -1109,18 +1125,15 @@ int nfs_sync_file(struct inode *inode, s wait = how & FLUSH_WAIT; how &= ~FLUSH_WAIT; - if (!inode && file) - inode = file->f_dentry->d_inode; - do { error = 0; if (wait) - error = nfs_wait_on_requests(inode, file, idx_start, npages); + error = nfs_wait_on_requests(inode, idx_start, npages); if (error == 0) - error = nfs_flush_file(inode, file, idx_start, npages, how); + error = nfs_flush_inode(inode, idx_start, npages, how); #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) if (error == 0) - error = nfs_commit_file(inode, file, idx_start, npages, how); + error = nfs_commit_inode(inode, idx_start, npages, how); #endif } while (error > 0); return error; --- linux-2.6.4-rc2/fs/open.c 2004-02-03 20:42:38.000000000 -0800 +++ 25/fs/open.c 2004-03-07 20:48:22.000000000 -0800 @@ -192,7 +192,9 @@ int do_truncate(struct dentry *dentry, l newattrs.ia_size = length; newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME; down(&dentry->d_inode->i_sem); + down_write(&dentry->d_inode->i_alloc_sem); err = notify_change(dentry, &newattrs); + up_write(&dentry->d_inode->i_alloc_sem); up(&dentry->d_inode->i_sem); return err; } @@ -890,7 +892,7 @@ static inline void __put_unused_fd(struc files->next_fd = fd; } -void put_unused_fd(unsigned int fd) +void fastcall put_unused_fd(unsigned int fd) { struct files_struct *files = current->files; spin_lock(&files->file_lock); @@ -913,7 +915,7 @@ EXPORT_SYMBOL(put_unused_fd); * will follow. */ -void fd_install(unsigned int fd, struct file * file) +void fastcall fd_install(unsigned int fd, struct file * file) { struct files_struct *files = current->files; spin_lock(&files->file_lock); --- linux-2.6.4-rc2/fs/proc/generic.c 2004-01-09 00:04:32.000000000 -0800 +++ 25/fs/proc/generic.c 2004-03-07 20:48:07.000000000 -0800 @@ -661,6 +661,7 @@ void remove_proc_entry(const char *name, proc_alloc_map); proc_kill_inodes(de); de->nlink = 0; + BUG_ON(de->subdir); if (!atomic_read(&de->count)) free_proc_entry(de); else { --- linux-2.6.4-rc2/fs/proc/inode.c 2003-10-08 15:07:10.000000000 -0700 +++ 25/fs/proc/inode.c 2004-03-07 20:47:08.000000000 -0800 @@ -231,6 +231,7 @@ int proc_fill_super(struct super_block * { struct inode * root_inode; + s->s_flags |= MS_NODIRATIME; s->s_blocksize = 1024; s->s_blocksize_bits = 10; s->s_magic = PROC_SUPER_MAGIC; --- linux-2.6.4-rc2/fs/proc/kmsg.c 2003-09-27 18:57:46.000000000 -0700 +++ 25/fs/proc/kmsg.c 2004-03-07 20:47:44.000000000 -0800 @@ -33,6 +33,8 @@ static int kmsg_release(struct inode * i static ssize_t kmsg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { + if ((file->f_flags & O_NONBLOCK) && !do_syslog(9, 0, 0)) + return -EAGAIN; return do_syslog(2, buf, count); } --- linux-2.6.4-rc2/fs/proc/proc_misc.c 2004-02-17 20:48:46.000000000 -0800 +++ 25/fs/proc/proc_misc.c 2004-03-07 20:48:17.000000000 -0800 @@ -389,7 +389,7 @@ int show_stat(struct seq_file *p, void * jiffies_to_clock_t(iowait), jiffies_to_clock_t(irq), jiffies_to_clock_t(softirq)); - for_each_online_cpu(i) { + for_each_cpu(i) { seq_printf(p, "cpu%d %u %u %u %u %u %u %u\n", i, jiffies_to_clock_t(kstat_cpu(i).cpustat.user), @@ -424,7 +424,7 @@ int show_stat(struct seq_file *p, void * static int stat_open(struct inode *inode, struct file *file) { - unsigned size = 4096 * (1 + num_online_cpus() / 32); + unsigned size = 4096 * (1 + num_possible_cpus() / 32); char *buf; struct seq_file *m; int res; @@ -641,6 +641,36 @@ static void create_seq_entry(char *name, entry->proc_fops = f; } +#ifdef CONFIG_LOCKMETER +extern ssize_t get_lockmeter_info(char *, size_t, loff_t *); +extern ssize_t put_lockmeter_info(const char *, size_t); +extern int get_lockmeter_info_size(void); + +/* + * This function accesses lock metering information. + */ +static ssize_t read_lockmeter(struct file *file, char *buf, + size_t count, loff_t *ppos) +{ + return get_lockmeter_info(buf, count, ppos); +} + +/* + * Writing to /proc/lockmeter resets the counters + */ +static ssize_t write_lockmeter(struct file * file, const char * buf, + size_t count, loff_t *ppos) +{ + return put_lockmeter_info(buf, count); +} + +static struct file_operations proc_lockmeter_operations = { + NULL, /* lseek */ + read: read_lockmeter, + write: write_lockmeter, +}; +#endif /* CONFIG_LOCKMETER */ + void __init proc_misc_init(void) { struct proc_dir_entry *entry; @@ -708,6 +738,13 @@ void __init proc_misc_init(void) if (entry) entry->proc_fops = &proc_sysrq_trigger_operations; #endif +#ifdef CONFIG_LOCKMETER + entry = create_proc_entry("lockmeter", S_IWUSR | S_IRUGO, NULL); + if (entry) { + entry->proc_fops = &proc_lockmeter_operations; + entry->size = get_lockmeter_info_size(); + } +#endif #ifdef CONFIG_PPC32 { extern struct file_operations ppc_htab_operations; --- linux-2.6.4-rc2/fs/proc/task_mmu.c 2004-02-03 20:42:38.000000000 -0800 +++ 25/fs/proc/task_mmu.c 2004-03-07 20:47:37.000000000 -0800 @@ -4,6 +4,22 @@ #include #include +#ifdef CONFIG_NUMA +char *task_mem_pernode(struct mm_struct *mm, char *buffer) +{ + int nid; + + for (nid = 0; nid < MAX_NUMNODES; nid++){ + buffer += sprintf(buffer, "VmRSS-node_%d:\t%8lu kb\n", + nid, mm->pernode_rss[nid] << (PAGE_SHIFT-10)); + } + + return buffer; +} +#else /* !CONFIG_NUMA */ +#define task_mem_pernode(mm, buffer) (buffer) +#endif /* CONFIG_NUMA */ + char *task_mem(struct mm_struct *mm, char *buffer) { unsigned long data = 0, stack = 0, exec = 0, lib = 0; @@ -40,6 +56,7 @@ char *task_mem(struct mm_struct *mm, cha mm->rss << (PAGE_SHIFT-10), data - stack, stack, exec - lib, lib); + buffer = task_mem_pernode(mm, buffer); up_read(&mm->mmap_sem); return buffer; } --- linux-2.6.4-rc2/fs/qnx4/dir.c 2003-06-14 12:18:24.000000000 -0700 +++ 25/fs/qnx4/dir.c 2004-03-07 20:47:08.000000000 -0800 @@ -76,8 +76,6 @@ static int qnx4_readdir(struct file *fil } brelse(bh); } - update_atime(inode); - out: unlock_kernel(); return 0; --- linux-2.6.4-rc2/fs/quota_v1.c 2003-10-08 15:07:10.000000000 -0700 +++ 25/fs/quota_v1.c 2004-03-07 20:47:09.000000000 -0800 @@ -60,7 +60,7 @@ static int v1_read_dqblk(struct dquot *d v1_disk2mem_dqblk(&dquot->dq_dqb, &dqblk); if (dquot->dq_dqb.dqb_bhardlimit == 0 && dquot->dq_dqb.dqb_bsoftlimit == 0 && dquot->dq_dqb.dqb_ihardlimit == 0 && dquot->dq_dqb.dqb_isoftlimit == 0) - dquot->dq_flags |= DQ_FAKE; + set_bit(DQ_FAKE_B, &dquot->dq_flags); dqstats.reads++; return 0; @@ -80,12 +80,7 @@ static int v1_commit_dqblk(struct dquot fs = get_fs(); set_fs(KERNEL_DS); - /* - * Note: clear the DQ_MOD flag unconditionally, - * so we don't loop forever on failure. - */ v1_mem2disk_dqblk(&dqblk, &dquot->dq_dqb); - dquot->dq_flags &= ~DQ_MOD; if (dquot->dq_id == 0) { dqblk.dqb_btime = sb_dqopt(dquot->dq_sb)->info[type].dqi_bgrace; dqblk.dqb_itime = sb_dqopt(dquot->dq_sb)->info[type].dqi_igrace; --- linux-2.6.4-rc2/fs/quota_v2.c 2003-06-14 12:18:30.000000000 -0700 +++ 25/fs/quota_v2.c 2004-03-07 20:47:09.000000000 -0800 @@ -65,7 +65,7 @@ static int v2_read_file_info(struct supe set_fs(fs); if (size != sizeof(struct v2_disk_dqinfo)) { printk(KERN_WARNING "Can't read info structure on device %s.\n", - f->f_vfsmnt->mnt_sb->s_id); + f->f_dentry->d_sb->s_id); return -1; } info->dqi_bgrace = le32_to_cpu(dinfo.dqi_bgrace); @@ -87,10 +87,12 @@ static int v2_write_file_info(struct sup ssize_t size; loff_t offset = V2_DQINFOOFF; + spin_lock(&dq_data_lock); info->dqi_flags &= ~DQF_INFO_DIRTY; dinfo.dqi_bgrace = cpu_to_le32(info->dqi_bgrace); dinfo.dqi_igrace = cpu_to_le32(info->dqi_igrace); dinfo.dqi_flags = cpu_to_le32(info->dqi_flags & DQF_MASK); + spin_unlock(&dq_data_lock); dinfo.dqi_blocks = cpu_to_le32(info->u.v2_i.dqi_blocks); dinfo.dqi_free_blk = cpu_to_le32(info->u.v2_i.dqi_free_blk); dinfo.dqi_free_entry = cpu_to_le32(info->u.v2_i.dqi_free_entry); @@ -100,7 +102,7 @@ static int v2_write_file_info(struct sup set_fs(fs); if (size != sizeof(struct v2_disk_dqinfo)) { printk(KERN_WARNING "Can't write info structure on device %s.\n", - f->f_vfsmnt->mnt_sb->s_id); + f->f_dentry->d_sb->s_id); return -1; } return 0; @@ -173,9 +175,10 @@ static ssize_t write_blk(struct file *fi } /* Remove empty block from list and return it */ -static int get_free_dqblk(struct file *filp, struct mem_dqinfo *info) +static int get_free_dqblk(struct file *filp, int type) { dqbuf_t buf = getdqbuf(); + struct mem_dqinfo *info = sb_dqinfo(filp->f_dentry->d_sb, type); struct v2_disk_dqdbheader *dh = (struct v2_disk_dqdbheader *)buf; int ret, blk; @@ -193,7 +196,7 @@ static int get_free_dqblk(struct file *f goto out_buf; blk = info->u.v2_i.dqi_blocks++; } - mark_info_dirty(info); + mark_info_dirty(filp->f_dentry->d_sb, type); ret = blk; out_buf: freedqbuf(buf); @@ -201,8 +204,9 @@ out_buf: } /* Insert empty block to the list */ -static int put_free_dqblk(struct file *filp, struct mem_dqinfo *info, dqbuf_t buf, uint blk) +static int put_free_dqblk(struct file *filp, int type, dqbuf_t buf, uint blk) { + struct mem_dqinfo *info = sb_dqinfo(filp->f_dentry->d_sb, type); struct v2_disk_dqdbheader *dh = (struct v2_disk_dqdbheader *)buf; int err; @@ -210,16 +214,17 @@ static int put_free_dqblk(struct file *f dh->dqdh_prev_free = cpu_to_le32(0); dh->dqdh_entries = cpu_to_le16(0); info->u.v2_i.dqi_free_blk = blk; - mark_info_dirty(info); + mark_info_dirty(filp->f_dentry->d_sb, type); if ((err = write_blk(filp, blk, buf)) < 0) /* Some strange block. We had better leave it... */ return err; return 0; } /* Remove given block from the list of blocks with free entries */ -static int remove_free_dqentry(struct file *filp, struct mem_dqinfo *info, dqbuf_t buf, uint blk) +static int remove_free_dqentry(struct file *filp, int type, dqbuf_t buf, uint blk) { dqbuf_t tmpbuf = getdqbuf(); + struct mem_dqinfo *info = sb_dqinfo(filp->f_dentry->d_sb, type); struct v2_disk_dqdbheader *dh = (struct v2_disk_dqdbheader *)buf; uint nextblk = le32_to_cpu(dh->dqdh_next_free), prevblk = le32_to_cpu(dh->dqdh_prev_free); int err; @@ -242,7 +247,7 @@ static int remove_free_dqentry(struct fi } else { info->u.v2_i.dqi_free_entry = nextblk; - mark_info_dirty(info); + mark_info_dirty(filp->f_dentry->d_sb, type); } freedqbuf(tmpbuf); dh->dqdh_next_free = dh->dqdh_prev_free = cpu_to_le32(0); @@ -255,9 +260,10 @@ out_buf: } /* Insert given block to the beginning of list with free entries */ -static int insert_free_dqentry(struct file *filp, struct mem_dqinfo *info, dqbuf_t buf, uint blk) +static int insert_free_dqentry(struct file *filp, int type, dqbuf_t buf, uint blk) { dqbuf_t tmpbuf = getdqbuf(); + struct mem_dqinfo *info = sb_dqinfo(filp->f_dentry->d_sb, type); struct v2_disk_dqdbheader *dh = (struct v2_disk_dqdbheader *)buf; int err; @@ -276,7 +282,7 @@ static int insert_free_dqentry(struct fi } freedqbuf(tmpbuf); info->u.v2_i.dqi_free_entry = blk; - mark_info_dirty(info); + mark_info_dirty(filp->f_dentry->d_sb, type); return 0; out_buf: freedqbuf(tmpbuf); @@ -307,7 +313,7 @@ static uint find_free_dqentry(struct dqu goto out_buf; } else { - blk = get_free_dqblk(filp, info); + blk = get_free_dqblk(filp, dquot->dq_type); if ((int)blk < 0) { *err = blk; freedqbuf(buf); @@ -315,10 +321,10 @@ static uint find_free_dqentry(struct dqu } memset(buf, 0, V2_DQBLKSIZE); info->u.v2_i.dqi_free_entry = blk; /* This is enough as block is already zeroed and entry list is empty... */ - mark_info_dirty(info); + mark_info_dirty(dquot->dq_sb, dquot->dq_type); } if (le16_to_cpu(dh->dqdh_entries)+1 >= V2_DQSTRINBLK) /* Block will be full? */ - if ((*err = remove_free_dqentry(filp, info, buf, blk)) < 0) { + if ((*err = remove_free_dqentry(filp, dquot->dq_type, buf, blk)) < 0) { printk(KERN_ERR "VFS: find_free_dqentry(): Can't remove block (%u) from entry free list.\n", blk); goto out_buf; } @@ -349,7 +355,6 @@ out_buf: static int do_insert_tree(struct dquot *dquot, uint *treeblk, int depth) { struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type]; - struct mem_dqinfo *info = sb_dqopt(dquot->dq_sb)->info + dquot->dq_type; dqbuf_t buf; int ret = 0, newson = 0, newact = 0; u32 *ref; @@ -358,7 +363,7 @@ static int do_insert_tree(struct dquot * if (!(buf = getdqbuf())) return -ENOMEM; if (!*treeblk) { - ret = get_free_dqblk(filp, info); + ret = get_free_dqblk(filp, dquot->dq_type); if (ret < 0) goto out_buf; *treeblk = ret; @@ -392,7 +397,7 @@ static int do_insert_tree(struct dquot * ret = write_blk(filp, *treeblk, buf); } else if (newact && ret < 0) - put_free_dqblk(filp, info, buf, *treeblk); + put_free_dqblk(filp, dquot->dq_type, buf, *treeblk); out_buf: freedqbuf(buf); return ret; @@ -417,6 +422,7 @@ static int v2_write_dquot(struct dquot * ssize_t ret; struct v2_disk_dqblk ddquot; + /* dq_off is guarded by dqio_sem */ if (!dquot->dq_off) if ((ret = dq_insert_tree(dquot)) < 0) { printk(KERN_ERR "VFS: Error %Zd occurred while creating quota.\n", ret); @@ -424,7 +430,9 @@ static int v2_write_dquot(struct dquot * } filp = sb_dqopt(dquot->dq_sb)->files[type]; offset = dquot->dq_off; + spin_lock(&dq_data_lock); mem2diskdqb(&ddquot, &dquot->dq_dqb, dquot->dq_id); + spin_unlock(&dq_data_lock); fs = get_fs(); set_fs(KERNEL_DS); ret = filp->f_op->write(filp, (char *)&ddquot, sizeof(struct v2_disk_dqblk), &offset); @@ -445,7 +453,6 @@ static int v2_write_dquot(struct dquot * static int free_dqentry(struct dquot *dquot, uint blk) { struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type]; - struct mem_dqinfo *info = sb_dqopt(dquot->dq_sb)->info + dquot->dq_type; struct v2_disk_dqdbheader *dh; dqbuf_t buf = getdqbuf(); int ret = 0; @@ -463,8 +470,8 @@ static int free_dqentry(struct dquot *dq dh = (struct v2_disk_dqdbheader *)buf; dh->dqdh_entries = cpu_to_le16(le16_to_cpu(dh->dqdh_entries)-1); if (!le16_to_cpu(dh->dqdh_entries)) { /* Block got free? */ - if ((ret = remove_free_dqentry(filp, info, buf, blk)) < 0 || - (ret = put_free_dqblk(filp, info, buf, blk)) < 0) { + if ((ret = remove_free_dqentry(filp, dquot->dq_type, buf, blk)) < 0 || + (ret = put_free_dqblk(filp, dquot->dq_type, buf, blk)) < 0) { printk(KERN_ERR "VFS: Can't move quota data block (%u) to free list.\n", blk); goto out_buf; } @@ -473,7 +480,7 @@ static int free_dqentry(struct dquot *dq memset(buf+(dquot->dq_off & ((1 << V2_DQBLKSIZE_BITS)-1)), 0, sizeof(struct v2_disk_dqblk)); if (le16_to_cpu(dh->dqdh_entries) == V2_DQSTRINBLK-1) { /* Insert will write block itself */ - if ((ret = insert_free_dqentry(filp, info, buf, blk)) < 0) { + if ((ret = insert_free_dqentry(filp, dquot->dq_type, buf, blk)) < 0) { printk(KERN_ERR "VFS: Can't insert quota data block (%u) to free entry list.\n", blk); goto out_buf; } @@ -494,7 +501,6 @@ out_buf: static int remove_tree(struct dquot *dquot, uint *blk, int depth) { struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type]; - struct mem_dqinfo *info = sb_dqopt(dquot->dq_sb)->info + dquot->dq_type; dqbuf_t buf = getdqbuf(); int ret = 0; uint newblk; @@ -518,7 +524,7 @@ static int remove_tree(struct dquot *dqu ref[GETIDINDEX(dquot->dq_id, depth)] = cpu_to_le32(0); for (i = 0; i < V2_DQBLKSIZE && !buf[i]; i++); /* Block got empty? */ if (i == V2_DQBLKSIZE) { - put_free_dqblk(filp, info, buf, *blk); + put_free_dqblk(filp, dquot->dq_type, buf, *blk); *blk = 0; } else @@ -632,7 +638,7 @@ static int v2_read_dquot(struct dquot *d if (offset < 0) printk(KERN_ERR "VFS: Can't read quota structure for id %u.\n", dquot->dq_id); dquot->dq_off = 0; - dquot->dq_flags |= DQ_FAKE; + set_bit(DQ_FAKE_B, &dquot->dq_flags); memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk)); ret = offset; } @@ -650,21 +656,24 @@ static int v2_read_dquot(struct dquot *d ret = 0; set_fs(fs); disk2memdqb(&dquot->dq_dqb, &ddquot); + if (!dquot->dq_dqb.dqb_bhardlimit && + !dquot->dq_dqb.dqb_bsoftlimit && + !dquot->dq_dqb.dqb_ihardlimit && + !dquot->dq_dqb.dqb_isoftlimit) + set_bit(DQ_FAKE_B, &dquot->dq_flags); } dqstats.reads++; return ret; } -/* Commit changes of dquot to disk - it might also mean deleting it when quota became fake one and user has no blocks... */ -static int v2_commit_dquot(struct dquot *dquot) +/* Check whether dquot should not be deleted. We know we are + * the only one operating on dquot (thanks to dq_lock) */ +static int v2_release_dquot(struct dquot *dquot) { - /* We clear the flag everytime so we don't loop when there was an IO error... */ - dquot->dq_flags &= ~DQ_MOD; - if (dquot->dq_flags & DQ_FAKE && !(dquot->dq_dqb.dqb_curinodes | dquot->dq_dqb.dqb_curspace)) + if (test_bit(DQ_FAKE_B, &dquot->dq_flags) && !(dquot->dq_dqb.dqb_curinodes | dquot->dq_dqb.dqb_curspace)) return v2_delete_dquot(dquot); - else - return v2_write_dquot(dquot); + return 0; } static struct quota_format_ops v2_format_ops = { @@ -673,7 +682,8 @@ static struct quota_format_ops v2_format .write_file_info = v2_write_file_info, .free_file_info = NULL, .read_dqblk = v2_read_dquot, - .commit_dqblk = v2_commit_dquot, + .commit_dqblk = v2_write_dquot, + .release_dqblk = v2_release_dquot, }; static struct quota_format_type v2_quota_format = { --- linux-2.6.4-rc2/fs/readdir.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/fs/readdir.c 2004-03-07 20:47:08.000000000 -0800 @@ -32,6 +32,7 @@ int vfs_readdir(struct file *file, filld res = -ENOENT; if (!IS_DEADDIR(inode)) { res = file->f_op->readdir(file, buf, filler); + update_atime(inode); } up(&inode->i_sem); out: --- linux-2.6.4-rc2/fs/reiserfs/dir.c 2003-06-14 12:18:22.000000000 -0700 +++ 25/fs/reiserfs/dir.c 2004-03-07 20:47:08.000000000 -0800 @@ -186,7 +186,6 @@ static int reiserfs_readdir (struct file filp->f_pos = next_pos; pathrelse (&path_to_entry); reiserfs_check_path(&path_to_entry) ; - update_atime(inode) ; out: reiserfs_write_unlock(inode->i_sb); return ret; --- linux-2.6.4-rc2/fs/romfs/inode.c 2003-09-08 13:58:59.000000000 -0700 +++ 25/fs/romfs/inode.c 2004-03-07 20:46:46.000000000 -0800 @@ -115,6 +115,7 @@ static int romfs_fill_super(struct super { struct buffer_head *bh; struct romfs_super_block *rsb; + struct inode *root; int sz; /* I would parse the options here, but there are none.. :) */ @@ -154,23 +155,25 @@ static int romfs_fill_super(struct super strnlen(rsb->name, ROMFS_MAXFN) + 1 + ROMFH_PAD) & ROMFH_MASK; - brelse(bh); - s->s_op = &romfs_ops; + root = iget(s, sz); + if (!root) + goto out; + s->s_root = d_alloc_root(iget(s, sz)); if (!s->s_root) - goto outnobh; + goto outiput; - /* Ehrhm; sorry.. :) And thanks to Hans-Joachim Widmaier :) */ - if (0) { + brelse(bh); + return 0; + +outiput: + iput(root); out: - brelse(bh); + brelse(bh); outnobh: - return -EINVAL; - } - - return 0; + return -EINVAL; } /* That's simple too. */ --- linux-2.6.4-rc2/fs/select.c 2003-10-08 15:07:10.000000000 -0700 +++ 25/fs/select.c 2004-03-07 20:47:42.000000000 -0800 @@ -291,8 +291,6 @@ static void select_bits_free(void *bits, * Update: ERESTARTSYS breaks at least the xview clock binary, so * I'm trying ERESTARTNOHAND which restart only when you want to. */ -#define MAX_SELECT_SECONDS \ - ((unsigned long) (MAX_SCHEDULE_TIMEOUT / HZ)-1) asmlinkage long sys_select(int n, fd_set __user *inp, fd_set __user *outp, fd_set __user *exp, struct timeval __user *tvp) @@ -315,9 +313,11 @@ sys_select(int n, fd_set __user *inp, fd if (sec < 0 || usec < 0) goto out_nofds; - if ((unsigned long) sec < MAX_SELECT_SECONDS) { + if ((unsigned long) sec < (MAX_SCHEDULE_TIMEOUT-1) / HZ - 1) { timeout = ROUND_UP(usec, 1000000/HZ); timeout += sec * (unsigned long) HZ; + } else { + timeout = MAX_SCHEDULE_TIMEOUT-1; } } @@ -469,11 +469,17 @@ asmlinkage long sys_poll(struct pollfd _ return -EINVAL; if (timeout) { - /* Careful about overflow in the intermediate values */ - if ((unsigned long) timeout < MAX_SCHEDULE_TIMEOUT / HZ) - timeout = (unsigned long)(timeout*HZ+999)/1000+1; - else /* Negative or overflow */ + if (timeout < 0) { timeout = MAX_SCHEDULE_TIMEOUT; + } else { + /* Careful about overflow in the intermediate values */ + long seconds = timeout/1000; + timeout = ((timeout - 1000*seconds)*HZ + 999)/1000 + 1; + if (seconds <= (MAX_SCHEDULE_TIMEOUT-2) / HZ - 1) + timeout += seconds*HZ; + else + timeout = MAX_SCHEDULE_TIMEOUT-1; + } } poll_initwait(&table); --- linux-2.6.4-rc2/fs/smbfs/inode.c 2003-10-08 15:07:10.000000000 -0700 +++ 25/fs/smbfs/inode.c 2004-03-07 20:47:55.000000000 -0800 @@ -499,6 +499,7 @@ int smb_fill_super(struct super_block *s if (ver != SMB_MOUNT_OLDVERSION && cpu_to_be32(ver) != SMB_MOUNT_ASCII) goto out_wrong_data; + sb->s_flags |= MS_NODIRATIME; sb->s_blocksize = 1024; /* Eh... Is this correct? */ sb->s_blocksize_bits = 10; sb->s_magic = SMB_SUPER_MAGIC; @@ -778,6 +779,7 @@ static struct file_system_type smb_fs_ty .name = "smbfs", .get_sb = smb_get_sb, .kill_sb = kill_anon_super, + .fs_flags = FS_BINARY_MOUNTDATA, }; static int __init init_smb_fs(void) --- linux-2.6.4-rc2/fs/stat.c 2004-02-03 20:42:38.000000000 -0800 +++ 25/fs/stat.c 2004-03-07 20:47:09.000000000 -0800 @@ -397,6 +397,8 @@ EXPORT_SYMBOL(inode_get_bytes); void inode_set_bytes(struct inode *inode, loff_t bytes) { + /* Caller is here responsible for sufficient locking + * (ie. inode->i_lock) */ inode->i_blocks = bytes >> 9; inode->i_bytes = bytes & 511; } --- linux-2.6.4-rc2/fs/super.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/fs/super.c 2004-03-07 20:47:55.000000000 -0800 @@ -68,6 +68,7 @@ static struct super_block *alloc_super(v INIT_LIST_HEAD(&s->s_files); INIT_LIST_HEAD(&s->s_instances); INIT_HLIST_HEAD(&s->s_anon); + INIT_LIST_HEAD(&s->s_inodes); init_rwsem(&s->s_umount); sema_init(&s->s_lock, 1); down_write(&s->s_umount); @@ -745,7 +746,7 @@ do_kern_mount(const char *fstype, int fl goto out_mnt; } - error = security_sb_copy_data(fstype, data, secdata); + error = security_sb_copy_data(type, data, secdata); if (error) { sb = ERR_PTR(error); goto out_free_secdata; --- linux-2.6.4-rc2/fs/sysfs/dir.c 2004-01-09 00:04:32.000000000 -0800 +++ 25/fs/sysfs/dir.c 2004-03-07 20:48:01.000000000 -0800 @@ -20,6 +20,18 @@ static int init_dir(struct inode * inode return 0; } +static void sysfs_d_iput(struct dentry * dentry, struct inode * inode) +{ + struct kobject * kobj = dentry->d_fsdata; + + if (kobj) + kobject_put(kobj); + iput(inode); +} + +static struct dentry_operations sysfs_dentry_operations = { + .d_iput = &sysfs_d_iput, +}; static int create_dir(struct kobject * k, struct dentry * p, const char * n, struct dentry ** d) @@ -33,7 +45,8 @@ static int create_dir(struct kobject * k S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO, init_dir); if (!error) { - (*d)->d_fsdata = k; + (*d)->d_op = &sysfs_dentry_operations; + (*d)->d_fsdata = kobject_get(k); p->d_inode->i_nlink++; } dput(*d); @@ -96,6 +109,8 @@ static void remove_dir(struct dentry * d void sysfs_remove_subdir(struct dentry * d) { remove_dir(d); + /* release the "extra" ref taken during sysfs_create() */ + dput(d); } @@ -120,13 +135,14 @@ void sysfs_remove_dir(struct kobject * k down(&dentry->d_inode->i_sem); spin_lock(&dcache_lock); +restart: node = dentry->d_subdirs.next; while (node != &dentry->d_subdirs) { struct dentry * d = list_entry(node,struct dentry,d_child); - list_del_init(node); + node = node->next; pr_debug(" o %s (%d): ",d->d_name.name,atomic_read(&d->d_count)); - if (d->d_inode) { + if (!d_unhashed(d) && (d->d_inode)) { d = dget_locked(d); pr_debug("removing"); @@ -137,12 +153,12 @@ void sysfs_remove_dir(struct kobject * k d_delete(d); simple_unlink(dentry->d_inode,d); dput(d); + pr_debug(" done\n"); spin_lock(&dcache_lock); + /* re-acquired dcache_lock, need to restart */ + goto restart; } - pr_debug(" done\n"); - node = dentry->d_subdirs.next; } - list_del_init(&dentry->d_child); spin_unlock(&dcache_lock); up(&dentry->d_inode->i_sem); --- linux-2.6.4-rc2/fs/sysfs/group.c 2003-11-09 16:45:05.000000000 -0800 +++ 25/fs/sysfs/group.c 2004-03-07 20:47:36.000000000 -0800 @@ -68,12 +68,13 @@ void sysfs_remove_group(struct kobject * if (grp->name) dir = sysfs_get_dentry(kobj->dentry,grp->name); else - dir = kobj->dentry; + dir = dget(kobj->dentry); remove_files(dir,grp); - dput(dir); if (grp->name) sysfs_remove_subdir(dir); + /* release the ref. taken in this routine */ + dput(dir); } --- linux-2.6.4-rc2/fs/sysv/dir.c 2003-06-14 12:18:05.000000000 -0700 +++ 25/fs/sysv/dir.c 2004-03-07 20:47:08.000000000 -0800 @@ -116,7 +116,6 @@ static int sysv_readdir(struct file * fi done: filp->f_pos = (n << PAGE_CACHE_SHIFT) | offset; - update_atime(inode); unlock_kernel(); return 0; } --- linux-2.6.4-rc2/fs/udf/dir.c 2003-06-14 12:18:05.000000000 -0700 +++ 25/fs/udf/dir.c 2004-03-07 20:47:56.000000000 -0800 @@ -15,7 +15,7 @@ * ftp://prep.ai.mit.edu/pub/gnu/GPL * Each contributing author retains all rights to their own work. * - * (C) 1998-2001 Ben Fennema + * (C) 1998-2004 Ben Fennema * * HISTORY * @@ -98,7 +98,6 @@ int udf_readdir(struct file *filp, void } result = do_udf_readdir(dir, filp, filldir, dirent); - update_atime(dir); unlock_kernel(); return result; } @@ -112,7 +111,7 @@ do_udf_readdir(struct inode * dir, struc int block, iblock; loff_t nf_pos = filp->f_pos - 1; int flen; - char fname[255]; + char fname[UDF_NAME_LEN]; char *nameptr; uint16_t liu; uint8_t lfi; --- linux-2.6.4-rc2/fs/udf/file.c 2003-07-10 18:50:32.000000000 -0700 +++ 25/fs/udf/file.c 2004-03-07 20:47:56.000000000 -0800 @@ -16,7 +16,7 @@ * Each contributing author retains all rights to their own work. * * (C) 1998-1999 Dave Boynton - * (C) 1998-2001 Ben Fennema + * (C) 1998-2004 Ben Fennema * (C) 1999-2000 Stelias Computing Inc * * HISTORY @@ -247,9 +247,9 @@ static int udf_release_file(struct inode { if (filp->f_mode & FMODE_WRITE) { - lock_kernel(); + down(&inode->i_sem); udf_discard_prealloc(inode); - unlock_kernel(); + up(&inode->i_sem); } return 0; } --- linux-2.6.4-rc2/fs/udf/inode.c 2003-10-08 15:07:10.000000000 -0700 +++ 25/fs/udf/inode.c 2004-03-07 20:47:56.000000000 -0800 @@ -16,7 +16,7 @@ * Each contributing author retains all rights to their own work. * * (C) 1998 Dave Boynton - * (C) 1998-2001 Ben Fennema + * (C) 1998-2004 Ben Fennema * (C) 1999-2000 Stelias Computing Inc * * HISTORY @@ -84,9 +84,9 @@ void udf_put_inode(struct inode * inode) { if (!(inode->i_sb->s_flags & MS_RDONLY)) { - lock_kernel(); + down(&inode->i_sem); udf_discard_prealloc(inode); - unlock_kernel(); + up(&inode->i_sem); } } @@ -130,15 +130,6 @@ void udf_clear_inode(struct inode *inode UDF_I_DATA(inode) = NULL; } -void udf_discard_prealloc(struct inode * inode) -{ - if (inode->i_size && inode->i_size != UDF_I_LENEXTENTS(inode) && - UDF_I_ALLOCTYPE(inode) != ICBTAG_FLAG_AD_IN_ICB) - { - udf_truncate_extents(inode); - } -} - static int udf_writepage(struct page *page, struct writeback_control *wbc) { return block_write_full_page(page, udf_get_block, wbc); @@ -516,11 +507,8 @@ static struct buffer_head * inode_getblk else lastblock = 1; } + udf_release_data(cbh); udf_release_data(nbh); - if (!pbh) - pbh = cbh; - else - udf_release_data(cbh); /* if the current extent is not recorded but allocated, get the block in the extent corresponding to the requested block */ @@ -595,7 +583,7 @@ static void udf_split_extents(struct ino int curr = *c; int blen = ((laarr[curr].extLength & UDF_EXTENT_LENGTH_MASK) + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits; - int type = laarr[curr].extLength & ~UDF_EXTENT_LENGTH_MASK; + int8_t etype = (laarr[curr].extLength >> 30); if (blen == 1) ; @@ -612,7 +600,7 @@ static void udf_split_extents(struct ino if (offset) { - if ((type >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30)) + if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) { udf_free_blocks(inode->i_sb, inode, laarr[curr].extLocation, 0, offset); laarr[curr].extLength = EXT_NOT_RECORDED_NOT_ALLOCATED | @@ -621,7 +609,7 @@ static void udf_split_extents(struct ino laarr[curr].extLocation.partitionReferenceNum = 0; } else - laarr[curr].extLength = type | + laarr[curr].extLength = (etype << 30) | (offset << inode->i_sb->s_blocksize_bits); curr ++; (*c) ++; @@ -629,7 +617,7 @@ static void udf_split_extents(struct ino } laarr[curr].extLocation.logicalBlockNum = newblocknum; - if ((type >> 30) == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) + if (etype == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) laarr[curr].extLocation.partitionReferenceNum = UDF_I_LOCATION(inode).partitionReferenceNum; laarr[curr].extLength = EXT_RECORDED_ALLOCATED | @@ -638,9 +626,9 @@ static void udf_split_extents(struct ino if (blen != offset + 1) { - if ((type >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30)) + if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) laarr[curr].extLocation.logicalBlockNum += (offset + 1); - laarr[curr].extLength = type | + laarr[curr].extLength = (etype << 30) | ((blen - (offset + 1)) << inode->i_sb->s_blocksize_bits); curr ++; (*endnum) ++; @@ -761,8 +749,8 @@ static void udf_merge_extents(struct ino laarr[i+1].extLength = (laarr[i+1].extLength - (laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + UDF_EXTENT_LENGTH_MASK) & ~(inode->i_sb->s_blocksize-1); - laarr[i].extLength = (UDF_EXTENT_LENGTH_MASK + 1) - - inode->i_sb->s_blocksize; + laarr[i].extLength = (laarr[i].extLength & UDF_EXTENT_FLAG_MASK) + + (UDF_EXTENT_LENGTH_MASK + 1) - inode->i_sb->s_blocksize; laarr[i+1].extLocation.logicalBlockNum = laarr[i].extLocation.logicalBlockNum + ((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) >> @@ -781,6 +769,47 @@ static void udf_merge_extents(struct ino } } } + else if (((laarr[i].extLength >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30)) && + ((laarr[i+1].extLength >> 30) == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30))) + { + udf_free_blocks(inode->i_sb, inode, laarr[i].extLocation, 0, + ((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits); + laarr[i].extLocation.logicalBlockNum = 0; + laarr[i].extLocation.partitionReferenceNum = 0; + + if (((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + + (laarr[i+1].extLength & UDF_EXTENT_LENGTH_MASK) + + inode->i_sb->s_blocksize - 1) & ~UDF_EXTENT_LENGTH_MASK) + { + laarr[i+1].extLength = (laarr[i+1].extLength - + (laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + + UDF_EXTENT_LENGTH_MASK) & ~(inode->i_sb->s_blocksize-1); + laarr[i].extLength = (laarr[i].extLength & UDF_EXTENT_FLAG_MASK) + + (UDF_EXTENT_LENGTH_MASK + 1) - inode->i_sb->s_blocksize; + } + else + { + laarr[i].extLength = laarr[i+1].extLength + + (((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + + inode->i_sb->s_blocksize - 1) & ~(inode->i_sb->s_blocksize-1)); + if (*endnum > (i+2)) + memmove(&laarr[i+1], &laarr[i+2], + sizeof(long_ad) * (*endnum - (i+2))); + i --; + (*endnum) --; + } + } + else if ((laarr[i].extLength >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30)) + { + udf_free_blocks(inode->i_sb, inode, laarr[i].extLocation, 0, + ((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits); + laarr[i].extLocation.logicalBlockNum = 0; + laarr[i].extLocation.partitionReferenceNum = 0; + laarr[i].extLength = (laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) | + EXT_NOT_RECORDED_NOT_ALLOCATED; + } } } @@ -1014,7 +1043,7 @@ static void udf_fill_inode(struct inode struct extendedFileEntry *efe; time_t convtime; long convtime_usec; - int offset, alen; + int offset; fe = (struct fileEntry *)bh->b_data; efe = (struct extendedFileEntry *)bh->b_data; @@ -1115,7 +1144,6 @@ static void udf_fill_inode(struct inode UDF_I_LENEATTR(inode) = le32_to_cpu(fe->lengthExtendedAttr); UDF_I_LENALLOC(inode) = le32_to_cpu(fe->lengthAllocDescs); offset = sizeof(struct fileEntry) + UDF_I_LENEATTR(inode); - alen = offset + UDF_I_LENALLOC(inode); } else { @@ -1170,7 +1198,6 @@ static void udf_fill_inode(struct inode UDF_I_LENEATTR(inode) = le32_to_cpu(efe->lengthExtendedAttr); UDF_I_LENALLOC(inode) = le32_to_cpu(efe->lengthAllocDescs); offset = sizeof(struct extendedFileEntry) + UDF_I_LENEATTR(inode); - alen = offset + UDF_I_LENALLOC(inode); } switch (fe->icbTag.fileType) @@ -1211,6 +1238,11 @@ static void udf_fill_inode(struct inode init_special_inode(inode, inode->i_mode | S_IFIFO, 0); break; } + case ICBTAG_FILE_TYPE_SOCKET: + { + init_special_inode(inode, inode->i_mode | S_IFSOCK, 0); + break; + } case ICBTAG_FILE_TYPE_SYMLINK: { inode->i_data.a_ops = &udf_symlink_aops; @@ -1228,19 +1260,16 @@ static void udf_fill_inode(struct inode } if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) { - struct buffer_head *tbh = NULL; struct deviceSpec *dsea = (struct deviceSpec *) - udf_get_extendedattr(inode, 12, 1, &tbh); + udf_get_extendedattr(inode, 12, 1); if (dsea) { init_special_inode(inode, inode->i_mode, MKDEV( le32_to_cpu(dsea->majorDeviceIdent), - le32_to_cpu(dsea->minorDeviceIdent) - )); + le32_to_cpu(dsea->minorDeviceIdent))); /* Developer ID ??? */ - udf_release_data(tbh); } else { @@ -1372,17 +1401,16 @@ udf_update_inode(struct inode *inode, in if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) { regid *eid; - struct buffer_head *tbh = NULL; struct deviceSpec *dsea = (struct deviceSpec *) - udf_get_extendedattr(inode, 12, 1, &tbh); + udf_get_extendedattr(inode, 12, 1); if (!dsea) { dsea = (struct deviceSpec *) udf_add_extendedattr(inode, sizeof(struct deviceSpec) + - sizeof(regid), 12, 0x3, &tbh); + sizeof(regid), 12, 0x3); dsea->attrType = 12; dsea->attrSubtype = 1; dsea->attrLength = sizeof(struct deviceSpec) + @@ -1396,8 +1424,6 @@ udf_update_inode(struct inode *inode, in eid->identSuffix[1] = UDF_OS_ID_LINUX; dsea->majorDeviceIdent = cpu_to_le32(imajor(inode)); dsea->minorDeviceIdent = cpu_to_le32(iminor(inode)); - mark_buffer_dirty_inode(tbh, inode); - udf_release_data(tbh); } if (UDF_I_EFE(inode) == 0) @@ -1493,6 +1519,8 @@ udf_update_inode(struct inode *inode, in fe->icbTag.fileType = ICBTAG_FILE_TYPE_CHAR; else if (S_ISFIFO(inode->i_mode)) fe->icbTag.fileType = ICBTAG_FILE_TYPE_FIFO; + else if (S_ISSOCK(inode->i_mode)) + fe->icbTag.fileType = ICBTAG_FILE_TYPE_SOCKET; icbflags = UDF_I_ALLOCTYPE(inode) | ((inode->i_mode & S_ISUID) ? ICBTAG_FLAG_SETUID : 0) | @@ -1625,7 +1653,7 @@ int8_t udf_add_aext(struct inode *inode, int err, loffset; lb_addr obloc = *bloc; - if (!(bloc->logicalBlockNum = udf_new_block(inode->i_sb, inode, + if (!(bloc->logicalBlockNum = udf_new_block(inode->i_sb, NULL, obloc.partitionReferenceNum, obloc.logicalBlockNum, &err))) { return -1; @@ -1833,7 +1861,7 @@ int8_t udf_current_aext(struct inode *in if (!(*extoffset)) *extoffset = sizeof(struct allocExtDesc); ptr = (*bh)->b_data + *extoffset; - alen = le32_to_cpu(((struct allocExtDesc *)(*bh)->b_data)->lengthAllocDescs); + alen = sizeof(struct allocExtDesc) + le32_to_cpu(((struct allocExtDesc *)(*bh)->b_data)->lengthAllocDescs); } switch (UDF_I_ALLOCTYPE(inode)) --- linux-2.6.4-rc2/fs/udf/misc.c 2003-06-14 12:18:23.000000000 -0700 +++ 25/fs/udf/misc.c 2004-03-07 20:47:56.000000000 -0800 @@ -16,7 +16,7 @@ * Each contributing author retains all rights to their own work. * * (C) 1998 Dave Boynton - * (C) 1998-2001 Ben Fennema + * (C) 1998-2004 Ben Fennema * (C) 1999-2000 Stelias Computing Inc * * HISTORY @@ -34,18 +34,6 @@ #include "udf_i.h" #include "udf_sb.h" -uint32_t -udf64_low32(uint64_t indat) -{ - return indat & 0x00000000FFFFFFFFULL; -} - -uint32_t -udf64_high32(uint64_t indat) -{ - return indat >> 32; -} - extern struct buffer_head * udf_tgetblk(struct super_block *sb, int block) { @@ -66,42 +54,24 @@ udf_tread(struct super_block *sb, int bl extern struct genericFormat * udf_add_extendedattr(struct inode * inode, uint32_t size, uint32_t type, - uint8_t loc, struct buffer_head **bh) + uint8_t loc) { uint8_t *ea = NULL, *ad = NULL; - long_ad eaicb; int offset; + uint16_t crclen; + int i; - *bh = udf_tread(inode->i_sb, inode->i_ino); - - if (UDF_I_EFE(inode) == 0) - { - struct fileEntry *fe; - - fe = (struct fileEntry *)(*bh)->b_data; - eaicb = lela_to_cpu(fe->extendedAttrICB); - offset = sizeof(struct fileEntry); - } - else - { - struct extendedFileEntry *efe; - - efe = (struct extendedFileEntry *)(*bh)->b_data; - eaicb = lela_to_cpu(efe->extendedAttrICB); - offset = sizeof(struct extendedFileEntry); - } - - ea = &(*bh)->b_data[offset]; + ea = UDF_I_DATA(inode); if (UDF_I_LENEATTR(inode)) - offset += UDF_I_LENEATTR(inode); + ad = UDF_I_DATA(inode) + UDF_I_LENEATTR(inode); else + { + ad = ea; size += sizeof(struct extendedAttrHeaderDesc); + } - ad = &(*bh)->b_data[offset]; - if (UDF_I_LENALLOC(inode)) - offset += UDF_I_LENALLOC(inode); - - offset = inode->i_sb->s_blocksize - offset; + offset = inode->i_sb->s_blocksize - udf_file_entry_alloc_offset(inode) - + UDF_I_LENALLOC(inode); /* TODO - Check for FreeEASpace */ @@ -121,7 +91,6 @@ udf_add_extendedattr(struct inode * inod if (le16_to_cpu(eahd->descTag.tagIdent) != TAG_IDENT_EAHD || le32_to_cpu(eahd->descTag.tagLocation) != UDF_I_LOCATION(inode).logicalBlockNum) { - udf_release_data(*bh); return NULL; } } @@ -130,8 +99,11 @@ udf_add_extendedattr(struct inode * inod size -= sizeof(struct extendedAttrHeaderDesc); UDF_I_LENEATTR(inode) += sizeof(struct extendedAttrHeaderDesc); eahd->descTag.tagIdent = cpu_to_le16(TAG_IDENT_EAHD); - eahd->descTag.descVersion = cpu_to_le16(2); - eahd->descTag.tagSerialNum = cpu_to_le16(1); + if (UDF_SB_UDFREV(inode->i_sb) >= 0x0200) + eahd->descTag.descVersion = cpu_to_le16(3); + else + eahd->descTag.descVersion = cpu_to_le16(2); + eahd->descTag.tagSerialNum = cpu_to_le16(UDF_SB_SERIALNUM(inode->i_sb)); eahd->descTag.tagLocation = cpu_to_le32(UDF_I_LOCATION(inode).logicalBlockNum); eahd->impAttrLocation = cpu_to_le32(0xFFFFFFFF); eahd->appAttrLocation = cpu_to_le32(0xFFFFFFFF); @@ -169,45 +141,30 @@ udf_add_extendedattr(struct inode * inod } } /* rewrite CRC + checksum of eahd */ + crclen = sizeof(struct extendedAttrHeaderDesc) - sizeof(tag); + eahd->descTag.descCRCLength = cpu_to_le16(crclen); + eahd->descTag.descCRC = cpu_to_le16(udf_crc((char *)eahd + sizeof(tag), crclen, 0)); + eahd->descTag.tagChecksum = 0; + for (i=0; i<16; i++) + if (i != 4) + eahd->descTag.tagChecksum += ((uint8_t *)&(eahd->descTag))[i]; UDF_I_LENEATTR(inode) += size; return (struct genericFormat *)&ea[offset]; } if (loc & 0x02) { } - udf_release_data(*bh); return NULL; } extern struct genericFormat * -udf_get_extendedattr(struct inode * inode, uint32_t type, uint8_t subtype, - struct buffer_head **bh) +udf_get_extendedattr(struct inode *inode, uint32_t type, uint8_t subtype) { struct genericFormat *gaf; uint8_t *ea = NULL; - long_ad eaicb; uint32_t offset; - *bh = udf_tread(inode->i_sb, inode->i_ino); - - if (UDF_I_EFE(inode) == 0) - { - struct fileEntry *fe; - - fe = (struct fileEntry *)(*bh)->b_data; - eaicb = lela_to_cpu(fe->extendedAttrICB); - if (UDF_I_LENEATTR(inode)) - ea = fe->extendedAttr; - } - else - { - struct extendedFileEntry *efe; - - efe = (struct extendedFileEntry *)(*bh)->b_data; - eaicb = lela_to_cpu(efe->extendedAttrICB); - if (UDF_I_LENEATTR(inode)) - ea = efe->extendedAttr; - } + ea = UDF_I_DATA(inode); if (UDF_I_LENEATTR(inode)) { @@ -218,7 +175,6 @@ udf_get_extendedattr(struct inode * inod if (le16_to_cpu(eahd->descTag.tagIdent) != TAG_IDENT_EAHD || le32_to_cpu(eahd->descTag.tagLocation) != UDF_I_LOCATION(inode).logicalBlockNum) { - udf_release_data(*bh); return NULL; } @@ -238,12 +194,6 @@ udf_get_extendedattr(struct inode * inod offset += le32_to_cpu(gaf->attrLength); } } - - udf_release_data(*bh); - if (eaicb.extLength) - { - /* TODO */ - } return NULL; } --- linux-2.6.4-rc2/fs/udf/namei.c 2003-09-08 13:58:59.000000000 -0700 +++ 25/fs/udf/namei.c 2004-03-07 20:47:56.000000000 -0800 @@ -15,7 +15,7 @@ * ftp://prep.ai.mit.edu/pub/gnu/GPL * Each contributing author retains all rights to their own work. * - * (C) 1998-2001 Ben Fennema + * (C) 1998-2004 Ben Fennema * (C) 1999-2000 Stelias Computing Inc * * HISTORY @@ -36,11 +36,11 @@ #include #include -static inline int udf_match(int len, const char * const name, struct qstr *qs) +static inline int udf_match(int len1, const char *name1, int len2, const char *name2) { - if (len != qs->len) + if (len1 != len2) return 0; - return !memcmp(name, qs->name, len); + return !memcmp(name1, name2, len1); } int udf_write_fi(struct inode *inode, struct fileIdentDesc *cfi, @@ -154,8 +154,8 @@ udf_find_entry(struct inode *dir, struct { struct fileIdentDesc *fi=NULL; loff_t f_pos; - int block, flen; - char fname[255]; + int block, namelen; + char name[UDF_NAME_LEN], fname[UDF_NAME_LEN]; char *nameptr; uint8_t lfi; uint16_t liu; @@ -167,6 +167,9 @@ udf_find_entry(struct inode *dir, struct if (!dir) return NULL; + if ( !(namelen = udf_put_filename(dir->i_sb, dentry->d_name.name, name, dentry->d_name.len))) + return NULL; + f_pos = (udf_ext0_offset(dir) >> 2); fibh->soffset = fibh->eoffset = (f_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2; @@ -250,13 +253,10 @@ udf_find_entry(struct inode *dir, struct if (!lfi) continue; - if ((flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi))) + if (udf_match(namelen, name, lfi, nameptr)) { - if (udf_match(flen, fname, &(dentry->d_name))) - { - udf_release_data(bh); - return fi; - } + udf_release_data(bh); + return fi; } } if (fibh->sbh != fibh->ebh) @@ -306,7 +306,7 @@ udf_lookup(struct inode *dir, struct den struct fileIdentDesc cfi, *fi; struct udf_fileident_bh fibh; - if (dentry->d_name.len > UDF_NAME_LEN) + if (dentry->d_name.len > UDF_NAME_LEN-2) return ERR_PTR(-ENAMETOOLONG); lock_kernel(); @@ -353,7 +353,6 @@ udf_add_entry(struct inode *dir, struct char name[UDF_NAME_LEN], fname[UDF_NAME_LEN]; int namelen; loff_t f_pos; - int flen; char *nameptr; loff_t size = (udf_ext0_offset(dir) + dir->i_size) >> 2; int nfidlen; @@ -481,8 +480,7 @@ udf_add_entry(struct inode *dir, struct if (!lfi || !dentry) continue; - if ((flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi)) && - udf_match(flen, fname, &(dentry->d_name))) + if (udf_match(namelen, name, lfi, nameptr)) { if (fibh->sbh != fibh->ebh) udf_release_data(fibh->ebh); @@ -674,8 +672,8 @@ static int udf_mknod(struct inode * dir, { struct inode * inode; struct udf_fileident_bh fibh; - int err; struct fileIdentDesc cfi, *fi; + int err; if (!old_valid_dev(rdev)) return -EINVAL; @@ -721,8 +719,8 @@ static int udf_mkdir(struct inode * dir, { struct inode * inode; struct udf_fileident_bh fibh; - int err; struct fileIdentDesc cfi, *fi; + int err; lock_kernel(); err = -EMLINK; @@ -1119,8 +1117,8 @@ static int udf_link(struct dentry * old_ { struct inode *inode = old_dentry->d_inode; struct udf_fileident_bh fibh; - int err; struct fileIdentDesc cfi, *fi; + int err; lock_kernel(); if (inode->i_nlink >= (256<i_nlink))-1) --- linux-2.6.4-rc2/fs/udf/osta_udf.h 2003-06-14 12:18:25.000000000 -0700 +++ 25/fs/udf/osta_udf.h 2004-03-07 20:47:56.000000000 -0800 @@ -1,10 +1,10 @@ /* * osta_udf.h * - * This file is based on OSTA UDF(tm) 2.01 (March 15, 2000) + * This file is based on OSTA UDF(tm) 2.50 (April 30, 2003) * http://www.osta.org * - * Copyright (c) 2001-2002 Ben Fennema + * Copyright (c) 2001-2004 Ben Fennema * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,12 +37,12 @@ #ifndef _OSTA_UDF_H #define _OSTA_UDF_H 1 -/* OSTA CS0 Charspec (UDF 2.01 2.1.2) */ +/* OSTA CS0 Charspec (UDF 2.50 2.1.2) */ #define UDF_CHAR_SET_TYPE 0 #define UDF_CHAR_SET_INFO "OSTA Compressed Unicode" -/* Entity Identifier (UDF 2.01 2.1.5) */ -/* Identifiers (UDF 2.01 2.1.5.2) */ +/* Entity Identifier (UDF 2.50 2.1.5) */ +/* Identifiers (UDF 2.50 2.1.5.2) */ #define UDF_ID_DEVELOPER "*Linux UDFFS" #define UDF_ID_COMPLIANT "*OSTA UDF Compliant" #define UDF_ID_LV_INFO "*UDF LV Info" @@ -59,8 +59,9 @@ #define UDF_ID_SPARABLE "*UDF Sparable Partition" #define UDF_ID_ALLOC "*UDF Virtual Alloc Tbl" #define UDF_ID_SPARING "*UDF Sparing Table" +#define UDF_ID_METADATA "*UDF Metadata Partition" -/* Identifier Suffix (UDF 2.01 2.1.5.3) */ +/* Identifier Suffix (UDF 2.50 2.1.5.3) */ #define IS_DF_HARD_WRITE_PROTECT 0x01 #define IS_DF_SOFT_WRITE_PROTECT 0x02 @@ -84,8 +85,8 @@ struct appIdentSuffix uint8_t impUse[8]; } __attribute__ ((packed)); -/* Logical Volume Integrity Descriptor (UDF 2.01 2.2.6) */ -/* Implementation Use (UDF 2.01 2.2.6.4) */ +/* Logical Volume Integrity Descriptor (UDF 2.50 2.2.6) */ +/* Implementation Use (UDF 2.50 2.2.6.4) */ struct logicalVolIntegrityDescImpUse { regid impIdent; @@ -97,8 +98,8 @@ struct logicalVolIntegrityDescImpUse uint8_t impUse[0]; } __attribute__ ((packed)); -/* Implementation Use Volume Descriptor (UDF 2.01 2.2.7) */ -/* Implementation Use (UDF 2.01 2.2.7.2) */ +/* Implementation Use Volume Descriptor (UDF 2.50 2.2.7) */ +/* Implementation Use (UDF 2.50 2.2.7.2) */ struct impUseVolDescImpUse { charspec LVICharset; @@ -120,7 +121,7 @@ struct udfPartitionMap2 uint16_t partitionNum; } __attribute__ ((packed)); -/* Virtual Partition Map (UDF 2.01 2.2.8) */ +/* Virtual Partition Map (UDF 2.50 2.2.8) */ struct virtualPartitionMap { uint8_t partitionMapType; @@ -132,7 +133,7 @@ struct virtualPartitionMap uint8_t reserved2[24]; } __attribute__ ((packed)); -/* Sparable Partition Map (UDF 2.01 2.2.9) */ +/* Sparable Partition Map (UDF 2.50 2.2.9) */ struct sparablePartitionMap { uint8_t partitionMapType; @@ -148,25 +149,43 @@ struct sparablePartitionMap uint32_t locSparingTable[4]; } __attribute__ ((packed)); +/* Metadata Partition Map (UDF 2.4.0 2.2.10) */ +struct metadataPartitionMap +{ + uint8_t partitionMapType; + uint8_t partitionMapLength; + uint8_t reserved1[2]; + regid partIdent; + uint16_t volSeqNum; + uint16_t partitionNum; + uint32_t metadataFileLoc; + uint32_t metadataMirrorFileLoc; + uint32_t metadataBitmapFileLoc; + uint32_t allocUnitSize; + uint16_t alignUnitSize; + uint8_t flags; + uint8_t reserved2[5]; +} __attribute__ ((packed)); + /* Virtual Allocation Table (UDF 1.5 2.2.10) */ struct virtualAllocationTable15 { uint32_t VirtualSector[0]; - regid ident; - uint32_t previousVATICB; + regid vatIdent; + uint32_t previousVATICBLoc; } __attribute__ ((packed)); #define ICBTAG_FILE_TYPE_VAT15 0x00U -/* Virtual Allocation Table (UDF 2.01 2.2.10) */ +/* Virtual Allocation Table (UDF 2.50 2.2.11) */ struct virtualAllocationTable20 { uint16_t lengthHeader; uint16_t lengthImpUse; dstring logicalVolIdent[128]; - uint32_t previousVatICBLoc; - uint32_t numFIDSFiles; - uint32_t numFIDSDirectories; + uint32_t previousVATICBLoc; + uint32_t numFiles; + uint32_t numDirs; uint16_t minReadRevision; uint16_t minWriteRevision; uint16_t maxWriteRevision; @@ -177,7 +196,7 @@ struct virtualAllocationTable20 #define ICBTAG_FILE_TYPE_VAT20 0xF8U -/* Sparing Table (UDF 2.01 2.2.11) */ +/* Sparing Table (UDF 2.50 2.2.12) */ struct sparingEntry { uint32_t origLocation; @@ -195,7 +214,12 @@ struct sparingTable mapEntry[0]; } __attribute__ ((packed)); -/* struct long_ad ICB - ADImpUse (UDF 2.01 2.2.4.3) */ +/* Metadata File (and Metadata Mirror File) (UDF 2.50 2.2.13.1) */ +#define ICBTAG_FILE_TYPE_MAIN 0xFA +#define ICBTAG_FILE_TYPE_MIRROR 0xFB +#define ICBTAG_FILE_TYPE_BITMAP 0xFC + +/* struct long_ad ICB - ADImpUse (UDF 2.50 2.2.4.3) */ struct allocDescImpUse { uint16_t flags; @@ -204,18 +228,18 @@ struct allocDescImpUse #define AD_IU_EXT_ERASED 0x0001 -/* Real-Time Files (UDF 2.01 6.11) */ +/* Real-Time Files (UDF 2.50 6.11) */ #define ICBTAG_FILE_TYPE_REALTIME 0xF9U -/* Implementation Use Extended Attribute (UDF 2.01 3.3.4.5) */ -/* FreeEASpace (UDF 2.01 3.3.4.5.1.1) */ +/* Implementation Use Extended Attribute (UDF 2.50 3.3.4.5) */ +/* FreeEASpace (UDF 2.50 3.3.4.5.1.1) */ struct freeEaSpace { uint16_t headerChecksum; uint8_t freeEASpace[0]; } __attribute__ ((packed)); -/* DVD Copyright Management Information (UDF 2.01 3.3.4.5.1.2) */ +/* DVD Copyright Management Information (UDF 2.50 3.3.4.5.1.2) */ struct DVDCopyrightImpUse { uint16_t headerChecksum; @@ -224,21 +248,21 @@ struct DVDCopyrightImpUse uint8_t protectionSystemInfo[4]; } __attribute__ ((packed)); -/* Application Use Extended Attribute (UDF 2.01 3.3.4.6) */ -/* FreeAppEASpace (UDF 2.01 3.3.4.6.1) */ +/* Application Use Extended Attribute (UDF 2.50 3.3.4.6) */ +/* FreeAppEASpace (UDF 2.50 3.3.4.6.1) */ struct freeAppEASpace { uint16_t headerChecksum; uint8_t freeEASpace[0]; } __attribute__ ((packed)); -/* UDF Defined System Stream (UDF 2.01 3.3.7) */ +/* UDF Defined System Stream (UDF 2.50 3.3.7) */ #define UDF_ID_UNIQUE_ID "*UDF Unique ID Mapping Data" #define UDF_ID_NON_ALLOC "*UDF Non-Allocatable Space" #define UDF_ID_POWER_CAL "*UDF Power Cal Table" #define UDF_ID_BACKUP "*UDF Backup" -/* Operating System Identifiers (UDF 2.01 6.3) */ +/* Operating System Identifiers (UDF 2.50 6.3) */ #define UDF_OS_CLASS_UNDEF 0x00U #define UDF_OS_CLASS_DOS 0x01U #define UDF_OS_CLASS_OS2 0x02U @@ -254,6 +278,7 @@ struct freeAppEASpace #define UDF_OS_ID_DOS 0x00U #define UDF_OS_ID_OS2 0x00U #define UDF_OS_ID_MAC 0x00U +#define UDF_OS_ID_MAX_OSX 0x01U #define UDF_OS_ID_UNIX 0x00U #define UDF_OS_ID_AIX 0x01U #define UDF_OS_ID_SOLARIS 0x02U --- linux-2.6.4-rc2/fs/udf/super.c 2004-01-09 00:04:32.000000000 -0800 +++ 25/fs/udf/super.c 2004-03-07 20:47:56.000000000 -0800 @@ -26,7 +26,7 @@ * Each contributing author retains all rights to their own work. * * (C) 1998 Dave Boynton - * (C) 1998-2001 Ben Fennema + * (C) 1998-2004 Ben Fennema * (C) 2000 Stelias Computing Inc * * HISTORY @@ -133,7 +133,8 @@ static void init_once(void * foo, kmem_c struct udf_inode_info *ei = (struct udf_inode_info *) foo; if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == - SLAB_CTOR_CONSTRUCTOR) { + SLAB_CTOR_CONSTRUCTOR) + { ei->i_ext.i_data = NULL; inode_init_once(&ei->vfs_inode); } @@ -324,106 +325,106 @@ udf_parse_options(char *options, struct if (!options) return 1; - while ((p = strsep(&options, ",")) != NULL) { + while ((p = strsep(&options, ",")) != NULL) + { substring_t args[MAX_OPT_ARGS]; int token; if (!*p) continue; token = match_token(p, tokens, args); - switch (token) { - case Opt_novrs: - uopt->novrs = 1; - break; - case Opt_bs: - if (match_int(&args[0], &option)) - return 0; - uopt->blocksize = option; - break; - case Opt_unhide: - uopt->flags |= (1 << UDF_FLAG_UNHIDE); - break; - case Opt_undelete: - uopt->flags |= (1 << UDF_FLAG_UNDELETE); - break; - case Opt_noadinicb: - uopt->flags &= ~(1 << UDF_FLAG_USE_AD_IN_ICB); - break; - case Opt_adinicb: - uopt->flags |= (1 << UDF_FLAG_USE_AD_IN_ICB); - break; - case Opt_shortad: - uopt->flags |= (1 << UDF_FLAG_USE_SHORT_AD); - break; - case Opt_longad: - uopt->flags &= ~(1 << UDF_FLAG_USE_SHORT_AD); - break; - case Opt_gid: - if (match_int(args, &option)) - return 0; - uopt->gid = option; - break; - case Opt_uid: - if (match_int(args, &option)) - return 0; - uopt->uid = option; - break; - case Opt_umask: - if (match_octal(args, &option)) - return 0; - uopt->umask = option; - break; - case Opt_nostrict: - uopt->flags &= ~(1 << UDF_FLAG_STRICT); - break; - case Opt_session: - if (match_int(args, &option)) - return 0; - uopt->session = option; - break; - case Opt_lastblock: - if (match_int(args, &option)) - return 0; - uopt->lastblock = option; - break; - case Opt_anchor: - if (match_int(args, &option)) - return 0; - uopt->anchor = option; - break; - case Opt_volume: - if (match_int(args, &option)) - return 0; - uopt->volume = option; - break; - case Opt_partition: - if (match_int(args, &option)) - return 0; - uopt->partition = option; - break; - case Opt_fileset: - if (match_int(args, &option)) - return 0; - uopt->fileset = option; - break; - case Opt_rootdir: - if (match_int(args, &option)) - return 0; - uopt->rootdir = option; - break; - case Opt_utf8: - uopt->flags |= (1 << UDF_FLAG_UTF8); - break; + switch (token) + { + case Opt_novrs: + uopt->novrs = 1; + case Opt_bs: + if (match_int(&args[0], &option)) + return 0; + uopt->blocksize = option; + break; + case Opt_unhide: + uopt->flags |= (1 << UDF_FLAG_UNHIDE); + break; + case Opt_undelete: + uopt->flags |= (1 << UDF_FLAG_UNDELETE); + break; + case Opt_noadinicb: + uopt->flags &= ~(1 << UDF_FLAG_USE_AD_IN_ICB); + break; + case Opt_adinicb: + uopt->flags |= (1 << UDF_FLAG_USE_AD_IN_ICB); + break; + case Opt_shortad: + uopt->flags |= (1 << UDF_FLAG_USE_SHORT_AD); + break; + case Opt_longad: + uopt->flags &= ~(1 << UDF_FLAG_USE_SHORT_AD); + break; + case Opt_gid: + if (match_int(args, &option)) + return 0; + uopt->gid = option; + break; + case Opt_uid: + if (match_int(args, &option)) + return 0; + uopt->uid = option; + break; + case Opt_umask: + if (match_octal(args, &option)) + return 0; + uopt->umask = option; + break; + case Opt_nostrict: + uopt->flags &= ~(1 << UDF_FLAG_STRICT); + break; + case Opt_session: + if (match_int(args, &option)) + return 0; + uopt->session = option; + break; + case Opt_lastblock: + if (match_int(args, &option)) + return 0; + uopt->lastblock = option; + break; + case Opt_anchor: + if (match_int(args, &option)) + return 0; + uopt->anchor = option; + break; + case Opt_volume: + if (match_int(args, &option)) + return 0; + uopt->volume = option; + break; + case Opt_partition: + if (match_int(args, &option)) + return 0; + uopt->partition = option; + break; + case Opt_fileset: + if (match_int(args, &option)) + return 0; + uopt->fileset = option; + break; + case Opt_rootdir: + if (match_int(args, &option)) + return 0; + uopt->rootdir = option; + break; + case Opt_utf8: + uopt->flags |= (1 << UDF_FLAG_UTF8); + break; #if defined(CONFIG_NLS) || defined(CONFIG_NLS_MODULE) - case Opt_iocharset: - uopt->nls_map = load_nls(args[0].from); - uopt->flags |= (1 << UDF_FLAG_NLS_MAP); - break; + case Opt_iocharset: + uopt->nls_map = load_nls(args[0].from); + uopt->flags |= (1 << UDF_FLAG_NLS_MAP); + break; #endif - default: - printk(KERN_ERR "udf: bad mount option \"%s\" " - "or missing value\n", - p); + default: + printk(KERN_ERR "udf: bad mount option \"%s\" " + "or missing value\n", p); return 0; } } @@ -1651,23 +1652,9 @@ error_out: if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_TABLE) iput(UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_fspace.s_table); if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_BITMAP) - { - for (i=0; inumDirs)) : 0) + buf->f_bfree; buf->f_ffree = buf->f_bfree; /* __kernel_fsid_t f_fsid */ - buf->f_namelen = UDF_NAME_LEN; + buf->f_namelen = UDF_NAME_LEN-2; return 0; } --- linux-2.6.4-rc2/fs/udf/truncate.c 2003-06-14 12:18:07.000000000 -0700 +++ 25/fs/udf/truncate.c 2004-03-07 20:47:56.000000000 -0800 @@ -15,7 +15,7 @@ * ftp://prep.ai.mit.edu/pub/gnu/GPL * Each contributing author retains all rights to their own work. * - * (C) 1999-2001 Ben Fennema + * (C) 1999-2004 Ben Fennema * (C) 1999 Stelias Computing Inc * * HISTORY @@ -66,6 +66,67 @@ static void extent_trunc(struct inode * } } +void udf_discard_prealloc(struct inode * inode) +{ + lb_addr bloc, eloc; + uint32_t extoffset = 0, elen, nelen; + uint64_t lbcount = 0; + int8_t etype = -1, netype; + struct buffer_head *bh = NULL; + int adsize; + + if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB || + inode->i_size == UDF_I_LENEXTENTS(inode)) + { + return; + } + + if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT) + adsize = sizeof(short_ad); + else if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_LONG) + adsize = sizeof(long_ad); + else + adsize = 0; + + bloc = UDF_I_LOCATION(inode); + + while ((netype = udf_next_aext(inode, &bloc, &extoffset, &eloc, &elen, &bh, 1)) != -1) + { + etype = netype; + lbcount += elen; + if (lbcount > inode->i_size && lbcount - inode->i_size < inode->i_sb->s_blocksize) + { + nelen = elen - (lbcount - inode->i_size); + extent_trunc(inode, bloc, extoffset-adsize, eloc, etype, elen, bh, nelen); + lbcount = inode->i_size; + } + } + if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) + { + extoffset -= adsize; + lbcount -= elen; + extent_trunc(inode, bloc, extoffset, eloc, etype, elen, bh, 0); + if (!bh) + { + UDF_I_LENALLOC(inode) = extoffset - udf_file_entry_alloc_offset(inode); + mark_inode_dirty(inode); + } + else + { + struct allocExtDesc *aed = (struct allocExtDesc *)(bh->b_data); + aed->lengthAllocDescs = cpu_to_le32(extoffset - sizeof(struct allocExtDesc)); + if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201) + udf_update_tag(bh->b_data, extoffset); + else + udf_update_tag(bh->b_data, sizeof(struct allocExtDesc)); + mark_buffer_dirty_inode(bh, inode); + } + } + UDF_I_LENEXTENTS(inode) = lbcount; + + udf_release_data(bh); +} + void udf_truncate_extents(struct inode * inode) { lb_addr bloc, eloc, neloc = { 0, 0 }; --- linux-2.6.4-rc2/fs/udf/udfdecl.h 2003-09-08 13:58:59.000000000 -0700 +++ 25/fs/udf/udfdecl.h 2004-03-07 20:47:56.000000000 -0800 @@ -21,7 +21,7 @@ #define UDF_EXTENT_FLAG_MASK 0xC0000000 #define UDF_NAME_PAD 4 -#define UDF_NAME_LEN 255 +#define UDF_NAME_LEN 256 #define UDF_PATH_LEN 1023 #define udf_file_entry_alloc_offset(inode)\ @@ -59,13 +59,6 @@ struct udf_fileident_bh int eoffset; }; -struct udf_directory_record -{ - uint32_t d_parent; - uint32_t d_inode; - uint32_t d_name[255]; -}; - struct udf_vds_record { uint32_t block; @@ -81,7 +74,7 @@ struct generic_desc struct ustr { uint8_t u_cmpID; - uint8_t u_name[UDF_NAME_LEN]; + uint8_t u_name[UDF_NAME_LEN-2]; uint8_t u_len; }; @@ -116,19 +109,16 @@ extern int8_t udf_insert_aext(struct ino extern int8_t udf_delete_aext(struct inode *, lb_addr, int, lb_addr, uint32_t, struct buffer_head *); extern int8_t udf_next_aext(struct inode *, lb_addr *, int *, lb_addr *, uint32_t *, struct buffer_head **, int); extern int8_t udf_current_aext(struct inode *, lb_addr *, int *, lb_addr *, uint32_t *, struct buffer_head **, int); -extern void udf_discard_prealloc(struct inode *); /* misc.c */ extern int udf_read_tagged_data(char *, int size, int fd, int block, int partref); extern struct buffer_head *udf_tgetblk(struct super_block *, int); extern struct buffer_head *udf_tread(struct super_block *, int); -extern struct genericFormat *udf_add_extendedattr(struct inode *, uint32_t, uint32_t, uint8_t, struct buffer_head **); -extern struct genericFormat *udf_get_extendedattr(struct inode *, uint32_t, uint8_t, struct buffer_head **); +extern struct genericFormat *udf_add_extendedattr(struct inode *, uint32_t, uint32_t, uint8_t); +extern struct genericFormat *udf_get_extendedattr(struct inode *, uint32_t, uint8_t); extern struct buffer_head *udf_read_tagged(struct super_block *, uint32_t, uint32_t, uint16_t *); extern struct buffer_head *udf_read_ptagged(struct super_block *, lb_addr, uint32_t, uint16_t *); extern void udf_release_data(struct buffer_head *); -extern uint32_t udf64_low32(uint64_t); -extern uint32_t udf64_high32(uint64_t); extern void udf_update_tag(char *, int); extern void udf_new_tag(char *, uint16_t, uint16_t, uint16_t, uint32_t, int); @@ -154,6 +144,7 @@ extern void udf_free_inode(struct inode extern struct inode * udf_new_inode (struct inode *, int, int *); /* truncate.c */ +extern void udf_discard_prealloc(struct inode *); extern void udf_truncate_extents(struct inode *); /* balloc.c */ --- linux-2.6.4-rc2/fs/udf/udf_sb.h 2003-06-14 12:18:30.000000000 -0700 +++ 25/fs/udf/udf_sb.h 2004-03-07 20:47:56.000000000 -0800 @@ -64,13 +64,14 @@ static inline struct udf_sb_info *UDF_SB {\ int nr_groups = ((UDF_SB_PARTLEN((X),(Y)) + (sizeof(struct spaceBitmapDesc) << 3) +\ ((X)->s_blocksize * 8) - 1) / ((X)->s_blocksize * 8));\ - UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap = kmalloc(sizeof(struct udf_bitmap) +\ - sizeof(struct buffer_head *) * nr_groups,\ - GFP_KERNEL);\ + int size = sizeof(struct udf_bitmap) + (sizeof(struct buffer_head *) * nr_groups);\ + if (size <= PAGE_SIZE)\ + UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap = kmalloc(size, GFP_KERNEL);\ + else\ + UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap = vmalloc(size);\ if (UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap != NULL)\ {\ - memset(UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap, 0x00,\ - sizeof(struct udf_bitmap) + sizeof(struct buffer_head *) * nr_groups);\ + memset(UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap, 0x00, size);\ UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap->s_block_bitmap =\ (struct buffer_head **)(UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap + 1);\ UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap->s_nr_groups = nr_groups;\ @@ -81,6 +82,21 @@ static inline struct udf_sb_info *UDF_SB }\ } +#define UDF_SB_FREE_BITMAP(X,Y,Z)\ +{\ + int i;\ + int nr_groups = UDF_SB_BITMAP_NR_GROUPS(X,Y,Z);\ + int size = sizeof(struct udf_bitmap) + (sizeof(struct buffer_head *) * nr_groups);\ + for (i=0; is_flags & ( 1 << (Y) ) ) #define UDF_SET_FLAG(X,Y) ( UDF_SB(X)->s_flags |= ( 1 << (Y) ) ) @@ -99,7 +115,7 @@ static inline struct udf_sb_info *UDF_SB #define UDF_SB_PARTFUNC(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_partition_func ) #define UDF_SB_PARTFLAGS(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_partition_flags ) #define UDF_SB_BITMAP(X,Y,Z,I) ( UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap->s_block_bitmap[I] ) -#define UDF_SB_BITMAP_NR_GROUPS(X,Y,Z) ( UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap->s_nr_groups ) +#define UDF_SB_BITMAP_NR_GROUPS(X,Y,Z) ( UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap->s_nr_groups ) #define UDF_SB_VOLIDENT(X) ( UDF_SB(X)->s_volident ) #define UDF_SB_NUMPARTS(X) ( UDF_SB(X)->s_partitions ) --- linux-2.6.4-rc2/fs/udf/unicode.c 2003-06-14 12:18:33.000000000 -0700 +++ 25/fs/udf/unicode.c 2004-03-07 20:47:56.000000000 -0800 @@ -36,7 +36,7 @@ static int udf_translate_to_linux(uint8_ static int udf_char_to_ustr(struct ustr *dest, const uint8_t *src, int strlen) { - if ( (!dest) || (!src) || (!strlen) || (strlen >= UDF_NAME_LEN) ) + if ( (!dest) || (!src) || (!strlen) || (strlen > UDF_NAME_LEN-2) ) return 0; memset(dest, 0, sizeof(struct ustr)); memcpy(dest->u_name, src, strlen); @@ -181,14 +181,14 @@ int udf_CS0toUTF8(struct ustr *utf_o, st static int udf_UTF8toCS0(dstring *ocu, struct ustr *utf, int length) { unsigned c, i, max_val, utf_char; - int utf_cnt; - int u_len = 0; + int utf_cnt, u_len; memset(ocu, 0, sizeof(dstring) * length); ocu[0] = 8; max_val = 0xffU; try_again: + u_len = 0U; utf_char = 0U; utf_cnt = 0U; for (i = 0U; i < utf->u_len; i++) @@ -264,8 +264,8 @@ try_again: if (utf_cnt) { error_out: - printk(KERN_ERR "udf: bad UTF-8 character\n"); - return 0; + ocu[++u_len] = '?'; + printk(KERN_DEBUG "udf: bad UTF-8 character\n"); } ocu[length - 1] = (uint8_t)u_len + 1; @@ -318,21 +318,21 @@ static int udf_NLStoCS0(struct nls_table { unsigned len, i, max_val; uint16_t uni_char; - int uni_cnt; - int u_len = 0; + int u_len; memset(ocu, 0, sizeof(dstring) * length); ocu[0] = 8; max_val = 0xffU; try_again: - uni_char = 0U; - uni_cnt = 0U; + u_len = 0U; for (i = 0U; i < uni->u_len; i++) { len = nls->char2uni(&uni->u_name[i], uni->u_len-i, &uni_char); + if (len <= 0) + continue; - if (len == 2 && max_val == 0xff) + if (uni_char > max_val) { max_val = 0xffffU; ocu[0] = (uint8_t)0x10U; @@ -340,11 +340,9 @@ try_again: } if (max_val == 0xffffU) - { ocu[++u_len] = (uint8_t)(uni_char >> 8); - i++; - } ocu[++u_len] = (uint8_t)(uni_char & 0xffU); + i += len - 1; } ocu[length - 1] = (uint8_t)u_len + 1; --- linux-2.6.4-rc2/fs/ufs/dir.c 2003-06-14 12:18:30.000000000 -0700 +++ 25/fs/ufs/dir.c 2004-03-07 20:47:08.000000000 -0800 @@ -166,7 +166,6 @@ revalidate: offset = 0; brelse (bh); } - update_atime(inode); unlock_kernel(); return 0; } --- linux-2.6.4-rc2/fs/ufs/inode.c 2003-09-08 13:58:59.000000000 -0700 +++ 25/fs/ufs/inode.c 2004-03-07 20:47:39.000000000 -0800 @@ -82,7 +82,12 @@ static int ufs_block_to_path(struct inod return n; } -int ufs_frag_map(struct inode *inode, int frag) +/* + * Returns the location of the fragment from + * the begining of the filesystem. + */ + +u64 ufs_frag_map(struct inode *inode, int frag) { struct ufs_inode_info *ufsi = UFS_I(inode); struct super_block *sb = inode->i_sb; @@ -93,6 +98,9 @@ int ufs_frag_map(struct inode *inode, in int depth = ufs_block_to_path(inode, frag >> uspi->s_fpbshift, offsets); int ret = 0; u32 block; + u64 u2_block = 0; + unsigned flags = UFS_SB(sb)->s_flags; + u64 temp = 0; if (depth == 0) return 0; @@ -100,6 +108,9 @@ int ufs_frag_map(struct inode *inode, in p = offsets; lock_kernel(); + if ((flags & UFS_TYPE_MASK) == UFS_TYPE_UFS2) + goto ufs2; + block = ufsi->i_u1.i_data[*p++]; if (!block) goto out; @@ -116,6 +127,28 @@ int ufs_frag_map(struct inode *inode, in goto out; } ret = uspi->s_sbbase + fs32_to_cpu(sb, block) + (frag & uspi->s_fpbmask); + goto out; +ufs2: + u2_block = ufsi->i_u1.u2_i_data[*p++]; + if (!u2_block) + goto out; + + temp = (u64)uspi->s_sbbase + fs64_to_cpu(sb, u2_block); + + while (--depth) { + struct buffer_head *bh; + u64 n = *p++; + + bh = sb_bread(sb, temp +(n>>shift)); + if (!bh) + goto out; + u2_block = ((u64*)bh->b_data)[n & mask]; + brelse(bh); + if (!u2_block) + goto out; + } + ret = temp + (frag & uspi->s_fpbmask); + out: unlock_kernel(); return ret; @@ -132,12 +165,20 @@ static struct buffer_head * ufs_inode_ge unsigned block, blockoff, lastfrag, lastblock, lastblockoff; unsigned tmp, goal; u32 * p, * p2; + unsigned flags = 0; UFSD(("ENTER, ino %lu, fragment %u, new_fragment %u, required %u\n", inode->i_ino, fragment, new_fragment, required)) sb = inode->i_sb; uspi = UFS_SB(sb)->s_uspi; + + flags = UFS_SB(sb)->s_flags; + /* TODO : to be done for write support + if ( (flags & UFS_TYPE_MASK) == UFS_TYPE_UFS2) + goto ufs2; + */ + block = ufs_fragstoblks (fragment); blockoff = ufs_fragnum (fragment); p = ufsi->i_u1.i_data + block; @@ -230,6 +271,21 @@ repeat: mark_inode_dirty(inode); UFSD(("EXIT, result %u\n", tmp + blockoff)) return result; + + /* This part : To be implemented .... + Required only for writing, not required for READ-ONLY. +ufs2: + + u2_block = ufs_fragstoblks(fragment); + u2_blockoff = ufs_fragnum(fragment); + p = ufsi->i_u1.u2_i_data + block; + goal = 0; + +repeat2: + tmp = fs32_to_cpu(sb, *p); + lastfrag = ufsi->i_lastfrag; + + */ } static struct buffer_head * ufs_block_getfrag (struct inode *inode, @@ -308,21 +364,28 @@ out: return result; } +/* + * This function gets the block which contains the fragment. + */ + static int ufs_getfrag_block (struct inode *inode, sector_t fragment, struct buffer_head *bh_result, int create) { struct super_block * sb = inode->i_sb; struct ufs_sb_private_info * uspi = UFS_SB(sb)->s_uspi; struct buffer_head * bh; int ret, err, new; - unsigned long ptr, phys; + unsigned long ptr,phys; + u64 phys64 = 0; if (!create) { - phys = ufs_frag_map(inode, fragment); - if (phys) - map_bh(bh_result, sb, phys); + phys64 = ufs_frag_map(inode, fragment); + if (phys64) + map_bh(bh_result, sb, phys64); return 0; } + /* This code entered only while writing ....? */ + err = -EIO; new = 0; ret = 0; @@ -474,6 +537,7 @@ void ufs_read_inode (struct inode * inod struct super_block * sb; struct ufs_sb_private_info * uspi; struct ufs_inode * ufs_inode; + struct ufs2_inode *ufs2_inode; struct buffer_head * bh; mode_t mode; unsigned i; @@ -496,6 +560,9 @@ void ufs_read_inode (struct inode * inod ufs_warning (sb, "ufs_read_inode", "unable to read inode %lu\n", inode->i_ino); goto bad_inode; } + if ((flags & UFS_TYPE_MASK) == UFS_TYPE_UFS2) + goto ufs2_inode; + ufs_inode = (struct ufs_inode *) (bh->b_data + sizeof(struct ufs_inode) * ufs_inotofsbo(inode->i_ino)); /* @@ -564,6 +631,78 @@ void ufs_read_inode (struct inode * inod bad_inode: make_bad_inode(inode); return; + +ufs2_inode : + UFSD(("Reading ufs2 inode, ino %lu\n", inode->i_ino)) + + ufs2_inode = (struct ufs2_inode *)(bh->b_data + sizeof(struct ufs2_inode) * ufs_inotofsbo(inode->i_ino)); + + /* + * Copy data to the in-core inode. + */ + inode->i_mode = mode = fs16_to_cpu(sb, ufs2_inode->ui_mode); + inode->i_nlink = fs16_to_cpu(sb, ufs2_inode->ui_nlink); + if (inode->i_nlink == 0) + ufs_error (sb, "ufs_read_inode", "inode %lu has zero nlink\n", inode->i_ino); + + /* + * Linux now has 32-bit uid and gid, so we can support EFT. + */ + inode->i_uid = fs32_to_cpu(sb, ufs2_inode->ui_uid); + inode->i_gid = fs32_to_cpu(sb, ufs2_inode->ui_gid); + + inode->i_size = fs64_to_cpu(sb, ufs2_inode->ui_size); + inode->i_atime.tv_sec = fs32_to_cpu(sb, ufs2_inode->ui_atime.tv_sec); + inode->i_ctime.tv_sec = fs32_to_cpu(sb, ufs2_inode->ui_ctime.tv_sec); + inode->i_mtime.tv_sec = fs32_to_cpu(sb, ufs2_inode->ui_mtime.tv_sec); + inode->i_mtime.tv_nsec = 0; + inode->i_atime.tv_nsec = 0; + inode->i_ctime.tv_nsec = 0; + inode->i_blocks = fs64_to_cpu(sb, ufs2_inode->ui_blocks); + inode->i_blksize = PAGE_SIZE; /*This is the optimal IO size(for stat)*/ + + inode->i_version++; + ufsi->i_flags = fs32_to_cpu(sb, ufs2_inode->ui_flags); + ufsi->i_gen = fs32_to_cpu(sb, ufs2_inode->ui_gen); + /* + ufsi->i_shadow = fs32_to_cpu(sb, ufs_inode->ui_u3.ui_sun.ui_shadow); + ufsi->i_oeftflag = fs32_to_cpu(sb, ufs_inode->ui_u3.ui_sun.ui_oeftflag); + */ + ufsi->i_lastfrag= (inode->i_size + uspi->s_fsize- 1) >> uspi->s_fshift; + + if (S_ISCHR(mode) || S_ISBLK(mode) || inode->i_blocks) { + for (i = 0; i < (UFS_NDADDR + UFS_NINDIR); i++) + ufsi->i_u1.u2_i_data[i] = + ufs2_inode->ui_u2.ui_addr.ui_db[i]; + } + else { + for (i = 0; i < (UFS_NDADDR + UFS_NINDIR) * 4; i++) + ufsi->i_u1.i_symlink[i] = ufs2_inode->ui_u2.ui_symlink[i]; + } + ufsi->i_osync = 0; + + if (S_ISREG(inode->i_mode)) { + inode->i_op = &ufs_file_inode_operations; + inode->i_fop = &ufs_file_operations; + inode->i_mapping->a_ops = &ufs_aops; + } else if (S_ISDIR(inode->i_mode)) { + inode->i_op = &ufs_dir_inode_operations; + inode->i_fop = &ufs_dir_operations; + } else if (S_ISLNK(inode->i_mode)) { + if (!inode->i_blocks) + inode->i_op = &ufs_fast_symlink_inode_operations; + else { + inode->i_op = &page_symlink_inode_operations; + inode->i_mapping->a_ops = &ufs_aops; + } + } else /* TODO : here ...*/ + init_special_inode(inode, inode->i_mode, + old_decode_dev(fs32_to_cpu(sb, ufsi->i_u1.i_data[0]))); + + brelse(bh); + + UFSD(("EXIT\n")) + return; } static int ufs_update_inode(struct inode * inode, int do_sync) --- linux-2.6.4-rc2/fs/ufs/namei.c 2003-09-27 18:57:46.000000000 -0700 +++ 25/fs/ufs/namei.c 2004-03-07 20:47:39.000000000 -0800 @@ -31,7 +31,10 @@ #include #include "swab.h" /* will go away - see comment in mknod() */ +/* #undef UFS_NAMEI_DEBUG +*/ +#define UFS_NAMEI_DEBUG #ifdef UFS_NAMEI_DEBUG #define UFSD(x) printk("(%s, %d), %s: ", __FILE__, __LINE__, __FUNCTION__); printk x; --- linux-2.6.4-rc2/fs/ufs/super.c 2004-02-17 20:48:46.000000000 -0800 +++ 25/fs/ufs/super.c 2004-03-07 20:47:39.000000000 -0800 @@ -58,6 +58,9 @@ * HP/UX hfs filesystem support added by * Martin K. Petersen , August 1999 * + * UFS2 (of FreeBSD 5.x) support added by + * Niraj Kumar , Jan 2004 + * */ @@ -142,6 +145,28 @@ void ufs_print_super_stuff(struct super_ printk("\n"); } +/* + * Print contents of ufs2 ufs_super_block, useful for debugging + */ +void ufs2_print_super_stuff( + struct super_block *sb, + struct ufs_super_block *usb) +{ + printk("ufs_print_super_stuff\n"); + printk("size of usb: %u\n", sizeof(struct ufs_super_block)); + printk(" magic: 0x%x\n", fs32_to_cpu(sb, usb->fs_magic)); + printk(" fs_size: %u\n",fs64_to_cpu(sb, usb->fs_u11.fs_u2.fs_size)); + printk(" fs_dsize: %u\n",fs64_to_cpu(sb, usb->fs_u11.fs_u2.fs_dsize)); + printk(" fs_volname: %s\n", usb->fs_u11.fs_u2.fs_volname); + printk(" fs_fsmnt: %s\n", usb->fs_u11.fs_u2.fs_fsmnt); + printk(" fs_sblockloc: %u\n",fs64_to_cpu(sb, + usb->fs_u11.fs_u2.fs_sblockloc)); + printk(" cs_ndir(No of dirs): %u\n",fs64_to_cpu(sb, + usb->fs_u11.fs_u2.fs_cstotal.cs_ndir)); + printk(" cs_nbfree(No of free blocks): %u\n",fs64_to_cpu(sb, + usb->fs_u11.fs_u2.fs_cstotal.cs_nbfree)); + printk("\n"); +} /* * Print contents of ufs_cylinder_group, useful for debugging @@ -253,7 +278,7 @@ void ufs_warning (struct super_block * s enum { Opt_type_old, Opt_type_sunx86, Opt_type_sun, Opt_type_44bsd, - Opt_type_hp, Opt_type_nextstepcd, Opt_type_nextstep, + Opt_type_ufs2, Opt_type_hp, Opt_type_nextstepcd, Opt_type_nextstep, Opt_type_openstep, Opt_onerror_panic, Opt_onerror_lock, Opt_onerror_umount, Opt_onerror_repair, Opt_err }; @@ -263,6 +288,8 @@ static match_table_t tokens = { {Opt_type_sunx86, "ufstype=sunx86"}, {Opt_type_sun, "ufstype=sun"}, {Opt_type_44bsd, "ufstype=44bsd"}, + {Opt_type_ufs2, "ufstype=ufs2"}, + {Opt_type_ufs2, "ufstype=5xbsd"}, {Opt_type_hp, "ufstype=hp"}, {Opt_type_nextstepcd, "ufstype=nextstep-cd"}, {Opt_type_nextstep, "ufstype=nextstep"}, @@ -307,6 +334,10 @@ static int ufs_parse_options (char * opt ufs_clear_opt (*mount_options, UFSTYPE); ufs_set_opt (*mount_options, UFSTYPE_44BSD); break; + case Opt_type_ufs2: + ufs_clear_opt(*mount_options, UFSTYPE); + ufs_set_opt(*mount_options, UFSTYPE_UFS2); + break; case Opt_type_hp: ufs_clear_opt (*mount_options, UFSTYPE); ufs_set_opt (*mount_options, UFSTYPE_HP); @@ -356,13 +387,20 @@ static int ufs_parse_options (char * opt int ufs_read_cylinder_structures (struct super_block * sb) { struct ufs_sb_info * sbi = UFS_SB(sb); struct ufs_sb_private_info * uspi; + struct ufs_super_block *usb; struct ufs_buffer_head * ubh; unsigned char * base, * space; unsigned size, blks, i; + unsigned flags = 0; UFSD(("ENTER\n")) uspi = sbi->s_uspi; + + usb = (struct ufs_super_block *) + ((struct ufs_buffer_head *)uspi)->bh[0]->b_data; + + flags = UFS_SB(sb)->s_flags; /* * Read cs structures from (usually) first data block @@ -377,11 +415,22 @@ int ufs_read_cylinder_structures (struct size = uspi->s_bsize; if (i + uspi->s_fpb > blks) size = (blks - i) * uspi->s_fsize; - ubh = ubh_bread(sb, uspi->s_csaddr + i, size); - if (!ubh) - goto failed; - ubh_ubhcpymem (space, ubh, size); - sbi->s_csp[ufs_fragstoblks(i)] = (struct ufs_csum *)space; + + if ((flags & UFS_TYPE_MASK) == UFS_TYPE_UFS2) { + ubh = ubh_bread(sb, + fs64_to_cpu(sb, usb->fs_u11.fs_u2.fs_csaddr) + i, size); + if (!ubh) + goto failed; + ubh_ubhcpymem (space, ubh, size); + sbi->s_csp[ufs_fragstoblks(i)]=(struct ufs_csum *)space; + } + else { + ubh = ubh_bread(sb, uspi->s_csaddr + i, size); + if (!ubh) + goto failed; + ubh_ubhcpymem(space, ubh, size); + sbi->s_csp[ufs_fragstoblks(i)]=(struct ufs_csum *)space; + } space += size; ubh_brelse (ubh); ubh = NULL; @@ -480,6 +529,7 @@ static int ufs_fill_super(struct super_b struct ufs_super_block_first * usb1; struct ufs_super_block_second * usb2; struct ufs_super_block_third * usb3; + struct ufs_super_block *usb; struct ufs_buffer_head * ubh; struct inode *inode; unsigned block_size, super_block_size; @@ -520,7 +570,7 @@ static int ufs_fill_super(struct super_b if (!silent) printk("You didn't specify the type of your ufs filesystem\n\n" "mount -t ufs -o ufstype=" - "sun|sunx86|44bsd|old|hp|nextstep|netxstep-cd|openstep ...\n\n" + "sun|sunx86|44bsd|ufs2|5xbsd|old|hp|nextstep|netxstep-cd|openstep ...\n\n" ">>>WARNING<<< Wrong ufstype may corrupt your filesystem, " "default is ufstype=old\n"); ufs_set_opt (sbi->s_mount_opt, UFSTYPE_OLD); @@ -545,6 +595,19 @@ static int ufs_fill_super(struct super_b uspi->s_sbbase = 0; flags |= UFS_DE_44BSD | UFS_UID_44BSD | UFS_ST_44BSD | UFS_CG_44BSD; break; + case UFS_MOUNT_UFSTYPE_UFS2: + UFSD(("ufstype=ufs2\n")) + uspi->s_fsize = block_size = 512; + uspi->s_fmask = ~(512 - 1); + uspi->s_fshift = 9; + uspi->s_sbsize = super_block_size = 1536; + uspi->s_sbbase = 0; + flags |= UFS_TYPE_UFS2 | UFS_DE_44BSD | UFS_UID_44BSD | UFS_ST_44BSD | UFS_CG_44BSD; + if (!(sb->s_flags & MS_RDONLY)) { + printk(KERN_INFO "ufstype=ufs2 is supported read-only\n"); + sb->s_flags |= MS_RDONLY; + } + break; case UFS_MOUNT_UFSTYPE_SUN: UFSD(("ufstype=sun\n")) @@ -657,27 +720,37 @@ again: /* * read ufs super block from device */ - ubh = ubh_bread_uspi (uspi, sb, uspi->s_sbbase + UFS_SBLOCK/block_size, super_block_size); + if ( (flags & UFS_TYPE_MASK) == UFS_TYPE_UFS2) { + ubh = ubh_bread_uspi(uspi, sb, uspi->s_sbbase + SBLOCK_UFS2/block_size, super_block_size); + } + else { + ubh = ubh_bread_uspi(uspi, sb, uspi->s_sbbase + UFS_SBLOCK/block_size, super_block_size); + } if (!ubh) - goto failed; + goto failed; + usb1 = ubh_get_usb_first(USPI_UBH); usb2 = ubh_get_usb_second(USPI_UBH); usb3 = ubh_get_usb_third(USPI_UBH); + usb = (struct ufs_super_block *) + ((struct ufs_buffer_head *)uspi)->bh[0]->b_data ; /* * Check ufs magic number */ - switch (__constant_le32_to_cpu(usb3->fs_magic)) { + switch ((uspi->fs_magic = __constant_le32_to_cpu(usb3->fs_magic))) { case UFS_MAGIC: + case UFS2_MAGIC: case UFS_MAGIC_LFN: case UFS_MAGIC_FEA: case UFS_MAGIC_4GB: sbi->s_bytesex = BYTESEX_LE; goto magic_found; } - switch (__constant_be32_to_cpu(usb3->fs_magic)) { + switch ((uspi->fs_magic = __constant_be32_to_cpu(usb3->fs_magic))) { case UFS_MAGIC: + case UFS2_MAGIC: case UFS_MAGIC_LFN: case UFS_MAGIC_FEA: case UFS_MAGIC_4GB: @@ -748,7 +821,10 @@ magic_found: } #ifdef UFS_SUPER_DEBUG_MORE - ufs_print_super_stuff(sb, usb1, usb2, usb3); + if ((flags & UFS_TYPE_MASK) == UFS_TYPE_UFS2) + ufs2_print_super_stuff(sb,usb); + else + ufs_print_super_stuff(sb, usb1, usb2, usb3); #endif /* @@ -802,8 +878,16 @@ magic_found: uspi->s_dblkno = fs32_to_cpu(sb, usb1->fs_dblkno); uspi->s_cgoffset = fs32_to_cpu(sb, usb1->fs_cgoffset); uspi->s_cgmask = fs32_to_cpu(sb, usb1->fs_cgmask); - uspi->s_size = fs32_to_cpu(sb, usb1->fs_size); - uspi->s_dsize = fs32_to_cpu(sb, usb1->fs_dsize); + + if ((flags & UFS_TYPE_MASK) == UFS_TYPE_UFS2) { + uspi->s_u2_size = fs64_to_cpu(sb, usb->fs_u11.fs_u2.fs_size); + uspi->s_u2_dsize = fs64_to_cpu(sb, usb->fs_u11.fs_u2.fs_dsize); + } + else { + uspi->s_size = fs32_to_cpu(sb, usb1->fs_size); + uspi->s_dsize = fs32_to_cpu(sb, usb1->fs_dsize); + } + uspi->s_ncg = fs32_to_cpu(sb, usb1->fs_ncg); /* s_bsize already set */ /* s_fsize already set */ @@ -1021,21 +1105,36 @@ int ufs_statfs (struct super_block * sb, { struct ufs_sb_private_info * uspi; struct ufs_super_block_first * usb1; + struct ufs_super_block * usb; + unsigned flags = 0; lock_kernel(); uspi = UFS_SB(sb)->s_uspi; usb1 = ubh_get_usb_first (USPI_UBH); + usb = (struct ufs_super_block *) + ((struct ufs_buffer_head *)uspi)->bh[0]->b_data ; - buf->f_type = UFS_MAGIC; + flags = UFS_SB(sb)->s_flags; + if ((flags & UFS_TYPE_MASK) == UFS_TYPE_UFS2) { + buf->f_type = UFS2_MAGIC; + buf->f_blocks = usb->fs_u11.fs_u2.fs_dsize; + buf->f_bfree = ufs_blkstofrags(fs64_to_cpu(sb, usb->fs_u11.fs_u2.fs_cstotal.cs_nbfree)) + + fs64_to_cpu(sb, usb->fs_u11.fs_u2.fs_cstotal.cs_nffree); + buf->f_ffree = fs64_to_cpu(sb, + usb->fs_u11.fs_u2.fs_cstotal.cs_nifree); + } + else { + buf->f_type = UFS_MAGIC; + buf->f_blocks = uspi->s_dsize; + buf->f_bfree = ufs_blkstofrags(fs32_to_cpu(sb, usb1->fs_cstotal.cs_nbfree)) + + fs32_to_cpu(sb, usb1->fs_cstotal.cs_nffree); + buf->f_ffree = fs32_to_cpu(sb, usb1->fs_cstotal.cs_nifree); + } buf->f_bsize = sb->s_blocksize; - buf->f_blocks = uspi->s_dsize; - buf->f_bfree = ufs_blkstofrags(fs32_to_cpu(sb, usb1->fs_cstotal.cs_nbfree)) + - fs32_to_cpu(sb, usb1->fs_cstotal.cs_nffree); buf->f_bavail = (buf->f_bfree > (((long)buf->f_blocks / 100) * uspi->s_minfree)) ? (buf->f_bfree - (((long)buf->f_blocks / 100) * uspi->s_minfree)) : 0; buf->f_files = uspi->s_ncg * uspi->s_ipg; - buf->f_ffree = fs32_to_cpu(sb, usb1->fs_cstotal.cs_nifree); buf->f_namelen = UFS_MAXNAMLEN; unlock_kernel(); --- linux-2.6.4-rc2/fs/ufs/util.c 2003-06-14 12:18:08.000000000 -0700 +++ 25/fs/ufs/util.c 2004-03-07 20:47:39.000000000 -0800 @@ -24,10 +24,11 @@ struct ufs_buffer_head * _ubh_bread_ (struct ufs_sb_private_info * uspi, - struct super_block *sb, unsigned fragment, unsigned size) + struct super_block *sb, u64 fragment, u64 size) { struct ufs_buffer_head * ubh; - unsigned i, j, count; + unsigned i, j ; + u64 count = 0; if (size & ~uspi->s_fmask) return NULL; count = size >> uspi->s_fshift; @@ -53,9 +54,10 @@ failed: } struct ufs_buffer_head * ubh_bread_uspi (struct ufs_sb_private_info * uspi, - struct super_block *sb, unsigned fragment, unsigned size) + struct super_block *sb, u64 fragment, u64 size) { - unsigned i, j, count; + unsigned i, j; + u64 count = 0; if (size & ~uspi->s_fmask) return NULL; count = size >> uspi->s_fshift; --- linux-2.6.4-rc2/fs/ufs/util.h 2003-06-14 12:18:21.000000000 -0700 +++ 25/fs/ufs/util.h 2004-03-07 20:47:39.000000000 -0800 @@ -228,8 +228,8 @@ ufs_set_inode_gid(struct super_block *sb * These functions manipulate ufs buffers */ #define ubh_bread(sb,fragment,size) _ubh_bread_(uspi,sb,fragment,size) -extern struct ufs_buffer_head * _ubh_bread_(struct ufs_sb_private_info *, struct super_block *, unsigned, unsigned); -extern struct ufs_buffer_head * ubh_bread_uspi(struct ufs_sb_private_info *, struct super_block *, unsigned, unsigned); +extern struct ufs_buffer_head * _ubh_bread_(struct ufs_sb_private_info *, struct super_block *, u64 , u64); +extern struct ufs_buffer_head * ubh_bread_uspi(struct ufs_sb_private_info *, struct super_block *, u64, u64); extern void ubh_brelse (struct ufs_buffer_head *); extern void ubh_brelse_uspi (struct ufs_sb_private_info *); extern void ubh_mark_buffer_dirty (struct ufs_buffer_head *); --- linux-2.6.4-rc2/fs/xfs/linux/xfs_aops.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/fs/xfs/linux/xfs_aops.c 2004-03-07 20:48:22.000000000 -0800 @@ -1032,7 +1032,8 @@ linvfs_direct_IO( if (error) return -error; - return blockdev_direct_IO(rw, iocb, inode, iomap.iomap_target->pbr_bdev, + return blockdev_direct_IO_no_locking(rw, iocb, inode, + iomap.iomap_target->pbr_bdev, iov, offset, nr_segs, linvfs_get_blocks_direct, linvfs_unwritten_convert_direct); --- linux-2.6.4-rc2/fs/xfs/linux/xfs_super.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/fs/xfs/linux/xfs_super.c 2004-03-07 20:46:46.000000000 -0800 @@ -247,7 +247,7 @@ xfs_flush_inode( { struct inode *inode = LINVFS_GET_IP(XFS_ITOV(ip)); - filemap_fdatawrite(inode->i_mapping); + filemap_flush(inode->i_mapping); } void --- linux-2.6.4-rc2/fs/xfs/xfs_log.c 2004-03-03 23:12:46.000000000 -0800 +++ 25/fs/xfs/xfs_log.c 2004-03-07 20:46:46.000000000 -0800 @@ -1227,7 +1227,7 @@ xlog_alloc_log(xfs_mount_t *mp, kmem_zalloc(sizeof(xlog_in_core_t), KM_SLEEP); iclog = *iclogp; iclog->hic_data = (xlog_in_core_2_t *) - kmem_alloc(iclogsize, KM_SLEEP); + kmem_zalloc(iclogsize, KM_SLEEP); iclog->ic_prev = prev_iclog; prev_iclog = iclog; --- linux-2.6.4-rc2/include/acpi/acconfig.h 2004-03-03 23:12:46.000000000 -0800 +++ 25/include/acpi/acconfig.h 2004-03-07 20:46:46.000000000 -0800 @@ -64,7 +64,7 @@ /* Version string */ -#define ACPI_CA_VERSION 0x20040211 +#define ACPI_CA_VERSION 0x20040220 /* Maximum objects in the various object caches */ --- linux-2.6.4-rc2/include/acpi/acglobal.h 2004-02-03 20:42:38.000000000 -0800 +++ 25/include/acpi/acglobal.h 2004-03-07 20:46:46.000000000 -0800 @@ -57,6 +57,12 @@ #define ACPI_EXTERN extern #endif +/* + * Keep local copies of these FADT-based registers. NOTE: These globals + * are first in this file for alignment reasons on 64-bit systems. + */ +ACPI_EXTERN struct acpi_generic_address acpi_gbl_xpm1a_enable; +ACPI_EXTERN struct acpi_generic_address acpi_gbl_xpm1b_enable; /***************************************************************************** * @@ -97,6 +103,11 @@ ACPI_EXTERN FADT_DESCRIPTOR * ACPI_EXTERN struct acpi_table_header *acpi_gbl_DSDT; ACPI_EXTERN FACS_DESCRIPTOR *acpi_gbl_FACS; ACPI_EXTERN struct acpi_common_facs acpi_gbl_common_fACS; +/* + * Since there may be multiple SSDTs and PSDTS, a single pointer is not + * sufficient; Therefore, there isn't one! + */ + /* * Handle both ACPI 1.0 and ACPI 2.0 Integer widths @@ -107,17 +118,6 @@ ACPI_EXTERN u8 ACPI_EXTERN u8 acpi_gbl_integer_byte_width; ACPI_EXTERN u8 acpi_gbl_integer_nybble_width; -/* Keep local copies of these FADT-based registers */ - -ACPI_EXTERN struct acpi_generic_address acpi_gbl_xpm1a_enable; -ACPI_EXTERN struct acpi_generic_address acpi_gbl_xpm1b_enable; - -/* - * Since there may be multiple SSDTs and PSDTS, a single pointer is not - * sufficient; Therefore, there isn't one! - */ - - /* * ACPI Table info arrays */ @@ -165,7 +165,8 @@ ACPI_EXTERN u8 extern u8 acpi_gbl_shutdown; extern u32 acpi_gbl_startup_flags; extern const u8 acpi_gbl_decode_to8bit[8]; -extern const char *acpi_gbl_db_sleep_states[ACPI_S_STATE_COUNT]; +extern const char *acpi_gbl_sleep_state_names[ACPI_S_STATE_COUNT]; +extern const char *acpi_gbl_highest_dstate_names[4]; extern const struct acpi_opcode_info acpi_gbl_aml_op_info[AML_NUM_OPCODES]; extern const char *acpi_gbl_region_types[ACPI_NUM_PREDEFINED_REGIONS]; --- linux-2.6.4-rc2/include/acpi/acpixf.h 2004-03-03 23:12:46.000000000 -0800 +++ 25/include/acpi/acpixf.h 2004-03-07 20:48:07.000000000 -0800 @@ -450,7 +450,7 @@ acpi_status asmlinkage acpi_enter_sleep_state ( u8 sleep_state); -acpi_status +acpi_status asmlinkage acpi_enter_sleep_state_s4bios ( void); --- linux-2.6.4-rc2/include/acpi/actypes.h 2004-02-03 20:42:38.000000000 -0800 +++ 25/include/acpi/actypes.h 2004-03-07 20:46:46.000000000 -0800 @@ -880,7 +880,8 @@ struct acpi_device_info { ACPI_COMMON_OBJ_INFO; - u32 valid; /* Indicates which fields are valid */ + u8 highest_dstates[4]; /* _sx_d values 0xFF indicates not valid */ + u32 valid; /* Indicates which fields below are valid */ u32 current_status; /* _STA value */ acpi_integer address; /* _ADR value if any */ struct acpi_device_id hardware_id; /* _HID value if any */ --- linux-2.6.4-rc2/include/acpi/acutils.h 2004-02-03 20:42:38.000000000 -0800 +++ 25/include/acpi/acutils.h 2004-03-07 20:46:46.000000000 -0800 @@ -508,6 +508,10 @@ acpi_ut_execute_UID ( struct acpi_namespace_node *device_node, struct acpi_device_id *uid); +acpi_status +acpi_ut_execute_sxds ( + struct acpi_namespace_node *device_node, + u8 *highest); /* * ut_mutex - mutual exclusion interfaces --- linux-2.6.4-rc2/include/asm-alpha/irq.h 2003-08-08 22:55:13.000000000 -0700 +++ 25/include/asm-alpha/irq.h 2004-03-07 20:48:21.000000000 -0800 @@ -93,5 +93,8 @@ extern void enable_irq(unsigned int); struct pt_regs; extern void (*perf_irq)(unsigned long, struct pt_regs *); +struct irqaction; +int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); + #endif /* _ALPHA_IRQ_H */ --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/include/asm-alpha/lockmeter.h 2004-03-07 20:48:17.000000000 -0800 @@ -0,0 +1,84 @@ +/* + * Written by John Hawkes (hawkes@sgi.com) + * Based on klstat.h by Jack Steiner (steiner@sgi.com) + * + * Modified by Peter Rival (frival@zk3.dec.com) + */ + +#ifndef _ALPHA_LOCKMETER_H +#define _ALPHA_LOCKMETER_H + +#include +#define CPU_CYCLE_FREQUENCY hwrpb->cycle_freq + +#define get_cycles64() get_cycles() + +#define THIS_CPU_NUMBER smp_processor_id() + +#include + +#define SPINLOCK_MAGIC_INIT /**/ + +/* + * Macros to cache and retrieve an index value inside of a lock + * these macros assume that there are less than 65536 simultaneous + * (read mode) holders of a rwlock. + * We also assume that the hash table has less than 32767 entries. + * the high order bit is used for write locking a rw_lock + * Note: although these defines and macros are the same as what is being used + * in include/asm-i386/lockmeter.h, they are present here to easily + * allow an alternate Alpha implementation. + */ +/* + * instrumented spinlock structure -- never used to allocate storage + * only used in macros below to overlay a spinlock_t + */ +typedef struct inst_spinlock_s { + /* remember, Alpha is little endian */ + unsigned short lock; + unsigned short index; +} inst_spinlock_t; +#define PUT_INDEX(lock_ptr,indexv) ((inst_spinlock_t *)(lock_ptr))->index = indexv +#define GET_INDEX(lock_ptr) ((inst_spinlock_t *)(lock_ptr))->index + +/* + * macros to cache and retrieve an index value in a read/write lock + * as well as the cpu where a reader busy period started + * we use the 2nd word (the debug word) for this, so require the + * debug word to be present + */ +/* + * instrumented rwlock structure -- never used to allocate storage + * only used in macros below to overlay a rwlock_t + */ +typedef struct inst_rwlock_s { + volatile int lock; + unsigned short index; + unsigned short cpu; +} inst_rwlock_t; +#define PUT_RWINDEX(rwlock_ptr,indexv) ((inst_rwlock_t *)(rwlock_ptr))->index = indexv +#define GET_RWINDEX(rwlock_ptr) ((inst_rwlock_t *)(rwlock_ptr))->index +#define PUT_RW_CPU(rwlock_ptr,cpuv) ((inst_rwlock_t *)(rwlock_ptr))->cpu = cpuv +#define GET_RW_CPU(rwlock_ptr) ((inst_rwlock_t *)(rwlock_ptr))->cpu + +/* + * return true if rwlock is write locked + * (note that other lock attempts can cause the lock value to be negative) + */ +#define RWLOCK_IS_WRITE_LOCKED(rwlock_ptr) (((inst_rwlock_t *)rwlock_ptr)->lock & 1) +#define IABS(x) ((x) > 0 ? (x) : -(x)) + +#define RWLOCK_READERS(rwlock_ptr) rwlock_readers(rwlock_ptr) +extern inline int rwlock_readers(rwlock_t *rwlock_ptr) +{ + int tmp = (int) ((inst_rwlock_t *)rwlock_ptr)->lock; + /* readers subtract 2, so we have to: */ + /* - andnot off a possible writer (bit 0) */ + /* - get the absolute value */ + /* - divide by 2 (right shift by one) */ + /* to find the number of readers */ + if (tmp == 0) return(0); + else return(IABS(tmp & ~1)>>1); +} + +#endif /* _ALPHA_LOCKMETER_H */ --- linux-2.6.4-rc2/include/asm-alpha/pci.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/asm-alpha/pci.h 2004-03-07 20:46:59.000000000 -0800 @@ -88,7 +88,7 @@ extern void pci_free_consistent(struct p /* Map a single buffer of the indicate size for PCI DMA in streaming mode. The 32-bit PCI bus mastering address to use is returned. Once the device is given the dma address, the device owns this memory - until either pci_unmap_single or pci_dma_sync_single is performed. */ + until either pci_unmap_single or pci_dma_sync_single_for_cpu is performed. */ extern dma_addr_t pci_map_single(struct pci_dev *, void *, size_t, int); @@ -142,28 +142,44 @@ extern int pci_map_sg(struct pci_dev *, extern void pci_unmap_sg(struct pci_dev *, struct scatterlist *, int, int); /* Make physical memory consistent for a single streaming mode DMA - translation after a transfer. + translation after a transfer and device currently has ownership + of the buffer. If you perform a pci_map_single() but wish to interrogate the buffer using the cpu, yet do not wish to teardown the PCI dma mapping, you must call this function before doing so. At the next - point you give the PCI dma address back to the card, the device - again owns the buffer. */ + point you give the PCI dma address back to the card, you must first + perform a pci_dma_sync_for_device, and then the device again owns + the buffer. */ static inline void -pci_dma_sync_single(struct pci_dev *dev, dma_addr_t dma_addr, long size, - int direction) +pci_dma_sync_single_for_cpu(struct pci_dev *dev, dma_addr_t dma_addr, long size, + int direction) +{ + /* Nothing to do. */ +} + +static inline void +pci_dma_sync_single_for_device(struct pci_dev *dev, dma_addr_t dma_addr, long size, + int direction) { /* Nothing to do. */ } /* Make physical memory consistent for a set of streaming mode DMA - translations after a transfer. The same as pci_dma_sync_single but - for a scatter-gather list, same rules and usage. */ + translations after a transfer. The same as pci_dma_sync_single_* + but for a scatter-gather list, same rules and usage. */ + +static inline void +pci_dma_sync_sg_for_cpu(struct pci_dev *dev, struct scatterlist *sg, int nents, + int direction) +{ + /* Nothing to do. */ +} static inline void -pci_dma_sync_sg(struct pci_dev *dev, struct scatterlist *sg, int nents, - int direction) +pci_dma_sync_sg_for_device(struct pci_dev *dev, struct scatterlist *sg, int nents, + int direction) { /* Nothing to do. */ } @@ -184,8 +200,14 @@ extern dma64_addr_t pci_dac_page_to_dma( extern struct page *pci_dac_dma_to_page(struct pci_dev *, dma64_addr_t); extern unsigned long pci_dac_dma_to_offset(struct pci_dev *, dma64_addr_t); -static __inline__ void -pci_dac_dma_sync_single(struct pci_dev *pdev, dma64_addr_t dma_addr, size_t len, int direction) +static inline void +pci_dac_dma_sync_single_for_cpu(struct pci_dev *pdev, dma64_addr_t dma_addr, size_t len, int direction) +{ + /* Nothing to do. */ +} + +static inline void +pci_dac_dma_sync_single_for_device(struct pci_dev *pdev, dma64_addr_t dma_addr, size_t len, int direction) { /* Nothing to do. */ } --- linux-2.6.4-rc2/include/asm-alpha/spinlock.h 2003-11-09 16:45:05.000000000 -0800 +++ 25/include/asm-alpha/spinlock.h 2004-03-07 20:48:17.000000000 -0800 @@ -6,6 +6,10 @@ #include #include +#ifdef CONFIG_LOCKMETER +#undef DEBUG_SPINLOCK +#undef DEBUG_RWLOCK +#endif /* * Simple spin lock operations. There are two variants, one clears IRQ's @@ -95,9 +99,18 @@ static inline int _raw_spin_trylock(spin typedef struct { volatile int write_lock:1, read_counter:31; +#ifdef CONFIG_LOCKMETER + /* required for LOCKMETER since all bits in lock are used */ + /* need this storage for CPU and lock INDEX ............. */ + unsigned magic; +#endif } /*__attribute__((aligned(32)))*/ rwlock_t; +#ifdef CONFIG_LOCKMETER +#define RW_LOCK_UNLOCKED (rwlock_t) { 0, 0, 0 } +#else #define RW_LOCK_UNLOCKED (rwlock_t) { 0, 0 } +#endif #define rwlock_init(x) do { *(x) = RW_LOCK_UNLOCKED; } while(0) #define rwlock_is_locked(x) (*(volatile int *)(x) != 0) @@ -169,4 +182,41 @@ static inline void _raw_read_unlock(rwlo : "m" (*lock) : "memory"); } +#ifdef CONFIG_LOCKMETER +static inline int _raw_write_trylock(rwlock_t *lock) +{ + long temp,result; + + __asm__ __volatile__( + " ldl_l %1,%0\n" + " mov $31,%2\n" + " bne %1,1f\n" + " or $31,1,%2\n" + " stl_c %2,%0\n" + "1: mb\n" + : "=m" (*(volatile int *)lock), "=&r" (temp), "=&r" (result) + : "m" (*(volatile int *)lock) + ); + + return (result); +} + +static inline int _raw_read_trylock(rwlock_t *lock) +{ + unsigned long temp,result; + + __asm__ __volatile__( + " ldl_l %1,%0\n" + " mov $31,%2\n" + " blbs %1,1f\n" + " subl %1,2,%2\n" + " stl_c %2,%0\n" + "1: mb\n" + : "=m" (*(volatile int *)lock), "=&r" (temp), "=&r" (result) + : "m" (*(volatile int *)lock) + ); + return (result); +} +#endif /* CONFIG_LOCKMETER */ + #endif /* _ALPHA_SPINLOCK_H */ --- linux-2.6.4-rc2/include/asm-arm26/irq.h 2003-06-14 12:18:29.000000000 -0700 +++ 25/include/asm-arm26/irq.h 2004-03-07 20:48:21.000000000 -0800 @@ -45,6 +45,8 @@ extern void enable_irq(unsigned int); int set_irq_type(unsigned int irq, unsigned int type); int setup_irq(unsigned int, struct irqaction *); +struct pt_regs; +int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); #endif --- linux-2.6.4-rc2/include/asm-arm/dma-mapping.h 2004-03-03 23:12:46.000000000 -0800 +++ 25/include/asm-arm/dma-mapping.h 2004-03-07 20:46:59.000000000 -0800 @@ -26,8 +26,10 @@ dma_addr_t sa1111_map_single(struct devi void sa1111_unmap_single(struct device *dev, dma_addr_t, size_t, enum dma_data_direction); int sa1111_map_sg(struct device *dev, struct scatterlist *, int, enum dma_data_direction); void sa1111_unmap_sg(struct device *dev, struct scatterlist *, int, enum dma_data_direction); -void sa1111_dma_sync_single(struct device *dev, dma_addr_t, size_t, enum dma_data_direction); -void sa1111_dma_sync_sg(struct device *dev, struct scatterlist *, int, enum dma_data_direction); +void sa1111_dma_sync_single_for_cpu(struct device *dev, dma_addr_t, size_t, enum dma_data_direction); +void sa1111_dma_sync_single_for_device(struct device *dev, dma_addr_t, size_t, enum dma_data_direction); +void sa1111_dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *, int, enum dma_data_direction); +void sa1111_dma_sync_sg_for_device(struct device *dev, struct scatterlist *, int, enum dma_data_direction); #ifdef CONFIG_SA1111 @@ -115,7 +117,8 @@ dma_free_coherent(struct device *dev, si * or written back. * * The device owns this memory once this call has completed. The CPU - * can regain ownership by calling dma_unmap_single() or dma_sync_single(). + * can regain ownership by calling dma_unmap_single() or + * dma_sync_single_for_cpu(). */ static inline dma_addr_t dma_map_single(struct device *dev, void *cpu_addr, size_t size, @@ -140,7 +143,8 @@ dma_map_single(struct device *dev, void * or written back. * * The device owns this memory once this call has completed. The CPU - * can regain ownership by calling dma_unmap_page() or dma_sync_single(). + * can regain ownership by calling dma_unmap_page() or + * dma_sync_single_for_cpu(). */ static inline dma_addr_t dma_map_page(struct device *dev, struct page *page, @@ -204,7 +208,7 @@ dma_unmap_page(struct device *dev, dma_a * * Map a set of buffers described by scatterlist in streaming * mode for DMA. This is the scatter-gather version of the - * above pci_map_single interface. Here the scatter gather list + * above dma_map_single interface. Here the scatter gather list * elements are each tagged with the appropriate dma address * and length. They are obtained via sg_dma_{address,length}(SG). * @@ -214,7 +218,7 @@ dma_unmap_page(struct device *dev, dma_a * The routine returns the number of addr/length pairs actually * used, at most nents. * - * Device ownership issues as mentioned above for pci_map_single are + * Device ownership issues as mentioned above for dma_map_single are * the same here. */ static inline int @@ -246,7 +250,7 @@ dma_map_sg(struct device *dev, struct sc * * Unmap a set of streaming mode DMA translations. * Again, CPU read rules concerning calls here are the same as for - * pci_unmap_single() above. + * dma_unmap_single() above. */ static inline void dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nents, @@ -261,7 +265,7 @@ dma_unmap_sg(struct device *dev, struct } /** - * dma_sync_single + * dma_sync_single_for_cpu * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices * @handle: DMA address of buffer * @size: size of buffer to map @@ -270,18 +274,31 @@ dma_unmap_sg(struct device *dev, struct * Make physical memory consistent for a single streaming mode DMA * translation after a transfer. * - * If you perform a pci_map_single() but wish to interrogate the + * If you perform a dma_map_single() but wish to interrogate the * buffer using the cpu, yet do not wish to teardown the PCI dma * mapping, you must call this function before doing so. At the - * next point you give the PCI dma address back to the card, the + * next point you give the PCI dma address back to the card, you + * must first the perform a dma_sync_for_device, and then the * device again owns the buffer. */ static inline void -dma_sync_single(struct device *dev, dma_addr_t handle, size_t size, - enum dma_data_direction dir) +dma_sync_single_for_cpu(struct device *dev, dma_addr_t handle, size_t size, + enum dma_data_direction dir) { if (dmadev_is_sa1111(dev)) { - sa1111_dma_sync_single(dev, handle, size, dir); + sa1111_dma_sync_single_for_cpu(dev, handle, size, dir); + return; + } + + consistent_sync((void *)__bus_to_virt(handle), size, dir); +} + +static inline void +dma_sync_single_for_device(struct device *dev, dma_addr_t handle, size_t size, + enum dma_data_direction dir) +{ + if (dmadev_is_sa1111(dev)) { + sa1111_dma_sync_single_for_device(dev, handle, size, dir); return; } @@ -289,7 +306,7 @@ dma_sync_single(struct device *dev, dma_ } /** - * dma_sync_sg + * dma_sync_sg_for_cpu * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices * @sg: list of buffers * @nents: number of buffers to map @@ -298,17 +315,34 @@ dma_sync_single(struct device *dev, dma_ * Make physical memory consistent for a set of streaming * mode DMA translations after a transfer. * - * The same as pci_dma_sync_single but for a scatter-gather list, + * The same as dma_sync_single_for_* but for a scatter-gather list, * same rules and usage. */ static inline void -dma_sync_sg(struct device *dev, struct scatterlist *sg, int nents, - enum dma_data_direction dir) +dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nents, + enum dma_data_direction dir) +{ + int i; + + if (dmadev_is_sa1111(dev)) { + sa1111_dma_sync_sg_for_cpu(dev, sg, nents, dir); + return; + } + + for (i = 0; i < nents; i++, sg++) { + char *virt = page_address(sg->page) + sg->offset; + consistent_sync(virt, sg->length, dir); + } +} + +static inline void +dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nents, + enum dma_data_direction dir) { int i; if (dmadev_is_sa1111(dev)) { - sa1111_dma_sync_sg(dev, sg, nents, dir); + sa1111_dma_sync_sg_for_device(dev, sg, nents, dir); return; } --- linux-2.6.4-rc2/include/asm-arm/irq.h 2003-06-14 12:18:52.000000000 -0700 +++ 25/include/asm-arm/irq.h 2004-03-07 20:48:21.000000000 -0800 @@ -44,5 +44,9 @@ void disable_irq_wake(unsigned int irq); void enable_irq_wake(unsigned int irq); int setup_irq(unsigned int, struct irqaction *); +struct irqaction; +struct pt_regs; +int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); + #endif --- linux-2.6.4-rc2/include/asm-cris/irq.h 2003-07-10 18:50:32.000000000 -0700 +++ 25/include/asm-cris/irq.h 2004-03-07 20:48:21.000000000 -0800 @@ -14,6 +14,10 @@ extern void enable_irq(unsigned int); #define disable_irq_nosync disable_irq #define enable_irq_nosync enable_irq +struct irqaction; +struct pt_regs; +int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); + #endif /* _ASM_IRQ_H */ --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/include/asm-generic/compat_signal.h 2004-03-07 20:47:06.000000000 -0800 @@ -0,0 +1,25 @@ +#ifndef _ASM_GENERIC_COMPAT_SIGNAL_H +#define _ASM_GENERIC_COMPAT_SIGNAL_H + +#ifndef __ASSEMBLY__ +#include + +typedef compat_uptr_t compat_sighandler_t; + +typedef struct compat_sigaltstack { + compat_uptr_t ss_sp; + compat_int_t ss_flags; + compat_size_t ss_size; +} compat_stack_t; + +/* Most things should be clean enough to redefine this at will, if care + is taken to make libc match. */ + +struct compat_sigaction { + compat_sighandler_t sa_handler; + compat_uint_t sa_flags; + compat_sigset_t sa_mask; /* mask last for extensibility */ +}; + +#endif /* !__ASSEMBLY__ */ +#endif /* !_ASM_GENERIC_COMPAT_SIGNAL_H */ --- linux-2.6.4-rc2/include/asm-generic/dma-mapping.h 2003-06-14 12:18:33.000000000 -0700 +++ 25/include/asm-generic/dma-mapping.h 2004-03-07 20:46:59.000000000 -0800 @@ -103,21 +103,41 @@ dma_unmap_sg(struct device *dev, struct } static inline void -dma_sync_single(struct device *dev, dma_addr_t dma_handle, size_t size, - enum dma_data_direction direction) +dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, size_t size, + enum dma_data_direction direction) { BUG_ON(dev->bus != &pci_bus_type); - pci_dma_sync_single(to_pci_dev(dev), dma_handle, size, (int)direction); + pci_dma_sync_single_for_cpu(to_pci_dev(dev), dma_handle, + size, (int)direction); } static inline void -dma_sync_sg(struct device *dev, struct scatterlist *sg, int nelems, - enum dma_data_direction direction) +dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, size_t size, + enum dma_data_direction direction) { BUG_ON(dev->bus != &pci_bus_type); - pci_dma_sync_sg(to_pci_dev(dev), sg, nelems, (int)direction); + pci_dma_sync_single_for_device(to_pci_dev(dev), dma_handle, + size, (int)direction); +} + +static inline void +dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems, + enum dma_data_direction direction) +{ + BUG_ON(dev->bus != &pci_bus_type); + + pci_dma_sync_sg_for_cpu(to_pci_dev(dev), sg, nelems, (int)direction); +} + +static inline void +dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems, + enum dma_data_direction direction) +{ + BUG_ON(dev->bus != &pci_bus_type); + + pci_dma_sync_sg_for_device(to_pci_dev(dev), sg, nelems, (int)direction); } /* Now for the API extensions over the pci_ one */ @@ -135,12 +155,21 @@ dma_get_cache_alignment(void) } static inline void -dma_sync_single_range(struct device *dev, dma_addr_t dma_handle, - unsigned long offset, size_t size, - enum dma_data_direction direction) +dma_sync_single_range_for_cpu(struct device *dev, dma_addr_t dma_handle, + unsigned long offset, size_t size, + enum dma_data_direction direction) +{ + /* just sync everything, that's all the pci API can do */ + dma_sync_single_for_cpu(dev, dma_handle, offset+size, direction); +} + +static inline void +dma_sync_single_range_for_device(struct device *dev, dma_addr_t dma_handle, + unsigned long offset, size_t size, + enum dma_data_direction direction) { /* just sync everything, that's all the pci API can do */ - dma_sync_single(dev, dma_handle, offset+size, direction); + dma_sync_single_for_device(dev, dma_handle, offset+size, direction); } static inline void --- linux-2.6.4-rc2/include/asm-generic/pci-dma-compat.h 2003-06-14 12:17:59.000000000 -0700 +++ 25/include/asm-generic/pci-dma-compat.h 2004-03-07 20:46:59.000000000 -0800 @@ -71,17 +71,31 @@ pci_unmap_sg(struct pci_dev *hwdev, stru } static inline void -pci_dma_sync_single(struct pci_dev *hwdev, dma_addr_t dma_handle, +pci_dma_sync_single_for_cpu(struct pci_dev *hwdev, dma_addr_t dma_handle, size_t size, int direction) { - dma_sync_single(hwdev == NULL ? NULL : &hwdev->dev, dma_handle, size, (enum dma_data_direction)direction); + dma_sync_single_for_cpu(hwdev == NULL ? NULL : &hwdev->dev, dma_handle, size, (enum dma_data_direction)direction); } static inline void -pci_dma_sync_sg(struct pci_dev *hwdev, struct scatterlist *sg, +pci_dma_sync_single_for_device(struct pci_dev *hwdev, dma_addr_t dma_handle, + size_t size, int direction) +{ + dma_sync_single_for_device(hwdev == NULL ? NULL : &hwdev->dev, dma_handle, size, (enum dma_data_direction)direction); +} + +static inline void +pci_dma_sync_sg_for_cpu(struct pci_dev *hwdev, struct scatterlist *sg, + int nelems, int direction) +{ + dma_sync_sg_for_cpu(hwdev == NULL ? NULL : &hwdev->dev, sg, nelems, (enum dma_data_direction)direction); +} + +static inline void +pci_dma_sync_sg_for_device(struct pci_dev *hwdev, struct scatterlist *sg, int nelems, int direction) { - dma_sync_sg(hwdev == NULL ? NULL : &hwdev->dev, sg, nelems, (enum dma_data_direction)direction); + dma_sync_sg_for_device(hwdev == NULL ? NULL : &hwdev->dev, sg, nelems, (enum dma_data_direction)direction); } #endif --- linux-2.6.4-rc2/include/asm-generic/siginfo.h 2003-08-22 19:23:42.000000000 -0700 +++ 25/include/asm-generic/siginfo.h 2004-03-07 20:47:49.000000000 -0800 @@ -118,6 +118,7 @@ typedef struct siginfo { #define __SI_FAULT (3 << 16) #define __SI_CHLD (4 << 16) #define __SI_RT (5 << 16) +#define __SI_MESGQ (6 << 16) #define __SI_CODE(T,N) ((T) | ((N) & 0xffff)) #else #define __SI_KILL 0 @@ -126,6 +127,7 @@ typedef struct siginfo { #define __SI_FAULT 0 #define __SI_CHLD 0 #define __SI_RT 0 +#define __SI_MESGQ 0 #define __SI_CODE(T,N) (N) #endif @@ -137,7 +139,7 @@ typedef struct siginfo { #define SI_KERNEL 0x80 /* sent by the kernel from somewhere */ #define SI_QUEUE -1 /* sent by sigqueue */ #define SI_TIMER __SI_CODE(__SI_TIMER,-2) /* sent by timer expiration */ -#define SI_MESGQ -3 /* sent by real time mesq state change */ +#define SI_MESGQ __SI_CODE(__SI_MESGQ,-3) /* sent by real time mesq state change */ #define SI_ASYNCIO -4 /* sent by AIO completion */ #define SI_SIGIO -5 /* sent by queued SIGIO */ #define SI_TKILL -6 /* sent by tkill system call */ --- linux-2.6.4-rc2/include/asm-generic/tlb.h 2004-03-03 23:12:46.000000000 -0800 +++ 25/include/asm-generic/tlb.h 2004-03-07 20:47:37.000000000 -0800 @@ -39,7 +39,6 @@ struct mmu_gather { unsigned int nr; /* set to ~0U means fast mode */ unsigned int need_flush;/* Really unmapped some ptes? */ unsigned int fullmm; /* non-zero means full mm flush */ - unsigned long freed; struct page * pages[FREE_PTE_NR]; }; @@ -60,7 +59,6 @@ tlb_gather_mmu(struct mm_struct *mm, uns tlb->nr = num_online_cpus() > 1 ? 0U : ~0U; tlb->fullmm = full_mm_flush; - tlb->freed = 0; return tlb; } @@ -85,13 +83,6 @@ tlb_flush_mmu(struct mmu_gather *tlb, un static inline void tlb_finish_mmu(struct mmu_gather *tlb, unsigned long start, unsigned long end) { - int freed = tlb->freed; - struct mm_struct *mm = tlb->mm; - int rss = mm->rss; - - if (rss < freed) - freed = rss; - mm->rss = rss - freed; tlb_flush_mmu(tlb, start, end); /* keep the page table cache within bounds */ @@ -146,4 +137,6 @@ static inline void tlb_remove_page(struc __pmd_free_tlb(tlb, pmdp); \ } while (0) +#define tlb_migrate_prepare(mm) do { } while(0) + #endif /* _ASM_GENERIC__TLB_H */ --- linux-2.6.4-rc2/include/asm-h8300/irq.h 2003-09-08 13:58:59.000000000 -0700 +++ 25/include/asm-h8300/irq.h 2004-03-07 20:48:21.000000000 -0800 @@ -48,4 +48,8 @@ extern void disable_irq(unsigned int); #define enable_irq_nosync(x) enable_irq(x) #define disable_irq_nosync(x) disable_irq(x) +struct irqaction; +struct pt_regs; +int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); + #endif /* _H8300_IRQ_H_ */ --- linux-2.6.4-rc2/include/asm-h8300/unistd.h 2004-03-03 23:12:46.000000000 -0800 +++ 25/include/asm-h8300/unistd.h 2004-03-07 20:47:07.000000000 -0800 @@ -490,7 +490,6 @@ asmlinkage int sys_execve(char *name, ch int dummy, ...); asmlinkage int sys_pipe(unsigned long *fildes); asmlinkage int sys_ptrace(long request, long pid, long addr, long data); -asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int on); struct sigaction; asmlinkage long sys_rt_sigaction(int sig, const struct sigaction __user *act, --- linux-2.6.4-rc2/include/asm-i386/acpi.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/asm-i386/acpi.h 2004-03-07 20:46:49.000000000 -0800 @@ -61,33 +61,36 @@ * Immediate values in the assembly are preceded by "$" as in "$0x1" * The final asm parameter are the operation altered non-output registers. */ + +static inline int +__acpi_acquire_global_lock (unsigned int *lock) +{ + unsigned int old, new, val; + do { + old = *lock; + new = (((old & ~0x3) + 2) + ((old >> 1) & 0x1)); + val = cmpxchg(lock, old, new); + } while (unlikely (val != old)); + return (new < 3) ? -1 : 0; +} + +static inline int +__acpi_release_global_lock (unsigned int *lock) +{ + unsigned int old, new, val; + do { + old = *lock; + new = old & ~0x3; + val = cmpxchg(lock, old, new); + } while (unlikely (val != old)); + return old & 0x1; +} + #define ACPI_ACQUIRE_GLOBAL_LOCK(GLptr, Acq) \ - do { \ - int dummy; \ - asm("1: movl (%1),%%eax;" \ - "movl %%eax,%%edx;" \ - "andl %2,%%edx;" \ - "btsl $0x1,%%edx;" \ - "adcl $0x0,%%edx;" \ - "lock; cmpxchgl %%edx,(%1);" \ - "jnz 1b;" \ - "cmpb $0x3,%%dl;" \ - "sbbl %%eax,%%eax" \ - :"=a"(Acq),"=c"(dummy):"c"(GLptr),"i"(~1L):"dx"); \ - } while(0) + ((Acq) = __acpi_acquire_global_lock((unsigned int *) GLptr)) #define ACPI_RELEASE_GLOBAL_LOCK(GLptr, Acq) \ - do { \ - int dummy; \ - asm("1: movl (%1),%%eax;" \ - "movl %%eax,%%edx;" \ - "andl %2,%%edx;" \ - "lock; cmpxchgl %%edx,(%1);" \ - "jnz 1b;" \ - "andl $0x1,%%eax" \ - :"=a"(Acq),"=c"(dummy):"c"(GLptr),"i"(~3L):"dx"); \ - } while(0) - + ((Acq) = __acpi_release_global_lock((unsigned int *) GLptr)) /* * Math helper asm macros @@ -110,6 +113,7 @@ extern int acpi_lapic; extern int acpi_ioapic; extern int acpi_noirq; +extern int acpi_strict; /* Fixmap pages to reserve for ACPI boot-time tables (see fixmap.h) */ #define FIX_ACPI_PAGES 4 --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/include/asm-i386/atomic_kmap.h 2004-03-07 20:48:18.000000000 -0800 @@ -0,0 +1,95 @@ +/* + * atomic_kmap.h: temporary virtual kernel memory mappings + * + * Copyright (C) 2003 Ingo Molnar + */ + +#ifndef _ASM_ATOMIC_KMAP_H +#define _ASM_ATOMIC_KMAP_H + +#ifdef __KERNEL__ + +#include +#include + +#ifdef CONFIG_DEBUG_HIGHMEM +#define HIGHMEM_DEBUG 1 +#else +#define HIGHMEM_DEBUG 0 +#endif + +extern pte_t *kmap_pte; +#define kmap_prot PAGE_KERNEL + +#define PKMAP_BASE (0xff000000UL) +#define NR_SHARED_PMDS ((0xffffffff-PKMAP_BASE+1)/PMD_SIZE) + +static inline unsigned long __kmap_atomic_vaddr(enum km_type type) +{ + enum fixed_addresses idx; + + idx = type + KM_TYPE_NR*smp_processor_id(); + return __fix_to_virt(FIX_KMAP_BEGIN + idx); +} + +static inline void *__kmap_atomic_noflush(struct page *page, enum km_type type) +{ + enum fixed_addresses idx; + unsigned long vaddr; + + idx = type + KM_TYPE_NR*smp_processor_id(); + vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx); + /* + * NOTE: entries that rely on some secondary TLB-flush + * effect must not be global: + */ + set_pte(kmap_pte-idx, mk_pte(page, PAGE_KERNEL)); + + return (void*) vaddr; +} + +static inline void *__kmap_atomic(struct page *page, enum km_type type) +{ + enum fixed_addresses idx; + unsigned long vaddr; + + idx = type + KM_TYPE_NR*smp_processor_id(); + vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx); +#if HIGHMEM_DEBUG + BUG_ON(!pte_none(*(kmap_pte-idx))); +#else + /* + * Performance optimization - do not flush if the new + * pte is the same as the old one: + */ + if (pte_val(*(kmap_pte-idx)) == pte_val(mk_pte(page, kmap_prot))) + return (void *) vaddr; +#endif + set_pte(kmap_pte-idx, mk_pte(page, kmap_prot)); + __flush_tlb_one(vaddr); + + return (void*) vaddr; +} + +static inline void __kunmap_atomic(void *kvaddr, enum km_type type) +{ +#if HIGHMEM_DEBUG + unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK; + enum fixed_addresses idx = type + KM_TYPE_NR*smp_processor_id(); + + BUG_ON(vaddr != __fix_to_virt(FIX_KMAP_BEGIN+idx)); + /* + * force other mappings to Oops if they'll try to access + * this pte without first remap it + */ + pte_clear(kmap_pte-idx); + __flush_tlb_one(vaddr); +#endif +} + +#define __kunmap_atomic_type(type) \ + __kunmap_atomic((void *)__kmap_atomic_vaddr(type), (type)) + +#endif /* __KERNEL__ */ + +#endif /* _ASM_ATOMIC_KMAP_H */ --- linux-2.6.4-rc2/include/asm-i386/bugs.h 2003-11-09 16:45:05.000000000 -0800 +++ 25/include/asm-i386/bugs.h 2004-03-07 20:47:02.000000000 -0800 @@ -1,11 +1,11 @@ /* * include/asm-i386/bugs.h * - * Copyright (C) 1994 Linus Torvalds + * Copyright (C) 1994 Linus Torvalds * * Cyrix stuff, June 1998 by: * - Rafael R. Reilova (moved everything from head.S), - * + * * - Channing Corn (tests & fixes), * - Andrew D. Balsa (code cleanup). * @@ -25,7 +25,20 @@ #include #include #include - +#ifdef CONFIG_KGDB +/* + * Provied the command line "gdb" initial break + */ +int __init kgdb_initial_break(char * str) +{ + if (*str == '\0'){ + breakpoint(); + return 1; + } + return 0; +} +__setup("gdb",kgdb_initial_break); +#endif static int __init no_halt(char *s) { boot_cpu_data.hlt_works_ok = 0; @@ -140,7 +153,7 @@ static void __init check_popad(void) : "ecx", "edi" ); /* If this fails, it means that any user program may lock the CPU hard. Too bad. */ if (res != 12345678) printk( "Buggy.\n" ); - else printk( "OK.\n" ); + else printk( "OK.\n" ); #endif } --- linux-2.6.4-rc2/include/asm-i386/checksum.h 2003-11-23 19:03:01.000000000 -0800 +++ 25/include/asm-i386/checksum.h 2004-03-07 20:48:18.000000000 -0800 @@ -25,7 +25,7 @@ asmlinkage unsigned int csum_partial(con * better 64-bit) boundary */ -asmlinkage unsigned int csum_partial_copy_generic( const char *src, char *dst, int len, int sum, +asmlinkage unsigned int direct_csum_partial_copy_generic( const char *src, char *dst, int len, int sum, int *src_err_ptr, int *dst_err_ptr); /* @@ -39,14 +39,19 @@ static __inline__ unsigned int csum_partial_copy_nocheck ( const char *src, char *dst, int len, int sum) { - return csum_partial_copy_generic ( src, dst, len, sum, NULL, NULL); + /* + * The direct function is OK for kernel-space => kernel-space copies: + */ + return direct_csum_partial_copy_generic ( src, dst, len, sum, NULL, NULL); } static __inline__ unsigned int csum_partial_copy_from_user ( const char *src, char *dst, int len, int sum, int *err_ptr) { - return csum_partial_copy_generic ( src, dst, len, sum, err_ptr, NULL); + if (copy_from_user(dst, src, len)) + *err_ptr = -EFAULT; + return csum_partial(dst, len, sum); } /* @@ -172,11 +177,26 @@ static __inline__ unsigned short int csu * Copy and checksum to user */ #define HAVE_CSUM_COPY_USER -static __inline__ unsigned int csum_and_copy_to_user(const char *src, char *dst, +static __inline__ unsigned int direct_csum_and_copy_to_user(const char *src, char *dst, int len, int sum, int *err_ptr) { if (access_ok(VERIFY_WRITE, dst, len)) - return csum_partial_copy_generic(src, dst, len, sum, NULL, err_ptr); + return direct_csum_partial_copy_generic(src, dst, len, sum, NULL, err_ptr); + + if (len) + *err_ptr = -EFAULT; + + return -1; /* invalid checksum */ +} + +static __inline__ unsigned int csum_and_copy_to_user(const char *src, char *dst, + int len, int sum, int *err_ptr) +{ + if (access_ok(VERIFY_WRITE, dst, len)) { + if (copy_to_user(dst, src, len)) + *err_ptr = -EFAULT; + return csum_partial(src, len, sum); + } if (len) *err_ptr = -EFAULT; --- linux-2.6.4-rc2/include/asm-i386/cpufeature.h 2003-09-27 18:57:47.000000000 -0700 +++ 25/include/asm-i386/cpufeature.h 2004-03-07 20:47:35.000000000 -0800 @@ -76,6 +76,9 @@ /* VIA/Cyrix/Centaur-defined CPU features, CPUID level 0xC0000001, word 5 */ #define X86_FEATURE_XSTORE (5*32+ 2) /* on-CPU RNG present (xstore insn) */ +#define X86_FEATURE_XSTORE_EN (5*32+ 3) /* on-CPU RNG enabled */ +#define X86_FEATURE_XCRYPT (5*32+ 6) /* on-CPU crypto (xcrypt insn) */ +#define X86_FEATURE_XCRYPT_EN (5*32+ 7) /* on-CPU crypto enabled */ #define cpu_has(c, bit) test_bit(bit, (c)->x86_capability) @@ -101,6 +104,7 @@ #define cpu_has_cyrix_arr boot_cpu_has(X86_FEATURE_CYRIX_ARR) #define cpu_has_centaur_mcr boot_cpu_has(X86_FEATURE_CENTAUR_MCR) #define cpu_has_xstore boot_cpu_has(X86_FEATURE_XSTORE) +#define cpu_has_xcrypt boot_cpu_has(X86_FEATURE_XCRYPT) #endif /* __ASM_I386_CPUFEATURE_H */ --- linux-2.6.4-rc2/include/asm-i386/desc.h 2003-11-09 16:45:05.000000000 -0800 +++ 25/include/asm-i386/desc.h 2004-03-07 20:48:18.000000000 -0800 @@ -21,6 +21,13 @@ struct Xgt_desc_struct { extern struct Xgt_desc_struct idt_descr, cpu_gdt_descr[NR_CPUS]; +extern void trap_init_virtual_IDT(void); +extern void trap_init_virtual_GDT(void); + +asmlinkage int system_call(void); +asmlinkage void lcall7(void); +asmlinkage void lcall27(void); + #define load_TR_desc() __asm__ __volatile__("ltr %%ax"::"a" (GDT_ENTRY_TSS*8)) #define load_LDT_desc() __asm__ __volatile__("lldt %%ax"::"a" (GDT_ENTRY_LDT*8)) @@ -30,6 +37,7 @@ extern struct Xgt_desc_struct idt_descr, */ extern struct desc_struct default_ldt[]; extern void set_intr_gate(unsigned int irq, void * addr); +extern void set_trap_gate(unsigned int n, void *addr); #define _set_tssldt_desc(n,addr,limit,type) \ __asm__ __volatile__ ("movw %w3,0(%2)\n\t" \ @@ -90,31 +98,8 @@ static inline void load_TLS(struct threa #undef C } -static inline void clear_LDT(void) -{ - int cpu = get_cpu(); - - set_ldt_desc(cpu, &default_ldt[0], 5); - load_LDT_desc(); - put_cpu(); -} - -/* - * load one particular LDT into the current CPU - */ -static inline void load_LDT_nolock(mm_context_t *pc, int cpu) -{ - void *segments = pc->ldt; - int count = pc->size; - - if (likely(!count)) { - segments = &default_ldt[0]; - count = 5; - } - - set_ldt_desc(cpu, segments, count); - load_LDT_desc(); -} +extern struct page *default_ldt_page; +extern void load_LDT_nolock(mm_context_t *pc, int cpu); static inline void load_LDT(mm_context_t *pc) { @@ -123,6 +108,6 @@ static inline void load_LDT(mm_context_t put_cpu(); } -#endif /* !__ASSEMBLY__ */ +#endif /* !__ASSEMBLY__ */ #endif --- linux-2.6.4-rc2/include/asm-i386/dma-mapping.h 2003-06-14 12:18:51.000000000 -0700 +++ 25/include/asm-i386/dma-mapping.h 2004-03-07 20:46:59.000000000 -0800 @@ -70,24 +70,42 @@ dma_unmap_sg(struct device *dev, struct } static inline void -dma_sync_single(struct device *dev, dma_addr_t dma_handle, size_t size, - enum dma_data_direction direction) +dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, size_t size, + enum dma_data_direction direction) +{ +} + +static inline void +dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, size_t size, + enum dma_data_direction direction) { flush_write_buffers(); } static inline void -dma_sync_single_range(struct device *dev, dma_addr_t dma_handle, - unsigned long offset, size_t size, - enum dma_data_direction direction) +dma_sync_single_range_for_cpu(struct device *dev, dma_addr_t dma_handle, + unsigned long offset, size_t size, + enum dma_data_direction direction) +{ +} + +static inline void +dma_sync_single_range_for_device(struct device *dev, dma_addr_t dma_handle, + unsigned long offset, size_t size, + enum dma_data_direction direction) { flush_write_buffers(); } +static inline void +dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems, + enum dma_data_direction direction) +{ +} static inline void -dma_sync_sg(struct device *dev, struct scatterlist *sg, int nelems, - enum dma_data_direction direction) +dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems, + enum dma_data_direction direction) { flush_write_buffers(); } --- linux-2.6.4-rc2/include/asm-i386/fixmap.h 2003-11-09 16:45:05.000000000 -0800 +++ 25/include/asm-i386/fixmap.h 2004-03-07 20:48:18.000000000 -0800 @@ -18,17 +18,15 @@ #include #include #include -#ifdef CONFIG_HIGHMEM #include #include -#endif /* * Here we define all the compile-time 'special' virtual * addresses. The point is to have a constant address at * compile time, but to set the physical address only - * in the boot process. We allocate these special addresses - * from the end of virtual memory (0xfffff000) backwards. + * in the boot process. We allocate these special addresses + * from the end of virtual memory (0xffffe000) backwards. * Also this lets us do fail-safe vmalloc(), we * can guarantee that these special addresses and * vmalloc()-ed addresses never overlap. @@ -41,11 +39,20 @@ * TLB entries of such buffers will not be flushed across * task switches. */ + +/* + * on UP currently we will have no trace of the fixmap mechanizm, + * no page table allocations, etc. This might change in the + * future, say framebuffers for the console driver(s) could be + * fix-mapped? + */ enum fixed_addresses { FIX_HOLE, FIX_VSYSCALL, #ifdef CONFIG_X86_LOCAL_APIC FIX_APIC_BASE, /* local (CPU) APIC) -- required for SMP or not */ +#else + FIX_VSTACK_HOLE_1, #endif #ifdef CONFIG_X86_IO_APIC FIX_IO_APIC_BASE_0, @@ -57,20 +64,28 @@ enum fixed_addresses { FIX_LI_PCIA, /* Lithium PCI Bridge A */ FIX_LI_PCIB, /* Lithium PCI Bridge B */ #endif -#ifdef CONFIG_X86_F00F_BUG - FIX_F00F_IDT, /* Virtual mapping for IDT */ -#endif + FIX_IDT, + FIX_GDT_1, + FIX_GDT_0, + FIX_TSS_3, + FIX_TSS_2, + FIX_TSS_1, + FIX_TSS_0, + FIX_ENTRY_TRAMPOLINE_1, + FIX_ENTRY_TRAMPOLINE_0, #ifdef CONFIG_X86_CYCLONE_TIMER FIX_CYCLONE_TIMER, /*cyclone timer register*/ + FIX_VSTACK_HOLE_2, #endif -#ifdef CONFIG_HIGHMEM FIX_KMAP_BEGIN, /* reserved pte's for temporary kernel mappings */ FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1, -#endif #ifdef CONFIG_ACPI_BOOT FIX_ACPI_BEGIN, FIX_ACPI_END = FIX_ACPI_BEGIN + FIX_ACPI_PAGES - 1, #endif +#ifdef CONFIG_PCI_MMCONFIG + FIX_PCIE_MCFG, +#endif __end_of_permanent_fixed_addresses, /* temporary boot-time mappings, used before ioremap() is functional */ #define NR_FIX_BTMAPS 16 @@ -95,12 +110,15 @@ extern void __set_fixmap (enum fixed_add __set_fixmap(idx, 0, __pgprot(0)) /* - * used by vmalloc.c. + * used by vmalloc.c and various other places. * * Leave one empty page between vmalloc'ed areas and * the start of the fixmap. + * + * IMPORTANT: dont change FIXADDR_TOP without adjusting KM_VSTACK0 + * and KM_VSTACK1 so that the virtual stack is 8K aligned. */ -#define FIXADDR_TOP (0xfffff000UL) +#define FIXADDR_TOP (0xffffe000UL) #define __FIXADDR_SIZE (__end_of_permanent_fixed_addresses << PAGE_SHIFT) #define FIXADDR_START (FIXADDR_TOP - __FIXADDR_SIZE) --- linux-2.6.4-rc2/include/asm-i386/highmem.h 2003-11-09 16:45:05.000000000 -0800 +++ 25/include/asm-i386/highmem.h 2004-03-07 20:48:18.000000000 -0800 @@ -25,26 +25,19 @@ #include #include #include +#include /* declarations for highmem.c */ extern unsigned long highstart_pfn, highend_pfn; -extern pte_t *kmap_pte; -extern pgprot_t kmap_prot; extern pte_t *pkmap_page_table; - -extern void kmap_init(void); +extern void kmap_init(void) __init; /* * Right now we initialize only a single pte table. It can be extended * easily, subsequent pte tables have to be allocated in one physical * chunk of RAM. */ -#if NR_CPUS <= 32 -#define PKMAP_BASE (0xff800000UL) -#else -#define PKMAP_BASE (0xff600000UL) -#endif #ifdef CONFIG_X86_PAE #define LAST_PKMAP 512 #else --- linux-2.6.4-rc2/include/asm-i386/io.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/asm-i386/io.h 2004-03-07 20:47:45.000000000 -0800 @@ -45,17 +45,6 @@ #include -/* - * Temporary debugging check to catch old code using - * unmapped ISA addresses. Will be removed in 2.4. - */ -#ifdef CONFIG_DEBUG_IOVIRT - extern void *__io_virt_debug(unsigned long x, const char *file, int line); - #define __io_virt(x) __io_virt_debug((unsigned long)(x), __FILE__, __LINE__) -#else - #define __io_virt(x) ((void *)(x)) -#endif - /** * virt_to_phys - map virtual addresses to physical * @address: address to remap @@ -150,9 +139,9 @@ extern void bt_iounmap(void *addr, unsig * memory location directly. */ -#define readb(addr) (*(volatile unsigned char *) __io_virt(addr)) -#define readw(addr) (*(volatile unsigned short *) __io_virt(addr)) -#define readl(addr) (*(volatile unsigned int *) __io_virt(addr)) +#define readb(addr) (*(volatile unsigned char *) (addr)) +#define readw(addr) (*(volatile unsigned short *) (addr)) +#define readl(addr) (*(volatile unsigned int *) (addr)) #define readb_relaxed(addr) readb(addr) #define readw_relaxed(addr) readw(addr) #define readl_relaxed(addr) readl(addr) @@ -160,16 +149,16 @@ extern void bt_iounmap(void *addr, unsig #define __raw_readw readw #define __raw_readl readl -#define writeb(b,addr) (*(volatile unsigned char *) __io_virt(addr) = (b)) -#define writew(b,addr) (*(volatile unsigned short *) __io_virt(addr) = (b)) -#define writel(b,addr) (*(volatile unsigned int *) __io_virt(addr) = (b)) +#define writeb(b,addr) (*(volatile unsigned char *) (addr) = (b)) +#define writew(b,addr) (*(volatile unsigned short *) (addr) = (b)) +#define writel(b,addr) (*(volatile unsigned int *) (addr) = (b)) #define __raw_writeb writeb #define __raw_writew writew #define __raw_writel writel -#define memset_io(a,b,c) memset(__io_virt(a),(b),(c)) -#define memcpy_fromio(a,b,c) __memcpy((a),__io_virt(b),(c)) -#define memcpy_toio(a,b,c) __memcpy(__io_virt(a),(b),(c)) +#define memset_io(a,b,c) memset((void *)(a),(b),(c)) +#define memcpy_fromio(a,b,c) __memcpy((a),(void *)(b),(c)) +#define memcpy_toio(a,b,c) __memcpy((void *)(a),(b),(c)) /* * ISA space is 'always mapped' on a typical x86 system, no need to @@ -196,8 +185,8 @@ extern void bt_iounmap(void *addr, unsig * Again, i386 does not require mem IO specific function. */ -#define eth_io_copy_and_sum(a,b,c,d) eth_copy_and_sum((a),__io_virt(b),(c),(d)) -#define isa_eth_io_copy_and_sum(a,b,c,d) eth_copy_and_sum((a),__io_virt(__ISA_IO_base + (b)),(c),(d)) +#define eth_io_copy_and_sum(a,b,c,d) eth_copy_and_sum((a),(void *)(b),(c),(d)) +#define isa_eth_io_copy_and_sum(a,b,c,d) eth_copy_and_sum((a),(void *)(__ISA_IO_base + (b)),(c),(d)) /** * check_signature - find BIOS signatures --- linux-2.6.4-rc2/include/asm-i386/irq.h 2004-01-09 00:04:32.000000000 -0800 +++ 25/include/asm-i386/irq.h 2004-03-07 20:48:21.000000000 -0800 @@ -14,6 +14,7 @@ #include /* include comes from machine specific directory */ #include "irq_vectors.h" +#include static __inline__ int irq_canonicalize(int irq) { @@ -30,4 +31,28 @@ extern int can_request_irq(unsigned int, #define ARCH_HAS_NMI_WATCHDOG /* See include/linux/nmi.h */ #endif +#ifdef CONFIG_4KSTACKS +/* + * per-CPU IRQ handling contexts (thread information and stack) + */ +union irq_ctx { + struct thread_info tinfo; + u32 stack[THREAD_SIZE/sizeof(u32)]; +}; + +extern union irq_ctx *hardirq_ctx[NR_CPUS]; +extern union irq_ctx *softirq_ctx[NR_CPUS]; + +extern void irq_ctx_init(int cpu); + +#define __ARCH_HAS_DO_SOFTIRQ +#else +#define irq_ctx_init(cpu) do { ; } while (0) +#endif + +struct irqaction; +struct pt_regs; +asmlinkage int handle_IRQ_event(unsigned int, struct pt_regs *, + struct irqaction *); + #endif /* _ASM_IRQ_H */ --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/include/asm-i386/kgdb.h 2004-03-07 20:47:03.000000000 -0800 @@ -0,0 +1,69 @@ +#ifndef __KGDB +#define __KGDB + +/* + * This file should not include ANY others. This makes it usable + * most anywhere without the fear of include order or inclusion. + * Make it so! + * + * This file may be included all the time. It is only active if + * CONFIG_KGDB is defined, otherwise it stubs out all the macros + * and entry points. + */ +#if defined(CONFIG_KGDB) && !defined(__ASSEMBLY__) + +extern void breakpoint(void); +#define INIT_KGDB_INTS kgdb_enable_ints() + +#ifndef BREAKPOINT +#define BREAKPOINT asm(" int $3") +#endif + +extern void kgdb_schedule_breakpoint(void); +extern void kgdb_process_breakpoint(void); + +extern int kgdb_tty_hook(void); +extern int kgdb_eth_hook(void); +extern int kgdboe; + +/* + * GDB debug stub (or any debug stub) can point the 'linux_debug_hook' + * pointer to its routine and it will be entered as the first thing + * when a trap occurs. + * + * Return values are, at present, undefined. + * + * The debug hook routine does not necessarily return to its caller. + * It has the register image and thus may choose to resume execution + * anywhere it pleases. + */ +struct pt_regs; + +extern int kgdb_handle_exception(int trapno, + int signo, int err_code, struct pt_regs *regs); +extern int in_kgdb(struct pt_regs *regs); + +#ifdef CONFIG_KGDB_TS +void kgdb_tstamp(int line, char *source, int data0, int data1); +/* + * This is the time stamp function. The macro adds the source info and + * does a cast on the data to allow most any 32-bit value. + */ + +#define kgdb_ts(data0,data1) kgdb_tstamp(__LINE__,__FILE__,(int)data0,(int)data1) +#else +#define kgdb_ts(data0,data1) +#endif +#else /* CONFIG_KGDB && ! __ASSEMBLY__ ,stubs follow... */ +#ifndef BREAKPOINT +#define BREAKPOINT +#endif +#define kgdb_ts(data0,data1) +#define in_kgdb +#define kgdb_handle_exception +#define breakpoint +#define INIT_KGDB_INTS +#define kgdb_process_breakpoint() do {} while(0) + +#endif +#endif /* __KGDB */ --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/include/asm-i386/kgdb_local.h 2004-03-07 20:47:02.000000000 -0800 @@ -0,0 +1,102 @@ +#ifndef __KGDB_LOCAL +#define ___KGDB_LOCAL +#include +#include +#include +#include +#include +#include +#include +#include + +#define PORT 0x3f8 +#ifdef CONFIG_KGDB_PORT +#undef PORT +#define PORT CONFIG_KGDB_PORT +#endif +#define IRQ 4 +#ifdef CONFIG_KGDB_IRQ +#undef IRQ +#define IRQ CONFIG_KGDB_IRQ +#endif +#define SB_CLOCK 1843200 +#define SB_BASE (SB_CLOCK/16) +#define SB_BAUD9600 SB_BASE/9600 +#define SB_BAUD192 SB_BASE/19200 +#define SB_BAUD384 SB_BASE/38400 +#define SB_BAUD576 SB_BASE/57600 +#define SB_BAUD1152 SB_BASE/115200 +#ifdef CONFIG_KGDB_9600BAUD +#define SB_BAUD SB_BAUD9600 +#endif +#ifdef CONFIG_KGDB_19200BAUD +#define SB_BAUD SB_BAUD192 +#endif +#ifdef CONFIG_KGDB_38400BAUD +#define SB_BAUD SB_BAUD384 +#endif +#ifdef CONFIG_KGDB_57600BAUD +#define SB_BAUD SB_BAUD576 +#endif +#ifdef CONFIG_KGDB_115200BAUD +#define SB_BAUD SB_BAUD1152 +#endif +#ifndef SB_BAUD +#define SB_BAUD SB_BAUD1152 /* Start with this if not given */ +#endif + +#ifndef CONFIG_X86_TSC +#undef rdtsc +#define rdtsc(a,b) if (a++ > 10000){a = 0; b++;} +#undef rdtscll +#define rdtscll(s) s++ +#endif + +#ifdef _raw_read_unlock /* must use a name that is "define"ed, not an inline */ +#undef spin_lock +#undef spin_trylock +#undef spin_unlock +#define spin_lock _raw_spin_lock +#define spin_trylock _raw_spin_trylock +#define spin_unlock _raw_spin_unlock +#else +#endif +#undef spin_unlock_wait +#define spin_unlock_wait(x) do { cpu_relax(); barrier();} \ + while(spin_is_locked(x)) + +#define SB_IER 1 +#define SB_MCR UART_MCR_OUT2 | UART_MCR_DTR | UART_MCR_RTS + +#define FLAGS 0 +#define SB_STATE { \ + magic: SSTATE_MAGIC, \ + baud_base: SB_BASE, \ + port: PORT, \ + irq: IRQ, \ + flags: FLAGS, \ + custom_divisor:SB_BAUD} +#define SB_INFO { \ + magic: SERIAL_MAGIC, \ + port: PORT,0,FLAGS, \ + state: &state, \ + tty: (struct tty_struct *)&state, \ + IER: SB_IER, \ + MCR: SB_MCR} +extern void putDebugChar(int); +/* RTAI support needs us to really stop/start interrupts */ + +#define kgdb_sti() __asm__ __volatile__("sti": : :"memory") +#define kgdb_cli() __asm__ __volatile__("cli": : :"memory") +#define kgdb_local_save_flags(x) __asm__ __volatile__(\ + "pushfl ; popl %0":"=g" (x): /* no input */) +#define kgdb_local_irq_restore(x) __asm__ __volatile__(\ + "pushl %0 ; popfl": \ + /* no output */ :"g" (x):"memory", "cc") +#define kgdb_local_irq_save(x) kgdb_local_save_flags(x); kgdb_cli() + +#ifdef CONFIG_SERIAL +extern void shutdown_for_kgdb(struct async_struct *info); +#endif +#define INIT_KDEBUG putDebugChar("+"); +#endif /* __KGDB_LOCAL */ --- linux-2.6.4-rc2/include/asm-i386/kmap_types.h 2003-11-09 16:45:05.000000000 -0800 +++ 25/include/asm-i386/kmap_types.h 2004-03-07 20:48:18.000000000 -0800 @@ -3,30 +3,36 @@ #include -#ifdef CONFIG_DEBUG_HIGHMEM -# define D(n) __KM_FENCE_##n , -#else -# define D(n) -#endif - enum km_type { -D(0) KM_BOUNCE_READ, -D(1) KM_SKB_SUNRPC_DATA, -D(2) KM_SKB_DATA_SOFTIRQ, -D(3) KM_USER0, -D(4) KM_USER1, -D(5) KM_BIO_SRC_IRQ, -D(6) KM_BIO_DST_IRQ, -D(7) KM_PTE0, -D(8) KM_PTE1, -D(9) KM_PTE2, -D(10) KM_IRQ0, -D(11) KM_IRQ1, -D(12) KM_SOFTIRQ0, -D(13) KM_SOFTIRQ1, -D(14) KM_TYPE_NR -}; - -#undef D + /* + * IMPORTANT: don't move these 3 entries, and only add entries in + * pairs: the 4G/4G virtual stack must be 8K aligned on each cpu. + */ + KM_BOUNCE_READ, + KM_VSTACK1, + KM_VSTACK0, + KM_LDT_PAGE15, + KM_LDT_PAGE0 = KM_LDT_PAGE15 + 16-1, + KM_USER_COPY, + KM_VSTACK_HOLE, + KM_SKB_SUNRPC_DATA, + KM_SKB_DATA_SOFTIRQ, + KM_USER0, + KM_USER1, + KM_BIO_SRC_IRQ, + KM_BIO_DST_IRQ, + KM_PTE0, + KM_PTE1, + KM_PTE2, + KM_IRQ0, + KM_IRQ1, + KM_SOFTIRQ0, + KM_SOFTIRQ1, + /* + * Add new entries in pairs: + * the 4G/4G virtual stack must be 8K aligned on each cpu. + */ + KM_TYPE_NR +}; #endif --- linux-2.6.4-rc2/include/asm-i386/linkage.h 2003-06-14 12:18:52.000000000 -0700 +++ 25/include/asm-i386/linkage.h 2004-03-07 20:46:46.000000000 -0800 @@ -3,6 +3,7 @@ #define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0))) #define FASTCALL(x) x __attribute__((regparm(3))) +#define fastcall __attribute__((regparm(3))) #ifdef CONFIG_X86_ALIGNMENT_16 #define __ALIGN .align 16,0x90 --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/include/asm-i386/lockmeter.h 2004-03-07 20:48:17.000000000 -0800 @@ -0,0 +1,115 @@ +/* + * Copyright (C) 1999,2000 Silicon Graphics, Inc. + * + * Written by John Hawkes (hawkes@sgi.com) + * Based on klstat.h by Jack Steiner (steiner@sgi.com) + * + * Modified by Ray Bryant (raybry@us.ibm.com) + * Changes Copyright (C) 2000 IBM, Inc. + * Added save of index in spinlock_t to improve efficiency + * of "hold" time reporting for spinlocks. + * Added support for hold time statistics for read and write + * locks. + * Moved machine dependent code here from include/lockmeter.h. + * + */ + +#ifndef _I386_LOCKMETER_H +#define _I386_LOCKMETER_H + +#include +#include + +#include + +#ifdef __KERNEL__ +extern unsigned long cpu_khz; +#define CPU_CYCLE_FREQUENCY (cpu_khz * 1000) +#else +#define CPU_CYCLE_FREQUENCY 450000000 +#endif + +#define THIS_CPU_NUMBER smp_processor_id() + +/* + * macros to cache and retrieve an index value inside of a spin lock + * these macros assume that there are less than 65536 simultaneous + * (read mode) holders of a rwlock. Not normally a problem!! + * we also assume that the hash table has less than 65535 entries. + */ +/* + * instrumented spinlock structure -- never used to allocate storage + * only used in macros below to overlay a spinlock_t + */ +typedef struct inst_spinlock_s { + /* remember, Intel is little endian */ + unsigned short lock; + unsigned short index; +} inst_spinlock_t; +#define PUT_INDEX(lock_ptr,indexv) ((inst_spinlock_t *)(lock_ptr))->index = indexv +#define GET_INDEX(lock_ptr) ((inst_spinlock_t *)(lock_ptr))->index + +/* + * macros to cache and retrieve an index value in a read/write lock + * as well as the cpu where a reader busy period started + * we use the 2nd word (the debug word) for this, so require the + * debug word to be present + */ +/* + * instrumented rwlock structure -- never used to allocate storage + * only used in macros below to overlay a rwlock_t + */ +typedef struct inst_rwlock_s { + volatile int lock; + unsigned short index; + unsigned short cpu; +} inst_rwlock_t; +#define PUT_RWINDEX(rwlock_ptr,indexv) ((inst_rwlock_t *)(rwlock_ptr))->index = indexv +#define GET_RWINDEX(rwlock_ptr) ((inst_rwlock_t *)(rwlock_ptr))->index +#define PUT_RW_CPU(rwlock_ptr,cpuv) ((inst_rwlock_t *)(rwlock_ptr))->cpu = cpuv +#define GET_RW_CPU(rwlock_ptr) ((inst_rwlock_t *)(rwlock_ptr))->cpu + +/* + * return the number of readers for a rwlock_t + */ +#define RWLOCK_READERS(rwlock_ptr) rwlock_readers(rwlock_ptr) + +extern inline int rwlock_readers(rwlock_t *rwlock_ptr) +{ + int tmp = (int) rwlock_ptr->lock; + /* read and write lock attempts may cause the lock value to temporarily */ + /* be negative. Until it is >= 0 we know nothing (i. e. can't tell if */ + /* is -1 because it was write locked and somebody tried to read lock it */ + /* or if it is -1 because it was read locked and somebody tried to write*/ + /* lock it. ........................................................... */ + do { + tmp = (int) rwlock_ptr->lock; + } while (tmp < 0); + if (tmp == 0) return(0); + else return(RW_LOCK_BIAS-tmp); +} + +/* + * return true if rwlock is write locked + * (note that other lock attempts can cause the lock value to be negative) + */ +#define RWLOCK_IS_WRITE_LOCKED(rwlock_ptr) ((rwlock_ptr)->lock <= 0) +#define IABS(x) ((x) > 0 ? (x) : -(x)) +#define RWLOCK_IS_READ_LOCKED(rwlock_ptr) ((IABS((rwlock_ptr)->lock) % RW_LOCK_BIAS) != 0) + +/* this is a lot of typing just to get gcc to emit "rdtsc" */ +static inline long long get_cycles64 (void) +{ + union longlong_u { + long long intlong; + struct intint_s { + uint32_t eax; + uint32_t edx; + } intint; + } longlong; + + rdtsc(longlong.intint.eax,longlong.intint.edx); + return longlong.intlong; +} + +#endif /* _I386_LOCKMETER_H */ --- linux-2.6.4-rc2/include/asm-i386/mman.h 2003-10-08 15:07:10.000000000 -0700 +++ 25/include/asm-i386/mman.h 2004-03-07 20:48:04.000000000 -0800 @@ -22,6 +22,7 @@ #define MAP_NORESERVE 0x4000 /* don't check for reservations */ #define MAP_POPULATE 0x8000 /* populate (prefault) pagetables */ #define MAP_NONBLOCK 0x10000 /* do not block on IO */ +#define MAP_INHERIT 0x20000 /* inherit the protection bits of the underlying vma*/ #define MS_ASYNC 1 /* sync memory asynchronously */ #define MS_INVALIDATE 2 /* invalidate the caches */ --- linux-2.6.4-rc2/include/asm-i386/mmu_context.h 2003-11-09 16:45:05.000000000 -0800 +++ 25/include/asm-i386/mmu_context.h 2004-03-07 20:48:18.000000000 -0800 @@ -29,6 +29,10 @@ static inline void switch_mm(struct mm_s { int cpu = smp_processor_id(); +#ifdef CONFIG_X86_SWITCH_PAGETABLES + if (tsk->mm) + tsk->thread_info->user_pgd = (void *)__pa(tsk->mm->pgd); +#endif if (likely(prev != next)) { /* stop flush ipis for the previous mm */ cpu_clear(cpu, prev->cpu_vm_mask); @@ -39,12 +43,14 @@ static inline void switch_mm(struct mm_s cpu_set(cpu, next->cpu_vm_mask); /* Re-load page tables */ +#if !defined(CONFIG_X86_SWITCH_PAGETABLES) load_cr3(next->pgd); +#endif /* * load the LDT, if the LDT is different: */ - if (unlikely(prev->context.ldt != next->context.ldt)) + if (unlikely(prev->context.size + next->context.size)) load_LDT_nolock(&next->context, cpu); } #ifdef CONFIG_SMP @@ -56,7 +62,9 @@ static inline void switch_mm(struct mm_s /* We were in lazy tlb mode and leave_mm disabled * tlb flush IPI delivery. We must reload %cr3. */ +#if !defined(CONFIG_X86_SWITCH_PAGETABLES) load_cr3(next->pgd); +#endif load_LDT_nolock(&next->context, cpu); } } @@ -67,6 +75,6 @@ static inline void switch_mm(struct mm_s asm("movl %0,%%fs ; movl %0,%%gs": :"r" (0)) #define activate_mm(prev, next) \ - switch_mm((prev),(next),NULL) + switch_mm((prev),(next),current) #endif --- linux-2.6.4-rc2/include/asm-i386/mmu.h 2003-11-09 16:45:05.000000000 -0800 +++ 25/include/asm-i386/mmu.h 2004-03-07 20:48:18.000000000 -0800 @@ -8,10 +8,13 @@ * * cpu_vm_mask is used to optimize ldt flushing. */ + +#define MAX_LDT_PAGES 16 + typedef struct { int size; struct semaphore sem; - void *ldt; + struct page *ldt_pages[MAX_LDT_PAGES]; } mm_context_t; #endif --- linux-2.6.4-rc2/include/asm-i386/module.h 2004-03-03 23:12:46.000000000 -0800 +++ 25/include/asm-i386/module.h 2004-03-07 20:48:21.000000000 -0800 @@ -36,7 +36,7 @@ struct mod_arch_specific #define MODULE_PROC_FAMILY "K7 " #elif defined CONFIG_MK8 #define MODULE_PROC_FAMILY "K8 " -#elif defined CONFIG_MELAN +#elif defined CONFIG_X86_ELAN #define MODULE_PROC_FAMILY "ELAN " #elif defined CONFIG_MCRUSOE #define MODULE_PROC_FAMILY "CRUSOE " @@ -60,6 +60,12 @@ struct mod_arch_specific #define MODULE_REGPARM "" #endif -#define MODULE_ARCH_VERMAGIC MODULE_PROC_FAMILY MODULE_REGPARM +#ifdef CONFIG_4KSTACKS +#define MODULE_STACKSIZE "4KSTACKS " +#else +#define MODULE_STACKSIZE "" +#endif + +#define MODULE_ARCH_VERMAGIC MODULE_PROC_FAMILY MODULE_REGPARM MODULE_STACKSIZE #endif /* _ASM_I386_MODULE_H */ --- linux-2.6.4-rc2/include/asm-i386/page.h 2003-11-09 16:45:05.000000000 -0800 +++ 25/include/asm-i386/page.h 2004-03-07 20:48:18.000000000 -0800 @@ -1,6 +1,8 @@ #ifndef _I386_PAGE_H #define _I386_PAGE_H +#include + /* PAGE_SHIFT determines the page size */ #define PAGE_SHIFT 12 #define PAGE_SIZE (1UL << PAGE_SHIFT) @@ -9,11 +11,10 @@ #define LARGE_PAGE_MASK (~(LARGE_PAGE_SIZE-1)) #define LARGE_PAGE_SIZE (1UL << PMD_SHIFT) -#ifdef __KERNEL__ -#ifndef __ASSEMBLY__ - #include +#ifdef __KERNEL__ +#ifndef __ASSEMBLY__ #ifdef CONFIG_X86_USE_3DNOW #include @@ -88,8 +89,19 @@ typedef struct { unsigned long pgprot; } * * If you want more physical memory than this then see the CONFIG_HIGHMEM4G * and CONFIG_HIGHMEM64G options in the kernel configuration. + * + * Note: on PAE the kernel must never go below 32 MB, we use the + * first 8 entries of the 2-level boot pgd for PAE magic. */ +#ifdef CONFIG_X86_4G_VM_LAYOUT +#define __PAGE_OFFSET (0x02000000) +#define TASK_SIZE (0xff000000) +#else +#define __PAGE_OFFSET (0xc0000000) +#define TASK_SIZE (0xc0000000) +#endif + /* * This much address space is reserved for vmalloc() and iomap() * as well as fixmap mappings. @@ -114,16 +126,10 @@ static __inline__ int get_order(unsigned #endif /* __ASSEMBLY__ */ -#ifdef __ASSEMBLY__ -#define __PAGE_OFFSET (0xC0000000) -#else -#define __PAGE_OFFSET (0xC0000000UL) -#endif - - #define PAGE_OFFSET ((unsigned long)__PAGE_OFFSET) #define VMALLOC_RESERVE ((unsigned long)__VMALLOC_RESERVE) -#define MAXMEM (-__PAGE_OFFSET-__VMALLOC_RESERVE) +#define __MAXMEM (-__PAGE_OFFSET-__VMALLOC_RESERVE) +#define MAXMEM ((unsigned long)(-PAGE_OFFSET-VMALLOC_RESERVE)) #define __pa(x) ((unsigned long)(x)-PAGE_OFFSET) #define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET)) #define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT) --- linux-2.6.4-rc2/include/asm-i386/pci.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/asm-i386/pci.h 2004-03-07 20:46:59.000000000 -0800 @@ -60,27 +60,32 @@ struct pci_dev; /* This is always fine. */ #define pci_dac_dma_supported(pci_dev, mask) (1) -static __inline__ dma64_addr_t +static inline dma64_addr_t pci_dac_page_to_dma(struct pci_dev *pdev, struct page *page, unsigned long offset, int direction) { return ((dma64_addr_t) page_to_phys(page) + (dma64_addr_t) offset); } -static __inline__ struct page * +static inline struct page * pci_dac_dma_to_page(struct pci_dev *pdev, dma64_addr_t dma_addr) { return pfn_to_page(dma_addr >> PAGE_SHIFT); } -static __inline__ unsigned long +static inline unsigned long pci_dac_dma_to_offset(struct pci_dev *pdev, dma64_addr_t dma_addr) { return (dma_addr & ~PAGE_MASK); } -static __inline__ void -pci_dac_dma_sync_single(struct pci_dev *pdev, dma64_addr_t dma_addr, size_t len, int direction) +static inline void +pci_dac_dma_sync_single_for_cpu(struct pci_dev *pdev, dma64_addr_t dma_addr, size_t len, int direction) +{ +} + +static inline void +pci_dac_dma_sync_single_for_device(struct pci_dev *pdev, dma64_addr_t dma_addr, size_t len, int direction) { flush_write_buffers(); } --- linux-2.6.4-rc2/include/asm-i386/pgtable-2level.h 2003-06-14 12:18:22.000000000 -0700 +++ 25/include/asm-i386/pgtable-2level.h 2004-03-07 20:48:04.000000000 -0800 @@ -64,15 +64,20 @@ static inline pmd_t * pmd_offset(pgd_t * #define pfn_pmd(pfn, prot) __pmd(((pfn) << PAGE_SHIFT) | pgprot_val(prot)) /* - * Bits 0, 6 and 7 are taken, split up the 29 bits of offset + * Bits 0, 1, 6 and 7 are taken, split up the 28 bits of offset * into this range: */ -#define PTE_FILE_MAX_BITS 29 +#define PTE_FILE_MAX_BITS 28 #define pte_to_pgoff(pte) \ - ((((pte).pte_low >> 1) & 0x1f ) + (((pte).pte_low >> 8) << 5 )) + ((((pte).pte_low >> 2) & 0xf ) + (((pte).pte_low >> 8) << 4 )) +#define pte_to_pgprot(pte) \ + __pgprot(((pte).pte_low & (_PAGE_RW | _PAGE_PROTNONE)) \ + | (((pte).pte_low & _PAGE_PROTNONE) ? 0 : \ + (_PAGE_USER | _PAGE_PRESENT)) | _PAGE_ACCESSED) -#define pgoff_to_pte(off) \ - ((pte_t) { (((off) & 0x1f) << 1) + (((off) >> 5) << 8) + _PAGE_FILE }) +#define pgoff_prot_to_pte(off, prot) \ + ((pte_t) { (((off) & 0xf) << 2) + (((off) >> 4) << 8) + \ + (pgprot_val(prot) & (_PAGE_RW | _PAGE_PROTNONE)) + _PAGE_FILE }) #endif /* _I386_PGTABLE_2LEVEL_H */ --- linux-2.6.4-rc2/include/asm-i386/pgtable-3level.h 2003-08-08 22:55:13.000000000 -0700 +++ 25/include/asm-i386/pgtable-3level.h 2004-03-07 20:48:04.000000000 -0800 @@ -120,7 +120,16 @@ static inline pmd_t pfn_pmd(unsigned lon * put the 32 bits of offset into the high part. */ #define pte_to_pgoff(pte) ((pte).pte_high) -#define pgoff_to_pte(off) ((pte_t) { _PAGE_FILE, (off) }) + +#define pte_to_pgprot(pte) \ + __pgprot(((pte).pte_low & (_PAGE_RW | _PAGE_PROTNONE)) \ + | (((pte).pte_low & _PAGE_PROTNONE) ? 0 : \ + (_PAGE_USER | _PAGE_PRESENT)) | _PAGE_ACCESSED) + +#define pgoff_prot_to_pte(off, prot) \ + ((pte_t) { _PAGE_FILE + \ + (pgprot_val(prot) & (_PAGE_RW | _PAGE_PROTNONE)) , (off) }) + #define PTE_FILE_MAX_BITS 32 #endif /* _I386_PGTABLE_3LEVEL_H */ --- linux-2.6.4-rc2/include/asm-i386/pgtable.h 2004-02-03 20:42:38.000000000 -0800 +++ 25/include/asm-i386/pgtable.h 2004-03-07 20:48:18.000000000 -0800 @@ -32,16 +32,17 @@ #define ZERO_PAGE(vaddr) (virt_to_page(empty_zero_page)) extern unsigned long empty_zero_page[1024]; extern pgd_t swapper_pg_dir[1024]; -extern kmem_cache_t *pgd_cache; -extern kmem_cache_t *pmd_cache; +extern kmem_cache_t *pgd_cache, *pmd_cache, *kpmd_cache; extern spinlock_t pgd_lock; extern struct list_head pgd_list; void pmd_ctor(void *, kmem_cache_t *, unsigned long); +void kpmd_ctor(void *, kmem_cache_t *, unsigned long); void pgd_ctor(void *, kmem_cache_t *, unsigned long); void pgd_dtor(void *, kmem_cache_t *, unsigned long); void pgtable_cache_init(void); void paging_init(void); +void setup_identity_mappings(pgd_t *pgd_base, unsigned long start, unsigned long end); #endif /* !__ASSEMBLY__ */ @@ -51,6 +52,11 @@ void paging_init(void); * newer 3-level PAE-mode page tables. */ #ifndef __ASSEMBLY__ + +extern void set_system_gate(unsigned int n, void *addr); +extern void init_entry_mappings(void); +extern void entry_trampoline_setup(void); + #ifdef CONFIG_X86_PAE # include #else @@ -63,7 +69,12 @@ void paging_init(void); #define PGDIR_SIZE (1UL << PGDIR_SHIFT) #define PGDIR_MASK (~(PGDIR_SIZE-1)) -#define USER_PTRS_PER_PGD (TASK_SIZE/PGDIR_SIZE) +#if defined(CONFIG_X86_PAE) && defined(CONFIG_X86_4G_VM_LAYOUT) +# define USER_PTRS_PER_PGD 4 +#else +# define USER_PTRS_PER_PGD ((TASK_SIZE/PGDIR_SIZE) + ((TASK_SIZE % PGDIR_SIZE) + PGDIR_SIZE-1)/PGDIR_SIZE) +#endif + #define FIRST_USER_PGD_NR 0 #define USER_PGD_PTRS (PAGE_OFFSET >> PGDIR_SHIFT) @@ -173,8 +184,8 @@ extern unsigned long __PAGE_KERNEL; */ #undef TEST_VERIFY_AREA -/* page table for 0-4MB for everybody */ -extern unsigned long pg0[1024]; +/* The boot page tables (all created as a single array) */ +extern unsigned long pg0[]; #define pte_present(x) ((x).pte_low & (_PAGE_PRESENT | _PAGE_PROTNONE)) #define pte_clear(xp) do { set_pte(xp, __pte(0)); } while (0) @@ -233,6 +244,7 @@ static inline void ptep_mkdirty(pte_t *p #define mk_pte(page, pgprot) pfn_pte(page_to_pfn(page), (pgprot)) #define mk_pte_huge(entry) ((entry).pte_low |= _PAGE_PRESENT | _PAGE_PSE) +#define mk_pte_phys(physpage, pgprot) pfn_pte((physpage) >> PAGE_SHIFT, pgprot) static inline pte_t pte_modify(pte_t pte, pgprot_t newprot) { --- linux-2.6.4-rc2/include/asm-i386/processor.h 2004-03-03 23:12:46.000000000 -0800 +++ 25/include/asm-i386/processor.h 2004-03-07 20:48:18.000000000 -0800 @@ -291,11 +291,6 @@ extern unsigned int machine_submodel_id; extern unsigned int BIOS_revision; extern unsigned int mca_pentium_flag; -/* - * User space process size: 3GB (default). - */ -#define TASK_SIZE (PAGE_OFFSET) - /* This decides where the kernel will search for a free chunk of vm * space during mmap's. */ @@ -406,6 +401,7 @@ struct tss_struct { struct thread_struct { /* cached TLS descriptors. */ struct desc_struct tls_array[GDT_ENTRY_TLS_ENTRIES]; + void *stack_page0, *stack_page1; unsigned long esp0; unsigned long sysenter_cs; unsigned long eip; @@ -449,7 +445,8 @@ struct thread_struct { .io_bitmap = { [ 0 ... IO_BITMAP_LONGS] = ~0 }, \ } -static inline void load_esp0(struct tss_struct *tss, struct thread_struct *thread) +static inline void +load_esp0(struct tss_struct *tss, struct thread_struct *thread) { tss->esp0 = thread->esp0; /* This can only happen when SEP is enabled, no need to test "SEP"arately */ @@ -485,6 +482,23 @@ extern void prepare_to_copy(struct task_ */ extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags); +#ifdef CONFIG_X86_HIGH_ENTRY +#define virtual_esp0(tsk) \ + ((unsigned long)(tsk)->thread_info->virtual_stack + ((tsk)->thread.esp0 - (unsigned long)(tsk)->thread_info->real_stack)) +#else +# define virtual_esp0(tsk) ((tsk)->thread.esp0) +#endif + +#define load_virtual_esp0(tss, task) \ + do { \ + tss->esp0 = virtual_esp0(task); \ + if (likely(cpu_has_sep) && unlikely(tss->ss1 != task->thread.sysenter_cs)) { \ + tss->ss1 = task->thread.sysenter_cs; \ + wrmsr(MSR_IA32_SYSENTER_CS, \ + task->thread.sysenter_cs, 0); \ + } \ + } while (0) + extern unsigned long thread_saved_pc(struct task_struct *tsk); void show_trace(struct task_struct *task, unsigned long *stack); @@ -646,4 +660,9 @@ extern inline void prefetchw(const void extern void select_idle_routine(const struct cpuinfo_x86 *c); +#ifdef CONFIG_SCHED_SMT +#define ARCH_HAS_SCHED_DOMAIN +#define ARCH_HAS_SCHED_WAKE_BALANCE +#endif + #endif /* __ASM_I386_PROCESSOR_H */ --- linux-2.6.4-rc2/include/asm-i386/rwlock.h 2003-11-09 16:45:05.000000000 -0800 +++ 25/include/asm-i386/rwlock.h 2004-03-07 20:47:10.000000000 -0800 @@ -20,28 +20,52 @@ #define RW_LOCK_BIAS 0x01000000 #define RW_LOCK_BIAS_STR "0x01000000" -#define __build_read_lock_ptr(rw, helper) \ - asm volatile(LOCK "subl $1,(%0)\n\t" \ - "js 2f\n" \ - "1:\n" \ - LOCK_SECTION_START("") \ - "2:\tcall " helper "\n\t" \ - "jmp 1b\n" \ - LOCK_SECTION_END \ - ::"a" (rw) : "memory") - -#define __build_read_lock_const(rw, helper) \ - asm volatile(LOCK "subl $1,%0\n\t" \ - "js 2f\n" \ - "1:\n" \ - LOCK_SECTION_START("") \ - "2:\tpushl %%eax\n\t" \ - "leal %0,%%eax\n\t" \ - "call " helper "\n\t" \ - "popl %%eax\n\t" \ - "jmp 1b\n" \ - LOCK_SECTION_END \ - :"=m" (*(volatile int *)rw) : : "memory") +#ifdef CONFIG_SPINLINE + + #define __build_read_lock_ptr(rw, helper) \ + asm volatile(LOCK "subl $1,(%0)\n\t" \ + "jns 1f\n\t" \ + "call " helper "\n\t" \ + "1:\t" \ + ::"a" (rw) : "memory") + + #define __build_read_lock_const(rw, helper) \ + asm volatile(LOCK "subl $1,%0\n\t" \ + "jns 1f\n\t" \ + "pushl %%eax\n\t" \ + "leal %0,%%eax\n\t" \ + "call " helper "\n\t" \ + "popl %%eax\n\t" \ + "1:\t" \ + :"=m" (*(volatile int *)rw) : : "memory") + +#else /* !CONFIG_SPINLINE */ + + #define __build_read_lock_ptr(rw, helper) \ + asm volatile(LOCK "subl $1,(%0)\n\t" \ + "js 2f\n" \ + "1:\n" \ + LOCK_SECTION_START("") \ + "2:\tcall " helper "\n\t" \ + "jmp 1b\n" \ + LOCK_SECTION_END \ + ::"a" (rw) : "memory") + + #define __build_read_lock_const(rw, helper) \ + asm volatile(LOCK "subl $1,%0\n\t" \ + "js 2f\n" \ + "1:\n" \ + LOCK_SECTION_START("") \ + "2:\tpushl %%eax\n\t" \ + "leal %0,%%eax\n\t" \ + "call " helper "\n\t" \ + "popl %%eax\n\t" \ + "jmp 1b\n" \ + LOCK_SECTION_END \ + :"=m" (*(volatile int *)rw) : : "memory") + +#endif /* CONFIG_SPINLINE */ + #define __build_read_lock(rw, helper) do { \ if (__builtin_constant_p(rw)) \ @@ -50,28 +74,51 @@ __build_read_lock_ptr(rw, helper); \ } while (0) -#define __build_write_lock_ptr(rw, helper) \ - asm volatile(LOCK "subl $" RW_LOCK_BIAS_STR ",(%0)\n\t" \ - "jnz 2f\n" \ - "1:\n" \ - LOCK_SECTION_START("") \ - "2:\tcall " helper "\n\t" \ - "jmp 1b\n" \ - LOCK_SECTION_END \ - ::"a" (rw) : "memory") - -#define __build_write_lock_const(rw, helper) \ - asm volatile(LOCK "subl $" RW_LOCK_BIAS_STR ",%0\n\t" \ - "jnz 2f\n" \ - "1:\n" \ - LOCK_SECTION_START("") \ - "2:\tpushl %%eax\n\t" \ - "leal %0,%%eax\n\t" \ - "call " helper "\n\t" \ - "popl %%eax\n\t" \ - "jmp 1b\n" \ - LOCK_SECTION_END \ - :"=m" (*(volatile int *)rw) : : "memory") +#ifdef CONFIG_SPINLINE + + #define __build_write_lock_ptr(rw, helper) \ + asm volatile(LOCK "subl $" RW_LOCK_BIAS_STR ",(%0)\n\t" \ + "jz 1f\n\t" \ + "call " helper "\n\t" \ + "1:\n" \ + ::"a" (rw) : "memory") + + #define __build_write_lock_const(rw, helper) \ + asm volatile(LOCK "subl $" RW_LOCK_BIAS_STR ",%0\n\t" \ + "jz 1f\n\t" \ + "pushl %%eax\n\t" \ + "leal %0,%%eax\n\t" \ + "call " helper "\n\t" \ + "popl %%eax\n\t" \ + "1:\n" \ + :"=m" (*(volatile int *)rw) : : "memory") + +#else /* !CONFIG_SPINLINE */ + + #define __build_write_lock_ptr(rw, helper) \ + asm volatile(LOCK "subl $" RW_LOCK_BIAS_STR ",(%0)\n\t" \ + "jnz 2f\n" \ + "1:\n" \ + LOCK_SECTION_START("") \ + "2:\tcall " helper "\n\t" \ + "jmp 1b\n" \ + LOCK_SECTION_END \ + ::"a" (rw) : "memory") + + #define __build_write_lock_const(rw, helper) \ + asm volatile(LOCK "subl $" RW_LOCK_BIAS_STR ",%0\n\t" \ + "jnz 2f\n" \ + "1:\n" \ + LOCK_SECTION_START("") \ + "2:\tpushl %%eax\n\t" \ + "leal %0,%%eax\n\t" \ + "call " helper "\n\t" \ + "popl %%eax\n\t" \ + "jmp 1b\n" \ + LOCK_SECTION_END \ + :"=m" (*(volatile int *)rw) : : "memory") + +#endif /* CONFIG_SPINLINE */ #define __build_write_lock(rw, helper) do { \ if (__builtin_constant_p(rw)) \ --- linux-2.6.4-rc2/include/asm-i386/smp.h 2004-02-03 20:42:38.000000000 -0800 +++ 25/include/asm-i386/smp.h 2004-03-07 20:47:15.000000000 -0800 @@ -34,11 +34,10 @@ extern void smp_alloc_memory(void); extern int pic_mode; extern int smp_num_siblings; -extern int cpu_sibling_map[]; +extern cpumask_t cpu_sibling_map[]; extern void smp_flush_tlb(void); extern void smp_message_irq(int cpl, void *dev_id, struct pt_regs *regs); -extern void smp_send_reschedule(int cpu); extern void smp_invalidate_rcv(void); /* Process an NMI */ extern void (*mtrr_hook) (void); extern void zap_low_mappings (void); --- linux-2.6.4-rc2/include/asm-i386/spinlock.h 2004-03-03 23:12:46.000000000 -0800 +++ 25/include/asm-i386/spinlock.h 2004-03-07 20:48:17.000000000 -0800 @@ -43,18 +43,35 @@ typedef struct { #define spin_is_locked(x) (*(volatile signed char *)(&(x)->lock) <= 0) #define spin_unlock_wait(x) do { barrier(); } while(spin_is_locked(x)) -#define spin_lock_string \ - "\n1:\t" \ - "lock ; decb %0\n\t" \ - "js 2f\n" \ - LOCK_SECTION_START("") \ - "2:\t" \ - "rep;nop\n\t" \ - "cmpb $0,%0\n\t" \ - "jle 2b\n\t" \ - "jmp 1b\n" \ - LOCK_SECTION_END +#ifdef CONFIG_SPINLINE + #define spin_lock_string \ + "\n1:\t" \ + "lock ; decb %0\n\t" \ + "js 2f\n" \ + "jmp 3f\n" \ + "2:\t" \ + "rep;nop\n\t" \ + "cmpb $0,%0\n\t" \ + "jle 2b\n\t" \ + "jmp 1b\n" \ + "3:\t" + +#else /* !CONFIG_SPINLINE */ + + #define spin_lock_string \ + "\n1:\t" \ + "lock ; decb %0\n\t" \ + "js 2f\n" \ + LOCK_SECTION_START("") \ + "2:\t" \ + "rep;nop\n\t" \ + "cmpb $0,%0\n\t" \ + "jle 2b\n\t" \ + "jmp 1b\n" \ + LOCK_SECTION_END + +#endif /* CONFIG_SPINLINE */ /* * This works. Despite all the confusion. * (except on PPro SMP or if we are using OOSTORE) @@ -138,6 +155,11 @@ here: */ typedef struct { volatile unsigned int lock; +#ifdef CONFIG_LOCKMETER + /* required for LOCKMETER since all bits in lock are used */ + /* and we need this storage for CPU and lock INDEX */ + unsigned lockmeter_magic; +#endif #ifdef CONFIG_DEBUG_SPINLOCK unsigned magic; #endif @@ -145,11 +167,19 @@ typedef struct { #define RWLOCK_MAGIC 0xdeaf1eed +#ifdef CONFIG_LOCKMETER +#ifdef CONFIG_DEBUG_SPINLOCK +#define RWLOCK_MAGIC_INIT , 0, RWLOCK_MAGIC +#else +#define RWLOCK_MAGIC_INIT , 0 +#endif +#else /* !CONFIG_LOCKMETER */ #ifdef CONFIG_DEBUG_SPINLOCK #define RWLOCK_MAGIC_INIT , RWLOCK_MAGIC #else #define RWLOCK_MAGIC_INIT /* */ #endif +#endif /* !CONFIG_LOCKMETER */ #define RW_LOCK_UNLOCKED (rwlock_t) { RW_LOCK_BIAS RWLOCK_MAGIC_INIT } @@ -196,4 +226,60 @@ static inline int _raw_write_trylock(rwl return 0; } +#ifdef CONFIG_LOCKMETER +static inline int _raw_read_trylock(rwlock_t *lock) +{ +/* FIXME -- replace with assembler */ + atomic_t *count = (atomic_t *)lock; + atomic_dec(count); + if (count->counter > 0) + return 1; + atomic_inc(count); + return 0; +} +#endif + +#if defined(CONFIG_LOCKMETER) && defined(CONFIG_HAVE_DEC_LOCK) +extern void _metered_spin_lock (spinlock_t *lock); +extern void _metered_spin_unlock(spinlock_t *lock); + +/* + * Matches what is in arch/i386/lib/dec_and_lock.c, except this one is + * "static inline" so that the spin_lock(), if actually invoked, is charged + * against the real caller, not against the catch-all atomic_dec_and_lock + */ +static inline int atomic_dec_and_lock(atomic_t *atomic, spinlock_t *lock) +{ + int counter; + int newcount; + +repeat: + counter = atomic_read(atomic); + newcount = counter-1; + + if (!newcount) + goto slow_path; + + asm volatile("lock; cmpxchgl %1,%2" + :"=a" (newcount) + :"r" (newcount), "m" (atomic->counter), "0" (counter)); + + /* If the above failed, "eax" will have changed */ + if (newcount != counter) + goto repeat; + return 0; + +slow_path: + preempt_disable(); + _metered_spin_lock(lock); + if (atomic_dec_and_test(atomic)) + return 1; + _metered_spin_unlock(lock); + preempt_enable(); + return 0; +} + +#define ATOMIC_DEC_AND_LOCK +#endif + #endif /* __ASM_SPINLOCK_H */ --- linux-2.6.4-rc2/include/asm-i386/string.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/asm-i386/string.h 2004-03-07 20:48:18.000000000 -0800 @@ -58,6 +58,29 @@ __asm__ __volatile__( return dest; } +/* + * This is a more generic variant of strncpy_count() suitable for + * implementing string-access routines with all sorts of return + * code semantics. It's used by mm/usercopy.c. + */ +static inline size_t strncpy_count(char * dest,const char *src,size_t count) +{ + __asm__ __volatile__( + + "1:\tdecl %0\n\t" + "js 2f\n\t" + "lodsb\n\t" + "stosb\n\t" + "testb %%al,%%al\n\t" + "jne 1b\n\t" + "2:" + "incl %0" + : "=c" (count) + :"S" (src),"D" (dest),"0" (count) : "memory"); + + return count; +} + static inline char * strcat(char * dest,const char * src) { int d0, d1, d2, d3; --- linux-2.6.4-rc2/include/asm-i386/thread_info.h 2004-03-03 23:12:46.000000000 -0800 +++ 25/include/asm-i386/thread_info.h 2004-03-07 20:48:20.000000000 -0800 @@ -9,6 +9,9 @@ #ifdef __KERNEL__ +#include +#include + #ifndef __ASSEMBLY__ #include #endif @@ -29,31 +32,30 @@ struct thread_info { __u32 cpu; /* current CPU */ __s32 preempt_count; /* 0 => preemptable, <0 => BUG */ + unsigned long previous_esp; /* ESP of the previous stack in case + of nested (IRQ) stacks + */ + mm_segment_t addr_limit; /* thread address space: 0-0xBFFFFFFF for user-thead 0-0xFFFFFFFF for kernel-thread */ - struct restart_block restart_block; + void *real_stack, *virtual_stack, *user_pgd; + struct restart_block restart_block; __u8 supervisor_stack[0]; }; -#else /* !__ASSEMBLY__ */ - -/* offsets into the thread_info struct for assembly code access */ -#define TI_TASK 0x00000000 -#define TI_EXEC_DOMAIN 0x00000004 -#define TI_FLAGS 0x00000008 -#define TI_STATUS 0x0000000C -#define TI_CPU 0x00000010 -#define TI_PRE_COUNT 0x00000014 -#define TI_ADDR_LIMIT 0x00000018 -#define TI_RESTART_BLOCK 0x000001C - #endif #define PREEMPT_ACTIVE 0x4000000 +#ifdef CONFIG_4KSTACKS +#define THREAD_SIZE (4096) +#else +#define THREAD_SIZE (8192) +#endif +#define STACK_WARN (THREAD_SIZE/8) /* * macros/functions for gaining access to the thread information structure * @@ -61,7 +63,7 @@ struct thread_info { */ #ifndef __ASSEMBLY__ -#define INIT_THREAD_INFO(tsk) \ +#define INIT_THREAD_INFO(tsk, thread_info) \ { \ .task = &tsk, \ .exec_domain = &default_exec_domain, \ @@ -72,12 +74,12 @@ struct thread_info { .restart_block = { \ .fn = do_no_restart_syscall, \ }, \ + .real_stack = &thread_info, \ } #define init_thread_info (init_thread_union.thread_info) #define init_stack (init_thread_union.stack) -#define THREAD_SIZE (2*PAGE_SIZE) /* how to get the thread information struct from C */ static inline struct thread_info *current_thread_info(void) @@ -87,6 +89,14 @@ static inline struct thread_info *curren return ti; } +/* how to get the current stack pointer from C */ +static inline unsigned long current_stack_pointer(void) +{ + unsigned long ti; + __asm__("movl %%esp,%0; ":"=r" (ti) : ); + return ti; +} + /* thread information allocation */ #ifdef CONFIG_DEBUG_STACK_USAGE #define alloc_thread_info(tsk) \ @@ -108,8 +118,6 @@ static inline struct thread_info *curren #else /* !__ASSEMBLY__ */ -#define THREAD_SIZE 8192 - /* how to get the thread information struct from ASM */ #define GET_THREAD_INFO(reg) \ movl $-THREAD_SIZE, reg; \ @@ -133,6 +141,7 @@ static inline struct thread_info *curren #define TIF_NEED_RESCHED 3 /* rescheduling necessary */ #define TIF_SINGLESTEP 4 /* restore singlestep on return to user mode */ #define TIF_IRET 5 /* return with iret */ +#define TIF_DB7 6 /* has debug registers */ #define TIF_POLLING_NRFLAG 16 /* true if poll_idle() is polling TIF_NEED_RESCHED */ #define _TIF_SYSCALL_TRACE (1<active_mm) __flush_tlb(); +#endif } static inline void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr) { +#ifndef CONFIG_X86_SWITCH_PAGETABLES if (vma->vm_mm == current->active_mm) __flush_tlb_one(addr); +#endif } static inline void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end) { +#ifndef CONFIG_X86_SWITCH_PAGETABLES if (vma->vm_mm == current->active_mm) __flush_tlb(); +#endif } #else @@ -111,11 +117,10 @@ static inline void flush_tlb_range(struc __flush_tlb() extern void flush_tlb_all(void); -extern void flush_tlb_current_task(void); extern void flush_tlb_mm(struct mm_struct *); extern void flush_tlb_page(struct vm_area_struct *, unsigned long); -#define flush_tlb() flush_tlb_current_task() +#define flush_tlb() flush_tlb_all() static inline void flush_tlb_range(struct vm_area_struct * vma, unsigned long start, unsigned long end) { --- linux-2.6.4-rc2/include/asm-i386/topology.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/asm-i386/topology.h 2004-03-07 20:47:57.000000000 -0800 @@ -66,6 +66,9 @@ static inline cpumask_t pcibus_to_cpumas return node_to_cpumask(mp_bus_id_to_node[bus]); } +/* Node-to-Node distance */ +#define node_distance(from, to) (from != to) + /* Cross-node load balancing interval. */ #define NODE_BALANCE_RATE 100 --- linux-2.6.4-rc2/include/asm-i386/uaccess.h 2003-11-09 16:45:05.000000000 -0800 +++ 25/include/asm-i386/uaccess.h 2004-03-07 20:48:18.000000000 -0800 @@ -26,7 +26,7 @@ #define KERNEL_DS MAKE_MM_SEG(0xFFFFFFFFUL) -#define USER_DS MAKE_MM_SEG(PAGE_OFFSET) +#define USER_DS MAKE_MM_SEG(TASK_SIZE) #define get_ds() (KERNEL_DS) #define get_fs() (current_thread_info()->addr_limit) @@ -149,6 +149,45 @@ extern void __get_user_4(void); :"=a" (ret),"=d" (x) \ :"0" (ptr)) +extern int get_user_size(unsigned int size, void *val, const void *ptr); +extern int put_user_size(unsigned int size, const void *val, void *ptr); +extern int zero_user_size(unsigned int size, void *ptr); +extern int copy_str_fromuser_size(unsigned int size, void *val, const void *ptr); +extern int strlen_fromuser_size(unsigned int size, const void *ptr); + + +# define indirect_get_user(x,ptr) \ +({ int __ret_gu,__val_gu; \ + __typeof__(ptr) __ptr_gu = (ptr); \ + __ret_gu = get_user_size(sizeof(*__ptr_gu), &__val_gu,__ptr_gu) ? -EFAULT : 0;\ + (x) = (__typeof__(*__ptr_gu))__val_gu; \ + __ret_gu; \ +}) +#define indirect_put_user(x,ptr) \ +({ \ + __typeof__(*(ptr)) *__ptr_pu = (ptr), __x_pu = (x); \ + put_user_size(sizeof(*__ptr_pu), &__x_pu, __ptr_pu) ? -EFAULT : 0; \ +}) +#define __indirect_put_user indirect_put_user +#define __indirect_get_user indirect_get_user + +#define indirect_copy_from_user(to,from,n) get_user_size(n,to,from) +#define indirect_copy_to_user(to,from,n) put_user_size(n,from,to) + +#define __indirect_copy_from_user indirect_copy_from_user +#define __indirect_copy_to_user indirect_copy_to_user + +#define indirect_strncpy_from_user(dst, src, count) \ + copy_str_fromuser_size(count, dst, src) + +extern int strlen_fromuser_size(unsigned int size, const void *ptr); +#define indirect_strnlen_user(str, n) strlen_fromuser_size(n, str) +#define indirect_strlen_user(str) indirect_strnlen_user(str, ~0UL >> 1) + +extern int zero_user_size(unsigned int size, void *ptr); + +#define indirect_clear_user(mem, len) zero_user_size(len, mem) +#define __indirect_clear_user clear_user /* Careful: we have to cast the result to the type of the pointer for sign reasons */ /** @@ -168,7 +207,7 @@ extern void __get_user_4(void); * Returns zero on success, or -EFAULT on error. * On error, the variable @x is set to zero. */ -#define get_user(x,ptr) \ +#define direct_get_user(x,ptr) \ ({ int __ret_gu,__val_gu; \ switch(sizeof (*(ptr))) { \ case 1: __get_user_x(1,__ret_gu,__val_gu,ptr); break; \ @@ -198,7 +237,7 @@ extern void __put_user_bad(void); * * Returns zero on success, or -EFAULT on error. */ -#define put_user(x,ptr) \ +#define direct_put_user(x,ptr) \ __put_user_check((__typeof__(*(ptr)))(x),(ptr),sizeof(*(ptr))) @@ -222,7 +261,7 @@ extern void __put_user_bad(void); * Returns zero on success, or -EFAULT on error. * On error, the variable @x is set to zero. */ -#define __get_user(x,ptr) \ +#define __direct_get_user(x,ptr) \ __get_user_nocheck((x),(ptr),sizeof(*(ptr))) @@ -245,7 +284,7 @@ extern void __put_user_bad(void); * * Returns zero on success, or -EFAULT on error. */ -#define __put_user(x,ptr) \ +#define __direct_put_user(x,ptr) \ __put_user_nocheck((__typeof__(*(ptr)))(x),(ptr),sizeof(*(ptr))) #define __put_user_nocheck(x,ptr,size) \ @@ -396,7 +435,7 @@ unsigned long __copy_from_user_ll(void * * On success, this will be zero. */ static inline unsigned long -__copy_to_user(void __user *to, const void *from, unsigned long n) +__direct_copy_to_user(void __user *to, const void *from, unsigned long n) { if (__builtin_constant_p(n)) { unsigned long ret; @@ -434,7 +473,7 @@ __copy_to_user(void __user *to, const vo * data to the requested size using zero bytes. */ static inline unsigned long -__copy_from_user(void *to, const void __user *from, unsigned long n) +__direct_copy_from_user(void *to, const void __user *from, unsigned long n) { if (__builtin_constant_p(n)) { unsigned long ret; @@ -468,11 +507,11 @@ __copy_from_user(void *to, const void __ * On success, this will be zero. */ static inline unsigned long -copy_to_user(void __user *to, const void *from, unsigned long n) +direct_copy_to_user(void __user *to, const void *from, unsigned long n) { might_sleep(); if (access_ok(VERIFY_WRITE, to, n)) - n = __copy_to_user(to, from, n); + n = __direct_copy_to_user(to, from, n); return n; } @@ -493,11 +532,11 @@ copy_to_user(void __user *to, const void * data to the requested size using zero bytes. */ static inline unsigned long -copy_from_user(void *to, const void __user *from, unsigned long n) +direct_copy_from_user(void *to, const void __user *from, unsigned long n) { might_sleep(); if (access_ok(VERIFY_READ, from, n)) - n = __copy_from_user(to, from, n); + n = __direct_copy_from_user(to, from, n); else memset(to, 0, n); return n; @@ -520,10 +559,68 @@ long __strncpy_from_user(char *dst, cons * If there is a limit on the length of a valid string, you may wish to * consider using strnlen_user() instead. */ -#define strlen_user(str) strnlen_user(str, ~0UL >> 1) -long strnlen_user(const char __user *str, long n); -unsigned long clear_user(void __user *mem, unsigned long len); -unsigned long __clear_user(void __user *mem, unsigned long len); +long direct_strncpy_from_user(char *dst, const char *src, long count); +long __direct_strncpy_from_user(char *dst, const char *src, long count); +#define direct_strlen_user(str) direct_strnlen_user(str, ~0UL >> 1) +long direct_strnlen_user(const char *str, long n); +unsigned long direct_clear_user(void *mem, unsigned long len); +unsigned long __direct_clear_user(void *mem, unsigned long len); + +extern int indirect_uaccess; + +#ifdef CONFIG_X86_UACCESS_INDIRECT + +/* + * Return code and zeroing semantics: + + __clear_user 0 <-> bytes not done + clear_user 0 <-> bytes not done + __copy_to_user 0 <-> bytes not done + copy_to_user 0 <-> bytes not done + __copy_from_user 0 <-> bytes not done, zero rest + copy_from_user 0 <-> bytes not done, zero rest + __get_user 0 <-> -EFAULT + get_user 0 <-> -EFAULT + __put_user 0 <-> -EFAULT + put_user 0 <-> -EFAULT + strlen_user strlen + 1 <-> 0 + strnlen_user strlen + 1 (or n+1) <-> 0 + strncpy_from_user strlen (or n) <-> -EFAULT + + */ + +#define __clear_user(mem,len) __indirect_clear_user(mem,len) +#define clear_user(mem,len) indirect_clear_user(mem,len) +#define __copy_to_user(to,from,n) __indirect_copy_to_user(to,from,n) +#define copy_to_user(to,from,n) indirect_copy_to_user(to,from,n) +#define __copy_from_user(to,from,n) __indirect_copy_from_user(to,from,n) +#define copy_from_user(to,from,n) indirect_copy_from_user(to,from,n) +#define __get_user(val,ptr) __indirect_get_user(val,ptr) +#define get_user(val,ptr) indirect_get_user(val,ptr) +#define __put_user(val,ptr) __indirect_put_user(val,ptr) +#define put_user(val,ptr) indirect_put_user(val,ptr) +#define strlen_user(str) indirect_strlen_user(str) +#define strnlen_user(src,count) indirect_strnlen_user(src,count) +#define strncpy_from_user(dst,src,count) \ + indirect_strncpy_from_user(dst,src,count) + +#else + +#define __clear_user __direct_clear_user +#define clear_user direct_clear_user +#define __copy_to_user __direct_copy_to_user +#define copy_to_user direct_copy_to_user +#define __copy_from_user __direct_copy_from_user +#define copy_from_user direct_copy_from_user +#define __get_user __direct_get_user +#define get_user direct_get_user +#define __put_user __direct_put_user +#define put_user direct_put_user +#define strlen_user direct_strlen_user +#define strnlen_user direct_strnlen_user +#define strncpy_from_user direct_strncpy_from_user + +#endif /* CONFIG_X86_UACCESS_INDIRECT */ #endif /* __i386_UACCESS_H */ --- linux-2.6.4-rc2/include/asm-i386/unistd.h 2004-03-03 23:12:46.000000000 -0800 +++ 25/include/asm-i386/unistd.h 2004-03-07 20:48:04.000000000 -0800 @@ -262,7 +262,7 @@ #define __NR_epoll_create 254 #define __NR_epoll_ctl 255 #define __NR_epoll_wait 256 -#define __NR_remap_file_pages 257 +#define __NR_old_remap_file_pages 257 #define __NR_set_tid_address 258 #define __NR_timer_create 259 #define __NR_timer_settime (__NR_timer_create+1) @@ -279,8 +279,15 @@ #define __NR_utimes 271 #define __NR_fadvise64_64 272 #define __NR_vserver 273 +#define __NR_mq_open 274 +#define __NR_mq_unlink (__NR_mq_open+1) +#define __NR_mq_timedsend (__NR_mq_open+2) +#define __NR_mq_timedreceive (__NR_mq_open+3) +#define __NR_mq_notify (__NR_mq_open+4) +#define __NR_mq_getsetattr (__NR_mq_open+5) +#define __NR_remap_file_pages 280 -#define NR_syscalls 274 +#define NR_syscalls 281 /* user-visible error numbers are in the range -1 - -124: see */ @@ -375,6 +382,7 @@ __syscall_return(type,__res); \ #include #include +#include #include /* --- linux-2.6.4-rc2/include/asm-ia64/acpi.h 2004-02-03 20:42:38.000000000 -0800 +++ 25/include/asm-ia64/acpi.h 2004-03-07 20:46:46.000000000 -0800 @@ -88,6 +88,8 @@ ia64_acpi_release_global_lock (unsigned #define ACPI_RELEASE_GLOBAL_LOCK(GLptr, Acq) \ ((Acq) = ia64_acpi_release_global_lock((unsigned int *) GLptr)) +#define acpi_strict 1 /* no ACPI spec workarounds on IA64 */ + const char *acpi_get_sysname (void); int acpi_request_vector (u32 int_type); int acpi_register_irq (u32 gsi, u32 polarity, u32 trigger); --- linux-2.6.4-rc2/include/asm-ia64/compat.h 2003-08-08 22:55:13.000000000 -0700 +++ 25/include/asm-ia64/compat.h 2004-03-07 20:47:07.000000000 -0800 @@ -26,6 +26,8 @@ typedef u16 compat_ipc_pid_t; typedef s32 compat_daddr_t; typedef u32 compat_caddr_t; typedef __kernel_fsid_t compat_fsid_t; +typedef s32 compat_key_t; +typedef u32 compat_timer_t; typedef s32 compat_int_t; typedef s32 compat_long_t; @@ -116,6 +118,64 @@ typedef u32 compat_sigset_word; #define COMPAT_OFF_T_MAX 0x7fffffff #define COMPAT_LOFF_T_MAX 0x7fffffffffffffffL +struct compat_ipc64_perm { + compat_key_t key; + compat_uid32_t uid; + compat_gid32_t gid; + compat_uid32_t cuid; + compat_gid32_t cgid; + unsigned short mode; + unsigned short __pad1; + unsigned short seq; + unsigned short __pad2; + compat_ulong_t unused1; + compat_ulong_t unused2; +}; + +struct compat_semid64_ds { + struct compat_ipc64_perm sem_perm; + compat_time_t sem_otime; + compat_ulong_t __unused1; + compat_time_t sem_ctime; + compat_ulong_t __unused2; + compat_ulong_t sem_nsems; + compat_ulong_t __unused3; + compat_ulong_t __unused4; +}; + +struct compat_msqid64_ds { + struct compat_ipc64_perm msg_perm; + compat_time_t msg_stime; + compat_ulong_t __unused1; + compat_time_t msg_rtime; + compat_ulong_t __unused2; + compat_time_t msg_ctime; + compat_ulong_t __unused3; + compat_ulong_t msg_cbytes; + compat_ulong_t msg_qnum; + compat_ulong_t msg_qbytes; + compat_pid_t msg_lspid; + compat_pid_t msg_lrpid; + compat_ulong_t __unused4; + compat_ulong_t __unused5; +}; + +struct compat_shmid64_ds { + struct compat_ipc64_perm shm_perm; + compat_size_t shm_segsz; + compat_time_t shm_atime; + compat_ulong_t __unused1; + compat_time_t shm_dtime; + compat_ulong_t __unused2; + compat_time_t shm_ctime; + compat_ulong_t __unused3; + compat_pid_t shm_cpid; + compat_pid_t shm_lpid; + compat_ulong_t shm_nattch; + compat_ulong_t __unused4; + compat_ulong_t __unused5; +}; + /* * A pointer passed in from user mode. This should not be used for syscall parameters, * just declare them as pointers because the syscall entry code will have appropriately --- linux-2.6.4-rc2/include/asm-ia64/dma-mapping.h 2003-06-14 12:18:29.000000000 -0700 +++ 25/include/asm-ia64/dma-mapping.h 2004-03-07 20:46:59.000000000 -0800 @@ -14,8 +14,10 @@ #define dma_map_sg platform_dma_map_sg #define dma_unmap_single platform_dma_unmap_single #define dma_unmap_sg platform_dma_unmap_sg -#define dma_sync_single platform_dma_sync_single -#define dma_sync_sg platform_dma_sync_sg +#define dma_sync_single_for_cpu platform_dma_sync_single_for_cpu +#define dma_sync_sg_for_cpu platform_dma_sync_sg_for_cpu +#define dma_sync_single_for_device platform_dma_sync_single_for_device +#define dma_sync_sg_for_device platform_dma_sync_sg_for_device #define dma_map_page(dev, pg, off, size, dir) \ dma_map_single(dev, page_address(pg) + (off), (size), (dir)) @@ -27,8 +29,10 @@ * See Documentation/DMA-API.txt for details. */ -#define dma_sync_single_range(dev, dma_handle, offset, size, dir) \ - dma_sync_single(dev, dma_handle, size, dir) +#define dma_sync_single_range_for_cpu(dev, dma_handle, offset, size, dir) \ + dma_sync_single_for_cpu(dev, dma_handle, size, dir) +#define dma_sync_single_range_for_device(dev, dma_handle, offset, size, dir) \ + dma_sync_single_for_device(dev, dma_handle, size, dir) #define dma_supported platform_dma_supported --- linux-2.6.4-rc2/include/asm-ia64/irq.h 2003-06-14 12:18:48.000000000 -0700 +++ 25/include/asm-ia64/irq.h 2004-03-07 20:48:21.000000000 -0800 @@ -29,4 +29,8 @@ extern void disable_irq_nosync (unsigned extern void enable_irq (unsigned int); extern void set_irq_affinity_info (unsigned int irq, int dest, int redir); +struct irqaction; +struct pt_regs; +int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); + #endif /* _ASM_IA64_IRQ_H */ --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/include/asm-ia64/lockmeter.h 2004-03-07 20:48:17.000000000 -0800 @@ -0,0 +1,72 @@ +/* + * Copyright (C) 1999,2000 Silicon Graphics, Inc. + * + * Written by John Hawkes (hawkes@sgi.com) + * Based on klstat.h by Jack Steiner (steiner@sgi.com) + */ + +#ifndef _IA64_LOCKMETER_H +#define _IA64_LOCKMETER_H + +#ifdef local_cpu_data +#define CPU_CYCLE_FREQUENCY local_cpu_data->itc_freq +#else +#define CPU_CYCLE_FREQUENCY my_cpu_data.itc_freq +#endif +#define get_cycles64() get_cycles() + +#define THIS_CPU_NUMBER smp_processor_id() + +/* + * macros to cache and retrieve an index value inside of a lock + * these macros assume that there are less than 65536 simultaneous + * (read mode) holders of a rwlock. + * we also assume that the hash table has less than 32767 entries. + */ +/* + * instrumented spinlock structure -- never used to allocate storage + * only used in macros below to overlay a spinlock_t + */ +typedef struct inst_spinlock_s { + /* remember, Intel is little endian */ + volatile unsigned short lock; + volatile unsigned short index; +} inst_spinlock_t; +#define PUT_INDEX(lock_ptr,indexv) ((inst_spinlock_t *)(lock_ptr))->index = indexv +#define GET_INDEX(lock_ptr) ((inst_spinlock_t *)(lock_ptr))->index + +/* + * macros to cache and retrieve an index value in a read/write lock + * as well as the cpu where a reader busy period started + * we use the 2nd word (the debug word) for this, so require the + * debug word to be present + */ +/* + * instrumented rwlock structure -- never used to allocate storage + * only used in macros below to overlay a rwlock_t + */ +typedef struct inst_rwlock_s { + volatile int read_counter:31; + volatile int write_lock:1; + volatile unsigned short index; + volatile unsigned short cpu; +} inst_rwlock_t; +#define PUT_RWINDEX(rwlock_ptr,indexv) ((inst_rwlock_t *)(rwlock_ptr))->index = indexv +#define GET_RWINDEX(rwlock_ptr) ((inst_rwlock_t *)(rwlock_ptr))->index +#define PUT_RW_CPU(rwlock_ptr,cpuv) ((inst_rwlock_t *)(rwlock_ptr))->cpu = cpuv +#define GET_RW_CPU(rwlock_ptr) ((inst_rwlock_t *)(rwlock_ptr))->cpu + +/* + * return the number of readers for a rwlock_t + */ +#define RWLOCK_READERS(rwlock_ptr) ((rwlock_ptr)->read_counter) + +/* + * return true if rwlock is write locked + * (note that other lock attempts can cause the lock value to be negative) + */ +#define RWLOCK_IS_WRITE_LOCKED(rwlock_ptr) ((rwlock_ptr)->write_lock) +#define RWLOCK_IS_READ_LOCKED(rwlock_ptr) ((rwlock_ptr)->read_counter) + +#endif /* _IA64_LOCKMETER_H */ + --- linux-2.6.4-rc2/include/asm-ia64/machvec.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/asm-ia64/machvec.h 2004-03-07 20:46:59.000000000 -0800 @@ -42,8 +42,10 @@ typedef dma_addr_t ia64_mv_dma_map_singl typedef void ia64_mv_dma_unmap_single (struct device *, dma_addr_t, size_t, int); typedef int ia64_mv_dma_map_sg (struct device *, struct scatterlist *, int, int); typedef void ia64_mv_dma_unmap_sg (struct device *, struct scatterlist *, int, int); -typedef void ia64_mv_dma_sync_single (struct device *, dma_addr_t, size_t, int); -typedef void ia64_mv_dma_sync_sg (struct device *, struct scatterlist *, int, int); +typedef void ia64_mv_dma_sync_single_for_cpu (struct device *, dma_addr_t, size_t, int); +typedef void ia64_mv_dma_sync_sg_for_cpu (struct device *, struct scatterlist *, int, int); +typedef void ia64_mv_dma_sync_single_for_device (struct device *, dma_addr_t, size_t, int); +typedef void ia64_mv_dma_sync_sg_for_device (struct device *, struct scatterlist *, int, int); typedef int ia64_mv_dma_supported (struct device *, u64); /* @@ -104,8 +106,10 @@ extern void machvec_memory_fence (void); # define platform_dma_unmap_single ia64_mv.dma_unmap_single # define platform_dma_map_sg ia64_mv.dma_map_sg # define platform_dma_unmap_sg ia64_mv.dma_unmap_sg -# define platform_dma_sync_single ia64_mv.dma_sync_single -# define platform_dma_sync_sg ia64_mv.dma_sync_sg +# define platform_dma_sync_single_for_cpu ia64_mv.dma_sync_single_for_cpu +# define platform_dma_sync_sg_for_cpu ia64_mv.dma_sync_sg_for_cpu +# define platform_dma_sync_single_for_device ia64_mv.dma_sync_single_for_device +# define platform_dma_sync_sg_for_device ia64_mv.dma_sync_sg_for_device # define platform_dma_supported ia64_mv.dma_supported # define platform_irq_desc ia64_mv.irq_desc # define platform_irq_to_vector ia64_mv.irq_to_vector @@ -150,8 +154,10 @@ struct ia64_machine_vector { ia64_mv_dma_unmap_single *dma_unmap_single; ia64_mv_dma_map_sg *dma_map_sg; ia64_mv_dma_unmap_sg *dma_unmap_sg; - ia64_mv_dma_sync_single *dma_sync_single; - ia64_mv_dma_sync_sg *dma_sync_sg; + ia64_mv_dma_sync_single *dma_sync_single_for_cpu; + ia64_mv_dma_sync_sg *dma_sync_sg_for_cpu; + ia64_mv_dma_sync_single *dma_sync_single_for_device; + ia64_mv_dma_sync_sg *dma_sync_sg_for_device; ia64_mv_dma_supported *dma_supported; ia64_mv_irq_desc *irq_desc; ia64_mv_irq_to_vector *irq_to_vector; @@ -192,8 +198,10 @@ struct ia64_machine_vector { platform_dma_unmap_single, \ platform_dma_map_sg, \ platform_dma_unmap_sg, \ - platform_dma_sync_single, \ - platform_dma_sync_sg, \ + platform_dma_sync_single_for_cpu, \ + platform_dma_sync_sg_for_cpu, \ + platform_dma_sync_single_for_device, \ + platform_dma_sync_sg_for_device, \ platform_dma_supported, \ platform_irq_desc, \ platform_irq_to_vector, \ @@ -231,8 +239,10 @@ extern ia64_mv_dma_map_single swiotlb_m extern ia64_mv_dma_unmap_single swiotlb_unmap_single; extern ia64_mv_dma_map_sg swiotlb_map_sg; extern ia64_mv_dma_unmap_sg swiotlb_unmap_sg; -extern ia64_mv_dma_sync_single swiotlb_sync_single; -extern ia64_mv_dma_sync_sg swiotlb_sync_sg; +extern ia64_mv_dma_sync_single_for_cpu swiotlb_sync_single_for_cpu; +extern ia64_mv_dma_sync_sg_for_cpu swiotlb_sync_sg_for_cpu; +extern ia64_mv_dma_sync_single_for_device swiotlb_sync_single_for_device; +extern ia64_mv_dma_sync_sg_for_device swiotlb_sync_sg_for_device; extern ia64_mv_dma_supported swiotlb_dma_supported; /* @@ -290,11 +300,17 @@ extern ia64_mv_dma_supported swiotlb_dm #ifndef platform_dma_unmap_sg # define platform_dma_unmap_sg swiotlb_unmap_sg #endif -#ifndef platform_dma_sync_single -# define platform_dma_sync_single swiotlb_sync_single +#ifndef platform_dma_sync_single_for_cpu +# define platform_dma_sync_single_for_cpu swiotlb_sync_single_for_cpu #endif -#ifndef platform_dma_sync_sg -# define platform_dma_sync_sg swiotlb_sync_sg +#ifndef platform_dma_sync_sg_for_cpu +# define platform_dma_sync_sg_for_cpu swiotlb_sync_sg_for_cpu +#endif +#ifndef platform_dma_sync_single_for_device +# define platform_dma_sync_single_for_device swiotlb_sync_single_for_device +#endif +#ifndef platform_dma_sync_sg_for_device +# define platform_dma_sync_sg_for_device swiotlb_sync_sg_for_device #endif #ifndef platform_dma_supported # define platform_dma_supported swiotlb_dma_supported --- linux-2.6.4-rc2/include/asm-ia64/machvec_hpzx1.h 2003-06-14 12:17:58.000000000 -0700 +++ 25/include/asm-ia64/machvec_hpzx1.h 2004-03-07 20:46:59.000000000 -0800 @@ -26,8 +26,10 @@ extern ia64_mv_dma_supported sba_dma_su #define platform_dma_unmap_single sba_unmap_single #define platform_dma_map_sg sba_map_sg #define platform_dma_unmap_sg sba_unmap_sg -#define platform_dma_sync_single ((ia64_mv_dma_sync_single *) machvec_memory_fence) -#define platform_dma_sync_sg ((ia64_mv_dma_sync_sg *) machvec_memory_fence) +#define platform_dma_sync_single_for_cpu ((ia64_mv_dma_sync_single_for_cpu *) machvec_memory_fence) +#define platform_dma_sync_sg_for_cpu ((ia64_mv_dma_sync_sg_for_cpu *) machvec_memory_fence) +#define platform_dma_sync_single_for_device ((ia64_mv_dma_sync_single_for_device *) machvec_memory_fence) +#define platform_dma_sync_sg_for_device ((ia64_mv_dma_sync_sg_for_device *) machvec_memory_fence) #define platform_dma_supported sba_dma_supported #endif /* _ASM_IA64_MACHVEC_HPZX1_h */ --- linux-2.6.4-rc2/include/asm-ia64/machvec_sn2.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/asm-ia64/machvec_sn2.h 2004-03-07 20:46:59.000000000 -0800 @@ -62,8 +62,10 @@ extern ia64_mv_dma_map_single sn_dma_ma extern ia64_mv_dma_unmap_single sn_dma_unmap_single; extern ia64_mv_dma_map_sg sn_dma_map_sg; extern ia64_mv_dma_unmap_sg sn_dma_unmap_sg; -extern ia64_mv_dma_sync_single sn_dma_sync_single; -extern ia64_mv_dma_sync_sg sn_dma_sync_sg; +extern ia64_mv_dma_sync_single_for_cpu sn_dma_sync_single_for_cpu; +extern ia64_mv_dma_sync_sg_for_cpu sn_dma_sync_sg_for_cpu; +extern ia64_mv_dma_sync_single_for_device sn_dma_sync_single_for_device; +extern ia64_mv_dma_sync_sg_for_device sn_dma_sync_sg_for_device; extern ia64_mv_dma_supported sn_dma_supported; /* @@ -105,8 +107,10 @@ extern ia64_mv_dma_supported sn_dma_sup #define platform_dma_unmap_single sn_dma_unmap_single #define platform_dma_map_sg sn_dma_map_sg #define platform_dma_unmap_sg sn_dma_unmap_sg -#define platform_dma_sync_single sn_dma_sync_single -#define platform_dma_sync_sg sn_dma_sync_sg +#define platform_dma_sync_single_for_cpu sn_dma_sync_single_for_cpu +#define platform_dma_sync_sg_for_cpu sn_dma_sync_sg_for_cpu +#define platform_dma_sync_single_for_device sn_dma_sync_single_for_device +#define platform_dma_sync_sg_for_device sn_dma_sync_sg_for_device #define platform_dma_supported sn_dma_supported #include --- linux-2.6.4-rc2/include/asm-ia64/pci.h 2004-03-03 23:12:47.000000000 -0800 +++ 25/include/asm-ia64/pci.h 2004-03-07 20:46:59.000000000 -0800 @@ -76,7 +76,8 @@ extern int pcibios_prep_mwi (struct pci_ #define pci_dac_page_to_dma(dev,pg,off,dir) ((dma_addr_t) page_to_bus(pg) + (off)) #define pci_dac_dma_to_page(dev,dma_addr) (virt_to_page(bus_to_virt(dma_addr))) #define pci_dac_dma_to_offset(dev,dma_addr) offset_in_page(dma_addr) -#define pci_dac_dma_sync_single(dev,dma_addr,len,dir) do { mb(); } while (0) +#define pci_dac_dma_sync_single_for_cpu(dev,dma_addr,len,dir) do { } while (0) +#define pci_dac_dma_sync_single_for_device(dev,dma_addr,len,dir) do { mb(); } while (0) #define sg_dma_len(sg) ((sg)->dma_length) #define sg_dma_address(sg) ((sg)->dma_address) --- linux-2.6.4-rc2/include/asm-ia64/spinlock.h 2004-01-09 00:04:32.000000000 -0800 +++ 25/include/asm-ia64/spinlock.h 2004-03-07 20:48:17.000000000 -0800 @@ -110,8 +110,18 @@ do { \ typedef struct { volatile int read_counter : 31; volatile int write_lock : 1; +#ifdef CONFIG_LOCKMETER + /* required for LOCKMETER since all bits in lock are used */ + /* and we need this storage for CPU and lock INDEX */ + unsigned lockmeter_magic; +#endif } rwlock_t; + +#ifdef CONFIG_LOCKMETER +#define RW_LOCK_UNLOCKED (rwlock_t) { 0, 0, 0 } +#else #define RW_LOCK_UNLOCKED (rwlock_t) { 0, 0 } +#endif #define rwlock_init(x) do { *(x) = RW_LOCK_UNLOCKED; } while(0) #define rwlock_is_locked(x) (*(volatile int *) (x) != 0) @@ -127,6 +137,48 @@ do { \ } \ } while (0) +#ifdef CONFIG_LOCKMETER +/* + * HACK: This works, but still have a timing window that affects performance: + * we see that no one owns the Write lock, then someone * else grabs for Write + * lock before we do a read_lock(). + * This means that on rare occasions our read_lock() will stall and spin-wait + * until we acquire for Read, instead of simply returning a trylock failure. + */ +static inline int _raw_read_trylock(rwlock_t *rw) +{ + if (rw->write_lock) { + return 0; + } else { + _raw_read_lock(rw); + return 1; + } +} + +static inline int _raw_write_trylock(rwlock_t *rw) +{ + if (!(rw->write_lock)) { + /* isn't currently write-locked... that looks promising... */ + if (test_and_set_bit(31, rw) == 0) { + /* now it is write-locked by me... */ + if (rw->read_counter) { + /* really read-locked, so release write-lock and fail */ + clear_bit(31, rw); + } else { + /* we've the the write-lock, no read-lockers... success! */ + barrier(); + return 1; + } + + } + } + + /* falls through ... fails to write-lock */ + barrier(); + return 0; +} +#endif + #define _raw_read_unlock(rw) \ do { \ rwlock_t *__read_lock_ptr = (rw); \ @@ -190,4 +242,25 @@ do { \ clear_bit(31, (x)); \ }) +#ifdef CONFIG_LOCKMETER +extern void _metered_spin_lock (spinlock_t *lock); +extern void _metered_spin_unlock(spinlock_t *lock); + +/* + * Use a less efficient, and inline, atomic_dec_and_lock() if lockmetering + * so we can see the callerPC of who is actually doing the spin_lock(). + * Otherwise, all we see is the generic rollup of all locks done by + * atomic_dec_and_lock(). + */ +static inline int atomic_dec_and_lock(atomic_t *atomic, spinlock_t *lock) +{ + _metered_spin_lock(lock); + if (atomic_dec_and_test(atomic)) + return 1; + _metered_spin_unlock(lock); + return 0; +} +#define ATOMIC_DEC_AND_LOCK +#endif + #endif /* _ASM_IA64_SPINLOCK_H */ --- linux-2.6.4-rc2/include/asm-ia64/tlb.h 2004-03-03 23:12:47.000000000 -0800 +++ 25/include/asm-ia64/tlb.h 2004-03-07 20:47:22.000000000 -0800 @@ -211,6 +211,8 @@ __tlb_remove_tlb_entry (struct mmu_gathe tlb->end_addr = address + PAGE_SIZE; } +#define tlb_migrate_prepare(mm) flush_tlb_mm(mm) + #define tlb_start_vma(tlb, vma) do { } while (0) #define tlb_end_vma(tlb, vma) do { } while (0) --- linux-2.6.4-rc2/include/asm-m68k/irq.h 2004-03-03 23:12:47.000000000 -0800 +++ 25/include/asm-m68k/irq.h 2004-03-07 20:48:21.000000000 -0800 @@ -76,10 +76,10 @@ extern void (*disable_irq)(unsigned int) struct pt_regs; -extern int sys_request_irq(unsigned int, - irqreturn_t (*)(int, void *, struct pt_regs *), - unsigned long, const char *, void *); -extern void sys_free_irq(unsigned int, void *); +extern int cpu_request_irq(unsigned int, + irqreturn_t (*)(int, void *, struct pt_regs *), + unsigned long, const char *, void *); +extern void cpu_free_irq(unsigned int, void *); /* * various flags for request_irq() - the Amiga now uses the standard @@ -124,4 +124,8 @@ extern volatile unsigned int num_spuriou */ extern irq_node_t *new_irq_node(void); +struct irqaction; +struct pt_regs; +int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); + #endif /* _M68K_IRQ_H_ */ --- linux-2.6.4-rc2/include/asm-m68knommu/irq.h 2004-03-03 23:12:47.000000000 -0800 +++ 25/include/asm-m68knommu/irq.h 2004-03-07 20:48:21.000000000 -0800 @@ -121,4 +121,8 @@ extern irq_node_t *new_irq_node(void); #define enable_irq_nosync(x) enable_irq(x) #define disable_irq_nosync(x) disable_irq(x) +struct irqaction; +struct pt_regs; +int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); + #endif /* _M68K_IRQ_H_ */ --- linux-2.6.4-rc2/include/asm-m68knommu/unistd.h 2004-03-03 23:12:47.000000000 -0800 +++ 25/include/asm-m68knommu/unistd.h 2004-03-07 20:47:07.000000000 -0800 @@ -416,7 +416,6 @@ asmlinkage long sys_mmap2(unsigned long asmlinkage int sys_execve(char *name, char **argv, char **envp); asmlinkage int sys_pipe(unsigned long *fildes); asmlinkage int sys_ptrace(long request, long pid, long addr, long data); -asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int on); struct pt_regs; int sys_request_irq(unsigned int, irqreturn_t (*)(int, void *, struct pt_regs *), --- linux-2.6.4-rc2/include/asm-m68k/unistd.h 2004-03-03 23:12:47.000000000 -0800 +++ 25/include/asm-m68k/unistd.h 2004-03-07 20:47:07.000000000 -0800 @@ -374,7 +374,6 @@ asmlinkage long sys_mmap2( asmlinkage int sys_execve(char *name, char **argv, char **envp); asmlinkage int sys_pipe(unsigned long *fildes); asmlinkage int sys_ptrace(long request, long pid, long addr, long data); -asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int on); struct pt_regs; struct sigaction; asmlinkage long sys_rt_sigaction(int sig, --- linux-2.6.4-rc2/include/asm-mips/dma-mapping.h 2004-03-03 23:12:47.000000000 -0800 +++ 25/include/asm-mips/dma-mapping.h 2004-03-07 20:46:59.000000000 -0800 @@ -29,11 +29,17 @@ extern void dma_unmap_page(struct device size_t size, enum dma_data_direction direction); extern void dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nhwentries, enum dma_data_direction direction); -extern void dma_sync_single(struct device *dev, dma_addr_t dma_handle, +extern void dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, size_t size, enum dma_data_direction direction); -extern void dma_sync_single_range(struct device *dev, dma_addr_t dma_handle, +extern void dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, + size_t size, enum dma_data_direction direction); +extern void dma_sync_single_range_for_cpu(struct device *dev, dma_addr_t dma_handle, + unsigned long offset, size_t size, enum dma_data_direction direction); +extern void dma_sync_single_range_for_device(struct device *dev, dma_addr_t dma_handle, unsigned long offset, size_t size, enum dma_data_direction direction); -extern void dma_sync_sg(struct device *dev, struct scatterlist *sg, int nelems, +extern void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems, + enum dma_data_direction direction); +extern void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems, enum dma_data_direction direction); extern int dma_supported(struct device *dev, u64 mask); --- linux-2.6.4-rc2/include/asm-mips/irq.h 2004-03-03 23:12:47.000000000 -0800 +++ 25/include/asm-mips/irq.h 2004-03-07 20:48:21.000000000 -0800 @@ -31,4 +31,7 @@ extern asmlinkage unsigned int do_IRQ(in extern void init_generic_irq(void); +struct irqaction; +int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); + #endif /* _ASM_IRQ_H */ --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/include/asm-mips/lockmeter.h 2004-03-07 20:48:17.000000000 -0800 @@ -0,0 +1,126 @@ +/* + * Copyright (C) 1999,2000 Silicon Graphics, Inc. + * + * Written by John Hawkes (hawkes@sgi.com) + * Based on klstat.h by Jack Steiner (steiner@sgi.com) + * Ported to mips32 for Asita Technologies + * by D.J. Barrow ( dj.barrow@asitatechnologies.com ) + */ +#ifndef _ASM_LOCKMETER_H +#define _ASM_LOCKMETER_H + +/* do_gettimeoffset is a function pointer on mips */ +/* & it is not included by */ +#include +#include +#include + +#define SPINLOCK_MAGIC_INIT /* */ + +#define CPU_CYCLE_FREQUENCY get_cpu_cycle_frequency() + +#define THIS_CPU_NUMBER smp_processor_id() + +static uint32_t cpu_cycle_frequency = 0; + +static uint32_t get_cpu_cycle_frequency(void) +{ + /* a total hack, slow and invasive, but ... it works */ + int sec; + uint32_t start_cycles; + struct timeval tv; + + if (cpu_cycle_frequency == 0) { /* uninitialized */ + do_gettimeofday(&tv); + sec = tv.tv_sec; /* set up to catch the tv_sec rollover */ + while (sec == tv.tv_sec) { do_gettimeofday(&tv); } + sec = tv.tv_sec; /* rolled over to a new sec value */ + start_cycles = get_cycles(); + while (sec == tv.tv_sec) { do_gettimeofday(&tv); } + cpu_cycle_frequency = get_cycles() - start_cycles; + } + + return cpu_cycle_frequency; +} + +extern struct timeval xtime; + +static uint64_t get_cycles64(void) +{ + static uint64_t last_get_cycles64 = 0; + uint64_t ret; + unsigned long sec; + unsigned long usec, usec_offset; + +again: + sec = xtime.tv_sec; + usec = xtime.tv_usec; + usec_offset = do_gettimeoffset(); + if ((xtime.tv_sec != sec) || + (xtime.tv_usec != usec)|| + (usec_offset >= 20000)) + goto again; + + ret = ((uint64_t)(usec + usec_offset) * cpu_cycle_frequency); + /* We can't do a normal 64 bit division on mips without libgcc.a */ + do_div(ret,1000000); + ret += ((uint64_t)sec * cpu_cycle_frequency); + + /* XXX why does time go backwards? do_gettimeoffset? general time adj? */ + if (ret <= last_get_cycles64) + ret = last_get_cycles64+1; + last_get_cycles64 = ret; + + return ret; +} + +/* + * macros to cache and retrieve an index value inside of a lock + * these macros assume that there are less than 65536 simultaneous + * (read mode) holders of a rwlock. + * we also assume that the hash table has less than 32767 entries. + * the high order bit is used for write locking a rw_lock + */ +#define INDEX_MASK 0x7FFF0000 +#define READERS_MASK 0x0000FFFF +#define INDEX_SHIFT 16 +#define PUT_INDEX(lockp,index) \ + lockp->lock = (((lockp->lock) & ~INDEX_MASK) | (index) << INDEX_SHIFT) +#define GET_INDEX(lockp) \ + (((lockp->lock) & INDEX_MASK) >> INDEX_SHIFT) + +/* + * macros to cache and retrieve an index value in a read/write lock + * as well as the cpu where a reader busy period started + * we use the 2nd word (the debug word) for this, so require the + * debug word to be present + */ +/* + * instrumented rwlock structure -- never used to allocate storage + * only used in macros below to overlay a rwlock_t + */ +typedef struct inst_rwlock_s { + volatile int lock; + unsigned short index; + unsigned short cpu; +} inst_rwlock_t; +#define PUT_RWINDEX(rwlock_ptr,indexv) ((inst_rwlock_t *)(rwlock_ptr))->index = indexv +#define GET_RWINDEX(rwlock_ptr) ((inst_rwlock_t *)(rwlock_ptr))->index +#define PUT_RW_CPU(rwlock_ptr,cpuv) ((inst_rwlock_t *)(rwlock_ptr))->cpu = cpuv +#define GET_RW_CPU(rwlock_ptr) ((inst_rwlock_t *)(rwlock_ptr))->cpu + +/* + * return the number of readers for a rwlock_t + */ +#define RWLOCK_READERS(rwlock_ptr) rwlock_readers(rwlock_ptr) + +extern inline int rwlock_readers(rwlock_t *rwlock_ptr) +{ + int tmp = (int) rwlock_ptr->lock; + return (tmp >= 0) ? tmp : 0; +} + +#define RWLOCK_IS_WRITE_LOCKED(rwlock_ptr) ((rwlock_ptr)->lock < 0) +#define RWLOCK_IS_READ_LOCKED(rwlock_ptr) ((rwlock_ptr)->lock > 0) + +#endif /* _ASM_LOCKMETER_H */ --- linux-2.6.4-rc2/include/asm-mips/pci.h 2004-03-03 23:12:48.000000000 -0800 +++ 25/include/asm-mips/pci.h 2004-03-07 20:46:59.000000000 -0800 @@ -82,7 +82,9 @@ extern struct page *pci_dac_dma_to_page( dma64_addr_t dma_addr); extern unsigned long pci_dac_dma_to_offset(struct pci_dev *pdev, dma64_addr_t dma_addr); -extern void pci_dac_dma_sync_single(struct pci_dev *pdev, +extern void pci_dac_dma_sync_single_for_cpu(struct pci_dev *pdev, + dma64_addr_t dma_addr, size_t len, int direction); +extern void pci_dac_dma_sync_single_for_device(struct pci_dev *pdev, dma64_addr_t dma_addr, size_t len, int direction); #endif /* __KERNEL__ */ --- linux-2.6.4-rc2/include/asm-mips/spinlock.h 2004-03-03 23:12:48.000000000 -0800 +++ 25/include/asm-mips/spinlock.h 2004-03-07 20:48:17.000000000 -0800 @@ -91,9 +91,18 @@ static inline unsigned int _raw_spin_try typedef struct { volatile unsigned int lock; +#ifdef CONFIG_LOCKMETER + /* required for LOCKMETER since all bits in lock are used */ + /* and we need this storage for CPU and lock INDEX */ + unsigned lockmeter_magic; +#endif } rwlock_t; +#ifdef CONFIG_LOCKMETER +#define RW_LOCK_UNLOCKED (rwlock_t) { 0, 0 } +#else #define RW_LOCK_UNLOCKED (rwlock_t) { 0 } +#endif #define rwlock_init(x) do { *(x) = RW_LOCK_UNLOCKED; } while(0) --- linux-2.6.4-rc2/include/asm-parisc/dma-mapping.h 2004-01-09 00:04:32.000000000 -0800 +++ 25/include/asm-parisc/dma-mapping.h 2004-03-07 20:46:59.000000000 -0800 @@ -15,8 +15,10 @@ struct hppa_dma_ops { void (*unmap_single)(struct device *dev, dma_addr_t iova, size_t size, enum dma_data_direction direction); int (*map_sg)(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction direction); void (*unmap_sg)(struct device *dev, struct scatterlist *sg, int nhwents, enum dma_data_direction direction); - void (*dma_sync_single)(struct device *dev, dma_addr_t iova, unsigned long offset, size_t size, enum dma_data_direction direction); - void (*dma_sync_sg)(struct device *dev, struct scatterlist *sg, int nelems, enum dma_data_direction direction); + void (*dma_sync_single_for_cpu)(struct device *dev, dma_addr_t iova, unsigned long offset, size_t size, enum dma_data_direction direction); + void (*dma_sync_single_for_device)(struct device *dev, dma_addr_t iova, unsigned long offset, size_t size, enum dma_data_direction direction); + void (*dma_sync_sg_for_cpu)(struct device *dev, struct scatterlist *sg, int nelems, enum dma_data_direction direction); + void (*dma_sync_sg_for_device)(struct device *dev, struct scatterlist *sg, int nelems, enum dma_data_direction direction); }; /* @@ -116,28 +118,53 @@ dma_unmap_page(struct device *dev, dma_a static inline void -dma_sync_single(struct device *dev, dma_addr_t dma_handle, size_t size, +dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, size_t size, enum dma_data_direction direction) { - if(hppa_dma_ops->dma_sync_single) - hppa_dma_ops->dma_sync_single(dev, dma_handle, 0, size, direction); + if(hppa_dma_ops->dma_sync_single_for_cpu) + hppa_dma_ops->dma_sync_single_for_cpu(dev, dma_handle, 0, size, direction); } static inline void -dma_sync_single_range(struct device *dev, dma_addr_t dma_handle, +dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, size_t size, + enum dma_data_direction direction) +{ + if(hppa_dma_ops->dma_sync_single_for_device) + hppa_dma_ops->dma_sync_single_for_device(dev, dma_handle, 0, size, direction); +} + +static inline void +dma_sync_single_range_for_cpu(struct device *dev, dma_addr_t dma_handle, + unsigned long offset, size_t size, + enum dma_data_direction direction) +{ + if(hppa_dma_ops->dma_sync_single_for_cpu) + hppa_dma_ops->dma_sync_single_for_cpu(dev, dma_handle, offset, size, direction); +} + +static inline void +dma_sync_single_range_for_device(struct device *dev, dma_addr_t dma_handle, unsigned long offset, size_t size, enum dma_data_direction direction) { - if(hppa_dma_ops->dma_sync_single) - hppa_dma_ops->dma_sync_single(dev, dma_handle, offset, size, direction); + if(hppa_dma_ops->dma_sync_single_for_device) + hppa_dma_ops->dma_sync_single_for_device(dev, dma_handle, offset, size, direction); +} + +static inline void +dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems, + enum dma_data_direction direction) +{ + if(hppa_dma_ops->dma_sync_sg_for_cpu) + hppa_dma_ops->dma_sync_sg_for_cpu(dev, sg, nelems, direction); } static inline void -dma_sync_sg(struct device *dev, struct scatterlist *sg, int nelems, +dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems, enum dma_data_direction direction) { - if(hppa_dma_ops->dma_sync_sg) - hppa_dma_ops->dma_sync_sg(dev, sg, nelems, direction); + if(hppa_dma_ops->dma_sync_sg_for_device) + hppa_dma_ops->dma_sync_sg_for_device(dev, sg, nelems, direction); } static inline int @@ -166,14 +193,14 @@ dma_get_cache_alignment(void) static inline int dma_is_consistent(dma_addr_t dma_addr) { - return (hppa_dma_ops->dma_sync_single == NULL); + return (hppa_dma_ops->dma_sync_single_for_cpu == NULL); } static inline void dma_cache_sync(void *vaddr, size_t size, enum dma_data_direction direction) { - if(hppa_dma_ops->dma_sync_single) + if(hppa_dma_ops->dma_sync_single_for_cpu) flush_kernel_dcache_range((unsigned long)vaddr, size); } --- linux-2.6.4-rc2/include/asm-parisc/irq.h 2003-10-08 15:07:10.000000000 -0700 +++ 25/include/asm-parisc/irq.h 2004-03-07 20:48:21.000000000 -0800 @@ -96,4 +96,7 @@ extern unsigned long txn_alloc_addr(int) /* soft power switch support (power.c) */ extern struct tasklet_struct power_tasklet; +struct irqaction; +int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); + #endif /* _ASM_PARISC_IRQ_H */ --- linux-2.6.4-rc2/include/asm-parisc/unistd.h 2004-03-03 23:12:48.000000000 -0800 +++ 25/include/asm-parisc/unistd.h 2004-03-07 20:47:07.000000000 -0800 @@ -909,7 +909,6 @@ int sys_clone(unsigned long clone_flags, int sys_vfork(struct pt_regs *regs); int sys_pipe(int *fildes); long sys_ptrace(long request, pid_t pid, long addr, long data); -asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int turn_on); struct sigaction; asmlinkage long sys_rt_sigaction(int sig, const struct sigaction __user *act, --- linux-2.6.4-rc2/include/asm-ppc64/compat.h 2004-02-03 20:42:38.000000000 -0800 +++ 25/include/asm-ppc64/compat.h 2004-03-07 20:47:06.000000000 -0800 @@ -25,6 +25,7 @@ typedef u16 compat_ipc_pid_t; typedef s32 compat_daddr_t; typedef u32 compat_caddr_t; typedef __kernel_fsid_t compat_fsid_t; +typedef u32 compat_timer_t; typedef s32 compat_int_t; typedef s32 compat_long_t; --- linux-2.6.4-rc2/include/asm-ppc64/irq.h 2004-03-03 23:12:48.000000000 -0800 +++ 25/include/asm-ppc64/irq.h 2004-03-07 20:48:21.000000000 -0800 @@ -48,5 +48,9 @@ static __inline__ int irq_canonicalize(i #define NR_MASK_WORDS ((NR_IRQS + 63) / 64) +struct irqaction; +struct pt_regs; +int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); + #endif /* _ASM_IRQ_H */ #endif /* __KERNEL__ */ --- linux-2.6.4-rc2/include/asm-ppc64/iSeries/vio.h 2004-03-03 23:12:48.000000000 -0800 +++ 25/include/asm-ppc64/iSeries/vio.h 2004-03-07 20:46:46.000000000 -0800 @@ -127,4 +127,8 @@ enum viorc { viorc_openRejected = 0x0301 }; +struct device; + +extern struct device *iSeries_vio_dev; + #endif /* _ISERIES_VIO_H */ --- linux-2.6.4-rc2/include/asm-ppc64/mmu_context.h 2004-03-03 23:12:48.000000000 -0800 +++ 25/include/asm-ppc64/mmu_context.h 2004-03-07 20:46:46.000000000 -0800 @@ -52,7 +52,7 @@ struct mmu_context_queue_t { long head; long tail; long size; - mm_context_t elements[LAST_USER_CONTEXT]; + mm_context_id_t elements[LAST_USER_CONTEXT]; }; extern struct mmu_context_queue_t mmu_context_queue; @@ -83,7 +83,6 @@ init_new_context(struct task_struct *tsk long head; unsigned long flags; /* This does the right thing across a fork (I hope) */ - unsigned long low_hpages = mm->context & CONTEXT_LOW_HPAGES; spin_lock_irqsave(&mmu_context_queue.lock, flags); @@ -93,8 +92,7 @@ init_new_context(struct task_struct *tsk } head = mmu_context_queue.head; - mm->context = mmu_context_queue.elements[head]; - mm->context |= low_hpages; + mm->context.id = mmu_context_queue.elements[head]; head = (head < LAST_USER_CONTEXT-1) ? head+1 : 0; mmu_context_queue.head = head; @@ -132,8 +130,7 @@ destroy_context(struct mm_struct *mm) #endif mmu_context_queue.size++; - mmu_context_queue.elements[index] = - mm->context & ~CONTEXT_LOW_HPAGES; + mmu_context_queue.elements[index] = mm->context.id; spin_unlock_irqrestore(&mmu_context_queue.lock, flags); } @@ -212,8 +209,6 @@ get_vsid( unsigned long context, unsigne { unsigned long ordinal, vsid; - context &= ~CONTEXT_LOW_HPAGES; - ordinal = (((ea >> 28) & 0x1fffff) * LAST_USER_CONTEXT) | context; vsid = (ordinal * VSID_RANDOMIZER) & VSID_MASK; --- linux-2.6.4-rc2/include/asm-ppc64/mmu.h 2004-02-03 20:42:38.000000000 -0800 +++ 25/include/asm-ppc64/mmu.h 2004-03-07 20:46:46.000000000 -0800 @@ -18,15 +18,25 @@ #ifndef __ASSEMBLY__ -/* Default "unsigned long" context */ -typedef unsigned long mm_context_t; +/* Time to allow for more things here */ +typedef unsigned long mm_context_id_t; +typedef struct { + mm_context_id_t id; +#ifdef CONFIG_HUGETLB_PAGE + int low_hpages; +#endif +} mm_context_t; #ifdef CONFIG_HUGETLB_PAGE -#define CONTEXT_LOW_HPAGES (1UL<<63) +#define KERNEL_LOW_HPAGES .low_hpages = 0, #else -#define CONTEXT_LOW_HPAGES 0 +#define KERNEL_LOW_HPAGES #endif +#define KERNEL_CONTEXT(ea) ({ \ + mm_context_t ctx = { .id = REGION_ID(ea), KERNEL_LOW_HPAGES}; \ + ctx; }) + /* * Hardware Segment Lookaside Buffer Entry * This structure has been padded out to two 64b doublewords (actual SLBE's are --- linux-2.6.4-rc2/include/asm-ppc64/page.h 2004-03-03 23:12:48.000000000 -0800 +++ 25/include/asm-ppc64/page.h 2004-03-07 20:46:46.000000000 -0800 @@ -32,6 +32,7 @@ /* For 64-bit processes the hugepage range is 1T-1.5T */ #define TASK_HPAGE_BASE (0x0000010000000000UL) #define TASK_HPAGE_END (0x0000018000000000UL) + /* For 32-bit processes the hugepage range is 2-3G */ #define TASK_HPAGE_BASE_32 (0x80000000UL) #define TASK_HPAGE_END_32 (0xc0000000UL) @@ -39,7 +40,7 @@ #define ARCH_HAS_HUGEPAGE_ONLY_RANGE #define is_hugepage_only_range(addr, len) \ ( ((addr > (TASK_HPAGE_BASE-len)) && (addr < TASK_HPAGE_END)) || \ - ((current->mm->context & CONTEXT_LOW_HPAGES) && \ + (current->mm->context.low_hpages && \ (addr > (TASK_HPAGE_BASE_32-len)) && (addr < TASK_HPAGE_END_32)) ) #define hugetlb_free_pgtables free_pgtables #define HAVE_ARCH_HUGETLB_UNMAPPED_AREA @@ -47,7 +48,7 @@ #define in_hugepage_area(context, addr) \ ((cur_cpu_spec->cpu_features & CPU_FTR_16M_PAGE) && \ ((((addr) >= TASK_HPAGE_BASE) && ((addr) < TASK_HPAGE_END)) || \ - (((context) & CONTEXT_LOW_HPAGES) && \ + ((context).low_hpages && \ (((addr) >= TASK_HPAGE_BASE_32) && ((addr) < TASK_HPAGE_END_32))))) #else /* !CONFIG_HUGETLB_PAGE */ --- linux-2.6.4-rc2/include/asm-ppc64/pci.h 2004-03-03 23:12:48.000000000 -0800 +++ 25/include/asm-ppc64/pci.h 2004-03-07 20:46:59.000000000 -0800 @@ -112,17 +112,33 @@ static inline void pci_unmap_sg(struct p pci_dma_ops.pci_unmap_sg(hwdev, sg, nents, direction); } -static inline void pci_dma_sync_single(struct pci_dev *hwdev, - dma_addr_t dma_handle, - size_t size, int direction) +static inline void pci_dma_sync_single_for_cpu(struct pci_dev *hwdev, + dma_addr_t dma_handle, + size_t size, int direction) { BUG_ON(direction == PCI_DMA_NONE); /* nothing to do */ } -static inline void pci_dma_sync_sg(struct pci_dev *hwdev, - struct scatterlist *sg, - int nelems, int direction) +static inline void pci_dma_sync_single_for_device(struct pci_dev *hwdev, + dma_addr_t dma_handle, + size_t size, int direction) +{ + BUG_ON(direction == PCI_DMA_NONE); + /* nothing to do */ +} + +static inline void pci_dma_sync_sg_for_cpu(struct pci_dev *hwdev, + struct scatterlist *sg, + int nelems, int direction) +{ + BUG_ON(direction == PCI_DMA_NONE); + /* nothing to do */ +} + +static inline void pci_dma_sync_sg_for_device(struct pci_dev *hwdev, + struct scatterlist *sg, + int nelems, int direction) { BUG_ON(direction == PCI_DMA_NONE); /* nothing to do */ --- linux-2.6.4-rc2/include/asm-ppc64/ppc32.h 2004-02-03 20:42:38.000000000 -0800 +++ 25/include/asm-ppc64/ppc32.h 2004-03-07 20:47:06.000000000 -0800 @@ -2,6 +2,7 @@ #define _PPC64_PPC32_H #include +#include #include #include @@ -40,55 +41,6 @@ /* These are here to support 32-bit syscalls on a 64-bit kernel. */ -typedef struct compat_siginfo { - int si_signo; - int si_errno; - int si_code; - - union { - int _pad[SI_PAD_SIZE32]; - - /* kill() */ - struct { - compat_pid_t _pid; /* sender's pid */ - compat_uid_t _uid; /* sender's uid */ - } _kill; - - /* POSIX.1b timers */ - struct { - unsigned int _timer1; - unsigned int _timer2; - } _timer; - - /* POSIX.1b signals */ - struct { - compat_pid_t _pid; /* sender's pid */ - compat_uid_t _uid; /* sender's uid */ - compat_sigval_t _sigval; - } _rt; - - /* SIGCHLD */ - struct { - compat_pid_t _pid; /* which child */ - compat_uid_t _uid; /* sender's uid */ - int _status; /* exit code */ - compat_clock_t _utime; - compat_clock_t _stime; - } _sigchld; - - /* SIGILL, SIGFPE, SIGSEGV, SIGBUS, SIGEMT */ - struct { - unsigned int _addr; /* faulting insn/memory ref. */ - } _sigfault; - - /* SIGPOLL */ - struct { - int _band; /* POLL_IN, POLL_OUT, POLL_MSG */ - int _fd; - } _sigpoll; - } _sifields; -} compat_siginfo_t; - #define __old_sigaction32 old_sigaction32 struct __old_sigaction32 { @@ -141,20 +93,6 @@ struct ucontext32 { struct mcontext32 uc_mcontext; }; -typedef struct compat_sigevent { - compat_sigval_t sigev_value; - int sigev_signo; - int sigev_notify; - union { - int _pad[SIGEV_PAD_SIZE]; - int _tid; - struct { - compat_uptr_t _function; - compat_uptr_t _attribute; - } _sigev_thread; - } _sigev_un; -} compat_sigevent_t; - struct ipc_kludge_32 { unsigned int msgp; int msgtyp; --- linux-2.6.4-rc2/include/asm-ppc/irq.h 2003-09-27 18:57:47.000000000 -0700 +++ 25/include/asm-ppc/irq.h 2004-03-07 20:48:21.000000000 -0800 @@ -211,5 +211,9 @@ extern unsigned long ppc_cached_irq_mask extern unsigned long ppc_lost_interrupts[NR_MASK_WORDS]; extern atomic_t ppc_n_lost_interrupts; +struct irqaction; +struct pt_regs; +int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); + #endif /* _ASM_IRQ_H */ #endif /* __KERNEL__ */ --- linux-2.6.4-rc2/include/asm-ppc/mman.h 2003-09-27 18:57:47.000000000 -0700 +++ 25/include/asm-ppc/mman.h 2004-03-07 20:48:04.000000000 -0800 @@ -23,6 +23,7 @@ #define MAP_EXECUTABLE 0x1000 /* mark it as an executable */ #define MAP_POPULATE 0x8000 /* populate (prefault) pagetables */ #define MAP_NONBLOCK 0x10000 /* do not block on IO */ +#define MAP_INHERIT 0x20000 /* inherit prot of underlying vma */ #define MS_ASYNC 1 /* sync memory asynchronously */ #define MS_INVALIDATE 2 /* invalidate the caches */ --- linux-2.6.4-rc2/include/asm-ppc/pci.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/asm-ppc/pci.h 2004-03-07 20:46:59.000000000 -0800 @@ -91,7 +91,7 @@ extern void pci_free_consistent(struct p * The 32-bit bus address to use is returned. * * Once the device is given the dma address, the device owns this memory - * until either pci_unmap_single or pci_dma_sync_single is performed. + * until either pci_unmap_single or pci_dma_sync_single_for_cpu is performed. */ static inline dma_addr_t pci_map_single(struct pci_dev *hwdev, void *ptr, size_t size, int direction) @@ -190,35 +190,58 @@ static inline void pci_unmap_sg(struct p * If you perform a pci_map_single() but wish to interrogate the * buffer using the cpu, yet do not wish to teardown the PCI dma * mapping, you must call this function before doing so. At the - * next point you give the PCI dma address back to the card, the - * device again owns the buffer. + * next point you give the PCI dma address back to the card, you + * must first perform a pci_dma_sync_for_device, and then the device + * again owns the buffer. */ -static inline void pci_dma_sync_single(struct pci_dev *hwdev, - dma_addr_t dma_handle, - size_t size, int direction) +static inline void pci_dma_sync_single_for_cpu(struct pci_dev *hwdev, + dma_addr_t dma_handle, + size_t size, int direction) { BUG_ON(direction == PCI_DMA_NONE); - consistent_sync(bus_to_virt(dma_handle), size, direction); + consistent_sync_for_cpu(bus_to_virt(dma_handle), size, direction); +} + +static inline void pci_dma_sync_single_for_device(struct pci_dev *hwdev, + dma_addr_t dma_handle, + size_t size, int direction) +{ + BUG_ON(direction == PCI_DMA_NONE); + + consistent_sync_for_device(bus_to_virt(dma_handle), size, direction); } /* Make physical memory consistent for a set of streaming * mode DMA translations after a transfer. * - * The same as pci_dma_sync_single but for a scatter-gather list, + * The same as pci_dma_sync_single_for_* but for a scatter-gather list, * same rules and usage. */ -static inline void pci_dma_sync_sg(struct pci_dev *hwdev, - struct scatterlist *sg, - int nelems, int direction) +static inline void pci_dma_sync_sg_for_cpu(struct pci_dev *hwdev, + struct scatterlist *sg, + int nelems, int direction) { int i; BUG_ON(direction == PCI_DMA_NONE); for (i = 0; i < nelems; i++, sg++) - consistent_sync_page(sg->page, sg->offset, - sg->length, direction); + consistent_sync_page_for_cpu(sg->page, sg->offset, + sg->length, direction); +} + +static inline void pci_dma_sync_sg_for_device(struct pci_dev *hwdev, + struct scatterlist *sg, + int nelems, int direction) +{ + int i; + + BUG_ON(direction == PCI_DMA_NONE); + + for (i = 0; i < nelems; i++, sg++) + consistent_sync_page_for_device(sg->page, sg->offset, + sg->length, direction); } /* Return whether the given PCI device DMA address mask can @@ -237,26 +260,32 @@ static inline int pci_dma_supported(stru */ #define pci_dac_dma_supported(pci_dev, mask) (0) -static __inline__ dma64_addr_t +static inline dma64_addr_t pci_dac_page_to_dma(struct pci_dev *pdev, struct page *page, unsigned long offset, int direction) { return (dma64_addr_t) page_to_bus(page) + offset; } -static __inline__ struct page * +static inline struct page * pci_dac_dma_to_page(struct pci_dev *pdev, dma64_addr_t dma_addr) { return mem_map + (unsigned long)(dma_addr >> PAGE_SHIFT); } -static __inline__ unsigned long +static inline unsigned long pci_dac_dma_to_offset(struct pci_dev *pdev, dma64_addr_t dma_addr) { return (dma_addr & ~PAGE_MASK); } -static __inline__ void -pci_dac_dma_sync_single(struct pci_dev *pdev, dma64_addr_t dma_addr, size_t len, int direction) +static inline void +pci_dac_dma_sync_single_for_cpu(struct pci_dev *pdev, dma64_addr_t dma_addr, size_t len, int direction) +{ + /* Nothing to do. */ +} + +static inline void +pci_dac_dma_sync_single_for_device(struct pci_dev *pdev, dma64_addr_t dma_addr, size_t len, int direction) { /* Nothing to do. */ } --- linux-2.6.4-rc2/include/asm-ppc/pgtable.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/asm-ppc/pgtable.h 2004-03-07 20:48:04.000000000 -0800 @@ -264,8 +264,8 @@ extern unsigned long ioremap_bot, iorema /* Definitions for 60x, 740/750, etc. */ #define _PAGE_PRESENT 0x001 /* software: pte contains a translation */ #define _PAGE_HASHPTE 0x002 /* hash_page has made an HPTE for this pte */ -#define _PAGE_FILE 0x004 /* when !present: nonlinear file mapping */ #define _PAGE_USER 0x004 /* usermode access allowed */ +#define _PAGE_FILE 0x008 /* when !present: nonlinear file mapping */ #define _PAGE_GUARDED 0x008 /* G: prohibit speculative access */ #define _PAGE_COHERENT 0x010 /* M: enforce memory coherence (SMP systems) */ #define _PAGE_NO_CACHE 0x020 /* I: cache inhibit */ @@ -628,9 +628,16 @@ extern void add_hash_page(unsigned conte #define __swp_entry_to_pte(x) ((pte_t) { (x).val << 3 }) /* Encode and decode a nonlinear file mapping entry */ -#define PTE_FILE_MAX_BITS 29 -#define pte_to_pgoff(pte) (pte_val(pte) >> 3) -#define pgoff_to_pte(off) ((pte_t) { ((off) << 3) | _PAGE_FILE }) +#define PTE_FILE_MAX_BITS 27 +#define pte_to_pgoff(pte) (((pte_val(pte) & ~0x7ff) >> 5) \ + | ((pte_val(pte) & 0x3f0) >> 4)) +#define pte_to_pgprot(pte) \ +__pgprot((pte_val(pte) & (_PAGE_USER|_PAGE_RW|_PAGE_PRESENT)) | _PAGE_ACCESSED) + +#define pgoff_prot_to_pte(off, prot) \ + ((pte_t) { (((off) << 5) & ~0x7ff) | (((off) << 4) & 0x3f0) \ + | (pgprot_val(prot) & (_PAGE_USER|_PAGE_RW)) \ + | _PAGE_FILE }) /* CONFIG_APUS */ /* For virtual address to physical address conversion */ --- linux-2.6.4-rc2/include/asm-s390/compat.h 2004-03-03 23:12:48.000000000 -0800 +++ 25/include/asm-s390/compat.h 2004-03-07 20:47:07.000000000 -0800 @@ -15,6 +15,8 @@ typedef s32 compat_clock_t; typedef s32 compat_pid_t; typedef u16 compat_uid_t; typedef u16 compat_gid_t; +typedef u32 compat_uid32_t; +typedef u32 compat_gid32_t; typedef u16 compat_mode_t; typedef u32 compat_ino_t; typedef u16 compat_dev_t; @@ -25,6 +27,7 @@ typedef u16 compat_ipc_pid_t; typedef s32 compat_daddr_t; typedef u32 compat_caddr_t; typedef __kernel_fsid_t compat_fsid_t; +typedef s32 compat_key_t; typedef s32 compat_timer_t; typedef s32 compat_int_t; @@ -135,4 +138,61 @@ static inline void *compat_alloc_user_sp return (void *) (stack - len); } +struct compat_ipc64_perm { + compat_key_t key; + compat_uid32_t uid; + compat_gid32_t gid; + compat_uid32_t cuid; + compat_gid32_t cgid; + compat_mode_t mode; + unsigned short __pad1; + unsigned short seq; + unsigned short __pad2; + unsigned int __unused1; + unsigned int __unused2; +}; + +struct compat_semid64_ds { + struct compat_ipc64_perm sem_perm; + compat_time_t sem_otime; + compat_ulong_t __pad1; + compat_time_t sem_ctime; + compat_ulong_t __pad2; + compat_ulong_t sem_nsems; + compat_ulong_t __unused1; + compat_ulong_t __unused2; +}; + +struct compat_msqid64_ds { + struct compat_ipc64_perm msg_perm; + compat_time_t msg_stime; + compat_ulong_t __pad1; + compat_time_t msg_rtime; + compat_ulong_t __pad2; + compat_time_t msg_ctime; + compat_ulong_t __pad3; + compat_ulong_t msg_cbytes; + compat_ulong_t msg_qnum; + compat_ulong_t msg_qbytes; + compat_pid_t msg_lspid; + compat_pid_t msg_lrpid; + compat_ulong_t __unused1; + compat_ulong_t __unused2; +}; + +struct compat_shmid64_ds { + struct compat_ipc64_perm shm_perm; + compat_size_t shm_segsz; + compat_time_t shm_atime; + compat_ulong_t __pad1; + compat_time_t shm_dtime; + compat_ulong_t __pad2; + compat_time_t shm_ctime; + compat_ulong_t __pad3; + compat_pid_t shm_cpid; + compat_pid_t shm_lpid; + compat_ulong_t shm_nattch; + compat_ulong_t __unused1; + compat_ulong_t __unused2; +}; #endif /* _ASM_S390X_COMPAT_H */ --- linux-2.6.4-rc2/include/asm-s390/irq.h 2003-07-27 12:14:40.000000000 -0700 +++ 25/include/asm-s390/irq.h 2004-03-07 20:48:21.000000000 -0800 @@ -21,6 +21,10 @@ enum interruption_class { #define touch_nmi_watchdog() do { } while(0) +struct irqaction; +struct pt_regs; +int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); + #endif /* __KERNEL__ */ #endif --- linux-2.6.4-rc2/include/asm-s390/unistd.h 2004-03-03 23:12:48.000000000 -0800 +++ 25/include/asm-s390/unistd.h 2004-03-07 20:47:07.000000000 -0800 @@ -553,7 +553,6 @@ asmlinkage int sys_vfork(struct pt_regs #endif /* CONFIG_ARCH_S390X */ asmlinkage __SYS_RETTYPE sys_pipe(unsigned long *fildes); asmlinkage int sys_ptrace(long request, long pid, long addr, long data); -asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int on); struct sigaction; asmlinkage long sys_rt_sigaction(int sig, const struct sigaction __user *act, --- linux-2.6.4-rc2/include/asm-sh/irq.h 2004-02-03 20:42:38.000000000 -0800 +++ 25/include/asm-sh/irq.h 2004-03-07 20:48:21.000000000 -0800 @@ -324,4 +324,8 @@ static inline int generic_irq_demux(int #define irq_canonicalize(irq) (irq) #define irq_demux(irq) __irq_demux(sh_mv.mv_irq_demux(irq)) +struct irqaction; +struct pt_regs; +int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); + #endif /* __ASM_SH_IRQ_H */ --- linux-2.6.4-rc2/include/asm-sh/pci.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/asm-sh/pci.h 2004-03-07 20:46:59.000000000 -0800 @@ -84,7 +84,7 @@ extern void pci_free_consistent(struct p * The 32-bit bus address to use is returned. * * Once the device is given the dma address, the device owns this memory - * until either pci_unmap_single or pci_dma_sync_single is performed. + * until either pci_unmap_single or pci_dma_sync_single_for_cpu is performed. */ static inline dma_addr_t pci_map_single(struct pci_dev *hwdev, void *ptr, size_t size, int direction) @@ -184,12 +184,21 @@ static inline void pci_unmap_sg(struct p * If you perform a pci_map_single() but wish to interrogate the * buffer using the cpu, yet do not wish to teardown the PCI dma * mapping, you must call this function before doing so. At the - * next point you give the PCI dma address back to the card, the - * device again owns the buffer. - */ -static inline void pci_dma_sync_single(struct pci_dev *hwdev, - dma_addr_t dma_handle, - size_t size, int direction) + * next point you give the PCI dma address back to the card, you + * must first perform a pci_dma_sync_for_device, and then the device + * again owns the buffer. + */ +static inline void pci_dma_sync_single_for_cpu(struct pci_dev *hwdev, + dma_addr_t dma_handle, + size_t size, int direction) +{ + if (direction == PCI_DMA_NONE) + BUG(); +} + +static inline void pci_dma_sync_single_for_device(struct pci_dev *hwdev, + dma_addr_t dma_handle, + size_t size, int direction) { if (direction == PCI_DMA_NONE) BUG(); @@ -203,12 +212,20 @@ static inline void pci_dma_sync_single(s /* Make physical memory consistent for a set of streaming * mode DMA translations after a transfer. * - * The same as pci_dma_sync_single but for a scatter-gather list, + * The same as pci_dma_sync_single_* but for a scatter-gather list, * same rules and usage. */ -static inline void pci_dma_sync_sg(struct pci_dev *hwdev, - struct scatterlist *sg, - int nelems, int direction) +static inline void pci_dma_sync_sg_for_cpu(struct pci_dev *hwdev, + struct scatterlist *sg, + int nelems, int direction) +{ + if (direction == PCI_DMA_NONE) + BUG(); +} + +static inline void pci_dma_sync_sg_for_device(struct pci_dev *hwdev, + struct scatterlist *sg, + int nelems, int direction) { if (direction == PCI_DMA_NONE) BUG(); --- linux-2.6.4-rc2/include/asm-sparc64/irq.h 2003-08-22 19:23:42.000000000 -0700 +++ 25/include/asm-sparc64/irq.h 2004-03-07 20:48:21.000000000 -0800 @@ -150,4 +150,8 @@ static __inline__ unsigned long get_soft return retval; } +struct irqaction; +struct pt_regs; +int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); + #endif --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/include/asm-sparc64/lockmeter.h 2004-03-07 20:48:17.000000000 -0800 @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2000 Anton Blanchard (anton@linuxcare.com) + * Copyright (C) 2003 David S. Miller (davem@redhat.com) + */ + +#ifndef _SPARC64_LOCKMETER_H +#define _SPARC64_LOCKMETER_H + +#include +#include +#include +#include + +/* Actually, this is not the CPU frequency by the system tick + * frequency which is good enough for lock metering. + */ +#define CPU_CYCLE_FREQUENCY (timer_tick_offset * HZ) +#define THIS_CPU_NUMBER smp_processor_id() + +#define PUT_INDEX(lock_ptr,indexv) (lock_ptr)->index = (indexv) +#define GET_INDEX(lock_ptr) (lock_ptr)->index + +#define PUT_RWINDEX(rwlock_ptr,indexv) (rwlock_ptr)->index = (indexv) +#define GET_RWINDEX(rwlock_ptr) (rwlock_ptr)->index +#define PUT_RW_CPU(rwlock_ptr,cpuv) (rwlock_ptr)->cpu = (cpuv) +#define GET_RW_CPU(rwlock_ptr) (rwlock_ptr)->cpu + +#define RWLOCK_READERS(rwlock_ptr) rwlock_readers(rwlock_ptr) + +extern inline int rwlock_readers(rwlock_t *rwlock_ptr) +{ + signed int tmp = rwlock_ptr->lock; + + if (tmp > 0) + return tmp; + else + return 0; +} + +#define RWLOCK_IS_WRITE_LOCKED(rwlock_ptr) ((signed int)((rwlock_ptr)->lock) < 0) +#define RWLOCK_IS_READ_LOCKED(rwlock_ptr) ((signed int)((rwlock_ptr)->lock) > 0) + +#define get_cycles64() get_cycles() + +#endif /* _SPARC64_LOCKMETER_H */ --- linux-2.6.4-rc2/include/asm-sparc64/pci.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/asm-sparc64/pci.h 2004-03-07 20:46:59.000000000 -0800 @@ -60,7 +60,7 @@ extern void pci_free_consistent(struct p * The 32-bit bus address to use is returned. * * Once the device is given the dma address, the device owns this memory - * until either pci_unmap_single or pci_dma_sync_single is performed. + * until either pci_unmap_single or pci_dma_sync_single_for_cpu is performed. */ extern dma_addr_t pci_map_single(struct pci_dev *hwdev, void *ptr, size_t size, int direction); @@ -123,19 +123,36 @@ extern void pci_unmap_sg(struct pci_dev * If you perform a pci_map_single() but wish to interrogate the * buffer using the cpu, yet do not wish to teardown the PCI dma * mapping, you must call this function before doing so. At the - * next point you give the PCI dma address back to the card, the + * next point you give the PCI dma address back to the card, you + * must first perform a pci_dma_sync_for_device, and then the * device again owns the buffer. */ -extern void pci_dma_sync_single(struct pci_dev *hwdev, dma_addr_t dma_handle, - size_t size, int direction); +extern void pci_dma_sync_single_for_cpu(struct pci_dev *hwdev, dma_addr_t dma_handle, + size_t size, int direction); + +static inline void +pci_dma_sync_single_for_device(struct pci_dev *hwdev, dma_addr_t dma_handle, + size_t size, int direction) +{ + /* No flushing needed to sync cpu writes to the device. */ + BUG_ON(direction == PCI_DMA_NONE); +} /* Make physical memory consistent for a set of streaming * mode DMA translations after a transfer. * - * The same as pci_dma_sync_single but for a scatter-gather list, + * The same as pci_dma_sync_single_* but for a scatter-gather list, * same rules and usage. */ -extern void pci_dma_sync_sg(struct pci_dev *hwdev, struct scatterlist *sg, int nelems, int direction); +extern void pci_dma_sync_sg_for_cpu(struct pci_dev *hwdev, struct scatterlist *sg, int nelems, int direction); + +static inline void +pci_dma_sync_sg_for_device(struct pci_dev *hwdev, struct scatterlist *sg, + int nelems, int direction) +{ + /* No flushing needed to sync cpu writes to the device. */ + BUG_ON(direction == PCI_DMA_NONE); +} /* Return whether the given PCI device DMA address mask can * be supported properly. For example, if your device can @@ -159,14 +176,14 @@ extern int pci_dma_supported(struct pci_ #define pci_dac_dma_supported(pci_dev, mask) \ ((((mask) & PCI64_REQUIRED_MASK) == PCI64_REQUIRED_MASK) ? 1 : 0) -static __inline__ dma64_addr_t +static inline dma64_addr_t pci_dac_page_to_dma(struct pci_dev *pdev, struct page *page, unsigned long offset, int direction) { return (PCI64_ADDR_BASE + __pa(page_address(page)) + offset); } -static __inline__ struct page * +static inline struct page * pci_dac_dma_to_page(struct pci_dev *pdev, dma64_addr_t dma_addr) { unsigned long paddr = (dma_addr & PAGE_MASK) - PCI64_ADDR_BASE; @@ -174,14 +191,22 @@ pci_dac_dma_to_page(struct pci_dev *pdev return virt_to_page(__va(paddr)); } -static __inline__ unsigned long +static inline unsigned long pci_dac_dma_to_offset(struct pci_dev *pdev, dma64_addr_t dma_addr) { return (dma_addr & ~PAGE_MASK); } -static __inline__ void -pci_dac_dma_sync_single(struct pci_dev *pdev, dma64_addr_t dma_addr, size_t len, int direction) +static inline void +pci_dac_dma_sync_single_for_cpu(struct pci_dev *pdev, dma64_addr_t dma_addr, size_t len, int direction) +{ + /* DAC cycle addressing does not make use of the + * PCI controller's streaming cache, so nothing to do. + */ +} + +static inline void +pci_dac_dma_sync_single_for_device(struct pci_dev *pdev, dma64_addr_t dma_addr, size_t len, int direction) { /* DAC cycle addressing does not make use of the * PCI controller's streaming cache, so nothing to do. --- linux-2.6.4-rc2/include/asm-sparc64/sbus.h 2003-06-14 12:17:59.000000000 -0700 +++ 25/include/asm-sparc64/sbus.h 2004-03-07 20:46:59.000000000 -0800 @@ -111,7 +111,11 @@ extern int sbus_map_sg(struct sbus_dev * extern void sbus_unmap_sg(struct sbus_dev *, struct scatterlist *, int, int); /* Finally, allow explicit synchronization of streamable mappings. */ -extern void sbus_dma_sync_single(struct sbus_dev *, dma_addr_t, size_t, int); -extern void sbus_dma_sync_sg(struct sbus_dev *, struct scatterlist *, int, int); +extern void sbus_dma_sync_single_for_cpu(struct sbus_dev *, dma_addr_t, size_t, int); +#define sbus_dma_sync_single sbus_dma_sync_single_for_cpu +extern void sbus_dma_sync_single_for_device(struct sbus_dev *, dma_addr_t, size_t, int); +extern void sbus_dma_sync_sg_for_cpu(struct sbus_dev *, struct scatterlist *, int, int); +#define sbus_dma_sync_sg sbus_dma_sync_sg_for_cpu +extern void sbus_dma_sync_sg_for_device(struct sbus_dev *, struct scatterlist *, int, int); #endif /* !(_SPARC64_SBUS_H) */ --- linux-2.6.4-rc2/include/asm-sparc64/smp.h 2004-02-03 20:42:38.000000000 -0800 +++ 25/include/asm-sparc64/smp.h 2004-03-07 20:47:24.000000000 -0800 @@ -35,11 +35,6 @@ extern unsigned char boot_cpu_id; extern cpumask_t phys_cpu_present_map; #define cpu_possible_map phys_cpu_present_map -#define cpu_online(cpu) cpu_isset(cpu, cpu_online_map) - -extern atomic_t sparc64_num_cpus_possible; -#define num_possible_cpus() (atomic_read(&sparc64_num_cpus_possible)) - /* * General functions that each host system must provide. */ @@ -75,10 +70,6 @@ static __inline__ int hard_smp_processor #endif /* !(__ASSEMBLY__) */ -#else - -#define num_possible_cpus() (1) - #endif /* !(CONFIG_SMP) */ #define NO_PROC_ID 0xFF --- linux-2.6.4-rc2/include/asm-sparc64/spinlock.h 2004-03-03 23:12:48.000000000 -0800 +++ 25/include/asm-sparc64/spinlock.h 2004-03-07 20:48:17.000000000 -0800 @@ -31,15 +31,23 @@ #ifndef CONFIG_DEBUG_SPINLOCK -typedef unsigned char spinlock_t; -#define SPIN_LOCK_UNLOCKED 0 +typedef struct { + unsigned char lock; + unsigned int index; +} spinlock_t; -#define spin_lock_init(lock) (*((unsigned char *)(lock)) = 0) -#define spin_is_locked(lock) (*((volatile unsigned char *)(lock)) != 0) +#ifdef CONFIG_LOCKMETER +#define SPIN_LOCK_UNLOCKED (spinlock_t) {0, 0} +#else +#define SPIN_LOCK_UNLOCKED (spinlock_t) { 0 } +#endif -#define spin_unlock_wait(lock) \ +#define spin_lock_init(__lock) do { *(__lock) = SPIN_LOCK_UNLOCKED; } while(0) +#define spin_is_locked(__lock) (*((volatile unsigned char *)(&((__lock)->lock))) != 0) + +#define spin_unlock_wait(__lock) \ do { membar("#LoadLoad"); \ -} while(*((volatile unsigned char *)lock)) +} while(*((volatile unsigned char *)(&(((spinlock_t *)__lock)->lock)))) static __inline__ void _raw_spin_lock(spinlock_t *lock) { @@ -110,17 +118,31 @@ extern int _spin_trylock (spinlock_t *lo #ifndef CONFIG_DEBUG_SPINLOCK -typedef unsigned int rwlock_t; -#define RW_LOCK_UNLOCKED 0 -#define rwlock_init(lp) do { *(lp) = RW_LOCK_UNLOCKED; } while(0) -#define rwlock_is_locked(x) (*(x) != RW_LOCK_UNLOCKED) +#ifdef CONFIG_LOCKMETER +typedef struct { + unsigned int lock; + unsigned int index; + unsigned int cpu; +} rwlock_t; +#define RW_LOCK_UNLOCKED (rwlock_t) { 0, 0, 0xff } +#else +typedef struct { + unsigned int lock; +} rwlock_t; +#define RW_LOCK_UNLOCKED (rwlock_t) { 0 } +#endif + +#define rwlock_init(lp) do { *(lp) = RW_LOCK_UNLOCKED; } while(0) +#define rwlock_is_locked(x) ((x)->lock != 0) +extern int __read_trylock(rwlock_t *); extern void __read_lock(rwlock_t *); extern void __read_unlock(rwlock_t *); extern void __write_lock(rwlock_t *); extern void __write_unlock(rwlock_t *); extern int __write_trylock(rwlock_t *); +#define _raw_read_trylock(p) __read_trylock(p) #define _raw_read_lock(p) __read_lock(p) #define _raw_read_unlock(p) __read_unlock(p) #define _raw_write_lock(p) __write_lock(p) --- linux-2.6.4-rc2/include/asm-sparc64/unistd.h 2004-03-03 23:12:48.000000000 -0800 +++ 25/include/asm-sparc64/unistd.h 2004-03-07 20:47:07.000000000 -0800 @@ -447,7 +447,6 @@ asmlinkage unsigned long sys_mmap( unsigned long addr, unsigned long len, unsigned long prot, unsigned long flags, unsigned long fd, unsigned long off); -asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int on); struct sigaction; asmlinkage long sys_rt_sigaction(int sig, const struct sigaction __user *act, --- linux-2.6.4-rc2/include/asm-sparc/irq.h 2003-10-17 15:58:04.000000000 -0700 +++ 25/include/asm-sparc/irq.h 2004-03-07 20:48:21.000000000 -0800 @@ -184,4 +184,8 @@ extern struct sun4m_intregs *sun4m_inter #define SUN4M_INT_SBUS(x) (1 << (x+7)) #define SUN4M_INT_VME(x) (1 << (x)) +struct irqaction; +struct pt_regs; +int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); + #endif --- linux-2.6.4-rc2/include/asm-sparc/pci.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/asm-sparc/pci.h 2004-03-07 20:46:59.000000000 -0800 @@ -52,7 +52,7 @@ extern void pci_free_consistent(struct p * The 32-bit bus address to use is returned. * * Once the device is given the dma address, the device owns this memory - * until either pci_unmap_single or pci_dma_sync_single is performed. + * until either pci_unmap_single or pci_dma_sync_single_for_cpu is performed. */ extern dma_addr_t pci_map_single(struct pci_dev *hwdev, void *ptr, size_t size, int direction); @@ -116,18 +116,21 @@ extern void pci_unmap_sg(struct pci_dev * If you perform a pci_map_single() but wish to interrogate the * buffer using the cpu, yet do not wish to teardown the PCI dma * mapping, you must call this function before doing so. At the - * next point you give the PCI dma address back to the card, the - * device again owns the buffer. + * next point you give the PCI dma address back to the card, you + * must first perform a pci_dma_sync_for_device, and then the device + * again owns the buffer. */ -extern void pci_dma_sync_single(struct pci_dev *hwdev, dma_addr_t dma_handle, size_t size, int direction); +extern void pci_dma_sync_single_for_cpu(struct pci_dev *hwdev, dma_addr_t dma_handle, size_t size, int direction); +extern void pci_dma_sync_single_for_device(struct pci_dev *hwdev, dma_addr_t dma_handle, size_t size, int direction); /* Make physical memory consistent for a set of streaming * mode DMA translations after a transfer. * - * The same as pci_dma_sync_single but for a scatter-gather list, + * The same as pci_dma_sync_single_* but for a scatter-gather list, * same rules and usage. */ -extern void pci_dma_sync_sg(struct pci_dev *hwdev, struct scatterlist *sg, int nelems, int direction); +extern void pci_dma_sync_sg_for_cpu(struct pci_dev *hwdev, struct scatterlist *sg, int nelems, int direction); +extern void pci_dma_sync_sg_for_device(struct pci_dev *hwdev, struct scatterlist *sg, int nelems, int direction); /* Return whether the given PCI device DMA address mask can * be supported properly. For example, if your device can --- linux-2.6.4-rc2/include/asm-sparc/sbus.h 2003-06-14 12:17:59.000000000 -0700 +++ 25/include/asm-sparc/sbus.h 2004-03-07 20:46:59.000000000 -0800 @@ -118,8 +118,12 @@ extern int sbus_map_sg(struct sbus_dev * extern void sbus_unmap_sg(struct sbus_dev *, struct scatterlist *, int, int); /* Finally, allow explicit synchronization of streamable mappings. */ -extern void sbus_dma_sync_single(struct sbus_dev *, dma_addr_t, size_t, int); -extern void sbus_dma_sync_sg(struct sbus_dev *, struct scatterlist *, int, int); +extern void sbus_dma_sync_single_for_cpu(struct sbus_dev *, dma_addr_t, size_t, int); +#define sbus_dma_sync_single sbus_dma_sync_single_for_cpu +extern void sbus_dma_sync_single_for_device(struct sbus_dev *, dma_addr_t, size_t, int); +extern void sbus_dma_sync_sg_for_cpu(struct sbus_dev *, struct scatterlist *, int, int); +#define sbus_dma_sync_sg sbus_dma_sync_sg_for_cpu +extern void sbus_dma_sync_sg_for_device(struct sbus_dev *, struct scatterlist *, int, int); /* Eric Brower (ebrower@usa.net) * Translate SBus interrupt levels to ino values-- --- linux-2.6.4-rc2/include/asm-sparc/system.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/asm-sparc/system.h 2004-03-07 20:46:46.000000000 -0800 @@ -75,7 +75,7 @@ extern void fpsave(unsigned long *fpregs #ifdef CONFIG_SMP #define SWITCH_ENTER(prv) \ do { \ - if (test_tsk_thread_flag(prv, TIF_USEDFPU) { \ + if (test_tsk_thread_flag(prv, TIF_USEDFPU)) { \ put_psr(get_psr() | PSR_EF); \ fpsave(&(prv)->thread.float_regs[0], &(prv)->thread.fsr, \ &(prv)->thread.fpqueue[0], &(prv)->thread.fpqdepth); \ --- linux-2.6.4-rc2/include/asm-sparc/thread_info.h 2004-03-03 23:12:48.000000000 -0800 +++ 25/include/asm-sparc/thread_info.h 2004-03-07 20:46:46.000000000 -0800 @@ -79,9 +79,9 @@ register struct thread_info *current_thr /* * thread information allocation */ -#ifdef CONFIG_SUN4 +#if PAGE_SHIFT == 13 #define THREAD_INFO_ORDER 0 -#else +#else /* PAGE_SHIFT */ #define THREAD_INFO_ORDER 1 #endif --- linux-2.6.4-rc2/include/asm-sparc/unistd.h 2004-03-03 23:12:48.000000000 -0800 +++ 25/include/asm-sparc/unistd.h 2004-03-07 20:46:46.000000000 -0800 @@ -461,7 +461,6 @@ asmlinkage unsigned long sys_mmap2( unsigned long addr, unsigned long len, unsigned long prot, unsigned long flags, unsigned long fd, unsigned long pgoff); -asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on); struct sigaction; asmlinkage long sys_rt_sigaction(int sig, const struct sigaction __user *act, --- linux-2.6.4-rc2/include/asm-um/irq.h 2003-06-14 12:18:49.000000000 -0700 +++ 25/include/asm-um/irq.h 2004-03-07 20:48:21.000000000 -0800 @@ -32,4 +32,9 @@ extern int um_request_irq(unsigned int i void (*handler)(int, void *, struct pt_regs *), unsigned long irqflags, const char * devname, void *dev_id); + +struct irqaction; +struct pt_regs; +int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); + #endif --- linux-2.6.4-rc2/include/asm-um/linkage.h 2003-06-14 12:18:33.000000000 -0700 +++ 25/include/asm-um/linkage.h 2004-03-07 20:46:46.000000000 -0800 @@ -2,5 +2,6 @@ #define __ASM_LINKAGE_H #define FASTCALL(x) x __attribute__((regparm(3))) +#define fastcall __attribute__((regparm(3))) #endif --- linux-2.6.4-rc2/include/asm-v850/irq.h 2003-06-14 12:18:34.000000000 -0700 +++ 25/include/asm-v850/irq.h 2004-03-07 20:48:21.000000000 -0800 @@ -65,4 +65,8 @@ extern void disable_irq_nosync (unsigned #endif /* !__ASSEMBLY__ */ +struct irqaction; +struct pt_regs; +int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); + #endif /* __V850_IRQ_H__ */ --- linux-2.6.4-rc2/include/asm-v850/pci.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/asm-v850/pci.h 2004-03-07 20:46:59.000000000 -0800 @@ -27,7 +27,7 @@ extern void pcibios_set_master (struct p /* `Grant' to PDEV the memory block at CPU_ADDR, for doing DMA. The 32-bit PCI bus mastering address to use is returned. the device owns - this memory until either pci_unmap_single or pci_dma_sync_single is + this memory until either pci_unmap_single or pci_dma_sync_single_for_cpu is performed. */ extern dma_addr_t pci_map_single (struct pci_dev *pdev, void *cpu_addr, size_t size, int dir); @@ -44,10 +44,15 @@ pci_unmap_single (struct pci_dev *pdev, If you perform a pci_map_single() but wish to interrogate the buffer using the cpu, yet do not wish to teardown the PCI dma mapping, you must call this function before doing so. At the next - point you give the PCI dma address back to the card, the device - again owns the buffer. */ + point you give the PCI dma address back to the card, you must first + perform a pci_dma_sync_for_device, and then the device again owns + the buffer. */ extern void -pci_dma_sync_single (struct pci_dev *dev, dma_addr_t dma_addr, size_t size, +pci_dma_sync_single_for_cpu (struct pci_dev *dev, dma_addr_t dma_addr, size_t size, + int dir); + +extern void +pci_dma_sync_single_for_device (struct pci_dev *dev, dma_addr_t dma_addr, size_t size, int dir); --- linux-2.6.4-rc2/include/asm-x86_64/acpi.h 2004-02-03 20:42:38.000000000 -0800 +++ 25/include/asm-x86_64/acpi.h 2004-03-07 20:46:49.000000000 -0800 @@ -60,7 +60,7 @@ __acpi_acquire_global_lock (unsigned int do { old = *lock; new = (((old & ~0x3) + 2) + ((old >> 1) & 0x1)); - val = cmpxchg4_locked(lock, new, old); + val = cmpxchg(lock, old, new); } while (unlikely (val != old)); return (new < 3) ? -1 : 0; } @@ -72,7 +72,7 @@ __acpi_release_global_lock (unsigned int do { old = *lock; new = old & ~0x3; - val = cmpxchg4_locked(lock, new, old); + val = cmpxchg(lock, old, new); } while (unlikely (val != old)); return old & 0x1; } @@ -104,6 +104,7 @@ __acpi_release_global_lock (unsigned int extern int acpi_lapic; extern int acpi_ioapic; extern int acpi_noirq; +extern int acpi_strict; /* Fixmap pages to reserve for ACPI boot-time tables (see fixmap.h) */ #define FIX_ACPI_PAGES 4 --- linux-2.6.4-rc2/include/asm-x86_64/compat.h 2004-03-03 23:12:48.000000000 -0800 +++ 25/include/asm-x86_64/compat.h 2004-03-07 20:47:07.000000000 -0800 @@ -29,6 +29,7 @@ typedef s32 compat_daddr_t; typedef u32 compat_caddr_t; typedef __kernel_fsid_t compat_fsid_t; typedef u32 compat_timer_t; +typedef s32 compat_key_t; typedef s32 compat_int_t; typedef s32 compat_long_t; @@ -119,6 +120,64 @@ typedef u32 compat_sigset_ #define COMPAT_OFF_T_MAX 0x7fffffff #define COMPAT_LOFF_T_MAX 0x7fffffffffffffff +struct compat_ipc64_perm { + compat_key_t key; + compat_uid32_t uid; + compat_gid32_t gid; + compat_uid32_t cuid; + compat_gid32_t cgid; + unsigned short mode; + unsigned short __pad1; + unsigned short seq; + unsigned short __pad2; + compat_ulong_t unused1; + compat_ulong_t unused2; +}; + +struct compat_semid64_ds { + struct compat_ipc64_perm sem_perm; + compat_time_t sem_otime; + compat_ulong_t __unused1; + compat_time_t sem_ctime; + compat_ulong_t __unused2; + compat_ulong_t sem_nsems; + compat_ulong_t __unused3; + compat_ulong_t __unused4; +}; + +struct compat_msqid64_ds { + struct compat_ipc64_perm msg_perm; + compat_time_t msg_stime; + compat_ulong_t __unused1; + compat_time_t msg_rtime; + compat_ulong_t __unused2; + compat_time_t msg_ctime; + compat_ulong_t __unused3; + compat_ulong_t msg_cbytes; + compat_ulong_t msg_qnum; + compat_ulong_t msg_qbytes; + compat_pid_t msg_lspid; + compat_pid_t msg_lrpid; + compat_ulong_t __unused4; + compat_ulong_t __unused5; +}; + +struct compat_shmid64_ds { + struct compat_ipc64_perm shm_perm; + compat_size_t shm_segsz; + compat_time_t shm_atime; + compat_ulong_t __unused1; + compat_time_t shm_dtime; + compat_ulong_t __unused2; + compat_time_t shm_ctime; + compat_ulong_t __unused3; + compat_pid_t shm_cpid; + compat_pid_t shm_lpid; + compat_ulong_t shm_nattch; + compat_ulong_t __unused4; + compat_ulong_t __unused5; +}; + /* * A pointer passed in from user mode. This should not * be used for syscall parameters, just declare them --- linux-2.6.4-rc2/include/asm-x86_64/ia32_unistd.h 2003-09-08 13:58:59.000000000 -0700 +++ 25/include/asm-x86_64/ia32_unistd.h 2004-03-07 20:48:04.000000000 -0800 @@ -262,7 +262,7 @@ #define __NR_ia32_sys_epoll_create 254 #define __NR_ia32_sys_epoll_ctl 255 #define __NR_ia32_sys_epoll_wait 256 -#define __NR_ia32_remap_file_pages 257 +#define __NR_ia32_old_remap_file_pages 257 #define __NR_ia32_set_tid_address 258 #define __NR_ia32_timer_create 259 #define __NR_ia32_timer_settime (__NR_ia32_timer_create+1) --- linux-2.6.4-rc2/include/asm-x86_64/io.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/asm-x86_64/io.h 2004-03-07 20:47:45.000000000 -0800 @@ -109,17 +109,6 @@ __OUTS(l) #include -/* - * Temporary debugging check to catch old code using - * unmapped ISA addresses. Will be removed in 2.4. - */ -#ifdef CONFIG_IO_DEBUG - extern void *__io_virt_debug(unsigned long x, const char *file, int line); - #define __io_virt(x) __io_virt_debug((unsigned long)(x), __FILE__, __LINE__) -#else - #define __io_virt(x) ((void *)(x)) -#endif - #ifndef __i386__ /* * Change virtual addresses to physical addresses and vv. @@ -184,10 +173,10 @@ extern void iounmap(void *addr); * memory location directly. */ -#define readb(addr) (*(volatile unsigned char *) __io_virt(addr)) -#define readw(addr) (*(volatile unsigned short *) __io_virt(addr)) -#define readl(addr) (*(volatile unsigned int *) __io_virt(addr)) -#define readq(addr) (*(volatile unsigned long *) __io_virt(addr)) +#define readb(addr) (*(volatile unsigned char *) (addr)) +#define readw(addr) (*(volatile unsigned short *) (addr)) +#define readl(addr) (*(volatile unsigned int *) (addr)) +#define readq(addr) (*(volatile unsigned long *) (addr)) #define readb_relaxed(a) readb(a) #define readw_relaxed(a) readw(a) #define readl_relaxed(a) readl(a) @@ -197,10 +186,10 @@ extern void iounmap(void *addr); #define __raw_readl readl #define __raw_readq readq -#define writeb(b,addr) (*(volatile unsigned char *) __io_virt(addr) = (b)) -#define writew(b,addr) (*(volatile unsigned short *) __io_virt(addr) = (b)) -#define writel(b,addr) (*(volatile unsigned int *) __io_virt(addr) = (b)) -#define writeq(b,addr) (*(volatile unsigned long *) __io_virt(addr) = (b)) +#define writeb(b,addr) (*(volatile unsigned char *) (addr) = (b)) +#define writew(b,addr) (*(volatile unsigned short *) (addr) = (b)) +#define writel(b,addr) (*(volatile unsigned int *) (addr) = (b)) +#define writeq(b,addr) (*(volatile unsigned long *) (addr) = (b)) #define __raw_writeb writeb #define __raw_writew writew #define __raw_writel writel @@ -208,7 +197,7 @@ extern void iounmap(void *addr); void *memcpy_fromio(void*,const void*,unsigned); void *memcpy_toio(void*,const void*,unsigned); -#define memset_io(a,b,c) memset(__io_virt(a),(b),(c)) +#define memset_io(a,b,c) memset((void *)(a),(b),(c)) /* * ISA space is 'always mapped' on a typical x86 system, no need to @@ -235,8 +224,8 @@ void *memcpy_toio(void*,const void*,unsi * Again, x86-64 does not require mem IO specific function. */ -#define eth_io_copy_and_sum(a,b,c,d) eth_copy_and_sum((a),__io_virt(b),(c),(d)) -#define isa_eth_io_copy_and_sum(a,b,c,d) eth_copy_and_sum((a),__io_virt(__ISA_IO_base + (b)),(c),(d)) +#define eth_io_copy_and_sum(a,b,c,d) eth_copy_and_sum((a),(void *)(b),(c),(d)) +#define isa_eth_io_copy_and_sum(a,b,c,d) eth_copy_and_sum((a),(void *)(__ISA_IO_base + (b)),(c),(d)) /** * check_signature - find BIOS signatures --- linux-2.6.4-rc2/include/asm-x86_64/irq.h 2004-01-09 00:04:32.000000000 -0800 +++ 25/include/asm-x86_64/irq.h 2004-03-07 20:48:21.000000000 -0800 @@ -53,4 +53,8 @@ extern int can_request_irq(unsigned int, #define ARCH_HAS_NMI_WATCHDOG /* See include/linux/nmi.h */ #endif +struct irqaction; +struct pt_regs; +int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); + #endif /* _ASM_IRQ_H */ --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/include/asm-x86_64/kgdb.h 2004-03-07 20:47:04.000000000 -0800 @@ -0,0 +1,71 @@ +#ifndef __KGDB +#define __KGDB + +/* + * This file should not include ANY others. This makes it usable + * most anywhere without the fear of include order or inclusion. + * Make it so! + * + * This file may be included all the time. It is only active if + * CONFIG_KGDB is defined, otherwise it stubs out all the macros + * and entry points. + */ +#if defined(CONFIG_KGDB) && !defined(__ASSEMBLY__) + +extern void breakpoint(void); +#define INIT_KGDB_INTS kgdb_enable_ints() + +#ifndef BREAKPOINT +#define BREAKPOINT asm(" int $3") +#endif + +extern void kgdb_schedule_breakpoint(void); +extern void kgdb_process_breakpoint(void); + +extern int kgdb_tty_hook(void); +extern int kgdb_eth_hook(void); +extern int kgdboe; + +/* + * GDB debug stub (or any debug stub) can point the 'linux_debug_hook' + * pointer to its routine and it will be entered as the first thing + * when a trap occurs. + * + * Return values are, at present, undefined. + * + * The debug hook routine does not necessarily return to its caller. + * It has the register image and thus may choose to resume execution + * anywhere it pleases. + */ +struct pt_regs; + +extern int kgdb_handle_exception(int trapno, + int signo, int err_code, struct pt_regs *regs); +extern int in_kgdb(struct pt_regs *regs); + +extern void set_debug_traps(void); + +#ifdef CONFIG_KGDB_TS +void kgdb_tstamp(int line, char *source, int data0, int data1); +/* + * This is the time stamp function. The macro adds the source info and + * does a cast on the data to allow most any 32-bit value. + */ + +#define kgdb_ts(data0,data1) kgdb_tstamp(__LINE__,__FILE__,(int)data0,(int)data1) +#else +#define kgdb_ts(data0,data1) +#endif +#else /* CONFIG_KGDB && ! __ASSEMBLY__ ,stubs follow... */ +#ifndef BREAKPOINT +#define BREAKPOINT +#endif +#define kgdb_ts(data0,data1) +#define in_kgdb (0) +#define kgdb_handle_exception +#define breakpoint +#define INIT_KGDB_INTS +#define kgdb_process_breakpoint() do {} while(0) + +#endif +#endif /* __KGDB */ --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/include/asm-x86_64/kgdb_local.h 2004-03-07 20:47:04.000000000 -0800 @@ -0,0 +1,102 @@ +#ifndef __KGDB_LOCAL +#define ___KGDB_LOCAL +#include +#include +#include +#include +#include +#include +#include +#include + +#define PORT 0x3f8 +#ifdef CONFIG_KGDB_PORT +#undef PORT +#define PORT CONFIG_KGDB_PORT +#endif +#define IRQ 4 +#ifdef CONFIG_KGDB_IRQ +#undef IRQ +#define IRQ CONFIG_KGDB_IRQ +#endif +#define SB_CLOCK 1843200 +#define SB_BASE (SB_CLOCK/16) +#define SB_BAUD9600 SB_BASE/9600 +#define SB_BAUD192 SB_BASE/19200 +#define SB_BAUD384 SB_BASE/38400 +#define SB_BAUD576 SB_BASE/57600 +#define SB_BAUD1152 SB_BASE/115200 +#ifdef CONFIG_KGDB_9600BAUD +#define SB_BAUD SB_BAUD9600 +#endif +#ifdef CONFIG_KGDB_19200BAUD +#define SB_BAUD SB_BAUD192 +#endif +#ifdef CONFIG_KGDB_38400BAUD +#define SB_BAUD SB_BAUD384 +#endif +#ifdef CONFIG_KGDB_57600BAUD +#define SB_BAUD SB_BAUD576 +#endif +#ifdef CONFIG_KGDB_115200BAUD +#define SB_BAUD SB_BAUD1152 +#endif +#ifndef SB_BAUD +#define SB_BAUD SB_BAUD1152 /* Start with this if not given */ +#endif + +#ifndef CONFIG_X86_TSC +#undef rdtsc +#define rdtsc(a,b) if (a++ > 10000){a = 0; b++;} +#undef rdtscll +#define rdtscll(s) s++ +#endif + +#ifdef _raw_read_unlock /* must use a name that is "define"ed, not an inline */ +#undef spin_lock +#undef spin_trylock +#undef spin_unlock +#define spin_lock _raw_spin_lock +#define spin_trylock _raw_spin_trylock +#define spin_unlock _raw_spin_unlock +#else +#endif +#undef spin_unlock_wait +#define spin_unlock_wait(x) do { cpu_relax(); barrier();} \ + while(spin_is_locked(x)) + +#define SB_IER 1 +#define SB_MCR UART_MCR_OUT2 | UART_MCR_DTR | UART_MCR_RTS + +#define FLAGS 0 +#define SB_STATE { \ + magic: SSTATE_MAGIC, \ + baud_base: SB_BASE, \ + port: PORT, \ + irq: IRQ, \ + flags: FLAGS, \ + custom_divisor:SB_BAUD} +#define SB_INFO { \ + magic: SERIAL_MAGIC, \ + port: PORT,0,FLAGS, \ + state: &state, \ + tty: (struct tty_struct *)&state, \ + IER: SB_IER, \ + MCR: SB_MCR} +extern void putDebugChar(int); +/* RTAI support needs us to really stop/start interrupts */ + +#define kgdb_sti() __asm__ __volatile__("sti": : :"memory") +#define kgdb_cli() __asm__ __volatile__("cli": : :"memory") +#define kgdb_local_save_flags(x) __asm__ __volatile__(\ + "pushfl ; popl %0":"=g" (x): /* no input */) +#define kgdb_local_irq_restore(x) __asm__ __volatile__(\ + "pushl %0 ; popfl": \ + /* no output */ :"g" (x):"memory", "cc") +#define kgdb_local_irq_save(x) kgdb_local_save_flags(x); kgdb_cli() + +#ifdef CONFIG_SERIAL +extern void shutdown_for_kgdb(struct async_struct *info); +#endif +#define INIT_KDEBUG putDebugChar("+"); +#endif /* __KGDB_LOCAL */ --- linux-2.6.4-rc2/include/asm-x86_64/mman.h 2003-09-27 18:57:47.000000000 -0700 +++ 25/include/asm-x86_64/mman.h 2004-03-07 20:48:04.000000000 -0800 @@ -23,6 +23,7 @@ #define MAP_NORESERVE 0x4000 /* don't check for reservations */ #define MAP_POPULATE 0x8000 /* populate (prefault) pagetables */ #define MAP_NONBLOCK 0x10000 /* do not block on IO */ +#define MAP_INHERIT 0x20000 /* inherit the protection bits of the underlying vma*/ #define MS_ASYNC 1 /* sync memory asynchronously */ #define MS_INVALIDATE 2 /* invalidate the caches */ --- linux-2.6.4-rc2/include/asm-x86_64/pci.h 2004-03-03 23:12:48.000000000 -0800 +++ 25/include/asm-x86_64/pci.h 2004-03-07 20:46:59.000000000 -0800 @@ -78,10 +78,18 @@ extern dma_addr_t swiotlb_map_single (st int dir); extern void swiotlb_unmap_single (struct device *hwdev, dma_addr_t dev_addr, size_t size, int dir); -extern void swiotlb_sync_single (struct device *hwdev, dma_addr_t dev_addr, - size_t size, int dir); -extern void swiotlb_sync_sg (struct device *hwdev, struct scatterlist *sg, int nelems, - int dir); +extern void swiotlb_sync_single_for_cpu (struct device *hwdev, + dma_addr_t dev_addr, + size_t size, int dir); +extern void swiotlb_sync_single_for_device (struct device *hwdev, + dma_addr_t dev_addr, + size_t size, int dir); +extern void swiotlb_sync_sg_for_cpu (struct device *hwdev, + struct scatterlist *sg, int nelems, + int dir); +extern void swiotlb_sync_sg_for_device (struct device *hwdev, + struct scatterlist *sg, int nelems, + int dir); extern int swiotlb_map_sg(struct device *hwdev, struct scatterlist *sg, int nents, int direction); extern void swiotlb_unmap_sg(struct device *hwdev, struct scatterlist *sg, @@ -95,7 +103,7 @@ extern void swiotlb_unmap_sg(struct devi * The 32-bit bus address to use is returned. * * Once the device is given the dma address, the device owns this memory - * until either pci_unmap_single or pci_dma_sync_single is performed. + * until either pci_unmap_single or pci_dma_sync_single_for_cpu is performed. */ extern dma_addr_t pci_map_single(struct pci_dev *hwdev, void *ptr, size_t size, int direction); @@ -125,29 +133,56 @@ void pci_unmap_single(struct pci_dev *hw #define pci_unmap_len_set(PTR, LEN_NAME, VAL) \ (((PTR)->LEN_NAME) = (VAL)) -static inline void pci_dma_sync_single(struct pci_dev *hwdev, - dma_addr_t dma_handle, - size_t size, int direction) +static inline void pci_dma_sync_single_for_cpu(struct pci_dev *hwdev, + dma_addr_t dma_handle, + size_t size, int direction) { BUG_ON(direction == PCI_DMA_NONE); #ifdef CONFIG_SWIOTLB if (swiotlb) - return swiotlb_sync_single(&hwdev->dev,dma_handle,size,direction); + return swiotlb_sync_single_for_cpu(&hwdev->dev,dma_handle,size,direction); #endif flush_write_buffers(); } -static inline void pci_dma_sync_sg(struct pci_dev *hwdev, - struct scatterlist *sg, - int nelems, int direction) +static inline void pci_dma_sync_single_for_device(struct pci_dev *hwdev, + dma_addr_t dma_handle, + size_t size, int direction) +{ + BUG_ON(direction == PCI_DMA_NONE); + +#ifdef CONFIG_SWIOTLB + if (swiotlb) + return swiotlb_sync_single_for_device(&hwdev->dev,dma_handle,size,direction); +#endif + + flush_write_buffers(); +} + +static inline void pci_dma_sync_sg_for_cpu(struct pci_dev *hwdev, + struct scatterlist *sg, + int nelems, int direction) +{ + BUG_ON(direction == PCI_DMA_NONE); + +#ifdef CONFIG_SWIOTLB + if (swiotlb) + return swiotlb_sync_sg_for_cpu(&hwdev->dev,sg,nelems,direction); +#endif + flush_write_buffers(); +} + +static inline void pci_dma_sync_sg_for_device(struct pci_dev *hwdev, + struct scatterlist *sg, + int nelems, int direction) { BUG_ON(direction == PCI_DMA_NONE); #ifdef CONFIG_SWIOTLB if (swiotlb) - return swiotlb_sync_sg(&hwdev->dev,sg,nelems,direction); + return swiotlb_sync_sg_for_device(&hwdev->dev,sg,nelems,direction); #endif flush_write_buffers(); } @@ -218,12 +253,21 @@ static inline dma_addr_t pci_map_page(st * If you perform a pci_map_single() but wish to interrogate the * buffer using the cpu, yet do not wish to teardown the PCI dma * mapping, you must call this function before doing so. At the - * next point you give the PCI dma address back to the card, the + * next point you give the PCI dma address back to the card, you + * must first perform a pci_dma_sync_for_device, and then the * device again owns the buffer. */ -static inline void pci_dma_sync_single(struct pci_dev *hwdev, - dma_addr_t dma_handle, - size_t size, int direction) +static inline void pci_dma_sync_single_for_cpu(struct pci_dev *hwdev, + dma_addr_t dma_handle, + size_t size, int direction) +{ + if (direction == PCI_DMA_NONE) + out_of_line_bug(); +} + +static inline void pci_dma_sync_single_for_device(struct pci_dev *hwdev, + dma_addr_t dma_handle, + size_t size, int direction) { if (direction == PCI_DMA_NONE) out_of_line_bug(); @@ -233,12 +277,20 @@ static inline void pci_dma_sync_single(s /* Make physical memory consistent for a set of streaming * mode DMA translations after a transfer. * - * The same as pci_dma_sync_single but for a scatter-gather list, + * The same as pci_dma_sync_single_* but for a scatter-gather list, * same rules and usage. */ -static inline void pci_dma_sync_sg(struct pci_dev *hwdev, - struct scatterlist *sg, - int nelems, int direction) +static inline void pci_dma_sync_sg_for_cpu(struct pci_dev *hwdev, + struct scatterlist *sg, + int nelems, int direction) +{ + if (direction == PCI_DMA_NONE) + out_of_line_bug(); +} + +static inline void pci_dma_sync_sg_for_device(struct pci_dev *hwdev, + struct scatterlist *sg, + int nelems, int direction) { if (direction == PCI_DMA_NONE) out_of_line_bug(); @@ -264,27 +316,32 @@ extern void pci_unmap_sg(struct pci_dev */ extern int pci_dma_supported(struct pci_dev *hwdev, u64 mask); -static __inline__ dma64_addr_t +static inline dma64_addr_t pci_dac_page_to_dma(struct pci_dev *pdev, struct page *page, unsigned long offset, int direction) { return ((dma64_addr_t) page_to_phys(page) + (dma64_addr_t) offset); } -static __inline__ struct page * +static inline struct page * pci_dac_dma_to_page(struct pci_dev *pdev, dma64_addr_t dma_addr) { return virt_to_page(__va(dma_addr)); } -static __inline__ unsigned long +static inline unsigned long pci_dac_dma_to_offset(struct pci_dev *pdev, dma64_addr_t dma_addr) { return (dma_addr & ~PAGE_MASK); } -static __inline__ void -pci_dac_dma_sync_single(struct pci_dev *pdev, dma64_addr_t dma_addr, size_t len, int direction) +static inline void +pci_dac_dma_sync_single_for_cpu(struct pci_dev *pdev, dma64_addr_t dma_addr, size_t len, int direction) +{ +} + +static inline void +pci_dac_dma_sync_single_for_device(struct pci_dev *pdev, dma64_addr_t dma_addr, size_t len, int direction) { flush_write_buffers(); } --- linux-2.6.4-rc2/include/asm-x86_64/pgtable.h 2004-03-03 23:12:48.000000000 -0800 +++ 25/include/asm-x86_64/pgtable.h 2004-03-07 20:48:04.000000000 -0800 @@ -344,9 +344,19 @@ static inline pgd_t *current_pgd_offset_ #define pmd_pfn(x) ((pmd_val(x) >> PAGE_SHIFT) & __PHYSICAL_MASK) #define pte_to_pgoff(pte) ((pte_val(pte) & PHYSICAL_PAGE_MASK) >> PAGE_SHIFT) -#define pgoff_to_pte(off) ((pte_t) { ((off) << PAGE_SHIFT) | _PAGE_FILE }) #define PTE_FILE_MAX_BITS __PHYSICAL_MASK_SHIFT +#define pte_to_pgprot(pte) \ + __pgprot((pte_val(pte) & (_PAGE_RW | _PAGE_PROTNONE)) \ + | ((pte_val(pte) & _PAGE_PROTNONE) ? 0 : \ + (_PAGE_USER | _PAGE_PRESENT)) | _PAGE_ACCESSED) + +#define pgoff_prot_to_pte(off, prot) \ + ((pte_t) { _PAGE_FILE + \ + (pgprot_val(prot) & (_PAGE_RW | _PAGE_PROTNONE)) + \ + ((off) << PAGE_SHIFT) }) + + /* PTE - Level 1 access. */ /* page, protection -> pte */ --- linux-2.6.4-rc2/include/asm-x86_64/system.h 2004-03-03 23:12:48.000000000 -0800 +++ 25/include/asm-x86_64/system.h 2004-03-07 20:46:49.000000000 -0800 @@ -276,13 +276,6 @@ static inline unsigned long __cmpxchg(vo ((__typeof__(*(ptr)))__cmpxchg((ptr),(unsigned long)(o),\ (unsigned long)(n),sizeof(*(ptr)))) -static inline __u32 cmpxchg4_locked(__u32 *ptr, __u32 old, __u32 new) -{ - asm volatile("lock ; cmpxchgl %k1,%2" : - "=r" (new) : "0" (old), "m" (*(__u32 *)ptr) : "memory"); - return new; -} - #ifdef CONFIG_SMP #define smp_mb() mb() #define smp_rmb() rmb() --- linux-2.6.4-rc2/include/asm-x86_64/unistd.h 2004-03-03 23:12:48.000000000 -0800 +++ 25/include/asm-x86_64/unistd.h 2004-03-07 20:48:04.000000000 -0800 @@ -490,8 +490,8 @@ __SYSCALL(__NR_epoll_create, sys_epoll_c __SYSCALL(__NR_epoll_ctl_old, sys_ni_syscall) #define __NR_epoll_wait_old 215 __SYSCALL(__NR_epoll_wait_old, sys_ni_syscall) -#define __NR_remap_file_pages 216 -__SYSCALL(__NR_remap_file_pages, sys_remap_file_pages) +#define __NR_old_remap_file_pages 216 +__SYSCALL(__NR_old_remap_file_pages, old_remap_file_pages) #define __NR_getdents64 217 __SYSCALL(__NR_getdents64, sys_getdents64) #define __NR_set_tid_address 218 --- linux-2.6.4-rc2/include/linux/acpi.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/linux/acpi.h 2004-03-07 20:46:46.000000000 -0800 @@ -233,8 +233,27 @@ struct acpi_table_hpet { } __attribute__ ((packed)); /* + * Simple Boot Flags + * http://www.microsoft.com/whdc/hwdev/resources/specs/simp_bios.mspx + */ +struct acpi_table_sbf +{ + u8 sbf_signature[4]; + u32 sbf_len; + u8 sbf_revision; + u8 sbf_csum; + u8 sbf_oemid[6]; + u8 sbf_oemtable[8]; + u8 sbf_revdata[4]; + u8 sbf_creator[4]; + u8 sbf_crearev[4]; + u8 sbf_cmos; + u8 sbf_spare[3]; +} __attribute__ ((packed)); + +/* * System Resource Affinity Table (SRAT) - * see http://www.microsoft.com/hwdev/design/srat.htm + * http://www.microsoft.com/whdc/hwdev/platform/proc/SRAT.mspx */ struct acpi_table_srat { @@ -317,6 +336,15 @@ struct acpi_table_ecdt { char ec_id[0]; } __attribute__ ((packed)); +/* PCI MMCONFIG */ + +struct acpi_table_mcfg { + struct acpi_table_header header; + u8 reserved[8]; + u32 base_address; + u32 base_reserved; +} __attribute__ ((packed)); + /* Table Handlers */ enum acpi_table_id { @@ -338,6 +366,7 @@ enum acpi_table_id { ACPI_SSDT, ACPI_SPMI, ACPI_HPET, + ACPI_MCFG, ACPI_TABLE_COUNT }; @@ -369,6 +398,10 @@ void acpi_numa_arch_fixup(void); extern int acpi_mp_config; +extern u32 pci_mmcfg_base_addr; + +extern int sbf_port ; + #else /*!CONFIG_ACPI_BOOT*/ #define acpi_mp_config 0 --- linux-2.6.4-rc2/include/linux/backing-dev.h 2003-06-14 12:18:06.000000000 -0700 +++ 25/include/linux/backing-dev.h 2004-03-07 20:47:53.000000000 -0800 @@ -20,10 +20,14 @@ enum bdi_state { BDI_unused, /* Available bits start here */ }; +typedef int (congested_fn)(void *, int); + struct backing_dev_info { unsigned long ra_pages; /* max readahead in PAGE_CACHE_SIZE units */ unsigned long state; /* Always use atomic bitops on this */ int memory_backed; /* Cannot clean pages with writepage */ + congested_fn *congested_fn; /* Function pointer if device is md/dm */ + void *congested_data; /* Pointer to aux data for congested func */ }; extern struct backing_dev_info default_backing_dev_info; @@ -32,14 +36,27 @@ int writeback_acquire(struct backing_dev int writeback_in_progress(struct backing_dev_info *bdi); void writeback_release(struct backing_dev_info *bdi); +static inline int bdi_congested(struct backing_dev_info *bdi, int bdi_bits) +{ + if (bdi->congested_fn) + return bdi->congested_fn(bdi->congested_data, bdi_bits); + return (bdi->state & bdi_bits); +} + static inline int bdi_read_congested(struct backing_dev_info *bdi) { - return test_bit(BDI_read_congested, &bdi->state); + return bdi_congested(bdi, 1 << BDI_read_congested); } static inline int bdi_write_congested(struct backing_dev_info *bdi) { - return test_bit(BDI_write_congested, &bdi->state); + return bdi_congested(bdi, 1 << BDI_write_congested); +} + +static inline int bdi_rw_congested(struct backing_dev_info *bdi) +{ + return bdi_congested(bdi, (1 << BDI_read_congested)| + (1 << BDI_write_congested)); } #endif /* _LINUX_BACKING_DEV_H */ --- linux-2.6.4-rc2/include/linux/binfmts.h 2003-08-08 22:55:14.000000000 -0700 +++ 25/include/linux/binfmts.h 2004-03-07 20:47:34.000000000 -0800 @@ -35,9 +35,13 @@ struct linux_binprm{ char * interp; /* Name of the binary really executed. Most of the time same as filename, but could be different for binfmt_{misc,script} */ + unsigned long interp_flags; unsigned long loader, exec; }; +#define BINPRM_FLAGS_ENFORCE_NONDUMP_BIT 0 +#define BINPRM_FLAGS_ENFORCE_NONDUMP (1 << BINPRM_FLAGS_ENFORCE_NONDUMP_BIT) + /* * This structure defines the functions that are used to load the binary formats that * linux accepts. --- linux-2.6.4-rc2/include/linux/blkdev.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/linux/blkdev.h 2004-03-07 20:48:10.000000000 -0800 @@ -372,6 +372,8 @@ struct request_queue #define blk_queue_plugged(q) !list_empty(&(q)->plug_list) #define blk_queue_tagged(q) test_bit(QUEUE_FLAG_QUEUED, &(q)->queue_flags) +#define blk_queue_stopped(q) test_bit(QUEUE_FLAG_STOPPED, &(q)->queue_flags) + #define blk_fs_request(rq) ((rq)->flags & REQ_CMD) #define blk_pc_request(rq) ((rq)->flags & REQ_BLOCK_PC) #define blk_noretry_request(rq) ((rq)->flags & REQ_FAILFAST) @@ -514,6 +516,9 @@ extern void blk_stop_queue(request_queue extern void __blk_stop_queue(request_queue_t *q); extern void blk_run_queue(request_queue_t *q); extern void blk_queue_activity_fn(request_queue_t *, activity_fn *, void *); +extern struct request *blk_rq_map_user(request_queue_t *, int, void __user *, unsigned int); +extern int blk_rq_unmap_user(struct request *, void __user *, unsigned int, int); +extern int blk_execute_rq(request_queue_t *, struct gendisk *, struct request *); static inline request_queue_t *bdev_get_queue(struct block_device *bdev) { @@ -586,7 +591,7 @@ extern int blk_queue_init_tags(request_q extern void blk_queue_free_tags(request_queue_t *); extern int blk_queue_resize_tags(request_queue_t *, int); extern void blk_queue_invalidate_tags(request_queue_t *); -extern void blk_congestion_wait(int rw, long timeout); +extern long blk_congestion_wait(int rw, long timeout); extern void blk_rq_bio_prep(request_queue_t *, struct request *, struct bio *); extern void blk_rq_prep_restart(struct request *); --- linux-2.6.4-rc2/include/linux/cdrom.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/linux/cdrom.h 2004-03-07 20:48:00.000000000 -0800 @@ -877,10 +877,15 @@ struct mode_page_header { #include /* not really needed, later.. */ #include +#define CDDA_OLD 0 /* old style */ +#define CDDA_BPC_SINGLE 1 /* block_pc, but single frame */ +#define CDDA_BPC_FULL 2 /* full speed block pc */ + /* Uniform cdrom data structures for cdrom.c */ struct cdrom_device_info { struct cdrom_device_ops *ops; /* link to device_ops */ struct cdrom_device_info *next; /* next device_info for this major */ + struct gendisk *disk; /* matching block layer disk */ void *handle; /* driver-dependent data */ /* specifications */ int mask; /* mask of capability: disables them */ @@ -894,6 +899,7 @@ struct cdrom_device_info { /* per-device flags */ __u8 sanyo_slot : 2; /* Sanyo 3 CD changer support */ __u8 reserved : 6; /* not used yet */ + int cdda_method; /* see flags */ int for_data; int (*exit)(struct cdrom_device_info *); int mrw_mode_page; --- linux-2.6.4-rc2/include/linux/compat.h 2004-02-03 20:42:38.000000000 -0800 +++ 25/include/linux/compat.h 2004-03-07 20:47:07.000000000 -0800 @@ -6,15 +6,27 @@ */ #include -#ifdef CONFIG_COMPAT +#ifndef CONFIG_COMPAT + +/* Non-native task requiring compat... doesn't exist */ +#define is_compat_task(x) 0 + +#else #include #include /* for HZ */ +#include +#include /* Conditional process compat */ #include #define compat_jiffies_to_clock_t(x) \ (((unsigned long)(x) * COMPAT_USER_HZ) / HZ) +/* Non-native task requiring compat */ +#ifndef HAVE_ARCH_IS_COMPAT_TASK +#define is_compat_task(x) (x->personality == PER_LINUX32) +#endif + struct compat_itimerspec { struct compat_timespec it_interval; struct compat_timespec it_value; @@ -45,7 +57,7 @@ typedef struct { extern int cp_compat_stat(struct kstat *, struct compat_stat *); extern int get_compat_timespec(struct timespec *, const struct compat_timespec *); -extern int put_compat_timespec(struct timespec *, const struct compat_timespec *); +extern int put_compat_timespec(const struct timespec *, struct compat_timespec *); struct compat_iovec { compat_uptr_t iov_base; @@ -83,10 +95,15 @@ struct compat_dirent { char d_name[256]; }; -typedef union compat_sigval { - compat_int_t sival_int; - compat_uptr_t sival_ptr; -} compat_sigval_t; - +long compat_sys_semctl(int first, int second, int third, void __user *uptr); +long compat_sys_msgsnd(int first, int second, int third, void __user *uptr); +long compat_sys_msgrcv(int first, int second, int msgtyp, int third, + int version, void __user *uptr); +long compat_sys_msgctl(int first, int second, void __user *uptr); +long compat_sys_shmat(int first, int second, compat_uptr_t third, int version, + void __user *uptr); +long compat_sys_shmctl(int first, int second, void __user *uptr); +long compat_sys_semtimedop(int semid, struct sembuf __user *tsems, + unsigned nsems, const struct compat_timespec __user *timeout); #endif /* CONFIG_COMPAT */ #endif /* _LINUX_COMPAT_H */ --- linux-2.6.4-rc2/include/linux/compat_ioctl.h 2004-02-03 20:42:38.000000000 -0800 +++ 25/include/linux/compat_ioctl.h 2004-03-07 20:47:52.000000000 -0800 @@ -122,7 +122,6 @@ COMPATIBLE_IOCTL(STOP_ARRAY) COMPATIBLE_IOCTL(STOP_ARRAY_RO) COMPATIBLE_IOCTL(RESTART_ARRAY_RW) /* DM */ -#ifdef CONFIG_DM_IOCTL_V4 COMPATIBLE_IOCTL(DM_VERSION) COMPATIBLE_IOCTL(DM_LIST_DEVICES) COMPATIBLE_IOCTL(DM_DEV_CREATE) @@ -135,19 +134,7 @@ COMPATIBLE_IOCTL(DM_TABLE_LOAD) COMPATIBLE_IOCTL(DM_TABLE_CLEAR) COMPATIBLE_IOCTL(DM_TABLE_DEPS) COMPATIBLE_IOCTL(DM_TABLE_STATUS) -#else -COMPATIBLE_IOCTL(DM_VERSION) -COMPATIBLE_IOCTL(DM_REMOVE_ALL) -COMPATIBLE_IOCTL(DM_DEV_CREATE) -COMPATIBLE_IOCTL(DM_DEV_REMOVE) -COMPATIBLE_IOCTL(DM_DEV_RELOAD) -COMPATIBLE_IOCTL(DM_DEV_SUSPEND) -COMPATIBLE_IOCTL(DM_DEV_RENAME) -COMPATIBLE_IOCTL(DM_DEV_DEPS) -COMPATIBLE_IOCTL(DM_DEV_STATUS) -COMPATIBLE_IOCTL(DM_TARGET_STATUS) -COMPATIBLE_IOCTL(DM_TARGET_WAIT) -#endif +COMPATIBLE_IOCTL(DM_LIST_VERSIONS) /* Big K */ COMPATIBLE_IOCTL(PIO_FONT) COMPATIBLE_IOCTL(GIO_FONT) --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/include/linux/compat_siginfo.h 2004-03-07 20:47:06.000000000 -0800 @@ -0,0 +1,170 @@ +#ifndef _ASM_GENERIC_COMPAT_SIGINFO_H +#define _ASM_GENERIC_COMPAT_SIGINFO_H + +#include +#include + +#ifndef CONFIG_COMPAT + +/* No compatibility layer required, add empty definitions for the compiler */ + +typedef struct compat_siginfo{ +} compat_siginfo_t; + +static inline int compat_copy_siginfo_to_user(compat_siginfo_t __user *to, + struct siginfo *from) +{ + return -1; +} + +#else + +#include +#include + +/* compat view of sigval_t */ +typedef union compat_sigval { + compat_int_t sival_int; + compat_uptr_t sival_ptr; +} compat_sigval_t; + +/* + * This is the size (including padding) of the part of the + * struct siginfo that is before the union. + */ +#ifndef __ARCH_SI_COMPAT_PREAMBLE_SIZE +#define __ARCH_SI_COMPAT_PREAMBLE_SIZE (3 * sizeof(int)) +#endif + +#define SI_COMPAT_MAX_SIZE 128 +#ifndef SI_COMPAT_PAD_SIZE +#define SI_COMPAT_PAD_SIZE ((SI_COMPAT_MAX_SIZE - __ARCH_SI_COMPAT_PREAMBLE_SIZE) / sizeof(int)) +#endif + +/* 32-bit view of si.uid_t */ +#ifndef __ARCH_SI_COMPAT_UID_T +#define __ARCH_SI_COMPAT_UID_T compat_uid_t +#endif + +/* 32-bit view of si.band_t */ +#ifndef __ARCH_SI_COMPAT_BAND_T +#define __ARCH_SI_COMPAT_BAND_T compat_int_t +#endif + +#ifndef HAVE_ARCH_COMPAT_SIGINFO_T + +/* Compat view of siginfo_t */ +typedef struct compat_siginfo { + compat_int_t si_signo; + compat_int_t si_errno; + compat_int_t si_code; + + union { + compat_int_t _pad[SI_COMPAT_PAD_SIZE]; + + /* kill() */ + struct { + compat_pid_t _pid; /* sender's pid */ + __ARCH_SI_COMPAT_UID_T _uid; /* sender's uid */ + } _kill; + + /* POSIX.1b timers */ + struct { + compat_timer_t _tid; /* timer id */ + compat_int_t _overrun; /* overrun count */ + char _pad[sizeof( __ARCH_SI_UID_T) - sizeof(int)]; + compat_sigval_t _sigval; /* same as below */ + compat_int_t _sys_private; /* not to be passed to user */ + } _timer; + + /* POSIX.1b signals */ + struct { + compat_pid_t _pid; /* sender's pid */ + __ARCH_SI_COMPAT_UID_T _uid; /* sender's uid */ + compat_sigval_t _sigval; + } _rt; + + /* SIGCHLD */ + struct { + compat_pid_t _pid; /* which child */ + __ARCH_SI_COMPAT_UID_T _uid; /* sender's uid */ + compat_int_t _status; /* exit code */ + compat_clock_t _utime; + compat_clock_t _stime; + } _sigchld; + + /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ + struct { + compat_uptr_t _addr; /* faulting insn/memory ref. */ +#ifdef __ARCH_SI_COMPAT_TRAPNO + compat_int_t _trapno; /* TRAP # which caused the signal */ +#endif + } _sigfault; + + /* SIGPOLL */ + struct { + __ARCH_SI_COMPAT_BAND_T _band; /* POLL_IN, POLL_OUT, POLL_MSG */ + compat_int_t _fd; + } _sigpoll; + } _sifields; +} compat_siginfo_t; +#endif /* !HAVE_ARCH_COMPAT_SIGINFO_T */ + +#ifdef __ARCH_SI_COMPAT_TRAPNO +#define si_trapno _sifields._sigfault._trapno +#endif + +/* + * sigevent definitions + * + * It seems likely that SIGEV_THREAD will have to be handled from + * userspace, libpthread transmuting it to SIGEV_SIGNAL, which the + * thread manager then catches and does the appropriate nonsense. + * However, everything is written out here so as to not get lost. + */ + +#define SIGEV_COMPAT_MAX_SIZE 64 +#ifndef SIGEV_COMPAT_PAD_SIZE +#define SIGEV_COMPAT_PAD_SIZE ((SIGEV_COMPAT_MAX_SIZE/sizeof(int)) - 3) +#endif + +#ifndef HAVE_ARCH_COMPAT_SIGEVENT_T + +/* 32-bit view of sigevent_t */ +typedef struct compat_sigevent { + compat_sigval_t sigev_value; + compat_int_t sigev_signo; + compat_int_t sigev_notify; + union { + compat_int_t _pad[SIGEV_COMPAT_PAD_SIZE]; + compat_int_t _tid; + + struct { + compat_uptr_t _function; + compat_uptr_t _attribute; /* really pthread_attr_t */ + } _sigev_thread; + } _sigev_un; +} compat_sigevent_t; + +#endif /* HAVE_ARCH_COMPAT_SIGEVENT_T */ + +#ifndef HAVE_ARCH_COMPAT_COPY_SIGINFO + +#include + +static inline void compat_copy_siginfo(struct compat_siginfo *to, struct compat_siginfo *from) +{ + if (from->si_code < 0) + memcpy(to, from, sizeof(*to)); + else + /* _sigchld is currently the largest know union member */ + memcpy(to, from, __ARCH_SI_COMPAT_PREAMBLE_SIZE + sizeof(from->_sifields._sigchld)); +} + +#endif /* !HAVE_ARCH_COMPAT_COPY_SIGINFO */ + +extern int compat_copy_siginfo_to_user(compat_siginfo_t __user *to, struct siginfo *from); + +#endif /* CONFIG_COMPAT */ +#endif /* _ASM_GENERIC_COMPAT_SIGINFO_H */ + --- linux-2.6.4-rc2/include/linux/compiler-gcc3.h 2004-03-03 23:12:48.000000000 -0800 +++ 25/include/linux/compiler-gcc3.h 2004-03-07 20:48:20.000000000 -0800 @@ -3,7 +3,7 @@ /* These definitions are for GCC v3.x. */ #include -#if __GNUC_MINOR__ >= 1 +#if __GNUC_MINOR__ >= 1 && __GNUC_MINOR__ < 4 # define inline __inline__ __attribute__((always_inline)) # define __inline__ __inline__ __attribute__((always_inline)) # define __inline __inline__ __attribute__((always_inline)) --- linux-2.6.4-rc2/include/linux/compiler-gcc.h 2003-11-09 16:45:05.000000000 -0800 +++ 25/include/linux/compiler-gcc.h 2004-03-07 20:47:06.000000000 -0800 @@ -13,5 +13,5 @@ shouldn't recognize the original var, and make assumptions about it */ #define RELOC_HIDE(ptr, off) \ ({ unsigned long __ptr; \ - __asm__ ("" : "=g"(__ptr) : "0"(ptr)); \ + __asm__ ("" : "=r"(__ptr) : "0"(ptr)); \ (typeof(ptr)) (__ptr + (off)); }) --- linux-2.6.4-rc2/include/linux/compiler.h 2004-03-03 23:12:48.000000000 -0800 +++ 25/include/linux/compiler.h 2004-03-07 20:48:03.000000000 -0800 @@ -39,6 +39,20 @@ #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) +/* Optimization barrier */ +#ifndef barrier +# define barrier() __memory_barrier() +#endif + +#ifndef RELOC_HIDE +# define RELOC_HIDE(ptr, off) \ + ({ unsigned long __ptr; \ + __ptr = (unsigned long) (ptr); \ + (typeof(ptr)) (__ptr + (off)); }) +#endif + +#endif /* __KERNEL__ */ + /* * Allow us to mark functions as 'deprecated' and have gcc emit a nice * warning for each use, in hopes of speeding the functions removal. @@ -100,18 +114,4 @@ #define noinline #endif -/* Optimization barrier */ -#ifndef barrier -# define barrier() __memory_barrier() -#endif - -#ifndef RELOC_HIDE -# define RELOC_HIDE(ptr, off) \ - ({ unsigned long __ptr; \ - __ptr = (unsigned long) (ptr); \ - (typeof(ptr)) (__ptr + (off)); }) -#endif - -#endif /* __KERNEL__ */ - #endif /* __LINUX_COMPILER_H */ --- linux-2.6.4-rc2/include/linux/config.h 2003-11-09 16:45:05.000000000 -0800 +++ 25/include/linux/config.h 2004-03-07 20:47:04.000000000 -0800 @@ -2,5 +2,8 @@ #define _LINUX_CONFIG_H #include +#ifdef CONFIG_X86 +#include +#endif #endif --- linux-2.6.4-rc2/include/linux/cpu.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/linux/cpu.h 2004-03-07 20:47:23.000000000 -0800 @@ -21,6 +21,8 @@ #include #include +#include +#include #include struct cpu { @@ -56,9 +58,21 @@ extern struct sysdev_class cpu_sysdev_cl extern struct semaphore cpucontrol; #define lock_cpu_hotplug() down(&cpucontrol) #define unlock_cpu_hotplug() up(&cpucontrol) +#define lock_cpu_hotplug_interruptible() down_interruptible(&cpucontrol) +int cpu_down(unsigned int cpu); +#define hotcpu_notifier(fn, pri) { \ + static struct notifier_block fn##_nb = { fn, pri }; \ + register_cpu_notifier(&fn##_nb); \ +} +#define cpu_is_offline(cpu) unlikely(!cpu_online(cpu)) #else #define lock_cpu_hotplug() do { } while (0) #define unlock_cpu_hotplug() do { } while (0) +#define lock_cpu_hotplug_interruptible() 0 +#define hotcpu_notifier(fn, pri) + +/* CPUs don't go offline once they're online w/o CONFIG_HOTPLUG_CPU */ +#define cpu_is_offline(cpu) 0 #endif #endif /* _LINUX_CPU_H_ */ --- linux-2.6.4-rc2/include/linux/cpumask.h 2004-03-03 23:12:48.000000000 -0800 +++ 25/include/linux/cpumask.h 2004-03-07 20:47:23.000000000 -0800 @@ -12,6 +12,7 @@ extern cpumask_t cpu_online_map; extern cpumask_t cpu_possible_map; #define num_online_cpus() cpus_weight(cpu_online_map) +#define num_possible_cpus() cpus_weight(cpu_possible_map) #define cpu_online(cpu) cpu_isset(cpu, cpu_online_map) #define cpu_possible(cpu) cpu_isset(cpu, cpu_possible_map) @@ -24,7 +25,9 @@ extern cpumask_t cpu_possible_map; #define for_each_online_cpu(cpu) for_each_cpu_mask(cpu, cpu_online_map) #else #define cpu_online_map cpumask_of_cpu(0) +#define cpu_possible_map cpumask_of_cpu(0) #define num_online_cpus() 1 +#define num_possible_cpus() 1 #define cpu_online(cpu) ({ BUG_ON((cpu) != 0); 1; }) #define cpu_possible(cpu) ({ BUG_ON((cpu) != 0); 1; }) --- linux-2.6.4-rc2/include/linux/device.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/linux/device.h 2004-03-07 20:47:00.000000000 -0800 @@ -285,6 +285,12 @@ struct device { detached from its driver. */ u64 *dma_mask; /* dma mask (if dma'able device) */ + u64 coherent_dma_mask;/* Like dma_mask, but for + alloc_coherent mappings as + not all hardware supports + 64 bit addresses for consistent + allocations such descriptors. */ + struct list_head dma_pools; /* dma pools (if dma'ble) */ void (*release)(struct device * dev); --- linux-2.6.4-rc2/include/linux/device-mapper.h 2003-06-14 12:18:25.000000000 -0700 +++ 25/include/linux/device-mapper.h 2004-03-07 20:47:52.000000000 -0800 @@ -13,6 +13,11 @@ struct dm_dev; typedef enum { STATUSTYPE_INFO, STATUSTYPE_TABLE } status_type_t; +union map_info { + void *ptr; + unsigned long long ll; +}; + /* * In the constructor the target parameter will already have the * table, type, begin and len fields filled in. @@ -32,7 +37,19 @@ typedef void (*dm_dtr_fn) (struct dm_tar * = 0: The target will handle the io by resubmitting it later * > 0: simple remap complete */ -typedef int (*dm_map_fn) (struct dm_target *ti, struct bio *bio); +typedef int (*dm_map_fn) (struct dm_target *ti, struct bio *bio, + union map_info *map_context); + +/* + * Returns: + * < 0 : error (currently ignored) + * 0 : ended successfully + * 1 : for some reason the io has still not completed (eg, + * multipath target might want to requeue a failed io). + */ +typedef int (*dm_endio_fn) (struct dm_target *ti, + struct bio *bio, int error, + union map_info *map_context); typedef void (*dm_suspend_fn) (struct dm_target *ti); typedef void (*dm_resume_fn) (struct dm_target *ti); @@ -57,9 +74,11 @@ void dm_put_device(struct dm_target *ti, struct target_type { const char *name; struct module *module; + unsigned version[3]; dm_ctr_fn ctr; dm_dtr_fn dtr; dm_map_fn map; + dm_endio_fn end_io; dm_suspend_fn suspend; dm_resume_fn resume; dm_status_fn status; @@ -86,7 +105,7 @@ struct dm_target { sector_t split_io; /* - * These are automaticall filled in by + * These are automatically filled in by * dm_table_get_device. */ struct io_restrictions limits; --- linux-2.6.4-rc2/include/linux/dma-mapping.h 2003-06-14 12:18:31.000000000 -0700 +++ 25/include/linux/dma-mapping.h 2004-03-07 20:46:59.000000000 -0800 @@ -12,6 +12,10 @@ enum dma_data_direction { #include +/* Backwards compat, remove in 2.7.x */ +#define dma_sync_single dma_sync_single_for_cpu +#define dma_sync_sg dma_sync_sg_for_cpu + #endif --- linux-2.6.4-rc2/include/linux/dm-ioctl.h 2003-07-27 12:14:40.000000000 -0700 +++ 25/include/linux/dm-ioctl.h 2004-03-07 20:47:52.000000000 -0800 @@ -1,18 +1,252 @@ /* - * Copyright (C) 2003 Sistina Software (UK) Limited. + * Copyright (C) 2001 - 2003 Sistina Software (UK) Limited. * * This file is released under the LGPL. */ -#ifndef _LINUX_DM_IOCTL_H -#define _LINUX_DM_IOCTL_H +#ifndef _LINUX_DM_IOCTL_V4_H +#define _LINUX_DM_IOCTL_V4_H -#include +#include -#ifdef CONFIG_DM_IOCTL_V4 -#include "dm-ioctl-v4.h" -#else -#include "dm-ioctl-v1.h" -#endif +#define DM_DIR "mapper" /* Slashes not supported */ +#define DM_MAX_TYPE_NAME 16 +#define DM_NAME_LEN 128 +#define DM_UUID_LEN 129 -#endif +/* + * A traditional ioctl interface for the device mapper. + * + * Each device can have two tables associated with it, an + * 'active' table which is the one currently used by io passing + * through the device, and an 'inactive' one which is a table + * that is being prepared as a replacement for the 'active' one. + * + * DM_VERSION: + * Just get the version information for the ioctl interface. + * + * DM_REMOVE_ALL: + * Remove all dm devices, destroy all tables. Only really used + * for debug. + * + * DM_LIST_DEVICES: + * Get a list of all the dm device names. + * + * DM_DEV_CREATE: + * Create a new device, neither the 'active' or 'inactive' table + * slots will be filled. The device will be in suspended state + * after creation, however any io to the device will get errored + * since it will be out-of-bounds. + * + * DM_DEV_REMOVE: + * Remove a device, destroy any tables. + * + * DM_DEV_RENAME: + * Rename a device. + * + * DM_SUSPEND: + * This performs both suspend and resume, depending which flag is + * passed in. + * Suspend: This command will not return until all pending io to + * the device has completed. Further io will be deferred until + * the device is resumed. + * Resume: It is no longer an error to issue this command on an + * unsuspended device. If a table is present in the 'inactive' + * slot, it will be moved to the active slot, then the old table + * from the active slot will be _destroyed_. Finally the device + * is resumed. + * + * DM_DEV_STATUS: + * Retrieves the status for the table in the 'active' slot. + * + * DM_DEV_WAIT: + * Wait for a significant event to occur to the device. This + * could either be caused by an event triggered by one of the + * targets of the table in the 'active' slot, or a table change. + * + * DM_TABLE_LOAD: + * Load a table into the 'inactive' slot for the device. The + * device does _not_ need to be suspended prior to this command. + * + * DM_TABLE_CLEAR: + * Destroy any table in the 'inactive' slot (ie. abort). + * + * DM_TABLE_DEPS: + * Return a set of device dependencies for the 'active' table. + * + * DM_TABLE_STATUS: + * Return the targets status for the 'active' table. + */ + +/* + * All ioctl arguments consist of a single chunk of memory, with + * this structure at the start. If a uuid is specified any + * lookup (eg. for a DM_INFO) will be done on that, *not* the + * name. + */ +struct dm_ioctl { + /* + * The version number is made up of three parts: + * major - no backward or forward compatibility, + * minor - only backwards compatible, + * patch - both backwards and forwards compatible. + * + * All clients of the ioctl interface should fill in the + * version number of the interface that they were + * compiled with. + * + * All recognised ioctl commands (ie. those that don't + * return -ENOTTY) fill out this field, even if the + * command failed. + */ + uint32_t version[3]; /* in/out */ + uint32_t data_size; /* total size of data passed in + * including this struct */ + + uint32_t data_start; /* offset to start of data + * relative to start of this struct */ + + uint32_t target_count; /* in/out */ + int32_t open_count; /* out */ + uint32_t flags; /* in/out */ + uint32_t event_nr; /* in/out */ + uint32_t padding; + + uint64_t dev; /* in/out */ + + char name[DM_NAME_LEN]; /* device name */ + char uuid[DM_UUID_LEN]; /* unique identifier for + * the block device */ +}; + +/* + * Used to specify tables. These structures appear after the + * dm_ioctl. + */ +struct dm_target_spec { + uint64_t sector_start; + uint64_t length; + int32_t status; /* used when reading from kernel only */ + + /* + * Offset in bytes (from the start of this struct) to + * next target_spec. + */ + uint32_t next; + + char target_type[DM_MAX_TYPE_NAME]; + + /* + * Parameter string starts immediately after this object. + * Be careful to add padding after string to ensure correct + * alignment of subsequent dm_target_spec. + */ +}; + +/* + * Used to retrieve the target dependencies. + */ +struct dm_target_deps { + uint32_t count; /* Array size */ + uint32_t padding; /* unused */ + uint64_t dev[0]; /* out */ +}; + +/* + * Used to get a list of all dm devices. + */ +struct dm_name_list { + uint64_t dev; + uint32_t next; /* offset to the next record from + the _start_ of this */ + char name[0]; +}; + +/* + * Used to retrieve the target versions + */ +struct dm_target_versions { + uint32_t next; + uint32_t version[3]; + + char name[0]; +}; + +/* + * If you change this make sure you make the corresponding change + * to dm-ioctl.c:lookup_ioctl() + */ +enum { + /* Top level cmds */ + DM_VERSION_CMD = 0, + DM_REMOVE_ALL_CMD, + DM_LIST_DEVICES_CMD, + + /* device level cmds */ + DM_DEV_CREATE_CMD, + DM_DEV_REMOVE_CMD, + DM_DEV_RENAME_CMD, + DM_DEV_SUSPEND_CMD, + DM_DEV_STATUS_CMD, + DM_DEV_WAIT_CMD, + + /* Table level cmds */ + DM_TABLE_LOAD_CMD, + DM_TABLE_CLEAR_CMD, + DM_TABLE_DEPS_CMD, + DM_TABLE_STATUS_CMD, + + /* Added later */ + DM_LIST_VERSIONS_CMD, +}; + +#define DM_IOCTL 0xfd + +#define DM_VERSION _IOWR(DM_IOCTL, DM_VERSION_CMD, struct dm_ioctl) +#define DM_REMOVE_ALL _IOWR(DM_IOCTL, DM_REMOVE_ALL_CMD, struct dm_ioctl) +#define DM_LIST_DEVICES _IOWR(DM_IOCTL, DM_LIST_DEVICES_CMD, struct dm_ioctl) + +#define DM_DEV_CREATE _IOWR(DM_IOCTL, DM_DEV_CREATE_CMD, struct dm_ioctl) +#define DM_DEV_REMOVE _IOWR(DM_IOCTL, DM_DEV_REMOVE_CMD, struct dm_ioctl) +#define DM_DEV_RENAME _IOWR(DM_IOCTL, DM_DEV_RENAME_CMD, struct dm_ioctl) +#define DM_DEV_SUSPEND _IOWR(DM_IOCTL, DM_DEV_SUSPEND_CMD, struct dm_ioctl) +#define DM_DEV_STATUS _IOWR(DM_IOCTL, DM_DEV_STATUS_CMD, struct dm_ioctl) +#define DM_DEV_WAIT _IOWR(DM_IOCTL, DM_DEV_WAIT_CMD, struct dm_ioctl) + +#define DM_TABLE_LOAD _IOWR(DM_IOCTL, DM_TABLE_LOAD_CMD, struct dm_ioctl) +#define DM_TABLE_CLEAR _IOWR(DM_IOCTL, DM_TABLE_CLEAR_CMD, struct dm_ioctl) +#define DM_TABLE_DEPS _IOWR(DM_IOCTL, DM_TABLE_DEPS_CMD, struct dm_ioctl) +#define DM_TABLE_STATUS _IOWR(DM_IOCTL, DM_TABLE_STATUS_CMD, struct dm_ioctl) + +#define DM_LIST_VERSIONS _IOWR(DM_IOCTL, DM_LIST_VERSIONS_CMD, struct dm_ioctl) + +#define DM_VERSION_MAJOR 4 +#define DM_VERSION_MINOR 1 +#define DM_VERSION_PATCHLEVEL 0 +#define DM_VERSION_EXTRA "-ioctl (2003-12-10)" + +/* Status bits */ +#define DM_READONLY_FLAG (1 << 0) /* In/Out */ +#define DM_SUSPEND_FLAG (1 << 1) /* In/Out */ +#define DM_PERSISTENT_DEV_FLAG (1 << 3) /* In */ + +/* + * Flag passed into ioctl STATUS command to get table information + * rather than current status. + */ +#define DM_STATUS_TABLE_FLAG (1 << 4) /* In */ + +/* + * Flags that indicate whether a table is present in either of + * the two table slots that a device has. + */ +#define DM_ACTIVE_PRESENT_FLAG (1 << 5) /* Out */ +#define DM_INACTIVE_PRESENT_FLAG (1 << 6) /* Out */ + +/* + * Indicates that the buffer passed in wasn't big enough for the + * results. + */ +#define DM_BUFFER_FULL_FLAG (1 << 8) /* Out */ + +#endif /* _LINUX_DM_IOCTL_H */ --- linux-2.6.4-rc2/include/linux/dm-ioctl-v1.h 2003-09-27 18:57:47.000000000 -0700 +++ /dev/null 2002-08-30 16:31:37.000000000 -0700 @@ -1,149 +0,0 @@ -/* - * Copyright (C) 2001 Sistina Software (UK) Limited. - * - * This file is released under the LGPL. - */ - -#ifndef _LINUX_DM_IOCTL_V1_H -#define _LINUX_DM_IOCTL_V1_H - -#include - -#define DM_DIR "mapper" /* Slashes not supported */ -#define DM_MAX_TYPE_NAME 16 -#define DM_NAME_LEN 128 -#define DM_UUID_LEN 129 - -/* - * Implements a traditional ioctl interface to the device mapper. - */ - -/* - * All ioctl arguments consist of a single chunk of memory, with - * this structure at the start. If a uuid is specified any - * lookup (eg. for a DM_INFO) will be done on that, *not* the - * name. - */ -struct dm_ioctl { - /* - * The version number is made up of three parts: - * major - no backward or forward compatibility, - * minor - only backwards compatible, - * patch - both backwards and forwards compatible. - * - * All clients of the ioctl interface should fill in the - * version number of the interface that they were - * compiled with. - * - * All recognised ioctl commands (ie. those that don't - * return -ENOTTY) fill out this field, even if the - * command failed. - */ - uint32_t version[3]; /* in/out */ - uint32_t data_size; /* total size of data passed in - * including this struct */ - - uint32_t data_start; /* offset to start of data - * relative to start of this struct */ - - uint32_t target_count; /* in/out */ - uint32_t open_count; /* out */ - uint32_t flags; /* in/out */ - - __kernel_old_dev_t dev; /* in/out */ - - char name[DM_NAME_LEN]; /* device name */ - char uuid[DM_UUID_LEN]; /* unique identifier for - * the block device */ -}; - -/* - * Used to specify tables. These structures appear after the - * dm_ioctl. - */ -struct dm_target_spec { - int32_t status; /* used when reading from kernel only */ - uint64_t sector_start; - uint32_t length; - - /* - * Offset in bytes (from the start of this struct) to - * next target_spec. - */ - uint32_t next; - - char target_type[DM_MAX_TYPE_NAME]; - - /* - * Parameter string starts immediately after this object. - * Be careful to add padding after string to ensure correct - * alignment of subsequent dm_target_spec. - */ -}; - -/* - * Used to retrieve the target dependencies. - */ -struct dm_target_deps { - uint32_t count; - - __kernel_old_dev_t dev[0]; /* out */ -}; - -/* - * If you change this make sure you make the corresponding change - * to dm-ioctl.c:lookup_ioctl() - */ -enum { - /* Top level cmds */ - DM_VERSION_CMD = 0, - DM_REMOVE_ALL_CMD, - - /* device level cmds */ - DM_DEV_CREATE_CMD, - DM_DEV_REMOVE_CMD, - DM_DEV_RELOAD_CMD, - DM_DEV_RENAME_CMD, - DM_DEV_SUSPEND_CMD, - DM_DEV_DEPS_CMD, - DM_DEV_STATUS_CMD, - - /* target level cmds */ - DM_TARGET_STATUS_CMD, - DM_TARGET_WAIT_CMD -}; - -#define DM_IOCTL 0xfd - -#define DM_VERSION _IOWR(DM_IOCTL, DM_VERSION_CMD, struct dm_ioctl) -#define DM_REMOVE_ALL _IOWR(DM_IOCTL, DM_REMOVE_ALL_CMD, struct dm_ioctl) - -#define DM_DEV_CREATE _IOWR(DM_IOCTL, DM_DEV_CREATE_CMD, struct dm_ioctl) -#define DM_DEV_REMOVE _IOWR(DM_IOCTL, DM_DEV_REMOVE_CMD, struct dm_ioctl) -#define DM_DEV_RELOAD _IOWR(DM_IOCTL, DM_DEV_RELOAD_CMD, struct dm_ioctl) -#define DM_DEV_SUSPEND _IOWR(DM_IOCTL, DM_DEV_SUSPEND_CMD, struct dm_ioctl) -#define DM_DEV_RENAME _IOWR(DM_IOCTL, DM_DEV_RENAME_CMD, struct dm_ioctl) -#define DM_DEV_DEPS _IOWR(DM_IOCTL, DM_DEV_DEPS_CMD, struct dm_ioctl) -#define DM_DEV_STATUS _IOWR(DM_IOCTL, DM_DEV_STATUS_CMD, struct dm_ioctl) - -#define DM_TARGET_STATUS _IOWR(DM_IOCTL, DM_TARGET_STATUS_CMD, struct dm_ioctl) -#define DM_TARGET_WAIT _IOWR(DM_IOCTL, DM_TARGET_WAIT_CMD, struct dm_ioctl) - -#define DM_VERSION_MAJOR 1 -#define DM_VERSION_MINOR 0 -#define DM_VERSION_PATCHLEVEL 6 -#define DM_VERSION_EXTRA "-ioctl (2002-10-15)" - -/* Status bits */ -#define DM_READONLY_FLAG 0x00000001 -#define DM_SUSPEND_FLAG 0x00000002 -#define DM_EXISTS_FLAG 0x00000004 -#define DM_PERSISTENT_DEV_FLAG 0x00000008 - -/* - * Flag passed into ioctl STATUS command to get table information - * rather than current status. - */ -#define DM_STATUS_TABLE_FLAG 0x00000010 - -#endif /* _LINUX_DM_IOCTL_H */ --- linux-2.6.4-rc2/include/linux/dm-ioctl-v4.h 2003-07-27 12:14:40.000000000 -0700 +++ /dev/null 2002-08-30 16:31:37.000000000 -0700 @@ -1,237 +0,0 @@ -/* - * Copyright (C) 2001 - 2003 Sistina Software (UK) Limited. - * - * This file is released under the LGPL. - */ - -#ifndef _LINUX_DM_IOCTL_V4_H -#define _LINUX_DM_IOCTL_V4_H - -#include - -#define DM_DIR "mapper" /* Slashes not supported */ -#define DM_MAX_TYPE_NAME 16 -#define DM_NAME_LEN 128 -#define DM_UUID_LEN 129 - -/* - * A traditional ioctl interface for the device mapper. - * - * Each device can have two tables associated with it, an - * 'active' table which is the one currently used by io passing - * through the device, and an 'inactive' one which is a table - * that is being prepared as a replacement for the 'active' one. - * - * DM_VERSION: - * Just get the version information for the ioctl interface. - * - * DM_REMOVE_ALL: - * Remove all dm devices, destroy all tables. Only really used - * for debug. - * - * DM_LIST_DEVICES: - * Get a list of all the dm device names. - * - * DM_DEV_CREATE: - * Create a new device, neither the 'active' or 'inactive' table - * slots will be filled. The device will be in suspended state - * after creation, however any io to the device will get errored - * since it will be out-of-bounds. - * - * DM_DEV_REMOVE: - * Remove a device, destroy any tables. - * - * DM_DEV_RENAME: - * Rename a device. - * - * DM_SUSPEND: - * This performs both suspend and resume, depending which flag is - * passed in. - * Suspend: This command will not return until all pending io to - * the device has completed. Further io will be deferred until - * the device is resumed. - * Resume: It is no longer an error to issue this command on an - * unsuspended device. If a table is present in the 'inactive' - * slot, it will be moved to the active slot, then the old table - * from the active slot will be _destroyed_. Finally the device - * is resumed. - * - * DM_DEV_STATUS: - * Retrieves the status for the table in the 'active' slot. - * - * DM_DEV_WAIT: - * Wait for a significant event to occur to the device. This - * could either be caused by an event triggered by one of the - * targets of the table in the 'active' slot, or a table change. - * - * DM_TABLE_LOAD: - * Load a table into the 'inactive' slot for the device. The - * device does _not_ need to be suspended prior to this command. - * - * DM_TABLE_CLEAR: - * Destroy any table in the 'inactive' slot (ie. abort). - * - * DM_TABLE_DEPS: - * Return a set of device dependencies for the 'active' table. - * - * DM_TABLE_STATUS: - * Return the targets status for the 'active' table. - */ - -/* - * All ioctl arguments consist of a single chunk of memory, with - * this structure at the start. If a uuid is specified any - * lookup (eg. for a DM_INFO) will be done on that, *not* the - * name. - */ -struct dm_ioctl { - /* - * The version number is made up of three parts: - * major - no backward or forward compatibility, - * minor - only backwards compatible, - * patch - both backwards and forwards compatible. - * - * All clients of the ioctl interface should fill in the - * version number of the interface that they were - * compiled with. - * - * All recognised ioctl commands (ie. those that don't - * return -ENOTTY) fill out this field, even if the - * command failed. - */ - uint32_t version[3]; /* in/out */ - uint32_t data_size; /* total size of data passed in - * including this struct */ - - uint32_t data_start; /* offset to start of data - * relative to start of this struct */ - - uint32_t target_count; /* in/out */ - int32_t open_count; /* out */ - uint32_t flags; /* in/out */ - uint32_t event_nr; /* in/out */ - uint32_t padding; - - uint64_t dev; /* in/out */ - - char name[DM_NAME_LEN]; /* device name */ - char uuid[DM_UUID_LEN]; /* unique identifier for - * the block device */ -}; - -/* - * Used to specify tables. These structures appear after the - * dm_ioctl. - */ -struct dm_target_spec { - uint64_t sector_start; - uint64_t length; - int32_t status; /* used when reading from kernel only */ - - /* - * Offset in bytes (from the start of this struct) to - * next target_spec. - */ - uint32_t next; - - char target_type[DM_MAX_TYPE_NAME]; - - /* - * Parameter string starts immediately after this object. - * Be careful to add padding after string to ensure correct - * alignment of subsequent dm_target_spec. - */ -}; - -/* - * Used to retrieve the target dependencies. - */ -struct dm_target_deps { - uint32_t count; /* Array size */ - uint32_t padding; /* unused */ - uint64_t dev[0]; /* out */ -}; - -/* - * Used to get a list of all dm devices. - */ -struct dm_name_list { - uint64_t dev; - uint32_t next; /* offset to the next record from - the _start_ of this */ - char name[0]; -}; - -/* - * If you change this make sure you make the corresponding change - * to dm-ioctl.c:lookup_ioctl() - */ -enum { - /* Top level cmds */ - DM_VERSION_CMD = 0, - DM_REMOVE_ALL_CMD, - DM_LIST_DEVICES_CMD, - - /* device level cmds */ - DM_DEV_CREATE_CMD, - DM_DEV_REMOVE_CMD, - DM_DEV_RENAME_CMD, - DM_DEV_SUSPEND_CMD, - DM_DEV_STATUS_CMD, - DM_DEV_WAIT_CMD, - - /* Table level cmds */ - DM_TABLE_LOAD_CMD, - DM_TABLE_CLEAR_CMD, - DM_TABLE_DEPS_CMD, - DM_TABLE_STATUS_CMD, -}; - -#define DM_IOCTL 0xfd - -#define DM_VERSION _IOWR(DM_IOCTL, DM_VERSION_CMD, struct dm_ioctl) -#define DM_REMOVE_ALL _IOWR(DM_IOCTL, DM_REMOVE_ALL_CMD, struct dm_ioctl) -#define DM_LIST_DEVICES _IOWR(DM_IOCTL, DM_LIST_DEVICES_CMD, struct dm_ioctl) - -#define DM_DEV_CREATE _IOWR(DM_IOCTL, DM_DEV_CREATE_CMD, struct dm_ioctl) -#define DM_DEV_REMOVE _IOWR(DM_IOCTL, DM_DEV_REMOVE_CMD, struct dm_ioctl) -#define DM_DEV_RENAME _IOWR(DM_IOCTL, DM_DEV_RENAME_CMD, struct dm_ioctl) -#define DM_DEV_SUSPEND _IOWR(DM_IOCTL, DM_DEV_SUSPEND_CMD, struct dm_ioctl) -#define DM_DEV_STATUS _IOWR(DM_IOCTL, DM_DEV_STATUS_CMD, struct dm_ioctl) -#define DM_DEV_WAIT _IOWR(DM_IOCTL, DM_DEV_WAIT_CMD, struct dm_ioctl) - -#define DM_TABLE_LOAD _IOWR(DM_IOCTL, DM_TABLE_LOAD_CMD, struct dm_ioctl) -#define DM_TABLE_CLEAR _IOWR(DM_IOCTL, DM_TABLE_CLEAR_CMD, struct dm_ioctl) -#define DM_TABLE_DEPS _IOWR(DM_IOCTL, DM_TABLE_DEPS_CMD, struct dm_ioctl) -#define DM_TABLE_STATUS _IOWR(DM_IOCTL, DM_TABLE_STATUS_CMD, struct dm_ioctl) - -#define DM_VERSION_MAJOR 4 -#define DM_VERSION_MINOR 0 -#define DM_VERSION_PATCHLEVEL 0 -#define DM_VERSION_EXTRA "-ioctl (2003-06-04)" - -/* Status bits */ -#define DM_READONLY_FLAG (1 << 0) /* In/Out */ -#define DM_SUSPEND_FLAG (1 << 1) /* In/Out */ -#define DM_PERSISTENT_DEV_FLAG (1 << 3) /* In */ - -/* - * Flag passed into ioctl STATUS command to get table information - * rather than current status. - */ -#define DM_STATUS_TABLE_FLAG (1 << 4) /* In */ - -/* - * Flags that indicate whether a table is present in either of - * the two table slots that a device has. - */ -#define DM_ACTIVE_PRESENT_FLAG (1 << 5) /* Out */ -#define DM_INACTIVE_PRESENT_FLAG (1 << 6) /* Out */ - -/* - * Indicates that the buffer passed in wasn't big enough for the - * results. - */ -#define DM_BUFFER_FULL_FLAG (1 << 8) /* Out */ - -#endif /* _LINUX_DM_IOCTL_H */ --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/include/linux/dwarf2.h 2004-03-07 20:47:02.000000000 -0800 @@ -0,0 +1,738 @@ +/* Declarations and definitions of codes relating to the DWARF2 symbolic + debugging information format. + Copyright (C) 1992, 1993, 1995, 1996, 1997, 1999, 2000, 2001, 2002 + Free Software Foundation, Inc. + + Written by Gary Funck (gary@intrepid.com) The Ada Joint Program + Office (AJPO), Florida State Unviversity and Silicon Graphics Inc. + provided support for this effort -- June 21, 1995. + + Derived from the DWARF 1 implementation written by Ron Guilmette + (rfg@netcom.com), November 1990. + + This file is part of GCC. + + GCC 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. + + GCC 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 GCC; see the file COPYING. If not, write to the Free + Software Foundation, 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +/* This file is derived from the DWARF specification (a public document) + Revision 2.0.0 (July 27, 1993) developed by the UNIX International + Programming Languages Special Interest Group (UI/PLSIG) and distributed + by UNIX International. Copies of this specification are available from + UNIX International, 20 Waterview Boulevard, Parsippany, NJ, 07054. + + This file also now contains definitions from the DWARF 3 specification. */ + +/* This file is shared between GCC and GDB, and should not contain + prototypes. */ + +#ifndef _ELF_DWARF2_H +#define _ELF_DWARF2_H + +/* Structure found in the .debug_line section. */ +#ifndef __ASSEMBLY__ +typedef struct +{ + unsigned char li_length [4]; + unsigned char li_version [2]; + unsigned char li_prologue_length [4]; + unsigned char li_min_insn_length [1]; + unsigned char li_default_is_stmt [1]; + unsigned char li_line_base [1]; + unsigned char li_line_range [1]; + unsigned char li_opcode_base [1]; +} +DWARF2_External_LineInfo; + +typedef struct +{ + unsigned long li_length; + unsigned short li_version; + unsigned int li_prologue_length; + unsigned char li_min_insn_length; + unsigned char li_default_is_stmt; + int li_line_base; + unsigned char li_line_range; + unsigned char li_opcode_base; +} +DWARF2_Internal_LineInfo; + +/* Structure found in .debug_pubnames section. */ +typedef struct +{ + unsigned char pn_length [4]; + unsigned char pn_version [2]; + unsigned char pn_offset [4]; + unsigned char pn_size [4]; +} +DWARF2_External_PubNames; + +typedef struct +{ + unsigned long pn_length; + unsigned short pn_version; + unsigned long pn_offset; + unsigned long pn_size; +} +DWARF2_Internal_PubNames; + +/* Structure found in .debug_info section. */ +typedef struct +{ + unsigned char cu_length [4]; + unsigned char cu_version [2]; + unsigned char cu_abbrev_offset [4]; + unsigned char cu_pointer_size [1]; +} +DWARF2_External_CompUnit; + +typedef struct +{ + unsigned long cu_length; + unsigned short cu_version; + unsigned long cu_abbrev_offset; + unsigned char cu_pointer_size; +} +DWARF2_Internal_CompUnit; + +typedef struct +{ + unsigned char ar_length [4]; + unsigned char ar_version [2]; + unsigned char ar_info_offset [4]; + unsigned char ar_pointer_size [1]; + unsigned char ar_segment_size [1]; +} +DWARF2_External_ARange; + +typedef struct +{ + unsigned long ar_length; + unsigned short ar_version; + unsigned long ar_info_offset; + unsigned char ar_pointer_size; + unsigned char ar_segment_size; +} +DWARF2_Internal_ARange; + +#define ENUM(name) enum name { +#define IF_NOT_ASM(a) a +#define COMMA , +#else +#define ENUM(name) +#define IF_NOT_ASM(a) +#define COMMA + +#endif + +/* Tag names and codes. */ +ENUM(dwarf_tag) + + DW_TAG_padding = 0x00 COMMA + DW_TAG_array_type = 0x01 COMMA + DW_TAG_class_type = 0x02 COMMA + DW_TAG_entry_point = 0x03 COMMA + DW_TAG_enumeration_type = 0x04 COMMA + DW_TAG_formal_parameter = 0x05 COMMA + DW_TAG_imported_declaration = 0x08 COMMA + DW_TAG_label = 0x0a COMMA + DW_TAG_lexical_block = 0x0b COMMA + DW_TAG_member = 0x0d COMMA + DW_TAG_pointer_type = 0x0f COMMA + DW_TAG_reference_type = 0x10 COMMA + DW_TAG_compile_unit = 0x11 COMMA + DW_TAG_string_type = 0x12 COMMA + DW_TAG_structure_type = 0x13 COMMA + DW_TAG_subroutine_type = 0x15 COMMA + DW_TAG_typedef = 0x16 COMMA + DW_TAG_union_type = 0x17 COMMA + DW_TAG_unspecified_parameters = 0x18 COMMA + DW_TAG_variant = 0x19 COMMA + DW_TAG_common_block = 0x1a COMMA + DW_TAG_common_inclusion = 0x1b COMMA + DW_TAG_inheritance = 0x1c COMMA + DW_TAG_inlined_subroutine = 0x1d COMMA + DW_TAG_module = 0x1e COMMA + DW_TAG_ptr_to_member_type = 0x1f COMMA + DW_TAG_set_type = 0x20 COMMA + DW_TAG_subrange_type = 0x21 COMMA + DW_TAG_with_stmt = 0x22 COMMA + DW_TAG_access_declaration = 0x23 COMMA + DW_TAG_base_type = 0x24 COMMA + DW_TAG_catch_block = 0x25 COMMA + DW_TAG_const_type = 0x26 COMMA + DW_TAG_constant = 0x27 COMMA + DW_TAG_enumerator = 0x28 COMMA + DW_TAG_file_type = 0x29 COMMA + DW_TAG_friend = 0x2a COMMA + DW_TAG_namelist = 0x2b COMMA + DW_TAG_namelist_item = 0x2c COMMA + DW_TAG_packed_type = 0x2d COMMA + DW_TAG_subprogram = 0x2e COMMA + DW_TAG_template_type_param = 0x2f COMMA + DW_TAG_template_value_param = 0x30 COMMA + DW_TAG_thrown_type = 0x31 COMMA + DW_TAG_try_block = 0x32 COMMA + DW_TAG_variant_part = 0x33 COMMA + DW_TAG_variable = 0x34 COMMA + DW_TAG_volatile_type = 0x35 COMMA + /* DWARF 3. */ + DW_TAG_dwarf_procedure = 0x36 COMMA + DW_TAG_restrict_type = 0x37 COMMA + DW_TAG_interface_type = 0x38 COMMA + DW_TAG_namespace = 0x39 COMMA + DW_TAG_imported_module = 0x3a COMMA + DW_TAG_unspecified_type = 0x3b COMMA + DW_TAG_partial_unit = 0x3c COMMA + DW_TAG_imported_unit = 0x3d COMMA + /* SGI/MIPS Extensions. */ + DW_TAG_MIPS_loop = 0x4081 COMMA + /* GNU extensions. */ + DW_TAG_format_label = 0x4101 COMMA /* For FORTRAN 77 and Fortran 90. */ + DW_TAG_function_template = 0x4102 COMMA /* For C++. */ + DW_TAG_class_template = 0x4103 COMMA /* For C++. */ + DW_TAG_GNU_BINCL = 0x4104 COMMA + DW_TAG_GNU_EINCL = 0x4105 COMMA + /* Extensions for UPC. See: http://upc.gwu.edu/~upc. */ + DW_TAG_upc_shared_type = 0x8765 COMMA + DW_TAG_upc_strict_type = 0x8766 COMMA + DW_TAG_upc_relaxed_type = 0x8767 +IF_NOT_ASM(};) + +#define DW_TAG_lo_user 0x4080 +#define DW_TAG_hi_user 0xffff + +/* Flag that tells whether entry has a child or not. */ +#define DW_children_no 0 +#define DW_children_yes 1 + +/* Form names and codes. */ +ENUM(dwarf_form) + + DW_FORM_addr = 0x01 COMMA + DW_FORM_block2 = 0x03 COMMA + DW_FORM_block4 = 0x04 COMMA + DW_FORM_data2 = 0x05 COMMA + DW_FORM_data4 = 0x06 COMMA + DW_FORM_data8 = 0x07 COMMA + DW_FORM_string = 0x08 COMMA + DW_FORM_block = 0x09 COMMA + DW_FORM_block1 = 0x0a COMMA + DW_FORM_data1 = 0x0b COMMA + DW_FORM_flag = 0x0c COMMA + DW_FORM_sdata = 0x0d COMMA + DW_FORM_strp = 0x0e COMMA + DW_FORM_udata = 0x0f COMMA + DW_FORM_ref_addr = 0x10 COMMA + DW_FORM_ref1 = 0x11 COMMA + DW_FORM_ref2 = 0x12 COMMA + DW_FORM_ref4 = 0x13 COMMA + DW_FORM_ref8 = 0x14 COMMA + DW_FORM_ref_udata = 0x15 COMMA + DW_FORM_indirect = 0x16 +IF_NOT_ASM(};) + +/* Attribute names and codes. */ + +ENUM(dwarf_attribute) + + DW_AT_sibling = 0x01 COMMA + DW_AT_location = 0x02 COMMA + DW_AT_name = 0x03 COMMA + DW_AT_ordering = 0x09 COMMA + DW_AT_subscr_data = 0x0a COMMA + DW_AT_byte_size = 0x0b COMMA + DW_AT_bit_offset = 0x0c COMMA + DW_AT_bit_size = 0x0d COMMA + DW_AT_element_list = 0x0f COMMA + DW_AT_stmt_list = 0x10 COMMA + DW_AT_low_pc = 0x11 COMMA + DW_AT_high_pc = 0x12 COMMA + DW_AT_language = 0x13 COMMA + DW_AT_member = 0x14 COMMA + DW_AT_discr = 0x15 COMMA + DW_AT_discr_value = 0x16 COMMA + DW_AT_visibility = 0x17 COMMA + DW_AT_import = 0x18 COMMA + DW_AT_string_length = 0x19 COMMA + DW_AT_common_reference = 0x1a COMMA + DW_AT_comp_dir = 0x1b COMMA + DW_AT_const_value = 0x1c COMMA + DW_AT_containing_type = 0x1d COMMA + DW_AT_default_value = 0x1e COMMA + DW_AT_inline = 0x20 COMMA + DW_AT_is_optional = 0x21 COMMA + DW_AT_lower_bound = 0x22 COMMA + DW_AT_producer = 0x25 COMMA + DW_AT_prototyped = 0x27 COMMA + DW_AT_return_addr = 0x2a COMMA + DW_AT_start_scope = 0x2c COMMA + DW_AT_stride_size = 0x2e COMMA + DW_AT_upper_bound = 0x2f COMMA + DW_AT_abstract_origin = 0x31 COMMA + DW_AT_accessibility = 0x32 COMMA + DW_AT_address_class = 0x33 COMMA + DW_AT_artificial = 0x34 COMMA + DW_AT_base_types = 0x35 COMMA + DW_AT_calling_convention = 0x36 COMMA + DW_AT_count = 0x37 COMMA + DW_AT_data_member_location = 0x38 COMMA + DW_AT_decl_column = 0x39 COMMA + DW_AT_decl_file = 0x3a COMMA + DW_AT_decl_line = 0x3b COMMA + DW_AT_declaration = 0x3c COMMA + DW_AT_discr_list = 0x3d COMMA + DW_AT_encoding = 0x3e COMMA + DW_AT_external = 0x3f COMMA + DW_AT_frame_base = 0x40 COMMA + DW_AT_friend = 0x41 COMMA + DW_AT_identifier_case = 0x42 COMMA + DW_AT_macro_info = 0x43 COMMA + DW_AT_namelist_items = 0x44 COMMA + DW_AT_priority = 0x45 COMMA + DW_AT_segment = 0x46 COMMA + DW_AT_specification = 0x47 COMMA + DW_AT_static_link = 0x48 COMMA + DW_AT_type = 0x49 COMMA + DW_AT_use_location = 0x4a COMMA + DW_AT_variable_parameter = 0x4b COMMA + DW_AT_virtuality = 0x4c COMMA + DW_AT_vtable_elem_location = 0x4d COMMA + /* DWARF 3 values. */ + DW_AT_allocated = 0x4e COMMA + DW_AT_associated = 0x4f COMMA + DW_AT_data_location = 0x50 COMMA + DW_AT_stride = 0x51 COMMA + DW_AT_entry_pc = 0x52 COMMA + DW_AT_use_UTF8 = 0x53 COMMA + DW_AT_extension = 0x54 COMMA + DW_AT_ranges = 0x55 COMMA + DW_AT_trampoline = 0x56 COMMA + DW_AT_call_column = 0x57 COMMA + DW_AT_call_file = 0x58 COMMA + DW_AT_call_line = 0x59 COMMA + /* SGI/MIPS extensions. */ + DW_AT_MIPS_fde = 0x2001 COMMA + DW_AT_MIPS_loop_begin = 0x2002 COMMA + DW_AT_MIPS_tail_loop_begin = 0x2003 COMMA + DW_AT_MIPS_epilog_begin = 0x2004 COMMA + DW_AT_MIPS_loop_unroll_factor = 0x2005 COMMA + DW_AT_MIPS_software_pipeline_depth = 0x2006 COMMA + DW_AT_MIPS_linkage_name = 0x2007 COMMA + DW_AT_MIPS_stride = 0x2008 COMMA + DW_AT_MIPS_abstract_name = 0x2009 COMMA + DW_AT_MIPS_clone_origin = 0x200a COMMA + DW_AT_MIPS_has_inlines = 0x200b COMMA + /* GNU extensions. */ + DW_AT_sf_names = 0x2101 COMMA + DW_AT_src_info = 0x2102 COMMA + DW_AT_mac_info = 0x2103 COMMA + DW_AT_src_coords = 0x2104 COMMA + DW_AT_body_begin = 0x2105 COMMA + DW_AT_body_end = 0x2106 COMMA + DW_AT_GNU_vector = 0x2107 COMMA + /* VMS extensions. */ + DW_AT_VMS_rtnbeg_pd_address = 0x2201 COMMA + /* UPC extension. */ + DW_AT_upc_threads_scaled = 0x3210 +IF_NOT_ASM(};) + +#define DW_AT_lo_user 0x2000 /* Implementation-defined range start. */ +#define DW_AT_hi_user 0x3ff0 /* Implementation-defined range end. */ + +/* Location atom names and codes. */ +ENUM(dwarf_location_atom) + + DW_OP_addr = 0x03 COMMA + DW_OP_deref = 0x06 COMMA + DW_OP_const1u = 0x08 COMMA + DW_OP_const1s = 0x09 COMMA + DW_OP_const2u = 0x0a COMMA + DW_OP_const2s = 0x0b COMMA + DW_OP_const4u = 0x0c COMMA + DW_OP_const4s = 0x0d COMMA + DW_OP_const8u = 0x0e COMMA + DW_OP_const8s = 0x0f COMMA + DW_OP_constu = 0x10 COMMA + DW_OP_consts = 0x11 COMMA + DW_OP_dup = 0x12 COMMA + DW_OP_drop = 0x13 COMMA + DW_OP_over = 0x14 COMMA + DW_OP_pick = 0x15 COMMA + DW_OP_swap = 0x16 COMMA + DW_OP_rot = 0x17 COMMA + DW_OP_xderef = 0x18 COMMA + DW_OP_abs = 0x19 COMMA + DW_OP_and = 0x1a COMMA + DW_OP_div = 0x1b COMMA + DW_OP_minus = 0x1c COMMA + DW_OP_mod = 0x1d COMMA + DW_OP_mul = 0x1e COMMA + DW_OP_neg = 0x1f COMMA + DW_OP_not = 0x20 COMMA + DW_OP_or = 0x21 COMMA + DW_OP_plus = 0x22 COMMA + DW_OP_plus_uconst = 0x23 COMMA + DW_OP_shl = 0x24 COMMA + DW_OP_shr = 0x25 COMMA + DW_OP_shra = 0x26 COMMA + DW_OP_xor = 0x27 COMMA + DW_OP_bra = 0x28 COMMA + DW_OP_eq = 0x29 COMMA + DW_OP_ge = 0x2a COMMA + DW_OP_gt = 0x2b COMMA + DW_OP_le = 0x2c COMMA + DW_OP_lt = 0x2d COMMA + DW_OP_ne = 0x2e COMMA + DW_OP_skip = 0x2f COMMA + DW_OP_lit0 = 0x30 COMMA + DW_OP_lit1 = 0x31 COMMA + DW_OP_lit2 = 0x32 COMMA + DW_OP_lit3 = 0x33 COMMA + DW_OP_lit4 = 0x34 COMMA + DW_OP_lit5 = 0x35 COMMA + DW_OP_lit6 = 0x36 COMMA + DW_OP_lit7 = 0x37 COMMA + DW_OP_lit8 = 0x38 COMMA + DW_OP_lit9 = 0x39 COMMA + DW_OP_lit10 = 0x3a COMMA + DW_OP_lit11 = 0x3b COMMA + DW_OP_lit12 = 0x3c COMMA + DW_OP_lit13 = 0x3d COMMA + DW_OP_lit14 = 0x3e COMMA + DW_OP_lit15 = 0x3f COMMA + DW_OP_lit16 = 0x40 COMMA + DW_OP_lit17 = 0x41 COMMA + DW_OP_lit18 = 0x42 COMMA + DW_OP_lit19 = 0x43 COMMA + DW_OP_lit20 = 0x44 COMMA + DW_OP_lit21 = 0x45 COMMA + DW_OP_lit22 = 0x46 COMMA + DW_OP_lit23 = 0x47 COMMA + DW_OP_lit24 = 0x48 COMMA + DW_OP_lit25 = 0x49 COMMA + DW_OP_lit26 = 0x4a COMMA + DW_OP_lit27 = 0x4b COMMA + DW_OP_lit28 = 0x4c COMMA + DW_OP_lit29 = 0x4d COMMA + DW_OP_lit30 = 0x4e COMMA + DW_OP_lit31 = 0x4f COMMA + DW_OP_reg0 = 0x50 COMMA + DW_OP_reg1 = 0x51 COMMA + DW_OP_reg2 = 0x52 COMMA + DW_OP_reg3 = 0x53 COMMA + DW_OP_reg4 = 0x54 COMMA + DW_OP_reg5 = 0x55 COMMA + DW_OP_reg6 = 0x56 COMMA + DW_OP_reg7 = 0x57 COMMA + DW_OP_reg8 = 0x58 COMMA + DW_OP_reg9 = 0x59 COMMA + DW_OP_reg10 = 0x5a COMMA + DW_OP_reg11 = 0x5b COMMA + DW_OP_reg12 = 0x5c COMMA + DW_OP_reg13 = 0x5d COMMA + DW_OP_reg14 = 0x5e COMMA + DW_OP_reg15 = 0x5f COMMA + DW_OP_reg16 = 0x60 COMMA + DW_OP_reg17 = 0x61 COMMA + DW_OP_reg18 = 0x62 COMMA + DW_OP_reg19 = 0x63 COMMA + DW_OP_reg20 = 0x64 COMMA + DW_OP_reg21 = 0x65 COMMA + DW_OP_reg22 = 0x66 COMMA + DW_OP_reg23 = 0x67 COMMA + DW_OP_reg24 = 0x68 COMMA + DW_OP_reg25 = 0x69 COMMA + DW_OP_reg26 = 0x6a COMMA + DW_OP_reg27 = 0x6b COMMA + DW_OP_reg28 = 0x6c COMMA + DW_OP_reg29 = 0x6d COMMA + DW_OP_reg30 = 0x6e COMMA + DW_OP_reg31 = 0x6f COMMA + DW_OP_breg0 = 0x70 COMMA + DW_OP_breg1 = 0x71 COMMA + DW_OP_breg2 = 0x72 COMMA + DW_OP_breg3 = 0x73 COMMA + DW_OP_breg4 = 0x74 COMMA + DW_OP_breg5 = 0x75 COMMA + DW_OP_breg6 = 0x76 COMMA + DW_OP_breg7 = 0x77 COMMA + DW_OP_breg8 = 0x78 COMMA + DW_OP_breg9 = 0x79 COMMA + DW_OP_breg10 = 0x7a COMMA + DW_OP_breg11 = 0x7b COMMA + DW_OP_breg12 = 0x7c COMMA + DW_OP_breg13 = 0x7d COMMA + DW_OP_breg14 = 0x7e COMMA + DW_OP_breg15 = 0x7f COMMA + DW_OP_breg16 = 0x80 COMMA + DW_OP_breg17 = 0x81 COMMA + DW_OP_breg18 = 0x82 COMMA + DW_OP_breg19 = 0x83 COMMA + DW_OP_breg20 = 0x84 COMMA + DW_OP_breg21 = 0x85 COMMA + DW_OP_breg22 = 0x86 COMMA + DW_OP_breg23 = 0x87 COMMA + DW_OP_breg24 = 0x88 COMMA + DW_OP_breg25 = 0x89 COMMA + DW_OP_breg26 = 0x8a COMMA + DW_OP_breg27 = 0x8b COMMA + DW_OP_breg28 = 0x8c COMMA + DW_OP_breg29 = 0x8d COMMA + DW_OP_breg30 = 0x8e COMMA + DW_OP_breg31 = 0x8f COMMA + DW_OP_regx = 0x90 COMMA + DW_OP_fbreg = 0x91 COMMA + DW_OP_bregx = 0x92 COMMA + DW_OP_piece = 0x93 COMMA + DW_OP_deref_size = 0x94 COMMA + DW_OP_xderef_size = 0x95 COMMA + DW_OP_nop = 0x96 COMMA + /* DWARF 3 extensions. */ + DW_OP_push_object_address = 0x97 COMMA + DW_OP_call2 = 0x98 COMMA + DW_OP_call4 = 0x99 COMMA + DW_OP_call_ref = 0x9a COMMA + /* GNU extensions. */ + DW_OP_GNU_push_tls_address = 0xe0 +IF_NOT_ASM(};) + +#define DW_OP_lo_user 0xe0 /* Implementation-defined range start. */ +#define DW_OP_hi_user 0xff /* Implementation-defined range end. */ + +/* Type encodings. */ +ENUM(dwarf_type) + + DW_ATE_void = 0x0 COMMA + DW_ATE_address = 0x1 COMMA + DW_ATE_boolean = 0x2 COMMA + DW_ATE_complex_float = 0x3 COMMA + DW_ATE_float = 0x4 COMMA + DW_ATE_signed = 0x5 COMMA + DW_ATE_signed_char = 0x6 COMMA + DW_ATE_unsigned = 0x7 COMMA + DW_ATE_unsigned_char = 0x8 COMMA + /* DWARF 3. */ + DW_ATE_imaginary_float = 0x9 +IF_NOT_ASM(};) + +#define DW_ATE_lo_user 0x80 +#define DW_ATE_hi_user 0xff + +/* Array ordering names and codes. */ +ENUM(dwarf_array_dim_ordering) + + DW_ORD_row_major = 0 COMMA + DW_ORD_col_major = 1 +IF_NOT_ASM(};) + +/* Access attribute. */ +ENUM(dwarf_access_attribute) + + DW_ACCESS_public = 1 COMMA + DW_ACCESS_protected = 2 COMMA + DW_ACCESS_private = 3 +IF_NOT_ASM(};) + +/* Visibility. */ +ENUM(dwarf_visibility_attribute) + + DW_VIS_local = 1 COMMA + DW_VIS_exported = 2 COMMA + DW_VIS_qualified = 3 +IF_NOT_ASM(};) + +/* Virtuality. */ +ENUM(dwarf_virtuality_attribute) + + DW_VIRTUALITY_none = 0 COMMA + DW_VIRTUALITY_virtual = 1 COMMA + DW_VIRTUALITY_pure_virtual = 2 +IF_NOT_ASM(};) + +/* Case sensitivity. */ +ENUM(dwarf_id_case) + + DW_ID_case_sensitive = 0 COMMA + DW_ID_up_case = 1 COMMA + DW_ID_down_case = 2 COMMA + DW_ID_case_insensitive = 3 +IF_NOT_ASM(};) + +/* Calling convention. */ +ENUM(dwarf_calling_convention) + + DW_CC_normal = 0x1 COMMA + DW_CC_program = 0x2 COMMA + DW_CC_nocall = 0x3 +IF_NOT_ASM(};) + +#define DW_CC_lo_user 0x40 +#define DW_CC_hi_user 0xff + +/* Inline attribute. */ +ENUM(dwarf_inline_attribute) + + DW_INL_not_inlined = 0 COMMA + DW_INL_inlined = 1 COMMA + DW_INL_declared_not_inlined = 2 COMMA + DW_INL_declared_inlined = 3 +IF_NOT_ASM(};) + +/* Discriminant lists. */ +ENUM(dwarf_discrim_list) + + DW_DSC_label = 0 COMMA + DW_DSC_range = 1 +IF_NOT_ASM(};) + +/* Line number opcodes. */ +ENUM(dwarf_line_number_ops) + + DW_LNS_extended_op = 0 COMMA + DW_LNS_copy = 1 COMMA + DW_LNS_advance_pc = 2 COMMA + DW_LNS_advance_line = 3 COMMA + DW_LNS_set_file = 4 COMMA + DW_LNS_set_column = 5 COMMA + DW_LNS_negate_stmt = 6 COMMA + DW_LNS_set_basic_block = 7 COMMA + DW_LNS_const_add_pc = 8 COMMA + DW_LNS_fixed_advance_pc = 9 COMMA + /* DWARF 3. */ + DW_LNS_set_prologue_end = 10 COMMA + DW_LNS_set_epilogue_begin = 11 COMMA + DW_LNS_set_isa = 12 +IF_NOT_ASM(};) + +/* Line number extended opcodes. */ +ENUM(dwarf_line_number_x_ops) + + DW_LNE_end_sequence = 1 COMMA + DW_LNE_set_address = 2 COMMA + DW_LNE_define_file = 3 +IF_NOT_ASM(};) + +/* Call frame information. */ +ENUM(dwarf_call_frame_info) + + DW_CFA_advance_loc = 0x40 COMMA + DW_CFA_offset = 0x80 COMMA + DW_CFA_restore = 0xc0 COMMA + DW_CFA_nop = 0x00 COMMA + DW_CFA_set_loc = 0x01 COMMA + DW_CFA_advance_loc1 = 0x02 COMMA + DW_CFA_advance_loc2 = 0x03 COMMA + DW_CFA_advance_loc4 = 0x04 COMMA + DW_CFA_offset_extended = 0x05 COMMA + DW_CFA_restore_extended = 0x06 COMMA + DW_CFA_undefined = 0x07 COMMA + DW_CFA_same_value = 0x08 COMMA + DW_CFA_register = 0x09 COMMA + DW_CFA_remember_state = 0x0a COMMA + DW_CFA_restore_state = 0x0b COMMA + DW_CFA_def_cfa = 0x0c COMMA + DW_CFA_def_cfa_register = 0x0d COMMA + DW_CFA_def_cfa_offset = 0x0e COMMA + + /* DWARF 3. */ + DW_CFA_def_cfa_expression = 0x0f COMMA + DW_CFA_expression = 0x10 COMMA + DW_CFA_offset_extended_sf = 0x11 COMMA + DW_CFA_def_cfa_sf = 0x12 COMMA + DW_CFA_def_cfa_offset_sf = 0x13 COMMA + + /* SGI/MIPS specific. */ + DW_CFA_MIPS_advance_loc8 = 0x1d COMMA + + /* GNU extensions. */ + DW_CFA_GNU_window_save = 0x2d COMMA + DW_CFA_GNU_args_size = 0x2e COMMA + DW_CFA_GNU_negative_offset_extended = 0x2f +IF_NOT_ASM(};) + +#define DW_CIE_ID 0xffffffff +#define DW_CIE_VERSION 1 + +#define DW_CFA_extended 0 +#define DW_CFA_lo_user 0x1c +#define DW_CFA_hi_user 0x3f + +#define DW_CHILDREN_no 0x00 +#define DW_CHILDREN_yes 0x01 + +#define DW_ADDR_none 0 + +/* Source language names and codes. */ +ENUM(dwarf_source_language) + + DW_LANG_C89 = 0x0001 COMMA + DW_LANG_C = 0x0002 COMMA + DW_LANG_Ada83 = 0x0003 COMMA + DW_LANG_C_plus_plus = 0x0004 COMMA + DW_LANG_Cobol74 = 0x0005 COMMA + DW_LANG_Cobol85 = 0x0006 COMMA + DW_LANG_Fortran77 = 0x0007 COMMA + DW_LANG_Fortran90 = 0x0008 COMMA + DW_LANG_Pascal83 = 0x0009 COMMA + DW_LANG_Modula2 = 0x000a COMMA + DW_LANG_Java = 0x000b COMMA + /* DWARF 3. */ + DW_LANG_C99 = 0x000c COMMA + DW_LANG_Ada95 = 0x000d COMMA + DW_LANG_Fortran95 = 0x000e COMMA + /* MIPS. */ + DW_LANG_Mips_Assembler = 0x8001 COMMA + /* UPC. */ + DW_LANG_Upc = 0x8765 +IF_NOT_ASM(};) + +#define DW_LANG_lo_user 0x8000 /* Implementation-defined range start. */ +#define DW_LANG_hi_user 0xffff /* Implementation-defined range start. */ + +/* Names and codes for macro information. */ +ENUM(dwarf_macinfo_record_type) + + DW_MACINFO_define = 1 COMMA + DW_MACINFO_undef = 2 COMMA + DW_MACINFO_start_file = 3 COMMA + DW_MACINFO_end_file = 4 COMMA + DW_MACINFO_vendor_ext = 255 +IF_NOT_ASM(};) + +/* @@@ For use with GNU frame unwind information. */ + +#define DW_EH_PE_absptr 0x00 +#define DW_EH_PE_omit 0xff + +#define DW_EH_PE_uleb128 0x01 +#define DW_EH_PE_udata2 0x02 +#define DW_EH_PE_udata4 0x03 +#define DW_EH_PE_udata8 0x04 +#define DW_EH_PE_sleb128 0x09 +#define DW_EH_PE_sdata2 0x0A +#define DW_EH_PE_sdata4 0x0B +#define DW_EH_PE_sdata8 0x0C +#define DW_EH_PE_signed 0x08 + +#define DW_EH_PE_pcrel 0x10 +#define DW_EH_PE_textrel 0x20 +#define DW_EH_PE_datarel 0x30 +#define DW_EH_PE_funcrel 0x40 +#define DW_EH_PE_aligned 0x50 + +#define DW_EH_PE_indirect 0x80 + +#endif /* _ELF_DWARF2_H */ --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/include/linux/dwarf2-lang.h 2004-03-07 20:47:02.000000000 -0800 @@ -0,0 +1,132 @@ +#ifndef DWARF2_LANG +#define DWARF2_LANG +#include + +/* + * This 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 file defines macros that allow generation of DWARF debug records + * for asm files. This file is platform independent. Register numbers + * (which are about the only thing that is platform dependent) are to be + * supplied by a platform defined file. + */ +#define DWARF_preamble() .section .debug_frame,"",@progbits +/* + * This macro starts a debug frame section. The debug_frame describes + * where to find the registers that the enclosing function saved on + * entry. + * + * ORD is use by the label generator and should be the same as what is + * passed to CFI_postamble. + * + * pc, pc register gdb ordinal. + * + * code_align this is the factor used to define locations or regions + * where the given definitions apply. If you use labels to define these + * this should be 1. + * + * data_align this is the factor used to define register offsets. If + * you use struct offset, this should be the size of the register in + * bytes or the negative of that. This is how it is used: you will + * define a register as the reference register, say the stack pointer, + * then you will say where a register is located relative to this + * reference registers value, say 40 for register 3 (the gdb register + * number). The <40> will be multiplied by to define the + * byte offset of the given register (3, in this example). So if your + * <40> is the byte offset and the reference register points at the + * begining, you would want 1 for the data_offset. If <40> was the 40th + * 4-byte element in that structure you would want 4. And if your + * reference register points at the end of the structure you would want + * a negative data_align value(and you would have to do other math as + * well). + */ + +#define CFI_preamble(ORD, pc, code_align, data_align) \ +.section .debug_frame,"",@progbits ; \ +frame/**/_/**/ORD: \ + .long end/**/_/**/ORD-start/**/_/**/ORD; \ +start/**/_/**/ORD: \ + .long DW_CIE_ID; \ + .byte DW_CIE_VERSION; \ + .byte 0 ; \ + .uleb128 code_align; \ + .sleb128 data_align; \ + .byte pc; + +/* + * After the above macro and prior to the CFI_postamble, you need to + * define the initial state. This starts with defining the reference + * register and, usually the pc. Here are some helper macros: + */ + +#define CFA_define_reference(reg, offset) \ + .byte DW_CFA_def_cfa; \ + .uleb128 reg; \ + .uleb128 (offset); + +#define CFA_define_offset(reg, offset) \ + .byte (DW_CFA_offset + reg); \ + .uleb128 (offset); + +#define CFI_postamble(ORD) \ + .align 4; \ +end/**/_/**/ORD: +/* + * So now your code pushs stuff on the stack, you need a new location + * and the rules for what to do. This starts a running description of + * the call frame. You need to describe what changes with respect to + * the call registers as the location of the pc moves through the code. + * The following builds an FDE (fram descriptor entry?). Like the + * above, it has a preamble and a postamble. It also is tied to the CFI + * above. + * The first entry after the preamble must be the location in the code + * that the call frame is being described for. + */ +#define FDE_preamble(ORD, fde_no, initial_address, length) \ + .long FDE_end/**/_/**/fde_no-FDE_start/**/_/**/fde_no; \ +FDE_start/**/_/**/fde_no: \ + .long frame/**/_/**/ORD; \ + .long initial_address; \ + .long length; + +#define FDE_postamble(fde_no) \ + .align 4; \ +FDE_end/**/_/**/fde_no: +/* + * That done, you can now add registers, subtract registers, move the + * reference and even change the reference. You can also define a new + * area of code the info applies to. For discontinuous bits you should + * start a new FDE. You may have as many as you like. + */ + +/* + * To advance the address by + */ + +#define FDE_advance(bytes) \ + .byte DW_CFA_advance_loc4 \ + .long bytes + + + +/* + * With the above you can define all the register locations. But + * suppose the reference register moves... Takes the new offset NOT an + * increment. This is how esp is tracked if it is not saved. + */ + +#define CFA_define_cfa_offset(offset) \ + .byte $DW_CFA_def_cfa_offset; \ + .uleb128 (offset); +/* + * Or suppose you want to use a different reference register... + */ +#define CFA_define_cfa_register(reg) \ + .byte DW_CFA_def_cfa_register; \ + .uleb128 reg; + +#endif --- linux-2.6.4-rc2/include/linux/elevator.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/linux/elevator.h 2004-03-07 20:47:10.000000000 -0800 @@ -94,6 +94,11 @@ extern elevator_t iosched_deadline; */ extern elevator_t iosched_as; +/* + * completely fair queueing I/O scheduler + */ +extern elevator_t iosched_cfq; + extern int elevator_init(request_queue_t *, elevator_t *); extern void elevator_exit(request_queue_t *); extern int elv_rq_merge_ok(struct request *, struct bio *); --- linux-2.6.4-rc2/include/linux/ext3_fs_sb.h 2004-02-03 20:42:38.000000000 -0800 +++ 25/include/linux/ext3_fs_sb.h 2004-03-07 20:47:09.000000000 -0800 @@ -69,6 +69,10 @@ struct ext3_sb_info { struct timer_list turn_ro_timer; /* For turning read-only (crash simulation) */ wait_queue_head_t ro_wait_queue; /* For people waiting for the fs to go read-only */ #endif +#ifdef CONFIG_QUOTA + char *s_qf_names[MAXQUOTAS]; /* Names of quota files with journalled quota */ + int s_jquota_fmt; /* Format of quota to use */ +#endif }; #endif /* _LINUX_EXT3_FS_SB */ --- linux-2.6.4-rc2/include/linux/ext3_jbd.h 2004-02-03 20:42:38.000000000 -0800 +++ 25/include/linux/ext3_jbd.h 2004-03-07 20:47:09.000000000 -0800 @@ -42,8 +42,9 @@ * superblock only gets updated once, of course, so don't bother * counting that again for the quota updates. */ -#define EXT3_DATA_TRANS_BLOCKS (3 * EXT3_SINGLEDATA_TRANS_BLOCKS + \ - EXT3_XATTR_TRANS_BLOCKS - 2) +#define EXT3_DATA_TRANS_BLOCKS (EXT3_SINGLEDATA_TRANS_BLOCKS + \ + EXT3_XATTR_TRANS_BLOCKS - 2 + \ + 2*EXT3_QUOTA_TRANS_BLOCKS) extern int ext3_writepage_trans_blocks(struct inode *inode); @@ -72,6 +73,19 @@ extern int ext3_writepage_trans_blocks(s #define EXT3_INDEX_EXTRA_TRANS_BLOCKS 8 +#ifdef CONFIG_QUOTA +/* Amount of blocks needed for quota update - we know that the structure was + * allocated so we need to update only inode+data */ +#define EXT3_QUOTA_TRANS_BLOCKS 2 +/* Amount of blocks needed for quota insert/delete - we do some block writes + * but inode, sb and group updates are done only once */ +#define EXT3_QUOTA_INIT_BLOCKS (DQUOT_MAX_WRITES*\ + (EXT3_SINGLEDATA_TRANS_BLOCKS-3)+3) +#else +#define EXT3_QUOTA_TRANS_BLOCKS 0 +#define EXT3_QUOTA_INIT_BLOCKS 0 +#endif + int ext3_mark_iloc_dirty(handle_t *handle, struct inode *inode, --- linux-2.6.4-rc2/include/linux/fs.h 2004-03-03 23:12:48.000000000 -0800 +++ 25/include/linux/fs.h 2004-03-07 20:48:23.000000000 -0800 @@ -89,6 +89,7 @@ extern int leases_enable, dir_notify_ena /* public flags for file_system_type */ #define FS_REQUIRES_DEV 1 +#define FS_BINARY_MOUNTDATA 2 #define FS_REVAL_DOT 16384 /* Check the paths ".", ".." for staleness */ #define FS_ODD_RENAME 32768 /* Temporary stuff; will go away as soon * as nfs_rename() will be cleaned up @@ -137,6 +138,7 @@ extern int leases_enable, dir_notify_ena #define S_DEAD 32 /* removed, but still open directory */ #define S_NOQUOTA 64 /* Inode is not counted to quota */ #define S_DIRSYNC 128 /* Directory modifications are synchronous */ +#define S_NOCMTIME 256 /* Do not update file c/mtime */ /* * Note that nosuid etc flags are inode-specific: setting some file-system @@ -170,6 +172,7 @@ extern int leases_enable, dir_notify_ena #define IS_ONE_SECOND(inode) __IS_FLG(inode, MS_ONE_SECOND) #define IS_DEADDIR(inode) ((inode)->i_flags & S_DEAD) +#define IS_NOCMTIME(inode) ((inode)->i_flags & S_NOCMTIME) /* the read-only stuff doesn't really belong here, but any other place is probably as bad and I don't want to create yet another include file. */ @@ -336,6 +339,7 @@ struct address_space { spinlock_t private_lock; /* for use by the address_space */ struct list_head private_list; /* ditto */ struct address_space *assoc_mapping; /* ditto */ + struct rw_semaphore wb_rwsema; /* serialize SYNC writebacks */ }; struct block_device { @@ -376,6 +380,7 @@ struct block_device { struct inode { struct hlist_node i_hash; struct list_head i_list; + struct list_head i_sb_list; struct list_head i_dentry; unsigned long i_ino; atomic_t i_count; @@ -395,6 +400,7 @@ struct inode { unsigned short i_bytes; spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */ struct semaphore i_sem; + struct rw_semaphore i_alloc_sem; struct inode_operations *i_op; struct file_operations *i_fop; /* former ->i_op->default_file_ops */ struct super_block *i_sb; @@ -507,6 +513,8 @@ struct file_ra_state { unsigned long prev_page; /* Cache last read() position */ unsigned long ahead_start; /* Ahead window */ unsigned long ahead_size; + unsigned long serial_cnt; /* measure of sequentiality */ + unsigned long average; /* another measure of sequentiality */ unsigned long ra_pages; /* Maximum readahead window */ unsigned long mmap_hit; /* Cache hit stat for mmap accesses */ unsigned long mmap_miss; /* Cache miss stat for mmap accesses */ @@ -700,6 +708,7 @@ struct super_block { atomic_t s_active; void *s_security; + struct list_head s_inodes; /* all inodes */ struct list_head s_dirty; /* dirty inodes */ struct list_head s_io; /* parked for writeback */ struct hlist_head s_anon; /* anonymous dentries for (nfs) exporting */ @@ -1133,6 +1142,7 @@ extern void vfs_caches_init(unsigned lon extern int register_blkdev(unsigned int, const char *); extern int unregister_blkdev(unsigned int, const char *); extern struct block_device *bdget(dev_t); +extern void bd_set_size(struct block_device *, loff_t size); extern void bd_forget(struct inode *inode); extern void bdput(struct block_device *); extern int blkdev_open(struct inode *, struct file *); @@ -1210,6 +1220,7 @@ extern void write_inode_now(struct inode extern int filemap_fdatawrite(struct address_space *); extern int filemap_flush(struct address_space *); extern int filemap_fdatawait(struct address_space *); +extern int filemap_write_and_wait(struct address_space *mapping); extern void sync_supers(void); extern void sync_filesystems(int wait); extern void emergency_sync(void); @@ -1321,9 +1332,6 @@ extern void file_ra_state_init(struct file_ra_state *ra, struct address_space *mapping); extern ssize_t generic_file_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, loff_t offset, unsigned long nr_segs); -extern int blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode, - struct block_device *bdev, const struct iovec *iov, loff_t offset, - unsigned long nr_segs, get_blocks_t *get_blocks, dio_iodone_t *end_io); extern ssize_t generic_file_readv(struct file *filp, const struct iovec *iov, unsigned long nr_segs, loff_t *ppos); ssize_t generic_file_writev(struct file *filp, const struct iovec *iov, @@ -1345,6 +1353,32 @@ static inline void do_generic_file_read( actor); } +int __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode, + struct block_device *bdev, const struct iovec *iov, loff_t offset, + unsigned long nr_segs, get_blocks_t get_blocks, dio_iodone_t end_io, + int needs_special_locking); + +/* + * For filesystems which need locking between buffered and direct access + */ +static inline int blockdev_direct_IO(int rw, struct kiocb *iocb, + struct inode *inode, struct block_device *bdev, const struct iovec *iov, + loff_t offset, unsigned long nr_segs, get_blocks_t get_blocks, + dio_iodone_t end_io) +{ + return __blockdev_direct_IO(rw, iocb, inode, bdev, iov, offset, + nr_segs, get_blocks, end_io, 1); +} + +static inline int blockdev_direct_IO_no_locking(int rw, struct kiocb *iocb, + struct inode *inode, struct block_device *bdev, const struct iovec *iov, + loff_t offset, unsigned long nr_segs, get_blocks_t get_blocks, + dio_iodone_t end_io) +{ + return __blockdev_direct_IO(rw, iocb, inode, bdev, iov, offset, + nr_segs, get_blocks, end_io, 0); +} + extern struct file_operations generic_ro_fops; #define special_file(m) (S_ISCHR(m)||S_ISBLK(m)||S_ISFIFO(m)||S_ISSOCK(m)) --- linux-2.6.4-rc2/include/linux/hiddev.h 2003-09-08 13:58:59.000000000 -0700 +++ 25/include/linux/hiddev.h 2004-03-07 20:46:53.000000000 -0800 @@ -39,33 +39,33 @@ struct hiddev_event { }; struct hiddev_devinfo { - unsigned int bustype; - unsigned int busnum; - unsigned int devnum; - unsigned int ifnum; - short vendor; - short product; - short version; - unsigned num_applications; + __u32 bustype; + __u32 busnum; + __u32 devnum; + __u32 ifnum; + __s16 vendor; + __s16 product; + __s16 version; + __u32 num_applications; }; struct hiddev_collection_info { - unsigned index; - unsigned type; - unsigned usage; - unsigned level; + __u32 index; + __u32 type; + __u32 usage; + __u32 level; }; #define HID_STRING_SIZE 256 struct hiddev_string_descriptor { - int index; + __s32 index; char value[HID_STRING_SIZE]; }; struct hiddev_report_info { - unsigned report_type; - unsigned report_id; - unsigned num_fields; + __u32 report_type; + __u32 report_id; + __u32 num_fields; }; /* To do a GUSAGE/SUSAGE, fill in at least usage_code, report_type and @@ -88,20 +88,20 @@ struct hiddev_report_info { #define HID_REPORT_TYPE_MAX 3 struct hiddev_field_info { - unsigned report_type; - unsigned report_id; - unsigned field_index; - unsigned maxusage; - unsigned flags; - unsigned physical; /* physical usage for this field */ - unsigned logical; /* logical usage for this field */ - unsigned application; /* application usage for this field */ + __u32 report_type; + __u32 report_id; + __u32 field_index; + __u32 maxusage; + __u32 flags; + __u32 physical; /* physical usage for this field */ + __u32 logical; /* logical usage for this field */ + __u32 application; /* application usage for this field */ __s32 logical_minimum; __s32 logical_maximum; __s32 physical_minimum; __s32 physical_maximum; - unsigned unit_exponent; - unsigned unit; + __u32 unit_exponent; + __u32 unit; }; /* Fill in report_type, report_id and field_index to get the information on a @@ -118,14 +118,22 @@ struct hiddev_field_info { #define HID_FIELD_BUFFERED_BYTE 0x100 struct hiddev_usage_ref { - unsigned report_type; - unsigned report_id; - unsigned field_index; - unsigned usage_index; - unsigned usage_code; + __u32 report_type; + __u32 report_id; + __u32 field_index; + __u32 usage_index; + __u32 usage_code; __s32 value; }; +/* hiddev_usage_ref_multi is used for sending multiple bytes to a control. + * It really manifests itself as setting the value of consecutive usages */ +struct hiddev_usage_ref_multi { + struct hiddev_usage_ref uref; + __u32 num_values; + __s32 values[HID_MAX_USAGES]; +}; + /* FIELD_INDEX_NONE is returned in read() data from the kernel when flags * is set to (HIDDEV_FLAG_UREF | HIDDEV_FLAG_REPORT) and a new report has * been sent by the device @@ -161,6 +169,10 @@ struct hiddev_usage_ref { #define HIDIOCGCOLLECTIONINFO _IOWR('H', 0x11, struct hiddev_collection_info) #define HIDIOCGPHYS(len) _IOC(_IOC_READ, 'H', 0x12, len) +/* For writing/reading to multiple/consecutive usages */ +#define HIDIOCGUSAGES _IOWR('H', 0x13, struct hiddev_usage_ref_multi) +#define HIDIOCSUSAGES _IOW('H', 0x14, struct hiddev_usage_ref_multi) + /* * Flags to be used in HIDIOCSFLAG */ --- linux-2.6.4-rc2/include/linux/i2o-dev.h 2003-06-14 12:17:58.000000000 -0700 +++ 25/include/linux/i2o-dev.h 2004-03-07 20:46:46.000000000 -0800 @@ -182,7 +182,7 @@ typedef struct _i2o_hrt_entry { u32 adapter_id; u32 parent_tid:12; - u32 tate:4; + u32 state:4; u32 bus_num:8; u32 bus_type:8; union --- linux-2.6.4-rc2/include/linux/i2o.h 2003-06-14 12:18:03.000000000 -0700 +++ 25/include/linux/i2o.h 2004-03-07 20:46:46.000000000 -0800 @@ -544,6 +544,25 @@ extern int i2o_delete_controller(struct #define I2O_DSC_DEVICE_BUSY 0x001B #define I2O_DSC_DEVICE_NOT_AVAILABLE 0x001C +/* DetailedStatusCode defines for Block Storage Operation: Table 6-7 Detailed + Status Codes.*/ + +#define I2O_BSA_DSC_SUCCESS 0x0000 +#define I2O_BSA_DSC_MEDIA_ERROR 0x0001 +#define I2O_BSA_DSC_ACCESS_ERROR 0x0002 +#define I2O_BSA_DSC_DEVICE_FAILURE 0x0003 +#define I2O_BSA_DSC_DEVICE_NOT_READY 0x0004 +#define I2O_BSA_DSC_MEDIA_NOT_PRESENT 0x0005 +#define I2O_BSA_DSC_MEDIA_LOCKED 0x0006 +#define I2O_BSA_DSC_MEDIA_FAILURE 0x0007 +#define I2O_BSA_DSC_PROTOCOL_FAILURE 0x0008 +#define I2O_BSA_DSC_BUS_FAILURE 0x0009 +#define I2O_BSA_DSC_ACCESS_VIOLATION 0x000A +#define I2O_BSA_DSC_WRITE_PROTECTED 0x000B +#define I2O_BSA_DSC_DEVICE_RESET 0x000C +#define I2O_BSA_DSC_VOLUME_CHANGED 0x000D +#define I2O_BSA_DSC_TIMEOUT 0x000E + /* FailureStatusCodes, Table 3-3 Message Failure Codes */ #define I2O_FSC_TRANSPORT_SERVICE_SUSPENDED 0x81 --- linux-2.6.4-rc2/include/linux/ide.h 2004-03-03 23:12:48.000000000 -0800 +++ 25/include/linux/ide.h 2004-03-07 20:48:00.000000000 -0800 @@ -143,17 +143,8 @@ typedef unsigned char byte; /* used ever #define IDE_FEATURE_OFFSET IDE_ERROR_OFFSET #define IDE_COMMAND_OFFSET IDE_STATUS_OFFSET -#define IDE_DATA_OFFSET_HOB (0) -#define IDE_ERROR_OFFSET_HOB (1) -#define IDE_NSECTOR_OFFSET_HOB (2) -#define IDE_SECTOR_OFFSET_HOB (3) -#define IDE_LCYL_OFFSET_HOB (4) -#define IDE_HCYL_OFFSET_HOB (5) -#define IDE_SELECT_OFFSET_HOB (6) #define IDE_CONTROL_OFFSET_HOB (7) -#define IDE_FEATURE_OFFSET_HOB IDE_ERROR_OFFSET_HOB - #define IDE_DATA_REG (HWIF(drive)->io_ports[IDE_DATA_OFFSET]) #define IDE_ERROR_REG (HWIF(drive)->io_ports[IDE_ERROR_OFFSET]) #define IDE_NSECTOR_REG (HWIF(drive)->io_ports[IDE_NSECTOR_OFFSET]) @@ -165,16 +156,6 @@ typedef unsigned char byte; /* used ever #define IDE_CONTROL_REG (HWIF(drive)->io_ports[IDE_CONTROL_OFFSET]) #define IDE_IRQ_REG (HWIF(drive)->io_ports[IDE_IRQ_OFFSET]) -#define IDE_DATA_REG_HOB (HWIF(drive)->io_ports[IDE_DATA_OFFSET]) -#define IDE_ERROR_REG_HOB (HWIF(drive)->io_ports[IDE_ERROR_OFFSET]) -#define IDE_NSECTOR_REG_HOB (HWIF(drive)->io_ports[IDE_NSECTOR_OFFSET]) -#define IDE_SECTOR_REG_HOB (HWIF(drive)->io_ports[IDE_SECTOR_OFFSET]) -#define IDE_LCYL_REG_HOB (HWIF(drive)->io_ports[IDE_LCYL_OFFSET]) -#define IDE_HCYL_REG_HOB (HWIF(drive)->io_ports[IDE_HCYL_OFFSET]) -#define IDE_SELECT_REG_HOB (HWIF(drive)->io_ports[IDE_SELECT_OFFSET]) -#define IDE_STATUS_REG_HOB (HWIF(drive)->io_ports[IDE_STATUS_OFFSET]) -#define IDE_CONTROL_REG_HOB (HWIF(drive)->io_ports[IDE_CONTROL_OFFSET]) - #define IDE_FEATURE_REG IDE_ERROR_REG #define IDE_COMMAND_REG IDE_STATUS_REG #define IDE_ALTSTATUS_REG IDE_CONTROL_REG @@ -991,6 +972,7 @@ typedef struct hwif_s { unsigned dma; void (*led_act)(void *data, int rw); + unsigned int (*max_rqsize)(ide_drive_t *); } ide_hwif_t; /* @@ -998,7 +980,6 @@ typedef struct hwif_s { */ typedef ide_startstop_t (ide_pre_handler_t)(ide_drive_t *, struct request *); typedef ide_startstop_t (ide_handler_t)(ide_drive_t *); -typedef ide_startstop_t (ide_post_handler_t)(ide_drive_t *); typedef int (ide_expiry_t)(ide_drive_t *); typedef struct hwgroup_s { @@ -1360,7 +1341,6 @@ typedef struct ide_task_s { int command_type; ide_pre_handler_t *prehandler; ide_handler_t *handler; - ide_post_handler_t *posthandler; struct request *rq; /* copy of request */ void *special; /* valid_t generally */ } ide_task_t; @@ -1459,15 +1439,6 @@ extern void ide_init_drive_taskfile(stru extern int ide_raw_taskfile(ide_drive_t *, ide_task_t *, u8 *); -extern ide_pre_handler_t * ide_pre_handler_parser(struct hd_drive_task_hdr *, struct hd_drive_hob_hdr *); - -extern ide_handler_t * ide_handler_parser(struct hd_drive_task_hdr *, struct hd_drive_hob_hdr *); - -extern ide_post_handler_t * ide_post_handler_parser(struct hd_drive_task_hdr *, struct hd_drive_hob_hdr *); - -/* Expects args is a full set of TF registers and parses the command type */ -extern int ide_cmd_type_parser(ide_task_t *); - int ide_taskfile_ioctl(ide_drive_t *, unsigned int, unsigned long); int ide_cmd_ioctl(ide_drive_t *, unsigned int, unsigned long); int ide_task_ioctl(ide_drive_t *, unsigned int, unsigned long); @@ -1643,6 +1614,7 @@ extern int ide_dma_enable(ide_drive_t *d extern char *ide_xfer_verbose(u8 xfer_rate); extern void ide_toggle_bounce(ide_drive_t *drive, int on); extern int ide_set_xfer_rate(ide_drive_t *drive, u8 rate); +extern byte ide_dump_atapi_status(ide_drive_t *drive, const char *msg, byte stat); typedef struct ide_pio_timings_s { int setup_time; /* Address setup (ns) minimum */ --- linux-2.6.4-rc2/include/linux/init.h 2003-11-23 19:03:02.000000000 -0800 +++ 25/include/linux/init.h 2004-03-07 20:46:53.000000000 -0800 @@ -110,12 +110,21 @@ struct obs_kernel_param { }; /* OBSOLETE: see moduleparam.h for the right way. */ -#define __setup(str, fn) \ - static char __setup_str_##fn[] __initdata = str; \ - static struct obs_kernel_param __setup_##fn \ +#define __setup_param(str, unique_id, fn) \ + static char __setup_str_##unique_id[] __initdata = str; \ + static struct obs_kernel_param __setup_##unique_id \ __attribute_used__ \ __attribute__((__section__(".init.setup"))) \ - = { __setup_str_##fn, fn } + = { __setup_str_##unique_id, fn } + +#define __setup_null_param(str, unique_id) \ + __setup_param(str, unique_id, NULL) + +#define __setup(str, fn) \ + __setup_param(str, fn, fn) + +#define __obsolete_setup(str) \ + __setup_null_param(str, __LINE__) #endif /* __ASSEMBLY__ */ @@ -172,7 +181,10 @@ struct obs_kernel_param { { return exitfn; } \ void cleanup_module(void) __attribute__((alias(#exitfn))); -#define __setup(str,func) /* nothing */ +#define __setup_param(str, unique_id, fn) /* nothing */ +#define __setup_null_param(str, unique_id) /* nothing */ +#define __setup(str, func) /* nothing */ +#define __obsolete_setup(str) /* nothing */ #endif /* Data marked not to be saved by software_suspend() */ --- linux-2.6.4-rc2/include/linux/interrupt.h 2003-09-27 18:57:47.000000000 -0700 +++ 25/include/linux/interrupt.h 2004-03-07 20:47:23.000000000 -0800 @@ -211,6 +211,7 @@ static inline void tasklet_hi_enable(str } extern void tasklet_kill(struct tasklet_struct *t); +extern void tasklet_kill_immediate(struct tasklet_struct *t, unsigned int cpu); extern void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data); --- linux-2.6.4-rc2/include/linux/ipmi.h 2003-06-14 12:18:51.000000000 -0700 +++ 25/include/linux/ipmi.h 2004-03-07 20:48:02.000000000 -0800 @@ -109,6 +109,35 @@ struct ipmi_ipmb_addr unsigned char lun; }; +/* + * A LAN Address. This is an address to/from a LAN interface bridged + * by the BMC, not an address actually out on the LAN. + * + * A concious decision was made here to deviate slightly from the IPMI + * spec. We do not use rqSWID and rsSWID like it shows in the + * message. Instead, we use remote_SWID and local_SWID. This means + * that any message (a request or response) from another device will + * always have exactly the same address. If you didn't do this, + * requests and responses from the same device would have different + * addresses, and that's not too cool. + * + * In this address, the remote_SWID is always the SWID the remote + * message came from, or the SWID we are sending the message to. + * local_SWID is always our SWID. Note that having our SWID in the + * message is a little wierd, but this is required. + */ +#define IPMI_LAN_ADDR_TYPE 0x04 +struct ipmi_lan_addr +{ + int addr_type; + short channel; + unsigned char privilege; + unsigned char session_handle; + unsigned char remote_SWID; + unsigned char local_SWID; + unsigned char lun; +}; + /* * Channel for talking directly with the BMC. When using this @@ -145,10 +174,20 @@ struct ipmi_msg * Receive types for messages coming from the receive interface. This * is used for the receive in-kernel interface and in the receive * IOCTL. + * + * The "IPMI_RESPONSE_RESPNOSE_TYPE" is a little strange sounding, but + * it allows you to get the message results when you send a response + * message. */ #define IPMI_RESPONSE_RECV_TYPE 1 /* A response to a command */ #define IPMI_ASYNC_EVENT_RECV_TYPE 2 /* Something from the event queue */ #define IPMI_CMD_RECV_TYPE 3 /* A command from somewhere else */ +#define IPMI_RESPONSE_RESPONSE_TYPE 4 /* The response for + a sent response, giving any + error status for sending the + response. When you send a + response message, this will + be returned. */ /* Note that async events and received commands do not have a completion code as the first byte of the incoming data, unlike a response. */ @@ -160,6 +199,7 @@ struct ipmi_msg * The in-kernel interface. */ #include +#include /* Opaque type for a IPMI message user. One of these is needed to send and receive messages. */ @@ -185,6 +225,12 @@ struct ipmi_recv_msg long msgid; struct ipmi_msg msg; + /* The user_msg_data is the data supplied when a message was + sent, if this is a response to a sent message. If this is + not a response to a sent message, then user_msg_data will + be NULL. */ + void *user_msg_data; + /* Call this when done with the message. It will presumably free the message and do any other necessary cleanup. */ void (*done)(struct ipmi_recv_msg *msg); @@ -206,9 +252,10 @@ struct ipmi_user_hndl /* Routine type to call when a message needs to be routed to the upper layer. This will be called with some locks held, the only IPMI routines that can be called are ipmi_request - and the alloc/free operations. */ + and the alloc/free operations. The handler_data is the + variable supplied when the receive handler was registered. */ void (*ipmi_recv_hndl)(struct ipmi_recv_msg *msg, - void *handler_data); + void *user_msg_data); /* Called when the interface detects a watchdog pre-timeout. If this is NULL, it will be ignored for the user. */ @@ -221,7 +268,12 @@ int ipmi_create_user(unsigned int void *handler_data, ipmi_user_t *user); -/* Destroy the given user of the IPMI layer. */ +/* Destroy the given user of the IPMI layer. Note that after this + function returns, the system is guaranteed to not call any + callbacks for the user. Thus as long as you destroy all the users + before you unload a module, you will be safe. And if you destroy + the users before you destroy the callback structures, it should be + safe, too. */ int ipmi_destroy_user(ipmi_user_t user); /* Get the IPMI version of the BMC we are talking to. */ @@ -253,20 +305,51 @@ unsigned char ipmi_get_my_LUN(ipmi_user_ * in the msgid field of the received command. If the priority is > * 0, the message will go into a high-priority queue and be sent * first. Otherwise, it goes into a normal-priority queue. + * The user_msg_data field will be returned in any response to this + * message. + * + * Note that if you send a response (with the netfn lower bit set), + * you *will* get back a SEND_MSG response telling you what happened + * when the response was sent. You will not get back a response to + * the message itself. */ int ipmi_request(ipmi_user_t user, struct ipmi_addr *addr, long msgid, struct ipmi_msg *msg, + void *user_msg_data, int priority); /* + * Like ipmi_request, but lets you specify the number of retries and + * the retry time. The retries is the number of times the message + * will be resent if no reply is received. If set to -1, the default + * value will be used. The retry time is the time in milliseconds + * between retries. If set to zero, the default value will be + * used. + * + * Don't use this unless you *really* have to. It's primarily for the + * IPMI over LAN converter; since the LAN stuff does its own retries, + * it makes no sense to do it here. However, this can be used if you + * have unusual requirements. + */ +int ipmi_request_settime(ipmi_user_t user, + struct ipmi_addr *addr, + long msgid, + struct ipmi_msg *msg, + void *user_msg_data, + int priority, + int max_retries, + unsigned int retry_time_ms); + +/* * Like ipmi_request, but lets you specify the slave return address. */ int ipmi_request_with_source(ipmi_user_t user, struct ipmi_addr *addr, long msgid, struct ipmi_msg *msg, + void *user_msg_data, int priority, unsigned char source_address, unsigned char source_lun); @@ -284,6 +367,7 @@ int ipmi_request_supply_msgs(ipmi_user_t struct ipmi_addr *addr, long msgid, struct ipmi_msg *msg, + void *user_msg_data, void *supplied_smi, struct ipmi_recv_msg *supplied_recv, int priority); @@ -331,6 +415,10 @@ struct ipmi_smi_watcher { struct list_head link; + /* You must set the owner to the current module, if you are in + a module (generally just set it to "THIS_MODULE"). */ + struct module *owner; + /* These two are called with read locks held for the interface the watcher list. So you can add and remove users from the IPMI interface, send messages, etc., but you cannot add @@ -422,6 +510,29 @@ struct ipmi_req #define IPMICTL_SEND_COMMAND _IOR(IPMI_IOC_MAGIC, 13, \ struct ipmi_req) +/* Messages sent to the interface with timing parameters are this + format. */ +struct ipmi_req_settime +{ + struct ipmi_req req; + + /* See ipmi_request_settime() above for details on these + values. */ + int retries; + unsigned int retry_time_ms; +}; +/* + * Send a message to the interfaces with timing parameters. error values + * are: + * - EFAULT - an address supplied was invalid. + * - EINVAL - The address supplied was not valid, or the command + * was not allowed. + * - EMSGSIZE - The message to was too large. + * - ENOMEM - Buffers could not be allocated for the command. + */ +#define IPMICTL_SEND_COMMAND_SETTIME _IOR(IPMI_IOC_MAGIC, 21, \ + struct ipmi_req_settime) + /* Messages received from the interface are this format. */ struct ipmi_recv { @@ -513,4 +624,18 @@ struct ipmi_cmdspec #define IPMICTL_SET_MY_LUN_CMD _IOR(IPMI_IOC_MAGIC, 19, unsigned int) #define IPMICTL_GET_MY_LUN_CMD _IOR(IPMI_IOC_MAGIC, 20, unsigned int) +/* + * Get/set the default timing values for an interface. You shouldn't + * generally mess with these. + */ +struct ipmi_timing_parms +{ + int retries; + unsigned int retry_time_ms; +}; +#define IPMICTL_SET_TIMING_PARMS_CMD _IOR(IPMI_IOC_MAGIC, 22, \ + struct ipmi_timing_parms) +#define IPMICTL_GET_TIMING_PARMS_CMD _IOR(IPMI_IOC_MAGIC, 23, \ + struct ipmi_timing_parms) + #endif /* __LINUX_IPMI_H */ --- linux-2.6.4-rc2/include/linux/ipmi_msgdefs.h 2003-10-17 15:58:04.000000000 -0700 +++ 25/include/linux/ipmi_msgdefs.h 2004-03-07 20:48:02.000000000 -0800 @@ -53,6 +53,7 @@ #define IPMI_SET_BMC_GLOBAL_ENABLES_CMD 0x2e #define IPMI_GET_BMC_GLOBAL_ENABLES_CMD 0x2f #define IPMI_READ_EVENT_MSG_BUFFER_CMD 0x35 +#define IPMI_GET_CHANNEL_INFO_CMD 0x42 #define IPMI_NETFN_STORAGE_REQUEST 0x0a #define IPMI_NETFN_STORAGE_RESPONSE 0x0b @@ -61,8 +62,39 @@ /* The default slave address */ #define IPMI_BMC_SLAVE_ADDR 0x20 -#define IPMI_MAX_MSG_LENGTH 80 +/* The BT interface on high-end HP systems supports up to 255 bytes in + * one transfer. Its "virtual" BMC supports some commands that are longer + * than 128 bytes. Use the full 256, plus NetFn/LUN, Cmd, cCode, plus + * some overhead. It would be nice to base this on the "BT Capabilities" + * but that's too hard to propogate to the rest of the driver. */ +#define IPMI_MAX_MSG_LENGTH 272 /* multiple of 16 */ -#define IPMI_CC_NO_ERROR 0 +#define IPMI_CC_NO_ERROR 0x00 +#define IPMI_NODE_BUSY_ERR 0xc0 +#define IPMI_ERR_MSG_TRUNCATED 0xc6 +#define IPMI_LOST_ARBITRATION_ERR 0x81 +#define IPMI_ERR_UNSPECIFIED 0xff + +#define IPMI_CHANNEL_PROTOCOL_IPMB 1 +#define IPMI_CHANNEL_PROTOCOL_ICMB 2 +#define IPMI_CHANNEL_PROTOCOL_SMBUS 4 +#define IPMI_CHANNEL_PROTOCOL_KCS 5 +#define IPMI_CHANNEL_PROTOCOL_SMIC 6 +#define IPMI_CHANNEL_PROTOCOL_BT10 7 +#define IPMI_CHANNEL_PROTOCOL_BT15 8 +#define IPMI_CHANNEL_PROTOCOL_TMODE 9 + +#define IPMI_CHANNEL_MEDIUM_IPMB 1 +#define IPMI_CHANNEL_MEDIUM_ICMB10 2 +#define IPMI_CHANNEL_MEDIUM_ICMB09 3 +#define IPMI_CHANNEL_MEDIUM_8023LAN 4 +#define IPMI_CHANNEL_MEDIUM_ASYNC 5 +#define IPMI_CHANNEL_MEDIUM_OTHER_LAN 6 +#define IPMI_CHANNEL_MEDIUM_PCI_SMBUS 7 +#define IPMI_CHANNEL_MEDIUM_SMBUS1 8 +#define IPMI_CHANNEL_MEDIUM_SMBUS2 9 +#define IPMI_CHANNEL_MEDIUM_USB1 10 +#define IPMI_CHANNEL_MEDIUM_USB2 11 +#define IPMI_CHANNEL_MEDIUM_SYSINTF 12 #endif /* __LINUX_IPMI_MSGDEFS_H */ --- linux-2.6.4-rc2/include/linux/ipmi_smi.h 2003-06-14 12:18:24.000000000 -0700 +++ 25/include/linux/ipmi_smi.h 2004-03-07 20:48:02.000000000 -0800 @@ -35,6 +35,8 @@ #define __LINUX_IPMI_SMI_H #include +#include +#include /* This files describes the interface for IPMI system management interface drivers to bind into the IPMI message handler. */ @@ -48,7 +50,7 @@ typedef struct ipmi_smi *ipmi_smi_t; * been received, it will report this same data structure back up to * the upper layer. If an error occurs, it should fill in the * response with an error code in the completion code location. When - * asyncronous data is received, one of these is allocated, the + * asynchronous data is received, one of these is allocated, the * data_size is set to zero and the response holds the data from the * get message or get event command that the interface initiated. * Note that it is the interfaces responsibility to detect @@ -62,9 +64,6 @@ struct ipmi_smi_msg long msgid; void *user_data; - /* If 0, add to the end of the queue. If 1, add to the beginning. */ - int prio; - int data_size; unsigned char data[IPMI_MAX_MSG_LENGTH]; @@ -134,4 +133,11 @@ static inline void ipmi_free_smi_msg(str msg->done(msg); } +/* Allow the lower layer to add things to the proc filesystem + directory for this interface. Note that the entry will + automatically be dstroyed when the interface is destroyed. */ +int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name, + read_proc_t *read_proc, write_proc_t *write_proc, + void *data, struct module *owner); + #endif /* __LINUX_IPMI_SMI_H */ --- linux-2.6.4-rc2/include/linux/irq.h 2003-08-22 19:23:42.000000000 -0700 +++ 25/include/linux/irq.h 2004-03-07 20:48:21.000000000 -0800 @@ -71,7 +71,6 @@ extern irq_desc_t irq_desc [NR_IRQS]; #include /* the arch dependent stuff */ -extern int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); extern int setup_irq(unsigned int , struct irqaction * ); extern hw_irq_controller no_irq_type; /* needed in every arch ? */ --- linux-2.6.4-rc2/include/linux/kmalloc_sizes.h 2003-06-14 12:18:30.000000000 -0700 +++ 25/include/linux/kmalloc_sizes.h 2004-03-07 20:47:44.000000000 -0800 @@ -12,6 +12,9 @@ CACHE(256) CACHE(512) CACHE(1024) +#if (PAGE_SIZE != 4096) /* special cache for eth skbs - 5 fit into one 8 kB page */ + CACHE(1620) +#endif CACHE(2048) CACHE(4096) CACHE(8192) --- linux-2.6.4-rc2/include/linux/linkage.h 2003-06-14 12:18:23.000000000 -0700 +++ 25/include/linux/linkage.h 2004-03-07 20:46:46.000000000 -0800 @@ -37,6 +37,7 @@ #ifndef FASTCALL #define FASTCALL(x) x +#define fastcall #endif #endif --- linux-2.6.4-rc2/include/linux/list.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/linux/list.h 2004-03-07 20:48:16.000000000 -0800 @@ -142,8 +142,11 @@ static inline void __list_del(struct lis * Note: list_empty on entry does not return true after this, the entry is * in an undefined state. */ +#include /* BUG_ON */ static inline void list_del(struct list_head *entry) { + BUG_ON(entry->prev->next != entry); + BUG_ON(entry->next->prev != entry); __list_del(entry->prev, entry->next); entry->next = LIST_POISON1; entry->prev = LIST_POISON2; --- linux-2.6.4-rc2/include/linux/lockd/debug.h 2003-06-14 12:18:30.000000000 -0700 +++ 25/include/linux/lockd/debug.h 2004-03-07 20:47:31.000000000 -0800 @@ -23,7 +23,7 @@ #undef ifdebug #if defined(RPC_DEBUG) && defined(LOCKD_DEBUG) -# define ifdebug(flag) if (nlm_debug & NLMDBG_##flag) +# define ifdebug(flag) if (unlikely(nlm_debug & NLMDBG_##flag)) #else # define ifdebug(flag) if (0) #endif --- linux-2.6.4-rc2/include/linux/lockd/lockd.h 2003-08-22 19:23:42.000000000 -0700 +++ 25/include/linux/lockd/lockd.h 2004-03-07 20:47:32.000000000 -0800 @@ -165,6 +165,7 @@ u32 nlmsvc_cancel_blocked(struct nlm_ unsigned long nlmsvc_retry_blocked(void); int nlmsvc_traverse_blocks(struct nlm_host *, struct nlm_file *, int action); +void nlmsvc_grant_reply(struct svc_rqst *, struct nlm_cookie *, u32); /* * File handling for the server personality --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/include/linux/lockmeter.h 2004-03-07 20:48:17.000000000 -0800 @@ -0,0 +1,320 @@ +/* + * Copyright (C) 1999-2002 Silicon Graphics, Inc. + * + * Written by John Hawkes (hawkes@sgi.com) + * Based on klstat.h by Jack Steiner (steiner@sgi.com) + * + * Modified by Ray Bryant (raybry@us.ibm.com) Feb-Apr 2000 + * Changes Copyright (C) 2000 IBM, Inc. + * Added save of index in spinlock_t to improve efficiency + * of "hold" time reporting for spinlocks + * Added support for hold time statistics for read and write + * locks. + * Moved machine dependent code to include/asm/lockmeter.h. + * + */ + +#ifndef _LINUX_LOCKMETER_H +#define _LINUX_LOCKMETER_H + + +/*--------------------------------------------------- + * architecture-independent lockmeter.h + *-------------------------------------------------*/ + +/* + * raybry -- version 2: added efficient hold time statistics + * requires lstat recompile, so flagged as new version + * raybry -- version 3: added global reader lock data + * hawkes -- version 4: removed some unnecessary fields to simplify mips64 port + */ +#define LSTAT_VERSION 5 + +int lstat_update(void*, void*, int); +int lstat_update_time(void*, void*, int, uint32_t); + +/* + * Currently, the mips64 and sparc64 kernels talk to a 32-bit lockstat, so we + * need to force compatibility in the inter-communication data structure. + */ + +#if defined(CONFIG_MIPS32_COMPAT) +#define TIME_T uint32_t +#elif defined(CONFIG_SPARC) || defined(CONFIG_SPARC64) +#define TIME_T uint64_t +#else +#define TIME_T time_t +#endif + +#if defined(__KERNEL__) || (!defined(CONFIG_MIPS32_COMPAT) && !defined(CONFIG_SPARC) && !defined(CONFIG_SPARC64)) || (_MIPS_SZLONG==32) +#define POINTER void * +#else +#define POINTER int64_t +#endif + +/* + * Values for the "action" parameter passed to lstat_update. + * ZZZ - do we want a try-success status here??? + */ +#define LSTAT_ACT_NO_WAIT 0 +#define LSTAT_ACT_SPIN 1 +#define LSTAT_ACT_REJECT 2 +#define LSTAT_ACT_WW_SPIN 3 +#define LSTAT_ACT_SLEPT 4 /* UNUSED */ + +#define LSTAT_ACT_MAX_VALUES 4 /* NOTE: Increase to 5 if use ACT_SLEPT */ + +/* + * Special values for the low 2 bits of an RA passed to + * lstat_update. + */ +/* we use these values to figure out what kind of lock data */ +/* is stored in the statistics table entry at index ....... */ +#define LSTAT_RA_SPIN 0 /* spin lock data */ +#define LSTAT_RA_READ 1 /* read lock statistics */ +#define LSTAT_RA_SEMA 2 /* RESERVED */ +#define LSTAT_RA_WRITE 3 /* write lock statistics*/ + +#define LSTAT_RA(n) \ + ((void*)( ((unsigned long)__builtin_return_address(0) & ~3) | n) ) + +/* + * Constants used for lock addresses in the lstat_directory + * to indicate special values of the lock address. + */ +#define LSTAT_MULTI_LOCK_ADDRESS NULL + +/* + * Maximum size of the lockstats tables. Increase this value + * if its not big enough. (Nothing bad happens if its not + * big enough although some locks will not be monitored.) + * We record overflows of this quantity in lstat_control.dir_overflows + * + * Note: The max value here must fit into the field set + * and obtained by the macro's PUT_INDEX() and GET_INDEX(). + * This value depends on how many bits are available in the + * lock word in the particular machine implementation we are on. + */ +#define LSTAT_MAX_STAT_INDEX 2000 + +/* + * Size and mask for the hash table into the directory. + */ +#define LSTAT_HASH_TABLE_SIZE 4096 /* must be 2**N */ +#define LSTAT_HASH_TABLE_MASK (LSTAT_HASH_TABLE_SIZE-1) + +#define DIRHASH(ra) ((unsigned long)(ra)>>2 & LSTAT_HASH_TABLE_MASK) + +/* + * This defines an entry in the lockstat directory. It contains + * information about a lock being monitored. + * A directory entry only contains the lock identification - + * counts on usage of the lock are kept elsewhere in a per-cpu + * data structure to minimize cache line pinging. + */ +typedef struct { + POINTER caller_ra; /* RA of code that set lock */ + POINTER lock_ptr; /* lock address */ + ushort next_stat_index; /* Used to link multiple locks that have the same hash table value */ +} lstat_directory_entry_t; + +/* + * A multi-dimensioned array used to contain counts for lock accesses. + * The array is 3-dimensional: + * - CPU number. Keep from thrashing cache lines between CPUs + * - Directory entry index. Identifies the lock + * - Action. Indicates what kind of contention occurred on an + * access to the lock. + * + * The index of an entry in the directory is the same as the 2nd index + * of the entry in the counts array. + */ +/* + * This table contains data for spin_locks, write locks, and read locks + * Not all data is used for all cases. In particular, the hold time + * information is not stored here for read locks since that is a global + * (e. g. cannot be separated out by return address) quantity. + * See the lstat_read_lock_counts_t structure for the global read lock + * hold time. + */ +typedef struct { + uint64_t cum_wait_ticks; /* sum of wait times */ + /* for write locks, sum of time a */ + /* writer is waiting for a reader */ + int64_t cum_hold_ticks; /* cumulative sum of holds */ + /* not used for read mode locks */ + /* must be signed. ............... */ + uint32_t max_wait_ticks; /* max waiting time */ + uint32_t max_hold_ticks; /* max holding time */ + uint64_t cum_wait_ww_ticks; /* sum times writer waits on writer*/ + uint32_t max_wait_ww_ticks; /* max wait time writer vs writer */ + /* prev 2 only used for write locks*/ + uint32_t acquire_time; /* time lock acquired this CPU */ + uint32_t count[LSTAT_ACT_MAX_VALUES]; +} lstat_lock_counts_t; + +typedef lstat_lock_counts_t lstat_cpu_counts_t[LSTAT_MAX_STAT_INDEX]; + +/* + * User request to: + * - turn statistic collection on/off, or to reset + */ +#define LSTAT_OFF 0 +#define LSTAT_ON 1 +#define LSTAT_RESET 2 +#define LSTAT_RELEASE 3 + +#define LSTAT_MAX_READ_LOCK_INDEX 1000 +typedef struct { + POINTER lock_ptr; /* address of lock for output stats */ + uint32_t read_lock_count; + int64_t cum_hold_ticks; /* sum of read lock hold times over */ + /* all callers. ....................*/ + uint32_t write_index; /* last write lock hash table index */ + uint32_t busy_periods; /* count of busy periods ended this */ + uint64_t start_busy; /* time this busy period started. ..*/ + uint64_t busy_ticks; /* sum of busy periods this lock. ..*/ + uint64_t max_busy; /* longest busy period for this lock*/ + uint32_t max_readers; /* maximum number of readers ...... */ +#ifdef USER_MODE_TESTING + rwlock_t entry_lock; /* lock for this read lock entry... */ + /* avoid having more than one rdr at*/ + /* needed for user space testing... */ + /* not needed for kernel 'cause it */ + /* is non-preemptive. ............. */ +#endif +} lstat_read_lock_counts_t; +typedef lstat_read_lock_counts_t lstat_read_lock_cpu_counts_t[LSTAT_MAX_READ_LOCK_INDEX]; + +#if defined(__KERNEL__) || defined(USER_MODE_TESTING) + +#ifndef USER_MODE_TESTING +#include +#else +#include "asm_newlockmeter.h" +#endif + +/* + * Size and mask for the hash table into the directory. + */ +#define LSTAT_HASH_TABLE_SIZE 4096 /* must be 2**N */ +#define LSTAT_HASH_TABLE_MASK (LSTAT_HASH_TABLE_SIZE-1) + +#define DIRHASH(ra) ((unsigned long)(ra)>>2 & LSTAT_HASH_TABLE_MASK) + +/* + * This version eliminates the per processor lock stack. What we do is to + * store the index of the lock hash structure in unused bits in the lock + * itself. Then on unlock we can find the statistics record without doing + * any additional hash or lock stack lookup. This works for spin_locks. + * Hold time reporting is now basically as cheap as wait time reporting + * so we ignore the difference between LSTAT_ON_HOLD and LSTAT_ON_WAIT + * as in version 1.1.* of lockmeter. + * + * For rw_locks, we store the index of a global reader stats structure in + * the lock and the writer index is stored in the latter structure. + * For read mode locks we hash at the time of the lock to find an entry + * in the directory for reader wait time and the like. + * At unlock time for read mode locks, we update just the global structure + * so we don't need to know the reader directory index value at unlock time. + * + */ + +/* + * Protocol to change lstat_control.state + * This is complicated because we don't want the cum_hold_time for + * a rw_lock to be decremented in _read_lock_ without making sure it + * is incremented in _read_lock_ and vice versa. So here is the + * way we change the state of lstat_control.state: + * I. To Turn Statistics On + * After allocating storage, set lstat_control.state non-zero. + * This works because we don't start updating statistics for in use + * locks until the reader lock count goes to zero. + * II. To Turn Statistics Off: + * (0) Disable interrupts on this CPU + * (1) Seize the lstat_control.directory_lock + * (2) Obtain the current value of lstat_control.next_free_read_lock_index + * (3) Store a zero in lstat_control.state. + * (4) Release the lstat_control.directory_lock + * (5) For each lock in the read lock list up to the saved value + * (well, -1) of the next_free_read_lock_index, do the following: + * (a) Check validity of the stored lock address + * by making sure that the word at the saved addr + * has an index that matches this entry. If not + * valid, then skip this entry. + * (b) If there is a write lock already set on this lock, + * skip to (d) below. + * (c) Set a non-metered write lock on the lock + * (d) set the cached INDEX in the lock to zero + * (e) Release the non-metered write lock. + * (6) Re-enable interrupts + * + * These rules ensure that a read lock will not have its statistics + * partially updated even though the global lock recording state has + * changed. See put_lockmeter_info() for implementation. + * + * The reason for (b) is that there may be write locks set on the + * syscall path to put_lockmeter_info() from user space. If we do + * not do this check, then we can deadlock. A similar problem would + * occur if the lock was read locked by the current CPU. At the + * moment this does not appear to happen. + */ + +/* + * Main control structure for lockstat. Used to turn statistics on/off + * and to maintain directory info. + */ +typedef struct { + int state; + spinlock_t control_lock; /* used to serialize turning statistics on/off */ + spinlock_t directory_lock; /* for serialize adding entries to directory */ + volatile int next_free_dir_index;/* next free entry in the directory */ + /* FIXME not all of these fields are used / needed .............. */ + /* the following fields represent data since */ + /* first "lstat on" or most recent "lstat reset" */ + TIME_T first_started_time; /* time when measurement first enabled */ + TIME_T started_time; /* time when measurement last started */ + TIME_T ending_time; /* time when measurement last disabled */ + uint64_t started_cycles64; /* cycles when measurement last started */ + uint64_t ending_cycles64; /* cycles when measurement last disabled */ + uint64_t enabled_cycles64; /* total cycles with measurement enabled */ + int intervals; /* number of measurement intervals recorded */ + /* i. e. number of times did lstat on;lstat off */ + lstat_directory_entry_t *dir; /* directory */ + int dir_overflow; /* count of times ran out of space in directory */ + int rwlock_overflow; /* count of times we couldn't allocate a rw block*/ + ushort *hashtab; /* hash table for quick dir scans */ + lstat_cpu_counts_t *counts[NR_CPUS]; /* Array of pointers to per-cpu stats */ + int next_free_read_lock_index; /* next rwlock reader (global) stats block */ + lstat_read_lock_cpu_counts_t *read_lock_counts[NR_CPUS]; /* per cpu read lock stats */ +} lstat_control_t; + +#endif /* defined(__KERNEL__) || defined(USER_MODE_TESTING) */ + +typedef struct { + short lstat_version; /* version of the data */ + short state; /* the current state is returned */ + int maxcpus; /* Number of cpus present */ + int next_free_dir_index; /* index of the next free directory entry */ + TIME_T first_started_time; /* when measurement enabled for first time */ + TIME_T started_time; /* time in secs since 1969 when stats last turned on */ + TIME_T ending_time; /* time in secs since 1969 when stats last turned off */ + uint32_t cycleval; /* cycles per second */ +#ifdef notyet + void *kernel_magic_addr; /* address of kernel_magic */ + void *kernel_end_addr; /* contents of kernel magic (points to "end") */ +#endif + int next_free_read_lock_index; /* index of next (global) read lock stats struct */ + uint64_t started_cycles64; /* cycles when measurement last started */ + uint64_t ending_cycles64; /* cycles when stats last turned off */ + uint64_t enabled_cycles64; /* total cycles with measurement enabled */ + int intervals; /* number of measurement intervals recorded */ + /* i.e. number of times we did lstat on;lstat off*/ + int dir_overflow; /* number of times we wanted more space in directory */ + int rwlock_overflow; /* # of times we wanted more space in read_locks_count */ + struct new_utsname uts; /* info about machine where stats are measured */ + /* -T option of lockstat allows data to be */ + /* moved to another machine. ................. */ +} lstat_user_request_t; + +#endif /* _LINUX_LOCKMETER_H */ --- linux-2.6.4-rc2/include/linux/loop.h 2004-03-03 23:12:48.000000000 -0800 +++ 25/include/linux/loop.h 2004-03-07 20:48:06.000000000 -0800 @@ -153,5 +153,6 @@ int loop_unregister_transfer(int number) #define LOOP_GET_STATUS 0x4C03 #define LOOP_SET_STATUS64 0x4C04 #define LOOP_GET_STATUS64 0x4C05 +#define LOOP_CHANGE_FD 0x4C06 #endif --- linux-2.6.4-rc2/include/linux/miscdevice.h 2004-02-03 20:42:39.000000000 -0800 +++ 25/include/linux/miscdevice.h 2004-03-07 20:46:53.000000000 -0800 @@ -3,7 +3,6 @@ #include #include -#define BUSMOUSE_MINOR 0 #define PSMOUSE_MINOR 1 #define MS_BUSMOUSE_MINOR 2 #define ATIXL_BUSMOUSE_MINOR 3 --- linux-2.6.4-rc2/include/linux/mm.h 2004-03-03 23:12:48.000000000 -0800 +++ 25/include/linux/mm.h 2004-03-07 20:48:04.000000000 -0800 @@ -415,10 +415,11 @@ static inline int page_mapped(struct pag * Used to decide whether a process gets delivered SIGBUS or * just gets major/minor fault counters bumped up. */ -#define VM_FAULT_OOM (-1) -#define VM_FAULT_SIGBUS 0 -#define VM_FAULT_MINOR 1 -#define VM_FAULT_MAJOR 2 +#define VM_FAULT_OOM (-1) +#define VM_FAULT_SIGBUS 0 +#define VM_FAULT_MINOR 1 +#define VM_FAULT_MAJOR 2 +#define VM_FAULT_SIGSEGV 3 #define offset_in_page(p) ((unsigned long)(p) & ~PAGE_MASK) @@ -451,9 +452,10 @@ extern pmd_t *FASTCALL(__pmd_alloc(struc extern pte_t *FASTCALL(pte_alloc_kernel(struct mm_struct *mm, pmd_t *pmd, unsigned long address)); extern pte_t *FASTCALL(pte_alloc_map(struct mm_struct *mm, pmd_t *pmd, unsigned long address)); extern int install_page(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long addr, struct page *page, pgprot_t prot); -extern int install_file_pte(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long addr, unsigned long pgoff, pgprot_t prot); +extern int install_file_pte(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long addr, unsigned long pgoff, pgprot_t prot, int linear); extern int handle_mm_fault(struct mm_struct *mm,struct vm_area_struct *vma, unsigned long address, int write_access); extern int make_pages_present(unsigned long addr, unsigned long end); +extern long __remap_file_pages(struct mm_struct *mm, unsigned long start, unsigned long size, unsigned long prot, unsigned long pgoff, unsigned long flags); extern int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len, int write); void put_dirty_page(struct task_struct *tsk, struct page *page, unsigned long address, pgprot_t prot); @@ -530,7 +532,8 @@ extern void si_meminfo_node(struct sysin /* mmap.c */ extern void insert_vm_struct(struct mm_struct *, struct vm_area_struct *); -extern void build_mmap_rb(struct mm_struct *); +extern void __vma_link_rb(struct mm_struct *, struct vm_area_struct *, + struct rb_node **, struct rb_node *); extern void exit_mmap(struct mm_struct *); extern unsigned long get_unmapped_area(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); @@ -633,6 +636,30 @@ extern struct page * follow_page(struct extern int remap_page_range(struct vm_area_struct *vma, unsigned long from, unsigned long to, unsigned long size, pgprot_t prot); +#ifdef CONFIG_NUMA +static inline void zero_rss(struct mm_struct *mm) +{ + mm->rss = 0; + memset(mm->pernode_rss, 0, MAX_NUMNODES * sizeof(*mm->pernode_rss)); +} + +static inline void inc_rss(struct mm_struct *mm, struct page *page) +{ + mm->rss++; + mm->pernode_rss[page_nodenum(page)]++; +} + +static inline void dec_rss(struct mm_struct *mm, struct page *page) +{ + mm->rss--; + mm->pernode_rss[page_nodenum(page)]--; +} +#else /* !CONFIG_NUMA */ +#define zero_rss(mm) ((mm)->rss = 0) +#define inc_rss(mm, page) ((mm)->rss++) +#define dec_rss(mm, page) ((mm)->rss--) +#endif /* CONFIG_NUMA */ + #ifndef CONFIG_DEBUG_PAGEALLOC static inline void kernel_map_pages(struct page *page, int numpages, int enable) --- linux-2.6.4-rc2/include/linux/mmzone.h 2004-03-03 23:12:48.000000000 -0800 +++ 25/include/linux/mmzone.h 2004-03-07 20:48:15.000000000 -0800 @@ -76,7 +76,8 @@ struct zone { spinlock_t lru_lock; struct list_head active_list; struct list_head inactive_list; - atomic_t refill_counter; + atomic_t nr_scan_active; + atomic_t nr_scan_inactive; unsigned long nr_active; unsigned long nr_inactive; int all_unreclaimable; /* All pages pinned */ @@ -213,6 +214,7 @@ typedef struct pglist_data { int node_id; struct pglist_data *pgdat_next; wait_queue_head_t kswapd_wait; + struct task_struct *kswapd; } pg_data_t; #define node_present_pages(nid) (NODE_DATA(nid)->node_present_pages) @@ -288,6 +290,11 @@ static inline int is_highmem(struct zone return (zone - zone->zone_pgdat->node_zones == ZONE_HIGHMEM); } +static inline int is_normal(struct zone *zone) +{ + return (zone - zone->zone_pgdat->node_zones == ZONE_NORMAL); +} + /* These two functions are used to setup the per zone pages min values */ struct ctl_table; struct file; --- linux-2.6.4-rc2/include/linux/moduleparam.h 2004-02-03 20:42:39.000000000 -0800 +++ 25/include/linux/moduleparam.h 2004-03-07 20:46:53.000000000 -0800 @@ -126,13 +126,16 @@ extern int param_get_invbool(char *buffe #define param_check_invbool(name, p) __param_check(name, p, int) /* Comma-separated array: num is set to number they actually specified. */ -#define module_param_array(name, type, num, perm) \ +#define module_param_array_named(name, array, type, num, perm) \ static struct kparam_array __param_arr_##name \ - = { ARRAY_SIZE(name), &num, param_set_##type, param_get_##type, \ - sizeof(name[0]), name }; \ + = { ARRAY_SIZE(array), &num, param_set_##type, param_get_##type,\ + sizeof(array[0]), array }; \ module_param_call(name, param_array_set, param_array_get, \ &__param_arr_##name, perm) +#define module_param_array(name, type, num, perm) \ + module_param_array_named(name, name, type, num, perm) + extern int param_array_set(const char *val, struct kernel_param *kp); extern int param_array_get(char *buffer, struct kernel_param *kp); --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/include/linux/mqueue.h 2004-03-07 20:47:50.000000000 -0800 @@ -0,0 +1,37 @@ +/* Copyright (C) 2003 Krzysztof Benedyczak & Michal Wronski + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + It 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this software; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#ifndef _LINUX_MQUEUE_H +#define _LINUX_MQUEUE_H + +#define MQ_PRIO_MAX 32768 + +typedef int mqd_t; + +struct mq_attr { + long mq_flags; /* message queue flags */ + long mq_maxmsg; /* maximum number of messages */ + long mq_msgsize; /* maximum message size */ + long mq_curmsgs; /* number of messages currently queued */ + long __reserved[4]; /* ignored for input, zeroed for output */ +}; + +#define NOTIFY_NONE 0 +#define NOTIFY_WOKENUP 1 +#define NOTIFY_REMOVED 2 + +#endif --- linux-2.6.4-rc2/include/linux/msg.h 2004-03-03 23:12:48.000000000 -0800 +++ 25/include/linux/msg.h 2004-03-07 20:47:48.000000000 -0800 @@ -74,9 +74,6 @@ struct msg_msg { /* the actual message follows immediately */ }; -#define DATALEN_MSG (PAGE_SIZE-sizeof(struct msg_msg)) -#define DATALEN_SEG (PAGE_SIZE-sizeof(struct msg_msgseg)) - /* one msq_queue structure for each present queue on the system */ struct msg_queue { struct kern_ipc_perm q_perm; --- linux-2.6.4-rc2/include/linux/netdevice.h 2004-03-03 23:12:48.000000000 -0800 +++ 25/include/linux/netdevice.h 2004-03-07 20:46:54.000000000 -0800 @@ -456,6 +456,12 @@ struct net_device unsigned char *haddr); int (*neigh_setup)(struct net_device *dev, struct neigh_parms *); int (*accept_fastpath)(struct net_device *, struct dst_entry*); +#ifdef CONFIG_NETPOLL_RX + int netpoll_rx; +#endif +#ifdef CONFIG_NET_POLL_CONTROLLER + void (*poll_controller)(struct net_device *dev); +#endif /* bridge stuff */ struct net_bridge_port *br_port; @@ -540,6 +546,9 @@ extern int dev_new_index(void); extern struct net_device *dev_get_by_index(int ifindex); extern struct net_device *__dev_get_by_index(int ifindex); extern int dev_restart(struct net_device *dev); +#ifdef CONFIG_NETPOLL_TRAP +extern int netpoll_trap(void); +#endif typedef int gifconf_func_t(struct net_device * dev, char * bufptr, int len); extern int register_gifconf(unsigned int family, gifconf_func_t * gifconf); @@ -598,12 +607,20 @@ static inline void netif_start_queue(str static inline void netif_wake_queue(struct net_device *dev) { +#ifdef CONFIG_NETPOLL_TRAP + if (netpoll_trap()) + return; +#endif if (test_and_clear_bit(__LINK_STATE_XOFF, &dev->state)) __netif_schedule(dev); } static inline void netif_stop_queue(struct net_device *dev) { +#ifdef CONFIG_NETPOLL_TRAP + if (netpoll_trap()) + return; +#endif set_bit(__LINK_STATE_XOFF, &dev->state); } --- linux-2.6.4-rc2/include/linux/net.h 2004-02-03 20:42:39.000000000 -0800 +++ 25/include/linux/net.h 2004-03-07 20:48:02.000000000 -0800 @@ -25,7 +25,7 @@ struct poll_table_struct; struct inode; -#define NPROTO 32 /* should be enough for now.. */ +#define NPROTO 64 /* should be enough for now.. */ #define SYS_SOCKET 1 /* sys_socket(2) */ #define SYS_BIND 2 /* sys_bind(2) */ --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/include/linux/netpoll.h 2004-03-07 20:46:54.000000000 -0800 @@ -0,0 +1,38 @@ +/* + * Common code for low-level network console, dump, and debugger code + * + * Derived from netconsole, kgdb-over-ethernet, and netdump patches + */ + +#ifndef _LINUX_NETPOLL_H +#define _LINUX_NETPOLL_H + +#include +#include +#include +#include + +struct netpoll; + +struct netpoll { + struct net_device *dev; + char dev_name[16], *name; + void (*rx_hook)(struct netpoll *, int, char *, int); + u32 local_ip, remote_ip; + u16 local_port, remote_port; + unsigned char local_mac[6], remote_mac[6]; + struct list_head rx_list; +}; + +void netpoll_poll(struct netpoll *np); +void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb); +void netpoll_send_udp(struct netpoll *np, const char *msg, int len); +int netpoll_parse_options(struct netpoll *np, char *opt); +int netpoll_setup(struct netpoll *np); +int netpoll_trap(void); +void netpoll_set_trap(int trap); +void netpoll_cleanup(struct netpoll *np); +int netpoll_rx(struct sk_buff *skb); + + +#endif --- linux-2.6.4-rc2/include/linux/nfsd/nfsfh.h 2004-03-03 23:12:48.000000000 -0800 +++ 25/include/linux/nfsd/nfsfh.h 2004-03-07 20:46:46.000000000 -0800 @@ -66,8 +66,9 @@ struct nfs_fhbase_old { * 0 - 4 byte device id (ms-2-bytes major, ls-2-bytes minor), 4byte inode number * NOTE: we cannot use the kdev_t device id value, because kdev_t.h * says we mustn't. We must break it up and reassemble. - * Possible future encodings: * 1 - 4 byte user specified identifier + * 2 - 4 byte major, 4 byte minor, 4 byte inode number - DEPRECATED + * 3 - 4 byte device id, encoded for user-space, 4 byte inode number * * The fileid_type identified how the file within the filesystem is encoded. * This is (will be) passed to, and set by, the underlying filesystem if it supports @@ -114,6 +115,7 @@ struct knfsd_fh { #define fh_auth_type fh_base.fh_new.fb_auth_type #define fh_fileid_type fh_base.fh_new.fb_fileid_type #define fh_auth fh_base.fh_new.fb_auth +#define fh_fsid fh_base.fh_new.fb_auth #ifdef __KERNEL__ @@ -183,6 +185,23 @@ static inline void mk_fsid_v2(u32 *fsidv fsidv[2] = ino_t_to_u32(ino); } +static inline void mk_fsid_v3(u32 *fsidv, dev_t dev, ino_t ino) +{ + fsidv[0] = new_encode_dev(dev); + fsidv[1] = ino_t_to_u32(ino); +} + +static inline int key_len(int type) +{ + switch(type) { + case 0: return 8; + case 1: return 4; + case 2: return 12; + case 3: return 8; + default: return 0; + } +} + /* * Shorthand for dprintk()'s */ --- linux-2.6.4-rc2/include/linux/nfs_fs.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/linux/nfs_fs.h 2004-03-07 20:47:34.000000000 -0800 @@ -99,7 +99,7 @@ struct nfs_inode { /* * Various flags */ - unsigned short flags; + unsigned int flags; /* * read_cache_jiffies is when we started read-caching this inode, @@ -118,19 +118,22 @@ struct nfs_inode { * * mtime != read_cache_mtime */ + unsigned long readdir_timestamp; unsigned long read_cache_jiffies; - struct timespec read_cache_ctime; - struct timespec read_cache_mtime; - __u64 read_cache_isize; unsigned long attrtimeo; unsigned long attrtimeo_timestamp; __u64 change_attr; /* v4 only */ + /* "Generation counter" for the attribute cache. This is + * bumped whenever we update the metadata on the + * server. + */ + unsigned long cache_change_attribute; /* - * Timestamp that dates the change made to read_cache_mtime. - * This is of use for dentry revalidation + * Counter indicating the number of outstanding requests that + * will cause a file data update. */ - unsigned long cache_mtime_jiffies; + atomic_t data_updates; struct nfs_access_cache cache_access; @@ -170,8 +173,9 @@ struct nfs_inode { #define NFS_INO_STALE 0x0001 /* possible stale inode */ #define NFS_INO_ADVISE_RDPLUS 0x0002 /* advise readdirplus */ #define NFS_INO_REVALIDATING 0x0004 /* revalidating attrs */ -#define NFS_INO_FLUSH 0x0008 /* inode is due for flushing */ -#define NFS_INO_FAKE_ROOT 0x0080 /* root inode placeholder */ +#define NFS_INO_INVALID_ATTR 0x0008 /* cached attrs are invalid */ +#define NFS_INO_INVALID_DATA 0x0010 /* cached data is invalid */ +#define NFS_INO_INVALID_ATIME 0x0020 /* cached atime is invalid */ static inline struct nfs_inode *NFS_I(struct inode *inode) { @@ -186,15 +190,7 @@ static inline struct nfs_inode *NFS_I(st #define NFS_ADDR(inode) (RPC_PEERADDR(NFS_CLIENT(inode))) #define NFS_COOKIEVERF(inode) (NFS_I(inode)->cookieverf) #define NFS_READTIME(inode) (NFS_I(inode)->read_cache_jiffies) -#define NFS_MTIME_UPDATE(inode) (NFS_I(inode)->cache_mtime_jiffies) -#define NFS_CACHE_CTIME(inode) (NFS_I(inode)->read_cache_ctime) -#define NFS_CACHE_MTIME(inode) (NFS_I(inode)->read_cache_mtime) -#define NFS_CACHE_ISIZE(inode) (NFS_I(inode)->read_cache_isize) #define NFS_CHANGE_ATTR(inode) (NFS_I(inode)->change_attr) -#define NFS_CACHEINV(inode) \ -do { \ - NFS_READTIME(inode) = jiffies - NFS_MAXATTRTIMEO(inode) - 1; \ -} while (0) #define NFS_ATTRTIMEO(inode) (NFS_I(inode)->attrtimeo) #define NFS_MINATTRTIMEO(inode) \ (S_ISDIR(inode->i_mode)? NFS_SERVER(inode)->acdirmin \ @@ -207,10 +203,20 @@ do { \ #define NFS_FLAGS(inode) (NFS_I(inode)->flags) #define NFS_REVALIDATING(inode) (NFS_FLAGS(inode) & NFS_INO_REVALIDATING) #define NFS_STALE(inode) (NFS_FLAGS(inode) & NFS_INO_STALE) -#define NFS_FAKE_ROOT(inode) (NFS_FLAGS(inode) & NFS_INO_FAKE_ROOT) #define NFS_FILEID(inode) (NFS_I(inode)->fileid) +static inline int nfs_caches_unstable(struct inode *inode) +{ + return atomic_read(&NFS_I(inode)->data_updates) != 0; +} + +static inline void NFS_CACHEINV(struct inode *inode) +{ + if (!nfs_caches_unstable(inode)) + NFS_FLAGS(inode) |= NFS_INO_INVALID_ATTR; +} + static inline int nfs_server_capable(struct inode *inode, int cap) { return NFS_SERVER(inode)->caps & cap; @@ -227,13 +233,37 @@ loff_t page_offset(struct page *page) return ((loff_t)page->index) << PAGE_CACHE_SHIFT; } +/** + * nfs_save_change_attribute - Returns the inode attribute change cookie + * @inode - pointer to inode + * The "change attribute" is updated every time we finish an operation + * that will result in a metadata change on the server. + */ +static inline long nfs_save_change_attribute(struct inode *inode) +{ + return NFS_I(inode)->cache_change_attribute; +} + +/** + * nfs_verify_change_attribute - Detects NFS inode cache updates + * @inode - pointer to inode + * @chattr - previously saved change attribute + * Return "false" if metadata has been updated (or is in the process of + * being updated) since the change attribute was saved. + */ +static inline int nfs_verify_change_attribute(struct inode *inode, unsigned long chattr) +{ + return !nfs_caches_unstable(inode) + && chattr == NFS_I(inode)->cache_change_attribute; +} + /* * linux/fs/nfs/inode.c */ extern void nfs_zap_caches(struct inode *); extern struct inode *nfs_fhget(struct super_block *, struct nfs_fh *, struct nfs_fattr *); -extern int __nfs_refresh_inode(struct inode *, struct nfs_fattr *); +extern int nfs_refresh_inode(struct inode *, struct nfs_fattr *); extern int nfs_getattr(struct vfsmount *, struct dentry *, struct kstat *); extern int nfs_permission(struct inode *, int, struct nameidata *); extern void nfs_set_mmcred(struct inode *, struct rpc_cred *); @@ -241,6 +271,13 @@ extern int nfs_open(struct inode *, stru extern int nfs_release(struct inode *, struct file *); extern int __nfs_revalidate_inode(struct nfs_server *, struct inode *); extern int nfs_setattr(struct dentry *, struct iattr *); +extern void nfs_begin_attr_update(struct inode *); +extern void nfs_end_attr_update(struct inode *); +extern void nfs_begin_data_update(struct inode *); +extern void nfs_end_data_update(struct inode *); + +/* linux/net/ipv4/ipconfig.c: trims ip addr off front of name, too. */ +extern u32 root_nfs_parse_addr(char *name); /*__init*/ /* * linux/fs/nfs/file.c @@ -309,16 +346,15 @@ extern void nfs_commit_done(struct rpc_t * Try to write back everything synchronously (but check the * return value!) */ -extern int nfs_sync_file(struct inode *, struct file *, unsigned long, unsigned int, int); -extern int nfs_flush_file(struct inode *, struct file *, unsigned long, unsigned int, int); +extern int nfs_sync_inode(struct inode *, unsigned long, unsigned int, int); +extern int nfs_flush_inode(struct inode *, unsigned long, unsigned int, int); extern int nfs_flush_list(struct list_head *, int, int); #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) -extern int nfs_commit_file(struct inode *, struct file *, unsigned long, unsigned int, int); +extern int nfs_commit_inode(struct inode *, unsigned long, unsigned int, int); extern int nfs_commit_list(struct list_head *, int); #else static inline int -nfs_commit_file(struct inode *inode, struct file *file, unsigned long offset, - unsigned int len, int flags) +nfs_commit_inode(struct inode *inode, unsigned long idx_start, unsigned int npages, int how) { return 0; } @@ -333,7 +369,7 @@ nfs_have_writebacks(struct inode *inode) static inline int nfs_wb_all(struct inode *inode) { - int error = nfs_sync_file(inode, 0, 0, 0, FLUSH_WAIT); + int error = nfs_sync_inode(inode, 0, 0, FLUSH_WAIT); return (error < 0) ? error : 0; } @@ -343,21 +379,11 @@ nfs_wb_all(struct inode *inode) static inline int nfs_wb_page(struct inode *inode, struct page* page) { - int error = nfs_sync_file(inode, 0, page->index, 1, + int error = nfs_sync_inode(inode, page->index, 1, FLUSH_WAIT | FLUSH_STABLE); return (error < 0) ? error : 0; } -/* - * Write back all pending writes for one user.. - */ -static inline int -nfs_wb_file(struct inode *inode, struct file *file) -{ - int error = nfs_sync_file(inode, file, 0, 0, FLUSH_WAIT); - return (error < 0) ? error : 0; -} - /* Hack for future NFS swap support */ #ifndef IS_SWAPFILE # define IS_SWAPFILE(inode) (0) @@ -383,20 +409,27 @@ extern int nfsroot_mount(struct sockadd /* * inline functions */ -static inline int -nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) + +static inline int nfs_attribute_timeout(struct inode *inode) { - if (time_before(jiffies, NFS_READTIME(inode)+NFS_ATTRTIMEO(inode))) - return NFS_STALE(inode) ? -ESTALE : 0; - return __nfs_revalidate_inode(server, inode); + struct nfs_inode *nfsi = NFS_I(inode); + + return time_after(jiffies, nfsi->read_cache_jiffies+nfsi->attrtimeo); } -static inline int -nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) +/** + * nfs_revalidate_inode - Revalidate the inode attributes + * @server - pointer to nfs_server struct + * @inode - pointer to inode struct + * + * Updates inode attribute information by retrieving the data from the server. + */ +static inline int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) { - if ((fattr->valid & NFS_ATTR_FATTR) == 0) - return 0; - return __nfs_refresh_inode(inode,fattr); + if (!(NFS_FLAGS(inode) & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA)) + && !nfs_attribute_timeout(inode)) + return NFS_STALE(inode) ? -ESTALE : 0; + return __nfs_revalidate_inode(server, inode); } static inline loff_t @@ -661,7 +694,7 @@ struct nfs4_mount_data; #ifdef __KERNEL__ # undef ifdebug # ifdef NFS_DEBUG -# define ifdebug(fac) if (nfs_debug & NFSDBG_##fac) +# define ifdebug(fac) if (unlikely(nfs_debug & NFSDBG_##fac)) # else # define ifdebug(fac) if (0) # endif --- linux-2.6.4-rc2/include/linux/nfs_page.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/linux/nfs_page.h 2004-03-07 20:47:30.000000000 -0800 @@ -53,7 +53,7 @@ extern void nfs_release_request(struct n extern void nfs_list_add_request(struct nfs_page *, struct list_head *); extern int nfs_scan_list(struct list_head *, struct list_head *, - struct file *, unsigned long, unsigned int); + unsigned long, unsigned int); extern int nfs_coalesce_requests(struct list_head *, struct list_head *, unsigned int); extern int nfs_wait_on_request(struct nfs_page *); --- linux-2.6.4-rc2/include/linux/nfs_xdr.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/linux/nfs_xdr.h 2004-03-07 20:47:27.000000000 -0800 @@ -700,7 +700,7 @@ struct nfs_rpc_ops { struct inode_operations *dir_inode_ops; int (*getroot) (struct nfs_server *, struct nfs_fh *, - struct nfs_fattr *); + struct nfs_fsinfo *); int (*getattr) (struct inode *, struct nfs_fattr *); int (*setattr) (struct dentry *, struct nfs_fattr *, struct iattr *); --- linux-2.6.4-rc2/include/linux/notifier.h 2003-06-14 12:17:57.000000000 -0700 +++ 25/include/linux/notifier.h 2004-03-07 20:47:23.000000000 -0800 @@ -63,7 +63,6 @@ extern int notifier_call_chain(struct no #define CPU_ONLINE 0x0002 /* CPU (unsigned)v is up */ #define CPU_UP_PREPARE 0x0003 /* CPU (unsigned)v coming up */ #define CPU_UP_CANCELED 0x0004 /* CPU (unsigned)v NOT coming up */ -#define CPU_OFFLINE 0x0005 /* CPU (unsigned)v offline (still scheduling) */ #define CPU_DEAD 0x0006 /* CPU (unsigned)v dead */ #endif /* __KERNEL__ */ --- linux-2.6.4-rc2/include/linux/page-flags.h 2004-01-09 00:04:32.000000000 -0800 +++ 25/include/linux/page-flags.h 2004-03-07 20:48:10.000000000 -0800 @@ -98,23 +98,38 @@ struct page_state { unsigned long pgpgout; /* Disk writes */ unsigned long pswpin; /* swap reads */ unsigned long pswpout; /* swap writes */ - unsigned long pgalloc; /* page allocations */ + unsigned long pgalloc_high; /* page allocations */ + unsigned long pgalloc_normal; + unsigned long pgalloc_dma; unsigned long pgfree; /* page freeings */ unsigned long pgactivate; /* pages moved inactive->active */ unsigned long pgdeactivate; /* pages moved active->inactive */ + unsigned long pgfault; /* faults (major+minor) */ unsigned long pgmajfault; /* faults (major only) */ - - unsigned long pgscan; /* pages scanned by page reclaim */ - unsigned long pgrefill; /* inspected in refill_inactive_zone */ - unsigned long pgsteal; /* total pages reclaimed */ + unsigned long pgrefill_high; /* inspected in refill_inactive_zone */ + unsigned long pgrefill_normal; + unsigned long pgrefill_dma; + + unsigned long pgsteal_high; /* total highmem pages reclaimed */ + unsigned long pgsteal_normal; + unsigned long pgsteal_dma; + unsigned long pgscan_kswapd_high;/* total highmem pages scanned */ + unsigned long pgscan_kswapd_normal; + + unsigned long pgscan_kswapd_dma; + unsigned long pgscan_direct_high;/* total highmem pages scanned */ + unsigned long pgscan_direct_normal; + unsigned long pgscan_direct_dma; unsigned long pginodesteal; /* pages reclaimed via inode freeing */ - unsigned long kswapd_steal; /* pages reclaimed by kswapd */ + unsigned long slabs_scanned; /* slab objects scanned */ + unsigned long kswapd_steal; /* pages reclaimed by kswapd */ unsigned long kswapd_inodesteal;/* reclaimed via kswapd inode freeing */ unsigned long pageoutrun; /* kswapd's calls to page reclaim */ unsigned long allocstall; /* direct reclaim calls */ + unsigned long pgrotated; /* pages rotated to tail of the LRU */ } ____cacheline_aligned; @@ -131,11 +146,24 @@ extern void get_full_page_state(struct p local_irq_restore(flags); \ } while (0) + #define inc_page_state(member) mod_page_state(member, 1UL) #define dec_page_state(member) mod_page_state(member, 0UL - 1) #define add_page_state(member,delta) mod_page_state(member, (delta)) #define sub_page_state(member,delta) mod_page_state(member, 0UL - (delta)) +#define mod_page_state_zone(zone, member, delta) \ + do { \ + unsigned long flags; \ + local_irq_save(flags); \ + if (is_highmem(zone)) \ + __get_cpu_var(page_states).member##_high += (delta);\ + else if (is_normal(zone)) \ + __get_cpu_var(page_states).member##_normal += (delta);\ + else \ + __get_cpu_var(page_states).member##_dma += (delta);\ + local_irq_restore(flags); \ + } while (0) /* * Manipulation of page state flags --- linux-2.6.4-rc2/include/linux/pci.h 2004-03-03 23:12:48.000000000 -0800 +++ 25/include/linux/pci.h 2004-03-07 20:47:00.000000000 -0800 @@ -393,11 +393,6 @@ struct pci_dev { this if your device has broken DMA or supports 64-bit transfers. */ - u64 consistent_dma_mask;/* Like dma_mask, but for - pci_alloc_consistent mappings as - not all hardware supports - 64 bit addresses for consistent - allocations such descriptors. */ u32 current_state; /* Current operating state. In ACPI-speak, this is D0-D3, D0 being fully functional, and D3 being off. */ @@ -724,6 +719,10 @@ extern int msi_free_vectors(struct pci_d #include +/* Backwards compat, remove in 2.7.x */ +#define pci_dma_sync_single pci_dma_sync_single_for_cpu +#define pci_dma_sync_sg pci_dma_sync_sg_for_cpu + /* * If the system does not have PCI, clearly these return errors. Define * these as simple inline functions to avoid hair in drivers. --- linux-2.6.4-rc2/include/linux/pci_ids.h 2004-03-03 23:12:48.000000000 -0800 +++ 25/include/linux/pci_ids.h 2004-03-07 20:48:01.000000000 -0800 @@ -342,6 +342,8 @@ #define PCI_DEVICE_ID_ATI_RS300_133 0x5831 #define PCI_DEVICE_ID_ATI_RS300_166 0x5832 #define PCI_DEVICE_ID_ATI_RS300_200 0x5833 +/* ATI IXP Chipset */ +#define PCI_DEVICE_ID_ATI_IXP_IDE 0x4349 #define PCI_VENDOR_ID_VLSI 0x1004 #define PCI_DEVICE_ID_VLSI_82C592 0x0005 @@ -1808,6 +1810,7 @@ #define PCI_VENDOR_ID_AFAVLAB 0x14db #define PCI_DEVICE_ID_AFAVLAB_P028 0x2180 +#define PCI_DEVICE_ID_AFAVLAB_P030 0x2182 #define PCI_VENDOR_ID_BROADCOM 0x14e4 #define PCI_DEVICE_ID_TIGON3_5700 0x1644 --- linux-2.6.4-rc2/include/linux/quota.h 2004-01-09 00:04:32.000000000 -0800 +++ 25/include/linux/quota.h 2004-03-07 20:47:09.000000000 -0800 @@ -138,6 +138,10 @@ struct if_dqinfo { #include #include +/* Maximal numbers of writes for quota operation (insert/delete/update) + * (over all formats) - info block, 4 pointer blocks, data block */ +#define DQUOT_MAX_WRITES 6 + /* * Data for one user/group kept in memory */ @@ -168,22 +172,21 @@ struct mem_dqinfo { } u; }; +struct super_block; + #define DQF_MASK 0xffff /* Mask for format specific flags */ #define DQF_INFO_DIRTY_B 16 #define DQF_ANY_DQUOT_DIRTY_B 17 #define DQF_INFO_DIRTY (1 << DQF_INFO_DIRTY_B) /* Is info dirty? */ #define DQF_ANY_DQUOT_DIRTY (1 << DQF_ANY_DQUOT_DIRTY_B) /* Is any dquot dirty? */ -extern inline void mark_info_dirty(struct mem_dqinfo *info) -{ - set_bit(DQF_INFO_DIRTY_B, &info->dqi_flags); -} - +extern void mark_info_dirty(struct super_block *sb, int type); #define info_dirty(info) test_bit(DQF_INFO_DIRTY_B, &(info)->dqi_flags) #define info_any_dquot_dirty(info) test_bit(DQF_ANY_DQUOT_DIRTY_B, &(info)->dqi_flags) #define info_any_dirty(info) (info_dirty(info) || info_any_dquot_dirty(info)) #define sb_dqopt(sb) (&(sb)->s_dquot) +#define sb_dqinfo(sb, type) (sb_dqopt(sb)->info+(type)) struct dqstats { int lookups; @@ -200,15 +203,13 @@ extern struct dqstats dqstats; #define NR_DQHASH 43 /* Just an arbitrary number */ -#define DQ_MOD_B 0 -#define DQ_BLKS_B 1 -#define DQ_INODES_B 2 -#define DQ_FAKE_B 3 - -#define DQ_MOD (1 << DQ_MOD_B) /* dquot modified since read */ -#define DQ_BLKS (1 << DQ_BLKS_B) /* uid/gid has been warned about blk limit */ -#define DQ_INODES (1 << DQ_INODES_B) /* uid/gid has been warned about inode limit */ -#define DQ_FAKE (1 << DQ_FAKE_B) /* no limits only usage */ +#define DQ_MOD_B 0 /* dquot modified since read */ +#define DQ_BLKS_B 1 /* uid/gid has been warned about blk limit */ +#define DQ_INODES_B 2 /* uid/gid has been warned about inode limit */ +#define DQ_FAKE_B 3 /* no limits only usage */ +#define DQ_READ_B 4 /* dquot was read into memory */ +#define DQ_ACTIVE_B 5 /* dquot is active (dquot_release not called) */ +#define DQ_WAITFREE_B 6 /* dquot being waited (by invalidate_dquots) */ struct dquot { struct list_head dq_hash; /* Hash list in memory */ @@ -216,8 +217,7 @@ struct dquot { struct list_head dq_free; /* Free list element */ struct semaphore dq_lock; /* dquot IO lock */ atomic_t dq_count; /* Use count */ - - /* fields after this point are cleared when invalidating */ + wait_queue_head_t dq_wait_unused; /* Wait queue for dquot to become unused */ struct super_block *dq_sb; /* superblock this applies to */ unsigned int dq_id; /* ID this applies to (uid, gid) */ loff_t dq_off; /* Offset of dquot on disk */ @@ -238,19 +238,22 @@ struct quota_format_ops { int (*write_file_info)(struct super_block *sb, int type); /* Write main info about file */ int (*free_file_info)(struct super_block *sb, int type); /* Called on quotaoff() */ int (*read_dqblk)(struct dquot *dquot); /* Read structure for one user */ - int (*commit_dqblk)(struct dquot *dquot); /* Write (or delete) structure for one user */ + int (*commit_dqblk)(struct dquot *dquot); /* Write structure for one user */ + int (*release_dqblk)(struct dquot *dquot); /* Called when last reference to dquot is being dropped */ }; /* Operations working with dquots */ struct dquot_operations { - void (*initialize) (struct inode *, int); - void (*drop) (struct inode *); + int (*initialize) (struct inode *, int); + int (*drop) (struct inode *); int (*alloc_space) (struct inode *, qsize_t, int); int (*alloc_inode) (const struct inode *, unsigned long); - void (*free_space) (struct inode *, qsize_t); - void (*free_inode) (const struct inode *, unsigned long); + int (*free_space) (struct inode *, qsize_t); + int (*free_inode) (const struct inode *, unsigned long); int (*transfer) (struct inode *, struct iattr *); - int (*write_dquot) (struct dquot *); + int (*write_dquot) (struct dquot *); /* Ordinary dquot write */ + int (*mark_dirty) (struct dquot *); /* Dquot is marked dirty */ + int (*write_info) (struct super_block *, int); /* Write of quota "superblock" */ }; /* Operations handling requests from userspace */ @@ -289,10 +292,7 @@ struct quota_info { }; /* Inline would be better but we need to dereference super_block which is not defined yet */ -#define mark_dquot_dirty(dquot) do {\ - set_bit(DQF_ANY_DQUOT_DIRTY_B, &(sb_dqopt((dquot)->dq_sb)->info[(dquot)->dq_type].dqi_flags));\ - set_bit(DQ_MOD_B, &(dquot)->dq_flags);\ -} while (0) +int mark_dquot_dirty(struct dquot *dquot); #define dquot_dirty(dquot) test_bit(DQ_MOD_B, &(dquot)->dq_flags) @@ -304,7 +304,6 @@ struct quota_info { int register_quota_format(struct quota_format_type *fmt); void unregister_quota_format(struct quota_format_type *fmt); -void init_dquot_operations(struct dquot_operations *fsdqops); struct quota_module_name { int qm_fmt_id; --- linux-2.6.4-rc2/include/linux/quotaops.h 2004-02-03 20:42:39.000000000 -0800 +++ 25/include/linux/quotaops.h 2004-03-07 20:47:09.000000000 -0800 @@ -22,16 +22,31 @@ */ extern void sync_dquots(struct super_block *sb, int type); -extern void dquot_initialize(struct inode *inode, int type); -extern void dquot_drop(struct inode *inode); +extern int dquot_initialize(struct inode *inode, int type); +extern int dquot_drop(struct inode *inode); -extern int dquot_alloc_space(struct inode *inode, qsize_t number, int prealloc); -extern int dquot_alloc_inode(const struct inode *inode, unsigned long number); +extern int dquot_alloc_space(struct inode *inode, qsize_t number, int prealloc); +extern int dquot_alloc_inode(const struct inode *inode, unsigned long number); -extern void dquot_free_space(struct inode *inode, qsize_t number); -extern void dquot_free_inode(const struct inode *inode, unsigned long number); +extern int dquot_free_space(struct inode *inode, qsize_t number); +extern int dquot_free_inode(const struct inode *inode, unsigned long number); -extern int dquot_transfer(struct inode *inode, struct iattr *iattr); +extern int dquot_transfer(struct inode *inode, struct iattr *iattr); +extern int dquot_commit(struct dquot *dquot); +extern int dquot_acquire(struct dquot *dquot); +extern int dquot_release(struct dquot *dquot); +extern int dquot_commit_info(struct super_block *sb, int type); +extern int dquot_mark_dquot_dirty(struct dquot *dquot); + +extern int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path); +extern int vfs_quota_on_mount(int type, int format_id, struct dentry *dentry); +extern int vfs_quota_off(struct super_block *sb, int type); +#define vfs_quota_off_mount(sb, type) vfs_quota_off(sb, type) +extern int vfs_quota_sync(struct super_block *sb, int type); +extern int vfs_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii); +extern int vfs_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii); +extern int vfs_get_dqblk(struct super_block *sb, int type, qid_t id, struct if_dqblk *di); +extern int vfs_set_dqblk(struct super_block *sb, int type, qid_t id, struct if_dqblk *di); /* * Operations supported for diskquotas. @@ -42,6 +57,8 @@ extern struct quotactl_ops vfs_quotactl_ #define sb_dquot_ops (&dquot_operations) #define sb_quotactl_ops (&vfs_quotactl_ops) +/* It is better to call this function outside of any transaction as it might + * need a lot of space in journal for dquot structure allocation. */ static __inline__ void DQUOT_INIT(struct inode *inode) { BUG_ON(!inode->i_sb); @@ -49,6 +66,7 @@ static __inline__ void DQUOT_INIT(struct inode->i_sb->dq_op->initialize(inode, -1); } +/* The same as with DQUOT_INIT */ static __inline__ void DQUOT_DROP(struct inode *inode) { if (IS_QUOTAINIT(inode)) { @@ -57,6 +75,8 @@ static __inline__ void DQUOT_DROP(struct } } +/* The following allocation/freeing/transfer functions *must* be called inside + * a transaction (deadlocks possible otherwise) */ static __inline__ int DQUOT_PREALLOC_SPACE_NODIRTY(struct inode *inode, qsize_t nr) { if (sb_any_quota_enabled(inode->i_sb)) { @@ -64,11 +84,8 @@ static __inline__ int DQUOT_PREALLOC_SPA if (inode->i_sb->dq_op->alloc_space(inode, nr, 1) == NO_QUOTA) return 1; } - else { - spin_lock(&dq_data_lock); + else inode_add_bytes(inode, nr); - spin_unlock(&dq_data_lock); - } return 0; } @@ -87,11 +104,8 @@ static __inline__ int DQUOT_ALLOC_SPACE_ if (inode->i_sb->dq_op->alloc_space(inode, nr, 0) == NO_QUOTA) return 1; } - else { - spin_lock(&dq_data_lock); + else inode_add_bytes(inode, nr); - spin_unlock(&dq_data_lock); - } return 0; } @@ -117,11 +131,8 @@ static __inline__ void DQUOT_FREE_SPACE_ { if (sb_any_quota_enabled(inode->i_sb)) inode->i_sb->dq_op->free_space(inode, nr); - else { - spin_lock(&dq_data_lock); + else inode_sub_bytes(inode, nr); - spin_unlock(&dq_data_lock); - } } static __inline__ void DQUOT_FREE_SPACE(struct inode *inode, qsize_t nr) @@ -146,6 +157,7 @@ static __inline__ int DQUOT_TRANSFER(str return 0; } +/* The following two functions cannot be called inside a transaction */ #define DQUOT_SYNC(sb) sync_dquots(sb, -1) static __inline__ int DQUOT_OFF(struct super_block *sb) --- linux-2.6.4-rc2/include/linux/sched.h 2004-03-03 23:12:48.000000000 -0800 +++ 25/include/linux/sched.h 2004-03-07 20:47:38.000000000 -0800 @@ -147,6 +147,7 @@ extern spinlock_t mmlist_lock; typedef struct task_struct task_t; extern void sched_init(void); +extern void sched_init_smp(void); extern void init_idle(task_t *idle, int cpu); extern void show_state(void); @@ -193,7 +194,7 @@ struct mm_struct { atomic_t mm_count; /* How many references to "struct mm_struct" (users count as 1) */ int map_count; /* number of VMAs */ struct rw_semaphore mmap_sem; - spinlock_t page_table_lock; /* Protects task page tables and mm->rss */ + spinlock_t page_table_lock; /* Protects task page tables and RSS data */ struct list_head mmlist; /* List of all active mm's. These are globally strung * together off init_mm.mmlist, and are protected @@ -203,7 +204,11 @@ struct mm_struct { unsigned long start_code, end_code, start_data, end_data; unsigned long start_brk, brk, start_stack; unsigned long arg_start, arg_end, env_start, env_end; - unsigned long rss, total_vm, locked_vm; + unsigned long total_vm, locked_vm; + unsigned long rss; +#ifdef CONFIG_NUMA + unsigned long pernode_rss[MAX_NUMNODES]; +#endif unsigned long def_flags; unsigned long saved_auxv[40]; /* for /proc/PID/auxv */ @@ -527,8 +532,111 @@ do { if (atomic_dec_and_test(&(tsk)->usa #define PF_SWAPOFF 0x00080000 /* I am in swapoff */ #define PF_LESS_THROTTLE 0x00100000 /* Throttle me less: I clean memory */ #define PF_SYNCWRITE 0x00200000 /* I am doing a sync write */ +#define PF_FUTEX_DEBUG 0x00400000 #ifdef CONFIG_SMP +#define SCHED_LOAD_SHIFT 7 /* increase resolution of load calculations */ +#define SCHED_LOAD_SCALE (1UL << SCHED_LOAD_SHIFT) + +#define SD_FLAG_NEWIDLE 1 /* Balance when about to become idle */ +#define SD_FLAG_EXEC 2 /* Balance on exec */ +#define SD_FLAG_WAKE 4 /* Balance on task wakeup */ +#define SD_FLAG_FASTMIGRATE 8 /* Sync wakes put task on waking CPU */ +#define SD_FLAG_SHARE_CPUPOWER 16 /* Domain members share cpu power */ + +struct sched_group { + struct sched_group *next; /* Must be a circular list */ + cpumask_t cpumask; + + /* + * CPU power of this group, SCHED_LOAD_SCALE being max power for a + * single CPU. This should be read only (except for setup). Although + * it will need to be written to at cpu hot(un)plug time, perhaps the + * cpucontrol semaphore will provide enough exclusion? + */ + unsigned long cpu_power; +}; + +struct sched_domain { + /* These fields must be setup */ + struct sched_domain *parent; /* top domain must be null terminated */ + struct sched_group *groups; /* the balancing groups of the domain */ + cpumask_t span; /* span of all CPUs in this domain */ + unsigned long min_interval; /* Minimum balance interval ms */ + unsigned long max_interval; /* Maximum balance interval ms */ + unsigned int busy_factor; /* less balancing by factor if busy */ + unsigned int imbalance_pct; /* No balance until over watermark */ + unsigned long long cache_hot_time; /* Task considered cache hot (ns) */ + unsigned int cache_nice_tries; /* Leave cache hot tasks for # tries */ + unsigned int per_cpu_gain; /* CPU % gained by adding domain cpus */ + int flags; /* See SD_FLAG_* */ + + /* Runtime fields. */ + unsigned long last_balance; /* init to jiffies. units in jiffies */ + unsigned int balance_interval; /* initialise to 1. units in ms. */ + unsigned int nr_balance_failed; /* initialise to 0 */ +}; + +/* Common values for SMT siblings */ +#define SD_SIBLING_INIT (struct sched_domain) { \ + .span = CPU_MASK_NONE, \ + .parent = NULL, \ + .groups = NULL, \ + .min_interval = 1, \ + .max_interval = 2, \ + .busy_factor = 8, \ + .imbalance_pct = 110, \ + .cache_hot_time = 0, \ + .cache_nice_tries = 0, \ + .per_cpu_gain = 15, \ + .flags = SD_FLAG_FASTMIGRATE | SD_FLAG_NEWIDLE | SD_FLAG_WAKE,\ + .last_balance = jiffies, \ + .balance_interval = 1, \ + .nr_balance_failed = 0, \ +} + +/* Common values for CPUs */ +#define SD_CPU_INIT (struct sched_domain) { \ + .span = CPU_MASK_NONE, \ + .parent = NULL, \ + .groups = NULL, \ + .min_interval = 1, \ + .max_interval = 4, \ + .busy_factor = 64, \ + .imbalance_pct = 125, \ + .cache_hot_time = (5*1000000/2), \ + .cache_nice_tries = 1, \ + .per_cpu_gain = 100, \ + .flags = SD_FLAG_FASTMIGRATE | SD_FLAG_NEWIDLE,\ + .last_balance = jiffies, \ + .balance_interval = 1, \ + .nr_balance_failed = 0, \ +} + +#ifdef CONFIG_NUMA +/* Common values for NUMA nodes */ +#define SD_NODE_INIT (struct sched_domain) { \ + .span = CPU_MASK_NONE, \ + .parent = NULL, \ + .groups = NULL, \ + .min_interval = 8, \ + .max_interval = 256*fls(num_online_cpus()),\ + .busy_factor = 8, \ + .imbalance_pct = 125, \ + .cache_hot_time = (10*1000000), \ + .cache_nice_tries = 1, \ + .per_cpu_gain = 100, \ + .flags = SD_FLAG_EXEC, \ + .last_balance = jiffies, \ + .balance_interval = 1, \ + .nr_balance_failed = 0, \ +} +#endif + +DECLARE_PER_CPU(struct sched_domain, base_domains); +#define cpu_sched_domain(cpu) (&per_cpu(base_domains, (cpu))) +#define this_sched_domain() (&__get_cpu_var(base_domains)) + extern int set_cpus_allowed(task_t *p, cpumask_t new_mask); #else static inline int set_cpus_allowed(task_t *p, cpumask_t new_mask) @@ -541,12 +649,14 @@ extern unsigned long long sched_clock(vo #ifdef CONFIG_NUMA extern void sched_balance_exec(void); -extern void node_nr_running_init(void); #else #define sched_balance_exec() {} -#define node_nr_running_init() {} #endif +/* Move tasks off this (offline) CPU onto another. */ +extern void migrate_all_tasks(void); +/* Try to move me here, if possible. */ +void migrate_to_cpu(int dest_cpu); extern void set_user_nice(task_t *p, long nice); extern int task_prio(task_t *p); extern int task_nice(task_t *p); @@ -602,7 +712,7 @@ extern void do_timer(struct pt_regs *); extern int FASTCALL(wake_up_state(struct task_struct * tsk, unsigned int state)); extern int FASTCALL(wake_up_process(struct task_struct * tsk)); #ifdef CONFIG_SMP - extern void FASTCALL(kick_process(struct task_struct * tsk)); + extern void kick_process(struct task_struct *tsk); #else static inline void kick_process(struct task_struct *tsk) { } #endif --- linux-2.6.4-rc2/include/linux/security.h 2004-03-03 23:12:48.000000000 -0800 +++ 25/include/linux/security.h 2004-03-07 20:47:55.000000000 -0800 @@ -177,7 +177,7 @@ struct swap_info_struct; * options cleanly (a filesystem may modify the data e.g. with strsep()). * This also allows the original mount data to be stripped of security- * specific options to avoid having to make filesystems aware of them. - * @fstype the type of filesystem being mounted. + * @type the type of filesystem being mounted. * @orig the original mount data copied from userspace. * @copy copied data which will be passed to the security module. * Returns 0 if the copy was successful. @@ -1033,7 +1033,8 @@ struct security_operations { int (*sb_alloc_security) (struct super_block * sb); void (*sb_free_security) (struct super_block * sb); - int (*sb_copy_data)(const char *fstype, void *orig, void *copy); + int (*sb_copy_data)(struct file_system_type *type, + void *orig, void *copy); int (*sb_kern_mount) (struct super_block *sb, void *data); int (*sb_statfs) (struct super_block * sb); int (*sb_mount) (char *dev_name, struct nameidata * nd, @@ -1318,9 +1319,10 @@ static inline void security_sb_free (str security_ops->sb_free_security (sb); } -static inline int security_sb_copy_data (const char *fstype, void *orig, void *copy) +static inline int security_sb_copy_data (struct file_system_type *type, + void *orig, void *copy) { - return security_ops->sb_copy_data (fstype, orig, copy); + return security_ops->sb_copy_data (type, orig, copy); } static inline int security_sb_kern_mount (struct super_block *sb, void *data) @@ -1988,7 +1990,8 @@ static inline int security_sb_alloc (str static inline void security_sb_free (struct super_block *sb) { } -static inline int security_sb_copy_data (const char *fstype, void *orig, void *copy) +static inline int security_sb_copy_data (struct file_system_type *type, + void *orig, void *copy) { return 0; } --- linux-2.6.4-rc2/include/linux/sem.h 2004-03-03 23:12:48.000000000 -0800 +++ 25/include/linux/sem.h 2004-03-07 20:47:48.000000000 -0800 @@ -134,7 +134,22 @@ struct sysv_sem { struct sem_undo_list *undo_list; }; -void exit_sem(struct task_struct *p); +#ifdef CONFIG_SYSVIPC + +extern int copy_semundo(unsigned long clone_flags, struct task_struct *tsk); +extern void exit_sem(struct task_struct *tsk); + +#else +static inline int copy_semundo(unsigned long clone_flags, struct task_struct *tsk) +{ + return 0; +} + +static inline void exit_sem(struct task_struct *tsk) +{ + return; +} +#endif #endif /* __KERNEL__ */ --- linux-2.6.4-rc2/include/linux/serial_core.h 2004-03-03 23:12:48.000000000 -0800 +++ 25/include/linux/serial_core.h 2004-03-07 20:47:02.000000000 -0800 @@ -84,6 +84,7 @@ #include #include #include +#include struct uart_port; struct uart_info; @@ -159,7 +160,9 @@ struct uart_port { unsigned char x_char; /* xon/xoff char */ unsigned char regshift; /* reg offset shift */ unsigned char iotype; /* io access style */ - +#ifdef CONFIG_KGDB + int kgdb; /* in use by kgdb */ +#endif #define UPIO_PORT (0) #define UPIO_HUB6 (1) #define UPIO_MEM (2) --- linux-2.6.4-rc2/include/linux/serio.h 2004-01-09 00:04:32.000000000 -0800 +++ 25/include/linux/serio.h 2004-03-07 20:46:53.000000000 -0800 @@ -117,6 +117,7 @@ static __inline__ void serio_cleanup(str #define SERIO_MZ 0x05 #define SERIO_MZP 0x06 #define SERIO_MZPP 0x07 +#define SERIO_VSXXXAA 0x08 #define SERIO_SUNKBD 0x10 #define SERIO_WARRIOR 0x18 #define SERIO_SPACEORB 0x19 @@ -134,6 +135,7 @@ static __inline__ void serio_cleanup(str #define SERIO_HIL 0x25 #define SERIO_SNES232 0x26 #define SERIO_SEMTECH 0x27 +#define SERIO_LKKBD 0x28 #define SERIO_ID 0xff00UL #define SERIO_EXTRA 0xff0000UL --- linux-2.6.4-rc2/include/linux/smp.h 2004-02-03 20:42:39.000000000 -0800 +++ 25/include/linux/smp.h 2004-03-07 20:46:46.000000000 -0800 @@ -30,7 +30,7 @@ extern void smp_send_stop(void); /* * sends a 'reschedule' event to another CPU: */ -extern void FASTCALL(smp_send_reschedule(int cpu)); +extern void smp_send_reschedule(int cpu); /* --- linux-2.6.4-rc2/include/linux/socket.h 2004-03-03 23:12:49.000000000 -0800 +++ 25/include/linux/socket.h 2004-03-07 20:48:02.000000000 -0800 @@ -178,7 +178,8 @@ struct ucred { #define AF_WANPIPE 25 /* Wanpipe API Sockets */ #define AF_LLC 26 /* Linux LLC */ #define AF_BLUETOOTH 31 /* Bluetooth sockets */ -#define AF_MAX 32 /* For now.. */ +#define AF_IPMI 32 /* IPMI sockers */ +#define AF_MAX 33 /* For now.. */ /* Protocol families, same as address families. */ #define PF_UNSPEC AF_UNSPEC @@ -210,6 +211,7 @@ struct ucred { #define PF_WANPIPE AF_WANPIPE #define PF_LLC AF_LLC #define PF_BLUETOOTH AF_BLUETOOTH +#define PF_IPMI AF_IPMI #define PF_MAX AF_MAX /* Maximum queue length specifiable by listen. */ --- linux-2.6.4-rc2/include/linux/spinlock.h 2003-11-09 16:45:05.000000000 -0800 +++ 25/include/linux/spinlock.h 2004-03-07 20:48:17.000000000 -0800 @@ -15,6 +15,12 @@ #include /* for cpu relax */ #include +#ifdef CONFIG_KGDB +#include +#define SET_WHO(x, him) (x)->who = him; +#else +#define SET_WHO(x, him) +#endif /* * Must define these before including other files, inline functions need them @@ -55,6 +61,9 @@ typedef struct { const char *module; char *owner; int oline; +#ifdef CONFIG_KGDB + struct task_struct *who; +#endif } spinlock_t; #define SPIN_LOCK_UNLOCKED (spinlock_t) { SPINLOCK_MAGIC, 0, 10, __FILE__ , NULL, 0} @@ -66,6 +75,7 @@ typedef struct { (x)->module = __FILE__; \ (x)->owner = NULL; \ (x)->oline = 0; \ + SET_WHO(x, NULL) \ } while (0) #define CHECK_LOCK(x) \ @@ -88,6 +98,7 @@ typedef struct { (x)->lock = 1; \ (x)->owner = __FILE__; \ (x)->oline = __LINE__; \ + SET_WHO(x, current) \ } while (0) /* without debugging, spin_is_locked on UP always says @@ -118,6 +129,7 @@ typedef struct { (x)->lock = 1; \ (x)->owner = __FILE__; \ (x)->oline = __LINE__; \ + SET_WHO(x, current) \ 1; \ }) @@ -184,6 +196,17 @@ typedef struct { #endif /* !SMP */ +#ifdef CONFIG_LOCKMETER +extern void _metered_spin_lock (spinlock_t *lock); +extern void _metered_spin_unlock (spinlock_t *lock); +extern int _metered_spin_trylock(spinlock_t *lock); +extern void _metered_read_lock (rwlock_t *lock); +extern void _metered_read_unlock (rwlock_t *lock); +extern void _metered_write_lock (rwlock_t *lock); +extern void _metered_write_unlock (rwlock_t *lock); +extern int _metered_write_trylock(rwlock_t *lock); +#endif + /* * Define the various spin_lock and rw_lock methods. Note we define these * regardless of whether CONFIG_SMP or CONFIG_PREEMPT are set. The various @@ -389,6 +412,141 @@ do { \ _raw_spin_trylock(lock) ? 1 : \ ({preempt_enable(); local_bh_enable(); 0;});}) +#ifdef CONFIG_LOCKMETER +#undef spin_lock +#undef spin_trylock +#undef spin_unlock +#undef spin_lock_irqsave +#undef spin_lock_irq +#undef spin_lock_bh +#undef read_lock +#undef read_unlock +#undef write_lock +#undef write_unlock +#undef write_trylock +#undef spin_unlock_bh +#undef read_lock_irqsave +#undef read_lock_irq +#undef read_lock_bh +#undef read_unlock_bh +#undef write_lock_irqsave +#undef write_lock_irq +#undef write_lock_bh +#undef write_unlock_bh + +#define spin_lock(lock) \ +do { \ + preempt_disable(); \ + _metered_spin_lock(lock); \ +} while(0) + +#define spin_trylock(lock) ({preempt_disable(); _metered_spin_trylock(lock) ? \ + 1 : ({preempt_enable(); 0;});}) +#define spin_unlock(lock) \ +do { \ + _metered_spin_unlock(lock); \ + preempt_enable(); \ +} while (0) + +#define spin_lock_irqsave(lock, flags) \ +do { \ + local_irq_save(flags); \ + preempt_disable(); \ + _metered_spin_lock(lock); \ +} while (0) + +#define spin_lock_irq(lock) \ +do { \ + local_irq_disable(); \ + preempt_disable(); \ + _metered_spin_lock(lock); \ +} while (0) + +#define spin_lock_bh(lock) \ +do { \ + local_bh_disable(); \ + preempt_disable(); \ + _metered_spin_lock(lock); \ +} while (0) + +#define spin_unlock_bh(lock) \ +do { \ + _metered_spin_unlock(lock); \ + preempt_enable(); \ + local_bh_enable(); \ +} while (0) + + +#define read_lock(lock) ({preempt_disable(); _metered_read_lock(lock);}) +#define read_unlock(lock) ({_metered_read_unlock(lock); preempt_enable();}) +#define write_lock(lock) ({preempt_disable(); _metered_write_lock(lock);}) +#define write_unlock(lock) ({_metered_write_unlock(lock); preempt_enable();}) +#define write_trylock(lock) ({preempt_disable();_metered_write_trylock(lock) ? \ + 1 : ({preempt_enable(); 0;});}) +#define spin_unlock_no_resched(lock) \ +do { \ + _metered_spin_unlock(lock); \ + preempt_enable_no_resched(); \ +} while (0) + +#define read_lock_irqsave(lock, flags) \ +do { \ + local_irq_save(flags); \ + preempt_disable(); \ + _metered_read_lock(lock); \ +} while (0) + +#define read_lock_irq(lock) \ +do { \ + local_irq_disable(); \ + preempt_disable(); \ + _metered_read_lock(lock); \ +} while (0) + +#define read_lock_bh(lock) \ +do { \ + local_bh_disable(); \ + preempt_disable(); \ + _metered_read_lock(lock); \ +} while (0) + +#define read_unlock_bh(lock) \ +do { \ + _metered_read_unlock(lock); \ + preempt_enable(); \ + local_bh_enable(); \ +} while (0) + +#define write_lock_irqsave(lock, flags) \ +do { \ + local_irq_save(flags); \ + preempt_disable(); \ + _metered_write_lock(lock); \ +} while (0) + +#define write_lock_irq(lock) \ +do { \ + local_irq_disable(); \ + preempt_disable(); \ + _metered_write_lock(lock); \ +} while (0) + +#define write_lock_bh(lock) \ +do { \ + local_bh_disable(); \ + preempt_disable(); \ + _metered_write_lock(lock); \ +} while (0) + +#define write_unlock_bh(lock) \ +do { \ + _metered_write_unlock(lock); \ + preempt_enable(); \ + local_bh_enable(); \ +} while (0) + +#endif /* !CONFIG_LOCKMETER */ + /* "lock on reference count zero" */ #ifndef ATOMIC_DEC_AND_LOCK #include --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/include/linux/stop_machine.h 2004-03-07 20:47:22.000000000 -0800 @@ -0,0 +1,52 @@ +#ifndef _LINUX_STOP_MACHINE +#define _LINUX_STOP_MACHINE +/* "Bogolock": stop the entire machine, disable interrupts. This is a + very heavy lock, which is equivalent to grabbing every spinlock + (and more). So the "read" side to such a lock is anything which + diables preeempt. */ +#include +#include +#include + +#ifdef CONFIG_SMP +/** + * stop_machine_run: freeze the machine on all CPUs and run this function + * @fn: the function to run + * @data: the data ptr for the @fn() + * @cpu: the cpu to run @fn() on (or any, if @cpu == NR_CPUS. + * + * Description: This causes a thread to be scheduled on every other cpu, + * each of which disables interrupts, and finally interrupts are disabled + * on the current CPU. The result is that noone is holding a spinlock + * or inside any other preempt-disabled region when @fn() runs. + * + * This can be thought of as a very heavy write lock, equivalent to + * grabbing every spinlock in the kernel. */ +int stop_machine_run(int (*fn)(void *), void *data, unsigned int cpu); + +/** + * __stop_machine_run: freeze the machine on all CPUs and run this function + * @fn: the function to run + * @data: the data ptr for the @fn + * @cpu: the cpu to run @fn on (or any, if @cpu == NR_CPUS. + * + * Description: This is a special version of the above, which returns the + * thread which has run @fn(): kthread_stop will return the return value + * of @fn(). Used by hotplug cpu. + */ +struct task_struct *__stop_machine_run(int (*fn)(void *), void *data, + unsigned int cpu); + +#else + +static inline int stop_machine_run(int (*fn)(void *), void *data, + unsigned int cpu) +{ + int ret; + local_irq_disable(); + ret = fn(data); + local_irq_enable(); + return ret; +} +#endif /* CONFIG_SMP */ +#endif /* _LINUX_STOP_MACHINE */ --- linux-2.6.4-rc2/include/linux/sunrpc/debug.h 2003-07-02 14:53:18.000000000 -0700 +++ 25/include/linux/sunrpc/debug.h 2004-03-07 20:47:33.000000000 -0800 @@ -54,7 +54,7 @@ extern unsigned int nlm_debug; #undef ifdebug #ifdef RPC_DEBUG -# define ifdebug(fac) if (rpc_debug & RPCDBG_##fac) +# define ifdebug(fac) if (unlikely(rpc_debug & RPCDBG_##fac)) # define dfprintk(fac, args...) do { ifdebug(fac) printk(args); } while(0) # define RPC_IFDEBUG(x) x #else @@ -92,6 +92,8 @@ enum { CTL_NFSDEBUG, CTL_NFSDDEBUG, CTL_NLMDEBUG, + CTL_SLOTTABLE_UDP, + CTL_SLOTTABLE_TCP, }; #endif /* _LINUX_SUNRPC_DEBUG_H_ */ --- linux-2.6.4-rc2/include/linux/sunrpc/timer.h 2003-10-08 15:07:10.000000000 -0700 +++ 25/include/linux/sunrpc/timer.h 2004-03-07 20:47:29.000000000 -0800 @@ -25,9 +25,18 @@ extern unsigned long rpc_calc_rto(struct static inline void rpc_set_timeo(struct rpc_rtt *rt, int timer, int ntimeo) { + int *t; if (!timer) return; - rt->ntimeouts[timer-1] = ntimeo; + t = &rt->ntimeouts[timer-1]; + if (ntimeo < *t) { + if (*t > 0) + (*t)--; + } else { + if (ntimeo > 8) + ntimeo = 8; + *t = ntimeo; + } } static inline int rpc_ntimeo(struct rpc_rtt *rt, int timer) --- linux-2.6.4-rc2/include/linux/sunrpc/xdr.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/linux/sunrpc/xdr.h 2004-03-07 20:47:33.000000000 -0800 @@ -87,7 +87,7 @@ struct xdr_buf { /* * Miscellaneous XDR helper functions */ -u32 * xdr_encode_array(u32 *p, const char *s, unsigned int len); +u32 * xdr_encode_array(u32 *p, const void *s, unsigned int len); u32 * xdr_encode_string(u32 *p, const char *s); u32 * xdr_decode_string(u32 *p, char **sp, int *lenp, int maxlen); u32 * xdr_decode_string_inplace(u32 *p, char **sp, int *lenp, int maxlen); --- linux-2.6.4-rc2/include/linux/sunrpc/xprt.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/linux/sunrpc/xprt.h 2004-03-07 20:47:33.000000000 -0800 @@ -28,16 +28,18 @@ * * Upper procedures may check whether a request would block waiting for * a free RPC slot by using the RPC_CONGESTED() macro. - * - * Note: on machines with low memory we should probably use a smaller - * MAXREQS value: At 32 outstanding reqs with 8 megs of RAM, fragment - * reassembly will frequently run out of memory. - */ -#define RPC_MAXCONG (16) -#define RPC_MAXREQS RPC_MAXCONG -#define RPC_CWNDSCALE (256) -#define RPC_MAXCWND (RPC_MAXCONG * RPC_CWNDSCALE) + */ +extern unsigned int xprt_udp_slot_table_entries; +extern unsigned int xprt_tcp_slot_table_entries; + +#define RPC_MIN_SLOT_TABLE (2U) +#define RPC_DEF_SLOT_TABLE (16U) +#define RPC_MAX_SLOT_TABLE (128U) + +#define RPC_CWNDSHIFT (8U) +#define RPC_CWNDSCALE (1U << RPC_CWNDSHIFT) #define RPC_INITCWND RPC_CWNDSCALE +#define RPC_MAXCWND(xprt) ((xprt)->max_reqs << RPC_CWNDSHIFT) #define RPCXPRT_CONGESTED(xprt) ((xprt)->cong >= (xprt)->cwnd) /* Default timeout values */ @@ -92,7 +94,6 @@ struct rpc_rqst { */ struct rpc_task * rq_task; /* RPC task data */ __u32 rq_xid; /* request XID */ - struct rpc_rqst * rq_next; /* free list */ int rq_cong; /* has incremented xprt->cong */ int rq_received; /* receive completed */ u32 rq_seqno; /* gss seq no. used on req. */ @@ -102,7 +103,6 @@ struct rpc_rqst { struct xdr_buf rq_private_buf; /* The receive buffer * used in the softirq. */ - /* * For authentication (e.g. auth_des) */ @@ -146,8 +146,9 @@ struct rpc_xprt { struct rpc_wait_queue resend; /* requests waiting to resend */ struct rpc_wait_queue pending; /* requests in flight */ struct rpc_wait_queue backlog; /* waiting for slot */ - struct rpc_rqst * free; /* free slots */ - struct rpc_rqst slot[RPC_MAXREQS]; + struct list_head free; /* free slots */ + struct rpc_rqst * slot; /* slot table storage */ + unsigned int max_reqs; /* total slots */ unsigned long sockstate; /* Socket state */ unsigned char shutdown : 1, /* being shut down */ nocong : 1, /* no congestion control */ @@ -155,6 +156,11 @@ struct rpc_xprt { stream : 1; /* TCP */ /* + * XID + */ + __u32 xid; /* Next XID value to use */ + + /* * State of TCP reply receive stuff */ u32 tcp_recm, /* Fragment header */ @@ -164,6 +170,11 @@ struct rpc_xprt { unsigned long tcp_copied, /* copied to request */ tcp_flags; /* + * Connection of sockets + */ + struct work_struct sock_connect; + unsigned short port; + /* * Disconnection of idle sockets */ struct work_struct task_cleanup; --- linux-2.6.4-rc2/include/linux/syscalls.h 2004-03-03 23:12:49.000000000 -0800 +++ 25/include/linux/syscalls.h 2004-03-07 20:48:04.000000000 -0800 @@ -48,6 +48,8 @@ struct timex; struct timezone; struct tms; struct utimbuf; +typedef int mqd_t; +struct mq_attr; #include #include @@ -252,7 +254,10 @@ asmlinkage long sys_mprotect(unsigned lo asmlinkage unsigned long sys_mremap(unsigned long addr, unsigned long old_len, unsigned long new_len, unsigned long flags, unsigned long new_addr); -long sys_remap_file_pages(unsigned long start, unsigned long size, +asmlinkage long old_remap_file_pages(unsigned long start, unsigned long size, + unsigned long __prot, unsigned long pgoff, + unsigned long flags); +asmlinkage long sys_remap_file_pages(unsigned long start, unsigned long size, unsigned long prot, unsigned long pgoff, unsigned long flags); asmlinkage long sys_msync(unsigned long start, size_t len, int flags); @@ -386,24 +391,24 @@ asmlinkage long sys_getdents64(unsigned unsigned int count); asmlinkage long sys_setsockopt(int fd, int level, int optname, - char *optval, int optlen); + char __user *optval, int optlen); asmlinkage long sys_getsockopt(int fd, int level, int optname, char __user *optval, int __user *optlen); -asmlinkage long sys_bind(int, struct sockaddr *, int); -asmlinkage long sys_connect(int, struct sockaddr *, int); -asmlinkage long sys_accept(int, struct sockaddr *, int *); -asmlinkage long sys_getsockname(int, struct sockaddr *, int *); -asmlinkage long sys_getpeername(int, struct sockaddr *, int *); -asmlinkage long sys_send(int, void *, size_t, unsigned); -asmlinkage long sys_sendto(int, void *, size_t, unsigned, - struct sockaddr *, int); +asmlinkage long sys_bind(int, struct sockaddr __user *, int); +asmlinkage long sys_connect(int, struct sockaddr __user *, int); +asmlinkage long sys_accept(int, struct sockaddr __user *, int __user *); +asmlinkage long sys_getsockname(int, struct sockaddr __user *, int __user *); +asmlinkage long sys_getpeername(int, struct sockaddr __user *, int __user *); +asmlinkage long sys_send(int, void __user *, size_t, unsigned); +asmlinkage long sys_sendto(int, void __user *, size_t, unsigned, + struct sockaddr __user *, int); asmlinkage long sys_sendmsg(int fd, struct msghdr __user *msg, unsigned flags); -asmlinkage long sys_recv(int, void *, size_t, unsigned); -asmlinkage long sys_recvfrom(int, void *, size_t, unsigned, - struct sockaddr *, int *); +asmlinkage long sys_recv(int, void __user *, size_t, unsigned); +asmlinkage long sys_recvfrom(int, void __user *, size_t, unsigned, + struct sockaddr __user *, int __user *); asmlinkage long sys_recvmsg(int fd, struct msghdr __user *msg, unsigned flags); asmlinkage long sys_socket(int, int, int); -asmlinkage long sys_socketpair(int, int, int, int [2]); +asmlinkage long sys_socketpair(int, int, int, int __user *); asmlinkage long sys_socketcall(int call, unsigned long __user *args); asmlinkage long sys_listen(int, int); asmlinkage long sys_poll(struct pollfd __user *ufds, unsigned int nfds, @@ -450,6 +455,13 @@ asmlinkage long sys_shmget(key_t key, si asmlinkage long sys_shmdt(char __user *shmaddr); asmlinkage long sys_shmctl(int shmid, int cmd, struct shmid_ds __user *buf); +asmlinkage long sys_mq_open(const char __user *name, int oflag, mode_t mode, struct mq_attr __user *attr); +asmlinkage long sys_mq_unlink(const char __user *name); +asmlinkage long sys_mq_timedsend(mqd_t mqdes, const char __user *msg_ptr, size_t msg_len, unsigned int msg_prio, const struct timespec __user *abs_timeout); +asmlinkage ssize_t sys_mq_timedreceive(mqd_t mqdes, char __user *msg_ptr, size_t msg_len, unsigned int __user *msg_prio, const struct timespec __user *abs_timeout); +asmlinkage long sys_mq_notify(mqd_t mqdes, const struct sigevent __user *notification); +asmlinkage long sys_mq_getsetattr(mqd_t mqdes, const struct mq_attr __user *mqstat, struct mq_attr __user *omqstat); + asmlinkage long sys_pciconfig_iobase(long which, unsigned long bus, unsigned long devfn); asmlinkage long sys_pciconfig_read(unsigned long bus, unsigned long dfn, unsigned long off, unsigned long len, --- linux-2.6.4-rc2/include/linux/sysctl.h 2004-03-03 23:12:49.000000000 -0800 +++ 25/include/linux/sysctl.h 2004-03-07 20:47:20.000000000 -0800 @@ -158,6 +158,8 @@ enum VM_SWAPPINESS=19, /* Tendency to steal mapped memory */ VM_LOWER_ZONE_PROTECTION=20,/* Amount of protection of lower zones */ VM_MIN_FREE_KBYTES=21, /* Minimum free kilobytes to maintain */ + VM_LAPTOP_MODE=22, /* vm laptop mode */ + VM_BLOCK_DUMP=23, /* block dump mode */ }; @@ -321,6 +323,7 @@ enum NET_TCP_LOW_LATENCY=93, NET_IPV4_IPFRAG_SECRET_INTERVAL=94, NET_TCP_WESTWOOD=95, + NET_IPV4_IGMP_MAX_MSF=96, }; enum { --- linux-2.6.4-rc2/include/linux/sysfs.h 2003-11-09 16:45:05.000000000 -0800 +++ 25/include/linux/sysfs.h 2004-03-07 20:47:12.000000000 -0800 @@ -18,6 +18,12 @@ struct attribute { mode_t mode; }; +struct attribute_group { + char * name; + struct attribute ** attrs; +}; + + struct bin_attribute { struct attribute attr; size_t size; @@ -25,14 +31,13 @@ struct bin_attribute { ssize_t (*write)(struct kobject *, char *, loff_t, size_t); }; -int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr); -int sysfs_remove_bin_file(struct kobject * kobj, struct bin_attribute * attr); - struct sysfs_ops { ssize_t (*show)(struct kobject *, struct attribute *,char *); ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t); }; +#ifdef CONFIG_SYSFS + extern int sysfs_create_dir(struct kobject *); @@ -57,13 +62,75 @@ sysfs_create_link(struct kobject * kobj, extern void sysfs_remove_link(struct kobject *, char * name); - -struct attribute_group { - char * name; - struct attribute ** attrs; -}; +int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr); +int sysfs_remove_bin_file(struct kobject * kobj, struct bin_attribute * attr); int sysfs_create_group(struct kobject *, const struct attribute_group *); void sysfs_remove_group(struct kobject *, const struct attribute_group *); +#else /* CONFIG_SYSFS */ + +static inline int sysfs_create_dir(struct kobject * k) +{ + return 0; +} + +static inline void sysfs_remove_dir(struct kobject * k) +{ + ; +} + +static inline void sysfs_rename_dir(struct kobject * k, const char *new_name) +{ + ; +} + +static inline int sysfs_create_file(struct kobject * k, const struct attribute * a) +{ + return 0; +} + +static inline int sysfs_update_file(struct kobject * k, const struct attribute * a) +{ + return 0; +} + +static inline void sysfs_remove_file(struct kobject * k, const struct attribute * a) +{ + ; +} + +static inline int sysfs_create_link(struct kobject * k, struct kobject * t, char * n) +{ + return 0; +} + +static inline void sysfs_remove_link(struct kobject * k, char * name) +{ + ; +} + + +static inline int sysfs_create_bin_file(struct kobject * k, struct bin_attribute * a) +{ + return 0; +} + +static inline int sysfs_remove_bin_file(struct kobject * k, struct bin_attribute * a) +{ + return 0; +} + +static inline int sysfs_create_group(struct kobject * k, const struct attribute_group *g) +{ + return 0; +} + +static inline void sysfs_remove_group(struct kobject * k, const struct attribute_group * g) +{ + ; +} + +#endif /* CONFIG_SYSFS */ + #endif /* _SYSFS_H_ */ --- linux-2.6.4-rc2/include/linux/topology.h 2003-08-22 19:23:42.000000000 -0700 +++ 25/include/linux/topology.h 2004-03-07 20:47:57.000000000 -0800 @@ -54,4 +54,11 @@ static inline int __next_node_with_cpus( #define for_each_node_with_cpus(node) \ for (node = 0; node < numnodes; node = __next_node_with_cpus(node)) +#ifndef node_distance +#define node_distance(from,to) (from != to) +#endif +#ifndef PENALTY_FOR_NODE_WITH_CPUS +#define PENALTY_FOR_NODE_WITH_CPUS (1) +#endif + #endif /* _LINUX_TOPOLOGY_H */ --- linux-2.6.4-rc2/include/linux/udf_fs.h 2003-06-14 12:17:59.000000000 -0700 +++ 25/include/linux/udf_fs.h 2004-03-07 20:47:56.000000000 -0800 @@ -8,7 +8,7 @@ * OSTA-UDF(tm) = Optical Storage Technology Association * Universal Disk Format. * - * This code is based on version 2.00 of the UDF specification, + * This code is based on version 2.50 of the UDF specification, * and revision 3 of the ECMA 167 standard [equivalent to ISO 13346]. * http://www.osta.org/ * http://www.ecma.ch/ * http://www.iso.org/ @@ -24,7 +24,7 @@ * ftp://prep.ai.mit.edu/pub/gnu/GPL * Each contributing author retains all rights to their own work. * - * (C) 1999-2000 Ben Fennema + * (C) 1999-2004 Ben Fennema * (C) 1999-2000 Stelias Computing Inc * * HISTORY @@ -37,8 +37,8 @@ #define UDF_PREALLOCATE #define UDF_DEFAULT_PREALLOC_BLOCKS 8 -#define UDFFS_DATE "2002/11/15" -#define UDFFS_VERSION "0.9.7" +#define UDFFS_DATE "2004/29/09" +#define UDFFS_VERSION "0.9.8.1" #define UDFFS_DEBUG --- linux-2.6.4-rc2/include/linux/ufs_fs.h 2003-06-14 12:18:30.000000000 -0700 +++ 25/include/linux/ufs_fs.h 2004-03-07 20:47:39.000000000 -0800 @@ -22,6 +22,9 @@ * HP/UX hfs filesystem support added by * Martin K. Petersen , August 1999 * + * UFS2 (of FreeBSD 5.x) support added by + * Niraj Kumar , Jan 2004 + * */ #ifndef __LINUX_UFS_FS_H @@ -43,8 +46,50 @@ #define UFS_SECTOR_SIZE 512 #define UFS_SECTOR_BITS 9 -#define UFS_MAGIC 0x00011954 -#define UFS_CIGAM 0x54190100 /* byteswapped MAGIC */ +#define UFS_MAGIC 0x00011954 +#define UFS2_MAGIC 0x19540119 +#define UFS_CIGAM 0x54190100 /* byteswapped MAGIC */ + +/* Copied from FreeBSD */ +/* + * Each disk drive contains some number of filesystems. + * A filesystem consists of a number of cylinder groups. + * Each cylinder group has inodes and data. + * + * A filesystem is described by its super-block, which in turn + * describes the cylinder groups. The super-block is critical + * data and is replicated in each cylinder group to protect against + * catastrophic loss. This is done at `newfs' time and the critical + * super-block data does not change, so the copies need not be + * referenced further unless disaster strikes. + * + * For filesystem fs, the offsets of the various blocks of interest + * are given in the super block as: + * [fs->fs_sblkno] Super-block + * [fs->fs_cblkno] Cylinder group block + * [fs->fs_iblkno] Inode blocks + * [fs->fs_dblkno] Data blocks + * The beginning of cylinder group cg in fs, is given by + * the ``cgbase(fs, cg)'' macro. + * + * Depending on the architecture and the media, the superblock may + * reside in any one of four places. For tiny media where every block + * counts, it is placed at the very front of the partition. Historically, + * UFS1 placed it 8K from the front to leave room for the disk label and + * a small bootstrap. For UFS2 it got moved to 64K from the front to leave + * room for the disk label and a bigger bootstrap, and for really piggy + * systems we check at 256K from the front if the first three fail. In + * all cases the size of the superblock will be SBLOCKSIZE. All values are + * given in byte-offset form, so they do not imply a sector size. The + * SBLOCKSEARCH specifies the order in which the locations should be searched. + */ +#define SBLOCK_FLOPPY 0 +#define SBLOCK_UFS1 8192 +#define SBLOCK_UFS2 65536 +#define SBLOCK_PIGGY 262144 +#define SBLOCKSIZE 8192 +#define SBLOCKSEARCH \ + { SBLOCK_UFS2, SBLOCK_UFS1, SBLOCK_FLOPPY, SBLOCK_PIGGY, -1 } /* HP specific MAGIC values */ @@ -120,6 +165,11 @@ #define UFS_CG_OLD 0x00000000 #define UFS_CG_44BSD 0x00002000 #define UFS_CG_SUN 0x00001000 +/* filesystem type encoding */ +#define UFS_TYPE_MASK 0x00010000 /* mask for the following */ +#define UFS_TYPE_UFS1 0x00000000 +#define UFS_TYPE_UFS2 0x00010000 + /* fs_inodefmt options */ #define UFS_42INODEFMT -1 @@ -132,7 +182,7 @@ #define UFS_MOUNT_ONERROR_UMOUNT 0x00000004 #define UFS_MOUNT_ONERROR_REPAIR 0x00000008 -#define UFS_MOUNT_UFSTYPE 0x00000FF0 +#define UFS_MOUNT_UFSTYPE 0x0000FFF0 #define UFS_MOUNT_UFSTYPE_OLD 0x00000010 #define UFS_MOUNT_UFSTYPE_44BSD 0x00000020 #define UFS_MOUNT_UFSTYPE_SUN 0x00000040 @@ -141,6 +191,7 @@ #define UFS_MOUNT_UFSTYPE_OPENSTEP 0x00000200 #define UFS_MOUNT_UFSTYPE_SUNx86 0x00000400 #define UFS_MOUNT_UFSTYPE_HP 0x00000800 +#define UFS_MOUNT_UFSTYPE_UFS2 0x00001000 #define ufs_clear_opt(o,opt) o &= ~UFS_MOUNT_##opt #define ufs_set_opt(o,opt) o |= UFS_MOUNT_##opt @@ -173,7 +224,8 @@ * They calc file system addresses of cylinder group data structures. */ #define ufs_cgbase(c) (uspi->s_fpg * (c)) -#define ufs_cgstart(c) (ufs_cgbase(c) + uspi->s_cgoffset * ((c) & ~uspi->s_cgmask)) +#define ufs_cgstart(c) ((uspi)->fs_magic == UFS2_MAGIC ? ufs_cgbase(c) : \ + (ufs_cgbase(c) + uspi->s_cgoffset * ((c) & ~uspi->s_cgmask))) #define ufs_cgsblock(c) (ufs_cgstart(c) + uspi->s_sblkno) /* super blk */ #define ufs_cgcmin(c) (ufs_cgstart(c) + uspi->s_cblkno) /* cg block */ #define ufs_cgimin(c) (ufs_cgstart(c) + uspi->s_iblkno) /* inode blk */ @@ -227,8 +279,14 @@ #define UFS_MAXNAMLEN 255 #define UFS_MAXMNTLEN 512 +#define UFS2_MAXMNTLEN 468 +#define UFS2_MAXVOLLEN 32 /* #define UFS_MAXCSBUFS 31 */ #define UFS_LINK_MAX 32000 +/* +#define UFS2_NOCSPTRS ((128 / sizeof(void *)) - 4) +*/ +#define UFS2_NOCSPTRS 28 /* * UFS_DIR_PAD defines the directory entries boundaries @@ -262,6 +320,14 @@ struct ufs_csum { __u32 cs_nifree; /* number of free inodes */ __u32 cs_nffree; /* number of free frags */ }; +struct ufs2_csum_total { + __u64 cs_ndir; /* number of directories */ + __u64 cs_nbfree; /* number of free blocks */ + __u64 cs_nifree; /* number of free inodes */ + __u64 cs_nffree; /* number of free frags */ + __u64 cs_numclusters; /* number of free clusters */ + __u64 cs_spare[3]; /* future expansion */ +}; /* * This is the actual superblock, as it is laid out on the disk. @@ -333,7 +399,7 @@ struct ufs_super_block { __u32 fs_ncyl; /* cylinders in file system */ /* these fields can be computed from the others */ __u32 fs_cpg; /* cylinders per group */ - __u32 fs_ipg; /* inodes per group */ + __u32 fs_ipg; /* inodes per cylinder group */ __u32 fs_fpg; /* blocks per group * fs_frag */ /* this data must be re-computed after crashes */ struct ufs_csum fs_cstotal; /* cylinder summary information */ @@ -342,13 +408,39 @@ struct ufs_super_block { __s8 fs_clean; /* file system is clean flag */ __s8 fs_ronly; /* mounted read-only flag */ __s8 fs_flags; /* currently unused flag */ - __s8 fs_fsmnt[UFS_MAXMNTLEN]; /* name mounted on */ -/* these fields retain the current block allocation info */ - __u32 fs_cgrotor; /* last cg searched */ - __u32 fs_csp[UFS_MAXCSBUFS]; /* list of fs_cs info buffers */ - __u32 fs_maxcluster; - __u32 fs_cpc; /* cyl per cycle in postbl */ - __u16 fs_opostbl[16][8]; /* old rotation block list head */ + union { + struct { + __s8 fs_fsmnt[UFS_MAXMNTLEN];/* name mounted on */ + __u32 fs_cgrotor; /* last cg searched */ + __u32 fs_csp[UFS_MAXCSBUFS];/*list of fs_cs info buffers */ + __u32 fs_maxcluster; + __u32 fs_cpc; /* cyl per cycle in postbl */ + __u16 fs_opostbl[16][8]; /* old rotation block list head */ + } fs_u1; + struct { + __s8 fs_fsmnt[UFS2_MAXMNTLEN]; /* name mounted on */ + __u8 fs_volname[UFS2_MAXVOLLEN]; /* volume name */ + __u64 fs_swuid; /* system-wide uid */ + __s32 fs_pad; /* due to alignment of fs_swuid */ + __u32 fs_cgrotor; /* last cg searched */ + __u32 fs_ocsp[UFS2_NOCSPTRS]; /*list of fs_cs info buffers */ + __u32 fs_contigdirs;/*# of contiguously allocated dirs */ + __u32 fs_csp; /* cg summary info buffer for fs_cs */ + __u32 fs_maxcluster; + __u32 fs_active;/* used by snapshots to track fs */ + __s32 fs_old_cpc; /* cyl per cycle in postbl */ + __s32 fs_maxbsize;/*maximum blocking factor permitted */ + __s64 fs_sparecon64[17];/*old rotation block list head */ + __s64 fs_sblockloc; /* byte offset of standard superblock */ + struct ufs2_csum_total fs_cstotal;/*cylinder summary information*/ + struct ufs_timeval fs_time; /* last time written */ + __s64 fs_size; /* number of blocks in fs */ + __s64 fs_dsize; /* number of data blocks in fs */ + __u64 fs_csaddr; /* blk addr of cyl grp summary area */ + __s64 fs_pendingblocks;/* blocks in process of being freed */ + __s32 fs_pendinginodes;/*inodes in process of being freed */ + } fs_u2; + } fs_u11; union { struct { __s32 fs_sparecon[53];/* reserved for future constants */ @@ -441,6 +533,16 @@ struct ufs_cylinder_group { __u32 cg_nclusterblks; /* number of clusters this cg */ __u32 cg_sparecon[13]; /* reserved for future use */ } cg_44; + struct { + __u32 cg_clustersumoff;/* (u_int32) counts of avail clusters */ + __u32 cg_clusteroff; /* (u_int8) free cluster map */ + __u32 cg_nclusterblks;/* number of clusters this cg */ + __u32 cg_niblk; /* number of inode blocks this cg */ + __u32 cg_initediblk; /* last initialized inode */ + __u32 cg_sparecon32[3];/* reserved for future use */ + __u64 cg_time; /* time last written */ + __u64 cg_sparecon[3]; /* reserved for future use */ + } cg_u2; __u32 cg_sparecon[16]; /* reserved for future use */ } cg_u; __u8 cg_space[1]; /* space for cylinder group maps */ @@ -497,6 +599,39 @@ struct ufs_inode { } ui_u3; }; +#define UFS_NXADDR 2 /* External addresses in inode. */ +struct ufs2_inode { + __u16 ui_mode; /* 0: IFMT, permissions; see below. */ + __s16 ui_nlink; /* 2: File link count. */ + __u32 ui_uid; /* 4: File owner. */ + __u32 ui_gid; /* 8: File group. */ + __u32 ui_blksize; /* 12: Inode blocksize. */ + __u64 ui_size; /* 16: File byte count. */ + __u64 ui_blocks; /* 24: Bytes actually held. */ + struct ufs_timeval ui_atime; /* 32: Last access time. */ + struct ufs_timeval ui_mtime; /* 40: Last modified time. */ + struct ufs_timeval ui_ctime; /* 48: Last inode change time. */ + struct ufs_timeval ui_birthtime; /* 56: Inode creation time. */ + __s32 ui_mtimensec; /* 64: Last modified time. */ + __s32 ui_atimensec; /* 68: Last access time. */ + __s32 ui_ctimensec; /* 72: Last inode change time. */ + __s32 ui_birthnsec; /* 76: Inode creation time. */ + __s32 ui_gen; /* 80: Generation number. */ + __u32 ui_kernflags; /* 84: Kernel flags. */ + __u32 ui_flags; /* 88: Status flags (chflags). */ + __s32 ui_extsize; /* 92: External attributes block. */ + __s64 ui_extb[UFS_NXADDR];/* 96: External attributes block. */ + union { + struct { + __s64 ui_db[UFS_NDADDR]; /* 112: Direct disk blocks. */ + __s64 ui_ib[UFS_NINDIR];/* 208: Indirect disk blocks.*/ + } ui_addr; + __u8 ui_symlink[2*4*(UFS_NDADDR+UFS_NINDIR)];/* 0x28 fast symlink */ + } ui_u2; + __s64 ui_spare[3]; /* 232: Reserved; currently unused */ +}; + + /* FreeBSD has these in sys/stat.h */ /* ui_flags that can be set by a file owner */ #define UFS_UF_SETTABLE 0x0000ffff @@ -517,8 +652,8 @@ struct ufs_inode { * than the size of fragment. */ struct ufs_buffer_head { - unsigned fragment; /* first fragment */ - unsigned count; /* number of fragments */ + __u64 fragment; /* first fragment */ + __u64 count; /* number of fragments */ struct buffer_head * bh[UFS_MAXFRAG]; /* buffers */ }; @@ -551,6 +686,8 @@ struct ufs_sb_private_info { __u32 s_cgmask; /* used to calc mod fs_ntrak */ __u32 s_size; /* number of blocks (fragments) in fs */ __u32 s_dsize; /* number of data blocks in fs */ + __u64 s_u2_size; /* ufs2: number of blocks (fragments) in fs */ + __u64 s_u2_dsize; /*ufs2: number of data blocks in fs */ __u32 s_ncg; /* number of cylinder groups */ __u32 s_bsize; /* size of basic blocks */ __u32 s_fsize; /* size of fragments */ @@ -577,7 +714,7 @@ struct ufs_sb_private_info { __u32 s_ntrak; /* tracks per cylinder */ __u32 s_nsect; /* sectors per track */ __u32 s_spc; /* sectors per cylinder */ - __u32 s_ipg; /* inodes per group */ + __u32 s_ipg; /* inodes per cylinder group */ __u32 s_fpg; /* fragments per group */ __u32 s_cpc; /* cyl per cycle in postbl */ __s32 s_contigsumsize;/* size of cluster summary array, 44bsd */ @@ -605,6 +742,7 @@ struct ufs_sb_private_info { __u32 s_bpfmask; /* bits per fragment mask */ __u32 s_maxsymlinklen;/* upper limit on fast symlinks' size */ + __s32 fs_magic; /* filesystem magic */ }; /* @@ -758,7 +896,7 @@ extern void ufs_free_inode (struct inode extern struct inode * ufs_new_inode (struct inode *, int); /* inode.c */ -extern int ufs_frag_map (struct inode *, int); +extern u64 ufs_frag_map (struct inode *, int); extern void ufs_read_inode (struct inode *); extern void ufs_put_inode (struct inode *); extern void ufs_write_inode (struct inode *, int); --- linux-2.6.4-rc2/include/linux/ufs_fs_i.h 2003-06-14 12:18:22.000000000 -0700 +++ 25/include/linux/ufs_fs_i.h 2004-03-07 20:47:39.000000000 -0800 @@ -17,6 +17,7 @@ struct ufs_inode_info { union { __u32 i_data[15]; __u8 i_symlink[4*15]; + __u64 u2_i_data[15]; } i_u1; __u32 i_flags; __u32 i_gen; --- linux-2.6.4-rc2/include/linux/usb_gadget.h 2004-01-09 00:04:33.000000000 -0800 +++ 25/include/linux/usb_gadget.h 2004-03-07 20:46:59.000000000 -0800 @@ -117,7 +117,7 @@ struct usb_ep_ops { void (*free_buffer) (struct usb_ep *ep, void *buf, dma_addr_t dma, unsigned bytes); // NOTE: on 2.5, drivers may also use dma_map() and - // dma_sync_single() to manage dma overhead. + // dma_sync_single_*() to manage dma overhead. int (*queue) (struct usb_ep *ep, struct usb_request *req, int gfp_flags); @@ -690,7 +690,7 @@ int usb_gadget_unregister_driver (struct /** * struct usb_string - wraps a C string and its USB id * @id:the (nonzero) ID for this string - * @s:the string, in ISO-8859/1 characters + * @s:the string, in UTF-8 encoding * * If you're using usb_gadget_get_string(), use this to wrap a string * together with its ID. @@ -716,6 +716,17 @@ struct usb_gadget_strings { /* put descriptor for string with that id into buf (buflen >= 256) */ int usb_gadget_get_string (struct usb_gadget_strings *table, int id, u8 *buf); +/*-------------------------------------------------------------------------*/ + +/* utility to simplify managing config descriptors */ + +/* write vector of descriptors into buffer */ +int usb_descriptor_fillbuf(void *, unsigned, + const struct usb_descriptor_header **); + +/* build config descriptor from single descriptor vector */ +int usb_gadget_config_buf(const struct usb_config_descriptor *config, + void *buf, unsigned buflen, const struct usb_descriptor_header **desc); #endif /* __KERNEL__ */ --- linux-2.6.4-rc2/include/linux/usb.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/linux/usb.h 2004-03-07 20:46:59.000000000 -0800 @@ -72,14 +72,11 @@ struct usb_host_interface { /** * struct usb_interface - what usb device drivers talk to - * @altsetting: array of interface descriptors, one for each alternate + * @altsetting: array of interface structures, one for each alternate * setting that may be selected. Each one includes a set of - * endpoint configurations and will be in numberic order, - * 0..num_altsetting. + * endpoint configurations. They will be in no particular order. * @num_altsetting: number of altsettings defined. - * @act_altsetting: index of current altsetting. this number is always - * less than num_altsetting. after the device is configured, each - * interface uses its default setting of zero. + * @cur_altsetting: the current altsetting. * @driver: the USB driver that is bound to this interface. * @minor: the minor number assigned to this interface, if this * interface is bound to a driver that uses the USB major number. @@ -89,6 +86,8 @@ struct usb_host_interface { * number from the USB core by calling usb_register_dev(). * @dev: driver model's view of this device * @class_dev: driver model's class view of this device. + * @released: wait for the interface to be released when changing + * configurations. * * USB device drivers attach to interfaces on a physical device. Each * interface encapsulates a single high level function, such as feeding @@ -102,26 +101,33 @@ struct usb_host_interface { * calls such as dev_get_drvdata() on the dev member of this structure. * * Each interface may have alternate settings. The initial configuration - * of a device sets the first of these, but the device driver can change + * of a device sets altsetting 0, but the device driver can change * that setting using usb_set_interface(). Alternate settings are often * used to control the the use of periodic endpoints, such as by having * different endpoints use different amounts of reserved USB bandwidth. * All standards-conformant USB devices that use isochronous endpoints * will use them in non-default settings. + * + * The USB specification says that alternate setting numbers must run from + * 0 to one less than the total number of alternate settings. But some + * devices manage to mess this up, and the structures aren't necessarily + * stored in numerical order anyhow. Use usb_altnum_to_altsetting() to + * look up an alternate setting in the altsetting array based on its number. */ struct usb_interface { - /* array of alternate settings for this interface. - * these will be in numeric order, 0..num_altsettting - */ + /* array of alternate settings for this interface, + * stored in no particular order */ struct usb_host_interface *altsetting; - unsigned act_altsetting; /* active alternate setting */ + struct usb_host_interface *cur_altsetting; /* the currently + * active alternate setting */ unsigned num_altsetting; /* number of alternate settings */ struct usb_driver *driver; /* driver */ int minor; /* minor number this interface is bound to */ struct device dev; /* interface specific device info */ struct class_device *class_dev; + struct completion *released; /* wait for release */ }; #define to_usb_interface(d) container_of(d, struct usb_interface, dev) #define interface_to_usbdev(intf) \ @@ -140,19 +146,43 @@ static inline void usb_set_intfdata (str /* this maximum is arbitrary */ #define USB_MAXINTERFACES 32 -/* USB_DT_CONFIG: Configuration descriptor information. - * - * USB_DT_OTHER_SPEED_CONFIG is the same descriptor, except that the - * descriptor type is different. Highspeed-capable devices can look - * different depending on what speed they're currently running. Only - * devices with a USB_DT_DEVICE_QUALIFIER have an OTHER_SPEED_CONFIG. +/** + * struct usb_host_config - representation of a device's configuration + * @desc: the device's configuration descriptor. + * @interface: array of usb_interface structures, one for each interface + * in the configuration. The number of interfaces is stored in + * desc.bNumInterfaces. + * @extra: pointer to buffer containing all extra descriptors associated + * with this configuration (those preceding the first interface + * descriptor). + * @extralen: length of the extra descriptors buffer. + * + * USB devices may have multiple configurations, but only one can be active + * at any time. Each encapsulates a different operational environment; + * for example, a dual-speed device would have separate configurations for + * full-speed and high-speed operation. The number of configurations + * available is stored in the device descriptor as bNumConfigurations. + * + * A configuration can contain multiple interfaces. Each corresponds to + * a different function of the USB device, and all are available whenever + * the configuration is active. The USB standard says that interfaces + * are supposed to be numbered from 0 to desc.bNumInterfaces-1, but a lot + * of devices get this wrong. In addition, the interface array is not + * guaranteed to be sorted in numerical order. Use usb_ifnum_to_if() to + * look up an interface entry based on its number. + * + * Device drivers should not attempt to activate configurations. The choice + * of which configuration to install is a policy decision based on such + * considerations as available power, functionality provided, and the user's + * desires (expressed through hotplug scripts). However, drivers can call + * usb_reset_configuration() to reinitialize the current configuration and + * all its interfaces. */ struct usb_host_config { struct usb_config_descriptor desc; - /* the interfaces associated with this configuration - * these will be in numeric order, 0..desc.bNumInterfaces - */ + /* the interfaces associated with this configuration, + * stored in no particular order */ struct usb_interface *interface[USB_MAXINTERFACES]; unsigned char *extra; /* Extra descriptors */ @@ -294,8 +324,12 @@ extern void usb_driver_release_interface const struct usb_device_id *usb_match_id(struct usb_interface *interface, const struct usb_device_id *id); -extern struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor); -extern struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum); +extern struct usb_interface *usb_find_interface(struct usb_driver *drv, + int minor); +extern struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, + unsigned ifnum); +extern struct usb_host_interface *usb_altnum_to_altsetting( + struct usb_interface *intf, unsigned int altnum); /** @@ -830,14 +864,18 @@ void usb_buffer_free (struct usb_device void *addr, dma_addr_t dma); struct urb *usb_buffer_map (struct urb *urb); +#if 0 void usb_buffer_dmasync (struct urb *urb); +#endif void usb_buffer_unmap (struct urb *urb); struct scatterlist; int usb_buffer_map_sg (struct usb_device *dev, unsigned pipe, struct scatterlist *sg, int nents); +#if 0 void usb_buffer_dmasync_sg (struct usb_device *dev, unsigned pipe, struct scatterlist *sg, int n_hw_ents); +#endif void usb_buffer_unmap_sg (struct usb_device *dev, unsigned pipe, struct scatterlist *sg, int n_hw_ents); --- linux-2.6.4-rc2/include/linux/workqueue.h 2004-03-03 23:12:49.000000000 -0800 +++ 25/include/linux/workqueue.h 2004-03-07 20:47:57.000000000 -0800 @@ -52,7 +52,9 @@ struct work_struct { extern struct workqueue_struct *create_workqueue(const char *name); extern void destroy_workqueue(struct workqueue_struct *wq); -extern int FASTCALL(queue_work(struct workqueue_struct *wq, struct work_struct *work)); +int queue_work(struct workqueue_struct *wq, struct work_struct *work); +int queue_work_on_cpu(struct workqueue_struct *wq, + struct work_struct *work, int cpu); extern int FASTCALL(queue_delayed_work(struct workqueue_struct *wq, struct work_struct *work, unsigned long delay)); extern void FASTCALL(flush_workqueue(struct workqueue_struct *wq)); --- linux-2.6.4-rc2/include/linux/writeback.h 2003-11-09 16:45:05.000000000 -0800 +++ 25/include/linux/writeback.h 2004-03-07 20:47:20.000000000 -0800 @@ -71,12 +71,15 @@ static inline void wait_on_inode(struct * mm/page-writeback.c */ int wakeup_bdflush(long nr_pages); +void disk_is_spun_up(int postpone_writeback); -/* These 5 are exported to sysctl. */ +/* These are exported to sysctl. */ extern int dirty_background_ratio; extern int vm_dirty_ratio; extern int dirty_writeback_centisecs; extern int dirty_expire_centisecs; +extern int block_dump; +extern int laptop_mode; struct ctl_table; struct file; --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/include/net/af_ipmi.h 2004-03-07 20:48:02.000000000 -0800 @@ -0,0 +1,59 @@ +/* + * IPMI Socket Glue + * + * Author: Louis Zhuang + * Copyright by Intel Corp., 2003 + */ +#ifndef _NET_IPMI_H +#define _NET_IPMI_H + +#include + +/* + * This is ipmi address for socket + */ +struct sockaddr_ipmi { + sa_family_t sipmi_family; /* AF_IPMI */ + int if_num; /* IPMI interface number */ + struct ipmi_addr ipmi_addr; +}; +#define SOCKADDR_IPMI_OVERHEAD (sizeof(struct sockaddr_ipmi) \ + - sizeof(struct ipmi_addr)) + +/* A msg_control item, this takes a 'struct ipmi_timing_parms' */ +#define IPMI_CMSG_TIMING_PARMS 0x01 + +/* + * This is ipmi message for socket + */ +struct ipmi_sock_msg { + int recv_type; + long msgid; + + unsigned char netfn; + unsigned char cmd; + int data_len; + unsigned char data[0]; +}; + +#define IPMI_MAX_SOCK_MSG_LENGTH (sizeof(struct ipmi_sock_msg)+IPMI_MAX_MSG_LENGTH) + +/* Register/unregister to receive specific commands. Uses struct + ipmi_cmdspec from linux/ipmi.h */ +#define SIOCIPMIREGCMD (SIOCPROTOPRIVATE + 0) +#define SIOCIPMIUNREGCMD (SIOCPROTOPRIVATE + 1) + +/* Register to receive events. Takes an integer */ +#define SIOCIPMIGETEVENT (SIOCPROTOPRIVATE + 2) + +/* Set the default timing parameters for the socket. Takes a struct + ipmi_timing_parms from linux/ipmi.h */ +#define SIOCIPMISETTIMING (SIOCPROTOPRIVATE + 3) +#define SIOCIPMIGETTIMING (SIOCPROTOPRIVATE + 4) + +/* Set/Get the IPMB address of the MC we are connected to, takes an + unsigned int. */ +#define SIOCIPMISETADDR (SIOCPROTOPRIVATE + 5) +#define SIOCIPMIGETADDR (SIOCPROTOPRIVATE + 6) + +#endif/*_NET_IPMI_H*/ --- linux-2.6.4-rc2/include/net/bluetooth/hci_core.h 2004-03-03 23:12:49.000000000 -0800 +++ 25/include/net/bluetooth/hci_core.h 2004-03-07 20:46:46.000000000 -0800 @@ -77,6 +77,8 @@ struct hci_dev { __u16 link_policy; __u16 link_mode; + unsigned long quirks; + atomic_t cmd_cnt; unsigned int acl_cnt; unsigned int sco_cnt; @@ -128,6 +130,7 @@ struct hci_dev { int (*flush)(struct hci_dev *hdev); int (*send)(struct sk_buff *skb); void (*destruct)(struct hci_dev *hdev); + void (*notify)(struct hci_dev *hdev, unsigned int evt); int (*ioctl)(struct hci_dev *hdev, unsigned int cmd, unsigned long arg); }; --- linux-2.6.4-rc2/include/net/bluetooth/hci.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/net/bluetooth/hci.h 2004-03-07 20:46:46.000000000 -0800 @@ -35,21 +35,31 @@ #define HCI_MAX_FRAME_SIZE (HCI_MAX_ACL_SIZE + 4) /* HCI dev events */ -#define HCI_DEV_REG 1 -#define HCI_DEV_UNREG 2 -#define HCI_DEV_UP 3 -#define HCI_DEV_DOWN 4 -#define HCI_DEV_SUSPEND 5 -#define HCI_DEV_RESUME 6 +#define HCI_DEV_REG 1 +#define HCI_DEV_UNREG 2 +#define HCI_DEV_UP 3 +#define HCI_DEV_DOWN 4 +#define HCI_DEV_SUSPEND 5 +#define HCI_DEV_RESUME 6 + +/* HCI notify events */ +#define HCI_NOTIFY_CONN_ADD 1 +#define HCI_NOTIFY_CONN_DEL 2 +#define HCI_NOTIFY_VOICE_SETTING 3 /* HCI device types */ #define HCI_VHCI 0 #define HCI_USB 1 #define HCI_PCCARD 2 -#define HCI_UART 3 -#define HCI_RS232 4 +#define HCI_UART 3 +#define HCI_RS232 4 #define HCI_PCI 5 +/* HCI device quirks */ +enum { + HCI_QUIRK_RESET_ON_INIT +}; + /* HCI device flags */ enum { HCI_UP, @@ -90,24 +100,24 @@ enum { #define HCIINQUIRY _IOR('H', 240, int) /* HCI timeouts */ -#define HCI_CONN_TIMEOUT (HZ * 40) -#define HCI_DISCONN_TIMEOUT (HZ * 2) +#define HCI_CONN_TIMEOUT (HZ * 40) +#define HCI_DISCONN_TIMEOUT (HZ * 2) #define HCI_CONN_IDLE_TIMEOUT (HZ * 60) /* HCI Packet types */ #define HCI_COMMAND_PKT 0x01 -#define HCI_ACLDATA_PKT 0x02 -#define HCI_SCODATA_PKT 0x03 +#define HCI_ACLDATA_PKT 0x02 +#define HCI_SCODATA_PKT 0x03 #define HCI_EVENT_PKT 0x04 #define HCI_UNKNOWN_PKT 0xff /* HCI Packet types */ -#define HCI_DM1 0x0008 -#define HCI_DM3 0x0400 -#define HCI_DM5 0x4000 -#define HCI_DH1 0x0010 -#define HCI_DH3 0x0800 -#define HCI_DH5 0x8000 +#define HCI_DM1 0x0008 +#define HCI_DM3 0x0400 +#define HCI_DM5 0x4000 +#define HCI_DH1 0x0010 +#define HCI_DH3 0x0800 +#define HCI_DH5 0x8000 #define HCI_HV1 0x0020 #define HCI_HV2 0x0040 --- linux-2.6.4-rc2/include/net/irda/vlsi_ir.h 2003-09-08 13:58:59.000000000 -0700 +++ 25/include/net/irda/vlsi_ir.h 2004-03-07 20:46:59.000000000 -0800 @@ -41,19 +41,6 @@ #define PCI_CLASS_SUBCLASS_MASK 0xffff #endif -/* missing pci-dma api call to give streaming dma buffer back to hw - * patch was floating on lkml around 2.5.2x and might be present later. - * Defining it this way is ok, since the vlsi-ir is only - * used on two oldish x86-based notebooks which are cache-coherent - * (and flush_write_buffers also handles PPro errata and C3 OOstore) - */ -#ifdef CONFIG_X86 -#include -#define pci_dma_prep_single(dev, addr, size, direction) flush_write_buffers() -#else -#error missing pci dma api call -#endif - /* in recent 2.5 interrupt handlers have non-void return value */ #ifndef IRQ_RETVAL typedef void irqreturn_t; --- linux-2.6.4-rc2/include/scsi/scsi_device.h 2003-11-26 13:53:35.000000000 -0800 +++ 25/include/scsi/scsi_device.h 2004-03-07 20:46:55.000000000 -0800 @@ -94,6 +94,7 @@ struct scsi_device { unsigned skip_ms_page_8:1; /* do not use MODE SENSE page 0x08 */ unsigned skip_ms_page_3f:1; /* do not use MODE SENSE page 0x3f */ unsigned no_start_on_add:1; /* do not issue start on add */ + unsigned allow_restart:1; /* issue START_UNIT in error handler */ unsigned int device_blocked; /* Device returned QUEUE_FULL. */ @@ -103,12 +104,17 @@ struct scsi_device { struct device sdev_gendev; struct class_device sdev_classdev; + struct class_device transport_classdev; + enum scsi_device_state sdev_state; -}; + unsigned long transport_data[0]; +} __attribute__((aligned(sizeof(unsigned long)))); #define to_scsi_device(d) \ container_of(d, struct scsi_device, sdev_gendev) #define class_to_sdev(d) \ container_of(d, struct scsi_device, sdev_classdev) +#define transport_class_to_sdev(class_dev) \ + container_of(class_dev, struct scsi_device, transport_classdev) extern struct scsi_device *scsi_add_device(struct Scsi_Host *, uint, uint, uint); --- linux-2.6.4-rc2/include/scsi/scsi.h 2004-03-03 23:12:49.000000000 -0800 +++ 25/include/scsi/scsi.h 2004-03-07 20:46:55.000000000 -0800 @@ -200,6 +200,7 @@ static inline int scsi_status_is_good(in #define TYPE_MEDIUM_CHANGER 0x08 #define TYPE_COMM 0x09 /* Communications device */ #define TYPE_ENCLOSURE 0x0d /* Enclosure Services Device */ +#define TYPE_RAID 0x0c #define TYPE_NO_LUN 0x7f /* --- linux-2.6.4-rc2/include/scsi/scsi_host.h 2003-11-23 19:03:02.000000000 -0800 +++ 25/include/scsi/scsi_host.h 2004-03-07 20:46:55.000000000 -0800 @@ -11,6 +11,7 @@ struct scsi_cmnd; struct scsi_device; struct Scsi_Host; struct scsi_host_cmd_pool; +struct scsi_transport_template; /* @@ -395,6 +396,7 @@ struct Scsi_Host { unsigned int eh_kill:1; /* set when killing the eh thread */ wait_queue_head_t host_wait; struct scsi_host_template *hostt; + struct scsi_transport_template *transportt; volatile unsigned short host_busy; /* commands actually active on low-level */ volatile unsigned short host_failed; /* commands that failed. */ --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/include/scsi/scsi_transport_fc.h 2004-03-07 20:46:55.000000000 -0800 @@ -0,0 +1,38 @@ +/* + * FiberChannel transport specific attributes exported to sysfs. + * + * Copyright (c) 2003 Silicon Graphics, Inc. All rights reserved. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef SCSI_TRANSPORT_FC_H +#define SCSI_TRANSPORT_FC_H + +struct scsi_transport_template; + +struct fc_transport_attrs { + int port_id; + uint64_t node_name; + uint64_t port_name; +}; + +/* accessor functions */ +#define fc_port_id(x) (((struct fc_transport_attrs *)&(x)->transport_data)->port_id) +#define fc_node_name(x) (((struct fc_transport_attrs *)&(x)->transport_data)->node_name) +#define fc_port_name(x) (((struct fc_transport_attrs *)&(x)->transport_data)->port_name) + +extern struct scsi_transport_template fc_transport_template; + +#endif /* SCSI_TRANSPORT_FC_H */ --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/include/scsi/scsi_transport.h 2004-03-07 20:46:55.000000000 -0800 @@ -0,0 +1,41 @@ +/* + * Transport specific attributes. + * + * Copyright (c) 2003 Silicon Graphics, Inc. All rights reserved. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef SCSI_TRANSPORT_H +#define SCSI_TRANSPORT_H + +struct scsi_transport_template { + /* The NULL terminated list of transport attributes + * that should be exported. + */ + struct class_device_attribute **attrs; + + /* The transport class that the device is in */ + struct class *class; + + /* Constructor/Destructor functions */ + int (* setup)(struct scsi_device *); + void (* cleanup)(struct scsi_device *); + /* The size of the specific transport attribute structure (a + * space of this size will be left at the end of the + * scsi_device structure */ + int size; +}; + +#endif /* SCSI_TRANSPORT_H */ --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/include/scsi/scsi_transport_spi.h 2004-03-07 20:46:55.000000000 -0800 @@ -0,0 +1,38 @@ +/* + * Parallel SCSI (SPI) transport specific attributes exported to sysfs. + * + * Copyright (c) 2003 Silicon Graphics, Inc. All rights reserved. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef SCSI_TRANSPORT_SPI_H +#define SCSI_TRANSPORT_SPI_H + +#include + +struct scsi_transport_template; + +struct spi_transport_attrs { + int period; + int offset; +}; + +/* accessor functions */ +#define spi_period(x) (((struct spi_transport_attrs *)&(x)->transport_data)->period) +#define spi_offset(x) (((struct spi_transport_attrs *)&(x)->transport_data)->offset) + +extern struct scsi_transport_template spi_transport_template; + +#endif /* SCSI_TRANSPORT_SPI_H */ --- linux-2.6.4-rc2/include/sound/ac97_codec.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/sound/ac97_codec.h 2004-03-07 20:46:50.000000000 -0800 @@ -484,7 +484,8 @@ enum { AC97_TUNE_HP_ONLY, /* headphone (true line-out) control as master only */ AC97_TUNE_SWAP_HP, /* swap headphone and master controls */ AC97_TUNE_SWAP_SURROUND, /* swap master and surround controls */ - AC97_TUNE_AD_SHARING /* for AD1985, turn on OMS bit and use headphone */ + AC97_TUNE_AD_SHARING, /* for AD1985, turn on OMS bit and use headphone */ + AC97_TUNE_ALC_JACK, /* for Realtek, enable JACK detection */ }; struct ac97_quirk { --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/include/sound/ak4117.h 2004-03-07 20:46:50.000000000 -0800 @@ -0,0 +1,191 @@ +#ifndef __SOUND_AK4117_H +#define __SOUND_AK4117_H + +/* + * Routines for Asahi Kasei AK4117 + * Copyright (c) by Jaroslav Kysela , + * + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define AK4117_REG_PWRDN 0x00 /* power down */ +#define AK4117_REG_CLOCK 0x01 /* clock control */ +#define AK4117_REG_IO 0x02 /* input/output control */ +#define AK4117_REG_INT0_MASK 0x03 /* interrupt0 mask */ +#define AK4117_REG_INT1_MASK 0x04 /* interrupt1 mask */ +#define AK4117_REG_RCS0 0x05 /* receiver status 0 */ +#define AK4117_REG_RCS1 0x06 /* receiver status 1 */ +#define AK4117_REG_RCS2 0x07 /* receiver status 2 */ +#define AK4117_REG_RXCSB0 0x08 /* RX channel status byte 0 */ +#define AK4117_REG_RXCSB1 0x09 /* RX channel status byte 1 */ +#define AK4117_REG_RXCSB2 0x0a /* RX channel status byte 2 */ +#define AK4117_REG_RXCSB3 0x0b /* RX channel status byte 3 */ +#define AK4117_REG_RXCSB4 0x0c /* RX channel status byte 4 */ +#define AK4117_REG_Pc0 0x0d /* burst preamble Pc byte 0 */ +#define AK4117_REG_Pc1 0x0e /* burst preamble Pc byte 1 */ +#define AK4117_REG_Pd0 0x0f /* burst preamble Pd byte 0 */ +#define AK4117_REG_Pd1 0x10 /* burst preamble Pd byte 1 */ +#define AK4117_REG_QSUB_ADDR 0x11 /* Q-subcode address + control */ +#define AK4117_REG_QSUB_TRACK 0x12 /* Q-subcode track */ +#define AK4117_REG_QSUB_INDEX 0x13 /* Q-subcode index */ +#define AK4117_REG_QSUB_MINUTE 0x14 /* Q-subcode minute */ +#define AK4117_REG_QSUB_SECOND 0x15 /* Q-subcode second */ +#define AK4117_REG_QSUB_FRAME 0x16 /* Q-subcode frame */ +#define AK4117_REG_QSUB_ZERO 0x17 /* Q-subcode zero */ +#define AK4117_REG_QSUB_ABSMIN 0x18 /* Q-subcode absolute minute */ +#define AK4117_REG_QSUB_ABSSEC 0x19 /* Q-subcode absolute second */ +#define AK4117_REG_QSUB_ABSFRM 0x1a /* Q-subcode absolute frame */ + +/* sizes */ +#define AK4117_REG_RXCSB_SIZE ((AK4117_REG_RXCSB4-AK4117_REG_RXCSB0)+1) +#define AK4117_REG_QSUB_SIZE ((AK4117_REG_QSUB_ABSFRM-AK4117_REG_QSUB_ADDR)+1) + +/* AK4117_REG_PWRDN bits */ +#define AK4117_EXCT (1<<4) /* 0 = X'tal mode, 1 = external clock mode */ +#define AK4117_XTL1 (1<<3) /* XTL1=0,XTL0=0 -> 11.2896Mhz; XTL1=0,XTL0=1 -> 12.288Mhz */ +#define AK4117_XTL0 (1<<2) /* XTL1=1,XTL0=0 -> 24.576Mhz; XTL1=1,XTL0=1 -> use channel status */ +#define AK4117_XTL_11_2896M (0) +#define AK4117_XTL_12_288M AK4117_XTL0 +#define AK4117_XTL_24_576M AK4117_XTL1 +#define AK4117_XTL_EXT (AK4117_XTL1|AK4117_XTL0) +#define AK4117_PWN (1<<1) /* 0 = power down, 1 = normal operation */ +#define AK4117_RST (1<<0) /* 0 = reset & initialize (except this register), 1 = normal operation */ + +/* AK4117_REQ_CLOCK bits */ +#define AK4117_LP (1<<7) /* 0 = normal mode, 1 = low power mode (Fs up to 48kHz only) */ +#define AK4117_PKCS1 (1<<6) /* master clock frequency at PLL mode (when LP == 0) */ +#define AK4117_PKCS0 (1<<5) +#define AK4117_PKCS_512fs (0) +#define AK4117_PKCS_256fs AK4117_PKCS0 +#define AK4117_PKCS_128fs AK4117_PKCS1 +#define AK4117_DIV (1<<4) /* 0 = MCKO == Fs, 1 = MCKO == Fs / 2; X'tal mode only */ +#define AK4117_XCKS1 (1<<3) /* master clock frequency at X'tal mode */ +#define AK4117_XCKS0 (1<<2) +#define AK4117_XCKS_128fs (0) +#define AK4117_XCKS_256fs AK4117_XCKS0 +#define AK4117_XCKS_512fs AK4117_XCKS1 +#define AK4117_XCKS_1024fs (AK4117_XCKS1|AK4117_XCKS0) +#define AK4117_CM1 (1<<1) /* MCKO operation mode select */ +#define AK4117_CM0 (1<<0) +#define AK4117_CM_PLL (0) /* use RX input as master clock */ +#define AK4117_CM_XTAL (AK4117_CM0) /* use X'tal as master clock */ +#define AK4117_CM_PLL_XTAL (AK4117_CM1) /* use Rx input but X'tal when PLL loses lock */ +#define AK4117_CM_MONITOR (AK4117_CM0|AK4117_CM1) /* use X'tal as master clock, but use PLL for monitoring */ + +/* AK4117_REG_IO */ +#define AK4117_IPS (1<<7) /* Input Recovery Data Select, 0 = RX0, 1 = RX1 */ +#define AK4117_UOUTE (1<<6) /* U-bit output enable to UOUT, 0 = disable, 1 = enable */ +#define AK4117_CS12 (1<<5) /* channel status select, 0 = channel1, 1 = channel2 */ +#define AK4117_EFH2 (1<<4) /* INT0 pin hold count select */ +#define AK4117_EFH1 (1<<3) +#define AK4117_EFH_512LRCLK (0) +#define AK4117_EFH_1024LRCLK (AK4117_EFH1) +#define AK4117_EFH_2048LRCLK (AK4117_EFH2) +#define AK4117_EFH_4096LRCLK (AK4117_EFH1|AK4117_EFH2) +#define AK4117_DIF2 (1<<2) /* audio data format control */ +#define AK4117_DIF1 (1<<1) +#define AK4117_DIF0 (1<<0) +#define AK4117_DIF_16R (0) /* STDO: 16-bit, right justified */ +#define AK4117_DIF_18R (AK4117_DIF0) /* STDO: 18-bit, right justified */ +#define AK4117_DIF_20R (AK4117_DIF1) /* STDO: 20-bit, right justified */ +#define AK4117_DIF_24R (AK4117_DIF1|AK4117_DIF0) /* STDO: 24-bit, right justified */ +#define AK4117_DIF_24L (AK4117_DIF2) /* STDO: 24-bit, left justified */ +#define AK4117_DIF_24I2S (AK4117_DIF2|AK4117_DIF0) /* STDO: I2S */ + +/* AK4117_REG_INT0_MASK & AK4117_INT1_MASK */ +#define AK4117_MULK (1<<7) /* mask enable for UNLOCK bit */ +#define AK4117_MPAR (1<<6) /* mask enable for PAR bit */ +#define AK4117_MAUTO (1<<5) /* mask enable for AUTO bit */ +#define AK4117_MV (1<<4) /* mask enable for V bit */ +#define AK4117_MAUD (1<<3) /* mask enable for AUDION bit */ +#define AK4117_MSTC (1<<2) /* mask enable for STC bit */ +#define AK4117_MCIT (1<<1) /* mask enable for CINT bit */ +#define AK4117_MQIT (1<<0) /* mask enable for QINT bit */ + +/* AK4117_REG_RCS0 */ +#define AK4117_UNLCK (1<<7) /* PLL lock status, 0 = lock, 1 = unlock */ +#define AK4117_PAR (1<<6) /* parity error or biphase error status, 0 = no error, 1 = error */ +#define AK4117_AUTO (1<<5) /* Non-PCM or DTS stream auto detection, 0 = no detect, 1 = detect */ +#define AK4117_V (1<<4) /* Validity bit, 0 = valid, 1 = invalid */ +#define AK4117_AUDION (1<<3) /* audio bit output, 0 = audio, 1 = non-audio */ +#define AK4117_STC (1<<2) /* sampling frequency or Pre-emphasis change, 0 = no detect, 1 = detect */ +#define AK4117_CINT (1<<1) /* channel status buffer interrupt, 0 = no change, 1 = change */ +#define AK4117_QINT (1<<0) /* Q-subcode buffer interrupt, 0 = no change, 1 = changed */ + +/* AK4117_REG_RCS1 */ +#define AK4117_DTSCD (1<<6) /* DTS-CD bit audio stream detect, 0 = no detect, 1 = detect */ +#define AK4117_NPCM (1<<5) /* Non-PCM bit stream detection, 0 = no detect, 1 = detect */ +#define AK4117_PEM (1<<4) /* Pre-emphasis detect, 0 = OFF, 1 = ON */ +#define AK4117_FS3 (1<<3) /* sampling frequency detection */ +#define AK4117_FS2 (1<<2) +#define AK4117_FS1 (1<<1) +#define AK4117_FS0 (1<<0) +#define AK4117_FS_44100HZ (0) +#define AK4117_FS_48000HZ (AK4117_FS1) +#define AK4117_FS_32000HZ (AK4117_FS1|AK4117_FS0) +#define AK4117_FS_88200HZ (AK4117_FS3) +#define AK4117_FS_96000HZ (AK4117_FS3|AK4117_FS1) +#define AK4117_FS_176400HZ (AK4117_FS3|AK4117_FS2) +#define AK4117_FS_192000HZ (AK4117_FS3|AK4117_FS2|AK4117_FS1) + +/* AK4117_REG_RCS2 */ +#define AK4117_CCRC (1<<1) /* CRC for channel status, 0 = no error, 1 = error */ +#define AK4117_QCRC (1<<0) /* CRC for Q-subcode, 0 = no error, 1 = error */ + +/* flags for snd_ak4117_check_rate_and_errors() */ +#define AK4117_CHECK_NO_STAT (1<<0) /* no statistics */ +#define AK4117_CHECK_NO_RATE (1<<1) /* no rate check */ + +#define AK4117_CONTROLS 13 + +typedef void (ak4117_write_t)(void *private_data, unsigned char addr, unsigned char data); +typedef unsigned char (ak4117_read_t)(void *private_data, unsigned char addr); + +typedef struct ak4117 ak4117_t; + +struct ak4117 { + snd_card_t * card; + ak4117_write_t * write; + ak4117_read_t * read; + void * private_data; + unsigned int init: 1; + spinlock_t lock; + unsigned char regmap[5]; + snd_kcontrol_t *kctls[AK4117_CONTROLS]; + snd_pcm_substream_t *substream; + unsigned long parity_errors; + unsigned long v_bit_errors; + unsigned long qcrc_errors; + unsigned long ccrc_errors; + unsigned char rcs0; + unsigned char rcs1; + unsigned char rcs2; + struct timer_list timer; /* statistic timer */ + void *change_callback_private; + void (*change_callback)(ak4117_t *ak4117, unsigned char c0, unsigned char c1); +}; + +int snd_ak4117_create(snd_card_t *card, ak4117_read_t *read, ak4117_write_t *write, + unsigned char pgm[5], void *private_data, ak4117_t **r_ak4117); +void snd_ak4117_reg_write(ak4117_t *chip, unsigned char reg, unsigned char mask, unsigned char val); +void snd_ak4117_reinit(ak4117_t *chip); +int snd_ak4117_build(ak4117_t *ak4117, snd_pcm_substream_t *capture_substream); +int snd_ak4117_external_rate(ak4117_t *ak4117); +int snd_ak4117_check_rate_and_errors(ak4117_t *ak4117, unsigned int flags); + +#endif /* __SOUND_AK4117_H */ + --- linux-2.6.4-rc2/include/sound/asequencer.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/sound/asequencer.h 2004-03-07 20:46:50.000000000 -0800 @@ -594,6 +594,7 @@ struct sndrv_seq_remove_events { #define SNDRV_SEQ_PORT_TYPE_MIDI_GS (1<<3) /* GS compatible device */ #define SNDRV_SEQ_PORT_TYPE_MIDI_XG (1<<4) /* XG compatible device */ #define SNDRV_SEQ_PORT_TYPE_MIDI_MT32 (1<<5) /* MT-32 compatible device */ +#define SNDRV_SEQ_PORT_TYPE_MIDI_GM2 (1<<6) /* General MIDI 2 compatible device */ /* other standards...*/ #define SNDRV_SEQ_PORT_TYPE_SYNTH (1<<10) /* Synth device (no MIDI compatible - direct wavetable) */ @@ -605,7 +606,7 @@ struct sndrv_seq_remove_events { /* misc. conditioning flags */ #define SNDRV_SEQ_PORT_FLG_GIVEN_PORT (1<<0) #define SNDRV_SEQ_PORT_FLG_TIMESTAMP (1<<1) -#define SNDRV_SEQ_PORT_FLG_TIME_REAL (1<<1) +#define SNDRV_SEQ_PORT_FLG_TIME_REAL (1<<2) struct sndrv_seq_port_info { struct sndrv_seq_addr addr; /* client/port numbers */ --- linux-2.6.4-rc2/include/sound/asound.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/sound/asound.h 2004-03-07 20:46:50.000000000 -0800 @@ -153,7 +153,7 @@ enum { * * *****************************************************************************/ -#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 5) +#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 6) typedef unsigned long sndrv_pcm_uframes_t; typedef long sndrv_pcm_sframes_t; --- linux-2.6.4-rc2/include/sound/cs46xx.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/sound/cs46xx.h 2004-03-07 20:46:50.000000000 -0800 @@ -1646,9 +1646,7 @@ typedef struct _snd_cs46xx cs46xx_t; typedef struct _snd_cs46xx_pcm_t { - unsigned char *hw_area; - dma_addr_t hw_addr; /* PCI bus address, not accessible */ - unsigned long hw_size; + struct snd_dma_buffer hw_buf; unsigned int ctl; unsigned int shift; /* Shift count to trasform frames in bytes */ @@ -1693,9 +1691,7 @@ struct _snd_cs46xx { unsigned int mode; struct { - unsigned char *hw_area; - dma_addr_t hw_addr; /* PCI bus address, not accessible */ - unsigned long hw_size; + struct snd_dma_buffer hw_buf; unsigned int ctl; unsigned int shift; /* Shift count to trasform frames in bytes */ @@ -1727,6 +1723,8 @@ struct _snd_cs46xx { unsigned int midcr; unsigned int uartm; + struct snd_dma_device dma_dev; + int amplifier; void (*amplifier_ctrl)(cs46xx_t *, int); void (*active_ctrl)(cs46xx_t *, int); --- linux-2.6.4-rc2/include/sound/emu10k1.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/sound/emu10k1.h 2004-03-07 20:46:50.000000000 -0800 @@ -644,9 +644,13 @@ #define SOLEH 0x5d /* Stop on loop enable high register */ #define SPBYPASS 0x5e /* SPDIF BYPASS mode register */ -#define SPBYPASS_ENABLE 0x00000001 /* Enable SPDIF bypass mode */ +#define SPBYPASS_SPDIF0_MASK 0x00000003 /* SPDIF 0 bypass mode */ +#define SPBYPASS_SPDIF1_MASK 0x0000000c /* SPDIF 1 bypass mode */ +/* bypass mode: 0 - DSP; 1 - SPDIF A, 2 - SPDIF B, 3 - SPDIF C */ +#define SPBYPASS_FORMAT 0x00000f00 /* If 1, SPDIF XX uses 24 bit, if 0 - 20 bit */ #define AC97SLOT 0x5f /* additional AC97 slots enable bits */ +#define AC97SLOT_10K2 0x03 #define AC97SLOT_CNTR 0x10 /* Center enable */ #define AC97SLOT_LFE 0x20 /* LFE enable */ @@ -837,7 +841,7 @@ typedef struct { typedef struct snd_emu10k1_memblk { snd_util_memblk_t mem; /* private part */ - short first_page, last_page, pages, mapped_page; + int first_page, last_page, pages, mapped_page; unsigned int map_locked; struct list_head mapped_link; struct list_head mapped_order_link; @@ -897,9 +901,7 @@ typedef struct { unsigned short extout_mask; /* used external outputs (bitmask) */ unsigned short pad1; unsigned int itram_size; /* internal TRAM size in samples */ - unsigned int etram_size; /* external TRAM size in samples */ - void *etram_pages; /* allocated pages for external TRAM */ - dma_addr_t etram_pages_dmaaddr; + struct snd_dma_buffer etram_pages; /* external TRAM pages and size */ unsigned int dbg; /* FX debugger register */ unsigned char name[128]; int gpr_size; /* size of allocated GPR controls */ @@ -943,11 +945,10 @@ struct _snd_emu10k1 { unsigned int card_type; /* EMU10K1_CARD_* */ unsigned int ecard_ctrl; /* ecard control bits */ unsigned long dma_mask; /* PCI DMA mask */ + struct snd_dma_device dma_dev; /* DMA device description */ int max_cache_pages; /* max memory size / PAGE_SIZE */ - void *silent_page; /* silent page */ - dma_addr_t silent_page_dmaaddr; - volatile u32 *ptb_pages; /* page table pages */ - dma_addr_t ptb_pages_dmaaddr; + struct snd_dma_buffer silent_page; /* silent page */ + struct snd_dma_buffer ptb_pages; /* page table pages */ snd_util_memhdr_t *memhdr; /* page allocation list */ emu10k1_memblk_t *reserved_page; /* reserved page */ --- linux-2.6.4-rc2/include/sound/info.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/sound/info.h 2004-03-07 20:46:50.000000000 -0800 @@ -171,7 +171,7 @@ static inline void snd_remove_proc_entry struct proc_dir_entry *de) { ; } #define snd_card_proc_new(card,name,entryp) 0 /* always success */ -#define snd_info_set_text_ops(entry,private_data,read) /*NOP*/ +#define snd_info_set_text_ops(entry,private_data,read_size,read) /*NOP*/ #endif --- linux-2.6.4-rc2/include/sound/memalloc.h 2003-06-14 12:17:57.000000000 -0700 +++ 25/include/sound/memalloc.h 2004-03-07 20:46:50.000000000 -0800 @@ -24,49 +24,33 @@ #ifndef __SOUND_MEMALLOC_H #define __SOUND_MEMALLOC_H -#include -#ifdef CONFIG_SBUS -#include -#endif +struct device; /* * buffer device info */ struct snd_dma_device { int type; /* SNDRV_MEM_TYPE_XXX */ - union { - struct pci_dev *pci; /* for PCI and PCI-SG types */ - unsigned int flags; /* GFP_XXX for continous and ISA types */ -#ifdef CONFIG_SBUS - struct sbus_dev *sbus; /* for SBUS type */ -#endif - } dev; + struct device *dev; /* generic device */ unsigned int id; /* a unique ID */ }; +#ifndef snd_dma_pci_data +#define snd_dma_pci_data(pci) (&(pci)->dev) +#define snd_dma_isa_data() NULL +#define snd_dma_sbus_data(sbus) ((struct device *)(sbus)) +#define snd_dma_continuous_data(x) ((struct device *)(unsigned long)(x)) +#endif + + /* * buffer types */ #define SNDRV_DMA_TYPE_UNKNOWN 0 /* not defined */ #define SNDRV_DMA_TYPE_CONTINUOUS 1 /* continuous no-DMA memory */ -#define SNDRV_DMA_TYPE_ISA 2 /* ISA continuous */ -#define SNDRV_DMA_TYPE_PCI 3 /* PCI continuous */ +#define SNDRV_DMA_TYPE_DEV 2 /* generic device continuous */ +#define SNDRV_DMA_TYPE_DEV_SG 3 /* generic device SG-buffer */ #define SNDRV_DMA_TYPE_SBUS 4 /* SBUS continuous */ -#define SNDRV_DMA_TYPE_PCI_SG 5 /* PCI SG-buffer */ - -#ifdef CONFIG_PCI -/* - * compose a snd_dma_device struct for the PCI device - */ -static inline void snd_dma_device_pci(struct snd_dma_device *dev, struct pci_dev *pci, unsigned int id) -{ - memset(dev, 0, sizeof(*dev)); - dev->type = SNDRV_DMA_TYPE_PCI; - dev->dev.pci = pci; - dev->id = id; -} -#endif - /* * info for buffer allocation @@ -78,67 +62,8 @@ struct snd_dma_buffer { void *private_data; /* private for allocator; don't touch */ }; -/* allocate/release a buffer */ -int snd_dma_alloc_pages(const struct snd_dma_device *dev, size_t size, struct snd_dma_buffer *dmab); -void snd_dma_free_pages(const struct snd_dma_device *dev, struct snd_dma_buffer *dmab); - -/* buffer-preservation managements */ -size_t snd_dma_get_reserved(const struct snd_dma_device *dev, struct snd_dma_buffer *dmab); -int snd_dma_free_reserved(const struct snd_dma_device *dev); -int snd_dma_set_reserved(const struct snd_dma_device *dev, struct snd_dma_buffer *dmab); - - -/* - * Generic memory allocators - */ - -/* - * continuous pages - */ -void *snd_malloc_pages(size_t size, unsigned int gfp_flags); -void *snd_malloc_pages_fallback(size_t size, unsigned int gfp_flags, size_t *res_size); -void snd_free_pages(void *ptr, size_t size); - -#ifdef CONFIG_PCI -/* - * PCI continuous pages - */ -void *snd_malloc_pci_pages(struct pci_dev *pci, size_t size, dma_addr_t *dma_addr); -void *snd_malloc_pci_pages_fallback(struct pci_dev *pci, size_t size, dma_addr_t *dma_addr, size_t *res_size); -void snd_free_pci_pages(struct pci_dev *pci, size_t size, void *ptr, dma_addr_t dma_addr); -/* one page allocation */ -void *snd_malloc_pci_page(struct pci_dev *pci, dma_addr_t *dma_addr); -#define snd_free_pci_page(pci,ptr,addr) snd_free_pci_pages(pci,PAGE_SIZE,ptr,addr) -#endif - -#ifdef CONFIG_SBUS -/* - * SBUS continuous pages - */ -void *snd_malloc_sbus_pages(struct sbus_dev *sdev, size_t size, dma_addr_t *dma_addr); -void *snd_malloc_sbus_pages_fallback(struct sbus_dev *sdev, size_t size, dma_addr_t *dma_addr, size_t *res_size); -void snd_free_sbus_pages(struct sbus_dev *sdev, size_t size, void *ptr, dma_addr_t dma_addr); -#endif - -#ifdef CONFIG_ISA /* - * ISA continuous pages - */ -void *snd_malloc_isa_pages(size_t size, dma_addr_t *dma_addr); -void *snd_malloc_isa_pages_fallback(size_t size, dma_addr_t *dma_addr, size_t *res_size); -void snd_free_isa_pages(size_t size, void *ptr, dma_addr_t addr); -#ifdef CONFIG_PCI -#define snd_malloc_isa_pages(size, dma_addr) snd_malloc_pci_pages(NULL, size, dma_addr) -#define snd_malloc_isa_pages_fallback(size, dma_addr, res_size) snd_malloc_pci_pages_fallback(NULL, size, dma_addr, res_size) -#define snd_free_isa_pages(size, ptr, dma_addr) snd_free_pci_pages(NULL, size, ptr, dma_addr) -#else /* !CONFIG_PCI */ -#define snd_free_isa_pages(size, ptr, dma_addr) snd_free_pages(ptr, size) -#endif /* CONFIG_PCI */ -#endif /* CONFIG_ISA */ - -#ifdef CONFIG_PCI -/* - * Scatter-Gather PCI pages + * Scatter-Gather generic device pages */ struct snd_sg_page { void *buf; @@ -151,12 +76,9 @@ struct snd_sg_buf { int tblsize; /* allocated table size */ struct snd_sg_page *table; /* address table */ struct page **page_table; /* page table (for vmap/vunmap) */ - struct pci_dev *pci; + struct snd_dma_device dev; }; -void *snd_malloc_sgbuf_pages(struct pci_dev *pci, size_t size, struct snd_dma_buffer *dmab); -int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab); - /* * return the pages matching with the given byte size */ @@ -172,6 +94,24 @@ static inline dma_addr_t snd_sgbuf_get_a { return sgbuf->table[offset >> PAGE_SHIFT].addr + offset % PAGE_SIZE; } -#endif /* CONFIG_PCI */ + + +/* allocate/release a buffer */ +int snd_dma_alloc_pages(const struct snd_dma_device *dev, size_t size, + struct snd_dma_buffer *dmab); +int snd_dma_alloc_pages_fallback(const struct snd_dma_device *dev, size_t size, + struct snd_dma_buffer *dmab); +void snd_dma_free_pages(const struct snd_dma_device *dev, struct snd_dma_buffer *dmab); + +/* buffer-preservation managements */ +size_t snd_dma_get_reserved(const struct snd_dma_device *dev, struct snd_dma_buffer *dmab); +int snd_dma_free_reserved(const struct snd_dma_device *dev); +int snd_dma_set_reserved(const struct snd_dma_device *dev, struct snd_dma_buffer *dmab); + +/* basic memory allocation functions */ +void *snd_malloc_pages(size_t size, unsigned int gfp_flags); +void *snd_malloc_pages_fallback(size_t size, unsigned int gfp_flags, size_t *res_size); +void snd_free_pages(void *ptr, size_t size); #endif /* __SOUND_MEMALLOC_H */ + --- linux-2.6.4-rc2/include/sound/pcm.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/sound/pcm.h 2004-03-07 20:46:50.000000000 -0800 @@ -889,6 +889,9 @@ snd_pcm_sframes_t snd_pcm_lib_writev(snd snd_pcm_sframes_t snd_pcm_lib_readv(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames); +int snd_pcm_limit_hw_rates(snd_pcm_runtime_t *runtime); + + /* * Timer interface */ @@ -904,49 +907,18 @@ void snd_pcm_timer_done(snd_pcm_substrea int snd_pcm_lib_preallocate_free(snd_pcm_substream_t *substream); int snd_pcm_lib_preallocate_free_for_all(snd_pcm_t *pcm); int snd_pcm_lib_preallocate_pages(snd_pcm_substream_t *substream, - size_t size, size_t max, - unsigned int flags); + int type, struct device *data, + size_t size, size_t max); int snd_pcm_lib_preallocate_pages_for_all(snd_pcm_t *pcm, - size_t size, size_t max, - unsigned int flags); + int type, void *data, + size_t size, size_t max); int snd_pcm_lib_malloc_pages(snd_pcm_substream_t *substream, size_t size); int snd_pcm_lib_free_pages(snd_pcm_substream_t *substream); -#ifdef CONFIG_ISA -int snd_pcm_lib_preallocate_isa_pages(snd_pcm_substream_t *substream, - size_t size, size_t max); -int snd_pcm_lib_preallocate_isa_pages_for_all(snd_pcm_t *pcm, - size_t size, size_t max); -#endif -#ifdef CONFIG_PCI -int snd_pcm_lib_preallocate_pci_pages(struct pci_dev *pci, - snd_pcm_substream_t *substream, - size_t size, size_t max); -int snd_pcm_lib_preallocate_pci_pages_for_all(struct pci_dev *pci, - snd_pcm_t *pcm, - size_t size, - size_t max); -int snd_pcm_lib_preallocate_sg_pages(struct pci_dev *pci, - snd_pcm_substream_t *substream, - size_t size, size_t max); -int snd_pcm_lib_preallocate_sg_pages_for_all(struct pci_dev *pci, - snd_pcm_t *pcm, - size_t size, size_t max); #define snd_pcm_substream_sgbuf(substream) ((substream)->runtime->dma_private) #define snd_pcm_sgbuf_pages(size) snd_sgbuf_aligned_pages(size) #define snd_pcm_sgbuf_get_addr(sgbuf,ofs) snd_sgbuf_get_addr(sgbuf,ofs) struct page *snd_pcm_sgbuf_ops_page(snd_pcm_substream_t *substream, unsigned long offset); -#endif - -#ifdef CONFIG_SBUS -int snd_pcm_lib_preallocate_sbus_pages(struct sbus_dev *sdev, - snd_pcm_substream_t *substream, - size_t size, size_t max); -int snd_pcm_lib_preallocate_sbus_pages_for_all(struct sbus_dev *sdev, - snd_pcm_t *pcm, - size_t size, - size_t max); -#endif static inline void snd_pcm_limit_isa_dma_size(int dma, size_t *max) { --- linux-2.6.4-rc2/include/sound/pcm_oss.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/sound/pcm_oss.h 2004-03-07 20:46:50.000000000 -0800 @@ -50,6 +50,7 @@ typedef struct _snd_pcm_oss_runtime { unsigned int maxfrags; unsigned int subdivision; /* requested subdivision */ size_t period_bytes; /* requested period size */ + size_t period_frames; /* period frames for poll */ size_t period_ptr; /* actual write pointer to period */ unsigned int periods; size_t buffer_bytes; /* requested buffer size */ --- linux-2.6.4-rc2/include/sound/sndmagic.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/sound/sndmagic.h 2004-03-07 20:46:50.000000000 -0800 @@ -200,6 +200,7 @@ static inline int _snd_magic_bad(void *o #define azf3328_t_magic 0xa15a4200 #define snd_card_harmony_t_magic 0xa15a4300 #define bt87x_t_magic 0xa15a4400 +#define pdacf_t_magic 0xa15a4500 #else --- linux-2.6.4-rc2/include/sound/trident.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/sound/trident.h 2004-03-07 20:46:50.000000000 -0800 @@ -308,11 +308,9 @@ typedef struct { unsigned int * entries; /* 16k-aligned TLB table */ dma_addr_t entries_dmaaddr; /* 16k-aligned PCI address to TLB table */ unsigned long * shadow_entries; /* shadow entries with virtual addresses */ - void * buffer; /* pointer for table calloc */ - dma_addr_t buffer_dmaaddr; /* not accessible PCI BUS physical address */ + struct snd_dma_buffer buffer; snd_util_memhdr_t * memhdr; /* page allocation list */ - void * silent_page; /* silent page */ - dma_addr_t silent_page_dmaaddr; /* not accessible PCI BUS physical address */ + struct snd_dma_buffer silent_page; } snd_trident_tlb_t; struct _snd_trident_voice { @@ -435,6 +433,8 @@ struct _snd_trident { spinlock_t event_lock; spinlock_t voice_alloc; + struct snd_dma_device dma_dev; + struct pci_dev *pci; snd_card_t *card; snd_pcm_t *pcm; /* ADC/DAC PCM */ --- linux-2.6.4-rc2/include/sound/version.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/sound/version.h 2004-03-07 20:46:50.000000000 -0800 @@ -1,3 +1,3 @@ /* include/version.h. Generated by configure. */ -#define CONFIG_SND_VERSION "1.0.2c" -#define CONFIG_SND_DATE " (Thu Feb 05 15:41:49 2004 UTC)" +#define CONFIG_SND_VERSION "1.0.3" +#define CONFIG_SND_DATE " (Mon Mar 01 10:12:14 2004 UTC)" --- linux-2.6.4-rc2/include/sound/ymfpci.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/include/sound/ymfpci.h 2004-03-07 20:46:50.000000000 -0800 @@ -316,9 +316,8 @@ struct _snd_ymfpci { struct gameport gameport; #endif - void *work_ptr; - dma_addr_t work_ptr_addr; - unsigned long work_ptr_size; + struct snd_dma_device dma_dev; + struct snd_dma_buffer work_ptr; unsigned int bank_size_playback; unsigned int bank_size_capture; @@ -333,8 +332,7 @@ struct _snd_ymfpci { dma_addr_t bank_base_capture_addr; dma_addr_t bank_base_effect_addr; dma_addr_t work_base_addr; - void *ac3_tmp_base; - dma_addr_t ac3_tmp_base_addr; + struct snd_dma_buffer ac3_tmp_base; u32 *ctrl_playback; snd_ymfpci_playback_bank_t *bank_playback[YDSXG_PLAYBACK_VOICES][2]; --- linux-2.6.4-rc2/init/do_mounts.c 2004-01-09 00:04:33.000000000 -0800 +++ 25/init/do_mounts.c 2004-03-07 20:47:47.000000000 -0800 @@ -66,11 +66,11 @@ static dev_t __init try_name(char *name, /* read device number from .../dev */ sprintf(path, "/sys/block/%s/dev", name); - fd = open(path, 0, 0); + fd = sys_open(path, 0, 0); if (fd < 0) goto fail; - len = read(fd, buf, 32); - close(fd); + len = sys_read(fd, buf, 32); + sys_close(fd); if (len <= 0 || len == 32 || buf[len - 1] != '\n') goto fail; buf[len - 1] = '\0'; @@ -96,11 +96,11 @@ static dev_t __init try_name(char *name, /* otherwise read range from .../range */ sprintf(path, "/sys/block/%s/range", name); - fd = open(path, 0, 0); + fd = sys_open(path, 0, 0); if (fd < 0) goto fail; - len = read(fd, buf, 32); - close(fd); + len = sys_read(fd, buf, 32); + sys_close(fd); if (len <= 0 || len == 32 || buf[len - 1] != '\n') goto fail; buf[len - 1] = '\0'; @@ -141,9 +141,11 @@ dev_t __init name_to_dev_t(char *name) dev_t res = 0; int part; +#ifdef CONFIG_SYSFS sys_mkdir("/sys", 0700); if (sys_mount("sysfs", "/sys", "sysfs", 0, NULL) < 0) goto out; +#endif if (strncmp(name, "/dev/", 5) != 0) { unsigned maj, min; @@ -285,7 +287,7 @@ retry: continue; } /* - * Allow the user to distinguish between failed open + * Allow the user to distinguish between failed sys_open * and bad superblock on root device. */ __bdevname(ROOT_DEV, b); @@ -324,21 +326,21 @@ void __init change_floppy(char *fmt, ... va_start(args, fmt); vsprintf(buf, fmt, args); va_end(args); - fd = open("/dev/root", O_RDWR | O_NDELAY, 0); + fd = sys_open("/dev/root", O_RDWR | O_NDELAY, 0); if (fd >= 0) { sys_ioctl(fd, FDEJECT, 0); - close(fd); + sys_close(fd); } printk(KERN_NOTICE "VFS: Insert %s and press ENTER\n", buf); - fd = open("/dev/console", O_RDWR, 0); + fd = sys_open("/dev/console", O_RDWR, 0); if (fd >= 0) { sys_ioctl(fd, TCGETS, (long)&termios); termios.c_lflag &= ~ICANON; sys_ioctl(fd, TCSETSF, (long)&termios); - read(fd, &c, 1); + sys_read(fd, &c, 1); termios.c_lflag |= ICANON; sys_ioctl(fd, TCSETSF, (long)&termios); - close(fd); + sys_close(fd); } } #endif --- linux-2.6.4-rc2/init/do_mounts_devfs.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/init/do_mounts_devfs.c 2004-03-07 20:47:47.000000000 -0800 @@ -24,7 +24,7 @@ static int __init do_read_dir(int fd, vo { long bytes, n; char *p = buf; - lseek(fd, 0, 0); + sys_lseek(fd, 0, 0); for (bytes = 0; bytes < len; bytes += n) { n = sys_getdents64(fd, (struct linux_dirent64 *)(p + bytes), @@ -45,7 +45,7 @@ static int __init do_read_dir(int fd, vo static void * __init read_dir(char *path, int *len) { int size; - int fd = open(path, 0, 0); + int fd = sys_open(path, 0, 0); *len = 0; if (fd < 0) @@ -58,7 +58,7 @@ static void * __init read_dir(char *path break; n = do_read_dir(fd, p, size); if (n > 0) { - close(fd); + sys_close(fd); *len = n; return p; } @@ -68,7 +68,7 @@ static void * __init read_dir(char *path if (n < 0) break; } - close(fd); + sys_close(fd); return NULL; } --- linux-2.6.4-rc2/init/do_mounts.h 2004-03-03 23:12:49.000000000 -0800 +++ 25/init/do_mounts.h 2004-03-07 20:47:47.000000000 -0800 @@ -1,4 +1,3 @@ -#define __KERNEL_SYSCALLS__ #include #include #include --- linux-2.6.4-rc2/init/do_mounts_initrd.c 2003-10-25 14:45:46.000000000 -0700 +++ 25/init/do_mounts_initrd.c 2004-03-07 20:47:47.000000000 -0800 @@ -1,4 +1,5 @@ - +#define __KERNEL_SYSCALLS__ +#include #include #include #include @@ -28,12 +29,12 @@ static int __init do_linuxrc(void * shel static char *argv[] = { "linuxrc", NULL, }; extern char * envp_init[]; - close(old_fd);close(root_fd); - close(0);close(1);close(2); - setsid(); - (void) open("/dev/console",O_RDWR,0); - (void) dup(0); - (void) dup(0); + sys_close(old_fd);sys_close(root_fd); + sys_close(0);sys_close(1);sys_close(2); + sys_setsid(); + (void) sys_open("/dev/console",O_RDWR,0); + (void) sys_dup(0); + (void) sys_dup(0); return execve(shell, argv, envp_init); } @@ -47,8 +48,8 @@ static void __init handle_initrd(void) /* mount initrd on rootfs' /root */ mount_block_root("/dev/root.old", root_mountflags & ~MS_RDONLY); sys_mkdir("/old", 0700); - root_fd = open("/", 0, 0); - old_fd = open("/old", 0, 0); + root_fd = sys_open("/", 0, 0); + old_fd = sys_open("/old", 0, 0); /* move initrd over / and chdir/chroot in initrd root */ sys_chdir("/root"); sys_mount(".", "/", NULL, MS_MOVE, NULL); @@ -57,7 +58,7 @@ static void __init handle_initrd(void) pid = kernel_thread(do_linuxrc, "/linuxrc", SIGCHLD); if (pid > 0) { - while (pid != waitpid(-1, &i, 0)) + while (pid != sys_wait4(-1, &i, 0, 0)) yield(); } @@ -67,8 +68,8 @@ static void __init handle_initrd(void) /* switch root and cwd back to / of rootfs */ sys_fchdir(root_fd); sys_chroot("."); - close(old_fd); - close(root_fd); + sys_close(old_fd); + sys_close(root_fd); umount_devfs("/old/dev"); if (new_decode_dev(real_root_dev) == Root_RAM0) { @@ -84,7 +85,7 @@ static void __init handle_initrd(void) if (!error) printk("okay\n"); else { - int fd = open("/dev/root.old", O_RDWR, 0); + int fd = sys_open("/dev/root.old", O_RDWR, 0); printk("failed\n"); printk(KERN_NOTICE "Unmounting old root\n"); sys_umount("/old", MNT_DETACH); @@ -93,7 +94,7 @@ static void __init handle_initrd(void) error = fd; } else { error = sys_ioctl(fd, BLKFLSBUF, 0); - close(fd); + sys_close(fd); } printk(!error ? "okay\n" : "failed\n"); } --- linux-2.6.4-rc2/init/do_mounts_md.c 2003-09-27 18:57:47.000000000 -0700 +++ 25/init/do_mounts_md.c 2004-03-07 20:48:03.000000000 -0800 @@ -12,14 +12,17 @@ * The code for that is here. */ -static int __initdata raid_noautodetect; +static int __initdata raid_noautodetect, raid_autopart; static struct { - char device_set [MAX_MD_DEVS]; - int pers[MAX_MD_DEVS]; - int chunk[MAX_MD_DEVS]; - char *device_names[MAX_MD_DEVS]; -} md_setup_args __initdata; + int minor; + int partitioned; + int pers; + int chunk; + char *device_names; +} md_setup_args[MAX_MD_DEVS] __initdata; + +static int md_setup_ents __initdata; /* * Parse the command-line parameters given our kernel, but do not @@ -43,21 +46,37 @@ static struct { */ static int __init md_setup(char *str) { - int minor, level, factor, fault, pers; + int minor, level, factor, fault, pers, partitioned = 0; char *pername = ""; - char *str1 = str; + char *str1; + int ent; + if (*str == 'd') { + partitioned = 1; + str++; + } if (get_option(&str, &minor) != 2) { /* MD Number */ printk(KERN_WARNING "md: Too few arguments supplied to md=.\n"); return 0; } + str1 = str; if (minor >= MAX_MD_DEVS) { printk(KERN_WARNING "md: md=%d, Minor device number too high.\n", minor); return 0; - } else if (md_setup_args.device_names[minor]) { - printk(KERN_WARNING "md: md=%d, Specified more than once. " - "Replacing previous definition.\n", minor); } + for (ent=0 ; ent< md_setup_ents ; ent++) + if (md_setup_args[ent].minor == minor && + md_setup_args[ent].partitioned == partitioned) { + printk(KERN_WARNING "md: md=%s%d, Specified more than once. " + "Replacing previous definition.\n", partitioned?"d":"", minor); + break; + } + if (ent >= MAX_MD_DEVS) { + printk(KERN_WARNING "md: md=%s%d - too many md initialisations\n", partitioned?"d":"", minor); + return 0; + } + if (ent >= md_setup_ents) + md_setup_ents++; switch (get_option(&str, &level)) { /* RAID Personality */ case 2: /* could be 0 or -1.. */ if (level == 0 || level == LEVEL_LINEAR) { @@ -66,24 +85,16 @@ static int __init md_setup(char *str) printk(KERN_WARNING "md: Too few arguments supplied to md=.\n"); return 0; } - md_setup_args.pers[minor] = level; - md_setup_args.chunk[minor] = 1 << (factor+12); - switch(level) { - case LEVEL_LINEAR: + md_setup_args[ent].pers = level; + md_setup_args[ent].chunk = 1 << (factor+12); + if (level == LEVEL_LINEAR) { pers = LINEAR; pername = "linear"; - break; - case 0: + } else { pers = RAID0; pername = "raid0"; - break; - default: - printk(KERN_WARNING - "md: The kernel has not been configured for raid%d support!\n", - level); - return 0; } - md_setup_args.pers[minor] = pers; + md_setup_args[ent].pers = pers; break; } /* FALL THROUGH */ @@ -91,35 +102,38 @@ static int __init md_setup(char *str) str = str1; /* FALL THROUGH */ case 0: - md_setup_args.pers[minor] = 0; + md_setup_args[ent].pers = 0; pername="super-block"; } printk(KERN_INFO "md: Will configure md%d (%s) from %s, below.\n", minor, pername, str); - md_setup_args.device_names[minor] = str; + md_setup_args[ent].device_names = str; + md_setup_args[ent].partitioned = partitioned; + md_setup_args[ent].minor = minor; return 1; } static void __init md_setup_drive(void) { - int minor, i; + int minor, i, ent, partitioned; dev_t dev; dev_t devices[MD_SB_DISKS+1]; - for (minor = 0; minor < MAX_MD_DEVS; minor++) { + for (ent = 0; ent < md_setup_ents ; ent++) { int fd; int err = 0; char *devname; mdu_disk_info_t dinfo; char name[16], devfs_name[16]; - if (!(devname = md_setup_args.device_names[minor])) - continue; - - sprintf(name, "/dev/md%d", minor); - sprintf(devfs_name, "/dev/md/%d", minor); + minor = md_setup_args[ent].minor; + partitioned = md_setup_args[ent].partitioned; + devname = md_setup_args[ent].device_names; + + sprintf(name, "/dev/md%s%d", partitioned?"_d":"", minor); + sprintf(devfs_name, "/dev/md/%s%d", partitioned?"d":"", minor); create_dev(name, MKDEV(MD_MAJOR, minor), devfs_name); for (i = 0; i < MD_SB_DISKS && devname != 0; i++) { char *p; @@ -143,34 +157,36 @@ static void __init md_setup_drive(void) } devices[i] = dev; - md_setup_args.device_set[minor] = 1; devname = p; } devices[i] = 0; - if (!md_setup_args.device_set[minor]) + if (!i) continue; - printk(KERN_INFO "md: Loading md%d: %s\n", minor, md_setup_args.device_names[minor]); + printk(KERN_INFO "md: Loading md%s%d: %s\n", + partitioned ? "_d" : "", minor, + md_setup_args[ent].device_names); - fd = open(name, 0, 0); + fd = sys_open(name, 0, 0); if (fd < 0) { - printk(KERN_ERR "md: open failed - cannot start array %d\n", minor); + printk(KERN_ERR "md: open failed - cannot start " + "array %s\n", name); continue; } if (sys_ioctl(fd, SET_ARRAY_INFO, 0) == -EBUSY) { printk(KERN_WARNING "md: Ignoring md=%d, already autodetected. (Use raid=noautodetect)\n", minor); - close(fd); + sys_close(fd); continue; } - if (md_setup_args.pers[minor]) { + if (md_setup_args[ent].pers) { /* non-persistent */ mdu_array_info_t ainfo; - ainfo.level = pers_to_level(md_setup_args.pers[minor]); + ainfo.level = pers_to_level(md_setup_args[ent].pers); ainfo.size = 0; ainfo.nr_disks =0; ainfo.raid_disks =0; @@ -181,7 +197,7 @@ static void __init md_setup_drive(void) ainfo.state = (1 << MD_SB_CLEAN); ainfo.layout = 0; - ainfo.chunk_size = md_setup_args.chunk[minor]; + ainfo.chunk_size = md_setup_args[ent].chunk; err = sys_ioctl(fd, SET_ARRAY_INFO, (long)&ainfo); for (i = 0; !err && i <= MD_SB_DISKS; i++) { dev = devices[i]; @@ -209,7 +225,7 @@ static void __init md_setup_drive(void) err = sys_ioctl(fd, RUN_ARRAY, 0); if (err) printk(KERN_WARNING "md: starting md%d failed\n", minor); - close(fd); + sys_close(fd); } } @@ -229,6 +245,10 @@ static int __init raid_setup(char *str) if (!strncmp(str, "noautodetect", wlen)) raid_noautodetect = 1; + if (strncmp(str, "partitionable", wlen)==0) + raid_autopart = 1; + if (strncmp(str, "part", wlen)==0) + raid_autopart = 1; pos += wlen+1; } return 1; @@ -243,10 +263,10 @@ void __init md_run_setup(void) if (raid_noautodetect) printk(KERN_INFO "md: Skipping autodetection of RAID arrays. (raid=noautodetect)\n"); else { - int fd = open("/dev/md0", 0, 0); + int fd = sys_open("/dev/md0", 0, 0); if (fd >= 0) { - sys_ioctl(fd, RAID_AUTORUN, 0); - close(fd); + sys_ioctl(fd, RAID_AUTORUN, raid_autopart); + sys_close(fd); } } md_setup_drive(); --- linux-2.6.4-rc2/init/do_mounts_rd.c 2004-02-17 20:48:46.000000000 -0800 +++ 25/init/do_mounts_rd.c 2004-03-07 20:47:47.000000000 -0800 @@ -69,8 +69,8 @@ identify_ramdisk_image(int fd, int start /* * Read block 0 to test for gzipped kernel */ - lseek(fd, start_block * BLOCK_SIZE, 0); - read(fd, buf, size); + sys_lseek(fd, start_block * BLOCK_SIZE, 0); + sys_read(fd, buf, size); /* * If it matches the gzip magic numbers, return -1 @@ -104,8 +104,8 @@ identify_ramdisk_image(int fd, int start /* * Read block 1 to test for minix and ext2 superblock */ - lseek(fd, (start_block+1) * BLOCK_SIZE, 0); - read(fd, buf, size); + sys_lseek(fd, (start_block+1) * BLOCK_SIZE, 0); + sys_read(fd, buf, size); /* Try minix */ if (minixsb->s_magic == MINIX_SUPER_MAGIC || @@ -131,7 +131,7 @@ identify_ramdisk_image(int fd, int start start_block); done: - lseek(fd, start_block * BLOCK_SIZE, 0); + sys_lseek(fd, start_block * BLOCK_SIZE, 0); kfree(buf); return nblocks; } @@ -148,11 +148,11 @@ int __init rd_load_image(char *from) char rotator[4] = { '|' , '/' , '-' , '\\' }; #endif - out_fd = open("/dev/ram", O_RDWR, 0); + out_fd = sys_open("/dev/ram", O_RDWR, 0); if (out_fd < 0) goto out; - in_fd = open(from, O_RDONLY, 0); + in_fd = sys_open(from, O_RDONLY, 0); if (in_fd < 0) goto noclose_input; @@ -217,20 +217,20 @@ int __init rd_load_image(char *from) if (i && (i % devblocks == 0)) { printk("done disk #%d.\n", disk++); rotate = 0; - if (close(in_fd)) { + if (sys_close(in_fd)) { printk("Error closing the disk.\n"); goto noclose_input; } change_floppy("disk #%d", disk); - in_fd = open(from, O_RDONLY, 0); + in_fd = sys_open(from, O_RDONLY, 0); if (in_fd < 0) { printk("Error opening disk.\n"); goto noclose_input; } printk("Loading disk #%d... ", disk); } - read(in_fd, buf, BLOCK_SIZE); - write(out_fd, buf, BLOCK_SIZE); + sys_read(in_fd, buf, BLOCK_SIZE); + sys_write(out_fd, buf, BLOCK_SIZE); #if !defined(CONFIG_ARCH_S390) && !defined(CONFIG_PPC_ISERIES) if (!(i % 16)) { printk("%c\b", rotator[rotate & 0x3]); @@ -243,9 +243,9 @@ int __init rd_load_image(char *from) successful_load: res = 1; done: - close(in_fd); + sys_close(in_fd); noclose_input: - close(out_fd); + sys_close(out_fd); out: kfree(buf); sys_unlink("/dev/ram"); @@ -342,7 +342,7 @@ static int __init fill_inbuf(void) { if (exit_code) return -1; - insize = read(crd_infd, inbuf, INBUFSIZ); + insize = sys_read(crd_infd, inbuf, INBUFSIZ); if (insize == 0) { error("RAMDISK: ran out of compressed data"); return -1; @@ -363,7 +363,7 @@ static void __init flush_window(void) unsigned n, written; uch *in, ch; - written = write(crd_outfd, window, outcnt); + written = sys_write(crd_outfd, window, outcnt); if (written != outcnt && unzip_error == 0) { printk(KERN_ERR "RAMDISK: incomplete write (%d != %d) %ld\n", written, outcnt, bytes_out); --- linux-2.6.4-rc2/init/initramfs.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/init/initramfs.c 2004-03-07 20:47:47.000000000 -0800 @@ -1,10 +1,8 @@ -#define __KERNEL_SYSCALLS__ #include #include #include #include #include -#include #include #include #include --- linux-2.6.4-rc2/init/Kconfig 2004-03-03 23:12:49.000000000 -0800 +++ 25/init/Kconfig 2004-03-07 20:47:49.000000000 -0800 @@ -43,7 +43,7 @@ config CLEAN_COMPILE config STANDALONE bool "Select only drivers that don't need compile-time external firmware" if EXPERIMENTAL - default y + default n help Select this option if you don't have magic firmware for drivers that need it. @@ -91,6 +91,24 @@ config SYSVIPC section 6.4 of the Linux Programmer's Guide, available from . +config POSIX_MQUEUE + bool "POSIX Message Queues" + depends on EXPERIMENTAL + ---help--- + POSIX variant of message queues is a part of IPC. In POSIX message + queues every message has a priority which decides about succession + of receiving it by a process. If you want to compile and run + programs written e.g. for Solaris with use of its POSIX message + queues (functions mq_*) say Y here. To use this feature you will + also need mqueue library, available from + + + POSIX message queues are visible as a filesystem called 'mqueue' + and can be mounted somewhere if you want to do filesystem + operations on message queues. + + If unsure, say Y. + config BSD_PROCESS_ACCT bool "BSD Process Accounting" help @@ -304,4 +322,10 @@ config KMOD runs modprobe with the appropriate arguments, thereby loading the module if it is available. If unsure, say Y. +config STOP_MACHINE + bool + default y + depends on (SMP && MODULE_UNLOAD) || HOTPLUG_CPU + help + Need stop_machine() primitive. endmenu --- linux-2.6.4-rc2/init/main.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/init/main.c 2004-03-07 20:47:47.000000000 -0800 @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include @@ -41,6 +40,7 @@ #include #include #include +#include #include #include @@ -155,8 +155,11 @@ static int __init obsolete_checksetup(ch p = &__setup_start; do { int n = strlen(p->str); - if (!strncmp(line,p->str,n)) { - if (p->setup_func(line+n)) + if (!strncmp(line, p->str, n)) { + if (!p->setup_func) { + printk(KERN_WARNING "Parameter %s is obsolete, ignored\n", p->str); + return 1; + } else if (p->setup_func(line + n)) return 1; } p++; @@ -417,12 +420,12 @@ asmlinkage void __init start_kernel(void build_all_zonelists(); page_alloc_init(); + trap_init(); printk("Kernel command line: %s\n", saved_command_line); parse_args("Booting kernel", command_line, __start___param, __stop___param - __start___param, &unknown_bootoption); sort_main_extable(); - trap_init(); rcu_init(); init_IRQ(); pidhash_init(); @@ -564,7 +567,6 @@ static void do_pre_smp_initcalls(void) migration_init(); #endif - node_nr_running_init(); spawn_ksoftirqd(); } @@ -595,8 +597,16 @@ static int init(void * unused) do_pre_smp_initcalls(); smp_init(); + sched_init_smp(); do_basic_setup(); + /* + * check if there is an early userspace init, if yes + * let it do all the work + */ + if (sys_access("/init", 0) == 0) + execute_command = "/init"; + else prepare_namespace(); /* @@ -608,11 +618,11 @@ static int init(void * unused) unlock_kernel(); system_running = 1; - if (open("/dev/console", O_RDWR, 0) < 0) + if (sys_open("/dev/console", O_RDWR, 0) < 0) printk("Warning: unable to open an initial console.\n"); - (void) dup(0); - (void) dup(0); + (void) sys_dup(0); + (void) sys_dup(0); /* * We try each of these until one succeeds. --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/ipc/compat.c 2004-03-07 20:47:07.000000000 -0800 @@ -0,0 +1,734 @@ +/* + * 32 bit compatibility code for System V IPC + * + * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1999 Arun Sharma + * Copyright (C) 2000 VA Linux Co + * Copyright (C) 2000 Don Dugger + * Copyright (C) 2000 Hewlett-Packard Co. + * Copyright (C) 2000 David Mosberger-Tang + * Copyright (C) 2000 Gerhard Tonn (ton@de.ibm.com) + * Copyright (C) 2000-2002 Andi Kleen, SuSE Labs (x86-64 port) + * Copyright (C) 2000 Silicon Graphics, Inc. + * Copyright (C) 2001 IBM + * Copyright (C) 2004 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright (C) 2004 Arnd Bergmann (arnd@arndb.de) + * + * This code is collected from the versions for sparc64, mips64, s390x, ia64, + * ppc64 and x86_64, all of which are based on the original sparc64 version + * by Jakub Jelinek. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "util.h" + +struct compat_msgbuf { + compat_long_t mtype; + char mtext[1]; +}; + +struct compat_ipc_perm { + key_t key; + compat_uid_t uid; + compat_gid_t gid; + compat_uid_t cuid; + compat_gid_t cgid; + compat_mode_t mode; + unsigned short seq; +}; + +struct compat_semid_ds { + struct compat_ipc_perm sem_perm; + compat_time_t sem_otime; + compat_time_t sem_ctime; + compat_uptr_t sem_base; + compat_uptr_t sem_pending; + compat_uptr_t sem_pending_last; + compat_uptr_t undo; + unsigned short sem_nsems; +}; + +struct compat_msqid_ds { + struct compat_ipc_perm msg_perm; + compat_uptr_t msg_first; + compat_uptr_t msg_last; + compat_time_t msg_stime; + compat_time_t msg_rtime; + compat_time_t msg_ctime; + compat_ulong_t msg_lcbytes; + compat_ulong_t msg_lqbytes; + unsigned short msg_cbytes; + unsigned short msg_qnum; + unsigned short msg_qbytes; + compat_ipc_pid_t msg_lspid; + compat_ipc_pid_t msg_lrpid; +}; + +struct compat_shmid_ds { + struct compat_ipc_perm shm_perm; + int shm_segsz; + compat_time_t shm_atime; + compat_time_t shm_dtime; + compat_time_t shm_ctime; + compat_ipc_pid_t shm_cpid; + compat_ipc_pid_t shm_lpid; + unsigned short shm_nattch; + unsigned short shm_unused; + compat_uptr_t shm_unused2; + compat_uptr_t shm_unused3; +}; + +struct compat_ipc_kludge { + compat_uptr_t msgp; + compat_long_t msgtyp; +}; + +struct compat_shminfo64 { + compat_ulong_t shmmax; + compat_ulong_t shmmin; + compat_ulong_t shmmni; + compat_ulong_t shmseg; + compat_ulong_t shmall; + compat_ulong_t __unused1; + compat_ulong_t __unused2; + compat_ulong_t __unused3; + compat_ulong_t __unused4; +}; + +struct compat_shm_info { + compat_int_t used_ids; + compat_ulong_t shm_tot, shm_rss, shm_swp; + compat_ulong_t swap_attempts, swap_successes; +}; + +extern int sem_ctls[]; +#define sc_semopm (sem_ctls[2]) +#define MAXBUF (64*1024) + +static inline int compat_ipc_parse_version(int *cmd) +{ + int version = *cmd & IPC_64; + + /* this is tricky: architectures that have support for the old + * ipc structures in 64 bit binaries need to have IPC_64 set + * in cmd, the others need to have it cleared */ +#ifndef ipc_parse_version + *cmd |= IPC_64; +#else + *cmd &= ~IPC_64; +#endif + return version; +} + +static inline int __get_compat_ipc64_perm(struct ipc64_perm *p64, + struct compat_ipc64_perm *up64) +{ + int err; + + err = __get_user(p64->uid, &up64->uid); + err |= __get_user(p64->gid, &up64->gid); + err |= __get_user(p64->mode, &up64->mode); + return err; +} + +static inline int __get_compat_ipc_perm(struct ipc64_perm *p, + struct compat_ipc_perm *up) +{ + int err; + + err = __get_user(p->uid, &up->uid); + err |= __get_user(p->gid, &up->gid); + err |= __get_user(p->mode, &up->mode); + return err; +} + +static inline int __put_compat_ipc64_perm(struct ipc64_perm *p64, + struct compat_ipc64_perm *up64) +{ + int err; + + err = __put_user(p64->key, &up64->key); + err |= __put_user(p64->uid, &up64->uid); + err |= __put_user(p64->gid, &up64->gid); + err |= __put_user(p64->cuid, &up64->cuid); + err |= __put_user(p64->cgid, &up64->cgid); + err |= __put_user(p64->mode, &up64->mode); + err |= __put_user(p64->seq, &up64->seq); + return err; +} + +static inline int __put_compat_ipc_perm(struct ipc64_perm *p, + struct compat_ipc_perm *up) +{ + int err; + compat_uid_t u; + compat_gid_t g; + + err = __put_user(p->key, &up->key); + SET_UID(u, p->uid); + err |= __put_user(u, &up->uid); + SET_GID(g, p->gid); + err |= __put_user(g, &up->gid); + SET_UID(u, p->cuid); + err |= __put_user(u, &up->cuid); + SET_GID(g, p->cgid); + err |= __put_user(g, &up->cgid); + err |= __put_user(p->mode, &up->mode); + err |= __put_user(p->seq, &up->seq); + return err; +} + +static inline int get_compat_semid64_ds(struct semid64_ds *s64, + struct compat_semid64_ds *up64) +{ + if (!access_ok (VERIFY_READ, up64, sizeof(*up64))) + return -EFAULT; + return __get_compat_ipc64_perm(&s64->sem_perm, &up64->sem_perm); +} + +static inline int get_compat_semid_ds(struct semid64_ds *s, + struct compat_semid_ds *up) +{ + if (!access_ok (VERIFY_READ, up, sizeof(*up))) + return -EFAULT; + return __get_compat_ipc_perm(&s->sem_perm, &up->sem_perm); +} + +static inline int put_compat_semid64_ds(struct semid64_ds *s64, + struct compat_semid64_ds *up64) +{ + int err; + + if (!access_ok (VERIFY_WRITE, up64, sizeof(*up64))) + return -EFAULT; + err = __put_compat_ipc64_perm(&s64->sem_perm, &up64->sem_perm); + err |= __put_user(s64->sem_otime, &up64->sem_otime); + err |= __put_user(s64->sem_ctime, &up64->sem_ctime); + err |= __put_user(s64->sem_nsems, &up64->sem_nsems); + return err; +} + +static inline int put_compat_semid_ds(struct semid64_ds *s, + struct compat_semid_ds *up) +{ + int err; + + if (!access_ok (VERIFY_WRITE, up, sizeof(*up))) + err = -EFAULT; + err = __put_compat_ipc_perm(&s->sem_perm, &up->sem_perm); + err |= __put_user(s->sem_otime, &up->sem_otime); + err |= __put_user(s->sem_ctime, &up->sem_ctime); + err |= __put_user(s->sem_nsems, &up->sem_nsems); + return err; +} + +static inline int do_semctl(int semid, int semnum, int cmd, union semun arg) +{ + mm_segment_t old_fs; + int err; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_semctl(semid, semnum, cmd, arg); + set_fs(old_fs); + + return err; +} +long compat_sys_semctl(int first, int second, int third, void __user *uptr) +{ + union semun fourth; + u32 pad; + int err, err2; + struct semid64_ds s64; + int version = compat_ipc_parse_version(&third); + + if (!uptr) + return -EINVAL; + if (get_user(pad, (u32 __user *) uptr)) + return -EFAULT; + if ((third & (~IPC_64)) == SETVAL) + fourth.val = (int) pad; + else + fourth.__pad = compat_ptr(pad); + switch (third & (~IPC_64)) { + case IPC_INFO: + case IPC_RMID: + case SEM_INFO: + case GETVAL: + case GETPID: + case GETNCNT: + case GETZCNT: + case GETALL: + case SETVAL: + case SETALL: + err = sys_semctl(first, second, third, fourth); + break; + + case IPC_STAT: + case SEM_STAT: + fourth.__pad = &s64; + err = do_semctl(first, second, third, fourth); + if (err < 0) + break; + + if (version == IPC_64) { + err2 = put_compat_semid64_ds(&s64, compat_ptr(pad)); + } else { + err2 = put_compat_semid_ds(&s64, compat_ptr(pad)); + } + if (err2) + err = -EFAULT; + break; + + case IPC_SET: + if (version == IPC_64) { + err = get_compat_semid64_ds(&s64, compat_ptr(pad)); + } else { + err = get_compat_semid_ds(&s64, compat_ptr(pad)); + } + if (err) + break; + + fourth.__pad = &s64; + err = do_semctl(first, second, third, fourth); + break; + + default: + err = -EINVAL; + break; + } + return err; +} + +long compat_sys_msgsnd(int first, int second, int third, void __user *uptr) +{ + struct msgbuf *p; + struct compat_msgbuf __user *up; + mm_segment_t old_fs; + int err; + + if (first < 0) + return -EINVAL; + if (second < 0 || (second >= MAXBUF - sizeof(struct msgbuf))) + return -EINVAL; + + p = kmalloc(second + sizeof(struct msgbuf), GFP_USER); + if (!p) + return -ENOMEM; + err = -EFAULT; + up = uptr; + if (get_user(p->mtype, &up->mtype) || + copy_from_user(p->mtext, &up->mtext, second)) + goto out; + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_msgsnd(first, p, second, third); + set_fs(old_fs); +out: + kfree(p); + return err; +} + +long compat_sys_msgrcv(int first, int second, int msgtyp, int third, + int version, void __user *uptr) +{ + struct msgbuf *p; + struct compat_msgbuf __user *up; + mm_segment_t old_fs; + int err; + + if (first < 0) + return -EINVAL; + if (second < 0 || (second >= MAXBUF - sizeof(struct msgbuf))) + return -EINVAL; + + if (!version) { + struct compat_ipc_kludge __user *uipck = uptr; + struct compat_ipc_kludge ipck; + + err = -EINVAL; + if (!uptr) + goto out; + err = -EFAULT; + if (copy_from_user (&ipck, uipck, sizeof(ipck))) + goto out; + uptr = compat_ptr(ipck.msgp); + msgtyp = ipck.msgtyp; + } + err = -ENOMEM; + p = kmalloc(second + sizeof(struct msgbuf), GFP_USER); + if (!p) + goto out; + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_msgrcv(first, p, second, msgtyp, third); + set_fs(old_fs); + if (err < 0) + goto free_then_out; + up = uptr; + if (put_user(p->mtype, &up->mtype) || + __copy_to_user(&up->mtext, p->mtext, err)) + err = -EFAULT; +free_then_out: + kfree(p); +out: + return err; +} + +static inline int get_compat_msqid64(struct msqid64_ds *m64, + struct compat_msqid64_ds __user *up64) +{ + int err; + + if (!access_ok(VERIFY_READ, up64, sizeof(*up64))) + return -EFAULT; + err = __get_compat_ipc64_perm(&m64->msg_perm, &up64->msg_perm); + err |= __get_user(m64->msg_qbytes, &up64->msg_qbytes); + return err; +} + +static inline int get_compat_msqid(struct msqid64_ds *m, + struct compat_msqid_ds __user *up) +{ + int err; + + if (!access_ok(VERIFY_READ, up, sizeof(*up))) + return -EFAULT; + err = __get_compat_ipc_perm(&m->msg_perm, &up->msg_perm); + err |= __get_user(m->msg_qbytes, &up->msg_qbytes); + return err; +} + +static inline int put_compat_msqid64_ds(struct msqid64_ds *m64, + struct compat_msqid64_ds __user __user *up64) +{ + int err; + + if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64))) + return -EFAULT; + err = __put_compat_ipc64_perm(&m64->msg_perm, &up64->msg_perm); + err |= __put_user(m64->msg_stime, &up64->msg_stime); + err |= __put_user(m64->msg_rtime, &up64->msg_rtime); + err |= __put_user(m64->msg_ctime, &up64->msg_ctime); + err |= __put_user(m64->msg_cbytes, &up64->msg_cbytes); + err |= __put_user(m64->msg_qnum, &up64->msg_qnum); + err |= __put_user(m64->msg_qbytes, &up64->msg_qbytes); + err |= __put_user(m64->msg_lspid, &up64->msg_lspid); + err |= __put_user(m64->msg_lrpid, &up64->msg_lrpid); + return err; +} + +static inline int put_compat_msqid_ds(struct msqid64_ds *m, + struct compat_msqid_ds __user *up) +{ + int err; + + if (!access_ok(VERIFY_WRITE, up, sizeof(*up))) + return -EFAULT; + err = __put_compat_ipc_perm(&m->msg_perm, &up->msg_perm); + err |= __put_user(m->msg_stime, &up->msg_stime); + err |= __put_user(m->msg_rtime, &up->msg_rtime); + err |= __put_user(m->msg_ctime, &up->msg_ctime); + err |= __put_user(m->msg_cbytes, &up->msg_cbytes); + err |= __put_user(m->msg_qnum, &up->msg_qnum); + err |= __put_user(m->msg_qbytes, &up->msg_qbytes); + err |= __put_user(m->msg_lspid, &up->msg_lspid); + err |= __put_user(m->msg_lrpid, &up->msg_lrpid); + return err; +} + +static inline int do_msgctl(int first, int second, void __user *buf) +{ + mm_segment_t old_fs; + int err; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_msgctl(first, second, buf); + set_fs(old_fs); + + return err; +} + +long compat_sys_msgctl(int first, int second, void __user *uptr) +{ + int err, err2; + struct msqid64_ds m64; + int version = compat_ipc_parse_version(&second); + + switch (second & (~IPC_64)) { + case IPC_INFO: + case IPC_RMID: + case MSG_INFO: + err = sys_msgctl(first, second, uptr); + break; + + case IPC_SET: + if (version == IPC_64) { + err = get_compat_msqid64(&m64, uptr); + } else { + err = get_compat_msqid(&m64, uptr); + } + if (err) + break; + + err = do_msgctl(first, second, &m64); + break; + + case IPC_STAT: + case MSG_STAT: + err = do_msgctl(first, second, &m64); + if (err < 0) + break; + + if (version == IPC_64) { + err2 = put_compat_msqid64_ds(&m64, uptr); + } else { + err2 = put_compat_msqid_ds(&m64, uptr); + } + if (err2) + err = -EFAULT; + break; + + default: + err = -EINVAL; + break; + } + return err; +} + +long compat_sys_shmat(int first, int second, compat_uptr_t third, int version, + void __user *uptr) +{ + int err; + unsigned long raddr; + compat_ulong_t __user *uaddr; + + if (version == 1) + return -EINVAL; + err = do_shmat(first, uptr, second, &raddr); + if (err < 0) + return err; + uaddr = compat_ptr(third); + return put_user(raddr, uaddr); +} + +static inline int get_compat_shmid64_ds(struct shmid64_ds *s64, + struct compat_shmid64_ds __user *up64) +{ + if (!access_ok(VERIFY_READ, up64, sizeof(*up64))) + return -EFAULT; + return __get_compat_ipc64_perm(&s64->shm_perm, &up64->shm_perm); +} + +static inline int get_compat_shmid_ds(struct shmid64_ds *s, + struct compat_shmid_ds __user *up) +{ + if (!access_ok(VERIFY_READ, up, sizeof(*up))) + return -EFAULT; + return __get_compat_ipc_perm(&s->shm_perm, &up->shm_perm); +} + +static inline int put_compat_shmid64_ds(struct shmid64_ds *s64, + struct compat_shmid64_ds __user *up64) +{ + int err; + + if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64))) + return -EFAULT; + err = __put_compat_ipc64_perm(&s64->shm_perm, &up64->shm_perm); + err |= __put_user(s64->shm_atime, &up64->shm_atime); + err |= __put_user(s64->shm_dtime, &up64->shm_dtime); + err |= __put_user(s64->shm_ctime, &up64->shm_ctime); + err |= __put_user(s64->shm_segsz, &up64->shm_segsz); + err |= __put_user(s64->shm_nattch, &up64->shm_nattch); + err |= __put_user(s64->shm_cpid, &up64->shm_cpid); + err |= __put_user(s64->shm_lpid, &up64->shm_lpid); + return err; +} + +static inline int put_compat_shmid_ds(struct shmid64_ds *s, + struct compat_shmid_ds __user *up) +{ + int err; + + if (!access_ok(VERIFY_WRITE, up, sizeof(*up))) + return -EFAULT; + err = __put_compat_ipc_perm(&s->shm_perm, &up->shm_perm); + err |= __put_user(s->shm_atime, &up->shm_atime); + err |= __put_user(s->shm_dtime, &up->shm_dtime); + err |= __put_user(s->shm_ctime, &up->shm_ctime); + err |= __put_user(s->shm_segsz, &up->shm_segsz); + err |= __put_user(s->shm_nattch, &up->shm_nattch); + err |= __put_user(s->shm_cpid, &up->shm_cpid); + err |= __put_user(s->shm_lpid, &up->shm_lpid); + return err; +} + +static inline int put_compat_shminfo64(struct shminfo64 *smi, + struct compat_shminfo64 __user *up64) +{ + int err; + + if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64))) + return -EFAULT; + err = __put_user(smi->shmmax, &up64->shmmax); + err |= __put_user(smi->shmmin, &up64->shmmin); + err |= __put_user(smi->shmmni, &up64->shmmni); + err |= __put_user(smi->shmseg, &up64->shmseg); + err |= __put_user(smi->shmall, &up64->shmall); + return err; +} + +static inline int put_compat_shminfo(struct shminfo64 *smi, + struct shminfo __user *up) +{ + int err; + + if (!access_ok(VERIFY_WRITE, up, sizeof(*up))) + return -EFAULT; + err = __put_user(smi->shmmax, &up->shmmax); + err |= __put_user(smi->shmmin, &up->shmmin); + err |= __put_user(smi->shmmni, &up->shmmni); + err |= __put_user(smi->shmseg, &up->shmseg); + err |= __put_user(smi->shmall, &up->shmall); +} + +static inline int put_compat_shm_info(struct shm_info *si, + struct compat_shm_info __user *uip) +{ + int err; + + if (!access_ok(VERIFY_WRITE, uip, sizeof(*uip))) + return -EFAULT; + err = __put_user(si->used_ids, &uip->used_ids); + err |= __put_user(si->shm_tot, &uip->shm_tot); + err |= __put_user(si->shm_rss, &uip->shm_rss); + err |= __put_user(si->shm_swp, &uip->shm_swp); + err |= __put_user(si->swap_attempts, &uip->swap_attempts); + err |= __put_user(si->swap_successes, &uip->swap_successes); + return err; +} + +static inline int do_shmctl(int shmid, int cmd, void *buf) +{ + mm_segment_t old_fs; + int err; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_shmctl(shmid, cmd, buf); + set_fs(old_fs); + + return err; +} + +long compat_sys_shmctl(int first, int second, void __user *uptr) +{ + struct shmid64_ds s64; + struct shminfo64 smi; + struct shm_info si; + int err, err2; + int version = compat_ipc_parse_version(&second); + + switch (second & (~IPC_64)) { + case IPC_RMID: + case SHM_LOCK: + case SHM_UNLOCK: + err = sys_shmctl(first, second, uptr); + break; + + case IPC_INFO: + err = do_shmctl(first, second, &smi); + if (err < 0) + break; + + if (version == IPC_64) { + err2 = put_compat_shminfo64(&smi, uptr); + } else { + err2 = put_compat_shminfo(&smi, uptr); + } + if (err2) + err = -EFAULT; + break; + + + case IPC_SET: + if (version == IPC_64) { + err = get_compat_shmid64_ds(&s64, uptr); + } else { + err = get_compat_shmid_ds(&s64, uptr); + } + if (err) + break; + + err = do_shmctl(first, second, &s64); + break; + + case IPC_STAT: + case SHM_STAT: + err = do_shmctl(first, second, &s64); + if (err < 0) + break; + + if (version == IPC_64) { + err2 = put_compat_shmid64_ds(&s64, uptr); + } else { + err2 = put_compat_shmid_ds(&s64, uptr); + } + if (err2) + err = -EFAULT; + break; + + case SHM_INFO: + err = do_shmctl(first, second, &si); + if (err < 0) + break; + err2 = put_compat_shm_info(&si, uptr); + if (err2) + err = -EFAULT; + break; + + default: + err = -EINVAL; + break; + } + return err; +} + +long compat_sys_semtimedop(int semid, struct sembuf __user *tsems, + unsigned nsops, const struct compat_timespec __user *timeout) +{ + struct timespec ts, __user *ts64; + + /* parameter checking precedence should mirror sys_semtimedop() */ + if (nsops < 1 || semid < 0) + return -EINVAL; + if (nsops > sc_semopm) + return -E2BIG; + if (!access_ok(VERIFY_READ, tsems, nsops * sizeof(struct sembuf))) + return -EFAULT; + if (!timeout) + return sys_semtimedop(semid, tsems, nsops, 0); + + ts64 = compat_alloc_user_space(sizeof(*ts64)); + if (get_compat_timespec(&ts, timeout)) + return -EFAULT; + if (copy_to_user(ts64, &ts, sizeof(ts))) + return -EFAULT; + + return sys_semtimedop(semid, tsems, nsops, ts64); +} --- linux-2.6.4-rc2/ipc/Makefile 2003-06-14 12:17:58.000000000 -0700 +++ 25/ipc/Makefile 2004-03-07 20:47:49.000000000 -0800 @@ -2,6 +2,7 @@ # Makefile for the linux ipc. # -obj-y := util.o +obj-$(CONFIG_SYSVIPC_COMPAT) += compat.o +obj-$(CONFIG_SYSVIPC) += util.o msgutil.o msg.o sem.o shm.o +obj-$(CONFIG_POSIX_MQUEUE) += mqueue.o msgutil.o -obj-$(CONFIG_SYSVIPC) += msg.o sem.o shm.o --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/ipc/mqueue.c 2004-03-07 20:47:51.000000000 -0800 @@ -0,0 +1,1256 @@ +/* + * POSIX message queues filesystem for Linux. + * + * Copyright (C) 2003,2004 Krzysztof Benedyczak (golbi@mat.uni.torun.pl) + * Michal Wronski (wrona@mat.uni.torun.pl) + * + * Spinlocks: Mohamed Abbas (abbas.mohamed@intel.com) + * Lockless receive & send, fd based notify: + * Manfred Spraul (manfred@colorfullife.com) + * + * This file is released under the GPL. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "util.h" + +#define MQUEUE_MAGIC 0x19800202 +#define DIRENT_SIZE 20 +#define FILENT_SIZE 80 + +#define SEND 0 +#define RECV 1 + +#define STATE_NONE 0 +#define STATE_PENDING 1 +#define STATE_READY 2 + +#define NP_NONE ((void*)NOTIFY_NONE) +#define NP_WOKENUP ((void*)NOTIFY_WOKENUP) +#define NP_REMOVED ((void*)NOTIFY_REMOVED) +/* used by sysctl */ +#define FS_MQUEUE 1 +#define CTL_QUEUESMAX 2 +#define CTL_MSGMAX 3 +#define CTL_MSGSIZEMAX 4 + +/* default values */ +#define DFLT_QUEUESMAX 64 /* max number of message queues */ +#define DFLT_MSGMAX 40 /* max number of messages in each queue */ +#define HARD_MSGMAX (131072/sizeof(void*)) +#define DFLT_MSGSIZEMAX 16384 /* max message size */ + +struct ext_wait_queue { /* queue of sleeping tasks */ + struct task_struct *task; + struct list_head list; + struct msg_msg *msg; /* ptr of loaded message */ + int state; /* one of STATE_* values */ +}; + +struct mqueue_inode_info { + struct mq_attr attr; + struct msg_msg **messages; + + pid_t notify_owner; /* != 0 means notification registered */ + struct sigevent notify; + struct file *notify_filp; + + /* for tasks waiting for free space and messages, respectively */ + struct ext_wait_queue e_wait_q[2]; + wait_queue_head_t wait_q; + + unsigned long qsize; /* size of queue in memory (sum of all msgs) */ + spinlock_t lock; + struct inode vfs_inode; +}; + +static struct inode_operations mqueue_dir_inode_operations; +static struct file_operations mqueue_file_operations; +static struct file_operations mqueue_notify_fops; +static struct super_operations mqueue_super_ops; +static void remove_notification(struct mqueue_inode_info *info); + +static spinlock_t mq_lock; +static kmem_cache_t *mqueue_inode_cachep; +static struct vfsmount *mqueue_mnt; + +static unsigned int queues_count; +static unsigned int queues_max = DFLT_QUEUESMAX; +static unsigned int msg_max = DFLT_MSGMAX; +static unsigned int msgsize_max = DFLT_MSGSIZEMAX; + +static struct ctl_table_header * mq_sysctl_table; + +static inline struct mqueue_inode_info *MQUEUE_I(struct inode *inode) +{ + return container_of(inode, struct mqueue_inode_info, vfs_inode); +} + +static struct inode *mqueue_get_inode(struct super_block *sb, int mode) +{ + struct inode *inode; + + inode = new_inode(sb); + if (inode) { + inode->i_mode = mode; + inode->i_uid = current->fsuid; + inode->i_gid = current->fsgid; + inode->i_blksize = PAGE_CACHE_SIZE; + inode->i_blocks = 0; + inode->i_mtime = inode->i_ctime = inode->i_atime = + CURRENT_TIME; + + if (S_ISREG(mode)) { + struct mqueue_inode_info *info; + + inode->i_fop = &mqueue_file_operations; + inode->i_size = FILENT_SIZE; + /* mqueue specific info */ + info = MQUEUE_I(inode); + spin_lock_init(&info->lock); + init_waitqueue_head(&info->wait_q); + INIT_LIST_HEAD(&info->e_wait_q[0].list); + INIT_LIST_HEAD(&info->e_wait_q[1].list); + info->notify_owner = 0; + info->qsize = 0; + memset(&info->attr, 0, sizeof(info->attr)); + info->attr.mq_maxmsg = DFLT_MSGMAX; + info->attr.mq_msgsize = DFLT_MSGSIZEMAX; + info->messages = kmalloc(DFLT_MSGMAX * sizeof(struct msg_msg *), GFP_KERNEL); + if (!info->messages) { + make_bad_inode(inode); + iput(inode); + inode = NULL; + } + } else if (S_ISDIR(mode)) { + inode->i_nlink++; + /* Some things misbehave if size == 0 on a directory */ + inode->i_size = 2 * DIRENT_SIZE; + inode->i_op = &mqueue_dir_inode_operations; + inode->i_fop = &simple_dir_operations; + } + } + return inode; +} + +static int mqueue_fill_super(struct super_block *sb, void *data, int silent) +{ + struct inode *inode; + + sb->s_blocksize = PAGE_CACHE_SIZE; + sb->s_blocksize_bits = PAGE_CACHE_SHIFT; + sb->s_magic = MQUEUE_MAGIC; + sb->s_op = &mqueue_super_ops; + + inode = mqueue_get_inode(sb, S_IFDIR | S_IRWXUGO); + if (!inode) + return -ENOMEM; + + sb->s_root = d_alloc_root(inode); + if (!sb->s_root) { + iput(inode); + return -ENOMEM; + } + + return 0; +} + +static struct super_block *mqueue_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *data) +{ + return get_sb_single(fs_type, flags, data, mqueue_fill_super); +} + +static void init_once(void *foo, kmem_cache_t * cachep, unsigned long flags) +{ + struct mqueue_inode_info *p = (struct mqueue_inode_info *) foo; + + if ((flags & (SLAB_CTOR_VERIFY | SLAB_CTOR_CONSTRUCTOR)) == + SLAB_CTOR_CONSTRUCTOR) + inode_init_once(&p->vfs_inode); +} + +static struct inode *mqueue_alloc_inode(struct super_block *sb) +{ + struct mqueue_inode_info *ei; + + ei = kmem_cache_alloc(mqueue_inode_cachep, SLAB_KERNEL); + if (!ei) + return NULL; + return &ei->vfs_inode; +} + +static void mqueue_destroy_inode(struct inode *inode) +{ + kmem_cache_free(mqueue_inode_cachep, MQUEUE_I(inode)); +} + +static void mqueue_delete_inode(struct inode *inode) +{ + struct mqueue_inode_info *info; + int i; + + if (S_ISDIR(inode->i_mode)) { + clear_inode(inode); + return; + } + info = MQUEUE_I(inode); + spin_lock(&info->lock); + for (i = 0; i < info->attr.mq_curmsgs; i++) + free_msg(info->messages[i]); + kfree(info->messages); + spin_unlock(&info->lock); + + clear_inode(inode); + + spin_lock(&mq_lock); + queues_count--; + spin_unlock(&mq_lock); +} + +static int mqueue_create(struct inode *dir, struct dentry *dentry, + int mode, struct nameidata *nd) +{ + struct inode *inode; + int error; + + spin_lock(&mq_lock); + if (queues_count >= queues_max && !capable(CAP_SYS_RESOURCE)) { + error = -ENOSPC; + goto out_lock; + } + queues_count++; + spin_unlock(&mq_lock); + + inode = mqueue_get_inode(dir->i_sb, mode); + if (!inode) { + error = -ENOMEM; + spin_lock(&mq_lock); + queues_count--; + goto out_lock; + } + + dir->i_size += DIRENT_SIZE; + dir->i_ctime = dir->i_mtime = dir->i_atime = CURRENT_TIME; + + d_instantiate(dentry, inode); + dget(dentry); + return 0; +out_lock: + spin_unlock(&mq_lock); + return error; +} + +static int mqueue_unlink(struct inode *dir, struct dentry *dentry) +{ + struct inode *inode = dentry->d_inode; + + dir->i_ctime = dir->i_mtime = dir->i_atime = CURRENT_TIME; + dir->i_size -= DIRENT_SIZE; + inode->i_nlink--; + dput(dentry); + return 0; +} + +/* +* This is routine for system read from queue file. +* To avoid mess with doing here some sort of mq_receive we allow +* to read only queue size & notification info (the only values +* that are interesting from user point of view and aren't accessible +* through std routines) +*/ +static ssize_t mqueue_read_file(struct file *filp, char __user *u_data, + size_t count, loff_t * off) +{ + struct mqueue_inode_info *info = MQUEUE_I(filp->f_dentry->d_inode); + char buffer[FILENT_SIZE]; + size_t slen; + loff_t o; + + if (!count) + return 0; + + spin_lock(&info->lock); + snprintf(buffer, sizeof(buffer), + "QSIZE:%-10lu NOTIFY:%-5d SIGNO:%-5d NOTIFY_PID:%-6d\n", + info->qsize, + info->notify_owner ? info->notify.sigev_notify : SIGEV_NONE, + (info->notify_owner && info->notify.sigev_notify == SIGEV_SIGNAL ) ? + info->notify.sigev_signo : 0, + info->notify_owner); + spin_unlock(&info->lock); + buffer[sizeof(buffer)-1] = '\0'; + slen = strlen(buffer)+1; + + o = *off; + if (o > slen) + return 0; + + if (o + count > slen) + count = slen - o; + + if (copy_to_user(u_data, buffer + o, count)) + return -EFAULT; + + *off = o + count; + filp->f_dentry->d_inode->i_atime = filp->f_dentry->d_inode->i_ctime = CURRENT_TIME; + return count; +} + +static int mqueue_flush_file(struct file *filp) +{ + struct mqueue_inode_info *info = MQUEUE_I(filp->f_dentry->d_inode); + + spin_lock(&info->lock); + if (current->tgid == info->notify_owner) + remove_notification(info); + + spin_unlock(&info->lock); + return 0; +} + +static unsigned int mqueue_poll_file(struct file *filp, struct poll_table_struct *poll_tab) +{ + struct mqueue_inode_info *info = MQUEUE_I(filp->f_dentry->d_inode); + int retval = 0; + + poll_wait(filp, &info->wait_q, poll_tab); + + spin_lock(&info->lock); + if (info->attr.mq_curmsgs) + retval = POLLIN | POLLRDNORM; + + if (info->attr.mq_curmsgs < info->attr.mq_maxmsg) + retval |= POLLOUT | POLLWRNORM; + spin_unlock(&info->lock); + + return retval; +} + +/* Adds current to info->e_wait_q[sr] before element with smaller prio */ +static void wq_add(struct mqueue_inode_info *info, int sr, + struct ext_wait_queue *ewp) +{ + struct ext_wait_queue *walk; + + ewp->task = current; + + list_for_each_entry(walk, &info->e_wait_q[sr].list, list) { + if (walk->task->static_prio <= current->static_prio) { + list_add_tail(&ewp->list, &walk->list); + return; + } + } + list_add_tail(&ewp->list, &info->e_wait_q[sr].list); +} + +/* + * Puts current task to sleep. Caller must hold queue lock. After return + * lock isn't held. + * sr: SEND or RECV + */ +static int wq_sleep(struct mqueue_inode_info *info, int sr, + long timeout, struct ext_wait_queue *ewp) +{ + int retval; + signed long time; + + wq_add(info, sr, ewp); + + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + + spin_unlock(&info->lock); + time = schedule_timeout(timeout); + + while (ewp->state == STATE_PENDING) + cpu_relax(); + + if (ewp->state == STATE_READY) { + retval = 0; + goto out; + } + spin_lock(&info->lock); + if (ewp->state == STATE_READY) { + retval = 0; + goto out_unlock; + } + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + if (time == 0) { + retval = -ETIMEDOUT; + break; + } + } + list_del(&ewp->list); +out_unlock: + spin_unlock(&info->lock); +out: + return retval; +} + +/* + * Returns waiting task that should be serviced first or NULL if none exists + */ +static struct ext_wait_queue *wq_get_first_waiter( + struct mqueue_inode_info *info, int sr) +{ + struct list_head *ptr; + + ptr = info->e_wait_q[sr].list.prev; + if (ptr == &info->e_wait_q[sr].list) + return NULL; + return list_entry(ptr, struct ext_wait_queue, list); +} + +/* Auxiliary functions to manipulate messages' list */ +static void msg_insert(struct msg_msg *ptr, struct mqueue_inode_info *info) +{ + int k; + + k = info->attr.mq_curmsgs - 1; + while (k >= 0 && info->messages[k]->m_type >= ptr->m_type) { + info->messages[k + 1] = info->messages[k]; + k--; + } + info->attr.mq_curmsgs++; + info->qsize += ptr->m_ts; + info->messages[k + 1] = ptr; +} + +static inline struct msg_msg *msg_get(struct mqueue_inode_info *info) +{ + info->qsize -= info->messages[--info->attr.mq_curmsgs]->m_ts; + return info->messages[info->attr.mq_curmsgs]; +} + +/* + * The next function is only to split too long sys_mq_timedsend + */ +static void __do_notify(struct mqueue_inode_info *info) +{ + /* notification + * invoked when there is registered process and there isn't process + * waiting synchronously for message AND state of queue changed from + * empty to not empty. Here we are sure that no one is waiting + * synchronously. */ + if (info->notify_owner && info->attr.mq_curmsgs == 1) { + /* sends signal */ + if (info->notify.sigev_notify == SIGEV_SIGNAL) { + struct siginfo sig_i; + + sig_i.si_signo = info->notify.sigev_signo; + sig_i.si_errno = 0; + sig_i.si_code = SI_MESGQ; + sig_i.si_value = info->notify.sigev_value; + sig_i.si_pid = current->tgid; + sig_i.si_uid = current->uid; + + kill_proc_info(info->notify.sigev_signo, + &sig_i, info->notify_owner); + } else if (info->notify.sigev_notify == SIGEV_THREAD) { + info->notify_filp->private_data = (void*)NP_WOKENUP; + } + /* after notification unregisters process */ + info->notify_owner = 0; + } + wake_up(&info->wait_q); +} + +static long prepare_timeout(const struct timespec __user *u_arg) +{ + struct timespec ts, nowts; + long timeout; + + if (u_arg) { + if (unlikely(copy_from_user(&ts, u_arg, + sizeof(struct timespec)))) + return -EFAULT; + + if (unlikely(ts.tv_nsec < 0 || ts.tv_sec < 0 + || ts.tv_nsec >= NSEC_PER_SEC)) + return -EINVAL; + nowts = CURRENT_TIME; + /* first subtract as jiffies can't be too big */ + ts.tv_sec -= nowts.tv_sec; + if (ts.tv_nsec < nowts.tv_nsec) { + ts.tv_nsec += NSEC_PER_SEC; + ts.tv_sec--; + } + ts.tv_nsec -= nowts.tv_nsec; + if (ts.tv_sec < 0) + return 0; + + timeout = timespec_to_jiffies(&ts) + 1; + } else + return MAX_SCHEDULE_TIMEOUT; + + return timeout; +} + +/* + * File descriptor based notification, intended to be used to implement + * SIGEV_THREAD: + * SIGEV_THREAD means that a notification function should be called in the + * context of a new thread. The kernel can't do that. Therefore mq_notify + * calls with SIGEV_THREAD return a new file descriptor. A user space helper + * must create a new thread and then read from the given file descriptor. + * The read always returns one byte. If it's NOTIFY_WOKENUP, then it must + * call the notification function. If it's NOTIFY_REMOVED, then the + * notification was removed. The file descriptor supports poll, thus one + * supervisor thread can manage multiple message queue notifications. + * + * The implementation must support multiple outstanding notifications: + * It's possible that a new notification is added and signaled before user + * space calls mqueue_notify_read for the previous notification. + * Therefore the notification state is stored in the private_data field of + * the file descriptor. + */ +static unsigned int mqueue_notify_poll(struct file *filp, + struct poll_table_struct *poll_tab) +{ + struct mqueue_inode_info *info = MQUEUE_I(filp->f_dentry->d_inode); + int retval; + + poll_wait(filp, &info->wait_q, poll_tab); + + if (filp->private_data == NP_NONE) + retval = 0; + else + retval = POLLIN | POLLRDNORM; + return retval; +} + +static ssize_t mqueue_notify_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct mqueue_inode_info *info = MQUEUE_I(filp->f_dentry->d_inode); + char result; + + if (!count) + return 0; + if (*ppos != 0) + return 0; + spin_lock(&info->lock); + while (filp->private_data == NP_NONE) { + DEFINE_WAIT(wait); + if (filp->f_flags & O_NONBLOCK) { + spin_unlock(&info->lock); + return -EAGAIN; + } + prepare_to_wait(&info->wait_q, &wait, TASK_INTERRUPTIBLE); + spin_unlock(&info->lock); + schedule(); + finish_wait(&info->wait_q, &wait); + spin_lock(&info->lock); + } + spin_unlock(&info->lock); + result = (char)(unsigned long)filp->private_data; + if (put_user(result, buf)) + return -EFAULT; + *ppos = 1; + return 1; +} + +static int mqueue_notify_release(struct inode *inode, struct file *filp) +{ + struct mqueue_inode_info *info = MQUEUE_I(filp->f_dentry->d_inode); + + spin_lock(&info->lock); + if (info->notify_owner && info->notify_filp == filp) + info->notify_owner = 0; + filp->private_data = NP_REMOVED; + spin_unlock(&info->lock); + + return 0; +} + +static void remove_notification(struct mqueue_inode_info *info) +{ + if (info->notify.sigev_notify == SIGEV_THREAD) { + info->notify_filp->private_data = NP_REMOVED; + wake_up(&info->wait_q); + } + info->notify_owner = 0; +} + +/* + * Invoked when creating a new queue via sys_mq_open + */ +static struct file *do_create(struct dentry *dir, struct dentry *dentry, + int oflag, mode_t mode, struct mq_attr __user *u_attr) +{ + struct file *filp; + struct inode *inode; + struct mqueue_inode_info *info; + struct msg_msg **msgs = NULL; + struct mq_attr attr; + int ret; + + if (u_attr != NULL) { + if (copy_from_user(&attr, u_attr, sizeof(attr))) + return ERR_PTR(-EFAULT); + + if (attr.mq_maxmsg <= 0 || attr.mq_msgsize <= 0) + return ERR_PTR(-EINVAL); + if (capable(CAP_SYS_RESOURCE)) { + if (attr.mq_maxmsg > HARD_MSGMAX) + return ERR_PTR(-EINVAL); + } else { + if (attr.mq_maxmsg > msg_max || + attr.mq_msgsize > msgsize_max) + return ERR_PTR(-EINVAL); + } + msgs = kmalloc(attr.mq_maxmsg * sizeof(*msgs), GFP_KERNEL); + if (!msgs) + return ERR_PTR(-ENOMEM); + } else { + msgs = NULL; + } + + ret = vfs_create(dir->d_inode, dentry, mode, NULL); + if (ret) { + kfree(msgs); + return ERR_PTR(ret); + } + + inode = dentry->d_inode; + info = MQUEUE_I(inode); + + if (msgs) { + info->attr.mq_maxmsg = attr.mq_maxmsg; + info->attr.mq_msgsize = attr.mq_msgsize; + kfree(info->messages); + info->messages = msgs; + } + + filp = dentry_open(dentry, mqueue_mnt, oflag); + if (!IS_ERR(filp)) + dget(dentry); + + return filp; +} + +/* Opens existing queue */ +static struct file *do_open(struct dentry *dentry, int oflag) +{ +static int oflag2acc[O_ACCMODE] = { MAY_READ, MAY_WRITE, + MAY_READ | MAY_WRITE }; + struct file *filp; + + if ((oflag & O_ACCMODE) == (O_RDWR | O_WRONLY)) + return ERR_PTR(-EINVAL); + + if (permission(dentry->d_inode, oflag2acc[oflag & O_ACCMODE], NULL)) + return ERR_PTR(-EACCES); + + filp = dentry_open(dentry, mqueue_mnt, oflag); + + if (!IS_ERR(filp)) + dget(dentry); + + return filp; +} + +asmlinkage long sys_mq_open(const char __user *u_name, int oflag, mode_t mode, + struct mq_attr __user *u_attr) +{ + struct dentry *dentry; + struct file *filp; + char *name; + int fd, error; + + if (IS_ERR(name = getname(u_name))) + return PTR_ERR(name); + + fd = get_unused_fd(); + if (fd < 0) + goto out_putname; + + down(&mqueue_mnt->mnt_root->d_inode->i_sem); + dentry = lookup_one_len(name, mqueue_mnt->mnt_root, strlen(name)); + if (IS_ERR(dentry)) { + error = PTR_ERR(dentry); + goto out_err; + } + mntget(mqueue_mnt); + + if (oflag & O_CREAT) { + if (dentry->d_inode) { /* entry already exists */ + filp = (oflag & O_EXCL) ? ERR_PTR(-EEXIST) : + do_open(dentry, oflag); + } else { + filp = do_create(mqueue_mnt->mnt_root, dentry, + oflag, mode, u_attr); + } + } else + filp = (dentry->d_inode) ? do_open(dentry, oflag) : + ERR_PTR(-ENOENT); + + dput(dentry); + + if (IS_ERR(filp)) { + error = PTR_ERR(filp); + goto out_putfd; + } + + fd_install(fd, filp); + goto out_upsem; + +out_putfd: + mntput(mqueue_mnt); + put_unused_fd(fd); +out_err: + fd = error; +out_upsem: + up(&mqueue_mnt->mnt_root->d_inode->i_sem); +out_putname: + putname(name); + return fd; +} + +asmlinkage long sys_mq_unlink(const char __user *u_name) +{ + int err; + char *name; + struct dentry *dentry; + struct inode *inode = NULL; + + name = getname(u_name); + if (IS_ERR(name)) + return PTR_ERR(name); + + down(&mqueue_mnt->mnt_root->d_inode->i_sem); + dentry = lookup_one_len(name, mqueue_mnt->mnt_root, strlen(name)); + if (IS_ERR(dentry)) { + err = PTR_ERR(dentry); + goto out_unlock; + } + + if (!dentry->d_inode) { + err = -ENOENT; + goto out_err; + } + + if (permission(dentry->d_inode, MAY_WRITE, NULL)) { + err = -EACCES; + goto out_err; + } + inode = dentry->d_inode; + if (inode) + atomic_inc(&inode->i_count); + + err = vfs_unlink(dentry->d_parent->d_inode, dentry); +out_err: + dput(dentry); + +out_unlock: + up(&mqueue_mnt->mnt_root->d_inode->i_sem); + putname(name); + if (inode) + iput(inode); + + return err; +} + +/* Pipelined send and receive functions. + * + * If a receiver finds no waiting message, then it registers itself in the + * list of waiting receivers. A sender checks that list before adding the new + * message into the message array. If there is a waiting receiver, then it + * bypasses the message array and directly hands the message over to the + * receiver. + * The receiver accepts the message and returns without grabbing the queue + * spinlock. Therefore an intermediate STATE_PENDING state and memory barriers + * are necessary. The same algorithm is used for sysv semaphores, see + * ipc/sem.c fore more details. + * + * The same algorithm is used for senders. + */ + +/* pipelined_send() - send a message directly to the task waiting in + * sys_mq_timedreceive() (without inserting message into a queue). */ +static inline void pipelined_send(struct mqueue_inode_info *info, + struct msg_msg *message, + struct ext_wait_queue *receiver) +{ + receiver->msg = message; + list_del(&receiver->list); + receiver->state = STATE_PENDING; + wake_up_process(receiver->task); + wmb(); + receiver->state = STATE_READY; +} + +/* pipelined_receive() - if there is task waiting in sys_mq_timedsend() + * gets its message and put to the queue (we have one free place for sure). */ +static inline void pipelined_receive(struct mqueue_inode_info *info) +{ + struct ext_wait_queue *sender = wq_get_first_waiter(info, SEND); + + if (!sender) { + /* for poll */ + wake_up_interruptible(&info->wait_q); + return; + } + msg_insert(sender->msg, info); + list_del(&sender->list); + sender->state = STATE_PENDING; + wake_up_process(sender->task); + wmb(); + sender->state = STATE_READY; +} + +asmlinkage long sys_mq_timedsend(mqd_t mqdes, const char __user *u_msg_ptr, + size_t msg_len, unsigned int msg_prio, + const struct timespec __user *u_abs_timeout) +{ + struct file *filp; + struct inode *inode; + struct ext_wait_queue wait; + struct ext_wait_queue *receiver; + struct msg_msg *msg_ptr; + struct mqueue_inode_info *info; + long timeout; + int ret; + + if (unlikely(msg_prio >= (unsigned long) MQ_PRIO_MAX)) + return -EINVAL; + + if (unlikely((timeout = prepare_timeout(u_abs_timeout)) < 0)) + return timeout; + + ret = -EBADF; + filp = fget(mqdes); + if (unlikely(!filp)) + goto out; + + inode = filp->f_dentry->d_inode; + if (unlikely(filp->f_op != &mqueue_file_operations)) + goto out_fput; + info = MQUEUE_I(inode); + + if (unlikely(!(filp->f_mode & FMODE_WRITE))) + goto out_fput; + + if (unlikely(msg_len > info->attr.mq_msgsize)) { + ret = -EMSGSIZE; + goto out_fput; + } + + /* First try to allocate memory, before doing anything with + * existing queues. */ + msg_ptr = load_msg((void *)u_msg_ptr, msg_len); + if (unlikely(IS_ERR(msg_ptr))) { + ret = PTR_ERR(msg_ptr); + goto out_fput; + } + msg_ptr->m_ts = msg_len; + msg_ptr->m_type = msg_prio; + + spin_lock(&info->lock); + + if (info->attr.mq_curmsgs == info->attr.mq_maxmsg) { + if (filp->f_flags & O_NONBLOCK) { + spin_unlock(&info->lock); + ret = -EAGAIN; + } else { + wait.task = current; + wait.msg = (void *) msg_ptr; + wait.state = STATE_NONE; + ret = wq_sleep(info, SEND, timeout, &wait); + if (ret < 0) + free_msg(msg_ptr); + } + } else { + receiver = wq_get_first_waiter(info, RECV); + if (receiver) { + pipelined_send(info, msg_ptr, receiver); + } else { + /* adds message to the queue */ + msg_insert(msg_ptr, info); + __do_notify(info); + } + inode->i_atime = inode->i_mtime = inode->i_ctime = + CURRENT_TIME; + spin_unlock(&info->lock); + ret = 0; + } +out_fput: + fput(filp); +out: + return ret; +} + +asmlinkage ssize_t sys_mq_timedreceive(mqd_t mqdes, char __user *u_msg_ptr, + size_t msg_len, unsigned int __user *u_msg_prio, + const struct timespec __user *u_abs_timeout) +{ + long timeout; + ssize_t ret; + struct msg_msg *msg_ptr; + struct file *filp; + struct inode *inode; + struct mqueue_inode_info *info; + struct ext_wait_queue wait; + + + if (unlikely((timeout = prepare_timeout(u_abs_timeout)) < 0)) + return timeout; + + ret = -EBADF; + filp = fget(mqdes); + if (unlikely(!filp)) + goto out; + + inode = filp->f_dentry->d_inode; + if (unlikely(filp->f_op != &mqueue_file_operations)) + goto out_fput; + info = MQUEUE_I(inode); + + if (unlikely(!(filp->f_mode & FMODE_READ))) + goto out_fput; + + /* checks if buffer is big enough */ + if (unlikely(msg_len < info->attr.mq_msgsize)) { + ret = -EMSGSIZE; + goto out_fput; + } + + spin_lock(&info->lock); + if (info->attr.mq_curmsgs == 0) { + if (filp->f_flags & O_NONBLOCK) { + spin_unlock(&info->lock); + ret = -EAGAIN; + msg_ptr = NULL; + } else { + wait.task = current; + wait.state = STATE_NONE; + ret = wq_sleep(info, RECV, timeout, &wait); + msg_ptr = wait.msg; + } + } else { + msg_ptr = msg_get(info); + + inode->i_atime = inode->i_mtime = inode->i_ctime = + CURRENT_TIME; + + /* There is now free space in queue. */ + pipelined_receive(info); + spin_unlock(&info->lock); + ret = 0; + } + if (ret == 0) { + ret = msg_ptr->m_ts; + + if ((u_msg_prio && put_user(msg_ptr->m_type, u_msg_prio)) || + store_msg(u_msg_ptr, msg_ptr, msg_ptr->m_ts)) { + ret = -EFAULT; + } + free_msg(msg_ptr); + } +out_fput: + fput(filp); +out: + return ret; +} + +/* + * Notes: the case when user wants us to deregister (with NULL as pointer + * or SIGEV_NONE) and he isn't currently owner of notification will be + * silently discarded. It isn't explicitly defined in the POSIX. + */ +asmlinkage long sys_mq_notify(mqd_t mqdes, + const struct sigevent __user *u_notification) +{ + int ret, fd; + struct file *filp, *nfilp; + struct inode *inode; + struct sigevent notification; + struct mqueue_inode_info *info; + + if (u_notification == NULL) { + notification.sigev_notify = SIGEV_NONE; + } else { + if (copy_from_user(¬ification, u_notification, + sizeof(struct sigevent))) + return -EFAULT; + + if (unlikely(notification.sigev_notify != SIGEV_NONE && + notification.sigev_notify != SIGEV_SIGNAL && + notification.sigev_notify != SIGEV_THREAD)) + return -EINVAL; + if (notification.sigev_notify == SIGEV_SIGNAL && + (notification.sigev_signo < 0 || + notification.sigev_signo > _NSIG)) { + return -EINVAL; + } + } + + ret = -EBADF; + filp = fget(mqdes); + if (!filp) + goto out; + + inode = filp->f_dentry->d_inode; + if (unlikely(filp->f_op != &mqueue_file_operations)) + goto out_fput; + info = MQUEUE_I(inode); + + ret = 0; + if (notification.sigev_notify == SIGEV_THREAD) { + ret = get_unused_fd(); + if (ret < 0) + goto out_fput; + fd = ret; + nfilp = get_empty_filp(); + if (!nfilp) { + ret = -ENFILE; + goto out_dropfd; + } + nfilp->private_data = NP_NONE; + nfilp->f_op = &mqueue_notify_fops; + nfilp->f_vfsmnt = mntget(mqueue_mnt); + nfilp->f_dentry = dget(filp->f_dentry); + nfilp->f_mapping = filp->f_dentry->d_inode->i_mapping; + nfilp->f_flags = O_RDONLY; + nfilp->f_mode = FMODE_READ; + } else { + nfilp = NULL; + fd = -1; + } + + spin_lock(&info->lock); + + if (notification.sigev_notify == SIGEV_NONE) { + if (info->notify_owner == current->tgid) { + remove_notification(info); + inode->i_atime = inode->i_ctime = CURRENT_TIME; + } + } else if (info->notify_owner) { + ret = -EBUSY; + } else if (notification.sigev_notify == SIGEV_THREAD) { + info->notify_filp = nfilp; + fd_install(fd, nfilp); + ret = fd; + fd = -1; + nfilp = NULL; + info->notify.sigev_notify = SIGEV_THREAD; + info->notify_owner = current->tgid; + inode->i_atime = inode->i_ctime = CURRENT_TIME; + } else { + info->notify.sigev_signo = notification.sigev_signo; + info->notify.sigev_value = notification.sigev_value; + info->notify.sigev_notify = SIGEV_SIGNAL; + info->notify_owner = current->tgid; + inode->i_atime = inode->i_ctime = CURRENT_TIME; + } + spin_unlock(&info->lock); +out_dropfd: + if (fd != -1) + put_unused_fd(fd); +out_fput: + fput(filp); +out: + return ret; +} + +asmlinkage long sys_mq_getsetattr(mqd_t mqdes, + const struct mq_attr __user *u_mqstat, + struct mq_attr __user *u_omqstat) +{ + int ret; + struct mq_attr mqstat, omqstat; + struct file *filp; + struct inode *inode; + struct mqueue_inode_info *info; + + if (u_mqstat != NULL) { + if (copy_from_user(&mqstat, u_mqstat, sizeof(struct mq_attr))) + return -EFAULT; + if (mqstat.mq_flags & (~O_NONBLOCK)) + return -EINVAL; + } + + ret = -EBADF; + filp = fget(mqdes); + if (!filp) + goto out; + + inode = filp->f_dentry->d_inode; + if (unlikely(filp->f_op != &mqueue_file_operations)) + goto out_fput; + info = MQUEUE_I(inode); + + spin_lock(&info->lock); + + omqstat = info->attr; + omqstat.mq_flags = filp->f_flags & O_NONBLOCK; + if (u_mqstat) { + if (mqstat.mq_flags & O_NONBLOCK) + filp->f_flags |= O_NONBLOCK; + else + filp->f_flags &= ~O_NONBLOCK; + + inode->i_atime = inode->i_ctime = CURRENT_TIME; + } + + spin_unlock(&info->lock); + + ret = 0; + if (u_omqstat != NULL && copy_to_user(u_omqstat, &omqstat, + sizeof(struct mq_attr))) + ret = -EFAULT; + +out_fput: + fput(filp); +out: + return ret; +} + +static struct inode_operations mqueue_dir_inode_operations = { + .lookup = simple_lookup, + .create = mqueue_create, + .unlink = mqueue_unlink, +}; + +static struct file_operations mqueue_file_operations = { + .flush = mqueue_flush_file, + .poll = mqueue_poll_file, + .read = mqueue_read_file, +}; + +static struct file_operations mqueue_notify_fops = { + .poll = mqueue_notify_poll, + .read = mqueue_notify_read, + .release = mqueue_notify_release, +}; + + +static struct super_operations mqueue_super_ops = { + .alloc_inode = mqueue_alloc_inode, + .destroy_inode = mqueue_destroy_inode, + .statfs = simple_statfs, + .delete_inode = mqueue_delete_inode, + .drop_inode = generic_delete_inode, +}; + +static struct file_system_type mqueue_fs_type = { + .name = "mqueue", + .get_sb = mqueue_get_sb, + .kill_sb = kill_litter_super, +}; + +static int msg_max_limit_min = DFLT_MSGMAX; +static int msg_max_limit_max = HARD_MSGMAX; + +static int msg_maxsize_limit_min = DFLT_MSGSIZEMAX; +static int msg_maxsize_limit_max = INT_MAX; + +static ctl_table mq_sysctls[] = { + { + .ctl_name = CTL_QUEUESMAX, + .procname = "queues_max", + .data = &queues_max, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, + { + .ctl_name = CTL_MSGMAX, + .procname = "msg_max", + .data = &msg_max, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec_minmax, + .extra1 = &msg_max_limit_min, + .extra2 = &msg_max_limit_max, + }, + { + .ctl_name = CTL_MSGSIZEMAX, + .procname = "msgsize_max", + .data = &msgsize_max, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec_minmax, + .extra1 = &msg_maxsize_limit_min, + .extra2 = &msg_maxsize_limit_max, + }, + { .ctl_name = 0 } +}; + +static ctl_table mq_sysctl_dir[] = { + { + .ctl_name = FS_MQUEUE, + .procname = "mqueue", + .mode = 0555, + .child = mq_sysctls, + }, + { .ctl_name = 0 } +}; + +static ctl_table mq_sysctl_root[] = { + { + .ctl_name = CTL_FS, + .procname = "fs", + .mode = 0555, + .child = mq_sysctl_dir, + }, + { .ctl_name = 0 } +}; + +static int __init init_mqueue_fs(void) +{ + int error; + + mqueue_inode_cachep = kmem_cache_create("mqueue_inode_cache", + sizeof(struct mqueue_inode_info), 0, + SLAB_HWCACHE_ALIGN, init_once, NULL); + if (mqueue_inode_cachep == NULL) + return -ENOMEM; + + mq_sysctl_table = register_sysctl_table(mq_sysctl_root, 0); + if (!mq_sysctl_table) { + error = -ENOMEM; + goto out_cache; + } + + error = register_filesystem(&mqueue_fs_type); + if (error) + goto out_sysctl; + + if (IS_ERR(mqueue_mnt = kern_mount(&mqueue_fs_type))) { + error = PTR_ERR(mqueue_mnt); + goto out_filesystem; + } + + /* internal initialization - not common for vfs */ + queues_count = 0; + spin_lock_init(&mq_lock); + + return 0; + +out_filesystem: + unregister_filesystem(&mqueue_fs_type); +out_sysctl: + unregister_sysctl_table(mq_sysctl_table); +out_cache: + if (kmem_cache_destroy(mqueue_inode_cachep)) { + printk(KERN_INFO + "mqueue_inode_cache: not all structures were freed\n"); + } + return error; +} + +__initcall(init_mqueue_fs); --- linux-2.6.4-rc2/ipc/msg.c 2004-02-03 20:42:39.000000000 -0800 +++ 25/ipc/msg.c 2004-03-07 20:47:48.000000000 -0800 @@ -51,11 +51,6 @@ struct msg_sender { struct task_struct* tsk; }; -struct msg_msgseg { - struct msg_msgseg* next; - /* the next part of the message follows immediately */ -}; - #define SEARCH_ANY 1 #define SEARCH_EQUAL 2 #define SEARCH_NOTEQUAL 3 @@ -129,106 +124,6 @@ static int newque (key_t key, int msgflg return msg_buildid(id,msq->q_perm.seq); } -static void free_msg(struct msg_msg* msg) -{ - struct msg_msgseg* seg; - - security_msg_msg_free(msg); - - seg = msg->next; - kfree(msg); - while(seg != NULL) { - struct msg_msgseg* tmp = seg->next; - kfree(seg); - seg = tmp; - } -} - -static struct msg_msg* load_msg(void* src, int len) -{ - struct msg_msg* msg; - struct msg_msgseg** pseg; - int err; - int alen; - - alen = len; - if(alen > DATALEN_MSG) - alen = DATALEN_MSG; - - msg = (struct msg_msg *) kmalloc (sizeof(*msg) + alen, GFP_KERNEL); - if(msg==NULL) - return ERR_PTR(-ENOMEM); - - msg->next = NULL; - msg->security = NULL; - - if (copy_from_user(msg+1, src, alen)) { - err = -EFAULT; - goto out_err; - } - - len -= alen; - src = ((char*)src)+alen; - pseg = &msg->next; - while(len > 0) { - struct msg_msgseg* seg; - alen = len; - if(alen > DATALEN_SEG) - alen = DATALEN_SEG; - seg = (struct msg_msgseg *) kmalloc (sizeof(*seg) + alen, GFP_KERNEL); - if(seg==NULL) { - err=-ENOMEM; - goto out_err; - } - *pseg = seg; - seg->next = NULL; - if(copy_from_user (seg+1, src, alen)) { - err = -EFAULT; - goto out_err; - } - pseg = &seg->next; - len -= alen; - src = ((char*)src)+alen; - } - - err = security_msg_msg_alloc(msg); - if (err) - goto out_err; - - return msg; - -out_err: - free_msg(msg); - return ERR_PTR(err); -} - -static int store_msg(void* dest, struct msg_msg* msg, int len) -{ - int alen; - struct msg_msgseg *seg; - - alen = len; - if(alen > DATALEN_MSG) - alen = DATALEN_MSG; - if(copy_to_user (dest, msg+1, alen)) - return -1; - - len -= alen; - dest = ((char*)dest)+alen; - seg = msg->next; - while(len > 0) { - alen = len; - if(alen > DATALEN_SEG) - alen = DATALEN_SEG; - if(copy_to_user (dest, seg+1, alen)) - return -1; - len -= alen; - dest = ((char*)dest)+alen; - seg=seg->next; - } - return 0; -} - static inline void ss_add(struct msg_queue* msq, struct msg_sender* mss) { mss->tsk=current; --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/ipc/msgutil.c 2004-03-07 20:47:48.000000000 -0800 @@ -0,0 +1,127 @@ +/* + * linux/ipc/util.c + * Copyright (C) 1999, 2004 Manfred Spraul + * + * This file is released under GNU General Public Licence version 2 or + * (at your option) any later version. + * + * See the file COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include "util.h" + +struct msg_msgseg { + struct msg_msgseg* next; + /* the next part of the message follows immediately */ +}; + +#define DATALEN_MSG (PAGE_SIZE-sizeof(struct msg_msg)) +#define DATALEN_SEG (PAGE_SIZE-sizeof(struct msg_msgseg)) + +struct msg_msg *load_msg(void __user *src, int len) +{ + struct msg_msg *msg; + struct msg_msgseg **pseg; + int err; + int alen; + + alen = len; + if (alen > DATALEN_MSG) + alen = DATALEN_MSG; + + msg = (struct msg_msg *)kmalloc(sizeof(*msg) + alen, GFP_KERNEL); + if (msg == NULL) + return ERR_PTR(-ENOMEM); + + msg->next = NULL; + msg->security = NULL; + + if (copy_from_user(msg + 1, src, alen)) { + err = -EFAULT; + goto out_err; + } + + len -= alen; + src = ((char *)src) + alen; + pseg = &msg->next; + while (len > 0) { + struct msg_msgseg *seg; + alen = len; + if (alen > DATALEN_SEG) + alen = DATALEN_SEG; + seg = (struct msg_msgseg *)kmalloc(sizeof(*seg) + alen, + GFP_KERNEL); + if (seg == NULL) { + err = -ENOMEM; + goto out_err; + } + *pseg = seg; + seg->next = NULL; + if (copy_from_user(seg + 1, src, alen)) { + err = -EFAULT; + goto out_err; + } + pseg = &seg->next; + len -= alen; + src = ((char *)src) + alen; + } + + err = security_msg_msg_alloc(msg); + if (err) + goto out_err; + + return msg; + +out_err: + free_msg(msg); + return ERR_PTR(err); +} + +int store_msg(void __user *dest, struct msg_msg *msg, int len) +{ + int alen; + struct msg_msgseg *seg; + + alen = len; + if (alen > DATALEN_MSG) + alen = DATALEN_MSG; + if (copy_to_user(dest, msg + 1, alen)) + return -1; + + len -= alen; + dest = ((char *)dest) + alen; + seg = msg->next; + while (len > 0) { + alen = len; + if (alen > DATALEN_SEG) + alen = DATALEN_SEG; + if (copy_to_user(dest, seg + 1, alen)) + return -1; + len -= alen; + dest = ((char *)dest) + alen; + seg = seg->next; + } + return 0; +} + +void free_msg(struct msg_msg *msg) +{ + struct msg_msgseg *seg; + + security_msg_msg_free(msg); + + seg = msg->next; + kfree(msg); + while (seg != NULL) { + struct msg_msgseg *tmp = seg->next; + kfree(seg); + seg = tmp; + } +} --- linux-2.6.4-rc2/ipc/sem.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/ipc/sem.c 2004-03-07 20:48:07.000000000 -0800 @@ -993,7 +993,6 @@ static struct sem_undo *find_undo(int se } error = sem_revalidate(semid, sma, nsems, 0); if (error) { - sem_unlock(sma); unlock_semundo(); kfree(new); un = ERR_PTR(error); --- linux-2.6.4-rc2/ipc/util.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/ipc/util.c 2004-03-07 20:47:48.000000000 -0800 @@ -25,8 +25,6 @@ #include #include -#if defined(CONFIG_SYSVIPC) - #include "util.h" /** @@ -531,20 +529,3 @@ int ipc_parse_version (int *cmd) } #endif /* __ia64__ */ - -#else -/* - * Dummy functions when SYSV IPC isn't configured - */ - -int copy_semundo(unsigned long clone_flags, struct task_struct *tsk) -{ - return 0; -} - -void exit_sem(struct task_struct *tsk) -{ - return; -} - -#endif /* CONFIG_SYSVIPC */ --- linux-2.6.4-rc2/ipc/util.h 2004-02-17 20:48:46.000000000 -0800 +++ 25/ipc/util.h 2004-03-07 20:47:48.000000000 -0800 @@ -4,6 +4,10 @@ * * ipc helper functions (c) 1999 Manfred Spraul */ + +#ifndef _IPC_UTIL_H +#define _IPC_UTIL_H + #define USHRT_MAX 0xffff #define SEQ_MULTIPLIER (IPCMNI) @@ -62,3 +66,9 @@ void ipc64_perm_to_ipc_perm(struct ipc64 #else int ipc_parse_version (int *cmd); #endif + +extern void free_msg(struct msg_msg *msg); +extern struct msg_msg *load_msg(void __user *src, int len); +extern int store_msg(void __user *dest, struct msg_msg *msg, int len); + +#endif --- linux-2.6.4-rc2/kernel/compat.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/kernel/compat.c 2004-03-07 20:46:46.000000000 -0800 @@ -30,7 +30,7 @@ int get_compat_timespec(struct timespec __get_user(ts->tv_nsec, &cts->tv_nsec)) ? -EFAULT : 0; } -int put_compat_timespec(struct timespec *ts, const struct compat_timespec *cts) +int put_compat_timespec(const struct timespec *ts, struct compat_timespec *cts) { return (verify_area(VERIFY_WRITE, cts, sizeof(*cts)) || __put_user(ts->tv_sec, &cts->tv_sec) || --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/kernel/compat_signal.c 2004-03-07 20:47:06.000000000 -0800 @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2003 Carlos O'Donell + * + * 2003-12-20 Carlos O'Donell + * Copied linux/kernel/compat_signal.c (copy_siginfo_to_user) + * and modified to use compat_siginfo_t for thunking down to + * 32-bit userspace from a 64-bit kernel. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include + +#ifndef HAVE_ARCH_COMPAT_COPY_SIGINFO_TO_USER + +int compat_copy_siginfo_to_user(compat_siginfo_t __user *to, siginfo_t *from) +{ + int err; + compat_siginfo_t compat_from; + + if (!access_ok (VERIFY_WRITE, to, sizeof(compat_siginfo_t))) + return -EFAULT; + + /* + * If you change compat_siginfo_t structure *or* siginfo_t, + * please be sure this code is fixed accordingly. + * It should never copy any pad contained in the structure + * to avoid security leaks, but must copy the generic + * 3 ints plus the relevant union member. + */ + + /* Convert structure, don't leak anything in the copy */ + memset(&compat_from,'\0',sizeof(compat_siginfo_t)); + compat_from.si_signo = (compat_int_t)(from->si_signo); + compat_from.si_errno = (compat_int_t)(from->si_errno); + compat_from.si_code = (compat_int_t)(from->si_code); + + if (from->si_code < 0) + return __copy_to_user(to, &compat_from, sizeof(compat_siginfo_t)) + ? -EFAULT : 0; + + err = __put_user(compat_from.si_signo, &to->si_signo); + err |= __put_user(compat_from.si_errno, &to->si_errno); + err |= __put_user(compat_from.si_code, &to->si_code); + + switch (from->si_code & __SI_MASK) { + case __SI_KILL: + compat_from.si_pid = (compat_pid_t)(from->si_pid); + compat_from.si_uid = (__ARCH_SI_COMPAT_UID_T)(from->si_uid); + err |= __put_user(compat_from.si_pid, &to->si_pid); + err |= __put_user(compat_from.si_uid, &to->si_uid); + break; + case __SI_TIMER: + compat_from.si_pid = (compat_timer_t)(from->si_tid); + compat_from.si_overrun = (compat_int_t)(from->si_overrun); + compat_from.si_ptr = (compat_uptr_t)((u64)(from->si_ptr) & 0xffffffffUL); + err |= __put_user(compat_from.si_tid, &to->si_tid); + err |= __put_user(compat_from.si_overrun, &to->si_overrun); + err |= __put_user(compat_from.si_ptr, &to->si_ptr); + break; + case __SI_POLL: + compat_from.si_band = (__ARCH_SI_COMPAT_BAND_T)(from->si_band); + compat_from.si_fd = (compat_int_t)(from->si_fd); + err |= __put_user(compat_from.si_band, &to->si_band); + err |= __put_user(compat_from.si_fd, &to->si_fd); + break; + case __SI_FAULT: + compat_from.si_addr = (compat_uptr_t)((u64)(from->si_addr) & 0xffffffffUL); + err |= __put_user(compat_from.si_addr, &to->si_addr); +#ifdef __ARCH_SI_COMPAT_TRAPNO + compat_from.si_trapno = (compat_int_t)(from->si_addr); + err |= __put_user(compat_from.si_trapno, &to->si_trapno); +#endif + break; + case __SI_CHLD: + compat_from.si_pid = (compat_pid_t)(from->si_pid); + compat_from.si_uid = (__ARCH_SI_COMPAT_UID_T)(from->si_uid); + compat_from.si_status = (compat_int_t)(from->si_status); + compat_from.si_utime = (compat_clock_t)(from->si_utime); + compat_from.si_stime = (compat_clock_t)(from->si_stime); + err |= __put_user(compat_from.si_pid, &to->si_pid); + err |= __put_user(compat_from.si_uid, &to->si_uid); + err |= __put_user(compat_from.si_status, &to->si_status); + err |= __put_user(compat_from.si_utime, &to->si_utime); + err |= __put_user(compat_from.si_stime, &to->si_stime); + break; + case __SI_RT: /* This is not generated by the kernel as of now. */ + compat_from.si_pid = (compat_pid_t)(from->si_pid); + compat_from.si_uid = (__ARCH_SI_COMPAT_UID_T)(from->si_uid); + compat_from.si_int = (compat_int_t)(from->si_int); + compat_from.si_ptr = (compat_uptr_t)((u64)(from->si_ptr) & 0xffffffffUL); + err |= __put_user(compat_from.si_pid, &to->si_pid); + err |= __put_user(compat_from.si_uid, &to->si_uid); + err |= __put_user(compat_from.si_int, &to->si_int); + err |= __put_user(compat_from.si_ptr, &to->si_ptr); + break; + default: /* this is just in case for now ... */ + compat_from.si_pid = (compat_pid_t)(from->si_pid); + compat_from.si_uid = (__ARCH_SI_COMPAT_UID_T)(from->si_uid); + err |= __put_user(compat_from.si_pid, &to->si_pid); + err |= __put_user(compat_from.si_uid, &to->si_uid); + break; + } + return err; +} + +#endif --- linux-2.6.4-rc2/kernel/cpu.c 2004-02-17 20:48:46.000000000 -0800 +++ 25/kernel/cpu.c 2004-03-07 20:47:23.000000000 -0800 @@ -1,14 +1,19 @@ /* CPU control. - * (C) 2001 Rusty Russell + * (C) 2001, 2002, 2003, 2004 Rusty Russell + * * This code is licenced under the GPL. */ #include #include #include -#include #include #include +#include /* for hotplug_path */ #include +#include +#include +#include +#include #include /* This protects CPUs going up and down... */ @@ -19,20 +24,149 @@ static struct notifier_block *cpu_chain; /* Need to know about CPUs going up/down? */ int register_cpu_notifier(struct notifier_block *nb) { - return notifier_chain_register(&cpu_chain, nb); + int ret; + + if ((ret = down_interruptible(&cpucontrol)) != 0) + return ret; + ret = notifier_chain_register(&cpu_chain, nb); + up(&cpucontrol); + return ret; } +EXPORT_SYMBOL(register_cpu_notifier); void unregister_cpu_notifier(struct notifier_block *nb) { - notifier_chain_unregister(&cpu_chain,nb); + down(&cpucontrol); + notifier_chain_unregister(&cpu_chain, nb); + up(&cpucontrol); +} +EXPORT_SYMBOL(unregister_cpu_notifier); + +#ifdef CONFIG_HOTPLUG_CPU +static inline void check_for_tasks(int cpu, struct task_struct *k) +{ + struct task_struct *p; + + write_lock_irq(&tasklist_lock); + for_each_process(p) { + if (task_cpu(p) == cpu && p != k) + printk(KERN_WARNING "Task %s is on cpu %d\n", + p->comm, cpu); + } + write_unlock_irq(&tasklist_lock); +} + +/* Notify userspace when a cpu event occurs, by running '/sbin/hotplug + * cpu' with certain environment variables set. */ +static int cpu_run_sbin_hotplug(unsigned int cpu, const char *action) +{ + char *argv[3], *envp[5], cpu_str[12], action_str[32]; + int i; + + sprintf(cpu_str, "CPU=%d", cpu); + sprintf(action_str, "ACTION=%s", action); + /* FIXME: Add DEVPATH. --RR */ + + i = 0; + argv[i++] = hotplug_path; + argv[i++] = "cpu"; + argv[i] = NULL; + + i = 0; + /* minimal command environment */ + envp [i++] = "HOME=/"; + envp [i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; + envp [i++] = cpu_str; + envp [i++] = action_str; + envp [i] = NULL; + + return call_usermodehelper(argv[0], argv, envp, 0); +} + +/* Take this CPU down. */ +static int take_cpu_down(void *unused) +{ + int err; + + /* Take offline: makes arch_cpu_down somewhat easier. */ + cpu_clear(smp_processor_id(), cpu_online_map); + + /* Ensure this CPU doesn't handle any more interrupts. */ + err = __cpu_disable(); + if (err < 0) + cpu_set(smp_processor_id(), cpu_online_map); + else + /* Everyone else gets kicked off. */ + migrate_all_tasks(); + + return err; +} + +int cpu_down(unsigned int cpu) +{ + int err; + struct task_struct *p; + + if ((err = lock_cpu_hotplug_interruptible()) != 0) + return err; + + if (num_online_cpus() == 1) { + err = -EBUSY; + goto out; + } + + if (!cpu_online(cpu)) { + err = -EINVAL; + goto out; + } + + p = __stop_machine_run(take_cpu_down, NULL, cpu); + if (IS_ERR(p)) { + err = PTR_ERR(p); + goto out; + } + + if (cpu_online(cpu)) + goto out_thread; + + check_for_tasks(cpu, p); + + /* Wait for it to sleep (leaving idle task). */ + while (!idle_cpu(cpu)) + yield(); + + /* This actually kills the CPU. */ + __cpu_die(cpu); + + /* Move it here so it can run. */ + kthread_bind(p, smp_processor_id()); + + /* CPU is completely dead: tell everyone. Too late to complain. */ + if (notifier_call_chain(&cpu_chain, CPU_DEAD, (void *)(long)cpu) + == NOTIFY_BAD) + BUG(); + + cpu_run_sbin_hotplug(cpu, "offline"); + +out_thread: + err = kthread_stop(p); +out: + unlock_cpu_hotplug(); + return err; +} +#else +static inline int cpu_run_sbin_hotplug(unsigned int cpu, const char *action) +{ + return 0; } +#endif /*CONFIG_HOTPLUG_CPU*/ int __devinit cpu_up(unsigned int cpu) { int ret; void *hcpu = (void *)(long)cpu; - if ((ret = down_interruptible(&cpucontrol)) != 0) + if ((ret = lock_cpu_hotplug_interruptible()) != 0) return ret; if (cpu_online(cpu)) { @@ -61,6 +195,6 @@ out_notify: if (ret != 0) notifier_call_chain(&cpu_chain, CPU_UP_CANCELED, hcpu); out: - up(&cpucontrol); + unlock_cpu_hotplug(); return ret; } --- linux-2.6.4-rc2/kernel/exit.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/kernel/exit.c 2004-03-07 20:46:46.000000000 -0800 @@ -386,7 +386,7 @@ static inline void close_files(struct fi } } -void put_files_struct(struct files_struct *files) +void fastcall put_files_struct(struct files_struct *files) { if (atomic_dec_and_test(&files->count)) { close_files(files); @@ -810,7 +810,7 @@ asmlinkage long sys_exit(int error_code) do_exit((error_code&0xff)<<8); } -task_t *next_thread(task_t *p) +task_t fastcall *next_thread(task_t *p) { struct pid_link *link = p->pids + PIDTYPE_TGID; struct list_head *tmp, *head = &link->pidptr->task_list; --- linux-2.6.4-rc2/kernel/fork.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/kernel/fork.c 2004-03-07 20:47:48.000000000 -0800 @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -39,9 +40,6 @@ #include #include -extern int copy_semundo(unsigned long clone_flags, struct task_struct *tsk); -extern void exit_sem(struct task_struct *tsk); - /* The idle threads do not count.. * Protected by write_lock_irq(&tasklist_lock) */ @@ -91,7 +89,7 @@ void __put_task_struct(struct task_struc free_task(tsk); } -void add_wait_queue(wait_queue_head_t *q, wait_queue_t * wait) +void fastcall add_wait_queue(wait_queue_head_t *q, wait_queue_t * wait) { unsigned long flags; @@ -103,7 +101,7 @@ void add_wait_queue(wait_queue_head_t *q EXPORT_SYMBOL(add_wait_queue); -void add_wait_queue_exclusive(wait_queue_head_t *q, wait_queue_t * wait) +void fastcall add_wait_queue_exclusive(wait_queue_head_t *q, wait_queue_t * wait) { unsigned long flags; @@ -115,7 +113,7 @@ void add_wait_queue_exclusive(wait_queue EXPORT_SYMBOL(add_wait_queue_exclusive); -void remove_wait_queue(wait_queue_head_t *q, wait_queue_t * wait) +void fastcall remove_wait_queue(wait_queue_head_t *q, wait_queue_t * wait) { unsigned long flags; @@ -139,7 +137,7 @@ EXPORT_SYMBOL(remove_wait_queue); * stops them from bleeding out - it would still allow subsequent * loads to move into the the critical region). */ -void prepare_to_wait(wait_queue_head_t *q, wait_queue_t *wait, int state) +void fastcall prepare_to_wait(wait_queue_head_t *q, wait_queue_t *wait, int state) { unsigned long flags; @@ -153,7 +151,7 @@ void prepare_to_wait(wait_queue_head_t * EXPORT_SYMBOL(prepare_to_wait); -void +void fastcall prepare_to_wait_exclusive(wait_queue_head_t *q, wait_queue_t *wait, int state) { unsigned long flags; @@ -168,7 +166,7 @@ prepare_to_wait_exclusive(wait_queue_hea EXPORT_SYMBOL(prepare_to_wait_exclusive); -void finish_wait(wait_queue_head_t *q, wait_queue_t *wait) +void fastcall finish_wait(wait_queue_head_t *q, wait_queue_t *wait) { unsigned long flags; @@ -265,6 +263,7 @@ static struct task_struct *dup_task_stru static inline int dup_mmap(struct mm_struct * mm, struct mm_struct * oldmm) { struct vm_area_struct * mpnt, *tmp, **pprev; + struct rb_node **rb_link, *rb_parent; int retval; unsigned long charge = 0; @@ -275,8 +274,11 @@ static inline int dup_mmap(struct mm_str mm->mmap_cache = NULL; mm->free_area_cache = TASK_UNMAPPED_BASE; mm->map_count = 0; - mm->rss = 0; + zero_rss(mm); cpus_clear(mm->cpu_vm_mask); + mm->mm_rb = RB_ROOT; + rb_link = &mm->mm_rb.rb_node; + rb_parent = NULL; pprev = &mm->mmap; /* @@ -324,11 +326,17 @@ static inline int dup_mmap(struct mm_str /* * Link in the new vma and copy the page table entries: - * link in first so that swapoff can see swap entries. + * link in first so that swapoff can see swap entries, + * and try_to_unmap_one's find_vma find the new vma. */ spin_lock(&mm->page_table_lock); *pprev = tmp; pprev = &tmp->vm_next; + + __vma_link_rb(mm, tmp, rb_link, rb_parent); + rb_link = &tmp->vm_rb.rb_right; + rb_parent = &tmp->vm_rb; + mm->map_count++; retval = copy_page_range(mm, current->mm, tmp); spin_unlock(&mm->page_table_lock); @@ -340,7 +348,6 @@ static inline int dup_mmap(struct mm_str goto fail; } retval = 0; - build_mmap_rb(mm); out: flush_tlb_mm(current->mm); @@ -418,7 +425,7 @@ struct mm_struct * mm_alloc(void) * is dropped: either by a lazy thread or by * mmput. Free the page directory and the mm. */ -void __mmdrop(struct mm_struct *mm) +void fastcall __mmdrop(struct mm_struct *mm) { BUG_ON(mm == &init_mm); mm_free_pgd(mm); --- linux-2.6.4-rc2/kernel/futex.c 2004-02-17 20:48:46.000000000 -0800 +++ 25/kernel/futex.c 2004-03-07 20:47:38.000000000 -0800 @@ -269,7 +269,11 @@ static void wake_futex(struct futex_q *q * The lock in wake_up_all() is a crucial memory barrier after the * list_del_init() and also before assigning to q->lock_ptr. */ + + current->flags |= PF_FUTEX_DEBUG; wake_up_all(&q->waiters); + current->flags &= ~PF_FUTEX_DEBUG; + /* * The waiting task can free the futex_q as soon as this is written, * without taking any locks. This must come last. @@ -490,8 +494,11 @@ static int futex_wait(unsigned long uadd * !list_empty() is safe here without any lock. * q.lock_ptr != 0 is not safe, because of ordering against wakeup. */ - if (likely(!list_empty(&q.list))) + if (likely(!list_empty(&q.list))) { + current->flags |= PF_FUTEX_DEBUG; time = schedule_timeout(time); + current->flags &= ~PF_FUTEX_DEBUG; + } __set_current_state(TASK_RUNNING); /* @@ -505,7 +512,11 @@ static int futex_wait(unsigned long uadd if (time == 0) return -ETIMEDOUT; /* A spurious wakeup should never happen. */ - WARN_ON(!signal_pending(current)); + if (!signal_pending(current)) { + printk("futex_wait woken: %lu %i %lu\n", + uaddr, val, time); + WARN_ON(1); + } return -EINTR; out_unqueue: --- linux-2.6.4-rc2/kernel/kthread.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/kernel/kthread.c 2004-03-07 20:47:54.000000000 -0800 @@ -91,7 +91,6 @@ static void keventd_create_kthread(void } else { wait_for_completion(&create->started); create->result = find_task_by_pid(pid); - wait_task_inactive(create->result); } complete(&create->done); } @@ -131,7 +130,9 @@ struct task_struct *kthread_create(int ( void kthread_bind(struct task_struct *k, unsigned int cpu) { BUG_ON(k->state != TASK_INTERRUPTIBLE); - k->thread_info->cpu = cpu; + /* Must have done schedule() in kthread() before we set_task_cpu */ + wait_task_inactive(k); + set_task_cpu(k, cpu); k->cpus_allowed = cpumask_of_cpu(cpu); } --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/kernel/lockmeter.c 2004-03-07 20:48:17.000000000 -0800 @@ -0,0 +1,1178 @@ +/* + * Copyright (C) 1999,2000 Silicon Graphics, Inc. + * + * Written by John Hawkes (hawkes@sgi.com) + * Based on klstat.c by Jack Steiner (steiner@sgi.com) + * + * Modified by Ray Bryant (raybry@us.ibm.com) + * Changes Copyright (C) 2000 IBM, Inc. + * Added save of index in spinlock_t to improve efficiency + * of "hold" time reporting for spinlocks + * Added support for hold time statistics for read and write + * locks. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ASSERT(cond) +#define bzero(loc,size) memset(loc,0,size) + +/*<---------------------------------------------------*/ +/* lockmeter.c */ +/*>---------------------------------------------------*/ + +static lstat_control_t lstat_control __cacheline_aligned = + { LSTAT_OFF, SPIN_LOCK_UNLOCKED, SPIN_LOCK_UNLOCKED, + 19 * 0, NR_CPUS * 0, 0, NR_CPUS * 0 }; + +static ushort lstat_make_dir_entry(void *, void *); + +/* + * lstat_lookup + * + * Given a RA, locate the directory entry for the lock. + */ +static ushort +lstat_lookup(void *lock_ptr, void *caller_ra) +{ + ushort index; + lstat_directory_entry_t *dirp; + + dirp = lstat_control.dir; + + index = lstat_control.hashtab[DIRHASH(caller_ra)]; + while (dirp[index].caller_ra != caller_ra) { + if (index == 0) { + return lstat_make_dir_entry(lock_ptr, caller_ra); + } + index = dirp[index].next_stat_index; + } + + if (dirp[index].lock_ptr != NULL && dirp[index].lock_ptr != lock_ptr) { + dirp[index].lock_ptr = NULL; + } + + return index; +} + +/* + * lstat_make_dir_entry + * Called to add a new lock to the lock directory. + */ +static ushort +lstat_make_dir_entry(void *lock_ptr, void *caller_ra) +{ + lstat_directory_entry_t *dirp; + ushort index, hindex; + unsigned long flags; + + /* lock the table without recursively reentering this metering code */ + local_irq_save(flags); + _raw_spin_lock(&lstat_control.directory_lock); + + hindex = DIRHASH(caller_ra); + index = lstat_control.hashtab[hindex]; + dirp = lstat_control.dir; + while (index && dirp[index].caller_ra != caller_ra) + index = dirp[index].next_stat_index; + + if (index == 0) { + if (lstat_control.next_free_dir_index < LSTAT_MAX_STAT_INDEX) { + index = lstat_control.next_free_dir_index++; + lstat_control.dir[index].caller_ra = caller_ra; + lstat_control.dir[index].lock_ptr = lock_ptr; + lstat_control.dir[index].next_stat_index = + lstat_control.hashtab[hindex]; + lstat_control.hashtab[hindex] = index; + } else { + lstat_control.dir_overflow++; + } + } + _raw_spin_unlock(&lstat_control.directory_lock); + local_irq_restore(flags); + return index; +} + +int +lstat_update(void *lock_ptr, void *caller_ra, int action) +{ + int index; + int cpu; + + ASSERT(action < LSTAT_ACT_MAX_VALUES); + + if (lstat_control.state == LSTAT_OFF) + return 0; + + index = lstat_lookup(lock_ptr, caller_ra); + cpu = THIS_CPU_NUMBER; + (*lstat_control.counts[cpu])[index].count[action]++; + (*lstat_control.counts[cpu])[index].acquire_time = get_cycles(); + + return index; +} + +int +lstat_update_time(void *lock_ptr, void *caller_ra, int action, uint32_t ticks) +{ + ushort index; + int cpu; + + ASSERT(action < LSTAT_ACT_MAX_VALUES); + + if (lstat_control.state == LSTAT_OFF) + return 0; + + index = lstat_lookup(lock_ptr, caller_ra); + cpu = THIS_CPU_NUMBER; + (*lstat_control.counts[cpu])[index].count[action]++; + (*lstat_control.counts[cpu])[index].cum_wait_ticks += (uint64_t) ticks; + if ((*lstat_control.counts[cpu])[index].max_wait_ticks < ticks) + (*lstat_control.counts[cpu])[index].max_wait_ticks = ticks; + + (*lstat_control.counts[cpu])[index].acquire_time = get_cycles(); + + return index; +} + +void +_metered_spin_lock(spinlock_t * lock_ptr) +{ + if (lstat_control.state == LSTAT_OFF) { + _raw_spin_lock(lock_ptr); /* do the real lock */ + PUT_INDEX(lock_ptr, 0); /* clean index in case lockmetering */ + /* gets turned on before unlock */ + } else { + void *this_pc = LSTAT_RA(LSTAT_RA_SPIN); + int index; + + if (_raw_spin_trylock(lock_ptr)) { + index = lstat_update(lock_ptr, this_pc, + LSTAT_ACT_NO_WAIT); + } else { + uint32_t start_cycles = get_cycles(); + _raw_spin_lock(lock_ptr); /* do the real lock */ + index = lstat_update_time(lock_ptr, this_pc, + LSTAT_ACT_SPIN, get_cycles() - start_cycles); + } + /* save the index in the lock itself for use in spin unlock */ + PUT_INDEX(lock_ptr, index); + } +} + +int +_metered_spin_trylock(spinlock_t * lock_ptr) +{ + if (lstat_control.state == LSTAT_OFF) { + return _raw_spin_trylock(lock_ptr); + } else { + int retval; + void *this_pc = LSTAT_RA(LSTAT_RA_SPIN); + + if ((retval = _raw_spin_trylock(lock_ptr))) { + int index = lstat_update(lock_ptr, this_pc, + LSTAT_ACT_NO_WAIT); + /* + * save the index in the lock itself for use in spin + * unlock + */ + PUT_INDEX(lock_ptr, index); + } else { + lstat_update(lock_ptr, this_pc, LSTAT_ACT_REJECT); + } + + return retval; + } +} + +void +_metered_spin_unlock(spinlock_t * lock_ptr) +{ + int index = -1; + + if (lstat_control.state != LSTAT_OFF) { + index = GET_INDEX(lock_ptr); + /* + * If statistics were turned off when we set the lock, + * then the index can be zero. If that is the case, + * then collect no stats on this call. + */ + if (index > 0) { + uint32_t hold_time; + int cpu = THIS_CPU_NUMBER; + hold_time = get_cycles() - + (*lstat_control.counts[cpu])[index].acquire_time; + (*lstat_control.counts[cpu])[index].cum_hold_ticks += + (uint64_t) hold_time; + if ((*lstat_control.counts[cpu])[index].max_hold_ticks < + hold_time) + (*lstat_control.counts[cpu])[index]. + max_hold_ticks = hold_time; + } + } + + /* make sure we don't have a stale index value saved */ + PUT_INDEX(lock_ptr, 0); + _raw_spin_unlock(lock_ptr); /* do the real unlock */ +} + +/* + * allocate the next global read lock structure and store its index + * in the rwlock at "lock_ptr". + */ +uint32_t +alloc_rwlock_struct(rwlock_t * rwlock_ptr) +{ + int index; + unsigned long flags; + int cpu = THIS_CPU_NUMBER; + + /* If we've already overflowed, then do a quick exit */ + if (lstat_control.next_free_read_lock_index > + LSTAT_MAX_READ_LOCK_INDEX) { + lstat_control.rwlock_overflow++; + return 0; + } + + local_irq_save(flags); + _raw_spin_lock(&lstat_control.directory_lock); + + /* It is possible this changed while we were waiting for the directory_lock */ + if (lstat_control.state == LSTAT_OFF) { + index = 0; + goto unlock; + } + + /* It is possible someone else got here first and set the index */ + if ((index = GET_RWINDEX(rwlock_ptr)) == 0) { + /* + * we can't turn on read stats for this lock while there are + * readers (this would mess up the running hold time sum at + * unlock time) + */ + if (RWLOCK_READERS(rwlock_ptr) != 0) { + index = 0; + goto unlock; + } + + /* + * if stats are turned on after being off, we may need to + * return an old index from when the statistics were on last + * time. + */ + for (index = 1; index < lstat_control.next_free_read_lock_index; + index++) + if ((*lstat_control.read_lock_counts[cpu])[index]. + lock_ptr == rwlock_ptr) + goto put_index_and_unlock; + + /* allocate the next global read lock structure */ + if (lstat_control.next_free_read_lock_index >= + LSTAT_MAX_READ_LOCK_INDEX) { + lstat_control.rwlock_overflow++; + index = 0; + goto unlock; + } + index = lstat_control.next_free_read_lock_index++; + + /* + * initialize the global read stats data structure for each + * cpu + */ + for (cpu = 0; cpu < num_online_cpus(); cpu++) { + (*lstat_control.read_lock_counts[cpu])[index].lock_ptr = + rwlock_ptr; + } +put_index_and_unlock: + /* store the index for the read lock structure into the lock */ + PUT_RWINDEX(rwlock_ptr, index); + } + +unlock: + _raw_spin_unlock(&lstat_control.directory_lock); + local_irq_restore(flags); + return index; +} + +void +_metered_read_lock(rwlock_t * rwlock_ptr) +{ + void *this_pc; + uint32_t start_cycles; + int index; + int cpu; + unsigned long flags; + int readers_before, readers_after; + uint64_t cycles64; + + if (lstat_control.state == LSTAT_OFF) { + _raw_read_lock(rwlock_ptr); + /* clean index in case lockmetering turns on before an unlock */ + PUT_RWINDEX(rwlock_ptr, 0); + return; + } + + this_pc = LSTAT_RA(LSTAT_RA_READ); + cpu = THIS_CPU_NUMBER; + index = GET_RWINDEX(rwlock_ptr); + + /* allocate the global stats entry for this lock, if needed */ + if (index == 0) + index = alloc_rwlock_struct(rwlock_ptr); + + readers_before = RWLOCK_READERS(rwlock_ptr); + if (_raw_read_trylock(rwlock_ptr)) { + /* + * We have decremented the lock to count a new reader, + * and have confirmed that no writer has it locked. + */ + /* update statistics if enabled */ + if (index > 0) { + local_irq_save(flags); + lstat_update((void *) rwlock_ptr, this_pc, + LSTAT_ACT_NO_WAIT); + /* preserve value of TSC so cum_hold_ticks and start_busy use same value */ + cycles64 = get_cycles64(); + (*lstat_control.read_lock_counts[cpu])[index]. + cum_hold_ticks -= cycles64; + + /* record time and cpu of start of busy period */ + /* this is not perfect (some race conditions are possible) */ + if (readers_before == 0) { + (*lstat_control.read_lock_counts[cpu])[index]. + start_busy = cycles64; + PUT_RW_CPU(rwlock_ptr, cpu); + } + readers_after = RWLOCK_READERS(rwlock_ptr); + if (readers_after > + (*lstat_control.read_lock_counts[cpu])[index]. + max_readers) + (*lstat_control.read_lock_counts[cpu])[index]. + max_readers = readers_after; + local_irq_restore(flags); + } + + return; + } + /* If we get here, then we could not quickly grab the read lock */ + + start_cycles = get_cycles(); /* start counting the wait time */ + + /* Now spin until read_lock is successful */ + _raw_read_lock(rwlock_ptr); + + lstat_update_time((void *) rwlock_ptr, this_pc, LSTAT_ACT_SPIN, + get_cycles() - start_cycles); + + /* update statistics if they are enabled for this lock */ + if (index > 0) { + local_irq_save(flags); + cycles64 = get_cycles64(); + (*lstat_control.read_lock_counts[cpu])[index].cum_hold_ticks -= + cycles64; + + /* this is not perfect (some race conditions are possible) */ + if (readers_before == 0) { + (*lstat_control.read_lock_counts[cpu])[index]. + start_busy = cycles64; + PUT_RW_CPU(rwlock_ptr, cpu); + } + readers_after = RWLOCK_READERS(rwlock_ptr); + if (readers_after > + (*lstat_control.read_lock_counts[cpu])[index].max_readers) + (*lstat_control.read_lock_counts[cpu])[index]. + max_readers = readers_after; + local_irq_restore(flags); + } +} + +void +_metered_read_unlock(rwlock_t * rwlock_ptr) +{ + int index; + int cpu; + unsigned long flags; + uint64_t busy_length; + uint64_t cycles64; + + if (lstat_control.state == LSTAT_OFF) { + _raw_read_unlock(rwlock_ptr); + return; + } + + index = GET_RWINDEX(rwlock_ptr); + cpu = THIS_CPU_NUMBER; + + if (index > 0) { + local_irq_save(flags); + /* + * preserve value of TSC so cum_hold_ticks and busy_ticks are + * consistent. + */ + cycles64 = get_cycles64(); + (*lstat_control.read_lock_counts[cpu])[index].cum_hold_ticks += + cycles64; + (*lstat_control.read_lock_counts[cpu])[index].read_lock_count++; + + /* + * once again, this is not perfect (some race conditions are + * possible) + */ + if (RWLOCK_READERS(rwlock_ptr) == 1) { + int cpu1 = GET_RW_CPU(rwlock_ptr); + uint64_t last_start_busy = + (*lstat_control.read_lock_counts[cpu1])[index]. + start_busy; + (*lstat_control.read_lock_counts[cpu])[index]. + busy_periods++; + if (cycles64 > last_start_busy) { + busy_length = cycles64 - last_start_busy; + (*lstat_control.read_lock_counts[cpu])[index]. + busy_ticks += busy_length; + if (busy_length > + (*lstat_control. + read_lock_counts[cpu])[index]. + max_busy) + (*lstat_control. + read_lock_counts[cpu])[index]. + max_busy = busy_length; + } + } + local_irq_restore(flags); + } + _raw_read_unlock(rwlock_ptr); +} + +void +_metered_write_lock(rwlock_t * rwlock_ptr) +{ + uint32_t start_cycles; + void *this_pc; + uint32_t spin_ticks = 0; /* in anticipation of a potential wait */ + int index; + int write_index = 0; + int cpu; + enum { + writer_writer_conflict, + writer_reader_conflict + } why_wait = writer_writer_conflict; + + if (lstat_control.state == LSTAT_OFF) { + _raw_write_lock(rwlock_ptr); + /* clean index in case lockmetering turns on before an unlock */ + PUT_RWINDEX(rwlock_ptr, 0); + return; + } + + this_pc = LSTAT_RA(LSTAT_RA_WRITE); + cpu = THIS_CPU_NUMBER; + index = GET_RWINDEX(rwlock_ptr); + + /* allocate the global stats entry for this lock, if needed */ + if (index == 0) { + index = alloc_rwlock_struct(rwlock_ptr); + } + + if (_raw_write_trylock(rwlock_ptr)) { + /* We acquired the lock on the first try */ + write_index = lstat_update((void *) rwlock_ptr, this_pc, + LSTAT_ACT_NO_WAIT); + /* save the write_index for use in unlock if stats enabled */ + if (index > 0) + (*lstat_control.read_lock_counts[cpu])[index]. + write_index = write_index; + return; + } + + /* If we get here, then we could not quickly grab the write lock */ + start_cycles = get_cycles(); /* start counting the wait time */ + + why_wait = RWLOCK_READERS(rwlock_ptr) ? + writer_reader_conflict : writer_writer_conflict; + + /* Now set the lock and wait for conflicts to disappear */ + _raw_write_lock(rwlock_ptr); + + spin_ticks = get_cycles() - start_cycles; + + /* update stats -- if enabled */ + if (index > 0 && spin_ticks) { + if (why_wait == writer_reader_conflict) { + /* waited due to a reader holding the lock */ + write_index = lstat_update_time((void *)rwlock_ptr, + this_pc, LSTAT_ACT_SPIN, spin_ticks); + } else { + /* + * waited due to another writer holding the lock + */ + write_index = lstat_update_time((void *)rwlock_ptr, + this_pc, LSTAT_ACT_WW_SPIN, spin_ticks); + (*lstat_control.counts[cpu])[write_index]. + cum_wait_ww_ticks += spin_ticks; + if (spin_ticks > + (*lstat_control.counts[cpu])[write_index]. + max_wait_ww_ticks) { + (*lstat_control.counts[cpu])[write_index]. + max_wait_ww_ticks = spin_ticks; + } + } + + /* save the directory index for use on write_unlock */ + (*lstat_control.read_lock_counts[cpu])[index]. + write_index = write_index; + } +} + +void +_metered_write_unlock(rwlock_t * rwlock_ptr) +{ + int index; + int cpu; + int write_index; + uint32_t hold_time; + + if (lstat_control.state == LSTAT_OFF) { + _raw_write_unlock(rwlock_ptr); + return; + } + + cpu = THIS_CPU_NUMBER; + index = GET_RWINDEX(rwlock_ptr); + + /* update statistics if stats enabled for this lock */ + if (index > 0) { + write_index = + (*lstat_control.read_lock_counts[cpu])[index].write_index; + + hold_time = get_cycles() - + (*lstat_control.counts[cpu])[write_index].acquire_time; + (*lstat_control.counts[cpu])[write_index].cum_hold_ticks += + (uint64_t) hold_time; + if ((*lstat_control.counts[cpu])[write_index].max_hold_ticks < + hold_time) + (*lstat_control.counts[cpu])[write_index]. + max_hold_ticks = hold_time; + } + _raw_write_unlock(rwlock_ptr); +} + +int +_metered_write_trylock(rwlock_t * rwlock_ptr) +{ + int retval; + void *this_pc = LSTAT_RA(LSTAT_RA_WRITE); + + if ((retval = _raw_write_trylock(rwlock_ptr))) { + lstat_update(rwlock_ptr, this_pc, LSTAT_ACT_NO_WAIT); + } else { + lstat_update(rwlock_ptr, this_pc, LSTAT_ACT_REJECT); + } + + return retval; +} + +static void +init_control_space(void) +{ + /* Set all control space pointers to null and indices to "empty" */ + int cpu; + + /* + * Access CPU_CYCLE_FREQUENCY at the outset, which in some + * architectures may trigger a runtime calculation that uses a + * spinlock. Let's do this before lockmetering is turned on. + */ + if (CPU_CYCLE_FREQUENCY == 0) + BUG(); + + lstat_control.hashtab = NULL; + lstat_control.dir = NULL; + for (cpu = 0; cpu < NR_CPUS; cpu++) { + lstat_control.counts[cpu] = NULL; + lstat_control.read_lock_counts[cpu] = NULL; + } +} + +static int +reset_lstat_data(void) +{ + int cpu, flags; + + flags = 0; + lstat_control.next_free_dir_index = 1; /* 0 is for overflows */ + lstat_control.next_free_read_lock_index = 1; + lstat_control.dir_overflow = 0; + lstat_control.rwlock_overflow = 0; + + lstat_control.started_cycles64 = 0; + lstat_control.ending_cycles64 = 0; + lstat_control.enabled_cycles64 = 0; + lstat_control.first_started_time = 0; + lstat_control.started_time = 0; + lstat_control.ending_time = 0; + lstat_control.intervals = 0; + + /* + * paranoia -- in case someone does a "lockstat reset" before + * "lockstat on" + */ + if (lstat_control.hashtab) { + bzero(lstat_control.hashtab, + LSTAT_HASH_TABLE_SIZE * sizeof (short)); + bzero(lstat_control.dir, LSTAT_MAX_STAT_INDEX * + sizeof (lstat_directory_entry_t)); + + for (cpu = 0; cpu < num_online_cpus(); cpu++) { + bzero(lstat_control.counts[cpu], + sizeof (lstat_cpu_counts_t)); + bzero(lstat_control.read_lock_counts[cpu], + sizeof (lstat_read_lock_cpu_counts_t)); + } + } +#ifdef NOTDEF + _raw_spin_unlock(&lstat_control.directory_lock); + local_irq_restore(flags); +#endif + return 1; +} + +static void +release_control_space(void) +{ + /* + * Called when either (1) allocation of kmem + * or (2) when user writes LSTAT_RELEASE to /pro/lockmeter. + * Assume that all pointers have been initialized to zero, + * i.e., nonzero pointers are valid addresses. + */ + int cpu; + + if (lstat_control.hashtab) { + kfree(lstat_control.hashtab); + lstat_control.hashtab = NULL; + } + + if (lstat_control.dir) { + vfree(lstat_control.dir); + lstat_control.dir = NULL; + } + + for (cpu = 0; cpu < NR_CPUS; cpu++) { + if (lstat_control.counts[cpu]) { + vfree(lstat_control.counts[cpu]); + lstat_control.counts[cpu] = NULL; + } + if (lstat_control.read_lock_counts[cpu]) { + kfree(lstat_control.read_lock_counts[cpu]); + lstat_control.read_lock_counts[cpu] = NULL; + } + } +} + +int +get_lockmeter_info_size(void) +{ + return sizeof (lstat_user_request_t) + + num_online_cpus() * sizeof (lstat_cpu_counts_t) + + num_online_cpus() * sizeof (lstat_read_lock_cpu_counts_t) + + (LSTAT_MAX_STAT_INDEX * sizeof (lstat_directory_entry_t)); +} + +ssize_t +get_lockmeter_info(char *buffer, size_t max_len, loff_t * last_index) +{ + lstat_user_request_t req; + struct timeval tv; + ssize_t next_ret_bcount; + ssize_t actual_ret_bcount = 0; + int cpu; + + *last_index = 0; /* a one-shot read */ + + req.lstat_version = LSTAT_VERSION; + req.state = lstat_control.state; + req.maxcpus = num_online_cpus(); + req.cycleval = CPU_CYCLE_FREQUENCY; +#ifdef notyet + req.kernel_magic_addr = (void *) &_etext; + req.kernel_end_addr = (void *) &_etext; +#endif + req.uts = system_utsname; + req.intervals = lstat_control.intervals; + + req.first_started_time = lstat_control.first_started_time; + req.started_time = lstat_control.started_time; + req.started_cycles64 = lstat_control.started_cycles64; + + req.next_free_dir_index = lstat_control.next_free_dir_index; + req.next_free_read_lock_index = lstat_control.next_free_read_lock_index; + req.dir_overflow = lstat_control.dir_overflow; + req.rwlock_overflow = lstat_control.rwlock_overflow; + + if (lstat_control.state == LSTAT_OFF) { + if (req.intervals == 0) { + /* mesasurement is off and no valid data present */ + next_ret_bcount = sizeof (lstat_user_request_t); + req.enabled_cycles64 = 0; + + if ((actual_ret_bcount + next_ret_bcount) > max_len) + return actual_ret_bcount; + + copy_to_user(buffer, (void *) &req, next_ret_bcount); + actual_ret_bcount += next_ret_bcount; + return actual_ret_bcount; + } else { + /* + * measurement is off but valid data present + * fetch time info from lstat_control + */ + req.ending_time = lstat_control.ending_time; + req.ending_cycles64 = lstat_control.ending_cycles64; + req.enabled_cycles64 = lstat_control.enabled_cycles64; + } + } else { + /* + * this must be a read while data active--use current time, + * etc + */ + do_gettimeofday(&tv); + req.ending_time = tv.tv_sec; + req.ending_cycles64 = get_cycles64(); + req.enabled_cycles64 = req.ending_cycles64 - + req.started_cycles64 + lstat_control.enabled_cycles64; + } + + next_ret_bcount = sizeof (lstat_user_request_t); + if ((actual_ret_bcount + next_ret_bcount) > max_len) + return actual_ret_bcount; + + copy_to_user(buffer, (void *) &req, next_ret_bcount); + actual_ret_bcount += next_ret_bcount; + + if (!lstat_control.counts[0]) /* not initialized? */ + return actual_ret_bcount; + + next_ret_bcount = sizeof (lstat_cpu_counts_t); + for (cpu = 0; cpu < num_online_cpus(); cpu++) { + if ((actual_ret_bcount + next_ret_bcount) > max_len) + return actual_ret_bcount; /* leave early */ + copy_to_user(buffer + actual_ret_bcount, + lstat_control.counts[cpu], next_ret_bcount); + actual_ret_bcount += next_ret_bcount; + } + + next_ret_bcount = LSTAT_MAX_STAT_INDEX * + sizeof (lstat_directory_entry_t); + if (((actual_ret_bcount + next_ret_bcount) > max_len) + || !lstat_control.dir) + return actual_ret_bcount; /* leave early */ + + copy_to_user(buffer + actual_ret_bcount, lstat_control.dir, + next_ret_bcount); + actual_ret_bcount += next_ret_bcount; + + next_ret_bcount = sizeof (lstat_read_lock_cpu_counts_t); + for (cpu = 0; cpu < num_online_cpus(); cpu++) { + if (actual_ret_bcount + next_ret_bcount > max_len) + return actual_ret_bcount; + copy_to_user(buffer + actual_ret_bcount, + lstat_control.read_lock_counts[cpu], + next_ret_bcount); + actual_ret_bcount += next_ret_bcount; + } + + return actual_ret_bcount; +} + +/* + * Writing to the /proc lockmeter node enables or disables metering. + * based upon the first byte of the "written" data. + * The following values are defined: + * LSTAT_ON: 1st call: allocates storage, intializes and turns on measurement + * subsequent calls just turn on measurement + * LSTAT_OFF: turns off measurement + * LSTAT_RESET: resets statistics + * LSTAT_RELEASE: releases statistics storage + * + * This allows one to accumulate statistics over several lockstat runs: + * + * lockstat on + * lockstat off + * ...repeat above as desired... + * lockstat get + * ...now start a new set of measurements... + * lockstat reset + * lockstat on + * ... + * + */ +ssize_t +put_lockmeter_info(const char *buffer, size_t len) +{ + int error = 0; + int dirsize, countsize, read_lock_countsize, hashsize; + int cpu; + char put_char; + int i, read_lock_blocks; + unsigned long flags; + rwlock_t *lock_ptr; + struct timeval tv; + + if (len <= 0) + return -EINVAL; + + _raw_spin_lock(&lstat_control.control_lock); + + get_user(put_char, buffer); + switch (put_char) { + + case LSTAT_OFF: + if (lstat_control.state != LSTAT_OFF) { + /* + * To avoid seeing read lock hold times in an + * inconsisent state, we have to follow this protocol + * to turn off statistics + */ + local_irq_save(flags); + /* + * getting this lock will stop any read lock block + * allocations + */ + _raw_spin_lock(&lstat_control.directory_lock); + /* + * keep any more read lock blocks from being + * allocated + */ + lstat_control.state = LSTAT_OFF; + /* record how may read lock blocks there are */ + read_lock_blocks = + lstat_control.next_free_read_lock_index; + _raw_spin_unlock(&lstat_control.directory_lock); + /* now go through the list of read locks */ + cpu = THIS_CPU_NUMBER; + for (i = 1; i < read_lock_blocks; i++) { + lock_ptr = + (*lstat_control.read_lock_counts[cpu])[i]. + lock_ptr; + /* is this saved lock address still valid? */ + if (GET_RWINDEX(lock_ptr) == i) { + /* + * lock address appears to still be + * valid because we only hold one lock + * at a time, this can't cause a + * deadlock unless this is a lock held + * as part of the current system call + * path. At the moment there + * are no READ mode locks held to get + * here from user space, so we solve + * this by skipping locks held in + * write mode. + */ + if (RWLOCK_IS_WRITE_LOCKED(lock_ptr)) { + PUT_RWINDEX(lock_ptr, 0); + continue; + } + /* + * now we know there are no read + * holders of this lock! stop + * statistics collection for this + * lock + */ + _raw_write_lock(lock_ptr); + PUT_RWINDEX(lock_ptr, 0); + _raw_write_unlock(lock_ptr); + } + /* + * it may still be possible for the hold time + * sum to be negative e.g. if a lock is + * reallocated while "busy" we will have to fix + * this up in the data reduction program. + */ + } + local_irq_restore(flags); + lstat_control.intervals++; + lstat_control.ending_cycles64 = get_cycles64(); + lstat_control.enabled_cycles64 += + lstat_control.ending_cycles64 - + lstat_control.started_cycles64; + do_gettimeofday(&tv); + lstat_control.ending_time = tv.tv_sec; + /* + * don't deallocate the structures -- we may do a + * lockstat on to add to the data that is already + * there. Use LSTAT_RELEASE to release storage + */ + } else { + error = -EBUSY; /* already OFF */ + } + break; + + case LSTAT_ON: + if (lstat_control.state == LSTAT_OFF) { +#ifdef DEBUG_LOCKMETER + printk("put_lockmeter_info(cpu=%d): LSTAT_ON\n", + THIS_CPU_NUMBER); +#endif + lstat_control.next_free_dir_index = 1; /* 0 is for overflows */ + + dirsize = LSTAT_MAX_STAT_INDEX * + sizeof (lstat_directory_entry_t); + hashsize = + (1 + LSTAT_HASH_TABLE_SIZE) * sizeof (ushort); + countsize = sizeof (lstat_cpu_counts_t); + read_lock_countsize = + sizeof (lstat_read_lock_cpu_counts_t); +#ifdef DEBUG_LOCKMETER + printk(" dirsize:%d", dirsize); + printk(" hashsize:%d", hashsize); + printk(" countsize:%d", countsize); + printk(" read_lock_countsize:%d\n", + read_lock_countsize); +#endif +#ifdef DEBUG_LOCKMETER + { + int secs; + unsigned long cycles; + uint64_t cycles64; + + do_gettimeofday(&tv); + secs = tv.tv_sec; + do { + do_gettimeofday(&tv); + } while (secs == tv.tv_sec); + cycles = get_cycles(); + cycles64 = get_cycles64(); + secs = tv.tv_sec; + do { + do_gettimeofday(&tv); + } while (secs == tv.tv_sec); + cycles = get_cycles() - cycles; + cycles64 = get_cycles64() - cycles; + printk("lockmeter: cycleFrequency:%d " + "cycles:%d cycles64:%d\n", + CPU_CYCLE_FREQUENCY, cycles, cycles64); + } +#endif + + /* + * if this is the first call, allocate storage and + * initialize + */ + if (!lstat_control.hashtab) { + + spin_lock_init(&lstat_control.directory_lock); + + /* guarantee all pointers at zero */ + init_control_space(); + + lstat_control.hashtab = + kmalloc(hashsize, GFP_KERNEL); + if (!lstat_control.hashtab) { + error = -ENOSPC; +#ifdef DEBUG_LOCKMETER + printk("!!error kmalloc of hashtab\n"); +#endif + } + lstat_control.dir = vmalloc(dirsize); + if (!lstat_control.dir) { + error = -ENOSPC; +#ifdef DEBUG_LOCKMETER + printk("!!error kmalloc of dir\n"); +#endif + } + + for (cpu = 0; cpu < num_online_cpus(); cpu++) { + lstat_control.counts[cpu] = + vmalloc(countsize); + if (!lstat_control.counts[cpu]) { + error = -ENOSPC; +#ifdef DEBUG_LOCKMETER + printk("!!error vmalloc of " + "counts[%d]\n", cpu); +#endif + } + lstat_control.read_lock_counts[cpu] = + (lstat_read_lock_cpu_counts_t *) + kmalloc(read_lock_countsize, + GFP_KERNEL); + if (!lstat_control. + read_lock_counts[cpu]) { + error = -ENOSPC; +#ifdef DEBUG_LOCKMETER + printk("!!error kmalloc of " + "read_lock_counts[%d]\n", + cpu); +#endif + } + } + } + + if (error) { + /* + * One or more kmalloc failures -- free + * everything + */ + release_control_space(); + } else { + + if (!reset_lstat_data()) { + error = -EINVAL; + break; + }; + + /* + * record starting and ending times and the + * like + */ + if (lstat_control.intervals == 0) { + do_gettimeofday(&tv); + lstat_control.first_started_time = + tv.tv_sec; + } + lstat_control.started_cycles64 = get_cycles64(); + do_gettimeofday(&tv); + lstat_control.started_time = tv.tv_sec; + + lstat_control.state = LSTAT_ON; + } + } else { + error = -EBUSY; /* already ON */ + } + break; + + case LSTAT_RESET: + if (lstat_control.state == LSTAT_OFF) { + if (!reset_lstat_data()) + error = -EINVAL; + } else { + error = -EBUSY; /* still on; can't reset */ + } + break; + + case LSTAT_RELEASE: + if (lstat_control.state == LSTAT_OFF) { + release_control_space(); + lstat_control.intervals = 0; + lstat_control.enabled_cycles64 = 0; + } else { + error = -EBUSY; + } + break; + + default: + error = -EINVAL; + } /* switch */ + + _raw_spin_unlock(&lstat_control.control_lock); + return error ? error : len; +} + +#ifdef USER_MODE_TESTING +/* following used for user mode testing */ +void +lockmeter_init() +{ + int dirsize, hashsize, countsize, read_lock_countsize, cpu; + + printf("lstat_control is at %x size=%d\n", &lstat_control, + sizeof (lstat_control)); + printf("sizeof(spinlock_t)=%d\n", sizeof (spinlock_t)); + lstat_control.state = LSTAT_ON; + + lstat_control.directory_lock = SPIN_LOCK_UNLOCKED; + lstat_control.next_free_dir_index = 1; /* 0 is for overflows */ + lstat_control.next_free_read_lock_index = 1; + + dirsize = LSTAT_MAX_STAT_INDEX * sizeof (lstat_directory_entry_t); + hashsize = (1 + LSTAT_HASH_TABLE_SIZE) * sizeof (ushort); + countsize = sizeof (lstat_cpu_counts_t); + read_lock_countsize = sizeof (lstat_read_lock_cpu_counts_t); + + lstat_control.hashtab = (ushort *) malloc(hashsize); + + if (lstat_control.hashtab == 0) { + printf("malloc failure for at line %d in lockmeter.c\n", + __LINE__); + exit(0); + } + + lstat_control.dir = (lstat_directory_entry_t *) malloc(dirsize); + + if (lstat_control.dir == 0) { + printf("malloc failure for at line %d in lockmeter.c\n", cpu, + __LINE__); + exit(0); + } + + for (cpu = 0; cpu < num_online_cpus(); cpu++) { + int j, k; + j = (int) (lstat_control.counts[cpu] = + (lstat_cpu_counts_t *) malloc(countsize)); + k = (int) (lstat_control.read_lock_counts[cpu] = + (lstat_read_lock_cpu_counts_t *) + malloc(read_lock_countsize)); + if (j * k == 0) { + printf("malloc failure for cpu=%d at line %d in " + "lockmeter.c\n", cpu, __LINE__); + exit(0); + } + } + + memset(lstat_control.hashtab, 0, hashsize); + memset(lstat_control.dir, 0, dirsize); + + for (cpu = 0; cpu < num_online_cpus(); cpu++) { + memset(lstat_control.counts[cpu], 0, countsize); + memset(lstat_control.read_lock_counts[cpu], 0, + read_lock_countsize); + } +} + +asm(" \ +.align 4 \ +.globl __write_lock_failed \ +__write_lock_failed: \ + " LOCK "addl $" RW_LOCK_BIAS_STR ",(%eax) \ +1: cmpl $" RW_LOCK_BIAS_STR ",(%eax) \ + jne 1b \ +\ + " LOCK "subl $" RW_LOCK_BIAS_STR ",(%eax) \ + jnz __write_lock_failed \ + ret \ +\ +\ +.align 4 \ +.globl __read_lock_failed \ +__read_lock_failed: \ + lock ; incl (%eax) \ +1: cmpl $1,(%eax) \ + js 1b \ +\ + lock ; decl (%eax) \ + js __read_lock_failed \ + ret \ +"); +#endif + +EXPORT_SYMBOL(_metered_spin_lock); +EXPORT_SYMBOL(_metered_spin_unlock); +EXPORT_SYMBOL(_metered_spin_trylock); +EXPORT_SYMBOL(_metered_read_lock); +EXPORT_SYMBOL(_metered_read_unlock); +EXPORT_SYMBOL(_metered_write_lock); +EXPORT_SYMBOL(_metered_write_unlock); --- linux-2.6.4-rc2/kernel/Makefile 2004-03-03 23:12:49.000000000 -0800 +++ 25/kernel/Makefile 2004-03-07 20:48:17.000000000 -0800 @@ -12,14 +12,16 @@ obj-y = sched.o fork.o exec_domain.o obj-$(CONFIG_FUTEX) += futex.o obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o obj-$(CONFIG_SMP) += cpu.o +obj-$(CONFIG_LOCKMETER) += lockmeter.o obj-$(CONFIG_UID16) += uid16.o obj-$(CONFIG_MODULES) += module.o obj-$(CONFIG_KALLSYMS) += kallsyms.o obj-$(CONFIG_PM) += power/ obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o -obj-$(CONFIG_COMPAT) += compat.o +obj-$(CONFIG_COMPAT) += compat.o compat_signal.o obj-$(CONFIG_IKCONFIG) += configs.o obj-$(CONFIG_IKCONFIG_PROC) += configs.o +obj-$(CONFIG_STOP_MACHINE) += stop_machine.o ifneq ($(CONFIG_IA64),y) # According to Alan Modra , the -fno-omit-frame-pointer is --- linux-2.6.4-rc2/kernel/module.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/kernel/module.c 2004-03-07 20:47:38.000000000 -0800 @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include #include @@ -474,190 +474,36 @@ static inline int try_force(unsigned int } #endif /* CONFIG_MODULE_FORCE_UNLOAD */ -static int try_stop_module_local(struct module *mod, int flags, int *forced) -{ - local_irq_disable(); - - /* If it's not unused, quit unless we are told to block. */ - if ((flags & O_NONBLOCK) && module_refcount(mod) != 0) { - if (!(*forced = try_force(flags))) { - local_irq_enable(); - return -EWOULDBLOCK; - } - } - - /* Mark it as dying. */ - mod->waiter = current; - mod->state = MODULE_STATE_GOING; - local_irq_enable(); - return 0; -} - -#ifdef CONFIG_SMP -/* Thread to stop each CPU in user context. */ -enum stopref_state { - STOPREF_WAIT, - STOPREF_PREPARE, - STOPREF_DISABLE_IRQ, - STOPREF_EXIT, -}; - -static enum stopref_state stopref_state; -static unsigned int stopref_num_threads; -static atomic_t stopref_thread_ack; - -static int stopref(void *cpu) -{ - int irqs_disabled = 0; - int prepared = 0; - - set_cpus_allowed(current, cpumask_of_cpu((int)(long)cpu)); - - /* Ack: we are alive */ - mb(); /* Theoretically the ack = 0 might not be on this CPU yet. */ - atomic_inc(&stopref_thread_ack); - - /* Simple state machine */ - while (stopref_state != STOPREF_EXIT) { - if (stopref_state == STOPREF_DISABLE_IRQ && !irqs_disabled) { - local_irq_disable(); - irqs_disabled = 1; - /* Ack: irqs disabled. */ - mb(); /* Must read state first. */ - atomic_inc(&stopref_thread_ack); - } else if (stopref_state == STOPREF_PREPARE && !prepared) { - /* Everyone is in place, hold CPU. */ - preempt_disable(); - prepared = 1; - mb(); /* Must read state first. */ - atomic_inc(&stopref_thread_ack); - } - if (irqs_disabled || prepared) - cpu_relax(); - else - yield(); - } - - /* Ack: we are exiting. */ - mb(); /* Must read state first. */ - atomic_inc(&stopref_thread_ack); - - if (irqs_disabled) - local_irq_enable(); - if (prepared) - preempt_enable(); - - return 0; -} - -/* Change the thread state */ -static void stopref_set_state(enum stopref_state state, int sleep) -{ - atomic_set(&stopref_thread_ack, 0); - wmb(); - stopref_state = state; - while (atomic_read(&stopref_thread_ack) != stopref_num_threads) { - if (sleep) - yield(); - else - cpu_relax(); - } -} - struct stopref { struct module *mod; int flags; int *forced; - struct completion started; }; -static int spawn_stopref(void *data) +/* Whole machine is stopped with interrupts off when this runs. */ +static inline int __try_stop_module(void *_sref) { - struct stopref *sref = data; - struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; - unsigned int i, cpu = smp_processor_id(); - int ret = 0; + struct stopref *sref = _sref; - complete(&sref->started); - - /* One high-prio thread per cpu. We'll do one (any one). */ - set_cpus_allowed(current, cpumask_of_cpu(cpu)); - sys_sched_setscheduler(current->pid, SCHED_FIFO, ¶m); - - atomic_set(&stopref_thread_ack, 0); - stopref_num_threads = 0; - stopref_state = STOPREF_WAIT; - - for_each_online_cpu(i) { - if (i == cpu) - continue; - ret = kernel_thread(stopref, (void *)(long)i, CLONE_KERNEL); - if (ret < 0) - break; - stopref_num_threads++; - } - - /* Wait for them all to come to life. */ - while (atomic_read(&stopref_thread_ack) != stopref_num_threads) - yield(); - - /* If some failed, kill them all. */ - if (ret < 0) { - stopref_set_state(STOPREF_EXIT, 1); - goto out; + /* If it's not unused, quit unless we are told to block. */ + if ((sref->flags & O_NONBLOCK) && module_refcount(sref->mod) != 0) { + if (!(*sref->forced = try_force(sref->flags))) + return -EWOULDBLOCK; } - /* Don't schedule us away at this point, please. */ - preempt_disable(); - - /* Now they are all started, make them hold the CPUs, ready. */ - stopref_set_state(STOPREF_PREPARE, 0); - - /* Make them disable irqs. */ - stopref_set_state(STOPREF_DISABLE_IRQ, 0); - - /* Atomically disable module if possible */ - ret = try_stop_module_local(sref->mod, sref->flags, sref->forced); - - stopref_set_state(STOPREF_EXIT, 0); - preempt_enable(); - -out: - /* Wait for kthread_stop */ - while (!kthread_should_stop()) { - __set_current_state(TASK_INTERRUPTIBLE); - schedule(); - } - return ret; + /* Mark it as dying. */ + sref->mod->waiter = current; + sref->mod->state = MODULE_STATE_GOING; + return 0; } static int try_stop_module(struct module *mod, int flags, int *forced) { - struct task_struct *p; struct stopref sref = { mod, flags, forced }; - int ret; - - init_completion(&sref.started); - /* No CPUs can come up or down during this. */ - lock_cpu_hotplug(); - p = kthread_run(spawn_stopref, &sref, "krmmod"); - if (IS_ERR(p)) - ret = PTR_ERR(p); - else { - wait_for_completion(&sref.started); - ret = kthread_stop(p); - } - unlock_cpu_hotplug(); - return ret; -} -#else /* ...!SMP */ -static inline int try_stop_module(struct module *mod, int flags, int *forced) -{ - return try_stop_module_local(mod, flags, forced); + return stop_machine_run(__try_stop_module, &sref, NR_CPUS); } -#endif unsigned int module_refcount(struct module *mod) { @@ -750,7 +596,9 @@ sys_delete_module(const char __user *nam wait_for_zero_refcount(mod); /* Final destruction now noone is using it. */ + up(&module_mutex); mod->exit(); + down(&module_mutex); free_module(mod); out: --- linux-2.6.4-rc2/kernel/pid.c 2003-11-09 16:45:05.000000000 -0800 +++ 25/kernel/pid.c 2004-03-07 20:47:20.000000000 -0800 @@ -57,7 +57,7 @@ static pidmap_t *map_limit = pidmap_arra static spinlock_t pidmap_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED; -inline void free_pidmap(int pid) +fastcall void free_pidmap(int pid) { pidmap_t *map = pidmap_array + pid / BITS_PER_PAGE; int offset = pid & BITS_PER_PAGE_MASK; @@ -122,6 +122,8 @@ return_pid: } if (!offset || !atomic_read(&map->nr_free)) { + if (!offset) + map--; next_map: map = next_free_map(map, &max_steps); if (!map) @@ -146,7 +148,7 @@ failure: return -1; } -inline struct pid *find_pid(enum pid_type type, int nr) +fastcall struct pid *find_pid(enum pid_type type, int nr) { struct list_head *elem, *bucket = &pid_hash[type][pid_hashfn(nr)]; struct pid *pid; @@ -159,14 +161,14 @@ inline struct pid *find_pid(enum pid_typ return NULL; } -void link_pid(task_t *task, struct pid_link *link, struct pid *pid) +void fastcall link_pid(task_t *task, struct pid_link *link, struct pid *pid) { atomic_inc(&pid->count); list_add_tail(&link->pid_chain, &pid->task_list); link->pidptr = pid; } -int attach_pid(task_t *task, enum pid_type type, int nr) +int fastcall attach_pid(task_t *task, enum pid_type type, int nr) { struct pid *pid = find_pid(type, nr); @@ -209,7 +211,7 @@ static void _detach_pid(task_t *task, en __detach_pid(task, type); } -void detach_pid(task_t *task, enum pid_type type) +void fastcall detach_pid(task_t *task, enum pid_type type) { int nr = __detach_pid(task, type); @@ -268,6 +270,9 @@ void switch_exec_pids(task_t *leader, ta * machine. From a minimum of 16 slots up to 4096 slots at one gigabyte or * more. */ +#ifdef CONFIG_KGDB +int kgdb_pid_init_done; /* so we don't call prior to... */ +#endif void __init pidhash_init(void) { int i, j, pidhash_size; @@ -289,6 +294,9 @@ void __init pidhash_init(void) for (j = 0; j < pidhash_size; j++) INIT_LIST_HEAD(&pid_hash[i][j]); } +#ifdef CONFIG_KGDB + kgdb_pid_init_done++; +#endif } void __init pidmap_init(void) --- linux-2.6.4-rc2/kernel/printk.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/kernel/printk.c 2004-03-07 20:47:23.000000000 -0800 @@ -522,7 +522,7 @@ asmlinkage int printk(const char *fmt, . log_level_unknown = 1; } - if (!cpu_online(smp_processor_id())) { + if (!cpu_online(smp_processor_id()) && !system_running) { /* * Some console drivers may assume that per-cpu resources have * been allocated. So don't allow them to be called by this --- linux-2.6.4-rc2/kernel/rcupdate.c 2003-10-08 15:07:10.000000000 -0700 +++ 25/kernel/rcupdate.c 2004-03-07 20:47:26.000000000 -0800 @@ -66,7 +66,7 @@ static DEFINE_PER_CPU(struct tasklet_str * The read-side of critical section that use call_rcu() for updation must * be protected by rcu_read_lock()/rcu_read_unlock(). */ -void call_rcu(struct rcu_head *head, void (*func)(void *arg), void *arg) +void fastcall call_rcu(struct rcu_head *head, void (*func)(void *arg), void *arg) { int cpu; unsigned long flags; @@ -110,6 +110,7 @@ static void rcu_start_batch(long newbatc !cpus_empty(rcu_ctrlblk.rcu_cpu_mask)) { return; } + /* Can't change, since spin lock held. */ rcu_ctrlblk.rcu_cpu_mask = cpu_online_map; } @@ -154,6 +155,60 @@ out_unlock: } +#ifdef CONFIG_HOTPLUG_CPU + +/* warning! helper for rcu_offline_cpu. do not use elsewhere without reviewing + * locking requirements, the list it's pulling from has to belong to a cpu + * which is dead and hence not processing interrupts. + */ +static void rcu_move_batch(struct list_head *list) +{ + struct list_head *entry; + int cpu = smp_processor_id(); + + local_irq_disable(); + while (!list_empty(list)) { + entry = list->next; + list_del(entry); + list_add_tail(entry, &RCU_nxtlist(cpu)); + } + local_irq_enable(); +} + +static void rcu_offline_cpu(int cpu) +{ + /* if the cpu going offline owns the grace period + * we can block indefinitely waiting for it, so flush + * it here + */ + spin_lock_irq(&rcu_ctrlblk.mutex); + if (cpus_empty(rcu_ctrlblk.rcu_cpu_mask)) + goto unlock; + + cpu_clear(cpu, rcu_ctrlblk.rcu_cpu_mask); + if (cpus_empty(rcu_ctrlblk.rcu_cpu_mask)) { + rcu_ctrlblk.curbatch++; + /* We may avoid calling start batch if + * we are starting the batch only + * because of the DEAD CPU (the current + * CPU will start a new batch anyway for + * the callbacks we will move to current CPU). + * However, we will avoid this optimisation + * for now. + */ + rcu_start_batch(rcu_ctrlblk.maxbatch); + } +unlock: + spin_unlock_irq(&rcu_ctrlblk.mutex); + + rcu_move_batch(&RCU_curlist(cpu)); + rcu_move_batch(&RCU_nxtlist(cpu)); + + tasklet_kill_immediate(&RCU_tasklet(cpu), cpu); +} + +#endif + /* * This does the RCU processing work from tasklet context. */ @@ -214,7 +269,11 @@ static int __devinit rcu_cpu_notify(stru case CPU_UP_PREPARE: rcu_online_cpu(cpu); break; - /* Space reserved for CPU_OFFLINE :) */ +#ifdef CONFIG_HOTPLUG_CPU + case CPU_DEAD: + rcu_offline_cpu(cpu); + break; +#endif default: break; } --- linux-2.6.4-rc2/kernel/sched.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/kernel/sched.c 2004-03-07 20:47:38.000000000 -0800 @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -90,7 +92,6 @@ #define MAX_SLEEP_AVG (AVG_TIMESLICE * MAX_BONUS) #define STARVATION_LIMIT (MAX_SLEEP_AVG) #define NS_MAX_SLEEP_AVG (JIFFIES_TO_NS(MAX_SLEEP_AVG)) -#define NODE_THRESHOLD 125 #define CREDIT_LIMIT 100 /* @@ -186,7 +187,7 @@ static inline unsigned int task_timeslic typedef struct runqueue runqueue_t; struct prio_array { - int nr_active; + unsigned int nr_active; unsigned long bitmap[BITMAP_SIZE]; struct list_head queue[MAX_PRIO]; }; @@ -201,23 +202,32 @@ struct prio_array { struct runqueue { spinlock_t lock; unsigned long nr_running, nr_switches, expired_timestamp, - nr_uninterruptible, timestamp_last_tick; + nr_uninterruptible; + unsigned long long timestamp_last_tick; task_t *curr, *idle; struct mm_struct *prev_mm; prio_array_t *active, *expired, arrays[2]; - int best_expired_prio, prev_cpu_load[NR_CPUS]; -#ifdef CONFIG_NUMA - atomic_t *node_nr_running; - int prev_node_load[MAX_NUMNODES]; + int best_expired_prio; + int cpu; + atomic_t nr_iowait; +#ifdef CONFIG_SMP + unsigned long cpu_load[NR_CPUS]; #endif + /* For active balancing */ + int active_balance; + int push_cpu; + task_t *migration_thread; struct list_head migration_queue; - - atomic_t nr_iowait; }; static DEFINE_PER_CPU(struct runqueue, runqueues); +#ifdef CONFIG_SMP +/* Mandatory scheduling domains */ +DEFINE_PER_CPU(struct sched_domain, base_domains); +#endif + #define cpu_rq(cpu) (&per_cpu(runqueues, (cpu))) #define this_rq() (&__get_cpu_var(runqueues)) #define task_rq(p) cpu_rq(task_cpu(p)) @@ -232,51 +242,16 @@ static DEFINE_PER_CPU(struct runqueue, r # define task_running(rq, p) ((rq)->curr == (p)) #endif -#ifdef CONFIG_NUMA - -/* - * Keep track of running tasks. - */ - -static atomic_t node_nr_running[MAX_NUMNODES] ____cacheline_maxaligned_in_smp = - {[0 ...MAX_NUMNODES-1] = ATOMIC_INIT(0)}; - -static inline void nr_running_init(struct runqueue *rq) -{ - rq->node_nr_running = &node_nr_running[0]; -} - static inline void nr_running_inc(runqueue_t *rq) { - atomic_inc(rq->node_nr_running); rq->nr_running++; } static inline void nr_running_dec(runqueue_t *rq) { - atomic_dec(rq->node_nr_running); rq->nr_running--; } -__init void node_nr_running_init(void) -{ - int i; - - for (i = 0; i < NR_CPUS; i++) { - if (cpu_possible(i)) - cpu_rq(i)->node_nr_running = - &node_nr_running[cpu_to_node(i)]; - } -} - -#else /* !CONFIG_NUMA */ - -# define nr_running_init(rq) do { } while (0) -# define nr_running_inc(rq) do { (rq)->nr_running++; } while (0) -# define nr_running_dec(rq) do { (rq)->nr_running--; } while (0) - -#endif /* CONFIG_NUMA */ - /* * task_rq_lock - lock the runqueue a given task resides on and disable * interrupts. Note the ordering: we can safely lookup the task_rq without @@ -544,37 +519,30 @@ inline int task_curr(task_t *p) typedef struct { struct list_head list; task_t *task; + int dest_cpu; struct completion done; } migration_req_t; /* - * The task's runqueue lock must be held, and the new mask must be valid. + * The task's runqueue lock must be held. * Returns true if you have to wait for migration thread. */ -static int __set_cpus_allowed(task_t *p, cpumask_t new_mask, - migration_req_t *req) +static int migrate_task(task_t *p, int dest_cpu, migration_req_t *req) { runqueue_t *rq = task_rq(p); - p->cpus_allowed = new_mask; - /* - * Can the task run on the task's current CPU? If not then - * migrate the thread off to a proper CPU. - */ - if (cpu_isset(task_cpu(p), new_mask)) - return 0; - /* * If the task is not on a runqueue (and not running), then * it is sufficient to simply update the task's cpu field. */ if (!p->array && !task_running(rq, p)) { - set_task_cpu(p, any_online_cpu(p->cpus_allowed)); + set_task_cpu(p, dest_cpu); return 0; } init_completion(&req->done); req->task = p; + req->dest_cpu = dest_cpu; list_add(&req->list, &rq->migration_queue); return 1; } @@ -635,9 +603,78 @@ void kick_process(task_t *p) } EXPORT_SYMBOL_GPL(kick_process); +/* + * Return a low guess at the load of cpu. Update previous history if update + * is true + */ +static inline unsigned long get_low_cpu_load(int cpu, int update) +{ + runqueue_t *rq = cpu_rq(cpu); + runqueue_t *this_rq = this_rq(); + unsigned long nr = rq->nr_running << SCHED_LOAD_SHIFT; + unsigned long load = this_rq->cpu_load[cpu]; + unsigned long ret = min(nr, load); + + if (update) + this_rq->cpu_load[cpu] = (nr + load) / 2; + + return ret; +} + +static inline unsigned long get_high_cpu_load(int cpu, int update) +{ + runqueue_t *rq = cpu_rq(cpu); + runqueue_t *this_rq = this_rq(); + unsigned long nr = rq->nr_running << SCHED_LOAD_SHIFT; + unsigned long load = this_rq->cpu_load[cpu]; + unsigned long ret = max(nr, load); + + if (update) + this_rq->cpu_load[cpu] = (nr + load) / 2; + + return ret; +} #endif +/* + * sched_balance_wake can be used with SMT architectures to wake a + * task onto an idle sibling if cpu is not idle. Returns cpu if + * cpu is idle or no siblings are idle, otherwise returns an idle + * sibling. + */ +#if defined(CONFIG_SMP) && defined(ARCH_HAS_SCHED_WAKE_BALANCE) +static int sched_balance_wake(int cpu, task_t *p) +{ + cpumask_t tmp; + struct sched_domain *domain; + int i; + + if (idle_cpu(cpu)) + return cpu; + + domain = cpu_sched_domain(cpu); + if (!(domain->flags & SD_FLAG_WAKE)) + return cpu; + + cpus_and(tmp, domain->span, cpu_online_map); + for_each_cpu_mask(i, tmp) { + if (!cpu_isset(i, p->cpus_allowed)) + continue; + + if (idle_cpu(i)) + return i; + } + + return cpu; +} +#else +static inline int sched_balance_wake(int cpu, task_t *p) +{ + return cpu; +} +#endif + /*** * try_to_wake_up - wake up a thread * @p: the to-be-woken-up thread @@ -658,49 +695,122 @@ static int try_to_wake_up(task_t * p, un int success = 0; long old_state; runqueue_t *rq; + int cpu, this_cpu; +#ifdef CONFIG_SMP + unsigned long long now; + unsigned long load, this_load; + int new_cpu; + struct sched_domain *sd; + runqueue_t *this_rq; +#endif -repeat_lock_task: rq = task_rq_lock(p, &flags); old_state = p->state; - if (old_state & state) { - if (!p->array) { - /* - * Fast-migrate the task if it's not running or runnable - * currently. Do not violate hard affinity. - */ - if (unlikely(sync && !task_running(rq, p) && - (task_cpu(p) != smp_processor_id()) && - cpu_isset(smp_processor_id(), - p->cpus_allowed))) { - - set_task_cpu(p, smp_processor_id()); - task_rq_unlock(rq, &flags); - goto repeat_lock_task; - } - if (old_state == TASK_UNINTERRUPTIBLE) { - rq->nr_uninterruptible--; - /* - * Tasks on involuntary sleep don't earn - * sleep_avg beyond just interactive state. - */ - p->activated = -1; - } - if (sync && (task_cpu(p) == smp_processor_id())) - __activate_task(p, rq); - else { - activate_task(p, rq); - if (TASK_PREEMPTS_CURR(p, rq)) - resched_task(rq->curr); - } - success = 1; + if (!(old_state & state)) + goto out; + + if (p->array) + goto out_running; + + this_cpu = smp_processor_id(); + cpu = task_cpu(p); + +#ifdef CONFIG_SMP + if (cpu == this_cpu) + goto out_activate; + + if (unlikely(!cpu_isset(this_cpu, p->cpus_allowed) + || task_running(rq, p) + || cpu_is_offline(this_cpu))) + goto out_activate; + + /* Passive load balancing */ + load = get_low_cpu_load(cpu, 1); + this_load = get_high_cpu_load(this_cpu, 1) + SCHED_LOAD_SCALE; + if (load > this_load) { + new_cpu = sched_balance_wake(this_cpu, p); + set_task_cpu(p, new_cpu); + goto repeat_lock_task; + } + + this_rq = this_rq(); + now = sched_clock(); + sd = cpu_sched_domain(this_cpu); + + /* + * Fast-migrate the task if it's not running or + * runnable currently. Do not violate hard affinity. + */ + do { + if (!(sd->flags & SD_FLAG_FASTMIGRATE)) + break; + if (now - p->timestamp < sd->cache_hot_time) + break; + + if (cpu_isset(cpu, sd->span)) { + new_cpu = sched_balance_wake(this_cpu, p); + set_task_cpu(p, new_cpu); + goto repeat_lock_task; } - p->state = TASK_RUNNING; + sd = sd->parent; + } while (sd); + + new_cpu = sched_balance_wake(cpu, p); + if (new_cpu != cpu) { + set_task_cpu(p, new_cpu); + goto repeat_lock_task; + } + goto out_activate; + + if ((p->flags & PF_FUTEX_DEBUG) + && !(current->flags & PF_FUTEX_DEBUG)) { + printk("%s %i waking %s: %i %i\n", + current->comm, (int)in_interrupt(), + p->comm, p->tgid, p->pid); + WARN_ON(1); } + +repeat_lock_task: + task_rq_unlock(rq, &flags); + rq = task_rq_lock(p, &flags); + old_state = p->state; + if (!(old_state & state)) + goto out; + + if (p->array) + goto out_running; + + this_cpu = smp_processor_id(); + cpu = task_cpu(p); + +out_activate: +#endif /* CONFIG_SMP */ + if (old_state == TASK_UNINTERRUPTIBLE) { + rq->nr_uninterruptible--; + /* + * Tasks on involuntary sleep don't earn + * sleep_avg beyond just interactive state. + */ + p->activated = -1; + } + + if (sync && cpu == this_cpu) { + __activate_task(p, rq); + } else { + activate_task(p, rq); + if (TASK_PREEMPTS_CURR(p, rq)) + resched_task(rq->curr); + } + success = 1; + +out_running: + p->state = TASK_RUNNING; +out: task_rq_unlock(rq, &flags); return success; } -int wake_up_process(task_t * p) +int fastcall wake_up_process(task_t * p) { return try_to_wake_up(p, TASK_STOPPED | TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE, 0); @@ -708,7 +818,7 @@ int wake_up_process(task_t * p) EXPORT_SYMBOL(wake_up_process); -int wake_up_state(task_t *p, unsigned int state) +int fastcall wake_up_state(task_t *p, unsigned int state) { return try_to_wake_up(p, state, 0); } @@ -717,7 +827,7 @@ int wake_up_state(task_t *p, unsigned in * Perform scheduler related setup for a newly forked process p. * p is forked by current. */ -void sched_fork(task_t *p) +void fastcall sched_fork(task_t *p) { /* * We mark the process as running here, but have not actually @@ -754,8 +864,8 @@ void sched_fork(task_t *p) p->timestamp = sched_clock(); if (!current->time_slice) { /* - * This case is rare, it happens when the parent has only - * a single jiffy left from its timeslice. Taking the + * This case is rare, it happens when the parent has only + * a single jiffy left from its timeslice. Taking the * runqueue lock is not a problem. */ current->time_slice = 1; @@ -773,7 +883,7 @@ void sched_fork(task_t *p) * This function will do some initial scheduler statistics housekeeping * that must be done for every newly created process. */ -void wake_up_forked_process(task_t * p) +void fastcall wake_up_forked_process(task_t * p) { unsigned long flags; runqueue_t *rq = task_rq_lock(current, &flags); @@ -817,7 +927,7 @@ void wake_up_forked_process(task_t * p) * artificially, because any timeslice recovered here * was given away by the parent in the first place.) */ -void sched_exit(task_t * p) +void fastcall sched_exit(task_t * p) { unsigned long flags; runqueue_t *rq; @@ -871,7 +981,7 @@ static inline void finish_task_switch(ta * still held, otherwise prev could be scheduled on another cpu, die * there before we look at prev->state, and then the reference would * be dropped twice. - * Manfred Spraul + * Manfred Spraul */ prev_task_flags = prev->flags; finish_arch_switch(rq, prev); @@ -933,7 +1043,7 @@ unsigned long nr_running(void) { unsigned long i, sum = 0; - for (i = 0; i < NR_CPUS; i++) + for_each_cpu(i) sum += cpu_rq(i)->nr_running; return sum; @@ -1003,40 +1113,44 @@ static inline void double_rq_unlock(runq spin_unlock(&rq2->lock); } +enum idle_type +{ + IDLE, + NOT_IDLE, + NEWLY_IDLE, +}; + +#ifdef CONFIG_SMP + #ifdef CONFIG_NUMA /* - * If dest_cpu is allowed for this process, migrate the task to it. - * This is accomplished by forcing the cpu_allowed mask to only - * allow dest_cpu, which will force the cpu onto dest_cpu. Then - * the cpu_allowed mask is restored. + * If dest_cpu is allowed for this process, migrate us to it. */ -static void sched_migrate_task(task_t *p, int dest_cpu) +void migrate_to_cpu(int dest_cpu) { runqueue_t *rq; migration_req_t req; unsigned long flags; - cpumask_t old_mask, new_mask = cpumask_of_cpu(dest_cpu); - rq = task_rq_lock(p, &flags); - old_mask = p->cpus_allowed; - if (!cpu_isset(dest_cpu, old_mask) || !cpu_online(dest_cpu)) + rq = task_rq_lock(current, &flags); + if (!cpu_isset(dest_cpu, current->cpus_allowed)) goto out; /* force the process onto the specified CPU */ - if (__set_cpus_allowed(p, new_mask, &req)) { + if (migrate_task(current, dest_cpu, &req)) { /* Need to wait for migration thread. */ task_rq_unlock(rq, &flags); wake_up_process(rq->migration_thread); wait_for_completion(&req.done); - /* If we raced with sys_sched_setaffinity, don't - * restore mask. */ - rq = task_rq_lock(p, &flags); - if (likely(cpus_equal(p->cpus_allowed, new_mask))) { - /* Restore old mask: won't need migration - * thread, since current cpu is allowed. */ - BUG_ON(__set_cpus_allowed(p, old_mask, NULL)); - } + /* + * we want a new context here. This eliminates TLB + * flushes on the cpus where the process executed prior to + * the migration. + */ + tlb_migrate_prepare(current->mm); + + return; } out: task_rq_unlock(rq, &flags); @@ -1046,215 +1160,81 @@ out: * Find the least loaded CPU. Slightly favor the current CPU by * setting its runqueue length as the minimum to start. */ -static int sched_best_cpu(struct task_struct *p) +static int sched_best_cpu(struct task_struct *p, struct sched_domain *domain) { - int i, minload, load, best_cpu, node = 0; - cpumask_t cpumask; + cpumask_t tmp; + int i, min_load, this_cpu, best_cpu; - best_cpu = task_cpu(p); - if (cpu_rq(best_cpu)->nr_running <= 2) - return best_cpu; + best_cpu = this_cpu = task_cpu(p); + min_load = INT_MAX; - minload = 10000000; - for_each_node_with_cpus(i) { - /* - * Node load is always divided by nr_cpus_node to normalise - * load values in case cpu count differs from node to node. - * We first multiply node_nr_running by 10 to get a little - * better resolution. - */ - load = 10 * atomic_read(&node_nr_running[i]) / nr_cpus_node(i); - if (load < minload) { - minload = load; - node = i; - } - } + cpus_and(tmp, domain->span, cpu_online_map); + for_each_cpu_mask(i, tmp) { + unsigned long load; + if (i == this_cpu) + load = get_low_cpu_load(i, 0); + else + load = get_high_cpu_load(i, 0) + SCHED_LOAD_SCALE; - minload = 10000000; - cpumask = node_to_cpumask(node); - for (i = 0; i < NR_CPUS; ++i) { - if (!cpu_isset(i, cpumask)) - continue; - if (cpu_rq(i)->nr_running < minload) { + if (min_load > load) { best_cpu = i; - minload = cpu_rq(i)->nr_running; + min_load = load; } + } return best_cpu; } void sched_balance_exec(void) { + struct sched_domain *domain = this_sched_domain(); int new_cpu; + int this_cpu = smp_processor_id(); + if (numnodes == 1) + return; - if (numnodes > 1) { - new_cpu = sched_best_cpu(current); - if (new_cpu != smp_processor_id()) - sched_migrate_task(current, new_cpu); - } -} + if (this_rq()->nr_running <= 1) + return; -/* - * Find the busiest node. All previous node loads contribute with a - * geometrically deccaying weight to the load measure: - * load_{t} = load_{t-1}/2 + nr_node_running_{t} - * This way sudden load peaks are flattened out a bit. - * Node load is divided by nr_cpus_node() in order to compare nodes - * of different cpu count but also [first] multiplied by 10 to - * provide better resolution. - */ -static int find_busiest_node(int this_node) -{ - int i, node = -1, load, this_load, maxload; - - if (!nr_cpus_node(this_node)) - return node; - this_load = maxload = (this_rq()->prev_node_load[this_node] >> 1) - + (10 * atomic_read(&node_nr_running[this_node]) - / nr_cpus_node(this_node)); - this_rq()->prev_node_load[this_node] = this_load; - for_each_node_with_cpus(i) { - if (i == this_node) - continue; - load = (this_rq()->prev_node_load[i] >> 1) - + (10 * atomic_read(&node_nr_running[i]) - / nr_cpus_node(i)); - this_rq()->prev_node_load[i] = load; - if (load > maxload && (100*load > NODE_THRESHOLD*this_load)) { - maxload = load; - node = i; - } + while (domain->parent && !(domain->flags & SD_FLAG_EXEC)) + domain = domain->parent; + + if (domain->flags & SD_FLAG_EXEC) { + new_cpu = sched_best_cpu(current, domain); + if (new_cpu != this_cpu) + migrate_to_cpu(new_cpu); } - return node; } - #endif /* CONFIG_NUMA */ -#ifdef CONFIG_SMP - /* - * double_lock_balance - lock the busiest runqueue - * - * this_rq is locked already. Recalculate nr_running if we have to - * drop the runqueue lock. + * double_lock_balance - lock the busiest runqueue, this_rq is locked already. */ -static inline -unsigned int double_lock_balance(runqueue_t *this_rq, runqueue_t *busiest, - int this_cpu, int idle, - unsigned int nr_running) +static inline void double_lock_balance(runqueue_t *this_rq, runqueue_t *busiest) { if (unlikely(!spin_trylock(&busiest->lock))) { if (busiest < this_rq) { spin_unlock(&this_rq->lock); spin_lock(&busiest->lock); spin_lock(&this_rq->lock); - /* Need to recalculate nr_running */ - if (idle || (this_rq->nr_running > - this_rq->prev_cpu_load[this_cpu])) - nr_running = this_rq->nr_running; - else - nr_running = this_rq->prev_cpu_load[this_cpu]; } else spin_lock(&busiest->lock); } - return nr_running; -} - -/* - * find_busiest_queue - find the busiest runqueue among the cpus in cpumask. - */ -static inline -runqueue_t *find_busiest_queue(runqueue_t *this_rq, int this_cpu, int idle, - int *imbalance, cpumask_t cpumask) -{ - int nr_running, load, max_load, i; - runqueue_t *busiest, *rq_src; - - /* - * We search all runqueues to find the most busy one. - * We do this lockless to reduce cache-bouncing overhead, - * we re-check the 'best' source CPU later on again, with - * the lock held. - * - * We fend off statistical fluctuations in runqueue lengths by - * saving the runqueue length (as seen by the balancing CPU) during - * the previous load-balancing operation and using the smaller one - * of the current and saved lengths. If a runqueue is long enough - * for a longer amount of time then we recognize it and pull tasks - * from it. - * - * The 'current runqueue length' is a statistical maximum variable, - * for that one we take the longer one - to avoid fluctuations in - * the other direction. So for a load-balance to happen it needs - * stable long runqueue on the target CPU and stable short runqueue - * on the local runqueue. - * - * We make an exception if this CPU is about to become idle - in - * that case we are less picky about moving a task across CPUs and - * take what can be taken. - */ - if (idle || (this_rq->nr_running > this_rq->prev_cpu_load[this_cpu])) - nr_running = this_rq->nr_running; - else - nr_running = this_rq->prev_cpu_load[this_cpu]; - - busiest = NULL; - max_load = 1; - for (i = 0; i < NR_CPUS; i++) { - if (!cpu_isset(i, cpumask)) - continue; - - rq_src = cpu_rq(i); - if (idle || (rq_src->nr_running < this_rq->prev_cpu_load[i])) - load = rq_src->nr_running; - else - load = this_rq->prev_cpu_load[i]; - this_rq->prev_cpu_load[i] = rq_src->nr_running; - - if ((load > max_load) && (rq_src != this_rq)) { - busiest = rq_src; - max_load = load; - } - } - - if (likely(!busiest)) - goto out; - - *imbalance = max_load - nr_running; - - /* It needs an at least ~25% imbalance to trigger balancing. */ - if (!idle && ((*imbalance)*4 < max_load)) { - busiest = NULL; - goto out; - } - - nr_running = double_lock_balance(this_rq, busiest, this_cpu, - idle, nr_running); - /* - * Make sure nothing changed since we checked the - * runqueue length. - */ - if (busiest->nr_running <= nr_running) { - spin_unlock(&busiest->lock); - busiest = NULL; - } -out: - return busiest; } /* * pull_task - move a task from a remote runqueue to the local runqueue. * Both runqueues must be locked. */ -static inline -void pull_task(runqueue_t *src_rq, prio_array_t *src_array, task_t *p, - runqueue_t *this_rq, int this_cpu) +static inline void pull_task(runqueue_t *src_rq, prio_array_t *src_array, + task_t *p, runqueue_t *this_rq, prio_array_t *this_array, + int this_cpu) { dequeue_task(p, src_array); nr_running_dec(src_rq); set_task_cpu(p, this_cpu); nr_running_inc(this_rq); - enqueue_task(p, this_rq->active); + enqueue_task(p, this_array); p->timestamp = sched_clock() - (src_rq->timestamp_last_tick - p->timestamp); /* @@ -1262,69 +1242,71 @@ void pull_task(runqueue_t *src_rq, prio_ * to be always true for them. */ if (TASK_PREEMPTS_CURR(p, this_rq)) - set_need_resched(); + resched_task(this_rq->curr); } /* * can_migrate_task - may task p from runqueue rq be migrated to this_cpu? */ static inline -int can_migrate_task(task_t *tsk, runqueue_t *rq, int this_cpu, int idle) +int can_migrate_task(task_t *p, runqueue_t *rq, int this_cpu, + struct sched_domain *domain, enum idle_type idle) { - unsigned long delta = rq->timestamp_last_tick - tsk->timestamp; - /* * We do not migrate tasks that are: * 1) running (obviously), or * 2) cannot be migrated to this CPU due to cpus_allowed, or * 3) are cache-hot on their current CPU. */ - if (task_running(rq, tsk)) - return 0; - if (!cpu_isset(this_cpu, tsk->cpus_allowed)) + if (task_running(rq, p)) return 0; - if (!idle && (delta <= JIFFIES_TO_NS(cache_decay_ticks))) + if (!cpu_isset(this_cpu, p->cpus_allowed)) return 0; + + /* Aggressive migration if we've failed balancing */ + if (idle == NEWLY_IDLE || + domain->nr_balance_failed < domain->cache_nice_tries) { + if ((rq->timestamp_last_tick - p->timestamp) + < domain->cache_hot_time) + return 0; + } + return 1; } /* - * Current runqueue is empty, or rebalance tick: if there is an - * inbalance (current runqueue is too short) then pull from - * busiest runqueue(s). - * - * We call this with the current runqueue locked, - * irqs disabled. - */ -static void load_balance(runqueue_t *this_rq, int idle, cpumask_t cpumask) + * move_tasks tries to move up to max_nr_move tasks from busiest to this_rq, + * as part of a balancing operation within "domain". Returns the number of + * tasks moved. + * + * Called with both runqueues locked. + */ +static int move_tasks(runqueue_t *this_rq, int this_cpu, runqueue_t *busiest, + unsigned long max_nr_move, struct sched_domain *domain, + enum idle_type idle) { - int imbalance, idx, this_cpu = smp_processor_id(); - runqueue_t *busiest; - prio_array_t *array; + int idx; + int pulled = 0; + prio_array_t *array, *dst_array; struct list_head *head, *curr; task_t *tmp; - busiest = find_busiest_queue(this_rq, this_cpu, idle, - &imbalance, cpumask); - if (!busiest) + if (max_nr_move <= 0 || busiest->nr_running <= 1) goto out; /* - * We only want to steal a number of tasks equal to 1/2 the imbalance, - * otherwise we'll just shift the imbalance to the new queue: - */ - imbalance /= 2; - - /* * We first consider expired tasks. Those will likely not be * executed in the near future, and they are most likely to * be cache-cold, thus switching CPUs has the least effect * on them. */ - if (busiest->expired->nr_active) + if (busiest->expired->nr_active) { array = busiest->expired; - else + dst_array = this_rq->expired; + } else { array = busiest->active; + dst_array = this_rq->active; + } new_array: /* Start searching at priority 0: */ @@ -1337,9 +1319,10 @@ skip_bitmap: if (idx >= MAX_PRIO) { if (array == busiest->expired) { array = busiest->active; + dst_array = this_rq->active; goto new_array; } - goto out_unlock; + goto out; } head = array->queue + idx; @@ -1349,104 +1332,478 @@ skip_queue: curr = curr->prev; - if (!can_migrate_task(tmp, busiest, this_cpu, idle)) { + if (!can_migrate_task(tmp, busiest, this_cpu, domain, idle)) { if (curr != head) goto skip_queue; idx++; goto skip_bitmap; } - pull_task(busiest, array, tmp, this_rq, this_cpu); + pull_task(busiest, array, tmp, this_rq, dst_array, this_cpu); + pulled++; - /* Only migrate one task if we are idle */ - if (!idle && --imbalance) { + /* We only want to steal up to the prescribed number of tasks. */ + if (pulled < max_nr_move) { if (curr != head) goto skip_queue; idx++; goto skip_bitmap; } -out_unlock: - spin_unlock(&busiest->lock); out: - ; + return pulled; +} + +/* + * find_busiest_group finds and returns the busiest CPU group within the + * domain. It calculates and returns the number of tasks which should be + * moved to restore balance via the imbalance parameter. + */ +static struct sched_group * +find_busiest_group(struct sched_domain *domain, int this_cpu, + unsigned long *imbalance, enum idle_type idle) +{ + unsigned long max_load, avg_load, total_load, this_load; + unsigned int total_pwr; + int modify; + struct sched_group *busiest = NULL, *this = NULL, *group = domain->groups; + + max_load = 0; + this_load = 0; + total_load = 0; + total_pwr = 0; + + if (group == NULL) + goto out_balanced; + + /* + * Don't modify when we newly become idle because that ruins our + * statistics: its triggered by some value of nr_running (ie. 0). + * Timer based balancing is a good statistic though. + */ + if (idle == NEWLY_IDLE) + modify = 0; + else + modify = 1; + + do { + cpumask_t tmp; + unsigned long load; + int local_group; + int i, nr_cpus = 0; + + local_group = cpu_isset(this_cpu, group->cpumask); + + /* Tally up the load of all CPUs in the group */ + avg_load = 0; + cpus_and(tmp, group->cpumask, cpu_online_map); + for_each_cpu_mask(i, tmp) { + /* Bias balancing toward cpus of our domain */ + if (local_group) { + load = get_high_cpu_load(i, modify); + } else + load = get_low_cpu_load(i, modify); + + nr_cpus++; + avg_load += load; + } + + if (!nr_cpus) + goto nextgroup; + + total_load += avg_load; + total_pwr += group->cpu_power; + + /* Adjust by relative CPU power of the group */ + avg_load = (avg_load << SCHED_LOAD_SHIFT) / group->cpu_power; + + if (local_group) { + this_load = avg_load; + this = group; + goto nextgroup; + } + if (avg_load > max_load) { + max_load = avg_load; + busiest = group; + } +nextgroup: + group = group->next; + } while (group != domain->groups); + + if (!busiest || this_load >= max_load) + goto out_balanced; + + avg_load = (SCHED_LOAD_SCALE * total_load) / total_pwr; + + if (idle == NOT_IDLE) { + if (this_load >= avg_load || + 100*max_load <= domain->imbalance_pct*this_load) + goto out_balanced; + } + + /* + * We're trying to get all the cpus to the average_load, so we don't + * want to push ourselves above the average load, nor do we wish to + * reduce the max loaded cpu below the average load, as either of these + * actions would just result in more rebalancing later, and ping-pong + * tasks around. Thus we look for the minimum possible imbalance. + * Negative imbalances (*we* are more loaded than anyone else) will + * be counted as no imbalance for these purposes -- we can't fix that + * by pulling tasks to us. Be careful of negative numbers as they'll + * appear as very large values with unsigned longs. + */ + *imbalance = (min(max_load - avg_load, avg_load - this_load) + 1) / 2; + + if (*imbalance <= SCHED_LOAD_SCALE/2) { + unsigned long pwr_now = 0, pwr_move = 0; + unsigned long tmp; + + /* + * OK, we don't have enough imbalance to justify moving tasks, + * however we may be able to increase total CPU power used by + * moving them. + */ + + pwr_now += busiest->cpu_power*min(SCHED_LOAD_SCALE, max_load); + pwr_now += this->cpu_power*min(SCHED_LOAD_SCALE, this_load); + pwr_now >>= SCHED_LOAD_SHIFT; + + /* Amount of load we'd subtract */ + tmp = SCHED_LOAD_SCALE*SCHED_LOAD_SCALE/busiest->cpu_power; + if (max_load > tmp) + pwr_move += busiest->cpu_power*min(SCHED_LOAD_SCALE, + max_load - tmp); + + /* Amount of load we'd add */ + tmp = SCHED_LOAD_SCALE*SCHED_LOAD_SCALE/this->cpu_power; + pwr_move += this->cpu_power*min(this->cpu_power, this_load + tmp); + pwr_move >>= SCHED_LOAD_SHIFT; + + /* Move if we gain another 8th of a CPU worth of throughput */ + if (pwr_move < pwr_now + SCHED_LOAD_SCALE / 8) + goto out_balanced; + *imbalance = 1; + return busiest; + } + + /* How many tasks to actually move to equalise the imbalance */ + *imbalance = (*imbalance * min(busiest->cpu_power, this->cpu_power)) + >> SCHED_LOAD_SHIFT; + /* Get rid of the scaling factor, rounding *up* as we divide */ + *imbalance = (*imbalance + SCHED_LOAD_SCALE/2) >> SCHED_LOAD_SHIFT; + + return busiest; + +out_balanced: + if (busiest && idle == NEWLY_IDLE) { + *imbalance = 1; + return busiest; + } + + *imbalance = 0; + return NULL; } /* - * One of the idle_cpu_tick() and busy_cpu_tick() functions will - * get called every timer tick, on every CPU. Our balancing action - * frequency and balancing agressivity depends on whether the CPU is - * idle or not. + * find_busiest_queue - find the busiest runqueue among the cpus in group. + */ +static runqueue_t *find_busiest_queue(struct sched_group *group) +{ + cpumask_t tmp; + int i; + unsigned long max_load = 0; + runqueue_t *busiest = NULL; + + cpus_and(tmp, group->cpumask, cpu_online_map); + for_each_cpu_mask(i, tmp) { + unsigned long load; + + load = get_low_cpu_load(i, 0); + + if (load >= max_load) { + max_load = load; + busiest = cpu_rq(i); + } + } + + return busiest; +} + +/* + * Check this_cpu to ensure it is balanced within domain. Attempt to move + * tasks if there is an imbalance. * - * busy-rebalance every 200 msecs. idle-rebalance every 1 msec. (or on - * systems with HZ=100, every 10 msecs.) + * Called with this_rq unlocked. + */ +static int load_balance(int this_cpu, runqueue_t *this_rq, + struct sched_domain *domain, enum idle_type idle) +{ + struct sched_group *group; + runqueue_t *busiest = NULL; + unsigned long imbalance; + int balanced = 0, failed = 0; + int nr_moved = 0; + + spin_lock(&this_rq->lock); + + group = find_busiest_group(domain, this_cpu, &imbalance, idle); + if (!group) { + balanced = 1; + goto out; + } + + busiest = find_busiest_queue(group); + if (!busiest || busiest == this_rq) { + balanced = 1; + goto out; + } + + /* Attempt to move tasks */ + double_lock_balance(this_rq, busiest); + + nr_moved = move_tasks(this_rq, this_cpu, busiest, + imbalance, domain, idle); + spin_unlock(&busiest->lock); +out: + spin_unlock(&this_rq->lock); + + if (!balanced && nr_moved == 0) + failed = 1; + + if (failed && busiest && + domain->nr_balance_failed > domain->cache_nice_tries) { + int wake = 0; + + spin_lock(&busiest->lock); + if (!busiest->active_balance) { + busiest->active_balance = 1; + busiest->push_cpu = this_cpu; + wake = 1; + } + spin_unlock(&busiest->lock); + if (wake) + wake_up_process(busiest->migration_thread); + } + + if (failed) + domain->nr_balance_failed++; + else + domain->nr_balance_failed = 0; + + if (balanced) { + if (domain->balance_interval < domain->max_interval) + domain->balance_interval *= 2; + } else { + domain->balance_interval = domain->min_interval; + } + + return nr_moved; +} + +/* + * Check this_cpu to ensure it is balanced within domain. Attempt to move + * tasks if there is an imbalance. * - * On NUMA, do a node-rebalance every 400 msecs. + * Called from schedule when this_rq is about to become idle (NEWLY_IDLE). + * this_rq is locked. */ -#define IDLE_REBALANCE_TICK (HZ/1000 ?: 1) -#define BUSY_REBALANCE_TICK (HZ/5 ?: 1) -#define IDLE_NODE_REBALANCE_TICK (IDLE_REBALANCE_TICK * 5) -#define BUSY_NODE_REBALANCE_TICK (BUSY_REBALANCE_TICK * 2) +static int load_balance_newidle(int this_cpu, runqueue_t *this_rq, + struct sched_domain *domain) +{ + struct sched_group *group; + runqueue_t *busiest = NULL; + unsigned long imbalance; + int nr_moved = 0; -#ifdef CONFIG_NUMA -static void balance_node(runqueue_t *this_rq, int idle, int this_cpu) + group = find_busiest_group(domain, this_cpu, &imbalance, NEWLY_IDLE); + if (!group) + goto out; + + busiest = find_busiest_queue(group); + if (!busiest || busiest == this_rq) + goto out; + + /* Attempt to move tasks */ + double_lock_balance(this_rq, busiest); + + nr_moved = move_tasks(this_rq, this_cpu, busiest, + imbalance, domain, NEWLY_IDLE); + + spin_unlock(&busiest->lock); + +out: + return nr_moved; +} + +/* + * idle_balance is called by schedule() if this_cpu is about to become + * idle. Attempts to pull tasks from other CPUs. + */ +static inline void idle_balance(int this_cpu, runqueue_t *this_rq) { - int node = find_busiest_node(cpu_to_node(this_cpu)); + struct sched_domain *domain = this_sched_domain(); - if (node >= 0) { - cpumask_t cpumask = node_to_cpumask(node); - cpu_set(this_cpu, cpumask); - spin_lock(&this_rq->lock); - load_balance(this_rq, idle, cpumask); - spin_unlock(&this_rq->lock); - } + if (cpu_is_offline(this_cpu)) + return; + + do { + if (unlikely(!domain->groups)) + /* hasn't been setup yet */ + break; + + if (domain->flags & SD_FLAG_NEWIDLE) { + if (load_balance_newidle(this_cpu, this_rq, domain)) { + /* We've pulled tasks over so stop searching */ + break; + } + } + + domain = domain->parent; + } while (domain); } -#endif -static void rebalance_tick(runqueue_t *this_rq, int idle) +/* + * active_load_balance is run by migration threads. It pushes a running + * task off the cpu. It can be required to correctly have at least 1 task + * running on each physical CPU where possible, and not have a physical / + * logical imbalance. + * + * Called with busiest locked. + */ +static void active_load_balance(runqueue_t *busiest, int busiest_cpu) { -#ifdef CONFIG_NUMA - int this_cpu = smp_processor_id(); -#endif - unsigned long j = jiffies; + int i; + struct sched_domain *sd = cpu_sched_domain(busiest_cpu); + struct sched_group *group, *busy_group; - /* - * First do inter-node rebalancing, then intra-node rebalancing, - * if both events happen in the same tick. The inter-node - * rebalancing does not necessarily have to create a perfect - * balance within the node, since we load-balance the most loaded - * node with the current CPU. (ie. other CPUs in the local node - * are not balanced.) - */ - if (idle) { -#ifdef CONFIG_NUMA - if (!(j % IDLE_NODE_REBALANCE_TICK)) - balance_node(this_rq, idle, this_cpu); -#endif - if (!(j % IDLE_REBALANCE_TICK)) { - spin_lock(&this_rq->lock); - load_balance(this_rq, idle, cpu_to_node_mask(this_cpu)); - spin_unlock(&this_rq->lock); + if (busiest->nr_running <= 1) + return; + + /* sd->parent should never cause a NULL dereference, if it did so, + * then push_cpu was set to a buggy value */ + while (!cpu_isset(busiest->push_cpu, sd->span)) { + sd = sd->parent; + if (!sd->parent && !cpu_isset(busiest->push_cpu, sd->span)) { + WARN_ON(1); + return; } + } + + if (!sd->groups) { + WARN_ON(1); return; } -#ifdef CONFIG_NUMA - if (!(j % BUSY_NODE_REBALANCE_TICK)) - balance_node(this_rq, idle, this_cpu); -#endif - if (!(j % BUSY_REBALANCE_TICK)) { - spin_lock(&this_rq->lock); - load_balance(this_rq, idle, cpu_to_node_mask(this_cpu)); - spin_unlock(&this_rq->lock); + + group = sd->groups; + while (!cpu_isset(busiest_cpu, group->cpumask)) { + group = group->next; + if (group == sd->groups) { + WARN_ON(1); + return; + } } + busy_group = group; + + group = sd->groups; + do { + cpumask_t tmp; + runqueue_t *rq; + int push_cpu = 0, nr = 0; + + if (group == busy_group) + goto next_group; + + cpus_and(tmp, group->cpumask, cpu_online_map); + for_each_cpu_mask(i, tmp) { + if (!idle_cpu(i)) + goto next_group; + push_cpu = i; + nr++; + } + if (nr == 0) + goto next_group; + + rq = cpu_rq(push_cpu); + double_lock_balance(busiest, rq); + move_tasks(rq, push_cpu, busiest, 1, sd, IDLE); + spin_unlock(&rq->lock); +next_group: + group = group->next; + } while (group != sd->groups); +} + +/* + * rebalance_tick will get called every timer tick, on every CPU. + * + * It checks each scheduling domain to see if it is due to be balanced, + * and initiates a balancing operation if so. + * + * Balancing parameters are set up in arch_init_sched_domains. + */ + +/* Don't have all balancing operations going off at once */ +#define CPU_OFFSET(cpu) (HZ * cpu / NR_CPUS) + +static void rebalance_tick(int this_cpu, runqueue_t *this_rq, enum idle_type idle) +{ + unsigned long j = jiffies + CPU_OFFSET(this_cpu); + struct sched_domain *domain = this_sched_domain(); + + BUG_ON(cpu_is_offline(this_cpu) && system_running); + + /* Run through all this CPU's domains */ + do { + unsigned long interval; + + if (unlikely(!domain->groups)) + break; + + interval = domain->balance_interval; + if (idle != IDLE) + interval *= domain->busy_factor; + + /* scale ms to jiffies */ + interval = interval * HZ / 1000; + if (unlikely(interval == 0)) + interval = 1; + + if (j - domain->last_balance >= interval) { + if (load_balance(this_cpu, this_rq, domain, idle)) { + /* We've pulled tasks over so no longer idle */ + idle = NOT_IDLE; + } + domain->last_balance += interval; + } + + domain = domain->parent; + } while (domain); } #else /* * on UP we do not need to balance between CPUs: */ -static inline void rebalance_tick(runqueue_t *this_rq, int idle) +static inline void rebalance_tick(int this_cpu, runqueue_t *this_rq, enum idle_type idle) { } #endif +#ifdef CONFIG_SCHED_SMT +static inline int wake_priority_sleeper(runqueue_t *rq) +{ /* + * If an SMT sibling task has been put to sleep for priority + * reasons reschedule the idle task to see if it can now run. + */ + if (rq->nr_running) { + resched_task(rq->idle); + return 1; + } + return 0; +} +#else +static inline int wake_priority_sleeper(runqueue_t *rq) +{ + return 0; +} +#endif + DEFINE_PER_CPU(struct kernel_stat, kstat); EXPORT_PER_CPU_SYMBOL(kstat); @@ -1500,7 +1857,9 @@ void scheduler_tick(int user_ticks, int cpustat->iowait += sys_ticks; else cpustat->idle += sys_ticks; - rebalance_tick(rq, 1); + if (wake_priority_sleeper(rq)) + goto out; + rebalance_tick(cpu, rq, IDLE); return; } if (TASK_NICE(p) > 0) @@ -1584,9 +1943,94 @@ void scheduler_tick(int user_ticks, int out_unlock: spin_unlock(&rq->lock); out: - rebalance_tick(rq, 0); + rebalance_tick(cpu, rq, NOT_IDLE); +} + +#ifdef CONFIG_SCHED_SMT +static inline void wake_sleeping_dependent(runqueue_t *rq) +{ + int i, this_cpu = rq->cpu; + struct sched_domain *sd = cpu_sched_domain(this_cpu); + cpumask_t sibling_map; + + if (!(sd->flags & SD_FLAG_SHARE_CPUPOWER)) { + /* Not SMT */ + return; + } + + cpus_and(sibling_map, sd->span, cpu_online_map); + cpu_clear(this_cpu, sibling_map); + for_each_cpu_mask(i, sibling_map) { + runqueue_t *smt_rq; + + smt_rq = cpu_rq(i); + + /* + * If an SMT sibling task is sleeping due to priority + * reasons wake it up now. + */ + if (smt_rq->curr == smt_rq->idle && smt_rq->nr_running) + resched_task(smt_rq->idle); + } } +static inline int dependent_sleeper(runqueue_t *rq, task_t *p) +{ + int ret = 0, i, this_cpu = rq->cpu; + struct sched_domain *sd = cpu_sched_domain(this_cpu); + cpumask_t sibling_map; + + if (!(sd->flags & SD_FLAG_SHARE_CPUPOWER)) { + /* Not SMT */ + return 0; + } + + cpus_and(sibling_map, sd->span, cpu_online_map); + cpu_clear(this_cpu, sibling_map); + for_each_cpu_mask(i, sibling_map) { + runqueue_t *smt_rq; + task_t *smt_curr; + + smt_rq = cpu_rq(i); + smt_curr = smt_rq->curr; + + /* + * If a user task with lower static priority than the + * running task on the SMT sibling is trying to schedule, + * delay it till there is proportionately less timeslice + * left of the sibling task to prevent a lower priority + * task from using an unfair proportion of the + * physical cpu's resources. -ck + */ + if (((smt_curr->time_slice * (100 - sd->per_cpu_gain) / 100) > + task_timeslice(p) || rt_task(smt_curr)) && + p->mm && smt_curr->mm && !rt_task(p)) + ret |= 1; + + /* + * Reschedule a lower priority task on the SMT sibling, + * or wake it up if it has been put to sleep for priority + * reasons. + */ + if ((((p->time_slice * (100 - sd->per_cpu_gain) / 100) > + task_timeslice(smt_curr) || rt_task(p)) && + smt_curr->mm && p->mm && !rt_task(smt_curr)) || + (smt_curr == smt_rq->idle && smt_rq->nr_running)) + resched_task(smt_curr); + } + return ret; +} +#else +static inline void wake_sleeping_dependent(runqueue_t *rq) +{ +} + +static inline int dependent_sleeper(runqueue_t *rq, task_t *p) +{ + return 0; +} +#endif + void scheduling_functions_start_here(void) { } /* @@ -1653,11 +2097,12 @@ need_resched: if (unlikely(!rq->nr_running)) { #ifdef CONFIG_SMP - load_balance(rq, 1, cpu_to_node_mask(smp_processor_id())); + idle_balance(smp_processor_id(), rq); #endif if (!rq->nr_running) { next = rq->idle; rq->expired_timestamp = 0; + wake_sleeping_dependent(rq); goto switch_tasks; } } @@ -1678,6 +2123,11 @@ need_resched: queue = array->queue + idx; next = list_entry(queue->next, task_t, run_list); + if (dependent_sleeper(rq, next)) { + next = rq->idle; + goto switch_tasks; + } + if (next->activated > 0) { unsigned long long delta = now - next->timestamp; @@ -1796,7 +2246,7 @@ static void __wake_up_common(wait_queue_ * @mode: which threads * @nr_exclusive: how many wake-one or wake-many threads to wake up */ -void __wake_up(wait_queue_head_t *q, unsigned int mode, int nr_exclusive) +void fastcall __wake_up(wait_queue_head_t *q, unsigned int mode, int nr_exclusive) { unsigned long flags; @@ -1810,7 +2260,7 @@ EXPORT_SYMBOL(__wake_up); /* * Same as __wake_up but called with the spinlock in wait_queue_head_t held. */ -void __wake_up_locked(wait_queue_head_t *q, unsigned int mode) +void fastcall __wake_up_locked(wait_queue_head_t *q, unsigned int mode) { __wake_up_common(q, mode, 1, 0); } @@ -1828,7 +2278,7 @@ void __wake_up_locked(wait_queue_head_t * * On UP it can prevent extra preemption. */ -void __wake_up_sync(wait_queue_head_t *q, unsigned int mode, int nr_exclusive) +void fastcall __wake_up_sync(wait_queue_head_t *q, unsigned int mode, int nr_exclusive) { unsigned long flags; @@ -1845,7 +2295,7 @@ void __wake_up_sync(wait_queue_head_t *q EXPORT_SYMBOL_GPL(__wake_up_sync); /* For internal use only */ -void complete(struct completion *x) +void fastcall complete(struct completion *x) { unsigned long flags; @@ -1858,7 +2308,7 @@ void complete(struct completion *x) EXPORT_SYMBOL(complete); -void complete_all(struct completion *x) +void fastcall complete_all(struct completion *x) { unsigned long flags; @@ -1869,7 +2319,7 @@ void complete_all(struct completion *x) spin_unlock_irqrestore(&x->wait.lock, flags); } -void wait_for_completion(struct completion *x) +void fastcall wait_for_completion(struct completion *x) { might_sleep(); spin_lock_irq(&x->wait.lock); @@ -1907,7 +2357,7 @@ EXPORT_SYMBOL(wait_for_completion); __remove_wait_queue(q, &wait); \ spin_unlock_irqrestore(&q->lock, flags); -void interruptible_sleep_on(wait_queue_head_t *q) +void fastcall interruptible_sleep_on(wait_queue_head_t *q) { SLEEP_ON_VAR @@ -1920,7 +2370,7 @@ void interruptible_sleep_on(wait_queue_h EXPORT_SYMBOL(interruptible_sleep_on); -long interruptible_sleep_on_timeout(wait_queue_head_t *q, long timeout) +long fastcall interruptible_sleep_on_timeout(wait_queue_head_t *q, long timeout) { SLEEP_ON_VAR @@ -1935,7 +2385,7 @@ long interruptible_sleep_on_timeout(wait EXPORT_SYMBOL(interruptible_sleep_on_timeout); -void sleep_on(wait_queue_head_t *q) +void fastcall sleep_on(wait_queue_head_t *q) { SLEEP_ON_VAR @@ -1948,7 +2398,7 @@ void sleep_on(wait_queue_head_t *q) EXPORT_SYMBOL(sleep_on); -long sleep_on_timeout(wait_queue_head_t *q, long timeout) +long fastcall sleep_on_timeout(wait_queue_head_t *q, long timeout) { SLEEP_ON_VAR @@ -2014,6 +2464,13 @@ out_unlock: EXPORT_SYMBOL(set_user_nice); +#if defined( CONFIG_KGDB) +struct task_struct * kgdb_get_idle(int this_cpu) +{ + return cpu_rq(this_cpu)->idle; +} +#endif + #ifndef __alpha__ /* @@ -2311,11 +2768,13 @@ asmlinkage long sys_sched_setaffinity(pi if (copy_from_user(&new_mask, user_mask_ptr, sizeof(new_mask))) return -EFAULT; + lock_cpu_hotplug(); read_lock(&tasklist_lock); p = find_process_by_pid(pid); if (!p) { read_unlock(&tasklist_lock); + unlock_cpu_hotplug(); return -ESRCH; } @@ -2336,6 +2795,7 @@ asmlinkage long sys_sched_setaffinity(pi out_unlock: put_task_struct(p); + unlock_cpu_hotplug(); return retval; } @@ -2365,7 +2825,7 @@ asmlinkage long sys_sched_getaffinity(pi goto out_unlock; retval = 0; - cpus_and(mask, p->cpus_allowed, cpu_online_map); + cpus_and(mask, p->cpus_allowed, cpu_possible_map); out_unlock: read_unlock(&tasklist_lock); @@ -2712,7 +3172,12 @@ int set_cpus_allowed(task_t *p, cpumask_ goto out; } - if (__set_cpus_allowed(p, new_mask, &req)) { + p->cpus_allowed = new_mask; + /* Can the task run on the task's current CPU? If so, we're done */ + if (cpu_isset(task_cpu(p), new_mask)) + goto out; + + if (migrate_task(p, any_online_cpu(new_mask), &req)) { /* Need help from migration thread: drop lock and wait. */ task_rq_unlock(rq, &flags); wake_up_process(rq->migration_thread); @@ -2726,8 +3191,16 @@ out: EXPORT_SYMBOL_GPL(set_cpus_allowed); -/* Move (not current) task off this cpu, onto dest cpu. */ -static void move_task_away(struct task_struct *p, int dest_cpu) +/* + * Move (not current) task off this cpu, onto dest cpu. We're doing + * this because either it can't run here any more (set_cpus_allowed() + * away from this CPU, or CPU going down), or because we're + * attempting to rebalance this task on exec (sched_balance_exec). + * + * So we race with normal scheduler movements, but that's OK, as long + * as the task is no longer on this CPU. + */ +static void __migrate_task(struct task_struct *p, int dest_cpu) { runqueue_t *rq_dest; unsigned long flags; @@ -2736,14 +3209,21 @@ static void move_task_away(struct task_s local_irq_save(flags); double_rq_lock(this_rq(), rq_dest); + /* Already moved. */ if (task_cpu(p) != smp_processor_id()) - goto out; /* Already moved */ + goto out; + /* Affinity changed (again). */ + if (!cpu_isset(dest_cpu, p->cpus_allowed)) + goto out; + /* CPU went down. */ + if (cpu_is_offline(dest_cpu)) + goto out; set_task_cpu(p, dest_cpu); if (p->array) { deactivate_task(p, this_rq()); activate_task(p, rq_dest); - if (p->prio < rq_dest->curr->prio) + if (TASK_PREEMPTS_CURR(p, rq_dest)) resched_task(rq_dest->curr); } p->timestamp = rq_dest->timestamp_last_tick; @@ -2766,10 +3246,9 @@ static int migration_thread(void * data) int cpu = (long)data; int ret; - BUG_ON(smp_processor_id() != cpu); ret = setscheduler(0, SCHED_FIFO, ¶m); - rq = this_rq(); + rq = cpu_rq(cpu); BUG_ON(rq->migration_thread != current); while (!kthread_should_stop()) { @@ -2780,7 +3259,13 @@ static int migration_thread(void * data) refrigerator(PF_IOTHREAD); spin_lock_irq(&rq->lock); + if (rq->active_balance) { + active_load_balance(rq, cpu); + rq->active_balance = 0; + } + head = &rq->migration_queue; + current->state = TASK_INTERRUPTIBLE; if (list_empty(head)) { spin_unlock_irq(&rq->lock); @@ -2791,13 +3276,69 @@ static int migration_thread(void * data) list_del_init(head->next); spin_unlock_irq(&rq->lock); - move_task_away(req->task, - any_online_cpu(req->task->cpus_allowed)); + __migrate_task(req->task, req->dest_cpu); complete(&req->done); } return 0; } +#ifdef CONFIG_HOTPLUG_CPU +/* migrate_all_tasks - function to migrate all the tasks from the + * current cpu caller must have already scheduled this to the target + * cpu via set_cpus_allowed. Machine is stopped. */ +void migrate_all_tasks(void) +{ + struct task_struct *tsk, *t; + int dest_cpu, src_cpu; + unsigned int node; + + /* We're nailed to this CPU. */ + src_cpu = smp_processor_id(); + + /* Not required, but here for neatness. */ + write_lock(&tasklist_lock); + + /* watch out for per node tasks, let's stay on this node */ + node = cpu_to_node(src_cpu); + + do_each_thread(t, tsk) { + cpumask_t mask; + if (tsk == current) + continue; + + if (task_cpu(tsk) != src_cpu) + continue; + + /* Figure out where this task should go (attempting to + * keep it on-node), and check if it can be migrated + * as-is. NOTE that kernel threads bound to more than + * one online cpu will be migrated. */ + mask = node_to_cpumask(node); + cpus_and(mask, mask, tsk->cpus_allowed); + dest_cpu = any_online_cpu(mask); + if (dest_cpu == NR_CPUS) + dest_cpu = any_online_cpu(tsk->cpus_allowed); + if (dest_cpu == NR_CPUS) { + cpus_clear(tsk->cpus_allowed); + cpus_complement(tsk->cpus_allowed); + dest_cpu = any_online_cpu(tsk->cpus_allowed); + + /* Don't tell them about moving exiting tasks + or kernel threads (both mm NULL), since + they never leave kernel. */ + if (tsk->mm && printk_ratelimit()) + printk(KERN_INFO "process %d (%s) no " + "longer affine to cpu%d\n", + tsk->pid, tsk->comm, src_cpu); + } + + __migrate_task(tsk, dest_cpu); + } while_each_thread(t, tsk); + + write_unlock(&tasklist_lock); +} +#endif /* CONFIG_HOTPLUG_CPU */ + /* * migration_call - callback that gets triggered when a CPU is added. * Here we can start up the necessary migration thread for the new CPU. @@ -2820,17 +3361,22 @@ static int migration_call(struct notifie /* Strictly unneccessary, as first user will wake it. */ wake_up_process(cpu_rq(cpu)->migration_thread); break; +#ifdef CONFIG_HOTPLUG_CPU + case CPU_UP_CANCELED: + /* Unbind it from offline cpu so it can run. Fall thru. */ + kthread_bind(cpu_rq(cpu)->migration_thread,smp_processor_id()); + case CPU_DEAD: + kthread_stop(cpu_rq(cpu)->migration_thread); + cpu_rq(cpu)->migration_thread = NULL; + BUG_ON(cpu_rq(cpu)->nr_running != 0); + break; +#endif } return NOTIFY_OK; } -/* - * We want this after the other threads, so they can use set_cpus_allowed - * from their CPU_OFFLINE callback - */ static struct notifier_block __devinitdata migration_notifier = { .notifier_call = migration_call, - .priority = -10, }; int __init migration_init(void) @@ -2859,6 +3405,210 @@ int __init migration_init(void) spinlock_t kernel_flag __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED; EXPORT_SYMBOL(kernel_flag); +#ifdef CONFIG_SMP +#ifdef ARCH_HAS_SCHED_DOMAIN +extern void __init arch_init_sched_domains(void); +#else +static struct sched_group sched_group_cpus[NR_CPUS]; +#ifdef CONFIG_NUMA +static struct sched_group sched_group_nodes[MAX_NUMNODES]; +DEFINE_PER_CPU(struct sched_domain, node_domains); +static void __init arch_init_sched_domains(void) +{ + int i; + struct sched_group *first_node = NULL, *last_node = NULL; + + /* Set up domains */ + for_each_cpu(i) { + int node = cpu_to_node(i); + cpumask_t nodemask = node_to_cpumask(node); + struct sched_domain *node_domain = &per_cpu(node_domains, i); + struct sched_domain *cpu_domain = cpu_sched_domain(i); + + *node_domain = SD_NODE_INIT; + node_domain->span = cpu_possible_map; + + *cpu_domain = SD_CPU_INIT; + cpus_and(cpu_domain->span, nodemask, cpu_possible_map); + cpu_domain->parent = node_domain; + } + + /* Set up groups */ + for (i = 0; i < MAX_NUMNODES; i++) { + struct sched_group *first_cpu = NULL, *last_cpu = NULL; + int j; + cpumask_t nodemask; + struct sched_group *node = &sched_group_nodes[i]; + cpumask_t tmp = node_to_cpumask(i); + + cpus_and(nodemask, tmp, cpu_possible_map); + + if (cpus_empty(nodemask)) + continue; + + node->cpumask = nodemask; + node->cpu_power = SCHED_LOAD_SCALE * cpus_weight(node->cpumask); + + for_each_cpu_mask(j, node->cpumask) { + struct sched_group *cpu = &sched_group_cpus[j]; + + cpus_clear(cpu->cpumask); + cpu_set(j, cpu->cpumask); + cpu->cpu_power = SCHED_LOAD_SCALE; + + if (!first_cpu) + first_cpu = cpu; + if (last_cpu) + last_cpu->next = cpu; + last_cpu = cpu; + } + last_cpu->next = first_cpu; + + if (!first_node) + first_node = node; + if (last_node) + last_node->next = node; + last_node = node; + } + last_node->next = first_node; + + mb(); + for_each_cpu(i) { + struct sched_domain *node_domain = &per_cpu(node_domains, i); + struct sched_domain *cpu_domain = cpu_sched_domain(i); + node_domain->groups = &sched_group_nodes[cpu_to_node(i)]; + cpu_domain->groups = &sched_group_cpus[i]; + } +} + +#else /* CONFIG_NUMA */ +static void __init arch_init_sched_domains(void) +{ + int i; + struct sched_group *first_cpu = NULL, *last_cpu = NULL; + + /* Set up domains */ + for_each_cpu(i) { + struct sched_domain *cpu_domain = cpu_sched_domain(i); + + *cpu_domain = SD_CPU_INIT; + cpu_domain->span = cpu_possible_map; + } + + /* Set up CPU groups */ + for_each_cpu_mask(i, cpu_possible_map) { + struct sched_group *cpu = &sched_group_cpus[i]; + + cpus_clear(cpu->cpumask); + cpu_set(i, cpu->cpumask); + cpu->cpu_power = SCHED_LOAD_SCALE; + + if (!first_cpu) + first_cpu = cpu; + if (last_cpu) + last_cpu->next = cpu; + last_cpu = cpu; + } + last_cpu->next = first_cpu; + + mb(); + for_each_cpu(i) { + struct sched_domain *cpu_domain = cpu_sched_domain(i); + cpu_domain->groups = &sched_group_cpus[i]; + } +} + +#endif /* CONFIG_NUMA */ +#endif /* ARCH_HAS_SCHED_DOMAIN */ + +#undef SCHED_DOMAIN_DEBUG +#ifdef SCHED_DOMAIN_DEBUG +void sched_domain_debug(void) +{ + int i; + + for_each_cpu(i) { + int level = 0; + struct sched_domain *cpu_domain = cpu_sched_domain(i); + + printk(KERN_DEBUG "CPU%d: %s\n", + i, (cpu_online(i) ? " online" : "offline")); + + do { + int j; + char str[NR_CPUS]; + struct sched_group *group = cpu_domain->groups; + cpumask_t groupmask, tmp; + + cpumask_snprintf(str, NR_CPUS, cpu_domain->span); + cpus_clear(groupmask); + + printk(KERN_DEBUG); + for (j = 0; j < level + 1; j++) + printk(" "); + printk("domain %d: span %s\n", level, str); + + if (!cpu_isset(i, cpu_domain->span)) + printk(KERN_DEBUG "ERROR domain->span does not contain CPU%d\n", i); + if (!cpu_isset(i, group->cpumask)) + printk(KERN_DEBUG "ERROR domain->groups does not contain CPU%d\n", i); + + printk(KERN_DEBUG); + for (j = 0; j < level + 2; j++) + printk(" "); + printk("groups:"); + do { + if (group == NULL) { + printk(" ERROR: NULL"); + break; + } + + if (cpus_weight(group->cpumask) == 0) + printk(" ERROR empty group:"); + + cpus_and(tmp, groupmask, group->cpumask); + if (cpus_weight(tmp) > 0) + printk(" ERROR repeated CPUs:"); + + cpus_or(groupmask, groupmask, group->cpumask); + + cpumask_snprintf(str, NR_CPUS, group->cpumask); + printk(" %s", str); + + group = group->next; + } while (group != cpu_domain->groups); + printk("\n"); + + if (!cpus_equal(cpu_domain->span, groupmask)) + printk(KERN_DEBUG "ERROR groups don't span domain->span\n"); + + level++; + cpu_domain = cpu_domain->parent; + + if (cpu_domain) { + cpus_and(tmp, groupmask, cpu_domain->span); + if (!cpus_equal(tmp, groupmask)) + printk(KERN_DEBUG "ERROR parent span is not a superset of domain->span\n"); + } + + } while (cpu_domain); + } +} +#else +#define sched_domain_debug() {} +#endif + +void __init sched_init_smp(void) +{ + arch_init_sched_domains(); + sched_domain_debug(); +} +#else +void __init sched_init_smp(void) +{ +} +#endif /* CONFIG_SMP */ + void __init sched_init(void) { runqueue_t *rq; @@ -2866,8 +3616,14 @@ void __init sched_init(void) for (i = 0; i < NR_CPUS; i++) { prio_array_t *array; +#ifdef CONFIG_SMP + struct sched_domain *domain; + domain = cpu_sched_domain(i); + memset(domain, 0, sizeof(struct sched_domain)); +#endif rq = cpu_rq(i); + rq->cpu = i; rq->active = rq->arrays; rq->expired = rq->arrays + 1; rq->best_expired_prio = MAX_PRIO; @@ -2875,7 +3631,6 @@ void __init sched_init(void) spin_lock_init(&rq->lock); INIT_LIST_HEAD(&rq->migration_queue); atomic_set(&rq->nr_iowait, 0); - nr_running_init(rq); for (j = 0; j < 2; j++) { array = rq->arrays + j; --- linux-2.6.4-rc2/kernel/signal.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/kernel/signal.c 2004-03-07 20:47:49.000000000 -0800 @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -213,7 +214,7 @@ static inline int has_pending_signals(si #define PENDING(p,b) has_pending_signals(&(p)->signal, (b)) -inline void recalc_sigpending_tsk(struct task_struct *t) +fastcall void recalc_sigpending_tsk(struct task_struct *t) { if (t->signal->group_stop_count > 0 || PENDING(&t->pending, &t->blocked) || @@ -2003,6 +2004,12 @@ int copy_siginfo_to_user(siginfo_t __use if (from->si_code < 0) return __copy_to_user(to, from, sizeof(siginfo_t)) ? -EFAULT : 0; + + /* Use compat_siginfo_t with 32-bit signals */ + if(is_compat_task(current)){ + return compat_copy_siginfo_to_user((compat_siginfo_t __user *)to,from); + } + /* * If you change siginfo_t structure, please be sure * this code is fixed accordingly. @@ -2041,6 +2048,7 @@ int copy_siginfo_to_user(siginfo_t __use err |= __put_user(from->si_stime, &to->si_stime); break; case __SI_RT: /* This is not generated by the kernel as of now. */ + case __SI_MESGQ: /* But this is */ err |= __put_user(from->si_pid, &to->si_pid); err |= __put_user(from->si_uid, &to->si_uid); err |= __put_user(from->si_int, &to->si_int); --- linux-2.6.4-rc2/kernel/softirq.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/kernel/softirq.c 2004-03-07 20:48:20.000000000 -0800 @@ -16,6 +16,7 @@ #include #include +#include /* - No shared variables, all the data are CPU local. - If a softirq needs serialization, let it serialize itself @@ -69,60 +70,84 @@ static inline void wakeup_softirqd(void) */ #define MAX_SOFTIRQ_RESTART 10 -asmlinkage void do_softirq(void) +asmlinkage void __do_softirq(void) { - int max_restart = MAX_SOFTIRQ_RESTART; + struct softirq_action *h; __u32 pending; - unsigned long flags; + int max_restart = MAX_SOFTIRQ_RESTART; - if (in_interrupt()) - return; + pending = local_softirq_pending(); - local_irq_save(flags); + local_bh_disable(); +restart: + /* Reset the pending bitmask before enabling irqs */ + local_softirq_pending() = 0; + + local_irq_enable(); + + h = softirq_vec; + + do { + if (pending & 1) + h->action(h); + h++; + pending >>= 1; + } while (pending); + + local_irq_disable(); pending = local_softirq_pending(); + if (pending && --max_restart) + goto restart; - if (pending) { - struct softirq_action *h; + if (pending) + wakeup_softirqd(); - local_bh_disable(); -restart: - /* Reset the pending bitmask before enabling irqs */ - local_softirq_pending() = 0; + __local_bh_enable(); +} - local_irq_enable(); +#ifndef __ARCH_HAS_DO_SOFTIRQ - h = softirq_vec; +asmlinkage void do_softirq(void) +{ + __u32 pending; + unsigned long flags; - do { - if (pending & 1) - h->action(h); - h++; - pending >>= 1; - } while (pending); + if (in_interrupt()) + return; - local_irq_disable(); + local_irq_save(flags); - pending = local_softirq_pending(); - if (pending && --max_restart) - goto restart; - if (pending) - wakeup_softirqd(); - __local_bh_enable(); - } + pending = local_softirq_pending(); + + if (pending) + __do_softirq(); local_irq_restore(flags); } EXPORT_SYMBOL(do_softirq); +#endif + void local_bh_enable(void) { + if (in_irq()) { + printk("local_bh_enable() was called in hard irq context. " + "This is probably a bug\n"); + dump_stack(); + } + __local_bh_enable(); - WARN_ON(irqs_disabled()); - if (unlikely(!in_interrupt() && - local_softirq_pending())) + if (unlikely(!in_interrupt() && local_softirq_pending())) { + if (irqs_disabled()) { + printk("local_bh_enable() was called with local " + "interrupts disabled. This is probably a" + " bug\n"); + dump_stack(); + } invoke_softirq(); + } preempt_check_resched(); } EXPORT_SYMBOL(local_bh_enable); @@ -130,7 +155,7 @@ EXPORT_SYMBOL(local_bh_enable); /* * This function must run with irqs disabled! */ -inline void raise_softirq_irqoff(unsigned int nr) +inline fastcall void raise_softirq_irqoff(unsigned int nr) { __raise_softirq_irqoff(nr); @@ -149,7 +174,7 @@ inline void raise_softirq_irqoff(unsigne EXPORT_SYMBOL(raise_softirq_irqoff); -void raise_softirq(unsigned int nr) +void fastcall raise_softirq(unsigned int nr) { unsigned long flags; @@ -179,7 +204,7 @@ struct tasklet_head static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec) = { NULL }; static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec) = { NULL }; -void __tasklet_schedule(struct tasklet_struct *t) +void fastcall __tasklet_schedule(struct tasklet_struct *t) { unsigned long flags; @@ -192,7 +217,7 @@ void __tasklet_schedule(struct tasklet_s EXPORT_SYMBOL(__tasklet_schedule); -void __tasklet_hi_schedule(struct tasklet_struct *t) +void fastcall __tasklet_hi_schedule(struct tasklet_struct *t) { unsigned long flags; @@ -308,13 +333,9 @@ void __init softirq_init(void) static int ksoftirqd(void * __bind_cpu) { - int cpu = (int) (long) __bind_cpu; - set_user_nice(current, 19); current->flags |= PF_IOTHREAD; - BUG_ON(smp_processor_id() != cpu); - set_current_state(TASK_INTERRUPTIBLE); while (!kthread_should_stop()) { @@ -324,15 +345,83 @@ static int ksoftirqd(void * __bind_cpu) __set_current_state(TASK_RUNNING); while (local_softirq_pending()) { + /* Preempt disable stops cpu going offline. + If already offline, we'll be on wrong CPU: + don't process */ + preempt_disable(); + if (cpu_is_offline((long)__bind_cpu)) + goto wait_to_die; do_softirq(); + preempt_enable(); cond_resched(); } __set_current_state(TASK_INTERRUPTIBLE); } return 0; + +wait_to_die: + preempt_enable(); + /* Wait for kthread_stop */ + while (!kthread_should_stop()) { + __set_current_state(TASK_INTERRUPTIBLE); + schedule(); + } + return 0; +} + +#ifdef CONFIG_HOTPLUG_CPU +/* + * tasklet_kill_immediate is called to remove a tasklet which can already be + * scheduled for execution on @cpu. + * + * Unlike tasklet_kill, this function removes the tasklet + * _immediately_, even if the tasklet is in TASKLET_STATE_SCHED state. + * + * When this function is called, @cpu must be in the CPU_DEAD state. + */ +void tasklet_kill_immediate(struct tasklet_struct *t, unsigned int cpu) +{ + struct tasklet_struct **i; + + BUG_ON(cpu_online(cpu)); + BUG_ON(test_bit(TASKLET_STATE_RUN, &t->state)); + + if (!test_bit(TASKLET_STATE_SCHED, &t->state)) + return; + + /* CPU is dead, so no lock needed. */ + for (i = &per_cpu(tasklet_vec, cpu).list; *i; i = &(*i)->next) { + if (*i == t) { + *i = t->next; + return; + } + } + BUG(); } +static void takeover_tasklets(unsigned int cpu) +{ + struct tasklet_struct **i; + + /* CPU is dead, so no lock needed. */ + local_irq_disable(); + + /* Find end, append list for that CPU. */ + for (i = &__get_cpu_var(tasklet_vec).list; *i; i = &(*i)->next); + *i = per_cpu(tasklet_vec, cpu).list; + per_cpu(tasklet_vec, cpu).list = NULL; + raise_softirq_irqoff(TASKLET_SOFTIRQ); + + for (i = &__get_cpu_var(tasklet_hi_vec).list; *i; i = &(*i)->next); + *i = per_cpu(tasklet_hi_vec, cpu).list; + per_cpu(tasklet_hi_vec, cpu).list = NULL; + raise_softirq_irqoff(HI_SOFTIRQ); + + local_irq_enable(); +} +#endif /* CONFIG_HOTPLUG_CPU */ + static int __devinit cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) @@ -349,13 +438,23 @@ static int __devinit cpu_callback(struct printk("ksoftirqd for %i failed\n", hotcpu); return NOTIFY_BAD; } - per_cpu(ksoftirqd, hotcpu) = p; kthread_bind(p, hotcpu); per_cpu(ksoftirqd, hotcpu) = p; break; case CPU_ONLINE: wake_up_process(per_cpu(ksoftirqd, hotcpu)); break; +#ifdef CONFIG_HOTPLUG_CPU + case CPU_UP_CANCELED: + /* Unbind so it can run. Fall thru. */ + kthread_bind(per_cpu(ksoftirqd, hotcpu), smp_processor_id()); + case CPU_DEAD: + p = per_cpu(ksoftirqd, hotcpu); + per_cpu(ksoftirqd, hotcpu) = NULL; + kthread_stop(p); + takeover_tasklets(hotcpu); + break; +#endif /* CONFIG_HOTPLUG_CPU */ } return NOTIFY_OK; } --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/kernel/stop_machine.c 2004-03-07 20:47:24.000000000 -0800 @@ -0,0 +1,200 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +/* Since we effect priority and affinity (both of which are visible + * to, and settable by outside processes) we do indirection via a + * kthread. */ + +/* Thread to stop each CPU in user context. */ +enum stopmachine_state { + STOPMACHINE_WAIT, + STOPMACHINE_PREPARE, + STOPMACHINE_DISABLE_IRQ, + STOPMACHINE_EXIT, +}; + +static enum stopmachine_state stopmachine_state; +static unsigned int stopmachine_num_threads; +static atomic_t stopmachine_thread_ack; +static DECLARE_MUTEX(stopmachine_mutex); + +static int stopmachine(void *cpu) +{ + int irqs_disabled = 0; + int prepared = 0; + + set_cpus_allowed(current, cpumask_of_cpu((int)(long)cpu)); + + /* Ack: we are alive */ + mb(); /* Theoretically the ack = 0 might not be on this CPU yet. */ + atomic_inc(&stopmachine_thread_ack); + + /* Simple state machine */ + while (stopmachine_state != STOPMACHINE_EXIT) { + if (stopmachine_state == STOPMACHINE_DISABLE_IRQ + && !irqs_disabled) { + local_irq_disable(); + irqs_disabled = 1; + /* Ack: irqs disabled. */ + mb(); /* Must read state first. */ + atomic_inc(&stopmachine_thread_ack); + } else if (stopmachine_state == STOPMACHINE_PREPARE + && !prepared) { + /* Everyone is in place, hold CPU. */ + preempt_disable(); + prepared = 1; + mb(); /* Must read state first. */ + atomic_inc(&stopmachine_thread_ack); + } + cpu_relax(); + } + + /* Ack: we are exiting. */ + mb(); /* Must read state first. */ + atomic_inc(&stopmachine_thread_ack); + + if (irqs_disabled) + local_irq_enable(); + if (prepared) + preempt_enable(); + + return 0; +} + +/* Change the thread state */ +static void stopmachine_set_state(enum stopmachine_state state) +{ + atomic_set(&stopmachine_thread_ack, 0); + wmb(); + stopmachine_state = state; + while (atomic_read(&stopmachine_thread_ack) != stopmachine_num_threads) + cpu_relax(); +} + +static int stop_machine(void) +{ + int i, ret = 0; + struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; + + /* One high-prio thread per cpu. We'll do this one. */ + sys_sched_setscheduler(current->pid, SCHED_FIFO, ¶m); + + atomic_set(&stopmachine_thread_ack, 0); + stopmachine_num_threads = 0; + stopmachine_state = STOPMACHINE_WAIT; + + for_each_online_cpu(i) { + if (i == smp_processor_id()) + continue; + ret = kernel_thread(stopmachine, (void *)(long)i,CLONE_KERNEL); + if (ret < 0) + break; + stopmachine_num_threads++; + } + + /* Wait for them all to come to life. */ + while (atomic_read(&stopmachine_thread_ack) != stopmachine_num_threads) + yield(); + + /* If some failed, kill them all. */ + if (ret < 0) { + stopmachine_set_state(STOPMACHINE_EXIT); + up(&stopmachine_mutex); + return ret; + } + + /* Don't schedule us away at this point, please. */ + local_irq_disable(); + + /* Now they are all started, make them hold the CPUs, ready. */ + stopmachine_set_state(STOPMACHINE_PREPARE); + + /* Make them disable irqs. */ + stopmachine_set_state(STOPMACHINE_DISABLE_IRQ); + + return 0; +} + +static void restart_machine(void) +{ + stopmachine_set_state(STOPMACHINE_EXIT); + local_irq_enable(); +} + +struct stop_machine_data +{ + int (*fn)(void *); + void *data; + struct completion done; +}; + +static int do_stop(void *_smdata) +{ + struct stop_machine_data *smdata = _smdata; + int ret; + + ret = stop_machine(); + if (ret == 0) { + ret = smdata->fn(smdata->data); + restart_machine(); + } + + /* We're done: you can kthread_stop us now */ + complete(&smdata->done); + + /* Wait for kthread_stop */ + while (!kthread_should_stop()) { + __set_current_state(TASK_INTERRUPTIBLE); + schedule(); + } + return ret; +} + +struct task_struct *__stop_machine_run(int (*fn)(void *), void *data, + unsigned int cpu) +{ + struct stop_machine_data smdata; + struct task_struct *p; + + smdata.fn = fn; + smdata.data = data; + init_completion(&smdata.done); + + down(&stopmachine_mutex); + + /* If they don't care which CPU fn runs on, bind to any online one. */ + if (cpu == NR_CPUS) + cpu = smp_processor_id(); + + p = kthread_create(do_stop, &smdata, "kstopmachine"); + if (!IS_ERR(p)) { + kthread_bind(p, cpu); + wake_up_process(p); + wait_for_completion(&smdata.done); + } + up(&stopmachine_mutex); + return p; +} + +int stop_machine_run(int (*fn)(void *), void *data, unsigned int cpu) +{ + struct task_struct *p; + int ret; + + /* No CPUs can come up or down during this. */ + lock_cpu_hotplug(); + p = __stop_machine_run(fn, data, cpu); + if (!IS_ERR(p)) + ret = kthread_stop(p); + else + ret = PTR_ERR(p); + unlock_cpu_hotplug(); + + return ret; +} --- linux-2.6.4-rc2/kernel/sys.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/kernel/sys.c 2004-03-07 20:47:49.000000000 -0800 @@ -260,6 +260,12 @@ cond_syscall(sys_msgctl) cond_syscall(sys_shmget) cond_syscall(sys_shmdt) cond_syscall(sys_shmctl) +cond_syscall(sys_mq_open) +cond_syscall(sys_mq_unlink) +cond_syscall(sys_mq_timedsend) +cond_syscall(sys_mq_timedreceive) +cond_syscall(sys_mq_notify) +cond_syscall(sys_mq_getsetattr) /* arch-specific weak syscall entries */ cond_syscall(sys_pciconfig_read) --- linux-2.6.4-rc2/kernel/sysctl.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/kernel/sysctl.c 2004-03-07 20:47:20.000000000 -0800 @@ -736,6 +736,26 @@ static ctl_table vm_table[] = { .strategy = &sysctl_intvec, .extra1 = &zero, }, + { + .ctl_name = VM_LAPTOP_MODE, + .procname = "laptop_mode", + .data = &laptop_mode, + .maxlen = sizeof(laptop_mode), + .mode = 0644, + .proc_handler = &proc_dointvec, + .strategy = &sysctl_intvec, + .extra1 = &zero, + }, + { + .ctl_name = VM_BLOCK_DUMP, + .procname = "block_dump", + .data = &block_dump, + .maxlen = sizeof(block_dump), + .mode = 0644, + .proc_handler = &proc_dointvec, + .strategy = &sysctl_intvec, + .extra1 = &zero, + }, { .ctl_name = 0 } }; --- linux-2.6.4-rc2/kernel/timer.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/kernel/timer.c 2004-03-07 20:47:43.000000000 -0800 @@ -677,7 +677,6 @@ static void update_wall_time(unsigned lo if (xtime.tv_nsec >= 1000000000) { xtime.tv_nsec -= 1000000000; xtime.tv_sec++; - time_interpolator_update(NSEC_PER_SEC); second_overflow(); } } @@ -971,6 +970,13 @@ static void process_timeout(unsigned lon wake_up_process((task_t *)__data); } +static void futex_timeout(unsigned long __data) +{ + current->flags |= PF_FUTEX_DEBUG; + wake_up_process((task_t *)__data); + current->flags &= ~PF_FUTEX_DEBUG; +} + /** * schedule_timeout - sleep until timeout * @timeout: timeout value in jiffies @@ -997,7 +1003,7 @@ static void process_timeout(unsigned lon * * In all cases the return value is guaranteed to be non-negative. */ -signed long schedule_timeout(signed long timeout) +fastcall signed long schedule_timeout(signed long timeout) { struct timer_list timer; unsigned long expire; @@ -1037,7 +1043,10 @@ signed long schedule_timeout(signed long init_timer(&timer); timer.expires = expire; timer.data = (unsigned long) current; - timer.function = process_timeout; + if (current->flags & PF_FUTEX_DEBUG) + timer.function = futex_timeout; + else + timer.function = process_timeout; add_timer(&timer); schedule(); @@ -1223,7 +1232,73 @@ static void __devinit init_timers_cpu(in base->timer_jiffies = jiffies; } - + +#ifdef CONFIG_HOTPLUG_CPU +static int migrate_timer_list(tvec_base_t *new_base, struct list_head *head) +{ + struct timer_list *timer; + + while (!list_empty(head)) { + timer = list_entry(head->next, struct timer_list, entry); + /* We're locking backwards from __mod_timer order here, + beware deadlock. */ + if (!spin_trylock(&timer->lock)) + return 0; + list_del(&timer->entry); + internal_add_timer(new_base, timer); + timer->base = new_base; + spin_unlock(&timer->lock); + } + return 1; +} + +static void __devinit migrate_timers(int cpu) +{ + tvec_base_t *old_base; + tvec_base_t *new_base; + int i; + + BUG_ON(cpu_online(cpu)); + old_base = &per_cpu(tvec_bases, cpu); + new_base = &get_cpu_var(tvec_bases); + + local_irq_disable(); +again: + /* Prevent deadlocks via ordering by old_base < new_base. */ + if (old_base < new_base) { + spin_lock(&new_base->lock); + spin_lock(&old_base->lock); + } else { + spin_lock(&old_base->lock); + spin_lock(&new_base->lock); + } + + if (old_base->running_timer) + BUG(); + for (i = 0; i < TVR_SIZE; i++) + if (!migrate_timer_list(new_base, old_base->tv1.vec + i)) + goto unlock_again; + for (i = 0; i < TVN_SIZE; i++) + if (!migrate_timer_list(new_base, old_base->tv2.vec + i) + || !migrate_timer_list(new_base, old_base->tv3.vec + i) + || !migrate_timer_list(new_base, old_base->tv4.vec + i) + || !migrate_timer_list(new_base, old_base->tv5.vec + i)) + goto unlock_again; + spin_unlock(&old_base->lock); + spin_unlock(&new_base->lock); + local_irq_enable(); + put_cpu_var(tvec_bases); + return; + +unlock_again: + /* Avoid deadlock with __mod_timer, by backing off. */ + spin_unlock(&old_base->lock); + spin_unlock(&new_base->lock); + cpu_relax(); + goto again; +} +#endif /* CONFIG_HOTPLUG_CPU */ + static int __devinit timer_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu) { @@ -1232,6 +1307,11 @@ static int __devinit timer_cpu_notify(st case CPU_UP_PREPARE: init_timers_cpu(cpu); break; +#ifdef CONFIG_HOTPLUG_CPU + case CPU_DEAD: + migrate_timers(cpu); + break; +#endif default: break; } --- linux-2.6.4-rc2/kernel/workqueue.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/kernel/workqueue.c 2004-03-07 20:47:57.000000000 -0800 @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include /* @@ -55,8 +57,22 @@ struct cpu_workqueue_struct { */ struct workqueue_struct { struct cpu_workqueue_struct cpu_wq[NR_CPUS]; + const char *name; + struct list_head list; }; +#ifdef CONFIG_HOTPLUG_CPU +/* All the workqueues on the system, for hotplug cpu to add/remove + threads to each one as cpus come/go. Protected by cpucontrol + sem. */ +static LIST_HEAD(workqueues); +#define add_workqueue(wq) list_add(&(wq)->list, &workqueues) +#define del_workqueue(wq) list_del(&(wq)->list) +#else +#define add_workqueue(wq) +#define del_workqueue(wq) +#endif /* CONFIG_HOTPLUG_CPU */ + /* Preempt must be disabled. */ static void __queue_work(struct cpu_workqueue_struct *cwq, struct work_struct *work) @@ -78,18 +94,29 @@ static void __queue_work(struct cpu_work * We queue the work to the CPU it was submitted, but there is no * guarantee that it will be processed by that CPU. */ -int queue_work(struct workqueue_struct *wq, struct work_struct *work) +int queue_work_on_cpu(struct workqueue_struct *wq, + struct work_struct *work, int cpu) { - int ret = 0, cpu = get_cpu(); + int ret = 0; if (!test_and_set_bit(0, &work->pending)) { BUG_ON(!list_empty(&work->entry)); __queue_work(wq->cpu_wq + cpu, work); ret = 1; } + return ret; +} +EXPORT_SYMBOL_GPL(queue_work); + +int queue_work(struct workqueue_struct *wq, struct work_struct *work) +{ + int ret; + + ret = queue_work_on_cpu(wq, work, get_cpu()); put_cpu(); return ret; } +EXPORT_SYMBOL_GPL(queue_work_on_cpu); static void delayed_work_timer_fn(unsigned long __data) { @@ -99,7 +126,7 @@ static void delayed_work_timer_fn(unsign __queue_work(wq->cpu_wq + smp_processor_id(), work); } -int queue_delayed_work(struct workqueue_struct *wq, +int fastcall queue_delayed_work(struct workqueue_struct *wq, struct work_struct *work, unsigned long delay) { int ret = 0; @@ -152,7 +179,6 @@ static inline void run_workqueue(struct static int worker_thread(void *__cwq) { struct cpu_workqueue_struct *cwq = __cwq; - int cpu = cwq - cwq->wq->cpu_wq; DECLARE_WAITQUEUE(wait, current); struct k_sigaction sa; sigset_t blocked; @@ -160,7 +186,6 @@ static int worker_thread(void *__cwq) current->flags |= PF_IOTHREAD; set_user_nice(current, -10); - BUG_ON(smp_processor_id() != cpu); /* Block and flush all signals */ sigfillset(&blocked); @@ -203,13 +228,14 @@ static int worker_thread(void *__cwq) * This function used to run the workqueues itself. Now we just wait for the * helper threads to do it. */ -void flush_workqueue(struct workqueue_struct *wq) +void fastcall flush_workqueue(struct workqueue_struct *wq) { struct cpu_workqueue_struct *cwq; int cpu; might_sleep(); + lock_cpu_hotplug(); for (cpu = 0; cpu < NR_CPUS; cpu++) { DEFINE_WAIT(wait); long sequence_needed; @@ -231,11 +257,10 @@ void flush_workqueue(struct workqueue_st finish_wait(&cwq->work_done, &wait); spin_unlock_irq(&cwq->lock); } + unlock_cpu_hotplug(); } -static int create_workqueue_thread(struct workqueue_struct *wq, - const char *name, - int cpu) +static int create_workqueue_thread(struct workqueue_struct *wq, int cpu) { struct cpu_workqueue_struct *cwq = wq->cpu_wq + cpu; struct task_struct *p; @@ -249,7 +274,7 @@ static int create_workqueue_thread(struc init_waitqueue_head(&cwq->more_work); init_waitqueue_head(&cwq->work_done); - p = kthread_create(worker_thread, cwq, "%s/%d", name, cpu); + p = kthread_create(worker_thread, cwq, "%s/%d", wq->name, cpu); if (IS_ERR(p)) return PTR_ERR(p); cwq->thread = p; @@ -268,14 +293,19 @@ struct workqueue_struct *create_workqueu if (!wq) return NULL; + wq->name = name; + /* We don't need the distraction of CPUs appearing and vanishing. */ + lock_cpu_hotplug(); for (cpu = 0; cpu < NR_CPUS; cpu++) { if (!cpu_online(cpu)) continue; - if (create_workqueue_thread(wq, name, cpu) < 0) + if (create_workqueue_thread(wq, cpu) < 0) destroy = 1; else wake_up_process(wq->cpu_wq[cpu].thread); } + add_workqueue(wq); + /* * Was there any error during startup? If yes then clean up: */ @@ -283,16 +313,23 @@ struct workqueue_struct *create_workqueu destroy_workqueue(wq); wq = NULL; } + unlock_cpu_hotplug(); return wq; } static void cleanup_workqueue_thread(struct workqueue_struct *wq, int cpu) { struct cpu_workqueue_struct *cwq; + unsigned long flags; + struct task_struct *p; cwq = wq->cpu_wq + cpu; - if (cwq->thread) - kthread_stop(cwq->thread); + spin_lock_irqsave(&cwq->lock, flags); + p = cwq->thread; + cwq->thread = NULL; + spin_unlock_irqrestore(&cwq->lock, flags); + if (p) + kthread_stop(p); } void destroy_workqueue(struct workqueue_struct *wq) @@ -301,21 +338,25 @@ void destroy_workqueue(struct workqueue_ flush_workqueue(wq); + /* We don't need the distraction of CPUs appearing and vanishing. */ + lock_cpu_hotplug(); for (cpu = 0; cpu < NR_CPUS; cpu++) { if (cpu_online(cpu)) cleanup_workqueue_thread(wq, cpu); } + del_workqueue(wq); + unlock_cpu_hotplug(); kfree(wq); } static struct workqueue_struct *keventd_wq; -int schedule_work(struct work_struct *work) +int fastcall schedule_work(struct work_struct *work) { return queue_work(keventd_wq, work); } -int schedule_delayed_work(struct work_struct *work, unsigned long delay) +int fastcall schedule_delayed_work(struct work_struct *work, unsigned long delay) { return queue_delayed_work(keventd_wq, work, delay); } @@ -345,14 +386,80 @@ int current_is_keventd(void) return 0; } +#ifdef CONFIG_HOTPLUG_CPU +/* Take the work from this (downed) CPU. */ +static void take_over_work(struct workqueue_struct *wq, unsigned int cpu) +{ + struct cpu_workqueue_struct *cwq = wq->cpu_wq + cpu; + LIST_HEAD(list); + struct work_struct *work; + + spin_lock_irq(&cwq->lock); + list_splice_init(&cwq->worklist, &list); + + while (!list_empty(&list)) { + printk("Taking work for %s\n", wq->name); + work = list_entry(list.next,struct work_struct,entry); + list_del(&work->entry); + __queue_work(wq->cpu_wq + smp_processor_id(), work); + } + spin_unlock_irq(&cwq->lock); +} + +/* We're holding the cpucontrol mutex here */ +static int __devinit workqueue_cpu_callback(struct notifier_block *nfb, + unsigned long action, + void *hcpu) +{ + unsigned int hotcpu = (unsigned long)hcpu; + struct workqueue_struct *wq; + + switch (action) { + case CPU_UP_PREPARE: + /* Create a new workqueue thread for it. */ + list_for_each_entry(wq, &workqueues, list) { + if (create_workqueue_thread(wq, hotcpu) < 0) { + printk("workqueue for %i failed\n", hotcpu); + return NOTIFY_BAD; + } + } + break; + + case CPU_ONLINE: + /* Kick off worker threads. */ + list_for_each_entry(wq, &workqueues, list) + wake_up_process(wq->cpu_wq[hotcpu].thread); + break; + + case CPU_UP_CANCELED: + list_for_each_entry(wq, &workqueues, list) { + /* Unbind so it can run. */ + kthread_bind(wq->cpu_wq[hotcpu].thread, + smp_processor_id()); + cleanup_workqueue_thread(wq, hotcpu); + } + break; + + case CPU_DEAD: + list_for_each_entry(wq, &workqueues, list) + cleanup_workqueue_thread(wq, hotcpu); + list_for_each_entry(wq, &workqueues, list) + take_over_work(wq, hotcpu); + break; + } + + return NOTIFY_OK; +} +#endif + void init_workqueues(void) { + hotcpu_notifier(workqueue_cpu_callback, 0); keventd_wq = create_workqueue("events"); BUG_ON(!keventd_wq); } EXPORT_SYMBOL_GPL(create_workqueue); -EXPORT_SYMBOL_GPL(queue_work); EXPORT_SYMBOL_GPL(queue_delayed_work); EXPORT_SYMBOL_GPL(flush_workqueue); EXPORT_SYMBOL_GPL(destroy_workqueue); --- linux-2.6.4-rc2/lib/rwsem.c 2003-06-14 12:17:56.000000000 -0700 +++ 25/lib/rwsem.c 2004-03-07 20:46:46.000000000 -0800 @@ -162,7 +162,7 @@ static inline struct rw_semaphore *rwsem /* * wait for the read lock to be granted */ -struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *sem) +struct rw_semaphore fastcall *rwsem_down_read_failed(struct rw_semaphore *sem) { struct rwsem_waiter waiter; @@ -178,7 +178,7 @@ struct rw_semaphore *rwsem_down_read_fai /* * wait for the write lock to be granted */ -struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *sem) +struct rw_semaphore fastcall *rwsem_down_write_failed(struct rw_semaphore *sem) { struct rwsem_waiter waiter; @@ -195,7 +195,7 @@ struct rw_semaphore *rwsem_down_write_fa * handle waking up a waiter on the semaphore * - up_read has decremented the active part of the count if we come here */ -struct rw_semaphore *rwsem_wake(struct rw_semaphore *sem) +struct rw_semaphore fastcall *rwsem_wake(struct rw_semaphore *sem) { rwsemtrace(sem,"Entering rwsem_wake"); @@ -217,7 +217,7 @@ struct rw_semaphore *rwsem_wake(struct r * - caller incremented waiting part of count, and discovered it to be still negative * - just wake up any readers at the front of the queue */ -struct rw_semaphore *rwsem_downgrade_wake(struct rw_semaphore *sem) +struct rw_semaphore fastcall *rwsem_downgrade_wake(struct rw_semaphore *sem) { rwsemtrace(sem,"Entering rwsem_downgrade_wake"); --- linux-2.6.4-rc2/lib/rwsem-spinlock.c 2003-06-14 12:17:58.000000000 -0700 +++ 25/lib/rwsem-spinlock.c 2004-03-07 20:46:46.000000000 -0800 @@ -29,7 +29,7 @@ void rwsemtrace(struct rw_semaphore *sem /* * initialise the semaphore */ -void init_rwsem(struct rw_semaphore *sem) +void fastcall init_rwsem(struct rw_semaphore *sem) { sem->activity = 0; spin_lock_init(&sem->wait_lock); @@ -117,7 +117,7 @@ static inline struct rw_semaphore *__rws /* * get a read lock on the semaphore */ -void __down_read(struct rw_semaphore *sem) +void fastcall __down_read(struct rw_semaphore *sem) { struct rwsem_waiter waiter; struct task_struct *tsk; @@ -162,7 +162,7 @@ void __down_read(struct rw_semaphore *se /* * trylock for reading -- returns 1 if successful, 0 if contention */ -int __down_read_trylock(struct rw_semaphore *sem) +int fastcall __down_read_trylock(struct rw_semaphore *sem) { int ret = 0; rwsemtrace(sem,"Entering __down_read_trylock"); @@ -185,7 +185,7 @@ int __down_read_trylock(struct rw_semaph * get a write lock on the semaphore * - note that we increment the waiting count anyway to indicate an exclusive lock */ -void __down_write(struct rw_semaphore *sem) +void fastcall __down_write(struct rw_semaphore *sem) { struct rwsem_waiter waiter; struct task_struct *tsk; @@ -230,7 +230,7 @@ void __down_write(struct rw_semaphore *s /* * trylock for writing -- returns 1 if successful, 0 if contention */ -int __down_write_trylock(struct rw_semaphore *sem) +int fastcall __down_write_trylock(struct rw_semaphore *sem) { int ret = 0; rwsemtrace(sem,"Entering __down_write_trylock"); @@ -252,7 +252,7 @@ int __down_write_trylock(struct rw_semap /* * release a read lock on the semaphore */ -void __up_read(struct rw_semaphore *sem) +void fastcall __up_read(struct rw_semaphore *sem) { rwsemtrace(sem,"Entering __up_read"); @@ -269,7 +269,7 @@ void __up_read(struct rw_semaphore *sem) /* * release a write lock on the semaphore */ -void __up_write(struct rw_semaphore *sem) +void fastcall __up_write(struct rw_semaphore *sem) { rwsemtrace(sem,"Entering __up_write"); @@ -288,7 +288,7 @@ void __up_write(struct rw_semaphore *sem * downgrade a write lock into a read lock * - just wake up any readers at the front of the queue */ -void __downgrade_write(struct rw_semaphore *sem) +void fastcall __downgrade_write(struct rw_semaphore *sem) { rwsemtrace(sem,"Entering __downgrade_write"); --- linux-2.6.4-rc2/MAINTAINERS 2004-03-03 23:12:42.000000000 -0800 +++ 25/MAINTAINERS 2004-03-07 20:47:56.000000000 -0800 @@ -172,7 +172,7 @@ ACPI P: Len Brown M: len.brown@intel.com L: acpi-devel@lists.sourceforge.net -W: http://sf.net/projects/acpi/ +W: http://acpi.sourceforge.net/ S: Maintained AD1816 SOUND DRIVER @@ -1186,6 +1186,12 @@ W: http://sf.net/projects/kernel-janitor W: http://developer.osdl.org/rddunlap/kj-patches/ S: Maintained +KGDB FOR I386 PLATFORM +P: George Anzinger +M: george@mvista.com +L: linux-net@vger.kernel.org +S: Supported + KERNEL NFSD P: Neil Brown M: neilb@cse.unsw.edu.au @@ -2037,8 +2043,6 @@ S: Maintained UDF FILESYSTEM P: Ben Fennema M: bfennema@falcon.csc.calpoly.edu -P: Dave Boynton -M: dave@trylinux.com L: linux_udf@hpesjro.fc.hp.com W: http://linux-udf.sourceforge.net S: Maintained --- linux-2.6.4-rc2/Makefile 2004-03-03 23:12:42.000000000 -0800 +++ 25/Makefile 2004-03-07 20:48:05.000000000 -0800 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 4 -EXTRAVERSION =-rc2 +EXTRAVERSION =-rc2-mm1 NAME=Feisty Dunnart # *DOCUMENTATION* @@ -444,6 +444,7 @@ endif ifdef CONFIG_DEBUG_INFO CFLAGS += -g +AFLAGS += -g endif # warn about C99 declaration after statement @@ -757,26 +758,15 @@ endef # Any core files spread around are deleted as well # make distclean Remove editor backup files, patch leftover files and the like -# Files removed with 'make clean' -CLEAN_FILES += vmlinux System.map MC* +# Directories & files removed with 'make clean' +CLEAN_DIRS += $(MODVERDIR) include/config include2 +CLEAN_FILES += vmlinux System.map \ + include/linux/autoconf.h include/linux/version.h \ + include/asm include/linux/modversions.h \ + kernel.spec .tmp* # Files removed with 'make mrproper' -MRPROPER_FILES += \ - include/linux/autoconf.h include/linux/version.h \ - .version .config .config.old config.in config.old \ - .menuconfig.log \ - include/asm \ - .hdepend include/linux/modversions.h \ - tags TAGS cscope* kernel.spec \ - .tmp* - -# Directories removed with 'make mrproper' -MRPROPER_DIRS += \ - $(MODVERDIR) \ - .tmp_export-objs \ - include/config \ - include/linux/modules \ - include2 +MRPROPER_FILES += .version .config .config.old tags TAGS cscope* # clean - Delete all intermediate files # @@ -785,28 +775,36 @@ clean-dirs += $(addprefix _clean_,$(ALL_ $(clean-dirs): $(Q)$(MAKE) $(clean)=$(patsubst _clean_%,%,$@) -quiet_cmd_rmclean = RM $$(CLEAN_FILES) -cmd_rmclean = rm -f $(CLEAN_FILES) +clean: rm-dirs := $(wildcard $(CLEAN_DIRS)) +mrproper: rm-dirs := $(wildcard $(MRPROPER_DIRS)) +quiet_cmd_rmdirs = $(if $(rm-dirs),CLEAN $(rm-dirs)) + cmd_rmdirs = rm -rf $(rm-dirs) + +clean: rm-files := $(wildcard $(CLEAN_FILES)) +mrproper: rm-files := $(wildcard $(MRPROPER_FILES)) +quiet_cmd_rmfiles = $(if $(rm-files),CLEAN $(rm-files)) + cmd_rmfiles = rm -rf $(rm-files) + clean: archclean $(clean-dirs) - $(call cmd,rmclean) + $(call cmd,rmdirs) + $(call cmd,rmfiles) @find . $(RCS_FIND_IGNORE) \ \( -name '*.[oas]' -o -name '*.ko' -o -name '.*.cmd' \ -o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \) \ -type f -print | xargs rm -f -# mrproper - delete configuration + modules + core files +# mrproper # -quiet_cmd_mrproper = RM $$(MRPROPER_DIRS) + $$(MRPROPER_FILES) -cmd_mrproper = rm -rf $(MRPROPER_DIRS) && rm -f $(MRPROPER_FILES) -mrproper distclean: clean archmrproper - @echo ' Making $@ in the srctree' +distclean: mrproper +mrproper: clean archmrproper + $(call cmd,rmdirs) + $(call cmd,rmfiles) @find . $(RCS_FIND_IGNORE) \ \( -name '*.orig' -o -name '*.rej' -o -name '*~' \ -o -name '*.bak' -o -name '#*#' -o -name '.*.orig' \ -o -name '.*.rej' -o -size 0 \ -o -name '*%' -o -name '.*.cmd' -o -name 'core' \) \ -type f -print | xargs rm -f - $(call cmd,mrproper) # Generate tags for editors # --------------------------------------------------------------------------- --- linux-2.6.4-rc2/mm/filemap.c 2004-02-17 20:48:46.000000000 -0800 +++ 25/mm/filemap.c 2004-03-07 22:11:31.000000000 -0800 @@ -39,6 +39,7 @@ #include #include +#include /* * Shared mappings implemented 30.11.1994. It's not fully working yet, @@ -73,6 +74,9 @@ * ->mmap_sem * ->i_sem (msync) * + * ->i_sem + * ->i_alloc_sem (various) + * * ->inode_lock * ->sb_lock (fs/fs-writeback.c) * ->mapping->page_lock (__sync_single_inode) @@ -125,6 +129,8 @@ static inline int sync_page(struct page return 0; } +extern struct super_block *blockdev_superblock; + /** * filemap_fdatawrite - start writeback against all of a mapping's dirty pages * @mapping: address space structure to write @@ -141,14 +147,30 @@ static int __filemap_fdatawrite(struct a .sync_mode = sync_mode, .nr_to_write = mapping->nrpages * 2, }; + int blkdev = mapping->host->i_sb == blockdev_superblock; if (mapping->backing_dev_info->memory_backed) return 0; + if (!blkdev) { + if (sync_mode == WB_SYNC_NONE) { + if (!down_read_trylock(&mapping->wb_rwsema)) + return 0; + } else + down_write(&mapping->wb_rwsema); + } + spin_lock(&mapping->page_lock); list_splice_init(&mapping->dirty_pages, &mapping->io_pages); spin_unlock(&mapping->page_lock); ret = do_writepages(mapping, &wbc); + + if (!blkdev) { + if (sync_mode == WB_SYNC_NONE) + up_read(&mapping->wb_rwsema); + else + up_write(&mapping->wb_rwsema); + } return ret; } @@ -161,12 +183,13 @@ EXPORT_SYMBOL(filemap_fdatawrite); /* * This is a mostly non-blocking flush. Not suitable for data-integrity - * purposes. + * purposes - I/O may not be started against all dirty pages. */ int filemap_flush(struct address_space *mapping) { return __filemap_fdatawrite(mapping, WB_SYNC_NONE); } +EXPORT_SYMBOL(filemap_flush); /** * filemap_fdatawait - walk the list of locked pages of the given address @@ -185,13 +208,24 @@ restart: struct page *page; page = list_entry(mapping->locked_pages.next,struct page,list); - list_del(&page->list); - if (PageDirty(page)) - list_add(&page->list, &mapping->dirty_pages); - else + /* + * Leave page on locked list until i/o has finished + * so parallel filemap_fdatawait()s will all see the page. + */ + + if (!PageDirty(page) && !PageLocked(page) && + !PageWriteback(page)) { + + /* + * The page is checked if locked because it might + * be in process of being setup for writeback with + * PG_dirty cleared and PG_writeback not set yet. + * The page is not dirty and i/o has finished + * so we can move it to the clean list. + */ + list_del(&page->list); list_add(&page->list, &mapping->clean_pages); - if (!PageWriteback(page)) { if (++progress > 32) { if (need_resched()) { spin_unlock(&mapping->page_lock); @@ -206,10 +240,15 @@ restart: page_cache_get(page); spin_unlock(&mapping->page_lock); - wait_on_page_writeback(page); - if (PageError(page)) - ret = -EIO; - + lock_page(page); + if (PageDirty(page) && mapping->a_ops->writepage) { + write_one_page(page, 1); + } else { + wait_on_page_writeback(page); + unlock_page(page); + } + if (PageError(page)) + ret = -EIO; page_cache_release(page); spin_lock(&mapping->page_lock); } @@ -226,6 +265,18 @@ restart: EXPORT_SYMBOL(filemap_fdatawait); +int filemap_write_and_wait(struct address_space *mapping) +{ + int retval = 0; + + if (mapping->nrpages) { + retval = filemap_fdatawrite(mapping); + if (retval == 0) + retval = filemap_fdatawait(mapping); + } + return retval; +} + /* * This adds a page to the page cache, starting out as locked, unreferenced, * not uptodate and with no errors. @@ -292,7 +343,7 @@ static wait_queue_head_t *page_waitqueue return &zone->wait_table[hash_ptr(page, zone->wait_table_bits)]; } -void wait_on_page_bit(struct page *page, int bit_nr) +void fastcall wait_on_page_bit(struct page *page, int bit_nr) { wait_queue_head_t *waitqueue = page_waitqueue(page); DEFINE_WAIT(wait); @@ -324,7 +375,7 @@ EXPORT_SYMBOL(wait_on_page_bit); * the clear_bit and the read of the waitqueue (to avoid SMP races with a * parallel wait_on_page_locked()). */ -void unlock_page(struct page *page) +void fastcall unlock_page(struct page *page) { wait_queue_head_t *waitqueue = page_waitqueue(page); smp_mb__before_clear_bit(); @@ -365,7 +416,7 @@ EXPORT_SYMBOL(end_page_writeback); * chances are that on the second loop, the block layer's plug list is empty, * so sync_page() will then return in state TASK_UNINTERRUPTIBLE. */ -void __lock_page(struct page *page) +void fastcall __lock_page(struct page *page) { wait_queue_head_t *wqh = page_waitqueue(page); DEFINE_WAIT(wait); @@ -953,7 +1004,7 @@ asmlinkage ssize_t sys_readahead(int fd, * and schedules an I/O to read in its contents from disk. */ static int FASTCALL(page_cache_read(struct file * file, unsigned long offset)); -static int page_cache_read(struct file * file, unsigned long offset) +static int fastcall page_cache_read(struct file * file, unsigned long offset) { struct address_space *mapping = file->f_mapping; struct page *page; @@ -1198,8 +1249,13 @@ retry_find: * Ok, found a page in the page cache, now we need to check * that it's up-to-date. */ - if (!PageUptodate(page)) + if (!PageUptodate(page)) { + if (nonblock) { + page_cache_release(page); + return NULL; + } goto page_not_uptodate; + } success: /* @@ -1292,12 +1348,22 @@ static int filemap_populate(struct vm_ar { struct file *file = vma->vm_file; struct address_space *mapping = file->f_mapping; + int linear = !(vma->vm_flags & VM_NONLINEAR); struct inode *inode = mapping->host; unsigned long size; struct mm_struct *mm = vma->vm_mm; struct page *page; int err; + /* + * mapping-removal fastpath: + */ + if ((vma->vm_flags & VM_SHARED) && + (pgprot_val(prot) == pgprot_val(PAGE_NONE))) { + zap_page_range(vma, addr, len); + return 0; + } + if (!nonblock) force_page_cache_readahead(mapping, vma->vm_file, pgoff, len >> PAGE_CACHE_SHIFT); @@ -1317,19 +1383,9 @@ repeat: return err; } } else { - /* - * If a nonlinear mapping then store the file page offset - * in the pte. - */ - unsigned long pgidx; - pgidx = (addr - vma->vm_start) >> PAGE_SHIFT; - pgidx += vma->vm_pgoff; - pgidx >>= PAGE_CACHE_SHIFT - PAGE_SHIFT; - if (pgoff != pgidx) { - err = install_file_pte(mm, vma, addr, pgoff, prot); - if (err) - return err; - } + err = install_file_pte(mm, vma, addr, pgoff, prot, linear); + if (err) + return err; } len -= PAGE_SIZE; @@ -1713,6 +1769,7 @@ EXPORT_SYMBOL(generic_write_checks); /* * Write to a file through the page cache. + * Called under i_sem for S_ISREG files. * * We put everything into the page cache prior to writing it. This is not a * problem when writing full pages. With partial pages, however, we first have @@ -1801,12 +1858,21 @@ generic_file_aio_write_nolock(struct kio /* * Sync the fs metadata but not the minor inode changes and * of course not the data as we did direct DMA for the IO. + * i_sem is held, which protects generic_osync_inode() from + * livelocking. */ if (written >= 0 && file->f_flags & O_SYNC) status = generic_osync_inode(inode, mapping, OSYNC_METADATA); - if (written >= 0 && !is_sync_kiocb(iocb)) + if (written == count && !is_sync_kiocb(iocb)) written = -EIOCBQUEUED; - goto out_status; + if (written < 0 || written == count) + goto out_status; + /* + * direct-io write to a hole: fall through to buffered I/O + * for completing the rest of the request. + */ + pos += written; + count -= written; } buf = iov->iov_base; @@ -1895,6 +1961,14 @@ generic_file_aio_write_nolock(struct kio OSYNC_METADATA|OSYNC_DATA); } + /* + * If we get here for O_DIRECT writes then we must have fallen through + * to buffered writes (block instantiation inside i_size). So we sync + * the file data here, to try to honour O_DIRECT expectations. + */ + if (unlikely(file->f_flags & O_DIRECT) && written) + status = filemap_write_and_wait(mapping); + out_status: err = written ? written : status; out: @@ -1986,6 +2060,9 @@ ssize_t generic_file_writev(struct file EXPORT_SYMBOL(generic_file_writev); +/* + * Called under i_sem for writes to S_ISREG files + */ ssize_t generic_file_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, loff_t offset, unsigned long nr_segs) @@ -1994,18 +2071,13 @@ generic_file_direct_IO(int rw, struct ki struct address_space *mapping = file->f_mapping; ssize_t retval; - if (mapping->nrpages) { - retval = filemap_fdatawrite(mapping); - if (retval == 0) - retval = filemap_fdatawait(mapping); - if (retval) - goto out; + retval = filemap_write_and_wait(mapping); + if (retval == 0) { + retval = mapping->a_ops->direct_IO(rw, iocb, iov, + offset, nr_segs); + if (rw == WRITE && mapping->nrpages) + invalidate_inode_pages2(mapping); } - - retval = mapping->a_ops->direct_IO(rw, iocb, iov, offset, nr_segs); - if (rw == WRITE && mapping->nrpages) - invalidate_inode_pages2(mapping); -out: return retval; } --- linux-2.6.4-rc2/mm/fremap.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/mm/fremap.c 2004-03-07 20:48:04.000000000 -0800 @@ -38,7 +38,7 @@ static inline void zap_pte(struct mm_str set_page_dirty(page); page_remove_rmap(page, ptep); page_cache_release(page); - mm->rss--; + dec_rss(mm, page); } } } else { @@ -53,7 +53,7 @@ static inline void zap_pte(struct mm_str * previously existing mapping. */ int install_page(struct mm_struct *mm, struct vm_area_struct *vma, - unsigned long addr, struct page *page, pgprot_t prot) + unsigned long addr, struct page *page, pgprot_t pgprot) { int err = -ENOMEM; pte_t *pte; @@ -75,12 +75,19 @@ int install_page(struct mm_struct *mm, s pte = pte_alloc_map(mm, pmd, addr); if (!pte) goto err_unlock; + /* + * Only install a new page for a non-shared mapping if it's + * not existent yet: + */ + err = -EEXIST; + if (!pte_none(*pte) && !(vma->vm_flags & VM_SHARED)) + goto err_unlock; zap_pte(mm, vma, addr, pte); - mm->rss++; + inc_rss(mm, page); flush_icache_page(vma, page); - set_pte(pte, mk_pte(page, prot)); + set_pte(pte, mk_pte(page, pgprot)); pte_chain = page_add_rmap(page, pte, pte_chain); pte_val = *pte; pte_unmap(pte); @@ -103,7 +110,7 @@ EXPORT_SYMBOL(install_page); * previously existing mapping. */ int install_file_pte(struct mm_struct *mm, struct vm_area_struct *vma, - unsigned long addr, unsigned long pgoff, pgprot_t prot) + unsigned long addr, unsigned long pgoff, pgprot_t pgprot, int linear) { int err = -ENOMEM; pte_t *pte; @@ -111,6 +118,8 @@ int install_file_pte(struct mm_struct *m pmd_t *pmd; pte_t pte_val; + BUG_ON(!linear && !(vma->vm_flags & VM_SHARED)); + pgd = pgd_offset(mm, addr); spin_lock(&mm->page_table_lock); @@ -121,10 +130,23 @@ int install_file_pte(struct mm_struct *m pte = pte_alloc_map(mm, pmd, addr); if (!pte) goto err_unlock; + /* + * Skip linear non-existent ptes: + */ + err = 0; + if (linear && pte_none(*pte)) + goto err_unlock; + /* + * Only install a new page for a non-shared mapping if it's + * not existent yet: + */ + err = -EEXIST; + if (!pte_none(*pte) && !(vma->vm_flags & VM_SHARED)) + goto err_unlock; zap_pte(mm, vma, addr, pte); - set_pte(pte, pgoff_to_pte(pgoff)); + set_pte(pte, pgoff_prot_to_pte(pgoff, pgprot)); pte_val = *pte; pte_unmap(pte); update_mmu_cache(vma, addr, pte_val); @@ -144,27 +166,22 @@ err_unlock: * @size: size of the remapped virtual memory range * @prot: new protection bits of the range * @pgoff: to be mapped page of the backing store file - * @flags: 0 or MAP_NONBLOCKED - the later will cause no IO. + * @flags: bits MAP_INHERIT or MAP_NONBLOCKED - the later will cause no IO. * * this syscall works purely via pagetables, so it's the most efficient * way to map the same (large) file into a given virtual window. Unlike * mmap()/mremap() it does not create any new vmas. The new mappings are * also safe across swapout. - * - * NOTE: the 'prot' parameter right now is ignored, and the vma's default - * protection is used. Arbitrary protections might be implemented in the - * future. */ -asmlinkage long sys_remap_file_pages(unsigned long start, unsigned long size, - unsigned long __prot, unsigned long pgoff, unsigned long flags) +long __remap_file_pages(struct mm_struct *mm, unsigned long start, + unsigned long size, unsigned long prot, + unsigned long pgoff, unsigned long flags) { - struct mm_struct *mm = current->mm; + pgprot_t pgprot = protection_map[calc_vm_prot_bits(prot) | VM_SHARED]; unsigned long end = start + size; struct vm_area_struct *vma; int err = -EINVAL; - if (__prot) - return err; /* * Sanitize the syscall parameters: */ @@ -184,37 +201,71 @@ asmlinkage long sys_remap_file_pages(uns /* We need down_write() to change vma->vm_flags. */ down_write(&mm->mmap_sem); vma = find_vma(mm, start); - /* - * Make sure the vma is shared, that it supports prefaulting, - * and that the remapped range is valid and fully within - * the single existing vma: - */ - if (vma && (vma->vm_flags & VM_SHARED) && - vma->vm_ops && vma->vm_ops->populate && - end > start && start >= vma->vm_start && - end <= vma->vm_end) { - - /* Must set VM_NONLINEAR before any pages are populated. */ - if (pgoff != ((start - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff) - vma->vm_flags |= VM_NONLINEAR; - - /* ->populate can take a long time, so downgrade the lock. */ - downgrade_write(&mm->mmap_sem); - err = vma->vm_ops->populate(vma, start, size, - vma->vm_page_prot, - pgoff, flags & MAP_NONBLOCK); - - /* - * We can't clear VM_NONLINEAR because we'd have to do - * it after ->populate completes, and that would prevent - * downgrading the lock. (Locks can't be upgraded). - */ - up_read(&mm->mmap_sem); - } else { - up_write(&mm->mmap_sem); + * Make sure the permissions are right, the vma is shared + * (or linearly remapped - ie. prefaulted), that it supports + * prefaulting, and that the remapped range is valid and fully + * within the single existing vma: + */ + if (!vma) + goto out_unlock; + if (unlikely(flags & MAP_INHERIT)) + pgprot = vma->vm_page_prot; + else { + err = -EPERM; + if (((prot & PROT_READ) && !(vma->vm_flags & VM_MAYREAD))) + goto out_unlock; + if (((prot & PROT_WRITE) && !(vma->vm_flags & VM_MAYWRITE))) + goto out_unlock; + if (((prot & PROT_EXEC) && !(vma->vm_flags & VM_MAYEXEC))) + goto out_unlock; + } + + if (!vma->vm_ops || !vma->vm_ops->populate || end <= start || + start < vma->vm_start || end > vma->vm_end) + goto out_unlock; + + if (pgoff != ((start - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff) { + if (!(vma->vm_flags & VM_SHARED)) + goto out_unlock; + vma->vm_flags |= VM_NONLINEAR; } + /* + * ->populate can take a long time, so downgrade the lock: + */ + downgrade_write(&mm->mmap_sem); + err = vma->vm_ops->populate(vma, start, size, + pgprot, pgoff, flags & MAP_NONBLOCK); + + /* + * We can't clear VM_NONLINEAR because we'd have to do + * it after ->populate completes, and that would prevent + * downgrading the lock. (Locks can't be upgraded). + */ + up_read(&mm->mmap_sem); + return err; + +out_unlock: + up_write(&mm->mmap_sem); return err; } +asmlinkage long sys_remap_file_pages(unsigned long start, unsigned long size, + unsigned long prot, unsigned long pgoff, unsigned long flags) +{ + return __remap_file_pages(current->mm, start, size, prot, pgoff, flags); +} + +/* + * sys_remap_file_pages - the old API. Implies MAP_INHERIT. + */ +asmlinkage long old_remap_file_pages(unsigned long start, unsigned long size, + unsigned long __prot, unsigned long pgoff, unsigned long flags) +{ + if (__prot) + return -EINVAL; + + return __remap_file_pages(current->mm, start, size, PROT_NONE, + pgoff, flags | MAP_INHERIT); +} --- linux-2.6.4-rc2/mm/highmem.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/mm/highmem.c 2004-03-07 20:46:46.000000000 -0800 @@ -147,7 +147,7 @@ start: return vaddr; } -void *kmap_high(struct page *page) +void fastcall *kmap_high(struct page *page) { unsigned long vaddr; @@ -170,7 +170,7 @@ void *kmap_high(struct page *page) EXPORT_SYMBOL(kmap_high); -void kunmap_high(struct page *page) +void fastcall kunmap_high(struct page *page) { unsigned long vaddr; unsigned long nr; --- linux-2.6.4-rc2/mm/Makefile 2003-11-09 16:45:06.000000000 -0800 +++ 25/mm/Makefile 2004-03-07 20:48:18.000000000 -0800 @@ -12,3 +12,4 @@ obj-y := bootmem.o filemap.o mempool.o slab.o swap.o truncate.o vmscan.o $(mmu-y) obj-$(CONFIG_SWAP) += page_io.o swap_state.o swapfile.o +obj-$(CONFIG_X86_4G) += usercopy.o --- linux-2.6.4-rc2/mm/memory.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/mm/memory.c 2004-03-07 20:48:18.000000000 -0800 @@ -109,7 +109,8 @@ static inline void free_one_pmd(struct m pte_free_tlb(tlb, page); } -static inline void free_one_pgd(struct mmu_gather *tlb, pgd_t * dir) +static inline void free_one_pgd(struct mmu_gather *tlb, pgd_t * dir, + int pgd_idx) { int j; pmd_t * pmd; @@ -123,8 +124,11 @@ static inline void free_one_pgd(struct m } pmd = pmd_offset(dir, 0); pgd_clear(dir); - for (j = 0; j < PTRS_PER_PMD ; j++) + for (j = 0; j < PTRS_PER_PMD ; j++) { + if (pgd_idx * PGDIR_SIZE + j * PMD_SIZE >= TASK_SIZE) + break; free_one_pmd(tlb, pmd+j); + } pmd_free_tlb(tlb, pmd); } @@ -137,15 +141,17 @@ static inline void free_one_pgd(struct m void clear_page_tables(struct mmu_gather *tlb, unsigned long first, int nr) { pgd_t * page_dir = tlb->mm->pgd; + int pgd_idx = first; page_dir += first; do { - free_one_pgd(tlb, page_dir); + free_one_pgd(tlb, page_dir, pgd_idx); page_dir++; + pgd_idx++; } while (--nr); } -pte_t * pte_alloc_map(struct mm_struct *mm, pmd_t *pmd, unsigned long address) +pte_t fastcall * pte_alloc_map(struct mm_struct *mm, pmd_t *pmd, unsigned long address) { if (!pmd_present(*pmd)) { struct page *new; @@ -171,7 +177,7 @@ out: return pte_offset_map(pmd, address); } -pte_t * pte_alloc_kernel(struct mm_struct *mm, pmd_t *pmd, unsigned long address) +pte_t fastcall * pte_alloc_kernel(struct mm_struct *mm, pmd_t *pmd, unsigned long address) { if (!pmd_present(*pmd)) { pte_t *new; @@ -328,7 +334,7 @@ skip_copy_pte_range: pte = pte_mkclean(pte); pte = pte_mkold(pte); get_page(page); - dst->rss++; + inc_rss(dst, page); set_pte(dst_pte, pte); pte_chain = page_add_rmap(page, dst_pte, @@ -420,7 +426,14 @@ zap_pte_range(struct mmu_gather *tlb, pm if (page->mapping && pte_young(pte) && !PageSwapCache(page)) mark_page_accessed(page); - tlb->freed++; + /* + * While we have the page that is being + * freed handy, make sure we decrement + * the mm's RSS accordingly. This is + * only important for NUMA per-node + * RSS accounting. + */ + dec_rss(tlb->mm, page); page_remove_rmap(page, ptep); tlb_remove_page(tlb, page); } @@ -439,7 +452,7 @@ zap_pmd_range(struct mmu_gather *tlb, pg unsigned long address, unsigned long size) { pmd_t * pmd; - unsigned long end; + unsigned long end, pgd_boundary; if (pgd_none(*dir)) return; @@ -450,8 +463,9 @@ zap_pmd_range(struct mmu_gather *tlb, pg } pmd = pmd_offset(dir, address); end = address + size; - if (end > ((address + PGDIR_SIZE) & PGDIR_MASK)) - end = ((address + PGDIR_SIZE) & PGDIR_MASK); + pgd_boundary = ((address + PGDIR_SIZE) & PGDIR_MASK); + if (pgd_boundary && (end > pgd_boundary)) + end = pgd_boundary; do { zap_pte_range(tlb, pmd, address, end - address); address = (address + PMD_SIZE) & PMD_MASK; @@ -606,6 +620,11 @@ void zap_page_range(struct vm_area_struc might_sleep(); if (is_vm_hugetlb_page(vma)) { + static int x; + if (x < 10) { + x++; + dump_stack(); + } zap_hugepage_range(vma, address, size); return; } @@ -693,6 +712,7 @@ int get_user_pages(struct task_struct *t struct page **pages, struct vm_area_struct **vmas) { int i; + int vm_io; unsigned int flags; /* @@ -736,8 +756,10 @@ int get_user_pages(struct task_struct *t continue; } - if (!vma || (pages && (vma->vm_flags & VM_IO)) - || !(flags & vma->vm_flags)) + if (!vma) + return i ? : -EFAULT; + vm_io = vma->vm_flags & VM_IO; + if ((pages && vm_io) || !(flags & vma->vm_flags)) return i ? : -EFAULT; if (is_vm_hugetlb_page(vma)) { @@ -747,8 +769,15 @@ int get_user_pages(struct task_struct *t } spin_lock(&mm->page_table_lock); do { - struct page *map; + struct page *map = NULL; int lookup_write = write; + + /* + * We don't follow pagetables for VM_IO regions - they + * may have no pageframes. + */ + if (vm_io) + goto no_follow; while (!(map = follow_page(mm, start, lookup_write))) { spin_unlock(&mm->page_table_lock); switch (handle_mm_fault(mm,vma,start,write)) { @@ -788,6 +817,7 @@ int get_user_pages(struct task_struct *t if (!PageReserved(pages[i])) page_cache_get(pages[i]); } +no_follow: if (vmas) vmas[i] = vma; i++; @@ -1068,7 +1098,7 @@ static int do_wp_page(struct mm_struct * page_table = pte_offset_map(pmd, address); if (pte_same(*page_table, pte)) { if (PageReserved(old_page)) - ++mm->rss; + inc_rss(mm, new_page); page_remove_rmap(old_page, page_table); break_cow(vma, new_page, address, page_table); pte_chain = page_add_rmap(new_page, page_table, pte_chain); @@ -1302,7 +1332,7 @@ static int do_swap_page(struct mm_struct if (vm_swap_full()) remove_exclusive_swap_page(page); - mm->rss++; + inc_rss(mm, page); pte = mk_pte(page, vma->vm_page_prot); if (write_access && can_share_swap_page(page)) pte = maybe_mkwrite(pte_mkdirty(pte), vma); @@ -1371,7 +1401,7 @@ do_anonymous_page(struct mm_struct *mm, ret = VM_FAULT_MINOR; goto out; } - mm->rss++; + inc_rss(mm, page); entry = maybe_mkwrite(pte_mkdirty(mk_pte(page, vma->vm_page_prot)), vma); @@ -1486,7 +1516,7 @@ retry: /* Only go through if we didn't race with anybody else... */ if (pte_none(*page_table)) { if (!PageReserved(new_page)) - ++mm->rss; + inc_rss(mm, new_page); flush_icache_page(vma, new_page); entry = mk_pte(new_page, vma->vm_page_prot); if (write_access) @@ -1523,6 +1553,7 @@ static int do_file_page(struct mm_struct unsigned long address, int write_access, pte_t *pte, pmd_t *pmd) { unsigned long pgoff; + pgprot_t pgprot; int err; BUG_ON(!vma->vm_ops || !vma->vm_ops->nopage); @@ -1537,11 +1568,12 @@ static int do_file_page(struct mm_struct } pgoff = pte_to_pgoff(*pte); + pgprot = pte_to_pgprot(*pte); pte_unmap(pte); spin_unlock(&mm->page_table_lock); - err = vma->vm_ops->populate(vma, address & PAGE_MASK, PAGE_SIZE, vma->vm_page_prot, pgoff, 0); + err = vma->vm_ops->populate(vma, address & PAGE_MASK, PAGE_SIZE, pgprot, pgoff, 0); if (err == -ENOMEM) return VM_FAULT_OOM; if (err) @@ -1590,6 +1622,15 @@ static inline int handle_pte_fault(struc return do_swap_page(mm, vma, address, pte, pmd, entry, write_access); } + /* + * Generate a SIGSEGV if a PROT_NONE page is accessed: + */ + if (pgprot_val(pte_to_pgprot(entry)) == pgprot_val(__P000)) { + pte_unmap(pte); + spin_unlock(&mm->page_table_lock); + return VM_FAULT_SIGSEGV; + } + if (write_access) { if (!pte_write(entry)) return do_wp_page(mm, vma, address, pte, pmd, entry); @@ -1646,7 +1687,7 @@ int handle_mm_fault(struct mm_struct *mm * On a two-level page table, this ends up actually being entirely * optimized away. */ -pmd_t *__pmd_alloc(struct mm_struct *mm, pgd_t *pgd, unsigned long address) +pmd_t fastcall *__pmd_alloc(struct mm_struct *mm, pgd_t *pgd, unsigned long address) { pmd_t *new; --- linux-2.6.4-rc2/mm/mmap.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/mm/mmap.c 2004-03-07 20:48:04.000000000 -0800 @@ -222,8 +222,8 @@ __vma_link_list(struct mm_struct *mm, st } } -static void __vma_link_rb(struct mm_struct *mm, struct vm_area_struct *vma, - struct rb_node **rb_link, struct rb_node *rb_parent) +void __vma_link_rb(struct mm_struct *mm, struct vm_area_struct *vma, + struct rb_node **rb_link, struct rb_node *rb_parent) { rb_link_node(&vma->vm_rb, rb_parent, rb_link); rb_insert_color(&vma->vm_rb, &mm->mm_rb); @@ -691,8 +691,12 @@ out: } if (flags & MAP_POPULATE) { up_write(&mm->mmap_sem); - sys_remap_file_pages(addr, len, prot, - pgoff, flags & MAP_NONBLOCK); + /* + * remap_file_pages() works even if the mapping is private, + * in the linearly-mapped case: + */ + __remap_file_pages(mm, addr, len, PROT_NONE, pgoff, + MAP_INHERIT | (flags & MAP_NONBLOCK)); down_write(&mm->mmap_sem); } return addr; @@ -1404,22 +1408,6 @@ out: EXPORT_SYMBOL(do_brk); -/* Build the RB tree corresponding to the VMA list. */ -void build_mmap_rb(struct mm_struct * mm) -{ - struct vm_area_struct * vma; - struct rb_node ** rb_link, * rb_parent; - - mm->mm_rb = RB_ROOT; - rb_link = &mm->mm_rb.rb_node; - rb_parent = NULL; - for (vma = mm->mmap; vma; vma = vma->vm_next) { - __vma_link_rb(mm, vma, rb_link, rb_parent); - rb_parent = &vma->vm_rb; - rb_link = &rb_parent->rb_right; - } -} - /* Release all mmaps. */ void exit_mmap(struct mm_struct *mm) { @@ -1446,7 +1434,7 @@ void exit_mmap(struct mm_struct *mm) vma = mm->mmap; mm->mmap = mm->mmap_cache = NULL; mm->mm_rb = RB_ROOT; - mm->rss = 0; + zero_rss(mm); mm->total_vm = 0; mm->locked_vm = 0; --- linux-2.6.4-rc2/mm/page_alloc.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/mm/page_alloc.c 2004-03-07 20:48:15.000000000 -0800 @@ -390,6 +390,27 @@ static int rmqueue_bulk(struct zone *zon return allocated; } +#if defined(CONFIG_PM) || defined(CONFIG_HOTPLUG_CPU) +static void __drain_pages(unsigned int cpu) +{ + struct zone *zone; + int i; + + for_each_zone(zone) { + struct per_cpu_pageset *pset; + + pset = &zone->pageset[cpu]; + for (i = 0; i < ARRAY_SIZE(pset->pcp); i++) { + struct per_cpu_pages *pcp; + + pcp = &pset->pcp[i]; + pcp->count -= free_pages_bulk(zone, pcp->count, + &pcp->list, 0); + } + } +} +#endif /* CONFIG_PM || CONFIG_HOTPLUG_CPU */ + #ifdef CONFIG_PM int is_head_of_free_region(struct page *page) { @@ -419,22 +440,9 @@ int is_head_of_free_region(struct page * void drain_local_pages(void) { unsigned long flags; - struct zone *zone; - int i; local_irq_save(flags); - for_each_zone(zone) { - struct per_cpu_pageset *pset; - - pset = &zone->pageset[smp_processor_id()]; - for (i = 0; i < ARRAY_SIZE(pset->pcp); i++) { - struct per_cpu_pages *pcp; - - pcp = &pset->pcp[i]; - pcp->count -= free_pages_bulk(zone, pcp->count, - &pcp->list, 0); - } - } + __drain_pages(smp_processor_id()); local_irq_restore(flags); } #endif /* CONFIG_PM */ @@ -443,7 +451,7 @@ void drain_local_pages(void) * Free a 0-order page */ static void FASTCALL(free_hot_cold_page(struct page *page, int cold)); -static void free_hot_cold_page(struct page *page, int cold) +static void fastcall free_hot_cold_page(struct page *page, int cold) { struct zone *zone = page_zone(page); struct per_cpu_pages *pcp; @@ -462,12 +470,12 @@ static void free_hot_cold_page(struct pa put_cpu(); } -void free_hot_page(struct page *page) +void fastcall free_hot_page(struct page *page) { free_hot_cold_page(page, 0); } -void free_cold_page(struct page *page) +void fastcall free_cold_page(struct page *page) { free_hot_cold_page(page, 1); } @@ -510,7 +518,7 @@ static struct page *buffered_rmqueue(str if (page != NULL) { BUG_ON(bad_range(zone, page)); - mod_page_state(pgalloc, 1 << order); + mod_page_state_zone(zone, pgalloc, 1 << order); prep_new_page(page, order); } return page; @@ -532,7 +540,7 @@ static struct page *buffered_rmqueue(str * sized machine, GFP_HIGHMEM and GFP_KERNEL requests basically leave the DMA * zone untouched. */ -struct page * +struct page * fastcall __alloc_pages(unsigned int gfp_mask, unsigned int order, struct zonelist *zonelist) { @@ -682,13 +690,53 @@ got_pg: EXPORT_SYMBOL(__alloc_pages); +#ifdef CONFIG_NUMA +/* Early boot: Everything is done by one cpu, but the data structures will be + * used by all cpus - spread them on all nodes. + */ +static __init unsigned long get_boot_pages(unsigned int gfp_mask, unsigned int order) +{ +static int nodenr; + int i = nodenr; + struct page *page; + + for (;;) { + if (i > nodenr + numnodes) + return 0; + if (node_present_pages(i%numnodes)) { + struct zone **z; + /* The node contains memory. Check that there is + * memory in the intended zonelist. + */ + z = NODE_DATA(i%numnodes)->node_zonelists[gfp_mask & GFP_ZONEMASK].zones; + while (*z) { + if ( (*z)->free_pages > (1UL<pages[i], pvec->cold); } -void __free_pages(struct page *page, unsigned int order) +fastcall void __free_pages(struct page *page, unsigned int order) { if (!PageReserved(page) && put_page_testzero(page)) { if (order == 0) @@ -738,7 +786,7 @@ void __free_pages(struct page *page, uns EXPORT_SYMBOL(__free_pages); -void free_pages(unsigned long addr, unsigned int order) +fastcall void free_pages(unsigned long addr, unsigned int order) { if (addr != 0) { BUG_ON(!virt_addr_valid(addr)); @@ -1013,6 +1061,7 @@ void show_free_areas(void) " high:%lukB" " active:%lukB" " inactive:%lukB" + " present:%lukB" "\n", zone->name, K(zone->free_pages), @@ -1020,7 +1069,8 @@ void show_free_areas(void) K(zone->pages_low), K(zone->pages_high), K(zone->nr_active), - K(zone->nr_inactive) + K(zone->nr_inactive), + K(zone->present_pages) ); } @@ -1080,6 +1130,109 @@ static int __init build_zonelists_node(p return j; } +#ifdef CONFIG_NUMA +#define MAX_NODE_LOAD (numnodes) +static int __initdata node_load[MAX_NUMNODES]; +/** + * find_next_best_node - find the next node that should appear in a given + * node's fallback list + * @node: node whose fallback list we're appending + * @used_node_mask: pointer to the bitmap of already used nodes + * + * We use a number of factors to determine which is the next node that should + * appear on a given node's fallback list. The node should not have appeared + * already in @node's fallback list, and it should be the next closest node + * according to the distance array (which contains arbitrary distance values + * from each node to each node in the system), and should also prefer nodes + * with no CPUs, since presumably they'll have very little allocation pressure + * on them otherwise. + * It returns -1 if no node is found. + */ +static int __init find_next_best_node(int node, void *used_node_mask) +{ + int i, n, val; + int min_val = INT_MAX; + int best_node = -1; + + for (i = 0; i < numnodes; i++) { + /* Start from local node */ + n = (node+i)%numnodes; + + /* Don't want a node to appear more than once */ + if (test_bit(n, used_node_mask)) + continue; + + /* Use the distance array to find the distance */ + val = node_distance(node, n); + + /* Give preference to headless and unused nodes */ + if (!cpus_empty(node_to_cpumask(n))) + val += PENALTY_FOR_NODE_WITH_CPUS; + + /* Slight preference for less loaded node */ + val *= (MAX_NODE_LOAD*MAX_NUMNODES); + val += node_load[n]; + + if (val < min_val) { + min_val = val; + best_node = n; + } + } + + if (best_node >= 0) + set_bit(best_node, used_node_mask); + + return best_node; +} + +static void __init build_zonelists(pg_data_t *pgdat) +{ + int i, j, k, node, local_node; + int prev_node, load; + struct zonelist *zonelist; + DECLARE_BITMAP(used_mask, MAX_NUMNODES); + + /* initialize zonelists */ + for (i = 0; i < MAX_NR_ZONES; i++) { + zonelist = pgdat->node_zonelists + i; + memset(zonelist, 0, sizeof(*zonelist)); + zonelist->zones[0] = NULL; + } + + /* NUMA-aware ordering of nodes */ + local_node = pgdat->node_id; + load = numnodes; + prev_node = local_node; + CLEAR_BITMAP(used_mask, MAX_NUMNODES); + while ((node = find_next_best_node(local_node, used_mask)) >= 0) { + /* + * We don't want to pressure a particular node. + * So adding penalty to the first node in same + * distance group to make it round-robin. + */ + if (node_distance(local_node, node) != + node_distance(local_node, prev_node)) + node_load[node] += load; + prev_node = node; + load--; + for (i = 0; i < MAX_NR_ZONES; i++) { + zonelist = pgdat->node_zonelists + i; + for (j = 0; zonelist->zones[j] != NULL; j++); + + k = ZONE_NORMAL; + if (i & __GFP_HIGHMEM) + k = ZONE_HIGHMEM; + if (i & __GFP_DMA) + k = ZONE_DMA; + + j = build_zonelists_node(NODE_DATA(node), zonelist, j, k); + zonelist->zones[j] = NULL; + } + } +} + +#else /* CONFIG_NUMA */ + static void __init build_zonelists(pg_data_t *pgdat) { int i, j, k, node, local_node; @@ -1116,6 +1269,8 @@ static void __init build_zonelists(pg_da } } +#endif /* CONFIG_NUMA */ + void __init build_all_zonelists(void) { int i; @@ -1290,7 +1445,8 @@ static void __init free_area_init_core(s zone_names[j], realsize, batch); INIT_LIST_HEAD(&zone->active_list); INIT_LIST_HEAD(&zone->inactive_list); - atomic_set(&zone->refill_counter, 0); + atomic_set(&zone->nr_scan_active, 0); + atomic_set(&zone->nr_scan_inactive, 0); zone->nr_active = 0; zone->nr_inactive = 0; if (!size) @@ -1473,23 +1629,38 @@ static char *vmstat_text[] = { "pgpgout", "pswpin", "pswpout", - "pgalloc", + "pgalloc_high", + "pgalloc_normal", + "pgalloc_dma", "pgfree", "pgactivate", "pgdeactivate", + "pgfault", "pgmajfault", - - "pgscan", - "pgrefill", - "pgsteal", + "pgrefill_high", + "pgrefill_normal", + "pgrefill_dma", + + "pgsteal_high", + "pgsteal_normal", + "pgsteal_dma", + "pgscan_kswapd_high", + "pgscan_kswapd_normal", + + "pgscan_kswapd_dma", + "pgscan_direct_high", + "pgscan_direct_normal", + "pgscan_direct_dma", "pginodesteal", - "kswapd_steal", + "slabs_scanned", + "kswapd_steal", "kswapd_inodesteal", "pageoutrun", "allocstall", + "pgrotated", }; @@ -1542,9 +1713,27 @@ struct seq_operations vmstat_op = { #endif /* CONFIG_PROC_FS */ +#ifdef CONFIG_HOTPLUG_CPU +static int page_alloc_cpu_notify(struct notifier_block *self, + unsigned long action, void *hcpu) +{ + int cpu = (unsigned long)hcpu; + long *count; + + if (action == CPU_DEAD) { + /* Drain local pagecache count. */ + count = &per_cpu(nr_pagecache_local, cpu); + atomic_add(*count, &nr_pagecache); + *count = 0; + __drain_pages(cpu); + } + return NOTIFY_OK; +} +#endif /* CONFIG_HOTPLUG_CPU */ void __init page_alloc_init(void) { + hotcpu_notifier(page_alloc_cpu_notify, 0); } /* --- linux-2.6.4-rc2/mm/page-writeback.c 2004-02-03 20:42:39.000000000 -0800 +++ 25/mm/page-writeback.c 2004-03-07 20:48:24.000000000 -0800 @@ -28,6 +28,7 @@ #include #include #include +#include /* * The maximum number of pages to writeout in a single bdflush/kupdate @@ -81,6 +82,16 @@ int dirty_writeback_centisecs = 5 * 100; */ int dirty_expire_centisecs = 30 * 100; +/* + * Flag that makes the machine dump writes/reads and block dirtyings. + */ +int block_dump; + +/* + * Flag that puts the machine in "laptop mode". + */ +int laptop_mode; + /* End of sysctl-exported parameters */ @@ -195,7 +206,7 @@ static void balance_dirty_pages(struct a if (nr_reclaimable + ps.nr_writeback <= dirty_thresh) dirty_exceeded = 0; - if (!writeback_in_progress(bdi) && nr_reclaimable > background_thresh) + if (!unlikely(laptop_mode) && !writeback_in_progress(bdi) && nr_reclaimable > background_thresh) pdflush_operation(background_writeout, 0); } @@ -289,7 +300,17 @@ int wakeup_bdflush(long nr_pages) return pdflush_operation(background_writeout, nr_pages); } -static struct timer_list wb_timer; + +static void wb_timer_fn(unsigned long unused); + +/* + * Both timers share the same handler + */ +static struct timer_list wb_timer = + TIMER_INITIALIZER(wb_timer_fn, 0, 0); +static struct timer_list laptop_mode_wb_timer = + TIMER_INITIALIZER(wb_timer_fn, 0, 0); + /* * Periodic writeback of "old" data. @@ -328,6 +349,8 @@ static void wb_kupdate(unsigned long arg oldest_jif = jiffies - (dirty_expire_centisecs * HZ) / 100; start_jif = jiffies; next_jif = start_jif + (dirty_writeback_centisecs * HZ) / 100; + if (laptop_mode) + wbc.older_than_this = NULL; nr_to_write = ps.nr_dirty + ps.nr_unstable + (inodes_stat.nr_inodes - inodes_stat.nr_unused); while (nr_to_write > 0) { @@ -346,8 +369,10 @@ static void wb_kupdate(unsigned long arg next_jif = jiffies + HZ; if (dirty_writeback_centisecs) mod_timer(&wb_timer, next_jif); + del_timer(&laptop_mode_wb_timer); /* May have been set as a result of our writes. */ } + /* * sysctl handler for /proc/sys/vm/dirty_writeback_centisecs */ @@ -364,13 +389,31 @@ int dirty_writeback_centisecs_handler(ct return 0; } +/* + * We've spun up the disk and we're in laptop mode: schedule writeback + * of all dirty data in 5 seconds. + * + * Laptop mode writeback will be delayed if it has previously been + * scheduled to occur within 5 seconds. That way, the writeback will + * only be triggered if the system is truly quiet again. + */ +void disk_is_spun_up(int postpone_writeback) +{ + if (postpone_writeback || !timer_pending(&laptop_mode_wb_timer)) + mod_timer(&laptop_mode_wb_timer, jiffies + 5 * HZ); +} + + +/* + * Handler for wb_timer and laptop_mode_wb_timer. + */ static void wb_timer_fn(unsigned long unused) { if (pdflush_operation(wb_kupdate, 0) < 0) mod_timer(&wb_timer, jiffies + HZ); /* delay 1 second */ - } + /* * If ratelimit_pages is too high then we can get into dirty-data overload * if a large number of processes all perform writes at the same time. @@ -430,11 +473,9 @@ void __init page_writeback_init(void) vm_dirty_ratio /= 100; } - init_timer(&wb_timer); wb_timer.expires = jiffies + (dirty_writeback_centisecs * HZ) / 100; - wb_timer.data = 0; - wb_timer.function = wb_timer_fn; add_timer(&wb_timer); + set_ratelimit(); register_cpu_notifier(&ratelimit_nb); } @@ -526,6 +567,8 @@ int __set_page_dirty_nobuffers(struct pa __mark_inode_dirty(mapping->host, I_DIRTY_PAGES); } + if (unlikely(block_dump)) + printk("%s(%d): dirtied page\n", current->comm, current->pid); } return ret; } --- linux-2.6.4-rc2/mm/pdflush.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/mm/pdflush.c 2004-03-07 20:47:10.000000000 -0800 @@ -88,6 +88,8 @@ struct pdflush_work { unsigned long when_i_went_to_sleep; }; +static int wakeup_count = 100; + static int __pdflush(struct pdflush_work *my_work) { current->flags |= PF_FLUSHER; @@ -114,7 +116,10 @@ static int __pdflush(struct pdflush_work spin_lock_irq(&pdflush_lock); if (!list_empty(&my_work->list)) { - printk("pdflush: bogus wakeup!\n"); + if (wakeup_count > 0) { + wakeup_count--; + printk("pdflush: bogus wakeup!\n"); + } my_work->fn = NULL; continue; } @@ -184,6 +189,7 @@ int pdflush_operation(void (*fn)(unsigne { unsigned long flags; int ret = 0; + static int poke_count = 0; if (fn == NULL) BUG(); /* Hard to diagnose if it's deferred */ @@ -192,9 +198,19 @@ int pdflush_operation(void (*fn)(unsigne if (list_empty(&pdflush_list)) { spin_unlock_irqrestore(&pdflush_lock, flags); ret = -1; + if (wakeup_count < 100 && poke_count < 10) { + printk("%s: no threads\n", __FUNCTION__); + dump_stack(); + poke_count++; + } } else { struct pdflush_work *pdf; + if (wakeup_count < 100 && poke_count < 10) { + printk("%s: found a thread\n", __FUNCTION__); + dump_stack(); + poke_count++; + } pdf = list_entry(pdflush_list.next, struct pdflush_work, list); list_del_init(&pdf->list); if (list_empty(&pdflush_list)) --- linux-2.6.4-rc2/mm/readahead.c 2004-02-03 20:42:39.000000000 -0800 +++ 25/mm/readahead.c 2004-03-07 20:47:36.000000000 -0800 @@ -30,6 +30,7 @@ file_ra_state_init(struct file_ra_state { memset(ra, 0, sizeof(*ra)); ra->ra_pages = mapping->backing_dev_info->ra_pages; + ra->average = ra->ra_pages / 2; } EXPORT_SYMBOL(file_ra_state_init); @@ -380,9 +381,18 @@ page_cache_readahead(struct address_spac */ first_access=1; ra->next_size = max / 2; + ra->prev_page = offset; + ra->serial_cnt++; goto do_io; } + if (offset == ra->prev_page + 1) { + if (ra->serial_cnt <= (max * 2)) + ra->serial_cnt++; + } else { + ra->average = (ra->average + ra->serial_cnt) / 2; + ra->serial_cnt = 1; + } preoffset = ra->prev_page; ra->prev_page = offset; @@ -449,8 +459,12 @@ do_io: * accessed in the current window, there * is a high probability that around 'n' pages * shall be used in the next current window. + * + * To minimize lazy-readahead triggered + * in the next current window, read in + * an extra page. */ - ra->next_size = preoffset - ra->start + 1; + ra->next_size = preoffset - ra->start + 2; } ra->start = offset; ra->size = ra->next_size; @@ -468,17 +482,34 @@ do_io: } } else { /* - * This read request is within the current window. It is time - * to submit I/O for the ahead window while the application is - * crunching through the current window. + * This read request is within the current window. It may be + * time to submit I/O for the ahead window while the + * application is about to step into the ahead window. */ if (ra->ahead_start == 0) { - ra->ahead_start = ra->start + ra->size; - ra->ahead_size = ra->next_size; - actual = do_page_cache_readahead(mapping, filp, + /* + * if the average io-size is less than maximum + * readahead size of the file the io pattern is + * sequential. Hence bring in the readahead window + * immediately. + * Else the i/o pattern is random. Bring + * in the readahead window only if the last page of + * the current window is accessed (lazy readahead). + */ + unsigned long average = ra->average; + + if (ra->serial_cnt > average) + average = (ra->serial_cnt + ra->average) / 2; + + if ((average >= max) || (offset == (ra->start + + ra->size - 1))) { + ra->ahead_start = ra->start + ra->size; + ra->ahead_size = ra->next_size; + actual = do_page_cache_readahead(mapping, filp, ra->ahead_start, ra->ahead_size); - check_ra_success(ra, ra->ahead_size, - actual, orig_next_size); + check_ra_success(ra, ra->ahead_size, + actual, orig_next_size); + } } } out: --- linux-2.6.4-rc2/mm/rmap.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/mm/rmap.c 2004-03-07 20:48:04.000000000 -0800 @@ -112,7 +112,7 @@ pte_chain_encode(struct pte_chain *pte_c * If the page has a single-entry pte_chain, collapse that back to a PageDirect * representation. This way, it's only done under memory pressure. */ -int page_referenced(struct page * page) +int fastcall page_referenced(struct page * page) { struct pte_chain *pc; int referenced = 0; @@ -165,7 +165,7 @@ int page_referenced(struct page * page) * Add a new pte reverse mapping to a page. * The caller needs to hold the mm->page_table_lock. */ -struct pte_chain * +struct pte_chain * fastcall page_add_rmap(struct page *page, pte_t *ptep, struct pte_chain *pte_chain) { pte_addr_t pte_paddr = ptep_to_paddr(ptep); @@ -221,7 +221,7 @@ out: * the page. * Caller needs to hold the mm->page_table_lock. */ -void page_remove_rmap(struct page *page, pte_t *ptep) +void fastcall page_remove_rmap(struct page *page, pte_t *ptep) { pte_addr_t pte_paddr = ptep_to_paddr(ptep); struct pte_chain *pc; @@ -293,7 +293,7 @@ out_unlock: * mm->page_table_lock try_to_unmap_one(), trylock */ static int FASTCALL(try_to_unmap_one(struct page *, pte_addr_t)); -static int try_to_unmap_one(struct page * page, pte_addr_t paddr) +static int fastcall try_to_unmap_one(struct page * page, pte_addr_t paddr) { pte_t *ptep = rmap_ptep_map(paddr); unsigned long address = ptep_to_address(ptep); @@ -343,6 +343,7 @@ static int try_to_unmap_one(struct page BUG_ON(pte_file(*ptep)); } else { unsigned long pgidx; + pgprot_t pgprot = pte_to_pgprot(pte); /* * If a nonlinear mapping then store the file page offset * in the pte. @@ -350,8 +351,10 @@ static int try_to_unmap_one(struct page pgidx = (address - vma->vm_start) >> PAGE_SHIFT; pgidx += vma->vm_pgoff; pgidx >>= PAGE_CACHE_SHIFT - PAGE_SHIFT; - if (page->index != pgidx) { - set_pte(ptep, pgoff_to_pte(page->index)); + if (page->index != pgidx || + pgprot_val(pgprot) != pgprot_val(vma->vm_page_prot)) { + + set_pte(ptep, pgoff_prot_to_pte(page->index, pgprot)); BUG_ON(!pte_file(*ptep)); } } @@ -360,7 +363,7 @@ static int try_to_unmap_one(struct page if (pte_dirty(pte)) set_page_dirty(page); - mm->rss--; + dec_rss(mm, page); page_cache_release(page); ret = SWAP_SUCCESS; @@ -382,7 +385,7 @@ out_unlock: * SWAP_AGAIN - we missed a trylock, try again later * SWAP_FAIL - the page is unswappable */ -int try_to_unmap(struct page * page) +int fastcall try_to_unmap(struct page * page) { struct pte_chain *pc, *next_pc, *start; int ret = SWAP_SUCCESS; --- linux-2.6.4-rc2/mm/shmem.c 2004-02-03 20:42:39.000000000 -0800 +++ 25/mm/shmem.c 2004-03-07 20:48:04.000000000 -0800 @@ -1002,6 +1002,7 @@ static int shmem_populate(struct vm_area struct mm_struct *mm = vma->vm_mm; enum sgp_type sgp = nonblock? SGP_QUICK: SGP_CACHE; unsigned long size; + int linear = !(vma->vm_flags & VM_NONLINEAR); size = (i_size_read(inode) + PAGE_SIZE - 1) >> PAGE_SHIFT; if (pgoff >= size || pgoff + (len >> PAGE_SHIFT) > size) @@ -1023,20 +1024,14 @@ static int shmem_populate(struct vm_area page_cache_release(page); return err; } - } else if (nonblock) { + } else { /* - * If a nonlinear mapping then store the file page - * offset in the pte. + * Store the file page offset in the pte: */ - unsigned long pgidx; - pgidx = (addr - vma->vm_start) >> PAGE_SHIFT; - pgidx += vma->vm_pgoff; - pgidx >>= PAGE_CACHE_SHIFT - PAGE_SHIFT; - if (pgoff != pgidx) { - err = install_file_pte(mm, vma, addr, pgoff, prot); - if (err) - return err; - } + err = install_file_pte(mm, vma, addr, + pgoff, prot, linear); + if (err) + return err; } len -= PAGE_SIZE; --- linux-2.6.4-rc2/mm/slab.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/mm/slab.c 2004-03-07 20:48:18.000000000 -0800 @@ -445,8 +445,8 @@ static inline void **dbg_userword(kmem_c /* * Do not go above this order unless 0 objects fit into the slab. */ -#define BREAK_GFP_ORDER_HI 2 -#define BREAK_GFP_ORDER_LO 1 +#define BREAK_GFP_ORDER_HI 1 +#define BREAK_GFP_ORDER_LO 0 static int slab_break_gfp_order = BREAK_GFP_ORDER_LO; /* Macros for storing/retrieving the cachep and or slab from the @@ -521,9 +521,19 @@ enum { static DEFINE_PER_CPU(struct timer_list, reap_timers); static void reap_timer_fnc(unsigned long data); - +static void free_block (kmem_cache_t* cachep, void** objpp, int len); static void enable_cpucache (kmem_cache_t *cachep); +static inline void ** ac_entry(struct array_cache *ac) +{ + return (void**)(ac+1); +} + +static inline struct array_cache *ac_data(kmem_cache_t *cachep) +{ + return cachep->array[smp_processor_id()]; +} + /* Cal the num objs, wastage, and bytes left over for a given slab size. */ static void cache_estimate (unsigned long gfporder, size_t size, int flags, size_t *left_over, unsigned int *num) @@ -573,32 +583,39 @@ static void start_cpu_timer(int cpu) if (rt->function == NULL) { init_timer(rt); rt->expires = jiffies + HZ + 3*cpu; + rt->data = cpu; rt->function = reap_timer_fnc; add_timer_on(rt, cpu); } } -/* - * Note: if someone calls kmem_cache_alloc() on the new - * cpu before the cpuup callback had a chance to allocate - * the head arrays, it will oops. - * Is CPU_ONLINE early enough? - */ +#ifdef CONFIG_HOTPLUG_CPU +static void stop_cpu_timer(int cpu) +{ + struct timer_list *rt = &per_cpu(reap_timers, cpu); + + if (rt->function) { + del_timer_sync(rt); + WARN_ON(timer_pending(rt)); + rt->function = NULL; + } +} +#endif + static int __devinit cpuup_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) { long cpu = (long)hcpu; - struct list_head *p; + kmem_cache_t* cachep; switch (action) { case CPU_UP_PREPARE: down(&cache_chain_sem); - list_for_each(p, &cache_chain) { + list_for_each_entry(cachep, &cache_chain, next) { int memsize; struct array_cache *nc; - kmem_cache_t* cachep = list_entry(p, kmem_cache_t, next); memsize = sizeof(void*)*cachep->limit+sizeof(struct array_cache); nc = kmalloc(memsize, GFP_KERNEL); if (!nc) @@ -618,22 +635,30 @@ static int __devinit cpuup_callback(stru up(&cache_chain_sem); break; case CPU_ONLINE: - if (g_cpucache_up == FULL) - start_cpu_timer(cpu); + start_cpu_timer(cpu); break; + +#ifdef CONFIG_HOTPLUG_CPU + case CPU_DEAD: + stop_cpu_timer(cpu); + /* fall thru */ case CPU_UP_CANCELED: down(&cache_chain_sem); - - list_for_each(p, &cache_chain) { + list_for_each_entry(cachep, &cache_chain, next) { struct array_cache *nc; - kmem_cache_t* cachep = list_entry(p, kmem_cache_t, next); + spin_lock_irq(&cachep->spinlock); + /* cpu is dead; no one can alloc from it. */ nc = cachep->array[cpu]; cachep->array[cpu] = NULL; + cachep->free_limit -= cachep->batchcount; + free_block(cachep, ac_entry(nc), nc->avail); + spin_unlock_irq(&cachep->spinlock); kfree(nc); } up(&cache_chain_sem); break; +#endif /* CONFIG_HOTPLUG_CPU */ } return NOTIFY_OK; bad: @@ -643,16 +668,6 @@ bad: static struct notifier_block cpucache_notifier = { &cpuup_callback, NULL, 0 }; -static inline void ** ac_entry(struct array_cache *ac) -{ - return (void**)(ac+1); -} - -static inline struct array_cache *ac_data(kmem_cache_t *cachep) -{ - return cachep->array[smp_processor_id()]; -} - /* Initialisation. * Called after the gfp() functions have been enabled, and before smp_init(). */ @@ -1228,7 +1243,8 @@ next: cachep = NULL; goto opps; } - slab_size = L1_CACHE_ALIGN(cachep->num*sizeof(kmem_bufctl_t)+sizeof(struct slab)); + slab_size = L1_CACHE_ALIGN(cachep->num*sizeof(kmem_bufctl_t) + + sizeof(struct slab)); /* * If the slab has been placed off-slab, and we have enough space then @@ -1272,10 +1288,13 @@ next: * the cache that's used by kmalloc(24), otherwise * the creation of further caches will BUG(). */ - cachep->array[smp_processor_id()] = &initarray_generic.cache; + cachep->array[smp_processor_id()] = + &initarray_generic.cache; g_cpucache_up = PARTIAL; } else { - cachep->array[smp_processor_id()] = kmalloc(sizeof(struct arraycache_init),GFP_KERNEL); + cachep->array[smp_processor_id()] = + kmalloc(sizeof(struct arraycache_init), + GFP_KERNEL); } BUG_ON(!ac_data(cachep)); ac_data(cachep)->avail = 0; @@ -1289,7 +1308,7 @@ next: } cachep->lists.next_reap = jiffies + REAPTIMEOUT_LIST3 + - ((unsigned long)cachep)%REAPTIMEOUT_LIST3; + ((unsigned long)cachep)%REAPTIMEOUT_LIST3; /* Need the semaphore to access the chain. */ down(&cache_chain_sem); @@ -1302,16 +1321,24 @@ next: list_for_each(p, &cache_chain) { kmem_cache_t *pc = list_entry(p, kmem_cache_t, next); char tmp; - /* This happens when the module gets unloaded and doesn't - destroy its slab cache and noone else reuses the vmalloc - area of the module. Print a warning. */ - if (__get_user(tmp,pc->name)) { - printk("SLAB: cache with size %d has lost its name\n", - pc->objsize); + + /* + * This happens when the module gets unloaded and + * doesn't destroy its slab cache and noone else reuses + * the vmalloc area of the module. Print a warning. + */ +#ifdef CONFIG_X86_UACCESS_INDIRECT + if (__direct_get_user(tmp,pc->name)) { +#else + if (__get_user(tmp,pc->name)) { +#endif + printk("SLAB: cache with size %d has lost its " + "name\n", pc->objsize); continue; } if (!strcmp(pc->name,name)) { - printk("kmem_cache_create: duplicate cache %s\n",name); + printk("kmem_cache_create: duplicate " + "cache %s\n",name); up(&cache_chain_sem); BUG(); } @@ -1368,7 +1395,6 @@ static void smp_call_function_all_cpus(v preempt_enable(); } -static void free_block (kmem_cache_t* cachep, void** objpp, int len); static void drain_array_locked(kmem_cache_t* cachep, struct array_cache *ac, int force); @@ -1489,6 +1515,9 @@ int kmem_cache_destroy (kmem_cache_t * c return 1; } + /* no cpu_online check required here since we clear the percpu + * array on cpu offline and set this to NULL. + */ for (i = 0; i < NR_CPUS; i++) kfree(cachep->array[i]); @@ -1937,6 +1966,15 @@ cache_alloc_debugcheck_after(kmem_cache_ *dbg_redzone1(cachep, objp) = RED_ACTIVE; *dbg_redzone2(cachep, objp) = RED_ACTIVE; } + { + int objnr; + struct slab *slabp; + + slabp = GET_PAGE_SLAB(virt_to_page(objp)); + + objnr = (objp - slabp->s_mem) / cachep->objsize; + slab_bufctl(slabp)[objnr] = (unsigned long)caller; + } objp += obj_dbghead(cachep); if (cachep->ctor && cachep->flags & SLAB_POISON) { unsigned long ctor_flags = SLAB_CTOR_CONSTRUCTOR; @@ -1998,12 +2036,14 @@ static void free_block(kmem_cache_t *cac objnr = (objp - slabp->s_mem) / cachep->objsize; check_slabp(cachep, slabp); #if DEBUG +#if 0 if (slab_bufctl(slabp)[objnr] != BUFCTL_FREE) { printk(KERN_ERR "slab: double free detected in cache '%s', objp %p.\n", cachep->name, objp); BUG(); } #endif +#endif slab_bufctl(slabp)[objnr] = slabp->free; slabp->free = objnr; STATS_DEC_ACTIVE(cachep); @@ -2134,7 +2174,7 @@ EXPORT_SYMBOL(kmem_cache_alloc); * * Currently only used for dentry validation. */ -int kmem_ptr_validate(kmem_cache_t *cachep, void *ptr) +int fastcall kmem_ptr_validate(kmem_cache_t *cachep, void *ptr) { unsigned long addr = (unsigned long) ptr; unsigned long min_addr = PAGE_OFFSET; @@ -2601,17 +2641,19 @@ next: } /* - * This is a timer handler. There is on per CPU. It is called periodially + * This is a timer handler. There is one per CPU. It is called periodially * to shrink this CPU's caches. Otherwise there could be memory tied up * for long periods (or for ever) due to load changes. */ -static void reap_timer_fnc(unsigned long data) +static void reap_timer_fnc(unsigned long cpu) { - int cpu = smp_processor_id(); struct timer_list *rt = &__get_cpu_var(reap_timers); - cache_reap(); - mod_timer(rt, jiffies + REAPTIMEOUT_CPUC + cpu); + /* CPU hotplug can drag us off cpu: don't run on wrong CPU */ + if (!cpu_is_offline(cpu)) { + cache_reap(); + mod_timer(rt, jiffies + REAPTIMEOUT_CPUC + cpu); + } } #ifdef CONFIG_PROC_FS @@ -2782,6 +2824,29 @@ struct seq_operations slabinfo_op = { .show = s_show, }; +static void do_dump_slabp(kmem_cache_t *cachep) +{ +#if DEBUG + struct list_head *q; + + check_irq_on(); + spin_lock_irq(&cachep->spinlock); + list_for_each(q,&cachep->lists.slabs_full) { + struct slab *slabp; + int i; + slabp = list_entry(q, struct slab, list); + for (i = 0; i < cachep->num; i++) { + unsigned long sym = slab_bufctl(slabp)[i]; + + printk("obj %p/%d: %p", slabp, i, (void *)sym); + print_symbol(" <%s>", sym); + printk("\n"); + } + } + spin_unlock_irq(&cachep->spinlock); +#endif +} + #define MAX_SLABINFO_WRITE 128 /** * slabinfo_write - Tuning for the slab allocator @@ -2822,9 +2887,11 @@ ssize_t slabinfo_write(struct file *file batchcount < 1 || batchcount > limit || shared < 0) { - res = -EINVAL; + do_dump_slabp(cachep); + res = 0; } else { - res = do_tune_cpucache(cachep, limit, batchcount, shared); + res = do_tune_cpucache(cachep, limit, + batchcount, shared); } break; } --- linux-2.6.4-rc2/mm/swap.c 2004-01-09 00:04:33.000000000 -0800 +++ 25/mm/swap.c 2004-03-07 20:47:23.000000000 -0800 @@ -27,6 +27,9 @@ #include #include #include +#include +#include +#include /* How many pages do we try to swap or page in/out together? */ int page_cluster; @@ -76,7 +79,7 @@ int rotate_reclaimable_page(struct page /* * FIXME: speed this up? */ -void activate_page(struct page *page) +void fastcall activate_page(struct page *page) { struct zone *zone = page_zone(page); @@ -97,7 +100,7 @@ void activate_page(struct page *page) * inactive,referenced -> active,unreferenced * active,unreferenced -> active,referenced */ -void mark_page_accessed(struct page *page) +void fastcall mark_page_accessed(struct page *page) { if (!PageActive(page) && PageReferenced(page) && PageLRU(page)) { activate_page(page); @@ -116,7 +119,7 @@ EXPORT_SYMBOL(mark_page_accessed); static DEFINE_PER_CPU(struct pagevec, lru_add_pvecs) = { 0, }; static DEFINE_PER_CPU(struct pagevec, lru_add_active_pvecs) = { 0, }; -void lru_cache_add(struct page *page) +void fastcall lru_cache_add(struct page *page) { struct pagevec *pvec = &get_cpu_var(lru_add_pvecs); @@ -126,7 +129,7 @@ void lru_cache_add(struct page *page) put_cpu_var(lru_add_pvecs); } -void lru_cache_add_active(struct page *page) +void fastcall lru_cache_add_active(struct page *page) { struct pagevec *pvec = &get_cpu_var(lru_add_active_pvecs); @@ -152,7 +155,7 @@ void lru_add_drain(void) * This path almost never happens for VM activity - pages are normally * freed via pagevecs. But it gets used by networking. */ -void __page_cache_release(struct page *page) +void fastcall __page_cache_release(struct page *page) { unsigned long flags; struct zone *zone = page_zone(page); @@ -381,7 +384,37 @@ void vm_acct_memory(long pages) preempt_enable(); } EXPORT_SYMBOL(vm_acct_memory); -#endif + +#ifdef CONFIG_HOTPLUG_CPU +static void lru_drain_cache(unsigned int cpu) +{ + struct pagevec *pvec = &per_cpu(lru_add_pvecs, cpu); + + /* CPU is dead, so no locking needed. */ + if (pagevec_count(pvec)) + __pagevec_lru_add(pvec); + pvec = &per_cpu(lru_add_active_pvecs, cpu); + if (pagevec_count(pvec)) + __pagevec_lru_add_active(pvec); +} + +/* Drop the CPU's cached committed space back into the central pool. */ +static int cpu_swap_callback(struct notifier_block *nfb, + unsigned long action, + void *hcpu) +{ + long *committed; + + committed = &per_cpu(committed_space, (long)hcpu); + if (action == CPU_DEAD) { + atomic_add(*committed, &vm_committed_space); + *committed = 0; + lru_drain_cache((long)hcpu); + } + return NOTIFY_OK; +} +#endif /* CONFIG_HOTPLUG_CPU */ +#endif /* CONFIG_SMP */ #ifdef CONFIG_SMP void percpu_counter_mod(struct percpu_counter *fbc, long amount) @@ -420,4 +453,5 @@ void __init swap_setup(void) * Right now other parts of the system means that we * _really_ don't want to cluster much more */ + hotcpu_notifier(cpu_swap_callback, 0); } --- linux-2.6.4-rc2/mm/swapfile.c 2004-02-17 20:48:46.000000000 -0800 +++ 25/mm/swapfile.c 2004-03-07 20:47:37.000000000 -0800 @@ -387,7 +387,7 @@ static void unuse_pte(struct vm_area_struct *vma, unsigned long address, pte_t *dir, swp_entry_t entry, struct page *page, struct pte_chain **pte_chainp) { - vma->vm_mm->rss++; + inc_rss(vma->vm_mm, page); get_page(page); set_pte(dir, pte_mkold(mk_pte(page, vma->vm_page_prot))); *pte_chainp = page_add_rmap(page, dir, *pte_chainp); --- linux-2.6.4-rc2/mm/swap_state.c 2003-08-08 22:55:14.000000000 -0700 +++ 25/mm/swap_state.c 2004-03-07 20:48:23.000000000 -0800 @@ -38,6 +38,7 @@ struct address_space swapper_space = { .truncate_count = ATOMIC_INIT(0), .private_lock = SPIN_LOCK_UNLOCKED, .private_list = LIST_HEAD_INIT(swapper_space.private_list), + .wb_rwsema = __RWSEM_INITIALIZER(swapper_space.wb_rwsema) }; #define INC_CACHE_INFO(x) do { swap_cache_info.x++; } while (0) --- linux-2.6.4-rc2/mm/truncate.c 2003-11-09 16:45:06.000000000 -0800 +++ 25/mm/truncate.c 2004-03-07 20:47:14.000000000 -0800 @@ -174,6 +174,14 @@ void truncate_inode_pages(struct address } pagevec_release(&pvec); } + + if (lstart == 0) { + WARN_ON(mapping->nrpages); + WARN_ON(!list_empty(&mapping->clean_pages)); + WARN_ON(!list_empty(&mapping->dirty_pages)); + WARN_ON(!list_empty(&mapping->locked_pages)); + WARN_ON(!list_empty(&mapping->io_pages)); + } } EXPORT_SYMBOL(truncate_inode_pages); --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/mm/usercopy.c 2004-03-07 20:48:20.000000000 -0800 @@ -0,0 +1,291 @@ +/* + * linux/mm/usercopy.c + * + * (C) Copyright 2003 Ingo Molnar + * + * Generic implementation of all the user-VM access functions, without + * relying on being able to access the VM directly. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * Get kernel address of the user page and pin it. + */ +static inline struct page *pin_page(unsigned long addr, int write) +{ + struct mm_struct *mm = current->mm ? : &init_mm; + struct page *page = NULL; + int ret; + + /* + * Do a quick atomic lookup first - this is the fastpath. + */ +retry: + page = follow_page(mm, addr, write); + if (likely(page != NULL)) { + if (!PageReserved(page)) + get_page(page); + return page; + } + + /* + * No luck - bad address or need to fault in the page: + */ + + /* Release the lock so get_user_pages can sleep */ + spin_unlock(&mm->page_table_lock); + + /* + * In the context of filemap_copy_from_user(), we are not allowed + * to sleep. We must fail this usercopy attempt and allow + * filemap_copy_from_user() to recover: drop its atomic kmap and use + * a sleeping kmap instead. + */ + if (in_atomic()) { + spin_lock(&mm->page_table_lock); + return NULL; + } + + down_read(&mm->mmap_sem); + ret = get_user_pages(current, mm, addr, 1, write, 0, NULL, NULL); + up_read(&mm->mmap_sem); + spin_lock(&mm->page_table_lock); + + if (ret <= 0) + return NULL; + + /* + * Go try the follow_page again. + */ + goto retry; +} + +static inline void unpin_page(struct page *page) +{ + put_page(page); +} + +/* + * Access another process' address space. + * Source/target buffer must be kernel space, + * Do not walk the page table directly, use get_user_pages + */ +static int rw_vm(unsigned long addr, void *buf, int len, int write) +{ + struct mm_struct *mm = current->mm ? : &init_mm; + + if (!len) + return 0; + + spin_lock(&mm->page_table_lock); + + /* ignore errors, just check how much was sucessfully transfered */ + while (len) { + struct page *page = NULL; + int bytes, offset; + void *maddr; + + page = pin_page(addr, write); + if (!page) + break; + + bytes = len; + offset = addr & (PAGE_SIZE-1); + if (bytes > PAGE_SIZE-offset) + bytes = PAGE_SIZE-offset; + + maddr = kmap_atomic(page, KM_USER_COPY); + +#define HANDLE_TYPE(type) \ + case sizeof(type): *(type *)(maddr+offset) = *(type *)(buf); break; + + if (write) { + switch (bytes) { + HANDLE_TYPE(char); + HANDLE_TYPE(int); + HANDLE_TYPE(long long); + default: + memcpy(maddr + offset, buf, bytes); + } + } else { +#undef HANDLE_TYPE +#define HANDLE_TYPE(type) \ + case sizeof(type): *(type *)(buf) = *(type *)(maddr+offset); break; + switch (bytes) { + HANDLE_TYPE(char); + HANDLE_TYPE(int); + HANDLE_TYPE(long long); + default: + memcpy(buf, maddr + offset, bytes); + } +#undef HANDLE_TYPE + } + kunmap_atomic(maddr, KM_USER_COPY); + unpin_page(page); + len -= bytes; + buf += bytes; + addr += bytes; + } + spin_unlock(&mm->page_table_lock); + + return len; +} + +static int str_vm(unsigned long addr, void *buf0, int len, int copy) +{ + struct mm_struct *mm = current->mm ? : &init_mm; + struct page *page; + void *buf = buf0; + + if (!len) + return len; + + spin_lock(&mm->page_table_lock); + + /* ignore errors, just check how much was sucessfully transfered */ + while (len) { + int bytes, offset, left, copied; + char *maddr; + + page = pin_page(addr, copy == 2); + if (!page) { + spin_unlock(&mm->page_table_lock); + return -EFAULT; + } + bytes = len; + offset = addr & (PAGE_SIZE-1); + if (bytes > PAGE_SIZE-offset) + bytes = PAGE_SIZE-offset; + + maddr = kmap_atomic(page, KM_USER_COPY); + if (copy == 2) { + memset(maddr + offset, 0, bytes); + copied = bytes; + left = 0; + } else if (copy == 1) { + left = strncpy_count(buf, maddr + offset, bytes); + copied = bytes - left; + } else { + copied = strnlen(maddr + offset, bytes); + left = bytes - copied; + } + BUG_ON(bytes < 0 || copied < 0); + kunmap_atomic(maddr, KM_USER_COPY); + unpin_page(page); + len -= copied; + buf += copied; + addr += copied; + if (left) + break; + } + spin_unlock(&mm->page_table_lock); + + return len; +} + +/* + * Copies memory from userspace (ptr) into kernelspace (val). + * + * returns # of bytes not copied. + */ +int get_user_size(unsigned int size, void *val, const void *ptr) +{ + int ret; + + if (unlikely(segment_eq(get_fs(), KERNEL_DS))) + ret = __direct_copy_from_user(val, ptr, size); + else + ret = rw_vm((unsigned long)ptr, val, size, 0); + if (ret) + /* + * Zero the rest: + */ + memset(val + size - ret, 0, ret); + return ret; +} + +/* + * Copies memory from kernelspace (val) into userspace (ptr). + * + * returns # of bytes not copied. + */ +int put_user_size(unsigned int size, const void *val, void *ptr) +{ + if (unlikely(segment_eq(get_fs(), KERNEL_DS))) + return __direct_copy_to_user(ptr, val, size); + else + return rw_vm((unsigned long)ptr, (void *)val, size, 1); +} + +int copy_str_fromuser_size(unsigned int size, void *val, const void *ptr) +{ + int copied, left; + + if (unlikely(segment_eq(get_fs(), KERNEL_DS))) { + left = strncpy_count(val, ptr, size); + copied = size - left; + BUG_ON(copied < 0); + + return copied; + } + left = str_vm((unsigned long)ptr, val, size, 1); + if (left < 0) + return left; + copied = size - left; + BUG_ON(copied < 0); + + return copied; +} + +int strlen_fromuser_size(unsigned int size, const void *ptr) +{ + int copied, left; + + if (unlikely(segment_eq(get_fs(), KERNEL_DS))) { + copied = strnlen(ptr, size) + 1; + BUG_ON(copied < 0); + + return copied; + } + left = str_vm((unsigned long)ptr, NULL, size, 0); + if (left < 0) + return 0; + copied = size - left + 1; + BUG_ON(copied < 0); + + return copied; +} + +int zero_user_size(unsigned int size, void *ptr) +{ + int left; + + if (unlikely(segment_eq(get_fs(), KERNEL_DS))) { + memset(ptr, 0, size); + return 0; + } + left = str_vm((unsigned long)ptr, NULL, size, 2); + if (left < 0) + return size; + return left; +} + +EXPORT_SYMBOL(get_user_size); +EXPORT_SYMBOL(put_user_size); +EXPORT_SYMBOL(zero_user_size); +EXPORT_SYMBOL(copy_str_fromuser_size); +EXPORT_SYMBOL(strlen_fromuser_size); + --- linux-2.6.4-rc2/mm/vmscan.c 2004-02-17 20:48:46.000000000 -0800 +++ 25/mm/vmscan.c 2004-03-07 20:48:16.000000000 -0800 @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include #include @@ -135,7 +137,7 @@ EXPORT_SYMBOL(remove_shrinker); * * We do weird things to avoid (scanned*seeks*entries) overflowing 32 bits. */ -static int shrink_slab(long scanned, unsigned int gfp_mask) +static int shrink_slab(unsigned long scanned, unsigned int gfp_mask) { struct shrinker *shrinker; long pages; @@ -147,7 +149,7 @@ static int shrink_slab(long scanned, uns list_for_each_entry(shrinker, &shrinker_list, list) { unsigned long long delta; - delta = 4 * (scanned / shrinker->seeks); + delta = 4 * scanned / shrinker->seeks; delta *= (*shrinker->shrinker)(0, gfp_mask); do_div(delta, pages + 1); shrinker->nr += delta; @@ -155,6 +157,7 @@ static int shrink_slab(long scanned, uns long nr_to_scan = shrinker->nr; shrinker->nr = 0; + mod_page_state(slabs_scanned, nr_to_scan); while (nr_to_scan) { long this_scan = nr_to_scan; @@ -243,8 +246,7 @@ static void handle_write_error(struct ad * shrink_list returns the number of reclaimed pages */ static int -shrink_list(struct list_head *page_list, unsigned int gfp_mask, - int *max_scan, int *nr_mapped) +shrink_list(struct list_head *page_list, unsigned int gfp_mask, int *nr_scanned) { struct address_space *mapping; LIST_HEAD(ret_pages); @@ -268,7 +270,7 @@ shrink_list(struct list_head *page_list, /* Double the slab pressure for mapped and swapcache pages */ if (page_mapped(page) || PageSwapCache(page)) - (*nr_mapped)++; + (*nr_scanned)++; BUG_ON(PageActive(page)); @@ -459,9 +461,6 @@ keep: list_splice(&ret_pages, page_list); if (pagevec_count(&freed_pvec)) __pagevec_release_nonlru(&freed_pvec); - mod_page_state(pgsteal, ret); - if (current_is_kswapd()) - mod_page_state(kswapd_steal, ret); mod_page_state(pgactivate, pgactivate); return ret; } @@ -471,39 +470,31 @@ keep: * a batch of pages and working on them outside the lock. Any pages which were * not freed will be added back to the LRU. * - * shrink_cache() is passed the number of pages to try to free, and returns - * the number of pages which were reclaimed. + * shrink_cache() is passed the number of pages to scan and returns the number + * of pages which were reclaimed. * * For pagecache intensive workloads, the first loop here is the hottest spot * in the kernel (apart from the copy_*_user functions). */ static int -shrink_cache(const int nr_pages, struct zone *zone, - unsigned int gfp_mask, int max_scan, int *nr_mapped) +shrink_cache(struct zone *zone, unsigned int gfp_mask, + int max_scan, int *total_scanned) { LIST_HEAD(page_list); struct pagevec pvec; - int nr_to_process; int ret = 0; - /* - * Try to ensure that we free `nr_pages' pages in one pass of the loop. - */ - nr_to_process = nr_pages; - if (nr_to_process < SWAP_CLUSTER_MAX) - nr_to_process = SWAP_CLUSTER_MAX; - pagevec_init(&pvec, 1); lru_add_drain(); spin_lock_irq(&zone->lru_lock); - while (max_scan > 0 && ret < nr_pages) { + while (max_scan > 0) { struct page *page; int nr_taken = 0; int nr_scan = 0; int nr_freed; - while (nr_scan++ < nr_to_process && + while (nr_scan++ < SWAP_CLUSTER_MAX && !list_empty(&zone->inactive_list)) { page = list_entry(zone->inactive_list.prev, struct page, lru); @@ -532,9 +523,16 @@ shrink_cache(const int nr_pages, struct goto done; max_scan -= nr_scan; - mod_page_state(pgscan, nr_scan); - nr_freed = shrink_list(&page_list, gfp_mask, - &max_scan, nr_mapped); + if (current_is_kswapd()) + mod_page_state_zone(zone, pgscan_kswapd, nr_scan); + else + mod_page_state_zone(zone, pgscan_direct, nr_scan); + nr_freed = shrink_list(&page_list, gfp_mask, total_scanned); + *total_scanned += nr_taken; + if (current_is_kswapd()) + mod_page_state(kswapd_steal, nr_freed); + mod_page_state_zone(zone, pgsteal, nr_freed); + ret += nr_freed; if (nr_freed <= 0 && list_empty(&page_list)) goto done; @@ -584,7 +582,7 @@ done: */ static void refill_inactive_zone(struct zone *zone, const int nr_pages_in, - struct page_state *ps, int priority) + struct page_state *ps) { int pgmoved; int pgdeactivate = 0; @@ -657,17 +655,17 @@ refill_inactive_zone(struct zone *zone, page = list_entry(l_hold.prev, struct page, lru); list_del(&page->lru); if (page_mapped(page)) { - pte_chain_lock(page); - if (page_mapped(page) && page_referenced(page)) { - pte_chain_unlock(page); + if (!reclaim_mapped) { list_add(&page->lru, &l_active); continue; } - pte_chain_unlock(page); - if (!reclaim_mapped) { + pte_chain_lock(page); + if (page_referenced(page)) { + pte_chain_unlock(page); list_add(&page->lru, &l_active); continue; } + pte_chain_unlock(page); } /* * FIXME: need to consider page_count(page) here if/when we @@ -733,20 +731,20 @@ refill_inactive_zone(struct zone *zone, spin_unlock_irq(&zone->lru_lock); pagevec_release(&pvec); - mod_page_state(pgrefill, nr_pages_in - nr_pages); + mod_page_state_zone(zone, pgrefill, nr_pages_in - nr_pages); mod_page_state(pgdeactivate, pgdeactivate); } /* - * Try to reclaim `nr_pages' from this zone. Returns the number of reclaimed - * pages. This is a basic per-zone page freer. Used by both kswapd and - * direct reclaim. + * Scan `nr_pages' from this zone. Returns the number of reclaimed pages. + * This is a basic per-zone page freer. Used by both kswapd and direct reclaim. */ static int shrink_zone(struct zone *zone, int max_scan, unsigned int gfp_mask, - const int nr_pages, int *nr_mapped, struct page_state *ps, int priority) + int *total_scanned, struct page_state *ps) { unsigned long ratio; + int count; /* * Try to keep the active list 2/3 of the size of the cache. And @@ -758,26 +756,23 @@ shrink_zone(struct zone *zone, int max_s * just to make sure that the kernel will slowly sift through the * active list. */ - ratio = (unsigned long)nr_pages * zone->nr_active / + ratio = (unsigned long)SWAP_CLUSTER_MAX * zone->nr_active / ((zone->nr_inactive | 1) * 2); - atomic_add(ratio+1, &zone->refill_counter); - if (atomic_read(&zone->refill_counter) > SWAP_CLUSTER_MAX) { - int count; - /* - * Don't try to bring down too many pages in one attempt. - * If this fails, the caller will increase `priority' and - * we'll try again, with an increased chance of reclaiming - * mapped memory. - */ - count = atomic_read(&zone->refill_counter); - if (count > SWAP_CLUSTER_MAX * 4) - count = SWAP_CLUSTER_MAX * 4; - atomic_set(&zone->refill_counter, 0); - refill_inactive_zone(zone, count, ps, priority); + atomic_add(ratio+1, &zone->nr_scan_active); + count = atomic_read(&zone->nr_scan_active); + if (count >= SWAP_CLUSTER_MAX) { + atomic_set(&zone->nr_scan_active, 0); + refill_inactive_zone(zone, count, ps); + } + + atomic_add(max_scan, &zone->nr_scan_inactive); + count = atomic_read(&zone->nr_scan_inactive); + if (count >= SWAP_CLUSTER_MAX) { + atomic_set(&zone->nr_scan_inactive, 0); + return shrink_cache(zone, gfp_mask, count, total_scanned); } - return shrink_cache(nr_pages, zone, gfp_mask, - max_scan, nr_mapped); + return 0; } /* @@ -798,15 +793,13 @@ shrink_zone(struct zone *zone, int max_s */ static int shrink_caches(struct zone **zones, int priority, int *total_scanned, - int gfp_mask, int nr_pages, struct page_state *ps) + int gfp_mask, struct page_state *ps) { int ret = 0; int i; for (i = 0; zones[i] != NULL; i++) { - int to_reclaim = max(nr_pages, SWAP_CLUSTER_MAX); struct zone *zone = zones[i]; - int nr_mapped = 0; int max_scan; if (zone->free_pages < zone->pages_high) @@ -815,18 +808,8 @@ shrink_caches(struct zone **zones, int p if (zone->all_unreclaimable && priority != DEF_PRIORITY) continue; /* Let kswapd poll it */ - /* - * If we cannot reclaim `nr_pages' pages by scanning twice - * that many pages then fall back to the next zone. - */ max_scan = zone->nr_inactive >> priority; - if (max_scan < to_reclaim * 2) - max_scan = to_reclaim * 2; - ret += shrink_zone(zone, max_scan, gfp_mask, - to_reclaim, &nr_mapped, ps, priority); - *total_scanned += max_scan + nr_mapped; - if (ret >= nr_pages) - break; + ret += shrink_zone(zone, max_scan, gfp_mask, total_scanned, ps); } return ret; } @@ -853,7 +836,6 @@ int try_to_free_pages(struct zone **zone { int priority; int ret = 0; - const int nr_pages = SWAP_CLUSTER_MAX; int nr_reclaimed = 0; struct reclaim_state *reclaim_state = current->reclaim_state; int i; @@ -869,8 +851,13 @@ int try_to_free_pages(struct zone **zone get_page_state(&ps); nr_reclaimed += shrink_caches(zones, priority, &total_scanned, - gfp_mask, nr_pages, &ps); - if (nr_reclaimed >= nr_pages) { + gfp_mask, &ps); + shrink_slab(total_scanned, gfp_mask); + if (reclaim_state) { + nr_reclaimed += reclaim_state->reclaimed_slab; + reclaim_state->reclaimed_slab = 0; + } + if (nr_reclaimed >= SWAP_CLUSTER_MAX) { ret = 1; goto out; } @@ -884,14 +871,8 @@ int try_to_free_pages(struct zone **zone wakeup_bdflush(total_scanned); /* Take a nap, wait for some writeback to complete */ - blk_congestion_wait(WRITE, HZ/10); - if (zones[0] - zones[0]->zone_pgdat->node_zones < ZONE_HIGHMEM) { - shrink_slab(total_scanned, gfp_mask); - if (reclaim_state) { - nr_reclaimed += reclaim_state->reclaimed_slab; - reclaim_state->reclaimed_slab = 0; - } - } + if (total_scanned && priority < DEF_PRIORITY - 2) + blk_congestion_wait(WRITE, HZ/10); } if ((gfp_mask & __GFP_FS) && !(gfp_mask & __GFP_NORETRY)) out_of_memory(); @@ -918,6 +899,13 @@ out: * scanned twice and there has been zero successful reclaim. Mark the zone as * dead and from now on, only perform a short scan. Basically we're polling * the zone for when the problem goes away. + * + * kswapd scans the zones in the highmem->normal->dma direction. It skips + * zones which have free_pages > pages_high, but once a zone is found to have + * free_pages <= pages_high, we scan that zone and the lower zones regardless + * of the number of free pages in the lower zones. This interoperates with + * the page allocator fallback scheme to ensure that aging of pages is balanced + * across the zones. */ static int balance_pgdat(pg_data_t *pgdat, int nr_pages, struct page_state *ps) { @@ -936,48 +924,80 @@ static int balance_pgdat(pg_data_t *pgda for (priority = DEF_PRIORITY; priority; priority--) { int all_zones_ok = 1; + int pages_scanned = 0; + int end_zone = 0; /* Inclusive. 0 = ZONE_DMA */ - for (i = 0; i < pgdat->nr_zones; i++) { + + if (nr_pages == 0) { + /* + * Scan in the highmem->dma direction for the highest + * zone which needs scanning + */ + for (i = pgdat->nr_zones - 1; i >= 0; i--) { + struct zone *zone = pgdat->node_zones + i; + + if (zone->all_unreclaimable && + priority != DEF_PRIORITY) + continue; + + if (zone->free_pages <= zone->pages_high) { + end_zone = i; + goto scan; + } + } + goto out; + } else { + end_zone = pgdat->nr_zones - 1; + } +scan: + /* + * Now scan the zone in the dma->highmem direction, stopping + * at the last zone which needs scanning. + * + * We do this because the page allocator works in the opposite + * direction. This prevents the page allocator from allocating + * pages behind kswapd's direction of progress, which would + * cause too much scanning of the lower zones. + */ + for (i = 0; i <= end_zone; i++) { struct zone *zone = pgdat->node_zones + i; - int nr_mapped = 0; + int total_scanned = 0; int max_scan; - int to_reclaim; + int reclaimed; if (zone->all_unreclaimable && priority != DEF_PRIORITY) continue; - if (nr_pages && to_free > 0) { /* Software suspend */ - to_reclaim = min(to_free, SWAP_CLUSTER_MAX*8); - } else { /* Zone balancing */ - to_reclaim = zone->pages_high-zone->free_pages; - if (to_reclaim <= 0) - continue; + if (nr_pages == 0) { /* Not software suspend */ + if (zone->free_pages <= zone->pages_high) + all_zones_ok = 0; } zone->temp_priority = priority; - all_zones_ok = 0; max_scan = zone->nr_inactive >> priority; - if (max_scan < to_reclaim * 2) - max_scan = to_reclaim * 2; - if (max_scan < SWAP_CLUSTER_MAX) - max_scan = SWAP_CLUSTER_MAX; - to_free -= shrink_zone(zone, max_scan, GFP_KERNEL, - to_reclaim, &nr_mapped, ps, priority); - if (i < ZONE_HIGHMEM) { - reclaim_state->reclaimed_slab = 0; - shrink_slab(max_scan + nr_mapped, GFP_KERNEL); - to_free -= reclaim_state->reclaimed_slab; - } + reclaimed = shrink_zone(zone, max_scan, GFP_KERNEL, + &total_scanned, ps); + total_scanned += pages_scanned; + reclaim_state->reclaimed_slab = 0; + shrink_slab(total_scanned, GFP_KERNEL); + reclaimed += reclaim_state->reclaimed_slab; + to_free -= reclaimed; if (zone->all_unreclaimable) continue; if (zone->pages_scanned > zone->present_pages * 2) zone->all_unreclaimable = 1; } + if (nr_pages && to_free > 0) + continue; /* swsusp: need to do more work */ if (all_zones_ok) - break; - if (to_free > 0) + break; /* kswapd: all done */ + /* + * OK, kswapd is getting into trouble. Take a nap, then take + * another pass across the zones. + */ + if (pages_scanned && priority < DEF_PRIORITY - 2) blk_congestion_wait(WRITE, HZ/10); } - +out: for (i = 0; i < pgdat->nr_zones; i++) { struct zone *zone = pgdat->node_zones + i; @@ -1085,13 +1105,39 @@ int shrink_all_memory(int nr_pages) } #endif +#ifdef CONFIG_HOTPLUG_CPU +/* It's optimal to keep kswapds on the same CPUs as their memory, but + not required for correctness. So if the last cpu in a node goes + away, we get changed to run anywhere: as the first one comes back, + restore their cpu bindings. */ +static int __devinit cpu_callback(struct notifier_block *nfb, + unsigned long action, + void *hcpu) +{ + pg_data_t *pgdat; + cpumask_t mask; + + if (action == CPU_ONLINE) { + for_each_pgdat(pgdat) { + mask = node_to_cpumask(pgdat->node_id); + if (any_online_cpu(mask) != NR_CPUS) + /* One of our CPUs online: restore mask */ + set_cpus_allowed(pgdat->kswapd, mask); + } + } + return NOTIFY_OK; +} +#endif /* CONFIG_HOTPLUG_CPU */ + static int __init kswapd_init(void) { pg_data_t *pgdat; swap_setup(); for_each_pgdat(pgdat) - kernel_thread(kswapd, pgdat, CLONE_KERNEL); + pgdat->kswapd + = find_task_by_pid(kernel_thread(kswapd, pgdat, CLONE_KERNEL)); total_memory = nr_free_pagecache_pages(); + hotcpu_notifier(cpu_callback, 0); return 0; } --- linux-2.6.4-rc2/net/bluetooth/hci_conn.c 2004-02-17 20:48:46.000000000 -0800 +++ 25/net/bluetooth/hci_conn.c 2004-03-07 20:46:46.000000000 -0800 @@ -170,6 +170,9 @@ struct hci_conn *hci_conn_add(struct hci hci_conn_hash_add(hdev, conn); tasklet_enable(&hdev->tx_task); + if (hdev->notify) + hdev->notify(hdev, HCI_NOTIFY_CONN_ADD); + return conn; } @@ -196,6 +199,9 @@ int hci_conn_del(struct hci_conn *conn) hdev->acl_cnt += conn->sent; } + if (hdev->notify) + hdev->notify(hdev, HCI_NOTIFY_CONN_DEL); + tasklet_disable(&hdev->tx_task); hci_conn_hash_del(hdev, conn); tasklet_enable(&hdev->tx_task); --- linux-2.6.4-rc2/net/bluetooth/hci_core.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/net/bluetooth/hci_core.c 2004-03-07 20:46:46.000000000 -0800 @@ -189,6 +189,10 @@ static void hci_init_req(struct hci_dev /* Mandatory initialization */ + /* Reset */ + if (test_bit(HCI_QUIRK_RESET_ON_INIT, &hdev->quirks)) + hci_send_cmd(hdev, OGF_HOST_CTL, OCF_RESET, 0, NULL); + /* Read Local Supported Features */ hci_send_cmd(hdev, OGF_INFO_PARAM, OCF_READ_LOCAL_FEATURES, 0, NULL); --- linux-2.6.4-rc2/net/bluetooth/hci_event.c 2004-02-17 20:48:46.000000000 -0800 +++ 25/net/bluetooth/hci_event.c 2004-03-07 20:46:46.000000000 -0800 @@ -232,6 +232,9 @@ static void hci_cc_host_ctl(struct hci_d hdev->voice_setting = setting; BT_DBG("%s: voice setting 0x%04x", hdev->name, setting); + + if (hdev->notify) + hdev->notify(hdev, HCI_NOTIFY_VOICE_SETTING); } break; @@ -247,6 +250,9 @@ static void hci_cc_host_ctl(struct hci_d hdev->voice_setting = setting; BT_DBG("%s: voice setting 0x%04x", hdev->name, setting); + + if (hdev->notify) + hdev->notify(hdev, HCI_NOTIFY_VOICE_SETTING); } hci_req_complete(hdev, status); break; --- linux-2.6.4-rc2/net/bluetooth/rfcomm/core.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/net/bluetooth/rfcomm/core.c 2004-03-07 20:46:46.000000000 -0800 @@ -50,7 +50,7 @@ #include #include -#define VERSION "1.1" +#define VERSION "1.2" #ifndef CONFIG_BT_RFCOMM_DEBUG #undef BT_DBG @@ -409,7 +409,7 @@ int rfcomm_dlc_send(struct rfcomm_dlc *d return len; } -void __rfcomm_dlc_throttle(struct rfcomm_dlc *d) +void fastcall __rfcomm_dlc_throttle(struct rfcomm_dlc *d) { BT_DBG("dlc %p state %ld", d, d->state); @@ -420,7 +420,7 @@ void __rfcomm_dlc_throttle(struct rfcomm rfcomm_schedule(RFCOMM_SCHED_TX); } -void __rfcomm_dlc_unthrottle(struct rfcomm_dlc *d) +void fastcall __rfcomm_dlc_unthrottle(struct rfcomm_dlc *d) { BT_DBG("dlc %p state %ld", d, d->state); --- linux-2.6.4-rc2/net/bluetooth/rfcomm/tty.c 2004-02-17 20:48:46.000000000 -0800 +++ 25/net/bluetooth/rfcomm/tty.c 2004-03-07 20:46:46.000000000 -0800 @@ -50,6 +50,8 @@ #define RFCOMM_TTY_MAJOR 216 /* device node major id of the usb/bluetooth.c driver */ #define RFCOMM_TTY_MINOR 0 +static struct tty_driver *rfcomm_tty_driver; + struct rfcomm_dev { struct list_head list; atomic_t refcnt; @@ -98,6 +100,8 @@ static void rfcomm_dev_destruct(struct r rfcomm_dlc_put(dlc); + tty_unregister_device(rfcomm_tty_driver, dev->id); + /* Refcount should only hit zero when called from rfcomm_dev_del() which will have taken us off the list. Everything else are refcounting bugs. */ @@ -239,8 +243,11 @@ out: if (err) { kfree(dev); return err; - } else - return dev->id; + } + + tty_register_device(rfcomm_tty_driver, dev->id, NULL); + + return dev->id; } static void rfcomm_dev_del(struct rfcomm_dev *dev) @@ -871,8 +878,6 @@ static int rfcomm_tty_tiocmset(struct tt /* ---- TTY structure ---- */ -static struct tty_driver *rfcomm_tty_driver; - static struct tty_operations rfcomm_ops = { .open = rfcomm_tty_open, .close = rfcomm_tty_close, @@ -906,7 +911,7 @@ int rfcomm_init_ttys(void) rfcomm_tty_driver->minor_start = RFCOMM_TTY_MINOR; rfcomm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; rfcomm_tty_driver->subtype = SERIAL_TYPE_NORMAL; - rfcomm_tty_driver->flags = TTY_DRIVER_REAL_RAW; + rfcomm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; rfcomm_tty_driver->init_termios = tty_std_termios; rfcomm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; tty_set_operations(rfcomm_tty_driver, &rfcomm_ops); --- linux-2.6.4-rc2/net/core/dev.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/net/core/dev.c 2004-03-07 20:47:24.000000000 -0800 @@ -105,6 +105,8 @@ #include #include #include +#include +#include #ifdef CONFIG_NET_RADIO #include /* Note : will define WIRELESS_EXT */ #include @@ -1513,7 +1515,6 @@ static void sample_queue(unsigned long d } #endif - /** * netif_rx - post buffer to the network code * @skb: buffer to post @@ -1538,6 +1539,13 @@ int netif_rx(struct sk_buff *skb) struct softnet_data *queue; unsigned long flags; +#ifdef CONFIG_NETPOLL_RX + if (skb->dev->netpoll_rx && netpoll_rx(skb)) { + kfree_skb(skb); + return NET_RX_DROP; + } +#endif + if (!skb->stamp.tv_sec) do_gettimeofday(&skb->stamp); @@ -1693,6 +1701,13 @@ int netif_receive_skb(struct sk_buff *sk int ret = NET_RX_DROP; unsigned short type; +#ifdef CONFIG_NETPOLL_RX + if (skb->dev->netpoll_rx && skb->dev->poll && netpoll_rx(skb)) { + kfree_skb(skb); + return NET_RX_DROP; + } +#endif + if (!skb->stamp.tv_sec) do_gettimeofday(&skb->stamp); @@ -1819,7 +1834,6 @@ static void net_rx_action(struct softirq unsigned long start_time = jiffies; int budget = netdev_max_backlog; - preempt_disable(); local_irq_disable(); @@ -1846,6 +1860,10 @@ static void net_rx_action(struct softirq dev_put(dev); local_irq_disable(); } + +#ifdef CONFIG_KGDBOE + kgdb_process_breakpoint(); +#endif } out: local_irq_enable(); @@ -3116,6 +3134,52 @@ int unregister_netdevice(struct net_devi return 0; } +#ifdef CONFIG_HOTPLUG_CPU +static int dev_cpu_callback(struct notifier_block *nfb, + unsigned long action, + void *ocpu) +{ + struct sk_buff **list_skb; + struct net_device **list_net; + struct sk_buff *skb; + unsigned int cpu, oldcpu = (unsigned long)ocpu; + struct softnet_data *sd, *oldsd; + + if (action != CPU_DEAD) + return NOTIFY_OK; + + local_irq_disable(); + cpu = smp_processor_id(); + sd = &per_cpu(softnet_data, cpu); + oldsd = &per_cpu(softnet_data, oldcpu); + + /* Find end of our completion_queue. */ + list_skb = &sd->completion_queue; + while (*list_skb) + list_skb = &(*list_skb)->next; + /* Append completion queue from offline CPU. */ + *list_skb = oldsd->completion_queue; + oldsd->completion_queue = NULL; + + /* Find end of our output_queue. */ + list_net = &sd->output_queue; + while (*list_net) + list_net = &(*list_net)->next_sched; + /* Append output queue from offline CPU. */ + *list_net = oldsd->output_queue; + oldsd->output_queue = NULL; + + raise_softirq_irqoff(NET_TX_SOFTIRQ); + local_irq_enable(); + + /* Process offline CPU's input_pkt_queue */ + while ((skb = __skb_dequeue(&oldsd->input_pkt_queue))) + netif_rx(skb); + + return NOTIFY_OK; +} +#endif /* CONFIG_HOTPLUG_CPU */ + /* * Initialize the DEV module. At boot time this walks the device list and @@ -3182,6 +3246,7 @@ static int __init net_dev_init(void) dst_init(); dev_mcast_init(); + hotcpu_notifier(dev_cpu_callback, 0); rc = 0; out: return rc; --- linux-2.6.4-rc2/net/core/flow.c 2004-02-17 20:48:46.000000000 -0800 +++ 25/net/core/flow.c 2004-03-07 20:47:24.000000000 -0800 @@ -326,6 +326,17 @@ static void __devinit flow_cache_cpu_pre tasklet_init(tasklet, flow_cache_flush_tasklet, 0); } +#ifdef CONFIG_HOTPLUG_CPU +static int flow_cache_cpu(struct notifier_block *nfb, + unsigned long action, + void *hcpu) +{ + if (action == CPU_DEAD) + __flow_cache_shrink((unsigned long)hcpu, 0); + return NOTIFY_OK; +} +#endif /* CONFIG_HOTPLUG_CPU */ + static int __init flow_cache_init(void) { int i; @@ -350,6 +361,7 @@ static int __init flow_cache_init(void) for_each_cpu(i) flow_cache_cpu_prepare(i); + hotcpu_notifier(flow_cache_cpu, 0); return 0; } --- linux-2.6.4-rc2/net/core/Makefile 2003-09-08 13:58:59.000000000 -0700 +++ 25/net/core/Makefile 2004-03-07 20:46:54.000000000 -0800 @@ -13,3 +13,4 @@ obj-$(CONFIG_NETFILTER) += netfilter.o obj-$(CONFIG_NET_DIVERT) += dv.o obj-$(CONFIG_NET_PKTGEN) += pktgen.o obj-$(CONFIG_NET_RADIO) += wireless.o +obj-$(CONFIG_NETPOLL) += netpoll.o --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/net/core/netpoll.c 2004-03-07 20:46:54.000000000 -0800 @@ -0,0 +1,651 @@ +/* + * Common framework for low-level network console, dump, and debugger code + * + * Sep 8 2003 Matt Mackall + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * We maintain a small pool of fully-sized skbs, to make sure the + * message gets out even in extreme OOM situations. + */ + +#define MAX_SKBS 32 +#define MAX_UDP_CHUNK 1460 + +static spinlock_t skb_list_lock = SPIN_LOCK_UNLOCKED; +static int nr_skbs; +static struct sk_buff *skbs; + +static spinlock_t rx_list_lock = SPIN_LOCK_UNLOCKED; +static LIST_HEAD(rx_list); + +static int trapped; + +#define MAX_SKB_SIZE \ + (MAX_UDP_CHUNK + sizeof(struct udphdr) + \ + sizeof(struct iphdr) + sizeof(struct ethhdr)) + +static void zap_completion_queue(void); + +static int checksum_udp(struct sk_buff *skb, struct udphdr *uh, + unsigned short ulen, u32 saddr, u32 daddr) +{ + if (uh->check == 0) + return 0; + + if (skb->ip_summed == CHECKSUM_HW) + return csum_tcpudp_magic( + saddr, daddr, ulen, IPPROTO_UDP, skb->csum); + + skb->csum = csum_tcpudp_nofold(saddr, daddr, ulen, IPPROTO_UDP, 0); + + return csum_fold(skb_checksum(skb, 0, skb->len, skb->csum)); +} + +void netpoll_poll(struct netpoll *np) +{ + int budget = 1; + + if(!np->dev || !netif_running(np->dev) || !np->dev->poll_controller) + return; + + /* Process pending work on NIC */ + np->dev->poll_controller(np->dev); + + /* If scheduling is stopped, tickle NAPI bits */ + if(trapped && np->dev->poll && + test_bit(__LINK_STATE_RX_SCHED, &np->dev->state)) + np->dev->poll(np->dev, &budget); + zap_completion_queue(); +} + +static void refill_skbs(void) +{ + struct sk_buff *skb; + unsigned long flags; + + spin_lock_irqsave(&skb_list_lock, flags); + while (nr_skbs < MAX_SKBS) { + skb = alloc_skb(MAX_SKB_SIZE, GFP_ATOMIC); + if (!skb) + break; + + skb->next = skbs; + skbs = skb; + nr_skbs++; + } + spin_unlock_irqrestore(&skb_list_lock, flags); +} + +static void zap_completion_queue(void) +{ + unsigned long flags; + struct softnet_data *sd = &get_cpu_var(softnet_data); + + if (sd->completion_queue) { + struct sk_buff *clist; + + local_irq_save(flags); + clist = sd->completion_queue; + sd->completion_queue = NULL; + local_irq_restore(flags); + + while (clist != NULL) { + struct sk_buff *skb = clist; + clist = clist->next; + __kfree_skb(skb); + } + } + + put_cpu_var(softnet_data); +} + +static struct sk_buff * find_skb(struct netpoll *np, int len, int reserve) +{ + int once = 1, count = 0; + unsigned long flags; + struct sk_buff *skb = NULL; + + zap_completion_queue(); +repeat: + if (nr_skbs < MAX_SKBS) + refill_skbs(); + + skb = alloc_skb(len, GFP_ATOMIC); + + if (!skb) { + spin_lock_irqsave(&skb_list_lock, flags); + skb = skbs; + if (skb) + skbs = skb->next; + skb->next = NULL; + nr_skbs--; + spin_unlock_irqrestore(&skb_list_lock, flags); + } + + if(!skb) { + count++; + if (once && (count == 1000000)) { + printk("out of netpoll skbs!\n"); + once = 0; + } + netpoll_poll(np); + goto repeat; + } + + atomic_set(&skb->users, 1); + skb_reserve(skb, reserve); + return skb; +} + +void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb) +{ + int status; + +repeat: + if(!np || !np->dev || !netif_running(np->dev)) { + __kfree_skb(skb); + return; + } + + spin_lock(&np->dev->xmit_lock); + np->dev->xmit_lock_owner = smp_processor_id(); + + if (netif_queue_stopped(np->dev)) { + np->dev->xmit_lock_owner = -1; + spin_unlock(&np->dev->xmit_lock); + + netpoll_poll(np); + goto repeat; + } + + status = np->dev->hard_start_xmit(skb, np->dev); + np->dev->xmit_lock_owner = -1; + spin_unlock(&np->dev->xmit_lock); + + /* transmit busy */ + if(status) + goto repeat; +} + +void netpoll_send_udp(struct netpoll *np, const char *msg, int len) +{ + int total_len, eth_len, ip_len, udp_len; + struct sk_buff *skb; + struct udphdr *udph; + struct iphdr *iph; + struct ethhdr *eth; + + udp_len = len + sizeof(*udph); + ip_len = eth_len = udp_len + sizeof(*iph); + total_len = eth_len + ETH_HLEN; + + skb = find_skb(np, total_len, total_len - len); + if (!skb) + return; + + memcpy(skb->data, msg, len); + skb->len += len; + + udph = (struct udphdr *) skb_push(skb, sizeof(*udph)); + udph->source = htons(np->local_port); + udph->dest = htons(np->remote_port); + udph->len = htons(udp_len); + udph->check = 0; + + iph = (struct iphdr *)skb_push(skb, sizeof(*iph)); + + iph->version = 4; + iph->ihl = 5; + iph->tos = 0; + iph->tot_len = htons(ip_len); + iph->id = 0; + iph->frag_off = 0; + iph->ttl = 64; + iph->protocol = IPPROTO_UDP; + iph->check = 0; + iph->saddr = htonl(np->local_ip); + iph->daddr = htonl(np->remote_ip); + iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); + + eth = (struct ethhdr *) skb_push(skb, ETH_HLEN); + + eth->h_proto = htons(ETH_P_IP); + memcpy(eth->h_source, np->local_mac, 6); + memcpy(eth->h_dest, np->remote_mac, 6); + + netpoll_send_skb(np, skb); +} + +static void arp_reply(struct sk_buff *skb) +{ + struct in_device *in_dev = (struct in_device *) skb->dev->ip_ptr; + struct arphdr *arp; + unsigned char *arp_ptr, *sha, *tha; + int size, type = ARPOP_REPLY, ptype = ETH_P_ARP; + u32 sip, tip; + struct sk_buff *send_skb; + unsigned long flags; + struct list_head *p; + struct netpoll *np = 0; + + spin_lock_irqsave(&rx_list_lock, flags); + list_for_each(p, &rx_list) { + np = list_entry(p, struct netpoll, rx_list); + if ( np->dev == skb->dev ) + break; + np = 0; + } + spin_unlock_irqrestore(&rx_list_lock, flags); + + if (!np) return; + + /* No arp on this interface */ + if (!in_dev || skb->dev->flags & IFF_NOARP) + return; + + if (!pskb_may_pull(skb, (sizeof(struct arphdr) + + (2 * skb->dev->addr_len) + + (2 * sizeof(u32))))) + return; + + skb->h.raw = skb->nh.raw = skb->data; + arp = skb->nh.arph; + + if ((arp->ar_hrd != htons(ARPHRD_ETHER) && + arp->ar_hrd != htons(ARPHRD_IEEE802)) || + arp->ar_pro != htons(ETH_P_IP) || + arp->ar_op != htons(ARPOP_REQUEST)) + return; + + arp_ptr= (unsigned char *)(arp+1); + sha = arp_ptr; + arp_ptr += skb->dev->addr_len; + memcpy(&sip, arp_ptr, 4); + arp_ptr += 4; + tha = arp_ptr; + arp_ptr += skb->dev->addr_len; + memcpy(&tip, arp_ptr, 4); + + /* Should we ignore arp? */ + if (tip != in_dev->ifa_list->ifa_address || + LOOPBACK(tip) || MULTICAST(tip)) + return; + + + size = sizeof(struct arphdr) + 2 * (skb->dev->addr_len + 4); + send_skb = find_skb(np, size + LL_RESERVED_SPACE(np->dev), + LL_RESERVED_SPACE(np->dev)); + + if (!send_skb) + return; + + send_skb->nh.raw = send_skb->data; + arp = (struct arphdr *) skb_put(send_skb, size); + send_skb->dev = skb->dev; + send_skb->protocol = htons(ETH_P_ARP); + + /* Fill the device header for the ARP frame */ + + if (np->dev->hard_header && + np->dev->hard_header(send_skb, skb->dev, ptype, + np->remote_mac, np->local_mac, + send_skb->len) < 0) { + kfree_skb(send_skb); + return; + } + + /* + * Fill out the arp protocol part. + * + * we only support ethernet device type, + * which (according to RFC 1390) should always equal 1 (Ethernet). + */ + + arp->ar_hrd = htons(np->dev->type); + arp->ar_pro = htons(ETH_P_IP); + arp->ar_hln = np->dev->addr_len; + arp->ar_pln = 4; + arp->ar_op = htons(type); + + arp_ptr=(unsigned char *)(arp + 1); + memcpy(arp_ptr, np->dev->dev_addr, np->dev->addr_len); + arp_ptr += np->dev->addr_len; + memcpy(arp_ptr, &tip, 4); + arp_ptr += 4; + memcpy(arp_ptr, np->local_mac, np->dev->addr_len); + arp_ptr += np->dev->addr_len; + memcpy(arp_ptr, &sip, 4); + + netpoll_send_skb(np, send_skb); +} + +int netpoll_rx(struct sk_buff *skb) +{ + int proto, len, ulen; + struct iphdr *iph; + struct udphdr *uh; + struct netpoll *np; + struct list_head *p; + unsigned long flags; + + if (skb->dev->type != ARPHRD_ETHER) + goto out; + + /* check if netpoll clients need ARP */ + if (skb->protocol == __constant_htons(ETH_P_ARP) && trapped) { + arp_reply(skb); + return 1; + } + + proto = ntohs(skb->mac.ethernet->h_proto); + if (proto != ETH_P_IP) + goto out; + if (skb->pkt_type == PACKET_OTHERHOST) + goto out; + if (skb_shared(skb)) + goto out; + + iph = (struct iphdr *)skb->data; + if (!pskb_may_pull(skb, sizeof(struct iphdr))) + goto out; + if (iph->ihl < 5 || iph->version != 4) + goto out; + if (!pskb_may_pull(skb, iph->ihl*4)) + goto out; + if (ip_fast_csum((u8 *)iph, iph->ihl) != 0) + goto out; + + len = ntohs(iph->tot_len); + if (skb->len < len || len < iph->ihl*4) + goto out; + + if (iph->protocol != IPPROTO_UDP) + goto out; + + len -= iph->ihl*4; + uh = (struct udphdr *)(((char *)iph) + iph->ihl*4); + ulen = ntohs(uh->len); + + if (ulen != len) + goto out; + if (checksum_udp(skb, uh, ulen, iph->saddr, iph->daddr) < 0) + goto out; + + spin_lock_irqsave(&rx_list_lock, flags); + list_for_each(p, &rx_list) { + np = list_entry(p, struct netpoll, rx_list); + if (np->dev && np->dev != skb->dev) + continue; + if (np->local_ip && np->local_ip != ntohl(iph->daddr)) + continue; + if (np->remote_ip && np->remote_ip != ntohl(iph->saddr)) + continue; + if (np->local_port && np->local_port != ntohs(uh->dest)) + continue; + + spin_unlock_irqrestore(&rx_list_lock, flags); + + if (np->rx_hook) + np->rx_hook(np, ntohs(uh->source), + (char *)(uh+1), + ulen - sizeof(struct udphdr)); + + return 1; + } + spin_unlock_irqrestore(&rx_list_lock, flags); + +out: + return trapped; +} + +int netpoll_parse_options(struct netpoll *np, char *opt) +{ + char *cur=opt, *delim; + + if(*cur != '@') { + if ((delim = strchr(cur, '@')) == NULL) + goto parse_failed; + *delim=0; + np->local_port=simple_strtol(cur, 0, 10); + cur=delim; + } + cur++; + printk(KERN_INFO "%s: local port %d\n", np->name, np->local_port); + + if(*cur != '/') { + if ((delim = strchr(cur, '/')) == NULL) + goto parse_failed; + *delim=0; + np->local_ip=ntohl(in_aton(cur)); + cur=delim; + + printk(KERN_INFO "%s: local IP %d.%d.%d.%d\n", + np->name, HIPQUAD(np->local_ip)); + } + cur++; + + if ( *cur != ',') { + /* parse out dev name */ + if ((delim = strchr(cur, ',')) == NULL) + goto parse_failed; + *delim=0; + strlcpy(np->dev_name, cur, sizeof(np->dev_name)); + cur=delim; + } + cur++; + + printk(KERN_INFO "%s: interface %s\n", np->name, np->dev_name); + + if ( *cur != '@' ) { + /* dst port */ + if ((delim = strchr(cur, '@')) == NULL) + goto parse_failed; + *delim=0; + np->remote_port=simple_strtol(cur, 0, 10); + cur=delim; + } + cur++; + printk(KERN_INFO "%s: remote port %d\n", np->name, np->remote_port); + + /* dst ip */ + if ((delim = strchr(cur, '/')) == NULL) + goto parse_failed; + *delim=0; + np->remote_ip=ntohl(in_aton(cur)); + cur=delim+1; + + printk(KERN_INFO "%s: remote IP %d.%d.%d.%d\n", + np->name, HIPQUAD(np->remote_ip)); + + if( *cur != 0 ) + { + /* MAC address */ + if ((delim = strchr(cur, ':')) == NULL) + goto parse_failed; + *delim=0; + np->remote_mac[0]=simple_strtol(cur, 0, 16); + cur=delim+1; + if ((delim = strchr(cur, ':')) == NULL) + goto parse_failed; + *delim=0; + np->remote_mac[1]=simple_strtol(cur, 0, 16); + cur=delim+1; + if ((delim = strchr(cur, ':')) == NULL) + goto parse_failed; + *delim=0; + np->remote_mac[2]=simple_strtol(cur, 0, 16); + cur=delim+1; + if ((delim = strchr(cur, ':')) == NULL) + goto parse_failed; + *delim=0; + np->remote_mac[3]=simple_strtol(cur, 0, 16); + cur=delim+1; + if ((delim = strchr(cur, ':')) == NULL) + goto parse_failed; + *delim=0; + np->remote_mac[4]=simple_strtol(cur, 0, 16); + cur=delim+1; + np->remote_mac[5]=simple_strtol(cur, 0, 16); + } + + printk(KERN_INFO "%s: remote ethernet address " + "%02x:%02x:%02x:%02x:%02x:%02x\n", + np->name, + np->remote_mac[0], + np->remote_mac[1], + np->remote_mac[2], + np->remote_mac[3], + np->remote_mac[4], + np->remote_mac[5]); + + return 0; + + parse_failed: + printk(KERN_INFO "%s: couldn't parse config at %s!\n", + np->name, cur); + return -1; +} + +int netpoll_setup(struct netpoll *np) +{ + struct net_device *ndev = NULL; + struct in_device *in_dev; + + if (np->dev_name) + ndev = dev_get_by_name(np->dev_name); + if (!ndev) { + printk(KERN_ERR "%s: %s doesn't exist, aborting.\n", + np->name, np->dev_name); + return -1; + } + if (!ndev->poll_controller) { + printk(KERN_ERR "%s: %s doesn't support polling, aborting.\n", + np->name, np->dev_name); + goto release; + } + + if (!(ndev->flags & IFF_UP)) { + unsigned short oflags; + unsigned long atmost, atleast; + + printk(KERN_INFO "%s: device %s not up yet, forcing it\n", + np->name, np->dev_name); + + oflags = ndev->flags; + + rtnl_shlock(); + if (dev_change_flags(ndev, oflags | IFF_UP) < 0) { + printk(KERN_ERR "%s: failed to open %s\n", + np->name, np->dev_name); + rtnl_shunlock(); + goto release; + } + rtnl_shunlock(); + + atleast = jiffies + HZ/10; + atmost = jiffies + 10*HZ; + while (!netif_carrier_ok(ndev)) { + if (time_after(jiffies, atmost)) { + printk(KERN_NOTICE + "%s: timeout waiting for carrier\n", + np->name); + break; + } + cond_resched(); + } + + if (time_before(jiffies, atleast)) { + printk(KERN_NOTICE "%s: carrier detect appears flaky," + " waiting 10 seconds\n", + np->name); + while (time_before(jiffies, atmost)) + cond_resched(); + } + } + + if (!memcmp(np->local_mac, "\0\0\0\0\0\0", 6) && ndev->dev_addr) + memcpy(np->local_mac, ndev->dev_addr, 6); + + if (!np->local_ip) { + in_dev = in_dev_get(ndev); + + if (!in_dev) { + printk(KERN_ERR "%s: no IP address for %s, aborting\n", + np->name, np->dev_name); + goto release; + } + + np->local_ip = ntohl(in_dev->ifa_list->ifa_local); + in_dev_put(in_dev); + printk(KERN_INFO "%s: local IP %d.%d.%d.%d\n", + np->name, HIPQUAD(np->local_ip)); + } + + np->dev = ndev; + + if(np->rx_hook) { + unsigned long flags; + +#ifdef CONFIG_NETPOLL_RX + np->dev->netpoll_rx = 1; +#endif + + spin_lock_irqsave(&rx_list_lock, flags); + list_add(&np->rx_list, &rx_list); + spin_unlock_irqrestore(&rx_list_lock, flags); + } + + return 0; + release: + dev_put(ndev); + return -1; +} + +void netpoll_cleanup(struct netpoll *np) +{ + if(np->rx_hook) { + unsigned long flags; + + spin_lock_irqsave(&rx_list_lock, flags); + list_del(&np->rx_list); +#ifdef CONFIG_NETPOLL_RX + np->dev->netpoll_rx = 0; +#endif + spin_unlock_irqrestore(&rx_list_lock, flags); + } + + dev_put(np->dev); + np->dev = 0; +} + +int netpoll_trap() +{ + return trapped; +} + +void netpoll_set_trap(int trap) +{ + trapped = trap; +} + +EXPORT_SYMBOL(netpoll_set_trap); +EXPORT_SYMBOL(netpoll_trap); +EXPORT_SYMBOL(netpoll_parse_options); +EXPORT_SYMBOL(netpoll_setup); +EXPORT_SYMBOL(netpoll_cleanup); +EXPORT_SYMBOL(netpoll_send_skb); +EXPORT_SYMBOL(netpoll_send_udp); +EXPORT_SYMBOL(netpoll_poll); --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/net/ipmi/af_ipmi.c 2004-03-07 20:48:02.000000000 -0800 @@ -0,0 +1,612 @@ +/* + * IPMI Socket Glue + * + * Author: Louis Zhuang + * Copyright by Intel Corp., 2003 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IPMI_SOCKINTF_VERSION "v31" + +#ifdef CONFIG_DEBUG_KERNEL +static int debug = 0; +#define dbg(format, arg...) \ + do { \ + if(debug) \ + printk (KERN_DEBUG "%s: " format "\n", \ + __FUNCTION__, ## arg); \ + } while(0) +#else +#define dbg(format, arg...) +#endif /* CONFIG_DEBUG_KERNEL */ + +#define err(format, arg...) \ + printk(KERN_ERR "%s: " format "\n", \ + __FUNCTION__ , ## arg) +#define info(format, arg...) \ + printk(KERN_INFO "%s: " format "\n", \ + __FUNCTION__ , ## arg) +#define warn(format, arg...) \ + printk(KERN_WARNING "%s: " format "\n", \ + __FUNCTION__ , ## arg) +#define trace(format, arg...) \ + printk(KERN_INFO "%s(" format ")\n", \ + __FUNCTION__ , ## arg) + +struct ipmi_sock { + /* WARNING: sk has to be the first member */ + struct sock sk; + + ipmi_user_t user; + struct sockaddr_ipmi addr; + struct list_head msg_list; + + wait_queue_head_t wait; + spinlock_t lock; + + int default_retries; + unsigned int default_retry_time_ms; +}; + +static kmem_cache_t *ipmi_sk_cachep = NULL; + +static atomic_t ipmi_nr_socks = ATOMIC_INIT(0); + + + +/* + * utility functions + */ +static inline struct ipmi_sock *to_ipmi_sock(struct sock *sk) +{ + return container_of(sk, struct ipmi_sock, sk); +} + +static inline void ipmi_release_sock(struct sock *sk, int embrion) +{ + struct ipmi_sock *i = to_ipmi_sock(sk); + struct sk_buff *skb; + + if (i->user) { + ipmi_destroy_user(i->user); + i->user = NULL; + } + + sock_orphan(&i->sk); + sk->sk_shutdown = SHUTDOWN_MASK; + sk->sk_state = TCP_CLOSE; + + while((skb=skb_dequeue(&sk->sk_receive_queue))!=NULL) + kfree_skb(skb); + + sock_put(sk); +} + +static inline long ipmi_wait_for_queue(struct ipmi_sock *i, long timeo) +{ + + DECLARE_WAITQUEUE(wait, current); + + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue_exclusive(&i->wait, &wait); + timeo = schedule_timeout(timeo); + remove_wait_queue(&i->wait, &wait); + return timeo; +} + +/* + * IPMI operation functions + */ +static void sock_receive_handler(struct ipmi_recv_msg *msg, + void *handler_data) +{ + struct ipmi_sock *i = (struct ipmi_sock *)handler_data; + unsigned long flags; + + spin_lock_irqsave(&i->lock, flags); + list_add_tail(&msg->link, &i->msg_list); + spin_unlock_irqrestore(&i->lock, flags); + + wake_up_interruptible(&i->wait); +} + +/* + * protocol operation functions + */ +static int ipmi_release(struct socket *sock) +{ + struct sock *sk = sock->sk; + + if (!sk) + return 0; + + sock->sk=NULL; + ipmi_release_sock(sk, 0); + return 0; +} + +static struct ipmi_user_hndl ipmi_hnd = { + .ipmi_recv_hndl = sock_receive_handler +}; + +static int ipmi_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) +{ + struct ipmi_sock *i = to_ipmi_sock(sock->sk); + struct sockaddr_ipmi *addr = (struct sockaddr_ipmi *)uaddr; + int err = -EINVAL; + + if (i->user != NULL) { + dbg("Cannot bind twice: %p", i->user); + return -EINVAL; + } + + err = ipmi_create_user(addr->if_num, &ipmi_hnd, i, &i->user); + if (err) { + dbg("Cannot create user for the socket: %p", i->user); + return err; + } + + memcpy(&i->addr, addr, sizeof(i->addr)); + return 0; +} + +static int ipmi_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer) +{ + struct ipmi_sock *i = to_ipmi_sock(sock->sk); + memcpy(uaddr, &i->addr, sizeof(i->addr)); + return 0; +} + +static unsigned int ipmi_poll(struct file * file, struct socket *sock, poll_table *wait) +{ + unsigned int has_msg = 0; + struct ipmi_sock *i = to_ipmi_sock(sock->sk); + unsigned long flags; + + poll_wait(file, &i->wait, wait); + spin_lock_irqsave(&i->lock, flags); + if (!list_empty(&i->msg_list)) + has_msg = 1; + spin_unlock_irqrestore(&i->lock, flags); + + if (has_msg) + return POLLIN | POLLRDNORM; + return 0; +} + +static int ipmi_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) +{ + struct ipmi_sock *i = to_ipmi_sock(sock->sk); + struct ipmi_cmdspec val; + int ival; + unsigned int uival; + int err; + + dbg("cmd=%#x, arg=%#lx", cmd, arg); + switch(cmd) { + case SIOCIPMIREGCMD: + err = copy_from_user((void *)&val, (void *)arg, + sizeof(cmd)); + if (err) { + err = -EFAULT; + break; + } + + err = ipmi_register_for_cmd(i->user, val.netfn, + val.cmd); + break; + + case SIOCIPMIUNREGCMD: + err = copy_from_user((void *)&val, (void *)arg, + sizeof(cmd)); + if (err) { + err = -EFAULT; + break; + } + + err = ipmi_unregister_for_cmd(i->user, val.netfn, + val.cmd); + break; + + case SIOCIPMIGETEVENT: + err = copy_from_user((void *)&ival, (void *)arg, + sizeof(ival)); + if (err) { + err = -EFAULT; + break; + } + + err = ipmi_set_gets_events(i->user, ival); + break; + + case SIOCIPMISETADDR: + err = copy_from_user((void *)&uival, (void *)arg, + sizeof(uival)); + if (err) { + err = -EFAULT; + break; + } + + ipmi_set_my_address(i->user, uival); + break; + + case SIOCIPMIGETADDR: + uival = ipmi_get_my_address(i->user); + + if (copy_to_user((void *) arg, &uival, sizeof(uival))) { + err = -EFAULT; + break; + } + err = 0; + break; + + case SIOCIPMISETTIMING: + { + struct ipmi_timing_parms parms; + + if (copy_from_user(&parms, (void *) arg, sizeof(parms))) { + err = -EFAULT; + break; + } + + i->default_retries = parms.retries; + i->default_retry_time_ms = parms.retry_time_ms; + err = 0; + break; + } + + case SIOCIPMIGETTIMING: + { + struct ipmi_timing_parms parms; + + parms.retries = i->default_retries; + parms.retry_time_ms = i->default_retry_time_ms; + + if (copy_to_user((void *) arg, &parms, sizeof(parms))) { + err = -EFAULT; + break; + } + + err = 0; + break; + } + + default: + err = dev_ioctl(cmd, (void *)arg); + break; + } + + return err; +} + +static int ipmi_recvmsg(struct kiocb *iocb, struct socket *sock, + struct msghdr *msg, size_t size, + int rflags) +{ + struct ipmi_sock *i = to_ipmi_sock(sock->sk); + long timeo; + struct ipmi_recv_msg *rcvmsg; + struct sockaddr_ipmi addr; + char buf[IPMI_MAX_SOCK_MSG_LENGTH]; + struct ipmi_sock_msg *smsg = (struct ipmi_sock_msg *)buf; + int err; + unsigned long flags; + + + timeo = sock_rcvtimeo(&i->sk, rflags & MSG_DONTWAIT); + + while (1) { + spin_lock_irqsave(&i->lock, flags); + if (!list_empty(&i->msg_list)) + break; + spin_unlock_irqrestore(&i->lock, flags); + if (!timeo) { + return -EAGAIN; + } else if (signal_pending (current)) { + dbg("Signal pending: %d", 1); + return -EINTR; + } + + timeo = ipmi_wait_for_queue(i, timeo); + } + + rcvmsg = list_entry(i->msg_list.next, struct ipmi_recv_msg, link); + list_del(&rcvmsg->link); + spin_unlock_irqrestore(&i->lock, flags); + + memcpy(&addr.ipmi_addr, &rcvmsg->addr, sizeof(addr.ipmi_addr)); + addr.if_num = i->addr.if_num; + addr.sipmi_family = i->addr.sipmi_family; + memcpy(msg->msg_name, &addr, sizeof(addr)); + msg->msg_namelen = (SOCKADDR_IPMI_OVERHEAD + + ipmi_addr_length(rcvmsg->addr.addr_type)); + + smsg->recv_type = rcvmsg->recv_type; + smsg->msgid = rcvmsg->msgid; + smsg->netfn = rcvmsg->msg.netfn; + smsg->cmd = rcvmsg->msg.cmd; + smsg->data_len = rcvmsg->msg.data_len; + memcpy(smsg->data, rcvmsg->msg.data, smsg->data_len); + + ipmi_free_recv_msg(rcvmsg); + + err = memcpy_toiovec(msg->msg_iov, (void *)smsg, + sizeof(struct ipmi_sock_msg) + smsg->data_len); + if (err) { + dbg("Cannot copy data to user: %p", i->user); + return err; + } + + dbg("user=%p", i->user); + dbg("addr_type=%x, channel=%x", + addr.ipmi_addr.addr_type, addr.ipmi_addr.channel); + dbg("netfn=%#02x, cmd=%#02x, data=%p, data_len=%x", + smsg->netfn, smsg->cmd, smsg->data, smsg->data_len); + + return (sizeof(struct ipmi_sock_msg) + smsg->data_len); +} + +static int ipmi_sendmsg(struct kiocb *iocb, struct socket *sock, + struct msghdr *msg, size_t len) +{ + struct ipmi_sock *i = to_ipmi_sock(sock->sk); + struct sockaddr_ipmi *addr = (struct sockaddr_ipmi *)msg->msg_name; + struct ipmi_msg imsg; + unsigned char buf[IPMI_MAX_SOCK_MSG_LENGTH]; + struct ipmi_sock_msg *smsg = (struct ipmi_sock_msg *) buf; + int err; + struct ipmi_timing_parms tparms; + struct cmsghdr *cmsg; + + err = ipmi_validate_addr(&addr->ipmi_addr, + msg->msg_namelen - SOCKADDR_IPMI_OVERHEAD); + if (err) { + dbg("Invalid IPMI address: %p", i->user); + goto err; + } + + if (len > IPMI_MAX_SOCK_MSG_LENGTH) { + err = -EINVAL; + dbg("Message too long: %p", i->user); + goto err; + } + + if (len < sizeof(struct ipmi_sock_msg)) { + err = -EINVAL; + dbg("Msg data too small for header: %p", i->user); + goto err; + } + + err = memcpy_fromiovec((void *)smsg, msg->msg_iov, len); + if (err) { + dbg("Cannot copy data to kernel: %p", i->user); + goto err; + } + + if (len < smsg->data_len+sizeof(struct ipmi_sock_msg)) { + err = -EINVAL; + dbg("Msg data is out of bound: %p", i->user); + goto err; + } + + /* Set defaults. */ + tparms.retries = i->default_retries; + tparms.retry_time_ms = i->default_retry_time_ms; + + for (cmsg=CMSG_FIRSTHDR(msg); + cmsg; + cmsg = CMSG_NXTHDR(msg, cmsg)) + { + if (cmsg->cmsg_len < sizeof(struct cmsghdr)) { + err = -EINVAL; + dbg("cmsg length too short: %p", i->user); + goto err; + } + + if (cmsg->cmsg_level != SOL_SOCKET) + continue; + + if (cmsg->cmsg_type == IPMI_CMSG_TIMING_PARMS) { + struct ipmi_timing_parms *pparms; + + if (cmsg->cmsg_len != CMSG_LEN(sizeof(*pparms))) { + err = -EINVAL; + dbg("timing parms cmsg not right size: %p", + i->user); + goto err; + } + pparms = (struct ipmi_timing_parms *) CMSG_DATA(cmsg); + tparms.retries = pparms->retries; + tparms.retry_time_ms = pparms->retry_time_ms; + } + } + + imsg.netfn = smsg->netfn; + imsg.cmd = smsg->cmd; + imsg.data = smsg->data; + imsg.data_len = smsg->data_len; + + dbg("user=%p", i->user); + dbg("addr_type=%x, channel=%x", + addr->ipmi_addr.addr_type, addr->ipmi_addr.channel); + dbg("netfn=%#02x, cmd=%#02x, data=%p, data_len=%x", + imsg.netfn, imsg.cmd, imsg.data, imsg.data_len); + err = ipmi_request_settime(i->user, &addr->ipmi_addr, + smsg->msgid, &imsg, NULL, 0, + tparms.retries, tparms.retry_time_ms); + if (err) { + dbg("Cannot send message: %p", i->user); + goto err; + } + +err: + return err; +} + +static struct proto_ops ipmi_ops = { + .family = PF_IPMI, + .owner = THIS_MODULE, + .release = ipmi_release, + .bind = ipmi_bind, + .connect = sock_no_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .getname = ipmi_getname, + .poll = ipmi_poll, + .ioctl = ipmi_ioctl, + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .setsockopt = sock_no_setsockopt, + .getsockopt = sock_no_getsockopt, + .sendmsg = ipmi_sendmsg, + .recvmsg = ipmi_recvmsg, + .mmap = sock_no_mmap, + .sendpage = sock_no_sendpage +}; + + +static void ipmi_sock_destructor(struct sock *sk) +{ + skb_queue_purge(&sk->sk_receive_queue); + + BUG_TRAP(!atomic_read(&sk->sk_wmem_alloc)); + BUG_TRAP(sk_unhashed(sk)); + BUG_TRAP(!sk->sk_socket); + if (!sock_flag(sk, SOCK_DEAD)) { + printk("Attempt to release alive ipmi socket: %p\n", sk); + return; + } + + atomic_dec(&ipmi_nr_socks); +} + +/* + * net protocol functions + */ +static struct ipmi_sock *ipmi_socket_create1(struct socket *sock) +{ + struct ipmi_sock *i; + + if (atomic_read(&ipmi_nr_socks) >= 2*files_stat.max_files) + return NULL; + + i = (struct ipmi_sock *)sk_alloc(PF_IPMI, GFP_KERNEL, + sizeof(struct ipmi_sock), ipmi_sk_cachep); + if (!i) { + return NULL; + } + + + sock_init_data(sock, &i->sk); + sk_set_owner(&i->sk, THIS_MODULE); + i->sk.sk_destruct = ipmi_sock_destructor; + i->sk.sk_rcvtimeo = 5*HZ; + spin_lock_init(&i->lock); + INIT_LIST_HEAD(&i->msg_list); + init_waitqueue_head(&i->wait); + + /* Set to use default values. */ + i->default_retries = -1; + i->default_retry_time_ms = 0; + + atomic_inc(&ipmi_nr_socks); + return i; +} + +static int ipmi_socket_create(struct socket *sock, int protocol) +{ + if (!capable(CAP_NET_RAW)) + return -EPERM; + if (protocol && protocol != PF_IPMI) + return -EPROTONOSUPPORT; + + sock->state = SS_UNCONNECTED; + + switch (sock->type) { + case SOCK_RAW: + sock->type=SOCK_DGRAM; + case SOCK_DGRAM: + sock->ops = &ipmi_ops; + break; + default: + return -EPROTONOSUPPORT; + } + + return ipmi_socket_create1(sock)? 0 : -ENOMEM; +} + +static struct net_proto_family ipmi_family_ops = { + .family = PF_IPMI, + .create = ipmi_socket_create, + .owner = THIS_MODULE, +}; + + +/* + * init/exit functions + */ +static int __init ipmi_socket_init(void) +{ + + int err=0; + + printk(KERN_INFO "ipmi socket interface version " + IPMI_SOCKINTF_VERSION "\n"); + + ipmi_sk_cachep = kmem_cache_create("ipmi_sock", + sizeof(struct ipmi_sock), 0, + SLAB_HWCACHE_ALIGN, 0, 0); + if (!ipmi_sk_cachep) { + printk(KERN_CRIT "%s: Unable to create ipmi_sock SLAB cache!\n", __func__); + err = -ENOMEM; + goto out; + } + + err = sock_register(&ipmi_family_ops); + if (err) + kmem_cache_destroy(ipmi_sk_cachep); +out: + return err; +} + +static void __exit ipmi_socket_exit(void) +{ + sock_unregister(PF_IPMI); + kmem_cache_destroy(ipmi_sk_cachep); +} + +#ifdef CONFIG_DEBUG_KERNEL +module_param(debug, int, 0); +#endif +module_init(ipmi_socket_init); +module_exit(ipmi_socket_exit); + +MODULE_LICENSE("GPL"); --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/net/ipmi/Makefile 2004-03-07 20:48:02.000000000 -0800 @@ -0,0 +1 @@ +obj-$(CONFIG_IPMI_SOCKET) = af_ipmi.o --- linux-2.6.4-rc2/net/ipv4/igmp.c 2004-02-17 20:48:46.000000000 -0800 +++ 25/net/ipv4/igmp.c 2004-03-07 20:46:46.000000000 -0800 @@ -105,7 +105,8 @@ #include #endif -#define IP_MAX_MEMBERSHIPS 20 +#define IP_MAX_MEMBERSHIPS 20 +#define IP_MAX_MSF 10 #ifdef CONFIG_IP_MULTICAST /* Parameter names and values are taken from igmp-v2-06 draft */ @@ -1325,6 +1326,7 @@ static struct in_device * ip_mc_find_dev * Join a socket to a group */ int sysctl_igmp_max_memberships = IP_MAX_MEMBERSHIPS; +int sysctl_igmp_max_msf = IP_MAX_MSF; static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode, @@ -1790,6 +1792,10 @@ int ip_mc_source(int add, int omode, str } /* else, add a new source to the filter */ + if (psl && psl->sl_count >= sysctl_igmp_max_msf) { + err = -ENOBUFS; + goto done; + } if (!psl || psl->sl_count == psl->sl_max) { struct ip_sf_socklist *newpsl; int count = IP_SFBLOCK; --- linux-2.6.4-rc2/net/ipv4/ipconfig.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/net/ipv4/ipconfig.c 2004-03-07 20:47:34.000000000 -0800 @@ -1189,12 +1189,47 @@ static struct file_operations pnp_seq_fo #endif /* CONFIG_PROC_FS */ /* + * Extract IP address from the parameter string if needed. Note that we + * need to have root_server_addr set _before_ IPConfig gets called as it + * can override it. + */ +u32 __init root_nfs_parse_addr(char *name) +{ + u32 addr; + int octets = 0; + char *cp, *cq; + + cp = cq = name; + while (octets < 4) { + while (*cp >= '0' && *cp <= '9') + cp++; + if (cp == cq || cp - cq > 3) + break; + if (*cp == '.' || octets == 3) + octets++; + if (octets < 4) + cp++; + cq = cp; + } + if (octets == 4 && (*cp == ':' || *cp == '\0')) { + if (*cp == ':') + *cp++ = '\0'; + addr = in_aton(name); + strcpy(name, cp); + } else + addr = INADDR_NONE; + + return addr; +} + +/* * IP Autoconfig dispatcher. */ static int __init ip_auto_config(void) { unsigned long jiff; + u32 addr; #ifdef CONFIG_PROC_FS proc_net_fops_create("pnp", S_IRUGO, &pnp_seq_fops); @@ -1283,6 +1318,10 @@ static int __init ip_auto_config(void) ic_dev = ic_first_dev->dev; } + addr = root_nfs_parse_addr(root_server_path); + if (root_server_addr == INADDR_NONE) + root_server_addr = addr; + /* * Use defaults whereever applicable. */ --- linux-2.6.4-rc2/net/ipv4/ip_sockglue.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/net/ipv4/ip_sockglue.c 2004-03-07 20:46:46.000000000 -0800 @@ -618,6 +618,7 @@ int ip_setsockopt(struct sock *sk, int l case IP_MSFILTER: { extern int sysctl_optmem_max; + extern int sysctl_igmp_max_msf; struct ip_msfilter *msf; if (optlen < IP_MSFILTER_SIZE(0)) @@ -636,9 +637,14 @@ int ip_setsockopt(struct sock *sk, int l kfree(msf); break; } - if (IP_MSFILTER_SIZE(msf->imsf_numsrc) < - IP_MSFILTER_SIZE(0) || - IP_MSFILTER_SIZE(msf->imsf_numsrc) > optlen) { + /* numsrc >= (1G-4) overflow in 32 bits */ + if (msf->imsf_numsrc >= 0x3ffffffcU || + msf->imsf_numsrc > sysctl_igmp_max_msf) { + kfree(msf); + err = -ENOBUFS; + break; + } + if (IP_MSFILTER_SIZE(msf->imsf_numsrc) > optlen) { kfree(msf); err = -EINVAL; break; --- linux-2.6.4-rc2/net/ipv4/sysctl_net_ipv4.c 2004-02-17 20:48:46.000000000 -0800 +++ 25/net/ipv4/sysctl_net_ipv4.c 2004-03-07 20:46:46.000000000 -0800 @@ -39,6 +39,7 @@ extern int sysctl_icmp_ratemask; /* From igmp.c */ extern int sysctl_igmp_max_memberships; +extern int sysctl_igmp_max_msf; /* From inetpeer.c */ extern int inet_peer_threshold; @@ -412,6 +413,14 @@ ctl_table ipv4_table[] = { #endif { + .ctl_name = NET_IPV4_IGMP_MAX_MSF, + .procname = "igmp_max_msf", + .data = &sysctl_igmp_max_msf, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec + }, + { .ctl_name = NET_IPV4_INET_PEER_THRESHOLD, .procname = "inet_peer_threshold", .data = &inet_peer_threshold, --- linux-2.6.4-rc2/net/Kconfig 2004-03-03 23:12:49.000000000 -0800 +++ 25/net/Kconfig 2004-03-07 20:48:02.000000000 -0800 @@ -71,6 +71,17 @@ config UNIX Say Y unless you know what you are doing. +config IPMI_SOCKET + tristate "IPMI sockets" + depends on IPMI_HANDLER + ---help--- + If you say Y here, you will include support for IPMI sockets; + This way you don't have to use devices to access IPMI. You + must also enable the IPMI message handler and a low-level + driver in the Character Drivers if you enable this. + + If unsure, say N. + config NET_KEY tristate "PF_KEY sockets" select XFRM @@ -658,4 +669,19 @@ source "net/irda/Kconfig" source "net/bluetooth/Kconfig" +config KGDBOE + def_bool X86 && KGDB + +config NETPOLL + def_bool NETCONSOLE || KGDBOE + +config NETPOLL_RX + def_bool KGDBOE + +config NETPOLL_TRAP + def_bool KGDBOE + +config NET_POLL_CONTROLLER + def_bool NETPOLL + endmenu --- linux-2.6.4-rc2/net/Makefile 2003-10-08 15:07:10.000000000 -0700 +++ 25/net/Makefile 2004-03-07 20:48:02.000000000 -0800 @@ -38,6 +38,7 @@ obj-$(CONFIG_DECNET) += decnet/ obj-$(CONFIG_ECONET) += econet/ obj-$(CONFIG_VLAN_8021Q) += 8021q/ obj-$(CONFIG_IP_SCTP) += sctp/ +obj-$(CONFIG_IPMI_SOCKET) += ipmi/ ifeq ($(CONFIG_NET),y) obj-$(CONFIG_SYSCTL) += sysctl_net.o --- linux-2.6.4-rc2/net/sunrpc/auth_gss/svcauth_gss.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/net/sunrpc/auth_gss/svcauth_gss.c 2004-03-07 20:46:46.000000000 -0800 @@ -594,12 +594,13 @@ gss_write_verf(struct svc_rqst *rqstp, s iov.iov_len = sizeof(xdr_seq); xdr_buf_from_iov(&iov, &verf_data); p = rqstp->rq_res.head->iov_base + rqstp->rq_res.head->iov_len; + mic.data = (u8 *)(p + 1); maj_stat = gss_get_mic(ctx_id, 0, &verf_data, &mic); if (maj_stat != GSS_S_COMPLETE) return -1; - p = xdr_encode_netobj(rqstp->rq_res.head->iov_base - + rqstp->rq_res.head->iov_len, &mic); - kfree(mic.data); + *p++ = htonl(mic.len); + memset((u8 *)p + mic.len, 0, round_up_to_quad(mic.len) - mic.len); + p += XDR_QUADLEN(mic.len); if (!xdr_ressize_check(rqstp, p)) return -1; return 0; --- linux-2.6.4-rc2/net/sunrpc/auth_unix.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/net/sunrpc/auth_unix.c 2004-03-07 20:47:33.000000000 -0800 @@ -149,7 +149,7 @@ unx_marshal(struct rpc_task *task, u32 * struct rpc_clnt *clnt = task->tk_client; struct unx_cred *cred = (struct unx_cred *) task->tk_msg.rpc_cred; u32 *base, *hold; - int i, n; + int i; *p++ = htonl(RPC_AUTH_UNIX); base = p++; @@ -158,10 +158,7 @@ unx_marshal(struct rpc_task *task, u32 * /* * Copy the UTS nodename captured when the client was created. */ - n = clnt->cl_nodelen; - *p++ = htonl(n); - memcpy(p, clnt->cl_nodename, n); - p += (n + 3) >> 2; + p = xdr_encode_array(p, clnt->cl_nodename, clnt->cl_nodelen); /* Note: we don't use real uid if it involves raising privilege */ if (ruid && cred->uc_puid != 0 && cred->uc_pgid != 0) { --- linux-2.6.4-rc2/net/sunrpc/clnt.c 2004-02-17 20:48:46.000000000 -0800 +++ 25/net/sunrpc/clnt.c 2004-03-07 20:47:33.000000000 -0800 @@ -102,19 +102,22 @@ rpc_create_client(struct rpc_xprt *xprt, { struct rpc_version *version; struct rpc_clnt *clnt = NULL; + int err; int len; dprintk("RPC: creating %s client for %s (xprt %p)\n", program->name, servname, xprt); + err = -EINVAL; if (!xprt) - goto out; + goto out_err; if (vers >= program->nrvers || !(version = program->version[vers])) - goto out; + goto out_err; + err = -ENOMEM; clnt = (struct rpc_clnt *) kmalloc(sizeof(*clnt), GFP_KERNEL); if (!clnt) - goto out_no_clnt; + goto out_err; memset(clnt, 0, sizeof(*clnt)); atomic_set(&clnt->cl_users, 0); atomic_set(&clnt->cl_count, 1); @@ -149,9 +152,11 @@ rpc_create_client(struct rpc_xprt *xprt, clnt->cl_rtt = &clnt->cl_rtt_default; rpc_init_rtt(&clnt->cl_rtt_default, xprt->timeout.to_initval); - if (rpc_setup_pipedir(clnt, program->pipe_dir_name) < 0) + err = rpc_setup_pipedir(clnt, program->pipe_dir_name); + if (err < 0) goto out_no_path; + err = -ENOMEM; if (!rpcauth_create(flavor, clnt)) { printk(KERN_INFO "RPC: Couldn't create auth handle (flavor %u)\n", flavor); @@ -163,20 +168,16 @@ rpc_create_client(struct rpc_xprt *xprt, if (clnt->cl_nodelen > UNX_MAXNODENAME) clnt->cl_nodelen = UNX_MAXNODENAME; memcpy(clnt->cl_nodename, system_utsname.nodename, clnt->cl_nodelen); -out: return clnt; -out_no_clnt: - printk(KERN_INFO "RPC: out of memory in rpc_create_client\n"); - goto out; out_no_auth: rpc_rmdir(clnt->cl_pathname); out_no_path: if (clnt->cl_server != clnt->cl_inline_name) kfree(clnt->cl_server); kfree(clnt); - clnt = NULL; - goto out; +out_err: + return ERR_PTR(err); } /* @@ -198,11 +199,10 @@ rpc_clone_client(struct rpc_clnt *clnt) atomic_inc(&new->cl_parent->cl_count); if (new->cl_auth) atomic_inc(&new->cl_auth->au_count); -out: return new; out_no_clnt: printk(KERN_INFO "RPC: out of memory in %s\n", __FUNCTION__); - goto out; + return ERR_PTR(-ENOMEM); } /* @@ -611,9 +611,6 @@ call_encode(struct rpc_task *task) rcvbuf->page_len = 0; rcvbuf->len = bufsiz; - /* Zero buffer so we have automatic zero-padding of opaque & string */ - memset(task->tk_buffer, 0, bufsiz); - /* Encode header and provided arguments */ encode = task->tk_msg.rpc_proc->p_encode; if (!(p = call_header(task))) { --- linux-2.6.4-rc2/net/sunrpc/pmap_clnt.c 2004-02-17 20:48:46.000000000 -0800 +++ 25/net/sunrpc/pmap_clnt.c 2004-03-07 20:47:30.000000000 -0800 @@ -65,9 +65,11 @@ rpc_getport(struct rpc_task *task, struc map->pm_binding = 1; spin_unlock(&pmap_lock); - task->tk_status = -EACCES; /* why set this? returns -EIO below */ - if (!(pmap_clnt = pmap_create(clnt->cl_server, sap, map->pm_prot))) + pmap_clnt = pmap_create(clnt->cl_server, sap, map->pm_prot); + if (IS_ERR(pmap_clnt)) { + task->tk_status = PTR_ERR(pmap_clnt); goto bailout; + } task->tk_status = 0; /* @@ -110,8 +112,9 @@ rpc_getport_external(struct sockaddr_in NIPQUAD(sin->sin_addr.s_addr), prog, vers, prot); sprintf(hostname, "%u.%u.%u.%u", NIPQUAD(sin->sin_addr.s_addr)); - if (!(pmap_clnt = pmap_create(hostname, sin, prot))) - return -EACCES; + pmap_clnt = pmap_create(hostname, sin, prot); + if (IS_ERR(pmap_clnt)) + return PTR_ERR(pmap_clnt); /* Setup the call info struct */ status = rpc_call(pmap_clnt, PMAP_GETPORT, &map, &map.pm_port, 0); @@ -161,16 +164,18 @@ rpc_register(u32 prog, u32 vers, int pro struct sockaddr_in sin; struct rpc_portmap map; struct rpc_clnt *pmap_clnt; - unsigned int error = 0; + int error = 0; dprintk("RPC: registering (%d, %d, %d, %d) with portmapper.\n", prog, vers, prot, port); sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - if (!(pmap_clnt = pmap_create("localhost", &sin, IPPROTO_UDP))) { - dprintk("RPC: couldn't create pmap client\n"); - return -EACCES; + pmap_clnt = pmap_create("localhost", &sin, IPPROTO_UDP); + if (IS_ERR(pmap_clnt)) { + error = PTR_ERR(pmap_clnt); + dprintk("RPC: couldn't create pmap client. Error = %d\n", error); + return error; } map.pm_prog = prog; @@ -199,15 +204,16 @@ pmap_create(char *hostname, struct socka struct rpc_clnt *clnt; /* printk("pmap: create xprt\n"); */ - if (!(xprt = xprt_create_proto(proto, srvaddr, NULL))) - return NULL; + xprt = xprt_create_proto(proto, srvaddr, NULL); + if (IS_ERR(xprt)) + return (struct rpc_clnt *)xprt; xprt->addr.sin_port = htons(RPC_PMAP_PORT); /* printk("pmap: create clnt\n"); */ clnt = rpc_create_client(xprt, hostname, &pmap_program, RPC_PMAP_VERSION, RPC_AUTH_NULL); - if (!clnt) { + if (IS_ERR(clnt)) { xprt_destroy(xprt); } else { clnt->cl_softrtry = 1; --- linux-2.6.4-rc2/net/sunrpc/sched.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/net/sunrpc/sched.c 2004-03-07 20:47:29.000000000 -0800 @@ -530,6 +530,9 @@ __rpc_execute(struct rpc_task *task) if (!task->tk_action) break; task->tk_action(task); + /* micro-optimization to avoid spinlock */ + if (RPC_IS_RUNNING(task)) + continue; } /* @@ -545,29 +548,31 @@ __rpc_execute(struct rpc_task *task) } spin_unlock_bh(&rpc_queue_lock); - while (RPC_IS_SLEEPING(task)) { - /* sync task: sleep here */ - dprintk("RPC: %4d sync task going to sleep\n", - task->tk_pid); - if (current->pid == rpciod_pid) - printk(KERN_ERR "RPC: rpciod waiting on sync task!\n"); + if (!RPC_IS_SLEEPING(task)) + continue; + /* sync task: sleep here */ + dprintk("RPC: %4d sync task going to sleep\n", task->tk_pid); + if (current->pid == rpciod_pid) + printk(KERN_ERR "RPC: rpciod waiting on sync task!\n"); + if (!task->tk_client->cl_intr) { __wait_event(task->tk_wait, !RPC_IS_SLEEPING(task)); - dprintk("RPC: %4d sync task resuming\n", task->tk_pid); - + } else { + __wait_event_interruptible(task->tk_wait, !RPC_IS_SLEEPING(task), status); /* * When a sync task receives a signal, it exits with * -ERESTARTSYS. In order to catch any callbacks that * clean up after sleeping on some queue, we don't * break the loop here, but go around once more. */ - if (task->tk_client->cl_intr && signalled()) { + if (status == -ERESTARTSYS) { dprintk("RPC: %4d got signal\n", task->tk_pid); task->tk_flags |= RPC_TASK_KILLED; rpc_exit(task, -ERESTARTSYS); rpc_wake_up_task(task); } } + dprintk("RPC: %4d sync task resuming\n", task->tk_pid); } if (task->tk_exit) { --- linux-2.6.4-rc2/net/sunrpc/sunrpc_syms.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/net/sunrpc/sunrpc_syms.c 2004-03-07 20:47:33.000000000 -0800 @@ -63,6 +63,8 @@ EXPORT_SYMBOL(rpc_mkpipe); EXPORT_SYMBOL(xprt_create_proto); EXPORT_SYMBOL(xprt_destroy); EXPORT_SYMBOL(xprt_set_timeout); +EXPORT_SYMBOL(xprt_udp_slot_table_entries); +EXPORT_SYMBOL(xprt_tcp_slot_table_entries); /* Client credential cache */ EXPORT_SYMBOL(rpcauth_register); --- linux-2.6.4-rc2/net/sunrpc/sysctl.c 2004-02-03 20:42:39.000000000 -0800 +++ 25/net/sunrpc/sysctl.c 2004-03-07 20:47:33.000000000 -0800 @@ -1,7 +1,7 @@ /* * linux/net/sunrpc/sysctl.c * - * Sysctl interface to sunrpc module. This is for debugging only now. + * Sysctl interface to sunrpc module. * * I would prefer to register the sunrpc table below sys/net, but that's * impossible at the moment. @@ -19,6 +19,7 @@ #include #include #include +#include /* * Declare the debug flags here @@ -117,6 +118,9 @@ done: return 0; } +static unsigned int min_slot_table_size = RPC_MIN_SLOT_TABLE; +static unsigned int max_slot_table_size = RPC_MAX_SLOT_TABLE; + static ctl_table debug_table[] = { { .ctl_name = CTL_RPCDEBUG, @@ -150,6 +154,28 @@ static ctl_table debug_table[] = { .mode = 0644, .proc_handler = &proc_dodebug }, + { + .ctl_name = CTL_SLOTTABLE_UDP, + .procname = "udp_slot_table_entries", + .data = &xprt_udp_slot_table_entries, + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = &proc_dointvec_minmax, + .strategy = &sysctl_intvec, + .extra1 = &min_slot_table_size, + .extra2 = &max_slot_table_size + }, + { + .ctl_name = CTL_SLOTTABLE_TCP, + .procname = "tcp_slot_table_entries", + .data = &xprt_tcp_slot_table_entries, + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = &proc_dointvec_minmax, + .strategy = &sysctl_intvec, + .extra1 = &min_slot_table_size, + .extra2 = &max_slot_table_size + }, { .ctl_name = 0 } }; --- linux-2.6.4-rc2/net/sunrpc/xdr.c 2004-02-17 20:48:46.000000000 -0800 +++ 25/net/sunrpc/xdr.c 2004-03-07 20:47:33.000000000 -0800 @@ -54,7 +54,7 @@ xdr_decode_netobj(u32 *p, struct xdr_net } u32 * -xdr_encode_array(u32 *p, const char *array, unsigned int len) +xdr_encode_array(u32 *p, const void *array, unsigned int len) { int quadlen = XDR_QUADLEN(len); --- linux-2.6.4-rc2/net/sunrpc/xprt.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/net/sunrpc/xprt.c 2004-03-07 20:47:33.000000000 -0800 @@ -57,6 +57,7 @@ #include #include #include +#include #include #include @@ -74,6 +75,7 @@ #define XPRT_MAX_BACKOFF (8) #define XPRT_IDLE_TIMEOUT (5*60*HZ) +#define XPRT_MAX_RESVPORT (800) /* * Local functions @@ -84,7 +86,7 @@ static void xprt_disconnect(struct rpc_x static void xprt_connect_status(struct rpc_task *task); static struct rpc_xprt * xprt_setup(int proto, struct sockaddr_in *ap, struct rpc_timeout *to); -static struct socket *xprt_create_socket(int, struct rpc_timeout *, int); +static struct socket *xprt_create_socket(struct rpc_xprt *, int, int); static void xprt_bind_socket(struct rpc_xprt *, struct socket *); static int __xprt_get_cong(struct rpc_xprt *, struct rpc_task *); @@ -336,8 +338,8 @@ xprt_adjust_cwnd(struct rpc_xprt *xprt, /* The (cwnd >> 1) term makes sure * the result gets rounded properly. */ cwnd += (RPC_CWNDSCALE * RPC_CWNDSCALE + (cwnd >> 1)) / cwnd; - if (cwnd > RPC_MAXCWND) - cwnd = RPC_MAXCWND; + if (cwnd > RPC_MAXCWND(xprt)) + cwnd = RPC_MAXCWND(xprt); __xprt_lock_write_next(xprt); } else if (result == -ETIMEDOUT) { cwnd >>= 1; @@ -452,6 +454,68 @@ out_abort: spin_unlock(&xprt->sock_lock); } +static void +xprt_socket_connect(void *args) +{ + struct rpc_xprt *xprt = (struct rpc_xprt *)args; + struct socket *sock = xprt->sock; + int status = -EIO; + + if (xprt->shutdown) { + rpc_wake_up_status(&xprt->pending, -EIO); + return; + } + if (!xprt->addr.sin_port) + goto out_err; + + /* + * Start by resetting any existing state + */ + xprt_close(xprt); + sock = xprt_create_socket(xprt, xprt->prot, xprt->resvport); + if (sock == NULL) { + /* couldn't create socket or bind to reserved port; + * this is likely a permanent error, so cause an abort */ + goto out_err; + return; + } + xprt_bind_socket(xprt, sock); + xprt_sock_setbufsize(xprt); + + if (!xprt->stream) + goto out; + + /* + * Tell the socket layer to start connecting... + */ + status = sock->ops->connect(sock, (struct sockaddr *) &xprt->addr, + sizeof(xprt->addr), O_NONBLOCK); + dprintk("RPC: %p connect status %d connected %d sock state %d\n", + xprt, -status, xprt_connected(xprt), sock->sk->sk_state); + if (status >= 0) + goto out; + switch (status) { + case -EINPROGRESS: + case -EALREADY: + return; + default: + goto out_err; + } +out: + spin_lock_bh(&xprt->sock_lock); + if (xprt->snd_task) + rpc_wake_up_task(xprt->snd_task); + spin_unlock_bh(&xprt->sock_lock); + return; +out_err: + spin_lock_bh(&xprt->sock_lock); + if (xprt->snd_task) { + xprt->snd_task->tk_status = status; + rpc_wake_up_task(xprt->snd_task); + } + spin_unlock_bh(&xprt->sock_lock); +} + /* * Attempt to connect a TCP socket. * @@ -460,9 +524,6 @@ void xprt_connect(struct rpc_task *task) { struct rpc_xprt *xprt = task->tk_xprt; - struct socket *sock = xprt->sock; - struct sock *inet; - int status; dprintk("RPC: %4d xprt_connect xprt %p %s connected\n", task->tk_pid, xprt, (xprt_connected(xprt) ? "is" : "is not")); @@ -483,79 +544,9 @@ xprt_connect(struct rpc_task *task) if (task->tk_rqstp) task->tk_rqstp->rq_bytes_sent = 0; - /* - * We're here because the xprt was marked disconnected. - * Start by resetting any existing state. - */ - xprt_close(xprt); - if (!(sock = xprt_create_socket(xprt->prot, &xprt->timeout, xprt->resvport))) { - /* couldn't create socket or bind to reserved port; - * this is likely a permanent error, so cause an abort */ - task->tk_status = -EIO; - goto out_write; - } - xprt_bind_socket(xprt, sock); - xprt_sock_setbufsize(xprt); - - if (!xprt->stream) - goto out_write; - - inet = sock->sk; - - /* - * Tell the socket layer to start connecting... - */ - status = sock->ops->connect(sock, (struct sockaddr *) &xprt->addr, - sizeof(xprt->addr), O_NONBLOCK); - dprintk("RPC: %4d connect status %d connected %d sock state %d\n", - task->tk_pid, -status, xprt_connected(xprt), inet->sk_state); - - if (status >= 0) - return; - - switch (status) { - case -EINPROGRESS: - case -EALREADY: - /* Protect against TCP socket state changes */ - lock_sock(inet); - if (inet->sk_state != TCP_ESTABLISHED) { - dprintk("RPC: %4d waiting for connection\n", - task->tk_pid); - task->tk_timeout = RPC_CONNECT_TIMEOUT; - /* if the socket is already closing, delay briefly */ - if ((1 << inet->sk_state) & - ~(TCPF_SYN_SENT | TCPF_SYN_RECV)) - task->tk_timeout = RPC_REESTABLISH_TIMEOUT; - rpc_sleep_on(&xprt->pending, task, xprt_connect_status, - NULL); - } - release_sock(inet); - break; - case -ECONNREFUSED: - case -ECONNRESET: - case -ENOTCONN: - if (!RPC_IS_SOFT(task)) { - rpc_delay(task, RPC_REESTABLISH_TIMEOUT); - task->tk_status = -ENOTCONN; - break; - } - default: - /* Report myriad other possible returns. If this file - * system is soft mounted, just error out, like Solaris. */ - if (RPC_IS_SOFT(task)) { - printk(KERN_WARNING - "RPC: error %d connecting to server %s, exiting\n", - -status, task->tk_client->cl_server); - task->tk_status = -EIO; - goto out_write; - } - printk(KERN_WARNING "RPC: error %d connecting to server %s\n", - -status, task->tk_client->cl_server); - /* This will prevent anybody else from reconnecting */ - rpc_delay(task, RPC_REESTABLISH_TIMEOUT); - task->tk_status = status; - break; - } + task->tk_timeout = RPC_CONNECT_TIMEOUT; + rpc_sleep_on(&xprt->pending, task, xprt_connect_status, NULL); + schedule_work(&xprt->sock_connect); return; out_write: xprt_release_write(xprt, task); @@ -580,6 +571,8 @@ xprt_connect_status(struct rpc_task *tas task->tk_status = -EIO; switch (task->tk_status) { + case -ECONNREFUSED: + case -ECONNRESET: case -ENOTCONN: rpc_delay(task, RPC_REESTABLISH_TIMEOUT); return; @@ -1313,10 +1306,9 @@ do_xprt_reserve(struct rpc_task *task) task->tk_status = 0; if (task->tk_rqstp) return; - if (xprt->free) { - struct rpc_rqst *req = xprt->free; - xprt->free = req->rq_next; - req->rq_next = NULL; + if (!list_empty(&xprt->free)) { + struct rpc_rqst *req = list_entry(xprt->free.next, struct rpc_rqst, rq_list); + list_del_init(&req->rq_list); task->tk_rqstp = req; xprt_request_init(task, xprt); return; @@ -1330,22 +1322,14 @@ do_xprt_reserve(struct rpc_task *task) /* * Allocate a 'unique' XID */ -static u32 -xprt_alloc_xid(void) +static inline u32 xprt_alloc_xid(struct rpc_xprt *xprt) +{ + return xprt->xid++; +} + +static inline void xprt_init_xid(struct rpc_xprt *xprt) { - static spinlock_t xid_lock = SPIN_LOCK_UNLOCKED; - static int need_init = 1; - static u32 xid; - u32 ret; - - spin_lock(&xid_lock); - if (unlikely(need_init)) { - xid = get_seconds() << 12; - need_init = 0; - } - ret = xid++; - spin_unlock(&xid_lock); - return ret; + get_random_bytes(&xprt->xid, sizeof(xprt->xid)); } /* @@ -1359,8 +1343,7 @@ xprt_request_init(struct rpc_task *task, req->rq_timeout = xprt->timeout; req->rq_task = task; req->rq_xprt = xprt; - req->rq_xid = xprt_alloc_xid(); - INIT_LIST_HEAD(&req->rq_list); + req->rq_xid = xprt_alloc_xid(xprt); dprintk("RPC: %4d reserved req %p xid %08x\n", task->tk_pid, req, req->rq_xid); } @@ -1391,9 +1374,7 @@ xprt_release(struct rpc_task *task) dprintk("RPC: %4d release request %p\n", task->tk_pid, req); spin_lock(&xprt->xprt_lock); - req->rq_next = xprt->free; - xprt->free = req; - + list_add(&req->rq_list, &xprt->free); xprt_clear_backlog(xprt); spin_unlock(&xprt->xprt_lock); } @@ -1424,6 +1405,9 @@ xprt_set_timeout(struct rpc_timeout *to, to->to_exponential = 0; } +unsigned int xprt_udp_slot_table_entries = RPC_DEF_SLOT_TABLE; +unsigned int xprt_tcp_slot_table_entries = RPC_DEF_SLOT_TABLE << 2; + /* * Initialize an RPC client */ @@ -1431,21 +1415,33 @@ static struct rpc_xprt * xprt_setup(int proto, struct sockaddr_in *ap, struct rpc_timeout *to) { struct rpc_xprt *xprt; + unsigned int entries; + size_t slot_table_size; struct rpc_rqst *req; - int i; dprintk("RPC: setting up %s transport...\n", proto == IPPROTO_UDP? "UDP" : "TCP"); + entries = (proto == IPPROTO_TCP)? + xprt_tcp_slot_table_entries : xprt_udp_slot_table_entries; + if ((xprt = kmalloc(sizeof(struct rpc_xprt), GFP_KERNEL)) == NULL) - return NULL; + return ERR_PTR(-ENOMEM); memset(xprt, 0, sizeof(*xprt)); /* Nnnngh! */ + xprt->max_reqs = entries; + slot_table_size = entries * sizeof(xprt->slot[0]); + xprt->slot = kmalloc(slot_table_size, GFP_KERNEL); + if (xprt->slot == NULL) { + kfree(xprt); + return ERR_PTR(-ENOMEM); + } + memset(xprt->slot, 0, slot_table_size); xprt->addr = *ap; xprt->prot = proto; xprt->stream = (proto == IPPROTO_TCP)? 1 : 0; if (xprt->stream) { - xprt->cwnd = RPC_MAXCWND; + xprt->cwnd = RPC_MAXCWND(xprt); xprt->nocong = 1; } else xprt->cwnd = RPC_INITCWND; @@ -1453,12 +1449,15 @@ xprt_setup(int proto, struct sockaddr_in spin_lock_init(&xprt->xprt_lock); init_waitqueue_head(&xprt->cong_wait); + INIT_LIST_HEAD(&xprt->free); INIT_LIST_HEAD(&xprt->recv); + INIT_WORK(&xprt->sock_connect, xprt_socket_connect, xprt); INIT_WORK(&xprt->task_cleanup, xprt_socket_autoclose, xprt); init_timer(&xprt->timer); xprt->timer.function = xprt_init_autodisconnect; xprt->timer.data = (unsigned long) xprt; xprt->last_used = jiffies; + xprt->port = XPRT_MAX_RESVPORT; /* Set timeout parameters */ if (to) { @@ -1473,15 +1472,16 @@ xprt_setup(int proto, struct sockaddr_in INIT_RPC_WAITQ(&xprt->backlog, "xprt_backlog"); /* initialize free list */ - for (i = 0, req = xprt->slot; i < RPC_MAXREQS-1; i++, req++) - req->rq_next = req + 1; - req->rq_next = NULL; - xprt->free = xprt->slot; + for (req = &xprt->slot[entries-1]; req >= &xprt->slot[0]; req--) + list_add(&req->rq_list, &xprt->free); + + xprt_init_xid(xprt); /* Check whether we want to use a reserved port */ xprt->resvport = capable(CAP_NET_BIND_SERVICE) ? 1 : 0; - dprintk("RPC: created transport %p\n", xprt); + dprintk("RPC: created transport %p with %u slots\n", xprt, + xprt->max_reqs); return xprt; } @@ -1490,30 +1490,28 @@ xprt_setup(int proto, struct sockaddr_in * Bind to a reserved port */ static inline int -xprt_bindresvport(struct socket *sock) +xprt_bindresvport(struct rpc_xprt *xprt, struct socket *sock) { - struct sockaddr_in myaddr; + struct sockaddr_in myaddr = { + .sin_family = AF_INET, + }; int err, port; - kernel_cap_t saved_cap = current->cap_effective; - /* Override capabilities. - * They were checked in xprt_create_proto i.e. at mount time - */ - cap_raise(current->cap_effective, CAP_NET_BIND_SERVICE); - - memset(&myaddr, 0, sizeof(myaddr)); - myaddr.sin_family = AF_INET; - port = 800; + /* Were we already bound to a given port? Try to reuse it */ + port = xprt->port; do { myaddr.sin_port = htons(port); err = sock->ops->bind(sock, (struct sockaddr *) &myaddr, sizeof(myaddr)); - } while (err == -EADDRINUSE && --port > 0); - current->cap_effective = saved_cap; - - if (err < 0) - printk("RPC: Can't bind to reserved port (%d).\n", -err); + if (err == 0) { + xprt->port = port; + return 0; + } + if (--port == 0) + port = XPRT_MAX_RESVPORT; + } while (err == -EADDRINUSE && port != xprt->port); + printk("RPC: Can't bind to reserved port (%d).\n", -err); return err; } @@ -1563,11 +1561,11 @@ xprt_sock_setbufsize(struct rpc_xprt *xp return; if (xprt->rcvsize) { sk->sk_userlocks |= SOCK_RCVBUF_LOCK; - sk->sk_rcvbuf = xprt->rcvsize * RPC_MAXCONG * 2; + sk->sk_rcvbuf = xprt->rcvsize * xprt->max_reqs * 2; } if (xprt->sndsize) { sk->sk_userlocks |= SOCK_SNDBUF_LOCK; - sk->sk_sndbuf = xprt->sndsize * RPC_MAXCONG * 2; + sk->sk_sndbuf = xprt->sndsize * xprt->max_reqs * 2; sk->sk_write_space(sk); } } @@ -1577,7 +1575,7 @@ xprt_sock_setbufsize(struct rpc_xprt *xp * and connect stream sockets. */ static struct socket * -xprt_create_socket(int proto, struct rpc_timeout *to, int resvport) +xprt_create_socket(struct rpc_xprt *xprt, int proto, int resvport) { struct socket *sock; int type, err; @@ -1593,7 +1591,7 @@ xprt_create_socket(int proto, struct rpc } /* If the caller has the capability, bind to a reserved port */ - if (resvport && xprt_bindresvport(sock) < 0) { + if (resvport && xprt_bindresvport(xprt, sock) < 0) { printk("RPC: can't bind to reserved port.\n"); goto failed; } @@ -1614,16 +1612,11 @@ xprt_create_proto(int proto, struct sock struct rpc_xprt *xprt; xprt = xprt_setup(proto, sap, to); - if (!xprt) - goto out_bad; - - dprintk("RPC: xprt_create_proto created xprt %p\n", xprt); + if (IS_ERR(xprt)) + dprintk("RPC: xprt_create_proto failed\n"); + else + dprintk("RPC: xprt_create_proto created xprt %p\n", xprt); return xprt; - out_bad: - dprintk("RPC: xprt_create_proto failed\n"); - if (xprt) - kfree(xprt); - return NULL; } /* @@ -1662,6 +1655,7 @@ xprt_destroy(struct rpc_xprt *xprt) dprintk("RPC: destroying transport %p\n", xprt); xprt_shutdown(xprt); xprt_close(xprt); + kfree(xprt->slot); kfree(xprt); return 0; --- linux-2.6.4-rc2/scripts/modpost.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/scripts/modpost.c 2004-03-07 20:48:07.000000000 -0800 @@ -64,17 +64,20 @@ new_module(char *modname) { struct module *mod; char *p; + size_t len; mod = NOFAIL(malloc(sizeof(*mod))); memset(mod, 0, sizeof(*mod)); - mod->name = NOFAIL(strdup(modname)); + p = NOFAIL(strdup(modname)); + + len = strlen(p); /* strip trailing .o */ - p = strstr(mod->name, ".o"); - if (p) - *p = 0; + if (len > 2 && p[len-2] == '.' && p[len-1] == 'o') + p[len -2] = '\0'; /* add to list */ + mod->name = NOFAIL(strdup(p)); mod->next = modules; modules = mod; --- linux-2.6.4-rc2/security/dummy.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/security/dummy.c 2004-03-07 20:47:55.000000000 -0800 @@ -194,7 +194,8 @@ static void dummy_sb_free_security (stru return; } -static int dummy_sb_copy_data (const char *fstype, void *orig, void *copy) +static int dummy_sb_copy_data (struct file_system_type *type, + void *orig, void *copy) { return 0; } --- linux-2.6.4-rc2/security/selinux/hooks.c 2004-03-03 23:12:49.000000000 -0800 +++ 25/security/selinux/hooks.c 2004-03-07 20:47:55.000000000 -0800 @@ -331,25 +331,24 @@ static int try_context_mount(struct supe name = sb->s_type->name; - /* Ignore these fileystems with binary mount option data. */ - if (!strcmp(name, "coda") || - !strcmp(name, "afs") || !strcmp(name, "smbfs")) - goto out; + if (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA) { - /* NFS we understand. */ - if (!strcmp(name, "nfs")) { - struct nfs_mount_data *d = data; + /* NFS we understand. */ + if (!strcmp(name, "nfs")) { + struct nfs_mount_data *d = data; - if (d->version < NFS_MOUNT_VERSION) - goto out; + if (d->version < NFS_MOUNT_VERSION) + goto out; - if (d->context[0]) { - context = d->context; - seen |= Opt_context; - } + if (d->context[0]) { + context = d->context; + seen |= Opt_context; + } + } else + goto out; - /* Standard string-based options. */ } else { + /* Standard string-based options. */ char *p, *options = data; while ((p = strsep(&options, ",")) != NULL) { @@ -751,6 +750,7 @@ static int inode_doinit_with_dentry(stru inode->i_ino); goto out; } + BUG_ON(inode != dentry->d_inode); len = INITCONTEXTLEN; context = kmalloc(len, GFP_KERNEL); @@ -1885,7 +1885,7 @@ static inline void take_option(char **to *to += len; } -static int selinux_sb_copy_data(const char *fstype, void *orig, void *copy) +static int selinux_sb_copy_data(struct file_system_type *type, void *orig, void *copy) { int fnosec, fsec, rc = 0; char *in_save, *in_curr, *in_end; @@ -1895,8 +1895,7 @@ static int selinux_sb_copy_data(const ch sec_curr = copy; /* Binary mount data: just copy */ - if (!strcmp(fstype, "nfs") || !strcmp(fstype, "coda") || - !strcmp(fstype, "smbfs") || !strcmp(fstype, "afs")) { + if (type->fs_flags & FS_BINARY_MOUNTDATA) { copy_page(sec_curr, in_curr); goto out; } --- linux-2.6.4-rc2/sound/arm/Kconfig 2003-06-14 12:18:33.000000000 -0700 +++ 25/sound/arm/Kconfig 2004-03-07 20:46:50.000000000 -0800 @@ -6,6 +6,7 @@ menu "ALSA ARM devices" config SND_SA11XX_UDA1341 tristate "SA11xx UDA1341TS driver (H3600)" depends on ARCH_SA1100 && SND && L3 + select SND_PCM help Say Y or M if you have a Compaq iPaq H3x00 handheld computer and want to use its Philips UDA 1341 audio chip. --- linux-2.6.4-rc2/sound/arm/sa11xx-uda1341.c 2003-08-22 19:23:42.000000000 -0700 +++ 25/sound/arm/sa11xx-uda1341.c 2004-03-07 20:46:50.000000000 -0800 @@ -21,7 +21,7 @@ * merged HAL layer (patches from Brian) */ -/* $Id: sa11xx-uda1341.c,v 1.12 2003/08/13 13:14:31 tiwai Exp $ */ +/* $Id: sa11xx-uda1341.c,v 1.13 2004/03/02 15:32:35 perex Exp $ */ /*************************************************************************************************** * @@ -840,7 +840,9 @@ static int __init snd_card_sa11xx_uda134 * isa works but I'm not sure why (or if) it's the right choice * this may be too large, trying it for now */ - snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, 64*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_ISA, + snd_pcm_dma_flags(0), + 64*1024, 64*1024); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_card_sa11xx_uda1341_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_sa11xx_uda1341_capture_ops); --- linux-2.6.4-rc2/sound/core/init.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/core/init.c 2004-03-07 20:46:50.000000000 -0800 @@ -79,6 +79,7 @@ snd_card_t *snd_card_new(int idx, const goto __error; strlcpy(card->id, xid, sizeof(card->id)); } + err = 0; write_lock(&snd_card_rwlock); if (idx < 0) { int idx2; @@ -94,15 +95,14 @@ snd_card_t *snd_card_new(int idx, const idx = snd_ecards_limit++; } else if (idx < snd_ecards_limit) { if (snd_cards_lock & (1 << idx)) - idx = -1; /* invalid */ + err = -ENODEV; /* invalid */ } else if (idx < SNDRV_CARDS) snd_ecards_limit = idx + 1; /* increase the limit */ else - idx = -1; - if (idx < 0) { + err = -ENODEV; + if (idx < 0 || err < 0) { write_unlock(&snd_card_rwlock); - if (idx >= snd_ecards_limit) - snd_printk(KERN_ERR "card %i is out of range (0-%i)\n", idx, snd_ecards_limit-1); + snd_printk(KERN_ERR "cannot find the slot for index %d (range 0-%i)\n", idx, snd_ecards_limit - 1); goto __error; } snd_cards_lock |= 1 << idx; /* lock it */ --- linux-2.6.4-rc2/sound/core/Kconfig 2003-06-14 12:18:28.000000000 -0700 +++ 25/sound/core/Kconfig 2004-03-07 20:46:50.000000000 -0800 @@ -3,9 +3,23 @@ config SND_BIT32_EMUL tristate "Emulation for 32-bit applications" depends on SND && (SPARC64 || PPC64 || X86_64 && IA32_EMULATION) +config SND_TIMER + tristate + +config SND_PCM + tristate + select SND_TIMER + +config SND_HWDEP + tristate + +config SND_RAWMIDI + tristate + config SND_SEQUENCER tristate "Sequencer support" depends on SND + select SND_TIMER help Say 'Y' or 'M' to enable MIDI sequencer and router support. This feature allows routing and enqueing MIDI events. Events can be processed at given @@ -20,26 +34,27 @@ config SND_SEQ_DUMMY immediately. config SND_OSSEMUL - bool "OSS API emulation" - depends on SND - help - Say 'Y' to enable OSS (Open Sound System) API emulation code. + bool config SND_MIXER_OSS tristate "OSS Mixer API" - depends on SND_OSSEMUL && SND + depends on SND + select SND_OSSEMUL help Say 'Y' or 'M' to enable mixer OSS API emulation (/dev/mixer*). config SND_PCM_OSS tristate "OSS PCM (digital audio) API" - depends on SND_OSSEMUL && SND + depends on SND + select SND_OSSEMUL + select SND_PCM help Say 'Y' or 'M' to enable digital audio (PCM) OSS API emulation (/dev/dsp*). config SND_SEQUENCER_OSS bool "OSS Sequencer API" - depends on SND_OSSEMUL && SND_SEQUENCER + depends on SND_SEQUENCER + select SND_OSSEMUL help Say 'Y' to enable OSS sequencer emulation (both /dev/sequencer and /dev/music interfaces). @@ -47,6 +62,7 @@ config SND_SEQUENCER_OSS config SND_RTCTIMER tristate "RTC Timer support" depends on SND && RTC + select SND_TIMER help Say 'Y' or 'M' to enable RTC timer support for ALSA. ALSA code uses RTC timer as precise timing source and maps the RTC timer to the ALSA's timer --- linux-2.6.4-rc2/sound/core/Makefile 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/core/Makefile 2004-03-07 20:46:50.000000000 -0800 @@ -15,97 +15,20 @@ endif snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \ pcm_memory.o -snd-page-alloc-objs := memalloc.o -ifeq ($(CONFIG_PCI),y) -snd-page-alloc-objs += sgbuf.o -endif +snd-page-alloc-objs := memalloc.o sgbuf.o snd-rawmidi-objs := rawmidi.o snd-timer-objs := timer.o snd-rtctimer-objs := rtctimer.o snd-hwdep-objs := hwdep.o -obj-$(CONFIG_SND) += snd.o -ifeq ($(subst m,y,$(CONFIG_RTC)),y) - obj-$(CONFIG_SND_RTCTIMER) += snd-timer.o - obj-$(CONFIG_SND_RTCTIMER) += snd-rtctimer.o -endif -obj-$(CONFIG_SND_HWDEP) += snd-hwdep.o +obj-$(CONFIG_SND) += snd.o +obj-$(CONFIG_SND_HWDEP) += snd-hwdep.o +obj-$(CONFIG_SND_TIMER) += snd-timer.o +obj-$(CONFIG_SND_RTCTIMER) += snd-rtctimer.o +obj-$(CONFIG_SND_PCM) += snd-pcm.o snd-page-alloc.o +obj-$(CONFIG_SND_RAWMIDI) += snd-rawmidi.o -obj-$(CONFIG_SND_MIXER_OSS) += oss/ -obj-$(CONFIG_SND_PCM_OSS) += snd-pcm.o snd-timer.o snd-page-alloc.o oss/ -obj-$(CONFIG_SND_SEQUENCER) += snd-timer.o seq/ +obj-$(CONFIG_SND_OSSEMUL) += oss/ +obj-$(CONFIG_SND_SEQUENCER) += seq/ obj-$(CONFIG_SND_BIT32_EMUL) += ioctl32/ - -# Toplevel Module Dependency -obj-$(CONFIG_SND_DUMMY) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o -obj-$(CONFIG_SND_VIRMIDI) += snd-rawmidi.o snd.o snd-timer.o -obj-$(CONFIG_SND_SERIAL_U16550) += snd-rawmidi.o snd.o snd-timer.o -obj-$(CONFIG_SND_MTPAV) += snd-rawmidi.o snd.o snd-timer.o -obj-$(CONFIG_SND_MPU401) += snd-rawmidi.o snd.o snd-timer.o -obj-$(CONFIG_SND_ALS100) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_AZT2320) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_AZT3328) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_CMI8330) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o -obj-$(CONFIG_SND_DT019X) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_ES18XX) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_OPL3SA2) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_SGALAXY) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o -obj-$(CONFIG_SND_AD1816A) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_AD1848) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o -obj-$(CONFIG_SND_CS4231) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o -obj-$(CONFIG_SND_CS4232) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_CS4236) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_ES1688) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_GUSCLASSIC) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o -obj-$(CONFIG_SND_GUSMAX) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o -obj-$(CONFIG_SND_GUSEXTREME) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_INTERWAVE) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o -obj-$(CONFIG_SND_INTERWAVE_STB) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o -obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_OPTI93X) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_SB8) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_SB16) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_SBAWE) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_ES968) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o -obj-$(CONFIG_SND_WAVEFRONT) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_SSCAPE) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_ALS4000) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_CMIPCI) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_CS4281) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_ENS1370) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o -obj-$(CONFIG_SND_ENS1371) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o -obj-$(CONFIG_SND_ES1938) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-hwdep.o snd-rawmidi.o -obj-$(CONFIG_SND_ES1968) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o -obj-$(CONFIG_SND_FM801) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_ICE1712) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o -obj-$(CONFIG_SND_ICE1724) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o -obj-$(CONFIG_SND_INTEL8X0) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o -obj-$(CONFIG_SND_MAESTRO3) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o -obj-$(CONFIG_SND_RME32) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o -obj-$(CONFIG_SND_RME96) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o -obj-$(CONFIG_SND_SONICVIBES) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_VIA82XX) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o -obj-$(CONFIG_SND_ALI5451) += snd.o snd-rawmidi.o snd-timer.o snd-page-alloc.o snd-pcm.o -obj-$(CONFIG_SND_CS46XX) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o -obj-$(CONFIG_SND_EMU10K1) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_KORG1212) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o -obj-$(CONFIG_SND_NM256) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o -obj-$(CONFIG_SND_RME9652) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o -obj-$(CONFIG_SND_HDSP) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_TRIDENT) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o -obj-$(CONFIG_SND_YMFPCI) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_POWERMAC) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o -obj-$(CONFIG_SND_SA11XX_UDA1341) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o -obj-$(CONFIG_SND_PC98_CS4232) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_USB_AUDIO) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o -obj-$(CONFIG_SND_SUN_AMD7930) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o -obj-$(CONFIG_SND_SUN_CS4231) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o -obj-$(CONFIG_SND_HARMONY) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o -obj-$(CONFIG_SND_VXPOCKET) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-hwdep.o -obj-$(CONFIG_SND_VXP440) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-hwdep.o -obj-$(CONFIG_SND_VX222) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-hwdep.o -obj-$(CONFIG_SND_BT87X) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o - -obj-m := $(sort $(obj-m)) --- linux-2.6.4-rc2/sound/core/memalloc.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/core/memalloc.c 2004-03-07 20:46:50.000000000 -0800 @@ -25,10 +25,15 @@ #include #include #include +#include #include #include +#include #include #include +#ifdef CONFIG_SBUS +#include +#endif MODULE_AUTHOR("Takashi Iwai , Jaroslav Kysela "); @@ -43,6 +48,13 @@ static int enable[SNDRV_CARDS] = {[0 ... MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); MODULE_PARM_DESC(enable, "Enable cards to allocate buffers."); +/* + */ + +void *snd_malloc_sgbuf_pages(const struct snd_dma_device *dev, + size_t size, struct snd_dma_buffer *dmab, + size_t *res_size); +int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab); /* */ @@ -73,14 +85,40 @@ struct snd_mem_list { #define snd_assert(expr, args...) /**/ #endif -#ifdef CONFIG_PCI +/* + * Hacks + */ + +static void *snd_dma_alloc_coherent1(struct device *dev, size_t size, + dma_addr_t *dma_handle, int flags) +{ + if (dev) + return dma_alloc_coherent(dev, size, dma_handle, flags); + else /* FIXME: dma_alloc_coherent does't always accept dev=NULL */ + return pci_alloc_consistent(NULL, size, dma_handle); +} + +static void snd_dma_free_coherent1(struct device *dev, size_t size, void *dma_addr, + dma_addr_t dma_handle) +{ + if (dev) + return dma_free_coherent(dev, size, dma_addr, dma_handle); + else + return pci_free_consistent(NULL, size, dma_addr, dma_handle); +} + +#undef dma_alloc_coherent +#define dma_alloc_coherent snd_dma_alloc_coherent1 +#undef dma_free_coherent +#define dma_free_coherent snd_dma_free_coherent1 + + #if defined(__i386__) || defined(__ppc__) || defined(__x86_64__) -#define HACK_PCI_ALLOC_CONSISTENT /* - * A hack to allocate large buffers via pci_alloc_consistent() + * A hack to allocate large buffers via dma_alloc_coherent() * - * since pci_alloc_consistent always tries GFP_DMA when the requested + * since dma_alloc_coherent always tries GFP_DMA when the requested * pci memory region is below 32bit, it happens quite often that even * 2 order of pages cannot be allocated. * @@ -88,46 +126,248 @@ struct snd_mem_list { * allocation will be done without GFP_DMA. if the area doesn't match * with the requested region, then realloate with the original dma_mask * again. + * + * Really, we want to move this type of thing into dma_alloc_coherent() + * so dma_mask doesn't have to be messed with. */ -static void *snd_pci_hack_alloc_consistent(struct pci_dev *hwdev, size_t size, - dma_addr_t *dma_handle) +static void *snd_dma_hack_alloc_coherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, int flags) { void *ret; - u64 dma_mask, cdma_mask; - unsigned long mask; + u64 dma_mask; - if (hwdev == NULL) - return pci_alloc_consistent(hwdev, size, dma_handle); - dma_mask = hwdev->dma_mask; - cdma_mask = hwdev->consistent_dma_mask; - mask = (unsigned long)dma_mask && (unsigned long)cdma_mask; - hwdev->dma_mask = 0xffffffff; /* do without masking */ - hwdev->consistent_dma_mask = 0xffffffff; /* do without masking */ - ret = pci_alloc_consistent(hwdev, size, dma_handle); - hwdev->dma_mask = dma_mask; /* restore */ - hwdev->consistent_dma_mask = cdma_mask; /* restore */ + if (dev == NULL || !dev->dma_mask) + return dma_alloc_coherent(dev, size, dma_handle, flags); + dma_mask = *dev->dma_mask; + *dev->dma_mask = 0xffffffff; /* do without masking */ + ret = dma_alloc_coherent(dev, size, dma_handle, flags); + *dev->dma_mask = dma_mask; /* restore */ if (ret) { /* obtained address is out of range? */ - if (((unsigned long)*dma_handle + size - 1) & ~mask) { + if (((unsigned long)*dma_handle + size - 1) & ~dma_mask) { /* reallocate with the proper mask */ - pci_free_consistent(hwdev, size, ret, *dma_handle); - ret = pci_alloc_consistent(hwdev, size, dma_handle); + dma_free_coherent(dev, size, ret, *dma_handle); + ret = dma_alloc_coherent(dev, size, dma_handle, flags); } } else { /* wish to success now with the proper mask... */ - if (mask != 0xffffffffUL) - ret = pci_alloc_consistent(hwdev, size, dma_handle); + if (dma_mask != 0xffffffffUL) + ret = dma_alloc_coherent(dev, size, dma_handle, flags); } return ret; } -/* redefine pci_alloc_consistent for some architectures */ -#undef pci_alloc_consistent -#define pci_alloc_consistent snd_pci_hack_alloc_consistent +/* redefine dma_alloc_coherent for some architectures */ +#undef dma_alloc_coherent +#define dma_alloc_coherent snd_dma_hack_alloc_coherent #endif /* arch */ -#endif /* CONFIG_PCI */ + +/* + * + * Generic memory allocators + * + */ + +static long snd_allocated_pages; /* holding the number of allocated pages */ + +static void mark_pages(void *res, int order) +{ + struct page *page = virt_to_page(res); + struct page *last_page = page + (1 << order); + while (page < last_page) + SetPageReserved(page++); + snd_allocated_pages += 1 << order; +} + +static void unmark_pages(void *res, int order) +{ + struct page *page = virt_to_page(res); + struct page *last_page = page + (1 << order); + while (page < last_page) + ClearPageReserved(page++); + snd_allocated_pages -= 1 << order; +} + +/** + * snd_malloc_pages - allocate pages with the given size + * @size: the size to allocate in bytes + * @gfp_flags: the allocation conditions, GFP_XXX + * + * Allocates the physically contiguous pages with the given size. + * + * Returns the pointer of the buffer, or NULL if no enoguh memory. + */ +void *snd_malloc_pages(size_t size, unsigned int gfp_flags) +{ + int pg; + void *res; + + snd_assert(size > 0, return NULL); + snd_assert(gfp_flags != 0, return NULL); + for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); + if ((res = (void *) __get_free_pages(gfp_flags, pg)) != NULL) { + mark_pages(res, pg); + } + return res; +} + +/** + * snd_malloc_pages_fallback - allocate pages with the given size with fallback + * @size: the requested size to allocate in bytes + * @gfp_flags: the allocation conditions, GFP_XXX + * @res_size: the pointer to store the size of buffer actually allocated + * + * Allocates the physically contiguous pages with the given request + * size. When no space is left, this function reduces the size and + * tries to allocate again. The size actually allocated is stored in + * res_size argument. + * + * Returns the pointer of the buffer, or NULL if no enoguh memory. + */ +void *snd_malloc_pages_fallback(size_t size, unsigned int gfp_flags, size_t *res_size) +{ + void *res; + + snd_assert(size > 0, return NULL); + snd_assert(res_size != NULL, return NULL); + do { + if ((res = snd_malloc_pages(size, gfp_flags)) != NULL) { + *res_size = size; + return res; + } + size >>= 1; + } while (size >= PAGE_SIZE); + return NULL; +} + +/** + * snd_free_pages - release the pages + * @ptr: the buffer pointer to release + * @size: the allocated buffer size + * + * Releases the buffer allocated via snd_malloc_pages(). + */ +void snd_free_pages(void *ptr, size_t size) +{ + int pg; + + if (ptr == NULL) + return; + for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); + unmark_pages(ptr, pg); + free_pages((unsigned long) ptr, pg); +} + +/* + * + * Bus-specific memory allocators + * + */ + +static void *snd_malloc_dev_pages(struct device *dev, size_t size, dma_addr_t *dma) +{ + int pg; + void *res; + unsigned int gfp_flags; + + snd_assert(size > 0, return NULL); + snd_assert(dma != NULL, return NULL); + pg = get_order(size); + gfp_flags = GFP_KERNEL; + if (pg > 0) + gfp_flags |= __GFP_NOWARN; + res = dma_alloc_coherent(dev, PAGE_SIZE << pg, dma, gfp_flags); + if (res != NULL) + mark_pages(res, pg); + + return res; +} + +static void *snd_malloc_dev_pages_fallback(struct device *dev, size_t size, + dma_addr_t *dma, size_t *res_size) +{ + void *res; + + snd_assert(res_size != NULL, return NULL); + do { + if ((res = snd_malloc_dev_pages(dev, size, dma)) != NULL) { + *res_size = size; + return res; + } + size >>= 1; + } while (size >= PAGE_SIZE); + return NULL; +} + +static void snd_free_dev_pages(struct device *dev, size_t size, void *ptr, + dma_addr_t dma) +{ + int pg; + + if (ptr == NULL) + return; + pg = get_order(size); + unmark_pages(ptr, pg); + dma_free_coherent(dev, PAGE_SIZE << pg, ptr, dma); +} + +#ifdef CONFIG_SBUS + +static void *snd_malloc_sbus_pages(struct device *dev, size_t size, + dma_addr_t *dma_addr) +{ + struct sbus_dev *sdev = (struct sbus_dev *)dev; + int pg; + void *res; + + snd_assert(size > 0, return NULL); + snd_assert(dma_addr != NULL, return NULL); + for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); + res = sbus_alloc_consistent(sdev, PAGE_SIZE * (1 << pg), dma_addr); + if (res != NULL) { + mark_pages(res, pg); + } + return res; +} + +static void *snd_malloc_sbus_pages_fallback(struct device *dev, size_t size, + dma_addr_t *dma_addr, size_t *res_size) +{ + void *res; + + snd_assert(res_size != NULL, return NULL); + do { + if ((res = snd_malloc_sbus_pages(dev, size, dma_addr)) != NULL) { + *res_size = size; + return res; + } + size >>= 1; + } while (size >= PAGE_SIZE); + return NULL; +} + +static void snd_free_sbus_pages(struct device *dev, size_t size, + void *ptr, dma_addr_t dma_addr) +{ + struct sbus_dev *sdev = (struct sbus_dev *)dev; + int pg; + + if (ptr == NULL) + return; + for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); + unmark_pages(ptr, pg); + sbus_free_consistent(sdev, PAGE_SIZE * (1 << pg), ptr, dma_addr); +} + +#endif /* CONFIG_SBUS */ + +/* + * + * ALSA generic memory management + * + */ /* @@ -142,23 +382,7 @@ static int compare_device(const struct s if (! allow_unused || (a->id != SNDRV_DMA_DEVICE_UNUSED && b->id != SNDRV_DMA_DEVICE_UNUSED)) return 0; } - switch (a->type) { - case SNDRV_DMA_TYPE_CONTINUOUS: -#ifdef CONFIG_ISA - case SNDRV_DMA_TYPE_ISA: -#endif - return a->dev.flags == b->dev.flags; -#ifdef CONFIG_PCI - case SNDRV_DMA_TYPE_PCI: - case SNDRV_DMA_TYPE_PCI_SG: - return a->dev.pci == b->dev.pci; -#endif -#ifdef CONFIG_SBUS - case SNDRV_DMA_TYPE_SBUS: - return a->dev.sbus == b->dev.sbus; -#endif - } - return 0; + return a->dev == b->dev; } /** @@ -183,27 +407,70 @@ int snd_dma_alloc_pages(const struct snd dmab->bytes = 0; switch (dev->type) { case SNDRV_DMA_TYPE_CONTINUOUS: - dmab->area = snd_malloc_pages(size, dev->dev.flags); + dmab->area = snd_malloc_pages(size, (unsigned long)dev->dev); dmab->addr = 0; break; -#ifdef CONFIG_ISA - case SNDRV_DMA_TYPE_ISA: - dmab->area = snd_malloc_isa_pages(size, &dmab->addr); +#ifdef CONFIG_SBUS + case SNDRV_DMA_TYPE_SBUS: + dmab->area = snd_malloc_sbus_pages(dev->dev, size, &dmab->addr); break; #endif -#ifdef CONFIG_PCI - case SNDRV_DMA_TYPE_PCI: - dmab->area = snd_malloc_pci_pages(dev->dev.pci, size, &dmab->addr); + case SNDRV_DMA_TYPE_DEV: + dmab->area = snd_malloc_dev_pages(dev->dev, size, &dmab->addr); break; - case SNDRV_DMA_TYPE_PCI_SG: - snd_malloc_sgbuf_pages(dev->dev.pci, size, dmab); + case SNDRV_DMA_TYPE_DEV_SG: + snd_malloc_sgbuf_pages(dev, size, dmab, NULL); + break; + default: + printk(KERN_ERR "snd-malloc: invalid device type %d\n", dev->type); + dmab->area = NULL; + dmab->addr = 0; + return -ENXIO; + } + if (! dmab->area) + return -ENOMEM; + dmab->bytes = size; + return 0; +} + +/** + * snd_dma_alloc_pages_fallback - allocate the buffer area according to the given type with fallback + * @dev: the buffer device info + * @size: the buffer size to allocate + * @dmab: buffer allocation record to store the allocated data + * + * Calls the memory-allocator function for the corresponding + * buffer type. When no space is left, this function reduces the size and + * tries to allocate again. The size actually allocated is stored in + * res_size argument. + * + * Returns zero if the buffer with the given size is allocated successfuly, + * other a negative value at error. + */ +int snd_dma_alloc_pages_fallback(const struct snd_dma_device *dev, size_t size, + struct snd_dma_buffer *dmab) +{ + snd_assert(dev != NULL, return -ENXIO); + snd_assert(size > 0, return -ENXIO); + snd_assert(dmab != NULL, return -ENXIO); + + dmab->bytes = 0; + switch (dev->type) { + case SNDRV_DMA_TYPE_CONTINUOUS: + dmab->area = snd_malloc_pages_fallback(size, (unsigned long)dev->dev, &dmab->bytes); + dmab->addr = 0; break; -#endif #ifdef CONFIG_SBUS case SNDRV_DMA_TYPE_SBUS: - dmab->area = snd_malloc_sbus_pages(dev->dev.sbus, size, &dmab->addr); + dmab->area = snd_malloc_sbus_pages_fallback(dev->dev, size, &dmab->addr, &dmab->bytes); break; #endif + case SNDRV_DMA_TYPE_DEV: + dmab->area = snd_malloc_dev_pages_fallback(dev->dev, size, &dmab->addr, &dmab->bytes); + break; + case SNDRV_DMA_TYPE_DEV_SG: + snd_malloc_sgbuf_pages(dev, size, dmab, &dmab->bytes); + break; default: printk(KERN_ERR "snd-malloc: invalid device type %d\n", dev->type); dmab->area = NULL; @@ -212,7 +479,6 @@ int snd_dma_alloc_pages(const struct snd } if (! dmab->area) return -ENOMEM; - dmab->bytes = size; return 0; } @@ -230,24 +496,17 @@ void snd_dma_free_pages(const struct snd case SNDRV_DMA_TYPE_CONTINUOUS: snd_free_pages(dmab->area, dmab->bytes); break; -#ifdef CONFIG_ISA - case SNDRV_DMA_TYPE_ISA: - snd_free_isa_pages(dmab->bytes, dmab->area, dmab->addr); +#ifdef CONFIG_SBUS + case SNDRV_DMA_TYPE_SBUS: + snd_free_sbus_pages(dev->dev, dmab->bytes, dmab->area, dmab->addr); break; #endif -#ifdef CONFIG_PCI - case SNDRV_DMA_TYPE_PCI: - snd_free_pci_pages(dev->dev.pci, dmab->bytes, dmab->area, dmab->addr); + case SNDRV_DMA_TYPE_DEV: + snd_free_dev_pages(dev->dev, dmab->bytes, dmab->area, dmab->addr); break; - case SNDRV_DMA_TYPE_PCI_SG: + case SNDRV_DMA_TYPE_DEV_SG: snd_free_sgbuf_pages(dmab); break; -#endif -#ifdef CONFIG_SBUS - case SNDRV_DMA_TYPE_SBUS: - snd_free_sbus_pages(dev->dev.sbus, dmab->bytes, dmab->area, dmab->addr); - break; -#endif default: printk(KERN_ERR "snd-malloc: invalid device type %d\n", dev->type); } @@ -394,395 +653,7 @@ static void free_all_reserved_pages(void } up(&list_mutex); } - - -/* - * - * Generic memory allocators - * - */ - -static long snd_allocated_pages; /* holding the number of allocated pages */ - -static void mark_pages(void *res, int order) -{ - struct page *page = virt_to_page(res); - struct page *last_page = page + (1 << order); - while (page < last_page) - SetPageReserved(page++); - snd_allocated_pages += 1 << order; -} - -static void unmark_pages(void *res, int order) -{ - struct page *page = virt_to_page(res); - struct page *last_page = page + (1 << order); - while (page < last_page) - ClearPageReserved(page++); - snd_allocated_pages -= 1 << order; -} - -/** - * snd_malloc_pages - allocate pages with the given size - * @size: the size to allocate in bytes - * @gfp_flags: the allocation conditions, GFP_XXX - * - * Allocates the physically contiguous pages with the given size. - * - * Returns the pointer of the buffer, or NULL if no enoguh memory. - */ -void *snd_malloc_pages(size_t size, unsigned int gfp_flags) -{ - int pg; - void *res; - - snd_assert(size > 0, return NULL); - snd_assert(gfp_flags != 0, return NULL); - for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); - if ((res = (void *) __get_free_pages(gfp_flags, pg)) != NULL) { - mark_pages(res, pg); - } - return res; -} - -/** - * snd_malloc_pages_fallback - allocate pages with the given size with fallback - * @size: the requested size to allocate in bytes - * @gfp_flags: the allocation conditions, GFP_XXX - * @res_size: the pointer to store the size of buffer actually allocated - * - * Allocates the physically contiguous pages with the given request - * size. When no space is left, this function reduces the size and - * tries to allocate again. The size actually allocated is stored in - * res_size argument. - * - * Returns the pointer of the buffer, or NULL if no enoguh memory. - */ -void *snd_malloc_pages_fallback(size_t size, unsigned int gfp_flags, size_t *res_size) -{ - void *res; - - snd_assert(size > 0, return NULL); - snd_assert(res_size != NULL, return NULL); - do { - if ((res = snd_malloc_pages(size, gfp_flags)) != NULL) { - *res_size = size; - return res; - } - size >>= 1; - } while (size >= PAGE_SIZE); - return NULL; -} - -/** - * snd_free_pages - release the pages - * @ptr: the buffer pointer to release - * @size: the allocated buffer size - * - * Releases the buffer allocated via snd_malloc_pages(). - */ -void snd_free_pages(void *ptr, size_t size) -{ - int pg; - - if (ptr == NULL) - return; - for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); - unmark_pages(ptr, pg); - free_pages((unsigned long) ptr, pg); -} - -#if defined(CONFIG_ISA) && ! defined(CONFIG_PCI) - -/** - * snd_malloc_isa_pages - allocate pages for ISA bus with the given size - * @size: the size to allocate in bytes - * @dma_addr: the pointer to store the physical address of the buffer - * - * Allocates the physically contiguous pages with the given size for - * ISA bus. - * - * Returns the pointer of the buffer, or NULL if no enoguh memory. - */ -void *snd_malloc_isa_pages(size_t size, dma_addr_t *dma_addr) -{ - void *dma_area; - dma_area = snd_malloc_pages(size, GFP_ATOMIC|GFP_DMA); - *dma_addr = dma_area ? isa_virt_to_bus(dma_area) : 0UL; - return dma_area; -} - -/** - * snd_malloc_isa_pages_fallback - allocate pages with the given size with fallback for ISA bus - * @size: the requested size to allocate in bytes - * @dma_addr: the pointer to store the physical address of the buffer - * @res_size: the pointer to store the size of buffer actually allocated - * - * Allocates the physically contiguous pages with the given request - * size for PCI bus. When no space is left, this function reduces the size and - * tries to allocate again. The size actually allocated is stored in - * res_size argument. - * - * Returns the pointer of the buffer, or NULL if no enoguh memory. - */ -void *snd_malloc_isa_pages_fallback(size_t size, - dma_addr_t *dma_addr, - size_t *res_size) -{ - void *dma_area; - dma_area = snd_malloc_pages_fallback(size, GFP_ATOMIC|GFP_DMA, res_size); - *dma_addr = dma_area ? isa_virt_to_bus(dma_area) : 0UL; - return dma_area; -} - -#endif /* CONFIG_ISA && !CONFIG_PCI */ - -#ifdef CONFIG_PCI - -/** - * snd_malloc_pci_pages - allocate pages for PCI bus with the given size - * @pci: the pci device pointer - * @size: the size to allocate in bytes - * @dma_addr: the pointer to store the physical address of the buffer - * - * Allocates the physically contiguous pages with the given size for - * PCI bus. - * - * Returns the pointer of the buffer, or NULL if no enoguh memory. - */ -void *snd_malloc_pci_pages(struct pci_dev *pci, - size_t size, - dma_addr_t *dma_addr) -{ - int pg; - void *res; - - snd_assert(size > 0, return NULL); - snd_assert(dma_addr != NULL, return NULL); - for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); - res = pci_alloc_consistent(pci, PAGE_SIZE * (1 << pg), dma_addr); - if (res != NULL) { - mark_pages(res, pg); - } - return res; -} - -/** - * snd_malloc_pci_pages_fallback - allocate pages with the given size with fallback for PCI bus - * @pci: pci device pointer - * @size: the requested size to allocate in bytes - * @dma_addr: the pointer to store the physical address of the buffer - * @res_size: the pointer to store the size of buffer actually allocated - * - * Allocates the physically contiguous pages with the given request - * size for PCI bus. When no space is left, this function reduces the size and - * tries to allocate again. The size actually allocated is stored in - * res_size argument. - * - * Returns the pointer of the buffer, or NULL if no enoguh memory. - */ -void *snd_malloc_pci_pages_fallback(struct pci_dev *pci, - size_t size, - dma_addr_t *dma_addr, - size_t *res_size) -{ - void *res; - - snd_assert(res_size != NULL, return NULL); - do { - if ((res = snd_malloc_pci_pages(pci, size, dma_addr)) != NULL) { - *res_size = size; - return res; - } - size >>= 1; - } while (size >= PAGE_SIZE); - return NULL; -} - -/** - * snd_free_pci_pages - release the pages - * @pci: pci device pointer - * @size: the allocated buffer size - * @ptr: the buffer pointer to release - * @dma_addr: the physical address of the buffer - * - * Releases the buffer allocated via snd_malloc_pci_pages(). - */ -void snd_free_pci_pages(struct pci_dev *pci, - size_t size, - void *ptr, - dma_addr_t dma_addr) -{ - int pg; - - if (ptr == NULL) - return; - for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); - unmark_pages(ptr, pg); - pci_free_consistent(pci, PAGE_SIZE * (1 << pg), ptr, dma_addr); -} - - -#if defined(__i386__) -/* - * on ix86, we allocate a page with GFP_KERNEL to assure the - * allocation. the code is almost same with kernel/i386/pci-dma.c but - * it allocates only a single page and checks the validity of the - * page address with the given pci dma mask. - */ - -/** - * snd_malloc_pci_page - allocate a page in the valid pci dma mask - * @pci: pci device pointer - * @addrp: the pointer to store the physical address of the buffer - * - * Allocates a single page for the given PCI device and returns - * the virtual address and stores the physical address on addrp. - * - * This function cannot be called from interrupt handlers or - * within spinlocks. - */ -void *snd_malloc_pci_page(struct pci_dev *pci, dma_addr_t *addrp) -{ - void *ptr; - dma_addr_t addr; - unsigned long mask; - - mask = pci ? (unsigned long)pci->consistent_dma_mask : 0x00ffffffUL; - ptr = (void *)__get_free_page(GFP_KERNEL); - if (ptr) { - addr = virt_to_phys(ptr); - if (((unsigned long)addr + PAGE_SIZE - 1) & ~mask) { - /* try to reallocate with the GFP_DMA */ - free_page((unsigned long)ptr); - /* use GFP_ATOMIC for the DMA zone to avoid stall */ - ptr = (void *)__get_free_page(GFP_ATOMIC | GFP_DMA); - if (ptr) /* ok, the address must be within lower 16MB... */ - addr = virt_to_phys(ptr); - else - addr = 0; - } - } else - addr = 0; - if (ptr) { - memset(ptr, 0, PAGE_SIZE); - mark_pages(ptr, 0); - } - *addrp = addr; - return ptr; -} -#else - -/* on other architectures, call snd_malloc_pci_pages() helper function - * which uses pci_alloc_consistent(). - */ -void *snd_malloc_pci_page(struct pci_dev *pci, dma_addr_t *addrp) -{ - return snd_malloc_pci_pages(pci, PAGE_SIZE, addrp); -} - -#endif - -#if 0 /* for kernel-doc */ -/** - * snd_free_pci_page - release a page - * @pci: pci device pointer - * @ptr: the buffer pointer to release - * @dma_addr: the physical address of the buffer - * - * Releases the buffer allocated via snd_malloc_pci_page(). - */ -void snd_free_pci_page(struct pci_dev *pci, void *ptr, dma_addr_t dma_addr); -#endif /* for kernel-doc */ - -#endif /* CONFIG_PCI */ - -#ifdef CONFIG_SBUS - -/** - * snd_malloc_sbus_pages - allocate pages for SBUS with the given size - * @sdev: sbus device pointer - * @size: the size to allocate in bytes - * @dma_addr: the pointer to store the physical address of the buffer - * - * Allocates the physically contiguous pages with the given size for - * SBUS. - * - * Returns the pointer of the buffer, or NULL if no enoguh memory. - */ -void *snd_malloc_sbus_pages(struct sbus_dev *sdev, - size_t size, - dma_addr_t *dma_addr) -{ - int pg; - void *res; - - snd_assert(size > 0, return NULL); - snd_assert(dma_addr != NULL, return NULL); - for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); - res = sbus_alloc_consistent(sdev, PAGE_SIZE * (1 << pg), dma_addr); - if (res != NULL) { - mark_pages(res, pg); - } - return res; -} - -/** - * snd_malloc_pci_pages_fallback - allocate pages with the given size with fallback for SBUS - * @sdev: sbus device pointer - * @size: the requested size to allocate in bytes - * @dma_addr: the pointer to store the physical address of the buffer - * @res_size: the pointer to store the size of buffer actually allocated - * - * Allocates the physically contiguous pages with the given request - * size for SBUS. When no space is left, this function reduces the size and - * tries to allocate again. The size actually allocated is stored in - * res_size argument. - * - * Returns the pointer of the buffer, or NULL if no enoguh memory. - */ -void *snd_malloc_sbus_pages_fallback(struct sbus_dev *sdev, - size_t size, - dma_addr_t *dma_addr, - size_t *res_size) -{ - void *res; - - snd_assert(res_size != NULL, return NULL); - do { - if ((res = snd_malloc_sbus_pages(sdev, size, dma_addr)) != NULL) { - *res_size = size; - return res; - } - size >>= 1; - } while (size >= PAGE_SIZE); - return NULL; -} - -/** - * snd_free_sbus_pages - release the pages - * @sdev: sbus device pointer - * @size: the allocated buffer size - * @ptr: the buffer pointer to release - * @dma_addr: the physical address of the buffer - * - * Releases the buffer allocated via snd_malloc_pci_pages(). - */ -void snd_free_sbus_pages(struct sbus_dev *sdev, - size_t size, - void *ptr, - dma_addr_t dma_addr) -{ - int pg; - - if (ptr == NULL) - return; - for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); - unmark_pages(ptr, pg); - sbus_free_consistent(sdev, PAGE_SIZE * (1 << pg), ptr, dma_addr); -} - -#endif /* CONFIG_SBUS */ + /* @@ -821,6 +692,17 @@ static struct prealloc_dev prealloc_devi { }, /* terminator */ }; +/* + * compose a snd_dma_device struct for the PCI device + */ +static inline void snd_dma_device_pci(struct snd_dma_device *dev, struct pci_dev *pci, unsigned int id) +{ + memset(dev, 0, sizeof(*dev)); + dev->type = SNDRV_DMA_TYPE_DEV; + dev->dev = snd_dma_pci_data(pci); + dev->id = id; +} + static void __init preallocate_cards(void) { struct pci_dev *pci = NULL; @@ -902,29 +784,25 @@ static int snd_mem_proc_read(char *page, len += sprintf(page + len, " : type "); switch (mem->dev.type) { case SNDRV_DMA_TYPE_CONTINUOUS: - len += sprintf(page + len, "CONT [%x]", mem->dev.dev.flags); + len += sprintf(page + len, "CONT [%p]", mem->dev.dev); break; -#ifdef CONFIG_PCI - case SNDRV_DMA_TYPE_PCI: - case SNDRV_DMA_TYPE_PCI_SG: - if (mem->dev.dev.pci) { - len += sprintf(page + len, "%s [%04x:%04x]", - mem->dev.type == SNDRV_DMA_TYPE_PCI ? "PCI" : "PCI-SG", - mem->dev.dev.pci->vendor, - mem->dev.dev.pci->device); - } - break; -#endif -#ifdef CONFIG_ISA - case SNDRV_DMA_TYPE_ISA: - len += sprintf(page + len, "ISA [%x]", mem->dev.dev.flags); - break; -#endif #ifdef CONFIG_SBUS case SNDRV_DMA_TYPE_SBUS: - len += sprintf(page + len, "SBUS [%x]", mem->dev.dev.sbus->slot); + { + struct sbus_dev *sdev = (struct sbus_dev *)(mem->dev.dev); + len += sprintf(page + len, "SBUS [%x]", sbus->slot); + } break; #endif + case SNDRV_DMA_TYPE_DEV: + case SNDRV_DMA_TYPE_DEV_SG: + if (mem->dev.dev) { + len += sprintf(page + len, "%s [%s]", + mem->dev.type == SNDRV_DMA_TYPE_DEV_SG ? "DEV-SG" : "DEV", + mem->dev.dev->bus_id); + } else + len += sprintf(page + len, "ISA"); + break; default: len += sprintf(page + len, "UNKNOWN"); break; @@ -987,7 +865,9 @@ __setup("snd-page-alloc=", snd_mem_setup * exports */ EXPORT_SYMBOL(snd_dma_alloc_pages); +EXPORT_SYMBOL(snd_dma_alloc_pages_fallback); EXPORT_SYMBOL(snd_dma_free_pages); + EXPORT_SYMBOL(snd_dma_get_reserved); EXPORT_SYMBOL(snd_dma_free_reserved); EXPORT_SYMBOL(snd_dma_set_reserved); @@ -995,20 +875,3 @@ EXPORT_SYMBOL(snd_dma_set_reserved); EXPORT_SYMBOL(snd_malloc_pages); EXPORT_SYMBOL(snd_malloc_pages_fallback); EXPORT_SYMBOL(snd_free_pages); -#if defined(CONFIG_ISA) && ! defined(CONFIG_PCI) -EXPORT_SYMBOL(snd_malloc_isa_pages); -EXPORT_SYMBOL(snd_malloc_isa_pages_fallback); -#endif -#ifdef CONFIG_PCI -EXPORT_SYMBOL(snd_malloc_pci_pages); -EXPORT_SYMBOL(snd_malloc_pci_pages_fallback); -EXPORT_SYMBOL(snd_malloc_pci_page); -EXPORT_SYMBOL(snd_free_pci_pages); -EXPORT_SYMBOL(snd_malloc_sgbuf_pages); -EXPORT_SYMBOL(snd_free_sgbuf_pages); -#endif -#ifdef CONFIG_SBUS -EXPORT_SYMBOL(snd_malloc_sbus_pages); -EXPORT_SYMBOL(snd_malloc_sbus_pages_fallback); -EXPORT_SYMBOL(snd_free_sbus_pages); -#endif --- linux-2.6.4-rc2/sound/core/oss/pcm_oss.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/core/oss/pcm_oss.c 2004-03-07 20:46:50.000000000 -0800 @@ -133,6 +133,15 @@ static long snd_pcm_oss_bytes(snd_pcm_su return (runtime->oss.buffer_bytes * frames) / buffer_size; } +static long snd_pcm_alsa_frames(snd_pcm_substream_t *substream, long bytes) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_uframes_t buffer_size = snd_pcm_lib_buffer_bytes(substream); + if (buffer_size == runtime->oss.buffer_bytes) + return bytes_to_frames(runtime, bytes); + return bytes_to_frames(runtime, (buffer_size * bytes) / runtime->oss.buffer_bytes); +} + static int snd_pcm_oss_format_from(int format) { switch (format) { @@ -254,6 +263,7 @@ static int snd_pcm_oss_period_size(snd_p snd_assert(oss_period_size >= 16, return -EINVAL); runtime->oss.period_bytes = oss_period_size; + runtime->oss.period_frames = 1; runtime->oss.periods = oss_periods; return 0; } @@ -511,6 +521,8 @@ static int snd_pcm_oss_change_params(snd if (runtime->dma_area) snd_pcm_format_set_silence(runtime->format, runtime->dma_area, bytes_to_samples(runtime, runtime->dma_bytes)); + runtime->oss.period_frames = snd_pcm_alsa_frames(substream, oss_period_size); + err = 0; failure: if (sw_params) @@ -2098,7 +2110,7 @@ static int snd_pcm_oss_playback_ready(sn if (atomic_read(&runtime->mmap_count)) return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt; else - return snd_pcm_playback_ready(substream); + return snd_pcm_playback_avail(runtime) >= runtime->oss.period_frames; } static int snd_pcm_oss_capture_ready(snd_pcm_substream_t *substream) @@ -2107,7 +2119,7 @@ static int snd_pcm_oss_capture_ready(snd if (atomic_read(&runtime->mmap_count)) return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt; else - return snd_pcm_capture_ready(substream); + return snd_pcm_capture_avail(runtime) >= runtime->oss.period_frames; } static unsigned int snd_pcm_oss_poll(struct file *file, poll_table * wait) --- linux-2.6.4-rc2/sound/core/pcm.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/core/pcm.c 2004-03-07 20:46:50.000000000 -0800 @@ -327,8 +327,9 @@ static void snd_pcm_substream_proc_hw_pa snd_iprintf(buffer, "OSS format: %s\n", snd_pcm_oss_format_name(runtime->oss.format)); snd_iprintf(buffer, "OSS channels: %u\n", runtime->oss.channels); snd_iprintf(buffer, "OSS rate: %u\n", runtime->oss.rate); - snd_iprintf(buffer, "OSS period bytes: %lu\n", (unsigned long)runtime->oss.period_bytes); + snd_iprintf(buffer, "OSS period bytes: %lu\n", (unsigned long)runtime->oss.period_bytes); snd_iprintf(buffer, "OSS periods: %u\n", runtime->oss.periods); + snd_iprintf(buffer, "OSS period frames: %lu\n", (unsigned long)runtime->oss.period_frames); } #endif snd_pcm_stream_unlock_irq(substream); @@ -1060,3 +1061,4 @@ EXPORT_SYMBOL(snd_pcm_format_size); EXPORT_SYMBOL(snd_pcm_format_silence_64); EXPORT_SYMBOL(snd_pcm_format_set_silence); EXPORT_SYMBOL(snd_pcm_build_linear_format); +EXPORT_SYMBOL(snd_pcm_limit_hw_rates); --- linux-2.6.4-rc2/sound/core/pcm_lib.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/core/pcm_lib.c 2004-03-07 20:46:50.000000000 -0800 @@ -67,8 +67,11 @@ void snd_pcm_playback_silence(snd_pcm_su frames = runtime->silence_size; } else { if (new_hw_ptr == ULONG_MAX) { /* initialization */ - runtime->silence_filled = 0; - runtime->silence_start = runtime->control->appl_ptr; + snd_pcm_sframes_t avail = snd_pcm_playback_hw_avail(runtime); + runtime->silence_filled = avail > 0 ? avail : 0; + runtime->silence_start = (runtime->status->hw_ptr + + runtime->silence_filled) % + runtime->boundary; } else { ofs = runtime->status->hw_ptr; frames = new_hw_ptr - ofs; @@ -2659,20 +2662,6 @@ EXPORT_SYMBOL(snd_pcm_lib_preallocate_fr EXPORT_SYMBOL(snd_pcm_lib_preallocate_free_for_all); EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages); EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages_for_all); +EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page); EXPORT_SYMBOL(snd_pcm_lib_malloc_pages); EXPORT_SYMBOL(snd_pcm_lib_free_pages); -#ifdef CONFIG_ISA -EXPORT_SYMBOL(snd_pcm_lib_preallocate_isa_pages); -EXPORT_SYMBOL(snd_pcm_lib_preallocate_isa_pages_for_all); -#endif -#ifdef CONFIG_PCI -EXPORT_SYMBOL(snd_pcm_lib_preallocate_pci_pages); -EXPORT_SYMBOL(snd_pcm_lib_preallocate_pci_pages_for_all); -EXPORT_SYMBOL(snd_pcm_lib_preallocate_sg_pages); -EXPORT_SYMBOL(snd_pcm_lib_preallocate_sg_pages_for_all); -EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page); -#endif -#ifdef CONFIG_SBUS -EXPORT_SYMBOL(snd_pcm_lib_preallocate_sbus_pages); -EXPORT_SYMBOL(snd_pcm_lib_preallocate_sbus_pages_for_all); -#endif --- linux-2.6.4-rc2/sound/core/pcm_memory.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/core/pcm_memory.c 2004-03-07 20:46:50.000000000 -0800 @@ -229,97 +229,71 @@ static inline void setup_pcm_id(snd_pcm_ } /** - * snd_pcm_lib_preallocate_pages - pre-allocation for the continuous memory type + * snd_pcm_lib_preallocate_pages - pre-allocation for the given DMA type * @substream: the pcm substream instance + * @type: DMA type (SNDRV_DMA_TYPE_*) + * @data: DMA type dependant data * @size: the requested pre-allocation size in bytes * @max: the max. allowed pre-allocation size - * @flags: allocation condition, GFP_XXX * - * Do pre-allocation for the continuous memory type. + * Do pre-allocation for the given DMA type. * * Returns zero if successful, or a negative error code on failure. */ int snd_pcm_lib_preallocate_pages(snd_pcm_substream_t *substream, - size_t size, size_t max, - unsigned int flags) + int type, struct device *data, + size_t size, size_t max) { - substream->dma_device.type = SNDRV_DMA_TYPE_CONTINUOUS; - substream->dma_device.dev.flags = flags; + substream->dma_device.type = type; + substream->dma_device.dev = data; setup_pcm_id(substream); return snd_pcm_lib_preallocate_pages1(substream, size, max); } /** * snd_pcm_lib_preallocate_pages_for_all - pre-allocation for continous memory type (all substreams) - * @pcm: pcm to assign the buffer + * @substream: the pcm substream instance + * @type: DMA type (SNDRV_DMA_TYPE_*) + * @data: DMA type dependant data * @size: the requested pre-allocation size in bytes - * @max: max. buffer size acceptable for the changes via proc file - * @flags: allocation condition, GFP_XXX + * @max: the max. allowed pre-allocation size * * Do pre-allocation to all substreams of the given pcm for the - * continuous memory type. + * specified DMA type. * * Returns zero if successful, or a negative error code on failure. */ int snd_pcm_lib_preallocate_pages_for_all(snd_pcm_t *pcm, - size_t size, size_t max, - unsigned int flags) + int type, void *data, + size_t size, size_t max) { snd_pcm_substream_t *substream; int stream, err; for (stream = 0; stream < 2; stream++) for (substream = pcm->streams[stream].substream; substream; substream = substream->next) - if ((err = snd_pcm_lib_preallocate_pages(substream, size, max, flags)) < 0) + if ((err = snd_pcm_lib_preallocate_pages(substream, type, data, size, max)) < 0) return err; return 0; } -#ifdef CONFIG_ISA /** - * snd_pcm_lib_preallocate_isa_pages - pre-allocation for the ISA bus - * @substream: substream to assign the buffer - * @size: the requested pre-allocation size in bytes - * @max: max. buffer size acceptable for the changes via proc file - * - * Do pre-allocation for the ISA bus. - * - * Returns zero if successful, or a negative error code on failure. - */ -int snd_pcm_lib_preallocate_isa_pages(snd_pcm_substream_t *substream, - size_t size, size_t max) -{ - substream->dma_device.type = SNDRV_DMA_TYPE_ISA; - substream->dma_device.dev.flags = 0; /* FIXME: any good identifier? */ - setup_pcm_id(substream); - return snd_pcm_lib_preallocate_pages1(substream, size, max); -} - -/* - * FIXME: the function name is too long for docbook! - * - * snd_pcm_lib_preallocate_isa_pages_for_all - pre-allocation for the ISA bus (all substreams) - * @pcm: pcm to assign the buffer - * @max: max. buffer size acceptable for the changes via proc file - * - * Do pre-allocation to all substreams of the given pcm for the - * ISA bus. + * snd_pcm_sgbuf_ops_page - get the page struct at the given offset + * @substream: the pcm substream instance + * @offset: the buffer offset * - * Returns zero if successful, or a negative error code on failure. + * Returns the page struct at the given buffer offset. + * Used as the page callback of PCM ops. */ -int snd_pcm_lib_preallocate_isa_pages_for_all(snd_pcm_t *pcm, - size_t size, size_t max) +struct page *snd_pcm_sgbuf_ops_page(snd_pcm_substream_t *substream, unsigned long offset) { - snd_pcm_substream_t *substream; - int stream, err; + struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream); - for (stream = 0; stream < 2; stream++) - for (substream = pcm->streams[stream].substream; substream; substream = substream->next) - if ((err = snd_pcm_lib_preallocate_isa_pages(substream, size, max)) < 0) - return err; - return 0; + unsigned int idx = offset >> PAGE_SHIFT; + if (idx >= (unsigned int)sgbuf->pages) + return NULL; + return sgbuf->page_table[idx]; } -#endif /* CONFIG_ISA */ /** * snd_pcm_lib_malloc_pages - allocate the DMA buffer @@ -398,183 +372,6 @@ int snd_pcm_lib_free_pages(snd_pcm_subst return 0; } -#ifdef CONFIG_PCI -/** - * snd_pcm_lib_preallocate_pci_pages - pre-allocation for the PCI bus - * - * @pci: pci device - * @substream: substream to assign the buffer - * @size: the requested pre-allocation size in bytes - * @max: max. buffer size acceptable for the changes via proc file - * - * Do pre-allocation for the PCI bus. - * - * Returns zero if successful, or a negative error code on failure. - */ -int snd_pcm_lib_preallocate_pci_pages(struct pci_dev *pci, - snd_pcm_substream_t *substream, - size_t size, size_t max) -{ - substream->dma_device.type = SNDRV_DMA_TYPE_PCI; - substream->dma_device.dev.pci = pci; - setup_pcm_id(substream); - return snd_pcm_lib_preallocate_pages1(substream, size, max); -} - -/* - * FIXME: the function name is too long for docbook! - * - * snd_pcm_lib_preallocate_pci_pages_for_all - pre-allocation for the PCI bus (all substreams) - * @pci: pci device - * @pcm: pcm to assign the buffer - * @size: the requested pre-allocation size in bytes - * @max: max. buffer size acceptable for the changes via proc file - * - * Do pre-allocation to all substreams of the given pcm for the - * PCI bus. - * - * Returns zero if successful, or a negative error code on failure. - */ -int snd_pcm_lib_preallocate_pci_pages_for_all(struct pci_dev *pci, - snd_pcm_t *pcm, - size_t size, size_t max) -{ - snd_pcm_substream_t *substream; - int stream, err; - - for (stream = 0; stream < 2; stream++) - for (substream = pcm->streams[stream].substream; substream; substream = substream->next) - if ((err = snd_pcm_lib_preallocate_pci_pages(pci, substream, size, max)) < 0) - return err; - return 0; -} - -#endif /* CONFIG_PCI */ - -#ifdef CONFIG_SBUS -/** - * snd_pcm_lib_preallocate_sbus_pages - pre-allocation for the SBUS bus - * - * @sbus: SBUS device - * @substream: substream to assign the buffer - * @max: max. buffer size acceptable for the changes via proc file - * - * Do pre-allocation for the SBUS. - * - * Returns zero if successful, or a negative error code on failure. - */ -int snd_pcm_lib_preallocate_sbus_pages(struct sbus_dev *sdev, - snd_pcm_substream_t *substream, - size_t size, size_t max) -{ - substream->dma_device.type = SNDRV_DMA_TYPE_SBUS; - substream->dma_device.dev.sbus = sdev; - setup_pcm_id(substream); - return snd_pcm_lib_preallocate_pages1(substream, size, max); -} - -/* - * FIXME: the function name is too long for docbook! - * - * snd_pcm_lib_preallocate_sbus_pages_for_all - pre-allocation for the SBUS bus (all substreams) - * @sbus: SBUS device - * @pcm: pcm to assign the buffer - * @size: the requested pre-allocation size in bytes - * @max: max. buffer size acceptable for the changes via proc file - * - * Do pre-allocation to all substreams of the given pcm for the - * SUBS. - * - * Returns zero if successful, or a negative error code on failure. - */ -int snd_pcm_lib_preallocate_sbus_pages_for_all(struct sbus_dev *sdev, - snd_pcm_t *pcm, - size_t size, size_t max) -{ - snd_pcm_substream_t *substream; - int stream, err; - - for (stream = 0; stream < 2; stream++) - for (substream = pcm->streams[stream].substream; substream; substream = substream->next) - if ((err = snd_pcm_lib_preallocate_sbus_pages(sdev, substream, size, max)) < 0) - return err; - return 0; -} - -#endif /* CONFIG_SBUS */ - - -#ifdef CONFIG_PCI -/** - * snd_pcm_lib_preallocate_sg_pages - initialize SG-buffer for the PCI bus - * - * @pci: pci device - * @substream: substream to assign the buffer - * @size: the requested pre-allocation size in bytes - * @max: max. buffer size acceptable for the changes via proc file - * - * Initializes SG-buffer for the PCI bus. - * - * Returns zero if successful, or a negative error code on failure. - */ -int snd_pcm_lib_preallocate_sg_pages(struct pci_dev *pci, - snd_pcm_substream_t *substream, - size_t size, size_t max) -{ - substream->dma_device.type = SNDRV_DMA_TYPE_PCI_SG; - substream->dma_device.dev.pci = pci; - setup_pcm_id(substream); - return snd_pcm_lib_preallocate_pages1(substream, size, max); -} - -/* - * FIXME: the function name is too long for docbook! - * - * snd_pcm_lib_preallocate_sg_pages_for_all - initialize SG-buffer for the PCI bus (all substreams) - * @pci: pci device - * @pcm: pcm to assign the buffer - * @size: the requested pre-allocation size in bytes - * @max: max. buffer size acceptable for the changes via proc file - * - * Initialize the SG-buffer to all substreams of the given pcm for the - * PCI bus. - * - * Returns zero if successful, or a negative error code on failure. - */ -int snd_pcm_lib_preallocate_sg_pages_for_all(struct pci_dev *pci, - snd_pcm_t *pcm, - size_t size, size_t max) -{ - snd_pcm_substream_t *substream; - int stream, err; - - for (stream = 0; stream < 2; stream++) - for (substream = pcm->streams[stream].substream; substream; substream = substream->next) - if ((err = snd_pcm_lib_preallocate_sg_pages(pci, substream, size, max)) < 0) - return err; - return 0; -} - -/** - * snd_pcm_sgbuf_ops_page - get the page struct at the given offset - * @substream: the pcm substream instance - * @offset: the buffer offset - * - * Returns the page struct at the given buffer offset. - * Used as the page callback of PCM ops. - */ -struct page *snd_pcm_sgbuf_ops_page(snd_pcm_substream_t *substream, unsigned long offset) -{ - struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream); - - unsigned int idx = offset >> PAGE_SHIFT; - if (idx >= (unsigned int)sgbuf->pages) - return NULL; - return sgbuf->page_table[idx]; -} - -#endif /* CONFIG_PCI */ - #ifndef MODULE /* format is: snd-pcm=preallocate_dma,maximum_substreams */ --- linux-2.6.4-rc2/sound/core/pcm_misc.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/core/pcm_misc.c 2004-03-07 20:46:50.000000000 -0800 @@ -654,3 +654,35 @@ snd_pcm_format_t snd_pcm_build_linear_fo } return snd_int_to_enum(((int(*)[2][2])linear_formats)[width][!!unsignd][!!big_endian]); } + +/** + * snd_pcm_limit_hw_rates - determine rate_min/rate_max fields + * @runtime: the runtime instance + * + * Determines the rate_min and rate_max fields from the rates bits of + * the given runtime->hw. + * + * Returns zero if successful. + */ +int snd_pcm_limit_hw_rates(snd_pcm_runtime_t *runtime) +{ + static unsigned rates[] = { + /* ATTENTION: these values depend on the definition in pcm.h! */ + 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, + 64000, 88200, 96000, 176400, 192000 + }; + int i; + for (i = 0; i < (int)ARRAY_SIZE(rates); i++) { + if (runtime->hw.rates & (1 << i)) { + runtime->hw.rate_min = rates[i]; + break; + } + } + for (i = (int)ARRAY_SIZE(rates) - 1; i >= 0; i--) { + if (runtime->hw.rates & (1 << i)) { + runtime->hw.rate_max = rates[i]; + break; + } + } + return 0; +} --- linux-2.6.4-rc2/sound/core/seq/oss/seq_oss_midi.c 2003-06-14 12:17:56.000000000 -0700 +++ 25/sound/core/seq/oss/seq_oss_midi.c 2004-03-07 20:46:50.000000000 -0800 @@ -491,7 +491,7 @@ snd_seq_oss_midi_reset(seq_oss_devinfo_t } } } - snd_seq_oss_midi_close(dp, dev); + // snd_seq_oss_midi_close(dp, dev); snd_use_lock_free(&mdev->use_lock); } @@ -578,6 +578,7 @@ send_synth_event(seq_oss_devinfo_t *dp, ossev.v.code = EV_CHN_VOICE; ossev.v.note = ev->data.note.note; ossev.v.parm = ev->data.note.velocity; + ossev.v.chn = ev->data.note.channel; break; case SNDRV_SEQ_EVENT_CONTROLLER: case SNDRV_SEQ_EVENT_PGMCHANGE: @@ -585,10 +586,12 @@ send_synth_event(seq_oss_devinfo_t *dp, ossev.l.code = EV_CHN_COMMON; ossev.l.p1 = ev->data.control.param; ossev.l.val = ev->data.control.value; + ossev.l.chn = ev->data.control.channel; break; case SNDRV_SEQ_EVENT_PITCHBEND: ossev.l.code = EV_CHN_COMMON; ossev.l.val = ev->data.control.value + 8192; + ossev.l.chn = ev->data.control.channel; break; } --- linux-2.6.4-rc2/sound/core/seq/oss/seq_oss_synth.c 2003-06-14 12:17:56.000000000 -0700 +++ 25/sound/core/seq/oss/seq_oss_synth.c 2004-03-07 20:46:50.000000000 -0800 @@ -410,6 +410,8 @@ snd_seq_oss_synth_reset(seq_oss_devinfo_ if (midi_synth_dev.opened <= 0) return; snd_seq_oss_midi_reset(dp, info->midi_mapped); + /* reopen the device */ + snd_seq_oss_midi_close(dp, dev); if (snd_seq_oss_midi_open(dp, info->midi_mapped, dp->file_mode) < 0) { midi_synth_dev.opened--; --- linux-2.6.4-rc2/sound/core/seq/seq_clientmgr.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/core/seq/seq_clientmgr.c 2004-03-07 20:46:50.000000000 -0800 @@ -547,6 +547,36 @@ static int update_timestamp_of_queue(snd /* + * expand a quoted event. + */ +static int expand_quoted_event(snd_seq_event_t *event) +{ + snd_seq_event_t *quoted; + + quoted = event->data.quote.event; + if (quoted == NULL) { + snd_printd("seq: quoted event is NULL\n"); + return -EINVAL; + } + + event->type = quoted->type; + event->tag = quoted->tag; + event->source = quoted->source; + /* don't use quoted destination */ + event->data = quoted->data; + /* use quoted timestamp only if subscription/port didn't update it */ + if (event->queue == SNDRV_SEQ_QUEUE_DIRECT) { + event->flags = quoted->flags; + event->queue = quoted->queue; + event->time = quoted->time; + } else { + event->flags = (event->flags & SNDRV_SEQ_TIME_STAMP_MASK) + | (quoted->flags & ~SNDRV_SEQ_TIME_STAMP_MASK); + } + return 0; +} + +/* * deliver an event to the specified destination. * if filter is non-zero, client filter bitmap is tested. * @@ -581,12 +611,9 @@ static int snd_seq_deliver_single_event( update_timestamp_of_queue(event, dest_port->time_queue, dest_port->time_real); - /* expand the quoted event */ if (event->type == SNDRV_SEQ_EVENT_KERNEL_QUOTE) { quoted = 1; - event = event->data.quote.event; - if (event == NULL) { - snd_printd("seq: quoted event is NULL\n"); + if (expand_quoted_event(event) < 0) { result = 0; /* do not send bounce error */ goto __skip; } @@ -694,8 +721,8 @@ static int port_broadcast_event(client_t if (dest_client == NULL) return 0; /* no matching destination */ - read_lock(&client->ports_lock); - list_for_each(p, &client->ports_list_head) { + read_lock(&dest_client->ports_lock); + list_for_each(p, &dest_client->ports_list_head) { client_port_t *port = list_entry(p, client_port_t, list); event->dest.port = port->addr.port; /* pass NULL as source client to avoid error bounce */ @@ -706,7 +733,7 @@ static int port_broadcast_event(client_t break; num_ev++; } - read_unlock(&client->ports_lock); + read_unlock(&dest_client->ports_lock); snd_seq_client_unlock(dest_client); event->dest.port = SNDRV_SEQ_ADDRESS_BROADCAST; /* restore */ return (err < 0) ? err : num_ev; --- linux-2.6.4-rc2/sound/core/seq/seq_fifo.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/core/seq/seq_fifo.c 2004-03-07 20:46:50.000000000 -0800 @@ -171,10 +171,12 @@ int snd_seq_fifo_cell_out(fifo_t *f, snd { snd_seq_event_cell_t *cell; unsigned long flags; + wait_queue_t wait; snd_assert(f != NULL, return -EINVAL); *cellp = NULL; + init_waitqueue_entry(&wait, current); spin_lock_irqsave(&f->lock, flags); while ((cell = fifo_cell_out(f)) == NULL) { if (nonblock) { @@ -182,17 +184,19 @@ int snd_seq_fifo_cell_out(fifo_t *f, snd spin_unlock_irqrestore(&f->lock, flags); return -EAGAIN; } - spin_unlock(&f->lock); - interruptible_sleep_on(&f->input_sleep); - spin_lock(&f->lock); - + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&f->input_sleep, &wait); + spin_unlock_irq(&f->lock); + schedule(); + spin_lock_irq(&f->lock); + remove_wait_queue(&f->input_sleep, &wait); if (signal_pending(current)) { spin_unlock_irqrestore(&f->lock, flags); return -ERESTARTSYS; } } - *cellp = cell; spin_unlock_irqrestore(&f->lock, flags); + *cellp = cell; return 0; } --- linux-2.6.4-rc2/sound/core/seq/seq_memory.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/core/seq/seq_memory.c 2004-03-07 20:46:50.000000000 -0800 @@ -220,12 +220,14 @@ int snd_seq_cell_alloc(pool_t *pool, snd snd_seq_event_cell_t *cell; unsigned long flags; int err = -EAGAIN; + wait_queue_t wait; if (pool == NULL) return -EINVAL; *cellp = NULL; + init_waitqueue_entry(&wait, current); spin_lock_irqsave(&pool->lock, flags); if (pool->ptr == NULL) { /* not initialized */ snd_printd("seq: pool is not initialized\n"); @@ -234,9 +236,12 @@ int snd_seq_cell_alloc(pool_t *pool, snd } while (pool->free == NULL && ! nonblock && ! pool->closing) { - spin_unlock(&pool->lock); - interruptible_sleep_on(&pool->output_sleep); - spin_lock(&pool->lock); + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&pool->output_sleep, &wait); + spin_unlock_irq(&pool->lock); + schedule(); + spin_lock_irq(&pool->lock); + remove_wait_queue(&pool->output_sleep, &wait); /* interrupted? */ if (signal_pending(current)) { err = -ERESTARTSYS; --- linux-2.6.4-rc2/sound/core/seq/seq_midi.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/core/seq/seq_midi.c 2004-03-07 20:46:50.000000000 -0800 @@ -285,7 +285,7 @@ static int set_client_name(seq_midisynth cinfo.client = client->seq_client; cinfo.type = KERNEL_CLIENT; name = rmidi->name[0] ? (const char *)rmidi->name : "External MIDI"; - snprintf(cinfo.name, sizeof(cinfo.name), "Rawmidi %d - %s", card->number, name); + snprintf(cinfo.name, sizeof(cinfo.name), "%s - Rawmidi %d", name, card->number); return snd_seq_kernel_client_ctl(client->seq_client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo); } --- linux-2.6.4-rc2/sound/core/sgbuf.c 2003-06-14 12:18:25.000000000 -0700 +++ 25/sound/core/sgbuf.c 2004-03-07 20:46:50.000000000 -0800 @@ -20,7 +20,6 @@ */ #include -#include #include #include #include @@ -31,27 +30,42 @@ #define SGBUF_TBL_ALIGN 32 #define sgbuf_align_table(tbl) ((((tbl) + SGBUF_TBL_ALIGN - 1) / SGBUF_TBL_ALIGN) * SGBUF_TBL_ALIGN) +int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab) +{ + struct snd_sg_buf *sgbuf = dmab->private_data; + struct snd_dma_buffer tmpb; + int i; -/** - * snd_malloc_sgbuf_pages - allocate the pages for the PCI SG buffer - * @pci: the pci device pointer - * @size: the requested buffer size in bytes - * @dmab: the buffer record to store - * - * Initializes the SG-buffer table and allocates the buffer pages - * for the given size. - * The pages are mapped to the virtually continuous memory. - * - * This function is usually called from the middle-level functions such as - * snd_pcm_lib_malloc_pages(). - * - * Returns the mapped virtual address of the buffer if allocation was - * successful, or NULL at error. - */ -void *snd_malloc_sgbuf_pages(struct pci_dev *pci, size_t size, struct snd_dma_buffer *dmab) + if (! sgbuf) + return -EINVAL; + + for (i = 0; i < sgbuf->pages; i++) { + tmpb.area = sgbuf->table[i].buf; + tmpb.addr = sgbuf->table[i].addr; + tmpb.bytes = PAGE_SIZE; + snd_dma_free_pages(&sgbuf->dev, &tmpb); + } + if (dmab->area) + vunmap(dmab->area); + dmab->area = NULL; + + if (sgbuf->table) + kfree(sgbuf->table); + if (sgbuf->page_table) + kfree(sgbuf->page_table); + kfree(sgbuf); + dmab->private_data = NULL; + + return 0; +} + +void *snd_malloc_sgbuf_pages(const struct snd_dma_device *dev, + size_t size, struct snd_dma_buffer *dmab, + size_t *res_size) { struct snd_sg_buf *sgbuf; unsigned int i, pages; + struct snd_dma_buffer tmpb; dmab->area = NULL; dmab->addr = 0; @@ -59,7 +73,8 @@ void *snd_malloc_sgbuf_pages(struct pci_ if (! sgbuf) return NULL; memset(sgbuf, 0, sizeof(*sgbuf)); - sgbuf->pci = pci; + sgbuf->dev = *dev; + sgbuf->dev.type = SNDRV_DMA_TYPE_DEV; pages = snd_sgbuf_aligned_pages(size); sgbuf->tblsize = sgbuf_align_table(pages); sgbuf->table = kmalloc(sizeof(*sgbuf->table) * sgbuf->tblsize, GFP_KERNEL); @@ -73,14 +88,15 @@ void *snd_malloc_sgbuf_pages(struct pci_ /* allocate each page */ for (i = 0; i < pages; i++) { - void *ptr; - dma_addr_t addr; - ptr = snd_malloc_pci_page(sgbuf->pci, &addr); - if (! ptr) - goto _failed; - sgbuf->table[i].buf = ptr; - sgbuf->table[i].addr = addr; - sgbuf->page_table[i] = virt_to_page(ptr); + if (snd_dma_alloc_pages(&sgbuf->dev, PAGE_SIZE, &tmpb) < 0) { + if (res_size == NULL) + goto _failed; + *res_size = size = sgbuf->pages * PAGE_SIZE; + break; + } + sgbuf->table[i].buf = tmpb.area; + sgbuf->table[i].addr = tmpb.addr; + sgbuf->page_table[i] = virt_to_page(tmpb.area); sgbuf->pages++; } @@ -94,38 +110,3 @@ void *snd_malloc_sgbuf_pages(struct pci_ snd_free_sgbuf_pages(dmab); /* free the table */ return NULL; } - -/** - * snd_free_sgbuf_pages - free the sg buffer - * @dmab: buffer record - * - * Releases the pages and the SG-buffer table. - * - * This function is called usually from the middle-level function - * such as snd_pcm_lib_free_pages(). - * - * Returns zero if successful, or a negative error code on failure. - */ -int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab) -{ - struct snd_sg_buf *sgbuf = dmab->private_data; - int i; - - if (! sgbuf) - return -EINVAL; - - for (i = 0; i < sgbuf->pages; i++) - snd_free_pci_page(sgbuf->pci, sgbuf->table[i].buf, sgbuf->table[i].addr); - if (dmab->area) - vunmap(dmab->area); - dmab->area = NULL; - - if (sgbuf->table) - kfree(sgbuf->table); - if (sgbuf->page_table) - kfree(sgbuf->page_table); - kfree(sgbuf); - dmab->private_data = NULL; - - return 0; -} --- linux-2.6.4-rc2/sound/drivers/Kconfig 2003-06-14 12:18:29.000000000 -0700 +++ 25/sound/drivers/Kconfig 2004-03-07 20:46:50.000000000 -0800 @@ -3,17 +3,41 @@ menu "Generic devices" depends on SND!=n + +config SND_MPU401_UART + tristate + select SND_TIMER + select SND_RAWMIDI + +config SND_OPL3_LIB + tristate + select SND_TIMER + select SND_HWDEP + +config SND_OPL4_LIB + tristate + select SND_TIMER + select SND_HWDEP + +config SND_VX_LIB + tristate + select SND_HWDEP + select SND_PCM + + config SND_DUMMY tristate "Dummy (/dev/null) soundcard" depends on SND + select SND_PCM help Say 'Y' or 'M' to include dummy driver. This driver does nothing, but emulates various mixer controls and PCM devices. - config SND_VIRMIDI tristate "Virtual MIDI soundcard" depends on SND_SEQUENCER + select SND_TIMER + select SND_RAWMIDI help Say 'Y' or 'M' to include virtual MIDI driver. This driver allows to connect applications using raw MIDI devices to sequencer. @@ -21,6 +45,8 @@ config SND_VIRMIDI config SND_MTPAV tristate "MOTU MidiTimePiece AV multiport MIDI" depends on SND + select SND_TIMER + select SND_RAWMIDI help Say 'Y' or 'M' to include support for MOTU MidiTimePiece AV multiport MIDI adapter. @@ -28,6 +54,8 @@ config SND_MTPAV config SND_SERIAL_U16550 tristate "UART16550 - MIDI only driver" depends on SND + select SND_TIMER + select SND_RAWMIDI help Say 'Y' or 'M' to include support for MIDI serial port driver. It works with serial UARTs 16550 and better. @@ -35,8 +63,8 @@ config SND_SERIAL_U16550 config SND_MPU401 tristate "Generic MPU-401 UART driver" depends on SND + select SND_MPU401_UART help Say 'Y' or 'M' to include support for MPU401 hardware using UART access. endmenu - --- linux-2.6.4-rc2/sound/drivers/mpu401/Makefile 2003-06-14 12:18:51.000000000 -0700 +++ 25/sound/drivers/mpu401/Makefile 2004-03-07 20:46:50.000000000 -0800 @@ -6,40 +6,7 @@ snd-mpu401-objs := mpu401.o snd-mpu401-uart-objs := mpu401_uart.o -# Toplevel Module Dependency -obj-$(CONFIG_SND_MPU401) += snd-mpu401.o snd-mpu401-uart.o -obj-$(CONFIG_SND_ALS100) += snd-mpu401-uart.o -obj-$(CONFIG_SND_AZT2320) += snd-mpu401-uart.o -obj-$(CONFIG_SND_AZT3328) += snd-mpu401-uart.o -obj-$(CONFIG_SND_DT019X) += snd-mpu401-uart.o -obj-$(CONFIG_SND_ES18XX) += snd-mpu401-uart.o -obj-$(CONFIG_SND_OPL3SA2) += snd-mpu401-uart.o -obj-$(CONFIG_SND_AD1816A) += snd-mpu401-uart.o -obj-$(CONFIG_SND_CS4231) += snd-mpu401-uart.o -obj-$(CONFIG_SND_CS4232) += snd-mpu401-uart.o -obj-$(CONFIG_SND_CS4236) += snd-mpu401-uart.o -obj-$(CONFIG_SND_ES1688) += snd-mpu401-uart.o -obj-$(CONFIG_SND_GUSEXTREME) += snd-mpu401-uart.o -obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-mpu401-uart.o -obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-mpu401-uart.o -obj-$(CONFIG_SND_OPTI93X) += snd-mpu401-uart.o -obj-$(CONFIG_SND_SB16) += snd-mpu401-uart.o -obj-$(CONFIG_SND_SBAWE) += snd-mpu401-uart.o -obj-$(CONFIG_SND_WAVEFRONT) += snd-mpu401-uart.o -obj-$(CONFIG_SND_ALS4000) += snd-mpu401-uart.o -obj-$(CONFIG_SND_CMIPCI) += snd-mpu401-uart.o -obj-$(CONFIG_SND_ES1938) += snd-mpu401-uart.o -obj-$(CONFIG_SND_ES1968) += snd-mpu401-uart.o -obj-$(CONFIG_SND_FM801) += snd-mpu401-uart.o -obj-$(CONFIG_SND_ICE1712) += snd-mpu401-uart.o -obj-$(CONFIG_SND_ICE1724) += snd-mpu401-uart.o -obj-$(CONFIG_SND_INTEL8X0) += snd-mpu401-uart.o -obj-$(CONFIG_SND_SONICVIBES) += snd-mpu401-uart.o -obj-$(CONFIG_SND_VIA82XX) += snd-mpu401-uart.o -obj-$(CONFIG_SND_ALI5451) += snd-mpu401-uart.o -obj-$(CONFIG_SND_TRIDENT) += snd-mpu401-uart.o -obj-$(CONFIG_SND_YMFPCI) += snd-mpu401-uart.o -obj-$(CONFIG_SND_PC98_CS4232) += snd-mpu401-uart.o -obj-$(CONFIG_SND_SSCAPE) += snd-mpu401-uart.o +obj-$(CONFIG_SND_MPU401_UART) += snd-mpu401-uart.o -obj-m := $(sort $(obj-m)) +# Toplevel Module Dependency +obj-$(CONFIG_SND_MPU401) += snd-mpu401.o --- linux-2.6.4-rc2/sound/drivers/mpu401/mpu401_uart.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/drivers/mpu401/mpu401_uart.c 2004-03-07 20:46:50.000000000 -0800 @@ -528,9 +528,9 @@ int snd_mpu401_uart_new(snd_card_t * car mpu->irq = irq; mpu->irq_flags = irq_flags; if (card->shortname[0]) - snprintf(rmidi->name, sizeof(rmidi->name), "%s MPU-401", card->shortname); + snprintf(rmidi->name, sizeof(rmidi->name), "%s MIDI", card->shortname); else - sprintf(rmidi->name, "MPU-401 (UART) %d-%d", card->number, device); + sprintf(rmidi->name, "MPU-401 MIDI %d-%d", card->number, device); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_mpu401_uart_output); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_mpu401_uart_input); rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | --- linux-2.6.4-rc2/sound/drivers/opl3/Makefile 2003-06-14 12:17:56.000000000 -0700 +++ 25/sound/drivers/opl3/Makefile 2004-03-07 20:46:50.000000000 -0800 @@ -9,37 +9,13 @@ ifeq ($(CONFIG_SND_SEQUENCER_OSS),y) snd-opl3-synth-objs += opl3_oss.o endif -OPL3_OBJS = snd-opl3-lib.o -ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y) -OPL3_OBJS += snd-opl3-synth.o -endif - -# Toplevel Module Dependency -obj-$(CONFIG_SND_ALS100) += $(OPL3_OBJS) -obj-$(CONFIG_SND_AZT2320) += $(OPL3_OBJS) -obj-$(CONFIG_SND_AZT3328) += $(OPL3_OBJS) -obj-$(CONFIG_SND_DT019X) += $(OPL3_OBJS) -obj-$(CONFIG_SND_ES18XX) += $(OPL3_OBJS) -obj-$(CONFIG_SND_OPL3SA2) += $(OPL3_OBJS) -obj-$(CONFIG_SND_AD1816A) += $(OPL3_OBJS) -obj-$(CONFIG_SND_CS4232) += $(OPL3_OBJS) -obj-$(CONFIG_SND_PC98_CS4232) += $(OPL3_OBJS) -obj-$(CONFIG_SND_CS4236) += $(OPL3_OBJS) -obj-$(CONFIG_SND_ES1688) += $(OPL3_OBJS) -obj-$(CONFIG_SND_GUSEXTREME) += $(OPL3_OBJS) -obj-$(CONFIG_SND_OPTI92X_AD1848) += $(OPL3_OBJS) -obj-$(CONFIG_SND_OPTI92X_CS4231) += $(OPL3_OBJS) -obj-$(CONFIG_SND_OPTI93X) += $(OPL3_OBJS) -obj-$(CONFIG_SND_SB8) += $(OPL3_OBJS) -obj-$(CONFIG_SND_SB16) += $(OPL3_OBJS) -obj-$(CONFIG_SND_SBAWE) += $(OPL3_OBJS) -obj-$(CONFIG_SND_WAVEFRONT) += $(OPL3_OBJS) -obj-$(CONFIG_SND_ALS4000) += $(OPL3_OBJS) -obj-$(CONFIG_SND_CMIPCI) += $(OPL3_OBJS) -obj-$(CONFIG_SND_CS4281) += $(OPL3_OBJS) -obj-$(CONFIG_SND_ES1938) += $(OPL3_OBJS) -obj-$(CONFIG_SND_FM801) += $(OPL3_OBJS) -obj-$(CONFIG_SND_SONICVIBES) += $(OPL3_OBJS) -obj-$(CONFIG_SND_YMFPCI) += $(OPL3_OBJS) +# +# this function returns: +# "m" - CONFIG_SND_SEQUENCER is m +# - CONFIG_SND_SEQUENCER is undefined +# otherwise parameter #1 value +# +sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1))) -obj-m := $(sort $(obj-m)) +obj-$(CONFIG_SND_OPL3_LIB) += snd-opl3-lib.o +obj-$(call sequencer,$(CONFIG_SND_OPL3_LIB)) += snd-opl3-synth.o --- linux-2.6.4-rc2/sound/drivers/opl4/Makefile 2003-06-14 12:18:32.000000000 -0700 +++ 25/sound/drivers/opl4/Makefile 2004-03-07 20:46:50.000000000 -0800 @@ -6,10 +6,13 @@ snd-opl4-lib-objs := opl4_lib.o opl4_mixer.o opl4_proc.o snd-opl4-synth-objs := opl4_seq.o opl4_synth.o yrw801.o -OPL4_OBJS := snd-opl4-lib.o -ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y) - OPL4_OBJS += snd-opl4-synth.o -endif +# +# this function returns: +# "m" - CONFIG_SND_SEQUENCER is m +# - CONFIG_SND_SEQUENCER is undefined +# otherwise parameter #1 value +# +sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1))) -obj-$(CONFIG_SND_OPTI92X_AD1848) += $(OPL4_OBJS) -obj-$(CONFIG_SND_OPTI92X_CS4231) += $(OPL4_OBJS) +obj-$(CONFIG_SND_OPL4_LIB) += snd-opl4-lib.o +obj-$(call sequencer,$(CONFIG_SND_OPL4_LIB)) += snd-opl4-synth.o \ No newline at end of file --- linux-2.6.4-rc2/sound/drivers/vx/Makefile 2003-06-14 12:17:58.000000000 -0700 +++ 25/sound/drivers/vx/Makefile 2004-03-07 20:46:50.000000000 -0800 @@ -5,7 +5,4 @@ snd-vx-lib-objs := vx_core.o vx_hwdep.o vx_pcm.o vx_mixer.o vx_cmd.o vx_uer.o -obj-$(CONFIG_SND_VXPOCKET) += snd-vx-lib.o -obj-$(CONFIG_SND_VXP440) += snd-vx-lib.o -obj-$(CONFIG_SND_VX222) += snd-vx-lib.o - +obj-$(CONFIG_SND_VX_LIB) += snd-vx-lib.o --- linux-2.6.4-rc2/sound/drivers/vx/vx_core.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/drivers/vx/vx_core.c 2004-03-07 20:46:50.000000000 -0800 @@ -355,11 +355,12 @@ int vx_send_msg_nolock(vx_core_t *chip, */ int vx_send_msg(vx_core_t *chip, struct vx_rmh *rmh) { + unsigned long flags; int err; - spin_lock_bh(&chip->lock); + spin_lock_irqsave(&chip->lock, flags); err = vx_send_msg_nolock(chip, rmh); - spin_unlock_bh(&chip->lock); + spin_unlock_irqrestore(&chip->lock, flags); return err; } @@ -414,11 +415,12 @@ int vx_send_rih_nolock(vx_core_t *chip, */ int vx_send_rih(vx_core_t *chip, int cmd) { + unsigned long flags; int err; - spin_lock_bh(&chip->lock); + spin_lock_irqsave(&chip->lock, flags); err = vx_send_rih_nolock(chip, cmd); - spin_unlock_bh(&chip->lock); + spin_unlock_irqrestore(&chip->lock, flags); return err; } --- linux-2.6.4-rc2/sound/drivers/vx/vx_mixer.c 2003-06-14 12:18:04.000000000 -0700 +++ 25/sound/drivers/vx/vx_mixer.c 2004-03-07 20:46:50.000000000 -0800 @@ -919,7 +919,7 @@ int snd_vx_mixer_new(vx_core_t *chip) if ((err = snd_ctl_add(card, snd_ctl_new1(&vx_control_iec958, chip))) < 0) return err; /* VU, peak, saturation meters */ - for (c = 0; c < 1; c++) { + for (c = 0; c < 2; c++) { static char *dir[2] = { "Output", "Input" }; for (i = 0; i < chip->hw->num_ins; i++) { int val = (i * 2) | (c << 8); --- linux-2.6.4-rc2/sound/drivers/vx/vx_pcm.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/drivers/vx/vx_pcm.c 2004-03-07 20:46:50.000000000 -0800 @@ -561,7 +561,7 @@ static snd_pcm_hardware_t vx_pcm_playbac .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, .rate_min = 5000, .rate_max = 48000, - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .buffer_bytes_max = (128*1024), .period_bytes_min = 126, @@ -958,7 +958,7 @@ static snd_pcm_hardware_t vx_pcm_capture .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, .rate_min = 5000, .rate_max = 48000, - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .buffer_bytes_max = (128*1024), .period_bytes_min = 126, --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/sound/i2c/other/ak4117.c 2004-03-07 20:46:50.000000000 -0800 @@ -0,0 +1,561 @@ +/* + * Routines for control of the AK4117 via 4-wire serial interface + * IEC958 (S/PDIF) receiver by Asahi Kasei + * Copyright (c) by Jaroslav Kysela + * + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("AK4117 IEC958 (S/PDIF) receiver by Asahi Kasei"); +MODULE_LICENSE("GPL"); + +#define chip_t ak4117_t + +#define AK4117_ADDR 0x00 /* fixed address */ + +static void snd_ak4117_timer(unsigned long data); + +static void reg_write(ak4117_t *ak4117, unsigned char reg, unsigned char val) +{ + ak4117->write(ak4117->private_data, reg, val); + if (reg < sizeof(ak4117->regmap)) + ak4117->regmap[reg] = val; +} + +static inline unsigned char reg_read(ak4117_t *ak4117, unsigned char reg) +{ + return ak4117->read(ak4117->private_data, reg); +} + +#if 0 +static void reg_dump(ak4117_t *ak4117) +{ + int i; + + printk("AK4117 REG DUMP:\n"); + for (i = 0; i < 0x1b; i++) + printk("reg[%02x] = %02x (%02x)\n", i, reg_read(ak4117, i), i < sizeof(ak4117->regmap) ? ak4117->regmap[i] : 0); +} +#endif + +static void snd_ak4117_free(ak4117_t *chip) +{ + del_timer(&chip->timer); + snd_magic_kfree(chip); +} + +static int snd_ak4117_dev_free(snd_device_t *device) +{ + ak4117_t *chip = snd_magic_cast(ak4117_t, device->device_data, return -ENXIO); + snd_ak4117_free(chip); + return 0; +} + +int snd_ak4117_create(snd_card_t *card, ak4117_read_t *read, ak4117_write_t *write, + unsigned char pgm[5], void *private_data, ak4117_t **r_ak4117) +{ + ak4117_t *chip; + int err = 0; + unsigned char reg; + static snd_device_ops_t ops = { + .dev_free = snd_ak4117_dev_free, + }; + + chip = (ak4117_t *)snd_magic_kcalloc(ak4117_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + spin_lock_init(&chip->lock); + chip->card = card; + chip->read = read; + chip->write = write; + chip->private_data = private_data; + init_timer(&chip->timer); + chip->timer.data = (unsigned long)chip; + chip->timer.function = snd_ak4117_timer; + + for (reg = 0; reg < 5; reg++) + chip->regmap[reg] = pgm[reg]; + snd_ak4117_reinit(chip); + + chip->rcs0 = reg_read(chip, AK4117_REG_RCS0) & ~(AK4117_QINT | AK4117_CINT | AK4117_STC); + chip->rcs1 = reg_read(chip, AK4117_REG_RCS1); + chip->rcs2 = reg_read(chip, AK4117_REG_RCS2); + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) + goto __fail; + + if (r_ak4117) + *r_ak4117 = chip; + return 0; + + __fail: + snd_ak4117_free(chip); + return err < 0 ? err : -EIO; +} + +void snd_ak4117_reg_write(ak4117_t *chip, unsigned char reg, unsigned char mask, unsigned char val) +{ + if (reg >= 5) + return; + reg_write(chip, reg, (chip->regmap[reg] & ~mask) | val); +} + +void snd_ak4117_reinit(ak4117_t *chip) +{ + unsigned char old = chip->regmap[AK4117_REG_PWRDN], reg; + + del_timer(&chip->timer); + chip->init = 1; + /* bring the chip to reset state and powerdown state */ + reg_write(chip, AK4117_REG_PWRDN, 0); + udelay(200); + /* release reset, but leave powerdown */ + reg_write(chip, AK4117_REG_PWRDN, (old | AK4117_RST) & ~AK4117_PWN); + udelay(200); + for (reg = 1; reg < 5; reg++) + reg_write(chip, reg, chip->regmap[reg]); + /* release powerdown, everything is initialized now */ + reg_write(chip, AK4117_REG_PWRDN, old | AK4117_RST | AK4117_PWN); + chip->init = 0; + chip->timer.expires = 1 + jiffies; + add_timer(&chip->timer); +} + +static unsigned int external_rate(unsigned char rcs1) +{ + switch (rcs1 & (AK4117_FS0|AK4117_FS1|AK4117_FS2|AK4117_FS3)) { + case AK4117_FS_32000HZ: return 32000; + case AK4117_FS_44100HZ: return 44100; + case AK4117_FS_48000HZ: return 48000; + case AK4117_FS_88200HZ: return 88200; + case AK4117_FS_96000HZ: return 96000; + case AK4117_FS_176400HZ: return 176400; + case AK4117_FS_192000HZ: return 192000; + default: return 0; + } +} + +static int snd_ak4117_in_error_info(snd_kcontrol_t *kcontrol, + snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = LONG_MAX; + return 0; +} + +static int snd_ak4117_in_error_get(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + ak4117_t *chip = snd_kcontrol_chip(kcontrol); + long *ptr; + + spin_lock_irq(&chip->lock); + ptr = (long *)(((char *)chip) + kcontrol->private_value); + ucontrol->value.integer.value[0] = *ptr; + *ptr = 0; + spin_unlock_irq(&chip->lock); + return 0; +} + +static int snd_ak4117_in_bit_info(snd_kcontrol_t *kcontrol, + snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_ak4117_in_bit_get(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + ak4117_t *chip = snd_kcontrol_chip(kcontrol); + unsigned char reg = kcontrol->private_value & 0xff; + unsigned char bit = (kcontrol->private_value >> 8) & 0xff; + unsigned char inv = (kcontrol->private_value >> 31) & 1; + + ucontrol->value.integer.value[0] = ((reg_read(chip, reg) & (1 << bit)) ? 1 : 0) ^ inv; + return 0; +} + +static int snd_ak4117_rx_info(snd_kcontrol_t *kcontrol, + snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_ak4117_rx_get(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + ak4117_t *chip = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = (chip->regmap[AK4117_REG_IO] & AK4117_IPS) ? 1 : 0; + return 0; +} + +static int snd_ak4117_rx_put(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + ak4117_t *chip = snd_kcontrol_chip(kcontrol); + int change; + u8 old_val; + + spin_lock_irq(&chip->lock); + old_val = chip->regmap[AK4117_REG_IO]; + change = !!ucontrol->value.integer.value[0] != ((old_val & AK4117_IPS) ? 1 : 0); + if (change) + reg_write(chip, AK4117_REG_IO, (old_val & ~AK4117_IPS) | (ucontrol->value.integer.value[0] ? AK4117_IPS : 0)); + spin_unlock_irq(&chip->lock); + return change; +} + +static int snd_ak4117_rate_info(snd_kcontrol_t *kcontrol, + snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 192000; + return 0; +} + +static int snd_ak4117_rate_get(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + ak4117_t *chip = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = external_rate(reg_read(chip, AK4117_REG_RCS1)); + return 0; +} + +static int snd_ak4117_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_ak4117_spdif_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ak4117_t *chip = snd_kcontrol_chip(kcontrol); + unsigned i; + + for (i = 0; i < AK4117_REG_RXCSB_SIZE; i++) + ucontrol->value.iec958.status[i] = reg_read(chip, AK4117_REG_RXCSB0 + i); + return 0; +} + +static int snd_ak4117_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_ak4117_spdif_mask_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + memset(ucontrol->value.iec958.status, 0xff, AK4117_REG_RXCSB_SIZE); + return 0; +} + +static int snd_ak4117_spdif_pinfo(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xffff; + uinfo->count = 4; + return 0; +} + +static int snd_ak4117_spdif_pget(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ak4117_t *chip = snd_kcontrol_chip(kcontrol); + unsigned short tmp; + + ucontrol->value.integer.value[0] = 0xf8f2; + ucontrol->value.integer.value[1] = 0x4e1f; + tmp = reg_read(chip, AK4117_REG_Pc0) | (reg_read(chip, AK4117_REG_Pc1) << 8); + ucontrol->value.integer.value[2] = tmp; + tmp = reg_read(chip, AK4117_REG_Pd0) | (reg_read(chip, AK4117_REG_Pd1) << 8); + ucontrol->value.integer.value[3] = tmp; + return 0; +} + +static int snd_ak4117_spdif_qinfo(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = AK4117_REG_QSUB_SIZE; + return 0; +} + +static int snd_ak4117_spdif_qget(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ak4117_t *chip = snd_kcontrol_chip(kcontrol); + unsigned i; + + for (i = 0; i < AK4117_REG_QSUB_SIZE; i++) + ucontrol->value.bytes.data[i] = reg_read(chip, AK4117_REG_QSUB_ADDR + i); + return 0; +} + +/* Don't forget to change AK4117_CONTROLS define!!! */ +static snd_kcontrol_new_t snd_ak4117_iec958_controls[] = { +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 Parity Errors", + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_ak4117_in_error_info, + .get = snd_ak4117_in_error_get, + .private_value = offsetof(ak4117_t, parity_errors), +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 V-Bit Errors", + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_ak4117_in_error_info, + .get = snd_ak4117_in_error_get, + .private_value = offsetof(ak4117_t, v_bit_errors), +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 C-CRC Errors", + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_ak4117_in_error_info, + .get = snd_ak4117_in_error_get, + .private_value = offsetof(ak4117_t, ccrc_errors), +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 Q-CRC Errors", + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_ak4117_in_error_info, + .get = snd_ak4117_in_error_get, + .private_value = offsetof(ak4117_t, qcrc_errors), +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 External Rate", + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_ak4117_rate_info, + .get = snd_ak4117_rate_get, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",CAPTURE,MASK), + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .info = snd_ak4117_spdif_mask_info, + .get = snd_ak4117_spdif_mask_get, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",CAPTURE,DEFAULT), + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_ak4117_spdif_info, + .get = snd_ak4117_spdif_get, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 Preample Capture Default", + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_ak4117_spdif_pinfo, + .get = snd_ak4117_spdif_pget, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 Q-subcode Capture Default", + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_ak4117_spdif_qinfo, + .get = snd_ak4117_spdif_qget, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 Audio", + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_ak4117_in_bit_info, + .get = snd_ak4117_in_bit_get, + .private_value = (1<<31) | (3<<8) | AK4117_REG_RCS0, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 Non-PCM Bitstream", + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_ak4117_in_bit_info, + .get = snd_ak4117_in_bit_get, + .private_value = (5<<8) | AK4117_REG_RCS1, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 DTS Bitstream", + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_ak4117_in_bit_info, + .get = snd_ak4117_in_bit_get, + .private_value = (6<<8) | AK4117_REG_RCS1, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "AK4117 Input Select", + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE, + .info = snd_ak4117_rx_info, + .get = snd_ak4117_rx_get, + .put = snd_ak4117_rx_put, +} +}; + +int snd_ak4117_build(ak4117_t *ak4117, snd_pcm_substream_t *cap_substream) +{ + snd_kcontrol_t *kctl; + unsigned int idx; + int err; + + snd_assert(cap_substream, return -EINVAL); + ak4117->substream = cap_substream; + for (idx = 0; idx < AK4117_CONTROLS; idx++) { + kctl = snd_ctl_new1(&snd_ak4117_iec958_controls[idx], ak4117); + if (kctl == NULL) + return -ENOMEM; + kctl->id.device = cap_substream->pcm->device; + kctl->id.subdevice = cap_substream->number; + err = snd_ctl_add(ak4117->card, kctl); + if (err < 0) + return err; + ak4117->kctls[idx] = kctl; + } + return 0; +} + +int snd_ak4117_external_rate(ak4117_t *ak4117) +{ + unsigned char rcs1; + + rcs1 = reg_read(ak4117, AK4117_REG_RCS1); + return external_rate(rcs1); +} + +int snd_ak4117_check_rate_and_errors(ak4117_t *ak4117, unsigned int flags) +{ + snd_pcm_runtime_t *runtime = ak4117->substream ? ak4117->substream->runtime : NULL; + unsigned long _flags; + int res = 0; + unsigned char rcs0, rcs1, rcs2; + unsigned char c0, c1; + + rcs1 = reg_read(ak4117, AK4117_REG_RCS1); + if (flags & AK4117_CHECK_NO_STAT) + goto __rate; + rcs0 = reg_read(ak4117, AK4117_REG_RCS0); + rcs2 = reg_read(ak4117, AK4117_REG_RCS2); + // printk("AK IRQ: rcs0 = 0x%x, rcs1 = 0x%x, rcs2 = 0x%x\n", rcs0, rcs1, rcs2); + spin_lock_irqsave(&ak4117->lock, _flags); + if (rcs0 & AK4117_PAR) + ak4117->parity_errors++; + if (rcs0 & AK4117_V) + ak4117->v_bit_errors++; + if (rcs2 & AK4117_CCRC) + ak4117->ccrc_errors++; + if (rcs2 & AK4117_QCRC) + ak4117->qcrc_errors++; + c0 = (ak4117->rcs0 & (AK4117_QINT | AK4117_CINT | AK4117_STC | AK4117_AUDION | AK4117_AUTO | AK4117_UNLCK)) ^ + (rcs0 & (AK4117_QINT | AK4117_CINT | AK4117_STC | AK4117_AUDION | AK4117_AUTO | AK4117_UNLCK)); + c1 = (ak4117->rcs1 & (AK4117_DTSCD | AK4117_NPCM | AK4117_PEM | 0x0f)) ^ + (rcs1 & (AK4117_DTSCD | AK4117_NPCM | AK4117_PEM | 0x0f)); + ak4117->rcs0 = rcs0 & ~(AK4117_QINT | AK4117_CINT | AK4117_STC); + ak4117->rcs1 = rcs1; + ak4117->rcs2 = rcs2; + spin_unlock_irqrestore(&ak4117->lock, _flags); + + if (rcs0 & AK4117_PAR) + snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[0]->id); + if (rcs0 & AK4117_V) + snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[1]->id); + if (rcs2 & AK4117_CCRC) + snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[2]->id); + if (rcs2 & AK4117_QCRC) + snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[3]->id); + + /* rate change */ + if (c1 & 0x0f) + snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[4]->id); + + if ((c1 & AK4117_PEM) | (c0 & AK4117_CINT)) + snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[6]->id); + if (c0 & AK4117_QINT) + snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[8]->id); + + if (c0 & AK4117_AUDION) + snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[9]->id); + if (c1 & AK4117_NPCM) + snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[10]->id); + if (c1 & AK4117_DTSCD) + snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[11]->id); + + if (ak4117->change_callback && (c0 | c1) != 0) + ak4117->change_callback(ak4117, c0, c1); + + __rate: + /* compare rate */ + res = external_rate(rcs1); + if (!(flags & AK4117_CHECK_NO_RATE) && runtime && runtime->rate != res) { + snd_pcm_stream_lock_irqsave(ak4117->substream, _flags); + if (snd_pcm_running(ak4117->substream)) { + // printk("rate changed (%i <- %i)\n", runtime->rate, res); + snd_pcm_stop(ak4117->substream, SNDRV_PCM_STATE_DRAINING); + wake_up(&runtime->sleep); + res = 1; + } + snd_pcm_stream_unlock_irqrestore(ak4117->substream, _flags); + } + return res; +} + +static void snd_ak4117_timer(unsigned long data) +{ + ak4117_t *chip = snd_magic_cast(ak4117_t, (void *)data, return); + + if (chip->init) + return; + snd_ak4117_check_rate_and_errors(chip, 0); + chip->timer.expires = 1 + jiffies; + add_timer(&chip->timer); +} + +EXPORT_SYMBOL(snd_ak4117_create); +EXPORT_SYMBOL(snd_ak4117_reg_write); +EXPORT_SYMBOL(snd_ak4117_reinit); +EXPORT_SYMBOL(snd_ak4117_build); +EXPORT_SYMBOL(snd_ak4117_external_rate); +EXPORT_SYMBOL(snd_ak4117_check_rate_and_errors); --- linux-2.6.4-rc2/sound/i2c/other/Makefile 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/i2c/other/Makefile 2004-03-07 20:46:50.000000000 -0800 @@ -3,10 +3,12 @@ # Copyright (c) 2003 by Jaroslav Kysela # +snd-ak4117-objs := ak4117.o snd-ak4xxx-adda-objs := ak4xxx-adda.o snd-tea575x-tuner-objs := tea575x-tuner.o # Module Dependency +obj-$(CONFIG_SND_PDAUDIOCF) += snd-ak4117.o obj-$(CONFIG_SND_ICE1712) += snd-ak4xxx-adda.o obj-$(CONFIG_SND_ICE1724) += snd-ak4xxx-adda.o obj-$(CONFIG_SND_FM801_TEA575X) += snd-tea575x-tuner.o --- linux-2.6.4-rc2/sound/isa/ad1816a/ad1816a_lib.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/isa/ad1816a/ad1816a_lib.c 2004-03-07 20:46:50.000000000 -0800 @@ -684,7 +684,9 @@ int snd_ad1816a_pcm(ad1816a_t *chip, int strcpy(pcm->name, snd_ad1816a_chip_id(chip)); snd_ad1816a_init(chip); - snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_isa_data(), + 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); chip->pcm = pcm; if (rpcm) --- linux-2.6.4-rc2/sound/isa/ad1848/ad1848_lib.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/isa/ad1848/ad1848_lib.c 2004-03-07 20:46:50.000000000 -0800 @@ -1043,7 +1043,9 @@ int snd_ad1848_pcm(ad1848_t *chip, int d pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; strcpy(pcm->name, snd_ad1848_chip_id(chip)); - snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, chip->dma > 3 ? 128*1024 : 64*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_isa_data(), + 64*1024, chip->dma > 3 ? 128*1024 : 64*1024); chip->pcm = pcm; if (rpcm) --- linux-2.6.4-rc2/sound/isa/cmi8330.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/isa/cmi8330.c 2004-03-07 20:46:50.000000000 -0800 @@ -438,7 +438,9 @@ static int __devinit snd_cmi8330_pcm(snd snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &chip->streams[SNDRV_PCM_STREAM_PLAYBACK].ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &chip->streams[SNDRV_PCM_STREAM_CAPTURE].ops); - snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_isa_data(), + 64*1024, 128*1024); chip->pcm = pcm; return 0; --- linux-2.6.4-rc2/sound/isa/cs423x/cs4231_lib.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/isa/cs423x/cs4231_lib.c 2004-03-07 20:46:50.000000000 -0800 @@ -1653,17 +1653,21 @@ int snd_cs4231_pcm(cs4231_t *chip, int d strcpy(pcm->name, snd_cs4231_chip_id(chip)); #ifdef LEGACY_SUPPORT - snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_isa_data(), + 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); #else # ifdef EBUS_SUPPORT if (chip->ebus_flag) { - snd_pcm_lib_preallocate_pci_pages_for_all(chip->dev_u.pdev, pcm, - 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_PCI, + chip->dev_u.pdev, + 64*1024, 128*1024); } else { # endif # ifdef SBUS_SUPPORT - snd_pcm_lib_preallocate_sbus_pages_for_all(chip->dev_u.sdev, pcm, - 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_SBUS, + chip->dev_u.sdev, + 64*1024, 128*1024); # endif # ifdef EBUS_SUPPORT } --- linux-2.6.4-rc2/sound/isa/es1688/es1688_lib.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/isa/es1688/es1688_lib.c 2004-03-07 20:46:50.000000000 -0800 @@ -752,7 +752,9 @@ int snd_es1688_pcm(es1688_t * chip, int sprintf(pcm->name, snd_es1688_chip_id(chip)); chip->pcm = pcm; - snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, 64*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_isa_data(), + 64*1024, 64*1024); if (rpcm) *rpcm = pcm; --- linux-2.6.4-rc2/sound/isa/es18xx.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/isa/es18xx.c 2004-03-07 20:46:50.000000000 -0800 @@ -1598,7 +1598,10 @@ int __devinit snd_es18xx_pcm(es18xx_t *c sprintf(pcm->name, "ESS AudioDrive ES%x", chip->version); chip->pcm = pcm; - snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_isa_data(), + 64*1024, + chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); if (rpcm) *rpcm = pcm; --- linux-2.6.4-rc2/sound/isa/gus/gus_pcm.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/isa/gus/gus_pcm.c 2004-03-07 20:46:50.000000000 -0800 @@ -874,7 +874,9 @@ int snd_gf1_pcm_new(snd_gus_card_t * gus snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_gf1_pcm_playback_ops); for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) - snd_pcm_lib_preallocate_isa_pages(substream, 64*1024, gus->gf1.dma1 > 3 ? 128*1024 : 64*1024); + snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV, + snd_dma_isa_data(), + 64*1024, gus->gf1.dma1 > 3 ? 128*1024 : 64*1024); pcm->info_flags = 0; pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; @@ -882,7 +884,9 @@ int snd_gf1_pcm_new(snd_gus_card_t * gus snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_gf1_pcm_capture_ops); if (gus->gf1.dma2 == gus->gf1.dma1) pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX; - snd_pcm_lib_preallocate_isa_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, 64*1024, gus->gf1.dma2 > 3 ? 128*1024 : 64*1024); + snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, + SNDRV_DMA_TYPE_DEV, snd_dma_isa_data(), + 64*1024, gus->gf1.dma2 > 3 ? 128*1024 : 64*1024); } strcpy(pcm->name, pcm->id); if (gus->interwave) { --- linux-2.6.4-rc2/sound/isa/Kconfig 2003-06-14 12:18:32.000000000 -0700 +++ 25/sound/isa/Kconfig 2004-03-07 20:46:50.000000000 -0800 @@ -6,6 +6,9 @@ menu "ISA devices" config SND_AD1816A tristate "Analog Devices SoundPort AD1816A" depends on SND && ISAPNP + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for Analog Devices SoundPort AD1816A or compatible sound chips. @@ -13,6 +16,7 @@ config SND_AD1816A config SND_AD1848 tristate "Generic AD1848/CS4248 driver" depends on SND + select SND_PCM help Say 'Y' or 'M' to include support for AD1848 (Analog Devices) or CS4248 (Cirrus Logic - Crystal Semiconductors) chips. Please, for newer chips @@ -21,6 +25,8 @@ config SND_AD1848 config SND_CS4231 tristate "Generic Cirrus Logic CS4231 driver" depends on SND + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for CS4231 chips from Cirrus Logic - Crystal Semiconductors. @@ -28,6 +34,9 @@ config SND_CS4231 config SND_CS4232 tristate "Generic Cirrus Logic CS4232 driver" depends on SND + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for CS4232 chips from Cirrus Logic - Crystal Semiconductors. @@ -35,6 +44,9 @@ config SND_CS4232 config SND_CS4236 tristate "Generic Cirrus Logic CS4236+ driver" depends on SND + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for CS4235,CS4236,CS4237B,CS4238B,CS4239 chips from Cirrus Logic - Crystal Semiconductors. @@ -42,6 +54,9 @@ config SND_CS4236 config SND_PC98_CS4232 tristate "NEC PC9800 CS4232 driver" depends on SND && X86_PC9800 + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for NEC PC-9801/PC-9821 on-board soundchip based on CS4232. @@ -49,42 +64,59 @@ config SND_PC98_CS4232 config SND_ES968 tristate "Generic ESS ES968 driver" depends on SND && ISAPNP + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for ESS AudioDrive ES968 chip. config SND_ES1688 tristate "Generic ESS ES688/ES1688 driver" depends on SND + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for ESS AudioDrive ES688 or ES1688 chips. config SND_ES18XX tristate "Generic ESS ES18xx driver" depends on SND + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for ESS AudioDrive ES18xx chips. config SND_GUSCLASSIC tristate "Gravis UltraSound Classic" depends on SND + select SND_RAWMIDI + select SND_PCM help Say 'Y' or 'M' to include support for Gravis UltraSound Classic soundcard. config SND_GUSEXTREME tristate "Gravis UltraSound Extreme" depends on SND + select SND_HWDEP + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for Gravis UltraSound Extreme soundcard. config SND_GUSMAX tristate "Gravis UltraSound MAX" depends on SND + select SND_RAWMIDI + select SND_PCM help Say 'Y' or 'M' to include support for Gravis UltraSound MAX soundcard. config SND_INTERWAVE tristate "AMD InterWave, Gravis UltraSound PnP" depends on SND + select SND_RAWMIDI + select SND_PCM help Say 'Y' or 'M' to include support for AMD InterWave based soundcards (Gravis UltraSound Plug & Play, STB SoundRage32, MED3210, Dynasonic Pro, @@ -93,6 +125,8 @@ config SND_INTERWAVE config SND_INTERWAVE_STB tristate "AMD InterWave + TEA6330T (UltraSound 32-Pro)" depends on SND + select SND_RAWMIDI + select SND_PCM help Say 'Y' or 'M' to include support for AMD InterWave based soundcards with TEA6330T bass and treble regulator (UltraSound 32-Pro). @@ -100,6 +134,10 @@ config SND_INTERWAVE_STB config SND_OPTI92X_AD1848 tristate "OPTi 82C92x - AD1848" depends on SND + select SND_OPL3_LIB + select SND_OPL4_LIB + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for Opti92x soundcards equiped with AD1848 codec. @@ -107,6 +145,10 @@ config SND_OPTI92X_AD1848 config SND_OPTI92X_CS4231 tristate "OPTi 82C92x - CS4231" depends on SND + select SND_OPL3_LIB + select SND_OPL4_LIB + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for Opti92x soundcards equiped with CS4231 codec. @@ -114,12 +156,18 @@ config SND_OPTI92X_CS4231 config SND_OPTI93X tristate "OPTi 82C93x" depends on SND + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for Opti93x soundcards. config SND_SB8 tristate "Sound Blaster 1.0/2.0/Pro (8-bit)" depends on SND + select SND_OPL3_LIB + select SND_RAWMIDI + select SND_PCM help Say 'Y' or 'M' to include support for Sound Blaster 1.0/2.0/Pro (8-bit) soundcards or 100% compatible from Creative. @@ -127,6 +175,9 @@ config SND_SB8 config SND_SB16 tristate "Sound Blaster 16 (PnP)" depends on SND + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for Sound Blaster 16 (including Plug and Play version). @@ -134,6 +185,9 @@ config SND_SB16 config SND_SBAWE tristate "Sound Blaster AWE (32,64) (PnP)" depends on SND + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for Sound Blaster AWE (including Plug and Play version). @@ -149,6 +203,9 @@ config SND_SB16_CSP config SND_WAVEFRONT tristate "Turtle Beach Maui,Tropez,Tropez+ (Wavefront)" depends on SND + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for Turtle Beach Maui, Tropez and Tropez+ soundcards based on Wavefront chip. @@ -156,6 +213,9 @@ config SND_WAVEFRONT config SND_ALS100 tristate "Avance Logic ALS100/ALS120" depends on SND && ISAPNP + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for Avance Logic ALS100, ALS110, ALS120 and ALS200 soundcards. @@ -163,18 +223,25 @@ config SND_ALS100 config SND_AZT2320 tristate "Aztech Systems AZT2320" depends on SND && ISAPNP + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for Aztech Systems AZT2320 soundcard. config SND_CMI8330 tristate "C-Media CMI8330" depends on SND + select SND_PCM help Say 'Y' or 'M' to include support for C-Media CMI8330 based soundcards. config SND_DT019X tristate "Diamond Technologies DT-019X, Avance Logic ALS-007" depends on SND && ISAPNP + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for Diamond Technologies DT-019X and Avance Logic ALS-007 soundcards. @@ -182,18 +249,25 @@ config SND_DT019X config SND_OPL3SA2 tristate "Yamaha OPL3-SA2/SA3" depends on SND + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for Yamaha OPL3SA2 or OPL3SA3 chips. config SND_SGALAXY tristate "Aztech Sound Galaxy" depends on SND + select SND_PCM help Say 'Y' or 'M' to include support for Aztech Sound Galaxy. config SND_SSCAPE tristate "Ensoniq SoundScape PnP driver" depends on SND + select SND_HWDEP + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for Ensoniq SoundScape PnP soundcard. --- linux-2.6.4-rc2/sound/isa/opti9xx/opti92x-ad1848.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/isa/opti9xx/opti92x-ad1848.c 2004-03-07 20:46:50.000000000 -0800 @@ -311,8 +311,12 @@ static char * snd_opti9xx_names[] = { static long snd_legacy_find_free_ioport(long *port_table, long size) { while (*port_table != -1) { - if (!check_region(*port_table, size)) + struct resource *res; + if ((res = request_region(*port_table, size, "ALSA test")) != NULL) { + release_resource(res); + kfree_nocheck(res); return *port_table; + } port_table++; } return -1; @@ -1399,7 +1403,9 @@ int snd_opti93x_pcm(opti93x_t *codec, in strcpy(pcm->name, snd_opti93x_chip_id(codec)); - snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, codec->dma1 > 3 || codec->dma2 > 3 ? 128*1024 : 64*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_isa_data(), + 64*1024, codec->dma1 > 3 || codec->dma2 > 3 ? 128*1024 : 64*1024); codec->pcm = pcm; if (rpcm) @@ -1672,13 +1678,18 @@ static int __devinit snd_card_opti9xx_de if ((err = snd_opti9xx_init(chip, i)) < 0) return err; - if (check_region(chip->mc_base, chip->mc_base_size)) + if ((chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, "OPTi9xx MC")) == NULL) continue; value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(1)); if ((value != 0xff) && (value != inb(chip->mc_base + 1))) if (value == snd_opti9xx_read(chip, OPTi9XX_MC_REG(1))) return 1; + + release_resource(chip->res_mc_base); + kfree_nocheck(chip->res_mc_base); + chip->res_mc_base = NULL; + } #else /* OPTi93X */ for (i = OPTi9XX_HW_82C931; i >= OPTi9XX_HW_82C930; i--) { @@ -1688,7 +1699,7 @@ static int __devinit snd_card_opti9xx_de if ((err = snd_opti9xx_init(chip, i)) < 0) return err; - if (check_region(chip->mc_base, chip->mc_base_size)) + if ((chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, "OPTi9xx MC")) == NULL) continue; spin_lock_irqsave(&chip->lock, flags); @@ -1701,6 +1712,10 @@ static int __devinit snd_card_opti9xx_de snd_opti9xx_write(chip, OPTi9XX_MC_REG(7), 0xff - value); if (snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)) == 0xff - value) return 1; + + release_resource(chip->res_mc_base); + kfree_nocheck(chip->res_mc_base); + chip->res_mc_base = NULL; } #endif /* OPTi93X */ @@ -1984,7 +1999,8 @@ static int __devinit snd_card_opti9xx_pr } #endif /* CONFIG_PNP */ - if ((chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, "OPTi9xx MC")) == NULL) { + if (! chip->res_mc_base && + (chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, "OPTi9xx MC")) == NULL) { snd_card_free(card); return -ENOMEM; } --- linux-2.6.4-rc2/sound/isa/sb/sb16_main.c 2003-06-14 12:18:09.000000000 -0700 +++ 25/sound/isa/sb/sb16_main.c 2004-03-07 20:46:50.000000000 -0800 @@ -881,7 +881,9 @@ int snd_sb16dsp_pcm(sb_t * chip, int dev else pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; - snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_isa_data(), + 64*1024, 128*1024); if (rpcm) *rpcm = pcm; --- linux-2.6.4-rc2/sound/isa/sb/sb8_main.c 2003-06-14 12:18:08.000000000 -0700 +++ 25/sound/isa/sb/sb8_main.c 2004-03-07 20:46:50.000000000 -0800 @@ -535,7 +535,9 @@ int snd_sb8dsp_pcm(sb_t *chip, int devic snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sb8_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sb8_capture_ops); - snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, 64*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_isa_data(), + 64*1024, 64*1024); if (rpcm) *rpcm = pcm; --- linux-2.6.4-rc2/sound/isa/sscape.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/isa/sscape.c 2004-03-07 20:46:50.000000000 -0800 @@ -168,23 +168,20 @@ static inline struct soundscape *get_hwd } -struct dmabuf { - size_t size; - unsigned char *data; - dma_addr_t addr; -}; - /* * Allocates some kernel memory that we can use for DMA. * I think this means that the memory has to map to * contiguous pages of physical memory. */ -static struct dmabuf *get_dmabuf(struct dmabuf *buf, unsigned long s) +static struct snd_dma_buffer *get_dmabuf(struct snd_dma_buffer *buf, unsigned long size) { if (buf) { - buf->data = snd_malloc_isa_pages_fallback(s, &buf->addr, &buf->size); - if (!buf->data) { - snd_printk(KERN_ERR "sscape: Failed to allocate %lu bytes for DMA\n", s); + struct snd_dma_device dev; + memset(&dev, 0, sizeof(dev)); + dev.type = SNDRV_DMA_TYPE_DEV; + dev.dev = snd_dma_isa_data(); + if (snd_dma_alloc_pages_fallback(&dev, size, buf) < 0) { + snd_printk(KERN_ERR "sscape: Failed to allocate %lu bytes for DMA\n", size); return NULL; } } @@ -195,10 +192,15 @@ static struct dmabuf *get_dmabuf(struct /* * Release the DMA-able kernel memory ... */ -static void free_dmabuf(struct dmabuf *buf) +static void free_dmabuf(struct snd_dma_buffer *buf) { - if (buf && buf->data) - snd_free_isa_pages(buf->size, buf->data, buf->addr); + if (buf && buf->area) { + struct snd_dma_device dev; + memset(&dev, 0, sizeof(dev)); + dev.type = SNDRV_DMA_TYPE_DEV; + dev.dev = snd_dma_isa_data(); + snd_dma_free_pages(&dev, buf); + } } @@ -456,7 +458,7 @@ static int upload_dma_data(struct sounds size_t size) { unsigned long flags; - struct dmabuf dma; + struct snd_dma_buffer dma; int ret; if (!get_dmabuf(&dma, PAGE_ALIGN(size))) @@ -500,8 +502,8 @@ static int upload_dma_data(struct sounds * comes from USERSPACE. We have already verified * the userspace pointer ... */ - len = min(size, dma.size); - __copy_from_user(dma.data, data, len); + len = min(size, dma.bytes); + __copy_from_user(dma.area, data, len); data += len; size -= len; --- linux-2.6.4-rc2/sound/isa/wavefront/wavefront_synth.c 2003-06-14 12:18:07.000000000 -0700 +++ 25/sound/isa/wavefront/wavefront_synth.c 2004-03-07 20:47:47.000000000 -0800 @@ -1913,11 +1913,11 @@ wavefront_reset_to_cleanliness (snd_wave return (1); } -#define __KERNEL_SYSCALLS__ #include #include #include #include +#include #include static int errno; @@ -1947,7 +1947,7 @@ wavefront_download_firmware (snd_wavefro fs = get_fs(); set_fs (get_ds()); - if ((fd = open (path, 0, 0)) < 0) { + if ((fd = sys_open (path, 0, 0)) < 0) { snd_printk ("Unable to load \"%s\".\n", path); return 1; @@ -1956,7 +1956,7 @@ wavefront_download_firmware (snd_wavefro while (1) { int x; - if ((x = read (fd, §ion_length, sizeof (section_length))) != + if ((x = sys_read (fd, §ion_length, sizeof (section_length))) != sizeof (section_length)) { snd_printk ("firmware read error.\n"); goto failure; @@ -1966,7 +1966,7 @@ wavefront_download_firmware (snd_wavefro break; } - if (read (fd, section, section_length) != section_length) { + if (sys_read (fd, section, section_length) != section_length) { snd_printk ("firmware section " "read error.\n"); goto failure; @@ -2005,12 +2005,12 @@ wavefront_download_firmware (snd_wavefro } - close (fd); + sys_close (fd); set_fs (fs); return 0; failure: - close (fd); + sys_close (fd); set_fs (fs); snd_printk ("firmware download failed!!!\n"); return 1; --- linux-2.6.4-rc2/sound/oss/ac97_plugin_ad1980.c 2003-10-08 15:07:10.000000000 -0700 +++ 25/sound/oss/ac97_plugin_ad1980.c 2004-03-07 20:46:46.000000000 -0800 @@ -123,3 +123,4 @@ static int ad1980_init(void) module_init(ad1980_init); module_exit(ad1980_exit); +MODULE_LICENSE("GPL"); --- linux-2.6.4-rc2/sound/oss/cs46xx_wrapper-24.h 2003-07-13 21:44:35.000000000 -0700 +++ 25/sound/oss/cs46xx_wrapper-24.h 2004-03-07 20:46:46.000000000 -0800 @@ -28,7 +28,7 @@ #include -#define CS_OWNER owner: +#define CS_OWNER .owner = #define CS_THIS_MODULE THIS_MODULE, void cs46xx_null(struct pci_dev *pcidev) { return; } #define cs4x_mem_map_reserve(page) SetPageReserved(page) --- linux-2.6.4-rc2/sound/oss/emu10k1/cardwi.c 2003-06-14 12:18:24.000000000 -0700 +++ 25/sound/oss/emu10k1/cardwi.c 2004-03-07 20:46:46.000000000 -0800 @@ -164,7 +164,6 @@ int emu10k1_wavein_open(struct emu10k1_w if (alloc_buffer(card, &wiinst->buffer) < 0) { ERROR(); - emu10k1_wavein_close(wave_dev); return -1; } --- linux-2.6.4-rc2/sound/oss/Makefile 2003-07-27 12:14:40.000000000 -0700 +++ 25/sound/oss/Makefile 2004-03-07 20:46:46.000000000 -0800 @@ -68,7 +68,7 @@ obj-$(CONFIG_SOUND_RME96XX) += rme96 obj-$(CONFIG_SOUND_BT878) += btaudio.o obj-$(CONFIG_SOUND_ALI5455) += ali5455.o ac97_codec.o obj-$(CONFIG_SOUND_IT8172) += ite8172.o ac97_codec.o -obj-$(CONFIG_SOUND_FORTE) += forte.o +obj-$(CONFIG_SOUND_FORTE) += forte.o ac97_codec.o obj-$(CONFIG_SOUND_AD1980) += ac97_plugin_ad1980.o obj-$(CONFIG_SOUND_WM97XX) += ac97_plugin_wm97xx.o --- linux-2.6.4-rc2/sound/oss/sb_audio.c 2003-06-14 12:18:30.000000000 -0700 +++ 25/sound/oss/sb_audio.c 2004-03-07 20:46:46.000000000 -0800 @@ -882,7 +882,7 @@ sb16_copy_from_user(int dev, c -= locallen; p += locallen; } /* used = ( samples * 16 bits size ) */ - *used = len << 1; + *used = max_in > ( max_out << 1) ? (max_out << 1) : max_in; /* returned = ( samples * 8 bits size ) */ *returned = len; } --- linux-2.6.4-rc2/sound/oss/wavfront.c 2003-06-14 12:18:22.000000000 -0700 +++ 25/sound/oss/wavfront.c 2004-03-07 20:47:47.000000000 -0800 @@ -2489,11 +2489,9 @@ static int __init detect_wavefront (int } #include "os.h" -#define __KERNEL_SYSCALLS__ #include #include #include -#include #include static int errno; @@ -2523,7 +2521,7 @@ wavefront_download_firmware (char *path) fs = get_fs(); set_fs (get_ds()); - if ((fd = open (path, 0, 0)) < 0) { + if ((fd = sys_open (path, 0, 0)) < 0) { printk (KERN_WARNING LOGNAME "Unable to load \"%s\".\n", path); return 1; @@ -2532,7 +2530,7 @@ wavefront_download_firmware (char *path) while (1) { int x; - if ((x = read (fd, §ion_length, sizeof (section_length))) != + if ((x = sys_read (fd, §ion_length, sizeof (section_length))) != sizeof (section_length)) { printk (KERN_ERR LOGNAME "firmware read error.\n"); goto failure; @@ -2542,7 +2540,7 @@ wavefront_download_firmware (char *path) break; } - if (read (fd, section, section_length) != section_length) { + if (sys_read (fd, section, section_length) != section_length) { printk (KERN_ERR LOGNAME "firmware section " "read error.\n"); goto failure; @@ -2581,12 +2579,12 @@ wavefront_download_firmware (char *path) } - close (fd); + sys_close (fd); set_fs (fs); return 0; failure: - close (fd); + sys_close (fd); set_fs (fs); printk (KERN_ERR "\nWaveFront: firmware download failed!!!\n"); return 1; --- linux-2.6.4-rc2/sound/parisc/harmony.c 2003-10-08 15:07:10.000000000 -0700 +++ 25/sound/parisc/harmony.c 2004-03-07 20:46:50.000000000 -0800 @@ -205,6 +205,7 @@ typedef struct snd_card_harmony { unsigned char *silence_addr; dma_addr_t silence_dma; int silence_count; + struct snd_dma_device dma_dev; /* alsa stuff */ snd_card_t *card; @@ -844,22 +845,27 @@ static int snd_card_harmony_pcm_init(snd harmony->pcm = pcm; /* initialize graveyard buffer */ - harmony->graveyard_addr = snd_malloc_pci_pages(harmony->fake_pci_dev, + harmony->dma_dev.type = SNDRV_DMA_TYPE_PCI; + harmony->dma_dev.dev = snd_dma_pci_data(harmony->fake_pci_dev); + harmony->graveyard_addr = snd_dma_alloc_pages(&chip->dma_dev, HARMONY_BUF_SIZE*GRAVEYARD_BUFS, &harmony->graveyard_dma); harmony->graveyard_count = 0; /* initialize silence buffers */ - harmony->silence_addr = snd_malloc_pci_pages(harmony->fake_pci_dev, + harmony->silence_addr = snd_dma_alloc_pages(&chip->dma_dev, HARMONY_BUF_SIZE*SILENCE_BUFS, &harmony->silence_dma); harmony->silence_count = 0; - harmony->ply_stopped = harmony->cap_stopped = 1; harmony->playback_substream = NULL; harmony->capture_substream = NULL; harmony->graveyard_count = 0; + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(harmony->fake_pci_dev), + 64 * 1024, 128 * 1024); + return 0; } --- linux-2.6.4-rc2/sound/parisc/Kconfig 2003-06-14 12:18:23.000000000 -0700 +++ 25/sound/parisc/Kconfig 2004-03-07 20:46:50.000000000 -0800 @@ -6,6 +6,7 @@ menu "ALSA PA-RISC devices" config SND_HARMONY tristate "Harmony/Vivace sound chip" depends on SND + select SND_PCM help Say 'Y' or 'M' to include support for Harmony/Vivace soundchip on HP712s, 715/new and many other GSC based machines. --- linux-2.6.4-rc2/sound/pci/ac97/ac97_codec.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/ac97/ac97_codec.c 2004-03-07 20:46:50.000000000 -0800 @@ -100,13 +100,12 @@ static const ac97_codec_id_t snd_ac97_co { 0x41445361, 0xffffffff, "AD1886", patch_ad1886, NULL }, { 0x41445362, 0xffffffff, "AD1887", patch_ad1881, NULL }, { 0x41445363, 0xffffffff, "AD1886A", patch_ad1881, NULL }, +{ 0x41445368, 0xffffffff, "AD1888", patch_ad1888, NULL }, { 0x41445370, 0xffffffff, "AD1980", patch_ad1980, NULL }, { 0x41445372, 0xffffffff, "AD1981A", patch_ad1981a, NULL }, { 0x41445374, 0xffffffff, "AD1981B", patch_ad1981b, NULL }, { 0x41445375, 0xffffffff, "AD1985", patch_ad1985, NULL }, -{ 0x414c4300, 0xfffffff0, "RL5306", NULL, NULL }, -{ 0x414c4310, 0xfffffff0, "RL5382", NULL, NULL }, -{ 0x414c4320, 0xfffffff0, "RL5383", NULL, NULL }, +{ 0x414c4300, 0xffffff00, "ALC100/100P", NULL, NULL }, { 0x414c4710, 0xfffffff0, "ALC200/200P", NULL, NULL }, { 0x414c4720, 0xfffffff0, "ALC650", patch_alc650, NULL }, { 0x414c4721, 0xfffffff0, "ALC650D", patch_alc650, NULL }, @@ -273,6 +272,11 @@ void snd_ac97_write(ac97_t *ac97, unsign { if (!snd_ac97_valid_reg(ac97, reg)) return; + if ((ac97->id & 0xffffff00) == 0x414c4300) { + /* Fix H/W bug of ALC100/100P */ + if (reg == AC97_MASTER || reg == AC97_HEADPHONE) + ac97->bus->write(ac97, AC97_RESET, 0); /* reset audio codec */ + } ac97->bus->write(ac97, reg, value); } @@ -688,7 +692,7 @@ AC97_DOUBLE("Surround Playback Volume", }; static const snd_kcontrol_new_t snd_ac97_control_eapd = -AC97_SINGLE("External Amplifier Power Down", AC97_POWERDOWN, 15, 1, 0); +AC97_SINGLE("External Amplifier", AC97_POWERDOWN, 15, 1, 1); static int snd_ac97_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) { @@ -1526,38 +1530,40 @@ static int snd_ac97_modem_build(snd_card return 0; } -static int snd_ac97_test_rate(ac97_t *ac97, int reg, int rate) +static int snd_ac97_test_rate(ac97_t *ac97, int reg, int shadow_reg, int rate) { unsigned short val; unsigned int tmp; tmp = ((unsigned int)rate * ac97->bus->clock) / 48000; snd_ac97_write_cache(ac97, reg, tmp & 0xffff); + if (shadow_reg) + snd_ac97_write_cache(ac97, shadow_reg, tmp & 0xffff); val = snd_ac97_read(ac97, reg); return val == (tmp & 0xffff); } -static void snd_ac97_determine_rates(ac97_t *ac97, int reg, unsigned int *r_result) +static void snd_ac97_determine_rates(ac97_t *ac97, int reg, int shadow_reg, unsigned int *r_result) { unsigned int result = 0; /* test a non-standard rate */ - if (snd_ac97_test_rate(ac97, reg, 11000)) + if (snd_ac97_test_rate(ac97, reg, shadow_reg, 11000)) result |= SNDRV_PCM_RATE_CONTINUOUS; /* let's try to obtain standard rates */ - if (snd_ac97_test_rate(ac97, reg, 8000)) + if (snd_ac97_test_rate(ac97, reg, shadow_reg, 8000)) result |= SNDRV_PCM_RATE_8000; - if (snd_ac97_test_rate(ac97, reg, 11025)) + if (snd_ac97_test_rate(ac97, reg, shadow_reg, 11025)) result |= SNDRV_PCM_RATE_11025; - if (snd_ac97_test_rate(ac97, reg, 16000)) + if (snd_ac97_test_rate(ac97, reg, shadow_reg, 16000)) result |= SNDRV_PCM_RATE_16000; - if (snd_ac97_test_rate(ac97, reg, 22050)) + if (snd_ac97_test_rate(ac97, reg, shadow_reg, 22050)) result |= SNDRV_PCM_RATE_22050; - if (snd_ac97_test_rate(ac97, reg, 32000)) + if (snd_ac97_test_rate(ac97, reg, shadow_reg, 32000)) result |= SNDRV_PCM_RATE_32000; - if (snd_ac97_test_rate(ac97, reg, 44100)) + if (snd_ac97_test_rate(ac97, reg, shadow_reg, 44100)) result |= SNDRV_PCM_RATE_44100; - if (snd_ac97_test_rate(ac97, reg, 48000)) + if (snd_ac97_test_rate(ac97, reg, shadow_reg, 48000)) result |= SNDRV_PCM_RATE_48000; *r_result = result; } @@ -1866,8 +1872,8 @@ int snd_ac97_mixer(ac97_bus_t * bus, ac9 if (ac97->ext_id & 0x0189) /* L/R, MIC, SDAC, LDAC VRA support */ snd_ac97_write_cache(ac97, AC97_EXTENDED_STATUS, ac97->ext_id & 0x0189); if (ac97->ext_id & AC97_EI_VRA) { /* VRA support */ - snd_ac97_determine_rates(ac97, AC97_PCM_FRONT_DAC_RATE, &ac97->rates[AC97_RATES_FRONT_DAC]); - snd_ac97_determine_rates(ac97, AC97_PCM_LR_ADC_RATE, &ac97->rates[AC97_RATES_ADC]); + snd_ac97_determine_rates(ac97, AC97_PCM_FRONT_DAC_RATE, 0, &ac97->rates[AC97_RATES_FRONT_DAC]); + snd_ac97_determine_rates(ac97, AC97_PCM_LR_ADC_RATE, 0, &ac97->rates[AC97_RATES_ADC]); } else { ac97->rates[AC97_RATES_FRONT_DAC] = SNDRV_PCM_RATE_48000; ac97->rates[AC97_RATES_ADC] = SNDRV_PCM_RATE_48000; @@ -1884,16 +1890,16 @@ int snd_ac97_mixer(ac97_bus_t * bus, ac9 SNDRV_PCM_RATE_32000; } if (ac97->ext_id & AC97_EI_VRM) { /* MIC VRA support */ - snd_ac97_determine_rates(ac97, AC97_PCM_MIC_ADC_RATE, &ac97->rates[AC97_RATES_MIC_ADC]); + snd_ac97_determine_rates(ac97, AC97_PCM_MIC_ADC_RATE, 0, &ac97->rates[AC97_RATES_MIC_ADC]); } else { ac97->rates[AC97_RATES_MIC_ADC] = SNDRV_PCM_RATE_48000; } if (ac97->ext_id & AC97_EI_SDAC) { /* SDAC support */ - snd_ac97_determine_rates(ac97, AC97_PCM_SURR_DAC_RATE, &ac97->rates[AC97_RATES_SURR_DAC]); + snd_ac97_determine_rates(ac97, AC97_PCM_SURR_DAC_RATE, AC97_PCM_FRONT_DAC_RATE, &ac97->rates[AC97_RATES_SURR_DAC]); ac97->scaps |= AC97_SCAP_SURROUND_DAC; } if (ac97->ext_id & AC97_EI_LDAC) { /* LDAC support */ - snd_ac97_determine_rates(ac97, AC97_PCM_LFE_DAC_RATE, &ac97->rates[AC97_RATES_LFE_DAC]); + snd_ac97_determine_rates(ac97, AC97_PCM_LFE_DAC_RATE, AC97_PCM_FRONT_DAC_RATE, &ac97->rates[AC97_RATES_LFE_DAC]); ac97->scaps |= AC97_SCAP_CENTER_LFE_DAC; } /* additional initializations */ @@ -1937,6 +1943,15 @@ int snd_ac97_mixer(ac97_bus_t * bus, ac9 return -ENOMEM; } } + /* make sure the proper powerdown bits are cleared */ + if (ac97->scaps) { + reg = snd_ac97_read(ac97, AC97_EXTENDED_ID); + if (ac97->scaps & AC97_SCAP_SURROUND_DAC) + reg &= ~AC97_EA_PRJ; + if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC) + reg &= ~(AC97_EA_PRI | AC97_EA_PRK); + snd_ac97_write_cache(ac97, AC97_EXTENDED_ID, reg); + } snd_ac97_proc_init(ac97); if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ac97, &ops)) < 0) { snd_ac97_free(ac97); @@ -1991,11 +2006,18 @@ void snd_ac97_resume(ac97_t *ac97) snd_ac97_write(ac97, AC97_GENERAL_PURPOSE, 0); snd_ac97_write(ac97, AC97_POWERDOWN, ac97->regs[AC97_POWERDOWN]); - snd_ac97_write(ac97, AC97_MASTER, 0x8101); + ac97->bus->write(ac97, AC97_MASTER, 0x8101); for (i = 0; i < 10; i++) { if (snd_ac97_read(ac97, AC97_MASTER) == 0x8101) break; - mdelay(1); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } + /* FIXME: extra delay */ + ac97->bus->write(ac97, AC97_MASTER, 0x8000); + if (snd_ac97_read(ac97, AC97_MASTER) != 0x8000) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/4); } __reset_ready: @@ -2112,6 +2134,8 @@ static int swap_headphone(ac97_t *ac97, { /* FIXME: error checks.. */ if (remove_master) { + if (ctl_find(ac97, "Headphone Playback Switch") == NULL) + return 0; snd_ac97_remove_ctl(ac97, "Master Playback Switch"); snd_ac97_remove_ctl(ac97, "Master Playback Volume"); } else { @@ -2134,12 +2158,30 @@ static int swap_surround(ac97_t *ac97) static int tune_ad_sharing(ac97_t *ac97) { unsigned short scfg; + if ((ac97->id & 0xffffff00) != 0x41445300) { + snd_printk(KERN_ERR "ac97_quirk AD_SHARING is only for AD codecs\n"); + return -EINVAL; + } /* Turn on OMS bit to route microphone to back panel */ scfg = snd_ac97_read(ac97, AC97_AD_SERIAL_CFG); snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, scfg | 0x0200); return 0; } +static const snd_kcontrol_new_t snd_ac97_alc_jack_detect = +AC97_SINGLE("Jack Detect", AC97_ALC650_CLOCK, 5, 1, 0); + +static int tune_alc_jack(ac97_t *ac97) +{ + if ((ac97->id & 0xffffff00) != 0x414c4700) { + snd_printk(KERN_ERR "ac97_quirk ALC_JACK is only for Realtek codecs\n"); + return -EINVAL; + } + snd_ac97_update_bits(ac97, 0x7a, 0x20, 0x20); /* select jack detect function */ + snd_ac97_update_bits(ac97, 0x7a, 0x01, 0x01); /* Line-out auto mute */ + return snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&snd_ac97_alc_jack_detect, ac97)); +} + static int apply_quirk(ac97_t *ac97, int quirk) { switch (quirk) { @@ -2153,6 +2195,8 @@ static int apply_quirk(ac97_t *ac97, int return swap_surround(ac97); case AC97_TUNE_AD_SHARING: return tune_ad_sharing(ac97); + case AC97_TUNE_ALC_JACK: + return tune_alc_jack(ac97); } return -EINVAL; } --- linux-2.6.4-rc2/sound/pci/ac97/ac97_patch.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/ac97/ac97_patch.c 2004-03-07 20:46:50.000000000 -0800 @@ -292,6 +292,9 @@ int patch_wolfson11(ac97_t * ac97) return 0; } +/* + * Tritech codec + */ int patch_tritech_tr28028(ac97_t * ac97) { snd_ac97_write_cache(ac97, 0x26, 0x0300); @@ -301,6 +304,9 @@ int patch_tritech_tr28028(ac97_t * ac97) return 0; } +/* + * Sigmatel STAC97xx codecs + */ static int patch_sigmatel_stac9700_3d(ac97_t * ac97) { snd_kcontrol_t *kctl; @@ -441,6 +447,9 @@ int patch_sigmatel_stac9756(ac97_t * ac9 return 0; } +/* + * Cirrus Logic CS42xx codecs + */ static const snd_kcontrol_new_t snd_ac97_cirrus_controls_spdif[2] = { AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), AC97_CSR_SPDIF, 15, 1, 0), AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "AC97-SPSA", AC97_CSR_ACMODE, 0, 3, 0) @@ -499,6 +508,9 @@ int patch_cirrus_cs4299(ac97_t * ac97) return patch_cirrus_spdif(ac97); } +/* + * Conexant codecs + */ static const snd_kcontrol_new_t snd_ac97_conexant_controls_spdif[1] = { AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), AC97_CXR_AUDIO_MISC, 3, 1, 0), }; @@ -530,6 +542,9 @@ int patch_conexant(ac97_t * ac97) return 0; } +/* + * Analog Device AD18xx, AD19xx codecs + */ int patch_ad1819(ac97_t * ac97) { // patch for Analog Devices @@ -652,7 +667,7 @@ int patch_ad1881(ac97_t * ac97) static const snd_kcontrol_new_t snd_ac97_controls_ad1885[] = { AC97_SINGLE("Digital Mono Direct", AC97_AD_MISC, 11, 1, 0), - AC97_SINGLE("Digital Audio Mode", AC97_AD_MISC, 12, 1, 0), + /* AC97_SINGLE("Digital Audio Mode", AC97_AD_MISC, 12, 1, 0), */ /* seems problematic */ AC97_SINGLE("Low Power Mixer", AC97_AD_MISC, 14, 1, 0), AC97_SINGLE("Zero Fill DAC", AC97_AD_MISC, 15, 1, 0), }; @@ -682,6 +697,9 @@ int patch_ad1885(ac97_t * ac97) jack = snd_ac97_read(ac97, AC97_AD_JACK_SPDIF); snd_ac97_write_cache(ac97, AC97_AD_JACK_SPDIF, jack | 0x0300); + /* set default */ + snd_ac97_write_cache(ac97, AC97_AD_MISC, 0x0404); + ac97->build_ops = &patch_ad1885_build_ops; return 0; } @@ -799,7 +817,7 @@ int patch_ad1981b(ac97_t *ac97) return 0; } -static int snd_ac97_ad1980_lohpsel_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +static int snd_ac97_ad1888_lohpsel_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; uinfo->count = 1; @@ -808,7 +826,7 @@ static int snd_ac97_ad1980_lohpsel_info( return 0; } -static int snd_ac97_ad1980_lohpsel_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t* ucontrol) +static int snd_ac97_ad1888_lohpsel_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t* ucontrol) { ac97_t *ac97 = snd_kcontrol_chip(kcontrol); unsigned short val; @@ -818,7 +836,7 @@ static int snd_ac97_ad1980_lohpsel_get(s return 0; } -static int snd_ac97_ad1980_lohpsel_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int snd_ac97_ad1888_lohpsel_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) { ac97_t *ac97 = snd_kcontrol_chip(kcontrol); unsigned short val; @@ -829,7 +847,7 @@ static int snd_ac97_ad1980_lohpsel_put(s AC97_AD198X_LOSEL | AC97_AD198X_HPSEL, val); } -static int snd_ac97_ad1980_downmix_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +static int snd_ac97_ad1888_downmix_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) { static char *texts[3] = {"Off", "6 -> 4", "6 -> 2"}; @@ -842,7 +860,7 @@ static int snd_ac97_ad1980_downmix_info( return 0; } -static int snd_ac97_ad1980_downmix_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t* ucontrol) +static int snd_ac97_ad1888_downmix_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t* ucontrol) { ac97_t *ac97 = snd_kcontrol_chip(kcontrol); unsigned short val; @@ -855,7 +873,7 @@ static int snd_ac97_ad1980_downmix_get(s return 0; } -static int snd_ac97_ad1980_downmix_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int snd_ac97_ad1888_downmix_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) { ac97_t *ac97 = snd_kcontrol_chip(kcontrol); unsigned short val; @@ -871,51 +889,47 @@ static int snd_ac97_ad1980_downmix_put(s AC97_AD198X_DMIX0 | AC97_AD198X_DMIX1, val); } -static const snd_kcontrol_new_t snd_ac97_ad1980_controls[] = { +static const snd_kcontrol_new_t snd_ac97_ad1888_controls[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Exchange Front/Surround", - .info = snd_ac97_ad1980_lohpsel_info, - .get = snd_ac97_ad1980_lohpsel_get, - .put = snd_ac97_ad1980_lohpsel_put + .info = snd_ac97_ad1888_lohpsel_info, + .get = snd_ac97_ad1888_lohpsel_get, + .put = snd_ac97_ad1888_lohpsel_put }, AC97_SINGLE("Spread Front to Surround and Center/LFE", AC97_AD_MISC, 7, 1, 0), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Downmix", - .info = snd_ac97_ad1980_downmix_info, - .get = snd_ac97_ad1980_downmix_get, - .put = snd_ac97_ad1980_downmix_put + .info = snd_ac97_ad1888_downmix_info, + .get = snd_ac97_ad1888_downmix_get, + .put = snd_ac97_ad1888_downmix_put }, AC97_SINGLE("Surround Jack as Input", AC97_AD_MISC, 12, 1, 0), AC97_SINGLE("Center/LFE Jack as Input", AC97_AD_MISC, 11, 1, 0), }; -static int patch_ad1980_specific(ac97_t *ac97) +static int patch_ad1888_specific(ac97_t *ac97) { - int err; - /* rename 0x04 as "Master" and 0x02 as "Master Surround" */ snd_ac97_rename_ctl(ac97, "Master Playback Switch", "Master Surround Playback Switch"); snd_ac97_rename_ctl(ac97, "Master Playback Volume", "Master Surround Playback Volume"); snd_ac97_rename_ctl(ac97, "Headphone Playback Switch", "Master Playback Switch"); snd_ac97_rename_ctl(ac97, "Headphone Playback Volume", "Master Playback Volume"); - if ((err = patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1)) < 0) - return err; - return patch_build_controls(ac97, snd_ac97_ad1980_controls, ARRAY_SIZE(snd_ac97_ad1980_controls)); + return patch_build_controls(ac97, snd_ac97_ad1888_controls, ARRAY_SIZE(snd_ac97_ad1888_controls)); } -static struct snd_ac97_build_ops patch_ad1980_build_ops = { +static struct snd_ac97_build_ops patch_ad1888_build_ops = { .build_post_spdif = patch_ad198x_post_spdif, - .build_specific = patch_ad1980_specific + .build_specific = patch_ad1888_specific }; -int patch_ad1980(ac97_t * ac97) +int patch_ad1888(ac97_t * ac97) { unsigned short misc; patch_ad1881(ac97); - ac97->build_ops = &patch_ad1980_build_ops; + ac97->build_ops = &patch_ad1888_build_ops; /* Switch FRONT/SURROUND LINE-OUT/HP-OUT default connection */ /* it seems that most vendors connect line-out connector to headphone out of AC'97 */ /* AD-compatible mode */ @@ -930,6 +944,27 @@ int patch_ad1980(ac97_t * ac97) return 0; } +static int patch_ad1980_specific(ac97_t *ac97) +{ + int err; + + if ((err = patch_ad1888_specific(ac97)) < 0) + return err; + return patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1); +} + +static struct snd_ac97_build_ops patch_ad1980_build_ops = { + .build_post_spdif = patch_ad198x_post_spdif, + .build_specific = patch_ad1980_specific +}; + +int patch_ad1980(ac97_t * ac97) +{ + patch_ad1888(ac97); + ac97->build_ops = &patch_ad1980_build_ops; + return 0; +} + static const snd_kcontrol_new_t snd_ac97_ad1985_controls[] = { AC97_SINGLE("Center/LFE Jack as Mic", AC97_AD_SERIAL_CFG, 9, 1, 0), AC97_SINGLE("Exchange Center/LFE", AC97_AD_SERIAL_CFG, 3, 1, 0) @@ -972,6 +1007,36 @@ int patch_ad1985(ac97_t * ac97) return 0; } +/* + * realtek ALC65x codecs + */ +static int snd_ac97_alc650_mic_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = (ac97->regs[AC97_ALC650_MULTICH] >> 10) & 1; + return 0; +} + +static int snd_ac97_alc650_mic_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + int change, val; + val = !!(snd_ac97_read(ac97, AC97_ALC650_MULTICH) & (1 << 10)); + change = (ucontrol->value.integer.value[0] != val); + if (change) { + /* disable/enable vref */ + snd_ac97_update_bits(ac97, AC97_ALC650_CLOCK, 1 << 12, + ucontrol->value.integer.value[0] ? (1 << 12) : 0); + /* turn on/off center-on-mic */ + snd_ac97_update_bits(ac97, AC97_ALC650_MULTICH, 1 << 10, + ucontrol->value.integer.value[0] ? (1 << 10) : 0); + /* GPIO0 high for mic */ + snd_ac97_update_bits(ac97, AC97_ALC650_GPIO_STATUS, 0x100, + ucontrol->value.integer.value[0] ? 0 : 0x100); + } + return change; +} + static const snd_kcontrol_new_t snd_ac97_controls_alc650[] = { AC97_SINGLE("Duplicate Front", AC97_ALC650_MULTICH, 0, 1, 0), AC97_SINGLE("Surround Down Mix", AC97_ALC650_MULTICH, 1, 1, 0), @@ -994,42 +1059,13 @@ static const snd_kcontrol_new_t snd_ac97 AC97_SINGLE("Center/LFE DAC Switch", AC97_ALC650_LFE_DAC_VOL, 15, 1, 1), AC97_DOUBLE("Center/LFE DAC Volume", AC97_ALC650_LFE_DAC_VOL, 8, 0, 31, 1), #endif -}; - -static const snd_kcontrol_new_t snd_ac97_control_alc650_mic = -AC97_SINGLE("Mic As Center/LFE", AC97_ALC650_MULTICH, 10, 1, 0); - -static int snd_ac97_alc650_mic_gpio_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol) -{ - ac97_t *ac97 = snd_kcontrol_chip(kcontrol); - ucontrol->value.integer.value[0] = (ac97->regs[AC97_ALC650_MULTICH] >> 10) & 1; - return 0; -} - -static int snd_ac97_alc650_mic_gpio_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol) -{ - ac97_t *ac97 = snd_kcontrol_chip(kcontrol); - int change; - change = snd_ac97_update_bits(ac97, AC97_ALC650_MULTICH, 1 << 10, - ucontrol->value.integer.value[0] ? (1 << 10) : 0); - if (change) { - /* GPIO0 write for mic */ - snd_ac97_update_bits(ac97, 0x76, 0x01, - ucontrol->value.integer.value[0] ? 0 : 0x01); - /* GPIO0 high for mic */ - snd_ac97_update_bits(ac97, 0x78, 0x100, - ucontrol->value.integer.value[0] ? 0 : 0x100); - } - return change; -} - -static const snd_kcontrol_new_t snd_ac97_control_alc650_mic_gpio = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Mic As Center/LFE", - .info = snd_ac97_info_single, - .get = snd_ac97_alc650_mic_gpio_get, - .put = snd_ac97_alc650_mic_gpio_put, - .private_value = (1 << 16), /* for info */ + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Mic As Center/LFE", + .info = snd_ac97_info_single, + .get = snd_ac97_alc650_mic_get, + .put = snd_ac97_alc650_mic_put, + }, }; static const snd_kcontrol_new_t snd_ac97_spdif_controls_alc650[] = { @@ -1044,11 +1080,6 @@ static int patch_alc650_specific(ac97_t if ((err = patch_build_controls(ac97, snd_ac97_controls_alc650, ARRAY_SIZE(snd_ac97_controls_alc650))) < 0) return err; - if ((err = patch_build_controls(ac97, - ac97->spec.dev_flags ? - &snd_ac97_control_alc650_mic : - &snd_ac97_control_alc650_mic_gpio, 1)) < 0) - return err; if (ac97->ext_id & AC97_EI_SPDIF) { if ((err = patch_build_controls(ac97, snd_ac97_spdif_controls_alc650, ARRAY_SIZE(snd_ac97_spdif_controls_alc650))) < 0) return err; @@ -1076,34 +1107,25 @@ int patch_alc650(ac97_t * ac97) snd_ac97_read(ac97, AC97_ALC650_GPIO_STATUS) | 0x8000); /* Enable SPDIF-IN only on Rev.E and above */ - if (ac97->spec.dev_flags) { - /* enable spdif in */ - snd_ac97_write_cache(ac97, AC97_ALC650_CLOCK, - snd_ac97_read(ac97, AC97_ALC650_CLOCK) | 0x03); - } + val = snd_ac97_read(ac97, AC97_ALC650_CLOCK); + /* SPDIF IN with pin 47 */ + if (ac97->spec.dev_flags) + val |= 0x03; /* enable */ + else + val &= ~0x03; /* disable */ + snd_ac97_write_cache(ac97, AC97_ALC650_CLOCK, val); val = snd_ac97_read(ac97, AC97_ALC650_MULTICH); val &= ~0xc000; /* slot: 3,4,7,8,6,9 */ + val &= ~(1 << 10); /* center-on-mic off */ snd_ac97_write_cache(ac97, AC97_ALC650_MULTICH, val); - if (! ac97->spec.dev_flags) { - /* set GPIO */ - int mic_off; - mic_off = snd_ac97_read(ac97, AC97_ALC650_MULTICH) & (1 << 10); - /* GPIO0 direction */ - val = snd_ac97_read(ac97, AC97_ALC650_GPIO_SETUP); - if (mic_off) - val &= ~0x01; - else - val |= 0x01; - snd_ac97_write_cache(ac97, AC97_ALC650_GPIO_SETUP, val); - val = snd_ac97_read(ac97, AC97_ALC650_GPIO_STATUS); - if (mic_off) - val &= ~0x100; - else - val = val | 0x100; - snd_ac97_write_cache(ac97, AC97_ALC650_GPIO_STATUS, val); - } + /* set GPIO0 for mic bias */ + /* GPIO0 pin output, no interrupt, high */ + snd_ac97_write_cache(ac97, AC97_ALC650_GPIO_SETUP, + snd_ac97_read(ac97, AC97_ALC650_GPIO_SETUP) | 0x01); + snd_ac97_write_cache(ac97, AC97_ALC650_GPIO_STATUS, + (snd_ac97_read(ac97, AC97_ALC650_GPIO_STATUS) | 0x100) & ~0x10); /* full DAC volume */ snd_ac97_write_cache(ac97, AC97_ALC650_SURR_DAC_VOL, 0x0808); @@ -1111,10 +1133,37 @@ int patch_alc650(ac97_t * ac97) return 0; } +static int snd_ac97_alc655_mic_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = (ac97->regs[AC97_ALC650_MULTICH] >> 10) & 1; + return 0; +} + +static int snd_ac97_alc655_mic_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + int change; + + /* misc control; vrefout disable */ + snd_ac97_update_bits(ac97, AC97_ALC650_CLOCK, 1 << 12, + ucontrol->value.integer.value[0] ? (1 << 12) : 0); + change = snd_ac97_update_bits(ac97, AC97_ALC650_MULTICH, 1 << 10, + ucontrol->value.integer.value[0] ? (1 << 10) : 0); + return change; +} + + static const snd_kcontrol_new_t snd_ac97_controls_alc655[] = { AC97_SINGLE("Duplicate Front", AC97_ALC650_MULTICH, 0, 1, 0), AC97_SINGLE("Line-In As Surround", AC97_ALC650_MULTICH, 9, 1, 0), - AC97_SINGLE("Mic As Center/LFE", AC97_ALC650_MULTICH, 10, 1, 0), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Mic As Center/LFE", + .info = snd_ac97_info_single, + .get = snd_ac97_alc655_mic_get, + .put = snd_ac97_alc655_mic_put, + }, }; static int alc655_iec958_route_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) @@ -1187,15 +1236,21 @@ static struct snd_ac97_build_ops patch_a int patch_alc655(ac97_t * ac97) { + unsigned int val; + ac97->spec.dev_flags = (ac97->id == 0x414c4780); /* ALC658 */ ac97->build_ops = &patch_alc655_ops; - /* enable spdif in */ - snd_ac97_write_cache(ac97, AC97_ALC650_CLOCK, - snd_ac97_read(ac97, AC97_ALC650_MULTICH) | 0x8000); - snd_ac97_write_cache(ac97, AC97_ALC650_CLOCK, - snd_ac97_read(ac97, AC97_ALC650_CLOCK) | 0x02); + /* adjust default values */ + val = snd_ac97_read(ac97, 0x7a); /* misc control */ + val |= (1 << 1); /* spdif input pin */ + val &= ~(1 << 12); /* vref enable */ + snd_ac97_write_cache(ac97, 0x7a, val); + val = snd_ac97_read(ac97, AC97_ALC650_MULTICH); + val |= (1 << 15); /* enable spdif in */ + val &= ~(1 << 10); /* disable center on mic */ + snd_ac97_write_cache(ac97, AC97_ALC650_MULTICH, val); /* full DAC volume */ snd_ac97_write_cache(ac97, AC97_ALC650_SURR_DAC_VOL, 0x0808); @@ -1203,6 +1258,9 @@ int patch_alc655(ac97_t * ac97) return 0; } +/* + * C-Media CM97xx codecs + */ static const snd_kcontrol_new_t snd_ac97_cm9738_controls[] = { AC97_SINGLE("Line-In As Surround", AC97_CM9738_VENDOR_CTRL, 10, 1, 0), AC97_SINGLE("Duplicate Front", AC97_CM9738_VENDOR_CTRL, 13, 1, 0), @@ -1312,7 +1370,10 @@ int patch_cm9739(ac97_t * ac97) /* bit 13: enable internal vref output for mic */ /* bit 12: enable center/lfe */ /* bit 14: 0 = SPDIF, 1 = EAPD */ - snd_ac97_write_cache(ac97, AC97_CM9739_MULTI_CHAN, 0x3000); + val = (1 << 12) | (1 << 13); + if (! (ac97->ext_id & AC97_EI_SPDIF)) + val |= (1 << 14); + snd_ac97_write_cache(ac97, AC97_CM9739_MULTI_CHAN, val); /* FIXME: set up GPIO */ snd_ac97_write_cache(ac97, 0x70, 0x0100); @@ -1321,6 +1382,9 @@ int patch_cm9739(ac97_t * ac97) return 0; } +/* + * VIA VT1616 codec + */ static const snd_kcontrol_new_t snd_ac97_controls_vt1616[] = { AC97_SINGLE("DC Offset removal", 0x5a, 10, 1, 0), AC97_SINGLE("Alternate Level to Surround Out", 0x5a, 15, 1, 0), --- linux-2.6.4-rc2/sound/pci/ac97/ac97_patch.h 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/ac97/ac97_patch.h 2004-03-07 20:46:50.000000000 -0800 @@ -41,6 +41,7 @@ int patch_ad1819(ac97_t * ac97); int patch_ad1881(ac97_t * ac97); int patch_ad1885(ac97_t * ac97); int patch_ad1886(ac97_t * ac97); +int patch_ad1888(ac97_t * ac97); int patch_ad1980(ac97_t * ac97); int patch_ad1981a(ac97_t * ac97); int patch_ad1981b(ac97_t * ac97); --- linux-2.6.4-rc2/sound/pci/ac97/ac97_pcm.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/ac97/ac97_pcm.c 2004-03-07 20:46:50.000000000 -0800 @@ -31,6 +31,7 @@ #include #include #include +#include #include "ac97_patch.h" #include "ac97_id.h" #include "ac97_local.h" @@ -176,6 +177,7 @@ static unsigned char get_slot_reg(struct static int set_spdif_rate(ac97_t *ac97, unsigned short rate) { unsigned short old, bits, reg, mask; + unsigned int sbits; if (! (ac97->ext_id & AC97_EI_SPDIF)) return -ENODEV; @@ -213,6 +215,26 @@ static int set_spdif_rate(ac97_t *ac97, if (old != bits) { snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0); snd_ac97_update_bits(ac97, reg, mask, bits); + /* update the internal spdif bits */ + spin_lock(&ac97->reg_lock); + sbits = ac97->spdif_status; + if (sbits & IEC958_AES0_PROFESSIONAL) { + sbits &= ~IEC958_AES0_PRO_FS; + switch (rate) { + case 44100: sbits |= IEC958_AES0_PRO_FS_44100; break; + case 48000: sbits |= IEC958_AES0_PRO_FS_48000; break; + case 32000: sbits |= IEC958_AES0_PRO_FS_32000; break; + } + } else { + sbits &= ~(IEC958_AES3_CON_FS << 24); + switch (rate) { + case 44100: sbits |= IEC958_AES3_CON_FS_44100<<24; break; + case 48000: sbits |= IEC958_AES3_CON_FS_48000<<24; break; + case 32000: sbits |= IEC958_AES3_CON_FS_32000<<24; break; + } + } + ac97->spdif_status = sbits; + spin_unlock(&ac97->reg_lock); } snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, AC97_EA_SPDIF); return 0; --- linux-2.6.4-rc2/sound/pci/ac97/ak4531_codec.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/ac97/ak4531_codec.c 2004-03-07 20:46:50.000000000 -0800 @@ -285,7 +285,7 @@ AK4531_INPUT_SW("Line Capture Route", 0, AK4531_DOUBLE("Aux Switch", 0, AK4531_LAUXA, AK4531_RAUXA, 7, 7, 1, 1), AK4531_DOUBLE("Aux Volume", 0, AK4531_LAUXA, AK4531_RAUXA, 0, 0, 0x1f, 1), AK4531_DOUBLE("Aux Playback Switch", 0, AK4531_OUT_SW2, AK4531_OUT_SW2, 5, 4, 1, 0), -AK4531_INPUT_SW("Aux Input Route", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 4, 3), +AK4531_INPUT_SW("Aux Capture Route", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 4, 3), AK4531_SINGLE("Mono Switch", 0, AK4531_MONO1, 7, 1, 1), AK4531_SINGLE("Mono Volume", 0, AK4531_MONO1, 0, 0x1f, 1), --- linux-2.6.4-rc2/sound/pci/ac97/Makefile 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/ac97/Makefile 2004-03-07 20:46:50.000000000 -0800 @@ -7,21 +7,7 @@ snd-ac97-codec-objs := ac97_codec.o ac97 snd-ak4531-codec-objs := ak4531_codec.o # Toplevel Module Dependency -obj-$(CONFIG_SND_CS4281) += snd-ac97-codec.o +obj-$(CONFIG_SND_AC97_CODEC) += snd-ac97-codec.o obj-$(CONFIG_SND_ENS1370) += snd-ak4531-codec.o -obj-$(CONFIG_SND_ENS1371) += snd-ac97-codec.o -obj-$(CONFIG_SND_ES1968) += snd-ac97-codec.o -obj-$(CONFIG_SND_FM801) += snd-ac97-codec.o -obj-$(CONFIG_SND_ICE1712) += snd-ac97-codec.o -obj-$(CONFIG_SND_ICE1724) += snd-ac97-codec.o -obj-$(CONFIG_SND_INTEL8X0) += snd-ac97-codec.o -obj-$(CONFIG_SND_MAESTRO3) += snd-ac97-codec.o -obj-$(CONFIG_SND_VIA82XX) += snd-ac97-codec.o -obj-$(CONFIG_SND_ALI5451) += snd-ac97-codec.o -obj-$(CONFIG_SND_CS46XX) += snd-ac97-codec.o -obj-$(CONFIG_SND_EMU10K1) += snd-ac97-codec.o -obj-$(CONFIG_SND_NM256) += snd-ac97-codec.o -obj-$(CONFIG_SND_TRIDENT) += snd-ac97-codec.o -obj-$(CONFIG_SND_YMFPCI) += snd-ac97-codec.o obj-m := $(sort $(obj-m)) --- linux-2.6.4-rc2/sound/pci/ali5451/ali5451.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/ali5451/ali5451.c 2004-03-07 20:46:50.000000000 -0800 @@ -1742,7 +1742,8 @@ static int __devinit snd_ali_pcm(ali_t * snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ali_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ali_capture_ops); - snd_pcm_lib_preallocate_pci_pages_for_all(codec->pci, pcm, 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(codec->pci), 64*1024, 128*1024); pcm->info_flags = 0; pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; --- linux-2.6.4-rc2/sound/pci/als4000.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/als4000.c 2004-03-07 20:46:50.000000000 -0800 @@ -99,9 +99,11 @@ MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); MODULE_PARM_DESC(enable, "Enable ALS4000 soundcard."); MODULE_PARM_SYNTAX(enable, SNDRV_INDEX_DESC); +#ifdef SUPPORT_JOYSTICK MODULE_PARM(joystick_port, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); MODULE_PARM_DESC(joystick_port, "Joystick port address for ALS4000 soundcard. (0 = disabled)"); MODULE_PARM_SYNTAX(joystick_port, SNDRV_ENABLED); +#endif #define chip_t sb_t @@ -521,7 +523,8 @@ static int __devinit snd_als4000_pcm(sb_ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_als4000_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_als4000_capture_ops); - snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 64*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), + 64*1024, 64*1024); chip->pcm = pcm; --- linux-2.6.4-rc2/sound/pci/azt3328.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/azt3328.c 2004-03-07 20:46:50.000000000 -0800 @@ -1253,7 +1253,8 @@ static int __devinit snd_azf3328_pcm(azf strcpy(pcm->name, chip->card->shortname); chip->pcm = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 64*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), 64*1024, 64*1024); snd_azf3328_dbgcallleave(); return 0; --- linux-2.6.4-rc2/sound/pci/bt87x.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/bt87x.c 2004-03-07 20:46:50.000000000 -0800 @@ -167,8 +167,8 @@ struct snd_bt87x { long opened; snd_pcm_substream_t *substream; - u32 *risc; - dma_addr_t risc_dma; + struct snd_dma_device dma_dev; + struct snd_dma_buffer dma_risc; unsigned int line_bytes; unsigned int lines; @@ -195,13 +195,14 @@ static int snd_bt87x_create_risc(bt87x_t unsigned int i, offset; u32 *risc; - if (!chip->risc) { - chip->risc = (u32*)snd_malloc_pci_pages - (chip->pci, PAGE_ALIGN(MAX_RISC_SIZE), &chip->risc_dma); - if (!chip->risc) + if (chip->dma_risc.area == NULL) { + memset(&chip->dma_dev, 0, sizeof(chip->dma_dev)); + chip->dma_dev.type = SNDRV_DMA_TYPE_DEV; + chip->dma_dev.dev = snd_dma_pci_data(chip->pci); + if (snd_dma_alloc_pages(&chip->dma_dev, PAGE_ALIGN(MAX_RISC_SIZE), &chip->dma_risc) < 0) return -ENOMEM; } - risc = chip->risc; + risc = (u32 *)chip->dma_risc.area; offset = 0; *risc++ = cpu_to_le32(RISC_SYNC | RISC_SYNC_FM1); *risc++ = cpu_to_le32(0); @@ -233,7 +234,7 @@ static int snd_bt87x_create_risc(bt87x_t *risc++ = cpu_to_le32(RISC_SYNC | RISC_SYNC_VRO); *risc++ = cpu_to_le32(0); *risc++ = cpu_to_le32(RISC_JUMP); - *risc++ = cpu_to_le32(chip->risc_dma); + *risc++ = cpu_to_le32(chip->dma_risc.addr); chip->line_bytes = period_bytes; chip->lines = periods; return 0; @@ -241,10 +242,9 @@ static int snd_bt87x_create_risc(bt87x_t static void snd_bt87x_free_risc(bt87x_t *chip) { - if (chip->risc) { - snd_free_pci_pages(chip->pci, PAGE_ALIGN(MAX_RISC_SIZE), - chip->risc, chip->risc_dma); - chip->risc = NULL; + if (chip->dma_risc.area) { + snd_dma_free_pages(&chip->dma_dev, &chip->dma_risc); + chip->dma_risc.area = NULL; } } @@ -459,7 +459,7 @@ static int snd_bt87x_start(bt87x_t *chip spin_lock_irqsave(&chip->reg_lock, flags); chip->current_line = 0; chip->reg_control |= CTL_FIFO_ENABLE | CTL_RISC_ENABLE | CTL_ACAP_EN; - snd_bt87x_writel(chip, REG_RISC_STRT_ADD, chip->risc_dma); + snd_bt87x_writel(chip, REG_RISC_STRT_ADD, chip->dma_risc.addr); snd_bt87x_writel(chip, REG_PACKET_LEN, chip->line_bytes | (chip->lines << 16)); snd_bt87x_writel(chip, REG_INT_MASK, MY_INTERRUPTS); @@ -681,7 +681,9 @@ static int __devinit snd_bt87x_pcm(bt87x pcm->private_data = chip; strcpy(pcm->name, name); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_bt87x_pcm_ops); - return snd_pcm_lib_preallocate_sg_pages_for_all(chip->pci, pcm, + return snd_pcm_lib_preallocate_pages_for_all(pcm, + SNDRV_DMA_TYPE_DEV_SG, + snd_dma_pci_data(chip->pci), 128 * 1024, (255 * 4092 + 1023) & ~1023); } --- linux-2.6.4-rc2/sound/pci/cmipci.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/cmipci.c 2004-03-07 20:46:50.000000000 -0800 @@ -2087,7 +2087,8 @@ static int __devinit snd_cmipci_pcm_new( strcpy(pcm->name, "C-Media PCI DAC/ADC"); cm->pcm = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(cm->pci, pcm, 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(cm->pci), 64*1024, 128*1024); return 0; } @@ -2109,7 +2110,8 @@ static int __devinit snd_cmipci_pcm2_new strcpy(pcm->name, "C-Media PCI 2nd DAC"); cm->pcm2 = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(cm->pci, pcm, 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(cm->pci), 64*1024, 128*1024); return 0; } @@ -2139,7 +2141,8 @@ static int __devinit snd_cmipci_pcm_spdi strcpy(pcm->name, "C-Media PCI IEC958"); cm->pcm_spdif = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(cm->pci, pcm, 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(cm->pci), 64*1024, 128*1024); return 0; } @@ -3151,7 +3154,7 @@ static int __devinit snd_cmipci_create(s #ifdef SUPPORT_JOYSTICK if (joystick_port[dev] > 0) { if (joystick_port[dev] == 1) { /* auto-detect */ - static int ports[] = { 0x200, 0x201, 0 }; + static int ports[] = { 0x201, 0x200, 0 }; /* FIXME: majority is 0x201? */ int i; for (i = 0; ports[i]; i++) { joystick_port[dev] = ports[i]; --- linux-2.6.4-rc2/sound/pci/cs4281.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/cs4281.c 2004-03-07 20:46:50.000000000 -0800 @@ -1039,7 +1039,8 @@ static int __devinit snd_cs4281_pcm(cs42 strcpy(pcm->name, "CS4281"); chip->pcm = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 512*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), 64*1024, 512*1024); if (rpcm) *rpcm = pcm; --- linux-2.6.4-rc2/sound/pci/cs46xx/cs46xx_lib.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/cs46xx/cs46xx_lib.c 2004-03-07 20:46:50.000000000 -0800 @@ -66,6 +66,8 @@ #include "cs46xx_lib.h" #include "dsp_spos.h" +static void amp_voyetra(cs46xx_t *chip, int change); + static unsigned short snd_cs46xx_codec_read(cs46xx_t *chip, unsigned short reg, int codec_index) @@ -203,6 +205,13 @@ static unsigned short snd_cs46xx_ac97_re chip->active_ctrl(chip, 1); val = snd_cs46xx_codec_read(chip, reg, codec_index); chip->active_ctrl(chip, -1); + + /* HACK: voyetra uses EAPD bit in the reverse way. + * we flip the bit to show the mixer status correctly + */ + if (reg == AC97_POWERDOWN && chip->amplifier_ctrl == amp_voyetra) + val ^= 0x8000; + return val; } @@ -284,6 +293,12 @@ static void snd_cs46xx_ac97_write(ac97_t else snd_assert(0,return); + /* HACK: voyetra uses EAPD bit in the reverse way. + * we flip the bit to show the mixer status correctly + */ + if (reg == AC97_POWERDOWN && chip->amplifier_ctrl == amp_voyetra) + val ^= 0x8000; + chip->active_ctrl(chip, 1); snd_cs46xx_codec_write(chip, reg, val, codec_index); chip->active_ctrl(chip, -1); @@ -699,7 +714,7 @@ static int snd_cs46xx_playback_transfer( bytes = hw_to_end; if (sw_to_end < bytes) bytes = sw_to_end; - memcpy(cpcm->hw_area + cpcm->hw_data, + memcpy(cpcm->hw_buf.area + cpcm->hw_data, runtime->dma_area + cpcm->sw_data, bytes); cpcm->hw_data += bytes; @@ -740,7 +755,7 @@ static int snd_cs46xx_capture_transfer(s if (sw_to_end < bytes) bytes = sw_to_end; memcpy(runtime->dma_area + chip->capt.sw_data, - chip->capt.hw_area + chip->capt.hw_data, + chip->capt.hw_buf.area + chip->capt.hw_data, bytes); chip->capt.hw_data += bytes; if ((int)chip->capt.hw_data == buffer_size) @@ -766,7 +781,7 @@ static snd_pcm_uframes_t snd_cs46xx_play #else ptr = snd_cs46xx_peek(chip, BA1_PBA); #endif - ptr -= cpcm->hw_addr; + ptr -= cpcm->hw_buf.addr; return ptr >> cpcm->shift; } @@ -784,7 +799,7 @@ static snd_pcm_uframes_t snd_cs46xx_play #else ptr = snd_cs46xx_peek(chip, BA1_PBA); #endif - ptr -= cpcm->hw_addr; + ptr -= cpcm->hw_buf.addr; bytes = ptr - cpcm->hw_io; @@ -802,14 +817,14 @@ static snd_pcm_uframes_t snd_cs46xx_play static snd_pcm_uframes_t snd_cs46xx_capture_direct_pointer(snd_pcm_substream_t * substream) { cs46xx_t *chip = snd_pcm_substream_chip(substream); - size_t ptr = snd_cs46xx_peek(chip, BA1_CBA) - chip->capt.hw_addr; + size_t ptr = snd_cs46xx_peek(chip, BA1_CBA) - chip->capt.hw_buf.addr; return ptr >> chip->capt.shift; } static snd_pcm_uframes_t snd_cs46xx_capture_indirect_pointer(snd_pcm_substream_t * substream) { cs46xx_t *chip = snd_pcm_substream_chip(substream); - size_t ptr = snd_cs46xx_peek(chip, BA1_CBA) - chip->capt.hw_addr; + size_t ptr = snd_cs46xx_peek(chip, BA1_CBA) - chip->capt.hw_buf.addr; ssize_t bytes = ptr - chip->capt.hw_io; int buffer_size = substream->runtime->period_size * CS46XX_FRAGS << chip->capt.shift; @@ -933,7 +948,7 @@ static int _cs46xx_adjust_sample_rate (c /* If PCMReaderSCB and SrcTaskSCB not created yet ... */ if ( cpcm->pcm_channel == NULL) { cpcm->pcm_channel = cs46xx_dsp_create_pcm_channel (chip, sample_rate, - cpcm, cpcm->hw_addr,cpcm->pcm_channel_id); + cpcm, cpcm->hw_buf.addr,cpcm->pcm_channel_id); if (cpcm->pcm_channel == NULL) { snd_printk(KERN_ERR "cs46xx: failed to create virtual PCM channel\n"); return -ENOMEM; @@ -946,7 +961,7 @@ static int _cs46xx_adjust_sample_rate (c cs46xx_dsp_destroy_pcm_channel (chip,cpcm->pcm_channel); if ( (cpcm->pcm_channel = cs46xx_dsp_create_pcm_channel (chip, sample_rate, cpcm, - cpcm->hw_addr, + cpcm->hw_buf.addr, cpcm->pcm_channel_id)) == NULL) { snd_printk(KERN_ERR "cs46xx: failed to re-create virtual PCM channel\n"); return -ENOMEM; @@ -1002,11 +1017,11 @@ static int snd_cs46xx_playback_hw_params #endif if (params_periods(hw_params) == CS46XX_FRAGS) { - if (runtime->dma_area != cpcm->hw_area) + if (runtime->dma_area != cpcm->hw_buf.area) snd_pcm_lib_free_pages(substream); - runtime->dma_area = cpcm->hw_area; - runtime->dma_addr = cpcm->hw_addr; - runtime->dma_bytes = cpcm->hw_size; + runtime->dma_area = cpcm->hw_buf.area; + runtime->dma_addr = cpcm->hw_buf.addr; + runtime->dma_bytes = cpcm->hw_buf.bytes; #ifdef CONFIG_SND_CS46XX_NEW_DSP @@ -1026,7 +1041,7 @@ static int snd_cs46xx_playback_hw_params #endif } else { - if (runtime->dma_area == cpcm->hw_area) { + if (runtime->dma_area == cpcm->hw_buf.area) { runtime->dma_area = NULL; runtime->dma_addr = 0; runtime->dma_bytes = 0; @@ -1075,7 +1090,7 @@ static int snd_cs46xx_playback_hw_free(s is called and cpcm can actually be NULL here */ if (!cpcm) return -ENXIO; - if (runtime->dma_area != cpcm->hw_area) + if (runtime->dma_area != cpcm->hw_buf.area) snd_pcm_lib_free_pages(substream); runtime->dma_area = NULL; @@ -1144,7 +1159,7 @@ static int snd_cs46xx_playback_prepare(s /* playback format && interrupt enable */ snd_cs46xx_poke(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 1) << 2, pfie | cpcm->pcm_channel->pcm_slot); #else - snd_cs46xx_poke(chip, BA1_PBA, cpcm->hw_addr); + snd_cs46xx_poke(chip, BA1_PBA, cpcm->hw_buf.addr); tmp = snd_cs46xx_peek(chip, BA1_PDTC); tmp &= ~0x000003ff; tmp |= (4 << cpcm->shift) - 1; @@ -1167,14 +1182,14 @@ static int snd_cs46xx_capture_hw_params( cs46xx_dsp_pcm_ostream_set_period (chip, params_period_bytes(hw_params)); #endif if (runtime->periods == CS46XX_FRAGS) { - if (runtime->dma_area != chip->capt.hw_area) + if (runtime->dma_area != chip->capt.hw_buf.area) snd_pcm_lib_free_pages(substream); - runtime->dma_area = chip->capt.hw_area; - runtime->dma_addr = chip->capt.hw_addr; - runtime->dma_bytes = chip->capt.hw_size; + runtime->dma_area = chip->capt.hw_buf.area; + runtime->dma_addr = chip->capt.hw_buf.addr; + runtime->dma_bytes = chip->capt.hw_buf.bytes; substream->ops = &snd_cs46xx_capture_ops; } else { - if (runtime->dma_area == chip->capt.hw_area) { + if (runtime->dma_area == chip->capt.hw_buf.area) { runtime->dma_area = NULL; runtime->dma_addr = 0; runtime->dma_bytes = 0; @@ -1192,7 +1207,7 @@ static int snd_cs46xx_capture_hw_free(sn cs46xx_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - if (runtime->dma_area != chip->capt.hw_area) + if (runtime->dma_area != chip->capt.hw_buf.area) snd_pcm_lib_free_pages(substream); runtime->dma_area = NULL; runtime->dma_addr = 0; @@ -1206,7 +1221,7 @@ static int snd_cs46xx_capture_prepare(sn cs46xx_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - snd_cs46xx_poke(chip, BA1_CBA, chip->capt.hw_addr); + snd_cs46xx_poke(chip, BA1_CBA, chip->capt.hw_buf.addr); chip->capt.shift = 2; chip->capt.sw_bufsize = snd_pcm_lib_buffer_bytes(substream); chip->capt.sw_data = chip->capt.sw_io = chip->capt.sw_ready = 0; @@ -1382,8 +1397,7 @@ static int _cs46xx_playback_open_channel cpcm = snd_magic_kcalloc(cs46xx_pcm_t, 0, GFP_KERNEL); if (cpcm == NULL) return -ENOMEM; - cpcm->hw_size = PAGE_SIZE; - if ((cpcm->hw_area = snd_malloc_pci_pages(chip->pci, cpcm->hw_size, &cpcm->hw_addr)) == NULL) { + if (snd_dma_alloc_pages(&chip->dma_dev, PAGE_SIZE, &cpcm->hw_buf) < 0) { snd_magic_kfree(cpcm); return -ENOMEM; } @@ -1472,7 +1486,7 @@ static int snd_cs46xx_capture_open(snd_p { cs46xx_t *chip = snd_pcm_substream_chip(substream); - if ((chip->capt.hw_area = snd_malloc_pci_pages(chip->pci, chip->capt.hw_size, &chip->capt.hw_addr)) == NULL) + if (snd_dma_alloc_pages(&chip->dma_dev, PAGE_SIZE, &chip->capt.hw_buf) < 0) return -ENOMEM; chip->capt.substream = substream; substream->runtime->hw = snd_cs46xx_capture; @@ -1513,7 +1527,7 @@ static int snd_cs46xx_playback_close(snd #endif cpcm->substream = NULL; - snd_free_pci_pages(chip->pci, cpcm->hw_size, cpcm->hw_area, cpcm->hw_addr); + snd_dma_free_pages(&chip->dma_dev, &cpcm->hw_buf); chip->active_ctrl(chip, -1); return 0; @@ -1524,7 +1538,7 @@ static int snd_cs46xx_capture_close(snd_ cs46xx_t *chip = snd_pcm_substream_chip(substream); chip->capt.substream = NULL; - snd_free_pci_pages(chip->pci, chip->capt.hw_size, chip->capt.hw_area, chip->capt.hw_addr); + snd_dma_free_pages(&chip->dma_dev, &chip->capt.hw_buf); chip->active_ctrl(chip, -1); return 0; @@ -1703,7 +1717,8 @@ int __devinit snd_cs46xx_pcm(cs46xx_t *c strcpy(pcm->name, "CS46xx"); chip->pcm = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), 64*1024, 256*1024); if (rpcm) *rpcm = pcm; @@ -1734,7 +1749,8 @@ int __devinit snd_cs46xx_pcm_rear(cs46xx strcpy(pcm->name, "CS46xx - Rear"); chip->pcm_rear = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), 64*1024, 256*1024); if (rpcm) *rpcm = pcm; @@ -1763,7 +1779,8 @@ int __devinit snd_cs46xx_pcm_center_lfe( strcpy(pcm->name, "CS46xx - Center LFE"); chip->pcm_center_lfe = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), 64*1024, 256*1024); if (rpcm) *rpcm = pcm; @@ -1792,7 +1809,8 @@ int __devinit snd_cs46xx_pcm_iec958(cs46 strcpy(pcm->name, "CS46xx - IEC958"); chip->pcm_rear = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), 64*1024, 256*1024); if (rpcm) *rpcm = pcm; @@ -2547,7 +2565,7 @@ int __devinit snd_cs46xx_mixer(cs46xx_t /* get EAPD mixer switch (for voyetra hack) */ memset(&id, 0, sizeof(id)); id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(id.name, "External Amplifier Power Down"); + strcpy(id.name, "External Amplifier"); chip->eapd_switch = snd_ctl_find_id(chip->card, &id); #ifdef CONFIG_SND_CS46XX_NEW_DSP @@ -3877,7 +3895,6 @@ int __devinit snd_cs46xx_create(snd_card #endif chip->card = card; chip->pci = pci; - chip->capt.hw_size = PAGE_SIZE; chip->irq = -1; chip->ba0_addr = pci_resource_start(pci, 0); chip->ba1_addr = pci_resource_start(pci, 1); @@ -3913,6 +3930,10 @@ int __devinit snd_cs46xx_create(snd_card region->base = chip->ba1_addr + BA1_SP_REG; region->size = CS46XX_BA1_REG_SIZE; + memset(&chip->dma_dev, 0, sizeof(chip->dma_dev)); + chip->dma_dev.type = SNDRV_DMA_TYPE_DEV; + chip->dma_dev.dev = snd_dma_pci_data(pci); + /* set up amp and clkrun hack */ pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &ss_vendor); pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &ss_card); --- linux-2.6.4-rc2/sound/pci/emu10k1/emu10k1_callback.c 2003-06-14 12:18:06.000000000 -0700 +++ 25/sound/pci/emu10k1/emu10k1_callback.c 2004-03-07 20:46:50.000000000 -0800 @@ -405,7 +405,7 @@ start_voice(snd_emux_voice_t *vp) snd_emu10k1_ptr_write(hw, Z2, ch, 0); /* invalidate maps */ - temp = (hw->silent_page_dmaaddr << 1) | MAP_PTI_MASK; + temp = (hw->silent_page.addr << 1) | MAP_PTI_MASK; snd_emu10k1_ptr_write(hw, MAPA, ch, temp); snd_emu10k1_ptr_write(hw, MAPB, ch, temp); #if 0 --- linux-2.6.4-rc2/sound/pci/emu10k1/emu10k1_main.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/emu10k1/emu10k1_main.c 2004-03-07 20:46:50.000000000 -0800 @@ -97,7 +97,8 @@ static int __devinit snd_emu10k1_init(em unsigned int silent_page; emu->fx8010.itram_size = (16 * 1024)/2; - emu->fx8010.etram_size = 0; + emu->fx8010.etram_pages.area = NULL; + emu->fx8010.etram_pages.bytes = 0; /* disable audio and lock cache */ outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE, emu->port + HCFG); @@ -184,15 +185,15 @@ static int __devinit snd_emu10k1_init(em /* * Clear page with silence & setup all pointers to this page */ - memset(emu->silent_page, 0, PAGE_SIZE); - silent_page = emu->silent_page_dmaaddr << 1; + memset(emu->silent_page.area, 0, PAGE_SIZE); + silent_page = emu->silent_page.addr << 1; for (idx = 0; idx < MAXPAGES; idx++) - emu->ptb_pages[idx] = cpu_to_le32(silent_page | idx); - snd_emu10k1_ptr_write(emu, PTB, 0, emu->ptb_pages_dmaaddr); + ((u32 *)emu->ptb_pages.area)[idx] = cpu_to_le32(silent_page | idx); + snd_emu10k1_ptr_write(emu, PTB, 0, emu->ptb_pages.addr); snd_emu10k1_ptr_write(emu, TCB, 0, 0); /* taken from original driver */ snd_emu10k1_ptr_write(emu, TCBS, 0, 4); /* taken from original driver */ - silent_page = (emu->silent_page_dmaaddr << 1) | MAP_PTI_MASK; + silent_page = (emu->silent_page.addr << 1) | MAP_PTI_MASK; for (ch = 0; ch < NUM_G; ch++) { snd_emu10k1_ptr_write(emu, MAPA, ch, silent_page); snd_emu10k1_ptr_write(emu, MAPB, ch, silent_page); @@ -546,10 +547,10 @@ static int snd_emu10k1_free(emu10k1_t *e } if (emu->memhdr) snd_util_memhdr_free(emu->memhdr); - if (emu->silent_page) - snd_free_pci_pages(emu->pci, EMUPAGESIZE, emu->silent_page, emu->silent_page_dmaaddr); - if (emu->ptb_pages) - snd_free_pci_pages(emu->pci, 32 * 1024, (void *)emu->ptb_pages, emu->ptb_pages_dmaaddr); + if (emu->silent_page.area) + snd_dma_free_pages(&emu->dma_dev, &emu->silent_page); + if (emu->ptb_pages.area) + snd_dma_free_pages(&emu->dma_dev, &emu->ptb_pages); if (emu->page_ptr_table) vfree(emu->page_ptr_table); if (emu->page_addr_table) @@ -638,9 +639,12 @@ int __devinit snd_emu10k1_create(snd_car } emu->irq = pci->irq; + memset(&emu->dma_dev, 0, sizeof(emu->dma_dev)); + emu->dma_dev.type = SNDRV_DMA_TYPE_DEV; + emu->dma_dev.dev = snd_dma_pci_data(pci); + emu->max_cache_pages = max_cache_bytes >> PAGE_SHIFT; - emu->ptb_pages = snd_malloc_pci_pages(pci, 32 * 1024, &emu->ptb_pages_dmaaddr); - if (emu->ptb_pages == NULL) { + if (snd_dma_alloc_pages(&emu->dma_dev, 32 * 1024, &emu->ptb_pages) < 0) { snd_emu10k1_free(emu); return -ENOMEM; } @@ -652,8 +656,7 @@ int __devinit snd_emu10k1_create(snd_car return -ENOMEM; } - emu->silent_page = snd_malloc_pci_pages(pci, EMUPAGESIZE, &emu->silent_page_dmaaddr); - if (emu->silent_page == NULL) { + if (snd_dma_alloc_pages(&emu->dma_dev, EMUPAGESIZE, &emu->silent_page) < 0) { snd_emu10k1_free(emu); return -ENOMEM; } --- linux-2.6.4-rc2/sound/pci/emu10k1/emufx.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/emu10k1/emufx.c 2004-03-07 20:46:50.000000000 -0800 @@ -26,6 +26,7 @@ */ #include +#include #include #include #include @@ -506,16 +507,16 @@ static void snd_emu10k1_fx8010_playback_ while (frames > *tram_pos) { count = *tram_pos + 1; - snd_emu10k1_fx8010_playback_tram_poke1((unsigned short *)emu->fx8010.etram_pages + *tram_pos, - (unsigned short *)emu->fx8010.etram_pages + *tram_pos + tram_size / 2, + snd_emu10k1_fx8010_playback_tram_poke1((unsigned short *)emu->fx8010.etram_pages.area + *tram_pos, + (unsigned short *)emu->fx8010.etram_pages.area + *tram_pos + tram_size / 2, src, count, *tram_shift); src += count * 2; frames -= count; *tram_pos = (tram_size / 2) - 1; (*tram_shift)++; } - snd_emu10k1_fx8010_playback_tram_poke1((unsigned short *)emu->fx8010.etram_pages + *tram_pos, - (unsigned short *)emu->fx8010.etram_pages + *tram_pos + tram_size / 2, + snd_emu10k1_fx8010_playback_tram_poke1((unsigned short *)emu->fx8010.etram_pages.area + *tram_pos, + (unsigned short *)emu->fx8010.etram_pages.area + *tram_pos + tram_size / 2, src, frames, *tram_shift++); *tram_pos -= frames; } @@ -760,7 +761,7 @@ int snd_emu10k1_fx8010_pcm(emu10k1_t * e strcpy(pcm->name, "EMU10K1 FX8010"); emu->pcm_fx8010 = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(emu->pci, pcm, 64*1024, 0); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 0); if (rpcm) *rpcm = pcm; @@ -2218,32 +2219,30 @@ int snd_emu10k1_fx8010_tram_setup(emu10k } size = 0x2000 << size_reg; } - if (emu->fx8010.etram_size == size) + if (emu->fx8010.etram_pages.bytes == size) return 0; spin_lock_irq(&emu->emu_lock); outl(HCFG_LOCKTANKCACHE_MASK | inl(emu->port + HCFG), emu->port + HCFG); spin_unlock_irq(&emu->emu_lock); snd_emu10k1_ptr_write(emu, TCB, 0, 0); snd_emu10k1_ptr_write(emu, TCBS, 0, 0); - if (emu->fx8010.etram_pages != NULL) { - snd_free_pci_pages(emu->pci, emu->fx8010.etram_size * 2, emu->fx8010.etram_pages, emu->fx8010.etram_pages_dmaaddr); - emu->fx8010.etram_pages = NULL; - emu->fx8010.etram_size = 0; + if (emu->fx8010.etram_pages.area != NULL) { + snd_dma_free_pages(&emu->dma_dev, &emu->fx8010.etram_pages); + emu->fx8010.etram_pages.area = NULL; + emu->fx8010.etram_pages.bytes = 0; } if (size > 0) { - emu->fx8010.etram_pages = snd_malloc_pci_pages(emu->pci, size * 2, &emu->fx8010.etram_pages_dmaaddr); - if (emu->fx8010.etram_pages == NULL) + if (snd_dma_alloc_pages(&emu->dma_dev, size * 2, &emu->fx8010.etram_pages) < 0) return -ENOMEM; - memset(emu->fx8010.etram_pages, 0, size * 2); - snd_emu10k1_ptr_write(emu, TCB, 0, emu->fx8010.etram_pages_dmaaddr); + memset(emu->fx8010.etram_pages.area, 0, size * 2); + snd_emu10k1_ptr_write(emu, TCB, 0, emu->fx8010.etram_pages.addr); snd_emu10k1_ptr_write(emu, TCBS, 0, size_reg); spin_lock_irq(&emu->emu_lock); outl(inl(emu->port + HCFG) & ~HCFG_LOCKTANKCACHE_MASK, emu->port + HCFG); spin_unlock_irq(&emu->emu_lock); } - emu->fx8010.etram_size = size; return 0; } @@ -2269,7 +2268,7 @@ static int snd_emu10k1_fx8010_info(emu10 memset(info, 0, sizeof(info)); info->card = emu->card_type; info->internal_tram_size = emu->fx8010.itram_size; - info->external_tram_size = emu->fx8010.etram_size; + info->external_tram_size = emu->fx8010.etram_pages.bytes; fxbus = fxbuses; extin = emu->audigy ? audigy_ins : creative_ins; extout = emu->audigy ? audigy_outs : creative_outs; --- linux-2.6.4-rc2/sound/pci/emu10k1/emupcm.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/emu10k1/emupcm.c 2004-03-07 20:46:50.000000000 -0800 @@ -26,6 +26,7 @@ */ #include +#include #include #include #include @@ -323,7 +324,7 @@ static void snd_emu10k1_pcm_init_voice(e snd_emu10k1_ptr_write(emu, Z1, voice, 0); snd_emu10k1_ptr_write(emu, Z2, voice, 0); // invalidate maps - silent_page = ((unsigned int)emu->silent_page_dmaaddr << 1) | MAP_PTI_MASK; + silent_page = ((unsigned int)emu->silent_page.addr << 1) | MAP_PTI_MASK; snd_emu10k1_ptr_write(emu, MAPA, voice, silent_page); snd_emu10k1_ptr_write(emu, MAPB, voice, silent_page); // modulation envelope @@ -998,11 +999,11 @@ int __devinit snd_emu10k1_pcm(emu10k1_t emu->pcm = pcm; for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) - if ((err = snd_pcm_lib_preallocate_sg_pages(emu->pci, substream, 64*1024, 64*1024)) < 0) + if ((err = snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG, snd_dma_pci_data(emu->pci), 64*1024, 64*1024)) < 0) return err; for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; substream; substream = substream->next) - snd_pcm_lib_preallocate_pci_pages(emu->pci, substream, 64*1024, 64*1024); + snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 64*1024); if (rpcm) *rpcm = pcm; @@ -1048,7 +1049,7 @@ int __devinit snd_emu10k1_pcm_mic(emu10k strcpy(pcm->name, "EMU10K1 MIC"); emu->pcm_mic = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(emu->pci, pcm, 64*1024, 64*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 64*1024); if (rpcm) *rpcm = pcm; @@ -1157,7 +1158,7 @@ int __devinit snd_emu10k1_pcm_efx(emu10k emu->efx_voices_mask[1] = 0; snd_ctl_add(emu->card, snd_ctl_new1(&snd_emu10k1_pcm_efx_voices_mask, emu)); - snd_pcm_lib_preallocate_pci_pages_for_all(emu->pci, pcm, 64*1024, 64*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 64*1024); return 0; } --- linux-2.6.4-rc2/sound/pci/emu10k1/emuproc.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/emu10k1/emuproc.c 2004-03-07 20:46:50.000000000 -0800 @@ -117,7 +117,7 @@ static void snd_emu10k1_proc_read(snd_in snd_iprintf(buffer, "Card : %s\n", emu->audigy ? "Audigy" : (emu->APS ? "EMU APS" : "Creative")); snd_iprintf(buffer, "Internal TRAM (words) : 0x%x\n", emu->fx8010.itram_size); - snd_iprintf(buffer, "External TRAM (words) : 0x%x\n", emu->fx8010.etram_size); + snd_iprintf(buffer, "External TRAM (words) : 0x%x\n", emu->fx8010.etram_pages.bytes); snd_iprintf(buffer, "\n"); if (emu->audigy) { snd_iprintf(buffer, "Effect Send Routing : A=%i, B=%i, C=%i, D=%i\n", --- linux-2.6.4-rc2/sound/pci/emu10k1/memory.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/emu10k1/memory.c 2004-03-07 20:46:50.000000000 -0800 @@ -30,7 +30,7 @@ * aligned pages in others */ #define __set_ptb_entry(emu,page,addr) \ - ((emu)->ptb_pages[page] = cpu_to_le32(((addr) << 1) | (page))) + (((u32 *)(emu)->ptb_pages.area)[page] = cpu_to_le32(((addr) << 1) | (page))) #define UNIT_PAGES (PAGE_SIZE / EMUPAGESIZE) #define MAX_ALIGN_PAGES (MAXPAGES / UNIT_PAGES) @@ -44,7 +44,7 @@ /* fill PTB entrie(s) corresponding to page with addr */ #define set_ptb_entry(emu,page,addr) __set_ptb_entry(emu,page,addr) /* fill PTB entrie(s) corresponding to page with silence pointer */ -#define set_silent_ptb(emu,page) __set_ptb_entry(emu,page,emu->silent_page_dmaaddr) +#define set_silent_ptb(emu,page) __set_ptb_entry(emu,page,emu->silent_page.addr) #else /* fill PTB entries -- we need to fill UNIT_PAGES entries */ static inline void set_ptb_entry(emu10k1_t *emu, int page, dma_addr_t addr) @@ -297,7 +297,6 @@ snd_emu10k1_alloc_pages(emu10k1_t *emu, int page, err, idx; snd_assert(emu, return NULL); - snd_assert(substream->dma_device.type == SNDRV_DMA_TYPE_PCI_SG, return NULL); snd_assert(runtime->dma_bytes > 0 && runtime->dma_bytes < MAXPAGES * EMUPAGESIZE, return NULL); hdr = emu->memhdr; snd_assert(hdr, return NULL); @@ -436,22 +435,20 @@ static void get_single_page_range(snd_ut static int synth_alloc_pages(emu10k1_t *emu, emu10k1_memblk_t *blk) { int page, first_page, last_page; - void *ptr; - dma_addr_t addr; + struct snd_dma_buffer dmab; emu10k1_memblk_init(blk); get_single_page_range(emu->memhdr, blk, &first_page, &last_page); /* allocate kernel pages */ for (page = first_page; page <= last_page; page++) { - ptr = snd_malloc_pci_page(emu->pci, &addr); - if (ptr == NULL) + if (snd_dma_alloc_pages(&emu->dma_dev, PAGE_SIZE, &dmab) < 0) goto __fail; - if (! is_valid_page(emu, addr)) { - snd_free_pci_page(emu->pci, ptr, addr); + if (! is_valid_page(emu, dmab.addr)) { + snd_dma_free_pages(&emu->dma_dev, &dmab); goto __fail; } - emu->page_addr_table[page] = addr; - emu->page_ptr_table[page] = ptr; + emu->page_addr_table[page] = dmab.addr; + emu->page_ptr_table[page] = dmab.area; } return 0; @@ -459,7 +456,10 @@ __fail: /* release allocated pages */ last_page = page - 1; for (page = first_page; page <= last_page; page++) { - snd_free_pci_page(emu->pci, emu->page_ptr_table[page], emu->page_addr_table[page]); + dmab.area = emu->page_ptr_table[page]; + dmab.addr = emu->page_addr_table[page]; + dmab.bytes = PAGE_SIZE; + snd_dma_free_pages(&emu->dma_dev, &dmab); emu->page_addr_table[page] = 0; emu->page_ptr_table[page] = NULL; } @@ -473,11 +473,16 @@ __fail: static int synth_free_pages(emu10k1_t *emu, emu10k1_memblk_t *blk) { int page, first_page, last_page; + struct snd_dma_buffer dmab; get_single_page_range(emu->memhdr, blk, &first_page, &last_page); for (page = first_page; page <= last_page; page++) { - if (emu->page_ptr_table[page]) - snd_free_pci_page(emu->pci, emu->page_ptr_table[page], emu->page_addr_table[page]); + if (emu->page_ptr_table[page] == NULL) + continue; + dmab.area = emu->page_ptr_table[page]; + dmab.addr = emu->page_addr_table[page]; + dmab.bytes = PAGE_SIZE; + snd_dma_free_pages(&emu->dma_dev, &dmab); emu->page_addr_table[page] = 0; emu->page_ptr_table[page] = NULL; } --- linux-2.6.4-rc2/sound/pci/ens1370.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/ens1370.c 2004-03-07 20:46:50.000000000 -0800 @@ -434,8 +434,8 @@ struct _snd_ensoniq { unsigned int spdif_stream; #ifdef CHIP1370 - unsigned char *bugbuf; - dma_addr_t bugbuf_addr; + struct snd_dma_device dma_dev; + struct snd_dma_buffer dma_bug; #endif #ifdef SUPPORT_JOYSTICK @@ -1250,7 +1250,8 @@ static int __devinit snd_ensoniq_pcm(ens #endif ensoniq->pcm1 = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(ensoniq->pci, pcm, 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(ensoniq->pci), 64*1024, 128*1024); if (rpcm) *rpcm = pcm; @@ -1294,7 +1295,8 @@ static int __devinit snd_ensoniq_pcm2(en #endif ensoniq->pcm2 = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(ensoniq->pci, pcm, 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(ensoniq->pci), 64*1024, 128*1024); if (rpcm) *rpcm = pcm; @@ -1829,8 +1831,8 @@ static int snd_ensoniq_free(ensoniq_t *e pci_set_power_state(ensoniq->pci, 3); __hw_end: #ifdef CHIP1370 - if (ensoniq->bugbuf) - snd_free_pci_pages(ensoniq->pci, 16, ensoniq->bugbuf, ensoniq->bugbuf_addr); + if (ensoniq->dma_bug.area) + snd_dma_free_pages(&ensoniq->dma_dev, &ensoniq->dma_bug); #endif if (ensoniq->res_port) { release_resource(ensoniq->res_port); @@ -1911,8 +1913,11 @@ static int __devinit snd_ensoniq_create( } ensoniq->irq = pci->irq; #ifdef CHIP1370 - if ((ensoniq->bugbuf = snd_malloc_pci_pages(pci, 16, &ensoniq->bugbuf_addr)) == NULL) { - snd_printk("unable to allocate space for phantom area - bugbuf\n"); + memset(&ensoniq->dma_dev, 0, sizeof(ensoniq->dma_dev)); + ensoniq->dma_dev.type = SNDRV_DMA_TYPE_DEV; + ensoniq->dma_dev.dev = snd_dma_pci_data(pci); + if (snd_dma_alloc_pages(&ensoniq->dma_dev, 16, &ensoniq->dma_bug) < 0) { + snd_printk("unable to allocate space for phantom area - dma_bug\n"); snd_ensoniq_free(ensoniq); return -EBUSY; } @@ -1936,7 +1941,7 @@ static int __devinit snd_ensoniq_create( outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL)); outl(ES_MEM_PAGEO(ES_PAGE_ADC), ES_REG(ensoniq, MEM_PAGE)); - outl(ensoniq->bugbuf_addr, ES_REG(ensoniq, PHANTOM_FRAME)); + outl(ensoniq->dma_bug.addr, ES_REG(ensoniq, PHANTOM_FRAME)); outl(0, ES_REG(ensoniq, PHANTOM_COUNT)); #else ensoniq->ctrl = 0; --- linux-2.6.4-rc2/sound/pci/es1938.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/es1938.c 2004-03-07 20:46:50.000000000 -0800 @@ -946,17 +946,14 @@ static int snd_es1938_playback_open(snd_ static int snd_es1938_capture_close(snd_pcm_substream_t * substream) { es1938_t *chip = snd_pcm_substream_chip(substream); - snd_pcm_runtime_t *runtime = substream->runtime; chip->capture_substream = NULL; - snd_free_pci_pages(chip->pci, runtime->dma_bytes, runtime->dma_area, runtime->dma_addr); return 0; } static int snd_es1938_playback_close(snd_pcm_substream_t * substream) { es1938_t *chip = snd_pcm_substream_chip(substream); - snd_pcm_runtime_t *runtime = substream->runtime; switch (substream->number) { case 0: @@ -969,7 +966,6 @@ static int snd_es1938_playback_close(snd snd_BUG(); return -EINVAL; } - snd_free_pci_pages(chip->pci, runtime->dma_bytes, runtime->dma_area, runtime->dma_addr); return 0; } @@ -1018,7 +1014,8 @@ static int __devinit snd_es1938_new_pcm( pcm->info_flags = 0; strcpy(pcm->name, "ESS Solo-1"); - snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 64*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), 64*1024, 64*1024); if (rpcm) *rpcm = pcm; --- linux-2.6.4-rc2/sound/pci/es1968.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/es1968.c 2004-03-07 20:46:50.000000000 -0800 @@ -1495,13 +1495,15 @@ static void snd_es1968_free_dmabuf(es196 static int __devinit snd_es1968_init_dmabuf(es1968_t *chip) { + int err; esm_memory_t *chunk; - snd_dma_device_pci(&chip->dma_dev, chip->pci, 0); + chip->dma_dev.type = SNDRV_DMA_TYPE_DEV; + chip->dma_dev.dev = snd_dma_pci_data(chip->pci); + chip->dma_dev.id = 0; if (! snd_dma_get_reserved(&chip->dma_dev, &chip->dma)) { - chip->dma.area = snd_malloc_pci_pages_fallback(chip->pci, chip->total_bufsize, - &chip->dma.addr, &chip->dma.bytes); - if (chip->dma.area == NULL) { + err = snd_dma_alloc_pages_fallback(&chip->dma_dev, chip->total_bufsize, &chip->dma); + if (err < 0 || ! chip->dma.area) { snd_printk("es1968: can't allocate dma pages for size %d\n", chip->total_bufsize); return -ENOMEM; --- linux-2.6.4-rc2/sound/pci/fm801.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/fm801.c 2004-03-07 20:46:50.000000000 -0800 @@ -35,7 +35,7 @@ #include -#if defined(CONFIG_SND_FM801_TEA575X) && (defined(CONFIG_VIDEO_DEV) || defined(CONFIG_VIDEO_DEV_MODULE)) +#if (defined(CONFIG_SND_FM801_TEA575X) || defined(CONFIG_SND_FM801_TEA575X_MODULE)) && (defined(CONFIG_VIDEO_DEV) || defined(CONFIG_VIDEO_DEV_MODULE)) #include #define TEA575X_RADIO 1 #endif @@ -703,7 +703,9 @@ static int __devinit snd_fm801_pcm(fm801 strcpy(pcm->name, "FM801"); chip->pcm = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, chip->multichannel ? 128*1024 : 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), + chip->multichannel ? 128*1024 : 64*1024, 128*1024); if (rpcm) *rpcm = pcm; --- linux-2.6.4-rc2/sound/pci/ice1712/amp.c 2003-06-14 12:18:21.000000000 -0700 +++ 25/sound/pci/ice1712/amp.c 2004-03-07 20:46:50.000000000 -0800 @@ -38,6 +38,7 @@ static int __devinit snd_vt1724_amp_init /* only use basic functionality for now */ ice->num_total_dacs = 2; /* only PSDOUT0 is connected */ + ice->num_total_adcs = 2; return 0; } --- linux-2.6.4-rc2/sound/pci/ice1712/aureon.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/ice1712/aureon.c 2004-03-07 20:46:50.000000000 -0800 @@ -409,10 +409,13 @@ static int __devinit aureon_init(ice1712 unsigned int tmp; unsigned int i; - if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AUREON51_SKY) + if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AUREON51_SKY) { ice->num_total_dacs = 6; - else + ice->num_total_adcs = 6; + } else { ice->num_total_dacs = 8; + ice->num_total_adcs = 8; + } /* to remeber the register values */ ice->akm = snd_kcalloc(sizeof(akm4xxx_t), GFP_KERNEL); --- linux-2.6.4-rc2/sound/pci/ice1712/delta.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/ice1712/delta.c 2004-03-07 20:46:50.000000000 -0800 @@ -511,20 +511,25 @@ static int __devinit snd_ice1712_delta_i switch (ice->eeprom.subvendor) { case ICE1712_SUBDEVICE_AUDIOPHILE: ice->num_total_dacs = 2; + ice->num_total_adcs = 2; break; case ICE1712_SUBDEVICE_DELTA410: ice->num_total_dacs = 8; + ice->num_total_adcs = 2; break; case ICE1712_SUBDEVICE_DELTA44: case ICE1712_SUBDEVICE_DELTA66: ice->num_total_dacs = ice->omni ? 8 : 4; + ice->num_total_adcs = ice->omni ? 8 : 4; break; case ICE1712_SUBDEVICE_DELTA1010: case ICE1712_SUBDEVICE_DELTA1010LT: ice->num_total_dacs = 8; + ice->num_total_adcs = 8; break; case ICE1712_SUBDEVICE_VX442: ice->num_total_dacs = 4; + ice->num_total_adcs = 4; break; } --- linux-2.6.4-rc2/sound/pci/ice1712/ews.c 2003-08-08 22:55:15.000000000 -0700 +++ 25/sound/pci/ice1712/ews.c 2004-03-07 20:46:50.000000000 -0800 @@ -406,15 +406,18 @@ static int __devinit snd_ice1712_ews_ini switch (ice->eeprom.subvendor) { case ICE1712_SUBDEVICE_EWX2496: ice->num_total_dacs = 2; + ice->num_total_adcs = 2; break; case ICE1712_SUBDEVICE_EWS88MT: case ICE1712_SUBDEVICE_EWS88MT_NEW: ice->num_total_dacs = 8; + ice->num_total_adcs = 8; break; case ICE1712_SUBDEVICE_EWS88D: break; /* no analog */ case ICE1712_SUBDEVICE_DMX6FIRE: ice->num_total_dacs = 6; + ice->num_total_adcs = 6; break; } --- linux-2.6.4-rc2/sound/pci/ice1712/hoontech.c 2003-06-14 12:18:23.000000000 -0700 +++ 25/sound/pci/ice1712/hoontech.c 2004-03-07 20:46:50.000000000 -0800 @@ -153,6 +153,7 @@ static int __devinit snd_ice1712_hoontec int box, chn; ice->num_total_dacs = 8; + ice->num_total_adcs = 8; ice->hoontech_boxbits[0] = ice->hoontech_boxbits[1] = --- linux-2.6.4-rc2/sound/pci/ice1712/ice1712.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/ice1712/ice1712.c 2004-03-07 20:46:50.000000000 -0800 @@ -886,7 +886,8 @@ static int __devinit snd_ice1712_pcm(ice strcpy(pcm->name, "ICE1712 consumer"); ice->pcm = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 64*1024, 64*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(ice->pci), 64*1024, 64*1024); if (rpcm) *rpcm = pcm; @@ -922,7 +923,8 @@ static int __devinit snd_ice1712_pcm_ds( strcpy(pcm->name, "ICE1712 consumer (DS)"); ice->pcm_ds = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(ice->pci), 64*1024, 128*1024); if (rpcm) *rpcm = pcm; @@ -1269,7 +1271,8 @@ static int __devinit snd_ice1712_pcm_pro pcm->info_flags = 0; strcpy(pcm->name, "ICE1712 multi"); - snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 256*1024, 256*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(ice->pci), 256*1024, 256*1024); ice->pcm_pro = pcm; if (rpcm) @@ -1381,7 +1384,7 @@ static int snd_ice1712_pro_mixer_volume_ } -static snd_kcontrol_new_t snd_ice1712_multi_ctrls[] __devinitdata = { +static snd_kcontrol_new_t snd_ice1712_multi_playback_ctrls[] __devinitdata = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Multi Playback Switch", @@ -1400,24 +1403,44 @@ static snd_kcontrol_new_t snd_ice1712_mu .private_value = 0, .count = 10, }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Multi Capture Switch", - .info = snd_ice1712_pro_mixer_switch_info, - .get = snd_ice1712_pro_mixer_switch_get, - .put = snd_ice1712_pro_mixer_switch_put, - .private_value = 10, - .count = 10, - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Multi Capture Volume", - .info = snd_ice1712_pro_mixer_volume_info, - .get = snd_ice1712_pro_mixer_volume_get, - .put = snd_ice1712_pro_mixer_volume_put, - .private_value = 10, - .count = 10, - }, +}; + +static snd_kcontrol_new_t snd_ice1712_multi_capture_analog_switch __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "H/W Multi Capture Switch", + .info = snd_ice1712_pro_mixer_switch_info, + .get = snd_ice1712_pro_mixer_switch_get, + .put = snd_ice1712_pro_mixer_switch_put, + .private_value = 10, +}; + +static snd_kcontrol_new_t snd_ice1712_multi_capture_spdif_switch __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "IEC958 Multi Capture Switch", + .info = snd_ice1712_pro_mixer_switch_info, + .get = snd_ice1712_pro_mixer_switch_get, + .put = snd_ice1712_pro_mixer_switch_put, + .private_value = 18, + .count = 2, +}; + +static snd_kcontrol_new_t snd_ice1712_multi_capture_analog_volume __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "H/W Multi Capture Volume", + .info = snd_ice1712_pro_mixer_volume_info, + .get = snd_ice1712_pro_mixer_volume_get, + .put = snd_ice1712_pro_mixer_volume_put, + .private_value = 10, +}; + +static snd_kcontrol_new_t snd_ice1712_multi_capture_spdif_volume __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "IEC958 Multi Capture Volume", + .info = snd_ice1712_pro_mixer_volume_info, + .get = snd_ice1712_pro_mixer_volume_get, + .put = snd_ice1712_pro_mixer_volume_put, + .private_value = 18, + .count = 2, }; static int __devinit snd_ice1712_build_pro_mixer(ice1712_t *ice) @@ -1427,14 +1450,46 @@ static int __devinit snd_ice1712_build_p int err; /* multi-channel mixer */ - for (idx = 0; idx < ARRAY_SIZE(snd_ice1712_multi_ctrls); idx++) { - err = snd_ctl_add(card, snd_ctl_new1(&snd_ice1712_multi_ctrls[idx], ice)); + for (idx = 0; idx < ARRAY_SIZE(snd_ice1712_multi_playback_ctrls); idx++) { + err = snd_ctl_add(card, snd_ctl_new1(&snd_ice1712_multi_playback_ctrls[idx], ice)); if (err < 0) return err; } + if (ice->num_total_adcs > 0) { + snd_kcontrol_new_t tmp = snd_ice1712_multi_capture_analog_switch; + tmp.count = ice->num_total_adcs; + err = snd_ctl_add(card, snd_ctl_new1(&tmp, ice)); + if (err < 0) + return err; + } + + err = snd_ctl_add(card, snd_ctl_new1(&snd_ice1712_multi_capture_spdif_switch, ice)); + if (err < 0) + return err; + + if (ice->num_total_adcs > 0) { + snd_kcontrol_new_t tmp = snd_ice1712_multi_capture_analog_volume; + tmp.count = ice->num_total_adcs; + err = snd_ctl_add(card, snd_ctl_new1(&tmp, ice)); + if (err < 0) + return err; + } + + err = snd_ctl_add(card, snd_ctl_new1(&snd_ice1712_multi_capture_spdif_volume, ice)); + if (err < 0) + return err; + /* initialize volumes */ - for (idx = 0; idx < 20; idx++) { + for (idx = 0; idx < 10; idx++) { + ice->pro_volumes[idx] = 0x80008000; /* mute */ + snd_ice1712_update_volume(ice, idx); + } + for (idx = 10; idx < 10 + ice->num_total_adcs; idx++) { + ice->pro_volumes[idx] = 0x80008000; /* mute */ + snd_ice1712_update_volume(ice, idx); + } + for (idx = 18; idx < 20; idx++) { ice->pro_volumes[idx] = 0x80008000; /* mute */ snd_ice1712_update_volume(ice, idx); } @@ -2375,6 +2430,7 @@ static int __devinit snd_ice1712_create( ice->omni = omni ? 1 : 0; spin_lock_init(&ice->reg_lock); init_MUTEX(&ice->gpio_mutex); + init_MUTEX(&ice->open_mutex); ice->gpio.set_mask = snd_ice1712_set_gpio_mask; ice->gpio.set_dir = snd_ice1712_set_gpio_dir; ice->gpio.set_data = snd_ice1712_set_gpio_data; --- linux-2.6.4-rc2/sound/pci/ice1712/ice1712.h 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/ice1712/ice1712.h 2004-03-07 20:46:50.000000000 -0800 @@ -330,11 +330,15 @@ struct _snd_ice1712 { unsigned int omni: 1; /* Delta Omni I/O */ unsigned int vt1724: 1; unsigned int num_total_dacs; /* total DACs */ + unsigned int num_total_adcs; /* total ADCs */ unsigned char hoontech_boxbits[4]; unsigned int hoontech_config; unsigned short hoontech_boxconfig[4]; unsigned int cur_rate; /* current rate */ + struct semaphore open_mutex; + snd_pcm_substream_t *pcm_reserved[4]; + unsigned int akm_codecs; akm4xxx_t *akm; struct snd_ice1712_spdif spdif; --- linux-2.6.4-rc2/sound/pci/ice1712/ice1724.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/ice1712/ice1724.c 2004-03-07 20:46:50.000000000 -0800 @@ -237,6 +237,18 @@ static irqreturn_t snd_vt1724_interrupt( if (ice->capture_pro_substream) snd_pcm_period_elapsed(ice->capture_pro_substream); } + if (mtstat & VT1724_MULTI_PDMA1) { + if (ice->playback_con_substream_ds[0]) + snd_pcm_period_elapsed(ice->playback_con_substream_ds[0]); + } + if (mtstat & VT1724_MULTI_PDMA2) { + if (ice->playback_con_substream_ds[1]) + snd_pcm_period_elapsed(ice->playback_con_substream_ds[1]); + } + if (mtstat & VT1724_MULTI_PDMA3) { + if (ice->playback_con_substream_ds[2]) + snd_pcm_period_elapsed(ice->playback_con_substream_ds[2]); + } if (mtstat & VT1724_MULTI_PDMA4) { if (ice->playback_con_substream) snd_pcm_period_elapsed(ice->playback_con_substream); @@ -282,16 +294,6 @@ static snd_pcm_hw_constraint_list_t hw_c .mask = 0, }; -static unsigned int hw_channels[] = { - 2, 4, 6, 8 -}; - -static snd_pcm_hw_constraint_list_t hw_constraints_channels = { - .count = ARRAY_SIZE(hw_channels), - .list = hw_channels, - .mask = 0, -}; - static int snd_vt1724_pcm_trigger(snd_pcm_substream_t *substream, int cmd) { ice1712_t *ice = snd_pcm_substream_chip(substream); @@ -300,21 +302,16 @@ static int snd_vt1724_pcm_trigger(snd_pc struct list_head *pos; snd_pcm_substream_t *s; + what = 0; + snd_pcm_group_for_each(pos, substream) { + s = snd_pcm_group_substream_entry(pos); + what |= (unsigned long)(s->runtime->private_data); + snd_pcm_trigger_done(s, substream); + } + switch (cmd) { case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - what = 0; - snd_pcm_group_for_each(pos, substream) { - s = snd_pcm_group_substream_entry(pos); - if (s == ice->playback_pro_substream) - what |= VT1724_PDMA0_PAUSE; - else if (s == ice->capture_pro_substream) - what |= VT1724_RDMA0_PAUSE; - else if (s == ice->playback_con_substream) - what |= VT1724_PDMA4_PAUSE; - else if (s == ice->capture_con_substream) - what |= VT1724_RDMA1_PAUSE; - } spin_lock(&ice->reg_lock); old = inb(ICEMT1724(ice, DMA_PAUSE)); if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH) @@ -327,24 +324,6 @@ static int snd_vt1724_pcm_trigger(snd_pc case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_STOP: - what = 0; - s = substream; - snd_pcm_group_for_each(pos, substream) { - s = snd_pcm_group_substream_entry(pos); - if (s == ice->playback_pro_substream) { - what |= VT1724_PDMA0_START; - snd_pcm_trigger_done(s, substream); - } else if (s == ice->capture_pro_substream) { - what |= VT1724_RDMA0_START; - snd_pcm_trigger_done(s, substream); - } else if (s == ice->playback_con_substream) { - what |= VT1724_PDMA4_START; - snd_pcm_trigger_done(s, substream); - } else if (s == ice->capture_con_substream) { - what |= VT1724_RDMA1_START; - snd_pcm_trigger_done(s, substream); - } - } spin_lock(&ice->reg_lock); old = inb(ICEMT1724(ice, DMA_CONTROL)); if (cmd == SNDRV_PCM_TRIGGER_START) @@ -364,8 +343,10 @@ static int snd_vt1724_pcm_trigger(snd_pc /* */ -#define DMA_STARTS (VT1724_RDMA0_START|VT1724_PDMA0_START|VT1724_RDMA1_START|VT1724_PDMA4_START) -#define DMA_PAUSES (VT1724_RDMA0_PAUSE|VT1724_PDMA0_PAUSE|VT1724_RDMA1_PAUSE|VT1724_PDMA4_PAUSE) +#define DMA_STARTS (VT1724_RDMA0_START|VT1724_PDMA0_START|VT1724_RDMA1_START|\ + VT1724_PDMA1_START|VT1724_PDMA2_START|VT1724_PDMA3_START|VT1724_PDMA4_START) +#define DMA_PAUSES (VT1724_RDMA0_PAUSE|VT1724_PDMA0_PAUSE|VT1724_RDMA1_PAUSE|\ + VT1724_PDMA1_PAUSE|VT1724_PDMA2_PAUSE|VT1724_PDMA3_PAUSE|VT1724_PDMA4_PAUSE) static void snd_vt1724_set_pro_rate(ice1712_t *ice, unsigned int rate, int force) { @@ -448,13 +429,52 @@ static int snd_vt1724_pcm_hw_params(snd_ snd_pcm_hw_params_t * hw_params) { ice1712_t *ice = snd_pcm_substream_chip(substream); + int i, chs; + chs = params_channels(hw_params); + down(&ice->open_mutex); + /* mark surround channels */ + if (substream == ice->playback_pro_substream) { + chs = chs / 2 - 1; + for (i = 0; i < chs; i++) { + if (ice->pcm_reserved[i] && ice->pcm_reserved[i] != substream) { + up(&ice->open_mutex); + return -EBUSY; + } + ice->pcm_reserved[i] = substream; + } + for (; i < 3; i++) { + if (ice->pcm_reserved[i] == substream) + ice->pcm_reserved[i] = NULL; + } + } else { + for (i = 0; i < 3; i++) { + if (ice->playback_con_substream_ds[i] == substream) { + if (ice->pcm_reserved[i] && ice->pcm_reserved[i] != substream) { + up(&ice->open_mutex); + return -EBUSY; + } + ice->pcm_reserved[i] = substream; + break; + } + } + } + up(&ice->open_mutex); snd_vt1724_set_pro_rate(ice, params_rate(hw_params), 0); return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); } static int snd_vt1724_pcm_hw_free(snd_pcm_substream_t * substream) { + ice1712_t *ice = snd_pcm_substream_chip(substream); + int i; + + down(&ice->open_mutex); + /* unmark surround channels */ + for (i = 0; i < 3; i++) + if (ice->pcm_reserved[i] == substream) + ice->pcm_reserved[i] = NULL; + up(&ice->open_mutex); return snd_pcm_lib_free_pages(substream); } @@ -593,14 +613,14 @@ static snd_pcm_hardware_t snd_vt1724_pla .rate_max = 192000, .channels_min = 2, .channels_max = 8, - .buffer_bytes_max = (1UL << 21), /* 18bits dword */ + .buffer_bytes_max = (1UL << 21), /* 19bits dword */ .period_bytes_min = 8 * 4 * 2, /* FIXME: constraints needed */ .period_bytes_max = (1UL << 21), - .periods_min = 1, + .periods_min = 2, .periods_max = 1024, }; -static snd_pcm_hardware_t snd_vt1724_capture_pro = +static snd_pcm_hardware_t snd_vt1724_2ch_stereo = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | @@ -612,10 +632,10 @@ static snd_pcm_hardware_t snd_vt1724_cap .rate_max = 192000, .channels_min = 2, .channels_max = 2, - .buffer_bytes_max = (256*1024), + .buffer_bytes_max = (1UL << 18), /* 16bits dword */ .period_bytes_min = 2 * 4 * 2, - .period_bytes_max = (256*1024), - .periods_min = 1, + .period_bytes_max = (1UL << 18), + .periods_min = 2, .periods_max = 1024, }; @@ -628,7 +648,9 @@ static int snd_vt1724_playback_pro_open( { snd_pcm_runtime_t *runtime = substream->runtime; ice1712_t *ice = snd_pcm_substream_chip(substream); + int chs; + runtime->private_data = (void*)VT1724_PDMA0_START; /* irq/status/trigger bit */ ice->playback_pro_substream = substream; runtime->hw = snd_vt1724_playback_pro; snd_pcm_set_sync(substream); @@ -639,7 +661,17 @@ static int snd_vt1724_playback_pro_open( else snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates_96); - snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels); + down(&ice->open_mutex); + /* calculate the currently available channels */ + for (chs = 0; chs < 3; chs++) { + if (ice->pcm_reserved[chs]) + break; + } + chs = (chs + 1) * 2; + runtime->hw.channels_max = chs; + if (chs > 2) /* channels must be even */ + snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, 2); + up(&ice->open_mutex); snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, VT1724_BUFFER_ALIGN); snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, @@ -652,8 +684,9 @@ static int snd_vt1724_capture_pro_open(s ice1712_t *ice = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; + runtime->private_data = (void*)VT1724_RDMA0_START; /* irq/status/trigger bit */ ice->capture_pro_substream = substream; - runtime->hw = snd_vt1724_capture_pro; + runtime->hw = snd_vt1724_2ch_stereo; snd_pcm_set_sync(substream); snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); if ((ice->eeprom.data[ICE_EEP2_ACLINK] & 0x80) && @@ -723,7 +756,8 @@ static int __devinit snd_vt1724_pcm_prof pcm->info_flags = 0; strcpy(pcm->name, "ICE1724"); - snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 256*1024, 256*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(ice->pci), 256*1024, 256*1024); ice->pcm_pro = pcm; @@ -735,25 +769,6 @@ static int __devinit snd_vt1724_pcm_prof * SPDIF PCM */ -static snd_pcm_hardware_t snd_vt1724_playback_spdif = -{ - .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START), - .formats = SNDRV_PCM_FMTBIT_S32_LE, - .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_192000, - .rate_min = 4000, - .rate_max = 192000, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = (256*1024), - .period_bytes_min = 2 * 4 * 2, - .period_bytes_max = (256*1024), - .periods_min = 1, - .periods_max = 1024, -}; - const static struct vt1724_pcm_reg vt1724_playback_spdif_reg = { .addr = VT1724_MT_PDMA4_ADDR, .size = VT1724_MT_PDMA4_SIZE, @@ -795,8 +810,9 @@ static int snd_vt1724_playback_spdif_ope ice1712_t *ice = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; + runtime->private_data = (void*)VT1724_PDMA4_START; /* irq/status/trigger bit */ ice->playback_con_substream = substream; - runtime->hw = snd_vt1724_playback_spdif; + runtime->hw = snd_vt1724_2ch_stereo; snd_pcm_set_sync(substream); snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); @@ -820,8 +836,9 @@ static int snd_vt1724_capture_spdif_open ice1712_t *ice = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; + runtime->private_data = (void*)VT1724_RDMA1_START; /* irq/status/trigger bit */ ice->capture_con_substream = substream; - runtime->hw = snd_vt1724_playback_spdif; + runtime->hw = snd_vt1724_2ch_stereo; snd_pcm_set_sync(substream); snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates_96); @@ -894,7 +911,8 @@ static int __devinit snd_vt1724_pcm_spdi pcm->info_flags = 0; strcpy(pcm->name, "ICE1724 IEC958"); - snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 64*1024, 64*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(ice->pci), 64*1024, 64*1024); ice->pcm = pcm; @@ -903,6 +921,128 @@ static int __devinit snd_vt1724_pcm_spdi /* + * independent surround PCMs + */ + +const static struct vt1724_pcm_reg vt1724_playback_dma_regs[3] = { + { + .addr = VT1724_MT_PDMA1_ADDR, + .size = VT1724_MT_PDMA1_SIZE, + .count = VT1724_MT_PDMA1_COUNT, + .start = VT1724_PDMA1_START, + .pause = VT1724_PDMA1_PAUSE, + }, + { + .addr = VT1724_MT_PDMA2_ADDR, + .size = VT1724_MT_PDMA2_SIZE, + .count = VT1724_MT_PDMA2_COUNT, + .start = VT1724_PDMA2_START, + .pause = VT1724_PDMA2_PAUSE, + }, + { + .addr = VT1724_MT_PDMA3_ADDR, + .size = VT1724_MT_PDMA3_SIZE, + .count = VT1724_MT_PDMA3_COUNT, + .start = VT1724_PDMA3_START, + .pause = VT1724_PDMA3_PAUSE, + }, +}; + +static int snd_vt1724_playback_indep_prepare(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + unsigned char val; + + spin_lock(&ice->reg_lock); + val = 3 - substream->number; + if (inb(ICEMT1724(ice, BURST)) < val) + outb(val, ICEMT1724(ice, BURST)); + spin_unlock(&ice->reg_lock); + return snd_vt1724_pcm_prepare(substream, &vt1724_playback_dma_regs[substream->number]); +} + +static snd_pcm_uframes_t snd_vt1724_playback_indep_pointer(snd_pcm_substream_t * substream) +{ + return snd_vt1724_pcm_pointer(substream, &vt1724_playback_dma_regs[substream->number]); +} + +static int snd_vt1724_playback_indep_open(snd_pcm_substream_t *substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + down(&ice->open_mutex); + /* already used by PDMA0? */ + if (ice->pcm_reserved[substream->number]) { + up(&ice->open_mutex); + return -EBUSY; /* FIXME: should handle blocking mode properly */ + } + up(&ice->open_mutex); + runtime->private_data = (void*)(1 << (substream->number + 4)); + ice->playback_con_substream_ds[substream->number] = substream; + runtime->hw = snd_vt1724_2ch_stereo; + snd_pcm_set_sync(substream); + snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); + + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates_192); + return 0; +} + +static int snd_vt1724_playback_indep_close(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + + if (PRO_RATE_RESET) + snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 0); + ice->playback_con_substream_ds[substream->number] = NULL; + ice->pcm_reserved[substream->number] = NULL; + + return 0; +} + +static snd_pcm_ops_t snd_vt1724_playback_indep_ops = { + .open = snd_vt1724_playback_indep_open, + .close = snd_vt1724_playback_indep_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_vt1724_pcm_hw_params, + .hw_free = snd_vt1724_pcm_hw_free, + .prepare = snd_vt1724_playback_indep_prepare, + .trigger = snd_vt1724_pcm_trigger, + .pointer = snd_vt1724_playback_indep_pointer, +}; + + +static int __devinit snd_vt1724_pcm_indep(ice1712_t * ice, int device) +{ + snd_pcm_t *pcm; + int play; + int err; + + play = ice->num_total_dacs / 2 - 1; + if (play <= 0) + return 0; + + err = snd_pcm_new(ice->card, "ICE1724 Surrounds", device, play, 0, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_vt1724_playback_indep_ops); + + pcm->private_data = ice; + pcm->info_flags = 0; + strcpy(pcm->name, "ICE1724 Surround PCM"); + + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(ice->pci), 64*1024, 64*1024); + + ice->pcm_ds = pcm; + + return 0; +} + + +/* * Mixer section */ @@ -1808,6 +1948,7 @@ static int __devinit snd_vt1724_create(s ice->vt1724 = 1; spin_lock_init(&ice->reg_lock); init_MUTEX(&ice->gpio_mutex); + init_MUTEX(&ice->open_mutex); ice->gpio.set_mask = snd_vt1724_set_gpio_mask; ice->gpio.set_dir = snd_vt1724_set_gpio_dir; ice->gpio.set_data = snd_vt1724_set_gpio_data; @@ -1932,6 +2073,11 @@ static int __devinit snd_vt1724_probe(st return err; } + if ((err = snd_vt1724_pcm_indep(ice, pcm_dev++)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_vt1724_ac97_mixer(ice)) < 0) { snd_card_free(card); return err; --- linux-2.6.4-rc2/sound/pci/ice1712/prodigy.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/ice1712/prodigy.c 2004-03-07 20:46:50.000000000 -0800 @@ -585,6 +585,7 @@ static int __devinit prodigy_init(ice171 printk(KERN_INFO "ice1724: Apostolos Dimitromanolakis \n"); ice->num_total_dacs = 8; + ice->num_total_adcs = 8; /* to remeber the register values */ ice->akm = snd_kcalloc(sizeof(akm4xxx_t), GFP_KERNEL); --- linux-2.6.4-rc2/sound/pci/ice1712/revo.c 2003-09-27 18:57:48.000000000 -0700 +++ 25/sound/pci/ice1712/revo.c 2004-03-07 20:46:50.000000000 -0800 @@ -128,6 +128,7 @@ static int __devinit revo_init(ice1712_t switch (ice->eeprom.subvendor) { case VT1724_SUBDEVICE_REVOLUTION71: ice->num_total_dacs = 8; + ice->num_total_adcs = 4; break; default: snd_BUG(); --- linux-2.6.4-rc2/sound/pci/intel8x0.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/intel8x0.c 2004-03-07 20:46:50.000000000 -0800 @@ -143,6 +143,9 @@ MODULE_PARM_SYNTAX(mpu_port, SNDRV_ENABL #ifndef PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO #define PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO 0x00da #endif +#ifndef PCI_DEVICE_ID_NVIDIA_CK8S_AUDIO +#define PCI_DEVICE_ID_NVIDIA_CK8S_AUDIO 0x00ea +#endif enum { DEVICE_INTEL, DEVICE_INTEL_ICH4, DEVICE_SIS, DEVICE_ALI, DEVICE_NFORCE }; @@ -396,6 +399,8 @@ struct _snd_intel8x0 { unsigned long remap_bmaddr; struct resource *res_bm; + struct snd_dma_device dma_dev; + struct pci_dev *pci; snd_card_t *card; @@ -420,8 +425,7 @@ struct _snd_intel8x0 { spinlock_t ac97_lock; u32 bdbars_count; - u32 *bdbars; - dma_addr_t bdbars_addr; + struct snd_dma_buffer bdbars; u32 int_sta_reg; /* interrupt status register */ u32 int_sta_mask; /* interrupt status mask */ unsigned int pcm_pos_shift; @@ -443,6 +447,7 @@ static struct pci_device_id snd_intel8x0 { 0x10de, 0x01b1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* NFORCE */ { 0x10de, 0x006a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* NFORCE2 */ { 0x10de, 0x00da, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* NFORCE3 */ + { 0x10de, 0x00ea, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* CK8S */ { 0x1022, 0x746d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* AMD8111 */ { 0x1022, 0x7445, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* AMD768 */ { 0x10b9, 0x5455, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_ALI }, /* Ali5455 */ @@ -804,10 +809,20 @@ static irqreturn_t snd_intel8x0_interrup spin_lock(&chip->reg_lock); status = igetdword(chip, chip->int_sta_reg); if ((status & chip->int_sta_mask) == 0) { - if (status) + static int err_count = 10; + if (status) { + /* ack */ iputdword(chip, chip->int_sta_reg, status); + if (chip->device_type != DEVICE_NFORCE) + status ^= igetdword(chip, chip->int_sta_reg); + } spin_unlock(&chip->reg_lock); - return IRQ_NONE; + if (chip->device_type != DEVICE_NFORCE && status && err_count) { + err_count--; + snd_printd("intel8x0: unknown IRQ bits 0x%x (sta_mask=0x%x)\n", + status, chip->int_sta_mask); + } + return IRQ_RETVAL(status); } for (i = 0; i < chip->bdbars_count; i++) { @@ -1017,6 +1032,7 @@ static snd_pcm_uframes_t snd_intel8x0_pc { intel8x0_t *chip = snd_pcm_substream_chip(substream); ichdev_t *ichdev = get_ichdev(substream); + unsigned long flags; size_t ptr1, ptr; ptr1 = igetword(chip, ichdev->reg_offset + ichdev->roff_picb) << chip->pcm_pos_shift; @@ -1024,7 +1040,9 @@ static snd_pcm_uframes_t snd_intel8x0_pc ptr = ichdev->fragsize1 - ptr1; else ptr = 0; + spin_lock_irqsave(&chip->reg_lock, flags); ptr += ichdev->position; + spin_unlock_irqrestore(&chip->reg_lock, flags); if (ptr >= ichdev->size) return 0; return bytes_to_frames(substream->runtime, ptr); @@ -1079,21 +1097,12 @@ static int snd_intel8x0_pcm_open(snd_pcm { intel8x0_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - static unsigned int i, rates[] = { - /* ATTENTION: these values depend on the definition in pcm.h! */ - 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000 - }; int err; ichdev->substream = substream; runtime->hw = snd_intel8x0_stream; runtime->hw.rates = ichdev->pcm->rates; - for (i = 0; i < ARRAY_SIZE(rates); i++) { - if (runtime->hw.rates & (1 << i)) { - runtime->hw.rate_min = rates[i]; - break; - } - } + snd_pcm_limit_hw_rates(runtime); if (chip->device_type == DEVICE_SIS) { runtime->hw.buffer_bytes_max = 64*1024; runtime->hw.period_bytes_max = 64*1024; @@ -1443,8 +1452,8 @@ static int __devinit snd_intel8x0_pcm1(i strcpy(pcm->name, chip->card->shortname); chip->pcm[device] = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, rec->prealloc_size, - rec->prealloc_max_size); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), + rec->prealloc_size, rec->prealloc_max_size); return 0; } @@ -1666,6 +1675,12 @@ static struct ac97_pcm ac97_pcm_defs[] _ static struct ac97_quirk ac97_quirks[] __devinitdata = { { + .vendor = 0x1014, + .device = 0x1f00, + .name = "MS-9128", + .type = AC97_TUNE_ALC_JACK + }, + { .vendor = 0x1028, .device = 0x00d8, .name = "Dell Precision 530", /* AD1885 */ @@ -1741,6 +1756,12 @@ static struct ac97_quirk ac97_quirks[] _ }, { .vendor = 0x8086, + .device = 0x4856, + .name = "Intel D845WN (82801BA)", + .type = AC97_TUNE_SWAP_HP + }, + { + .vendor = 0x8086, .device = 0x4d44, .name = "Intel D850EMV2", /* AD1885 */ .type = AC97_TUNE_HP_ONLY @@ -1759,6 +1780,7 @@ static struct ac97_quirk ac97_quirks[] _ .name = "Intel ICH5/AD1985", .type = AC97_TUNE_AD_SHARING }, +#if 0 /* FIXME: this seems wrong on most boards */ { .vendor = 0x8086, .device = 0xa000, @@ -1766,6 +1788,7 @@ static struct ac97_quirk ac97_quirks[] _ .name = "Intel ICH5/AD1985", .type = AC97_TUNE_HP_ONLY }, +#endif { } /* terminator */ }; @@ -2116,10 +2139,10 @@ static int snd_intel8x0_free(intel8x0_t /* --- */ synchronize_irq(chip->irq); __hw_end: - if (chip->bdbars) { + if (chip->bdbars.area) { if (chip->fix_nocache) - fill_nocache(chip->bdbars, chip->bdbars_count * sizeof(u32) * ICH_MAX_FRAGS * 2, 0); - snd_free_pci_pages(chip->pci, chip->bdbars_count * sizeof(u32) * ICH_MAX_FRAGS * 2, chip->bdbars, chip->bdbars_addr); + fill_nocache(chip->bdbars.area, chip->bdbars.bytes, 0); + snd_dma_free_pages(&chip->dma_dev, &chip->bdbars); } if (chip->remap_addr) iounmap((void *) chip->remap_addr); @@ -2509,23 +2532,27 @@ static int __devinit snd_intel8x0_create /* SIS7012 handles the pcm data in bytes, others are in words */ chip->pcm_pos_shift = (device_type == DEVICE_SIS) ? 0 : 1; + memset(&chip->dma_dev, 0, sizeof(chip->dma_dev)); + chip->dma_dev.type = SNDRV_DMA_TYPE_DEV; + chip->dma_dev.dev = snd_dma_pci_data(pci); + /* allocate buffer descriptor lists */ /* the start of each lists must be aligned to 8 bytes */ - chip->bdbars = (u32 *)snd_malloc_pci_pages(pci, chip->bdbars_count * sizeof(u32) * ICH_MAX_FRAGS * 2, &chip->bdbars_addr); - if (chip->bdbars == NULL) { + if (snd_dma_alloc_pages(&chip->dma_dev, chip->bdbars_count * sizeof(u32) * ICH_MAX_FRAGS * 2, &chip->bdbars) < 0) { snd_intel8x0_free(chip); + snd_printk(KERN_ERR "intel8x0: cannot allocate buffer descriptors\n"); return -ENOMEM; } /* tables must be aligned to 8 bytes here, but the kernel pages are much bigger, so we don't care (on i386) */ /* workaround for 440MX */ if (chip->fix_nocache) - fill_nocache(chip->bdbars, chip->bdbars_count * sizeof(u32) * ICH_MAX_FRAGS * 2, 1); + fill_nocache(chip->bdbars.area, chip->bdbars.bytes, 1); int_sta_masks = 0; for (i = 0; i < chip->bdbars_count; i++) { ichdev = &chip->ichd[i]; - ichdev->bdbar = chip->bdbars + (i * ICH_MAX_FRAGS * 2); - ichdev->bdbar_addr = chip->bdbars_addr + (i * sizeof(u32) * ICH_MAX_FRAGS * 2); + ichdev->bdbar = ((u32 *)chip->bdbars.area) + (i * ICH_MAX_FRAGS * 2); + ichdev->bdbar_addr = chip->bdbars.addr + (i * sizeof(u32) * ICH_MAX_FRAGS * 2); int_sta_masks |= ichdev->int_sta_mask; } chip->int_sta_reg = device_type == DEVICE_ALI ? ICH_REG_ALI_INTERRUPTSR : ICH_REG_GLOB_STA; @@ -2567,6 +2594,7 @@ static struct shortname_table { { PCI_DEVICE_ID_NVIDIA_MCP_AUDIO, "NVidia nForce" }, { PCI_DEVICE_ID_NVIDIA_MCP2_AUDIO, "NVidia nForce2" }, { PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO, "NVidia nForce3" }, + { PCI_DEVICE_ID_NVIDIA_CK8S_AUDIO, "NVidia CK8S" }, { 0x746d, "AMD AMD8111" }, { 0x7445, "AMD AMD768" }, { 0x5455, "ALi M5455" }, --- linux-2.6.4-rc2/sound/pci/Kconfig 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/Kconfig 2004-03-07 20:46:50.000000000 -0800 @@ -3,21 +3,31 @@ menu "PCI devices" depends on SND!=n && PCI +config SND_AC97_CODEC + tristate + select SND_PCM + config SND_ALI5451 tristate "ALi PCI Audio M5451" depends on SND + select SND_MPU401_UART + select SND_AC97_CODEC help Say 'Y' or 'M' to include support for ALI PCI Audio M5451 sound core. config SND_AZT3328 tristate "Aztech AZF3328 / PCI168 (EXPERIMENTAL)" depends on SND && EXPERIMENTAL + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for Aztech AZF3328 (PCI168) soundcards. config SND_BT87X tristate "Bt87x Audio Capture" depends on SND + select SND_PCM help Say 'Y' or 'M' to include support for recording audio from TV cards based on Brooktree Bt878/Bt879 chips. @@ -25,7 +35,8 @@ config SND_BT87X config SND_CS46XX tristate "Cirrus Logic (Sound Fusion) CS4280/CS461x/CS462x/CS463x" depends on SND - select GAMEPORT + select SND_RAWMIDI + select SND_AC97_CODEC help Say 'Y' or 'M' to include support for Cirrus Logic CS4610 / CS4612 / CS4614 / CS4615 / CS4622 / CS4624 / CS4630 / CS4280 chips. @@ -39,12 +50,18 @@ config SND_CS46XX_NEW_DSP config SND_CS4281 tristate "Cirrus Logic (Sound Fusion) CS4281" depends on SND + select SND_OPL3_LIB + select SND_RAWMIDI + select SND_AC97_CODEC help Say 'Y' or 'M' to include support for Cirrus Logic CS4281. config SND_EMU10K1 tristate "EMU10K1 (SB Live! & Audigy, E-mu APS)" depends on SND + select SND_HWDEP + select SND_RAWMIDI + select SND_AC97_CODEC help Say 'Y' or 'M' to include support for Sound Blaster PCI 512, Live!, Audigy and E-mu APS (partially supported). @@ -52,18 +69,29 @@ config SND_EMU10K1 config SND_KORG1212 tristate "Korg 1212 IO" depends on SND + select SND_PCM help Say 'Y' or 'M' to include support for Korg 1212IO. +config SND_MIXART + tristate "Digigram miXart" + depends on SND + select SND_HWDEP + select SND_PCM + help + Say 'Y' or 'M' to include support for Digigram miXart soundcard. + config SND_NM256 tristate "NeoMagic NM256AV/ZX" depends on SND + select SND_AC97_CODEC help Say 'Y' or 'M' to include support for NeoMagic NM256AV/ZX chips. config SND_RME32 tristate "RME Digi32, 32/8, 32 PRO" depends on SND + select SND_PCM help Say 'Y' or 'M' to include support for RME Digi32, Digi32 PRO and Digi32/8 (Sek'd Prodif32, Prodif96 and Prodif Gold) audio devices. @@ -71,6 +99,7 @@ config SND_RME32 config SND_RME96 tristate "RME Digi96, 96/8, 96/8 PRO" depends on SND + select SND_PCM help Say 'Y' or 'M' to include support for RME Digi96, Digi96/8 and Digi96/8 PRO/PAD/PST. @@ -78,6 +107,7 @@ config SND_RME96 config SND_RME9652 tristate "RME Digi9652 (Hammerfall)" depends on SND + select SND_PCM help Say 'Y' or 'M' to include support for RME Hammerfall (RME Digi9652 / Digi9636) soundcards. @@ -85,6 +115,9 @@ config SND_RME9652 config SND_HDSP tristate "RME Hammerfall DSP Audio" depends on SND + select SND_HWDEP + select SND_RAWMIDI + select SND_PCM help Say 'Y' or 'M' to include support for RME Hammerfall DSP Audio soundcards. @@ -92,6 +125,8 @@ config SND_HDSP config SND_TRIDENT tristate "Trident 4D-Wave DX/NX; SiS 7018" depends on SND + select SND_MPU401_UART + select SND_AC97_CODEC help Say 'Y' or 'M' to include support for Trident 4D-Wave DX/NX and SiS 7018 soundcards. @@ -99,6 +134,9 @@ config SND_TRIDENT config SND_YMFPCI tristate "Yamaha YMF724/740/744/754" depends on SND + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_AC97_CODEC help Say 'Y' or 'M' to include support for Yamaha PCI audio chips - YMF724, YMF724F, YMF740, YMF740C, YMF744, YMF754. @@ -106,12 +144,18 @@ config SND_YMFPCI config SND_ALS4000 tristate "Avance Logic ALS4000" depends on SND + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for Avance Logic ALS4000. config SND_CMIPCI tristate "C-Media 8738, 8338" depends on SND + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for C-Media CMI8338 and 8738 PCI soundcards. @@ -119,12 +163,16 @@ config SND_CMIPCI config SND_ENS1370 tristate "(Creative) Ensoniq AudioPCI 1370" depends on SND + select SND_RAWMIDI + select SND_PCM help Say 'Y' or 'M' to include support for Ensoniq AudioPCI ES1370. config SND_ENS1371 tristate "(Creative) Ensoniq AudioPCI 1371/1373" depends on SND + select SND_RAWMIDI + select SND_AC97_CODEC help Say 'Y' or 'M' to include support for Ensoniq AudioPCI ES1371 and Sound Blaster PCI 64 or 128 soundcards. @@ -132,6 +180,9 @@ config SND_ENS1371 config SND_ES1938 tristate "ESS ES1938/1946/1969 (Solo-1)" depends on SND + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_AC97_CODEC help Say 'Y' or 'M' to include support for ESS Solo-1 (ES1938, ES1946, ES1969) soundcard. @@ -139,24 +190,31 @@ config SND_ES1938 config SND_ES1968 tristate "ESS ES1968/1978 (Maestro-1/2/2E)" depends on SND + select SND_MPU401_UART + select SND_AC97_CODEC help Say 'Y' or 'M' to include support for ESS Maestro 1/2/2E. config SND_MAESTRO3 tristate "ESS Allegro/Maestro3" depends on SND + select SND_AC97_CODEC help Say 'Y' or 'M' to include support for ESS Maestro 3 (Allegro) soundcard. config SND_FM801 tristate "ForteMedia FM801" depends on SND + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_AC97_CODEC help Say 'Y' or 'M' to include support for ForteMedia FM801 based soundcards. -config CONFIG_SND_FM801_TEA575X +config SND_FM801_TEA575X tristate "ForteMedia FM801 + TEA5757 tuner" - depends on SND_FM801 && CONFIG_VIDEO_DEV + depends on SND_FM801 + select VIDEO_DEV help Say 'Y' or 'M' to include support for ForteMedia FM801 based soundcards with TEA5757 tuner connected to GPIO1-3 pins (Media Forte SF256-PCS-02). @@ -164,6 +222,8 @@ config CONFIG_SND_FM801_TEA575X config SND_ICE1712 tristate "ICEnsemble ICE1712 (Envy24)" depends on SND + select SND_MPU401_UART + select SND_AC97_CODEC help Say 'Y' or 'M' to include support for ICE1712 (Envy24) based soundcards. Currently supported hardware is: MidiMan M Audio - Delta 1010(LT), Dio 2496, @@ -173,6 +233,8 @@ config SND_ICE1712 config SND_ICE1724 tristate "ICE/VT1724 (Envy24HT)" depends on SND + select SND_MPU401_UART + select SND_AC97_CODEC help Say 'Y' or 'M' to include support for ICE/VT1724 (Envy24HT) based soundcards. @@ -182,6 +244,8 @@ config SND_ICE1724 config SND_INTEL8X0 tristate "Intel i8x0/MX440, SiS 7012; Ali 5455; NForce Audio; AMD768/8111" depends on SND + select SND_MPU401_UART + select SND_AC97_CODEC help Say 'Y' or 'M' to include support for Intel8x0 based soundcards, SiS 7012, AMD768/8111, NVidia NForce and ALi 5455 chips. @@ -189,18 +253,24 @@ config SND_INTEL8X0 config SND_SONICVIBES tristate "S3 SonicVibes" depends on SND + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_AC97_CODEC help Say 'Y' or 'M' to include support for S3 SonicVibes based soundcards. config SND_VIA82XX tristate "VIA 82C686A/B, 8233 South Bridge" depends on SND + select SND_MPU401_UART + select SND_AC97_CODEC help Say 'Y' or 'M' to include support for VIA VT82C686A/B, VT8233 South Bridge. config SND_VX222 tristate "Digigram VX222" depends on SND + select SND_VX_LIB help Say 'Y' or 'M' to include support for Digigram VX222 soundcards. --- linux-2.6.4-rc2/sound/pci/korg1212/korg1212.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/korg1212/korg1212.c 2004-03-07 20:46:50.000000000 -0800 @@ -347,9 +347,14 @@ struct _snd_korg1212 { struct resource *res_ioport; struct resource *res_iomem2; + struct snd_dma_device dma_dev; + + struct snd_dma_buffer dma_dsp; + struct snd_dma_buffer dma_play; + struct snd_dma_buffer dma_rec; + struct snd_dma_buffer dma_shared; + u32 dspCodeSize; - u32 dspMemPhy; // DSP memory block handle (Physical Address) - void * dspMemPtr; // block memory (Virtual Address) u32 DataBufsSize; @@ -357,6 +362,7 @@ struct _snd_korg1212 { KorgAudioBuffer * recordDataBufsPtr; KorgSharedBuffer * sharedBufferPtr; + u32 RecDataPhy; u32 PlayDataPhy; unsigned long sharedBufferPhy; @@ -1238,10 +1244,10 @@ static int snd_korg1212_downloadDSPCode( snd_korg1212_setCardState(korg1212, K1212_STATE_DSP_IN_PROCESS); - memcpy(korg1212->dspMemPtr, dspCode, korg1212->dspCodeSize); + memcpy(korg1212->dma_dsp.area, dspCode, korg1212->dspCodeSize); rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_StartDSPDownload, - UpperWordSwap(korg1212->dspMemPhy), + UpperWordSwap(korg1212->dma_dsp.addr), 0, 0, 0); #if K1212_DEBUG_LEVEL > 0 @@ -2134,12 +2140,9 @@ snd_korg1212_free(korg1212_t *korg1212) // ---------------------------------------------------- // free up memory resources used for the DSP download. // ---------------------------------------------------- - if (korg1212->dspMemPtr) { - snd_free_pci_pages(korg1212->pci, korg1212->dspCodeSize, - korg1212->dspMemPtr, (dma_addr_t)korg1212->dspMemPhy); - korg1212->dspMemPhy = 0; - korg1212->dspMemPtr = 0; - korg1212->dspCodeSize = 0; + if (korg1212->dma_dsp.area) { + snd_dma_free_pages(&korg1212->dma_dev, &korg1212->dma_dsp); + korg1212->dma_dsp.area = NULL; } #ifndef K1212_LARGEALLOC @@ -2147,18 +2150,14 @@ snd_korg1212_free(korg1212_t *korg1212) // ------------------------------------------------------ // free up memory resources used for the Play/Rec Buffers // ------------------------------------------------------ - if (korg1212->playDataBufsPtr) { - snd_free_pci_pages(korg1212->pci, korg1212->DataBufsSize, - korg1212->playDataBufsPtr, (dma_addr_t)korg1212->PlayDataPhy); - korg1212->PlayDataPhy = 0; - korg1212->playDataBufsPtr = NULL; + if (korg1212->dma_play.area) { + snd_dma_free_pages(&korg1212->dma_dev, &korg1212->dma_play); + korg1212->dma_play.area = NULL; } - if (korg1212->recordDataBufsPtr) { - snd_free_pci_pages(korg1212->pci, korg1212->DataBufsSize, - korg1212->recordDataBufsPtr, (dma_addr_t)korg1212->RecDataPhy); - korg1212->RecDataPhy = 0; - korg1212->recordDataBufsPtr = NULL; + if (korg1212->dma_rec.area) { + snd_dma_free_pages(&korg1212->dma_dev, &korg1212->dma_rec); + korg1212->dma_rec.area = NULL; } #endif @@ -2166,11 +2165,9 @@ snd_korg1212_free(korg1212_t *korg1212) // ---------------------------------------------------- // free up memory resources used for the Shared Buffers // ---------------------------------------------------- - if (korg1212->sharedBufferPtr) { - snd_free_pci_pages(korg1212->pci, (u32) sizeof(KorgSharedBuffer), - korg1212->sharedBufferPtr, (dma_addr_t)korg1212->sharedBufferPhy); - korg1212->sharedBufferPhy = 0; - korg1212->sharedBufferPtr = NULL; + if (korg1212->dma_shared.area) { + snd_dma_free_pages(&korg1212->dma_dev, &korg1212->dma_shared); + korg1212->dma_shared.area = NULL; } snd_magic_kfree(korg1212); @@ -2193,7 +2190,6 @@ static int __devinit snd_korg1212_create int err; unsigned int i; unsigned ioport_size, iomem_size, iomem2_size; - dma_addr_t phys_addr; korg1212_t * korg1212; static snd_device_ops_t ops = { @@ -2332,13 +2328,16 @@ static int __devinit snd_korg1212_create stateName[korg1212->cardState]); #endif - korg1212->sharedBufferPtr = (KorgSharedBuffer *) snd_malloc_pci_pages(korg1212->pci, sizeof(KorgSharedBuffer), &phys_addr); - korg1212->sharedBufferPhy = (unsigned long)phys_addr; + memset(&korg1212->dma_dev, 0, sizeof(korg1212->dma_dev)); + korg1212->dma_dev.type = SNDRV_DMA_TYPE_DEV; + korg1212->dma_dev.dev = snd_dma_pci_data(korg1212->pci); - if (korg1212->sharedBufferPtr == NULL) { + if (snd_dma_alloc_pages(&korg1212->dma_dev, sizeof(KorgSharedBuffer), &korg1212->dma_shared) < 0) { snd_printk(KERN_ERR "can not allocate shared buffer memory (%Zd bytes)\n", sizeof(KorgSharedBuffer)); return -ENOMEM; } + korg1212->sharedBufferPtr = (KorgSharedBuffer *)korg1212->dma_shared.area; + korg1212->sharedBufferPhy = korg1212->dma_shared.addr; #if K1212_DEBUG_LEVEL > 0 K1212_DEBUG_PRINTK("K1212_DEBUG: Shared Buffer Area = 0x%p (0x%08lx), %d bytes\n", korg1212->sharedBufferPtr, korg1212->sharedBufferPhy, sizeof(KorgSharedBuffer)); @@ -2348,30 +2347,28 @@ static int __devinit snd_korg1212_create korg1212->DataBufsSize = sizeof(KorgAudioBuffer) * kNumBuffers; - korg1212->playDataBufsPtr = (KorgAudioBuffer *) snd_malloc_pci_pages(korg1212->pci, korg1212->DataBufsSize, &phys_addr); - korg1212->PlayDataPhy = (u32)phys_addr; - - if (korg1212->playDataBufsPtr == NULL) { + if (snd_dma_alloc_pages(&korg1212->dma_dev, korg1212->DataBufsSize, &korg1212->dma_play) < 0) { snd_printk(KERN_ERR "can not allocate play data buffer memory (%d bytes)\n", korg1212->DataBufsSize); return -ENOMEM; } + korg1212->playDataBufsPtr = (KorgAudioBuffer *)korg1212->dma_play.area; + korg1212->PlayDataPhy = korg1212->dma_play.addr; #if K1212_DEBUG_LEVEL > 0 K1212_DEBUG_PRINTK("K1212_DEBUG: Play Data Area = 0x%p (0x%08x), %d bytes\n", korg1212->playDataBufsPtr, korg1212->PlayDataPhy, korg1212->DataBufsSize); #endif - korg1212->recordDataBufsPtr = (KorgAudioBuffer *) snd_malloc_pci_pages(korg1212->pci, korg1212->DataBufsSize, &phys_addr); - korg1212->RecDataPhy = (u32)phys_addr; - - if (korg1212->recordDataBufsPtr == NULL) { + if (snd_dma_alloc_pages(&korg1212->dma_dev, korg1212->DataBufsSize, &korg1212->dma_rec) < 0) { snd_printk(KERN_ERR "can not allocate record data buffer memory (%d bytes)\n", korg1212->DataBufsSize); return -ENOMEM; } + korg1212->recordDataBufsPtr = (KorgAudioBuffer *)korg1212->dma_rec.area; + korg1212->RecDataPhy = korg1212->dma_rec.addr; #if K1212_DEBUG_LEVEL > 0 K1212_DEBUG_PRINTK("K1212_DEBUG: Record Data Area = 0x%p (0x%08x), %d bytes\n", - korg1212->recordDataBufsPtr, korg1212->RecDataPhy, korg1212->DataBufsSize); + korg1212->recordDataBufsPtr, korg1212->RecDataBufsPhy, korg1212->DataBufsSize); #endif #else // K1212_LARGEALLOC @@ -2392,17 +2389,14 @@ static int __devinit snd_korg1212_create korg1212->AdatTimeCodePhy = korg1212->sharedBufferPhy + offsetof(KorgSharedBuffer, AdatTimeCode); - korg1212->dspMemPtr = snd_malloc_pci_pages(korg1212->pci, korg1212->dspCodeSize, &phys_addr); - korg1212->dspMemPhy = (u32)phys_addr; - - if (korg1212->dspMemPtr == NULL) { + if (snd_dma_alloc_pages(&korg1212->dma_dev, korg1212->dspCodeSize, &korg1212->dma_dsp) < 0) { snd_printk(KERN_ERR "can not allocate dsp code memory (%d bytes)\n", korg1212->dspCodeSize); return -ENOMEM; } #if K1212_DEBUG_LEVEL > 0 K1212_DEBUG_PRINTK("K1212_DEBUG: DSP Code area = 0x%p (0x%08x) %d bytes [%s]\n", - korg1212->dspMemPtr, korg1212->dspMemPhy, korg1212->dspCodeSize, + korg1212->dma_dsp.area, korg1212->dma_dsp.addr, korg1212->dspCodeSize, stateName[korg1212->cardState]); #endif @@ -2425,7 +2419,7 @@ static int __devinit snd_korg1212_create "VolumeTablePhy = %08x L[%08x]\n" "RoutingTablePhy = %08x L[%08x]\n" "AdatTimeCodePhy = %08x L[%08x]\n", - korg1212->dspMemPhy, UpperWordSwap(korg1212->dspMemPhy), + korg1212->dma_dsp.addr, UpperWordSwap(korg1212->dma_dsp.addr), korg1212->PlayDataPhy, LowerWordSwap(korg1212->PlayDataPhy), korg1212->RecDataPhy, LowerWordSwap(korg1212->RecDataPhy), korg1212->VolumeTablePhy, LowerWordSwap(korg1212->VolumeTablePhy), --- linux-2.6.4-rc2/sound/pci/maestro3.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/maestro3.c 2004-03-07 20:46:50.000000000 -0800 @@ -1816,7 +1816,8 @@ snd_m3_pcm(m3_t * chip, int device) strcpy(pcm->name, chip->card->driver); chip->pcm = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 64*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), 64*1024, 64*1024); return 0; } --- linux-2.6.4-rc2/sound/pci/Makefile 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/Makefile 2004-03-07 20:46:50.000000000 -0800 @@ -38,4 +38,16 @@ obj-$(CONFIG_SND_RME96) += snd-rme96.o obj-$(CONFIG_SND_SONICVIBES) += snd-sonicvibes.o obj-$(CONFIG_SND_VIA82XX) += snd-via82xx.o -obj-$(CONFIG_SND) += ac97/ ali5451/ cs46xx/ emu10k1/ korg1212/ nm256/ rme9652/ trident/ ymfpci/ ice1712/ vx222/ +obj-$(CONFIG_SND) += \ + ac97/ \ + ali5451/ \ + cs46xx/ \ + emu10k1/ \ + ice1712/ \ + korg1212/ \ + mixart/ \ + nm256/ \ + rme9652/ \ + trident/ \ + ymfpci/ \ + vx222/ --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/sound/pci/mixart/Makefile 2004-03-07 20:46:50.000000000 -0800 @@ -0,0 +1,8 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +snd-mixart-objs := mixart.o mixart_core.o mixart_hwdep.o mixart_mixer.o + +obj-$(CONFIG_SND_MIXART) += snd-mixart.o --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/sound/pci/mixart/mixart.c 2004-03-07 20:46:50.000000000 -0800 @@ -0,0 +1,1473 @@ +/* + * Driver for Digigram miXart soundcards + * + * main file with alsa callbacks + * + * Copyright (c) 2003 by Digigram + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#define SNDRV_GET_ID +#include +#include +#include +#include +#include +#include "mixart.h" +#include "mixart_hwdep.h" +#include "mixart_core.h" +#include "mixart_mixer.h" + +#define CARD_NAME "miXart" + +MODULE_AUTHOR("Digigram "); +MODULE_DESCRIPTION("Digigram " CARD_NAME); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Digigram," CARD_NAME "}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ + +#define chip_t mixart_t + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for Digigram " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for Digigram " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable Digigram " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); + +/* + */ + +static struct pci_device_id snd_mixart_ids[] = { + { 0x1057, 0x0003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* MC8240 */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_mixart_ids); + + +static int mixart_set_pipe_state(mixart_mgr_t *mgr, mixart_pipe_t* pipe, int start) +{ + mixart_group_state_req_t group_state; + mixart_group_state_resp_t group_state_resp; + mixart_msg_t request; + int err; + u32 system_msg_uid; + + switch(pipe->status) { + case PIPE_RUNNING: + case PIPE_CLOCK_SET: + if(start) return 0; /* already started */ + break; + case PIPE_STOPPED: + if(!start) return 0; /* already stopped */ + break; + default: + snd_printk(KERN_ERR "error mixart_set_pipe_state called with wrong pipe->status!\n"); + return -EINVAL; /* function called with wrong pipe status */ + } + + system_msg_uid = 0x12345678; /* the event ! (take care: the MSB and two LSB's have to be 0) */ + + /* wait on the last MSG_SYSTEM_SEND_SYNCHRO_CMD command to be really finished */ + + request.message_id = MSG_SYSTEM_WAIT_SYNCHRO_CMD; + request.uid = (mixart_uid_t){0,0}; + request.data = &system_msg_uid; + request.size = sizeof(system_msg_uid); + + err = snd_mixart_send_msg_wait_notif(mgr, &request, system_msg_uid); + if(err) { + snd_printk(KERN_ERR "error : MSG_SYSTEM_WAIT_SYNCHRO_CMD was not notified !\n"); + return err; + } + + /* start or stop the pipe (1 pipe) */ + + memset(&group_state, 0, sizeof(group_state)); + group_state.pipe_count = 1; + group_state.pipe_uid[0] = pipe->group_uid; + + if(start) + request.message_id = MSG_STREAM_START_STREAM_GRP_PACKET; + else + request.message_id = MSG_STREAM_STOP_STREAM_GRP_PACKET; + + request.uid = pipe->group_uid; /*(mixart_uid_t){0,0};*/ + request.data = &group_state; + request.size = sizeof(group_state); + + err = snd_mixart_send_msg(mgr, &request, sizeof(group_state_resp), &group_state_resp); + if (err < 0 || group_state_resp.txx_status != 0) { + snd_printk(KERN_ERR "error MSG_STREAM_ST***_STREAM_GRP_PACKET err=%x stat=%x !\n", err, group_state_resp.txx_status); + return -EINVAL; + } + + if(start) { + u32 stat; + + group_state.pipe_count = 0; /* in case of start same command once again with pipe_count=0 */ + + err = snd_mixart_send_msg(mgr, &request, sizeof(group_state_resp), &group_state_resp); + if (err < 0 || group_state_resp.txx_status != 0) { + snd_printk(KERN_ERR "error MSG_STREAM_START_STREAM_GRP_PACKET err=%x stat=%x !\n", err, group_state_resp.txx_status); + return -EINVAL; + } + + /* in case of start send a synchro top */ + + request.message_id = MSG_SYSTEM_SEND_SYNCHRO_CMD; + request.uid = (mixart_uid_t){0,0}; + request.data = NULL; + request.size = 0; + + err = snd_mixart_send_msg(mgr, &request, sizeof(stat), &stat); + if (err < 0 || stat != 0) { + snd_printk(KERN_ERR "error MSG_SYSTEM_SEND_SYNCHRO_CMD err=%x stat=%x !\n", err, stat); + return -EINVAL; + } + + pipe->status = PIPE_RUNNING; + } + else /* !start */ + pipe->status = PIPE_STOPPED; + + return 0; +} + + +static int mixart_set_clock(mixart_mgr_t *mgr, mixart_pipe_t *pipe, unsigned int rate) +{ + mixart_msg_t request; + mixart_clock_properties_t clock_properties; + mixart_clock_properties_resp_t clock_prop_resp; + int err; + + switch(pipe->status) { + case PIPE_CLOCK_SET: + break; + case PIPE_RUNNING: + if(rate != 0) + break; + default: + if(rate == 0) + return 0; /* nothing to do */ + else { + snd_printk(KERN_ERR "error mixart_set_clock(%d) called with wrong pipe->status !\n", rate); + return -EINVAL; + } + } + + memset(&clock_properties, 0, sizeof(clock_properties)); + clock_properties.clock_generic_type = (rate != 0) ? CGT_INTERNAL_CLOCK : CGT_NO_CLOCK; + clock_properties.clock_mode = CM_STANDALONE; + clock_properties.frequency = rate; + clock_properties.nb_callers = 1; /* only one entry in uid_caller ! */ + clock_properties.uid_caller[0] = pipe->group_uid; + + snd_printdd("mixart_set_clock to %d kHz\n", rate); + + request.message_id = MSG_CLOCK_SET_PROPERTIES; + request.uid = mgr->uid_console_manager; + request.data = &clock_properties; + request.size = sizeof(clock_properties); + + err = snd_mixart_send_msg(mgr, &request, sizeof(clock_prop_resp), &clock_prop_resp); + if (err < 0 || clock_prop_resp.status != 0 || clock_prop_resp.clock_mode != CM_STANDALONE) { + snd_printk(KERN_ERR "error MSG_CLOCK_SET_PROPERTIES err=%x stat=%x mod=%x !\n", err, clock_prop_resp.status, clock_prop_resp.clock_mode); + return -EINVAL; + } + + if(rate) pipe->status = PIPE_CLOCK_SET; + else pipe->status = PIPE_RUNNING; + + return 0; +} + + +/* + * Allocate or reference output pipe for analog IOs (pcmp0/1) + */ +mixart_pipe_t* snd_mixart_add_ref_pipe( mixart_t *chip, int pcm_number, int capture, int monitoring) +{ + int stream_count; + mixart_pipe_t *pipe; + mixart_msg_t request; + + if(capture) { + if (pcm_number == MIXART_PCM_ANALOG) { + pipe = &(chip->pipe_in_ana); /* analog inputs */ + } else { + pipe = &(chip->pipe_in_dig); /* digital inputs */ + } + request.message_id = MSG_STREAM_ADD_OUTPUT_GROUP; + stream_count = MIXART_CAPTURE_STREAMS; + } else { + if (pcm_number == MIXART_PCM_ANALOG) { + pipe = &(chip->pipe_out_ana); /* analog outputs */ + } else { + pipe = &(chip->pipe_out_dig); /* digital outputs */ + } + request.message_id = MSG_STREAM_ADD_INPUT_GROUP; + stream_count = MIXART_PLAYBACK_STREAMS; + } + + /* a new stream is opened and there are already all streams in use */ + if( (monitoring == 0) && (pipe->references >= stream_count) ) { + return NULL; + } + + /* pipe is not yet defined */ + if( pipe->status == PIPE_UNDEFINED ) { + int err, i; + mixart_streaming_group_t streaming_group_resp; + mixart_streaming_group_req_t streaming_group_req; + + snd_printdd("add_ref_pipe audio chip(%d) pcm(%d)\n", chip->chip_idx, pcm_number); + + request.uid = (mixart_uid_t){0,0}; /* should be StreamManagerUID, but zero is OK if there is only one ! */ + request.data = &streaming_group_req; + request.size = sizeof(streaming_group_req); + + memset(&streaming_group_req, 0, sizeof(streaming_group_req)); + + streaming_group_req.stream_count = stream_count; + streaming_group_req.channel_count = 2; + streaming_group_req.latency = 256; + streaming_group_req.connector = pipe->uid_left_connector; /* the left connector */ + + for (i=0; ichip_idx * MIXART_MAX_STREAM_PER_CARD) + (pcm_number * (MIXART_PLAYBACK_STREAMS + MIXART_CAPTURE_STREAMS)) + i; + if(capture) j += MIXART_PLAYBACK_STREAMS; /* in the array capture is behind playback */ + + streaming_group_req.flow_entry[i] = j; + + flowinfo = (struct mixart_flowinfo *)chip->mgr->flowinfo.area; + flowinfo[j].bufferinfo_array_phy_address = (u32)chip->mgr->bufferinfo.addr + (j * sizeof(mixart_bufferinfo_t)); + flowinfo[j].bufferinfo_count = 1; /* 1 will set the miXart to ring-buffer mode ! */ + + bufferinfo = (struct mixart_bufferinfo *)chip->mgr->bufferinfo.area; + bufferinfo[j].buffer_address = 0; /* buffer is not yet allocated */ + bufferinfo[j].available_length = 0; /* buffer is not yet allocated */ + + /* construct the identifier of the stream buffer received in the interrupts ! */ + bufferinfo[j].buffer_id = (chip->chip_idx << MIXART_NOTIFY_CARD_OFFSET) + (pcm_number << MIXART_NOTIFY_PCM_OFFSET ) + i; + if(capture) { + bufferinfo[j].buffer_id |= MIXART_NOTIFY_CAPT_MASK; + } + } + + err = snd_mixart_send_msg(chip->mgr, &request, sizeof(streaming_group_resp), &streaming_group_resp); + if((err < 0) || (streaming_group_resp.status != 0)) { + snd_printk(KERN_ERR "error MSG_STREAM_ADD_**PUT_GROUP err=%x stat=%x !\n", err, streaming_group_resp.status); + return NULL; + } + + pipe->group_uid = streaming_group_resp.group; /* id of the pipe, as returned by embedded */ + pipe->stream_count = streaming_group_resp.stream_count; + /* pipe->stream_uid[i] = streaming_group_resp.stream[i].stream_uid; */ + + pipe->status = PIPE_STOPPED; + } + + if(monitoring) pipe->monitoring = 1; + else pipe->references++; + + return pipe; +} + + +int snd_mixart_kill_ref_pipe( mixart_mgr_t *mgr, mixart_pipe_t *pipe, int monitoring) +{ + int err = 0; + + if(pipe->status == PIPE_UNDEFINED) + return 0; + + if(monitoring) + pipe->monitoring = 0; + else + pipe->references--; + + if((pipe->references <= 0) && (pipe->monitoring == 0)) { + + mixart_msg_t request; + mixart_delete_group_resp_t delete_resp; + + /* release the clock */ + err = mixart_set_clock( mgr, pipe, 0); + if( err < 0 ) { + snd_printk(KERN_ERR "mixart_set_clock(0) return error!\n"); + } + + /* stop the pipe */ + err = mixart_set_pipe_state(mgr, pipe, 0); + if( err < 0 ) { + snd_printk(KERN_ERR "error stopping pipe!\n"); + } + + request.message_id = MSG_STREAM_DELETE_GROUP; + request.uid = (mixart_uid_t){0,0}; + request.data = &pipe->group_uid; /* the streaming group ! */ + request.size = sizeof(pipe->group_uid); + + /* delete the pipe */ + err = snd_mixart_send_msg(mgr, &request, sizeof(delete_resp), &delete_resp); + if ((err < 0) || (delete_resp.status != 0)) { + snd_printk(KERN_ERR "error MSG_STREAM_DELETE_GROUP err(%x), status(%x)\n", err, delete_resp.status); + } + + pipe->group_uid = (mixart_uid_t){0,0}; + pipe->stream_count = 0; + pipe->status = PIPE_UNDEFINED; + } + + return err; +} + +static int mixart_set_stream_state(mixart_stream_t *stream, int start) +{ + mixart_t *chip; + mixart_stream_state_req_t stream_state_req; + mixart_msg_t request; + + if(!stream->substream) + return -EINVAL; + + memset(&stream_state_req, 0, sizeof(stream_state_req)); + stream_state_req.stream_count = 1; + stream_state_req.stream_info.stream_desc.uid_pipe = stream->pipe->group_uid; + stream_state_req.stream_info.stream_desc.stream_idx = stream->substream->number; + + if (stream->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + request.message_id = start ? MSG_STREAM_START_INPUT_STAGE_PACKET : MSG_STREAM_STOP_INPUT_STAGE_PACKET; + else + request.message_id = start ? MSG_STREAM_START_OUTPUT_STAGE_PACKET : MSG_STREAM_STOP_OUTPUT_STAGE_PACKET; + + request.uid = (mixart_uid_t){0,0}; + request.data = &stream_state_req; + request.size = sizeof(stream_state_req); + + stream->abs_period_elapsed = 0; /* reset stream pos */ + stream->buf_periods = 0; + stream->buf_period_frag = 0; + + chip = snd_pcm_substream_chip(stream->substream); + + return snd_mixart_send_msg_nonblock(chip->mgr, &request); +} + +/* + * Trigger callback + */ + +static int snd_mixart_trigger(snd_pcm_substream_t *subs, int cmd) +{ + mixart_stream_t *stream = (mixart_stream_t*)subs->runtime->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + + snd_printdd("SNDRV_PCM_TRIGGER_START\n"); + + /* START_STREAM */ + if( mixart_set_stream_state(stream, 1) ) + return -EINVAL; + + stream->status = MIXART_STREAM_STATUS_RUNNING; + + break; + case SNDRV_PCM_TRIGGER_STOP: + + /* STOP_STREAM */ + if( mixart_set_stream_state(stream, 0) ) + return -EINVAL; + + stream->status = MIXART_STREAM_STATUS_OPEN; + + snd_printdd("SNDRV_PCM_TRIGGER_STOP\n"); + + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + /* TODO */ + stream->status = MIXART_STREAM_STATUS_PAUSE; + snd_printdd("SNDRV_PCM_PAUSE_PUSH\n"); + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + /* TODO */ + stream->status = MIXART_STREAM_STATUS_RUNNING; + snd_printdd("SNDRV_PCM_PAUSE_RELEASE\n"); + break; + default: + return -EINVAL; + } + return 0; +} + +static int mixart_sync_nonblock_events(mixart_mgr_t *mgr) +{ + int timeout = HZ; + while (atomic_read(&mgr->msg_processed) > 0) { + if (! timeout--) { + snd_printk(KERN_ERR "mixart: cannot process nonblock events!\n"); + return -EBUSY; + } + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } + return 0; +} + +/* + * prepare callback for all pcms + * + * NOTE: this callback is non-atomic (pcm->info_flags |= SNDRV_PCM_INFO_NONATOMIC_OPS) + */ +static int snd_mixart_prepare(snd_pcm_substream_t *subs) +{ + mixart_t *chip = snd_pcm_substream_chip(subs); + mixart_stream_t *stream = (mixart_stream_t*)subs->runtime->private_data; + + /* TODO de façon non bloquante, réappliquer les hw_params (rate, bits, codec) */ + + snd_printdd("snd_mixart_prepare\n"); + + mixart_sync_nonblock_events(chip->mgr); + + /* only the first stream can choose the sample rate */ + /* the further opened streams will be limited to its frequency (see open) */ + if(chip->mgr->ref_count_rate == 1) + chip->mgr->sample_rate = subs->runtime->rate; + + /* set the clock only once (first stream) on the same pipe */ + if(stream->pipe->references == 1) { + if( mixart_set_clock(chip->mgr, stream->pipe, subs->runtime->rate) ) + return -EINVAL; + } + + return 0; +} + + +static int mixart_set_format(mixart_stream_t *stream, snd_pcm_format_t format) +{ + int err; + mixart_t *chip; + mixart_msg_t request; + mixart_stream_param_desc_t stream_param; + mixart_return_uid_t resp; + + chip = snd_pcm_substream_chip(stream->substream); + + memset(&stream_param, 0, sizeof(stream_param)); + + stream_param.coding_type = CT_LINEAR; + stream_param.number_of_channel = stream->channels; + + stream_param.sampling_freq = chip->mgr->sample_rate; + if(stream_param.sampling_freq == 0) + stream_param.sampling_freq = 44100; /* if frequency not yet defined, use some default */ + + switch(format){ + case SNDRV_PCM_FORMAT_U8: + stream_param.sample_type = ST_INTEGER_8; + stream_param.sample_size = 8; + break; + case SNDRV_PCM_FORMAT_S16_LE: + stream_param.sample_type = ST_INTEGER_16LE; + stream_param.sample_size = 16; + break; + case SNDRV_PCM_FORMAT_S16_BE: + stream_param.sample_type = ST_INTEGER_16BE; + stream_param.sample_size = 16; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + stream_param.sample_type = ST_INTEGER_24LE; + stream_param.sample_size = 24; + break; + case SNDRV_PCM_FORMAT_S24_3BE: + stream_param.sample_type = ST_INTEGER_24BE; + stream_param.sample_size = 24; + break; + case SNDRV_PCM_FMTBIT_FLOAT_LE: + stream_param.sample_type = ST_FLOATING_POINT_32LE; + stream_param.sample_size = 32; + break; + case SNDRV_PCM_FMTBIT_FLOAT_BE: + stream_param.sample_type = ST_FLOATING_POINT_32BE; + stream_param.sample_size = 32; + break; + default: + snd_printk(KERN_ERR "error mixart_set_format() : unknown format\n"); + return -EINVAL; + } + + snd_printdd("set SNDRV_PCM_FORMAT sample_type(%d) sample_size(%d) freq(%d) channels(%d)\n", + stream_param.sample_type, stream_param.sample_size, stream_param.sampling_freq, stream->channels); + + /* TODO: what else to configure ? */ + /* stream_param.samples_per_frame = 2; */ + /* stream_param.bytes_per_frame = 4; */ + /* stream_param.bytes_per_sample = 2; */ + + stream_param.pipe_count = 1; /* set to 1 */ + stream_param.stream_count = 1; /* set to 1 */ + stream_param.stream_desc[0].uid_pipe = stream->pipe->group_uid; + stream_param.stream_desc[0].stream_idx = stream->substream->number; + + request.message_id = MSG_STREAM_SET_INPUT_STAGE_PARAM; + request.uid = (mixart_uid_t){0,0}; + request.data = &stream_param; + request.size = sizeof(stream_param); + + err = snd_mixart_send_msg(chip->mgr, &request, sizeof(resp), &resp); + if((err < 0) || resp.error_code) { + snd_printk(KERN_ERR "MSG_STREAM_SET_INPUT_STAGE_PARAM err=%x; resp=%x\n", err, resp.error_code); + return -EINVAL; + } + return 0; +} + + +/* + * HW_PARAMS callback for all pcms + */ +static int snd_mixart_hw_params(snd_pcm_substream_t *subs, + snd_pcm_hw_params_t *hw) +{ + mixart_t *chip = snd_pcm_substream_chip(subs); + mixart_mgr_t *mgr = chip->mgr; + mixart_stream_t *stream = (mixart_stream_t*)subs->runtime->private_data; + snd_pcm_format_t format; + int err; + int channels; + + /* set up channels */ + channels = params_channels(hw); + + /* set up format for the stream */ + format = params_format(hw); + + down(&mgr->setup_mutex); + + /* update the stream levels */ + if( stream->pcm_number <= MIXART_PCM_DIGITAL ) { + int is_aes = stream->pcm_number > MIXART_PCM_ANALOG; + if( subs->stream == SNDRV_PCM_STREAM_PLAYBACK ) + mixart_update_playback_stream_level(chip, is_aes, subs->number); + else + mixart_update_capture_stream_level( chip, is_aes); + } + + stream->channels = channels; + + /* set the format to the board */ + err = mixart_set_format(stream, format); + if(err < 0) { + return err; + } + + /* allocate buffer */ + err = snd_pcm_lib_malloc_pages(subs, params_buffer_bytes(hw)); + + if (err > 0) { + struct mixart_bufferinfo *bufferinfo; + int i = (chip->chip_idx * MIXART_MAX_STREAM_PER_CARD) + (stream->pcm_number * (MIXART_PLAYBACK_STREAMS+MIXART_CAPTURE_STREAMS)) + subs->number; + if( subs->stream == SNDRV_PCM_STREAM_CAPTURE ) { + i += MIXART_PLAYBACK_STREAMS; /* in array capture is behind playback */ + } + + bufferinfo = (struct mixart_bufferinfo *)chip->mgr->bufferinfo.area; + bufferinfo[i].buffer_address = subs->runtime->dma_addr; + bufferinfo[i].available_length = subs->runtime->dma_bytes; + /* bufferinfo[i].buffer_id is already defined */ + + snd_printdd("snd_mixart_hw_params(pcm %d) : dma_addr(%x) dma_bytes(%x) subs-number(%d)\n", i, subs->runtime->dma_addr, subs->runtime->dma_bytes, subs->number); + } + up(&mgr->setup_mutex); + + return err; +} + +static int snd_mixart_hw_free(snd_pcm_substream_t *subs) +{ + mixart_t *chip = snd_pcm_substream_chip(subs); + snd_pcm_lib_free_pages(subs); + mixart_sync_nonblock_events(chip->mgr); + return 0; +} + + + +/* + * TODO CONFIGURATION SPACE for all pcms, mono pcm must update channels_max + */ +static snd_pcm_hardware_t snd_mixart_analog_caps = +{ + .info = ( SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_PAUSE), + .formats = ( SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | + SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE ), + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (32*1024), + .period_bytes_min = 256, /* 256 frames U8 mono*/ + .period_bytes_max = (16*1024), + .periods_min = 2, + .periods_max = (32*1024/256), +}; + +static snd_pcm_hardware_t snd_mixart_digital_caps = +{ + .info = ( SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_PAUSE), + .formats = ( SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | + SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE ), + .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, + .rate_min = 32000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (32*1024), + .period_bytes_min = 256, /* 256 frames U8 mono*/ + .period_bytes_max = (16*1024), + .periods_min = 2, + .periods_max = (32*1024/256), +}; + + +static int snd_mixart_playback_open(snd_pcm_substream_t *subs) +{ + mixart_t *chip = snd_pcm_substream_chip(subs); + mixart_mgr_t *mgr = chip->mgr; + snd_pcm_runtime_t *runtime = subs->runtime; + snd_pcm_t *pcm = subs->pcm; + mixart_stream_t *stream; + mixart_pipe_t *pipe; + int err = 0; + int pcm_number; + + down(&mgr->setup_mutex); + + if ( pcm == chip->pcm ) { + pcm_number = MIXART_PCM_ANALOG; + runtime->hw = snd_mixart_analog_caps; + } else { + snd_assert ( pcm == chip->pcm_dig ); + pcm_number = MIXART_PCM_DIGITAL; + runtime->hw = snd_mixart_digital_caps; + } + snd_printdd("snd_mixart_playback_open C%d/P%d/Sub%d\n", chip->chip_idx, pcm_number, subs->number); + + /* get stream info */ + stream = &(chip->playback_stream[pcm_number][subs->number]); + + if (stream->status != MIXART_STREAM_STATUS_FREE){ + /* streams in use */ + snd_printk(KERN_ERR "snd_mixart_playback_open C%d/P%d/Sub%d in use\n", chip->chip_idx, pcm_number, subs->number); + err = -EBUSY; + goto _exit_open; + } + + /* get pipe pointer (out pipe) */ + pipe = snd_mixart_add_ref_pipe(chip, pcm_number, 0, 0); + + if (pipe == NULL) { + err = -EINVAL; + goto _exit_open; + } + + /* start the pipe if necessary */ + err = mixart_set_pipe_state(chip->mgr, pipe, 1); + if( err < 0 ) { + snd_printk(KERN_ERR "error starting pipe!\n"); + snd_mixart_kill_ref_pipe(chip->mgr, pipe, 0); + err = -EINVAL; + goto _exit_open; + } + + stream->pipe = pipe; + stream->pcm_number = pcm_number; + stream->status = MIXART_STREAM_STATUS_OPEN; + stream->substream = subs; + stream->channels = 0; /* not configured yet */ + + runtime->private_data = stream; + + snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); + snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 64); + + /* if a sample rate is already used, another stream cannot change */ + if(mgr->ref_count_rate++) { + if(mgr->sample_rate) { + runtime->hw.rate_min = runtime->hw.rate_max = mgr->sample_rate; + } + } + + _exit_open: + up(&mgr->setup_mutex); + + return err; +} + + +static int snd_mixart_capture_open(snd_pcm_substream_t *subs) +{ + mixart_t *chip = snd_pcm_substream_chip(subs); + mixart_mgr_t *mgr = chip->mgr; + snd_pcm_runtime_t *runtime = subs->runtime; + snd_pcm_t *pcm = subs->pcm; + mixart_stream_t *stream; + mixart_pipe_t *pipe; + int err = 0; + int pcm_number; + + down(&mgr->setup_mutex); + + if ( pcm == chip->pcm ) { + pcm_number = MIXART_PCM_ANALOG; + runtime->hw = snd_mixart_analog_caps; + } else { + snd_assert ( pcm == chip->pcm_dig ); + pcm_number = MIXART_PCM_DIGITAL; + runtime->hw = snd_mixart_digital_caps; + } + + runtime->hw.channels_min = 2; /* for instance, no mono */ + + snd_printdd("snd_mixart_capture_open C%d/P%d/Sub%d\n", chip->chip_idx, pcm_number, subs->number); + + /* get stream info */ + stream = &(chip->capture_stream[pcm_number]); + + if (stream->status != MIXART_STREAM_STATUS_FREE){ + /* streams in use */ + snd_printk(KERN_ERR "snd_mixart_capture_open C%d/P%d/Sub%d in use\n", chip->chip_idx, pcm_number, subs->number); + err = -EBUSY; + goto _exit_open; + } + + /* get pipe pointer (in pipe) */ + pipe = snd_mixart_add_ref_pipe(chip, pcm_number, 1, 0); + + if (pipe == NULL) { + err = -EINVAL; + goto _exit_open; + } + + /* start the pipe if necessary */ + err = mixart_set_pipe_state(chip->mgr, pipe, 1); + if( err < 0 ) { + snd_printk(KERN_ERR "error starting pipe!\n"); + snd_mixart_kill_ref_pipe(chip->mgr, pipe, 0); + err = -EINVAL; + goto _exit_open; + } + + stream->pipe = pipe; + stream->pcm_number = pcm_number; + stream->status = MIXART_STREAM_STATUS_OPEN; + stream->substream = subs; + stream->channels = 0; /* not configured yet */ + + runtime->private_data = stream; + + snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); + snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 64); + + /* if a sample rate is already used, another stream cannot change */ + if(mgr->ref_count_rate++) { + if(mgr->sample_rate) { + runtime->hw.rate_min = runtime->hw.rate_max = mgr->sample_rate; + } + } + + _exit_open: + up(&mgr->setup_mutex); + + return err; +} + + + +static int snd_mixart_close(snd_pcm_substream_t *subs) +{ + mixart_t *chip = snd_pcm_substream_chip(subs); + mixart_mgr_t *mgr = chip->mgr; + mixart_stream_t *stream = (mixart_stream_t*)subs->runtime->private_data; + + down(&mgr->setup_mutex); + + snd_printdd("snd_mixart_close C%d/P%d/Sub%d\n", chip->chip_idx, stream->pcm_number, subs->number); + + /* sample rate released */ + if(--mgr->ref_count_rate == 0) { + mgr->sample_rate = 0; + } + + /* delete pipe */ + if (snd_mixart_kill_ref_pipe(mgr, stream->pipe, 0 ) < 0) { + + snd_printk(KERN_ERR "error snd_mixart_kill_ref_pipe C%dP%d\n", chip->chip_idx, stream->pcm_number); + } + + stream->pipe = NULL; + stream->status = MIXART_STREAM_STATUS_FREE; + stream->substream = NULL; + + up(&mgr->setup_mutex); + return 0; +} + + +static snd_pcm_uframes_t snd_mixart_stream_pointer(snd_pcm_substream_t * subs) +{ + snd_pcm_runtime_t *runtime = subs->runtime; + mixart_stream_t *stream = (mixart_stream_t*)runtime->private_data; + + return (snd_pcm_uframes_t)((stream->buf_periods * runtime->period_size) + stream->buf_period_frag); +} + + + +static snd_pcm_ops_t snd_mixart_playback_ops = { + .open = snd_mixart_playback_open, + .close = snd_mixart_close, + .ioctl = snd_pcm_lib_ioctl, + .prepare = snd_mixart_prepare, + .hw_params = snd_mixart_hw_params, + .hw_free = snd_mixart_hw_free, + .trigger = snd_mixart_trigger, + .pointer = snd_mixart_stream_pointer, +}; + +static snd_pcm_ops_t snd_mixart_capture_ops = { + .open = snd_mixart_capture_open, + .close = snd_mixart_close, + .ioctl = snd_pcm_lib_ioctl, + .prepare = snd_mixart_prepare, + .hw_params = snd_mixart_hw_params, + .hw_free = snd_mixart_hw_free, + .trigger = snd_mixart_trigger, + .pointer = snd_mixart_stream_pointer, +}; + +static void preallocate_buffers(mixart_t *chip, snd_pcm_t *pcm) +{ + snd_pcm_substream_t *subs; + int stream; + + for (stream = 0; stream < 2; stream++) { + int idx = 0; + for (subs = pcm->streams[stream].substream; subs; subs = subs->next, idx++) + /* set up the unique device id with the chip index */ + subs->dma_device.id = subs->pcm->device << 16 | + subs->stream << 8 | (subs->number + 1) | + (chip->chip_idx + 1) << 24; + } + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->mgr->pci), 32*1024, 32*1024); +} + +/* + */ +static int snd_mixart_pcm_analog(mixart_t *chip) +{ + int err; + snd_pcm_t *pcm; + char name[32]; + + sprintf(name, "miXart analog %d", chip->chip_idx); + if ((err = snd_pcm_new(chip->card, name, MIXART_PCM_ANALOG, + MIXART_PLAYBACK_STREAMS, + MIXART_CAPTURE_STREAMS, &pcm)) < 0) { + snd_printk(KERN_ERR "cannot create the analog pcm %d\n", chip->chip_idx); + return err; + } + + pcm->private_data = chip; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_mixart_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_mixart_capture_ops); + + pcm->info_flags = SNDRV_PCM_INFO_NONATOMIC_OPS; + strcpy(pcm->name, name); + + preallocate_buffers(chip, pcm); + + chip->pcm = pcm; + return 0; +} + + +/* + */ +static int snd_mixart_pcm_digital(mixart_t *chip) +{ + int err; + snd_pcm_t *pcm; + char name[32]; + + sprintf(name, "miXart AES/EBU %d", chip->chip_idx); + if ((err = snd_pcm_new(chip->card, name, MIXART_PCM_DIGITAL, + MIXART_PLAYBACK_STREAMS, + MIXART_CAPTURE_STREAMS, &pcm)) < 0) { + snd_printk(KERN_ERR "cannot create the digital pcm %d\n", chip->chip_idx); + return err; + } + + pcm->private_data = chip; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_mixart_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_mixart_capture_ops); + + pcm->info_flags = SNDRV_PCM_INFO_NONATOMIC_OPS; + strcpy(pcm->name, name); + + preallocate_buffers(chip, pcm); + + chip->pcm_dig = pcm; + return 0; +} + +static int snd_mixart_chip_free(mixart_t *chip) +{ + snd_magic_kfree(chip); + return 0; +} + +static int snd_mixart_chip_dev_free(snd_device_t *device) +{ + mixart_t *chip = snd_magic_cast(mixart_t, device->device_data, return -ENXIO); + return snd_mixart_chip_free(chip); +} + + +/* + */ +static int __devinit snd_mixart_create(mixart_mgr_t *mgr, snd_card_t *card, int idx) +{ + int err; + mixart_t *chip; + static snd_device_ops_t ops = { + .dev_free = snd_mixart_chip_dev_free, + }; + + mgr->chip[idx] = chip = snd_magic_kcalloc(mixart_t, 0, GFP_KERNEL); + if (! chip) { + snd_printk(KERN_ERR "cannot allocate chip\n"); + return -ENOMEM; + } + + chip->card = card; + chip->chip_idx = idx; + chip->mgr = mgr; + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_mixart_chip_free(chip); + return err; + } + + if (idx == 0) { + /* create a DSP loader only on first cardX*/ + err = snd_mixart_hwdep_new(mgr); + if (err < 0) + return err; + } + + snd_card_set_dev(card, &mgr->pci->dev); + + return 0; +} + +int snd_mixart_create_pcm(mixart_t* chip) +{ + int err; + + err = snd_mixart_pcm_analog(chip); + if (err < 0) + return err; + + if(chip->mgr->board_type == MIXART_DAUGHTER_TYPE_AES) { + + err = snd_mixart_pcm_digital(chip); + if (err < 0) + return err; + } + return err; +} + + +/* + * release all the cards assigned to a manager instance + */ +static int snd_mixart_free(mixart_mgr_t *mgr) +{ + unsigned int i; + + for (i = 0; i < mgr->num_cards; i++) { + if (mgr->chip[i]) + snd_card_free(mgr->chip[i]->card); + } + + /* stop mailbox */ + snd_mixart_exit_mailbox(mgr); + + /* release irq */ + if (mgr->irq >= 0) + free_irq(mgr->irq, (void *)mgr); + + /* reset board if some firmware was loaded */ + if(mgr->hwdep->dsp_loaded) { + snd_mixart_reset_board(mgr); + snd_printdd("reset miXart !\n"); + } + + /* release the i/o ports */ + for (i = 0; i < 2; i++) { + if (mgr->mem[i].virt) + iounmap((void *)mgr->mem[i].virt); + if (mgr->mem[i].res) { + release_resource(mgr->mem[i].res); + kfree_nocheck(mgr->mem[i].res); + } + } + + /* free flowarray */ + if(mgr->flowinfo.area) { + snd_dma_free_pages(&mgr->dma_dev, &mgr->flowinfo); + mgr->flowinfo.area = NULL; + } + /* free bufferarray */ + if(mgr->bufferinfo.area) { + snd_dma_free_pages(&mgr->dma_dev, &mgr->bufferinfo); + mgr->bufferinfo.area = NULL; + } + + snd_magic_kfree(mgr); + return 0; +} + +/* + * proc interface + */ +static long long snd_mixart_BA0_llseek(snd_info_entry_t *entry, + void *private_file_data, + struct file *file, + long long offset, + int orig) +{ + offset = offset & ~3; /* 4 bytes aligned */ + + switch(orig) { + case 0: /* SEEK_SET */ + file->f_pos = offset; + break; + case 1: /* SEEK_CUR */ + file->f_pos += offset; + break; + case 2: /* SEEK_END, offset is negative */ + file->f_pos = MIXART_BA0_SIZE + offset; + break; + default: + return -EINVAL; + } + if(file->f_pos > MIXART_BA0_SIZE) + file->f_pos = MIXART_BA0_SIZE; + return file->f_pos; +} + +static long long snd_mixart_BA1_llseek(snd_info_entry_t *entry, + void *private_file_data, + struct file *file, + long long offset, + int orig) +{ + offset = offset & ~3; /* 4 bytes aligned */ + + switch(orig) { + case 0: /* SEEK_SET */ + file->f_pos = offset; + break; + case 1: /* SEEK_CUR */ + file->f_pos += offset; + break; + case 2: /* SEEK_END, offset is negative */ + file->f_pos = MIXART_BA1_SIZE + offset; + break; + default: + return -EINVAL; + } + if(file->f_pos > MIXART_BA1_SIZE) + file->f_pos = MIXART_BA1_SIZE; + return file->f_pos; +} + +/* + mixart_BA0 proc interface for BAR 0 - read callback + */ +static long snd_mixart_BA0_read(snd_info_entry_t *entry, void *file_private_data, + struct file *file, char *buf, long count) +{ + mixart_mgr_t *mgr = snd_magic_cast(mixart_mgr_t, entry->private_data, return -ENXIO); + + count = count & ~3; /* make sure the read size is a multiple of 4 bytes */ + if(count <= 0) + return 0; + if(file->f_pos + count > MIXART_BA0_SIZE) + count = (long)(MIXART_BA0_SIZE - file->f_pos); + if(copy_to_user_fromio(buf, MIXART_MEM( mgr, file->f_pos ), count)) + return -EFAULT; + file->f_pos += count; + return count; +} + +/* + mixart_BA1 proc interface for BAR 1 - read callback + */ +static long snd_mixart_BA1_read(snd_info_entry_t *entry, void *file_private_data, + struct file *file, char *buf, long count) +{ + mixart_mgr_t *mgr = snd_magic_cast(mixart_mgr_t, entry->private_data, return -ENXIO); + + count = count & ~3; /* make sure the read size is a multiple of 4 bytes */ + if(count <= 0) + return 0; + if(file->f_pos + count > MIXART_BA1_SIZE) + count = (long)(MIXART_BA1_SIZE - file->f_pos); + if(copy_to_user_fromio(buf, MIXART_REG( mgr, file->f_pos ), count)) + return -EFAULT; + file->f_pos += count; + return count; +} + +static struct snd_info_entry_ops snd_mixart_proc_ops_BA0 = { + .read = snd_mixart_BA0_read, + .llseek = snd_mixart_BA0_llseek +}; + +static struct snd_info_entry_ops snd_mixart_proc_ops_BA1 = { + .read = snd_mixart_BA1_read, + .llseek = snd_mixart_BA1_llseek +}; + + +static void snd_mixart_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + mixart_t *chip = snd_magic_cast(mixart_t, entry->private_data, return); + u32 ref; + + snd_iprintf(buffer, "Digigram miXart (alsa card %d)\n\n", chip->chip_idx); + + /* stats available when embedded OS is running */ + if (chip->mgr->hwdep->dsp_loaded & ( 1 << MIXART_MOTHERBOARD_ELF_INDEX)) { + snd_iprintf(buffer, "- hardware -\n"); + switch (chip->mgr->board_type ) { + case MIXART_DAUGHTER_TYPE_NONE : snd_iprintf(buffer, "\tmiXart8 (no daughter board)\n\n"); break; + case MIXART_DAUGHTER_TYPE_AES : snd_iprintf(buffer, "\tmiXart8 AES/EBU\n\n"); break; + case MIXART_DAUGHTER_TYPE_COBRANET : snd_iprintf(buffer, "\tmiXart8 Cobranet\n\n"); break; + default: snd_iprintf(buffer, "\tUNKNOWN!\n\n"); break; + } + + snd_iprintf(buffer, "- system load -\n"); + + /* get perf reference */ + + ref = readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_SYSTEM_LOAD_OFFSET)); + + if (ref) { + u32 mailbox = 100 * readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_MAILBX_LOAD_OFFSET)) / ref; + u32 streaming = 100 * readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_STREAM_LOAD_OFFSET)) / ref; + u32 interr = 100 * readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_INTERR_LOAD_OFFSET)) / ref; + + snd_iprintf(buffer, "\tstreaming : %d\n", streaming); + snd_iprintf(buffer, "\tmailbox : %d\n", mailbox); + snd_iprintf(buffer, "\tinterrups handling : %d\n\n", interr); + } + } /* endif elf loaded */ +} + +static void __devinit snd_mixart_proc_init(mixart_t *chip) +{ + snd_info_entry_t *entry; + + /* text interface to read perf and temp meters */ + if (! snd_card_proc_new(chip->card, "board_info", &entry)) { + entry->private_data = chip; + entry->c.text.read_size = 1024; + entry->c.text.read = snd_mixart_proc_read; + } + + if (! snd_card_proc_new(chip->card, "mixart_BA0", &entry)) { + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->private_data = chip->mgr; + entry->c.ops = &snd_mixart_proc_ops_BA0; + entry->size = MIXART_BA0_SIZE; + } + if (! snd_card_proc_new(chip->card, "mixart_BA1", &entry)) { + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->private_data = chip->mgr; + entry->c.ops = &snd_mixart_proc_ops_BA1; + entry->size = MIXART_BA1_SIZE; + } +} +/* end of proc interface */ + + +/* + * probe function - creates the card manager + */ +static int __devinit snd_mixart_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + mixart_mgr_t *mgr; + unsigned int i; + int err; + size_t size; + + /* + */ + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (! enable[dev]) { + dev++; + return -ENOENT; + } + + /* enable PCI device */ + if ((err = pci_enable_device(pci)) < 0) + return err; + pci_set_master(pci); + + /* check if we can restrict PCI DMA transfers to 32 bits */ + if (!pci_dma_supported(pci, 0xffffffff)) { + snd_printk(KERN_ERR "architecture does not support 32bit PCI busmaster DMA\n"); + return -ENXIO; + } + pci_set_dma_mask(pci, 0xffffffff); + + /* + */ + mgr = snd_magic_kcalloc(mixart_mgr_t, 0, GFP_KERNEL); + if (! mgr) + return -ENOMEM; + + mgr->pci = pci; + mgr->irq = -1; + + /* resource assignment */ + for (i = 0; i < 2; i++) { + static int memory_sizes[2] = { + MIXART_BA0_SIZE, /* 16M */ + MIXART_BA1_SIZE /* 4 k */ + }; + mgr->mem[i].phys = pci_resource_start(pci, i); + mgr->mem[i].res = request_mem_region(mgr->mem[i].phys, memory_sizes[i], CARD_NAME); + if (! mgr->mem[i].res) { + snd_printk(KERN_ERR "unable to grab memory 0x%lx\n", mgr->mem[i].phys); + snd_mixart_free(mgr); + return -EBUSY; + } + mgr->mem[i].virt = (unsigned long)ioremap_nocache(mgr->mem[i].phys, memory_sizes[i]); + } + + if (request_irq(pci->irq, snd_mixart_interrupt, SA_INTERRUPT|SA_SHIRQ, CARD_NAME, (void *)mgr)) { + snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); + snd_mixart_free(mgr); + return -EBUSY; + } + mgr->irq = pci->irq; + + sprintf(mgr->shortname, "Digigram miXart"); + sprintf(mgr->longname, "%s at 0x%lx & 0x%lx, irq %i", mgr->shortname, mgr->mem[0].phys, mgr->mem[1].phys, mgr->irq); + + /* ISR spinlock */ + mgr->lock = SPIN_LOCK_UNLOCKED; + + /* init mailbox */ + mgr->msg_fifo_readptr = 0; + mgr->msg_fifo_writeptr = 0; + + mgr->msg_lock = SPIN_LOCK_UNLOCKED; + init_MUTEX(&mgr->msg_mutex); + init_waitqueue_head(&mgr->msg_sleep); + atomic_set(&mgr->msg_processed, 0); + + /* init setup mutex*/ + init_MUTEX(&mgr->setup_mutex); + + /* init message taslket */ + tasklet_init( &mgr->msg_taskq, snd_mixart_msg_tasklet, (unsigned long) mgr); + + /* card assignment */ + mgr->num_cards = MIXART_MAX_CARDS; /* 4 FIXME: configurable? */ + for (i = 0; i < mgr->num_cards; i++) { + snd_card_t *card; + char tmpid[16]; + int idx; + + if (index[dev] < 0) + idx = index[dev]; + else + idx = index[dev] + i; + snprintf(tmpid, sizeof(tmpid), "%s-%d", id[dev], i); + card = snd_card_new(idx, tmpid, THIS_MODULE, 0); + + if (! card) { + snd_printk(KERN_ERR "cannot allocate the card %d\n", i); + snd_mixart_free(mgr); + return -ENOMEM; + } + + strcpy(card->driver, CARD_NAME); + sprintf(card->shortname, "%s [PCM #%d]", mgr->shortname, i); + sprintf(card->longname, "%s [PCM #%d]", mgr->longname, i); + + if ((err = snd_mixart_create(mgr, card, i)) < 0) { + snd_mixart_free(mgr); + return err; + } + + if(i==0) { + /* init proc interface only for chip0 */ + snd_mixart_proc_init(mgr->chip[i]); + } + + if ((err = snd_card_register(card)) < 0) { + snd_mixart_free(mgr); + return err; + } + } + + /* init firmware status (mgr->hwdep->dsp_loaded reset in hwdep_new) */ + mgr->board_type = MIXART_DAUGHTER_TYPE_NONE; + + memset(&mgr->dma_dev, 0, sizeof(mgr->dma_dev)); + mgr->dma_dev.type = SNDRV_DMA_TYPE_DEV; + mgr->dma_dev.dev = snd_dma_pci_data(mgr->pci); + + /* create array of streaminfo */ + size = PAGE_ALIGN( (MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS * sizeof(mixart_flowinfo_t)) ); + if (snd_dma_alloc_pages(&mgr->dma_dev, size, &mgr->flowinfo) < 0) { + snd_mixart_free(mgr); + return -ENOMEM; + } + /* init streaminfo_array */ + memset(mgr->flowinfo.area, 0, size); + + /* create array of bufferinfo */ + size = PAGE_ALIGN( (MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS * sizeof(mixart_bufferinfo_t)) ); + if (snd_dma_alloc_pages(&mgr->dma_dev, size, &mgr->bufferinfo) < 0) { + snd_mixart_free(mgr); + return -ENOMEM; + } + /* init bufferinfo_array */ + memset(mgr->bufferinfo.area, 0, size); + + pci_set_drvdata(pci, mgr); + dev++; + return 0; +} + +static void __devexit snd_mixart_remove(struct pci_dev *pci) +{ + snd_mixart_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + .name = "Digigram miXart", + .id_table = snd_mixart_ids, + .probe = snd_mixart_probe, + .remove = __devexit_p(snd_mixart_remove), +}; + +static int __init alsa_card_mixart_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + snd_printk(KERN_ERR "Digigram miXart soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_mixart_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_mixart_init) +module_exit(alsa_card_mixart_exit) + +#ifndef MODULE + +/* format is: snd-mixart=enable,index,id */ + +static int __init alsa_card_mixart_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-mixart=", alsa_card_mixart_setup); + +#endif /* ifndef MODULE */ --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/sound/pci/mixart/mixart_core.c 2004-03-07 20:46:50.000000000 -0800 @@ -0,0 +1,585 @@ +/* + * Driver for Digigram miXart soundcards + * + * low level interface with interrupt handling and mail box implementation + * + * Copyright (c) 2003 by Digigram + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include "mixart.h" +#include "mixart_hwdep.h" +#include "mixart_core.h" + + +#define MSG_TIMEOUT_JIFFIES (400 * HZ) / 1000 /* 400 ms */ + +#define MSG_DESCRIPTOR_SIZE 0x24 +#define MSG_HEADER_SIZE (MSG_DESCRIPTOR_SIZE + 4) + +#define MSG_DEFAULT_SIZE 512 + +#define MSG_TYPE_MASK 0x00000003 /* mask for following types */ +#define MSG_TYPE_NOTIFY 0 /* embedded -> driver (only notification, do not get_msg() !) */ +#define MSG_TYPE_COMMAND 1 /* driver <-> embedded (a command has no answer) */ +#define MSG_TYPE_REQUEST 2 /* driver -> embedded (request will get an answer back) */ +#define MSG_TYPE_ANSWER 3 /* embedded -> driver */ +#define MSG_CANCEL_NOTIFY_MASK 0x80000000 /* this bit is set for a notification that has been canceled */ + + +static int retrieve_msg_frame(mixart_mgr_t *mgr, u32 *msg_frame) +{ + /* read the message frame fifo */ + u32 headptr, tailptr; + + tailptr = readl_be(MIXART_MEM(mgr, MSG_OUTBOUND_POST_TAIL)); + headptr = readl_be(MIXART_MEM(mgr, MSG_OUTBOUND_POST_HEAD)); + + if (tailptr == headptr) + return 0; /* no message posted */ + + snd_assert( tailptr >= MSG_OUTBOUND_POST_STACK, return 0); /* error */ + snd_assert( tailptr < (MSG_OUTBOUND_POST_STACK+MSG_BOUND_STACK_SIZE), return 0); /* error */ + + *msg_frame = readl_be(MIXART_MEM(mgr, tailptr)); + + /* increment the tail index */ + tailptr += 4; + if( tailptr >= (MSG_OUTBOUND_POST_STACK+MSG_BOUND_STACK_SIZE) ) + tailptr = MSG_OUTBOUND_POST_STACK; + writel_be(tailptr, MIXART_MEM(mgr, MSG_OUTBOUND_POST_TAIL)); + + return 1; +} + +static int get_msg(mixart_mgr_t *mgr, mixart_msg_t *resp, u32 msg_frame_address ) +{ + unsigned long flags; + u32 headptr, i; + u32 size; + int err; + + spin_lock_irqsave(&mgr->msg_lock, flags); + err = 0; + + /* copy message descriptor from miXart to driver */ + size = readl_be(MIXART_MEM(mgr, msg_frame_address)); /* size of descriptor + response */ + resp->message_id = readl_be(MIXART_MEM(mgr, msg_frame_address + 4)); /* dwMessageID */ + resp->uid.object_id = readl_be(MIXART_MEM(mgr, msg_frame_address + 8)); /* uidDest */ + resp->uid.desc = readl_be(MIXART_MEM(mgr, msg_frame_address + 12)); /* */ + + if( (size < MSG_DESCRIPTOR_SIZE) || (resp->size < (size - MSG_DESCRIPTOR_SIZE))) { + err = -EINVAL; + snd_printk(KERN_ERR "problem with response size = %d\n", size); + goto _clean_exit; + } + size -= MSG_DESCRIPTOR_SIZE; + + memcpy_fromio(resp->data, MIXART_MEM(mgr, msg_frame_address + MSG_HEADER_SIZE ), size); + resp->size = size; + + /* swap if necessary */ +#ifndef __BIG_ENDIAN + size /= 4; /* u32 size */ + for(i=0; i < size; i++) { + ((u32*)resp->data)[i] = be32_to_cpu(((u32*)resp->data)[i]); + } +#endif + + /* + * free message frame address + */ + headptr = readl_be(MIXART_MEM(mgr, MSG_OUTBOUND_FREE_HEAD)); + + if( (headptr < MSG_OUTBOUND_FREE_STACK) || ( headptr >= (MSG_OUTBOUND_FREE_STACK+MSG_BOUND_STACK_SIZE))) { + err = -EINVAL; + goto _clean_exit; + } + + /* give address back to outbound fifo */ + writel_be(msg_frame_address, MIXART_MEM(mgr, headptr)); + + /* increment the outbound free head */ + headptr += 4; + if( headptr >= (MSG_OUTBOUND_FREE_STACK+MSG_BOUND_STACK_SIZE) ) + headptr = MSG_OUTBOUND_FREE_STACK; + + writel_be(headptr, MIXART_MEM(mgr, MSG_OUTBOUND_FREE_HEAD)); + + _clean_exit: + spin_unlock_irqrestore(&mgr->msg_lock, flags); + + return err; +} + + +/* + * send a message to miXart. return: the msg_frame used for this message + */ +/* call with mgr->msg_lock held! */ +static int send_msg( mixart_mgr_t *mgr, + mixart_msg_t *msg, + int max_answersize, + int mark_pending, + u32 *msg_event) +{ + u32 headptr, tailptr; + u32 msg_frame_address; + int err, i; + + snd_assert(msg->size % 4 == 0, return -EINVAL); + + err = 0; + + /* get message frame address */ + tailptr = readl_be(MIXART_MEM(mgr, MSG_INBOUND_FREE_TAIL)); + headptr = readl_be(MIXART_MEM(mgr, MSG_INBOUND_FREE_HEAD)); + + if (tailptr == headptr) { + snd_printk(KERN_ERR "error: no message frame available\n"); + return -EBUSY; + } + + if( (tailptr < MSG_INBOUND_FREE_STACK) || (tailptr >= (MSG_INBOUND_FREE_STACK+MSG_BOUND_STACK_SIZE))) { + return -EINVAL; + } + + msg_frame_address = readl_be(MIXART_MEM(mgr, tailptr)); + writel(0, MIXART_MEM(mgr, tailptr)); /* set address to zero on this fifo position */ + + /* increment the inbound free tail */ + tailptr += 4; + if( tailptr >= (MSG_INBOUND_FREE_STACK+MSG_BOUND_STACK_SIZE) ) + tailptr = MSG_INBOUND_FREE_STACK; + + writel_be(tailptr, MIXART_MEM(mgr, MSG_INBOUND_FREE_TAIL)); + + /* TODO : use memcpy_toio() with intermediate buffer to copy the message */ + + /* copy message descriptor to card memory */ + writel_be( msg->size + MSG_DESCRIPTOR_SIZE, MIXART_MEM(mgr, msg_frame_address) ); /* size of descriptor + request */ + writel_be( msg->message_id , MIXART_MEM(mgr, msg_frame_address + 4) ); /* dwMessageID */ + writel_be( msg->uid.object_id, MIXART_MEM(mgr, msg_frame_address + 8) ); /* uidDest */ + writel_be( msg->uid.desc, MIXART_MEM(mgr, msg_frame_address + 12) ); /* */ + writel_be( MSG_DESCRIPTOR_SIZE, MIXART_MEM(mgr, msg_frame_address + 16) ); /* SizeHeader */ + writel_be( MSG_DESCRIPTOR_SIZE, MIXART_MEM(mgr, msg_frame_address + 20) ); /* OffsetDLL_T16 */ + writel_be( msg->size, MIXART_MEM(mgr, msg_frame_address + 24) ); /* SizeDLL_T16 */ + writel_be( MSG_DESCRIPTOR_SIZE, MIXART_MEM(mgr, msg_frame_address + 28) ); /* OffsetDLL_DRV */ + writel_be( 0, MIXART_MEM(mgr, msg_frame_address + 32) ); /* SizeDLL_DRV */ + writel_be( MSG_DESCRIPTOR_SIZE + max_answersize, MIXART_MEM(mgr, msg_frame_address + 36) ); /* dwExpectedAnswerSize */ + + /* copy message data to card memory */ + for( i=0; i < msg->size; i+=4 ) { + writel_be( *(u32*)(msg->data + i), MIXART_MEM(mgr, MSG_HEADER_SIZE + msg_frame_address + i) ); + } + + if( mark_pending ) { + if( *msg_event ) { + /* the pending event is the notification we wait for ! */ + mgr->pending_event = *msg_event; + } + else { + /* the pending event is the answer we wait for (same address than the request)! */ + mgr->pending_event = msg_frame_address; + + /* copy address back to caller */ + *msg_event = msg_frame_address; + } + } + + /* mark the frame as a request (will have an answer) */ + msg_frame_address |= MSG_TYPE_REQUEST; + + /* post the frame */ + headptr = readl_be(MIXART_MEM(mgr, MSG_INBOUND_POST_HEAD)); + + if( (headptr < MSG_INBOUND_POST_STACK) || (headptr >= (MSG_INBOUND_POST_STACK+MSG_BOUND_STACK_SIZE))) { + return -EINVAL; + } + + writel_be(msg_frame_address, MIXART_MEM(mgr, headptr)); + + /* increment the inbound post head */ + headptr += 4; + if( headptr >= (MSG_INBOUND_POST_STACK+MSG_BOUND_STACK_SIZE) ) + headptr = MSG_INBOUND_POST_STACK; + + writel_be(headptr, MIXART_MEM(mgr, MSG_INBOUND_POST_HEAD)); + + return 0; +} + + +int snd_mixart_send_msg(mixart_mgr_t *mgr, mixart_msg_t *request, int max_resp_size, void *resp_data) +{ + mixart_msg_t resp; + u32 msg_frame = 0; /* set to 0, so it's no notification to wait for, but the answer */ + int err; + wait_queue_t wait; + long timeout; + + down(&mgr->msg_mutex); + + init_waitqueue_entry(&wait, current); + + spin_lock_irq(&mgr->msg_lock); + /* send the message */ + err = send_msg(mgr, request, max_resp_size, 1, &msg_frame); /* send and mark the answer pending */ + if (err) { + spin_unlock_irq(&mgr->msg_lock); + up(&mgr->msg_mutex); + return err; + } + + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&mgr->msg_sleep, &wait); + spin_unlock_irq(&mgr->msg_lock); + timeout = schedule_timeout(MSG_TIMEOUT_JIFFIES); + remove_wait_queue(&mgr->msg_sleep, &wait); + + if (! timeout) { + /* error - no ack */ + up(&mgr->msg_mutex); + snd_printk(KERN_ERR "error: no reponse on msg %x\n", msg_frame); + return -EIO; + } + + /* retrieve the answer into the same mixart_msg_t */ + resp.message_id = 0; + resp.uid = (mixart_uid_t){0,0}; + resp.data = resp_data; + resp.size = max_resp_size; + + err = get_msg(mgr, &resp, msg_frame); + + if( request->message_id != resp.message_id ) + snd_printk(KERN_ERR "REPONSE ERROR!\n"); + + up(&mgr->msg_mutex); + return err; +} + + +int snd_mixart_send_msg_wait_notif(mixart_mgr_t *mgr, mixart_msg_t *request, u32 notif_event) +{ + int err; + wait_queue_t wait; + long timeout; + + snd_assert(notif_event != 0, return -EINVAL); + snd_assert((notif_event & MSG_TYPE_MASK) == MSG_TYPE_NOTIFY, return -EINVAL); + snd_assert((notif_event & MSG_CANCEL_NOTIFY_MASK) == 0, return -EINVAL); + + down(&mgr->msg_mutex); + + init_waitqueue_entry(&wait, current); + + spin_lock_irq(&mgr->msg_lock); + /* send the message */ + err = send_msg(mgr, request, MSG_DEFAULT_SIZE, 1, ¬if_event); /* send and mark the notification event pending */ + if(err) { + spin_unlock_irq(&mgr->msg_lock); + up(&mgr->msg_mutex); + return err; + } + + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&mgr->msg_sleep, &wait); + spin_unlock_irq(&mgr->msg_lock); + timeout = schedule_timeout(MSG_TIMEOUT_JIFFIES); + remove_wait_queue(&mgr->msg_sleep, &wait); + + if (! timeout) { + /* error - no ack */ + up(&mgr->msg_mutex); + snd_printk(KERN_ERR "error: notification %x not received\n", notif_event); + return -EIO; + } + + up(&mgr->msg_mutex); + return 0; +} + + +int snd_mixart_send_msg_nonblock(mixart_mgr_t *mgr, mixart_msg_t *request) +{ + u32 message_frame; + unsigned long flags; + int err; + + /* just send the message (do not mark it as a pending one) */ + spin_lock_irqsave(&mgr->msg_lock, flags); + err = send_msg(mgr, request, MSG_DEFAULT_SIZE, 0, &message_frame); + spin_unlock_irqrestore(&mgr->msg_lock, flags); + + /* the answer will be handled by snd_mixart_msg_tasklet() */ + atomic_inc(&mgr->msg_processed); + + return err; +} + + +/* common buffer of tasklet and interrupt to send/receive messages */ +static u32 mixart_msg_data[MSG_DEFAULT_SIZE / 4]; + + +void snd_mixart_msg_tasklet( unsigned long arg) +{ + mixart_mgr_t *mgr = ( mixart_mgr_t*)(arg); + mixart_msg_t resp; + u32 msg, addr, type; + int err; + + spin_lock(&mgr->lock); + + while (mgr->msg_fifo_readptr != mgr->msg_fifo_writeptr) { + msg = mgr->msg_fifo[mgr->msg_fifo_readptr]; + mgr->msg_fifo_readptr++; + mgr->msg_fifo_readptr %= MSG_FIFO_SIZE; + + /* process the message ... */ + addr = msg & ~MSG_TYPE_MASK; + type = msg & MSG_TYPE_MASK; + + switch (type) { + case MSG_TYPE_ANSWER: + /* answer to a message on that we did not wait for (send_msg_nonblock) */ + resp.message_id = 0; + resp.data = mixart_msg_data; + resp.size = sizeof(mixart_msg_data); + err = get_msg(mgr, &resp, addr); + if( err < 0 ) { + snd_printk(KERN_ERR "tasklet: error(%d) reading mf %x\n", err, msg); + break; + } + + switch(resp.message_id) { + case MSG_STREAM_START_INPUT_STAGE_PACKET: + case MSG_STREAM_START_OUTPUT_STAGE_PACKET: + case MSG_STREAM_STOP_INPUT_STAGE_PACKET: + case MSG_STREAM_STOP_OUTPUT_STAGE_PACKET: + if(mixart_msg_data[0]) + snd_printk(KERN_ERR "tasklet : error MSG_STREAM_ST***_***PUT_STAGE_PACKET status=%x\n", mixart_msg_data[0]); + break; + default: + snd_printdd("tasklet received mf(%x) : msg_id(%x) uid(%x, %x) size(%d)\n", + msg, resp.message_id, resp.uid.object_id, resp.uid.desc, resp.size); + break; + } + break; + case MSG_TYPE_NOTIFY: + /* msg contains no address ! do not get_msg() ! */ + case MSG_TYPE_COMMAND: + /* get_msg() necessary */ + default: + snd_printk(KERN_ERR "tasklet doesn't know what to do with message %x\n", msg); + } /* switch type */ + + /* decrement counter */ + atomic_dec(&mgr->msg_processed); + + } /* while there is a msg in fifo */ + + spin_unlock(&mgr->lock); +} + + +irqreturn_t snd_mixart_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + mixart_mgr_t *mgr = snd_magic_cast(mixart_mgr_t, dev_id, return IRQ_NONE); + int err; + mixart_msg_t resp; + + u32 msg; + u32 it_reg; + + spin_lock(&mgr->lock); + + it_reg = readl_le(MIXART_REG(mgr, MIXART_PCI_OMISR_OFFSET)); + if( !(it_reg & MIXART_OIDI) ) { + /* this device did not cause the interrupt */ + spin_unlock(&mgr->lock); + return IRQ_NONE; + } + + /* mask all interrupts */ + writel_le(MIXART_HOST_ALL_INTERRUPT_MASKED, MIXART_REG(mgr, MIXART_PCI_OMIMR_OFFSET)); + + /* outdoorbell register clear */ + it_reg = readl(MIXART_REG(mgr, MIXART_PCI_ODBR_OFFSET)); + writel(it_reg, MIXART_REG(mgr, MIXART_PCI_ODBR_OFFSET)); + + /* clear interrupt */ + writel_le( MIXART_OIDI, MIXART_REG(mgr, MIXART_PCI_OMISR_OFFSET) ); + + /* process interrupt */ + while (retrieve_msg_frame(mgr, &msg)) { + + switch (msg & MSG_TYPE_MASK) { + case MSG_TYPE_COMMAND: + resp.message_id = 0; + resp.data = mixart_msg_data; + resp.size = sizeof(mixart_msg_data); + err = get_msg(mgr, &resp, msg & ~MSG_TYPE_MASK); + if( err < 0 ) { + snd_printk(KERN_ERR "interrupt: error(%d) reading mf %x\n", err, msg); + break; + } + + if(resp.message_id == MSG_SERVICES_TIMER_NOTIFY) { + int i; + mixart_timer_notify_t *notify = (mixart_timer_notify_t*)mixart_msg_data; + + for(i=0; istream_count; i++) { + + u32 buffer_id = notify->streams[i].buffer_id; + unsigned int chip_number = (buffer_id & MIXART_NOTIFY_CARD_MASK) >> MIXART_NOTIFY_CARD_OFFSET; /* card0 to 3 */ + unsigned int pcm_number = (buffer_id & MIXART_NOTIFY_PCM_MASK ) >> MIXART_NOTIFY_PCM_OFFSET; /* pcm0 to 3 */ + unsigned int sub_number = buffer_id & MIXART_NOTIFY_SUBS_MASK; /* 0 to MIXART_PLAYBACK_STREAMS */ + unsigned int is_capture = ((buffer_id & MIXART_NOTIFY_CAPT_MASK) != 0); /* playback == 0 / capture == 1 */ + + mixart_t *chip = mgr->chip[chip_number]; + mixart_stream_t *stream; + + if ((chip_number >= mgr->num_cards) || (pcm_number >= MIXART_PCM_TOTAL) || (sub_number >= MIXART_PLAYBACK_STREAMS)) { + snd_printk(KERN_ERR "error MSG_SERVICES_TIMER_NOTIFY buffer_id (%x) pos(%d)\n", + buffer_id, notify->streams[i].sample_pos_low_part); + break; + } + + if (is_capture) + stream = &chip->capture_stream[pcm_number]; + else + stream = &chip->playback_stream[pcm_number][sub_number]; + + if (stream->substream && (stream->status == MIXART_STREAM_STATUS_RUNNING)) { + snd_pcm_runtime_t *runtime = stream->substream->runtime; + int elapsed = 0; + u64 sample_count = ((u64)notify->streams[i].sample_pos_high_part) << 32; + sample_count |= notify->streams[i].sample_pos_low_part; + + while (1) { + u64 new_elapse_pos = stream->abs_period_elapsed + runtime->period_size; + + if (new_elapse_pos > sample_count) { + break; /* while */ + } + else { + elapsed = 1; + stream->buf_periods++; + if (stream->buf_periods >= runtime->periods) + stream->buf_periods = 0; + + stream->abs_period_elapsed = new_elapse_pos; + } + } + stream->buf_period_frag = (u32)( sample_count - stream->abs_period_elapsed ); + + if(elapsed) { + spin_unlock(&mgr->lock); + snd_pcm_period_elapsed(stream->substream); + spin_lock(&mgr->lock); + } + } + } + break; + } + if(resp.message_id == MSG_SERVICES_REPORT_TRACES) { + if(resp.size > 1) { +#ifndef __BIG_ENDIAN + /* Traces are text: the swapped msg_data has to be swapped back ! */ + int i; + for(i=0; i<(resp.size/4); i++) { + (mixart_msg_data)[i] = cpu_to_be32((mixart_msg_data)[i]); + } +#endif + ((char*)mixart_msg_data)[resp.size - 1] = 0; + snd_printdd("MIXART TRACE : %s\n", (char*)mixart_msg_data); + } + break; + } + + snd_printdd("command %x not handled\n", resp.message_id); + break; + + case MSG_TYPE_NOTIFY: + if(msg & MSG_CANCEL_NOTIFY_MASK) { + msg &= ~MSG_CANCEL_NOTIFY_MASK; + snd_printk(KERN_ERR "canceled notification %x !\n", msg); + } + /* no break, continue ! */ + case MSG_TYPE_ANSWER: + /* answer or notification to a message we are waiting for*/ + spin_lock(&mgr->msg_lock); + if( (msg & ~MSG_TYPE_MASK) == mgr->pending_event ) { + wake_up(&mgr->msg_sleep); + mgr->pending_event = 0; + } + /* answer to a message we did't want to wait for */ + else { + mgr->msg_fifo[mgr->msg_fifo_writeptr] = msg; + mgr->msg_fifo_writeptr++; + mgr->msg_fifo_writeptr %= MSG_FIFO_SIZE; + tasklet_hi_schedule(&mgr->msg_taskq); + } + spin_unlock(&mgr->msg_lock); + break; + case MSG_TYPE_REQUEST: + default: + snd_printdd("interrupt received request %x\n", msg); + /* TODO : are there things to do here ? */ + break; + } /* switch on msg type */ + } /* while there are msgs */ + + /* allow interrupt again */ + writel_le( MIXART_ALLOW_OUTBOUND_DOORBELL, MIXART_REG( mgr, MIXART_PCI_OMIMR_OFFSET)); + + spin_unlock(&mgr->lock); + + return IRQ_HANDLED; +} + + +void snd_mixart_init_mailbox(mixart_mgr_t *mgr) +{ + writel( 0, MIXART_MEM( mgr, MSG_HOST_RSC_PROTECTION ) ); + writel( 0, MIXART_MEM( mgr, MSG_AGENT_RSC_PROTECTION ) ); + + /* allow outbound messagebox to generate interrupts */ + if(mgr->irq >= 0) { + writel_le( MIXART_ALLOW_OUTBOUND_DOORBELL, MIXART_REG( mgr, MIXART_PCI_OMIMR_OFFSET)); + } + return; +} + +void snd_mixart_exit_mailbox(mixart_mgr_t *mgr) +{ + /* no more interrupts on outbound messagebox */ + writel_le( MIXART_HOST_ALL_INTERRUPT_MASKED, MIXART_REG( mgr, MIXART_PCI_OMIMR_OFFSET)); + return; +} + +void snd_mixart_reset_board(mixart_mgr_t *mgr) +{ + /* reset miXart */ + writel_be( 1, MIXART_REG(mgr, MIXART_BA1_BRUTAL_RESET_OFFSET) ); + return; +} --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/sound/pci/mixart/mixart_core.h 2004-03-07 20:46:50.000000000 -0800 @@ -0,0 +1,607 @@ +/* + * Driver for Digigram miXart soundcards + * + * low level interface with interrupt handling and mail box implementation + * + * Copyright (c) 2003 by Digigram + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __SOUND_MIXART_CORE_H +#define __SOUND_MIXART_CORE_H + + +enum mixart_message_id { + MSG_CONNECTOR_GET_AUDIO_INFO = 0x050008, + MSG_CONNECTOR_GET_OUT_AUDIO_LEVEL = 0x050009, + MSG_CONNECTOR_SET_OUT_AUDIO_LEVEL = 0x05000A, + + MSG_CONSOLE_MANAGER = 0x070000, + MSG_CONSOLE_GET_CLOCK_UID = 0x070003, + + MSG_PHYSICALIO_SET_LEVEL = 0x0F0008, + + MSG_STREAM_ADD_INPUT_GROUP = 0x130000, + MSG_STREAM_ADD_OUTPUT_GROUP = 0x130001, + MSG_STREAM_DELETE_GROUP = 0x130004, + MSG_STREAM_START_STREAM_GRP_PACKET = 0x130006, + MSG_STREAM_START_INPUT_STAGE_PACKET = 0x130007, + MSG_STREAM_START_OUTPUT_STAGE_PACKET = 0x130008, + MSG_STREAM_STOP_STREAM_GRP_PACKET = 0x130009, + MSG_STREAM_STOP_INPUT_STAGE_PACKET = 0x13000A, + MSG_STREAM_STOP_OUTPUT_STAGE_PACKET = 0x13000B, + MSG_STREAM_SET_INPUT_STAGE_PARAM = 0x13000F, + MSG_STREAM_SET_OUTPUT_STAGE_PARAM = 0x130010, + MSG_STREAM_SET_IN_AUDIO_LEVEL = 0x130015, + MSG_STREAM_SET_OUT_STREAM_LEVEL = 0x130017, + + MSG_SYSTEM_FIRST_ID = 0x160000, + MSG_SYSTEM_ENUM_PHYSICAL_IO = 0x16000E, + MSG_SYSTEM_ENUM_PLAY_CONNECTOR = 0x160017, + MSG_SYSTEM_ENUM_RECORD_CONNECTOR = 0x160018, + MSG_SYSTEM_WAIT_SYNCHRO_CMD = 0x16002C, + MSG_SYSTEM_SEND_SYNCHRO_CMD = 0x16002D, + + MSG_SERVICES_TIMER_NOTIFY = 0x1D0404, + MSG_SERVICES_REPORT_TRACES = 0x1D0700, + + MSG_CLOCK_CHECK_PROPERTIES = 0x200001, + MSG_CLOCK_SET_PROPERTIES = 0x200002, +}; + + +typedef struct mixart_msg mixart_msg_t; +struct mixart_msg +{ + u32 message_id; + mixart_uid_t uid; + void* data; + size_t size; +}; + +/* structs used to communicate with miXart */ + +typedef struct mixart_enum_connector_resp mixart_enum_connector_resp_t; +struct mixart_enum_connector_resp +{ + u32 error_code; + u32 first_uid_offset; + u32 uid_count; + u32 current_uid_index; + mixart_uid_t uid[MIXART_MAX_PHYS_CONNECTORS]; +} __attribute__((packed)); + + +/* used for following struct */ +#define MIXART_FLOAT_P_22_0_TO_HEX 0x41b00000 /* 22.0f */ +#define MIXART_FLOAT_M_20_0_TO_HEX 0xc1a00000 /* -20.0f */ +#define MIXART_FLOAT____0_0_TO_HEX 0x00000000 /* 0.0f */ + +typedef struct mixart_audio_info_req mixart_audio_info_req_t; +struct mixart_audio_info_req +{ + u32 line_max_level; /* float */ + u32 micro_max_level; /* float */ + u32 cd_max_level; /* float */ +} __attribute__((packed)); + +typedef struct mixart_analog_hw_info mixart_analog_hw_info_t; +struct mixart_analog_hw_info +{ + u32 is_present; + u32 hw_connection_type; + u32 max_level; /* float */ + u32 min_var_level; /* float */ + u32 max_var_level; /* float */ + u32 step_var_level; /* float */ + u32 fix_gain; /* float */ + u32 zero_var; /* float */ +} __attribute__((packed)); + +typedef struct mixart_digital_hw_info mixart_digital_hw_info_t; +struct mixart_digital_hw_info +{ + u32 hw_connection_type; + u32 presence; + u32 clock; + u32 reserved; +} __attribute__((packed)); + +typedef struct mixart_analog_info mixart_analog_info_t; +struct mixart_analog_info +{ + u32 type_mask; + mixart_analog_hw_info_t micro_info; + mixart_analog_hw_info_t line_info; + mixart_analog_hw_info_t cd_info; + u32 analog_level_present; +} __attribute__((packed)); + +typedef struct mixart_digital_info mixart_digital_info_t; +struct mixart_digital_info +{ + u32 type_mask; + mixart_digital_hw_info_t aes_info; + mixart_digital_hw_info_t adat_info; +} __attribute__((packed)); + +typedef struct mixart_audio_info mixart_audio_info_t; +struct mixart_audio_info +{ + u32 clock_type_mask; + mixart_analog_info_t analog_info; + mixart_digital_info_t digital_info; +} __attribute__((packed)); + +typedef struct mixart_audio_info_resp mixart_audio_info_resp_t; +struct mixart_audio_info_resp +{ + u32 txx_status; + mixart_audio_info_t info; +} __attribute__((packed)); + + +/* used for nb_bytes_max_per_sample */ +#define MIXART_FLOAT_P__4_0_TO_HEX 0x40800000 /* +4.0f */ +#define MIXART_FLOAT_P__8_0_TO_HEX 0x41000000 /* +8.0f */ + +typedef struct mixart_stream_info mixart_stream_info_t; +struct mixart_stream_info +{ + u32 size_max_byte_frame; + u32 size_max_sample_frame; + u32 nb_bytes_max_per_sample; /* float */ +} __attribute__((packed)); + +/* MSG_STREAM_ADD_INPUT_GROUP */ +/* MSG_STREAM_ADD_OUTPUT_GROUP */ + +typedef struct mixart_streaming_group_req mixart_streaming_group_req_t; +struct mixart_streaming_group_req +{ + u32 stream_count; + u32 channel_count; + u32 user_grp_number; + u32 first_phys_audio; + u32 latency; + mixart_stream_info_t stream_info[32]; + mixart_uid_t connector; + u32 flow_entry[32]; +} __attribute__((packed)); + +typedef struct mixart_stream_desc mixart_stream_desc_t; +struct mixart_stream_desc +{ + mixart_uid_t stream_uid; + u32 stream_desc; +} __attribute__((packed)); + +typedef struct mixart_streaming_group mixart_streaming_group_t; +struct mixart_streaming_group +{ + u32 status; + mixart_uid_t group; + u32 pipe_desc; + u32 stream_count; + mixart_stream_desc_t stream[32]; +} __attribute__((packed)); + +/* MSG_STREAM_DELETE_GROUP */ + +/* request : mixart_uid_t group */ + +typedef struct mixart_delete_group_resp mixart_delete_group_resp_t; +struct mixart_delete_group_resp +{ + u32 status; + u32 unused[2]; +} __attribute__((packed)); + + +/* MSG_STREAM_START_INPUT_STAGE_PACKET = 0x130000 + 7, + MSG_STREAM_START_OUTPUT_STAGE_PACKET = 0x130000 + 8, + MSG_STREAM_STOP_INPUT_STAGE_PACKET = 0x130000 + 10, + MSG_STREAM_STOP_OUTPUT_STAGE_PACKET = 0x130000 + 11, + */ + +typedef struct mixart_fx_couple_uid mixart_fx_couple_uid_t; +struct mixart_fx_couple_uid +{ + mixart_uid_t uid_fx_code; + mixart_uid_t uid_fx_data; +} __attribute__((packed)); + +typedef struct mixart_txx_stream_desc mixart_txx_stream_desc_t; +struct mixart_txx_stream_desc +{ + mixart_uid_t uid_pipe; + u32 stream_idx; + u32 fx_number; + mixart_fx_couple_uid_t uid_fx[4]; +} __attribute__((packed)); + +typedef struct mixart_flow_info mixart_flow_info_t; +struct mixart_flow_info +{ + mixart_txx_stream_desc_t stream_desc; + u32 flow_entry; + u32 flow_phy_addr; +} __attribute__((packed)); + +typedef struct mixart_stream_state_req mixart_stream_state_req_t; +struct mixart_stream_state_req +{ + u32 delayed; + u64 scheduler; + u32 reserved4np[3]; + u32 stream_count; /* set to 1 for instance */ + mixart_flow_info_t stream_info; /* could be an array[stream_count] */ +} __attribute__((packed)); + +/* MSG_STREAM_START_STREAM_GRP_PACKET = 0x130000 + 6 + MSG_STREAM_STOP_STREAM_GRP_PACKET = 0x130000 + 9 + */ + +typedef struct mixart_group_state_req mixart_group_state_req_t; +struct mixart_group_state_req +{ + u32 delayed; + u64 scheduler; + u32 reserved4np[2]; + u32 pipe_count; /* set to 1 for instance */ + mixart_uid_t pipe_uid[1]; /* could be an array[pipe_count] */ +} __attribute__((packed)); + +typedef struct mixart_group_state_resp mixart_group_state_resp_t; +struct mixart_group_state_resp +{ + u32 txx_status; + u64 scheduler; +} __attribute__((packed)); + + + +/* Structures used by the MSG_SERVICES_TIMER_NOTIFY command */ + +typedef struct mixart_sample_pos mixart_sample_pos_t; +struct mixart_sample_pos +{ + u32 buffer_id; + u32 validity; + u32 sample_pos_high_part; + u32 sample_pos_low_part; +} __attribute__((packed)); + +typedef struct mixart_timer_notify mixart_timer_notify_t; +struct mixart_timer_notify +{ + u32 stream_count; + mixart_sample_pos_t streams[MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS]; +} __attribute__((packed)); + + +/* MSG_CONSOLE_GET_CLOCK_UID = 0x070003, + */ + +/* request is a uid with desc = MSG_CONSOLE_MANAGER | cardindex */ + +typedef struct mixart_return_uid mixart_return_uid_t; +struct mixart_return_uid +{ + u32 error_code; + mixart_uid_t uid; +} __attribute__((packed)); + +/* MSG_CLOCK_CHECK_PROPERTIES = 0x200001, + MSG_CLOCK_SET_PROPERTIES = 0x200002, +*/ + +enum mixart_clock_generic_type { + CGT_NO_CLOCK, + CGT_INTERNAL_CLOCK, + CGT_PROGRAMMABLE_CLOCK, + CGT_INTERNAL_ENSLAVED_CLOCK, + CGT_EXTERNAL_CLOCK, + CGT_CURRENT_CLOCK +}; + +enum mixart_clock_mode { + CM_UNDEFINED, + CM_MASTER, + CM_SLAVE, + CM_STANDALONE, + CM_NOT_CONCERNED +}; + + +typedef struct mixart_clock_properties mixart_clock_properties_t; +struct mixart_clock_properties +{ + u32 error_code; + u32 validation_mask; + u32 frequency; + u32 reference_frequency; + u32 clock_generic_type; + u32 clock_mode; + mixart_uid_t uid_clock_source; + mixart_uid_t uid_event_source; + u32 event_mode; + u32 synchro_signal_presence; + u32 format; + u32 board_mask; + u32 nb_callers; /* set to 1 (see below) */ + mixart_uid_t uid_caller[1]; +} __attribute__((packed)); + +typedef struct mixart_clock_properties_resp mixart_clock_properties_resp_t; +struct mixart_clock_properties_resp +{ + u32 status; + u32 clock_mode; +} __attribute__((packed)); + + +/* MSG_STREAM_SET_INPUT_STAGE_PARAM = 0x13000F */ +/* MSG_STREAM_SET_OUTPUT_STAGE_PARAM = 0x130010 */ + +enum mixart_coding_type { + CT_NOT_DEFINED, + CT_LINEAR, + CT_MPEG_L1, + CT_MPEG_L2, + CT_MPEG_L3, + CT_MPEG_L3_LSF, + CT_GSM +}; +enum mixart_sample_type { + ST_NOT_DEFINED, + ST_FLOATING_POINT_32BE, + ST_FLOATING_POINT_32LE, + ST_FLOATING_POINT_64BE, + ST_FLOATING_POINT_64LE, + ST_FIXED_POINT_8, + ST_FIXED_POINT_16BE, + ST_FIXED_POINT_16LE, + ST_FIXED_POINT_24BE, + ST_FIXED_POINT_24LE, + ST_FIXED_POINT_32BE, + ST_FIXED_POINT_32LE, + ST_INTEGER_8, + ST_INTEGER_16BE, + ST_INTEGER_16LE, + ST_INTEGER_24BE, + ST_INTEGER_24LE, + ST_INTEGER_32BE, + ST_INTEGER_32LE +}; + +typedef struct mixart_stream_param_desc mixart_stream_param_desc_t; +struct mixart_stream_param_desc +{ + u32 coding_type; /* use enum mixart_coding_type */ + u32 sample_type; /* use enum mixart_sample_type */ + + union { + struct { + u32 linear_endian_ness; + u32 linear_bits; + u32 is_signed; + u32 is_float; + } linear_format_info; + + struct { + u32 mpeg_layer; + u32 mpeg_mode; + u32 mpeg_mode_extension; + u32 mpeg_pre_emphasis; + u32 mpeg_has_padding_bit; + u32 mpeg_has_crc; + u32 mpeg_has_extension; + u32 mpeg_is_original; + u32 mpeg_has_copyright; + } mpeg_format_info; + } format_info; + + u32 delayed; + u64 scheduler; + u32 sample_size; + u32 has_header; + u32 has_suffix; + u32 has_bitrate; + u32 samples_per_frame; + u32 bytes_per_frame; + u32 bytes_per_sample; + u32 sampling_freq; + u32 number_of_channel; + u32 stream_number; + u32 buffer_size; + u32 differed_time; + u32 reserved4np[3]; + u32 pipe_count; /* set to 1 (array size !) */ + u32 stream_count; /* set to 1 (array size !) */ + mixart_txx_stream_desc_t stream_desc[1]; /* only one stream per command, but this could be an array */ + +} __attribute__((packed)); + + +/* MSG_CONNECTOR_GET_OUT_AUDIO_LEVEL = 0x050009, + */ + + +typedef struct mixart_get_out_audio_level mixart_get_out_audio_level_t; +struct mixart_get_out_audio_level +{ + u32 txx_status; + u32 digital_level; /* float */ + u32 analog_level; /* float */ + u32 monitor_level; /* float */ + u32 mute; + u32 monitor_mute1; + u32 monitor_mute2; +} __attribute__((packed)); + + +/* MSG_CONNECTOR_SET_OUT_AUDIO_LEVEL = 0x05000A, + */ + +/* used for valid_mask below */ +#define MIXART_AUDIO_LEVEL_ANALOG_MASK 0x01 +#define MIXART_AUDIO_LEVEL_DIGITAL_MASK 0x02 +#define MIXART_AUDIO_LEVEL_MONITOR_MASK 0x04 +#define MIXART_AUDIO_LEVEL_MUTE_MASK 0x08 +#define MIXART_AUDIO_LEVEL_MUTE_M1_MASK 0x10 +#define MIXART_AUDIO_LEVEL_MUTE_M2_MASK 0x20 + +typedef struct mixart_set_out_audio_level mixart_set_out_audio_level_t; +struct mixart_set_out_audio_level +{ + u32 delayed; + u64 scheduler; + u32 valid_mask1; + u32 valid_mask2; + u32 digital_level; /* float */ + u32 analog_level; /* float */ + u32 monitor_level; /* float */ + u32 mute; + u32 monitor_mute1; + u32 monitor_mute2; + u32 reserved4np; +} __attribute__((packed)); + + +/* MSG_SYSTEM_ENUM_PHYSICAL_IO = 0x16000E, + */ + +#define MIXART_MAX_PHYS_IO (MIXART_MAX_CARDS * 2 * 2) /* 4 * (analog+digital) * (playback+capture) */ + +typedef struct mixart_uid_enumeration mixart_uid_enumeration_t; +struct mixart_uid_enumeration +{ + u32 error_code; + u32 first_uid_offset; + u32 nb_uid; + u32 current_uid_index; + mixart_uid_t uid[MIXART_MAX_PHYS_IO]; +} __attribute__((packed)); + + +/* MSG_PHYSICALIO_SET_LEVEL = 0x0F0008, + MSG_PHYSICALIO_GET_LEVEL = 0x0F000C, +*/ + +typedef struct mixart_io_channel_level mixart_io_channel_level_t; +struct mixart_io_channel_level +{ + u32 analog_level; /* float */ + u32 unused[2]; +} __attribute__((packed)); + +typedef struct mixart_io_level mixart_io_level_t; +struct mixart_io_level +{ + s32 channel; /* 0=left, 1=right, -1=both, -2=both same */ + mixart_io_channel_level_t level[2]; +} __attribute__((packed)); + + +/* MSG_STREAM_SET_IN_AUDIO_LEVEL = 0x130015, + */ + +typedef struct mixart_in_audio_level_info mixart_in_audio_level_info_t; +struct mixart_in_audio_level_info +{ + mixart_uid_t connector; + u32 valid_mask1; + u32 valid_mask2; + u32 digital_level; + u32 analog_level; +} __attribute__((packed)); + +typedef struct mixart_set_in_audio_level_req mixart_set_in_audio_level_req_t; +struct mixart_set_in_audio_level_req +{ + u32 delayed; + u64 scheduler; + u32 audio_count; /* set to <= 2 */ + u32 reserved4np; + mixart_in_audio_level_info_t level[2]; +} __attribute__((packed)); + +/* response is a 32 bit status */ + + +/* MSG_STREAM_SET_OUT_STREAM_LEVEL = 0x130017, + */ + +/* defines used for valid_mask1 */ +#define MIXART_OUT_STREAM_SET_LEVEL_LEFT_AUDIO1 0x01 +#define MIXART_OUT_STREAM_SET_LEVEL_LEFT_AUDIO2 0x02 +#define MIXART_OUT_STREAM_SET_LEVEL_RIGHT_AUDIO1 0x04 +#define MIXART_OUT_STREAM_SET_LEVEL_RIGHT_AUDIO2 0x08 +#define MIXART_OUT_STREAM_SET_LEVEL_STREAM_1 0x10 +#define MIXART_OUT_STREAM_SET_LEVEL_STREAM_2 0x20 +#define MIXART_OUT_STREAM_SET_LEVEL_MUTE_1 0x40 +#define MIXART_OUT_STREAM_SET_LEVEL_MUTE_2 0x80 + +typedef struct mixart_out_stream_level_info mixart_out_stream_level_info_t; +struct mixart_out_stream_level_info +{ + u32 valid_mask1; + u32 valid_mask2; + u32 left_to_out1_level; + u32 left_to_out2_level; + u32 right_to_out1_level; + u32 right_to_out2_level; + u32 digital_level1; + u32 digital_level2; + u32 mute1; + u32 mute2; +} __attribute__((packed)); + +typedef struct mixart_set_out_stream_level mixart_set_out_stream_level_t; +struct mixart_set_out_stream_level +{ + mixart_txx_stream_desc_t desc; + mixart_out_stream_level_info_t out_level; +} __attribute__((packed)); + +typedef struct mixart_set_out_stream_level_req mixart_set_out_stream_level_req_t; +struct mixart_set_out_stream_level_req +{ + u32 delayed; + u64 scheduler; + u32 reserved4np[2]; + u32 nb_of_stream; /* set to 1 */ + mixart_set_out_stream_level_t stream_level; /* could be an array */ +} __attribute__((packed)); + +/* response to this request is a u32 status value */ + + +/* exported */ +void snd_mixart_init_mailbox(mixart_mgr_t *mgr); +void snd_mixart_exit_mailbox(mixart_mgr_t *mgr); + +int snd_mixart_send_msg(mixart_mgr_t *mgr, mixart_msg_t *request, int max_resp_size, void *resp_data); +int snd_mixart_send_msg_wait_notif(mixart_mgr_t *mgr, mixart_msg_t *request, u32 notif_event); +int snd_mixart_send_msg_nonblock(mixart_mgr_t *mgr, mixart_msg_t *request); + +irqreturn_t snd_mixart_interrupt(int irq, void *dev_id, struct pt_regs *regs); +void snd_mixart_msg_tasklet( unsigned long arg); + +void snd_mixart_reset_board(mixart_mgr_t *mgr); + +#endif /* __SOUND_MIXART_CORE_H */ --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/sound/pci/mixart/mixart.h 2004-03-07 20:47:44.000000000 -0800 @@ -0,0 +1,243 @@ +/* + * Driver for Digigram miXart soundcards + * + * main header file + * + * Copyright (c) 2003 by Digigram + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __SOUND_MIXART_H +#define __SOUND_MIXART_H + +#include +#include + +#define MIXART_DRIVER_VERSION 0x000100 /* 0.1.0 */ + + +/* + */ + +#define mixart_t_magic 0xa17a3e01 +#define mixart_mgr_t_magic 0xa17a3e02 + +typedef struct snd_mixart mixart_t; +typedef struct snd_mixart_mgr mixart_mgr_t; + +typedef struct snd_mixart_stream mixart_stream_t; +typedef struct snd_mixart_pipe mixart_pipe_t; + +typedef struct mixart_bufferinfo mixart_bufferinfo_t; +typedef struct mixart_flowinfo mixart_flowinfo_t; +typedef struct mixart_uid mixart_uid_t; + +struct mixart_uid +{ + u32 object_id; + u32 desc; +}; + +struct mem_area { + unsigned long phys; + unsigned long virt; + struct resource *res; +}; + + +typedef struct mixart_route mixart_route_t; +struct mixart_route { + unsigned char connected; + unsigned char phase_inv; + int volume; +}; + + +/* firmware status codes */ +#define MIXART_MOTHERBOARD_XLX_INDEX 0 +#define MIXART_MOTHERBOARD_ELF_INDEX 1 +#define MIXART_AESEBUBOARD_XLX_INDEX 2 +#define MIXART_HARDW_FILES_MAX_INDEX 3 /* xilinx, elf, AESEBU xilinx */ + +#define MIXART_MAX_CARDS 4 +#define MSG_FIFO_SIZE 16 + +#define MIXART_MAX_PHYS_CONNECTORS (MIXART_MAX_CARDS * 2 * 2) /* 4 * stereo * (analog+digital) */ + +struct snd_mixart_mgr { + unsigned int num_cards; + mixart_t *chip[MIXART_MAX_CARDS]; + + struct pci_dev *pci; + + int irq; + + /* memory-maps */ + struct mem_area mem[2]; + + /* share the name */ + char shortname[32]; /* short name of this soundcard */ + char longname[80]; /* name of this soundcard */ + + /* message tasklet */ + struct tasklet_struct msg_taskq; + + /* one and only blocking message or notification may be pending */ + u32 pending_event; + wait_queue_head_t msg_sleep; + + /* messages stored for tasklet */ + u32 msg_fifo[MSG_FIFO_SIZE]; + int msg_fifo_readptr; + int msg_fifo_writeptr; + atomic_t msg_processed; /* number of messages to be processed in takslet */ + + spinlock_t lock; /* interrupt spinlock */ + spinlock_t msg_lock; /* mailbox spinlock */ + struct semaphore msg_mutex; /* mutex for blocking_requests */ + + struct semaphore setup_mutex; /* mutex used in hw_params, open and close */ + + /* hardware interface */ + snd_hwdep_t *hwdep; + unsigned int board_type; /* read from embedded once elf file is loaded, 250 = miXart8, 251 = with AES, 252 = with Cobranet */ + + struct snd_dma_device dma_dev; + struct snd_dma_buffer flowinfo; + struct snd_dma_buffer bufferinfo; + + mixart_uid_t uid_console_manager; + int sample_rate; + int ref_count_rate; + + struct semaphore mixer_mutex; /* mutex for mixer */ + +}; + + +#define MIXART_STREAM_STATUS_FREE 0 +#define MIXART_STREAM_STATUS_OPEN 1 +#define MIXART_STREAM_STATUS_RUNNING 2 +#define MIXART_STREAM_STATUS_DRAINING 3 +#define MIXART_STREAM_STATUS_PAUSE 4 + +#define MIXART_PLAYBACK_STREAMS 4 +#define MIXART_CAPTURE_STREAMS 1 + +#define MIXART_PCM_ANALOG 0 +#define MIXART_PCM_DIGITAL 1 +#define MIXART_PCM_TOTAL 2 + +#define MIXART_MAX_STREAM_PER_CARD (MIXART_PCM_TOTAL * (MIXART_PLAYBACK_STREAMS + MIXART_CAPTURE_STREAMS) ) + + +#define MIXART_NOTIFY_CARD_MASK 0xF000 +#define MIXART_NOTIFY_CARD_OFFSET 12 +#define MIXART_NOTIFY_PCM_MASK 0x0F00 +#define MIXART_NOTIFY_PCM_OFFSET 8 +#define MIXART_NOTIFY_CAPT_MASK 0x0080 +#define MIXART_NOTIFY_SUBS_MASK 0x007F + + +struct snd_mixart_stream { + snd_pcm_substream_t *substream; + mixart_pipe_t *pipe; + int pcm_number; + + int status; /* nothing, running, draining */ + + u64 abs_period_elapsed; /* last absolute stream position where period_elapsed was called (multiple of runtime->period_size) */ + u32 buf_periods; /* periods counter in the buffer (< runtime->periods) */ + u32 buf_period_frag; /* defines with buf_period_pos the exact position in the buffer (< runtime->period_size) */ + + int channels; +}; + + +enum mixart_pipe_status { + PIPE_UNDEFINED, + PIPE_STOPPED, + PIPE_RUNNING, + PIPE_CLOCK_SET +}; + +struct snd_mixart_pipe { + mixart_uid_t group_uid; /* id of the pipe, as returned by embedded */ + int stream_count; + mixart_uid_t uid_left_connector; /* UID's for the audio connectors */ + mixart_uid_t uid_right_connector; + enum mixart_pipe_status status; + int references; /* number of subs openned */ + int monitoring; /* pipe used for monitoring issue */ +}; + + +struct snd_mixart { + snd_card_t *card; + mixart_mgr_t *mgr; + int chip_idx; /* zero based */ + snd_hwdep_t *hwdep; /* DSP loader, only for the first card */ + + snd_pcm_t *pcm; /* PCM analog i/o */ + snd_pcm_t *pcm_dig; /* PCM digital i/o */ + + /* allocate stereo pipe for instance */ + mixart_pipe_t pipe_in_ana; + mixart_pipe_t pipe_out_ana; + + /* if AES/EBU daughter board is available, additional pipes possible on pcm_dig */ + mixart_pipe_t pipe_in_dig; + mixart_pipe_t pipe_out_dig; + + mixart_stream_t playback_stream[MIXART_PCM_TOTAL][MIXART_PLAYBACK_STREAMS]; /* 0 = pcm, 1 = pcm_dig */ + mixart_stream_t capture_stream[MIXART_PCM_TOTAL]; /* 0 = pcm, 1 = pcm_dig */ + + /* UID's for the physical io's */ + mixart_uid_t uid_out_analog_physio; + mixart_uid_t uid_in_analog_physio; + + int analog_playback_active[2]; /* Mixer : Master Playback active (!mute) */ + int analog_playback_volume[2]; /* Mixer : Master Playback Volume */ + int analog_capture_volume[2]; /* Mixer : Master Capture Volume */ + int digital_playback_active[2*MIXART_PLAYBACK_STREAMS][2]; /* Mixer : Digital Playback Active [(analog+AES output)*streams][stereo]*/ + int digital_playback_volume[2*MIXART_PLAYBACK_STREAMS][2]; /* Mixer : Digital Playback Volume [(analog+AES output)*streams][stereo]*/ + int digital_capture_volume[2][2]; /* Mixer : Digital Capture Volume [analog+AES output][stereo] */ + int monitoring_active[2]; /* Mixer : Monitoring Active */ + int monitoring_volume[2]; /* Mixer : Monitoring Volume */ +}; + +struct mixart_bufferinfo +{ + u32 buffer_address; + u32 reserved[5]; + u32 available_length; + u32 buffer_id; +}; + +struct mixart_flowinfo +{ + u32 bufferinfo_array_phy_address; + u32 reserved[11]; + u32 bufferinfo_count; + u32 capture; +}; + +/* exported */ +int snd_mixart_create_pcm(mixart_t* chip); +mixart_pipe_t* snd_mixart_add_ref_pipe( mixart_t *chip, int pcm_number, int capture, int monitoring); +int snd_mixart_kill_ref_pipe( mixart_mgr_t *mgr, mixart_pipe_t *pipe, int monitoring); + +#endif /* __SOUND_MIXART_H */ --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/sound/pci/mixart/mixart_hwdep.c 2004-03-07 20:46:50.000000000 -0800 @@ -0,0 +1,572 @@ +/* + * Driver for Digigram miXart soundcards + * + * hwdep device manager + * + * Copyright (c) 2003 by Digigram + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include "mixart.h" +#include "mixart_mixer.h" +#include "mixart_core.h" +#include "mixart_hwdep.h" + + +/* miXart hwdep interface id string */ +#define SND_MIXART_HWDEP_ID "miXart Loader" + +static int mixart_hwdep_open(snd_hwdep_t *hw, struct file *file) +{ + return 0; +} + +static int mixart_hwdep_release(snd_hwdep_t *hw, struct file *file) +{ + return 0; +} + +/** + * wait for a value on a peudo register, exit with a timeout + * + * @param mgr pointer to miXart manager structure + * @param offset unsigned pseudo_register base + offset of value + * @param value value + * @param timeout timeout in centisenconds + */ +static int mixart_wait_nice_for_register_value(mixart_mgr_t *mgr, u32 offset, int is_egal, u32 value, unsigned long timeout) +{ + unsigned long end_time = jiffies + (timeout * HZ / 100); + u32 read; + + do { /* we may take too long time in this loop. + * so give controls back to kernel if needed. + */ + cond_resched(); + + read = readl_be( MIXART_MEM( mgr, offset )); + if(is_egal) { + if(read == value) return 0; + } + else { /* wait for different value */ + if(read != value) return 0; + } + } while ( time_after_eq(end_time, jiffies) ); + + return -EBUSY; +} + + +/* + structures needed to upload elf code packets + */ +typedef struct snd_mixart_elf32_ehdr snd_mixart_elf32_ehdr_t; + +struct snd_mixart_elf32_ehdr { + u8 e_ident[16]; + u16 e_type; + u16 e_machine; + u32 e_version; + u32 e_entry; + u32 e_phoff; + u32 e_shoff; + u32 e_flags; + u16 e_ehsize; + u16 e_phentsize; + u16 e_phnum; + u16 e_shentsize; + u16 e_shnum; + u16 e_shstrndx; +}; + +typedef struct snd_mixart_elf32_phdr snd_mixart_elf32_phdr_t; + +struct snd_mixart_elf32_phdr { + u32 p_type; + u32 p_offset; + u32 p_vaddr; + u32 p_paddr; + u32 p_filesz; + u32 p_memsz; + u32 p_flags; + u32 p_align; +}; + +static int mixart_load_elf(mixart_mgr_t *mgr, snd_hwdep_dsp_image_t *dsp ) +{ + char elf32_magic_number[4] = {0x7f,'E','L','F'}; + snd_mixart_elf32_ehdr_t elf_header; + int i; + + if ( copy_from_user(&elf_header, dsp->image , sizeof(snd_mixart_elf32_ehdr_t)) ) + return -EFAULT; + + for( i=0; i<4; i++ ) + if ( elf32_magic_number[i] != elf_header.e_ident[i] ) + return -EINVAL; + + if( elf_header.e_phoff != 0 ) { + snd_mixart_elf32_phdr_t elf_programheader; + + for( i=0; i < be16_to_cpu(elf_header.e_phnum); i++ ) { + u32 pos = be32_to_cpu(elf_header.e_phoff) + (u32)(i * be16_to_cpu(elf_header.e_phentsize)); + + if( copy_from_user( &elf_programheader, dsp->image + pos, sizeof(elf_programheader) ) ) + return -EFAULT; + + if(elf_programheader.p_type != 0) { + if( elf_programheader.p_filesz != 0 ) { + if(copy_from_user_toio( MIXART_MEM( mgr, be32_to_cpu(elf_programheader.p_vaddr)), + dsp->image + be32_to_cpu( elf_programheader.p_offset ), + be32_to_cpu( elf_programheader.p_filesz ))) + return -EFAULT; + } + } + } + } + return 0; +} + +static int mixart_hwdep_dsp_status(snd_hwdep_t *hw, snd_hwdep_dsp_status_t *info) +{ + mixart_mgr_t *mgr = snd_magic_cast(mixart_mgr_t, hw->private_data, return -ENXIO); + + strcpy(info->id, "miXart"); + info->num_dsps = MIXART_HARDW_FILES_MAX_INDEX; + + if (mgr->hwdep->dsp_loaded & (1 << MIXART_MOTHERBOARD_ELF_INDEX)) + info->chip_ready = 1; + + info->version = MIXART_DRIVER_VERSION; + return 0; +} + +/* + * get basic information and init miXart + */ + +/* audio IDs for request to the board */ +#define MIXART_FIRST_ANA_AUDIO_ID 0 +#define MIXART_FIRST_DIG_AUDIO_ID 8 + +static int mixart_enum_connectors(mixart_mgr_t *mgr) +{ + u32 k; + int err; + mixart_msg_t request; + mixart_enum_connector_resp_t connector; + mixart_audio_info_req_t audio_info_req; + mixart_audio_info_resp_t audio_info; + + audio_info_req.line_max_level = MIXART_FLOAT_P_22_0_TO_HEX; + audio_info_req.micro_max_level = MIXART_FLOAT_M_20_0_TO_HEX; + audio_info_req.cd_max_level = MIXART_FLOAT____0_0_TO_HEX; + + request.message_id = MSG_SYSTEM_ENUM_PLAY_CONNECTOR; + request.uid = (mixart_uid_t){0,0}; /* board num = 0 */ + request.data = NULL; + request.size = 0; + + err = snd_mixart_send_msg(mgr, &request, sizeof(connector), &connector); + if((err < 0) || (connector.error_code) || (connector.uid_count > MIXART_MAX_PHYS_CONNECTORS)) { + snd_printk(KERN_ERR "error MSG_SYSTEM_ENUM_PLAY_CONNECTOR\n"); + return -EINVAL; + } + + for(k=0; k < connector.uid_count; k++) { + mixart_pipe_t* pipe; + + if(k < MIXART_FIRST_DIG_AUDIO_ID) { + pipe = &mgr->chip[k/2]->pipe_out_ana; + } else { + pipe = &mgr->chip[(k-MIXART_FIRST_DIG_AUDIO_ID)/2]->pipe_out_dig; + } + if(k & 1) { + pipe->uid_right_connector = connector.uid[k]; /* odd */ + } else { + pipe->uid_left_connector = connector.uid[k]; /* even */ + } + + /* snd_printk(KERN_DEBUG "playback connector[%d].object_id = %x\n", k, connector.uid[k].object_id); */ + + /* TODO: really need send_msg MSG_CONNECTOR_GET_AUDIO_INFO for each connector ? perhaps for analog level caps ? */ + request.message_id = MSG_CONNECTOR_GET_AUDIO_INFO; + request.uid = connector.uid[k]; + request.data = &audio_info_req; + request.size = sizeof(audio_info_req); + + err = snd_mixart_send_msg(mgr, &request, sizeof(audio_info), &audio_info); + if( err < 0 ) { + snd_printk(KERN_ERR "error MSG_CONNECTOR_GET_AUDIO_INFO\n"); + return err; + } + /*snd_printk(KERN_DEBUG "play analog_info.analog_level_present = %x\n", audio_info.info.analog_info.analog_level_present);*/ + } + + request.message_id = MSG_SYSTEM_ENUM_RECORD_CONNECTOR; + request.uid = (mixart_uid_t){0,0}; /* board num = 0 */ + request.data = NULL; + request.size = 0; + + err = snd_mixart_send_msg(mgr, &request, sizeof(connector), &connector); + if((err < 0) || (connector.error_code) || (connector.uid_count > MIXART_MAX_PHYS_CONNECTORS)) { + snd_printk(KERN_ERR "error MSG_SYSTEM_ENUM_RECORD_CONNECTOR\n"); + return -EINVAL; + } + + for(k=0; k < connector.uid_count; k++) { + mixart_pipe_t* pipe; + + if(k < MIXART_FIRST_DIG_AUDIO_ID) { + pipe = &mgr->chip[k/2]->pipe_in_ana; + } else { + pipe = &mgr->chip[(k-MIXART_FIRST_DIG_AUDIO_ID)/2]->pipe_in_dig; + } + if(k & 1) { + pipe->uid_right_connector = connector.uid[k]; /* odd */ + } else { + pipe->uid_left_connector = connector.uid[k]; /* even */ + } + + /* snd_printk(KERN_DEBUG "capture connector[%d].object_id = %x\n", k, connector.uid[k].object_id); */ + + /* TODO: really need send_msg MSG_CONNECTOR_GET_AUDIO_INFO for each connector ? perhaps for analog level caps ? */ + request.message_id = MSG_CONNECTOR_GET_AUDIO_INFO; + request.uid = connector.uid[k]; + request.data = &audio_info_req; + request.size = sizeof(audio_info_req); + + err = snd_mixart_send_msg(mgr, &request, sizeof(audio_info), &audio_info); + if( err < 0 ) { + snd_printk(KERN_ERR "error MSG_CONNECTOR_GET_AUDIO_INFO\n"); + return err; + } + /*snd_printk(KERN_DEBUG "rec analog_info.analog_level_present = %x\n", audio_info.info.analog_info.analog_level_present);*/ + } + + return 0; +} + +static int mixart_enum_physio(mixart_mgr_t *mgr) +{ + u32 k; + int err; + mixart_msg_t request; + mixart_uid_t get_console_mgr; + mixart_return_uid_t console_mgr; + mixart_uid_enumeration_t phys_io; + + /* get the uid for the console manager */ + get_console_mgr.object_id = 0; + get_console_mgr.desc = MSG_CONSOLE_MANAGER | 0; /* cardindex = 0 */ + + request.message_id = MSG_CONSOLE_GET_CLOCK_UID; + request.uid = get_console_mgr; + request.data = &get_console_mgr; + request.size = sizeof(get_console_mgr); + + err = snd_mixart_send_msg(mgr, &request, sizeof(console_mgr), &console_mgr); + + if( (err < 0) || (console_mgr.error_code != 0) ) { + snd_printk(KERN_DEBUG "error MSG_CONSOLE_GET_CLOCK_UID : err=%x\n", console_mgr.error_code); + return -EINVAL; + } + + /* used later for clock issues ! */ + mgr->uid_console_manager = console_mgr.uid; + + request.message_id = MSG_SYSTEM_ENUM_PHYSICAL_IO; + request.uid = (mixart_uid_t){0,0}; + request.data = &console_mgr.uid; + request.size = sizeof(console_mgr.uid); + + err = snd_mixart_send_msg(mgr, &request, sizeof(phys_io), &phys_io); + if( (err < 0) || ( phys_io.error_code != 0 ) ) { + snd_printk(KERN_ERR "error MSG_SYSTEM_ENUM_PHYSICAL_IO err(%x) error_code(%x)\n", err, phys_io.error_code ); + return -EINVAL; + } + + snd_assert(phys_io.nb_uid >= (MIXART_MAX_CARDS * 2), return -EINVAL); /* min 2 phys io per card (analog in + analog out) */ + + for(k=0; knum_cards; k++) { + mgr->chip[k]->uid_in_analog_physio = phys_io.uid[k]; + mgr->chip[k]->uid_out_analog_physio = phys_io.uid[phys_io.nb_uid/2 + k]; + } + + return 0; +} + + +static int mixart_first_init(mixart_mgr_t *mgr) +{ + u32 k; + int err; + mixart_msg_t request; + + if((err = mixart_enum_connectors(mgr)) < 0) return err; + + if((err = mixart_enum_physio(mgr)) < 0) return err; + + /* send a synchro command to card (necessary to do this before first MSG_STREAM_START_STREAM_GRP_PACKET) */ + /* though why not here */ + request.message_id = MSG_SYSTEM_SEND_SYNCHRO_CMD; + request.uid = (mixart_uid_t){0,0}; + request.data = NULL; + request.size = 0; + /* this command has no data. response is a 32 bit status */ + err = snd_mixart_send_msg(mgr, &request, sizeof(k), &k); + if( (err < 0) || (k != 0) ) { + snd_printk(KERN_ERR "error MSG_SYSTEM_SEND_SYNCHRO_CMD\n"); + return err == 0 ? -EINVAL : err; + } + + return 0; +} + + +/* firmware base addresses (when hard coded) */ +#define MIXART_MOTHERBOARD_XLX_BASE_ADDRESS 0x00600000 + +static int mixart_hwdep_dsp_load(snd_hwdep_t *hw, snd_hwdep_dsp_image_t *dsp) +{ + mixart_mgr_t* mgr = snd_magic_cast(mixart_mgr_t, hw->private_data, return -ENXIO); + int err, card_index; + u32 status_xilinx, status_elf, status_daught; + u32 val; + + /* read motherboard xilinx status */ + status_xilinx = readl_be( MIXART_MEM( mgr,MIXART_PSEUDOREG_MXLX_STATUS_OFFSET )); + /* read elf status */ + status_elf = readl_be( MIXART_MEM( mgr,MIXART_PSEUDOREG_ELF_STATUS_OFFSET )); + /* read daughterboard xilinx status */ + status_daught = readl_be( MIXART_MEM( mgr,MIXART_PSEUDOREG_DXLX_STATUS_OFFSET )); + + /* motherboard xilinx status 5 will say that the board is performing a reset */ + if( status_xilinx == 5 ) { + snd_printk( KERN_ERR "miXart is resetting !\n"); + return -EAGAIN; /* try again later */ + } + + switch (dsp->index) { + case MIXART_MOTHERBOARD_XLX_INDEX: + + /* xilinx already loaded ? */ + if( status_xilinx == 4 ) { + snd_printk( KERN_DEBUG "xilinx is already loaded !\n"); + return 0; + } + /* the status should be 0 == "idle" */ + if( status_xilinx != 0 ) { + snd_printk( KERN_ERR "xilinx load error ! status = %d\n", status_xilinx); + return -EIO; /* modprob -r may help ? */ + } + + /* check xilinx validity */ + snd_assert(((u32*)(dsp->image))[0]==0xFFFFFFFF, return -EINVAL); + snd_assert(dsp->length % 4 == 0, return -EINVAL); + + /* set xilinx status to copying */ + writel_be( 1, MIXART_MEM( mgr, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET )); + + /* setup xilinx base address */ + writel_be( MIXART_MOTHERBOARD_XLX_BASE_ADDRESS, MIXART_MEM( mgr,MIXART_PSEUDOREG_MXLX_BASE_ADDR_OFFSET )); + /* setup code size for xilinx file */ + writel_be( dsp->length, MIXART_MEM( mgr, MIXART_PSEUDOREG_MXLX_SIZE_OFFSET )); + + /* copy xilinx code */ + if (copy_from_user_toio( MIXART_MEM( mgr, MIXART_MOTHERBOARD_XLX_BASE_ADDRESS), dsp->image, dsp->length)) + return -EFAULT; + + /* set xilinx status to copy finished */ + writel_be( 2, MIXART_MEM( mgr, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET )); + + /* return, because no further processing needed */ + return 0; + + case MIXART_MOTHERBOARD_ELF_INDEX: + + if( status_elf == 4 ) { + snd_printk( KERN_DEBUG "elf file already loaded !\n"); + return 0; + } + + /* the status should be 0 == "idle" */ + if( status_elf != 0 ) { + snd_printk( KERN_ERR "elf load error ! status = %d\n", status_elf); + return -EIO; /* modprob -r may help ? */ + } + + /* wait for xilinx status == 4 */ + err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET, 1, 4, 500); /* 5sec */ + if (err < 0) { + snd_printk( KERN_ERR "xilinx was not loaded or could not be started\n"); + return err; + } + + /* init some data on the card */ + writel_be( 0, MIXART_MEM( mgr, MIXART_PSEUDOREG_BOARDNUMBER ) ); /* set miXart boardnumber to 0 */ + writel_be( 0, MIXART_MEM( mgr, MIXART_FLOWTABLE_PTR ) ); /* reset pointer to flow table on miXart */ + + /* set elf status to copying */ + writel_be( 1, MIXART_MEM( mgr, MIXART_PSEUDOREG_ELF_STATUS_OFFSET )); + + /* process the copying of the elf packets */ + err = mixart_load_elf( mgr, dsp); + if (err < 0) return err; + + /* set elf status to copy finished */ + writel_be( 2, MIXART_MEM( mgr, MIXART_PSEUDOREG_ELF_STATUS_OFFSET )); + + /* wait for elf status == 4 */ + err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_ELF_STATUS_OFFSET, 1, 4, 300); /* 3sec */ + if (err < 0) { + snd_printk( KERN_ERR "elf could not be started\n"); + return err; + } + + /* miXart waits at this point on the pointer to the flow table */ + writel_be( (u32)mgr->flowinfo.addr, MIXART_MEM( mgr, MIXART_FLOWTABLE_PTR ) ); /* give pointer of flow table to miXart */ + + return 0; /* return, another xilinx file has to be loaded before */ + + case MIXART_AESEBUBOARD_XLX_INDEX: + default: + + /* elf and xilinx should be loaded */ + if( (status_elf != 4) || (status_xilinx != 4) ) { + printk( KERN_ERR "xilinx or elf not successfully loaded\n"); + return -EIO; /* modprob -r may help ? */ + } + + /* wait for daughter detection != 0 */ + err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DBRD_PRESENCE_OFFSET, 0, 0, 30); /* 300msec */ + if (err < 0) { + snd_printk( KERN_ERR "error starting elf file\n"); + return err; + } + + /* the board type can now be retrieved */ + mgr->board_type = (DAUGHTER_TYPE_MASK & readl_be( MIXART_MEM( mgr, MIXART_PSEUDOREG_DBRD_TYPE_OFFSET))); + + if (mgr->board_type == MIXART_DAUGHTER_TYPE_NONE) + break; /* no daughter board; the file does not have to be loaded, continue after the switch */ + + /* only if aesebu daughter board presence (elf code must run) */ + if (mgr->board_type != MIXART_DAUGHTER_TYPE_AES ) + return -EINVAL; + + /* daughter should be idle */ + if( status_daught != 0 ) { + printk( KERN_ERR "daughter load error ! status = %d\n", status_daught); + return -EIO; /* modprob -r may help ? */ + } + + /* check daughterboard xilinx validity */ + snd_assert(((u32*)(dsp->image))[0]==0xFFFFFFFF, return -EINVAL); + snd_assert(dsp->length % 4 == 0, return -EINVAL); + + /* inform mixart about the size of the file */ + writel_be( dsp->length, MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_SIZE_OFFSET )); + + /* set daughterboard status to 1 */ + writel_be( 1, MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET )); + + /* wait for status == 2 */ + err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET, 1, 2, 30); /* 300msec */ + if (err < 0) { + snd_printk( KERN_ERR "daughter board load error\n"); + return err; + } + + /* get the address where to write the file */ + val = readl_be( MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_BASE_ADDR_OFFSET )); + snd_assert(val != 0, return -EINVAL); + + /* copy daughterboard xilinx code */ + if (copy_from_user_toio( MIXART_MEM( mgr, val), dsp->image, dsp->length)) + return -EFAULT; + + /* set daughterboard status to 4 */ + writel_be( 4, MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET )); + + /* continue with init */ + break; + } /* end of switch file index*/ + + /* wait for daughter status == 3 */ + err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET, 1, 3, 300); /* 3sec */ + if (err < 0) { + snd_printk( KERN_ERR "daughter board could not be initialised\n"); + return err; + } + + /* init mailbox (communication with embedded) */ + snd_mixart_init_mailbox(mgr); + + /* first communication with embedded */ + err = mixart_first_init(mgr); + if (err < 0) { + snd_printk( KERN_ERR "miXart could not be set up\n"); + return err; + } + + /* create devices and mixer in accordance with HW options*/ + for (card_index = 0; card_index < mgr->num_cards; card_index++) { + mixart_t *chip = mgr->chip[card_index]; + + if ((err = snd_mixart_create_pcm(chip)) < 0) + return err; + + if (card_index == 0) { + if ((err = snd_mixart_create_mixer(chip->mgr)) < 0) + return err; + } + + if ((err = snd_card_register(chip->card)) < 0) + return err; + }; + + snd_printdd("miXart firmware downloaded and successfully set up\n"); + + return 0; +} + + +int snd_mixart_hwdep_new(mixart_mgr_t *mgr) +{ + int err; + snd_hwdep_t *hw; + + /* only create hwdep interface for first cardX (see "index" module parameter)*/ + if ((err = snd_hwdep_new(mgr->chip[0]->card, SND_MIXART_HWDEP_ID, 0, &hw)) < 0) + return err; + + hw->iface = SNDRV_HWDEP_IFACE_MIXART; + hw->private_data = mgr; + hw->ops.open = mixart_hwdep_open; + hw->ops.release = mixart_hwdep_release; + hw->ops.dsp_status = mixart_hwdep_dsp_status; + hw->ops.dsp_load = mixart_hwdep_dsp_load; + hw->exclusive = 1; + sprintf(hw->name, SND_MIXART_HWDEP_ID); + mgr->hwdep = hw; + mgr->hwdep->dsp_loaded = 0; + return 0; +} --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/sound/pci/mixart/mixart_hwdep.h 2004-03-07 20:46:50.000000000 -0800 @@ -0,0 +1,146 @@ +/* + * Driver for Digigram miXart soundcards + * + * definitions and makros for basic card access + * + * Copyright (c) 2003 by Digigram + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __SOUND_MIXART_HWDEP_H +#define __SOUND_MIXART_HWDEP_H + +#include + +#define readl_be(x) be32_to_cpu(__raw_readl(x)) +#define writel_be(data,addr) __raw_writel(cpu_to_be32(data),addr) + +#define readl_le(x) le32_to_cpu(__raw_readl(x)) +#define writel_le(data,addr) __raw_writel(cpu_to_le32(data),addr) + +#define MIXART_MEM(mgr,x) ((mgr)->mem[0].virt + (x)) +#define MIXART_REG(mgr,x) ((mgr)->mem[1].virt + (x)) + + +/* Daughter board Type */ +#define DAUGHTER_TYPE_MASK 0x0F +#define DAUGHTER_VER_MASK 0xF0 +#define DAUGHTER_TYPEVER_MASK (DAUGHTER_TYPE_MASK|DAUGHTER_VER_MASK) + +#define MIXART_DAUGHTER_TYPE_NONE 0x00 +#define MIXART_DAUGHTER_TYPE_COBRANET 0x08 +#define MIXART_DAUGHTER_TYPE_AES 0x0E + + + +#define MIXART_BA0_SIZE (16 * 1024 * 1024) /* 16M */ +#define MIXART_BA1_SIZE (4 * 1024) /* 4k */ + +/* + * -----------BAR 0 -------------------------------------------------------------------------------------------------------- + */ +#define MIXART_PSEUDOREG 0x2000 /* base address for pseudoregister */ + +#define MIXART_PSEUDOREG_BOARDNUMBER MIXART_PSEUDOREG+0 /* board number */ + +/* perfmeter (available when elf loaded)*/ +#define MIXART_PSEUDOREG_PERF_STREAM_LOAD_OFFSET MIXART_PSEUDOREG+0x70 /* streaming load */ +#define MIXART_PSEUDOREG_PERF_SYSTEM_LOAD_OFFSET MIXART_PSEUDOREG+0x78 /* system load (reference)*/ +#define MIXART_PSEUDOREG_PERF_MAILBX_LOAD_OFFSET MIXART_PSEUDOREG+0x7C /* mailbox load */ +#define MIXART_PSEUDOREG_PERF_INTERR_LOAD_OFFSET MIXART_PSEUDOREG+0x74 /* interrupt handling load */ + +/* motherboard xilinx loader info */ +#define MIXART_PSEUDOREG_MXLX_BASE_ADDR_OFFSET MIXART_PSEUDOREG+0x9C /* 0x00600000 */ +#define MIXART_PSEUDOREG_MXLX_SIZE_OFFSET MIXART_PSEUDOREG+0xA0 /* xilinx size in bytes */ +#define MIXART_PSEUDOREG_MXLX_STATUS_OFFSET MIXART_PSEUDOREG+0xA4 /* status = EMBEBBED_STAT_XXX */ + +/* elf loader info */ +#define MIXART_PSEUDOREG_ELF_STATUS_OFFSET MIXART_PSEUDOREG+0xB0 /* status = EMBEBBED_STAT_XXX */ + +/* +* after the elf code is loaded, and the flowtable info was passed to it, +* the driver polls on this address, until it shows 1 (presence) or 2 (absence) +* once it is non-zero, the daughter board type may be read +*/ +#define MIXART_PSEUDOREG_DBRD_PRESENCE_OFFSET MIXART_PSEUDOREG+0x990 + +/* Global info structure */ +#define MIXART_PSEUDOREG_DBRD_TYPE_OFFSET MIXART_PSEUDOREG+0x994 /* Type and version of daughterboard */ + + +/* daughterboard xilinx loader info */ +#define MIXART_PSEUDOREG_DXLX_BASE_ADDR_OFFSET MIXART_PSEUDOREG+0x998 /* get the address here where to write the file */ +#define MIXART_PSEUDOREG_DXLX_SIZE_OFFSET MIXART_PSEUDOREG+0x99C /* xilinx size in bytes */ +#define MIXART_PSEUDOREG_DXLX_STATUS_OFFSET MIXART_PSEUDOREG+0x9A0 /* status = EMBEBBED_STAT_XXX */ + +/* */ +#define MIXART_FLOWTABLE_PTR 0x3000 /* pointer to flow table */ + +/* mailbox addresses */ + +/* message DRV -> EMB */ +#define MSG_INBOUND_POST_HEAD 0x010008 /* DRV posts MF + increment4 */ +#define MSG_INBOUND_POST_TAIL 0x01000C /* EMB gets MF + increment4 */ +/* message EMB -> DRV */ +#define MSG_OUTBOUND_POST_TAIL 0x01001C /* DRV gets MF + increment4 */ +#define MSG_OUTBOUND_POST_HEAD 0x010018 /* EMB posts MF + increment4 */ +/* Get Free Frames */ +#define MSG_INBOUND_FREE_TAIL 0x010004 /* DRV gets MFA + increment4 */ +#define MSG_OUTBOUND_FREE_TAIL 0x010014 /* EMB gets MFA + increment4 */ +/* Put Free Frames */ +#define MSG_OUTBOUND_FREE_HEAD 0x010010 /* DRV puts MFA + increment4 */ +#define MSG_INBOUND_FREE_HEAD 0x010000 /* EMB puts MFA + increment4 */ + +/* firmware addresses of the message fifos */ +#define MSG_BOUND_STACK_SIZE 0x004000 /* size of each following stack */ +/* posted messages */ +#define MSG_OUTBOUND_POST_STACK 0x108000 /* stack of messages to the DRV */ +#define MSG_INBOUND_POST_STACK 0x104000 /* stack of messages to the EMB */ +/* available empty messages */ +#define MSG_OUTBOUND_FREE_STACK 0x10C000 /* stack of free enveloped for EMB */ +#define MSG_INBOUND_FREE_STACK 0x100000 /* stack of free enveloped for DRV */ + + +/* defines for mailbox message frames */ +#define MSG_FRAME_OFFSET 0x64 +#define MSG_FRAME_SIZE 0x6400 +#define MSG_FRAME_NUMBER 32 +#define MSG_FROM_AGENT_ITMF_OFFSET (MSG_FRAME_OFFSET + (MSG_FRAME_SIZE * MSG_FRAME_NUMBER)) +#define MSG_TO_AGENT_ITMF_OFFSET (MSG_FROM_AGENT_ITMF_OFFSET + MSG_FRAME_SIZE) +#define MSG_HOST_RSC_PROTECTION (MSG_TO_AGENT_ITMF_OFFSET + MSG_FRAME_SIZE) +#define MSG_AGENT_RSC_PROTECTION (MSG_HOST_RSC_PROTECTION + 4) + + +/* + * -----------BAR 1 -------------------------------------------------------------------------------------------------------- + */ + +/* interrupt addresses and constants */ +#define MIXART_PCI_OMIMR_OFFSET 0x34 /* outbound message interrupt mask register */ +#define MIXART_PCI_OMISR_OFFSET 0x30 /* outbound message interrupt status register */ +#define MIXART_PCI_ODBR_OFFSET 0x60 /* outbound doorbell register */ + +#define MIXART_BA1_BRUTAL_RESET_OFFSET 0x68 /* write 1 in LSBit to reset board */ + +#define MIXART_HOST_ALL_INTERRUPT_MASKED 0x02B /* 0000 0010 1011 */ +#define MIXART_ALLOW_OUTBOUND_DOORBELL 0x023 /* 0000 0010 0011 */ +#define MIXART_OIDI 0x008 /* 0000 0000 1000 */ + + +/* exported */ +int snd_mixart_hwdep_new(mixart_mgr_t *mgr); + +#endif /* __SOUND_MIXART_HWDEP_H */ --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/sound/pci/mixart/mixart_mixer.c 2004-03-07 20:46:50.000000000 -0800 @@ -0,0 +1,1138 @@ +/* + * Driver for Digigram miXart soundcards + * + * mixer callbacks + * + * Copyright (c) 2003 by Digigram + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include "mixart.h" +#include "mixart_core.h" +#include "mixart_hwdep.h" +#include +#include "mixart_mixer.h" + +#define chip_t mixart_t + +static u32 mixart_analog_level[256] = { + 0xc2c00000, /* [000] -96.0 dB */ + 0xc2bf0000, /* [001] -95.5 dB */ + 0xc2be0000, /* [002] -95.0 dB */ + 0xc2bd0000, /* [003] -94.5 dB */ + 0xc2bc0000, /* [004] -94.0 dB */ + 0xc2bb0000, /* [005] -93.5 dB */ + 0xc2ba0000, /* [006] -93.0 dB */ + 0xc2b90000, /* [007] -92.5 dB */ + 0xc2b80000, /* [008] -92.0 dB */ + 0xc2b70000, /* [009] -91.5 dB */ + 0xc2b60000, /* [010] -91.0 dB */ + 0xc2b50000, /* [011] -90.5 dB */ + 0xc2b40000, /* [012] -90.0 dB */ + 0xc2b30000, /* [013] -89.5 dB */ + 0xc2b20000, /* [014] -89.0 dB */ + 0xc2b10000, /* [015] -88.5 dB */ + 0xc2b00000, /* [016] -88.0 dB */ + 0xc2af0000, /* [017] -87.5 dB */ + 0xc2ae0000, /* [018] -87.0 dB */ + 0xc2ad0000, /* [019] -86.5 dB */ + 0xc2ac0000, /* [020] -86.0 dB */ + 0xc2ab0000, /* [021] -85.5 dB */ + 0xc2aa0000, /* [022] -85.0 dB */ + 0xc2a90000, /* [023] -84.5 dB */ + 0xc2a80000, /* [024] -84.0 dB */ + 0xc2a70000, /* [025] -83.5 dB */ + 0xc2a60000, /* [026] -83.0 dB */ + 0xc2a50000, /* [027] -82.5 dB */ + 0xc2a40000, /* [028] -82.0 dB */ + 0xc2a30000, /* [029] -81.5 dB */ + 0xc2a20000, /* [030] -81.0 dB */ + 0xc2a10000, /* [031] -80.5 dB */ + 0xc2a00000, /* [032] -80.0 dB */ + 0xc29f0000, /* [033] -79.5 dB */ + 0xc29e0000, /* [034] -79.0 dB */ + 0xc29d0000, /* [035] -78.5 dB */ + 0xc29c0000, /* [036] -78.0 dB */ + 0xc29b0000, /* [037] -77.5 dB */ + 0xc29a0000, /* [038] -77.0 dB */ + 0xc2990000, /* [039] -76.5 dB */ + 0xc2980000, /* [040] -76.0 dB */ + 0xc2970000, /* [041] -75.5 dB */ + 0xc2960000, /* [042] -75.0 dB */ + 0xc2950000, /* [043] -74.5 dB */ + 0xc2940000, /* [044] -74.0 dB */ + 0xc2930000, /* [045] -73.5 dB */ + 0xc2920000, /* [046] -73.0 dB */ + 0xc2910000, /* [047] -72.5 dB */ + 0xc2900000, /* [048] -72.0 dB */ + 0xc28f0000, /* [049] -71.5 dB */ + 0xc28e0000, /* [050] -71.0 dB */ + 0xc28d0000, /* [051] -70.5 dB */ + 0xc28c0000, /* [052] -70.0 dB */ + 0xc28b0000, /* [053] -69.5 dB */ + 0xc28a0000, /* [054] -69.0 dB */ + 0xc2890000, /* [055] -68.5 dB */ + 0xc2880000, /* [056] -68.0 dB */ + 0xc2870000, /* [057] -67.5 dB */ + 0xc2860000, /* [058] -67.0 dB */ + 0xc2850000, /* [059] -66.5 dB */ + 0xc2840000, /* [060] -66.0 dB */ + 0xc2830000, /* [061] -65.5 dB */ + 0xc2820000, /* [062] -65.0 dB */ + 0xc2810000, /* [063] -64.5 dB */ + 0xc2800000, /* [064] -64.0 dB */ + 0xc27e0000, /* [065] -63.5 dB */ + 0xc27c0000, /* [066] -63.0 dB */ + 0xc27a0000, /* [067] -62.5 dB */ + 0xc2780000, /* [068] -62.0 dB */ + 0xc2760000, /* [069] -61.5 dB */ + 0xc2740000, /* [070] -61.0 dB */ + 0xc2720000, /* [071] -60.5 dB */ + 0xc2700000, /* [072] -60.0 dB */ + 0xc26e0000, /* [073] -59.5 dB */ + 0xc26c0000, /* [074] -59.0 dB */ + 0xc26a0000, /* [075] -58.5 dB */ + 0xc2680000, /* [076] -58.0 dB */ + 0xc2660000, /* [077] -57.5 dB */ + 0xc2640000, /* [078] -57.0 dB */ + 0xc2620000, /* [079] -56.5 dB */ + 0xc2600000, /* [080] -56.0 dB */ + 0xc25e0000, /* [081] -55.5 dB */ + 0xc25c0000, /* [082] -55.0 dB */ + 0xc25a0000, /* [083] -54.5 dB */ + 0xc2580000, /* [084] -54.0 dB */ + 0xc2560000, /* [085] -53.5 dB */ + 0xc2540000, /* [086] -53.0 dB */ + 0xc2520000, /* [087] -52.5 dB */ + 0xc2500000, /* [088] -52.0 dB */ + 0xc24e0000, /* [089] -51.5 dB */ + 0xc24c0000, /* [090] -51.0 dB */ + 0xc24a0000, /* [091] -50.5 dB */ + 0xc2480000, /* [092] -50.0 dB */ + 0xc2460000, /* [093] -49.5 dB */ + 0xc2440000, /* [094] -49.0 dB */ + 0xc2420000, /* [095] -48.5 dB */ + 0xc2400000, /* [096] -48.0 dB */ + 0xc23e0000, /* [097] -47.5 dB */ + 0xc23c0000, /* [098] -47.0 dB */ + 0xc23a0000, /* [099] -46.5 dB */ + 0xc2380000, /* [100] -46.0 dB */ + 0xc2360000, /* [101] -45.5 dB */ + 0xc2340000, /* [102] -45.0 dB */ + 0xc2320000, /* [103] -44.5 dB */ + 0xc2300000, /* [104] -44.0 dB */ + 0xc22e0000, /* [105] -43.5 dB */ + 0xc22c0000, /* [106] -43.0 dB */ + 0xc22a0000, /* [107] -42.5 dB */ + 0xc2280000, /* [108] -42.0 dB */ + 0xc2260000, /* [109] -41.5 dB */ + 0xc2240000, /* [110] -41.0 dB */ + 0xc2220000, /* [111] -40.5 dB */ + 0xc2200000, /* [112] -40.0 dB */ + 0xc21e0000, /* [113] -39.5 dB */ + 0xc21c0000, /* [114] -39.0 dB */ + 0xc21a0000, /* [115] -38.5 dB */ + 0xc2180000, /* [116] -38.0 dB */ + 0xc2160000, /* [117] -37.5 dB */ + 0xc2140000, /* [118] -37.0 dB */ + 0xc2120000, /* [119] -36.5 dB */ + 0xc2100000, /* [120] -36.0 dB */ + 0xc20e0000, /* [121] -35.5 dB */ + 0xc20c0000, /* [122] -35.0 dB */ + 0xc20a0000, /* [123] -34.5 dB */ + 0xc2080000, /* [124] -34.0 dB */ + 0xc2060000, /* [125] -33.5 dB */ + 0xc2040000, /* [126] -33.0 dB */ + 0xc2020000, /* [127] -32.5 dB */ + 0xc2000000, /* [128] -32.0 dB */ + 0xc1fc0000, /* [129] -31.5 dB */ + 0xc1f80000, /* [130] -31.0 dB */ + 0xc1f40000, /* [131] -30.5 dB */ + 0xc1f00000, /* [132] -30.0 dB */ + 0xc1ec0000, /* [133] -29.5 dB */ + 0xc1e80000, /* [134] -29.0 dB */ + 0xc1e40000, /* [135] -28.5 dB */ + 0xc1e00000, /* [136] -28.0 dB */ + 0xc1dc0000, /* [137] -27.5 dB */ + 0xc1d80000, /* [138] -27.0 dB */ + 0xc1d40000, /* [139] -26.5 dB */ + 0xc1d00000, /* [140] -26.0 dB */ + 0xc1cc0000, /* [141] -25.5 dB */ + 0xc1c80000, /* [142] -25.0 dB */ + 0xc1c40000, /* [143] -24.5 dB */ + 0xc1c00000, /* [144] -24.0 dB */ + 0xc1bc0000, /* [145] -23.5 dB */ + 0xc1b80000, /* [146] -23.0 dB */ + 0xc1b40000, /* [147] -22.5 dB */ + 0xc1b00000, /* [148] -22.0 dB */ + 0xc1ac0000, /* [149] -21.5 dB */ + 0xc1a80000, /* [150] -21.0 dB */ + 0xc1a40000, /* [151] -20.5 dB */ + 0xc1a00000, /* [152] -20.0 dB */ + 0xc19c0000, /* [153] -19.5 dB */ + 0xc1980000, /* [154] -19.0 dB */ + 0xc1940000, /* [155] -18.5 dB */ + 0xc1900000, /* [156] -18.0 dB */ + 0xc18c0000, /* [157] -17.5 dB */ + 0xc1880000, /* [158] -17.0 dB */ + 0xc1840000, /* [159] -16.5 dB */ + 0xc1800000, /* [160] -16.0 dB */ + 0xc1780000, /* [161] -15.5 dB */ + 0xc1700000, /* [162] -15.0 dB */ + 0xc1680000, /* [163] -14.5 dB */ + 0xc1600000, /* [164] -14.0 dB */ + 0xc1580000, /* [165] -13.5 dB */ + 0xc1500000, /* [166] -13.0 dB */ + 0xc1480000, /* [167] -12.5 dB */ + 0xc1400000, /* [168] -12.0 dB */ + 0xc1380000, /* [169] -11.5 dB */ + 0xc1300000, /* [170] -11.0 dB */ + 0xc1280000, /* [171] -10.5 dB */ + 0xc1200000, /* [172] -10.0 dB */ + 0xc1180000, /* [173] -9.5 dB */ + 0xc1100000, /* [174] -9.0 dB */ + 0xc1080000, /* [175] -8.5 dB */ + 0xc1000000, /* [176] -8.0 dB */ + 0xc0f00000, /* [177] -7.5 dB */ + 0xc0e00000, /* [178] -7.0 dB */ + 0xc0d00000, /* [179] -6.5 dB */ + 0xc0c00000, /* [180] -6.0 dB */ + 0xc0b00000, /* [181] -5.5 dB */ + 0xc0a00000, /* [182] -5.0 dB */ + 0xc0900000, /* [183] -4.5 dB */ + 0xc0800000, /* [184] -4.0 dB */ + 0xc0600000, /* [185] -3.5 dB */ + 0xc0400000, /* [186] -3.0 dB */ + 0xc0200000, /* [187] -2.5 dB */ + 0xc0000000, /* [188] -2.0 dB */ + 0xbfc00000, /* [189] -1.5 dB */ + 0xbf800000, /* [190] -1.0 dB */ + 0xbf000000, /* [191] -0.5 dB */ + 0x00000000, /* [192] 0.0 dB */ + 0x3f000000, /* [193] 0.5 dB */ + 0x3f800000, /* [194] 1.0 dB */ + 0x3fc00000, /* [195] 1.5 dB */ + 0x40000000, /* [196] 2.0 dB */ + 0x40200000, /* [197] 2.5 dB */ + 0x40400000, /* [198] 3.0 dB */ + 0x40600000, /* [199] 3.5 dB */ + 0x40800000, /* [200] 4.0 dB */ + 0x40900000, /* [201] 4.5 dB */ + 0x40a00000, /* [202] 5.0 dB */ + 0x40b00000, /* [203] 5.5 dB */ + 0x40c00000, /* [204] 6.0 dB */ + 0x40d00000, /* [205] 6.5 dB */ + 0x40e00000, /* [206] 7.0 dB */ + 0x40f00000, /* [207] 7.5 dB */ + 0x41000000, /* [208] 8.0 dB */ + 0x41080000, /* [209] 8.5 dB */ + 0x41100000, /* [210] 9.0 dB */ + 0x41180000, /* [211] 9.5 dB */ + 0x41200000, /* [212] 10.0 dB */ + 0x41280000, /* [213] 10.5 dB */ + 0x41300000, /* [214] 11.0 dB */ + 0x41380000, /* [215] 11.5 dB */ + 0x41400000, /* [216] 12.0 dB */ + 0x41480000, /* [217] 12.5 dB */ + 0x41500000, /* [218] 13.0 dB */ + 0x41580000, /* [219] 13.5 dB */ + 0x41600000, /* [220] 14.0 dB */ + 0x41680000, /* [221] 14.5 dB */ + 0x41700000, /* [222] 15.0 dB */ + 0x41780000, /* [223] 15.5 dB */ + 0x41800000, /* [224] 16.0 dB */ + 0x41840000, /* [225] 16.5 dB */ + 0x41880000, /* [226] 17.0 dB */ + 0x418c0000, /* [227] 17.5 dB */ + 0x41900000, /* [228] 18.0 dB */ + 0x41940000, /* [229] 18.5 dB */ + 0x41980000, /* [230] 19.0 dB */ + 0x419c0000, /* [231] 19.5 dB */ + 0x41a00000, /* [232] 20.0 dB */ + 0x41a40000, /* [233] 20.5 dB */ + 0x41a80000, /* [234] 21.0 dB */ + 0x41ac0000, /* [235] 21.5 dB */ + 0x41b00000, /* [236] 22.0 dB */ + 0x41b40000, /* [237] 22.5 dB */ + 0x41b80000, /* [238] 23.0 dB */ + 0x41bc0000, /* [239] 23.5 dB */ + 0x41c00000, /* [240] 24.0 dB */ + 0x41c40000, /* [241] 24.5 dB */ + 0x41c80000, /* [242] 25.0 dB */ + 0x41cc0000, /* [243] 25.5 dB */ + 0x41d00000, /* [244] 26.0 dB */ + 0x41d40000, /* [245] 26.5 dB */ + 0x41d80000, /* [246] 27.0 dB */ + 0x41dc0000, /* [247] 27.5 dB */ + 0x41e00000, /* [248] 28.0 dB */ + 0x41e40000, /* [249] 28.5 dB */ + 0x41e80000, /* [250] 29.0 dB */ + 0x41ec0000, /* [251] 29.5 dB */ + 0x41f00000, /* [252] 30.0 dB */ + 0x41f40000, /* [253] 30.5 dB */ + 0x41f80000, /* [254] 31.0 dB */ + 0x41fc0000, /* [255] 31.5 dB */ +}; + +#define MIXART_ANALOG_CAPTURE_LEVEL_MIN 0 /* -96.0 dB + 8.0 dB = -88.0 dB */ +#define MIXART_ANALOG_CAPTURE_LEVEL_MAX 255 /* 31.5 dB + 8.0 dB = 39.5 dB */ +#define MIXART_ANALOG_CAPTURE_ZERO_LEVEL 176 /* -8.0 dB + 8.0 dB = 0.0 dB */ + +#define MIXART_ANALOG_PLAYBACK_LEVEL_MIN 0 /* -96.0 dB + 1.5 dB = -94.5 dB (possible is down to (-114.0+1.5)dB) */ +#define MIXART_ANALOG_PLAYBACK_LEVEL_MAX 192 /* 0.0 dB + 1.5 dB = 1.5 dB */ +#define MIXART_ANALOG_PLAYBACK_ZERO_LEVEL 189 /* -1.5 dB + 1.5 dB = 0.0 dB */ + +static int mixart_update_analog_audio_level(mixart_t* chip, int is_capture) +{ + int i, err; + mixart_msg_t request; + mixart_io_level_t io_level; + mixart_return_uid_t resp; + + memset(&io_level, 0, sizeof(io_level)); + io_level.channel = -1; /* left and right */ + + for(i=0; i<2; i++) { + if(is_capture) { + io_level.level[i].analog_level = mixart_analog_level[chip->analog_capture_volume[i]]; + } else { + if(chip->analog_playback_active[i]) + io_level.level[i].analog_level = mixart_analog_level[chip->analog_playback_volume[i]]; + else + io_level.level[i].analog_level = mixart_analog_level[MIXART_ANALOG_PLAYBACK_LEVEL_MIN]; + } + } + + if(is_capture) request.uid = chip->uid_in_analog_physio; + else request.uid = chip->uid_out_analog_physio; + request.message_id = MSG_PHYSICALIO_SET_LEVEL; + request.data = &io_level; + request.size = sizeof(io_level); + + err = snd_mixart_send_msg(chip->mgr, &request, sizeof(resp), &resp); + if((err<0) || (resp.error_code)) { + snd_printk(KERN_DEBUG "error MSG_PHYSICALIO_SET_LEVEL card(%d) is_capture(%d) error_code(%x)\n", chip->chip_idx, is_capture, resp.error_code); + return -EINVAL; + } + return 0; +} + +/* + * analog level control + */ +static int mixart_analog_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + if(kcontrol->private_value == 0) { /* playback */ + uinfo->value.integer.min = MIXART_ANALOG_PLAYBACK_LEVEL_MIN; /* -96 dB */ + uinfo->value.integer.max = MIXART_ANALOG_PLAYBACK_LEVEL_MAX; /* 0 dB */ + } else { /* capture */ + uinfo->value.integer.min = MIXART_ANALOG_CAPTURE_LEVEL_MIN; /* -96 dB */ + uinfo->value.integer.max = MIXART_ANALOG_CAPTURE_LEVEL_MAX; /* 31.5 dB */ + } + return 0; +} + +static int mixart_analog_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + mixart_t *chip = snd_kcontrol_chip(kcontrol); + down(&chip->mgr->mixer_mutex); + if(kcontrol->private_value == 0) { /* playback */ + ucontrol->value.integer.value[0] = chip->analog_playback_volume[0]; + ucontrol->value.integer.value[1] = chip->analog_playback_volume[1]; + } else { /* capture */ + ucontrol->value.integer.value[0] = chip->analog_capture_volume[0]; + ucontrol->value.integer.value[1] = chip->analog_capture_volume[1]; + } + up(&chip->mgr->mixer_mutex); + return 0; +} + +static int mixart_analog_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + mixart_t *chip = snd_kcontrol_chip(kcontrol); + int changed = 0; + int is_capture, i; + + down(&chip->mgr->mixer_mutex); + is_capture = (kcontrol->private_value != 0); + for(i=0; i<2; i++) { + int new_volume = ucontrol->value.integer.value[i]; + int* stored_volume = is_capture ? &chip->analog_capture_volume[i] : &chip->analog_playback_volume[i]; + if(*stored_volume != new_volume) { + *stored_volume = new_volume; + changed = 1; + } + } + if(changed) mixart_update_analog_audio_level(chip, is_capture); + up(&chip->mgr->mixer_mutex); + return changed; +} + +static snd_kcontrol_new_t mixart_control_analog_level = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* name will be filled later */ + .info = mixart_analog_vol_info, + .get = mixart_analog_vol_get, + .put = mixart_analog_vol_put, +}; + +/* shared */ +static int mixart_sw_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int mixart_audio_sw_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + mixart_t *chip = snd_kcontrol_chip(kcontrol); + + down(&chip->mgr->mixer_mutex); + ucontrol->value.integer.value[0] = chip->analog_playback_active[0]; + ucontrol->value.integer.value[1] = chip->analog_playback_active[1]; + up(&chip->mgr->mixer_mutex); + return 0; +} + +static int mixart_audio_sw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + mixart_t *chip = snd_kcontrol_chip(kcontrol); + int i, changed = 0; + down(&chip->mgr->mixer_mutex); + for(i=0; i<2; i++) { + if(chip->analog_playback_active[i] != ucontrol->value.integer.value[i]) { + chip->analog_playback_active[i] = ucontrol->value.integer.value[i]; + changed = 1; + } + } + if(changed) mixart_update_analog_audio_level(chip, 0); /* update playback levels */ + up(&chip->mgr->mixer_mutex); + return changed; +} + +static snd_kcontrol_new_t mixart_control_output_switch = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = mixart_sw_info, /* shared */ + .get = mixart_audio_sw_get, + .put = mixart_audio_sw_put +}; + +static u32 mixart_digital_level[256] = { + 0x00000000, /* [000] = 0.00e+000 = mute if <= -109.5dB */ + 0x366e1c7a, /* [001] = 3.55e-006 = pow(10.0, 0.05 * -109.0dB) */ + 0x367c3860, /* [002] = 3.76e-006 = pow(10.0, 0.05 * -108.5dB) */ + 0x36859525, /* [003] = 3.98e-006 = pow(10.0, 0.05 * -108.0dB) */ + 0x368d7f74, /* [004] = 4.22e-006 = pow(10.0, 0.05 * -107.5dB) */ + 0x3695e1d4, /* [005] = 4.47e-006 = pow(10.0, 0.05 * -107.0dB) */ + 0x369ec362, /* [006] = 4.73e-006 = pow(10.0, 0.05 * -106.5dB) */ + 0x36a82ba8, /* [007] = 5.01e-006 = pow(10.0, 0.05 * -106.0dB) */ + 0x36b222a0, /* [008] = 5.31e-006 = pow(10.0, 0.05 * -105.5dB) */ + 0x36bcb0c1, /* [009] = 5.62e-006 = pow(10.0, 0.05 * -105.0dB) */ + 0x36c7defd, /* [010] = 5.96e-006 = pow(10.0, 0.05 * -104.5dB) */ + 0x36d3b6d3, /* [011] = 6.31e-006 = pow(10.0, 0.05 * -104.0dB) */ + 0x36e0424e, /* [012] = 6.68e-006 = pow(10.0, 0.05 * -103.5dB) */ + 0x36ed8c14, /* [013] = 7.08e-006 = pow(10.0, 0.05 * -103.0dB) */ + 0x36fb9f6c, /* [014] = 7.50e-006 = pow(10.0, 0.05 * -102.5dB) */ + 0x37054423, /* [015] = 7.94e-006 = pow(10.0, 0.05 * -102.0dB) */ + 0x370d29a5, /* [016] = 8.41e-006 = pow(10.0, 0.05 * -101.5dB) */ + 0x371586f0, /* [017] = 8.91e-006 = pow(10.0, 0.05 * -101.0dB) */ + 0x371e631b, /* [018] = 9.44e-006 = pow(10.0, 0.05 * -100.5dB) */ + 0x3727c5ac, /* [019] = 1.00e-005 = pow(10.0, 0.05 * -100.0dB) */ + 0x3731b69a, /* [020] = 1.06e-005 = pow(10.0, 0.05 * -99.5dB) */ + 0x373c3e53, /* [021] = 1.12e-005 = pow(10.0, 0.05 * -99.0dB) */ + 0x374765c8, /* [022] = 1.19e-005 = pow(10.0, 0.05 * -98.5dB) */ + 0x3753366f, /* [023] = 1.26e-005 = pow(10.0, 0.05 * -98.0dB) */ + 0x375fba4f, /* [024] = 1.33e-005 = pow(10.0, 0.05 * -97.5dB) */ + 0x376cfc07, /* [025] = 1.41e-005 = pow(10.0, 0.05 * -97.0dB) */ + 0x377b06d5, /* [026] = 1.50e-005 = pow(10.0, 0.05 * -96.5dB) */ + 0x3784f352, /* [027] = 1.58e-005 = pow(10.0, 0.05 * -96.0dB) */ + 0x378cd40b, /* [028] = 1.68e-005 = pow(10.0, 0.05 * -95.5dB) */ + 0x37952c42, /* [029] = 1.78e-005 = pow(10.0, 0.05 * -95.0dB) */ + 0x379e030e, /* [030] = 1.88e-005 = pow(10.0, 0.05 * -94.5dB) */ + 0x37a75fef, /* [031] = 2.00e-005 = pow(10.0, 0.05 * -94.0dB) */ + 0x37b14ad5, /* [032] = 2.11e-005 = pow(10.0, 0.05 * -93.5dB) */ + 0x37bbcc2c, /* [033] = 2.24e-005 = pow(10.0, 0.05 * -93.0dB) */ + 0x37c6ecdd, /* [034] = 2.37e-005 = pow(10.0, 0.05 * -92.5dB) */ + 0x37d2b65a, /* [035] = 2.51e-005 = pow(10.0, 0.05 * -92.0dB) */ + 0x37df32a3, /* [036] = 2.66e-005 = pow(10.0, 0.05 * -91.5dB) */ + 0x37ec6c50, /* [037] = 2.82e-005 = pow(10.0, 0.05 * -91.0dB) */ + 0x37fa6e9b, /* [038] = 2.99e-005 = pow(10.0, 0.05 * -90.5dB) */ + 0x3804a2b3, /* [039] = 3.16e-005 = pow(10.0, 0.05 * -90.0dB) */ + 0x380c7ea4, /* [040] = 3.35e-005 = pow(10.0, 0.05 * -89.5dB) */ + 0x3814d1cc, /* [041] = 3.55e-005 = pow(10.0, 0.05 * -89.0dB) */ + 0x381da33c, /* [042] = 3.76e-005 = pow(10.0, 0.05 * -88.5dB) */ + 0x3826fa6f, /* [043] = 3.98e-005 = pow(10.0, 0.05 * -88.0dB) */ + 0x3830df51, /* [044] = 4.22e-005 = pow(10.0, 0.05 * -87.5dB) */ + 0x383b5a49, /* [045] = 4.47e-005 = pow(10.0, 0.05 * -87.0dB) */ + 0x3846743b, /* [046] = 4.73e-005 = pow(10.0, 0.05 * -86.5dB) */ + 0x38523692, /* [047] = 5.01e-005 = pow(10.0, 0.05 * -86.0dB) */ + 0x385eab48, /* [048] = 5.31e-005 = pow(10.0, 0.05 * -85.5dB) */ + 0x386bdcf1, /* [049] = 5.62e-005 = pow(10.0, 0.05 * -85.0dB) */ + 0x3879d6bc, /* [050] = 5.96e-005 = pow(10.0, 0.05 * -84.5dB) */ + 0x38845244, /* [051] = 6.31e-005 = pow(10.0, 0.05 * -84.0dB) */ + 0x388c2971, /* [052] = 6.68e-005 = pow(10.0, 0.05 * -83.5dB) */ + 0x3894778d, /* [053] = 7.08e-005 = pow(10.0, 0.05 * -83.0dB) */ + 0x389d43a4, /* [054] = 7.50e-005 = pow(10.0, 0.05 * -82.5dB) */ + 0x38a6952c, /* [055] = 7.94e-005 = pow(10.0, 0.05 * -82.0dB) */ + 0x38b0740f, /* [056] = 8.41e-005 = pow(10.0, 0.05 * -81.5dB) */ + 0x38bae8ac, /* [057] = 8.91e-005 = pow(10.0, 0.05 * -81.0dB) */ + 0x38c5fbe2, /* [058] = 9.44e-005 = pow(10.0, 0.05 * -80.5dB) */ + 0x38d1b717, /* [059] = 1.00e-004 = pow(10.0, 0.05 * -80.0dB) */ + 0x38de2440, /* [060] = 1.06e-004 = pow(10.0, 0.05 * -79.5dB) */ + 0x38eb4de8, /* [061] = 1.12e-004 = pow(10.0, 0.05 * -79.0dB) */ + 0x38f93f3a, /* [062] = 1.19e-004 = pow(10.0, 0.05 * -78.5dB) */ + 0x39040206, /* [063] = 1.26e-004 = pow(10.0, 0.05 * -78.0dB) */ + 0x390bd472, /* [064] = 1.33e-004 = pow(10.0, 0.05 * -77.5dB) */ + 0x39141d84, /* [065] = 1.41e-004 = pow(10.0, 0.05 * -77.0dB) */ + 0x391ce445, /* [066] = 1.50e-004 = pow(10.0, 0.05 * -76.5dB) */ + 0x39263027, /* [067] = 1.58e-004 = pow(10.0, 0.05 * -76.0dB) */ + 0x3930090d, /* [068] = 1.68e-004 = pow(10.0, 0.05 * -75.5dB) */ + 0x393a7753, /* [069] = 1.78e-004 = pow(10.0, 0.05 * -75.0dB) */ + 0x394583d2, /* [070] = 1.88e-004 = pow(10.0, 0.05 * -74.5dB) */ + 0x395137ea, /* [071] = 2.00e-004 = pow(10.0, 0.05 * -74.0dB) */ + 0x395d9d8a, /* [072] = 2.11e-004 = pow(10.0, 0.05 * -73.5dB) */ + 0x396abf37, /* [073] = 2.24e-004 = pow(10.0, 0.05 * -73.0dB) */ + 0x3978a814, /* [074] = 2.37e-004 = pow(10.0, 0.05 * -72.5dB) */ + 0x3983b1f8, /* [075] = 2.51e-004 = pow(10.0, 0.05 * -72.0dB) */ + 0x398b7fa6, /* [076] = 2.66e-004 = pow(10.0, 0.05 * -71.5dB) */ + 0x3993c3b2, /* [077] = 2.82e-004 = pow(10.0, 0.05 * -71.0dB) */ + 0x399c8521, /* [078] = 2.99e-004 = pow(10.0, 0.05 * -70.5dB) */ + 0x39a5cb5f, /* [079] = 3.16e-004 = pow(10.0, 0.05 * -70.0dB) */ + 0x39af9e4d, /* [080] = 3.35e-004 = pow(10.0, 0.05 * -69.5dB) */ + 0x39ba063f, /* [081] = 3.55e-004 = pow(10.0, 0.05 * -69.0dB) */ + 0x39c50c0b, /* [082] = 3.76e-004 = pow(10.0, 0.05 * -68.5dB) */ + 0x39d0b90a, /* [083] = 3.98e-004 = pow(10.0, 0.05 * -68.0dB) */ + 0x39dd1726, /* [084] = 4.22e-004 = pow(10.0, 0.05 * -67.5dB) */ + 0x39ea30db, /* [085] = 4.47e-004 = pow(10.0, 0.05 * -67.0dB) */ + 0x39f81149, /* [086] = 4.73e-004 = pow(10.0, 0.05 * -66.5dB) */ + 0x3a03621b, /* [087] = 5.01e-004 = pow(10.0, 0.05 * -66.0dB) */ + 0x3a0b2b0d, /* [088] = 5.31e-004 = pow(10.0, 0.05 * -65.5dB) */ + 0x3a136a16, /* [089] = 5.62e-004 = pow(10.0, 0.05 * -65.0dB) */ + 0x3a1c2636, /* [090] = 5.96e-004 = pow(10.0, 0.05 * -64.5dB) */ + 0x3a2566d5, /* [091] = 6.31e-004 = pow(10.0, 0.05 * -64.0dB) */ + 0x3a2f33cd, /* [092] = 6.68e-004 = pow(10.0, 0.05 * -63.5dB) */ + 0x3a399570, /* [093] = 7.08e-004 = pow(10.0, 0.05 * -63.0dB) */ + 0x3a44948c, /* [094] = 7.50e-004 = pow(10.0, 0.05 * -62.5dB) */ + 0x3a503a77, /* [095] = 7.94e-004 = pow(10.0, 0.05 * -62.0dB) */ + 0x3a5c9112, /* [096] = 8.41e-004 = pow(10.0, 0.05 * -61.5dB) */ + 0x3a69a2d7, /* [097] = 8.91e-004 = pow(10.0, 0.05 * -61.0dB) */ + 0x3a777ada, /* [098] = 9.44e-004 = pow(10.0, 0.05 * -60.5dB) */ + 0x3a83126f, /* [099] = 1.00e-003 = pow(10.0, 0.05 * -60.0dB) */ + 0x3a8ad6a8, /* [100] = 1.06e-003 = pow(10.0, 0.05 * -59.5dB) */ + 0x3a9310b1, /* [101] = 1.12e-003 = pow(10.0, 0.05 * -59.0dB) */ + 0x3a9bc784, /* [102] = 1.19e-003 = pow(10.0, 0.05 * -58.5dB) */ + 0x3aa50287, /* [103] = 1.26e-003 = pow(10.0, 0.05 * -58.0dB) */ + 0x3aaec98e, /* [104] = 1.33e-003 = pow(10.0, 0.05 * -57.5dB) */ + 0x3ab924e5, /* [105] = 1.41e-003 = pow(10.0, 0.05 * -57.0dB) */ + 0x3ac41d56, /* [106] = 1.50e-003 = pow(10.0, 0.05 * -56.5dB) */ + 0x3acfbc31, /* [107] = 1.58e-003 = pow(10.0, 0.05 * -56.0dB) */ + 0x3adc0b51, /* [108] = 1.68e-003 = pow(10.0, 0.05 * -55.5dB) */ + 0x3ae91528, /* [109] = 1.78e-003 = pow(10.0, 0.05 * -55.0dB) */ + 0x3af6e4c6, /* [110] = 1.88e-003 = pow(10.0, 0.05 * -54.5dB) */ + 0x3b02c2f2, /* [111] = 2.00e-003 = pow(10.0, 0.05 * -54.0dB) */ + 0x3b0a8276, /* [112] = 2.11e-003 = pow(10.0, 0.05 * -53.5dB) */ + 0x3b12b782, /* [113] = 2.24e-003 = pow(10.0, 0.05 * -53.0dB) */ + 0x3b1b690d, /* [114] = 2.37e-003 = pow(10.0, 0.05 * -52.5dB) */ + 0x3b249e76, /* [115] = 2.51e-003 = pow(10.0, 0.05 * -52.0dB) */ + 0x3b2e5f8f, /* [116] = 2.66e-003 = pow(10.0, 0.05 * -51.5dB) */ + 0x3b38b49f, /* [117] = 2.82e-003 = pow(10.0, 0.05 * -51.0dB) */ + 0x3b43a669, /* [118] = 2.99e-003 = pow(10.0, 0.05 * -50.5dB) */ + 0x3b4f3e37, /* [119] = 3.16e-003 = pow(10.0, 0.05 * -50.0dB) */ + 0x3b5b85e0, /* [120] = 3.35e-003 = pow(10.0, 0.05 * -49.5dB) */ + 0x3b6887cf, /* [121] = 3.55e-003 = pow(10.0, 0.05 * -49.0dB) */ + 0x3b764f0e, /* [122] = 3.76e-003 = pow(10.0, 0.05 * -48.5dB) */ + 0x3b8273a6, /* [123] = 3.98e-003 = pow(10.0, 0.05 * -48.0dB) */ + 0x3b8a2e77, /* [124] = 4.22e-003 = pow(10.0, 0.05 * -47.5dB) */ + 0x3b925e89, /* [125] = 4.47e-003 = pow(10.0, 0.05 * -47.0dB) */ + 0x3b9b0ace, /* [126] = 4.73e-003 = pow(10.0, 0.05 * -46.5dB) */ + 0x3ba43aa2, /* [127] = 5.01e-003 = pow(10.0, 0.05 * -46.0dB) */ + 0x3badf5d1, /* [128] = 5.31e-003 = pow(10.0, 0.05 * -45.5dB) */ + 0x3bb8449c, /* [129] = 5.62e-003 = pow(10.0, 0.05 * -45.0dB) */ + 0x3bc32fc3, /* [130] = 5.96e-003 = pow(10.0, 0.05 * -44.5dB) */ + 0x3bcec08a, /* [131] = 6.31e-003 = pow(10.0, 0.05 * -44.0dB) */ + 0x3bdb00c0, /* [132] = 6.68e-003 = pow(10.0, 0.05 * -43.5dB) */ + 0x3be7facc, /* [133] = 7.08e-003 = pow(10.0, 0.05 * -43.0dB) */ + 0x3bf5b9b0, /* [134] = 7.50e-003 = pow(10.0, 0.05 * -42.5dB) */ + 0x3c02248a, /* [135] = 7.94e-003 = pow(10.0, 0.05 * -42.0dB) */ + 0x3c09daac, /* [136] = 8.41e-003 = pow(10.0, 0.05 * -41.5dB) */ + 0x3c1205c6, /* [137] = 8.91e-003 = pow(10.0, 0.05 * -41.0dB) */ + 0x3c1aacc8, /* [138] = 9.44e-003 = pow(10.0, 0.05 * -40.5dB) */ + 0x3c23d70a, /* [139] = 1.00e-002 = pow(10.0, 0.05 * -40.0dB) */ + 0x3c2d8c52, /* [140] = 1.06e-002 = pow(10.0, 0.05 * -39.5dB) */ + 0x3c37d4dd, /* [141] = 1.12e-002 = pow(10.0, 0.05 * -39.0dB) */ + 0x3c42b965, /* [142] = 1.19e-002 = pow(10.0, 0.05 * -38.5dB) */ + 0x3c4e4329, /* [143] = 1.26e-002 = pow(10.0, 0.05 * -38.0dB) */ + 0x3c5a7bf1, /* [144] = 1.33e-002 = pow(10.0, 0.05 * -37.5dB) */ + 0x3c676e1e, /* [145] = 1.41e-002 = pow(10.0, 0.05 * -37.0dB) */ + 0x3c7524ac, /* [146] = 1.50e-002 = pow(10.0, 0.05 * -36.5dB) */ + 0x3c81d59f, /* [147] = 1.58e-002 = pow(10.0, 0.05 * -36.0dB) */ + 0x3c898712, /* [148] = 1.68e-002 = pow(10.0, 0.05 * -35.5dB) */ + 0x3c91ad39, /* [149] = 1.78e-002 = pow(10.0, 0.05 * -35.0dB) */ + 0x3c9a4efc, /* [150] = 1.88e-002 = pow(10.0, 0.05 * -34.5dB) */ + 0x3ca373af, /* [151] = 2.00e-002 = pow(10.0, 0.05 * -34.0dB) */ + 0x3cad2314, /* [152] = 2.11e-002 = pow(10.0, 0.05 * -33.5dB) */ + 0x3cb76563, /* [153] = 2.24e-002 = pow(10.0, 0.05 * -33.0dB) */ + 0x3cc24350, /* [154] = 2.37e-002 = pow(10.0, 0.05 * -32.5dB) */ + 0x3ccdc614, /* [155] = 2.51e-002 = pow(10.0, 0.05 * -32.0dB) */ + 0x3cd9f773, /* [156] = 2.66e-002 = pow(10.0, 0.05 * -31.5dB) */ + 0x3ce6e1c6, /* [157] = 2.82e-002 = pow(10.0, 0.05 * -31.0dB) */ + 0x3cf49003, /* [158] = 2.99e-002 = pow(10.0, 0.05 * -30.5dB) */ + 0x3d0186e2, /* [159] = 3.16e-002 = pow(10.0, 0.05 * -30.0dB) */ + 0x3d0933ac, /* [160] = 3.35e-002 = pow(10.0, 0.05 * -29.5dB) */ + 0x3d1154e1, /* [161] = 3.55e-002 = pow(10.0, 0.05 * -29.0dB) */ + 0x3d19f169, /* [162] = 3.76e-002 = pow(10.0, 0.05 * -28.5dB) */ + 0x3d231090, /* [163] = 3.98e-002 = pow(10.0, 0.05 * -28.0dB) */ + 0x3d2cba15, /* [164] = 4.22e-002 = pow(10.0, 0.05 * -27.5dB) */ + 0x3d36f62b, /* [165] = 4.47e-002 = pow(10.0, 0.05 * -27.0dB) */ + 0x3d41cd81, /* [166] = 4.73e-002 = pow(10.0, 0.05 * -26.5dB) */ + 0x3d4d494a, /* [167] = 5.01e-002 = pow(10.0, 0.05 * -26.0dB) */ + 0x3d597345, /* [168] = 5.31e-002 = pow(10.0, 0.05 * -25.5dB) */ + 0x3d6655c3, /* [169] = 5.62e-002 = pow(10.0, 0.05 * -25.0dB) */ + 0x3d73fbb4, /* [170] = 5.96e-002 = pow(10.0, 0.05 * -24.5dB) */ + 0x3d813856, /* [171] = 6.31e-002 = pow(10.0, 0.05 * -24.0dB) */ + 0x3d88e078, /* [172] = 6.68e-002 = pow(10.0, 0.05 * -23.5dB) */ + 0x3d90fcbf, /* [173] = 7.08e-002 = pow(10.0, 0.05 * -23.0dB) */ + 0x3d99940e, /* [174] = 7.50e-002 = pow(10.0, 0.05 * -22.5dB) */ + 0x3da2adad, /* [175] = 7.94e-002 = pow(10.0, 0.05 * -22.0dB) */ + 0x3dac5156, /* [176] = 8.41e-002 = pow(10.0, 0.05 * -21.5dB) */ + 0x3db68738, /* [177] = 8.91e-002 = pow(10.0, 0.05 * -21.0dB) */ + 0x3dc157fb, /* [178] = 9.44e-002 = pow(10.0, 0.05 * -20.5dB) */ + 0x3dcccccd, /* [179] = 1.00e-001 = pow(10.0, 0.05 * -20.0dB) */ + 0x3dd8ef67, /* [180] = 1.06e-001 = pow(10.0, 0.05 * -19.5dB) */ + 0x3de5ca15, /* [181] = 1.12e-001 = pow(10.0, 0.05 * -19.0dB) */ + 0x3df367bf, /* [182] = 1.19e-001 = pow(10.0, 0.05 * -18.5dB) */ + 0x3e00e9f9, /* [183] = 1.26e-001 = pow(10.0, 0.05 * -18.0dB) */ + 0x3e088d77, /* [184] = 1.33e-001 = pow(10.0, 0.05 * -17.5dB) */ + 0x3e10a4d3, /* [185] = 1.41e-001 = pow(10.0, 0.05 * -17.0dB) */ + 0x3e1936ec, /* [186] = 1.50e-001 = pow(10.0, 0.05 * -16.5dB) */ + 0x3e224b06, /* [187] = 1.58e-001 = pow(10.0, 0.05 * -16.0dB) */ + 0x3e2be8d7, /* [188] = 1.68e-001 = pow(10.0, 0.05 * -15.5dB) */ + 0x3e361887, /* [189] = 1.78e-001 = pow(10.0, 0.05 * -15.0dB) */ + 0x3e40e2bb, /* [190] = 1.88e-001 = pow(10.0, 0.05 * -14.5dB) */ + 0x3e4c509b, /* [191] = 2.00e-001 = pow(10.0, 0.05 * -14.0dB) */ + 0x3e586bd9, /* [192] = 2.11e-001 = pow(10.0, 0.05 * -13.5dB) */ + 0x3e653ebb, /* [193] = 2.24e-001 = pow(10.0, 0.05 * -13.0dB) */ + 0x3e72d424, /* [194] = 2.37e-001 = pow(10.0, 0.05 * -12.5dB) */ + 0x3e809bcc, /* [195] = 2.51e-001 = pow(10.0, 0.05 * -12.0dB) */ + 0x3e883aa8, /* [196] = 2.66e-001 = pow(10.0, 0.05 * -11.5dB) */ + 0x3e904d1c, /* [197] = 2.82e-001 = pow(10.0, 0.05 * -11.0dB) */ + 0x3e98da02, /* [198] = 2.99e-001 = pow(10.0, 0.05 * -10.5dB) */ + 0x3ea1e89b, /* [199] = 3.16e-001 = pow(10.0, 0.05 * -10.0dB) */ + 0x3eab8097, /* [200] = 3.35e-001 = pow(10.0, 0.05 * -9.5dB) */ + 0x3eb5aa1a, /* [201] = 3.55e-001 = pow(10.0, 0.05 * -9.0dB) */ + 0x3ec06dc3, /* [202] = 3.76e-001 = pow(10.0, 0.05 * -8.5dB) */ + 0x3ecbd4b4, /* [203] = 3.98e-001 = pow(10.0, 0.05 * -8.0dB) */ + 0x3ed7e89b, /* [204] = 4.22e-001 = pow(10.0, 0.05 * -7.5dB) */ + 0x3ee4b3b6, /* [205] = 4.47e-001 = pow(10.0, 0.05 * -7.0dB) */ + 0x3ef240e2, /* [206] = 4.73e-001 = pow(10.0, 0.05 * -6.5dB) */ + 0x3f004dce, /* [207] = 5.01e-001 = pow(10.0, 0.05 * -6.0dB) */ + 0x3f07e80b, /* [208] = 5.31e-001 = pow(10.0, 0.05 * -5.5dB) */ + 0x3f0ff59a, /* [209] = 5.62e-001 = pow(10.0, 0.05 * -5.0dB) */ + 0x3f187d50, /* [210] = 5.96e-001 = pow(10.0, 0.05 * -4.5dB) */ + 0x3f21866c, /* [211] = 6.31e-001 = pow(10.0, 0.05 * -4.0dB) */ + 0x3f2b1896, /* [212] = 6.68e-001 = pow(10.0, 0.05 * -3.5dB) */ + 0x3f353bef, /* [213] = 7.08e-001 = pow(10.0, 0.05 * -3.0dB) */ + 0x3f3ff911, /* [214] = 7.50e-001 = pow(10.0, 0.05 * -2.5dB) */ + 0x3f4b5918, /* [215] = 7.94e-001 = pow(10.0, 0.05 * -2.0dB) */ + 0x3f5765ac, /* [216] = 8.41e-001 = pow(10.0, 0.05 * -1.5dB) */ + 0x3f642905, /* [217] = 8.91e-001 = pow(10.0, 0.05 * -1.0dB) */ + 0x3f71adf9, /* [218] = 9.44e-001 = pow(10.0, 0.05 * -0.5dB) */ + 0x3f800000, /* [219] = 1.00e+000 = pow(10.0, 0.05 * 0.0dB) */ + 0x3f8795a0, /* [220] = 1.06e+000 = pow(10.0, 0.05 * 0.5dB) */ + 0x3f8f9e4d, /* [221] = 1.12e+000 = pow(10.0, 0.05 * 1.0dB) */ + 0x3f9820d7, /* [222] = 1.19e+000 = pow(10.0, 0.05 * 1.5dB) */ + 0x3fa12478, /* [223] = 1.26e+000 = pow(10.0, 0.05 * 2.0dB) */ + 0x3faab0d5, /* [224] = 1.33e+000 = pow(10.0, 0.05 * 2.5dB) */ + 0x3fb4ce08, /* [225] = 1.41e+000 = pow(10.0, 0.05 * 3.0dB) */ + 0x3fbf84a6, /* [226] = 1.50e+000 = pow(10.0, 0.05 * 3.5dB) */ + 0x3fcaddc8, /* [227] = 1.58e+000 = pow(10.0, 0.05 * 4.0dB) */ + 0x3fd6e30d, /* [228] = 1.68e+000 = pow(10.0, 0.05 * 4.5dB) */ + 0x3fe39ea9, /* [229] = 1.78e+000 = pow(10.0, 0.05 * 5.0dB) */ + 0x3ff11b6a, /* [230] = 1.88e+000 = pow(10.0, 0.05 * 5.5dB) */ + 0x3fff64c1, /* [231] = 2.00e+000 = pow(10.0, 0.05 * 6.0dB) */ + 0x40074368, /* [232] = 2.11e+000 = pow(10.0, 0.05 * 6.5dB) */ + 0x400f4735, /* [233] = 2.24e+000 = pow(10.0, 0.05 * 7.0dB) */ + 0x4017c496, /* [234] = 2.37e+000 = pow(10.0, 0.05 * 7.5dB) */ + 0x4020c2bf, /* [235] = 2.51e+000 = pow(10.0, 0.05 * 8.0dB) */ + 0x402a4952, /* [236] = 2.66e+000 = pow(10.0, 0.05 * 8.5dB) */ + 0x40346063, /* [237] = 2.82e+000 = pow(10.0, 0.05 * 9.0dB) */ + 0x403f1082, /* [238] = 2.99e+000 = pow(10.0, 0.05 * 9.5dB) */ + 0x404a62c2, /* [239] = 3.16e+000 = pow(10.0, 0.05 * 10.0dB) */ + 0x405660bd, /* [240] = 3.35e+000 = pow(10.0, 0.05 * 10.5dB) */ + 0x406314a0, /* [241] = 3.55e+000 = pow(10.0, 0.05 * 11.0dB) */ + 0x40708933, /* [242] = 3.76e+000 = pow(10.0, 0.05 * 11.5dB) */ + 0x407ec9e1, /* [243] = 3.98e+000 = pow(10.0, 0.05 * 12.0dB) */ + 0x4086f161, /* [244] = 4.22e+000 = pow(10.0, 0.05 * 12.5dB) */ + 0x408ef052, /* [245] = 4.47e+000 = pow(10.0, 0.05 * 13.0dB) */ + 0x4097688d, /* [246] = 4.73e+000 = pow(10.0, 0.05 * 13.5dB) */ + 0x40a06142, /* [247] = 5.01e+000 = pow(10.0, 0.05 * 14.0dB) */ + 0x40a9e20e, /* [248] = 5.31e+000 = pow(10.0, 0.05 * 14.5dB) */ + 0x40b3f300, /* [249] = 5.62e+000 = pow(10.0, 0.05 * 15.0dB) */ + 0x40be9ca5, /* [250] = 5.96e+000 = pow(10.0, 0.05 * 15.5dB) */ + 0x40c9e807, /* [251] = 6.31e+000 = pow(10.0, 0.05 * 16.0dB) */ + 0x40d5debc, /* [252] = 6.68e+000 = pow(10.0, 0.05 * 16.5dB) */ + 0x40e28aeb, /* [253] = 7.08e+000 = pow(10.0, 0.05 * 17.0dB) */ + 0x40eff755, /* [254] = 7.50e+000 = pow(10.0, 0.05 * 17.5dB) */ + 0x40fe2f5e, /* [255] = 7.94e+000 = pow(10.0, 0.05 * 18.0dB) */ +}; + +#define MIXART_DIGITAL_LEVEL_MIN 0 /* -109.5 dB */ +#define MIXART_DIGITAL_LEVEL_MAX 255 /* 18.0 dB */ +#define MIXART_DIGITAL_ZERO_LEVEL 219 /* 0.0 dB */ + + +int mixart_update_playback_stream_level(mixart_t* chip, int is_aes, int idx) +{ + int err, i; + int volume[2]; + mixart_msg_t request; + mixart_set_out_stream_level_req_t set_level; + u32 status; + mixart_pipe_t *pipe; + + memset(&set_level, 0, sizeof(set_level)); + set_level.nb_of_stream = 1; + set_level.stream_level.desc.stream_idx = idx; + + if(is_aes) { + pipe = &chip->pipe_out_dig; /* AES playback */ + idx += MIXART_PLAYBACK_STREAMS; + } else { + pipe = &chip->pipe_out_ana; /* analog playback */ + } + + /* only when pipe exists ! */ + if(pipe->status == PIPE_UNDEFINED) + return 0; + + set_level.stream_level.desc.uid_pipe = pipe->group_uid; + + for(i=0; i<2; i++) { + if(chip->digital_playback_active[idx][i]) + volume[i] = chip->digital_playback_volume[idx][i]; + else + volume[i] = MIXART_DIGITAL_LEVEL_MIN; + } + + set_level.stream_level.out_level.valid_mask1 = MIXART_OUT_STREAM_SET_LEVEL_LEFT_AUDIO1 | MIXART_OUT_STREAM_SET_LEVEL_RIGHT_AUDIO2; + set_level.stream_level.out_level.left_to_out1_level = mixart_digital_level[volume[0]]; + set_level.stream_level.out_level.right_to_out2_level = mixart_digital_level[volume[1]]; + + request.message_id = MSG_STREAM_SET_OUT_STREAM_LEVEL; + request.uid = (mixart_uid_t){0,0}; + request.data = &set_level; + request.size = sizeof(set_level); + + err = snd_mixart_send_msg(chip->mgr, &request, sizeof(status), &status); + if((err<0) || status) { + snd_printk(KERN_DEBUG "error MSG_STREAM_SET_OUT_STREAM_LEVEL card(%d) status(%x)\n", chip->chip_idx, status); + return -EINVAL; + } + return 0; +} + +int mixart_update_capture_stream_level(mixart_t* chip, int is_aes) +{ + int err, i, idx; + mixart_pipe_t* pipe; + mixart_msg_t request; + mixart_set_in_audio_level_req_t set_level; + u32 status; + + if(is_aes) { + idx = 1; + pipe = &chip->pipe_in_dig; + } else { + idx = 0; + pipe = &chip->pipe_in_ana; + } + + /* only when pipe exists ! */ + if(pipe->status == PIPE_UNDEFINED) + return 0; + + memset(&set_level, 0, sizeof(set_level)); + set_level.audio_count = 2; + set_level.level[0].connector = pipe->uid_left_connector; + set_level.level[1].connector = pipe->uid_right_connector; + + for(i=0; i<2; i++) { + set_level.level[i].valid_mask1 = MIXART_AUDIO_LEVEL_DIGITAL_MASK; + set_level.level[i].digital_level = mixart_digital_level[chip->digital_capture_volume[idx][i]]; + } + + request.message_id = MSG_STREAM_SET_IN_AUDIO_LEVEL; + request.uid = (mixart_uid_t){0,0}; + request.data = &set_level; + request.size = sizeof(set_level); + + err = snd_mixart_send_msg(chip->mgr, &request, sizeof(status), &status); + if((err<0) || status) { + snd_printk(KERN_DEBUG "error MSG_STREAM_SET_IN_AUDIO_LEVEL card(%d) status(%x)\n", chip->chip_idx, status); + return -EINVAL; + } + return 0; +} + + +/* shared */ +static int mixart_digital_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = MIXART_DIGITAL_LEVEL_MIN; /* -109.5 dB */ + uinfo->value.integer.max = MIXART_DIGITAL_LEVEL_MAX; /* 18.0 dB */ + return 0; +} + +#define MIXART_VOL_REC_MASK 1 +#define MIXART_VOL_AES_MASK 2 + +static int mixart_pcm_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + mixart_t *chip = snd_kcontrol_chip(kcontrol); + int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */ + int *stored_volume; + int is_capture = kcontrol->private_value & MIXART_VOL_REC_MASK; + int is_aes = kcontrol->private_value & MIXART_VOL_AES_MASK; + down(&chip->mgr->mixer_mutex); + if(is_capture) { + if(is_aes) stored_volume = chip->digital_capture_volume[1]; /* AES capture */ + else stored_volume = chip->digital_capture_volume[0]; /* analog capture */ + } else { + snd_assert ( idx < MIXART_PLAYBACK_STREAMS ); + if(is_aes) stored_volume = chip->digital_playback_volume[MIXART_PLAYBACK_STREAMS + idx]; /* AES playback */ + else stored_volume = chip->digital_playback_volume[idx]; /* analog playback */ + } + ucontrol->value.integer.value[0] = stored_volume[0]; + ucontrol->value.integer.value[1] = stored_volume[1]; + up(&chip->mgr->mixer_mutex); + return 0; +} + +static int mixart_pcm_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + mixart_t *chip = snd_kcontrol_chip(kcontrol); + int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */ + int changed = 0; + int is_capture = kcontrol->private_value & MIXART_VOL_REC_MASK; + int is_aes = kcontrol->private_value & MIXART_VOL_AES_MASK; + int* stored_volume; + int i; + down(&chip->mgr->mixer_mutex); + if(is_capture) { + if(is_aes) stored_volume = chip->digital_capture_volume[1]; /* AES capture */ + else stored_volume = chip->digital_capture_volume[0]; /* analog capture */ + } else { + snd_assert ( idx < MIXART_PLAYBACK_STREAMS ); + if(is_aes) stored_volume = chip->digital_playback_volume[MIXART_PLAYBACK_STREAMS + idx]; /* AES playback */ + else stored_volume = chip->digital_playback_volume[idx]; /* analog playback */ + } + for(i=0; i<2; i++) { + if(stored_volume[i] != ucontrol->value.integer.value[i]) { + stored_volume[i] = ucontrol->value.integer.value[i]; + changed = 1; + } + } + if(changed) { + if(is_capture) mixart_update_capture_stream_level(chip, is_aes); + else mixart_update_playback_stream_level(chip, is_aes, idx); + } + up(&chip->mgr->mixer_mutex); + return changed; +} + +static snd_kcontrol_new_t snd_mixart_pcm_vol = +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* name will be filled later */ + /* count will be filled later */ + .info = mixart_digital_vol_info, /* shared */ + .get = mixart_pcm_vol_get, + .put = mixart_pcm_vol_put, +}; + + +static int mixart_pcm_sw_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + mixart_t *chip = snd_kcontrol_chip(kcontrol); + int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */ + snd_assert ( idx < MIXART_PLAYBACK_STREAMS ); + down(&chip->mgr->mixer_mutex); + if(kcontrol->private_value & MIXART_VOL_AES_MASK) /* AES playback */ + idx += MIXART_PLAYBACK_STREAMS; + ucontrol->value.integer.value[0] = chip->digital_playback_active[idx][0]; + ucontrol->value.integer.value[1] = chip->digital_playback_active[idx][1]; + up(&chip->mgr->mixer_mutex); + return 0; +} + +static int mixart_pcm_sw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + mixart_t *chip = snd_kcontrol_chip(kcontrol); + int changed = 0; + int is_aes = kcontrol->private_value & MIXART_VOL_AES_MASK; + int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */ + int i, j; + snd_assert ( idx < MIXART_PLAYBACK_STREAMS ); + down(&chip->mgr->mixer_mutex); + j = idx; + if(is_aes) j += MIXART_PLAYBACK_STREAMS; + for(i=0; i<2; i++) { + if(chip->digital_playback_active[j][i] != ucontrol->value.integer.value[i]) { + chip->digital_playback_active[j][i] = ucontrol->value.integer.value[i]; + changed = 1; + } + } + if(changed) mixart_update_playback_stream_level(chip, is_aes, idx); + up(&chip->mgr->mixer_mutex); + return changed; +} + +static snd_kcontrol_new_t mixart_control_pcm_switch = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* name will be filled later */ + .count = MIXART_PLAYBACK_STREAMS, + .info = mixart_sw_info, /* shared */ + .get = mixart_pcm_sw_get, + .put = mixart_pcm_sw_put +}; + +static int mixart_update_monitoring(mixart_t* chip, int channel) +{ + int err; + mixart_msg_t request; + mixart_set_out_audio_level_t audio_level; + u32 resp; + + if(chip->pipe_out_ana.status == PIPE_UNDEFINED) + return -EINVAL; /* no pipe defined */ + + if(!channel) request.uid = chip->pipe_out_ana.uid_left_connector; + else request.uid = chip->pipe_out_ana.uid_right_connector; + request.message_id = MSG_CONNECTOR_SET_OUT_AUDIO_LEVEL; + request.data = &audio_level; + request.size = sizeof(audio_level); + + memset(&audio_level, 0, sizeof(audio_level)); + audio_level.valid_mask1 = MIXART_AUDIO_LEVEL_MONITOR_MASK | MIXART_AUDIO_LEVEL_MUTE_M1_MASK; + audio_level.monitor_level = mixart_digital_level[chip->monitoring_volume[channel!=0]]; + audio_level.monitor_mute1 = !chip->monitoring_active[channel!=0]; + + err = snd_mixart_send_msg(chip->mgr, &request, sizeof(resp), &resp); + if((err<0) || resp) { + snd_printk(KERN_DEBUG "error MSG_CONNECTOR_SET_OUT_AUDIO_LEVEL card(%d) resp(%x)\n", chip->chip_idx, resp); + return -EINVAL; + } + return 0; +} + +/* + * monitoring level control + */ + +static int mixart_monitor_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + mixart_t *chip = snd_kcontrol_chip(kcontrol); + down(&chip->mgr->mixer_mutex); + ucontrol->value.integer.value[0] = chip->monitoring_volume[0]; + ucontrol->value.integer.value[1] = chip->monitoring_volume[1]; + up(&chip->mgr->mixer_mutex); + return 0; +} + +static int mixart_monitor_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + mixart_t *chip = snd_kcontrol_chip(kcontrol); + int changed = 0; + int i; + down(&chip->mgr->mixer_mutex); + for(i=0; i<2; i++) { + if(chip->monitoring_volume[i] != ucontrol->value.integer.value[i]) { + chip->monitoring_volume[i] = ucontrol->value.integer.value[i]; + mixart_update_monitoring(chip, i); + changed = 1; + } + } + up(&chip->mgr->mixer_mutex); + return changed; +} + +static snd_kcontrol_new_t mixart_control_monitor_vol = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Monitoring Volume", + .info = mixart_digital_vol_info, /* shared */ + .get = mixart_monitor_vol_get, + .put = mixart_monitor_vol_put, +}; + +/* + * monitoring switch control + */ + +static int mixart_monitor_sw_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + mixart_t *chip = snd_kcontrol_chip(kcontrol); + down(&chip->mgr->mixer_mutex); + ucontrol->value.integer.value[0] = chip->monitoring_active[0]; + ucontrol->value.integer.value[1] = chip->monitoring_active[1]; + up(&chip->mgr->mixer_mutex); + return 0; +} + +static int mixart_monitor_sw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + mixart_t *chip = snd_kcontrol_chip(kcontrol); + int changed = 0; + int i; + down(&chip->mgr->mixer_mutex); + for(i=0; i<2; i++) { + if(chip->monitoring_active[i] != ucontrol->value.integer.value[i]) { + chip->monitoring_active[i] = ucontrol->value.integer.value[i]; + changed |= (1<monitoring_active[0] || chip->monitoring_active[1]; + if(allocate) { + snd_mixart_add_ref_pipe( chip, MIXART_PCM_ANALOG, 0, 1); /* allocate the playback pipe for monitoring */ + snd_mixart_add_ref_pipe( chip, MIXART_PCM_ANALOG, 1, 1); /* allocate the capture pipe for monitoring */ + } + if(changed & 0x01) mixart_update_monitoring(chip, 0); + if(changed & 0x02) mixart_update_monitoring(chip, 1); + if(!allocate) { + snd_mixart_kill_ref_pipe( chip->mgr, &chip->pipe_in_ana, 1); /* release the capture pipe for monitoring */ + snd_mixart_kill_ref_pipe( chip->mgr, &chip->pipe_out_ana, 1); /* release the playback pipe for monitoring */ + } + } + + up(&chip->mgr->mixer_mutex); + return (changed != 0); +} + +static snd_kcontrol_new_t mixart_control_monitor_sw = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Monitoring Switch", + .info = mixart_sw_info, /* shared */ + .get = mixart_monitor_sw_get, + .put = mixart_monitor_sw_put +}; + + +static void mixart_reset_audio_levels(mixart_t *chip) +{ + /* analog volumes can be set even if there is no pipe */ + mixart_update_analog_audio_level(chip, 0); + /* analog levels for capture only on the first two chips */ + if(chip->chip_idx < 2) { + mixart_update_analog_audio_level(chip, 1); + } + return; +} + + +int snd_mixart_create_mixer(mixart_mgr_t *mgr) +{ + mixart_t *chip; + int err, i; + + init_MUTEX(&mgr->mixer_mutex); /* can be in another place */ + + for(i=0; inum_cards; i++) { + snd_kcontrol_new_t temp; + chip = mgr->chip[i]; + + /* analog output level control */ + temp = mixart_control_analog_level; + temp.name = "Master Playback Volume"; + temp.private_value = 0; /* playback */ + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0) + return err; + /* output mute controls */ + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&mixart_control_output_switch, chip))) < 0) + return err; + + /* analog input level control only on first two chips !*/ + if(i<2) { + temp = mixart_control_analog_level; + temp.name = "Master Capture Volume"; + temp.private_value = 1; /* capture */ + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0) + return err; + } + + temp = snd_mixart_pcm_vol; + temp.name = "PCM Playback Volume"; + temp.count = MIXART_PLAYBACK_STREAMS; + temp.private_value = 0; /* playback analog */ + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0) + return err; + + temp.name = "PCM Capture Volume"; + temp.count = 1; + temp.private_value = MIXART_VOL_REC_MASK; /* capture analog */ + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0) + return err; + + if(mgr->board_type == MIXART_DAUGHTER_TYPE_AES) { + temp.name = "AES Playback Volume"; + temp.count = MIXART_PLAYBACK_STREAMS; + temp.private_value = MIXART_VOL_AES_MASK; /* playback AES/EBU */ + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0) + return err; + + temp.name = "AES Capture Volume"; + temp.count = 0; + temp.private_value = MIXART_VOL_REC_MASK | MIXART_VOL_AES_MASK; /* capture AES/EBU */ + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0) + return err; + } + temp = mixart_control_pcm_switch; + temp.name = "PCM Playback Switch"; + temp.private_value = 0; /* playback analog */ + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0) + return err; + + if(mgr->board_type == MIXART_DAUGHTER_TYPE_AES) { + temp.name = "AES Playback Switch"; + temp.private_value = MIXART_VOL_AES_MASK; /* playback AES/EBU */ + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0) + return err; + } + + /* monitoring */ + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&mixart_control_monitor_vol, chip))) < 0) + return err; + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&mixart_control_monitor_sw, chip))) < 0) + return err; + + /* init all mixer data and program the master volumes/switches */ + mixart_reset_audio_levels(chip); + } + return 0; +} --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/sound/pci/mixart/mixart_mixer.h 2004-03-07 20:46:50.000000000 -0800 @@ -0,0 +1,31 @@ +/* + * Driver for Digigram miXart soundcards + * + * include file for mixer + * + * Copyright (c) 2003 by Digigram + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __SOUND_MIXART_MIXER_H +#define __SOUND_MIXART_MIXER_H + +/* exported */ +int mixart_update_playback_stream_level(mixart_t* chip, int is_aes, int idx); +int mixart_update_capture_stream_level(mixart_t* chip, int is_aes); +int snd_mixart_create_mixer(mixart_mgr_t* mgr); + +#endif /* __SOUND_MIXART_MIXER_H */ --- linux-2.6.4-rc2/sound/pci/rme32.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/rme32.c 2004-03-07 20:46:50.000000000 -0800 @@ -1378,9 +1378,10 @@ static int __devinit snd_rme32_create(rm rme32->spdif_pcm->info_flags = 0; snd_pcm_lib_preallocate_pages_for_all(rme32->spdif_pcm, + SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), RME32_BUFFER_SIZE, - RME32_BUFFER_SIZE, - GFP_KERNEL); + RME32_BUFFER_SIZE); /* set up ALSA pcm device for ADAT */ if ((pci->device == PCI_DEVICE_ID_DIGI32) || @@ -1405,9 +1406,10 @@ static int __devinit snd_rme32_create(rm rme32->adat_pcm->info_flags = 0; snd_pcm_lib_preallocate_pages_for_all(rme32->adat_pcm, + SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), RME32_BUFFER_SIZE, - RME32_BUFFER_SIZE, - GFP_KERNEL); + RME32_BUFFER_SIZE); } --- linux-2.6.4-rc2/sound/pci/rme9652/hdsp.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/rme9652/hdsp.c 2004-03-07 20:46:50.000000000 -0800 @@ -568,7 +568,10 @@ static void *snd_hammerfall_get_buffer(s struct snd_dma_device pdev; struct snd_dma_buffer dmbuf; - snd_dma_device_pci(&pdev, pci, capture); + memset(&pdev, 0, sizeof(pdev)); + pdev.type = SNDRV_DMA_TYPE_DEV; + pdev.dev = snd_dma_pci_data(pci); + pdev.id = capture; dmbuf.bytes = 0; if (! snd_dma_get_reserved(&pdev, &dmbuf)) { if (snd_dma_alloc_pages(&pdev, size, &dmbuf) < 0) @@ -581,9 +584,13 @@ static void *snd_hammerfall_get_buffer(s static void snd_hammerfall_free_buffer(struct pci_dev *pci, size_t size, void *ptr, dma_addr_t addr, int capture) { - struct snd_dma_device dev; - snd_dma_device_pci(&dev, pci, capture); - snd_dma_free_reserved(&dev); + struct snd_dma_device pdev; + + memset(&pdev, 0, sizeof(pdev)); + pdev.type = SNDRV_DMA_TYPE_DEV; + pdev.dev = snd_dma_pci_data(pci); + pdev.id = capture; + snd_dma_free_reserved(&pdev); } #else @@ -3810,7 +3817,7 @@ static char *hdsp_channel_buffer_locatio { int mapped_channel; - snd_assert(channel >= 0 || channel < hdsp->max_channels, return NULL); + snd_assert(channel >= 0 && channel < hdsp->max_channels, return NULL); if ((mapped_channel = hdsp->channel_map[channel]) < 0) { return NULL; @@ -3833,7 +3840,8 @@ static int snd_hdsp_playback_copy(snd_pc channel_buf = hdsp_channel_buffer_location (hdsp, substream->pstr->stream, channel); snd_assert(channel_buf != NULL, return -EIO); - copy_from_user(channel_buf + pos * 4, src, count * 4); + if (copy_from_user(channel_buf + pos * 4, src, count * 4)) + return -EFAULT; return count; } @@ -3847,7 +3855,8 @@ static int snd_hdsp_capture_copy(snd_pcm channel_buf = hdsp_channel_buffer_location (hdsp, substream->pstr->stream, channel); snd_assert(channel_buf != NULL, return -EIO); - copy_to_user(dst, channel_buf + pos * 4, count * 4); + if (copy_to_user(dst, channel_buf + pos * 4, count * 4)) + return -EFAULT; return count; } --- linux-2.6.4-rc2/sound/pci/rme9652/rme9652.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/rme9652/rme9652.c 2004-03-07 20:46:51.000000000 -0800 @@ -314,7 +314,10 @@ static void *snd_hammerfall_get_buffer(s struct snd_dma_device pdev; struct snd_dma_buffer dmbuf; - snd_dma_device_pci(&pdev, pci, capture); + memset(&pdev, 0, sizeof(pdev)); + pdev.type = SNDRV_DMA_TYPE_DEV; + pdev.dev = snd_dma_pci_data(pci); + pdev.id = capture; dmbuf.bytes = 0; if (! snd_dma_get_reserved(&pdev, &dmbuf)) { if (snd_dma_alloc_pages(&pdev, size, &dmbuf) < 0) @@ -327,9 +330,13 @@ static void *snd_hammerfall_get_buffer(s static void snd_hammerfall_free_buffer(struct pci_dev *pci, size_t size, void *ptr, dma_addr_t addr, int capture) { - struct snd_dma_device dev; - snd_dma_device_pci(&dev, pci, capture); - snd_dma_free_reserved(&dev); + struct snd_dma_device pdev; + + memset(&pdev, 0, sizeof(pdev)); + pdev.type = SNDRV_DMA_TYPE_DEV; + pdev.dev = snd_dma_pci_data(pci); + pdev.id = capture; + snd_dma_free_reserved(&pdev); } #else --- linux-2.6.4-rc2/sound/pci/rme96.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/rme96.c 2004-03-07 20:46:50.000000000 -0800 @@ -1718,7 +1718,11 @@ snd_rme96_create(rme96_t *rme96) rme96->spdif_pcm->info_flags = 0; - snd_pcm_lib_preallocate_pages_for_all(rme96->spdif_pcm, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE, GFP_KERNEL); + snd_pcm_lib_preallocate_pages_for_all(rme96->spdif_pcm, + SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), + RME96_BUFFER_SIZE, + RME96_BUFFER_SIZE); /* set up ALSA pcm device for ADAT */ if (pci->device == PCI_DEVICE_ID_DIGI96) { @@ -1738,7 +1742,11 @@ snd_rme96_create(rme96_t *rme96) rme96->adat_pcm->info_flags = 0; - snd_pcm_lib_preallocate_pages_for_all(rme96->adat_pcm, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE, GFP_KERNEL); + snd_pcm_lib_preallocate_pages_for_all(rme96->adat_pcm, + SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), + RME96_BUFFER_SIZE, + RME96_BUFFER_SIZE); } rme96->playback_periodsize = 0; --- linux-2.6.4-rc2/sound/pci/sonicvibes.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/sonicvibes.c 2004-03-07 20:46:51.000000000 -0800 @@ -886,7 +886,8 @@ static int __devinit snd_sonicvibes_pcm( strcpy(pcm->name, "S3 SonicVibes"); sonic->pcm = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(sonic->pci, pcm, 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(sonic->pci), 64*1024, 128*1024); if (rpcm) *rpcm = pcm; --- linux-2.6.4-rc2/sound/pci/trident/trident_main.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/trident/trident_main.c 2004-03-07 20:46:51.000000000 -0800 @@ -1009,8 +1009,6 @@ static int snd_trident_capture_prepare(s snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; unsigned int val, ESO_bytes; - snd_assert(substream->dma_device.type == SNDRV_DMA_TYPE_PCI, return -EIO); - spin_lock(&trident->reg_lock); // Initilize the channel and set channel Mode @@ -2189,10 +2187,15 @@ int __devinit snd_trident_pcm(trident_t if (trident->tlb.entries) { snd_pcm_substream_t *substream; for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) - snd_pcm_lib_preallocate_sg_pages(trident->pci, substream, 64*1024, 128*1024); - snd_pcm_lib_preallocate_pci_pages(trident->pci, pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG, + snd_dma_pci_data(trident->pci), + 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, + SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(trident->pci), + 64*1024, 128*1024); } else { - snd_pcm_lib_preallocate_pci_pages_for_all(trident->pci, pcm, 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(trident->pci), 64*1024, 128*1024); } if (rpcm) @@ -2246,9 +2249,11 @@ int __devinit snd_trident_foldback_pcm(t trident->foldback = foldback; if (trident->tlb.entries) - snd_pcm_lib_preallocate_sg_pages_for_all(trident->pci, foldback, 0, 128*1024); + snd_pcm_lib_preallocate_pages_for_all(foldback, SNDRV_DMA_TYPE_DEV_SG, + snd_dma_pci_data(trident->pci), 0, 128*1024); else - snd_pcm_lib_preallocate_pci_pages_for_all(trident->pci, foldback, 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages_for_all(foldback, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(trident->pci), 64*1024, 128*1024); if (rpcm) *rpcm = foldback; @@ -2287,7 +2292,7 @@ int __devinit snd_trident_spdif_pcm(trid strcpy(spdif->name, "Trident 4DWave IEC958"); trident->spdif = spdif; - snd_pcm_lib_preallocate_pci_pages_for_all(trident->pci, spdif, 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages_for_all(spdif, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(trident->pci), 64*1024, 128*1024); if (rpcm) *rpcm = spdif; @@ -3052,29 +3057,49 @@ static int __devinit snd_trident_mixer(t } if (trident->device == TRIDENT_DEVICE_ID_NX || trident->device == TRIDENT_DEVICE_ID_SI7018) { - if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_spdif_control, trident))) < 0) + kctl = snd_ctl_new1(&snd_trident_spdif_control, trident); + if (kctl == NULL) { + err = -ENOMEM; goto __out; + } if (trident->ac97->ext_id & AC97_EI_SPDIF) kctl->id.index++; if (trident->ac97_sec && (trident->ac97_sec->ext_id & AC97_EI_SPDIF)) kctl->id.index++; idx = kctl->id.index; + if ((err = snd_ctl_add(card, kctl)) < 0) + goto __out; kctl->put(kctl, uctl); - if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_spdif_default, trident))) < 0) + kctl = snd_ctl_new1(&snd_trident_spdif_default, trident); + if (kctl == NULL) { + err = -ENOMEM; goto __out; + } kctl->id.index = idx; kctl->id.device = pcm_spdif_device; + if ((err = snd_ctl_add(card, kctl)) < 0) + goto __out; - if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_spdif_mask, trident))) < 0) + kctl = snd_ctl_new1(&snd_trident_spdif_mask, trident); + if (kctl == NULL) { + err = -ENOMEM; goto __out; + } kctl->id.index = idx; kctl->id.device = pcm_spdif_device; + if ((err = snd_ctl_add(card, kctl)) < 0) + goto __out; - if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_spdif_stream, trident))) < 0) + kctl = snd_ctl_new1(&snd_trident_spdif_stream, trident); + if (kctl == NULL) { + err = -ENOMEM; goto __out; + } kctl->id.index = idx; kctl->id.device = pcm_spdif_device; + if ((err = snd_ctl_add(card, kctl)) < 0) + goto __out; trident->spdif_pcm_ctl = kctl; } @@ -3328,13 +3353,12 @@ static int __devinit snd_trident_tlb_all /* TLB array must be aligned to 16kB !!! so we allocate 32kB region and correct offset when necessary */ - trident->tlb.buffer = snd_malloc_pci_pages(trident->pci, 2 * SNDRV_TRIDENT_MAX_PAGES * 4, &trident->tlb.buffer_dmaaddr); - if (trident->tlb.buffer == NULL) { + if (snd_dma_alloc_pages(&trident->dma_dev, 2 * SNDRV_TRIDENT_MAX_PAGES * 4, &trident->tlb.buffer) < 0) { snd_printk(KERN_ERR "trident: unable to allocate TLB buffer\n"); return -ENOMEM; } - trident->tlb.entries = (unsigned int*)(((unsigned long)trident->tlb.buffer + SNDRV_TRIDENT_MAX_PAGES * 4 - 1) & ~(SNDRV_TRIDENT_MAX_PAGES * 4 - 1)); - trident->tlb.entries_dmaaddr = (trident->tlb.buffer_dmaaddr + SNDRV_TRIDENT_MAX_PAGES * 4 - 1) & ~(SNDRV_TRIDENT_MAX_PAGES * 4 - 1); + trident->tlb.entries = (unsigned int*)(((unsigned long)trident->tlb.buffer.area + SNDRV_TRIDENT_MAX_PAGES * 4 - 1) & ~(SNDRV_TRIDENT_MAX_PAGES * 4 - 1)); + trident->tlb.entries_dmaaddr = (trident->tlb.buffer.addr + SNDRV_TRIDENT_MAX_PAGES * 4 - 1) & ~(SNDRV_TRIDENT_MAX_PAGES * 4 - 1); /* allocate shadow TLB page table (virtual addresses) */ trident->tlb.shadow_entries = (unsigned long *)vmalloc(SNDRV_TRIDENT_MAX_PAGES*sizeof(unsigned long)); if (trident->tlb.shadow_entries == NULL) { @@ -3342,15 +3366,14 @@ static int __devinit snd_trident_tlb_all return -ENOMEM; } /* allocate and setup silent page and initialise TLB entries */ - trident->tlb.silent_page = snd_malloc_pci_pages(trident->pci, SNDRV_TRIDENT_PAGE_SIZE, &trident->tlb.silent_page_dmaaddr); - if (trident->tlb.silent_page == 0UL) { + if (snd_dma_alloc_pages(&trident->dma_dev, SNDRV_TRIDENT_PAGE_SIZE, &trident->tlb.silent_page) < 0) { snd_printk(KERN_ERR "trident: unable to allocate silent page\n"); return -ENOMEM; } - memset(trident->tlb.silent_page, 0, SNDRV_TRIDENT_PAGE_SIZE); + memset(trident->tlb.silent_page.area, 0, SNDRV_TRIDENT_PAGE_SIZE); for (i = 0; i < SNDRV_TRIDENT_MAX_PAGES; i++) { - trident->tlb.entries[i] = cpu_to_le32(trident->tlb.silent_page_dmaaddr & ~(SNDRV_TRIDENT_PAGE_SIZE-1)); - trident->tlb.shadow_entries[i] = (unsigned long)trident->tlb.silent_page; + trident->tlb.entries[i] = cpu_to_le32(trident->tlb.silent_page.addr & ~(SNDRV_TRIDENT_PAGE_SIZE-1)); + trident->tlb.shadow_entries[i] = (unsigned long)trident->tlb.silent_page.area; } /* use emu memory block manager code to manage tlb page allocation */ @@ -3565,9 +3588,13 @@ int __devinit snd_trident_create(snd_car } trident->irq = pci->irq; + memset(&trident->dma_dev, 0, sizeof(trident->dma_dev)); + trident->dma_dev.type = SNDRV_DMA_TYPE_DEV; + trident->dma_dev.dev = snd_dma_pci_data(pci); + /* allocate 16k-aligned TLB for NX cards */ trident->tlb.entries = NULL; - trident->tlb.buffer = NULL; + trident->tlb.buffer.area = NULL; if (trident->device == TRIDENT_DEVICE_ID_NX) { if ((err = snd_trident_tlb_alloc(trident)) < 0) { snd_trident_free(trident); @@ -3661,15 +3688,15 @@ int snd_trident_free(trident_t *trident) else if (trident->device == TRIDENT_DEVICE_ID_SI7018) { outl(0, TRID_REG(trident, SI_SERIAL_INTF_CTRL)); } - if (trident->tlb.buffer) { + if (trident->tlb.buffer.area) { outl(0, TRID_REG(trident, NX_TLBC)); if (trident->tlb.memhdr) snd_util_memhdr_free(trident->tlb.memhdr); - if (trident->tlb.silent_page) - snd_free_pci_pages(trident->pci, SNDRV_TRIDENT_PAGE_SIZE, trident->tlb.silent_page, trident->tlb.silent_page_dmaaddr); + if (trident->tlb.silent_page.area) + snd_dma_free_pages(&trident->dma_dev, &trident->tlb.silent_page); if (trident->tlb.shadow_entries) vfree(trident->tlb.shadow_entries); - snd_free_pci_pages(trident->pci, 2 * SNDRV_TRIDENT_MAX_PAGES * 4, trident->tlb.buffer, trident->tlb.buffer_dmaaddr); + snd_dma_free_pages(&trident->dma_dev, &trident->tlb.buffer); } if (trident->irq >= 0) free_irq(trident->irq, (void *)trident); --- linux-2.6.4-rc2/sound/pci/trident/trident_memory.c 2003-06-14 12:18:25.000000000 -0700 +++ 25/sound/pci/trident/trident_memory.c 2004-03-07 20:46:51.000000000 -0800 @@ -47,7 +47,7 @@ /* fill TLB entrie(s) corresponding to page with ptr */ #define set_tlb_bus(trident,page,ptr,addr) __set_tlb_bus(trident,page,ptr,addr) /* fill TLB entrie(s) corresponding to page with silence pointer */ -#define set_silent_tlb(trident,page) __set_tlb_bus(trident, page, (unsigned long)trident->tlb.silent_page, trident->tlb.silent_page_dmaaddr) +#define set_silent_tlb(trident,page) __set_tlb_bus(trident, page, (unsigned long)trident->tlb.silent_page.area, trident->tlb.silent_page.addr) /* get aligned page from offset address */ #define get_aligned_page(offset) ((offset) >> 12) /* get offset address from aligned page */ @@ -191,7 +191,6 @@ snd_trident_alloc_sg_pages(trident_t *tr int idx, page; struct snd_sg_buf *sgbuf = runtime->dma_private; - snd_assert(substream->dma_device.type == SNDRV_DMA_TYPE_PCI_SG, return NULL); snd_assert(runtime->dma_bytes > 0 && runtime->dma_bytes <= SNDRV_TRIDENT_MAX_PAGES * SNDRV_TRIDENT_PAGE_SIZE, return NULL); hdr = trident->tlb.memhdr; snd_assert(hdr != NULL, return NULL); @@ -240,7 +239,6 @@ snd_trident_alloc_cont_pages(trident_t * dma_addr_t addr; unsigned long ptr; - snd_assert(substream->dma_device.type == SNDRV_DMA_TYPE_PCI, return NULL); snd_assert(runtime->dma_bytes> 0 && runtime->dma_bytes <= SNDRV_TRIDENT_MAX_PAGES * SNDRV_TRIDENT_PAGE_SIZE, return NULL); hdr = trident->tlb.memhdr; snd_assert(hdr != NULL, return NULL); @@ -276,7 +274,7 @@ snd_trident_alloc_pages(trident_t *tride { snd_assert(trident != NULL, return NULL); snd_assert(substream != NULL, return NULL); - if (substream->dma_device.type == SNDRV_DMA_TYPE_PCI_SG) + if (substream->dma_device.type == SNDRV_DMA_TYPE_DEV_SG) return snd_trident_alloc_sg_pages(trident, substream); else return snd_trident_alloc_cont_pages(trident, substream); @@ -367,8 +365,13 @@ static void clear_tlb(trident_t *trident void *ptr = page_to_ptr(trident, page); dma_addr_t addr = page_to_addr(trident, page); set_silent_tlb(trident, page); - if (ptr) - snd_free_pci_pages(trident->pci, ALIGN_PAGE_SIZE, ptr, addr); + if (ptr) { + struct snd_dma_buffer dmab; + dmab.area = ptr; + dmab.addr = addr; + dmab.bytes = ALIGN_PAGE_SIZE; + snd_dma_free_pages(&trident->dma_dev, &dmab); + } } /* check new allocation range */ @@ -399,8 +402,7 @@ static void get_single_page_range(snd_ut static int synth_alloc_pages(trident_t *hw, snd_util_memblk_t *blk) { int page, first_page, last_page; - void *ptr; - dma_addr_t addr; + struct snd_dma_buffer dmab; firstpg(blk) = get_aligned_page(blk->offset); lastpg(blk) = get_aligned_page(blk->offset + blk->size - 1); @@ -410,14 +412,13 @@ static int synth_alloc_pages(trident_t * * fortunately Trident page size and kernel PAGE_SIZE is identical! */ for (page = first_page; page <= last_page; page++) { - ptr = snd_malloc_pci_pages(hw->pci, ALIGN_PAGE_SIZE, &addr); - if (ptr == NULL) + if (snd_dma_alloc_pages(&hw->dma_dev, ALIGN_PAGE_SIZE, &dmab) < 0) goto __fail; - if (! is_valid_page(addr)) { - snd_free_pci_pages(hw->pci, ALIGN_PAGE_SIZE, ptr, addr); + if (! is_valid_page(dmab.addr)) { + snd_dma_free_pages(&hw->dma_dev, &dmab); goto __fail; } - set_tlb_bus(hw, page, (unsigned long)ptr, addr); + set_tlb_bus(hw, page, (unsigned long)dmab.area, dmab.addr); } return 0; --- linux-2.6.4-rc2/sound/pci/trident/trident_synth.c 2003-06-14 12:18:08.000000000 -0700 +++ 25/sound/pci/trident/trident_synth.c 2004-03-07 20:46:51.000000000 -0800 @@ -507,7 +507,6 @@ static int snd_trident_simple_put_sample char *data, long len, int atomic) { trident_t *trident = snd_magic_cast(trident_t, private_data, return -ENXIO); - unsigned char *block = NULL; int size = instr->size; int shift = 0; @@ -530,7 +529,7 @@ static int snd_trident_simple_put_sample if (trident->tlb.entries) { snd_util_memblk_t *memblk; - memblk = snd_trident_synth_alloc(trident,size); + memblk = snd_trident_synth_alloc(trident, size); if (memblk == NULL) return -ENOMEM; if (snd_trident_synth_copy_from_user(trident, memblk, 0, data, size) ) { @@ -540,17 +539,17 @@ static int snd_trident_simple_put_sample instr->address.ptr = (unsigned char*)memblk; instr->address.memory = memblk->offset; } else { - dma_addr_t addr; - block = (unsigned char *) snd_malloc_pci_pages(trident->pci, size, &addr); - if (block == NULL) + struct snd_dma_buffer dmab; + + if (snd_dma_alloc_pages(&trident->dma_dev, size, &dmab) < 0) return -ENOMEM; - if (copy_from_user(block, data, size)) { - snd_free_pci_pages(trident->pci, size, block, addr); + if (copy_from_user(dmab.area, data, size)) { + snd_dma_free_pages(&trident->dma_dev, &dmab); return -EFAULT; } - instr->address.ptr = block; - instr->address.memory = addr; + instr->address.ptr = dmab.area; + instr->address.memory = dmab.addr; } trident->synth.current_size += size; --- linux-2.6.4-rc2/sound/pci/via82xx.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/via82xx.c 2004-03-07 20:46:51.000000000 -0800 @@ -337,8 +337,7 @@ struct via_dev { snd_pcm_substream_t *substream; int running; unsigned int tbl_entries; /* # descriptors */ - u32 *table; /* physical address + flag */ - dma_addr_t table_addr; + struct snd_dma_buffer table; struct snd_via_sg_table *idx_table; /* for recovery from the unexpected pointer */ unsigned int lastpos; @@ -347,6 +346,73 @@ struct via_dev { }; +enum { TYPE_CARD_VIA686 = 1, TYPE_CARD_VIA8233 }; +enum { TYPE_VIA686, TYPE_VIA8233, TYPE_VIA8233A }; + +#define VIA_MAX_DEVS 7 /* 4 playback, 1 multi, 2 capture */ + +struct via_rate_lock { + spinlock_t lock; + int rate; + int used; +}; + +struct _snd_via82xx { + int irq; + + unsigned long port; + struct resource *res_port; + struct resource *mpu_res; + int chip_type; + unsigned char revision; + + unsigned char old_legacy; + unsigned char old_legacy_cfg; + + unsigned char playback_volume[4][2]; /* for VIA8233/C/8235; default = 0 */ + + unsigned int intr_mask; /* SGD_SHADOW mask to check interrupts */ + + struct pci_dev *pci; + snd_card_t *card; + + unsigned int num_devs; + unsigned int playback_devno, multi_devno, capture_devno; + viadev_t devs[VIA_MAX_DEVS]; + struct via_rate_lock rates[2]; /* playback and capture */ + unsigned int dxs_fixed: 1; /* DXS channel accepts only 48kHz */ + unsigned int no_vra: 1; /* no need to set VRA on DXS channels */ + + snd_rawmidi_t *rmidi; + + ac97_bus_t *ac97_bus; + ac97_t *ac97; + unsigned int ac97_clock; + unsigned int ac97_secondary; /* secondary AC'97 codec is present */ + + spinlock_t reg_lock; + spinlock_t ac97_lock; + snd_info_entry_t *proc_entry; + + struct snd_dma_device dma_dev; + +#ifdef SUPPORT_JOYSTICK + struct gameport gameport; + struct resource *res_joystick; +#endif +}; + +static struct pci_device_id snd_via82xx_ids[] = { + { 0x1106, 0x3058, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_CARD_VIA686, }, /* 686A */ + { 0x1106, 0x3059, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_CARD_VIA8233, }, /* VT8233 */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_via82xx_ids); + +/* + */ + /* * allocate and initialize the descriptor buffers * periods = number of periods @@ -357,14 +423,14 @@ static int build_via_table(viadev_t *dev unsigned int periods, unsigned int fragsize) { unsigned int i, idx, ofs, rest; + via82xx_t *chip = snd_pcm_substream_chip(substream); struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream); - if (! dev->table) { + if (dev->table.area == NULL) { /* the start of each lists must be aligned to 8 bytes, * but the kernel pages are much bigger, so we don't care */ - dev->table = (u32*)snd_malloc_pci_pages(pci, PAGE_ALIGN(VIA_TABLE_SIZE * 2 * 8), &dev->table_addr); - if (! dev->table) + if (snd_dma_alloc_pages(&chip->dma_dev, PAGE_ALIGN(VIA_TABLE_SIZE * 2 * 8), &dev->table) < 0) return -ENOMEM; } if (! dev->idx_table) { @@ -390,7 +456,7 @@ static int build_via_table(viadev_t *dev snd_printk(KERN_ERR "via82xx: too much table size!\n"); return -EINVAL; } - dev->table[idx << 1] = cpu_to_le32((u32)snd_pcm_sgbuf_get_addr(sgbuf, ofs)); + ((u32 *)dev->table.area)[idx << 1] = cpu_to_le32((u32)snd_pcm_sgbuf_get_addr(sgbuf, ofs)); r = PAGE_SIZE - (ofs % PAGE_SIZE); if (rest < r) r = rest; @@ -403,7 +469,7 @@ static int build_via_table(viadev_t *dev } else flag = 0; /* period continues to the next */ // printk("via: tbl %d: at %d size %d (rest %d)\n", idx, ofs, r, rest); - dev->table[(idx<<1) + 1] = cpu_to_le32(r | flag); + ((u32 *)dev->table.area)[(idx<<1) + 1] = cpu_to_le32(r | flag); dev->idx_table[idx].offset = ofs; dev->idx_table[idx].size = r; ofs += r; @@ -417,85 +483,21 @@ static int build_via_table(viadev_t *dev } -static void clean_via_table(viadev_t *dev, snd_pcm_substream_t *substream, - struct pci_dev *pci) +static int clean_via_table(viadev_t *dev, snd_pcm_substream_t *substream, + struct pci_dev *pci) { - if (dev->table) { - snd_free_pci_pages(pci, PAGE_ALIGN(VIA_TABLE_SIZE * 2 * 8), dev->table, dev->table_addr); - dev->table = NULL; + via82xx_t *chip = snd_pcm_substream_chip(substream); + if (dev->table.area) { + snd_dma_free_pages(&chip->dma_dev, &dev->table); + dev->table.area = NULL; } if (dev->idx_table) { kfree(dev->idx_table); dev->idx_table = NULL; } + return 0; } - -/* - */ - -enum { TYPE_CARD_VIA686 = 1, TYPE_CARD_VIA8233 }; -enum { TYPE_VIA686, TYPE_VIA8233, TYPE_VIA8233A }; - -#define VIA_MAX_DEVS 7 /* 4 playback, 1 multi, 2 capture */ - -struct via_rate_lock { - spinlock_t lock; - int rate; - int used; -}; - -struct _snd_via82xx { - int irq; - - unsigned long port; - struct resource *res_port; - struct resource *mpu_res; - int chip_type; - unsigned char revision; - - unsigned char old_legacy; - unsigned char old_legacy_cfg; - - unsigned char playback_volume[4][2]; /* for VIA8233/C/8235; default = 0 */ - - unsigned int intr_mask; /* SGD_SHADOW mask to check interrupts */ - - struct pci_dev *pci; - snd_card_t *card; - - unsigned int num_devs; - unsigned int playback_devno, multi_devno, capture_devno; - viadev_t devs[VIA_MAX_DEVS]; - struct via_rate_lock rates[2]; /* playback and capture */ - unsigned int dxs_fixed: 1; /* DXS channel accepts only 48kHz */ - unsigned int no_vra: 1; /* no need to set VRA on DXS channels */ - - snd_rawmidi_t *rmidi; - - ac97_bus_t *ac97_bus; - ac97_t *ac97; - unsigned int ac97_clock; - unsigned int ac97_secondary; /* secondary AC'97 codec is present */ - - spinlock_t reg_lock; - spinlock_t ac97_lock; - snd_info_entry_t *proc_entry; - -#ifdef SUPPORT_JOYSTICK - struct gameport gameport; - struct resource *res_joystick; -#endif -}; - -static struct pci_device_id snd_via82xx_ids[] = { - { 0x1106, 0x3058, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_CARD_VIA686, }, /* 686A */ - { 0x1106, 0x3059, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_CARD_VIA8233, }, /* VT8233 */ - { 0, } -}; - -MODULE_DEVICE_TABLE(pci, snd_via82xx_ids); - /* * Basic I/O */ @@ -756,10 +758,10 @@ static snd_pcm_uframes_t snd_via686_pcm_ * so we need to calculate the index from CURR_PTR. */ ptr = inl(VIADEV_REG(viadev, OFFSET_CURR_PTR)); - if (ptr <= (unsigned int)viadev->table_addr) + if (ptr <= (unsigned int)viadev->table.addr) idx = 0; else /* CURR_PTR holds the address + 8 */ - idx = ((ptr - (unsigned int)viadev->table_addr) / 8 - 1) % viadev->tbl_entries; + idx = ((ptr - (unsigned int)viadev->table.addr) / 8 - 1) % viadev->tbl_entries; res = calc_linear_pos(viadev, idx, count); spin_unlock(&chip->reg_lock); @@ -840,7 +842,7 @@ static int snd_via82xx_hw_free(snd_pcm_s static void snd_via82xx_set_table_ptr(via82xx_t *chip, viadev_t *viadev) { snd_via82xx_codec_ready(chip, 0); - outl((u32)viadev->table_addr, VIADEV_REG(viadev, OFFSET_TABLE_PTR)); + outl((u32)viadev->table.addr, VIADEV_REG(viadev, OFFSET_TABLE_PTR)); udelay(20); snd_via82xx_codec_ready(chip, 0); } @@ -922,12 +924,10 @@ static int snd_via8233_playback_prepare( chip->no_vra ? 48000 : runtime->rate); snd_ac97_set_rate(chip->ac97, AC97_SPDIF, runtime->rate); } -#if 0 - if (chip->revision == VIA_REV_8233A) - rbits = 0; + if (runtime->rate == 48000) + rbits = 0xfffff; else -#endif - rbits = (0xfffff / 48000) * runtime->rate + ((0xfffff % 48000) * runtime->rate) / 48000; + rbits = (0x100000 / 48000) * runtime->rate + ((0x100000 % 48000) * runtime->rate) / 48000; snd_assert((rbits & ~0xfffff) == 0, return -EINVAL); snd_via82xx_channel_reset(chip, viadev); snd_via82xx_set_table_ptr(chip, viadev); @@ -1290,7 +1290,8 @@ static int __devinit snd_via8233_pcm_new /* capture */ init_viadev(chip, chip->capture_devno, VIA_REG_CAPTURE_8233_STATUS, 1); - if ((err = snd_pcm_lib_preallocate_sg_pages_for_all(chip->pci, pcm, 64*1024, 128*1024)) < 0) + if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, + snd_dma_pci_data(chip->pci), 64*1024, 128*1024)) < 0) return err; /* PCM #1: multi-channel playback and 2nd capture */ @@ -1306,7 +1307,8 @@ static int __devinit snd_via8233_pcm_new /* set up capture */ init_viadev(chip, chip->capture_devno + 1, VIA_REG_CAPTURE_8233_STATUS + 0x10, 1); - if ((err = snd_pcm_lib_preallocate_sg_pages_for_all(chip->pci, pcm, 64*1024, 128*1024)) < 0) + if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, + snd_dma_pci_data(chip->pci), 64*1024, 128*1024)) < 0) return err; return 0; @@ -1339,7 +1341,8 @@ static int __devinit snd_via8233a_pcm_ne /* capture */ init_viadev(chip, chip->capture_devno, VIA_REG_CAPTURE_8233_STATUS, 1); - if ((err = snd_pcm_lib_preallocate_sg_pages_for_all(chip->pci, pcm, 64*1024, 128*1024)) < 0) + if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, + snd_dma_pci_data(chip->pci), 64*1024, 128*1024)) < 0) return err; /* PCM #1: DXS3 playback (for spdif) */ @@ -1352,7 +1355,8 @@ static int __devinit snd_via8233a_pcm_ne /* set up playback */ init_viadev(chip, chip->playback_devno, 0x30, 0); - if ((err = snd_pcm_lib_preallocate_sg_pages_for_all(chip->pci, pcm, 64*1024, 128*1024)) < 0) + if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, + snd_dma_pci_data(chip->pci), 64*1024, 128*1024)) < 0) return err; return 0; @@ -1381,7 +1385,8 @@ static int __devinit snd_via686_pcm_new( init_viadev(chip, 0, VIA_REG_PLAYBACK_STATUS, 0); init_viadev(chip, 1, VIA_REG_CAPTURE_STATUS, 1); - if ((err = snd_pcm_lib_preallocate_sg_pages_for_all(chip->pci, pcm, 64*1024, 128*1024)) < 0) + if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, + snd_dma_pci_data(chip->pci), 64*1024, 128*1024)) < 0) return err; return 0; @@ -1560,6 +1565,18 @@ static struct ac97_quirk ac97_quirks[] = .name = "ASRock K7VM2", .type = AC97_TUNE_HP_ONLY /* VT1616 */ }, + { + .vendor = 0x14cd, + .device = 0x7002, + .name = "Unknown", + .type = AC97_TUNE_ALC_JACK + }, + { + .vendor = 0x1071, + .device = 0x8590, + .name = "Mitac Mobo", + .type = AC97_TUNE_ALC_JACK + }, { } /* terminator */ }; @@ -1913,6 +1930,10 @@ static int __devinit snd_via82xx_create( chip->pci = pci; chip->irq = -1; + memset(&chip->dma_dev, 0, sizeof(chip->dma_dev)); + chip->dma_dev.type = SNDRV_DMA_TYPE_DEV; + chip->dma_dev.dev = snd_dma_pci_data(pci); + pci_read_config_byte(pci, VIA_FUNC_ENABLE, &chip->old_legacy); pci_read_config_byte(pci, VIA_PNP_CONTROL, &chip->old_legacy_cfg); @@ -1987,13 +2008,16 @@ static int __devinit check_dxs_list(stru { .vendor = 0x1043, .device = 0x80b0, .action = VIA_DXS_NO_VRA }, /* ASUS A7V600 & K8V*/ { .vendor = 0x10cf, .device = 0x118e, .action = VIA_DXS_ENABLE }, /* FSC laptop */ { .vendor = 0x1106, .device = 0x4161, .action = VIA_DXS_NO_VRA }, /* ASRock K7VT2 */ + { .vendor = 0x1106, .device = 0xaa01, .action = VIA_DXS_NO_VRA }, /* EPIA MII */ { .vendor = 0x1297, .device = 0xa232, .action = VIA_DXS_ENABLE }, /* Shuttle ?? */ { .vendor = 0x1297, .device = 0xc160, .action = VIA_DXS_ENABLE }, /* Shuttle SK41G */ - { .vendor = 0x1458, .device = 0xa002, .action = VIA_DXS_ENABLE }, /* Gigabyte GA-7VAXP */ + { .vendor = 0x1458, .device = 0xa002, .action = VIA_DXS_NO_VRA }, /* Gigabyte GA-7VAXP (FIXME: or DXS_ENABLE?) */ { .vendor = 0x147b, .device = 0x1401, .action = VIA_DXS_ENABLE }, /* ABIT KD7(-RAID) */ { .vendor = 0x14ff, .device = 0x0403, .action = VIA_DXS_ENABLE }, /* Twinhead mobo */ { .vendor = 0x1462, .device = 0x3800, .action = VIA_DXS_ENABLE }, /* MSI KT266 */ { .vendor = 0x1462, .device = 0x7120, .action = VIA_DXS_ENABLE }, /* MSI KT4V */ + { .vendor = 0x1462, .device = 0x5901, .action = VIA_DXS_NO_VRA }, /* MSI KT6 Delta-SR */ + { .vendor = 0x1584, .device = 0x8120, .action = VIA_DXS_ENABLE }, /* Gericom/Targa/Vobis/Uniwill laptop */ { .vendor = 0x1631, .device = 0xe004, .action = VIA_DXS_ENABLE }, /* Easy Note 3174, Packard Bell */ { .vendor = 0x1695, .device = 0x3005, .action = VIA_DXS_ENABLE }, /* EPoX EP-8K9A */ { .vendor = 0x1849, .device = 0x3059, .action = VIA_DXS_NO_VRA }, /* ASRock K7VM2 */ --- linux-2.6.4-rc2/sound/pci/ymfpci/ymfpci_main.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pci/ymfpci/ymfpci_main.c 2004-03-07 20:46:51.000000000 -0800 @@ -541,20 +541,15 @@ static void snd_ymfpci_pcm_init_voice(ym static int __devinit snd_ymfpci_ac3_init(ymfpci_t *chip) { - unsigned char *ptr; - dma_addr_t ptr_addr; - - if ((ptr = snd_malloc_pci_pages(chip->pci, 4096, &ptr_addr)) == NULL) + if (snd_dma_alloc_pages(&chip->dma_dev, 4096, &chip->ac3_tmp_base) < 0) return -ENOMEM; - chip->ac3_tmp_base = ptr; - chip->ac3_tmp_base_addr = ptr_addr; chip->bank_effect[3][0]->base = - chip->bank_effect[3][1]->base = cpu_to_le32(chip->ac3_tmp_base_addr); + chip->bank_effect[3][1]->base = cpu_to_le32(chip->ac3_tmp_base.addr); chip->bank_effect[3][0]->loop_end = chip->bank_effect[3][1]->loop_end = cpu_to_le32(1024); chip->bank_effect[4][0]->base = - chip->bank_effect[4][1]->base = cpu_to_le32(chip->ac3_tmp_base_addr + 2048); + chip->bank_effect[4][1]->base = cpu_to_le32(chip->ac3_tmp_base.addr + 2048); chip->bank_effect[4][0]->loop_end = chip->bank_effect[4][1]->loop_end = cpu_to_le32(1024); @@ -572,9 +567,9 @@ static int snd_ymfpci_ac3_done(ymfpci_t snd_ymfpci_readl(chip, YDSXGR_MAPOFEFFECT) & ~(3 << 3)); spin_unlock_irq(&chip->reg_lock); // snd_ymfpci_irq_wait(chip); - if (chip->ac3_tmp_base) { - snd_free_pci_pages(chip->pci, 4096, chip->ac3_tmp_base, chip->ac3_tmp_base_addr); - chip->ac3_tmp_base = NULL; + if (chip->ac3_tmp_base.area) { + snd_dma_free_pages(&chip->dma_dev, &chip->ac3_tmp_base); + chip->ac3_tmp_base.area = NULL; } return 0; } @@ -1104,7 +1099,8 @@ int __devinit snd_ymfpci_pcm(ymfpci_t *c strcpy(pcm->name, "YMFPCI"); chip->pcm = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), 64*1024, 256*1024); if (rpcm) *rpcm = pcm; @@ -1149,7 +1145,8 @@ int __devinit snd_ymfpci_pcm2(ymfpci_t * chip->device_id == PCI_DEVICE_ID_YAMAHA_754 ? "Direct Recording" : "AC'97"); chip->pcm2 = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), 64*1024, 256*1024); if (rpcm) *rpcm = pcm; @@ -1193,7 +1190,8 @@ int __devinit snd_ymfpci_pcm_spdif(ymfpc strcpy(pcm->name, "YMFPCI - IEC958"); chip->pcm_spdif = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), 64*1024, 256*1024); if (rpcm) *rpcm = pcm; @@ -1237,7 +1235,8 @@ int __devinit snd_ymfpci_pcm_4ch(ymfpci_ strcpy(pcm->name, "YMFPCI - Rear PCM"); chip->pcm_4ch = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), 64*1024, 256*1024); if (rpcm) *rpcm = pcm; @@ -1976,12 +1975,11 @@ static int __devinit snd_ymfpci_memalloc chip->work_size; /* work_ptr must be aligned to 256 bytes, but it's already covered with the kernel page allocation mechanism */ - if ((ptr = snd_malloc_pci_pages(chip->pci, size, &ptr_addr)) == NULL) + if (snd_dma_alloc_pages(&chip->dma_dev, size, &chip->work_ptr) < 0) return -ENOMEM; + ptr = chip->work_ptr.area; + ptr_addr = chip->work_ptr.addr; memset(ptr, 0, size); /* for sure */ - chip->work_ptr = ptr; - chip->work_ptr_addr = ptr_addr; - chip->work_ptr_size = size; chip->bank_base_playback = ptr; chip->bank_base_playback_addr = ptr_addr; @@ -2024,7 +2022,7 @@ static int __devinit snd_ymfpci_memalloc chip->work_base = ptr; chip->work_base_addr = ptr_addr; - snd_assert(ptr + chip->work_size == chip->work_ptr + chip->work_ptr_size, ); + snd_assert(ptr + chip->work_size == chip->work_ptr.area + chip->work_ptr.bytes, ); snd_ymfpci_writel(chip, YDSXGR_PLAYCTRLBASE, chip->bank_base_playback_addr); snd_ymfpci_writel(chip, YDSXGR_RECCTRLBASE, chip->bank_base_capture_addr); @@ -2107,8 +2105,8 @@ static int snd_ymfpci_free(ymfpci_t *chi #endif if (chip->reg_area_virt) iounmap((void *)chip->reg_area_virt); - if (chip->work_ptr) - snd_free_pci_pages(chip->pci, chip->work_ptr_size, chip->work_ptr, chip->work_ptr_addr); + if (chip->work_ptr.area) + snd_dma_free_pages(&chip->dma_dev, &chip->work_ptr); if (chip->irq >= 0) free_irq(chip->irq, (void *)chip); @@ -2276,6 +2274,10 @@ int __devinit snd_ymfpci_create(snd_card } chip->irq = pci->irq; + memset(&chip->dma_dev, 0, sizeof(chip->dma_dev)); + chip->dma_dev.type = SNDRV_DMA_TYPE_DEV; + chip->dma_dev.dev = snd_dma_pci_data(pci); + snd_ymfpci_aclink_reset(pci); if (snd_ymfpci_codec_ready(chip, 0) < 0) { snd_ymfpci_free(chip); --- linux-2.6.4-rc2/sound/pcmcia/Kconfig 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/pcmcia/Kconfig 2004-03-07 20:46:51.000000000 -0800 @@ -6,13 +6,21 @@ menu "PCMCIA devices" config SND_VXPOCKET tristate "Digigram VXpocket" depends on SND && PCMCIA && ISA + select SND_VX_LIB help Say 'Y' or 'M' to include support for Digigram VXpocket soundcard. config SND_VXP440 tristate "Digigram VXpocket 440" depends on SND && PCMCIA && ISA + select SND_VX_LIB help Say 'Y' or 'M' to include support for Digigram VXpocket 440 soundcard. +config SND_PDAUDIOCF + tristate "Sound Core PDAudioCF" + depends on SND && PCMCIA && ISA + help + Say 'Y' or 'M' to include support for Sound Core PDAudioCF soundcard. + endmenu --- linux-2.6.4-rc2/sound/pcmcia/Makefile 2003-06-14 12:17:55.000000000 -0700 +++ 25/sound/pcmcia/Makefile 2004-03-07 20:46:51.000000000 -0800 @@ -3,6 +3,4 @@ # Copyright (c) 2001 by Jaroslav Kysela # -obj-$(CONFIG_SND) += vx/ - - +obj-$(CONFIG_SND) += vx/ pdaudiocf/ --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/sound/pcmcia/pdaudiocf/Makefile 2004-03-07 20:46:51.000000000 -0800 @@ -0,0 +1,8 @@ +# +# Makefile for ALSA +# Copyright (c) 2004 by Jaroslav Kysela +# + +snd-pdaudiocf-objs := pdaudiocf.o pdaudiocf_core.o pdaudiocf_irq.o pdaudiocf_pcm.o + +obj-$(CONFIG_SND_PDAUDIOCF) += snd-pdaudiocf.o --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/sound/pcmcia/pdaudiocf/pdaudiocf.c 2004-03-07 20:46:51.000000000 -0800 @@ -0,0 +1,426 @@ +/* + * Driver for Sound Core PDAudioCF soundcard + * + * Copyright (c) 2003 by Jaroslav Kysela + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include "pdaudiocf.h" +#define SNDRV_GET_ID +#include + +/* + */ + +#define CARD_NAME "PDAudio-CF" + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Sound Core " CARD_NAME); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Sound Core," CARD_NAME "}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */ +static unsigned int irq_mask = 0xffff; +static int irq_list[4] = { -1 }; + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_PARM(irq_mask, "i"); +MODULE_PARM_DESC(irq_mask, "IRQ bitmask for " CARD_NAME " soundcard."); +MODULE_PARM(irq_list, "1-4i"); +MODULE_PARM_DESC(irq_list, "List of Available interrupts for " CARD_NAME " soundcard."); + + +/* + */ + +static dev_info_t dev_info = "snd-pdaudiocf"; +static snd_card_t *card_list[SNDRV_CARDS]; +static dev_link_t *dev_list; + +/* + * prototypes + */ +static void pdacf_config(dev_link_t *link); +static int pdacf_event(event_t event, int priority, event_callback_args_t *args); +static void snd_pdacf_detach(dev_link_t *link); + +static void pdacf_release(dev_link_t *link) +{ + if (link->state & DEV_CONFIG) { + /* release cs resources */ + pcmcia_release_configuration(link->handle); + pcmcia_release_io(link->handle, &link->io); + pcmcia_release_irq(link->handle, &link->irq); + link->state &= ~DEV_CONFIG; + } +} + +/* + * destructor + */ +static int snd_pdacf_free(pdacf_t *pdacf) +{ + dev_link_t *link = &pdacf->link; + + pdacf_release(link); + + /* Break the link with Card Services */ + if (link->handle) + pcmcia_deregister_client(link->handle); + + card_list[pdacf->index] = NULL; + pdacf->card = NULL; + + snd_magic_kfree(pdacf); + return 0; +} + +static int snd_pdacf_dev_free(snd_device_t *device) +{ + pdacf_t *chip = snd_magic_cast(pdacf_t, device->device_data, return -ENXIO); + return snd_pdacf_free(chip); +} + +/* + * snd_pdacf_attach - attach callback for cs + */ +static dev_link_t *snd_pdacf_attach(void) +{ + client_reg_t client_reg; /* Register with cardmgr */ + dev_link_t *link; /* Info for cardmgr */ + int i, ret; + pdacf_t *pdacf; + snd_card_t *card; + static snd_device_ops_t ops = { + .dev_free = snd_pdacf_dev_free, + }; + + snd_printdd(KERN_DEBUG "pdacf_attach called\n"); + /* find an empty slot from the card list */ + for (i = 0; i < SNDRV_CARDS; i++) { + if (! card_list[i]) + break; + } + if (i >= SNDRV_CARDS) { + snd_printk(KERN_ERR "pdacf: too many cards found\n"); + return NULL; + } + if (! enable[i]) + return NULL; /* disabled explicitly */ + + /* ok, create a card instance */ + card = snd_card_new(index[i], id[i], THIS_MODULE, 0); + if (card == NULL) { + snd_printk(KERN_ERR "pdacf: cannot create a card instance\n"); + return NULL; + } + + pdacf = snd_pdacf_create(card); + if (! pdacf) + return NULL; + + if (snd_device_new(card, SNDRV_DEV_LOWLEVEL, pdacf, &ops) < 0) { + snd_magic_kfree(pdacf); + snd_card_free(card); + return NULL; + } + + pdacf->index = i; + card_list[i] = card; + + link = &pdacf->link; + link->priv = pdacf; + + link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; + link->io.NumPorts1 = 16; + + link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT | IRQ_FORCED_PULSE; + // link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED; + + link->irq.IRQInfo1 = IRQ_INFO2_VALID /* | IRQ_LEVEL_ID */; + if (irq_list[0] == -1) + link->irq.IRQInfo2 = irq_mask; + else + for (i = 0; i < 4; i++) + link->irq.IRQInfo2 |= 1 << irq_list[i]; + link->irq.Handler = pdacf_interrupt; + link->irq.Instance = pdacf; + link->conf.Attributes = CONF_ENABLE_IRQ; + link->conf.IntType = INT_MEMORY_AND_IO; + link->conf.ConfigIndex = 1; + link->conf.Present = PRESENT_OPTION; + + /* Chain drivers */ + link->next = dev_list; + dev_list = link; + + /* Register with Card Services */ + client_reg.dev_info = &dev_info; + client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; + client_reg.EventMask = + CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL +#ifdef CONFIG_PM + | CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET + | CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME +#endif + ; + client_reg.event_handler = &pdacf_event; + client_reg.Version = 0x0210; + client_reg.event_callback_args.client_data = link; + + ret = pcmcia_register_client(&link->handle, &client_reg); + if (ret != CS_SUCCESS) { + cs_error(link->handle, RegisterClient, ret); + snd_pdacf_detach(link); + return NULL; + } + + return link; +} + + +/** + * snd_pdacf_assign_resources - initialize the hardware and card instance. + * @port: i/o port for the card + * @irq: irq number for the card + * + * this function assigns the specified port and irq, boot the card, + * create pcm and control instances, and initialize the rest hardware. + * + * returns 0 if successful, or a negative error code. + */ +static int snd_pdacf_assign_resources(pdacf_t *pdacf, int port, int irq) +{ + int err; + snd_card_t *card = pdacf->card; + + snd_printdd(KERN_DEBUG "pdacf assign resources: port = 0x%x, irq = %d\n", port, irq); + pdacf->port = port; + pdacf->irq = irq; + pdacf->chip_status |= PDAUDIOCF_STAT_IS_CONFIGURED; + + err = snd_pdacf_ak4117_create(pdacf); + if (err < 0) + return err; + + strcpy(card->driver, "PDAudio-CF"); + sprintf(card->shortname, "Core Sound %s", card->driver); + sprintf(card->longname, "%s at 0x%x, irq %i", + card->shortname, port, irq); + + err = snd_pdacf_pcm_new(pdacf); + if (err < 0) + return err; + +#ifdef CONFIG_PM + card->power_state_private_data = pdacf; + card->set_power_state = snd_pdacf_set_power_state; +#endif + + if ((err = snd_card_register(card)) < 0) + return err; + + return 0; +} + + +/* + * snd_pdacf_detach - detach callback for cs + */ +static void snd_pdacf_detach(dev_link_t *link) +{ + pdacf_t *chip = snd_magic_cast(pdacf_t, link->priv, return); + + snd_printdd(KERN_DEBUG "pdacf_detach called\n"); + /* Remove the interface data from the linked list */ + { + dev_link_t **linkp; + /* Locate device structure */ + for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) + if (*linkp == link) + break; + if (*linkp) + *linkp = link->next; + } + if (chip->chip_status & PDAUDIOCF_STAT_IS_CONFIGURED) + snd_pdacf_powerdown(chip); + chip->chip_status |= PDAUDIOCF_STAT_IS_STALE; /* to be sure */ + snd_card_disconnect(chip->card); + snd_card_free_in_thread(chip->card); +} + +/* + * snd_pdacf_detach_all - detach all instances linked to the hw + */ +static void snd_pdacf_detach_all(void) +{ + while (dev_list != NULL) + snd_pdacf_detach(dev_list); +} + +/* + * configuration callback + */ + +#define CS_CHECK(fn, ret) \ +do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) + +static void pdacf_config(dev_link_t *link) +{ + client_handle_t handle = link->handle; + pdacf_t *pdacf = snd_magic_cast(pdacf_t, link->priv, return); + tuple_t tuple; + cisparse_t parse; + config_info_t conf; + u_short buf[32]; + int last_fn, last_ret; + + snd_printdd(KERN_DEBUG "pdacf_config called\n"); + tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; + tuple.Attributes = 0; + tuple.TupleData = (cisdata_t *)buf; + tuple.TupleDataMax = sizeof(buf); + tuple.TupleOffset = 0; + tuple.DesiredTuple = CISTPL_CONFIG; + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); + link->conf.ConfigBase = parse.config.base; + link->conf.ConfigIndex = 0x5; + + CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &conf)); + link->conf.Vcc = conf.Vcc; + + /* Configure card */ + link->state |= DEV_CONFIG; + + CS_CHECK(RequestIO, pcmcia_request_io(handle, &link->io)); + CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq)); + CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf)); + + if (snd_pdacf_assign_resources(pdacf, link->io.BasePort1, link->irq.AssignedIRQ) < 0) + goto failed; + + link->dev = &pdacf->node; + link->state &= ~DEV_CONFIG_PENDING; + return; + +cs_failed: + cs_error(link->handle, last_fn, last_ret); +failed: + pcmcia_release_configuration(link->handle); + pcmcia_release_io(link->handle, &link->io); + pcmcia_release_irq(link->handle, &link->irq); +} + +/* + * event callback + */ +static int pdacf_event(event_t event, int priority, event_callback_args_t *args) +{ + dev_link_t *link = args->client_data; + pdacf_t *chip = link->priv; + + switch (event) { + case CS_EVENT_CARD_REMOVAL: + snd_printdd(KERN_DEBUG "CARD_REMOVAL..\n"); + link->state &= ~DEV_PRESENT; + if (link->state & DEV_CONFIG) { + chip->chip_status |= PDAUDIOCF_STAT_IS_STALE; + } + break; + case CS_EVENT_CARD_INSERTION: + snd_printdd(KERN_DEBUG "CARD_INSERTION..\n"); + link->state |= DEV_PRESENT; + pdacf_config(link); + break; +#ifdef CONFIG_PM + case CS_EVENT_PM_SUSPEND: + snd_printdd(KERN_DEBUG "SUSPEND\n"); + link->state |= DEV_SUSPEND; + if (chip) { + snd_printdd(KERN_DEBUG "snd_pdacf_suspend calling\n"); + snd_pdacf_suspend(chip); + } + /* Fall through... */ + case CS_EVENT_RESET_PHYSICAL: + snd_printdd(KERN_DEBUG "RESET_PHYSICAL\n"); + if (link->state & DEV_CONFIG) + pcmcia_release_configuration(link->handle); + break; + case CS_EVENT_PM_RESUME: + snd_printdd(KERN_DEBUG "RESUME\n"); + link->state &= ~DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_CARD_RESET: + snd_printdd(KERN_DEBUG "CARD_RESET\n"); + if (DEV_OK(link)) { + snd_printdd(KERN_DEBUG "requestconfig...\n"); + pcmcia_request_configuration(link->handle, &link->conf); + if (chip) { + snd_printdd(KERN_DEBUG "calling snd_pdacf_resume\n"); + snd_pdacf_resume(chip); + } + } + snd_printdd(KERN_DEBUG "resume done!\n"); + break; +#endif + } + return 0; +} + +/* + * Module entry points + */ +static struct pcmcia_driver pdacf_cs_driver = { + .owner = THIS_MODULE, + .drv = { + .name = "snd-pdaudiocf", + }, + .attach = snd_pdacf_attach, + .detach = snd_pdacf_detach +}; + +static int __init init_pdacf(void) +{ + return pcmcia_register_driver(&pdacf_cs_driver); +} + +static void __exit exit_pdacf(void) +{ + pcmcia_unregister_driver(&pdacf_cs_driver); + snd_pdacf_detach_all(); +} + +module_init(init_pdacf); +module_exit(exit_pdacf); --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/sound/pcmcia/pdaudiocf/pdaudiocf_core.c 2004-03-07 20:46:51.000000000 -0800 @@ -0,0 +1,317 @@ +/* + * Driver for Sound Core PDAudioCF soundcard + * + * Copyright (c) 2003 by Jaroslav Kysela + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include "pdaudiocf.h" +#define SNDRV_GET_ID +#include + +/* + * + */ +unsigned char pdacf_ak4117_read(void *private_data, unsigned char reg) +{ + pdacf_t *chip = snd_magic_cast(pdacf_t, private_data, return 0); + unsigned long timeout; + unsigned long flags; + unsigned char res; + + spin_lock_irqsave(&chip->ak4117_lock, flags); + timeout = 1000; + while (pdacf_reg_read(chip, PDAUDIOCF_REG_SCR) & PDAUDIOCF_AK_SBP) { + udelay(5); + if (--timeout == 0) { + spin_unlock_irqrestore(&chip->ak4117_lock, flags); + snd_printk(KERN_ERR "AK4117 ready timeout (read)\n"); + return 0; + } + } + pdacf_reg_write(chip, PDAUDIOCF_REG_AK_IFR, (u16)reg << 8); + timeout = 1000; + while (pdacf_reg_read(chip, PDAUDIOCF_REG_SCR) & PDAUDIOCF_AK_SBP) { + udelay(5); + if (--timeout == 0) { + spin_unlock_irqrestore(&chip->ak4117_lock, flags); + snd_printk(KERN_ERR "AK4117 read timeout (read2)\n"); + return 0; + } + } + res = (unsigned char)pdacf_reg_read(chip, PDAUDIOCF_REG_AK_IFR); + spin_unlock_irqrestore(&chip->ak4117_lock, flags); + return res; +} + +void pdacf_ak4117_write(void *private_data, unsigned char reg, unsigned char val) +{ + pdacf_t *chip = snd_magic_cast(pdacf_t, private_data, return); + unsigned long timeout; + unsigned long flags; + + spin_lock_irqsave(&chip->ak4117_lock, flags); + timeout = 1000; + while (inw(chip->port + PDAUDIOCF_REG_SCR) & PDAUDIOCF_AK_SBP) { + udelay(5); + if (--timeout == 0) { + spin_unlock_irqrestore(&chip->ak4117_lock, flags); + snd_printk(KERN_ERR "AK4117 ready timeout (write)\n"); + return; + } + } + outw((u16)reg << 8 | val | (1<<13), chip->port + PDAUDIOCF_REG_AK_IFR); + spin_unlock_irqrestore(&chip->ak4117_lock, flags); +} + +#if 0 +void pdacf_dump(pdacf_t *chip) +{ + printk("PDAUDIOCF DUMP (0x%lx):\n", chip->port); + printk("WPD : 0x%x\n", inw(chip->port + PDAUDIOCF_REG_WDP)); + printk("RDP : 0x%x\n", inw(chip->port + PDAUDIOCF_REG_RDP)); + printk("TCR : 0x%x\n", inw(chip->port + PDAUDIOCF_REG_TCR)); + printk("SCR : 0x%x\n", inw(chip->port + PDAUDIOCF_REG_SCR)); + printk("ISR : 0x%x\n", inw(chip->port + PDAUDIOCF_REG_ISR)); + printk("IER : 0x%x\n", inw(chip->port + PDAUDIOCF_REG_IER)); + printk("AK_IFR : 0x%x\n", inw(chip->port + PDAUDIOCF_REG_AK_IFR)); +} +#endif + +static int pdacf_reset(pdacf_t *chip, int powerdown) +{ + u16 val; + + val = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR); + val |= PDAUDIOCF_PDN; + val &= ~PDAUDIOCF_RECORD; /* for sure */ + pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val); + udelay(5); + val |= PDAUDIOCF_RST; + pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val); + udelay(200); + val &= ~PDAUDIOCF_RST; + pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val); + udelay(5); + if (!powerdown) { + val &= ~PDAUDIOCF_PDN; + pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val); + udelay(200); + } + return 0; +} + +void pdacf_reinit(pdacf_t *chip, int resume) +{ + pdacf_reset(chip, 0); + if (resume) + pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, chip->suspend_reg_scr); + snd_ak4117_reinit(chip->ak4117); + pdacf_reg_write(chip, PDAUDIOCF_REG_TCR, chip->regmap[PDAUDIOCF_REG_TCR>>1]); + pdacf_reg_write(chip, PDAUDIOCF_REG_IER, chip->regmap[PDAUDIOCF_REG_IER>>1]); +} + +static void pdacf_proc_read(snd_info_entry_t * entry, + snd_info_buffer_t * buffer) +{ + pdacf_t *chip = snd_magic_cast(pdacf_t, entry->private_data, return); + u16 tmp; + + snd_iprintf(buffer, "PDAudioCF\n\n"); + tmp = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR); + snd_iprintf(buffer, "FPGA revision : 0x%x\n", PDAUDIOCF_FPGAREV(tmp)); + +} + +static void pdacf_proc_init(pdacf_t *chip) +{ + snd_info_entry_t *entry; + + if (! snd_card_proc_new(chip->card, "pdaudiocf", &entry)) + snd_info_set_text_ops(entry, chip, 1024, pdacf_proc_read); +} + +pdacf_t *snd_pdacf_create(snd_card_t *card) +{ + pdacf_t *chip; + + chip = snd_magic_kcalloc(pdacf_t, 0, GFP_KERNEL); + if (chip == NULL) + return NULL; + chip->card = card; + spin_lock_init(&chip->reg_lock); + spin_lock_init(&chip->ak4117_lock); + tasklet_init(&chip->tq, pdacf_tasklet, (unsigned long)chip); + card->private_data = chip; + + pdacf_proc_init(chip); + return chip; +} + +static void snd_pdacf_ak4117_change(ak4117_t *ak4117, unsigned char c0, unsigned char c1) +{ + pdacf_t *chip = ak4117->change_callback_private; + unsigned long flags; + u16 val; + + if (!(c0 & AK4117_UNLCK)) + return; + spin_lock_irqsave(&chip->reg_lock, flags); + val = chip->regmap[PDAUDIOCF_REG_SCR>>1]; + if (ak4117->rcs0 & AK4117_UNLCK) + val |= PDAUDIOCF_BLUE_LED_OFF; + else + val &= ~PDAUDIOCF_BLUE_LED_OFF; + pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +int snd_pdacf_ak4117_create(pdacf_t *chip) +{ + int err; + u16 val; + /* design note: if we unmask PLL unlock, parity, valid, audio or auto bit interrupts */ + /* from AK4117 then INT1 pin from AK4117 will be high all time, because PCMCIA interrupts are */ + /* egde based and FPGA does logical OR for all interrupt sources, we cannot use these */ + /* high-rate sources */ + static unsigned char pgm[5] = { + AK4117_XTL_24_576M | AK4117_EXCT, /* AK4117_REG_PWRDN */ + AK4117_CM_PLL_XTAL | AK4117_PKCS_128fs | AK4117_XCKS_128fs, /* AK4117_REQ_CLOCK */ + AK4117_EFH_1024LRCLK | AK4117_DIF_24R | AK4117_IPS, /* AK4117_REG_IO */ + 0xff, /* AK4117_REG_INT0_MASK */ + AK4117_MAUTO | AK4117_MAUD | AK4117_MULK | AK4117_MPAR | AK4117_MV, /* AK4117_REG_INT1_MASK */ + }; + + err = pdacf_reset(chip, 0); + if (err < 0) + return err; + err = snd_ak4117_create(chip->card, pdacf_ak4117_read, pdacf_ak4117_write, pgm, chip, &chip->ak4117); + if (err < 0) + return err; + + val = pdacf_reg_read(chip, PDAUDIOCF_REG_TCR); +#if 1 /* normal operation */ + val &= ~(PDAUDIOCF_ELIMAKMBIT|PDAUDIOCF_TESTDATASEL); +#else /* debug */ + val |= PDAUDIOCF_ELIMAKMBIT; + val &= ~PDAUDIOCF_TESTDATASEL; +#endif + pdacf_reg_write(chip, PDAUDIOCF_REG_TCR, val); + + /* setup the FPGA to match AK4117 setup */ + val = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR); + val &= ~(PDAUDIOCF_CLKDIV0 | PDAUDIOCF_CLKDIV1); /* use 24.576Mhz clock */ + val &= ~(PDAUDIOCF_RED_LED_OFF|PDAUDIOCF_BLUE_LED_OFF); + val |= PDAUDIOCF_DATAFMT0 | PDAUDIOCF_DATAFMT1; /* 24-bit data */ + pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val); + + /* setup LEDs and IRQ */ + val = pdacf_reg_read(chip, PDAUDIOCF_REG_IER); + val &= ~(PDAUDIOCF_IRQLVLEN0 | PDAUDIOCF_IRQLVLEN1); + val &= ~(PDAUDIOCF_BLUEDUTY0 | PDAUDIOCF_REDDUTY0 | PDAUDIOCF_REDDUTY1); + val |= PDAUDIOCF_BLUEDUTY1 | PDAUDIOCF_HALFRATE; + val |= PDAUDIOCF_IRQOVREN | PDAUDIOCF_IRQAKMEN; + pdacf_reg_write(chip, PDAUDIOCF_REG_IER, val); + + chip->ak4117->change_callback_private = chip; + chip->ak4117->change_callback = snd_pdacf_ak4117_change; + + /* update LED status */ + snd_pdacf_ak4117_change(chip->ak4117, AK4117_UNLCK, 0); + + return 0; +} + +void snd_pdacf_powerdown(pdacf_t *chip) +{ + u16 val; + + val = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR); + chip->suspend_reg_scr = val; + val |= PDAUDIOCF_RED_LED_OFF | PDAUDIOCF_BLUE_LED_OFF; + pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val); + /* disable interrupts, but use direct write to preserve old register value in chip->regmap */ + val = inw(chip->port + PDAUDIOCF_REG_IER); + val &= ~(PDAUDIOCF_IRQOVREN|PDAUDIOCF_IRQAKMEN|PDAUDIOCF_IRQLVLEN0|PDAUDIOCF_IRQLVLEN1); + outw(val, chip->port + PDAUDIOCF_REG_IER); + pdacf_reset(chip, 1); +} + +#ifdef CONFIG_PM + +void snd_pdacf_suspend(pdacf_t *chip) +{ + snd_card_t *card = chip->card; + u16 val; + + if (card->power_state == SNDRV_CTL_POWER_D3hot) + return; + snd_pcm_suspend_all(chip->pcm); + /* disable interrupts, but use direct write to preserve old register value in chip->regmap */ + val = inw(chip->port + PDAUDIOCF_REG_IER); + val &= ~(PDAUDIOCF_IRQOVREN|PDAUDIOCF_IRQAKMEN|PDAUDIOCF_IRQLVLEN0|PDAUDIOCF_IRQLVLEN1); + outw(val, chip->port + PDAUDIOCF_REG_IER); + chip->chip_status |= PDAUDIOCF_STAT_IS_SUSPENDED; /* ignore interrupts from now */ + snd_pdacf_powerdown(chip); + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); +} + +static inline int check_signal(pdacf_t *chip) +{ + return (chip->ak4117->rcs0 & AK4117_UNLCK) == 0; +} + +void snd_pdacf_resume(pdacf_t *chip) +{ + snd_card_t *card = chip->card; + int timeout = 40; + + if (card->power_state == SNDRV_CTL_POWER_D0) + return; + pdacf_reinit(chip, 1); + /* wait for AK4117's PLL */ + while (timeout-- > 0 && + (snd_ak4117_external_rate(chip->ak4117) <= 0 || !check_signal(chip))) + mdelay(1); + chip->chip_status &= ~PDAUDIOCF_STAT_IS_SUSPENDED; + snd_power_change_state(card, SNDRV_CTL_POWER_D0); +} + +int snd_pdacf_set_power_state(snd_card_t *card, unsigned int power_state) +{ + pdacf_t *chip = snd_magic_cast(pdacf_t, card->power_state_private_data, return -ENXIO); + + switch (power_state) { + case SNDRV_CTL_POWER_D0: + case SNDRV_CTL_POWER_D1: + case SNDRV_CTL_POWER_D2: + snd_pdacf_resume(chip); + break; + case SNDRV_CTL_POWER_D3hot: + case SNDRV_CTL_POWER_D3cold: + snd_pdacf_suspend(chip); + break; + default: + return -EINVAL; + } + return 0; +} + +#endif --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/sound/pcmcia/pdaudiocf/pdaudiocf.h 2004-03-07 20:46:51.000000000 -0800 @@ -0,0 +1,148 @@ +/* + * Driver for Sound Cors PDAudioCF soundcard + * + * Copyright (c) 2003 by Jaroslav Kysela + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PDAUDIOCF_H +#define __PDAUDIOCF_H + +#include +#include +#include +#include +#include +#include +#include + +#include + +/* PDAUDIOCF registers */ +#define PDAUDIOCF_REG_MD 0x00 /* music data, R/O */ +#define PDAUDIOCF_REG_WDP 0x02 /* write data pointer / 2, R/O */ +#define PDAUDIOCF_REG_RDP 0x04 /* read data pointer / 2, R/O */ +#define PDAUDIOCF_REG_TCR 0x06 /* test control register W/O */ +#define PDAUDIOCF_REG_SCR 0x08 /* status and control, R/W (see bit description) */ +#define PDAUDIOCF_REG_ISR 0x0a /* interrupt status, R/O */ +#define PDAUDIOCF_REG_IER 0x0c /* interrupt enable, R/W */ +#define PDAUDIOCF_REG_AK_IFR 0x0e /* AK interface register, R/W */ + +/* PDAUDIOCF_REG_TCR */ +#define PDAUDIOCF_ELIMAKMBIT (1<<0) /* simulate AKM music data */ +#define PDAUDIOCF_TESTDATASEL (1<<1) /* test data selection, 0 = 0x55, 1 = pseudo-random */ + +/* PDAUDIOCF_REG_SCR */ +#define PDAUDIOCF_AK_SBP (1<<0) /* serial port busy flag */ +#define PDAUDIOCF_RST (1<<2) /* FPGA, AKM + SRAM buffer reset */ +#define PDAUDIOCF_PDN (1<<3) /* power down bit */ +#define PDAUDIOCF_CLKDIV0 (1<<4) /* choose 24.576Mhz clock divided by 1,2,3 or 4 */ +#define PDAUDIOCF_CLKDIV1 (1<<5) +#define PDAUDIOCF_RECORD (1<<6) /* start capturing to SRAM */ +#define PDAUDIOCF_AK_SDD (1<<7) /* music data detected */ +#define PDAUDIOCF_RED_LED_OFF (1<<8) /* red LED off override */ +#define PDAUDIOCF_BLUE_LED_OFF (1<<9) /* blue LED off override */ +#define PDAUDIOCF_DATAFMT0 (1<<10) /* data format bits: 00 = 16-bit, 01 = 18-bit */ +#define PDAUDIOCF_DATAFMT1 (1<<11) /* 10 = 20-bit, 11 = 24-bit, all right justified */ +#define PDAUDIOCF_FPGAREV(x) ((x>>12)&0x0f) /* FPGA revision */ + +/* PDAUDIOCF_REG_ISR */ +#define PDAUDIOCF_IRQLVL (1<<0) /* Buffer level IRQ */ +#define PDAUDIOCF_IRQOVR (1<<1) /* Overrun IRQ */ +#define PDAUDIOCF_IRQAKM (1<<2) /* AKM IRQ */ + +/* PDAUDIOCF_REG_IER */ +#define PDAUDIOCF_IRQLVLEN0 (1<<0) /* fill threshold levels; 00 = none, 01 = 1/8th of buffer */ +#define PDAUDIOCF_IRQLVLEN1 (1<<1) /* 10 = 1/4th of buffer, 11 = 1/2th of buffer */ +#define PDAUDIOCF_IRQOVREN (1<<2) /* enable overrun IRQ */ +#define PDAUDIOCF_IRQAKMEN (1<<3) /* enable AKM IRQ */ +#define PDAUDIOCF_BLUEDUTY0 (1<<8) /* blue LED duty cycle; 00 = 100%, 01 = 50% */ +#define PDAUDIOCF_BLUEDUTY1 (1<<9) /* 02 = 25%, 11 = 12% */ +#define PDAUDIOCF_REDDUTY0 (1<<10) /* red LED duty cycle; 00 = 100%, 01 = 50% */ +#define PDAUDIOCF_REDDUTY1 (1<<11) /* 02 = 25%, 11 = 12% */ +#define PDAUDIOCF_BLUESDD (1<<12) /* blue LED against SDD bit */ +#define PDAUDIOCF_BLUEMODULATE (1<<13) /* save power when 100% duty cycle selected */ +#define PDAUDIOCF_REDMODULATE (1<<14) /* save power when 100% duty cycle selected */ +#define PDAUDIOCF_HALFRATE (1<<15) /* slow both LED blinks by half (also spdif detect rate) */ + +/* chip status */ +#define PDAUDIOCF_STAT_IS_STALE (1<<0) +#define PDAUDIOCF_STAT_IS_CONFIGURED (1<<1) +#define PDAUDIOCF_STAT_IS_SUSPENDED (1<<2) + +typedef struct { + snd_card_t *card; + int index; + + unsigned long port; + int irq; + + spinlock_t reg_lock; + unsigned short regmap[8]; + unsigned short suspend_reg_scr; + struct tasklet_struct tq; + + spinlock_t ak4117_lock; + ak4117_t *ak4117; + + unsigned int chip_status; + + snd_pcm_t *pcm; + snd_pcm_substream_t *pcm_substream; + unsigned int pcm_running: 1; + unsigned int pcm_channels; + unsigned int pcm_swab; + unsigned int pcm_little; + unsigned int pcm_frame; + unsigned int pcm_sample; + unsigned int pcm_xor; + unsigned int pcm_size; + unsigned int pcm_period; + unsigned int pcm_tdone; + unsigned int pcm_hwptr; + void *pcm_area; + + /* pcmcia stuff */ + dev_link_t link; + dev_node_t node; +} pdacf_t; + +static inline void pdacf_reg_write(pdacf_t *chip, unsigned char reg, unsigned short val) +{ + outw(chip->regmap[reg>>1] = val, chip->port + reg); +} + +static inline unsigned short pdacf_reg_read(pdacf_t *chip, unsigned char reg) +{ + return inw(chip->port + reg); +} + +unsigned char pdacf_ak4117_read(void *private_data, unsigned char reg); +void pdacf_ak4117_write(void *private_data, unsigned char reg, unsigned char val); +pdacf_t *snd_pdacf_create(snd_card_t *card); +int snd_pdacf_ak4117_create(pdacf_t *pdacf); +void snd_pdacf_powerdown(pdacf_t *chip); +#ifdef CONFIG_PM +void snd_pdacf_suspend(pdacf_t *chip); +void snd_pdacf_resume(pdacf_t *chip); +int snd_pdacf_set_power_state(snd_card_t *card, unsigned int power_state); +#endif +int snd_pdacf_pcm_new(pdacf_t *chip); +void pdacf_interrupt(int irq, void *dev, struct pt_regs *regs); +void pdacf_tasklet(unsigned long private_data); +void pdacf_reinit(pdacf_t *chip, int resume); + +#endif /* __PDAUDIOCF_H */ --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c 2004-03-07 20:46:51.000000000 -0800 @@ -0,0 +1,325 @@ +/* + * Driver for Sound Core PDAudioCF soundcard + * + * Copyright (c) 2003 by Jaroslav Kysela + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include "pdaudiocf.h" +#define SNDRV_GET_ID +#include + +/* + * + */ +void pdacf_interrupt(int irq, void *dev, struct pt_regs *regs) +{ + pdacf_t *chip = snd_magic_cast(pdacf_t, dev, return); + unsigned short stat; + + if ((chip->chip_status & (PDAUDIOCF_STAT_IS_STALE| + PDAUDIOCF_STAT_IS_CONFIGURED| + PDAUDIOCF_STAT_IS_SUSPENDED)) != PDAUDIOCF_STAT_IS_CONFIGURED) + return; + + stat = inw(chip->port + PDAUDIOCF_REG_ISR); + if (stat & (PDAUDIOCF_IRQLVL|PDAUDIOCF_IRQOVR)) { + if (stat & PDAUDIOCF_IRQOVR) /* should never happen */ + snd_printk(KERN_ERR "PDAUDIOCF SRAM buffer overrun detected!\n"); + if (chip->pcm_substream) + tasklet_hi_schedule(&chip->tq); + if (!(stat & PDAUDIOCF_IRQAKM)) + stat |= PDAUDIOCF_IRQAKM; /* check rate */ + } + if (regs != NULL) + snd_ak4117_check_rate_and_errors(chip->ak4117, 0); +} + +static inline void pdacf_transfer_mono16(u16 *dst, u16 xor, unsigned int size, unsigned long rdp_port) +{ + while (size-- > 0) { + *dst++ = inw(rdp_port) ^ xor; + inw(rdp_port); + } +} + +static inline void pdacf_transfer_mono32(u32 *dst, u32 xor, unsigned int size, unsigned long rdp_port) +{ + register u16 val1, val2; + + while (size-- > 0) { + val1 = inw(rdp_port); + val2 = inw(rdp_port); + inw(rdp_port); + *dst++ = ((((u32)val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor; + } +} + +static inline void pdacf_transfer_stereo16(u16 *dst, u16 xor, unsigned int size, unsigned long rdp_port) +{ + while (size-- > 0) { + *dst++ = inw(rdp_port) ^ xor; + *dst++ = inw(rdp_port) ^ xor; + } +} + +static inline void pdacf_transfer_stereo32(u32 *dst, u32 xor, unsigned int size, unsigned long rdp_port) +{ + register u16 val1, val2, val3; + + while (size-- > 0) { + val1 = inw(rdp_port); + val2 = inw(rdp_port); + val3 = inw(rdp_port); + *dst++ = ((((u32)val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor; + *dst++ = (((u32)val3 << 16) | (val2 & 0xff00)) ^ xor; + } +} + +static inline void pdacf_transfer_mono16sw(u16 *dst, u16 xor, unsigned int size, unsigned long rdp_port) +{ + while (size-- > 0) { + *dst++ = swab16(inw(rdp_port) ^ xor); + inw(rdp_port); + } +} + +static inline void pdacf_transfer_mono32sw(u32 *dst, u32 xor, unsigned int size, unsigned long rdp_port) +{ + register u16 val1, val2; + + while (size-- > 0) { + val1 = inw(rdp_port); + val2 = inw(rdp_port); + inw(rdp_port); + *dst++ = swab32((((val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor); + } +} + +static inline void pdacf_transfer_stereo16sw(u16 *dst, u16 xor, unsigned int size, unsigned long rdp_port) +{ + while (size-- > 0) { + *dst++ = swab16(inw(rdp_port) ^ xor); + *dst++ = swab16(inw(rdp_port) ^ xor); + } +} + +static inline void pdacf_transfer_stereo32sw(u32 *dst, u32 xor, unsigned int size, unsigned long rdp_port) +{ + register u16 val1, val2, val3; + + while (size-- > 0) { + val1 = inw(rdp_port); + val2 = inw(rdp_port); + val3 = inw(rdp_port); + *dst++ = swab32((((val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor); + *dst++ = swab32((((u32)val3 << 16) | (val2 & 0xff00)) ^ xor); + } +} + +static inline void pdacf_transfer_mono24le(u8 *dst, u16 xor, unsigned int size, unsigned long rdp_port) +{ + register u16 val1, val2; + register u32 xval1; + + while (size-- > 0) { + val1 = inw(rdp_port); + val2 = inw(rdp_port); + inw(rdp_port); + xval1 = (((val2 & 0xff) << 8) | (val1 << 16)) ^ xor; + *dst++ = (u8)(xval1 >> 8); + *dst++ = (u8)(xval1 >> 16); + *dst++ = (u8)(xval1 >> 24); + } +} + +static inline void pdacf_transfer_mono24be(u8 *dst, u16 xor, unsigned int size, unsigned long rdp_port) +{ + register u16 val1, val2; + register u32 xval1; + + while (size-- > 0) { + val1 = inw(rdp_port); + val2 = inw(rdp_port); + inw(rdp_port); + xval1 = (((val2 & 0xff) << 8) | (val1 << 16)) ^ xor; + *dst++ = (u8)(xval1 >> 24); + *dst++ = (u8)(xval1 >> 16); + *dst++ = (u8)(xval1 >> 8); + } +} + +static inline void pdacf_transfer_stereo24le(u8 *dst, u32 xor, unsigned int size, unsigned long rdp_port) +{ + register u16 val1, val2, val3; + register u32 xval1, xval2; + + while (size-- > 0) { + val1 = inw(rdp_port); + val2 = inw(rdp_port); + val3 = inw(rdp_port); + xval1 = ((((u32)val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor; + xval2 = (((u32)val3 << 16) | (val2 & 0xff00)) ^ xor; + *dst++ = (u8)(xval1 >> 8); + *dst++ = (u8)(xval1 >> 16); + *dst++ = (u8)(xval1 >> 24); + *dst++ = (u8)(xval2 >> 8); + *dst++ = (u8)(xval2 >> 16); + *dst++ = (u8)(xval2 >> 24); + } +} + +static inline void pdacf_transfer_stereo24be(u8 *dst, u32 xor, unsigned int size, unsigned long rdp_port) +{ + register u16 val1, val2, val3; + register u32 xval1, xval2; + + while (size-- > 0) { + val1 = inw(rdp_port); + val2 = inw(rdp_port); + val3 = inw(rdp_port); + xval1 = ((((u32)val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor; + xval2 = (((u32)val3 << 16) | (val2 & 0xff00)) ^ xor; + *dst++ = (u8)(xval1 >> 24); + *dst++ = (u8)(xval1 >> 16); + *dst++ = (u8)(xval1 >> 8); + *dst++ = (u8)(xval2 >> 24); + *dst++ = (u8)(xval2 >> 16); + *dst++ = (u8)(xval2 >> 8); + } +} + +static void pdacf_transfer(pdacf_t *chip, unsigned int size, unsigned int off) +{ + unsigned long rdp_port = chip->port + PDAUDIOCF_REG_MD; + unsigned int xor = chip->pcm_xor; + + if (chip->pcm_sample == 3) { + if (chip->pcm_little) { + if (chip->pcm_channels == 1) { + pdacf_transfer_mono24le((char *)chip->pcm_area + (off * 3), xor, size, rdp_port); + } else { + pdacf_transfer_stereo24le((char *)chip->pcm_area + (off * 6), xor, size, rdp_port); + } + } else { + if (chip->pcm_channels == 1) { + pdacf_transfer_mono24be((char *)chip->pcm_area + (off * 3), xor, size, rdp_port); + } else { + pdacf_transfer_stereo24be((char *)chip->pcm_area + (off * 6), xor, size, rdp_port); + } + } + return; + } + if (chip->pcm_swab == 0) { + if (chip->pcm_channels == 1) { + if (chip->pcm_frame == 2) { + pdacf_transfer_mono16((u16 *)chip->pcm_area + off, xor, size, rdp_port); + } else { + pdacf_transfer_mono32((u32 *)chip->pcm_area + off, xor, size, rdp_port); + } + } else { + if (chip->pcm_frame == 2) { + pdacf_transfer_stereo16((u16 *)chip->pcm_area + (off * 2), xor, size, rdp_port); + } else { + pdacf_transfer_stereo32((u32 *)chip->pcm_area + (off * 2), xor, size, rdp_port); + } + } + } else { + if (chip->pcm_channels == 1) { + if (chip->pcm_frame == 2) { + pdacf_transfer_mono16sw((u16 *)chip->pcm_area + off, xor, size, rdp_port); + } else { + pdacf_transfer_mono32sw((u32 *)chip->pcm_area + off, xor, size, rdp_port); + } + } else { + if (chip->pcm_frame == 2) { + pdacf_transfer_stereo16sw((u16 *)chip->pcm_area + (off * 2), xor, size, rdp_port); + } else { + pdacf_transfer_stereo32sw((u32 *)chip->pcm_area + (off * 2), xor, size, rdp_port); + } + } + } +} + +void pdacf_tasklet(unsigned long private_data) +{ + pdacf_t *chip = snd_magic_cast(pdacf_t, (void *)private_data, return); + int size, off, cont, rdp, wdp; + + if ((chip->chip_status & (PDAUDIOCF_STAT_IS_STALE|PDAUDIOCF_STAT_IS_CONFIGURED)) != PDAUDIOCF_STAT_IS_CONFIGURED) + return; + + if (chip->pcm_substream == NULL || chip->pcm_substream->runtime == NULL || !snd_pcm_running(chip->pcm_substream)) + return; + + rdp = inw(chip->port + PDAUDIOCF_REG_RDP); + wdp = inw(chip->port + PDAUDIOCF_REG_WDP); + // printk("TASKLET: rdp = %x, wdp = %x\n", rdp, wdp); + size = wdp - rdp; + if (size < 0) + size += 0x10000; + if (size == 0) + size = 0x10000; + size /= chip->pcm_frame; + if (size > 64) + size -= 32; + +#if 0 + chip->pcm_hwptr += size; + chip->pcm_hwptr %= chip->pcm_size; + chip->pcm_tdone += size; + if (chip->pcm_frame == 2) { + unsigned long rdp_port = chip->port + PDAUDIOCF_REG_MD; + while (size-- > 0) { + inw(rdp_port); + inw(rdp_port); + } + } else { + unsigned long rdp_port = chip->port + PDAUDIOCF_REG_MD; + while (size-- > 0) { + inw(rdp_port); + inw(rdp_port); + inw(rdp_port); + } + } +#else + off = chip->pcm_hwptr + chip->pcm_tdone; + off %= chip->pcm_size; + chip->pcm_tdone += size; + while (size > 0) { + cont = chip->pcm_size - off; + if (cont > size) + cont = size; + pdacf_transfer(chip, cont, off); + off += cont; + off %= chip->pcm_size; + size -= cont; + } +#endif + spin_lock(&chip->reg_lock); + while (chip->pcm_tdone >= chip->pcm_period) { + chip->pcm_hwptr += chip->pcm_period; + chip->pcm_hwptr %= chip->pcm_size; + chip->pcm_tdone -= chip->pcm_period; + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(chip->pcm_substream); + spin_lock(&chip->reg_lock); + } + spin_unlock(&chip->reg_lock); + // printk("TASKLET: end\n"); +} --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c 2004-03-07 20:46:51.000000000 -0800 @@ -0,0 +1,363 @@ +/* + * Driver for Sound Core PDAudioCF soundcards + * + * PCM part + * + * Copyright (c) 2003 by Jaroslav Kysela + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include "pdaudiocf.h" + +#define chip_t pdacf_t + + +/* + * we use a vmalloc'ed (sg-)buffer + */ + +/* get the physical page pointer on the given offset */ +static struct page *snd_pcm_get_vmalloc_page(snd_pcm_substream_t *subs, unsigned long offset) +{ + void *pageptr = subs->runtime->dma_area + offset; + return vmalloc_to_page(pageptr); +} + +/* + * hw_params callback + * NOTE: this may be called not only once per pcm open! + */ +static int snd_pcm_alloc_vmalloc_buffer(snd_pcm_substream_t *subs, size_t size) +{ + snd_pcm_runtime_t *runtime = subs->runtime; + if (runtime->dma_area) { + if (runtime->dma_bytes >= size) + return 0; /* already enough large */ + vfree_nocheck(runtime->dma_area); + } + runtime->dma_area = vmalloc_nocheck(size); + if (! runtime->dma_area) + return -ENOMEM; + runtime->dma_bytes = size; + return 0; +} + +/* + * hw_free callback + * NOTE: this may be called not only once per pcm open! + */ +static int snd_pcm_free_vmalloc_buffer(snd_pcm_substream_t *subs) +{ + snd_pcm_runtime_t *runtime = subs->runtime; + if (runtime->dma_area) { + vfree_nocheck(runtime->dma_area); + runtime->dma_area = NULL; + } + return 0; +} + +/* + * clear the SRAM contents + */ +static int pdacf_pcm_clear_sram(pdacf_t *chip) +{ + int max_loop = 64 * 1024; + + while (inw(chip->port + PDAUDIOCF_REG_RDP) != inw(chip->port + PDAUDIOCF_REG_WDP)) { + if (max_loop-- < 0) + return -EIO; + inw(chip->port + PDAUDIOCF_REG_MD); + } + return 0; +} + +/* + * pdacf_pcm_trigger - trigger callback for capture + */ +static int pdacf_pcm_trigger(snd_pcm_substream_t *subs, int cmd) +{ + pdacf_t *chip = snd_pcm_substream_chip(subs); + snd_pcm_runtime_t *runtime = subs->runtime; + int inc, ret = 0, rate; + unsigned short mask, val, tmp; + + if (chip->chip_status & PDAUDIOCF_STAT_IS_STALE) + return -EBUSY; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + chip->pcm_hwptr = 0; + chip->pcm_tdone = 0; + /* fall thru */ + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + mask = 0; + val = PDAUDIOCF_RECORD; + inc = 1; + rate = snd_ak4117_check_rate_and_errors(chip->ak4117, AK4117_CHECK_NO_STAT|AK4117_CHECK_NO_RATE); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + mask = PDAUDIOCF_RECORD; + val = 0; + inc = -1; + rate = 0; + break; + default: + return -EINVAL; + } + spin_lock(&chip->reg_lock); + chip->pcm_running += inc; + tmp = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR); + if (chip->pcm_running) { + if ((chip->ak4117->rcs0 & AK4117_UNLCK) || runtime->rate != rate) { + chip->pcm_running -= inc; + ret = -EIO; + goto __end; + } + } + tmp &= ~mask; + tmp |= val; + pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, tmp); + __end: + spin_unlock(&chip->reg_lock); + snd_ak4117_check_rate_and_errors(chip->ak4117, AK4117_CHECK_NO_RATE); + return ret; +} + +/* + * pdacf_pcm_hw_params - hw_params callback for playback and capture + */ +static int pdacf_pcm_hw_params(snd_pcm_substream_t *subs, + snd_pcm_hw_params_t *hw_params) +{ + return snd_pcm_alloc_vmalloc_buffer(subs, params_buffer_bytes(hw_params)); +} + +/* + * pdacf_pcm_hw_free - hw_free callback for playback and capture + */ +static int pdacf_pcm_hw_free(snd_pcm_substream_t *subs) +{ + return snd_pcm_free_vmalloc_buffer(subs); +} + +/* + * pdacf_pcm_prepare - prepare callback for playback and capture + */ +static int pdacf_pcm_prepare(snd_pcm_substream_t *subs) +{ + pdacf_t *chip = snd_pcm_substream_chip(subs); + snd_pcm_runtime_t *runtime = subs->runtime; + u16 val, nval, aval; + + if (chip->chip_status & PDAUDIOCF_STAT_IS_STALE) + return -EBUSY; + + chip->pcm_channels = runtime->channels; + + chip->pcm_little = snd_pcm_format_little_endian(runtime->format) > 0; +#ifdef SNDRV_LITTLE_ENDIAN + chip->pcm_swab = snd_pcm_format_big_endian(runtime->format) > 0; +#else + chip->pcm_swab = chip->pcm_little; +#endif + + if (snd_pcm_format_unsigned(runtime->format)) + chip->pcm_xor = 0x80008000; + + if (pdacf_pcm_clear_sram(chip) < 0) + return -EIO; + + val = nval = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR); + nval &= ~(PDAUDIOCF_DATAFMT0|PDAUDIOCF_DATAFMT1); + switch (runtime->format) { + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_S16_BE: + break; + default: /* 24-bit */ + nval |= PDAUDIOCF_DATAFMT0 | PDAUDIOCF_DATAFMT1; + break; + } + aval = 0; + chip->pcm_sample = 4; + switch (runtime->format) { + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_S16_BE: + aval = AK4117_DIF_16R; + chip->pcm_frame = 2; + chip->pcm_sample = 2; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + case SNDRV_PCM_FORMAT_S24_3BE: + chip->pcm_sample = 3; + /* fall trough */ + default: /* 24-bit */ + aval = AK4117_DIF_24R; + chip->pcm_frame = 3; + chip->pcm_xor &= 0xffff0000; + break; + } + + if (val != nval) { + snd_ak4117_reg_write(chip->ak4117, AK4117_REG_IO, AK4117_DIF2|AK4117_DIF1|AK4117_DIF0, aval); + pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, nval); + } + + val = pdacf_reg_read(chip, PDAUDIOCF_REG_IER); + val &= ~(PDAUDIOCF_IRQLVLEN1); + val |= PDAUDIOCF_IRQLVLEN0; + pdacf_reg_write(chip, PDAUDIOCF_REG_IER, val); + + chip->pcm_size = runtime->buffer_size; + chip->pcm_period = runtime->period_size; + chip->pcm_area = runtime->dma_area; + + return 0; +} + + +/* + * capture hw information + */ + +static snd_pcm_hardware_t pdacf_pcm_capture_hw = { + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE, + .rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, + .rate_min = 32000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (512*1024), + .period_bytes_min = 8*1024, + .period_bytes_max = (64*1024), + .periods_min = 2, + .periods_max = 128, + .fifo_size = 0, +}; + + +/* + * pdacf_pcm_capture_open - open callback for capture + */ +static int pdacf_pcm_capture_open(snd_pcm_substream_t *subs) +{ + snd_pcm_runtime_t *runtime = subs->runtime; + pdacf_t *chip = snd_pcm_substream_chip(subs); + + if (chip->chip_status & PDAUDIOCF_STAT_IS_STALE) + return -EBUSY; + + runtime->hw = pdacf_pcm_capture_hw; + runtime->private_data = chip; + chip->pcm_substream = subs; + + return 0; +} + +/* + * pdacf_pcm_capture_close - close callback for capture + */ +static int pdacf_pcm_capture_close(snd_pcm_substream_t *subs) +{ + pdacf_t *chip = snd_pcm_substream_chip(subs); + + if (!chip) + return -EINVAL; + pdacf_reinit(chip, 0); + chip->pcm_substream = NULL; + return 0; +} + + +/* + * pdacf_pcm_capture_pointer - pointer callback for capture + */ +static snd_pcm_uframes_t pdacf_pcm_capture_pointer(snd_pcm_substream_t *subs) +{ + pdacf_t *chip = snd_pcm_substream_chip(subs); + return chip->pcm_hwptr; +} + +/* + * operators for PCM capture + */ +static snd_pcm_ops_t pdacf_pcm_capture_ops = { + .open = pdacf_pcm_capture_open, + .close = pdacf_pcm_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pdacf_pcm_hw_params, + .hw_free = pdacf_pcm_hw_free, + .prepare = pdacf_pcm_prepare, + .trigger = pdacf_pcm_trigger, + .pointer = pdacf_pcm_capture_pointer, + .page = snd_pcm_get_vmalloc_page, +}; + + +/* + * free callback for pcm + */ +static void snd_pdacf_pcm_free(snd_pcm_t *pcm) +{ + pdacf_t *chip = snd_magic_cast(pdacf_t, pcm->private_data, return); + chip->pcm = NULL; +} + +/* + * snd_pdacf_pcm_new - create and initialize a pcm + */ +int snd_pdacf_pcm_new(pdacf_t *chip) +{ + snd_pcm_t *pcm; + int err; + + err = snd_pcm_new(chip->card, "PDAudioCF", 0, 0, 1, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pdacf_pcm_capture_ops); + + pcm->private_data = chip; + pcm->private_free = snd_pdacf_pcm_free; + pcm->info_flags = 0; + strcpy(pcm->name, chip->card->shortname); + chip->pcm = pcm; + + err = snd_ak4117_build(chip->ak4117, pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream); + if (err < 0) + return err; + + return 0; +} --- linux-2.6.4-rc2/sound/ppc/Kconfig 2003-06-14 12:18:08.000000000 -0700 +++ 25/sound/ppc/Kconfig 2004-03-07 20:46:51.000000000 -0800 @@ -6,6 +6,7 @@ menu "ALSA PowerMac devices" config SND_POWERMAC tristate "PowerMac (AWACS, DACA, Burgundy, Tumbler, Keywest)" depends on SND + select SND_PCM endmenu --- linux-2.6.4-rc2/sound/ppc/pmac.c 2003-09-27 18:57:48.000000000 -0700 +++ 25/sound/ppc/pmac.c 2004-03-07 20:46:51.000000000 -0800 @@ -664,7 +664,9 @@ int __init snd_pmac_pcm_new(pmac_t *chip chip->capture.cur_freqs = chip->freqs_ok; /* preallocate 64k buffer */ - snd_pcm_lib_preallocate_pages_for_all(pcm, 64 * 1024, 64 * 1024, GFP_KERNEL); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, + snd_pcm_dma_flags(GFP_KERNEL), + 64 * 1024, 64 * 1024); return 0; } --- linux-2.6.4-rc2/sound/ppc/tumbler.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/ppc/tumbler.c 2004-03-07 20:46:51.000000000 -0800 @@ -136,7 +136,7 @@ static int snapper_init_client(pmac_keyw /* normal operation, all-pass mode */ TAS_REG_MCS2, (1<<1), /* normal output, no deemphasis, A input, power-up */ - TAS_REG_ACS, 0, + TAS_REG_ACS, 2, 0, /* terminator */ }; return send_init_client(i2c, regs); @@ -929,8 +929,8 @@ static void tumbler_resume(pmac_t *chip) snapper_set_mix_vol(mix, VOL_IDX_PCM); snapper_set_mix_vol(mix, VOL_IDX_PCM2); snapper_set_mix_vol(mix, VOL_IDX_ADC); - tumbler_set_mono_volume(mix, &tumbler_bass_vol_info); - tumbler_set_mono_volume(mix, &tumbler_treble_vol_info); + tumbler_set_mono_volume(mix, &snapper_bass_vol_info); + tumbler_set_mono_volume(mix, &snapper_treble_vol_info); snapper_set_drc(mix); } tumbler_set_master_volume(mix); --- linux-2.6.4-rc2/sound/sparc/amd7930.c 2003-08-22 19:23:43.000000000 -0700 +++ 25/sound/sparc/amd7930.c 2004-03-07 20:46:51.000000000 -0800 @@ -791,7 +791,9 @@ static int __init snd_amd7930_pcm(amd793 strcpy(pcm->name, amd->card->shortname); amd->pcm = pcm; - snd_pcm_lib_preallocate_pages_for_all(pcm, 64*1024, 64*1024, GFP_KERNEL); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), + 64*1024, 64*1024); return 0; } --- linux-2.6.4-rc2/sound/sparc/cs4231.c 2003-08-22 19:23:43.000000000 -0700 +++ 25/sound/sparc/cs4231.c 2004-03-07 20:46:51.000000000 -0800 @@ -1570,13 +1570,15 @@ int snd_cs4231_pcm(cs4231_t *chip) #ifdef EBUS_SUPPORT if (chip->flags & CS4231_FLAG_EBUS) { - snd_pcm_lib_preallocate_pci_pages_for_all(chip->dev_u.pdev, pcm, - 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_PCI, + snd_dma_pci_data(chip->dev_u.pdev) + 64*1024, 128*1024); } else { #endif #ifdef SBUS_SUPPORT - snd_pcm_lib_preallocate_sbus_pages_for_all(chip->dev_u.sdev, pcm, - 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_SBUS, + snd_dma_sbus_data(chip->dev_u.sdev), + 64*1024, 128*1024); #endif #ifdef EBUS_SUPPORT } --- linux-2.6.4-rc2/sound/sparc/Kconfig 2003-06-14 12:18:29.000000000 -0700 +++ 25/sound/sparc/Kconfig 2004-03-07 20:46:51.000000000 -0800 @@ -6,11 +6,13 @@ menu "ALSA Sparc devices" config SND_SUN_AMD7930 tristate "Sun AMD7930" depends on SBUS && SND + select SND_PCM # dep_tristate 'Sun DBRI' CONFIG_SND_SUN_DBRI $CONFIG_SND config SND_SUN_CS4231 tristate "Sun CS4231" depends on SND + select SND_PCM endmenu --- linux-2.6.4-rc2/sound/synth/Makefile 2003-06-14 12:17:56.000000000 -0700 +++ 25/sound/synth/Makefile 2004-03-07 20:46:51.000000000 -0800 @@ -5,10 +5,16 @@ snd-util-mem-objs := util_mem.o +# +# this function returns: +# "m" - CONFIG_SND_SEQUENCER is m +# - CONFIG_SND_SEQUENCER is undefined +# otherwise parameter #1 value +# +sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1))) + # Toplevel Module Dependency obj-$(CONFIG_SND_EMU10K1) += snd-util-mem.o obj-$(CONFIG_SND_TRIDENT) += snd-util-mem.o -ifdef CONFIG_SND_SEQUENCER - obj-$(CONFIG_SND_SBAWE) += snd-util-mem.o - obj-$(CONFIG_SND) += emux/ -endif +obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-util-mem.o +obj-$(call sequencer,$(CONFIG_SND)) += emux/ --- linux-2.6.4-rc2/sound/usb/Kconfig 2003-06-14 12:18:29.000000000 -0700 +++ 25/sound/usb/Kconfig 2004-03-07 20:46:51.000000000 -0800 @@ -6,6 +6,8 @@ menu "ALSA USB devices" config SND_USB_AUDIO tristate "USB Audio/MIDI driver" depends on SND && USB + select SND_RAWMIDI + select SND_PCM help Say 'Y' or 'M' to include support for USB audio and USB MIDI devices. --- linux-2.6.4-rc2/sound/usb/usbaudio.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/usb/usbaudio.c 2004-03-07 20:46:51.000000000 -0800 @@ -45,7 +45,6 @@ #include #include #include -#include #include #include #include @@ -117,7 +116,7 @@ struct audioformat { struct list_head list; snd_pcm_format_t format; /* format type */ unsigned int channels; /* # channels */ - unsigned int nonaudio: 1; /* non-audio (type II) */ + unsigned int fmt_type; /* USB audio format type (1-3) */ unsigned int frame_size; /* samples per frame for non-audio */ int iface; /* interface number */ unsigned char altsetting; /* corresponding alternate setting */ @@ -171,7 +170,7 @@ struct snd_usb_substream { unsigned int curpacksize; /* current packet size in bytes (for capture) */ unsigned int curframesize; /* current packet size in frames (for capture) */ unsigned int fill_max: 1; /* fill max packet size always */ - unsigned int nonaudio: 1; /* Type II format (MPEG, AC3) */ + unsigned int fmt_type; /* USB audio format type (1-3) */ unsigned int running: 1; /* running status */ @@ -201,6 +200,7 @@ struct snd_usb_stream { snd_usb_audio_t *chip; snd_pcm_t *pcm; int pcm_index; + unsigned int fmt_type; /* USB audio format type (1-3) */ snd_usb_substream_t substream[2]; struct list_head list; }; @@ -477,7 +477,7 @@ static int prepare_playback_urb(snd_usb_ subs->transfer_sched += counts; if (subs->transfer_sched >= runtime->period_size) { subs->transfer_sched -= runtime->period_size; - if (subs->nonaudio) { + if (subs->fmt_type == USB_FORMAT_TYPE_II) { if (subs->transfer_sched > 0) { /* FIXME: fill-max mode is not supported yet */ offs -= subs->transfer_sched; @@ -894,7 +894,7 @@ static int init_substream_urbs(snd_usb_s u->subs = subs; u->transfer = 0; u->packets = npacks[i]; - if (subs->nonaudio) + if (subs->fmt_type == USB_FORMAT_TYPE_II) u->packets++; /* for transfer delimiter */ if (! is_playback) { /* allocate a capture buffer per urb */ @@ -1129,15 +1129,20 @@ static int set_format(snd_usb_substream_ (! is_playback && attr == EP_ATTR_ADAPTIVE)) && altsd->bNumEndpoints >= 2) { /* check sync-pipe endpoint */ - if (get_endpoint(alts, 1)->bmAttributes != 0x01 || - get_endpoint(alts, 1)->bSynchAddress != 0) { + /* ... and check descriptor size before accessing bSynchAddress + because there is a version of the SB Audigy 2 NX firmware lacking + the audio fields in the endpoint descriptors */ + if ((get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != 0x01 || + (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE && + get_endpoint(alts, 1)->bSynchAddress != 0)) { snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n", dev->devnum, fmt->iface, fmt->altsetting); return -EINVAL; } ep = get_endpoint(alts, 1)->bEndpointAddress; - if ((is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) || - (! is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN))) { + if (get_endpoint(alts, 0)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE && + (( is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) || + (!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)))) { snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n", dev->devnum, fmt->iface, fmt->altsetting); return -EINVAL; @@ -1588,7 +1593,7 @@ static int setup_hw_info(snd_pcm_runtime runtime->hw.channels_min = fp->channels; if (runtime->hw.channels_max < fp->channels) runtime->hw.channels_max = fp->channels; - if (fp->nonaudio && fp->frame_size > 0) { + if (fp->fmt_type == USB_FORMAT_TYPE_II && fp->frame_size > 0) { /* FIXME: there might be more than one audio formats... */ runtime->hw.period_bytes_min = runtime->hw.period_bytes_max = fp->frame_size; @@ -1886,7 +1891,9 @@ static void init_substream(snd_usb_strea subs->dev = as->chip->dev; subs->ops = audio_urb_ops[stream]; snd_pcm_lib_preallocate_pages(as->pcm->streams[stream].substream, - 64 * 1024, 128 * 1024, GFP_ATOMIC); + SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), + 64 * 1024, 128 * 1024); snd_pcm_set_ops(as->pcm, stream, stream == SNDRV_PCM_STREAM_PLAYBACK ? &snd_usb_playback_ops : &snd_usb_capture_ops); @@ -1895,7 +1902,7 @@ static void init_substream(snd_usb_strea subs->formats |= 1ULL << fp->format; subs->endpoint = fp->endpoint; subs->num_formats++; - subs->nonaudio = fp->nonaudio; + subs->fmt_type = fp->fmt_type; } @@ -1954,17 +1961,12 @@ static int add_audio_endpoint(snd_usb_au list_for_each(p, &chip->pcm_list) { as = list_entry(p, snd_usb_stream_t, list); + if (as->fmt_type != fp->fmt_type) + continue; subs = &as->substream[stream]; if (! subs->endpoint) - break; + continue; if (subs->endpoint == fp->endpoint) { - if (fp->nonaudio) { - if (!subs->nonaudio || subs->formats != (1ULL << fp->format)) - continue; /* non-linear formats are handled exclusively */ - } else { - if (subs->nonaudio) - continue; - } list_add_tail(&fp->list, &subs->fmt_list); subs->num_formats++; subs->formats |= 1ULL << fp->format; @@ -1974,6 +1976,8 @@ static int add_audio_endpoint(snd_usb_au /* look for an empty stream */ list_for_each(p, &chip->pcm_list) { as = list_entry(p, snd_usb_stream_t, list); + if (as->fmt_type != fp->fmt_type) + continue; subs = &as->substream[stream]; if (subs->endpoint) continue; @@ -1991,6 +1995,7 @@ static int add_audio_endpoint(snd_usb_au memset(as, 0, sizeof(*as)); as->pcm_index = chip->pcm_devs; as->chip = chip; + as->fmt_type = fp->fmt_type; err = snd_pcm_new(chip->card, "USB Audio", chip->pcm_devs, stream == SNDRV_PCM_STREAM_PLAYBACK ? 1 : 0, stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1, @@ -2216,7 +2221,6 @@ static int parse_audio_format_ii(struct break; } fp->channels = 1; - fp->nonaudio = 1; brate = combine_word(&fmt[4]); /* fmt[4,5] : wMaxBitRate (in kbps) */ framesize = combine_word(&fmt[6]); /* fmt[6,7]: wSamplesPerFrame */ snd_printd(KERN_INFO "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize); @@ -2242,6 +2246,7 @@ static int parse_audio_format(struct usb dev->devnum, fp->iface, fp->altsetting, fmt[3]); return -1; } + fp->fmt_type = fmt[3]; if (err < 0) return err; #if 1 @@ -2326,6 +2331,9 @@ static int parse_audio_endpoints(snd_usb } csep = snd_usb_find_desc(alts->endpoint[0].extra, alts->endpoint[0].extralen, NULL, USB_DT_CS_ENDPOINT); + /* Creamware Noah has this descriptor after the 2nd endpoint */ + if (!csep && altsd->bNumEndpoints >= 2) + csep = snd_usb_find_desc(alts->endpoint[1].extra, alts->endpoint[1].extralen, NULL, USB_DT_CS_ENDPOINT); if (!csep || csep[0] < 7 || csep[2] != EP_GENERAL) { snd_printk(KERN_ERR "%d:%u:%d : no or invalid class specific endpoint descriptor\n", dev->devnum, iface_no, altno); --- linux-2.6.4-rc2/sound/usb/usbmidi.c 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/usb/usbmidi.c 2004-03-07 20:46:51.000000000 -0800 @@ -1,7 +1,7 @@ /* * usbmidi.c - ALSA USB MIDI driver * - * Copyright (c) 2002 Clemens Ladisch + * Copyright (c) 2002-2004 Clemens Ladisch * All rights reserved. * * Based on the OSS usb-midi driver by NAGANO Daisuke, @@ -727,18 +727,118 @@ static snd_rawmidi_substream_t* snd_usbm return NULL; } +/* + * This list specifies names for ports that do not fit into the standard + * "(product) MIDI (n)" schema because they aren't external MIDI ports, + * such as internal control or synthesizer ports. + */ +static struct { + __u16 vendor; + __u16 product; + int port; + const char *name_format; +} snd_usbmidi_port_names[] = { + /* Roland UA-100 */ + {0x0582, 0x0000, 2, "%s Control"}, + /* Roland SC-8850 */ + {0x0582, 0x0003, 0, "%s Part A"}, + {0x0582, 0x0003, 1, "%s Part B"}, + {0x0582, 0x0003, 2, "%s Part C"}, + {0x0582, 0x0003, 3, "%s Part D"}, + {0x0582, 0x0003, 4, "%s MIDI 1"}, + {0x0582, 0x0003, 5, "%s MIDI 2"}, + /* Roland U-8 */ + {0x0582, 0x0004, 0, "%s MIDI"}, + {0x0582, 0x0004, 1, "%s Control"}, + /* Roland SC-8820 */ + {0x0582, 0x0007, 0, "%s Part A"}, + {0x0582, 0x0007, 1, "%s Part B"}, + {0x0582, 0x0007, 2, "%s MIDI"}, + /* Roland SK-500 */ + {0x0582, 0x000b, 0, "%s Part A"}, + {0x0582, 0x000b, 1, "%s Part B"}, + {0x0582, 0x000b, 2, "%s MIDI"}, + /* Roland SC-D70 */ + {0x0582, 0x000c, 0, "%s Part A"}, + {0x0582, 0x000c, 1, "%s Part B"}, + {0x0582, 0x000c, 2, "%s MIDI"}, + /* Edirol UM-880 */ + {0x0582, 0x0014, 8, "%s Control"}, + /* Edirol SD-90 */ + {0x0582, 0x0016, 0, "%s Part A"}, + {0x0582, 0x0016, 1, "%s Part B"}, + {0x0582, 0x0016, 2, "%s MIDI 1"}, + {0x0582, 0x0016, 3, "%s MIDI 2"}, + /* Edirol UM-550 */ + {0x0582, 0x0023, 5, "%s Control"}, + /* Edirol SD-20 */ + {0x0582, 0x0027, 0, "%s Part A"}, + {0x0582, 0x0027, 1, "%s Part B"}, + {0x0582, 0x0027, 2, "%s MIDI"}, + /* Edirol SD-80 */ + {0x0582, 0x0029, 0, "%s Part A"}, + {0x0582, 0x0029, 1, "%s Part B"}, + {0x0582, 0x0029, 2, "%s MIDI 1"}, + {0x0582, 0x0029, 3, "%s MIDI 2"}, + /* Edirol UA-700 */ + {0x0582, 0x002b, 0, "%s MIDI"}, + {0x0582, 0x002b, 1, "%s Control"}, + /* Roland VariOS */ + {0x0582, 0x002f, 0, "%s MIDI"}, + {0x0582, 0x002f, 1, "%s External MIDI"}, + {0x0582, 0x002f, 2, "%s Sync"}, + /* Edirol PCR */ + {0x0582, 0x0033, 0, "%s MIDI"}, + {0x0582, 0x0033, 1, "%s 1"}, + {0x0582, 0x0033, 2, "%s 2"}, + /* BOSS GS-10 */ + {0x0582, 0x003b, 0, "%s MIDI"}, + {0x0582, 0x003b, 1, "%s Control"}, + /* Edirol UA-1000 */ + {0x0582, 0x0044, 0, "%s MIDI"}, + {0x0582, 0x0044, 1, "%s Control"}, + /* Edirol UR-80 */ + {0x0582, 0x0048, 0, "%s MIDI"}, + {0x0582, 0x0048, 1, "%s 1"}, + {0x0582, 0x0048, 2, "%s 2"}, + /* Edirol PCR-A */ + {0x0582, 0x004d, 0, "%s MIDI"}, + {0x0582, 0x004d, 1, "%s 1"}, + {0x0582, 0x004d, 2, "%s 2"}, + /* M-Audio MidiSport 8x8 */ + {0x0763, 0x1031, 8, "%s Control"}, + {0x0763, 0x1033, 8, "%s Control"}, +}; + static void snd_usbmidi_init_substream(snd_usb_midi_t* umidi, int stream, int number, snd_rawmidi_substream_t** rsubstream) { + int i; + __u16 vendor, product; + const char *name_format; + snd_rawmidi_substream_t* substream = snd_usbmidi_find_substream(umidi, stream, number); if (!substream) { snd_printd(KERN_ERR "substream %d:%d not found\n", stream, number); return; } + /* TODO: read port name from jack descriptor */ + name_format = "%s MIDI %d"; + vendor = umidi->chip->dev->descriptor.idVendor; + product = umidi->chip->dev->descriptor.idProduct; + for (i = 0; i < ARRAY_SIZE(snd_usbmidi_port_names); ++i) { + if (snd_usbmidi_port_names[i].vendor == vendor && + snd_usbmidi_port_names[i].product == product && + snd_usbmidi_port_names[i].port == number) { + name_format = snd_usbmidi_port_names[i].name_format; + break; + } + } snprintf(substream->name, sizeof(substream->name), - "%s Port %d", umidi->chip->card->shortname, number); + name_format, umidi->chip->card->shortname, number + 1); + *rsubstream = substream; } --- linux-2.6.4-rc2/sound/usb/usbquirks.h 2004-02-17 20:48:47.000000000 -0800 +++ 25/sound/usb/usbquirks.h 2004-03-07 20:46:51.000000000 -0800 @@ -104,7 +104,7 @@ YAMAHA_DEVICE(0x5008, "01V96"), #undef YAMAHA_INTERFACE /* - * Roland/RolandED/Edirol devices + * Roland/RolandED/Edirol/BOSS devices */ { USB_DEVICE(0x0582, 0x0000), @@ -196,8 +196,8 @@ YAMAHA_DEVICE(0x5008, "01V96"), .ifnum = 2, .type = QUIRK_MIDI_FIXED_ENDPOINT, .data = & (const snd_usb_midi_endpoint_info_t) { - .out_cables = 0x0003, - .in_cables = 0x0003 + .out_cables = 0x0005, + .in_cables = 0x0005 } } }, @@ -393,6 +393,32 @@ YAMAHA_DEVICE(0x5008, "01V96"), } }, { + USB_DEVICE(0x0582, 0x001b), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Roland", + .product_name = "MMP-2", + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + } +}, +{ + USB_DEVICE(0x0582, 0x001d), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Roland", + .product_name = "V-SYNTH", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + } +}, +{ USB_DEVICE(0x0582, 0x0023), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "EDIROL", @@ -489,6 +515,19 @@ YAMAHA_DEVICE(0x5008, "01V96"), } }, { + USB_DEVICE(0x0582, 0x002f), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Roland", + .product_name = "VariOS", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0007, + .in_cables = 0x0007 + } + } +}, +{ USB_DEVICE(0x0582, 0x0033), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "EDIROL", @@ -502,6 +541,110 @@ YAMAHA_DEVICE(0x5008, "01V96"), } }, { + USB_DEVICE(0x0582, 0x0037), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Roland", + .product_name = "Digital Piano", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + } +}, +{ + USB_DEVICE_VENDOR_SPEC(0x0582, 0x003b), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "BOSS", + .product_name = "GS-10", + .ifnum = 3, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0003, + .in_cables = 0x0003 + } + } +}, +{ + USB_DEVICE(0x0582, 0x0040), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Roland", + .product_name = "GI-20", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + } +}, +{ + USB_DEVICE(0x0582, 0x0048), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "EDIROL", + .product_name = "UR-80", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0003, + .in_cables = 0x0007 + } + } +}, +{ + USB_DEVICE(0x0582, 0x004d), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "EDIROL", + .product_name = "PCR-A", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0003, + .in_cables = 0x0007 + } + } +}, +{ + USB_DEVICE(0x0582, 0x0065), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "EDIROL", + .product_name = "PCR-1", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0001, + .in_cables = 0x0003 + } + } +}, +{ + /* + * This quirk is for the "Advanced Driver" mode. If off, the UA-3FX + * is standard compliant, but has only 16-bit PCM. + */ + USB_DEVICE(0x0582, 0x0050), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "EDIROL", + .product_name = "UA-3FX", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = & (const snd_usb_audio_quirk_t[]) { + { + .ifnum = 1, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = -1 + } + } + } +}, +{ USB_DEVICE(0x0582, 0x0052), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "EDIROL", @@ -688,4 +831,14 @@ YAMAHA_DEVICE(0x5008, "01V96"), }, +{ + USB_DEVICE_VENDOR_SPEC(0x0ccd, 0x0013), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Terratec", + .product_name = "PHASE 26", + .ifnum = 3, + .type = QUIRK_MIDI_STANDARD_INTERFACE + } +}, + #undef USB_DEVICE_VENDOR_SPEC