summaryrefslogtreecommitdiffstats
path: root/legacy-driver-guest/rpc/gsp_rpc.c
diff options
context:
space:
mode:
Diffstat (limited to 'legacy-driver-guest/rpc/gsp_rpc.c')
-rw-r--r--legacy-driver-guest/rpc/gsp_rpc.c331
1 files changed, 331 insertions, 0 deletions
diff --git a/legacy-driver-guest/rpc/gsp_rpc.c b/legacy-driver-guest/rpc/gsp_rpc.c
new file mode 100644
index 0000000..6bdcdb2
--- /dev/null
+++ b/legacy-driver-guest/rpc/gsp_rpc.c
@@ -0,0 +1,331 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * GSP vGPU-IO Driver - RPC Client Implementation
+ */
+
+#include "gsp_core.h"
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+
+/* Ring Buffer Operations */
+static int gsp_ring_init(struct gsp_msg_ring *ring, void *base, size_t size)
+{
+ ring->base = base;
+ ring->size = size;
+ ring->head = 0;
+ ring->tail = 0;
+ spin_lock_init(&ring->lock);
+ return 0;
+}
+
+static bool gsp_ring_empty(struct gsp_msg_ring *ring)
+{
+ return ring->head == ring->tail;
+}
+
+static bool gsp_ring_full(struct gsp_msg_ring *ring)
+{
+ return ((ring->head + 1) % (ring->size / GSP_MSG_MAX_SIZE)) == ring->tail;
+}
+
+static int gsp_ring_write(struct gsp_msg_ring *ring, struct gsp_rpc_message *msg, size_t len)
+{
+ unsigned long flags;
+ uint32_t next_head;
+ void *dst;
+
+ if (len > GSP_MSG_MAX_SIZE)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ring->lock, flags);
+
+ if (gsp_ring_full(ring)) {
+ spin_unlock_irqrestore(&ring->lock, flags);
+ return -EBUSY;
+ }
+
+ dst = ring->base + (ring->head * GSP_MSG_MAX_SIZE);
+ memcpy(dst, msg, len);
+
+ next_head = (ring->head + 1) % (ring->size / GSP_MSG_MAX_SIZE);
+ ring->head = next_head;
+
+ spin_unlock_irqrestore(&ring->lock, flags);
+ return 0;
+}
+
+static int gsp_ring_read(struct gsp_msg_ring *ring, struct gsp_rpc_message *msg)
+{
+ unsigned long flags;
+ void *src;
+
+ spin_lock_irqsave(&ring->lock, flags);
+
+ if (gsp_ring_empty(ring)) {
+ spin_unlock_irqrestore(&ring->lock, flags);
+ return 0;
+ }
+
+ src = ring->base + (ring->tail * GSP_MSG_MAX_SIZE);
+ memcpy(msg, src, GSP_MSG_MAX_SIZE);
+
+ ring->tail = (ring->tail + 1) % (ring->size / GSP_MSG_MAX_SIZE);
+
+ spin_unlock_irqrestore(&ring->lock, flags);
+ return 1;
+}
+
+/* RPC Client Initialization */
+struct gsp_rpc_client *gsp_rpc_client_init(struct gsp_device *gsp)
+{
+ struct gsp_rpc_client *rpc;
+ void *ring_mem;
+
+ rpc = kzalloc(sizeof(*rpc), GFP_KERNEL);
+ if (!rpc)
+ return ERR_PTR(-ENOMEM);
+
+ rpc->gsp = gsp;
+ mutex_init(&rpc->lock);
+ atomic_set(&rpc->seq, 0);
+ init_waitqueue_head(&rpc->wq);
+
+ /* Allocate shared memory for message rings (2 pages each) */
+ ring_mem = dma_alloc_coherent(gsp->dev, 8 * PAGE_SIZE,
+ &rpc->tx_ring_dma, GFP_KERNEL);
+ if (!ring_mem) {
+ kfree(rpc);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ rpc->tx_ring = kzalloc(sizeof(*rpc->tx_ring), GFP_KERNEL);
+ rpc->rx_ring = kzalloc(sizeof(*rpc->rx_ring), GFP_KERNEL);
+ if (!rpc->tx_ring || !rpc->rx_ring) {
+ dma_free_coherent(gsp->dev, 8 * PAGE_SIZE, ring_mem, rpc->tx_ring_dma);
+ kfree(rpc->tx_ring);
+ kfree(rpc->rx_ring);
+ kfree(rpc);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ gsp_ring_init(rpc->tx_ring, ring_mem, 4 * PAGE_SIZE);
+ gsp_ring_init(rpc->rx_ring, ring_mem + 4 * PAGE_SIZE, 4 * PAGE_SIZE);
+
+ /* Map rings to GSP via queue head registers (BAR0) */
+ writel(rpc->tx_ring_dma, gsp->bar0 + GSP_BAR0_QUEUE_HEAD(0));
+ writel(rpc->tx_ring_dma >> 32, gsp->bar0 + GSP_BAR0_QUEUE_HEAD(0) + 4);
+ writel(rpc->rx_ring_dma, gsp->bar0 + GSP_BAR0_QUEUE_HEAD(1));
+ writel(rpc->rx_ring_dma >> 32, gsp->bar0 + GSP_BAR0_QUEUE_HEAD(1) + 4);
+
+ /* Allocate response buffers */
+ for (int i = 0; i < GSP_MAX_PENDING; i++) {
+ rpc->resp_buf[i] = kzalloc(GSP_MSG_MAX_SIZE, GFP_KERNEL);
+ if (!rpc->resp_buf[i]) {
+ /* Cleanup on failure */
+ for (int j = 0; j < i; j++)
+ kfree(rpc->resp_buf[j]);
+ gsp_rpc_client_fini(rpc);
+ return ERR_PTR(-ENOMEM);
+ }
+ }
+
+ dev_info(gsp->dev, "RPC client initialized\n");
+ return rpc;
+}
+
+void gsp_rpc_client_fini(struct gsp_rpc_client *rpc)
+{
+ if (!rpc)
+ return;
+
+ for (int i = 0; i < GSP_MAX_PENDING; i++)
+ kfree(rpc->resp_buf[i]);
+
+ if (rpc->tx_ring) {
+ dma_free_coherent(rpc->gsp->dev, 8 * PAGE_SIZE,
+ rpc->tx_ring->base, rpc->tx_ring_dma);
+ kfree(rpc->tx_ring);
+ }
+ kfree(rpc->rx_ring);
+ kfree(rpc);
+}
+
+/* Synchronous RPC Send */
+int gsp_rpc_send_sync(struct gsp_rpc_client *rpc,
+ enum gsp_rpc_type type,
+ void *payload, size_t payload_size,
+ void *response, size_t *response_size)
+{
+ struct gsp_rpc_message msg = {0};
+ struct completion done;
+ uint32_t seq;
+ int ret;
+
+ if (payload_size > GSP_MSG_MAX_SIZE - sizeof(struct gsp_rpc_header))
+ return -EINVAL;
+
+ seq = atomic_inc_return(&rpc->seq);
+ init_completion(&done);
+
+ msg.hdr.magic = 0x47535052; /* "GSPR" */
+ msg.hdr.version = 1;
+ msg.hdr.rpc_type = type;
+ msg.hdr.sequence = seq;
+ msg.hdr.timestamp_ns = ktime_get_ns();
+ msg.hdr.payload_size = payload_size;
+ msg.hdr.flags = GSP_MSG_SYNC;
+ msg.hdr.correlation_id = seq;
+
+ if (payload && payload_size > 0)
+ memcpy(msg.payload, payload, payload_size);
+
+ mutex_lock(&rpc->lock);
+
+ rpc->pending[seq % GSP_MAX_PENDING] = &done;
+
+ ret = gsp_ring_write(rpc->tx_ring, &msg, GSP_MSG_MAX_SIZE);
+ if (ret) {
+ rpc->pending[seq % GSP_MAX_PENDING] = NULL;
+ mutex_unlock(&rpc->lock);
+ return ret;
+ }
+
+ /* Ring doorbell - write to queue head to signal GSP */
+ writel(1, rpc->gsp->bar0 + GSP_BAR0_QUEUE_HEAD(0));
+
+ mutex_unlock(&rpc->lock);
+
+ /* Wait for completion (5 second timeout) */
+ if (!wait_for_completion_timeout(&done, msecs_to_jiffies(5000))) {
+ mutex_lock(&rpc->lock);
+ rpc->pending[seq % GSP_MAX_PENDING] = NULL;
+ mutex_unlock(&rpc->lock);
+ atomic64_inc(&rpc->gsp->rpc_errors);
+ return -ETIMEDOUT;
+ }
+
+ /* Get response */
+ struct gsp_rpc_response *resp = rpc->resp_buf[seq % GSP_MAX_PENDING];
+
+ atomic64_inc(&rpc->gsp->rpc_total);
+
+ if (resp->status != 0) {
+ atomic64_inc(&rpc->gsp->rpc_errors);
+ return gsp_errno_to_errno(resp->status);
+ }
+
+ if (response && response_size) {
+ size_t copy_size = min(*response_size, (size_t)resp->payload_size);
+ memcpy(response, resp->payload, copy_size);
+ *response_size = copy_size;
+ }
+
+ return 0;
+}
+
+/* Async RPC Send */
+int gsp_rpc_send_async(struct gsp_rpc_client *rpc,
+ enum gsp_rpc_type type,
+ void *payload, size_t payload_size,
+ void (*callback)(struct gsp_rpc_client *,
+ struct gsp_rpc_response *,
+ void *),
+ void *ctx)
+{
+ struct gsp_rpc_message msg = {0};
+ uint32_t seq;
+ int ret;
+
+ if (payload_size > GSP_MSG_MAX_SIZE - sizeof(struct gsp_rpc_header))
+ return -EINVAL;
+
+ seq = atomic_inc_return(&rpc->seq);
+
+ msg.hdr.magic = 0x47535052;
+ msg.hdr.version = 1;
+ msg.hdr.rpc_type = type;
+ msg.hdr.sequence = seq;
+ msg.hdr.timestamp_ns = ktime_get_ns();
+ msg.hdr.payload_size = payload_size;
+ msg.hdr.flags = GSP_MSG_ASYNC;
+ msg.hdr.correlation_id = (uint64_t)callback | ((uint64_t)ctx << 32);
+
+ if (payload && payload_size > 0)
+ memcpy(msg.payload, payload, payload_size);
+
+ mutex_lock(&rpc->lock);
+ ret = gsp_ring_write(rpc->tx_ring, &msg, GSP_MSG_MAX_SIZE);
+ if (!ret)
+ writel(1, rpc->gsp->bar0 + GSP_BAR0_QUEUE_HEAD(0));
+ mutex_unlock(&rpc->lock);
+
+ return ret;
+}
+
+/* ISR - Handle Completions */
+irqreturn_t gsp_isr(int irq, void *data)
+{
+ struct gsp_device *gsp = data;
+ struct gsp_rpc_message msg;
+ int handled = 0;
+
+ while (gsp_ring_read(gsp->rpc->rx_ring, &msg)) {
+ handled = 1;
+
+ if (msg.hdr.flags & GSP_MSG_SYNC) {
+ uint32_t seq = msg.hdr.sequence;
+ struct completion *done = gsp->rpc->pending[seq % GSP_MAX_PENDING];
+ struct gsp_rpc_response *resp = gsp->rpc->resp_buf[seq % GSP_MAX_PENDING];
+
+ if (done && resp) {
+ resp->status = ((struct gsp_rpc_response *)msg.payload)->status;
+ resp->payload_size = msg.hdr.payload_size;
+ memcpy(resp->payload, ((struct gsp_rpc_response *)msg.payload)->payload,
+ msg.hdr.payload_size);
+ complete(done);
+ }
+ gsp->rpc->pending[seq % GSP_MAX_PENDING] = NULL;
+ } else {
+ /* Async notification - handle via callback */
+ gsp_handle_notification(gsp, &msg);
+ }
+ }
+
+ return handled ? IRQ_HANDLED : IRQ_NONE;
+}
+
+/* Notification Handler */
+void gsp_handle_notification(struct gsp_device *gsp, struct gsp_rpc_message *msg)
+{
+ switch (msg->hdr.rpc_type) {
+ case GSP_RPC_NOTIFY:
+ /* Handle VF notifications (WPR, migration, etc.) */
+ break;
+ case GSP_RPC_BAR_TRAP:
+ atomic64_inc(&gsp->bar_traps);
+ break;
+ default:
+ dev_dbg(gsp->dev, "Unhandled notification type 0x%02x\n", msg->hdr.rpc_type);
+ }
+}
+
+/* Error Code Translation */
+int gsp_errno_to_errno(int gsp_err)
+{
+ switch (gsp_err) {
+ case GSP_OK: return 0;
+ case GSP_EPERM: return -EPERM;
+ case GSP_ENOENT: return -ENOENT;
+ case GSP_EINVAL: return -EINVAL;
+ case GSP_ENODEV: return -ENODEV;
+ case GSP_ENOMEM: return -ENOMEM;
+ case GSP_EBUSY: return -EBUSY;
+ case GSP_ENOSPC: return -ENOSPC;
+ case GSP_ETIMEDOUT: return -ETIMEDOUT;
+ case GSP_EOVERFLOW: return -EOVERFLOW;
+ case GSP_EDEV: return -EIO;
+ default: return -EIO;
+ }
+} \ No newline at end of file