Source-Changes-HG archive

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

[src/trunk]: src/sys autoconf(9): Prevent concurrent attach/detach and detach...



details:   https://anonhg.NetBSD.org/src/rev/de94566b349b
branches:  trunk
changeset: 379614:de94566b349b
user:      riastradh <riastradh%NetBSD.org@localhost>
date:      Sat Jun 12 12:13:51 2021 +0000

description:
autoconf(9): Prevent concurrent attach/detach and detach/detach.

- Use config_pending_incr/decr around attach.  No need for separate
  flag indicating device is still attaching.  (dv_flags locking is also
  incoherent.)

- Any config_pending now blocks config_detach.

- New dv_detaching exclusive lock for config_detach.

diffstat:

 sys/kern/subr_autoconf.c |  72 +++++++++++++++++++++++++++++++++++++++++------
 sys/sys/device.h         |   4 ++-
 2 files changed, 65 insertions(+), 11 deletions(-)

diffs (180 lines):

diff -r 074b245e238e -r de94566b349b sys/kern/subr_autoconf.c
--- a/sys/kern/subr_autoconf.c  Sat Jun 12 12:13:23 2021 +0000
+++ b/sys/kern/subr_autoconf.c  Sat Jun 12 12:13:51 2021 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: subr_autoconf.c,v 1.282 2021/06/12 12:12:11 riastradh Exp $ */
+/* $NetBSD: subr_autoconf.c,v 1.283 2021/06/12 12:13:51 riastradh Exp $ */
 
 /*
  * Copyright (c) 1996, 2000 Christopher G. Demetriou
@@ -79,7 +79,7 @@
 #define        __SUBR_AUTOCONF_PRIVATE /* see <sys/device.h> */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: subr_autoconf.c,v 1.282 2021/06/12 12:12:11 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: subr_autoconf.c,v 1.283 2021/06/12 12:13:51 riastradh Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_ddb.h"
@@ -471,7 +471,6 @@ config_interrupts_thread(void *cookie)
                kmem_free(dc, sizeof(*dc));
 
                mutex_enter(&config_misc_lock);
-               dev->dv_flags &= ~DVF_ATTACH_INPROGRESS;
        }
        mutex_exit(&config_misc_lock);
 
