Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=d9fe526a83b84edc9c5ff217a00c896bfc20b2ce
Commit:     d9fe526a83b84edc9c5ff217a00c896bfc20b2ce
Parent:     73b1262fa43a778b1e154deea632cdef5009d6a1
Author:     Hugh Dickins <[EMAIL PROTECTED]>
AuthorDate: Mon Feb 4 22:28:51 2008 -0800
Committer:  Linus Torvalds <[EMAIL PROTECTED]>
CommitDate: Tue Feb 5 09:44:15 2008 -0800

    tmpfs: allow filepage alongside swappage
    
    tmpfs has long allowed for a fresh filepage to be created in pagecache, just
    before shmem_getpage gets the chance to match it up with the swappage which
    already belongs to that offset.  But unionfs_writepage now does a
    find_or_create_page, divorced from shmem_getpage, which leaves conflicting
    filepage and swappage outstanding indefinitely, when unionfs is over tmpfs.
    
    Therefore shmem_writepage (where a page is swizzled from file to swap) must
    now be on the lookout for existing swap, ready to free it in favour of the
    more uptodate filepage, instead of BUGging on that clash.  And when the
    add_to_page_cache fails in shmem_unuse_inode, it must defer to an uptodate
    filepage, otherwise swapoff would hang.  Whereas when add_to_page_cache 
fails
    in shmem_getpage, it should retry in the same way it already does.
    
    Signed-off-by: Hugh Dickins <[EMAIL PROTECTED]>
    Signed-off-by: Andrew Morton <[EMAIL PROTECTED]>
    Signed-off-by: Linus Torvalds <[EMAIL PROTECTED]>
---
 mm/shmem.c |   69 ++++++++++++++++++++++++++++++++++++++---------------------
 1 files changed, 44 insertions(+), 25 deletions(-)

diff --git a/mm/shmem.c b/mm/shmem.c
index e577adf..4ae47f5 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -827,6 +827,7 @@ static int shmem_unuse_inode(struct shmem_inode_info *info, 
swp_entry_t entry, s
        struct page *subdir;
        swp_entry_t *ptr;
        int offset;
+       int error;
 
        idx = 0;
        ptr = info->i_direct;
@@ -884,7 +885,20 @@ lost2:
 found:
        idx += offset;
        inode = &info->vfs_inode;
-       if (add_to_page_cache(page, inode->i_mapping, idx, GFP_ATOMIC) == 0) {
+       error = add_to_page_cache(page, inode->i_mapping, idx, GFP_ATOMIC);
+       if (error == -EEXIST) {
+               struct page *filepage = find_get_page(inode->i_mapping, idx);
+               if (filepage) {
+                       /*
+                        * There might be a more uptodate page coming down
+                        * from a stacked writepage: forget our swappage if so.
+                        */
+                       if (PageUptodate(filepage))
+                               error = 0;
+                       page_cache_release(filepage);
+               }
+       }
+       if (!error) {
                delete_from_swap_cache(page);
                set_page_dirty(page);
                info->flags |= SHMEM_PAGEIN;
@@ -937,44 +951,45 @@ static int shmem_writepage(struct page *page, struct 
writeback_control *wbc)
        struct inode *inode;
 
        BUG_ON(!PageLocked(page));
-       /*
-        * shmem_backing_dev_info's capabilities prevent regular writeback or
-        * sync from ever calling shmem_writepage; but a stacking filesystem
-        * may use the ->writepage of its underlying filesystem, in which case
-        * we want to do nothing when that underlying filesystem is tmpfs
-        * (writing out to swap is useful as a response to memory pressure, but
-        * of no use to stabilize the data) - just redirty the page, unlock it
-        * and claim success in this case.  AOP_WRITEPAGE_ACTIVATE, and the
-        * page_mapped check below, must be avoided unless we're in reclaim.
-        */
-       if (!wbc->for_reclaim) {
-               set_page_dirty(page);
-               unlock_page(page);
-               return 0;
-       }
-       BUG_ON(page_mapped(page));
-
        mapping = page->mapping;
        index = page->index;
        inode = mapping->host;
        info = SHMEM_I(inode);
        if (info->flags & VM_LOCKED)
                goto redirty;
-       swap = get_swap_page();
-       if (!swap.val)
+       if (!total_swap_pages)
                goto redirty;
 
+       /*
+        * shmem_backing_dev_info's capabilities prevent regular writeback or
+        * sync from ever calling shmem_writepage; but a stacking filesystem
+        * may use the ->writepage of its underlying filesystem, in which case
+        * tmpfs should write out to swap only in response to memory pressure,
+        * and not for pdflush or sync.  However, in those cases, we do still
+        * want to check if there's a redundant swappage to be discarded.
+        */
+       if (wbc->for_reclaim)
+               swap = get_swap_page();
+       else
+               swap.val = 0;
+
        spin_lock(&info->lock);
-       shmem_recalc_inode(inode);
        if (index >= info->next_index) {
                BUG_ON(!(info->flags & SHMEM_TRUNCATE));
                goto unlock;
        }
        entry = shmem_swp_entry(info, index, NULL);
-       BUG_ON(!entry);
-       BUG_ON(entry->val);
+       if (entry->val) {
+               /*
+                * The more uptodate page coming down from a stacked
+                * writepage should replace our old swappage.
+                */
+               free_swap_and_cache(*entry);
+               shmem_swp_set(info, entry, 0);
+       }
+       shmem_recalc_inode(inode);
 
-       if (add_to_swap_cache(page, swap, GFP_ATOMIC) == 0) {
+       if (swap.val && add_to_swap_cache(page, swap, GFP_ATOMIC) == 0) {
                remove_from_page_cache(page);
                shmem_swp_set(info, entry, swap.val);
                shmem_swp_unmap(entry);
@@ -986,6 +1001,7 @@ static int shmem_writepage(struct page *page, struct 
writeback_control *wbc)
                        spin_unlock(&shmem_swaplist_lock);
                }
                swap_duplicate(swap);
+               BUG_ON(page_mapped(page));
                page_cache_release(page);       /* pagecache ref */
                set_page_dirty(page);
                unlock_page(page);
@@ -998,7 +1014,10 @@ unlock:
        swap_free(swap);
 redirty:
        set_page_dirty(page);
-       return AOP_WRITEPAGE_ACTIVATE;  /* Return with the page locked */
+       if (wbc->for_reclaim)
+               return AOP_WRITEPAGE_ACTIVATE;  /* Return with page locked */
+       unlock_page(page);
+       return 0;
 }
 
 #ifdef CONFIG_NUMA
-
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