diff options
author | Konsta Holtta <kholtta@nvidia.com> | 2014-10-31 04:47:00 -0400 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2015-03-18 15:11:58 -0400 |
commit | 7bb9fe2748d3324915108af85e6014da367071f6 (patch) | |
tree | b95b11407d99657318b7a669891102a530b18323 | |
parent | 14577a339ccc160ed58f8d936ebcbd96dba3b6ca (diff) |
gpu: nvgpu: protect channel update callback access
Protect callback races from spurious gk20a channel updates by testing if
the channel update callback still exists when in the scheduled work
(instead of only when scheduling the work to the queue), and by
canceling the work when the channel is freed. Protect access to the
callback and its data by accessing them together inside
spinlock-protected regions.
Bug 200051384
Change-Id: Ib4e1571c35f662195e1dec1e362df32ddc099eb3
Signed-off-by: Konsta Holtta <kholtta@nvidia.com>
Reviewed-on: http://git-master/r/592026
Reviewed-by: Terje Bergstrom <tbergstrom@nvidia.com>
Tested-by: Terje Bergstrom <tbergstrom@nvidia.com>
-rw-r--r-- | drivers/gpu/nvgpu/gk20a/channel_gk20a.c | 18 | ||||
-rw-r--r-- | drivers/gpu/nvgpu/gk20a/channel_gk20a.h | 2 |
2 files changed, 18 insertions, 2 deletions
diff --git a/drivers/gpu/nvgpu/gk20a/channel_gk20a.c b/drivers/gpu/nvgpu/gk20a/channel_gk20a.c index f554cf77..f48ef35f 100644 --- a/drivers/gpu/nvgpu/gk20a/channel_gk20a.c +++ b/drivers/gpu/nvgpu/gk20a/channel_gk20a.c | |||
@@ -682,8 +682,11 @@ void gk20a_free_channel(struct channel_gk20a *ch, bool finish) | |||
682 | else | 682 | else |
683 | gk20a_vm_put(ch_vm); | 683 | gk20a_vm_put(ch_vm); |
684 | 684 | ||
685 | spin_lock(&ch->update_fn_lock); | ||
685 | ch->update_fn = NULL; | 686 | ch->update_fn = NULL; |
686 | ch->update_fn_data = NULL; | 687 | ch->update_fn_data = NULL; |
688 | spin_unlock(&ch->update_fn_lock); | ||
689 | cancel_work_sync(&ch->update_fn_work); | ||
687 | 690 | ||
688 | unbind: | 691 | unbind: |
689 | if (gk20a_is_channel_marked_as_tsg(ch)) | 692 | if (gk20a_is_channel_marked_as_tsg(ch)) |
@@ -745,7 +748,16 @@ static void gk20a_channel_update_runcb_fn(struct work_struct *work) | |||
745 | { | 748 | { |
746 | struct channel_gk20a *ch = | 749 | struct channel_gk20a *ch = |
747 | container_of(work, struct channel_gk20a, update_fn_work); | 750 | container_of(work, struct channel_gk20a, update_fn_work); |
748 | ch->update_fn(ch, ch->update_fn_data); | 751 | void (*update_fn)(struct channel_gk20a *, void *); |
752 | void *update_fn_data; | ||
753 | |||
754 | spin_lock(&ch->update_fn_lock); | ||
755 | update_fn = ch->update_fn; | ||
756 | update_fn_data = ch->update_fn_data; | ||
757 | spin_unlock(&ch->update_fn_lock); | ||
758 | |||
759 | if (update_fn) | ||
760 | update_fn(ch, update_fn_data); | ||
749 | } | 761 | } |
750 | 762 | ||
751 | struct channel_gk20a *gk20a_open_new_channel_with_cb(struct gk20a *g, | 763 | struct channel_gk20a *gk20a_open_new_channel_with_cb(struct gk20a *g, |
@@ -755,8 +767,10 @@ struct channel_gk20a *gk20a_open_new_channel_with_cb(struct gk20a *g, | |||
755 | struct channel_gk20a *ch = gk20a_open_new_channel(g); | 767 | struct channel_gk20a *ch = gk20a_open_new_channel(g); |
756 | 768 | ||
757 | if (ch) { | 769 | if (ch) { |
770 | spin_lock(&ch->update_fn_lock); | ||
758 | ch->update_fn = update_fn; | 771 | ch->update_fn = update_fn; |
759 | ch->update_fn_data = update_fn_data; | 772 | ch->update_fn_data = update_fn_data; |
773 | spin_unlock(&ch->update_fn_lock); | ||
760 | } | 774 | } |
761 | 775 | ||
762 | return ch; | 776 | return ch; |
@@ -811,7 +825,7 @@ struct channel_gk20a *gk20a_open_new_channel(struct gk20a *g) | |||
811 | 825 | ||
812 | ch->update_fn = NULL; | 826 | ch->update_fn = NULL; |
813 | ch->update_fn_data = NULL; | 827 | ch->update_fn_data = NULL; |
814 | 828 | spin_lock_init(&ch->update_fn_lock); | |
815 | INIT_WORK(&ch->update_fn_work, gk20a_channel_update_runcb_fn); | 829 | INIT_WORK(&ch->update_fn_work, gk20a_channel_update_runcb_fn); |
816 | 830 | ||
817 | return ch; | 831 | return ch; |
diff --git a/drivers/gpu/nvgpu/gk20a/channel_gk20a.h b/drivers/gpu/nvgpu/gk20a/channel_gk20a.h index a028b6f3..263ca291 100644 --- a/drivers/gpu/nvgpu/gk20a/channel_gk20a.h +++ b/drivers/gpu/nvgpu/gk20a/channel_gk20a.h | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <linux/mutex.h> | 24 | #include <linux/mutex.h> |
25 | #include <uapi/linux/nvgpu.h> | 25 | #include <uapi/linux/nvgpu.h> |
26 | #include <linux/poll.h> | 26 | #include <linux/poll.h> |
27 | #include <linux/spinlock.h> | ||
27 | 28 | ||
28 | struct gk20a; | 29 | struct gk20a; |
29 | struct gr_gk20a; | 30 | struct gr_gk20a; |
@@ -162,6 +163,7 @@ struct channel_gk20a { | |||
162 | * via schedule_work */ | 163 | * via schedule_work */ |
163 | void (*update_fn)(struct channel_gk20a *, void *); | 164 | void (*update_fn)(struct channel_gk20a *, void *); |
164 | void *update_fn_data; | 165 | void *update_fn_data; |
166 | spinlock_t update_fn_lock; /* make access to the two above atomic */ | ||
165 | struct work_struct update_fn_work; | 167 | struct work_struct update_fn_work; |
166 | }; | 168 | }; |
167 | 169 | ||