>From 4fdc5d9a66dfe0286ef4f4a7f53fd3b15086470f Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 19 Nov 2012 20:01:16 +0100 Subject: [PATCH] writeback: Put unused inodes to LRU after writeback completion Commit 169ebd90 removed iget-iput pair from inode writeback. As a side effect, inodes that are dirty during iput_final() call won't be ever added to inode LRU (iput_final() doesn't add dirty inodes to LRU and later when the inode is cleaned there's noone to add the inode there). Thus inodes are effectively unreclaimable until someone looks them up again. Practical effect of this bug is limited by the fact that inodes are pinned by a dentry for long enough that the inode gets cleaned. But still the bug can have nasty consequences leading up to OOM conditions under certain circumstances. Following can easily reproduce the problem: for (( i = 0; i < 1000; i++ )); do mkdir $i for (( j = 0; j < 1000; j++ )); do touch $i/$j echo 2 > /proc/sys/vm/drop_caches done done then one needs to run 'sync; ls -lR' to make inodes reclaimable again. We fix the issue by inserting unused clean inodes into the LRU after writeback finishes in inode_sync_complete(). CC: Al Viro Reported-by: OGAWA Hirofumi Signed-off-by: Jan Kara --- fs/fs-writeback.c | 3 +++ fs/inode.c | 2 +- fs/internal.h | 1 + 3 files changed, 5 insertions(+), 1 deletions(-) diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 51ea267..ed7613b 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -227,6 +227,9 @@ static void requeue_io(struct inode *inode, struct bdi_writeback *wb) static void inode_sync_complete(struct inode *inode) { + /* If inode is clean an unused, put it into LRU now. */ + if (!(inode->i_state & I_DIRTY) && !atomic_read(&inode->i_count)) + inode_lru_list_add(inode); inode->i_state &= ~I_SYNC; /* Waiters must see I_SYNC cleared before being woken up */ smp_mb(); diff --git a/fs/inode.c b/fs/inode.c index b03c719..275e447 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -397,7 +397,7 @@ void ihold(struct inode *inode) } EXPORT_SYMBOL(ihold); -static void inode_lru_list_add(struct inode *inode) +void inode_lru_list_add(struct inode *inode) { spin_lock(&inode->i_sb->s_inode_lru_lock); if (list_empty(&inode->i_lru)) { diff --git a/fs/internal.h b/fs/internal.h index 916b7cb..3ecf43d 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -110,6 +110,7 @@ extern int open_check_o_direct(struct file *f); * inode.c */ extern spinlock_t inode_sb_list_lock; +extern void inode_lru_list_add(struct inode *inode); /* * fs-writeback.c -- 1.7.1