Index: head/lib/libdevctl/devctl.h =================================================================== --- head/lib/libdevctl/devctl.h +++ head/lib/libdevctl/devctl.h @@ -41,5 +41,7 @@ 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__ */ Index: head/lib/libdevctl/devctl.3 =================================================================== --- head/lib/libdevctl/devctl.3 +++ head/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 @@ .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 \ Index: head/lib/libdevctl/devctl.c =================================================================== --- head/lib/libdevctl/devctl.c +++ head/lib/libdevctl/devctl.c @@ -145,3 +145,17 @@ 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)); +} Index: head/sys/kern/subr_bus.c =================================================================== --- head/sys/kern/subr_bus.c +++ head/sys/kern/subr_bus.c @@ -82,6 +82,8 @@ 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(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 @@ 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 @@ * 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 @@ 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 @@ 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 @@ 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 @@ 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); Index: head/sys/sys/bus.h =================================================================== --- head/sys/sys/bus.h +++ head/sys/sys/bus.h @@ -92,7 +92,8 @@ #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 @@ #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 Index: head/usr.sbin/devctl/devctl.c =================================================================== --- head/usr.sbin/devctl/devctl.c +++ head/usr.sbin/devctl/devctl.c @@ -71,17 +71,19 @@ 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 @@ 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[])