xref: /freebsd/sys/kern/sched_shim.c (revision 120ca8d74b46caa260702485e30fe5f9f9984682)
1ce38aceeSKonstantin Belousov /*
2ce38aceeSKonstantin Belousov  * Copyright 2026 The FreeBSD Foundation
3ce38aceeSKonstantin Belousov  *
4ce38aceeSKonstantin Belousov  * SPDX-License-Identifier: BSD-2-Clause
5ce38aceeSKonstantin Belousov  *
6ce38aceeSKonstantin Belousov  * This software was developed by Konstantin Belousov <kib@FreeBSD.org>
7ce38aceeSKonstantin Belousov  * under sponsorship from the FreeBSD Foundation.
8ce38aceeSKonstantin Belousov  */
9ce38aceeSKonstantin Belousov 
10ce38aceeSKonstantin Belousov #include "opt_sched.h"
11ce38aceeSKonstantin Belousov 
12ce38aceeSKonstantin Belousov #include <sys/systm.h>
13ce38aceeSKonstantin Belousov #include <sys/kernel.h>
14ce38aceeSKonstantin Belousov #include <sys/lock.h>
15ce38aceeSKonstantin Belousov #include <sys/proc.h>
16ce38aceeSKonstantin Belousov #include <sys/runq.h>
17ba8f429fSKonstantin Belousov #include <sys/sbuf.h>
18ce38aceeSKonstantin Belousov #include <sys/sched.h>
19*120ca8d7SKonstantin Belousov #include <sys/smp.h>
20bab24f22SKonstantin Belousov #include <sys/sysctl.h>
21ce38aceeSKonstantin Belousov #include <machine/ifunc.h>
22ce38aceeSKonstantin Belousov 
23ce38aceeSKonstantin Belousov const struct sched_instance *active_sched;
24ce38aceeSKonstantin Belousov 
25ce38aceeSKonstantin Belousov #ifndef __DO_NOT_HAVE_SYS_IFUNCS
26ce38aceeSKonstantin Belousov #define	__DEFINE_SHIM(__m, __r, __n, __p, __a)	\
27ce38aceeSKonstantin Belousov 	DEFINE_IFUNC(, __r, __n, __p)		\
28ce38aceeSKonstantin Belousov 	{					\
29ce38aceeSKonstantin Belousov 		return (active_sched->__m);	\
30ce38aceeSKonstantin Belousov 	}
31ce38aceeSKonstantin Belousov #else
32ce38aceeSKonstantin Belousov #define	__DEFINE_SHIM(__m, __r, __n, __p, __a)	\
33ce38aceeSKonstantin Belousov 	__r					\
34ce38aceeSKonstantin Belousov 	__n __p					\
35ce38aceeSKonstantin Belousov 	{					\
36ce38aceeSKonstantin Belousov 		return (active_sched->__m __a);	\
37ce38aceeSKonstantin Belousov 	}
38ce38aceeSKonstantin Belousov #endif
39ce38aceeSKonstantin Belousov #define	DEFINE_SHIM0(__m, __r, __n)	\
40ce38aceeSKonstantin Belousov     __DEFINE_SHIM(__m, __r, __n, (void), ())
41ce38aceeSKonstantin Belousov #define	DEFINE_SHIM1(__m, __r, __n, __t1, __a1)	\
42ce38aceeSKonstantin Belousov     __DEFINE_SHIM(__m, __r, __n, (__t1 __a1), (__a1))
43ce38aceeSKonstantin Belousov #define	DEFINE_SHIM2(__m, __r, __n, __t1, __a1, __t2, __a2)	\
44ce38aceeSKonstantin Belousov     __DEFINE_SHIM(__m, __r, __n, (__t1 __a1, __t2 __a2), (__a1, __a2))
45ce38aceeSKonstantin Belousov 
46ce38aceeSKonstantin Belousov DEFINE_SHIM0(load, int, sched_load)
47ce38aceeSKonstantin Belousov DEFINE_SHIM0(rr_interval, int, sched_rr_interval)
48ce38aceeSKonstantin Belousov DEFINE_SHIM0(runnable, bool, sched_runnable)
49ce38aceeSKonstantin Belousov DEFINE_SHIM2(exit, void, sched_exit, struct proc *, p,
50ce38aceeSKonstantin Belousov     struct thread *, childtd)
51ce38aceeSKonstantin Belousov DEFINE_SHIM2(fork, void, sched_fork, struct thread *, td,
52ce38aceeSKonstantin Belousov     struct thread *, childtd)
53ce38aceeSKonstantin Belousov DEFINE_SHIM1(fork_exit, void, sched_fork_exit, struct thread *, td)
54ce38aceeSKonstantin Belousov DEFINE_SHIM2(class, void, sched_class, struct thread *, td, int, class)
55ce38aceeSKonstantin Belousov DEFINE_SHIM2(nice, void, sched_nice, struct proc *, p, int, nice)
56ce38aceeSKonstantin Belousov DEFINE_SHIM0(ap_entry, void, sched_ap_entry)
57ce38aceeSKonstantin Belousov DEFINE_SHIM2(exit_thread, void, sched_exit_thread, struct thread *, td,
58ce38aceeSKonstantin Belousov     struct thread *, child)
59ce38aceeSKonstantin Belousov DEFINE_SHIM1(estcpu, u_int, sched_estcpu, struct thread *, td)
60ce38aceeSKonstantin Belousov DEFINE_SHIM2(fork_thread, void, sched_fork_thread, struct thread *, td,
61ce38aceeSKonstantin Belousov     struct thread *, child)
62ce38aceeSKonstantin Belousov DEFINE_SHIM2(ithread_prio, void, sched_ithread_prio, struct thread *, td,
63ce38aceeSKonstantin Belousov     u_char, prio)
64ce38aceeSKonstantin Belousov DEFINE_SHIM2(lend_prio, void, sched_lend_prio, struct thread *, td,
65ce38aceeSKonstantin Belousov     u_char, prio)
66ce38aceeSKonstantin Belousov DEFINE_SHIM2(lend_user_prio, void, sched_lend_user_prio, struct thread *, td,
67ce38aceeSKonstantin Belousov     u_char, pri)
68ce38aceeSKonstantin Belousov DEFINE_SHIM2(lend_user_prio_cond, void, sched_lend_user_prio_cond,
69ce38aceeSKonstantin Belousov     struct thread *, td, u_char, pri)
70ce38aceeSKonstantin Belousov DEFINE_SHIM1(pctcpu, fixpt_t, sched_pctcpu, struct thread *, td)
71ce38aceeSKonstantin Belousov DEFINE_SHIM2(prio, void, sched_prio, struct thread *, td, u_char, prio)
72ce38aceeSKonstantin Belousov DEFINE_SHIM2(sleep, void, sched_sleep, struct thread *, td, int, prio)
73ce38aceeSKonstantin Belousov DEFINE_SHIM2(sswitch, void, sched_switch, struct thread *, td, int, flags)
74ce38aceeSKonstantin Belousov DEFINE_SHIM1(throw, void, sched_throw, struct thread *, td)
75ce38aceeSKonstantin Belousov DEFINE_SHIM2(unlend_prio, void, sched_unlend_prio, struct thread *, td,
76ce38aceeSKonstantin Belousov     u_char, prio)
77ce38aceeSKonstantin Belousov DEFINE_SHIM2(user_prio, void, sched_user_prio, struct thread *, td,
78ce38aceeSKonstantin Belousov     u_char, prio)
79ce38aceeSKonstantin Belousov DEFINE_SHIM1(userret_slowpath, void, sched_userret_slowpath,
80ce38aceeSKonstantin Belousov     struct thread *, td)
81ce38aceeSKonstantin Belousov DEFINE_SHIM2(add, void, sched_add, struct thread *, td, int, flags)
82ce38aceeSKonstantin Belousov DEFINE_SHIM0(choose, struct thread *, sched_choose)
83ce38aceeSKonstantin Belousov DEFINE_SHIM2(clock, void, sched_clock, struct thread *, td, int, cnt)
84ce38aceeSKonstantin Belousov DEFINE_SHIM1(idletd, void, sched_idletd, void *, dummy)
85ce38aceeSKonstantin Belousov DEFINE_SHIM1(preempt, void, sched_preempt, struct thread *, td)
86ce38aceeSKonstantin Belousov DEFINE_SHIM1(relinquish, void, sched_relinquish, struct thread *, td)
87ce38aceeSKonstantin Belousov DEFINE_SHIM1(rem, void, sched_rem, struct thread *, td)
88ce38aceeSKonstantin Belousov DEFINE_SHIM2(wakeup, void, sched_wakeup, struct thread *, td, int, srqflags)
89ce38aceeSKonstantin Belousov DEFINE_SHIM2(bind, void, sched_bind, struct thread *, td, int, cpu)
90ce38aceeSKonstantin Belousov DEFINE_SHIM1(unbind, void, sched_unbind, struct thread *, td)
91ce38aceeSKonstantin Belousov DEFINE_SHIM1(is_bound, int, sched_is_bound, struct thread *, td)
92ce38aceeSKonstantin Belousov DEFINE_SHIM1(affinity, void, sched_affinity, struct thread *, td)
93ce38aceeSKonstantin Belousov DEFINE_SHIM0(sizeof_proc, int, sched_sizeof_proc)
94ce38aceeSKonstantin Belousov DEFINE_SHIM0(sizeof_thread, int, sched_sizeof_thread)
95ce38aceeSKonstantin Belousov DEFINE_SHIM1(tdname, char *, sched_tdname, struct thread *, td)
96ce38aceeSKonstantin Belousov DEFINE_SHIM1(clear_tdname, void, sched_clear_tdname, struct thread *, td)
97c384b35eSKonstantin Belousov DEFINE_SHIM0(do_timer_accounting, bool, sched_do_timer_accounting)
98b602ba1bSKonstantin Belousov DEFINE_SHIM1(find_l2_neighbor, int, sched_find_l2_neighbor, int, cpu)
99ce38aceeSKonstantin Belousov DEFINE_SHIM0(init_ap, void, schedinit_ap)
100bab24f22SKonstantin Belousov 
101a84a39dfSKonstantin Belousov 
102a84a39dfSKonstantin Belousov SCHED_STAT_DEFINE(ithread_demotions, "Interrupt thread priority demotions");
103a84a39dfSKonstantin Belousov SCHED_STAT_DEFINE(ithread_preemptions,
104a84a39dfSKonstantin Belousov     "Interrupt thread preemptions due to time-sharing");
105a84a39dfSKonstantin Belousov 
1069409e869SKonstantin Belousov SDT_PROVIDER_DEFINE(sched);
1079409e869SKonstantin Belousov 
1089409e869SKonstantin Belousov SDT_PROBE_DEFINE3(sched, , , change__pri, "struct thread *",
1099409e869SKonstantin Belousov     "struct proc *", "uint8_t");
1109409e869SKonstantin Belousov SDT_PROBE_DEFINE3(sched, , , dequeue, "struct thread *",
1119409e869SKonstantin Belousov     "struct proc *", "void *");
1129409e869SKonstantin Belousov SDT_PROBE_DEFINE4(sched, , , enqueue, "struct thread *",
1139409e869SKonstantin Belousov     "struct proc *", "void *", "int");
1149409e869SKonstantin Belousov SDT_PROBE_DEFINE4(sched, , , lend__pri, "struct thread *",
1159409e869SKonstantin Belousov     "struct proc *", "uint8_t", "struct thread *");
1169409e869SKonstantin Belousov SDT_PROBE_DEFINE2(sched, , , load__change, "int", "int");
1179409e869SKonstantin Belousov SDT_PROBE_DEFINE2(sched, , , off__cpu, "struct thread *",
1189409e869SKonstantin Belousov     "struct proc *");
1199409e869SKonstantin Belousov SDT_PROBE_DEFINE(sched, , , on__cpu);
1209409e869SKonstantin Belousov SDT_PROBE_DEFINE(sched, , , remain__cpu);
1219409e869SKonstantin Belousov SDT_PROBE_DEFINE2(sched, , , surrender, "struct thread *",
1229409e869SKonstantin Belousov     "struct proc *");
1239409e869SKonstantin Belousov 
124783b8a0fSKonstantin Belousov #ifdef KDTRACE_HOOKS
125783b8a0fSKonstantin Belousov #include <sys/dtrace_bsd.h>
126783b8a0fSKonstantin Belousov int __read_mostly		dtrace_vtime_active;
127783b8a0fSKonstantin Belousov dtrace_vtime_switch_func_t	dtrace_vtime_switch_func;
128783b8a0fSKonstantin Belousov #endif
129783b8a0fSKonstantin Belousov 
130bab24f22SKonstantin Belousov static char sched_name[32] = "ULE";
131bab24f22SKonstantin Belousov 
132bab24f22SKonstantin Belousov SET_DECLARE(sched_instance_set, struct sched_selection);
133bab24f22SKonstantin Belousov 
134bab24f22SKonstantin Belousov void
135bab24f22SKonstantin Belousov sched_instance_select(void)
136bab24f22SKonstantin Belousov {
137bab24f22SKonstantin Belousov 	struct sched_selection *s, **ss;
138bab24f22SKonstantin Belousov 	int i;
139bab24f22SKonstantin Belousov 
140bab24f22SKonstantin Belousov 	TUNABLE_STR_FETCH("kern.sched.name", sched_name, sizeof(sched_name));
141bab24f22SKonstantin Belousov 	SET_FOREACH(ss, sched_instance_set) {
142bab24f22SKonstantin Belousov 		s = *ss;
143bab24f22SKonstantin Belousov 		for (i = 0; s->name[i] == sched_name[i]; i++) {
144bab24f22SKonstantin Belousov 			if (s->name[i] == '\0') {
145bab24f22SKonstantin Belousov 				active_sched = s->instance;
146bab24f22SKonstantin Belousov 				return;
147bab24f22SKonstantin Belousov 			}
148bab24f22SKonstantin Belousov 		}
149bab24f22SKonstantin Belousov 	}
150bab24f22SKonstantin Belousov 
151bab24f22SKonstantin Belousov 	/*
152bab24f22SKonstantin Belousov 	 * No scheduler matching the configuration was found.  If
153bab24f22SKonstantin Belousov 	 * there is any scheduler compiled in, at all, use the first
154bab24f22SKonstantin Belousov 	 * scheduler from the linker set.
155bab24f22SKonstantin Belousov 	 */
156bab24f22SKonstantin Belousov 	if (SET_BEGIN(sched_instance_set) < SET_LIMIT(sched_instance_set)) {
157bab24f22SKonstantin Belousov 		s = *SET_BEGIN(sched_instance_set);
158bab24f22SKonstantin Belousov 		active_sched = s->instance;
159bab24f22SKonstantin Belousov 		for (i = 0;; i++) {
160bab24f22SKonstantin Belousov 			sched_name[i] = s->name[i];
161bab24f22SKonstantin Belousov 			if (s->name[i] == '\0')
162bab24f22SKonstantin Belousov 				break;
163bab24f22SKonstantin Belousov 		}
164bab24f22SKonstantin Belousov 	}
165bab24f22SKonstantin Belousov }
166bab24f22SKonstantin Belousov 
167bab24f22SKonstantin Belousov void
168bab24f22SKonstantin Belousov schedinit(void)
169bab24f22SKonstantin Belousov {
170bab24f22SKonstantin Belousov 	if (active_sched == NULL)
171bab24f22SKonstantin Belousov 		panic("Cannot find scheduler %s", sched_name);
172bab24f22SKonstantin Belousov 	active_sched->init();
173bab24f22SKonstantin Belousov }
174bab24f22SKonstantin Belousov 
175*120ca8d7SKonstantin Belousov struct cpu_group __read_mostly *cpu_top;		/* CPU topology */
176*120ca8d7SKonstantin Belousov 
1777efbfd6fSKonstantin Belousov static void
1787efbfd6fSKonstantin Belousov sched_setup(void *dummy)
1797efbfd6fSKonstantin Belousov {
180*120ca8d7SKonstantin Belousov 	cpu_top = smp_topo();
1817efbfd6fSKonstantin Belousov 	active_sched->setup();
1827efbfd6fSKonstantin Belousov }
1837efbfd6fSKonstantin Belousov SYSINIT(sched_setup, SI_SUB_RUN_QUEUE, SI_ORDER_FIRST, sched_setup, NULL);
1847efbfd6fSKonstantin Belousov 
1857efbfd6fSKonstantin Belousov static void
1867efbfd6fSKonstantin Belousov sched_initticks(void *dummy)
1877efbfd6fSKonstantin Belousov {
1887efbfd6fSKonstantin Belousov 	active_sched->initticks();
1897efbfd6fSKonstantin Belousov }
1907efbfd6fSKonstantin Belousov SYSINIT(sched_initticks, SI_SUB_CLOCKS, SI_ORDER_THIRD, sched_initticks,
1917efbfd6fSKonstantin Belousov     NULL);
1927efbfd6fSKonstantin Belousov 
1937efbfd6fSKonstantin Belousov static void
1947efbfd6fSKonstantin Belousov sched_schedcpu(void)
1957efbfd6fSKonstantin Belousov {
1967efbfd6fSKonstantin Belousov 	active_sched->schedcpu();
1977efbfd6fSKonstantin Belousov }
1987efbfd6fSKonstantin Belousov SYSINIT(schedcpu, SI_SUB_LAST, SI_ORDER_FIRST, sched_schedcpu, NULL);
1997efbfd6fSKonstantin Belousov 
2007efbfd6fSKonstantin Belousov SYSCTL_NODE(_kern, OID_AUTO, sched, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
2017efbfd6fSKonstantin Belousov     "Scheduler");
2027efbfd6fSKonstantin Belousov 
203bab24f22SKonstantin Belousov SYSCTL_STRING(_kern_sched, OID_AUTO, name, CTLFLAG_RD, sched_name, 0,
204bab24f22SKonstantin Belousov     "Scheduler name");
205ba8f429fSKonstantin Belousov 
206ba8f429fSKonstantin Belousov static int
207ba8f429fSKonstantin Belousov sysctl_kern_sched_available(SYSCTL_HANDLER_ARGS)
208ba8f429fSKonstantin Belousov {
209ba8f429fSKonstantin Belousov 	struct sched_selection *s, **ss;
210ba8f429fSKonstantin Belousov 	struct sbuf *sb, sm;
211ba8f429fSKonstantin Belousov 	int error;
212ba8f429fSKonstantin Belousov 	bool first;
213ba8f429fSKonstantin Belousov 
214ba8f429fSKonstantin Belousov 	sb = sbuf_new_for_sysctl(&sm, NULL, 0, req);
215ba8f429fSKonstantin Belousov 	if (sb == NULL)
216ba8f429fSKonstantin Belousov 		return (ENOMEM);
217ba8f429fSKonstantin Belousov 	first = true;
218ba8f429fSKonstantin Belousov 	SET_FOREACH(ss, sched_instance_set) {
219ba8f429fSKonstantin Belousov 		s = *ss;
220ba8f429fSKonstantin Belousov 		if (first)
221ba8f429fSKonstantin Belousov 			first = false;
222ba8f429fSKonstantin Belousov 		else
223ba8f429fSKonstantin Belousov 			sbuf_cat(sb, ",");
224ba8f429fSKonstantin Belousov 		sbuf_cat(sb, s->name);
225ba8f429fSKonstantin Belousov 	}
226ba8f429fSKonstantin Belousov 	error = sbuf_finish(sb);
227ba8f429fSKonstantin Belousov 	sbuf_delete(sb);
228ba8f429fSKonstantin Belousov 	return (error);
229ba8f429fSKonstantin Belousov }
230ba8f429fSKonstantin Belousov 
231ba8f429fSKonstantin Belousov SYSCTL_PROC(_kern_sched, OID_AUTO, available,
232ba8f429fSKonstantin Belousov     CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
233ba8f429fSKonstantin Belousov     NULL, 0, sysctl_kern_sched_available, "A",
234ba8f429fSKonstantin Belousov     "List of available schedulers");
235ff870b78SKonstantin Belousov 
236ff870b78SKonstantin Belousov fixpt_t ccpu;
237ff870b78SKonstantin Belousov SYSCTL_UINT(_kern, OID_AUTO, ccpu, CTLFLAG_RD, &ccpu, 0,
238ff870b78SKonstantin Belousov     "Decay factor used for updating %CPU");
239*120ca8d7SKonstantin Belousov 
240*120ca8d7SKonstantin Belousov /*
241*120ca8d7SKonstantin Belousov  * Build the CPU topology dump string. Is recursively called to collect
242*120ca8d7SKonstantin Belousov  * the topology tree.
243*120ca8d7SKonstantin Belousov  */
244*120ca8d7SKonstantin Belousov static int
245*120ca8d7SKonstantin Belousov sysctl_kern_sched_topology_spec_internal(struct sbuf *sb,
246*120ca8d7SKonstantin Belousov     struct cpu_group *cg, int indent)
247*120ca8d7SKonstantin Belousov {
248*120ca8d7SKonstantin Belousov 	char cpusetbuf[CPUSETBUFSIZ];
249*120ca8d7SKonstantin Belousov 	int i, first;
250*120ca8d7SKonstantin Belousov 
251*120ca8d7SKonstantin Belousov 	if (cpu_top == NULL) {
252*120ca8d7SKonstantin Belousov 		sbuf_printf(sb, "%*s<group level=\"1\" cache-level=\"1\">\n",
253*120ca8d7SKonstantin Belousov 		    indent, "");
254*120ca8d7SKonstantin Belousov 		sbuf_printf(sb, "%*s</group>\n", indent, "");
255*120ca8d7SKonstantin Belousov 		return (0);
256*120ca8d7SKonstantin Belousov 	}
257*120ca8d7SKonstantin Belousov 
258*120ca8d7SKonstantin Belousov 	sbuf_printf(sb, "%*s<group level=\"%d\" cache-level=\"%d\">\n", indent,
259*120ca8d7SKonstantin Belousov 	    "", 1 + indent / 2, cg->cg_level);
260*120ca8d7SKonstantin Belousov 	sbuf_printf(sb, "%*s <cpu count=\"%d\" mask=\"%s\">", indent, "",
261*120ca8d7SKonstantin Belousov 	    cg->cg_count, cpusetobj_strprint(cpusetbuf, &cg->cg_mask));
262*120ca8d7SKonstantin Belousov 	first = TRUE;
263*120ca8d7SKonstantin Belousov 	for (i = cg->cg_first; i <= cg->cg_last; i++) {
264*120ca8d7SKonstantin Belousov 		if (CPU_ISSET(i, &cg->cg_mask)) {
265*120ca8d7SKonstantin Belousov 			if (!first)
266*120ca8d7SKonstantin Belousov 				sbuf_cat(sb, ", ");
267*120ca8d7SKonstantin Belousov 			else
268*120ca8d7SKonstantin Belousov 				first = FALSE;
269*120ca8d7SKonstantin Belousov 			sbuf_printf(sb, "%d", i);
270*120ca8d7SKonstantin Belousov 		}
271*120ca8d7SKonstantin Belousov 	}
272*120ca8d7SKonstantin Belousov 	sbuf_cat(sb, "</cpu>\n");
273*120ca8d7SKonstantin Belousov 
274*120ca8d7SKonstantin Belousov 	if (cg->cg_flags != 0) {
275*120ca8d7SKonstantin Belousov 		sbuf_printf(sb, "%*s <flags>", indent, "");
276*120ca8d7SKonstantin Belousov 		if ((cg->cg_flags & CG_FLAG_HTT) != 0)
277*120ca8d7SKonstantin Belousov 			sbuf_cat(sb, "<flag name=\"HTT\">HTT group</flag>");
278*120ca8d7SKonstantin Belousov 		if ((cg->cg_flags & CG_FLAG_THREAD) != 0)
279*120ca8d7SKonstantin Belousov 			sbuf_cat(sb, "<flag name=\"THREAD\">THREAD group</flag>");
280*120ca8d7SKonstantin Belousov 		if ((cg->cg_flags & CG_FLAG_SMT) != 0)
281*120ca8d7SKonstantin Belousov 			sbuf_cat(sb, "<flag name=\"SMT\">SMT group</flag>");
282*120ca8d7SKonstantin Belousov 		if ((cg->cg_flags & CG_FLAG_NODE) != 0)
283*120ca8d7SKonstantin Belousov 			sbuf_cat(sb, "<flag name=\"NODE\">NUMA node</flag>");
284*120ca8d7SKonstantin Belousov 		sbuf_cat(sb, "</flags>\n");
285*120ca8d7SKonstantin Belousov 	}
286*120ca8d7SKonstantin Belousov 
287*120ca8d7SKonstantin Belousov 	if (cg->cg_children > 0) {
288*120ca8d7SKonstantin Belousov 		sbuf_printf(sb, "%*s <children>\n", indent, "");
289*120ca8d7SKonstantin Belousov 		for (i = 0; i < cg->cg_children; i++)
290*120ca8d7SKonstantin Belousov 			sysctl_kern_sched_topology_spec_internal(sb,
291*120ca8d7SKonstantin Belousov 			    &cg->cg_child[i], indent + 2);
292*120ca8d7SKonstantin Belousov 		sbuf_printf(sb, "%*s </children>\n", indent, "");
293*120ca8d7SKonstantin Belousov 	}
294*120ca8d7SKonstantin Belousov 	sbuf_printf(sb, "%*s</group>\n", indent, "");
295*120ca8d7SKonstantin Belousov 	return (0);
296*120ca8d7SKonstantin Belousov }
297*120ca8d7SKonstantin Belousov 
298*120ca8d7SKonstantin Belousov /*
299*120ca8d7SKonstantin Belousov  * Sysctl handler for retrieving topology dump. It's a wrapper for
300*120ca8d7SKonstantin Belousov  * the recursive sysctl_kern_smp_topology_spec_internal().
301*120ca8d7SKonstantin Belousov  */
302*120ca8d7SKonstantin Belousov static int
303*120ca8d7SKonstantin Belousov sysctl_kern_sched_topology_spec(SYSCTL_HANDLER_ARGS)
304*120ca8d7SKonstantin Belousov {
305*120ca8d7SKonstantin Belousov 	struct sbuf *topo;
306*120ca8d7SKonstantin Belousov 	int err;
307*120ca8d7SKonstantin Belousov 
308*120ca8d7SKonstantin Belousov 	topo = sbuf_new_for_sysctl(NULL, NULL, 512, req);
309*120ca8d7SKonstantin Belousov 	if (topo == NULL)
310*120ca8d7SKonstantin Belousov 		return (ENOMEM);
311*120ca8d7SKonstantin Belousov 
312*120ca8d7SKonstantin Belousov 	sbuf_cat(topo, "<groups>\n");
313*120ca8d7SKonstantin Belousov 	err = sysctl_kern_sched_topology_spec_internal(topo, cpu_top, 1);
314*120ca8d7SKonstantin Belousov 	sbuf_cat(topo, "</groups>\n");
315*120ca8d7SKonstantin Belousov 
316*120ca8d7SKonstantin Belousov 	if (err == 0)
317*120ca8d7SKonstantin Belousov 		err = sbuf_finish(topo);
318*120ca8d7SKonstantin Belousov 	sbuf_delete(topo);
319*120ca8d7SKonstantin Belousov 	return (err);
320*120ca8d7SKonstantin Belousov }
321*120ca8d7SKonstantin Belousov 
322*120ca8d7SKonstantin Belousov SYSCTL_PROC(_kern_sched, OID_AUTO, topology_spec, CTLTYPE_STRING |
323*120ca8d7SKonstantin Belousov     CTLFLAG_MPSAFE | CTLFLAG_RD, NULL, 0,
324*120ca8d7SKonstantin Belousov     sysctl_kern_sched_topology_spec, "A",
325*120ca8d7SKonstantin Belousov     "XML dump of detected CPU topology");
326