aio: ctx->dead cleanups
Subsystems (4)
cover aio: ctx->dead cleanups
Al, please help. We are trying to backport some aio fixes and I am absolutely confused by your b2edffdd912b "fix mremap() vs. ioctx_kill() race".
Firstly, I simply can't understand what exactly it tries to fix. OK, aio_free_ring() can race with kill and we can remap the soon-to-be-killed ctx. So what? kill_ioctx() will the the correct (already re-mapped) ctx->mmap_base after it drops mm->ioctx_lock.
So it seems to me we only need this change to ensure that move_vma() can not succeed if ctx was already removed from ->ioctx_table, or, if we race with ioctx_alloc(), it was not added to ->ioctx_table. IOW, we need to ensure that move_vma()->aio_ring_mmap() can not race with vm_munmap(ctx->mmap_base) in kill_ioctx() or ioctx_alloc(). And this race doesn't look really bad. The kernel can't crash, just the application can fool itself.
But I guess I missed something, and I'd like to know what I have missed. Could you explain?
Also. The change in move_vma() looks "obviously wrong". Don't we need something like the patch at the end to ensure we do not "leak" new_vma or I am totally confused?
But to me the main problem is atomic_read(ctx->dead) in aio_remap(). I mean, it complicates the backporting, and it looks unnecessary and confusing. See the 1st patch.
Please review, I do not know how to test this.
Oleg.
mm/mremap.c +5 -6
@@ -275,6 +275,8 @@ static unsigned long move_vma(struct vm_ moved_len =move_page_tables (vma, old_addr, new_vma, new_addr, old_len, need_rmap_locks);if (moved_len < old_len) { + err = -ENOMEM ; +xxx:/* * On error, move entries back from new area to old, * which will succeed since page tables still there,
@@ -285,14 +287,11 @@ static unsigned long move_vma(struct vm_ vma = new_vma; old_len = new_len; old_addr = new_addr; - new_addr = -ENOMEM ; + new_addr = err; }else if (vma->vm_file && vma->vm_file->f_op->mremap) { err = vma->vm_file->f_op->mremap (vma->vm_file, new_vma); -if (err <0 ) { -move_page_tables (new_vma, new_addr, vma, old_addr, - moved_len, true); -return err; - } +if (err <0 ) + goto xxx; }/* Conceal VM_ACCOUNT so old reservation is not undone */
PATCH 1/3 aio_ring_remap: turn the ctx->dead check into WARN_ON()
atomic_read(&ctx->dead) in aio_ring_remap() looks confusing. 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.
If we really want this check, we should put it under WARN_ON() and it should not depend on aio_ring_file == file.
--- fs/aio.c | 16 ++++++++-------- 1 files changed, 8 insertions(+), 8 deletions(-)
fs/aio.c +8 -8
@@ -325,14 +325,14 @@ 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; - - 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 ; - } +struct kioctx *ctx = table->table[i]; + +if (!ctx ||WARN_ON (atomic_read (&ctx->dead))) +continue ; + +if (ctx->aio_ring_file == file) { + ctx->user_id = ctx->mmap_base = vma->vm_start; + res =0 ;break ; } }
Signature · 1 line
1.5.5.1
PATCH 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().
--- fs/aio.c | 11 ++++++----- 1 files changed, 6 insertions(+), 5 deletions(-)
fs/aio.c +6 -5
@@ -84,7 +84,7 @@ struct ctx_rq_wait {struct kioctx {struct percpu_ref users; -atomic_t dead; +bool dead;struct percpu_ref reqs;
@@ -327,7 +327,7 @@ static int aio_ring_remap(struct file *file, struct vm_area_struct *vma)for (i =0 ; i < table->nr; i++) {struct kioctx *ctx = table->table[i]; -if (!ctx ||WARN_ON (atomic_read (&ctx->dead))) +if (!ctx || ctx->dead)continue ;if (ctx->aio_ring_file == file) {
@@ -768,7 +768,7 @@ static struct kioctx *ioctx_alloc(unsigned nr_events) err_cleanup:aio_nr_sub (ctx->max_reqs); err_ctx: -atomic_set (&ctx->dead,1 ); + ctx->dead = true;/* unneeded */ if (ctx->mmap_size)vm_munmap (ctx->mmap_base, ctx->mmap_size);aio_free_ring (ctx);
@@ -793,11 +793,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 ;
@@ -1239,7 +1240,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
PATCH 3/3 aio_free_ring: don't do page_count(NULL)
In reply to Oleg Nesterovaio_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
@@ -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
PATCH v2 2/3 aio: make aio_ring->dead boolean
In reply to Oleg NesterovReview static int aio_ring_remap(struct file *file, struct vm_area_struct *vma) · line 330
Quoted 7 lines · Oleg Nesterov
(quoted diff) +1
-1@@ -327,7 +327,7 @@ static int aio_ring_remap(struct file *file, struct vm_area_struct *vma) for (i = 0; i < table->nr; i++) { struct kioctx *ctx = table->table[i]; - if (!ctx || WARN_ON(atomic_read(&ctx->dead))) + if (!ctx || ctx->dead)
Argh, sorry, I removed WARN_ON() by accident.
------------------------------------------------------------------------------
Subject: [PATCH v2 2/3] aio: make aio_ring->dead boolean
"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().
--- fs/aio.c | 11 ++++++----- 1 files changed, 6 insertions(+), 5 deletions(-)
fs/aio.c +6 -5
@@ -84,7 +84,7 @@ struct ctx_rq_wait {struct kioctx {struct percpu_ref users; -atomic_t dead; +bool dead;struct percpu_ref reqs;
@@ -327,7 +327,7 @@ static int aio_ring_remap(struct file *file, struct vm_area_struct *vma)for (i =0 ; i < table->nr; i++) {struct kioctx *ctx = table->table[i]; -if (!ctx ||WARN_ON (atomic_read (&ctx->dead))) +if (!ctx ||WARN_ON (ctx->dead))continue ;if (ctx->aio_ring_file == file) {
@@ -768,7 +768,7 @@ static struct kioctx *ioctx_alloc(unsigned nr_events) err_cleanup:aio_nr_sub (ctx->max_reqs); err_ctx: -atomic_set (&ctx->dead,1 ); + ctx->dead = true;/* unneeded */ if (ctx->mmap_size)vm_munmap (ctx->mmap_base, ctx->mmap_size);aio_free_ring (ctx);
@@ -793,11 +793,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 ;
@@ -1239,7 +1240,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
Al Viro
maintainerQuoted 9 lines · Oleg Nesterov
Al, please help. We are trying to backport some aio fixes and I am absolutely confused by your
b2edffdd912b"fixmremap()vs.ioctx_kill()race".Firstly, I simply can't understand what exactly it tries to fix. OK,
aio_free_ring()can race with kill and we can remap the soon-to-be-killed ctx. So what?kill_ioctx()will the the correct (already re-mapped)ctx->mmap_baseafter it dropsmm->ioctx_lock.
Huh? kill_ioctx() picks ctx->mmap_base and passes it to vm_munmap(). Which tries to grab mmap_sem, blocks for mremap() from another thread and waits for it to drop mmap_sem. By that time ctx->mmap_base has nothing whatsoever to the argument we'd passed to vm_munmap(). Sure, it had been recalculated by aio_ring_remap(), but it's too late for us - we'd already fetched the old value.
Al Viro
maintainerQuoted 6 lines · Al Viro
Huh?
kill_ioctx()picksctx->mmap_baseand passes it tovm_munmap(). Which tries to grabmmap_sem, blocks formremap()from another thread and waits for it to dropmmap_sem. By that timectx->mmap_basehas nothing whatsoever to the argument we'd passed tovm_munmap(). Sure, it had been recalculated byaio_ring_remap(), but it's too late for us - we'd already fetched the old value.
And yes, the leak you've spotted is real, but I would very much prefer to avoid that goto - something like this instead:
mm/mremap.c +4 -1
@@ -291,7 +291,10 @@ static unsigned long move_vma(struct vm_area_struct *vma,if (err <0 ) {move_page_tables (new_vma, new_addr, vma, old_addr, moved_len, true); -return err; + vma = new_vma; + old_len = new_len; + old_addr = new_addr; + new_addr = err; } }
Quoted 12 lines · Al Viro
Quoted 9 lines · Oleg Nesterov
Al, please help. We are trying to backport some aio fixes and I am absolutely confused by your
b2edffdd912b"fixmremap()vs.ioctx_kill()race".Firstly, I simply can't understand what exactly it tries to fix. OK,
aio_free_ring()can race with kill and we can remap the soon-to-be-killed ctx. So what?kill_ioctx()will the the correct (already re-mapped)ctx->mmap_baseafter it dropsmm->ioctx_lock.
Quoted 4 lines
Huh?
kill_ioctx()picksctx->mmap_baseand passes it tovm_munmap(). Which tries to grabmmap_sem, blocks formremap()from another thread and waits for it to dropmmap_sem. By that timectx->mmap_basehas nothing whatsoever to the argument we'd passed tovm_munmap().
Yes. But it seems that you missed another part of my email:
So it seems to me we only need this change to ensure that move_vma() can not succeed if ctx was already removed from ->ioctx_table, or, if we race with ioctx_alloc(), it was not added to ->ioctx_table. IOW, we need to ensure that move_vma()->aio_ring_mmap() can not race with vm_munmap(ctx->mmap_base) in kill_ioctx() or ioctx_alloc(). And this race doesn't look really bad. The kernel can't crash, just the application can fool itself.
So once again, could explain why do we really need to prevent this? Afaics, if the application is stupid, it can only fool itself.
And please note that ctx->mmap_base or/and ctx->mmap_size can be wrong anyway. Say, an application can munmap() this vma, or munmap() the part of this vma.
Oleg.
Quoted 29 lines · Oleg Nesterov
Quoted 12 lines · Al Viro
Quoted 9 lines · Oleg Nesterov
Al, please help. We are trying to backport some aio fixes and I am absolutely confused by your
b2edffdd912b"fixmremap()vs.ioctx_kill()race".Firstly, I simply can't understand what exactly it tries to fix. OK,
aio_free_ring()can race with kill and we can remap the soon-to-be-killed ctx. So what?kill_ioctx()will the the correct (already re-mapped)ctx->mmap_baseafter it dropsmm->ioctx_lock.Quoted 4 lines
Huh?
kill_ioctx()picksctx->mmap_baseand passes it tovm_munmap(). Which tries to grabmmap_sem, blocks formremap()from another thread and waits for it to dropmmap_sem. By that timectx->mmap_basehas nothing whatsoever to the argument we'd passed tovm_munmap().Yes. But it seems that you missed another part of my email:
So it seems to me we only need this change to ensure that move_vma() can not succeed if ctx was already removed from ->ioctx_table, or, if we race with ioctx_alloc(), it was not added to ->ioctx_table. IOW, we need to ensure that move_vma()->aio_ring_mmap() can not race with vm_munmap(ctx->mmap_base) in kill_ioctx() or ioctx_alloc(). And this race doesn't look really bad. The kernel can't crash, just the application can fool itself.
Quoted 6 lines
So once again, could explain why do we really need to prevent this? Afaics, if the application is stupid, it can only fool itself.
And please note that
ctx->mmap_baseor/andctx->mmap_sizecan be wrong anyway. Say, an application canmunmap()this vma, ormunmap()the part of this vma.
And speaking of aio_ring_remap() it can "corrupt" ->mmap_base even with this patch. Just you need to mremap() the tail of aio-mapped memory.
No?
Oleg.
Quoted 18 lines · Al Viro
Quoted 6 lines · Al Viro
Huh?
kill_ioctx()picksctx->mmap_baseand passes it tovm_munmap(). Which tries to grabmmap_sem, blocks formremap()from another thread and waits for it to dropmmap_sem. By that timectx->mmap_basehas nothing whatsoever to the argument we'd passed tovm_munmap(). Sure, it had been recalculated byaio_ring_remap(), but it's too late for us - we'd already fetched the old value.And yes, the leak you've spotted is real, but I would very much prefer to avoid that goto - something like this instead:
mm/mremap.c +0
-0
mm/mremap.c static unsigned long move_vma(struct vm_area_struct *vma, · line 297
Quoted 8 lines
mm/mremap.c +4
-1@@ -291,7 +291,10 @@ static unsigned long move_vma(struct vm_area_struct *vma,if (err <0 ) {move_page_tables (new_vma, new_addr, vma, old_addr, moved_len, true); -return err; + vma = new_vma; + old_len = new_len; + old_addr = new_addr; + new_addr = err;
Personally, I'd really prefer to factor out at least this move_page_tables() with six args. Although I agree, "goto previous_if" doesn't look nice too, this needs cleanup.
But this is minor. I am already sleeping, most probably I misread this code. But it seems that there is another bug with VM_ACCOUNT.
I'll recheck tomorrow and write another email.
Oleg.
Damn, sorry for noise, forgot to mention.
And whatever I missed, this ctx->dead check in aio_ring_remap() looks wrong anyway. Please correct me.
Quoted 44 lines · Oleg Nesterov
Quoted 29 lines · Oleg Nesterov
Quoted 12 lines · Al Viro
Quoted 9 lines · Oleg Nesterov
Al, please help. We are trying to backport some aio fixes and I am absolutely confused by your
b2edffdd912b"fixmremap()vs.ioctx_kill()race".Firstly, I simply can't understand what exactly it tries to fix. OK,
aio_free_ring()can race with kill and we can remap the soon-to-be-killed ctx. So what?kill_ioctx()will the the correct (already re-mapped)ctx->mmap_baseafter it dropsmm->ioctx_lock.Quoted 4 lines
Huh?
kill_ioctx()picksctx->mmap_baseand passes it tovm_munmap(). Which tries to grabmmap_sem, blocks formremap()from another thread and waits for it to dropmmap_sem. By that timectx->mmap_basehas nothing whatsoever to the argument we'd passed tovm_munmap().Yes. But it seems that you missed another part of my email:
So it seems to me we only need this change to ensure that move_vma() can not succeed if ctx was already removed from ->ioctx_table, or, if we race with ioctx_alloc(), it was not added to ->ioctx_table. IOW, we need to ensure that move_vma()->aio_ring_mmap() can not race with vm_munmap(ctx->mmap_base) in kill_ioctx() or ioctx_alloc(). And this race doesn't look really bad. The kernel can't crash, just the application can fool itself.Quoted 6 lines
So once again, could explain why do we really need to prevent this? Afaics, if the application is stupid, it can only fool itself.
And please note that
ctx->mmap_baseor/andctx->mmap_sizecan be wrong anyway. Say, an application canmunmap()this vma, ormunmap()the part of this vma.And speaking of
aio_ring_remap()it can "corrupt" ->mmap_base even with this patch. Just you need tomremap()the tail of aio-mapped memory.No?
Oleg.
Re: [PATCH 1/3] aio_ring_remap: turn the ctx->dead check into WARN_ON()
In reply to Oleg NesterovQuoted 7 lines · Oleg Nesterov
atomic_read(&ctx->dead)inaio_ring_remap()looks confusing.kill_ioctx()setsctx->deadand removes ctx from ->ioctx_table "atomically" undermm->ioctx_lock, soaio_ring_remap()can never see a dead ctx.If we really want this check, we should put it under
WARN_ON()and it should not depend onaio_ring_file== file.
Just get rid of the check, it doesn't make any sense.
-Jeff
Quoted 33 lines
Signed-off-by Oleg Nesterov <[email protected]>--- fs/aio.c | 16 ++++++++-------- 1 files changed, 8 insertions(+), 8 deletions(-)fs/aio.c +8
-8@@ -325,14 +325,14 @@ 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; - - 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 ; - } +struct kioctx *ctx = table->table[i]; + +if (!ctx ||WARN_ON (atomic_read (&ctx->dead))) +continue ; + +if (ctx->aio_ring_file == file) { + ctx->user_id = ctx->mmap_base = vma->vm_start; + res =0 ;break ; } }
Re: [PATCH 3/3] aio_free_ring: don't do page_count(NULL)
In reply to Oleg NesterovQuoted 6 lines · Oleg Nesterov
aio_free_ring()can actually see the NULL page in ->ring_pages[], this can happen ifaio_setup_ring()fails.And in this case
page_count(ctx->ring_pages[i])can OOPS.Signed-off-by Oleg Nesterov <[email protected]>
I'm not sure the reformatting was necessary, but whatever.
Quoted 25 lines
--- fs/aio.c | 8 ++++---- 1 files changed, 4 insertions(+), 4 deletions(-)fs/aio.c +4
-4@@ -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); }
Re: [PATCH 1/3] aio_ring_remap: turn the ctx->dead check into WARN_ON()
In reply to Jeff MoyerQuoted 8 lines · Jeff Moyer
Quoted 5 lines · Oleg Nesterov
atomic_read(&ctx->dead)inaio_ring_remap()looks confusing.kill_ioctx()setsctx->deadand removes ctx from ->ioctx_table "atomically" undermm->ioctx_lock, soaio_ring_remap()can never see a dead ctx.
Quoted 4 lines
Quoted 2 lines
If we really want this check, we should put it under
WARN_ON()and it should not depend onaio_ring_file== file.Just get rid of the check, it doesn't make any sense.
Yes, agreed, will redo/resend tomorrow.
Thanks!
Quoted 35 lines
-Jeff
Quoted 33 lines
Signed-off-by Oleg Nesterov <[email protected]>--- fs/aio.c | 16 ++++++++-------- 1 files changed, 8 insertions(+), 8 deletions(-)fs/aio.c +8
-8@@ -325,14 +325,14 @@ 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; - - 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 ; - } +struct kioctx *ctx = table->table[i]; + +if (!ctx ||WARN_ON (atomic_read (&ctx->dead))) +continue ; + +if (ctx->aio_ring_file == file) { + ctx->user_id = ctx->mmap_base = vma->vm_start; + res =0 ;break ; } }
Re: [PATCH v2 2/3] aio: make aio_ring->dead boolean
In reply to Oleg NesterovQuoted 11 lines · Oleg Nesterov
fs/aio.c static int aio_ring_remap(struct file *file, struct vm_area_struct *vma) · line 330
Quoted 7 lines · Oleg Nesterov
fs/aio.c +1
-1@@ -327,7 +327,7 @@ static int aio_ring_remap(struct file *file, struct vm_area_struct *vma)for (i =0 ; i < table->nr; i++) {struct kioctx *ctx = table->table[i]; -if (!ctx ||WARN_ON (atomic_read (&ctx->dead))) +if (!ctx || ctx->dead)Argh, sorry, I removed
WARN_ON()by accident.
Quoted 8 lines
------------------------------------------------------------------------------
Subject: [PATCH v2 2/3] aio: makeaio_ring->deadboolean"
atomic_tdead" makes no sense.atomic_read()is the plain LOAD, it doesn't have some "additional" synchronization withxchg().And now that
kill_ioctx()sets "dead" undermm->ioctx_lockwe do not even needxchg().
I think this makes sense and is safe. The key for the reader is that it will see the updated ctx->dead after it's been woken up.
fs/aio.c static struct kioctx *ioctx_alloc(unsigned nr_events) · line 771
Quoted 6 lines
fs/aio.c +1
-1@@ -768,7 +768,7 @@ static struct kioctx *ioctx_alloc(unsigned nr_events) err_cleanup:aio_nr_sub (ctx->max_reqs); err_ctx: -atomic_set (&ctx->dead,1 ); + ctx->dead = true;/* unneeded */
And I agree that this can be nuked. You can add my "Reviewed-by: Jeff Moyer <[email protected]>" to your v3 posting.
Cheers,
Jeff
Quoted 26 lines · Oleg Nesterov
Quoted 11 lines · Al Viro
On Wed, Jun 17, 2015 at 01:39:06AM +0100, Al Viro wrote:
And yes, the leak you've spotted is real, but I would very much prefer to avoid that goto - something like this instead:
mm/mremap.c +0
-0mm/mremap.c static unsigned long move_vma(struct vm_area_struct *vma, · line 297
Quoted 8 lines
mm/mremap.c +4
-1@@ -291,7 +291,10 @@ static unsigned long move_vma(struct vm_area_struct *vma,if (err <0 ) {move_page_tables (new_vma, new_addr, vma, old_addr, moved_len, true); -return err; + vma = new_vma; + old_len = new_len; + old_addr = new_addr; + new_addr = err;Personally, I'd really prefer to factor out at least this
move_page_tables()with six args. Although I agree, "gotoprevious_if" doesn't look nice too, this needs cleanup.
Quoted 4 lines
But this is minor. I am already sleeping, most probably I misread this code. But it seems that there is another bug with
VM_ACCOUNT.I'll recheck tomorrow and write another email.
Yes this look wrong. At least we shouldn't set *locked on failure. mm->locked_vm += new_len is probably fine, but doesn't look really nice because ->locked_vm can underflow in do_munmap() before that.
I'll send the fixes, but also I'll try to cleanup this code. Not sure I will succeed ;)
Oleg.