@ -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 .