struct device

struct device is the lowest common denominator of the Linux device model: every piece of hardware in the system — a PCI card, a USB webcam, an I²C temperature sensor, a clock on a System-on-Chip (SoC) — is represented, somewhere, by an instance of it. The kerneldoc puts it plainly: “At the lowest level, every device in a Linux system is represented by an instance of struct device” (device.h, v6.12). It is the node the driver core threads onto the unified object graph — it carries the kobject that gives it a reference count and a /sys directory, the back-pointers to the bus it sits on and the driver bound to it, the dev_t that mints a /dev node, the DMA masks the device’s hardware imposes, and the power-management state the suspend/resume machinery walks. Crucially, you almost never see a bare struct device: it is embedded inside a larger, bus-specific structure (struct pci_dev, struct platform_device, struct usb_device), and code climbs from the generic device back up to the specific type with container_of.

This note covers the data structure and its registration lifecycle. Its functional grouping cousin — struct class — is in Device Classes; the bus it points at is in struct bus_type and Bus Registration; the attribute files it sprouts in sysfs are in Device Attributes and sysfs Files; and the whole model it belongs to is in The Linux Device Model.

Mental Model

Think of struct device as a standard electrical connector bolted onto every device-specific structure. The connector is identical everywhere, so the driver core can plug any device into the same machinery: refcount it, give it a sysfs directory, match it to a driver, suspend it. The device-specific structure around it (a pci_dev knows about BARs and config space; a platform_device knows about resources and a Device-Tree node) is the part the bus layer cares about. The generic core only ever touches the connector.

flowchart TB
  subgraph WRAP["Bus-specific wrapper (one allocation)"]
    direction TB
    PCI["struct pci_dev<br/>vendor, device, BARs, config space"]
    DEV["struct device dev<br/>(embedded — the 'connector')"]
    PCI -.->|"contains"| DEV
  end
  DEV -->|"kobj"| KO["struct kobject<br/>refcount + /sys dir"]
  DEV -->|"bus"| BUS["struct bus_type<br/>(pci_bus_type)"]
  DEV -->|"driver"| DRV["struct device_driver<br/>(once bound)"]
  DEV -->|"class"| CLS["struct class<br/>(optional functional group)"]
  DEV -->|"devt"| NODE["/dev node<br/>(if MAJOR(devt) != 0)"]
  DEV -->|"parent"| PARENT["parent struct device<br/>(forms the tree)"]
  DEV -->|"power"| PM["struct dev_pm_info<br/>suspend/resume/runtime PM"]
  DRV -.->|"to_pci_dev(dev) = container_of"| PCI

The embedded-connector model. What it shows: a single allocation of struct pci_dev contains a struct device field; the generic driver core only ever holds a struct device *, while the PCI bus layer recovers the full pci_dev with container_of. The device fans out to a kobject (refcount + sysfs), its bus, its bound driver, an optional class, an optional /dev node, its parent in the tree, and its power state. The insight: the generic core and the bus layer share the same memory but see different “shapes” of it — that is exactly what lets one device model serve PCI, USB, platform, and I²C without the core knowing anything bus-specific.

What the Structure Actually Contains

The full definition lives at struct device in device.h (line 721 in v6.12). It is large — dozens of fields, many guarded by #ifdef CONFIG_* — but a handful do the conceptual heavy lifting. Walking them in the order they matter:

struct kobject kobj — the very first field. A kobject is the kernel’s generic reference-counted object: it carries a kref (the atomic reference count), a name, a parent pointer, and the hook that makes a directory appear under /sys. Because the device embeds a kobject, the device inherits all of that for free: get_device()/put_device() are thin wrappers over kobject_get()/kobject_put(), and the device’s sysfs directory is the kobject’s directory. This is the single most important structural fact about the device model — see The Linux Device Model for why “everything is a kobject” is the organizing principle.

struct device *parent — the device this one is attached to, “in most cases … some sort of bus or host controller” (kerneldoc). A USB mouse’s parent is the USB hub port; the hub’s parent is the USB controller; the controller’s parent is a PCI device; and so on up to a virtual root. A NULL parent makes the device top-level — “not usually what you want,” the docs warn. These parent links are what give /sys/devices/ its deep tree shape and what the power-management core walks to suspend children before parents. The parent/child relationship is detailed in The Device Hierarchy and Parent-Child Relationships.

const struct bus_type *bus — which bus this device sits on (pci, usb, i2c, platform, …). The bus owns the .match() and .probe() logic that pairs the device with a driver. See struct bus_type and Bus Registration.

struct device_driver *driver — which driver is currently bound, or NULL if none. Set by the core when .probe() succeeds, cleared on unbind. A subtlety the kernel itself flags: this pointer “can change to NULL underneath us because of unbinding,” so the core reads it with READ_ONCE() in dev_driver_string() (core.c). Driver binding is covered in Driver Binding and the Probe Flow.

