hyperv/vmbus: Cleanup TX bufring write process.

MFC after:	1 week
Sponsored by:	Microsoft
Differential Revision:	https://reviews.freebsd.org/D7315
This commit is contained in:
Sepherosa Ziehau 2016-07-27 06:52:43 +00:00
parent 60b0ea0dfb
commit 455cc95f3f

View File

@ -38,9 +38,6 @@
#define VMBUS_BR_WAVAIL(r, w, z) \
(((w) >= (r)) ? ((z) - ((w) - (r))) : ((r) - (w)))
static uint32_t copy_to_ring_buffer(const struct vmbus_txbr *tbr,
uint32_t start_write_offset, const uint8_t *src,
uint32_t src_len);
static uint32_t copy_from_ring_buffer(const struct vmbus_rxbr *rbr,
char *dest, uint32_t dest_len, uint32_t start_read_offset);
@ -149,41 +146,6 @@ vmbus_rxbr_intr_unmask(struct vmbus_rxbr *rbr)
return vmbus_rxbr_avail(rbr);
}
/*
* When we write to the ring buffer, check if the host needs to be
* signaled.
*
* The contract:
* - The host guarantees that while it is draining the TX bufring,
* it will set the br_imask to indicate it does not need to be
* interrupted when new data are added.
* - The host guarantees that it will completely drain the TX bufring
* before exiting the read loop. Further, once the TX bufring is
* empty, it will clear the br_imask and re-check to see if new
* data have arrived.
*/
static boolean_t
hv_ring_buffer_needsig_on_write(uint32_t old_write_location,
const struct vmbus_txbr *tbr)
{
mb();
if (tbr->txbr_imask)
return (FALSE);
/* XXX only compiler fence is needed */
/* Read memory barrier */
rmb();
/*
* This is the only case we need to signal when the
* ring transitions from being empty to non-empty.
*/
if (old_write_location == tbr->txbr_rindex)
return (TRUE);
return (FALSE);
}
static void
vmbus_br_setup(struct vmbus_br *br, void *buf, int blen)
{
@ -227,6 +189,40 @@ vmbus_txbr_setup(struct vmbus_txbr *tbr, void *buf, int blen)
vmbus_br_setup(&tbr->txbr, buf, blen);
}
/*
* When we write to the ring buffer, check if the host needs to be
* signaled.
*
* The contract:
* - The host guarantees that while it is draining the TX bufring,
* it will set the br_imask to indicate it does not need to be
* interrupted when new data are added.
* - The host guarantees that it will completely drain the TX bufring
* before exiting the read loop. Further, once the TX bufring is
* empty, it will clear the br_imask and re-check to see if new
* data have arrived.
*/
static __inline boolean_t
vmbus_txbr_need_signal(const struct vmbus_txbr *tbr, uint32_t old_windex)
{
mb();
if (tbr->txbr_imask)
return (FALSE);
/* XXX only compiler fence is needed */
/* Read memory barrier */
rmb();
/*
* This is the only case we need to signal when the
* ring transitions from being empty to non-empty.
*/
if (old_windex == tbr->txbr_rindex)
return (TRUE);
return (FALSE);
}
static __inline uint32_t
vmbus_txbr_avail(const struct vmbus_txbr *tbr)
{
@ -239,25 +235,52 @@ vmbus_txbr_avail(const struct vmbus_txbr *tbr)
return VMBUS_BR_WAVAIL(rindex, windex, tbr->txbr_dsize);
}
static __inline uint32_t
vmbus_txbr_copyto(const struct vmbus_txbr *tbr, uint32_t windex,
const void *src0, uint32_t cplen)
{
const uint8_t *src = src0;
uint8_t *br_data = tbr->txbr_data;
uint32_t br_dsize = tbr->txbr_dsize;
if (cplen > br_dsize - windex) {
uint32_t fraglen;
/* Wrap-around detected! */
fraglen = br_dsize - windex;
memcpy(br_data + windex, src, fraglen);
memcpy(br_data, src + fraglen, cplen - fraglen);
} else {
memcpy(br_data + windex, src, cplen);
}
windex += cplen;
windex %= br_dsize;
return windex;
}
/*
* Write scattered channel packet to TX bufring.
*
* The offset of this channel packet is written as a 64bits value
* immediately after this channel packet.
*/
int
vmbus_txbr_write(struct vmbus_txbr *tbr, const struct iovec iov[], int iovlen,
boolean_t *need_sig)
{
int i = 0;
uint32_t byte_avail_to_write;
uint32_t old_write_location;
uint32_t total_bytes_to_write = 0;
volatile uint32_t next_write_location;
uint64_t prev_indices = 0;
uint32_t old_windex, windex, total;
uint64_t save_windex;
int i;
total = 0;
for (i = 0; i < iovlen; i++)
total_bytes_to_write += iov[i].iov_len;
total_bytes_to_write += sizeof(uint64_t);
total += iov[i].iov_len;
total += sizeof(save_windex);
mtx_lock_spin(&tbr->txbr_lock);
byte_avail_to_write = vmbus_txbr_avail(tbr);
/*
* NOTE:
* If this write is going to make br_windex same as br_rindex,
@ -265,29 +288,29 @@ vmbus_txbr_write(struct vmbus_txbr *tbr, const struct iovec iov[], int iovlen,
* we can't do it then, since br_windex == br_rindex means that
* the bufring is empty.
*/
if (byte_avail_to_write <= total_bytes_to_write) {
if (vmbus_txbr_avail(tbr) <= total) {
mtx_unlock_spin(&tbr->txbr_lock);
return (EAGAIN);
}
/* Save br_windex for later use */
old_windex = tbr->txbr_windex;
/*
* Copy the scattered channel packet to the TX bufring.
*/
next_write_location = tbr->txbr_windex;
old_write_location = next_write_location;
windex = old_windex;
for (i = 0; i < iovlen; i++) {
next_write_location = copy_to_ring_buffer(tbr,
next_write_location, iov[i].iov_base, iov[i].iov_len);
windex = vmbus_txbr_copyto(tbr, windex,
iov[i].iov_base, iov[i].iov_len);
}
/*
* Set the offset of the current channel packet.
*/
prev_indices = ((uint64_t)tbr->txbr_windex) << 32;
next_write_location = copy_to_ring_buffer(tbr,
next_write_location, (char *)&prev_indices, sizeof(uint64_t));
save_windex = ((uint64_t)old_windex) << 32;
windex = vmbus_txbr_copyto(tbr, windex, &save_windex,
sizeof(save_windex));
/*
* XXX only compiler fence is needed.
@ -296,13 +319,14 @@ vmbus_txbr_write(struct vmbus_txbr *tbr, const struct iovec iov[], int iovlen,
mb();
/*
* Now, update the write index.
* Update the write index _after_ the channel packet
* is copied.
*/
tbr->txbr_windex = next_write_location;
tbr->txbr_windex = windex;
mtx_unlock_spin(&tbr->txbr_lock);
*need_sig = hv_ring_buffer_needsig_on_write(old_write_location, tbr);
*need_sig = vmbus_txbr_need_signal(tbr, old_windex);
return (0);
}
@ -379,29 +403,6 @@ vmbus_rxbr_read(struct vmbus_rxbr *rbr, void *data, int dlen, uint32_t offset)
return (0);
}
static uint32_t
copy_to_ring_buffer(const struct vmbus_txbr *tbr,
uint32_t start_write_offset, const uint8_t *src, uint32_t src_len)
{
char *ring_buffer = tbr->txbr_data;
uint32_t ring_buffer_size = tbr->txbr_dsize;
uint32_t fragLen;
if (src_len > ring_buffer_size - start_write_offset) {
/* Wrap-around detected! */
fragLen = ring_buffer_size - start_write_offset;
memcpy(ring_buffer + start_write_offset, src, fragLen);
memcpy(ring_buffer, src + fragLen, src_len - fragLen);
} else {
memcpy(ring_buffer + start_write_offset, src, src_len);
}
start_write_offset += src_len;
start_write_offset %= ring_buffer_size;
return (start_write_offset);
}
static uint32_t
copy_from_ring_buffer(const struct vmbus_rxbr *rbr, char *dest,
uint32_t dest_len, uint32_t start_read_offset)