From: Hardik Garg <[email protected]> Sent: Tuesday, July 14, 2026 
2:39 PM
> 
> VMBus sends CHANNELMSG_INITIATE_CONTACT through a Hyper-V message
> connection ID. Older protocol versions use VMBUS_MESSAGE_CONNECTION_ID,
> while protocol version 5.0 and newer normally use
> VMBUS_MESSAGE_CONNECTION_ID_4.
> 
> When the Linux VMBus driver runs at VTL2, the VMBus control plane may
> be reached through a VMBus relay instead of the standard host endpoint.
> In that setup the relay listens on the redirect message connection ID,
> and an Initiate Contact message sent to the standard ID is not delivered
> to the control plane.
> 
> The connection ID selects the Hyper-V message port used to reach the
> VMBus control plane. If Linux uses the wrong port, the host does not
> receive the Initiate Contact message.

This is a judgment call, but to me the previous two paragraphs are
explaining VTL2 implementation details that aren't necessary for
understanding this patch. Yes, in earlier discussions I asked questions
about "why" all this funky behavior with connection IDs, but that's
just me as a former Microsoft insider. :-)  Details about VMBus relays and
message ports are fairly obscure for people in the general Linux community.
It would be sufficient for the commit message to simply state that VTL2
may respond on either of two connection IDs. There's no separate indication
of which one will respond in a given VM, so it is necessary to try at runtime
to see which one works.

> 
> For Linux running at VTL2 with VMBus protocol 5.0 or newer, try the
> redirect connection ID first. In VTL2, the redirect connection ID is the
> VMBus relay endpoint. If the relay is present, it accepts Initiate
> Contact and completes the normal version-response handshake. Systems
> without the relay reject the redirect ID synchronously with
> HV_STATUS_INVALID_CONNECTION_ID, allowing fallback to
> VMBUS_MESSAGE_CONNECTION_ID_4.

Is there a reason for trying the REDIRECT connection ID first? Or
is the ordering of trying arbitrary? If the REDIRECT connection
ID must be tried first, state that as a VTL2 requirement.

> 
> Return a distinct error for an invalid Initiate Contact connection ID so
> this fallback does not mask other post-message failures or
> protocol-version rejections. For older protocol versions, and for Linux
> below VTL2, keep the existing connection ID selection.
> 
> Signed-off-by: Hardik Garg <[email protected]>
> ---
>  drivers/hv/connection.c   | 76 
> ++++++++++++++++++++++++++++++++++++++++-------
>  drivers/hv/hyperv_vmbus.h |  2 ++
>  2 files changed, 67 insertions(+), 11 deletions(-)
> 
> diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c
> index 1fe3573ae52a4..eb871f87a819d 100644
> --- a/drivers/hv/connection.c
> +++ b/drivers/hv/connection.c
> @@ -71,7 +71,19 @@ module_param(max_version, uint, S_IRUGO);
>  MODULE_PARM_DESC(max_version,
>                "Maximal VMBus protocol version which can be negotiated");
> 
> -int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 
> version)
> +/* Connection IDs to try for VTL2 VMBus protocol 5.0 and newer. */
> +static const u32 connection_ids[] = {
> +     VMBUS_MESSAGE_CONNECTION_ID_REDIRECT,
> +     VMBUS_MESSAGE_CONNECTION_ID_4,
> +};

Using an array of connection IDs to iterate through matches the
design of how multiple VMBus protocols versions are tried. But with
only two entries in the array, this feels a bit over-engineered unless
there is reason to expect that more connection ID values are coming
in the reasonably near future. Absent an array, the code in the
vmbus_negotiate_version() could just hard code to test the redirect
connection ID first if running at VTL2, and if that fails with -ENXIO,
fall through to the normal case. 

