aboutsummaryrefslogtreecommitdiff
path: root/sys/kern/sched_ule.c
diff options
context:
space:
mode:
authorKyle Evans <kevans@FreeBSD.org>2021-11-02 18:06:47 +0000
committerKyle Evans <kevans@FreeBSD.org>2021-11-03 20:54:59 +0000
commit589aed00e36c22733d3fd9c9016deccf074830b1 (patch)
treeae8c77b91823e8342d10a27a6260b1cc8ab99c5f /sys/kern/sched_ule.c
parentae49051c033a2468af2f1f0079ecaf069b993245 (diff)
downloadsrc-589aed00e36c22733d3fd9c9016deccf074830b1.tar.gz
src-589aed00e36c22733d3fd9c9016deccf074830b1.zip
sched: separate out schedinit_ap()
schedinit_ap() sets up an AP for a later call to sched_throw(NULL). Currently, ULE sets up some pcpu bits and fixes the idlethread lock with a call to sched_throw(NULL); this results in a window where curthread is setup in platforms' init_secondary(), but it has the wrong td_lock. Typical platform AP startup procedure looks something like: - Setup curthread - ... other stuff, including cpu_initclocks_ap() - Signal smp_started - sched_throw(NULL) to enter the scheduler cpu_initclocks_ap() may have callouts to process (e.g., nvme) and attempt to sched_add() for this AP, but this attempt fails because of the noted violated assumption leading to locking heartburn in sched_setpreempt(). Interrupts are still disabled until cpu_throw() so we're not really at risk of being preempted -- just let the scheduler in on it a little earlier as part of setting up curthread. Reviewed by: alfredo, kib, markj Triage help from: andrew, markj Smoke-tested by: alfredo (ppc), kevans (arm64, x86), mhorne (arm) Differential Revision: https://reviews.freebsd.org/D32797
Diffstat (limited to 'sys/kern/sched_ule.c')
-rw-r--r--sys/kern/sched_ule.c29
1 files changed, 22 insertions, 7 deletions
diff --git a/sys/kern/sched_ule.c b/sys/kern/sched_ule.c
index 1b9473b93773..ce7ce4cd2bd8 100644
--- a/sys/kern/sched_ule.c
+++ b/sys/kern/sched_ule.c
@@ -1744,6 +1744,26 @@ schedinit(void)
}
/*
+ * schedinit_ap() is needed prior to calling sched_throw(NULL) to ensure that
+ * the pcpu requirements are met for any calls in the period between curthread
+ * initialization and sched_throw(). One can safely add threads to the queue
+ * before sched_throw(), for instance, as long as the thread lock is setup
+ * correctly.
+ *
+ * TDQ_SELF() relies on the below sched pcpu setting; it may be used only
+ * after schedinit_ap().
+ */
+void
+schedinit_ap(void)
+{
+
+#ifdef SMP
+ PCPU_SET(sched, DPCPU_PTR(tdq));
+#endif
+ PCPU_GET(idlethread)->td_lock = TDQ_LOCKPTR(TDQ_SELF());
+}
+
+/*
* This is only somewhat accurate since given many processes of the same
* priority they will switch when their slices run out, which will be
* at most sched_slice stathz ticks.
@@ -2973,19 +2993,14 @@ sched_throw(struct thread *td)
struct thread *newtd;
struct tdq *tdq;
+ tdq = TDQ_SELF();
if (__predict_false(td == NULL)) {
-#ifdef SMP
- PCPU_SET(sched, DPCPU_PTR(tdq));
-#endif
- /* Correct spinlock nesting and acquire the correct lock. */
- tdq = TDQ_SELF();
TDQ_LOCK(tdq);
+ /* Correct spinlock nesting. */
spinlock_exit();
PCPU_SET(switchtime, cpu_ticks());
PCPU_SET(switchticks, ticks);
- PCPU_GET(idlethread)->td_lock = TDQ_LOCKPTR(tdq);
} else {
- tdq = TDQ_SELF();
THREAD_LOCK_ASSERT(td, MA_OWNED);
THREAD_LOCKPTR_ASSERT(td, TDQ_LOCKPTR(tdq));
tdq_load_rem(tdq, td);