From: Michael Kelley <[email protected]> Sent: Wednesday, September 23, 2026 10:06 AM > > From: Rafael J. Wysocki <[email protected]> Sent: Wednesday, September 9, > 2026 9:41 AM > > > > The ACPI bus type does not allow drivers to be registered, so the sysfs > > attributes related to drivers created for it and its devices are > > useless, and its drivers/ directory is always empty. All of that is > > confusing and wasteful. > > > > To allow skipping the creation of those sysfs attributes, introduce a > > "companion" bus type concept and add a special registration function for > > registering "companion" bus types, companion_bus_register(). > > > > Signed-off-by: Rafael J. Wysocki <[email protected]> > > As a heads up, this patch breaks Linux guests booting on Hyper-V. > Something goes wrong in the VMBus driver getting loaded and > initialized (drivers/hv/vmbus_drv.c). It may be that the VMBus > driver is doing something wrong or making an invalid assumption. > I'll debug the problem today or tomorrow, but any insight you can > offer would be appreciated. I'm working with linux-next20260921.
Here's what I've learned so far: 1) The problem is apparently due to systemd-udevd being unable to load modules with the VMBus bus driver or any of the individual drivers for VMBus devices. 2) I'm testing on Ubuntu 20.04 with systemd version 245. The problem reproduces on a different 20.04 instance. But everything works correctly on Ubuntu 24.04 with systemd version 255. 3) If the VMBus bus driver and key VMBus device drivers are compiled as built-in instead of as modules, the Ubuntu 20.04 system will boot. 4) If I keep your patch, but change companion_bus_register() to pass "true" as the second argument instead of "false", then the /sys/bus/acpi/drivers directory is created as before, and everything works. Also, only adding back the .match function as you suggested does not make any difference. 5) The VMBus bus is enumerated in the ACPI DSDT. The individual synthetic devices that are logically on VMBus are not -- they are "offered" by Hyper-V to the guest at runtime via a custom protocol. The synthetic devices end up with paths like: /sys/devices/LNXSYSTM:00/LNXSYBUS:00/ACPI0004:00/MSFT1000:00/<some GUID> My conclusion is that for some reason, the older system-udevd has a dependency on the /sys/bus/acpi/drivers directory being present. This isn't a kernel problem per se, but there's an implicit ABI of sorts that assumes the existence of the "drivers" directory. To confirm all this more precisely, I'm trying to find a clever way to get an strace of systemd-udevd when it errors out due to the missing "drivers" directory, and somehow relate that back to the git history of systemd-udevd. Thoughts? Michael > > --- > > > > v1 -> v2: > > * Address Sashiko feedback regarding possible leaks of references in two > > places: > > > > https://sashiko.dev/#/patchset/8753121.T7Z3S40VBb%40rafael.j.wysocki > > > > --- > > drivers/acpi/bus.c | 8 ----- > > drivers/base/bus.c | 72 > > ++++++++++++++++++++++++++++++++++----------- > > include/linux/device/bus.h | 1 > > 3 files changed, 57 insertions(+), 24 deletions(-) > > > > --- a/drivers/acpi/bus.c > > +++ b/drivers/acpi/bus.c > > @@ -1120,11 +1120,6 @@ EXPORT_SYMBOL_GPL(acpi_driver_match_devi > > ACPI Bus operations > > > > -------------------------------------------------------------------------- > > */ > > > > -static int acpi_bus_match(struct device *dev, const struct device_driver > > *drv) > > -{ > > - return 0; > > -} > > - > > static int acpi_device_uevent(const struct device *dev, struct > > kobj_uevent_env *env) > > { > > return __acpi_device_uevent_modalias(to_acpi_device(dev), env); > > @@ -1132,7 +1127,6 @@ static int acpi_device_uevent(const stru > > > > const struct bus_type acpi_bus_type = { > > .name = "acpi", > > - .match = acpi_bus_match, > > .uevent = acpi_device_uevent, > > }; > > > > @@ -1451,7 +1445,7 @@ static int __init acpi_bus_init(void) > > */ > > acpi_root_dir = proc_mkdir(ACPI_BUS_FILE_ROOT, NULL); > > > > - result = bus_register(&acpi_bus_type); > > + result = companion_bus_register(&acpi_bus_type); > > if (!result) > > return 0; > > > > --- a/drivers/base/bus.c > > +++ b/drivers/base/bus.c > > @@ -738,6 +738,11 @@ int bus_add_driver(struct device_driver > > if (!sp) > > return -EINVAL; > > > > + if (!sp->drivers_kset) { > > + error = -ENXIO; > > + goto out_put_bus; > > + } > > + > > /* > > * Reference in sp is now incremented and will be dropped when > > * the driver is removed from the bus > > @@ -930,15 +935,7 @@ static ssize_t bus_uevent_store(const st > > static struct bus_attribute bus_attr_uevent = __ATTR(uevent, 0200, NULL, > > bus_uevent_store); > > > > -/** > > - * bus_register - register a driver-core subsystem > > - * @bus: bus to register > > - * > > - * Once we have that, we register the bus with the kobject > > - * infrastructure, then register the children subsystems it has: > > - * the devices and drivers that belong to the subsystem. > > - */ > > -int bus_register(const struct bus_type *bus) > > +static int bus_register_internal(const struct bus_type *bus, bool > > use_drivers) > > { > > int retval; > > struct subsys_private *priv; > > @@ -960,7 +957,7 @@ int bus_register(const struct bus_type * > > > > bus_kobj->kset = bus_kset; > > bus_kobj->ktype = &bus_ktype; > > - priv->drivers_autoprobe = 1; > > + priv->drivers_autoprobe = use_drivers; > > > > retval = kset_register(&priv->subsys); > > if (retval) > > @@ -976,10 +973,12 @@ int bus_register(const struct bus_type * > > goto bus_devices_fail; > > } > > > > - priv->drivers_kset = kset_create_and_add("drivers", NULL, bus_kobj); > > - if (!priv->drivers_kset) { > > - retval = -ENOMEM; > > - goto bus_drivers_fail; > > + if (use_drivers) { > > + priv->drivers_kset = kset_create_and_add("drivers", NULL, > > bus_kobj); > > + if (!priv->drivers_kset) { > > + retval = -ENOMEM; > > + goto bus_drivers_fail; > > + } > > } > > > > INIT_LIST_HEAD(&priv->interfaces); > > @@ -989,9 +988,11 @@ int bus_register(const struct bus_type * > > klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put); > > klist_init(&priv->klist_drivers, NULL, NULL); > > > > - retval = add_probe_files(bus); > > - if (retval) > > - goto bus_probe_files_fail; > > + if (use_drivers) { > > + retval = add_probe_files(bus); > > + if (retval) > > + goto bus_probe_files_fail; > > + } > > > > retval = sysfs_create_groups(bus_kobj, bus->bus_groups); > > if (retval) > > @@ -1016,9 +1017,41 @@ out: > > kfree(priv); > > return retval; > > } > > + > > +/** > > + * bus_register - register a driver-core subsystem > > + * @bus: bus to register > > + * > > + * Once we have that, we register the bus with the kobject > > + * infrastructure, then register the children subsystems it has: > > + * the devices and drivers that belong to the subsystem. > > + */ > > +int bus_register(const struct bus_type *bus) > > +{ > > + return bus_register_internal(bus, true); > > +} > > EXPORT_SYMBOL_GPL(bus_register); > > > > /** > > + * companion_bus_register - register a companion bus type > > + * @bus: companion bus to register > > + * > > + * A companion bus is a bus without drivers. Devices that belong to it > > can be > > + * bound to other devices as their "companions" and represent interfaces > > that > > + * can be used by the drivers of those other devices. They may also be > > used for > > + * the enumeration of those other devices. > > + * > > + * The ACPI bus is a specific example of a companion bus. > > + * > > + * Registering a companion bus is like registering a regular bus except > > that it > > + * skips the creation of sysfs interfaces related to drivers for @bus. > > + */ > > +int companion_bus_register(const struct bus_type *bus) > > +{ > > + return bus_register_internal(bus, false); > > +} > > + > > +/** > > * bus_unregister - remove a bus from the system > > * @bus: bus. > > * > > @@ -1415,6 +1448,11 @@ struct device_driver *driver_find(const > > if (!sp) > > return NULL; > > > > + if (!sp->drivers_kset) { > > + subsys_put(sp); > > + return NULL; > > + } > > + > > k = kset_find_obj(sp->drivers_kset, name); > > subsys_put(sp); > > if (!k) > > --- a/include/linux/device/bus.h > > +++ b/include/linux/device/bus.h > > @@ -113,6 +113,7 @@ struct bus_type { > > bool need_parent_lock; > > }; > > > > +int __must_check companion_bus_register(const struct bus_type *bus); > > int __must_check bus_register(const struct bus_type *bus); > > > > void bus_unregister(const struct bus_type *bus); > > > > > >