void *driver_data — a single opaque pointer the bound driver owns. The core “doesn’t touch it.” This is where a driver stashes its per-device private state — typically a pointer to a kzalloc’d struct holding register base addresses, IRQ numbers, ring buffers, whatever. It is set and read only through the accessors dev_set_drvdata()/dev_get_drvdata() (more below); driver code is not supposed to poke dev->driver_data directly.

void *platform_data — legacy board-specific data, distinct from driver_data. On embedded and SoC systems before Device Tree took over, board files pointed platform_data at structures describing wiring (“what ports are available, chip variants, which GPIO pins act in what additional roles”). It shrank the “Board Support Packages” of the pre-DT era. Modern code prefers Device-Tree / fwnode properties over platform_data — see Platform Devices and Drivers.

dev_t devt — the device number (a packed major:minor, dev_t). If non-zero (MAJOR(devt) != 0), the core treats this device as having a character/block node: during device_add() it creates the sysfs dev file and calls devtmpfs_create_node() to materialize the /dev entry. If devt is 0:0 there is no /dev node. The major/minor split is the subject of Major and Minor Numbers; the kernel-maintained /dev is devtmpfs and the dev Directory.

const struct class *class — an optional pointer to the device’s functional class (e.g. net, block, tty, leds). When set, the device gets a symlink under /sys/class/<name>/, and udev keys off it. A device can be on a bus, in a class, or both.

const struct attribute_group **groups — a NULL-terminated array of attribute groups. The core creates these sysfs files before it fires the KOBJ_ADD uevent, which is precisely why drivers must declare attributes through groups (or dev_groups on the class/bus) rather than calling device_create_file() later — userspace would never be notified of the late files (device.rst). The attribute machinery is Device Attributes and sysfs Files.

struct device_node *of_node / struct fwnode_handle *fwnode — the firmware description of this device. of_node is the Device-Tree node (Open Firmware lineage, hence “of”); fwnode is the firmware-agnostic handle that abstracts over Device Tree and ACPI, so generic property code (device_property_read_u32() etc.) works regardless of the firmware type. See The Unified Device Property Interface fwnode.

u64 *dma_mask, u64 coherent_dma_mask, const struct dma_map_ops *dma_ops, u64 bus_dma_limit — the DMA addressing constraints of the hardware. dma_mask is the range of physical (or IOMMU-translated) addresses the device can reach for streaming mappings; coherent_dma_mask the same for coherent allocations (it is separate because “not all hardware supports 64-bit addresses for consistent allocations”). dma_ops is the per-device set of map/unmap callbacks the DMA API dispatches through. These fields are why the DMA API needs only a struct device * to know how a given device can talk to RAM.

struct dev_pm_info power / struct dev_pm_domain *pm_domain — the power-management state block. power holds runtime-PM counters, the suspend/resume status, wakeup info; pm_domain plugs in extra callbacks for a power domain. The PM core walks the device tree using these. See Runtime Power Management in Drivers and Linux Power Management MOC.

struct dev_links_info links — the supplier/consumer device links for this device, making probe-, suspend-, and shutdown-ordering dependencies explicit rather than guessed from the parent tree.

void (*release)(struct device *dev) — the destructor, invoked when the last reference is dropped (the kobject’s refcount hits zero). This must be set by whoever allocated the device — typically the bus driver that discovered it — and is where the wrapping structure is freed. The kernel is emphatic that you never kfree() a device directly; you put_device() and let release run when the refcount truly reaches zero, because outstanding references (a sysfs reader, an open file) can outlive your code’s interest in the object.

const struct device_type *type — an optional pointer to a struct device_type, a lighter-weight grouping than a class that bundles shared groups, a uevent callback, a devnode callback, a release, and pm ops. The PCI subsystem, for example, uses device types to distinguish a regular device from a bridge.

Uncertain

Verify: the precise set of #ifdef-guarded DMA fields (dma_io_tlb_pools, dma_iommu, dma_skip_sync) present in 6.18 LTS, and whether any field above was renamed/added between 6.12 and 6.18. Reason: the field walk here is pinned to the v6.12 source blob; the DMA-related members in particular churn release-to-release. To resolve: diff include/linux/device.h between the v6.12 and v6.18 tags. uncertain

Embedding: the Container-of Pattern

The kerneldoc states the rule directly: “it is rare for devices to be represented by bare device structures; instead, that structure, like kobject structures, is usually embedded within a higher-level representation of the device” (device.h). Concretely, struct platform_device (platform_device.h, v6.12) looks like:

