lkml.org 
[lkml]   [2002]   [Aug]   [11]   [last100]   RSS Feed
Views: [more markup]  [less markup]  [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
DateSun, 11 Aug 2002 00:40:08 -0700
FromAndrew Morton <>
Subject[patch 18/21] per-zone LRU locking

Now the LRUs are per-zone, make their lock per-zone as well.

In this patch the per-zone lock shares a cacheline with the zone's
buddy list lock, which is very bad.  Some groundwork is needed to fix
this well.

This change is expected to be a significant win on NUMA, where most
page allocation comes from the local node's zones.

For NUMA the `struct zone' itself should really be placed in that
node's memory, which is something the platform owners should look at. 
However the internode cache will help here.

Per-node kswapd would make heaps of sense too.




 include/linux/mm.h         |    2 
 include/linux/mmzone.h     |    1 
 include/linux/page-flags.h |    2 
 include/linux/swap.h       |    2 
 mm/filemap.c               |    3 -
 mm/memory.c                |    2 
 mm/page_alloc.c            |    3 -
 mm/rmap.c                  |    6 +-
 mm/swap.c                  |   99 +++++++++++++++++++++++++--------------------
 mm/vmscan.c                |   38 ++++++++---------
 10 files changed, 86 insertions(+), 72 deletions(-)
--- 2.5.31/mm/filemap.c~per-zone-lock	Sun Aug 11 00:20:35 2002
+++ 2.5.31-akpm/mm/filemap.c	Sun Aug 11 00:20:57 2002
@@ -59,7 +59,7 @@
  *        ->swap_device_lock		(exclusive_swap_page, others)
  *	    ->rmap_lock			(to/from swapcache)
  *            ->mapping->page_lock
- *		->pagemap_lru_lock	(zap_pte_range)
+ *		->zone->lru_lock	(zap_pte_range)
  *      ->inode_lock			(__mark_inode_dirty)
  *        ->sb_lock			(fs/fs-writeback.c)
  *
@@ -68,7 +68,6 @@
  *    ->mapping->page_lock		(try_to_unmap_one)
  *
  */
-spinlock_t _pagemap_lru_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
 
 /*
  * Remove a page from the page cache and free it. Caller has to make
--- 2.5.31/mm/memory.c~per-zone-lock	Sun Aug 11 00:20:35 2002
+++ 2.5.31-akpm/mm/memory.c	Sun Aug 11 00:20:35 2002
@@ -405,7 +405,7 @@ static void zap_pte_range(mmu_gather_t *
 			cached_rmap_lock(page, &rmap_lock, &last_lockno);
 			__page_remove_rmap(page, ptep);
 			/*
-			 * This will take pagemap_lru_lock.  Which nests inside
+			 * This will take zone->lru_lock.  Which nests inside
 			 * rmap_lock
 			 */
 			tlb_remove_page(tlb, &pvec, page);
--- 2.5.31/mm/rmap.c~per-zone-lock	Sun Aug 11 00:20:35 2002
+++ 2.5.31-akpm/mm/rmap.c	Sun Aug 11 00:20:44 2002
@@ -14,7 +14,7 @@
 /*
  * Locking:
  * - the page->pte.chain is protected by the PG_chainlock bit,
- *   which nests within the pagemap_lru_lock, then the
+ *   which nests within the zone->lru_lock, then the
  *   mm->page_table_lock, and then the page lock.
  * - because swapout locking is opposite to the locking order
  *   in the page fault path, the swapout path uses trylocks
@@ -317,7 +317,7 @@ void page_remove_rmap(struct page *page,
  * table entry mapping a page. Because locking order here is opposite
  * to the locking order used by the page fault path, we use trylocks.
  * Locking:
- *	pagemap_lru_lock		page_launder()
+ *	zone->lru_lock			page_launder()
  *	    page lock			page_launder(), trylock
  *		rmap_lock		page_launder()
  *		    mm->page_table_lock	try_to_unmap_one(), trylock
@@ -385,7 +385,7 @@ out_unlock:
  * @page: the page to get unmapped
  *
  * Tries to remove all the page table entries which are mapping this
- * page, used in the pageout path.  Caller must hold pagemap_lru_lock
+ * page, used in the pageout path.  Caller must hold zone->lru_lock
  * and the page lock.  Return values are:
  *
  * SWAP_SUCCESS	- we succeeded in removing all mappings
--- 2.5.31/mm/swap.c~per-zone-lock	Sun Aug 11 00:20:35 2002
+++ 2.5.31-akpm/mm/swap.c	Sun Aug 11 00:20:35 2002
@@ -26,26 +26,20 @@
 int page_cluster;
 
 /*
- * Move an inactive page to the active list.
+ * FIXME: speed this up?
  */
-static inline void activate_page_nolock(struct page * page)
+void activate_page(struct page *page)
 {
+	struct zone *zone = page_zone(page);
+
+	spin_lock_irq(&zone->lru_lock);
 	if (PageLRU(page) && !PageActive(page)) {
-		del_page_from_inactive_list(page);
+		del_page_from_inactive_list(zone, page);
 		SetPageActive(page);
-		add_page_to_active_list(page);
+		add_page_to_active_list(zone, page);
 		KERNEL_STAT_INC(pgactivate);
 	}
-}
-
-/*
- * FIXME: speed this up?
- */
-void activate_page(struct page * page)
-{
-	spin_lock_irq(&_pagemap_lru_lock);
-	activate_page_nolock(page);
-	spin_unlock_irq(&_pagemap_lru_lock);
+	spin_unlock_irq(&zone->lru_lock);
 }
 
 /**
@@ -80,13 +74,14 @@ void __page_cache_release(struct page *p
 {
 	BUG_ON(page_count(page) != 0);
 	if (PageLRU(page)) {
+		struct zone *zone = page_zone(page);
 		unsigned long flags;
 
-		spin_lock_irqsave(&_pagemap_lru_lock, flags);
+		spin_lock_irqsave(&zone->lru_lock, flags);
 		if (!TestClearPageLRU(page))
 			BUG();
-		del_page_from_lru(page);
-		spin_unlock_irqrestore(&_pagemap_lru_lock, flags);
+		del_page_from_lru(zone, page);
+		spin_unlock_irqrestore(&zone->lru_lock, flags);
 	}
 	__free_page(page);
 }
@@ -96,7 +91,7 @@ void __page_cache_release(struct page *p
  * pagevec's pages.  If it fell to zero then remove the page from the LRU and
  * free it.
  *
- * Avoid taking pagemap_lru_lock if possible, but if it is taken, retain it
+ * Avoid taking zone->lru_lock if possible, but if it is taken, retain it
  * for the remainder of the operation.
  *
  * The locking in this function is against shrink_cache(): we recheck the
@@ -108,28 +103,31 @@ void __page_cache_release(struct page *p
 void __pagevec_release(struct pagevec *pvec)
 {
 	int i;
-	int lock_held = 0;
 	struct pagevec pages_to_free;
+	struct zone *zone = NULL;
 
 	pagevec_init(&pages_to_free);
 	for (i = 0; i < pagevec_count(pvec); i++) {
 		struct page *page = pvec->pages[i];
+		struct zone *pagezone;
 
 		if (!put_page_testzero(page))
 			continue;
 
-		if (!lock_held) {
-			spin_lock_irq(&_pagemap_lru_lock);
-			lock_held = 1;
+		pagezone = page_zone(page);
+		if (pagezone != zone) {
+			if (zone)
+				spin_unlock_irq(&zone->lru_lock);
+			zone = pagezone;
+			spin_lock_irq(&zone->lru_lock);
 		}
-
 		if (TestClearPageLRU(page))
-			del_page_from_lru(page);
+			del_page_from_lru(zone, page);
 		if (page_count(page) == 0)
 			pagevec_add(&pages_to_free, page);
 	}
-	if (lock_held)
-		spin_unlock_irq(&_pagemap_lru_lock);
+	if (zone)
+		spin_unlock_irq(&zone->lru_lock);
 
 	pagevec_free(&pages_to_free);
 	pagevec_init(pvec);
@@ -164,26 +162,27 @@ void __pagevec_release_nonlru(struct pag
 void pagevec_deactivate_inactive(struct pagevec *pvec)
 {
 	int i;
-	int lock_held = 0;
+	struct zone *zone = NULL;
 
 	if (pagevec_count(pvec) == 0)
 		return;
 	for (i = 0; i < pagevec_count(pvec); i++) {
 		struct page *page = pvec->pages[i];
+		struct zone *pagezone = page_zone(page);
 
-		if (!lock_held) {
+		if (pagezone != zone) {
 			if (PageActive(page) || !PageLRU(page))
 				continue;
-			spin_lock_irq(&_pagemap_lru_lock);
-			lock_held = 1;
-		}
-		if (!PageActive(page) && PageLRU(page)) {
-			struct zone *zone = page_zone(page);
-			list_move(&page->lru, &zone->inactive_list);
+			if (zone)
+				spin_unlock_irq(&zone->lru_lock);
+			zone = pagezone;
+			spin_lock_irq(&zone->lru_lock);
 		}
+		if (!PageActive(page) && PageLRU(page))
+			list_move(&page->lru, &pagezone->inactive_list);
 	}
-	if (lock_held)
-		spin_unlock_irq(&_pagemap_lru_lock);
+	if (zone)
+		spin_unlock_irq(&zone->lru_lock);
 	__pagevec_release(pvec);
 }
 
@@ -194,16 +193,24 @@ void pagevec_deactivate_inactive(struct 
 void __pagevec_lru_add(struct pagevec *pvec)
 {
 	int i;
+	struct zone *zone = NULL;
 
-	spin_lock_irq(&_pagemap_lru_lock);
 	for (i = 0; i < pagevec_count(pvec); i++) {
 		struct page *page = pvec->pages[i];
+		struct zone *pagezone = page_zone(page);
 
+		if (pagezone != zone) {
+			if (zone)
+				spin_unlock_irq(&zone->lru_lock);
+			zone = pagezone;
+			spin_lock_irq(&zone->lru_lock);
+		}
 		if (TestSetPageLRU(page))
 			BUG();
-		add_page_to_inactive_list(page);
+		add_page_to_inactive_list(zone, page);
 	}
-	spin_unlock_irq(&_pagemap_lru_lock);
+	if (zone)
+		spin_unlock_irq(&zone->lru_lock);
 	pagevec_release(pvec);
 }
 
@@ -214,16 +221,24 @@ void __pagevec_lru_add(struct pagevec *p
 void __pagevec_lru_del(struct pagevec *pvec)
 {
 	int i;
+	struct zone *zone = NULL;
 
-	spin_lock_irq(&_pagemap_lru_lock);
 	for (i = 0; i < pagevec_count(pvec); i++) {
 		struct page *page = pvec->pages[i];
+		struct zone *pagezone = page_zone(page);
 
+		if (pagezone != zone) {
+			if (zone)
+				spin_unlock_irq(&zone->lru_lock);
+			zone = pagezone;
+			spin_lock_irq(&zone->lru_lock);
+		}
 		if (!TestClearPageLRU(page))
 			BUG();
-		del_page_from_lru(page);
+		del_page_from_lru(zone, page);
 	}
-	spin_unlock_irq(&_pagemap_lru_lock);
+	if (zone)
+		spin_unlock_irq(&zone->lru_lock);
 	pagevec_release(pvec);
 }
 
--- 2.5.31/mm/vmscan.c~per-zone-lock	Sun Aug 11 00:20:35 2002
+++ 2.5.31-akpm/mm/vmscan.c	Sun Aug 11 00:20:35 2002
@@ -264,7 +264,7 @@ keep:
 }
 
 /*
- * pagemap_lru_lock is heavily contented.  We relieve it by quickly privatising
+ * zone->lru_lock is heavily contented.  We relieve it by quickly privatising
  * a batch of pages and working on them outside the lock.  Any pages which were
  * not freed will be added back to the LRU.
  *
@@ -292,7 +292,7 @@ shrink_cache(int nr_pages, struct zone *
 	pagevec_init(&pvec);
 
 	lru_add_drain();
-	spin_lock_irq(&_pagemap_lru_lock);
+	spin_lock_irq(&zone->lru_lock);
 	while (max_scan > 0 && nr_pages > 0) {
 		struct page *page;
 		int n = 0;
@@ -318,7 +318,7 @@ shrink_cache(int nr_pages, struct zone *
 			n++;
 		}
 		zone->nr_inactive -= n;
-		spin_unlock_irq(&_pagemap_lru_lock);
+		spin_unlock_irq(&zone->lru_lock);
 
 		if (list_empty(&page_list))
 			goto done;
@@ -331,7 +331,7 @@ shrink_cache(int nr_pages, struct zone *
 		if (nr_pages <= 0 && list_empty(&page_list))
 			goto done;
 
-		spin_lock_irq(&_pagemap_lru_lock);
+		spin_lock_irq(&zone->lru_lock);
 		/*
 		 * Put back any unfreeable pages.
 		 */
@@ -341,17 +341,17 @@ shrink_cache(int nr_pages, struct zone *
 				BUG();
 			list_del(&page->lru);
 			if (PageActive(page))
-				add_page_to_active_list(page);
+				add_page_to_active_list(zone, page);
 			else
-				add_page_to_inactive_list(page);
+				add_page_to_inactive_list(zone, page);
 			if (!pagevec_add(&pvec, page)) {
-				spin_unlock_irq(&_pagemap_lru_lock);
+				spin_unlock_irq(&zone->lru_lock);
 				__pagevec_release(&pvec);
-				spin_lock_irq(&_pagemap_lru_lock);
+				spin_lock_irq(&zone->lru_lock);
 			}
 		}
   	}
-	spin_unlock_irq(&_pagemap_lru_lock);
+	spin_unlock_irq(&zone->lru_lock);
 done:
 	pagevec_release(&pvec);
 	return nr_pages;	
@@ -364,9 +364,9 @@ done:
  * processes, from rmap.
  *
  * If the pages are mostly unmapped, the processing is fast and it is
- * appropriate to hold pagemap_lru_lock across the whole operation.  But if
+ * 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 pagemap_lru_lock around each page.  It's impossible to balance
+ * 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.
@@ -388,7 +388,7 @@ refill_inactive_zone(struct zone *zone, 
 	unsigned last_lockno = -1;
 
 	lru_add_drain();
-	spin_lock_irq(&_pagemap_lru_lock);
+	spin_lock_irq(&zone->lru_lock);
 	while (nr_pages && !list_empty(&zone->active_list)) {
 		page = list_entry(zone->active_list.prev, struct page, lru);
 		prefetchw_prev_lru_page(page, &zone->active_list, flags);
@@ -398,7 +398,7 @@ refill_inactive_zone(struct zone *zone, 
 		list_move(&page->lru, &l_hold);
 		nr_pages--;
 	}
-	spin_unlock_irq(&_pagemap_lru_lock);
+	spin_unlock_irq(&zone->lru_lock);
 
 	while (!list_empty(&l_hold)) {
 		page = list_entry(l_hold.prev, struct page, lru);
@@ -416,7 +416,7 @@ refill_inactive_zone(struct zone *zone, 
 	drop_rmap_lock(&rmap_lock, &last_lockno);
 
 	pagevec_init(&pvec);
-	spin_lock_irq(&_pagemap_lru_lock);
+	spin_lock_irq(&zone->lru_lock);
 	while (!list_empty(&l_inactive)) {
 		page = list_entry(l_inactive.prev, struct page, lru);
 		prefetchw_prev_lru_page(page, &l_inactive, flags);
@@ -426,9 +426,9 @@ refill_inactive_zone(struct zone *zone, 
 			BUG();
 		list_move(&page->lru, &zone->inactive_list);
 		if (!pagevec_add(&pvec, page)) {
-			spin_unlock_irq(&_pagemap_lru_lock);
+			spin_unlock_irq(&zone->lru_lock);
 			__pagevec_release(&pvec);
-			spin_lock_irq(&_pagemap_lru_lock);
+			spin_lock_irq(&zone->lru_lock);
 		}
 	}
 	while (!list_empty(&l_active)) {
@@ -439,14 +439,14 @@ refill_inactive_zone(struct zone *zone, 
 		BUG_ON(!PageActive(page));
 		list_move(&page->lru, &zone->active_list);
 		if (!pagevec_add(&pvec, page)) {
-			spin_unlock_irq(&_pagemap_lru_lock);
+			spin_unlock_irq(&zone->lru_lock);
 			__pagevec_release(&pvec);
-			spin_lock_irq(&_pagemap_lru_lock);
+			spin_lock_irq(&zone->lru_lock);
 		}
 	}
 	zone->nr_active -= pgdeactivate;
 	zone->nr_inactive += pgdeactivate;
-	spin_unlock_irq(&_pagemap_lru_lock);
+	spin_unlock_irq(&zone->lru_lock);
 	pagevec_release(&pvec);
 
 	KERNEL_STAT_ADD(pgscan, nr_pages_in - nr_pages);
--- 2.5.31/include/linux/mm.h~per-zone-lock	Sun Aug 11 00:20:35 2002
+++ 2.5.31-akpm/include/linux/mm.h	Sun Aug 11 00:20:50 2002
@@ -157,7 +157,7 @@ struct page {
 	struct address_space *mapping;	/* The inode (or ...) we belong to. */
 	unsigned long index;		/* Our offset within mapping. */
 	struct list_head lru;		/* Pageout list, eg. active_list;
-					   protected by pagemap_lru_lock !! */
+					   protected by zone->lru_lock !! */
 	union {
 		struct pte_chain * chain;	/* Reverse pte mapping pointer.
 					 * protected by PG_chainlock */
--- 2.5.31/include/linux/page-flags.h~per-zone-lock	Sun Aug 11 00:20:35 2002
+++ 2.5.31-akpm/include/linux/page-flags.h	Sun Aug 11 00:20:35 2002
@@ -28,7 +28,7 @@
  *
  * Note that the referenced bit, the page->lru list_head and the active,
  * inactive_dirty and inactive_clean lists are protected by the
- * pagemap_lru_lock, and *NOT* by the usual PG_locked bit!
+ * zone->lru_lock, and *NOT* by the usual PG_locked bit!
  *
  * PG_error is set to indicate that an I/O error occurred on this page.
  *
--- 2.5.31/include/linux/swap.h~per-zone-lock	Sun Aug 11 00:20:35 2002
+++ 2.5.31-akpm/include/linux/swap.h	Sun Aug 11 00:20:35 2002
@@ -212,8 +212,6 @@ extern struct swap_list_t swap_list;
 asmlinkage long sys_swapoff(const char *);
 asmlinkage long sys_swapon(const char *, int);
 
-extern spinlock_t _pagemap_lru_lock;
-
 extern void FASTCALL(mark_page_accessed(struct page *));
 
 extern spinlock_t swaplock;
--- 2.5.31/mm/page_alloc.c~per-zone-lock	Sun Aug 11 00:20:35 2002
+++ 2.5.31-akpm/mm/page_alloc.c	Sun Aug 11 00:20:50 2002
@@ -829,7 +829,8 @@ void __init free_area_init_core(int nid,
 		printk("zone(%lu): %lu pages.\n", j, size);
 		zone->size = size;
 		zone->name = zone_names[j];
-		zone->lock = SPIN_LOCK_UNLOCKED;
+		spin_lock_init(&zone->lock);
+		spin_lock_init(&zone->lru_lock);
 		zone->zone_pgdat = pgdat;
 		zone->free_pages = 0;
 		zone->need_balance = 0;
--- 2.5.31/include/linux/mmzone.h~per-zone-lock	Sun Aug 11 00:20:35 2002
+++ 2.5.31-akpm/include/linux/mmzone.h	Sun Aug 11 00:20:58 2002
@@ -44,6 +44,7 @@ struct zone {
 	unsigned long		pages_min, pages_low, pages_high;
 	int			need_balance;
 
+	spinlock_t		lru_lock;	
 	struct list_head	active_list;
 	struct list_head	inactive_list;
 	atomic_t		refill_counter;
.
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

\
 
 \ /
  Last update: 2005-03-22 12:27    [from the cache]
©2003-2008