On Thu, 12 May 2011, [email protected] wrote:
> 
> The patch below does not apply to the .38-stable tree.
> If someone wants it applied there, or to any other stable or longterm
> tree, then please email the backport, including the original git commit
> id to <[email protected]>.

Thank you for reporting that the original of this patch failed to apply
to the .33-longterm tree.  Please apply instead the backport below.

Thanks,
Hugh

commit 59a16ead572330deb38e5848151d30ed1af754bc backported
Author: Hugh Dickins <[email protected]>
Date:   Wed May 11 15:13:38 2011 -0700
Subject: [PATCH] tmpfs: fix spurious ENOSPC when racing with unswap
    
    Testing the shmem_swaplist replacements for igrab() revealed another bug:
    writes to /dev/loop0 on a tmpfs file which fills its filesystem were
    sometimes failing with "Buffer I/O error"s.
    
    These came from ENOSPC failures of shmem_getpage(), when racing with
    swapoff: the same could happen when racing with another shmem_getpage(),
    pulling the page in from swap in between our find_lock_page() and our
    taking the info->lock (though not in the single-threaded loop case).
    
    This is unacceptable, and surprising that I've not noticed it before:
    it dates back many years, but (presumably) was made a lot easier to
    reproduce in 2.6.36, which sited a page preallocation in the race window.
    
    Fix it by rechecking the page cache before settling on an ENOSPC error.
    
    Signed-off-by: Hugh Dickins <[email protected]>
    Cc: Konstantin Khlebnikov <[email protected]>
    Cc: <[email protected]>
    Signed-off-by: Andrew Morton <[email protected]>
    Signed-off-by: Linus Torvalds <[email protected]>

--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -1396,18 +1396,13 @@ repeat:
                        if (sbinfo->free_blocks == 0 ||
                            shmem_acct_block(info->flags)) {
                                spin_unlock(&sbinfo->stat_lock);
-                               spin_unlock(&info->lock);
-                               error = -ENOSPC;
-                               goto failed;
+                               goto nospace;
                        }
                        sbinfo->free_blocks--;
                        inode->i_blocks += BLOCKS_PER_PAGE;
                        spin_unlock(&sbinfo->stat_lock);
-               } else if (shmem_acct_block(info->flags)) {
-                       spin_unlock(&info->lock);
-                       error = -ENOSPC;
-                       goto failed;
-               }
+               } else if (shmem_acct_block(info->flags))
+                       goto nospace;
 
                if (!filepage) {
                        int ret;
@@ -1476,6 +1471,24 @@ done:
        *pagep = filepage;
        return 0;
 
+nospace:
+       /*
+        * Perhaps the page was brought in from swap between find_lock_page
+        * and taking info->lock?  We allow for that at add_to_page_cache_lru,
+        * but must also avoid reporting a spurious ENOSPC while working on a
+        * full tmpfs.  (When filepage has been passed in to shmem_getpage, it
+        * is already in page cache, which prevents this race from occurring.)
+        */
+       if (!filepage) {
+               struct page *page = find_get_page(mapping, idx);
+               if (page) {
+                       spin_unlock(&info->lock);
+                       page_cache_release(page);
+                       goto repeat;
+               }
+       }
+       spin_unlock(&info->lock);
+       error = -ENOSPC;
 failed:
        if (*pagep != filepage) {
                unlock_page(filepage);

_______________________________________________
stable mailing list
[email protected]
http://linux.kernel.org/mailman/listinfo/stable

Reply via email to