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:
parent
67b60a1b7d
commit
5fa2979791
@ -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 \
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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__ */
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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[])
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user