lkml.org 
[lkml]   [2012]   [Mar]   [16]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subject[ 32/38] Block: use a freezable workqueue for disk-event polling
    3.0-stable review patch.  If anyone has any objections, please let me know.

    ------------------

    From: Alan Stern <stern@rowland.harvard.edu>

    commit 62d3c5439c534b0e6c653fc63e6d8c67be3a57b1 upstream.

    This patch (as1519) fixes a bug in the block layer's disk-events
    polling. The polling is done by a work routine queued on the
    system_nrt_wq workqueue. Since that workqueue isn't freezable, the
    polling continues even in the middle of a system sleep transition.

    Obviously, polling a suspended drive for media changes and such isn't
    a good thing to do; in the case of USB mass-storage devices it can
    lead to real problems requiring device resets and even re-enumeration.

    The patch fixes things by creating a new system-wide, non-reentrant,
    freezable workqueue and using it for disk-events polling.

    Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
    Acked-by: Tejun Heo <tj@kernel.org>
    Acked-by: Rafael J. Wysocki <rjw@sisk.pl>
    Signed-off-by: Jens Axboe <axboe@kernel.dk>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

    ---
    block/genhd.c | 10 +++++-----
    include/linux/workqueue.h | 4 ++++
    kernel/workqueue.c | 7 ++++++-
    3 files changed, 15 insertions(+), 6 deletions(-)

    --- a/block/genhd.c
    +++ b/block/genhd.c
    @@ -1487,9 +1487,9 @@ static void __disk_unblock_events(struct
    intv = disk_events_poll_jiffies(disk);
    set_timer_slack(&ev->dwork.timer, intv / 4);
    if (check_now)
    - queue_delayed_work(system_nrt_wq, &ev->dwork, 0);
    + queue_delayed_work(system_nrt_freezable_wq, &ev->dwork, 0);
    else if (intv)
    - queue_delayed_work(system_nrt_wq, &ev->dwork, intv);
    + queue_delayed_work(system_nrt_freezable_wq, &ev->dwork, intv);
    out_unlock:
    spin_unlock_irqrestore(&ev->lock, flags);
    }
    @@ -1530,7 +1530,7 @@ void disk_check_events(struct gendisk *d
    spin_lock_irqsave(&ev->lock, flags);
    if (!ev->block) {
    cancel_delayed_work(&ev->dwork);
    - queue_delayed_work(system_nrt_wq, &ev->dwork, 0);
    + queue_delayed_work(system_nrt_freezable_wq, &ev->dwork, 0);
    }
    spin_unlock_irqrestore(&ev->lock, flags);
    }
    @@ -1568,7 +1568,7 @@ unsigned int disk_clear_events(struct ge

    /* uncondtionally schedule event check and wait for it to finish */
    disk_block_events(disk);
    - queue_delayed_work(system_nrt_wq, &ev->dwork, 0);
    + queue_delayed_work(system_nrt_freezable_wq, &ev->dwork, 0);
    flush_delayed_work(&ev->dwork);
    __disk_unblock_events(disk, false);

    @@ -1605,7 +1605,7 @@ static void disk_events_workfn(struct wo

    intv = disk_events_poll_jiffies(disk);
    if (!ev->block && intv)
    - queue_delayed_work(system_nrt_wq, &ev->dwork, intv);
    + queue_delayed_work(system_nrt_freezable_wq, &ev->dwork, intv);

    spin_unlock_irq(&ev->lock);

    --- a/include/linux/workqueue.h
    +++ b/include/linux/workqueue.h
    @@ -289,12 +289,16 @@ enum {
    *
    * system_freezable_wq is equivalent to system_wq except that it's
    * freezable.
    + *
    + * system_nrt_freezable_wq is equivalent to system_nrt_wq except that
    + * it's freezable.
    */
    extern struct workqueue_struct *system_wq;
    extern struct workqueue_struct *system_long_wq;
    extern struct workqueue_struct *system_nrt_wq;
    extern struct workqueue_struct *system_unbound_wq;
    extern struct workqueue_struct *system_freezable_wq;
    +extern struct workqueue_struct *system_nrt_freezable_wq;

    extern struct workqueue_struct *
    __alloc_workqueue_key(const char *name, unsigned int flags, int max_active,
    --- a/kernel/workqueue.c
    +++ b/kernel/workqueue.c
    @@ -252,11 +252,13 @@ struct workqueue_struct *system_long_wq
    struct workqueue_struct *system_nrt_wq __read_mostly;
    struct workqueue_struct *system_unbound_wq __read_mostly;
    struct workqueue_struct *system_freezable_wq __read_mostly;
    +struct workqueue_struct *system_nrt_freezable_wq __read_mostly;
    EXPORT_SYMBOL_GPL(system_wq);
    EXPORT_SYMBOL_GPL(system_long_wq);
    EXPORT_SYMBOL_GPL(system_nrt_wq);
    EXPORT_SYMBOL_GPL(system_unbound_wq);
    EXPORT_SYMBOL_GPL(system_freezable_wq);
    +EXPORT_SYMBOL_GPL(system_nrt_freezable_wq);

    #define CREATE_TRACE_POINTS
    #include <trace/events/workqueue.h>
    @@ -3796,8 +3798,11 @@ static int __init init_workqueues(void)
    WQ_UNBOUND_MAX_ACTIVE);
    system_freezable_wq = alloc_workqueue("events_freezable",
    WQ_FREEZABLE, 0);
    + system_nrt_freezable_wq = alloc_workqueue("events_nrt_freezable",
    + WQ_NON_REENTRANT | WQ_FREEZABLE, 0);
    BUG_ON(!system_wq || !system_long_wq || !system_nrt_wq ||
    - !system_unbound_wq || !system_freezable_wq);
    + !system_unbound_wq || !system_freezable_wq ||
    + !system_nrt_freezable_wq);
    return 0;
    }
    early_initcall(init_workqueues);



    \
     
     \ /
      Last update: 2012-03-17 00:55    [W:4.193 / U:0.004 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site