TL; DR:如果Linux内核丢失了缓冲的I / O写操作,那么应用程序有什么方法可以找出来?
我知道您必须fsync()
对该文件(及其父目录)具有持久性。问题是,如果内核由于I / O错误而丢失了待写的脏缓冲区,那么应用程序如何检测到它并恢复或中止?
考虑数据库应用程序等,其中写入顺序和写入持久性可能至关重要。
丢了写?怎么样?
在某些情况下,Linux内核的块层失去缓冲已被成功提交的I / O请求write()
,pwrite()
等等,有这样的错误:
Buffer I/O error on device dm-0, logical block 12345
lost page write due to I/O error on dm-0
(请参阅end_buffer_write_sync(...)
和end_buffer_async_write(...)
中的fs/buffer.c
)。
Buffer I/O error on dev dm-0, logical block 12345, lost async page write
由于应用程序write()
将已经返回且没有错误,因此似乎无法将错误报告给应用程序。
检测到他们?
我对内核源代码并不熟悉,但是我认为AS_EIO
它是在异步写入失败的缓冲区上设置的:
set_bit(AS_EIO, &page->mapping->flags);
set_buffer_write_io_error(bh);
clear_buffer_uptodate(bh);
SetPageError(page);
但我不清楚应用程序稍后是否fsync()
在文件中确认其是否在磁盘上时是否能够找到该信息。
它看起来像wait_on_page_writeback_range(...)
在mm/filemap.c
借着do_sync_mapping_range(...)
在fs/sync.c
其中被称为转sys_sync_file_range(...)
。-EIO
如果无法写入一个或多个缓冲区,它将返回。
如果按照我的猜测,这会传播到fsync()
的结果,那么,如果应用程序出现了I / O错误fsync()
并且知道重启后如何重新执行工作时会慌乱并急于解决,那应该是足够的保障吗?
大概没有办法让应用程序知道文件中哪些字节偏移量与丢失的页面相对应,因此如果知道如何可以重写它们,但是如果应用程序重复了自fsync()
文件最后一次成功以来的所有未完成工作,并且重写了任何与文件丢失写入相对应的脏内核缓冲区,都应清除丢失页面上的所有I / O错误标志并允许下一个fsync()
完成-对吗?
难道还有其他无害的情况fsync()
可能会回来-EIO
,而救援和重做工作将过于激烈吗?
为什么?
当然,这种错误应该不会发生。在这种情况下,错误是由dm-multipath
驱动程序的默认值与SAN用来报告分配精简配置的存储失败的感知代码之间不幸的交互作用引起的。但是,这并不是唯一的情况下,他们可能会发生-我也看到了从例如精简配置LVM它报告,13759 libvirt的,泊坞窗等。诸如数据库之类的关键应用程序应尝试应对此类错误,而不是盲目进行,好像一切都很好。
如果内核认为可以在不死于内核恐慌的情况下丢失写入就可以了,那么应用程序必须找到一种应对方法。
实际的影响是,我发现了一个案例,其中SAN的多路径问题导致丢失的写入丢失,从而导致数据库损坏,因为DBMS不知道其写入失败。不好玩。