diff options
-rw-r--r-- | fs/fuse/dev.c | 10 | ||||
-rw-r--r-- | include/linux/memcontrol.h | 4 | ||||
-rw-r--r-- | include/linux/pagemap.h | 1 | ||||
-rw-r--r-- | mm/filemap.c | 70 | ||||
-rw-r--r-- | mm/memcontrol.c | 4 | ||||
-rw-r--r-- | mm/migrate.c | 2 |
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 | ||
97 | extern int | 97 | extern int |
98 | mem_cgroup_prepare_migration(struct page *page, | 98 | mem_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); |
100 | extern void mem_cgroup_end_migration(struct mem_cgroup *mem, | 100 | extern 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 | ||
250 | static inline int | 250 | static inline int |
251 | mem_cgroup_prepare_migration(struct page *page, struct page *newpage, | 251 | mem_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); |
458 | extern void remove_from_page_cache(struct page *page); | 458 | extern void remove_from_page_cache(struct page *page); |
459 | extern void __remove_from_page_cache(struct page *page); | 459 | extern void __remove_from_page_cache(struct page *page); |
460 | int 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, | |||
387 | EXPORT_SYMBOL(filemap_write_and_wait_range); | 387 | EXPORT_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 | */ | ||
404 | int 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 | } | ||
457 | EXPORT_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 | */ |
2885 | int mem_cgroup_prepare_migration(struct page *page, | 2885 | int 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; |