Commit e80dfa19 authored by Dave Chinner's avatar Dave Chinner Committed by Al Viro

xfs: convert buftarg LRU to generic code

Convert the buftarg LRU to use the new generic LRU list and take advantage
of the functionality it supplies to make the buffer cache shrinker node
aware.
Signed-off-by: default avatarGlauber Costa <glommer@openvz.org>
Signed-off-by: default avatarDave Chinner <dchinner@redhat.com>
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: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 9b17c623
...@@ -86,20 +86,14 @@ xfs_buf_vmap_len( ...@@ -86,20 +86,14 @@ xfs_buf_vmap_len(
* The LRU takes a new reference to the buffer so that it will only be freed * The LRU takes a new reference to the buffer so that it will only be freed
* once the shrinker takes the buffer off the LRU. * once the shrinker takes the buffer off the LRU.
*/ */
STATIC void static void
xfs_buf_lru_add( xfs_buf_lru_add(
struct xfs_buf *bp) struct xfs_buf *bp)
{ {
struct xfs_buftarg *btp = bp->b_target; if (list_lru_add(&bp->b_target->bt_lru, &bp->b_lru)) {
spin_lock(&btp->bt_lru_lock);
if (list_empty(&bp->b_lru)) {
atomic_inc(&bp->b_hold);
list_add_tail(&bp->b_lru, &btp->bt_lru);
btp->bt_lru_nr++;
bp->b_lru_flags &= ~_XBF_LRU_DISPOSE; bp->b_lru_flags &= ~_XBF_LRU_DISPOSE;
atomic_inc(&bp->b_hold);
} }
spin_unlock(&btp->bt_lru_lock);
} }
/* /*
...@@ -108,24 +102,13 @@ xfs_buf_lru_add( ...@@ -108,24 +102,13 @@ xfs_buf_lru_add(
* The unlocked check is safe here because it only occurs when there are not * The unlocked check is safe here because it only occurs when there are not
* b_lru_ref counts left on the inode under the pag->pag_buf_lock. it is there * b_lru_ref counts left on the inode under the pag->pag_buf_lock. it is there
* to optimise the shrinker removing the buffer from the LRU and calling * to optimise the shrinker removing the buffer from the LRU and calling
* xfs_buf_free(). i.e. it removes an unnecessary round trip on the * xfs_buf_free().
* bt_lru_lock.
*/ */
STATIC void static void
xfs_buf_lru_del( xfs_buf_lru_del(
struct xfs_buf *bp) struct xfs_buf *bp)
{ {
struct xfs_buftarg *btp = bp->b_target; list_lru_del(&bp->b_target->bt_lru, &bp->b_lru);
if (list_empty(&bp->b_lru))
return;
spin_lock(&btp->bt_lru_lock);
if (!list_empty(&bp->b_lru)) {
list_del_init(&bp->b_lru);
btp->bt_lru_nr--;
}
spin_unlock(&btp->bt_lru_lock);
} }
/* /*
...@@ -152,18 +135,10 @@ xfs_buf_stale( ...@@ -152,18 +135,10 @@ xfs_buf_stale(
bp->b_flags &= ~_XBF_DELWRI_Q; bp->b_flags &= ~_XBF_DELWRI_Q;
atomic_set(&(bp)->b_lru_ref, 0); atomic_set(&(bp)->b_lru_ref, 0);
if (!list_empty(&bp->b_lru)) { if (!(bp->b_lru_flags & _XBF_LRU_DISPOSE) &&
struct xfs_buftarg *btp = bp->b_target; (list_lru_del(&bp->b_target->bt_lru, &bp->b_lru)))
spin_lock(&btp->bt_lru_lock);
if (!list_empty(&bp->b_lru) &&
!(bp->b_lru_flags & _XBF_LRU_DISPOSE)) {
list_del_init(&bp->b_lru);
btp->bt_lru_nr--;
atomic_dec(&bp->b_hold); atomic_dec(&bp->b_hold);
}
spin_unlock(&btp->bt_lru_lock);
}
ASSERT(atomic_read(&bp->b_hold) >= 1); ASSERT(atomic_read(&bp->b_hold) >= 1);
} }
...@@ -1502,83 +1477,97 @@ xfs_buf_iomove( ...@@ -1502,83 +1477,97 @@ xfs_buf_iomove(
* returned. These buffers will have an elevated hold count, so wait on those * returned. These buffers will have an elevated hold count, so wait on those
* while freeing all the buffers only held by the LRU. * while freeing all the buffers only held by the LRU.
*/ */
void static enum lru_status
xfs_wait_buftarg( xfs_buftarg_wait_rele(
struct xfs_buftarg *btp) struct list_head *item,
spinlock_t *lru_lock,
void *arg)
{ {
struct xfs_buf *bp; struct xfs_buf *bp = container_of(item, struct xfs_buf, b_lru);
restart:
spin_lock(&btp->bt_lru_lock);
while (!list_empty(&btp->bt_lru)) {
bp = list_first_entry(&btp->bt_lru, struct xfs_buf, b_lru);
if (atomic_read(&bp->b_hold) > 1) { if (atomic_read(&bp->b_hold) > 1) {
/* need to wait */
trace_xfs_buf_wait_buftarg(bp, _RET_IP_); trace_xfs_buf_wait_buftarg(bp, _RET_IP_);
list_move_tail(&bp->b_lru, &btp->bt_lru); spin_unlock(lru_lock);
spin_unlock(&btp->bt_lru_lock);
delay(100); delay(100);
goto restart; } else {
}
/* /*
* clear the LRU reference count so the buffer doesn't get * clear the LRU reference count so the buffer doesn't get
* ignored in xfs_buf_rele(). * ignored in xfs_buf_rele().
*/ */
atomic_set(&bp->b_lru_ref, 0); atomic_set(&bp->b_lru_ref, 0);
spin_unlock(&btp->bt_lru_lock); spin_unlock(lru_lock);
xfs_buf_rele(bp); xfs_buf_rele(bp);
spin_lock(&btp->bt_lru_lock);
} }
spin_unlock(&btp->bt_lru_lock);
spin_lock(lru_lock);
return LRU_RETRY;
} }
int void
xfs_buftarg_shrink( xfs_wait_buftarg(
struct shrinker *shrink, struct xfs_buftarg *btp)
struct shrink_control *sc)
{ {
struct xfs_buftarg *btp = container_of(shrink, while (list_lru_count(&btp->bt_lru))
struct xfs_buftarg, bt_shrinker); list_lru_walk(&btp->bt_lru, xfs_buftarg_wait_rele,
struct xfs_buf *bp; NULL, LONG_MAX);
int nr_to_scan = sc->nr_to_scan; }
LIST_HEAD(dispose);
if (!nr_to_scan)
return btp->bt_lru_nr;
spin_lock(&btp->bt_lru_lock);
while (!list_empty(&btp->bt_lru)) {
if (nr_to_scan-- <= 0)
break;
bp = list_first_entry(&btp->bt_lru, struct xfs_buf, b_lru); static enum lru_status
xfs_buftarg_isolate(
struct list_head *item,
spinlock_t *lru_lock,
void *arg)
{
struct xfs_buf *bp = container_of(item, struct xfs_buf, b_lru);
struct list_head *dispose = arg;
/* /*
* Decrement the b_lru_ref count unless the value is already * Decrement the b_lru_ref count unless the value is already
* zero. If the value is already zero, we need to reclaim the * zero. If the value is already zero, we need to reclaim the
* buffer, otherwise it gets another trip through the LRU. * buffer, otherwise it gets another trip through the LRU.
*/ */
if (!atomic_add_unless(&bp->b_lru_ref, -1, 0)) { if (!atomic_add_unless(&bp->b_lru_ref, -1, 0))
list_move_tail(&bp->b_lru, &btp->bt_lru); return LRU_ROTATE;
continue;
}
/*
* remove the buffer from the LRU now to avoid needing another
* lock round trip inside xfs_buf_rele().
*/
list_move(&bp->b_lru, &dispose);
btp->bt_lru_nr--;
bp->b_lru_flags |= _XBF_LRU_DISPOSE; bp->b_lru_flags |= _XBF_LRU_DISPOSE;
} list_move(item, dispose);
spin_unlock(&btp->bt_lru_lock); return LRU_REMOVED;
}
static long
xfs_buftarg_shrink_scan(
struct shrinker *shrink,
struct shrink_control *sc)
{
struct xfs_buftarg *btp = container_of(shrink,
struct xfs_buftarg, bt_shrinker);
LIST_HEAD(dispose);
long freed;
unsigned long nr_to_scan = sc->nr_to_scan;
freed = list_lru_walk_node(&btp->bt_lru, sc->nid, xfs_buftarg_isolate,
&dispose, &nr_to_scan);
while (!list_empty(&dispose)) { while (!list_empty(&dispose)) {
struct xfs_buf *bp;
bp = list_first_entry(&dispose, struct xfs_buf, b_lru); bp = list_first_entry(&dispose, struct xfs_buf, b_lru);
list_del_init(&bp->b_lru); list_del_init(&bp->b_lru);
xfs_buf_rele(bp); xfs_buf_rele(bp);
} }
return btp->bt_lru_nr; return freed;
}
static long
xfs_buftarg_shrink_count(
struct shrinker *shrink,
struct shrink_control *sc)
{
struct xfs_buftarg *btp = container_of(shrink,
struct xfs_buftarg, bt_shrinker);
return list_lru_count_node(&btp->bt_lru, sc->nid);
} }
void void
...@@ -1660,12 +1649,13 @@ xfs_alloc_buftarg( ...@@ -1660,12 +1649,13 @@ xfs_alloc_buftarg(
if (!btp->bt_bdi) if (!btp->bt_bdi)
goto error; goto error;
INIT_LIST_HEAD(&btp->bt_lru); list_lru_init(&btp->bt_lru);
spin_lock_init(&btp->bt_lru_lock);
if (xfs_setsize_buftarg_early(btp, bdev)) if (xfs_setsize_buftarg_early(btp, bdev))
goto error; goto error;
btp->bt_shrinker.shrink = xfs_buftarg_shrink; btp->bt_shrinker.count_objects = xfs_buftarg_shrink_count;
btp->bt_shrinker.scan_objects = xfs_buftarg_shrink_scan;
btp->bt_shrinker.seeks = DEFAULT_SEEKS; btp->bt_shrinker.seeks = DEFAULT_SEEKS;
btp->bt_shrinker.flags = SHRINKER_NUMA_AWARE;
register_shrinker(&btp->bt_shrinker); register_shrinker(&btp->bt_shrinker);
return btp; return btp;
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/buffer_head.h> #include <linux/buffer_head.h>
#include <linux/uio.h> #include <linux/uio.h>
#include <linux/list_lru.h>
/* /*
* Base types * Base types
...@@ -92,9 +93,7 @@ typedef struct xfs_buftarg { ...@@ -92,9 +93,7 @@ typedef struct xfs_buftarg {
/* LRU control structures */ /* LRU control structures */
struct shrinker bt_shrinker; struct shrinker bt_shrinker;
struct list_head bt_lru; struct list_lru bt_lru;
spinlock_t bt_lru_lock;
unsigned int bt_lru_nr;
} xfs_buftarg_t; } xfs_buftarg_t;
struct xfs_buf; struct xfs_buf;
......
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