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
This commit is contained in:
Warner Losh 2018-08-23 05:05:47 +00:00
parent 67b60a1b7d
commit 5fa2979791
6 changed files with 174 additions and 17 deletions

View File

@ -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 \

View File

@ -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));
}

View File

@ -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__ */

View File

@ -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);
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,12 +1226,17 @@ devclass_driver_deleted(devclass_t busclass, devclass_t dc, driver_t *driver)
dev->parent->devclass == busclass) {
if ((error = device_detach(dev)) != 0)
return (error);
if (device_frozen) {
dev->flags &= ~DF_DONENOMATCH;
dev->flags |= DF_NEEDNOMATCH;
} else {
BUS_PROBE_NOMATCH(dev->parent, dev);
devnomatch(dev);
dev->flags |= DF_DONENOMATCH;
}
}
}
}
/*
* Walk through the children classes. Since we only keep a
@ -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);

View File

@ -93,6 +93,7 @@ struct u_device {
#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_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

View File

@ -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[])
{