Ryan Grimm
2006-Aug-21 20:55 UTC
[Xen-devel] [PATCH 2 of 6] dm-userspace userspace tool base patch
# HG changeset patch # User Ryan Grimm <grimm@us.ibm.com> # Date 1156190587 18000 # Node ID 53c5bcecfcfdb70cb3a2aed0adb564312988fbdd # Parent 9ebba79efbe99774c4063174ab569783017c7e78 dm-userspace userspace tool base patch Signed-off-by: Ryan Grimm <grimm@us.ibm.com> Signed-off-by: Dan Smith <danms@us.ibm.com> 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@us.ibm.com> + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License. See the file COPYING in the main directory + * of this archive for more details. + * + */ + +#include <stdio.h> +#include <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@us.ibm.com> + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License. See the file COPYING in the main directory + * of this archive for more details. + * + */ + +#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@us.ibm.com> + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License. See the file COPYING in the main directory + * of this archive for more details. + * + */ + +#include <stdio.h> +#include <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@us.ibm.com> + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License. See the file COPYING in the main directory + * of this archive for more details. + * + */ + +#include <stdio.h> +#include <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@us.ibm.com> + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License. See the file COPYING in the main directory + * of this archive for more details. + * + */ + +#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@us.ibm.com> + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License. See the file COPYING in the main directory + * of this archive for more details. + * + */ + +#include <stdio.h> +#include <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@us.ibm.com> + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License. See the file COPYING in the main directory + * of this archive for more details. + * + */ + +#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@us.ibm.com> + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License. See the file COPYING in the main directory + * of this archive for more details. + * + */ + +#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@us.ibm.com> + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License. See the file COPYING in the main directory + * of this archive for more details. + * + */ + +#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@lists.xensource.com http://lists.xensource.com/xen-devel
Ryan Grimm
2006-Aug-25 21:23 UTC
[Xen-devel] [PATCH 2 of 6] dm-userspace userspace tool base patch
Signed-off-by: Ryan Grimm <grimm@us.ibm.com> Signed-off-by: Dan Smith <danms@us.ibm.com> # HG changeset patch # User Ryan Grimm <grimm@us.ibm.com> # 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@us.ibm.com> + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License. See the file COPYING in the main directory + * of this archive for more details. + * + */ + +#include <stdio.h> +#include <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@us.ibm.com> + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License. See the file COPYING in the main directory + * of this archive for more details. + * + */ + +#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@us.ibm.com> + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License. See the file COPYING in the main directory + * of this archive for more details. + * + */ + +#include <stdio.h> +#include <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@us.ibm.com> + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License. See the file COPYING in the main directory + * of this archive for more details. + * + */ + +#include <stdio.h> +#include <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@us.ibm.com> + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License. See the file COPYING in the main directory + * of this archive for more details. + * + */ + +#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@us.ibm.com> + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License. See the file COPYING in the main directory + * of this archive for more details. + * + */ + +#include <stdio.h> +#include <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@us.ibm.com> + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License. See the file COPYING in the main directory + * of this archive for more details. + * + */ + +#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@us.ibm.com> + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License. See the file COPYING in the main directory + * of this archive for more details. + * + */ + +#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@us.ibm.com> + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License. See the file COPYING in the main directory + * of this archive for more details. + * + */ + +#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@lists.xensource.com http://lists.xensource.com/xen-devel