Skip to content

Commit 3f97b16

Browse files
Vladimir Davydovtorvalds
authored andcommitted
list_lru: add helpers to isolate items
Currently, the isolate callback passed to the list_lru_walk family of functions is supposed to just delete an item from the list upon returning LRU_REMOVED or LRU_REMOVED_RETRY, while nr_items counter is fixed by __list_lru_walk_one after the callback returns. Since the callback is allowed to drop the lock after removing an item (it has to return LRU_REMOVED_RETRY then), the nr_items can be less than the actual number of elements on the list even if we check them under the lock. This makes it difficult to move items from one list_lru_one to another, which is required for per-memcg list_lru reparenting - we can't just splice the lists, we have to move entries one by one. This patch therefore introduces helpers that must be used by callback functions to isolate items instead of raw list_del/list_move. These are list_lru_isolate and list_lru_isolate_move. They not only remove the entry from the list, but also fix the nr_items counter, making sure nr_items always reflects the actual number of elements on the list if checked under the appropriate lock. Signed-off-by: Vladimir Davydov <[email protected]> Cc: Johannes Weiner <[email protected]> Cc: Michal Hocko <[email protected]> Cc: Tejun Heo <[email protected]> Cc: Christoph Lameter <[email protected]> Cc: Pekka Enberg <[email protected]> Cc: David Rientjes <[email protected]> Cc: Joonsoo Kim <[email protected]> Cc: Dave Chinner <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 2a4db7e commit 3f97b16

File tree

8 files changed

+50
-26
lines changed

8 files changed

+50
-26
lines changed

fs/dcache.c

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -400,19 +400,20 @@ static void d_shrink_add(struct dentry *dentry, struct list_head *list)
400400
* LRU lists entirely, while shrink_move moves it to the indicated
401401
* private list.
402402
*/
403-
static void d_lru_isolate(struct dentry *dentry)
403+
static void d_lru_isolate(struct list_lru_one *lru, struct dentry *dentry)
404404
{
405405
D_FLAG_VERIFY(dentry, DCACHE_LRU_LIST);
406406
dentry->d_flags &= ~DCACHE_LRU_LIST;
407407
this_cpu_dec(nr_dentry_unused);
408-
list_del_init(&dentry->d_lru);
408+
list_lru_isolate(lru, &dentry->d_lru);
409409
}
410410

411-
static void d_lru_shrink_move(struct dentry *dentry, struct list_head *list)
411+
static void d_lru_shrink_move(struct list_lru_one *lru, struct dentry *dentry,
412+
struct list_head *list)
412413
{
413414
D_FLAG_VERIFY(dentry, DCACHE_LRU_LIST);
414415
dentry->d_flags |= DCACHE_SHRINK_LIST;
415-
list_move_tail(&dentry->d_lru, list);
416+
list_lru_isolate_move(lru, &dentry->d_lru, list);
416417
}
417418

418419
/*
@@ -869,8 +870,8 @@ static void shrink_dentry_list(struct list_head *list)
869870
}
870871
}
871872

872-
static enum lru_status
873-
dentry_lru_isolate(struct list_head *item, spinlock_t *lru_lock, void *arg)
873+
static enum lru_status dentry_lru_isolate(struct list_head *item,
874+
struct list_lru_one *lru, spinlock_t *lru_lock, void *arg)
874875
{
875876
struct list_head *freeable = arg;
876877
struct dentry *dentry = container_of(item, struct dentry, d_lru);
@@ -890,7 +891,7 @@ dentry_lru_isolate(struct list_head *item, spinlock_t *lru_lock, void *arg)
890891
* another pass through the LRU.
891892
*/
892893
if (dentry->d_lockref.count) {
893-
d_lru_isolate(dentry);
894+
d_lru_isolate(lru, dentry);
894895
spin_unlock(&dentry->d_lock);
895896
return LRU_REMOVED;
896897
}
@@ -921,7 +922,7 @@ dentry_lru_isolate(struct list_head *item, spinlock_t *lru_lock, void *arg)
921922
return LRU_ROTATE;
922923
}
923924

924-
d_lru_shrink_move(dentry, freeable);
925+
d_lru_shrink_move(lru, dentry, freeable);
925926
spin_unlock(&dentry->d_lock);
926927

927928
return LRU_REMOVED;
@@ -951,7 +952,7 @@ long prune_dcache_sb(struct super_block *sb, struct shrink_control *sc)
951952
}
952953

