aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWarner Losh <imp@FreeBSD.org>2018-08-23 05:05:47 +0000
committerWarner Losh <imp@FreeBSD.org>2018-08-23 05:05:47 +0000
commit5fa29797910346fc0c54829bd979856e83b9b7ea (patch)
tree461c4837323a9d9612e2ccd14735ea250d763699
parent67b60a1b7d46d959bdd4925944b149eebbfb593f (diff)
downloadsrc-5fa29797910346fc0c54829bd979856e83b9b7ea.tar.gz
src-5fa29797910346fc0c54829bd979856e83b9b7ea.zip
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
Notes
Notes: svn path=/head/; revision=338233
-rw-r--r--lib/libdevctl/devctl.318
-rw-r--r--lib/libdevctl/devctl.c14
-rw-r--r--lib/libdevctl/devctl.h2
-rw-r--r--sys/kern/subr_bus.c88
-rw-r--r--sys/sys/bus.h5
-rw-r--r--usr.sbin/devctl/devctl.c64
6 files changed, 174 insertions, 17 deletions
diff --git a/lib/libdevctl/devctl.3 b/lib/libdevctl/devctl.3
index a4fe0620ac3f..8749515ecd74 100644
--- a/lib/libdevctl/devctl.3
+++ b/lib/libdevctl/devctl.3
@@ -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 \
diff --git a/lib/libdevctl/devctl.c b/lib/libdevctl/devctl.c
index 5013fb0b9d57..9c9ab7805672 100644
--- a/lib/libdevctl/devctl.c
+++ b/lib/libdevctl/devctl.c
@@ -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));
+}
diff --git a/lib/libdevctl/devctl.h b/lib/libdevctl/devctl.h
index d49199ad0b59..11a21753809d 100644
--- a/lib/libdevctl/devctl.h
+++ b/lib/libdevctl/devctl.h
@@ -41,5 +41,7 @@ int devctl_set_driver(const char *device, const char *driver, bool force);
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__ */
diff --git a/sys/kern/subr_bus.c b/sys/kern/subr_bus.c
index ed4defe0ccd5..9d09d341ae1d 100644
--- a/sys/kern/subr_bus.c
+++ b/sys/kern/subr_bus.c
@@ -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, int pass, devclass_t *dcp)
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_t dc, driver_t *driver)
* 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_t dc, driver_t *driver)
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 data, int fflag,
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 data, int fflag,
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);
diff --git a/sys/sys/bus.h b/sys/sys/bus.h
index ae643069eac3..a96cfc6c8f72 100644
--- a/sys/sys/bus.h
+++ b/sys/sys/bus.h
@@ -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
diff --git a/usr.sbin/devctl/devctl.c b/usr.sbin/devctl/devctl.c
index 6b266f42fe40..81fafcfd265b 100644
--- a/usr.sbin/devctl/devctl.c
+++ b/usr.sbin/devctl/devctl.c
@@ -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);
}
@@ -343,6 +345,46 @@ delete(int ac, char **av)
}
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[])
{