diff options
author | K. Y. Srinivasan <kys@microsoft.com> | 2013-07-02 10:31:30 -0700 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-07-26 16:40:41 -0700 |
commit | 6741335bc7294548ac95cb1f4671991ff30da193 (patch) | |
tree | b5930cd4e558ff76838966e8a23941352acf9a0e /drivers/hv/channel_mgmt.c | |
parent | d12e14692448e9f256028f53926a9bd3c3091e11 (diff) |
Drivers: hv: util: Fix a bug in version negotiation code for util services
The current code picked the highest version advertised by the host. WS2012 R2
has implemented a protocol version for KVP that is not compatible with prior
protocol versions of KVP. Fix the bug in the current code by explicitly specifying
the protocol version that the guest can support.
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/hv/channel_mgmt.c')
-rw-r--r-- | drivers/hv/channel_mgmt.c | 75 |
1 files changed, 53 insertions, 22 deletions
diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 0df75908200e..12ec8c801b25 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -48,30 +48,39 @@ struct vmbus_channel_message_table_entry { * @negop is of type &struct icmsg_negotiate. * Set up and fill in default negotiate response message. * - * The max_fw_version specifies the maximum framework version that - * we can support and max _srv_version specifies the maximum service - * version we can support. A special value MAX_SRV_VER can be - * specified to indicate that we can handle the maximum version - * exposed by the host. + * The fw_version specifies the framework version that + * we can support and srv_version specifies the service + * version we can support. * * Mainly used by Hyper-V drivers. */ -void vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, +bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, struct icmsg_negotiate *negop, u8 *buf, - int max_fw_version, int max_srv_version) + int fw_version, int srv_version) { - int icframe_vercnt; - int icmsg_vercnt; + int icframe_major, icframe_minor; + int icmsg_major, icmsg_minor; + int fw_major, fw_minor; + int srv_major, srv_minor; int i; + bool found_match = false; icmsghdrp->icmsgsize = 0x10; + fw_major = (fw_version >> 16); + fw_minor = (fw_version & 0xFFFF); + + srv_major = (srv_version >> 16); + srv_minor = (srv_version & 0xFFFF); negop = (struct icmsg_negotiate *)&buf[ sizeof(struct vmbuspipe_hdr) + sizeof(struct icmsg_hdr)]; - icframe_vercnt = negop->icframe_vercnt; - icmsg_vercnt = negop->icmsg_vercnt; + icframe_major = negop->icframe_vercnt; + icframe_minor = 0; + + icmsg_major = negop->icmsg_vercnt; + icmsg_minor = 0; /* * Select the framework version number we will @@ -79,26 +88,48 @@ void vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, */ for (i = 0; i < negop->icframe_vercnt; i++) { - if (negop->icversion_data[i].major <= max_fw_version) - icframe_vercnt = negop->icversion_data[i].major; + if ((negop->icversion_data[i].major == fw_major) && + (negop->icversion_data[i].minor == fw_minor)) { + icframe_major = negop->icversion_data[i].major; + icframe_minor = negop->icversion_data[i].minor; + found_match = true; + } } + if (!found_match) + goto fw_error; + + found_match = false; + for (i = negop->icframe_vercnt; (i < negop->icframe_vercnt + negop->icmsg_vercnt); i++) { - if (negop->icversion_data[i].major <= max_srv_version) - icmsg_vercnt = negop->icversion_data[i].major; + if ((negop->icversion_data[i].major == srv_major) && + (negop->icversion_data[i].minor == srv_minor)) { + icmsg_major = negop->icversion_data[i].major; + icmsg_minor = negop->icversion_data[i].minor; + found_match = true; + } } /* - * Respond with the maximum framework and service + * Respond with the framework and service * version numbers we can support. */ - negop->icframe_vercnt = 1; - negop->icmsg_vercnt = 1; - negop->icversion_data[0].major = icframe_vercnt; - negop->icversion_data[0].minor = 0; - negop->icversion_data[1].major = icmsg_vercnt; - negop->icversion_data[1].minor = 0; + +fw_error: + if (!found_match) { + negop->icframe_vercnt = 0; + negop->icmsg_vercnt = 0; + } else { + negop->icframe_vercnt = 1; + negop->icmsg_vercnt = 1; + } + + negop->icversion_data[0].major = icframe_major; + negop->icversion_data[0].minor = icframe_minor; + negop->icversion_data[1].major = icmsg_major; + negop->icversion_data[1].minor = icmsg_minor; + return found_match; } EXPORT_SYMBOL_GPL(vmbus_prep_negotiate_resp); |