Commit a1656454 authored by Alex Ng's avatar Alex Ng Committed by Greg Kroah-Hartman

Drivers: hv: vmbus: Use all supported IC versions to negotiate

Previously, we were assuming that each IC protocol version was tied to a
specific host version. For example, some Windows 10 preview hosts only
support v3 TimeSync even though driver assumes v4 is supported by all
Windows 10 hosts.

The guest will stop trying to negotiate even though older supported
versions may still be offered by the host.

Make IC version negotiation more robust by going through all versions
that are supported by the guest.

Fixes: 3da0401b ("Drivers: hv: utils: Fix the mapping between host
version and protocol to use")
Reported-by: default avatarRolf Neugebauer <rolf.neugebauer@docker.com>
Signed-off-by: default avatarAlex Ng <alexng@messages.microsoft.com>
Signed-off-by: default avatarK. Y. Srinivasan <kys@microsoft.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent ad6d4125
...@@ -204,33 +204,34 @@ static u16 hv_get_dev_type(const struct vmbus_channel *channel) ...@@ -204,33 +204,34 @@ static u16 hv_get_dev_type(const struct vmbus_channel *channel)
* @buf: Raw buffer channel data * @buf: Raw buffer channel data
* *
* @icmsghdrp is of type &struct icmsg_hdr. * @icmsghdrp is of type &struct icmsg_hdr.
* @negop is of type &struct icmsg_negotiate.
* Set up and fill in default negotiate response message. * Set up and fill in default negotiate response message.
* *
* The fw_version specifies the framework version that * The fw_version and fw_vercnt specifies the framework version that
* we can support and srv_version specifies the service * we can support.
* version we can support. *
* The srv_version and srv_vercnt specifies the service
* versions we can support.
*
* Versions are given in decreasing order.
*
* nego_fw_version and nego_srv_version store the selected protocol versions.
* *
* Mainly used by Hyper-V drivers. * Mainly used by Hyper-V drivers.
*/ */
bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp,
struct icmsg_negotiate *negop, u8 *buf, u8 *buf, const int *fw_version, int fw_vercnt,
int fw_version, int srv_version) const int *srv_version, int srv_vercnt,
int *nego_fw_version, int *nego_srv_version)
{ {
int icframe_major, icframe_minor; int icframe_major, icframe_minor;
int icmsg_major, icmsg_minor; int icmsg_major, icmsg_minor;
int fw_major, fw_minor; int fw_major, fw_minor;
int srv_major, srv_minor; int srv_major, srv_minor;
int i; int i, j;
bool found_match = false; bool found_match = false;
struct icmsg_negotiate *negop;
icmsghdrp->icmsgsize = 0x10; 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[ negop = (struct icmsg_negotiate *)&buf[
sizeof(struct vmbuspipe_hdr) + sizeof(struct vmbuspipe_hdr) +
sizeof(struct icmsg_hdr)]; sizeof(struct icmsg_hdr)];
...@@ -246,13 +247,22 @@ bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, ...@@ -246,13 +247,22 @@ bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp,
* support. * support.
*/ */
for (i = 0; i < negop->icframe_vercnt; i++) { for (i = 0; i < fw_vercnt; i++) {
if ((negop->icversion_data[i].major == fw_major) && fw_major = (fw_version[i] >> 16);
(negop->icversion_data[i].minor == fw_minor)) { fw_minor = (fw_version[i] & 0xFFFF);
icframe_major = negop->icversion_data[i].major;
icframe_minor = negop->icversion_data[i].minor; for (j = 0; j < negop->icframe_vercnt; j++) {
found_match = true; if ((negop->icversion_data[j].major == fw_major) &&
(negop->icversion_data[j].minor == fw_minor)) {
icframe_major = negop->icversion_data[j].major;
icframe_minor = negop->icversion_data[j].minor;
found_match = true;
break;
}
} }
if (found_match)
break;
} }
if (!found_match) if (!found_match)
...@@ -260,14 +270,26 @@ bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, ...@@ -260,14 +270,26 @@ bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp,
found_match = false; found_match = false;
for (i = negop->icframe_vercnt; for (i = 0; i < srv_vercnt; i++) {
(i < negop->icframe_vercnt + negop->icmsg_vercnt); i++) { srv_major = (srv_version[i] >> 16);
if ((negop->icversion_data[i].major == srv_major) && srv_minor = (srv_version[i] & 0xFFFF);
(negop->icversion_data[i].minor == srv_minor)) {
icmsg_major = negop->icversion_data[i].major; for (j = negop->icframe_vercnt;
icmsg_minor = negop->icversion_data[i].minor; (j < negop->icframe_vercnt + negop->icmsg_vercnt);
found_match = true; j++) {
if ((negop->icversion_data[j].major == srv_major) &&
(negop->icversion_data[j].minor == srv_minor)) {
icmsg_major = negop->icversion_data[j].major;
icmsg_minor = negop->icversion_data[j].minor;
found_match = true;
break;
}
} }
if (found_match)
break;
} }
/* /*
...@@ -284,6 +306,12 @@ bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, ...@@ -284,6 +306,12 @@ bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp,
negop->icmsg_vercnt = 1; negop->icmsg_vercnt = 1;
} }
if (nego_fw_version)
*nego_fw_version = (icframe_major << 16) | icframe_minor;
if (nego_srv_version)
*nego_srv_version = (icmsg_major << 16) | icmsg_minor;
negop->icversion_data[0].major = icframe_major; negop->icversion_data[0].major = icframe_major;
negop->icversion_data[0].minor = icframe_minor; negop->icversion_data[0].minor = icframe_minor;
negop->icversion_data[1].major = icmsg_major; negop->icversion_data[1].major = icmsg_major;
......
...@@ -31,6 +31,16 @@ ...@@ -31,6 +31,16 @@
#define WIN8_SRV_MINOR 1 #define WIN8_SRV_MINOR 1
#define WIN8_SRV_VERSION (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR) #define WIN8_SRV_VERSION (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR)
#define FCOPY_VER_COUNT 1
static const int fcopy_versions[] = {
WIN8_SRV_VERSION
};
#define FW_VER_COUNT 1
static const int fw_versions[] = {
UTIL_FW_VERSION
};
/* /*
* Global state maintained for transaction that is being processed. * Global state maintained for transaction that is being processed.
* For a class of integration services, including the "file copy service", * For a class of integration services, including the "file copy service",
...@@ -228,8 +238,6 @@ void hv_fcopy_onchannelcallback(void *context) ...@@ -228,8 +238,6 @@ void hv_fcopy_onchannelcallback(void *context)
u64 requestid; u64 requestid;
struct hv_fcopy_hdr *fcopy_msg; struct hv_fcopy_hdr *fcopy_msg;
struct icmsg_hdr *icmsghdr; struct icmsg_hdr *icmsghdr;
struct icmsg_negotiate *negop = NULL;
int util_fw_version;
int fcopy_srv_version; int fcopy_srv_version;
if (fcopy_transaction.state > HVUTIL_READY) if (fcopy_transaction.state > HVUTIL_READY)
...@@ -243,10 +251,10 @@ void hv_fcopy_onchannelcallback(void *context) ...@@ -243,10 +251,10 @@ void hv_fcopy_onchannelcallback(void *context)
icmsghdr = (struct icmsg_hdr *)&recv_buffer[ icmsghdr = (struct icmsg_hdr *)&recv_buffer[
sizeof(struct vmbuspipe_hdr)]; sizeof(struct vmbuspipe_hdr)];
if (icmsghdr->icmsgtype == ICMSGTYPE_NEGOTIATE) { if (icmsghdr->icmsgtype == ICMSGTYPE_NEGOTIATE) {
util_fw_version = UTIL_FW_VERSION; vmbus_prep_negotiate_resp(icmsghdr, recv_buffer,
fcopy_srv_version = WIN8_SRV_VERSION; fw_versions, FW_VER_COUNT,
vmbus_prep_negotiate_resp(icmsghdr, negop, recv_buffer, fcopy_versions, FCOPY_VER_COUNT,
util_fw_version, fcopy_srv_version); NULL, &fcopy_srv_version);
} else { } else {
fcopy_msg = (struct hv_fcopy_hdr *)&recv_buffer[ fcopy_msg = (struct hv_fcopy_hdr *)&recv_buffer[
sizeof(struct vmbuspipe_hdr) + sizeof(struct vmbuspipe_hdr) +
......
...@@ -46,6 +46,19 @@ ...@@ -46,6 +46,19 @@
#define WIN8_SRV_MINOR 0 #define WIN8_SRV_MINOR 0
#define WIN8_SRV_VERSION (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR) #define WIN8_SRV_VERSION (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR)
#define KVP_VER_COUNT 3
static const int kvp_versions[] = {
WIN8_SRV_VERSION,
WIN7_SRV_VERSION,
WS2008_SRV_VERSION
};
#define FW_VER_COUNT 2
static const int fw_versions[] = {
UTIL_FW_VERSION,
UTIL_WS2K8_FW_VERSION
};
/* /*
* Global state maintained for transaction that is being processed. For a class * Global state maintained for transaction that is being processed. For a class
* of integration services, including the "KVP service", the specified protocol * of integration services, including the "KVP service", the specified protocol
...@@ -610,8 +623,6 @@ void hv_kvp_onchannelcallback(void *context) ...@@ -610,8 +623,6 @@ void hv_kvp_onchannelcallback(void *context)
struct hv_kvp_msg *kvp_msg; struct hv_kvp_msg *kvp_msg;
struct icmsg_hdr *icmsghdrp; struct icmsg_hdr *icmsghdrp;
struct icmsg_negotiate *negop = NULL;
int util_fw_version;
int kvp_srv_version; int kvp_srv_version;
static enum {NEGO_NOT_STARTED, static enum {NEGO_NOT_STARTED,
NEGO_IN_PROGRESS, NEGO_IN_PROGRESS,
...@@ -640,28 +651,10 @@ void hv_kvp_onchannelcallback(void *context) ...@@ -640,28 +651,10 @@ void hv_kvp_onchannelcallback(void *context)
sizeof(struct vmbuspipe_hdr)]; sizeof(struct vmbuspipe_hdr)];
if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
/* vmbus_prep_negotiate_resp(icmsghdrp,
* Based on the host, select appropriate recv_buffer, fw_versions, FW_VER_COUNT,
* framework and service versions we will kvp_versions, KVP_VER_COUNT,
* negotiate. NULL, &kvp_srv_version);
*/
switch (vmbus_proto_version) {
case (VERSION_WS2008):
util_fw_version = UTIL_WS2K8_FW_VERSION;
kvp_srv_version = WS2008_SRV_VERSION;
break;
case (VERSION_WIN7):
util_fw_version = UTIL_FW_VERSION;
kvp_srv_version = WIN7_SRV_VERSION;
break;
default:
util_fw_version = UTIL_FW_VERSION;
kvp_srv_version = WIN8_SRV_VERSION;
}
vmbus_prep_negotiate_resp(icmsghdrp, negop,
recv_buffer, util_fw_version,
kvp_srv_version);
} else { } else {
kvp_msg = (struct hv_kvp_msg *)&recv_buffer[ kvp_msg = (struct hv_kvp_msg *)&recv_buffer[
sizeof(struct vmbuspipe_hdr) + sizeof(struct vmbuspipe_hdr) +
......
...@@ -31,6 +31,16 @@ ...@@ -31,6 +31,16 @@
#define VSS_MINOR 0 #define VSS_MINOR 0
#define VSS_VERSION (VSS_MAJOR << 16 | VSS_MINOR) #define VSS_VERSION (VSS_MAJOR << 16 | VSS_MINOR)
#define VSS_VER_COUNT 1
static const int vss_versions[] = {
VSS_VERSION
};
#define FW_VER_COUNT 1
static const int fw_versions[] = {
UTIL_FW_VERSION
};
/* /*
* Timeout values are based on expecations from host * Timeout values are based on expecations from host
*/ */
...@@ -297,7 +307,6 @@ void hv_vss_onchannelcallback(void *context) ...@@ -297,7 +307,6 @@ void hv_vss_onchannelcallback(void *context)
struct icmsg_hdr *icmsghdrp; struct icmsg_hdr *icmsghdrp;
struct icmsg_negotiate *negop = NULL;
if (vss_transaction.state > HVUTIL_READY) if (vss_transaction.state > HVUTIL_READY)
return; return;
...@@ -310,9 +319,10 @@ void hv_vss_onchannelcallback(void *context) ...@@ -310,9 +319,10 @@ void hv_vss_onchannelcallback(void *context)
sizeof(struct vmbuspipe_hdr)]; sizeof(struct vmbuspipe_hdr)];
if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
vmbus_prep_negotiate_resp(icmsghdrp, negop, vmbus_prep_negotiate_resp(icmsghdrp,
recv_buffer, UTIL_FW_VERSION, recv_buffer, fw_versions, FW_VER_COUNT,
VSS_VERSION); vss_versions, VSS_VER_COUNT,
NULL, NULL);
} else { } else {
vss_msg = (struct hv_vss_msg *)&recv_buffer[ vss_msg = (struct hv_vss_msg *)&recv_buffer[
sizeof(struct vmbuspipe_hdr) + sizeof(struct vmbuspipe_hdr) +
......
...@@ -58,7 +58,31 @@ ...@@ -58,7 +58,31 @@
static int sd_srv_version; static int sd_srv_version;
static int ts_srv_version; static int ts_srv_version;
static int hb_srv_version; static int hb_srv_version;
static int util_fw_version;
#define SD_VER_COUNT 2
static const int sd_versions[] = {
SD_VERSION,
SD_VERSION_1
};
#define TS_VER_COUNT 3
static const int ts_versions[] = {
TS_VERSION,
TS_VERSION_3,
TS_VERSION_1
};
#define HB_VER_COUNT 2
static const int hb_versions[] = {
HB_VERSION,
HB_VERSION_1
};
#define FW_VER_COUNT 2
static const int fw_versions[] = {
UTIL_FW_VERSION,
UTIL_WS2K8_FW_VERSION
};
static void shutdown_onchannelcallback(void *context); static void shutdown_onchannelcallback(void *context);
static struct hv_util_service util_shutdown = { static struct hv_util_service util_shutdown = {
...@@ -119,7 +143,6 @@ static void shutdown_onchannelcallback(void *context) ...@@ -119,7 +143,6 @@ static void shutdown_onchannelcallback(void *context)
struct shutdown_msg_data *shutdown_msg; struct shutdown_msg_data *shutdown_msg;
struct icmsg_hdr *icmsghdrp; struct icmsg_hdr *icmsghdrp;
struct icmsg_negotiate *negop = NULL;
vmbus_recvpacket(channel, shut_txf_buf, vmbus_recvpacket(channel, shut_txf_buf,
PAGE_SIZE, &recvlen, &requestid); PAGE_SIZE, &recvlen, &requestid);
...@@ -129,9 +152,14 @@ static void shutdown_onchannelcallback(void *context) ...@@ -129,9 +152,14 @@ static void shutdown_onchannelcallback(void *context)
sizeof(struct vmbuspipe_hdr)]; sizeof(struct vmbuspipe_hdr)];
if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
vmbus_prep_negotiate_resp(icmsghdrp, negop, if (vmbus_prep_negotiate_resp(icmsghdrp, shut_txf_buf,
shut_txf_buf, util_fw_version, fw_versions, FW_VER_COUNT,
sd_srv_version); sd_versions, SD_VER_COUNT,
NULL, &sd_srv_version)) {
pr_info("Shutdown IC version %d.%d\n",
sd_srv_version >> 16,
sd_srv_version & 0xFFFF);
}
} else { } else {
shutdown_msg = shutdown_msg =
(struct shutdown_msg_data *)&shut_txf_buf[ (struct shutdown_msg_data *)&shut_txf_buf[
...@@ -254,7 +282,6 @@ static void timesync_onchannelcallback(void *context) ...@@ -254,7 +282,6 @@ static void timesync_onchannelcallback(void *context)
struct ictimesync_data *timedatap; struct ictimesync_data *timedatap;
struct ictimesync_ref_data *refdata; struct ictimesync_ref_data *refdata;
u8 *time_txf_buf = util_timesynch.recv_buffer; u8 *time_txf_buf = util_timesynch.recv_buffer;
struct icmsg_negotiate *negop = NULL;
vmbus_recvpacket(channel, time_txf_buf, vmbus_recvpacket(channel, time_txf_buf,
PAGE_SIZE, &recvlen, &requestid); PAGE_SIZE, &recvlen, &requestid);
...@@ -264,12 +291,14 @@ static void timesync_onchannelcallback(void *context) ...@@ -264,12 +291,14 @@ static void timesync_onchannelcallback(void *context)
sizeof(struct vmbuspipe_hdr)]; sizeof(struct vmbuspipe_hdr)];
if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
vmbus_prep_negotiate_resp(icmsghdrp, negop, if (vmbus_prep_negotiate_resp(icmsghdrp, time_txf_buf,
time_txf_buf, fw_versions, FW_VER_COUNT,
util_fw_version, ts_versions, TS_VER_COUNT,
ts_srv_version); NULL, &ts_srv_version)) {
pr_info("Using TimeSync version %d.%d\n", pr_info("TimeSync version %d.%d\n",
ts_srv_version >> 16, ts_srv_version & 0xFFFF); ts_srv_version >> 16,
ts_srv_version & 0xFFFF);
}
} else { } else {
if (ts_srv_version > TS_VERSION_3) { if (ts_srv_version > TS_VERSION_3) {
refdata = (struct ictimesync_ref_data *) refdata = (struct ictimesync_ref_data *)
...@@ -313,7 +342,6 @@ static void heartbeat_onchannelcallback(void *context) ...@@ -313,7 +342,6 @@ static void heartbeat_onchannelcallback(void *context)
struct icmsg_hdr *icmsghdrp; struct icmsg_hdr *icmsghdrp;
struct heartbeat_msg_data *heartbeat_msg; struct heartbeat_msg_data *heartbeat_msg;
u8 *hbeat_txf_buf = util_heartbeat.recv_buffer; u8 *hbeat_txf_buf = util_heartbeat.recv_buffer;
struct icmsg_negotiate *negop = NULL;
while (1) { while (1) {
...@@ -327,9 +355,16 @@ static void heartbeat_onchannelcallback(void *context) ...@@ -327,9 +355,16 @@ static void heartbeat_onchannelcallback(void *context)
sizeof(struct vmbuspipe_hdr)]; sizeof(struct vmbuspipe_hdr)];
if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
vmbus_prep_negotiate_resp(icmsghdrp, negop, if (vmbus_prep_negotiate_resp(icmsghdrp,
hbeat_txf_buf, util_fw_version, hbeat_txf_buf,
hb_srv_version); fw_versions, FW_VER_COUNT,
hb_versions, HB_VER_COUNT,
NULL, &hb_srv_version)) {
pr_info("Heartbeat version %d.%d\n",
hb_srv_version >> 16,
hb_srv_version & 0xFFFF);
}
} else { } else {
heartbeat_msg = heartbeat_msg =
(struct heartbeat_msg_data *)&hbeat_txf_buf[ (struct heartbeat_msg_data *)&hbeat_txf_buf[
...@@ -379,33 +414,6 @@ static int util_probe(struct hv_device *dev, ...@@ -379,33 +414,6 @@ static int util_probe(struct hv_device *dev,
hv_set_drvdata(dev, srv); hv_set_drvdata(dev, srv);
/*
* Based on the host; initialize the framework and
* service version numbers we will negotiate.
*/
switch (vmbus_proto_version) {
case (VERSION_WS2008):
util_fw_version = UTIL_WS2K8_FW_VERSION;
sd_srv_version = SD_VERSION_1;
ts_srv_version = TS_VERSION_1;
hb_srv_version = HB_VERSION_1;
break;
case VERSION_WIN7:
case VERSION_WIN8:
case VERSION_WIN8_1:
util_fw_version = UTIL_FW_VERSION;
sd_srv_version = SD_VERSION;
ts_srv_version = TS_VERSION_3;
hb_srv_version = HB_VERSION;
break;
case VERSION_WIN10:
default:
util_fw_version = UTIL_FW_VERSION;
sd_srv_version = SD_VERSION;
ts_srv_version = TS_VERSION;
hb_srv_version = HB_VERSION;
}
ret = vmbus_open(dev->channel, 4 * PAGE_SIZE, 4 * PAGE_SIZE, NULL, 0, ret = vmbus_open(dev->channel, 4 * PAGE_SIZE, 4 * PAGE_SIZE, NULL, 0,
srv->util_cb, dev->channel); srv->util_cb, dev->channel);
if (ret) if (ret)
......
...@@ -1445,9 +1445,10 @@ struct hyperv_service_callback { ...@@ -1445,9 +1445,10 @@ struct hyperv_service_callback {
}; };
#define MAX_SRV_VER 0x7ffffff #define MAX_SRV_VER 0x7ffffff
extern bool vmbus_prep_negotiate_resp(struct icmsg_hdr *, extern bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, u8 *buf,
struct icmsg_negotiate *, u8 *, int, const int *fw_version, int fw_vercnt,
int); const int *srv_version, int srv_vercnt,
int *nego_fw_version, int *nego_srv_version);
void hv_event_tasklet_disable(struct vmbus_channel *channel); void hv_event_tasklet_disable(struct vmbus_channel *channel);
void hv_event_tasklet_enable(struct vmbus_channel *channel); void hv_event_tasklet_enable(struct vmbus_channel *channel);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment