]> git.neil.brown.name Git - history.git/commitdiff
[PATCH] fix a race between set_page_dirty and truncate
authorAndrew Morton <akpm@zip.com.au>
Sat, 10 Aug 2002 09:44:55 +0000 (02:44 -0700)
committerLinus Torvalds <torvalds@home.transmeta.com>
Sat, 10 Aug 2002 09:44:55 +0000 (02:44 -0700)
Fix a race between set_page_dirty() and truncate.

The page could have been removed from the mapping while this CPU is
spinning on the lock.  __free_pages_ok() will go BUG.

This has not been observed in practice - most callers of
set_page_dirty() hold the page lock which gives exclusion from
truncate.  But zap_pte_range() does not.

A fix for this has been sent to Marcelo also.

mm/page-writeback.c

index 5ebb5673154e3c02576da0495e8c4d35a0e7d1f3..1709a495183ec70e75a58ef988f7de68432bba20 100644 (file)
@@ -433,8 +433,10 @@ int __set_page_dirty_buffers(struct page *page)
 
        if (!TestSetPageDirty(page)) {
                write_lock(&mapping->page_lock);
-               list_del(&page->list);
-               list_add(&page->list, &mapping->dirty_pages);
+               if (page->mapping) {    /* Race with truncate? */
+                       list_del(&page->list);
+                       list_add(&page->list, &mapping->dirty_pages);
+               }
                write_unlock(&mapping->page_lock);
                __mark_inode_dirty(mapping->host, I_DIRTY_PAGES);
        }
@@ -467,8 +469,10 @@ int __set_page_dirty_nobuffers(struct page *page)
 
                if (mapping) {
                        write_lock(&mapping->page_lock);
-                       list_del(&page->list);
-                       list_add(&page->list, &mapping->dirty_pages);
+                       if (page->mapping) {    /* Race with truncate? */
+                               list_del(&page->list);
+                               list_add(&page->list, &mapping->dirty_pages);
+                       }
                        write_unlock(&mapping->page_lock);
                        __mark_inode_dirty(mapping->host, I_DIRTY_PAGES);
                }