Signed-off-by: Ryan Grimm <grimm@xxxxxxxxxx>
Signed-off-by: Dan Smith <danms@xxxxxxxxxx>
# HG changeset patch
# User Ryan Grimm <grimm@xxxxxxxxxx>
# Date 1156536093 18000
# Node ID 7ca9885684d9eaeef4422d52f9ae9efd033650d0
# Parent 2cb702dcea0e44dcfb9c243943d3e523245ad495
dm-userspace userspace tool base patch
diff -r 2cb702dcea0e -r 7ca9885684d9 tools/Makefile
--- a/tools/Makefile Fri Aug 25 10:58:10 2006 -0500
+++ b/tools/Makefile Fri Aug 25 15:01:33 2006 -0500
@@ -31,6 +31,7 @@ all: check
$(MAKE) -C $$subdir $@; \
done
$(MAKE) ioemu
+ $(MAKE) cowd
.PHONY: install
install: check
@@ -38,6 +39,7 @@ install: check
$(MAKE) -C $$subdir $@; \
done
$(MAKE) ioemuinstall
+ $(MAKE) cowdinstall
$(INSTALL_DIR) -p $(DESTDIR)/var/xen/dump
.PHONY: clean
@@ -46,6 +48,7 @@ clean: check_clean
$(MAKE) -C $$subdir $@; \
done
$(MAKE) ioemuclean
+ $(MAKE) cowdclean
.PHONY: distclean
distclean: clean
@@ -71,3 +74,11 @@ ioemu ioemuinstall ioemuclean:
ioemu ioemuinstall ioemuclean:
endif
+.PHONY: cowd cowdinstall cowclean
+cowd/Makefile:
+ -which libtoolize && which aclocal && which automake && \
+ cd cowd && sh autogen && sh configure
+cowd cowdinstall: cowd/Makefile
+ -$(MAKE) -C cowd $(patsubst cowd%,%,$@)
+cowdclean:
+ [ -f ./cowd/Makefile ] && $(MAKE) -C cowd clean || true
diff -r 2cb702dcea0e -r 7ca9885684d9 tools/cowd/Makefile.am
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/Makefile.am Fri Aug 25 15:01:33 2006 -0500
@@ -0,0 +1,11 @@
+bin_PROGRAMS = cowd
+
+cowd_SOURCES = cowd.c util.c cowd_loader.c cowd_control_loop.c \
+ cowd_plugin.h cowd.h cowd_loader.h cowd_ll.c cowd_ll.h
+cowd_CFLAGS = -I/lib/modules/`uname -r`/build/include \
+ -DDEFAULT_PLUGIN_DIR=\"@PLUGIN_DIR@\" @GLOBAL_CFLAGS@
+cowd_LDADD = -ldevmapper -lltdl
+cowd_LDFLAGS = -rdynamic -L./lib
+
+clean-local:
+ rm -f *~
diff -r 2cb702dcea0e -r 7ca9885684d9 tools/cowd/README
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/README Fri Aug 25 15:01:33 2006 -0500
@@ -0,0 +1,68 @@
+***
+*** dm-userspace cow daemon
+***
+
+The tools in this directory are the userspace-side of a functioning
+dm-userspace system. The 'cowd' daemon is responsible for communicating
+with the kernel module and passing requests to a loadable plugin.
+
+##############
+## Building ##
+##############
+
+Make sure you have the following packages on your system:
+ A patched device-mapper (for libdevmapper.so)
+ ltdl-devel (for ltdl.h and ltdl.so)
+
+A patch against the device-mapper package is available here:
+
+ http://static.danplanet.com/dm-userspace/
+
+Once you have an appropriately-patched device-mapper library, simply
+run the following:
+
+ % ./configure
+ % make
+
+And then as root:
+
+ # make install
+
+#############
+## Running ##
+#############
+
+First, you must load the kernel module. If you have a patched kernel,
+then run:
+
+ # modprobe dm-user
+
+if not, build the module and insert it manually:
+
+ # insmod ./dm-user.ko
+
+The following will create a /dev/mapper/mycow device, using the
+image.qcow file:
+
+ # ./cowd -p qcow mycow image.qcow
+
+Note that qcow support is a little shaky at the moment. It's probably
+a better idea to use the dscow plugin. This will create a foo.dscow
+file with a 64k block size:
+
+ # ./plugins/dscow_tool foo.dscow /path/to/base.img 64
+
+Then load it into cowd as such:
+
+ # ./cowd -p dscow mycow foo.dscow
+
+You might also want to enable verbose output with "-v" or even
+debugging output with "-d", so you can watch the magic. Adding "-n"
+in either situation would be a good idea.
+
+After starting the daemon, you can then use /dev/mapper/mycow as a
+normal block device. Reads to unmodified blocks will go directly to
+the base device (specified when image.qcow was created). Writes will
+trigger a block copy from the base image to image.qcow, followed by a
+write of the changes to image.qcow. Subsequent reads will go directly
+to the remapped block in image.qcow.
diff -r 2cb702dcea0e -r 7ca9885684d9 tools/cowd/autogen
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/autogen Fri Aug 25 15:01:33 2006 -0500
@@ -0,0 +1,5 @@
+#!/bin/sh
+libtoolize --force
+aclocal
+automake --add-missing --copy --foreign
+autoconf
diff -r 2cb702dcea0e -r 7ca9885684d9 tools/cowd/configure.in
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/configure.in Fri Aug 25 15:01:33 2006 -0500
@@ -0,0 +1,77 @@
+AC_PREREQ(2.59)
+AC_INIT(cowd, 0.4.0)
+AC_CONFIG_AUX_DIR(.)
+AM_INIT_AUTOMAKE
+
+GLOBAL_CFLAGS="-Werror"
+
+libdevmapper_error() {
+ echo "*************************************************************"
+ echo "* ERROR: You need a newer version of libdevmapper for cowd. *"
+ echo "* The version of libdevmapper on this system does *"
+ echo "* not contain dm-userspace support *"
+ echo "* *"
+ echo "*************************************************************"
+
+ exit 1
+}
+
+AC_CONFIG_SRCDIR([cowd_plugin.h])
+# AC_CONFIG_HEADER([config.h])
+
+AC_ARG_WITH(plugindir,
+ [AC_HELP_STRING([--with-plugindir=<dir>],[Location of plugins])],
+ PLUGIN_DIR=$withval,
+ PLUGIN_DIR=$libdir)
+
+AC_ARG_ENABLE(gcov,
+ [AC_HELP_STRING([--enable-gcov],
+ [Enable coverage analysis])],
+ COVERAGE="-fprofile-arcs -ftest-coverage",
+ COVERAGE="")
+
+# Checks for programs.
+AC_PROG_CC
+AC_PROG_LIBTOOL
+
+# Checks for libraries.
+AC_CHECK_LIB([devmapper], [dm_task_create],, exit)
+AC_CHECK_LIB([ltdl], [lt_dlsym],, exit)
+AC_CHECK_LIB([devmapper], [dmu_ctl_open],, libdevmapper_error)
+
+if test -z "$COVERAGE"; then
+ GLOBAL_CFLAGS="$GLOBAL_CFLAGS"
+else
+ GLOBAL_CFLAGS="$COVERAGE $GLOBAL_CFLAGS"
+ AC_CHECK_LIB([gcov], [__gcov_init])
+fi
+
+# Checks for header files.
+AC_HEADER_STDC
+AC_CHECK_HEADERS([fcntl.h inttypes.h netinet/in.h stdint.h stdlib.h \
+ string.h sys/ioctl.h unistd.h ltdl.h])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_C_INLINE
+AC_TYPE_PID_T
+AC_CHECK_MEMBERS([struct stat.st_rdev])
+
+# Checks for library functions.
+AC_FUNC_FORK
+AC_PROG_GCC_TRADITIONAL
+AC_FUNC_MALLOC
+AC_TYPE_SIGNAL
+AC_FUNC_STAT
+AC_CHECK_FUNCS([memset strtol strtoull])
+
+AC_SUBST(PLUGIN_DIR)
+AC_SUBST(GLOBAL_CFLAGS)
+
+AC_CONFIG_FILES([Makefile])
+
+# This just makes it easier to run cowd from the source directory
+# for testing
+mkdir -p lib
+
+AC_OUTPUT
+
diff -r 2cb702dcea0e -r 7ca9885684d9 tools/cowd/cowd.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/cowd.c Fri Aug 25 15:01:33 2006 -0500
@@ -0,0 +1,453 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2006
+ * Author: Dan Smith <danms@xxxxxxxxxx>
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <inttypes.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sched.h>
+#include <errno.h>
+#include <signal.h>
+#include <wait.h>
+#include <getopt.h>
+#include <syslog.h>
+
+#include <libdevmapper.h>
+
+#include "cowd.h"
+#include "cowd_plugin.h"
+#include "cowd_loader.h"
+
+#define ERR_LEN 1024
+
+/* global control variables */
+int running;
+struct config_struct config;
+
+int initialize_plugin(struct cow_device *dev, char *name)
+{
+ if (! load_plugin(&dev->plugin, name)) {
+ printf("Loading %s failed: %s\n",
+ name, dev->plugin.errmsg);
+ return -1;
+ }
+
+ if (dev->plugin.init_plugin(dev, config.debug) != PLUGIN_OK) {
+ printf("Initializing %s failed: %s\n",
+ name, dev->plugin.errmsg);
+ return -1;
+ }
+
+ if (config.verbose) {
+ printf("Device %s: %lu blocks @ %lu KB\n",
+ dev->name,
+ dev->blocks,
+ dev->block_size >> 10);
+ }
+
+ return 1;
+}
+
+void make_dm_node(struct cow_device *dev)
+{
+ struct dm_task *task;
+ dev_t devno;
+ char filename[256];
+
+ snprintf(filename, 256, "/dev/mapper/%s", dev->name);
+
+ task = dm_task_create(DM_DEVICE_INFO);
+ dm_task_set_name(task, dev->name);
+ if (!dm_task_run(task)) {
+ fprintf(stderr,
+ "Failed to get info for device %s\n", dev->name);
+ return;
+ }
+
+ if (!dm_task_get_info(task, &dev->info)) {
+ fprintf(stderr,
+ "Failed to get info for device %s\n", dev->name);
+ return;
+ }
+
+ devno = MKDEV(dev->info.major, dev->info.minor);
+
+ if (config.debug)
+ printf("Creating /dev/mapper/%s with 0x%llx (%i %i)\n",
+ dev->name, devno, dev->info.major, dev->info.minor);
+
+ mknod(filename, S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP, devno);
+}
+
+void remove_dm_node(struct cow_device *dev)
+{
+ char filename[256];
+
+ snprintf(filename, 256, "/dev/mapper/%s", dev->name);
+ unlink(filename);
+}
+
+int destroy_dm_device(struct cow_device *dev)
+{
+ struct dm_task *task;
+
+ task = dm_task_create(DM_DEVICE_REMOVE);
+
+ dm_task_set_name(task, dev->name);
+ dm_task_run(task);
+ dm_task_destroy(task);
+
+ remove_dm_node(dev);
+
+ return 1;
+}
+
+void sighandler(int signal)
+{
+ int status;
+ pid_t child;
+
+ switch (signal) {
+ case SIGINT:
+ case SIGTERM:
+ running = 0;
+ break;
+ case SIGCHLD:
+ child = waitpid(0, &status, WNOHANG);
+ break;
+ default:
+ /* Unknown Signal */
+ break;
+ }
+}
+
+void version()
+{
+ printf("cowd v%i.%i.%i\n", 0, 0, 1);
+}
+
+void usage(char *name)
+{
+ printf("%s [OPTS] <name> <plugin args ...>\n"
+ "\n"
+ "name: The name to register for this device\n"
+ "plugin args: Arguments to be passed to the plugin\n"
+ "\n"
+ "Options:\n"
+ " -p,--plugin=name : Use plugin <name>\n"
+ " -b,--bsize=kb : Set blocks size to <bsize> KB\n"
+ " -I,--init : Force plugin to initialize CoW space\n"
+ " -n,--nodaemon : Do not daemonize\n"
+ " -r,--resume : Do not initialize device\n"
+ " -s,--sync : Operate block in sync-alloc mode\n"
+ " -V,--version : Display version and exit\n"
+ " -d,--debug : Enable debugging output\n"
+ " -v,--verbose : Enable verbose output\n"
+ " -i,--pidfile=path : Write pid to path\n"
+ "\n", name);
+}
+
+int parse_arguments(int argc, char **argv, struct cow_device *dev)
+{
+ int c;
+ int optidx = 0;
+ int logmask = 0;
+ static struct option lopts[] = {
+ {"plugin", 1, 0, 'p'},
+ {"verbose", 0, 0, 'v'},
+ {"nodaemon", 0, 0, 'n'},
+ {"version", 0, 0, 'V'},
+ {"bsize", 1, 0, 'b'},
+ {"sync", 0, 0, 's'},
+ {"debug", 0, 0, 'd'},
+ {"resume", 0, 0, 'r'},
+ {"init", 0, 0, 'I'},
+ {"pidfile", 1, 0, 'i'},
+ {0, 0, 0, 0 }
+ };
+
+ /* Defaults */
+ strncpy(dev->plugin_name, "dscow", MAX_PLUGIN_LEN);
+ config.verbose = 0;
+ config.debug = 0;
+ config.daemonize = 1;
+ config.init_device = 1;
+ config.init = 0;
+ config.block_size = 0;
+ config.sync_mode = 0;
+ config.pidfile = NULL;
+
+ while (1) {
+ c = getopt_long(argc, argv, "+p:NvnVb:drIs", lopts, &optidx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+
+ case 'p':
+ strncpy(dev->plugin_name, optarg, MAX_PLUGIN_LEN);
+ break;
+
+ case 'v':
+ config.verbose = 1;
+ break;
+
+ case 'n':
+ config.daemonize = 0;
+ break;
+
+ case 'V':
+ version();
+ return(1);
+
+ case 'r':
+ config.init_device = 0;
+ break;
+
+ case 'b':
+ config.block_size = strtol(optarg, NULL, 0) << 10;
+ if (config.block_size & (config.block_size - 1)) {
+ fprintf(stderr,
+ "Block size must be a power of 2!\n");
+ return -1;
+ }
+ break;
+
+ case 'd':
+ config.debug = 1;
+ break;
+
+ case 'I':
+ config.init = 1;
+ break;
+
+ case 's':
+ config.sync_mode = 1;
+ break;
+
+ case 'i':
+ config.pidfile = strdup(optarg);
+ break;
+
+ default:
+ if ((c > 'a') && (c < 'Z')) {
+ fprintf(stderr, "Invalid argument: `%c'\n", c);
+ } else {
+ fprintf(stderr, "[ %c ]\n", c);
+ }
+ usage(argv[0]);
+ return -1;
+ };
+ }
+
+ if ((argc - optind) == 0) {
+ fprintf(stderr, "Error: `name' is required\n");
+ usage(argv[0]);
+ return -1;
+ }
+
+ dev->name = (char *)malloc(strlen(argv[optind])+1);
+ strcpy(dev->name, argv[optind]);
+
+ logmask = LOG_CONS | LOG_PID | LOG_NDELAY;
+ if (!config.daemonize)
+ logmask |= LOG_PERROR;
+ openlog("cowd", logmask, LOG_USER);
+
+ logmask = LOG_UPTO(LOG_NOTICE);
+ if (config.verbose)
+ logmask |= LOG_MASK(LOG_INFO);
+ if (config.debug)
+ logmask |= LOG_MASK(LOG_DEBUG);
+ setlogmask(logmask);
+
+ if (config.verbose) {
+ fprintf(stderr, "Daemon Configuration:\n");
+ fprintf(stderr,
+ "Plugin: %s\n"
+ "Daemon: %s\n"
+ "Init CoW: %s\n"
+ "Verbose: %s\n"
+ "Block Size: %lu KB\n"
+ "Init device:%s\n",
+ dev->plugin_name,
+ config.daemonize ? "yes" : "no",
+ config.init ? "yes" : "no",
+ "yes",
+ config.block_size >> 10,
+ config.init_device ? "yes" : "no");
+ }
+
+ if (optind < argc) {
+ dev->plugin_args = (char **)calloc(sizeof(char*),
+ (argc - optind) + 2);
+ dev->plugin_num_args = (argc - optind);
+
+ for (c = 0; c < dev->plugin_num_args; c++) {
+ dev->plugin_args[c] =
+ (char *)malloc(strlen(argv[optind+c])+1);
+ strcpy(dev->plugin_args[c],
+ argv[optind+c]);
+ if (config.debug)
+ fprintf(stderr,
+ "Adding plugin arg %i/%i: %s\n",
+ c, dev->plugin_num_args,
+ dev->plugin_args[c]);
+ }
+ }
+
+ return 0;
+}
+
+int make_dm_table(struct cow_device *dev)
+{
+ struct dm_task *task;
+ char params[256]; /* Yes, these are magic numbers */
+ char devstr[7];
+ int r, i;
+ uint64_t sectors;
+ dev_t *devs;
+ int dev_count;
+
+ devs = dev->plugin.get_devs(dev, &dev_count);
+
+ sectors = (dev->blocks * dev->block_size) / ((uint64_t)512);
+
+ snprintf(params, 256, "%s %lu", dev->name, dev->block_size);
+
+ for (i = 0; i < dev_count; i++) {
+ snprintf(devstr, 7, " %u:%u",
+ (unsigned)(devs[i] & 0xFF00) >> 8,
+ (unsigned)(devs[i] & 0x00FF));
+ strcat(params, devstr);
+ }
+
+ free(devs);
+
+ if (config.debug)
+ fprintf(stderr, "Creating dm device: %s\n", params);
+
+ task = dm_task_create(DM_DEVICE_CREATE);
+
+ dm_task_set_name(task, dev->name);
+
+ r = dm_task_add_target(task,
+ 0, sectors,
+ "userspace", params);
+
+ if (!r) {
+ fprintf(stderr, "Failed to add target: %u %u %s %s\n",
+ 0, dev->blocks / 512,
+ "userspace", params);
+ return 0;
+ }
+
+ r = dm_task_run(task);
+ if (!r) {
+ fprintf(stderr, "Failed to run device-mapper command!\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+int main(int argc, char **argv)
+{
+ struct cow_device *dev;
+ int r;
+ pid_t pid;
+
+ dev = (struct cow_device *)malloc(sizeof(*dev));
+ if (!dev) {
+ fprintf(stderr, "Failed to allocate device: out of memory\n");
+ exit(1);
+ }
+
+ r = parse_arguments(argc, argv, dev);
+ if (r > 0)
+ exit(0);
+ else if (r < 0)
+ exit(1);
+
+ syslog(LOG_INFO, "Starting");
+
+ /* Load the plugin */
+ if (initialize_plugin(dev, dev->plugin_name) < 0) {
+ fprintf(stderr, "Failed to initialize plugin: %s\n",
+ dev->plugin_name);
+ exit(1);
+ }
+
+ /* Build initial device */
+ r = make_dm_table(dev);
+ if (!r) {
+ fprintf(stderr, "Failed to create DM device\n");
+ dev->plugin.cleanup_plugin(dev);
+ exit(1);
+ }
+
+ /* Create /dev/mapper/foo */
+ make_dm_node(dev);
+
+ dev->ctx = dmu_ctl_open(dev->name, O_NONBLOCK);
+ if (!dev->ctx) {
+ fprintf(stderr, "Unable to open control device\n");
+ dev->plugin.cleanup_plugin(dev);
+ exit(1);
+ }
+
+ /* initialize link list of sync'd maps */
+ ll_init(&sync_list);
+
+ running = 1;
+
+ if (config.daemonize) {
+ int ret = daemon(0, 1);
+ if (ret) {
+ fprintf(stderr, "Unable to daemonize\n");
+ dev->plugin.cleanup_plugin(dev);
+ exit(1);
+ }
+ }
+
+ pid = getpid();
+ if (config.pidfile) {
+ FILE *fpid = fopen(config.pidfile, "w");
+ if (fpid) {
+ fprintf(fpid, "%d\n", pid);
+ fclose(fpid);
+ }
+ }
+
+ signal(SIGTERM, sighandler);
+ signal(SIGCHLD, sighandler);
+ signal(SIGINT, sighandler);
+
+ cow_ctl_loop(dev);
+
+ dmu_ctl_close(dev->ctx);
+
+ destroy_dm_device(dev);
+
+ dev->plugin.cleanup_plugin(dev);
+
+ if (!config.daemonize)
+ fprintf(stderr, "Exiting...\n");
+
+ if (config.pidfile)
+ unlink(config.pidfile);
+
+ return 0;
+}
+
diff -r 2cb702dcea0e -r 7ca9885684d9 tools/cowd/cowd.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/cowd.h Fri Aug 25 15:01:33 2006 -0500
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2006
+ * Author: Dan Smith <danms@xxxxxxxxxx>
+ *
+ * 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.
+ *
+ */
+
+#ifndef __COWD_H
+#define __COWD_H
+
+#include "cowd_ll.h"
+#include <stdint.h>
+
+struct config_struct {
+ int verbose;
+ int debug;
+ int daemonize;
+ int init_device;
+ int init;
+ unsigned long block_size;
+ int sync_mode;
+ char *pidfile;
+};
+
+extern struct config_struct config;
+
+struct sync_blocks {
+ uint32_t id;
+ uint64_t block;
+ struct ll_member *member;
+};
+
+struct ll *sync_list;
+
+#endif
diff -r 2cb702dcea0e -r 7ca9885684d9 tools/cowd/cowd_control_loop.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/cowd_control_loop.c Fri Aug 25 15:01:33 2006 -0500
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2006
+ * Author: Dan Smith <danms@xxxxxxxxxx>
+ *
+ * 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 <sys/types.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <syslog.h>
+
+#ifdef INTERNAL_DMU
+# include <dmu.h>
+#endif
+
+#include "cowd.h"
+#include "cowd_plugin.h"
+
+/* Permit the signal handler to tell us that we should wrap things up
+ * soon */
+extern int running;
+
+/* Global cowd configuration */
+extern struct config_struct config;
+
+int need_hup = 0;
+
+static struct ll_member *find_sync_block_by_org(uint64_t org)
+{
+ struct ll_member *p;
+ struct sync_blocks *sb;
+
+ for (p = sync_list->head; p != NULL; p = p->next) {
+ sb = p->member_of;
+
+ if (sb->block == org)
+ return p;
+ }
+
+ return NULL;
+}
+
+static struct ll_member *find_sync_block_by_id(uint32_t id)
+{
+ struct ll_member *p;
+ struct sync_blocks *sb;
+
+ for (p = sync_list->head; p != NULL; p = p->next) {
+ sb = p->member_of;
+
+ if (sb->id == id)
+ return p;
+ }
+
+ return NULL;
+}
+
+
+static int map_handler(void *data, struct dmu_map_data *map_data)
+{
+ struct cow_device *dev = (struct cow_device *)data;
+ int ret;
+ uint64_t org, new;
+
+ org = dmu_map_get_block(map_data);
+ ret = dev->plugin.map_prepare(dev, map_data);
+ new = dmu_map_get_block(map_data);
+
+ if (ret != PLUGIN_OK) {
+ syslog(LOG_ERR, "Plugin failed to map %llu", org);
+ return 0;
+ }
+
+ if (config.verbose)
+ syslog(LOG_INFO, "Plugin mapped %llu->%llu [%c]",
+ org, dmu_map_get_block(map_data),
+ dmu_map_is_write(map_data) ? 'W' : 'R');
+
+ if (dmu_map_is_write(map_data) && (org != new)){
+ /* A mapping was made */
+ if (config.sync_mode) {
+ /* Request to sync metadata with mapping */
+ struct sync_blocks *sb;
+
+ syslog(LOG_DEBUG,
+ "setting sync flag for %llu", org);
+
+ sb = malloc(sizeof(*sb));
+ if (!sb) {
+ syslog(LOG_CRIT, "malloc failed");
+ return -1;
+ }
+
+ sb->id = dmu_map_get_id(map_data);
+ sb->block = org;
+ ll_member_init(&sb->member, sb);
+ ll_add_tail(sync_list, sb->member);
+ dmu_map_set_sync(map_data);
+ } else {
+ /* No sync needed, Complete mapping immediately */
+ dev->plugin.map_complete(dev, org);
+ }
+ }
+
+ if ((org != new) && (org != (new-1))) {
+ printf("**** ERROR: Mapping %llu -> %llu\n", org, new);
+ }
+ out:
+ return 1;
+}
+
+static int status_msg_handler(void *data, uint32_t id, uint32_t status)
+{
+ struct cow_device *dev = (struct cow_device *)data;
+
+ switch (status) {
+
+ case DMU_STATUS_INVAL_COMPLETE:
+ syslog(LOG_INFO, "Invalidation %u complete", id);
+ break;
+
+ case DMU_STATUS_INVAL_FAILED:
+ syslog(LOG_INFO, "Invalidation %u FAILED", id);
+ break;
+
+ case DMU_STATUS_BLOCK_FLUSHED:
+ syslog(LOG_INFO, "Request %u has flushed");
+ break;
+
+ case DMU_STATUS_SYNC_COMPLETE:
+ {
+ struct ll_member *p;
+ struct sync_blocks *sb;
+
+ if (!config.sync_mode) {
+ syslog(LOG_ERR,
+ "Aiee! Got a SYNC_COMPLETE in aync mode!");
+ break;
+ }
+
+ for (p = sync_list->head; p != NULL; p = p->next) {
+ sb = p->member_of;
+
+ if (sb->id == id) {
+ syslog(LOG_INFO,
+ "Writing metadata for id:%d,
block:%llu", sb->id,
+ sb->block);
+ dmu_sync_complete(dev->ctx, id);
+ dev->plugin.map_complete(dev, sb->block);
+ dev->plugin.write_metadata(dev);
+ ll_remove(p);
+ break;
+ }
+ }
+
+ if (p == NULL) {
+ syslog(LOG_ERR,
+ "Got a SYNC_COMPLETE for %u "
+ "that has no match\n",
+ id);
+ }
+
+ break;
+ }
+
+ case DMU_STATUS_UNKNOWN:
+ default:
+ syslog(LOG_ERR, "Unknown status received (%u) for id %u",
+ status, id);
+ break;
+ };
+
+ return 0;
+}
+
+void hup_handler(int signal)
+{
+ if (signal == SIGHUP)
+ need_hup = 1;
+}
+
+/*
+ * Invalidate all possible remaps for the entire device. This happens
+ * all at once, which is kinda atomic. In other words, we invalidate
+ * all of these blocks before we process any new map requests, which
+ * should give some checkpoint-like behavior.
+ */
+void invalidate_all(struct cow_device *dev)
+{
+ uint64_t i;
+ int r;
+
+ syslog(LOG_INFO, "Invalidating blocks...");
+
+ for (i = 0; i < dev->blocks; i++) {
+ r = dmu_invalidate_block(dev->ctx, i);
+ if (!r){
+ /* No more buffer space */
+ dmu_ctl_send_queue(dev->ctx);
+ sleep(1);
+ }
+ }
+
+ dmu_ctl_send_queue(dev->ctx);
+
+ syslog(LOG_DEBUG, "Invalidated blocks %llu - %llu",
+ 0, dev->blocks - 1);
+}
+
+/* This is the main loop of the daemon that handles:
+ 1. Servicing requests from userspace
+ 2. Occasionally poking the plugin to write metadata
+*/
+void cow_ctl_loop(struct cow_device *dev)
+{
+ int reqs;
+ int ret;
+
+ struct ll_member *p;
+ struct sync_blocks *sb;
+ int dangle_count = 0;
+
+ /* Register SIGHUP handler */
+ signal(SIGHUP, hup_handler);
+
+ dmu_register_map_handler(dev->ctx, map_handler, dev);
+ dmu_register_status_handler(dev->ctx, status_msg_handler, dev);
+
+ while (running) {
+ if (need_hup) {
+ invalidate_all(dev);
+ need_hup = 0;
+ continue;
+ }
+
+ if (dmu_events_pending(dev->ctx, 1000)) {
+ dmu_process_events(dev->ctx);
+ /* Read-ahead */
+ dmu_ctl_send_queue(dev->ctx);
+ }
+ }
+
+ for (p = sync_list->head; p != NULL; p = p->next) {
+ sb = p->member_of;
+
+ syslog(LOG_ERR, "Completing dangling block %llu (%i)",
+ sb->block, ++dangle_count);
+ dev->plugin.map_complete(dev, sb->block);
+ }
+
+ syslog(LOG_INFO, "%i dangling blocks (%p)",
+ dangle_count, sync_list->head);
+
+ syslog(LOG_INFO, "Exiting...");
+}
diff -r 2cb702dcea0e -r 7ca9885684d9 tools/cowd/cowd_ll.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/cowd_ll.c Fri Aug 25 15:01:33 2006 -0500
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2006
+ * Author: Ryan Grimm <grimm@xxxxxxxxxx>
+ *
+ * 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 <malloc.h>
+#include <syslog.h>
+#include "cowd_ll.h"
+
+#define COWD_LL_MAIN 0
+
+int ll_init(struct ll **ll)
+{
+ *ll = malloc(sizeof(**ll));
+ if (!*ll) {
+ syslog(LOG_CRIT, "%s: malloc() failed", __FUNCTION__);
+ return -1;
+ }
+ (*ll)->head = (*ll)->tail = NULL;
+ return 0;
+}
+
+int ll_member_init(struct ll_member **member, void *member_of)
+{
+ *member = malloc(sizeof(**member));
+ if (!*member) {
+ syslog(LOG_CRIT, "%s: malloc() failed", __FUNCTION__);
+ return -1;
+ }
+ (*member)->next = (*member)->prev = NULL;
+ (*member)->member_of = member_of;
+ (*member)->ll = NULL;
+ return 0;
+}
+
+int ll_add_tail(struct ll *ll, struct ll_member *member)
+{
+ if (ll->head == NULL) {
+ ll->head = ll->tail = member;
+ member->next = NULL;
+ member->prev = NULL;
+ member->ll = ll;
+ } else {
+ ll->tail->next = member;
+ member->next = NULL;
+ member->prev = ll->tail;
+ ll->tail = member;
+ member->ll = ll;
+ }
+ return 0;
+}
+
+int ll_remove(struct ll_member *member)
+{
+ if (member->prev)
+ member->prev->next = member->next;
+ else
+ member->ll->head = member->next;
+
+ if (member->next)
+ member->next->prev = member->prev;
+ else
+ member->ll->tail = member->prev;
+
+ free(member);
+}
diff -r 2cb702dcea0e -r 7ca9885684d9 tools/cowd/cowd_ll.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/cowd_ll.h Fri Aug 25 15:01:33 2006 -0500
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2006
+ * Author: Ryan Grimm <grimm@xxxxxxxxxx>
+ *
+ * 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.
+ *
+ */
+
+#ifndef __COWD_LL_H__
+#define __COWD_LL_H__
+
+struct ll_member {
+ void *member_of;
+ struct ll *ll;
+ struct ll_member *next;
+ struct ll_member *prev;
+};
+
+struct ll {
+ struct ll_member *head;
+ struct ll_member *tail;
+};
+
+
+int ll_init(struct ll **ll);
+int ll_member_init(struct ll_member **member, void *member_of);
+int ll_add_tail(struct ll *ll, struct ll_member *member);
+int ll_remove(struct ll_member *member);
+#endif
diff -r 2cb702dcea0e -r 7ca9885684d9 tools/cowd/cowd_loader.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/cowd_loader.c Fri Aug 25 15:01:33 2006 -0500
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2006
+ * Author: Dan Smith <danms@xxxxxxxxxx>
+ *
+ * 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 <stdlib.h>
+#include <dlfcn.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "cowd_plugin.h"
+
+/* Calls a specially-named function in the plugin to initialize the
+ jump table */
+static int poke_plugin(struct cowd_plugin *plugin, void *handle)
+{
+ int (*loader)(struct cowd_plugin *plugin);
+
+ loader = dlsym(handle, "load_plugin");
+
+ if (!loader) {
+ fprintf(stderr, "Failed to find LOAD_PLUGIN\n");
+ return 0;
+ }
+
+ return loader(plugin);
+}
+
+/* Load the dynamic library plugin */
+int load_plugin(struct cowd_plugin *plugin, char *name)
+{
+ void *handle;
+ char *filename;
+ char *dir;
+ int len;
+
+ dir = getenv("COWD_PLUGIN_DIR");
+ if (!dir) {
+ dir = DEFAULT_PLUGIN_DIR;
+ }
+
+ len = strlen(dir) + strlen(name) + 13;
+
+ filename = (char *)malloc(len);
+
+ snprintf(filename, len, "%s/libcowd_%s.so", dir, name);
+
+ handle = dlopen(filename, RTLD_NOW | RTLD_GLOBAL);
+ if (handle == NULL) {
+ snprintf(filename, len, "libcowd_%s.so", name);
+ handle = dlopen(filename, RTLD_NOW | RTLD_GLOBAL);
+ if (handle == NULL) {
+ fprintf(stderr, "Failed to load %s: %s\n",
+ filename, dlerror());
+ return 0;
+ }
+
+ syslog(LOG_INFO,
+ "Loaded libcowd_%s.so from system path",
+ name);
+ } else {
+ syslog(LOG_INFO, "Loaded %s", filename);
+ }
+
+ return poke_plugin(plugin, handle);
+}
diff -r 2cb702dcea0e -r 7ca9885684d9 tools/cowd/cowd_loader.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/cowd_loader.h Fri Aug 25 15:01:33 2006 -0500
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2006
+ * Author: Dan Smith <danms@xxxxxxxxxx>
+ *
+ * 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.
+ *
+ */
+
+#ifndef __COWD_LOADER_H
+#define __COWD_LOADER_H
+
+#include "cowd_plugin.h"
+
+#ifndef DEFAULT_PLUGIN_DIR
+# define DEFAULT_PLUGIN_DIR "./lib"
+#endif
+
+int load_plugin(struct cowd_plugin *plugin, char *name);
+
+#endif
diff -r 2cb702dcea0e -r 7ca9885684d9 tools/cowd/cowd_plugin.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/cowd_plugin.h Fri Aug 25 15:01:33 2006 -0500
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2006
+ * Author: Dan Smith <danms@xxxxxxxxxx>
+ *
+ * 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.
+ *
+ */
+
+#ifndef __COWD_PLUGIN_H
+#define __COWD_PLUGIN_H
+
+#include <stdbool.h>
+
+#include <stdint.h>
+
+#include <libdevmapper.h>
+
+#ifdef INTERNAL_DMU
+#include <dmu.h>
+#endif
+
+#define MKDEV(x,y) (((x << 8) & 0xFF00) | (y & 0xFF))
+
+#define MAX_PLUGIN_LEN 256
+
+unsigned long get_device_blocks(char *dev);
+uint64_t get_file_size(char *path);
+unsigned long long get_device_size(char *dev, uint64_t *size);
+char *make_dev_str(char *dev);
+loff_t dio_lseek(int fd, loff_t offset, int whence);
+int is_file(char *path);
+
+typedef enum plugin_status {
+ PLUGIN_OK=0,
+ PLUGIN_FAIL=-1,
+} p_status;
+
+enum dev_types {
+ COW,
+ BASE,
+};
+
+struct cow_device;
+
+struct cowd_plugin {
+ int (*init_plugin)(struct cow_device *, int debug);
+ int (*write_metadata)(struct cow_device *);
+ bool (*need_flush)(struct cow_device *);
+ int (*map_prepare)(struct cow_device *, struct dmu_map_data *);
+ int (*map_complete)(struct cow_device *, uint64_t org_block);
+ void (*cleanup_plugin)(struct cow_device *);
+ dev_t *(*get_devs)(struct cow_device *, int *count);
+ char *errmsg;
+};
+
+struct cow_device {
+ /* User-supplied attributes */
+ char *name;
+
+ uint64_t block_size;
+ uint64_t blocks;
+
+ /* The assigned control device */
+ struct dmu_context *ctx;
+
+ /* Device mapper info */
+ struct dm_info info;
+
+ /* Plugin information */
+ char plugin_name[MAX_PLUGIN_LEN];
+ int plugin_num_args;
+ char **plugin_args;
+ struct cowd_plugin plugin;
+ void *plugin_private;
+
+};
+
+
+#endif
diff -r 2cb702dcea0e -r 7ca9885684d9 tools/cowd/util.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/util.c Fri Aug 25 15:01:33 2006 -0500
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2006
+ * Author: Dan Smith <danms@xxxxxxxxxx>
+ *
+ * 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.
+ *
+ */
+
+#define __USE_LARGEFILE64
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <asm/fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <syslog.h>
+
+#include "cowd_plugin.h"
+
+size_t round_up_to_sector(size_t value)
+{
+ return (value + 511) & ~511;
+}
+
+/*
+ * Direct I/O helper functions
+ *
+ * These allow us to do simple reads and writes of any size (and to
+ * any location) without having to worry about sector alignment. Note
+ * that if the underlying format is anything other than sector chunks,
+ * data loss may occur.
+ */
+
+loff_t ppos; /* Only one dio file open at a time right now */
+loff_t vpos;
+/* FIXME: Do we want to update {v,p}pos after read/write? */
+
+int dio_open(char *path, int flags)
+{
+ ppos = vpos = 0;
+ return open(path, O_DIRECT | O_LARGEFILE | flags);
+}
+
+loff_t dio_lseek(int fd, loff_t offset, int whence)
+{
+ if (whence != SEEK_SET)
+ return -1;
+
+ vpos = offset;
+
+ if (offset % 512)
+ ppos = round_up_to_sector(offset) - 512;
+ else
+ ppos = offset;
+
+ if (lseek(fd, ppos, SEEK_SET) != ppos)
+ return offset - 1;
+ else
+ return offset;
+}
+
+int dio_read(int fd, void *buffer, size_t count)
+{
+ void *aligned_buf;
+ size_t aligned_size;
+ int ret;
+
+ aligned_size = round_up_to_sector(count);
+
+ ret = posix_memalign(&aligned_buf, 512, aligned_size);
+ if (ret != 0)
+ return -EINVAL;
+
+ ret = read(fd, aligned_buf, aligned_size);
+ memcpy(buffer, aligned_buf + (vpos - ppos), count);
+
+ if (ret < 0) {
+ syslog(LOG_CRIT, "dio_read(%i) failed: %m",
+ aligned_size);
+ }
+
+ free(aligned_buf);
+
+ if (ret == aligned_size)
+ ret = count;
+
+ return ret;
+}
+
+int dio_write(int fd, void *buffer, size_t count)
+{
+ void *aligned_buf;
+ size_t aligned_size;
+ int ret;
+ loff_t prev_ppos;
+
+ if (vpos != ppos) {
+ syslog(LOG_ERR, "dio_write(): vpos: %llu ppos: %llu",
+ vpos, ppos);
+ }
+
+ aligned_size = round_up_to_sector(count);
+
+ ret = posix_memalign(&aligned_buf, 512, aligned_size);
+ if (ret != 0)
+ return -EINVAL;
+
+ /* Prime the buffer */
+ prev_ppos = ppos;
+ ret = read(fd, aligned_buf, aligned_size);
+ if (ret < aligned_size) {
+ syslog(LOG_ERR, "dio_write() failed to prime: %m");
+ return ret;
+ } if (ret != aligned_size) {
+ syslog(LOG_ERR, "dio_write() failed to prime");
+ return -EIO;
+ }
+
+ if (lseek(fd, prev_ppos, SEEK_SET) != prev_ppos) {
+ syslog(LOG_ERR, "dio_write() failed to re-lseek: %m");
+ return -EIO;
+ }
+
+ memcpy(aligned_buf + (vpos - ppos), buffer, count);
+ ret = write(fd, aligned_buf, aligned_size);
+
+ free(aligned_buf);
+
+ if (ret < 0) {
+ syslog(LOG_ERR, "dio_write(%i) failed:%m",
+ aligned_size);
+ }
+
+ if (ret == aligned_size)
+ ret = count;
+
+ return ret;
+}
+
+inline unsigned long get_device_blocks(char *dev)
+{
+ int fd;
+ unsigned long size;
+
+ fd = open(dev, O_RDONLY);
+
+ if (fd <= 0) {
+ syslog(LOG_ERR, "Error trying to open %s: %m", dev);
+ return 0;
+ }
+
+ ioctl(fd, BLKGETSIZE, &size);
+ close(fd);
+
+ return size;
+}
+
+inline unsigned long long get_device_size(char *dev, uint64_t *size)
+{
+ (*size) = ((unsigned long long)get_device_blocks(dev)) * 512;
+ return ((unsigned long long)get_device_blocks(dev)) * 512;
+}
+
+uint64_t get_file_size(char *path)
+{
+ struct stat s;
+
+ if (stat(path, &s)) {
+ perror(path);
+ return 0;
+ } else {
+ return s.st_size;
+ }
+}
+
+char *make_dev_str(char *dev)
+{
+ struct stat s;
+ static char str[10];
+ unsigned int maj, min;
+
+ stat(dev, &s);
+
+ maj = (s.st_rdev & 0xFF00) >> 8;
+ min = (s.st_rdev & 0x00FF);
+
+ snprintf(str, 10, "%i:%i",
+ maj, min);
+
+ return str;
+}
+
+/*
+ * Loop setup functions. These need to be replaced by an ioctl()
+ * implementation, but this is good enough for now.
+ */
+
+int loop_setup(char *dev, char *path)
+{
+ char cmd[256];
+ int ret;
+
+ snprintf(cmd, 256, "losetup %s %s 2>&1", dev, path);
+
+ ret = system(cmd);
+
+ return ret == 0;
+}
+
+int loop_destroy(char *dev)
+{
+ char cmd[256];
+ int ret;
+
+ snprintf(cmd, 256, "losetup -d %s", dev);
+
+ ret = system(cmd);
+
+ return ret == 0;
+}
+
+int is_file(char *path)
+{
+ struct stat s;
+
+ stat(path, &s);
+
+ return s.st_mode & S_IFREG;
+}
+
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel
|