Index: share/man/man9/DEVICE_ATTACH.9 =================================================================== --- share/man/man9/DEVICE_ATTACH.9 +++ share/man/man9/DEVICE_ATTACH.9 @@ -28,7 +28,7 @@ .\" .\" $FreeBSD$ .\" -.Dd January 15, 2017 +.Dd May 20, 2018 .Dt DEVICE_ATTACH 9 .Os .Sh NAME @@ -57,12 +57,26 @@ If this is combined with the use of .Xr bus_generic_attach 9 the child devices will be automatically probed and attached. +.Pp +If +.Fn DEVICE_DELAY_ATTACH +is implemented, +.Fn DEVICE_ATTACH +will not be called until +.Fn DEVICE_DELAY_ATTACH +returns true, even after a successful probe. +This can be used to delay attachment until after other devices in +the tree on which this device depends have successfully attached in +cases where +.Fn EARLY_DRIVER_MODULE +is insufficient. .Sh RETURN VALUES Zero is returned on success, otherwise an appropriate error is returned. .Sh SEE ALSO .Xr devfs 5 , .Xr device 9 , .Xr DEVICE_DETACH 9 , +.Xr DEVICE_DELAY_ATTACH 9 , .Xr DEVICE_IDENTIFY 9 , .Xr DEVICE_PROBE 9 , .Xr DEVICE_SHUTDOWN 9 Index: share/man/man9/DEVICE_DELAY_ATTACH.9 =================================================================== --- share/man/man9/DEVICE_DELAY_ATTACH.9 +++ share/man/man9/DEVICE_DELAY_ATTACH.9 @@ -0,0 +1,79 @@ +.\" -*- nroff -*- +.\" +.\" Copyright (c) 2018 Nathan Whitehorn +.\" +.\" All rights reserved. +.\" +.\" This program is free software. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd May 20, 2018 +.Dt DEVICE_DELAY_ATTACH 9 +.Os +.Sh NAME +.Nm DEVICE_DELAY_ATTACH +.Nd check if probed device is ready to attach +.Sh SYNOPSIS +.In sys/param.h +.In sys/bus.h +.Ft bool +.Fn DEVICE_DELAY_ATTACH "device_t dev" +.Sh DESCRIPTION +Signal that +.Fn DEVICE_ATTACH +should not be called immediately after the +.Fn DEVICE_PROBE +method has been called and has indicated that +the device exists. +If +.Fn DEVICE_DELAY_ATTACH +is implemented, +.Fn DEVICE_ATTACH +will not be called until +.Fn DEVICE_DELAY_ATTACH +returns true, even after a successful probe. +This can be used to delay attachment until after other devices in the tree +on which this device depends have successfully attached. +This is particularly useful in cases where +.Fn EARLY_DRIVER_MODULE +is insufficient, for example if the needed attach order is only known at +runtime. +Note that management of dependent device detachment is up to the individual +driver. +.Sh RETURN VALUES +True should be returned if attachment should not be attempted yet. +False should be returned if attachment can proceed. +If unimplemented, returns false. +This function (by design) provides no way to report errors; if a fatal error +occurs, it should return false, allowing immediate attachment, with the error +returned by +.Fn DEVICE_ATTACH . +.Sh SEE ALSO +.Xr devfs 5 , +.Xr device 9 , +.Xr DEVICE_ATTACH 9 , +.Xr EARLY_DRIVER_MODULE 9 , +.Sh AUTHORS +This manual page was written by +.An Nathan Whitehorn Aq Mt nwhitehorn@FreeBSD.org . Index: share/man/man9/Makefile =================================================================== --- share/man/man9/Makefile +++ share/man/man9/Makefile @@ -89,6 +89,7 @@ device.9 \ device_add_child.9 \ DEVICE_ATTACH.9 \ + DEVICE_DELAY_ATTACH.9 \ device_delete_child.9 \ DEVICE_DETACH.9 \ device_enable.9 \ Index: sys/kern/device_if.m =================================================================== --- sys/kern/device_if.m +++ sys/kern/device_if.m @@ -48,6 +48,11 @@ # Default implementations of some methods. # CODE { + static bool null_delay_attach(device_t dev) + { + return FALSE; + } + static int null_shutdown(device_t dev) { return 0; @@ -188,6 +193,27 @@ }; /** + * @brief See if a device's dependencies are present. + * If not, delay attach for the future but continue claiming the device. + + * To include this method in a device driver, use a line like this + * in the driver's method list: + * + * @code + * KOBJMETHOD(device_delay_attach, foo_delay_attach) + * @endcode + * + * @param dev the device to probe + * + * @retval TRUE postpone attachment + * @retval FALSE attach now + * @see DEVICE_ATTACH() + */ +METHOD bool delay_attach { + device_t dev; +} DEFAULT null_delay_attach; + +/** * @brief Attach a device to a device driver * * Normally only called via device_probe_and_attach(), this is called Index: sys/kern/subr_bus.c =================================================================== --- sys/kern/subr_bus.c +++ sys/kern/subr_bus.c @@ -935,6 +935,59 @@ TAILQ_INSERT_TAIL(&passes, new, passlink); } + +/* + * Recursively descend the device tree, trying to attach any devices + * left in limbo from a delayed attachment. Returns the total number of devices + * so attached. + */ +static int +bus_try_attach_pending_children(device_t parent) +{ + int attachments_made = 0; + device_t child; + + /* + * Try to attach anything in DS_ALIVE that is a direct child and + * descend to any grandchildren on fully-attached children. + */ + + TAILQ_FOREACH(child, &parent->children, link) { + if (child->state == DS_ALIVE) { /* Left in pending state */ + if (device_attach(child) != EAGAIN) + attachments_made++; + } else if (child->state == DS_ATTACHED) { + attachments_made += + bus_try_attach_pending_children(child); + } + } + + return (attachments_made); +} + +/* + * Iteratively rescan the entire device tree, trying to attach any devices + * left in DS_ALIVE limbo because DEVICE_DELAY_ATTACH() returned true. Will + * continue to rescan and attempt attachment so long as at least one pending + * device attaches in each round, guaranteeing the attachment of all devices + * with non-circular met dependencies. + * + * Returns the total number of delayed attachments completed. + */ +static int +bus_try_attach_pending(void) +{ + int attachments_made, this_round; + + attachments_made = 0; + do { + this_round = bus_try_attach_pending_children(root_bus); + attachments_made += this_round; + } while (this_round > 0); + + return (attachments_made); +} + /** * @brief Raise the current bus pass * @@ -978,6 +1031,11 @@ if (bus_current_pass < pass) bus_current_pass = pass; KASSERT(bus_current_pass == pass, ("Failed to update bus pass level")); + + /* + * Try to attach any pending devices left over at this point. + */ + bus_try_attach_pending(); } /* @@ -1121,6 +1179,11 @@ if (dc->parent == parent) devclass_driver_added(dc, driver); } + + /* + * Try to attach any pending devices left over at this point. + */ + bus_try_attach_pending(); } /** @@ -2928,6 +2991,12 @@ return (ENXIO); } + if (DEVICE_DELAY_ATTACH(dev)) { + if (bootverbose) + device_printf(dev, "attach postponed\n"); + return (EAGAIN); + } + device_sysctl_init(dev); if (!device_is_quiet(dev)) device_print_child(dev->parent, dev); Index: sys/powerpc/powermac/smu.c =================================================================== --- sys/powerpc/powermac/smu.c +++ sys/powerpc/powermac/smu.c @@ -154,6 +154,7 @@ /* regular bus attachment functions */ static int smu_probe(device_t); +static bool smu_delay_attach(device_t); static int smu_attach(device_t); static const struct ofw_bus_devinfo * smu_get_devinfo(device_t bus, device_t dev); @@ -186,6 +187,7 @@ static device_method_t smu_methods[] = { /* Device interface */ DEVMETHOD(device_probe, smu_probe), + DEVMETHOD(device_delay_attach, smu_delay_attach), DEVMETHOD(device_attach, smu_attach), /* Clock interface */ @@ -278,6 +280,14 @@ sc->sc_cmd_phys = segs[0].ds_addr; } +static bool +smu_delay_attach(device_t dev) +{ + + /* Delay attachment until our GPIO has attached */ + return (smu_doorbell == NULL); +} + static int smu_attach(device_t dev) {