I/O スケジューラの sysfs エントリについてのまとめ
1. sysfs とデータ構造
カーネルが用意するユーザプログラムとの通信を行う為のインターフェイスとして用意されているオンメモリのファイルシステムを sysfs と言う。
sysfs のディレクトリファイルは kobject というデータ構造で表現され、各 kobject に属性 (attribute) 情報がぶら下がり、これがファイルという形でユーザに提供されている。そして、このファイルに対する読み書きに対して、カーネル側が制御を行っている。これを行う際に参照されるデータ構造が kobj_type オブジェクトである。
2. io スケジューラにおける sysfs エントリの更新
2-1. 概要
io ブロックレイヤは /sys/block/${ブロックデバイス名}/queue/ というパスに位置する kobject を持っている。この中に scheduler と呼ばれるファイルが存在するが、このファイルに対して書き込みを行う事で、io スケジューラの更新処理が行える。
io スケジューラが持つ sysfs エントリは /sys/block/${ブロックデバイス名}/queue/iosched というディレクトリで表される kobject にまとめられている。ちなみに v2.6.26 (debian-lenny のカーネルバージョン) におけるデフォルトの io スケジューラである cfq における sysfs エントリは次のようになっている。
$ ls /sys/block/sda/queue/iosched/
back_seek_max
back_seek_penalty
fifo_expire_async
fifo_expire_sync
quantum
slice_async
slice_async_rq
slice_idle
slice_sync
これら 9 個のファイルが cfq がユーザに提供するインターフェイスとなっている。これらに対して読み書きを行う事で cfq の内部情報を参照したりカスタマイズする事ができる。
2-2. io スケジューラ変更に伴う初期化処理
io ブロックレイヤのインターフェイスである scheduler ファイルに対して書き込みを行うと、カーネルの elv_iosched_sotre() 関数が実行される。ここから呼び出される elv_register_queue() によって io スケジューラに関する sysfs エントリが更新される。以下が elv_register_queue() の処理になる。
1 int elv_register_queue(struct request_queue *q)
2 {
3 elevator_t *e = q->elevator;
4 int error;
5
6 error = kobject_add(&e->kobj, &q->kobj, "%s", "iosched");
7 if (!error) {
8 struct elv_fs_entry *attr = e->elevator_type->elevator_attrs;
9 if (attr) {
10 while (attr->attr.name) {
11 if (sysfs_create_file(&e->kobj, &attr->attr))
12 break;
13 attr++;
14 }
15 }
16 kobject_uevent(&e->kobj, KOBJ_ADD);
17 }
18 return error;
19 }
8 行目で取り出されている elv_fs_entry の配列は cfq では次のように静的に宣言されている。
1 static struct elv_fs_entry cfq_attrs[] = {
2 CFQ_ATTR(quantum),
3 CFQ_ATTR(fifo_expire_sync),
4 CFQ_ATTR(fifo_expire_async),
5 CFQ_ATTR(back_seek_max),
6 CFQ_ATTR(back_seek_penalty),
7 CFQ_ATTR(slice_sync),
8 CFQ_ATTR(slice_async),
9 CFQ_ATTR(slice_async_rq),
10 CFQ_ATTR(slice_idle),
11 __ATTR_NULL
12 };
これらの attribute オブジェクトを elv_register_queue() の 10 行目から 14 行目のループで sysfs_create_file() を呼び出して、6 行目で再構築した iosched の kobject に登録する。
2-3. sysfs エントリに対する読み書きの実装
では sysfs エントリファイルに対して読み書きを行うと GNU/Linux カーネル内部では何が起こるのか?
その名のとおり sysfs は、ファイルという抽象データ構造を提供するファイルシステムであり、そのファイルに対する読み書きに対する処理は仮想ファイルシステム (vfs) が担当する。
vfs ではセキュリティチェック等の処理を実行した後、具体的なファイル操作の処理を file_operations 型のオブジェクトに依託する。このファイルオペレーションオブジェクトが提供する各種メソッドが、低レベルファイルシステムの処理になる。
sysfs も sysfs_file_operations というファイルオペレーションオブジェクトを vfs に提供している。そして、sysfs エントリファイルに対する書き込みが行われると vfs から sysfs_write_file() が呼び出される。
sysfs_write_file() 及びそこから呼び出される flush_write_buffer() のコードは次のようになっている。
1 static ssize_t
2 sysfs_write_file(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
3 {
4 struct sysfs_buffer * buffer = file->private_data;
5 ssize_t len;
6
7 mutex_lock(&buffer->mutex);
8 len = fill_write_buffer(buffer, buf, count);
9 if (len > 0)
10 len = flush_write_buffer(file->f_path.dentry, buffer, len);
11 if (len > 0)
12 *ppos += len;
13 mutex_unlock(&buffer->mutex);
14 return len;
15 }
1 static int
2 flush_write_buffer(struct dentry * dentry, struct sysfs_buffer * buffer, size_t count)
3 {
4 struct sysfs_dirent *attr_sd = dentry->d_fsdata;
5 struct kobject *kobj = attr_sd->s_parent->s_dir.kobj;
6 struct sysfs_ops * ops = buffer->ops;
7 int rc;
8
9 /* need attr_sd for attr and ops, its parent for kobj */
10 if (!sysfs_get_active_two(attr_sd))
11 return -ENODEV;
12
13 rc = ops->store(kobj, attr_sd->s_attr.attr, buffer->page, count);
14
15 sysfs_put_active_two(attr_sd);
16
17 return rc;
18 }
sysfs_write_file() の 10 行目で flush_write_buffer() を呼び出し、この関数の 13 行目で sysfs_ops オブジェクトの store メソッドを実行している。
sysfs_ops オブジェクトは、sysfs エントリファイルに対する読み書きにおいて、各 kobject 毎に用意できるデータ構造である。sysfs_ops 型オブジェクトは、kobj_type オブジェクトに包含されるデータ構造であり、kobject_init() によって、kobject と関連付けられる。io スケジューラでは、elevator_alloc() によってこれが行われている。
そして flush_write_buffer() の store メソッドが実行によって、elv_attr_store() 関数が呼び出される。以下がそのコード。
1 static ssize_t
2 elv_attr_store(struct kobject *kobj, struct attribute *attr,
3 const char *page, size_t length)
4 {
5 elevator_t *e = container_of(kobj, elevator_t, kobj);
6 struct elv_fs_entry *entry = to_elv(attr);
7 ssize_t error;
8
9 if (!entry->store)
10 return -EIO;
11
12 mutex_lock(&e->sysfs_lock);
13 error = e->ops ? entry->store(e, page, length) : -ENOENT;
14 mutex_unlock(&e->sysfs_lock);
15 return error;
16 }
ここでは 6 行目の to_elv() マクロによって、[2-2] で示した io スケジューラ内部において静的に宣言される elv_fs_entry オブジェクトを取得し、13 行目で同オブジェクトの store メソッドを実行している。
3. まとめ
sysfs の概要、及び主だったデータ構造について示し、その内部構造について見てきた。
また io スケジューラの sysfs エントリの更新処理を追うことで、sysfs のファイルに対する読み書き時に、カーネル内部でどのような処理が行われるのかを確認した。
- Category(s)
- 学習経過
- The URL to Trackback this entry is:
- http://dev.ariel-networks.com/Members/ohyama/i-o-30b930b130e530fc30e9306e-sysfs-30a830f330c830ea306b306430443066306e307e30683081/tbping