Index: sys/dev/hyperv/include/hyperv.h =================================================================== --- sys/dev/hyperv/include/hyperv.h +++ sys/dev/hyperv/include/hyperv.h @@ -663,8 +663,27 @@ * vmbus connection also uses this data structure */ volatile uint32_t interrupt_mask; + + /* + * If VSP cannot write the ring buffer for it is full, + * VSP will maintain the pending content and tell VSC + * the pending content length. Each time VSC finishes + * reading from ring buffer, it has to check whether + * the buffer has enough space for VSP to write and + * inform the VSP to write the pending data. + */ + volatile uint32_t pending_send_sz; + + uint32_t reserved1[12]; + union { + struct { + uint32_t feat_pending_send_sz:1; + }; + uint32_t value; + } feature_bits; + /* pad it to PAGE_SIZE so that data starts on a page */ - uint8_t reserved[4084]; + uint8_t reserved[4028]; /* * WARNING: Ring data starts here + ring_data_start_offset Index: sys/dev/hyperv/vmbus/hv_channel.c =================================================================== --- sys/dev/hyperv/vmbus/hv_channel.c +++ sys/dev/hyperv/vmbus/hv_channel.c @@ -812,6 +812,7 @@ uint32_t user_len; uint32_t packet_len; hv_vm_packet_descriptor desc; + boolean_t signal; *buffer_actual_len = 0; *request_id = 0; @@ -833,9 +834,10 @@ /* Copy over the packet to the user buffer */ ret = hv_ring_buffer_read(&channel->inbound, Buffer, user_len, - (desc.data_offset8 << 3)); - - return (0); + (desc.data_offset8 << 3), &signal); + if (ret == 0 && signal) + vmbus_channel_set_event(channel); + return (ret); } /** @@ -853,6 +855,7 @@ uint32_t packetLen; uint32_t userLen; hv_vm_packet_descriptor desc; + boolean_t signal; *buffer_actual_len = 0; *request_id = 0; @@ -875,7 +878,9 @@ *request_id = desc.transaction_id; /* Copy over the entire packet to the user buffer */ - ret = hv_ring_buffer_read(&channel->inbound, buffer, packetLen, 0); + ret = hv_ring_buffer_read(&channel->inbound, buffer, packetLen, 0, &signal); + if (ret == 0 && signal) + vmbus_channel_set_event(channel); - return (0); + return (ret); } Index: sys/dev/hyperv/vmbus/hv_ring_buffer.c =================================================================== --- sys/dev/hyperv/vmbus/hv_ring_buffer.c +++ sys/dev/hyperv/vmbus/hv_ring_buffer.c @@ -37,6 +37,7 @@ #define HV_BYTES_AVAIL_TO_WRITE(r, w, z) ((w) >= (r))? \ ((z) - ((w) - (r))):((r) - (w)) +#define PENDING_SEND_SZ_FEATURE_ENABLED 0x00001 /** * @brief Get number of bytes available to read and to write to * for the specified ring buffer @@ -244,6 +245,9 @@ ring_info->ring_buffer->read_index = ring_info->ring_buffer->write_index = 0; + ring_info->ring_buffer->feature_bits.value = + PENDING_SEND_SZ_FEATURE_ENABLED; + ring_info->ring_size = buffer_len; ring_info->ring_data_size = buffer_len - sizeof(hv_vmbus_ring_buffer); @@ -380,7 +384,46 @@ return (0); } +/** + * In order to optimize the flow on sending side (VSP), + * if the ring buffer writer was blocked for insufficient space, + * the ring buffer reader can inform the writer once there + * are enough space to write. + * + * @param prev_write_sz is the lastest available write bytes. + * @param rbi is the ring buffer pointer. + * @return true if there are enough space for pending content. + */ +static inline boolean_t +hv_need_to_signal_on_read(uint32_t prev_write_sz, + hv_vmbus_ring_buffer_info *rbi) +{ + uint32_t cur_write_sz; + uint32_t r_size; + uint32_t write_loc = rbi->ring_buffer->write_index; + uint32_t read_loc = rbi->ring_buffer->read_index; + uint32_t pending_sz = rbi->ring_buffer->pending_send_sz; + /* + * If the other end is not blocked on write don't bother. + */ + if (pending_sz == 0) + return (FALSE); + + r_size = rbi->ring_data_size; + cur_write_sz = write_loc >= read_loc ? r_size - (write_loc - read_loc) : + read_loc - write_loc; + + /** + * There is insufficient space before last read, + * and enough space shows up after that read. Then, + * we need to inform VSP to write. + */ + if ((prev_write_sz < pending_sz) && (cur_write_sz >= pending_sz)) + return (TRUE); + + return (FALSE); +} /** * @brief Read and advance the read index. */ @@ -389,12 +432,14 @@ hv_vmbus_ring_buffer_info* in_ring_info, void* buffer, uint32_t buffer_len, - uint32_t offset) + uint32_t offset, + boolean_t* signal) { uint32_t bytes_avail_to_write; uint32_t bytes_avail_to_read; uint32_t next_read_location = 0; uint64_t prev_indices = 0; + *signal = FALSE; if (buffer_len <= 0) return (EINVAL); @@ -443,6 +488,7 @@ mtx_unlock_spin(&in_ring_info->ring_lock); + *signal = hv_need_to_signal_on_read(bytes_avail_to_write, in_ring_info); return (0); } Index: sys/dev/hyperv/vmbus/hv_vmbus_priv.h =================================================================== --- sys/dev/hyperv/vmbus/hv_vmbus_priv.h +++ sys/dev/hyperv/vmbus/hv_vmbus_priv.h @@ -624,7 +624,8 @@ hv_vmbus_ring_buffer_info *ring_info, void *buffer, uint32_t buffer_len, - uint32_t offset); + uint32_t offset, + boolean_t *signal); uint32_t hv_vmbus_get_ring_buffer_interrupt_mask( hv_vmbus_ring_buffer_info *ring_info);