summaryrefslogtreecommitdiff
path: root/lib/intel_allocator_msgchannel.c
diff options
context:
space:
mode:
authorZbigniew Kempczyński <zbigniew.kempczynski@intel.com>2020-10-23 09:30:11 +0200
committerZbigniew Kempczyński <zbigniew.kempczynski@intel.com>2021-04-13 15:44:38 +0200
commite4d8b8b01d25d507cf850c69a3da3ec08ec6c88e (patch)
tree0c85e75f3f27d0352a31ae93af28d18ead9a1aa2 /lib/intel_allocator_msgchannel.c
parent86f1cf4d0c929aaf9b2ceec6de79c36fa44a0bdf (diff)
lib/intel_allocator: Add intel_allocator core
For discrete gens we have to cease of using relocations when batch buffers are submitted to GPU. On cards which have ppgtt we can use softpin establishing addresses on our own. We added simple allocator (taken from Mesa; works on lists) and random allocator to exercise batches with different addresses. All of that works for single VM (context) so we have to add additional layer (intel_allocator) to support multiprocessing / multithreading. For main IGT process (also for threads created in it) intel_allocator resolves addresses "locally", just by mutexing access to global allocator data (ctx/vm map). When fork() is in use children cannot establish addresses on they own and have to contact to the thread spawned within main IGT process. Currently SysV IPC message queue was chosen as a communication channel between children and allocator thread. Child calls same functions as main IGT process, only communication path will be chosen instead of acquiring addresses locally. v2: Add intel_allocator_open_full() to allow user pass vm range. Add strategy: NONE, LOW_TO_HIGH, HIGH_TO_LOW passed to allocator backend. v3: Child is now able to use allocator directly as standalone. It only need to call intel_allocator_init() to reinitialize appropriate structures. v4: Add pseudo allocator - INTEL_ALLOCATOR_RELOC which just increments offsets to avoid unnecessary conditional code. v5: Alter allocator core according to igt_map changes. v6: Add internal version __intel_allocator_alloc() to return ALLOC_INVALID_ADDRESS without assertion. v7: Add libatomic for linking libigt library. It is required on some archs, like mips. Signed-off-by: Zbigniew Kempczyński <zbigniew.kempczynski@intel.com> Signed-off-by: Dominik Grzegorzek <dominik.grzegorzek@intel.com> Cc: Chris Wilson <chris@chris-wilson.co.uk> Cc: Petri Latvala <petri.latvala@intel.com> Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch> Acked-by: Petri Latvala <petri.latvala@intel.com>
Diffstat (limited to 'lib/intel_allocator_msgchannel.c')
-rw-r--r--lib/intel_allocator_msgchannel.c187
1 files changed, 187 insertions, 0 deletions
diff --git a/lib/intel_allocator_msgchannel.c b/lib/intel_allocator_msgchannel.c
new file mode 100644
index 00000000..8280bc4e
--- /dev/null
+++ b/lib/intel_allocator_msgchannel.c
@@ -0,0 +1,187 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2021 Intel Corporation
+ */
+
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/msg.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "igt.h"
+#include "intel_allocator_msgchannel.h"
+
+extern __thread pid_t child_tid;
+
+/* ----- SYSVIPC MSGQUEUE ----- */
+
+#define FTOK_IGT_ALLOCATOR_KEY "/tmp/igt.allocator.key"
+#define FTOK_IGT_ALLOCATOR_PROJID 2020
+
+#define ALLOCATOR_REQUEST 1
+
+struct msgqueue_data {
+ key_t key;
+ int queue;
+};
+
+struct msgqueue_buf {
+ long mtype;
+ union {
+ struct alloc_req request;
+ struct alloc_resp response;
+ } data;
+};
+
+static void msgqueue_init(struct msg_channel *channel)
+{
+ struct msgqueue_data *msgdata;
+ struct msqid_ds qstat;
+ key_t key;
+ int fd, queue;
+
+ igt_debug("Init msgqueue\n");
+
+ /* Create ftok key only if not exists */
+ fd = open(FTOK_IGT_ALLOCATOR_KEY, O_CREAT | O_EXCL | O_WRONLY, 0600);
+ igt_assert(fd >= 0 || errno == EEXIST);
+ if (fd >= 0)
+ close(fd);
+
+ key = ftok(FTOK_IGT_ALLOCATOR_KEY, FTOK_IGT_ALLOCATOR_PROJID);
+ igt_assert(key != -1);
+ igt_debug("Queue key: %x\n", (int) key);
+
+ queue = msgget(key, 0);
+ if (queue != -1) {
+ igt_assert(msgctl(queue, IPC_STAT, &qstat) == 0);
+ igt_debug("old messages: %lu\n", qstat.msg_qnum);
+ igt_assert(msgctl(queue, IPC_RMID, NULL) == 0);
+ }
+
+ queue = msgget(key, IPC_CREAT);
+ igt_debug("msg queue: %d\n", queue);
+
+ msgdata = calloc(1, sizeof(*msgdata));
+ igt_assert(msgdata);
+ msgdata->key = key;
+ msgdata->queue = queue;
+ channel->priv = msgdata;
+}
+
+static void msgqueue_deinit(struct msg_channel *channel)
+{
+ struct msgqueue_data *msgdata = channel->priv;
+
+ igt_debug("Deinit msgqueue\n");
+ msgctl(msgdata->queue, IPC_RMID, NULL);
+ free(channel->priv);
+}
+
+static int msgqueue_send_req(struct msg_channel *channel,
+ struct alloc_req *request)
+{
+ struct msgqueue_data *msgdata = channel->priv;
+ struct msgqueue_buf buf = {0};
+ int ret;
+
+ buf.mtype = ALLOCATOR_REQUEST;
+ buf.data.request.request_type = 1;
+ memcpy(&buf.data.request, request, sizeof(*request));
+
+retry:
+ ret = msgsnd(msgdata->queue, &buf, sizeof(buf) - sizeof(long), 0);
+ if (ret == -1 && errno == EINTR)
+ goto retry;
+
+ if (ret == -1)
+ igt_warn("Error: %s\n", strerror(errno));
+
+ return ret;
+}
+
+static int msgqueue_recv_req(struct msg_channel *channel,
+ struct alloc_req *request)
+{
+ struct msgqueue_data *msgdata = channel->priv;
+ struct msgqueue_buf buf = {0};
+ int ret, size = sizeof(buf) - sizeof(long);
+
+retry:
+ ret = msgrcv(msgdata->queue, &buf, size, ALLOCATOR_REQUEST, 0);
+ if (ret == -1 && errno == EINTR)
+ goto retry;
+
+ if (ret == size)
+ memcpy(request, &buf.data.request, sizeof(*request));
+ else if (ret == -1)
+ igt_warn("Error: %s\n", strerror(errno));
+
+ return ret;
+}
+
+static int msgqueue_send_resp(struct msg_channel *channel,
+ struct alloc_resp *response)
+{
+ struct msgqueue_data *msgdata = channel->priv;
+ struct msgqueue_buf buf = {0};
+ int ret;
+
+ buf.mtype = response->tid;
+ memcpy(&buf.data.response, response, sizeof(*response));
+
+retry:
+ ret = msgsnd(msgdata->queue, &buf, sizeof(buf) - sizeof(long), 0);
+ if (ret == -1 && errno == EINTR)
+ goto retry;
+
+ if (ret == -1)
+ igt_warn("Error: %s\n", strerror(errno));
+
+ return ret;
+}
+
+static int msgqueue_recv_resp(struct msg_channel *channel,
+ struct alloc_resp *response)
+{
+ struct msgqueue_data *msgdata = channel->priv;
+ struct msgqueue_buf buf = {0};
+ int ret, size = sizeof(buf) - sizeof(long);
+
+retry:
+ ret = msgrcv(msgdata->queue, &buf, sizeof(buf) - sizeof(long),
+ response->tid, 0);
+ if (ret == -1 && errno == EINTR)
+ goto retry;
+
+ if (ret == size)
+ memcpy(response, &buf.data.response, sizeof(*response));
+ else if (ret == -1)
+ igt_warn("Error: %s\n", strerror(errno));
+
+ return ret;
+}
+
+static struct msg_channel msgqueue_channel = {
+ .priv = NULL,
+ .init = msgqueue_init,
+ .deinit = msgqueue_deinit,
+ .send_req = msgqueue_send_req,
+ .recv_req = msgqueue_recv_req,
+ .send_resp = msgqueue_send_resp,
+ .recv_resp = msgqueue_recv_resp,
+};
+
+struct msg_channel *intel_allocator_get_msgchannel(enum msg_channel_type type)
+{
+ struct msg_channel *channel = NULL;
+
+ switch (type) {
+ case CHANNEL_SYSVIPC_MSGQUEUE:
+ channel = &msgqueue_channel;
+ }
+
+ igt_assert(channel);
+
+ return channel;
+}