diff -r 0200430c78db configure --- a/configure Thu Jan 25 23:36:05 2007 +0000 +++ b/configure Mon Jan 29 14:32:56 2007 -0800 @@ -310,7 +310,7 @@ ac_includes_default="\ #endif" ac_default_prefix=/usr -ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS build build_cpu build_vendor build_os host host_cpu host_vendor host_os target target_cpu target_vendor target_os AWK CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CPP EGREP INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA LN_S SET_MAKE RANLIB ac_ct_RANLIB LIBOBJS MSGFMT usrlibdir JOBS STATIC_LINK OWNER GROUP interface kerneldir missingkernel kernelvsn tmpdir COPTIMISE_FLAG CLDFLAGS LDDEPS LIB_SUFFIX DEBUG DM_LIB_VERSION COMPAT DMIOCTLS LOCALEDIR INTL_PACKAGE INTL DEVICE_UID DEVICE_GID DEVICE_MODE DMEVENTD PKGCONFIG LTLIBOBJS' +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS build build_cpu build_vendor build_os host host_cpu host_vendor host_os target target_cpu target_vendor target_os AWK CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CPP EGREP INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA LN_S SET_MAKE RANLIB ac_ct_RANLIB LIBOBJS MSGFMT usrlibdir JOBS STATIC_LINK OWNER GROUP interface kerneldir missingkernel kernelvsn tmpdir COPTIMISE_FLAG CLDFLAGS LDDEPS LIB_SUFFIX DEBUG DM_LIB_VERSION COMPAT DMIOCTLS LOCALEDIR INTL_PACKAGE INTL DEVICE_UID DEVICE_GID DEVICE_MODE DMEVENTD PKGCONFIG DMU LTLIBOBJS' ac_subst_files='' # Initialize some variables set by options. @@ -856,6 +856,7 @@ Optional Features: statically. Default is dynamic linking --disable-selinux Disable selinux support --enable-nls Enable Native Language Support + --disable-dmu Disable dm-userspace support Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] @@ -1445,7 +1446,8 @@ case "$host_os" in LDDEPS="$LDDEPS .export.sym" LIB_SUFFIX="so" DMIOCTLS="yes" - SELINUX="yes" ;; + SELINUX="yes" + DMU="yes" ;; darwin*) CFLAGS="$CFLAGS -no-cpp-precomp -fno-common" COPTIMISE_FLAG="-O2" @@ -1453,7 +1455,8 @@ case "$host_os" in LDDEPS="$LDDEPS" LIB_SUFFIX="dylib" DMIOCTLS="no" - SELINUX="no" ;; + SELINUX="no" + DMU="no" ;; esac ################################################################################ @@ -5963,6 +5966,26 @@ fi fi ################################################################################ +echo "$as_me:$LINENO: checking whether to enable dm-userspace" >&5 +echo $ECHO_N "checking whether to enable dm-userspace... $ECHO_C" >&6 +# Check whether --enable-dmu or --disable-dmu was given. +if test "${enable_dmu+set}" = set; then + enableval="$enable_dmu" + DMU=$enableval +fi; +echo "$as_me:$LINENO: result: $DMU" >&5 +echo "${ECHO_T}$DMU" >&6 + +if test "x${DMU}" = "xyes"; then + if test "x${missingkernel}" = xyes; then + { { echo "$as_me:$LINENO: error: \"Kernel source required to build dm-userspace tools\"" >&5 +echo "$as_me: error: \"Kernel source required to build dm-userspace tools\"" >&2;} + { (exit 1); exit 1; }; } + fi +fi + + +################################################################################ echo "$as_me:$LINENO: checking for kernel version" >&5 echo $ECHO_N "checking for kernel version... $ECHO_C" >&6 @@ -6044,6 +6067,7 @@ _ACEOF ################################################################################ + if test "$DMEVENTD" = yes; then @@ -6799,6 +6823,7 @@ s,@DEVICE_MODE@,$DEVICE_MODE,;t t s,@DEVICE_MODE@,$DEVICE_MODE,;t t s,@DMEVENTD@,$DMEVENTD,;t t s,@PKGCONFIG@,$PKGCONFIG,;t t +s,@DMU@,$DMU,;t t s,@LTLIBOBJS@,$LTLIBOBJS,;t t CEOF diff -r 0200430c78db configure.in --- a/configure.in Thu Jan 25 23:36:05 2007 +0000 +++ b/configure.in Mon Jan 29 14:32:56 2007 -0800 @@ -38,7 +38,8 @@ case "$host_os" in LDDEPS="$LDDEPS .export.sym" LIB_SUFFIX="so" DMIOCTLS="yes" - SELINUX="yes" ;; + SELINUX="yes" + DMU="yes" ;; darwin*) CFLAGS="$CFLAGS -no-cpp-precomp -fno-common" COPTIMISE_FLAG="-O2" @@ -46,7 +47,8 @@ case "$host_os" in LDDEPS="$LDDEPS" LIB_SUFFIX="dylib" DMIOCTLS="no" - SELINUX="no" ;; + SELINUX="no" + DMU="no" ;; esac ################################################################################ @@ -296,6 +298,20 @@ else else test -d "${kerneldir}" || { AC_MSG_WARN(kernel dir $kerneldir not found); missingkernel=yes ; } fi + +################################################################################ +dnl -- Disable dm-userspace +AC_MSG_CHECKING(whether to enable dm-userspace) +AC_ARG_ENABLE(dmu, [ --disable-dmu Disable dm-userspace support], +DMU=$enableval) +AC_MSG_RESULT($DMU) + +if test "x${DMU}" = "xyes"; then + if test "x${missingkernel}" = xyes; then + AC_ERROR("Kernel source required to build dm-userspace tools") + fi +fi + ################################################################################ dnl -- Kernel version string @@ -413,6 +429,7 @@ AC_SUBST(DEVICE_MODE) AC_SUBST(DEVICE_MODE) AC_SUBST(DMEVENTD) AC_SUBST(PKGCONFIG) +AC_SUBST(DMU) ################################################################################ dnl -- First and last lines should not contain files to generate in order to diff -r 0200430c78db lib/.exported_symbols --- a/lib/.exported_symbols Thu Jan 25 23:36:05 2007 +0000 +++ b/lib/.exported_symbols Mon Jan 29 14:32:56 2007 -0800 @@ -127,3 +127,26 @@ dm_report_field_uint32 dm_report_field_uint32 dm_report_field_uint64 dm_report_field_set_value +dmu_async_map +dmu_async_map_done +dmu_ctl_close +dmu_ctl_open +dmu_ctl_send_queue +dmu_events_pending +dmu_get_ctl_fd +dmu_kill_mapping +dmu_make_mapping +dmu_map_dup +dmu_map_get_block +dmu_map_get_id +dmu_map_is_write +dmu_map_set_block +dmu_map_set_copy_src_dev +dmu_map_set_dest_dev +dmu_map_set_offset +dmu_map_set_origin_block +dmu_map_set_sync +dmu_map_set_writable +dmu_process_events +dmu_register_map_done_handler +dmu_register_map_handler \ No newline at end of file diff -r 0200430c78db lib/Makefile.in --- a/lib/Makefile.in Thu Jan 25 23:36:05 2007 +0000 +++ b/lib/Makefile.in Mon Jan 29 14:32:56 2007 -0800 @@ -16,6 +16,7 @@ top_srcdir = @top_srcdir@ top_srcdir = @top_srcdir@ VPATH = @srcdir@ interface = @interface@ +kerneldir = @kerneldir@ SOURCES =\ datastruct/bitset.c \ @@ -30,6 +31,11 @@ SOURCES =\ $(interface)/libdm-iface.c INCLUDES = -I$(interface) + +ifeq ("@DMU@", "yes") + INCLUDES += -I$(kerneldir)/include + SOURCES += dmu.c +endif LIB_STATIC = $(interface)/libdevmapper.a diff -r 0200430c78db lib/libdevmapper.h --- a/lib/libdevmapper.h Thu Jan 25 23:36:05 2007 +0000 +++ b/lib/libdevmapper.h Mon Jan 29 14:32:56 2007 -0800 @@ -1,6 +1,7 @@ /* * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved. + * Copyright IBM Corp., 2006 * * This file is part of the device-mapper userspace tools. * @@ -27,6 +28,7 @@ #include #include #include +#include /***************************************************************** * The first section of this file provides direct access to the @@ -711,4 +713,58 @@ void dm_report_field_set_value(struct dm void dm_report_field_set_value(struct dm_report_field *field, const void *value, const void *sortvalue); + +/************** + * dm-userspace + **************/ + +struct dmu_context; +struct dmu_map_data; + +/* Returns 1 to allow IO to complete, 0 to delay */ +typedef int (*map_done_handler_t)(void *data, struct dmu_map_data *map_data); + +/* Returns 1 to map IO, -1 to fail IO, 0 to delay */ +typedef int (*map_req_handler_t)(void *data, struct dmu_map_data *map_data); + +/* High-level control operations */ +struct dmu_context *dmu_ctl_open(char *dev, int flags); +int dmu_ctl_close(struct dmu_context *ctx); +int dmu_ctl_send_queue(struct dmu_context *ctx); +void dmu_register_map_done_handler(struct dmu_context *ctx, + map_done_handler_t handler, + void *data); +void dmu_register_map_handler(struct dmu_context *ctx, + map_req_handler_t handler, + void *data); +int dmu_invalidate_block(struct dmu_context *ctx, uint64_t block); +int dmu_events_pending(struct dmu_context *ctx, unsigned int msec); +int dmu_process_events(struct dmu_context *ctx); +int dmu_get_ctl_fd(struct dmu_context *ctx); + +/* Map manipulation functions */ +void dmu_map_set_block(struct dmu_map_data *data, uint64_t block); +void dmu_map_set_origin_block(struct dmu_map_data *data, uint64_t block); +uint64_t dmu_map_get_block(struct dmu_map_data *data); +void dmu_map_set_offset(struct dmu_map_data *data, int64_t offset); +uint32_t dmu_map_get_id(struct dmu_map_data *data); +void dmu_map_set_dest_dev(struct dmu_map_data *data, dev_t dev); +void dmu_map_set_copy_src_dev(struct dmu_map_data *data, dev_t dev); +int dmu_map_is_write(struct dmu_map_data *data); +void dmu_map_set_sync(struct dmu_map_data *data); +void dmu_map_set_writable(struct dmu_map_data *data, int rw); +struct dmu_map_data *dmu_map_dup(struct dmu_map_data *data); + +/* Functions for submitting out-of-order events */ +int dmu_async_map(struct dmu_context *ctx, + struct dmu_map_data *data, + int fail); +int dmu_async_map_done(struct dmu_context *ctx, uint64_t id, int fail); + +/* Functions to manipulate the kernel map cache */ +int dmu_make_mapping(struct dmu_context *ctx, + struct dmu_map_data *data); +int dmu_kill_mapping(struct dmu_context *ctx, + struct dmu_map_data *data); + #endif /* LIB_DEVICE_MAPPER_H */ diff -r 0200430c78db lib/dmu.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/dmu.c Mon Jan 29 14:32:56 2007 -0800 @@ -0,0 +1,638 @@ +/* + * Copyright IBM Corp., 2006 + * Author: Dan Smith + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License. See the file COPYING in the main directory + * of this archive for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PAGE_SHIFT 12 +#define PAGE_SIZE (1UL << PAGE_SHIFT) + +#define MAX_MAJ_VER 0 +#define MAX_MIN_VER 1 + +#define DMU_MSG_DEBUG 0 + +struct uring { + uint32_t idx; + char *buf; + int size; +}; + +#if DMU_MSG_DEBUG +#define DPRINTF( s, arg... ) fprintf(stderr, s, ##arg) +#else +#define DPRINTF( s, arg... ) +#endif + +struct dmu_events { + map_done_handler_t map_done_fn; + map_req_handler_t map_fn; +}; + +struct dmu_event_data { + void *map_done_user_data; + void *map_user_data; +}; + +struct dmu_context { + int fd; + uint32_t id_ctr; + struct dmu_events events; + struct dmu_event_data event_data; + + struct uring ukring; + struct uring kuring; + + uint32_t pending; +}; + +struct dmu_map_data { + uint64_t org_block; + uint64_t block; + int64_t offset; + uint32_t id; + uint32_t flags; + dev_t dest_dev; + dev_t copy_src_dev; +}; + +void dmu_map_set_origin_block(struct dmu_map_data *data, uint64_t block) +{ + data->org_block = block; +} + +void dmu_map_set_writable(struct dmu_map_data *data, int rw) +{ + dmu_set_flag(&data->flags, DMU_FLAG_WR); +} + +void dmu_map_set_block(struct dmu_map_data *data, uint64_t block) +{ + data->block = block; +} + +uint64_t dmu_map_get_block(struct dmu_map_data *data) +{ + return data->block; +} + +void dmu_map_set_offset(struct dmu_map_data *data, int64_t offset) +{ + data->offset = offset; +} + +uint32_t dmu_map_get_id(struct dmu_map_data *data) +{ + return data->id; +} + +void dmu_map_set_dest_dev(struct dmu_map_data *data, dev_t dev) +{ + data->dest_dev = dev; +} + +void dmu_map_set_copy_src_dev(struct dmu_map_data *data, dev_t dev) +{ + data->copy_src_dev = dev; + dmu_set_flag(&data->flags, DMU_FLAG_COPY_FIRST); +} + +int dmu_map_is_write(struct dmu_map_data *data) +{ + return dmu_get_flag(&data->flags, DMU_FLAG_WR); +} + +void dmu_map_set_sync(struct dmu_map_data *data) +{ + dmu_set_flag(&data->flags, DMU_FLAG_SYNC); +} + +struct dmu_map_data *dmu_map_dup(struct dmu_map_data *data) +{ + struct dmu_map_data *dup; + + dup = malloc(sizeof(*dup)); + if (!dup) + return NULL; + + if (data) + memcpy(dup, data, sizeof(*dup)); + + return dup; +} + +/* + * Get the major/minor of the character control device that @dm_device + * has exported for us. We do this by looking at the device status + * string. + */ +static int get_dm_control_dev(char *dm_device, + unsigned *maj, unsigned *min) +{ + struct dm_task *task; + int ret; + void *next = NULL; + uint64_t start, length; + char *ttype = NULL, *params = NULL; + + task = dm_task_create(DM_DEVICE_STATUS); + + ret = dm_task_set_name(task, dm_device); + if (!ret) { + DPRINTF("Failed to set device-mapper target name\n"); + dm_task_destroy(task); + return -1; + } + + ret = dm_task_run(task); + if (!ret) { + DPRINTF("Failed to run device-mapper task\n"); + dm_task_destroy(task); + return -1; + } + + ret = 0; + do { + next = dm_get_next_target(task, next, &start, &length, + &ttype, ¶ms); + + if (strcmp(ttype, "userspace") == 0) { + ret = sscanf(params, "%x:%x", maj, min); + if (ret == 2) + break; + } + + } while (next); + + return 0; +} + +/* + * Create the character device node for our control channel + */ +static int make_device_node(unsigned major, unsigned minor) +{ + char path[256]; + + sprintf(path, "/dev/dmu%i", minor); + + return mknod(path, S_IFCHR, makedev(major, minor)); +} + +static char *dmu_get_ctl_device(char *dm_device) +{ + unsigned ctl_major, ctl_minor; + static char path[256]; + + if (get_dm_control_dev(dm_device, &ctl_major, &ctl_minor) < 0) + return NULL; + + if (ctl_major == 0) { + DPRINTF("Unable to get device number\n"); + return NULL; + } + + sprintf(path, "/dev/dmu%i", ctl_minor); + + if (access(path, R_OK | W_OK)) { + if (make_device_node(ctl_major, ctl_minor)) { + DPRINTF("Failed to create device node: %s", + strerror(errno)); + return NULL; + } + } + + return path; +} + +static void dmu_split_dev(dev_t dev, uint32_t *maj, uint32_t *min) +{ + *maj = (dev & 0xFF00) >> 8; + *min = (dev & 0x00FF); +} + +static inline void ring_index_inc(struct uring *ring) +{ + ring->idx = (ring->idx == DMU_MAX_EVENTS - 1) ? 0 : ring->idx + 1; +} + +static inline struct dmu_msg *head_ring_hdr(struct uring *ring) +{ + uint32_t pidx, off, pos; + + pidx = ring->idx / DMU_EVENT_PER_PAGE; + off = ring->idx % DMU_EVENT_PER_PAGE; + pos = pidx * PAGE_SIZE + off * sizeof(struct dmu_msg); + + return (struct dmu_msg *) (ring->buf + pos); +} + +/* Queue a message for sending */ +static int dmu_ctl_queue_msg(struct dmu_context *ctx, int type, void *msgbuf) +{ + struct dmu_msg *msg; + + msg = (struct dmu_msg *)head_ring_hdr(&ctx->ukring); + if (msg->hdr.status) { + DPRINTF("No room in ring, flushing...\n"); + dmu_ctl_send_queue(ctx); + + /* FIXME: Need a better way to wait for space to free up */ + usleep(50000); + + msg = (struct dmu_msg *)head_ring_hdr(&ctx->ukring); + if (msg->hdr.status) { + printf("#################### Still no room!\n"); + return -ENOMEM; + } + } + + msg->hdr.msg_type = type; + msg->hdr.id = ctx->id_ctr++; + + memcpy(&msg->payload, msgbuf, sizeof(msg->payload)); + + ring_index_inc(&ctx->ukring); + msg->hdr.status = 1; + ctx->pending++; + + return 1; +} + +/* Flush queue of messages to the kernel */ +int dmu_ctl_send_queue(struct dmu_context *ctx) +{ + int r; + + DPRINTF("Flushing outgoing queue\n"); + + r = write(ctx->fd, &r, 1); + + ctx->pending = 0; + + return r; +} + +static int check_version(char *dev) +{ + struct dm_task *task; + struct dm_versions *target, *last; + int ret; + + task = dm_task_create(DM_DEVICE_LIST_VERSIONS); + + ret = dm_task_set_name(task, dev); + if (!ret) { + DPRINTF("Failed to set device-mapper target name\n"); + dm_task_destroy(task); + return -1; + } + + ret = dm_task_run(task); + if (!ret) { + DPRINTF("Failed to run device-mapper task\n"); + dm_task_destroy(task); + return -1; + } + + target = dm_task_get_versions(task); + + do { + last = target; + + if (strcmp(target->name, "userspace") == 0) { + DPRINTF("%s version: %i.%i.%i\n", + target->name, + target->version[0], + target->version[1], + target->version[2]); + break; + } + + target = (void *) target + target->next; + } while (last != target); + + if (!target) { + DPRINTF("userspace target not found\n"); + return -1; + } + + if ((target->version[0] == MAX_MAJ_VER) && + (target->version[1] == MAX_MIN_VER)) + return 1; + else + return 0; /* Unsupported */ +} + +struct dmu_context *dmu_ctl_open(char *dev, int flags) +{ + int fd, r; + struct dmu_context *ctx = NULL; + char *ctl_dev; + char *ringbuf; + + r = check_version(dev); + if (r <= 0) { + return NULL; + } + + ctl_dev = dmu_get_ctl_device(dev); + if (ctl_dev == NULL) + return NULL; + else if (access(ctl_dev, R_OK | W_OK)) + return NULL; + + fd = open(ctl_dev, O_RDWR | flags); + if (fd < 0) + goto out; + + ctx = calloc(sizeof(*ctx), 1); + if (!ctx) + goto out; + + ctx->fd = fd; + ctx->id_ctr = 0; + memset(&ctx->events, 0, sizeof(ctx->events)); + memset(&ctx->event_data, 0, sizeof(ctx->event_data)); + + ringbuf = mmap(NULL, DMU_RING_SIZE * 2, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, 0); + if (ringbuf == MAP_FAILED) { + printf("fail to mmap, %m\n"); + return NULL; + } + + ctx->kuring.idx = ctx->ukring.idx = 0; + ctx->kuring.buf = ringbuf; + ctx->ukring.buf = ringbuf + DMU_RING_SIZE; + + return ctx; + + out: + if (ctx) + free(ctx); + + return NULL; +} + +int dmu_ctl_close(struct dmu_context *ctx) +{ + return close(ctx->fd); +} + +void dmu_register_map_done_handler(struct dmu_context *ctx, + map_done_handler_t handler, + void *data) +{ + ctx->events.map_done_fn = handler; + ctx->event_data.map_done_user_data = data; +} + +void dmu_register_map_handler(struct dmu_context *ctx, + map_req_handler_t handler, + void *data) +{ + ctx->events.map_fn = handler; + ctx->event_data.map_user_data = data; +} + +int dmu_make_mapping(struct dmu_context *ctx, + struct dmu_map_data *data) +{ + struct dmu_msg_make_mapping msg; + int r; + + msg.org_block = data->org_block; + msg.new_block = data->block; + msg.offset = data->offset; + dmu_split_dev(data->dest_dev, &msg.dev_maj, &msg.dev_min); + msg.flags = 0; + dmu_cpy_flag(&msg.flags, data->flags, DMU_FLAG_WR); + + r = dmu_ctl_queue_msg(ctx, DM_USERSPACE_MAKE_MAPPING, &msg); + + return r; +} + +int dmu_kill_mapping(struct dmu_context *ctx, + struct dmu_map_data *data) +{ + struct dmu_msg_make_mapping msg; + int r; + + msg.org_block = data->org_block; + + r = dmu_ctl_queue_msg(ctx, DM_USERSPACE_KILL_MAPPING, &msg); + + return r; +} + +int dmu_async_map_done(struct dmu_context *ctx, uint64_t id, int fail) +{ + struct dmu_msg_map_done msg; + int r; + + msg.org_block = 0; + msg.flags = 0; + msg.id_of_op = id; + + if (fail) + r = dmu_ctl_queue_msg(ctx, DM_USERSPACE_MAP_DONE_FAILED, &msg); + else + r = dmu_ctl_queue_msg(ctx, DM_USERSPACE_MAP_DONE, &msg); + + return r; +} + +int dmu_async_map(struct dmu_context *ctx, + struct dmu_map_data *data, + int fail) +{ + struct dmu_msg_map_response msg; + int r; + + msg.new_block = data->block; + msg.offset = data->offset; + msg.flags = data->flags; + msg.id_of_req = data->id; + + dmu_split_dev(data->copy_src_dev, &msg.src_maj, &msg.src_min); + dmu_split_dev(data->dest_dev, &msg.dst_maj, &msg.dst_min); + + if (fail) + r = dmu_ctl_queue_msg(ctx, DM_USERSPACE_MAP_FAILED, &msg); + else + r = dmu_ctl_queue_msg(ctx, DM_USERSPACE_MAP_BLOCK_RESP, &msg); + + return r; +} + +int dmu_events_pending(struct dmu_context *ctx, unsigned int msec) +{ + fd_set fds; + struct timeval tv; + + FD_ZERO(&fds); + FD_SET(ctx->fd, &fds); + + tv.tv_sec = msec / 1000; + tv.tv_usec = (msec % 1000) * 1000; + + if (select(ctx->fd + 1, &fds, NULL, NULL, &tv) < 0) + return 0; + + if (FD_ISSET(ctx->fd, &fds)) + return 1; + else + return 0; +} + +static int fire_map_req_event(struct dmu_context *ctx, + struct dmu_msg_map_request *req, + uint64_t id) +{ + struct dmu_map_data data; + int ret; + + if (!ctx->events.map_fn) + return 1; + + DPRINTF("Map event for %llu %c\n", + req->org_block, + dmu_get_flag(&req->flags, DMU_FLAG_WR) ? 'W':'R'); + + data.block = req->org_block; + data.offset = 0; + data.id = id; + data.flags = req->flags; + data.dest_dev = data.copy_src_dev = 0; + + dmu_clr_flag(&data.flags, DMU_FLAG_COPY_FIRST); + dmu_clr_flag(&data.flags, DMU_FLAG_SYNC); + + ret = ctx->events.map_fn(ctx->event_data.map_user_data, &data); + + if (ret != 0) { + /* If the handler returns 0, we assume they will + * complete the operation later + */ + dmu_async_map(ctx, &data, ret < 0); + DPRINTF("Mapped %llu\n", data.block); + } + + return ret != 0; +} + +static int fire_map_done_event(struct dmu_context *ctx, + struct dmu_msg_map_done *msg, + uint64_t id) +{ + struct dmu_map_data data; + int ret = 1; + + if (ctx->events.map_done_fn) { + data.block = msg->org_block; + data.offset = 0; + data.id = msg->id_of_op; + data.flags = msg->flags; + data.dest_dev = data.copy_src_dev = 0; + + ret = ctx->events.map_done_fn(ctx->event_data.map_done_user_data, + &data); + } + + if (ret > 0) { + /* If the handler returns 0, we assume they will + * complete the operation later + */ + dmu_async_map_done(ctx, msg->id_of_op, ret < 0); + DPRINTF("Completed %llu (%llu)\n", + msg->org_block, msg->id_of_op); + } + + return ret != 0; +} + +static int decode_message(struct dmu_context *ctx, int type, uint64_t id, + uint8_t *msg) +{ + switch (type) { + case DM_USERSPACE_MAP_BLOCK_REQ: + DPRINTF("Request event: %u\n", id); + return fire_map_req_event(ctx, + (struct dmu_msg_map_request *)msg, + id); + case DM_USERSPACE_MAP_DONE: + DPRINTF("Map Done event\n"); + return fire_map_done_event(ctx, + (struct dmu_msg_map_done *)msg, + id); + default: + printf("Unknown message type: %i\n", type); + return -1; /* Unknown message type */ + }; +} + +static int dmu_process_event(struct dmu_context *ctx) +{ + struct dmu_msg *msg; + int ret; + + msg = head_ring_hdr(&ctx->kuring); + if (!msg->hdr.status) + return -1; + + ret = decode_message(ctx, msg->hdr.msg_type, msg->hdr.id, + (uint8_t *)&msg->payload); + + msg->hdr.status = 0; + ring_index_inc(&ctx->kuring); + + return ret; +} + +int dmu_process_events(struct dmu_context *ctx) +{ + int ret, do_flush = 1; + uint32_t count; + + //DPRINTF("Processing events\n"); + + for (count = 0; count < DMU_MAX_EVENTS; count++) { + ret = dmu_process_event(ctx); + + if (ret > 0) + do_flush = 1; + } + + DPRINTF("Pending events: %u\n", ctx->pending); + if (ctx->pending) + dmu_ctl_send_queue(ctx); + + //DPRINTF("Finished processing events\n"); + + return 1; +} + +int dmu_get_ctl_fd(struct dmu_context *ctx) +{ + return ctx->fd; +}