Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 23 Aug 2018 05:05:47 +0000 (UTC)
From:      Warner Losh <imp@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r338233 - in head: lib/libdevctl sys/kern sys/sys usr.sbin/devctl
Message-ID:  <201808230505.w7N55lsc062765@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: imp
Date: Thu Aug 23 05:05:47 2018
New Revision: 338233
URL: https://svnweb.freebsd.org/changeset/base/338233

Log:
  Create devctl freeze/thaw.
  
  This adds it to devctl, libdevctl, defines the two IOCTLs and
  implements the kernel bits. causes any new drivers that are added via
  kldload to be deferred until a 'thaw' comes in. These do not stack: it
  is an error to freeze while frozen, or thaw while thawed.
  
  Differential Revision: https://reviews.freebsd.org/D16735

Modified:
  head/lib/libdevctl/devctl.3
  head/lib/libdevctl/devctl.c
  head/lib/libdevctl/devctl.h
  head/sys/kern/subr_bus.c
  head/sys/sys/bus.h
  head/usr.sbin/devctl/devctl.c

Modified: head/lib/libdevctl/devctl.3
==============================================================================
--- head/lib/libdevctl/devctl.3	Thu Aug 23 02:26:40 2018	(r338232)
+++ head/lib/libdevctl/devctl.3	Thu Aug 23 05:05:47 2018	(r338233)
@@ -36,10 +36,12 @@
 .Nm devctl_detach ,
 .Nm devctl_disable ,
 .Nm devctl_enable ,
+.Nm devctl_freeze ,
 .Nm devctl_rescan ,
 .Nm devctl_resume ,
 .Nm devctl_set_driver ,
-.Nm devctl_suspend
+.Nm devctl_suspend ,
+.Nm devctl_thaw
 .Nd device control library
 .Sh LIBRARY
 .Lb libdevctl
@@ -58,6 +60,8 @@
 .Ft int
 .Fn devctl_enable "const char *device"
 .Ft int
+.Fn devctl_freeze "void"
+.Ft int
 .Fn devctl_rescan "const char *device"
 .Ft int
 .Fn devctl_resume "const char *device"
@@ -65,6 +69,8 @@
 .Fn devctl_set_driver "const char *device" "const char *driver" "bool force"
 .Ft int
 .Fn devctl_suspend "const char *device"
+.Ft int
+.Fn devctl_thaw "void"
 .Sh DESCRIPTION
 The
 .Nm
@@ -189,6 +195,16 @@ The
 .Fn devctl_rescan
 function rescans a bus device checking for devices that have been added or
 removed.
+.Pp
+The
+.Fn devctl_freeze
+function freezes probe and attach processing initiated in response to
+drivers being loaded.
+.Pp
+The
+.Fn devctl_thaw
+function resumes (thaws the freeze) probe and attach processing
+initiated in response to drivers being loaded.
 .Sh RETURN VALUES
 .Rv -std devctl_attach devctl_clear_driver devctl_delete devctl_detach \
 devctl_disable devctl_enable devctl_suspend devctl_rescan devctl_resume \

Modified: head/lib/libdevctl/devctl.c
==============================================================================
--- head/lib/libdevctl/devctl.c	Thu Aug 23 02:26:40 2018	(r338232)
+++ head/lib/libdevctl/devctl.c	Thu Aug 23 05:05:47 2018	(r338233)
@@ -145,3 +145,17 @@ devctl_delete(const char *device, bool force)
 	return (devctl_simple_request(DEV_DELETE, device, force ?
 	    DEVF_FORCE_DELETE : 0));
 }
+
+int
+devctl_freeze(void)
+{
+
+	return (devctl_simple_request(DEV_FREEZE, "", 0));
+}
+
+int
+devctl_thaw(void)
+{
+
+	return (devctl_simple_request(DEV_THAW, "", 0));
+}