@@ -1721,6 +1720,7 @@ config_vattach(device_t parent, cfdata_t
        device_t dev;
        struct cftable *ct;
        const char *drvname;
+       bool deferred;
 
        KASSERT(KERNEL_LOCKED_P());
 
@@ -1776,10 +1776,15 @@ config_vattach(device_t parent, cfdata_t
        /* Let userland know */
        devmon_report_device(dev, true);
 
+       config_pending_incr(dev);
        (*dev->dv_cfattach->ca_attach)(parent, dev, aux);
-
-       if (((dev->dv_flags & DVF_ATTACH_INPROGRESS) == 0)
-           && !device_pmf_is_registered(dev))
+       config_pending_decr(dev);
+
+       mutex_enter(&config_misc_lock);
+       deferred = (dev->dv_pending != 0);
+       mutex_exit(&config_misc_lock);
+
+       if (!deferred && !device_pmf_is_registered(dev))
                aprint_debug_dev(dev,
                    "WARNING: power management not supported\n");
 
@@ -1841,7 +1846,9 @@ config_attach_pseudo(cfdata_t cf)
        /* Let userland know */
        devmon_report_device(dev, true);
 
+       config_pending_incr(dev);
        (*dev->dv_cfattach->ca_attach)(ROOT, dev, NULL);
+       config_pending_decr(dev);
 
        config_process_deferred(&deferred_config_queue, dev);
        return dev;
@@ -1884,6 +1891,41 @@ config_dump_garbage(struct devicelist *g
        }
 }
 
+static int
+config_detach_enter(device_t dev)
+{
+       int error;
+
+       mutex_enter(&config_misc_lock);
+       for (;;) {
+               if (dev->dv_pending == 0 && dev->dv_detaching == NULL) {
+                       dev->dv_detaching = curlwp;
+                       error = 0;
+                       break;
+               }
+               KASSERTMSG(dev->dv_detaching != curlwp,
+                   "recursively detaching %s", device_xname(dev));
+               error = cv_wait_sig(&config_misc_cv, &config_misc_lock);
+               if (error)
+                       break;
+       }
+       KASSERT(error || dev->dv_detaching == curlwp);
+       mutex_exit(&config_misc_lock);
+
+       return error;
+}
+
+static void
+config_detach_exit(device_t dev)
+{
+
+       mutex_enter(&config_misc_lock);
+       KASSERT(dev->dv_detaching == curlwp);
+       dev->dv_detaching = NULL;
+       cv_broadcast(&config_misc_cv);
+       mutex_exit(&config_misc_lock);
+}
+
 /*
  * Detach a device.  Optionally forced (e.g. because of hardware
  * removal) and quiet.  Returns zero if successful, non-zero
@@ -1918,6 +1960,14 @@ config_detach(device_t dev, int flags)
        ca = dev->dv_cfattach;
        KASSERT(ca != NULL);
 
+       /*
+        * Only one detach at a time, please -- and not until fully
+        * attached.
+        */
+       rv = config_detach_enter(dev);
+       if (rv)
+               return rv;
+
        mutex_enter(&alldevs_lock);
        if (dev->dv_del_gen != 0) {
                mutex_exit(&alldevs_lock);
@@ -1925,6 +1975,7 @@ config_detach(device_t dev, int flags)
                printf("%s: %s is already detached\n", __func__,
                    device_xname(dev));
 #endif /* DIAGNOSTIC */
+               config_detach_exit(dev);
                return ENOENT;
        }
        alldevs_nwrite++;
@@ -2004,6 +2055,8 @@ config_detach(device_t dev, int flags)
                aprint_normal_dev(dev, "detached\n");
 
 out:
+       config_detach_exit(dev);
+
        config_alldevs_enter(&af);
        KASSERT(alldevs_nwrite != 0);
        --alldevs_nwrite;
@@ -2204,7 +2257,6 @@ config_interrupts(device_t dev, void (*f
        dc->dc_dev = dev;
        dc->dc_func = func;
        TAILQ_INSERT_TAIL(&interrupt_config_queue, dc, dc_queue);
-       dev->dv_flags |= DVF_ATTACH_INPROGRESS;
        mutex_exit(&config_misc_lock);
 }
 
@@ -2298,13 +2350,13 @@ config_pending_decr(device_t dev)
        mutex_enter(&config_misc_lock);
        KASSERTMSG(dev->dv_pending > 0,
            "%s: excess config_pending_decr", device_xname(dev));
-       if (--dev->dv_pending == 0)
+       if (--dev->dv_pending == 0) {
                TAILQ_REMOVE(&config_pending, dev, dv_pending_list);
+               cv_broadcast(&config_misc_cv);
+       }
 #ifdef DEBUG_AUTOCONF
        printf("%s: %s %d\n", __func__, device_xname(dev), dev->dv_pending);
 #endif
-       if (TAILQ_EMPTY(&config_pending))
-               cv_broadcast(&config_misc_cv);
        mutex_exit(&config_misc_lock);
 }
 
diff -r 074b245e238e -r de94566b349b sys/sys/device.h
--- a/sys/sys/device.h  Sat Jun 12 12:13:23 2021 +0000
+++ b/sys/sys/device.h  Sat Jun 12 12:13:51 2021 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: device.h,v 1.170 2021/05/10 13:59:30 thorpej Exp $ */
+/* $NetBSD: device.h,v 1.171 2021/06/12 12:13:51 riastradh Exp $ */
 
 /*
  * Copyright (c) 2021 The NetBSD Foundation, Inc.
@@ -274,6 +274,8 @@ struct device {
        int             dv_pending;     /* config_pending count */
        TAILQ_ENTRY(device) dv_pending_list;
 
+       struct lwp      *dv_detaching;  /* detach lock (config_misc_lock/cv) */
+
        size_t          dv_activity_count;
        void            (**dv_activity_handlers)(device_t, devactive_t);
 



Home | Main Index | Thread Index | Old Index