Skip to contentSkip to conversation

aio: ctx->dead cleanups

PATCH Superseded Oleg Nesterov 2 participants 7 messages Jun 18, 2015 – Jun 19, 2015

Subsystems (2)

v4 of 5 Superseded Latest is v5 →

lore
Share & tools
lore
#0 lore raw

v4 cover aio: ctx->dead cleanups

Quoted 4 lines

As Jeff suggested, 1/3 was changed to simply remove the ->dead check. I also updated the changelog.

Jeff, I preserved your acks in 2-3.

also remove ctx->dead setting in ioctx_alloc().

Oleg.

#1 lore raw

PATCH v4 1/3 aio_ring_remap: kill the bogus ctx->dead check

kill_ioctx() sets ctx->dead and removes ctx from ->ioctx_table "atomically" under mm->ioctx_lock, so aio_ring_remap() can never see a dead ctx.

And even -EINVAL doesn't look necessary. Yes, if mremap() races with kill_ioctx() vm_munmap(ctx->mmap_base, ctx->mmap_size) can unmap the wrong region. In this case the buggy application should blame itself. And there are other reasons why that vm_munmap() can be wrong. Say, an application can mremap() the part of aio region and then do io_destroy(). We could change aio_ring_remap() to verify vma->that vma_end - vma->vma_start == ctx->mmap_size but this won't help if the application does munmap() instead.

---
 fs/aio.c |    9 +++------
 1 files changed, 3 insertions(+), 6 deletions(-)
fs/aio.c +3 -6
index 480440f..893d300 100644
@@ -325,14 +325,11 @@ static int aio_ring_remap(struct file *file, struct vm_area_struct *vma)
 	rcu_read_lock();
 	table = rcu_dereference(mm->ioctx_table);
 	for (i = 0; i < table->nr; i++) {
-		struct kioctx *ctx;
+		struct kioctx *ctx = table->table[i];
 
-		ctx = table->table[i];
 		if (ctx && ctx->aio_ring_file == file) {
-			if (!atomic_read(&ctx->dead)) {
-				ctx->user_id = ctx->mmap_base = vma->vm_start;
-				res = 0;
-			}
+			ctx->user_id = ctx->mmap_base = vma->vm_start;
+			res = 0;
 			break;
 		}
 	}
Signature · 1 line
1.5.5.1
#5 lore raw

PATCH v4 2/3 aio: make aio_ring->dead boolean

In reply to Oleg Nesterov

"atomic_t dead" makes no sense. atomic_read() is the plain LOAD, it doesn't have some "additional" synchronization with xchg().

And now that kill_ioctx() sets "dead" under mm->ioctx_lock we do not even need xchg().

Also, the error path in ioctx_alloc() sets ->dead for no reason, remove this.

---
 fs/aio.c |    8 ++++----
 1 files changed, 4 insertions(+), 4 deletions(-)
fs/aio.c +4 -4
index 893d300..4a360be 100644
@@ -84,7 +84,7 @@ struct ctx_rq_wait {
 
 struct kioctx {
 	struct percpu_ref	users;
-	atomic_t		dead;
+	bool			dead;
 
 	struct percpu_ref	reqs;
 
@@ -765,7 +765,6 @@ static struct kioctx *ioctx_alloc(unsigned nr_events)
 err_cleanup:
 	aio_nr_sub(ctx->max_reqs);
 err_ctx:
-	atomic_set(&ctx->dead, 1);
 	if (ctx->mmap_size)
 		vm_munmap(ctx->mmap_base, ctx->mmap_size);
 	aio_free_ring(ctx);
@@ -790,11 +789,12 @@ static int kill_ioctx(struct mm_struct *mm, struct kioctx *ctx,
 	struct kioctx_table *table;
 
 	spin_lock(&mm->ioctx_lock);
-	if (atomic_xchg(&ctx->dead, 1)) {
+	if (unlikely(ctx->dead)) {
 		spin_unlock(&mm->ioctx_lock);
 		return -EINVAL;
 	}
 
+	ctx->dead = true;
 	table = rcu_dereference_raw(mm->ioctx_table);
 	WARN_ON(ctx != table->table[ctx->id]);
 	table->table[ctx->id] = NULL;
@@ -1236,7 +1236,7 @@ static bool aio_read_events(struct kioctx *ctx, long min_nr, long nr,
 	if (ret > 0)
 		*i += ret;
 
-	if (unlikely(atomic_read(&ctx->dead)))
+	if (unlikely(ctx->dead))
 		ret = -EINVAL;
 
 	if (!*i)
Signature · 1 line
1.5.5.1
#6 lore raw

PATCH v4 3/3 aio_free_ring: don't do page_count(NULL)

In reply to Oleg Nesterov

aio_free_ring() can actually see the NULL page in ->ring_pages[], this can happen if aio_setup_ring() fails.

And in this case page_count(ctx->ring_pages[i]) can OOPS.

---
 fs/aio.c |    8 ++++----
 1 files changed, 4 insertions(+), 4 deletions(-)
fs/aio.c +4 -4
index 4a360be..9bc1335 100644
@@ -292,12 +292,12 @@ static void aio_free_ring(struct kioctx *ctx)
 	put_aio_ring_file(ctx);
 
 	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]));
-		page = ctx->ring_pages[i];
+		struct page *page = ctx->ring_pages[i];
 		if (!page)
 			continue;
+
+		pr_debug("pid(%d) [%d] page->count=%d\n",
+				current->pid, i, page_count(page));
 		ctx->ring_pages[i] = NULL;
 		put_page(page);
 	}
Signature · 1 line
1.5.5.1
#2 lore raw

Re: [PATCH v4 1/3] aio_ring_remap: kill the bogus ctx->dead check

In reply to Oleg Nesterov
Quoted 4 lines · Oleg Nesterov

kill_ioctx() sets ctx->dead and removes ctx from ->ioctx_table "atomically" under mm->ioctx_lock, so aio_ring_remap() can never see a dead ctx.

Quoted 8 lines

And even -EINVAL doesn't look necessary. Yes, if mremap() races with kill_ioctx() vm_munmap(ctx->mmap_base, ctx->mmap_size) can unmap the wrong region. In this case the buggy application should blame itself. And there are other reasons why that vm_munmap() can be wrong. Say, an application can mremap() the part of aio region and then do io_destroy(). We could change aio_ring_remap() to verify vma->that vma_end - vma->vma_start == ctx->mmap_size but this won't help if the application does munmap() instead.

I don't think this paragraph really fits with the patch. It's interesting commentary, but if you feel strongly enough about it, send a patch that undoes b2edffdd912b. ;-)

