diff options
author | Lee Schermerhorn <Lee.Schermerhorn@hp.com> | 2008-10-18 23:26:43 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-10-20 11:50:26 -0400 |
commit | 89e004ea55abe201b29e2d6e35124101f1288ef7 (patch) | |
tree | 272a8f453106fd33d66fd7153f44696648dbe8b6 | |
parent | ba9ddf49391645e6bb93219131a40446538a5e76 (diff) |
SHM_LOCKED pages are unevictable
Shmem segments locked into memory via shmctl(SHM_LOCKED) should not be
kept on the normal LRU, since scanning them is a waste of time and might
throw off kswapd's balancing algorithms. Place them on the unevictable
LRU list instead.
Use the AS_UNEVICTABLE flag to mark address_space of SHM_LOCKed shared
memory regions as unevictable. Then these pages will be culled off the
normal LRU lists during vmscan.
Add new wrapper function to clear the mapping's unevictable state when/if
shared memory segment is munlocked.
Add 'scan_mapping_unevictable_page()' to mm/vmscan.c to scan all pages in
the shmem segment's mapping [struct address_space] for evictability now
that they're no longer locked. If so, move them to the appropriate zone
lru list.
Changes depend on [CONFIG_]UNEVICTABLE_LRU.
[kosaki.motohiro@jp.fujitsu.com: revert shm change]
Signed-off-by: Lee Schermerhorn <lee.schermerhorn@hp.com>
Signed-off-by: Rik van Riel <riel@redhat.com>
Signed-off-by: Kosaki Motohiro <kosaki.motohiro@jp.fujitsu.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | include/linux/mm.h | 4 | ||||
-rw-r--r-- | include/linux/pagemap.h | 12 | ||||
-rw-r--r-- | include/linux/swap.h | 4 | ||||
-rw-r--r-- | ipc/shm.c | 4 | ||||
-rw-r--r-- | mm/shmem.c | 4 | ||||
-rw-r--r-- | mm/vmscan.c | 89 |
6 files changed, 112 insertions, 5 deletions
diff --git a/include/linux/mm.h b/include/linux/mm.h index c61ba10768ea..40236290e2ae 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h | |||
@@ -700,10 +700,10 @@ static inline int page_mapped(struct page *page) | |||
700 | extern void show_free_areas(void); | 700 | extern void show_free_areas(void); |
701 | 701 | ||
702 | #ifdef CONFIG_SHMEM | 702 | #ifdef CONFIG_SHMEM |
703 | int shmem_lock(struct file *file, int lock, struct user_struct *user); | 703 | extern int shmem_lock(struct file *file, int lock, struct user_struct *user); |
704 | #else | 704 | #else |
705 | static inline int shmem_lock(struct file *file, int lock, | 705 | static inline int shmem_lock(struct file *file, int lock, |
706 | struct user_struct *user) | 706 | struct user_struct *user) |
707 | { | 707 | { |
708 | return 0; | 708 | return 0; |
709 | } | 709 | } |
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index 09164d2c5c27..4b6c4d8d26b8 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h | |||
@@ -40,14 +40,20 @@ static inline void mapping_set_unevictable(struct address_space *mapping) | |||
40 | set_bit(AS_UNEVICTABLE, &mapping->flags); | 40 | set_bit(AS_UNEVICTABLE, &mapping->flags); |
41 | } | 41 | } |
42 | 42 | ||
43 | static inline void mapping_clear_unevictable(struct address_space *mapping) | ||
44 | { | ||
45 | clear_bit(AS_UNEVICTABLE, &mapping->flags); | ||
46 | } | ||
47 | |||
43 | static inline int mapping_unevictable(struct address_space *mapping) | 48 | static inline int mapping_unevictable(struct address_space *mapping) |
44 | { | 49 | { |
45 | if (mapping && (mapping->flags & AS_UNEVICTABLE)) | 50 | if (likely(mapping)) |
46 | return 1; | 51 | return test_bit(AS_UNEVICTABLE, &mapping->flags); |
47 | return 0; | 52 | return !!mapping; |
48 | } | 53 | } |
49 | #else | 54 | #else |
50 | static inline void mapping_set_unevictable(struct address_space *mapping) { } | 55 | static inline void mapping_set_unevictable(struct address_space *mapping) { } |
56 | static inline void mapping_clear_unevictable(struct address_space *mapping) { } | ||
51 | static inline int mapping_unevictable(struct address_space *mapping) | 57 | static inline int mapping_unevictable(struct address_space *mapping) |
52 | { | 58 | { |
53 | return 0; | 59 | return 0; |
diff --git a/include/linux/swap.h b/include/linux/swap.h index a2113044d20a..7edb4cbc29f9 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h | |||
@@ -232,12 +232,16 @@ static inline int zone_reclaim(struct zone *z, gfp_t mask, unsigned int order) | |||
232 | 232 | ||
233 | #ifdef CONFIG_UNEVICTABLE_LRU | 233 | #ifdef CONFIG_UNEVICTABLE_LRU |
234 | extern int page_evictable(struct page *page, struct vm_area_struct *vma); | 234 | extern int page_evictable(struct page *page, struct vm_area_struct *vma); |
235 | extern void scan_mapping_unevictable_pages(struct address_space *); | ||
235 | #else | 236 | #else |
236 | static inline int page_evictable(struct page *page, | 237 | static inline int page_evictable(struct page *page, |
237 | struct vm_area_struct *vma) | 238 | struct vm_area_struct *vma) |
238 | { | 239 | { |
239 | return 1; | 240 | return 1; |
240 | } | 241 | } |
242 | static inline void scan_mapping_unevictable_pages(struct address_space *mapping) | ||
243 | { | ||
244 | } | ||
241 | #endif | 245 | #endif |
242 | 246 | ||
243 | extern int kswapd_run(int nid); | 247 | extern int kswapd_run(int nid); |
@@ -737,6 +737,10 @@ asmlinkage long sys_shmctl(int shmid, int cmd, struct shmid_ds __user *buf) | |||
737 | case SHM_LOCK: | 737 | case SHM_LOCK: |
738 | case SHM_UNLOCK: | 738 | case SHM_UNLOCK: |
739 | { | 739 | { |
740 | struct file *uninitialized_var(shm_file); | ||
741 | |||
742 | lru_add_drain_all(); /* drain pagevecs to lru lists */ | ||
743 | |||
740 | shp = shm_lock_check(ns, shmid); | 744 | shp = shm_lock_check(ns, shmid); |
741 | if (IS_ERR(shp)) { | 745 | if (IS_ERR(shp)) { |
742 | err = PTR_ERR(shp); | 746 | err = PTR_ERR(shp); |
diff --git a/mm/shmem.c b/mm/shmem.c index fc2ccf79a776..d38d7e61fcd0 100644 --- a/mm/shmem.c +++ b/mm/shmem.c | |||
@@ -1477,12 +1477,16 @@ int shmem_lock(struct file *file, int lock, struct user_struct *user) | |||
1477 | if (!user_shm_lock(inode->i_size, user)) | 1477 | if (!user_shm_lock(inode->i_size, user)) |
1478 | goto out_nomem; | 1478 | goto out_nomem; |
1479 | info->flags |= VM_LOCKED; | 1479 | info->flags |= VM_LOCKED; |
1480 | mapping_set_unevictable(file->f_mapping); | ||
1480 | } | 1481 | } |
1481 | if (!lock && (info->flags & VM_LOCKED) && user) { | 1482 | if (!lock && (info->flags & VM_LOCKED) && user) { |
1482 | user_shm_unlock(inode->i_size, user); | 1483 | user_shm_unlock(inode->i_size, user); |
1483 | info->flags &= ~VM_LOCKED; | 1484 | info->flags &= ~VM_LOCKED; |
1485 | mapping_clear_unevictable(file->f_mapping); | ||
1486 | scan_mapping_unevictable_pages(file->f_mapping); | ||
1484 | } | 1487 | } |
1485 | retval = 0; | 1488 | retval = 0; |
1489 | |||
1486 | out_nomem: | 1490 | out_nomem: |
1487 | spin_unlock(&info->lock); | 1491 | spin_unlock(&info->lock); |
1488 | return retval; | 1492 | return retval; |
diff --git a/mm/vmscan.c b/mm/vmscan.c index 9babfbc1ddc8..dfb342e0db9b 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c | |||
@@ -2346,4 +2346,93 @@ int page_evictable(struct page *page, struct vm_area_struct *vma) | |||
2346 | 2346 | ||
2347 | return 1; | 2347 | return 1; |
2348 | } | 2348 | } |
2349 | |||
2350 | /** | ||
2351 | * check_move_unevictable_page - check page for evictability and move to appropriate zone lru list | ||
2352 | * @page: page to check evictability and move to appropriate lru list | ||
2353 | * @zone: zone page is in | ||
2354 | * | ||
2355 | * Checks a page for evictability and moves the page to the appropriate | ||
2356 | * zone lru list. | ||
2357 | * | ||
2358 | * Restrictions: zone->lru_lock must be held, page must be on LRU and must | ||
2359 | * have PageUnevictable set. | ||
2360 | */ | ||
2361 | static void check_move_unevictable_page(struct page *page, struct zone *zone) | ||
2362 | { | ||
2363 | VM_BUG_ON(PageActive(page)); | ||
2364 | |||
2365 | retry: | ||
2366 | ClearPageUnevictable(page); | ||
2367 | if (page_evictable(page, NULL)) { | ||
2368 | enum lru_list l = LRU_INACTIVE_ANON + page_is_file_cache(page); | ||
2369 | __dec_zone_state(zone, NR_UNEVICTABLE); | ||
2370 | list_move(&page->lru, &zone->lru[l].list); | ||
2371 | __inc_zone_state(zone, NR_INACTIVE_ANON + l); | ||
2372 | __count_vm_event(UNEVICTABLE_PGRESCUED); | ||
2373 | } else { | ||
2374 | /* | ||
2375 | * rotate unevictable list | ||
2376 | */ | ||
2377 | SetPageUnevictable(page); | ||
2378 | list_move(&page->lru, &zone->lru[LRU_UNEVICTABLE].list); | ||
2379 | if (page_evictable(page, NULL)) | ||
2380 | goto retry; | ||
2381 | } | ||
2382 | } | ||
2383 | |||
2384 | /** | ||
2385 | * scan_mapping_unevictable_pages - scan an address space for evictable pages | ||
2386 | * @mapping: struct address_space to scan for evictable pages | ||
2387 | * | ||
2388 | * Scan all pages in mapping. Check unevictable pages for | ||
2389 | * evictability and move them to the appropriate zone lru list. | ||
2390 | */ | ||
2391 | void scan_mapping_unevictable_pages(struct address_space *mapping) | ||
2392 | { | ||
2393 | pgoff_t next = 0; | ||
2394 | pgoff_t end = (i_size_read(mapping->host) + PAGE_CACHE_SIZE - 1) >> | ||
2395 | PAGE_CACHE_SHIFT; | ||
2396 | struct zone *zone; | ||
2397 | struct pagevec pvec; | ||
2398 | |||
2399 | if (mapping->nrpages == 0) | ||
2400 | return; | ||
2401 | |||
2402 | pagevec_init(&pvec, 0); | ||
2403 | while (next < end && | ||
2404 | pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) { | ||
2405 | int i; | ||
2406 | int pg_scanned = 0; | ||
2407 | |||
2408 | zone = NULL; | ||
2409 | |||
2410 | for (i = 0; i < pagevec_count(&pvec); i++) { | ||
2411 | struct page *page = pvec.pages[i]; | ||
2412 | pgoff_t page_index = page->index; | ||
2413 | struct zone *pagezone = page_zone(page); | ||
2414 | |||
2415 | pg_scanned++; | ||
2416 | if (page_index > next) | ||
2417 | next = page_index; | ||
2418 | next++; | ||
2419 | |||
2420 | if (pagezone != zone) { | ||
2421 | if (zone) | ||
2422 | spin_unlock_irq(&zone->lru_lock); | ||
2423 | zone = pagezone; | ||
2424 | spin_lock_irq(&zone->lru_lock); | ||
2425 | } | ||
2426 | |||
2427 | if (PageLRU(page) && PageUnevictable(page)) | ||
2428 | check_move_unevictable_page(page, zone); | ||
2429 | } | ||
2430 | if (zone) | ||
2431 | spin_unlock_irq(&zone->lru_lock); | ||
2432 | pagevec_release(&pvec); | ||
2433 | |||
2434 | count_vm_events(UNEVICTABLE_PGSCANNED, pg_scanned); | ||
2435 | } | ||
2436 | |||
2437 | } | ||
2349 | #endif | 2438 | #endif |