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 @@ -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; }