So, drop that paragraph from the commit message, and you can add my reviewed by. The patch itself looks good to me.

Cheers,
Jeff

Quoted 28 lines
---
 fs/aio.c |    9 +++------
 1 files changed, 3 insertions(+), 6 deletions(-)
fs/aio.c +3 -6
index 480440f..893d300 100644
@@ -325,14 +325,11 @@ static int aio_ring_remap(struct file *file, struct vm_area_struct *vma)
 	rcu_read_lock();
 	table = rcu_dereference(mm->ioctx_table);
 	for (i = 0; i < table->nr; i++) {
-		struct kioctx *ctx;
+		struct kioctx *ctx = table->table[i];
 
-		ctx = table->table[i];
 		if (ctx && ctx->aio_ring_file == file) {
-			if (!atomic_read(&ctx->dead)) {
-				ctx->user_id = ctx->mmap_base = vma->vm_start;
-				res = 0;
-			}
+			ctx->user_id = ctx->mmap_base = vma->vm_start;
+			res = 0;
 			break;
 		}
 	}
#3 lore raw

Re: [PATCH v4 1/3] aio_ring_remap: kill the bogus ctx->dead check

Quoted 16 lines · Jeff Moyer
Quoted 4 lines · Oleg Nesterov

kill_ioctx() sets ctx->dead and removes ctx from ->ioctx_table "atomically" under mm->ioctx_lock, so aio_ring_remap() can never see a dead ctx.

Quoted 8 lines

And even -EINVAL doesn't look necessary. Yes, if mremap() races with kill_ioctx() vm_munmap(ctx->mmap_base, ctx->mmap_size) can unmap the wrong region. In this case the buggy application should blame itself. And there are other reasons why that vm_munmap() can be wrong. Say, an application can mremap() the part of aio region and then do io_destroy(). We could change aio_ring_remap() to verify vma->that vma_end - vma->vma_start == ctx->mmap_size but this won't help if the application does munmap() instead.

Quoted 2 lines

I don't think this paragraph really fits with the patch. It's interesting commentary,

Well, this time I disagree. It would be better to add a comment, but the changelog can help too to understand the code and potential problems if it races with kill_ioctx().

Quoted 2 lines

but if you feel strongly enough about it, send a
patch that undoes b2edffdd912b. ;-)

No, I think that commit makes sense. Just it was wrong and we need to fix it. And, in particular, its changelog was wrong (at least confusing), it looks as if there are strong reasons to prevent this race. So the changelog above can unconfuse the git-log reader.

But. I never argue with maintainers about non-technical issues ;)

Quoted 2 lines

So, drop that paragraph from the commit message, and you can add my reviewed by. The patch itself looks good to me.

OK, I am sending v5 with your reviewed-by.

Thanks!

Oleg.

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in Please read the FAQ at http://www.tux.org/lkml/

#4 lore raw

Re: [PATCH v4 1/3] aio_ring_remap: kill the bogus ctx->dead check

Quoted 27 lines · Oleg Nesterov
Quoted 16 lines · Jeff Moyer
Quoted 4 lines · Oleg Nesterov

kill_ioctx() sets ctx->dead and removes ctx from ->ioctx_table "atomically" under mm->ioctx_lock, so aio_ring_remap() can never see a dead ctx.

Quoted 8 lines

And even -EINVAL doesn't look necessary. Yes, if mremap() races with kill_ioctx() vm_munmap(ctx->mmap_base, ctx->mmap_size) can unmap the wrong region. In this case the buggy application should blame itself. And there are other reasons why that vm_munmap() can be wrong. Say, an application can mremap() the part of aio region and then do io_destroy(). We could change aio_ring_remap() to verify vma->that vma_end - vma->vma_start == ctx->mmap_size but this won't help if the application does munmap() instead.

Quoted 2 lines

I don't think this paragraph really fits with the patch. It's interesting commentary,

Well, this time I disagree. It would be better to add a comment, but the changelog can help too to understand the code and potential problems if it races with kill_ioctx().

Quoted 2 lines

but if you feel strongly enough about it, send a
patch that undoes b2edffdd912b. ;-)

Quoted 7 lines

No, I think that commit makes sense. Just it was wrong and we need to fix it. And, in particular, its changelog was wrong (at least confusing), it looks as if there are strong reasons to prevent this race. So the changelog above can unconfuse the git-log reader.

But. I never argue with maintainers about non-technical issues ;)

Well, then we can have Ben weigh in. I'm not trying to make a huge deal out of it.

Cheers,
Jeff
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in Please read the FAQ at http://www.tux.org/lkml/