Merge branch 'akpm' (patches from Andrew)
Merge more updates from Andrew Morton: "More MM work: a memcg scalability improvememt" * emailed patches from Andrew Morton <akpm@linux-foundation.org>: mm/lru: revise the comments of lru_lock mm/lru: introduce relock_page_lruvec() mm/lru: replace pgdat lru_lock with lruvec lock mm/swap.c: serialize memcg changes in pagevec_lru_move_fn mm/compaction: do page isolation first in compaction mm/lru: introduce TestClearPageLRU() mm/mlock: remove __munlock_isolate_lru_page() mm/mlock: remove lru_lock on TestClearPageMlocked mm/vmscan: remove lruvec reget in move_pages_to_lru mm/lru: move lock into lru_note_cost mm/swap.c: fold vm event PGROTATED into pagevec_move_tail_fn mm/memcg: add debug checking in lock_page_memcg mm: page_idle_get_page() does not need lru_lock mm/rmap: stop store reordering issue on page->mapping mm/vmscan: remove unnecessary lruvec adding mm/thp: narrow lru locking mm/thp: simplify lru_add_page_tail() mm/thp: use head for head page in lru_add_page_tail() mm/thp: move lru_add_page_tail() to huge_memory.c
This commit is contained in:
		
						commit
						5b200f5789
					
				| @ -133,18 +133,9 @@ Under below explanation, we assume CONFIG_MEM_RES_CTRL_SWAP=y. | ||||
| 
 | ||||
| 8. LRU | ||||
| ====== | ||||
|         Each memcg has its own private LRU. Now, its handling is under global | ||||
| 	VM's control (means that it's handled under global pgdat->lru_lock). | ||||
| 	Almost all routines around memcg's LRU is called by global LRU's | ||||
| 	list management functions under pgdat->lru_lock. | ||||
| 
 | ||||
| 	A special function is mem_cgroup_isolate_pages(). This scans | ||||
| 	memcg's private LRU and call __isolate_lru_page() to extract a page | ||||
| 	from LRU. | ||||
| 
 | ||||
| 	(By __isolate_lru_page(), the page is removed from both of global and | ||||
| 	private LRU.) | ||||
| 
 | ||||
| 	Each memcg has its own vector of LRUs (inactive anon, active anon, | ||||
| 	inactive file, active file, unevictable) of pages from each node, | ||||
| 	each LRU handled under a single lru_lock for that memcg and node. | ||||
| 
 | ||||
| 9. Typical Tests. | ||||
| ================= | ||||
|  | ||||
| @ -287,20 +287,17 @@ When oom event notifier is registered, event will be delivered. | ||||
| 2.6 Locking | ||||
| ----------- | ||||
| 
 | ||||
|    lock_page_cgroup()/unlock_page_cgroup() should not be called under | ||||
|    the i_pages lock. | ||||
| Lock order is as follows: | ||||
| 
 | ||||
|    Other lock order is following: | ||||
|   Page lock (PG_locked bit of page->flags) | ||||
|     mm->page_table_lock or split pte_lock | ||||
|       lock_page_memcg (memcg->move_lock) | ||||
|         mapping->i_pages lock | ||||
|           lruvec->lru_lock. | ||||
| 
 | ||||
|    PG_locked. | ||||
|      mm->page_table_lock | ||||
|          pgdat->lru_lock | ||||
| 	   lock_page_cgroup. | ||||
| 
 | ||||
|   In many cases, just lock_page_cgroup() is called. | ||||
| 
 | ||||
