From 9acb7c07b6b988ef42959e74d045656483b57486 Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Mon, 28 Oct 2019 15:16:40 +0000 Subject: [PATCH] Revert "sched: Rework pick_next_task() slow-path" This reverts commit 67692435c411e5c53a1c588ecca2037aebd81f2e temporarily in order to avoid the following splat: [ 222.515957] BUG: kernel NULL pointer dereference, address: 0000000000000040 [ 222.518871] #PF: supervisor read access in kernel mode [ 222.518871] #PF: error_code(0x0000) - not-present page [ 222.518871] PGD 800000007cdf7067 P4D 800000007cdf7067 PUD 7c868067 PMD 0 [ 222.518871] Oops: 0000 [#1] PREEMPT SMP PTI [ 222.518871] CPU: 2 PID: 0 Comm: swapper/2 Not tainted 5.4.0-rc4-mainline-g6a9b34166c05 #1 [ 222.518871] Hardware name: ChromiumOS crosvm, BIOS 0 [ 222.518871] RIP: 0010:set_next_entity+0xf/0xf0 [ 222.518871] Code: 42 0b e5 85 31 c0 e8 90 1a fc ff 0f 0b eb 9b 66 90 66 2e 0f 1f 84 00 00 00 00 00 55 48 89 e5 41 57 41 56 53 48 89 f3 49 89 fe <83> 7e 40 00 74 3d 4c 89 f7 48 89 de e8 80 f9 ff ff 4c 8d 7b 18 4d [ 222.518871] RSP: 0018:ffffb95040073de8 EFLAGS: 00010046 [ 222.518871] RAX: 0000000000000000 RBX: 0000000000000000 RCX: ffffffff85ec0c30 [ 222.518871] RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff9ec44c728500 [ 222.518871] RBP: ffffb95040073e00 R08: 0000000000000000 R09: 0000000000000000 [ 222.518871] R10: 0000000000000000 R11: ffffffff84ef0a30 R12: 0000000000000000 [ 222.518871] R13: 0000000000000000 R14: ffff9ec44c728500 R15: ffffb95040073e60 [ 222.518871] FS: 0000000000000000(0000) GS:ffff9ec44c700000(0000) knlGS:0000000000000000 [ 222.518871] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 222.518871] CR2: 0000000000000040 CR3: 000000007c87a002 CR4: 00000000003606e0 [ 222.518871] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 222.518871] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 [ 222.518871] Call Trace: [ 222.518871] pick_next_task_fair+0xe8/0x410 [ 222.518871] ? rcu_note_context_switch+0x3ef/0x520 [ 222.518871] ? finish_task_switch+0x10d/0x250 [ 222.518871] __schedule+0x1f8/0x6e0 [ 222.518871] schedule_idle+0x27/0x40 [ 222.518871] do_idle+0x21c/0x240 [ 222.518871] cpu_startup_entry+0x25/0x30 [ 222.518871] start_secondary+0x18d/0x1b0 [ 222.518871] secondary_startup_64+0xa4/0xb0 [ 222.518871] Modules linked in: vmw_vsock_virtio_transport vmw_vsock_virtio_transport_common vsock_diag vsock can_raw can snd_intel8x0 snd_ac97_codec ac97_bus virtio_input virtio_pci virtio_mmio virtio_crypto mmc_block mmc_core dummy_cpufreq rtc_test dummy_hcd can_dev vcan virtio_net net_failover failover virt_wifi virtio_blk virtio_gpu ttm virtio_rng virtio_ring virtio 8250_of crypto_engine [ 222.518871] CR2: 0000000000000040 [ 222.518871] ---[ end trace ae741809965b22f2 ]--- [ 222.518871] RIP: 0010:set_next_entity+0xf/0xf0 [ 222.518871] Code: 42 0b e5 85 31 c0 e8 90 1a fc ff 0f 0b eb 9b 66 90 66 2e 0f 1f 84 00 00 00 00 00 55 48 89 e5 41 57 41 56 53 48 89 f3 49 89 fe <83> 7e 40 00 74 3d 4c 89 f7 48 89 de e8 80 f9 ff ff 4c 8d 7b 18 4d [ 222.518871] RSP: 0018:ffffb95040073de8 EFLAGS: 00010046 [ 222.518871] RAX: 0000000000000000 RBX: 0000000000000000 RCX: ffffffff85ec0c30 [ 222.518871] RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff9ec44c728500 [ 222.518871] RBP: ffffb95040073e00 R08: 0000000000000000 R09: 0000000000000000 [ 222.518871] R10: 0000000000000000 R11: ffffffff84ef0a30 R12: 0000000000000000 [ 222.518871] R13: 0000000000000000 R14: ffff9ec44c728500 R15: ffffb95040073e60 [ 222.518871] FS: 0000000000000000(0000) GS:ffff9ec44c700000(0000) knlGS:0000000000000000 [ 222.518871] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 222.518871] CR2: 0000000000000040 CR3: 000000007c87a002 CR4: 00000000003606e0 [ 222.518871] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 222.518871] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 [ 222.518871] Kernel panic - not syncing: Fatal exception [ 222.518871] Kernel Offset: 0x3e00000 from 0xffffffff81000000 (relocation range: 0xffffffff80000000-0xffffffffbfffffff) Bug: 142182814 Test: 20/20 avd/avd_boot_test_kernel_triggered passing Signed-off-by: Quentin Perret Change-Id: I62bb09b4710fdc593cbf1f42bf4bbc066e401458 --- kernel/sched/core.c | 19 +++++++------------ kernel/sched/deadline.c | 30 ++++++++++++++++++++++++++++-- kernel/sched/fair.c | 9 +++------ kernel/sched/idle.c | 4 +--- kernel/sched/rt.c | 29 ++++++++++++++++++++++++++++- kernel/sched/sched.h | 13 +++++-------- kernel/sched/stop_task.c | 3 +-- 7 files changed, 73 insertions(+), 34 deletions(-) diff --git a/kernel/sched/core.c b/kernel/sched/core.c index c05d05094e70..eb3a7b8bd3cb 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -3919,7 +3919,7 @@ pick_next_task(struct rq *rq, struct task_struct *prev, struct rq_flags *rf) p = fair_sched_class.pick_next_task(rq, prev, rf); if (unlikely(p == RETRY_TASK)) - goto restart; + goto again; /* Assumes fair_sched_class->next == idle_sched_class */ if (unlikely(!p)) @@ -3928,19 +3928,14 @@ pick_next_task(struct rq *rq, struct task_struct *prev, struct rq_flags *rf) return p; } -restart: - /* - * Ensure that we put DL/RT tasks before the pick loop, such that they - * can PULL higher prio tasks when we lower the RQ 'priority'. - */ - prev->sched_class->put_prev_task(rq, prev, rf); - if (!rq->nr_running) - newidle_balance(rq, rf); - +again: for_each_class(class) { - p = class->pick_next_task(rq, NULL, NULL); - if (p) + p = class->pick_next_task(rq, prev, rf); + if (p) { + if (unlikely(p == RETRY_TASK)) + goto again; return p; + } } /* The idle class should always have a runnable task: */ diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c index 2dc48720f189..4ed2d10c196c 100644 --- a/kernel/sched/deadline.c +++ b/kernel/sched/deadline.c @@ -1761,13 +1761,39 @@ pick_next_task_dl(struct rq *rq, struct task_struct *prev, struct rq_flags *rf) struct task_struct *p; struct dl_rq *dl_rq; - WARN_ON_ONCE(prev || rf); - dl_rq = &rq->dl; + if (need_pull_dl_task(rq, prev)) { + /* + * 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're + * being very careful to re-start the picking loop. + */ + rq_unpin_lock(rq, rf); + pull_dl_task(rq); + rq_repin_lock(rq, rf); + /* + * pull_dl_task() can drop (and re-acquire) rq->lock; this + * means a stop task can slip in, in which case we need to + * re-start task selection. + */ + if (rq->stop && task_on_rq_queued(rq->stop)) + return RETRY_TASK; + } + + /* + * When prev is DL, we may throttle it in put_prev_task(). + * So, we update time before we check for dl_nr_running. + */ + if (prev->sched_class == &dl_sched_class) + update_curr_dl(rq); + if (unlikely(!dl_rq->dl_nr_running)) return NULL; + put_prev_task(rq, prev); + dl_se = pick_next_dl_entity(rq, dl_rq); BUG_ON(!dl_se); diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 8f8692baa679..31b3e1a10bb3 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -6810,7 +6810,7 @@ again: goto idle; #ifdef CONFIG_FAIR_GROUP_SCHED - if (!prev || prev->sched_class != &fair_sched_class) + if (prev->sched_class != &fair_sched_class) goto simple; /* @@ -6887,8 +6887,8 @@ again: goto done; simple: #endif - if (prev) - put_prev_task(rq, prev); + + put_prev_task(rq, prev); do { se = pick_next_entity(cfs_rq, NULL); @@ -6916,9 +6916,6 @@ done: __maybe_unused; return p; idle: - if (!rf) - return NULL; - new_tasks = newidle_balance(rq, rf); /* diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c index 8dad5aa600ea..e397a165b408 100644 --- a/kernel/sched/idle.c +++ b/kernel/sched/idle.c @@ -390,9 +390,7 @@ pick_next_task_idle(struct rq *rq, struct task_struct *prev, struct rq_flags *rf { struct task_struct *next = rq->idle; - if (prev) - put_prev_task(rq, prev); - + put_prev_task(rq, prev); set_next_task_idle(rq, next); return next; diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c index ebaa4e619684..f65653b139bc 100644 --- a/kernel/sched/rt.c +++ b/kernel/sched/rt.c @@ -1554,11 +1554,38 @@ pick_next_task_rt(struct rq *rq, struct task_struct *prev, struct rq_flags *rf) struct task_struct *p; struct rt_rq *rt_rq = &rq->rt; - WARN_ON_ONCE(prev || rf); + if (need_pull_rt_task(rq, prev)) { + /* + * 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're + * being very careful to re-start the picking loop. + */ + rq_unpin_lock(rq, rf); + pull_rt_task(rq); + rq_repin_lock(rq, rf); + /* + * pull_rt_task() can drop (and re-acquire) rq->lock; this + * means a dl or stop task can slip in, in which case we need + * to re-start task selection. + */ + if (unlikely((rq->stop && task_on_rq_queued(rq->stop)) || + rq->dl.dl_nr_running)) + return RETRY_TASK; + } + + /* + * We may dequeue prev's rt_rq in put_prev_task(). + * So, we update time before rt_queued check. + */ + if (prev->sched_class == &rt_sched_class) + update_curr_rt(rq); if (!rt_rq->rt_queued) return NULL; + put_prev_task(rq, prev); + p = _pick_next_task_rt(rq); set_next_task_rt(rq, p); diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 4f0d1668c283..e14ae174e0d1 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -1724,15 +1724,12 @@ struct sched_class { void (*check_preempt_curr)(struct rq *rq, struct task_struct *p, int flags); /* - * Both @prev and @rf are optional and may be NULL, in which case the - * caller must already have invoked put_prev_task(rq, prev, rf). + * It is the responsibility of the pick_next_task() method that will + * return the next task to call put_prev_task() on the @prev task or + * something equivalent. * - * Otherwise it is the responsibility of the pick_next_task() to call - * put_prev_task() on the @prev task or something equivalent, IFF it - * returns a next task. - * - * In that case (@rf != NULL) it may return RETRY_TASK when it finds a - * higher prio class has runnable tasks. + * May return RETRY_TASK when it finds a higher prio class has runnable + * tasks. */ struct task_struct * (*pick_next_task)(struct rq *rq, struct task_struct *prev, diff --git a/kernel/sched/stop_task.c b/kernel/sched/stop_task.c index 7e1cee4e65b2..8f414018d5e0 100644 --- a/kernel/sched/stop_task.c +++ b/kernel/sched/stop_task.c @@ -33,11 +33,10 @@ pick_next_task_stop(struct rq *rq, struct task_struct *prev, struct rq_flags *rf { struct task_struct *stop = rq->stop; - WARN_ON_ONCE(prev || rf); - if (!stop || !task_on_rq_queued(stop)) return NULL; + put_prev_task(rq, prev); set_next_task_stop(rq, stop); return stop;