Module Name: src Committed By: riastradh Date: Sat Jun 12 12:13:51 UTC 2021
Modified Files: src/sys/kern: subr_autoconf.c src/sys/sys: device.h Log Message: 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. To generate a diff of this commit: cvs rdiff -u -r1.282 -r1.283 src/sys/kern/subr_autoconf.c cvs rdiff -u -r1.170 -r1.171 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.282 src/sys/kern/subr_autoconf.c:1.283 --- src/sys/kern/subr_autoconf.c:1.282 Sat Jun 12 12:12:11 2021 +++ src/sys/kern/subr_autoconf.c Sat Jun 12 12:13:51 2021 @@ -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); + config_pending_decr(dev); - if (((dev->dv_flags & DVF_ATTACH_INPROGRESS) == 0) - && !device_pmf_is_registered(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); } Index: src/sys/sys/device.h diff -u src/sys/sys/device.h:1.170 src/sys/sys/device.h:1.171 --- src/sys/sys/device.h:1.170 Mon May 10 13:59:30 2021 +++ src/sys/sys/device.h Sat Jun 12 12:13:51 2021 @@ -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);