[igt-dev] [PATCH i-g-t 06/24] lib/intel_allocator: Add intel_allocator core

Petri Latvala petri.latvala at intel.com
Thu Oct 22 10:09:56 UTC 2020


On Thu, Oct 22, 2020 at 11:58:49AM +0200, Zbigniew Kempczyński wrote:
> Signed-off-by: Zbigniew Kempczyński <zbigniew.kempczynski at intel.com>
> Signed-off-by: Dominik Grzegorzek <dominik.grzegorzek at intel.com>
> Cc: Chris Wilson <chris at chris-wilson.co.uk>
> Cc: Petri Latvala <petri.latvala at intel.com>
> ---
>  .../igt-gpu-tools/igt-gpu-tools-docs.xml      |   1 +
>  lib/igt_core.c                                |   2 +
>  lib/intel_allocator.c                         | 939 ++++++++++++++++++
>  lib/intel_allocator.h                         | 136 +++
>  lib/intel_allocator_msgchannel.c              | 182 ++++
>  lib/intel_allocator_msgchannel.h              | 140 +++
>  lib/intel_allocator_simple.c                  |   2 -
>  lib/meson.build                               |   4 +
>  8 files changed, 1404 insertions(+), 2 deletions(-)
>  create mode 100644 lib/intel_allocator.c
>  create mode 100644 lib/intel_allocator.h
>  create mode 100644 lib/intel_allocator_msgchannel.c
>  create mode 100644 lib/intel_allocator_msgchannel.h
> 
> diff --git a/docs/reference/igt-gpu-tools/igt-gpu-tools-docs.xml b/docs/reference/igt-gpu-tools/igt-gpu-tools-docs.xml
> index bf5ac542..192d1df7 100644
> --- a/docs/reference/igt-gpu-tools/igt-gpu-tools-docs.xml
> +++ b/docs/reference/igt-gpu-tools/igt-gpu-tools-docs.xml
> @@ -43,6 +43,7 @@
>      <xi:include href="xml/igt_vc4.xml"/>
>      <xi:include href="xml/igt_vgem.xml"/>
>      <xi:include href="xml/igt_x86.xml"/>
> +    <xi:include href="xml/intel_allocator.xml"/>
>      <xi:include href="xml/intel_batchbuffer.xml"/>
>      <xi:include href="xml/intel_bufops.xml"/>
>      <xi:include href="xml/intel_chipset.xml"/>
> diff --git a/lib/igt_core.c b/lib/igt_core.c
> index 1f725d00..86653abc 100644
> --- a/lib/igt_core.c
> +++ b/lib/igt_core.c
> @@ -1414,6 +1414,8 @@ static void exit_subtest(const char *result)
>  	}
>  	num_test_children = 0;
>  
> +	intel_allocator_init();
> +
>  	if (!in_dynamic_subtest)
>  		_igt_dynamic_tests_executed = -1;
>  

Just drive-by commenting for others that might be as confused about
this as I was: intel_allocator_init call is only added to exit_subtest
because the first call to it is already done in an igt_constructor
block.

-- 
Petri Latvala




