summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/kern/subr_bus.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/sys/kern/subr_bus.c')
-rw-r--r--freebsd/sys/kern/subr_bus.c112
1 files changed, 85 insertions, 27 deletions
diff --git a/freebsd/sys/kern/subr_bus.c b/freebsd/sys/kern/subr_bus.c
index 8076e7e3..0626ec0a 100644
--- a/freebsd/sys/kern/subr_bus.c
+++ b/freebsd/sys/kern/subr_bus.c
@@ -1863,6 +1863,8 @@ make_device(device_t parent, const char *name, int unit)
return (NULL);
}
}
+ if (parent != NULL && device_has_quiet_children(parent))
+ dev->flags |= DF_QUIET | DF_QUIET_CHILDREN;
dev->ivars = NULL;
dev->softc = NULL;
@@ -2688,6 +2690,15 @@ device_quiet(device_t dev)
}
/**
+ * @brief Set the DF_QUIET_CHILDREN flag for the device
+ */
+void
+device_quiet_children(device_t dev)
+{
+ dev->flags |= DF_QUIET_CHILDREN;
+}
+
+/**
* @brief Clear the DF_QUIET flag for the device
*/
void
@@ -2697,6 +2708,15 @@ device_verbose(device_t dev)
}
/**
+ * @brief Return non-zero if the DF_QUIET_CHIDLREN flag is set on the device
+ */
+int
+device_has_quiet_children(device_t dev)
+{
+ return ((dev->flags & DF_QUIET_CHILDREN) != 0);
+}
+
+/**
* @brief Return non-zero if the DF_QUIET flag is set on the device
*/
int
@@ -3760,7 +3780,11 @@ bus_generic_detach(device_t dev)
if (dev->state != DS_ATTACHED)
return (EBUSY);
- TAILQ_FOREACH(child, &dev->children, link) {
+ /*
+ * Detach children in the reverse order.
+ * See bus_generic_suspend for details.
+ */
+ TAILQ_FOREACH_REVERSE(child, &dev->children, device_list, link) {
if ((error = device_detach(child)) != 0)
return (error);
}
@@ -3780,7 +3804,11 @@ bus_generic_shutdown(device_t dev)
{
device_t child;
- TAILQ_FOREACH(child, &dev->children, link) {
+ /*
+ * Shut down children in the reverse order.
+ * See bus_generic_suspend for details.
+ */
+ TAILQ_FOREACH_REVERSE(child, &dev->children, device_list, link) {
device_shutdown(child);
}
@@ -3833,15 +3861,23 @@ int
bus_generic_suspend(device_t dev)
{
int error;
- device_t child, child2;
+ device_t child;
- TAILQ_FOREACH(child, &dev->children, link) {
+ /*
+ * Suspend children in the reverse order.
+ * For most buses all children are equal, so the order does not matter.
+ * Other buses, such as acpi, carefully order their child devices to
+ * express implicit dependencies between them. For such buses it is
+ * safer to bring down devices in the reverse order.
+ */
+ TAILQ_FOREACH_REVERSE(child, &dev->children, device_list, link) {
error = BUS_SUSPEND_CHILD(dev, child);
- if (error) {
- for (child2 = TAILQ_FIRST(&dev->children);
- child2 && child2 != child;
- child2 = TAILQ_NEXT(child2, link))
- BUS_RESUME_CHILD(dev, child2);
+ if (error != 0) {
+ child = TAILQ_NEXT(child, link);
+ if (child != NULL) {
+ TAILQ_FOREACH_FROM(child, &dev->children, link)
+ BUS_RESUME_CHILD(dev, child);
+ }
return (error);
}
}
@@ -5285,8 +5321,9 @@ sysctl_devices(SYSCTL_HANDLER_ARGS)
u_int namelen = arg2;
int index;
device_t dev;
- struct u_device udev; /* XXX this is a bit big */
+ struct u_device *udev;
int error;
+ char *walker, *ep;
if (namelen != 2)
return (EINVAL);
@@ -5307,24 +5344,45 @@ sysctl_devices(SYSCTL_HANDLER_ARGS)
return (ENOENT);
/*
- * Populate the return array.
+ * Populate the return item, careful not to overflow the buffer.
*/
- bzero(&udev, sizeof(udev));
- udev.dv_handle = (uintptr_t)dev;
- udev.dv_parent = (uintptr_t)dev->parent;
- if (dev->nameunit != NULL)
- strlcpy(udev.dv_name, dev->nameunit, sizeof(udev.dv_name));
- if (dev->desc != NULL)
- strlcpy(udev.dv_desc, dev->desc, sizeof(udev.dv_desc));
- if (dev->driver != NULL && dev->driver->name != NULL)
- strlcpy(udev.dv_drivername, dev->driver->name,
- sizeof(udev.dv_drivername));
- bus_child_pnpinfo_str(dev, udev.dv_pnpinfo, sizeof(udev.dv_pnpinfo));
- bus_child_location_str(dev, udev.dv_location, sizeof(udev.dv_location));
- udev.dv_devflags = dev->devflags;
- udev.dv_flags = dev->flags;
- udev.dv_state = dev->state;
- error = SYSCTL_OUT(req, &udev, sizeof(udev));
+ udev = malloc(sizeof(*udev), M_BUS, M_WAITOK | M_ZERO);
+ if (udev == NULL)
+ return (ENOMEM);
+ udev->dv_handle = (uintptr_t)dev;
+ udev->dv_parent = (uintptr_t)dev->parent;
+ udev->dv_devflags = dev->devflags;
+ udev->dv_flags = dev->flags;
+ udev->dv_state = dev->state;
+ walker = udev->dv_fields;
+ ep = walker + sizeof(udev->dv_fields);
+#define CP(src) \
+ if ((src) == NULL) \
+ *walker++ = '\0'; \
+ else { \
+ strlcpy(walker, (src), ep - walker); \
+ walker += strlen(walker) + 1; \
+ } \
+ if (walker >= ep) \
+ break;
+
+ do {
+ CP(dev->nameunit);
+ CP(dev->desc);
+ CP(dev->driver != NULL ? dev->driver->name : NULL);
+ bus_child_pnpinfo_str(dev, walker, ep - walker);
+ walker += strlen(walker) + 1;
+ if (walker >= ep)
+ break;
+ bus_child_location_str(dev, walker, ep - walker);
+ walker += strlen(walker) + 1;
+ if (walker >= ep)
+ break;
+ *walker++ = '\0';
+ } while (0);
+#undef CP
+ error = SYSCTL_OUT(req, udev, sizeof(*udev));
+ free(udev, M_BUS);
return (error);
}