Index: head/sys/dev/hyperv/vmbus/hv_ring_buffer.c =================================================================== --- head/sys/dev/hyperv/vmbus/hv_ring_buffer.c +++ head/sys/dev/hyperv/vmbus/hv_ring_buffer.c @@ -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 @@ 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_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 @@ 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 @@ * 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 @@ 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); } @@ -380,29 +404,6 @@ } 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) {