From: Tianyu Lan <tianyu....@microsoft.com>

In Isolation VM, all shared memory with host needs to mark visible
to host via hvcall. vmbus_establish_gpadl() has already done it for
storvsc rx/tx ring buffer. The page buffer used by vmbus_sendpacket_
mpb_desc() still need to handle. Use DMA API to map/umap these
memory during sending/receiving packet and Hyper-V DMA ops callback
will use swiotlb function to allocate bounce buffer and copy data
from/to bounce buffer.

Signed-off-by: Tianyu Lan <tianyu....@microsoft.com>
---
 drivers/scsi/storvsc_drv.c | 68 +++++++++++++++++++++++++++++++++++---
 1 file changed, 63 insertions(+), 5 deletions(-)

diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c
index 403753929320..cc9cb32f6621 100644
--- a/drivers/scsi/storvsc_drv.c
+++ b/drivers/scsi/storvsc_drv.c
@@ -21,6 +21,8 @@
 #include <linux/device.h>
 #include <linux/hyperv.h>
 #include <linux/blkdev.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
 #include <scsi/scsi.h>
 #include <scsi/scsi_cmnd.h>
 #include <scsi/scsi_host.h>
@@ -427,6 +429,8 @@ struct storvsc_cmd_request {
        u32 payload_sz;
 
        struct vstor_packet vstor_packet;
+       u32 hvpg_count;
+       struct hv_dma_range *dma_range;
 };
 
 
@@ -509,6 +513,14 @@ struct storvsc_scan_work {
        u8 tgt_id;
 };
 
+#define storvsc_dma_map(dev, page, offset, size, dir) \
+       dma_map_page(dev, page, offset, size, dir)
+
+#define storvsc_dma_unmap(dev, dma_range, dir)         \
+               dma_unmap_page(dev, dma_range.dma,      \
+                              dma_range.mapping_size,  \
+                              dir ? DMA_FROM_DEVICE : DMA_TO_DEVICE)
+
 static void storvsc_device_scan(struct work_struct *work)
 {
        struct storvsc_scan_work *wrk;
@@ -1267,6 +1279,7 @@ static void storvsc_on_channel_callback(void *context)
        struct hv_device *device;
        struct storvsc_device *stor_device;
        struct Scsi_Host *shost;
+       int i;
 
        if (channel->primary_channel != NULL)
                device = channel->primary_channel->device_obj;
@@ -1321,6 +1334,15 @@ static void storvsc_on_channel_callback(void *context)
                                request = (struct storvsc_cmd_request 
*)scsi_cmd_priv(scmnd);
                        }
 
+                       if (request->dma_range) {
+                               for (i = 0; i < request->hvpg_count; i++)
+                                       storvsc_dma_unmap(&device->device,
+                                               request->dma_range[i],
+                                               
request->vstor_packet.vm_srb.data_in == READ_TYPE);
+
+                               kfree(request->dma_range);
+                       }
+
                        storvsc_on_receive(stor_device, packet, request);
                        continue;
                }
@@ -1817,7 +1839,9 @@ static int storvsc_queuecommand(struct Scsi_Host *host, 
struct scsi_cmnd *scmnd)
                unsigned int hvpgoff, hvpfns_to_add;
                unsigned long offset_in_hvpg = offset_in_hvpage(sgl->offset);
                unsigned int hvpg_count = HVPFN_UP(offset_in_hvpg + length);
+               dma_addr_t dma;
                u64 hvpfn;
+               u32 size;
 
                if (hvpg_count > MAX_PAGE_BUFFER_COUNT) {
 
@@ -1831,6 +1855,13 @@ static int storvsc_queuecommand(struct Scsi_Host *host, 
struct scsi_cmnd *scmnd)
                payload->range.len = length;
                payload->range.offset = offset_in_hvpg;
 
+               cmd_request->dma_range = kcalloc(hvpg_count,
+                                sizeof(*cmd_request->dma_range),
+                                GFP_ATOMIC);
+               if (!cmd_request->dma_range) {
+                       ret = -ENOMEM;
+                       goto free_payload;
+               }
 
                for (i = 0; sgl != NULL; sgl = sg_next(sgl)) {
                        /*
@@ -1854,9 +1885,29 @@ static int storvsc_queuecommand(struct Scsi_Host *host, 
struct scsi_cmnd *scmnd)
                         * last sgl should be reached at the same time that
                         * the PFN array is filled.
                         */
-                       while (hvpfns_to_add--)
-                               payload->range.pfn_array[i++] = hvpfn++;
+                       while (hvpfns_to_add--) {
+                               size = min(HV_HYP_PAGE_SIZE - offset_in_hvpg,
+                                          (unsigned long)length);
+                               dma = storvsc_dma_map(&dev->device, 
pfn_to_page(hvpfn++),
+                                                     offset_in_hvpg, size,
+                                                     scmnd->sc_data_direction);
+                               if (dma_mapping_error(&dev->device, dma)) {
+                                       ret = -ENOMEM;
+                                       goto free_dma_range;
+                               }
+
+                               if (offset_in_hvpg) {
+                                       payload->range.offset = dma & 
~HV_HYP_PAGE_MASK;
+                                       offset_in_hvpg = 0;
+                               }
+
+                               cmd_request->dma_range[i].dma = dma;
+                               cmd_request->dma_range[i].mapping_size = size;
+                               payload->range.pfn_array[i++] = dma >> 
HV_HYP_PAGE_SHIFT;
+                               length -= size;
+                       }
                }
+               cmd_request->hvpg_count = hvpg_count;
        }
 
        cmd_request->payload = payload;
@@ -1867,13 +1918,20 @@ static int storvsc_queuecommand(struct Scsi_Host *host, 
struct scsi_cmnd *scmnd)
        put_cpu();
 
        if (ret == -EAGAIN) {
-               if (payload_sz > sizeof(cmd_request->mpb))
-                       kfree(payload);
                /* no more space */
-               return SCSI_MLQUEUE_DEVICE_BUSY;
+               ret = SCSI_MLQUEUE_DEVICE_BUSY;
+               goto free_dma_range;
        }
 
        return 0;
+
+free_dma_range:
+       kfree(cmd_request->dma_range);
+
+free_payload:
+       if (payload_sz > sizeof(cmd_request->mpb))
+               kfree(payload);
+       return ret;
 }
 
 static struct scsi_host_template scsi_driver = {
-- 
2.25.1

_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

Reply via email to