ithread: Allow some ithreads to sleep

Some ithreads need to hold a sleep mutex, e.g. when calling ACPI
methods. Allow ithreads to be marked as sleepable when this is known
to be safe.

Reviewed by:	markj, jhb
Sponsored by:	Arm Ltd
Differential Revision:	https://reviews.freebsd.org/D48283
This commit is contained in:
Andrew Turner 2025-01-13 05:35:27 +00:00
parent ca81304f7c
commit 0863dc1035
4 changed files with 24 additions and 6 deletions

View file

@ -27,7 +27,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd October 30, 2022
.Dd January 24, 2025
.Dt INTR_EVENT 9
.Os
.Sh NAME
@ -295,6 +295,14 @@ from the handler's source triggers.
Presently, the
.Dv INTR_ENTROPY
flag is not valid for software interrupt handlers.
The
.Dv INTR_SLEEPABLE
flag specifies that the interrupt ithread may sleep.
Presently, the
.Dv INTR_SLEEPABLE
flag requires the
.Dv INTR_EXCL
flag to be set.
.Ss Handler Callbacks
Each
.Vt struct intr_event

View file

@ -610,6 +610,12 @@ intr_event_add_handler(struct intr_event *ie, const char *name,
if (ie == NULL || name == NULL || (handler == NULL && filter == NULL))
return (EINVAL);
if ((flags & INTR_SLEEPABLE) != 0 && (flags & INTR_EXCL) == 0) {
printf("%s: INTR_SLEEPABLE requires INTR_EXCL to be set\n",
__func__);
return (EINVAL);
}
/* Allocate and populate an interrupt handler structure. */
ih = malloc(sizeof(struct intr_handler), M_ITHREAD, M_WAITOK | M_ZERO);
ih->ih_filter = filter;
@ -627,16 +633,18 @@ intr_event_add_handler(struct intr_event *ie, const char *name,
if (flags & INTR_TYPE_NET)
ih->ih_flags |= IH_NET;
/* We can only have one exclusive handler in a event. */
/* We can only have one exclusive or sleepable handler in a event. */
mtx_lock(&ie->ie_lock);
if (!CK_SLIST_EMPTY(&ie->ie_handlers)) {
if ((flags & INTR_EXCL) ||
if ((flags & (INTR_EXCL | INTR_SLEEPABLE)) ||
(CK_SLIST_FIRST(&ie->ie_handlers)->ih_flags & IH_EXCLUSIVE)) {
mtx_unlock(&ie->ie_lock);
free(ih, M_ITHREAD);
return (EINVAL);
}
}
if (flags & INTR_SLEEPABLE)
ie->ie_flags |= IE_SLEEPABLE;
/* Create a thread if we need one. */
while (ie->ie_thread == NULL && handler != NULL) {
@ -1190,11 +1198,11 @@ static void
ithread_execute_handlers(struct proc *p, struct intr_event *ie)
{
/* Interrupt handlers should not sleep. */
if (!(ie->ie_flags & IE_SOFT))
/* Only specifically marked sleepable interrupt handlers can sleep. */
if (!(ie->ie_flags & (IE_SOFT | IE_SLEEPABLE)))
THREAD_NO_SLEEPING();
intr_event_execute_handlers(p, ie);
if (!(ie->ie_flags & IE_SOFT))
if (!(ie->ie_flags & (IE_SOFT | IE_SLEEPABLE)))
THREAD_SLEEPING_OK();
/*

View file

@ -277,6 +277,7 @@ enum intr_type {
INTR_EXCL = 256, /* exclusive interrupt */
INTR_MPSAFE = 512, /* this interrupt is SMP safe */
INTR_ENTROPY = 1024, /* this interrupt provides entropy */
INTR_SLEEPABLE = 2048, /* this interrupt handler can sleep */
INTR_MD1 = 4096, /* flag reserved for MD use */
INTR_MD2 = 8192, /* flag reserved for MD use */
INTR_MD3 = 16384, /* flag reserved for MD use */

View file

@ -129,6 +129,7 @@ struct intr_event {
/* Interrupt event flags kept in ie_flags. */
#define IE_SOFT 0x000001 /* Software interrupt. */
#define IE_SLEEPABLE 0x000002 /* Sleepable ithread */
#define IE_ADDING_THREAD 0x000004 /* Currently building an ithread. */
/* Flags to pass to swi_sched. */