forked from Minki/linux
inode: convert inode lru list to generic lru list code.
[glommer@openvz.org: adapted for new LRU return codes] Signed-off-by: Dave Chinner <dchinner@redhat.com> Signed-off-by: Glauber Costa <glommer@openvz.org> Cc: "Theodore Ts'o" <tytso@mit.edu> Cc: Adrian Hunter <adrian.hunter@intel.com> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: Artem Bityutskiy <artem.bityutskiy@linux.intel.com> Cc: Arve Hjønnevåg <arve@android.com> Cc: Carlos Maiolino <cmaiolino@redhat.com> Cc: Christoph Hellwig <hch@lst.de> Cc: Chuck Lever <chuck.lever@oracle.com> Cc: Daniel Vetter <daniel.vetter@ffwll.ch> Cc: David Rientjes <rientjes@google.com> Cc: Gleb Natapov <gleb@redhat.com> Cc: Greg Thelen <gthelen@google.com> Cc: J. Bruce Fields <bfields@redhat.com> Cc: Jan Kara <jack@suse.cz> Cc: Jerome Glisse <jglisse@redhat.com> Cc: John Stultz <john.stultz@linaro.org> Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> Cc: Kent Overstreet <koverstreet@google.com> Cc: Kirill A. Shutemov <kirill.shutemov@linux.intel.com> Cc: Marcelo Tosatti <mtosatti@redhat.com> Cc: Mel Gorman <mgorman@suse.de> Cc: Steven Whitehouse <swhiteho@redhat.com> Cc: Thomas Hellstrom <thellstrom@vmware.com> Cc: Trond Myklebust <Trond.Myklebust@netapp.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
parent
a38e408248
commit
bc3b14cb2d
187
fs/inode.c
187
fs/inode.c
@ -17,6 +17,7 @@
|
||||
#include <linux/prefetch.h>
|
||||
#include <linux/buffer_head.h> /* for inode_has_buffers */
|
||||
#include <linux/ratelimit.h>
|
||||
#include <linux/list_lru.h>
|
||||
#include "internal.h"
|
||||
|
||||
/*
|
||||
@ -24,7 +25,7 @@
|
||||
*
|
||||
* inode->i_lock protects:
|
||||
* inode->i_state, inode->i_hash, __iget()
|
||||
* inode->i_sb->s_inode_lru_lock protects:
|
||||
* Inode LRU list locks protect:
|
||||
* inode->i_sb->s_inode_lru, inode->i_lru
|
||||
* inode_sb_list_lock protects:
|
||||
* sb->s_inodes, inode->i_sb_list
|
||||
@ -37,7 +38,7 @@
|
||||
*
|
||||
* inode_sb_list_lock
|
||||
* inode->i_lock
|
||||
* inode->i_sb->s_inode_lru_lock
|
||||
* Inode LRU list locks
|
||||
*
|
||||
* bdi->wb.list_lock
|
||||
* inode->i_lock
|
||||
@ -401,13 +402,8 @@ EXPORT_SYMBOL(ihold);
|
||||
|
||||
static void inode_lru_list_add(struct inode *inode)
|
||||
{
|
||||
spin_lock(&inode->i_sb->s_inode_lru_lock);
|
||||
if (list_empty(&inode->i_lru)) {
|
||||
list_add(&inode->i_lru, &inode->i_sb->s_inode_lru);
|
||||
inode->i_sb->s_nr_inodes_unused++;
|
||||
if (list_lru_add(&inode->i_sb->s_inode_lru, &inode->i_lru))
|
||||
this_cpu_inc(nr_unused);
|
||||
}
|
||||
spin_unlock(&inode->i_sb->s_inode_lru_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -425,13 +421,9 @@ void inode_add_lru(struct inode *inode)
|
||||
|
||||
static void inode_lru_list_del(struct inode *inode)
|
||||
{
|
||||
spin_lock(&inode->i_sb->s_inode_lru_lock);
|
||||
if (!list_empty(&inode->i_lru)) {
|
||||
list_del_init(&inode->i_lru);
|
||||
inode->i_sb->s_nr_inodes_unused--;
|
||||
|
||||
if (list_lru_del(&inode->i_sb->s_inode_lru, &inode->i_lru))
|
||||
this_cpu_dec(nr_unused);
|
||||
}
|
||||
spin_unlock(&inode->i_sb->s_inode_lru_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -675,24 +667,8 @@ int invalidate_inodes(struct super_block *sb, bool kill_dirty)
|
||||
return busy;
|
||||
}
|
||||
|
||||
static int can_unuse(struct inode *inode)
|
||||
{
|
||||
if (inode->i_state & ~I_REFERENCED)
|
||||
return 0;
|
||||
if (inode_has_buffers(inode))
|
||||
return 0;
|
||||
if (atomic_read(&inode->i_count))
|
||||
return 0;
|
||||
if (inode->i_data.nrpages)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Walk the superblock inode LRU for freeable inodes and attempt to free them.
|
||||
* This is called from the superblock shrinker function with a number of inodes
|
||||
* to trim from the LRU. Inodes to be freed are moved to a temporary list and
|
||||
* then are freed outside inode_lock by dispose_list().
|
||||
* Isolate the inode from the LRU in preparation for freeing it.
|
||||
*
|
||||
* Any inodes which are pinned purely because of attached pagecache have their
|
||||
* pagecache removed. If the inode has metadata buffers attached to
|
||||
@ -706,90 +682,79 @@ static int can_unuse(struct inode *inode)
|
||||
* LRU does not have strict ordering. Hence we don't want to reclaim inodes
|
||||
* with this flag set because they are the inodes that are out of order.
|
||||
*/
|
||||
static enum lru_status
|
||||
inode_lru_isolate(struct list_head *item, spinlock_t *lru_lock, void *arg)
|
||||
{
|
||||
struct list_head *freeable = arg;
|
||||
struct inode *inode = container_of(item, struct inode, i_lru);
|
||||
|
||||
/*
|
||||
* we are inverting the lru lock/inode->i_lock here, so use a trylock.
|
||||
* If we fail to get the lock, just skip it.
|
||||
*/
|
||||
if (!spin_trylock(&inode->i_lock))
|
||||
return LRU_SKIP;
|
||||
|
||||
/*
|
||||
* Referenced or dirty inodes are still in use. Give them another pass
|
||||
* through the LRU as we canot reclaim them now.
|
||||
*/
|
||||
if (atomic_read(&inode->i_count) ||
|
||||
(inode->i_state & ~I_REFERENCED)) {
|
||||
list_del_init(&inode->i_lru);
|
||||
spin_unlock(&inode->i_lock);
|
||||
this_cpu_dec(nr_unused);
|
||||
return LRU_REMOVED;
|
||||
}
|
||||
|
||||
/* recently referenced inodes get one more pass */
|
||||
if (inode->i_state & I_REFERENCED) {
|
||||
inode->i_state &= ~I_REFERENCED;
|
||||
spin_unlock(&inode->i_lock);
|
||||
return LRU_ROTATE;
|
||||
}
|
||||
|
||||
if (inode_has_buffers(inode) || inode->i_data.nrpages) {
|
||||
__iget(inode);
|
||||
spin_unlock(&inode->i_lock);
|
||||
spin_unlock(lru_lock);
|
||||
if (remove_inode_buffers(inode)) {
|
||||
unsigned long reap;
|
||||
reap = invalidate_mapping_pages(&inode->i_data, 0, -1);
|
||||
if (current_is_kswapd())
|
||||
__count_vm_events(KSWAPD_INODESTEAL, reap);
|
||||
else
|
||||
__count_vm_events(PGINODESTEAL, reap);
|
||||
if (current->reclaim_state)
|
||||
current->reclaim_state->reclaimed_slab += reap;
|
||||
}
|
||||
iput(inode);
|
||||
spin_lock(lru_lock);
|
||||
return LRU_RETRY;
|
||||
}
|
||||
|
||||
WARN_ON(inode->i_state & I_NEW);
|
||||
inode->i_state |= I_FREEING;
|
||||
spin_unlock(&inode->i_lock);
|
||||
|
||||
list_move(&inode->i_lru, freeable);
|
||||
this_cpu_dec(nr_unused);
|
||||
return LRU_REMOVED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Walk the superblock inode LRU for freeable inodes and attempt to free them.
|
||||
* This is called from the superblock shrinker function with a number of inodes
|
||||
* to trim from the LRU. Inodes to be freed are moved to a temporary list and
|
||||
* then are freed outside inode_lock by dispose_list().
|
||||
*/
|
||||
long prune_icache_sb(struct super_block *sb, unsigned long nr_to_scan)
|
||||
{
|
||||
LIST_HEAD(freeable);
|
||||
long nr_scanned;
|
||||
long freed = 0;
|
||||
unsigned long reap = 0;
|
||||
|
||||
spin_lock(&sb->s_inode_lru_lock);
|
||||
for (nr_scanned = nr_to_scan; nr_scanned >= 0; nr_scanned--) {
|
||||
struct inode *inode;
|
||||
|
||||
if (list_empty(&sb->s_inode_lru))
|
||||
break;
|
||||
|
||||
inode = list_entry(sb->s_inode_lru.prev, struct inode, i_lru);
|
||||
|
||||
/*
|
||||
* we are inverting the sb->s_inode_lru_lock/inode->i_lock here,
|
||||
* so use a trylock. If we fail to get the lock, just move the
|
||||
* inode to the back of the list so we don't spin on it.
|
||||
*/
|
||||
if (!spin_trylock(&inode->i_lock)) {
|
||||
list_move(&inode->i_lru, &sb->s_inode_lru);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Referenced or dirty inodes are still in use. Give them
|
||||
* another pass through the LRU as we canot reclaim them now.
|
||||
*/
|
||||
if (atomic_read(&inode->i_count) ||
|
||||
(inode->i_state & ~I_REFERENCED)) {
|
||||
list_del_init(&inode->i_lru);
|
||||
spin_unlock(&inode->i_lock);
|
||||
sb->s_nr_inodes_unused--;
|
||||
this_cpu_dec(nr_unused);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* recently referenced inodes get one more pass */
|
||||
if (inode->i_state & I_REFERENCED) {
|
||||
inode->i_state &= ~I_REFERENCED;
|
||||
list_move(&inode->i_lru, &sb->s_inode_lru);
|
||||
spin_unlock(&inode->i_lock);
|
||||
continue;
|
||||
}
|
||||
if (inode_has_buffers(inode) || inode->i_data.nrpages) {
|
||||
__iget(inode);
|
||||
spin_unlock(&inode->i_lock);
|
||||
spin_unlock(&sb->s_inode_lru_lock);
|
||||
if (remove_inode_buffers(inode))
|
||||
reap += invalidate_mapping_pages(&inode->i_data,
|
||||
0, -1);
|
||||
iput(inode);
|
||||
spin_lock(&sb->s_inode_lru_lock);
|
||||
|
||||
if (inode != list_entry(sb->s_inode_lru.next,
|
||||
struct inode, i_lru))
|
||||
continue; /* wrong inode or list_empty */
|
||||
/* avoid lock inversions with trylock */
|
||||
if (!spin_trylock(&inode->i_lock))
|
||||
continue;
|
||||
if (!can_unuse(inode)) {
|
||||
spin_unlock(&inode->i_lock);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
WARN_ON(inode->i_state & I_NEW);
|
||||
inode->i_state |= I_FREEING;
|
||||
spin_unlock(&inode->i_lock);
|
||||
|
||||
list_move(&inode->i_lru, &freeable);
|
||||
sb->s_nr_inodes_unused--;
|
||||
this_cpu_dec(nr_unused);
|
||||
freed++;
|
||||
}
|
||||
if (current_is_kswapd())
|
||||
__count_vm_events(KSWAPD_INODESTEAL, reap);
|
||||
else
|
||||
__count_vm_events(PGINODESTEAL, reap);
|
||||
spin_unlock(&sb->s_inode_lru_lock);
|
||||
if (current->reclaim_state)
|
||||
current->reclaim_state->reclaimed_slab += reap;
|
||||
long freed;
|
||||
|
||||
freed = list_lru_walk(&sb->s_inode_lru, inode_lru_isolate,
|
||||
&freeable, nr_to_scan);
|
||||
dispose_list(&freeable);
|
||||
return freed;
|
||||
}
|
||||
|
12
fs/super.c
12
fs/super.c
@ -78,14 +78,13 @@ static unsigned long super_cache_scan(struct shrinker *shrink,
|
||||
if (sb->s_op->nr_cached_objects)
|
||||
fs_objects = sb->s_op->nr_cached_objects(sb);
|
||||
|
||||
total_objects = sb->s_nr_dentry_unused +
|
||||
sb->s_nr_inodes_unused + fs_objects + 1;
|
||||
inodes = list_lru_count(&sb->s_inode_lru);
|
||||
total_objects = sb->s_nr_dentry_unused + inodes + fs_objects + 1;
|
||||
|
||||
/* proportion the scan between the caches */
|
||||
dentries = mult_frac(sc->nr_to_scan, sb->s_nr_dentry_unused,
|
||||
total_objects);
|
||||
inodes = mult_frac(sc->nr_to_scan, sb->s_nr_inodes_unused,
|
||||
total_objects);
|
||||
inodes = mult_frac(sc->nr_to_scan, inodes, total_objects);
|
||||
|
||||
/*
|
||||
* prune the dcache first as the icache is pinned by it, then
|
||||
@ -119,7 +118,7 @@ static unsigned long super_cache_count(struct shrinker *shrink,
|
||||
total_objects = sb->s_op->nr_cached_objects(sb);
|
||||
|
||||
total_objects += sb->s_nr_dentry_unused;
|
||||
total_objects += sb->s_nr_inodes_unused;
|
||||
total_objects += list_lru_count(&sb->s_inode_lru);
|
||||
|
||||
total_objects = vfs_pressure_ratio(total_objects);
|
||||
drop_super(sb);
|
||||
@ -194,8 +193,7 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags)
|
||||
INIT_LIST_HEAD(&s->s_inodes);
|
||||
INIT_LIST_HEAD(&s->s_dentry_lru);
|
||||
spin_lock_init(&s->s_dentry_lru_lock);
|
||||
INIT_LIST_HEAD(&s->s_inode_lru);
|
||||
spin_lock_init(&s->s_inode_lru_lock);
|
||||
list_lru_init(&s->s_inode_lru);
|
||||
INIT_LIST_HEAD(&s->s_mounts);
|
||||
init_rwsem(&s->s_umount);
|
||||
lockdep_set_class(&s->s_umount, &type->s_umount_key);
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <linux/stat.h>
|
||||
#include <linux/cache.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/list_lru.h>
|
||||
#include <linux/llist.h>
|
||||
#include <linux/radix-tree.h>
|
||||
#include <linux/rbtree.h>
|
||||
@ -1275,10 +1276,7 @@ struct super_block {
|
||||
struct list_head s_dentry_lru; /* unused dentry lru */
|
||||
long s_nr_dentry_unused; /* # of dentry on lru */
|
||||
|
||||
/* s_inode_lru_lock protects s_inode_lru and s_nr_inodes_unused */
|
||||
spinlock_t s_inode_lru_lock ____cacheline_aligned_in_smp;
|
||||
struct list_head s_inode_lru; /* unused inode lru */
|
||||
long s_nr_inodes_unused; /* # of inodes on lru */
|
||||
struct list_lru s_inode_lru ____cacheline_aligned_in_smp;
|
||||
|
||||
struct block_device *s_bdev;
|
||||
struct backing_dev_info *s_bdi;
|
||||
|
Loading…
Reference in New Issue
Block a user