In The Zen of Kobjects, this
series looked at the kobject abstraction and the various interfaces that go
with it. That article, however, glossed over one important part of the
kobject structure (with a promise to fill in in later): its interface to
the sysfs virtual filesystem. The time
has come to fulfill our promise, however, and look at how sysfs works at
the lower levels.
To use the functions described below, you will need to include both
<linux/kobject.h> and <linux/sysfs.h> in your
source files.
How kobjects get sysfs entries
As we saw in the previous article, there are two functions which are used
to set up a kobject. If you use kobject_init() by itself, you
will get a standalone kobject with no representation in sysfs. If,
instead, you use kobject_register() (or call
kobject_add() separately), a sysfs directory will be created for
the kobject; no other effort is required on the programmer's part.
The name of the directory will be the same as the name given to the kobject
itself. The location within sysfs will reflect the kobject's position in
the hierarchy you have created. In short: the kobject's directory will be
found in its parent's directory, as determined by the kobject's
parent field. If you have not explicitly set the parent field,
but you have set its kset pointer, then the kset will become the
kobject's parent. If there is no parent and no kset, the kobject's
directory will become a top-level directory within sysfs, which is rarely
what you really want.
Populating a kobject's directory
Getting a sysfs directory corresponding to a kobject is easy, as we have
seen. That directory will be empty, however, which is not particularly
useful. Most applications will want the kobject's sysfs entry to contain
one or more attributes with useful information. Creating those attributes
requires some additional steps, but is not all that hard.
The key to sysfs attributes is the kobject's kobj_type pointer.
When we looked at kobject types before, we passed over a couple of
sysfs-related entries. One, called default_attrs, describes the
attributes that all kobjects of this type should have; it is a pointer 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
will appear within sysfs), owner is a pointer to the module (if any)
which is responsible for the implementation of this attribute, and
mode is the protection bits which 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 last entry in the default_attrs
list must be NULL.
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);
};
These functions will be called for each read and write operation,
respectively, on an attribute of a kobject of the given type. In each
case, kobj is the kobject whose attribute is being accessed,
attr is the struct attribute for the specific attribute,
and buffer is a one-page buffer for attribute data.
The show() function should encode the attribute's full value into
buffer, being sure not to overrun PAGE_SIZE. Remember
that the sysfs convention requires that attributes contain single values
or, at most, an array of similar values, so the one-page limit should never
be a problem. The return value is, of course, the number of bytes of data
actually put into buffer or a negative error code.
The store() function has a similar interface; the additional
size parameter gives the length of the data received from user
space. Never forget that buffer contains unchecked, user-supplied
data; treat it carefully and be sure that it fits whatever format you
require. The return value should normally be the same as size,
unless something has gone wrong.
As you can see, sysfs requires the use of a single set of show()
and store() functions for all attributes of kobjects of the same
type. Those functions will, usually, maintain their own array of attribute
information to enable them to find the real function charged with
implementing each attribute.
Non-default attributes
In many cases, the kobject type's default_attrs field describes
all of the attributes that kobject will ever have. It does not need to be
that way, however; attributes can be added and removed 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 will be created with the name given in the
attribute structure and the return value will be zero; otherwise,
the usual negative error code is returned.
Note that the same show() and store() functions will be
called to implement operations on the new attribute. Before you add a new,
non-default 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 will no longer appear 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.
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 is often more complicated than that,
however. For example, one sysfs subtree (/sys/devices) represents
all of the devices known to the
system, while others represent the device drivers. These trees do not,
however, represent the relationships between the drivers and the devices
they implement. Showing these additional relationships requires extra
pointers which, in sysfs, are implemented with symbolic links.
Creating a symbolic link within sysfs is easy:
int sysfs_create_link(struct kobject *kobj,
struct kobject *target,
char *name);
This function will create a link (called name) pointing to
target's sysfs entry as an attribute of kobj. It will be
a relative link, so it works regardless of where sysfs is mounted on any
particular system.
The link will persist 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
would not do much to create confidence in the proper functioning of the
system either.
Symbolic links can be removed with:
void sysfs_remove_link(struct kobject *kobj, char *name);
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 which can handle larger chunks of binary
data. In the 2.6.0-test kernel, the only use of binary attributes is in
the firmware subsystem. When a device requiring firmware 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
binary sysfs attribute. If you are contemplating any other use of binary
attributes, you should think carefully and be sure there is no other way to
accomplish your objective.
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 zero if there is no maximum). The
read() and write() functions 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 that 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);
Last notes
This article has described the low-level interface between kobjects and
sysfs. Unless you are implementing a new subsystem, however, you are
unlikely to work with this interface directly. Each subsystem typically
implements its own set of default attributes, and, perhaps, a mechanism for
interested code to add new ones. This mechanism is generally a
straightforward wrapper around the low-level attribute code, however, so it
should look familiar to readers of this page. No comments have been posted.
Post one now
|