Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 1 Dec 2024 04:58:11 GMT
From:      John Baldwin <jhb@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org
Subject:   git: 9ee1bb61d76a - stable/13 - new-bus: Fix some shortcomings in disabling devices via hints
Message-ID:  <202412010458.4B14wBPY098157@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch stable/13 has been updated by jhb:

URL: https://cgit.FreeBSD.org/src/commit/?id=9ee1bb61d76a9a74983ae13985b66485d16f49dd

commit 9ee1bb61d76a9a74983ae13985b66485d16f49dd
Author:     John Baldwin <jhb@FreeBSD.org>
AuthorDate: 2024-11-23 16:39:02 +0000
Commit:     John Baldwin <jhb@FreeBSD.org>
CommitDate: 2024-11-30 21:51:22 +0000

    new-bus: Fix some shortcomings in disabling devices via hints
    
    A device can be disabled via a hint after it is probed (but before it
    is attached).  The initial version of this marked the device disabled,
    but left the device "alive" meaning that dev->driver and dev->desc
    were untouched and still pointed into the driver that probed the
    device.  If that driver lives in a kernel module that is later
    unloaded, device_detach() called from devclass_delete_driver() doesn't
    do anything (the device's state is DS_ALIVE).  In particular, it
    doesn't call device_set_driver(dev, NULL) to disassociate the device
    from the driver that is being unloaded.
    
    There are several places where these stale pointers can be tripped
    over.  After kldunload, invoking the sysctl to fetch device info can
    dereference dev->desc and dev->driver causing panics.  Even without
    kldunload, a system suspend request will call the device_suspend and
    device_resume DEVMETHODs of the driver in question even though the
    device is not attached which can cause some excitement.
    
    To clean this up, more fully detach a device that is disabled by a
    hint by clearing the driver and setting the state to DS_NOTPRESENT.
    However, to keep the device name+unit combination reserved, leave the
    device attached to its devclass.
    
    This requires a change to 'devctl enable' handling to deal with this
    updated state.  It now checks for a non-NULL devclass to determine if
    a disabled device is in this state and if so it clears the hint.
    However, it also now clears the devclass before attaching the device.
    This gives all drivers an opportunity to attach to the now-enabled
    device.
    
    Reported by:    adrian
    Discussed with: imp
    Reviewed by:    imp
    MFC after:      1 week
    Differential Revision:  https://reviews.freebsd.org/D47691
    
    (cherry picked from commit b4c700fa7cca7d3e4da84dd417fc3efc42788a52)
---
 sys/kern/subr_bus.c | 21 +++++++++++++++------
 1 file changed, 15 insertions(+), 6 deletions(-)

diff --git a/sys/kern/subr_bus.c b/sys/kern/subr_bus.c
index aec5b4c615fd..0add4e019043 100644
--- a/sys/kern/subr_bus.c
+++ b/sys/kern/subr_bus.c
@@ -3018,7 +3018,13 @@ device_attach(device_t dev)
 	int error;
 
 	if (resource_disabled(dev->driver->name, dev->unit)) {
+		/*
+		 * Mostly detach the device, but leave it attached to
+		 * the devclass to reserve the name and unit.
+		 */
 		device_disable(dev);
+		(void)device_set_driver(dev, NULL);
+		dev->state = DS_NOTPRESENT;
 		if (bootverbose)
 			 device_printf(dev, "disabled via hints entry\n");
 		return (ENXIO);
@@ -5858,17 +5864,20 @@ devctl2_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
 		 * attach the device rather than doing a full probe.
 		 */
 		device_enable(dev);
-		if (device_is_alive(dev)) {
+		if (dev->devclass != NULL) {
 			/*
 			 * If the device was disabled via a hint, clear
 			 * the hint.
 			 */
-			if (resource_disabled(dev->driver->name, dev->unit))
-				resource_unset_value(dev->driver->name,
+			if (resource_disabled(dev->devclass->name, dev->unit))
+				resource_unset_value(dev->devclass->name,
 				    dev->unit, "disabled");
-			error = device_attach(dev);
-		} else
-			error = device_probe_and_attach(dev);
+
+			/* Allow any drivers to rebid. */
+			if (!(dev->flags & DF_FIXEDCLASS))
+				devclass_delete_device(dev->devclass, dev);
+		}
+		error = device_probe_and_attach(dev);
 		break;
 	case DEV_DISABLE:
 		if (!device_is_enabled(dev)) {



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202412010458.4B14wBPY098157>