Module Name: src
Committed By: riastradh
Date: Mon Mar 28 12:38:15 UTC 2022
Modified Files:
src/sys/kern: subr_autoconf.c
src/sys/sys: device.h
Log Message:
autoconf(9): New function config_detach_commit.
When a driver's .ca_detach function has committed to detaching -- it
definitely won't back out with EBUSY, for instance -- it can call
this to wake all pending calls to device_lookup_acquire and make them
fail immediately.
This is necessary to break a deadlock if the device_lookup_acquire
calls happen inside I/O operations which the driver's .ca_detach
function waits for the completion of -- without config_detach_commit,
I/O operations would be stuck in device_lookup_acquire waiting for
.ca_detach and .ca_detach would be stuck waiting for I/O operations
to return.
Most drivers won't need to call this: for autoconf drivers used the
traditional way by devsw for userland device nodes, the .ca_detach
routine uses vdevgone, and we will arrange to make vdevgone call
config_detach_commit automagically in such drivers anyway.
XXX kernel ABI change to struct device requires bump -- later change
will make struct device opaque to ABI, but we're not there yet
To generate a diff of this commit:
cvs rdiff -u -r1.298 -r1.299 src/sys/kern/subr_autoconf.c
cvs rdiff -u -r1.180 -r1.181 src/sys/sys/device.h
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/kern/subr_autoconf.c
diff -u src/sys/kern/subr_autoconf.c:1.298 src/sys/kern/subr_autoconf.c:1.299
--- src/sys/kern/subr_autoconf.c:1.298 Mon Mar 28 12:33:41 2022
+++ src/sys/kern/subr_autoconf.c Mon Mar 28 12:38:15 2022
@@ -1,4 +1,4 @@
-/* $NetBSD: subr_autoconf.c,v 1.298 2022/03/28 12:33:41 riastradh Exp $ */
+/* $NetBSD: subr_autoconf.c,v 1.299 2022/03/28 12:38:15 riastradh Exp $ */
/*
* Copyright (c) 1996, 2000 Christopher G. Demetriou
@@ -77,7 +77,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: subr_autoconf.c,v 1.298 2022/03/28 12:33:41 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: subr_autoconf.c,v 1.299 2022/03/28 12:38:15 riastradh Exp $");
#ifdef _KERNEL_OPT
#include "opt_ddb.h"
@@ -2039,10 +2039,17 @@ config_detach(device_t dev, int flags)
* If it was possible to detach the device, ensure that the
* device is deactivated.
*/
- if (rv == 0)
- dev->dv_flags &= ~DVF_ACTIVE;
- else if ((flags & DETACH_FORCE) == 0) {
- /* Detach failed -- likely EBUSY. */
+ if (rv == 0) {
+ config_detach_commit(dev);
+ dev->dv_flags &= ~DVF_ACTIVE; /* XXXSMP */
+ } else if ((flags & DETACH_FORCE) == 0) {
+ /*
+ * Detach failed -- likely EBUSY. Driver must not have
+ * called config_detach_commit.
+ */
+ KASSERTMSG(!dev->dv_detached,
+ "%s committed to detaching and then backed out",
+ device_xname(dev));
goto out;
} else {
panic("config_detach: forced detach of %s failed (%d)",
@@ -2059,7 +2066,8 @@ config_detach(device_t dev, int flags)
* responsibility of .ca_detach to ensure anything with open
* references will be interrupted and release them promptly,
* not block indefinitely. All new attempts to acquire
- * references will block until dv_detaching clears.
+ * references will fail, as config_detach_commit has arranged
+ * by now.
*/
mutex_enter(&config_misc_lock);
localcount_drain(dev->dv_localcount,
@@ -2133,6 +2141,30 @@ out:
return rv;
}
+/*
+ * config_detach_commit(dev)
+ *
+ * Issued by a driver's .ca_detach routine to notify anyone
+ * waiting in device_lookup_acquire that the driver is committed
+ * to detaching the device, which allows device_lookup_acquire to
+ * wake up and fail immediately.
+ *
+ * Safe to call multiple times -- idempotent. Must be called
+ * during config_detach_enter/exit. Safe to use with
+ * device_lookup because the device is not actually removed from
+ * the table until after config_detach_exit.
+ */
+void
+config_detach_commit(device_t dev)
+{
+
+ mutex_enter(&config_misc_lock);
+ KASSERT(dev->dv_detaching == curlwp);
+ dev->dv_detached = true;
+ cv_broadcast(&config_misc_cv);
+ mutex_exit(&config_misc_lock);
+}
+
int
config_detach_children(device_t parent, int flags)
{
@@ -2640,7 +2672,8 @@ device_lookup_acquire(cfdriver_t cd, int
mutex_enter(&alldevs_lock);
retry: if (unit < 0 || unit >= cd->cd_ndevs ||
(dv = cd->cd_devs[unit]) == NULL ||
- dv->dv_del_gen != 0) {
+ dv->dv_del_gen != 0 ||
+ dv->dv_detached) {
dv = NULL;
} else {
/*
Index: src/sys/sys/device.h
diff -u src/sys/sys/device.h:1.180 src/sys/sys/device.h:1.181
--- src/sys/sys/device.h:1.180 Mon Mar 28 12:33:41 2022
+++ src/sys/sys/device.h Mon Mar 28 12:38:15 2022
@@ -1,4 +1,4 @@
-/* $NetBSD: device.h,v 1.180 2022/03/28 12:33:41 riastradh Exp $ */
+/* $NetBSD: device.h,v 1.181 2022/03/28 12:38:15 riastradh Exp $ */
/*
* Copyright (c) 2021 The NetBSD Foundation, Inc.
@@ -281,6 +281,7 @@ struct device {
struct lwp *dv_attaching; /* thread not yet finished in attach */
struct lwp *dv_detaching; /* detach lock (config_misc_lock/cv) */
+ bool dv_detached; /* config_misc_lock */
size_t dv_activity_count;
void (**dv_activity_handlers)(device_t, devactive_t);
@@ -631,6 +632,7 @@ device_t config_attach_pseudo(cfdata_t);
int config_detach(device_t, int);
int config_detach_children(device_t, int flags);
+void config_detach_commit(device_t);
bool config_detach_all(int);
int config_deactivate(device_t);
void config_defer(device_t, void (*)(device_t));