요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. SPDX-License-Identifier: GPL-2.0
=====================================================
sysfs - _The_ filesystem for exporting kernel objects
=====================================================
Patrick Mochel <[email protected]>
Mike Murphy <[email protected]>
:Revised: 16 August 2011
:Original: 10 January 2003
What it is
~~~~~~~~~~
sysfs is a RAM-based filesystem initially based on ramfs. It provides
a means to export kernel data structures, their attributes, and the
linkages between them to userspace.
sysfs is tied inherently to the kobject infrastructure. Please read
Documentation/core-api/kobject.rst for more information concerning the kobject
interface.
Using sysfs
~~~~~~~~~~~
sysfs is always compiled in if CONFIG_SYSFS is defined. You can access
it by doing::
mount -t sysfs sysfs /sys
Directory Creation
~~~~~~~~~~~~~~~~~~
For every kobject that is registered with the system, a directory is
created for it in sysfs. That directory is created as a subdirectory
of the kobject's parent, expressing internal object hierarchies to
userspace. Top-level directories in sysfs represent the common
ancestors of object hierarchies; i.e. the subsystems the objects
belong to.
sysfs internally stores a pointer to the kobject that implements a
directory in the kernfs_node object associated with the directory. In
the past this kobject pointer has been used by sysfs to do reference
counting directly on the kobject whenever the file is opened or closed.
With the current sysfs implementation the kobject reference count is
only modified directly by the function sysfs_schedule_callback().
Attributes
~~~~~~~~~~
Attributes can be exported for kobjects in the form of regular files in
the filesystem. sysfs forwards file I/O operations to methods defined
for the attributes, providing a means to read and write kernel
attributes.
Attributes should be ASCII text files, preferably with only one value
per file. It is noted that it may not be efficient to contain only one
value per file, so it is socially acceptable to express an array of
values of the same type.
Mixing types, expressing multiple lines of data, and doing fancy
formatting of data is heavily frowned upon. Doing these things may get
you publicly humiliated and your code rewritten without notice.
An attribute definition is simply::
struct attribute {
char *name;
struct module *owner;
umode_t mode;
};
int sysfs_create_file(struct kobject * kobj, const struct attribute * attr);
void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr);
A bare attribute contains no means to read or write the value of the
attribute. Subsystems are encouraged to define their own attribute
structure and wrapper functions for adding and removing attributes for
a specific object type.
For example, the driver model defines struct device_attribute like::
struct device_attribute {
struct attribute attr;
ssize_t (*show)(struct device *dev, struct device_attribute *attr,
char *buf);
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);
};
int device_create_file(struct device *, const struct device_attribute *);
void device_remove_file(struct device *, const struct device_attribute *);
It also defines this helper for defining device attributes::
#define DEVICE_ATTR(_name, _mode, _show, _store) \
struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
For example, declaring::
static DEVICE_ATTR(foo, S_IWUSR | S_IRUGO, show_foo, store_foo);
is equivalent to doing::
static struct device_attribute dev_attr_foo = {
.attr = {
.name = "foo",
.mode = S_IWUSR | S_IRUGO,
},
.show = show_foo,
.store = store_foo,
};
Note as stated in include/linux/kernel.h "OTHER_WRITABLE? Generally
considered a bad idea." so trying to set a sysfs file writable for
everyone will fail reverting to RO mode for "Others".
For the common cases sysfs.h provides convenience macros to make
defining attributes easier as well as making code more concise and
readable. The above case could be shortened to:
static struct device_attribute dev_attr_foo = __ATTR_RW(foo);
the list of helpers available to define your wrapper function is:
__ATTR_RO(name):
assumes default name_show and mode 0444
__ATTR_WO(name):
assumes a name_store only and is restricted to mode
0200 that is root write access only.
__ATTR_RO_MODE(name, mode):
for more restrictive RO access; currently
only use case is the EFI System Resource Table
(see drivers/firmware/efi/esrt.c)
__ATTR_RW(name):
assumes default name_show, name_store and setting
mode to 0644.
__ATTR_NULL:
which sets the name to NULL and is used as end of list
indicator (see: kernel/workqueue.c)
Subsystem-Specific Callbacks
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When a subsystem defines a new attribute type, it must implement a
set of sysfs operations for forwarding read and write calls to the
show and store methods of the attribute owners::
struct sysfs_ops {
ssize_t (*show)(struct kobject *, struct attribute *, char *);
ssize_t (*store)(struct kobject *, struct attribute *, const char *, size_t);
};
[ Subsystems should have already defined a struct kobj_type as a
descriptor for this type, which is where the sysfs_ops pointer is
stored. See the kobject documentation for more information. ]
When a file is read or written, sysfs calls the appropriate method
for the type. The method then translates the generic struct kobject
and struct attribute pointers to the appropriate pointer types, and
calls the associated methods.
To illustrate::
#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr)
static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
struct device_attribute *dev_attr = to_dev_attr(attr);
struct device *dev = kobj_to_dev(kobj);
ssize_t ret = -EIO;
if (dev_attr->show)
ret = dev_attr->show(dev, dev_attr, buf);
if (ret >= (ssize_t)PAGE_SIZE) {
printk("dev_attr_show: %pS returned bad count\n",
dev_attr->show);
}
return ret;
}
Reading/Writing Attribute Data
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To read or write attributes, show() or store() methods must be
specified when declaring the attribute. The method types should be as
simple as those defined for device attributes::
ssize_t (*show)(struct device *dev, struct device_attribute *attr, char *buf);
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);
IOW, they should take only an object, an attribute, and a buffer as parameters.
sysfs allocates a buffer of size (PAGE_SIZE) and passes it to the
method. sysfs will call the method exactly once for each read or
write. This forces the following behavior on the method
implementations:
- On read(2), the show() method should fill the entire buffer.
Recall that an attribute should only be exporting one value, or an
array of similar values, so this shouldn't be that expensive.
This allows userspace to do partial reads and forward seeks
arbitrarily over the entire file at will. If userspace seeks back to
zero or does a pread(2) with an offset of '0' the show() method will
be called again, rearmed, to fill the buffer.
- On write(2), sysfs expects the entire buffer to be passed during the
first write. sysfs then passes the entire buffer to the store() method.
A terminating null is added after the data on stores. This makes
functions like sysfs_streq() safe to use.
When writing sysfs files, userspace processes should first read the
entire file, modify the values it wishes to change, then write the
entire buffer back.
Attribute method implementations should operate on an identical
buffer when reading and writing values.
Other notes:
- Writing causes the show() method to be rearmed regardless of current
file position.
- The buffer will always be PAGE_SIZE bytes in length. On x86, this
is 4096.
- show() methods should return the number of bytes printed into the
buffer.
- New implementations of show() methods should only use sysfs_emit() or
sysfs_emit_at() when formatting the value to be returned to user space.
- store() should return the number of bytes used from the buffer. If the
entire buffer has been used, just return the count argument.
- show() or store() can always return errors. If a bad value comes
through, be sure to return an error.
- The object passed to the methods will be pinned in memory via sysfs
reference counting its embedded object. However, the physical
entity (e.g. device) the object represents may not be present. Be
sure to have a way to check this, if necessary.
A very simple (and naive) implementation of a device attribute is::
static ssize_t show_name(struct device *dev, struct device_attribute *attr,
char *buf)
{
return sysfs_emit(buf, "%s\n", dev->name);
}
static ssize_t store_name(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
snprintf(dev->name, sizeof(dev->name), "%.*s",
(int)min(count, sizeof(dev->name) - 1), buf);
return count;
}
static DEVICE_ATTR(name, S_IRUGO, show_name, store_name);
(Note that the real implementation doesn't allow userspace to set the
name for a device.)
Top Level Directory Layout
~~~~~~~~~~~~~~~~~~~~~~~~~~
The sysfs directory arrangement exposes the relationship of kernel
data structures.
The top level sysfs directory looks like::
block/
bus/
class/
dev/
devices/
firmware/
fs/
hypervisor/
kernel/
module/
power/
devices/ contains a filesystem representation of the device tree. It maps
directly to the internal kernel device tree, which is a hierarchy of
struct device.
bus/ contains flat directory layout of the various bus types in the
kernel. Each bus's directory contains two subdirectories::
devices/
drivers/
devices/ contains symlinks for each device discovered in the system
that point to the device's directory under /sys/devices.
drivers/ contains a directory for each device driver that is loaded
for devices on that particular bus (this assumes that drivers do not
span multiple bus types).
fs/ contains a directory for some filesystems. Currently each
filesystem wanting to export attributes must create its own hierarchy
below fs/ (see fuse/fuse.rst for an example).
module/ contains parameter values and state information for all
loaded system modules, for both builtin and loadable modules.
dev/ contains two directories: char/ and block/. Inside these two
directories there are symlinks named <major>:<minor>. These symlinks
point to the directories under /sys/devices for each device. /sys/dev provides a
quick way to lookup the sysfs interface for a device from the result of
a stat(2) operation.
More information on driver-model specific features can be found in
Documentation/driver-api/driver-model/.
block/ contains symlinks to all the block devices discovered on the system.
These symlinks point to directories under /sys/devices.
class/ contains a directory for each device class, grouped by functional type.
Each directory in class/ contains symlinks to devices in the /sys/devices directory.
firmware/ contains system firmware data and configuration such as firmware tables,
ACPI information, and device tree data.
hypervisor/ contains virtualization platform information and provides an interface to
the underlying hypervisor. It is only present when running on a virtual machine.
kernel/ contains runtime kernel parameters, configuration settings, and status.
power/ contains power management subsystem information including
sleep states, suspend/resume capabilities, and policies.
Current Interfaces
~~~~~~~~~~~~~~~~~~
The following interface layers currently exist in sysfs.
devices (include/linux/device.h)
--------------------------------
Structure::
struct device_attribute {
struct attribute attr;
ssize_t (*show)(struct device *dev, struct device_attribute *attr,
char *buf);
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);
};
Declaring::
DEVICE_ATTR(_name, _mode, _show, _store);
Creation/Removal::
int device_create_file(struct device *dev, const struct device_attribute * attr);
void device_remove_file(struct device *dev, const struct device_attribute * attr);
bus drivers (include/linux/device.h)
------------------------------------
Structure::
struct bus_attribute {
struct attribute attr;
ssize_t (*show)(const struct bus_type *, char * buf);
ssize_t (*store)(const struct bus_type *, const char * buf, size_t count);
};
Declaring::
static BUS_ATTR_RW(name);
static BUS_ATTR_RO(name);
static BUS_ATTR_WO(name);
Creation/Removal::
int bus_create_file(struct bus_type *, struct bus_attribute *);
void bus_remove_file(struct bus_type *, struct bus_attribute *);
device drivers (include/linux/device.h)
---------------------------------------
Structure::
struct driver_attribute {
struct attribute attr;
ssize_t (*show)(struct device_driver *, char * buf);
ssize_t (*store)(struct device_driver *, const char * buf,
size_t count);
};
Declaring::
DRIVER_ATTR_RO(_name)
DRIVER_ATTR_RW(_name)
Creation/Removal::
int driver_create_file(struct device_driver *, const struct driver_attribute *);
void driver_remove_file(struct device_driver *, const struct driver_attribute *);
Documentation
~~~~~~~~~~~~~
The sysfs directory structure and the attributes in each directory define an
ABI between the kernel and user space. As for any ABI, it is important that
this ABI is stable and properly documented. All new sysfs attributes must be
documented in Documentation/ABI. See also Documentation/ABI/README for more
information.
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
목적·mount·kobject directory
1-53sysfs는 ramfs를 바탕으로 만든 RAM 기반 파일 시스템으로 kernel 자료구조, 그 attribute, 객체 사이 연결을 user space에 내보낸다. `kobject` infrastructure와 본질적으로 결합되어 있으므로 객체 interface는 `Documentation/core-api/kobject.rst`를 함께 읽어야 한다.
`CONFIG_SYSFS`가 정의되면 항상 kernel에 compile되며 `mount -t sysfs sysfs /sys`로 접근한다.
등록된 모든 kobject에는 sysfs directory가 하나 생긴다. 부모 kobject의 하위 directory에 배치되어 내부 객체 hierarchy를 user space에 드러내고, top-level directory는 subsystem 같은 공통 ancestor를 나타낸다.
sysfs는 directory의 `kernfs_node`에 구현 kobject 포인터를 저장한다. 과거에는 file open·close 때 sysfs가 이 포인터로 kobject reference count를 직접 조절했지만 현재 구현에서 이를 직접 바꾸는 함수는 `sysfs_schedule_callback()`뿐이다.
kernel 객체 hierarchy가 `/sys` 경로로 투영된다.
.. SPDX-License-Identifier: GPL-2.0
=====================================================
sysfs - _The_ filesystem for exporting kernel objects
=====================================================
Patrick Mochel <[email protected]>
Mike Murphy <[email protected]>
:Revised: 16 August 2011
:Original: 10 January 2003
What it is
~~~~~~~~~~
sysfs is a RAM-based filesystem initially based on ramfs. It provides
a means to export kernel data structures, their attributes, and the
linkages between them to userspace.
sysfs is tied inherently to the kobject infrastructure. Please read
Documentation/core-api/kobject.rst for more information concerning the kobject
interface.
Using sysfs
~~~~~~~~~~~
sysfs is always compiled in if CONFIG_SYSFS is defined. You can access
it by doing::
mount -t sysfs sysfs /sys
Directory Creation
~~~~~~~~~~~~~~~~~~
For every kobject that is registered with the system, a directory is
created for it in sysfs. That directory is created as a subdirectory
of the kobject's parent, expressing internal object hierarchies to
userspace. Top-level directories in sysfs represent the common
ancestors of object hierarchies; i.e. the subsystems the objects
belong to.
sysfs internally stores a pointer to the kobject that implements a
directory in the kernfs_node object associated with the directory. In
the past this kobject pointer has been used by sysfs to do reference
counting directly on the kobject whenever the file is opened or closed.
With the current sysfs implementation the kobject reference count is
only modified directly by the function sysfs_schedule_callback().
Attribute 파일의 기본 계약
54-89kobject의 attribute는 일반 파일 형태로 export된다. sysfs가 file I/O를 attribute용 method로 전달해 kernel 값을 읽고 쓰게 한다.
attribute는 ASCII text이며 가능하면 파일 하나에 값 하나만 둔다. 효율상 같은 type의 값 배열은 허용되지만 서로 다른 type 혼합, 여러 줄 data, 복잡한 formatting은 강하게 지양한다. sysfs 파일은 안정적인 단순 ABI여야 한다.
기본 `struct attribute`는 `name`, `owner` module, permission `mode`만 가진다. `sysfs_create_file()`과 `sysfs_remove_file()`로 kobject에 추가·제거한다. 이 bare attribute 자체에는 값을 읽고 쓰는 수단이 없으므로 subsystem은 자기 객체 type용 wrapper 구조체와 helper를 정의하는 것이 권장된다.
기본 metadata와 수명 API다.
Attributes
~~~~~~~~~~
Attributes can be exported for kobjects in the form of regular files in
the filesystem. sysfs forwards file I/O operations to methods defined
for the attributes, providing a means to read and write kernel
attributes.
Attributes should be ASCII text files, preferably with only one value
per file. It is noted that it may not be efficient to contain only one
value per file, so it is socially acceptable to express an array of
values of the same type.
Mixing types, expressing multiple lines of data, and doing fancy
formatting of data is heavily frowned upon. Doing these things may get
you publicly humiliated and your code rewritten without notice.
An attribute definition is simply::
struct attribute {
char *name;
struct module *owner;
umode_t mode;
};
int sysfs_create_file(struct kobject * kobj, const struct attribute * attr);
void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr);
A bare attribute contains no means to read or write the value of the
attribute. Subsystems are encouraged to define their own attribute
structure and wrapper functions for adding and removing attributes for
a specific object type.
`device_attribute`와 정의 macro
90-150driver model의 `struct device_attribute`는 기본 `struct attribute`와 `show()`·`store()` callback을 묶는다. `device_create_file()`과 `device_remove_file()`이 `struct device`에 이 attribute를 연결한다.
`DEVICE_ATTR(_name, _mode, _show, _store)`는 `dev_attr_<name>` 구조체를 `__ATTR`로 선언한다. 예제의 `DEVICE_ATTR(foo, S_IWUSR | S_IRUGO, show_foo, store_foo)`는 이름 `foo`, 해당 mode와 callback을 직접 채운 `struct device_attribute`와 같다.
`include/linux/kernel.h`가 경고하듯 OTHER_WRITABLE은 일반적으로 나쁜 생각이다. 모든 사용자가 sysfs file을 쓰도록 설정하려 하면 Others 부분은 read-only mode로 되돌아가 실패한다.
일반 사례는 `sysfs.h` helper로 더 간결하게 쓴다. `__ATTR_RO(name)`은 `name_show`와 0444, `__ATTR_WO(name)`은 `name_store`와 root-only 0200, `__ATTR_RO_MODE(name, mode)`는 더 제한적인 read-only, `__ATTR_RW(name)`은 `name_show`·`name_store`와 0644, `__ATTR_NULL`은 name NULL인 list terminator를 가정한다.
callback 이름과 기본 mode 규칙이다.
For example, the driver model defines struct device_attribute like::
struct device_attribute {
struct attribute attr;
ssize_t (*show)(struct device *dev, struct device_attribute *attr,
char *buf);
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);
};
int device_create_file(struct device *, const struct device_attribute *);
void device_remove_file(struct device *, const struct device_attribute *);
It also defines this helper for defining device attributes::
#define DEVICE_ATTR(_name, _mode, _show, _store) \
struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
For example, declaring::
static DEVICE_ATTR(foo, S_IWUSR | S_IRUGO, show_foo, store_foo);
is equivalent to doing::
static struct device_attribute dev_attr_foo = {
.attr = {
.name = "foo",
.mode = S_IWUSR | S_IRUGO,
},
.show = show_foo,
.store = store_foo,
};
Note as stated in include/linux/kernel.h "OTHER_WRITABLE? Generally
considered a bad idea." so trying to set a sysfs file writable for
everyone will fail reverting to RO mode for "Others".
For the common cases sysfs.h provides convenience macros to make
defining attributes easier as well as making code more concise and
readable. The above case could be shortened to:
static struct device_attribute dev_attr_foo = __ATTR_RW(foo);
the list of helpers available to define your wrapper function is:
__ATTR_RO(name):
assumes default name_show and mode 0444
__ATTR_WO(name):
assumes a name_store only and is restricted to mode
0200 that is root write access only.
__ATTR_RO_MODE(name, mode):
for more restrictive RO access; currently
only use case is the EFI System Resource Table
(see drivers/firmware/efi/esrt.c)
__ATTR_RW(name):
assumes default name_show, name_store and setting
mode to 0644.
__ATTR_NULL:
which sets the name to NULL and is used as end of list
indicator (see: kernel/workqueue.c)
Subsystem별 `sysfs_ops` forwarding
151-194새 attribute type을 정의한 subsystem은 generic read·write를 실제 owner의 `show()`·`store()`로 전달할 `struct sysfs_ops`를 구현해야 한다. 이 포인터는 해당 type descriptor인 `struct kobj_type`에 저장된다.
file I/O가 발생하면 sysfs가 type의 적절한 method를 호출한다. method는 generic `struct kobject`와 `struct attribute` 포인터를 subsystem별 type으로 변환하고 실제 callback을 호출한다.
예제 `dev_attr_show()`는 `container_of` 기반 `to_dev_attr()`로 attribute를 `device_attribute`로 바꾸고 `kobj_to_dev()`로 device를 얻는다. `show`가 있으면 호출하며 반환 크기가 `PAGE_SIZE` 이상이면 잘못된 count를 printk로 경고한다. callback이 없을 때 초기 반환값은 `-EIO`다.
generic VFS 요청을 subsystem별 callback으로 변환한다.
Subsystem-Specific Callbacks
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When a subsystem defines a new attribute type, it must implement a
set of sysfs operations for forwarding read and write calls to the
show and store methods of the attribute owners::
struct sysfs_ops {
ssize_t (*show)(struct kobject *, struct attribute *, char *);
ssize_t (*store)(struct kobject *, struct attribute *, const char *, size_t);
};
[ Subsystems should have already defined a struct kobj_type as a
descriptor for this type, which is where the sysfs_ops pointer is
stored. See the kobject documentation for more information. ]
When a file is read or written, sysfs calls the appropriate method
for the type. The method then translates the generic struct kobject
and struct attribute pointers to the appropriate pointer types, and
calls the associated methods.
To illustrate::
#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr)
static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
struct device_attribute *dev_attr = to_dev_attr(attr);
struct device *dev = kobj_to_dev(kobj);
ssize_t ret = -EIO;
if (dev_attr->show)
ret = dev_attr->show(dev, dev_attr, buf);
if (ret >= (ssize_t)PAGE_SIZE) {
printk("dev_attr_show: %pS returned bad count\n",
dev_attr->show);
}
return ret;
}
`show()`·`store()`의 PAGE_SIZE 계약
195-260attribute를 읽거나 쓰려면 선언 때 `show()` 또는 `store()`를 지정한다. callback 인자는 object, attribute, buffer만 받도록 단순해야 한다.
sysfs는 PAGE_SIZE buffer를 할당하고 read 또는 write 한 번마다 method를 정확히 한 번 호출한다. read의 `show()`는 값 하나 또는 같은 type 배열 전체를 buffer에 채워야 한다. user space는 반환된 file image에서 부분 read와 forward seek를 자유롭게 할 수 있다. offset 0으로 seek하거나 `pread(2)` offset 0을 요청하면 `show()`가 다시 호출되어 buffer를 새로 채운다.
write에서는 첫 write에 전체 buffer가 전달된다고 기대하고 그 전부를 `store()`에 넘긴다. data 뒤에 terminating NUL을 추가하므로 `sysfs_streq()`를 안전하게 쓸 수 있다. user process는 파일 전체를 읽고 바꿀 값을 수정한 뒤 전체 buffer를 다시 쓰는 방식이 권장되며, callback은 read와 write에서 동일한 형식의 buffer를 다뤄야 한다.
write는 현재 file position과 무관하게 `show()` snapshot을 rearm한다. buffer는 항상 PAGE_SIZE이며 x86에서는 4096 byte다. `show()`는 출력 byte 수를 반환하고 새 구현은 formatting에 `sysfs_emit()` 또는 `sysfs_emit_at()`만 사용해야 한다. `store()`는 소비한 byte 수를 반환하며 전부 사용했으면 `count`를 반환한다. 두 callback은 잘못된 값 등에서 error를 반환할 수 있다.
sysfs reference counting은 callback에 전달한 embedded object를 memory에 pin하지만 그 객체가 나타내는 실제 device 같은 물리 entity가 여전히 존재한다고 보장하지 않는다. 필요하면 callback이 별도 존재 여부를 확인해야 한다.
read와 write의 buffer·호출·반환 규칙을 비교한다.
callback은 한 번 호출되고 sysfs가 user offset을 처리한다.
Reading/Writing Attribute Data
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To read or write attributes, show() or store() methods must be
specified when declaring the attribute. The method types should be as
simple as those defined for device attributes::
ssize_t (*show)(struct device *dev, struct device_attribute *attr, char *buf);
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);
IOW, they should take only an object, an attribute, and a buffer as parameters.
sysfs allocates a buffer of size (PAGE_SIZE) and passes it to the
method. sysfs will call the method exactly once for each read or
write. This forces the following behavior on the method
implementations:
- On read(2), the show() method should fill the entire buffer.
Recall that an attribute should only be exporting one value, or an
array of similar values, so this shouldn't be that expensive.
This allows userspace to do partial reads and forward seeks
arbitrarily over the entire file at will. If userspace seeks back to
zero or does a pread(2) with an offset of '0' the show() method will
be called again, rearmed, to fill the buffer.
- On write(2), sysfs expects the entire buffer to be passed during the
first write. sysfs then passes the entire buffer to the store() method.
A terminating null is added after the data on stores. This makes
functions like sysfs_streq() safe to use.
When writing sysfs files, userspace processes should first read the
entire file, modify the values it wishes to change, then write the
entire buffer back.
Attribute method implementations should operate on an identical
buffer when reading and writing values.
Other notes:
- Writing causes the show() method to be rearmed regardless of current
file position.
- The buffer will always be PAGE_SIZE bytes in length. On x86, this
is 4096.
- show() methods should return the number of bytes printed into the
buffer.
- New implementations of show() methods should only use sysfs_emit() or
sysfs_emit_at() when formatting the value to be returned to user space.
- store() should return the number of bytes used from the buffer. If the
entire buffer has been used, just return the count argument.
- show() or store() can always return errors. If a bad value comes
through, be sure to return an error.
- The object passed to the methods will be pinned in memory via sysfs
reference counting its embedded object. However, the physical
entity (e.g. device) the object represents may not be present. Be
sure to have a way to check this, if necessary.
단순 device attribute 예제
261-283예제 `show_name()`은 `sysfs_emit(buf, "%s\n", dev->name)`로 device 이름과 newline을 출력하고 byte 수를 반환한다. `store_name()`은 `count`와 destination 크기 중 안전한 길이를 골라 `snprintf`로 이름을 복사한 뒤 `count`를 반환한다.
이 callback은 `DEVICE_ATTR(name, S_IRUGO, show_name, store_name)`으로 연결된다. 설명을 위한 단순 예제이며 실제 구현은 user space가 device 이름을 설정하지 못하게 한다.
선언과 callback 연결을 보여 준다.
A very simple (and naive) implementation of a device attribute is::
static ssize_t show_name(struct device *dev, struct device_attribute *attr,
char *buf)
{
return sysfs_emit(buf, "%s\n", dev->name);
}
static ssize_t store_name(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
snprintf(dev->name, sizeof(dev->name), "%.*s",
(int)min(count, sizeof(dev->name) - 1), buf);
return count;
}
static DEVICE_ATTR(name, S_IRUGO, show_name, store_name);
(Note that the real implementation doesn't allow userspace to set the
name for a device.)
Top-level `/sys` directory layout
284-353sysfs directory 배치는 kernel 자료구조 관계를 노출한다. top level에는 `block/`, `bus/`, `class/`, `dev/`, `devices/`, `firmware/`, `fs/`, `hypervisor/`, `kernel/`, `module/`, `power/`가 있다.
`devices/`는 `struct device` hierarchy인 내부 kernel device tree를 그대로 파일 시스템으로 표현한다. `bus/`는 bus type별 평면 directory이며 각 bus의 `devices/`에는 `/sys/devices`의 실제 device directory를 가리키는 symlink, `drivers/`에는 그 bus에 load된 driver별 directory가 있다.
`fs/`는 attribute를 export하는 일부 파일 시스템이 자체 hierarchy를 만드는 곳이다. `module/`은 built-in과 loadable module 모두의 parameter와 상태를 담는다. `dev/char`와 `dev/block`에는 `<major>:<minor>` symlink가 있어 `stat(2)` 결과에서 해당 `/sys/devices` interface를 빠르게 찾는다.
`block/`은 발견된 모든 block device의 `/sys/devices` symlink, `class/`는 기능 유형별 device class와 device symlink를 담는다. `firmware/`는 firmware table·ACPI·device tree data, `hypervisor/`는 VM에서만 나타나는 virtualization platform interface, `kernel/`은 runtime parameter·configuration·status, `power/`는 sleep·suspend/resume·policy를 제공한다.
각 최상위 directory의 핵심 역할이다.
Top Level Directory Layout
~~~~~~~~~~~~~~~~~~~~~~~~~~
The sysfs directory arrangement exposes the relationship of kernel
data structures.
The top level sysfs directory looks like::
block/
bus/
class/
dev/
devices/
firmware/
fs/
hypervisor/
kernel/
module/
power/
devices/ contains a filesystem representation of the device tree. It maps
directly to the internal kernel device tree, which is a hierarchy of
struct device.
bus/ contains flat directory layout of the various bus types in the
kernel. Each bus's directory contains two subdirectories::
devices/
drivers/
devices/ contains symlinks for each device discovered in the system
that point to the device's directory under /sys/devices.
drivers/ contains a directory for each device driver that is loaded
for devices on that particular bus (this assumes that drivers do not
span multiple bus types).
fs/ contains a directory for some filesystems. Currently each
filesystem wanting to export attributes must create its own hierarchy
below fs/ (see fuse/fuse.rst for an example).
module/ contains parameter values and state information for all
loaded system modules, for both builtin and loadable modules.
dev/ contains two directories: char/ and block/. Inside these two
directories there are symlinks named <major>:<minor>. These symlinks
point to the directories under /sys/devices for each device. /sys/dev provides a
quick way to lookup the sysfs interface for a device from the result of
a stat(2) operation.
More information on driver-model specific features can be found in
Documentation/driver-api/driver-model/.
block/ contains symlinks to all the block devices discovered on the system.
These symlinks point to directories under /sys/devices.
class/ contains a directory for each device class, grouped by functional type.
Each directory in class/ contains symlinks to devices in the /sys/devices directory.
firmware/ contains system firmware data and configuration such as firmware tables,
ACPI information, and device tree data.
hypervisor/ contains virtualization platform information and provides an interface to
the underlying hypervisor. It is only present when running on a virtual machine.
kernel/ contains runtime kernel parameters, configuration settings, and status.
power/ contains power management subsystem information including
sleep states, suspend/resume capabilities, and policies.
Device attribute interface
354-382현재 sysfs의 device layer는 `include/linux/device.h`의 `struct device_attribute`를 사용한다. 구조체는 기본 `attribute`와 device를 받는 `show()`·`store()` callback을 가진다.
`DEVICE_ATTR(_name, _mode, _show, _store)`로 선언하고 `device_create_file()`과 `device_remove_file()`로 특정 `struct device` 아래에 생성·제거한다.
구조·선언·수명 API를 묶어 본다.
Current Interfaces
~~~~~~~~~~~~~~~~~~
The following interface layers currently exist in sysfs.
devices (include/linux/device.h)
--------------------------------
Structure::
struct device_attribute {
struct attribute attr;
ssize_t (*show)(struct device *dev, struct device_attribute *attr,
char *buf);
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);
};
Declaring::
DEVICE_ATTR(_name, _mode, _show, _store);
Creation/Removal::
int device_create_file(struct device *dev, const struct device_attribute * attr);
void device_remove_file(struct device *dev, const struct device_attribute * attr);
Bus·driver attribute interface
383-426bus layer의 `struct bus_attribute`는 기본 attribute와 `const struct bus_type *`를 받는 `show()`·`store()`를 가진다. `BUS_ATTR_RW`, `BUS_ATTR_RO`, `BUS_ATTR_WO`로 선언하고 `bus_create_file()`·`bus_remove_file()`로 관리한다.
device driver layer의 `struct driver_attribute`는 `struct device_driver *`를 받는 callback을 가진다. `DRIVER_ATTR_RO`, `DRIVER_ATTR_RW`로 선언하고 `driver_create_file()`·`driver_remove_file()`로 생성·제거한다.
callback owner type과 helper family가 다르다.
bus drivers (include/linux/device.h)
------------------------------------
Structure::
struct bus_attribute {
struct attribute attr;
ssize_t (*show)(const struct bus_type *, char * buf);
ssize_t (*store)(const struct bus_type *, const char * buf, size_t count);
};
Declaring::
static BUS_ATTR_RW(name);
static BUS_ATTR_RO(name);
static BUS_ATTR_WO(name);
Creation/Removal::
int bus_create_file(struct bus_type *, struct bus_attribute *);
void bus_remove_file(struct bus_type *, struct bus_attribute *);
device drivers (include/linux/device.h)
---------------------------------------
Structure::
struct driver_attribute {
struct attribute attr;
ssize_t (*show)(struct device_driver *, char * buf);
ssize_t (*store)(struct device_driver *, const char * buf,
size_t count);
};
Declaring::
DRIVER_ATTR_RO(_name)
DRIVER_ATTR_RW(_name)
Creation/Removal::
int driver_create_file(struct device_driver *, const struct driver_attribute *);
void driver_remove_file(struct device_driver *, const struct driver_attribute *);
Sysfs ABI 문서화 의무
427-435sysfs directory 구조와 각 directory의 attribute는 kernel과 user space 사이 ABI를 정의한다. 다른 ABI와 마찬가지로 안정적으로 유지하고 제대로 문서화해야 한다.
새 sysfs attribute는 모두 `Documentation/ABI`에 문서화해야 하며 세부 규칙은 `Documentation/ABI/README`를 따른다.
code 추가만으로는 충분하지 않다.
Documentation
~~~~~~~~~~~~~
The sysfs directory structure and the attributes in each directory define an
ABI between the kernel and user space. As for any ABI, it is important that
this ABI is stable and properly documented. All new sysfs attributes must be
documented in Documentation/ABI. See also Documentation/ABI/README for more
information.
요약·해설
sysfs.rst:1-435sysfs는 kobject hierarchy와 attribute를 `/sys`의 directory·ASCII file·symlink로 투영한다. subsystem은 구체 attribute wrapper와 `sysfs_ops`를 제공하고 generic kobject 요청을 device·bus·driver callback으로 전달한다.
I/O 계약의 핵심은 PAGE_SIZE buffer와 callback 1회다. `show()`는 전체 값을 `sysfs_emit()`로 만들고 출력 byte 수를, `store()`는 소비 byte 수를 반환한다. memory에 pin된 kernel object와 실제 hardware의 존재는 별개이므로 callback이 필요한 상태 검사를 해야 한다.
kernel object에서 안정적인 user-space 파일 ABI까지다.