The max RPC size is 16 pages (including the RPC header). To send an RPC larger than 16 pages, nvkm should split it into multiple RPCs and send it accordingly. The first of the split RPCs has the expected function number, while the rest of the split RPCs are sent with function number as NV_VGPU_MSG_FUNCTION_CONTINUATION_RECORD. GSP will consume the split RPCs from the cmdq and always write the result back to the msgq. The result is also formed as split RPCs. However, NVKM is able to send split RPC when dealing with large RPCs, but totally not aware of handling the return of the large RPCs, which are the split RPC in the msgq. Thus, it keeps dumping the unknown RPC messages from msgq, which is actually CONTINUATION_RECORD message, discard them unexpectly. Thus, the caller will not be able to consume the result from GSP. Introduce the handling of split RPCs on the msgq path. Slightly re-factor the low-level part of receiving RPCs from the msgq, RPC vehicle handling to merge the split RPCs back into a large RPC before handling it to the upper level. Thus, the upper-level of RPC APIs don't need to be heavily changed. Signed-off-by: Zhi Wang <zhiw@xxxxxxxxxx> --- .../gpu/drm/nouveau/nvkm/subdev/gsp/r535.c | 237 +++++++++++++----- 1 file changed, 177 insertions(+), 60 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/r535.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/r535.c index 50ae56013344..9c422644c9e7 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/r535.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/r535.c @@ -72,6 +72,21 @@ struct r535_gsp_msg { #define GSP_MSG_HDR_SIZE offsetof(struct r535_gsp_msg, data) +struct nvfw_gsp_rpc { + u32 header_version; + u32 signature; + u32 length; + u32 function; + u32 rpc_result; + u32 rpc_result_private; + u32 sequence; + union { + u32 spare; + u32 cpuRmGfid; + }; + u8 data[]; +}; + static int r535_rpc_status_to_errno(uint32_t rpc_status) { @@ -86,16 +101,34 @@ r535_rpc_status_to_errno(uint32_t rpc_status) } } +struct gsp_msgq_recv_args { + /* timeout in us */ + int time; + /* if set, peek the msgq, otherwise copy it */ + u32 *prepc; + /* + * the size (without message header) of message to + * wait(when peek)/copy from the msgq + */ + u32 repc; + /* the message buffer */ + u8 *msg; + /* + * skip copying the rpc header, used when handling a large RPC. + * rpc header only shows up in the first segment of a large RPC. + */ + bool skip_copy_rpc_header; +}; + static void * -r535_gsp_msgq_wait(struct nvkm_gsp *gsp, u32 repc, u32 *prepc, int *ptime) +gsp_msgq_recv(struct nvkm_gsp *gsp, struct gsp_msgq_recv_args *args) { struct r535_gsp_msg *mqe; - u32 size, rptr = *gsp->msgq.rptr; + u32 rptr = *gsp->msgq.rptr; int used; - u8 *msg; - u32 len; + u32 size, len, repc; - size = DIV_ROUND_UP(GSP_MSG_HDR_SIZE + repc, GSP_PAGE_SIZE); + size = DIV_ROUND_UP(GSP_MSG_HDR_SIZE + args->repc, GSP_PAGE_SIZE); if (WARN_ON(!size || size >= gsp->msgq.cnt)) return ERR_PTR(-EINVAL); @@ -109,46 +142,149 @@ r535_gsp_msgq_wait(struct nvkm_gsp *gsp, u32 repc, u32 *prepc, int *ptime) break; usleep_range(1, 2); - } while (--(*ptime)); + } while (--(args->time)); - if (WARN_ON(!*ptime)) + if (WARN_ON(!args->time)) return ERR_PTR(-ETIMEDOUT); mqe = (void *)((u8 *)gsp->shm.msgq.ptr + 0x1000 + rptr * 0x1000); - if (prepc) { - *prepc = (used * GSP_PAGE_SIZE) - sizeof(*mqe); + if (args->prepc) { + *args->prepc = (used * GSP_PAGE_SIZE) - sizeof(*mqe); return mqe->data; } + repc = args->repc; size = ALIGN(repc + GSP_MSG_HDR_SIZE, GSP_PAGE_SIZE); - msg = kvmalloc(repc, GFP_KERNEL); - if (!msg) - return ERR_PTR(-ENOMEM); - len = ((gsp->msgq.cnt - rptr) * GSP_PAGE_SIZE) - sizeof(*mqe); len = min_t(u32, repc, len); - memcpy(msg, mqe->data, len); + if (!args->skip_copy_rpc_header) + memcpy(args->msg, mqe->data, len); + else + memcpy(args->msg, mqe->data + sizeof(struct nvfw_gsp_rpc), + len - sizeof(struct nvfw_gsp_rpc)); repc -= len; if (repc) { mqe = (void *)((u8 *)gsp->shm.msgq.ptr + 0x1000 + 0 * 0x1000); - memcpy(msg + len, mqe, repc); + memcpy(args->msg + len, mqe, repc); } rptr = (rptr + DIV_ROUND_UP(size, GSP_PAGE_SIZE)) % gsp->msgq.cnt; mb(); (*gsp->msgq.rptr) = rptr; - return msg; + return args->msg; +} + +static void +r535_gsp_msg_dump(struct nvkm_gsp *gsp, struct nvfw_gsp_rpc *msg, int lvl) +{ + if (gsp->subdev.debug >= lvl) { + nvkm_printk__(&gsp->subdev, lvl, info, + "msg fn:%d len:0x%x/0x%zx res:0x%x resp:0x%x\n", + msg->function, msg->length, msg->length - sizeof(*msg), + msg->rpc_result, msg->rpc_result_private); + print_hex_dump(KERN_INFO, "msg: ", DUMP_PREFIX_OFFSET, 16, 1, + msg->data, msg->length - sizeof(*msg), true); + } +} + +static void * +r535_gsp_msgq_recv_continuation(struct nvkm_gsp *gsp, u32 *payload_size, + u8 *buf, int time) +{ + struct nvkm_subdev *subdev = &gsp->subdev; + struct nvfw_gsp_rpc *msg; + struct gsp_msgq_recv_args args = { 0 }; + u32 size; + + /* Peek the header of message */ + args.time = time; + args.repc = sizeof(*msg); + args.prepc = &size; + + msg = gsp_msgq_recv(gsp, &args); + if (IS_ERR_OR_NULL(msg)) + return msg; + + if (msg->function != NV_VGPU_MSG_FUNCTION_CONTINUATION_RECORD) { + nvkm_error(subdev, "Not a continuation of a large RPC\n"); + r535_gsp_msg_dump(gsp, msg, NV_DBG_ERROR); + return ERR_PTR(-EIO); + } + + *payload_size = msg->length - sizeof(*msg); + + /* Recv the continuation message */ + args.time = time; + args.repc = msg->length; + args.prepc = NULL; + args.msg = buf; + args.skip_copy_rpc_header = true; + + return gsp_msgq_recv(gsp, &args); } static void * -r535_gsp_msgq_recv(struct nvkm_gsp *gsp, u32 repc, int *ptime) +r535_gsp_msgq_recv(struct nvkm_gsp *gsp, u32 msg_repc, u32 total_repc, + int time) { - return r535_gsp_msgq_wait(gsp, repc, NULL, ptime); + struct gsp_msgq_recv_args args = { 0 }; + struct nvfw_gsp_rpc *msg; + const u32 max_msg_size = (16 * 0x1000) - sizeof(struct r535_gsp_msg); + const u32 max_rpc_size = max_msg_size - sizeof(*msg); + u32 repc = total_repc; + u8 *buf, *next; + + if (WARN_ON(msg_repc > max_msg_size)) + return NULL; + + buf = kvmalloc(max_t(u32, msg_repc, total_repc + sizeof(*msg)), GFP_KERNEL); + if (!buf) + return ERR_PTR(-ENOMEM); + + /* Recv the message */ + args.time = time; + args.repc = msg_repc; + args.prepc = NULL; + args.msg = buf; + args.skip_copy_rpc_header = false; + + msg = gsp_msgq_recv(gsp, &args); + if (IS_ERR_OR_NULL(msg)) { + kfree(buf); + return msg; + } + + if (total_repc <= max_rpc_size) + return buf; + + /* Gather the message from the following continuation messages. */ + next = buf; + + next += msg_repc; + repc -= msg_repc - sizeof(*msg); + + while (repc) { + struct nvfw_gsp_rpc *cont_msg; + u32 size; + + cont_msg = r535_gsp_msgq_recv_continuation(gsp, &size, next, + time); + if (IS_ERR_OR_NULL(cont_msg)) { + kfree(buf); + return cont_msg; + } + repc -= size; + next += size; + } + + /* Patch the message length. The caller sees a consolidated message */ + msg->length = total_repc + sizeof(*msg); + return buf; } static int @@ -234,54 +370,33 @@ r535_gsp_cmdq_get(struct nvkm_gsp *gsp, u32 argc) return cmd->data; } -struct nvfw_gsp_rpc { - u32 header_version; - u32 signature; - u32 length; - u32 function; - u32 rpc_result; - u32 rpc_result_private; - u32 sequence; - union { - u32 spare; - u32 cpuRmGfid; - }; - u8 data[]; -}; - static void r535_gsp_msg_done(struct nvkm_gsp *gsp, struct nvfw_gsp_rpc *msg) { kvfree(msg); } -static void -r535_gsp_msg_dump(struct nvkm_gsp *gsp, struct nvfw_gsp_rpc *msg, int lvl) -{ - if (gsp->subdev.debug >= lvl) { - nvkm_printk__(&gsp->subdev, lvl, info, - "msg fn:%d len:0x%x/0x%zx res:0x%x resp:0x%x\n", - msg->function, msg->length, msg->length - sizeof(*msg), - msg->rpc_result, msg->rpc_result_private); - print_hex_dump(KERN_INFO, "msg: ", DUMP_PREFIX_OFFSET, 16, 1, - msg->data, msg->length - sizeof(*msg), true); - } -} - static struct nvfw_gsp_rpc * r535_gsp_msg_recv(struct nvkm_gsp *gsp, int fn, u32 repc) { struct nvkm_subdev *subdev = &gsp->subdev; + struct gsp_msgq_recv_args args = { 0 }; struct nvfw_gsp_rpc *msg; int time = 4000000, i; u32 size; retry: - msg = r535_gsp_msgq_wait(gsp, sizeof(*msg), &size, &time); + /* Peek the header of message */ + args.time = time; + args.repc = sizeof(*msg); + args.prepc = &size; + + msg = gsp_msgq_recv(gsp, &args); if (IS_ERR_OR_NULL(msg)) return msg; - msg = r535_gsp_msgq_recv(gsp, msg->length, &time); + /* Recv the message */ + msg = r535_gsp_msgq_recv(gsp, msg->length, repc, time); if (IS_ERR_OR_NULL(msg)) return msg; @@ -734,6 +849,7 @@ r535_gsp_rpc_push(struct nvkm_gsp *gsp, void *argv, bool wait, u32 repc) mutex_lock(&gsp->cmdq.mutex); if (rpc_size > max_rpc_size) { const u32 fn = rpc->function; + u32 remain_rpc_size = rpc_size; /* Adjust length, and send initial RPC. */ rpc->length = sizeof(*rpc) + max_rpc_size; @@ -744,11 +860,11 @@ r535_gsp_rpc_push(struct nvkm_gsp *gsp, void *argv, bool wait, u32 repc) goto done; argv += max_rpc_size; - rpc_size -= max_rpc_size; + remain_rpc_size -= max_rpc_size; /* Remaining chunks sent as CONTINUATION_RECORD RPCs. */ - while (rpc_size) { - u32 size = min(rpc_size, max_rpc_size); + while (remain_rpc_size) { + u32 size = min(remain_rpc_size, max_rpc_size); void *next; next = r535_gsp_rpc_get(gsp, NV_VGPU_MSG_FUNCTION_CONTINUATION_RECORD, size); @@ -764,19 +880,20 @@ r535_gsp_rpc_push(struct nvkm_gsp *gsp, void *argv, bool wait, u32 repc) goto done; argv += size; - rpc_size -= size; + remain_rpc_size -= size; } /* Wait for reply. */ - if (wait) { - rpc = r535_gsp_msg_recv(gsp, fn, repc); - if (!IS_ERR_OR_NULL(rpc)) + rpc = r535_gsp_msg_recv(gsp, fn, rpc_size); + if (!IS_ERR_OR_NULL(rpc)) { + if (wait) repv = rpc->data; - else - repv = rpc; - } else { - repv = NULL; - } + else { + nvkm_gsp_rpc_done(gsp, rpc); + repv = NULL; + } + } else + repv = wait ? rpc : NULL; } else { repv = r535_gsp_rpc_send(gsp, argv, wait, repc); } -- 2.34.1