struct platform_device {
        const char      *name;
        int             id;
        bool            id_auto;
        struct device   dev;            /* <-- the embedded generic device */
        u64             platform_dma_mask;
        struct device_dma_parameters dma_parms;
        u32             num_resources;
        struct resource *resource;
        ...
};

struct device dev is statically embedded (not a pointer), so discovering a device is a single allocation — the overview doc calls this out for pci_dev as well: “only one allocation on device discovery.” To recover the platform_device from a generic struct device *, the bus layer uses the container_of upcast wrapped in a named helper:

#define to_platform_device(x) container_of((x), struct platform_device, dev)

container_of(ptr, type, member) computes (char *)ptr - offsetof(type, member) and casts the result — given a pointer to the dev member, it returns a pointer to the enclosing struct. Two deliberate design choices flow from this. First, the struct device is not at offset zero of the wrapper (“not necessarily defined at the front”) — the overview doc says this is “to make people think about what they’re doing … and to discourage meaningless and incorrect casts.” Second, device drivers should not reach through dev into generic fields; only the bus layer does, so that “when a field was renamed or removed, every downstream driver” does not break — only the one bus layer changes. This is the encapsulation that lets the device model evolve.

The same shape recurs everywhere: to_pci_dev(dev), to_usb_device(dev), to_i2c_client(dev). Learn it once for struct device and it generalizes to every kobject-embedding structure in the kernel.

The Registration Lifecycle: initialize → add → register

A device comes to life in two steps, which device_register() simply chains (core.c):

int device_register(struct device *dev)
{
        device_initialize(dev);
        return device_add(dev);
}

device_initialize(dev) sets up the in-memory object without making it visible. It assigns the device to the devices_kset, calls kobject_init() (which sets the refcount to 1 and wires the device_ktype), initializes the dma_pools list, the mutex, the devres spinlock and list, the PM state (device_pm_init), the NUMA node, and the supplier/consumer link lists. After this the device has a refcount and is a valid object, but it is not in any list, has no sysfs directory, and fires no uevent.

device_add(dev) is where the device becomes real and visible. Its essential sequence (eliding the error-unwind labels):

  1. Resolve the device’s name: from init_name, or from dev->bus->dev_name + id, failing with -EINVAL if neither yields a name.
  2. Get the parent reference and compute the kobject parent (get_device_parent), then kobject_add()this is the moment the /sys directory appears.
  3. Create the uevent attribute file, then device_add_class_symlinks() (the /sys/class/<class>/<name> symlink for classed devices), then device_add_attrs() (the groups files).
  4. bus_add_device() — link the device into its bus’s device list and create the bus symlinks.
  5. dpm_sysfs_add() + device_pm_add() — register with the power-management core.
  6. If MAJOR(dev->devt) is set: create the dev attribute file, the /sys/dev/... entry, and call devtmpfs_create_node() to make the /dev node.
  7. bus_notify(BUS_NOTIFY_ADD_DEVICE) then kobject_uevent(&dev->kobj, KOBJ_ADD) — broadcast the “new device” uevent to userspace (udev). All attributes must already exist by now, which is why steps 3–6 precede this.
  8. Process firmware device links (fw_devlink_link_device), then bus_probe_device(dev) — kick off driver matching and probe.
  9. Add the device to its parent’s children list and to its class’s device list, notifying any registered class_interfaces.

The two-step split exists, the kerneldoc explains, for the rare case where you need to “use and refcount the device before it is added to the hierarchy” — most code just calls device_register().

Teardown mirrors this: device_unregister() = device_del() + put_device(). device_del() removes the sysfs files, fires KOBJ_REMOVE, unbinds the driver, and unlinks from bus/class/parent lists; put_device() drops the reference taken at init. The object is only actually freed (via release) when the last reference goes — the kerneldoc’s “rule of thumb: if device_add() succeeds, call device_del(); if it has not succeeded, use only put_device().”

Driver Data Accessors and Device Logging

Two small but ubiquitous API families round out the structure.

Driver-private data is read and written exclusively through trivial inline accessors (device.h):

static inline void *dev_get_drvdata(const struct device *dev)
{
        return dev->driver_data;
}
static inline void dev_set_drvdata(struct device *dev, void *data)
{
        dev->driver_data = data;
}

The indirection looks pointless — they just touch dev->driver_data — but it is the encapsulation contract: drivers go through the accessors so the field can move or grow logic later without touching every driver. A typical .probe() does priv = devm_kzalloc(...); dev_set_drvdata(dev, priv); and every other callback recovers state with priv = dev_get_drvdata(dev);. (Bus-specific wrappers exist too: platform_set_drvdata(), pci_set_drvdata() ultimately call dev_set_drvdata() on the embedded device.)

