aboutsummaryrefslogtreecommitdiffstats
path: root/mm
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@suse.cz>2011-03-22 19:30:52 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2011-03-22 20:44:02 -0400
commitef6a3c63112e865d632ff7c478ba7c7160cad0d1 (patch)
treed0bd3ee2b79674e22b8dd3f318814cd4789697b8 /mm
parent318b275fbca1ab9ec0862de71420e0e92c3d1aa7 (diff)
mm: add replace_page_cache_page() function
This function basically does: remove_from_page_cache(old); page_cache_release(old); add_to_page_cache_locked(new); Except it does this atomically, so there's no possibility for the "add" to fail because of a race. If memory cgroups are enabled, then the memory cgroup charge is also moved from the old page to the new. This function is currently used by fuse to move pages into the page cache on read, instead of copying the page contents. [minchan.kim@gmail.com: add freepage() hook to replace_page_cache_page()] Signed-off-by: Miklos Szeredi <mszeredi@suse.cz> Acked-by: Rik van Riel <riel@redhat.com> Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> Cc: Mel Gorman <mel@csn.ul.ie> Signed-off-by: Minchan Kim <minchan.kim@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm')
-rw-r--r--mm/filemap.c70
-rw-r--r--mm/memcontrol.c4
-rw-r--r--mm/migrate.c2
3 files changed, 73 insertions, 3 deletions
diff --git a/mm/filemap.c b/mm/filemap.c
index 312b6eb78430..c1459f2cdb5e 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -387,6 +387,76 @@ int filemap_write_and_wait_range(struct address_space *mapping,
387EXPORT_SYMBOL(filemap_write_and_wait_range); 387EXPORT_SYMBOL(filemap_write_and_wait_range);
388 388
389/** 389/**
390 * replace_page_cache_page - replace a pagecache page with a new one
391 * @old: page to be replaced
392 * @new: page to replace with
393 * @gfp_mask: allocation mode
394 *
395 * This function replaces a page in the pagecache with a new one. On
396 * success it acquires the pagecache reference for the new page and
397 * drops it for the old page. Both the old and new pages must be
398 * locked. This function does not add the new page to the LRU, the
399 * caller must do that.
400 *
401 * The remove + add is atomic. The only way this function can fail is
402 * memory allocation failure.
403 */
404int replace_page_cache_page(struct page *old, struct page *new, gfp_t gfp_mask)
405{
406 int error;
407 struct mem_cgroup *memcg = NULL;
408
409 VM_BUG_ON(!PageLocked(old));
410 VM_BUG_ON(!PageLocked(new));
411 VM_BUG_ON(new->mapping);
412
413 /*
414 * This is not page migration, but prepare_migration and
415 * end_migration does enough work for charge replacement.
416 *
417 * In the longer term we probably want a specialized function
418 * for moving the charge from old to new in a more efficient
419 * manner.
420 */
421 error = mem_cgroup_prepare_migration(old, new, &memcg, gfp_mask);
422 if (error)
423 return error;
424
425 error = radix_tree_preload(gfp_mask & ~__GFP_HIGHMEM);
426 if (!error) {
427 struct address_space *mapping = old->mapping;
428 void (*freepage)(struct page *);
429
430 pgoff_t offset = old->index;
431 freepage = mapping->a_ops->freepage;
432
433 page_cache_get(new);
434 new->mapping = mapping;
435 new->index = offset;
436
437 spin_lock_irq(&mapping->tree_lock);
438 __remove_from_page_cache(old);
439 error = radix_tree_insert(&mapping->page_tree, offset, new);
440 BUG_ON(error);
441 mapping->nrpages++;
442 __inc_zone_page_state(new, NR_FILE_PAGES);
443 if (PageSwapBacked(new))
444 __inc_zone_page_state(new, NR_SHMEM);
445 spin_unlock_irq(&mapping->tree_lock);
446 radix_tree_preload_end();
447 if (freepage)
448 freepage(old);
449 page_cache_release(old);
450 mem_cgroup_end_migration(memcg, old, new, true);
451 } else {
452 mem_cgroup_end_migration(memcg, old, new, false);
453 }
454
455 return error;
456}
457EXPORT_SYMBOL_GPL(replace_page_cache_page);
458
459/**
390 * add_to_page_cache_locked - add a locked page to the pagecache 460 * add_to_page_cache_locked - add a locked page to the pagecache
391 * @page: page to add 461 * @page: page to add
392 * @mapping: the page's address_space 462 * @mapping: the page's address_space
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index da53a252b259..6ef5c53dffcb 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -2883,7 +2883,7 @@ static inline int mem_cgroup_move_swap_account(swp_entry_t entry,
2883 * page belongs to. 2883 * page belongs to.
2884 */ 2884 */
2885int mem_cgroup_prepare_migration(struct page *page, 2885int mem_cgroup_prepare_migration(struct page *page,
2886 struct page *newpage, struct mem_cgroup **ptr) 2886 struct page *newpage, struct mem_cgroup **ptr, gfp_t gfp_mask)
2887{ 2887{
2888 struct page_cgroup *pc; 2888 struct page_cgroup *pc;
2889 struct mem_cgroup *mem = NULL; 2889 struct mem_cgroup *mem = NULL;
@@ -2940,7 +2940,7 @@ int mem_cgroup_prepare_migration(struct page *page,
2940 return 0; 2940 return 0;
2941 2941
2942 *ptr = mem; 2942 *ptr = mem;
2943 ret = __mem_cgroup_try_charge(NULL, GFP_KERNEL, ptr, false, PAGE_SIZE); 2943 ret = __mem_cgroup_try_charge(NULL, gfp_mask, ptr, false, PAGE_SIZE);
2944 css_put(&mem->css);/* drop extra refcnt */ 2944 css_put(&mem->css);/* drop extra refcnt */
2945 if (ret || *ptr == NULL) { 2945 if (ret || *ptr == NULL) {
2946 if (PageAnon(page)) { 2946 if (PageAnon(page)) {
diff --git a/mm/migrate.c b/mm/migrate.c
index 352de555626c..8aacce3af8cd 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -678,7 +678,7 @@ static int unmap_and_move(new_page_t get_new_page, unsigned long private,
678 } 678 }
679 679
680 /* charge against new page */ 680 /* charge against new page */
681 charge = mem_cgroup_prepare_migration(page, newpage, &mem); 681 charge = mem_cgroup_prepare_migration(page, newpage, &mem, GFP_KERNEL);
682 if (charge == -ENOMEM) { 682 if (charge == -ENOMEM) {
683 rc = -ENOMEM; 683 rc = -ENOMEM;
684 goto unlock; 684 goto unlock;