Suspendendo com root no USB

Suspendendo com root no USB

Eu tenho um laptop executando o Ubuntu 14.04 a partir de um sistema de arquivos raiz em armazenamento USB. Isso não está funcionando bem, porque depois de acordar da suspensão, o ext4 frequentemente tentará gravar no sistema de arquivos raiz antes que o USB esteja pronto.

Aqui está o que vejo no log do kernel quando isso acontece, observe como recebo vários erros de E/S sda1e, um segundo depois, a unidade de armazenamento USB é finalmente detectada pelo kernel.

[ 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

A princípio, não há indicação visível fora do log do kernel de que o problema foi acionado, mas se eu deixar o Ubuntu continuar rodando além desse ponto, o sistema de arquivos receberá erros e eventualmente mudará para o modo somente leitura. Nesse ponto, tenho que reiniciar no modo de recuperação e executar fsck.ext4manualmente a partir de um shell raiz para reparar o sistema de arquivos.

Existe alguma configuração que eu possa alterar para que o acesso ao dispositivo raiz após sair da suspensão possa ser adiado até que a unidade USB esteja pronta?

Responder1

A razão pela qual esse problema só é observado em dispositivos USB e não em outros dispositivos é uma combinação de dois fatores:

  • O armazenamento USB, ao contrário de outras mídias de armazenamento, depende de threads do kernel para operação.
  • Ao retomar da suspensão, o kernel ativa todos os threads simultaneamente.

O resultado é que durante a retomada haverá uma corrida entre o sistema USB, por um lado, tentando detectar mídia e syslog, por outro lado, tentando gravar mensagens de log da suspensão e da retomada no disco.

Se o syslog tentar uma gravação antes que o dispositivo USB seja detectado, o ext4 receberá um erro, que por algum motivo não será tratado corretamente e, eventualmente, o sistema de arquivos precisará do fsck para ser executado manualmente.

A solução que encontrei foi dar aos threads do kernel uma vantagem de 12 segundos antes que outros threads fossem ativados. Estas são as mudanças que tive que fazer no kernel para que isso funcionasse:

--- 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),

informação relacionada