Device-aware logging is the dev_printk family (dev_printk.h, v6.12). Instead of bare printk, drivers use:

dev_emerg(dev, fmt, ...)    /* KERN_EMERG  */
dev_alert(dev, fmt, ...)    /* KERN_ALERT  */
dev_crit(dev, fmt, ...)     /* KERN_CRIT   */
dev_err(dev, fmt, ...)      /* KERN_ERR    */
dev_warn(dev, fmt, ...)     /* KERN_WARNING*/
dev_notice(dev, fmt, ...)   /* KERN_NOTICE */
dev_info(dev, fmt, ...)     /* KERN_INFO   */
dev_dbg(dev, fmt, ...)      /* dynamic debug */

The payoff is the automatic prefix. The core’s __dev_printk() formats the line as "%s %s: <message>" using dev_driver_string(dev) and dev_name(dev) (core.c). dev_driver_string() returns the bound driver’s name if any, else the bus name, else the class name. So dev_err(&pdev->dev, "init failed: %d\n", ret) produces a kernel-log line like:

mydriver 10000000.serial: init failed: -22

— self-identifying, greppable, and unambiguous even when fifty identical devices exist. This is why dev_err(dev, ...) is strongly preferred over pr_err(...) in driver code. The dev_* macros also feed structured metadata (subsystem, device) into the printk record via dev_printk_emit(), which userspace log tooling can filter on.

Failure Modes and Common Misunderstandings

Freeing a device with kfree() instead of put_device(). The single most dangerous mistake. References to a device can outlive your driver’s interest — a userspace process with /sys/.../some_attr open, a sibling holding a device link. If you kfree() the wrapper while a reference exists, the next put_device() runs release on freed memory (use-after-free). The discipline: set dev->release to free the wrapper, and only ever put_device().

Forgetting to set release. If device_register() succeeds but dev->release is NULL, the core emits a loud warning (“Device ’…’ does not have a release() function, it is broken and must be fixed”) when the device is removed, and leaks the memory. Every dynamically allocated device needs a release callback; helpers like device_create() set one for you.

Adding attributes after KOBJ_ADD. Calling device_create_file() in .probe() (after the device is registered) creates sysfs files that udev never hears about, because the KOBJ_ADD uevent already fired. The device.rst doc warns of exactly this: “userspace won’t get notified and userspace will not know about the new attributes.” The fix is to attach attributes via the groups/dev_groups pointer so they exist before the uevent.

Touching generic struct device fields from a leaf driver. Reaching dev->kobj or dev->p from an ordinary PCI driver couples that driver to driver-core internals the overview doc explicitly tells you to leave to the bus layer. It compiles, but it breaks the moment the core refactors a field.

Reading dev->driver without care. Because unbind can NULL it concurrently, code that dereferences dev->driver outside the device lock can race; the core itself uses READ_ONCE().

Alternatives and When You Touch It Directly

Most driver authors never call device_register() themselves — they register a struct pci_driver, struct platform_driver, or struct i2c_driver, and the bus core creates and registers the embedded struct device when it enumerates hardware. You receive a fully-formed struct device * in your .probe(). You allocate and register a device manually only when you are:

  • Writing a bus or class core that discovers devices (the PCI layer minting a pci_dev per slot).
  • Creating a synthetic device — e.g. device_create() to get a /dev node and a /sys/class entry without a real bus underneath (the misc and many char-device frameworks do this; see The miscdevice Framework for the most ergonomic path to a single character device).
  • Using root_device_register() to create a virtual parent under /sys/devices/ for a family of synthetic devices.

When you only need one character device node, the miscdevice framework hides almost all of this — you fill a struct miscdevice and the framework handles device_create() and the minor allocation. Reach for raw struct device registration only when miscdevice or a real bus does not fit.

Production Notes

In real subsystems the registration is wrapped but the struct device is unmistakable. The character-memory driver drivers/char/mem.c (v6.12) registers a class and then, for each minor, calls device_create(&mem_class, NULL, MKDEV(MEM_MAJOR, minor), NULL, devlist[minor].name) — that single call allocates a struct device, sets its class, devt, and release, and runs the full device_add() flow, producing /dev/null, /dev/zero, /dev/mem, and friends along with their /sys/class/mem/* entries. The LED subsystem (drivers/leds/led-class.c) does the same via device_create_with_groups(), attaching per-LED attribute groups in the same atomic step.

The modern allocation idiom inside .probe() pairs struct device with managed resources: devm_kzalloc(dev, ...) for private state and devm_* for IRQs, clocks, and mappings, all tied to the device’s bound lifetime so they auto-release on unbind (Managed Device Resources devres). This is why correct manual error-path cleanup matters far less in 6.x driver code than it did a decade ago — the device’s devres_head list does the unwinding.

See Also