Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

VMCI: dma dg: add support for DMA datagrams sends

Use DMA based send operation from the transmit buffer instead of the
iowrite8_rep based datagram send when DMA datagrams are supported.

The outgoing datagram is sent as inline data in the VMCI transmit
buffer. Once the header has been configured, the send is initiated
by writing the lower 32 bit of the buffer base address to the
VMCI_DATA_OUT_LOW_ADDR register. Only then will the device process
the header and the datagram itself. Following that, the driver busy
waits (it isn't possible to sleep on the send path) for the header
busy flag to change - indicating that the send is complete.

Reviewed-by: Vishnu Dasa <vdasa@vmware.com>
Signed-off-by: Jorgen Hansen <jhansen@vmware.com>
Link: https://lore.kernel.org/r/20220207102725.2742-8-jhansen@vmware.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Jorgen Hansen and committed by
Greg Kroah-Hartman
22aa5c7f 5ee10982

+77 -2
+43 -2
drivers/misc/vmw_vmci/vmci_guest.c
··· 13 13 #include <linux/kernel.h> 14 14 #include <linux/mm.h> 15 15 #include <linux/module.h> 16 + #include <linux/processor.h> 16 17 #include <linux/sched.h> 17 18 #include <linux/slab.h> 18 19 #include <linux/init.h> ··· 115 114 iowrite32(val, dev->iobase + reg); 116 115 } 117 116 117 + static int vmci_write_data(struct vmci_guest_device *dev, 118 + struct vmci_datagram *dg) 119 + { 120 + int result; 121 + 122 + if (dev->mmio_base != NULL) { 123 + struct vmci_data_in_out_header *buffer_header = dev->tx_buffer; 124 + u8 *dg_out_buffer = (u8 *)(buffer_header + 1); 125 + 126 + if (VMCI_DG_SIZE(dg) > VMCI_MAX_DG_SIZE) 127 + return VMCI_ERROR_INVALID_ARGS; 128 + 129 + /* 130 + * Initialize send buffer with outgoing datagram 131 + * and set up header for inline data. Device will 132 + * not access buffer asynchronously - only after 133 + * the write to VMCI_DATA_OUT_LOW_ADDR. 134 + */ 135 + memcpy(dg_out_buffer, dg, VMCI_DG_SIZE(dg)); 136 + buffer_header->opcode = 0; 137 + buffer_header->size = VMCI_DG_SIZE(dg); 138 + buffer_header->busy = 1; 139 + 140 + vmci_write_reg(dev, lower_32_bits(dev->tx_buffer_base), 141 + VMCI_DATA_OUT_LOW_ADDR); 142 + 143 + /* Caller holds a spinlock, so cannot block. */ 144 + spin_until_cond(buffer_header->busy == 0); 145 + 146 + result = vmci_read_reg(vmci_dev_g, VMCI_RESULT_LOW_ADDR); 147 + if (result == VMCI_SUCCESS) 148 + result = (int)buffer_header->result; 149 + } else { 150 + iowrite8_rep(dev->iobase + VMCI_DATA_OUT_ADDR, 151 + dg, VMCI_DG_SIZE(dg)); 152 + result = vmci_read_reg(vmci_dev_g, VMCI_RESULT_LOW_ADDR); 153 + } 154 + 155 + return result; 156 + } 157 + 118 158 /* 119 159 * VM to hypervisor call mechanism. We use the standard VMware naming 120 160 * convention since shared code is calling this function as well. ··· 181 139 spin_lock_irqsave(&vmci_dev_spinlock, flags); 182 140 183 141 if (vmci_dev_g) { 184 - iowrite8_rep(vmci_dev_g->iobase + VMCI_DATA_OUT_ADDR, 185 - dg, VMCI_DG_SIZE(dg)); 142 + vmci_write_data(vmci_dev_g, dg); 186 143 result = vmci_read_reg(vmci_dev_g, VMCI_RESULT_LOW_ADDR); 187 144 } else { 188 145 result = VMCI_ERROR_UNAVAILABLE;
+34
include/linux/vmw_vmci_defs.h
··· 111 111 #define VMCI_MMIO_ACCESS_SIZE ((size_t)(64 * 1024)) 112 112 113 113 /* 114 + * For VMCI devices supporting the VMCI_CAPS_DMA_DATAGRAM capability, the 115 + * sending and receiving of datagrams can be performed using DMA to/from 116 + * a driver allocated buffer. 117 + * Sending and receiving will be handled as follows: 118 + * - when sending datagrams, the driver initializes the buffer where the 119 + * data part will refer to the outgoing VMCI datagram, sets the busy flag 120 + * to 1 and writes the address of the buffer to VMCI_DATA_OUT_HIGH_ADDR 121 + * and VMCI_DATA_OUT_LOW_ADDR. Writing to VMCI_DATA_OUT_LOW_ADDR triggers 122 + * the device processing of the buffer. When the device has processed the 123 + * buffer, it will write the result value to the buffer and then clear the 124 + * busy flag. 125 + * - when receiving datagrams, the driver initializes the buffer where the 126 + * data part will describe the receive buffer, clears the busy flag and 127 + * writes the address of the buffer to VMCI_DATA_IN_HIGH_ADDR and 128 + * VMCI_DATA_IN_LOW_ADDR. Writing to VMCI_DATA_IN_LOW_ADDR triggers the 129 + * device processing of the buffer. The device will copy as many available 130 + * datagrams into the buffer as possible, and then sets the busy flag. 131 + * When the busy flag is set, the driver will process the datagrams in the 132 + * buffer. 133 + */ 134 + struct vmci_data_in_out_header { 135 + uint32_t busy; 136 + uint32_t opcode; 137 + uint32_t size; 138 + uint32_t rsvd; 139 + uint64_t result; 140 + }; 141 + 142 + struct vmci_sg_elem { 143 + uint64_t addr; 144 + uint64_t size; 145 + }; 146 + 147 + /* 114 148 * We have a fixed set of resource IDs available in the VMX. 115 149 * This allows us to have a very simple implementation since we statically 116 150 * know how many will create datagram handles. If a new caller arrives and