Linux /sys/bus Explained: Kernel Device Bus Architecture and Practical Exploration
The article delves into the Linux /sys/bus directory of the sysfs virtual file system, detailing how the kernel registers bus types, organizes devices and drivers, explains core structures like struct bus_type, walks through the device‑add and driver‑probe workflow with concrete USB examples, and demonstrates practical inspection using NanoCode.
/sys/bus: organization by bus type
/sys/bus is a top‑level directory of the sysfs virtual file system that exposes every bus type registered in the kernel (e.g., usb, pci, platform). Each bus type is created by a struct bus_type instance registered with bus_register().
amba cec
clockevents clocksource
container cpu
event_source gadget
genpd gpio
hid i2c
iio mdio_bus
media mipi-dsi
mmc mmc_rpmb
nvmem pci
pci_express platform
scmi_protocol scsi
sdio snd_seq
soc spi
tee typec
usb workqueueEntering a specific bus directory (e.g., /sys/bus/usb) shows the standard layout:
geduer@ulan:/sys/bus$ cd /sys/bus/usb
geduer@ulan:/sys/bus/usb$ ls
devices drivers drivers_autoprobe drivers_probe ueventThe devices subdirectory contains symbolic links to the real device nodes under /sys/devices. For USB, entries such as 5-1:1.0 and usb5 correspond to interface devices and the root hub:
1-0:1.0 -> ../../../devices/platform/usbdrd3_1/fc400000.usb/xhci-hcd.6.auto/usb1/1-0:1.0
2-0:1.0 -> ../../../devices/platform/usbdrd3_1/fc400000.usb/xhci-hcd.6.auto/usb2/2-0:1.0
…
usb5 -> ../../../devices/platform/fc880000.usb/usb5The drivers subdirectory holds a directory per registered driver (e.g., usbhid, hub) and usually provides bind and unbind interfaces.
drwxr-xr-x 2 root root 0 Nov 5 17:35 asix
drwxr-xr-x 2 root root 0 Nov 5 17:35 cdc_acm
…
drwxr-xr-x 2 root root 0 Nov 5 17:35 usbtouchscreenKernel perspective: how /sys/bus is built
Core structure: struct bus_type
Each bus type is represented by a struct bus_type instance that defines the bus identity, matching rules, lifecycle callbacks, sysfs attributes and power‑management behavior. Using NanoCode, the structure can be inspected; for example, to view the spi_bus_type:
Run x lk!spi_bus_type to obtain the address.
Run dt lk!bus_type 0xffff8000`0aae35f8 to dump fields such as .name, .match, .probe, etc.
Bus registration: bus_register()
bus_register()registers a new bus type, creates the corresponding sysfs entries and links the bus into the global bus_kset.
kset vs. klist
kset manages the sysfs hierarchy visible to user space, creating devices and drivers directories under /sys/bus/xxx.
klist manages internal kernel object lists with reference‑count protection, enabling safe traversal during hot‑plug, driver binding and module unload.
During bus registration the kernel:
Associates the bus’s kobject with the global bus_kset.
Creates child ksets devices_kset and drivers_kset for device and driver nodes.
Initialises klist_devices and klist_drivers for runtime tracking.
Device‑driver matching based on the bus
When a device is added, device_add() calls bus_probe_device(), which delegates matching to the bus’s .match function. If drivers_autoprobe is enabled, the kernel attempts to bind a suitable driver. driver_match_device() invokes the bus’s match(drv, dev) function.
On a successful match, driver_probe_device() executes the driver’s probe method via the chain
driver_probe_device() → __driver_probe_device() → really_probe() → call_driver_probe().
For a USB storage device the flow is: storage_probe() (USB storage driver entry) performs protocol selection, applies vendor quirks, and schedules an asynchronous thread.
The thread eventually calls sd_probe() in the SCSI subsystem, which creates scsi_disk and gendisk, queries capacity via SCSI commands, and registers the block device with device_add_disk(), making /dev/sdX visible.
NanoCode practice
A NanoCode script recursively traverses all bus directories under /sys/bus and lists each bus’s devices and drivers. A shortened excerpt for the USB bus:
usb5 0xffff0001078388a8 [usb: 0xffff80000aaed490]
│ │ │ │
▼ ▼ ▼ ▼
DeviceName DeviceAddress DriverName DriverAddressFor the USB bus, the devices column lists entries such as usb5 with their kernel addresses, while the drivers column shows driver names like usbfs and their addresses.
usb5 0xffff0001078388a8
│ │
▼ ▼
DeviceName DeviceAddress
usbfs 0xffff80000aaed318
│ │
▼ ▼
DriverName DriverAddressUsing NanoCode’s dt command on a device address (e.g., dt lk!device 0xffff0001078388a8) displays the full struct device layout.
Command output for the USB bus is presented as a tree:
usb5 0xffff0001078388a8 [usb: 0xffff80000aaed490]
│ │ │ │
▼ ▼ ▼ ▼
设备名 设备地址(device) 关联的驱动名 关联驱动地址(device_driver) usbfs 0xffff80000aaed318
│ │
▼ ▼
驱动名 驱动地址(device_driver)Running dt lk!device 0xffff0001078388a8 shows the complete struct device fields.
Summary of the matching mechanism
The kernel does not let a device search for a driver; instead, the bus object actively matches devices to drivers using its .match callback, then invokes the driver’s probe via the call chain shown above. In the USB storage example, storage_probe() sets up an asynchronous task, sd_probe() creates the SCSI disk structures, and device_add_disk() registers the block device, exposing /dev/sdX to user space.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
