aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMikko Perttunen <mperttunen@nvidia.com>2017-06-14 19:18:42 -0400
committerThierry Reding <treding@nvidia.com>2017-06-15 08:25:38 -0400
commit8474b02531c4881a762c52ef869c52429e38633f (patch)
tree41cdb37fa4e424170bd7670e07ca0981c9f6c81f
parent03f0de770eda7a3f2e3950ca9f15d73e1dfd4596 (diff)
gpu: host1x: Refactor channel allocation code
This is largely a rewrite of the Host1x channel allocation code, bringing several changes: - The previous code could deadlock due to an interaction between the 'reflock' mutex and CDMA timeout handling. This gets rid of the mutex. - Support for more than 32 channels, required for Tegra186 - General refactoring, including better encapsulation of channel ownership handling into channel.c Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com> Reviewed-by: Dmitry Osipenko <digetx@gmail.com> Tested-by: Dmitry Osipenko <digetx@gmail.com> Signed-off-by: Thierry Reding <treding@nvidia.com>
-rw-r--r--drivers/gpu/drm/tegra/gr2d.c4
-rw-r--r--drivers/gpu/drm/tegra/gr3d.c4
-rw-r--r--drivers/gpu/drm/tegra/vic.c4
-rw-r--r--drivers/gpu/host1x/channel.c147
-rw-r--r--drivers/gpu/host1x/channel.h21
-rw-r--r--drivers/gpu/host1x/debug.c47
-rw-r--r--drivers/gpu/host1x/dev.c7
-rw-r--r--drivers/gpu/host1x/dev.h6
-rw-r--r--drivers/gpu/host1x/hw/channel_hw.c4
-rw-r--r--include/linux/host1x.h1
10 files changed, 135 insertions, 110 deletions
diff --git a/drivers/gpu/drm/tegra/gr2d.c b/drivers/gpu/drm/tegra/gr2d.c
index fbe0b8b25b42..6ea070da7718 100644
--- a/drivers/gpu/drm/tegra/gr2d.c
+++ b/drivers/gpu/drm/tegra/gr2d.c
@@ -38,7 +38,7 @@ static int gr2d_init(struct host1x_client *client)
38 38
39 client->syncpts[0] = host1x_syncpt_request(client->dev, flags); 39 client->syncpts[0] = host1x_syncpt_request(client->dev, flags);
40 if (!client->syncpts[0]) { 40 if (!client->syncpts[0]) {
41 host1x_channel_free(gr2d->channel); 41 host1x_channel_put(gr2d->channel);
42 return -ENOMEM; 42 return -ENOMEM;
43 } 43 }
44 44
@@ -57,7 +57,7 @@ static int gr2d_exit(struct host1x_client *client)
57 return err; 57 return err;
58 58
59 host1x_syncpt_free(client->syncpts[0]); 59 host1x_syncpt_free(client->syncpts[0]);
60 host1x_channel_free(gr2d->channel); 60 host1x_channel_put(gr2d->channel);
61 61
62 return 0; 62 return 0;
63} 63}
diff --git a/drivers/gpu/drm/tegra/gr3d.c b/drivers/gpu/drm/tegra/gr3d.c
index 13f0d1b7cd98..cee2ab645cde 100644
--- a/drivers/gpu/drm/tegra/gr3d.c
+++ b/drivers/gpu/drm/tegra/gr3d.c
@@ -48,7 +48,7 @@ static int gr3d_init(struct host1x_client *client)
48 48
49 client->syncpts[0] = host1x_syncpt_request(client->dev, flags); 49 client->syncpts[0] = host1x_syncpt_request(client->dev, flags);
50 if (!client->syncpts[0]) { 50 if (!client->syncpts[0]) {
51 host1x_channel_free(gr3d->channel); 51 host1x_channel_put(gr3d->channel);
52 return -ENOMEM; 52 return -ENOMEM;
53 } 53 }
54 54
@@ -67,7 +67,7 @@ static int gr3d_exit(struct host1x_client *client)
67 return err; 67 return err;
68 68
69 host1x_syncpt_free(client->syncpts[0]); 69 host1x_syncpt_free(client->syncpts[0]);
70 host1x_channel_free(gr3d->channel); 70 host1x_channel_put(gr3d->channel);
71 71
72 return 0; 72 return 0;
73} 73}
diff --git a/drivers/gpu/drm/tegra/vic.c b/drivers/gpu/drm/tegra/vic.c
index cd804e404a11..47cb1aaa58b1 100644
--- a/drivers/gpu/drm/tegra/vic.c
+++ b/drivers/gpu/drm/tegra/vic.c
@@ -182,7 +182,7 @@ static int vic_init(struct host1x_client *client)
182free_syncpt: 182free_syncpt:
183 host1x_syncpt_free(client->syncpts[0]); 183 host1x_syncpt_free(client->syncpts[0]);
184free_channel: 184free_channel:
185 host1x_channel_free(vic->channel); 185 host1x_channel_put(vic->channel);
186detach_device: 186detach_device:
187 if (tegra->domain) 187 if (tegra->domain)
188 iommu_detach_device(tegra->domain, vic->dev); 188 iommu_detach_device(tegra->domain, vic->dev);
@@ -203,7 +203,7 @@ static int vic_exit(struct host1x_client *client)
203 return err; 203 return err;
204 204
205 host1x_syncpt_free(client->syncpts[0]); 205 host1x_syncpt_free(client->syncpts[0]);
206 host1x_channel_free(vic->channel); 206 host1x_channel_put(vic->channel);
207 207
208 if (vic->domain) { 208 if (vic->domain) {
209 iommu_detach_device(vic->domain, vic->dev); 209 iommu_detach_device(vic->domain, vic->dev);
diff --git a/drivers/gpu/host1x/channel.c b/drivers/gpu/host1x/channel.c
index 8f437d924c10..db9b91d1384c 100644
--- a/drivers/gpu/host1x/channel.c
+++ b/drivers/gpu/host1x/channel.c
@@ -24,19 +24,33 @@
24#include "job.h" 24#include "job.h"
25 25
26/* Constructor for the host1x device list */ 26/* Constructor for the host1x device list */
27int host1x_channel_list_init(struct host1x *host) 27int host1x_channel_list_init(struct host1x_channel_list *chlist,
28 unsigned int num_channels)
28{ 29{
29 INIT_LIST_HEAD(&host->chlist.list); 30 chlist->channels = kcalloc(num_channels, sizeof(struct host1x_channel),
30 mutex_init(&host->chlist_mutex); 31 GFP_KERNEL);
31 32 if (!chlist->channels)
32 if (host->info->nb_channels > BITS_PER_LONG) { 33 return -ENOMEM;
33 WARN(1, "host1x hardware has more channels than supported by the driver\n"); 34
34 return -ENOSYS; 35 chlist->allocated_channels =
36 kcalloc(BITS_TO_LONGS(num_channels), sizeof(unsigned long),
37 GFP_KERNEL);
38 if (!chlist->allocated_channels) {
39 kfree(chlist->channels);
40 return -ENOMEM;
35 } 41 }
36 42
43 bitmap_zero(chlist->allocated_channels, num_channels);
44
37 return 0; 45 return 0;
38} 46}
39 47
48void host1x_channel_list_free(struct host1x_channel_list *chlist)
49{
50 kfree(chlist->allocated_channels);
51 kfree(chlist->channels);
52}
53
40int host1x_job_submit(struct host1x_job *job) 54int host1x_job_submit(struct host1x_job *job)
41{ 55{
42 struct host1x *host = dev_get_drvdata(job->channel->dev->parent); 56 struct host1x *host = dev_get_drvdata(job->channel->dev->parent);
@@ -47,86 +61,107 @@ EXPORT_SYMBOL(host1x_job_submit);
47 61
48struct host1x_channel *host1x_channel_get(struct host1x_channel *channel) 62struct host1x_channel *host1x_channel_get(struct host1x_channel *channel)
49{ 63{
50 int err = 0; 64 kref_get(&channel->refcount);
51 65
52 mutex_lock(&channel->reflock); 66 return channel;
67}
68EXPORT_SYMBOL(host1x_channel_get);
53 69
54 if (channel->refcount == 0) 70/**
55 err = host1x_cdma_init(&channel->cdma); 71 * host1x_channel_get_index() - Attempt to get channel reference by index
72 * @host: Host1x device object
73 * @index: Index of channel
74 *
75 * If channel number @index is currently allocated, increase its refcount
76 * and return a pointer to it. Otherwise, return NULL.
77 */
78struct host1x_channel *host1x_channel_get_index(struct host1x *host,
79 unsigned int index)
80{
81 struct host1x_channel *ch = &host->channel_list.channels[index];
56 82
57 if (!err) 83 if (!kref_get_unless_zero(&ch->refcount))
58 channel->refcount++; 84 return NULL;
59 85
60 mutex_unlock(&channel->reflock); 86 return ch;
87}
88
89static void release_channel(struct kref *kref)
90{
91 struct host1x_channel *channel =
92 container_of(kref, struct host1x_channel, refcount);
93 struct host1x *host = dev_get_drvdata(channel->dev->parent);
94 struct host1x_channel_list *chlist = &host->channel_list;
95
96 host1x_hw_cdma_stop(host, &channel->cdma);
97 host1x_cdma_deinit(&channel->cdma);
61 98
62 return err ? NULL : channel; 99 clear_bit(channel->id, chlist->allocated_channels);
63} 100}
64EXPORT_SYMBOL(host1x_channel_get);
65 101
66void host1x_channel_put(struct host1x_channel *channel) 102void host1x_channel_put(struct host1x_channel *channel)
67{ 103{
68 mutex_lock(&channel->reflock); 104 kref_put(&channel->refcount, release_channel);
105}
106EXPORT_SYMBOL(host1x_channel_put);
69 107
70 if (channel->refcount == 1) { 108static struct host1x_channel *acquire_unused_channel(struct host1x *host)
71 struct host1x *host = dev_get_drvdata(channel->dev->parent); 109{
110 struct host1x_channel_list *chlist = &host->channel_list;
111 unsigned int max_channels = host->info->nb_channels;
112 unsigned int index;
72 113
73 host1x_hw_cdma_stop(host, &channel->cdma); 114 index = find_first_zero_bit(chlist->allocated_channels, max_channels);
74 host1x_cdma_deinit(&channel->cdma); 115 if (index >= max_channels) {
116 dev_err(host->dev, "failed to find free channel\n");
117 return NULL;
75 } 118 }
76 119
77 channel->refcount--; 120 chlist->channels[index].id = index;
78 121
79 mutex_unlock(&channel->reflock); 122 set_bit(index, chlist->allocated_channels);
123
124 return &chlist->channels[index];
80} 125}
81EXPORT_SYMBOL(host1x_channel_put);
82 126
127/**
128 * host1x_channel_request() - Allocate a channel
129 * @device: Host1x unit this channel will be used to send commands to
130 *
131 * Allocates a new host1x channel for @device. If there are no free channels,
132 * this will sleep until one becomes available. May return NULL if CDMA
133 * initialization fails.
134 */
83struct host1x_channel *host1x_channel_request(struct device *dev) 135struct host1x_channel *host1x_channel_request(struct device *dev)
84{ 136{
85 struct host1x *host = dev_get_drvdata(dev->parent); 137 struct host1x *host = dev_get_drvdata(dev->parent);
86 unsigned int max_channels = host->info->nb_channels; 138 struct host1x_channel_list *chlist = &host->channel_list;
87 struct host1x_channel *channel = NULL; 139 struct host1x_channel *channel;
88 unsigned long index;
89 int err; 140 int err;
90 141
91 mutex_lock(&host->chlist_mutex); 142 channel = acquire_unused_channel(host);
143 if (!channel)
144 return NULL;
92 145
93 index = find_first_zero_bit(&host->allocated_channels, max_channels); 146 kref_init(&channel->refcount);
94 if (index >= max_channels) 147 mutex_init(&channel->submitlock);
95 goto fail; 148 channel->dev = dev;
96 149
97 channel = kzalloc(sizeof(*channel), GFP_KERNEL); 150 err = host1x_hw_channel_init(host, channel, channel->id);
98 if (!channel) 151 if (err < 0)
99 goto fail; 152 goto fail;
100 153
101 err = host1x_hw_channel_init(host, channel, index); 154 err = host1x_cdma_init(&channel->cdma);
102 if (err < 0) 155 if (err < 0)
103 goto fail; 156 goto fail;
104 157
105 /* Link device to host1x_channel */
106 channel->dev = dev;
107
108 /* Add to channel list */
109 list_add_tail(&channel->list, &host->chlist.list);
110
111 host->allocated_channels |= BIT(index);
112
113 mutex_unlock(&host->chlist_mutex);
114 return channel; 158 return channel;
115 159
116fail: 160fail:
117 dev_err(dev, "failed to init channel\n"); 161 clear_bit(channel->id, chlist->allocated_channels);
118 kfree(channel);
119 mutex_unlock(&host->chlist_mutex);
120 return NULL;
121}
122EXPORT_SYMBOL(host1x_channel_request);
123 162
124void host1x_channel_free(struct host1x_channel *channel) 163 dev_err(dev, "failed to initialize channel\n");
125{
126 struct host1x *host = dev_get_drvdata(channel->dev->parent);
127 164
128 host->allocated_channels &= ~BIT(channel->id); 165 return NULL;
129 list_del(&channel->list);
130 kfree(channel);
131} 166}
132EXPORT_SYMBOL(host1x_channel_free); 167EXPORT_SYMBOL(host1x_channel_request);
diff --git a/drivers/gpu/host1x/channel.h b/drivers/gpu/host1x/channel.h
index df767cf90d51..7068e42d42df 100644
--- a/drivers/gpu/host1x/channel.h
+++ b/drivers/gpu/host1x/channel.h
@@ -20,17 +20,21 @@
20#define __HOST1X_CHANNEL_H 20#define __HOST1X_CHANNEL_H
21 21
22#include <linux/io.h> 22#include <linux/io.h>
23#include <linux/kref.h>
23 24
24#include "cdma.h" 25#include "cdma.h"
25 26
26struct host1x; 27struct host1x;
28struct host1x_channel;
27 29
28struct host1x_channel { 30struct host1x_channel_list {
29 struct list_head list; 31 struct host1x_channel *channels;
32 unsigned long *allocated_channels;
33};
30 34
31 unsigned int refcount; 35struct host1x_channel {
36 struct kref refcount;
32 unsigned int id; 37 unsigned int id;
33 struct mutex reflock;
34 struct mutex submitlock; 38 struct mutex submitlock;
35 void __iomem *regs; 39 void __iomem *regs;
36 struct device *dev; 40 struct device *dev;
@@ -38,9 +42,10 @@ struct host1x_channel {
38}; 42};
39 43
40/* channel list operations */ 44/* channel list operations */
41int host1x_channel_list_init(struct host1x *host); 45int host1x_channel_list_init(struct host1x_channel_list *chlist,
42 46 unsigned int num_channels);
43#define host1x_for_each_channel(host, channel) \ 47void host1x_channel_list_free(struct host1x_channel_list *chlist);
44 list_for_each_entry(channel, &host->chlist.list, list) 48struct host1x_channel *host1x_channel_get_index(struct host1x *host,
49 unsigned int index);
45 50
46#endif 51#endif
diff --git a/drivers/gpu/host1x/debug.c b/drivers/gpu/host1x/debug.c
index d9330fcc62ad..2aae0e63214c 100644
--- a/drivers/gpu/host1x/debug.c
+++ b/drivers/gpu/host1x/debug.c
@@ -43,24 +43,19 @@ void host1x_debug_output(struct output *o, const char *fmt, ...)
43 o->fn(o->ctx, o->buf, len); 43 o->fn(o->ctx, o->buf, len);
44} 44}
45 45
46static int show_channels(struct host1x_channel *ch, void *data, bool show_fifo) 46static int show_channel(struct host1x_channel *ch, void *data, bool show_fifo)
47{ 47{
48 struct host1x *m = dev_get_drvdata(ch->dev->parent); 48 struct host1x *m = dev_get_drvdata(ch->dev->parent);
49 struct output *o = data; 49 struct output *o = data;
50 50
51 mutex_lock(&ch->reflock); 51 mutex_lock(&ch->cdma.lock);
52 52
53 if (ch->refcount) { 53 if (show_fifo)
54 mutex_lock(&ch->cdma.lock); 54 host1x_hw_show_channel_fifo(m, ch, o);
55 55
56 if (show_fifo) 56 host1x_hw_show_channel_cdma(m, ch, o);
57 host1x_hw_show_channel_fifo(m, ch, o);
58 57
59 host1x_hw_show_channel_cdma(m, ch, o); 58 mutex_unlock(&ch->cdma.lock);
60 mutex_unlock(&ch->cdma.lock);
61 }
62
63 mutex_unlock(&ch->reflock);
64 59
65 return 0; 60 return 0;
66} 61}
@@ -94,28 +89,22 @@ static void show_syncpts(struct host1x *m, struct output *o)
94 host1x_debug_output(o, "\n"); 89 host1x_debug_output(o, "\n");
95} 90}
96 91
97static void show_all(struct host1x *m, struct output *o) 92static void show_all(struct host1x *m, struct output *o, bool show_fifo)
98{ 93{
99 struct host1x_channel *ch; 94 int i;
100 95
101 host1x_hw_show_mlocks(m, o); 96 host1x_hw_show_mlocks(m, o);
102 show_syncpts(m, o); 97 show_syncpts(m, o);
103 host1x_debug_output(o, "---- channels ----\n"); 98 host1x_debug_output(o, "---- channels ----\n");
104 99
105 host1x_for_each_channel(m, ch) 100 for (i = 0; i < m->info->nb_channels; ++i) {
106 show_channels(ch, o, true); 101 struct host1x_channel *ch = host1x_channel_get_index(m, i);
107}
108
109static void show_all_no_fifo(struct host1x *host1x, struct output *o)
110{
111 struct host1x_channel *ch;
112
113 host1x_hw_show_mlocks(host1x, o);
114 show_syncpts(host1x, o);
115 host1x_debug_output(o, "---- channels ----\n");
116 102
117 host1x_for_each_channel(host1x, ch) 103 if (ch) {
118 show_channels(ch, o, false); 104 show_channel(ch, o, show_fifo);
105 host1x_channel_put(ch);
106 }
107 }
119} 108}
120 109
121static int host1x_debug_show_all(struct seq_file *s, void *unused) 110static int host1x_debug_show_all(struct seq_file *s, void *unused)
@@ -125,7 +114,7 @@ static int host1x_debug_show_all(struct seq_file *s, void *unused)
125 .ctx = s 114 .ctx = s
126 }; 115 };
127 116
128 show_all(s->private, &o); 117 show_all(s->private, &o, true);
129 118
130 return 0; 119 return 0;
131} 120}
@@ -137,7 +126,7 @@ static int host1x_debug_show(struct seq_file *s, void *unused)
137 .ctx = s 126 .ctx = s
138 }; 127 };
139 128
140 show_all_no_fifo(s->private, &o); 129 show_all(s->private, &o, false);
141 130
142 return 0; 131 return 0;
143} 132}
@@ -216,7 +205,7 @@ void host1x_debug_dump(struct host1x *host1x)
216 .fn = write_to_printk 205 .fn = write_to_printk
217 }; 206 };
218 207
219 show_all(host1x, &o); 208 show_all(host1x, &o, true);
220} 209}
221 210
222void host1x_debug_dump_syncpts(struct host1x *host1x) 211void host1x_debug_dump_syncpts(struct host1x *host1x)
diff --git a/drivers/gpu/host1x/dev.c b/drivers/gpu/host1x/dev.c
index f05ebb14fa63..5c1c711a21af 100644
--- a/drivers/gpu/host1x/dev.c
+++ b/drivers/gpu/host1x/dev.c
@@ -198,7 +198,8 @@ static int host1x_probe(struct platform_device *pdev)
198 host->iova_end = geometry->aperture_end; 198 host->iova_end = geometry->aperture_end;
199 } 199 }
200 200
201 err = host1x_channel_list_init(host); 201 err = host1x_channel_list_init(&host->channel_list,
202 host->info->nb_channels);
202 if (err) { 203 if (err) {
203 dev_err(&pdev->dev, "failed to initialize channel list\n"); 204 dev_err(&pdev->dev, "failed to initialize channel list\n");
204 goto fail_detach_device; 205 goto fail_detach_device;
@@ -207,7 +208,7 @@ static int host1x_probe(struct platform_device *pdev)
207 err = clk_prepare_enable(host->clk); 208 err = clk_prepare_enable(host->clk);
208 if (err < 0) { 209 if (err < 0) {
209 dev_err(&pdev->dev, "failed to enable clock\n"); 210 dev_err(&pdev->dev, "failed to enable clock\n");
210 goto fail_detach_device; 211 goto fail_free_channels;
211 } 212 }
212 213
213 err = reset_control_deassert(host->rst); 214 err = reset_control_deassert(host->rst);
@@ -244,6 +245,8 @@ fail_reset_assert:
244 reset_control_assert(host->rst); 245 reset_control_assert(host->rst);
245fail_unprepare_disable: 246fail_unprepare_disable:
246 clk_disable_unprepare(host->clk); 247 clk_disable_unprepare(host->clk);
248fail_free_channels:
249 host1x_channel_list_free(&host->channel_list);
247fail_detach_device: 250fail_detach_device:
248 if (host->domain) { 251 if (host->domain) {
249 put_iova_domain(&host->iova); 252 put_iova_domain(&host->iova);
diff --git a/drivers/gpu/host1x/dev.h b/drivers/gpu/host1x/dev.h
index 229d08b6a45e..ffdbc15b749b 100644
--- a/drivers/gpu/host1x/dev.h
+++ b/drivers/gpu/host1x/dev.h
@@ -129,10 +129,8 @@ struct host1x {
129 struct host1x_syncpt *nop_sp; 129 struct host1x_syncpt *nop_sp;
130 130
131 struct mutex syncpt_mutex; 131 struct mutex syncpt_mutex;
132 struct mutex chlist_mutex; 132
133 struct host1x_channel chlist; 133 struct host1x_channel_list channel_list;
134 unsigned long allocated_channels;
135 unsigned int num_allocated_channels;
136 134
137 struct dentry *debugfs; 135 struct dentry *debugfs;
138 136
diff --git a/drivers/gpu/host1x/hw/channel_hw.c b/drivers/gpu/host1x/hw/channel_hw.c
index 5e8df78b7acd..8447a56c41ca 100644
--- a/drivers/gpu/host1x/hw/channel_hw.c
+++ b/drivers/gpu/host1x/hw/channel_hw.c
@@ -181,10 +181,6 @@ error:
181static int host1x_channel_init(struct host1x_channel *ch, struct host1x *dev, 181static int host1x_channel_init(struct host1x_channel *ch, struct host1x *dev,
182 unsigned int index) 182 unsigned int index)
183{ 183{
184 ch->id = index;
185 mutex_init(&ch->reflock);
186 mutex_init(&ch->submitlock);
187
188 ch->regs = dev->regs + index * HOST1X_CHANNEL_SIZE; 184 ch->regs = dev->regs + index * HOST1X_CHANNEL_SIZE;
189 return 0; 185 return 0;
190} 186}
diff --git a/include/linux/host1x.h b/include/linux/host1x.h
index 476da0e06bb2..630b1a98ab58 100644
--- a/include/linux/host1x.h
+++ b/include/linux/host1x.h
@@ -172,7 +172,6 @@ struct host1x_channel;
172struct host1x_job; 172struct host1x_job;
173 173
174struct host1x_channel *host1x_channel_request(struct device *dev); 174struct host1x_channel *host1x_channel_request(struct device *dev);
175void host1x_channel_free(struct host1x_channel *channel);
176struct host1x_channel *host1x_channel_get(struct host1x_channel *channel); 175struct host1x_channel *host1x_channel_get(struct host1x_channel *channel);
177void host1x_channel_put(struct host1x_channel *channel); 176void host1x_channel_put(struct host1x_channel *channel);
178int host1x_job_submit(struct host1x_job *job); 177int host1x_job_submit(struct host1x_job *job);