/* $OpenBSD: kern_softintr.c,v 1.1 2025/04/23 15:07:00 visa Exp $ */ /* * Copyright (c) 2021, 2025 Visa Hankala * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #ifdef __USE_MI_SOFTINTR struct softintr_hand { TAILQ_ENTRY(softintr_hand) sih_q; void (*sih_fn)(void *); void *sih_arg; struct cpu_info *sih_runner; int sih_level; unsigned short sih_flags; unsigned short sih_state; }; #define SIF_MPSAFE 0x0001 #define SIS_DYING 0x0001 #define SIS_PENDING 0x0002 #define SIS_RESTART 0x0004 TAILQ_HEAD(softintr_queue, softintr_hand); struct softintr_queue softintr_queue[NSOFTINTR]; struct mutex softintr_lock = MUTEX_INITIALIZER(IPL_HIGH); void softintr_init(void) { int i; for (i = 0; i < nitems(softintr_queue); i++) TAILQ_INIT(&softintr_queue[i]); } void softintr_dispatch(int level) { struct cpu_info *ci = curcpu(); struct softintr_queue *queue = &softintr_queue[level]; struct softintr_hand *sih; mtx_enter(&softintr_lock); while ((sih = TAILQ_FIRST(queue)) != NULL) { KASSERT((sih->sih_state & (SIS_PENDING | SIS_RESTART)) == SIS_PENDING); KASSERT(sih->sih_runner == NULL); sih->sih_state &= ~SIS_PENDING; TAILQ_REMOVE(queue, sih, sih_q); sih->sih_runner = ci; mtx_leave(&softintr_lock); if (sih->sih_flags & SIF_MPSAFE) { (*sih->sih_fn)(sih->sih_arg); } else { KERNEL_LOCK(); (*sih->sih_fn)(sih->sih_arg); KERNEL_UNLOCK(); } mtx_enter(&softintr_lock); KASSERT((sih->sih_state & SIS_PENDING) == 0); sih->sih_runner = NULL; if (sih->sih_state & SIS_RESTART) { TAILQ_INSERT_TAIL(queue, sih, sih_q); sih->sih_state |= SIS_PENDING; sih->sih_state &= ~SIS_RESTART; } uvmexp.softs++; } mtx_leave(&softintr_lock); } void * softintr_establish(int ipl, void (*func)(void *), void *arg) { struct softintr_hand *sih; int level; unsigned short flags = 0; if (ipl & IPL_MPSAFE) flags |= SIF_MPSAFE; ipl &= ~IPL_MPSAFE; switch (ipl) { #ifdef IPL_SOFT case IPL_SOFT: #endif case IPL_SOFTCLOCK: level = SOFTINTR_CLOCK; break; case IPL_SOFTNET: level = SOFTINTR_NET; break; case IPL_TTY: case IPL_SOFTTTY: level = SOFTINTR_TTY; break; default: panic("softintr_establish: unhandled ipl %d", ipl); } sih = malloc(sizeof(*sih), M_DEVBUF, M_NOWAIT); if (sih != NULL) { sih->sih_fn = func; sih->sih_arg = arg; sih->sih_runner = NULL; sih->sih_level = level; sih->sih_flags = flags; sih->sih_state = 0; } return (sih); } void softintr_disestablish(void *arg) { struct cpu_info *runner; struct softintr_hand *sih = arg; assertwaitok(); mtx_enter(&softintr_lock); sih->sih_state |= SIS_DYING; sih->sih_state &= ~SIS_RESTART; if (sih->sih_state & SIS_PENDING) { sih->sih_state &= ~SIS_PENDING; TAILQ_REMOVE(&softintr_queue[sih->sih_level], sih, sih_q); } runner = sih->sih_runner; mtx_leave(&softintr_lock); if (runner != NULL) sched_barrier(runner); KASSERT((sih->sih_state & (SIS_PENDING | SIS_RESTART)) == 0); KASSERT(sih->sih_runner == NULL); free(sih, M_DEVBUF, sizeof(*sih)); } void softintr_schedule(void *arg) { struct softintr_hand *sih = arg; struct softintr_queue *queue = &softintr_queue[sih->sih_level]; mtx_enter(&softintr_lock); KASSERT((sih->sih_state & SIS_DYING) == 0); if (sih->sih_runner == NULL) { KASSERT((sih->sih_state & SIS_RESTART) == 0); if ((sih->sih_state & SIS_PENDING) == 0) { TAILQ_INSERT_TAIL(queue, sih, sih_q); sih->sih_state |= SIS_PENDING; /* Call softintr() while SPL is still at IPL_HIGH. */ softintr(sih->sih_level); } } else { KASSERT((sih->sih_state & SIS_PENDING) == 0); sih->sih_state |= SIS_RESTART; } mtx_leave(&softintr_lock); } #endif /* __USE_MI_SOFTINTR */