Modified: head/lib/libdevctl/devctl.h
==============================================================================
--- head/lib/libdevctl/devctl.h	Thu Aug 23 02:26:40 2018	(r338232)
+++ head/lib/libdevctl/devctl.h	Thu Aug 23 05:05:47 2018	(r338233)
@@ -41,5 +41,7 @@ int	devctl_set_driver(const char *device, const char *
 int	devctl_clear_driver(const char *device, bool force);
 int	devctl_rescan(const char *device);
 int	devctl_delete(const char *device, bool force);
+int	devctl_freeze(void);
+int	devctl_thaw(void);
 
 #endif /* !__DEVCTL_H__ */

Modified: head/sys/kern/subr_bus.c
==============================================================================
--- head/sys/kern/subr_bus.c	Thu Aug 23 02:26:40 2018	(r338232)
+++ head/sys/kern/subr_bus.c	Thu Aug 23 05:05:47 2018	(r338233)
@@ -82,6 +82,8 @@ struct driverlink {
 	kobj_class_t	driver;
 	TAILQ_ENTRY(driverlink) link;	/* list of drivers in devclass */
 	int		pass;
+	int		flags;
+#define DL_DEFERRED_PROBE	1	/* Probe deferred on this */
 	TAILQ_ENTRY(driverlink) passlink;
 };
 
@@ -152,6 +154,7 @@ EVENTHANDLER_LIST_DEFINE(device_detach);
 EVENTHANDLER_LIST_DEFINE(dev_lookup);
 
 static void devctl2_init(void);
+static bool device_frozen;
 
 #define DRIVERNAME(d)	((d)? d->name : "no driver")
 #define DEVCLANAME(d)	((d)? d->name : "no devclass")
@@ -1168,7 +1171,11 @@ devclass_add_driver(devclass_t dc, driver_t *driver, i
 	dl->pass = pass;
 	driver_register_pass(dl);
 
-	devclass_driver_added(dc, driver);
+	if (device_frozen) {
+		dl->flags |= DL_DEFERRED_PROBE;
+	} else {
+		devclass_driver_added(dc, driver);
+	}
 	bus_data_generation_update();
 	return (0);
 }
@@ -1208,6 +1215,9 @@ devclass_driver_deleted(devclass_t busclass, devclass_
 	 * Note that since a driver can be in multiple devclasses, we
 	 * should not detach devices which are not children of devices in
 	 * the affected devclass.
+	 *
+	 * If we're frozen, we don't generate NOMATCH events. Mark to
+	 * generate later.
 	 */
 	for (i = 0; i < dc->maxunit; i++) {
 		if (dc->devices[i]) {
@@ -1216,9 +1226,14 @@ devclass_driver_deleted(devclass_t busclass, devclass_
 			    dev->parent->devclass == busclass) {
 				if ((error = device_detach(dev)) != 0)
 					return (error);
-				BUS_PROBE_NOMATCH(dev->parent, dev);
-				devnomatch(dev);
-				dev->flags |= DF_DONENOMATCH;
+				if (device_frozen) {
+					dev->flags &= ~DF_DONENOMATCH;
+					dev->flags |= DF_NEEDNOMATCH;
+				} else {
+					BUS_PROBE_NOMATCH(dev->parent, dev);
+					devnomatch(dev);
+					dev->flags |= DF_DONENOMATCH;
+				}
 			}
 		}
 	}
@@ -5406,6 +5421,53 @@ driver_exists(device_t bus, const char *driver)
 	return (false);
 }
 
+static void
+device_gen_nomatch(device_t dev)
+{
+	device_t child;
+
+	if (dev->flags & DF_NEEDNOMATCH &&
+	    dev->state == DS_NOTPRESENT) {
+		BUS_PROBE_NOMATCH(dev->parent, dev);
+		devnomatch(dev);
+		dev->flags |= DF_DONENOMATCH;
+	}
+	dev->flags &= ~DF_NEEDNOMATCH;
+	TAILQ_FOREACH(child, &dev->children, link) {
+		device_gen_nomatch(child);
+	}
+}
+
+static void
+device_do_deferred_actions(void)
+{
+	devclass_t dc;
+	driverlink_t dl;
+
+	/*
+	 * Walk through the devclasses to find all the drivers we've tagged as
+	 * deferred during the freeze and call the driver added routines. They
+	 * have already been added to the lists in the background, so the driver
+	 * added routines that trigger a probe will have all the right bidders
+	 * for the probe auction.
+	 */
+	TAILQ_FOREACH(dc, &devclasses, link) {
+		TAILQ_FOREACH(dl, &dc->drivers, link) {
+			if (dl->flags & DL_DEFERRED_PROBE) {
+				devclass_driver_added(dc, dl->driver);
+				dl->flags &= ~DL_DEFERRED_PROBE;
+			}
+		}
+	}
+
+	/*
+	 * We also defer no-match events during a freeze. Walk the tree and
+	 * generate all the pent-up events that are still relevant.
+	 */
+	device_gen_nomatch(root_bus);
+	bus_data_generation_update();
+}
+
 static int
 devctl2_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
     struct thread *td)
