[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH 2 of 6] dm-userspace userspace tool base patch



# HG changeset patch
# User Ryan Grimm <grimm@xxxxxxxxxx>
# Date 1156190587 18000
# Node ID 53c5bcecfcfdb70cb3a2aed0adb564312988fbdd
# Parent  9ebba79efbe99774c4063174ab569783017c7e78
dm-userspace userspace tool base patch

Signed-off-by: Ryan Grimm <grimm@xxxxxxxxxx>
Signed-off-by: Dan Smith <danms@xxxxxxxxxx>

diff -r 9ebba79efbe9 -r 53c5bcecfcfd tools/Makefile
--- a/tools/Makefile    Mon Aug 21 15:03:02 2006 -0500
+++ b/tools/Makefile    Mon Aug 21 15:03:07 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,10 @@ ioemu ioemuinstall ioemuclean:
 ioemu ioemuinstall ioemuclean:
 endif
 
+.PHONY: cowd cowdinstall cowclean
+cowd/Makefile:
+       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 9ebba79efbe9 -r 53c5bcecfcfd tools/cowd/Makefile.am
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/Makefile.am    Mon Aug 21 15:03:07 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 9ebba79efbe9 -r 53c5bcecfcfd tools/cowd/README
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/README Mon Aug 21 15:03:07 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 9ebba79efbe9 -r 53c5bcecfcfd tools/cowd/autogen
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/autogen        Mon Aug 21 15:03:07 2006 -0500
@@ -0,0 +1,5 @@
+#!/bin/sh
+libtoolize --force
+aclocal
+automake --add-missing --copy --foreign
+autoconf
diff -r 9ebba79efbe9 -r 53c5bcecfcfd tools/cowd/configure.in
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/configure.in   Mon Aug 21 15:03:07 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
+}
+
+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 9ebba79efbe9 -r 53c5bcecfcfd tools/cowd/cowd.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/cowd.c Mon Aug 21 15:03:07 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 9ebba79efbe9 -r 53c5bcecfcfd tools/cowd/cowd.h
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/cowd.h Mon Aug 21 15:03:07 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 9ebba79efbe9 -r 53c5bcecfcfd tools/cowd/cowd_control_loop.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/cowd_control_loop.c    Mon Aug 21 15:03:07 2006 -0500
@@ -0,0 +1,263 @@
+/*
+ * 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);
+                               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 9ebba79efbe9 -r 53c5bcecfcfd tools/cowd/cowd_ll.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/cowd_ll.c      Mon Aug 21 15:03:07 2006 -0500
@@ -0,0 +1,105 @@
+/*
+ * 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;
+}
+
+#if COWD_LL_MAIN
+struct blah {
+       struct ll_member member;
+       int a;
+       int b;
+};
+
+int main() {
+       struct ll *ll;
+       struct blah blah;
+       struct blah blah2;
+       struct blah blah3;
+       struct ll_member *m;
+       ll_init(&ll);
+                       
+       blah.a = 10;
+       blah.b = 20;
+       blah2.a = 30;
+       blah2.b = 40;
+       blah3.a = 50;
+       blah3.b = 60;
+       ll_add_tail(ll, &blah.member);
+       ll_add_tail(ll, &blah2.member);
+       ll_add_tail(ll, &blah3.member);
+
+       //ll_remove(blah2.member);
+       //ll_remove(blah.member);
+       //ll_remove(blah3.member);
+       for (m = ll->head; m != NULL; m = m->next) {
+               struct blah *p = m->member_of;
+               printf("%d %d\n", p->a, p->b);
+       }
+}
+#endif
diff -r 9ebba79efbe9 -r 53c5bcecfcfd tools/cowd/cowd_ll.h
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/cowd_ll.h      Mon Aug 21 15:03:07 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 9ebba79efbe9 -r 53c5bcecfcfd tools/cowd/cowd_loader.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/cowd_loader.c  Mon Aug 21 15:03:07 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 9ebba79efbe9 -r 53c5bcecfcfd tools/cowd/cowd_loader.h
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/cowd_loader.h  Mon Aug 21 15:03:07 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 9ebba79efbe9 -r 53c5bcecfcfd tools/cowd/cowd_plugin.h
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/cowd_plugin.h  Mon Aug 21 15:03:07 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 9ebba79efbe9 -r 53c5bcecfcfd tools/cowd/util.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/cowd/util.c Mon Aug 21 15:03:07 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", 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


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.