Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=87df7241bd547da5d4d4a4e5397866dfe422e439
Commit:     87df7241bd547da5d4d4a4e5397866dfe422e439
Parent:     4cbf2aa35e1c189db234190fefc6c83b139ef963
Author:     Nick Piggin <[EMAIL PROTECTED]>
AuthorDate: Tue Jan 30 14:36:27 2007 +1100
Committer:  Linus Torvalds <[EMAIL PROTECTED]>
CommitDate: Mon Jan 29 20:20:42 2007 -0800

    [PATCH] Fix try_to_free_buffer() locking
    
    Fix commit ecdfc9787fe527491baefc22dce8b2dbd5b2908d
    
    Not to put too fine a point on it, but in a nutshell...
    
        __set_page_dirty_buffers() | try_to_free_buffers()
        ---------------------------+---------------------------
                                   | spin_lock(private_lock);
                                   | drop_bufers()
                                   | spin_unlock(private_lock);
        spin_lock(private_lock)    |
        !page_has_buffers()        |
        spin_unlock(private_lock)  |
        SetPageDirty()             |
                                   | cancel_dirty_page()
    
                              oops!
    
    Signed-off-by: Nick Piggin <[EMAIL PROTECTED]>
    Acked-by: Andrew Morton <[EMAIL PROTECTED]>
    Signed-off-by: Linus Torvalds <[EMAIL PROTECTED]>
---
 fs/buffer.c |    6 +++++-
 1 files changed, 5 insertions(+), 1 deletions(-)

diff --git a/fs/buffer.c b/fs/buffer.c
index 460f1c4..1ad674f 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -2844,7 +2844,6 @@ int try_to_free_buffers(struct page *page)
 
        spin_lock(&mapping->private_lock);
        ret = drop_buffers(page, &buffers_to_free);
-       spin_unlock(&mapping->private_lock);
 
        /*
         * If the filesystem writes its buffers by hand (eg ext3)
@@ -2855,9 +2854,14 @@ int try_to_free_buffers(struct page *page)
         * Also, during truncate, discard_buffer will have marked all
         * the page's buffers clean.  We discover that here and clean
         * the page also.
+        *
+        * private_lock must be held over this entire operation in order
+        * to synchronise against __set_page_dirty_buffers and prevent the
+        * dirty bit from being lost.
         */
        if (ret)
                cancel_dirty_page(page, PAGE_CACHE_SIZE);
+       spin_unlock(&mapping->private_lock);
 out:
        if (buffers_to_free) {
                struct buffer_head *bh = buffers_to_free;
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to