forked from rrcarlosr/Jetpack
284 lines
8.6 KiB
Diff
284 lines
8.6 KiB
Diff
From 41dc89eb8db8a13671d1e21071bce95810dd6831 Mon Sep 17 00:00:00 2001
|
|
From: Thomas Gleixner <tglx@linutronix.de>
|
|
Date: Wed, 22 Jun 2011 19:47:03 +0200
|
|
Subject: [PATCH 227/352] sched: Distangle worker accounting from rqlock
|
|
|
|
The worker accounting for cpu bound workers is plugged into the core
|
|
scheduler code and the wakeup code. This is not a hard requirement and
|
|
can be avoided by keeping track of the state in the workqueue code
|
|
itself.
|
|
|
|
Keep track of the sleeping state in the worker itself and call the
|
|
notifier before entering the core scheduler. There might be false
|
|
positives when the task is woken between that call and actually
|
|
scheduling, but that's not really different from scheduling and being
|
|
woken immediately after switching away. There is also no harm from
|
|
updating nr_running when the task returns from scheduling instead of
|
|
accounting it in the wakeup code.
|
|
|
|
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
|
|
Cc: Peter Zijlstra <peterz@infradead.org>
|
|
Cc: Tejun Heo <tj@kernel.org>
|
|
Cc: Jens Axboe <axboe@kernel.dk>
|
|
Cc: Linus Torvalds <torvalds@linux-foundation.org>
|
|
Link: http://lkml.kernel.org/r/20110622174919.135236139@linutronix.de
|
|
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
|
|
---
|
|
kernel/sched/core.c | 86 ++++++++-------------------------------------
|
|
kernel/workqueue.c | 52 ++++++++++++---------------
|
|
kernel/workqueue_internal.h | 5 +--
|
|
3 files changed, 41 insertions(+), 102 deletions(-)
|
|
|
|
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
|
|
index e61c6d30..b7b61c3 100644
|
|
--- a/kernel/sched/core.c
|
|
+++ b/kernel/sched/core.c
|
|
@@ -1745,10 +1745,6 @@ static inline void ttwu_activate(struct rq *rq, struct task_struct *p, int en_fl
|
|
{
|
|
activate_task(rq, p, en_flags);
|
|
p->on_rq = TASK_ON_RQ_QUEUED;
|
|
-
|
|
- /* if a worker is waking up, notify workqueue */
|
|
- if (p->flags & PF_WQ_WORKER)
|
|
- wq_worker_waking_up(p, cpu_of(rq));
|
|
}
|
|
|
|
/*
|
|
@@ -2200,58 +2196,6 @@ try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags)
|
|
}
|
|
|
|
/**
|
|
- * try_to_wake_up_local - try to wake up a local task with rq lock held
|
|
- * @p: the thread to be awakened
|
|
- * @cookie: context's cookie for pinning
|
|
- *
|
|
- * Put @p on the run-queue if it's not already there. The caller must
|
|
- * ensure that this_rq() is locked, @p is bound to this_rq() and not
|
|
- * the current task.
|
|
- */
|
|
-static void try_to_wake_up_local(struct task_struct *p, struct pin_cookie cookie)
|
|
-{
|
|
- struct rq *rq = task_rq(p);
|
|
-
|
|
- if (WARN_ON_ONCE(rq != this_rq()) ||
|
|
- WARN_ON_ONCE(p == current))
|
|
- return;
|
|
-
|
|
- lockdep_assert_held(&rq->lock);
|
|
-
|
|
- if (!raw_spin_trylock(&p->pi_lock)) {
|
|
- /*
|
|
- * This is OK, because current is on_cpu, which avoids it being
|
|
- * picked for load-balance and preemption/IRQs are still
|
|
- * disabled avoiding further scheduler activity on it and we've
|
|
- * not yet picked a replacement task.
|
|
- */
|
|
- lockdep_unpin_lock(&rq->lock, cookie);
|
|
- raw_spin_unlock(&rq->lock);
|
|
- raw_spin_lock(&p->pi_lock);
|
|
- raw_spin_lock(&rq->lock);
|
|
- lockdep_repin_lock(&rq->lock, cookie);
|
|
- }
|
|
-
|
|
- if (!(p->state & TASK_NORMAL))
|
|
- goto out;
|
|
-
|
|
- trace_sched_waking(p);
|
|
-
|
|
- if (!task_on_rq_queued(p)) {
|
|
- u64 wallclock = walt_ktime_clock();
|
|
-
|
|
- walt_update_task_ravg(rq->curr, rq, TASK_UPDATE, wallclock, 0);
|
|
- walt_update_task_ravg(p, rq, TASK_WAKE, wallclock, 0);
|
|
- ttwu_activate(rq, p, ENQUEUE_WAKEUP);
|
|
- }
|
|
-
|
|
- ttwu_do_wakeup(rq, p, 0, cookie);
|
|
- ttwu_stat(p, smp_processor_id(), 0);
|
|
-out:
|
|
- raw_spin_unlock(&p->pi_lock);
|
|
-}
|
|
-
|
|
-/**
|
|
* wake_up_process - Wake up a specific process
|
|
* @p: The process to be woken up.
|
|
*
|
|
@@ -3598,21 +3542,6 @@ static void __sched notrace __schedule(bool preempt)
|
|
} else {
|
|
deactivate_task(rq, prev, DEQUEUE_SLEEP);
|
|
prev->on_rq = 0;
|
|
-
|
|
- /*
|
|
- * If a worker went to sleep, notify and ask workqueue
|
|
- * whether it wants to wake up a task to maintain
|
|
- * concurrency.
|
|
- * Only call wake up if prev isn't blocked on a sleeping
|
|
- * spin lock.
|
|
- */
|
|
- if (prev->flags & PF_WQ_WORKER && !prev->saved_state) {
|
|
- struct task_struct *to_wakeup;
|
|
-
|
|
- to_wakeup = wq_worker_sleeping(prev);
|
|
- if (to_wakeup)
|
|
- try_to_wake_up_local(to_wakeup, cookie);
|
|
- }
|
|
}
|
|
switch_count = &prev->nvcsw;
|
|
}
|
|
@@ -3678,6 +3607,14 @@ static inline void sched_submit_work(struct task_struct *tsk)
|
|
{
|
|
if (!tsk->state || tsk_is_pi_blocked(tsk))
|
|
return;
|
|
+
|
|
+ /*
|
|
+ * If a worker went to sleep, notify and ask workqueue whether
|
|
+ * it wants to wake up a task to maintain concurrency.
|
|
+ */
|
|
+ if (tsk->flags & PF_WQ_WORKER)
|
|
+ wq_worker_sleeping(tsk);
|
|
+
|
|
/*
|
|
* If we are going to sleep and we have plugged IO queued,
|
|
* make sure to submit it to avoid deadlocks.
|
|
@@ -3686,6 +3623,12 @@ static inline void sched_submit_work(struct task_struct *tsk)
|
|
blk_schedule_flush_plug(tsk);
|
|
}
|
|
|
|
+static void sched_update_worker(struct task_struct *tsk)
|
|
+{
|
|
+ if (tsk->flags & PF_WQ_WORKER)
|
|
+ wq_worker_running(tsk);
|
|
+}
|
|
+
|
|
asmlinkage __visible void __sched schedule(void)
|
|
{
|
|
struct task_struct *tsk = current;
|
|
@@ -3696,6 +3639,7 @@ asmlinkage __visible void __sched schedule(void)
|
|
__schedule(false);
|
|
sched_preempt_enable_no_resched();
|
|
} while (need_resched());
|
|
+ sched_update_worker(tsk);
|
|
}
|
|
EXPORT_SYMBOL(schedule);
|
|
|
|
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
|
|
index 8793301..b69456e 100644
|
|
--- a/kernel/workqueue.c
|
|
+++ b/kernel/workqueue.c
|
|
@@ -843,43 +843,32 @@ static void wake_up_worker(struct worker_pool *pool)
|
|
}
|
|
|
|
/**
|
|
- * wq_worker_waking_up - a worker is waking up
|
|
+ * wq_worker_running - a worker is running again
|
|
* @task: task waking up
|
|
- * @cpu: CPU @task is waking up to
|
|
*
|
|
- * This function is called during try_to_wake_up() when a worker is
|
|
- * being awoken.
|
|
- *
|
|
- * CONTEXT:
|
|
- * spin_lock_irq(rq->lock)
|
|
+ * This function is called when a worker returns from schedule()
|
|
*/
|
|
-void wq_worker_waking_up(struct task_struct *task, int cpu)
|
|
+void wq_worker_running(struct task_struct *task)
|
|
{
|
|
struct worker *worker = kthread_data(task);
|
|
|
|
- if (!(worker->flags & WORKER_NOT_RUNNING)) {
|
|
- WARN_ON_ONCE(worker->pool->cpu != cpu);
|
|
+ if (!worker->sleeping)
|
|
+ return;
|
|
+ if (!(worker->flags & WORKER_NOT_RUNNING))
|
|
atomic_inc(&worker->pool->nr_running);
|
|
- }
|
|
+ worker->sleeping = 0;
|
|
}
|
|
|
|
/**
|
|
* wq_worker_sleeping - a worker is going to sleep
|
|
* @task: task going to sleep
|
|
*
|
|
- * This function is called during schedule() when a busy worker is
|
|
- * going to sleep. Worker on the same cpu can be woken up by
|
|
- * returning pointer to its task.
|
|
- *
|
|
- * CONTEXT:
|
|
- * spin_lock_irq(rq->lock)
|
|
- *
|
|
- * Return:
|
|
- * Worker task on @cpu to wake up, %NULL if none.
|
|
+ * This function is called from schedule() when a busy worker is
|
|
+ * going to sleep.
|
|
*/
|
|
-struct task_struct *wq_worker_sleeping(struct task_struct *task)
|
|
+void wq_worker_sleeping(struct task_struct *task)
|
|
{
|
|
- struct worker *worker = kthread_data(task), *to_wakeup = NULL;
|
|
+ struct worker *next, *worker = kthread_data(task);
|
|
struct worker_pool *pool;
|
|
|
|
/*
|
|
@@ -888,13 +877,15 @@ struct task_struct *wq_worker_sleeping(struct task_struct *task)
|
|
* checking NOT_RUNNING.
|
|
*/
|
|
if (worker->flags & WORKER_NOT_RUNNING)
|
|
- return NULL;
|
|
+ return;
|
|
|
|
pool = worker->pool;
|
|
|
|
- /* this can only happen on the local cpu */
|
|
- if (WARN_ON_ONCE(pool->cpu != raw_smp_processor_id()))
|
|
- return NULL;
|
|
+ if (WARN_ON_ONCE(worker->sleeping))
|
|
+ return;
|
|
+
|
|
+ worker->sleeping = 1;
|
|
+ spin_lock_irq(&pool->lock);
|
|
|
|
/*
|
|
* The counterpart of the following dec_and_test, implied mb,
|
|
@@ -908,9 +899,12 @@ struct task_struct *wq_worker_sleeping(struct task_struct *task)
|
|
* lock is safe.
|
|
*/
|
|
if (atomic_dec_and_test(&pool->nr_running) &&
|
|
- !list_empty(&pool->worklist))
|
|
- to_wakeup = first_idle_worker(pool);
|
|
- return to_wakeup ? to_wakeup->task : NULL;
|
|
+ !list_empty(&pool->worklist)) {
|
|
+ next = first_idle_worker(pool);
|
|
+ if (next)
|
|
+ wake_up_process(next->task);
|
|
+ }
|
|
+ spin_unlock_irq(&pool->lock);
|
|
}
|
|
|
|
/**
|
|
diff --git a/kernel/workqueue_internal.h b/kernel/workqueue_internal.h
|
|
index 29fa81f..42d1e39 100644
|
|
--- a/kernel/workqueue_internal.h
|
|
+++ b/kernel/workqueue_internal.h
|
|
@@ -44,6 +44,7 @@ struct worker {
|
|
unsigned long last_active; /* L: last active timestamp */
|
|
unsigned int flags; /* X: flags */
|
|
int id; /* I: worker id */
|
|
+ int sleeping; /* None */
|
|
|
|
/*
|
|
* Opaque string set with work_set_desc(). Printed out with task
|
|
@@ -69,7 +70,7 @@ static inline struct worker *current_wq_worker(void)
|
|
* Scheduler hooks for concurrency managed workqueue. Only to be used from
|
|
* sched/core.c and workqueue.c.
|
|
*/
|
|
-void wq_worker_waking_up(struct task_struct *task, int cpu);
|
|
-struct task_struct *wq_worker_sleeping(struct task_struct *task);
|
|
+void wq_worker_running(struct task_struct *task);
|
|
+void wq_worker_sleeping(struct task_struct *task);
|
|
|
|
#endif /* _KERNEL_WORKQUEUE_INTERNAL_H */
|
|
--
|
|
2.7.4
|
|
|