Skip to content

Commit e8487c5

Browse files
Muchun Songjfvogel
authored andcommitted
mm: memcontrol: make lruvec lock safe when LRU pages are reparented
The diagram below shows how to make the folio lruvec lock safe when LRU pages are reparented. folio_lruvec_lock(folio) rcu_read_lock(); retry: lruvec = folio_lruvec(folio); // The folio is reparented at this time. spin_lock(&lruvec->lru_lock); if (unlikely(lruvec_memcg(lruvec) != folio_memcg(folio))) // Acquired the wrong lruvec lock and need to retry. // Because this folio is on the parent memcg lruvec list. spin_unlock(&lruvec->lru_lock); goto retry; // If we reach here, it means that folio_memcg(folio) is stable. memcg_reparent_objcgs(memcg) // lruvec belongs to memcg and lruvec_parent belongs to parent memcg. spin_lock(&lruvec->lru_lock); spin_lock(&lruvec_parent->lru_lock); // Move all the pages from the lruvec list to the parent lruvec list. spin_unlock(&lruvec_parent->lru_lock); spin_unlock(&lruvec->lru_lock); After we acquire the lruvec lock, we need to check whether the folio is reparented. If so, we need to reacquire the new lruvec lock. On the routine of the LRU pages reparenting, we will also acquire the lruvec lock (will be implemented in the later patch). So folio_memcg() cannot be changed when we hold the lruvec lock. Since lruvec_memcg(lruvec) is always equal to folio_memcg(folio) after we hold the lruvec lock, lruvec_memcg_debug() check is pointless. So remove it. This is a preparation for reparenting the LRU pages. Signed-off-by: Muchun Song <[email protected]> Link: https://lore.kernel.org/all/[email protected]/ Orabug: 37405594 Conflicts: mm/compaction.c mm/memcontrol.c (Due to the presence of following commits in UEK-8: i. 'commit 56ae0bb mm: compaction: convert to use a folio in isolate_migratepages_block()' ii. 'commit 590ccea mm: compaction: update pageblock skip when first migration candidate is not at the start' Signed-off-by: Imran Khan <[email protected]> Reviewed-by: Kamalesh Babulal <[email protected]>
1 parent c70baaa commit e8487c5

File tree

4 files changed

+58
-40
lines changed

4 files changed

+58
-40
lines changed

