2014年5月28日

アトミック・カーネルスレッドと言うものがあるらしい

BTRFSのディスクからの読み込み終了時の関数 btrfs_readpage_end_io_hook() をいじっている。

目的とした機能が動作して最終テストを行っていたら、dmesg が下のように文句を言っている。

BUG: scheduling while atomic: btrfs-endio-1/1157/0x00000001
Modules linked in:
Pid: 1157, comm: btrfs-endio-1 Not tainted 3.0.101.RNx86_64.2.2+ #121
Call Trace:
 [<ffffffff8887d4bd>] ? __schedule_bug+0x54/0x58
 [<ffffffff8888712b>] ? __schedule+0x59b/0xa30
 [<ffffffff88398299>] ? put_dec+0x59/0x60
 [<ffffffff880d0be4>] ? add_partial+0x24/0x80
 [<ffffffff880d240e>] ? deactivate_slab+0x4e/0xf0
 [<ffffffff88774849>] ? __alloc_skb+0x99/0x270
 [<ffffffff8888768a>] ? schedule+0x3a/0x50
 [<ffffffff88042e6d>] ? sys_sched_yield+0x3d/0x50
 [<ffffffff888878bf>] ? yield+0x2f/0x40
 [<ffffffff88799ea0>] ? netlink_broadcast_filtered+0x330/0x480
 [<ffffffff8879a009>] ? netlink_broadcast+0x19/0x20
 [<ffffffff883ad39e>] ? nla_put+0x2e/0x40
 [<ffffffff883488af>] ? mdcsrepair_netlink+0x46f/0x580
 [<ffffffff882e6711>] ? btrfs_readpage_end_io_hook+0x1d1/0x3a0
 [<ffffffff882fd64e>] ? end_bio_extent_readpage+0x12e/0x9c0
 [<ffffffff88106fe8>] ? bio_endio+0x18/0x30
 [<ffffffff882d864f>] ? end_workqueue_fn+0x3f/0x50
 [<ffffffff8830d3f0>] ? worker_loop+0x140/0x500
 [<ffffffff8830d2b0>] ? btrfs_queue_worker+0x300/0x300
 [<ffffffff88063a09>] ? kthread+0x89/0x90
 [<ffffffff8888aa74>] ? kernel_thread_helper+0x4/0x10
 [<ffffffff88063980>] ? kthread_worker_fn+0x140/0x140
 [<ffffffff8888aa70>] ? gs_change+0xb/0xb


調べてみると、in_atomic_preempt_off() が真になっていることが直接の原因。どうもこのワーカースレッドはプリエンプションだけでなくスケジューリングも禁止しており、スレッドとは言え、割り込み処理のようにCPUを手放さずにスレッド終了まで実行しなければならないようだ。このコールスタックでは nla_put()(Netlinkのメッセージ構築関数)がスケジュールしてしまうためにバグとしてスタックダンプをしている。実際にはスタックダンプがあっても目的の機能は実行されて問題がないのだが、そこはホレ、やはりこういうものは出現させるべきではない。

解決策としては、 nla_put()をやめる訳には行かず、またこのスレッドをノン・アトミックにするのもはばかれるので、一計を案じてアトミックでない通常のスレッドを別に走らせてそこに必要なデータを渡してnla_put()以降の処理を依頼することにした。ここでは無限ループのサービススレッドでうまく行ったが、1回限りのワーカースレッドでもうまく行くかどうかは試していない。

0 件のコメント:

コメントを投稿