From 98fa8cf4bcc79cb14de8fd815bbcd00dcbd7b20e Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 1 Dec 2012 06:46:36 -0800 Subject: Drivers: hv: Optimize the signaling on the write path The host has already implemented the "read" side optimizations. Leverage that to optimize "write" side signaling. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'drivers/hv/channel.c') diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 773a2f25a8f0..727c5f1d6acf 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -564,6 +564,7 @@ int vmbus_sendpacket(struct vmbus_channel *channel, const void *buffer, struct scatterlist bufferlist[3]; u64 aligned_data = 0; int ret; + bool signal = false; /* Setup the descriptor */ @@ -580,9 +581,9 @@ int vmbus_sendpacket(struct vmbus_channel *channel, const void *buffer, sg_set_buf(&bufferlist[2], &aligned_data, packetlen_aligned - packetlen); - ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3); + ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, &signal); - if (ret == 0 && !hv_get_ringbuffer_interrupt_mask(&channel->outbound)) + if (ret == 0 && signal) vmbus_setevent(channel); return ret; @@ -606,6 +607,7 @@ int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel, u32 packetlen_aligned; struct scatterlist bufferlist[3]; u64 aligned_data = 0; + bool signal = false; if (pagecount > MAX_PAGE_BUFFER_COUNT) return -EINVAL; @@ -641,9 +643,9 @@ int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel, sg_set_buf(&bufferlist[2], &aligned_data, packetlen_aligned - packetlen); - ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3); + ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, &signal); - if (ret == 0 && !hv_get_ringbuffer_interrupt_mask(&channel->outbound)) + if (ret == 0 && signal) vmbus_setevent(channel); return ret; @@ -665,6 +667,7 @@ int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel, u32 packetlen_aligned; struct scatterlist bufferlist[3]; u64 aligned_data = 0; + bool signal = false; u32 pfncount = NUM_PAGES_SPANNED(multi_pagebuffer->offset, multi_pagebuffer->len); @@ -703,9 +706,9 @@ int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel, sg_set_buf(&bufferlist[2], &aligned_data, packetlen_aligned - packetlen); - ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3); + ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, &signal); - if (ret == 0 && !hv_get_ringbuffer_interrupt_mask(&channel->outbound)) + if (ret == 0 && signal) vmbus_setevent(channel); return ret; -- cgit v1.2.3 From 21c3bef5db359596806f19fee6c3ec0c033881d0 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 1 Dec 2012 06:46:43 -0800 Subject: Drivers: hv: Change the signature of vmbus_set_event() In preparation for supporting a per-connection signaling mechanism, change the signature of vmbus_set_event(). This change is also needed to implement other aspects of the signaling optimization. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel.c | 2 +- drivers/hv/connection.c | 3 ++- drivers/hv/hyperv_vmbus.h | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/hv/channel.c') diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 727c5f1d6acf..70a34daa04c1 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -55,7 +55,7 @@ static void vmbus_setevent(struct vmbus_channel *channel) [channel->monitor_grp].pending); } else { - vmbus_set_event(channel->offermsg.child_relid); + vmbus_set_event(channel); } } diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index 3f8ce7be4c18..0d8b13290e93 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -395,8 +395,9 @@ int vmbus_post_msg(void *buffer, size_t buflen) /* * vmbus_set_event - Send an event notification to the parent */ -int vmbus_set_event(u32 child_relid) +int vmbus_set_event(struct vmbus_channel *channel) { + u32 child_relid = channel->offermsg.child_relid; /* Each u32 represents 32 channels */ sync_set_bit(child_relid & 31, (unsigned long *)vmbus_connection.send_int_page + diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 61d2c4f6fc17..cd48ac331708 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -660,7 +660,7 @@ int vmbus_connect(void); int vmbus_post_msg(void *buffer, size_t buflen); -int vmbus_set_event(u32 child_relid); +int vmbus_set_event(struct vmbus_channel *channel); void vmbus_on_event(unsigned long data); -- cgit v1.2.3 From abbf3b2aa090b4a6bf22c935924b6467990266da Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 1 Dec 2012 06:46:48 -0800 Subject: Drivers: hv: Add state to manage incoming channel interrupt load Add state to bind a channel to a specific VCPU. This will help us better distribute incoming interrupt load. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel.c | 2 +- drivers/hv/channel_mgmt.c | 2 ++ include/linux/hyperv.h | 21 +++++++++++++++++++-- 3 files changed, 22 insertions(+), 3 deletions(-) (limited to 'drivers/hv/channel.c') diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 70a34daa04c1..9303252b2e19 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -181,7 +181,7 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, open_msg->ringbuffer_gpadlhandle = newchannel->ringbuffer_gpadlhandle; open_msg->downstream_ringbuffer_pageoffset = send_ringbuffer_size >> PAGE_SHIFT; - open_msg->server_contextarea_gpadlhandle = 0; + open_msg->target_vp = newchannel->target_vp; if (userdatalen > MAX_USER_DEFINED_BYTES) { err = -EINVAL; diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index f4d990285d99..56ed45c74d01 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -302,6 +302,8 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr) offer->connection_id; } + newchannel->target_vp = 0; + memcpy(&newchannel->offermsg, offer, sizeof(struct vmbus_channel_offer_channel)); newchannel->monitor_grp = (u8)offer->monitorid / 32; diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index c6e2c44a1be9..8c3cb1fc34d4 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -732,8 +732,15 @@ struct vmbus_channel_open_channel { /* GPADL for the channel's ring buffer. */ u32 ringbuffer_gpadlhandle; - /* GPADL for the channel's server context save area. */ - u32 server_contextarea_gpadlhandle; + /* + * Starting with win8, this field will be used to specify + * the target virtual processor on which to deliver the interrupt for + * the host to guest communication. + * Prior to win8, incoming channel interrupts would only + * be delivered on cpu 0. Setting this value to 0 would + * preserve the earlier behavior. + */ + u32 target_vp; /* * The upstream ring buffer begins at offset zero in the memory @@ -971,6 +978,16 @@ struct vmbus_channel { bool is_dedicated_interrupt; struct hv_input_signal_event_buffer sig_buf; struct hv_input_signal_event *sig_event; + + /* + * Starting with win8, this field will be used to specify + * the target virtual processor on which to deliver the interrupt for + * the host to guest communication. + * Prior to win8, incoming channel interrupts would only + * be delivered on cpu 0. Setting this value to 0 would + * preserve the earlier behavior. + */ + u32 target_vp; }; static inline void set_channel_read_state(struct vmbus_channel *c, bool state) -- cgit v1.2.3 From c2b8e5202cf7670f918d0f7439ed2123cd58e1b7 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 1 Dec 2012 06:46:57 -0800 Subject: Drivers: hv: Implement flow management on the send side Implement flow management on the send side. When the sender is blocked, the reader can potentially signal the sender to indicate there is now room to send. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel.c | 12 +++++++++-- drivers/hv/hyperv_vmbus.h | 2 +- drivers/hv/ring_buffer.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 61 insertions(+), 4 deletions(-) (limited to 'drivers/hv/channel.c') diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 9303252b2e19..064257e79f23 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -735,6 +735,7 @@ int vmbus_recvpacket(struct vmbus_channel *channel, void *buffer, u32 packetlen; u32 userlen; int ret; + bool signal = false; *buffer_actual_len = 0; *requestid = 0; @@ -761,8 +762,10 @@ int vmbus_recvpacket(struct vmbus_channel *channel, void *buffer, /* Copy over the packet to the user buffer */ ret = hv_ringbuffer_read(&channel->inbound, buffer, userlen, - (desc.offset8 << 3)); + (desc.offset8 << 3), &signal); + if (signal) + vmbus_setevent(channel); return 0; } @@ -779,6 +782,7 @@ int vmbus_recvpacket_raw(struct vmbus_channel *channel, void *buffer, u32 packetlen; u32 userlen; int ret; + bool signal = false; *buffer_actual_len = 0; *requestid = 0; @@ -805,7 +809,11 @@ int vmbus_recvpacket_raw(struct vmbus_channel *channel, void *buffer, *requestid = desc.trans_id; /* Copy over the entire packet to the user buffer */ - ret = hv_ringbuffer_read(&channel->inbound, buffer, packetlen, 0); + ret = hv_ringbuffer_read(&channel->inbound, buffer, packetlen, 0, + &signal); + + if (signal) + vmbus_setevent(channel); return 0; } diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index becb106918d6..ac111f223821 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -550,7 +550,7 @@ int hv_ringbuffer_peek(struct hv_ring_buffer_info *ring_info, void *buffer, int hv_ringbuffer_read(struct hv_ring_buffer_info *ring_info, void *buffer, u32 buflen, - u32 offset); + u32 offset, bool *signal); void hv_ringbuffer_get_debuginfo(struct hv_ring_buffer_info *ring_info, diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c index 2a0babc95709..cafa72ffdc30 100644 --- a/drivers/hv/ring_buffer.c +++ b/drivers/hv/ring_buffer.c @@ -84,6 +84,50 @@ 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(u32 old_rd, + struct hv_ring_buffer_info *rbi) +{ + u32 prev_write_sz; + u32 cur_write_sz; + u32 r_size; + u32 write_loc = rbi->ring_buffer->write_index; + u32 read_loc = rbi->ring_buffer->read_index; + u32 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_datasize; + cur_write_sz = write_loc >= read_loc ? r_size - (write_loc - read_loc) : + read_loc - write_loc; + + prev_write_sz = write_loc >= old_rd ? r_size - (write_loc - old_rd) : + old_rd - write_loc; + + + if ((prev_write_sz < pending_sz) && (cur_write_sz >= pending_sz)) + return true; + + return false; +} /* * hv_get_next_write_location() @@ -461,13 +505,14 @@ int hv_ringbuffer_peek(struct hv_ring_buffer_info *Inring_info, * */ int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info, void *buffer, - u32 buflen, u32 offset) + u32 buflen, u32 offset, bool *signal) { u32 bytes_avail_towrite; u32 bytes_avail_toread; u32 next_read_location = 0; u64 prev_indices = 0; unsigned long flags; + u32 old_read; if (buflen <= 0) return -EINVAL; @@ -478,6 +523,8 @@ int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info, void *buffer, &bytes_avail_toread, &bytes_avail_towrite); + old_read = bytes_avail_toread; + /* Make sure there is something to read */ if (bytes_avail_toread < buflen) { spin_unlock_irqrestore(&inring_info->ring_lock, flags); @@ -508,5 +555,7 @@ int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info, void *buffer, spin_unlock_irqrestore(&inring_info->ring_lock, flags); + *signal = hv_need_to_signal_on_read(old_read, inring_info); + return 0; } -- cgit v1.2.3 From f994a154c5318d132200862686fde13dca0979b1 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 4 Dec 2012 00:15:42 -0500 Subject: Drivers: hv: remove unused variable in vmbus_recvpacket_raw() The variable userlen is initialized but never used otherwise, so remove the unused variable. Signed-off-by: Wei Yongjun Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/hv/channel.c') diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 064257e79f23..0b122f8c7005 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -780,7 +780,6 @@ int vmbus_recvpacket_raw(struct vmbus_channel *channel, void *buffer, { struct vmpacket_descriptor desc; u32 packetlen; - u32 userlen; int ret; bool signal = false; @@ -795,7 +794,6 @@ int vmbus_recvpacket_raw(struct vmbus_channel *channel, void *buffer, packetlen = desc.len8 << 3; - userlen = packetlen - (desc.offset8 << 3); *buffer_actual_len = packetlen; -- cgit v1.2.3