Skip to content

Commit 22aa5c7

Browse files
Jorgen Hansengregkh
authored andcommitted
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 <[email protected]> Signed-off-by: Jorgen Hansen <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 5ee1098 commit 22aa5c7

File tree

2 files changed

+77
-2
lines changed

2 files changed

+77
-2
lines changed

drivers/misc/vmw_vmci/vmci_guest.c

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <linux/kernel.h>
1414
#include <linux/mm.h>
1515
#include <linux/module.h>
16+
#include <linux/processor.h>
1617
#include <linux/sched.h>
1718
#include <linux/slab.h>
1819
#include <linux/init.h>
@@ -114,6 +115,47 @@ static void vmci_write_reg(struct vmci_guest_device *dev, u32 val, u32 reg)
114115
iowrite32(val, dev->iobase + reg);
115116
}
116117

118+
static int vmci_write_data(struct vmci_guest_device *dev,
119+
struct vmci_datagram *dg)
120+
{
121+
int result;
122+
123+
if (dev->mmio_base != NULL) {
124+
struct vmci_data_in_out_header *buffer_header = dev->tx_buffer;
125+
u8 *dg_out_buffer = (u8 *)(buffer_header + 1);
126+
127+
if (VMCI_DG_SIZE(dg) > VMCI_MAX_DG_SIZE)
128+
return VMCI_ERROR_INVALID_ARGS;
129+
130+
/*
131+
* Initialize send buffer with outgoing datagram
132+
* and set up header for inline data. Device will
133+
* not access buffer asynchronously - only after
134+
* the write to VMCI_DATA_OUT_LOW_ADDR.
135+
*/
136+
memcpy(dg_out_buffer, dg, VMCI_DG_SIZE(dg));
137+
buffer_header->opcode = 0;
138+
buffer_header->size = VMCI_DG_SIZE(dg);
139+
buffer_header->busy = 1;
140+
141+
vmci_write_reg(dev, lower_32_bits(dev->tx_buffer_base),
142+
VMCI_DATA_OUT_LOW_ADDR);
143+
144+
/* Caller holds a spinlock, so cannot block. */
145+
spin_until_cond(buffer_header->busy == 0);
146+
147+
result = vmci_read_reg(vmci_dev_g, VMCI_RESULT_LOW_ADDR);
148+
if (result == VMCI_SUCCESS)
149+
result = (int)buffer_header->result;
150+
} else {
151+
iowrite8_rep(dev->iobase + VMCI_DATA_OUT_ADDR,
152+
dg, VMCI_DG_SIZE(dg));
153+
result = vmci_read_reg(vmci_dev_g, VMCI_RESULT_LOW_ADDR);
154+
}
155+
156+
return result;
157+
}
158+
117159
/*
118160
* VM to hypervisor call mechanism. We use the standard VMware naming
119161
* convention since shared code is calling this function as well.
@@ -139,8 +181,7 @@ int vmci_send_datagram(struct vmci_datagram *dg)
139181
spin_lock_irqsave(&vmci_dev_spinlock, flags);
140182

141183
if (vmci_dev_g) {
142-
iowrite8_rep(vmci_dev_g->iobase + VMCI_DATA_OUT_ADDR,
143-
dg, VMCI_DG_SIZE(dg));
184+
vmci_write_data(vmci_dev_g, dg);
144185
result = vmci_read_reg(vmci_dev_g, VMCI_RESULT_LOW_ADDR);
145186
} else {
146187
result = VMCI_ERROR_UNAVAILABLE;

include/linux/vmw_vmci_defs.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,40 @@ enum {
110110
#define VMCI_MMIO_ACCESS_OFFSET ((size_t)(128 * 1024))
111111
#define VMCI_MMIO_ACCESS_SIZE ((size_t)(64 * 1024))
112112

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+
113147
/*
114148
* We have a fixed set of resource IDs available in the VMX.
115149
* This allows us to have a very simple implementation since we statically

0 commit comments

Comments
 (0)