WARNING - OLD ARCHIVES

This is an archived copy of the Xen.org mailing list, which we have preserved to ensure that existing links to archives are not broken. The live archive, which contains the latest emails, can be found at http://lists.xen.org/
   
 
 
Xen 
 
Home Products Support Community News
 
   
 

xen-devel

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

To: Xen Devel <xen-devel@xxxxxxxxxxxxxxxxxxx>
Subject: [Xen-devel] [PATCH 2 of 6] dm-userspace userspace tool base patch
From: Ryan Grimm <grimm@xxxxxxxxxx>
Date: Mon, 21 Aug 2006 15:55:17 -0500
Cc: Dan Smith <danms@xxxxxxxxxx>
Delivery-date: Mon, 21 Aug 2006 13:57:13 -0700
Envelope-to: www-data@xxxxxxxxxxxxxxxxxx
In-reply-to: <patchbomb.1156192193@venkman-64>
List-help: <mailto:xen-devel-request@lists.xensource.com?subject=help>
List-id: Xen developer discussion <xen-devel.lists.xensource.com>
List-post: <mailto:xen-devel@lists.xensource.com>
List-subscribe: <http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=subscribe>
List-unsubscribe: <http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=unsubscribe>
Sender: xen-devel-bounces@xxxxxxxxxxxxxxxxxxx
User-agent: Mutt/1.5.6+20040907i
# 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

<Prev in Thread] Current Thread [Next in Thread>