Skip to content

Commit

Permalink
mm + fs: store shadow entries in page cache
Browse files Browse the repository at this point in the history
Reclaim will be leaving shadow entries in the page cache radix tree upon
evicting the real page.  As those pages are found from the LRU, an iput()
can lead to the inode being freed concurrently.  At this point, reclaim
must no longer install shadow pages because the inode freeing code needs
to ensure the page tree is really empty.

Add an address_space flag, AS_EXITING, that the inode freeing code sets
under the tree lock before doing the final truncate.  Reclaim will check
for this flag before installing shadow pages.

Signed-off-by: Johannes Weiner <[email protected]>
Reviewed-by: Rik van Riel <[email protected]>
Reviewed-by: Minchan Kim <[email protected]>
Cc: Andrea Arcangeli <[email protected]>
Cc: Bob Liu <[email protected]>
Cc: Christoph Hellwig <[email protected]>
Cc: Dave Chinner <[email protected]>
Cc: Greg Thelen <[email protected]>
Cc: Hugh Dickins <[email protected]>
Cc: Jan Kara <[email protected]>
Cc: KOSAKI Motohiro <[email protected]>
Cc: Luigi Semenzato <[email protected]>
Cc: Mel Gorman <[email protected]>
Cc: Metin Doslu <[email protected]>
Cc: Michel Lespinasse <[email protected]>
Cc: Ozgun Erdogan <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Roman Gushchin <[email protected]>
Cc: Ryan Mallon <[email protected]>
Cc: Tejun Heo <[email protected]>
Cc: Vlastimil Babka <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
  • Loading branch information
hnaz authored and sfrothwell committed Mar 6, 2014
1 parent d575c47 commit c52f57c
Show file tree
Hide file tree
Showing 50 changed files with 147 additions and 65 deletions.
6 changes: 3 additions & 3 deletions Documentation/filesystems/porting
Original file line number Diff line number Diff line change
Expand Up @@ -295,9 +295,9 @@ in the beginning of ->setattr unconditionally.
->clear_inode() and ->delete_inode() are gone; ->evict_inode() should
be used instead. It gets called whenever the inode is evicted, whether it has
remaining links or not. Caller does *not* evict the pagecache or inode-associated
metadata buffers; getting rid of those is responsibility of method, as it had
been for ->delete_inode(). Caller makes sure async writeback cannot be running
for the inode while (or after) ->evict_inode() is called.
metadata buffers; the method has to use truncate_inode_pages_final() to get rid
of those. Caller makes sure async writeback cannot be running for the inode while
(or after) ->evict_inode() is called.

->drop_inode() returns int now; it's called on final iput() with
inode->i_lock held and it returns true if filesystems wants the inode to be
Expand Down
2 changes: 1 addition & 1 deletion drivers/staging/lustre/lustre/llite/llite_lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -1877,7 +1877,7 @@ void ll_delete_inode(struct inode *inode)
cl_sync_file_range(inode, 0, OBD_OBJECT_EOF,
CL_FSYNC_DISCARD, 1);

truncate_inode_pages(&inode->i_data, 0);
truncate_inode_pages_final(&inode->i_data);

