diff --git a/sys-kernel/linux-sabayon/Manifest b/sys-kernel/linux-sabayon/Manifest index 52484201e..6941c8489 100644 --- a/sys-kernel/linux-sabayon/Manifest +++ b/sys-kernel/linux-sabayon/Manifest @@ -42,6 +42,22 @@ AUX 2.6.23/mactel-patches-2.6.23.patch 26289 RMD160 59e2d43eac3a0178bf85293046a1 MD5 4be03a2c74f6333cd5c8fdaa09007ef0 files/2.6.23/mactel-patches-2.6.23.patch 26289 RMD160 59e2d43eac3a0178bf85293046a1a1ed80062fbc files/2.6.23/mactel-patches-2.6.23.patch 26289 SHA256 dbe35b10ac9c4a2675b2dab7836f456ae994dd36990b101511cae6dbc7e4165b files/2.6.23/mactel-patches-2.6.23.patch 26289 +AUX 2.6.23/patch-2.6.23.12-13 2311 RMD160 396fa408ae86a748d264b7eaae715436d6d6af48 SHA1 b379b8903fa0e77d2b289891b01912245de086ed SHA256 7277f47c713c190f7f7d1dd615ac632e94bfc0ebf2d0bc2f4e4736d677836729 +MD5 5b6494c7f2b47aaa661ea398f0508da5 files/2.6.23/patch-2.6.23.12-13 2311 +RMD160 396fa408ae86a748d264b7eaae715436d6d6af48 files/2.6.23/patch-2.6.23.12-13 2311 +SHA256 7277f47c713c190f7f7d1dd615ac632e94bfc0ebf2d0bc2f4e4736d677836729 files/2.6.23/patch-2.6.23.12-13 2311 +AUX 2.6.23/patch-2.6.23.13-14 983 RMD160 447ec26a4d4fbf28f55814652035a031348e8df4 SHA1 ff8ded41a587ed170967f136cb4259f2537b4e64 SHA256 50cc0cc7792eb867ad89a48339ff651e2e4c7af9f366dc5fef3453b84a46e8ed +MD5 342372f79332442a854c4915caa36fc2 files/2.6.23/patch-2.6.23.13-14 983 +RMD160 447ec26a4d4fbf28f55814652035a031348e8df4 files/2.6.23/patch-2.6.23.13-14 983 +SHA256 50cc0cc7792eb867ad89a48339ff651e2e4c7af9f366dc5fef3453b84a46e8ed files/2.6.23/patch-2.6.23.13-14 983 +AUX 2.6.23/patch-2.6.23.14-15 263270 RMD160 c13b4094558ac573b247393417ae5553779813ec SHA1 ab8f0e0e9a04b83e2c6804f497a5aaf2253ae867 SHA256 5e4a2037787d32b83b30a8ba8264095c7e07d5baf8794597112e35fb383640f4 +MD5 79d8acc72f533dee2b313478e46ee9b8 files/2.6.23/patch-2.6.23.14-15 263270 +RMD160 c13b4094558ac573b247393417ae5553779813ec files/2.6.23/patch-2.6.23.14-15 263270 +SHA256 5e4a2037787d32b83b30a8ba8264095c7e07d5baf8794597112e35fb383640f4 files/2.6.23/patch-2.6.23.14-15 263270 +AUX 2.6.23/patch-2.6.23.15-16 583 RMD160 9d8136d234bc9fcdce76a622108a358fa88a51bb SHA1 1beb54cd9d5aa9687e87810d43d9d883438afe48 SHA256 bc4cc7a2dd997545a554c66be39e45b4e690df181c585245f07560901d1e9142 +MD5 0152e440d8184cf425568e464e67b079 files/2.6.23/patch-2.6.23.15-16 583 +RMD160 9d8136d234bc9fcdce76a622108a358fa88a51bb files/2.6.23/patch-2.6.23.15-16 583 +SHA256 bc4cc7a2dd997545a554c66be39e45b4e690df181c585245f07560901d1e9142 files/2.6.23/patch-2.6.23.15-16 583 AUX 2.6.23/pm_qos-2.6.23.patch 33320 RMD160 7dd597f8c0bff1469c67d4ad089d1d2b89e7e6e6 SHA1 d6528353d1b5d8cf3b93a1e7b7e89cadb21cb245 SHA256 7d47838a9e41c46c3d451c39d224cf0d06114474151b5bb769b0a164f50535a7 MD5 e73600df64adedefa126efa7e3379613 files/2.6.23/pm_qos-2.6.23.patch 33320 RMD160 7dd597f8c0bff1469c67d4ad089d1d2b89e7e6e6 files/2.6.23/pm_qos-2.6.23.patch 33320 @@ -154,10 +170,10 @@ DIST tuxonice-3.0-rc3-for-2.6.23.9.patch.bz2 111146 RMD160 6a44034aa195e941483f6 DIST tuxonice-3.0-rc5-for-2.6.24.patch.bz2 113258 RMD160 c1fde23de970b6347302ae2bff71bf3ab1bfbeb3 SHA1 bf559c8dead1d0372d3388e4f9d2c4d145862ec1 SHA256 0a3b502b89bebbcd0f701a0e89c072797257ce766b2ea2d2bda1f6762ca2ce9b DIST unionfs-2.2.1_for_2.6.23.12.diff.gz 76680 RMD160 9ecdffbb576ce9b7fcce0a145c6c78dddfb74cfb SHA1 a972ec81097b0e4262d6cf596ff741e120811ff5 SHA256 1caa738ba1f6c6ac4185f60c4325d9bad3990690b8476b6145c4704d04a2b5e9 DIST unionfs-2.2.3_for_2.6.24.diff.gz 76789 RMD160 c213efff91016cd1d7660913e791cf7b2dd6ea95 SHA1 a6d8fb804a7ffd2d94a4356c76c7d08d4d8798d7 SHA256 7bd36244b205f46452b577e79f77ed90b30cd6a5801e36fac57b696573ade513 -EBUILD linux-sabayon-2.6.23-r1.ebuild 4961 RMD160 b0ad3fcb9473b3b2f41edd51448304ebf0fb8259 SHA1 93083381b07c7e62c2ef0c607312356f30c7fde6 SHA256 6adae6d26a8ad9fd5151c6544d6d3deb8461affc35d665e64034902c603471f3 -MD5 9bfdc4dbf1045806b7c802c6c3d20016 linux-sabayon-2.6.23-r1.ebuild 4961 -RMD160 b0ad3fcb9473b3b2f41edd51448304ebf0fb8259 linux-sabayon-2.6.23-r1.ebuild 4961 -SHA256 6adae6d26a8ad9fd5151c6544d6d3deb8461affc35d665e64034902c603471f3 linux-sabayon-2.6.23-r1.ebuild 4961 +EBUILD linux-sabayon-2.6.23-r1.ebuild 5118 RMD160 09fb8743b9f40d3a34fe69d893406499e629e159 SHA1 5d5b6882cc15ebffa0e6ef83d1361a7bc0931fcc SHA256 91845f515bbe88f1b13e20262eea96072fc58a0108ea6a7141b25a5de8a6730b +MD5 d0efbf11afc605def0746befa22efe0c linux-sabayon-2.6.23-r1.ebuild 5118 +RMD160 09fb8743b9f40d3a34fe69d893406499e629e159 linux-sabayon-2.6.23-r1.ebuild 5118 +SHA256 91845f515bbe88f1b13e20262eea96072fc58a0108ea6a7141b25a5de8a6730b linux-sabayon-2.6.23-r1.ebuild 5118 EBUILD linux-sabayon-2.6.24.ebuild 4113 RMD160 5e29b3969984665025542bc8e7a7ec9f4f8da6b6 SHA1 00b12fd99c29acb3eda0ee7860a1e525acd87fe9 SHA256 a4bdf60fbbe206b03417a4f8b2feec1af0fb6020e48de9cf4cc5825256c23741 MD5 f8550ded82cb11bb0954882eaa98539a linux-sabayon-2.6.24.ebuild 4113 RMD160 5e29b3969984665025542bc8e7a7ec9f4f8da6b6 linux-sabayon-2.6.24.ebuild 4113 diff --git a/sys-kernel/linux-sabayon/files/2.6.23/patch-2.6.23.12-13 b/sys-kernel/linux-sabayon/files/2.6.23/patch-2.6.23.12-13 new file mode 100644 index 000000000..4d6206a51 --- /dev/null +++ b/sys-kernel/linux-sabayon/files/2.6.23/patch-2.6.23.12-13 @@ -0,0 +1,63 @@ +diff --git a/Makefile b/Makefile +index 96c3f02..55edf2f 100644 +--- a/Makefile ++++ b/Makefile +@@ -1,7 +1,7 @@ + VERSION = 2 + PATCHLEVEL = 6 + SUBLEVEL = 23 +-EXTRAVERSION = .12 ++EXTRAVERSION = .13 + NAME = Arr Matey! A Hairy Bilge Rat! + + # *DOCUMENTATION* +diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c +index d9a9ec7..bfd8828 100644 +--- a/drivers/hwmon/w83627ehf.c ++++ b/drivers/hwmon/w83627ehf.c +@@ -1276,23 +1276,31 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) + + data->vrm = vid_which_vrm(); + superio_enter(sio_data->sioreg); +- /* Set VID input sensibility if needed. In theory the BIOS should +- have set it, but in practice it's not always the case. */ +- en_vrm10 = superio_inb(sio_data->sioreg, SIO_REG_EN_VRM10); +- if ((en_vrm10 & 0x08) && data->vrm != 100) { +- dev_warn(dev, "Setting VID input voltage to TTL\n"); +- superio_outb(sio_data->sioreg, SIO_REG_EN_VRM10, +- en_vrm10 & ~0x08); +- } else if (!(en_vrm10 & 0x08) && data->vrm == 100) { +- dev_warn(dev, "Setting VID input voltage to VRM10\n"); +- superio_outb(sio_data->sioreg, SIO_REG_EN_VRM10, +- en_vrm10 | 0x08); +- } + /* Read VID value */ + superio_select(sio_data->sioreg, W83627EHF_LD_HWM); +- if (superio_inb(sio_data->sioreg, SIO_REG_VID_CTRL) & 0x80) ++ if (superio_inb(sio_data->sioreg, SIO_REG_VID_CTRL) & 0x80) { ++ /* Set VID input sensibility if needed. In theory the BIOS ++ should have set it, but in practice it's not always the ++ case. We only do it for the W83627EHF/EHG because the ++ W83627DHG is more complex in this respect. */ ++ if (sio_data->kind == w83627ehf) { ++ en_vrm10 = superio_inb(sio_data->sioreg, ++ SIO_REG_EN_VRM10); ++ if ((en_vrm10 & 0x08) && data->vrm == 90) { ++ dev_warn(dev, "Setting VID input voltage to " ++ "TTL\n"); ++ superio_outb(sio_data->sioreg, SIO_REG_EN_VRM10, ++ en_vrm10 & ~0x08); ++ } else if (!(en_vrm10 & 0x08) && data->vrm == 100) { ++ dev_warn(dev, "Setting VID input voltage to " ++ "VRM10\n"); ++ superio_outb(sio_data->sioreg, SIO_REG_EN_VRM10, ++ en_vrm10 | 0x08); ++ } ++ } ++ + data->vid = superio_inb(sio_data->sioreg, SIO_REG_VID_DATA) & 0x3f; +- else { ++ } else { + dev_info(dev, "VID pins in output mode, CPU VID not " + "available\n"); + data->vid = 0x3f; diff --git a/sys-kernel/linux-sabayon/files/2.6.23/patch-2.6.23.13-14 b/sys-kernel/linux-sabayon/files/2.6.23/patch-2.6.23.13-14 new file mode 100644 index 000000000..b790f43e0 --- /dev/null +++ b/sys-kernel/linux-sabayon/files/2.6.23/patch-2.6.23.13-14 @@ -0,0 +1,35 @@ +diff --git a/Makefile b/Makefile +index 55edf2f..5c31700 100644 +--- a/Makefile ++++ b/Makefile +@@ -1,7 +1,7 @@ + VERSION = 2 + PATCHLEVEL = 6 + SUBLEVEL = 23 +-EXTRAVERSION = .13 ++EXTRAVERSION = .14 + NAME = Arr Matey! A Hairy Bilge Rat! + + # *DOCUMENTATION* +diff --git a/fs/namei.c b/fs/namei.c +index a83160a..314afe6 100644 +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -1576,7 +1576,7 @@ int may_open(struct nameidata *nd, int acc_mode, int flag) + if (S_ISLNK(inode->i_mode)) + return -ELOOP; + +- if (S_ISDIR(inode->i_mode) && (flag & FMODE_WRITE)) ++ if (S_ISDIR(inode->i_mode) && (acc_mode & MAY_WRITE)) + return -EISDIR; + + error = vfs_permission(nd, acc_mode); +@@ -1595,7 +1595,7 @@ int may_open(struct nameidata *nd, int acc_mode, int flag) + return -EACCES; + + flag &= ~O_TRUNC; +- } else if (IS_RDONLY(inode) && (flag & FMODE_WRITE)) ++ } else if (IS_RDONLY(inode) && (acc_mode & MAY_WRITE)) + return -EROFS; + /* + * An append-only file must be opened in append mode for writing. diff --git a/sys-kernel/linux-sabayon/files/2.6.23/patch-2.6.23.14-15 b/sys-kernel/linux-sabayon/files/2.6.23/patch-2.6.23.14-15 new file mode 100644 index 000000000..97396aab2 --- /dev/null +++ b/sys-kernel/linux-sabayon/files/2.6.23/patch-2.6.23.14-15 @@ -0,0 +1,8722 @@ +diff --git a/Makefile b/Makefile +index 5c31700..8920cb8 100644 +--- a/Makefile ++++ b/Makefile +@@ -1,7 +1,7 @@ + VERSION = 2 + PATCHLEVEL = 6 + SUBLEVEL = 23 +-EXTRAVERSION = .14 ++EXTRAVERSION = .15 + NAME = Arr Matey! A Hairy Bilge Rat! + + # *DOCUMENTATION* +diff --git a/arch/i386/kernel/apm.c b/arch/i386/kernel/apm.c +index f02a8ac..14dc111 100644 +--- a/arch/i386/kernel/apm.c ++++ b/arch/i386/kernel/apm.c +@@ -2256,14 +2256,12 @@ static int __init apm_init(void) + apm_info.disabled = 1; + return -ENODEV; + } +- if (PM_IS_ACTIVE()) { ++ if (pm_flags & PM_ACPI) { + printk(KERN_NOTICE "apm: overridden by ACPI.\n"); + apm_info.disabled = 1; + return -ENODEV; + } +-#ifdef CONFIG_PM_LEGACY +- pm_active = 1; +-#endif ++ pm_flags |= PM_APM; + + /* + * Set up a segment that references the real mode segment 0x40 +@@ -2366,9 +2364,7 @@ static void __exit apm_exit(void) + kthread_stop(kapmd_task); + kapmd_task = NULL; + } +-#ifdef CONFIG_PM_LEGACY +- pm_active = 0; +-#endif ++ pm_flags &= ~PM_APM; + } + + module_init(apm_init); +diff --git a/arch/ia64/kernel/unaligned.c b/arch/ia64/kernel/unaligned.c +index fe6aa5a..ecf401b 100644 +--- a/arch/ia64/kernel/unaligned.c ++++ b/arch/ia64/kernel/unaligned.c +@@ -1487,16 +1487,19 @@ ia64_handle_unaligned (unsigned long ifa, struct pt_regs *regs) + case LDFA_OP: + case LDFCCLR_OP: + case LDFCNC_OP: +- case LDF_IMM_OP: +- case LDFA_IMM_OP: +- case LDFCCLR_IMM_OP: +- case LDFCNC_IMM_OP: + if (u.insn.x) + ret = emulate_load_floatpair(ifa, u.insn, regs); + else + ret = emulate_load_float(ifa, u.insn, regs); + break; + ++ case LDF_IMM_OP: ++ case LDFA_IMM_OP: ++ case LDFCCLR_IMM_OP: ++ case LDFCNC_IMM_OP: ++ ret = emulate_load_float(ifa, u.insn, regs); ++ break; ++ + case STF_OP: + case STF_IMM_OP: + ret = emulate_store_float(ifa, u.insn, regs); +diff --git a/arch/sparc64/kernel/chmc.c b/arch/sparc64/kernel/chmc.c +index 777d345..6d4f02e 100644 +--- a/arch/sparc64/kernel/chmc.c ++++ b/arch/sparc64/kernel/chmc.c +@@ -1,7 +1,6 @@ +-/* $Id: chmc.c,v 1.4 2002/01/08 16:00:14 davem Exp $ +- * memctrlr.c: Driver for UltraSPARC-III memory controller. ++/* memctrlr.c: Driver for UltraSPARC-III memory controller. + * +- * Copyright (C) 2001 David S. Miller (davem@redhat.com) ++ * Copyright (C) 2001, 2007 David S. Miller (davem@davemloft.net) + */ + + #include +@@ -16,6 +15,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -242,8 +242,11 @@ int chmc_getunumber(int syndrome_code, + */ + static u64 read_mcreg(struct mctrl_info *mp, unsigned long offset) + { +- unsigned long ret; +- int this_cpu = get_cpu(); ++ unsigned long ret, this_cpu; ++ ++ preempt_disable(); ++ ++ this_cpu = real_hard_smp_processor_id(); + + if (mp->portid == this_cpu) { + __asm__ __volatile__("ldxa [%1] %2, %0" +@@ -255,7 +258,8 @@ static u64 read_mcreg(struct mctrl_info *mp, unsigned long offset) + : "r" (mp->regs + offset), + "i" (ASI_PHYS_BYPASS_EC_E)); + } +- put_cpu(); ++ ++ preempt_enable(); + + return ret; + } +diff --git a/arch/sparc64/kernel/entry.S b/arch/sparc64/kernel/entry.S +index 8059531..193791c 100644 +--- a/arch/sparc64/kernel/entry.S ++++ b/arch/sparc64/kernel/entry.S +@@ -2593,3 +2593,15 @@ sun4v_mmustat_info: + retl + nop + .size sun4v_mmustat_info, .-sun4v_mmustat_info ++ ++ .globl sun4v_mmu_demap_all ++ .type sun4v_mmu_demap_all,#function ++sun4v_mmu_demap_all: ++ clr %o0 ++ clr %o1 ++ mov HV_MMU_ALL, %o2 ++ mov HV_FAST_MMU_DEMAP_ALL, %o5 ++ ta HV_FAST_TRAP ++ retl ++ nop ++ .size sun4v_mmu_demap_all, .-sun4v_mmu_demap_all +diff --git a/arch/sparc64/kernel/pci.c b/arch/sparc64/kernel/pci.c +index e8dac81..9bc05cf 100644 +--- a/arch/sparc64/kernel/pci.c ++++ b/arch/sparc64/kernel/pci.c +@@ -1276,4 +1276,20 @@ int pci_dma_supported(struct pci_dev *pdev, u64 device_mask) + return (device_mask & dma_addr_mask) == dma_addr_mask; + } + ++void pci_resource_to_user(const struct pci_dev *pdev, int bar, ++ const struct resource *rp, resource_size_t *start, ++ resource_size_t *end) ++{ ++ struct pci_pbm_info *pbm = pdev->dev.archdata.host_controller; ++ unsigned long offset; ++ ++ if (rp->flags & IORESOURCE_IO) ++ offset = pbm->io_space.start; ++ else ++ offset = pbm->mem_space.start; ++ ++ *start = rp->start - offset; ++ *end = rp->end - offset; ++} ++ + #endif /* !(CONFIG_PCI) */ +diff --git a/arch/sparc64/kernel/smp.c b/arch/sparc64/kernel/smp.c +index c73b7a4..34e8a01 100644 +--- a/arch/sparc64/kernel/smp.c ++++ b/arch/sparc64/kernel/smp.c +@@ -476,7 +476,7 @@ static __inline__ void spitfire_xcall_deliver(u64 data0, u64 data1, u64 data2, c + */ + static void cheetah_xcall_deliver(u64 data0, u64 data1, u64 data2, cpumask_t mask) + { +- u64 pstate, ver; ++ u64 pstate, ver, busy_mask; + int nack_busy_id, is_jbus, need_more; + + if (cpus_empty(mask)) +@@ -508,14 +508,20 @@ retry: + "i" (ASI_INTR_W)); + + nack_busy_id = 0; ++ busy_mask = 0; + { + int i; + + for_each_cpu_mask(i, mask) { + u64 target = (i << 14) | 0x70; + +- if (!is_jbus) ++ if (is_jbus) { ++ busy_mask |= (0x1UL << (i * 2)); ++ } else { + target |= (nack_busy_id << 24); ++ busy_mask |= (0x1UL << ++ (nack_busy_id * 2)); ++ } + __asm__ __volatile__( + "stxa %%g0, [%0] %1\n\t" + "membar #Sync\n\t" +@@ -531,15 +537,16 @@ retry: + + /* Now, poll for completion. */ + { +- u64 dispatch_stat; ++ u64 dispatch_stat, nack_mask; + long stuck; + + stuck = 100000 * nack_busy_id; ++ nack_mask = busy_mask << 1; + do { + __asm__ __volatile__("ldxa [%%g0] %1, %0" + : "=r" (dispatch_stat) + : "i" (ASI_INTR_DISPATCH_STAT)); +- if (dispatch_stat == 0UL) { ++ if (!(dispatch_stat & (busy_mask | nack_mask))) { + __asm__ __volatile__("wrpr %0, 0x0, %%pstate" + : : "r" (pstate)); + if (unlikely(need_more)) { +@@ -556,12 +563,12 @@ retry: + } + if (!--stuck) + break; +- } while (dispatch_stat & 0x5555555555555555UL); ++ } while (dispatch_stat & busy_mask); + + __asm__ __volatile__("wrpr %0, 0x0, %%pstate" + : : "r" (pstate)); + +- if ((dispatch_stat & ~(0x5555555555555555UL)) == 0) { ++ if (dispatch_stat & busy_mask) { + /* Busy bits will not clear, continue instead + * of freezing up on this cpu. + */ +diff --git a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c +index 3010227..ed2484d 100644 +--- a/arch/sparc64/mm/init.c ++++ b/arch/sparc64/mm/init.c +@@ -1135,14 +1135,9 @@ static void __init mark_kpte_bitmap(unsigned long start, unsigned long end) + } + } + +-static void __init kernel_physical_mapping_init(void) ++static void __init init_kpte_bitmap(void) + { + unsigned long i; +-#ifdef CONFIG_DEBUG_PAGEALLOC +- unsigned long mem_alloced = 0UL; +-#endif +- +- read_obp_memory("reg", &pall[0], &pall_ents); + + for (i = 0; i < pall_ents; i++) { + unsigned long phys_start, phys_end; +@@ -1151,14 +1146,24 @@ static void __init kernel_physical_mapping_init(void) + phys_end = phys_start + pall[i].reg_size; + + mark_kpte_bitmap(phys_start, phys_end); ++ } ++} + ++static void __init kernel_physical_mapping_init(void) ++{ + #ifdef CONFIG_DEBUG_PAGEALLOC ++ unsigned long i, mem_alloced = 0UL; ++ ++ for (i = 0; i < pall_ents; i++) { ++ unsigned long phys_start, phys_end; ++ ++ phys_start = pall[i].phys_addr; ++ phys_end = phys_start + pall[i].reg_size; ++ + mem_alloced += kernel_map_range(phys_start, phys_end, + PAGE_KERNEL); +-#endif + } + +-#ifdef CONFIG_DEBUG_PAGEALLOC + printk("Allocated %ld bytes for kernel page tables.\n", + mem_alloced); + +@@ -1400,6 +1405,10 @@ void __init paging_init(void) + + inherit_prom_mappings(); + ++ read_obp_memory("reg", &pall[0], &pall_ents); ++ ++ init_kpte_bitmap(); ++ + /* Ok, we can use our TLB miss and window trap handlers safely. */ + setup_tba(); + +@@ -1854,7 +1863,9 @@ void __flush_tlb_all(void) + "wrpr %0, %1, %%pstate" + : "=r" (pstate) + : "i" (PSTATE_IE)); +- if (tlb_type == spitfire) { ++ if (tlb_type == hypervisor) { ++ sun4v_mmu_demap_all(); ++ } else if (tlb_type == spitfire) { + for (i = 0; i < 64; i++) { + /* Spitfire Errata #32 workaround */ + /* NOTE: Always runs on spitfire, so no +diff --git a/drivers/acpi/blacklist.c b/drivers/acpi/blacklist.c +index 3ec110c..d2e5298 100644 +--- a/drivers/acpi/blacklist.c ++++ b/drivers/acpi/blacklist.c +@@ -3,6 +3,7 @@ + * + * Check to see if the given machine has a known bad ACPI BIOS + * or if the BIOS is too old. ++ * Check given machine against acpi_osi_dmi_table[]. + * + * Copyright (C) 2004 Len Brown + * Copyright (C) 2002 Andy Grover +@@ -50,6 +51,8 @@ struct acpi_blacklist_item { + u32 is_critical_error; + }; + ++static struct dmi_system_id acpi_osi_dmi_table[] __initdata; ++ + /* + * POLICY: If *anything* doesn't work, put it on the blacklist. + * If they are critical errors, mark it critical, and abort driver load. +@@ -67,8 +70,6 @@ static struct acpi_blacklist_item acpi_blacklist[] __initdata = { + /* IBM 600E - _ADR should return 7, but it returns 1 */ + {"IBM ", "TP600E ", 0x00000105, ACPI_SIG_DSDT, less_than_or_equal, + "Incorrect _ADR", 1}, +- {"ASUS\0\0", "P2B-S ", 0, ACPI_SIG_DSDT, all_versions, +- "Bogus PCI routing", 1}, + + {""} + }; +@@ -165,5 +166,388 @@ int __init acpi_blacklisted(void) + + blacklisted += blacklist_by_year(); + ++ dmi_check_system(acpi_osi_dmi_table); ++ + return blacklisted; + } ++#ifdef CONFIG_DMI ++static int __init dmi_enable_osi_linux(struct dmi_system_id *d) ++{ ++ acpi_dmi_osi_linux(1, d); /* enable */ ++ return 0; ++} ++static int __init dmi_disable_osi_linux(struct dmi_system_id *d) ++{ ++ acpi_dmi_osi_linux(0, d); /* disable */ ++ return 0; ++} ++static int __init dmi_unknown_osi_linux(struct dmi_system_id *d) ++{ ++ acpi_dmi_osi_linux(-1, d); /* unknown */ ++ return 0; ++} ++ ++/* ++ * Most BIOS that invoke OSI(Linux) do nothing with it. ++ * But some cause Linux to break. ++ * Only a couple use it to make Linux run better. ++ * ++ * Thus, Linux should continue to disable OSI(Linux) by default, ++ * should continue to discourage BIOS writers from using it, and ++ * should whitelist the few existing systems that require it. ++ * ++ * If it appears clear a vendor isn't using OSI(Linux) ++ * for anything constructive, blacklist them by name to disable ++ * unnecessary dmesg warnings on all of their products. ++ */ ++ ++static struct dmi_system_id acpi_osi_dmi_table[] __initdata = { ++ /* ++ * Disable OSI(Linux) warnings on all "Acer, inc." ++ * ++ * _OSI(Linux) disables the latest Windows BIOS code: ++ * DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3100"), ++ * DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5050"), ++ * DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5100"), ++ * DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5580"), ++ * DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 3010"), ++ * _OSI(Linux) effect unknown: ++ * DMI_MATCH(DMI_PRODUCT_NAME, "Ferrari 5000"), ++ */ ++ /* ++ * note that dmi_check_system() uses strstr() ++ * to match sub-strings rather than !strcmp(), ++ * so "Acer" below matches "Acer, inc." above. ++ */ ++ /* ++ * Disable OSI(Linux) warnings on all "Acer" ++ * ++ * _OSI(Linux) effect unknown: ++ * DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5315"), ++ * DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5610"), ++ * DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 7720Z"), ++ * DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 5520"), ++ * DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 6460"), ++ * DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 7510"), ++ * DMI_MATCH(DMI_PRODUCT_NAME, "Extensa 5220"), ++ */ ++ { ++ .callback = dmi_unknown_osi_linux, ++ .ident = "Acer", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Acer"), ++ }, ++ }, ++ /* ++ * Disable OSI(Linux) warnings on all "Apple Computer, Inc." ++ * ++ * _OSI(Linux) confirmed to be a NOP: ++ * DMI_MATCH(DMI_PRODUCT_NAME, "MacBook1,1"), ++ * DMI_MATCH(DMI_PRODUCT_NAME, "MacBook2,1"), ++ * DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro2,2"), ++ * _OSI(Linux) effect unknown: ++ * DMI_MATCH(DMI_PRODUCT_NAME, "MacPro2,1"), ++ * DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro1,1"), ++ * DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro3,1"), ++ */ ++ { ++ .callback = dmi_disable_osi_linux, ++ .ident = "Apple", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Apple Computer, Inc."), ++ }, ++ }, ++ /* ++ * Disable OSI(Linux) warnings on all "BenQ" ++ * ++ * _OSI(Linux) confirmed to be a NOP: ++ * DMI_MATCH(DMI_PRODUCT_NAME, "Joybook S31"), ++ */ ++ { ++ .callback = dmi_disable_osi_linux, ++ .ident = "BenQ", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "BenQ"), ++ }, ++ }, ++ /* ++ * Disable OSI(Linux) warnings on all "Clevo Co." ++ * ++ * _OSI(Linux) confirmed to be a NOP: ++ * DMI_MATCH(DMI_PRODUCT_NAME, "M570RU"), ++ */ ++ { ++ .callback = dmi_disable_osi_linux, ++ .ident = "Clevo", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Clevo Co."), ++ }, ++ }, ++ /* ++ * Disable OSI(Linux) warnings on all "COMPAL" ++ * ++ * _OSI(Linux) confirmed to be a NOP: ++ * DMI_MATCH(DMI_BOARD_NAME, "HEL8X"), ++ * _OSI(Linux) unknown effect: ++ * DMI_MATCH(DMI_BOARD_NAME, "IFL91"), ++ */ ++ { ++ .callback = dmi_unknown_osi_linux, ++ .ident = "Compal", ++ .matches = { ++ DMI_MATCH(DMI_BIOS_VENDOR, "COMPAL"), ++ }, ++ }, ++ { /* OSI(Linux) touches USB, unknown side-effect */ ++ .callback = dmi_disable_osi_linux, ++ .ident = "Dell Dimension 5150", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), ++ DMI_MATCH(DMI_PRODUCT_NAME, "Dell DM051"), ++ }, ++ }, ++ { /* OSI(Linux) is a NOP */ ++ .callback = dmi_disable_osi_linux, ++ .ident = "Dell", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), ++ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1501"), ++ }, ++ }, ++ { /* OSI(Linux) effect unknown */ ++ .callback = dmi_unknown_osi_linux, ++ .ident = "Dell", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), ++ DMI_MATCH(DMI_PRODUCT_NAME, "Latitude D830"), ++ }, ++ }, ++ { /* OSI(Linux) effect unknown */ ++ .callback = dmi_unknown_osi_linux, ++ .ident = "Dell", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), ++ DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex GX620"), ++ }, ++ }, ++ { /* OSI(Linux) effect unknown */ ++ .callback = dmi_unknown_osi_linux, ++ .ident = "Dell", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), ++ DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 1900"), ++ }, ++ }, ++ { /* OSI(Linux) touches USB */ ++ .callback = dmi_disable_osi_linux, ++ .ident = "Dell", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), ++ DMI_MATCH(DMI_PRODUCT_NAME, "Precision WorkStation 390"), ++ }, ++ }, ++ { /* OSI(Linux) is a NOP */ ++ .callback = dmi_disable_osi_linux, ++ .ident = "Dell Vostro 1000", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), ++ DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1000"), ++ }, ++ }, ++ { /* OSI(Linux) effect unknown */ ++ .callback = dmi_unknown_osi_linux, ++ .ident = "Dell", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), ++ DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge SC440"), ++ }, ++ }, ++ { /* OSI(Linux) effect unknown */ ++ .callback = dmi_unknown_osi_linux, ++ .ident = "Dialogue Flybook V5", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Dialogue Technology Corporation"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "Flybook V5"), ++ }, ++ }, ++ /* ++ * Disable OSI(Linux) warnings on all "FUJITSU SIEMENS" ++ * ++ * _OSI(Linux) disables latest Windows BIOS code: ++ * DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pa 2510"), ++ * _OSI(Linux) confirmed to be a NOP: ++ * DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pi 1536"), ++ * DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pi 1556"), ++ * DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 1546"), ++ * _OSI(Linux) unknown effect: ++ * DMI_MATCH(DMI_PRODUCT_NAME, "Amilo M1425"), ++ * DMI_MATCH(DMI_PRODUCT_NAME, "Amilo Si 1520"), ++ * DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Mobile V5505"), ++ */ ++ { ++ .callback = dmi_disable_osi_linux, ++ .ident = "Fujitsu Siemens", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), ++ }, ++ }, ++ /* ++ * Disable OSI(Linux) warnings on all "Hewlett-Packard" ++ * ++ * _OSI(Linux) confirmed to be a NOP: ++ * .ident = "HP Pavilion tx 1000" ++ * DMI_MATCH(DMI_BOARD_NAME, "30BF"), ++ * .ident = "HP Pavilion dv2000" ++ * DMI_MATCH(DMI_BOARD_NAME, "30B5"), ++ * .ident = "HP Pavilion dv5000", ++ * DMI_MATCH(DMI_BOARD_NAME, "30A7"), ++ * .ident = "HP Pavilion dv6300 30BC", ++ * DMI_MATCH(DMI_BOARD_NAME, "30BC"), ++ * .ident = "HP Pavilion dv6000", ++ * DMI_MATCH(DMI_BOARD_NAME, "30B7"), ++ * DMI_MATCH(DMI_BOARD_NAME, "30B8"), ++ * .ident = "HP Pavilion dv9000", ++ * DMI_MATCH(DMI_BOARD_NAME, "30B9"), ++ * .ident = "HP Pavilion dv9500", ++ * DMI_MATCH(DMI_BOARD_NAME, "30CB"), ++ * .ident = "HP/Compaq Presario C500", ++ * DMI_MATCH(DMI_BOARD_NAME, "30C6"), ++ * .ident = "HP/Compaq Presario F500", ++ * DMI_MATCH(DMI_BOARD_NAME, "30D3"), ++ * _OSI(Linux) unknown effect: ++ * .ident = "HP Pavilion dv6500", ++ * DMI_MATCH(DMI_BOARD_NAME, "30D0"), ++ */ ++ { ++ .callback = dmi_disable_osi_linux, ++ .ident = "Hewlett-Packard", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), ++ }, ++ }, ++ /* ++ * Lenovo has a mix of systems OSI(Linux) situations ++ * and thus we can not wildcard the vendor. ++ * ++ * _OSI(Linux) helps sound ++ * DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad R61"), ++ * DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T61"), ++ * _OSI(Linux) is a NOP: ++ * DMI_MATCH(DMI_PRODUCT_VERSION, "3000 N100"), ++ */ ++ { ++ .callback = dmi_enable_osi_linux, ++ .ident = "Lenovo ThinkPad R61", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad R61"), ++ }, ++ }, ++ { ++ .callback = dmi_enable_osi_linux, ++ .ident = "Lenovo ThinkPad T61", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T61"), ++ }, ++ }, ++ { ++ .callback = dmi_unknown_osi_linux, ++ .ident = "Lenovo 3000 V100", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_PRODUCT_VERSION, "LENOVO3000 V100"), ++ }, ++ }, ++ { ++ .callback = dmi_disable_osi_linux, ++ .ident = "Lenovo 3000 N100", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_PRODUCT_VERSION, "3000 N100"), ++ }, ++ }, ++ /* ++ * Disable OSI(Linux) warnings on all "LG Electronics" ++ * ++ * _OSI(Linux) confirmed to be a NOP: ++ * DMI_MATCH(DMI_PRODUCT_NAME, "P1-J150B"), ++ * with DMI_MATCH(DMI_BOARD_NAME, "ROCKY"), ++ * ++ * unknown: ++ * DMI_MATCH(DMI_PRODUCT_NAME, "S1-MDGDG"), ++ * with DMI_MATCH(DMI_BOARD_NAME, "ROCKY"), ++ */ ++ { ++ .callback = dmi_disable_osi_linux, ++ .ident = "LG", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "LG Electronics"), ++ }, ++ }, ++ /* NEC - OSI(Linux) effect unknown */ ++ { ++ .callback = dmi_unknown_osi_linux, ++ .ident = "NEC VERSA M360", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "NEC Computers SAS"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "NEC VERSA M360"), ++ }, ++ }, ++ /* ++ * Disable OSI(Linux) warnings on all "Samsung Electronics" ++ * ++ * OSI(Linux) disables PNP0C32 and other BIOS code for Windows: ++ * DMI_MATCH(DMI_PRODUCT_NAME, "R40P/R41P"), ++ * DMI_MATCH(DMI_PRODUCT_NAME, "R59P/R60P/R61P"), ++ */ ++ { ++ .callback = dmi_disable_osi_linux, ++ .ident = "Samsung", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), ++ }, ++ }, ++ /* ++ * Disable OSI(Linux) warnings on all "Sony Corporation" ++ * ++ * _OSI(Linux) is a NOP: ++ * DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SZ650N"), ++ * DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SZ38GP_C"), ++ * DMI_MATCH(DMI_PRODUCT_NAME, "VGN-TZ21MN_N"), ++ * _OSI(Linux) unknown effect: ++ * DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FZ11M"), ++ */ ++ { ++ .callback = dmi_unknown_osi_linux, ++ .ident = "Sony", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), ++ }, ++ }, ++ /* ++ * Disable OSI(Linux) warnings on all "TOSHIBA" ++ * ++ * _OSI(Linux) breaks sound (bugzilla 7787): ++ * DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P100"), ++ * DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P105"), ++ * _OSI(Linux) is a NOP: ++ * DMI_MATCH(DMI_PRODUCT_NAME, "Satellite A100"), ++ * DMI_MATCH(DMI_PRODUCT_NAME, "Satellite A210"), ++ * _OSI(Linux) unknown effect: ++ * DMI_MATCH(DMI_PRODUCT_NAME, "Satellite A135"), ++ * DMI_MATCH(DMI_PRODUCT_NAME, "Satellite A200"), ++ * DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P205"), ++ * DMI_MATCH(DMI_PRODUCT_NAME, "Satellite U305"), ++ */ ++ { ++ .callback = dmi_disable_osi_linux, ++ .ident = "Toshiba", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), ++ }, ++ }, ++ {} ++}; ++ ++#endif /* CONFIG_DMI */ +diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c +index 9ba778a..222fcec 100644 +--- a/drivers/acpi/bus.c ++++ b/drivers/acpi/bus.c +@@ -29,7 +29,6 @@ + #include + #include + #include +-#include + #include + #include + #ifdef CONFIG_X86 +@@ -757,16 +756,14 @@ static int __init acpi_init(void) + result = acpi_bus_init(); + + if (!result) { +-#ifdef CONFIG_PM_LEGACY +- if (!PM_IS_ACTIVE()) +- pm_active = 1; ++ if (!(pm_flags & PM_APM)) ++ pm_flags |= PM_ACPI; + else { + printk(KERN_INFO PREFIX + "APM is already active, exiting\n"); + disable_acpi(); + result = -ENODEV; + } +-#endif + } else + disable_acpi(); + +diff --git a/drivers/acpi/dispatcher/dsobject.c b/drivers/acpi/dispatcher/dsobject.c +index a474ca2..954ac8c 100644 +--- a/drivers/acpi/dispatcher/dsobject.c ++++ b/drivers/acpi/dispatcher/dsobject.c +@@ -137,6 +137,71 @@ acpi_ds_build_internal_object(struct acpi_walk_state *walk_state, + return_ACPI_STATUS(status); + } + } ++ ++ /* Special object resolution for elements of a package */ ++ ++ if ((op->common.parent->common.aml_opcode == AML_PACKAGE_OP) || ++ (op->common.parent->common.aml_opcode == ++ AML_VAR_PACKAGE_OP)) { ++ /* ++ * Attempt to resolve the node to a value before we insert it into ++ * the package. If this is a reference to a common data type, ++ * resolve it immediately. According to the ACPI spec, package ++ * elements can only be "data objects" or method references. ++ * Attempt to resolve to an Integer, Buffer, String or Package. ++ * If cannot, return the named reference (for things like Devices, ++ * Methods, etc.) Buffer Fields and Fields will resolve to simple ++ * objects (int/buf/str/pkg). ++ * ++ * NOTE: References to things like Devices, Methods, Mutexes, etc. ++ * will remain as named references. This behavior is not described ++ * in the ACPI spec, but it appears to be an oversight. ++ */ ++ obj_desc = (union acpi_operand_object *)op->common.node; ++ ++ status = ++ acpi_ex_resolve_node_to_value(ACPI_CAST_INDIRECT_PTR ++ (struct ++ acpi_namespace_node, ++ &obj_desc), ++ walk_state); ++ if (ACPI_FAILURE(status)) { ++ return_ACPI_STATUS(status); ++ } ++ ++ switch (op->common.node->type) { ++ /* ++ * For these types, we need the actual node, not the subobject. ++ * However, the subobject got an extra reference count above. ++ */ ++ case ACPI_TYPE_MUTEX: ++ case ACPI_TYPE_METHOD: ++ case ACPI_TYPE_POWER: ++ case ACPI_TYPE_PROCESSOR: ++ case ACPI_TYPE_EVENT: ++ case ACPI_TYPE_REGION: ++ case ACPI_TYPE_DEVICE: ++ case ACPI_TYPE_THERMAL: ++ ++ obj_desc = ++ (union acpi_operand_object *)op->common. ++ node; ++ break; ++ ++ default: ++ break; ++ } ++ ++ /* ++ * If above resolved to an operand object, we are done. Otherwise, ++ * we have a NS node, we must create the package entry as a named ++ * reference. ++ */ ++ if (ACPI_GET_DESCRIPTOR_TYPE(obj_desc) != ++ ACPI_DESC_TYPE_NAMED) { ++ goto exit; ++ } ++ } + } + + /* Create and init a new internal ACPI object */ +@@ -156,6 +221,7 @@ acpi_ds_build_internal_object(struct acpi_walk_state *walk_state, + return_ACPI_STATUS(status); + } + ++ exit: + *obj_desc_ptr = obj_desc; + return_ACPI_STATUS(AE_OK); + } +@@ -356,12 +422,25 @@ acpi_ds_build_internal_package_obj(struct acpi_walk_state *walk_state, + arg = arg->common.next; + for (i = 0; arg && (i < element_count); i++) { + if (arg->common.aml_opcode == AML_INT_RETURN_VALUE_OP) { +- +- /* This package element is already built, just get it */ +- +- obj_desc->package.elements[i] = +- ACPI_CAST_PTR(union acpi_operand_object, +- arg->common.node); ++ if (arg->common.node->type == ACPI_TYPE_METHOD) { ++ /* ++ * A method reference "looks" to the parser to be a method ++ * invocation, so we special case it here ++ */ ++ arg->common.aml_opcode = AML_INT_NAMEPATH_OP; ++ status = ++ acpi_ds_build_internal_object(walk_state, ++ arg, ++ &obj_desc-> ++ package. ++ elements[i]); ++ } else { ++ /* This package element is already built, just get it */ ++ ++ obj_desc->package.elements[i] = ++ ACPI_CAST_PTR(union acpi_operand_object, ++ arg->common.node); ++ } + } else { + status = acpi_ds_build_internal_object(walk_state, arg, + &obj_desc-> +diff --git a/drivers/acpi/events/evregion.c b/drivers/acpi/events/evregion.c +index e99f0c4..58ad097 100644 +--- a/drivers/acpi/events/evregion.c ++++ b/drivers/acpi/events/evregion.c +@@ -344,7 +344,7 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, + * setup will potentially execute control methods + * (e.g., _REG method for this region) + */ +- acpi_ex_relinquish_interpreter(); ++ acpi_ex_exit_interpreter(); + + status = region_setup(region_obj, ACPI_REGION_ACTIVATE, + handler_desc->address_space.context, +@@ -352,7 +352,7 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, + + /* Re-enter the interpreter */ + +- acpi_ex_reacquire_interpreter(); ++ acpi_ex_enter_interpreter(); + + /* Check for failure of the Region Setup */ + +@@ -405,7 +405,7 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, + * exit the interpreter because the handler *might* block -- we don't + * know what it will do, so we can't hold the lock on the intepreter. + */ +- acpi_ex_relinquish_interpreter(); ++ acpi_ex_exit_interpreter(); + } + + /* Call the handler */ +@@ -426,7 +426,7 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, + * We just returned from a non-default handler, we must re-enter the + * interpreter + */ +- acpi_ex_reacquire_interpreter(); ++ acpi_ex_enter_interpreter(); + } + + return_ACPI_STATUS(status); +diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c +index 12c09fa..cd573e4 100644 +--- a/drivers/acpi/osl.c ++++ b/drivers/acpi/osl.c +@@ -77,11 +77,55 @@ static struct workqueue_struct *kacpi_notify_wq; + #define OSI_STRING_LENGTH_MAX 64 /* arbitrary */ + static char osi_additional_string[OSI_STRING_LENGTH_MAX]; + +-static int osi_linux; /* disable _OSI(Linux) by default */ ++/* ++ * "Ode to _OSI(Linux)" ++ * ++ * osi_linux -- Control response to BIOS _OSI(Linux) query. ++ * ++ * As Linux evolves, the features that it supports change. ++ * So an OSI string such as "Linux" is not specific enough ++ * to be useful across multiple versions of Linux. It ++ * doesn't identify any particular feature, interface, ++ * or even any particular version of Linux... ++ * ++ * Unfortunately, Linux-2.6.22 and earlier responded "yes" ++ * to a BIOS _OSI(Linux) query. When ++ * a reference mobile BIOS started using it, its use ++ * started to spread to many vendor platforms. ++ * As it is not supportable, we need to halt that spread. ++ * ++ * Today, most BIOS references to _OSI(Linux) are noise -- ++ * they have no functional effect and are just dead code ++ * carried over from the reference BIOS. ++ * ++ * The next most common case is that _OSI(Linux) harms Linux, ++ * usually by causing the BIOS to follow paths that are ++ * not tested during Windows validation. ++ * ++ * Finally, there is a short list of platforms ++ * where OSI(Linux) benefits Linux. ++ * ++ * In Linux-2.6.23, OSI(Linux) is first disabled by default. ++ * DMI is used to disable the dmesg warning about OSI(Linux) ++ * on platforms where it is known to have no effect. ++ * But a dmesg warning remains for systems where ++ * we do not know if OSI(Linux) is good or bad for the system. ++ * DMI is also used to enable OSI(Linux) for the machines ++ * that are known to need it. ++ * ++ * BIOS writers should NOT query _OSI(Linux) on future systems. ++ * It will be ignored by default, and to get Linux to ++ * not ignore it will require a kernel source update to ++ * add a DMI entry, or a boot-time "acpi_osi=Linux" invocation. ++ */ ++#define OSI_LINUX_ENABLE 0 + +-#ifdef CONFIG_DMI +-static struct __initdata dmi_system_id acpi_osl_dmi_table[]; +-#endif ++static struct osi_linux { ++ unsigned int enable:1; ++ unsigned int dmi:1; ++ unsigned int cmdline:1; ++ unsigned int known:1; ++} osi_linux = { OSI_LINUX_ENABLE, 0, 0, 0}; + + static void __init acpi_request_region (struct acpi_generic_address *addr, + unsigned int length, char *desc) +@@ -133,7 +177,6 @@ device_initcall(acpi_reserve_resources); + + acpi_status __init acpi_os_initialize(void) + { +- dmi_check_system(acpi_osl_dmi_table); + return AE_OK; + } + +@@ -971,13 +1014,37 @@ static int __init acpi_os_name_setup(char *str) + + __setup("acpi_os_name=", acpi_os_name_setup); + +-static void enable_osi_linux(int enable) { ++static void __init set_osi_linux(unsigned int enable) ++{ ++ if (osi_linux.enable != enable) { ++ osi_linux.enable = enable; ++ printk(KERN_NOTICE PREFIX "%sed _OSI(Linux)\n", ++ enable ? "Add": "Delet"); ++ } ++ return; ++} + +- if (osi_linux != enable) +- printk(KERN_INFO PREFIX "%sabled _OSI(Linux)\n", +- enable ? "En": "Dis"); ++static void __init acpi_cmdline_osi_linux(unsigned int enable) ++{ ++ osi_linux.cmdline = 1; /* cmdline set the default */ ++ set_osi_linux(enable); ++ ++ return; ++} ++ ++void __init acpi_dmi_osi_linux(int enable, const struct dmi_system_id *d) ++{ ++ osi_linux.dmi = 1; /* DMI knows that this box asks OSI(Linux) */ ++ ++ printk(KERN_NOTICE PREFIX "DMI detected: %s\n", d->ident); ++ ++ if (enable == -1) ++ return; ++ ++ osi_linux.known = 1; /* DMI knows which OSI(Linux) default needed */ ++ ++ set_osi_linux(enable); + +- osi_linux = enable; + return; + } + +@@ -994,12 +1061,12 @@ static int __init acpi_osi_setup(char *str) + printk(KERN_INFO PREFIX "_OSI method disabled\n"); + acpi_gbl_create_osi_method = FALSE; + } else if (!strcmp("!Linux", str)) { +- enable_osi_linux(0); ++ acpi_cmdline_osi_linux(0); /* !enable */ + } else if (*str == '!') { + if (acpi_osi_invalidate(++str) == AE_OK) + printk(KERN_INFO PREFIX "Deleted _OSI(%s)\n", str); + } else if (!strcmp("Linux", str)) { +- enable_osi_linux(1); ++ acpi_cmdline_osi_linux(1); /* enable */ + } else if (*osi_additional_string == '\0') { + strncpy(osi_additional_string, str, OSI_STRING_LENGTH_MAX); + printk(KERN_INFO PREFIX "Added _OSI(%s)\n", str); +@@ -1156,6 +1223,34 @@ acpi_status acpi_os_release_object(acpi_cache_t * cache, void *object) + return (AE_OK); + } + ++/** ++ * acpi_dmi_dump - dump DMI slots needed for blacklist entry ++ * ++ * Returns 0 on success ++ */ ++static int acpi_dmi_dump(void) ++{ ++ ++ if (!dmi_available) ++ return -1; ++ ++ printk(KERN_NOTICE PREFIX "DMI System Vendor: %s\n", ++ dmi_get_system_info(DMI_SYS_VENDOR)); ++ printk(KERN_NOTICE PREFIX "DMI Product Name: %s\n", ++ dmi_get_system_info(DMI_PRODUCT_NAME)); ++ printk(KERN_NOTICE PREFIX "DMI Product Version: %s\n", ++ dmi_get_system_info(DMI_PRODUCT_VERSION)); ++ printk(KERN_NOTICE PREFIX "DMI Board Name: %s\n", ++ dmi_get_system_info(DMI_BOARD_NAME)); ++ printk(KERN_NOTICE PREFIX "DMI BIOS Vendor: %s\n", ++ dmi_get_system_info(DMI_BIOS_VENDOR)); ++ printk(KERN_NOTICE PREFIX "DMI BIOS Date: %s\n", ++ dmi_get_system_info(DMI_BIOS_DATE)); ++ ++ return 0; ++} ++ ++ + /****************************************************************************** + * + * FUNCTION: acpi_os_validate_interface +@@ -1175,13 +1270,29 @@ acpi_os_validate_interface (char *interface) + if (!strncmp(osi_additional_string, interface, OSI_STRING_LENGTH_MAX)) + return AE_OK; + if (!strcmp("Linux", interface)) { +- printk(KERN_WARNING PREFIX +- "System BIOS is requesting _OSI(Linux)\n"); +- printk(KERN_WARNING PREFIX +- "If \"acpi_osi=Linux\" works better,\n" +- "Please send dmidecode " +- "to linux-acpi@vger.kernel.org\n"); +- if(osi_linux) ++ ++ printk(KERN_NOTICE PREFIX ++ "BIOS _OSI(Linux) query %s%s\n", ++ osi_linux.enable ? "honored" : "ignored", ++ osi_linux.cmdline ? " via cmdline" : ++ osi_linux.dmi ? " via DMI" : ""); ++ ++ if (!osi_linux.dmi) { ++ if (acpi_dmi_dump()) ++ printk(KERN_NOTICE PREFIX ++ "[please extract dmidecode output]\n"); ++ printk(KERN_NOTICE PREFIX ++ "Please send DMI info above to " ++ "linux-acpi@vger.kernel.org\n"); ++ } ++ if (!osi_linux.known && !osi_linux.cmdline) { ++ printk(KERN_NOTICE PREFIX ++ "If \"acpi_osi=%sLinux\" works better, " ++ "please notify linux-acpi@vger.kernel.org\n", ++ osi_linux.enable ? "!" : ""); ++ } ++ ++ if (osi_linux.enable) + return AE_OK; + } + return AE_SUPPORT; +@@ -1213,28 +1324,4 @@ acpi_os_validate_address ( + return AE_OK; + } + +-#ifdef CONFIG_DMI +-static int dmi_osi_linux(struct dmi_system_id *d) +-{ +- printk(KERN_NOTICE "%s detected: enabling _OSI(Linux)\n", d->ident); +- enable_osi_linux(1); +- return 0; +-} +- +-static struct dmi_system_id acpi_osl_dmi_table[] __initdata = { +- /* +- * Boxes that need _OSI(Linux) +- */ +- { +- .callback = dmi_osi_linux, +- .ident = "Intel Napa CRB", +- .matches = { +- DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"), +- DMI_MATCH(DMI_BOARD_NAME, "MPAD-MSAE Customer Reference Boards"), +- }, +- }, +- {} +-}; +-#endif /* CONFIG_DMI */ +- + #endif +diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c +index dd3186a..62010c2 100644 +--- a/drivers/acpi/pci_irq.c ++++ b/drivers/acpi/pci_irq.c +@@ -429,6 +429,15 @@ int acpi_pci_irq_enable(struct pci_dev *dev) + &polarity, &link, + acpi_pci_allocate_irq); + ++ if (irq < 0) { ++ /* ++ * IDE legacy mode controller IRQs are magic. Why do compat ++ * extensions always make such a nasty mess. ++ */ ++ if (dev->class >> 8 == PCI_CLASS_STORAGE_IDE && ++ (dev->class & 0x05) == 0) ++ return 0; ++ } + /* + * No IRQ known to the ACPI subsystem - maybe the BIOS / + * driver reported one, then use it. Exit in any case. +diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c +index dad84c0..9d71f25 100644 +--- a/drivers/acpi/video.c ++++ b/drivers/acpi/video.c +@@ -573,7 +573,7 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) + struct acpi_video_device_brightness *br = NULL; + + +- memset(&device->cap, 0, 4); ++ memset(&device->cap, 0, sizeof(device->cap)); + + if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_ADR", &h_dummy1))) { + device->cap._ADR = 1; +@@ -693,7 +693,7 @@ static void acpi_video_bus_find_cap(struct acpi_video_bus *video) + { + acpi_handle h_dummy1; + +- memset(&video->cap, 0, 4); ++ memset(&video->cap, 0, sizeof(video->cap)); + if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_DOS", &h_dummy1))) { + video->cap._DOS = 1; + } +diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c +index 98e33f9..4895a42 100644 +--- a/drivers/ata/libata-core.c ++++ b/drivers/ata/libata-core.c +@@ -6121,19 +6121,6 @@ static void ata_host_release(struct device *gendev, void *res) + if (!ap) + continue; + +- if ((host->flags & ATA_HOST_STARTED) && ap->ops->port_stop) +- ap->ops->port_stop(ap); +- } +- +- if ((host->flags & ATA_HOST_STARTED) && host->ops->host_stop) +- host->ops->host_stop(host); +- +- for (i = 0; i < host->n_ports; i++) { +- struct ata_port *ap = host->ports[i]; +- +- if (!ap) +- continue; +- + if (ap->scsi_host) + scsi_host_put(ap->scsi_host); + +@@ -6258,6 +6245,24 @@ struct ata_host *ata_host_alloc_pinfo(struct device *dev, + return host; + } + ++static void ata_host_stop(struct device *gendev, void *res) ++{ ++ struct ata_host *host = dev_get_drvdata(gendev); ++ int i; ++ ++ WARN_ON(!(host->flags & ATA_HOST_STARTED)); ++ ++ for (i = 0; i < host->n_ports; i++) { ++ struct ata_port *ap = host->ports[i]; ++ ++ if (ap->ops->port_stop) ++ ap->ops->port_stop(ap); ++ } ++ ++ if (host->ops->host_stop) ++ host->ops->host_stop(host); ++} ++ + /** + * ata_host_start - start and freeze ports of an ATA host + * @host: ATA host to start ports for +@@ -6276,6 +6281,8 @@ struct ata_host *ata_host_alloc_pinfo(struct device *dev, + */ + int ata_host_start(struct ata_host *host) + { ++ int have_stop = 0; ++ void *start_dr = NULL; + int i, rc; + + if (host->flags & ATA_HOST_STARTED) +@@ -6287,6 +6294,22 @@ int ata_host_start(struct ata_host *host) + if (!host->ops && !ata_port_is_dummy(ap)) + host->ops = ap->ops; + ++ if (ap->ops->port_stop) ++ have_stop = 1; ++ } ++ ++ if (host->ops->host_stop) ++ have_stop = 1; ++ ++ if (have_stop) { ++ start_dr = devres_alloc(ata_host_stop, 0, GFP_KERNEL); ++ if (!start_dr) ++ return -ENOMEM; ++ } ++ ++ for (i = 0; i < host->n_ports; i++) { ++ struct ata_port *ap = host->ports[i]; ++ + if (ap->ops->port_start) { + rc = ap->ops->port_start(ap); + if (rc) { +@@ -6299,6 +6322,8 @@ int ata_host_start(struct ata_host *host) + ata_eh_freeze_port(ap); + } + ++ if (start_dr) ++ devres_add(host->dev, start_dr); + host->flags |= ATA_HOST_STARTED; + return 0; + +@@ -6309,6 +6334,7 @@ int ata_host_start(struct ata_host *host) + if (ap->ops->port_stop) + ap->ops->port_stop(ap); + } ++ devres_free(start_dr); + return rc; + } + +diff --git a/drivers/ata/sata_promise.c b/drivers/ata/sata_promise.c +index 25698cf..bab694a 100644 +--- a/drivers/ata/sata_promise.c ++++ b/drivers/ata/sata_promise.c +@@ -50,6 +50,7 @@ + enum { + PDC_MAX_PORTS = 4, + PDC_MMIO_BAR = 3, ++ PDC_MAX_PRD = LIBATA_MAX_PRD - 1, /* -1 for ASIC PRD bug workaround */ + + /* register offsets */ + PDC_FEATURE = 0x04, /* Feature/Error reg (per port) */ +@@ -155,7 +156,7 @@ static struct scsi_host_template pdc_ata_sht = { + .queuecommand = ata_scsi_queuecmd, + .can_queue = ATA_DEF_QUEUE, + .this_id = ATA_SHT_THIS_ID, +- .sg_tablesize = LIBATA_MAX_PRD, ++ .sg_tablesize = PDC_MAX_PRD, + .cmd_per_lun = ATA_SHT_CMD_PER_LUN, + .emulated = ATA_SHT_EMULATED, + .use_clustering = ATA_SHT_USE_CLUSTERING, +@@ -527,6 +528,84 @@ static void pdc_atapi_pkt(struct ata_queued_cmd *qc) + memcpy(buf+31, cdb, cdb_len); + } + ++/** ++ * pdc_fill_sg - Fill PCI IDE PRD table ++ * @qc: Metadata associated with taskfile to be transferred ++ * ++ * Fill PCI IDE PRD (scatter-gather) table with segments ++ * associated with the current disk command. ++ * Make sure hardware does not choke on it. ++ * ++ * LOCKING: ++ * spin_lock_irqsave(host lock) ++ * ++ */ ++static void pdc_fill_sg(struct ata_queued_cmd *qc) ++{ ++ struct ata_port *ap = qc->ap; ++ struct scatterlist *sg; ++ unsigned int idx; ++ const u32 SG_COUNT_ASIC_BUG = 41*4; ++ ++ if (!(qc->flags & ATA_QCFLAG_DMAMAP)) ++ return; ++ ++ WARN_ON(qc->__sg == NULL); ++ WARN_ON(qc->n_elem == 0 && qc->pad_len == 0); ++ ++ idx = 0; ++ ata_for_each_sg(sg, qc) { ++ u32 addr, offset; ++ u32 sg_len, len; ++ ++ /* determine if physical DMA addr spans 64K boundary. ++ * Note h/w doesn't support 64-bit, so we unconditionally ++ * truncate dma_addr_t to u32. ++ */ ++ addr = (u32) sg_dma_address(sg); ++ sg_len = sg_dma_len(sg); ++ ++ while (sg_len) { ++ offset = addr & 0xffff; ++ len = sg_len; ++ if ((offset + sg_len) > 0x10000) ++ len = 0x10000 - offset; ++ ++ ap->prd[idx].addr = cpu_to_le32(addr); ++ ap->prd[idx].flags_len = cpu_to_le32(len & 0xffff); ++ VPRINTK("PRD[%u] = (0x%X, 0x%X)\n", idx, addr, len); ++ ++ idx++; ++ sg_len -= len; ++ addr += len; ++ } ++ } ++ ++ if (idx) { ++ u32 len = le32_to_cpu(ap->prd[idx - 1].flags_len); ++ ++ if (len > SG_COUNT_ASIC_BUG) { ++ u32 addr; ++ ++ VPRINTK("Splitting last PRD.\n"); ++ ++ addr = le32_to_cpu(ap->prd[idx - 1].addr); ++ ap->prd[idx - 1].flags_len = cpu_to_le32(len - SG_COUNT_ASIC_BUG); ++ VPRINTK("PRD[%u] = (0x%X, 0x%X)\n", idx - 1, addr, SG_COUNT_ASIC_BUG); ++ ++ addr = addr + len - SG_COUNT_ASIC_BUG; ++ len = SG_COUNT_ASIC_BUG; ++ ap->prd[idx].addr = cpu_to_le32(addr); ++ ap->prd[idx].flags_len = cpu_to_le32(len); ++ VPRINTK("PRD[%u] = (0x%X, 0x%X)\n", idx, addr, len); ++ ++ idx++; ++ } ++ ++ ap->prd[idx - 1].flags_len |= cpu_to_le32(ATA_PRD_EOT); ++ } ++} ++ + static void pdc_qc_prep(struct ata_queued_cmd *qc) + { + struct pdc_port_priv *pp = qc->ap->private_data; +@@ -536,7 +615,7 @@ static void pdc_qc_prep(struct ata_queued_cmd *qc) + + switch (qc->tf.protocol) { + case ATA_PROT_DMA: +- ata_qc_prep(qc); ++ pdc_fill_sg(qc); + /* fall through */ + + case ATA_PROT_NODATA: +@@ -552,11 +631,11 @@ static void pdc_qc_prep(struct ata_queued_cmd *qc) + break; + + case ATA_PROT_ATAPI: +- ata_qc_prep(qc); ++ pdc_fill_sg(qc); + break; + + case ATA_PROT_ATAPI_DMA: +- ata_qc_prep(qc); ++ pdc_fill_sg(qc); + /*FALLTHROUGH*/ + case ATA_PROT_ATAPI_NODATA: + pdc_atapi_pkt(qc); +diff --git a/drivers/atm/nicstar.c b/drivers/atm/nicstar.c +index 14ced85..0c205b0 100644 +--- a/drivers/atm/nicstar.c ++++ b/drivers/atm/nicstar.c +@@ -625,14 +625,6 @@ static int __devinit ns_init_card(int i, struct pci_dev *pcidev) + if (mac[i] == NULL) + nicstar_init_eprom(card->membase); + +- if (request_irq(pcidev->irq, &ns_irq_handler, IRQF_DISABLED | IRQF_SHARED, "nicstar", card) != 0) +- { +- printk("nicstar%d: can't allocate IRQ %d.\n", i, pcidev->irq); +- error = 9; +- ns_init_card_error(card, error); +- return error; +- } +- + /* Set the VPI/VCI MSb mask to zero so we can receive OAM cells */ + writel(0x00000000, card->membase + VPM); + +@@ -858,8 +850,6 @@ static int __devinit ns_init_card(int i, struct pci_dev *pcidev) + card->iovpool.count++; + } + +- card->intcnt = 0; +- + /* Configure NICStAR */ + if (card->rct_size == 4096) + ns_cfg_rctsize = NS_CFG_RCTSIZE_4096_ENTRIES; +@@ -868,6 +858,15 @@ static int __devinit ns_init_card(int i, struct pci_dev *pcidev) + + card->efbie = 1; + ++ card->intcnt = 0; ++ if (request_irq(pcidev->irq, &ns_irq_handler, IRQF_DISABLED | IRQF_SHARED, "nicstar", card) != 0) ++ { ++ printk("nicstar%d: can't allocate IRQ %d.\n", i, pcidev->irq); ++ error = 9; ++ ns_init_card_error(card, error); ++ return error; ++ } ++ + /* Register device */ + card->atmdev = atm_dev_register("nicstar", &atm_ops, -1, NULL); + if (card->atmdev == NULL) +diff --git a/drivers/char/apm-emulation.c b/drivers/char/apm-emulation.c +index ec116df..72183bd 100644 +--- a/drivers/char/apm-emulation.c ++++ b/drivers/char/apm-emulation.c +@@ -295,7 +295,6 @@ static int + apm_ioctl(struct inode * inode, struct file *filp, u_int cmd, u_long arg) + { + struct apm_user *as = filp->private_data; +- unsigned long flags; + int err = -EINVAL; + + if (!as->suser || !as->writer) +@@ -331,10 +330,16 @@ apm_ioctl(struct inode * inode, struct file *filp, u_int cmd, u_long arg) + * Wait for the suspend/resume to complete. If there + * are pending acknowledges, we wait here for them. + */ +- flags = current->flags; ++ freezer_do_not_count(); + + wait_event(apm_suspend_waitqueue, + as->suspend_state == SUSPEND_DONE); ++ ++ /* ++ * Since we are waiting until the suspend is done, the ++ * try_to_freeze() in freezer_count() will not trigger ++ */ ++ freezer_count(); + } else { + as->suspend_state = SUSPEND_WAIT; + mutex_unlock(&state_lock); +@@ -362,14 +367,10 @@ apm_ioctl(struct inode * inode, struct file *filp, u_int cmd, u_long arg) + * Wait for the suspend/resume to complete. If there + * are pending acknowledges, we wait here for them. + */ +- flags = current->flags; +- +- wait_event_interruptible(apm_suspend_waitqueue, ++ wait_event_freezable(apm_suspend_waitqueue, + as->suspend_state == SUSPEND_DONE); + } + +- current->flags = flags; +- + mutex_lock(&state_lock); + err = as->suspend_result; + as->suspend_state = SUSPEND_NONE; +diff --git a/drivers/char/drm/drm_vm.c b/drivers/char/drm/drm_vm.c +index 68e36e5..caa0bce 100644 +--- a/drivers/char/drm/drm_vm.c ++++ b/drivers/char/drm/drm_vm.c +@@ -506,6 +506,7 @@ static int drm_mmap_dma(struct file *filp, struct vm_area_struct *vma) + vma->vm_ops = &drm_vm_dma_ops; + + vma->vm_flags |= VM_RESERVED; /* Don't swap */ ++ vma->vm_flags |= VM_DONTEXPAND; + + vma->vm_file = filp; /* Needed for drm_vm_open() */ + drm_vm_open_locked(vma); +@@ -655,6 +656,7 @@ static int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma) + return -EINVAL; /* This should never happen. */ + } + vma->vm_flags |= VM_RESERVED; /* Don't swap */ ++ vma->vm_flags |= VM_DONTEXPAND; + + vma->vm_file = filp; /* Needed for drm_vm_open() */ + drm_vm_open_locked(vma); +diff --git a/drivers/char/mspec.c b/drivers/char/mspec.c +index 04ac155..ada142a 100644 +--- a/drivers/char/mspec.c ++++ b/drivers/char/mspec.c +@@ -283,7 +283,7 @@ mspec_mmap(struct file *file, struct vm_area_struct *vma, + vdata->refcnt = ATOMIC_INIT(1); + vma->vm_private_data = vdata; + +- vma->vm_flags |= (VM_IO | VM_RESERVED | VM_PFNMAP); ++ vma->vm_flags |= (VM_IO | VM_RESERVED | VM_PFNMAP | VM_DONTEXPAND); + if (vdata->type == MSPEC_FETCHOP || vdata->type == MSPEC_UNCACHED) + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + vma->vm_ops = &mspec_vm_ops; +diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c +index 3ee73cf..d08c301 100644 +--- a/drivers/char/tty_ioctl.c ++++ b/drivers/char/tty_ioctl.c +@@ -62,7 +62,7 @@ void tty_wait_until_sent(struct tty_struct * tty, long timeout) + if (!timeout) + timeout = MAX_SCHEDULE_TIMEOUT; + if (wait_event_interruptible_timeout(tty->write_wait, +- !tty->driver->chars_in_buffer(tty), timeout)) ++ !tty->driver->chars_in_buffer(tty), timeout) < 0) + return; + if (tty->driver->wait_until_sent) + tty->driver->wait_until_sent(tty, timeout); +diff --git a/drivers/connector/cn_queue.c b/drivers/connector/cn_queue.c +index 296f510..12ceed5 100644 +--- a/drivers/connector/cn_queue.c ++++ b/drivers/connector/cn_queue.c +@@ -99,8 +99,8 @@ int cn_queue_add_callback(struct cn_queue_dev *dev, char *name, struct cb_id *id + spin_unlock_bh(&dev->queue_lock); + + if (found) { +- atomic_dec(&dev->refcnt); + cn_queue_free_callback(cbq); ++ atomic_dec(&dev->refcnt); + return -EINVAL; + } + +diff --git a/drivers/crypto/padlock-aes.c b/drivers/crypto/padlock-aes.c +index d4501dc..e9a23a4 100644 +--- a/drivers/crypto/padlock-aes.c ++++ b/drivers/crypto/padlock-aes.c +@@ -419,13 +419,58 @@ static int aes_set_key(struct crypto_tfm *tfm, const u8 *in_key, + /* ====== Encryption/decryption routines ====== */ + + /* These are the real call to PadLock. */ ++static inline void padlock_xcrypt(const u8 *input, u8 *output, void *key, ++ void *control_word) ++{ ++ asm volatile (".byte 0xf3,0x0f,0xa7,0xc8" /* rep xcryptecb */ ++ : "+S"(input), "+D"(output) ++ : "d"(control_word), "b"(key), "c"(1)); ++} ++ ++static void aes_crypt_copy(const u8 *in, u8 *out, u32 *key, struct cword *cword) ++{ ++ u8 buf[AES_BLOCK_SIZE * 2 + PADLOCK_ALIGNMENT - 1]; ++ u8 *tmp = PTR_ALIGN(&buf[0], PADLOCK_ALIGNMENT); ++ ++ memcpy(tmp, in, AES_BLOCK_SIZE); ++ padlock_xcrypt(tmp, out, key, cword); ++} ++ ++static inline void aes_crypt(const u8 *in, u8 *out, u32 *key, ++ struct cword *cword) ++{ ++ asm volatile ("pushfl; popfl"); ++ ++ /* padlock_xcrypt requires at least two blocks of data. */ ++ if (unlikely(!(((unsigned long)in ^ (PAGE_SIZE - AES_BLOCK_SIZE)) & ++ (PAGE_SIZE - 1)))) { ++ aes_crypt_copy(in, out, key, cword); ++ return; ++ } ++ ++ padlock_xcrypt(in, out, key, cword); ++} ++ + static inline void padlock_xcrypt_ecb(const u8 *input, u8 *output, void *key, + void *control_word, u32 count) + { ++ if (count == 1) { ++ aes_crypt(input, output, key, control_word); ++ return; ++ } ++ + asm volatile ("pushfl; popfl"); /* enforce key reload. */ +- asm volatile (".byte 0xf3,0x0f,0xa7,0xc8" /* rep xcryptecb */ ++ asm volatile ("test $1, %%cl;" ++ "je 1f;" ++ "lea -1(%%ecx), %%eax;" ++ "mov $1, %%ecx;" ++ ".byte 0xf3,0x0f,0xa7,0xc8;" /* rep xcryptecb */ ++ "mov %%eax, %%ecx;" ++ "1:" ++ ".byte 0xf3,0x0f,0xa7,0xc8" /* rep xcryptecb */ + : "+S"(input), "+D"(output) +- : "d"(control_word), "b"(key), "c"(count)); ++ : "d"(control_word), "b"(key), "c"(count) ++ : "ax"); + } + + static inline u8 *padlock_xcrypt_cbc(const u8 *input, u8 *output, void *key, +@@ -443,13 +488,13 @@ static inline u8 *padlock_xcrypt_cbc(const u8 *input, u8 *output, void *key, + static void aes_encrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in) + { + struct aes_ctx *ctx = aes_ctx(tfm); +- padlock_xcrypt_ecb(in, out, ctx->E, &ctx->cword.encrypt, 1); ++ aes_crypt(in, out, ctx->E, &ctx->cword.encrypt); + } + + static void aes_decrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in) + { + struct aes_ctx *ctx = aes_ctx(tfm); +- padlock_xcrypt_ecb(in, out, ctx->D, &ctx->cword.decrypt, 1); ++ aes_crypt(in, out, ctx->D, &ctx->cword.decrypt); + } + + static struct crypto_alg aes_alg = { +diff --git a/drivers/firmware/dmi-id.c b/drivers/firmware/dmi-id.c +index 59c3b5a..ed0d030 100644 +--- a/drivers/firmware/dmi-id.c ++++ b/drivers/firmware/dmi-id.c +@@ -159,8 +159,6 @@ static struct device *dmi_dev; + if (dmi_get_system_info(_field)) \ + sys_dmi_attributes[i++] = & sys_dmi_##_name##_attr.attr; + +-extern int dmi_available; +- + static int __init dmi_id_init(void) + { + int ret, i; +diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c +index f1c3d6c..27026f7 100644 +--- a/drivers/input/evdev.c ++++ b/drivers/input/evdev.c +@@ -30,6 +30,8 @@ struct evdev { + wait_queue_head_t wait; + struct evdev_client *grab; + struct list_head client_list; ++ spinlock_t client_lock; /* protects client_list */ ++ struct mutex mutex; + struct device dev; + }; + +@@ -37,39 +39,53 @@ struct evdev_client { + struct input_event buffer[EVDEV_BUFFER_SIZE]; + int head; + int tail; ++ spinlock_t buffer_lock; /* protects access to buffer, head and tail */ + struct fasync_struct *fasync; + struct evdev *evdev; + struct list_head node; + }; + + static struct evdev *evdev_table[EVDEV_MINORS]; ++static DEFINE_MUTEX(evdev_table_mutex); + +-static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) ++static void evdev_pass_event(struct evdev_client *client, ++ struct input_event *event) ++{ ++ /* ++ * Interrupts are disabled, just acquire the lock ++ */ ++ spin_lock(&client->buffer_lock); ++ client->buffer[client->head++] = *event; ++ client->head &= EVDEV_BUFFER_SIZE - 1; ++ spin_unlock(&client->buffer_lock); ++ ++ kill_fasync(&client->fasync, SIGIO, POLL_IN); ++} ++ ++/* ++ * Pass incoming event to all connected clients. Note that we are ++ * caleld under a spinlock with interrupts off so we don't need ++ * to use rcu_read_lock() here. Writers will be using syncronize_sched() ++ * instead of synchrnoize_rcu(). ++ */ ++static void evdev_event(struct input_handle *handle, ++ unsigned int type, unsigned int code, int value) + { + struct evdev *evdev = handle->private; + struct evdev_client *client; ++ struct input_event event; + +- if (evdev->grab) { +- client = evdev->grab; +- +- do_gettimeofday(&client->buffer[client->head].time); +- client->buffer[client->head].type = type; +- client->buffer[client->head].code = code; +- client->buffer[client->head].value = value; +- client->head = (client->head + 1) & (EVDEV_BUFFER_SIZE - 1); +- +- kill_fasync(&client->fasync, SIGIO, POLL_IN); +- } else +- list_for_each_entry(client, &evdev->client_list, node) { +- +- do_gettimeofday(&client->buffer[client->head].time); +- client->buffer[client->head].type = type; +- client->buffer[client->head].code = code; +- client->buffer[client->head].value = value; +- client->head = (client->head + 1) & (EVDEV_BUFFER_SIZE - 1); ++ do_gettimeofday(&event.time); ++ event.type = type; ++ event.code = code; ++ event.value = value; + +- kill_fasync(&client->fasync, SIGIO, POLL_IN); +- } ++ client = rcu_dereference(evdev->grab); ++ if (client) ++ evdev_pass_event(client, &event); ++ else ++ list_for_each_entry_rcu(client, &evdev->client_list, node) ++ evdev_pass_event(client, &event); + + wake_up_interruptible(&evdev->wait); + } +@@ -88,38 +104,145 @@ static int evdev_flush(struct file *file, fl_owner_t id) + { + struct evdev_client *client = file->private_data; + struct evdev *evdev = client->evdev; ++ int retval; ++ ++ retval = mutex_lock_interruptible(&evdev->mutex); ++ if (retval) ++ return retval; + + if (!evdev->exist) +- return -ENODEV; ++ retval = -ENODEV; ++ else ++ retval = input_flush_device(&evdev->handle, file); + +- return input_flush_device(&evdev->handle, file); ++ mutex_unlock(&evdev->mutex); ++ return retval; + } + + static void evdev_free(struct device *dev) + { + struct evdev *evdev = container_of(dev, struct evdev, dev); + +- evdev_table[evdev->minor] = NULL; + kfree(evdev); + } + ++/* ++ * Grabs an event device (along with underlying input device). ++ * This function is called with evdev->mutex taken. ++ */ ++static int evdev_grab(struct evdev *evdev, struct evdev_client *client) ++{ ++ int error; ++ ++ if (evdev->grab) ++ return -EBUSY; ++ ++ error = input_grab_device(&evdev->handle); ++ if (error) ++ return error; ++ ++ rcu_assign_pointer(evdev->grab, client); ++ /* ++ * We don't use synchronize_rcu() here because read-side ++ * critical section is protected by a spinlock instead ++ * of rcu_read_lock(). ++ */ ++ synchronize_sched(); ++ ++ return 0; ++} ++ ++static int evdev_ungrab(struct evdev *evdev, struct evdev_client *client) ++{ ++ if (evdev->grab != client) ++ return -EINVAL; ++ ++ rcu_assign_pointer(evdev->grab, NULL); ++ synchronize_sched(); ++ input_release_device(&evdev->handle); ++ ++ return 0; ++} ++ ++static void evdev_attach_client(struct evdev *evdev, ++ struct evdev_client *client) ++{ ++ spin_lock(&evdev->client_lock); ++ list_add_tail_rcu(&client->node, &evdev->client_list); ++ spin_unlock(&evdev->client_lock); ++ synchronize_sched(); ++} ++ ++static void evdev_detach_client(struct evdev *evdev, ++ struct evdev_client *client) ++{ ++ spin_lock(&evdev->client_lock); ++ list_del_rcu(&client->node); ++ spin_unlock(&evdev->client_lock); ++ synchronize_sched(); ++} ++ ++static int evdev_open_device(struct evdev *evdev) ++{ ++ int retval; ++ ++ retval = mutex_lock_interruptible(&evdev->mutex); ++ if (retval) ++ return retval; ++ ++ if (!evdev->exist) ++ retval = -ENODEV; ++ else if (!evdev->open++) { ++ retval = input_open_device(&evdev->handle); ++ if (retval) ++ evdev->open--; ++ } ++ ++ mutex_unlock(&evdev->mutex); ++ return retval; ++} ++ ++static void evdev_close_device(struct evdev *evdev) ++{ ++ mutex_lock(&evdev->mutex); ++ ++ if (evdev->exist && !--evdev->open) ++ input_close_device(&evdev->handle); ++ ++ mutex_unlock(&evdev->mutex); ++} ++ ++/* ++ * Wake up users waiting for IO so they can disconnect from ++ * dead device. ++ */ ++static void evdev_hangup(struct evdev *evdev) ++{ ++ struct evdev_client *client; ++ ++ spin_lock(&evdev->client_lock); ++ list_for_each_entry(client, &evdev->client_list, node) ++ kill_fasync(&client->fasync, SIGIO, POLL_HUP); ++ spin_unlock(&evdev->client_lock); ++ ++ wake_up_interruptible(&evdev->wait); ++} ++ + static int evdev_release(struct inode *inode, struct file *file) + { + struct evdev_client *client = file->private_data; + struct evdev *evdev = client->evdev; + +- if (evdev->grab == client) { +- input_release_device(&evdev->handle); +- evdev->grab = NULL; +- } ++ mutex_lock(&evdev->mutex); ++ if (evdev->grab == client) ++ evdev_ungrab(evdev, client); ++ mutex_unlock(&evdev->mutex); + + evdev_fasync(-1, file, 0); +- list_del(&client->node); ++ evdev_detach_client(evdev, client); + kfree(client); + +- if (!--evdev->open && evdev->exist) +- input_close_device(&evdev->handle); +- ++ evdev_close_device(evdev); + put_device(&evdev->dev); + + return 0; +@@ -127,41 +250,44 @@ static int evdev_release(struct inode *inode, struct file *file) + + static int evdev_open(struct inode *inode, struct file *file) + { +- struct evdev_client *client; + struct evdev *evdev; ++ struct evdev_client *client; + int i = iminor(inode) - EVDEV_MINOR_BASE; + int error; + + if (i >= EVDEV_MINORS) + return -ENODEV; + ++ error = mutex_lock_interruptible(&evdev_table_mutex); ++ if (error) ++ return error; + evdev = evdev_table[i]; ++ if (evdev) ++ get_device(&evdev->dev); ++ mutex_unlock(&evdev_table_mutex); + +- if (!evdev || !evdev->exist) ++ if (!evdev) + return -ENODEV; + +- get_device(&evdev->dev); +- + client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL); + if (!client) { + error = -ENOMEM; + goto err_put_evdev; + } + ++ spin_lock_init(&client->buffer_lock); + client->evdev = evdev; +- list_add_tail(&client->node, &evdev->client_list); ++ evdev_attach_client(evdev, client); + +- if (!evdev->open++ && evdev->exist) { +- error = input_open_device(&evdev->handle); +- if (error) +- goto err_free_client; +- } ++ error = evdev_open_device(evdev); ++ if (error) ++ goto err_free_client; + + file->private_data = client; + return 0; + + err_free_client: +- list_del(&client->node); ++ evdev_detach_client(evdev, client); + kfree(client); + err_put_evdev: + put_device(&evdev->dev); +@@ -197,12 +323,14 @@ static inline size_t evdev_event_size(void) + sizeof(struct input_event_compat) : sizeof(struct input_event); + } + +-static int evdev_event_from_user(const char __user *buffer, struct input_event *event) ++static int evdev_event_from_user(const char __user *buffer, ++ struct input_event *event) + { + if (COMPAT_TEST) { + struct input_event_compat compat_event; + +- if (copy_from_user(&compat_event, buffer, sizeof(struct input_event_compat))) ++ if (copy_from_user(&compat_event, buffer, ++ sizeof(struct input_event_compat))) + return -EFAULT; + + event->time.tv_sec = compat_event.time.tv_sec; +@@ -219,7 +347,8 @@ static int evdev_event_from_user(const char __user *buffer, struct input_event * + return 0; + } + +-static int evdev_event_to_user(char __user *buffer, const struct input_event *event) ++static int evdev_event_to_user(char __user *buffer, ++ const struct input_event *event) + { + if (COMPAT_TEST) { + struct input_event_compat compat_event; +@@ -230,7 +359,8 @@ static int evdev_event_to_user(char __user *buffer, const struct input_event *ev + compat_event.code = event->code; + compat_event.value = event->value; + +- if (copy_to_user(buffer, &compat_event, sizeof(struct input_event_compat))) ++ if (copy_to_user(buffer, &compat_event, ++ sizeof(struct input_event_compat))) + return -EFAULT; + + } else { +@@ -248,7 +378,8 @@ static inline size_t evdev_event_size(void) + return sizeof(struct input_event); + } + +-static int evdev_event_from_user(const char __user *buffer, struct input_event *event) ++static int evdev_event_from_user(const char __user *buffer, ++ struct input_event *event) + { + if (copy_from_user(event, buffer, sizeof(struct input_event))) + return -EFAULT; +@@ -256,7 +387,8 @@ static int evdev_event_from_user(const char __user *buffer, struct input_event * + return 0; + } + +-static int evdev_event_to_user(char __user *buffer, const struct input_event *event) ++static int evdev_event_to_user(char __user *buffer, ++ const struct input_event *event) + { + if (copy_to_user(buffer, event, sizeof(struct input_event))) + return -EFAULT; +@@ -266,37 +398,71 @@ static int evdev_event_to_user(char __user *buffer, const struct input_event *ev + + #endif /* CONFIG_COMPAT */ + +-static ssize_t evdev_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) ++static ssize_t evdev_write(struct file *file, const char __user *buffer, ++ size_t count, loff_t *ppos) + { + struct evdev_client *client = file->private_data; + struct evdev *evdev = client->evdev; + struct input_event event; +- int retval = 0; ++ int retval; + +- if (!evdev->exist) +- return -ENODEV; ++ retval = mutex_lock_interruptible(&evdev->mutex); ++ if (retval) ++ return retval; ++ ++ if (!evdev->exist) { ++ retval = -ENODEV; ++ goto out; ++ } + + while (retval < count) { + +- if (evdev_event_from_user(buffer + retval, &event)) +- return -EFAULT; +- input_inject_event(&evdev->handle, event.type, event.code, event.value); ++ if (evdev_event_from_user(buffer + retval, &event)) { ++ retval = -EFAULT; ++ goto out; ++ } ++ ++ input_inject_event(&evdev->handle, ++ event.type, event.code, event.value); + retval += evdev_event_size(); + } + ++ out: ++ mutex_unlock(&evdev->mutex); + return retval; + } + +-static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) ++static int evdev_fetch_next_event(struct evdev_client *client, ++ struct input_event *event) ++{ ++ int have_event; ++ ++ spin_lock_irq(&client->buffer_lock); ++ ++ have_event = client->head != client->tail; ++ if (have_event) { ++ *event = client->buffer[client->tail++]; ++ client->tail &= EVDEV_BUFFER_SIZE - 1; ++ } ++ ++ spin_unlock_irq(&client->buffer_lock); ++ ++ return have_event; ++} ++ ++static ssize_t evdev_read(struct file *file, char __user *buffer, ++ size_t count, loff_t *ppos) + { + struct evdev_client *client = file->private_data; + struct evdev *evdev = client->evdev; ++ struct input_event event; + int retval; + + if (count < evdev_event_size()) + return -EINVAL; + +- if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK)) ++ if (client->head == client->tail && evdev->exist && ++ (file->f_flags & O_NONBLOCK)) + return -EAGAIN; + + retval = wait_event_interruptible(evdev->wait, +@@ -307,14 +473,12 @@ static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, + if (!evdev->exist) + return -ENODEV; + +- while (client->head != client->tail && retval + evdev_event_size() <= count) { ++ while (retval + evdev_event_size() <= count && ++ evdev_fetch_next_event(client, &event)) { + +- struct input_event *event = (struct input_event *) client->buffer + client->tail; +- +- if (evdev_event_to_user(buffer + retval, event)) ++ if (evdev_event_to_user(buffer + retval, &event)) + return -EFAULT; + +- client->tail = (client->tail + 1) & (EVDEV_BUFFER_SIZE - 1); + retval += evdev_event_size(); + } + +@@ -409,8 +573,8 @@ static int str_to_user(const char *str, unsigned int maxlen, void __user *p) + return copy_to_user(p, str, len) ? -EFAULT : len; + } + +-static long evdev_ioctl_handler(struct file *file, unsigned int cmd, +- void __user *p, int compat_mode) ++static long evdev_do_ioctl(struct file *file, unsigned int cmd, ++ void __user *p, int compat_mode) + { + struct evdev_client *client = file->private_data; + struct evdev *evdev = client->evdev; +@@ -421,215 +585,289 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd, + int i, t, u, v; + int error; + +- if (!evdev->exist) +- return -ENODEV; +- + switch (cmd) { + +- case EVIOCGVERSION: +- return put_user(EV_VERSION, ip); ++ case EVIOCGVERSION: ++ return put_user(EV_VERSION, ip); + +- case EVIOCGID: +- if (copy_to_user(p, &dev->id, sizeof(struct input_id))) +- return -EFAULT; +- return 0; ++ case EVIOCGID: ++ if (copy_to_user(p, &dev->id, sizeof(struct input_id))) ++ return -EFAULT; ++ return 0; + +- case EVIOCGREP: +- if (!test_bit(EV_REP, dev->evbit)) +- return -ENOSYS; +- if (put_user(dev->rep[REP_DELAY], ip)) +- return -EFAULT; +- if (put_user(dev->rep[REP_PERIOD], ip + 1)) +- return -EFAULT; +- return 0; ++ case EVIOCGREP: ++ if (!test_bit(EV_REP, dev->evbit)) ++ return -ENOSYS; ++ if (put_user(dev->rep[REP_DELAY], ip)) ++ return -EFAULT; ++ if (put_user(dev->rep[REP_PERIOD], ip + 1)) ++ return -EFAULT; ++ return 0; + +- case EVIOCSREP: +- if (!test_bit(EV_REP, dev->evbit)) +- return -ENOSYS; +- if (get_user(u, ip)) +- return -EFAULT; +- if (get_user(v, ip + 1)) +- return -EFAULT; ++ case EVIOCSREP: ++ if (!test_bit(EV_REP, dev->evbit)) ++ return -ENOSYS; ++ if (get_user(u, ip)) ++ return -EFAULT; ++ if (get_user(v, ip + 1)) ++ return -EFAULT; + +- input_inject_event(&evdev->handle, EV_REP, REP_DELAY, u); +- input_inject_event(&evdev->handle, EV_REP, REP_PERIOD, v); ++ input_inject_event(&evdev->handle, EV_REP, REP_DELAY, u); ++ input_inject_event(&evdev->handle, EV_REP, REP_PERIOD, v); + +- return 0; ++ return 0; + +- case EVIOCGKEYCODE: +- if (get_user(t, ip)) +- return -EFAULT; ++ case EVIOCGKEYCODE: ++ if (get_user(t, ip)) ++ return -EFAULT; + +- error = dev->getkeycode(dev, t, &v); +- if (error) +- return error; ++ error = dev->getkeycode(dev, t, &v); ++ if (error) ++ return error; + +- if (put_user(v, ip + 1)) +- return -EFAULT; ++ if (put_user(v, ip + 1)) ++ return -EFAULT; + +- return 0; ++ return 0; + +- case EVIOCSKEYCODE: +- if (get_user(t, ip) || get_user(v, ip + 1)) +- return -EFAULT; ++ case EVIOCSKEYCODE: ++ if (get_user(t, ip) || get_user(v, ip + 1)) ++ return -EFAULT; + +- return dev->setkeycode(dev, t, v); ++ return dev->setkeycode(dev, t, v); + +- case EVIOCSFF: +- if (copy_from_user(&effect, p, sizeof(effect))) +- return -EFAULT; ++ case EVIOCSFF: ++ if (copy_from_user(&effect, p, sizeof(effect))) ++ return -EFAULT; + +- error = input_ff_upload(dev, &effect, file); ++ error = input_ff_upload(dev, &effect, file); + +- if (put_user(effect.id, &(((struct ff_effect __user *)p)->id))) +- return -EFAULT; ++ if (put_user(effect.id, &(((struct ff_effect __user *)p)->id))) ++ return -EFAULT; + +- return error; ++ return error; + +- case EVIOCRMFF: +- return input_ff_erase(dev, (int)(unsigned long) p, file); ++ case EVIOCRMFF: ++ return input_ff_erase(dev, (int)(unsigned long) p, file); + +- case EVIOCGEFFECTS: +- i = test_bit(EV_FF, dev->evbit) ? dev->ff->max_effects : 0; +- if (put_user(i, ip)) +- return -EFAULT; +- return 0; +- +- case EVIOCGRAB: +- if (p) { +- if (evdev->grab) +- return -EBUSY; +- if (input_grab_device(&evdev->handle)) +- return -EBUSY; +- evdev->grab = client; +- return 0; +- } else { +- if (evdev->grab != client) +- return -EINVAL; +- input_release_device(&evdev->handle); +- evdev->grab = NULL; +- return 0; +- } ++ case EVIOCGEFFECTS: ++ i = test_bit(EV_FF, dev->evbit) ? ++ dev->ff->max_effects : 0; ++ if (put_user(i, ip)) ++ return -EFAULT; ++ return 0; + +- default: ++ case EVIOCGRAB: ++ if (p) ++ return evdev_grab(evdev, client); ++ else ++ return evdev_ungrab(evdev, client); + +- if (_IOC_TYPE(cmd) != 'E') +- return -EINVAL; ++ default: + +- if (_IOC_DIR(cmd) == _IOC_READ) { ++ if (_IOC_TYPE(cmd) != 'E') ++ return -EINVAL; + +- if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0,0))) { ++ if (_IOC_DIR(cmd) == _IOC_READ) { + +- unsigned long *bits; +- int len; ++ if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0, 0))) { + +- switch (_IOC_NR(cmd) & EV_MAX) { +- case 0: bits = dev->evbit; len = EV_MAX; break; +- case EV_KEY: bits = dev->keybit; len = KEY_MAX; break; +- case EV_REL: bits = dev->relbit; len = REL_MAX; break; +- case EV_ABS: bits = dev->absbit; len = ABS_MAX; break; +- case EV_MSC: bits = dev->mscbit; len = MSC_MAX; break; +- case EV_LED: bits = dev->ledbit; len = LED_MAX; break; +- case EV_SND: bits = dev->sndbit; len = SND_MAX; break; +- case EV_FF: bits = dev->ffbit; len = FF_MAX; break; +- case EV_SW: bits = dev->swbit; len = SW_MAX; break; +- default: return -EINVAL; +- } +- return bits_to_user(bits, len, _IOC_SIZE(cmd), p, compat_mode); +- } ++ unsigned long *bits; ++ int len; + +- if (_IOC_NR(cmd) == _IOC_NR(EVIOCGKEY(0))) +- return bits_to_user(dev->key, KEY_MAX, _IOC_SIZE(cmd), +- p, compat_mode); ++ switch (_IOC_NR(cmd) & EV_MAX) { + +- if (_IOC_NR(cmd) == _IOC_NR(EVIOCGLED(0))) +- return bits_to_user(dev->led, LED_MAX, _IOC_SIZE(cmd), +- p, compat_mode); ++ case 0: bits = dev->evbit; len = EV_MAX; break; ++ case EV_KEY: bits = dev->keybit; len = KEY_MAX; break; ++ case EV_REL: bits = dev->relbit; len = REL_MAX; break; ++ case EV_ABS: bits = dev->absbit; len = ABS_MAX; break; ++ case EV_MSC: bits = dev->mscbit; len = MSC_MAX; break; ++ case EV_LED: bits = dev->ledbit; len = LED_MAX; break; ++ case EV_SND: bits = dev->sndbit; len = SND_MAX; break; ++ case EV_FF: bits = dev->ffbit; len = FF_MAX; break; ++ case EV_SW: bits = dev->swbit; len = SW_MAX; break; ++ default: return -EINVAL; ++ } ++ return bits_to_user(bits, len, _IOC_SIZE(cmd), p, compat_mode); ++ } + +- if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSND(0))) +- return bits_to_user(dev->snd, SND_MAX, _IOC_SIZE(cmd), +- p, compat_mode); ++ if (_IOC_NR(cmd) == _IOC_NR(EVIOCGKEY(0))) ++ return bits_to_user(dev->key, KEY_MAX, _IOC_SIZE(cmd), ++ p, compat_mode); + +- if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSW(0))) +- return bits_to_user(dev->sw, SW_MAX, _IOC_SIZE(cmd), +- p, compat_mode); ++ if (_IOC_NR(cmd) == _IOC_NR(EVIOCGLED(0))) ++ return bits_to_user(dev->led, LED_MAX, _IOC_SIZE(cmd), ++ p, compat_mode); + +- if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0))) +- return str_to_user(dev->name, _IOC_SIZE(cmd), p); ++ if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSND(0))) ++ return bits_to_user(dev->snd, SND_MAX, _IOC_SIZE(cmd), ++ p, compat_mode); + +- if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0))) +- return str_to_user(dev->phys, _IOC_SIZE(cmd), p); ++ if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSW(0))) ++ return bits_to_user(dev->sw, SW_MAX, _IOC_SIZE(cmd), ++ p, compat_mode); + +- if (_IOC_NR(cmd) == _IOC_NR(EVIOCGUNIQ(0))) +- return str_to_user(dev->uniq, _IOC_SIZE(cmd), p); ++ if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0))) ++ return str_to_user(dev->name, _IOC_SIZE(cmd), p); + +- if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) { ++ if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0))) ++ return str_to_user(dev->phys, _IOC_SIZE(cmd), p); + +- t = _IOC_NR(cmd) & ABS_MAX; ++ if (_IOC_NR(cmd) == _IOC_NR(EVIOCGUNIQ(0))) ++ return str_to_user(dev->uniq, _IOC_SIZE(cmd), p); + +- abs.value = dev->abs[t]; +- abs.minimum = dev->absmin[t]; +- abs.maximum = dev->absmax[t]; +- abs.fuzz = dev->absfuzz[t]; +- abs.flat = dev->absflat[t]; ++ if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) { + +- if (copy_to_user(p, &abs, sizeof(struct input_absinfo))) +- return -EFAULT; ++ t = _IOC_NR(cmd) & ABS_MAX; + +- return 0; +- } ++ abs.value = dev->abs[t]; ++ abs.minimum = dev->absmin[t]; ++ abs.maximum = dev->absmax[t]; ++ abs.fuzz = dev->absfuzz[t]; ++ abs.flat = dev->absflat[t]; + ++ if (copy_to_user(p, &abs, sizeof(struct input_absinfo))) ++ return -EFAULT; ++ ++ return 0; + } + +- if (_IOC_DIR(cmd) == _IOC_WRITE) { ++ } + +- if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) { ++ if (_IOC_DIR(cmd) == _IOC_WRITE) { + +- t = _IOC_NR(cmd) & ABS_MAX; ++ if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) { + +- if (copy_from_user(&abs, p, sizeof(struct input_absinfo))) +- return -EFAULT; ++ t = _IOC_NR(cmd) & ABS_MAX; + +- dev->abs[t] = abs.value; +- dev->absmin[t] = abs.minimum; +- dev->absmax[t] = abs.maximum; +- dev->absfuzz[t] = abs.fuzz; +- dev->absflat[t] = abs.flat; ++ if (copy_from_user(&abs, p, ++ sizeof(struct input_absinfo))) ++ return -EFAULT; + +- return 0; +- } ++ /* ++ * Take event lock to ensure that we are not ++ * changing device parameters in the middle ++ * of event. ++ */ ++ spin_lock_irq(&dev->event_lock); ++ ++ dev->abs[t] = abs.value; ++ dev->absmin[t] = abs.minimum; ++ dev->absmax[t] = abs.maximum; ++ dev->absfuzz[t] = abs.fuzz; ++ dev->absflat[t] = abs.flat; ++ ++ spin_unlock_irq(&dev->event_lock); ++ ++ return 0; + } ++ } + } + return -EINVAL; + } + ++static long evdev_ioctl_handler(struct file *file, unsigned int cmd, ++ void __user *p, int compat_mode) ++{ ++ struct evdev_client *client = file->private_data; ++ struct evdev *evdev = client->evdev; ++ int retval; ++ ++ retval = mutex_lock_interruptible(&evdev->mutex); ++ if (retval) ++ return retval; ++ ++ if (!evdev->exist) { ++ retval = -ENODEV; ++ goto out; ++ } ++ ++ retval = evdev_do_ioctl(file, cmd, p, compat_mode); ++ ++ out: ++ mutex_unlock(&evdev->mutex); ++ return retval; ++} ++ + static long evdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) + { + return evdev_ioctl_handler(file, cmd, (void __user *)arg, 0); + } + + #ifdef CONFIG_COMPAT +-static long evdev_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) ++static long evdev_ioctl_compat(struct file *file, ++ unsigned int cmd, unsigned long arg) + { + return evdev_ioctl_handler(file, cmd, compat_ptr(arg), 1); + } + #endif + + static const struct file_operations evdev_fops = { +- .owner = THIS_MODULE, +- .read = evdev_read, +- .write = evdev_write, +- .poll = evdev_poll, +- .open = evdev_open, +- .release = evdev_release, +- .unlocked_ioctl = evdev_ioctl, ++ .owner = THIS_MODULE, ++ .read = evdev_read, ++ .write = evdev_write, ++ .poll = evdev_poll, ++ .open = evdev_open, ++ .release = evdev_release, ++ .unlocked_ioctl = evdev_ioctl, + #ifdef CONFIG_COMPAT +- .compat_ioctl = evdev_ioctl_compat, ++ .compat_ioctl = evdev_ioctl_compat, + #endif +- .fasync = evdev_fasync, +- .flush = evdev_flush ++ .fasync = evdev_fasync, ++ .flush = evdev_flush + }; + ++static int evdev_install_chrdev(struct evdev *evdev) ++{ ++ /* ++ * No need to do any locking here as calls to connect and ++ * disconnect are serialized by the input core ++ */ ++ evdev_table[evdev->minor] = evdev; ++ return 0; ++} ++ ++static void evdev_remove_chrdev(struct evdev *evdev) ++{ ++ /* ++ * Lock evdev table to prevent race with evdev_open() ++ */ ++ mutex_lock(&evdev_table_mutex); ++ evdev_table[evdev->minor] = NULL; ++ mutex_unlock(&evdev_table_mutex); ++} ++ ++/* ++ * Mark device non-existent. This disables writes, ioctls and ++ * prevents new users from opening the device. Already posted ++ * blocking reads will stay, however new ones will fail. ++ */ ++static void evdev_mark_dead(struct evdev *evdev) ++{ ++ mutex_lock(&evdev->mutex); ++ evdev->exist = 0; ++ mutex_unlock(&evdev->mutex); ++} ++ ++static void evdev_cleanup(struct evdev *evdev) ++{ ++ struct input_handle *handle = &evdev->handle; ++ ++ evdev_mark_dead(evdev); ++ evdev_hangup(evdev); ++ evdev_remove_chrdev(evdev); ++ ++ /* evdev is marked dead so no one else accesses evdev->open */ ++ if (evdev->open) { ++ input_flush_device(handle, NULL); ++ input_close_device(handle); ++ } ++} ++ ++/* ++ * Create new evdev device. Note that input core serializes calls ++ * to connect and disconnect so we don't need to lock evdev_table here. ++ */ + static int evdev_connect(struct input_handler *handler, struct input_dev *dev, + const struct input_device_id *id) + { +@@ -637,7 +875,10 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev, + int minor; + int error; + +- for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++); ++ for (minor = 0; minor < EVDEV_MINORS; minor++) ++ if (!evdev_table[minor]) ++ break; ++ + if (minor == EVDEV_MINORS) { + printk(KERN_ERR "evdev: no more free evdev devices\n"); + return -ENFILE; +@@ -648,38 +889,44 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev, + return -ENOMEM; + + INIT_LIST_HEAD(&evdev->client_list); ++ spin_lock_init(&evdev->client_lock); ++ mutex_init(&evdev->mutex); + init_waitqueue_head(&evdev->wait); + ++ snprintf(evdev->name, sizeof(evdev->name), "event%d", minor); + evdev->exist = 1; + evdev->minor = minor; ++ + evdev->handle.dev = dev; + evdev->handle.name = evdev->name; + evdev->handle.handler = handler; + evdev->handle.private = evdev; +- snprintf(evdev->name, sizeof(evdev->name), "event%d", minor); + +- snprintf(evdev->dev.bus_id, sizeof(evdev->dev.bus_id), +- "event%d", minor); ++ strlcpy(evdev->dev.bus_id, evdev->name, sizeof(evdev->dev.bus_id)); ++ evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor); + evdev->dev.class = &input_class; + evdev->dev.parent = &dev->dev; +- evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor); + evdev->dev.release = evdev_free; + device_initialize(&evdev->dev); + +- evdev_table[minor] = evdev; +- +- error = device_add(&evdev->dev); ++ error = input_register_handle(&evdev->handle); + if (error) + goto err_free_evdev; + +- error = input_register_handle(&evdev->handle); ++ error = evdev_install_chrdev(evdev); ++ if (error) ++ goto err_unregister_handle; ++ ++ error = device_add(&evdev->dev); + if (error) +- goto err_delete_evdev; ++ goto err_cleanup_evdev; + + return 0; + +- err_delete_evdev: +- device_del(&evdev->dev); ++ err_cleanup_evdev: ++ evdev_cleanup(evdev); ++ err_unregister_handle: ++ input_unregister_handle(&evdev->handle); + err_free_evdev: + put_device(&evdev->dev); + return error; +@@ -688,21 +935,10 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev, + static void evdev_disconnect(struct input_handle *handle) + { + struct evdev *evdev = handle->private; +- struct evdev_client *client; + +- input_unregister_handle(handle); + device_del(&evdev->dev); +- +- evdev->exist = 0; +- +- if (evdev->open) { +- input_flush_device(handle, NULL); +- input_close_device(handle); +- list_for_each_entry(client, &evdev->client_list, node) +- kill_fasync(&client->fasync, SIGIO, POLL_HUP); +- wake_up_interruptible(&evdev->wait); +- } +- ++ evdev_cleanup(evdev); ++ input_unregister_handle(handle); + put_device(&evdev->dev); + } + +@@ -714,13 +950,13 @@ static const struct input_device_id evdev_ids[] = { + MODULE_DEVICE_TABLE(input, evdev_ids); + + static struct input_handler evdev_handler = { +- .event = evdev_event, +- .connect = evdev_connect, +- .disconnect = evdev_disconnect, +- .fops = &evdev_fops, +- .minor = EVDEV_MINOR_BASE, +- .name = "evdev", +- .id_table = evdev_ids, ++ .event = evdev_event, ++ .connect = evdev_connect, ++ .disconnect = evdev_disconnect, ++ .fops = &evdev_fops, ++ .minor = EVDEV_MINOR_BASE, ++ .name = "evdev", ++ .id_table = evdev_ids, + }; + + static int __init evdev_init(void) +diff --git a/drivers/input/input.c b/drivers/input/input.c +index 5fe7555..c59544f 100644 +--- a/drivers/input/input.c ++++ b/drivers/input/input.c +@@ -17,10 +17,10 @@ + #include + #include + #include +-#include + #include + #include + #include ++#include + + MODULE_AUTHOR("Vojtech Pavlik "); + MODULE_DESCRIPTION("Input core"); +@@ -31,167 +31,244 @@ MODULE_LICENSE("GPL"); + static LIST_HEAD(input_dev_list); + static LIST_HEAD(input_handler_list); + ++/* ++ * input_mutex protects access to both input_dev_list and input_handler_list. ++ * This also causes input_[un]register_device and input_[un]register_handler ++ * be mutually exclusive which simplifies locking in drivers implementing ++ * input handlers. ++ */ ++static DEFINE_MUTEX(input_mutex); ++ + static struct input_handler *input_table[8]; + +-/** +- * input_event() - report new input event +- * @dev: device that generated the event +- * @type: type of the event +- * @code: event code +- * @value: value of the event +- * +- * This function should be used by drivers implementing various input devices +- * See also input_inject_event() +- */ +-void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) ++static inline int is_event_supported(unsigned int code, ++ unsigned long *bm, unsigned int max) + { +- struct input_handle *handle; ++ return code <= max && test_bit(code, bm); ++} + +- if (type > EV_MAX || !test_bit(type, dev->evbit)) +- return; ++static int input_defuzz_abs_event(int value, int old_val, int fuzz) ++{ ++ if (fuzz) { ++ if (value > old_val - fuzz / 2 && value < old_val + fuzz / 2) ++ return old_val; + +- add_input_randomness(type, code, value); ++ if (value > old_val - fuzz && value < old_val + fuzz) ++ return (old_val * 3 + value) / 4; + +- switch (type) { ++ if (value > old_val - fuzz * 2 && value < old_val + fuzz * 2) ++ return (old_val + value) / 2; ++ } + +- case EV_SYN: +- switch (code) { +- case SYN_CONFIG: +- if (dev->event) +- dev->event(dev, type, code, value); +- break; +- +- case SYN_REPORT: +- if (dev->sync) +- return; +- dev->sync = 1; +- break; +- } +- break; ++ return value; ++} + +- case EV_KEY: ++/* ++ * Pass event through all open handles. This function is called with ++ * dev->event_lock held and interrupts disabled. Because of that we ++ * do not need to use rcu_read_lock() here although we are using RCU ++ * to access handle list. Note that because of that write-side uses ++ * synchronize_sched() instead of synchronize_ru(). ++ */ ++static void input_pass_event(struct input_dev *dev, ++ unsigned int type, unsigned int code, int value) ++{ ++ struct input_handle *handle = rcu_dereference(dev->grab); + +- if (code > KEY_MAX || !test_bit(code, dev->keybit) || !!test_bit(code, dev->key) == value) +- return; ++ if (handle) ++ handle->handler->event(handle, type, code, value); ++ else ++ list_for_each_entry_rcu(handle, &dev->h_list, d_node) ++ if (handle->open) ++ handle->handler->event(handle, ++ type, code, value); ++} + +- if (value == 2) +- break; ++/* ++ * Generate software autorepeat event. Note that we take ++ * dev->event_lock here to avoid racing with input_event ++ * which may cause keys get "stuck". ++ */ ++static void input_repeat_key(unsigned long data) ++{ ++ struct input_dev *dev = (void *) data; ++ unsigned long flags; + +- change_bit(code, dev->key); ++ spin_lock_irqsave(&dev->event_lock, flags); + +- if (test_bit(EV_REP, dev->evbit) && dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] && dev->timer.data && value) { +- dev->repeat_key = code; +- mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_DELAY])); +- } ++ if (test_bit(dev->repeat_key, dev->key) && ++ is_event_supported(dev->repeat_key, dev->keybit, KEY_MAX)) { + +- break; ++ input_pass_event(dev, EV_KEY, dev->repeat_key, 2); + +- case EV_SW: ++ if (dev->sync) { ++ /* ++ * Only send SYN_REPORT if we are not in a middle ++ * of driver parsing a new hardware packet. ++ * Otherwise assume that the driver will send ++ * SYN_REPORT once it's done. ++ */ ++ input_pass_event(dev, EV_SYN, SYN_REPORT, 1); ++ } + +- if (code > SW_MAX || !test_bit(code, dev->swbit) || !!test_bit(code, dev->sw) == value) +- return; ++ if (dev->rep[REP_PERIOD]) ++ mod_timer(&dev->timer, jiffies + ++ msecs_to_jiffies(dev->rep[REP_PERIOD])); ++ } + +- change_bit(code, dev->sw); ++ spin_unlock_irqrestore(&dev->event_lock, flags); ++} + +- break; ++static void input_start_autorepeat(struct input_dev *dev, int code) ++{ ++ if (test_bit(EV_REP, dev->evbit) && ++ dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] && ++ dev->timer.data) { ++ dev->repeat_key = code; ++ mod_timer(&dev->timer, ++ jiffies + msecs_to_jiffies(dev->rep[REP_DELAY])); ++ } ++} + +- case EV_ABS: ++#define INPUT_IGNORE_EVENT 0 ++#define INPUT_PASS_TO_HANDLERS 1 ++#define INPUT_PASS_TO_DEVICE 2 ++#define INPUT_PASS_TO_ALL (INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE) + +- if (code > ABS_MAX || !test_bit(code, dev->absbit)) +- return; ++static void input_handle_event(struct input_dev *dev, ++ unsigned int type, unsigned int code, int value) ++{ ++ int disposition = INPUT_IGNORE_EVENT; + +- if (dev->absfuzz[code]) { +- if ((value > dev->abs[code] - (dev->absfuzz[code] >> 1)) && +- (value < dev->abs[code] + (dev->absfuzz[code] >> 1))) +- return; ++ switch (type) { + +- if ((value > dev->abs[code] - dev->absfuzz[code]) && +- (value < dev->abs[code] + dev->absfuzz[code])) +- value = (dev->abs[code] * 3 + value) >> 2; ++ case EV_SYN: ++ switch (code) { ++ case SYN_CONFIG: ++ disposition = INPUT_PASS_TO_ALL; ++ break; + +- if ((value > dev->abs[code] - (dev->absfuzz[code] << 1)) && +- (value < dev->abs[code] + (dev->absfuzz[code] << 1))) +- value = (dev->abs[code] + value) >> 1; ++ case SYN_REPORT: ++ if (!dev->sync) { ++ dev->sync = 1; ++ disposition = INPUT_PASS_TO_HANDLERS; + } +- +- if (dev->abs[code] == value) +- return; +- +- dev->abs[code] = value; + break; ++ } ++ break; + +- case EV_REL: ++ case EV_KEY: ++ if (is_event_supported(code, dev->keybit, KEY_MAX) && ++ !!test_bit(code, dev->key) != value) { + +- if (code > REL_MAX || !test_bit(code, dev->relbit) || (value == 0)) +- return; ++ if (value != 2) { ++ __change_bit(code, dev->key); ++ if (value) ++ input_start_autorepeat(dev, code); ++ } + +- break; ++ disposition = INPUT_PASS_TO_HANDLERS; ++ } ++ break; + +- case EV_MSC: ++ case EV_SW: ++ if (is_event_supported(code, dev->swbit, SW_MAX) && ++ !!test_bit(code, dev->sw) != value) { + +- if (code > MSC_MAX || !test_bit(code, dev->mscbit)) +- return; ++ __change_bit(code, dev->sw); ++ disposition = INPUT_PASS_TO_HANDLERS; ++ } ++ break; + +- if (dev->event) +- dev->event(dev, type, code, value); ++ case EV_ABS: ++ if (is_event_supported(code, dev->absbit, ABS_MAX)) { + +- break; ++ value = input_defuzz_abs_event(value, ++ dev->abs[code], dev->absfuzz[code]); + +- case EV_LED: ++ if (dev->abs[code] != value) { ++ dev->abs[code] = value; ++ disposition = INPUT_PASS_TO_HANDLERS; ++ } ++ } ++ break; + +- if (code > LED_MAX || !test_bit(code, dev->ledbit) || !!test_bit(code, dev->led) == value) +- return; ++ case EV_REL: ++ if (is_event_supported(code, dev->relbit, REL_MAX) && value) ++ disposition = INPUT_PASS_TO_HANDLERS; + +- change_bit(code, dev->led); ++ break; + +- if (dev->event) +- dev->event(dev, type, code, value); ++ case EV_MSC: ++ if (is_event_supported(code, dev->mscbit, MSC_MAX)) ++ disposition = INPUT_PASS_TO_ALL; + +- break; ++ break; ++ ++ case EV_LED: ++ if (is_event_supported(code, dev->ledbit, LED_MAX) && ++ !!test_bit(code, dev->led) != value) { + +- case EV_SND: ++ __change_bit(code, dev->led); ++ disposition = INPUT_PASS_TO_ALL; ++ } ++ break; + +- if (code > SND_MAX || !test_bit(code, dev->sndbit)) +- return; ++ case EV_SND: ++ if (is_event_supported(code, dev->sndbit, SND_MAX)) { + + if (!!test_bit(code, dev->snd) != !!value) +- change_bit(code, dev->snd); ++ __change_bit(code, dev->snd); ++ disposition = INPUT_PASS_TO_ALL; ++ } ++ break; + +- if (dev->event) +- dev->event(dev, type, code, value); ++ case EV_REP: ++ if (code <= REP_MAX && value >= 0 && dev->rep[code] != value) { ++ dev->rep[code] = value; ++ disposition = INPUT_PASS_TO_ALL; ++ } ++ break; + +- break; ++ case EV_FF: ++ if (value >= 0) ++ disposition = INPUT_PASS_TO_ALL; ++ break; ++ } + +- case EV_REP: ++ if (type != EV_SYN) ++ dev->sync = 0; + +- if (code > REP_MAX || value < 0 || dev->rep[code] == value) +- return; ++ if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event) ++ dev->event(dev, type, code, value); + +- dev->rep[code] = value; +- if (dev->event) +- dev->event(dev, type, code, value); ++ if (disposition & INPUT_PASS_TO_HANDLERS) ++ input_pass_event(dev, type, code, value); ++} + +- break; ++/** ++ * input_event() - report new input event ++ * @dev: device that generated the event ++ * @type: type of the event ++ * @code: event code ++ * @value: value of the event ++ * ++ * This function should be used by drivers implementing various input ++ * devices. See also input_inject_event(). ++ */ + +- case EV_FF: ++void input_event(struct input_dev *dev, ++ unsigned int type, unsigned int code, int value) ++{ ++ unsigned long flags; + +- if (value < 0) +- return; ++ if (is_event_supported(type, dev->evbit, EV_MAX)) { + +- if (dev->event) +- dev->event(dev, type, code, value); +- break; ++ spin_lock_irqsave(&dev->event_lock, flags); ++ add_input_randomness(type, code, value); ++ input_handle_event(dev, type, code, value); ++ spin_unlock_irqrestore(&dev->event_lock, flags); + } +- +- if (type != EV_SYN) +- dev->sync = 0; +- +- if (dev->grab) +- dev->grab->handler->event(dev->grab, type, code, value); +- else +- list_for_each_entry(handle, &dev->h_list, d_node) +- if (handle->open) +- handle->handler->event(handle, type, code, value); + } + EXPORT_SYMBOL(input_event); + +@@ -202,102 +279,230 @@ EXPORT_SYMBOL(input_event); + * @code: event code + * @value: value of the event + * +- * Similar to input_event() but will ignore event if device is "grabbed" and handle +- * injecting event is not the one that owns the device. ++ * Similar to input_event() but will ignore event if device is ++ * "grabbed" and handle injecting event is not the one that owns ++ * the device. + */ +-void input_inject_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) ++void input_inject_event(struct input_handle *handle, ++ unsigned int type, unsigned int code, int value) + { +- if (!handle->dev->grab || handle->dev->grab == handle) +- input_event(handle->dev, type, code, value); +-} +-EXPORT_SYMBOL(input_inject_event); +- +-static void input_repeat_key(unsigned long data) +-{ +- struct input_dev *dev = (void *) data; ++ struct input_dev *dev = handle->dev; ++ struct input_handle *grab; ++ unsigned long flags; + +- if (!test_bit(dev->repeat_key, dev->key)) +- return; ++ if (is_event_supported(type, dev->evbit, EV_MAX)) { ++ spin_lock_irqsave(&dev->event_lock, flags); + +- input_event(dev, EV_KEY, dev->repeat_key, 2); +- input_sync(dev); ++ grab = rcu_dereference(dev->grab); ++ if (!grab || grab == handle) ++ input_handle_event(dev, type, code, value); + +- if (dev->rep[REP_PERIOD]) +- mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_PERIOD])); ++ spin_unlock_irqrestore(&dev->event_lock, flags); ++ } + } ++EXPORT_SYMBOL(input_inject_event); + ++/** ++ * input_grab_device - grabs device for exclusive use ++ * @handle: input handle that wants to own the device ++ * ++ * When a device is grabbed by an input handle all events generated by ++ * the device are delivered only to this handle. Also events injected ++ * by other input handles are ignored while device is grabbed. ++ */ + int input_grab_device(struct input_handle *handle) + { +- if (handle->dev->grab) +- return -EBUSY; ++ struct input_dev *dev = handle->dev; ++ int retval; + +- handle->dev->grab = handle; +- return 0; ++ retval = mutex_lock_interruptible(&dev->mutex); ++ if (retval) ++ return retval; ++ ++ if (dev->grab) { ++ retval = -EBUSY; ++ goto out; ++ } ++ ++ rcu_assign_pointer(dev->grab, handle); ++ /* ++ * Not using synchronize_rcu() because read-side is protected ++ * by a spinlock with interrupts off instead of rcu_read_lock(). ++ */ ++ synchronize_sched(); ++ ++ out: ++ mutex_unlock(&dev->mutex); ++ return retval; + } + EXPORT_SYMBOL(input_grab_device); + +-void input_release_device(struct input_handle *handle) ++static void __input_release_device(struct input_handle *handle) + { + struct input_dev *dev = handle->dev; + + if (dev->grab == handle) { +- dev->grab = NULL; ++ rcu_assign_pointer(dev->grab, NULL); ++ /* Make sure input_pass_event() notices that grab is gone */ ++ synchronize_sched(); + + list_for_each_entry(handle, &dev->h_list, d_node) +- if (handle->handler->start) ++ if (handle->open && handle->handler->start) + handle->handler->start(handle); + } + } ++ ++/** ++ * input_release_device - release previously grabbed device ++ * @handle: input handle that owns the device ++ * ++ * Releases previously grabbed device so that other input handles can ++ * start receiving input events. Upon release all handlers attached ++ * to the device have their start() method called so they have a change ++ * to synchronize device state with the rest of the system. ++ */ ++void input_release_device(struct input_handle *handle) ++{ ++ struct input_dev *dev = handle->dev; ++ ++ mutex_lock(&dev->mutex); ++ __input_release_device(handle); ++ mutex_unlock(&dev->mutex); ++} + EXPORT_SYMBOL(input_release_device); + ++/** ++ * input_open_device - open input device ++ * @handle: handle through which device is being accessed ++ * ++ * This function should be called by input handlers when they ++ * want to start receive events from given input device. ++ */ + int input_open_device(struct input_handle *handle) + { + struct input_dev *dev = handle->dev; +- int err; ++ int retval; + +- err = mutex_lock_interruptible(&dev->mutex); +- if (err) +- return err; ++ retval = mutex_lock_interruptible(&dev->mutex); ++ if (retval) ++ return retval; ++ ++ if (dev->going_away) { ++ retval = -ENODEV; ++ goto out; ++ } + + handle->open++; + + if (!dev->users++ && dev->open) +- err = dev->open(dev); +- +- if (err) +- handle->open--; ++ retval = dev->open(dev); ++ ++ if (retval) { ++ dev->users--; ++ if (!--handle->open) { ++ /* ++ * Make sure we are not delivering any more events ++ * through this handle ++ */ ++ synchronize_sched(); ++ } ++ } + ++ out: + mutex_unlock(&dev->mutex); +- +- return err; ++ return retval; + } + EXPORT_SYMBOL(input_open_device); + +-int input_flush_device(struct input_handle* handle, struct file* file) ++int input_flush_device(struct input_handle *handle, struct file *file) + { +- if (handle->dev->flush) +- return handle->dev->flush(handle->dev, file); ++ struct input_dev *dev = handle->dev; ++ int retval; + +- return 0; ++ retval = mutex_lock_interruptible(&dev->mutex); ++ if (retval) ++ return retval; ++ ++ if (dev->flush) ++ retval = dev->flush(dev, file); ++ ++ mutex_unlock(&dev->mutex); ++ return retval; + } + EXPORT_SYMBOL(input_flush_device); + ++/** ++ * input_close_device - close input device ++ * @handle: handle through which device is being accessed ++ * ++ * This function should be called by input handlers when they ++ * want to stop receive events from given input device. ++ */ + void input_close_device(struct input_handle *handle) + { + struct input_dev *dev = handle->dev; + +- input_release_device(handle); +- + mutex_lock(&dev->mutex); + ++ __input_release_device(handle); ++ + if (!--dev->users && dev->close) + dev->close(dev); +- handle->open--; ++ ++ if (!--handle->open) { ++ /* ++ * synchronize_sched() makes sure that input_pass_event() ++ * completed and that no more input events are delivered ++ * through this handle ++ */ ++ synchronize_sched(); ++ } + + mutex_unlock(&dev->mutex); + } + EXPORT_SYMBOL(input_close_device); + ++/* ++ * Prepare device for unregistering ++ */ ++static void input_disconnect_device(struct input_dev *dev) ++{ ++ struct input_handle *handle; ++ int code; ++ ++ /* ++ * Mark device as going away. Note that we take dev->mutex here ++ * not to protect access to dev->going_away but rather to ensure ++ * that there are no threads in the middle of input_open_device() ++ */ ++ mutex_lock(&dev->mutex); ++ dev->going_away = 1; ++ mutex_unlock(&dev->mutex); ++ ++ spin_lock_irq(&dev->event_lock); ++ ++ /* ++ * Simulate keyup events for all pressed keys so that handlers ++ * are not left with "stuck" keys. The driver may continue ++ * generate events even after we done here but they will not ++ * reach any handlers. ++ */ ++ if (is_event_supported(EV_KEY, dev->evbit, EV_MAX)) { ++ for (code = 0; code <= KEY_MAX; code++) { ++ if (is_event_supported(code, dev->keybit, KEY_MAX) && ++ test_bit(code, dev->key)) { ++ input_pass_event(dev, EV_KEY, code, 0); ++ } ++ } ++ input_pass_event(dev, EV_SYN, SYN_REPORT, 1); ++ } ++ ++ list_for_each_entry(handle, &dev->h_list, d_node) ++ handle->open = 0; ++ ++ spin_unlock_irq(&dev->event_lock); ++} ++ + static int input_fetch_keycode(struct input_dev *dev, int scancode) + { + switch (dev->keycodesize) { +@@ -473,7 +678,8 @@ static unsigned int input_proc_devices_poll(struct file *file, poll_table *wait) + + static void *input_devices_seq_start(struct seq_file *seq, loff_t *pos) + { +- /* acquire lock here ... Yes, we do need locking, I knowi, I know... */ ++ if (mutex_lock_interruptible(&input_mutex)) ++ return NULL; + + return seq_list_start(&input_dev_list, *pos); + } +@@ -485,7 +691,7 @@ static void *input_devices_seq_next(struct seq_file *seq, void *v, loff_t *pos) + + static void input_devices_seq_stop(struct seq_file *seq, void *v) + { +- /* release lock here */ ++ mutex_unlock(&input_mutex); + } + + static void input_seq_print_bitmap(struct seq_file *seq, const char *name, +@@ -569,7 +775,9 @@ static const struct file_operations input_devices_fileops = { + + static void *input_handlers_seq_start(struct seq_file *seq, loff_t *pos) + { +- /* acquire lock here ... Yes, we do need locking, I knowi, I know... */ ++ if (mutex_lock_interruptible(&input_mutex)) ++ return NULL; ++ + seq->private = (void *)(unsigned long)*pos; + return seq_list_start(&input_handler_list, *pos); + } +@@ -582,7 +790,7 @@ static void *input_handlers_seq_next(struct seq_file *seq, void *v, loff_t *pos) + + static void input_handlers_seq_stop(struct seq_file *seq, void *v) + { +- /* release lock here */ ++ mutex_unlock(&input_mutex); + } + + static int input_handlers_seq_show(struct seq_file *seq, void *v) +@@ -1005,6 +1213,7 @@ struct input_dev *input_allocate_device(void) + dev->dev.class = &input_class; + device_initialize(&dev->dev); + mutex_init(&dev->mutex); ++ spin_lock_init(&dev->event_lock); + INIT_LIST_HEAD(&dev->h_list); + INIT_LIST_HEAD(&dev->node); + +@@ -1022,7 +1231,7 @@ EXPORT_SYMBOL(input_allocate_device); + * This function should only be used if input_register_device() + * was not called yet or if it failed. Once device was registered + * use input_unregister_device() and memory will be freed once last +- * refrence to the device is dropped. ++ * reference to the device is dropped. + * + * Device should be allocated by input_allocate_device(). + * +@@ -1092,6 +1301,18 @@ void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int + } + EXPORT_SYMBOL(input_set_capability); + ++/** ++ * input_register_device - register device with input core ++ * @dev: device to be registered ++ * ++ * This function registers device with input core. The device must be ++ * allocated with input_allocate_device() and all it's capabilities ++ * set up before registering. ++ * If function fails the device must be freed with input_free_device(). ++ * Once device has been successfully registered it can be unregistered ++ * with input_unregister_device(); input_free_device() should not be ++ * called in this case. ++ */ + int input_register_device(struct input_dev *dev) + { + static atomic_t input_no = ATOMIC_INIT(0); +@@ -1099,7 +1320,7 @@ int input_register_device(struct input_dev *dev) + const char *path; + int error; + +- set_bit(EV_SYN, dev->evbit); ++ __set_bit(EV_SYN, dev->evbit); + + /* + * If delay and period are pre-set by the driver, then autorepeating +@@ -1120,8 +1341,6 @@ int input_register_device(struct input_dev *dev) + if (!dev->setkeycode) + dev->setkeycode = input_default_setkeycode; + +- list_add_tail(&dev->node, &input_dev_list); +- + snprintf(dev->dev.bus_id, sizeof(dev->dev.bus_id), + "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1); + +@@ -1137,49 +1356,79 @@ int input_register_device(struct input_dev *dev) + dev->name ? dev->name : "Unspecified device", path ? path : "N/A"); + kfree(path); + ++ error = mutex_lock_interruptible(&input_mutex); ++ if (error) { ++ device_del(&dev->dev); ++ return error; ++ } ++ ++ list_add_tail(&dev->node, &input_dev_list); ++ + list_for_each_entry(handler, &input_handler_list, node) + input_attach_handler(dev, handler); + + input_wakeup_procfs_readers(); + ++ mutex_unlock(&input_mutex); ++ + return 0; + } + EXPORT_SYMBOL(input_register_device); + ++/** ++ * input_unregister_device - unregister previously registered device ++ * @dev: device to be unregistered ++ * ++ * This function unregisters an input device. Once device is unregistered ++ * the caller should not try to access it as it may get freed at any moment. ++ */ + void input_unregister_device(struct input_dev *dev) + { + struct input_handle *handle, *next; +- int code; + +- for (code = 0; code <= KEY_MAX; code++) +- if (test_bit(code, dev->key)) +- input_report_key(dev, code, 0); +- input_sync(dev); ++ input_disconnect_device(dev); + +- del_timer_sync(&dev->timer); ++ mutex_lock(&input_mutex); + + list_for_each_entry_safe(handle, next, &dev->h_list, d_node) + handle->handler->disconnect(handle); + WARN_ON(!list_empty(&dev->h_list)); + ++ del_timer_sync(&dev->timer); + list_del_init(&dev->node); + +- device_unregister(&dev->dev); +- + input_wakeup_procfs_readers(); ++ ++ mutex_unlock(&input_mutex); ++ ++ device_unregister(&dev->dev); + } + EXPORT_SYMBOL(input_unregister_device); + ++/** ++ * input_register_handler - register a new input handler ++ * @handler: handler to be registered ++ * ++ * This function registers a new input handler (interface) for input ++ * devices in the system and attaches it to all input devices that ++ * are compatible with the handler. ++ */ + int input_register_handler(struct input_handler *handler) + { + struct input_dev *dev; ++ int retval; ++ ++ retval = mutex_lock_interruptible(&input_mutex); ++ if (retval) ++ return retval; + + INIT_LIST_HEAD(&handler->h_list); + + if (handler->fops != NULL) { +- if (input_table[handler->minor >> 5]) +- return -EBUSY; +- ++ if (input_table[handler->minor >> 5]) { ++ retval = -EBUSY; ++ goto out; ++ } + input_table[handler->minor >> 5] = handler; + } + +@@ -1189,14 +1438,26 @@ int input_register_handler(struct input_handler *handler) + input_attach_handler(dev, handler); + + input_wakeup_procfs_readers(); +- return 0; ++ ++ out: ++ mutex_unlock(&input_mutex); ++ return retval; + } + EXPORT_SYMBOL(input_register_handler); + ++/** ++ * input_unregister_handler - unregisters an input handler ++ * @handler: handler to be unregistered ++ * ++ * This function disconnects a handler from its input devices and ++ * removes it from lists of known handlers. ++ */ + void input_unregister_handler(struct input_handler *handler) + { + struct input_handle *handle, *next; + ++ mutex_lock(&input_mutex); ++ + list_for_each_entry_safe(handle, next, &handler->h_list, h_node) + handler->disconnect(handle); + WARN_ON(!list_empty(&handler->h_list)); +@@ -1207,14 +1468,50 @@ void input_unregister_handler(struct input_handler *handler) + input_table[handler->minor >> 5] = NULL; + + input_wakeup_procfs_readers(); ++ ++ mutex_unlock(&input_mutex); + } + EXPORT_SYMBOL(input_unregister_handler); + ++/** ++ * input_register_handle - register a new input handle ++ * @handle: handle to register ++ * ++ * This function puts a new input handle onto device's ++ * and handler's lists so that events can flow through ++ * it once it is opened using input_open_device(). ++ * ++ * This function is supposed to be called from handler's ++ * connect() method. ++ */ + int input_register_handle(struct input_handle *handle) + { + struct input_handler *handler = handle->handler; ++ struct input_dev *dev = handle->dev; ++ int error; ++ ++ /* ++ * We take dev->mutex here to prevent race with ++ * input_release_device(). ++ */ ++ error = mutex_lock_interruptible(&dev->mutex); ++ if (error) ++ return error; ++ list_add_tail_rcu(&handle->d_node, &dev->h_list); ++ mutex_unlock(&dev->mutex); ++ /* ++ * We don't use synchronize_rcu() here because we rely ++ * on dev->event_lock to protect read-side critical ++ * section in input_pass_event(). ++ */ ++ synchronize_sched(); + +- list_add_tail(&handle->d_node, &handle->dev->h_list); ++ /* ++ * Since we are supposed to be called from ->connect() ++ * which is mutually exclusive with ->disconnect() ++ * we can't be racing with input_unregister_handle() ++ * and so separate lock is not needed here. ++ */ + list_add_tail(&handle->h_node, &handler->h_list); + + if (handler->start) +@@ -1224,10 +1521,29 @@ int input_register_handle(struct input_handle *handle) + } + EXPORT_SYMBOL(input_register_handle); + ++/** ++ * input_unregister_handle - unregister an input handle ++ * @handle: handle to unregister ++ * ++ * This function removes input handle from device's ++ * and handler's lists. ++ * ++ * This function is supposed to be called from handler's ++ * disconnect() method. ++ */ + void input_unregister_handle(struct input_handle *handle) + { ++ struct input_dev *dev = handle->dev; ++ + list_del_init(&handle->h_node); +- list_del_init(&handle->d_node); ++ ++ /* ++ * Take dev->mutex to prevent race with input_release_device(). ++ */ ++ mutex_lock(&dev->mutex); ++ list_del_rcu(&handle->d_node); ++ mutex_unlock(&dev->mutex); ++ synchronize_sched(); + } + EXPORT_SYMBOL(input_unregister_handle); + +diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c +index a9a0180..f306c97 100644 +--- a/drivers/input/joydev.c ++++ b/drivers/input/joydev.c +@@ -43,6 +43,8 @@ struct joydev { + struct input_handle handle; + wait_queue_head_t wait; + struct list_head client_list; ++ spinlock_t client_lock; /* protects client_list */ ++ struct mutex mutex; + struct device dev; + + struct js_corr corr[ABS_MAX + 1]; +@@ -61,31 +63,61 @@ struct joydev_client { + int head; + int tail; + int startup; ++ spinlock_t buffer_lock; /* protects access to buffer, head and tail */ + struct fasync_struct *fasync; + struct joydev *joydev; + struct list_head node; + }; + + static struct joydev *joydev_table[JOYDEV_MINORS]; ++static DEFINE_MUTEX(joydev_table_mutex); + + static int joydev_correct(int value, struct js_corr *corr) + { + switch (corr->type) { +- case JS_CORR_NONE: +- break; +- case JS_CORR_BROKEN: +- value = value > corr->coef[0] ? (value < corr->coef[1] ? 0 : +- ((corr->coef[3] * (value - corr->coef[1])) >> 14)) : +- ((corr->coef[2] * (value - corr->coef[0])) >> 14); +- break; +- default: +- return 0; ++ ++ case JS_CORR_NONE: ++ break; ++ ++ case JS_CORR_BROKEN: ++ value = value > corr->coef[0] ? (value < corr->coef[1] ? 0 : ++ ((corr->coef[3] * (value - corr->coef[1])) >> 14)) : ++ ((corr->coef[2] * (value - corr->coef[0])) >> 14); ++ break; ++ ++ default: ++ return 0; + } + + return value < -32767 ? -32767 : (value > 32767 ? 32767 : value); + } + +-static void joydev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) ++static void joydev_pass_event(struct joydev_client *client, ++ struct js_event *event) ++{ ++ struct joydev *joydev = client->joydev; ++ ++ /* ++ * IRQs already disabled, just acquire the lock ++ */ ++ spin_lock(&client->buffer_lock); ++ ++ client->buffer[client->head] = *event; ++ ++ if (client->startup == joydev->nabs + joydev->nkey) { ++ client->head++; ++ client->head &= JOYDEV_BUFFER_SIZE - 1; ++ if (client->tail == client->head) ++ client->startup = 0; ++ } ++ ++ spin_unlock(&client->buffer_lock); ++ ++ kill_fasync(&client->fasync, SIGIO, POLL_IN); ++} ++ ++static void joydev_event(struct input_handle *handle, ++ unsigned int type, unsigned int code, int value) + { + struct joydev *joydev = handle->private; + struct joydev_client *client; +@@ -93,39 +125,32 @@ static void joydev_event(struct input_handle *handle, unsigned int type, unsigne + + switch (type) { + +- case EV_KEY: +- if (code < BTN_MISC || value == 2) +- return; +- event.type = JS_EVENT_BUTTON; +- event.number = joydev->keymap[code - BTN_MISC]; +- event.value = value; +- break; +- +- case EV_ABS: +- event.type = JS_EVENT_AXIS; +- event.number = joydev->absmap[code]; +- event.value = joydev_correct(value, joydev->corr + event.number); +- if (event.value == joydev->abs[event.number]) +- return; +- joydev->abs[event.number] = event.value; +- break; ++ case EV_KEY: ++ if (code < BTN_MISC || value == 2) ++ return; ++ event.type = JS_EVENT_BUTTON; ++ event.number = joydev->keymap[code - BTN_MISC]; ++ event.value = value; ++ break; + +- default: ++ case EV_ABS: ++ event.type = JS_EVENT_AXIS; ++ event.number = joydev->absmap[code]; ++ event.value = joydev_correct(value, ++ &joydev->corr[event.number]); ++ if (event.value == joydev->abs[event.number]) + return; ++ joydev->abs[event.number] = event.value; ++ break; ++ ++ default: ++ return; + } + + event.time = jiffies_to_msecs(jiffies); + +- list_for_each_entry(client, &joydev->client_list, node) { +- +- memcpy(client->buffer + client->head, &event, sizeof(struct js_event)); +- +- if (client->startup == joydev->nabs + joydev->nkey) +- if (client->tail == (client->head = (client->head + 1) & (JOYDEV_BUFFER_SIZE - 1))) +- client->startup = 0; +- +- kill_fasync(&client->fasync, SIGIO, POLL_IN); +- } ++ list_for_each_entry_rcu(client, &joydev->client_list, node) ++ joydev_pass_event(client, &event); + + wake_up_interruptible(&joydev->wait); + } +@@ -144,23 +169,88 @@ static void joydev_free(struct device *dev) + { + struct joydev *joydev = container_of(dev, struct joydev, dev); + +- joydev_table[joydev->minor] = NULL; + kfree(joydev); + } + ++static void joydev_attach_client(struct joydev *joydev, ++ struct joydev_client *client) ++{ ++ spin_lock(&joydev->client_lock); ++ list_add_tail_rcu(&client->node, &joydev->client_list); ++ spin_unlock(&joydev->client_lock); ++ /* ++ * We don't use synchronize_rcu() here because read-side ++ * critical section is protected by a spinlock (dev->event_lock) ++ * instead of rcu_read_lock(). ++ */ ++ synchronize_sched(); ++} ++ ++static void joydev_detach_client(struct joydev *joydev, ++ struct joydev_client *client) ++{ ++ spin_lock(&joydev->client_lock); ++ list_del_rcu(&client->node); ++ spin_unlock(&joydev->client_lock); ++ synchronize_sched(); ++} ++ ++static int joydev_open_device(struct joydev *joydev) ++{ ++ int retval; ++ ++ retval = mutex_lock_interruptible(&joydev->mutex); ++ if (retval) ++ return retval; ++ ++ if (!joydev->exist) ++ retval = -ENODEV; ++ else if (!joydev->open++) { ++ retval = input_open_device(&joydev->handle); ++ if (retval) ++ joydev->open--; ++ } ++ ++ mutex_unlock(&joydev->mutex); ++ return retval; ++} ++ ++static void joydev_close_device(struct joydev *joydev) ++{ ++ mutex_lock(&joydev->mutex); ++ ++ if (joydev->exist && !--joydev->open) ++ input_close_device(&joydev->handle); ++ ++ mutex_unlock(&joydev->mutex); ++} ++ ++/* ++ * Wake up users waiting for IO so they can disconnect from ++ * dead device. ++ */ ++static void joydev_hangup(struct joydev *joydev) ++{ ++ struct joydev_client *client; ++ ++ spin_lock(&joydev->client_lock); ++ list_for_each_entry(client, &joydev->client_list, node) ++ kill_fasync(&client->fasync, SIGIO, POLL_HUP); ++ spin_unlock(&joydev->client_lock); ++ ++ wake_up_interruptible(&joydev->wait); ++} ++ + static int joydev_release(struct inode *inode, struct file *file) + { + struct joydev_client *client = file->private_data; + struct joydev *joydev = client->joydev; + + joydev_fasync(-1, file, 0); +- +- list_del(&client->node); ++ joydev_detach_client(joydev, client); + kfree(client); + +- if (!--joydev->open && joydev->exist) +- input_close_device(&joydev->handle); +- ++ joydev_close_device(joydev); + put_device(&joydev->dev); + + return 0; +@@ -176,11 +266,16 @@ static int joydev_open(struct inode *inode, struct file *file) + if (i >= JOYDEV_MINORS) + return -ENODEV; + ++ error = mutex_lock_interruptible(&joydev_table_mutex); ++ if (error) ++ return error; + joydev = joydev_table[i]; +- if (!joydev || !joydev->exist) +- return -ENODEV; ++ if (joydev) ++ get_device(&joydev->dev); ++ mutex_unlock(&joydev_table_mutex); + +- get_device(&joydev->dev); ++ if (!joydev) ++ return -ENODEV; + + client = kzalloc(sizeof(struct joydev_client), GFP_KERNEL); + if (!client) { +@@ -188,37 +283,129 @@ static int joydev_open(struct inode *inode, struct file *file) + goto err_put_joydev; + } + ++ spin_lock_init(&client->buffer_lock); + client->joydev = joydev; +- list_add_tail(&client->node, &joydev->client_list); ++ joydev_attach_client(joydev, client); + +- if (!joydev->open++ && joydev->exist) { +- error = input_open_device(&joydev->handle); +- if (error) +- goto err_free_client; +- } ++ error = joydev_open_device(joydev); ++ if (error) ++ goto err_free_client; + + file->private_data = client; + return 0; + + err_free_client: +- list_del(&client->node); ++ joydev_detach_client(joydev, client); + kfree(client); + err_put_joydev: + put_device(&joydev->dev); + return error; + } + +-static ssize_t joydev_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) ++static int joydev_generate_startup_event(struct joydev_client *client, ++ struct input_dev *input, ++ struct js_event *event) + { +- return -EINVAL; ++ struct joydev *joydev = client->joydev; ++ int have_event; ++ ++ spin_lock_irq(&client->buffer_lock); ++ ++ have_event = client->startup < joydev->nabs + joydev->nkey; ++ ++ if (have_event) { ++ ++ event->time = jiffies_to_msecs(jiffies); ++ if (client->startup < joydev->nkey) { ++ event->type = JS_EVENT_BUTTON | JS_EVENT_INIT; ++ event->number = client->startup; ++ event->value = !!test_bit(joydev->keypam[event->number], ++ input->key); ++ } else { ++ event->type = JS_EVENT_AXIS | JS_EVENT_INIT; ++ event->number = client->startup - joydev->nkey; ++ event->value = joydev->abs[event->number]; ++ } ++ client->startup++; ++ } ++ ++ spin_unlock_irq(&client->buffer_lock); ++ ++ return have_event; ++} ++ ++static int joydev_fetch_next_event(struct joydev_client *client, ++ struct js_event *event) ++{ ++ int have_event; ++ ++ spin_lock_irq(&client->buffer_lock); ++ ++ have_event = client->head != client->tail; ++ if (have_event) { ++ *event = client->buffer[client->tail++]; ++ client->tail &= JOYDEV_BUFFER_SIZE - 1; ++ } ++ ++ spin_unlock_irq(&client->buffer_lock); ++ ++ return have_event; ++} ++ ++/* ++ * Old joystick interface ++ */ ++static ssize_t joydev_0x_read(struct joydev_client *client, ++ struct input_dev *input, ++ char __user *buf) ++{ ++ struct joydev *joydev = client->joydev; ++ struct JS_DATA_TYPE data; ++ int i; ++ ++ spin_lock_irq(&input->event_lock); ++ ++ /* ++ * Get device state ++ */ ++ for (data.buttons = i = 0; i < 32 && i < joydev->nkey; i++) ++ data.buttons |= ++ test_bit(joydev->keypam[i], input->key) ? (1 << i) : 0; ++ data.x = (joydev->abs[0] / 256 + 128) >> joydev->glue.JS_CORR.x; ++ data.y = (joydev->abs[1] / 256 + 128) >> joydev->glue.JS_CORR.y; ++ ++ /* ++ * Reset reader's event queue ++ */ ++ spin_lock(&client->buffer_lock); ++ client->startup = 0; ++ client->tail = client->head; ++ spin_unlock(&client->buffer_lock); ++ ++ spin_unlock_irq(&input->event_lock); ++ ++ if (copy_to_user(buf, &data, sizeof(struct JS_DATA_TYPE))) ++ return -EFAULT; ++ ++ return sizeof(struct JS_DATA_TYPE); ++} ++ ++static inline int joydev_data_pending(struct joydev_client *client) ++{ ++ struct joydev *joydev = client->joydev; ++ ++ return client->startup < joydev->nabs + joydev->nkey || ++ client->head != client->tail; + } + +-static ssize_t joydev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) ++static ssize_t joydev_read(struct file *file, char __user *buf, ++ size_t count, loff_t *ppos) + { + struct joydev_client *client = file->private_data; + struct joydev *joydev = client->joydev; + struct input_dev *input = joydev->handle.dev; +- int retval = 0; ++ struct js_event event; ++ int retval; + + if (!joydev->exist) + return -ENODEV; +@@ -226,68 +413,35 @@ static ssize_t joydev_read(struct file *file, char __user *buf, size_t count, lo + if (count < sizeof(struct js_event)) + return -EINVAL; + +- if (count == sizeof(struct JS_DATA_TYPE)) { +- +- struct JS_DATA_TYPE data; +- int i; +- +- for (data.buttons = i = 0; i < 32 && i < joydev->nkey; i++) +- data.buttons |= test_bit(joydev->keypam[i], input->key) ? (1 << i) : 0; +- data.x = (joydev->abs[0] / 256 + 128) >> joydev->glue.JS_CORR.x; +- data.y = (joydev->abs[1] / 256 + 128) >> joydev->glue.JS_CORR.y; +- +- if (copy_to_user(buf, &data, sizeof(struct JS_DATA_TYPE))) +- return -EFAULT; +- +- client->startup = 0; +- client->tail = client->head; ++ if (count == sizeof(struct JS_DATA_TYPE)) ++ return joydev_0x_read(client, input, buf); + +- return sizeof(struct JS_DATA_TYPE); +- } +- +- if (client->startup == joydev->nabs + joydev->nkey && +- client->head == client->tail && (file->f_flags & O_NONBLOCK)) ++ if (!joydev_data_pending(client) && (file->f_flags & O_NONBLOCK)) + return -EAGAIN; + + retval = wait_event_interruptible(joydev->wait, +- !joydev->exist || +- client->startup < joydev->nabs + joydev->nkey || +- client->head != client->tail); ++ !joydev->exist || joydev_data_pending(client)); + if (retval) + return retval; + + if (!joydev->exist) + return -ENODEV; + +- while (client->startup < joydev->nabs + joydev->nkey && retval + sizeof(struct js_event) <= count) { +- +- struct js_event event; +- +- event.time = jiffies_to_msecs(jiffies); +- +- if (client->startup < joydev->nkey) { +- event.type = JS_EVENT_BUTTON | JS_EVENT_INIT; +- event.number = client->startup; +- event.value = !!test_bit(joydev->keypam[event.number], input->key); +- } else { +- event.type = JS_EVENT_AXIS | JS_EVENT_INIT; +- event.number = client->startup - joydev->nkey; +- event.value = joydev->abs[event.number]; +- } ++ while (retval + sizeof(struct js_event) <= count && ++ joydev_generate_startup_event(client, input, &event)) { + + if (copy_to_user(buf + retval, &event, sizeof(struct js_event))) + return -EFAULT; + +- client->startup++; + retval += sizeof(struct js_event); + } + +- while (client->head != client->tail && retval + sizeof(struct js_event) <= count) { ++ while (retval + sizeof(struct js_event) <= count && ++ joydev_fetch_next_event(client, &event)) { + +- if (copy_to_user(buf + retval, client->buffer + client->tail, sizeof(struct js_event))) ++ if (copy_to_user(buf + retval, &event, sizeof(struct js_event))) + return -EFAULT; + +- client->tail = (client->tail + 1) & (JOYDEV_BUFFER_SIZE - 1); + retval += sizeof(struct js_event); + } + +@@ -301,126 +455,144 @@ static unsigned int joydev_poll(struct file *file, poll_table *wait) + struct joydev *joydev = client->joydev; + + poll_wait(file, &joydev->wait, wait); +- return ((client->head != client->tail || client->startup < joydev->nabs + joydev->nkey) ? +- (POLLIN | POLLRDNORM) : 0) | (joydev->exist ? 0 : (POLLHUP | POLLERR)); ++ return (joydev_data_pending(client) ? (POLLIN | POLLRDNORM) : 0) | ++ (joydev->exist ? 0 : (POLLHUP | POLLERR)); + } + +-static int joydev_ioctl_common(struct joydev *joydev, unsigned int cmd, void __user *argp) ++static int joydev_ioctl_common(struct joydev *joydev, ++ unsigned int cmd, void __user *argp) + { + struct input_dev *dev = joydev->handle.dev; + int i, j; + + switch (cmd) { + +- case JS_SET_CAL: +- return copy_from_user(&joydev->glue.JS_CORR, argp, ++ case JS_SET_CAL: ++ return copy_from_user(&joydev->glue.JS_CORR, argp, + sizeof(joydev->glue.JS_CORR)) ? -EFAULT : 0; + +- case JS_GET_CAL: +- return copy_to_user(argp, &joydev->glue.JS_CORR, ++ case JS_GET_CAL: ++ return copy_to_user(argp, &joydev->glue.JS_CORR, + sizeof(joydev->glue.JS_CORR)) ? -EFAULT : 0; + +- case JS_SET_TIMEOUT: +- return get_user(joydev->glue.JS_TIMEOUT, (s32 __user *) argp); ++ case JS_SET_TIMEOUT: ++ return get_user(joydev->glue.JS_TIMEOUT, (s32 __user *) argp); + +- case JS_GET_TIMEOUT: +- return put_user(joydev->glue.JS_TIMEOUT, (s32 __user *) argp); ++ case JS_GET_TIMEOUT: ++ return put_user(joydev->glue.JS_TIMEOUT, (s32 __user *) argp); + +- case JSIOCGVERSION: +- return put_user(JS_VERSION, (__u32 __user *) argp); ++ case JSIOCGVERSION: ++ return put_user(JS_VERSION, (__u32 __user *) argp); + +- case JSIOCGAXES: +- return put_user(joydev->nabs, (__u8 __user *) argp); ++ case JSIOCGAXES: ++ return put_user(joydev->nabs, (__u8 __user *) argp); + +- case JSIOCGBUTTONS: +- return put_user(joydev->nkey, (__u8 __user *) argp); ++ case JSIOCGBUTTONS: ++ return put_user(joydev->nkey, (__u8 __user *) argp); + +- case JSIOCSCORR: +- if (copy_from_user(joydev->corr, argp, +- sizeof(joydev->corr[0]) * joydev->nabs)) +- return -EFAULT; +- for (i = 0; i < joydev->nabs; i++) { +- j = joydev->abspam[i]; +- joydev->abs[i] = joydev_correct(dev->abs[j], joydev->corr + i); +- } +- return 0; ++ case JSIOCSCORR: ++ if (copy_from_user(joydev->corr, argp, ++ sizeof(joydev->corr[0]) * joydev->nabs)) ++ return -EFAULT; + +- case JSIOCGCORR: +- return copy_to_user(argp, joydev->corr, +- sizeof(joydev->corr[0]) * joydev->nabs) ? -EFAULT : 0; ++ for (i = 0; i < joydev->nabs; i++) { ++ j = joydev->abspam[i]; ++ joydev->abs[i] = joydev_correct(dev->abs[j], ++ &joydev->corr[i]); ++ } ++ return 0; + +- case JSIOCSAXMAP: +- if (copy_from_user(joydev->abspam, argp, sizeof(__u8) * (ABS_MAX + 1))) +- return -EFAULT; +- for (i = 0; i < joydev->nabs; i++) { +- if (joydev->abspam[i] > ABS_MAX) +- return -EINVAL; +- joydev->absmap[joydev->abspam[i]] = i; +- } +- return 0; +- +- case JSIOCGAXMAP: +- return copy_to_user(argp, joydev->abspam, +- sizeof(__u8) * (ABS_MAX + 1)) ? -EFAULT : 0; +- +- case JSIOCSBTNMAP: +- if (copy_from_user(joydev->keypam, argp, sizeof(__u16) * (KEY_MAX - BTN_MISC + 1))) ++ case JSIOCGCORR: ++ return copy_to_user(argp, joydev->corr, ++ sizeof(joydev->corr[0]) * joydev->nabs) ? -EFAULT : 0; ++ ++ case JSIOCSAXMAP: ++ if (copy_from_user(joydev->abspam, argp, ++ sizeof(__u8) * (ABS_MAX + 1))) ++ return -EFAULT; ++ ++ for (i = 0; i < joydev->nabs; i++) { ++ if (joydev->abspam[i] > ABS_MAX) ++ return -EINVAL; ++ joydev->absmap[joydev->abspam[i]] = i; ++ } ++ return 0; ++ ++ case JSIOCGAXMAP: ++ return copy_to_user(argp, joydev->abspam, ++ sizeof(__u8) * (ABS_MAX + 1)) ? -EFAULT : 0; ++ ++ case JSIOCSBTNMAP: ++ if (copy_from_user(joydev->keypam, argp, ++ sizeof(__u16) * (KEY_MAX - BTN_MISC + 1))) ++ return -EFAULT; ++ ++ for (i = 0; i < joydev->nkey; i++) { ++ if (joydev->keypam[i] > KEY_MAX || ++ joydev->keypam[i] < BTN_MISC) ++ return -EINVAL; ++ joydev->keymap[joydev->keypam[i] - BTN_MISC] = i; ++ } ++ ++ return 0; ++ ++ case JSIOCGBTNMAP: ++ return copy_to_user(argp, joydev->keypam, ++ sizeof(__u16) * (KEY_MAX - BTN_MISC + 1)) ? -EFAULT : 0; ++ ++ default: ++ if ((cmd & ~IOCSIZE_MASK) == JSIOCGNAME(0)) { ++ int len; ++ if (!dev->name) ++ return 0; ++ len = strlen(dev->name) + 1; ++ if (len > _IOC_SIZE(cmd)) ++ len = _IOC_SIZE(cmd); ++ if (copy_to_user(argp, dev->name, len)) + return -EFAULT; +- for (i = 0; i < joydev->nkey; i++) { +- if (joydev->keypam[i] > KEY_MAX || joydev->keypam[i] < BTN_MISC) +- return -EINVAL; +- joydev->keymap[joydev->keypam[i] - BTN_MISC] = i; +- } +- return 0; +- +- case JSIOCGBTNMAP: +- return copy_to_user(argp, joydev->keypam, +- sizeof(__u16) * (KEY_MAX - BTN_MISC + 1)) ? -EFAULT : 0; +- +- default: +- if ((cmd & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) == JSIOCGNAME(0)) { +- int len; +- if (!dev->name) +- return 0; +- len = strlen(dev->name) + 1; +- if (len > _IOC_SIZE(cmd)) +- len = _IOC_SIZE(cmd); +- if (copy_to_user(argp, dev->name, len)) +- return -EFAULT; +- return len; +- } ++ return len; ++ } + } + return -EINVAL; + } + + #ifdef CONFIG_COMPAT +-static long joydev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++static long joydev_compat_ioctl(struct file *file, ++ unsigned int cmd, unsigned long arg) + { + struct joydev_client *client = file->private_data; + struct joydev *joydev = client->joydev; + void __user *argp = (void __user *)arg; + s32 tmp32; + struct JS_DATA_SAVE_TYPE_32 ds32; +- int err; ++ int retval; + +- if (!joydev->exist) +- return -ENODEV; ++ retval = mutex_lock_interruptible(&joydev->mutex); ++ if (retval) ++ return retval; ++ ++ if (!joydev->exist) { ++ retval = -ENODEV; ++ goto out; ++ } ++ ++ switch (cmd) { + +- switch(cmd) { + case JS_SET_TIMELIMIT: +- err = get_user(tmp32, (s32 __user *) arg); +- if (err == 0) ++ retval = get_user(tmp32, (s32 __user *) arg); ++ if (retval == 0) + joydev->glue.JS_TIMELIMIT = tmp32; + break; ++ + case JS_GET_TIMELIMIT: + tmp32 = joydev->glue.JS_TIMELIMIT; +- err = put_user(tmp32, (s32 __user *) arg); ++ retval = put_user(tmp32, (s32 __user *) arg); + break; + + case JS_SET_ALL: +- err = copy_from_user(&ds32, argp, +- sizeof(ds32)) ? -EFAULT : 0; +- if (err == 0) { ++ retval = copy_from_user(&ds32, argp, ++ sizeof(ds32)) ? -EFAULT : 0; ++ if (retval == 0) { + joydev->glue.JS_TIMEOUT = ds32.JS_TIMEOUT; + joydev->glue.BUSY = ds32.BUSY; + joydev->glue.JS_EXPIRETIME = ds32.JS_EXPIRETIME; +@@ -438,55 +610,119 @@ static long joydev_compat_ioctl(struct file *file, unsigned int cmd, unsigned lo + ds32.JS_SAVE = joydev->glue.JS_SAVE; + ds32.JS_CORR = joydev->glue.JS_CORR; + +- err = copy_to_user(argp, &ds32, sizeof(ds32)) ? -EFAULT : 0; ++ retval = copy_to_user(argp, &ds32, sizeof(ds32)) ? -EFAULT : 0; + break; + + default: +- err = joydev_ioctl_common(joydev, cmd, argp); ++ retval = joydev_ioctl_common(joydev, cmd, argp); ++ break; + } +- return err; ++ ++ out: ++ mutex_unlock(&joydev->mutex); ++ return retval; + } + #endif /* CONFIG_COMPAT */ + +-static int joydev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) ++static long joydev_ioctl(struct file *file, ++ unsigned int cmd, unsigned long arg) + { + struct joydev_client *client = file->private_data; + struct joydev *joydev = client->joydev; + void __user *argp = (void __user *)arg; ++ int retval; + +- if (!joydev->exist) +- return -ENODEV; ++ retval = mutex_lock_interruptible(&joydev->mutex); ++ if (retval) ++ return retval; ++ ++ if (!joydev->exist) { ++ retval = -ENODEV; ++ goto out; ++ } ++ ++ switch (cmd) { ++ ++ case JS_SET_TIMELIMIT: ++ retval = get_user(joydev->glue.JS_TIMELIMIT, ++ (long __user *) arg); ++ break; ++ ++ case JS_GET_TIMELIMIT: ++ retval = put_user(joydev->glue.JS_TIMELIMIT, ++ (long __user *) arg); ++ break; ++ ++ case JS_SET_ALL: ++ retval = copy_from_user(&joydev->glue, argp, ++ sizeof(joydev->glue)) ? -EFAULT: 0; ++ break; ++ ++ case JS_GET_ALL: ++ retval = copy_to_user(argp, &joydev->glue, ++ sizeof(joydev->glue)) ? -EFAULT : 0; ++ break; + +- switch(cmd) { +- case JS_SET_TIMELIMIT: +- return get_user(joydev->glue.JS_TIMELIMIT, (long __user *) arg); +- case JS_GET_TIMELIMIT: +- return put_user(joydev->glue.JS_TIMELIMIT, (long __user *) arg); +- case JS_SET_ALL: +- return copy_from_user(&joydev->glue, argp, +- sizeof(joydev->glue)) ? -EFAULT : 0; +- case JS_GET_ALL: +- return copy_to_user(argp, &joydev->glue, +- sizeof(joydev->glue)) ? -EFAULT : 0; +- default: +- return joydev_ioctl_common(joydev, cmd, argp); ++ default: ++ retval = joydev_ioctl_common(joydev, cmd, argp); ++ break; + } ++ out: ++ mutex_unlock(&joydev->mutex); ++ return retval; + } + + static const struct file_operations joydev_fops = { +- .owner = THIS_MODULE, +- .read = joydev_read, +- .write = joydev_write, +- .poll = joydev_poll, +- .open = joydev_open, +- .release = joydev_release, +- .ioctl = joydev_ioctl, ++ .owner = THIS_MODULE, ++ .read = joydev_read, ++ .poll = joydev_poll, ++ .open = joydev_open, ++ .release = joydev_release, ++ .unlocked_ioctl = joydev_ioctl, + #ifdef CONFIG_COMPAT +- .compat_ioctl = joydev_compat_ioctl, ++ .compat_ioctl = joydev_compat_ioctl, + #endif +- .fasync = joydev_fasync, ++ .fasync = joydev_fasync, + }; + ++static int joydev_install_chrdev(struct joydev *joydev) ++{ ++ joydev_table[joydev->minor] = joydev; ++ return 0; ++} ++ ++static void joydev_remove_chrdev(struct joydev *joydev) ++{ ++ mutex_lock(&joydev_table_mutex); ++ joydev_table[joydev->minor] = NULL; ++ mutex_unlock(&joydev_table_mutex); ++} ++ ++/* ++ * Mark device non-existant. This disables writes, ioctls and ++ * prevents new users from opening the device. Already posted ++ * blocking reads will stay, however new ones will fail. ++ */ ++static void joydev_mark_dead(struct joydev *joydev) ++{ ++ mutex_lock(&joydev->mutex); ++ joydev->exist = 0; ++ mutex_unlock(&joydev->mutex); ++} ++ ++static void joydev_cleanup(struct joydev *joydev) ++{ ++ struct input_handle *handle = &joydev->handle; ++ ++ joydev_mark_dead(joydev); ++ joydev_hangup(joydev); ++ joydev_remove_chrdev(joydev); ++ ++ /* joydev is marked dead so noone else accesses joydev->open */ ++ if (joydev->open) ++ input_close_device(handle); ++} ++ + static int joydev_connect(struct input_handler *handler, struct input_dev *dev, + const struct input_device_id *id) + { +@@ -494,7 +730,10 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev, + int i, j, t, minor; + int error; + +- for (minor = 0; minor < JOYDEV_MINORS && joydev_table[minor]; minor++); ++ for (minor = 0; minor < JOYDEV_MINORS; minor++) ++ if (!joydev_table[minor]) ++ break; ++ + if (minor == JOYDEV_MINORS) { + printk(KERN_ERR "joydev: no more free joydev devices\n"); + return -ENFILE; +@@ -505,15 +744,19 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev, + return -ENOMEM; + + INIT_LIST_HEAD(&joydev->client_list); ++ spin_lock_init(&joydev->client_lock); ++ mutex_init(&joydev->mutex); + init_waitqueue_head(&joydev->wait); + ++ snprintf(joydev->name, sizeof(joydev->name), "js%d", minor); ++ joydev->exist = 1; + joydev->minor = minor; ++ + joydev->exist = 1; + joydev->handle.dev = dev; + joydev->handle.name = joydev->name; + joydev->handle.handler = handler; + joydev->handle.private = joydev; +- snprintf(joydev->name, sizeof(joydev->name), "js%d", minor); + + for (i = 0; i < ABS_MAX + 1; i++) + if (test_bit(i, dev->absbit)) { +@@ -545,67 +788,65 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev, + } + joydev->corr[i].type = JS_CORR_BROKEN; + joydev->corr[i].prec = dev->absfuzz[j]; +- joydev->corr[i].coef[0] = (dev->absmax[j] + dev->absmin[j]) / 2 - dev->absflat[j]; +- joydev->corr[i].coef[1] = (dev->absmax[j] + dev->absmin[j]) / 2 + dev->absflat[j]; +- if (!(t = ((dev->absmax[j] - dev->absmin[j]) / 2 - 2 * dev->absflat[j]))) +- continue; +- joydev->corr[i].coef[2] = (1 << 29) / t; +- joydev->corr[i].coef[3] = (1 << 29) / t; +- +- joydev->abs[i] = joydev_correct(dev->abs[j], joydev->corr + i); ++ joydev->corr[i].coef[0] = ++ (dev->absmax[j] + dev->absmin[j]) / 2 - dev->absflat[j]; ++ joydev->corr[i].coef[1] = ++ (dev->absmax[j] + dev->absmin[j]) / 2 + dev->absflat[j]; ++ ++ t = (dev->absmax[j] - dev->absmin[j]) / 2 - 2 * dev->absflat[j]; ++ if (t) { ++ joydev->corr[i].coef[2] = (1 << 29) / t; ++ joydev->corr[i].coef[3] = (1 << 29) / t; ++ ++ joydev->abs[i] = joydev_correct(dev->abs[j], ++ joydev->corr + i); ++ } + } + +- snprintf(joydev->dev.bus_id, sizeof(joydev->dev.bus_id), +- "js%d", minor); ++ strlcpy(joydev->dev.bus_id, joydev->name, sizeof(joydev->dev.bus_id)); ++ joydev->dev.devt = MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor); + joydev->dev.class = &input_class; + joydev->dev.parent = &dev->dev; +- joydev->dev.devt = MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor); + joydev->dev.release = joydev_free; + device_initialize(&joydev->dev); + +- joydev_table[minor] = joydev; +- +- error = device_add(&joydev->dev); ++ error = input_register_handle(&joydev->handle); + if (error) + goto err_free_joydev; + +- error = input_register_handle(&joydev->handle); ++ error = joydev_install_chrdev(joydev); + if (error) +- goto err_delete_joydev; ++ goto err_unregister_handle; ++ ++ error = device_add(&joydev->dev); ++ if (error) ++ goto err_cleanup_joydev; + + return 0; + +- err_delete_joydev: +- device_del(&joydev->dev); ++ err_cleanup_joydev: ++ joydev_cleanup(joydev); ++ err_unregister_handle: ++ input_unregister_handle(&joydev->handle); + err_free_joydev: + put_device(&joydev->dev); + return error; + } + +- + static void joydev_disconnect(struct input_handle *handle) + { + struct joydev *joydev = handle->private; +- struct joydev_client *client; + +- input_unregister_handle(handle); + device_del(&joydev->dev); +- +- joydev->exist = 0; +- +- if (joydev->open) { +- input_close_device(handle); +- list_for_each_entry(client, &joydev->client_list, node) +- kill_fasync(&client->fasync, SIGIO, POLL_HUP); +- wake_up_interruptible(&joydev->wait); +- } +- ++ joydev_cleanup(joydev); ++ input_unregister_handle(handle); + put_device(&joydev->dev); + } + + static const struct input_device_id joydev_blacklist[] = { + { +- .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT, ++ .flags = INPUT_DEVICE_ID_MATCH_EVBIT | ++ INPUT_DEVICE_ID_MATCH_KEYBIT, + .evbit = { BIT(EV_KEY) }, + .keybit = { [LONG(BTN_TOUCH)] = BIT(BTN_TOUCH) }, + }, /* Avoid itouchpads, touchscreens and tablets */ +@@ -614,17 +855,20 @@ static const struct input_device_id joydev_blacklist[] = { + + static const struct input_device_id joydev_ids[] = { + { +- .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT, ++ .flags = INPUT_DEVICE_ID_MATCH_EVBIT | ++ INPUT_DEVICE_ID_MATCH_ABSBIT, + .evbit = { BIT(EV_ABS) }, + .absbit = { BIT(ABS_X) }, + }, + { +- .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT, ++ .flags = INPUT_DEVICE_ID_MATCH_EVBIT | ++ INPUT_DEVICE_ID_MATCH_ABSBIT, + .evbit = { BIT(EV_ABS) }, + .absbit = { BIT(ABS_WHEEL) }, + }, + { +- .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT, ++ .flags = INPUT_DEVICE_ID_MATCH_EVBIT | ++ INPUT_DEVICE_ID_MATCH_ABSBIT, + .evbit = { BIT(EV_ABS) }, + .absbit = { BIT(ABS_THROTTLE) }, + }, +@@ -634,14 +878,14 @@ static const struct input_device_id joydev_ids[] = { + MODULE_DEVICE_TABLE(input, joydev_ids); + + static struct input_handler joydev_handler = { +- .event = joydev_event, +- .connect = joydev_connect, +- .disconnect = joydev_disconnect, +- .fops = &joydev_fops, +- .minor = JOYDEV_MINOR_BASE, +- .name = "joydev", +- .id_table = joydev_ids, +- .blacklist = joydev_blacklist, ++ .event = joydev_event, ++ .connect = joydev_connect, ++ .disconnect = joydev_disconnect, ++ .fops = &joydev_fops, ++ .minor = JOYDEV_MINOR_BASE, ++ .name = "joydev", ++ .id_table = joydev_ids, ++ .blacklist = joydev_blacklist, + }; + + static int __init joydev_init(void) +diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c +index 9173916..cc36edb 100644 +--- a/drivers/input/mousedev.c ++++ b/drivers/input/mousedev.c +@@ -61,9 +61,11 @@ struct mousedev { + int open; + int minor; + char name[16]; ++ struct input_handle handle; + wait_queue_head_t wait; + struct list_head client_list; +- struct input_handle handle; ++ spinlock_t client_lock; /* protects client_list */ ++ struct mutex mutex; + struct device dev; + + struct list_head mixdev_node; +@@ -113,108 +115,137 @@ static unsigned char mousedev_imex_seq[] = { 0xf3, 200, 0xf3, 200, 0xf3, 80 }; + static struct input_handler mousedev_handler; + + static struct mousedev *mousedev_table[MOUSEDEV_MINORS]; ++static DEFINE_MUTEX(mousedev_table_mutex); + static struct mousedev *mousedev_mix; + static LIST_HEAD(mousedev_mix_list); + ++static void mixdev_open_devices(void); ++static void mixdev_close_devices(void); ++ + #define fx(i) (mousedev->old_x[(mousedev->pkt_count - (i)) & 03]) + #define fy(i) (mousedev->old_y[(mousedev->pkt_count - (i)) & 03]) + +-static void mousedev_touchpad_event(struct input_dev *dev, struct mousedev *mousedev, unsigned int code, int value) ++static void mousedev_touchpad_event(struct input_dev *dev, ++ struct mousedev *mousedev, ++ unsigned int code, int value) + { + int size, tmp; + enum { FRACTION_DENOM = 128 }; + + switch (code) { +- case ABS_X: +- fx(0) = value; +- if (mousedev->touch && mousedev->pkt_count >= 2) { +- size = dev->absmax[ABS_X] - dev->absmin[ABS_X]; +- if (size == 0) +- size = 256 * 2; +- tmp = ((value - fx(2)) * (256 * FRACTION_DENOM)) / size; +- tmp += mousedev->frac_dx; +- mousedev->packet.dx = tmp / FRACTION_DENOM; +- mousedev->frac_dx = tmp - mousedev->packet.dx * FRACTION_DENOM; +- } +- break; + +- case ABS_Y: +- fy(0) = value; +- if (mousedev->touch && mousedev->pkt_count >= 2) { +- /* use X size to keep the same scale */ +- size = dev->absmax[ABS_X] - dev->absmin[ABS_X]; +- if (size == 0) +- size = 256 * 2; +- tmp = -((value - fy(2)) * (256 * FRACTION_DENOM)) / size; +- tmp += mousedev->frac_dy; +- mousedev->packet.dy = tmp / FRACTION_DENOM; +- mousedev->frac_dy = tmp - mousedev->packet.dy * FRACTION_DENOM; +- } +- break; ++ case ABS_X: ++ fx(0) = value; ++ if (mousedev->touch && mousedev->pkt_count >= 2) { ++ size = dev->absmax[ABS_X] - dev->absmin[ABS_X]; ++ if (size == 0) ++ size = 256 * 2; ++ tmp = ((value - fx(2)) * 256 * FRACTION_DENOM) / size; ++ tmp += mousedev->frac_dx; ++ mousedev->packet.dx = tmp / FRACTION_DENOM; ++ mousedev->frac_dx = ++ tmp - mousedev->packet.dx * FRACTION_DENOM; ++ } ++ break; ++ ++ case ABS_Y: ++ fy(0) = value; ++ if (mousedev->touch && mousedev->pkt_count >= 2) { ++ /* use X size to keep the same scale */ ++ size = dev->absmax[ABS_X] - dev->absmin[ABS_X]; ++ if (size == 0) ++ size = 256 * 2; ++ tmp = -((value - fy(2)) * 256 * FRACTION_DENOM) / size; ++ tmp += mousedev->frac_dy; ++ mousedev->packet.dy = tmp / FRACTION_DENOM; ++ mousedev->frac_dy = tmp - ++ mousedev->packet.dy * FRACTION_DENOM; ++ } ++ break; + } + } + +-static void mousedev_abs_event(struct input_dev *dev, struct mousedev *mousedev, unsigned int code, int value) ++static void mousedev_abs_event(struct input_dev *dev, struct mousedev *mousedev, ++ unsigned int code, int value) + { + int size; + + switch (code) { +- case ABS_X: +- size = dev->absmax[ABS_X] - dev->absmin[ABS_X]; +- if (size == 0) +- size = xres ? : 1; +- if (value > dev->absmax[ABS_X]) +- value = dev->absmax[ABS_X]; +- if (value < dev->absmin[ABS_X]) +- value = dev->absmin[ABS_X]; +- mousedev->packet.x = ((value - dev->absmin[ABS_X]) * xres) / size; +- mousedev->packet.abs_event = 1; +- break; + +- case ABS_Y: +- size = dev->absmax[ABS_Y] - dev->absmin[ABS_Y]; +- if (size == 0) +- size = yres ? : 1; +- if (value > dev->absmax[ABS_Y]) +- value = dev->absmax[ABS_Y]; +- if (value < dev->absmin[ABS_Y]) +- value = dev->absmin[ABS_Y]; +- mousedev->packet.y = yres - ((value - dev->absmin[ABS_Y]) * yres) / size; +- mousedev->packet.abs_event = 1; +- break; ++ case ABS_X: ++ size = dev->absmax[ABS_X] - dev->absmin[ABS_X]; ++ if (size == 0) ++ size = xres ? : 1; ++ if (value > dev->absmax[ABS_X]) ++ value = dev->absmax[ABS_X]; ++ if (value < dev->absmin[ABS_X]) ++ value = dev->absmin[ABS_X]; ++ mousedev->packet.x = ++ ((value - dev->absmin[ABS_X]) * xres) / size; ++ mousedev->packet.abs_event = 1; ++ break; ++ ++ case ABS_Y: ++ size = dev->absmax[ABS_Y] - dev->absmin[ABS_Y]; ++ if (size == 0) ++ size = yres ? : 1; ++ if (value > dev->absmax[ABS_Y]) ++ value = dev->absmax[ABS_Y]; ++ if (value < dev->absmin[ABS_Y]) ++ value = dev->absmin[ABS_Y]; ++ mousedev->packet.y = yres - ++ ((value - dev->absmin[ABS_Y]) * yres) / size; ++ mousedev->packet.abs_event = 1; ++ break; + } + } + +-static void mousedev_rel_event(struct mousedev *mousedev, unsigned int code, int value) ++static void mousedev_rel_event(struct mousedev *mousedev, ++ unsigned int code, int value) + { + switch (code) { +- case REL_X: mousedev->packet.dx += value; break; +- case REL_Y: mousedev->packet.dy -= value; break; +- case REL_WHEEL: mousedev->packet.dz -= value; break; ++ case REL_X: ++ mousedev->packet.dx += value; ++ break; ++ ++ case REL_Y: ++ mousedev->packet.dy -= value; ++ break; ++ ++ case REL_WHEEL: ++ mousedev->packet.dz -= value; ++ break; + } + } + +-static void mousedev_key_event(struct mousedev *mousedev, unsigned int code, int value) ++static void mousedev_key_event(struct mousedev *mousedev, ++ unsigned int code, int value) + { + int index; + + switch (code) { +- case BTN_TOUCH: +- case BTN_0: +- case BTN_LEFT: index = 0; break; +- case BTN_STYLUS: +- case BTN_1: +- case BTN_RIGHT: index = 1; break; +- case BTN_2: +- case BTN_FORWARD: +- case BTN_STYLUS2: +- case BTN_MIDDLE: index = 2; break; +- case BTN_3: +- case BTN_BACK: +- case BTN_SIDE: index = 3; break; +- case BTN_4: +- case BTN_EXTRA: index = 4; break; +- default: return; ++ ++ case BTN_TOUCH: ++ case BTN_0: ++ case BTN_LEFT: index = 0; break; ++ ++ case BTN_STYLUS: ++ case BTN_1: ++ case BTN_RIGHT: index = 1; break; ++ ++ case BTN_2: ++ case BTN_FORWARD: ++ case BTN_STYLUS2: ++ case BTN_MIDDLE: index = 2; break; ++ ++ case BTN_3: ++ case BTN_BACK: ++ case BTN_SIDE: index = 3; break; ++ ++ case BTN_4: ++ case BTN_EXTRA: index = 4; break; ++ ++ default: return; + } + + if (value) { +@@ -226,19 +257,22 @@ static void mousedev_key_event(struct mousedev *mousedev, unsigned int code, int + } + } + +-static void mousedev_notify_readers(struct mousedev *mousedev, struct mousedev_hw_data *packet) ++static void mousedev_notify_readers(struct mousedev *mousedev, ++ struct mousedev_hw_data *packet) + { + struct mousedev_client *client; + struct mousedev_motion *p; +- unsigned long flags; ++ unsigned int new_head; + int wake_readers = 0; + +- list_for_each_entry(client, &mousedev->client_list, node) { +- spin_lock_irqsave(&client->packet_lock, flags); ++ list_for_each_entry_rcu(client, &mousedev->client_list, node) { ++ ++ /* Just acquire the lock, interrupts already disabled */ ++ spin_lock(&client->packet_lock); + + p = &client->packets[client->head]; + if (client->ready && p->buttons != mousedev->packet.buttons) { +- unsigned int new_head = (client->head + 1) % PACKET_QUEUE_LEN; ++ new_head = (client->head + 1) % PACKET_QUEUE_LEN; + if (new_head != client->tail) { + p = &client->packets[client->head = new_head]; + memset(p, 0, sizeof(struct mousedev_motion)); +@@ -253,19 +287,22 @@ static void mousedev_notify_readers(struct mousedev *mousedev, struct mousedev_h + } + + client->pos_x += packet->dx; +- client->pos_x = client->pos_x < 0 ? 0 : (client->pos_x >= xres ? xres : client->pos_x); ++ client->pos_x = client->pos_x < 0 ? ++ 0 : (client->pos_x >= xres ? xres : client->pos_x); + client->pos_y += packet->dy; +- client->pos_y = client->pos_y < 0 ? 0 : (client->pos_y >= yres ? yres : client->pos_y); ++ client->pos_y = client->pos_y < 0 ? ++ 0 : (client->pos_y >= yres ? yres : client->pos_y); + + p->dx += packet->dx; + p->dy += packet->dy; + p->dz += packet->dz; + p->buttons = mousedev->packet.buttons; + +- if (p->dx || p->dy || p->dz || p->buttons != client->last_buttons) ++ if (p->dx || p->dy || p->dz || ++ p->buttons != client->last_buttons) + client->ready = 1; + +- spin_unlock_irqrestore(&client->packet_lock, flags); ++ spin_unlock(&client->packet_lock); + + if (client->ready) { + kill_fasync(&client->fasync, SIGIO, POLL_IN); +@@ -281,7 +318,8 @@ static void mousedev_touchpad_touch(struct mousedev *mousedev, int value) + { + if (!value) { + if (mousedev->touch && +- time_before(jiffies, mousedev->touch + msecs_to_jiffies(tap_time))) { ++ time_before(jiffies, ++ mousedev->touch + msecs_to_jiffies(tap_time))) { + /* + * Toggle left button to emulate tap. + * We rely on the fact that mousedev_mix always has 0 +@@ -290,7 +328,8 @@ static void mousedev_touchpad_touch(struct mousedev *mousedev, int value) + set_bit(0, &mousedev->packet.buttons); + set_bit(0, &mousedev_mix->packet.buttons); + mousedev_notify_readers(mousedev, &mousedev_mix->packet); +- mousedev_notify_readers(mousedev_mix, &mousedev_mix->packet); ++ mousedev_notify_readers(mousedev_mix, ++ &mousedev_mix->packet); + clear_bit(0, &mousedev->packet.buttons); + clear_bit(0, &mousedev_mix->packet.buttons); + } +@@ -302,54 +341,61 @@ static void mousedev_touchpad_touch(struct mousedev *mousedev, int value) + mousedev->touch = jiffies; + } + +-static void mousedev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) ++static void mousedev_event(struct input_handle *handle, ++ unsigned int type, unsigned int code, int value) + { + struct mousedev *mousedev = handle->private; + + switch (type) { +- case EV_ABS: +- /* Ignore joysticks */ +- if (test_bit(BTN_TRIGGER, handle->dev->keybit)) +- return; + +- if (test_bit(BTN_TOOL_FINGER, handle->dev->keybit)) +- mousedev_touchpad_event(handle->dev, mousedev, code, value); +- else +- mousedev_abs_event(handle->dev, mousedev, code, value); ++ case EV_ABS: ++ /* Ignore joysticks */ ++ if (test_bit(BTN_TRIGGER, handle->dev->keybit)) ++ return; + +- break; ++ if (test_bit(BTN_TOOL_FINGER, handle->dev->keybit)) ++ mousedev_touchpad_event(handle->dev, ++ mousedev, code, value); ++ else ++ mousedev_abs_event(handle->dev, mousedev, code, value); + +- case EV_REL: +- mousedev_rel_event(mousedev, code, value); +- break; ++ break; + +- case EV_KEY: +- if (value != 2) { +- if (code == BTN_TOUCH && test_bit(BTN_TOOL_FINGER, handle->dev->keybit)) +- mousedev_touchpad_touch(mousedev, value); +- else +- mousedev_key_event(mousedev, code, value); +- } +- break; ++ case EV_REL: ++ mousedev_rel_event(mousedev, code, value); ++ break; + +- case EV_SYN: +- if (code == SYN_REPORT) { +- if (mousedev->touch) { +- mousedev->pkt_count++; +- /* Input system eats duplicate events, but we need all of them +- * to do correct averaging so apply present one forward +- */ +- fx(0) = fx(1); +- fy(0) = fy(1); +- } +- +- mousedev_notify_readers(mousedev, &mousedev->packet); +- mousedev_notify_readers(mousedev_mix, &mousedev->packet); +- +- mousedev->packet.dx = mousedev->packet.dy = mousedev->packet.dz = 0; +- mousedev->packet.abs_event = 0; ++ case EV_KEY: ++ if (value != 2) { ++ if (code == BTN_TOUCH && ++ test_bit(BTN_TOOL_FINGER, handle->dev->keybit)) ++ mousedev_touchpad_touch(mousedev, value); ++ else ++ mousedev_key_event(mousedev, code, value); ++ } ++ break; ++ ++ case EV_SYN: ++ if (code == SYN_REPORT) { ++ if (mousedev->touch) { ++ mousedev->pkt_count++; ++ /* ++ * Input system eats duplicate events, ++ * but we need all of them to do correct ++ * averaging so apply present one forward ++ */ ++ fx(0) = fx(1); ++ fy(0) = fy(1); + } +- break; ++ ++ mousedev_notify_readers(mousedev, &mousedev->packet); ++ mousedev_notify_readers(mousedev_mix, &mousedev->packet); ++ ++ mousedev->packet.dx = mousedev->packet.dy = ++ mousedev->packet.dz = 0; ++ mousedev->packet.abs_event = 0; ++ } ++ break; + } + } + +@@ -367,41 +413,48 @@ static void mousedev_free(struct device *dev) + { + struct mousedev *mousedev = container_of(dev, struct mousedev, dev); + +- mousedev_table[mousedev->minor] = NULL; + kfree(mousedev); + } + +-static int mixdev_add_device(struct mousedev *mousedev) ++static int mousedev_open_device(struct mousedev *mousedev) + { +- int error; ++ int retval; + +- if (mousedev_mix->open) { +- error = input_open_device(&mousedev->handle); +- if (error) +- return error; ++ retval = mutex_lock_interruptible(&mousedev->mutex); ++ if (retval) ++ return retval; + +- mousedev->open++; +- mousedev->mixdev_open = 1; ++ if (mousedev->minor == MOUSEDEV_MIX) ++ mixdev_open_devices(); ++ else if (!mousedev->exist) ++ retval = -ENODEV; ++ else if (!mousedev->open++) { ++ retval = input_open_device(&mousedev->handle); ++ if (retval) ++ mousedev->open--; + } + +- get_device(&mousedev->dev); +- list_add_tail(&mousedev->mixdev_node, &mousedev_mix_list); +- +- return 0; ++ mutex_unlock(&mousedev->mutex); ++ return retval; + } + +-static void mixdev_remove_device(struct mousedev *mousedev) ++static void mousedev_close_device(struct mousedev *mousedev) + { +- if (mousedev->mixdev_open) { +- mousedev->mixdev_open = 0; +- if (!--mousedev->open && mousedev->exist) +- input_close_device(&mousedev->handle); +- } ++ mutex_lock(&mousedev->mutex); + +- list_del_init(&mousedev->mixdev_node); +- put_device(&mousedev->dev); ++ if (mousedev->minor == MOUSEDEV_MIX) ++ mixdev_close_devices(); ++ else if (mousedev->exist && !--mousedev->open) ++ input_close_device(&mousedev->handle); ++ ++ mutex_unlock(&mousedev->mutex); + } + ++/* ++ * Open all available devices so they can all be multiplexed in one. ++ * stream. Note that this function is called with mousedev_mix->mutex ++ * held. ++ */ + static void mixdev_open_devices(void) + { + struct mousedev *mousedev; +@@ -411,16 +464,19 @@ static void mixdev_open_devices(void) + + list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) { + if (!mousedev->mixdev_open) { +- if (!mousedev->open && mousedev->exist) +- if (input_open_device(&mousedev->handle)) +- continue; ++ if (mousedev_open_device(mousedev)) ++ continue; + +- mousedev->open++; + mousedev->mixdev_open = 1; + } + } + } + ++/* ++ * Close all devices that were opened as part of multiplexed ++ * device. Note that this function is called with mousedev_mix->mutex ++ * held. ++ */ + static void mixdev_close_devices(void) + { + struct mousedev *mousedev; +@@ -431,33 +487,50 @@ static void mixdev_close_devices(void) + list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) { + if (mousedev->mixdev_open) { + mousedev->mixdev_open = 0; +- if (!--mousedev->open && mousedev->exist) +- input_close_device(&mousedev->handle); ++ mousedev_close_device(mousedev); + } + } + } + ++ ++static void mousedev_attach_client(struct mousedev *mousedev, ++ struct mousedev_client *client) ++{ ++ spin_lock(&mousedev->client_lock); ++ list_add_tail_rcu(&client->node, &mousedev->client_list); ++ spin_unlock(&mousedev->client_lock); ++ /* ++ * We don't use synchronize_rcu() here because read-side ++ * critical section is protected by a spinlock (dev->event_lock) ++ * instead of rcu_read_lock(). ++ */ ++ synchronize_sched(); ++} ++ ++static void mousedev_detach_client(struct mousedev *mousedev, ++ struct mousedev_client *client) ++{ ++ spin_lock(&mousedev->client_lock); ++ list_del_rcu(&client->node); ++ spin_unlock(&mousedev->client_lock); ++ synchronize_sched(); ++} ++ + static int mousedev_release(struct inode *inode, struct file *file) + { + struct mousedev_client *client = file->private_data; + struct mousedev *mousedev = client->mousedev; + + mousedev_fasync(-1, file, 0); +- +- list_del(&client->node); ++ mousedev_detach_client(mousedev, client); + kfree(client); + +- if (mousedev->minor == MOUSEDEV_MIX) +- mixdev_close_devices(); +- else if (!--mousedev->open && mousedev->exist) +- input_close_device(&mousedev->handle); +- ++ mousedev_close_device(mousedev); + put_device(&mousedev->dev); + + return 0; + } + +- + static int mousedev_open(struct inode *inode, struct file *file) + { + struct mousedev_client *client; +@@ -475,12 +548,17 @@ static int mousedev_open(struct inode *inode, struct file *file) + if (i >= MOUSEDEV_MINORS) + return -ENODEV; + ++ error = mutex_lock_interruptible(&mousedev_table_mutex); ++ if (error) ++ return error; + mousedev = mousedev_table[i]; ++ if (mousedev) ++ get_device(&mousedev->dev); ++ mutex_unlock(&mousedev_table_mutex); ++ + if (!mousedev) + return -ENODEV; + +- get_device(&mousedev->dev); +- + client = kzalloc(sizeof(struct mousedev_client), GFP_KERNEL); + if (!client) { + error = -ENOMEM; +@@ -491,21 +569,17 @@ static int mousedev_open(struct inode *inode, struct file *file) + client->pos_x = xres / 2; + client->pos_y = yres / 2; + client->mousedev = mousedev; +- list_add_tail(&client->node, &mousedev->client_list); ++ mousedev_attach_client(mousedev, client); + +- if (mousedev->minor == MOUSEDEV_MIX) +- mixdev_open_devices(); +- else if (!mousedev->open++ && mousedev->exist) { +- error = input_open_device(&mousedev->handle); +- if (error) +- goto err_free_client; +- } ++ error = mousedev_open_device(mousedev); ++ if (error) ++ goto err_free_client; + + file->private_data = client; + return 0; + + err_free_client: +- list_del(&client->node); ++ mousedev_detach_client(mousedev, client); + kfree(client); + err_put_mousedev: + put_device(&mousedev->dev); +@@ -517,41 +591,41 @@ static inline int mousedev_limit_delta(int delta, int limit) + return delta > limit ? limit : (delta < -limit ? -limit : delta); + } + +-static void mousedev_packet(struct mousedev_client *client, signed char *ps2_data) ++static void mousedev_packet(struct mousedev_client *client, ++ signed char *ps2_data) + { +- struct mousedev_motion *p; +- unsigned long flags; +- +- spin_lock_irqsave(&client->packet_lock, flags); +- p = &client->packets[client->tail]; ++ struct mousedev_motion *p = &client->packets[client->tail]; + +- ps2_data[0] = 0x08 | ((p->dx < 0) << 4) | ((p->dy < 0) << 5) | (p->buttons & 0x07); ++ ps2_data[0] = 0x08 | ++ ((p->dx < 0) << 4) | ((p->dy < 0) << 5) | (p->buttons & 0x07); + ps2_data[1] = mousedev_limit_delta(p->dx, 127); + ps2_data[2] = mousedev_limit_delta(p->dy, 127); + p->dx -= ps2_data[1]; + p->dy -= ps2_data[2]; + + switch (client->mode) { +- case MOUSEDEV_EMUL_EXPS: +- ps2_data[3] = mousedev_limit_delta(p->dz, 7); +- p->dz -= ps2_data[3]; +- ps2_data[3] = (ps2_data[3] & 0x0f) | ((p->buttons & 0x18) << 1); +- client->bufsiz = 4; +- break; +- +- case MOUSEDEV_EMUL_IMPS: +- ps2_data[0] |= ((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1); +- ps2_data[3] = mousedev_limit_delta(p->dz, 127); +- p->dz -= ps2_data[3]; +- client->bufsiz = 4; +- break; +- +- case MOUSEDEV_EMUL_PS2: +- default: +- ps2_data[0] |= ((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1); +- p->dz = 0; +- client->bufsiz = 3; +- break; ++ case MOUSEDEV_EMUL_EXPS: ++ ps2_data[3] = mousedev_limit_delta(p->dz, 7); ++ p->dz -= ps2_data[3]; ++ ps2_data[3] = (ps2_data[3] & 0x0f) | ((p->buttons & 0x18) << 1); ++ client->bufsiz = 4; ++ break; ++ ++ case MOUSEDEV_EMUL_IMPS: ++ ps2_data[0] |= ++ ((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1); ++ ps2_data[3] = mousedev_limit_delta(p->dz, 127); ++ p->dz -= ps2_data[3]; ++ client->bufsiz = 4; ++ break; ++ ++ case MOUSEDEV_EMUL_PS2: ++ default: ++ ps2_data[0] |= ++ ((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1); ++ p->dz = 0; ++ client->bufsiz = 3; ++ break; + } + + if (!p->dx && !p->dy && !p->dz) { +@@ -561,12 +635,56 @@ static void mousedev_packet(struct mousedev_client *client, signed char *ps2_dat + } else + client->tail = (client->tail + 1) % PACKET_QUEUE_LEN; + } +- +- spin_unlock_irqrestore(&client->packet_lock, flags); + } + ++static void mousedev_generate_response(struct mousedev_client *client, ++ int command) ++{ ++ client->ps2[0] = 0xfa; /* ACK */ ++ ++ switch (command) { + +-static ssize_t mousedev_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) ++ case 0xeb: /* Poll */ ++ mousedev_packet(client, &client->ps2[1]); ++ client->bufsiz++; /* account for leading ACK */ ++ break; ++ ++ case 0xf2: /* Get ID */ ++ switch (client->mode) { ++ case MOUSEDEV_EMUL_PS2: ++ client->ps2[1] = 0; ++ break; ++ case MOUSEDEV_EMUL_IMPS: ++ client->ps2[1] = 3; ++ break; ++ case MOUSEDEV_EMUL_EXPS: ++ client->ps2[1] = 4; ++ break; ++ } ++ client->bufsiz = 2; ++ break; ++ ++ case 0xe9: /* Get info */ ++ client->ps2[1] = 0x60; client->ps2[2] = 3; client->ps2[3] = 200; ++ client->bufsiz = 4; ++ break; ++ ++ case 0xff: /* Reset */ ++ client->impsseq = client->imexseq = 0; ++ client->mode = MOUSEDEV_EMUL_PS2; ++ client->ps2[1] = 0xaa; client->ps2[2] = 0x00; ++ client->bufsiz = 3; ++ break; ++ ++ default: ++ client->bufsiz = 1; ++ break; ++ } ++ client->buffer = client->bufsiz; ++} ++ ++static ssize_t mousedev_write(struct file *file, const char __user *buffer, ++ size_t count, loff_t *ppos) + { + struct mousedev_client *client = file->private_data; + unsigned char c; +@@ -577,6 +695,8 @@ static ssize_t mousedev_write(struct file *file, const char __user *buffer, size + if (get_user(c, buffer + i)) + return -EFAULT; + ++ spin_lock_irq(&client->packet_lock); ++ + if (c == mousedev_imex_seq[client->imexseq]) { + if (++client->imexseq == MOUSEDEV_SEQ_LEN) { + client->imexseq = 0; +@@ -593,68 +713,39 @@ static ssize_t mousedev_write(struct file *file, const char __user *buffer, size + } else + client->impsseq = 0; + +- client->ps2[0] = 0xfa; +- +- switch (c) { +- +- case 0xeb: /* Poll */ +- mousedev_packet(client, &client->ps2[1]); +- client->bufsiz++; /* account for leading ACK */ +- break; +- +- case 0xf2: /* Get ID */ +- switch (client->mode) { +- case MOUSEDEV_EMUL_PS2: client->ps2[1] = 0; break; +- case MOUSEDEV_EMUL_IMPS: client->ps2[1] = 3; break; +- case MOUSEDEV_EMUL_EXPS: client->ps2[1] = 4; break; +- } +- client->bufsiz = 2; +- break; +- +- case 0xe9: /* Get info */ +- client->ps2[1] = 0x60; client->ps2[2] = 3; client->ps2[3] = 200; +- client->bufsiz = 4; +- break; +- +- case 0xff: /* Reset */ +- client->impsseq = client->imexseq = 0; +- client->mode = MOUSEDEV_EMUL_PS2; +- client->ps2[1] = 0xaa; client->ps2[2] = 0x00; +- client->bufsiz = 3; +- break; +- +- default: +- client->bufsiz = 1; +- break; +- } ++ mousedev_generate_response(client, c); + +- client->buffer = client->bufsiz; ++ spin_unlock_irq(&client->packet_lock); + } + + kill_fasync(&client->fasync, SIGIO, POLL_IN); +- + wake_up_interruptible(&client->mousedev->wait); + + return count; + } + +-static ssize_t mousedev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) ++static ssize_t mousedev_read(struct file *file, char __user *buffer, ++ size_t count, loff_t *ppos) + { + struct mousedev_client *client = file->private_data; ++ struct mousedev *mousedev = client->mousedev; ++ signed char data[sizeof(client->ps2)]; + int retval = 0; + +- if (!client->ready && !client->buffer && (file->f_flags & O_NONBLOCK)) ++ if (!client->ready && !client->buffer && mousedev->exist && ++ (file->f_flags & O_NONBLOCK)) + return -EAGAIN; + +- retval = wait_event_interruptible(client->mousedev->wait, +- !client->mousedev->exist || client->ready || client->buffer); +- ++ retval = wait_event_interruptible(mousedev->wait, ++ !mousedev->exist || client->ready || client->buffer); + if (retval) + return retval; + +- if (!client->mousedev->exist) ++ if (!mousedev->exist) + return -ENODEV; + ++ spin_lock_irq(&client->packet_lock); ++ + if (!client->buffer && client->ready) { + mousedev_packet(client, client->ps2); + client->buffer = client->bufsiz; +@@ -663,9 +754,12 @@ static ssize_t mousedev_read(struct file *file, char __user *buffer, size_t coun + if (count > client->buffer) + count = client->buffer; + ++ memcpy(data, client->ps2 + client->bufsiz - client->buffer, count); + client->buffer -= count; + +- if (copy_to_user(buffer, client->ps2 + client->bufsiz - client->buffer - count, count)) ++ spin_unlock_irq(&client->packet_lock); ++ ++ if (copy_to_user(buffer, data, count)) + return -EFAULT; + + return count; +@@ -692,6 +786,60 @@ static const struct file_operations mousedev_fops = { + .fasync = mousedev_fasync, + }; + ++static int mousedev_install_chrdev(struct mousedev *mousedev) ++{ ++ mousedev_table[mousedev->minor] = mousedev; ++ return 0; ++} ++ ++static void mousedev_remove_chrdev(struct mousedev *mousedev) ++{ ++ mutex_lock(&mousedev_table_mutex); ++ mousedev_table[mousedev->minor] = NULL; ++ mutex_unlock(&mousedev_table_mutex); ++} ++ ++/* ++ * Mark device non-existent. This disables writes, ioctls and ++ * prevents new users from opening the device. Already posted ++ * blocking reads will stay, however new ones will fail. ++ */ ++static void mousedev_mark_dead(struct mousedev *mousedev) ++{ ++ mutex_lock(&mousedev->mutex); ++ mousedev->exist = 0; ++ mutex_unlock(&mousedev->mutex); ++} ++ ++/* ++ * Wake up users waiting for IO so they can disconnect from ++ * dead device. ++ */ ++static void mousedev_hangup(struct mousedev *mousedev) ++{ ++ struct mousedev_client *client; ++ ++ spin_lock(&mousedev->client_lock); ++ list_for_each_entry(client, &mousedev->client_list, node) ++ kill_fasync(&client->fasync, SIGIO, POLL_HUP); ++ spin_unlock(&mousedev->client_lock); ++ ++ wake_up_interruptible(&mousedev->wait); ++} ++ ++static void mousedev_cleanup(struct mousedev *mousedev) ++{ ++ struct input_handle *handle = &mousedev->handle; ++ ++ mousedev_mark_dead(mousedev); ++ mousedev_hangup(mousedev); ++ mousedev_remove_chrdev(mousedev); ++ ++ /* mousedev is marked dead so no one else accesses mousedev->open */ ++ if (mousedev->open) ++ input_close_device(handle); ++} ++ + static struct mousedev *mousedev_create(struct input_dev *dev, + struct input_handler *handler, + int minor) +@@ -707,6 +855,10 @@ static struct mousedev *mousedev_create(struct input_dev *dev, + + INIT_LIST_HEAD(&mousedev->client_list); + INIT_LIST_HEAD(&mousedev->mixdev_node); ++ spin_lock_init(&mousedev->client_lock); ++ mutex_init(&mousedev->mutex); ++ lockdep_set_subclass(&mousedev->mutex, ++ minor == MOUSEDEV_MIX ? MOUSEDEV_MIX : 0); + init_waitqueue_head(&mousedev->wait); + + if (minor == MOUSEDEV_MIX) +@@ -731,14 +883,27 @@ static struct mousedev *mousedev_create(struct input_dev *dev, + mousedev->dev.release = mousedev_free; + device_initialize(&mousedev->dev); + +- mousedev_table[minor] = mousedev; ++ if (minor != MOUSEDEV_MIX) { ++ error = input_register_handle(&mousedev->handle); ++ if (error) ++ goto err_free_mousedev; ++ } ++ ++ error = mousedev_install_chrdev(mousedev); ++ if (error) ++ goto err_unregister_handle; + + error = device_add(&mousedev->dev); + if (error) +- goto err_free_mousedev; ++ goto err_cleanup_mousedev; + + return mousedev; + ++ err_cleanup_mousedev: ++ mousedev_cleanup(mousedev); ++ err_unregister_handle: ++ if (minor != MOUSEDEV_MIX) ++ input_unregister_handle(&mousedev->handle); + err_free_mousedev: + put_device(&mousedev->dev); + err_out: +@@ -747,29 +912,64 @@ static struct mousedev *mousedev_create(struct input_dev *dev, + + static void mousedev_destroy(struct mousedev *mousedev) + { +- struct mousedev_client *client; +- + device_del(&mousedev->dev); +- mousedev->exist = 0; ++ mousedev_cleanup(mousedev); ++ if (mousedev->minor != MOUSEDEV_MIX) ++ input_unregister_handle(&mousedev->handle); ++ put_device(&mousedev->dev); ++} + +- if (mousedev->open) { +- input_close_device(&mousedev->handle); +- list_for_each_entry(client, &mousedev->client_list, node) +- kill_fasync(&client->fasync, SIGIO, POLL_HUP); +- wake_up_interruptible(&mousedev->wait); ++static int mixdev_add_device(struct mousedev *mousedev) ++{ ++ int retval; ++ ++ retval = mutex_lock_interruptible(&mousedev_mix->mutex); ++ if (retval) ++ return retval; ++ ++ if (mousedev_mix->open) { ++ retval = mousedev_open_device(mousedev); ++ if (retval) ++ goto out; ++ ++ mousedev->mixdev_open = 1; + } + ++ get_device(&mousedev->dev); ++ list_add_tail(&mousedev->mixdev_node, &mousedev_mix_list); ++ ++ out: ++ mutex_unlock(&mousedev_mix->mutex); ++ return retval; ++} ++ ++static void mixdev_remove_device(struct mousedev *mousedev) ++{ ++ mutex_lock(&mousedev_mix->mutex); ++ ++ if (mousedev->mixdev_open) { ++ mousedev->mixdev_open = 0; ++ mousedev_close_device(mousedev); ++ } ++ ++ list_del_init(&mousedev->mixdev_node); ++ mutex_unlock(&mousedev_mix->mutex); ++ + put_device(&mousedev->dev); + } + +-static int mousedev_connect(struct input_handler *handler, struct input_dev *dev, ++static int mousedev_connect(struct input_handler *handler, ++ struct input_dev *dev, + const struct input_device_id *id) + { + struct mousedev *mousedev; + int minor; + int error; + +- for (minor = 0; minor < MOUSEDEV_MINORS && mousedev_table[minor]; minor++); ++ for (minor = 0; minor < MOUSEDEV_MINORS; minor++) ++ if (!mousedev_table[minor]) ++ break; ++ + if (minor == MOUSEDEV_MINORS) { + printk(KERN_ERR "mousedev: no more free mousedev devices\n"); + return -ENFILE; +@@ -779,21 +979,13 @@ static int mousedev_connect(struct input_handler *handler, struct input_dev *dev + if (IS_ERR(mousedev)) + return PTR_ERR(mousedev); + +- error = input_register_handle(&mousedev->handle); +- if (error) +- goto err_delete_mousedev; +- + error = mixdev_add_device(mousedev); +- if (error) +- goto err_unregister_handle; ++ if (error) { ++ mousedev_destroy(mousedev); ++ return error; ++ } + + return 0; +- +- err_unregister_handle: +- input_unregister_handle(&mousedev->handle); +- err_delete_mousedev: +- device_unregister(&mousedev->dev); +- return error; + } + + static void mousedev_disconnect(struct input_handle *handle) +@@ -801,33 +993,42 @@ static void mousedev_disconnect(struct input_handle *handle) + struct mousedev *mousedev = handle->private; + + mixdev_remove_device(mousedev); +- input_unregister_handle(handle); + mousedev_destroy(mousedev); + } + + static const struct input_device_id mousedev_ids[] = { + { +- .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_RELBIT, ++ .flags = INPUT_DEVICE_ID_MATCH_EVBIT | ++ INPUT_DEVICE_ID_MATCH_KEYBIT | ++ INPUT_DEVICE_ID_MATCH_RELBIT, + .evbit = { BIT(EV_KEY) | BIT(EV_REL) }, + .keybit = { [LONG(BTN_LEFT)] = BIT(BTN_LEFT) }, + .relbit = { BIT(REL_X) | BIT(REL_Y) }, +- }, /* A mouse like device, at least one button, two relative axes */ ++ }, /* A mouse like device, at least one button, ++ two relative axes */ + { +- .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_RELBIT, ++ .flags = INPUT_DEVICE_ID_MATCH_EVBIT | ++ INPUT_DEVICE_ID_MATCH_RELBIT, + .evbit = { BIT(EV_KEY) | BIT(EV_REL) }, + .relbit = { BIT(REL_WHEEL) }, + }, /* A separate scrollwheel */ + { +- .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_ABSBIT, ++ .flags = INPUT_DEVICE_ID_MATCH_EVBIT | ++ INPUT_DEVICE_ID_MATCH_KEYBIT | ++ INPUT_DEVICE_ID_MATCH_ABSBIT, + .evbit = { BIT(EV_KEY) | BIT(EV_ABS) }, + .keybit = { [LONG(BTN_TOUCH)] = BIT(BTN_TOUCH) }, + .absbit = { BIT(ABS_X) | BIT(ABS_Y) }, +- }, /* A tablet like device, at least touch detection, two absolute axes */ ++ }, /* A tablet like device, at least touch detection, ++ two absolute axes */ + { +- .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_ABSBIT, ++ .flags = INPUT_DEVICE_ID_MATCH_EVBIT | ++ INPUT_DEVICE_ID_MATCH_KEYBIT | ++ INPUT_DEVICE_ID_MATCH_ABSBIT, + .evbit = { BIT(EV_KEY) | BIT(EV_ABS) }, + .keybit = { [LONG(BTN_TOOL_FINGER)] = BIT(BTN_TOOL_FINGER) }, +- .absbit = { BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) | BIT(ABS_TOOL_WIDTH) }, ++ .absbit = { BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) | ++ BIT(ABS_TOOL_WIDTH) }, + }, /* A touchpad */ + + { }, /* Terminating entry */ +diff --git a/drivers/input/tsdev.c b/drivers/input/tsdev.c +index d2f882e..1202334 100644 +--- a/drivers/input/tsdev.c ++++ b/drivers/input/tsdev.c +@@ -112,6 +112,8 @@ struct tsdev { + struct input_handle handle; + wait_queue_head_t wait; + struct list_head client_list; ++ spinlock_t client_lock; /* protects client_list */ ++ struct mutex mutex; + struct device dev; + + int x, y, pressure; +@@ -122,8 +124,9 @@ struct tsdev_client { + struct fasync_struct *fasync; + struct list_head node; + struct tsdev *tsdev; ++ struct ts_event buffer[TSDEV_BUFFER_SIZE]; + int head, tail; +- struct ts_event event[TSDEV_BUFFER_SIZE]; ++ spinlock_t buffer_lock; /* protects access to buffer, head and tail */ + int raw; + }; + +@@ -137,6 +140,7 @@ struct tsdev_client { + #define TS_SET_CAL _IOW(IOC_H3600_TS_MAGIC, 11, struct ts_calibration) + + static struct tsdev *tsdev_table[TSDEV_MINORS/2]; ++static DEFINE_MUTEX(tsdev_table_mutex); + + static int tsdev_fasync(int fd, struct file *file, int on) + { +@@ -144,9 +148,94 @@ static int tsdev_fasync(int fd, struct file *file, int on) + int retval; + + retval = fasync_helper(fd, file, on, &client->fasync); ++ + return retval < 0 ? retval : 0; + } + ++static void tsdev_free(struct device *dev) ++{ ++ struct tsdev *tsdev = container_of(dev, struct tsdev, dev); ++ ++ kfree(tsdev); ++} ++ ++static void tsdev_attach_client(struct tsdev *tsdev, struct tsdev_client *client) ++{ ++ spin_lock(&tsdev->client_lock); ++ list_add_tail_rcu(&client->node, &tsdev->client_list); ++ spin_unlock(&tsdev->client_lock); ++ synchronize_sched(); ++} ++ ++static void tsdev_detach_client(struct tsdev *tsdev, struct tsdev_client *client) ++{ ++ spin_lock(&tsdev->client_lock); ++ list_del_rcu(&client->node); ++ spin_unlock(&tsdev->client_lock); ++ synchronize_sched(); ++} ++ ++static int tsdev_open_device(struct tsdev *tsdev) ++{ ++ int retval; ++ ++ retval = mutex_lock_interruptible(&tsdev->mutex); ++ if (retval) ++ return retval; ++ ++ if (!tsdev->exist) ++ retval = -ENODEV; ++ else if (!tsdev->open++) { ++ retval = input_open_device(&tsdev->handle); ++ if (retval) ++ tsdev->open--; ++ } ++ ++ mutex_unlock(&tsdev->mutex); ++ return retval; ++} ++ ++static void tsdev_close_device(struct tsdev *tsdev) ++{ ++ mutex_lock(&tsdev->mutex); ++ ++ if (tsdev->exist && !--tsdev->open) ++ input_close_device(&tsdev->handle); ++ ++ mutex_unlock(&tsdev->mutex); ++} ++ ++/* ++ * Wake up users waiting for IO so they can disconnect from ++ * dead device. ++ */ ++static void tsdev_hangup(struct tsdev *tsdev) ++{ ++ struct tsdev_client *client; ++ ++ spin_lock(&tsdev->client_lock); ++ list_for_each_entry(client, &tsdev->client_list, node) ++ kill_fasync(&client->fasync, SIGIO, POLL_HUP); ++ spin_unlock(&tsdev->client_lock); ++ ++ wake_up_interruptible(&tsdev->wait); ++} ++ ++static int tsdev_release(struct inode *inode, struct file *file) ++{ ++ struct tsdev_client *client = file->private_data; ++ struct tsdev *tsdev = client->tsdev; ++ ++ tsdev_fasync(-1, file, 0); ++ tsdev_detach_client(tsdev, client); ++ kfree(client); ++ ++ tsdev_close_device(tsdev); ++ put_device(&tsdev->dev); ++ ++ return 0; ++} ++ + static int tsdev_open(struct inode *inode, struct file *file) + { + int i = iminor(inode) - TSDEV_MINOR_BASE; +@@ -161,11 +250,16 @@ static int tsdev_open(struct inode *inode, struct file *file) + if (i >= TSDEV_MINORS) + return -ENODEV; + ++ error = mutex_lock_interruptible(&tsdev_table_mutex); ++ if (error) ++ return error; + tsdev = tsdev_table[i & TSDEV_MINOR_MASK]; +- if (!tsdev || !tsdev->exist) +- return -ENODEV; ++ if (tsdev) ++ get_device(&tsdev->dev); ++ mutex_unlock(&tsdev_table_mutex); + +- get_device(&tsdev->dev); ++ if (!tsdev) ++ return -ENODEV; + + client = kzalloc(sizeof(struct tsdev_client), GFP_KERNEL); + if (!client) { +@@ -173,51 +267,42 @@ static int tsdev_open(struct inode *inode, struct file *file) + goto err_put_tsdev; + } + ++ spin_lock_init(&client->buffer_lock); + client->tsdev = tsdev; +- client->raw = (i >= TSDEV_MINORS / 2) ? 1 : 0; +- list_add_tail(&client->node, &tsdev->client_list); ++ client->raw = i >= TSDEV_MINORS / 2; ++ tsdev_attach_client(tsdev, client); + +- if (!tsdev->open++ && tsdev->exist) { +- error = input_open_device(&tsdev->handle); +- if (error) +- goto err_free_client; +- } ++ error = tsdev_open_device(tsdev); ++ if (error) ++ goto err_free_client; + + file->private_data = client; + return 0; + + err_free_client: +- list_del(&client->node); ++ tsdev_detach_client(tsdev, client); + kfree(client); + err_put_tsdev: + put_device(&tsdev->dev); + return error; + } + +-static void tsdev_free(struct device *dev) +-{ +- struct tsdev *tsdev = container_of(dev, struct tsdev, dev); +- +- tsdev_table[tsdev->minor] = NULL; +- kfree(tsdev); +-} +- +-static int tsdev_release(struct inode *inode, struct file *file) ++static int tsdev_fetch_next_event(struct tsdev_client *client, ++ struct ts_event *event) + { +- struct tsdev_client *client = file->private_data; +- struct tsdev *tsdev = client->tsdev; ++ int have_event; + +- tsdev_fasync(-1, file, 0); +- +- list_del(&client->node); +- kfree(client); ++ spin_lock_irq(&client->buffer_lock); + +- if (!--tsdev->open && tsdev->exist) +- input_close_device(&tsdev->handle); ++ have_event = client->head != client->tail; ++ if (have_event) { ++ *event = client->buffer[client->tail++]; ++ client->tail &= TSDEV_BUFFER_SIZE - 1; ++ } + +- put_device(&tsdev->dev); ++ spin_unlock_irq(&client->buffer_lock); + +- return 0; ++ return have_event; + } + + static ssize_t tsdev_read(struct file *file, char __user *buffer, size_t count, +@@ -225,9 +310,11 @@ static ssize_t tsdev_read(struct file *file, char __user *buffer, size_t count, + { + struct tsdev_client *client = file->private_data; + struct tsdev *tsdev = client->tsdev; +- int retval = 0; ++ struct ts_event event; ++ int retval; + +- if (client->head == client->tail && tsdev->exist && (file->f_flags & O_NONBLOCK)) ++ if (client->head == client->tail && tsdev->exist && ++ (file->f_flags & O_NONBLOCK)) + return -EAGAIN; + + retval = wait_event_interruptible(tsdev->wait, +@@ -238,13 +325,14 @@ static ssize_t tsdev_read(struct file *file, char __user *buffer, size_t count, + if (!tsdev->exist) + return -ENODEV; + +- while (client->head != client->tail && +- retval + sizeof (struct ts_event) <= count) { +- if (copy_to_user (buffer + retval, client->event + client->tail, +- sizeof (struct ts_event))) ++ while (retval + sizeof(struct ts_event) <= count && ++ tsdev_fetch_next_event(client, &event)) { ++ ++ if (copy_to_user(buffer + retval, &event, ++ sizeof(struct ts_event))) + return -EFAULT; +- client->tail = (client->tail + 1) & (TSDEV_BUFFER_SIZE - 1); +- retval += sizeof (struct ts_event); ++ ++ retval += sizeof(struct ts_event); + } + + return retval; +@@ -261,14 +349,23 @@ static unsigned int tsdev_poll(struct file *file, poll_table *wait) + (tsdev->exist ? 0 : (POLLHUP | POLLERR)); + } + +-static int tsdev_ioctl(struct inode *inode, struct file *file, +- unsigned int cmd, unsigned long arg) ++static long tsdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) + { + struct tsdev_client *client = file->private_data; + struct tsdev *tsdev = client->tsdev; + int retval = 0; + ++ retval = mutex_lock_interruptible(&tsdev->mutex); ++ if (retval) ++ return retval; ++ ++ if (!tsdev->exist) { ++ retval = -ENODEV; ++ goto out; ++ } ++ + switch (cmd) { ++ + case TS_GET_CAL: + if (copy_to_user((void __user *)arg, &tsdev->cal, + sizeof (struct ts_calibration))) +@@ -277,7 +374,7 @@ static int tsdev_ioctl(struct inode *inode, struct file *file, + + case TS_SET_CAL: + if (copy_from_user(&tsdev->cal, (void __user *)arg, +- sizeof (struct ts_calibration))) ++ sizeof(struct ts_calibration))) + retval = -EFAULT; + break; + +@@ -286,29 +383,79 @@ static int tsdev_ioctl(struct inode *inode, struct file *file, + break; + } + ++ out: ++ mutex_unlock(&tsdev->mutex); + return retval; + } + + static const struct file_operations tsdev_fops = { +- .owner = THIS_MODULE, +- .open = tsdev_open, +- .release = tsdev_release, +- .read = tsdev_read, +- .poll = tsdev_poll, +- .fasync = tsdev_fasync, +- .ioctl = tsdev_ioctl, ++ .owner = THIS_MODULE, ++ .open = tsdev_open, ++ .release = tsdev_release, ++ .read = tsdev_read, ++ .poll = tsdev_poll, ++ .fasync = tsdev_fasync, ++ .unlocked_ioctl = tsdev_ioctl, + }; + ++static void tsdev_pass_event(struct tsdev *tsdev, struct tsdev_client *client, ++ int x, int y, int pressure, int millisecs) ++{ ++ struct ts_event *event; ++ int tmp; ++ ++ /* Interrupts are already disabled, just acquire the lock */ ++ spin_lock(&client->buffer_lock); ++ ++ event = &client->buffer[client->head++]; ++ client->head &= TSDEV_BUFFER_SIZE - 1; ++ ++ /* Calibration */ ++ if (!client->raw) { ++ x = ((x * tsdev->cal.xscale) >> 8) + tsdev->cal.xtrans; ++ y = ((y * tsdev->cal.yscale) >> 8) + tsdev->cal.ytrans; ++ if (tsdev->cal.xyswap) { ++ tmp = x; x = y; y = tmp; ++ } ++ } ++ ++ event->millisecs = millisecs; ++ event->x = x; ++ event->y = y; ++ event->pressure = pressure; ++ ++ spin_unlock(&client->buffer_lock); ++ ++ kill_fasync(&client->fasync, SIGIO, POLL_IN); ++} ++ ++static void tsdev_distribute_event(struct tsdev *tsdev) ++{ ++ struct tsdev_client *client; ++ struct timeval time; ++ int millisecs; ++ ++ do_gettimeofday(&time); ++ millisecs = time.tv_usec / 1000; ++ ++ list_for_each_entry_rcu(client, &tsdev->client_list, node) ++ tsdev_pass_event(tsdev, client, ++ tsdev->x, tsdev->y, ++ tsdev->pressure, millisecs); ++} ++ + static void tsdev_event(struct input_handle *handle, unsigned int type, + unsigned int code, int value) + { + struct tsdev *tsdev = handle->private; +- struct tsdev_client *client; +- struct timeval time; ++ struct input_dev *dev = handle->dev; ++ int wake_up_readers = 0; + + switch (type) { ++ + case EV_ABS: + switch (code) { ++ + case ABS_X: + tsdev->x = value; + break; +@@ -318,9 +465,9 @@ static void tsdev_event(struct input_handle *handle, unsigned int type, + break; + + case ABS_PRESSURE: +- if (value > handle->dev->absmax[ABS_PRESSURE]) +- value = handle->dev->absmax[ABS_PRESSURE]; +- value -= handle->dev->absmin[ABS_PRESSURE]; ++ if (value > dev->absmax[ABS_PRESSURE]) ++ value = dev->absmax[ABS_PRESSURE]; ++ value -= dev->absmin[ABS_PRESSURE]; + if (value < 0) + value = 0; + tsdev->pressure = value; +@@ -330,6 +477,7 @@ static void tsdev_event(struct input_handle *handle, unsigned int type, + + case EV_REL: + switch (code) { ++ + case REL_X: + tsdev->x += value; + if (tsdev->x < 0) +@@ -351,6 +499,7 @@ static void tsdev_event(struct input_handle *handle, unsigned int type, + case EV_KEY: + if (code == BTN_TOUCH || code == BTN_MOUSE) { + switch (value) { ++ + case 0: + tsdev->pressure = 0; + break; +@@ -362,49 +511,71 @@ static void tsdev_event(struct input_handle *handle, unsigned int type, + } + } + break; ++ ++ case EV_SYN: ++ if (code == SYN_REPORT) { ++ tsdev_distribute_event(tsdev); ++ wake_up_readers = 1; ++ } ++ break; + } + +- if (type != EV_SYN || code != SYN_REPORT) +- return; ++ if (wake_up_readers) ++ wake_up_interruptible(&tsdev->wait); ++} ++ ++static int tsdev_install_chrdev(struct tsdev *tsdev) ++{ ++ tsdev_table[tsdev->minor] = tsdev; ++ return 0; ++} + +- list_for_each_entry(client, &tsdev->client_list, node) { +- int x, y, tmp; ++static void tsdev_remove_chrdev(struct tsdev *tsdev) ++{ ++ mutex_lock(&tsdev_table_mutex); ++ tsdev_table[tsdev->minor] = NULL; ++ mutex_unlock(&tsdev_table_mutex); ++} + +- do_gettimeofday(&time); +- client->event[client->head].millisecs = time.tv_usec / 1000; +- client->event[client->head].pressure = tsdev->pressure; ++/* ++ * Mark device non-existant. This disables writes, ioctls and ++ * prevents new users from opening the device. Already posted ++ * blocking reads will stay, however new ones will fail. ++ */ ++static void tsdev_mark_dead(struct tsdev *tsdev) ++{ ++ mutex_lock(&tsdev->mutex); ++ tsdev->exist = 0; ++ mutex_unlock(&tsdev->mutex); ++} + +- x = tsdev->x; +- y = tsdev->y; ++static void tsdev_cleanup(struct tsdev *tsdev) ++{ ++ struct input_handle *handle = &tsdev->handle; + +- /* Calibration */ +- if (!client->raw) { +- x = ((x * tsdev->cal.xscale) >> 8) + tsdev->cal.xtrans; +- y = ((y * tsdev->cal.yscale) >> 8) + tsdev->cal.ytrans; +- if (tsdev->cal.xyswap) { +- tmp = x; x = y; y = tmp; +- } +- } ++ tsdev_mark_dead(tsdev); ++ tsdev_hangup(tsdev); ++ tsdev_remove_chrdev(tsdev); + +- client->event[client->head].x = x; +- client->event[client->head].y = y; +- client->head = (client->head + 1) & (TSDEV_BUFFER_SIZE - 1); +- kill_fasync(&client->fasync, SIGIO, POLL_IN); +- } +- wake_up_interruptible(&tsdev->wait); ++ /* tsdev is marked dead so noone else accesses tsdev->open */ ++ if (tsdev->open) ++ input_close_device(handle); + } + + static int tsdev_connect(struct input_handler *handler, struct input_dev *dev, + const struct input_device_id *id) + { + struct tsdev *tsdev; +- int minor, delta; ++ int delta; ++ int minor; + int error; + +- for (minor = 0; minor < TSDEV_MINORS / 2 && tsdev_table[minor]; minor++); +- if (minor >= TSDEV_MINORS / 2) { +- printk(KERN_ERR +- "tsdev: You have way too many touchscreens\n"); ++ for (minor = 0; minor < TSDEV_MINORS / 2; minor++) ++ if (!tsdev_table[minor]) ++ break; ++ ++ if (minor == TSDEV_MINORS) { ++ printk(KERN_ERR "tsdev: no more free tsdev devices\n"); + return -ENFILE; + } + +@@ -413,15 +584,18 @@ static int tsdev_connect(struct input_handler *handler, struct input_dev *dev, + return -ENOMEM; + + INIT_LIST_HEAD(&tsdev->client_list); ++ spin_lock_init(&tsdev->client_lock); ++ mutex_init(&tsdev->mutex); + init_waitqueue_head(&tsdev->wait); + ++ snprintf(tsdev->name, sizeof(tsdev->name), "ts%d", minor); + tsdev->exist = 1; + tsdev->minor = minor; ++ + tsdev->handle.dev = dev; + tsdev->handle.name = tsdev->name; + tsdev->handle.handler = handler; + tsdev->handle.private = tsdev; +- snprintf(tsdev->name, sizeof(tsdev->name), "ts%d", minor); + + /* Precompute the rough calibration matrix */ + delta = dev->absmax [ABS_X] - dev->absmin [ABS_X] + 1; +@@ -436,28 +610,31 @@ static int tsdev_connect(struct input_handler *handler, struct input_dev *dev, + tsdev->cal.yscale = (yres << 8) / delta; + tsdev->cal.ytrans = - ((dev->absmin [ABS_Y] * tsdev->cal.yscale) >> 8); + +- snprintf(tsdev->dev.bus_id, sizeof(tsdev->dev.bus_id), +- "ts%d", minor); ++ strlcpy(tsdev->dev.bus_id, tsdev->name, sizeof(tsdev->dev.bus_id)); ++ tsdev->dev.devt = MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + minor); + tsdev->dev.class = &input_class; + tsdev->dev.parent = &dev->dev; +- tsdev->dev.devt = MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + minor); + tsdev->dev.release = tsdev_free; + device_initialize(&tsdev->dev); + +- tsdev_table[minor] = tsdev; +- +- error = device_add(&tsdev->dev); ++ error = input_register_handle(&tsdev->handle); + if (error) + goto err_free_tsdev; + +- error = input_register_handle(&tsdev->handle); ++ error = tsdev_install_chrdev(tsdev); + if (error) +- goto err_delete_tsdev; ++ goto err_unregister_handle; ++ ++ error = device_add(&tsdev->dev); ++ if (error) ++ goto err_cleanup_tsdev; + + return 0; + +- err_delete_tsdev: +- device_del(&tsdev->dev); ++ err_cleanup_tsdev: ++ tsdev_cleanup(tsdev); ++ err_unregister_handle: ++ input_unregister_handle(&tsdev->handle); + err_free_tsdev: + put_device(&tsdev->dev); + return error; +@@ -466,20 +643,10 @@ static int tsdev_connect(struct input_handler *handler, struct input_dev *dev, + static void tsdev_disconnect(struct input_handle *handle) + { + struct tsdev *tsdev = handle->private; +- struct tsdev_client *client; + +- input_unregister_handle(handle); + device_del(&tsdev->dev); +- +- tsdev->exist = 0; +- +- if (tsdev->open) { +- input_close_device(handle); +- list_for_each_entry(client, &tsdev->client_list, node) +- kill_fasync(&client->fasync, SIGIO, POLL_HUP); +- wake_up_interruptible(&tsdev->wait); +- } +- ++ tsdev_cleanup(tsdev); ++ input_unregister_handle(handle); + put_device(&tsdev->dev); + } + +@@ -510,13 +677,13 @@ static const struct input_device_id tsdev_ids[] = { + MODULE_DEVICE_TABLE(input, tsdev_ids); + + static struct input_handler tsdev_handler = { +- .event = tsdev_event, +- .connect = tsdev_connect, +- .disconnect = tsdev_disconnect, +- .fops = &tsdev_fops, +- .minor = TSDEV_MINOR_BASE, +- .name = "tsdev", +- .id_table = tsdev_ids, ++ .event = tsdev_event, ++ .connect = tsdev_connect, ++ .disconnect = tsdev_disconnect, ++ .fops = &tsdev_fops, ++ .minor = TSDEV_MINOR_BASE, ++ .name = "tsdev", ++ .id_table = tsdev_ids, + }; + + static int __init tsdev_init(void) +diff --git a/drivers/isdn/capi/capidrv.c b/drivers/isdn/capi/capidrv.c +index 23b6f7b..f65b7f9 100644 +--- a/drivers/isdn/capi/capidrv.c ++++ b/drivers/isdn/capi/capidrv.c +@@ -2306,13 +2306,14 @@ static int __init capidrv_init(void) + + static void __exit capidrv_exit(void) + { +- char rev[10]; ++ char rev[32]; + char *p; + + if ((p = strchr(revision, ':')) != 0) { +- strcpy(rev, p + 1); +- p = strchr(rev, '$'); +- *p = 0; ++ strncpy(rev, p + 1, sizeof(rev)); ++ rev[sizeof(rev)-1] = 0; ++ if ((p = strchr(rev, '$')) != 0) ++ *p = 0; + } else { + strcpy(rev, " ??? "); + } +diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c +index bdc52d6..ba2e135 100644 +--- a/drivers/md/dm-crypt.c ++++ b/drivers/md/dm-crypt.c +@@ -399,7 +399,8 @@ static struct bio *crypt_alloc_buffer(struct dm_crypt_io *io, unsigned size) + struct bio *clone; + unsigned int nr_iovecs = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; + gfp_t gfp_mask = GFP_NOIO | __GFP_HIGHMEM; +- unsigned int i; ++ unsigned i, len; ++ struct page *page; + + clone = bio_alloc_bioset(GFP_NOIO, nr_iovecs, cc->bs); + if (!clone) +@@ -408,10 +409,8 @@ static struct bio *crypt_alloc_buffer(struct dm_crypt_io *io, unsigned size) + clone_init(io, clone); + + for (i = 0; i < nr_iovecs; i++) { +- struct bio_vec *bv = bio_iovec_idx(clone, i); +- +- bv->bv_page = mempool_alloc(cc->page_pool, gfp_mask); +- if (!bv->bv_page) ++ page = mempool_alloc(cc->page_pool, gfp_mask); ++ if (!page) + break; + + /* +@@ -422,15 +421,14 @@ static struct bio *crypt_alloc_buffer(struct dm_crypt_io *io, unsigned size) + if (i == (MIN_BIO_PAGES - 1)) + gfp_mask = (gfp_mask | __GFP_NOWARN) & ~__GFP_WAIT; + +- bv->bv_offset = 0; +- if (size > PAGE_SIZE) +- bv->bv_len = PAGE_SIZE; +- else +- bv->bv_len = size; ++ len = (size > PAGE_SIZE) ? PAGE_SIZE : size; ++ ++ if (!bio_add_page(clone, page, len, 0)) { ++ mempool_free(page, cc->page_pool); ++ break; ++ } + +- clone->bi_size += bv->bv_len; +- clone->bi_vcnt++; +- size -= bv->bv_len; ++ size -= len; + } + + if (!clone->bi_size) { +@@ -515,6 +513,9 @@ static int crypt_endio(struct bio *clone, unsigned int done, int error) + struct crypt_config *cc = io->target->private; + unsigned read_io = bio_data_dir(clone) == READ; + ++ if (unlikely(!bio_flagged(clone, BIO_UPTODATE) && !error)) ++ error = -EIO; ++ + /* + * free the processed pages, even if + * it's only a partially completed write +@@ -529,10 +530,8 @@ static int crypt_endio(struct bio *clone, unsigned int done, int error) + if (!read_io) + goto out; + +- if (unlikely(!bio_flagged(clone, BIO_UPTODATE))) { +- error = -EIO; ++ if (unlikely(error)) + goto out; +- } + + bio_put(clone); + io->post_process = 1; +diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c +index b441d82..8a4f63b 100644 +--- a/drivers/md/dm-ioctl.c ++++ b/drivers/md/dm-ioctl.c +@@ -1250,21 +1250,17 @@ static int target_message(struct dm_ioctl *param, size_t param_size) + if (!table) + goto out_argv; + +- if (tmsg->sector >= dm_table_get_size(table)) { ++ ti = dm_table_find_target(table, tmsg->sector); ++ if (!dm_target_is_valid(ti)) { + DMWARN("Target message sector outside device."); + r = -EINVAL; +- goto out_table; +- } +- +- ti = dm_table_find_target(table, tmsg->sector); +- if (ti->type->message) ++ } else if (ti->type->message) + r = ti->type->message(ti, argc, argv); + else { + DMWARN("Target type does not support messages"); + r = -EINVAL; + } + +- out_table: + dm_table_put(table); + out_argv: + kfree(argv); +diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c +index 2bcde57..72d2250 100644 +--- a/drivers/md/dm-table.c ++++ b/drivers/md/dm-table.c +@@ -187,8 +187,10 @@ static int alloc_targets(struct dm_table *t, unsigned int num) + + /* + * Allocate both the target array and offset array at once. ++ * Append an empty entry to catch sectors beyond the end of ++ * the device. + */ +- n_highs = (sector_t *) dm_vcalloc(num, sizeof(struct dm_target) + ++ n_highs = (sector_t *) dm_vcalloc(num + 1, sizeof(struct dm_target) + + sizeof(sector_t)); + if (!n_highs) + return -ENOMEM; +@@ -862,6 +864,9 @@ struct dm_target *dm_table_get_target(struct dm_table *t, unsigned int index) + + /* + * Search the btree for the correct target. ++ * ++ * Caller should check returned pointer with dm_target_is_valid() ++ * to trap I/O beyond end of device. + */ + struct dm_target *dm_table_find_target(struct dm_table *t, sector_t sector) + { +diff --git a/drivers/md/dm.c b/drivers/md/dm.c +index 998d450..fac09d5 100644 +--- a/drivers/md/dm.c ++++ b/drivers/md/dm.c +@@ -663,13 +663,19 @@ static struct bio *clone_bio(struct bio *bio, sector_t sector, + return clone; + } + +-static void __clone_and_map(struct clone_info *ci) ++static int __clone_and_map(struct clone_info *ci) + { + struct bio *clone, *bio = ci->bio; +- struct dm_target *ti = dm_table_find_target(ci->map, ci->sector); +- sector_t len = 0, max = max_io_len(ci->md, ci->sector, ti); ++ struct dm_target *ti; ++ sector_t len = 0, max; + struct dm_target_io *tio; + ++ ti = dm_table_find_target(ci->map, ci->sector); ++ if (!dm_target_is_valid(ti)) ++ return -EIO; ++ ++ max = max_io_len(ci->md, ci->sector, ti); ++ + /* + * Allocate a target io object. + */ +@@ -727,6 +733,9 @@ static void __clone_and_map(struct clone_info *ci) + do { + if (offset) { + ti = dm_table_find_target(ci->map, ci->sector); ++ if (!dm_target_is_valid(ti)) ++ return -EIO; ++ + max = max_io_len(ci->md, ci->sector, ti); + + tio = alloc_tio(ci->md); +@@ -750,6 +759,8 @@ static void __clone_and_map(struct clone_info *ci) + + ci->idx++; + } ++ ++ return 0; + } + + /* +@@ -758,6 +769,7 @@ static void __clone_and_map(struct clone_info *ci) + static void __split_bio(struct mapped_device *md, struct bio *bio) + { + struct clone_info ci; ++ int error = 0; + + ci.map = dm_get_table(md); + if (!ci.map) { +@@ -777,11 +789,11 @@ static void __split_bio(struct mapped_device *md, struct bio *bio) + ci.idx = bio->bi_idx; + + start_io_acct(ci.io); +- while (ci.sector_count) +- __clone_and_map(&ci); ++ while (ci.sector_count && !error) ++ error = __clone_and_map(&ci); + + /* drop the extra reference count */ +- dec_pending(ci.io, 0); ++ dec_pending(ci.io, error); + dm_table_put(ci.map); + } + /*----------------------------------------------------------------- +diff --git a/drivers/md/dm.h b/drivers/md/dm.h +index 462ee65..07298a3 100644 +--- a/drivers/md/dm.h ++++ b/drivers/md/dm.h +@@ -113,6 +113,11 @@ int dm_table_any_congested(struct dm_table *t, int bdi_bits); + void dm_table_unplug_all(struct dm_table *t); + int dm_table_flush_all(struct dm_table *t); + ++/* ++ * To check the return value from dm_table_find_target(). ++ */ ++#define dm_target_is_valid(t) ((t)->table) ++ + /*----------------------------------------------------------------- + * A registry of target types. + *---------------------------------------------------------------*/ +diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c +index e86cacb..3085228 100644 +--- a/drivers/md/raid5.c ++++ b/drivers/md/raid5.c +@@ -2875,7 +2875,8 @@ static void handle_stripe5(struct stripe_head *sh) + md_done_sync(conf->mddev, STRIPE_SECTORS, 1); + } + +- if (s.expanding && s.locked == 0) ++ if (s.expanding && s.locked == 0 && ++ !test_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.pending)) + handle_stripe_expansion(conf, sh, NULL); + + if (sh->ops.count) +@@ -3077,7 +3078,8 @@ static void handle_stripe6(struct stripe_head *sh, struct page *tmp_page) + md_done_sync(conf->mddev, STRIPE_SECTORS, 1); + } + +- if (s.expanding && s.locked == 0) ++ if (s.expanding && s.locked == 0 && ++ !test_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.pending)) + handle_stripe_expansion(conf, sh, &r6s); + + spin_unlock(&sh->lock); +diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c +index 0222bba..91047c7 100644 +--- a/drivers/misc/thinkpad_acpi.c ++++ b/drivers/misc/thinkpad_acpi.c +@@ -968,9 +968,9 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) + KEY_UNKNOWN, /* 0x0C: FN+BACKSPACE */ + KEY_UNKNOWN, /* 0x0D: FN+INSERT */ + KEY_UNKNOWN, /* 0x0E: FN+DELETE */ +- KEY_BRIGHTNESSUP, /* 0x0F: FN+HOME (brightness up) */ ++ KEY_RESERVED, /* 0x0F: FN+HOME (brightness up) */ + /* Scan codes 0x10 to 0x1F: Extended ACPI HKEY hot keys */ +- KEY_BRIGHTNESSDOWN, /* 0x10: FN+END (brightness down) */ ++ KEY_RESERVED, /* 0x10: FN+END (brightness down) */ + KEY_RESERVED, /* 0x11: FN+PGUP (thinklight toggle) */ + KEY_UNKNOWN, /* 0x12: FN+PGDOWN */ + KEY_ZOOM, /* 0x13: FN+SPACE (zoom) */ +diff --git a/drivers/net/atl1/atl1_main.c b/drivers/net/atl1/atl1_main.c +index f23e13c..d2d4730 100644 +--- a/drivers/net/atl1/atl1_main.c ++++ b/drivers/net/atl1/atl1_main.c +@@ -121,7 +121,7 @@ static int __devinit atl1_sw_init(struct atl1_adapter *adapter) + struct atl1_hw *hw = &adapter->hw; + struct net_device *netdev = adapter->netdev; + +- hw->max_frame_size = netdev->mtu + ETH_HLEN + ETH_FCS_LEN; ++ hw->max_frame_size = netdev->mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; + hw->min_frame_size = ETH_ZLEN + ETH_FCS_LEN; + + adapter->wol = 0; +@@ -689,7 +689,7 @@ static int atl1_change_mtu(struct net_device *netdev, int new_mtu) + { + struct atl1_adapter *adapter = netdev_priv(netdev); + int old_mtu = netdev->mtu; +- int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN; ++ int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; + + if ((max_frame < ETH_ZLEN + ETH_FCS_LEN) || + (max_frame > MAX_JUMBO_FRAME_SIZE)) { +@@ -854,8 +854,8 @@ static u32 atl1_configure(struct atl1_adapter *adapter) + /* set Interrupt Clear Timer */ + iowrite16(adapter->ict, hw->hw_addr + REG_CMBDISDMA_TIMER); + +- /* set MTU, 4 : VLAN */ +- iowrite32(hw->max_frame_size + 4, hw->hw_addr + REG_MTU); ++ /* set max frame size hw will accept */ ++ iowrite32(hw->max_frame_size, hw->hw_addr + REG_MTU); + + /* jumbo size & rrd retirement timer */ + value = (((u32) hw->rx_jumbo_th & RXQ_JMBOSZ_TH_MASK) +diff --git a/drivers/net/cassini.c b/drivers/net/cassini.c +index f6e4030..0883112 100644 +--- a/drivers/net/cassini.c ++++ b/drivers/net/cassini.c +@@ -336,30 +336,6 @@ static inline void cas_mask_intr(struct cas *cp) + cas_disable_irq(cp, i); + } + +-static inline void cas_buffer_init(cas_page_t *cp) +-{ +- struct page *page = cp->buffer; +- atomic_set((atomic_t *)&page->lru.next, 1); +-} +- +-static inline int cas_buffer_count(cas_page_t *cp) +-{ +- struct page *page = cp->buffer; +- return atomic_read((atomic_t *)&page->lru.next); +-} +- +-static inline void cas_buffer_inc(cas_page_t *cp) +-{ +- struct page *page = cp->buffer; +- atomic_inc((atomic_t *)&page->lru.next); +-} +- +-static inline void cas_buffer_dec(cas_page_t *cp) +-{ +- struct page *page = cp->buffer; +- atomic_dec((atomic_t *)&page->lru.next); +-} +- + static void cas_enable_irq(struct cas *cp, const int ring) + { + if (ring == 0) { /* all but TX_DONE */ +@@ -497,7 +473,6 @@ static int cas_page_free(struct cas *cp, cas_page_t *page) + { + pci_unmap_page(cp->pdev, page->dma_addr, cp->page_size, + PCI_DMA_FROMDEVICE); +- cas_buffer_dec(page); + __free_pages(page->buffer, cp->page_order); + kfree(page); + return 0; +@@ -527,7 +502,6 @@ static cas_page_t *cas_page_alloc(struct cas *cp, const gfp_t flags) + page->buffer = alloc_pages(flags, cp->page_order); + if (!page->buffer) + goto page_err; +- cas_buffer_init(page); + page->dma_addr = pci_map_page(cp->pdev, page->buffer, 0, + cp->page_size, PCI_DMA_FROMDEVICE); + return page; +@@ -606,7 +580,7 @@ static void cas_spare_recover(struct cas *cp, const gfp_t flags) + list_for_each_safe(elem, tmp, &list) { + cas_page_t *page = list_entry(elem, cas_page_t, list); + +- if (cas_buffer_count(page) > 1) ++ if (page_count(page->buffer) > 1) + continue; + + list_del(elem); +@@ -1374,7 +1348,7 @@ static inline cas_page_t *cas_page_spare(struct cas *cp, const int index) + cas_page_t *page = cp->rx_pages[1][index]; + cas_page_t *new; + +- if (cas_buffer_count(page) == 1) ++ if (page_count(page->buffer) == 1) + return page; + + new = cas_page_dequeue(cp); +@@ -1394,7 +1368,7 @@ static cas_page_t *cas_page_swap(struct cas *cp, const int ring, + cas_page_t **page1 = cp->rx_pages[1]; + + /* swap if buffer is in use */ +- if (cas_buffer_count(page0[index]) > 1) { ++ if (page_count(page0[index]->buffer) > 1) { + cas_page_t *new = cas_page_spare(cp, index); + if (new) { + page1[index] = page0[index]; +@@ -1979,6 +1953,7 @@ static int cas_rx_process_pkt(struct cas *cp, struct cas_rx_comp *rxc, + struct cas_page *page; + struct sk_buff *skb; + void *addr, *crcaddr; ++ __sum16 csum; + char *p; + + hlen = CAS_VAL(RX_COMP2_HDR_SIZE, words[1]); +@@ -2062,10 +2037,10 @@ static int cas_rx_process_pkt(struct cas *cp, struct cas_rx_comp *rxc, + + skb_shinfo(skb)->nr_frags++; + skb->data_len += hlen - swivel; ++ skb->truesize += hlen - swivel; + skb->len += hlen - swivel; + + get_page(page->buffer); +- cas_buffer_inc(page); + frag->page = page->buffer; + frag->page_offset = off; + frag->size = hlen - swivel; +@@ -2090,7 +2065,6 @@ static int cas_rx_process_pkt(struct cas *cp, struct cas_rx_comp *rxc, + frag++; + + get_page(page->buffer); +- cas_buffer_inc(page); + frag->page = page->buffer; + frag->page_offset = 0; + frag->size = hlen; +@@ -2158,14 +2132,15 @@ end_copy_pkt: + skb_put(skb, alloclen); + } + +- i = CAS_VAL(RX_COMP4_TCP_CSUM, words[3]); ++ csum = (__force __sum16)htons(CAS_VAL(RX_COMP4_TCP_CSUM, words[3])); + if (cp->crc_size) { + /* checksum includes FCS. strip it out. */ +- i = csum_fold(csum_partial(crcaddr, cp->crc_size, i)); ++ csum = csum_fold(csum_partial(crcaddr, cp->crc_size, ++ csum_unfold(csum))); + if (addr) + cas_page_unmap(addr); + } +- skb->csum = ntohs(i ^ 0xffff); ++ skb->csum = csum_unfold(~csum); + skb->ip_summed = CHECKSUM_COMPLETE; + skb->protocol = eth_type_trans(skb, cp->dev); + return len; +@@ -2253,7 +2228,7 @@ static int cas_post_rxds_ringN(struct cas *cp, int ring, int num) + released = 0; + while (entry != last) { + /* make a new buffer if it's still in use */ +- if (cas_buffer_count(page[entry]) > 1) { ++ if (page_count(page[entry]->buffer) > 1) { + cas_page_t *new = cas_page_dequeue(cp); + if (!new) { + /* let the timer know that we need to +diff --git a/drivers/net/cassini.h b/drivers/net/cassini.h +index a970804..a201431 100644 +--- a/drivers/net/cassini.h ++++ b/drivers/net/cassini.h +@@ -4122,8 +4122,8 @@ cas_saturn_patch_t cas_saturn_patch[] = { + inserted into + outgoing frame. */ + struct cas_tx_desc { +- u64 control; +- u64 buffer; ++ __le64 control; ++ __le64 buffer; + }; + + /* descriptor ring for free buffers contains page-sized buffers. the index +@@ -4131,8 +4131,8 @@ struct cas_tx_desc { + * the completion ring. + */ + struct cas_rx_desc { +- u64 index; +- u64 buffer; ++ __le64 index; ++ __le64 buffer; + }; + + /* received packets are put on the completion ring. */ +@@ -4210,10 +4210,10 @@ struct cas_rx_desc { + #define RX_INDEX_RELEASE 0x0000000000002000ULL + + struct cas_rx_comp { +- u64 word1; +- u64 word2; +- u64 word3; +- u64 word4; ++ __le64 word1; ++ __le64 word2; ++ __le64 word3; ++ __le64 word4; + }; + + enum link_state { +@@ -4252,7 +4252,7 @@ struct cas_init_block { + struct cas_rx_comp rxcs[N_RX_COMP_RINGS][INIT_BLOCK_RX_COMP]; + struct cas_rx_desc rxds[N_RX_DESC_RINGS][INIT_BLOCK_RX_DESC]; + struct cas_tx_desc txds[N_TX_RINGS][INIT_BLOCK_TX]; +- u64 tx_compwb; ++ __le64 tx_compwb; + }; + + /* tiny buffers to deal with target abort issue. we allocate a bit +diff --git a/drivers/net/chelsio/cxgb2.c b/drivers/net/chelsio/cxgb2.c +index 231ce43..a82a1fa 100644 +--- a/drivers/net/chelsio/cxgb2.c ++++ b/drivers/net/chelsio/cxgb2.c +@@ -370,6 +370,8 @@ static char stats_strings[][ETH_GSTRING_LEN] = { + "TxInternalMACXmitError", + "TxFramesWithExcessiveDeferral", + "TxFCSErrors", ++ "TxJumboFramesOk", ++ "TxJumboOctetsOk", + + "RxOctetsOK", + "RxOctetsBad", +@@ -388,15 +390,16 @@ static char stats_strings[][ETH_GSTRING_LEN] = { + "RxInRangeLengthErrors", + "RxOutOfRangeLengthField", + "RxFrameTooLongErrors", ++ "RxJumboFramesOk", ++ "RxJumboOctetsOk", + + /* Port stats */ +- "RxPackets", + "RxCsumGood", +- "TxPackets", + "TxCsumOffload", + "TxTso", + "RxVlan", + "TxVlan", ++ "TxNeedHeadroom", + + /* Interrupt stats */ + "rx drops", +@@ -454,23 +457,56 @@ static void get_stats(struct net_device *dev, struct ethtool_stats *stats, + const struct cmac_statistics *s; + const struct sge_intr_counts *t; + struct sge_port_stats ss; +- unsigned int len; + + s = mac->ops->statistics_update(mac, MAC_STATS_UPDATE_FULL); +- +- len = sizeof(u64)*(&s->TxFCSErrors + 1 - &s->TxOctetsOK); +- memcpy(data, &s->TxOctetsOK, len); +- data += len; +- +- len = sizeof(u64)*(&s->RxFrameTooLongErrors + 1 - &s->RxOctetsOK); +- memcpy(data, &s->RxOctetsOK, len); +- data += len; +- ++ t = t1_sge_get_intr_counts(adapter->sge); + t1_sge_get_port_stats(adapter->sge, dev->if_port, &ss); +- memcpy(data, &ss, sizeof(ss)); +- data += sizeof(ss); + +- t = t1_sge_get_intr_counts(adapter->sge); ++ *data++ = s->TxOctetsOK; ++ *data++ = s->TxOctetsBad; ++ *data++ = s->TxUnicastFramesOK; ++ *data++ = s->TxMulticastFramesOK; ++ *data++ = s->TxBroadcastFramesOK; ++ *data++ = s->TxPauseFrames; ++ *data++ = s->TxFramesWithDeferredXmissions; ++ *data++ = s->TxLateCollisions; ++ *data++ = s->TxTotalCollisions; ++ *data++ = s->TxFramesAbortedDueToXSCollisions; ++ *data++ = s->TxUnderrun; ++ *data++ = s->TxLengthErrors; ++ *data++ = s->TxInternalMACXmitError; ++ *data++ = s->TxFramesWithExcessiveDeferral; ++ *data++ = s->TxFCSErrors; ++ *data++ = s->TxJumboFramesOK; ++ *data++ = s->TxJumboOctetsOK; ++ ++ *data++ = s->RxOctetsOK; ++ *data++ = s->RxOctetsBad; ++ *data++ = s->RxUnicastFramesOK; ++ *data++ = s->RxMulticastFramesOK; ++ *data++ = s->RxBroadcastFramesOK; ++ *data++ = s->RxPauseFrames; ++ *data++ = s->RxFCSErrors; ++ *data++ = s->RxAlignErrors; ++ *data++ = s->RxSymbolErrors; ++ *data++ = s->RxDataErrors; ++ *data++ = s->RxSequenceErrors; ++ *data++ = s->RxRuntErrors; ++ *data++ = s->RxJabberErrors; ++ *data++ = s->RxInternalMACRcvError; ++ *data++ = s->RxInRangeLengthErrors; ++ *data++ = s->RxOutOfRangeLengthField; ++ *data++ = s->RxFrameTooLongErrors; ++ *data++ = s->RxJumboFramesOK; ++ *data++ = s->RxJumboOctetsOK; ++ ++ *data++ = ss.rx_cso_good; ++ *data++ = ss.tx_cso; ++ *data++ = ss.tx_tso; ++ *data++ = ss.vlan_xtract; ++ *data++ = ss.vlan_insert; ++ *data++ = ss.tx_need_hdrroom; ++ + *data++ = t->rx_drops; + *data++ = t->pure_rsps; + *data++ = t->unhandled_irqs; +diff --git a/drivers/net/chelsio/pm3393.c b/drivers/net/chelsio/pm3393.c +index 678778a..2117c4f 100644 +--- a/drivers/net/chelsio/pm3393.c ++++ b/drivers/net/chelsio/pm3393.c +@@ -45,7 +45,7 @@ + + #include + +-#define OFFSET(REG_ADDR) (REG_ADDR << 2) ++#define OFFSET(REG_ADDR) ((REG_ADDR) << 2) + + /* Max frame size PM3393 can handle. Includes Ethernet header and CRC. */ + #define MAX_FRAME_SIZE 9600 +@@ -428,69 +428,26 @@ static int pm3393_set_speed_duplex_fc(struct cmac *cmac, int speed, int duplex, + return 0; + } + +-static void pm3393_rmon_update(struct adapter *adapter, u32 offs, u64 *val, +- int over) +-{ +- u32 val0, val1, val2; +- +- t1_tpi_read(adapter, offs, &val0); +- t1_tpi_read(adapter, offs + 4, &val1); +- t1_tpi_read(adapter, offs + 8, &val2); +- +- *val &= ~0ull << 40; +- *val |= val0 & 0xffff; +- *val |= (val1 & 0xffff) << 16; +- *val |= (u64)(val2 & 0xff) << 32; +- +- if (over) +- *val += 1ull << 40; ++#define RMON_UPDATE(mac, name, stat_name) \ ++{ \ ++ t1_tpi_read((mac)->adapter, OFFSET(name), &val0); \ ++ t1_tpi_read((mac)->adapter, OFFSET((name)+1), &val1); \ ++ t1_tpi_read((mac)->adapter, OFFSET((name)+2), &val2); \ ++ (mac)->stats.stat_name = (u64)(val0 & 0xffff) | \ ++ ((u64)(val1 & 0xffff) << 16) | \ ++ ((u64)(val2 & 0xff) << 32) | \ ++ ((mac)->stats.stat_name & \ ++ 0xffffff0000000000ULL); \ ++ if (ro & \ ++ (1ULL << ((name - SUNI1x10GEXP_REG_MSTAT_COUNTER_0_LOW) >> 2))) \ ++ (mac)->stats.stat_name += 1ULL << 40; \ + } + + static const struct cmac_statistics *pm3393_update_statistics(struct cmac *mac, + int flag) + { +- static struct { +- unsigned int reg; +- unsigned int offset; +- } hw_stats [] = { +- +-#define HW_STAT(name, stat_name) \ +- { name, (&((struct cmac_statistics *)NULL)->stat_name) - (u64 *)NULL } +- +- /* Rx stats */ +- HW_STAT(RxOctetsReceivedOK, RxOctetsOK), +- HW_STAT(RxUnicastFramesReceivedOK, RxUnicastFramesOK), +- HW_STAT(RxMulticastFramesReceivedOK, RxMulticastFramesOK), +- HW_STAT(RxBroadcastFramesReceivedOK, RxBroadcastFramesOK), +- HW_STAT(RxPAUSEMACCtrlFramesReceived, RxPauseFrames), +- HW_STAT(RxFrameCheckSequenceErrors, RxFCSErrors), +- HW_STAT(RxFramesLostDueToInternalMACErrors, +- RxInternalMACRcvError), +- HW_STAT(RxSymbolErrors, RxSymbolErrors), +- HW_STAT(RxInRangeLengthErrors, RxInRangeLengthErrors), +- HW_STAT(RxFramesTooLongErrors , RxFrameTooLongErrors), +- HW_STAT(RxJabbers, RxJabberErrors), +- HW_STAT(RxFragments, RxRuntErrors), +- HW_STAT(RxUndersizedFrames, RxRuntErrors), +- HW_STAT(RxJumboFramesReceivedOK, RxJumboFramesOK), +- HW_STAT(RxJumboOctetsReceivedOK, RxJumboOctetsOK), +- +- /* Tx stats */ +- HW_STAT(TxOctetsTransmittedOK, TxOctetsOK), +- HW_STAT(TxFramesLostDueToInternalMACTransmissionError, +- TxInternalMACXmitError), +- HW_STAT(TxTransmitSystemError, TxFCSErrors), +- HW_STAT(TxUnicastFramesTransmittedOK, TxUnicastFramesOK), +- HW_STAT(TxMulticastFramesTransmittedOK, TxMulticastFramesOK), +- HW_STAT(TxBroadcastFramesTransmittedOK, TxBroadcastFramesOK), +- HW_STAT(TxPAUSEMACCtrlFramesTransmitted, TxPauseFrames), +- HW_STAT(TxJumboFramesReceivedOK, TxJumboFramesOK), +- HW_STAT(TxJumboOctetsReceivedOK, TxJumboOctetsOK) +- }, *p = hw_stats; +- u64 ro; +- u32 val0, val1, val2, val3; +- u64 *stats = (u64 *) &mac->stats; +- unsigned int i; ++ u64 ro; ++ u32 val0, val1, val2, val3; + + /* Snap the counters */ + pmwrite(mac, SUNI1x10GEXP_REG_MSTAT_CONTROL, +@@ -504,14 +461,35 @@ static const struct cmac_statistics *pm3393_update_statistics(struct cmac *mac, + ro = ((u64)val0 & 0xffff) | (((u64)val1 & 0xffff) << 16) | + (((u64)val2 & 0xffff) << 32) | (((u64)val3 & 0xffff) << 48); + +- for (i = 0; i < ARRAY_SIZE(hw_stats); i++) { +- unsigned reg = p->reg - SUNI1x10GEXP_REG_MSTAT_COUNTER_0_LOW; +- +- pm3393_rmon_update((mac)->adapter, OFFSET(p->reg), +- stats + p->offset, ro & (reg >> 2)); +- } +- +- ++ /* Rx stats */ ++ RMON_UPDATE(mac, RxOctetsReceivedOK, RxOctetsOK); ++ RMON_UPDATE(mac, RxUnicastFramesReceivedOK, RxUnicastFramesOK); ++ RMON_UPDATE(mac, RxMulticastFramesReceivedOK, RxMulticastFramesOK); ++ RMON_UPDATE(mac, RxBroadcastFramesReceivedOK, RxBroadcastFramesOK); ++ RMON_UPDATE(mac, RxPAUSEMACCtrlFramesReceived, RxPauseFrames); ++ RMON_UPDATE(mac, RxFrameCheckSequenceErrors, RxFCSErrors); ++ RMON_UPDATE(mac, RxFramesLostDueToInternalMACErrors, ++ RxInternalMACRcvError); ++ RMON_UPDATE(mac, RxSymbolErrors, RxSymbolErrors); ++ RMON_UPDATE(mac, RxInRangeLengthErrors, RxInRangeLengthErrors); ++ RMON_UPDATE(mac, RxFramesTooLongErrors , RxFrameTooLongErrors); ++ RMON_UPDATE(mac, RxJabbers, RxJabberErrors); ++ RMON_UPDATE(mac, RxFragments, RxRuntErrors); ++ RMON_UPDATE(mac, RxUndersizedFrames, RxRuntErrors); ++ RMON_UPDATE(mac, RxJumboFramesReceivedOK, RxJumboFramesOK); ++ RMON_UPDATE(mac, RxJumboOctetsReceivedOK, RxJumboOctetsOK); ++ ++ /* Tx stats */ ++ RMON_UPDATE(mac, TxOctetsTransmittedOK, TxOctetsOK); ++ RMON_UPDATE(mac, TxFramesLostDueToInternalMACTransmissionError, ++ TxInternalMACXmitError); ++ RMON_UPDATE(mac, TxTransmitSystemError, TxFCSErrors); ++ RMON_UPDATE(mac, TxUnicastFramesTransmittedOK, TxUnicastFramesOK); ++ RMON_UPDATE(mac, TxMulticastFramesTransmittedOK, TxMulticastFramesOK); ++ RMON_UPDATE(mac, TxBroadcastFramesTransmittedOK, TxBroadcastFramesOK); ++ RMON_UPDATE(mac, TxPAUSEMACCtrlFramesTransmitted, TxPauseFrames); ++ RMON_UPDATE(mac, TxJumboFramesReceivedOK, TxJumboFramesOK); ++ RMON_UPDATE(mac, TxJumboOctetsReceivedOK, TxJumboOctetsOK); + + return &mac->stats; + } +diff --git a/drivers/net/chelsio/sge.c b/drivers/net/chelsio/sge.c +index e4f874a..d77f1eb 100644 +--- a/drivers/net/chelsio/sge.c ++++ b/drivers/net/chelsio/sge.c +@@ -986,11 +986,10 @@ void t1_sge_get_port_stats(const struct sge *sge, int port, + for_each_possible_cpu(cpu) { + struct sge_port_stats *st = per_cpu_ptr(sge->port_stats[port], cpu); + +- ss->rx_packets += st->rx_packets; + ss->rx_cso_good += st->rx_cso_good; +- ss->tx_packets += st->tx_packets; + ss->tx_cso += st->tx_cso; + ss->tx_tso += st->tx_tso; ++ ss->tx_need_hdrroom += st->tx_need_hdrroom; + ss->vlan_xtract += st->vlan_xtract; + ss->vlan_insert += st->vlan_insert; + } +@@ -1379,11 +1378,10 @@ static void sge_rx(struct sge *sge, struct freelQ *fl, unsigned int len) + } + __skb_pull(skb, sizeof(*p)); + +- skb->dev->last_rx = jiffies; + st = per_cpu_ptr(sge->port_stats[p->iff], smp_processor_id()); +- st->rx_packets++; + + skb->protocol = eth_type_trans(skb, adapter->port[p->iff].dev); ++ skb->dev->last_rx = jiffies; + if ((adapter->flags & RX_CSUM_ENABLED) && p->csum == 0xffff && + skb->protocol == htons(ETH_P_IP) && + (skb->data[9] == IPPROTO_TCP || skb->data[9] == IPPROTO_UDP)) { +@@ -1851,7 +1849,8 @@ int t1_start_xmit(struct sk_buff *skb, struct net_device *dev) + { + struct adapter *adapter = dev->priv; + struct sge *sge = adapter->sge; +- struct sge_port_stats *st = per_cpu_ptr(sge->port_stats[dev->if_port], smp_processor_id()); ++ struct sge_port_stats *st = per_cpu_ptr(sge->port_stats[dev->if_port], ++ smp_processor_id()); + struct cpl_tx_pkt *cpl; + struct sk_buff *orig_skb = skb; + int ret; +@@ -1859,6 +1858,18 @@ int t1_start_xmit(struct sk_buff *skb, struct net_device *dev) + if (skb->protocol == htons(ETH_P_CPL5)) + goto send; + ++ /* ++ * We are using a non-standard hard_header_len. ++ * Allocate more header room in the rare cases it is not big enough. ++ */ ++ if (unlikely(skb_headroom(skb) < dev->hard_header_len - ETH_HLEN)) { ++ skb = skb_realloc_headroom(skb, sizeof(struct cpl_tx_pkt_lso)); ++ ++st->tx_need_hdrroom; ++ dev_kfree_skb_any(orig_skb); ++ if (!skb) ++ return NETDEV_TX_OK; ++ } ++ + if (skb_shinfo(skb)->gso_size) { + int eth_type; + struct cpl_tx_pkt_lso *hdr; +@@ -1892,24 +1903,6 @@ int t1_start_xmit(struct sk_buff *skb, struct net_device *dev) + return NETDEV_TX_OK; + } + +- /* +- * We are using a non-standard hard_header_len and some kernel +- * components, such as pktgen, do not handle it right. +- * Complain when this happens but try to fix things up. +- */ +- if (unlikely(skb_headroom(skb) < dev->hard_header_len - ETH_HLEN)) { +- pr_debug("%s: headroom %d header_len %d\n", dev->name, +- skb_headroom(skb), dev->hard_header_len); +- +- if (net_ratelimit()) +- printk(KERN_ERR "%s: inadequate headroom in " +- "Tx packet\n", dev->name); +- skb = skb_realloc_headroom(skb, sizeof(*cpl)); +- dev_kfree_skb_any(orig_skb); +- if (!skb) +- return NETDEV_TX_OK; +- } +- + if (!(adapter->flags & UDP_CSUM_CAPABLE) && + skb->ip_summed == CHECKSUM_PARTIAL && + ip_hdr(skb)->protocol == IPPROTO_UDP) { +@@ -1955,7 +1948,6 @@ int t1_start_xmit(struct sk_buff *skb, struct net_device *dev) + cpl->vlan_valid = 0; + + send: +- st->tx_packets++; + dev->trans_start = jiffies; + ret = t1_sge_tx(skb, adapter, 0, dev); + +diff --git a/drivers/net/chelsio/sge.h b/drivers/net/chelsio/sge.h +index d132a0e..80165f9 100644 +--- a/drivers/net/chelsio/sge.h ++++ b/drivers/net/chelsio/sge.h +@@ -57,13 +57,12 @@ struct sge_intr_counts { + }; + + struct sge_port_stats { +- u64 rx_packets; /* # of Ethernet packets received */ + u64 rx_cso_good; /* # of successful RX csum offloads */ +- u64 tx_packets; /* # of TX packets */ + u64 tx_cso; /* # of TX checksum offloads */ + u64 tx_tso; /* # of TSO requests */ + u64 vlan_xtract; /* # of VLAN tag extractions */ + u64 vlan_insert; /* # of VLAN tag insertions */ ++ u64 tx_need_hdrroom; /* # of TX skbs in need of more header room */ + }; + + struct sk_buff; +diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c +index fcbe508..cbcdf14 100644 +--- a/drivers/net/forcedeth.c ++++ b/drivers/net/forcedeth.c +@@ -5564,35 +5564,35 @@ static struct pci_device_id pci_tbl[] = { + }, + { /* MCP77 Ethernet Controller */ + PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_32), +- .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, ++ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR, + }, + { /* MCP77 Ethernet Controller */ + PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_33), +- .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, ++ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR, + }, + { /* MCP77 Ethernet Controller */ + PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_34), +- .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, ++ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR, + }, + { /* MCP77 Ethernet Controller */ + PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_35), +- .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, ++ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR, + }, + { /* MCP79 Ethernet Controller */ + PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_36), +- .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, ++ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR, + }, + { /* MCP79 Ethernet Controller */ + PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_37), +- .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, ++ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR, + }, + { /* MCP79 Ethernet Controller */ + PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_38), +- .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, ++ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR, + }, + { /* MCP79 Ethernet Controller */ + PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_39), +- .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, ++ .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR, + }, + {0,}, + }; +diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c +index 2575077..3ed45a3 100644 +--- a/drivers/net/sky2.c ++++ b/drivers/net/sky2.c +@@ -812,8 +812,13 @@ static void sky2_mac_init(struct sky2_hw *hw, unsigned port) + + sky2_write32(hw, SK_REG(port, RX_GMF_CTRL_T), rx_reg); + +- /* Flush Rx MAC FIFO on any flow control or error */ +- sky2_write16(hw, SK_REG(port, RX_GMF_FL_MSK), GMR_FS_ANY_ERR); ++ if (hw->chip_id == CHIP_ID_YUKON_XL) { ++ /* Hardware errata - clear flush mask */ ++ sky2_write16(hw, SK_REG(port, RX_GMF_FL_MSK), 0); ++ } else { ++ /* Flush Rx MAC FIFO on any flow control or error */ ++ sky2_write16(hw, SK_REG(port, RX_GMF_FL_MSK), GMR_FS_ANY_ERR); ++ } + + /* Set threshold to 0xa (64 bytes) + 1 to workaround pause bug */ + reg = RX_GMF_FL_THR_DEF + 1; +@@ -1307,15 +1312,11 @@ static int sky2_up(struct net_device *dev) + */ + if (otherdev && netif_running(otherdev) && + (cap = pci_find_capability(hw->pdev, PCI_CAP_ID_PCIX))) { +- struct sky2_port *osky2 = netdev_priv(otherdev); + u16 cmd; + + cmd = sky2_pci_read16(hw, cap + PCI_X_CMD); + cmd &= ~PCI_X_CMD_MAX_SPLIT; + sky2_pci_write16(hw, cap + PCI_X_CMD, cmd); +- +- sky2->rx_csum = 0; +- osky2->rx_csum = 0; + } + + if (netif_msg_ifup(sky2)) +@@ -4017,7 +4018,7 @@ static __devinit struct net_device *sky2_init_netdev(struct sky2_hw *hw, + sky2->duplex = -1; + sky2->speed = -1; + sky2->advertising = sky2_supported_modes(hw); +- sky2->rx_csum = 1; ++ sky2->rx_csum = (hw->chip_id != CHIP_ID_YUKON_XL); + sky2->wol = wol; + + spin_lock_init(&sky2->phy_lock); +diff --git a/drivers/net/usb/kaweth.c b/drivers/net/usb/kaweth.c +index 524dc5f..9057d71 100644 +--- a/drivers/net/usb/kaweth.c ++++ b/drivers/net/usb/kaweth.c +@@ -70,7 +70,7 @@ + #define KAWETH_TX_TIMEOUT (5 * HZ) + #define KAWETH_SCRATCH_SIZE 32 + #define KAWETH_FIRMWARE_BUF_SIZE 4096 +-#define KAWETH_CONTROL_TIMEOUT (30 * HZ) ++#define KAWETH_CONTROL_TIMEOUT (30000) + + #define KAWETH_STATUS_BROKEN 0x0000001 + #define KAWETH_STATUS_CLOSING 0x0000002 +diff --git a/drivers/net/usb/mcs7830.c b/drivers/net/usb/mcs7830.c +index 6240b97..3bbc5c4 100644 +--- a/drivers/net/usb/mcs7830.c ++++ b/drivers/net/usb/mcs7830.c +@@ -94,7 +94,7 @@ static int mcs7830_get_reg(struct usbnet *dev, u16 index, u16 size, void *data) + + ret = usb_control_msg(xdev, usb_rcvctrlpipe(xdev, 0), MCS7830_RD_BREQ, + MCS7830_RD_BMREQ, 0x0000, index, data, +- size, msecs_to_jiffies(MCS7830_CTRL_TIMEOUT)); ++ size, MCS7830_CTRL_TIMEOUT); + return ret; + } + +@@ -105,7 +105,7 @@ static int mcs7830_set_reg(struct usbnet *dev, u16 index, u16 size, void *data) + + ret = usb_control_msg(xdev, usb_sndctrlpipe(xdev, 0), MCS7830_WR_BREQ, + MCS7830_WR_BMREQ, 0x0000, index, data, +- size, msecs_to_jiffies(MCS7830_CTRL_TIMEOUT)); ++ size, MCS7830_CTRL_TIMEOUT); + return ret; + } + +diff --git a/drivers/pci/hotplug/fakephp.c b/drivers/pci/hotplug/fakephp.c +index 027f686..02a09d5 100644 +--- a/drivers/pci/hotplug/fakephp.c ++++ b/drivers/pci/hotplug/fakephp.c +@@ -39,6 +39,7 @@ + #include + #include + #include ++#include + #include "../pci.h" + + #if !defined(MODULE) +@@ -63,10 +64,16 @@ struct dummy_slot { + struct list_head node; + struct hotplug_slot *slot; + struct pci_dev *dev; ++ struct work_struct remove_work; ++ unsigned long removed; + }; + + static int debug; + static LIST_HEAD(slot_list); ++static struct workqueue_struct *dummyphp_wq; ++ ++static void pci_rescan_worker(struct work_struct *work); ++static DECLARE_WORK(pci_rescan_work, pci_rescan_worker); + + static int enable_slot (struct hotplug_slot *slot); + static int disable_slot (struct hotplug_slot *slot); +@@ -109,7 +116,7 @@ static int add_slot(struct pci_dev *dev) + slot->name = &dev->dev.bus_id[0]; + dbg("slot->name = %s\n", slot->name); + +- dslot = kmalloc(sizeof(struct dummy_slot), GFP_KERNEL); ++ dslot = kzalloc(sizeof(struct dummy_slot), GFP_KERNEL); + if (!dslot) + goto error_info; + +@@ -164,6 +171,14 @@ static void remove_slot(struct dummy_slot *dslot) + err("Problem unregistering a slot %s\n", dslot->slot->name); + } + ++/* called from the single-threaded workqueue handler to remove a slot */ ++static void remove_slot_worker(struct work_struct *work) ++{ ++ struct dummy_slot *dslot = ++ container_of(work, struct dummy_slot, remove_work); ++ remove_slot(dslot); ++} ++ + /** + * Rescan slot. + * Tries hard not to re-enable already existing devices +@@ -267,11 +282,17 @@ static inline void pci_rescan(void) { + pci_rescan_buses(&pci_root_buses); + } + ++/* called from the single-threaded workqueue handler to rescan all pci buses */ ++static void pci_rescan_worker(struct work_struct *work) ++{ ++ pci_rescan(); ++} + + static int enable_slot(struct hotplug_slot *hotplug_slot) + { + /* mis-use enable_slot for rescanning of the pci bus */ +- pci_rescan(); ++ cancel_work_sync(&pci_rescan_work); ++ queue_work(dummyphp_wq, &pci_rescan_work); + return -ENODEV; + } + +@@ -306,6 +327,10 @@ static int disable_slot(struct hotplug_slot *slot) + err("Can't remove PCI devices with other PCI devices behind it yet.\n"); + return -ENODEV; + } ++ if (test_and_set_bit(0, &dslot->removed)) { ++ dbg("Slot already scheduled for removal\n"); ++ return -ENODEV; ++ } + /* search for subfunctions and disable them first */ + if (!(dslot->dev->devfn & 7)) { + for (func = 1; func < 8; func++) { +@@ -328,8 +353,9 @@ static int disable_slot(struct hotplug_slot *slot) + /* remove the device from the pci core */ + pci_remove_bus_device(dslot->dev); + +- /* blow away this sysfs entry and other parts. */ +- remove_slot(dslot); ++ /* queue work item to blow away this sysfs entry and other parts. */ ++ INIT_WORK(&dslot->remove_work, remove_slot_worker); ++ queue_work(dummyphp_wq, &dslot->remove_work); + + return 0; + } +@@ -340,6 +366,7 @@ static void cleanup_slots (void) + struct list_head *next; + struct dummy_slot *dslot; + ++ destroy_workqueue(dummyphp_wq); + list_for_each_safe (tmp, next, &slot_list) { + dslot = list_entry (tmp, struct dummy_slot, node); + remove_slot(dslot); +@@ -351,6 +378,10 @@ static int __init dummyphp_init(void) + { + info(DRIVER_DESC "\n"); + ++ dummyphp_wq = create_singlethread_workqueue(MY_NAME); ++ if (!dummyphp_wq) ++ return -ENOMEM; ++ + return pci_scan_buses(); + } + +diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c +index 50f2dd9..75831c8 100644 +--- a/drivers/pci/quirks.c ++++ b/drivers/pci/quirks.c +@@ -465,6 +465,12 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_31, quirk + DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_0, quirk_ich6_lpc_acpi ); + DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_2, quirk_ich6_lpc_acpi ); + DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_3, quirk_ich6_lpc_acpi ); ++DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_1, quirk_ich6_lpc_acpi ); ++DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_4, quirk_ich6_lpc_acpi ); ++DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_2, quirk_ich6_lpc_acpi ); ++DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_4, quirk_ich6_lpc_acpi ); ++DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_7, quirk_ich6_lpc_acpi ); ++DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_8, quirk_ich6_lpc_acpi ); + + /* + * VIA ACPI: One IO region pointed to by longword at +diff --git a/drivers/spi/omap2_mcspi.c b/drivers/spi/omap2_mcspi.c +index 6b357cd..5a827ea 100644 +--- a/drivers/spi/omap2_mcspi.c ++++ b/drivers/spi/omap2_mcspi.c +@@ -350,6 +350,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) + tx = xfer->tx_buf; + + do { ++ c -= 1; + if (tx != NULL) { + if (mcspi_wait_for_reg_bit(chstat_reg, + OMAP2_MCSPI_CHSTAT_TXS) < 0) { +@@ -380,7 +381,6 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) + word_len, *(rx - 1)); + #endif + } +- c -= 1; + } while (c); + } else if (word_len <= 16) { + u16 *rx; +@@ -389,6 +389,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) + rx = xfer->rx_buf; + tx = xfer->tx_buf; + do { ++ c -= 2; + if (tx != NULL) { + if (mcspi_wait_for_reg_bit(chstat_reg, + OMAP2_MCSPI_CHSTAT_TXS) < 0) { +@@ -419,7 +420,6 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) + word_len, *(rx - 1)); + #endif + } +- c -= 2; + } while (c); + } else if (word_len <= 32) { + u32 *rx; +@@ -428,6 +428,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) + rx = xfer->rx_buf; + tx = xfer->tx_buf; + do { ++ c -= 4; + if (tx != NULL) { + if (mcspi_wait_for_reg_bit(chstat_reg, + OMAP2_MCSPI_CHSTAT_TXS) < 0) { +@@ -458,7 +459,6 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) + word_len, *(rx - 1)); + #endif + } +- c -= 4; + } while (c); + } + +diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c +index 0bb8de4..7cf21d7 100644 +--- a/drivers/usb/serial/sierra.c ++++ b/drivers/usb/serial/sierra.c +@@ -100,6 +100,7 @@ static struct usb_device_id id_table [] = { + { USB_DEVICE(0x1199, 0x0218) }, /* Sierra Wireless MC5720 */ + { USB_DEVICE(0x0f30, 0x1b1d) }, /* Sierra Wireless MC5720 */ + { USB_DEVICE(0x1199, 0x0020) }, /* Sierra Wireless MC5725 */ ++ { USB_DEVICE(0x1199, 0x0220) }, /* Sierra Wireless MC5725 */ + { USB_DEVICE(0x1199, 0x0019) }, /* Sierra Wireless AirCard 595 */ + { USB_DEVICE(0x1199, 0x0021) }, /* Sierra Wireless AirCard 597E */ + { USB_DEVICE(0x1199, 0x0120) }, /* Sierra Wireless USB Dongle 595U */ +@@ -108,6 +109,7 @@ static struct usb_device_id id_table [] = { + { USB_DEVICE(0x1199, 0x6804) }, /* Sierra Wireless MC8755 */ + { USB_DEVICE(0x1199, 0x6803) }, /* Sierra Wireless MC8765 */ + { USB_DEVICE(0x1199, 0x6812) }, /* Sierra Wireless MC8775 & AC 875U */ ++ { USB_DEVICE(0x1199, 0x6813) }, /* Sierra Wireless MC8775 (Thinkpad internal) */ + { USB_DEVICE(0x1199, 0x6820) }, /* Sierra Wireless AirCard 875 */ + { USB_DEVICE(0x1199, 0x6832) }, /* Sierra Wireless MC8780*/ + { USB_DEVICE(0x1199, 0x6833) }, /* Sierra Wireless MC8781*/ +@@ -136,6 +138,7 @@ static struct usb_device_id id_table_3port [] = { + { USB_DEVICE(0x0f30, 0x1b1d) }, /* Sierra Wireless MC5720 */ + { USB_DEVICE(0x1199, 0x0218) }, /* Sierra Wireless MC5720 */ + { USB_DEVICE(0x1199, 0x0020) }, /* Sierra Wireless MC5725 */ ++ { USB_DEVICE(0x1199, 0x0220) }, /* Sierra Wireless MC5725 */ + { USB_DEVICE(0x1199, 0x0019) }, /* Sierra Wireless AirCard 595 */ + { USB_DEVICE(0x1199, 0x0021) }, /* Sierra Wireless AirCard 597E */ + { USB_DEVICE(0x1199, 0x0120) }, /* Sierra Wireless USB Dongle 595U*/ +@@ -144,6 +147,7 @@ static struct usb_device_id id_table_3port [] = { + { USB_DEVICE(0x1199, 0x6804) }, /* Sierra Wireless MC8755 */ + { USB_DEVICE(0x1199, 0x6803) }, /* Sierra Wireless MC8765 */ + { USB_DEVICE(0x1199, 0x6812) }, /* Sierra Wireless MC8775 & AC 875U */ ++ { USB_DEVICE(0x1199, 0x6813) }, /* Sierra Wireless MC8775 (Thinkpad internal) */ + { USB_DEVICE(0x1199, 0x6820) }, /* Sierra Wireless AirCard 875 */ + { USB_DEVICE(0x1199, 0x6832) }, /* Sierra Wireless MC8780*/ + { USB_DEVICE(0x1199, 0x6833) }, /* Sierra Wireless MC8781*/ +diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c +index dd41677..48a61da 100644 +--- a/fs/cifs/inode.c ++++ b/fs/cifs/inode.c +@@ -919,6 +919,7 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) + goto mkdir_out; + } + ++ mode &= ~current->fs->umask; + rc = CIFSPOSIXCreate(xid, pTcon, SMB_O_DIRECTORY | SMB_O_CREAT, + mode, NULL /* netfid */, pInfo, &oplock, + full_path, cifs_sb->local_nls, +diff --git a/fs/exec.c b/fs/exec.c +index 073b0b8..401b850 100644 +--- a/fs/exec.c ++++ b/fs/exec.c +@@ -1786,6 +1786,12 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs) + but keep the previous behaviour for now. */ + if (!ispipe && !S_ISREG(inode->i_mode)) + goto close_fail; ++ /* ++ * Dont allow local users get cute and trick others to coredump ++ * into their pre-created files: ++ */ ++ if (inode->i_uid != current->fsuid) ++ goto close_fail; + if (!file->f_op) + goto close_fail; + if (!file->f_op->write) +diff --git a/fs/ncpfs/mmap.c b/fs/ncpfs/mmap.c +index a94473d..5d8dcb9 100644 +--- a/fs/ncpfs/mmap.c ++++ b/fs/ncpfs/mmap.c +@@ -50,10 +50,6 @@ static int ncp_file_mmap_fault(struct vm_area_struct *area, + pos = vmf->pgoff << PAGE_SHIFT; + + count = PAGE_SIZE; +- if ((unsigned long)vmf->virtual_address + PAGE_SIZE > area->vm_end) { +- WARN_ON(1); /* shouldn't happen? */ +- count = area->vm_end - (unsigned long)vmf->virtual_address; +- } + /* what we can read in one go */ + bufsize = NCP_SERVER(inode)->buffer_size; + +diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c +index 10f6e7d..2dc0a54 100644 +--- a/fs/nfsd/nfs3xdr.c ++++ b/fs/nfsd/nfs3xdr.c +@@ -396,8 +396,11 @@ nfs3svc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p, + * Round the length of the data which was specified up to + * the next multiple of XDR units and then compare that + * against the length which was actually received. ++ * Note that when RPCSEC/GSS (for example) is used, the ++ * data buffer can be padded so dlen might be larger ++ * than required. It must never be smaller. + */ +- if (dlen != XDR_QUADLEN(len)*4) ++ if (dlen < XDR_QUADLEN(len)*4) + return 0; + + if (args->count > max_blocksize) { +diff --git a/fs/nfsd/nfsxdr.c b/fs/nfsd/nfsxdr.c +index cb3e7fa..bd3d5b9 100644 +--- a/fs/nfsd/nfsxdr.c ++++ b/fs/nfsd/nfsxdr.c +@@ -313,8 +313,11 @@ nfssvc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p, + * Round the length of the data which was specified up to + * the next multiple of XDR units and then compare that + * against the length which was actually received. ++ * Note that when RPCSEC/GSS (for example) is used, the ++ * data buffer can be padded so dlen might be larger ++ * than required. It must never be smaller. + */ +- if (dlen != XDR_QUADLEN(len)*4) ++ if (dlen < XDR_QUADLEN(len)*4) + return 0; + + rqstp->rq_vec[0].iov_base = (void*)p; +diff --git a/fs/splice.c b/fs/splice.c +index 02c39ae..2aa8f5a 100644 +--- a/fs/splice.c ++++ b/fs/splice.c +@@ -1234,6 +1234,9 @@ static int copy_from_user_mmap_sem(void *dst, const void __user *src, size_t n) + { + int partial; + ++ if (!access_ok(VERIFY_READ, src, n)) ++ return -EFAULT; ++ + pagefault_disable(); + partial = __copy_from_user_inatomic(dst, src, n); + pagefault_enable(); +@@ -1442,6 +1445,11 @@ static long vmsplice_to_user(struct file *file, const struct iovec __user *iov, + break; + } + ++ if (unlikely(!access_ok(VERIFY_WRITE, base, len))) { ++ error = -EFAULT; ++ break; ++ } ++ + sd.len = 0; + sd.total_len = len; + sd.flags = flags; +diff --git a/include/asm-m68k/Kbuild b/include/asm-m68k/Kbuild +index c68e168..1a922fa 100644 +--- a/include/asm-m68k/Kbuild ++++ b/include/asm-m68k/Kbuild +@@ -1 +1,2 @@ + include include/asm-generic/Kbuild.asm ++header-y += cachectl.h +diff --git a/include/asm-powerpc/systbl.h b/include/asm-powerpc/systbl.h +index cc6d872..11d5383 100644 +--- a/include/asm-powerpc/systbl.h ++++ b/include/asm-powerpc/systbl.h +@@ -308,8 +308,8 @@ COMPAT_SYS_SPU(move_pages) + SYSCALL_SPU(getcpu) + COMPAT_SYS(epoll_pwait) + COMPAT_SYS_SPU(utimensat) +-COMPAT_SYS(fallocate) + COMPAT_SYS_SPU(signalfd) + COMPAT_SYS_SPU(timerfd) + SYSCALL_SPU(eventfd) + COMPAT_SYS_SPU(sync_file_range2) ++COMPAT_SYS(fallocate) +diff --git a/include/asm-sparc64/dma-mapping.h b/include/asm-sparc64/dma-mapping.h +index 1fc6554..38cbec7 100644 +--- a/include/asm-sparc64/dma-mapping.h ++++ b/include/asm-sparc64/dma-mapping.h +@@ -25,15 +25,9 @@ struct dma_ops { + void (*sync_single_for_cpu)(struct device *dev, + dma_addr_t dma_handle, size_t size, + enum dma_data_direction direction); +- void (*sync_single_for_device)(struct device *dev, +- dma_addr_t dma_handle, size_t size, +- enum dma_data_direction direction); + void (*sync_sg_for_cpu)(struct device *dev, struct scatterlist *sg, + int nelems, + enum dma_data_direction direction); +- void (*sync_sg_for_device)(struct device *dev, struct scatterlist *sg, +- int nelems, +- enum dma_data_direction direction); + }; + extern const struct dma_ops *dma_ops; + +@@ -105,7 +99,7 @@ static inline void dma_sync_single_for_device(struct device *dev, + size_t size, + enum dma_data_direction direction) + { +- dma_ops->sync_single_for_device(dev, dma_handle, size, direction); ++ /* No flushing needed to sync cpu writes to the device. */ + } + + static inline void dma_sync_single_range_for_cpu(struct device *dev, +@@ -123,7 +117,7 @@ static inline void dma_sync_single_range_for_device(struct device *dev, + size_t size, + enum dma_data_direction direction) + { +- dma_sync_single_for_device(dev, dma_handle+offset, size, direction); ++ /* No flushing needed to sync cpu writes to the device. */ + } + + +@@ -138,7 +132,7 @@ static inline void dma_sync_sg_for_device(struct device *dev, + struct scatterlist *sg, int nelems, + enum dma_data_direction direction) + { +- dma_ops->sync_sg_for_device(dev, sg, nelems, direction); ++ /* No flushing needed to sync cpu writes to the device. */ + } + + static inline int dma_mapping_error(dma_addr_t dma_addr) +diff --git a/include/asm-sparc64/hypervisor.h b/include/asm-sparc64/hypervisor.h +index 524d498..3ad45df 100644 +--- a/include/asm-sparc64/hypervisor.h ++++ b/include/asm-sparc64/hypervisor.h +@@ -709,6 +709,10 @@ extern unsigned long sun4v_mmu_tsb_ctx0(unsigned long num_descriptions, + */ + #define HV_FAST_MMU_DEMAP_ALL 0x24 + ++#ifndef __ASSEMBLY__ ++extern void sun4v_mmu_demap_all(void); ++#endif ++ + /* mmu_map_perm_addr() + * TRAP: HV_FAST_TRAP + * FUNCTION: HV_FAST_MMU_MAP_PERM_ADDR +diff --git a/include/asm-sparc64/pci.h b/include/asm-sparc64/pci.h +index 1393e57..f59f257 100644 +--- a/include/asm-sparc64/pci.h ++++ b/include/asm-sparc64/pci.h +@@ -200,6 +200,10 @@ static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel) + struct device_node; + extern struct device_node *pci_device_to_OF_node(struct pci_dev *pdev); + ++#define HAVE_ARCH_PCI_RESOURCE_TO_USER ++extern void pci_resource_to_user(const struct pci_dev *dev, int bar, ++ const struct resource *rsrc, ++ resource_size_t *start, resource_size_t *end); + #endif /* __KERNEL__ */ + + #endif /* __SPARC64_PCI_H */ +diff --git a/include/linux/acpi.h b/include/linux/acpi.h +index bf5e000..919e0a5 100644 +--- a/include/linux/acpi.h ++++ b/include/linux/acpi.h +@@ -40,6 +40,7 @@ + #include + #include + #include ++#include + + + #ifdef CONFIG_ACPI +@@ -187,7 +188,9 @@ extern int ec_transaction(u8 command, + #endif /*CONFIG_ACPI_EC*/ + + extern int acpi_blacklisted(void); +-extern void acpi_bios_year(char *s); ++#ifdef CONFIG_DMI ++extern void acpi_dmi_osi_linux(int enable, const struct dmi_system_id *d); ++#endif + + #define ACPI_CSTATE_LIMIT_DEFINED /* for driver builds */ + #ifdef CONFIG_ACPI +@@ -247,5 +250,5 @@ static inline int acpi_boot_table_init(void) + return 0; + } + +-#endif /* CONFIG_ACPI */ ++#endif /* !CONFIG_ACPI */ + #endif /*_LINUX_ACPI_H*/ +diff --git a/include/linux/dmi.h b/include/linux/dmi.h +index b8ac7b0..d8a946f 100644 +--- a/include/linux/dmi.h ++++ b/include/linux/dmi.h +@@ -78,6 +78,7 @@ extern struct dmi_device * dmi_find_device(int type, const char *name, + extern void dmi_scan_machine(void); + extern int dmi_get_year(int field); + extern int dmi_name_in_vendors(char *str); ++extern int dmi_available; + + #else + +@@ -87,6 +88,7 @@ static inline struct dmi_device * dmi_find_device(int type, const char *name, + struct dmi_device *from) { return NULL; } + static inline int dmi_get_year(int year) { return 0; } + static inline int dmi_name_in_vendors(char *s) { return 0; } ++#define dmi_available 0 + + #endif + +diff --git a/include/linux/freezer.h b/include/linux/freezer.h +index efded00..7fa9500 100644 +--- a/include/linux/freezer.h ++++ b/include/linux/freezer.h +@@ -4,6 +4,7 @@ + #define FREEZER_H_INCLUDED + + #include ++#include + + #ifdef CONFIG_PM_SLEEP + /* +@@ -126,6 +127,24 @@ static inline void set_freezable(void) + current->flags &= ~PF_NOFREEZE; + } + ++/* ++ * Freezer-friendly wrapper around wait_event_interruptible(), originally ++ * defined in ++ */ ++ ++#define wait_event_freezable(wq, condition) \ ++({ \ ++ int __retval; \ ++ do { \ ++ __retval = wait_event_interruptible(wq, \ ++ (condition) || freezing(current)); \ ++ if (__retval && !freezing(current)) \ ++ break; \ ++ else if (!(condition)) \ ++ __retval = -ERESTARTSYS; \ ++ } while (try_to_freeze()); \ ++ __retval; \ ++}) + #else /* !CONFIG_PM_SLEEP */ + static inline int frozen(struct task_struct *p) { return 0; } + static inline int freezing(struct task_struct *p) { return 0; } +@@ -143,6 +162,10 @@ static inline void freezer_do_not_count(void) {} + static inline void freezer_count(void) {} + static inline int freezer_should_skip(struct task_struct *p) { return 0; } + static inline void set_freezable(void) {} ++ ++#define wait_event_freezable(wq, condition) \ ++ wait_event_interruptible(wq, condition) ++ + #endif /* !CONFIG_PM_SLEEP */ + + #endif /* FREEZER_H_INCLUDED */ +diff --git a/include/linux/input.h b/include/linux/input.h +index 36e00aa..5ec6b68 100644 +--- a/include/linux/input.h ++++ b/include/linux/input.h +@@ -853,7 +853,7 @@ struct ff_rumble_effect { + * defining effect parameters + * + * This structure is sent through ioctl from the application to the driver. +- * To create a new effect aplication should set its @id to -1; the kernel ++ * To create a new effect application should set its @id to -1; the kernel + * will return assigned @id which can later be used to update or delete + * this effect. + * +@@ -933,9 +933,82 @@ struct ff_effect { + #define BIT(x) (1UL<<((x)%BITS_PER_LONG)) + #define LONG(x) ((x)/BITS_PER_LONG) + ++/** ++ * struct input_dev - represents an input device ++ * @name: name of the device ++ * @phys: physical path to the device in the system hierarchy ++ * @uniq: unique identification code for the device (if device has it) ++ * @id: id of the device (struct input_id) ++ * @evbit: bitmap of types of events supported by the device (EV_KEY, ++ * EV_REL, etc.) ++ * @keybit: bitmap of keys/buttons this device has ++ * @relbit: bitmap of relative axes for the device ++ * @absbit: bitmap of absolute axes for the device ++ * @mscbit: bitmap of miscellaneous events supported by the device ++ * @ledbit: bitmap of leds present on the device ++ * @sndbit: bitmap of sound effects supported by the device ++ * @ffbit: bitmap of force feedback effects supported by the device ++ * @swbit: bitmap of switches present on the device ++ * @keycodemax: size of keycode table ++ * @keycodesize: size of elements in keycode table ++ * @keycode: map of scancodes to keycodes for this device ++ * @setkeycode: optional method to alter current keymap, used to implement ++ * sparse keymaps. If not supplied default mechanism will be used ++ * @getkeycode: optional method to retrieve current keymap. If not supplied ++ * default mechanism will be used ++ * @ff: force feedback structure associated with the device if device ++ * supports force feedback effects ++ * @repeat_key: stores key code of the last key pressed; used to implement ++ * software autorepeat ++ * @timer: timer for software autorepeat ++ * @sync: set to 1 when there were no new events since last EV_SYNC ++ * @abs: current values for reports from absolute axes ++ * @rep: current values for autorepeat parameters (delay, rate) ++ * @key: reflects current state of device's keys/buttons ++ * @led: reflects current state of device's LEDs ++ * @snd: reflects current state of sound effects ++ * @sw: reflects current state of device's switches ++ * @absmax: maximum values for events coming from absolute axes ++ * @absmin: minimum values for events coming from absolute axes ++ * @absfuzz: describes noisiness for axes ++ * @absflat: size of the center flat position (used by joydev) ++ * @open: this method is called when the very first user calls ++ * input_open_device(). The driver must prepare the device ++ * to start generating events (start polling thread, ++ * request an IRQ, submit URB, etc.) ++ * @close: this method is called when the very last user calls ++ * input_close_device(). ++ * @flush: purges the device. Most commonly used to get rid of force ++ * feedback effects loaded into the device when disconnecting ++ * from it ++ * @event: event handler for events sent _to_ the device, like EV_LED ++ * or EV_SND. The device is expected to carry out the requested ++ * action (turn on a LED, play sound, etc.) The call is protected ++ * by @event_lock and must not sleep ++ * @grab: input handle that currently has the device grabbed (via ++ * EVIOCGRAB ioctl). When a handle grabs a device it becomes sole ++ * recipient for all input events coming from the device ++ * @event_lock: this spinlock is is taken when input core receives ++ * and processes a new event for the device (in input_event()). ++ * Code that accesses and/or modifies parameters of a device ++ * (such as keymap or absmin, absmax, absfuzz, etc.) after device ++ * has been registered with input core must take this lock. ++ * @mutex: serializes calls to open(), close() and flush() methods ++ * @users: stores number of users (input handlers) that opened this ++ * device. It is used by input_open_device() and input_close_device() ++ * to make sure that dev->open() is only called when the first ++ * user opens device and dev->close() is called when the very ++ * last user closes the device ++ * @going_away: marks devices that are in a middle of unregistering and ++ * causes input_open_device*() fail with -ENODEV. ++ * @dev: driver model's view of this device ++ * @h_list: list of input handles associated with the device. When ++ * accessing the list dev->mutex must be held ++ * @node: used to place the device onto input_dev_list ++ */ + struct input_dev { + +- void *private; ++ void *private; /* do not use */ + + const char *name; + const char *phys; +@@ -963,8 +1036,6 @@ struct input_dev { + unsigned int repeat_key; + struct timer_list timer; + +- int state; +- + int sync; + + int abs[ABS_MAX + 1]; +@@ -987,8 +1058,11 @@ struct input_dev { + + struct input_handle *grab; + +- struct mutex mutex; /* serializes open and close operations */ ++ spinlock_t event_lock; ++ struct mutex mutex; ++ + unsigned int users; ++ int going_away; + + struct device dev; + union { /* temporarily so while we switching to struct device */ +@@ -1054,7 +1128,9 @@ struct input_handle; + /** + * struct input_handler - implements one of interfaces for input devices + * @private: driver-specific data +- * @event: event handler ++ * @event: event handler. This method is being called by input core with ++ * interrupts disabled and dev->event_lock spinlock held and so ++ * it may not sleep + * @connect: called when attaching a handler to an input device + * @disconnect: disconnects a handler from input device + * @start: starts handler for given handle. This function is called by +@@ -1066,10 +1142,18 @@ struct input_handle; + * @name: name of the handler, to be shown in /proc/bus/input/handlers + * @id_table: pointer to a table of input_device_ids this driver can + * handle +- * @blacklist: prointer to a table of input_device_ids this driver should ++ * @blacklist: pointer to a table of input_device_ids this driver should + * ignore even if they match @id_table + * @h_list: list of input handles associated with the handler + * @node: for placing the driver onto input_handler_list ++ * ++ * Input handlers attach to input devices and create input handles. There ++ * are likely several handlers attached to any given input device at the ++ * same time. All of them will get their copy of input event generated by ++ * the device. ++ * ++ * Note that input core serializes calls to connect() and disconnect() ++ * methods. + */ + struct input_handler { + +@@ -1091,6 +1175,18 @@ struct input_handler { + struct list_head node; + }; + ++/** ++ * struct input_handle - links input device with an input handler ++ * @private: handler-specific data ++ * @open: counter showing whether the handle is 'open', i.e. should deliver ++ * events from its device ++ * @name: name given to the handle by handler that created it ++ * @dev: input device the handle is attached to ++ * @handler: handler that works with the device through this handle ++ * @d_node: used to put the handle on device's list of attached handles ++ * @h_node: used to put the handle on handler's list of handles from which ++ * it gets events ++ */ + struct input_handle { + + void *private; +@@ -1213,7 +1309,7 @@ extern struct class input_class; + * @max_effects: maximum number of effects supported by device + * @effects: pointer to an array of effects currently loaded into device + * @effect_owners: array of effect owners; when file handle owning +- * an effect gets closed the effcet is automatically erased ++ * an effect gets closed the effect is automatically erased + * + * Every force-feedback device must implement upload() and playback() + * methods; erase() is optional. set_gain() and set_autocenter() need +diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h +index 97de8aa..0349e82 100644 +--- a/include/linux/pci_ids.h ++++ b/include/linux/pci_ids.h +@@ -2287,6 +2287,8 @@ + #define PCI_DEVICE_ID_INTEL_ICH9_4 0x2914 + #define PCI_DEVICE_ID_INTEL_ICH9_5 0x2919 + #define PCI_DEVICE_ID_INTEL_ICH9_6 0x2930 ++#define PCI_DEVICE_ID_INTEL_ICH9_7 0x2916 ++#define PCI_DEVICE_ID_INTEL_ICH9_8 0x2918 + #define PCI_DEVICE_ID_INTEL_82855PM_HB 0x3340 + #define PCI_DEVICE_ID_INTEL_82830_HB 0x3575 + #define PCI_DEVICE_ID_INTEL_82830_CGC 0x3577 +diff --git a/include/linux/pm.h b/include/linux/pm.h +index 48b71ba..71e589b 100644 +--- a/include/linux/pm.h ++++ b/include/linux/pm.h +@@ -344,6 +344,15 @@ static inline int call_platform_enable_wakeup(struct device *dev, int is_on) + device_set_wakeup_enable(dev,val); \ + } while(0) + ++/* ++ * Global Power Management flags ++ * Used to keep APM and ACPI from both being active ++ */ ++extern unsigned int pm_flags; ++ ++#define PM_APM 1 ++#define PM_ACPI 2 ++ + #endif /* __KERNEL__ */ + + #endif /* _LINUX_PM_H */ +diff --git a/include/linux/pm_legacy.h b/include/linux/pm_legacy.h +index 514729a..446f4f4 100644 +--- a/include/linux/pm_legacy.h ++++ b/include/linux/pm_legacy.h +@@ -4,10 +4,6 @@ + + #ifdef CONFIG_PM_LEGACY + +-extern int pm_active; +- +-#define PM_IS_ACTIVE() (pm_active != 0) +- + /* + * Register a device with power management + */ +@@ -21,8 +17,6 @@ int __deprecated pm_send_all(pm_request_t rqst, void *data); + + #else /* CONFIG_PM_LEGACY */ + +-#define PM_IS_ACTIVE() 0 +- + static inline struct pm_dev *pm_register(pm_dev_t type, + unsigned long id, + pm_callback callback) +diff --git a/include/linux/quicklist.h b/include/linux/quicklist.h +index 9371c61..39b6671 100644 +--- a/include/linux/quicklist.h ++++ b/include/linux/quicklist.h +@@ -56,14 +56,6 @@ static inline void __quicklist_free(int nr, void (*dtor)(void *), void *p, + struct page *page) + { + struct quicklist *q; +- int nid = page_to_nid(page); +- +- if (unlikely(nid != numa_node_id())) { +- if (dtor) +- dtor(p); +- __free_page(page); +- return; +- } + + q = &get_cpu_var(quicklist)[nr]; + *(void **)p = q->page; +diff --git a/kernel/kmod.c b/kernel/kmod.c +index c6a4f8a..bb7df2a 100644 +--- a/kernel/kmod.c ++++ b/kernel/kmod.c +@@ -451,13 +451,11 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info, + enum umh_wait wait) + { + DECLARE_COMPLETION_ONSTACK(done); +- int retval; ++ int retval = 0; + + helper_lock(); +- if (sub_info->path[0] == '\0') { +- retval = 0; ++ if (sub_info->path[0] == '\0') + goto out; +- } + + if (!khelper_wq || usermodehelper_disabled) { + retval = -EBUSY; +@@ -468,13 +466,14 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info, + sub_info->wait = wait; + + queue_work(khelper_wq, &sub_info->work); +- if (wait == UMH_NO_WAIT) /* task has freed sub_info */ +- return 0; ++ if (wait == UMH_NO_WAIT) /* task has freed sub_info */ ++ goto unlock; + wait_for_completion(&done); + retval = sub_info->retval; + +- out: ++out: + call_usermodehelper_freeinfo(sub_info); ++unlock: + helper_unlock(); + return retval; + } +diff --git a/kernel/power/main.c b/kernel/power/main.c +index 350b485..0e44534 100644 +--- a/kernel/power/main.c ++++ b/kernel/power/main.c +@@ -27,6 +27,9 @@ BLOCKING_NOTIFIER_HEAD(pm_chain_head); + + DEFINE_MUTEX(pm_mutex); + ++unsigned int pm_flags; ++EXPORT_SYMBOL(pm_flags); ++ + #ifdef CONFIG_SUSPEND + + /* This is just an arbitrary number */ +diff --git a/kernel/power/pm.c b/kernel/power/pm.c +index c50d152..60c73fa 100644 +--- a/kernel/power/pm.c ++++ b/kernel/power/pm.c +@@ -27,8 +27,6 @@ + #include + #include + +-int pm_active; +- + /* + * Locking notes: + * pm_devs_lock can be a semaphore providing pm ops are not called +@@ -204,6 +202,4 @@ int pm_send_all(pm_request_t rqst, void *data) + + EXPORT_SYMBOL(pm_register); + EXPORT_SYMBOL(pm_send_all); +-EXPORT_SYMBOL(pm_active); +- + +diff --git a/kernel/relay.c b/kernel/relay.c +index ad85501..91bbfb7 100644 +--- a/kernel/relay.c ++++ b/kernel/relay.c +@@ -92,6 +92,7 @@ static int relay_mmap_buf(struct rchan_buf *buf, struct vm_area_struct *vma) + return -EINVAL; + + vma->vm_ops = &relay_file_mmap_ops; ++ vma->vm_flags |= VM_DONTEXPAND; + vma->vm_private_data = buf; + buf->chan->cb->buf_mapped(buf, filp); + +diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c +index 0962e05..1984669 100644 +--- a/kernel/time/tick-broadcast.c ++++ b/kernel/time/tick-broadcast.c +@@ -387,45 +387,19 @@ int tick_resume_broadcast_oneshot(struct clock_event_device *bc) + } + + /* +- * Reprogram the broadcast device: +- * +- * Called with tick_broadcast_lock held and interrupts disabled. +- */ +-static int tick_broadcast_reprogram(void) +-{ +- ktime_t expires = { .tv64 = KTIME_MAX }; +- struct tick_device *td; +- int cpu; +- +- /* +- * Find the event which expires next: +- */ +- for (cpu = first_cpu(tick_broadcast_oneshot_mask); cpu != NR_CPUS; +- cpu = next_cpu(cpu, tick_broadcast_oneshot_mask)) { +- td = &per_cpu(tick_cpu_device, cpu); +- if (td->evtdev->next_event.tv64 < expires.tv64) +- expires = td->evtdev->next_event; +- } +- +- if (expires.tv64 == KTIME_MAX) +- return 0; +- +- return tick_broadcast_set_event(expires, 0); +-} +- +-/* + * Handle oneshot mode broadcasting + */ + static void tick_handle_oneshot_broadcast(struct clock_event_device *dev) + { + struct tick_device *td; + cpumask_t mask; +- ktime_t now; ++ ktime_t now, next_event; + int cpu; + + spin_lock(&tick_broadcast_lock); + again: + dev->next_event.tv64 = KTIME_MAX; ++ next_event.tv64 = KTIME_MAX; + mask = CPU_MASK_NONE; + now = ktime_get(); + /* Find all expired events */ +@@ -434,19 +408,31 @@ again: + td = &per_cpu(tick_cpu_device, cpu); + if (td->evtdev->next_event.tv64 <= now.tv64) + cpu_set(cpu, mask); ++ else if (td->evtdev->next_event.tv64 < next_event.tv64) ++ next_event.tv64 = td->evtdev->next_event.tv64; + } + + /* +- * Wakeup the cpus which have an expired event. The broadcast +- * device is reprogrammed in the return from idle code. ++ * Wakeup the cpus which have an expired event. ++ */ ++ tick_do_broadcast(mask); ++ ++ /* ++ * Two reasons for reprogram: ++ * ++ * - The global event did not expire any CPU local ++ * events. This happens in dyntick mode, as the maximum PIT ++ * delta is quite small. ++ * ++ * - There are pending events on sleeping CPUs which were not ++ * in the event mask + */ +- if (!tick_do_broadcast(mask)) { ++ if (next_event.tv64 != KTIME_MAX) { + /* +- * The global event did not expire any CPU local +- * events. This happens in dyntick mode, as the +- * maximum PIT delta is quite small. ++ * Rearm the broadcast device. If event expired, ++ * repeat the above + */ +- if (tick_broadcast_reprogram()) ++ if (tick_broadcast_set_event(next_event, 0)) + goto again; + } + spin_unlock(&tick_broadcast_lock); +diff --git a/mm/mmap.c b/mm/mmap.c +index 0d40e66..f6058f6 100644 +--- a/mm/mmap.c ++++ b/mm/mmap.c +@@ -1619,6 +1619,12 @@ static inline int expand_downwards(struct vm_area_struct *vma, + */ + if (unlikely(anon_vma_prepare(vma))) + return -ENOMEM; ++ ++ address &= PAGE_MASK; ++ error = security_file_mmap(0, 0, 0, 0, address, 1); ++ if (error) ++ return error; ++ + anon_vma_lock(vma); + + /* +@@ -1626,8 +1632,6 @@ static inline int expand_downwards(struct vm_area_struct *vma, + * is required to hold the mmap_sem in read mode. We need the + * anon_vma lock to serialize against concurrent expand_stacks. + */ +- address &= PAGE_MASK; +- error = 0; + + /* Somebody else might have raced and expanded it already */ + if (address < vma->vm_start) { +@@ -1938,6 +1942,10 @@ unsigned long do_brk(unsigned long addr, unsigned long len) + if (is_hugepage_only_range(mm, addr, len)) + return -EINVAL; + ++ error = security_file_mmap(0, 0, 0, 0, addr, 1); ++ if (error) ++ return error; ++ + flags = VM_DATA_DEFAULT_FLAGS | VM_ACCOUNT | mm->def_flags; + + error = arch_mmap_check(addr, len, flags); +@@ -2209,7 +2217,7 @@ int install_special_mapping(struct mm_struct *mm, + vma->vm_start = addr; + vma->vm_end = addr + len; + +- vma->vm_flags = vm_flags | mm->def_flags; ++ vma->vm_flags = vm_flags | mm->def_flags | VM_DONTEXPAND; + vma->vm_page_prot = protection_map[vma->vm_flags & 7]; + + vma->vm_ops = &special_mapping_vmops; +diff --git a/mm/quicklist.c b/mm/quicklist.c +index ae8189c..3f703f7 100644 +--- a/mm/quicklist.c ++++ b/mm/quicklist.c +@@ -26,9 +26,17 @@ DEFINE_PER_CPU(struct quicklist, quicklist)[CONFIG_NR_QUICK]; + static unsigned long max_pages(unsigned long min_pages) + { + unsigned long node_free_pages, max; ++ struct zone *zones = NODE_DATA(numa_node_id())->node_zones; ++ ++ node_free_pages = ++#ifdef CONFIG_ZONE_DMA ++ zone_page_state(&zones[ZONE_DMA], NR_FREE_PAGES) + ++#endif ++#ifdef CONFIG_ZONE_DMA32 ++ zone_page_state(&zones[ZONE_DMA32], NR_FREE_PAGES) + ++#endif ++ zone_page_state(&zones[ZONE_NORMAL], NR_FREE_PAGES); + +- node_free_pages = node_page_state(numa_node_id(), +- NR_FREE_PAGES); + max = node_free_pages / FRACTION_OF_NODE_MEM; + return max(max, min_pages); + } +diff --git a/mm/truncate.c b/mm/truncate.c +index 5cdfbc1..39da569 100644 +--- a/mm/truncate.c ++++ b/mm/truncate.c +@@ -95,11 +95,11 @@ truncate_complete_page(struct address_space *mapping, struct page *page) + if (page->mapping != mapping) + return; + +- cancel_dirty_page(page, PAGE_CACHE_SIZE); +- + if (PagePrivate(page)) + do_invalidatepage(page, 0); + ++ cancel_dirty_page(page, PAGE_CACHE_SIZE); ++ + remove_from_page_cache(page); + ClearPageUptodate(page); + ClearPageMappedToDisk(page); +diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c +index ef3f789..21af441 100644 +--- a/net/8021q/vlan.c ++++ b/net/8021q/vlan.c +@@ -768,7 +768,7 @@ static int vlan_ioctl_handler(void __user *arg) + case SET_VLAN_NAME_TYPE_CMD: + err = -EPERM; + if (!capable(CAP_NET_ADMIN)) +- return -EPERM; ++ break; + if ((args.u.name_type >= 0) && + (args.u.name_type < VLAN_NAME_TYPE_HIGHEST)) { + vlan_name_type = args.u.name_type; +diff --git a/net/atm/mpc.c b/net/atm/mpc.c +index 7c85aa5..181c1c8 100644 +--- a/net/atm/mpc.c ++++ b/net/atm/mpc.c +@@ -542,6 +542,13 @@ static int mpc_send_packet(struct sk_buff *skb, struct net_device *dev) + if (eth->h_proto != htons(ETH_P_IP)) + goto non_ip; /* Multi-Protocol Over ATM :-) */ + ++ /* Weed out funny packets (e.g., AF_PACKET or raw). */ ++ if (skb->len < ETH_HLEN + sizeof(struct iphdr)) ++ goto non_ip; ++ skb_set_network_header(skb, ETH_HLEN); ++ if (skb->len < ETH_HLEN + ip_hdr(skb)->ihl * 4 || ip_hdr(skb)->ihl < 5) ++ goto non_ip; ++ + while (i < mpc->number_of_mps_macs) { + if (!compare_ether_addr(eth->h_dest, (mpc->mps_macs + i*ETH_ALEN))) + if ( send_via_shortcut(skb, mpc) == 0 ) /* try shortcut */ +diff --git a/net/ax25/ax25_in.c b/net/ax25/ax25_in.c +index 0ddaff0..8a9f0ac 100644 +--- a/net/ax25/ax25_in.c ++++ b/net/ax25/ax25_in.c +@@ -124,7 +124,7 @@ int ax25_rx_iframe(ax25_cb *ax25, struct sk_buff *skb) + } + + skb_pull(skb, 1); /* Remove PID */ +- skb_reset_mac_header(skb); ++ skb->mac_header = skb->network_header; + skb_reset_network_header(skb); + skb->dev = ax25->ax25_dev->dev; + skb->pkt_type = PACKET_HOST; +diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c +index fc13130..22545bd 100644 +--- a/net/bridge/br_netfilter.c ++++ b/net/bridge/br_netfilter.c +@@ -142,6 +142,23 @@ static inline struct nf_bridge_info *nf_bridge_alloc(struct sk_buff *skb) + return skb->nf_bridge; + } + ++static inline struct nf_bridge_info *nf_bridge_unshare(struct sk_buff *skb) ++{ ++ struct nf_bridge_info *nf_bridge = skb->nf_bridge; ++ ++ if (atomic_read(&nf_bridge->use) > 1) { ++ struct nf_bridge_info *tmp = nf_bridge_alloc(skb); ++ ++ if (tmp) { ++ memcpy(tmp, nf_bridge, sizeof(struct nf_bridge_info)); ++ atomic_set(&tmp->use, 1); ++ nf_bridge_put(nf_bridge); ++ } ++ nf_bridge = tmp; ++ } ++ return nf_bridge; ++} ++ + static inline void nf_bridge_push_encap_header(struct sk_buff *skb) + { + unsigned int len = nf_bridge_encap_header_len(skb); +@@ -247,8 +264,9 @@ static void __br_dnat_complain(void) + * Let us first consider the case that ip_route_input() succeeds: + * + * If skb->dst->dev equals the logical bridge device the packet +- * came in on, we can consider this bridging. We then call +- * skb->dst->output() which will make the packet enter br_nf_local_out() ++ * came in on, we can consider this bridging. The packet is passed ++ * through the neighbour output function to build a new destination ++ * MAC address, which will make the packet enter br_nf_local_out() + * not much later. In that function it is assured that the iptables + * FORWARD chain is traversed for the packet. + * +@@ -285,12 +303,17 @@ static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb) + skb->nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING; + + skb->dev = bridge_parent(skb->dev); +- if (!skb->dev) +- kfree_skb(skb); +- else { ++ if (skb->dev) { ++ struct dst_entry *dst = skb->dst; ++ + nf_bridge_pull_encap_header(skb); +- skb->dst->output(skb); ++ ++ if (dst->hh) ++ return neigh_hh_output(dst->hh, skb); ++ else if (dst->neighbour) ++ return dst->neighbour->output(skb); + } ++ kfree_skb(skb); + return 0; + } + +@@ -638,6 +661,11 @@ static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff **pskb, + if (!skb->nf_bridge) + return NF_ACCEPT; + ++ /* Need exclusive nf_bridge_info since we might have multiple ++ * different physoutdevs. */ ++ if (!nf_bridge_unshare(skb)) ++ return NF_DROP; ++ + parent = bridge_parent(out); + if (!parent) + return NF_DROP; +@@ -721,6 +749,11 @@ static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb, + if (!skb->nf_bridge) + return NF_ACCEPT; + ++ /* Need exclusive nf_bridge_info since we might have multiple ++ * different physoutdevs. */ ++ if (!nf_bridge_unshare(skb)) ++ return NF_DROP; ++ + nf_bridge = skb->nf_bridge; + if (!(nf_bridge->mask & BRNF_BRIDGED_DNAT)) + return NF_ACCEPT; +diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c +index 5dbe580..5ccc2d1 100644 +--- a/net/ipv4/devinet.c ++++ b/net/ipv4/devinet.c +@@ -1030,7 +1030,7 @@ static void inetdev_changename(struct net_device *dev, struct in_device *in_dev) + memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); + if (named++ == 0) + continue; +- dot = strchr(ifa->ifa_label, ':'); ++ dot = strchr(old, ':'); + if (dot == NULL) { + sprintf(old, ":%d", named); + dot = old; +diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c +index 5c14ed6..4b09b25 100644 +--- a/net/ipv4/ip_gre.c ++++ b/net/ipv4/ip_gre.c +@@ -613,7 +613,7 @@ static int ipgre_rcv(struct sk_buff *skb) + offset += 4; + } + +- skb_reset_mac_header(skb); ++ skb->mac_header = skb->network_header; + __pskb_pull(skb, offset); + skb_reset_network_header(skb); + skb_postpull_rcsum(skb, skb_transport_header(skb), offset); +diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c +index c6d7152..b45a610 100644 +--- a/net/ipv4/raw.c ++++ b/net/ipv4/raw.c +@@ -270,6 +270,7 @@ static int raw_send_hdrinc(struct sock *sk, void *from, size_t length, + int hh_len; + struct iphdr *iph; + struct sk_buff *skb; ++ unsigned int iphlen; + int err; + + if (length > rt->u.dst.dev->mtu) { +@@ -303,7 +304,8 @@ static int raw_send_hdrinc(struct sock *sk, void *from, size_t length, + goto error_fault; + + /* We don't modify invalid header */ +- if (length >= sizeof(*iph) && iph->ihl * 4U <= length) { ++ iphlen = iph->ihl * 4; ++ if (iphlen >= sizeof(*iph) && iphlen <= length) { + if (!iph->saddr) + iph->saddr = rt->rt_src; + iph->check = 0; +diff --git a/net/ipv4/route.c b/net/ipv4/route.c +index 198b732..efc4a3d 100644 +--- a/net/ipv4/route.c ++++ b/net/ipv4/route.c +@@ -2648,11 +2648,10 @@ int ip_rt_dump(struct sk_buff *skb, struct netlink_callback *cb) + int idx, s_idx; + + s_h = cb->args[0]; ++ if (s_h < 0) ++ s_h = 0; + s_idx = idx = cb->args[1]; +- for (h = 0; h <= rt_hash_mask; h++) { +- if (h < s_h) continue; +- if (h > s_h) +- s_idx = 0; ++ for (h = s_h; h <= rt_hash_mask; h++) { + rcu_read_lock_bh(); + for (rt = rcu_dereference(rt_hash_table[h].chain), idx = 0; rt; + rt = rcu_dereference(rt->u.dst.rt_next), idx++) { +@@ -2669,6 +2668,7 @@ int ip_rt_dump(struct sk_buff *skb, struct netlink_callback *cb) + dst_release(xchg(&skb->dst, NULL)); + } + rcu_read_unlock_bh(); ++ s_idx = 0; + } + + done: +diff --git a/net/irda/af_irda.c b/net/irda/af_irda.c +index 4c670cf..82fdca2 100644 +--- a/net/irda/af_irda.c ++++ b/net/irda/af_irda.c +@@ -1115,8 +1115,6 @@ static int irda_create(struct socket *sock, int protocol) + self->max_sdu_size_rx = TTP_SAR_UNBOUND; + break; + default: +- IRDA_ERROR("%s: protocol not supported!\n", +- __FUNCTION__); + return -ESOCKTNOSUPPORT; + } + break; +diff --git a/net/key/af_key.c b/net/key/af_key.c +index 7a5e993..9c75b9e 100644 +--- a/net/key/af_key.c ++++ b/net/key/af_key.c +@@ -2780,12 +2780,22 @@ static struct sadb_msg *pfkey_get_base_msg(struct sk_buff *skb, int *errp) + + static inline int aalg_tmpl_set(struct xfrm_tmpl *t, struct xfrm_algo_desc *d) + { +- return t->aalgos & (1 << d->desc.sadb_alg_id); ++ unsigned int id = d->desc.sadb_alg_id; ++ ++ if (id >= sizeof(t->aalgos) * 8) ++ return 0; ++ ++ return (t->aalgos >> id) & 1; + } + + static inline int ealg_tmpl_set(struct xfrm_tmpl *t, struct xfrm_algo_desc *d) + { +- return t->ealgos & (1 << d->desc.sadb_alg_id); ++ unsigned int id = d->desc.sadb_alg_id; ++ ++ if (id >= sizeof(t->ealgos) * 8) ++ return 0; ++ ++ return (t->ealgos >> id) & 1; + } + + static int count_ah_combs(struct xfrm_tmpl *t) +diff --git a/net/netrom/nr_dev.c b/net/netrom/nr_dev.c +index c7b5d93..69e77d5 100644 +--- a/net/netrom/nr_dev.c ++++ b/net/netrom/nr_dev.c +@@ -56,7 +56,7 @@ int nr_rx_ip(struct sk_buff *skb, struct net_device *dev) + + /* Spoof incoming device */ + skb->dev = dev; +- skb_reset_mac_header(skb); ++ skb->mac_header = skb->network_header; + skb_reset_network_header(skb); + skb->pkt_type = PACKET_HOST; + +diff --git a/net/x25/x25_forward.c b/net/x25/x25_forward.c +index 8738ec7..3447803 100644 +--- a/net/x25/x25_forward.c ++++ b/net/x25/x25_forward.c +@@ -118,13 +118,14 @@ int x25_forward_data(int lci, struct x25_neigh *from, struct sk_buff *skb) { + goto out; + + if ( (skbn = pskb_copy(skb, GFP_ATOMIC)) == NULL){ +- goto out; ++ goto output; + + } + x25_transmit_link(skbn, nb); + +- x25_neigh_put(nb); + rc = 1; ++output: ++ x25_neigh_put(nb); + out: + return rc; + } +diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c +index 7012891..75629f4 100644 +--- a/net/xfrm/xfrm_policy.c ++++ b/net/xfrm/xfrm_policy.c +@@ -1479,8 +1479,9 @@ restart: + + if (sk && sk->sk_policy[1]) { + policy = xfrm_sk_policy_lookup(sk, XFRM_POLICY_OUT, fl); ++ err = PTR_ERR(policy); + if (IS_ERR(policy)) +- return PTR_ERR(policy); ++ goto dropdst; + } + + if (!policy) { +@@ -1491,8 +1492,9 @@ restart: + + policy = flow_cache_lookup(fl, dst_orig->ops->family, + dir, xfrm_policy_lookup); ++ err = PTR_ERR(policy); + if (IS_ERR(policy)) +- return PTR_ERR(policy); ++ goto dropdst; + } + + if (!policy) +@@ -1661,8 +1663,9 @@ restart: + return 0; + + error: +- dst_release(dst_orig); + xfrm_pols_put(pols, npols); ++dropdst: ++ dst_release(dst_orig); + *dst_p = NULL; + return err; + } +diff --git a/sound/oss/via82cxxx_audio.c b/sound/oss/via82cxxx_audio.c +index 5d3c037..f95aa09 100644 +--- a/sound/oss/via82cxxx_audio.c ++++ b/sound/oss/via82cxxx_audio.c +@@ -2104,6 +2104,7 @@ static struct page * via_mm_nopage (struct vm_area_struct * vma, + { + struct via_info *card = vma->vm_private_data; + struct via_channel *chan = &card->ch_out; ++ unsigned long max_bufs; + struct page *dmapage; + unsigned long pgoff; + int rd, wr; +@@ -2127,14 +2128,11 @@ static struct page * via_mm_nopage (struct vm_area_struct * vma, + rd = card->ch_in.is_mapped; + wr = card->ch_out.is_mapped; + +-#ifndef VIA_NDEBUG +- { +- unsigned long max_bufs = chan->frag_number; +- if (rd && wr) max_bufs *= 2; +- /* via_dsp_mmap() should ensure this */ +- assert (pgoff < max_bufs); +- } +-#endif ++ max_bufs = chan->frag_number; ++ if (rd && wr) ++ max_bufs *= 2; ++ if (pgoff >= max_bufs) ++ return NOPAGE_SIGBUS; + + /* if full-duplex (read+write) and we have two sets of bufs, + * then the playback buffers come first, sez soundcard.c */ +diff --git a/sound/usb/usx2y/usX2Yhwdep.c b/sound/usb/usx2y/usX2Yhwdep.c +index b76b3dd..e617d7e 100644 +--- a/sound/usb/usx2y/usX2Yhwdep.c ++++ b/sound/usb/usx2y/usX2Yhwdep.c +@@ -88,7 +88,7 @@ static int snd_us428ctls_mmap(struct snd_hwdep * hw, struct file *filp, struct v + us428->us428ctls_sharedmem->CtlSnapShotLast = -2; + } + area->vm_ops = &us428ctls_vm_ops; +- area->vm_flags |= VM_RESERVED; ++ area->vm_flags |= VM_RESERVED | VM_DONTEXPAND; + area->vm_private_data = hw->private_data; + return 0; + } +diff --git a/sound/usb/usx2y/usx2yhwdeppcm.c b/sound/usb/usx2y/usx2yhwdeppcm.c +index a5e7bcd..6e70520 100644 +--- a/sound/usb/usx2y/usx2yhwdeppcm.c ++++ b/sound/usb/usx2y/usx2yhwdeppcm.c +@@ -728,7 +728,7 @@ static int snd_usX2Y_hwdep_pcm_mmap(struct snd_hwdep * hw, struct file *filp, st + return -ENODEV; + } + area->vm_ops = &snd_usX2Y_hwdep_pcm_vm_ops; +- area->vm_flags |= VM_RESERVED; ++ area->vm_flags |= VM_RESERVED | VM_DONTEXPAND; + area->vm_private_data = hw->private_data; + return 0; + } diff --git a/sys-kernel/linux-sabayon/files/2.6.23/patch-2.6.23.15-16 b/sys-kernel/linux-sabayon/files/2.6.23/patch-2.6.23.15-16 new file mode 100644 index 000000000..8304250a7 --- /dev/null +++ b/sys-kernel/linux-sabayon/files/2.6.23/patch-2.6.23.15-16 @@ -0,0 +1,26 @@ +diff --git a/Makefile b/Makefile +index 8920cb8..3a932c7 100644 +--- a/Makefile ++++ b/Makefile +@@ -1,7 +1,7 @@ + VERSION = 2 + PATCHLEVEL = 6 + SUBLEVEL = 23 +-EXTRAVERSION = .15 ++EXTRAVERSION = .16 + NAME = Arr Matey! A Hairy Bilge Rat! + + # *DOCUMENTATION* +diff --git a/fs/splice.c b/fs/splice.c +index 2aa8f5a..1a9c0e6 100644 +--- a/fs/splice.c ++++ b/fs/splice.c +@@ -1289,7 +1289,7 @@ static int get_iovec_page_array(const struct iovec __user *iov, + if (unlikely(!len)) + break; + error = -EFAULT; +- if (unlikely(!base)) ++ if (!access_ok(VERIFY_READ, base, len)) + break; + + /* diff --git a/sys-kernel/linux-sabayon/linux-sabayon-2.6.23-r1.ebuild b/sys-kernel/linux-sabayon/linux-sabayon-2.6.23-r1.ebuild index 5b0fffb57..35f81c212 100644 --- a/sys-kernel/linux-sabayon/linux-sabayon-2.6.23-r1.ebuild +++ b/sys-kernel/linux-sabayon/linux-sabayon-2.6.23-r1.ebuild @@ -68,6 +68,11 @@ UNIPATCH_LIST=" ${FILESDIR}/${PV}/aufs-cvs-0710115-2.6.23.patch ${FILESDIR}/${PV}/linux-2.6.23.11-12.patch + ${FILESDIR}/${PV}/patch-2.6.23.12-13 + ${FILESDIR}/${PV}/patch-2.6.23.13-14 + ${FILESDIR}/${PV}/patch-2.6.23.14-15 + ${FILESDIR}/${PV}/patch-2.6.23.15-16 + " SRC_URI="${KERNEL_URI} ${GENPATCHES_URI} ${ARCH_URI} ${SUSPEND2_URI} ${SL_PATCHES_URI}" ## END: Exported data