Poster of Linux kernelThe best gift for a Linux geek
 Linux kernel map 
⇦ prev ⇱ home next ⇨

14.2. Low-Level Sysfs Operations

Kobjects are the mechanism behind the sysfs virtual filesystem. For every directory found in sysfs, there is a kobject lurking somewhere within the kernel. Every kobject of interest also exports one or more attributes, which appear in that kobject's sysfs directory as files containing kernel-generated information. This section examines how kobjects and sysfs interact at a low level.

Code that works with sysfs should include <linux/sysfs.h>.

Getting a kobject to show up in sysfs is simply a matter of calling kobject_add. We have already seen that function as the way to add a kobject to a kset; creating entries in sysfs is also part of its job. There are a couple of things worth knowing about how the sysfs entry is created:

  • Sysfs entries for kobjects are always directories, so a call to kobject_add results in the creation of a directory in sysfs. Usually that directory contains one or more attributes; we see how attributes are specified shortly.

  • The name assigned to the kobject (with kobject_set_name) is the name used for the sysfs directory. Thus, kobjects that appear in the same part of the sysfs hierarchy must have unique names. Names assigned to kobjects should also be reasonable file names: they cannot contain the slash character, and the use of white space is strongly discouraged.

  • The sysfs entry is located in the directory corresponding to the kobject's parent pointer. If parent is NULL when kobject_add is called, it is set to the kobject embedded in the new kobject's kset; thus, the sysfs hierarchy usually matches the internal hierarchy created with ksets. If both parent and kset are NULL, the sysfs directory is created at the top level, which is almost certainly not what you want.

Using the mechanisms we have described so far, we can use a kobject to create an empty directory in sysfs. Usually, you want to do something a little more interesting than that, so it is time to look at the implementation of attributes.

14.2.1. Default Attributes

When created, every kobject is given a set of default attributes. These attributes are specified by way of the kobj_type structure. That structure, remember, looks like this:

struct kobj_type {
    void (*release)(struct kobject *);
    struct sysfs_ops *sysfs_ops;
    struct attribute **default_attrs;
};

The default_attrs field lists the attributes to be created for every kobject of this type, and sysfs_ops provides the methods to implement those attributes. We start with default_attrs, which points to an array of pointers to attribute structures:

struct attribute {
    char *name;
    struct module *owner;
    mode_t mode;
};