|   per-zone-per-cgroup LRU (cgroup's private LRU) is just guarded by | ||||
|   pgdat->lru_lock, it has no lock of its own. | ||||
| Per-node-per-memcgroup LRU (cgroup's private LRU) is guarded by | ||||
| lruvec->lru_lock; PG_lru bit of page->flags is cleared before | ||||
| isolating a page from its LRU under lruvec->lru_lock. | ||||
| 
 | ||||
| 2.7 Kernel Memory Extension (CONFIG_MEMCG_KMEM) | ||||
| ----------------------------------------------- | ||||
|  | ||||
| @ -69,7 +69,7 @@ When pages are freed in batch, the also mm_page_free_batched is triggered. | ||||
| Broadly speaking, pages are taken off the LRU lock in bulk and | ||||
| freed in batch with a page list. Significant amounts of activity here could | ||||
| indicate that the system is under memory pressure and can also indicate | ||||
| contention on the zone->lru_lock. | ||||
| contention on the lruvec->lru_lock. | ||||
| 
 | ||||
| 4. Per-CPU Allocator Activity | ||||
| ============================= | ||||
|  | ||||
| @ -33,7 +33,7 @@ reclaim in Linux.  The problems have been observed at customer sites on large | ||||
| memory x86_64 systems. | ||||
| 
 | ||||
| To illustrate this with an example, a non-NUMA x86_64 platform with 128GB of | ||||
| main memory will have over 32 million 4k pages in a single zone.  When a large | ||||
| main memory will have over 32 million 4k pages in a single node.  When a large | ||||
| fraction of these pages are not evictable for any reason [see below], vmscan | ||||
| will spend a lot of time scanning the LRU lists looking for the small fraction | ||||
| of pages that are evictable.  This can result in a situation where all CPUs are | ||||
| @ -55,7 +55,7 @@ unevictable, either by definition or by circumstance, in the future. | ||||
| The Unevictable Page List | ||||
| ------------------------- | ||||
| 
 | ||||
| The Unevictable LRU infrastructure consists of an additional, per-zone, LRU list | ||||
| The Unevictable LRU infrastructure consists of an additional, per-node, LRU list | ||||
| called the "unevictable" list and an associated page flag, PG_unevictable, to | ||||
| indicate that the page is being managed on the unevictable list. | ||||
| 
 | ||||
| @ -84,15 +84,9 @@ The unevictable list does not differentiate between file-backed and anonymous, | ||||
| swap-backed pages.  This differentiation is only important while the pages are, | ||||
| in fact, evictable. | ||||
| 
 | ||||
| The unevictable list benefits from the "arrayification" of the per-zone LRU | ||||
| The unevictable list benefits from the "arrayification" of the per-node LRU | ||||
| lists and statistics originally proposed and posted by Christoph Lameter. | ||||
| 
 | ||||
| The unevictable list does not use the LRU pagevec mechanism. Rather, | ||||
| unevictable pages are placed directly on the page's zone's unevictable list | ||||
| under the zone lru_lock.  This allows us to prevent the stranding of pages on | ||||
| the unevictable list when one task has the page isolated from the LRU and other | ||||
| tasks are changing the "evictability" state of the page. | ||||
| 
 | ||||
| 
 | ||||
| Memory Control Group Interaction | ||||
| -------------------------------- | ||||
| @ -101,8 +95,8 @@ The unevictable LRU facility interacts with the memory control group [aka | ||||
| memory controller; see Documentation/admin-guide/cgroup-v1/memory.rst] by extending the | ||||
| lru_list enum. | ||||
| 
 | ||||
| The memory controller data structure automatically gets a per-zone unevictable | ||||
| list as a result of the "arrayification" of the per-zone LRU lists (one per | ||||
| The memory controller data structure automatically gets a per-node unevictable | ||||
| list as a result of the "arrayification" of the per-node LRU lists (one per | ||||
| lru_list enum element).  The memory controller tracks the movement of pages to | ||||
| and from the unevictable list. | ||||
| 
 | ||||
| @ -196,7 +190,7 @@ for the sake of expediency, to leave a unevictable page on one of the regular | ||||
| active/inactive LRU lists for vmscan to deal with.  vmscan checks for such | ||||
| pages in all of the shrink_{active|inactive|page}_list() functions and will | ||||
| "cull" such pages that it encounters: that is, it diverts those pages to the | ||||
| unevictable list for the zone being scanned. | ||||
| unevictable list for the node being scanned. | ||||
| 
 | ||||
| There may be situations where a page is mapped into a VM_LOCKED VMA, but the | ||||
| page is not marked as PG_mlocked.  Such pages will make it all the way to | ||||
| @ -328,7 +322,7 @@ If the page was NOT already mlocked, mlock_vma_page() attempts to isolate the | ||||
| page from the LRU, as it is likely on the appropriate active or inactive list | ||||
| at that time.  If the isolate_lru_page() succeeds, mlock_vma_page() will put | ||||
| back the page - by calling putback_lru_page() - which will notice that the page | ||||
| is now mlocked and divert the page to the zone's unevictable list.  If | ||||
| is now mlocked and divert the page to the node's unevictable list.  If | ||||
| mlock_vma_page() is unable to isolate the page from the LRU, vmscan will handle | ||||
| it later if and when it attempts to reclaim the page. | ||||
| 
 | ||||
| @ -603,7 +597,7 @@ Some examples of these unevictable pages on the LRU lists are: | ||||
|      unevictable list in mlock_vma_page(). | ||||
| 
 | ||||
| shrink_inactive_list() also diverts any unevictable pages that it finds on the | ||||
| inactive lists to the appropriate zone's unevictable list. | ||||
| inactive lists to the appropriate node's unevictable list. | ||||
| 
 | ||||
| shrink_inactive_list() should only see SHM_LOCK'd pages that became SHM_LOCK'd | ||||
| after shrink_active_list() had moved them to the inactive list, or pages mapped | ||||
|  | ||||
| @ -654,12 +654,41 @@ out: | ||||
| 
 | ||||
| struct lruvec *mem_cgroup_page_lruvec(struct page *, struct pglist_data *); | ||||
| 
 | ||||
| static inline bool lruvec_holds_page_lru_lock(struct page *page, | ||||
| 					      struct lruvec *lruvec) | ||||
| { | ||||
| 	pg_data_t *pgdat = page_pgdat(page); | ||||
| 	const struct mem_cgroup *memcg; | ||||
| 	struct mem_cgroup_per_node *mz; | ||||
| 
 | ||||
| 	if (mem_cgroup_disabled()) | ||||
| 		return lruvec == &pgdat->__lruvec; | ||||
| 
 | ||||
| 	mz = container_of(lruvec, struct mem_cgroup_per_node, lruvec); | ||||
| 	memcg = page_memcg(page) ? : root_mem_cgroup; | ||||
| 
 | ||||
| 	return lruvec->pgdat == pgdat && mz->memcg == memcg; | ||||
| } | ||||
| 
 | ||||
| struct mem_cgroup *mem_cgroup_from_task(struct task_struct *p); | ||||
| 
 | ||||
| struct mem_cgroup *get_mem_cgroup_from_mm(struct mm_struct *mm); | ||||
| 
 | ||||
| struct mem_cgroup *get_mem_cgroup_from_page(struct page *page); | ||||
| 
 | ||||
| struct lruvec *lock_page_lruvec(struct page *page); | ||||
| struct lruvec *lock_page_lruvec_irq(struct page *page); | ||||
| struct lruvec *lock_page_lruvec_irqsave(struct page *page, | ||||
| 						unsigned long *flags); | ||||
| 
 | ||||
| #ifdef CONFIG_DEBUG_VM | ||||
| void lruvec_memcg_debug(struct lruvec *lruvec, struct page *page); | ||||
| #else | ||||
| static inline void lruvec_memcg_debug(struct lruvec *lruvec, struct page *page) | ||||
| { | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| static inline | ||||
| struct mem_cgroup *mem_cgroup_from_css(struct cgroup_subsys_state *css){ | ||||
| 	return css ? container_of(css, struct mem_cgroup, css) : NULL; | ||||
| @ -1167,6 +1196,14 @@ static inline struct lruvec *mem_cgroup_page_lruvec(struct page *page, | ||||
| 	return &pgdat->__lruvec; | ||||
| } | ||||
| 
 | ||||
| static inline bool lruvec_holds_page_lru_lock(struct page *page, | ||||
| 					      struct lruvec *lruvec) | ||||
| { | ||||
| 	pg_data_t *pgdat = page_pgdat(page); | ||||
| 
 | ||||
| 	return lruvec == &pgdat->__lruvec; | ||||
| } | ||||
| 
 | ||||
| static inline struct mem_cgroup *parent_mem_cgroup(struct mem_cgroup *memcg) | ||||
| { | ||||
| 	return NULL; | ||||
| @ -1192,6 +1229,31 @@ static inline void mem_cgroup_put(struct mem_cgroup *memcg) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| static inline struct lruvec *lock_page_lruvec(struct page *page) | ||||
| { | ||||
| 	struct pglist_data *pgdat = page_pgdat(page); | ||||
| 
 | ||||
| 	spin_lock(&pgdat->__lruvec.lru_lock); | ||||
| 	return &pgdat->__lruvec; | ||||
| } | ||||
| 
 | ||||
| static inline struct lruvec *lock_page_lruvec_irq(struct page *page) | ||||
| { | ||||
| 	struct pglist_data *pgdat = page_pgdat(page); | ||||
| 
 | ||||
| 	spin_lock_irq(&pgdat->__lruvec.lru_lock); | ||||
| 	return &pgdat->__lruvec; | ||||
| } | ||||
| 
 | ||||
| static inline struct lruvec *lock_page_lruvec_irqsave(struct page *page, | ||||
| 		unsigned long *flagsp) | ||||
| { | ||||
| 	struct pglist_data *pgdat = page_pgdat(page); | ||||
| 
 | ||||
| 	spin_lock_irqsave(&pgdat->__lruvec.lru_lock, *flagsp); | ||||
| 	return &pgdat->__lruvec; | ||||
| } | ||||
| 
 | ||||
| static inline struct mem_cgroup * | ||||
| mem_cgroup_iter(struct mem_cgroup *root, | ||||
| 		struct mem_cgroup *prev, | ||||
| @ -1411,6 +1473,10 @@ static inline | ||||
| void count_memcg_event_mm(struct mm_struct *mm, enum vm_event_item idx) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| static inline void lruvec_memcg_debug(struct lruvec *lruvec, struct page *page) | ||||
| { | ||||
| } | ||||
| #endif /* CONFIG_MEMCG */ | ||||
| 
 | ||||
| /* idx can be of type enum memcg_stat_item or node_stat_item */ | ||||
| @ -1492,6 +1558,50 @@ static inline struct lruvec *parent_lruvec(struct lruvec *lruvec) | ||||
| 	return mem_cgroup_lruvec(memcg, lruvec_pgdat(lruvec)); | ||||
| } | ||||
| 
 | ||||
| static inline void unlock_page_lruvec(struct lruvec *lruvec) | ||||
| { | ||||
| 	spin_unlock(&lruvec->lru_lock); | ||||
| } | ||||
| 
 | ||||
| static inline void unlock_page_lruvec_irq(struct lruvec *lruvec) | ||||
| { | ||||
| 	spin_unlock_irq(&lruvec->lru_lock); | ||||
| } | ||||
| 
 | ||||
| static inline void unlock_page_lruvec_irqrestore(struct lruvec *lruvec, | ||||
| 		unsigned long flags) | ||||
| { | ||||
| 	spin_unlock_irqrestore(&lruvec->lru_lock, flags); | ||||
| } | ||||
| 
 | ||||
| /* Don't lock again iff page's lruvec locked */ | ||||
| static inline struct lruvec *relock_page_lruvec_irq(struct page *page, | ||||
| 		struct lruvec *locked_lruvec) | ||||
| { | ||||
| 	if (locked_lruvec) { | ||||
| 		if (lruvec_holds_page_lru_lock(page, locked_lruvec)) | ||||
| 			return locked_lruvec; | ||||
| 
 | ||||
| 		unlock_page_lruvec_irq(locked_lruvec); | ||||
| 	} | ||||
| 
 | ||||
| 	return lock_page_lruvec_irq(page); | ||||
| } | ||||
| 
 | ||||
| /* Don't lock again iff page's lruvec locked */ | ||||
| static inline struct lruvec *relock_page_lruvec_irqsave(struct page *page, | ||||
| 		struct lruvec *locked_lruvec, unsigned long *flags) | ||||
| { | ||||
| 	if (locked_lruvec) { | ||||
| 		if (lruvec_holds_page_lru_lock(page, locked_lruvec)) | ||||
| 			return locked_lruvec; | ||||
| 
 | ||||
| 		unlock_page_lruvec_irqrestore(locked_lruvec, *flags); | ||||
| 	} | ||||
| 
 | ||||
| 	return lock_page_lruvec_irqsave(page, flags); | ||||
| } | ||||
| 
 | ||||
| #ifdef CONFIG_CGROUP_WRITEBACK | ||||
| 
 | ||||
| struct wb_domain *mem_cgroup_wb_domain(struct bdi_writeback *wb); | ||||
|  | ||||
| @ -79,7 +79,7 @@ struct page { | ||||
| 		struct {	/* Page cache and anonymous pages */ | ||||
| 			/**
 | ||||
| 			 * @lru: Pageout list, eg. active_list protected by | ||||
| 			 * pgdat->lru_lock.  Sometimes used as a generic list | ||||
| 			 * lruvec->lru_lock.  Sometimes used as a generic list | ||||
| 			 * by the page owner. | ||||
| 			 */ | ||||
| 			struct list_head lru; | ||||
|  | ||||
| @ -113,8 +113,7 @@ static inline bool free_area_empty(struct free_area *area, int migratetype) | ||||
| struct pglist_data; | ||||
| 
 | ||||
| /*
 | ||||
|  * zone->lock and the zone lru_lock are two of the hottest locks in the kernel. | ||||
|  * So add a wild amount of padding here to ensure that they fall into separate | ||||
|  * Add a wild amount of padding here to ensure datas fall into separate | ||||
|  * cachelines.  There are very few zone structures in the machine, so space | ||||
|  * consumption is not a concern here. | ||||
|  */ | ||||
| @ -276,6 +275,8 @@ enum lruvec_flags { | ||||
| 
 | ||||
| struct lruvec { | ||||
| 	struct list_head		lists[NR_LRU_LISTS]; | ||||
| 	/* per lruvec lru_lock for memcg */ | ||||
| 	spinlock_t			lru_lock; | ||||
| 	/*
 | ||||
| 	 * These track the cost of reclaiming one LRU - file or anon - | ||||
| 	 * over the other. As the observed cost of reclaiming one LRU | ||||
| @ -782,7 +783,6 @@ typedef struct pglist_data { | ||||
| 
 | ||||
| 	/* Write-intensive fields used by page reclaim */ | ||||
| 	ZONE_PADDING(_pad1_) | ||||
| 	spinlock_t		lru_lock; | ||||
| 
 | ||||
| #ifdef CONFIG_DEFERRED_STRUCT_PAGE_INIT | ||||
| 	/*
 | ||||
|  | ||||
| @ -334,6 +334,7 @@ PAGEFLAG(Referenced, referenced, PF_HEAD) | ||||
| PAGEFLAG(Dirty, dirty, PF_HEAD) TESTSCFLAG(Dirty, dirty, PF_HEAD) | ||||
| 	__CLEARPAGEFLAG(Dirty, dirty, PF_HEAD) | ||||
| PAGEFLAG(LRU, lru, PF_HEAD) __CLEARPAGEFLAG(LRU, lru, PF_HEAD) | ||||
| 	TESTCLEARFLAG(LRU, lru, PF_HEAD) | ||||
| PAGEFLAG(Active, active, PF_HEAD) __CLEARPAGEFLAG(Active, active, PF_HEAD) | ||||
| 	TESTCLEARFLAG(Active, active, PF_HEAD) | ||||
| PAGEFLAG(Workingset, workingset, PF_HEAD) | ||||
|  | ||||
| @ -338,8 +338,6 @@ extern void lru_note_cost(struct lruvec *lruvec, bool file, | ||||
| 			  unsigned int nr_pages); | ||||
| extern void lru_note_cost_page(struct page *); | ||||
| extern void lru_cache_add(struct page *); | ||||
| extern void lru_add_page_tail(struct page *page, struct page *page_tail, | ||||
| 			 struct lruvec *lruvec, struct list_head *head); | ||||
| extern void mark_page_accessed(struct page *); | ||||
| extern void lru_add_drain(void); | ||||
| extern void lru_add_drain_cpu(int cpu); | ||||
| @ -358,7 +356,7 @@ extern void lru_cache_add_inactive_or_unevictable(struct page *page, | ||||
| extern unsigned long zone_reclaimable_pages(struct zone *zone); | ||||
| extern unsigned long try_to_free_pages(struct zonelist *zonelist, int order, | ||||
| 					gfp_t gfp_mask, nodemask_t *mask); | ||||
| extern int __isolate_lru_page(struct page *page, isolate_mode_t mode); | ||||
| extern int __isolate_lru_page_prepare(struct page *page, isolate_mode_t mode); | ||||
| extern unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg, | ||||
| 						  unsigned long nr_pages, | ||||
| 						  gfp_t gfp_mask, | ||||
|  | ||||
| @ -804,7 +804,7 @@ isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn, | ||||
| 	unsigned long nr_scanned = 0, nr_isolated = 0; | ||||
| 	struct lruvec *lruvec; | ||||
| 	unsigned long flags = 0; | ||||
| 	bool locked = false; | ||||
| 	struct lruvec *locked = NULL; | ||||
| 	struct page *page = NULL, *valid_page = NULL; | ||||
| 	unsigned long start_pfn = low_pfn; | ||||
| 	bool skip_on_failure = false; | ||||
| @ -868,11 +868,20 @@ isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn, | ||||
| 		 * contention, to give chance to IRQs. Abort completely if | ||||
| 		 * a fatal signal is pending. | ||||
| 		 */ | ||||
| 		if (!(low_pfn % SWAP_CLUSTER_MAX) | ||||
| 		    && compact_unlock_should_abort(&pgdat->lru_lock, | ||||
| 					    flags, &locked, cc)) { | ||||
| 			low_pfn = 0; | ||||
| 			goto fatal_pending; | ||||
| 		if (!(low_pfn % SWAP_CLUSTER_MAX)) { | ||||
| 			if (locked) { | ||||
| 				unlock_page_lruvec_irqrestore(locked, flags); | ||||
| 				locked = NULL; | ||||
| 			} | ||||
| 
 | ||||
| 			if (fatal_signal_pending(current)) { | ||||
| 				cc->contended = true; | ||||
| 
 | ||||
| 				low_pfn = 0; | ||||
| 				goto fatal_pending; | ||||
| 			} | ||||
| 
 | ||||
| 			cond_resched(); | ||||
| 		} | ||||
| 
 | ||||
| 		if (!pfn_valid_within(low_pfn)) | ||||
| @ -890,6 +899,7 @@ isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn, | ||||
| 		if (!valid_page && IS_ALIGNED(low_pfn, pageblock_nr_pages)) { | ||||
| 			if (!cc->ignore_skip_hint && get_pageblock_skip(page)) { | ||||
| 				low_pfn = end_pfn; | ||||
| 				page = NULL; | ||||
| 				goto isolate_abort; | ||||
| 			} | ||||
| 			valid_page = page; | ||||
| @ -943,9 +953,8 @@ isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn, | ||||
| 			if (unlikely(__PageMovable(page)) && | ||||
| 					!PageIsolated(page)) { | ||||
| 				if (locked) { | ||||
| 					spin_unlock_irqrestore(&pgdat->lru_lock, | ||||
| 									flags); | ||||
| 					locked = false; | ||||
| 					unlock_page_lruvec_irqrestore(locked, flags); | ||||
| 					locked = NULL; | ||||
| 				} | ||||
| 
 | ||||
| 				if (!isolate_movable_page(page, isolate_mode)) | ||||
| @ -971,10 +980,34 @@ isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn, | ||||
| 		if (!(cc->gfp_mask & __GFP_FS) && page_mapping(page)) | ||||
| 			goto isolate_fail; | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * Be careful not to clear PageLRU until after we're | ||||
| 		 * sure the page is not being freed elsewhere -- the | ||||
| 		 * page release code relies on it. | ||||
| 		 */ | ||||
| 		if (unlikely(!get_page_unless_zero(page))) | ||||
| 			goto isolate_fail; | ||||
| 
 | ||||
| 		if (__isolate_lru_page_prepare(page, isolate_mode) != 0) | ||||
| 			goto isolate_fail_put; | ||||
| 
 | ||||
| 		/* Try isolate the page */ | ||||
| 		if (!TestClearPageLRU(page)) | ||||
| 			goto isolate_fail_put; | ||||
| 
 | ||||
| 		rcu_read_lock(); | ||||
| 		lruvec = mem_cgroup_page_lruvec(page, pgdat); | ||||
| 
 | ||||
| 		/* If we already hold the lock, we can skip some rechecking */ | ||||
| 		if (!locked) { | ||||
| 			locked = compact_lock_irqsave(&pgdat->lru_lock, | ||||
| 								&flags, cc); | ||||
| 		if (lruvec != locked) { | ||||
| 			if (locked) | ||||
| 				unlock_page_lruvec_irqrestore(locked, flags); | ||||
| 
 | ||||
| 			compact_lock_irqsave(&lruvec->lru_lock, &flags, cc); | ||||
| 			locked = lruvec; | ||||
| 			rcu_read_unlock(); | ||||
| 
 | ||||
| 			lruvec_memcg_debug(lruvec, page); | ||||
| 
 | ||||
| 			/* Try get exclusive access under lock */ | ||||
| 			if (!skip_updated) { | ||||
| @ -983,10 +1016,6 @@ isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn, | ||||
| 					goto isolate_abort; | ||||
| 			} | ||||
| 
 | ||||
| 			/* Recheck PageLRU and PageCompound under lock */ | ||||
| 			if (!PageLRU(page)) | ||||
| 				goto isolate_fail; | ||||
| 
 | ||||
| 			/*
 | ||||
| 			 * Page become compound since the non-locked check, | ||||
| 			 * and it's on LRU. It can only be a THP so the order | ||||
| @ -994,15 +1023,11 @@ isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn, | ||||
| 			 */ | ||||
| 			if (unlikely(PageCompound(page) && !cc->alloc_contig)) { | ||||
| 				low_pfn += compound_nr(page) - 1; | ||||
| 				goto isolate_fail; | ||||
| 				SetPageLRU(page); | ||||
| 				goto isolate_fail_put; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		lruvec = mem_cgroup_page_lruvec(page, pgdat); | ||||
| 
 | ||||
| 		/* Try isolate the page */ | ||||
| 		if (__isolate_lru_page(page, isolate_mode) != 0) | ||||
| 			goto isolate_fail; | ||||
| 		} else | ||||
| 			rcu_read_unlock(); | ||||
| 
 | ||||
| 		/* The whole page is taken off the LRU; skip the tail pages. */ | ||||
| 		if (PageCompound(page)) | ||||
| @ -1032,6 +1057,15 @@ isolate_success: | ||||
| 		} | ||||
| 
 | ||||
| 		continue; | ||||
| 
 | ||||
| isolate_fail_put: | ||||
| 		/* Avoid potential deadlock in freeing page under lru_lock */ | ||||
| 		if (locked) { | ||||
| 			unlock_page_lruvec_irqrestore(locked, flags); | ||||
| 			locked = NULL; | ||||
| 		} | ||||
| 		put_page(page); | ||||
| 
 | ||||
| isolate_fail: | ||||
| 		if (!skip_on_failure) | ||||
| 			continue; | ||||
| @ -1043,8 +1077,8 @@ isolate_fail: | ||||
| 		 */ | ||||
| 		if (nr_isolated) { | ||||
| 			if (locked) { | ||||
| 				spin_unlock_irqrestore(&pgdat->lru_lock, flags); | ||||
| 				locked = false; | ||||
| 				unlock_page_lruvec_irqrestore(locked, flags); | ||||
| 				locked = NULL; | ||||
| 			} | ||||
| 			putback_movable_pages(&cc->migratepages); | ||||
| 			cc->nr_migratepages = 0; | ||||
| @ -1068,9 +1102,15 @@ isolate_fail: | ||||
| 	if (unlikely(low_pfn > end_pfn)) | ||||
| 		low_pfn = end_pfn; | ||||
| 
 | ||||
| 	page = NULL; | ||||
| 
 | ||||
| isolate_abort: | ||||
| 	if (locked) | ||||
| 		spin_unlock_irqrestore(&pgdat->lru_lock, flags); | ||||
| 		unlock_page_lruvec_irqrestore(locked, flags); | ||||
| 	if (page) { | ||||
| 		SetPageLRU(page); | ||||
| 		put_page(page); | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Updated the cached scanner pfn once the pageblock has been scanned | ||||
|  | ||||
| @ -102,8 +102,8 @@ | ||||
|  *    ->swap_lock		(try_to_unmap_one) | ||||
|  *    ->private_lock		(try_to_unmap_one) | ||||
|  *    ->i_pages lock		(try_to_unmap_one) | ||||
|  *    ->pgdat->lru_lock		(follow_page->mark_page_accessed) | ||||
|  *    ->pgdat->lru_lock		(check_pte_range->isolate_lru_page) | ||||
|  *    ->lruvec->lru_lock	(follow_page->mark_page_accessed) | ||||
|  *    ->lruvec->lru_lock	(check_pte_range->isolate_lru_page) | ||||
|  *    ->private_lock		(page_remove_rmap->set_page_dirty) | ||||
|  *    ->i_pages lock		(page_remove_rmap->set_page_dirty) | ||||
|  *    bdi.wb->list_lock		(page_remove_rmap->set_page_dirty) | ||||
|  | ||||
| @ -2359,6 +2359,27 @@ static void remap_page(struct page *page, unsigned int nr) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void lru_add_page_tail(struct page *head, struct page *tail, | ||||
| 		struct lruvec *lruvec, struct list_head *list) | ||||
| { | ||||
| 	VM_BUG_ON_PAGE(!PageHead(head), head); | ||||
| 	VM_BUG_ON_PAGE(PageCompound(tail), head); | ||||
| 	VM_BUG_ON_PAGE(PageLRU(tail), head); | ||||
| 	lockdep_assert_held(&lruvec->lru_lock); | ||||
| 
 | ||||
| 	if (list) { | ||||
| 		/* page reclaim is reclaiming a huge page */ | ||||
| 		VM_WARN_ON(PageLRU(head)); | ||||
| 		get_page(tail); | ||||
| 		list_add_tail(&tail->lru, list); | ||||
| 	} else { | ||||
| 		/* head is still on lru (and we have it frozen) */ | ||||
| 		VM_WARN_ON(!PageLRU(head)); | ||||
| 		SetPageLRU(tail); | ||||
| 		list_add_tail(&tail->lru, &head->lru); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void __split_huge_page_tail(struct page *head, int tail, | ||||
| 		struct lruvec *lruvec, struct list_head *list) | ||||
| { | ||||
| @ -2425,18 +2446,15 @@ static void __split_huge_page_tail(struct page *head, int tail, | ||||
| } | ||||
| 
 | ||||
| static void __split_huge_page(struct page *page, struct list_head *list, | ||||
| 		pgoff_t end, unsigned long flags) | ||||
| 		pgoff_t end) | ||||
| { | ||||
| 	struct page *head = compound_head(page); | ||||
| 	pg_data_t *pgdat = page_pgdat(head); | ||||
| 	struct lruvec *lruvec; | ||||
| 	struct address_space *swap_cache = NULL; | ||||
| 	unsigned long offset = 0; | ||||
| 	unsigned int nr = thp_nr_pages(head); | ||||
| 	int i; | ||||
| 
 | ||||
| 	lruvec = mem_cgroup_page_lruvec(head, pgdat); | ||||
| 
 | ||||
| 	/* complete memcg works before add pages to LRU */ | ||||
| 	mem_cgroup_split_huge_fixup(head); | ||||
| 
 | ||||
| @ -2448,6 +2466,9 @@ static void __split_huge_page(struct page *page, struct list_head *list, | ||||
| 		xa_lock(&swap_cache->i_pages); | ||||
| 	} | ||||
| 
 | ||||
| 	/* lock lru list/PageCompound, ref freezed by page_ref_freeze */ | ||||
| 	lruvec = lock_page_lruvec(head); | ||||
| 
 | ||||
| 	for (i = nr - 1; i >= 1; i--) { | ||||
| 		__split_huge_page_tail(head, i, lruvec, list); | ||||
| 		/* Some pages can be beyond i_size: drop them from page cache */ | ||||
| @ -2467,6 +2488,8 @@ static void __split_huge_page(struct page *page, struct list_head *list, | ||||
| 	} | ||||
| 
 | ||||
| 	ClearPageCompound(head); | ||||
| 	unlock_page_lruvec(lruvec); | ||||
| 	/* Caller disabled irqs, so they are still disabled here */ | ||||
| 
 | ||||
| 	split_page_owner(head, nr); | ||||
| 
 | ||||
| @ -2484,8 +2507,7 @@ static void __split_huge_page(struct page *page, struct list_head *list, | ||||
| 		page_ref_add(head, 2); | ||||
| 		xa_unlock(&head->mapping->i_pages); | ||||
| 	} | ||||
| 
 | ||||
| 	spin_unlock_irqrestore(&pgdat->lru_lock, flags); | ||||
| 	local_irq_enable(); | ||||
| 
 | ||||
| 	remap_page(head, nr); | ||||
| 
 | ||||
| @ -2631,12 +2653,10 @@ bool can_split_huge_page(struct page *page, int *pextra_pins) | ||||
| int split_huge_page_to_list(struct page *page, struct list_head *list) | ||||
| { | ||||
| 	struct page *head = compound_head(page); | ||||
| 	struct pglist_data *pgdata = NODE_DATA(page_to_nid(head)); | ||||
| 	struct deferred_split *ds_queue = get_deferred_split_queue(head); | ||||
| 	struct anon_vma *anon_vma = NULL; | ||||
| 	struct address_space *mapping = NULL; | ||||
| 	int count, mapcount, extra_pins, ret; | ||||
| 	unsigned long flags; | ||||
| 	pgoff_t end; | ||||
| 
 | ||||
| 	VM_BUG_ON_PAGE(is_huge_zero_page(head), head); | ||||
| @ -2697,9 +2717,8 @@ int split_huge_page_to_list(struct page *page, struct list_head *list) | ||||
| 	unmap_page(head); | ||||
| 	VM_BUG_ON_PAGE(compound_mapcount(head), head); | ||||
| 
 | ||||
| 	/* prevent PageLRU to go away from under us, and freeze lru stats */ | ||||
| 	spin_lock_irqsave(&pgdata->lru_lock, flags); | ||||
| 
 | ||||
| 	/* block interrupt reentry in xa_lock and spinlock */ | ||||
| 	local_irq_disable(); | ||||
| 	if (mapping) { | ||||
| 		XA_STATE(xas, &mapping->i_pages, page_index(head)); | ||||
| 
 | ||||
| @ -2729,7 +2748,7 @@ int split_huge_page_to_list(struct page *page, struct list_head *list) | ||||
| 				__dec_lruvec_page_state(head, NR_FILE_THPS); | ||||
| 		} | ||||
| 
 | ||||
| 		__split_huge_page(page, list, end, flags); | ||||
| 		__split_huge_page(page, list, end); | ||||
| 		ret = 0; | ||||
| 	} else { | ||||
| 		if (IS_ENABLED(CONFIG_DEBUG_VM) && mapcount) { | ||||
| @ -2743,7 +2762,7 @@ int split_huge_page_to_list(struct page *page, struct list_head *list) | ||||
| 		spin_unlock(&ds_queue->split_queue_lock); | ||||
| fail:		if (mapping) | ||||
| 			xa_unlock(&mapping->i_pages); | ||||
| 		spin_unlock_irqrestore(&pgdata->lru_lock, flags); | ||||
| 		local_irq_enable(); | ||||
| 		remap_page(head, thp_nr_pages(head)); | ||||
| 		ret = -EBUSY; | ||||
| 	} | ||||
|  | ||||
| @ -20,6 +20,9 @@ | ||||
|  * Lockless page tracking & accounting | ||||
|  * Unified hierarchy configuration model | ||||
|  * Copyright (C) 2015 Red Hat, Inc., Johannes Weiner | ||||
|  * | ||||
|  * Per memcg lru locking | ||||
|  * Copyright (C) 2020 Alibaba, Inc, Alex Shi | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/page_counter.h> | ||||
| @ -1322,6 +1325,23 @@ int mem_cgroup_scan_tasks(struct mem_cgroup *memcg, | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| #ifdef CONFIG_DEBUG_VM | ||||
| void lruvec_memcg_debug(struct lruvec *lruvec, struct page *page) | ||||
| { | ||||
| 	struct mem_cgroup *memcg; | ||||
| 
 | ||||
| 	if (mem_cgroup_disabled()) | ||||
| 		return; | ||||
| 
 | ||||
| 	memcg = page_memcg(page); | ||||
| 
 | ||||
| 	if (!memcg) | ||||
| 		VM_BUG_ON_PAGE(lruvec_memcg(lruvec) != root_mem_cgroup, page); | ||||
| 	else | ||||
| 		VM_BUG_ON_PAGE(lruvec_memcg(lruvec) != memcg, page); | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| /**
 | ||||
|  * mem_cgroup_page_lruvec - return lruvec for isolating/putting an LRU page | ||||
|  * @page: the page | ||||
| @ -1362,6 +1382,60 @@ out: | ||||
| 	return lruvec; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * lock_page_lruvec - lock and return lruvec for a given page. | ||||
|  * @page: the page | ||||
|  * | ||||
|  * This series functions should be used in either conditions: | ||||
|  * PageLRU is cleared or unset | ||||
|  * or page->_refcount is zero | ||||
|  * or page is locked. | ||||
|  */ | ||||
| struct lruvec *lock_page_lruvec(struct page *page) | ||||
| { | ||||
| 	struct lruvec *lruvec; | ||||
| 	struct pglist_data *pgdat = page_pgdat(page); | ||||
| 
 | ||||
| 	rcu_read_lock(); | ||||
| 	lruvec = mem_cgroup_page_lruvec(page, pgdat); | ||||
| 	spin_lock(&lruvec->lru_lock); | ||||
| 	rcu_read_unlock(); | ||||
| 
 | ||||
| 	lruvec_memcg_debug(lruvec, page); | ||||
| 
 | ||||
| 	return lruvec; | ||||
| } | ||||
| 
 | ||||
| struct lruvec *lock_page_lruvec_irq(struct page *page) | ||||
| { | ||||
| 	struct lruvec *lruvec; | ||||
| 	struct pglist_data *pgdat = page_pgdat(page); | ||||
| 
 | ||||
| 	rcu_read_lock(); | ||||
| 	lruvec = mem_cgroup_page_lruvec(page, pgdat); | ||||
| 	spin_lock_irq(&lruvec->lru_lock); | ||||
| 	rcu_read_unlock(); | ||||
| 
 | ||||
| 	lruvec_memcg_debug(lruvec, page); | ||||
| 
 | ||||
| 	return lruvec; | ||||
| } | ||||
| 
 | ||||
| struct lruvec *lock_page_lruvec_irqsave(struct page *page, unsigned long *flags) | ||||
| { | ||||
| 	struct lruvec *lruvec; | ||||
| 	struct pglist_data *pgdat = page_pgdat(page); | ||||
| 
 | ||||
| 	rcu_read_lock(); | ||||
| 	lruvec = mem_cgroup_page_lruvec(page, pgdat); | ||||
| 	spin_lock_irqsave(&lruvec->lru_lock, *flags); | ||||
| 	rcu_read_unlock(); | ||||
| 
 | ||||
| 	lruvec_memcg_debug(lruvec, page); | ||||
| 
 | ||||
| 	return lruvec; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * mem_cgroup_update_lru_size - account for adding or removing an lru page | ||||
|  * @lruvec: mem_cgroup per zone lru vector | ||||
| @ -2142,6 +2216,12 @@ again: | ||||
| 	if (unlikely(!memcg)) | ||||
| 		return NULL; | ||||
| 
 | ||||
| #ifdef CONFIG_PROVE_LOCKING | ||||
| 	local_irq_save(flags); | ||||
| 	might_lock(&memcg->move_lock); | ||||
| 	local_irq_restore(flags); | ||||
| #endif | ||||
| 
 | ||||
| 	if (atomic_read(&memcg->moving_account) <= 0) | ||||
| 		return memcg; | ||||
| 
 | ||||
| @ -3263,10 +3343,8 @@ void obj_cgroup_uncharge(struct obj_cgroup *objcg, size_t size) | ||||
| #endif /* CONFIG_MEMCG_KMEM */ | ||||
| 
 | ||||
| #ifdef CONFIG_TRANSPARENT_HUGEPAGE | ||||
| 
 | ||||
| /*
 | ||||
|  * Because tail pages are not marked as "used", set it. We're under | ||||
|  * pgdat->lru_lock and migration entries setup in all page mappings. | ||||
|  * Because page_memcg(head) is not set on compound tails, set it now. | ||||
|  */ | ||||
| void mem_cgroup_split_huge_fixup(struct page *head) | ||||
| { | ||||
|  | ||||
							
								
								
									
										63
									
								
								mm/mlock.c
									
									
									
									
									
								
							
							
						
						
									
										63
									
								
								mm/mlock.c
									
									
									
									
									
								
							| @ -105,26 +105,6 @@ void mlock_vma_page(struct page *page) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Isolate a page from LRU with optional get_page() pin. | ||||
|  * Assumes lru_lock already held and page already pinned. | ||||
|  */ | ||||
| static bool __munlock_isolate_lru_page(struct page *page, bool getpage) | ||||
| { | ||||
| 	if (PageLRU(page)) { | ||||
| 		struct lruvec *lruvec; | ||||
| 
 | ||||
| 		lruvec = mem_cgroup_page_lruvec(page, page_pgdat(page)); | ||||
| 		if (getpage) | ||||
| 			get_page(page); | ||||
| 		ClearPageLRU(page); | ||||
| 		del_page_from_lru_list(page, lruvec, page_lru(page)); | ||||
| 		return true; | ||||
| 	} | ||||
| 
 | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Finish munlock after successful page isolation | ||||
|  * | ||||
| @ -187,40 +167,24 @@ static void __munlock_isolation_failed(struct page *page) | ||||
| unsigned int munlock_vma_page(struct page *page) | ||||
| { | ||||
| 	int nr_pages; | ||||
| 	pg_data_t *pgdat = page_pgdat(page); | ||||
| 
 | ||||
| 	/* For try_to_munlock() and to serialize with page migration */ | ||||
| 	BUG_ON(!PageLocked(page)); | ||||
| 
 | ||||
| 	VM_BUG_ON_PAGE(PageTail(page), page); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Serialize with any parallel __split_huge_page_refcount() which | ||||
| 	 * might otherwise copy PageMlocked to part of the tail pages before | ||||
| 	 * we clear it in the head page. It also stabilizes thp_nr_pages(). | ||||
| 	 */ | ||||
| 	spin_lock_irq(&pgdat->lru_lock); | ||||
| 
 | ||||
| 	if (!TestClearPageMlocked(page)) { | ||||
| 		/* Potentially, PTE-mapped THP: do not skip the rest PTEs */ | ||||
| 		nr_pages = 1; | ||||
| 		goto unlock_out; | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	nr_pages = thp_nr_pages(page); | ||||
| 	__mod_zone_page_state(page_zone(page), NR_MLOCK, -nr_pages); | ||||
| 	mod_zone_page_state(page_zone(page), NR_MLOCK, -nr_pages); | ||||
| 
 | ||||
| 	if (__munlock_isolate_lru_page(page, true)) { | ||||
| 		spin_unlock_irq(&pgdat->lru_lock); | ||||
| 	if (!isolate_lru_page(page)) | ||||
| 		__munlock_isolated_page(page); | ||||
| 		goto out; | ||||
| 	} | ||||
| 	__munlock_isolation_failed(page); | ||||
| 	else | ||||
| 		__munlock_isolation_failed(page); | ||||
| 
 | ||||
| unlock_out: | ||||
| 	spin_unlock_irq(&pgdat->lru_lock); | ||||
| 
 | ||||
| out: | ||||
| 	return nr_pages - 1; | ||||
| } | ||||
| 
 | ||||
| @ -298,12 +262,12 @@ static void __munlock_pagevec(struct pagevec *pvec, struct zone *zone) | ||||
| 	int nr = pagevec_count(pvec); | ||||
| 	int delta_munlocked = -nr; | ||||
| 	struct pagevec pvec_putback; | ||||
| 	struct lruvec *lruvec = NULL; | ||||
| 	int pgrescued = 0; | ||||
| 
 | ||||
| 	pagevec_init(&pvec_putback); | ||||
| 
 | ||||
| 	/* Phase 1: page isolation */ | ||||
| 	spin_lock_irq(&zone->zone_pgdat->lru_lock); | ||||
| 	for (i = 0; i < nr; i++) { | ||||
| 		struct page *page = pvec->pages[i]; | ||||
| 
 | ||||
| @ -312,9 +276,12 @@ static void __munlock_pagevec(struct pagevec *pvec, struct zone *zone) | ||||
| 			 * We already have pin from follow_page_mask() | ||||
| 			 * so we can spare the get_page() here. | ||||
| 			 */ | ||||
| 			if (__munlock_isolate_lru_page(page, false)) | ||||
| 			if (TestClearPageLRU(page)) { | ||||
| 				lruvec = relock_page_lruvec_irq(page, lruvec); | ||||
| 				del_page_from_lru_list(page, lruvec, | ||||
| 							page_lru(page)); | ||||
| 				continue; | ||||
| 			else | ||||
| 			} else | ||||
| 				__munlock_isolation_failed(page); | ||||
| 		} else { | ||||
| 			delta_munlocked++; | ||||
| @ -329,8 +296,12 @@ static void __munlock_pagevec(struct pagevec *pvec, struct zone *zone) | ||||
| 		pagevec_add(&pvec_putback, pvec->pages[i]); | ||||
| 		pvec->pages[i] = NULL; | ||||
| 	} | ||||
| 	__mod_zone_page_state(zone, NR_MLOCK, delta_munlocked); | ||||
| 	spin_unlock_irq(&zone->zone_pgdat->lru_lock); | ||||
| 	if (lruvec) { | ||||
| 		__mod_zone_page_state(zone, NR_MLOCK, delta_munlocked); | ||||
| 		unlock_page_lruvec_irq(lruvec); | ||||
| 	} else if (delta_munlocked) { | ||||
| 		mod_zone_page_state(zone, NR_MLOCK, delta_munlocked); | ||||
| 	} | ||||
| 
 | ||||
| 	/* Now we can release pins of pages that we are not munlocking */ | ||||
| 	pagevec_release(&pvec_putback); | ||||
|  | ||||
| @ -77,6 +77,7 @@ void lruvec_init(struct lruvec *lruvec) | ||||
| 	enum lru_list lru; | ||||
| 
 | ||||
| 	memset(lruvec, 0, sizeof(struct lruvec)); | ||||
| 	spin_lock_init(&lruvec->lru_lock); | ||||
| 
 | ||||
| 	for_each_lru(lru) | ||||
| 		INIT_LIST_HEAD(&lruvec->lists[lru]); | ||||
|  | ||||
| @ -6870,7 +6870,6 @@ static void __meminit pgdat_init_internals(struct pglist_data *pgdat) | ||||
| 	init_waitqueue_head(&pgdat->pfmemalloc_wait); | ||||
| 
 | ||||
| 	pgdat_page_ext_init(pgdat); | ||||
| 	spin_lock_init(&pgdat->lru_lock); | ||||
| 	lruvec_init(&pgdat->__lruvec); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -32,19 +32,15 @@ | ||||
| static struct page *page_idle_get_page(unsigned long pfn) | ||||
| { | ||||
| 	struct page *page = pfn_to_online_page(pfn); | ||||
| 	pg_data_t *pgdat; | ||||
| 
 | ||||
| 	if (!page || !PageLRU(page) || | ||||
| 	    !get_page_unless_zero(page)) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	pgdat = page_pgdat(page); | ||||
| 	spin_lock_irq(&pgdat->lru_lock); | ||||
| 	if (unlikely(!PageLRU(page))) { | ||||
| 		put_page(page); | ||||
| 		page = NULL; | ||||
| 	} | ||||
| 	spin_unlock_irq(&pgdat->lru_lock); | ||||
| 	return page; | ||||
| } | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										12
									
								
								mm/rmap.c
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								mm/rmap.c
									
									
									
									
									
								
							| @ -28,12 +28,12 @@ | ||||
|  *           hugetlb_fault_mutex (hugetlbfs specific page fault mutex) | ||||
|  *           anon_vma->rwsem | ||||
|  *             mm->page_table_lock or pte_lock | ||||
|  *               pgdat->lru_lock (in mark_page_accessed, isolate_lru_page) | ||||
|  *               swap_lock (in swap_duplicate, swap_info_get) | ||||
|  *                 mmlist_lock (in mmput, drain_mmlist and others) | ||||
|  *                 mapping->private_lock (in __set_page_dirty_buffers) | ||||
|  *                   mem_cgroup_{begin,end}_page_stat (memcg->move_lock) | ||||
|  *                   lock_page_memcg move_lock (in __set_page_dirty_buffers) | ||||
|  *                     i_pages lock (widely used) | ||||
|  *                       lruvec->lru_lock (in lock_page_lruvec_irq) | ||||
|  *                 inode->i_lock (in set_page_dirty's __mark_inode_dirty) | ||||
|  *                 bdi.wb->list_lock (in set_page_dirty's __mark_inode_dirty) | ||||
|  *                   sb_lock (within inode_lock in fs/fs-writeback.c) | ||||
| @ -1054,8 +1054,14 @@ static void __page_set_anon_rmap(struct page *page, | ||||
| 	if (!exclusive) | ||||
| 		anon_vma = anon_vma->root; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * page_idle does a lockless/optimistic rmap scan on page->mapping. | ||||
| 	 * Make sure the compiler doesn't split the stores of anon_vma and | ||||
| 	 * the PAGE_MAPPING_ANON type identifier, otherwise the rmap code | ||||
| 	 * could mistake the mapping for a struct address_space and crash. | ||||
| 	 */ | ||||
| 	anon_vma = (void *) anon_vma + PAGE_MAPPING_ANON; | ||||
| 	page->mapping = (struct address_space *) anon_vma; | ||||
| 	WRITE_ONCE(page->mapping, (struct address_space *) anon_vma); | ||||
| 	page->index = linear_page_index(vma, address); | ||||
| } | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										208
									
								
								mm/swap.c
									
									
									
									
									
								
							
							
						
						
									
										208
									
								
								mm/swap.c
									
									
									
									
									
								
							| @ -79,16 +79,14 @@ static DEFINE_PER_CPU(struct lru_pvecs, lru_pvecs) = { | ||||
| static void __page_cache_release(struct page *page) | ||||
| { | ||||
| 	if (PageLRU(page)) { | ||||
| 		pg_data_t *pgdat = page_pgdat(page); | ||||
| 		struct lruvec *lruvec; | ||||
| 		unsigned long flags; | ||||
| 
 | ||||
| 		spin_lock_irqsave(&pgdat->lru_lock, flags); | ||||
| 		lruvec = mem_cgroup_page_lruvec(page, pgdat); | ||||
| 		lruvec = lock_page_lruvec_irqsave(page, &flags); | ||||
| 		VM_BUG_ON_PAGE(!PageLRU(page), page); | ||||
| 		__ClearPageLRU(page); | ||||
| 		del_page_from_lru_list(page, lruvec, page_off_lru(page)); | ||||
| 		spin_unlock_irqrestore(&pgdat->lru_lock, flags); | ||||
| 		unlock_page_lruvec_irqrestore(lruvec, flags); | ||||
| 	} | ||||
| 	__ClearPageWaiters(page); | ||||
| } | ||||
| @ -204,63 +202,46 @@ int get_kernel_page(unsigned long start, int write, struct page **pages) | ||||
| EXPORT_SYMBOL_GPL(get_kernel_page); | ||||
| 
 | ||||
| static void pagevec_lru_move_fn(struct pagevec *pvec, | ||||
| 	void (*move_fn)(struct page *page, struct lruvec *lruvec, void *arg), | ||||
| 	void *arg) | ||||
| 	void (*move_fn)(struct page *page, struct lruvec *lruvec)) | ||||
| { | ||||
| 	int i; | ||||
| 	struct pglist_data *pgdat = NULL; | ||||
| 	struct lruvec *lruvec; | ||||
| 	struct lruvec *lruvec = NULL; | ||||
| 	unsigned long flags = 0; | ||||
| 
 | ||||
| 	for (i = 0; i < pagevec_count(pvec); i++) { | ||||
| 		struct page *page = pvec->pages[i]; | ||||
| 		struct pglist_data *pagepgdat = page_pgdat(page); | ||||
| 
 | ||||
| 		if (pagepgdat != pgdat) { | ||||
| 			if (pgdat) | ||||
| 				spin_unlock_irqrestore(&pgdat->lru_lock, flags); | ||||
| 			pgdat = pagepgdat; | ||||
| 			spin_lock_irqsave(&pgdat->lru_lock, flags); | ||||
| 		} | ||||
| 		/* block memcg migration during page moving between lru */ | ||||
| 		if (!TestClearPageLRU(page)) | ||||
| 			continue; | ||||
| 
 | ||||
| 		lruvec = mem_cgroup_page_lruvec(page, pgdat); | ||||
| 		(*move_fn)(page, lruvec, arg); | ||||
| 		lruvec = relock_page_lruvec_irqsave(page, lruvec, &flags); | ||||
| 		(*move_fn)(page, lruvec); | ||||
| 
 | ||||
| 		SetPageLRU(page); | ||||
| 	} | ||||
| 	if (pgdat) | ||||
| 		spin_unlock_irqrestore(&pgdat->lru_lock, flags); | ||||
| 	if (lruvec) | ||||
| 		unlock_page_lruvec_irqrestore(lruvec, flags); | ||||
| 	release_pages(pvec->pages, pvec->nr); | ||||
| 	pagevec_reinit(pvec); | ||||
| } | ||||
| 
 | ||||
| static void pagevec_move_tail_fn(struct page *page, struct lruvec *lruvec, | ||||
| 				 void *arg) | ||||
| static void pagevec_move_tail_fn(struct page *page, struct lruvec *lruvec) | ||||
| { | ||||
| 	int *pgmoved = arg; | ||||
| 
 | ||||
| 	if (PageLRU(page) && !PageUnevictable(page)) { | ||||
| 	if (!PageUnevictable(page)) { | ||||
| 		del_page_from_lru_list(page, lruvec, page_lru(page)); | ||||
| 		ClearPageActive(page); | ||||
| 		add_page_to_lru_list_tail(page, lruvec, page_lru(page)); | ||||
| 		(*pgmoved) += thp_nr_pages(page); | ||||
| 		__count_vm_events(PGROTATED, thp_nr_pages(page)); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * pagevec_move_tail() must be called with IRQ disabled. | ||||
|  * Otherwise this may cause nasty races. | ||||
|  */ | ||||
| static void pagevec_move_tail(struct pagevec *pvec) | ||||
| { | ||||
| 	int pgmoved = 0; | ||||
| 
 | ||||
| 	pagevec_lru_move_fn(pvec, pagevec_move_tail_fn, &pgmoved); | ||||
| 	__count_vm_events(PGROTATED, pgmoved); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Writeback is about to end against a page which has been marked for immediate | ||||
|  * reclaim.  If it still appears to be reclaimable, move it to the tail of the | ||||
|  * inactive list. | ||||
|  * | ||||
|  * rotate_reclaimable_page() must disable IRQs, to prevent nasty races. | ||||
|  */ | ||||
| void rotate_reclaimable_page(struct page *page) | ||||
| { | ||||
| @ -273,7 +254,7 @@ void rotate_reclaimable_page(struct page *page) | ||||
| 		local_lock_irqsave(&lru_rotate.lock, flags); | ||||
| 		pvec = this_cpu_ptr(&lru_rotate.pvec); | ||||
| 		if (!pagevec_add(pvec, page) || PageCompound(page)) | ||||
| 			pagevec_move_tail(pvec); | ||||
| 			pagevec_lru_move_fn(pvec, pagevec_move_tail_fn); | ||||
| 		local_unlock_irqrestore(&lru_rotate.lock, flags); | ||||
| 	} | ||||
| } | ||||
| @ -283,6 +264,14 @@ void lru_note_cost(struct lruvec *lruvec, bool file, unsigned int nr_pages) | ||||
| 	do { | ||||
| 		unsigned long lrusize; | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * Hold lruvec->lru_lock is safe here, since | ||||
| 		 * 1) The pinned lruvec in reclaim, or | ||||
| 		 * 2) From a pre-LRU page during refault (which also holds the | ||||
| 		 *    rcu lock, so would be safe even if the page was on the LRU | ||||
| 		 *    and could move simultaneously to a new lruvec). | ||||
| 		 */ | ||||
| 		spin_lock_irq(&lruvec->lru_lock); | ||||
| 		/* Record cost event */ | ||||
| 		if (file) | ||||
| 			lruvec->file_cost += nr_pages; | ||||
| @ -306,6 +295,7 @@ void lru_note_cost(struct lruvec *lruvec, bool file, unsigned int nr_pages) | ||||
| 			lruvec->file_cost /= 2; | ||||
| 			lruvec->anon_cost /= 2; | ||||
| 		} | ||||
| 		spin_unlock_irq(&lruvec->lru_lock); | ||||
| 	} while ((lruvec = parent_lruvec(lruvec))); | ||||
| } | ||||
| 
 | ||||
| @ -315,10 +305,9 @@ void lru_note_cost_page(struct page *page) | ||||
| 		      page_is_file_lru(page), thp_nr_pages(page)); | ||||
| } | ||||
| 
 | ||||
| static void __activate_page(struct page *page, struct lruvec *lruvec, | ||||
| 			    void *arg) | ||||
| static void __activate_page(struct page *page, struct lruvec *lruvec) | ||||
| { | ||||
| 	if (PageLRU(page) && !PageActive(page) && !PageUnevictable(page)) { | ||||
| 	if (!PageActive(page) && !PageUnevictable(page)) { | ||||
| 		int lru = page_lru_base_type(page); | ||||
| 		int nr_pages = thp_nr_pages(page); | ||||
| 
 | ||||
| @ -340,7 +329,7 @@ static void activate_page_drain(int cpu) | ||||
| 	struct pagevec *pvec = &per_cpu(lru_pvecs.activate_page, cpu); | ||||
| 
 | ||||
| 	if (pagevec_count(pvec)) | ||||
| 		pagevec_lru_move_fn(pvec, __activate_page, NULL); | ||||
| 		pagevec_lru_move_fn(pvec, __activate_page); | ||||
| } | ||||
| 
 | ||||
| static bool need_activate_page_drain(int cpu) | ||||
| @ -358,7 +347,7 @@ static void activate_page(struct page *page) | ||||
| 		pvec = this_cpu_ptr(&lru_pvecs.activate_page); | ||||
| 		get_page(page); | ||||
| 		if (!pagevec_add(pvec, page) || PageCompound(page)) | ||||
| 			pagevec_lru_move_fn(pvec, __activate_page, NULL); | ||||
| 			pagevec_lru_move_fn(pvec, __activate_page); | ||||
| 		local_unlock(&lru_pvecs.lock); | ||||
| 	} | ||||
| } | ||||
| @ -370,12 +359,15 @@ static inline void activate_page_drain(int cpu) | ||||
| 
 | ||||
| static void activate_page(struct page *page) | ||||
| { | ||||
| 	pg_data_t *pgdat = page_pgdat(page); | ||||
| 	struct lruvec *lruvec; | ||||
| 
 | ||||
| 	page = compound_head(page); | ||||
| 	spin_lock_irq(&pgdat->lru_lock); | ||||
| 	__activate_page(page, mem_cgroup_page_lruvec(page, pgdat), NULL); | ||||
| 	spin_unlock_irq(&pgdat->lru_lock); | ||||
| 	if (TestClearPageLRU(page)) { | ||||
| 		lruvec = lock_page_lruvec_irq(page); | ||||
| 		__activate_page(page, lruvec); | ||||
| 		unlock_page_lruvec_irq(lruvec); | ||||
| 		SetPageLRU(page); | ||||
| 	} | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| @ -525,16 +517,12 @@ void lru_cache_add_inactive_or_unevictable(struct page *page, | ||||
|  * be write it out by flusher threads as this is much more effective | ||||
|  * than the single-page writeout from reclaim. | ||||
|  */ | ||||
| static void lru_deactivate_file_fn(struct page *page, struct lruvec *lruvec, | ||||
| 			      void *arg) | ||||
| static void lru_deactivate_file_fn(struct page *page, struct lruvec *lruvec) | ||||
| { | ||||
| 	int lru; | ||||
| 	bool active; | ||||
| 	int nr_pages = thp_nr_pages(page); | ||||
| 
 | ||||
| 	if (!PageLRU(page)) | ||||
| 		return; | ||||
| 
 | ||||
| 	if (PageUnevictable(page)) | ||||
| 		return; | ||||
| 
 | ||||
| @ -573,10 +561,9 @@ static void lru_deactivate_file_fn(struct page *page, struct lruvec *lruvec, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void lru_deactivate_fn(struct page *page, struct lruvec *lruvec, | ||||
| 			    void *arg) | ||||
| static void lru_deactivate_fn(struct page *page, struct lruvec *lruvec) | ||||
| { | ||||
| 	if (PageLRU(page) && PageActive(page) && !PageUnevictable(page)) { | ||||
| 	if (PageActive(page) && !PageUnevictable(page)) { | ||||
| 		int lru = page_lru_base_type(page); | ||||
| 		int nr_pages = thp_nr_pages(page); | ||||
| 
 | ||||
| @ -591,10 +578,9 @@ static void lru_deactivate_fn(struct page *page, struct lruvec *lruvec, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void lru_lazyfree_fn(struct page *page, struct lruvec *lruvec, | ||||
| 			    void *arg) | ||||
| static void lru_lazyfree_fn(struct page *page, struct lruvec *lruvec) | ||||
| { | ||||
| 	if (PageLRU(page) && PageAnon(page) && PageSwapBacked(page) && | ||||
| 	if (PageAnon(page) && PageSwapBacked(page) && | ||||
| 	    !PageSwapCache(page) && !PageUnevictable(page)) { | ||||
| 		bool active = PageActive(page); | ||||
| 		int nr_pages = thp_nr_pages(page); | ||||
| @ -636,21 +622,21 @@ void lru_add_drain_cpu(int cpu) | ||||
| 
 | ||||
| 		/* No harm done if a racing interrupt already did this */ | ||||
| 		local_lock_irqsave(&lru_rotate.lock, flags); | ||||
| 		pagevec_move_tail(pvec); | ||||
| 		pagevec_lru_move_fn(pvec, pagevec_move_tail_fn); | ||||
| 		local_unlock_irqrestore(&lru_rotate.lock, flags); | ||||
| 	} | ||||
| 
 | ||||
| 	pvec = &per_cpu(lru_pvecs.lru_deactivate_file, cpu); | ||||
| 	if (pagevec_count(pvec)) | ||||
| 		pagevec_lru_move_fn(pvec, lru_deactivate_file_fn, NULL); | ||||
| 		pagevec_lru_move_fn(pvec, lru_deactivate_file_fn); | ||||
| 
 | ||||
| 	pvec = &per_cpu(lru_pvecs.lru_deactivate, cpu); | ||||
| 	if (pagevec_count(pvec)) | ||||
| 		pagevec_lru_move_fn(pvec, lru_deactivate_fn, NULL); | ||||
| 		pagevec_lru_move_fn(pvec, lru_deactivate_fn); | ||||
| 
 | ||||
| 	pvec = &per_cpu(lru_pvecs.lru_lazyfree, cpu); | ||||
| 	if (pagevec_count(pvec)) | ||||
| 		pagevec_lru_move_fn(pvec, lru_lazyfree_fn, NULL); | ||||
| 		pagevec_lru_move_fn(pvec, lru_lazyfree_fn); | ||||
| 
 | ||||
| 	activate_page_drain(cpu); | ||||
| } | ||||
| @ -679,7 +665,7 @@ void deactivate_file_page(struct page *page) | ||||
| 		pvec = this_cpu_ptr(&lru_pvecs.lru_deactivate_file); | ||||
| 
 | ||||
| 		if (!pagevec_add(pvec, page) || PageCompound(page)) | ||||
| 			pagevec_lru_move_fn(pvec, lru_deactivate_file_fn, NULL); | ||||
| 			pagevec_lru_move_fn(pvec, lru_deactivate_file_fn); | ||||
| 		local_unlock(&lru_pvecs.lock); | ||||
| 	} | ||||
| } | ||||
| @ -701,7 +687,7 @@ void deactivate_page(struct page *page) | ||||
| 		pvec = this_cpu_ptr(&lru_pvecs.lru_deactivate); | ||||
| 		get_page(page); | ||||
| 		if (!pagevec_add(pvec, page) || PageCompound(page)) | ||||
| 			pagevec_lru_move_fn(pvec, lru_deactivate_fn, NULL); | ||||
| 			pagevec_lru_move_fn(pvec, lru_deactivate_fn); | ||||
| 		local_unlock(&lru_pvecs.lock); | ||||
| 	} | ||||
| } | ||||
| @ -723,7 +709,7 @@ void mark_page_lazyfree(struct page *page) | ||||
| 		pvec = this_cpu_ptr(&lru_pvecs.lru_lazyfree); | ||||
| 		get_page(page); | ||||
| 		if (!pagevec_add(pvec, page) || PageCompound(page)) | ||||
| 			pagevec_lru_move_fn(pvec, lru_lazyfree_fn, NULL); | ||||
| 			pagevec_lru_move_fn(pvec, lru_lazyfree_fn); | ||||
| 		local_unlock(&lru_pvecs.lock); | ||||
| 	} | ||||
| } | ||||
| @ -871,8 +857,7 @@ void release_pages(struct page **pages, int nr) | ||||
| { | ||||
| 	int i; | ||||
| 	LIST_HEAD(pages_to_free); | ||||
| 	struct pglist_data *locked_pgdat = NULL; | ||||
| 	struct lruvec *lruvec; | ||||
| 	struct lruvec *lruvec = NULL; | ||||
| 	unsigned long flags; | ||||
| 	unsigned int lock_batch; | ||||
| 
 | ||||
| @ -882,11 +867,11 @@ void release_pages(struct page **pages, int nr) | ||||
| 		/*
 | ||||
| 		 * Make sure the IRQ-safe lock-holding time does not get | ||||
| 		 * excessive with a continuous string of pages from the | ||||
| 		 * same pgdat. The lock is held only if pgdat != NULL. | ||||
| 		 * same lruvec. The lock is held only if lruvec != NULL. | ||||
| 		 */ | ||||
| 		if (locked_pgdat && ++lock_batch == SWAP_CLUSTER_MAX) { | ||||
| 			spin_unlock_irqrestore(&locked_pgdat->lru_lock, flags); | ||||
| 			locked_pgdat = NULL; | ||||
| 		if (lruvec && ++lock_batch == SWAP_CLUSTER_MAX) { | ||||
| 			unlock_page_lruvec_irqrestore(lruvec, flags); | ||||
| 			lruvec = NULL; | ||||
| 		} | ||||
| 
 | ||||
| 		page = compound_head(page); | ||||
| @ -894,10 +879,9 @@ void release_pages(struct page **pages, int nr) | ||||
| 			continue; | ||||
| 
 | ||||
| 		if (is_zone_device_page(page)) { | ||||
| 			if (locked_pgdat) { | ||||
| 				spin_unlock_irqrestore(&locked_pgdat->lru_lock, | ||||
| 						       flags); | ||||
| 				locked_pgdat = NULL; | ||||
| 			if (lruvec) { | ||||
| 				unlock_page_lruvec_irqrestore(lruvec, flags); | ||||
| 				lruvec = NULL; | ||||
| 			} | ||||
| 			/*
 | ||||
| 			 * ZONE_DEVICE pages that return 'false' from | ||||
| @ -918,27 +902,22 @@ void release_pages(struct page **pages, int nr) | ||||
| 			continue; | ||||
| 
 | ||||
| 		if (PageCompound(page)) { | ||||
| 			if (locked_pgdat) { | ||||
| 				spin_unlock_irqrestore(&locked_pgdat->lru_lock, flags); | ||||
| 				locked_pgdat = NULL; | ||||
| 			if (lruvec) { | ||||
| 				unlock_page_lruvec_irqrestore(lruvec, flags); | ||||
| 				lruvec = NULL; | ||||
| 			} | ||||
| 			__put_compound_page(page); | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		if (PageLRU(page)) { | ||||
| 			struct pglist_data *pgdat = page_pgdat(page); | ||||
| 			struct lruvec *prev_lruvec = lruvec; | ||||
| 
 | ||||
| 			if (pgdat != locked_pgdat) { | ||||
| 				if (locked_pgdat) | ||||
| 					spin_unlock_irqrestore(&locked_pgdat->lru_lock, | ||||
| 									flags); | ||||
| 			lruvec = relock_page_lruvec_irqsave(page, lruvec, | ||||
| 									&flags); | ||||
| 			if (prev_lruvec != lruvec) | ||||
| 				lock_batch = 0; | ||||
| 				locked_pgdat = pgdat; | ||||
| 				spin_lock_irqsave(&locked_pgdat->lru_lock, flags); | ||||
| 			} | ||||
| 
 | ||||
| 			lruvec = mem_cgroup_page_lruvec(page, locked_pgdat); | ||||
| 			VM_BUG_ON_PAGE(!PageLRU(page), page); | ||||
| 			__ClearPageLRU(page); | ||||
| 			del_page_from_lru_list(page, lruvec, page_off_lru(page)); | ||||
| @ -948,8 +927,8 @@ void release_pages(struct page **pages, int nr) | ||||
| 
 | ||||
| 		list_add(&page->lru, &pages_to_free); | ||||
| 	} | ||||
| 	if (locked_pgdat) | ||||
| 		spin_unlock_irqrestore(&locked_pgdat->lru_lock, flags); | ||||
| 	if (lruvec) | ||||
| 		unlock_page_lruvec_irqrestore(lruvec, flags); | ||||
| 
 | ||||
| 	mem_cgroup_uncharge_list(&pages_to_free); | ||||
| 	free_unref_page_list(&pages_to_free); | ||||
| @ -977,41 +956,7 @@ void __pagevec_release(struct pagevec *pvec) | ||||
| } | ||||
| EXPORT_SYMBOL(__pagevec_release); | ||||
| 
 | ||||
| #ifdef CONFIG_TRANSPARENT_HUGEPAGE | ||||
| /* used by __split_huge_page_refcount() */ | ||||
| void lru_add_page_tail(struct page *page, struct page *page_tail, | ||||
| 		       struct lruvec *lruvec, struct list_head *list) | ||||
| { | ||||
| 	VM_BUG_ON_PAGE(!PageHead(page), page); | ||||
| 	VM_BUG_ON_PAGE(PageCompound(page_tail), page); | ||||
| 	VM_BUG_ON_PAGE(PageLRU(page_tail), page); | ||||
| 	lockdep_assert_held(&lruvec_pgdat(lruvec)->lru_lock); | ||||
| 
 | ||||
| 	if (!list) | ||||
| 		SetPageLRU(page_tail); | ||||
| 
 | ||||
| 	if (likely(PageLRU(page))) | ||||
| 		list_add_tail(&page_tail->lru, &page->lru); | ||||
| 	else if (list) { | ||||
| 		/* page reclaim is reclaiming a huge page */ | ||||
| 		get_page(page_tail); | ||||
| 		list_add_tail(&page_tail->lru, list); | ||||
| 	} else { | ||||
| 		/*
 | ||||
| 		 * Head page has not yet been counted, as an hpage, | ||||
| 		 * so we must account for each subpage individually. | ||||
| 		 * | ||||
| 		 * Put page_tail on the list at the correct position | ||||
| 		 * so they all end up in order. | ||||
| 		 */ | ||||
| 		add_page_to_lru_list_tail(page_tail, lruvec, | ||||
| 					  page_lru(page_tail)); | ||||
| 	} | ||||
| } | ||||
| #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ | ||||
| 
 | ||||
| static void __pagevec_lru_add_fn(struct page *page, struct lruvec *lruvec, | ||||
| 				 void *arg) | ||||
| static void __pagevec_lru_add_fn(struct page *page, struct lruvec *lruvec) | ||||
| { | ||||
| 	enum lru_list lru; | ||||
| 	int was_unevictable = TestClearPageUnevictable(page); | ||||
| @ -1070,7 +1015,20 @@ static void __pagevec_lru_add_fn(struct page *page, struct lruvec *lruvec, | ||||
|  */ | ||||
| void __pagevec_lru_add(struct pagevec *pvec) | ||||
| { | ||||
| 	pagevec_lru_move_fn(pvec, __pagevec_lru_add_fn, NULL); | ||||
| 	int i; | ||||
| 	struct lruvec *lruvec = NULL; | ||||
| 	unsigned long flags = 0; | ||||
| 
 | ||||
| 	for (i = 0; i < pagevec_count(pvec); i++) { | ||||
| 		struct page *page = pvec->pages[i]; | ||||
| 
 | ||||
| 		lruvec = relock_page_lruvec_irqsave(page, lruvec, &flags); | ||||
| 		__pagevec_lru_add_fn(page, lruvec); | ||||
| 	} | ||||
| 	if (lruvec) | ||||
| 		unlock_page_lruvec_irqrestore(lruvec, flags); | ||||
| 	release_pages(pvec->pages, pvec->nr); | ||||
| 	pagevec_reinit(pvec); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  | ||||
							
								
								
									
										207
									
								
								mm/vmscan.c
									
									
									
									
									
								
							
							
						
						
									
										207
									
								
								mm/vmscan.c
									
									
									
									
									
								
							| @ -1539,9 +1539,9 @@ unsigned int reclaim_clean_pages_from_list(struct zone *zone, | ||||
|  * | ||||
|  * returns 0 on success, -ve errno on failure. | ||||
|  */ | ||||
| int __isolate_lru_page(struct page *page, isolate_mode_t mode) | ||||
| int __isolate_lru_page_prepare(struct page *page, isolate_mode_t mode) | ||||
| { | ||||
| 	int ret = -EINVAL; | ||||
| 	int ret = -EBUSY; | ||||
| 
 | ||||
| 	/* Only take pages on the LRU. */ | ||||
| 	if (!PageLRU(page)) | ||||
| @ -1551,8 +1551,6 @@ int __isolate_lru_page(struct page *page, isolate_mode_t mode) | ||||
| 	if (PageUnevictable(page) && !(mode & ISOLATE_UNEVICTABLE)) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	ret = -EBUSY; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * To minimise LRU disruption, the caller can indicate that it only | ||||
| 	 * wants to isolate pages it will be able to operate on without | ||||
| @ -1593,20 +1591,9 @@ int __isolate_lru_page(struct page *page, isolate_mode_t mode) | ||||
| 	if ((mode & ISOLATE_UNMAPPED) && page_mapped(page)) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	if (likely(get_page_unless_zero(page))) { | ||||
| 		/*
 | ||||
| 		 * Be careful not to clear PageLRU until after we're | ||||
| 		 * sure the page is not being freed elsewhere -- the | ||||
| 		 * page release code relies on it. | ||||
| 		 */ | ||||
| 		ClearPageLRU(page); | ||||
| 		ret = 0; | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * Update LRU sizes after isolating pages. The LRU size updates must | ||||
|  * be complete before mem_cgroup_update_lru_size due to a sanity check. | ||||
| @ -1626,14 +1613,16 @@ static __always_inline void update_lru_sizes(struct lruvec *lruvec, | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * pgdat->lru_lock is heavily contended.  Some of the functions that | ||||
|  * Isolating page from the lruvec to fill in @dst list by nr_to_scan times. | ||||
|  * | ||||
|  * lruvec->lru_lock is heavily contended.  Some of the functions that | ||||
|  * shrink the lists perform better by taking out a batch of pages | ||||
|  * and working on them outside the LRU lock. | ||||
|  * | ||||
|  * For pagecache intensive workloads, this function is the hottest | ||||
|  * spot in the kernel (apart from copy_*_user functions). | ||||
|  * | ||||
|  * Appropriate locks must be held before calling this function. | ||||
|  * Lru_lock must be held before calling this function. | ||||
|  * | ||||
|  * @nr_to_scan:	The number of eligible pages to look through on the list. | ||||
|  * @lruvec:	The LRU vector to pull pages from. | ||||
| @ -1666,8 +1655,6 @@ static unsigned long isolate_lru_pages(unsigned long nr_to_scan, | ||||
| 		page = lru_to_page(src); | ||||
| 		prefetchw_prev_lru_page(page, src, flags); | ||||
| 
 | ||||
| 		VM_BUG_ON_PAGE(!PageLRU(page), page); | ||||
| 
 | ||||
| 		nr_pages = compound_nr(page); | ||||
| 		total_scan += nr_pages; | ||||
| 
 | ||||
| @ -1688,20 +1675,34 @@ static unsigned long isolate_lru_pages(unsigned long nr_to_scan, | ||||
| 		 * only when the page is being freed somewhere else. | ||||
| 		 */ | ||||
| 		scan += nr_pages; | ||||
| 		switch (__isolate_lru_page(page, mode)) { | ||||
| 		switch (__isolate_lru_page_prepare(page, mode)) { | ||||
| 		case 0: | ||||
| 			/*
 | ||||
| 			 * Be careful not to clear PageLRU until after we're | ||||
| 			 * sure the page is not being freed elsewhere -- the | ||||
| 			 * page release code relies on it. | ||||
| 			 */ | ||||
| 			if (unlikely(!get_page_unless_zero(page))) | ||||
| 				goto busy; | ||||
| 
 | ||||
| 			if (!TestClearPageLRU(page)) { | ||||
| 				/*
 | ||||
| 				 * This page may in other isolation path, | ||||
| 				 * but we still hold lru_lock. | ||||
| 				 */ | ||||
| 				put_page(page); | ||||
| 				goto busy; | ||||
| 			} | ||||
| 
 | ||||
| 			nr_taken += nr_pages; | ||||
| 			nr_zone_taken[page_zonenum(page)] += nr_pages; | ||||
| 			list_move(&page->lru, dst); | ||||
| 			break; | ||||
| 
 | ||||
| 		case -EBUSY: | ||||
| 		default: | ||||
| busy: | ||||
| 			/* else it is being freed elsewhere */ | ||||
| 			list_move(&page->lru, src); | ||||
| 			continue; | ||||
| 
 | ||||
| 		default: | ||||
| 			BUG(); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| @ -1764,21 +1765,16 @@ int isolate_lru_page(struct page *page) | ||||
| 	VM_BUG_ON_PAGE(!page_count(page), page); | ||||
| 	WARN_RATELIMIT(PageTail(page), "trying to isolate tail page"); | ||||
| 
 | ||||
| 	if (PageLRU(page)) { | ||||
| 		pg_data_t *pgdat = page_pgdat(page); | ||||
| 	if (TestClearPageLRU(page)) { | ||||
| 		struct lruvec *lruvec; | ||||
| 
 | ||||
| 		spin_lock_irq(&pgdat->lru_lock); | ||||
| 		lruvec = mem_cgroup_page_lruvec(page, pgdat); | ||||
| 		if (PageLRU(page)) { | ||||
| 			int lru = page_lru(page); | ||||
| 			get_page(page); | ||||
| 			ClearPageLRU(page); | ||||
| 			del_page_from_lru_list(page, lruvec, lru); | ||||
| 			ret = 0; | ||||
| 		} | ||||
| 		spin_unlock_irq(&pgdat->lru_lock); | ||||
| 		get_page(page); | ||||
| 		lruvec = lock_page_lruvec_irq(page); | ||||
| 		del_page_from_lru_list(page, lruvec, page_lru(page)); | ||||
| 		unlock_page_lruvec_irq(lruvec); | ||||
| 		ret = 0; | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| @ -1820,29 +1816,14 @@ static int too_many_isolated(struct pglist_data *pgdat, int file, | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * This moves pages from @list to corresponding LRU list. | ||||
|  * | ||||
|  * We move them the other way if the page is referenced by one or more | ||||
|  * processes, from rmap. | ||||
|  * | ||||
|  * If the pages are mostly unmapped, the processing is fast and it is | ||||
|  * appropriate to hold zone_lru_lock across the whole operation.  But if | ||||
|  * the pages are mapped, the processing is slow (page_referenced()) so we | ||||
|  * should drop zone_lru_lock around each page.  It's impossible to balance | ||||
|  * this, so instead we remove the pages from the LRU while processing them. | ||||
|  * It is safe to rely on PG_active against the non-LRU pages in here because | ||||
|  * nobody will play with that bit on a non-LRU page. | ||||
|  * | ||||
|  * The downside is that we have to touch page->_refcount against each page. | ||||
|  * But we had to alter page->flags anyway. | ||||
|  * move_pages_to_lru() moves pages from private @list to appropriate LRU list. | ||||
|  * On return, @list is reused as a list of pages to be freed by the caller. | ||||
|  * | ||||
|  * Returns the number of pages moved to the given lruvec. | ||||
|  */ | ||||
| 
 | ||||
| static unsigned noinline_for_stack move_pages_to_lru(struct lruvec *lruvec, | ||||
| 						     struct list_head *list) | ||||
| { | ||||
| 	struct pglist_data *pgdat = lruvec_pgdat(lruvec); | ||||
| 	int nr_pages, nr_moved = 0; | ||||
| 	LIST_HEAD(pages_to_free); | ||||
| 	struct page *page; | ||||
| @ -1851,38 +1832,54 @@ static unsigned noinline_for_stack move_pages_to_lru(struct lruvec *lruvec, | ||||
| 	while (!list_empty(list)) { | ||||
| 		page = lru_to_page(list); | ||||
| 		VM_BUG_ON_PAGE(PageLRU(page), page); | ||||
| 		list_del(&page->lru); | ||||
| 		if (unlikely(!page_evictable(page))) { | ||||
| 			list_del(&page->lru); | ||||
| 			spin_unlock_irq(&pgdat->lru_lock); | ||||
| 			spin_unlock_irq(&lruvec->lru_lock); | ||||
| 			putback_lru_page(page); | ||||
| 			spin_lock_irq(&pgdat->lru_lock); | ||||
| 			spin_lock_irq(&lruvec->lru_lock); | ||||
| 			continue; | ||||
| 		} | ||||
| 		lruvec = mem_cgroup_page_lruvec(page, pgdat); | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * The SetPageLRU needs to be kept here for list integrity. | ||||
| 		 * Otherwise: | ||||
| 		 *   #0 move_pages_to_lru             #1 release_pages | ||||
| 		 *   if !put_page_testzero | ||||
| 		 *				      if (put_page_testzero()) | ||||
| 		 *				        !PageLRU //skip lru_lock
 | ||||
| 		 *     SetPageLRU() | ||||
| 		 *     list_add(&page->lru,) | ||||
| 		 *                                        list_add(&page->lru,) | ||||
| 		 */ | ||||
| 		SetPageLRU(page); | ||||
| 		lru = page_lru(page); | ||||
| 
 | ||||
| 		nr_pages = thp_nr_pages(page); | ||||
| 		update_lru_size(lruvec, lru, page_zonenum(page), nr_pages); | ||||
| 		list_move(&page->lru, &lruvec->lists[lru]); | ||||
| 
 | ||||
| 		if (put_page_testzero(page)) { | ||||
| 		if (unlikely(put_page_testzero(page))) { | ||||
| 			__ClearPageLRU(page); | ||||
| 			__ClearPageActive(page); | ||||
| 			del_page_from_lru_list(page, lruvec, lru); | ||||
| 
 | ||||
| 			if (unlikely(PageCompound(page))) { | ||||
| 				spin_unlock_irq(&pgdat->lru_lock); | ||||
| 				spin_unlock_irq(&lruvec->lru_lock); | ||||
| 				destroy_compound_page(page); | ||||
| 				spin_lock_irq(&pgdat->lru_lock); | ||||
| 				spin_lock_irq(&lruvec->lru_lock); | ||||
| 			} else | ||||
| 				list_add(&page->lru, &pages_to_free); | ||||
| 		} else { | ||||
| 			nr_moved += nr_pages; | ||||
| 			if (PageActive(page)) | ||||
| 				workingset_age_nonresident(lruvec, nr_pages); | ||||
| 
 | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * All pages were isolated from the same lruvec (and isolation | ||||
| 		 * inhibits memcg migration). | ||||
| 		 */ | ||||
| 		VM_BUG_ON_PAGE(!lruvec_holds_page_lru_lock(page, lruvec), page); | ||||
| 		lru = page_lru(page); | ||||
| 		nr_pages = thp_nr_pages(page); | ||||
| 
 | ||||
| 		update_lru_size(lruvec, lru, page_zonenum(page), nr_pages); | ||||
| 		list_add(&page->lru, &lruvec->lists[lru]); | ||||
| 		nr_moved += nr_pages; | ||||
| 		if (PageActive(page)) | ||||
| 			workingset_age_nonresident(lruvec, nr_pages); | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| @ -1939,7 +1936,7 @@ shrink_inactive_list(unsigned long nr_to_scan, struct lruvec *lruvec, | ||||
| 
 | ||||
| 	lru_add_drain(); | ||||
| 
 | ||||
| 	spin_lock_irq(&pgdat->lru_lock); | ||||
| 	spin_lock_irq(&lruvec->lru_lock); | ||||
| 
 | ||||
| 	nr_taken = isolate_lru_pages(nr_to_scan, lruvec, &page_list, | ||||
| 				     &nr_scanned, sc, lru); | ||||
| @ -1951,27 +1948,25 @@ shrink_inactive_list(unsigned long nr_to_scan, struct lruvec *lruvec, | ||||
| 	__count_memcg_events(lruvec_memcg(lruvec), item, nr_scanned); | ||||
| 	__count_vm_events(PGSCAN_ANON + file, nr_scanned); | ||||
| 
 | ||||
| 	spin_unlock_irq(&pgdat->lru_lock); | ||||
| 	spin_unlock_irq(&lruvec->lru_lock); | ||||
| 
 | ||||
| 	if (nr_taken == 0) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	nr_reclaimed = shrink_page_list(&page_list, pgdat, sc, &stat, false); | ||||
| 
 | ||||
| 	spin_lock_irq(&pgdat->lru_lock); | ||||
| 
 | ||||
| 	spin_lock_irq(&lruvec->lru_lock); | ||||
| 	move_pages_to_lru(lruvec, &page_list); | ||||
| 
 | ||||
| 	__mod_node_page_state(pgdat, NR_ISOLATED_ANON + file, -nr_taken); | ||||
| 	lru_note_cost(lruvec, file, stat.nr_pageout); | ||||
| 	item = current_is_kswapd() ? PGSTEAL_KSWAPD : PGSTEAL_DIRECT; | ||||
| 	if (!cgroup_reclaim(sc)) | ||||
| 		__count_vm_events(item, nr_reclaimed); | ||||
| 	__count_memcg_events(lruvec_memcg(lruvec), item, nr_reclaimed); | ||||
| 	__count_vm_events(PGSTEAL_ANON + file, nr_reclaimed); | ||||
| 	spin_unlock_irq(&lruvec->lru_lock); | ||||
| 
 | ||||
| 	spin_unlock_irq(&pgdat->lru_lock); | ||||
| 
 | ||||
| 	lru_note_cost(lruvec, file, stat.nr_pageout); | ||||
| 	mem_cgroup_uncharge_list(&page_list); | ||||
| 	free_unref_page_list(&page_list); | ||||
| 
 | ||||
| @ -2003,6 +1998,23 @@ shrink_inactive_list(unsigned long nr_to_scan, struct lruvec *lruvec, | ||||
| 	return nr_reclaimed; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * shrink_active_list() moves pages from the active LRU to the inactive LRU. | ||||
|  * | ||||
|  * We move them the other way if the page is referenced by one or more | ||||
|  * processes. | ||||
|  * | ||||
|  * If the pages are mostly unmapped, the processing is fast and it is | ||||
|  * appropriate to hold lru_lock across the whole operation.  But if | ||||
|  * the pages are mapped, the processing is slow (page_referenced()), so | ||||
|  * we should drop lru_lock around each page.  It's impossible to balance | ||||
|  * this, so instead we remove the pages from the LRU while processing them. | ||||
|  * It is safe to rely on PG_active against the non-LRU pages in here because | ||||
|  * nobody will play with that bit on a non-LRU page. | ||||
|  * | ||||
|  * The downside is that we have to touch page->_refcount against each page. | ||||
|  * But we had to alter page->flags anyway. | ||||
|  */ | ||||
| static void shrink_active_list(unsigned long nr_to_scan, | ||||
| 			       struct lruvec *lruvec, | ||||
| 			       struct scan_control *sc, | ||||
| @ -2022,7 +2034,7 @@ static void shrink_active_list(unsigned long nr_to_scan, | ||||
| 
 | ||||
| 	lru_add_drain(); | ||||
| 
 | ||||
| 	spin_lock_irq(&pgdat->lru_lock); | ||||
| 	spin_lock_irq(&lruvec->lru_lock); | ||||
| 
 | ||||
| 	nr_taken = isolate_lru_pages(nr_to_scan, lruvec, &l_hold, | ||||
| 				     &nr_scanned, sc, lru); | ||||
| @ -2033,7 +2045,7 @@ static void shrink_active_list(unsigned long nr_to_scan, | ||||
| 		__count_vm_events(PGREFILL, nr_scanned); | ||||
| 	__count_memcg_events(lruvec_memcg(lruvec), PGREFILL, nr_scanned); | ||||
| 
 | ||||
| 	spin_unlock_irq(&pgdat->lru_lock); | ||||
| 	spin_unlock_irq(&lruvec->lru_lock); | ||||
| 
 | ||||
| 	while (!list_empty(&l_hold)) { | ||||
| 		cond_resched(); | ||||
| @ -2079,7 +2091,7 @@ static void shrink_active_list(unsigned long nr_to_scan, | ||||
| 	/*
 | ||||
| 	 * Move pages back to the lru list. | ||||
| 	 */ | ||||
| 	spin_lock_irq(&pgdat->lru_lock); | ||||
| 	spin_lock_irq(&lruvec->lru_lock); | ||||
| 
 | ||||
| 	nr_activate = move_pages_to_lru(lruvec, &l_active); | ||||
| 	nr_deactivate = move_pages_to_lru(lruvec, &l_inactive); | ||||
| @ -2090,7 +2102,7 @@ static void shrink_active_list(unsigned long nr_to_scan, | ||||
| 	__count_memcg_events(lruvec_memcg(lruvec), PGDEACTIVATE, nr_deactivate); | ||||
| 
 | ||||
| 	__mod_node_page_state(pgdat, NR_ISOLATED_ANON + file, -nr_taken); | ||||
| 	spin_unlock_irq(&pgdat->lru_lock); | ||||
| 	spin_unlock_irq(&lruvec->lru_lock); | ||||
| 
 | ||||
| 	mem_cgroup_uncharge_list(&l_active); | ||||
| 	free_unref_page_list(&l_active); | ||||
| @ -2678,10 +2690,10 @@ again: | ||||
| 	/*
 | ||||
| 	 * Determine the scan balance between anon and file LRUs. | ||||
| 	 */ | ||||
| 	spin_lock_irq(&pgdat->lru_lock); | ||||
| 	spin_lock_irq(&target_lruvec->lru_lock); | ||||
| 	sc->anon_cost = target_lruvec->anon_cost; | ||||
| 	sc->file_cost = target_lruvec->file_cost; | ||||
| 	spin_unlock_irq(&pgdat->lru_lock); | ||||
| 	spin_unlock_irq(&target_lruvec->lru_lock); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Target desirable inactive:active list ratios for the anon | ||||
| @ -4257,15 +4269,13 @@ int node_reclaim(struct pglist_data *pgdat, gfp_t gfp_mask, unsigned int order) | ||||
|  */ | ||||
| void check_move_unevictable_pages(struct pagevec *pvec) | ||||
| { | ||||
| 	struct lruvec *lruvec; | ||||
| 	struct pglist_data *pgdat = NULL; | ||||
| 	struct lruvec *lruvec = NULL; | ||||
| 	int pgscanned = 0; | ||||
| 	int pgrescued = 0; | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (i = 0; i < pvec->nr; i++) { | ||||
| 		struct page *page = pvec->pages[i]; | ||||
| 		struct pglist_data *pagepgdat = page_pgdat(page); | ||||
| 		int nr_pages; | ||||
| 
 | ||||
| 		if (PageTransTail(page)) | ||||
| @ -4274,18 +4284,12 @@ void check_move_unevictable_pages(struct pagevec *pvec) | ||||
| 		nr_pages = thp_nr_pages(page); | ||||
| 		pgscanned += nr_pages; | ||||
| 
 | ||||
| 		if (pagepgdat != pgdat) { | ||||
| 			if (pgdat) | ||||
| 				spin_unlock_irq(&pgdat->lru_lock); | ||||
| 			pgdat = pagepgdat; | ||||
| 			spin_lock_irq(&pgdat->lru_lock); | ||||
| 		} | ||||
| 		lruvec = mem_cgroup_page_lruvec(page, pgdat); | ||||
| 
 | ||||
| 		if (!PageLRU(page) || !PageUnevictable(page)) | ||||
| 		/* block memcg migration during page moving between lru */ | ||||
| 		if (!TestClearPageLRU(page)) | ||||
| 			continue; | ||||
| 
 | ||||
| 		if (page_evictable(page)) { | ||||
| 		lruvec = relock_page_lruvec_irq(page, lruvec); | ||||
| 		if (page_evictable(page) && PageUnevictable(page)) { | ||||
| 			enum lru_list lru = page_lru_base_type(page); | ||||
| 
 | ||||
| 			VM_BUG_ON_PAGE(PageActive(page), page); | ||||
| @ -4294,12 +4298,15 @@ void check_move_unevictable_pages(struct pagevec *pvec) | ||||
| 			add_page_to_lru_list(page, lruvec, lru); | ||||
| 			pgrescued += nr_pages; | ||||
| 		} | ||||
| 		SetPageLRU(page); | ||||
| 	} | ||||
| 
 | ||||
| 	if (pgdat) { | ||||
| 	if (lruvec) { | ||||
| 		__count_vm_events(UNEVICTABLE_PGRESCUED, pgrescued); | ||||
| 		__count_vm_events(UNEVICTABLE_PGSCANNED, pgscanned); | ||||
| 		spin_unlock_irq(&pgdat->lru_lock); | ||||
| 		unlock_page_lruvec_irq(lruvec); | ||||
| 	} else if (pgscanned) { | ||||
| 		count_vm_events(UNEVICTABLE_PGSCANNED, pgscanned); | ||||
| 	} | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(check_move_unevictable_pages); | ||||
|  | ||||
| @ -381,9 +381,7 @@ void workingset_refault(struct page *page, void *shadow) | ||||
| 	if (workingset) { | ||||
| 		SetPageWorkingset(page); | ||||
| 		/* XXX: Move to lru_cache_add() when it supports new vs putback */ | ||||
| 		spin_lock_irq(&page_pgdat(page)->lru_lock); | ||||
| 		lru_note_cost_page(page); | ||||
| 		spin_unlock_irq(&page_pgdat(page)->lru_lock); | ||||
| 		inc_lruvec_state(lruvec, WORKINGSET_RESTORE_BASE + file); | ||||
| 	} | ||||
| out: | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user