/* Workaround for LU-118 */
if (inode->i_data.nrpages) {
Expand Down
2 changes: 1 addition & 1 deletion fs/9p/vfs_inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,7 @@ void v9fs_evict_inode(struct inode *inode)
{
struct v9fs_inode *v9inode = V9FS_I(inode);

truncate_inode_pages(inode->i_mapping, 0);
truncate_inode_pages_final(inode->i_mapping);
clear_inode(inode);
filemap_fdatawrite(inode->i_mapping);

Expand Down
2 changes: 1 addition & 1 deletion fs/affs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ affs_evict_inode(struct inode *inode)
{
unsigned long cache_page;
pr_debug("AFFS: evict_inode(ino=%lu, nlink=%u)\n", inode->i_ino, inode->i_nlink);
truncate_inode_pages(&inode->i_data, 0);
truncate_inode_pages_final(&inode->i_data);

if (!inode->i_nlink) {
inode->i_size = 0;
Expand Down
2 changes: 1 addition & 1 deletion fs/afs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,7 @@ void afs_evict_inode(struct inode *inode)

ASSERTCMP(inode->i_ino, ==, vnode->fid.vnode);

truncate_inode_pages(&inode->i_data, 0);
truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);

afs_give_up_callback(vnode);
Expand Down
2 changes: 1 addition & 1 deletion fs/bfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ static void bfs_evict_inode(struct inode *inode)

dprintf("ino=%08lx\n", ino);

truncate_inode_pages(&inode->i_data, 0);
truncate_inode_pages_final(&inode->i_data);
invalidate_inode_buffers(inode);
clear_inode(inode);

Expand Down
4 changes: 2 additions & 2 deletions fs/block_dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ void kill_bdev(struct block_device *bdev)
{
struct address_space *mapping = bdev->bd_inode->i_mapping;

if (mapping->nrpages == 0)
if (mapping->nrpages == 0 && mapping->nrshadows == 0)
return;

invalidate_bh_lrus();
Expand Down Expand Up @@ -419,7 +419,7 @@ static void bdev_evict_inode(struct inode *inode)
{
struct block_device *bdev = &BDEV_I(inode)->bdev;
struct list_head *p;
truncate_inode_pages(&inode->i_data, 0);
truncate_inode_pages_final(&inode->i_data);
invalidate_inode_buffers(inode); /* is it needed here? */
clear_inode(inode);
spin_lock(&bdev_lock);
Expand Down
2 changes: 1 addition & 1 deletion fs/btrfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -4593,7 +4593,7 @@ static void evict_inode_truncate_pages(struct inode *inode)
struct rb_node *node;

ASSERT(inode->i_state & I_FREEING);
truncate_inode_pages(&inode->i_data, 0);
truncate_inode_pages_final(&inode->i_data);

write_lock(&map_tree->lock);
while (!RB_EMPTY_ROOT(&map_tree->map)) {
Expand Down
2 changes: 1 addition & 1 deletion fs/cifs/cifsfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ cifs_destroy_inode(struct inode *inode)
static void
cifs_evict_inode(struct inode *inode)
{
truncate_inode_pages(&inode->i_data, 0);
truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
cifs_fscache_release_inode_cookie(inode);
}
Expand Down
2 changes: 1 addition & 1 deletion fs/coda/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ static void coda_put_super(struct super_block *sb)

static void coda_evict_inode(struct inode *inode)
{
truncate_inode_pages(&inode->i_data, 0);
truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
coda_cache_clear_inode(inode);
}
Expand Down
2 changes: 1 addition & 1 deletion fs/ecryptfs/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ static int ecryptfs_statfs(struct dentry *dentry, struct kstatfs *buf)
*/
static void ecryptfs_evict_inode(struct inode *inode)
{
truncate_inode_pages(&inode->i_data, 0);
truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
iput(ecryptfs_inode_to_lower(inode));
}
Expand Down
2 changes: 1 addition & 1 deletion fs/exofs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -1486,7 +1486,7 @@ void exofs_evict_inode(struct inode *inode)
struct ore_io_state *ios;
int ret;

truncate_inode_pages(&inode->i_data, 0);
truncate_inode_pages_final(&inode->i_data);

/* TODO: should do better here */
if (inode->i_nlink || is_bad_inode(inode))
Expand Down
2 changes: 1 addition & 1 deletion fs/ext2/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ void ext2_evict_inode(struct inode * inode)
dquot_drop(inode);
}

truncate_inode_pages(&inode->i_data, 0);
truncate_inode_pages_final(&inode->i_data);

if (want_delete) {
sb_start_intwrite(inode->i_sb);
Expand Down
2 changes: 1 addition & 1 deletion fs/ext3/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ void ext3_evict_inode (struct inode *inode)
log_wait_commit(journal, commit_tid);
filemap_write_and_wait(&inode->i_data);
}
truncate_inode_pages(&inode->i_data, 0);
truncate_inode_pages_final(&inode->i_data);

ext3_discard_reservation(inode);
rsv = ei->i_block_alloc_info;
Expand Down
4 changes: 2 additions & 2 deletions fs/ext4/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ void ext4_evict_inode(struct inode *inode)
jbd2_complete_transaction(journal, commit_tid);
filemap_write_and_wait(&inode->i_data);
}
truncate_inode_pages(&inode->i_data, 0);
truncate_inode_pages_final(&inode->i_data);

WARN_ON(atomic_read(&EXT4_I(inode)->i_ioend_count));
goto no_delete;
Expand All @@ -225,7 +225,7 @@ void ext4_evict_inode(struct inode *inode)

if (ext4_should_order_data(inode))
ext4_begin_ordered_truncate(inode, 0);
truncate_inode_pages(&inode->i_data, 0);
truncate_inode_pages_final(&inode->i_data);

WARN_ON(atomic_read(&EXT4_I(inode)->i_ioend_count));
if (is_bad_inode(inode))
Expand Down
2 changes: 1 addition & 1 deletion fs/f2fs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ void f2fs_evict_inode(struct inode *inode)
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);

trace_f2fs_evict_inode(inode);
truncate_inode_pages(&inode->i_data, 0);
truncate_inode_pages_final(&inode->i_data);

if (inode->i_ino == F2FS_NODE_INO(sbi) ||
inode->i_ino == F2FS_META_INO(sbi))
Expand Down
2 changes: 1 addition & 1 deletion fs/fat/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,7 @@ EXPORT_SYMBOL_GPL(fat_build_inode);

static void fat_evict_inode(struct inode *inode)
{
truncate_inode_pages(&inode->i_data, 0);
truncate_inode_pages_final(&inode->i_data);
if (!inode->i_nlink) {
inode->i_size = 0;
fat_truncate_blocks(inode, 0);
Expand Down
2 changes: 1 addition & 1 deletion fs/freevxfs/vxfs_inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ static void vxfs_i_callback(struct rcu_head *head)
void
vxfs_evict_inode(struct inode *ip)
{
truncate_inode_pages(&ip->i_data, 0);
truncate_inode_pages_final(&ip->i_data);
clear_inode(ip);
call_rcu(&ip->i_rcu, vxfs_i_callback);
}
2 changes: 1 addition & 1 deletion fs/fuse/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ static void fuse_destroy_inode(struct inode *inode)

static void fuse_evict_inode(struct inode *inode)
{
truncate_inode_pages(&inode->i_data, 0);
truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
if (inode->i_sb->s_flags & MS_ACTIVE) {
struct fuse_conn *fc = get_fuse_conn(inode);
Expand Down
2 changes: 1 addition & 1 deletion fs/gfs2/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -1558,7 +1558,7 @@ static void gfs2_evict_inode(struct inode *inode)
fs_warn(sdp, "gfs2_evict_inode: %d\n", error);
out:
/* Case 3 starts here */
truncate_inode_pages(&inode->i_data, 0);
truncate_inode_pages_final(&inode->i_data);
gfs2_rs_delete(ip, NULL);
gfs2_ordered_del_inode(ip);
clear_inode(inode);
Expand Down
2 changes: 1 addition & 1 deletion fs/hfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@ static struct dentry *hfs_file_lookup(struct inode *dir, struct dentry *dentry,

void hfs_evict_inode(struct inode *inode)
{
truncate_inode_pages(&inode->i_data, 0);
truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
if (HFS_IS_RSRC(inode) && HFS_I(inode)->rsrc_inode) {
HFS_I(HFS_I(inode)->rsrc_inode)->rsrc_inode = NULL;
Expand Down
2 changes: 1 addition & 1 deletion fs/hfsplus/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ static int hfsplus_write_inode(struct inode *inode,
static void hfsplus_evict_inode(struct inode *inode)
{
hfs_dbg(INODE, "hfsplus_evict_inode: %lu\n", inode->i_ino);
truncate_inode_pages(&inode->i_data, 0);
truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
if (HFSPLUS_IS_RSRC(inode)) {
HFSPLUS_I(HFSPLUS_I(inode)->rsrc_inode)->rsrc_inode = NULL;
Expand Down
2 changes: 1 addition & 1 deletion fs/hostfs/hostfs_kern.c
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ static struct inode *hostfs_alloc_inode(struct super_block *sb)

static void hostfs_evict_inode(struct inode *inode)
{
truncate_inode_pages(&inode->i_data, 0);
truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
if (HOSTFS_I(inode)->fd != -1) {
close_file(&HOSTFS_I(inode)->fd);
Expand Down
2 changes: 1 addition & 1 deletion fs/hpfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ void hpfs_write_if_changed(struct inode *inode)

void hpfs_evict_inode(struct inode *inode)
{
truncate_inode_pages(&inode->i_data, 0);
truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
if (!inode->i_nlink) {
hpfs_lock(inode->i_sb);
Expand Down
4 changes: 2 additions & 2 deletions fs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,7 @@ void clear_inode(struct inode *inode)
*/
spin_lock_irq(&inode->i_data.tree_lock);
BUG_ON(inode->i_data.nrpages);
BUG_ON(inode->i_data.nrshadows);
spin_unlock_irq(&inode->i_data.tree_lock);
BUG_ON(!list_empty(&inode->i_data.private_list));
BUG_ON(!(inode->i_state & I_FREEING));
Expand Down Expand Up @@ -548,8 +549,7 @@ static void evict(struct inode *inode)
if (op->evict_inode) {
op->evict_inode(inode);
} else {
if (inode->i_data.nrpages)
truncate_inode_pages(&inode->i_data, 0);
truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
}
if (S_ISBLK(inode->i_mode) && inode->i_bdev)
Expand Down
2 changes: 1 addition & 1 deletion fs/jffs2/fs.c
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ void jffs2_evict_inode (struct inode *inode)

jffs2_dbg(1, "%s(): ino #%lu mode %o\n",
__func__, inode->i_ino, inode->i_mode);
truncate_inode_pages(&inode->i_data, 0);
truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
jffs2_do_clear_inode(c, f);
}
Expand Down
4 changes: 2 additions & 2 deletions fs/jfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ void jfs_evict_inode(struct inode *inode)
dquot_initialize(inode);

if (JFS_IP(inode)->fileset == FILESYSTEM_I) {
truncate_inode_pages(&inode->i_data, 0);
truncate_inode_pages_final(&inode->i_data);

if (test_cflag(COMMIT_Freewmap, inode))
jfs_free_zero_link(inode);
Expand All @@ -168,7 +168,7 @@ void jfs_evict_inode(struct inode *inode)
dquot_free_inode(inode);
}
} else {
truncate_inode_pages(&inode->i_data, 0);
truncate_inode_pages_final(&inode->i_data);
}
clear_inode(inode);
dquot_drop(inode);
Expand Down
2 changes: 1 addition & 1 deletion fs/kernfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ void kernfs_evict_inode(struct inode *inode)
{
struct kernfs_node *kn = inode->i_private;

truncate_inode_pages(&inode->i_data, 0);
truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
kernfs_put(kn);
}
Expand Down
2 changes: 1 addition & 1 deletion fs/logfs/readwrite.c
Original file line number Diff line number Diff line change
Expand Up @@ -2180,7 +2180,7 @@ void logfs_evict_inode(struct inode *inode)
do_delete_inode(inode);
}
}
truncate_inode_pages(&inode->i_data, 0);
truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);

/* Cheaper version of write_inode. All changes are concealed in
Expand Down
2 changes: 1 addition & 1 deletion fs/minix/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ static int minix_remount (struct super_block * sb, int * flags, char * data);

static void minix_evict_inode(struct inode *inode)
{
truncate_inode_pages(&inode->i_data, 0);
truncate_inode_pages_final(&inode->i_data);
if (!inode->i_nlink) {
inode->i_size = 0;
minix_truncate(inode);
Expand Down
2 changes: 1 addition & 1 deletion fs/ncpfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ ncp_iget(struct super_block *sb, struct ncp_entry_info *info)
static void
ncp_evict_inode(struct inode *inode)
{
truncate_inode_pages(&inode->i_data, 0);
truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);

if (S_ISDIR(inode->i_mode)) {
Expand Down
2 changes: 1 addition & 1 deletion fs/nfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ EXPORT_SYMBOL_GPL(nfs_clear_inode);

void nfs_evict_inode(struct inode *inode)
{
truncate_inode_pages(&inode->i_data, 0);
truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
nfs_clear_inode(inode);
}
Expand Down
2 changes: 1 addition & 1 deletion fs/nfs/nfs4super.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ static int nfs4_write_inode(struct inode *inode, struct writeback_control *wbc)
*/
static void nfs4_evict_inode(struct inode *inode)
{
truncate_inode_pages(&inode->i_data, 0);
truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
pnfs_return_layout(inode);
pnfs_destroy_layout(NFS_I(inode));
Expand Down
6 changes: 2 additions & 4 deletions fs/nilfs2/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -783,16 +783,14 @@ void nilfs_evict_inode(struct inode *inode)
int ret;

if (inode->i_nlink || !ii->i_root || unlikely(is_bad_inode(inode))) {
if (inode->i_data.nrpages)
truncate_inode_pages(&inode->i_data, 0);
truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
nilfs_clear_inode(inode);
return;
}
nilfs_transaction_begin(sb, &ti, 0); /* never fails */

if (inode->i_data.nrpages)
truncate_inode_pages(&inode->i_data, 0);
truncate_inode_pages_final(&inode->i_data);

/* TODO: some of the following operations may fail. */
nilfs_truncate_bmap(ii, 0);
Expand Down
2 changes: 1 addition & 1 deletion fs/ntfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -2259,7 +2259,7 @@ void ntfs_evict_big_inode(struct inode *vi)
{
ntfs_inode *ni = NTFS_I(vi);

truncate_inode_pages(&vi->i_data, 0);
truncate_inode_pages_final(&vi->i_data);
clear_inode(vi);

#ifdef NTFS_RW
Expand Down
4 changes: 2 additions & 2 deletions fs/ocfs2/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -964,7 +964,7 @@ static void ocfs2_cleanup_delete_inode(struct inode *inode,
(unsigned long long)OCFS2_I(inode)->ip_blkno, sync_data);
if (sync_data)
filemap_write_and_wait(inode->i_mapping);
truncate_inode_pages(&inode->i_data, 0);
truncate_inode_pages_final(&inode->i_data);
}

static void ocfs2_delete_inode(struct inode *inode)
Expand Down Expand Up @@ -1181,7 +1181,7 @@ void ocfs2_evict_inode(struct inode *inode)
(OCFS2_I(inode)->ip_flags & OCFS2_INODE_MAYBE_ORPHANED)) {
ocfs2_delete_inode(inode);
} else {
truncate_inode_pages(&inode->i_data, 0);
truncate_inode_pages_final(&inode->i_data);
}
ocfs2_clear_inode(inode);
}
Expand Down
2 changes: 1 addition & 1 deletion fs/omfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ int omfs_sync_inode(struct inode *inode)
*/
static void omfs_evict_inode(struct inode *inode)
{
truncate_inode_pages(&inode->i_data, 0);
truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);

if (inode->i_nlink)
Expand Down
Loading

0 comments on commit c52f57c

Please sign in to comment.