aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/rpmsg
diff options
context:
space:
mode:
authorSuman Anna <s-anna@ti.com>2011-08-24 11:41:19 -0700
committerIliyan Malchev <malchev@google.com>2011-08-26 14:51:32 -0700
commita0312e9a048a45cbdb3e6a81e221153309c482b8 (patch)
treea7e735e615b36c3d91e39dc8104bc070951838ab /drivers/rpmsg
parentb3b52ae2340fd47bbf7798de7a52302761ff6a2b (diff)
downloadkernel_samsung_espresso10-a0312e9a048a45cbdb3e6a81e221153309c482b8.zip
kernel_samsung_espresso10-a0312e9a048a45cbdb3e6a81e221153309c482b8.tar.gz
kernel_samsung_espresso10-a0312e9a048a45cbdb3e6a81e221153309c482b8.tar.bz2
rpmsg: serialize transmissions and add proper barriers
The rpmsg_send_offchannel_raw delivers messages to the remote processor by getting a vring buffer, copying the necessary payload and making it available to the remote processor. These three steps need to be atomic to ensure serialized delivery of messages to the remote processor. Memory barriers have also been added to the virtio_rpmsg_bus driver, since this driver uses the virtio framework across different processor sub-systems and the internal barriers in virtio driver are better suited to work within a SMP environment. TODO: The wait logic when no buffer is available needs to be moved out from this function. This code path currently is never reached. Change-Id: I91685ffbccc6769f60e77a2a7106c3a9daa55b69 Signed-off-by: Suman Anna <s-anna@ti.com> Signed-off-by: Iliyan Malchev <malchev@google.com>
Diffstat (limited to 'drivers/rpmsg')
-rw-r--r--drivers/rpmsg/virtio_rpmsg_bus.c36
1 files changed, 24 insertions, 12 deletions
diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c
index 33d3a49..bd6408a 100644
--- a/drivers/rpmsg/virtio_rpmsg_bus.c
+++ b/drivers/rpmsg/virtio_rpmsg_bus.c
@@ -420,9 +420,8 @@ static void *get_a_buf(struct virtproc_info *vrp)
unsigned int len;
void *buf = NULL;
- /* protect svq from simultaneous concurrent manipulations */
- mutex_lock(&vrp->svq_lock);
-
+ /* make sure the descriptors are updated before reading */
+ rmb();
/* either pick the next unused buffer */
if (vrp->last_sbuf < vrp->num_bufs / 2)
buf = vrp->sbufs + vrp->buf_size * vrp->last_sbuf++;
@@ -430,7 +429,6 @@ static void *get_a_buf(struct virtproc_info *vrp)
else
buf = virtqueue_get_buf(vrp->svq, &len);
- mutex_unlock(&vrp->svq_lock);
return buf;
}
@@ -457,10 +455,18 @@ int rpmsg_send_offchannel_raw(struct rpmsg_channel *rpdev, u32 src, u32 dst,
return -EMSGSIZE;
}
+ /*
+ * protect svq from simultaneous concurrent manipulations,
+ * and serialize the sending of messages
+ */
+ if (mutex_lock_interruptible(&vrp->svq_lock))
+ return -ERESTARTSYS;
/* grab a buffer */
msg = get_a_buf(vrp);
- if (!msg && !wait)
- return -ENOMEM;
+ if (!msg && !wait) {
+ err = -ENOMEM;
+ goto out;
+ }
/* no free buffer ? wait for one (but bail after 15 seconds) */
if (!msg) {
@@ -480,12 +486,15 @@ int rpmsg_send_offchannel_raw(struct rpmsg_channel *rpdev, u32 src, u32 dst,
/* on success, suppress "tx-complete" interrupts again */
virtqueue_disable_cb(vrp->svq);
- if (err < 0)
- return -ERESTARTSYS;
+ if (err < 0) {
+ err = -ERESTARTSYS;
+ goto out;
+ }
if (!msg) {
dev_err(dev, "timeout waiting for buffer\n");
- return -ETIMEDOUT;
+ err = -ETIMEDOUT;
+ goto out;
}
}
@@ -506,15 +515,14 @@ int rpmsg_send_offchannel_raw(struct rpmsg_channel *rpdev, u32 src, u32 dst,
sim_addr = vrp->sim_base + offset;
sg_init_one(&sg, sim_addr, sizeof(*msg) + len);
- /* protect svq from simultaneous concurrent manipulations */
- mutex_lock(&vrp->svq_lock);
-
/* add message to the remote processor's virtqueue */
err = virtqueue_add_buf_gfp(vrp->svq, &sg, 1, 0, msg, GFP_KERNEL);
if (err < 0) {
dev_err(dev, "virtqueue_add_buf_gfp failed: %d\n", err);
goto out;
}
+ /* descriptors must be written before kicking remote processor */
+ wmb();
/* tell the remote processor it has a pending message to read */
virtqueue_kick(vrp->svq);
@@ -538,6 +546,8 @@ static void rpmsg_recv_done(struct virtqueue *rvq)
struct device *dev = &rvq->vdev->dev;
int err;
+ /* make sure the descriptors are updated before reading */
+ rmb();
msg = virtqueue_get_buf(rvq, &len);
if (!msg) {
dev_err(dev, "uhm, incoming signal, but no used buffer ?\n");
@@ -570,6 +580,8 @@ static void rpmsg_recv_done(struct virtqueue *rvq)
dev_err(dev, "failed to add a virtqueue buffer: %d\n", err);
return;
}
+ /* descriptors must be written before kicking remote processor */
+ wmb();
/* tell the remote processor we added another available rx buffer */
virtqueue_kick(vrp->rvq);