Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1378708 > unrolled thread
| Started by | Xunlei Pang <xlpang@redhat.com> |
|---|---|
| First post | 2016-04-14 13:40 +0200 |
| Last post | 2016-04-15 04:20 +0200 |
| Articles | 6 — 3 participants |
Back to article view | Back to linux.kernel
[PATCH v3 0/6] sched/deadline/rtmutex: Fix two deadline PI issues Xunlei Pang <xlpang@redhat.com> - 2016-04-14 13:40 +0200
[PATCH v3 1/6] rtmutex: Deboost before waking up the top waiter Xunlei Pang <xlpang@redhat.com> - 2016-04-14 13:40 +0200
[PATCH v3 5/6] sched/deadline/rtmutex: Fix unprotected PI access in enqueue_task_dl() Xunlei Pang <xlpang@redhat.com> - 2016-04-14 13:40 +0200
Re: [PATCH v3 5/6] sched/deadline/rtmutex: Fix unprotected PI access in enqueue_task_dl() Peter Zijlstra <peterz@infradead.org> - 2016-04-14 17:40 +0200
Re: [PATCH v3 5/6] sched/deadline/rtmutex: Fix unprotected PI access in enqueue_task_dl() Xunlei Pang <xpang@redhat.com> - 2016-04-15 04:00 +0200
Re: [PATCH v3 5/6] sched/deadline/rtmutex: Fix unprotected PI access in enqueue_task_dl() Xunlei Pang <xpang@redhat.com> - 2016-04-15 04:20 +0200
| From | Xunlei Pang <xlpang@redhat.com> |
|---|---|
| Date | 2016-04-14 13:40 +0200 |
| Subject | [PATCH v3 0/6] sched/deadline/rtmutex: Fix two deadline PI issues |
| Message-ID | <rnNMR-Ww-3@gated-at.bofh.it> |
PATCH 1~2 mainly fix the deadline PI crash happened when doing
enqueue_task_dl()->rt_mutex_get_top_task() due to not holding
rq lock for the top waiter update.
PATCH 3~6 mainly fix the deadline PI issue happened when doing
enqueue_task_dl() after get @pi_task, and access pi_task's data
(dl.dl_runtime and dl.dl_period), because the access is not
holding any lock(pi lock or rq lock) of pi_task's. PATCH 3~4 are
separated out to make PATCH 5 smaller and easier to reviewers.
The two issues can be fixed using the same logic, so bind them
together as one series.
Xunlei Pang (6):
rtmutex: Deboost before waking up the top waiter
sched/rtmutex/deadline: Fix a PI crash for deadline tasks
rtmutex: Move "rt_mutex_waiter" definition to
"include/linux/rtmutex.h"
sched: Move dl_policy() to "include/linux/sched.h"
sched/deadline/rtmutex: Fix unprotected PI access in enqueue_task_dl()
sched/deadline/rtmutex: Don't miss the dl_runtime/dl_period update
include/linux/init_task.h | 3 +-
include/linux/rtmutex.h | 29 +++++++++++++-
include/linux/sched.h | 10 ++++-
include/linux/sched/deadline.h | 22 +++++++++++
kernel/fork.c | 1 +
kernel/futex.c | 5 +--
kernel/locking/rtmutex.c | 84 ++++++++++++++++++++++++++++-------------
kernel/locking/rtmutex_common.h | 22 +----------
kernel/sched/core.c | 2 +
kernel/sched/deadline.c | 10 +++--
kernel/sched/sched.h | 4 --
11 files changed, 132 insertions(+), 60 deletions(-)
--
1.8.3.1
[toc] | [next] | [standalone]
| From | Xunlei Pang <xlpang@redhat.com> |
|---|---|
| Date | 2016-04-14 13:40 +0200 |
| Subject | [PATCH v3 1/6] rtmutex: Deboost before waking up the top waiter |
| Message-ID | <rnNMS-Ww-23@gated-at.bofh.it> |
| In reply to | #1378708 |
We should deboost before waking the high-prio task such that
we don't run two tasks with the 'same' priority.
The patch fixed the logic, and introduced rt_mutex_postunlock()
to do some code refactor.
Suggested-by: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Xunlei Pang <xlpang@redhat.com>
---
kernel/futex.c | 5 ++---
kernel/locking/rtmutex.c | 28 ++++++++++++++++++++++++----
kernel/locking/rtmutex_common.h | 1 +
3 files changed, 27 insertions(+), 7 deletions(-)
diff --git a/kernel/futex.c b/kernel/futex.c
index 4e1a53e..4ae3523 100644
--- a/kernel/futex.c
+++ b/kernel/futex.c
@@ -1524,9 +1524,8 @@ static int wake_futex_pi(u32 __user *uaddr, u32 uval, struct futex_q *this,
* scheduled away before the wake up can take place.
*/
spin_unlock(&hb->lock);
- wake_up_q(&wake_q);
- if (deboost)
- rt_mutex_adjust_prio(current);
+
+ rt_mutex_postunlock(&wake_q, deboost);
return 0;
}
diff --git a/kernel/locking/rtmutex.c b/kernel/locking/rtmutex.c
index 3e74660..42bc59b 100644
--- a/kernel/locking/rtmutex.c
+++ b/kernel/locking/rtmutex.c
@@ -1390,12 +1390,32 @@ rt_mutex_fastunlock(struct rt_mutex *lock,
} else {
bool deboost = slowfn(lock, &wake_q);
- wake_up_q(&wake_q);
+ rt_mutex_postunlock(&wake_q, deboost);
+ }
+}
+
- /* Undo pi boosting if necessary: */
- if (deboost)
- rt_mutex_adjust_prio(current);
+/*
+ * Undo pi boosting (if necessary) and wake top waiter.
+ */
+void rt_mutex_postunlock(struct wake_q_head *wake_q, bool deboost)
+{
+ /*
+ * We should deboost before waking the high-prio task such that
+ * we don't run two tasks with the 'same' priority. This however
+ * can lead to prio-inversion if we would get preempted after
+ * the deboost but before waking our high-prio task, hence the
+ * preempt_disable.
+ */
+ if (deboost) {
+ preempt_disable();
+ rt_mutex_adjust_prio(current);
}
+
+ wake_up_q(wake_q);
+
+ if (deboost)
+ preempt_enable();
}
/**
diff --git a/kernel/locking/rtmutex_common.h b/kernel/locking/rtmutex_common.h
index 4f5f83c..93b0924 100644
--- a/kernel/locking/rtmutex_common.h
+++ b/kernel/locking/rtmutex_common.h
@@ -111,6 +111,7 @@ extern int rt_mutex_finish_proxy_lock(struct rt_mutex *lock,
extern int rt_mutex_timed_futex_lock(struct rt_mutex *l, struct hrtimer_sleeper *to);
extern bool rt_mutex_futex_unlock(struct rt_mutex *lock,
struct wake_q_head *wqh);
+extern void rt_mutex_postunlock(struct wake_q_head *wake_q, bool deboost);
extern void rt_mutex_adjust_prio(struct task_struct *task);
#ifdef CONFIG_DEBUG_RT_MUTEXES
--
1.8.3.1
[toc] | [prev] | [next] | [standalone]
| From | Xunlei Pang <xlpang@redhat.com> |
|---|---|
| Date | 2016-04-14 13:40 +0200 |
| Subject | [PATCH v3 5/6] sched/deadline/rtmutex: Fix unprotected PI access in enqueue_task_dl() |
| Message-ID | <rnNMS-Ww-27@gated-at.bofh.it> |
| In reply to | #1378708 |
We access @pi_task's data without any lock in enqueue_task_dl(), though
checked "dl_prio(pi_task->normal_prio)" condition, that's not enough.
"dl_period" and "dl_runtime" of "pi_task->dl" can change. For example,
if it changes to !deadline class, dl_runtime will be cleared to zero,
then we will hit an endless loop in replenish_dl_entity() below:
while (dl_se->runtime <= 0) {
dl_se->deadline += pi_se->dl_period;
dl_se->runtime += pi_se->dl_runtime;
}
or hit "BUG_ON(pi_se->dl_runtime <= 0)" earlier.
That's because without any lock of top waiter, there is no guarantee.
In order to solve it, we add some members in "rt_mutex_waiter" and use
this structure instead of task_struct as the one to be accessed by
enqueue_task_dl(), specifically added:
struct rt_mutex_waiter {
... ...
int prio;
+ /* Updated under waiter's pi_lock and rt_mutex lock */
+ u64 dl_runtime, dl_period;
+ /*
+ * Copied directly from above.
+ * Updated under owner's pi_lock, rq lock, and rt_mutex lock.
+ */
+ u64 dl_runtime_copy, dl_period_copy;
};
We must update "dl_runtime_copy" and "dl_period_copy" under rt_mutex
lock, because they are copied from rt_mutex_waiter's "dl_runtime" and
"dl_period" which are protected by the same rt_mutex lock. We update
the copy in rt_mutex_update_copy() introduced perviously, as it is
called by rt_mutex_setprio() which held owner's pi_lock, rq lock, and
rt_mutex lock.
"dl_runtime_copy" and "dl_period_copy" are updated under owner's pi_lock,
rq lock, and rt_mutex lock, plus the waiter was dependably blocked on
rtmutex, thus they can be safely accessed by enqueue_task_dl() which
held rq lock.
Note that, now we return a rt_mutex_waiter to enqueue_task_dl(), we add
a new "struct sched_dl_entity_fake" to fake as a real sched_dl_entity,
this is ok as long as we keep the "dl_runtime" and "dl_period" in it
the same order as that in sched_dl_entity. Also adjust the location of
"dl_period" in sched_dl_entity to make sched_dl_entity_fake smaller.
Signed-off-by: Xunlei Pang <xlpang@redhat.com>
---
include/linux/rtmutex.h | 7 +++++++
include/linux/sched.h | 2 +-
include/linux/sched/deadline.h | 20 ++++++++++++++++++++
kernel/locking/rtmutex.c | 23 +++++++++++++++++++++++
kernel/sched/deadline.c | 10 +++++++---
5 files changed, 58 insertions(+), 4 deletions(-)
diff --git a/include/linux/rtmutex.h b/include/linux/rtmutex.h
index f9bf40a..56e2aaf 100644
--- a/include/linux/rtmutex.h
+++ b/include/linux/rtmutex.h
@@ -58,6 +58,13 @@ struct rt_mutex_waiter {
struct rt_mutex *deadlock_lock;
#endif
int prio;
+ /* Updated under waiter's pi_lock and rt_mutex lock */
+ u64 dl_runtime, dl_period;
+ /*
+ * Copied directly from above.
+ * Updated under owner's pi_lock, rq lock, and rt_mutex lock.
+ */
+ u64 dl_runtime_copy, dl_period_copy;
};
struct hrtimer_sleeper;
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 8ad3522..960465c 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1323,8 +1323,8 @@ struct sched_dl_entity {
* the next sched_setattr().
*/
u64 dl_runtime; /* maximum runtime for each instance */
- u64 dl_deadline; /* relative deadline of each instance */
u64 dl_period; /* separation of two instances (period) */
+ u64 dl_deadline; /* relative deadline of each instance */
u64 dl_bw; /* dl_runtime / dl_deadline */
/*
diff --git a/include/linux/sched/deadline.h b/include/linux/sched/deadline.h
index e8304d4..ca5bae5 100644
--- a/include/linux/sched/deadline.h
+++ b/include/linux/sched/deadline.h
@@ -2,6 +2,16 @@
#define _SCHED_DEADLINE_H
/*
+ * Used by enqueue_task_dl() for PI cases to disguise sched_dl_entity,
+ * thus must be the same order as the counterparts in sched_dl_entity.
+ */
+struct sched_dl_entity_fake {
+ struct rb_node rb_node;
+ u64 dl_runtime;
+ u64 dl_period;
+};
+
+/*
* SCHED_DEADLINE tasks has negative priorities, reflecting
* the fact that any of them has higher prio than RT and
* NORMAL/BATCH tasks.
@@ -28,4 +38,14 @@ static inline bool dl_time_before(u64 a, u64 b)
extern void rt_mutex_update_copy(struct task_struct *p);
+#ifdef CONFIG_RT_MUTEXES
+extern struct rt_mutex_waiter *rt_mutex_get_top_waiter(struct task_struct *p);
+#else
+static inline
+struct rt_mutex_waiter *rt_mutex_get_top_waiter(struct task_struct *p)
+{
+ return NULL;
+}
+#endif
+
#endif /* _SCHED_DEADLINE_H */
diff --git a/kernel/locking/rtmutex.c b/kernel/locking/rtmutex.c
index 00c6560..4d14eee 100644
--- a/kernel/locking/rtmutex.c
+++ b/kernel/locking/rtmutex.c
@@ -280,6 +280,15 @@ struct task_struct *rt_mutex_get_top_task(struct task_struct *task)
struct rt_mutex_waiter, pi_tree_entry)->task;
}
+struct rt_mutex_waiter *rt_mutex_get_top_waiter(struct task_struct *task)
+{
+ if (!task->pi_waiters_leftmost_copy)
+ return NULL;
+
+ return rb_entry(task->pi_waiters_leftmost_copy,
+ struct rt_mutex_waiter, pi_tree_entry);
+}
+
/*
* Called by sched_setscheduler() to get the priority which will be
* effective after the change.
@@ -299,7 +308,17 @@ int rt_mutex_get_effective_prio(struct task_struct *task, int newprio)
*/
void rt_mutex_update_copy(struct task_struct *p)
{
+ struct rt_mutex_waiter *top_waiter;
+
+ /* We must always update it, even if NULL */
p->pi_waiters_leftmost_copy = p->pi_waiters_leftmost;
+
+ if (!task_has_pi_waiters(p))
+ return;
+
+ top_waiter = task_top_pi_waiter(p);
+ top_waiter->dl_runtime_copy = top_waiter->dl_runtime;
+ top_waiter->dl_period_copy = top_waiter->dl_period;
}
/*
@@ -632,6 +651,8 @@ static int rt_mutex_adjust_prio_chain(struct task_struct *task,
/* [7] Requeue the waiter in the lock waiter tree. */
rt_mutex_dequeue(lock, waiter);
waiter->prio = task->prio;
+ waiter->dl_runtime = dl_policy(task->policy) ? task->dl.dl_runtime : 0;
+ waiter->dl_period = dl_policy(task->policy) ? task->dl.dl_period : 0;
rt_mutex_enqueue(lock, waiter);
/* [8] Release the task */
@@ -902,6 +923,8 @@ static int task_blocks_on_rt_mutex(struct rt_mutex *lock,
waiter->task = task;
waiter->lock = lock;
waiter->prio = task->prio;
+ waiter->dl_runtime = dl_policy(task->policy) ? task->dl.dl_runtime : 0;
+ waiter->dl_period = dl_policy(task->policy) ? task->dl.dl_period : 0;
/* Get the top priority waiter on the lock */
if (rt_mutex_has_waiters(lock))
diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c
index affd97e..224aa64 100644
--- a/kernel/sched/deadline.c
+++ b/kernel/sched/deadline.c
@@ -932,8 +932,9 @@ static void dequeue_dl_entity(struct sched_dl_entity *dl_se)
static void enqueue_task_dl(struct rq *rq, struct task_struct *p, int flags)
{
- struct task_struct *pi_task = rt_mutex_get_top_task(p);
+ struct rt_mutex_waiter *top_waiter = rt_mutex_get_top_waiter(p);
struct sched_dl_entity *pi_se = &p->dl;
+ struct sched_dl_entity_fake pi_se_fake;
/*
* Use the scheduling parameters of the top pi-waiter
@@ -941,8 +942,11 @@ static void enqueue_task_dl(struct rq *rq, struct task_struct *p, int flags)
* smaller than our one... OTW we keep our runtime and
* deadline.
*/
- if (pi_task && p->dl.dl_boosted && dl_prio(pi_task->normal_prio)) {
- pi_se = &pi_task->dl;
+ if (top_waiter && p->dl.dl_boosted && top_waiter->dl_runtime_copy) {
+ BUG_ON(top_waiter->dl_period_copy == 0);
+ pi_se_fake.dl_runtime = top_waiter->dl_runtime_copy;
+ pi_se_fake.dl_period = top_waiter->dl_period_copy;
+ pi_se = (struct sched_dl_entity *)&pi_se_fake;
} else if (!dl_prio(p->normal_prio)) {
/*
* Special case in which we have a !SCHED_DEADLINE task
--
1.8.3.1
[toc] | [prev] | [next] | [standalone]
| From | Peter Zijlstra <peterz@infradead.org> |
|---|---|
| Date | 2016-04-14 17:40 +0200 |
| Subject | Re: [PATCH v3 5/6] sched/deadline/rtmutex: Fix unprotected PI access in enqueue_task_dl() |
| Message-ID | <rnRx7-408-9@gated-at.bofh.it> |
| In reply to | #1378711 |
On Thu, Apr 14, 2016 at 07:37:06PM +0800, Xunlei Pang wrote: > We access @pi_task's data without any lock in enqueue_task_dl(), though > checked "dl_prio(pi_task->normal_prio)" condition, that's not enough. The proper fix is to ensure that pi_task is guaranteed to be blocked.
[toc] | [prev] | [next] | [standalone]
| From | Xunlei Pang <xpang@redhat.com> |
|---|---|
| Date | 2016-04-15 04:00 +0200 |
| Subject | Re: [PATCH v3 5/6] sched/deadline/rtmutex: Fix unprotected PI access in enqueue_task_dl() |
| Message-ID | <ro1d8-3iG-5@gated-at.bofh.it> |
| In reply to | #1379032 |
On 2016/04/14 at 23:31, Peter Zijlstra wrote: > On Thu, Apr 14, 2016 at 07:37:06PM +0800, Xunlei Pang wrote: >> We access @pi_task's data without any lock in enqueue_task_dl(), though >> checked "dl_prio(pi_task->normal_prio)" condition, that's not enough. > The proper fix is to ensure that pi_task is guaranteed to be blocked. Even if pi_task was blocked, its parameters are still allowed to be changed, so we have to do that. Did I miss something? Regards, Xunlei
[toc] | [prev] | [next] | [standalone]
| From | Xunlei Pang <xpang@redhat.com> |
|---|---|
| Date | 2016-04-15 04:20 +0200 |
| Subject | Re: [PATCH v3 5/6] sched/deadline/rtmutex: Fix unprotected PI access in enqueue_task_dl() |
| Message-ID | <ro1wt-3Ok-1@gated-at.bofh.it> |
| In reply to | #1379416 |
On 2016/04/15 at 09:58, Xunlei Pang wrote: > On 2016/04/14 at 23:31, Peter Zijlstra wrote: >> On Thu, Apr 14, 2016 at 07:37:06PM +0800, Xunlei Pang wrote: >>> We access @pi_task's data without any lock in enqueue_task_dl(), though >>> checked "dl_prio(pi_task->normal_prio)" condition, that's not enough. >> The proper fix is to ensure that pi_task is guaranteed to be blocked. > Even if pi_task was blocked, its parameters are still allowed to be changed, > so we have to do that. Did I miss something? > > Regards, > Xunlei Fortunately, I just reproduced through an overnight test, so it really happened in reality as I thought. [50697.042391] kernel BUG at kernel/sched/deadline.c:398! [50697.048212] invalid opcode: 0000 [#1] SMP [50697.137676] CPU: 1 PID: 10676 Comm: bugon Tainted: G W 4.6.0-rc3+ #19 [50697.146250] Hardware name: Intel Corporation Broadwell Client platform/SawTooth Peak, BIOS BDW-E1R1.86C.0127.R00.150 8062034 08/06/2015 [50697.159942] task: ffff880089d72b80 ti: ffff880074bb4000 task.ti: ffff880074bb4000 [50697.168420] RIP: 0010:[<ffffffff810cb4ef>] [<ffffffff810cb4ef>] replenish_dl_entity+0xff/0x110 [50697.178292] RSP: 0000:ffff88016ec43d90 EFLAGS: 00010046 [50697.184307] RAX: 0000000000000001 RBX: ffff880089d72d50 RCX: 0000000000000001 [50697.192390] RDX: 0000000000000010 RSI: ffff8800719858d0 RDI: ffff880089d72d50 [50697.200473] RBP: ffff88016ec43da8 R08: 0000000000000001 R09: 0000000000000097 [50697.208556] R10: 0000000057102e72 R11: 000000000f9e6fd7 R12: ffff88016ec56e40 [50697.216638] R13: ffff88016ec56e40 R14: 0000000000016e40 R15: ffff880089d72d50 [50697.224721] FS: 00007f14e788b700(0000) GS:ffff88016ec40000(0000) knlGS:0000000000000000 [50697.233887] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [50697.240396] CR2: 000055be08240c68 CR3: 000000008a5d5000 CR4: 00000000003406e0 [50697.248478] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [50697.256561] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 [50697.264643] Stack: [50697.266917] ffff88016ec56e40 ffff880089d72b80 0000000000000010 ffff88016ec43de8 [50697.275338] ffffffff810cbfe4 ffff88016ec43de8 ffff880089d72b80 ffff88016ec56e40 [50697.283757] 00000000000188c5 ffff880089d72d50 ffff88016ec4f228 ffff88016ec43e18 [50697.292175] Call Trace: [50697.294943] <IRQ> [50697.297122] [<ffffffff810cbfe4>] enqueue_task_dl+0x264/0x340 [50697.303838] [<ffffffff810cc453>] update_curr_dl+0x1c3/0x1f0 [50697.310249] [<ffffffff810cc51c>] task_tick_dl+0x1c/0x80 [50697.316265] [<ffffffff810b66ac>] scheduler_tick+0x5c/0xe0 [50697.322480] [<ffffffff811060d0>] ? tick_sched_do_timer+0x50/0x50 [50697.329383] [<ffffffff810f60e1>] update_process_times+0x51/0x60 [50697.336188] [<ffffffff81105a25>] tick_sched_handle.isra.17+0x25/0x60 [50697.343486] [<ffffffff8110610d>] tick_sched_timer+0x3d/0x70 [50697.349895] [<ffffffff810f6c93>] __hrtimer_run_queues+0xf3/0x270 [50697.356797] [<ffffffff810f7168>] hrtimer_interrupt+0xa8/0x1a0 [50697.363404] [<ffffffff81053de5>] local_apic_timer_interrupt+0x35/0x60 [50697.370799] [<ffffffff816b7a1d>] smp_apic_timer_interrupt+0x3d/0x50 [50697.377996] [<ffffffff816b5b5c>] apic_timer_interrupt+0x8c/0xa0 [50697.384798] <EOI> [50697.384798] <EOI> [50697.386974] Code: a9 48 c7 c7 38 5f a0 81 31 c0 48 89 75 e8 c6 05 5c 48 c8 00 01 e8 74 20 0c 00 49 8b 84 24 28 09 00 00 8b 4b 54 48 8b 75 e8 eb c4 <0f> 0b 0f 1f 44 00 00 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 [50697.409201] RIP [<ffffffff810cb4ef>] replenish_dl_entity+0xff/0x110 [50697.416409] RSP <ffff88016ec43d90> [50697.433683] ---[ end trace da6e1e42babefb7f ]--- [50697.438913] Kernel panic - not syncing: Fatal exception in interrupt [50698.484088] Shutting down cpus with NMI [50698.488434] Kernel Offset: disabled [50698.492383] ---[ end Kernel panic - not syncing: Fatal exception in interrupt
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web