Add simple ocf usage example
Signed-off-by: Robert Baldyga <robert.baldyga@intel.com>
This commit is contained in:
parent
8581520658
commit
95d35ef337
37
example/simple/Makefile
Normal file
37
example/simple/Makefile
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#
|
||||||
|
# Copyright(c) 2019 Intel Corporation
|
||||||
|
# SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||||
|
#
|
||||||
|
|
||||||
|
OCFDIR=../../
|
||||||
|
SRCDIR=src/
|
||||||
|
INCDIR=include/
|
||||||
|
|
||||||
|
SRC=$(shell find ${SRCDIR} -name \*.c)
|
||||||
|
OBJS = $(patsubst %.c, %.o, $(SRC))
|
||||||
|
PROGRAM=simple
|
||||||
|
|
||||||
|
CC = gcc
|
||||||
|
CFLAGS = -g -Wall -I${INCDIR} -I${SRCDIR}/ocf/env/
|
||||||
|
LDFLAGS = -lm -lz -pthread
|
||||||
|
|
||||||
|
all: sync
|
||||||
|
$(MAKE) $(PROGRAM)
|
||||||
|
|
||||||
|
$(PROGRAM): $(OBJS)
|
||||||
|
$(CC) $(LDFLAGS) -o $@ $^
|
||||||
|
|
||||||
|
sync:
|
||||||
|
@$(MAKE) -C ${OCFDIR} inc O=$(PWD)
|
||||||
|
@$(MAKE) -C ${OCFDIR} src O=$(PWD)
|
||||||
|
@$(MAKE) -C ${OCFDIR} env O=$(PWD) ENV=posix
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@rm -rf $(PROGRAM) $(OBJS)
|
||||||
|
|
||||||
|
distclean:
|
||||||
|
@rm -rf $(PROGRAM) $(OBJS)
|
||||||
|
@rm -rf src/ocf
|
||||||
|
@rm -rf include/ocf
|
||||||
|
|
||||||
|
.PHONY: all clean
|
331
example/simple/src/ctx.c
Normal file
331
example/simple/src/ctx.c
Normal file
@ -0,0 +1,331 @@
|
|||||||
|
/*
|
||||||
|
* Copyright(c) 2019 Intel Corporation
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <execinfo.h>
|
||||||
|
#include <ocf/ocf.h>
|
||||||
|
#include "ocf_env.h"
|
||||||
|
#include "data.h"
|
||||||
|
#include "dobj.h"
|
||||||
|
#include "ctx.h"
|
||||||
|
|
||||||
|
#define PAGE_SIZE 4096
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate structure representing data for io operations.
|
||||||
|
*/
|
||||||
|
ctx_data_t *ctx_data_alloc(uint32_t pages)
|
||||||
|
{
|
||||||
|
struct dobj_data *data;
|
||||||
|
|
||||||
|
data = malloc(sizeof(*data));
|
||||||
|
data->ptr = malloc(pages * PAGE_SIZE);
|
||||||
|
data->offset = 0;
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free data structure.
|
||||||
|
*/
|
||||||
|
void ctx_data_free(ctx_data_t *ctx_data)
|
||||||
|
{
|
||||||
|
struct dobj_data *data = ctx_data;
|
||||||
|
|
||||||
|
if (!data)
|
||||||
|
return;
|
||||||
|
|
||||||
|
free(data->ptr);
|
||||||
|
free(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function is supposed to set protection of data pages against swapping.
|
||||||
|
* Can be non-implemented if not needed.
|
||||||
|
*/
|
||||||
|
static int ctx_data_mlock(ctx_data_t *ctx_data)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Stop protecting data pages against swapping.
|
||||||
|
*/
|
||||||
|
static void ctx_data_munlock(ctx_data_t *ctx_data)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read data into flat memory buffer.
|
||||||
|
*/
|
||||||
|
static uint32_t ctx_data_rd(void *dst, ctx_data_t *src, uint32_t size)
|
||||||
|
{
|
||||||
|
struct dobj_data *data = src;
|
||||||
|
|
||||||
|
memcpy(dst, data->ptr + data->offset, size);
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write data from flat memory buffer.
|
||||||
|
*/
|
||||||
|
static uint32_t ctx_data_wr(ctx_data_t *dst, const void *src, uint32_t size)
|
||||||
|
{
|
||||||
|
struct dobj_data *data = dst;
|
||||||
|
|
||||||
|
memcpy(data->ptr + data->offset, src, size);
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fill data with zeros.
|
||||||
|
*/
|
||||||
|
static uint32_t ctx_data_zero(ctx_data_t *dst, uint32_t size)
|
||||||
|
{
|
||||||
|
struct dobj_data *data = dst;
|
||||||
|
|
||||||
|
memset(data->ptr + data->offset, 0, size);
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Perform seek operation on data.
|
||||||
|
*/
|
||||||
|
static uint32_t ctx_data_seek(ctx_data_t *dst, ctx_data_seek_t seek,
|
||||||
|
uint32_t offset)
|
||||||
|
{
|
||||||
|
struct dobj_data *data = dst;
|
||||||
|
|
||||||
|
switch (seek) {
|
||||||
|
case ctx_data_seek_begin:
|
||||||
|
data->offset = offset;
|
||||||
|
break;
|
||||||
|
case ctx_data_seek_current:
|
||||||
|
data->offset += offset;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy data from one structure to another.
|
||||||
|
*/
|
||||||
|
static uint64_t ctx_data_cpy(ctx_data_t *dst, ctx_data_t *src,
|
||||||
|
uint64_t to, uint64_t from, uint64_t bytes)
|
||||||
|
{
|
||||||
|
struct dobj_data *data_dst = dst;
|
||||||
|
struct dobj_data *data_src = src;
|
||||||
|
|
||||||
|
memcpy(data_dst->ptr + to, data_src->ptr + from, bytes);
|
||||||
|
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Perform secure erase of data (e.g. fill pages with zeros).
|
||||||
|
* Can be left non-implemented if not needed.
|
||||||
|
*/
|
||||||
|
static void ctx_data_secure_erase(ctx_data_t *ctx_data)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize queue thread. To keep this example simple we handle queues
|
||||||
|
* synchronously, thus it's left non-implemented.
|
||||||
|
*/
|
||||||
|
static int ctx_queue_init(ocf_queue_t q)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Trigger queue asynchronously. Made synchronous for simplicity.
|
||||||
|
*/
|
||||||
|
static inline void ctx_queue_kick_async(ocf_queue_t q)
|
||||||
|
{
|
||||||
|
ocf_queue_run(q);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Trigger queue synchronously. May be implemented as asynchronous as well,
|
||||||
|
* but in some environments kicking queue synchronously may reduce latency,
|
||||||
|
* so to take advantage of such situations OCF call synchronous variant of
|
||||||
|
* queue kick callback where possible.
|
||||||
|
*/
|
||||||
|
static void ctx_queue_kick_sync(ocf_queue_t q)
|
||||||
|
{
|
||||||
|
ocf_queue_run(q);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Stop queue thread. To keep this example simple we handle queues
|
||||||
|
* synchronously, thus it's left non-implemented.
|
||||||
|
*/
|
||||||
|
static void ctx_queue_stop(ocf_queue_t q)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize cleaner thread. Cleaner thread is left non-implemented,
|
||||||
|
* to keep this example as simple as possible.
|
||||||
|
*/
|
||||||
|
static int ctx_cleaner_init(ocf_cleaner_t c)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Stop cleaner thread. Cleaner thread is left non-implemented, to keep
|
||||||
|
* this example as simple as possible.
|
||||||
|
*/
|
||||||
|
static void ctx_cleaner_stop(ocf_cleaner_t c)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize metadata updater thread. Metadata updater thread is left
|
||||||
|
* non-implemented to keep this example as simple as possible.
|
||||||
|
*/
|
||||||
|
static int ctx_metadata_updater_init(ocf_metadata_updater_t mu)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Kick metadata updater thread. Metadata updater thread is left
|
||||||
|
* non-implemented to keep this example as simple as possible.
|
||||||
|
*/
|
||||||
|
static void ctx_metadata_updater_kick(ocf_metadata_updater_t mu)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Stop metadata updater thread. Metadata updater thread is left
|
||||||
|
* non-implemented to keep this example as simple as possible.
|
||||||
|
*/
|
||||||
|
static void ctx_metadata_updater_stop(ocf_metadata_updater_t mu)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This structure describes context ops. They are splitted into few categories:
|
||||||
|
* - data ops, providing context specific data handing interface,
|
||||||
|
* - queue ops, providing interface for starting, stoping and kicking
|
||||||
|
* queue thread in both synchronous and asynchronous way,
|
||||||
|
* - cleaner ops, providing interface to start and stop clener thread,
|
||||||
|
* - metadata updater ops, providing interface for starting, stoping
|
||||||
|
* and kicking metadata updater thread.
|
||||||
|
*/
|
||||||
|
static const struct ocf_ctx_ops ctx_ops = {
|
||||||
|
.name = "OCF Example",
|
||||||
|
|
||||||
|
.data_alloc = ctx_data_alloc,
|
||||||
|
.data_free = ctx_data_free,
|
||||||
|
.data_mlock = ctx_data_mlock,
|
||||||
|
.data_munlock = ctx_data_munlock,
|
||||||
|
.data_rd = ctx_data_rd,
|
||||||
|
.data_wr = ctx_data_wr,
|
||||||
|
.data_zero = ctx_data_zero,
|
||||||
|
.data_seek = ctx_data_seek,
|
||||||
|
.data_cpy = ctx_data_cpy,
|
||||||
|
.data_secure_erase = ctx_data_secure_erase,
|
||||||
|
|
||||||
|
.queue_init = ctx_queue_init,
|
||||||
|
.queue_kick_sync = ctx_queue_kick_sync,
|
||||||
|
.queue_kick = ctx_queue_kick_async,
|
||||||
|
.queue_stop = ctx_queue_stop,
|
||||||
|
|
||||||
|
.cleaner_init = ctx_cleaner_init,
|
||||||
|
.cleaner_stop = ctx_cleaner_stop,
|
||||||
|
|
||||||
|
.metadata_updater_init = ctx_metadata_updater_init,
|
||||||
|
.metadata_updater_kick = ctx_metadata_updater_kick,
|
||||||
|
.metadata_updater_stop = ctx_metadata_updater_stop,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Function prividing interface for printing to log used by OCF internals.
|
||||||
|
* It can handle differently messages at varous log levels.
|
||||||
|
*/
|
||||||
|
static int ctx_log_printf(const struct ocf_logger *logger,
|
||||||
|
ocf_logger_lvl_t lvl, const char *fmt, va_list args)
|
||||||
|
{
|
||||||
|
FILE *lfile = stdout;
|
||||||
|
|
||||||
|
if (lvl > log_info)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (lvl <= log_warn)
|
||||||
|
lfile = stderr;
|
||||||
|
|
||||||
|
return vfprintf(lfile, fmt, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CTX_LOG_TRACE_DEPTH 16
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Function prividing interface for printing current stack. Used for debugging,
|
||||||
|
* and for providing additional information in log in case of errors.
|
||||||
|
*/
|
||||||
|
static int ctx_log_dump_stack(const struct ocf_logger *logger)
|
||||||
|
{
|
||||||
|
void *trace[CTX_LOG_TRACE_DEPTH];
|
||||||
|
char **messages = NULL;
|
||||||
|
int i, size;
|
||||||
|
|
||||||
|
size = backtrace(trace, CTX_LOG_TRACE_DEPTH);
|
||||||
|
messages = backtrace_symbols(trace, size);
|
||||||
|
printf("[stack trace]>>>\n");
|
||||||
|
for (i = 0; i < size; ++i)
|
||||||
|
printf("%s\n", messages[i]);
|
||||||
|
printf("<<<[stack trace]\n");
|
||||||
|
free(messages);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Structure containng logger ops.
|
||||||
|
*/
|
||||||
|
static const struct ocf_logger logger = {
|
||||||
|
.printf = ctx_log_printf,
|
||||||
|
.dump_stack = ctx_log_dump_stack,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Function initializing context. Prepares context, sets logger and
|
||||||
|
* registers data object type.
|
||||||
|
*/
|
||||||
|
int ctx_init(ocf_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = ocf_ctx_init(ctx, &ctx_ops);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ocf_ctx_set_logger(*ctx, &logger);
|
||||||
|
|
||||||
|
ret = dobj_init(*ctx);
|
||||||
|
if (ret) {
|
||||||
|
dobj_cleanup(*ctx);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Function cleaning up context. Unregisters data object type and
|
||||||
|
* deinitializes context.
|
||||||
|
*/
|
||||||
|
void ctx_cleanup(ocf_ctx_t ctx)
|
||||||
|
{
|
||||||
|
dobj_cleanup(ctx);
|
||||||
|
ocf_ctx_exit(ctx);
|
||||||
|
}
|
19
example/simple/src/ctx.h
Normal file
19
example/simple/src/ctx.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright(c) 2019 Intel Corporation
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __CTX_H__
|
||||||
|
#define __CTX_H__
|
||||||
|
|
||||||
|
#include <ocf/ocf.h>
|
||||||
|
|
||||||
|
#define OBJ_TYPE 1
|
||||||
|
|
||||||
|
ctx_data_t *ctx_data_alloc(uint32_t pages);
|
||||||
|
void ctx_data_free(ctx_data_t *ctx_data);
|
||||||
|
|
||||||
|
int ctx_init(ocf_ctx_t *ocf_ctx);
|
||||||
|
void ctx_cleanup(ocf_ctx_t ctx);
|
||||||
|
|
||||||
|
#endif
|
14
example/simple/src/data.h
Normal file
14
example/simple/src/data.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
* Copyright(c) 2019 Intel Corporation
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __DATA_H__
|
||||||
|
#define __DATA_H__
|
||||||
|
|
||||||
|
struct dobj_data {
|
||||||
|
void *ptr;
|
||||||
|
int offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
168
example/simple/src/dobj.c
Normal file
168
example/simple/src/dobj.c
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
/*
|
||||||
|
* Copyright(c) 2019 Intel Corporation
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ocf/ocf.h>
|
||||||
|
#include "dobj.h"
|
||||||
|
#include "data.h"
|
||||||
|
#include "ctx.h"
|
||||||
|
|
||||||
|
#define DOBJ_SIZE 200*1024*1024
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In open() function we store uuid data as object name (for debug messages)
|
||||||
|
* and allocate 200 MiB of memory to simulate backend storage device.
|
||||||
|
*/
|
||||||
|
static int dobj_open(ocf_data_obj_t obj)
|
||||||
|
{
|
||||||
|
const struct ocf_data_obj_uuid *uuid = ocf_dobj_get_uuid(obj);
|
||||||
|
struct dobj *dobj = ocf_dobj_get_priv(obj);
|
||||||
|
|
||||||
|
dobj->name = ocf_uuid_to_str(uuid);
|
||||||
|
dobj->mem = malloc(DOBJ_SIZE);
|
||||||
|
|
||||||
|
printf("DOBJ OPEN: (name: %s)\n", dobj->name);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In close() function we just free memory allocated in open().
|
||||||
|
*/
|
||||||
|
static void dobj_close(ocf_data_obj_t obj)
|
||||||
|
{
|
||||||
|
struct dobj *dobj = ocf_dobj_get_priv(obj);
|
||||||
|
|
||||||
|
printf("DOBJ CLOSE: (name: %s)\n", dobj->name);
|
||||||
|
free(dobj->mem);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In submit_io() function we simulate read or write to backend storage device
|
||||||
|
* by doing memcpy() to or from previously allocated memory buffer.
|
||||||
|
*/
|
||||||
|
static void dobj_submit_io(struct ocf_io *io)
|
||||||
|
{
|
||||||
|
struct dobj_data *data;
|
||||||
|
struct dobj *dobj;
|
||||||
|
|
||||||
|
data = ocf_io_get_data(io);
|
||||||
|
dobj = ocf_dobj_get_priv(io->obj);
|
||||||
|
|
||||||
|
if (io->dir == OCF_WRITE) {
|
||||||
|
memcpy(dobj->mem + io->addr,
|
||||||
|
data->ptr + data->offset, io->bytes);
|
||||||
|
} else {
|
||||||
|
memcpy(data->ptr + data->offset,
|
||||||
|
dobj->mem + io->addr, io->bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("DOBJ: (name: %s), IO: (dir: %s, addr: %ld, bytes: %d)\n",
|
||||||
|
dobj->name, io->dir == OCF_READ ? "read" : "write",
|
||||||
|
io->addr, io->bytes);
|
||||||
|
|
||||||
|
io->end(io, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We don't need to implement submit_flush(). Just complete io with success.
|
||||||
|
*/
|
||||||
|
static void dobj_submit_flush(struct ocf_io *io)
|
||||||
|
{
|
||||||
|
io->end(io, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We don't need to implement submit_discard(). Just complete io with success.
|
||||||
|
*/
|
||||||
|
static void dobj_submit_discard(struct ocf_io *io)
|
||||||
|
{
|
||||||
|
io->end(io, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Let's set maximum io size to 128 KiB.
|
||||||
|
*/
|
||||||
|
static unsigned int dobj_get_max_io_size(ocf_data_obj_t obj)
|
||||||
|
{
|
||||||
|
return 128 * 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return data object size.
|
||||||
|
*/
|
||||||
|
static uint64_t dobj_get_length(ocf_data_obj_t obj)
|
||||||
|
{
|
||||||
|
return DOBJ_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In set_data() we just assing data and offset to io.
|
||||||
|
*/
|
||||||
|
static int dobj_io_set_data(struct ocf_io *io, ctx_data_t *data,
|
||||||
|
uint32_t offset)
|
||||||
|
{
|
||||||
|
struct dobj_io *dobj_io = ocf_io_get_priv(io);
|
||||||
|
|
||||||
|
dobj_io->data = data;
|
||||||
|
dobj_io->offset = offset;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In get_data() return data stored in io.
|
||||||
|
*/
|
||||||
|
static ctx_data_t *dobj_io_get_data(struct ocf_io *io)
|
||||||
|
{
|
||||||
|
struct dobj_io *dobj_io = ocf_io_get_priv(io);
|
||||||
|
|
||||||
|
return dobj_io->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This structure contains data object properties. It describes data object
|
||||||
|
* type, which can be later instantiated as backend storage object for cache
|
||||||
|
* or core.
|
||||||
|
*/
|
||||||
|
const struct ocf_data_obj_properties dobj_properties = {
|
||||||
|
.name = "Example dobj",
|
||||||
|
.io_priv_size = sizeof(struct dobj_io),
|
||||||
|
.dobj_priv_size = sizeof(struct dobj),
|
||||||
|
.caps = {
|
||||||
|
.atomic_writes = 0,
|
||||||
|
},
|
||||||
|
.ops = {
|
||||||
|
.open = dobj_open,
|
||||||
|
.close = dobj_close,
|
||||||
|
.submit_io = dobj_submit_io,
|
||||||
|
.submit_flush = dobj_submit_flush,
|
||||||
|
.submit_discard = dobj_submit_discard,
|
||||||
|
.get_max_io_size = dobj_get_max_io_size,
|
||||||
|
.get_length = dobj_get_length,
|
||||||
|
},
|
||||||
|
.io_ops = {
|
||||||
|
.set_data = dobj_io_set_data,
|
||||||
|
.get_data = dobj_io_get_data,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function registers data object type in OCF context.
|
||||||
|
* It should be called just after context initialization.
|
||||||
|
*/
|
||||||
|
int dobj_init(ocf_ctx_t ocf_ctx)
|
||||||
|
{
|
||||||
|
return ocf_ctx_register_data_obj_type(ocf_ctx, OBJ_TYPE,
|
||||||
|
&dobj_properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function unregisters data object type in OCF context.
|
||||||
|
* It should be called just before context cleanup.
|
||||||
|
*/
|
||||||
|
void dobj_cleanup(ocf_ctx_t ocf_ctx)
|
||||||
|
{
|
||||||
|
ocf_ctx_unregister_data_obj_type(ocf_ctx, OBJ_TYPE);
|
||||||
|
}
|
27
example/simple/src/dobj.h
Normal file
27
example/simple/src/dobj.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright(c) 2019 Intel Corporation
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __DOBJ_H__
|
||||||
|
#define __DOBJ_H__
|
||||||
|
|
||||||
|
#include <ocf/ocf.h>
|
||||||
|
#include "ocf_env.h"
|
||||||
|
#include "ctx.h"
|
||||||
|
#include "data.h"
|
||||||
|
|
||||||
|
struct dobj_io {
|
||||||
|
struct dobj_data *data;
|
||||||
|
uint32_t offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dobj {
|
||||||
|
uint8_t *mem;
|
||||||
|
const char *name;
|
||||||
|
};
|
||||||
|
|
||||||
|
int dobj_init(ocf_ctx_t ocf_ctx);
|
||||||
|
void dobj_cleanup(ocf_ctx_t ocf_ctx);
|
||||||
|
|
||||||
|
#endif
|
217
example/simple/src/main.c
Normal file
217
example/simple/src/main.c
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
/*
|
||||||
|
* Copyright(c) 2019 Intel Corporation
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <ocf/ocf.h>
|
||||||
|
#include "data.h"
|
||||||
|
#include "ctx.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Helper function for error handling.
|
||||||
|
*/
|
||||||
|
void error(char *msg)
|
||||||
|
{
|
||||||
|
printf("ERROR: %s", msg);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Function starting cache and attaching cache device.
|
||||||
|
*/
|
||||||
|
int initialize_cache(ocf_ctx_t ctx, ocf_cache_t *cache)
|
||||||
|
{
|
||||||
|
struct ocf_mngt_cache_config cache_cfg = { };
|
||||||
|
struct ocf_mngt_cache_device_config device_cfg = { };
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Cache configuration */
|
||||||
|
cache_cfg.backfill.max_queue_size = 65536;
|
||||||
|
cache_cfg.backfill.queue_unblock_size = 60000;
|
||||||
|
cache_cfg.cache_line_size = ocf_cache_line_size_4;
|
||||||
|
cache_cfg.cache_mode = ocf_cache_mode_wt;
|
||||||
|
cache_cfg.metadata_volatile = true;
|
||||||
|
cache_cfg.io_queues = 1;
|
||||||
|
cache_cfg.name = "cache1";
|
||||||
|
|
||||||
|
/* Cache deivce (data object) configuration */
|
||||||
|
device_cfg.data_obj_type = OBJ_TYPE;
|
||||||
|
device_cfg.cache_line_size = ocf_cache_line_size_4;
|
||||||
|
ret = ocf_uuid_set_str(&device_cfg.uuid, "cache");
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Start cache */
|
||||||
|
ret = ocf_mngt_cache_start(ctx, cache, &cache_cfg);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Attach data object to cache */
|
||||||
|
ret = ocf_mngt_cache_attach(*cache, &device_cfg);
|
||||||
|
if (ret) {
|
||||||
|
ocf_mngt_cache_stop(*cache);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Function adding cache to core.
|
||||||
|
*/
|
||||||
|
int initialize_core(ocf_cache_t cache, ocf_core_t *core)
|
||||||
|
{
|
||||||
|
struct ocf_mngt_core_config core_cfg = { };
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Core configuration */
|
||||||
|
core_cfg.data_obj_type = OBJ_TYPE;
|
||||||
|
core_cfg.name = "core1";
|
||||||
|
ret = ocf_uuid_set_str(&core_cfg.uuid, "core");
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Add core to cache */
|
||||||
|
return ocf_mngt_cache_add_core(cache, core, &core_cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Callback function called when write completes.
|
||||||
|
*/
|
||||||
|
void complete_write(struct ocf_io *io, int error)
|
||||||
|
{
|
||||||
|
struct dobj_data *data = ocf_io_get_data(io);
|
||||||
|
|
||||||
|
printf("WRITE COMPLETE: (error: %d)\n", error);
|
||||||
|
|
||||||
|
/* Free data buffer and io */
|
||||||
|
ctx_data_free(data);
|
||||||
|
ocf_io_put(io);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Callback function called when read completes.
|
||||||
|
*/
|
||||||
|
void complete_read(struct ocf_io *io, int error)
|
||||||
|
{
|
||||||
|
struct dobj_data *data = ocf_io_get_data(io);
|
||||||
|
|
||||||
|
printf("WRITE COMPLETE (error: %d)\n", error);
|
||||||
|
printf("DATA: \"%s\"\n", (char *)data->ptr);
|
||||||
|
|
||||||
|
/* Free data buffer and io */
|
||||||
|
ctx_data_free(data);
|
||||||
|
ocf_io_put(io);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Wrapper function for io submition.
|
||||||
|
*/
|
||||||
|
int submit_io(ocf_core_t core, struct dobj_data *data,
|
||||||
|
uint64_t addr, uint64_t len, int dir, ocf_end_io_t cmpl)
|
||||||
|
{
|
||||||
|
struct ocf_io *io;
|
||||||
|
|
||||||
|
/* Allocate new io */
|
||||||
|
io = ocf_core_new_io(core);
|
||||||
|
if (!io)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
/* Setup io address, lenght, direction, flags and ioclass */
|
||||||
|
ocf_io_configure(io, addr, len, dir, 0, 0);
|
||||||
|
/* Assign data to io */
|
||||||
|
ocf_io_set_data(io, data, 0);
|
||||||
|
/* Setup completion function */
|
||||||
|
ocf_io_set_cmpl(io, NULL, NULL, cmpl);
|
||||||
|
/* Submit io */
|
||||||
|
ocf_core_submit_io(io);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function simulates actual business logic.
|
||||||
|
*
|
||||||
|
* It performs following steps:
|
||||||
|
* 1. Allocate data buffer for write and write it with example data.
|
||||||
|
* 2. Allocate new io, configure it for write, setup completion callback
|
||||||
|
* and perform write to the core.
|
||||||
|
* 3. Wait for write io completion (write is handled synchronosly, so no
|
||||||
|
* actual wait is needed, but in real life we would need to use some
|
||||||
|
* synchronization to be sure, that completion function has been already
|
||||||
|
* called). Alternatively we could issue read io from write completion
|
||||||
|
* callback.
|
||||||
|
* 4. Allocate data buffer for read.
|
||||||
|
* 5. Allocate new io, configure it for read, setup completion callback
|
||||||
|
* and perform read from the core, from the same address where data
|
||||||
|
* was previously written.
|
||||||
|
* 6. Print example data in read completion callback.
|
||||||
|
*
|
||||||
|
* Data buffers and ios are freed in completion callbacks, so there is no
|
||||||
|
* need to handle freeing in this function.
|
||||||
|
*/
|
||||||
|
void perform_workload(ocf_core_t core)
|
||||||
|
{
|
||||||
|
struct dobj_data *data1, *data2;
|
||||||
|
|
||||||
|
/* Allocate data buffer and fill it with example data */
|
||||||
|
data1 = ctx_data_alloc(1);
|
||||||
|
if (!data1)
|
||||||
|
error("Unable to allocate data1\n");
|
||||||
|
strcpy(data1->ptr, "This is some test data");
|
||||||
|
/* Prepare and submit write IO to the core */
|
||||||
|
submit_io(core, data1, 0, 512, OCF_WRITE, complete_write);
|
||||||
|
/* After write completes, complete_write() callback will be called. */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Here we would need to wait until write completes to be sure, that
|
||||||
|
* performing read we retrive written data.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Allocate data buffer for read */
|
||||||
|
data2 = ctx_data_alloc(1);
|
||||||
|
if (!data2)
|
||||||
|
error("Unable to allocate data2\n");
|
||||||
|
/* Prepare and submit read IO to the core */
|
||||||
|
submit_io(core, data2, 0, 512, OCF_READ, complete_read);
|
||||||
|
/* After read completes, complete_read() callback will be called,
|
||||||
|
* where we print our example data to stdout.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
ocf_ctx_t ctx;
|
||||||
|
ocf_cache_t cache1;
|
||||||
|
ocf_core_t core1;
|
||||||
|
|
||||||
|
/* Initialize OCF context */
|
||||||
|
if (ctx_init(&ctx))
|
||||||
|
error("Unable to initialize context\n");
|
||||||
|
|
||||||
|
/* Start cache */
|
||||||
|
if (initialize_cache(ctx, &cache1))
|
||||||
|
error("Unable to start cache\n");
|
||||||
|
|
||||||
|
/* Add core */
|
||||||
|
if (initialize_core(cache1, &core1))
|
||||||
|
error("Unable to add core\n");
|
||||||
|
|
||||||
|
/* Do some actual io operations */
|
||||||
|
perform_workload(core1);
|
||||||
|
|
||||||
|
/* Remove core from cache */
|
||||||
|
if (ocf_mngt_cache_remove_core(cache1, ocf_core_get_id(core1), false))
|
||||||
|
error("Unable to remove core\n");
|
||||||
|
|
||||||
|
/* Stop cache */
|
||||||
|
if (ocf_mngt_cache_stop(cache1))
|
||||||
|
error("Unable to stop cache\n");
|
||||||
|
|
||||||
|
/* Deinitialize context */
|
||||||
|
ctx_cleanup(ctx);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user