diff options
author | Kanigeri, Hari <h-kanigeri2@ti.com> | 2010-11-29 15:24:14 -0500 |
---|---|---|
committer | Hari Kanigeri <h-kanigeri2@ti.com> | 2010-12-02 06:43:16 -0500 |
commit | 582563074a691eb45cb22d2eca70eed8f2091c5f (patch) | |
tree | a10cfb02f1dd2c35f773d7892e6a8f75c3c7544c | |
parent | a42743c26a53a2a5f2b2018a9659ab3fb604d5bc (diff) |
OMAP: mailbox: add notification support for multiple readers
In the current mailbox driver, the mailbox internal pointer for
callback can be directly manipulated by the Users, so a second
User can easily corrupt the first user's callback pointer.
The initial effort to correct this issue can be referred here:
https://patchwork.kernel.org/patch/107520/
Along with fixing the above stated issue, this patch adds the
flexibility option to register notifications from
multiple readers to the events received on a mailbox instance.
The discussion regarding this can be referred here.
http://www.mail-archive.com/linux-omap@vger.kernel.org/msg30671.html
Signed-off-by: Hari Kanigeri <h-kanigeri2@ti.com>
Signed-off-by: Fernando Guzman Lugo <x0095840@ti.com>
Acked-by: Hiroshi Doyu <hiroshi.doyu@nokia.com>
-rw-r--r-- | arch/arm/plat-omap/include/plat/mailbox.h | 7 | ||||
-rw-r--r-- | arch/arm/plat-omap/mailbox.c | 103 |
2 files changed, 61 insertions, 49 deletions
diff --git a/arch/arm/plat-omap/include/plat/mailbox.h b/arch/arm/plat-omap/include/plat/mailbox.h index 13f2ef3ea0ff..cc3921e9059c 100644 --- a/arch/arm/plat-omap/include/plat/mailbox.h +++ b/arch/arm/plat-omap/include/plat/mailbox.h | |||
@@ -46,7 +46,6 @@ struct omap_mbox_queue { | |||
46 | struct kfifo fifo; | 46 | struct kfifo fifo; |
47 | struct work_struct work; | 47 | struct work_struct work; |
48 | struct tasklet_struct tasklet; | 48 | struct tasklet_struct tasklet; |
49 | int (*callback)(void *); | ||
50 | struct omap_mbox *mbox; | 49 | struct omap_mbox *mbox; |
51 | bool full; | 50 | bool full; |
52 | }; | 51 | }; |
@@ -58,13 +57,15 @@ struct omap_mbox { | |||
58 | struct omap_mbox_ops *ops; | 57 | struct omap_mbox_ops *ops; |
59 | struct device *dev; | 58 | struct device *dev; |
60 | void *priv; | 59 | void *priv; |
60 | int use_count; | ||
61 | struct blocking_notifier_head notifier; | ||
61 | }; | 62 | }; |
62 | 63 | ||
63 | int omap_mbox_msg_send(struct omap_mbox *, mbox_msg_t msg); | 64 | int omap_mbox_msg_send(struct omap_mbox *, mbox_msg_t msg); |
64 | void omap_mbox_init_seq(struct omap_mbox *); | 65 | void omap_mbox_init_seq(struct omap_mbox *); |
65 | 66 | ||
66 | struct omap_mbox *omap_mbox_get(const char *); | 67 | struct omap_mbox *omap_mbox_get(const char *, struct notifier_block *nb); |
67 | void omap_mbox_put(struct omap_mbox *); | 68 | void omap_mbox_put(struct omap_mbox *mbox, struct notifier_block *nb); |
68 | 69 | ||
69 | int omap_mbox_register(struct device *parent, struct omap_mbox **); | 70 | int omap_mbox_register(struct device *parent, struct omap_mbox **); |
70 | int omap_mbox_unregister(void); | 71 | int omap_mbox_unregister(void); |
diff --git a/arch/arm/plat-omap/mailbox.c b/arch/arm/plat-omap/mailbox.c index cc58b44325f2..459b319a9fad 100644 --- a/arch/arm/plat-omap/mailbox.c +++ b/arch/arm/plat-omap/mailbox.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #include <linux/slab.h> | 28 | #include <linux/slab.h> |
29 | #include <linux/kfifo.h> | 29 | #include <linux/kfifo.h> |
30 | #include <linux/err.h> | 30 | #include <linux/err.h> |
31 | #include <linux/notifier.h> | ||
31 | 32 | ||
32 | #include <plat/mailbox.h> | 33 | #include <plat/mailbox.h> |
33 | 34 | ||
@@ -150,8 +151,8 @@ static void mbox_rx_work(struct work_struct *work) | |||
150 | len = kfifo_out(&mq->fifo, (unsigned char *)&msg, sizeof(msg)); | 151 | len = kfifo_out(&mq->fifo, (unsigned char *)&msg, sizeof(msg)); |
151 | WARN_ON(len != sizeof(msg)); | 152 | WARN_ON(len != sizeof(msg)); |
152 | 153 | ||
153 | if (mq->callback) | 154 | blocking_notifier_call_chain(&mq->mbox->notifier, len, |
154 | mq->callback((void *)msg); | 155 | (void *)msg); |
155 | spin_lock_irq(&mq->lock); | 156 | spin_lock_irq(&mq->lock); |
156 | if (mq->full) { | 157 | if (mq->full) { |
157 | mq->full = false; | 158 | mq->full = false; |
@@ -249,41 +250,40 @@ static int omap_mbox_startup(struct omap_mbox *mbox) | |||
249 | int ret = 0; | 250 | int ret = 0; |
250 | struct omap_mbox_queue *mq; | 251 | struct omap_mbox_queue *mq; |
251 | 252 | ||
252 | if (mbox->ops->startup) { | 253 | mutex_lock(&mbox_configured_lock); |
253 | mutex_lock(&mbox_configured_lock); | 254 | if (!mbox_configured++) { |
254 | if (!mbox_configured) | 255 | if (likely(mbox->ops->startup)) { |
255 | ret = mbox->ops->startup(mbox); | 256 | ret = mbox->ops->startup(mbox); |
256 | 257 | if (unlikely(ret)) | |
257 | if (ret) { | 258 | goto fail_startup; |
258 | mutex_unlock(&mbox_configured_lock); | 259 | } else |
259 | return ret; | 260 | goto fail_startup; |
260 | } | ||
261 | mbox_configured++; | ||
262 | mutex_unlock(&mbox_configured_lock); | ||
263 | } | 261 | } |
264 | 262 | ||
265 | ret = request_irq(mbox->irq, mbox_interrupt, IRQF_SHARED, | 263 | if (!mbox->use_count++) { |
266 | mbox->name, mbox); | 264 | ret = request_irq(mbox->irq, mbox_interrupt, IRQF_SHARED, |
267 | if (ret) { | 265 | mbox->name, mbox); |
268 | printk(KERN_ERR | 266 | if (unlikely(ret)) { |
269 | "failed to register mailbox interrupt:%d\n", ret); | 267 | pr_err("failed to register mailbox interrupt:%d\n", |
270 | goto fail_request_irq; | 268 | ret); |
271 | } | 269 | goto fail_request_irq; |
272 | 270 | } | |
273 | mq = mbox_queue_alloc(mbox, NULL, mbox_tx_tasklet); | 271 | mq = mbox_queue_alloc(mbox, NULL, mbox_tx_tasklet); |
274 | if (!mq) { | 272 | if (!mq) { |
275 | ret = -ENOMEM; | 273 | ret = -ENOMEM; |
276 | goto fail_alloc_txq; | 274 | goto fail_alloc_txq; |
277 | } | 275 | } |
278 | mbox->txq = mq; | 276 | mbox->txq = mq; |
279 | 277 | ||
280 | mq = mbox_queue_alloc(mbox, mbox_rx_work, NULL); | 278 | mq = mbox_queue_alloc(mbox, mbox_rx_work, NULL); |
281 | if (!mq) { | 279 | if (!mq) { |
282 | ret = -ENOMEM; | 280 | ret = -ENOMEM; |
283 | goto fail_alloc_rxq; | 281 | goto fail_alloc_rxq; |
282 | } | ||
283 | mbox->rxq = mq; | ||
284 | mq->mbox = mbox; | ||
284 | } | 285 | } |
285 | mbox->rxq = mq; | 286 | mutex_unlock(&mbox_configured_lock); |
286 | |||
287 | return 0; | 287 | return 0; |
288 | 288 | ||
289 | fail_alloc_rxq: | 289 | fail_alloc_rxq: |
@@ -293,29 +293,34 @@ fail_alloc_txq: | |||
293 | fail_request_irq: | 293 | fail_request_irq: |
294 | if (mbox->ops->shutdown) | 294 | if (mbox->ops->shutdown) |
295 | mbox->ops->shutdown(mbox); | 295 | mbox->ops->shutdown(mbox); |
296 | 296 | mbox->use_count--; | |
297 | fail_startup: | ||
298 | mbox_configured--; | ||
299 | mutex_unlock(&mbox_configured_lock); | ||
297 | return ret; | 300 | return ret; |
298 | } | 301 | } |
299 | 302 | ||
300 | static void omap_mbox_fini(struct omap_mbox *mbox) | 303 | static void omap_mbox_fini(struct omap_mbox *mbox) |
301 | { | 304 | { |
302 | free_irq(mbox->irq, mbox); | 305 | mutex_lock(&mbox_configured_lock); |
303 | tasklet_kill(&mbox->txq->tasklet); | 306 | |
304 | flush_work(&mbox->rxq->work); | 307 | if (!--mbox->use_count) { |
305 | mbox_queue_free(mbox->txq); | 308 | free_irq(mbox->irq, mbox); |
306 | mbox_queue_free(mbox->rxq); | 309 | tasklet_kill(&mbox->txq->tasklet); |
310 | flush_work(&mbox->rxq->work); | ||
311 | mbox_queue_free(mbox->txq); | ||
312 | mbox_queue_free(mbox->rxq); | ||
313 | } | ||
307 | 314 | ||
308 | if (mbox->ops->shutdown) { | 315 | if (likely(mbox->ops->shutdown)) { |
309 | mutex_lock(&mbox_configured_lock); | 316 | if (!--mbox_configured) |
310 | if (mbox_configured > 0) | ||
311 | mbox_configured--; | ||
312 | if (!mbox_configured) | ||
313 | mbox->ops->shutdown(mbox); | 317 | mbox->ops->shutdown(mbox); |
314 | mutex_unlock(&mbox_configured_lock); | ||
315 | } | 318 | } |
319 | |||
320 | mutex_unlock(&mbox_configured_lock); | ||
316 | } | 321 | } |
317 | 322 | ||
318 | struct omap_mbox *omap_mbox_get(const char *name) | 323 | struct omap_mbox *omap_mbox_get(const char *name, struct notifier_block *nb) |
319 | { | 324 | { |
320 | struct omap_mbox *mbox; | 325 | struct omap_mbox *mbox; |
321 | int ret; | 326 | int ret; |
@@ -334,12 +339,16 @@ struct omap_mbox *omap_mbox_get(const char *name) | |||
334 | if (ret) | 339 | if (ret) |
335 | return ERR_PTR(-ENODEV); | 340 | return ERR_PTR(-ENODEV); |
336 | 341 | ||
342 | if (nb) | ||
343 | blocking_notifier_chain_register(&mbox->notifier, nb); | ||
344 | |||
337 | return mbox; | 345 | return mbox; |
338 | } | 346 | } |
339 | EXPORT_SYMBOL(omap_mbox_get); | 347 | EXPORT_SYMBOL(omap_mbox_get); |
340 | 348 | ||
341 | void omap_mbox_put(struct omap_mbox *mbox) | 349 | void omap_mbox_put(struct omap_mbox *mbox, struct notifier_block *nb) |
342 | { | 350 | { |
351 | blocking_notifier_chain_unregister(&mbox->notifier, nb); | ||
343 | omap_mbox_fini(mbox); | 352 | omap_mbox_fini(mbox); |
344 | } | 353 | } |
345 | EXPORT_SYMBOL(omap_mbox_put); | 354 | EXPORT_SYMBOL(omap_mbox_put); |
@@ -363,6 +372,8 @@ int omap_mbox_register(struct device *parent, struct omap_mbox **list) | |||
363 | ret = PTR_ERR(mbox->dev); | 372 | ret = PTR_ERR(mbox->dev); |
364 | goto err_out; | 373 | goto err_out; |
365 | } | 374 | } |
375 | |||
376 | BLOCKING_INIT_NOTIFIER_HEAD(&mbox->notifier); | ||
366 | } | 377 | } |
367 | return 0; | 378 | return 0; |
368 | 379 | ||