diff options
| author | Thomas Hellstrom <thellstrom@vmware.com> | 2019-05-29 02:15:19 -0400 |
|---|---|---|
| committer | Thomas Hellstrom <thellstrom@vmware.com> | 2019-06-11 10:59:54 -0400 |
| commit | cc0ba0d8624f210995924bb57a8b181ce8976606 (patch) | |
| tree | d9f31efec66c045ee2bb70dc00b75b4784be9984 | |
| parent | 5ed7f4b5eca11c3c69e7c8b53e4321812bc1ee1e (diff) | |
drm/vmwgfx: Use the backdoor port if the HB port is not available
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>
| -rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_msg.c | 146 |
1 files changed, 117 insertions, 29 deletions
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c b/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c index 8b9270f31409..e4e09d47c5c0 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c | |||
| @@ -136,6 +136,114 @@ static int vmw_close_channel(struct rpc_channel *channel) | |||
| 136 | return 0; | 136 | return 0; |
| 137 | } | 137 | } |
| 138 | 138 | ||
| 139 | /** | ||
| 140 | * vmw_port_hb_out - Send the message payload either through the | ||
| 141 | * high-bandwidth port if available, or through the backdoor otherwise. | ||
| 142 | * @channel: The rpc channel. | ||
| 143 | * @msg: NULL-terminated message. | ||
| 144 | * @hb: Whether the high-bandwidth port is available. | ||
| 145 | * | ||
| 146 | * Return: The port status. | ||
| 147 | */ | ||
| 148 | static unsigned long vmw_port_hb_out(struct rpc_channel *channel, | ||
| 149 | const char *msg, bool hb) | ||
| 150 | { | ||
| 151 | unsigned long si, di, eax, ebx, ecx, edx; | ||
| 152 | unsigned long msg_len = strlen(msg); | ||
| 153 | |||
| 154 | if (hb) { | ||
| 155 | unsigned long bp = channel->cookie_high; | ||
| 156 | |||
| 157 | si = (uintptr_t) msg; | ||
| 158 | di = channel->cookie_low; | ||
| 159 | |||
| 160 | VMW_PORT_HB_OUT( | ||
| 161 | (MESSAGE_STATUS_SUCCESS << 16) | VMW_PORT_CMD_HB_MSG, | ||
| 162 | msg_len, si, di, | ||
| 163 | VMW_HYPERVISOR_HB_PORT | (channel->channel_id << 16), | ||
| 164 | VMW_HYPERVISOR_MAGIC, bp, | ||
| 165 | eax, ebx, ecx, edx, si, di); | ||
| 166 | |||
| 167 | return ebx; | ||
| 168 | } | ||
| 169 | |||
| 170 | /* HB port not available. Send the message 4 bytes at a time. */ | ||
| 171 | ecx = MESSAGE_STATUS_SUCCESS << 16; | ||
| 172 | while (msg_len && (HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS)) { | ||
| 173 | unsigned int bytes = min_t(size_t, msg_len, 4); | ||
| 174 | unsigned long word = 0; | ||
| 175 | |||
| 176 | memcpy(&word, msg, bytes); | ||
| 177 | msg_len -= bytes; | ||
| 178 | msg += bytes; | ||
| 179 | si = channel->cookie_high; | ||
| 180 | di = channel->cookie_low; | ||
| 181 | |||
| 182 | VMW_PORT(VMW_PORT_CMD_MSG | (MSG_TYPE_SENDPAYLOAD << 16), | ||
| 183 | word, si, di, | ||
| 184 | VMW_HYPERVISOR_PORT | (channel->channel_id << 16), | ||
| 185 | VMW_HYPERVISOR_MAGIC, | ||
| 186 | eax, ebx, ecx, edx, si, di); | ||
| 187 | } | ||
| 188 | |||
| 189 | return ecx; | ||
| 190 | } | ||
| 191 | |||
| 192 | /** | ||
| 193 | * vmw_port_hb_in - Receive the message payload either through the | ||
| 194 | * high-bandwidth port if available, or through the backdoor otherwise. | ||
| 195 | * @channel: The rpc channel. | ||
| 196 | * @reply: Pointer to buffer holding reply. | ||
| 197 | * @reply_len: Length of the reply. | ||
| 198 | * @hb: Whether the high-bandwidth port is available. | ||
| 199 | * | ||
| 200 | * Return: The port status. | ||
| 201 | */ | ||
| 202 | static unsigned long vmw_port_hb_in(struct rpc_channel *channel, char *reply, | ||
| 203 | unsigned long reply_len, bool hb) | ||
| 204 | { | ||
| 205 | unsigned long si, di, eax, ebx, ecx, edx; | ||
| 206 | |||
| 207 | if (hb) { | ||
| 208 | unsigned long bp = channel->cookie_low; | ||
| 209 | |||
| 210 | si = channel->cookie_high; | ||
| 211 | di = (uintptr_t) reply; | ||
| 212 | |||
| 213 | VMW_PORT_HB_IN( | ||
| 214 | (MESSAGE_STATUS_SUCCESS << 16) | VMW_PORT_CMD_HB_MSG, | ||
| 215 | reply_len, si, di, | ||
| 216 | VMW_HYPERVISOR_HB_PORT | (channel->channel_id << 16), | ||
| 217 | VMW_HYPERVISOR_MAGIC, bp, | ||
| 218 | eax, ebx, ecx, edx, si, di); | ||
| 219 | |||
| 220 | return ebx; | ||
| 221 | } | ||
| 222 | |||
| 223 | /* HB port not available. Retrieve the message 4 bytes at a time. */ | ||
| 224 | ecx = MESSAGE_STATUS_SUCCESS << 16; | ||
| 225 | while (reply_len) { | ||
| 226 | unsigned int bytes = min_t(unsigned long, reply_len, 4); | ||
| 227 | |||
| 228 | si = channel->cookie_high; | ||
| 229 | di = channel->cookie_low; | ||
| 230 | |||
| 231 | VMW_PORT(VMW_PORT_CMD_MSG | (MSG_TYPE_RECVPAYLOAD << 16), | ||
| 232 | MESSAGE_STATUS_SUCCESS, si, di, | ||
| 233 | VMW_HYPERVISOR_PORT | (channel->channel_id << 16), | ||
| 234 | VMW_HYPERVISOR_MAGIC, | ||
| 235 | eax, ebx, ecx, edx, si, di); | ||
| 236 | |||
| 237 | if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) | ||
| 238 | break; | ||
| 239 | |||
| 240 | memcpy(reply, &ebx, bytes); | ||
| 241 | reply_len -= bytes; | ||
| 242 | reply += bytes; | ||
| 243 | } | ||
| 244 | |||
| 245 | return ecx; | ||
| 246 | } | ||
| 139 | 247 | ||
| 140 | 248 | ||
| 141 | /** | 249 | /** |
| @@ -148,11 +256,10 @@ static int vmw_close_channel(struct rpc_channel *channel) | |||
| 148 | */ | 256 | */ |
| 149 | static int vmw_send_msg(struct rpc_channel *channel, const char *msg) | 257 | static int vmw_send_msg(struct rpc_channel *channel, const char *msg) |
| 150 | { | 258 | { |
| 151 | unsigned long eax, ebx, ecx, edx, si, di, bp; | 259 | unsigned long eax, ebx, ecx, edx, si, di; |
| 152 | size_t msg_len = strlen(msg); | 260 | size_t msg_len = strlen(msg); |
| 153 | int retries = 0; | 261 | int retries = 0; |
| 154 | 262 | ||
| 155 | |||
| 156 | while (retries < RETRIES) { | 263 | while (retries < RETRIES) { |
| 157 | retries++; | 264 | retries++; |
| 158 | 265 | ||
| @@ -166,23 +273,14 @@ static int vmw_send_msg(struct rpc_channel *channel, const char *msg) | |||
| 166 | VMW_HYPERVISOR_MAGIC, | 273 | VMW_HYPERVISOR_MAGIC, |
| 167 | eax, ebx, ecx, edx, si, di); | 274 | eax, ebx, ecx, edx, si, di); |
| 168 | 275 | ||
| 169 | if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0 || | 276 | if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) { |
| 170 | (HIGH_WORD(ecx) & MESSAGE_STATUS_HB) == 0) { | 277 | /* Expected success. Give up. */ |
| 171 | /* Expected success + high-bandwidth. Give up. */ | ||
| 172 | return -EINVAL; | 278 | return -EINVAL; |
| 173 | } | 279 | } |
| 174 | 280 | ||
| 175 | /* Send msg */ | 281 | /* Send msg */ |
| 176 | si = (uintptr_t) msg; | 282 | ebx = vmw_port_hb_out(channel, msg, |
| 177 | di = channel->cookie_low; | 283 | !!(HIGH_WORD(ecx) & MESSAGE_STATUS_HB)); |
| 178 | bp = channel->cookie_high; | ||
| 179 | |||
| 180 | VMW_PORT_HB_OUT( | ||
| 181 | (MESSAGE_STATUS_SUCCESS << 16) | VMW_PORT_CMD_HB_MSG, | ||
| 182 | msg_len, si, di, | ||
| 183 | VMW_HYPERVISOR_HB_PORT | (channel->channel_id << 16), | ||
| 184 | VMW_HYPERVISOR_MAGIC, bp, | ||
| 185 | eax, ebx, ecx, edx, si, di); | ||
| 186 | 284 | ||
| 187 | if ((HIGH_WORD(ebx) & MESSAGE_STATUS_SUCCESS) != 0) { | 285 | if ((HIGH_WORD(ebx) & MESSAGE_STATUS_SUCCESS) != 0) { |
| 188 | return 0; | 286 | return 0; |
| @@ -211,7 +309,7 @@ STACK_FRAME_NON_STANDARD(vmw_send_msg); | |||
| 211 | static int vmw_recv_msg(struct rpc_channel *channel, void **msg, | 309 | static int vmw_recv_msg(struct rpc_channel *channel, void **msg, |
| 212 | size_t *msg_len) | 310 | size_t *msg_len) |
| 213 | { | 311 | { |
| 214 | unsigned long eax, ebx, ecx, edx, si, di, bp; | 312 | unsigned long eax, ebx, ecx, edx, si, di; |
| 215 | char *reply; | 313 | char *reply; |
| 216 | size_t reply_len; | 314 | size_t reply_len; |
| 217 | int retries = 0; | 315 | int retries = 0; |
| @@ -233,8 +331,7 @@ static int vmw_recv_msg(struct rpc_channel *channel, void **msg, | |||
| 233 | VMW_HYPERVISOR_MAGIC, | 331 | VMW_HYPERVISOR_MAGIC, |
| 234 | eax, ebx, ecx, edx, si, di); | 332 | eax, ebx, ecx, edx, si, di); |
| 235 | 333 | ||
| 236 | if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0 || | 334 | if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) { |
| 237 | (HIGH_WORD(ecx) & MESSAGE_STATUS_HB) == 0) { | ||
| 238 | DRM_ERROR("Failed to get reply size for host message.\n"); | 335 | DRM_ERROR("Failed to get reply size for host message.\n"); |
| 239 | return -EINVAL; | 336 | return -EINVAL; |
| 240 | } | 337 | } |
| @@ -252,17 +349,8 @@ static int vmw_recv_msg(struct rpc_channel *channel, void **msg, | |||
| 252 | 349 | ||
| 253 | 350 | ||
| 254 | /* Receive buffer */ | 351 | /* Receive buffer */ |
| 255 | si = channel->cookie_high; | 352 | ebx = vmw_port_hb_in(channel, reply, reply_len, |
| 256 | di = (uintptr_t) reply; | 353 | !!(HIGH_WORD(ecx) & MESSAGE_STATUS_HB)); |
| 257 | bp = channel->cookie_low; | ||
| 258 | |||
| 259 | VMW_PORT_HB_IN( | ||
| 260 | (MESSAGE_STATUS_SUCCESS << 16) | VMW_PORT_CMD_HB_MSG, | ||
| 261 | reply_len, si, di, | ||
| 262 | VMW_HYPERVISOR_HB_PORT | (channel->channel_id << 16), | ||
| 263 | VMW_HYPERVISOR_MAGIC, bp, | ||
| 264 | eax, ebx, ecx, edx, si, di); | ||
| 265 | |||
| 266 | if ((HIGH_WORD(ebx) & MESSAGE_STATUS_SUCCESS) == 0) { | 354 | if ((HIGH_WORD(ebx) & MESSAGE_STATUS_SUCCESS) == 0) { |
| 267 | kfree(reply); | 355 | kfree(reply); |
| 268 | 356 | ||
