Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys autoconf(9): New function config_detach_commit.



details:   https://anonhg.NetBSD.org/src/rev/3e4a95c8adcf
branches:  trunk
changeset: 364522:3e4a95c8adcf
user:      riastradh <riastradh%NetBSD.org@localhost>
date:      Mon Mar 28 12:38:15 2022 +0000

description:
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

diffstat:

 sys/kern/subr_autoconf.c |  49 ++++++++++++++++++++++++++++++++++++++++-------
 sys/sys/device.h         |   4 ++-
 2 files changed, 44 insertions(+), 9 deletions(-)

diffs (116 lines):

diff -r 8652f02d3f00 -r 3e4a95c8adcf sys/kern/subr_autoconf.c
--- a/sys/kern/subr_autoconf.c  Mon Mar 28 12:38:04 2022 +0000
+++ b/sys/kern/subr_autoconf.c  Mon Mar 28 12:38:15 2022 +0000
@@ -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 @@
         * 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 @@
         * 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 @@
        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 @@
        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 {
                /*
diff -r 8652f02d3f00 -r 3e4a95c8adcf sys/sys/device.h
--- a/sys/sys/device.h  Mon Mar 28 12:38:04 2022 +0000
+++ b/sys/sys/device.h  Mon Mar 28 12:38:15 2022 +0000
@@ -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 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 @@
 
 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));



Home | Main Index | Thread Index | Old Index