953954
static enum lru_status dentry_lru_isolate_shrink(struct list_head *item,
954-
spinlock_t *lru_lock, void *arg)
955+
struct list_lru_one *lru, spinlock_t *lru_lock, void *arg)
955956
{
956957
struct list_head *freeable = arg;
957958
struct dentry *dentry = container_of(item, struct dentry, d_lru);
@@ -964,7 +965,7 @@ static enum lru_status dentry_lru_isolate_shrink(struct list_head *item,
964965
if (!spin_trylock(&dentry->d_lock))
965966
return LRU_SKIP;
966967

967-
d_lru_shrink_move(dentry, freeable);
968+
d_lru_shrink_move(lru, dentry, freeable);
968969
spin_unlock(&dentry->d_lock);
969970

970971
return LRU_REMOVED;

fs/gfs2/quota.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,8 @@ static void gfs2_qd_dispose(struct list_head *list)
145145
}
146146

147147

148-
static enum lru_status gfs2_qd_isolate(struct list_head *item, spinlock_t *lock, void *arg)
148+
static enum lru_status gfs2_qd_isolate(struct list_head *item,
149+
struct list_lru_one *lru, spinlock_t *lru_lock, void *arg)
149150
{
150151
struct list_head *dispose = arg;
151152
struct gfs2_quota_data *qd = list_entry(item, struct gfs2_quota_data, qd_lru);
@@ -155,7 +156,7 @@ static enum lru_status gfs2_qd_isolate(struct list_head *item, spinlock_t *lock,
155156

156157
if (qd->qd_lockref.count == 0) {
157158
lockref_mark_dead(&qd->qd_lockref);
158-
list_move(&qd->qd_lru, dispose);
159+
list_lru_isolate_move(lru, &qd->qd_lru, dispose);
159160
}
160161

161162
spin_unlock(&qd->qd_lockref.lock);

fs/inode.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -685,8 +685,8 @@ int invalidate_inodes(struct super_block *sb, bool kill_dirty)
685685
* LRU does not have strict ordering. Hence we don't want to reclaim inodes
686686
* with this flag set because they are the inodes that are out of order.
687687
*/
688-
static enum lru_status
689-
inode_lru_isolate(struct list_head *item, spinlock_t *lru_lock, void *arg)
688+
static enum lru_status inode_lru_isolate(struct list_head *item,
689+
struct list_lru_one *lru, spinlock_t *lru_lock, void *arg)
690690
{
691691
struct list_head *freeable = arg;
692692
struct inode *inode = container_of(item, struct inode, i_lru);
@@ -704,7 +704,7 @@ inode_lru_isolate(struct list_head *item, spinlock_t *lru_lock, void *arg)
704704
*/
705705
if (atomic_read(&inode->i_count) ||
706706
(inode->i_state & ~I_REFERENCED)) {
707-
list_del_init(&inode->i_lru);
707+
list_lru_isolate(lru, &inode->i_lru);
708708
spin_unlock(&inode->i_lock);
709709
this_cpu_dec(nr_unused);
710710
return LRU_REMOVED;
@@ -738,7 +738,7 @@ inode_lru_isolate(struct list_head *item, spinlock_t *lru_lock, void *arg)
738738

739739
WARN_ON(inode->i_state & I_NEW);
740740
inode->i_state |= I_FREEING;
741-
list_move(&inode->i_lru, freeable);
741+
list_lru_isolate_move(lru, &inode->i_lru, freeable);
742742
spin_unlock(&inode->i_lock);
743743

744744
this_cpu_dec(nr_unused);

fs/xfs/xfs_buf.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1488,6 +1488,7 @@ xfs_buf_iomove(
14881488
static enum lru_status
14891489
xfs_buftarg_wait_rele(
14901490
struct list_head *item,
1491+
struct list_lru_one *lru,
14911492
spinlock_t *lru_lock,
14921493
void *arg)
14931494

@@ -1509,7 +1510,7 @@ xfs_buftarg_wait_rele(
15091510
*/
15101511
atomic_set(&bp->b_lru_ref, 0);
15111512
bp->b_state |= XFS_BSTATE_DISPOSE;
1512-
list_move(item, dispose);
1513+
list_lru_isolate_move(lru, item, dispose);
15131514
spin_unlock(&bp->b_lock);
15141515
return LRU_REMOVED;
15151516
}
@@ -1546,6 +1547,7 @@ xfs_wait_buftarg(
15461547
static enum lru_status
15471548
xfs_buftarg_isolate(
15481549
struct list_head *item,
1550+
struct list_lru_one *lru,
15491551
spinlock_t *lru_lock,
15501552
void *arg)
15511553
{
@@ -1569,7 +1571,7 @@ xfs_buftarg_isolate(
15691571
}
15701572

15711573
bp->b_state |= XFS_BSTATE_DISPOSE;
1572-
list_move(item, dispose);
1574+
list_lru_isolate_move(lru, item, dispose);
15731575
spin_unlock(&bp->b_lock);
15741576
return LRU_REMOVED;
15751577
}

fs/xfs/xfs_qm.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,7 @@ struct xfs_qm_isolate {
430430
static enum lru_status
431431
xfs_qm_dquot_isolate(
432432
struct list_head *item,
433+
struct list_lru_one *lru,
433434
spinlock_t *lru_lock,
434435
void *arg)
435436
__releases(lru_lock) __acquires(lru_lock)
@@ -450,7 +451,7 @@ xfs_qm_dquot_isolate(
450451
XFS_STATS_INC(xs_qm_dqwants);
451452

452453
trace_xfs_dqreclaim_want(dqp);
453-
list_del_init(&dqp->q_lru);
454+
list_lru_isolate(lru, &dqp->q_lru);
454455
XFS_STATS_DEC(xs_qm_dquot_unused);
455456
return LRU_REMOVED;
456457
}
@@ -494,7 +495,7 @@ xfs_qm_dquot_isolate(
494495
xfs_dqunlock(dqp);
495496

496497
ASSERT(dqp->q_nrefs == 0);
497-
list_move_tail(&dqp->q_lru, &isol->dispose);
498+
list_lru_isolate_move(lru, &dqp->q_lru, &isol->dispose);
498499
XFS_STATS_DEC(xs_qm_dquot_unused);
499500
trace_xfs_dqreclaim_done(dqp);
500501
XFS_STATS_INC(xs_qm_dqreclaims);

include/linux/list_lru.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,13 @@ static inline unsigned long list_lru_count(struct list_lru *lru)
125125
return count;
126126
}
127127

128-
typedef enum lru_status
129-
(*list_lru_walk_cb)(struct list_head *item, spinlock_t *lock, void *cb_arg);
128+
void list_lru_isolate(struct list_lru_one *list, struct list_head *item);
129+
void list_lru_isolate_move(struct list_lru_one *list, struct list_head *item,
130+
struct list_head *head);
131+
132+
typedef enum lru_status (*list_lru_walk_cb)(struct list_head *item,
133+
struct list_lru_one *list, spinlock_t *lock, void *cb_arg);
134+
130135
/**
131136
* list_lru_walk_one: walk a list_lru, isolating and disposing freeable items.
132137
* @lru: the lru pointer.

mm/list_lru.c

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,21 @@ bool list_lru_del(struct list_lru *lru, struct list_head *item)
132132
}
133133
EXPORT_SYMBOL_GPL(list_lru_del);
134134

135+
void list_lru_isolate(struct list_lru_one *list, struct list_head *item)
136+
{
137+
list_del_init(item);
138+
list->nr_items--;
139+
}
140+
EXPORT_SYMBOL_GPL(list_lru_isolate);
141+
142+
void list_lru_isolate_move(struct list_lru_one *list, struct list_head *item,
143+
struct list_head *head)
144+
{
145+
list_move(item, head);
146+
list->nr_items--;
147+
}
148+
EXPORT_SYMBOL_GPL(list_lru_isolate_move);
149+
135150
static unsigned long __list_lru_count_one(struct list_lru *lru,
136151
int nid, int memcg_idx)
137152
{
@@ -194,13 +209,11 @@ __list_lru_walk_one(struct list_lru *lru, int nid, int memcg_idx,
194209
break;
195210
--*nr_to_walk;
196211

197-
ret = isolate(item, &nlru->lock, cb_arg);
212+
ret = isolate(item, l, &nlru->lock, cb_arg);
198213
switch (ret) {
199214
case LRU_REMOVED_RETRY:
200215
assert_spin_locked(&nlru->lock);
201216
case LRU_REMOVED:
202-
l->nr_items--;
203-
WARN_ON_ONCE(l->nr_items < 0);
204217
isolated++;
205218
/*
206219
* If the lru lock has been dropped, our list

mm/workingset.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ static unsigned long count_shadow_nodes(struct shrinker *shrinker,
302302
}
303303

304304
static enum lru_status shadow_lru_isolate(struct list_head *item,
305+
struct list_lru_one *lru,
305306
spinlock_t *lru_lock,
306307
void *arg)
307308
{
@@ -332,7 +333,7 @@ static enum lru_status shadow_lru_isolate(struct list_head *item,
332333
goto out;
333334
}
334335

335-
list_del_init(item);
336+
list_lru_isolate(lru, item);
336337
spin_unlock(lru_lock);
337338

338339
/*

0 commit comments

Comments
 (0)