xfs: convert dquot cache lru to list_lru

Convert the XFS dquot lru to use the list_lru construct and convert the
shrinker to being node aware.

[glommer@openvz.org: edited for conflicts + warning fixes]
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:
Dave Chinner 2013-08-28 10:18:07 +10:00 committed by Al Viro
parent a408235726
commit cd56a39a59
3 changed files with 144 additions and 144 deletions

View File

@ -940,13 +940,8 @@ xfs_qm_dqput_final(
trace_xfs_dqput_free(dqp);
mutex_lock(&qi->qi_lru_lock);
if (list_empty(&dqp->q_lru)) {
list_add_tail(&dqp->q_lru, &qi->qi_lru_list);
qi->qi_lru_count++;
if (list_lru_add(&qi->qi_lru, &dqp->q_lru))
XFS_STATS_INC(xs_qm_dquot_unused);
}
mutex_unlock(&qi->qi_lru_lock);
/*
* If we just added a udquot to the freelist, then we want to release

View File

@ -51,8 +51,9 @@
*/
STATIC int xfs_qm_init_quotainos(xfs_mount_t *);
STATIC int xfs_qm_init_quotainfo(xfs_mount_t *);
STATIC int xfs_qm_shake(struct shrinker *, struct shrink_control *);
STATIC void xfs_qm_dqfree_one(struct xfs_dquot *dqp);
/*
* We use the batch lookup interface to iterate over the dquots as it
* currently is the only interface into the radix tree code that allows
@ -203,12 +204,9 @@ xfs_qm_dqpurge(
* We move dquots to the freelist as soon as their reference count
* hits zero, so it really should be on the freelist here.
*/
mutex_lock(&qi->qi_lru_lock);
ASSERT(!list_empty(&dqp->q_lru));
list_del_init(&dqp->q_lru);
qi->qi_lru_count--;
list_lru_del(&qi->qi_lru, &dqp->q_lru);
XFS_STATS_DEC(xs_qm_dquot_unused);
mutex_unlock(&qi->qi_lru_lock);
xfs_qm_dqdestroy(dqp);
@ -680,6 +678,141 @@ xfs_qm_calc_dquots_per_chunk(
return ndquots;
}
struct xfs_qm_isolate {
struct list_head buffers;
struct list_head dispose;
};
static enum lru_status
xfs_qm_dquot_isolate(
struct list_head *item,
spinlock_t *lru_lock,
void *arg)
{
struct xfs_dquot *dqp = container_of(item,
struct xfs_dquot, q_lru);
struct xfs_qm_isolate *isol = arg;
if (!xfs_dqlock_nowait(dqp))
goto out_miss_busy;
/*
* This dquot has acquired a reference in the meantime remove it from
* the freelist and try again.
*/
if (dqp->q_nrefs) {
xfs_dqunlock(dqp);
XFS_STATS_INC(xs_qm_dqwants);
trace_xfs_dqreclaim_want(dqp);
list_del_init(&dqp->q_lru);
XFS_STATS_DEC(xs_qm_dquot_unused);
return 0;
}
/*
* If the dquot is dirty, flush it. If it's already being flushed, just
* skip it so there is time for the IO to complete before we try to
* reclaim it again on the next LRU pass.
*/
if (!xfs_dqflock_nowait(dqp)) {
xfs_dqunlock(dqp);
goto out_miss_busy;
}
if (XFS_DQ_IS_DIRTY(dqp)) {
struct xfs_buf *bp = NULL;
int error;
trace_xfs_dqreclaim_dirty(dqp);
/* we have to drop the LRU lock to flush the dquot */
spin_unlock(lru_lock);
error = xfs_qm_dqflush(dqp, &bp);
if (error) {
xfs_warn(dqp->q_mount, "%s: dquot %p flush failed",
__func__, dqp);
goto out_unlock_dirty;
}
xfs_buf_delwri_queue(bp, &isol->buffers);
xfs_buf_relse(bp);
goto out_unlock_dirty;
}
xfs_dqfunlock(dqp);
/*
* Prevent lookups now that we are past the point of no return.
*/
dqp->dq_flags |= XFS_DQ_FREEING;
xfs_dqunlock(dqp);
ASSERT(dqp->q_nrefs == 0);
list_move_tail(&dqp->q_lru, &isol->dispose);
XFS_STATS_DEC(xs_qm_dquot_unused);
trace_xfs_dqreclaim_done(dqp);
XFS_STATS_INC(xs_qm_dqreclaims);
return 0;
out_miss_busy:
trace_xfs_dqreclaim_busy(dqp);
XFS_STATS_INC(xs_qm_dqreclaim_misses);
return 2;
out_unlock_dirty:
trace_xfs_dqreclaim_busy(dqp);
XFS_STATS_INC(xs_qm_dqreclaim_misses);
return 3;
}
static long
xfs_qm_shrink_scan(
struct shrinker *shrink,
struct shrink_control *sc)
{
struct xfs_quotainfo *qi = container_of(shrink,
struct xfs_quotainfo, qi_shrinker);
struct xfs_qm_isolate isol;
long freed;
int error;
unsigned long nr_to_scan = sc->nr_to_scan;
if ((sc->gfp_mask & (__GFP_FS|__GFP_WAIT)) != (__GFP_FS|__GFP_WAIT))
return 0;
INIT_LIST_HEAD(&isol.buffers);
INIT_LIST_HEAD(&isol.dispose);
freed = list_lru_walk_node(&qi->qi_lru, sc->nid, xfs_qm_dquot_isolate, &isol,
&nr_to_scan);
error = xfs_buf_delwri_submit(&isol.buffers);
if (error)
xfs_warn(NULL, "%s: dquot reclaim failed", __func__);
while (!list_empty(&isol.dispose)) {
struct xfs_dquot *dqp;
dqp = list_first_entry(&isol.dispose, struct xfs_dquot, q_lru);
list_del_init(&dqp->q_lru);
xfs_qm_dqfree_one(dqp);
}
return freed;
}
static long
xfs_qm_shrink_count(
struct shrinker *shrink,
struct shrink_control *sc)
{
struct xfs_quotainfo *qi = container_of(shrink,
struct xfs_quotainfo, qi_shrinker);
return list_lru_count_node(&qi->qi_lru, sc->nid);
}
/*
* This initializes all the quota information that's kept in the
* mount structure
@ -711,9 +844,7 @@ xfs_qm_init_quotainfo(
INIT_RADIX_TREE(&qinf->qi_pquota_tree, GFP_NOFS);
mutex_init(&qinf->qi_tree_lock);
INIT_LIST_HEAD(&qinf->qi_lru_list);
qinf->qi_lru_count = 0;
mutex_init(&qinf->qi_lru_lock);
list_lru_init(&qinf->qi_lru);
/* mutex used to serialize quotaoffs */
mutex_init(&qinf->qi_quotaofflock);
@ -779,8 +910,10 @@ xfs_qm_init_quotainfo(
qinf->qi_rtbwarnlimit = XFS_QM_RTBWARNLIMIT;
}
qinf->qi_shrinker.shrink = xfs_qm_shake;
qinf->qi_shrinker.count_objects = xfs_qm_shrink_count;
qinf->qi_shrinker.scan_objects = xfs_qm_shrink_scan;
qinf->qi_shrinker.seeks = DEFAULT_SEEKS;
qinf->qi_shrinker.flags = SHRINKER_NUMA_AWARE;
register_shrinker(&qinf->qi_shrinker);
return 0;
}
@ -1599,132 +1732,6 @@ xfs_qm_dqfree_one(
xfs_qm_dqdestroy(dqp);
}
STATIC void
xfs_qm_dqreclaim_one(
struct xfs_dquot *dqp,
struct list_head *buffer_list,
struct list_head *dispose_list)
{
struct xfs_mount *mp = dqp->q_mount;
struct xfs_quotainfo *qi = mp->m_quotainfo;
int error;
if (!xfs_dqlock_nowait(dqp))
goto out_move_tail;
/*
* This dquot has acquired a reference in the meantime remove it from
* the freelist and try again.
*/
if (dqp->q_nrefs) {
xfs_dqunlock(dqp);
trace_xfs_dqreclaim_want(dqp);
XFS_STATS_INC(xs_qm_dqwants);
list_del_init(&dqp->q_lru);
qi->qi_lru_count--;
XFS_STATS_DEC(xs_qm_dquot_unused);
return;
}
/*
* Try to grab the flush lock. If this dquot is in the process of
* getting flushed to disk, we don't want to reclaim it.
*/
if (!xfs_dqflock_nowait(dqp))
goto out_unlock_move_tail;
if (XFS_DQ_IS_DIRTY(dqp)) {
struct xfs_buf *bp = NULL;
trace_xfs_dqreclaim_dirty(dqp);
error = xfs_qm_dqflush(dqp, &bp);
if (error) {
xfs_warn(mp, "%s: dquot %p flush failed",
__func__, dqp);
goto out_unlock_move_tail;
}
xfs_buf_delwri_queue(bp, buffer_list);
xfs_buf_relse(bp);
/*
* Give the dquot another try on the freelist, as the
* flushing will take some time.
*/
goto out_unlock_move_tail;
}
xfs_dqfunlock(dqp);
/*
* Prevent lookups now that we are past the point of no return.
*/
dqp->dq_flags |= XFS_DQ_FREEING;
xfs_dqunlock(dqp);
ASSERT(dqp->q_nrefs == 0);
list_move_tail(&dqp->q_lru, dispose_list);
qi->qi_lru_count--;
XFS_STATS_DEC(xs_qm_dquot_unused);
trace_xfs_dqreclaim_done(dqp);
XFS_STATS_INC(xs_qm_dqreclaims);
return;
/*
* Move the dquot to the tail of the list so that we don't spin on it.
*/
out_unlock_move_tail:
xfs_dqunlock(dqp);
out_move_tail:
list_move_tail(&dqp->q_lru, &qi->qi_lru_list);
trace_xfs_dqreclaim_busy(dqp);
XFS_STATS_INC(xs_qm_dqreclaim_misses);
}
STATIC int
xfs_qm_shake(
struct shrinker *shrink,
struct shrink_control *sc)
{
struct xfs_quotainfo *qi =
container_of(shrink, struct xfs_quotainfo, qi_shrinker);
int nr_to_scan = sc->nr_to_scan;
LIST_HEAD (buffer_list);
LIST_HEAD (dispose_list);
struct xfs_dquot *dqp;
int error;
if ((sc->gfp_mask & (__GFP_FS|__GFP_WAIT)) != (__GFP_FS|__GFP_WAIT))
return 0;
if (!nr_to_scan)
goto out;
mutex_lock(&qi->qi_lru_lock);
while (!list_empty(&qi->qi_lru_list)) {
if (nr_to_scan-- <= 0)
break;
dqp = list_first_entry(&qi->qi_lru_list, struct xfs_dquot,
q_lru);
xfs_qm_dqreclaim_one(dqp, &buffer_list, &dispose_list);
}
mutex_unlock(&qi->qi_lru_lock);
error = xfs_buf_delwri_submit(&buffer_list);
if (error)
xfs_warn(NULL, "%s: dquot reclaim failed", __func__);
while (!list_empty(&dispose_list)) {
dqp = list_first_entry(&dispose_list, struct xfs_dquot, q_lru);
list_del_init(&dqp->q_lru);
xfs_qm_dqfree_one(dqp);
}
out:
return vfs_pressure_ratio(qi->qi_lru_count);
}
/*
* Start a transaction and write the incore superblock changes to
* disk. flags parameter indicates which fields have changed.

View File

@ -49,9 +49,7 @@ typedef struct xfs_quotainfo {
struct xfs_inode *qi_uquotaip; /* user quota inode */
struct xfs_inode *qi_gquotaip; /* group quota inode */
struct xfs_inode *qi_pquotaip; /* project quota inode */
struct list_head qi_lru_list;
struct mutex qi_lru_lock;
int qi_lru_count;
struct list_lru qi_lru;
int qi_dquots;
time_t qi_btimelimit; /* limit for blks timer */
time_t qi_itimelimit; /* limit for inodes timer */