> +
> +/*
> + * Send one CHANNELMSG_INITIATE_CONTACT attempt.
> + * The caller supplies the message connection ID and owns retry/fallback
> + * policy.
> + */
> +static int vmbus_try_connection_id(struct vmbus_channel_msginfo *msginfo,
> +                                u32 version, u32 connection_id)
>  {
>       int ret = 0;
>       struct vmbus_channel_initiate_contact *msg;
> @@ -87,7 +99,7 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo 
> *msginfo, u32 version)
> 
>       /*
>        * VMBus protocol 5.0 (VERSION_WIN10_V5) and higher require that we must
> -      * use VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate Contact Message,
> +      * use connection_id for the Initiate Contact Message,

This wording of this comment now feels a bit awkward. How about:

"For VMBus protocol 5.0 (VERSION_WIN10_V5) and higher, use the caller supplied
 connection_id for the Initiate Contact Message so the caller can implement the
necessary retry scheme. For subsequent messages, use the Message Connection ID
field in the host-returned Version Response Message."

followed by the rest of the existing text.


>        * and for subsequent messages, we must use the Message Connection ID
>        * field in the host-returned Version Response Message. And, with
>        * VERSION_WIN10_V5 and higher, we don't use msg->interrupt_page, but we
> @@ -99,7 +111,7 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo 
> *msginfo, u32 version)
>       if (version >= VERSION_WIN10_V5) {
>               msg->msg_sint = VMBUS_MESSAGE_SINT;
>               msg->msg_vtl = ms_hyperv.vtl;
> -             vmbus_connection.msg_conn_id = VMBUS_MESSAGE_CONNECTION_ID_4;
> +             vmbus_connection.msg_conn_id = connection_id;
>       } else {
>               msg->interrupt_page = virt_to_phys(vmbus_connection.int_page);
>               vmbus_connection.msg_conn_id = VMBUS_MESSAGE_CONNECTION_ID;
> @@ -161,6 +173,51 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo 
> *msginfo, u32 version)
>       return ret;
>  }
> 
> +/*
> + * Negotiate the given VMBus protocol version with the host.
> + * Protocol-specific connection ID policy is handled here so the single-try
> + * helper stays simple.
> + */
> +int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 
> version)
> +{
> +     int ret;
> +     size_t j;
> +
> +     /*
> +      * The redirect ID is not a speculative endpoint. If the VMBus relay is

What is a "speculative endpoint"?  And per my comment above, I'd drop
discussion of VMBus relay, and just phrase things in terms of trying the
REDIRECT connection ID, and if that doesn't work, then try the normal one.  

> +      * present, it accepts INITIATE_CONTACT and completes the normal version
> +      * response. Systems without the relay reject the ID synchronously, so
> +      * negotiation falls back to VMBUS_MESSAGE_CONNECTION_ID_4.
> +      */
> +     if (version >= VERSION_WIN10_V5 && ms_hyperv.vtl >= 2) {
> +             for (j = 0; j < ARRAY_SIZE(connection_ids); j++) {
> +                     ret = vmbus_try_connection_id(msginfo, version,
> +                                                   connection_ids[j]);
> +                     if (vmbus_connection.conn_state == CONNECTED)
> +                             return 0;
> +
> +                     if (ret == -ETIMEDOUT)
> +                             return ret;
> +
> +                     if (connection_ids[j] ==
> +                         VMBUS_MESSAGE_CONNECTION_ID_REDIRECT &&
> +                         ret == -ENXIO)
> +                             continue;
> +
> +                     return ret;
> +             }
> +             return ret;
> +     }

Per my earlier comment about not having an array of connection IDs, this
code could be as simple as:

if (version >= VERSION_WIN10_V5 && ms_hyperv.vtl >=2) {
        ret = vmbus_try_connection_id(msginfo, version,
                                VMBUS_MESSAGE_CONNECTION_ID_REDIRECT);
        if (ret != -ENXIO)
                return ret;
}

> +
> +     /*
> +      * Non-redirect path. Protocol 5.0+ below VTL2 uses the standard
> +      * v5 connection ID; pre-v5 ignores the supplied ID and uses the
> +      * legacy connection ID.
> +      */
> +     return vmbus_try_connection_id(msginfo, version,
> +                                    VMBUS_MESSAGE_CONNECTION_ID_4);
> +}
> +
>  /*
>   * vmbus_connect - Sends a connect request on the partition service 
> connection
>   */
> @@ -455,17 +507,14 @@ int vmbus_post_msg(void *buffer, size_t buflen, bool 
> can_sleep)
>               switch (ret) {
>               case HV_STATUS_INVALID_CONNECTION_ID:
>                       /*
> -                      * See vmbus_negotiate_version(): VMBus protocol 5.0
> -                      * and higher require that we must use
> -                      * VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate
> -                      * Contact message, but on old hosts that only
> -                      * support VMBus protocol 4.0 or lower, here we get
> -                      * HV_STATUS_INVALID_CONNECTION_ID and we should
> -                      * return an error immediately without retrying.
> +                      * Let negotiation distinguish an unusable
> +                      * endpoint from other failures.
> +                      *
> +                      * Other messages keep retry behavior.
>                        */
>                       hdr = buffer;
>                       if (hdr->msgtype == CHANNELMSG_INITIATE_CONTACT)
> -                             return -EINVAL;
> +                             return -ENXIO;
>                       /*
>                        * We could get this if we send messages too
>                        * frequently.
> diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
> index 49a72a4f3f6a7..951bdebdc0526 100644
> --- a/drivers/hv/hyperv_vmbus.h
> +++ b/drivers/hv/hyperv_vmbus.h
> @@ -109,6 +109,8 @@ struct hv_input_post_message {
>  enum {
>       VMBUS_MESSAGE_CONNECTION_ID     = 1,
>       VMBUS_MESSAGE_CONNECTION_ID_4   = 4,
> +     /* VMBus relay port used for INITIATE_CONTACT probing. */
> +     VMBUS_MESSAGE_CONNECTION_ID_REDIRECT = 0x800074,
>       VMBUS_MESSAGE_PORT_ID           = 1,
>       VMBUS_EVENT_CONNECTION_ID       = 2,
>       VMBUS_EVENT_PORT_ID             = 2,
> --
> 2.34.1


Reply via email to