Skip to content

Commit

Permalink
Merge git://git.kvack.org/~bcrl/aio-next
Browse files Browse the repository at this point in the history
Pull AIO leak fixes from Ben LaHaise:
 "I've put these two patches plus Linus's change through a round of
  tests, and it passes millions of iterations of the aio numa
  migratepage test, as well as a number of repetitions of a few simple
  read and write tests.

  The first patch fixes the memory leak Kent introduced, while the
  second patch makes aio_migratepage() much more paranoid and robust"

* git://git.kvack.org/~bcrl/aio-next:
  aio/migratepages: make aio migrate pages sane
  aio: fix kioctx leak introduced by "aio: Fix a trinity splat"
torvalds committed Dec 22, 2013
2 parents 3dc9acb + 8e321fe commit a8472b4
Showing 3 changed files with 55 additions and 16 deletions.
55 changes: 46 additions & 9 deletions fs/aio.c
Original file line number Diff line number Diff line change
@@ -244,9 +244,14 @@ static void aio_free_ring(struct kioctx *ctx)
int i;

for (i = 0; i < ctx->nr_pages; i++) {
struct page *page;
pr_debug("pid(%d) [%d] page->count=%d\n", current->pid, i,
page_count(ctx->ring_pages[i]));
put_page(ctx->ring_pages[i]);
page = ctx->ring_pages[i];
if (!page)
continue;
ctx->ring_pages[i] = NULL;
put_page(page);
}

put_aio_ring_file(ctx);
@@ -280,18 +285,38 @@ static int aio_migratepage(struct address_space *mapping, struct page *new,
unsigned long flags;
int rc;

rc = 0;

/* Make sure the old page hasn't already been changed */
spin_lock(&mapping->private_lock);
ctx = mapping->private_data;
if (ctx) {
pgoff_t idx;
spin_lock_irqsave(&ctx->completion_lock, flags);
idx = old->index;
if (idx < (pgoff_t)ctx->nr_pages) {
if (ctx->ring_pages[idx] != old)
rc = -EAGAIN;
} else
rc = -EINVAL;
spin_unlock_irqrestore(&ctx->completion_lock, flags);
} else
rc = -EINVAL;
spin_unlock(&mapping->private_lock);

if (rc != 0)
return rc;

/* Writeback must be complete */
BUG_ON(PageWriteback(old));
put_page(old);
get_page(new);

rc = migrate_page_move_mapping(mapping, new, old, NULL, mode);
rc = migrate_page_move_mapping(mapping, new, old, NULL, mode, 1);
if (rc != MIGRATEPAGE_SUCCESS) {
get_page(old);
put_page(new);
return rc;
}

get_page(new);

/* We can potentially race against kioctx teardown here. Use the
* address_space's private data lock to protect the mapping's
* private_data.
@@ -303,13 +328,24 @@ static int aio_migratepage(struct address_space *mapping, struct page *new,
spin_lock_irqsave(&ctx->completion_lock, flags);
migrate_page_copy(new, old);
idx = old->index;
if (idx < (pgoff_t)ctx->nr_pages)
ctx->ring_pages[idx] = new;
if (idx < (pgoff_t)ctx->nr_pages) {
/* And only do the move if things haven't changed */
if (ctx->ring_pages[idx] == old)
ctx->ring_pages[idx] = new;
else
rc = -EAGAIN;
} else
rc = -EINVAL;
spin_unlock_irqrestore(&ctx->completion_lock, flags);
} else
rc = -EBUSY;
spin_unlock(&mapping->private_lock);

if (rc == MIGRATEPAGE_SUCCESS)
put_page(old);
else
put_page(new);

return rc;
}
#endif
@@ -640,7 +676,8 @@ static struct kioctx *ioctx_alloc(unsigned nr_events)
aio_nr += ctx->max_reqs;
spin_unlock(&aio_nr_lock);

percpu_ref_get(&ctx->users); /* io_setup() will drop this ref */
percpu_ref_get(&ctx->users); /* io_setup() will drop this ref */
percpu_ref_get(&ctx->reqs); /* free_ioctx_users() will drop this */

err = ioctx_add_table(ctx, mm);
if (err)
3 changes: 2 additions & 1 deletion include/linux/migrate.h
Original file line number Diff line number Diff line change
@@ -55,7 +55,8 @@ extern int migrate_huge_page_move_mapping(struct address_space *mapping,
struct page *newpage, struct page *page);
extern int migrate_page_move_mapping(struct address_space *mapping,
struct page *newpage, struct page *page,
struct buffer_head *head, enum migrate_mode mode);
struct buffer_head *head, enum migrate_mode mode,
int extra_count);
#else

static inline void putback_lru_pages(struct list_head *l) {}
13 changes: 7 additions & 6 deletions mm/migrate.c
Original file line number Diff line number Diff line change
@@ -317,14 +317,15 @@ static inline bool buffer_migrate_lock_buffers(struct buffer_head *head,
*/
int migrate_page_move_mapping(struct address_space *mapping,
struct page *newpage, struct page *page,
struct buffer_head *head, enum migrate_mode mode)
struct buffer_head *head, enum migrate_mode mode,
int extra_count)
{
int expected_count = 0;
int expected_count = 1 + extra_count;
void **pslot;

if (!mapping) {
/* Anonymous page without mapping */
if (page_count(page) != 1)
if (page_count(page) != expected_count)
return -EAGAIN;
return MIGRATEPAGE_SUCCESS;
}
@@ -334,7 +335,7 @@ int migrate_page_move_mapping(struct address_space *mapping,
pslot = radix_tree_lookup_slot(&mapping->page_tree,
page_index(page));

expected_count = 2 + page_has_private(page);
expected_count += 1 + page_has_private(page);
if (page_count(page) != expected_count ||
radix_tree_deref_slot_protected(pslot, &mapping->tree_lock) != page) {
spin_unlock_irq(&mapping->tree_lock);
@@ -584,7 +585,7 @@ int migrate_page(struct address_space *mapping,

BUG_ON(PageWriteback(page)); /* Writeback must be complete */

rc = migrate_page_move_mapping(mapping, newpage, page, NULL, mode);
rc = migrate_page_move_mapping(mapping, newpage, page, NULL, mode, 0);

if (rc != MIGRATEPAGE_SUCCESS)
return rc;
@@ -611,7 +612,7 @@ int buffer_migrate_page(struct address_space *mapping,

head = page_buffers(page);

rc = migrate_page_move_mapping(mapping, newpage, page, head, mode);
rc = migrate_page_move_mapping(mapping, newpage, page, head, mode, 0);

if (rc != MIGRATEPAGE_SUCCESS)
return rc;

0 comments on commit a8472b4

Please sign in to comment.