drm/vmwgfx: Use the backdoor port if the HB port is not available
authorThomas Hellstrom <thellstrom@vmware.com>
Wed, 29 May 2019 06:15:19 +0000 (08:15 +0200)
committerThomas Hellstrom <thellstrom@vmware.com>
Tue, 11 Jun 2019 14:59:54 +0000 (16:59 +0200)
The HB port may not be available for various reasons. Either it has been
disabled by a config option or by the hypervisor for other reasons.
In that case, make sure we have a backup plan and use the backdoor port
instead with a performance penalty.

Cc: stable@vger.kernel.org
Fixes: 89da76fde68d ("drm/vmwgfx: Add VMWare host messaging capability")
Signed-off-by: Thomas Hellstrom <thellstrom@vmware.com>
Reviewed-by: Deepak Rawat <drawat@vmware.com>
drivers/gpu/drm/vmwgfx/vmwgfx_msg.c

index 8b9270f..e4e09d4 100644 (file)
@@ -136,6 +136,114 @@ static int vmw_close_channel(struct rpc_channel *channel)
        return 0;
 }
 
+/**
+ * vmw_port_hb_out - Send the message payload either through the
+ * high-bandwidth port if available, or through the backdoor otherwise.
+ * @channel: The rpc channel.
+ * @msg: NULL-terminated message.
+ * @hb: Whether the high-bandwidth port is available.
+ *
+ * Return: The port status.
+ */
+static unsigned long vmw_port_hb_out(struct rpc_channel *channel,
+                                    const char *msg, bool hb)
+{
+       unsigned long si, di, eax, ebx, ecx, edx;
+       unsigned long msg_len = strlen(msg);
+
+       if (hb) {
+               unsigned long bp = channel->cookie_high;
+
+               si = (uintptr_t) msg;
+               di = channel->cookie_low;
+
+               VMW_PORT_HB_OUT(
+                       (MESSAGE_STATUS_SUCCESS << 16) | VMW_PORT_CMD_HB_MSG,
+                       msg_len, si, di,
+                       VMW_HYPERVISOR_HB_PORT | (channel->channel_id << 16),
+                       VMW_HYPERVISOR_MAGIC, bp,
+                       eax, ebx, ecx, edx, si, di);
+
+               return ebx;
+       }
+
+       /* HB port not available. Send the message 4 bytes at a time. */
+       ecx = MESSAGE_STATUS_SUCCESS << 16;
+       while (msg_len && (HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS)) {
+               unsigned int bytes = min_t(size_t, msg_len, 4);
+               unsigned long word = 0;
+
+               memcpy(&word, msg, bytes);
+               msg_len -= bytes;
+               msg += bytes;
+               si = channel->cookie_high;
+               di = channel->cookie_low;
+
+               VMW_PORT(VMW_PORT_CMD_MSG | (MSG_TYPE_SENDPAYLOAD << 16),
+                        word, si, di,
+                        VMW_HYPERVISOR_PORT | (channel->channel_id << 16),
+                        VMW_HYPERVISOR_MAGIC,
+                        eax, ebx, ecx, edx, si, di);
+       }
+
+       return ecx;
+}
+
+/**
+ * vmw_port_hb_in - Receive the message payload either through the
+ * high-bandwidth port if available, or through the backdoor otherwise.
+ * @channel: The rpc channel.
+ * @reply: Pointer to buffer holding reply.
+ * @reply_len: Length of the reply.
+ * @hb: Whether the high-bandwidth port is available.
+ *
+ * Return: The port status.
+ */
+static unsigned long vmw_port_hb_in(struct rpc_channel *channel, char *reply,
+                                   unsigned long reply_len, bool hb)
+{
+       unsigned long si, di, eax, ebx, ecx, edx;
+
+       if (hb) {
+               unsigned long bp = channel->cookie_low;
+
+               si = channel->cookie_high;
+               di = (uintptr_t) reply;
+
+               VMW_PORT_HB_IN(
+                       (MESSAGE_STATUS_SUCCESS << 16) | VMW_PORT_CMD_HB_MSG,
+                       reply_len, si, di,
+                       VMW_HYPERVISOR_HB_PORT | (channel->channel_id << 16),
+                       VMW_HYPERVISOR_MAGIC, bp,
+                       eax, ebx, ecx, edx, si, di);
+
+               return ebx;
+       }
+
+       /* HB port not available. Retrieve the message 4 bytes at a time. */
+       ecx = MESSAGE_STATUS_SUCCESS << 16;
+       while (reply_len) {
+               unsigned int bytes = min_t(unsigned long, reply_len, 4);
+
+               si = channel->cookie_high;
+               di = channel->cookie_low;
+
+               VMW_PORT(VMW_PORT_CMD_MSG | (MSG_TYPE_RECVPAYLOAD << 16),
+                        MESSAGE_STATUS_SUCCESS, si, di,
+                        VMW_HYPERVISOR_PORT | (channel->channel_id << 16),
+                        VMW_HYPERVISOR_MAGIC,
+                        eax, ebx, ecx, edx, si, di);
+
+               if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0)
+                       break;
+
+               memcpy(reply, &ebx, bytes);
+               reply_len -= bytes;
+               reply += bytes;
+       }
+
+       return ecx;
+}
 
 
 /**
@@ -148,11 +256,10 @@ static int vmw_close_channel(struct rpc_channel *channel)
  */
 static int vmw_send_msg(struct rpc_channel *channel, const char *msg)
 {
-       unsigned long eax, ebx, ecx, edx, si, di, bp;
+       unsigned long eax, ebx, ecx, edx, si, di;
        size_t msg_len = strlen(msg);
        int retries = 0;
 
-
        while (retries < RETRIES) {
                retries++;
 
@@ -166,23 +273,14 @@ static int vmw_send_msg(struct rpc_channel *channel, const char *msg)
                        VMW_HYPERVISOR_MAGIC,
                        eax, ebx, ecx, edx, si, di);
 
-               if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0 ||
-                   (HIGH_WORD(ecx) & MESSAGE_STATUS_HB) == 0) {
-                       /* Expected success + high-bandwidth. Give up. */
+               if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) {
+                       /* Expected success. Give up. */
                        return -EINVAL;
                }
 
                /* Send msg */