In this structure, name is the name of the attribute (as it appears within the kobject's sysfs directory), owner is a pointer to the module (if any) that is responsible for the implementation of this attribute, and mode is the protection bits that are to be applied to this attribute. The mode is usually S_IRUGO for read-only attributes; if the attribute is writable, you can toss in S_IWUSR to give write access to root only (the macros for modes are defined in <linux/stat.h>). The last entry in the default_attrs list must be zero-filled.

The default_attrs array says what the attributes are but does not tell sysfs how to actually implement those attributes. That task falls to the kobj_type->sysfs_ops field, which points to a structure defined as:

struct sysfs_ops {
    ssize_t (*show)(struct kobject *kobj, struct attribute *attr, 
                    char *buffer);
    ssize_t (*store)(struct kobject *kobj, struct attribute *attr, 
                     const char *buffer, size_t size);
};

Whenever an attribute is read from user space, the show method is called with a pointer to the kobject and the appropriate attribute structure. That method should encode the value of the given attribute into buffer, being sure not to overrun it (it is PAGE_SIZE bytes), and return the actual length of the returned data. The conventions for sysfs state that each attribute should contain a single, human-readable value; if you have a lot of information to return, you may want to consider splitting it into multiple attributes.

The same show method is used for all attributes associated with a given kobject. The attr pointer passed into the function can be used to determine which attribute is being requested. Some show methods include a series of tests on the attribute name. Other implementations embed the attribute structure within another structure that contains the information needed to return the attribute's value; in this case, container_of may be used within the show method to obtain a pointer to the embedding structure.

The store method is similar; it should decode the data stored in buffer (size contains the length of that data, which does not exceed PAGE_SIZE), store and respond to the new value in whatever way makes sense, and return the number of bytes actually decoded. The store method can be called only if the attribute's permissions allow writes. When writing a store method, never forget that you are receiving arbitrary information from user space; you should validate it very carefully before taking any action in response. If the incoming data does not match expectations, return a negative error value rather than possibly doing something unwanted and unrecoverable. If your device exports a self_destruct attribute, you should require that a specific string be written there to invoke that functionality; an accidental, random write should yield only an error.

14.2.2. Nondefault Attributes

In many cases, the kobject type's default_attrs field describes all the attributes that kobject will ever have. But that's not a restriction in the design; attributes can be added and removed to kobjects at will. If you wish to add a new attribute to a kobject's sysfs directory, simply fill in an attribute structure and pass it to:

int sysfs_create_file(struct kobject *kobj, struct attribute *attr);

If all goes well, the file is created with the name given in the attribute structure, and the return value is 0; otherwise, the usual negative error code is returned.

Note that the same show( ) and store( ) functions are called to implement operations on the new attribute. Before you add a new, nondefault attribute to a kobject, you should take whatever steps are necessary to ensure that those functions know how to implement that attribute.

To remove an attribute, call:

int sysfs_remove_file(struct kobject *kobj, struct attribute *attr);

After the call, the attribute no longer appears in the kobject's sysfs entry. Do be aware, however, that a user-space process could have an open file descriptor for that attribute and that show and store calls are still possible after the attribute has been removed.

14.2.3. Binary Attributes

The sysfs conventions call for all attributes to contain a single value in a human-readable text format. That said, there is an occasional, rare need for the creation of attributes that can handle larger chunks of binary data. That need really only comes about when data must be passed, untouched, between user space and the device. For example, uploading firmware to devices requires this feature. When such a device is encountered in the system, a user-space program can be started (via the hotplug mechanism); that program then passes the firmware code to the kernel via a binary sysfs attribute, as is shown in Section 14.8.1.

Binary attributes are described with a bin_attribute structure:

struct bin_attribute {
    struct attribute attr;
    size_t size;
    ssize_t (*read)(struct kobject *kobj, char *buffer, 
                    loff_t pos, size_t size);
    ssize_t (*write)(struct kobject *kobj, char *buffer, 
                    loff_t pos, size_t size);
};

Here, attr is an attribute structure giving the name, owner, and permissions for the binary attribute, and size is the maximum size of the binary attribute (or 0 if there is no maximum). The read and write methods work similarly to the normal char driver equivalents; they can be called multiple times for a single load with a maximum of one page worth of data in each call. There is no way for sysfs to signal the last of a set of write operations, so code implementing a binary attribute must be able to determine the end of the data some other way.

Binary attributes must be created explicitly; they cannot be set up as default attributes. To create a binary attribute, call:

int sysfs_create_bin_file(struct kobject *kobj, 
                          struct bin_attribute *attr);

Binary attributes can be removed with:

int sysfs_remove_bin_file(struct kobject *kobj, 
                          struct bin_attribute *attr);

14.2.4. Symbolic Links

The sysfs filesystem has the usual tree structure, reflecting the hierarchical organization of the kobjects it represents. The relationships between objects in the kernel are often more complicated than that, however. For example, one sysfs subtree (/sys/devices) represents all of the devices known to the system, while other subtrees (under /sys/bus) represent the device drivers. These trees do not, however, represent the relationships between the drivers and the devices they manage. Showing these additional relationships requires extra pointers which, in sysfs, are implemented through symbolic links.

Creating a symbolic link within sysfs is easy:

int sysfs_create_link(struct kobject *kobj, struct kobject *target,
                      char *name);

This function creates a link (called name) pointing to target's sysfs entry as an attribute of kobj. It is a relative link, so it works regardless of where sysfs is mounted on any particular system.

The link persists even if target is removed from the system. If you are creating symbolic links to other kobjects, you should probably have a way of knowing about changes to those kobjects, or some sort of assurance that the target kobjects will not disappear. The consequences (dead symbolic links within sysfs) are not particularly grave, but they are not representative of the best programming style and can cause confusion in user space.

Symbolic links can be removed with:

void sysfs_remove_link(struct kobject *kobj, char *name);

    ⇦ prev ⇱ home next ⇨
    Poster of Linux kernelThe best gift for a Linux geek