xref: /netbsd-src/sys/kern/subr_interrupt.c (revision cbf5c65aff7a9fb3766c02a179c5c934c4485837)
1*cbf5c65aSandvar /*	$NetBSD: subr_interrupt.c,v 1.5 2021/12/10 20:36:04 andvar Exp $	*/
2a604df28Sknakahara 
3a604df28Sknakahara /*
4a604df28Sknakahara  * Copyright (c) 2015 Internet Initiative Japan Inc.
5a604df28Sknakahara  * All rights reserved.
6a604df28Sknakahara  *
7a604df28Sknakahara  * Redistribution and use in source and binary forms, with or without
8a604df28Sknakahara  * modification, are permitted provided that the following conditions
9a604df28Sknakahara  * are met:
10a604df28Sknakahara  * 1. Redistributions of source code must retain the above copyright
11a604df28Sknakahara  *    notice, this list of conditions and the following disclaimer.
12a604df28Sknakahara  * 2. Redistributions in binary form must reproduce the above copyright
13a604df28Sknakahara  *    notice, this list of conditions and the following disclaimer in the
14a604df28Sknakahara  *    documentation and/or other materials provided with the distribution.
15a604df28Sknakahara  *
16a604df28Sknakahara  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17a604df28Sknakahara  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18a604df28Sknakahara  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19a604df28Sknakahara  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20a604df28Sknakahara  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21a604df28Sknakahara  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22a604df28Sknakahara  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23a604df28Sknakahara  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24a604df28Sknakahara  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25a604df28Sknakahara  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26a604df28Sknakahara  * POSSIBILITY OF SUCH DAMAGE.
27a604df28Sknakahara  */
28a604df28Sknakahara 
29a604df28Sknakahara #include <sys/cdefs.h>
30*cbf5c65aSandvar __KERNEL_RCSID(0, "$NetBSD: subr_interrupt.c,v 1.5 2021/12/10 20:36:04 andvar Exp $");
31a604df28Sknakahara 
32a604df28Sknakahara #include <sys/param.h>
33a604df28Sknakahara #include <sys/systm.h>
34a604df28Sknakahara #include <sys/kernel.h>
35a604df28Sknakahara #include <sys/errno.h>
36a604df28Sknakahara #include <sys/cpu.h>
37a604df28Sknakahara #include <sys/interrupt.h>
38a604df28Sknakahara #include <sys/intr.h>
39a604df28Sknakahara #include <sys/kcpuset.h>
40a604df28Sknakahara #include <sys/kmem.h>
41a604df28Sknakahara #include <sys/proc.h>
42a604df28Sknakahara #include <sys/xcall.h>
43a604df28Sknakahara #include <sys/sysctl.h>
44a604df28Sknakahara 
45a604df28Sknakahara #include <sys/conf.h>
46a604df28Sknakahara #include <sys/intrio.h>
47a604df28Sknakahara #include <sys/kauth.h>
48a604df28Sknakahara 
49a604df28Sknakahara #include <machine/limits.h>
50a604df28Sknakahara 
51a604df28Sknakahara #ifdef INTR_DEBUG
52a604df28Sknakahara #define DPRINTF(msg) printf msg
53a604df28Sknakahara #else
54a604df28Sknakahara #define DPRINTF(msg)
55a604df28Sknakahara #endif
56a604df28Sknakahara 
57a604df28Sknakahara static struct intrio_set kintrio_set = { "\0", NULL, 0 };
58a604df28Sknakahara 
59a604df28Sknakahara #define UNSET_NOINTR_SHIELD	0
60a604df28Sknakahara #define SET_NOINTR_SHIELD	1
61a604df28Sknakahara 
62a604df28Sknakahara static void
interrupt_shield_xcall(void * arg1,void * arg2)63a604df28Sknakahara interrupt_shield_xcall(void *arg1, void *arg2)
64a604df28Sknakahara {
65a604df28Sknakahara 	struct cpu_info *ci;
66a604df28Sknakahara 	struct schedstate_percpu *spc;
67a604df28Sknakahara 	int s, shield;
68a604df28Sknakahara 
69a604df28Sknakahara 	ci = arg1;
70a604df28Sknakahara 	shield = (int)(intptr_t)arg2;
71a604df28Sknakahara 	spc = &ci->ci_schedstate;
72a604df28Sknakahara 
73a604df28Sknakahara 	s = splsched();
74a604df28Sknakahara 	if (shield == UNSET_NOINTR_SHIELD)
75a604df28Sknakahara 		spc->spc_flags &= ~SPCF_NOINTR;
76a604df28Sknakahara 	else if (shield == SET_NOINTR_SHIELD)
77a604df28Sknakahara 		spc->spc_flags |= SPCF_NOINTR;
78a604df28Sknakahara 	splx(s);
79a604df28Sknakahara }
80a604df28Sknakahara 
81a604df28Sknakahara /*
82a604df28Sknakahara  * Change SPCF_NOINTR flag of schedstate_percpu->spc_flags.
83a604df28Sknakahara  */
84a604df28Sknakahara static int
interrupt_shield(u_int cpu_idx,int shield)85a604df28Sknakahara interrupt_shield(u_int cpu_idx, int shield)
86a604df28Sknakahara {
87a604df28Sknakahara 	struct cpu_info *ci;
88a604df28Sknakahara 	struct schedstate_percpu *spc;
89a604df28Sknakahara 
90a604df28Sknakahara 	KASSERT(mutex_owned(&cpu_lock));
91a604df28Sknakahara 
92a604df28Sknakahara 	ci = cpu_lookup(cpu_idx);
93a604df28Sknakahara 	if (ci == NULL)
94a604df28Sknakahara 		return EINVAL;
95a604df28Sknakahara 
96a604df28Sknakahara 	spc = &ci->ci_schedstate;
97a604df28Sknakahara 	if (shield == UNSET_NOINTR_SHIELD) {
98a604df28Sknakahara 		if ((spc->spc_flags & SPCF_NOINTR) == 0)
99a604df28Sknakahara 			return 0;
100a604df28Sknakahara 	} else if (shield == SET_NOINTR_SHIELD) {
101a604df28Sknakahara 		if ((spc->spc_flags & SPCF_NOINTR) != 0)
102a604df28Sknakahara 			return 0;
103a604df28Sknakahara 	}
104a604df28Sknakahara 
105a604df28Sknakahara 	if (ci == curcpu() || !mp_online) {
106a604df28Sknakahara 		interrupt_shield_xcall(ci, (void *)(intptr_t)shield);
107a604df28Sknakahara 	} else {
108a604df28Sknakahara 		uint64_t where;
109a604df28Sknakahara 		where = xc_unicast(0, interrupt_shield_xcall, ci,
110a604df28Sknakahara 			(void *)(intptr_t)shield, ci);
111a604df28Sknakahara 		xc_wait(where);
112a604df28Sknakahara 	}
113a604df28Sknakahara 
114a604df28Sknakahara 	spc->spc_lastmod = time_second;
115a604df28Sknakahara 	return 0;
116a604df28Sknakahara }
117a604df28Sknakahara 
118a604df28Sknakahara /*
119a604df28Sknakahara  * Move all assigned interrupts from "cpu_idx" to the other cpu as possible.
120a604df28Sknakahara  * The destination cpu is the lowest cpuid of available cpus.
121a604df28Sknakahara  * If there are no available cpus, give up to move interrupts.
122a604df28Sknakahara  */
123a604df28Sknakahara static int
interrupt_avert_intr(u_int cpu_idx)124a604df28Sknakahara interrupt_avert_intr(u_int cpu_idx)
125a604df28Sknakahara {
126a604df28Sknakahara 	kcpuset_t *cpuset;
127a604df28Sknakahara 	struct intrids_handler *ii_handler;
128a604df28Sknakahara 	intrid_t *ids;
129c14455feSreinoud 	int error = 0, i, nids;
130a604df28Sknakahara 
131a604df28Sknakahara 	kcpuset_create(&cpuset, true);
132a604df28Sknakahara 	kcpuset_set(cpuset, cpu_idx);
133a604df28Sknakahara 
134a604df28Sknakahara 	ii_handler = interrupt_construct_intrids(cpuset);
135a604df28Sknakahara 	if (ii_handler == NULL) {
13696bd66bcSchristos 		error = EINVAL;
137a604df28Sknakahara 		goto out;
138a604df28Sknakahara 	}
139a604df28Sknakahara 	nids = ii_handler->iih_nids;
140a604df28Sknakahara 	if (nids == 0) {
141a604df28Sknakahara 		error = 0;
142a604df28Sknakahara 		goto destruct_out;
143a604df28Sknakahara 	}
144a604df28Sknakahara 
145a604df28Sknakahara 	interrupt_get_available(cpuset);
146a604df28Sknakahara 	kcpuset_clear(cpuset, cpu_idx);
147a604df28Sknakahara 	if (kcpuset_iszero(cpuset)) {
148a604df28Sknakahara 		DPRINTF(("%s: no available cpu\n", __func__));
149a604df28Sknakahara 		error = ENOENT;
150a604df28Sknakahara 		goto destruct_out;
151a604df28Sknakahara 	}
152a604df28Sknakahara 
153a604df28Sknakahara 	ids = ii_handler->iih_intrids;
154a604df28Sknakahara 	for (i = 0; i < nids; i++) {
155a604df28Sknakahara 		error = interrupt_distribute_handler(ids[i], cpuset, NULL);
156a604df28Sknakahara 		if (error)
157a604df28Sknakahara 			break;
158a604df28Sknakahara 	}
159a604df28Sknakahara 
160a604df28Sknakahara  destruct_out:
161a604df28Sknakahara 	interrupt_destruct_intrids(ii_handler);
162a604df28Sknakahara  out:
163a604df28Sknakahara 	kcpuset_destroy(cpuset);
164a604df28Sknakahara 	return error;
165a604df28Sknakahara }
166a604df28Sknakahara 
167a604df28Sknakahara /*
168a604df28Sknakahara  * Return actual intrio_list_line size.
169a604df28Sknakahara  * intrio_list_line size is variable by ncpu.
170a604df28Sknakahara  */
171a604df28Sknakahara static size_t
interrupt_intrio_list_line_size(void)172a604df28Sknakahara interrupt_intrio_list_line_size(void)
173a604df28Sknakahara {
174a604df28Sknakahara 
175a604df28Sknakahara 	return sizeof(struct intrio_list_line) +
176a604df28Sknakahara 		sizeof(struct intrio_list_line_cpu) * (ncpu - 1);
177a604df28Sknakahara }
178a604df28Sknakahara 
179a604df28Sknakahara /*
180a604df28Sknakahara  * Return the size of interrupts list data on success.
181a604df28Sknakahara  * Reterun 0 on failed.
182a604df28Sknakahara  */
18396bd66bcSchristos static int
interrupt_intrio_list_size(size_t * ilsize)18496bd66bcSchristos interrupt_intrio_list_size(size_t *ilsize)
185a604df28Sknakahara {
186a604df28Sknakahara 	struct intrids_handler *ii_handler;
187a604df28Sknakahara 
18896bd66bcSchristos 	*ilsize = 0;
189a604df28Sknakahara 
190a604df28Sknakahara 	/* buffer header */
19196bd66bcSchristos 	*ilsize += sizeof(struct intrio_list);
192a604df28Sknakahara 
193a604df28Sknakahara 	/* il_line body */
194a604df28Sknakahara 	ii_handler = interrupt_construct_intrids(kcpuset_running);
195a604df28Sknakahara 	if (ii_handler == NULL)
19696bd66bcSchristos 		return EOPNOTSUPP;
19796bd66bcSchristos 	*ilsize += interrupt_intrio_list_line_size() * ii_handler->iih_nids;
198a604df28Sknakahara 
199a604df28Sknakahara 	interrupt_destruct_intrids(ii_handler);
20096bd66bcSchristos 	return 0;
201a604df28Sknakahara }
202a604df28Sknakahara 
203a604df28Sknakahara /*
204a604df28Sknakahara  * Set intrctl list data to "il", and return list structure bytes.
205*cbf5c65aSandvar  * If error occurred, return <0.
206a604df28Sknakahara  * If "data" == NULL, simply return list structure bytes.
207a604df28Sknakahara  */
208a604df28Sknakahara static int
interrupt_intrio_list(struct intrio_list * il,size_t ilsize)20996bd66bcSchristos interrupt_intrio_list(struct intrio_list *il, size_t ilsize)
210a604df28Sknakahara {
211a604df28Sknakahara 	struct intrio_list_line *illine;
212a604df28Sknakahara 	kcpuset_t *assigned, *avail;
213a604df28Sknakahara 	struct intrids_handler *ii_handler;
214a604df28Sknakahara 	intrid_t *ids;
215a604df28Sknakahara 	u_int cpu_idx;
21696bd66bcSchristos 	int nids, intr_idx, error, line_size;
217a604df28Sknakahara 
218a604df28Sknakahara 	illine = (struct intrio_list_line *)
219a604df28Sknakahara 	    ((char *)il + sizeof(struct intrio_list));
220a604df28Sknakahara 	il->il_lineoffset = (off_t)((uintptr_t)illine - (uintptr_t)il);
221a604df28Sknakahara 
222a604df28Sknakahara 	kcpuset_create(&avail, true);
223a604df28Sknakahara 	interrupt_get_available(avail);
224a604df28Sknakahara 	kcpuset_create(&assigned, true);
225a604df28Sknakahara 
226a604df28Sknakahara 	ii_handler = interrupt_construct_intrids(kcpuset_running);
227a604df28Sknakahara 	if (ii_handler == NULL) {
228a604df28Sknakahara 		DPRINTF(("%s: interrupt_construct_intrids() failed\n",
229a604df28Sknakahara 		    __func__));
23096bd66bcSchristos 		error = EOPNOTSUPP;
231a604df28Sknakahara 		goto out;
232a604df28Sknakahara 	}
233a604df28Sknakahara 
234a604df28Sknakahara 	line_size = interrupt_intrio_list_line_size();
23596bd66bcSchristos 	/* ensure interrupts are not added after interrupt_intrio_list_size() */
236a604df28Sknakahara 	nids = ii_handler->iih_nids;
237a604df28Sknakahara 	ids = ii_handler->iih_intrids;
238a604df28Sknakahara 	if (ilsize < sizeof(struct intrio_list) + line_size * nids) {
239a604df28Sknakahara 		DPRINTF(("%s: interrupts are added during execution.\n",
240a604df28Sknakahara 		    __func__));
24196bd66bcSchristos 		error = EAGAIN;
242a604df28Sknakahara 		goto destruct_out;
243a604df28Sknakahara 	}
244a604df28Sknakahara 
245a604df28Sknakahara 	for (intr_idx = 0; intr_idx < nids; intr_idx++) {
246a604df28Sknakahara 		char devname[INTRDEVNAMEBUF];
247a604df28Sknakahara 
248a604df28Sknakahara 		strncpy(illine->ill_intrid, ids[intr_idx], INTRIDBUF);
249a604df28Sknakahara 		interrupt_get_devname(ids[intr_idx], devname, sizeof(devname));
250a604df28Sknakahara 		strncpy(illine->ill_xname, devname, INTRDEVNAMEBUF);
251a604df28Sknakahara 
252a604df28Sknakahara 		interrupt_get_assigned(ids[intr_idx], assigned);
253a604df28Sknakahara 		for (cpu_idx = 0; cpu_idx < ncpu; cpu_idx++) {
254a604df28Sknakahara 			struct intrio_list_line_cpu *illcpu =
255a604df28Sknakahara 			    &illine->ill_cpu[cpu_idx];
256a604df28Sknakahara 
257a604df28Sknakahara 			illcpu->illc_assigned =
25896bd66bcSchristos 			    kcpuset_isset(assigned, cpu_idx);
259a604df28Sknakahara 			illcpu->illc_count =
260a604df28Sknakahara 			    interrupt_get_count(ids[intr_idx], cpu_idx);
261a604df28Sknakahara 		}
262a604df28Sknakahara 
263a604df28Sknakahara 		illine = (struct intrio_list_line *)
264a604df28Sknakahara 		    ((char *)illine + line_size);
265a604df28Sknakahara 	}
266a604df28Sknakahara 
26796bd66bcSchristos 	error = 0;
268a604df28Sknakahara 	il->il_version = INTRIO_LIST_VERSION;
269a604df28Sknakahara 	il->il_ncpus = ncpu;
270a604df28Sknakahara 	il->il_nintrs = nids;
271a604df28Sknakahara 	il->il_linesize = line_size;
272a604df28Sknakahara 	il->il_bufsize = ilsize;
273a604df28Sknakahara 
274a604df28Sknakahara  destruct_out:
275a604df28Sknakahara 	interrupt_destruct_intrids(ii_handler);
276a604df28Sknakahara  out:
277a604df28Sknakahara 	kcpuset_destroy(assigned);
278a604df28Sknakahara 	kcpuset_destroy(avail);
279a604df28Sknakahara 
28096bd66bcSchristos 	return error;
281a604df28Sknakahara }
282a604df28Sknakahara 
283a604df28Sknakahara /*
284a604df28Sknakahara  * "intrctl list" entry
285a604df28Sknakahara  */
286a604df28Sknakahara static int
interrupt_intrio_list_sysctl(SYSCTLFN_ARGS)287a604df28Sknakahara interrupt_intrio_list_sysctl(SYSCTLFN_ARGS)
288a604df28Sknakahara {
28996bd66bcSchristos 	int error;
290a604df28Sknakahara 	void *buf;
29196bd66bcSchristos 	size_t ilsize;
292a604df28Sknakahara 
293a604df28Sknakahara 	if (oldlenp == NULL)
294a604df28Sknakahara 		return EINVAL;
295a604df28Sknakahara 
29696bd66bcSchristos 	if ((error = interrupt_intrio_list_size(&ilsize)) != 0)
29796bd66bcSchristos 		return error;
29896bd66bcSchristos 
299a604df28Sknakahara 	/*
300a604df28Sknakahara 	 * If oldp == NULL, the sysctl(8) caller process want to get the size of
301a604df28Sknakahara 	 * intrctl list data only.
302a604df28Sknakahara 	 */
303a604df28Sknakahara 	if (oldp == NULL) {
30496bd66bcSchristos 		*oldlenp = ilsize;
305a604df28Sknakahara 		return 0;
306a604df28Sknakahara 	}
307a604df28Sknakahara 
308a604df28Sknakahara 	/*
30996bd66bcSchristos 	 * If oldp != NULL, the sysctl(8) caller process want to get both the
31096bd66bcSchristos 	 * size and the contents of intrctl list data.
311a604df28Sknakahara 	 */
31296bd66bcSchristos 	if (*oldlenp < ilsize)
313a604df28Sknakahara 		return ENOMEM;
314a604df28Sknakahara 
31596bd66bcSchristos 	buf = kmem_zalloc(ilsize, KM_SLEEP);
31696bd66bcSchristos 	if ((error = interrupt_intrio_list(buf, ilsize)) != 0)
317a604df28Sknakahara 		goto out;
318a604df28Sknakahara 
31996bd66bcSchristos 	error = copyout(buf, oldp, ilsize);
320a604df28Sknakahara  out:
32196bd66bcSchristos 	kmem_free(buf, ilsize);
322a604df28Sknakahara 	return error;
323a604df28Sknakahara }
324a604df28Sknakahara 
325a604df28Sknakahara /*
326a604df28Sknakahara  * "intrctl affinity" entry
327a604df28Sknakahara  */
328a604df28Sknakahara static int
interrupt_set_affinity_sysctl(SYSCTLFN_ARGS)329a604df28Sknakahara interrupt_set_affinity_sysctl(SYSCTLFN_ARGS)
330a604df28Sknakahara {
331a604df28Sknakahara 	struct sysctlnode node;
332a604df28Sknakahara 	struct intrio_set *iset;
333a604df28Sknakahara 	cpuset_t *ucpuset;
334a604df28Sknakahara 	kcpuset_t *kcpuset;
335a604df28Sknakahara 	int error;
336a604df28Sknakahara 
337a604df28Sknakahara 	error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_INTR,
338a604df28Sknakahara 	    KAUTH_REQ_SYSTEM_INTR_AFFINITY, NULL, NULL, NULL);
339a604df28Sknakahara 	if (error)
340a604df28Sknakahara 		return EPERM;
341a604df28Sknakahara 
342a604df28Sknakahara 	node = *rnode;
343a604df28Sknakahara 	iset = (struct intrio_set *)node.sysctl_data;
344a604df28Sknakahara 
345a604df28Sknakahara 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
346a604df28Sknakahara 	if (error != 0 || newp == NULL)
347a604df28Sknakahara 		return error;
348a604df28Sknakahara 
349a604df28Sknakahara 	ucpuset = iset->cpuset;
350a604df28Sknakahara 	kcpuset_create(&kcpuset, true);
351a604df28Sknakahara 	error = kcpuset_copyin(ucpuset, kcpuset, iset->cpuset_size);
352a604df28Sknakahara 	if (error)
353a604df28Sknakahara 		goto out;
354a604df28Sknakahara 	if (kcpuset_iszero(kcpuset)) {
355a604df28Sknakahara 		error = EINVAL;
356a604df28Sknakahara 		goto out;
357a604df28Sknakahara 	}
358a604df28Sknakahara 
359a604df28Sknakahara 	error = interrupt_distribute_handler(iset->intrid, kcpuset, NULL);
360a604df28Sknakahara 
361a604df28Sknakahara  out:
362a604df28Sknakahara 	kcpuset_destroy(kcpuset);
363a604df28Sknakahara 	return error;
364a604df28Sknakahara }
365a604df28Sknakahara 
366a604df28Sknakahara /*
367a604df28Sknakahara  * "intrctl intr" entry
368a604df28Sknakahara  */
369a604df28Sknakahara static int
interrupt_intr_sysctl(SYSCTLFN_ARGS)370a604df28Sknakahara interrupt_intr_sysctl(SYSCTLFN_ARGS)
371a604df28Sknakahara {
372a604df28Sknakahara 	struct sysctlnode node;
373a604df28Sknakahara 	struct intrio_set *iset;
374a604df28Sknakahara 	cpuset_t *ucpuset;
375a604df28Sknakahara 	kcpuset_t *kcpuset;
376a604df28Sknakahara 	int error;
377a604df28Sknakahara 	u_int cpu_idx;
378a604df28Sknakahara 
379a604df28Sknakahara 	error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_CPU,
380a604df28Sknakahara 	    KAUTH_REQ_SYSTEM_CPU_SETSTATE, NULL, NULL, NULL);
381a604df28Sknakahara 	if (error)
382a604df28Sknakahara 		return EPERM;
383a604df28Sknakahara 
384a604df28Sknakahara 	node = *rnode;
385a604df28Sknakahara 	iset = (struct intrio_set *)node.sysctl_data;
386a604df28Sknakahara 
387a604df28Sknakahara 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
388a604df28Sknakahara 	if (error != 0 || newp == NULL)
389a604df28Sknakahara 		return error;
390a604df28Sknakahara 
391a604df28Sknakahara 	ucpuset = iset->cpuset;
392a604df28Sknakahara 	kcpuset_create(&kcpuset, true);
393a604df28Sknakahara 	error = kcpuset_copyin(ucpuset, kcpuset, iset->cpuset_size);
394a604df28Sknakahara 	if (error)
395a604df28Sknakahara 		goto out;
396a604df28Sknakahara 	if (kcpuset_iszero(kcpuset)) {
397a604df28Sknakahara 		error = EINVAL;
398a604df28Sknakahara 		goto out;
399a604df28Sknakahara 	}
400a604df28Sknakahara 
401a604df28Sknakahara 	cpu_idx = kcpuset_ffs(kcpuset) - 1; /* support one CPU only */
402a604df28Sknakahara 
403a604df28Sknakahara 	mutex_enter(&cpu_lock);
404a604df28Sknakahara 	error = interrupt_shield(cpu_idx, UNSET_NOINTR_SHIELD);
405a604df28Sknakahara 	mutex_exit(&cpu_lock);
406a604df28Sknakahara 
407a604df28Sknakahara  out:
408a604df28Sknakahara 	kcpuset_destroy(kcpuset);
409a604df28Sknakahara 	return error;
410a604df28Sknakahara }
411a604df28Sknakahara 
412a604df28Sknakahara /*
413a604df28Sknakahara  * "intrctl nointr" entry
414a604df28Sknakahara  */
415a604df28Sknakahara static int
interrupt_nointr_sysctl(SYSCTLFN_ARGS)416a604df28Sknakahara interrupt_nointr_sysctl(SYSCTLFN_ARGS)
417a604df28Sknakahara {
418a604df28Sknakahara 	struct sysctlnode node;
419a604df28Sknakahara 	struct intrio_set *iset;
420a604df28Sknakahara 	cpuset_t *ucpuset;
421a604df28Sknakahara 	kcpuset_t *kcpuset;
422a604df28Sknakahara 	int error;
423a604df28Sknakahara 	u_int cpu_idx;
424a604df28Sknakahara 
425a604df28Sknakahara 	error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_CPU,
426a604df28Sknakahara 	    KAUTH_REQ_SYSTEM_CPU_SETSTATE, NULL, NULL, NULL);
427a604df28Sknakahara 	if (error)
428a604df28Sknakahara 		return EPERM;
429a604df28Sknakahara 
430a604df28Sknakahara 	node = *rnode;
431a604df28Sknakahara 	iset = (struct intrio_set *)node.sysctl_data;
432a604df28Sknakahara 
433a604df28Sknakahara 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
434a604df28Sknakahara 	if (error != 0 || newp == NULL)
435a604df28Sknakahara 		return error;
436a604df28Sknakahara 
437a604df28Sknakahara 	ucpuset = iset->cpuset;
438a604df28Sknakahara 	kcpuset_create(&kcpuset, true);
439a604df28Sknakahara 	error = kcpuset_copyin(ucpuset, kcpuset, iset->cpuset_size);
440a604df28Sknakahara 	if (error)
441a604df28Sknakahara 		goto out;
442a604df28Sknakahara 	if (kcpuset_iszero(kcpuset)) {
443a604df28Sknakahara 		error = EINVAL;
444a604df28Sknakahara 		goto out;
445a604df28Sknakahara 	}
446a604df28Sknakahara 
447a604df28Sknakahara 	cpu_idx = kcpuset_ffs(kcpuset) - 1; /* support one CPU only */
448a604df28Sknakahara 
449a604df28Sknakahara 	mutex_enter(&cpu_lock);
450a604df28Sknakahara 	error = interrupt_shield(cpu_idx, SET_NOINTR_SHIELD);
451a604df28Sknakahara 	mutex_exit(&cpu_lock);
452a604df28Sknakahara 	if (error)
453a604df28Sknakahara 		goto out;
454a604df28Sknakahara 
455a604df28Sknakahara 	error = interrupt_avert_intr(cpu_idx);
456a604df28Sknakahara 
457a604df28Sknakahara  out:
458a604df28Sknakahara 	kcpuset_destroy(kcpuset);
459a604df28Sknakahara 	return error;
460a604df28Sknakahara }
461a604df28Sknakahara 
462a604df28Sknakahara SYSCTL_SETUP(sysctl_interrupt_setup, "sysctl interrupt setup")
463a604df28Sknakahara {
464a604df28Sknakahara 	const struct sysctlnode *node = NULL;
465a604df28Sknakahara 
466a604df28Sknakahara 	sysctl_createv(clog, 0, NULL, &node,
467a604df28Sknakahara 		       CTLFLAG_PERMANENT, CTLTYPE_NODE,
468a604df28Sknakahara 		       "intr", SYSCTL_DESCR("Interrupt options"),
469a604df28Sknakahara 		       NULL, 0, NULL, 0,
470a604df28Sknakahara 		       CTL_KERN, CTL_CREATE, CTL_EOL);
471a604df28Sknakahara 
472a604df28Sknakahara 	sysctl_createv(clog, 0, &node, NULL,
473a604df28Sknakahara 		       CTLFLAG_PERMANENT, CTLTYPE_STRUCT,
474a604df28Sknakahara 		       "list", SYSCTL_DESCR("intrctl list"),
475a604df28Sknakahara 		       interrupt_intrio_list_sysctl, 0, NULL,
476a604df28Sknakahara 		        0, CTL_CREATE, CTL_EOL);
477a604df28Sknakahara 
478a604df28Sknakahara 	sysctl_createv(clog, 0, &node, NULL,
479a604df28Sknakahara 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_STRUCT,
480a604df28Sknakahara 		       "affinity", SYSCTL_DESCR("set affinity"),
481a604df28Sknakahara 		       interrupt_set_affinity_sysctl, 0, &kintrio_set,
482a604df28Sknakahara 		       sizeof(kintrio_set), CTL_CREATE, CTL_EOL);
483a604df28Sknakahara 
484a604df28Sknakahara 	sysctl_createv(clog, 0, &node, NULL,
485a604df28Sknakahara 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_STRUCT,
486a604df28Sknakahara 		       "intr", SYSCTL_DESCR("set intr"),
487a604df28Sknakahara 		       interrupt_intr_sysctl, 0, &kintrio_set,
488a604df28Sknakahara 		       sizeof(kintrio_set), CTL_CREATE, CTL_EOL);
489a604df28Sknakahara 
490a604df28Sknakahara 	sysctl_createv(clog, 0, &node, NULL,
491a604df28Sknakahara 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_STRUCT,
492a604df28Sknakahara 		       "nointr", SYSCTL_DESCR("set nointr"),
493a604df28Sknakahara 		       interrupt_nointr_sysctl, 0, &kintrio_set,
494a604df28Sknakahara 		       sizeof(kintrio_set), CTL_CREATE, CTL_EOL);
495a604df28Sknakahara }
496