/*
* Copyright 2023 Red Hat Inc .
*
* Permission is hereby granted , free of charge , to any person obtaining a
* copy of this software and associated documentation files ( the " Software " ) ,
* to deal in the Software without restriction , including without limitation
* the rights to use , copy , modify , merge , publish , distribute , sublicense ,
* and / or sell copies of the Software , and to permit persons to whom the
* Software is furnished to do so , subject to the following conditions :
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL
* THE COPYRIGHT HOLDER ( S ) OR AUTHOR ( S ) BE LIABLE FOR ANY CLAIM , DAMAGES OR
* OTHER LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE ,
* ARISING FROM , OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE .
*/
#include <rm/rpc.h>
#include "nvrm/rpcfn.h"
#define GSP_MSG_MIN_SIZE GSP_PAGE_SIZE
#define GSP_MSG_MAX_SIZE (GSP_MSG_MIN_SIZE * 16 )
/**
* DOC : GSP message queue element
*
* https : //github.com/NVIDIA/open-gpu-kernel-modules/blob/535/src/nvidia/inc/kernel/gpu/gsp/message_queue_priv.h
*
* The GSP command queue and status queue are message queues for the
* communication between software and GSP . The software submits the GSP
* RPC via the GSP command queue , GSP writes the status of the submitted
* RPC in the status queue .
*
* A GSP message queue element consists of three parts :
*
* - message element header ( struct r535_gsp_msg ) , which mostly maintains
* the metadata for queuing the element .
*
* - RPC message header ( struct nvfw_gsp_rpc ) , which maintains the info
* of the RPC . E . g . , the RPC function number .
*
* - The payload , where the RPC message stays . E . g . the params of a
* specific RPC function . Some RPC functions also have their headers
* in the payload . E . g . rm_alloc , rm_control .
*
* The memory layout of a GSP message element can be illustrated below : :
*
* + - - - - - - - - - - - - - - - - - - - - - - - - +
* | Message Element Header |
* | ( r535_gsp_msg ) |
* | |
* | ( r535_gsp_msg . data ) |
* | | |
* | - - - - - - - - - - V - - - - - - - - - - - - - |
* | GSP RPC Header |
* | ( nvfw_gsp_rpc ) |
* | |
* | ( nvfw_gsp_rpc . data ) |
* | | |
* | - - - - - - - - - - V - - - - - - - - - - - - - |
* | Payload |
* | |
* | header ( optional ) |
* | params |
* + - - - - - - - - - - - - - - - - - - - - - - - - +
*
* The max size of a message queue element is 16 pages ( including the
* headers ) . When a GSP message to be sent is larger than 16 pages , the
* message should be split into multiple elements and sent accordingly .
*
* In the bunch of the split elements , the first element has the expected
* function number , while the rest of the elements are sent with the
* function number NV_VGPU_MSG_FUNCTION_CONTINUATION_RECORD .
*
* GSP consumes the elements from the cmdq and always writes the result
* back to the msgq . The result is also formed as split elements .
*
* Terminology :
*
* - gsp_msg ( msg ) : GSP message element ( element header + GSP RPC header +
* payload )
* - gsp_rpc ( rpc ) : GSP RPC ( RPC header + payload )
* - gsp_rpc_buf : buffer for ( GSP RPC header + payload )
* - gsp_rpc_len : size of ( GSP RPC header + payload )
* - params_size : size of params in the payload
* - payload_size : size of ( header if exists + params ) in the payload
*/
struct r535_gsp_msg {
u8 auth_tag_buffer[16 ];
u8 aad_buffer[16 ];
u32 checksum;
u32 sequence;
u32 elem_count;
u32 pad;
u8 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[];
};
#define GSP_MSG_HDR_SIZE offsetof(struct r535_gsp_msg, data)
#define to_gsp_hdr(p, header) \
container_of((void *)p, typeof(*header), data)
#define to_payload_hdr(p, header) \
container_of((void *)p, typeof(*header), params)
int
r535_rpc_status_to_errno(uint32_t rpc_status)
{
switch (rpc_status) {
case 0 x55: /* NV_ERR_NOT_READY */
case 0 x66: /* NV_ERR_TIMEOUT_RETRY */
return -EBUSY;
case 0 x51: /* NV_ERR_NO_MEMORY */
return -ENOMEM;
default :
return -EINVAL;
}
}
static int
r535_gsp_msgq_wait(struct nvkm_gsp *gsp, u32 gsp_rpc_len, int *ptime)
{
u32 size, rptr = *gsp->msgq.rptr;
int used;
size = DIV_ROUND_UP(GSP_MSG_HDR_SIZE + gsp_rpc_len,
GSP_PAGE_SIZE);
if (WARN_ON(!size || size >= gsp->msgq.cnt))
return -EINVAL;
do {
u32 wptr = *gsp->msgq.wptr;
used = wptr + gsp->msgq.cnt - rptr;
if (used >= gsp->msgq.cnt)
used -= gsp->msgq.cnt;
if (used >= size)
break ;
usleep_range(1 , 2 );
} while (--(*ptime));
if (WARN_ON(!*ptime))
return -ETIMEDOUT;
return used;
}
static struct r535_gsp_msg *
r535_gsp_msgq_get_entry(struct nvkm_gsp *gsp)
{
u32 rptr = *gsp->msgq.rptr;
/* Skip the first page, which is the message queue info */
return (void *)((u8 *)gsp->shm.msgq.ptr + GSP_PAGE_SIZE +
rptr * GSP_PAGE_SIZE);
}
/**
* DOC : Receive a GSP message queue element
*
* Receiving a GSP message queue element from the message queue consists of
* the following steps :
*
* - Peek the element from the queue : r535_gsp_msgq_peek ( ) .
* Peek the first page of the element to determine the total size of the
* message before allocating the proper memory .
*
* - Allocate memory for the message .
* Once the total size of the message is determined from the GSP message
* queue element , the caller of r535_gsp_msgq_recv ( ) allocates the
* required memory .
*
* - Receive the message : r535_gsp_msgq_recv ( ) .
* Copy the message into the allocated memory . Advance the read pointer .
* If the message is a large GSP message , r535_gsp_msgq_recv ( ) calls
* r535_gsp_msgq_recv_one_elem ( ) repeatedly to receive continuation parts
* until the complete message is received .
* r535_gsp_msgq_recv ( ) assembles the payloads of cotinuation parts into
* the return of the large GSP message .
*
* - Free the allocated memory : r535_gsp_msg_done ( ) .
* The user is responsible for freeing the memory allocated for the GSP
* message pages after they have been processed .
*/
static void *
r535_gsp_msgq_peek(struct nvkm_gsp *gsp, u32 gsp_rpc_len, int *retries)
{
struct r535_gsp_msg *mqe;
int ret;
ret = r535_gsp_msgq_wait(gsp, gsp_rpc_len, retries);
if (ret < 0 )
return ERR_PTR(ret);
mqe = r535_gsp_msgq_get_entry(gsp);
return mqe->data;
}
struct r535_gsp_msg_info {
int *retries;
u32 gsp_rpc_len;
void *gsp_rpc_buf;
bool continuation;
};
static void
r535_gsp_msg_dump(struct nvkm_gsp *gsp, struct nvfw_gsp_rpc *msg, int lvl);
static void *
r535_gsp_msgq_recv_one_elem(struct nvkm_gsp *gsp,
struct r535_gsp_msg_info *info)
{
u8 *buf = info->gsp_rpc_buf;
u32 rptr = *gsp->msgq.rptr;
struct r535_gsp_msg *mqe;
u32 size, expected, len;
int ret;
expected = info->gsp_rpc_len;
ret = r535_gsp_msgq_wait(gsp, expected, info->retries);
if (ret < 0 )
return ERR_PTR(ret);
mqe = r535_gsp_msgq_get_entry(gsp);
if (info->continuation) {
struct nvfw_gsp_rpc *rpc = (struct nvfw_gsp_rpc *)mqe->data;
if (rpc->function != NV_VGPU_MSG_FUNCTION_CONTINUATION_RECORD) {
nvkm_error(&gsp->subdev,
"Not a continuation of a large RPC\n" );
r535_gsp_msg_dump(gsp, rpc, NV_DBG_ERROR);
return ERR_PTR(-EIO);
}
}
size = ALIGN(expected + GSP_MSG_HDR_SIZE, GSP_PAGE_SIZE);
len = ((gsp->msgq.cnt - rptr) * GSP_PAGE_SIZE) - sizeof (*mqe);
len = min_t(u32, expected, len);
if (info->continuation)
memcpy(buf, mqe->data + sizeof (struct nvfw_gsp_rpc),
len - sizeof (struct nvfw_gsp_rpc));
else
memcpy(buf, mqe->data, len);
expected -= len;
if (expected) {
mqe = (void *)((u8 *)gsp->shm.msgq.ptr + 0 x1000 + 0 * 0 x1000);
memcpy(buf + len, mqe, expected);
}
rptr = (rptr + DIV_ROUND_UP(size, GSP_PAGE_SIZE)) % gsp->msgq.cnt;
mb();
(*gsp->msgq.rptr) = rptr;
return buf;
}
static void *
r535_gsp_msgq_recv(struct nvkm_gsp *gsp, u32 gsp_rpc_len, int *retries)
{
struct r535_gsp_msg *mqe;
const u32 max_rpc_size = GSP_MSG_MAX_SIZE - sizeof (*mqe);
struct nvfw_gsp_rpc *rpc;
struct r535_gsp_msg_info info = {0 };
u32 expected = gsp_rpc_len;
void *buf;
mqe = r535_gsp_msgq_get_entry(gsp);
rpc = (struct nvfw_gsp_rpc *)mqe->data;
if (WARN_ON(rpc->length > max_rpc_size))
return NULL;
buf = kvmalloc(max_t(u32, rpc->length, expected), GFP_KERNEL);
if (!buf)
return ERR_PTR(-ENOMEM);
info.gsp_rpc_buf = buf;
info.retries = retries;
info.gsp_rpc_len = rpc->length;
buf = r535_gsp_msgq_recv_one_elem(gsp, &info);
if (IS_ERR(buf)) {
kvfree(info.gsp_rpc_buf);
info.gsp_rpc_buf = NULL;
return buf;
}
if (expected <= max_rpc_size)
return buf;
info.gsp_rpc_buf += info.gsp_rpc_len;
expected -= info.gsp_rpc_len;
while (expected) {
u32 size;
rpc = r535_gsp_msgq_peek(gsp, sizeof (*rpc), info.retries);
if (IS_ERR_OR_NULL(rpc)) {
kvfree(buf);
return rpc;
}
info.gsp_rpc_len = rpc->length;
info.continuation = true ;
rpc = r535_gsp_msgq_recv_one_elem(gsp, &info);
if (IS_ERR_OR_NULL(rpc)) {
kvfree(buf);
return rpc;
}
size = info.gsp_rpc_len - sizeof (*rpc);
expected -= size;
info.gsp_rpc_buf += size;
}
rpc = buf;
rpc->length = gsp_rpc_len;
return buf;
}
static int
r535_gsp_cmdq_push(struct nvkm_gsp *gsp, void *rpc)
{
struct r535_gsp_msg *msg = to_gsp_hdr(rpc, msg);
struct r535_gsp_msg *cqe;
u32 gsp_rpc_len = msg->checksum;
u64 *ptr = (void *)msg;
u64 *end;
u64 csum = 0 ;
int free, time = 1000000 ;
u32 wptr, size, step, len;
u32 off = 0 ;
len = ALIGN(GSP_MSG_HDR_SIZE + gsp_rpc_len, GSP_PAGE_SIZE);
end = (u64 *)((char *)ptr + len);
msg->pad = 0 ;
msg->checksum = 0 ;
msg->sequence = gsp->cmdq.seq++;
msg->elem_count = DIV_ROUND_UP(len, 0 x1000);
while (ptr < end)
csum ^= *ptr++;
msg->checksum = upper_32_bits(csum) ^ lower_32_bits(csum);
wptr = *gsp->cmdq.wptr;
do {
do {
free = *gsp->cmdq.rptr + gsp->cmdq.cnt - wptr - 1 ;
if (free >= gsp->cmdq.cnt)
free -= gsp->cmdq.cnt;
if (free >= 1 )
break ;
usleep_range(1 , 2 );
} while (--time);
if (WARN_ON(!time)) {
kvfree(msg);
return -ETIMEDOUT;
}
cqe = (void *)((u8 *)gsp->shm.cmdq.ptr + 0 x1000 + wptr * 0 x1000);
step = min_t(u32, free, (gsp->cmdq.cnt - wptr));
size = min_t(u32, len, step * GSP_PAGE_SIZE);
memcpy(cqe, (u8 *)msg + off, size);
wptr += DIV_ROUND_UP(size, 0 x1000);
if (wptr == gsp->cmdq.cnt)
wptr = 0 ;
off += size;
len -= size;
} while (len);
nvkm_trace(&gsp->subdev, "cmdq: wptr %d\n" , wptr);
wmb();
(*gsp->cmdq.wptr) = wptr;
mb();
nvkm_falcon_wr32(&gsp->falcon, 0 xc00, 0 x00000000);
kvfree(msg);
return 0 ;
}
static void *
r535_gsp_cmdq_get(struct nvkm_gsp *gsp, u32 gsp_rpc_len)
{
struct r535_gsp_msg *msg;
u32 size = GSP_MSG_HDR_SIZE + gsp_rpc_len;
size = ALIGN(size, GSP_MSG_MIN_SIZE);
msg = kvzalloc(size, GFP_KERNEL);
if (!msg)
return ERR_PTR(-ENOMEM);
msg->checksum = gsp_rpc_len;
return msg->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 );
}
}
struct nvfw_gsp_rpc *
r535_gsp_msg_recv(struct nvkm_gsp *gsp, int fn, u32 gsp_rpc_len)
{
struct nvkm_subdev *subdev = &gsp->subdev;
struct nvfw_gsp_rpc *rpc;
int retries = 4000000 , i;
retry:
rpc = r535_gsp_msgq_peek(gsp, sizeof (*rpc), &retries);
if (IS_ERR_OR_NULL(rpc))
return rpc;
rpc = r535_gsp_msgq_recv(gsp, gsp_rpc_len, &retries);
if (IS_ERR_OR_NULL(rpc))
return rpc;
if (rpc->rpc_result) {
r535_gsp_msg_dump(gsp, rpc, NV_DBG_ERROR);
r535_gsp_msg_done(gsp, rpc);
return ERR_PTR(-EINVAL);
}
r535_gsp_msg_dump(gsp, rpc, NV_DBG_TRACE);
if (fn && rpc->function == fn) {
if (gsp_rpc_len) {
if (rpc->length < gsp_rpc_len) {
nvkm_error(subdev, "rpc len %d < %d\n" ,
rpc->length, gsp_rpc_len);
r535_gsp_msg_dump(gsp, rpc, NV_DBG_ERROR);
r535_gsp_msg_done(gsp, rpc);
return ERR_PTR(-EIO);
}
return rpc;
}
r535_gsp_msg_done(gsp, rpc);
return NULL;
}
for (i = 0 ; i < gsp->msgq.ntfy_nr; i++) {
struct nvkm_gsp_msgq_ntfy *ntfy = &gsp->msgq.ntfy[i];
if (ntfy->fn == rpc->function) {
if (ntfy->func)
ntfy->func(ntfy->priv, ntfy->fn, rpc->data,
rpc->length - sizeof (*rpc));
break ;
}
}
if (i == gsp->msgq.ntfy_nr)
r535_gsp_msg_dump(gsp, rpc, NV_DBG_WARN);
r535_gsp_msg_done(gsp, rpc);
if (fn)
goto retry;
if (*gsp->msgq.rptr != *gsp->msgq.wptr)
goto retry;
return NULL;
}
int
r535_gsp_msg_ntfy_add(struct nvkm_gsp *gsp, u32 fn, nvkm_gsp_msg_ntfy_func func, void *priv)
{
int ret = 0 ;
mutex_lock(&gsp->msgq.mutex);
if (WARN_ON(gsp->msgq.ntfy_nr >= ARRAY_SIZE(gsp->msgq.ntfy))) {
ret = -ENOSPC;
} else {
gsp->msgq.ntfy[gsp->msgq.ntfy_nr].fn = fn;
gsp->msgq.ntfy[gsp->msgq.ntfy_nr].func = func;
gsp->msgq.ntfy[gsp->msgq.ntfy_nr].priv = priv;
gsp->msgq.ntfy_nr++;
}
mutex_unlock(&gsp->msgq.mutex);
return ret;
}
int
r535_gsp_rpc_poll(struct nvkm_gsp *gsp, u32 fn)
{
void *repv;
mutex_lock(&gsp->cmdq.mutex);
repv = r535_gsp_msg_recv(gsp, fn, 0 );
mutex_unlock(&gsp->cmdq.mutex);
if (IS_ERR(repv))
return PTR_ERR(repv);
return 0 ;
}
static void *
r535_gsp_rpc_handle_reply(struct nvkm_gsp *gsp, u32 fn,
enum nvkm_gsp_rpc_reply_policy policy,
u32 gsp_rpc_len)
{
struct nvfw_gsp_rpc *reply;
void *repv = NULL;
switch (policy) {
case NVKM_GSP_RPC_REPLY_NOWAIT:
break ;
case NVKM_GSP_RPC_REPLY_RECV:
reply = r535_gsp_msg_recv(gsp, fn, gsp_rpc_len);
if (!IS_ERR_OR_NULL(reply))
repv = reply->data;
else
repv = reply;
break ;
case NVKM_GSP_RPC_REPLY_POLL:
repv = r535_gsp_msg_recv(gsp, fn, 0 );
break ;
}
return repv;
}
static void *
r535_gsp_rpc_send(struct nvkm_gsp *gsp, void *payload,
enum nvkm_gsp_rpc_reply_policy policy, u32 gsp_rpc_len)
{
struct nvfw_gsp_rpc *rpc = to_gsp_hdr(payload, rpc);
u32 fn = rpc->function;
int ret;
if (gsp->subdev.debug >= NV_DBG_TRACE) {
nvkm_trace(&gsp->subdev, "rpc fn:%d len:0x%x/0x%zx\n" , rpc->function,
rpc->length, rpc->length - sizeof (*rpc));
print_hex_dump(KERN_INFO, "rpc: " , DUMP_PREFIX_OFFSET, 16 , 1 ,
rpc->data, rpc->length - sizeof (*rpc), true );
}
ret = r535_gsp_cmdq_push(gsp, rpc);
if (ret)
return ERR_PTR(ret);
return r535_gsp_rpc_handle_reply(gsp, fn, policy, gsp_rpc_len);
}
static void
r535_gsp_rpc_done(struct nvkm_gsp *gsp, void *repv)
{
struct nvfw_gsp_rpc *rpc = container_of(repv, typeof(*rpc), data);
r535_gsp_msg_done(gsp, rpc);
}
static void *
r535_gsp_rpc_get(struct nvkm_gsp *gsp, u32 fn, u32 payload_size)
{
struct nvfw_gsp_rpc *rpc;
rpc = r535_gsp_cmdq_get(gsp, ALIGN(sizeof (*rpc) + payload_size,
sizeof (u64)));
if (IS_ERR(rpc))
return ERR_CAST(rpc);
rpc->header_version = 0 x03000000;
rpc->signature = ('C' << 24 ) | ('P' << 16 ) | ('R' << 8 ) | 'V' ;
rpc->function = fn;
rpc->rpc_result = 0 xffffffff;
rpc->rpc_result_private = 0 xffffffff;
rpc->length = sizeof (*rpc) + payload_size;
return rpc->data;
}
static void *
r535_gsp_rpc_push(struct nvkm_gsp *gsp, void *payload,
enum nvkm_gsp_rpc_reply_policy policy, u32 gsp_rpc_len)
{
struct nvfw_gsp_rpc *rpc = to_gsp_hdr(payload, rpc);
struct r535_gsp_msg *msg = to_gsp_hdr(rpc, msg);
const u32 max_rpc_size = GSP_MSG_MAX_SIZE - sizeof (*msg);
const u32 max_payload_size = max_rpc_size - sizeof (*rpc);
u32 payload_size = rpc->length - sizeof (*rpc);
void *repv;
mutex_lock(&gsp->cmdq.mutex);
if (payload_size > max_payload_size) {
const u32 fn = rpc->function;
u32 remain_payload_size = payload_size;
void *next;
/* Send initial RPC. */
next = r535_gsp_rpc_get(gsp, fn, max_payload_size);
if (IS_ERR(next)) {
repv = next;
goto done;
}
memcpy(next, payload, max_payload_size);
repv = r535_gsp_rpc_send(gsp, next, NVKM_GSP_RPC_REPLY_NOWAIT, 0 );
if (IS_ERR(repv))
goto done;
payload += max_payload_size;
remain_payload_size -= max_payload_size;
/* Remaining chunks sent as CONTINUATION_RECORD RPCs. */
while (remain_payload_size) {
u32 size = min(remain_payload_size,
max_payload_size);
next = r535_gsp_rpc_get(gsp, NV_VGPU_MSG_FUNCTION_CONTINUATION_RECORD, size);
if (IS_ERR(next)) {
repv = next;
goto done;
}
memcpy(next, payload, size);
repv = r535_gsp_rpc_send(gsp, next, NVKM_GSP_RPC_REPLY_NOWAIT, 0 );
if (IS_ERR(repv))
goto done;
payload += size;
remain_payload_size -= size;
}
/* Wait for reply. */
repv = r535_gsp_rpc_handle_reply(gsp, fn, policy, payload_size +
sizeof (*rpc));
if (!IS_ERR(repv))
kvfree(msg);
} else {
repv = r535_gsp_rpc_send(gsp, payload, policy, gsp_rpc_len);
}
done:
mutex_unlock(&gsp->cmdq.mutex);
return repv;
}
const struct nvkm_rm_api_rpc
r535_rpc = {
.get = r535_gsp_rpc_get,
.push = r535_gsp_rpc_push,
.done = r535_gsp_rpc_done,
};
Messung V0.5 in Prozent C=99 H=88 G=93
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.19Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-10-10)
¤
*Eine klare Vorstellung vom Zielzustand