Commit 58256307 authored by Kanigeri, Hari's avatar Kanigeri, Hari Committed by Hari Kanigeri

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.htmlSigned-off-by: default avatarHari Kanigeri <h-kanigeri2@ti.com>
Signed-off-by: default avatarFernando Guzman Lugo <x0095840@ti.com>
Acked-by: default avatarHiroshi Doyu <hiroshi.doyu@nokia.com>
parent a42743c2
...@@ -46,7 +46,6 @@ struct omap_mbox_queue { ...@@ -46,7 +46,6 @@ struct omap_mbox_queue {
struct kfifo fifo; struct kfifo fifo;
struct work_struct work; struct work_struct work;
struct tasklet_struct tasklet; struct tasklet_struct tasklet;
int (*callback)(void *);
struct omap_mbox *mbox; struct omap_mbox *mbox;
bool full; bool full;
}; };
...@@ -58,13 +57,15 @@ struct omap_mbox { ...@@ -58,13 +57,15 @@ struct omap_mbox {
struct omap_mbox_ops *ops; struct omap_mbox_ops *ops;
struct device *dev; struct device *dev;
void *priv; void *priv;
int use_count;
struct blocking_notifier_head notifier;
}; };
int omap_mbox_msg_send(struct omap_mbox *, mbox_msg_t msg); int omap_mbox_msg_send(struct omap_mbox *, mbox_msg_t msg);
void omap_mbox_init_seq(struct omap_mbox *); void omap_mbox_init_seq(struct omap_mbox *);
struct omap_mbox *omap_mbox_get(const char *); struct omap_mbox *omap_mbox_get(const char *, struct notifier_block *nb);
void omap_mbox_put(struct omap_mbox *); void omap_mbox_put(struct omap_mbox *mbox, struct notifier_block *nb);
int omap_mbox_register(struct device *parent, struct omap_mbox **); int omap_mbox_register(struct device *parent, struct omap_mbox **);
int omap_mbox_unregister(void); int omap_mbox_unregister(void);
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/kfifo.h> #include <linux/kfifo.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/notifier.h>
#include <plat/mailbox.h> #include <plat/mailbox.h>
...@@ -150,8 +151,8 @@ static void mbox_rx_work(struct work_struct *work) ...@@ -150,8 +151,8 @@ static void mbox_rx_work(struct work_struct *work)
len = kfifo_out(&mq->fifo, (unsigned char *)&msg, sizeof(msg)); len = kfifo_out(&mq->fifo, (unsigned char *)&msg, sizeof(msg));
WARN_ON(len != sizeof(msg)); WARN_ON(len != sizeof(msg));
if (mq->callback) blocking_notifier_call_chain(&mq->mbox->notifier, len,
mq->callback((void *)msg); (void *)msg);
spin_lock_irq(&mq->lock); spin_lock_irq(&mq->lock);
if (mq->full) { if (mq->full) {
mq->full = false; mq->full = false;
...@@ -249,27 +250,24 @@ static int omap_mbox_startup(struct omap_mbox *mbox) ...@@ -249,27 +250,24 @@ static int omap_mbox_startup(struct omap_mbox *mbox)
int ret = 0; int ret = 0;
struct omap_mbox_queue *mq; struct omap_mbox_queue *mq;
if (mbox->ops->startup) {
mutex_lock(&mbox_configured_lock); mutex_lock(&mbox_configured_lock);
if (!mbox_configured) if (!mbox_configured++) {
if (likely(mbox->ops->startup)) {
ret = mbox->ops->startup(mbox); ret = mbox->ops->startup(mbox);
if (unlikely(ret))
if (ret) { goto fail_startup;
mutex_unlock(&mbox_configured_lock); } else
return ret; goto fail_startup;
}
mbox_configured++;
mutex_unlock(&mbox_configured_lock);
} }
if (!mbox->use_count++) {
ret = request_irq(mbox->irq, mbox_interrupt, IRQF_SHARED, ret = request_irq(mbox->irq, mbox_interrupt, IRQF_SHARED,
mbox->name, mbox); mbox->name, mbox);
if (ret) { if (unlikely(ret)) {
printk(KERN_ERR pr_err("failed to register mailbox interrupt:%d\n",
"failed to register mailbox interrupt:%d\n", ret); ret);
goto fail_request_irq; goto fail_request_irq;
} }
mq = mbox_queue_alloc(mbox, NULL, mbox_tx_tasklet); mq = mbox_queue_alloc(mbox, NULL, mbox_tx_tasklet);
if (!mq) { if (!mq) {
ret = -ENOMEM; ret = -ENOMEM;
...@@ -283,7 +281,9 @@ static int omap_mbox_startup(struct omap_mbox *mbox) ...@@ -283,7 +281,9 @@ static int omap_mbox_startup(struct omap_mbox *mbox)
goto fail_alloc_rxq; goto fail_alloc_rxq;
} }
mbox->rxq = mq; mbox->rxq = mq;
mq->mbox = mbox;
}
mutex_unlock(&mbox_configured_lock);
return 0; return 0;
fail_alloc_rxq: fail_alloc_rxq:
...@@ -293,29 +293,34 @@ static int omap_mbox_startup(struct omap_mbox *mbox) ...@@ -293,29 +293,34 @@ static int omap_mbox_startup(struct omap_mbox *mbox)
fail_request_irq: fail_request_irq:
if (mbox->ops->shutdown) if (mbox->ops->shutdown)
mbox->ops->shutdown(mbox); mbox->ops->shutdown(mbox);
mbox->use_count--;
fail_startup:
mbox_configured--;
mutex_unlock(&mbox_configured_lock);
return ret; return ret;
} }
static void omap_mbox_fini(struct omap_mbox *mbox) static void omap_mbox_fini(struct omap_mbox *mbox)
{ {
mutex_lock(&mbox_configured_lock);
if (!--mbox->use_count) {
free_irq(mbox->irq, mbox); free_irq(mbox->irq, mbox);
tasklet_kill(&mbox->txq->tasklet); tasklet_kill(&mbox->txq->tasklet);
flush_work(&mbox->rxq->work); flush_work(&mbox->rxq->work);
mbox_queue_free(mbox->txq); mbox_queue_free(mbox->txq);
mbox_queue_free(mbox->rxq); mbox_queue_free(mbox->rxq);
}
if (mbox->ops->shutdown) { if (likely(mbox->ops->shutdown)) {
mutex_lock(&mbox_configured_lock); if (!--mbox_configured)
if (mbox_configured > 0)
mbox_configured--;
if (!mbox_configured)
mbox->ops->shutdown(mbox); mbox->ops->shutdown(mbox);
mutex_unlock(&mbox_configured_lock);
} }
mutex_unlock(&mbox_configured_lock);
} }
struct omap_mbox *omap_mbox_get(const char *name) struct omap_mbox *omap_mbox_get(const char *name, struct notifier_block *nb)
{ {
struct omap_mbox *mbox; struct omap_mbox *mbox;
int ret; int ret;
...@@ -334,12 +339,16 @@ struct omap_mbox *omap_mbox_get(const char *name) ...@@ -334,12 +339,16 @@ struct omap_mbox *omap_mbox_get(const char *name)
if (ret) if (ret)
return ERR_PTR(-ENODEV); return ERR_PTR(-ENODEV);
if (nb)
blocking_notifier_chain_register(&mbox->notifier, nb);
return mbox; return mbox;
} }
EXPORT_SYMBOL(omap_mbox_get); EXPORT_SYMBOL(omap_mbox_get);
void omap_mbox_put(struct omap_mbox *mbox) void omap_mbox_put(struct omap_mbox *mbox, struct notifier_block *nb)
{ {
blocking_notifier_chain_unregister(&mbox->notifier, nb);
omap_mbox_fini(mbox); omap_mbox_fini(mbox);
} }
EXPORT_SYMBOL(omap_mbox_put); EXPORT_SYMBOL(omap_mbox_put);
...@@ -363,6 +372,8 @@ int omap_mbox_register(struct device *parent, struct omap_mbox **list) ...@@ -363,6 +372,8 @@ int omap_mbox_register(struct device *parent, struct omap_mbox **list)
ret = PTR_ERR(mbox->dev); ret = PTR_ERR(mbox->dev);
goto err_out; goto err_out;
} }
BLOCKING_INIT_NOTIFIER_HEAD(&mbox->notifier);
} }
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment