diff options
author | Nick Piggin <nickpiggin@yahoo.com.au> | 2006-01-06 03:11:20 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-01-06 11:33:29 -0500 |
commit | a74609fafa2e5cc31d558012abaaa55ec9ad9da4 (patch) | |
tree | 0be653692864d99da345b575dfe2083994ee1d21 | |
parent | d3cb487149bd706aa6aeb02042332a450978dc1c (diff) |
[PATCH] mm: page_state opt
Optimise page_state manipulations by introducing interrupt unsafe accessors
to page_state fields. Callers must provide their own locking (either
disable interrupts or not update from interrupt context).
Switch over the hot callsites that can easily be moved under interrupts off
sections.
Signed-off-by: Nick Piggin <npiggin@suse.de>
Cc: Hugh Dickins <hugh@veritas.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r-- | include/linux/page-flags.h | 43 | ||||
-rw-r--r-- | mm/page_alloc.c | 89 | ||||
-rw-r--r-- | mm/rmap.c | 10 | ||||
-rw-r--r-- | mm/vmscan.c | 27 |
4 files changed, 104 insertions, 65 deletions
diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h index 32d09c8d952b..dede8d412dca 100644 --- a/include/linux/page-flags.h +++ b/include/linux/page-flags.h | |||
@@ -144,22 +144,33 @@ struct page_state { | |||
144 | extern void get_page_state(struct page_state *ret); | 144 | extern void get_page_state(struct page_state *ret); |
145 | extern void get_page_state_node(struct page_state *ret, int node); | 145 | extern void get_page_state_node(struct page_state *ret, int node); |
146 | extern void get_full_page_state(struct page_state *ret); | 146 | extern void get_full_page_state(struct page_state *ret); |
147 | extern unsigned long __read_page_state(unsigned long offset); | 147 | extern unsigned long read_page_state_offset(unsigned long offset); |
148 | extern void __mod_page_state(unsigned long offset, unsigned long delta); | 148 | extern void mod_page_state_offset(unsigned long offset, unsigned long delta); |
149 | extern void __mod_page_state_offset(unsigned long offset, unsigned long delta); | ||
149 | 150 | ||
150 | #define read_page_state(member) \ | 151 | #define read_page_state(member) \ |
151 | __read_page_state(offsetof(struct page_state, member)) | 152 | read_page_state_offset(offsetof(struct page_state, member)) |
152 | 153 | ||
153 | #define mod_page_state(member, delta) \ | 154 | #define mod_page_state(member, delta) \ |
154 | __mod_page_state(offsetof(struct page_state, member), (delta)) | 155 | mod_page_state_offset(offsetof(struct page_state, member), (delta)) |
155 | 156 | ||
156 | #define inc_page_state(member) mod_page_state(member, 1UL) | 157 | #define __mod_page_state(member, delta) \ |
157 | #define dec_page_state(member) mod_page_state(member, 0UL - 1) | 158 | __mod_page_state_offset(offsetof(struct page_state, member), (delta)) |
158 | #define add_page_state(member,delta) mod_page_state(member, (delta)) | ||
159 | #define sub_page_state(member,delta) mod_page_state(member, 0UL - (delta)) | ||
160 | 159 | ||
161 | #define mod_page_state_zone(zone, member, delta) \ | 160 | #define inc_page_state(member) mod_page_state(member, 1UL) |
162 | do { \ | 161 | #define dec_page_state(member) mod_page_state(member, 0UL - 1) |
162 | #define add_page_state(member,delta) mod_page_state(member, (delta)) | ||
163 | #define sub_page_state(member,delta) mod_page_state(member, 0UL - (delta)) | ||
164 | |||
165 | #define __inc_page_state(member) __mod_page_state(member, 1UL) | ||
166 | #define __dec_page_state(member) __mod_page_state(member, 0UL - 1) | ||
167 | #define __add_page_state(member,delta) __mod_page_state(member, (delta)) | ||
168 | #define __sub_page_state(member,delta) __mod_page_state(member, 0UL - (delta)) | ||
169 | |||
170 | #define page_state(member) (*__page_state(offsetof(struct page_state, member))) | ||
171 | |||
172 | #define state_zone_offset(zone, member) \ | ||
173 | ({ \ | ||
163 | unsigned offset; \ | 174 | unsigned offset; \ |
164 | if (is_highmem(zone)) \ | 175 | if (is_highmem(zone)) \ |
165 | offset = offsetof(struct page_state, member##_high); \ | 176 | offset = offsetof(struct page_state, member##_high); \ |
@@ -169,7 +180,17 @@ extern void __mod_page_state(unsigned long offset, unsigned long delta); | |||
169 | offset = offsetof(struct page_state, member##_dma32); \ | 180 | offset = offsetof(struct page_state, member##_dma32); \ |
170 | else \ | 181 | else \ |
171 | offset = offsetof(struct page_state, member##_dma); \ | 182 | offset = offsetof(struct page_state, member##_dma); \ |
172 | __mod_page_state(offset, (delta)); \ | 183 | offset; \ |
184 | }) | ||
185 | |||
186 | #define __mod_page_state_zone(zone, member, delta) \ | ||
187 | do { \ | ||
188 | __mod_page_state_offset(state_zone_offset(zone, member), (delta)); \ | ||
189 | } while (0) | ||
190 | |||
191 | #define mod_page_state_zone(zone, member, delta) \ | ||
192 | do { \ | ||
193 | mod_page_state_offset(state_zone_offset(zone, member), (delta)); \ | ||
173 | } while (0) | 194 | } while (0) |
174 | 195 | ||
175 | /* | 196 | /* |
diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 7f580779abdb..fd47494cb989 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c | |||
@@ -424,9 +424,9 @@ void __free_pages_ok(struct page *page, unsigned int order) | |||
424 | return; | 424 | return; |
425 | 425 | ||
426 | list_add(&page->lru, &list); | 426 | list_add(&page->lru, &list); |
427 | mod_page_state(pgfree, 1 << order); | ||
428 | kernel_map_pages(page, 1<<order, 0); | 427 | kernel_map_pages(page, 1<<order, 0); |
429 | local_irq_save(flags); | 428 | local_irq_save(flags); |
429 | __mod_page_state(pgfree, 1 << order); | ||
430 | free_pages_bulk(page_zone(page), 1, &list, order); | 430 | free_pages_bulk(page_zone(page), 1, &list, order); |
431 | local_irq_restore(flags); | 431 | local_irq_restore(flags); |
432 | } | 432 | } |
@@ -674,18 +674,14 @@ void drain_local_pages(void) | |||
674 | } | 674 | } |
675 | #endif /* CONFIG_PM */ | 675 | #endif /* CONFIG_PM */ |
676 | 676 | ||
677 | static void zone_statistics(struct zonelist *zonelist, struct zone *z) | 677 | static void zone_statistics(struct zonelist *zonelist, struct zone *z, int cpu) |
678 | { | 678 | { |
679 | #ifdef CONFIG_NUMA | 679 | #ifdef CONFIG_NUMA |
680 | unsigned long flags; | ||
681 | int cpu; | ||
682 | pg_data_t *pg = z->zone_pgdat; | 680 | pg_data_t *pg = z->zone_pgdat; |
683 | pg_data_t *orig = zonelist->zones[0]->zone_pgdat; | 681 | pg_data_t *orig = zonelist->zones[0]->zone_pgdat; |
684 | struct per_cpu_pageset *p; | 682 | struct per_cpu_pageset *p; |
685 | 683 | ||
686 | local_irq_save(flags); | 684 | p = zone_pcp(z, cpu); |
687 | cpu = smp_processor_id(); | ||
688 | p = zone_pcp(z,cpu); | ||
689 | if (pg == orig) { | 685 | if (pg == orig) { |
690 | p->numa_hit++; | 686 | p->numa_hit++; |
691 | } else { | 687 | } else { |
@@ -696,7 +692,6 @@ static void zone_statistics(struct zonelist *zonelist, struct zone *z) | |||
696 | p->local_node++; | 692 | p->local_node++; |
697 | else | 693 | else |
698 | p->other_node++; | 694 | p->other_node++; |
699 | local_irq_restore(flags); | ||
700 | #endif | 695 | #endif |
701 | } | 696 | } |
702 | 697 | ||
@@ -716,11 +711,11 @@ static void fastcall free_hot_cold_page(struct page *page, int cold) | |||
716 | if (free_pages_check(page)) | 711 | if (free_pages_check(page)) |
717 | return; | 712 | return; |
718 | 713 | ||
719 | inc_page_state(pgfree); | ||
720 | kernel_map_pages(page, 1, 0); | 714 | kernel_map_pages(page, 1, 0); |
721 | 715 | ||
722 | pcp = &zone_pcp(zone, get_cpu())->pcp[cold]; | 716 | pcp = &zone_pcp(zone, get_cpu())->pcp[cold]; |
723 | local_irq_save(flags); | 717 | local_irq_save(flags); |
718 | __inc_page_state(pgfree); | ||
724 | list_add(&page->lru, &pcp->list); | 719 | list_add(&page->lru, &pcp->list); |
725 | pcp->count++; | 720 | pcp->count++; |
726 | if (pcp->count >= pcp->high) | 721 | if (pcp->count >= pcp->high) |
@@ -753,49 +748,58 @@ static inline void prep_zero_page(struct page *page, int order, gfp_t gfp_flags) | |||
753 | * we cheat by calling it from here, in the order > 0 path. Saves a branch | 748 | * we cheat by calling it from here, in the order > 0 path. Saves a branch |
754 | * or two. | 749 | * or two. |
755 | */ | 750 | */ |
756 | static struct page * | 751 | static struct page *buffered_rmqueue(struct zonelist *zonelist, |
757 | buffered_rmqueue(struct zone *zone, int order, gfp_t gfp_flags) | 752 | struct zone *zone, int order, gfp_t gfp_flags) |
758 | { | 753 | { |
759 | unsigned long flags; | 754 | unsigned long flags; |
760 | struct page *page; | 755 | struct page *page; |
761 | int cold = !!(gfp_flags & __GFP_COLD); | 756 | int cold = !!(gfp_flags & __GFP_COLD); |
757 | int cpu; | ||
762 | 758 | ||
763 | again: | 759 | again: |
760 | cpu = get_cpu(); | ||
764 | if (order == 0) { | 761 | if (order == 0) { |
765 | struct per_cpu_pages *pcp; | 762 | struct per_cpu_pages *pcp; |
766 | 763 | ||
767 | page = NULL; | 764 | pcp = &zone_pcp(zone, cpu)->pcp[cold]; |
768 | pcp = &zone_pcp(zone, get_cpu())->pcp[cold]; | ||
769 | local_irq_save(flags); | 765 | local_irq_save(flags); |
770 | if (!pcp->count) | 766 | if (!pcp->count) { |
771 | pcp->count += rmqueue_bulk(zone, 0, | 767 | pcp->count += rmqueue_bulk(zone, 0, |
772 | pcp->batch, &pcp->list); | 768 | pcp->batch, &pcp->list); |
773 | if (likely(pcp->count)) { | 769 | if (unlikely(!pcp->count)) |
774 | page = list_entry(pcp->list.next, struct page, lru); | 770 | goto failed; |
775 | list_del(&page->lru); | ||
776 | pcp->count--; | ||
777 | } | 771 | } |
778 | local_irq_restore(flags); | 772 | page = list_entry(pcp->list.next, struct page, lru); |
779 | put_cpu(); | 773 | list_del(&page->lru); |
774 | pcp->count--; | ||
780 | } else { | 775 | } else { |
781 | spin_lock_irqsave(&zone->lock, flags); | 776 | spin_lock_irqsave(&zone->lock, flags); |
782 | page = __rmqueue(zone, order); | 777 | page = __rmqueue(zone, order); |
783 | spin_unlock_irqrestore(&zone->lock, flags); | 778 | spin_unlock(&zone->lock); |
779 | if (!page) | ||
780 | goto failed; | ||
784 | } | 781 | } |
785 | 782 | ||
786 | if (page != NULL) { | 783 | __mod_page_state_zone(zone, pgalloc, 1 << order); |
787 | BUG_ON(bad_range(zone, page)); | 784 | zone_statistics(zonelist, zone, cpu); |
788 | mod_page_state_zone(zone, pgalloc, 1 << order); | 785 | local_irq_restore(flags); |
789 | if (prep_new_page(page, order)) | 786 | put_cpu(); |
790 | goto again; | ||
791 | 787 | ||
792 | if (gfp_flags & __GFP_ZERO) | 788 | BUG_ON(bad_range(zone, page)); |
793 | prep_zero_page(page, order, gfp_flags); | 789 | if (prep_new_page(page, order)) |
790 | goto again; | ||
794 | 791 | ||
795 | if (order && (gfp_flags & __GFP_COMP)) | 792 | if (gfp_flags & __GFP_ZERO) |
796 | prep_compound_page(page, order); | 793 | prep_zero_page(page, order, gfp_flags); |
797 | } | 794 | |
795 | if (order && (gfp_flags & __GFP_COMP)) | ||
796 | prep_compound_page(page, order); | ||
798 | return page; | 797 | return page; |
798 | |||
799 | failed: | ||
800 | local_irq_restore(flags); | ||
801 | put_cpu(); | ||
802 | return NULL; | ||
799 | } | 803 | } |
800 | 804 | ||
801 | #define ALLOC_NO_WATERMARKS 0x01 /* don't check watermarks at all */ | 805 | #define ALLOC_NO_WATERMARKS 0x01 /* don't check watermarks at all */ |
@@ -871,9 +875,8 @@ get_page_from_freelist(gfp_t gfp_mask, unsigned int order, | |||
871 | continue; | 875 | continue; |
872 | } | 876 | } |
873 | 877 | ||
874 | page = buffered_rmqueue(*z, order, gfp_mask); | 878 | page = buffered_rmqueue(zonelist, *z, order, gfp_mask); |
875 | if (page) { | 879 | if (page) { |
876 | zone_statistics(zonelist, *z); | ||
877 | break; | 880 | break; |
878 | } | 881 | } |
879 | } while (*(++z) != NULL); | 882 | } while (*(++z) != NULL); |
@@ -1248,7 +1251,7 @@ void get_full_page_state(struct page_state *ret) | |||
1248 | __get_page_state(ret, sizeof(*ret) / sizeof(unsigned long), &mask); | 1251 | __get_page_state(ret, sizeof(*ret) / sizeof(unsigned long), &mask); |
1249 | } | 1252 | } |
1250 | 1253 | ||
1251 | unsigned long __read_page_state(unsigned long offset) | 1254 | unsigned long read_page_state_offset(unsigned long offset) |
1252 | { | 1255 | { |
1253 | unsigned long ret = 0; | 1256 | unsigned long ret = 0; |
1254 | int cpu; | 1257 | int cpu; |
@@ -1262,18 +1265,26 @@ unsigned long __read_page_state(unsigned long offset) | |||
1262 | return ret; | 1265 | return ret; |
1263 | } | 1266 | } |
1264 | 1267 | ||
1265 | void __mod_page_state(unsigned long offset, unsigned long delta) | 1268 | void __mod_page_state_offset(unsigned long offset, unsigned long delta) |
1269 | { | ||
1270 | void *ptr; | ||
1271 | |||
1272 | ptr = &__get_cpu_var(page_states); | ||
1273 | *(unsigned long *)(ptr + offset) += delta; | ||
1274 | } | ||
1275 | EXPORT_SYMBOL(__mod_page_state_offset); | ||
1276 | |||
1277 | void mod_page_state_offset(unsigned long offset, unsigned long delta) | ||
1266 | { | 1278 | { |
1267 | unsigned long flags; | 1279 | unsigned long flags; |
1268 | void* ptr; | 1280 | void *ptr; |
1269 | 1281 | ||
1270 | local_irq_save(flags); | 1282 | local_irq_save(flags); |
1271 | ptr = &__get_cpu_var(page_states); | 1283 | ptr = &__get_cpu_var(page_states); |
1272 | *(unsigned long*)(ptr + offset) += delta; | 1284 | *(unsigned long *)(ptr + offset) += delta; |
1273 | local_irq_restore(flags); | 1285 | local_irq_restore(flags); |
1274 | } | 1286 | } |
1275 | 1287 | EXPORT_SYMBOL(mod_page_state_offset); | |
1276 | EXPORT_SYMBOL(__mod_page_state); | ||
1277 | 1288 | ||
1278 | void __get_zone_counts(unsigned long *active, unsigned long *inactive, | 1289 | void __get_zone_counts(unsigned long *active, unsigned long *inactive, |
1279 | unsigned long *free, struct pglist_data *pgdat) | 1290 | unsigned long *free, struct pglist_data *pgdat) |
@@ -451,7 +451,11 @@ static void __page_set_anon_rmap(struct page *page, | |||
451 | 451 | ||
452 | page->index = linear_page_index(vma, address); | 452 | page->index = linear_page_index(vma, address); |
453 | 453 | ||
454 | inc_page_state(nr_mapped); | 454 | /* |
455 | * nr_mapped state can be updated without turning off | ||
456 | * interrupts because it is not modified via interrupt. | ||
457 | */ | ||
458 | __inc_page_state(nr_mapped); | ||
455 | } | 459 | } |
456 | 460 | ||
457 | /** | 461 | /** |
@@ -498,7 +502,7 @@ void page_add_file_rmap(struct page *page) | |||
498 | BUG_ON(!pfn_valid(page_to_pfn(page))); | 502 | BUG_ON(!pfn_valid(page_to_pfn(page))); |
499 | 503 | ||
500 | if (atomic_inc_and_test(&page->_mapcount)) | 504 | if (atomic_inc_and_test(&page->_mapcount)) |
501 | inc_page_state(nr_mapped); | 505 | __inc_page_state(nr_mapped); |
502 | } | 506 | } |
503 | 507 | ||
504 | /** | 508 | /** |
@@ -522,7 +526,7 @@ void page_remove_rmap(struct page *page) | |||
522 | */ | 526 | */ |
523 | if (page_test_and_clear_dirty(page)) | 527 | if (page_test_and_clear_dirty(page)) |
524 | set_page_dirty(page); | 528 | set_page_dirty(page); |
525 | dec_page_state(nr_mapped); | 529 | __dec_page_state(nr_mapped); |
526 | } | 530 | } |
527 | } | 531 | } |
528 | 532 | ||
diff --git a/mm/vmscan.c b/mm/vmscan.c index 7681d8ee04fe..be8235fb1939 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c | |||
@@ -645,16 +645,17 @@ static void shrink_cache(struct zone *zone, struct scan_control *sc) | |||
645 | goto done; | 645 | goto done; |
646 | 646 | ||
647 | max_scan -= nr_scan; | 647 | max_scan -= nr_scan; |
648 | if (current_is_kswapd()) | ||
649 | mod_page_state_zone(zone, pgscan_kswapd, nr_scan); | ||
650 | else | ||
651 | mod_page_state_zone(zone, pgscan_direct, nr_scan); | ||
652 | nr_freed = shrink_list(&page_list, sc); | 648 | nr_freed = shrink_list(&page_list, sc); |
653 | if (current_is_kswapd()) | ||
654 | mod_page_state(kswapd_steal, nr_freed); | ||
655 | mod_page_state_zone(zone, pgsteal, nr_freed); | ||
656 | 649 | ||
657 | spin_lock_irq(&zone->lru_lock); | 650 | local_irq_disable(); |
651 | if (current_is_kswapd()) { | ||
652 | __mod_page_state_zone(zone, pgscan_kswapd, nr_scan); | ||
653 | __mod_page_state(kswapd_steal, nr_freed); | ||
654 | } else | ||
655 | __mod_page_state_zone(zone, pgscan_direct, nr_scan); | ||
656 | __mod_page_state_zone(zone, pgsteal, nr_freed); | ||
657 | |||
658 | spin_lock(&zone->lru_lock); | ||
658 | /* | 659 | /* |
659 | * Put back any unfreeable pages. | 660 | * Put back any unfreeable pages. |
660 | */ | 661 | */ |
@@ -816,11 +817,13 @@ refill_inactive_zone(struct zone *zone, struct scan_control *sc) | |||
816 | } | 817 | } |
817 | } | 818 | } |
818 | zone->nr_active += pgmoved; | 819 | zone->nr_active += pgmoved; |
819 | spin_unlock_irq(&zone->lru_lock); | 820 | spin_unlock(&zone->lru_lock); |
820 | pagevec_release(&pvec); | 821 | |
822 | __mod_page_state_zone(zone, pgrefill, pgscanned); | ||
823 | __mod_page_state(pgdeactivate, pgdeactivate); | ||
824 | local_irq_enable(); | ||
821 | 825 | ||
822 | mod_page_state_zone(zone, pgrefill, pgscanned); | 826 | pagevec_release(&pvec); |
823 | mod_page_state(pgdeactivate, pgdeactivate); | ||
824 | } | 827 | } |
825 | 828 | ||
826 | /* | 829 | /* |