Skip to content

Commit

Permalink
Drivers: hv: util: Fix a bug in version negotiation code for util ser…
Browse files Browse the repository at this point in the history
…vices

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 <[email protected]>
Reviewed-by: Haiyang Zhang <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
  • Loading branch information
kattisrinivasan authored and gregkh committed Jul 26, 2013
1 parent d12e146 commit 6741335
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 39 deletions.
75 changes: 53 additions & 22 deletions drivers/hv/channel_mgmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,57 +48,88 @@ 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
* support.
*/

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);
Expand Down
24 changes: 23 additions & 1 deletion drivers/hv/hv_kvp.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@
#include <linux/hyperv.h>


/*
* Pre win8 version numbers used in ws2008 and ws 2008 r2 (win7)
*/
#define WIN7_SRV_MAJOR 3
#define WIN7_SRV_MINOR 0
#define WIN7_SRV_MAJOR_MINOR (WIN7_SRV_MAJOR << 16 | WIN7_SRV_MINOR)

#define WIN8_SRV_MAJOR 4
#define WIN8_SRV_MINOR 0
#define WIN8_SRV_MAJOR_MINOR (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR)

/*
* Global state maintained for transaction that is being processed.
Expand Down Expand Up @@ -593,8 +603,19 @@ void hv_kvp_onchannelcallback(void *context)
sizeof(struct vmbuspipe_hdr)];

if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
/*
* We start with win8 version and if the host cannot
* support that we use the previous version.
*/
if (vmbus_prep_negotiate_resp(icmsghdrp, negop,
recv_buffer, UTIL_FW_MAJOR_MINOR,
WIN8_SRV_MAJOR_MINOR))
goto done;

vmbus_prep_negotiate_resp(icmsghdrp, negop,
recv_buffer, MAX_SRV_VER, MAX_SRV_VER);
recv_buffer, UTIL_FW_MAJOR_MINOR,
WIN7_SRV_MAJOR_MINOR);

} else {
kvp_msg = (struct hv_kvp_msg *)&recv_buffer[
sizeof(struct vmbuspipe_hdr) +
Expand Down Expand Up @@ -626,6 +647,7 @@ void hv_kvp_onchannelcallback(void *context)
return;

}
done:

icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
| ICMSGHDRFLAG_RESPONSE;
Expand Down
18 changes: 6 additions & 12 deletions drivers/hv/hv_snapshot.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
#include <linux/workqueue.h>
#include <linux/hyperv.h>

#define VSS_MAJOR 5
#define VSS_MINOR 0
#define VSS_MAJOR_MINOR (VSS_MAJOR << 16 | VSS_MINOR)



/*
Expand Down Expand Up @@ -186,18 +190,8 @@ void hv_vss_onchannelcallback(void *context)

if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
vmbus_prep_negotiate_resp(icmsghdrp, negop,
recv_buffer, MAX_SRV_VER, MAX_SRV_VER);
/*
* We currently negotiate the highest number the
* host has presented. If this version is not
* atleast 5.0, reject.
*/
negop = (struct icmsg_negotiate *)&recv_buffer[
sizeof(struct vmbuspipe_hdr) +
sizeof(struct icmsg_hdr)];

if (negop->icversion_data[1].major < 5)
negop->icframe_vercnt = 0;
recv_buffer, UTIL_FW_MAJOR_MINOR,
VSS_MAJOR_MINOR);
} else {
vss_msg = (struct hv_vss_msg *)&recv_buffer[
sizeof(struct vmbuspipe_hdr) +
Expand Down
21 changes: 18 additions & 3 deletions drivers/hv/hv_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,18 @@
#include <linux/reboot.h>
#include <linux/hyperv.h>

#define SHUTDOWN_MAJOR 3
#define SHUTDOWN_MINOR 0
#define SHUTDOWN_MAJOR_MINOR (SHUTDOWN_MAJOR << 16 | SHUTDOWN_MINOR)

#define TIMESYNCH_MAJOR 3
#define TIMESYNCH_MINOR 0
#define TIMESYNCH_MAJOR_MINOR (TIMESYNCH_MAJOR << 16 | TIMESYNCH_MINOR)

#define HEARTBEAT_MAJOR 3
#define HEARTBEAT_MINOR 0
#define HEARTBEAT_MAJOR_MINOR (HEARTBEAT_MAJOR << 16 | HEARTBEAT_MINOR)

static void shutdown_onchannelcallback(void *context);
static struct hv_util_service util_shutdown = {
.util_cb = shutdown_onchannelcallback,
Expand Down Expand Up @@ -87,7 +99,8 @@ static void shutdown_onchannelcallback(void *context)

if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
vmbus_prep_negotiate_resp(icmsghdrp, negop,
shut_txf_buf, MAX_SRV_VER, MAX_SRV_VER);
shut_txf_buf, UTIL_FW_MAJOR_MINOR,
SHUTDOWN_MAJOR_MINOR);
} else {
shutdown_msg =
(struct shutdown_msg_data *)&shut_txf_buf[
Expand Down Expand Up @@ -213,7 +226,8 @@ static void timesync_onchannelcallback(void *context)

if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
vmbus_prep_negotiate_resp(icmsghdrp, NULL, time_txf_buf,
MAX_SRV_VER, MAX_SRV_VER);
UTIL_FW_MAJOR_MINOR,
TIMESYNCH_MAJOR_MINOR);
} else {
timedatap = (struct ictimesync_data *)&time_txf_buf[
sizeof(struct vmbuspipe_hdr) +
Expand Down Expand Up @@ -253,7 +267,8 @@ static void heartbeat_onchannelcallback(void *context)

if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
vmbus_prep_negotiate_resp(icmsghdrp, NULL,
hbeat_txf_buf, MAX_SRV_VER, MAX_SRV_VER);
hbeat_txf_buf, UTIL_FW_MAJOR_MINOR,
HEARTBEAT_MAJOR_MINOR);
} else {
heartbeat_msg =
(struct heartbeat_msg_data *)&hbeat_txf_buf[
Expand Down
10 changes: 9 additions & 1 deletion include/linux/hyperv.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@

#include <linux/types.h>

/*
* Framework version for util services.
*/

#define UTIL_FW_MAJOR 3
#define UTIL_FW_MINOR 0
#define UTIL_FW_MAJOR_MINOR (UTIL_FW_MAJOR << 16 | UTIL_FW_MINOR)


/*
* Implementation of host controlled snapshot of the guest.
Expand Down Expand Up @@ -1494,7 +1502,7 @@ struct hyperv_service_callback {
};

#define MAX_SRV_VER 0x7ffffff
extern void vmbus_prep_negotiate_resp(struct icmsg_hdr *,
extern bool vmbus_prep_negotiate_resp(struct icmsg_hdr *,
struct icmsg_negotiate *, u8 *, int,
int);

Expand Down

0 comments on commit 6741335

Please sign in to comment.