В Linux ядре исправлена (http://kerneltrap.org/node/7518) трудноуловимая ошибка приводящая к потере данных в файловых системах ext2, ext3, reiserfs. Ошибка присутствует во всех ядрах начиная с 2.6.5. Патч прилагается.----
diff --git a/mm/page-writeback.c b/mm/page-writeback.c
index b3a198c..ec01da1 100644
--- a/mm/page-writeback.c
+++ b/mm/page-writeback.c
@@ -862,17 +862,46 @@ int clear_page_dirty_for_io(struct page *page)
{
struct address_space *mapping = page_mapping(page);- if (!mapping)
- return TestClearPageDirty(page);
-
- if (TestClearPageDirty(page)) {
- if (mapping_cap_account_dirty(mapping)) {
- page_mkclean(page);
+ if (mapping && mapping_cap_account_dirty(mapping)) {
+ /*
+ * Yes, Virginia, this is indeed insane.
+ *
+ * We use this sequence to make sure that
+ * (a) we account for dirty stats properly
+ * (b) we tell the low-level filesystem to
+ * mark the whole page dirty if it was
+ * dirty in a pagetable. Only to then
+ * (c) clean the page again and return 1 to
+ * cause the writeback.
+ *
+ * This way we avoid all nasty races with the
+ * dirty bit in multiple places and clearing
+ * them concurrently from different threads.
+ *
+ * Note! Normally the "set_page_dirty(page)"
+ * has no effect on the actual dirty bit - since
+ * that will already usually be set. But we
+ * need the side effects, and it can help us
+ * avoid races.
+ *
+ * We basically use the page "master dirty bit"
+ * as a serialization point for all the different
+ * threds doing their things.
+ *
+ * FIXME! We still have a race here: if somebody
+ * adds the page back to the page tables in
+ * between the "page_mkclean()" and the "TestClearPageDirty()",
+ * we might have it mapped without the dirty bit set.
+ */
+ if (page_mkclean(page))
+ set_page_dirty(page);
+ if (TestClearPageDirty(page)) {
dec_zone_page_state(page, NR_FILE_DIRTY);
+ return 1;
}
- return 1;
+ return 0;
}
- return 0;
+ return TestClearPageDirty(page);
}
EXPORT_SYMBOL(clear_page_dirty_for_io);URL: http://kerneltrap.org/node/7518
Новость: http://www.opennet.me/opennews/art.shtml?num=9388
Шило на мыло...
Как выразился Линус, теперь этот race стал tiny tiny... у вас есть идеи?
Можно поздравить Линуса с находкой такой редкой, но тем ни менее зловредной, ошибкой. Отдельное спасибо сетям p2p - они сумели поймать баг :) Если бы NTFS так же тщательно тестировали, а затем исправляли :).
Согласен с тем,что лучше найденая ошибка чем ненайденая.
Что-то я при первом "наезде" не смог понять, в чём была ошибка. Похоже, речь идёт об отложенной записи страниц на диск, но дальше я увяз.
'The VM layer asked the filesystem to do the write, but the filesystem just didn't do it. I personally think it's because some buffer-head BH_dirty bit got scrogged, but it could be some event that makes the filesystem simply not do the IO because it thinks the "disk queues are too full", so it just says "IO completed", without actually doing anything at all.' (c) Linus. Я так понял, пачтик именно фиксит возможность легко протерять dirty бит из-за race condition, а от этого бита зависит, будет ли страница записана на диск.
Как понял из треда на LORе, баг касается только ext3, смонтированной по дефолту с data=ordered. Хотелось бы уточнить, читать тред на lkml влом :)
Ну незнаю как на ext3, а вот если корневая стоит xfs, то при загрузке она отказывается проверятся, орёт мол unable to run fsck: cannot load shared object libsepol.so все либы от SELinux: на месте. Да и вообще, до этого патча работало же.!!!
Народ, как его применять?Я что то не въехал.
Копируешь этот исходный код в пустой файл, скажем race_fix.diff, сохраняешь.
Копируешь в директорию с исходниками ядра, например /usr/src/linux# cp race_fix.diff /usr/src/linux
# cd /usr/src/linux
# patch -p1 < race_fix.txt
# make V=1Всё!
Сохраняешь код патча в race_fix.diff, копируешь в директорию с исходниками, на всякий случай очищаешь директорию сборки от предыдущих билдов, патчишь, и запускаешь сборку:# cp race_fix.diff /usr/src/linux
# cd /usr/src/linux
# patch -p1 < race_fix.txt
# make V=1
Короче, этот патч для старых ядер... вот переделал под 2.6.19.х=== кусь ===
--- b/mm/page-writeback.c 1970-01-01 00:00:00.000000000 +0300
+++ a/mm/page-writeback.c 2006-12-30 23:53:04.124975836 +0300
@@ -893,16 +893,16 @@
{
struct address_space *mapping = page_mapping(page);
- if (mapping) {
- if (TestClearPageDirty(page)) {
- if (mapping_cap_account_dirty(mapping)) {
- page_mkclean(page);
- dec_zone_page_state(page, NR_FILE_DIRTY);
- }
- return 1;
- }
- return 0;
- }
+ if (mapping && mapping_cap_account_dirty(mapping)) {
+ if (page_mkclean(page))
+ set_page_dirty(page);
+ if (TestClearPageDirty(page))
+ {
+ dec_zone_page_state(page, NR_FILE_DIRTY);
+ return 1;
+ }
+ return 0;
+ }
return TestClearPageDirty(page);
}
EXPORT_SYMBOL(clear_page_dirty_for_io);=== кусь ===