From 10d498b13f73f6c171f974d32d231740e6fbb98c Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 28 Nov 2012 21:22:43 -0500 Subject: hv: hv_balloon: remove duplicated include from hv_balloon.c Remove duplicated include. Signed-off-by: Wei Yongjun Acked-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_balloon.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/hv') diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index f6c0011..0ce08c4 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include -- cgit v1.1 From 6fdf3b21433e901dcba0ac186f00d604ce944f56 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 1 Dec 2012 06:46:32 -0800 Subject: Drivers: hv: Implement routines for read side signaling optimization Implement functions that will support read-side signaling optimization. By having the reader indicate the start of the "read" operation and the "end" of the read operation we can more efficiently handle the signaling protocol: while the read is in progress, there is no need for the "writer" to signal the "reader" as new items are put on the read queue. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hyperv_vmbus.h | 4 ++++ drivers/hv/ring_buffer.c | 24 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+) (limited to 'drivers/hv') diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index d8d1fad..3184f6f 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -570,6 +570,10 @@ u32 hv_get_ringbuffer_interrupt_mask(struct hv_ring_buffer_info *ring_info); void hv_ringbuffer_get_debuginfo(struct hv_ring_buffer_info *ring_info, struct hv_ring_buffer_debug_info *debug_info); +void hv_begin_read(struct hv_ring_buffer_info *rbi); + +u32 hv_end_read(struct hv_ring_buffer_info *rbi); + /* * Maximum channels is determined by the size of the interrupt page * which is PAGE_SIZE. 1/2 of PAGE_SIZE is for send endpoint interrupt diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c index 7233c88..0010792 100644 --- a/drivers/hv/ring_buffer.c +++ b/drivers/hv/ring_buffer.c @@ -29,6 +29,30 @@ #include "hyperv_vmbus.h" +void hv_begin_read(struct hv_ring_buffer_info *rbi) +{ + rbi->ring_buffer->interrupt_mask = 1; + smp_mb(); +} + +u32 hv_end_read(struct hv_ring_buffer_info *rbi) +{ + u32 read; + u32 write; + + rbi->ring_buffer->interrupt_mask = 0; + smp_mb(); + + /* + * Now check to see if the ring buffer is still empty. + * If it is not, we raced and we need to process new + * incoming messages. + */ + hv_get_ringbuffer_availbytes(rbi, &read, &write); + + return read; +} + /* * hv_get_next_write_location() -- cgit v1.1 From 132368bd0b286457f83f56d0bbdecd85999562dc Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 1 Dec 2012 06:46:33 -0800 Subject: Drivers: hv: Add state to manage batched reading For the "read" side signaling optimization, the reader has to completely drain the queue before exiting. Add state to manage this "batched" reading. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel_mgmt.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/hv') diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 2f84c5c..7bf5917 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -275,6 +275,13 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr) return; } + /* + * By default we setup state to enable batched + * reading. A specific service can choose to + * disable this prior to opening the channel. + */ + newchannel->batched_reading = true; + memcpy(&newchannel->offermsg, offer, sizeof(struct vmbus_channel_offer_channel)); newchannel->monitor_grp = (u8)offer->monitorid / 32; -- cgit v1.1 From 7ae3e035195735f9b6ce3308b6240af877a41ea0 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 1 Dec 2012 06:46:34 -0800 Subject: Drivers: hv: Turn off batched reading for util drivers Util driver is not a performance critical driver and furthermore some util services such as KVP can only handle one outstanding message from the host. Turn off batched reading for util drivers. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_util.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/hv') diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index a0667de..a62a760 100644 --- a/drivers/hv/hv_util.c +++ b/drivers/hv/hv_util.c @@ -274,6 +274,16 @@ static int util_probe(struct hv_device *dev, } } + /* + * The set of services managed by the util driver are not performance + * critical and do not need batched reading. Furthermore, some services + * such as KVP can only handle one message from the host at a time. + * Turn off batched reading for all util drivers before we open the + * channel. + */ + + set_channel_read_state(dev->channel, false); + ret = vmbus_open(dev->channel, 4 * PAGE_SIZE, 4 * PAGE_SIZE, NULL, 0, srv->util_cb, dev->channel); if (ret) -- cgit v1.1 From f878f3d59ed26f489add852ed6d5c8e5f3bbb1aa Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 1 Dec 2012 06:46:35 -0800 Subject: Drivers: hv: Optimize signaling in the read path Now that we have the infratructure for correctly determining when we should signal the host; optimize the signaling on the read side - signaling the guest from the host. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/connection.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) (limited to 'drivers/hv') diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index 650c9f0..d1019a7 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -212,6 +212,9 @@ static void process_chn_event(u32 relid) { struct vmbus_channel *channel; unsigned long flags; + void *arg; + bool read_state; + u32 bytes_to_read; /* * Find the channel based on this relid and invokes the @@ -234,10 +237,29 @@ static void process_chn_event(u32 relid) */ spin_lock_irqsave(&channel->inbound_lock, flags); - if (channel->onchannel_callback != NULL) - channel->onchannel_callback(channel->channel_callback_context); - else + if (channel->onchannel_callback != NULL) { + arg = channel->channel_callback_context; + read_state = channel->batched_reading; + /* + * This callback reads the messages sent by the host. + * We can optimize host to guest signaling by ensuring: + * 1. While reading the channel, we disable interrupts from + * host. + * 2. Ensure that we process all posted messages from the host + * before returning from this callback. + * 3. Once we return, enable signaling from the host. Once this + * state is set we check to see if additional packets are + * available to read. In this case we repeat the process. + */ + + do { + hv_begin_read(&channel->inbound); + channel->onchannel_callback(arg); + bytes_to_read = hv_end_read(&channel->inbound); + } while (read_state && (bytes_to_read != 0)); + } else { pr_err("no channel callback for relid - %u\n", relid); + } spin_unlock_irqrestore(&channel->inbound_lock, flags); } -- cgit v1.1 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 +++++++++------ drivers/hv/hyperv_vmbus.h | 2 +- drivers/hv/ring_buffer.c | 42 +++++++++++++++++++++++++++++++++++++++--- 3 files changed, 49 insertions(+), 10 deletions(-) (limited to 'drivers/hv') diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 773a2f2..727c5f1d6 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; diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 3184f6f..895c898 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -555,7 +555,7 @@ void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info); int hv_ringbuffer_write(struct hv_ring_buffer_info *ring_info, struct scatterlist *sglist, - u32 sgcount); + u32 sgcount, bool *signal); int hv_ringbuffer_peek(struct hv_ring_buffer_info *ring_info, void *buffer, u32 buflen); diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c index 0010792..9bc55f0 100644 --- a/drivers/hv/ring_buffer.c +++ b/drivers/hv/ring_buffer.c @@ -53,6 +53,37 @@ u32 hv_end_read(struct hv_ring_buffer_info *rbi) return read; } +/* + * When we write to the ring buffer, check if the host needs to + * be signaled. Here is the details of this protocol: + * + * 1. The host guarantees that while it is draining the + * ring buffer, it will set the interrupt_mask to + * indicate it does not need to be interrupted when + * new data is placed. + * + * 2. The host guarantees that it will completely drain + * the ring buffer before exiting the read loop. Further, + * once the ring buffer is empty, it will clear the + * interrupt_mask and re-check to see if new data has + * arrived. + */ + +static bool hv_need_to_signal(u32 old_write, struct hv_ring_buffer_info *rbi) +{ + if (rbi->ring_buffer->interrupt_mask) + return false; + + /* + * 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) + return true; + + return false; +} + /* * hv_get_next_write_location() @@ -322,7 +353,7 @@ void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info) * */ int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info, - struct scatterlist *sglist, u32 sgcount) + struct scatterlist *sglist, u32 sgcount, bool *signal) { int i = 0; u32 bytes_avail_towrite; @@ -331,6 +362,7 @@ int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info, struct scatterlist *sg; u32 next_write_location; + u32 old_write; u64 prev_indices = 0; unsigned long flags; @@ -359,6 +391,8 @@ int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info, /* Write to the ring buffer */ next_write_location = hv_get_next_write_location(outring_info); + old_write = next_write_location; + for_each_sg(sglist, sg, sgcount, i) { next_write_location = hv_copyto_ringbuffer(outring_info, @@ -375,14 +409,16 @@ int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info, &prev_indices, sizeof(u64)); - /* Make sure we flush all writes before updating the writeIndex */ - smp_wmb(); + /* Issue a full memory barrier before updating the write index */ + smp_mb(); /* Now, update the write location */ hv_set_next_write_location(outring_info, next_write_location); spin_unlock_irqrestore(&outring_info->ring_lock, flags); + + *signal = hv_need_to_signal(old_write, outring_info); return 0; } -- cgit v1.1 From 4fa152ce24724a4a6b2edd26ca2eb7757ff5b2b8 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 1 Dec 2012 06:46:37 -0800 Subject: Drivers: hv: Get rid of hv_get_ringbuffer_interrupt_mask() This function is no longer used; get rid of it. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hyperv_vmbus.h | 1 - drivers/hv/ring_buffer.c | 13 ------------- 2 files changed, 14 deletions(-) (limited to 'drivers/hv') diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 895c898..ac1e419 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -565,7 +565,6 @@ int hv_ringbuffer_read(struct hv_ring_buffer_info *ring_info, u32 buflen, u32 offset); -u32 hv_get_ringbuffer_interrupt_mask(struct hv_ring_buffer_info *ring_info); void hv_ringbuffer_get_debuginfo(struct hv_ring_buffer_info *ring_info, struct hv_ring_buffer_debug_info *debug_info); diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c index 9bc55f0..2a0babc 100644 --- a/drivers/hv/ring_buffer.c +++ b/drivers/hv/ring_buffer.c @@ -294,19 +294,6 @@ void hv_ringbuffer_get_debuginfo(struct hv_ring_buffer_info *ring_info, } } - -/* - * - * hv_get_ringbuffer_interrupt_mask() - * - * Get the interrupt mask for the specified ring buffer - * - */ -u32 hv_get_ringbuffer_interrupt_mask(struct hv_ring_buffer_info *rbi) -{ - return rbi->ring_buffer->interrupt_mask; -} - /* * * hv_ringbuffer_init() -- cgit v1.1 From 610071c38463998d5a66388ff9956aaeb24b49a8 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 1 Dec 2012 06:46:38 -0800 Subject: Drivers: hv: Support handling multiple VMBUS versions The current code hard coded the vmbus version independent of the host it was running on. Add code to dynamically negotiate the most appropriate version. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/connection.c | 165 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 111 insertions(+), 54 deletions(-) (limited to 'drivers/hv') diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index d1019a7..2b56a3f 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -40,15 +40,111 @@ struct vmbus_connection vmbus_connection = { }; /* + * VMBUS version is 32 bit entity broken up into + * two 16 bit quantities: major_number. minor_number. + * + * 0 . 13 (Windows Server 2008) + * 1 . 1 (Windows 7) + * 2 . 4 (Windows 8) + */ + +#define VERSION_WS2008 ((0 << 16) | (13)) +#define VERSION_WIN7 ((1 << 16) | (1)) +#define VERSION_WIN8 ((2 << 16) | (4)) + +#define VERSION_INVAL -1 + +static __u32 vmbus_get_next_version(__u32 current_version) +{ + switch (current_version) { + case (VERSION_WIN7): + return VERSION_WS2008; + + case (VERSION_WIN8): + return VERSION_WIN7; + + case (VERSION_WS2008): + default: + return VERSION_INVAL; + } +} + +static int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, + __u32 version) +{ + int ret = 0; + struct vmbus_channel_initiate_contact *msg; + unsigned long flags; + int t; + + init_completion(&msginfo->waitevent); + + msg = (struct vmbus_channel_initiate_contact *)msginfo->msg; + + msg->header.msgtype = CHANNELMSG_INITIATE_CONTACT; + msg->vmbus_version_requested = version; + msg->interrupt_page = virt_to_phys(vmbus_connection.int_page); + msg->monitor_page1 = virt_to_phys(vmbus_connection.monitor_pages); + msg->monitor_page2 = virt_to_phys( + (void *)((unsigned long)vmbus_connection.monitor_pages + + PAGE_SIZE)); + + /* + * Add to list before we send the request since we may + * receive the response before returning from this routine + */ + spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); + list_add_tail(&msginfo->msglistentry, + &vmbus_connection.chn_msg_list); + + spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); + + ret = vmbus_post_msg(msg, + sizeof(struct vmbus_channel_initiate_contact)); + if (ret != 0) { + spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); + list_del(&msginfo->msglistentry); + spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, + flags); + return ret; + } + + /* Wait for the connection response */ + t = wait_for_completion_timeout(&msginfo->waitevent, 5*HZ); + if (t == 0) { + spin_lock_irqsave(&vmbus_connection.channelmsg_lock, + flags); + list_del(&msginfo->msglistentry); + spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, + flags); + return -ETIMEDOUT; + } + + spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); + list_del(&msginfo->msglistentry); + spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); + + /* Check if successful */ + if (msginfo->response.version_response.version_supported) { + vmbus_connection.conn_state = CONNECTED; + } else { + pr_err("Unable to connect, " + "Version %d not supported by Hyper-V\n", + version); + return -ECONNREFUSED; + } + + return ret; +} + +/* * vmbus_connect - Sends a connect request on the partition service connection */ int vmbus_connect(void) { int ret = 0; - int t; struct vmbus_channel_msginfo *msginfo = NULL; - struct vmbus_channel_initiate_contact *msg; - unsigned long flags; + __u32 version; /* Initialize the vmbus connection */ vmbus_connection.conn_state = CONNECTING; @@ -99,64 +195,25 @@ int vmbus_connect(void) goto cleanup; } - init_completion(&msginfo->waitevent); - - msg = (struct vmbus_channel_initiate_contact *)msginfo->msg; - - msg->header.msgtype = CHANNELMSG_INITIATE_CONTACT; - msg->vmbus_version_requested = VMBUS_REVISION_NUMBER; - msg->interrupt_page = virt_to_phys(vmbus_connection.int_page); - msg->monitor_page1 = virt_to_phys(vmbus_connection.monitor_pages); - msg->monitor_page2 = virt_to_phys( - (void *)((unsigned long)vmbus_connection.monitor_pages + - PAGE_SIZE)); - /* - * Add to list before we send the request since we may - * receive the response before returning from this routine + * Negotiate a compatible VMBUS version number with the + * host. We start with the highest number we can support + * and work our way down until we negotiate a compatible + * version. */ - spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); - list_add_tail(&msginfo->msglistentry, - &vmbus_connection.chn_msg_list); - - spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); - ret = vmbus_post_msg(msg, - sizeof(struct vmbus_channel_initiate_contact)); - if (ret != 0) { - spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); - list_del(&msginfo->msglistentry); - spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, - flags); - goto cleanup; - } + version = VERSION_WS2008; - /* Wait for the connection response */ - t = wait_for_completion_timeout(&msginfo->waitevent, 5*HZ); - if (t == 0) { - spin_lock_irqsave(&vmbus_connection.channelmsg_lock, - flags); - list_del(&msginfo->msglistentry); - spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, - flags); - ret = -ETIMEDOUT; - goto cleanup; - } + do { + ret = vmbus_negotiate_version(msginfo, version); + if (ret == 0) + break; - spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); - list_del(&msginfo->msglistentry); - spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); + version = vmbus_get_next_version(version); + } while (version != VERSION_INVAL); - /* Check if successful */ - if (msginfo->response.version_response.version_supported) { - vmbus_connection.conn_state = CONNECTED; - } else { - pr_err("Unable to connect, " - "Version %d not supported by Hyper-V\n", - VMBUS_REVISION_NUMBER); - ret = -ECONNREFUSED; + if (version == VERSION_INVAL) goto cleanup; - } kfree(msginfo); return 0; -- cgit v1.1 From 37f7278b81a9ab9b76cf4d716ba420444ca4a616 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 1 Dec 2012 06:46:41 -0800 Subject: Drivers: hv: Save and export negotiated vmbus version Export the negotiated vmbus version as this may be useful for individual drivers. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/connection.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/hv') diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index 2b56a3f..70ea5d1 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include "hyperv_vmbus.h" @@ -54,6 +55,12 @@ struct vmbus_connection vmbus_connection = { #define VERSION_INVAL -1 +/* + * Negotiated protocol version with the host. + */ +__u32 vmbus_proto_version; +EXPORT_SYMBOL_GPL(vmbus_proto_version); + static __u32 vmbus_get_next_version(__u32 current_version) { switch (current_version) { @@ -215,6 +222,8 @@ int vmbus_connect(void) if (version == VERSION_INVAL) goto cleanup; + vmbus_proto_version = version; + pr_info("Negotiated host information %d\n", version); kfree(msginfo); return 0; -- cgit v1.1 From 1f42248d724a413baaafd5f83a8f4746bc6f51a5 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 1 Dec 2012 06:46:42 -0800 Subject: Drivers: hv: Change the signature for hv_signal_event() In preparation for implementing a per-connection signaling framework, change the signature of the function hv_signal_event(). The current code uses a global handle for signaling the host. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/connection.c | 2 +- drivers/hv/hv.c | 7 +++---- drivers/hv/hyperv_vmbus.h | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers/hv') diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index 70ea5d1..3f8ce7b 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -402,5 +402,5 @@ int vmbus_set_event(u32 child_relid) (unsigned long *)vmbus_connection.send_int_page + (child_relid >> 5)); - return hv_signal_event(); + return hv_signal_event(hv_context.signal_event_param); } diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 3648f8f..dd0af89 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -273,13 +273,12 @@ int hv_post_message(union hv_connection_id connection_id, * * This involves a hypercall. */ -u16 hv_signal_event(void) +u16 hv_signal_event(void *con_id) { u16 status; - status = do_hypercall(HVCALL_SIGNAL_EVENT, - hv_context.signal_event_param, - NULL) & 0xFFFF; + status = (do_hypercall(HVCALL_SIGNAL_EVENT, con_id, NULL) & 0xFFFF); + return status; } diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index ac1e419..61d2c4f 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -538,7 +538,7 @@ extern int hv_post_message(union hv_connection_id connection_id, enum hv_message_type message_type, void *payload, size_t payload_size); -extern u16 hv_signal_event(void); +extern u16 hv_signal_event(void *con_id); extern void hv_synic_init(void *irqarg); -- cgit v1.1 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') diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 727c5f1d6..70a34da 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 3f8ce7b..0d8b132 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 61d2c4f..cd48ac3 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.1 From eafa7072e7cd806dff42b705284ca26189e527a4 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 1 Dec 2012 06:46:44 -0800 Subject: Drivers: hv: Move vmbus version definitions to hyperv.h To support version specific optimization in various vmbus drivers, move the vmbus definitions to the public header file. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/connection.c | 15 --------------- 1 file changed, 15 deletions(-) (limited to 'drivers/hv') diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index 0d8b132..56b14e5 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -41,21 +41,6 @@ struct vmbus_connection vmbus_connection = { }; /* - * VMBUS version is 32 bit entity broken up into - * two 16 bit quantities: major_number. minor_number. - * - * 0 . 13 (Windows Server 2008) - * 1 . 1 (Windows 7) - * 2 . 4 (Windows 8) - */ - -#define VERSION_WS2008 ((0 << 16) | (13)) -#define VERSION_WIN7 ((1 << 16) | (1)) -#define VERSION_WIN8 ((2 << 16) | (4)) - -#define VERSION_INVAL -1 - -/* * Negotiated protocol version with the host. */ __u32 vmbus_proto_version; -- cgit v1.1 From b3bf60c7b4665d40b8eae2217b54c4745f49f470 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 1 Dec 2012 06:46:45 -0800 Subject: Drivers: hv: Manage signaling state on a per-connection basis The current code has a global handle for supporting signaling of the host from guest. Make this a per-channel attribute as on some versions of the host we can signal on per-channel handle. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel_mgmt.c | 20 ++++++++++++++++++++ drivers/hv/hyperv_vmbus.h | 21 --------------------- 2 files changed, 20 insertions(+), 21 deletions(-) (limited to 'drivers/hv') diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 7bf5917..f4d9902 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -282,6 +282,26 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr) */ newchannel->batched_reading = true; + /* + * Setup state for signalling the host. + */ + newchannel->sig_event = (struct hv_input_signal_event *) + (ALIGN((unsigned long) + &newchannel->sig_buf, + HV_HYPERCALL_PARAM_ALIGN)); + + newchannel->sig_event->connectionid.asu32 = 0; + newchannel->sig_event->connectionid.u.id = VMBUS_EVENT_CONNECTION_ID; + newchannel->sig_event->flag_number = 0; + newchannel->sig_event->rsvdz = 0; + + if (vmbus_proto_version != VERSION_WS2008) { + newchannel->is_dedicated_interrupt = + (offer->is_dedicated_interrupt != 0); + newchannel->sig_event->connectionid.u.id = + offer->connection_id; + } + memcpy(&newchannel->offermsg, offer, sizeof(struct vmbus_channel_offer_channel)); newchannel->monitor_grp = (u8)offer->monitorid / 32; diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index cd48ac3..1bc7500 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -101,15 +101,6 @@ enum hv_message_type { /* Define invalid partition identifier. */ #define HV_PARTITION_ID_INVALID ((u64)0x0) -/* Define connection identifier type. */ -union hv_connection_id { - u32 asu32; - struct { - u32 id:24; - u32 reserved:8; - } u; -}; - /* Define port identifier type. */ union hv_port_id { u32 asu32; @@ -338,13 +329,6 @@ struct hv_input_post_message { u64 payload[HV_MESSAGE_PAYLOAD_QWORD_COUNT]; }; -/* Definition of the hv_signal_event hypercall input structure. */ -struct hv_input_signal_event { - union hv_connection_id connectionid; - u16 flag_number; - u16 rsvdz; -}; - /* * Versioning definitions used for guests reporting themselves to the * hypervisor, and visa versa. @@ -498,11 +482,6 @@ static const uuid_le VMBUS_SERVICE_ID = { -struct hv_input_signal_event_buffer { - u64 align8; - struct hv_input_signal_event event; -}; - struct hv_context { /* We only support running on top of Hyper-V * So at this point this really can only contain the Hyper-V ID -- cgit v1.1 From 3be77774017fc3c7dd0b434265dfa417aac23545 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 1 Dec 2012 06:46:46 -0800 Subject: Drivers: hv: Cleanup vmbus_set_event() to support win7 and beyond On win7 (ws2008 R2) and beyond, we have the notion of having dedicated interrupts on a per-channel basis. When a channel has a dedicated interrupt assigned, there is no need to set the interrupt bit in the shared page. Implement this optimization. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/connection.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers/hv') diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index 56b14e5..114050d 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -383,10 +383,13 @@ int vmbus_post_msg(void *buffer, size_t buflen) 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 + - (child_relid >> 5)); - return hv_signal_event(hv_context.signal_event_param); + if (!channel->is_dedicated_interrupt) { + /* Each u32 represents 32 channels */ + sync_set_bit(child_relid & 31, + (unsigned long *)vmbus_connection.send_int_page + + (child_relid >> 5)); + } + + return hv_signal_event(channel->sig_event); } -- cgit v1.1 From 917ea427c78670958488f7f304e4629c325969a4 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 1 Dec 2012 06:46:47 -0800 Subject: Drivers: hv: Setup a mapping for Hyper-V's notion cpu ID On win8 (ws2012), incoming vmbus interrupt load can be spread across all available VCPUs in the guest. On a per-channel basis, the interrupts can be bound to specific CPUs. The Linux notion of cpu ID may be different from that of the hypervisor's. Setup a mapping structure. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv.c | 11 +++++++++++ drivers/hv/hyperv_vmbus.h | 10 ++++++++++ 2 files changed, 21 insertions(+) (limited to 'drivers/hv') diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index dd0af89..76304a6 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -137,6 +137,8 @@ int hv_init(void) memset(hv_context.synic_event_page, 0, sizeof(void *) * NR_CPUS); memset(hv_context.synic_message_page, 0, sizeof(void *) * NR_CPUS); + memset(hv_context.vp_index, 0, + sizeof(int) * NR_CPUS); max_leaf = query_hypervisor_info(); @@ -296,6 +298,7 @@ void hv_synic_init(void *irqarg) union hv_synic_siefp siefp; union hv_synic_sint shared_sint; union hv_synic_scontrol sctrl; + u64 vp_index; u32 irq_vector = *((u32 *)(irqarg)); int cpu = smp_processor_id(); @@ -355,6 +358,14 @@ void hv_synic_init(void *irqarg) wrmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64); hv_context.synic_initialized = true; + + /* + * Setup the mapping between Hyper-V's notion + * of cpuid and Linux' notion of cpuid. + * This array will be indexed using Linux cpuid. + */ + rdmsrl(HV_X64_MSR_VP_INDEX, vp_index); + hv_context.vp_index[cpu] = (u32)vp_index; return; cleanup: diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 1bc7500..6bbc197 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -502,6 +502,16 @@ struct hv_context { void *synic_message_page[NR_CPUS]; void *synic_event_page[NR_CPUS]; + /* + * Hypervisor's notion of virtual processor ID is different from + * Linux' notion of CPU ID. This information can only be retrieved + * in the context of the calling CPU. Setup a map for easy access + * to this information: + * + * vp_index[a] is the Hyper-V's processor ID corresponding to + * Linux cpuid 'a'. + */ + u32 vp_index[NR_CPUS]; }; extern struct hv_context hv_context; -- cgit v1.1 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 ++ 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/hv') diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 70a34da..9303252 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 f4d9902..56ed45c 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; -- cgit v1.1 From 6552ecd70cc6f31f4bb9d63f36a7dd023db3e519 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 1 Dec 2012 06:46:49 -0800 Subject: Drivers: hv: Modify the interrupt handling code to support win8 and beyond Starting with Win8 (WS2012), the event page can be used to directly get the channel ID that needs servicing. Modify the channel event handling code to take advantage of this feature. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/connection.c | 26 ++++++++++++++++++++++++-- drivers/hv/vmbus_drv.c | 27 ++++++++++++++++++++++----- 2 files changed, 46 insertions(+), 7 deletions(-) (limited to 'drivers/hv') diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index 114050d..ac71653 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -321,10 +321,32 @@ static void process_chn_event(u32 relid) void vmbus_on_event(unsigned long data) { u32 dword; - u32 maxdword = MAX_NUM_CHANNELS_SUPPORTED >> 5; + u32 maxdword; int bit; u32 relid; - u32 *recv_int_page = vmbus_connection.recv_int_page; + u32 *recv_int_page = NULL; + void *page_addr; + int cpu = smp_processor_id(); + union hv_synic_event_flags *event; + + if ((vmbus_proto_version == VERSION_WS2008) || + (vmbus_proto_version == VERSION_WIN7)) { + maxdword = MAX_NUM_CHANNELS_SUPPORTED >> 5; + recv_int_page = vmbus_connection.recv_int_page; + } else { + /* + * When the host is win8 and beyond, the event page + * can be directly checked to get the id of the channel + * that has the interrupt pending. + */ + maxdword = HV_EVENT_FLAGS_DWORD_COUNT; + page_addr = hv_context.synic_event_page[cpu]; + event = (union hv_synic_event_flags *)page_addr + + VMBUS_MESSAGE_SINT; + recv_int_page = event->flags32; + } + + /* Check events */ if (!recv_int_page) diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 8e1a9ec..c583a04 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -460,15 +460,32 @@ static irqreturn_t vmbus_isr(int irq, void *dev_id) * Hyper-V, and the Windows team suggested we do the same. */ - page_addr = hv_context.synic_event_page[cpu]; - event = (union hv_synic_event_flags *)page_addr + VMBUS_MESSAGE_SINT; + if ((vmbus_proto_version == VERSION_WS2008) || + (vmbus_proto_version == VERSION_WIN7)) { - /* Since we are a child, we only need to check bit 0 */ - if (sync_test_and_clear_bit(0, (unsigned long *) &event->flags32[0])) { + page_addr = hv_context.synic_event_page[cpu]; + event = (union hv_synic_event_flags *)page_addr + + VMBUS_MESSAGE_SINT; + + /* Since we are a child, we only need to check bit 0 */ + if (sync_test_and_clear_bit(0, + (unsigned long *) &event->flags32[0])) { + handled = true; + } + } else { + /* + * Our host is win8 or above. The signaling mechanism + * has changed and we can directly look at the event page. + * If bit n is set then we have an interrup on the channel + * whose id is n. + */ handled = true; - tasklet_schedule(&event_dpc); } + if (handled) + tasklet_schedule(&event_dpc); + + page_addr = hv_context.synic_message_page[cpu]; msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT; -- cgit v1.1 From a119845f6e98c890831bb5639cdad5ca9b79965f Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 1 Dec 2012 06:46:50 -0800 Subject: Drivers: hv: Add code to distribute channel interrupt load Implement a simple policy for distributing incoming interrupt load. We classify channels as (a) performance critical and (b) not performance critical. All non-performance critical channels will be bound to the boot cpu. Performance critical channels will be bound to the remaining available CPUs on a round-robin basis. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel_mgmt.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) (limited to 'drivers/hv') diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 56ed45c..c80fe62 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -257,6 +257,89 @@ static void vmbus_process_offer(struct work_struct *work) } } +enum { + IDE = 0, + SCSI, + NIC, + MAX_PERF_CHN, +}; + +/* + * This is an array of channels (devices) that are performance critical. + * We attempt to distribute the interrupt load for these devices across + * all available CPUs. + */ +static const uuid_le hp_devs[] = { + /* {32412632-86cb-44a2-9b5c-50d1417354f5} */ + /* IDE */ + { + .b = { + 0x32, 0x26, 0x41, 0x32, 0xcb, 0x86, 0xa2, 0x44, + 0x9b, 0x5c, 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5 + } + }, + /* {ba6163d9-04a1-4d29-b605-72e2ffb1dc7f} */ + /* Storage - SCSI */ + { + .b = { + 0xd9, 0x63, 0x61, 0xba, 0xa1, 0x04, 0x29, 0x4d, + 0xb6, 0x05, 0x72, 0xe2, 0xff, 0xb1, 0xdc, 0x7f + } + }, + /* {F8615163-DF3E-46c5-913F-F2D2F965ED0E} */ + /* Network */ + { + .b = { + 0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46, + 0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E + } + }, + +}; + + +/* + * We use this state to statically distribute the channel interrupt load. + */ +static u32 next_vp; + +/* + * Starting with Win8, we can statically distribute the incoming + * channel interrupt load by binding a channel to VCPU. We + * implement here a simple round robin scheme for distributing + * the interrupt load. + * We will bind channels that are not performance critical to cpu 0 and + * performance critical channels (IDE, SCSI and Network) will be uniformly + * distributed across all available CPUs. + */ +static u32 get_vp_index(uuid_le *type_guid) +{ + u32 cur_cpu; + int i; + bool perf_chn = false; + u32 max_cpus = num_online_cpus(); + + for (i = IDE; i < MAX_PERF_CHN; i++) { + if (!memcmp(type_guid->b, hp_devs[i].b, + sizeof(uuid_le))) { + perf_chn = true; + break; + } + } + if ((vmbus_proto_version == VERSION_WS2008) || + (vmbus_proto_version == VERSION_WIN7) || (!perf_chn)) { + /* + * Prior to win8, all channel interrupts are + * delivered on cpu 0. + * Also if the channel is not a performance critical + * channel, bind it to cpu 0. + */ + return 0; + } + cur_cpu = (++next_vp % max_cpus); + return hv_context.vp_index[cur_cpu]; +} + /* * vmbus_onoffer - Handler for channel offers from vmbus in parent partition. * @@ -302,7 +385,7 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr) offer->connection_id; } - newchannel->target_vp = 0; + newchannel->target_vp = get_vp_index(&offer->offer.if_type); memcpy(&newchannel->offermsg, offer, sizeof(struct vmbus_channel_offer_channel)); -- cgit v1.1 From 9acd6442c6839d404c1f47dc0f3ff3e0fb13e44c Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 1 Dec 2012 06:46:51 -0800 Subject: Drivers: hv: Get rid of the unused global signaling state Now that we have implemented a per-connection signaling mechanism, get rid of the global signaling state. For hosts that don't support per-connection signaling handle, we have moved the global state to be a per-channel state. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv.c | 24 ------------------------ drivers/hv/hyperv_vmbus.h | 8 -------- 2 files changed, 32 deletions(-) (limited to 'drivers/hv') diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 76304a6..e989c6fd 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -34,8 +34,6 @@ struct hv_context hv_context = { .synic_initialized = false, .hypercall_page = NULL, - .signal_event_param = NULL, - .signal_event_buffer = NULL, }; /* @@ -170,24 +168,6 @@ int hv_init(void) hv_context.hypercall_page = virtaddr; - /* Setup the global signal event param for the signal event hypercall */ - hv_context.signal_event_buffer = - kmalloc(sizeof(struct hv_input_signal_event_buffer), - GFP_KERNEL); - if (!hv_context.signal_event_buffer) - goto cleanup; - - hv_context.signal_event_param = - (struct hv_input_signal_event *) - (ALIGN((unsigned long) - hv_context.signal_event_buffer, - HV_HYPERCALL_PARAM_ALIGN)); - hv_context.signal_event_param->connectionid.asu32 = 0; - hv_context.signal_event_param->connectionid.u.id = - VMBUS_EVENT_CONNECTION_ID; - hv_context.signal_event_param->flag_number = 0; - hv_context.signal_event_param->rsvdz = 0; - return 0; cleanup: @@ -215,10 +195,6 @@ void hv_cleanup(void) /* Reset our OS id */ wrmsrl(HV_X64_MSR_GUEST_OS_ID, 0); - kfree(hv_context.signal_event_buffer); - hv_context.signal_event_buffer = NULL; - hv_context.signal_event_param = NULL; - if (hv_context.hypercall_page) { hypercall_msr.as_uint64 = 0; wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 6bbc197..9135a6f 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -492,14 +492,6 @@ struct hv_context { bool synic_initialized; - /* - * This is used as an input param to HvCallSignalEvent hypercall. The - * input param is immutable in our usage and must be dynamic mem (vs - * stack or global). */ - struct hv_input_signal_event_buffer *signal_event_buffer; - /* 8-bytes aligned of the buffer above */ - struct hv_input_signal_event *signal_event_param; - void *synic_message_page[NR_CPUS]; void *synic_event_page[NR_CPUS]; /* -- cgit v1.1 From 01986313289bd2e143d693f803c7675ea121a832 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 1 Dec 2012 06:46:52 -0800 Subject: Drivers: hv: Get rid of unnecessary request for offers This call to seek offers is not necessary and just adds unnecessary delay. Get rid of it. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/vmbus_drv.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/hv') diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index c583a04..4c92337 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -592,8 +592,6 @@ int __vmbus_driver_register(struct hv_driver *hv_driver, struct module *owner, c ret = driver_register(&hv_driver->driver); - vmbus_request_offers(); - return ret; } EXPORT_SYMBOL_GPL(__vmbus_driver_register); -- cgit v1.1 From db11f12a11c9f04d504510e1cc20775209b0e509 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 1 Dec 2012 06:46:53 -0800 Subject: Drivers: hv: Manage event tasklets on per-cpu basis Now that we can potentially take vmbus interrupts on any CPU, make the tasklets per-CPU. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv.c | 12 ++++++++++++ drivers/hv/hyperv_vmbus.h | 6 ++++++ drivers/hv/vmbus_drv.c | 4 +--- 3 files changed, 19 insertions(+), 3 deletions(-) (limited to 'drivers/hv') diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index e989c6fd..363532a 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include "hyperv_vmbus.h" @@ -137,6 +138,8 @@ int hv_init(void) sizeof(void *) * NR_CPUS); memset(hv_context.vp_index, 0, sizeof(int) * NR_CPUS); + memset(hv_context.event_dpc, 0, + sizeof(void *) * NR_CPUS); max_leaf = query_hypervisor_info(); @@ -285,6 +288,15 @@ void hv_synic_init(void *irqarg) /* Check the version */ rdmsrl(HV_X64_MSR_SVERSION, version); + hv_context.event_dpc[cpu] = (struct tasklet_struct *) + kmalloc(sizeof(struct tasklet_struct), + GFP_ATOMIC); + if (hv_context.event_dpc[cpu] == NULL) { + pr_err("Unable to allocate event dpc\n"); + goto cleanup; + } + tasklet_init(hv_context.event_dpc[cpu], vmbus_on_event, cpu); + hv_context.synic_message_page[cpu] = (void *)get_zeroed_page(GFP_ATOMIC); diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 9135a6f..becb106 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -504,6 +504,12 @@ struct hv_context { * Linux cpuid 'a'. */ u32 vp_index[NR_CPUS]; + /* + * Starting with win8, we can take channel interrupts on any CPU; + * we will manage the tasklet that handles events on a per CPU + * basis. + */ + struct tasklet_struct *event_dpc[NR_CPUS]; }; extern struct hv_context hv_context; diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 4c92337..6e4f857 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -41,7 +41,6 @@ static struct acpi_device *hv_acpi_dev; static struct tasklet_struct msg_dpc; -static struct tasklet_struct event_dpc; static struct completion probe_event; static int irq; @@ -483,7 +482,7 @@ static irqreturn_t vmbus_isr(int irq, void *dev_id) } if (handled) - tasklet_schedule(&event_dpc); + tasklet_schedule(hv_context.event_dpc[cpu]); page_addr = hv_context.synic_message_page[cpu]; @@ -523,7 +522,6 @@ static int vmbus_bus_init(int irq) } tasklet_init(&msg_dpc, vmbus_on_msg_dpc, 0); - tasklet_init(&event_dpc, vmbus_on_event, 0); ret = bus_register(&hv_bus); if (ret) -- cgit v1.1 From b0209501dc7586cbfbf6d023f2dd3ce4621aff2c Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 1 Dec 2012 06:46:54 -0800 Subject: Drivers: hv: Handle vmbus interrupts concurrently on all cpus Vmbus interrupts are unique in that while the interrupt is delivered on a given vector, these can be handled concurrently on different CPUs. Handle the vmbus interrupts concurrently on all the CPUs. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv.c | 2 +- drivers/hv/vmbus_drv.c | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) (limited to 'drivers/hv') diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 363532a..03e6a1e 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -335,7 +335,7 @@ void hv_synic_init(void *irqarg) shared_sint.as_uint64 = 0; shared_sint.vector = irq_vector; /* HV_SHARED_SINT_IDT_VECTOR + 0x20; */ shared_sint.masked = false; - shared_sint.auto_eoi = false; + shared_sint.auto_eoi = true; wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 6e4f857..e066d41 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include "hyperv_vmbus.h" @@ -501,6 +502,19 @@ static irqreturn_t vmbus_isr(int irq, void *dev_id) } /* + * vmbus interrupt flow handler: + * vmbus interrupts can concurrently occur on multiple CPUs and + * can be handled concurrently. + */ + +void vmbus_flow_handler(unsigned int irq, struct irq_desc *desc) +{ + kstat_incr_irqs_this_cpu(irq, desc); + + desc->action->handler(irq, desc->action->dev_id); +} + +/* * vmbus_bus_init -Main vmbus driver initialization routine. * * Here, we @@ -535,6 +549,13 @@ static int vmbus_bus_init(int irq) goto err_unregister; } + /* + * Vmbus interrupts can be handled concurrently on + * different CPUs. Establish an appropriate interrupt flow + * handler that can support this model. + */ + irq_set_handler(irq, vmbus_flow_handler); + vector = IRQ0_VECTOR + irq; /* -- cgit v1.1 From 5ab05951c586a2ed9e19a3ed8c437346bfabbfb7 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 1 Dec 2012 06:46:55 -0800 Subject: Drivers: hv: Add a check to deal with spurious interrupts We establish the handler before we have fully initialized the VMBUS state. Deal with spurious interrupts. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/vmbus_drv.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/hv') diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index e066d41..797142c 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -454,6 +454,12 @@ static irqreturn_t vmbus_isr(int irq, void *dev_id) union hv_synic_event_flags *event; bool handled = false; + page_addr = hv_context.synic_event_page[cpu]; + if (page_addr == NULL) + return IRQ_NONE; + + event = (union hv_synic_event_flags *)page_addr + + VMBUS_MESSAGE_SINT; /* * Check for events before checking for messages. This is the order * in which events and messages are checked in Windows guests on @@ -463,10 +469,6 @@ static irqreturn_t vmbus_isr(int irq, void *dev_id) if ((vmbus_proto_version == VERSION_WS2008) || (vmbus_proto_version == VERSION_WIN7)) { - page_addr = hv_context.synic_event_page[cpu]; - event = (union hv_synic_event_flags *)page_addr + - VMBUS_MESSAGE_SINT; - /* Since we are a child, we only need to check bit 0 */ if (sync_test_and_clear_bit(0, (unsigned long *) &event->flags32[0])) { -- cgit v1.1 From 2a5c43a821b3b26e6af1cdb987b4daeba6f13a6f Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 1 Dec 2012 06:46:56 -0800 Subject: Drivers: hv: Enable protocol negotiation with win8 hosts Now that we have implemented all of the Win8 (WS2012) functionality, negotiate Win8 protocol with the host. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/connection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/hv') diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index ac71653..3965537 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -194,7 +194,7 @@ int vmbus_connect(void) * version. */ - version = VERSION_WS2008; + version = VERSION_CURRENT; do { ret = vmbus_negotiate_version(msginfo, version); -- cgit v1.1 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') diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 9303252..064257e 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 becb106..ac111f2 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 2a0babc..cafa72f 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.1 From 5fbebb2d2095e5c7d289d5f4ffecc2f2661c584a Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 1 Dec 2012 06:46:58 -0800 Subject: Drivers: hv: Capture the host build information Capture the host build information so it can be presented along with the negotiated vmbus version information. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv.c | 9 +++++++++ drivers/hv/hyperv_vmbus.h | 7 +++++++ 2 files changed, 16 insertions(+) (limited to 'drivers/hv') diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 03e6a1e..0cd2da3 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -40,6 +40,11 @@ struct hv_context hv_context = { /* * query_hypervisor_info - Get version info of the windows hypervisor */ +unsigned int host_info_eax; +unsigned int host_info_ebx; +unsigned int host_info_ecx; +unsigned int host_info_edx; + static int query_hypervisor_info(void) { unsigned int eax; @@ -76,6 +81,10 @@ static int query_hypervisor_info(void) ecx, edx >> 24, edx & 0xFFFFFF); + host_info_eax = eax; + host_info_ebx = ebx; + host_info_ecx = ecx; + host_info_edx = edx; } return max_leaf; } diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index ac111f2..12f2f9e 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -531,6 +531,13 @@ extern void hv_synic_init(void *irqarg); extern void hv_synic_cleanup(void *arg); +/* + * Host version information. + */ +extern unsigned int host_info_eax; +extern unsigned int host_info_ebx; +extern unsigned int host_info_ecx; +extern unsigned int host_info_edx; /* Interface */ -- cgit v1.1 From 3bacaf0ce106d9caca75f64a58f3b938b070df29 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 1 Dec 2012 06:46:59 -0800 Subject: Drivers: hv: Cleanup and consolidate reporting of build/version info Now, cleanup and consolidate reporting of host and vmbus version numbers. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/connection.c | 11 +++++++---- drivers/hv/hv.c | 7 ------- 2 files changed, 7 insertions(+), 11 deletions(-) (limited to 'drivers/hv') diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index 3965537..253a74b 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -120,9 +120,6 @@ static int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, if (msginfo->response.version_response.version_supported) { vmbus_connection.conn_state = CONNECTED; } else { - pr_err("Unable to connect, " - "Version %d not supported by Hyper-V\n", - version); return -ECONNREFUSED; } @@ -208,11 +205,17 @@ int vmbus_connect(void) goto cleanup; vmbus_proto_version = version; - pr_info("Negotiated host information %d\n", version); + pr_info("Hyper-V Host Build:%d-%d.%d-%d-%d.%d; Vmbus version:%d.%d\n", + host_info_eax, host_info_ebx >> 16, + host_info_ebx & 0xFFFF, host_info_ecx, + host_info_edx >> 24, host_info_edx & 0xFFFFFF, + version >> 16, version & 0xFFFF); + kfree(msginfo); return 0; cleanup: + pr_err("Unable to connect to host\n"); vmbus_connection.conn_state = DISCONNECTED; if (vmbus_connection.work_queue) diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 0cd2da3..1c5481d 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -74,13 +74,6 @@ static int query_hypervisor_info(void) edx = 0; op = HVCPUID_VERSION; cpuid(op, &eax, &ebx, &ecx, &edx); - pr_info("Hyper-V Host OS Build:%d-%d.%d-%d-%d.%d\n", - eax, - ebx >> 16, - ebx & 0xFFFF, - ecx, - edx >> 24, - edx & 0xFFFFFF); host_info_eax = eax; host_info_ebx = ebx; host_info_ecx = ecx; -- cgit v1.1 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') diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 064257e..0b122f8 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.1 From 83ebf6e562afc31cf4c6346ee87a7dd9fe39c322 Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Fri, 18 Jan 2013 09:09:32 +0800 Subject: Drivers: hv: vmbus_flow_handler() can be static Signed-off-by: Fengguang Wu Signed-off-by: Greg Kroah-Hartman --- drivers/hv/vmbus_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/hv') diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 797142c..cf19dfa 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -509,7 +509,7 @@ static irqreturn_t vmbus_isr(int irq, void *dev_id) * can be handled concurrently. */ -void vmbus_flow_handler(unsigned int irq, struct irq_desc *desc) +static void vmbus_flow_handler(unsigned int irq, struct irq_desc *desc) { kstat_incr_irqs_this_cpu(irq, desc); -- cgit v1.1 From 0f3f2f86b22c41bb102a15ca4ca7f41c7414ab0d Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Thu, 17 Jan 2013 21:00:51 -0800 Subject: Drivers: hv: Bind all vmbbus interrupts to the boot CPU The default interrupt delivery model in Linux does not support the Hyper-V vmbus delivery model when the guest is configured with multiple VCPUs. I have sent a patch to address this - delivering the vmbus interrupt on a separate IDT vector. Until this patch is applied, bind all vmbus interrupts to the boot CPU. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel_mgmt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/hv') diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index c80fe62..7478ef9 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -337,7 +337,7 @@ static u32 get_vp_index(uuid_le *type_guid) return 0; } cur_cpu = (++next_vp % max_cpus); - return hv_context.vp_index[cur_cpu]; + return 0; } /* -- cgit v1.1 From 7fb96565e3e18ad41857ca6ffdaa9a26ae92df5a Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Wed, 23 Jan 2013 17:42:40 -0800 Subject: Drivers: hv: vmbus: Consolidate all offer GUID definitions in hyperv.h Consolidate all GUID definitions in hyperv.h and use these definitions in implementing channel bindings (as far as interrupt delivery goes). Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel_mgmt.c | 31 ++++++------------------------- 1 file changed, 6 insertions(+), 25 deletions(-) (limited to 'drivers/hv') diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 7478ef9..53a8600 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -265,36 +265,17 @@ enum { }; /* - * This is an array of channels (devices) that are performance critical. + * This is an array of device_ids (device types) that are performance critical. * We attempt to distribute the interrupt load for these devices across * all available CPUs. */ -static const uuid_le hp_devs[] = { - /* {32412632-86cb-44a2-9b5c-50d1417354f5} */ +static const struct hv_vmbus_device_id hp_devs[] = { /* IDE */ - { - .b = { - 0x32, 0x26, 0x41, 0x32, 0xcb, 0x86, 0xa2, 0x44, - 0x9b, 0x5c, 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5 - } - }, - /* {ba6163d9-04a1-4d29-b605-72e2ffb1dc7f} */ + { HV_IDE_GUID, }, /* Storage - SCSI */ - { - .b = { - 0xd9, 0x63, 0x61, 0xba, 0xa1, 0x04, 0x29, 0x4d, - 0xb6, 0x05, 0x72, 0xe2, 0xff, 0xb1, 0xdc, 0x7f - } - }, - /* {F8615163-DF3E-46c5-913F-F2D2F965ED0E} */ + { HV_SCSI_GUID, }, /* Network */ - { - .b = { - 0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46, - 0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E - } - }, - + { HV_NIC_GUID, }, }; @@ -320,7 +301,7 @@ static u32 get_vp_index(uuid_le *type_guid) u32 max_cpus = num_online_cpus(); for (i = IDE; i < MAX_PERF_CHN; i++) { - if (!memcmp(type_guid->b, hp_devs[i].b, + if (!memcmp(type_guid->b, hp_devs[i].guid, sizeof(uuid_le))) { perf_chn = true; break; -- cgit v1.1 From d13984e5c75d1d1db0fb60b468a0e504504c1b4f Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Wed, 23 Jan 2013 17:42:41 -0800 Subject: Drivers: hv: Use consolidated GUID definitions Use the consolidated GUID definitions in the util and balloon drivers. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_balloon.c | 4 +--- drivers/hv/hv_util.c | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 15 deletions(-) (limited to 'drivers/hv') diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index 0ce08c4..c1ad16f 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -1005,9 +1005,7 @@ static int balloon_remove(struct hv_device *dev) static const struct hv_vmbus_device_id id_table[] = { /* Dynamic Memory Class ID */ /* 525074DC-8985-46e2-8057-A307DC18A502 */ - { VMBUS_DEVICE(0xdc, 0x74, 0x50, 0X52, 0x85, 0x89, 0xe2, 0x46, - 0x80, 0x57, 0xa3, 0x07, 0xdc, 0x18, 0xa5, 0x02) - }, + { HV_DM_GUID, }, { }, }; diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index a62a760..8b7868a 100644 --- a/drivers/hv/hv_util.c +++ b/drivers/hv/hv_util.c @@ -314,21 +314,21 @@ static int util_remove(struct hv_device *dev) static const struct hv_vmbus_device_id id_table[] = { /* Shutdown guid */ - { VMBUS_DEVICE(0x31, 0x60, 0x0B, 0X0E, 0x13, 0x52, 0x34, 0x49, - 0x81, 0x8B, 0x38, 0XD9, 0x0C, 0xED, 0x39, 0xDB) - .driver_data = (unsigned long)&util_shutdown }, + { HV_SHUTDOWN_GUID, + .driver_data = (unsigned long)&util_shutdown + }, /* Time synch guid */ - { VMBUS_DEVICE(0x30, 0xe6, 0x27, 0x95, 0xae, 0xd0, 0x7b, 0x49, - 0xad, 0xce, 0xe8, 0x0a, 0xb0, 0x17, 0x5c, 0xaf) - .driver_data = (unsigned long)&util_timesynch }, + { HV_TS_GUID, + .driver_data = (unsigned long)&util_timesynch + }, /* Heartbeat guid */ - { VMBUS_DEVICE(0x39, 0x4f, 0x16, 0x57, 0x15, 0x91, 0x78, 0x4e, - 0xab, 0x55, 0x38, 0x2f, 0x3b, 0xd5, 0x42, 0x2d) - .driver_data = (unsigned long)&util_heartbeat }, + { HV_HEART_BEAT_GUID, + .driver_data = (unsigned long)&util_heartbeat + }, /* KVP guid */ - { VMBUS_DEVICE(0xe7, 0xf4, 0xa0, 0xa9, 0x45, 0x5a, 0x96, 0x4d, - 0xb8, 0x27, 0x8a, 0x84, 0x1e, 0x8c, 0x3, 0xe6) - .driver_data = (unsigned long)&util_kvp }, + { HV_KVP_GUID, + .driver_data = (unsigned long)&util_kvp + }, { }, }; -- cgit v1.1 From 3dd6cb497198a0533a2530b6a345c60c9a29b9bc Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Wed, 23 Jan 2013 17:42:45 -0800 Subject: Drivers: hv: Execute shutdown in a thread context Execute the shutdown code in a thread context. With recent changes made to the shutdown code, shutdown code cannot be invoked from an interrupt context. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_util.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers/hv') diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index 8b7868a..1d4cbd8 100644 --- a/drivers/hv/hv_util.c +++ b/drivers/hv/hv_util.c @@ -49,6 +49,16 @@ static struct hv_util_service util_kvp = { .util_deinit = hv_kvp_deinit, }; +static void perform_shutdown(struct work_struct *dummy) +{ + orderly_poweroff(true); +} + +/* + * Perform the shutdown operation in a thread context. + */ +static DECLARE_WORK(shutdown_work, perform_shutdown); + static void shutdown_onchannelcallback(void *context) { struct vmbus_channel *channel = context; @@ -106,7 +116,7 @@ static void shutdown_onchannelcallback(void *context) } if (execute_shutdown == true) - orderly_poweroff(true); + schedule_work(&shutdown_work); } /* -- cgit v1.1 From 0731572b6c529f8e8a320dc4df6d67d9a595ecf3 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Fri, 25 Jan 2013 16:18:47 -0800 Subject: Drivers: hv: balloon: Make adjustments to the pressure report The host expects that the pressure report includes the pressure due to the pages that have been ballooned. Make necessary adjustments to reflect that. Also, include the free memory information in the pressure report. Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_balloon.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers/hv') diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index ce6f9845..32a96f1 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -529,15 +529,21 @@ static void process_info(struct hv_dynmem_device *dm, struct dm_info_msg *msg) static void post_status(struct hv_dynmem_device *dm) { struct dm_status status; + struct sysinfo val; - + si_meminfo(&val); memset(&status, 0, sizeof(struct dm_status)); status.hdr.type = DM_STATUS_REPORT; status.hdr.size = sizeof(struct dm_status); status.hdr.trans_id = atomic_inc_return(&trans_id); - - status.num_committed = vm_memory_committed(); + /* + * The host expects the guest to report free memory. + * Further, the host expects the pressure information to + * include the ballooned out pages. + */ + status.num_avail = val.freeram; + status.num_committed = vm_memory_committed() + dm->num_pages_ballooned; vmbus_sendpacket(dm->dev->channel, &status, sizeof(struct dm_status), -- cgit v1.1 From e500d158fb07794724f12655f2eb5702fec613c4 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Fri, 8 Feb 2013 15:57:15 -0800 Subject: Drivers: hv: balloon: Add a parameter to delay pressure reporting Delay reporting memory pressure by a specified amount of time. This addresses the problem where the host may take memory balancing decisions based on incorrect memory pressure data that will be posted as soon as the balloon driver is loaded. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_balloon.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'drivers/hv') diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index 32a96f1..f8fc996 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -414,10 +414,17 @@ struct dm_info_msg { static bool hot_add; static bool do_hot_add; +/* + * Delay reporting memory pressure by + * the specified number of seconds. + */ +static uint pressure_report_delay = 30; module_param(hot_add, bool, (S_IRUGO | S_IWUSR)); MODULE_PARM_DESC(hot_add, "If set attempt memory hot_add"); +module_param(pressure_report_delay, uint, (S_IRUGO | S_IWUSR)); +MODULE_PARM_DESC(pressure_report_delay, "Delay in secs in reporting pressure"); static atomic_t trans_id = ATOMIC_INIT(0); static int dm_ring_size = (5 * PAGE_SIZE); @@ -531,6 +538,10 @@ static void post_status(struct hv_dynmem_device *dm) struct dm_status status; struct sysinfo val; + if (pressure_report_delay > 0) { + --pressure_report_delay; + return; + } si_meminfo(&val); memset(&status, 0, sizeof(struct dm_status)); status.hdr.type = DM_STATUS_REPORT; @@ -552,8 +563,6 @@ static void post_status(struct hv_dynmem_device *dm) } - - static void free_balloon_pages(struct hv_dynmem_device *dm, union dm_mem_page_range *range_array) { -- cgit v1.1 From 1c7db96f6feac95d90200ddd0f9b5d94614ea759 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Fri, 8 Feb 2013 15:57:16 -0800 Subject: Drivers: hv: balloon: Prevent the host from ballooning the guest too low Based on the amount of memory being managed set a floor on how low the guest can be ballooned. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_balloon.c | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) (limited to 'drivers/hv') diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index f8fc996..3787321 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -523,6 +523,34 @@ static void process_info(struct hv_dynmem_device *dm, struct dm_info_msg *msg) } } +unsigned long compute_balloon_floor(void) +{ + unsigned long min_pages; +#define MB2PAGES(mb) ((mb) << (20 - PAGE_SHIFT)) + /* Simple continuous piecewiese linear function: + * max MiB -> min MiB gradient + * 0 0 + * 16 16 + * 32 24 + * 128 72 (1/2) + * 512 168 (1/4) + * 2048 360 (1/8) + * 8192 552 (1/32) + * 32768 1320 + * 131072 4392 + */ + if (totalram_pages < MB2PAGES(128)) + min_pages = MB2PAGES(8) + (totalram_pages >> 1); + else if (totalram_pages < MB2PAGES(512)) + min_pages = MB2PAGES(40) + (totalram_pages >> 2); + else if (totalram_pages < MB2PAGES(2048)) + min_pages = MB2PAGES(104) + (totalram_pages >> 3); + else + min_pages = MB2PAGES(296) + (totalram_pages >> 5); +#undef MB2PAGES + return min_pages; +} + /* * Post our status as it relates memory pressure to the * host. Host expects the guests to post this status @@ -552,9 +580,14 @@ static void post_status(struct hv_dynmem_device *dm) * The host expects the guest to report free memory. * Further, the host expects the pressure information to * include the ballooned out pages. + * For a given amount of memory that we are managing, we + * need to compute a floor below which we should not balloon. + * Compute this and add it to the pressure report. */ status.num_avail = val.freeram; - status.num_committed = vm_memory_committed() + dm->num_pages_ballooned; + status.num_committed = vm_memory_committed() + + dm->num_pages_ballooned + + compute_balloon_floor(); vmbus_sendpacket(dm->dev->channel, &status, sizeof(struct dm_status), -- cgit v1.1