Commit 15cd651b authored by Swann Perarnau's avatar Swann Perarnau

[feature] implement simple, working dma engine

This patch adds the basics for a dma interface, including
type-dependent requests structures, and an API based on explicit
copy/move calls.

The APIs is flexible enough to deal with sync/async calls. The internal
design is inspired by aml_area, with the goal that create/init stay type
specific, but the core interactions are generic.
parent 8a150cf2
......@@ -16,12 +16,16 @@ BINDING_CSOURCES = binding.c \
binding_single.c \
binding_interleave.c
DMA_CSOURCES = dma.c \
dma_linux_seq.c
LIBCSOURCES = aml.c area.c arena.c \
$(ARENA_JEMALLOC_CSOURCES) \
$(AREA_LINUX_CSOURCES) \
$(AREA_POSIX_CSOURCES) \
$(TILING_CSOURCES) \
$(BINDING_CSOURCES)
$(BINDING_CSOURCES) \
$(DMA_CSOURCES)
LIBHSOURCES = aml.h
......
......@@ -284,26 +284,6 @@ void *aml_area_mmap(struct aml_area *, void *, size_t);
int aml_area_available(struct aml_area *);
int aml_area_binding(struct aml_area *, struct aml_binding **);
/*******************************************************************************
* DMA Engines:
* Low-level, direct movement of memory.
* We haven't decided in our design how we want to deal with memcpy/move_pages
* differences yet.
******************************************************************************/
struct aml_dma {
int (*copy)(struct aml_dma *, void *, const void *, size_t);
int (*move)(struct aml_dma *, struct aml_area *, struct aml_area *,
void *, size_t);
};
int aml_dma_init(struct aml_dma *, unsigned int);
int aml_dma_destroy(struct aml_dma *);
int aml_dma_copy(struct aml_dma *, void *, const void *, size_t);
int aml_dma_move(struct aml_dma *, struct aml_area *, struct aml_area *,
void *, size_t);
/*******************************************************************************
* Tiling:
* Representation of a data structure organization in memory.
......@@ -485,6 +465,99 @@ struct aml_binding_interleave_data {
(sizeof(struct aml_binding_interleave_data) + \
sizeof(struct aml_binding))
/*******************************************************************************
* DMA:
* Management of low-level movement of memory.
******************************************************************************/
#define AML_DMA_REQUEST_TYPE_INVALID -1
#define AML_DMA_REQUEST_TYPE_COPY 0
#define AML_DMA_REQUEST_TYPE_MOVE 1
struct aml_dma_request_data;
struct aml_dma_data;
struct aml_dma_request_ops {
int (*copy)(struct aml_dma_data *, struct aml_dma_request_data *);
int (*move)(struct aml_dma_data *, struct aml_dma_request_data *);
};
struct aml_dma_request {
struct aml_dma_request_ops *ops;
struct aml_dma_request_data *data;
};
struct aml_dma_ops {
int (*create_request)(struct aml_dma_data *,
struct aml_dma_request *, int, va_list);
int (*destroy_request)(struct aml_dma_data *, struct aml_dma_request *);
int (*wait_request)(struct aml_dma_data *, struct aml_dma_request *);
};
struct aml_dma {
struct aml_dma_ops *ops;
struct aml_dma_data *data;
};
int aml_dma_copy(struct aml_dma *, ...);
int aml_dma_async_copy(struct aml_dma *, struct aml_dma_request *, ...);
int aml_dma_move(struct aml_dma *, ...);
int aml_dma_async_move(struct aml_dma *, struct aml_dma_request *, ...);
int aml_dma_wait(struct aml_dma *, struct aml_dma_request *);
int aml_dma_cancel(struct aml_dma *, struct aml_dma_request *);
/*******************************************************************************
* Linux Sequential DMA API:
* DMA logic implemented based on general linux API, with the caller thread
* used as the only execution thread.
******************************************************************************/
struct aml_dma_request_linux_seq_data {
int type;
void *dest;
void *src;
size_t size;
int count;
void **pages;
int *nodes;
};
extern struct aml_dma_ops aml_dma_linux_seq_ops;
struct aml_dma_linux_seq_data {
size_t size;
struct aml_dma_request_linux_seq_data *requests;
};
struct aml_dma_linux_seq_ops {
int (*add_request)(struct aml_dma_linux_seq_data *,
struct aml_dma_request_linux_seq_data **);
int (*remove_request)(struct aml_dma_linux_seq_data *,
struct aml_dma_request_linux_seq_data **);
};
struct aml_dma_linux_seq {
struct aml_dma_linux_seq_ops ops;
struct aml_dma_linux_seq_data data;
};
#define AML_DMA_LINUX_SEQ_DECL(name) \
struct aml_dma_linux_seq __ ##name## _inner_data; \
struct aml_dma name = { \
&aml_dma_linux_seq_ops, \
(struct aml_dma_data *)&__ ## name ## _inner_data, \
};
#define AML_DMA_LINUX_SEQ_ALLOCSIZE \
(sizeof(struct aml_dma_linux_seq) + \
sizeof(struct aml_dma))
int aml_dma_linux_seq_create(struct aml_dma **, ...);
int aml_dma_linux_seq_init(struct aml_dma *, ...);
int aml_dma_linux_seq_vinit(struct aml_dma *, va_list);
int aml_dma_linux_seq_destroy(struct aml_dma *);
/*******************************************************************************
* General functions:
* Initialize internal structures, cleanup everything at the end.
......
......@@ -2,27 +2,79 @@
#include <assert.h>
/*******************************************************************************
* DMA implementation
* At this point, implement the single threaded synchronous stuff
* Generic DMA API:
* Most of the stuff is dispatched to a different layer, using type-specific
* functions.
*
* Note that the API is slightly different than the functions bellow, as we
* abstract the request creation after this layer.
******************************************************************************/
int aml_dma_init(struct aml_dma *dma, unsigned int type)
int aml_dma_copy(struct aml_dma *dma, ...)
{
return 0;
assert(dma != NULL);
va_list ap;
int ret;
struct aml_dma_request req;
va_start(ap, dma);
ret = dma->ops->create_request(dma->data, &req,
AML_DMA_REQUEST_TYPE_COPY, ap);
va_end(ap);
ret = dma->ops->wait_request(dma->data, &req);
return ret;
}
int aml_dma_destroy(struct aml_dma *dma)
int aml_dma_async_copy(struct aml_dma *dma, struct aml_dma_request *req, ...)
{
return 0;
assert(dma != NULL);
assert(req != NULL);
va_list ap;
int ret;
va_start(ap, req);
ret = dma->ops->create_request(dma->data, req,
AML_DMA_REQUEST_TYPE_COPY, ap);
va_end(ap);
return ret;
}
int aml_dma_copy(struct aml_dma *dma, void *dest, const void *src, size_t size)
int aml_dma_move(struct aml_dma *dma, ...)
{
return 0;
assert(dma != NULL);
struct aml_dma_request req;
va_list ap;
int ret;
va_start(ap, dma);
ret = dma->ops->create_request(dma->data, &req,
AML_DMA_REQUEST_TYPE_MOVE, ap);
va_end(ap);
ret = dma->ops->wait_request(dma->data, &req);
return ret;
}
int aml_dma_move(struct aml_dma *dma, struct aml_area *dest,
struct aml_area *src, void *ptr, size_t size)
int aml_dma_async_move(struct aml_dma *dma, struct aml_dma_request *req, ...)
{
assert(dma != NULL);
assert(req != NULL);
va_list ap;
int ret;
va_start(ap, req);
ret = dma->ops->create_request(dma->data, req,
AML_DMA_REQUEST_TYPE_MOVE, ap);
va_end(ap);
return ret;
}
int aml_dma_cancel(struct aml_dma *dma, struct aml_dma_request *req)
{
assert(dma != NULL);
assert(req != NULL);
return dma->ops->destroy_request(dma->data, req);
}
int aml_dma_wait(struct aml_dma *dma, struct aml_dma_request *req)
{
return 0;
assert(dma != NULL);
assert(req != NULL);
return dma->ops->wait_request(dma->data, req);
}
#include <aml.h>
#include <assert.h>
#include <errno.h>
#include <sys/mman.h>
/*******************************************************************************
* Linux-backed, sequential dma
* The dma itself is organized into several different components
* - request types: copy or move
* - implementation of the request
* - user API (i.e. generic request creation and call)
* - how to init the dma
******************************************************************************/
/*******************************************************************************
* Requests:
******************************************************************************/
int aml_dma_request_linux_seq_copy(struct aml_dma_data *dma,
struct aml_dma_request_data *req)
{
assert(dma != NULL);
assert(req != NULL);
struct aml_dma_request_linux_seq_data *data =
(struct aml_dma_request_linux_seq_data*)req;
memcpy(data->dest, data->src, data->size);
return 0;
}
int aml_dma_request_linux_seq_move(struct aml_dma_data *dma,
struct aml_dma_request_data *req)
{
assert(dma != NULL);
assert(req != NULL);
struct aml_dma_request_linux_seq_data *data =
(struct aml_dma_request_linux_seq_data *)req;
int status[data->count];
int err;
err = move_pages(0, data->count, data->pages, data->nodes, status,
MPOL_MF_MOVE);
if(err)
{
perror("move_pages:");
return errno;
}
return 0;
}
struct aml_dma_request_ops aml_dma_request_linux_seq_ops = {
aml_dma_request_linux_seq_copy,
aml_dma_request_linux_seq_move,
};
int aml_dma_request_linux_seq_copy_init(struct aml_dma_request *r,
struct aml_tiling *dt,
void *dptr, int dtid,
struct aml_tiling *st,
void *sptr, int stid)
{
assert(r != NULL);
struct aml_dma_request_linux_seq_data *data =
(struct aml_dma_request_linux_seq_data *)r->data;
data->type = AML_DMA_REQUEST_TYPE_MOVE;
/* figure out pointers */
data->dest = aml_tiling_tilestart(dt, dptr, dtid);
data->src = aml_tiling_tilestart(st, sptr, stid);
data->size = aml_tiling_tilesize(st, stid);
/* TODO: assert size match */
return 0;
}
int aml_dma_request_linux_seq_copy_destroy(struct aml_dma_request *r)
{
assert(r != NULL);
return 0;
}
int aml_dma_request_linux_seq_move_init(struct aml_dma_request *r,
struct aml_area *darea,
struct aml_tiling *tiling,
void *startptr, int tileid)
{
assert(r != NULL);
struct aml_binding *binding;
struct aml_dma_request_linux_seq_data *data =
(struct aml_dma_request_linux_seq_data *)r->data;
data->type = AML_DMA_REQUEST_TYPE_MOVE;
aml_area_binding(darea, &binding);
data->count = aml_binding_nbpages(binding, tiling, startptr, tileid);
data->pages = calloc(data->count, sizeof(void *));
data->nodes = calloc(data->count, sizeof(int));
aml_binding_pages(binding, data->pages, tiling, startptr, tileid);
aml_binding_nodes(binding, data->nodes, tiling, startptr, tileid);
free(binding);
return 0;
}
int aml_dma_request_linux_seq_move_destroy(struct aml_dma_request *r)
{
struct aml_dma_request_linux_seq_data *data =
(struct aml_dma_request_linux_seq_data *)r->data;
free(data->pages);
free(data->nodes);
return 0;
}
/*******************************************************************************
* Internal functions
******************************************************************************/
int aml_dma_linux_seq_add_request(struct aml_dma_linux_seq_data *data,
struct aml_dma_request_linux_seq_data **req)
{
for(int i = 0; i < data->size; i++)
{
if(data->requests[i].type == AML_DMA_REQUEST_TYPE_INVALID)
{
*req = &data->requests[i];
return 0;
}
}
/* TODO: slow path, need to resize the array */
return 0;
}
int aml_dma_linux_seq_remove_request(struct aml_dma_linux_seq_data *data,
struct aml_dma_request_linux_seq_data **req)
{
/* TODO: assert that the pointer is in the right place */
(*req)->type = AML_DMA_REQUEST_TYPE_INVALID;
return 0;
}
struct aml_dma_linux_seq_ops aml_dma_linux_seq_inner_ops = {
aml_dma_linux_seq_add_request,
aml_dma_linux_seq_remove_request,
};
/*******************************************************************************
* Public API
******************************************************************************/
int aml_dma_linux_seq_create_request(struct aml_dma_data *d,
struct aml_dma_request *r,
int type, va_list ap)
{
assert(d != NULL);
assert(r != NULL);
struct aml_dma_linux_seq *dma =
(struct aml_dma_linux_seq *)d;
struct aml_dma_request_linux_seq_data *req;
/* find an available request slot */
dma->ops.add_request(&dma->data, &req);
r->data = (struct aml_dma_request_data *)req;
/* init the request */
r->ops = &aml_dma_request_linux_seq_ops;
if(type == AML_DMA_REQUEST_TYPE_COPY)
{
struct aml_tiling *dt, *st;
void *dptr, *sptr;
int dtid, stid;
dt = va_arg(ap, struct aml_tiling *);
dptr = va_arg(ap, void *);
dtid = va_arg(ap, int);
st = va_arg(ap, struct aml_tiling *);
sptr = va_arg(ap, void *);
stid = va_arg(ap, int);
aml_dma_request_linux_seq_copy_init(r, dt, dptr, dtid,
st, sptr, stid);
}
else if(type == AML_DMA_REQUEST_TYPE_MOVE)
{
struct aml_area *darea = va_arg(ap, struct aml_area *);
struct aml_tiling *st = va_arg(ap, struct aml_tiling *);
void *sptr = va_arg(ap, void *);
int stid = va_arg(ap, int);
aml_dma_request_linux_seq_move_init(r, darea, st, sptr, stid);
}
return 0;
}
int aml_dma_linux_seq_destroy_request(struct aml_dma_data *d,
struct aml_dma_request *r)
{
assert(d != NULL);
assert(r != NULL);
struct aml_dma_linux_seq *dma =
(struct aml_dma_linux_seq *)d;
struct aml_dma_request_linux_seq_data *req =
(struct aml_dma_request_linux_seq_data *)r->data;
if(req->type == AML_DMA_REQUEST_TYPE_COPY)
aml_dma_request_linux_seq_copy_destroy(r);
else if(req->type == AML_DMA_REQUEST_TYPE_MOVE)
aml_dma_request_linux_seq_move_destroy(r);
dma->ops.remove_request(&dma->data, &req);
return 0;
}
int aml_dma_linux_seq_wait_request(struct aml_dma_data *d,
struct aml_dma_request *r)
{
assert(d != NULL);
assert(r != NULL);
struct aml_dma_request_linux_seq_data *req =
(struct aml_dma_request_linux_seq_data *)r->data;
/* execute */
if(req->type == AML_DMA_REQUEST_TYPE_COPY)
r->ops->copy(d, r->data);
else if(req->type == AML_DMA_REQUEST_TYPE_MOVE)
r->ops->move(d, r->data);
/* destroy a completed request */
aml_dma_linux_seq_destroy_request(d, r);
return 0;
}
struct aml_dma_ops aml_dma_linux_seq_ops = {
aml_dma_linux_seq_create_request,
aml_dma_linux_seq_destroy_request,
aml_dma_linux_seq_wait_request,
};
/*******************************************************************************
* Init functions:
******************************************************************************/
int aml_dma_linux_seq_create(struct aml_dma **d, ...)
{
va_list ap;
struct aml_dma *ret = NULL;
intptr_t baseptr, dataptr;
va_start(ap, d);
/* alloc */
baseptr = (intptr_t) calloc(1, AML_DMA_LINUX_SEQ_ALLOCSIZE);
dataptr = baseptr + sizeof(struct aml_dma);
ret = (struct aml_dma *)baseptr;
ret->data = (struct aml_dma_data *)dataptr;
aml_dma_linux_seq_vinit(ret, ap);
va_end(ap);
*d = ret;
return 0;
}
int aml_dma_linux_seq_vinit(struct aml_dma *d, va_list ap)
{
d->ops = &aml_dma_linux_seq_ops;
struct aml_dma_linux_seq *dma = (struct aml_dma_linux_seq *)d->data;
dma->ops = aml_dma_linux_seq_inner_ops;
/* allocate request array */
dma->data.size = va_arg(ap, size_t);
dma->data.requests = calloc(dma->data.size,
sizeof(struct aml_dma_request_linux_seq_data));
for(int i = 0; i < dma->data.size; i++)
dma->data.requests[i].type = AML_DMA_REQUEST_TYPE_INVALID;
return 0;
}
int aml_dma_linux_seq_init(struct aml_dma *d, ...)
{
int err;
va_list ap;
va_start(ap, d);
err = aml_dma_linux_seq_vinit(d, ap);
va_end(ap);
return err;
}
int aml_dma_linux_seq_destroy(struct aml_dma *d)
{
struct aml_dma_linux_seq *dma = (struct aml_dma_linux_seq *)d->data;
free(dma->data.requests);
return 0;
}
......@@ -22,12 +22,15 @@ TILING_TESTS = tiling
BINDING_TESTS = binding
DMA_LINUX_TESTS = dma_linux_seq
# unit tests
UNIT_TESTS = $(ARENA_JEMALLOC_TESTS) \
$(TILING_TESTS) \
$(BINDING_TESTS) \
$(AREA_LINUX_TESTS) \
$(AREA_POSIX_TESTS)
$(AREA_POSIX_TESTS) \
$(DMA_LINUX_TESTS)
# fonctional tests
FUNC_TESTS = stream_add_pth stream_add_omp stream_vanilla
......
#include <aml.h>
#include <assert.h>
#define TILESIZE (2)
#define NBTILES (4)
int main(int argc, char *argv[])
{
AML_BINDING_SINGLE_DECL(binding);
AML_TILING_1D_DECL(tiling);
AML_ARENA_JEMALLOC_DECL(arena);
AML_AREA_LINUX_DECL(area);
AML_DMA_LINUX_SEQ_DECL(dma);
unsigned long nodemask[AML_NODEMASK_SZ];
void *dst, *src;
/* library initialization */
aml_init(&argc, &argv);
/* initialize all the supporting struct */
assert(!aml_binding_init(&binding, AML_BINDING_TYPE_SINGLE, 0));
assert(!aml_tiling_init(&tiling, AML_TILING_TYPE_1D, TILESIZE*PAGE_SIZE,
TILESIZE*PAGE_SIZE*NBTILES));
AML_NODEMASK_ZERO(nodemask);
AML_NODEMASK_SET(nodemask, 0);
assert(!aml_arena_jemalloc_init(&arena, AML_ARENA_JEMALLOC_TYPE_REGULAR));
assert(!aml_area_linux_init(&area,
AML_AREA_LINUX_MANAGER_TYPE_SINGLE,
AML_AREA_LINUX_MBIND_TYPE_REGULAR,
AML_AREA_LINUX_MMAP_TYPE_ANONYMOUS,
&arena, MPOL_BIND, nodemask));
size_t maxrequests = NBTILES;
assert(!aml_dma_linux_seq_init(&dma, maxrequests));
/* allocate some memory */
src = aml_area_malloc(&area, TILESIZE*PAGE_SIZE*NBTILES);
assert(src != NULL);
dst = aml_area_malloc(&area, TILESIZE*PAGE_SIZE*NBTILES);
assert(dst != NULL);
/* move some stuff by copy */
for(int i = 0; i < NBTILES; i++)
aml_dma_copy(&dma, &tiling, dst, i, &tiling, src, i);
/* now move it by pages */
for(int i = 0; i < NBTILES; i++)
aml_dma_move(&dma, &area, &tiling, src, i);
/* delete everything */
aml_dma_linux_seq_destroy(&dma);
aml_area_free(&area, dst);
aml_area_free(&area, src);
aml_area_linux_destroy(&area);
aml_tiling_destroy(&tiling, AML_TILING_TYPE_1D);
aml_binding_destroy(&binding, AML_BINDING_TYPE_SINGLE);
aml_finalize();
return 0;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment