• Tejun Heo's avatar
    workqueue: Remove WORK_OFFQ_CANCELING · f09b10b6
    Tejun Heo authored
    cancel[_delayed]_work_sync() guarantees that it can shut down
    self-requeueing work items. To achieve that, it grabs and then holds
    WORK_STRUCT_PENDING bit set while flushing the currently executing instance.
    As the PENDING bit is set, all queueing attempts including the
    self-requeueing ones fail and once the currently executing instance is
    flushed, the work item should be idle as long as someone else isn't actively
    queueing it.
    
    This means that the cancel_work_sync path may hold the PENDING bit set while
    flushing the target work item. This isn't a problem for the queueing path -
    it can just fail which is the desired effect. It doesn't affect flush. It
    doesn't matter to cancel_work either as it can just report that the work
    item has successfully canceled. However, if there's another cancel_work_sync
    attempt on the work item, it can't simply fail or report success and that
    would breach the guarantee that it should provide. cancel_work_sync has to
    wait for and grab that PENDING bit and go through the motions.
    
    WORK_OFFQ_CANCELING and wq_cancel_waitq are what implement this
    cancel_work_sync to cancel_work_sync wait mechanism. When a work item is
    being canceled, WORK_OFFQ_CANCELING is also set on it and other
    cancel_work_sync attempts wait on the bit to be cleared using the wait
    queue.
    
    While this works, it's an isolated wart which doesn't jive with the rest of
    flush and cancel mechanisms and forces enable_work() and disable_work() to
    require a sleepable context, which hampers their usability.
    
    Now that a work item can be disabled, we can use that to block queueing
    while cancel_work_sync is in progress. Instead of holding PENDING the bit,
    it can temporarily disable the work item, flush and then re-enable it as
    that'd achieve the same end result of blocking queueings while canceling and
    thus enable canceling of self-requeueing work items.
    
    - WORK_OFFQ_CANCELING and the surrounding mechanims are removed.
    
    - work_grab_pending() is now simpler, no longer has to wait for a blocking
      operation and thus can be called from any context.
    
    - With work_grab_pending() simplified, no need to use try_to_grab_pending()
      directly. All users are converted to use work_grab_pending().
    
    - __cancel_work_sync() is updated to __cancel_work() with
      WORK_CANCEL_DISABLE to cancel and plug racing queueing attempts. It then
      flushes and re-enables the work item if necessary.
    
    - These changes allow disable_work() and enable_work() to be called from any
      context.
    
    v2: Lai pointed out that mod_delayed_work_on() needs to check the disable
        count before queueing the delayed work item. Added
        clear_pending_if_disabled() call.
    Signed-off-by: default avatarTejun Heo <tj@kernel.org>
    Reviewed-by: default avatarLai Jiangshan <jiangshanlai@gmail.com>
    f09b10b6
workqueue.c 222 KB