USB ストレージ上のルート ファイル システムから Ubuntu 14.04 を実行しているラップトップがあります。これはうまく機能しません。サスペンドから復帰した後、USB の準備ができる前に ext4 が頻繁にルート ファイル システムへの書き込みを試行するからです。
これが起こったときにカーネル ログに表示される内容は次のとおりです。 で多数の I/O エラーが発生しsda1
、1 秒後に USB ストレージ ドライブがカーネルによってようやく検出されることに注意してください。
[ 2826.517419] wlan0: associated
[ 2826.517452] IPv6: ADDRCONF(NETDEV_CHANGE): wlan0: link becomes ready
[ 2827.575371] EXT4-fs warning (device sda1): ext4_end_bio:317: I/O error -5 writing to inode 1733735 (offset 0 size 0 starting block 12629950)
[ 2827.575380] Buffer I/O error on device sda1, logical block 12629694
[ 2827.575400] EXT4-fs warning (device sda1): ext4_end_bio:317: I/O error -5 writing to inode 3148603 (offset 0 size 8192 starting block 12844470)
[ 2827.575404] Buffer I/O error on device sda1, logical block 12844212
[ 2827.575411] Buffer I/O error on device sda1, logical block 12844213
[ 2827.575448] EXT4-fs warning (device sda1): ext4_end_bio:317: I/O error -5 writing to inode 3015015 (offset 0 size 90112 starting block 6588832)
[ 2827.575453] Buffer I/O error on device sda1, logical block 6588576
[ 2827.575461] Buffer I/O error on device sda1, logical block 6588577
[ 2827.575465] Buffer I/O error on device sda1, logical block 6588578
[ 2827.575469] Buffer I/O error on device sda1, logical block 6588579
[ 2827.575473] Buffer I/O error on device sda1, logical block 6588580
[ 2827.575477] Buffer I/O error on device sda1, logical block 6588581
[ 2827.575481] Buffer I/O error on device sda1, logical block 6588582
[ 2828.857284] sd 0:0:0:0: [sda] No Caching mode page found
[ 2828.857293] sd 0:0:0:0: [sda] Assuming drive cache: write through
最初はカーネル ログ以外に問題が発生したことを示す目に見える兆候はありませんが、この時点以降も Ubuntu を実行し続けると、ファイル システムにエラーが発生し、最終的には読み取り専用モードに切り替わります。その時点で、ファイル システムをfsck.ext4
修復するには、リカバリ モードで再起動し、ルート シェルから手動で実行する必要があります。
サスペンドから復帰した後、USB ドライブの準備ができるまでルート デバイスへのアクセスを遅らせるように変更できる設定はありますか?
答え1
この問題が USB デバイスでのみ発生し、他のデバイスでは発生しない理由は、次の 2 つの要因の組み合わせです。
- USB ストレージは、他のストレージ メディアとは異なり、操作にカーネル スレッドに依存します。
- サスペンドから再開すると、カーネルはすべてのスレッドを同時に起動します。
その結果、再開時には、一方ではメディアを検出しようとする USB システムと、他方ではサスペンドと再開からのログ メッセージをディスクに書き込もうとする syslog との間で競合が発生します。
USB デバイスが検出される前に syslog が書き込みを試行すると、ext4 でエラーが発生しますが、何らかの理由でエラーが適切に処理されず、最終的にはファイル システムで fsck を手動で実行する必要があります。
私が見つけた解決策は、他のスレッドが起動される前にカーネル スレッドに 12 秒の先行を与えることでした。これを機能させるためにカーネルに加えなければならなかった変更は次のとおりです。
--- linux-3.13.0/kernel/power/suspend.c.orig 2014-01-20 03:40:07.000000000 +0100
+++ linux-3.13.0/kernel/power/suspend.c 2014-08-04 00:57:43.847038640 +0200
@@ -299,6 +299,8 @@
goto Resume_devices;
}
+unsigned int resume_delay = 0;
+
/**
* suspend_finish - Clean up before finishing the suspend sequence.
*
@@ -307,6 +309,15 @@
*/
static void suspend_finish(void)
{
+ if (resume_delay) {
+ /* Give kernel threads a head start, such that usb-storage
+ * can detect devices before syslog attempts to write log
+ * messages from the suspend code.
+ */
+ thaw_kernel_threads();
+ pr_debug("PM: Sleeping for %d milliseconds.\n", resume_delay);
+ msleep(resume_delay);
+ }
suspend_thaw_processes();
pm_notifier_call_chain(PM_POST_SUSPEND);
pm_restore_console();
--- linux-3.13.0/kernel/sysctl.c.orig 2014-08-04 08:11:26.000000000 +0200
+++ linux-3.13.0/kernel/sysctl.c 2014-08-03 23:27:23.796278219 +0200
@@ -277,8 +277,17 @@
static int max_extfrag_threshold = 1000;
#endif
+extern unsigned int resume_delay;
+
static struct ctl_table kern_table[] = {
{
+ .procname = "resume_delay",
+ .data = &resume_delay,
+ .maxlen = sizeof(unsigned int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+ {
.procname = "sched_child_runs_first",
.data = &sysctl_sched_child_runs_first,
.maxlen = sizeof(unsigned int),