aboutsummaryrefslogtreecommitdiff
path: root/drivers/rpmsg/virtio_rpmsg_bus.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/rpmsg/virtio_rpmsg_bus.c')
-rw-r--r--drivers/rpmsg/virtio_rpmsg_bus.c94
1 files changed, 55 insertions, 39 deletions
diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c
index f1e323924f1..b6135d4d54e 100644
--- a/drivers/rpmsg/virtio_rpmsg_bus.c
+++ b/drivers/rpmsg/virtio_rpmsg_bus.c
@@ -213,13 +213,10 @@ static struct rpmsg_endpoint *__rpmsg_create_ept(struct virtproc_info *vrp,
struct rpmsg_channel *rpdev, rpmsg_rx_cb_t cb,
void *priv, u32 addr)
{
- int err, tmpaddr, request;
+ int id_min, id_max, id;
struct rpmsg_endpoint *ept;
struct device *dev = rpdev ? &rpdev->dev : &vrp->vdev->dev;
- if (!idr_pre_get(&vrp->endpoints, GFP_KERNEL))
- return NULL;
-
ept = kzalloc(sizeof(*ept), GFP_KERNEL);
if (!ept) {
dev_err(dev, "failed to kzalloc a new ept\n");
@@ -234,31 +231,28 @@ static struct rpmsg_endpoint *__rpmsg_create_ept(struct virtproc_info *vrp,
ept->priv = priv;
/* do we need to allocate a local address ? */
- request = addr == RPMSG_ADDR_ANY ? RPMSG_RESERVED_ADDRESSES : addr;
+ if (addr == RPMSG_ADDR_ANY) {
+ id_min = RPMSG_RESERVED_ADDRESSES;
+ id_max = 0;
+ } else {
+ id_min = addr;
+ id_max = addr + 1;
+ }
mutex_lock(&vrp->endpoints_lock);
/* bind the endpoint to an rpmsg address (and allocate one if needed) */
- err = idr_get_new_above(&vrp->endpoints, ept, request, &tmpaddr);
- if (err) {
- dev_err(dev, "idr_get_new_above failed: %d\n", err);
+ id = idr_alloc(&vrp->endpoints, ept, id_min, id_max, GFP_KERNEL);
+ if (id < 0) {
+ dev_err(dev, "idr_alloc failed: %d\n", id);
goto free_ept;
}
-
- /* make sure the user's address request is fulfilled, if relevant */
- if (addr != RPMSG_ADDR_ANY && tmpaddr != addr) {
- dev_err(dev, "address 0x%x already in use\n", addr);
- goto rem_idr;
- }
-
- ept->addr = tmpaddr;
+ ept->addr = id;
mutex_unlock(&vrp->endpoints_lock);
return ept;
-rem_idr:
- idr_remove(&vrp->endpoints, request);
free_ept:
mutex_unlock(&vrp->endpoints_lock);
kref_put(&ept->refcount, __ept_release);
@@ -763,14 +757,14 @@ int rpmsg_send_offchannel_raw(struct rpmsg_channel *rpdev, u32 src, u32 dst,
mutex_lock(&vrp->tx_lock);
/* add message to the remote processor's virtqueue */
- err = virtqueue_add_buf(vrp->svq, &sg, 1, 0, msg, GFP_KERNEL);
+ err = virtqueue_add_outbuf(vrp->svq, &sg, 1, msg, GFP_KERNEL);
if (err) {
/*
* need to reclaim the buffer here, otherwise it's lost
* (memory won't leak, but rpmsg won't use it again for TX).
* this will wait for a buffer management overhaul.
*/
- dev_err(dev, "virtqueue_add_buf failed: %d\n", err);
+ dev_err(dev, "virtqueue_add_outbuf failed: %d\n", err);
goto out;
}
@@ -782,23 +776,13 @@ out:
}
EXPORT_SYMBOL(rpmsg_send_offchannel_raw);
-/* called when an rx buffer is used, and it's time to digest a message */
-static void rpmsg_recv_done(struct virtqueue *rvq)
+static int rpmsg_recv_single(struct virtproc_info *vrp, struct device *dev,
+ struct rpmsg_hdr *msg, unsigned int len)
{
- struct rpmsg_hdr *msg;
- unsigned int len;
struct rpmsg_endpoint *ept;
struct scatterlist sg;
- struct virtproc_info *vrp = rvq->vdev->priv;
- struct device *dev = &rvq->vdev->dev;
int err;
- msg = virtqueue_get_buf(rvq, &len);
- if (!msg) {
- dev_err(dev, "uhm, incoming signal, but no used buffer ?\n");
- return;
- }
-
dev_dbg(dev, "From: 0x%x, To: 0x%x, Len: %d, Flags: %d, Reserved: %d\n",
msg->src, msg->dst, msg->len,
msg->flags, msg->reserved);
@@ -812,7 +796,7 @@ static void rpmsg_recv_done(struct virtqueue *rvq)
if (len > RPMSG_BUF_SIZE ||
msg->len > (len - sizeof(struct rpmsg_hdr))) {
dev_warn(dev, "inbound msg too big: (%d, %d)\n", len, msg->len);
- return;
+ return -EINVAL;
}
/* use the dst addr to fetch the callback of the appropriate user */
@@ -839,20 +823,51 @@ static void rpmsg_recv_done(struct virtqueue *rvq)
/* farewell, ept, we don't need you anymore */
kref_put(&ept->refcount, __ept_release);
} else
- dev_warn(dev, "msg received with no recepient\n");
+ dev_warn(dev, "msg received with no recipient\n");
/* publish the real size of the buffer */
sg_init_one(&sg, msg, RPMSG_BUF_SIZE);
/* add the buffer back to the remote processor's virtqueue */
- err = virtqueue_add_buf(vrp->rvq, &sg, 0, 1, msg, GFP_KERNEL);
+ err = virtqueue_add_inbuf(vrp->rvq, &sg, 1, msg, GFP_KERNEL);
if (err < 0) {
dev_err(dev, "failed to add a virtqueue buffer: %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+/* called when an rx buffer is used, and it's time to digest a message */
+static void rpmsg_recv_done(struct virtqueue *rvq)
+{
+ struct virtproc_info *vrp = rvq->vdev->priv;
+ struct device *dev = &rvq->vdev->dev;
+ struct rpmsg_hdr *msg;
+ unsigned int len, msgs_received = 0;
+ int err;
+
+ msg = virtqueue_get_buf(rvq, &len);
+ if (!msg) {
+ dev_err(dev, "uhm, incoming signal, but no used buffer ?\n");
return;
}
+ while (msg) {
+ err = rpmsg_recv_single(vrp, dev, msg, len);
+ if (err)
+ break;
+
+ msgs_received++;
+
+ msg = virtqueue_get_buf(rvq, &len);
+ };
+
+ dev_dbg(dev, "Received %u messages\n", msgs_received);
+
/* tell the remote processor we added another available rx buffer */
- virtqueue_kick(vrp->rvq);
+ if (msgs_received)
+ virtqueue_kick(vrp->rvq);
}
/*
@@ -957,8 +972,10 @@ static int rpmsg_probe(struct virtio_device *vdev)
bufs_va = dma_alloc_coherent(vdev->dev.parent->parent,
RPMSG_TOTAL_BUF_SPACE,
&vrp->bufs_dma, GFP_KERNEL);
- if (!bufs_va)
+ if (!bufs_va) {
+ err = -ENOMEM;
goto vqs_del;
+ }
dev_dbg(&vdev->dev, "buffers: va %p, dma 0x%llx\n", bufs_va,
(unsigned long long)vrp->bufs_dma);
@@ -976,7 +993,7 @@ static int rpmsg_probe(struct virtio_device *vdev)
sg_init_one(&sg, cpu_addr, RPMSG_BUF_SIZE);
- err = virtqueue_add_buf(vrp->rvq, &sg, 0, 1, cpu_addr,
+ err = virtqueue_add_inbuf(vrp->rvq, &sg, 1, cpu_addr,
GFP_KERNEL);
WARN_ON(err); /* sanity check; this can't really happen */
}
@@ -1036,7 +1053,6 @@ static void rpmsg_remove(struct virtio_device *vdev)
if (vrp->ns_ept)
__rpmsg_destroy_ept(vrp, vrp->ns_ept);
- idr_remove_all(&vrp->endpoints);
idr_destroy(&vrp->endpoints);
vdev->config->del_vqs(vrp->vdev);