From 5025fc65cd2448de8a5b7295c9936b473a7b0194 Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Mon, 15 Jan 2018 16:37:35 -0800 Subject: [PATCH] kernel - Add a global priority hack for ACPI * Allows ACPI to fully probe and attach all acpi devices before attaching any non-ACPI devices. * May improve device attach stability. * Currently only implemented at the top-level of the acpi device tree. --- sys/dev/acpica/acpi.c | 10 ++- sys/dev/acpica/acpi_acad.c | 1 + sys/dev/acpica/acpi_button.c | 1 + sys/dev/acpica/acpi_cmbat.c | 1 + sys/dev/acpica/acpi_cpu.c | 3 +- sys/dev/acpica/acpi_cpu_cstate.c | 1 + sys/dev/acpica/acpi_cpu_pstate.c | 3 +- sys/dev/acpica/acpi_ec.c | 1 + sys/dev/acpica/acpi_hpet.c | 1 + sys/dev/acpica/acpi_isab.c | 1 + sys/dev/acpica/acpi_lid.c | 1 + sys/dev/acpica/acpi_pci_link.c | 1 + sys/dev/acpica/acpi_resource.c | 1 + sys/dev/acpica/acpi_smbat.c | 1 + sys/dev/acpica/acpi_thermal.c | 1 + sys/dev/acpica/acpi_timer.c | 1 + sys/kern/subr_bus.c | 170 ++++++++++++++++++++++++++++++++++++++- sys/sys/bus.h | 3 + sys/sys/kobj.h | 11 ++- 19 files changed, 205 insertions(+), 8 deletions(-) diff --git a/sys/dev/acpica/acpi.c b/sys/dev/acpica/acpi.c index ff42107fe7..123f259758 100644 --- a/sys/dev/acpica/acpi.c +++ b/sys/dev/acpica/acpi.c @@ -214,6 +214,7 @@ static driver_t acpi_driver = { "acpi", acpi_methods, sizeof(struct acpi_softc), + .gpri = KOBJ_GPRI_ACPI }; static devclass_t acpi_devclass; @@ -1666,13 +1667,18 @@ acpi_probe_children(device_t bus) /* Probe/attach all children, created staticly and from the namespace. */ ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "first bus_generic_attach\n")); - bus_generic_attach(bus); + bus_generic_attach_gpri(bus, KOBJ_GPRI_ACPI); + ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "second bus_generic_attach\n")); + bus_generic_attach_gpri(bus, KOBJ_GPRI_ACPI); /* * Some of these children may have attached others as part of their attach * process (eg. the root PCI bus driver), so rescan. */ - ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "second bus_generic_attach\n")); + ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "third bus_generic_attach\n")); + bus_generic_attach(bus); + + ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "fourth bus_generic_attach\n")); bus_generic_attach(bus); /* Attach wake sysctls. */ diff --git a/sys/dev/acpica/acpi_acad.c b/sys/dev/acpica/acpi_acad.c index 1bfb3636b5..c59a93bb8e 100644 --- a/sys/dev/acpica/acpi_acad.c +++ b/sys/dev/acpica/acpi_acad.c @@ -85,6 +85,7 @@ static driver_t acpi_acad_driver = { "acpi_acad", acpi_acad_methods, sizeof(struct acpi_acad_softc), + .gpri = KOBJ_GPRI_ACPI }; static devclass_t acpi_acad_devclass; diff --git a/sys/dev/acpica/acpi_button.c b/sys/dev/acpica/acpi_button.c index 69bf6c3cab..3f46a9b5dd 100644 --- a/sys/dev/acpica/acpi_button.c +++ b/sys/dev/acpica/acpi_button.c @@ -86,6 +86,7 @@ static driver_t acpi_button_driver = { "acpi_button", acpi_button_methods, sizeof(struct acpi_button_softc), + .gpri = KOBJ_GPRI_ACPI }; static devclass_t acpi_button_devclass; diff --git a/sys/dev/acpica/acpi_cmbat.c b/sys/dev/acpica/acpi_cmbat.c index d7a6dfc670..1f636430be 100644 --- a/sys/dev/acpica/acpi_cmbat.c +++ b/sys/dev/acpica/acpi_cmbat.c @@ -99,6 +99,7 @@ static driver_t acpi_cmbat_driver = { "battery", acpi_cmbat_methods, sizeof(struct acpi_cmbat_softc), + .gpri = KOBJ_GPRI_ACPI }; static devclass_t acpi_cmbat_devclass; diff --git a/sys/dev/acpica/acpi_cpu.c b/sys/dev/acpica/acpi_cpu.c index c81e0bc978..eaa5a4a8b9 100644 --- a/sys/dev/acpica/acpi_cpu.c +++ b/sys/dev/acpica/acpi_cpu.c @@ -94,7 +94,8 @@ static device_method_t acpi_cpu_methods[] = { static driver_t acpi_cpu_driver = { "cpu", acpi_cpu_methods, - sizeof(struct acpi_cpu_softc) + sizeof(struct acpi_cpu_softc), + .gpri = KOBJ_GPRI_ACPI }; static devclass_t acpi_cpu_devclass; diff --git a/sys/dev/acpica/acpi_cpu_cstate.c b/sys/dev/acpica/acpi_cpu_cstate.c index c7fedbd4f3..d643b2eb31 100644 --- a/sys/dev/acpica/acpi_cpu_cstate.c +++ b/sys/dev/acpica/acpi_cpu_cstate.c @@ -198,6 +198,7 @@ static driver_t acpi_cst_driver = { "cpu_cst", acpi_cst_methods, sizeof(struct acpi_cst_softc), + .gpri = KOBJ_GPRI_ACPI }; static devclass_t acpi_cst_devclass; diff --git a/sys/dev/acpica/acpi_cpu_pstate.c b/sys/dev/acpica/acpi_cpu_pstate.c index b72a5b02cb..4c391f378c 100644 --- a/sys/dev/acpica/acpi_cpu_pstate.c +++ b/sys/dev/acpica/acpi_cpu_pstate.c @@ -224,7 +224,8 @@ static device_method_t acpi_pst_methods[] = { static driver_t acpi_pst_driver = { "cpu_pst", acpi_pst_methods, - sizeof(struct acpi_pst_softc) + sizeof(struct acpi_pst_softc), + .gpri = KOBJ_GPRI_ACPI }; static devclass_t acpi_pst_devclass; diff --git a/sys/dev/acpica/acpi_ec.c b/sys/dev/acpica/acpi_ec.c index d386eb82d9..d586376507 100644 --- a/sys/dev/acpica/acpi_ec.c +++ b/sys/dev/acpica/acpi_ec.c @@ -291,6 +291,7 @@ static driver_t acpi_ec_driver = { "acpi_ec", acpi_ec_methods, sizeof(struct acpi_ec_softc), + .gpri = KOBJ_GPRI_ACPI }; static devclass_t acpi_ec_devclass; diff --git a/sys/dev/acpica/acpi_hpet.c b/sys/dev/acpica/acpi_hpet.c index 613dbfe294..3a9641f740 100644 --- a/sys/dev/acpica/acpi_hpet.c +++ b/sys/dev/acpica/acpi_hpet.c @@ -98,6 +98,7 @@ static driver_t acpi_hpet_driver = { "acpi_hpet", acpi_hpet_methods, sizeof(struct acpi_hpet_softc), + .gpri = KOBJ_GPRI_ACPI }; static devclass_t acpi_hpet_devclass; diff --git a/sys/dev/acpica/acpi_isab.c b/sys/dev/acpica/acpi_isab.c index 748e1a7c54..b6d56b9de3 100644 --- a/sys/dev/acpica/acpi_isab.c +++ b/sys/dev/acpica/acpi_isab.c @@ -83,6 +83,7 @@ static driver_t acpi_isab_driver = { "isab", acpi_isab_methods, sizeof(struct acpi_isab_softc), + .gpri = KOBJ_GPRI_ACPI }; DRIVER_MODULE(acpi_isab, acpi, acpi_isab_driver, isab_devclass, NULL, NULL); diff --git a/sys/dev/acpica/acpi_lid.c b/sys/dev/acpica/acpi_lid.c index fb5f2aeb20..1125ba2d82 100644 --- a/sys/dev/acpica/acpi_lid.c +++ b/sys/dev/acpica/acpi_lid.c @@ -77,6 +77,7 @@ static driver_t acpi_lid_driver = { "acpi_lid", acpi_lid_methods, sizeof(struct acpi_lid_softc), + .gpri = KOBJ_GPRI_ACPI }; static devclass_t acpi_lid_devclass; diff --git a/sys/dev/acpica/acpi_pci_link.c b/sys/dev/acpica/acpi_pci_link.c index 80ddceb0ae..ad637fdfa5 100644 --- a/sys/dev/acpica/acpi_pci_link.c +++ b/sys/dev/acpica/acpi_pci_link.c @@ -1113,6 +1113,7 @@ static driver_t acpi_pci_link_driver = { "pci_link", acpi_pci_link_methods, sizeof(struct acpi_pci_link_softc), + .gpri = KOBJ_GPRI_ACPI }; static devclass_t pci_link_devclass; diff --git a/sys/dev/acpica/acpi_resource.c b/sys/dev/acpica/acpi_resource.c index c5cf8add9f..75eed314b6 100644 --- a/sys/dev/acpica/acpi_resource.c +++ b/sys/dev/acpica/acpi_resource.c @@ -661,6 +661,7 @@ static driver_t acpi_sysres_driver = { "acpi_sysresource", acpi_sysres_methods, 0, + .gpri = KOBJ_GPRI_ACPI }; static devclass_t acpi_sysres_devclass; diff --git a/sys/dev/acpica/acpi_smbat.c b/sys/dev/acpica/acpi_smbat.c index 545e218e9f..37672d205d 100644 --- a/sys/dev/acpica/acpi_smbat.c +++ b/sys/dev/acpica/acpi_smbat.c @@ -94,6 +94,7 @@ static driver_t acpi_smbat_driver = { "battery", acpi_smbat_methods, sizeof(struct acpi_smbat_softc), + .gpri = KOBJ_GPRI_ACPI }; static devclass_t acpi_smbat_devclass; diff --git a/sys/dev/acpica/acpi_thermal.c b/sys/dev/acpica/acpi_thermal.c index 81accd4d16..12a91aa436 100644 --- a/sys/dev/acpica/acpi_thermal.c +++ b/sys/dev/acpica/acpi_thermal.c @@ -167,6 +167,7 @@ static driver_t acpi_tz_driver = { "acpi_tz", acpi_tz_methods, sizeof(struct acpi_tz_softc), + .gpri = KOBJ_GPRI_ACPI }; static char *acpi_tz_tmp_name = "_TMP"; diff --git a/sys/dev/acpica/acpi_timer.c b/sys/dev/acpica/acpi_timer.c index 83da56f97f..45df3f0126 100644 --- a/sys/dev/acpica/acpi_timer.c +++ b/sys/dev/acpica/acpi_timer.c @@ -93,6 +93,7 @@ static driver_t acpi_timer_driver = { "acpi_timer", acpi_timer_methods, 0, + .gpri = KOBJ_GPRI_ACPI }; static devclass_t acpi_timer_devclass; diff --git a/sys/kern/subr_bus.c b/sys/kern/subr_bus.c index 0e2bb99a59..b41ec9f918 100644 --- a/sys/kern/subr_bus.c +++ b/sys/kern/subr_bus.c @@ -1233,9 +1233,10 @@ device_add_child_ordered(device_t dev, int order, const char *name, int unit) return child; child->order = order; - TAILQ_FOREACH(place, &dev->children, link) + TAILQ_FOREACH(place, &dev->children, link) { if (place->order > order) break; + } if (place) { /* @@ -1392,7 +1393,7 @@ device_probe_child(device_t dev, device_t child) return(0); for (; dc; dc = dc->parent) { - for (dl = first_matching_driver(dc, child); dl; + for (dl = first_matching_driver(dc, child); dl; dl = next_matching_driver(dc, child, dl)) { PDEBUG(("Trying %s", DRIVERNAME(dl->driver))); device_set_driver(child, dl->driver); @@ -1431,13 +1432,108 @@ device_probe_child(device_t dev, device_t child) pri = result; continue; } + } + /* + * If we have unambiguous match in this devclass, + * don't look in the parent. + */ + if (best && pri == 0) + break; + } + + /* + * If we found a driver, change state and initialise the devclass. + */ + if (best) { + if (!child->devclass) + device_set_devclass(child, best->driver->name); + device_set_driver(child, best->driver); + if (pri < 0) { + /* + * A bit bogus. Call the probe method again to make + * sure that we have the right description. + */ + DEVICE_PROBE(child); + } + + bus_data_generation_update(); + child->state = DS_ALIVE; + return(0); + } + + return(ENXIO); +} + +int +device_probe_child_gpri(device_t dev, device_t child, u_int gpri) +{ + devclass_t dc; + driverlink_t best = NULL; + driverlink_t dl; + int result, pri = 0; + int hasclass = (child->devclass != NULL); + + dc = dev->devclass; + if (!dc) + panic("device_probe_child: parent device has no devclass"); + + if (child->state == DS_ALIVE) + return(0); + + for (; dc; dc = dc->parent) { + for (dl = first_matching_driver(dc, child); dl; + dl = next_matching_driver(dc, child, dl)) { + /* + * GPRI handling, only probe drivers with the + * specific GPRI. + */ + if (dl->driver->gpri != gpri) + continue; + + PDEBUG(("Trying %s", DRIVERNAME(dl->driver))); + device_set_driver(child, dl->driver); + if (!hasclass) + device_set_devclass(child, dl->driver->name); + result = DEVICE_PROBE(child); + if (!hasclass) + device_set_devclass(child, 0); + + /* + * If the driver returns SUCCESS, there can be + * no higher match for this device. + */ + if (result == 0) { + best = dl; + pri = 0; + break; + } + + /* + * The driver returned an error so it + * certainly doesn't match. + */ + if (result > 0) { + device_set_driver(child, NULL); + continue; + } + + /* + * A priority lower than SUCCESS, remember the + * best matching driver. Initialise the value + * of pri for the first match. + */ + if (best == NULL || result > pri) { + best = dl; + pri = result; + continue; + } } /* * If we have unambiguous match in this devclass, * don't look in the parent. */ if (best && pri == 0) - break; + break; } /* @@ -1851,6 +1947,62 @@ device_probe_and_attach(device_t dev) return(error); } +int +device_probe_and_attach_gpri(device_t dev, u_int gpri) +{ + device_t bus = dev->parent; + int error = 0; + + if (dev->state >= DS_ALIVE) + return(0); + + if ((dev->flags & DF_ENABLED) == 0) { + if (bootverbose) { + device_print_prettyname(dev); + kprintf("not probed (disabled)\n"); + } + return(0); + } + + error = device_probe_child_gpri(bus, dev, gpri); + if (error) { +#if 0 + if (!(dev->flags & DF_DONENOMATCH)) { + BUS_PROBE_NOMATCH(bus, dev); + devnomatch(dev); + dev->flags |= DF_DONENOMATCH; + } +#endif + return(error); + } + + /* + * Output the exact device chain prior to the attach in case the + * system locks up during attach, and generate the full info after + * the attach so correct irq and other information is displayed. + */ + if (bootverbose && !device_is_quiet(dev)) { + device_t tmp; + + kprintf("%s", device_get_nameunit(dev)); + for (tmp = dev->parent; tmp; tmp = tmp->parent) + kprintf(".%s", device_get_nameunit(tmp)); + kprintf("\n"); + } + if (!device_is_quiet(dev)) + device_print_child(bus, dev); + if ((dev->flags & DF_ASYNCPROBE) && do_async_attach) { + kprintf("%s: probing asynchronously\n", + device_get_nameunit(dev)); + dev->state = DS_INPROGRESS; + device_attach_async(dev); + error = 0; + } else { + error = device_doattach(dev); + } + return(error); +} + /* * Device is known to be alive, do the attach asynchronously. * However, serialize the attaches with the mp lock. @@ -2633,6 +2785,18 @@ bus_generic_attach(device_t dev) } int +bus_generic_attach_gpri(device_t dev, u_int gpri) +{ + device_t child; + + TAILQ_FOREACH(child, &dev->children, link) { + device_probe_and_attach_gpri(child, gpri); + } + + return(0); +} + +int bus_generic_detach(device_t dev) { device_t child; diff --git a/sys/sys/bus.h b/sys/sys/bus.h index 7f09ebb78d..e614b2314c 100644 --- a/sys/sys/bus.h +++ b/sys/sys/bus.h @@ -245,6 +245,7 @@ struct resource_list * int bus_generic_config_intr(device_t, device_t, int, enum intr_trigger, enum intr_polarity); int bus_generic_attach(device_t dev); +int bus_generic_attach_gpri(device_t dev, u_int gpri); int bus_generic_child_present(device_t dev, device_t child); int bus_generic_deactivate_resource(device_t dev, device_t child, int type, int rid, struct resource *r); @@ -383,7 +384,9 @@ int device_is_quiet(device_t dev); int device_print_prettyname(device_t dev); int device_printf(device_t dev, const char *, ...) __printflike(2, 3); int device_probe_and_attach(device_t dev); +int device_probe_and_attach_gpri(device_t dev, u_int gpri); int device_probe_child(device_t dev, device_t child); +int device_probe_child_gpri(device_t dev, device_t child, u_int gpri); void device_quiet(device_t dev); void device_set_desc(device_t dev, const char* desc); void device_set_desc_copy(device_t dev, const char* desc); diff --git a/sys/sys/kobj.h b/sys/sys/kobj.h index d002baed02..e023fc9358 100644 --- a/sys/sys/kobj.h +++ b/sys/sys/kobj.h @@ -65,7 +65,8 @@ struct kobj_method { size_t size; /* object size */ \ kobj_class_t *baseclasses; /* base classes */ \ u_int refs; /* reference count */ \ - kobj_ops_t ops /* compiled method table */ + kobj_ops_t ops; /* compiled method table */ \ + u_int gpri /* global probe/attach pri */ struct kobj_class { KOBJ_CLASS_FIELDS; @@ -82,6 +83,14 @@ struct kobj { }; /* + * gpri values (higher values probe/attach first, allows us to + * default the field to 0). + */ +#define KOBJ_GPRI_ACPI 0x00FFU +#define KOBJ_GPRI_LAST 0x0000U +#define KOBJ_GPRI_DEFAULT KOBJ_GPRI_LAST + +/* * The ops table is used as a cache of results from kobj_lookup_method(). */ -- 2.11.4.GIT