@naota344の今週のLKML

今週は

  • [PATCH] printk-formats.txt documentation update
  • [PATCH v1 00/30] Ext4 snapshots
  • [PATCH] init: use KERNEL_DS when trying to start init process

[PATCH] printk-formats.txt documentation update

http://permalink.gmane.org/gmane.linux.kernel.embedded/3600

printk()と言えばカーネルデバッグの第一の友ですが :D をの printk() のフォーマット指定についてのドキュメントの更新パッチが来ています。

ポインタから関数名を表示してくれるSymbols/Function PointersやMACアドレスIPv4アドレスを表示してくれるMAC/FDDI addresses・IPv4 addressesなどなかなか便利なフォーマット指定ができたようです。

[PATCH v1 00/30] Ext4 snapshots

http://permalink.gmane.org/gmane.comp.file-systems.ext4/25968

ext4にsnapshotを入れよう、というスレッドが盛り上がっています。カーネルに入れるメリット、LVM snapshotに対する明白なメリットをしめせ、と長く長く長くスレッドが続いています。extはメインで使われることが多いだけに判定が厳しいのでしょうか。

https://lkml.org/lkml/2011/6/8/296

LVMに対する優位性としては

  • パフォーマンス スナップショット書きこみにはほとんどオーバーヘッドがない
  • スケーラビリティ スナップショットを増やしても追加のオーバーヘッドがない
  • メンテナンス性 スナップショット用のディスク領域を確保しておく必要がない
  • 持続性 ディスクフルでもスナップショットが消えない

があげられています。

特に「パフォーマンス」に関して

  • COW時にはメタデータが全てキャッシュに乗っていること
  • データブロックがmove-on-writeの戦略をとるためスナップショット時にコピーされるものがないこと
  • ext4のスナップショットはどのブロックが実際に使われているか、を判定できるので、一時的に使った(その後消した)領域はコピーから省かれること

逆にLVM snapshotの利点には

  • どんなファイルシステムでも動く
  • スナップショットに書きこみ可能・スナップショットのスナップショットを作れる
  • スナップショットをメインのボリュームにマージ可能

といった利点があります。

また、GSoCではext4のsnapshotをLVMのsnapshotとしてexportするプロジェクトが進められているようです。これがあれば過去のスナップショットにrevertするといったことができるようになります。

[PATCH] init: use KERNEL_DS when trying to start init process

http://permalink.gmane.org/gmane.linux.kernel/1147716

こんな記事がありましたが 革命の日々! Use tglx's more complete linux-history tree ちょうど同じところを読んでいたのでつっこんで解説を…。

まず、そもそもここで話題になってる set_fs() ってなんでしょーか

とりあえずアドレス範囲を無効にするためのトリックなようですが、どんなチェックなんでしょうか?なぜ無効にするのでしょうか?

https://www.codeblog.org/blog/gniibe/20060323.html#p06にいい解説がありました。

copy_from_user() はユーザ空間のメモリ領域からデータをカーネル空間にコピーしてくる関数です。ここでコピーしてくる時にユーザ空間のメモリであること、有効なメモリであることをチェックしますが、set_fs(KERNEL_DS)することでこのチェックが(結果的に)外れます。すると、カーネル空間からシステムコールとか(普通はユーザ空間から呼ばれることを想定しているもの)を呼べてうれしい!というわけです。

さて、じゃあこのpatchはなにをしているんでしょうか。 init_postにLinuxカーネルが起動して init process を呼び出す処理があります

static noinline int init_post(void)
{
...
	run_init_process("/sbin/init");
	run_init_process("/etc/init");
	run_init_process("/bin/init");
	run_init_process("/bin/sh");

	panic("No init found.  Try passing init= option to kernel. "
	      "See Linux Documentation/init.txt for guidance.");
}

run_init_process() をちょこちょこ呼び出して順番に実行を試しています。この init_post の時点では set_fs(KERNEL_DS) されていることに注意しておきましょう。

run_init_process()の呼び出しをたどっていきます

  • run_init_process()
  • kernel_execve()
  • sys_execve()
  • do_execve()
  • do_execve_common()
  • search_binary_handler()

で、この search_binary_handler() の中で

int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs)
{
...
	/* kernel module loader fixup */
	/* so we don't try to load run modprobe in kernel space. */
	set_fs(USER_DS);
...
}

と set_fs(USER_DS) されてたわけです。ところが、この関数は対応してないバイナリフォーマットの実行形式なんかの時に return してしまいます。set_fs(USER_DS)のままで。

そうすると、残りの run_init_process() およびそこから呼び出される関数群は set_fs(KERNEL_DS) されてる (= systemcallとか呼べる)と思って動いてるわけなので、エラーが出てしまい起動できるはずの init が起動されない、という結果になります。

かなり昔からのバグだったようですが…気づかないところは気がつかないものですねぇ…