include/linux/memcontrol.h

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -781,7 +781,9 @@ static inline struct lruvec *mem_cgroup_lruvec(struct mem_cgroup *memcg,
781781
* folio_lruvec - return lruvec for isolating/putting an LRU folio
782782
* @folio: Pointer to the folio.
783783
*
784-
* This function relies on folio->mem_cgroup being stable.
784+
* The lruvec can be changed to its parent lruvec when the page reparented.
785+
* The caller need to recheck if it cares about this changes (just like
786+
* folio_lruvec_lock() does).
785787
*/
786788
static inline struct lruvec *folio_lruvec(struct folio *folio)
787789
{
@@ -804,15 +806,6 @@ struct lruvec *folio_lruvec_lock_irq(struct folio *folio);
804806
struct lruvec *folio_lruvec_lock_irqsave(struct folio *folio,
805807
unsigned long *flags);
806808

807-
#ifdef CONFIG_DEBUG_VM
808-
void lruvec_memcg_debug(struct lruvec *lruvec, struct folio *folio);
809-
#else
810-
static inline
811-
void lruvec_memcg_debug(struct lruvec *lruvec, struct folio *folio)
812-
{
813-
}
814-
#endif
815-
816809
static inline
817810
struct mem_cgroup *mem_cgroup_from_css(struct cgroup_subsys_state *css){
818811
return css ? container_of(css, struct mem_cgroup, css) : NULL;
@@ -1257,11 +1250,6 @@ static inline struct lruvec *folio_lruvec(struct folio *folio)
12571250
return &pgdat->__lruvec;
12581251
}
12591252

1260-
static inline
1261-
void lruvec_memcg_debug(struct lruvec *lruvec, struct folio *folio)
1262-
{
1263-
}
1264-
12651253
static inline struct mem_cgroup *parent_mem_cgroup(struct mem_cgroup *memcg)
12661254
{
12671255
return NULL;

mm/compaction.c

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,25 @@ static bool compact_lock_irqsave(spinlock_t *lock, unsigned long *flags,
550550
return true;
551551
}
552552

553+
static struct lruvec *
554+
compact_folio_lruvec_lock_irqsave(struct folio *folio, unsigned long *flags,
555+
struct compact_control *cc)
556+
{
557+
struct lruvec *lruvec;
558+
559+
rcu_read_lock();
560+
retry:
561+
lruvec = folio_lruvec(folio);
562+
compact_lock_irqsave(&lruvec->lru_lock, flags, cc);
563+
if (unlikely(lruvec_memcg(lruvec) != folio_memcg(folio))) {
564+
spin_unlock_irqrestore(&lruvec->lru_lock, *flags);
565+
goto retry;
566+
}
567+
rcu_read_unlock();
568+
569+
return lruvec;
570+
}
571+
553572
/*
554573
* Compaction requires the taking of some coarse locks that are potentially
555574
* very heavily contended. The lock should be periodically unlocked to avoid
@@ -1231,10 +1250,9 @@ isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn,
12311250
if (locked)
12321251
lruvec_unlock_irqrestore(locked, flags);
12331252

1234-
compact_lock_irqsave(&lruvec->lru_lock, &flags, cc);
1253+
lruvec = compact_folio_lruvec_lock_irqsave(folio, &flags, cc);
12351254
locked = lruvec;
12361255

1237-
lruvec_memcg_debug(lruvec, folio);
12381256

12391257
/*
12401258
* Try get exclusive access under lock. If marked for

mm/memcontrol.c

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1175,23 +1175,6 @@ void mem_cgroup_scan_tasks(struct mem_cgroup *memcg,
11751175
}
11761176
}
11771177

1178-
#ifdef CONFIG_DEBUG_VM
1179-
void lruvec_memcg_debug(struct lruvec *lruvec, struct folio *folio)
1180-
{
1181-
struct mem_cgroup *memcg;
1182-
1183-
if (mem_cgroup_disabled())
1184-
return;
1185-
1186-
memcg = folio_memcg(folio);
1187-
1188-
if (!memcg)
1189-
VM_BUG_ON_FOLIO(!mem_cgroup_is_root(lruvec_memcg(lruvec)), folio);
1190-
else
1191-
VM_BUG_ON_FOLIO(lruvec_memcg(lruvec) != memcg, folio);
1192-
}
1193-
#endif
1194-
11951178
/**
11961179
* folio_lruvec_lock - Lock the lruvec for a folio.
11971180
* @folio: Pointer to the folio.
@@ -1206,10 +1189,18 @@ void lruvec_memcg_debug(struct lruvec *lruvec, struct folio *folio)
12061189
*/
12071190
struct lruvec *folio_lruvec_lock(struct folio *folio)
12081191
{
1209-
struct lruvec *lruvec = folio_lruvec(folio);
1192+
struct lruvec *lruvec;
12101193

1194+
rcu_read_lock();
1195+
retry:
1196+
lruvec = folio_lruvec(folio);
12111197
spin_lock(&lruvec->lru_lock);
1212-
lruvec_memcg_debug(lruvec, folio);
1198+
1199+
if (unlikely(lruvec_memcg(lruvec) != folio_memcg(folio))) {
1200+
spin_unlock(&lruvec->lru_lock);
1201+
goto retry;
1202+
}
1203+
rcu_read_unlock();
12131204

12141205
return lruvec;
12151206
}
@@ -1229,10 +1220,18 @@ struct lruvec *folio_lruvec_lock(struct folio *folio)
12291220
*/
12301221
struct lruvec *folio_lruvec_lock_irq(struct folio *folio)
12311222
{
1232-
struct lruvec *lruvec = folio_lruvec(folio);
1223+
struct lruvec *lruvec;
12331224

1225+
rcu_read_lock();
1226+
retry:
1227+
lruvec = folio_lruvec(folio);
12341228
spin_lock_irq(&lruvec->lru_lock);
1235-
lruvec_memcg_debug(lruvec, folio);
1229+
1230+
if (unlikely(lruvec_memcg(lruvec) != folio_memcg(folio))) {
1231+
spin_unlock_irq(&lruvec->lru_lock);
1232+
goto retry;
1233+
}
1234+
rcu_read_unlock();
12361235

12371236
return lruvec;
12381237
}
@@ -1254,10 +1253,18 @@ struct lruvec *folio_lruvec_lock_irq(struct folio *folio)
12541253
struct lruvec *folio_lruvec_lock_irqsave(struct folio *folio,
12551254
unsigned long *flags)
12561255
{
1257-
struct lruvec *lruvec = folio_lruvec(folio);
1256+
struct lruvec *lruvec;
12581257

1258+
rcu_read_lock();
1259+
retry:
1260+
lruvec = folio_lruvec(folio);
12591261
spin_lock_irqsave(&lruvec->lru_lock, *flags);
1260-
lruvec_memcg_debug(lruvec, folio);
1262+
1263+
if (unlikely(lruvec_memcg(lruvec) != folio_memcg(folio))) {
1264+
spin_unlock_irqrestore(&lruvec->lru_lock, *flags);
1265+
goto retry;
1266+
}
1267+
rcu_read_unlock();
12611268

12621269
return lruvec;
12631270
}

mm/swap.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,11 @@ void lru_note_cost(struct lruvec *lruvec, bool file,
322322

323323
void lru_note_cost_refault(struct folio *folio)
324324
{
325+
WARN_ON_ONCE(!rcu_read_lock_held());
326+
/*
327+
* The rcu read lock is held by the caller, so we do not need to
328+
* care about the lruvec returned by folio_lruvec() being released.
329+
*/
325330
lru_note_cost(folio_lruvec(folio), folio_is_file_lru(folio),
326331
folio_nr_pages(folio), 0);
327332
}

0 commit comments

Comments
 (0)