@@ -5432,6 +5494,10 @@ devctl2_ioctl(struct cdev *cdev, u_long cmd, caddr_t d
 		if (error == 0)
 			error = find_device(req, &dev);
 		break;
+	case DEV_FREEZE:
+	case DEV_THAW:
+		error = priv_check(td, PRIV_DRIVER);
+		break;
 	default:
 		error = ENOTTY;
 		break;
@@ -5635,6 +5701,20 @@ devctl2_ioctl(struct cdev *cdev, u_long cmd, caddr_t d
 		error = device_delete_child(parent, dev);
 		break;
 	}
+	case DEV_FREEZE:
+		if (device_frozen)
+			error = EBUSY;
+		else
+			device_frozen = true;
+		break;
+	case DEV_THAW:
+		if (!device_frozen)
+			error = EBUSY;
+		else {
+			device_do_deferred_actions();
+			device_frozen = false;
+		}
+		break;
 	}
 	mtx_unlock(&Giant);
 	return (error);

Modified: head/sys/sys/bus.h
==============================================================================
--- head/sys/sys/bus.h	Thu Aug 23 02:26:40 2018	(r338232)
+++ head/sys/sys/bus.h	Thu Aug 23 05:05:47 2018	(r338233)
@@ -92,7 +92,8 @@ struct u_device {
 #define	DF_EXTERNALSOFTC 0x40		/* softc not allocated by us */
 #define	DF_REBID	0x80		/* Can rebid after attach */
 #define	DF_SUSPENDED	0x100		/* Device is suspended. */
-#define DF_QUIET_CHILDREN 0x200		/* Default to quiet for all my children */
+#define	DF_QUIET_CHILDREN 0x200		/* Default to quiet for all my children */
+#define	DF_NEEDNOMATCH	0x800		/* Has a pending NOMATCH event */
 
 /**
  * @brief Device request structure used for ioctl's.
@@ -126,6 +127,8 @@ struct devreq {
 #define	DEV_CLEAR_DRIVER _IOW('D', 8, struct devreq)
 #define	DEV_RESCAN	_IOW('D', 9, struct devreq)
 #define	DEV_DELETE	_IOW('D', 10, struct devreq)
+#define	DEV_FREEZE	_IOW('D', 11, struct devreq)
+#define	DEV_THAW	_IOW('D', 12, struct devreq)
 
 /* Flags for DEV_DETACH and DEV_DISABLE. */
 #define	DEVF_FORCE_DETACH	0x0000001

Modified: head/usr.sbin/devctl/devctl.c
==============================================================================
--- head/usr.sbin/devctl/devctl.c	Thu Aug 23 02:26:40 2018	(r338232)
+++ head/usr.sbin/devctl/devctl.c	Thu Aug 23 05:05:47 2018	(r338233)
@@ -71,17 +71,19 @@ DEVCTL_TABLE(top, set);
 static void
 usage(void)
 {
-	fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
-	    "usage: devctl attach device",
-	    "       devctl detach [-f] device",
-	    "       devctl disable [-f] device",
-	    "       devctl enable device",
-	    "       devctl suspend device",
-	    "       devctl resume device",
-	    "       devctl set driver [-f] device driver",
-	    "       devctl clear driver [-f] device",
-	    "       devctl rescan device",
-	    "       devctl delete [-f] device");
+	fprintf(stderr,
+	    "usage: devctl attach device\n"
+	    "       devctl detach [-f] device\n"
+	    "       devctl disable [-f] device\n"
+	    "       devctl enable device\n"
+	    "       devctl suspend device\n"
+	    "       devctl resume device\n"
+	    "       devctl set driver [-f] device driver\n"
+	    "       devctl clear driver [-f] device\n"
+	    "       devctl rescan device\n"
+	    "       devctl delete [-f] device\n"
+	    "       devctl freeze\n"
+	    "       devctl thaw\n");
 	exit(1);
 }
 
@@ -342,6 +344,46 @@ delete(int ac, char **av)
 	return (0);
 }
 DEVCTL_COMMAND(top, delete, delete);
+
+static void
+freeze_usage(void)
+{
+
+	fprintf(stderr, "usage: devctl freeze\n");
+	exit(1);
+}
+
+static int
+freeze(int ac, char **av __unused)
+{
+
+	if (ac != 1)
+		freeze_usage();
+	if (devctl_freeze() < 0)
+		err(1, "Failed to freeze probe/attach");
+	return (0);
+}
+DEVCTL_COMMAND(top, freeze, freeze);
+
+static void
+thaw_usage(void)
+{
+
+	fprintf(stderr, "usage: devctl thaw\n");
+	exit(1);
+}
+
+static int
+thaw(int ac, char **av __unused)
+{
+
+	if (ac != 1)
+		thaw_usage();
+	if (devctl_thaw() < 0)
+		err(1, "Failed to thaw probe/attach");
+	return (0);
+}
+DEVCTL_COMMAND(top, thaw, thaw);
 
 int
 main(int ac, char *av[])



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