aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/fuse/dev.c10
-rw-r--r--include/linux/memcontrol.h4
-rw-r--r--include/linux/pagemap.h1
-rw-r--r--mm/filemap.c70
-rw-r--r--mm/memcontrol.c4
-rw-r--r--mm/migrate.c2
6 files changed, 80 insertions, 11 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index 213d3cf4f5e..640fc229df1 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -737,14 +737,12 @@ static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep)
737 if (WARN_ON(PageMlocked(oldpage))) 737 if (WARN_ON(PageMlocked(oldpage)))
738 goto out_fallback_unlock; 738 goto out_fallback_unlock;
739 739
740 remove_from_page_cache(oldpage); 740 err = replace_page_cache_page(oldpage, newpage, GFP_KERNEL);
741 page_cache_release(oldpage);
742
743 err = add_to_page_cache_locked(newpage, mapping, index, GFP_KERNEL);
744 if (err) { 741 if (err) {
745 printk(KERN_WARNING "fuse_try_move_page: failed to add page"); 742 unlock_page(newpage);
746 goto out_fallback_unlock; 743 return err;
747 } 744 }
745
748 page_cache_get(newpage); 746 page_cache_get(newpage);
749 747
750 if (!(buf->flags & PIPE_BUF_FLAG_LRU)) 748 if (!(buf->flags & PIPE_BUF_FLAG_LRU))
diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index f512e189be5..a1a1e5384f6 100644
--- a/include/linux/memcontrol.h
+++ b/include/linux/memcontrol.h
@@ -96,7 +96,7 @@ extern struct cgroup_subsys_state *mem_cgroup_css(struct mem_cgroup *mem);
96 96
97extern int 97extern int
98mem_cgroup_prepare_migration(struct page *page, 98mem_cgroup_prepare_migration(struct page *page,
99 struct page *newpage, struct mem_cgroup **ptr); 99 struct page *newpage, struct mem_cgroup **ptr, gfp_t gfp_mask);
100extern void mem_cgroup_end_migration(struct mem_cgroup *mem, 100extern void mem_cgroup_end_migration(struct mem_cgroup *mem,
101 struct page *oldpage, struct page *newpage, bool migration_ok); 101 struct page *oldpage, struct page *newpage, bool migration_ok);
102 102
@@ -249,7 +249,7 @@ static inline struct cgroup_subsys_state *mem_cgroup_css(struct mem_cgroup *mem)
249 249
250static inline int 250static inline int
251mem_cgroup_prepare_migration(struct page *page, struct page *newpage, 251mem_cgroup_prepare_migration(struct page *page, struct page *newpage,
252 struct mem_cgroup **ptr) 252 struct mem_cgroup **ptr, gfp_t gfp_mask)
253{ 253{
254 return 0; 254 return 0;
255} 255}
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index 9c66e994540..26946ad483b 100644
--- a/include/linux/pagemap.h
+++ b/include/linux/pagemap.h
@@ -457,6 +457,7 @@ int add_to_page_cache_lru(struct page *page, struct address_space *mapping,
457 pgoff_t index, gfp_t gfp_mask); 457 pgoff_t index, gfp_t gfp_mask);
458extern void remove_from_page_cache(struct page *page); 458extern void remove_from_page_cache(struct page *page);
459extern void __remove_from_page_cache(struct page *page); 459extern void __remove_from_page_cache(struct page *page);
460int replace_page_cache_page(struct page *old, struct page *new, gfp_t gfp_mask);
460 461
461/* 462/*
462 * Like add_to_page_cache_locked, but used to add newly allocated pages: 463 * Like add_to_page_cache_locked, but used to add newly allocated pages:
diff --git a/mm/filemap.c b/mm/filemap.c
index 312b6eb7843..c1459f2cdb5 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 da53a252b25..6ef5c53dffc 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 352de555626..8aacce3af8c 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;