diff options
Diffstat (limited to 'fs/aio.c')
-rw-r--r-- | fs/aio.c | 44 |
1 files changed, 30 insertions, 14 deletions
@@ -68,9 +68,9 @@ struct aio_ring { | |||
68 | #define AIO_RING_PAGES 8 | 68 | #define AIO_RING_PAGES 8 |
69 | 69 | ||
70 | struct kioctx_table { | 70 | struct kioctx_table { |
71 | struct rcu_head rcu; | 71 | struct rcu_head rcu; |
72 | unsigned nr; | 72 | unsigned nr; |
73 | struct kioctx *table[]; | 73 | struct kioctx __rcu *table[]; |
74 | }; | 74 | }; |
75 | 75 | ||
76 | struct kioctx_cpu { | 76 | struct kioctx_cpu { |
@@ -115,7 +115,8 @@ struct kioctx { | |||
115 | struct page **ring_pages; | 115 | struct page **ring_pages; |
116 | long nr_pages; | 116 | long nr_pages; |
117 | 117 | ||
118 | struct work_struct free_work; | 118 | struct rcu_head free_rcu; |
119 | struct work_struct free_work; /* see free_ioctx() */ | ||
119 | 120 | ||
120 | /* | 121 | /* |
121 | * signals when all in-flight requests are done | 122 | * signals when all in-flight requests are done |
@@ -329,7 +330,7 @@ static int aio_ring_mremap(struct vm_area_struct *vma) | |||
329 | for (i = 0; i < table->nr; i++) { | 330 | for (i = 0; i < table->nr; i++) { |
330 | struct kioctx *ctx; | 331 | struct kioctx *ctx; |
331 | 332 | ||
332 | ctx = table->table[i]; | 333 | ctx = rcu_dereference(table->table[i]); |
333 | if (ctx && ctx->aio_ring_file == file) { | 334 | if (ctx && ctx->aio_ring_file == file) { |
334 | if (!atomic_read(&ctx->dead)) { | 335 | if (!atomic_read(&ctx->dead)) { |
335 | ctx->user_id = ctx->mmap_base = vma->vm_start; | 336 | ctx->user_id = ctx->mmap_base = vma->vm_start; |
@@ -588,6 +589,12 @@ static int kiocb_cancel(struct aio_kiocb *kiocb) | |||
588 | return cancel(&kiocb->common); | 589 | return cancel(&kiocb->common); |
589 | } | 590 | } |
590 | 591 | ||
592 | /* | ||
593 | * free_ioctx() should be RCU delayed to synchronize against the RCU | ||
594 | * protected lookup_ioctx() and also needs process context to call | ||
595 | * aio_free_ring(), so the double bouncing through kioctx->free_rcu and | ||
596 | * ->free_work. | ||
597 | */ | ||
591 | static void free_ioctx(struct work_struct *work) | 598 | static void free_ioctx(struct work_struct *work) |
592 | { | 599 | { |
593 | struct kioctx *ctx = container_of(work, struct kioctx, free_work); | 600 | struct kioctx *ctx = container_of(work, struct kioctx, free_work); |
@@ -601,6 +608,14 @@ static void free_ioctx(struct work_struct *work) | |||
601 | kmem_cache_free(kioctx_cachep, ctx); | 608 | kmem_cache_free(kioctx_cachep, ctx); |
602 | } | 609 | } |
603 | 610 | ||
611 | static void free_ioctx_rcufn(struct rcu_head *head) | ||
612 | { | ||
613 | struct kioctx *ctx = container_of(head, struct kioctx, free_rcu); | ||
614 | |||
615 | INIT_WORK(&ctx->free_work, free_ioctx); | ||
616 | schedule_work(&ctx->free_work); | ||
617 | } | ||
618 | |||
604 | static void free_ioctx_reqs(struct percpu_ref *ref) | 619 | static void free_ioctx_reqs(struct percpu_ref *ref) |
605 | { | 620 | { |
606 | struct kioctx *ctx = container_of(ref, struct kioctx, reqs); | 621 | struct kioctx *ctx = container_of(ref, struct kioctx, reqs); |
@@ -609,8 +624,8 @@ static void free_ioctx_reqs(struct percpu_ref *ref) | |||
609 | if (ctx->rq_wait && atomic_dec_and_test(&ctx->rq_wait->count)) | 624 | if (ctx->rq_wait && atomic_dec_and_test(&ctx->rq_wait->count)) |
610 | complete(&ctx->rq_wait->comp); | 625 | complete(&ctx->rq_wait->comp); |
611 | 626 | ||
612 | INIT_WORK(&ctx->free_work, free_ioctx); | 627 | /* Synchronize against RCU protected table->table[] dereferences */ |
613 | schedule_work(&ctx->free_work); | 628 | call_rcu(&ctx->free_rcu, free_ioctx_rcufn); |
614 | } | 629 | } |
615 | 630 | ||
616 | /* | 631 | /* |
@@ -651,9 +666,9 @@ static int ioctx_add_table(struct kioctx *ctx, struct mm_struct *mm) | |||
651 | while (1) { | 666 | while (1) { |
652 | if (table) | 667 | if (table) |
653 | for (i = 0; i < table->nr; i++) | 668 | for (i = 0; i < table->nr; i++) |
654 | if (!table->table[i]) { | 669 | if (!rcu_access_pointer(table->table[i])) { |
655 | ctx->id = i; | 670 | ctx->id = i; |
656 | table->table[i] = ctx; | 671 | rcu_assign_pointer(table->table[i], ctx); |
657 | spin_unlock(&mm->ioctx_lock); | 672 | spin_unlock(&mm->ioctx_lock); |
658 | 673 | ||
659 | /* While kioctx setup is in progress, | 674 | /* While kioctx setup is in progress, |
@@ -834,11 +849,11 @@ static int kill_ioctx(struct mm_struct *mm, struct kioctx *ctx, | |||
834 | } | 849 | } |
835 | 850 | ||
836 | table = rcu_dereference_raw(mm->ioctx_table); | 851 | table = rcu_dereference_raw(mm->ioctx_table); |
837 | WARN_ON(ctx != table->table[ctx->id]); | 852 | WARN_ON(ctx != rcu_access_pointer(table->table[ctx->id])); |
838 | table->table[ctx->id] = NULL; | 853 | RCU_INIT_POINTER(table->table[ctx->id], NULL); |
839 | spin_unlock(&mm->ioctx_lock); | 854 | spin_unlock(&mm->ioctx_lock); |
840 | 855 | ||
841 | /* percpu_ref_kill() will do the necessary call_rcu() */ | 856 | /* free_ioctx_reqs() will do the necessary RCU synchronization */ |
842 | wake_up_all(&ctx->wait); | 857 | wake_up_all(&ctx->wait); |
843 | 858 | ||
844 | /* | 859 | /* |
@@ -880,7 +895,8 @@ void exit_aio(struct mm_struct *mm) | |||
880 | 895 | ||
881 | skipped = 0; | 896 | skipped = 0; |
882 | for (i = 0; i < table->nr; ++i) { | 897 | for (i = 0; i < table->nr; ++i) { |
883 | struct kioctx *ctx = table->table[i]; | 898 | struct kioctx *ctx = |
899 | rcu_dereference_protected(table->table[i], true); | ||
884 | 900 | ||
885 | if (!ctx) { | 901 | if (!ctx) { |
886 | skipped++; | 902 | skipped++; |
@@ -1069,7 +1085,7 @@ static struct kioctx *lookup_ioctx(unsigned long ctx_id) | |||
1069 | if (!table || id >= table->nr) | 1085 | if (!table || id >= table->nr) |
1070 | goto out; | 1086 | goto out; |
1071 | 1087 | ||
1072 | ctx = table->table[id]; | 1088 | ctx = rcu_dereference(table->table[id]); |
1073 | if (ctx && ctx->user_id == ctx_id) { | 1089 | if (ctx && ctx->user_id == ctx_id) { |
1074 | percpu_ref_get(&ctx->users); | 1090 | percpu_ref_get(&ctx->users); |
1075 | ret = ctx; | 1091 | ret = ctx; |