> diff --git a/lib/intel_allocator.c b/lib/intel_allocator.c
> new file mode 100644
> index 00000000..b0ae1d8b
> --- /dev/null
> +++ b/lib/intel_allocator.c
> @@ -0,0 +1,939 @@
> +/*
> + * Copyright © 2020 Intel Corporation
> + *
> + * 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 (including the next
> + * paragraph) 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 AUTHORS OR COPYRIGHT HOLDERS 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 <sys/types.h>
> +#include <sys/stat.h>
> +#include <sys/ipc.h>
> +#include <sys/msg.h>
> +#include <fcntl.h>
> +#include <pthread.h>
> +#include <signal.h>
> +#include <stdlib.h>
> +#include <unistd.h>
> +#include "igt.h"
> +#include "igt_map.h"
> +#include "intel_allocator.h"
> +#include "intel_allocator_msgchannel.h"
> +
> +//#define ALLOCDBG
> +#ifdef ALLOCDBG
> +#define alloc_info igt_info
> +#define alloc_debug igt_debug
> +static const char *reqtype_str[] = {
> +	[REQ_STOP]		= "stop",
> +	[REQ_OPEN]		= "open",
> +	[REQ_CLOSE]		= "close",
> +	[REQ_REMOVE_HANDLE]	= "remove handle",
> +	[REQ_ADDRESS_RANGE]	= "address range",
> +	[REQ_ALLOC]		= "alloc",
> +	[REQ_FREE]		= "free",
> +	[REQ_IS_ALLOCATED]	= "is allocated",
> +	[REQ_RESERVE]		= "reserve",
> +	[REQ_UNRESERVE]		= "unreserve",
> +	[REQ_RESERVE_IF_NOT_ALLOCATED] = "reserve-ina",
> +	[REQ_IS_RESERVED]	= "is reserved",
> +};
> +static inline const char *reqstr(enum reqtype request_type)
> +{
> +	igt_assert(request_type >= REQ_STOP && request_type <= REQ_IS_RESERVED);
> +	return reqtype_str[request_type];
> +}
> +#else
> +#define alloc_info(...) {}
> +#define alloc_debug(...) {}
> +#endif
> +
> +struct intel_allocator *intel_allocator_random_create(int fd, uint32_t ctx);
> +struct intel_allocator *intel_allocator_simple_create(int fd, uint32_t ctx);
> +
> +static struct igt_map *allocators_map;
> +static pthread_mutex_t map_mutex = PTHREAD_MUTEX_INITIALIZER;
> +static bool multiprocess;
> +static pthread_t allocator_thread;
> +
> +static bool warn_if_not_empty;
> +
> +/* For allocator purposes we need to track pid/tid */
> +static pid_t allocator_pid = -1;
> +extern pid_t child_pid;
> +extern __thread pid_t child_tid;
> +
> +static struct msg_channel *channel;
> +
> +static int send_alloc_stop(struct msg_channel *msgchan)
> +{
> +	struct alloc_req req = {0};
> +
> +	req.request_type = REQ_STOP;
> +
> +	return msgchan->send_req(msgchan, &req);
> +}
> +
> +static int send_req(struct msg_channel *msgchan, pid_t tid,
> +		    struct alloc_req *request)
> +{
> +	request->tid = tid;
> +	return msgchan->send_req(msgchan, request);
> +}
> +
> +static int recv_req(struct msg_channel *msgchan, struct alloc_req *request)
> +{
> +	return msgchan->recv_req(msgchan, request);
> +}
> +
> +static int send_resp(struct msg_channel *msgchan,
> +		     pid_t tid, struct alloc_resp *response)
> +{
> +	response->tid = tid;
> +	return msgchan->send_resp(msgchan, response);
> +}
> +
> +static int recv_resp(struct msg_channel *msgchan,
> +		     pid_t tid, struct alloc_resp *response)
> +{
> +	response->tid = tid;
> +	return msgchan->recv_resp(msgchan, response);
> +}
> +
> +static struct intel_allocator *intel_allocator_create(int fd, uint32_t ctx,
> +						      uint8_t allocator_type)
> +{
> +	struct intel_allocator *ial;
> +
> +	switch (allocator_type) {
> +	case INTEL_ALLOCATOR_NONE:
> +		igt_assert_f(allocator_type != INTEL_ALLOCATOR_NONE,
> +			     "Bug, trying to use allocator with relocations");
> +		break;
> +	case INTEL_ALLOCATOR_RANDOM:
> +		ial = intel_allocator_random_create(fd, ctx);
> +		break;
> +	case INTEL_ALLOCATOR_SIMPLE:
> +		ial = intel_allocator_simple_create(fd, ctx);
> +		break;
> +	default:
> +		igt_assert_f(ial, "Allocator type %d not implemented\n",
> +			     allocator_type);
> +		break;
> +	}
> +
> +	ial->type = allocator_type;
> +	atomic_fetch_add(&ial->refcount, 1);
> +	pthread_mutex_init(&ial->mutex, NULL);
> +
> +	igt_map_add(allocators_map, ial, ial);
> +
> +	return ial;
> +}
> +
> +static void intel_allocator_destroy(struct intel_allocator *ial)
> +{
> +	alloc_info("Destroying allocator (empty: %d)\n",
> +		   ial->is_empty(ial));
> +
> +	ial->destroy(ial);
> +}
> +
> +static struct intel_allocator *__allocator_get(int fd, uint32_t ctx)
> +{
> +	struct intel_allocator *ial, ials = { .fd = fd, .ctx = ctx };
> +	int refcount;
> +
> +	ial = igt_map_find(allocators_map, &ials);
> +	if (!ial)
> +		goto out_get;
> +
> +	refcount = atomic_fetch_add(&ial->refcount, 1);
> +	igt_assert(refcount > 0);
> +
> +out_get:
> +
> +	return ial;
> +}
> +
> +static bool __allocator_put(struct intel_allocator *ial)
> +{
> +	struct intel_allocator ials = { .fd = ial->fd, .ctx = ial->ctx };
> +	bool released = false;
> +	int refcount;
> +
> +	ial = igt_map_find(allocators_map, &ials);
> +	igt_assert(ial);
> +
> +	refcount = atomic_fetch_sub(&ial->refcount, 1);
> +	alloc_debug("Refcount: %d\n", refcount);
> +	igt_assert(refcount >= 1);
> +	if (refcount == 1) {
> +		igt_map_del(allocators_map, ial);
> +
> +		if (!ial->is_empty(ial) && warn_if_not_empty)
> +			igt_warn("Allocator not clear before destroy!\n");
> +
> +		released = true;
> +	}
> +
> +	return released;
> +}
> +
> +static struct intel_allocator *allocator_open(int fd, uint32_t ctx,
> +					      uint8_t allocator_type)
> +{
> +	struct intel_allocator *ial;
> +
> +	pthread_mutex_lock(&map_mutex);
> +
> +	ial = __allocator_get(fd, ctx);
> +	if (ial) {
> +		if (ial->type != allocator_type)
> +			pthread_mutex_unlock(&map_mutex);
> +		igt_assert_f(ial->type == allocator_type,
> +			     "Allocator must be same type for fd/ctx\n");
> +	} else {
> +		alloc_debug("Allocator fd: %d, ctx: %u not found, creating one\n",
> +			    fd, ctx);
> +		ial = intel_allocator_create(fd, ctx, allocator_type);
> +	}
> +
> +	pthread_mutex_unlock(&map_mutex);
> +
> +	return ial;
> +}
> +
> +static bool allocator_close(uint64_t allocator_handle)
> +{
> +	struct intel_allocator *ial = from_user_pointer(allocator_handle);
> +	bool released, is_empty = false;
> +
> +	igt_assert(ial);
> +
> +	pthread_mutex_lock(&map_mutex);
> +
> +	released = __allocator_put(ial);
> +	if (released) {
> +		is_empty = ial->is_empty(ial);
> +		intel_allocator_destroy(ial);
> +	}
> +
> +	pthread_mutex_unlock(&map_mutex);
> +
> +	return is_empty;
> +}
> +
> +static int send_req_recv_resp(struct msg_channel *msgchan,
> +			      struct alloc_req *request,
> +			      struct alloc_resp *response)
> +{
> +	int ret;
> +
> +	ret = send_req(msgchan, child_tid, request);
> +	if (ret < 0) {
> +		igt_warn("Error sending request [type: %d]: err = %d [%s]\n",
> +			 request->request_type, errno, strerror(errno));
> +
> +		return ret;
> +	}
> +
> +	ret = recv_resp(msgchan, child_tid, response);
> +	if (ret < 0)
> +		igt_warn("Error receiving response [type: %d]: err = %d [%s]\n",
> +			 request->request_type, errno, strerror(errno));
> +
> +	/*
> +	 * This is main assumption - we receive message which size must be > 0.
> +	 * If this is fulfilled we return 0 as a success.
> +	 */
> +	if (ret > 0)
> +		ret = 0;
> +
> +	return ret;
> +}
> +
> +static int handle_request(struct alloc_req *req, struct alloc_resp *resp)
> +{
> +	bool same_process = child_pid == -1;
> +	int ret;
> +
> +	memset(resp, 0, sizeof(*resp));
> +
> +	if (same_process) {
> +		struct intel_allocator *ial;
> +		uint64_t start, end, size;
> +		bool allocated, reserved, unreserved;
> +
> +		/* Mutex only work on allocator instance, not stop/open/close */
> +		if (req->request_type > REQ_CLOSE) {
> +			ial = from_user_pointer(req->allocator_handle);
> +			igt_assert(ial);
> +
> +			pthread_mutex_lock(&ial->mutex);
> +		}
> +
> +		switch (req->request_type) {
> +		case REQ_STOP:
> +			alloc_info("<stop>\n");
> +			break;
> +
> +		case REQ_OPEN:
> +			ial = allocator_open(req->open.fd, req->open.ctx,
> +					     req->open.allocator_type);
> +			igt_assert(ial);
> +
> +			resp->response_type = RESP_OPEN;
> +			resp->open.allocator_handle = to_user_pointer(ial);
> +			alloc_info("<open> [tid: %ld] fd: %d, ctx: %u, alloc_type: %u, "
> +				   "ahnd: %p, refcnt: %d\n",
> +				   (long) req->tid, req->open.fd, req->open.ctx,
> +				   req->open.allocator_type, ial,
> +				   atomic_load(&ial->refcount));
> +			break;
> +
> +		case REQ_CLOSE:
> +			ial = from_user_pointer(req->allocator_handle);
> +			igt_assert(ial);
> +
> +			resp->response_type = RESP_CLOSE;
> +			ret = atomic_load(&ial->refcount);
> +			resp->close.is_empty = allocator_close(req->allocator_handle);
> +			alloc_info("<close> [tid: %ld] ahnd: %p, is_empty: %d, refcnt: %d\n",
> +				   (long) req->tid, ial, resp->close.is_empty, ret);
> +			break;
> +
> +		case REQ_ADDRESS_RANGE:
> +			resp->response_type = RESP_ADDRESS_RANGE;
> +			ial->get_address_range(ial, &start, &end);
> +			resp->address_range.start = start;
> +			resp->address_range.end = end;
> +			alloc_info("<address range> [tid: %ld] start: %" PRIx64
> +				   ", end: %" PRId64 "\n", (long) req->tid,
> +				   start, end);
> +			break;
> +
> +		case REQ_ALLOC:
> +			resp->response_type = RESP_ALLOC;
> +			resp->alloc.offset = ial->alloc(ial,
> +							req->alloc.handle,
> +							req->alloc.size,
> +							req->alloc.alignment);
> +			alloc_info("<alloc> [tid: %ld] handle: %u, offset: %" PRIx64
> +				   ", alignment: %" PRIx64 "\n",
> +				   (long) req->tid, req->alloc.handle,
> +				   resp->alloc.offset, req->alloc.alignment);
> +			break;
> +
> +		case REQ_FREE:
> +			resp->response_type = RESP_FREE;
> +			resp->free.freed = ial->free(ial, req->free.handle);
> +			alloc_info("<free> [tid: %ld] handle: %u, freed: %d\n",
> +				   (long) req->tid, req->free.handle, resp->free.freed);
> +			break;
> +
> +		case REQ_IS_ALLOCATED:
> +			resp->response_type = RESP_IS_ALLOCATED;
> +			allocated = ial->is_allocated(ial,
> +						      req->is_allocated.handle,
> +						      req->is_allocated.size,
> +						      req->is_allocated.offset);
> +			resp->is_allocated.allocated = allocated;
> +			alloc_info("<is allocated> [tid: %ld] offset: %" PRIx64
> +				   ", allocated: %d\n", (long) req->tid,
> +				   req->is_allocated.offset, allocated);
> +			break;
> +
> +		case REQ_RESERVE:
> +			resp->response_type = RESP_RESERVE;
> +			reserved = ial->reserve(ial,
> +						req->reserve.handle,
> +						req->reserve.start,
> +						req->reserve.end);
> +			resp->reserve.reserved = reserved;
> +			alloc_info("<reserve> [tid: %ld] handle: %u, start: %" PRIx64
> +				   ", end: %" PRIx64 ", reserved: %d\n",
> +				   (long) req->tid, req->reserve.handle,
> +				   req->reserve.start, req->reserve.end, reserved);
> +			break;
> +
> +		case REQ_UNRESERVE:
> +			resp->response_type = RESP_UNRESERVE;
> +			unreserved = ial->unreserve(ial,
> +						    req->unreserve.handle,
> +						    req->unreserve.start,
> +						    req->unreserve.end);
> +			resp->unreserve.unreserved = unreserved;
> +			alloc_info("<unreserve> [tid: %ld] handle: %u, start: %" PRIx64
> +				   ", end: %" PRIx64 ", unreserved: %d\n",
> +				   (long) req->tid, req->unreserve.handle,
> +				   req->unreserve.start, req->unreserve.end,
> +				   unreserved);
> +			break;
> +
> +		case REQ_IS_RESERVED:
> +			resp->response_type = RESP_IS_RESERVED;
> +			reserved = ial->is_reserved(ial,
> +						    req->is_reserved.start,
> +						    req->is_reserved.end);
> +			resp->is_reserved.reserved = reserved;
> +			alloc_info("<is reserved> [tid: %ld] start: %" PRIx64
> +				   ", end: %" PRIx64 ", reserved: %d\n",
> +				   (long) req->tid, req->is_reserved.start,
> +				   req->is_reserved.end, reserved);
> +			break;
> +
> +		case REQ_RESERVE_IF_NOT_ALLOCATED:
> +			resp->response_type = RESP_RESERVE_IF_NOT_ALLOCATED;
> +			size = DECANONICAL(req->reserve.end) - DECANONICAL(req->reserve.start);
> +
> +			allocated = ial->is_allocated(ial, req->reserve.handle,
> +						      size, req->reserve.start);
> +			if (allocated) {
> +				resp->reserve_if_not_allocated.allocated = allocated;
> +				alloc_info("<reserve if not allocated> [tid: %ld] handle: %u "
> +					   "size: %lx, start: %" PRIx64
> +					   ", end: %" PRIx64 ", allocated: %d, reserved: %d\n",
> +					   (long) req->tid, req->reserve.handle,
> +					   (long) size, req->reserve.start,
> +					   req->reserve.end, allocated, false);
> +				break;
> +			}
> +
> +			reserved = ial->reserve(ial,
> +						req->reserve.handle,
> +						req->reserve.start,
> +						req->reserve.end);
> +			resp->reserve_if_not_allocated.reserved = reserved;
> +			alloc_info("<reserve if not allocated> [tid: %ld] handle: %u"
> +				   ", start: %" PRIx64
> +				   ", end: %" PRIx64 ", allocated: %d, reserved: %d\n",
> +				   (long) req->tid, req->reserve.handle,
> +				   req->reserve.start, req->reserve.end,
> +				   false, reserved);
> +			break;
> +
> +		}
> +
> +		if (req->request_type > REQ_CLOSE)
> +			pthread_mutex_unlock(&ial->mutex);
> +
> +		return 0;
> +	}
> +
> +	ret = send_req_recv_resp(channel, req, resp);
> +
> +	if (ret < 0)
> +		exit(0);
> +
> +	return ret;
> +}
> +
> +static void kill_children(int sig)
> +{
> +	signal(sig, SIG_IGN);
> +	kill(-getpgrp(), sig);
> +	signal(sig, SIG_DFL);
> +}
> +
> +static void *allocator_thread_loop(void *data)
> +{
> +	struct alloc_req req;
> +	struct alloc_resp resp;
> +	int ret;
> +	(void) data;
> +
> +	alloc_info("Allocator pid: %ld, tid: %ld\n",
> +		   (long) allocator_pid, (long) gettid());
> +	alloc_info("Entering allocator loop\n");
> +
> +	while (1) {
> +		ret = recv_req(channel, &req);
> +
> +		if (ret == -1) {
> +			igt_warn("Error receiving request in thread, ret = %d [%s]\n",
> +				 ret, strerror(errno));
> +			kill_children(SIGINT);
> +			return (void *) -1;
> +		}
> +
> +		/* Fake message to stop the thread */
> +		if (req.request_type == REQ_STOP) {
> +			alloc_info("<stop request>\n");
> +			break;
> +		}
> +
> +		ret = handle_request(&req, &resp);
> +		if (ret) {
> +			igt_warn("Error handling request in thread, ret = %d [%s]\n",
> +				 ret, strerror(errno));
> +			break;
> +		}
> +
> +		ret = send_resp(channel, req.tid, &resp);
> +		if (ret) {
> +			igt_warn("Error sending response in thread, ret = %d [%s]\n",
> +				 ret, strerror(errno));
> +
> +			kill_children(SIGINT);
> +			return (void *) -1;
> +		}
> +	}
> +
> +	return NULL;
> +}
> +
> +/**
> + * intel_allocator_multiprocess_start:
> + *
> + * Function turns on intel_allocator multiprocess mode what means
> + * all allocations from children processes are performed in a separate thread
> + * within main igt process. Children are aware of the situation and use
> + * some interprocess communication channel to send/receive messages
> + * (open, close, alloc, free, ...) to/from allocator thread.
> + *
> + * Must be used when you want to use an allocator in non single-process code.
> + * All allocations in threads spawned in main igt process are handled by
> + * mutexing, not by sending/receiving messages to/from allocator thread.
> + *
> + * Note. This destroys all previously created allocators and theirs content.
> + */
> +void intel_allocator_multiprocess_start(void)
> +{
> +	alloc_info("allocator multiprocess start\n");
> +
> +	intel_allocator_init();
> +
> +	multiprocess = true;
> +	channel->init(channel);
> +
> +	pthread_create(&allocator_thread, NULL,
> +		       allocator_thread_loop, NULL);
> +}
> +
> +/**
> + * intel_allocator_multiprocess_stop:
> + *
> + * Function turns off intel_allocator multiprocess mode what means means
> + * stopping allocator thread and deinitializing its data.
> + */
> +void intel_allocator_multiprocess_stop(void)
> +{
> +	alloc_info("allocator multiprocess stop\n");
> +
> +	if (multiprocess) {
> +		send_alloc_stop(channel);
> +		/* Deinit, this should stop all blocked syscalls, if any */
> +		channel->deinit(channel);
> +		pthread_join(allocator_thread, NULL);
> +		/* But we're not sure does child will stuck */
> +		kill_children(SIGINT);
> +		igt_waitchildren_timeout(5, "Stopping children");
> +		multiprocess = false;
> +	}
> +}
> +
> +/**
> + * intel_allocator_open:
> + * @fd: i915 descriptor
> + * @ctx: context
> + * @allocator_type: one of INTEL_ALLOCATOR_* define
> + *
> + * Function opens an allocator instance for given @fd and @ctx and returns
> + * its handle. If the allocator for such pair doesn't exist it is created
> + * with refcount = 1. Parallel opens returns same handle bumping its refcount.
> + *
> + * Returns: unique handle to the currently opened allocator.
> + */
> +uint64_t intel_allocator_open(int fd, uint32_t ctx, uint8_t allocator_type)
> +{
> +	struct alloc_req req = { .request_type = REQ_OPEN,
> +				 .open.fd = fd,
> +				 .open.ctx = ctx,
> +				 .open.allocator_type = allocator_type };
> +	struct alloc_resp resp;
> +
> +	/* Get child_tid only once at open() */
> +	if (child_tid == -1)
> +		child_tid = gettid();
> +
> +	igt_assert(handle_request(&req, &resp) == 0);
> +	igt_assert(resp.open.allocator_handle);
> +	igt_assert(resp.response_type == RESP_OPEN);
> +
> +	return resp.open.allocator_handle;
> +}
> +
> +/**
> + * intel_allocator_close:
> + * @allocator_handle: handle to the allocator that will be closed
> + *
> + * Function decreases an allocator refcount for the given @handle.
> + * When refcount reaches zero allocator is closed (destroyed) and all
> + * allocated / reserved areas are freed.
> + *
> + * Returns: true if closed allocator was empty, false otherwise.
> + */
> +bool intel_allocator_close(uint64_t allocator_handle)
> +{
> +	struct alloc_req req = { .request_type = REQ_CLOSE,
> +				 .allocator_handle = allocator_handle };
> +	struct alloc_resp resp;
> +
> +	igt_assert(handle_request(&req, &resp) == 0);
> +	igt_assert(resp.response_type == RESP_CLOSE);
> +
> +	return resp.close.is_empty;
> +}
> +
> +/**
> + * intel_allocator_get_address_range:
> + * @allocator_handle: handle to an allocator
> + * @startp: pointer to the variable where function writes starting offset
> + * @endp: pointer to the variable where function writes ending offset
> + *
> + * Function fills @startp, @endp with respectively, starting and ending offset
> + * of the allocator working virtual address space range.
> + *
> + * Note. Allocators working ranges can differ depending on the device or
> + * the allocator type so before reserving a specific offset a good practise
> + * is to ensure that address is between accepted range.
> + */
> +void intel_allocator_get_address_range(uint64_t allocator_handle,
> +				       uint64_t *startp, uint64_t *endp)
> +{
> +	struct alloc_req req = { .request_type = REQ_ADDRESS_RANGE,
> +				 .allocator_handle = allocator_handle };
> +	struct alloc_resp resp;
> +
> +	igt_assert(handle_request(&req, &resp) == 0);
> +	igt_assert(resp.response_type == RESP_ADDRESS_RANGE);
> +
> +	if (startp)
> +		*startp = resp.address_range.start;
> +
> +	if (endp)
> +		*endp = resp.address_range.end;
> +}
> +
> +/**
> + * intel_allocator_alloc:
> + * @allocator_handle: handle to an allocator
> + * @handle: handle to an object
> + * @size: size of an object
> + * @alignment: determines object alignment
> + *
> + * Function finds and returns the most suitable offset with given @alignment
> + * for an object with @size identified by the @handle.
> + *
> + * Returns: currently assigned address for a given object. If an object was
> + * already allocated returns same address.
> + */
> +uint64_t intel_allocator_alloc(uint64_t allocator_handle, uint32_t handle,
> +			       uint64_t size, uint64_t alignment)
> +{
> +	struct alloc_req req = { .request_type = REQ_ALLOC,
> +				 .allocator_handle = allocator_handle,
> +				 .alloc.handle = handle,
> +				 .alloc.size = size,
> +				 .alloc.alignment = alignment };
> +	struct alloc_resp resp;
> +
> +	igt_assert(handle_request(&req, &resp) == 0);
> +	igt_assert(resp.response_type == RESP_ALLOC);
> +
> +	return resp.alloc.offset;
> +}
> +
> +/**
> + * intel_allocator_free:
> + * @allocator_handle: handle to an allocator
> + * @handle: handle to an object to be freed
> + *
> + * Function free object identified by the @handle in allocator what makes it
> + * offset again allocable.
> + *
> + * Note. Reserved objects can only be freed by an #intel_allocator_unreserve
> + * function.
> + *
> + * Returns: true if the object was successfully freed, otherwise false.
> + */
> +bool intel_allocator_free(uint64_t allocator_handle, uint32_t handle)
> +{
> +	struct alloc_req req = { .request_type = REQ_FREE,
> +				 .allocator_handle = allocator_handle,
> +				 .free.handle = handle };
> +	struct alloc_resp resp;
> +
> +	igt_assert(handle_request(&req, &resp) == 0);
> +	igt_assert(resp.response_type == RESP_FREE);
> +
> +	return resp.free.freed;
> +}
> +
> +/**
> + * intel_allocator_is_allocated:
> + * @allocator_handle: handle to an allocator
> + * @handle: handle to an object
> + * @size: size of an object
> + * @offset: address of an object
> + *
> + * Function checks whether the object identified by the @handle and @size
> + * is allocated at the @offset.
> + *
> + * Returns: true if the object is currently allocated at the @offset,
> + * otherwise false.
> + */
> +bool intel_allocator_is_allocated(uint64_t allocator_handle, uint32_t handle,
> +				  uint64_t size, uint64_t offset)
> +{
> +	struct alloc_req req = { .request_type = REQ_IS_ALLOCATED,
> +				 .allocator_handle = allocator_handle,
> +				 .is_allocated.handle = handle,
> +				 .is_allocated.size = size,
> +				 .is_allocated.offset = offset };
> +	struct alloc_resp resp;
> +
> +	igt_assert(handle_request(&req, &resp) == 0);
> +	igt_assert(resp.response_type == RESP_IS_ALLOCATED);
> +
> +	return resp.is_allocated.allocated;
> +}
> +
> +/**
> + * intel_allocator_reserve:
> + * @allocator_handle: handle to an allocator
> + * @handle: handle to an object
> + * @size: size of an object
> + * @offset: address of an object
> + *
> + * Function reserves space that starts at the @offset and has @size.
> + * Optionally we can pass @handle to mark that space is for a specific
> + * object, otherwise pass -1.
> + *
> + * Note. Reserved space is identified by offset and size, not a handle.
> + * So an object can have multiple reserved spaces with its handle.
> + *
> + * Returns: true if space is successfully reserved, otherwise false.
> + */
> +bool intel_allocator_reserve(uint64_t allocator_handle, uint32_t handle,
> +			     uint64_t size, uint64_t offset)
> +{
> +	struct alloc_req req = { .request_type = REQ_RESERVE,
> +				 .allocator_handle = allocator_handle,
> +				 .reserve.handle = handle,
> +				 .reserve.start = offset,
> +				 .reserve.end = offset + size };
> +	struct alloc_resp resp;
> +
> +	igt_assert(handle_request(&req, &resp) == 0);
> +	igt_assert(resp.response_type == RESP_RESERVE);
> +
> +	return resp.reserve.reserved;
> +}
> +
> +/**
> + * intel_allocator_unreserve:
> + * @allocator_handle: handle to an allocator
> + * @handle: handle to an object
> + * @size: size of an object
> + * @offset: address of an object
> + *
> + * Function unreserves space that starts at the @offset, @size and @handle.
> + *
> + * Note. @handle, @size and @offset have to match those used in reservation.
> + * i.e. check with the same offset but even smaller size will fail.
> + *
> + * Returns: true if the space is successfully unreserved, otherwise false.
> + */
> +bool intel_allocator_unreserve(uint64_t allocator_handle, uint32_t handle,
> +			       uint64_t size, uint64_t offset)
> +{
> +	struct alloc_req req = { .request_type = REQ_UNRESERVE,
> +				 .allocator_handle = allocator_handle,
> +				 .unreserve.handle = handle,
> +				 .unreserve.start = offset,
> +				 .unreserve.end = offset + size };
> +	struct alloc_resp resp;
> +
> +	igt_assert(handle_request(&req, &resp) == 0);
> +	igt_assert(resp.response_type == RESP_UNRESERVE);
> +
> +	return resp.unreserve.unreserved;
> +}
> +
> +/**
> + * intel_allocator_is_reserved:
> + * @allocator_handle: handle to an allocator
> + * @size: size of an object
> + * @offset: address of an object
> + *
> + * Function checks whether space starting at the @offset and @size is
> + * currently under reservation.
> + *
> + * Note. @size and @offset have to match those used in reservation,
> + * i.e. check with the same offset but even smaller size will fail.
> + *
> + * Returns: true if space is reserved, othwerise false.
> + */
> +bool intel_allocator_is_reserved(uint64_t allocator_handle,
> +				 uint64_t size, uint64_t offset)
> +{
> +	struct alloc_req req = { .request_type = REQ_IS_RESERVED,
> +				 .allocator_handle = allocator_handle,
> +				 .is_reserved.start = offset,
> +				 .is_reserved.end = offset + size };
> +	struct alloc_resp resp;
> +
> +	igt_assert(handle_request(&req, &resp) == 0);
> +	igt_assert(resp.response_type == RESP_IS_RESERVED);
> +
> +	return resp.is_reserved.reserved;
> +}
> +
> +/**
> + * intel_allocator_reserve_if_not_allocated:
> + * @allocator_handle: handle to an allocator
> + * @handle: handle to an object
> + * @size: size of an object
> + * @offset: address of an object
> + * @is_allocatedp: if not NULL function writes there object allocation status
> + * (true/false)
> + *
> + * Function checks whether the object identified by the @handle and @size
> + * is allocated at the @offset and writes the result to @is_allocatedp.
> + * If it's not it reserves it at the given @offset.
> + *
> + * Returns: true if the space for an object was reserved, otherwise false.
> + */
> +bool intel_allocator_reserve_if_not_allocated(uint64_t allocator_handle,
> +					      uint32_t handle,
> +					      uint64_t size, uint64_t offset,
> +					      bool *is_allocatedp)
> +{
> +	struct alloc_req req = { .request_type = REQ_RESERVE_IF_NOT_ALLOCATED,
> +				 .allocator_handle = allocator_handle,
> +				 .reserve.handle = handle,
> +				 .reserve.start = offset,
> +				 .reserve.end = offset + size };
> +	struct alloc_resp resp;
> +
> +	igt_assert(handle_request(&req, &resp) == 0);
> +	igt_assert(resp.response_type == RESP_RESERVE_IF_NOT_ALLOCATED);
> +
> +	if (is_allocatedp)
> +		*is_allocatedp = resp.reserve_if_not_allocated.allocated;
> +
> +	return resp.reserve_if_not_allocated.reserved;
> +}
> +
> +/**
> + * intel_allocator_print:
> + * @allocator_handle: handle to an allocator
> + *
> + * Function prints statistics and content of the allocator.
> + * Mainly for debugging purposes.
> + *
> + * Note. Printing possible only in the main process.
> + **/
> +void intel_allocator_print(uint64_t allocator_handle)
> +{
> +	bool same_process;
> +
> +	igt_assert(allocator_handle);
> +
> +	same_process = child_pid == -1;
> +
> +	if (!multiprocess || same_process) {
> +		struct intel_allocator *ial = from_user_pointer(allocator_handle);
> +		pthread_mutex_lock(&map_mutex);
> +		ial->print(ial, true);
> +		pthread_mutex_unlock(&map_mutex);
> +	} else {
> +		igt_warn("Print stats is in main process only\n");
> +	}
> +}
> +
> +static bool equal_allocators(const void *key1, const void *key2)
> +{
> +	const struct intel_allocator *a1 = key1, *a2 = key2;
> +
> +	alloc_debug("a1: <fd: %d, ctx: %u>, a2 <fd: %d, ctx: %u>\n",
> +		   a1->fd, a1->ctx, a2->fd, a2->ctx);
> +
> +	return a1->fd == a2->fd && a1->ctx == a2->ctx;
> +}
> +
> +/*  2^63 + 2^61 - 2^57 + 2^54 - 2^51 - 2^18 + 1 */
> +#define GOLDEN_RATIO_PRIME_64 0x9e37fffffffc0001UL
> +
> +static inline uint64_t hash_allocators(const void *val, unsigned int bits)
> +{
> +	uint64_t hash = ((struct intel_allocator *) val)->fd;
> +
> +	hash = hash * GOLDEN_RATIO_PRIME_64;
> +	return hash >> (64 - bits);
> +}
> +
> +static void __free_allocators(void)
> +{
> +	struct igt_map_entry *pos;
> +	struct intel_allocator *ial;
> +	int i;
> +
> +	if (allocators_map) {
> +		igt_map_for_each(allocators_map, i, pos) {
> +			ial = pos->value;
> +			ial->destroy(ial);
> +		}
> +	}
> +
> +	igt_map_free(allocators_map);
> +}
> +
> +/**
> + * intel_allocator_init:
> + *
> + * Function initializes the allocators infrastructure. The second call will
> + * override current infra and destroy existing there allocators. It is called
> + * in igt_constructor.
> + **/
> +void intel_allocator_init(void)
> +{
> +	alloc_info("Prepare an allocator infrastructure\n");
> +
> +	allocator_pid = getpid();
> +	alloc_info("Allocator pid: %ld\n", (long) allocator_pid);
> +
> +	if (allocators_map) {
> +		__free_allocators();
> +		free(allocators_map);
> +	}
> +
> +	allocators_map = calloc(sizeof(*allocators_map), 1);
> +	igt_assert(allocators_map);
> +
> +	igt_map_init(allocators_map, equal_allocators, hash_allocators, 8);
> +
> +	channel = intel_allocator_get_msgchannel(CHANNEL_SYSVIPC_MSGQUEUE);
> +}
> +
> +igt_constructor {
> +	intel_allocator_init();
> +}
> diff --git a/lib/intel_allocator.h b/lib/intel_allocator.h
> new file mode 100644
> index 00000000..f3747a8b
> --- /dev/null
> +++ b/lib/intel_allocator.h
> @@ -0,0 +1,136 @@
> +#ifndef __INTEL_ALLOCATOR_H__
> +#define __INTEL_ALLOCATOR_H__
> +
> +#include <stdint.h>
> +#include <stdbool.h>
> +#include <pthread.h>
> +#include <stdint.h>
> +#include <stdatomic.h>
> +
> +/**
> + * SECTION:intel_allocator
> + * @short_description: igt implementation of allocator
> + * @title: Intel allocator
> + * @include: intel_allocator.h
> + *
> + * # Intel allocator
> + *
> + * Since GPU devices driver has abandoned relocations for newer generations,
> + * we are facing the need to manage addresses in userspace. Intel allocator
> + * supply out of the box mechanisms providing correct virtual addresses.
> + * Specifically, intel_allocator is a multi-threading infrastructure wrapping
> + * a proper single-thread allocator, that can be the one of the following:
> + *
> + *  * INTEL_ALLOCATOR_SIMPLE - ported from Mesa, list-based, simple allocator
> + *  * INTEL_ALLOCATOR_RANDOM - stateless allocator, that provides random addresses
> + *  (sometime in the future the list can grow)
> + *
> + * Usage example:
> + *
> + * |[<!-- language="c" -->
> + * struct object {
> + * 	uint32_t handle;
> + * 	uint64_t offset;
> + * 	uint64_t size;
> + * };
> + *
> + * struct object obj1, obj2;
> + * uint64_t ahnd, startp, endp;
> + * int fd = -1;
> + *
> + * fd = drm_open_driver(DRIVER_INTEL);
> + * ahnd = intel_allocator_open(fd, 0, INTEL_ALLOCATOR_SIMPLE);
> + *
> + * obj1.handle = gem_create(4096);
> + * obj2.handle = gem_create(4096);
> + *
> + * // Reserve hole for an object in given address.
> + * // In this example the first possible address.
> + * intel_allocator_get_address_range(ahnd, &startp, &endp);
> + * obj1.offset = startp;
> + * igt_assert(intel_allocator_reserve(ahnd, obj1.handle, 4096, startp));
> + *
> + * // Get the most suitable offset for the object. Prefered way.
> + * obj2.offset = intel_allocator_alloc(ahnd, obj2.handle, 4096, 1 << 13);
> + *
> + *  ...
> + *
> + * // Reserved addresses can be only freed by unreserve.
> + * intel_allocator_unreserve(ahnd, obj1.handle, 4096, obj1.offset);
> + * intel_allocator_free(ahnd, obj2.handle);
> + *
> + * gem_close(obj1.handle);
> + * gem_close(obj2.handle);
> + * ]|
> + *
> + */
> +
> +struct intel_allocator {
> +	int fd;
> +	uint32_t ctx;
> +	uint8_t type;
> +	_Atomic(int32_t) refcount;
> +	pthread_mutex_t mutex;
> +
> +	/* allocator's private structure */
> +	void *priv;
> +
> +	void (*get_address_range)(struct intel_allocator *ial,
> +				  uint64_t *startp, uint64_t *endp);
> +	uint64_t (*alloc)(struct intel_allocator *ial, uint32_t handle,
> +			  uint64_t size, uint64_t alignment);
> +	bool (*is_allocated) (struct intel_allocator *ial, uint32_t handle,
> +			      uint64_t size, uint64_t alignment);
> +	bool (*reserve)(struct intel_allocator *ial,
> +			uint32_t handle, uint64_t start, uint64_t size);
> +	bool (*unreserve)(struct intel_allocator *ial,
> +			  uint32_t handle, uint64_t start, uint64_t size);
> +	bool (*is_reserved) (struct intel_allocator *ial,
> +			     uint64_t start, uint64_t size);
> +	bool (*free)(struct intel_allocator *ial, uint32_t handle);
> +
> +	void (*destroy)(struct intel_allocator *ial);
> +
> +	bool (*is_empty)(struct intel_allocator *ial);
> +
> +	void (*print)(struct intel_allocator *ial, bool full);
> +};
> +
> +void intel_allocator_init(void);
> +void intel_allocator_multiprocess_start(void);
> +void intel_allocator_multiprocess_stop(void);
> +
> +uint64_t intel_allocator_open(int fd, uint32_t ctx, uint8_t allocator_type);
> +bool intel_allocator_close(uint64_t allocator_handle);
> +void intel_allocator_get_address_range(uint64_t allocator_handle,
> +				       uint64_t *startp, uint64_t *endp);
> +uint64_t intel_allocator_alloc(uint64_t allocator_handle, uint32_t handle,
> +			       uint64_t size, uint64_t alignment);
> +bool intel_allocator_free(uint64_t allocator_handle, uint32_t handle);
> +bool intel_allocator_is_allocated(uint64_t allocator_handle, uint32_t handle,
> +				  uint64_t size, uint64_t offset);
> +bool intel_allocator_reserve(uint64_t allocator_handle, uint32_t handle,
> +			     uint64_t size, uint64_t offset);
> +bool intel_allocator_unreserve(uint64_t allocator_handle, uint32_t handle,
> +			       uint64_t size, uint64_t offset);
> +bool intel_allocator_is_reserved(uint64_t allocator_handle,
> +				 uint64_t start, uint64_t size);
> +bool intel_allocator_reserve_if_not_allocated(uint64_t allocator_handle,
> +					      uint32_t handle,
> +					      uint64_t size, uint64_t offset,
> +					      bool *is_allocatedp);
> +
> +void intel_allocator_print(uint64_t allocator_handle);
> +
> +#define INTEL_ALLOCATOR_NONE   0
> +#define INTEL_ALLOCATOR_RANDOM 1
> +#define INTEL_ALLOCATOR_SIMPLE 2
> +
> +static inline uint64_t CANONICAL(uint64_t address)
> +{
> +	return (int64_t)(address << 16) >> 16;
> +}
> +
> +#define DECANONICAL(offset) (offset & ((1ull << 48) - 1))
> +
> +#endif
> diff --git a/lib/intel_allocator_msgchannel.c b/lib/intel_allocator_msgchannel.c
> new file mode 100644
> index 00000000..084a977d
> --- /dev/null
> +++ b/lib/intel_allocator_msgchannel.c
> @@ -0,0 +1,182 @@
> +#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;
> +}
> diff --git a/lib/intel_allocator_msgchannel.h b/lib/intel_allocator_msgchannel.h
> new file mode 100644
> index 00000000..ab46d9ea
> --- /dev/null
> +++ b/lib/intel_allocator_msgchannel.h
> @@ -0,0 +1,140 @@
> +#ifndef __INTEL_ALLOCATOR_MSGCHANNEL_H__
> +#define __INTEL_ALLOCATOR_MSGCHANNEL_H__
> +
> +#include <sys/types.h>
> +#include <unistd.h>
> +#include <stdint.h>
> +
> +enum reqtype {
> +	REQ_STOP,
> +	REQ_OPEN,
> +	REQ_CLOSE,
> +	REQ_ADDRESS_RANGE,
> +	REQ_ALLOC,
> +	REQ_FREE,
> +	REQ_IS_ALLOCATED,
> +	REQ_RESERVE,
> +	REQ_UNRESERVE,
> +	REQ_RESERVE_IF_NOT_ALLOCATED,
> +	REQ_IS_RESERVED,
> +};
> +
> +enum resptype {
> +	RESP_OPEN,
> +	RESP_CLOSE,
> +	RESP_ADDRESS_RANGE,
> +	RESP_ALLOC,
> +	RESP_FREE,
> +	RESP_IS_ALLOCATED,
> +	RESP_RESERVE,
> +	RESP_UNRESERVE,
> +	RESP_IS_RESERVED,
> +	RESP_RESERVE_IF_NOT_ALLOCATED,
> +};
> +
> +struct alloc_req {
> +	enum reqtype request_type;
> +
> +	/* Common */
> +	pid_t tid;
> +	uint64_t allocator_handle;
> +
> +	union {
> +		struct {
> +			int fd;
> +			uint32_t ctx;
> +			uint8_t allocator_type;
> +		} open;
> +
> +		struct {
> +			uint32_t handle;
> +			uint64_t size;
> +			uint64_t alignment;
> +		} alloc;
> +
> +		struct {
> +			uint32_t handle;
> +		} free;
> +
> +		struct {
> +			uint32_t handle;
> +			uint64_t size;
> +			uint64_t offset;
> +		} is_allocated;
> +
> +		struct {
> +			uint32_t handle;
> +			uint64_t start;
> +			uint64_t end;
> +		} reserve, unreserve;
> +
> +		struct {
> +			uint64_t start;
> +			uint64_t end;
> +		} is_reserved;
> +
> +	};
> +};
> +
> +struct alloc_resp {
> +	enum resptype response_type;
> +	pid_t tid;
> +
> +	union {
> +		struct {
> +			uint64_t allocator_handle;
> +		} open;
> +
> +		struct {
> +			bool is_empty;
> +		} close;
> +
> +		struct {
> +			uint64_t start;
> +			uint64_t end;
> +		} address_range;
> +
> +		struct {
> +			uint64_t offset;
> +		} alloc;
> +
> +		struct {
> +			bool freed;
> +		} free;
> +
> +		struct {
> +			bool allocated;
> +		} is_allocated;
> +
> +		struct {
> +			bool reserved;
> +		} reserve, is_reserved;
> +
> +		struct {
> +			bool unreserved;
> +		} unreserve;
> +
> +		struct {
> +			bool allocated;
> +			bool reserved;
> +		} reserve_if_not_allocated;
> +	};
> +};
> +
> +struct msg_channel {
> +	void *priv;
> +	void (*init)(struct msg_channel *channel);
> +	void (*deinit)(struct msg_channel *channel);
> +	int (*send_req)(struct msg_channel *channel, struct alloc_req *request);
> +	int (*recv_req)(struct msg_channel *channel, struct alloc_req *request);
> +	int (*send_resp)(struct msg_channel *channel, struct alloc_resp *response);
> +	int (*recv_resp)(struct msg_channel *channel, struct alloc_resp *response);
> +};
> +
> +enum msg_channel_type {
> +	CHANNEL_SYSVIPC_MSGQUEUE
> +};
> +
> +struct msg_channel *intel_allocator_get_msgchannel(enum msg_channel_type type);
> +
> +#endif
> diff --git a/lib/intel_allocator_simple.c b/lib/intel_allocator_simple.c
> index 1f52db3f..1d0e117d 100644
> --- a/lib/intel_allocator_simple.c
> +++ b/lib/intel_allocator_simple.c
> @@ -82,8 +82,6 @@ struct intel_allocator_record {
>  #define simple_vma_foreach_hole_safe_rev(_hole, _heap, _tmp) \
>  	igt_list_for_each_entry_safe_reverse(_hole, _tmp,  &(_heap)->holes, link)
>  
> -#define DECANONICAL(offset) (offset & ((1ull << 48) - 1))
> -
>  static uint64_t get_bias(int fd)
>  {
>  	(void) fd;
> diff --git a/lib/meson.build b/lib/meson.build
> index 484d3c7b..7a322a44 100644
> --- a/lib/meson.build
> +++ b/lib/meson.build
> @@ -32,6 +32,10 @@ lib_sources = [
>  	'igt_vgem.c',
>  	'igt_x86.c',
>  	'instdone.c',
> +	'intel_allocator.c',
> +	'intel_allocator_msgchannel.c',
> +	'intel_allocator_random.c',
> +	'intel_allocator_simple.c',
>  	'intel_batchbuffer.c',
>  	'intel_bufops.c',
>  	'intel_chipset.c',
> -- 
> 2.26.0
> 


More information about the igt-dev mailing list