今天有同事提了个问题,上层应用针对文件读操作做了错误处理,处理方法为如果read失败,且errno = EIO, 则认为磁盘上该文件出现坏道,并做后续的相关补救操作。
线上有台机器上的一个磁盘出现问题,持续几周大量打印:
May 2 04:12:41 kernel: [30114401.884862] sd 0:1:0:5: [sdf] FAILED Result: hostbyte=DID_OK driverbyte=DRIVER_SENSE
May 2 04:12:41 kernel: [30114401.884869] sd 0:1:0:5: [sdf] Sense Key : Medium Error [current]
May 2 04:12:41 kernel: [30114401.884872] sd 0:1:0:5: [sdf] Add. Sense: Unrecovered read error
May 2 04:12:41 kernel: [30114401.884876] sd 0:1:0:5: [sdf] CDB: Read(10) 28 00 14 80 10 c8 00 00 08 00
确实是磁盘故障了。但应用层开发发现读取文件并校验后发现文件只读出前64K,并且没有进行EIO的错误处理,怀疑内核有问题(没有返EIO,而是返了成功)。看了一下代码,明明返了呀,有点懵。
思考了一下,他们做这个需求的目的,是希望在磁盘上读取数据时,能探测出数据所在的那部分磁盘是否坏掉,如果坏掉则做异常处理,比如放弃数据,避免读取到错误的数据。
这个愿望是好的,它适用于这种场景:磁盘正常的情况下写入了数据。过了一段时间,磁盘上的一部分读不出数据了。
但是,采用了这种方案后是否能避免文件出错呢?
考虑一种场景:磁盘写入时,由于坏道无法写入,导致数据丢失,只写入部分数据。过了一段时间读取数据,只读到之前写入的那部分数据。
在日志里找一下,果然,写入也发生了错误,ext4甚至提到This should not happen!! Data will be lost:
Apr 12 13:52:16 kernel: [28421156.156605] sd 0:1:0:5: [sdf] FAILED Result: hostbyte=DID_OK driverbyte=DRIVER_SENSE
Apr 12 13:52:16 kernel: [28421156.156611] sd 0:1:0:5: [sdf] Sense Key : Medium Error [current]
Apr 12 13:52:16 kernel: [28421156.156613] sd 0:1:0:5: [sdf] Add. Sense: Unrecovered read error
Apr 12 13:52:16 kernel: [28421156.156616] sd 0:1:0:5: [sdf] CDB: Read(10) 28 00 1a 40 08 08 00 00 08 00
Apr 12 13:52:16 kernel: [28421156.156618] blk_update_request: critical medium error, dev sdf, sector 440403976
Apr 12 13:52:16 kernel: [28421156.172615] EXT4-fs error (device sdf1): ext4_wait_block_bitmap:497: comm kworker/u66:0: Cannot read block bitmap - block_group = 1681, block_bitmap = 55050241
Apr 12 13:52:18 kernel: [28421157.768413] EXT4-fs: 160 callbacks suppressed
Apr 12 13:52:18 kernel: [28421157.780602] EXT4-fs (sdf1): Delayed block allocation failed for inode 27532190 at logical offset 389 with max blocks 1 with error 5
Apr 12 13:52:18 kernel: [28421157.811172] EXT4-fs (sdf1): This should not happen!! Data will be lost
那么,在写入时是否能根据errno的值来判断写入失败呢?
不行。因为写入只是把文件缓存置脏,然后直接返回成功。此时数据还未落盘,要过一会集中写入磁盘。此时掉电是会造成数据丢失的。当然,如果写入失败,那数据也可能会丢。看上面的错误打印,执行写入的进程是kworker/u66:0。
此类机器还是要做好磁盘错误打印的监测,并及时处理。如果有出现Data will be lost说明数据已经丢失了,情况很糟糕,不能再拖下去了。