Commit eaec8c0c authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] tmpfs 3/7 swapoff/truncate race fix

From: Hugh Dickins <hugh@veritas.com>

On 23 July, Sergey S. Kostyliov <rathamahata@php4.ru> reported a tmpfs
BUG_ON(inode->i_blocks) during swapoff: my last version of the fix to
swapoff/truncate race was inadequate, since I_FREEING might get set or
i_size be reduced (from another cpu) the instant after it's tested here.

So revert to the previous version of the fix, shmem_truncate calling
truncate_inode_pages again, if pages still left in cache; but avoid the
recall in usual cases of partial truncation, by having a "pagein" flag
to indicate when recall might be necessary.  (Since those flags already
use VM_ACCOUNT and VM_LOCKED, must redefine another VM_flag for this.)
Sergey and 2.4-aa have run fine with this for a couple of months.
parent d19c7b9d
...@@ -52,6 +52,9 @@ ...@@ -52,6 +52,9 @@
#define VM_ACCT(size) (PAGE_CACHE_ALIGN(size) >> PAGE_SHIFT) #define VM_ACCT(size) (PAGE_CACHE_ALIGN(size) >> PAGE_SHIFT)
/* info->flags needs a VM_flag to handle swapoff/truncate races efficiently */
#define SHMEM_PAGEIN VM_READ
/* Pretend that each entry is of this size in directory's i_size */ /* Pretend that each entry is of this size in directory's i_size */
#define BOGO_DIRENT_SIZE 20 #define BOGO_DIRENT_SIZE 20
...@@ -490,6 +493,16 @@ static void shmem_truncate(struct inode *inode) ...@@ -490,6 +493,16 @@ static void shmem_truncate(struct inode *inode)
} }
done2: done2:
BUG_ON(info->swapped > info->next_index); BUG_ON(info->swapped > info->next_index);
if (inode->i_mapping->nrpages && (info->flags & SHMEM_PAGEIN)) {
/*
* Call truncate_inode_pages again: racing shmem_unuse_inode
* may have swizzled a page in from swap since vmtruncate or
* generic_delete_inode did it, before we lowered next_index.
*/
spin_unlock(&info->lock);
truncate_inode_pages(inode->i_mapping, inode->i_size);
spin_lock(&info->lock);
}
shmem_recalc_inode(inode); shmem_recalc_inode(inode);
spin_unlock(&info->lock); spin_unlock(&info->lock);
} }
...@@ -524,6 +537,19 @@ static int shmem_notify_change(struct dentry *dentry, struct iattr *attr) ...@@ -524,6 +537,19 @@ static int shmem_notify_change(struct dentry *dentry, struct iattr *attr)
attr->ia_size>>PAGE_CACHE_SHIFT, attr->ia_size>>PAGE_CACHE_SHIFT,
&page, SGP_READ); &page, SGP_READ);
} }
/*
* Reset SHMEM_PAGEIN flag so that shmem_truncate can
* detect if any pages might have been added to cache
* after truncate_inode_pages. But we needn't bother
* if it's being fully truncated to zero-length: the
* nrpages check is efficient enough in that case.
*/
if (attr->ia_size) {
struct shmem_inode_info *info = SHMEM_I(inode);
spin_lock(&info->lock);
info->flags &= ~SHMEM_PAGEIN;
spin_unlock(&info->lock);
}
} }
} }
...@@ -638,14 +664,10 @@ static int shmem_unuse_inode(struct shmem_inode_info *info, swp_entry_t entry, s ...@@ -638,14 +664,10 @@ static int shmem_unuse_inode(struct shmem_inode_info *info, swp_entry_t entry, s
found: found:
idx += offset; idx += offset;
inode = &info->vfs_inode; inode = &info->vfs_inode;
if (move_from_swap_cache(page, idx, inode->i_mapping) == 0) {
/* Racing against delete or truncate? Must leave out of page cache */ info->flags |= SHMEM_PAGEIN;
limit = (inode->i_state & I_FREEING)? 0:
(i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
if (idx >= limit ||
move_from_swap_cache(page, idx, inode->i_mapping) == 0)
shmem_swp_set(info, ptr + offset, 0); shmem_swp_set(info, ptr + offset, 0);
}
shmem_swp_unmap(ptr); shmem_swp_unmap(ptr);
spin_unlock(&info->lock); spin_unlock(&info->lock);
/* /*
...@@ -653,7 +675,7 @@ static int shmem_unuse_inode(struct shmem_inode_info *info, swp_entry_t entry, s ...@@ -653,7 +675,7 @@ static int shmem_unuse_inode(struct shmem_inode_info *info, swp_entry_t entry, s
* try_to_unuse will skip over mms, then reincrement count. * try_to_unuse will skip over mms, then reincrement count.
*/ */
swap_free(entry); swap_free(entry);
return idx < limit; return 1;
} }
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment