sysfs Device Attributes in Linux Kernel: DEVICE_ATTR Explained-Free Linux Device Drivers Course

sysfs Device Attributes in Linux Kernel: DEVICE_ATTR Explained

Learn how kernel drivers expose read/write files under sysfs using device_attribute, updated for modern 6.x kernels

Free Linux Kernel Course
Free Embedded Systems Course
Beginner Friendly

« Previous Lecture  |  Next Lecture »

Continuing our free Linux device drivers course, this tutorial covers sysfs device attributes in the Linux kernel, the mechanism drivers use to expose simple, human-readable read/write files under /sys. If you have ever run cat on a file in /sys/class or /sys/devices to read a hardware value, you have used a device attribute. By the end of this lecture you will be able to create your own sysfs attribute files the modern, kernel-recommended way.

What You Will Learn

What sysfs is and why it exists struct device_attribute internals DEVICE_ATTR_RW / RO / WO macros Writing safe show and store callbacks sysfs_emit vs old sprintf usage Security and best practices

Prerequisites

  • Completion of the previous lecture on platform devices
  • Basic understanding of the Linux Device Model (struct device)
  • Comfort building and loading kernel modules

What Is sysfs?

sysfs is a virtual, in-memory filesystem, normally mounted at /sys, that exposes the kernel’s internal device model to user space as plain files and directories. Every struct device registered with the driver core automatically gets a directory in sysfs, and drivers can add extra files inside that directory to expose tunable parameters, status values, or simple counters, all readable with ordinary tools like cat and echo.

Anatomy of struct device_attribute

Each sysfs file backed by a device attribute is described by a small structure containing the file’s name, its permission bits, and two callback function pointers: one for reads and one for writes.

device_attribute Structure Layout
attr (name + permissions)
show() — called on read
store() — called on write

Rather than filling this structure in by hand, the kernel provides convenience macros. The most commonly used ones today are:

MacroAccessRequires
DEVICE_ATTR_RO(name)Read onlyname_show()
DEVICE_ATTR_WO(name)Write onlyname_store()
DEVICE_ATTR_RW(name)Read and writename_show() and name_store()

Writing show() and store() Callbacks the Modern Way

Older kernel code frequently used plain sprintf() inside a show() callback. Current kernel guidelines recommend sysfs_emit() instead, since it automatically respects the one-page buffer limit that sysfs guarantees and avoids a class of buffer-overflow bugs. Here is an original example exposing a simple integer debug level as a read-write sysfs attribute.

#include <linux/device.h>
#include <linux/sysfs.h>

static int epdemo_debug_level;

static ssize_t debug_level_show(struct device *dev,
                                 struct device_attribute *attr,
                                 char *buf)
{
    return sysfs_emit(buf, "%d\n", epdemo_debug_level);
}

static ssize_t debug_level_store(struct device *dev,
                                  struct device_attribute *attr,
                                  const char *buf, size_t count)
{
    int val, ret;

    ret = kstrtoint(buf, 10, &val);
    if (ret)
        return ret;

    if (val < 0 || val > 9)
        return -EINVAL;

    epdemo_debug_level = val;
    return count;
}

static DEVICE_ATTR_RW(debug_level);

Note: Always validate input in store() with functions like kstrtoint() rather than trusting raw user input. Rejecting out-of-range values, as shown above, prevents the driver’s internal state from becoming inconsistent.

Creating and Removing the sysfs File

Once the attribute is defined, it must be attached to an actual device and cleaned up when the driver unloads.

ret = device_create_file(dev, &dev_attr_debug_level);
if (ret)
    dev_err(dev, "failed to create sysfs attribute\n");

/* On cleanup */
device_remove_file(dev, &dev_attr_debug_level);

Read/Write Flow Between User Space and Kernel

sysfs Read/Write Path
User runs cat / echo on sysfs file
↓
VFS routes call to sysfs
↓
Driver’s show() or store() callback runs

Real-World Use Cases

  • Exposing hardware sensor readings, such as temperature or voltage, as read-only attributes
  • Tunable driver parameters like debug levels, polling intervals, or thresholds
  • Triggering one-shot actions, such as a firmware reload, through a write-only attribute

Common Mistakes and Troubleshooting

MistakeFix
Using sprintf() in show()Use sysfs_emit() to respect the sysfs page-size buffer limit
Not validating store() inputUse kstrtoint()/kstrtoul() and reject invalid ranges
Forgetting device_remove_file() on unloadAlways pair create and remove calls
Setting overly permissive file modesOnly grant write access (0644 or stricter) where genuinely required

Best Practices

  • Prefer sysfs_emit() over sprintf()/snprintf() in every new show() callback
  • Keep each sysfs file limited to a single, simple value, following the sysfs “one value per file” convention
  • Always validate and range-check data received in store()
  • Use attribute groups (struct attribute_group) when a driver exposes many related files

Security Considerations

Any writable sysfs file is a potential entry point for a local attacker or a misbehaving user-space process. Never trust the contents of the buf parameter in store() blindly, always bound-check numeric input, and choose the narrowest file permission bits that satisfy the actual use case rather than defaulting to world-writable attributes.

Performance Considerations

sysfs callbacks should be lightweight and non-blocking wherever possible, since they may be called frequently by monitoring tools. Avoid heavy computation or long-running I/O directly inside show() or store(); offload such work to a workqueue if needed.

Summary: Key Takeaways

  • sysfs exposes kernel device state to user space as simple files
  • struct device_attribute pairs a file name with show()/store() callbacks
  • Use DEVICE_ATTR_RW/RO/WO macros instead of manual structure initialization
  • Prefer sysfs_emit() over sprintf() on modern kernels
  • Always validate store() input and remove attributes on driver unload

Frequently Asked Questions

Q1. What is a sysfs device attribute?
It is a small structure pairing a sysfs file name and permissions with read (show) and write (store) callback functions.

Q2. Why use sysfs_emit() instead of sprintf()?
sysfs_emit() automatically enforces the one-page buffer limit that sysfs guarantees, preventing buffer overruns.

Q3. What is the difference between DEVICE_ATTR_RW and DEVICE_ATTR_RO?
DEVICE_ATTR_RW creates a file with both show() and store() callbacks; DEVICE_ATTR_RO only implements show().

Q4. How many values should one sysfs file hold?
By convention, exactly one value per file, to keep parsing simple and predictable for user space.

Q5. How do I remove a sysfs attribute?
Call device_remove_file() with the same device and attribute pointers used at creation.

Q6. Is this part of a free course?
Yes, this lecture is part of EmbeddedPathashala’s free Linux kernel and embedded systems course.

Continue Learning Linux Kernel Programming

This lecture is part of EmbeddedPathashala’s free Linux kernel development course covering device drivers, embedded systems, and Bluetooth/BLE programming.

« Previous Lecture  |  Next Lecture »

2 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *