[patch 00/73] 2.6.23-stable review - Kernel

This is a discussion on [patch 00/73] 2.6.23-stable review - Kernel ; 2.6.23-stable review patch. If anyone has any objections, please let us know. ------------------ From: Stephen Hemminger Backport commit 798fdd07fcc131f396e521febb4a7d42559bf4b5 I'm using a Marvell 88E8062 on a custom PPC64 blade and ran into RX lockups while validating the sky2 driver. The ...

+ Reply to Thread
Page 2 of 4 FirstFirst 1 2 3 4 LastLast
Results 21 to 40 of 66

Thread: [patch 00/73] 2.6.23-stable review

  1. [patch 14/73] sky2: RX lockup fix

    2.6.23-stable review patch. If anyone has any objections, please let us know.
    ------------------
    From: Stephen Hemminger

    Backport commit 798fdd07fcc131f396e521febb4a7d42559bf4b5

    I'm using a Marvell 88E8062 on a custom PPC64 blade and ran into RX
    lockups while validating the sky2 driver. The receive MAC FIFO would
    become stuck during testing with high traffic. One port of the 88E8062
    would lockup, while the other port remained functional. Re-inserting
    the sky2 module would not fix the problem - only a power cycle would.

    I looked over Marvell's most recent sk98lin driver and it looks like
    they had a "workaround" for the Yukon XL that the sky2 doesn't have yet.
    The sk98lin driver disables the RX MAC FIFO flush feature for all
    revisions of the Yukon XL.

    According to skgeinit.c of the sk98lin driver, "Flushing must be enabled
    (needed for ASF see dev. #4.29), but the flushing mask should be
    disabled (see dev. #4.115)". Nice. I implemented this same change in
    the sky2 driver and verified that the RX lockup I was seeing was
    resolved.

    Signed-off-by: Peter Tyser
    Signed-off-by: Stephen Hemminger
    Signed-off-by: Greg Kroah-Hartman


    ---
    drivers/net/sky2.c | 9 +++++++--
    1 file changed, 7 insertions(+), 2 deletions(-)

    --- a/drivers/net/sky2.c
    +++ b/drivers/net/sky2.c
    @@ -812,8 +812,13 @@ static void sky2_mac_init(struct sky2_hw

    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;

    --
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  2. [patch 13/73] sky2: disable rx checksum on Yukon XL

    2.6.23-stable review patch. If anyone has any objections, please let us know.
    ------------------
    From: Stephen Hemminger

    Backport of 8b31cfbcd1b54362ef06c85beb40e65a349169a2

    The Marvell Yukon XL chipset appears to have a hardware glitch
    where it will repeat the checksum of the last packet. Of course, this is
    timing sensitive and only happens sometimes...

    More info: http://bugzilla.kernel.org/show_bug.cgi?id=9381

    As a workaround just disable hardware checksumming by default on
    this chip version. The earlier workaround for PCIX, dual port
    was also on Yukon XL so don't need to disable checksumming there.

    Signed-off-by: Stephen Hemminger
    Signed-off-by: Greg Kroah-Hartman


    ---
    drivers/net/sky2.c | 6 +-----
    1 file changed, 1 insertion(+), 5 deletions(-)

    --- a/drivers/net/sky2.c
    +++ b/drivers/net/sky2.c
    @@ -1307,15 +1307,11 @@ static int sky2_up(struct net_device *de
    */
    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 +4013,7 @@ static __devinit struct net_device *sky2
    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);

    --
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  3. [patch 25/73] IPSEC: Avoid undefined shift operation when testing algorithm ID

    2.6.23-stable review patch. If anyone has any objections, please let us know.
    ------------------
    From: Herbert Xu

    [IPSEC]: Avoid undefined shift operation when testing algorithm ID

    [ Upstream commit: f398035f2dec0a6150833b0bc105057953594edb ]

    The aalgos/ealgos fields are only 32 bits wide. However, af_key tries
    to test them with the expression 1 << id where id can be as large as
    253. This produces different behaviour on different architectures.

    The following patch explicitly checks whether ID is greater than 31
    and fails the check if that's the case.

    We cannot easily extend the mask to be longer than 32 bits due to
    exposure to user-space. Besides, this whole interface is obsolete
    anyway in favour of the xfrm_user interface which doesn't use this
    bit mask in templates (well not within the kernel anyway).

    Signed-off-by: Herbert Xu
    Signed-off-by: David S. Miller
    Signed-off-by: Greg Kroah-Hartman

    ---
    net/key/af_key.c | 14 ++++++++++++--
    1 file changed, 12 insertions(+), 2 deletions(-)

    --- a/net/key/af_key.c
    +++ b/net/key/af_key.c
    @@ -2780,12 +2780,22 @@ static struct sadb_msg *pfkey_get_base_m

    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)

    --
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  4. [patch 21/73] CONNECTOR: Dont touch queue dev after decrement of ref count.

    2.6.23-stable review patch. If anyone has any objections, please let us know.
    ------------------
    From: Li Zefan

    [CONNECTOR]: Don't touch queue dev after decrement of ref count.

    [ Upstream commit: cf585ae8ae9ac7287a6d078425ea32f22bf7f1f7 ]

    cn_queue_free_callback() will touch 'dev'(i.e. cbq->pdev), so it
    should be called before atomic_dec(&dev->refcnt).

    Signed-off-by: Li Zefan
    Signed-off-by: David S. Miller
    Signed-off-by: Greg Kroah-Hartman

    ---
    drivers/connector/cn_queue.c | 2 +-
    1 file changed, 1 insertion(+), 1 deletion(-)

    --- a/drivers/connector/cn_queue.c
    +++ b/drivers/connector/cn_queue.c
    @@ -99,8 +99,8 @@ int cn_queue_add_callback(struct cn_queu
    spin_unlock_bh(&dev->queue_lock);

    if (found) {
    - atomic_dec(&dev->refcnt);
    cn_queue_free_callback(cbq);
    + atomic_dec(&dev->refcnt);
    return -EINVAL;
    }


    --
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  5. [patch 37/73] PM: ACPI and APM must not be enabled at the same time

    2.6.23-stable review patch. If anyone has any objections, please let us know.
    ------------------
    From: Len Brown

    patch 9f9adecd2d0e4f88fa0e8cb06c6ec207748df70a in mainline.

    ACPI and APM used "pm_active" to guarantee that
    they would not be simultaneously active.

    But pm_active was recently moved under CONFIG_PM_LEGACY,
    so that without CONFIG_PM_LEGACY, pm_active became a NOP --
    allowing ACPI and APM to both be simultaneously enabled.
    This caused unpredictable results, including boot hangs.

    Further, the code under CONFIG_PM_LEGACY is scheduled
    for removal.

    So replace pm_active with pm_flags.
    pm_flags depends only on CONFIG_PM,
    which is present for both CONFIG_APM and CONFIG_ACPI.

    http://bugzilla.kernel.org/show_bug.cgi?id=9194

    Signed-off-by: Len Brown
    Signed-off-by: Rafael J. Wysocki
    Signed-off-by: Greg Kroah-Hartman

    ---
    arch/i386/kernel/apm.c | 10 +++-------
    drivers/acpi/bus.c | 7 ++-----
    include/linux/pm.h | 9 +++++++++
    include/linux/pm_legacy.h | 6 ------
    kernel/power/main.c | 3 +++
    kernel/power/pm.c | 4 ----
    6 files changed, 17 insertions(+), 22 deletions(-)

    --- 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);
    --- 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();

    --- a/include/linux/pm.h
    +++ b/include/linux/pm.h
    @@ -344,6 +344,15 @@ static inline int call_platform_enable_w
    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 */
    --- 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_

    #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)
    --- 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 */
    --- 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

    EXPORT_SYMBOL(pm_register);
    EXPORT_SYMBOL(pm_send_all);
    -EXPORT_SYMBOL(pm_active);
    -


    --
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  6. [patch 41/73] Freezer: Fix APM emulation breakage

    2.6.23-stable review patch. If anyone has any objections, please let us know.
    ------------------
    From: Rafael J. Wysocki

    The APM emulation is currently broken as a result of commit
    831441862956fffa17b9801db37e6ea1650b0f69
    "Freezer: make kernel threads nonfreezable by default"
    that removed the PF_NOFREEZE annotations from apm_ioctl() without
    adding the appropriate freezer hooks. Fix it and remove the
    unnecessary variable flags from apm_ioctl().

    This problem has been fixed in the mainline by
    commit cb43c54ca05c01533c45e4d3abfe8f99b7acf624
    "Freezer: Fix APM emulation breakage".

    Signed-off-by: Rafael J. Wysocki
    Signed-off-by: Greg Kroah-Hartman

    ---
    drivers/char/apm-emulation.c | 15 ++++++++-------
    include/linux/freezer.h | 23 +++++++++++++++++++++++
    2 files changed, 31 insertions(+), 7 deletions(-)

    --- 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 f
    * 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 f
    * 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;
    --- 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(
    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 */

    --
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  7. [patch 36/73] ACPI: apply quirk_ich6_lpc_acpi to more ICH8 and ICH9

    2.6.23-stable review patch. If anyone has any objections, please let us know.
    ------------------
    From: Zhao Yakui

    patch d1ec7298fcefd7e4d1ca612da402ce9e5d5e2c13 in mainline.

    It is important that these resources be reserved
    to avoid conflicts with well known ACPI registers.

    Signed-off-by: Zhao Yakui
    Signed-off-by: Len Brown
    Signed-off-by: Greg Kroah-Hartman

    ---
    drivers/pci/quirks.c | 6 ++++++
    include/linux/pci_ids.h | 2 ++
    2 files changed, 8 insertions(+)

    --- a/drivers/pci/quirks.c
    +++ b/drivers/pci/quirks.c
    @@ -465,6 +465,12 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_I
    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
    --- 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

    --
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  8. [patch 44/73] quicklists: Only consider memory that can be used with GFP_KERNEL

    2.6.23-stable review patch. If anyone has any objections, please let us know.
    ------------------
    From: Christoph Lameter

    patch 96990a4ae979df9e235d01097d6175759331e88c in mainline.

    Quicklists calculates the size of the quicklists based on the number of
    free pages. This must be the number of free pages that can be allocated
    with GFP_KERNEL. node_page_state() includes the pages in ZONE_HIGHMEM and
    ZONE_MOVABLE which may lead the quicklists to become too large causing OOM.

    Signed-off-by: Christoph Lameter
    Tested-by: Dhaval Giani
    Signed-off-by: Andrew Morton
    Signed-off-by: Linus Torvalds
    Signed-off-by: Greg Kroah-Hartman

    ---
    mm/quicklist.c | 12 ++++++++++--
    1 file changed, 10 insertions(+), 2 deletions(-)

    --- a/mm/quicklist.c
    +++ b/mm/quicklist.c
    @@ -26,9 +26,17 @@ DEFINE_PER_CPU(struct quicklist, quickli
    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);
    }

    --
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  9. [patch 47/73] cxgb: fix stats


    2.6.23-stable review patch. If anyone has any objections, please let us know.
    ------------------
    From: Divy Le Ray

    patch e0348b9ae5374f9a24424ae680bcd80724415f60 in mainline.

    Fix MAC stats accounting.
    Fix get_stats.

    Signed-off-by: Divy Le Ray
    Signed-off-by: Jeff Garzik
    Signed-off-by: Greg Kroah-Hartman

    ---
    drivers/net/chelsio/cxgb2.c | 67 +++++++++++++++++++------
    drivers/net/chelsio/pm3393.c | 112 +++++++++++++++++--------------------------
    drivers/net/chelsio/sge.c | 4 -
    drivers/net/chelsio/sge.h | 2
    4 files changed, 96 insertions(+), 89 deletions(-)

    --- a/drivers/net/chelsio/cxgb2.c
    +++ b/drivers/net/chelsio/cxgb2.c
    @@ -370,7 +370,9 @@ static char stats_strings[][ETH_GSTRING_
    "TxInternalMACXmitError",
    "TxFramesWithExcessiveDeferral",
    "TxFCSErrors",
    -
    + "TxJumboFramesOk",
    + "TxJumboOctetsOk",
    +
    "RxOctetsOK",
    "RxOctetsBad",
    "RxUnicastFramesOK",
    @@ -388,11 +390,11 @@ static char stats_strings[][ETH_GSTRING_
    "RxInRangeLengthErrors",
    "RxOutOfRangeLengthField",
    "RxFrameTooLongErrors",
    + "RxJumboFramesOk",
    + "RxJumboOctetsOk",

    /* Port stats */
    - "RxPackets",
    "RxCsumGood",
    - "TxPackets",
    "TxCsumOffload",
    "TxTso",
    "RxVlan",
    @@ -455,23 +457,56 @@ static void get_stats(struct net_device
    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;
    --- 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(st
    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(TxFramesLostDueToInternalMACTransmissionEr ror,
    - 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 *pm3
    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;
    }
    --- a/drivers/net/chelsio/sge.c
    +++ b/drivers/net/chelsio/sge.c
    @@ -986,9 +986,7 @@ void t1_sge_get_port_stats(const struct
    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;
    @@ -1381,7 +1379,6 @@ static void sge_rx(struct sge *sge, stru
    __skb_pull(skb, sizeof(*p));

    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;
    @@ -1951,7 +1948,6 @@ int t1_start_xmit(struct sk_buff *skb, s
    cpl->vlan_valid = 0;

    send:
    - st->tx_packets++;
    dev->trans_start = jiffies;
    ret = t1_sge_tx(skb, adapter, 0, dev);

    --- a/drivers/net/chelsio/sge.h
    +++ b/drivers/net/chelsio/sge.h
    @@ -57,9 +57,7 @@ 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 */

    --
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  10. [patch 35/73] ACPICA: fix acpi_serialize hang regression

    2.6.23-stable review patch. If anyone has any objections, please let us know.
    ------------------
    From: Bob Moore

    patch 014d433f35d7f34b55dcc7b57c7635aaefc3757f in mainline.

    http://bugzilla.kernel.org/show_bug.cgi?id=8171

    Signed-off-by: Bob Moore
    Signed-off-by: Len Brown
    Signed-off-by: Greg Kroah-Hartman

    ---
    drivers/acpi/events/evregion.c | 8 ++++----
    1 file changed, 4 insertions(+), 4 deletions(-)

    --- a/drivers/acpi/events/evregion.c
    +++ b/drivers/acpi/events/evregion.c
    @@ -344,7 +344,7 @@ acpi_ev_address_space_dispatch(union acp
    * 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 acp

    /* 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 acp
    * 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 acp
    * 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);

    --
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  11. [patch 49/73] Input: evdev - implement proper locking

    2.6.23-stable review patch. If anyone has any objections, please let us know.
    ------------------
    From: Dmitry Torokhov

    patch 6addb1d6de1968b84852f54561cc9a999909b5a9 in mainline.

    Signed-off-by: Dmitry Torokhov
    Cc: Al Viro
    Signed-off-by: Greg Kroah-Hartman

    ---
    drivers/input/evdev.c | 719 +++++++++++++++++++++++++++++++++-----------------
    1 file changed, 476 insertions(+), 243 deletions(-)

    --- 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);
    -
    - kill_fasync(&client->fasync, SIGIO, POLL_IN);
    - }
    + do_gettimeofday(&event.time);
    + event.type = type;
    + event.code = code;
    + event.value = value;
    +
    + 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,142 @@ static int evdev_flush(struct file *file
    {
    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);
    +
    + 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 +247,44 @@ static int evdev_release(struct inode *i

    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 +320,14 @@ static inline size_t evdev_event_size(vo
    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 +344,8 @@ static int evdev_event_from_user(const c
    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 +356,8 @@ static int evdev_event_to_user(char __us
    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 +375,8 @@ static inline size_t evdev_event_size(vo
    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 +384,8 @@ static int evdev_event_from_user(const c
    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 +395,71 @@ static int evdev_event_to_user(char __us

    #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 +470,12 @@ static ssize_t evdev_read(struct file *f
    if (!evdev->exist)
    return -ENODEV;

    - while (client->head != client->tail && retval + evdev_event_size() <= count) {
    -
    - struct input_event *event = (struct input_event *) client->buffer + client->tail;
    + while (retval + evdev_event_size() <= count &&
    + evdev_fetch_next_event(client, &event)) {

    - 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 +570,8 @@ static int str_to_user(const char *str,
    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,186 +582,208 @@ static long evdev_ioctl_handler(struct f
    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 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 EVIOCGRAB:
    + if (p)
    + return evdev_grab(evdev, client);
    + else
    + return evdev_ungrab(evdev, client);
    +
    + default:
    +
    + if (_IOC_TYPE(cmd) != 'E')
    + return -EINVAL;
    +
    + if (_IOC_DIR(cmd) == _IOC_READ) {
    +
    + if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0, 0))) {
    +
    + unsigned long *bits;
    + int len;
    +
    + 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);
    }

    - default:
    + if (_IOC_NR(cmd) == _IOC_NR(EVIOCGKEY(0)))
    + return bits_to_user(dev->key, KEY_MAX, _IOC_SIZE(cmd),
    + p, compat_mode);

    - if (_IOC_TYPE(cmd) != 'E')
    - return -EINVAL;
    + if (_IOC_NR(cmd) == _IOC_NR(EVIOCGLED(0)))
    + return bits_to_user(dev->led, LED_MAX, _IOC_SIZE(cmd),
    + p, compat_mode);

    - if (_IOC_DIR(cmd) == _IOC_READ) {
    + 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) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0,0))) {
    + if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSW(0)))
    + return bits_to_user(dev->sw, SW_MAX, _IOC_SIZE(cmd),
    + p, compat_mode);

    - unsigned long *bits;
    - int len;
    + if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0)))
    + return str_to_user(dev->name, _IOC_SIZE(cmd), p);

    - 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);
    - }
    + if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0)))
    + return str_to_user(dev->phys, _IOC_SIZE(cmd), p);

    - 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(EVIOCGUNIQ(0)))
    + return str_to_user(dev->uniq, _IOC_SIZE(cmd), p);

    - 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) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {

    - if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSND(0)))
    - return bits_to_user(dev->snd, SND_MAX, _IOC_SIZE(cmd),
    - p, compat_mode);
    + t = _IOC_NR(cmd) & ABS_MAX;

    - if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSW(0)))
    - return bits_to_user(dev->sw, SW_MAX, _IOC_SIZE(cmd),
    - p, compat_mode);
    + 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) == _IOC_NR(EVIOCGNAME(0)))
    - return str_to_user(dev->name, _IOC_SIZE(cmd), p);
    + if (copy_to_user(p, &abs, sizeof(struct input_absinfo)))
    + return -EFAULT;

    - if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0)))
    - return str_to_user(dev->phys, _IOC_SIZE(cmd), p);
    + return 0;
    + }

    - if (_IOC_NR(cmd) == _IOC_NR(EVIOCGUNIQ(0)))
    - return str_to_user(dev->uniq, _IOC_SIZE(cmd), p);
    + }

    - if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {
    + if (_IOC_DIR(cmd) == _IOC_WRITE) {

    - t = _IOC_NR(cmd) & ABS_MAX;
    + if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(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];
    + t = _IOC_NR(cmd) & ABS_MAX;

    - if (copy_to_user(p, &abs, sizeof(struct input_absinfo)))
    - return -EFAULT;
    + if (copy_from_user(&abs, p,
    + sizeof(struct input_absinfo)))
    + return -EFAULT;
    +
    + /*
    + * 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;

    - return 0;
    - }
    + spin_unlock_irq(&dev->event_lock);

    + return 0;
    }
    + }
    + }
    + return -EINVAL;
    +}

    - if (_IOC_DIR(cmd) == _IOC_WRITE) {
    -
    - if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) {
    +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;

    - t = _IOC_NR(cmd) & ABS_MAX;
    + retval = mutex_lock_interruptible(&evdev->mutex);
    + if (retval)
    + return retval;

    - if (copy_from_user(&abs, p, sizeof(struct input_absinfo)))
    - return -EFAULT;
    + if (!evdev->exist) {
    + retval = -ENODEV;
    + goto out;
    + }

    - 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;
    + retval = evdev_do_ioctl(file, cmd, p, compat_mode);

    - return 0;
    - }
    - }
    - }
    - return -EINVAL;
    + out:
    + mutex_unlock(&evdev->mutex);
    + return retval;
    }

    static long evdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    @@ -609,27 +792,79 @@ static long evdev_ioctl(struct file *fil
    }

    #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 +872,10 @@ static int evdev_connect(struct input_ha
    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 +886,44 @@ static int evdev_connect(struct input_ha
    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 +932,10 @@ static int evdev_connect(struct input_ha
    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 +947,13 @@ static const struct input_device_id evde
    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)

    --
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  12. [patch 34/73] ACPI: Not register gsi for PCI IDE controller in legacy mode


    2.6.23-stable review patch. If anyone has any objections, please let us know.
    ------------------
    From: Alan Cox

    patch 96c2a8766bf4fe91abac863749c11637fabcc64f in mainline.

    When PCI IDE controller works in legacy mode and no PRT entry is found
    in ACPI PRT table, OSPM will neither read the irq number from the IDE
    PCI configuration space nor call the function of acpi_register_gsi to
    register gsi.

    http://bugzilla.kernel.org/show_bug.cgi?id=5637

    Signed-off-by: Alan Cox
    Signed-off-by: Zhao Yakui
    Signed-off-by: Zhang Rui
    Signed-off-by: Len Brown
    Signed-off-by: Greg Kroah-Hartman

    ---
    drivers/acpi/pci_irq.c | 9 +++++++++
    1 file changed, 9 insertions(+)

    --- a/drivers/acpi/pci_irq.c
    +++ b/drivers/acpi/pci_irq.c
    @@ -429,6 +429,15 @@ int acpi_pci_irq_enable(struct pci_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.

    --
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  13. [patch 39/73] USB: update sierra.c with latest device ids that are in 2.6.24-rc7

    2.6.23-stable review patch. If anyone has any objections, please let us know.
    ------------------
    From: Greg Kroah-Hartman

    Signed-off-by: Greg Kroah-Hartman

    ---
    drivers/usb/serial/sierra.c | 4 ++++
    1 file changed, 4 insertions(+)

    --- 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_3po
    { 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_3po
    { 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*/

    --
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  14. [patch 46/73] cxgb: fix T2 GSO


    2.6.23-stable review patch. If anyone has any objections, please let us know.
    ------------------
    From: Divy Le Ray

    patch 7832ee034b6ef78aab020c9ec1348544cd65ccbd in mainline.

    The patch ensures that a GSO skb has enough headroom
    to push an encapsulating cpl_tx_pkt_lso header.

    Signed-off-by: Divy Le Ray
    Signed-off-by: Jeff Garzik
    Signed-off-by: Greg Kroah-Hartman

    ---
    drivers/net/chelsio/cxgb2.c | 3 ++-
    drivers/net/chelsio/sge.c | 34 +++++++++++++++-------------------
    drivers/net/chelsio/sge.h | 1 +
    3 files changed, 18 insertions(+), 20 deletions(-)

    --- a/drivers/net/chelsio/cxgb2.c
    +++ b/drivers/net/chelsio/cxgb2.c
    @@ -397,7 +397,8 @@ static char stats_strings[][ETH_GSTRING_
    "TxTso",
    "RxVlan",
    "TxVlan",
    -
    + "TxNeedHeadroom",
    +
    /* Interrupt stats */
    "rx drops",
    "pure_rsps",
    --- a/drivers/net/chelsio/sge.c
    +++ b/drivers/net/chelsio/sge.c
    @@ -991,6 +991,7 @@ void t1_sge_get_port_stats(const struct
    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;
    }
    @@ -1851,7 +1852,8 @@ int t1_start_xmit(struct sk_buff *skb, s
    {
    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 +1861,18 @@ int t1_start_xmit(struct sk_buff *skb, s
    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 +1906,6 @@ int t1_start_xmit(struct sk_buff *skb, s
    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) {
    --- a/drivers/net/chelsio/sge.h
    +++ b/drivers/net/chelsio/sge.h
    @@ -64,6 +64,7 @@ struct sge_port_stats {
    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;

    --
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  15. [patch 43/73] quicklists: do not release off node pages early


    2.6.23-stable review patch. If anyone has any objections, please let us know.
    ------------------
    From: Christoph Lameter

    patch ed367fc3a7349b17354c7acef551533337764859 in mainline.

    quicklists must keep even off node pages on the quicklists until the TLB
    flush has been completed.

    Signed-off-by: Christoph Lameter
    Cc: Dhaval Giani
    Signed-off-by: Andrew Morton
    Signed-off-by: Linus Torvalds
    Signed-off-by: Greg Kroah-Hartman

    ---
    include/linux/quicklist.h | 8 --------
    1 file changed, 8 deletions(-)

    --- a/include/linux/quicklist.h
    +++ b/include/linux/quicklist.h
    @@ -56,14 +56,6 @@ static inline void __quicklist_free(int
    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;

    --
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  16. [patch 48/73] Input: implement proper locking in input core

    2.6.23-stable review patch. If anyone has any objections, please let us know.
    ------------------
    From: Dmitry Torokhov

    patch 8006479c9b75fb6594a7b746af3d7f1fbb68f18f in mainline.

    Also add some kerneldoc documentation to input.h

    Signed-off-by: Dmitry Torokhov
    Cc: Al Viro
    Signed-off-by: Greg Kroah-Hartman

    ---
    drivers/input/input.c | 666 ++++++++++++++++++++++++++++++++++++--------------
    include/linux/input.h | 112 +++++++-
    2 files changed, 595 insertions(+), 183 deletions(-)

    --- 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;
    -
    - if ((value > dev->abs[code] - dev->absfuzz[code]) &&
    - (value < dev->abs[code] + dev->absfuzz[code]))
    - value = (dev->abs[code] * 3 + value) >> 2;
    -
    - if ((value > dev->abs[code] - (dev->absfuzz[code] << 1)) &&
    - (value < dev->abs[code] + (dev->absfuzz[code] << 1)))
    - value = (dev->abs[code] + value) >> 1;
    - }
    + switch (type) {

    - if (dev->abs[code] == value)
    - return;
    + case EV_SYN:
    + switch (code) {
    + case SYN_CONFIG:
    + disposition = INPUT_PASS_TO_ALL;
    + break;

    - dev->abs[code] = value;
    + case SYN_REPORT:
    + if (!dev->sync) {
    + dev->sync = 1;
    + disposition = INPUT_PASS_TO_HANDLERS;
    + }
    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;
    +
    + case EV_ABS:
    + if (is_event_supported(code, dev->absbit, ABS_MAX)) {

    - if (dev->event)
    - dev->event(dev, type, code, value);
    + value = input_defuzz_abs_event(value,
    + dev->abs[code], dev->absfuzz[code]);

    - break;
    + if (dev->abs[code] != value) {
    + dev->abs[code] = value;
    + disposition = INPUT_PASS_TO_HANDLERS;
    + }
    + }
    + break;

    - case EV_LED:
    + case EV_REL:
    + if (is_event_supported(code, dev->relbit, REL_MAX) && value)
    + disposition = INPUT_PASS_TO_HANDLERS;

    - if (code > LED_MAX || !test_bit(code, dev->ledbit) || !!test_bit(code, dev->led) == value)
    - return;
    + break;

    - change_bit(code, dev->led);
    + case EV_MSC:
    + if (is_event_supported(code, dev->mscbit, MSC_MAX))
    + disposition = INPUT_PASS_TO_ALL;

    - if (dev->event)
    - dev->event(dev, type, code, value);
    + 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);
    + retval = dev->open(dev);

    - if (err)
    - handle->open--;
    + 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_p

    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(stru

    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 inpu

    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(str

    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(
    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_d
    }
    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_d
    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_d
    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_d
    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_
    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 inp
    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_h
    }
    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);

    --- 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

    --
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  17. [patch 45/73] chelsio: Fix skb->dev setting

    2.6.23-stable review patch. If anyone has any objections, please let us know.
    ------------------
    From: Divy Le Ray

    patch 7de6af0f23b25df8da9719ecae1916b669d0b03d in mainline.

    eth_type_trans() now sets skb->dev.
    Access skb->def after it gets set.

    Signed-off-by: Divy Le Ray
    Signed-off-by: David S. Miller
    Signed-off-by: Greg Kroah-Hartman

    ---
    drivers/net/chelsio/sge.c | 2 +-
    1 file changed, 1 insertion(+), 1 deletion(-)

    --- a/drivers/net/chelsio/sge.c
    +++ b/drivers/net/chelsio/sge.c
    @@ -1379,11 +1379,11 @@ static void sge_rx(struct sge *sge, stru
    }
    __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)) {

    --
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  18. [patch 42/73] vfs: coredumping fix (CVE-2007-6206)


    2.6.23-stable review patch. If anyone has any objections, please let us know.
    ------------------
    From: Ingo Molnar

    vfs: coredumping fix

    patch c46f739dd39db3b07ab5deb4e3ec81e1c04a91af in mainline

    fix: http://bugzilla.kernel.org/show_bug.cgi?id=3043

    only allow coredumping to the same uid that the coredumping
    task runs under.

    Signed-off-by: Ingo Molnar
    Acked-by: Alan Cox
    Acked-by: Christoph Hellwig
    Acked-by: Al Viro
    Signed-off-by: Linus Torvalds
    Cc: maximilian attems
    Signed-off-by: Greg Kroah-Hartman

    ---
    fs/exec.c | 6 ++++++
    1 file changed, 6 insertions(+)

    --- a/fs/exec.c
    +++ b/fs/exec.c
    @@ -1786,6 +1786,12 @@ int do_coredump(long signr, int exit_cod
    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)

    --
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  19. [patch 38/73] CRYPTO: padlock: Fix spurious ECB page fault

    2.6.23-stable review patch. If anyone has any objections, please let us know.
    ------------------
    From: Herbert Xu

    [CRYPTO] padlock: Fix spurious ECB page fault

    [ Upstream commit: d4a7dd8e637b322faaa934ffcd6dd07711af831f ]
    [ Upstream commit: 490fe3f05be3f7c87d7932bcb6e6e53e3db2cd9c ]

    The xcryptecb instruction always processes an even number of blocks so
    we need to ensure th existence of an extra block if we have to process
    an odd number of blocks.

    Signed-off-by: Herbert Xu
    Signed-off-by: Greg Kroah-Hartman

    ---
    drivers/crypto/padlock-aes.c | 53 +++++++++++++++++++++++++++++++++++++++----
    1 file changed, 49 insertions(+), 4 deletions(-)

    --- a/drivers/crypto/padlock-aes.c
    +++ b/drivers/crypto/padlock-aes.c
    @@ -419,13 +419,58 @@ static int aes_set_key(struct crypto_tfm
    /* ====== 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(con
    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 = {

    --
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  20. [patch 33/73] ACPICA: fix acpi-cpufreq boot crash due to _PSD return-by-reference


    2.6.23-stable review patch. If anyone has any objections, please let us know.
    ------------------
    From: Bob Moore

    patch 152c300d007c70c4a1847dad39ecdaba22e7d457 in mainline.

    Changed resolution of named references in packages

    Fixed a problem with the Package operator where all named
    references were created as object references and left otherwise
    unresolved. According to the ACPI specification, a Package can
    only contain Data Objects or references to control methods. The
    implication is that named references to Data Objects (Integer,
    Buffer, String, Package, BufferField, Field) should be resolved
    immediately upon package creation. This is the approach taken
    with this change. References to all other named objects (Methods,
    Devices, Scopes, etc.) are all now properly created as reference objects.

    http://bugzilla.kernel.org/show_bug.cgi?id=5328
    http://bugzilla.kernel.org/show_bug.cgi?id=9429

    Signed-off-by: Bob Moore
    Signed-off-by: Len Brown
    Signed-off-by: Greg Kroah-Hartman

    ---
    drivers/acpi/dispatcher/dsobject.c | 91 ++++++++++++++++++++++++++++++++++---
    1 file changed, 85 insertions(+), 6 deletions(-)

    --- a/drivers/acpi/dispatcher/dsobject.c
    +++ b/drivers/acpi/dispatcher/dsobject.c
    @@ -137,6 +137,71 @@ acpi_ds_build_internal_object(struct acp
    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_P TR
    + (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 acp
    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(struc
    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->

    --
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

+ Reply to Thread
Page 2 of 4 FirstFirst 1 2 3 4 LastLast