[dm-devel] [PATCH] Add dm-userspace support to libdevmapper
Dan Smith
danms at us.ibm.com
Thu Jul 27 21:49:41 UTC 2006
This patch against device-mapper-1.02.07 adds dm-userspace support to
libdevmapper. Comments on the interface it provides would be
appreciated. The patch also adds an example program to contrib/ that
demonstrates simple usage of the dm-userspace functions provided.
I added a copyright line at the top of libdevmapper.h, as is customary
in the Xen community. Please advise if this is not appropriate.
Signed-off-by: Dan Smith <danms at us.ibm.com>
Signed-off-by: Ryan Grimm <grimm at us.ibm.com>
diff -r 625e2e3552be -r 1a82e8ce64bc configure
--- a/configure Mon Jul 17 09:10:04 2006 -0700
+++ b/configure Thu Jul 27 10:30:09 2006 -0700
@@ -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]
@@ -1443,7 +1444,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"
@@ -1451,7 +1453,8 @@ case "$host_os" in
LDDEPS="$LDDEPS"
LIB_SUFFIX="dylib"
DMIOCTLS="no"
- SELINUX="no" ;;
+ SELINUX="no"
+ DMU="no" ;;
esac
################################################################################
@@ -5885,6 +5888,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
@@ -5961,6 +5984,7 @@ fi
################################################################################
+
@@ -6672,6 +6696,7 @@ s, at DEVICE_MODE@,$DEVICE_MODE,;t t
s, at DEVICE_MODE@,$DEVICE_MODE,;t t
s, at DMEVENTD@,$DMEVENTD,;t t
s, at PKGCONFIG@,$PKGCONFIG,;t t
+s, at DMU@,$DMU,;t t
s, at LTLIBOBJS@,$LTLIBOBJS,;t t
CEOF
diff -r 625e2e3552be -r 1a82e8ce64bc configure.in
--- a/configure.in Mon Jul 17 09:10:04 2006 -0700
+++ b/configure.in Thu Jul 27 10:30:09 2006 -0700
@@ -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
################################################################################
@@ -291,6 +293,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
@@ -383,6 +399,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 625e2e3552be -r 1a82e8ce64bc lib/.exported_symbols
--- a/lib/.exported_symbols Mon Jul 17 09:10:04 2006 -0700
+++ b/lib/.exported_symbols Thu Jul 27 10:30:09 2006 -0700
@@ -109,3 +109,21 @@ dm_hash_get_next
dm_hash_get_next
dm_set_selinux_context
dm_task_set_geometry
+dmu_ctl_open
+dmu_ctl_close
+dmu_ctl_send_queue
+dmu_register_status_handler
+dmu_register_map_handler
+dmu_invalidate_block
+dmu_sync_complete
+dmu_events_pending
+dmu_process_events
+dmu_map_set_block
+dmu_map_get_block
+dmu_map_set_offset
+dmu_map_get_id
+dmu_map_set_dest_dev
+dmu_map_set_copy_src_dev
+dmu_map_set_writable
+dmu_map_is_write
+dmu_map_set_sync
diff -r 625e2e3552be -r 1a82e8ce64bc lib/Makefile.in
--- a/lib/Makefile.in Mon Jul 17 09:10:04 2006 -0700
+++ b/lib/Makefile.in Thu Jul 27 10:30:09 2006 -0700
@@ -16,6 +16,7 @@ top_srcdir = @top_srcdir@
top_srcdir = @top_srcdir@
VPATH = @srcdir@
interface = @interface@
+kerneldir = @kerneldir@
SOURCES =\
datastruct/bitset.c \
@@ -28,6 +29,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 625e2e3552be -r 1a82e8ce64bc lib/libdevmapper.h
--- a/lib/libdevmapper.h Mon Jul 17 09:10:04 2006 -0700
+++ b/lib/libdevmapper.h Thu Jul 27 10:30:09 2006 -0700
@@ -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 (C) International Business Machines Corp., 2006
*
* This file is part of the device-mapper userspace tools.
*
@@ -582,4 +583,49 @@ struct dm_hash_node *dm_hash_get_next(st
*********/
int dm_set_selinux_context(const char *path, mode_t mode);
+
+/**************
+ * dm-userspace
+ **************/
+
+enum {
+ DMU_STATUS_UNKNOWN = 0,
+ DMU_STATUS_BLOCK_FLUSHED,
+ DMU_STATUS_INVAL_COMPLETE,
+ DMU_STATUS_INVAL_FAILED,
+ DMU_STATUS_SYNC_COMPLETE
+};
+
+struct dmu_context;
+struct dmu_map_data;
+
+typedef int (*status_handler)(void *data, uint32_t id, uint32_t status);
+typedef int (*map_req_handler)(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_status_handler(struct dmu_context *ctx,
+ status_handler handler,
+ void *data);
+void dmu_register_map_handler(struct dmu_context *ctx,
+ map_req_handler handler,
+ void *data);
+int dmu_invalidate_block(struct dmu_context *ctx, uint64_t block);
+int dmu_sync_complete(struct dmu_context *ctx, uint32_t id);
+int dmu_events_pending(struct dmu_context *ctx, unsigned int msec);
+int dmu_process_events(struct dmu_context *ctx);
+
+/* Map manipulation functions */
+void dmu_map_set_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);
+void dmu_map_set_writable(struct dmu_map_data *data, int writable);
+int dmu_map_is_write(struct dmu_map_data *data);
+void dmu_map_set_sync(struct dmu_map_data *data);
+
#endif /* LIB_DEVICE_MAPPER_H */
diff -r 625e2e3552be -r 1a82e8ce64bc .hgtags
--- /dev/null Thu Jan 1 00:00:00 1970 +0000
+++ b/.hgtags Thu Jul 27 10:30:09 2006 -0700
@@ -0,0 +1,2 @@
+625e2e3552bec3a9efd9f7f87c6f5fc41d7cacb8 ORIG
+3bf96d9f87d98ee8c402fb4cf3ed503cc0577048 PRE_IO_COMP
diff -r 625e2e3552be -r 1a82e8ce64bc contrib/dmu-example.c
--- /dev/null Thu Jan 1 00:00:00 1970 +0000
+++ b/contrib/dmu-example.c Thu Jul 27 10:30:09 2006 -0700
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2006
+ * Author: Dan Smith <danms at us.ibm.com>
+ *
+ * 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.
+ *
+ */
+/*
+ * This example program demonstrates a trivial use of the dmu pieces
+ * of the libdevmapper library for userspace orchestration of a
+ * device-mapper pseudo-device. To compile:
+ *
+ * # gcc -ldevmapper -o dmu-example dmu-example.c
+ *
+ * Here, we simply map all reads and writes to the device given as the
+ * first argument to the program. For example:
+ *
+ * # ./dmu-example /dev/ram0
+ *
+ * will create a device /dev/mapper/foo in which all accesses are
+ * redirected to /dev/ram0.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <libdevmapper.h>
+
+dev_t destination_dev;
+
+int map_handler(void *data, struct dmu_map_data *map_data)
+{
+ dmu_map_set_writable(map_data, 1);
+
+ /* We leave block pointing to the original location, but
+ * change the destination device
+ */
+ dmu_map_set_dest_dev(map_data, destination_dev);
+
+ printf("[%u] Mapping %llu -> 0x%llx\n",
+ dmu_map_get_id(map_data),
+ dmu_map_get_block(map_data),
+ destination_dev);
+
+ /* Return nonzero to flush outgoing messages back to
+ * dm-userspace after processing
+ */
+ return 1;
+}
+
+int main(int argc, char **argv)
+{
+ struct dmu_context *c;
+ struct stat s;
+ char cmd[256];
+
+ if (argc != 2) {
+ printf("Usage: %s <device>\n", argv[0]);
+ exit(1);
+ }
+
+ printf("I'm creating a device-mapper device called 'foo'. \n"
+ "Be sure to remove it when you're done with this example\n"
+ "program! (run 'dmsetup remove foo')\n");
+
+ /* Determine and record our destination device for mappings */
+ stat(argv[1], &s);
+ destination_dev = s.st_rdev;
+
+ /* Create a very simple device-mapper device with a small
+ * section of sectors mapped to dm-userspace, at 512-byte
+ * blocks
+ */
+ sprintf(cmd,
+ "echo 0 8192 userspace foo 4096 %i:%i | dmsetup create foo",
+ (unsigned)(destination_dev & 0xFF00) >> 8,
+ (unsigned)(destination_dev & 0x00FF));
+ system(cmd);
+
+ /* Open the control device for the device-mapper device 'foo' */
+ c = dmu_ctl_open("foo", 0);
+ if (!c) {
+ printf("Failed to get control device\n");
+ exit(1);
+ }
+
+ /* Register our callback function for handling map events */
+ dmu_register_map_handler(c, map_handler, NULL);
+
+ while (1) {
+ if (dmu_events_pending(c, 1000)) {
+ printf("Processing events...\n");
+ dmu_process_events(c);
+ } else {
+ printf("Nothing to do\n");
+ }
+ }
+}
diff -r 625e2e3552be -r 1a82e8ce64bc lib/dmu.c
--- /dev/null Thu Jan 1 00:00:00 1970 +0000
+++ b/lib/dmu.c Thu Jul 27 10:30:09 2006 -0700
@@ -0,0 +1,552 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2006
+ * Author: Dan Smith <danms at us.ibm.com>
+ *
+ * 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 <stdio.h>
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <libdevmapper.h>
+#include <linux/dm-userspace.h>
+
+#define DMU_MSG_DEBUG 0
+
+#define QUEUE_SIZE_KB 4096
+
+#if DMU_MSG_DEBUG
+#define DPRINTF( s, arg... ) fprintf(stderr, s, ##arg)
+#else
+#define DPRINTF( s, arg... )
+#endif
+
+struct dmu_events {
+ status_handler status_fn;
+ map_req_handler map_fn;
+};
+
+struct dmu_event_data {
+ void *status_user_data;
+ void *map_user_data;
+};
+
+struct dmu_context {
+ int fd;
+ unsigned int buf_size;
+ unsigned int in_ptr;
+ unsigned int out_ptr;
+ uint8_t *in_buf;
+ uint8_t *out_buf;
+ uint32_t id_ctr;
+ struct dmu_events events;
+ struct dmu_event_data event_data;
+};
+
+struct dmu_map_data {
+ 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_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);
+}
+
+void dmu_map_set_writable(struct dmu_map_data *data, int writable)
+{
+ if (writable)
+ dmu_set_flag(&data->flags, DMU_FLAG_WR);
+ else
+ dmu_clr_flag(&data->flags, DMU_FLAG_WR);
+}
+
+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);
+}
+
+/*
+ * 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 uint32_t make_version(int maj, int min, int patch)
+{
+ return 0 | (maj << 16) | (min << 8) | patch;
+}
+
+static void dmu_split_dev(dev_t dev, uint32_t *maj, uint32_t *min)
+{
+ *maj = (dev & 0xFF00) >> 8;
+ *min = (dev & 0x00FF);
+}
+
+/* Queue a message for sending */
+static int dmu_ctl_queue_msg(struct dmu_context *ctx, int type, void *msg)
+{
+ struct dmu_msg_header hdr;
+
+ hdr.msg_type = type;
+ hdr.payload_len = dmu_get_msg_len(type);
+ hdr.id = ctx->id_ctr++;
+
+ if ((ctx->out_ptr + (sizeof(hdr) + hdr.payload_len)) > ctx->buf_size)
+ return 0; /* No room for this */
+
+ memcpy(ctx->out_buf+ctx->out_ptr, &hdr, sizeof(hdr));
+ ctx->out_ptr += sizeof(hdr);
+
+ memcpy(ctx->out_buf+ctx->out_ptr, msg, hdr.payload_len);
+ ctx->out_ptr += hdr.payload_len;
+
+ return 1;
+}
+
+int dmu_invalidate_block(struct dmu_context *ctx, uint64_t block)
+{
+ struct dmu_msg_invalidate_map inv_msg;
+
+ inv_msg.org_block = block;
+
+ DPRINTF("Queuing invalidation for block %llu\n", block);
+
+ return dmu_ctl_queue_msg(ctx, DM_USERSPACE_MAP_INVALIDATE,
+ &inv_msg);
+}
+
+int dmu_sync_complete(struct dmu_context *ctx, uint32_t id)
+{
+ struct dmu_msg_status status_msg;
+
+ status_msg.id_of_op = id;
+ status_msg.status = DM_USERSPACE_SYNC_COMPLETE;
+
+ DPRINTF("Queuing metadata written for block %llu\n", block);
+
+ return dmu_ctl_queue_msg(ctx, DM_USERSPACE_STATUS,
+ &status_msg);
+}
+
+static int dmu_ctl_peek_queue(struct dmu_context *ctx,
+ int *type, void **msg)
+{
+ struct dmu_msg_header *hdr;
+
+ if (ctx->in_ptr < sizeof(*hdr))
+ return 0;
+
+ hdr = (struct dmu_msg_header *)ctx->in_buf;
+
+ *type = hdr->msg_type;
+ *msg = ctx->in_buf + sizeof(*hdr);
+
+ 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, ctx->out_buf, ctx->out_ptr);
+
+ if (r == ctx->out_ptr)
+ r = 1;
+ else
+ r = 0;
+
+ ctx->out_ptr = 0;
+
+ DPRINTF("Finished flushing queue\n");
+
+ return r;
+}
+
+/* Fill the queue with requests from the kernel */
+static int dmu_ctl_recv_queue(struct dmu_context *ctx)
+{
+ int r;
+
+ r = read(ctx->fd, ctx->in_buf, ctx->buf_size);
+
+ ctx->in_ptr = r;
+
+ if (r >= 0)
+ r = 1;
+ else
+ r = 0;
+
+ return r;
+}
+
+struct dmu_context *dmu_ctl_open(char *dev, int flags)
+{
+ int fd, r, type = 0;
+ struct dmu_msg_version msg;
+ struct dmu_msg_version *response;
+ struct dmu_context *ctx = NULL;
+ char *ctl_dev;
+
+ 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->in_buf = malloc(QUEUE_SIZE_KB << 10);
+ if (!ctx->in_buf)
+ goto out;
+ ctx->out_buf = malloc(QUEUE_SIZE_KB << 10);
+ if (!ctx->out_buf)
+ goto out;
+
+ ctx->fd = fd;
+ ctx->in_ptr = ctx->out_ptr = 0;
+ ctx->id_ctr = 0;
+ ctx->buf_size = 4 << 20;
+ memset(&ctx->events, 0, sizeof(ctx->events));
+ memset(&ctx->event_data, 0, sizeof(ctx->event_data));
+
+ msg.userspace_ver = make_version(0, 1, 0);
+
+ r = dmu_ctl_queue_msg(ctx, DM_USERSPACE_GET_VERSION, &msg);
+ if (r < 0)
+ goto out;
+
+ dmu_ctl_send_queue(ctx);
+ dmu_ctl_recv_queue(ctx);
+
+ r = dmu_ctl_peek_queue(ctx, &type, (void**)&response);
+ if (r < 0)
+ goto out;
+
+ if (type != DM_USERSPACE_GET_VERSION) {
+ DPRINTF(stderr, "Got non-version ping back: %i\n", type);
+ goto out;
+ }
+
+ if (response->kernel_ver != msg.userspace_ver) {
+ DPRINTF(stderr, "Version mismatch: %x != %x\n",
+ msg.userspace_ver, response->kernel_ver);
+ goto out;
+ } else {
+ DPRINTF("Version match: %x == %x\n",
+ msg.userspace_ver, response->kernel_ver);
+ }
+
+ return ctx;
+
+ out:
+ if (ctx && ctx->in_buf)
+ free(ctx->in_buf);
+
+ if (ctx && ctx->out_buf)
+ free(ctx->out_buf);
+
+ if (ctx)
+ free(ctx);
+
+ return NULL;
+}
+
+int dmu_ctl_close(struct dmu_context *ctx)
+{
+ return close(ctx->fd);
+}
+
+void dmu_register_status_handler(struct dmu_context *ctx,
+ status_handler handler,
+ void *data)
+{
+ ctx->events.status_fn = handler;
+ ctx->event_data.status_user_data = data;
+}
+
+void dmu_register_map_handler(struct dmu_context *ctx,
+ map_req_handler handler,
+ void *data)
+{
+ ctx->events.map_fn = handler;
+ ctx->event_data.map_user_data = data;
+}
+
+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,
+ uint32_t id)
+{
+ struct dmu_msg_map_response resp;
+ 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);
+
+ resp.org_block = req->org_block;
+ resp.new_block = data.block;
+ resp.offset = data.offset;
+ resp.flags = data.flags;
+ resp.id_of_req = data.id;
+
+ dmu_split_dev(data.copy_src_dev, &resp.src_maj, &resp.src_min);
+ dmu_split_dev(data.dest_dev, &resp.dst_maj, &resp.dst_min);
+
+ DPRINTF("Mapped %llu -> %llu\n", resp.org_block, resp.new_block);
+
+ if (ret < 0)
+ dmu_ctl_queue_msg(ctx, DM_USERSPACE_MAP_FAILED, &resp);
+ else
+ dmu_ctl_queue_msg(ctx, DM_USERSPACE_MAP_BLOCK_RESP, &resp);
+
+ return ret;
+}
+
+static int fire_status_event(struct dmu_context *ctx,
+ struct dmu_msg_status *status,
+ uint32_t id)
+{
+ uint32_t user_code;
+
+ switch (status->status) {
+ case DM_USERSPACE_INVAL_COMPLETE:
+ user_code = DMU_STATUS_INVAL_COMPLETE;
+ break;
+ case DM_USERSPACE_INVAL_FAILED:
+ user_code = DMU_STATUS_INVAL_FAILED;
+ break;
+ case DM_USERSPACE_SYNC_COMPLETE:
+ user_code = DMU_STATUS_SYNC_COMPLETE;
+ break;
+ default:
+ user_code = DMU_STATUS_UNKNOWN;
+ };
+
+ if (ctx->events.status_fn)
+ ctx->events.status_fn(ctx->event_data.status_user_data,
+ status->id_of_op, user_code);
+
+ return 0;
+}
+
+static int decode_message(struct dmu_context *ctx, int type, uint32_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_STATUS:
+ DPRINTF("Status event\n");
+ return fire_status_event(ctx,
+ (struct dmu_msg_status *)msg,
+ id);
+ default:
+ DPRINTF("Unknown message type: %i\n", type);
+ return -1; /* Unknown message type */
+ };
+}
+
+int dmu_process_events(struct dmu_context *ctx)
+{
+ struct dmu_msg_header *hdr;
+ int ptr = 0, ret, do_flush = 0;
+
+ if (!dmu_ctl_recv_queue(ctx))
+ return -1; /* Receive failed */
+
+ DPRINTF("Got %i bytes\n", ctx->in_ptr);
+
+ ptr = 0;
+ while (ptr < ctx->in_ptr) {
+ hdr = (struct dmu_msg_header *)&ctx->in_buf[ptr];
+ ptr += sizeof(*hdr);
+
+ ret = decode_message(ctx, hdr->msg_type, hdr->id,
+ &ctx->in_buf[ptr]);
+ if (ret > 0)
+ do_flush = 1;
+
+ ptr += hdr->payload_len;
+ };
+
+ ctx->in_ptr = 0;
+
+ if (do_flush) {
+ DPRINTF("Flushing outgoing message queue as requested\n");
+ dmu_ctl_send_queue(ctx);
+ }
+
+ return 1;
+}
+
--
Dan Smith
IBM Linux Technology Center
Open Hypervisor Team
email: danms at us.ibm.com
More information about the dm-devel
mailing list