diff options
author | Benjamin LaHaise <bcrl@kvack.org> | 2013-12-21 17:56:08 -0500 |
---|---|---|
committer | Benjamin LaHaise <bcrl@kvack.org> | 2013-12-21 17:56:08 -0500 |
commit | 8e321fefb0e60bae4e2a28d20fc4fa30758d27c6 (patch) | |
tree | c00de123ad058cc2d69a2e3d59dd7a2bb7500542 /mm/migrate.c | |
parent | 1881686f842065d2f92ec9c6424830ffc17d23b0 (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.c | 13 |
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 | */ |
318 | int migrate_page_move_mapping(struct address_space *mapping, | 318 | int 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; |