-               si  = (uintptr_t) msg;
-               di  = channel->cookie_low;
-               bp  = channel->cookie_high;
-
-               VMW_PORT_HB_OUT(
-                       (MESSAGE_STATUS_SUCCESS << 16) | VMW_PORT_CMD_HB_MSG,
-                       msg_len, si, di,
-                       VMW_HYPERVISOR_HB_PORT | (channel->channel_id << 16),
-                       VMW_HYPERVISOR_MAGIC, bp,
-                       eax, ebx, ecx, edx, si, di);
+               ebx = vmw_port_hb_out(channel, msg,
+                                     !!(HIGH_WORD(ecx) & MESSAGE_STATUS_HB));
 
                if ((HIGH_WORD(ebx) & MESSAGE_STATUS_SUCCESS) != 0) {
                        return 0;
@@ -211,7 +309,7 @@ STACK_FRAME_NON_STANDARD(vmw_send_msg);
 static int vmw_recv_msg(struct rpc_channel *channel, void **msg,
                        size_t *msg_len)
 {
-       unsigned long eax, ebx, ecx, edx, si, di, bp;
+       unsigned long eax, ebx, ecx, edx, si, di;
        char *reply;
        size_t reply_len;
        int retries = 0;
@@ -233,8 +331,7 @@ static int vmw_recv_msg(struct rpc_channel *channel, void **msg,
                        VMW_HYPERVISOR_MAGIC,
                        eax, ebx, ecx, edx, si, di);
 
-               if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0 ||
-                   (HIGH_WORD(ecx) & MESSAGE_STATUS_HB) == 0) {
+               if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) {
                        DRM_ERROR("Failed to get reply size for host message.\n");
                        return -EINVAL;
                }
@@ -252,17 +349,8 @@ static int vmw_recv_msg(struct rpc_channel *channel, void **msg,
 
 
                /* Receive buffer */
-               si  = channel->cookie_high;
-               di  = (uintptr_t) reply;
-               bp  = channel->cookie_low;
-
-               VMW_PORT_HB_IN(
-                       (MESSAGE_STATUS_SUCCESS << 16) | VMW_PORT_CMD_HB_MSG,
-                       reply_len, si, di,
-                       VMW_HYPERVISOR_HB_PORT | (channel->channel_id << 16),
-                       VMW_HYPERVISOR_MAGIC, bp,
-                       eax, ebx, ecx, edx, si, di);
-
+               ebx = vmw_port_hb_in(channel, reply, reply_len,
+                                    !!(HIGH_WORD(ecx) & MESSAGE_STATUS_HB));
                if ((HIGH_WORD(ebx) & MESSAGE_STATUS_SUCCESS) == 0) {
                        kfree(reply);