Drivers: hv: vmbus: Cleanup the ring buffer code
cover Drivers: hv: vmbus: Cleanup the ring buffer code
Cleanup and fix a bug in the ring buffer code. Also implement APIs for in place consumption of received packets.
K. Y. Srinivasan (6):
Drivers: hv: vmbus: Introduce functions for estimating room in the
ring buffer
Drivers: hv: vmbus: Use READ_ONCE() to read variables that are
volatile
Drivers: hv: vmbus: Use the new virt_xx barrier code
Drivers: hv: vmbus: Export the vmbus_set_event() API
Drivers: hv: vmbus: Move some ring buffer functions to hyperv.h
Drivers: hv: vmbus: Implement APIs to support "in place" consumption
of vmbus packets
drivers/hv/connection.c | 1 + drivers/hv/hyperv_vmbus.h | 2 - drivers/hv/ring_buffer.c | 95 +++---------------------- include/linux/hyperv.h | 168 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 181 insertions(+), 85 deletions(-)
Signature · 1 line
1.7.4.1
PATCH 1/6 Drivers: hv: vmbus: Introduce functions for estimating room in the ring buffer
Introduce separate functions for estimating how much can be read from and written to the ring buffer.
--- drivers/hv/ring_buffer.c | 25 ++++--------------------- include/linux/hyperv.h | 27 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 21 deletions(-)
drivers/hv/ring_buffer.c +4 -21
@@ -38,8 +38,6 @@ void hv_begin_read(struct hv_ring_buffer_info *rbi)u32 hv_end_read (struct hv_ring_buffer_info *rbi) { -u32 read; -u32 write; rbi->ring_buffer->interrupt_mask =0 ;mb ();
@@ -49,9 +47,7 @@ u32 hv_end_read(struct hv_ring_buffer_info *rbi)* If it is not, we raced and we need to process new * incoming messages. */ -hv_get_ringbuffer_availbytes (rbi, &read, &write); - -return read; +return hv_get_bytes_to_read (rbi); }/*
@@ -106,9 +102,6 @@ static bool hv_need_to_signal(u32 old_write, struct hv_ring_buffer_info *rbi)static bool hv_need_to_signal_on_read (struct hv_ring_buffer_info *rbi) {u32 cur_write_sz; -u32 r_size; -u32 write_loc; -u32 read_loc = rbi->ring_buffer->read_index;u32 pending_sz;/*
@@ -125,14 +118,11 @@ static bool hv_need_to_signal_on_read(struct hv_ring_buffer_info *rbi)mb (); pending_sz = rbi->ring_buffer->pending_send_sz; - write_loc = rbi->ring_buffer->write_index;/* If the other end is not blocked on write don't bother. */ if (pending_sz ==0 )return false; - r_size = rbi->ring_datasize; - cur_write_sz = write_loc >= read_loc ? r_size - (write_loc - read_loc) : - read_loc - write_loc; + cur_write_sz =hv_get_bytes_to_write (rbi);if (cur_write_sz >= pending_sz)return true;
@@ -332,7 +322,6 @@ int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info, {int i =0 ;u32 bytes_avail_towrite; -u32 bytes_avail_toread;u32 totalbytes_towrite =0 ;u32 next_write_location;
@@ -348,9 +337,7 @@ int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info,if (lock)spin_lock_irqsave (&outring_info->ring_lock, flags); -hv_get_ringbuffer_availbytes (outring_info, - &bytes_avail_toread, - &bytes_avail_towrite); + bytes_avail_towrite =hv_get_bytes_to_write (outring_info);/* * If there is only room for the packet, assume it is full.
@@ -401,7 +388,6 @@ int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info, void *buffer, u32 buflen, u32 *buffer_actual_len, u64 *requestid, bool *signal, bool raw) { - u32 bytes_avail_towrite; u32 bytes_avail_toread;u32 next_read_location =0 ;u64 prev_indices =0 ;
@@ -417,10 +403,7 @@ int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info, *buffer_actual_len =0 ; *requestid =0 ; -hv_get_ringbuffer_availbytes (inring_info, - &bytes_avail_toread, - &bytes_avail_towrite); - + bytes_avail_toread =hv_get_bytes_to_read (inring_info);/* Make sure there is something to read */ if (bytes_avail_toread <sizeof (desc)) {/*
include/linux/hyperv.h +27 -0
@@ -151,6 +151,33 @@ hv_get_ringbuffer_availbytes(struct hv_ring_buffer_info *rbi, *read = dsize - *write; } +static inline u32 hv_get_bytes_to_read (struct hv_ring_buffer_info *rbi) +{ +u32 read_loc, write_loc, dsize, read; + + dsize = rbi->ring_datasize; + read_loc = rbi->ring_buffer->read_index; + write_loc =READ_ONCE (rbi->ring_buffer->write_index); + + read = write_loc >= read_loc ? (write_loc - read_loc) : + (dsize - read_loc) + write_loc; + +return read; +} + +static inline u32 hv_get_bytes_to_write (struct hv_ring_buffer_info *rbi) +{ +u32 read_loc, write_loc, dsize, write; + + dsize = rbi->ring_datasize; + read_loc =READ_ONCE (rbi->ring_buffer->read_index); + write_loc = rbi->ring_buffer->write_index; + + write = write_loc >= read_loc ? dsize - (write_loc - read_loc) : + read_loc - write_loc; +return write; +} +/* * VMBUS version is 32 bit entity broken up into * two 16 bit quantities: major_number. minor_number.
Signature · 1 line
1.7.4.1
PATCH 2/6 Drivers: hv: vmbus: Use READ_ONCE() to read variables that are volatile
Use the READ_ONCE macro to access variabes that can change asynchronously. This is the recommended mechanism for dealing with "unsafe" compiler optimizations.
--- drivers/hv/ring_buffer.c | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-)
drivers/hv/ring_buffer.c +3 -3
@@ -69,7 +69,7 @@ u32 hv_end_read(struct hv_ring_buffer_info *rbi)static bool hv_need_to_signal (u32 old_write,struct hv_ring_buffer_info *rbi) {mb (); -if (rbi->ring_buffer->interrupt_mask) +if (READ_ONCE (rbi->ring_buffer->interrupt_mask))return false;/* check interrupt_mask before read_index */
@@ -78,7 +78,7 @@ static bool hv_need_to_signal(u32 old_write, struct hv_ring_buffer_info *rbi)* This is the only case we need to signal when the * ring transitions from being empty to non-empty. */ -if (old_write == rbi->ring_buffer->read_index) +if (old_write ==READ_ONCE (rbi->ring_buffer->read_index))return true;return false;
@@ -117,7 +117,7 @@ static bool hv_need_to_signal_on_read(struct hv_ring_buffer_info *rbi)*/ mb (); - pending_sz = rbi->ring_buffer->pending_send_sz; + pending_sz =READ_ONCE (rbi->ring_buffer->pending_send_sz);/* If the other end is not blocked on write don't bother. */ if (pending_sz ==0 )return false;
Signature · 1 line
1.7.4.1
PATCH 3/6 Drivers: hv: vmbus: Use the new virt_xx barrier code
In reply to K. Y. SrinivasanUse the virt_xx barriers that have been defined for use in virtual machines.
--- drivers/hv/ring_buffer.c | 14 +++++++------- 1 files changed, 7 insertions(+), 7 deletions(-)
drivers/hv/ring_buffer.c +7 -7
@@ -33,14 +33,14 @@void hv_begin_read (struct hv_ring_buffer_info *rbi) { rbi->ring_buffer->interrupt_mask =1 ; -mb (); +virt_mb (); }u32 hv_end_read (struct hv_ring_buffer_info *rbi) { rbi->ring_buffer->interrupt_mask =0 ; -mb (); +virt_mb ();/* * Now check to see if the ring buffer is still empty.
@@ -68,12 +68,12 @@ u32 hv_end_read(struct hv_ring_buffer_info *rbi)static bool hv_need_to_signal (u32 old_write,struct hv_ring_buffer_info *rbi) { -mb (); +virt_mb ();if (READ_ONCE (rbi->ring_buffer->interrupt_mask))return false;/* check interrupt_mask before read_index */ -rmb (); +virt_rmb ();/* * This is the only case we need to signal when the * ring transitions from being empty to non-empty.
@@ -115,7 +115,7 @@ static bool hv_need_to_signal_on_read(struct hv_ring_buffer_info *rbi)* read index, we could miss sending the interrupt. Issue a full * memory barrier to address this. */ -mb (); +virt_mb (); pending_sz =READ_ONCE (rbi->ring_buffer->pending_send_sz);/* If the other end is not blocked on write don't bother. */
@@ -371,7 +371,7 @@ int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info, sizeof(u64));/* Issue a full memory barrier before updating the write index */ -mb (); +virt_mb ();/* Now, update the write location */ hv_set_next_write_location (outring_info, next_write_location);
@@ -447,7 +447,7 @@ int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info,* the writer may start writing to the read area once the read index * is updated. */ -mb (); +virt_mb ();/* Update the read index */ hv_set_next_read_location (inring_info, next_read_location);
Signature · 1 line
1.7.4.1
PATCH 4/6 Drivers: hv: vmbus: Export the vmbus_set_event() API
In reply to K. Y. SrinivasanIn preparation for moving some ring buffer functionality out of the vmbus driver, export the API for signaling the host.
--- drivers/hv/connection.c | 1 + drivers/hv/hyperv_vmbus.h | 2 -- include/linux/hyperv.h | 1 + 3 files changed, 2 insertions(+), 2 deletions(-)
drivers/hv/connection.c +1 -0
@@ -495,3 +495,4 @@ void vmbus_set_event(struct vmbus_channel *channel)hv_do_hypercall (HVCALL_SIGNAL_EVENT , channel->sig_event,NULL ); } +EXPORT_SYMBOL_GPL (vmbus_set_event);
drivers/hv/hyperv_vmbus.h +0 -2
@@ -672,8 +672,6 @@ void vmbus_disconnect(void);int vmbus_post_msg (void *buffer,size_t buflen); -void vmbus_set_event (struct vmbus_channel *channel); -void vmbus_on_event (unsigned long data);void vmbus_on_msg_dpc (unsigned long data);
include/linux/hyperv.h +1 -0
@@ -1365,4 +1365,5 @@ extern __u32 vmbus_proto_version;int vmbus_send_tl_connect_request (const uuid_le *shv_guest_servie_id,const uuid_le *shv_host_servie_id); +void vmbus_set_event (struct vmbus_channel *channel);#endif /* _HYPERV_H */
Signature · 1 line
1.7.4.1
PATCH 5/6 Drivers: hv: vmbus: Move some ring buffer functions to hyperv.h
In reply to K. Y. SrinivasanIn preparation for implementing APIs for in-place consumption of VMBUS packets, movve some ring buffer functionality into hyperv.h
--- drivers/hv/ring_buffer.c | 55 ---------------------------------------------- include/linux/hyperv.h | 54 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 55 deletions(-)
drivers/hv/ring_buffer.c +0 -55
@@ -84,52 +84,6 @@ static bool hv_need_to_signal(u32 old_write, struct hv_ring_buffer_info *rbi)return false; } -/* -* To optimize the flow management on the send-side, -* when the sender is blocked because of lack of -* sufficient space in the ring buffer, potential the -* consumer of the ring buffer can signal the producer. -* This is controlled by the following parameters: -* -* 1. pending_send_sz: This is the size in bytes that the -* producer is trying to send. -* 2. The feature bit feat_pending_send_sz set to indicate if -* the consumer of the ring will signal when the ring -* state transitions from being full to a state where -* there is room for the producer to send the pending packet. -*/ - -static bool hv_need_to_signal_on_read (struct hv_ring_buffer_info *rbi) -{ -u32 cur_write_sz; -u32 pending_sz; - -/* -* Issue a full memory barrier before making the signaling decision. -* Here is the reason for having this barrier: -* If the reading of the pend_sz (in this function) -* were to be reordered and read before we commit the new read -* index (in the calling function) we could -* have a problem. If the host were to set the pending_sz after we -* have sampled pending_sz and go to sleep before we commit the -* read index, we could miss sending the interrupt. Issue a full -* memory barrier to address this. -*/ -virt_mb (); - - pending_sz =READ_ONCE (rbi->ring_buffer->pending_send_sz); -/* If the other end is not blocked on write don't bother. */ -if (pending_sz ==0 ) -return false; - - cur_write_sz =hv_get_bytes_to_write (rbi); - -if (cur_write_sz >= pending_sz) -return true; - -return false; -} -/* Get the next write location for the specified ring buffer. */ static inline u32 hv_get_next_write_location (struct hv_ring_buffer_info *ring_info)
@@ -180,15 +134,6 @@ hv_set_next_read_location(struct hv_ring_buffer_info *ring_info, ring_info->ring_buffer->read_index = next_read_location; } - -/* Get the start of the ring buffer. */ -static inline void * -hv_get_ring_buffer (struct hv_ring_buffer_info *ring_info) -{ -return (void *)ring_info->ring_buffer->buffer; -} - -/* Get the size of the ring buffer. */ static inline u32 hv_get_ring_buffersize (struct hv_ring_buffer_info *ring_info)
include/linux/hyperv.h +54 -0
58 lines not on this page · raw message
Signature · 1 line
1.7.4.1
PATCH 6/6 Drivers: hv: vmbus: Implement APIs to support "in place" consumption of vmbus packets
In reply to K. Y. SrinivasanImplement APIs for in-place consumption of vmbus packets. Currently, each packet is copied and processed one at a time and as part of processing each packet we potentially may signal the host (if it is waiting for room to produce a packet).
These APIs help batched in-place processing of vmbus packets. We also optimize host signaling by having a separate API to signal the end of in-place consumption. With netvsc using these APIs, on an iperf run on average I see about 20X reduction in checks to signal the host.
--- drivers/hv/ring_buffer.c | 1 + include/linux/hyperv.h | 86 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 0 deletions(-)
drivers/hv/ring_buffer.c +1 -0
@@ -132,6 +132,7 @@ hv_set_next_read_location(struct hv_ring_buffer_info *ring_info, u32 next_read_location) { ring_info->ring_buffer->read_index = next_read_location; + ring_info->priv_read_index = next_read_location; }/* Get the size of the ring buffer. */
include/linux/hyperv.h +86 -0
96 lines not on this page · raw message
Signature · 1 line
1.7.4.1