mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-17 09:43:59 +08:00
workqueue: move try_to_grab_pending() upwards
try_to_grab_pending() will be used by to-be-implemented mod_delayed_work[_on](). Move try_to_grab_pending() and related functions above queueing functions. This patch only moves functions around. Signed-off-by: Tejun Heo <tj@kernel.org>
This commit is contained in:
parent
715f130080
commit
bf4ede014e
@ -903,6 +903,149 @@ static struct worker *find_worker_executing_work(struct global_cwq *gcwq,
|
||||
work);
|
||||
}
|
||||
|
||||
/**
|
||||
* move_linked_works - move linked works to a list
|
||||
* @work: start of series of works to be scheduled
|
||||
* @head: target list to append @work to
|
||||
* @nextp: out paramter for nested worklist walking
|
||||
*
|
||||
* Schedule linked works starting from @work to @head. Work series to
|
||||
* be scheduled starts at @work and includes any consecutive work with
|
||||
* WORK_STRUCT_LINKED set in its predecessor.
|
||||
*
|
||||
* If @nextp is not NULL, it's updated to point to the next work of
|
||||
* the last scheduled work. This allows move_linked_works() to be
|
||||
* nested inside outer list_for_each_entry_safe().
|
||||
*
|
||||
* CONTEXT:
|
||||
* spin_lock_irq(gcwq->lock).
|
||||
*/
|
||||
static void move_linked_works(struct work_struct *work, struct list_head *head,
|
||||
struct work_struct **nextp)
|
||||
{
|
||||
struct work_struct *n;
|
||||
|
||||
/*
|
||||
* Linked worklist will always end before the end of the list,
|
||||
* use NULL for list head.
|
||||
*/
|
||||
list_for_each_entry_safe_from(work, n, NULL, entry) {
|
||||
list_move_tail(&work->entry, head);
|
||||
if (!(*work_data_bits(work) & WORK_STRUCT_LINKED))
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we're already inside safe list traversal and have moved
|
||||
* multiple works to the scheduled queue, the next position
|
||||
* needs to be updated.
|
||||
*/
|
||||
if (nextp)
|
||||
*nextp = n;
|
||||
}
|
||||
|
||||
static void cwq_activate_first_delayed(struct cpu_workqueue_struct *cwq)
|
||||
{
|
||||
struct work_struct *work = list_first_entry(&cwq->delayed_works,
|
||||
struct work_struct, entry);
|
||||
|
||||
trace_workqueue_activate_work(work);
|
||||
move_linked_works(work, &cwq->pool->worklist, NULL);
|
||||
__clear_bit(WORK_STRUCT_DELAYED_BIT, work_data_bits(work));
|
||||
cwq->nr_active++;
|
||||
}
|
||||
|
||||
/**
|
||||
* cwq_dec_nr_in_flight - decrement cwq's nr_in_flight
|
||||
* @cwq: cwq of interest
|
||||
* @color: color of work which left the queue
|
||||
* @delayed: for a delayed work
|
||||
*
|
||||
* A work either has completed or is removed from pending queue,
|
||||
* decrement nr_in_flight of its cwq and handle workqueue flushing.
|
||||
*
|
||||
* CONTEXT:
|
||||
* spin_lock_irq(gcwq->lock).
|
||||
*/
|
||||
static void cwq_dec_nr_in_flight(struct cpu_workqueue_struct *cwq, int color,
|
||||
bool delayed)
|
||||
{
|
||||
/* ignore uncolored works */
|
||||
if (color == WORK_NO_COLOR)
|
||||
return;
|
||||
|
||||
cwq->nr_in_flight[color]--;
|
||||
|
||||
if (!delayed) {
|
||||
cwq->nr_active--;
|
||||
if (!list_empty(&cwq->delayed_works)) {
|
||||
/* one down, submit a delayed one */
|
||||
if (cwq->nr_active < cwq->max_active)
|
||||
cwq_activate_first_delayed(cwq);
|
||||
}
|
||||
}
|
||||
|
||||
/* is flush in progress and are we at the flushing tip? */
|
||||
if (likely(cwq->flush_color != color))
|
||||
return;
|
||||
|
||||
/* are there still in-flight works? */
|
||||
if (cwq->nr_in_flight[color])
|
||||
return;
|
||||
|
||||
/* this cwq is done, clear flush_color */
|
||||
cwq->flush_color = -1;
|
||||
|
||||
/*
|
||||
* If this was the last cwq, wake up the first flusher. It
|
||||
* will handle the rest.
|
||||
*/
|
||||
if (atomic_dec_and_test(&cwq->wq->nr_cwqs_to_flush))
|
||||
complete(&cwq->wq->first_flusher->done);
|
||||
}
|
||||
|
||||
/*
|
||||
* Upon a successful return (>= 0), the caller "owns" WORK_STRUCT_PENDING bit,
|
||||
* so this work can't be re-armed in any way.
|
||||
*/
|
||||
static int try_to_grab_pending(struct work_struct *work)
|
||||
{
|
||||
struct global_cwq *gcwq;
|
||||
int ret = -1;
|
||||
|
||||
if (!test_and_set_bit(WORK_STRUCT_PENDING_BIT, work_data_bits(work)))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* The queueing is in progress, or it is already queued. Try to
|
||||
* steal it from ->worklist without clearing WORK_STRUCT_PENDING.
|
||||
*/
|
||||
gcwq = get_work_gcwq(work);
|
||||
if (!gcwq)
|
||||
return ret;
|
||||
|
||||
spin_lock_irq(&gcwq->lock);
|
||||
if (!list_empty(&work->entry)) {
|
||||
/*
|
||||
* This work is queued, but perhaps we locked the wrong gcwq.
|
||||
* In that case we must see the new value after rmb(), see
|
||||
* insert_work()->wmb().
|
||||
*/
|
||||
smp_rmb();
|
||||
if (gcwq == get_work_gcwq(work)) {
|
||||
debug_work_deactivate(work);
|
||||
list_del_init(&work->entry);
|
||||
cwq_dec_nr_in_flight(get_work_cwq(work),
|
||||
get_work_color(work),
|
||||
*work_data_bits(work) & WORK_STRUCT_DELAYED);
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
spin_unlock_irq(&gcwq->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* insert_work - insert a work into gcwq
|
||||
* @cwq: cwq @work belongs to
|
||||
@ -1831,107 +1974,6 @@ static bool manage_workers(struct worker *worker)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* move_linked_works - move linked works to a list
|
||||
* @work: start of series of works to be scheduled
|
||||
* @head: target list to append @work to
|
||||
* @nextp: out paramter for nested worklist walking
|
||||
*
|
||||
* Schedule linked works starting from @work to @head. Work series to
|
||||
* be scheduled starts at @work and includes any consecutive work with
|
||||
* WORK_STRUCT_LINKED set in its predecessor.
|
||||
*
|
||||
* If @nextp is not NULL, it's updated to point to the next work of
|
||||
* the last scheduled work. This allows move_linked_works() to be
|
||||
* nested inside outer list_for_each_entry_safe().
|
||||
*
|
||||
* CONTEXT:
|
||||
* spin_lock_irq(gcwq->lock).
|
||||
*/
|
||||
static void move_linked_works(struct work_struct *work, struct list_head *head,
|
||||
struct work_struct **nextp)
|
||||
{
|
||||
struct work_struct *n;
|
||||
|
||||
/*
|
||||
* Linked worklist will always end before the end of the list,
|
||||
* use NULL for list head.
|
||||
*/
|
||||
list_for_each_entry_safe_from(work, n, NULL, entry) {
|
||||
list_move_tail(&work->entry, head);
|
||||
if (!(*work_data_bits(work) & WORK_STRUCT_LINKED))
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we're already inside safe list traversal and have moved
|
||||
* multiple works to the scheduled queue, the next position
|
||||
* needs to be updated.
|
||||
*/
|
||||
if (nextp)
|
||||
*nextp = n;
|
||||
}
|
||||
|
||||
static void cwq_activate_first_delayed(struct cpu_workqueue_struct *cwq)
|
||||
{
|
||||
struct work_struct *work = list_first_entry(&cwq->delayed_works,
|
||||
struct work_struct, entry);
|
||||
|
||||
trace_workqueue_activate_work(work);
|
||||
move_linked_works(work, &cwq->pool->worklist, NULL);
|
||||
__clear_bit(WORK_STRUCT_DELAYED_BIT, work_data_bits(work));
|
||||
cwq->nr_active++;
|
||||
}
|
||||
|
||||
/**
|
||||
* cwq_dec_nr_in_flight - decrement cwq's nr_in_flight
|
||||
* @cwq: cwq of interest
|
||||
* @color: color of work which left the queue
|
||||
* @delayed: for a delayed work
|
||||
*
|
||||
* A work either has completed or is removed from pending queue,
|
||||
* decrement nr_in_flight of its cwq and handle workqueue flushing.
|
||||
*
|
||||
* CONTEXT:
|
||||
* spin_lock_irq(gcwq->lock).
|
||||
*/
|
||||
static void cwq_dec_nr_in_flight(struct cpu_workqueue_struct *cwq, int color,
|
||||
bool delayed)
|
||||
{
|
||||
/* ignore uncolored works */
|
||||
if (color == WORK_NO_COLOR)
|
||||
return;
|
||||
|
||||
cwq->nr_in_flight[color]--;
|
||||
|
||||
if (!delayed) {
|
||||
cwq->nr_active--;
|
||||
if (!list_empty(&cwq->delayed_works)) {
|
||||
/* one down, submit a delayed one */
|
||||
if (cwq->nr_active < cwq->max_active)
|
||||
cwq_activate_first_delayed(cwq);
|
||||
}
|
||||
}
|
||||
|
||||
/* is flush in progress and are we at the flushing tip? */
|
||||
if (likely(cwq->flush_color != color))
|
||||
return;
|
||||
|
||||
/* are there still in-flight works? */
|
||||
if (cwq->nr_in_flight[color])
|
||||
return;
|
||||
|
||||
/* this cwq is done, clear flush_color */
|
||||
cwq->flush_color = -1;
|
||||
|
||||
/*
|
||||
* If this was the last cwq, wake up the first flusher. It
|
||||
* will handle the rest.
|
||||
*/
|
||||
if (atomic_dec_and_test(&cwq->wq->nr_cwqs_to_flush))
|
||||
complete(&cwq->wq->first_flusher->done);
|
||||
}
|
||||
|
||||
/**
|
||||
* process_one_work - process single work
|
||||
* @worker: self
|
||||
@ -2767,48 +2809,6 @@ bool flush_work_sync(struct work_struct *work)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(flush_work_sync);
|
||||
|
||||
/*
|
||||
* Upon a successful return (>= 0), the caller "owns" WORK_STRUCT_PENDING bit,
|
||||
* so this work can't be re-armed in any way.
|
||||
*/
|
||||
static int try_to_grab_pending(struct work_struct *work)
|
||||
{
|
||||
struct global_cwq *gcwq;
|
||||
int ret = -1;
|
||||
|
||||
if (!test_and_set_bit(WORK_STRUCT_PENDING_BIT, work_data_bits(work)))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* The queueing is in progress, or it is already queued. Try to
|
||||
* steal it from ->worklist without clearing WORK_STRUCT_PENDING.
|
||||
*/
|
||||
gcwq = get_work_gcwq(work);
|
||||
if (!gcwq)
|
||||
return ret;
|
||||
|
||||
spin_lock_irq(&gcwq->lock);
|
||||
if (!list_empty(&work->entry)) {
|
||||
/*
|
||||
* This work is queued, but perhaps we locked the wrong gcwq.
|
||||
* In that case we must see the new value after rmb(), see
|
||||
* insert_work()->wmb().
|
||||
*/
|
||||
smp_rmb();
|
||||
if (gcwq == get_work_gcwq(work)) {
|
||||
debug_work_deactivate(work);
|
||||
list_del_init(&work->entry);
|
||||
cwq_dec_nr_in_flight(get_work_cwq(work),
|
||||
get_work_color(work),
|
||||
*work_data_bits(work) & WORK_STRUCT_DELAYED);
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
spin_unlock_irq(&gcwq->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool __cancel_work_timer(struct work_struct *work,
|
||||
struct timer_list* timer)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user