aboutsummaryrefslogtreecommitdiffstats
path: root/mm/migrate.c
diff options
context:
space:
mode:
authorBenjamin LaHaise <bcrl@kvack.org>2013-12-21 17:56:08 -0500
committerBenjamin LaHaise <bcrl@kvack.org>2013-12-21 17:56:08 -0500
commit8e321fefb0e60bae4e2a28d20fc4fa30758d27c6 (patch)
treec00de123ad058cc2d69a2e3d59dd7a2bb7500542 /mm/migrate.c
parent1881686f842065d2f92ec9c6424830ffc17d23b0 (diff)
aio/migratepages: make aio migrate pages sane
The arbitrary restriction on page counts offered by the core migrate_page_move_mapping() code results in rather suspicious looking fiddling with page reference counts in the aio_migratepage() operation. To fix this, make migrate_page_move_mapping() take an extra_count parameter that allows aio to tell the code about its own reference count on the page being migrated. While cleaning up aio_migratepage(), make it validate that the old page being passed in is actually what aio_migratepage() expects to prevent misbehaviour in the case of races. Signed-off-by: Benjamin LaHaise <bcrl@kvack.org>
Diffstat (limited to 'mm/migrate.c')
-rw-r--r--mm/migrate.c13
1 files changed, 7 insertions, 6 deletions
diff --git a/mm/migrate.c b/mm/migrate.c
index e9b710201335..9194375b2307 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -317,14 +317,15 @@ static inline bool buffer_migrate_lock_buffers(struct buffer_head *head,
317 */ 317 */
318int migrate_page_move_mapping(struct address_space *mapping, 318int migrate_page_move_mapping(struct address_space *mapping,
319 struct page *newpage, struct page *page, 319 struct page *newpage, struct page *page,
320 struct buffer_head *head, enum migrate_mode mode) 320 struct buffer_head *head, enum migrate_mode mode,
321 int extra_count)
321{ 322{
322 int expected_count = 0; 323 int expected_count = 1 + extra_count;
323 void **pslot; 324 void **pslot;
324 325
325 if (!mapping) { 326 if (!mapping) {
326 /* Anonymous page without mapping */ 327 /* Anonymous page without mapping */
327 if (page_count(page) != 1) 328 if (page_count(page) != expected_count)
328 return -EAGAIN; 329 return -EAGAIN;
329 return MIGRATEPAGE_SUCCESS; 330 return MIGRATEPAGE_SUCCESS;
330 } 331 }
@@ -334,7 +335,7 @@ int migrate_page_move_mapping(struct address_space *mapping,
334 pslot = radix_tree_lookup_slot(&mapping->page_tree, 335 pslot = radix_tree_lookup_slot(&mapping->page_tree,
335 page_index(page)); 336 page_index(page));
336 337
337 expected_count = 2 + page_has_private(page); 338 expected_count += 1 + page_has_private(page);
338 if (page_count(page) != expected_count || 339 if (page_count(page) != expected_count ||
339 radix_tree_deref_slot_protected(pslot, &mapping->tree_lock) != page) { 340 radix_tree_deref_slot_protected(pslot, &mapping->tree_lock) != page) {
340 spin_unlock_irq(&mapping->tree_lock); 341 spin_unlock_irq(&mapping->tree_lock);
@@ -584,7 +585,7 @@ int migrate_page(struct address_space *mapping,
584 585
585 BUG_ON(PageWriteback(page)); /* Writeback must be complete */ 586 BUG_ON(PageWriteback(page)); /* Writeback must be complete */
586 587
587 rc = migrate_page_move_mapping(mapping, newpage, page, NULL, mode); 588 rc = migrate_page_move_mapping(mapping, newpage, page, NULL, mode, 0);
588 589
589 if (rc != MIGRATEPAGE_SUCCESS) 590 if (rc != MIGRATEPAGE_SUCCESS)
590 return rc; 591 return rc;
@@ -611,7 +612,7 @@ int buffer_migrate_page(struct address_space *mapping,
611 612
612 head = page_buffers(page); 613 head = page_buffers(page);
613 614
614 rc = migrate_page_move_mapping(mapping, newpage, page, head, mode); 615 rc = migrate_page_move_mapping(mapping, newpage, page, head, mode, 0);
615 616
616 if (rc != MIGRATEPAGE_SUCCESS) 617 if (rc != MIGRATEPAGE_SUCCESS)
617 return rc; 618 return rc;