History

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.

Benjamin LaHaise committed 12 years ago

Commit 8e321fe1 parent 18816863 files · +53 −15Browse files

Unified · Split

3 files
  1. fs/aio.c +44 −8
  2. include/linux/migrate.h +2 −1
  3. mm/migrate.c +7 −6
fs/aio.c+44−8
@@ -244,9 +244,14 @@ static void aio_free_ring(struct kioctx *ctx) · 243 unmodified lines
244 int i;244 int i;
245 245
246 for (i = 0; i < ctx->nr_pages; i++) {246 for (i = 0; i < ctx->nr_pages; i++) {
247 struct page *page;
247 pr_debug("pid(%d) [%d] page->count=%d\n", current->pid, i,248 pr_debug("pid(%d) [%d] page->count=%d\n", current->pid, i,
248 page_count(ctx->ring_pages[i]));249 page_count(ctx->ring_pages[i]));
249 put_page(ctx->ring_pages[i]);250 page = ctx->ring_pages[i];
251 if (!page)
252 continue;
253 ctx->ring_pages[i] = NULL;
254 put_page(page);
250 }255 }
251 256
252 put_aio_ring_file(ctx);257 put_aio_ring_file(ctx);
@@ -280,18 +285,38 @@ static int aio_migratepage(struct address_space *mapping, struct page *new, · 27 unmodified lines
280 unsigned long flags;285 unsigned long flags;
281 int rc;286 int rc;
282 287
288 rc = 0;
289
290 /* Make sure the old page hasn't already been changed */
291 spin_lock(&mapping->private_lock);
292 ctx = mapping->private_data;
293 if (ctx) {
294 pgoff_t idx;
295 spin_lock_irqsave(&ctx->completion_lock, flags);
296 idx = old->index;
297 if (idx < (pgoff_t)ctx->nr_pages) {
298 if (ctx->ring_pages[idx] != old)
299 rc = -EAGAIN;
300 } else
301 rc = -EINVAL;
302 spin_unlock_irqrestore(&ctx->completion_lock, flags);
303 } else
304 rc = -EINVAL;
305 spin_unlock(&mapping->private_lock);
306
307 if (rc != 0)
308 return rc;
309
283 /* Writeback must be complete */310 /* Writeback must be complete */
284 BUG_ON(PageWriteback(old));311 BUG_ON(PageWriteback(old));
285 put_page(old);312 get_page(new);
286 313
287 rc = migrate_page_move_mapping(mapping, new, old, NULL, mode);314 rc = migrate_page_move_mapping(mapping, new, old, NULL, mode, 1);
288 if (rc != MIGRATEPAGE_SUCCESS) {315 if (rc != MIGRATEPAGE_SUCCESS) {
289 get_page(old);316 put_page(new);
290 return rc;317 return rc;
291 }318 }
292 319
293 get_page(new);
294
295 /* We can potentially race against kioctx teardown here. Use the320 /* We can potentially race against kioctx teardown here. Use the
296 * address_space's private data lock to protect the mapping's321 * address_space's private data lock to protect the mapping's
297 * private_data.322 * private_data.
@@ -303,13 +328,24 @@ static int aio_migratepage(struct address_space *mapping, struct page *new, · 5 unmodified lines
303 spin_lock_irqsave(&ctx->completion_lock, flags);328 spin_lock_irqsave(&ctx->completion_lock, flags);
304 migrate_page_copy(new, old);329 migrate_page_copy(new, old);
305 idx = old->index;330 idx = old->index;
306 if (idx < (pgoff_t)ctx->nr_pages)331 if (idx < (pgoff_t)ctx->nr_pages) {
307 ctx->ring_pages[idx] = new;332 /* And only do the move if things haven't changed */
333 if (ctx->ring_pages[idx] == old)
334 ctx->ring_pages[idx] = new;
335 else
336 rc = -EAGAIN;
337 } else
338 rc = -EINVAL;
308 spin_unlock_irqrestore(&ctx->completion_lock, flags);339 spin_unlock_irqrestore(&ctx->completion_lock, flags);
309 } else340 } else
310 rc = -EBUSY;341 rc = -EBUSY;
311 spin_unlock(&mapping->private_lock);342 spin_unlock(&mapping->private_lock);
312 343
344 if (rc == MIGRATEPAGE_SUCCESS)
345 put_page(old);
346 else
347 put_page(new);
348
313 return rc;349 return rc;
314}350}
315#endif351#endif
include/linux/migrate.h+2−1
@@ -55,7 +55,8 @@ extern int migrate_huge_page_move_mapping(struct address_space *mapping, · 54 unmodified lines
55 struct page *newpage, struct page *page);55 struct page *newpage, struct page *page);
56extern int migrate_page_move_mapping(struct address_space *mapping,56extern int migrate_page_move_mapping(struct address_space *mapping,
57 struct page *newpage, struct page *page,57 struct page *newpage, struct page *page,
58 struct buffer_head *head, enum migrate_mode mode);58 struct buffer_head *head, enum migrate_mode mode,
59 int extra_count);
59#else60#else
60 61
61static inline void putback_lru_pages(struct list_head *l) {}62static inline void putback_lru_pages(struct list_head *l) {}
mm/migrate.c+7−6
@@ -317,14 +317,15 @@ static inline bool buffer_migrate_lock_buffers(struct buffer_head *head, · 316 unmodified lines
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, · 3 unmodified lines
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, · 243 unmodified lines
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, · 20 unmodified lines
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;