HACK: rpmsg: virtio_rpmsg_bus: fix up vring buffer logic for TI Keystone SoCs
authorSuman Anna <s-anna@ti.com>
Sun, 24 Feb 2019 05:09:56 +0000 (23:09 -0600)
committerSuman Anna <s-anna@ti.com>
Sun, 24 Feb 2019 05:09:56 +0000 (23:09 -0600)
The virtio rpmsg logic is always based on publishing the physical vring
buffers in the vring control structures to expose them to the remote
processor side. Commit 9dd87c2af651 ("rpmsg: virtio_rpmsg_bus: fix
sg_set_buf() when addr is not a valid kernel address") has fixed up
this logic to scale for the case with the virtio rpmsg buffers were
allocated from either a HighMem region or a dedicated carveout (mapped
into kernel in vmalloc space). The fix initialized the sg_table with
appropriate page address which is translated back to a dma_addr_t
using sg_phys() call in virtqueue_add() (in vring_map_one_sg())
function. This fix was sufficient for SoCs where both the host and
the remote processors used 32-bit addressing scheme.

This logic failed for TI Keystone platforms though, which have a 36-bit
physical address and only a regular 32-bit dma address (the first 2 GB
of RAM at 0x800000000 is aliased at 0x80000000) due to the truncation of
the physical address. Furthermore, the remote processors on these SoCs
leverage the 32-bit aliased address for referencing the IPC shared memory
buffers. Fix this issue and make the virtio rpmsg stack functional on
these platforms by initializing the sg entry with the page address
corresponding to the dma address of the vring buffer instead of it's
physical address. This solution allows the core virtio_ring code to
remain unchanged, and results in publishing the desired aliased
addresses in the vring control structures.

Signed-off-by: Suman Anna <s-anna@ti.com>
drivers/rpmsg/virtio_rpmsg_bus.c

index 72a6f3dae00e424b9f2f41da24331eb696a03934..9c4ff0e34a379f6baaa4cca539b3716a3a2682ce 100644 (file)
@@ -186,6 +186,7 @@ static const struct rpmsg_endpoint_ops virtio_endpoint_ops = {
 
 /**
  * rpmsg_sg_init - initialize scatterlist according to cpu address location
+ * @vrp: virtual remoteproc structure used with this buffer
  * @sg: scatterlist to fill
  * @cpu_addr: virtual address of the buffer
  * @len: buffer length
@@ -194,9 +195,17 @@ static const struct rpmsg_endpoint_ops virtio_endpoint_ops = {
  * location (in vmalloc or in kernel).
  */
 static void
-rpmsg_sg_init(struct scatterlist *sg, void *cpu_addr, unsigned int len)
+rpmsg_sg_init(struct virtproc_info *vrp, struct scatterlist *sg,
+             void *cpu_addr, unsigned int len)
 {
-       if (is_vmalloc_addr(cpu_addr)) {
+       if (of_machine_is_compatible("ti,keystone")) {
+               unsigned long offset = cpu_addr - vrp->rbufs;
+               dma_addr_t dma_addr = vrp->bufs_dma + offset;
+
+               sg_init_table(sg, 1);
+               sg_set_page(sg, pfn_to_page(PHYS_PFN(dma_addr)), len,
+                           offset_in_page(dma_addr));
+       } else if (is_vmalloc_addr(cpu_addr)) {
                sg_init_table(sg, 1);
                sg_set_page(sg, vmalloc_to_page(cpu_addr), len,
                            offset_in_page(cpu_addr));
@@ -626,7 +635,7 @@ static int rpmsg_send_offchannel_raw(struct rpmsg_device *rpdev,
                         msg, sizeof(*msg) + msg->len, true);
 #endif
 
-       rpmsg_sg_init(&sg, msg, sizeof(*msg) + len);
+       rpmsg_sg_init(vrp, &sg, msg, sizeof(*msg) + len);
 
        mutex_lock(&vrp->tx_lock);
 
@@ -750,7 +759,7 @@ static int rpmsg_recv_single(struct virtproc_info *vrp, struct device *dev,
                dev_warn(dev, "msg received with no recipient\n");
 
        /* publish the real size of the buffer */
-       rpmsg_sg_init(&sg, msg, vrp->buf_size);
+       rpmsg_sg_init(vrp, &sg, msg, vrp->buf_size);
 
        /* add the buffer back to the remote processor's virtqueue */
        err = virtqueue_add_inbuf(vrp->rvq, &sg, 1, msg, GFP_KERNEL);
@@ -945,7 +954,7 @@ static int rpmsg_probe(struct virtio_device *vdev)
                struct scatterlist sg;
                void *cpu_addr = vrp->rbufs + i * vrp->buf_size;
 
-               rpmsg_sg_init(&sg, cpu_addr, vrp->buf_size);
+               rpmsg_sg_init(vrp, &sg, cpu_addr, vrp->buf_size);
 
                err = virtqueue_add_inbuf(vrp->rvq, &sg, 1, cpu_addr,
                                          GFP_KERNEL);