mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
tools: remove blktap2 source code
Signed-off-by: Wei Liu <wei.liu2@citrix.com> Acked-by: Ian Jackson <ian.jackson@eu.citrix.com>
This commit is contained in:
1 parent
3f0ae679f2
commit
44b2829a8b
126 files changed
-40123
No files matched your search
@@ -1,20 +0,0 @@
|
||||
XEN_ROOT = $(CURDIR)/../..
|
||||
include $(XEN_ROOT)/tools/Rules.mk
|
||||
|
||||
CFLAGS += $(CFLAGS_libxenctrl)
|
||||
LDLIBS += $(LDLIBS_libxenctrl)
|
||||
|
||||
SUBDIRS-y :=
|
||||
SUBDIRS-y += include
|
||||
SUBDIRS-y += lvm
|
||||
SUBDIRS-y += vhd
|
||||
SUBDIRS-$(CONFIG_Linux) += drivers
|
||||
SUBDIRS-$(CONFIG_Linux) += control
|
||||
|
||||
clean:
|
||||
rm -rf *.a *.so *.o *.rpm $(LIB) *~ $(DEPS) TAGS
|
||||
|
||||
distclean: clean
|
||||
|
||||
.PHONY: all clean install distclean
|
||||
all clean install distclean: %: subdirs-%
|
||||
@@ -1,321 +0,0 @@
|
||||
Blktap2 Userspace Tools + Library
|
||||
================================
|
||||
|
||||
Dutch Meyer
|
||||
4th June 2009
|
||||
|
||||
Andrew Warfield and Julian Chesterfield
|
||||
16th June 2006
|
||||
|
||||
|
||||
The blktap2 userspace toolkit provides a user-level disk I/O
|
||||
interface. The blktap2 mechanism involves a kernel driver that acts
|
||||
similarly to the existing Xen/Linux blkback driver, and a set of
|
||||
associated user-level libraries. Using these tools, blktap2 allows
|
||||
virtual block devices presented to VMs to be implemented in userspace
|
||||
and to be backed by raw partitions, files, network, etc.
|
||||
|
||||
The key benefit of blktap2 is that it makes it easy and fast to write
|
||||
arbitrary block backends, and that these user-level backends actually
|
||||
perform very well. Specifically:
|
||||
|
||||
- Metadata disk formats such as Copy-on-Write, encrypted disks, sparse
|
||||
formats and other compression features can be easily implemented.
|
||||
|
||||
- Accessing file-based images from userspace avoids problems related
|
||||
to flushing dirty pages which are present in the Linux loopback
|
||||
driver. (Specifically, doing a large number of writes to an
|
||||
NFS-backed image don't result in the OOM killer going berserk.)
|
||||
|
||||
- Per-disk handler processes enable easier userspace policing of block
|
||||
resources, and process-granularity QoS techniques (disk scheduling
|
||||
and related tools) may be trivially applied to block devices.
|
||||
|
||||
- It's very easy to take advantage of userspace facilities such as
|
||||
networking libraries, compression utilities, peer-to-peer
|
||||
file-sharing systems and so on to build more complex block backends.
|
||||
|
||||
- Crashes are contained -- incremental development/debugging is very
|
||||
fast.
|
||||
|
||||
How it works (in one paragraph):
|
||||
|
||||
Working in conjunction with the kernel blktap2 driver, all disk I/O
|
||||
requests from VMs are passed to the userspace deamon (using a shared
|
||||
memory interface) through a character device. Each active disk is
|
||||
mapped to an individual device node, allowing per-disk processes to
|
||||
implement individual block devices where desired. The userspace
|
||||
drivers are implemented using asynchronous (Linux libaio),
|
||||
O_DIRECT-based calls to preserve the unbuffered, batched and
|
||||
asynchronous request dispatch achieved with the existing blkback
|
||||
code. We provide a simple, asynchronous virtual disk interface that
|
||||
makes it quite easy to add new disk implementations.
|
||||
|
||||
As of June 2009 the current supported disk formats are:
|
||||
|
||||
- Raw Images (both on partitions and in image files)
|
||||
- Fast sharable RAM disk between VMs (requires some form of
|
||||
cluster-based filesystem support e.g. OCFS2 in the guest kernel)
|
||||
- VHD, including snapshots and sparse images
|
||||
- Qcow, including snapshots and sparse images
|
||||
|
||||
|
||||
Build and Installation Instructions
|
||||
===================================
|
||||
|
||||
Make to configure the blktap2 backend driver in your dom0 kernel. It
|
||||
will inter-operate with the existing backend and frontend drivers. It
|
||||
will also cohabitate with the original blktap driver. However, some
|
||||
formats (currently aio and qcow) will default to their blktap2
|
||||
versions when specified in a vm configuration file.
|
||||
|
||||
To build the tools separately, "make && make install" in
|
||||
tools/blktap2.
|
||||
|
||||
|
||||
Using the Tools
|
||||
===============
|
||||
|
||||
Preparing an image for boot:
|
||||
|
||||
The userspace disk agent is configured to start automatically via xend
|
||||
|
||||
Customize the VM config file to use the 'tap:tapdisk' handler,
|
||||
followed by the driver type. e.g. for a raw image such as a file or
|
||||
partition:
|
||||
|
||||
disk = ['tap:tapdisk:aio:<FILENAME>,sda1,w']
|
||||
|
||||
Alternatively, the vhd-util tool (installed with make install, or in
|
||||
/blktap2/vhd) can be used to build sparse copy-on-write vhd images.
|
||||
|
||||
For example, to build a sparse image -
|
||||
vhd-util create -n MyVHDFile -s 1024
|
||||
|
||||
This creates a sparse 1GB file named "MyVHDFile" that can be mounted
|
||||
and populated with data.
|
||||
|
||||
One can also base the image on a raw file -
|
||||
vhd-util snapshot -n MyVHDFile -p SomeRawFile -m
|
||||
|
||||
This creates a sparse VHD file named "MyVHDFile" using "SomeRawFile"
|
||||
as a parent image. Copy-on-write semantics ensure that writes will be
|
||||
stored in "MyVHDFile" while reads will be directed to the most
|
||||
recently written version of the data, either in "MyVHDFile" or
|
||||
"SomeRawFile" as is appropriate. Other options exist as well, consult
|
||||
the vhd-util application for the complete set of VHD tools.
|
||||
|
||||
VHD files can be mounted automatically in a guest similarly to the
|
||||
above AIO example simply by specifying the vhd driver.
|
||||
|
||||
disk = ['tap:tapdisk:vhd:<VHD FILENAME>,sda1,w']
|
||||
|
||||
|
||||
Snapshots:
|
||||
|
||||
Pausing a guest will also plug the corresponding IO queue for blktap2
|
||||
devices and stop blktap2 drivers. This can be used to implement a
|
||||
safe live snapshot of qcow and vhd disks. An example script "xmsnap"
|
||||
is shown in the tools/blktap2/drivers directory. This script will
|
||||
perform a live snapshot of a qcow disk. VHD files can use the
|
||||
"vhd-util snapshot" tool discussed above. If this snapshot command is
|
||||
applied to a raw file mounted with tap:tapdisk:AIO, include the -m
|
||||
flag and the driver will be reloaded as VHD. If applied to an already
|
||||
mounted VHD file, omit the -m flag.
|
||||
|
||||
|
||||
Mounting images in Dom0 using the blktap2 driver
|
||||
===============================================
|
||||
Tap (and blkback) disks are also mountable in Dom0 without requiring an
|
||||
active VM to attach.
|
||||
|
||||
The syntax is -
|
||||
tapdisk2 -n <type>:<full path to file>
|
||||
|
||||
For example -
|
||||
tapdisk2 -n aio:/home/images/rawFile.img
|
||||
|
||||
When successful the location of the new device will be provided by
|
||||
tapdisk2 to stdout and tapdisk2 will terminate. From that point
|
||||
forward control of the device is provided through sysfs in the
|
||||
directory-
|
||||
|
||||
/sys/class/blktap2/blktap#/
|
||||
|
||||
Where # is a blktap2 device number present in the path that tapdisk2
|
||||
printed before terminating. The sysfs interface is largely intuitive,
|
||||
for example, to remove tap device 0 one would-
|
||||
|
||||
echo 1 > /sys/class/blktap2/blktap0/remove
|
||||
|
||||
Similarly, a pause control is available, which is can be used to plug
|
||||
the request queue of a live running guest.
|
||||
|
||||
Previous versions of blktap mounted devices in dom0 by using blkfront
|
||||
in dom0 and the xm block-attach command. This approach is still
|
||||
available, though slightly more cumbersome.
|
||||
|
||||
|
||||
Tapdisk Development
|
||||
===============================================
|
||||
|
||||
People regularly ask how to develop their own tapdisk drivers, and
|
||||
while it has not yet been well documented, the process is relatively
|
||||
easy. Here I will provide a brief overview. The best reference, of
|
||||
course, comes from the existing drivers. Specifically,
|
||||
blktap2/drivers/block-ram.c and blktap2/drivers/block-aio.c provide
|
||||
the clearest examples of simple drivers.
|
||||
|
||||
|
||||
Setup:
|
||||
|
||||
First you need to register your new driver with blktap. This is done
|
||||
in disktypes.h. There are five things that you must do. To
|
||||
demonstrate, I will create a disk called "mynewdisk", you can name
|
||||
yours freely.
|
||||
|
||||
1) Forward declare an instance of struct tap_disk.
|
||||
|
||||
e.g. -
|
||||
extern struct tap_disk tapdisk_mynewdisk;
|
||||
|
||||
2) Claim one of the unused disk type numbers, take care to observe the
|
||||
MAX_DISK_TYPES macro, increasing the number if necessary.
|
||||
|
||||
e.g. -
|
||||
#define DISK_TYPE_MYNEWDISK 10
|
||||
|
||||
3) Create an instance of disk_info_t. The bulk of this file contains examples of these.
|
||||
|
||||
e.g. -
|
||||
static disk_info_t mynewdisk_disk = {
|
||||
DISK_TYPE_MYNEWDISK,
|
||||
"My New Disk (mynewdisk)",
|
||||
"mynewdisk",
|
||||
0,
|
||||
#ifdef TAPDISK
|
||||
&tapdisk_mynewdisk,
|
||||
#endif
|
||||
};
|
||||
|
||||
A few words about what these mean. The first field must be the disk
|
||||
type number you claimed in step (2). The second field is a string
|
||||
describing your disk, and may contain any relevant info. The third
|
||||
field is the name of your disk as will be used by the tapdisk2 utility
|
||||
and xend (for example tapdisk2 -n mynewdisk:/path/to/disk.image, or in
|
||||
your xm create config file). The forth is binary and determines
|
||||
whether you will have one instance of your driver, or many. Here, a 1
|
||||
means that your driver is a singleton and will coordinate access to
|
||||
any number of tap devices. 0 is more common, meaning that you will
|
||||
have one driver for each device that is created. The final field
|
||||
should contain a reference to the struct tap_disk you created in step
|
||||
(1).
|
||||
|
||||
4) Add a reference to your disk info structure (from step (3)) to the
|
||||
dtypes array. Take care here - you need to place it in the position
|
||||
corresponding to the device type number you claimed in step (2). So
|
||||
we would place &mynewdisk_disk in dtypes[10]. Look at the other
|
||||
devices in this array and pad with "&null_disk," as necessary.
|
||||
|
||||
5) Modify the xend python scripts. You need to add your disk name to
|
||||
the list of disks that xend recognizes.
|
||||
|
||||
edit:
|
||||
tools/python/xen/xend/server/BlktapController.py
|
||||
|
||||
And add your disk to the "blktap_disk_types" array near the top of
|
||||
your file. Use the same name you specified in the third field of step
|
||||
(3). The order of this list is not important.
|
||||
|
||||
|
||||
Now your driver is ready to be written. Create a block-mynewdisk.c in
|
||||
tools/blktap2/drivers and add it to the Makefile.
|
||||
|
||||
|
||||
Development:
|
||||
|
||||
Copying block-aio.c and block-ram.c would be a good place to start.
|
||||
Read those files as you go through this, I will be assisting by
|
||||
commenting on a few useful functions and structures.
|
||||
|
||||
struct tap_disk:
|
||||
|
||||
Remember the forward declaration in step (1) of the setup phase above?
|
||||
Now is the time to make that structure a reality. This structure
|
||||
contains a list of function pointers for all the routines that will be
|
||||
asked of your driver. Currently the required functions are open,
|
||||
close, read, write, get_parent_id, validate_parent, and debug.
|
||||
|
||||
e.g. -
|
||||
struct tap_disk tapdisk_mynewdisk = {
|
||||
.disk_type = "tapdisk_mynewdisk",
|
||||
.flags = 0,
|
||||
.private_data_size = sizeof(struct tdmynewdisk_state),
|
||||
.td_open = tdmynewdisk_open,
|
||||
....
|
||||
|
||||
The private_data_size field is used to provide a structure to store
|
||||
the state of your device. It is very likely that you will want
|
||||
something here, but you are free to design whatever structure you
|
||||
want. Blktap will allocate this space for you, you just need to tell
|
||||
it how much space you want.
|
||||
|
||||
|
||||
tdmynewdisk_open:
|
||||
|
||||
This is the open routine. The first argument is a structure
|
||||
representing your driver. Two fields in this array are
|
||||
interesting.
|
||||
|
||||
driver->data will contain a block of memory of the size your requested
|
||||
in in the .private_data_size field of your struct tap_disk (above).
|
||||
|
||||
driver->info contains a structure that details information about your
|
||||
disk. You need to fill this out. By convention this is done with a
|
||||
_get_image_info() function. Assign a size (the total number of
|
||||
sectors), sector_size (the size of each sector in bytes, and set
|
||||
driver->info->info to 0.
|
||||
|
||||
The second parameter contains the name that was specified in the
|
||||
creation of your device, either through xend, or on the command line
|
||||
with tapdisk2. Usually this specifies a file that you will open in
|
||||
this routine. The final parameter, flags, contains one of a number of
|
||||
flags specified in tapdisk.h that may change the way you treat the
|
||||
disk.
|
||||
|
||||
|
||||
_queue_read/write:
|
||||
|
||||
These are your read and write operations. What you do here will
|
||||
depend on your disk, but you should do exactly one of-
|
||||
|
||||
1) call td_complete_request with either error or success code.
|
||||
|
||||
2) Call td_forward_request, which will forward the request to the next
|
||||
driver in the stack.
|
||||
|
||||
3) Queue the request for asynchronous processing with
|
||||
td_prep_read/write. In doing so, you will also register a callback
|
||||
for request completion. When the request completes you must do one of
|
||||
options (1) or (2) above. Finally, call td_queue_tiocb to submit the
|
||||
request to a wait queue.
|
||||
|
||||
The above functions are defined in tapdisk-interface.c. If you don't
|
||||
use them as specified you will run into problems as your driver will
|
||||
fail to inform blktap of the state of requests that have been
|
||||
submitted. Blktap keeps track of all requests and does not like losing track.
|
||||
|
||||
|
||||
_close, _get_parent_id, _validate_parent:
|
||||
|
||||
These last few tend to be very routine. _close is called when the
|
||||
device is closed, and also when it is paused (in this case, open will
|
||||
also be called later). The other functions are used in stacking
|
||||
drivers. Most often drivers will return TD_NO_PARENT and -EINVAL,
|
||||
respectively.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
XEN_ROOT := $(CURDIR)/../../../
|
||||
include $(XEN_ROOT)/tools/Rules.mk
|
||||
|
||||
MAJOR = 1.0
|
||||
MINOR = 0
|
||||
LIBNAME = libblktapctl
|
||||
LIBSONAME = $(LIBNAME).so.$(MAJOR)
|
||||
|
||||
IBIN = tap-ctl
|
||||
|
||||
CFLAGS += -Werror
|
||||
CFLAGS += -Wno-unused
|
||||
CFLAGS += -I../include -I../drivers
|
||||
CFLAGS += $(CFLAGS_xeninclude)
|
||||
CFLAGS += $(CFLAGS_libxenctrl)
|
||||
CFLAGS += -D_GNU_SOURCE
|
||||
CFLAGS += -DTAPCTL
|
||||
|
||||
CTL_OBJS := tap-ctl-ipc.o
|
||||
CTL_OBJS += tap-ctl-list.o
|
||||
CTL_OBJS += tap-ctl-allocate.o
|
||||
CTL_OBJS += tap-ctl-free.o
|
||||
CTL_OBJS += tap-ctl-create.o
|
||||
CTL_OBJS += tap-ctl-destroy.o
|
||||
CTL_OBJS += tap-ctl-spawn.o
|
||||
CTL_OBJS += tap-ctl-attach.o
|
||||
CTL_OBJS += tap-ctl-detach.o
|
||||
CTL_OBJS += tap-ctl-open.o
|
||||
CTL_OBJS += tap-ctl-close.o
|
||||
CTL_OBJS += tap-ctl-pause.o
|
||||
CTL_OBJS += tap-ctl-unpause.o
|
||||
CTL_OBJS += tap-ctl-major.o
|
||||
CTL_OBJS += tap-ctl-check.o
|
||||
|
||||
CTL_PICS = $(patsubst %.o,%.opic,$(CTL_OBJS))
|
||||
|
||||
OBJS = $(CTL_OBJS) tap-ctl.o
|
||||
PICS = $(CTL_PICS)
|
||||
|
||||
LIB_STATIC = $(LIBNAME).a
|
||||
LIB_SHARED = $(LIBSONAME).$(MINOR)
|
||||
IBIN = tap-ctl
|
||||
|
||||
all: build
|
||||
|
||||
build: $(IBIN) $(LIB_STATIC) $(LIB_SHARED)
|
||||
|
||||
$(LIBNAME).so: $(LIBSONAME)
|
||||
ln -sf $< $@
|
||||
|
||||
$(LIBSONAME): $(LIB_SHARED)
|
||||
ln -sf $< $@
|
||||
|
||||
tap-ctl: tap-ctl.o $(LIBNAME).so
|
||||
$(CC) $(LDFLAGS) -o $@ $^ $(APPEND_LDFLAGS)
|
||||
|
||||
$(LIB_STATIC): $(CTL_OBJS)
|
||||
$(AR) r $@ $^
|
||||
|
||||
$(LIB_SHARED): $(CTL_PICS)
|
||||
$(CC) $(LDFLAGS) -fPIC -Wl,$(SONAME_LDFLAG) -Wl,$(LIBSONAME) $(SHLIB_LDFLAGS) -rdynamic $^ -o $@ $(APPEND_LDFLAGS)
|
||||
|
||||
install: $(IBIN) $(LIB_STATIC) $(LIB_SHARED)
|
||||
$(INSTALL_DIR) -p $(DESTDIR)$(sbindir)
|
||||
$(INSTALL_PROG) $(IBIN) $(DESTDIR)$(sbindir)
|
||||
$(INSTALL_DATA) $(LIB_STATIC) $(DESTDIR)$(libdir)
|
||||
$(INSTALL_PROG) $(LIB_SHARED) $(DESTDIR)$(libdir)
|
||||
ln -sf $(LIBSONAME) $(DESTDIR)$(libdir)/$(LIBNAME).so
|
||||
ln -sf $(LIB_SHARED) $(DESTDIR)$(libdir)/$(LIBSONAME)
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) $(PICS) $(DEPS) $(IBIN) $(LIB_STATIC) $(LIB_SHARED)
|
||||
rm -f $(LIBNAME).so $(LIBSONAME)
|
||||
rm -f *~
|
||||
|
||||
distclean: clean
|
||||
|
||||
.PHONY: all build clean distclean install
|
||||
|
||||
-include $(DEPS)
|
||||
@@ -1,242 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
#include <libgen.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/major.h>
|
||||
|
||||
#include "tap-ctl.h"
|
||||
#include "blktap2.h"
|
||||
|
||||
static int
|
||||
tap_ctl_prepare_directory(const char *dir)
|
||||
{
|
||||
int err;
|
||||
char *ptr, *name, *start;
|
||||
|
||||
err = access(dir, W_OK | R_OK);
|
||||
if (!err)
|
||||
return 0;
|
||||
|
||||
name = strdup(dir);
|
||||
if (!name)
|
||||
return ENOMEM;
|
||||
|
||||
start = name;
|
||||
|
||||
for (;;) {
|
||||
ptr = strchr(start + 1, '/');
|
||||
if (ptr)
|
||||
*ptr = '\0';
|
||||
|
||||
err = mkdir(name, 0755);
|
||||
if (err && errno != EEXIST) {
|
||||
PERROR("mkdir %s", name);
|
||||
err = errno;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ptr)
|
||||
break;
|
||||
else {
|
||||
*ptr = '/';
|
||||
start = ptr + 1;
|
||||
}
|
||||
}
|
||||
|
||||
free(name);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
tap_ctl_make_device(const char *devname, const int major,
|
||||
const int minor, const int perm)
|
||||
{
|
||||
int err;
|
||||
char *copy, *dir;
|
||||
|
||||
copy = strdup(devname);
|
||||
if (!copy)
|
||||
return ENOMEM;
|
||||
|
||||
dir = dirname(copy);
|
||||
|
||||
err = tap_ctl_prepare_directory(dir);
|
||||
free(copy);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!access(devname, F_OK))
|
||||
if (unlink(devname)) {
|
||||
PERROR("unlink %s", devname);
|
||||
return errno;
|
||||
}
|
||||
|
||||
err = mknod(devname, perm, makedev(major, minor));
|
||||
if (err) {
|
||||
PERROR("mknod %s", devname);
|
||||
return errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
tap_ctl_check_environment(void)
|
||||
{
|
||||
FILE *f;
|
||||
int err, minor;
|
||||
char name[256];
|
||||
|
||||
err = tap_ctl_prepare_directory(BLKTAP2_CONTROL_DIR);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!access(BLKTAP2_CONTROL_DEVICE, R_OK | W_OK))
|
||||
return 0;
|
||||
|
||||
memset(name, 0, sizeof(name));
|
||||
|
||||
f = fopen("/proc/misc", "r");
|
||||
if (!f) {
|
||||
EPRINTF("failed to open /proc/misc: %d\n", errno);
|
||||
return errno;
|
||||
}
|
||||
|
||||
while (fscanf(f, "%d %256s", &minor, name) == 2)
|
||||
if (!strcmp(name, BLKTAP2_CONTROL_NAME)) {
|
||||
err = tap_ctl_make_device(BLKTAP2_CONTROL_DEVICE,
|
||||
MISC_MAJOR,
|
||||
minor, S_IFCHR | 0600);
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = ENOSYS;
|
||||
EPRINTF("didn't find %s in /proc/misc\n", BLKTAP2_CONTROL_NAME);
|
||||
|
||||
out:
|
||||
fclose(f);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
tap_ctl_allocate_device(int *minor, char **devname)
|
||||
{
|
||||
char *name;
|
||||
int fd, err;
|
||||
struct blktap2_handle handle;
|
||||
|
||||
*minor = -1;
|
||||
if (!devname)
|
||||
return EINVAL;
|
||||
|
||||
fd = open(BLKTAP2_CONTROL_DEVICE, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
EPRINTF("failed to open control device: %d\n", errno);
|
||||
return errno;
|
||||
}
|
||||
|
||||
err = ioctl(fd, BLKTAP2_IOCTL_ALLOC_TAP, &handle);
|
||||
close(fd);
|
||||
if (err == -1) {
|
||||
EPRINTF("failed to allocate new device: %d\n", errno);
|
||||
return errno;
|
||||
}
|
||||
|
||||
err = asprintf(&name, "%s%d", BLKTAP2_RING_DEVICE, handle.minor);
|
||||
if (err == -1) {
|
||||
err = ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = tap_ctl_make_device(name, handle.ring,
|
||||
handle.minor, S_IFCHR | 0600);
|
||||
free(name);
|
||||
if (err) {
|
||||
EPRINTF("creating ring device for %d failed: %d\n",
|
||||
handle.minor, err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (*devname)
|
||||
name = *devname;
|
||||
else {
|
||||
err = asprintf(&name, "%s%d",
|
||||
BLKTAP2_IO_DEVICE, handle.minor);
|
||||
if (err == -1) {
|
||||
err = ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
*devname = name;
|
||||
}
|
||||
|
||||
err = tap_ctl_make_device(name, handle.device,
|
||||
handle.minor, S_IFBLK | 0600);
|
||||
if (err) {
|
||||
EPRINTF("creating IO device for %d failed: %d\n",
|
||||
handle.minor, err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
DBG("new interface: ring: %u, device: %u, minor: %u\n",
|
||||
handle.ring, handle.device, handle.minor);
|
||||
|
||||
*minor = handle.minor;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
tap_ctl_free(handle.minor);
|
||||
return err;
|
||||
}
|
||||
|
||||
int
|
||||
tap_ctl_allocate(int *minor, char **devname)
|
||||
{
|
||||
int err;
|
||||
|
||||
*minor = -1;
|
||||
|
||||
err = tap_ctl_check_environment();
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = tap_ctl_allocate_device(minor, devname);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include "tap-ctl.h"
|
||||
|
||||
int
|
||||
tap_ctl_attach(const int id, const int minor)
|
||||
{
|
||||
int err;
|
||||
tapdisk_message_t message;
|
||||
|
||||
memset(&message, 0, sizeof(message));
|
||||
message.type = TAPDISK_MESSAGE_ATTACH;
|
||||
message.cookie = minor;
|
||||
|
||||
err = tap_ctl_connect_send_and_receive(id, &message, 5);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (message.type == TAPDISK_MESSAGE_ATTACH_RSP) {
|
||||
err = message.u.response.error;
|
||||
if (err)
|
||||
EPRINTF("attach failed: %d\n", err);
|
||||
} else {
|
||||
EPRINTF("got unexpected result '%s' from %d\n",
|
||||
tapdisk_message_name(message.type), id);
|
||||
err = EINVAL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "tap-ctl.h"
|
||||
#include "blktap2.h"
|
||||
|
||||
int
|
||||
tap_ctl_check_blktap(const char **msg)
|
||||
{
|
||||
FILE *f;
|
||||
int err = 0, minor;
|
||||
char name[32];
|
||||
|
||||
memset(name, 0, sizeof(name));
|
||||
|
||||
f = fopen("/proc/misc", "r");
|
||||
if (!f) {
|
||||
*msg = "failed to open /proc/misc";
|
||||
return -errno;
|
||||
}
|
||||
|
||||
while (fscanf(f, "%d %32s", &minor, name) == 2) {
|
||||
if (!strcmp(name, BLKTAP2_CONTROL_NAME))
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = -ENOSYS;
|
||||
*msg = "blktap kernel module not installed";
|
||||
|
||||
out:
|
||||
fclose(f);
|
||||
return err;
|
||||
}
|
||||
|
||||
int
|
||||
tap_ctl_check(const char **msg)
|
||||
{
|
||||
int err;
|
||||
uid_t uid;
|
||||
|
||||
err = tap_ctl_check_blktap(msg);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = 0;
|
||||
*msg = "ok";
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include "tap-ctl.h"
|
||||
|
||||
static int
|
||||
__tap_ctl_close(const int id, const int minor, const int force)
|
||||
{
|
||||
int err;
|
||||
tapdisk_message_t message;
|
||||
|
||||
memset(&message, 0, sizeof(message));
|
||||
message.type = TAPDISK_MESSAGE_CLOSE;
|
||||
if (force)
|
||||
message.type = TAPDISK_MESSAGE_FORCE_SHUTDOWN;
|
||||
message.cookie = minor;
|
||||
|
||||
err = tap_ctl_connect_send_and_receive(id, &message, 5);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (message.type == TAPDISK_MESSAGE_CLOSE_RSP) {
|
||||
err = message.u.response.error;
|
||||
if (err)
|
||||
EPRINTF("close failed: %d\n", err);
|
||||
} else {
|
||||
EPRINTF("got unexpected result '%s' from %d\n",
|
||||
tapdisk_message_name(message.type), id);
|
||||
err = EINVAL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int
|
||||
tap_ctl_close(const int id, const int minor, const int force)
|
||||
{
|
||||
int i, err;
|
||||
|
||||
for (i = 0; i < 20; i++) {
|
||||
err = __tap_ctl_close(id, minor, force);
|
||||
if (!err)
|
||||
return 0;
|
||||
|
||||
err = (err < 0 ? -err : err);
|
||||
if (err != EAGAIN) {
|
||||
EPRINTF("close failed: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
usleep(1000);
|
||||
}
|
||||
|
||||
EPRINTF("close timed out\n");
|
||||
return EIO;
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include "tap-ctl.h"
|
||||
#include "blktap2.h"
|
||||
|
||||
int
|
||||
tap_ctl_create(const char *params, char **devname)
|
||||
{
|
||||
int err, id, minor;
|
||||
|
||||
err = tap_ctl_allocate(&minor, devname);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
id = tap_ctl_spawn();
|
||||
if (id < 0) {
|
||||
err = id;
|
||||
goto destroy;
|
||||
}
|
||||
|
||||
err = tap_ctl_attach(id, minor);
|
||||
if (err)
|
||||
goto destroy;
|
||||
|
||||
err = tap_ctl_open(id, minor, params);
|
||||
if (err)
|
||||
goto detach;
|
||||
|
||||
return 0;
|
||||
|
||||
detach:
|
||||
tap_ctl_detach(id, minor);
|
||||
destroy:
|
||||
tap_ctl_free(minor);
|
||||
return err;
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include "tap-ctl.h"
|
||||
#include "blktap2.h"
|
||||
|
||||
int
|
||||
tap_ctl_destroy(const int id, const int minor)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = tap_ctl_close(id, minor, 0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = tap_ctl_detach(id, minor);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = tap_ctl_free(minor);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include "tap-ctl.h"
|
||||
|
||||
int
|
||||
tap_ctl_detach(const int id, const int minor)
|
||||
{
|
||||
int err;
|
||||
tapdisk_message_t message;
|
||||
|
||||
memset(&message, 0, sizeof(message));
|
||||
message.type = TAPDISK_MESSAGE_DETACH;
|
||||
message.cookie = minor;
|
||||
|
||||
err = tap_ctl_connect_send_and_receive(id, &message, 5);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (message.type == TAPDISK_MESSAGE_DETACH_RSP) {
|
||||
err = message.u.response.error;
|
||||
if (err < 0)
|
||||
printf("detach failed: %d\n", err);
|
||||
} else {
|
||||
printf("got unexpected result '%s' from %d\n",
|
||||
tapdisk_message_name(message.type), id);
|
||||
err = EINVAL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "tap-ctl.h"
|
||||
#include "blktap2.h"
|
||||
|
||||
int
|
||||
tap_ctl_free(const int minor)
|
||||
{
|
||||
int fd, err;
|
||||
|
||||
fd = open(BLKTAP2_CONTROL_DEVICE, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
EPRINTF("failed to open control device: %d\n", errno);
|
||||
return errno;
|
||||
}
|
||||
|
||||
err = ioctl(fd, BLKTAP2_IOCTL_FREE_TAP, minor);
|
||||
close(fd);
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -1,249 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "tap-ctl.h"
|
||||
#include "blktap2.h"
|
||||
|
||||
int tap_ctl_debug = 0;
|
||||
|
||||
int
|
||||
tap_ctl_read_message(int fd, tapdisk_message_t *message, int timeout)
|
||||
{
|
||||
fd_set readfds;
|
||||
int ret, len, offset;
|
||||
struct timeval tv, *t;
|
||||
|
||||
t = NULL;
|
||||
offset = 0;
|
||||
len = sizeof(tapdisk_message_t);
|
||||
|
||||
if (timeout) {
|
||||
tv.tv_sec = timeout;
|
||||
tv.tv_usec = 0;
|
||||
t = &tv;
|
||||
}
|
||||
|
||||
memset(message, 0, sizeof(tapdisk_message_t));
|
||||
|
||||
while (offset < len) {
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(fd, &readfds);
|
||||
|
||||
ret = select(fd + 1, &readfds, NULL, NULL, t);
|
||||
if (ret == -1) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
else if (FD_ISSET(fd, &readfds)) {
|
||||
ret = read(fd, message + offset, len - offset);
|
||||
if (ret <= 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
offset += ret;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
if (offset != len) {
|
||||
EPRINTF("failure reading message\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
DBG("received '%s' message (uuid = %u)\n",
|
||||
tapdisk_message_name(message->type), message->cookie);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
tap_ctl_write_message(int fd, tapdisk_message_t *message, int timeout)
|
||||
{
|
||||
fd_set writefds;
|
||||
int ret, len, offset;
|
||||
struct timeval tv, *t;
|
||||
|
||||
t = NULL;
|
||||
offset = 0;
|
||||
len = sizeof(tapdisk_message_t);
|
||||
|
||||
if (timeout) {
|
||||
tv.tv_sec = timeout;
|
||||
tv.tv_usec = 0;
|
||||
t = &tv;
|
||||
}
|
||||
|
||||
DBG("sending '%s' message (uuid = %u)\n",
|
||||
tapdisk_message_name(message->type), message->cookie);
|
||||
|
||||
while (offset < len) {
|
||||
FD_ZERO(&writefds);
|
||||
FD_SET(fd, &writefds);
|
||||
|
||||
/* we don't bother reinitializing tv. at worst, it will wait a
|
||||
* bit more time than expected. */
|
||||
|
||||
ret = select(fd + 1, NULL, &writefds, NULL, t);
|
||||
if (ret == -1) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
else if (FD_ISSET(fd, &writefds)) {
|
||||
ret = write(fd, message + offset, len - offset);
|
||||
if (ret <= 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
offset += ret;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
if (offset != len) {
|
||||
EPRINTF("failure writing message\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
tap_ctl_send_and_receive(int sfd, tapdisk_message_t *message, int timeout)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = tap_ctl_write_message(sfd, message, timeout);
|
||||
if (err) {
|
||||
EPRINTF("failed to send '%s' message\n",
|
||||
tapdisk_message_name(message->type));
|
||||
return err;
|
||||
}
|
||||
|
||||
err = tap_ctl_read_message(sfd, message, timeout);
|
||||
if (err) {
|
||||
EPRINTF("failed to receive '%s' message\n",
|
||||
tapdisk_message_name(message->type));
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *
|
||||
tap_ctl_socket_name(int id)
|
||||
{
|
||||
char *name;
|
||||
|
||||
if (asprintf(&name, "%s/%s%d",
|
||||
BLKTAP2_CONTROL_DIR, BLKTAP2_CONTROL_SOCKET, id) == -1)
|
||||
return NULL;
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
int
|
||||
tap_ctl_connect(const char *name, int *sfd)
|
||||
{
|
||||
int fd, err;
|
||||
struct sockaddr_un saddr;
|
||||
|
||||
*sfd = -1;
|
||||
|
||||
fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (fd == -1) {
|
||||
EPRINTF("couldn't create socket for %s: %d\n", name, errno);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
memset(&saddr, 0, sizeof(saddr));
|
||||
saddr.sun_family = AF_UNIX;
|
||||
strcpy(saddr.sun_path, name);
|
||||
|
||||
err = connect(fd, (const struct sockaddr *)&saddr, sizeof(saddr));
|
||||
if (err) {
|
||||
EPRINTF("couldn't connect to %s: %d\n", name, errno);
|
||||
close(fd);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
*sfd = fd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
tap_ctl_connect_id(int id, int *sfd)
|
||||
{
|
||||
int err;
|
||||
char *name;
|
||||
|
||||
*sfd = -1;
|
||||
|
||||
if (id < 0) {
|
||||
EPRINTF("invalid id %d\n", id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
name = tap_ctl_socket_name(id);
|
||||
if (!name) {
|
||||
EPRINTF("couldn't name socket for %d\n", id);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
err = tap_ctl_connect(name, sfd);
|
||||
free(name);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int
|
||||
tap_ctl_connect_send_and_receive(int id, tapdisk_message_t *message, int timeout)
|
||||
{
|
||||
int err, sfd;
|
||||
|
||||
err = tap_ctl_connect_id(id, &sfd);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = tap_ctl_send_and_receive(sfd, message, timeout);
|
||||
|
||||
close(sfd);
|
||||
return err;
|
||||
}
|
||||
@@ -1,536 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <glob.h>
|
||||
|
||||
#include "tap-ctl.h"
|
||||
#include "blktap2.h"
|
||||
#include "list.h"
|
||||
|
||||
static void
|
||||
free_list(tap_list_t *entry)
|
||||
{
|
||||
if (entry->type) {
|
||||
free(entry->type);
|
||||
entry->type = NULL;
|
||||
}
|
||||
|
||||
if (entry->path) {
|
||||
free(entry->path);
|
||||
entry->path = NULL;
|
||||
}
|
||||
|
||||
free(entry);
|
||||
}
|
||||
|
||||
int
|
||||
_parse_params(const char *params, char **type, char **path)
|
||||
{
|
||||
char *ptr;
|
||||
size_t len;
|
||||
|
||||
ptr = strchr(params, ':');
|
||||
if (!ptr)
|
||||
return -EINVAL;
|
||||
|
||||
len = ptr - params;
|
||||
|
||||
*type = strndup(params, len);
|
||||
*path = strdup(params + len + 1);
|
||||
|
||||
if (!*type || !*path) {
|
||||
free(*type);
|
||||
*type = NULL;
|
||||
|
||||
free(*path);
|
||||
*path = NULL;
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
init_list(tap_list_t *entry,
|
||||
int tap_id, pid_t tap_pid, int vbd_minor, int vbd_state,
|
||||
const char *params)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
entry->id = tap_id;
|
||||
entry->pid = tap_pid;
|
||||
entry->minor = vbd_minor;
|
||||
entry->state = vbd_state;
|
||||
|
||||
if (params)
|
||||
err = _parse_params(params, &entry->type, &entry->path);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void
|
||||
tap_ctl_free_list(tap_list_t **list)
|
||||
{
|
||||
tap_list_t **_entry;
|
||||
|
||||
for (_entry = list; *_entry != NULL; ++_entry)
|
||||
free_list(*_entry);
|
||||
|
||||
free(list);
|
||||
}
|
||||
|
||||
static tap_list_t**
|
||||
tap_ctl_alloc_list(int n)
|
||||
{
|
||||
tap_list_t **list, *entry;
|
||||
size_t size;
|
||||
int i;
|
||||
|
||||
size = sizeof(tap_list_t*) * (n+1);
|
||||
list = malloc(size);
|
||||
if (!list)
|
||||
goto fail;
|
||||
|
||||
memset(list, 0, size);
|
||||
|
||||
for (i = 0; i < n; ++i) {
|
||||
tap_list_t *entry;
|
||||
|
||||
entry = malloc(sizeof(tap_list_t));
|
||||
if (!entry)
|
||||
goto fail;
|
||||
|
||||
memset(entry, 0, sizeof(tap_list_t));
|
||||
|
||||
list[i] = entry;
|
||||
}
|
||||
|
||||
return list;
|
||||
|
||||
fail:
|
||||
if (list)
|
||||
tap_ctl_free_list(list);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
tap_ctl_list_length(const tap_list_t **list)
|
||||
{
|
||||
const tap_list_t **_entry;
|
||||
int n;
|
||||
|
||||
n = 0;
|
||||
for (_entry = list; *_entry != NULL; ++_entry)
|
||||
n++;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static int
|
||||
_tap_minor_cmp(const void *a, const void *b)
|
||||
{
|
||||
return *(int*)a - *(int*)b;
|
||||
}
|
||||
|
||||
int
|
||||
_tap_ctl_find_minors(int **_minorv)
|
||||
{
|
||||
glob_t glbuf = { 0 };
|
||||
const char *pattern, *format;
|
||||
int *minorv = NULL, n_minors = 0;
|
||||
int err, i;
|
||||
|
||||
pattern = BLKTAP2_SYSFS_DIR"/blktap*";
|
||||
format = BLKTAP2_SYSFS_DIR"/blktap%d";
|
||||
|
||||
n_minors = 0;
|
||||
minorv = NULL;
|
||||
|
||||
err = glob(pattern, 0, NULL, &glbuf);
|
||||
switch (err) {
|
||||
case GLOB_NOMATCH:
|
||||
goto done;
|
||||
|
||||
case GLOB_ABORTED:
|
||||
case GLOB_NOSPACE:
|
||||
err = -errno;
|
||||
EPRINTF("%s: glob failed, err %d", pattern, err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
minorv = malloc(sizeof(int) * glbuf.gl_pathc);
|
||||
if (!minorv) {
|
||||
err = -errno;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (i = 0; i < glbuf.gl_pathc; ++i) {
|
||||
int n;
|
||||
|
||||
n = sscanf(glbuf.gl_pathv[i], format, &minorv[n_minors]);
|
||||
if (n != 1)
|
||||
continue;
|
||||
|
||||
n_minors++;
|
||||
}
|
||||
|
||||
qsort(minorv, n_minors, sizeof(int), _tap_minor_cmp);
|
||||
|
||||
done:
|
||||
*_minorv = minorv;
|
||||
err = 0;
|
||||
|
||||
out:
|
||||
if (glbuf.gl_pathv)
|
||||
globfree(&glbuf);
|
||||
|
||||
return err ? : n_minors;
|
||||
|
||||
fail:
|
||||
if (minorv)
|
||||
free(minorv);
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
struct tapdisk {
|
||||
int id;
|
||||
pid_t pid;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
static int
|
||||
_tap_tapdisk_cmp(const void *a, const void *b)
|
||||
{
|
||||
return ((struct tapdisk*)a)->id - ((struct tapdisk*)b)->id;
|
||||
}
|
||||
|
||||
int
|
||||
_tap_ctl_find_tapdisks(struct tapdisk **_tapv)
|
||||
{
|
||||
glob_t glbuf = { 0 };
|
||||
const char *pattern, *format;
|
||||
struct tapdisk *tapv = NULL;
|
||||
int err, i, n_taps = 0;
|
||||
|
||||
pattern = BLKTAP2_CONTROL_DIR"/"BLKTAP2_CONTROL_SOCKET"*";
|
||||
format = BLKTAP2_CONTROL_DIR"/"BLKTAP2_CONTROL_SOCKET"%d";
|
||||
|
||||
n_taps = 0;
|
||||
tapv = NULL;
|
||||
|
||||
err = glob(pattern, 0, NULL, &glbuf);
|
||||
switch (err) {
|
||||
case GLOB_NOMATCH:
|
||||
goto done;
|
||||
|
||||
case GLOB_ABORTED:
|
||||
case GLOB_NOSPACE:
|
||||
err = -errno;
|
||||
EPRINTF("%s: glob failed, err %d", pattern, err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
tapv = malloc(sizeof(struct tapdisk) * glbuf.gl_pathc);
|
||||
if (!tapv) {
|
||||
err = -errno;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (i = 0; i < glbuf.gl_pathc; ++i) {
|
||||
struct tapdisk *tap;
|
||||
int n;
|
||||
|
||||
tap = &tapv[n_taps];
|
||||
|
||||
err = sscanf(glbuf.gl_pathv[i], format, &tap->id);
|
||||
if (err != 1)
|
||||
continue;
|
||||
|
||||
tap->pid = tap_ctl_get_pid(tap->id);
|
||||
if (tap->pid < 0)
|
||||
continue;
|
||||
|
||||
n_taps++;
|
||||
}
|
||||
|
||||
qsort(tapv, n_taps, sizeof(struct tapdisk), _tap_tapdisk_cmp);
|
||||
|
||||
for (i = 0; i < n_taps; ++i)
|
||||
INIT_LIST_HEAD(&tapv[i].list);
|
||||
|
||||
done:
|
||||
*_tapv = tapv;
|
||||
err = 0;
|
||||
|
||||
out:
|
||||
if (glbuf.gl_pathv)
|
||||
globfree(&glbuf);
|
||||
|
||||
return err ? : n_taps;
|
||||
|
||||
fail:
|
||||
if (tapv)
|
||||
free(tapv);
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
struct tapdisk_list {
|
||||
int minor;
|
||||
int state;
|
||||
char *params;
|
||||
struct list_head entry;
|
||||
};
|
||||
|
||||
int
|
||||
_tap_ctl_list_tapdisk(int id, struct list_head *_list)
|
||||
{
|
||||
tapdisk_message_t message;
|
||||
struct list_head list;
|
||||
struct tapdisk_list *tl, *next;
|
||||
int err, sfd;
|
||||
|
||||
err = tap_ctl_connect_id(id, &sfd);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
memset(&message, 0, sizeof(message));
|
||||
message.type = TAPDISK_MESSAGE_LIST;
|
||||
message.cookie = -1;
|
||||
|
||||
err = tap_ctl_write_message(sfd, &message, 2);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
INIT_LIST_HEAD(&list);
|
||||
do {
|
||||
err = tap_ctl_read_message(sfd, &message, 2);
|
||||
if (err) {
|
||||
err = -EPROTO;
|
||||
break;
|
||||
}
|
||||
|
||||
if (message.u.list.count == 0)
|
||||
break;
|
||||
|
||||
tl = malloc(sizeof(struct tapdisk_list));
|
||||
if (!tl) {
|
||||
err = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
|
||||
tl->minor = message.u.list.minor;
|
||||
tl->state = message.u.list.state;
|
||||
if (message.u.list.path[0] != 0) {
|
||||
tl->params = strndup(message.u.list.path,
|
||||
sizeof(message.u.list.path));
|
||||
if (!tl->params) {
|
||||
err = -errno;
|
||||
break;
|
||||
}
|
||||
} else
|
||||
tl->params = NULL;
|
||||
|
||||
list_add(&tl->entry, &list);
|
||||
} while (1);
|
||||
|
||||
if (err)
|
||||
list_for_each_entry_safe(tl, next, &list, entry) {
|
||||
list_del(&tl->entry);
|
||||
free(tl->params);
|
||||
free(tl);
|
||||
}
|
||||
|
||||
close(sfd);
|
||||
list_splice(&list, _list);
|
||||
return err;
|
||||
}
|
||||
|
||||
void
|
||||
_tap_ctl_free_tapdisks(struct tapdisk *tapv, int n_taps)
|
||||
{
|
||||
struct tapdisk *tap;
|
||||
|
||||
for (tap = tapv; tap < &tapv[n_taps]; ++tap) {
|
||||
struct tapdisk_list *tl, *next;
|
||||
|
||||
list_for_each_entry_safe(tl, next, &tap->list, entry) {
|
||||
free(tl->params);
|
||||
free(tl);
|
||||
}
|
||||
}
|
||||
|
||||
free(tapv);
|
||||
}
|
||||
|
||||
int
|
||||
_tap_list_join3(int n_minors, int *minorv, int n_taps, struct tapdisk *tapv,
|
||||
tap_list_t ***_list)
|
||||
{
|
||||
tap_list_t **list, **_entry;
|
||||
int i, _m, err;
|
||||
|
||||
list = tap_ctl_alloc_list(n_minors + n_taps);
|
||||
if (!list) {
|
||||
err = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
_entry = list;
|
||||
|
||||
for (i = 0; i < n_taps; ++i) {
|
||||
struct tapdisk *tap = &tapv[i];
|
||||
struct tapdisk_list *tl;
|
||||
|
||||
/* orphaned tapdisk */
|
||||
if (list_empty(&tap->list)) {
|
||||
err = init_list(*_entry++, tap->id, tap->pid, -1, -1, NULL);
|
||||
if (err)
|
||||
goto fail;
|
||||
continue;
|
||||
}
|
||||
|
||||
list_for_each_entry(tl, &tap->list, entry) {
|
||||
|
||||
err = init_list(*_entry++,
|
||||
tap->id, tap->pid,
|
||||
tl->minor, tl->state, tl->params);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
if (tl->minor >= 0) {
|
||||
/* clear minor */
|
||||
for (_m = 0; _m < n_minors; ++_m) {
|
||||
if (minorv[_m] == tl->minor) {
|
||||
minorv[_m] = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* orphaned minors */
|
||||
for (_m = 0; _m < n_minors; ++_m) {
|
||||
int minor = minorv[_m];
|
||||
if (minor >= 0) {
|
||||
err = init_list(*_entry++, -1, -1, minor, -1, NULL);
|
||||
if (err)
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* free extraneous list entries */
|
||||
for (; *_entry != NULL; ++_entry) {
|
||||
free_list(*_entry);
|
||||
*_entry = NULL;
|
||||
}
|
||||
|
||||
*_list = list;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (list)
|
||||
tap_ctl_free_list(list);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int
|
||||
tap_ctl_list(tap_list_t ***list)
|
||||
{
|
||||
int n_taps, n_minors, err, *minorv;
|
||||
struct tapdisk *tapv, *tap;
|
||||
|
||||
n_taps = -1;
|
||||
n_minors = -1;
|
||||
|
||||
err = n_minors = _tap_ctl_find_minors(&minorv);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
err = n_taps = _tap_ctl_find_tapdisks(&tapv);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
for (tap = tapv; tap < &tapv[n_taps]; ++tap) {
|
||||
err = _tap_ctl_list_tapdisk(tap->id, &tap->list);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = _tap_list_join3(n_minors, minorv, n_taps, tapv, list);
|
||||
|
||||
out:
|
||||
if (n_taps > 0)
|
||||
_tap_ctl_free_tapdisks(tapv, n_taps);
|
||||
|
||||
if (n_minors > 0)
|
||||
free(minorv);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int
|
||||
tap_ctl_find(const char *type, const char *path, tap_list_t *tap)
|
||||
{
|
||||
tap_list_t **list, **_entry;
|
||||
int ret = -ENOENT, err;
|
||||
|
||||
err = tap_ctl_list(&list);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
for (_entry = list; *_entry != NULL; ++_entry) {
|
||||
tap_list_t *entry = *_entry;
|
||||
|
||||
if (type && (!entry->type || strcmp(entry->type, type)))
|
||||
continue;
|
||||
|
||||
if (path && (!entry->path || strcmp(entry->path, path)))
|
||||
continue;
|
||||
|
||||
*tap = *entry;
|
||||
tap->type = tap->path = NULL;
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
tap_ctl_free_list(list);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "tap-ctl.h"
|
||||
|
||||
int
|
||||
tap_ctl_blk_major(void)
|
||||
{
|
||||
FILE *devices;
|
||||
int rv, major;
|
||||
|
||||
devices = fopen("/proc/devices", "r");
|
||||
if (!devices) {
|
||||
rv = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
do {
|
||||
char buf[32], *s;
|
||||
int n, offset;
|
||||
|
||||
s = fgets(buf, sizeof(buf), devices);
|
||||
if (!s)
|
||||
break;
|
||||
|
||||
major = -ENODEV;
|
||||
offset = 0;
|
||||
|
||||
n = sscanf(buf, "%d tapdev%n", &major, &offset);
|
||||
if (n == 1 && offset)
|
||||
break;
|
||||
} while (1);
|
||||
|
||||
rv = major;
|
||||
|
||||
out:
|
||||
if (devices)
|
||||
fclose(devices);
|
||||
|
||||
return rv;
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include "tap-ctl.h"
|
||||
#include "blktaplib.h"
|
||||
|
||||
int
|
||||
tap_ctl_open(const int id, const int minor, const char *params)
|
||||
{
|
||||
int err;
|
||||
tapdisk_message_t message;
|
||||
|
||||
memset(&message, 0, sizeof(message));
|
||||
message.type = TAPDISK_MESSAGE_OPEN;
|
||||
message.cookie = minor;
|
||||
message.u.params.storage = TAPDISK_STORAGE_TYPE_DEFAULT;
|
||||
message.u.params.devnum = minor;
|
||||
|
||||
err = snprintf(message.u.params.path,
|
||||
sizeof(message.u.params.path) - 1, "%s", params);
|
||||
if (err >= sizeof(message.u.params.path)) {
|
||||
EPRINTF("name too long\n");
|
||||
return ENAMETOOLONG;
|
||||
}
|
||||
|
||||
err = tap_ctl_connect_send_and_receive(id, &message, 5);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
switch (message.type) {
|
||||
case TAPDISK_MESSAGE_OPEN_RSP:
|
||||
break;
|
||||
case TAPDISK_MESSAGE_ERROR:
|
||||
err = -message.u.response.error;
|
||||
EPRINTF("open failed, err %d\n", err);
|
||||
break;
|
||||
default:
|
||||
EPRINTF("got unexpected result '%s' from %d\n",
|
||||
tapdisk_message_name(message.type), id);
|
||||
err = EINVAL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "tap-ctl.h"
|
||||
|
||||
int
|
||||
tap_ctl_pause(const int id, const int minor)
|
||||
{
|
||||
int err;
|
||||
tapdisk_message_t message;
|
||||
|
||||
memset(&message, 0, sizeof(message));
|
||||
message.type = TAPDISK_MESSAGE_PAUSE;
|
||||
message.cookie = minor;
|
||||
|
||||
err = tap_ctl_connect_send_and_receive(id, &message, 5);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (message.type == TAPDISK_MESSAGE_PAUSE_RSP)
|
||||
err = message.u.response.error;
|
||||
else {
|
||||
err = EINVAL;
|
||||
EPRINTF("got unexpected result '%s' from %d\n",
|
||||
tapdisk_message_name(message.type), id);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "tap-ctl.h"
|
||||
#include "blktap2.h"
|
||||
|
||||
static pid_t
|
||||
__tap_ctl_spawn(int *readfd)
|
||||
{
|
||||
int err, child, channel[2];
|
||||
char *tapdisk;
|
||||
|
||||
if (pipe(channel)) {
|
||||
EPRINTF("pipe failed: %d\n", errno);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if ((child = fork()) == -1) {
|
||||
EPRINTF("fork failed: %d\n", errno);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (child) {
|
||||
close(channel[1]);
|
||||
*readfd = channel[0];
|
||||
return child;
|
||||
}
|
||||
|
||||
if (dup2(channel[1], STDOUT_FILENO) == -1) {
|
||||
EPRINTF("dup2 failed: %d\n", errno);
|
||||
exit(errno);
|
||||
}
|
||||
|
||||
if (dup2(channel[1], STDERR_FILENO) == -1) {
|
||||
EPRINTF("dup2 failed: %d\n", errno);
|
||||
exit(errno);
|
||||
}
|
||||
|
||||
close(channel[0]);
|
||||
close(channel[1]);
|
||||
|
||||
tapdisk = getenv("TAPDISK2");
|
||||
if (!tapdisk)
|
||||
tapdisk = "tapdisk2";
|
||||
|
||||
execlp(tapdisk, tapdisk, NULL);
|
||||
|
||||
EPRINTF("exec failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
pid_t
|
||||
tap_ctl_get_pid(const int id)
|
||||
{
|
||||
int err;
|
||||
tapdisk_message_t message;
|
||||
|
||||
memset(&message, 0, sizeof(message));
|
||||
message.type = TAPDISK_MESSAGE_PID;
|
||||
|
||||
err = tap_ctl_connect_send_and_receive(id, &message, 2);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return message.u.tapdisk_pid;
|
||||
}
|
||||
|
||||
static int
|
||||
tap_ctl_wait(pid_t child)
|
||||
{
|
||||
pid_t pid;
|
||||
int status;
|
||||
|
||||
pid = waitpid(child, &status, 0);
|
||||
if (pid < 0) {
|
||||
EPRINTF("wait(%d) failed, err %d\n", child, errno);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (WIFEXITED(status)) {
|
||||
int code = WEXITSTATUS(status);
|
||||
if (code)
|
||||
EPRINTF("tapdisk2[%d] failed, status %d\n", child, code);
|
||||
return -code;
|
||||
}
|
||||
|
||||
if (WIFSIGNALED(status)) {
|
||||
int signo = WTERMSIG(status);
|
||||
EPRINTF("tapdisk2[%d] killed by signal %d\n", child, signo);
|
||||
return -EINTR;
|
||||
}
|
||||
|
||||
EPRINTF("tapdisk2[%d]: unexpected status %#x\n", child, status);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
static int
|
||||
tap_ctl_get_child_id(int readfd)
|
||||
{
|
||||
int id;
|
||||
FILE *f;
|
||||
|
||||
f = fdopen(readfd, "r");
|
||||
if (!f) {
|
||||
EPRINTF("fdopen failed: %d\n", errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
if (fscanf(f, BLKTAP2_CONTROL_DIR"/"
|
||||
BLKTAP2_CONTROL_SOCKET"%d", &id) != 1) {
|
||||
errno = (errno ? : EINVAL);
|
||||
EPRINTF("parsing id failed: %d\n", errno);
|
||||
id = -1;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
return id;
|
||||
}
|
||||
|
||||
int
|
||||
tap_ctl_spawn(void)
|
||||
{
|
||||
pid_t child;
|
||||
int err, id, readfd;
|
||||
|
||||
readfd = -1;
|
||||
|
||||
child = __tap_ctl_spawn(&readfd);
|
||||
if (child < 0)
|
||||
return child;
|
||||
|
||||
err = tap_ctl_wait(child);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
id = tap_ctl_get_child_id(readfd);
|
||||
if (id < 0)
|
||||
EPRINTF("get_id failed, child %d err %d\n", child, errno);
|
||||
|
||||
return id;
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include "tap-ctl.h"
|
||||
|
||||
int
|
||||
tap_ctl_unpause(const int id, const int minor, const char *params)
|
||||
{
|
||||
int err;
|
||||
tapdisk_message_t message;
|
||||
|
||||
memset(&message, 0, sizeof(message));
|
||||
message.type = TAPDISK_MESSAGE_RESUME;
|
||||
message.cookie = minor;
|
||||
|
||||
if (params)
|
||||
strncpy(message.u.params.path, params,
|
||||
sizeof(message.u.params.path) - 1);
|
||||
|
||||
err = tap_ctl_connect_send_and_receive(id, &message, 15);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (message.type == TAPDISK_MESSAGE_RESUME_RSP)
|
||||
err = message.u.response.error;
|
||||
else {
|
||||
err = EINVAL;
|
||||
EPRINTF("got unexpected result '%s' from %d\n",
|
||||
tapdisk_message_name(message.type), id);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -1,815 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include "tap-ctl.h"
|
||||
|
||||
typedef int (*tap_ctl_func_t) (int, char **);
|
||||
|
||||
struct command {
|
||||
char *name;
|
||||
tap_ctl_func_t func;
|
||||
};
|
||||
|
||||
static void
|
||||
tap_cli_list_usage(FILE *stream)
|
||||
{
|
||||
fprintf(stream,
|
||||
"usage: list [-h] [-p pid] [-m minor] [-t type] [-f file]\n");
|
||||
}
|
||||
|
||||
static void
|
||||
tap_ctl_list_row(tap_list_t *entry)
|
||||
{
|
||||
char minor_str[10] = "-";
|
||||
char state_str[10] = "-";
|
||||
char pid_str[10] = "-";
|
||||
|
||||
if (entry->pid != -1)
|
||||
sprintf(pid_str, "%d", entry->pid);
|
||||
|
||||
if (entry->minor != -1)
|
||||
sprintf(minor_str, "%d", entry->minor);
|
||||
|
||||
if (entry->state != -1)
|
||||
sprintf(state_str, "%x", entry->state);
|
||||
|
||||
printf("%8s %2s %4s %10s %s\n",
|
||||
pid_str, minor_str, state_str,
|
||||
entry->type ? : "-", entry->path ? : "-");
|
||||
}
|
||||
|
||||
static void
|
||||
tap_ctl_list_dict(tap_list_t *entry)
|
||||
{
|
||||
int d = 0;
|
||||
|
||||
if (entry->pid != -1) {
|
||||
if (d) putc(' ', stdout);
|
||||
d = printf("pid=%d", entry->pid);
|
||||
}
|
||||
|
||||
if (entry->minor != -1) {
|
||||
if (d) putc(' ', stdout);
|
||||
d = printf("minor=%d", entry->minor);
|
||||
}
|
||||
|
||||
if (entry->state != -1) {
|
||||
if (d) putc(' ', stdout);
|
||||
d = printf("state=%d", entry->state);
|
||||
}
|
||||
|
||||
if (entry->type && entry->path) {
|
||||
if (d) putc(' ', stdout);
|
||||
d = printf("args=%s:%s", entry->type, entry->path);
|
||||
}
|
||||
|
||||
putc('\n', stdout);
|
||||
}
|
||||
|
||||
int
|
||||
tap_cli_list(int argc, char **argv)
|
||||
{
|
||||
tap_list_t **list, **_entry;
|
||||
int c, minor, tty, err;
|
||||
const char *type, *file;
|
||||
pid_t pid;
|
||||
|
||||
err = tap_ctl_list(&list);
|
||||
if (err)
|
||||
return -err;
|
||||
|
||||
pid = -1;
|
||||
minor = -1;
|
||||
type = NULL;
|
||||
file = NULL;
|
||||
|
||||
while ((c = getopt(argc, argv, "m:p:t:f:h")) != -1) {
|
||||
switch (c) {
|
||||
case 'm':
|
||||
minor = atoi(optarg);
|
||||
break;
|
||||
case 'p':
|
||||
pid = atoi(optarg);
|
||||
break;
|
||||
case 't':
|
||||
type = optarg;
|
||||
break;
|
||||
case 'f':
|
||||
file = optarg;
|
||||
break;
|
||||
case '?':
|
||||
goto usage;
|
||||
case 'h':
|
||||
tap_cli_list_usage(stdout);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
tty = isatty(STDOUT_FILENO);
|
||||
|
||||
for (_entry = list; *_entry != NULL; ++_entry) {
|
||||
tap_list_t *entry = *_entry;
|
||||
|
||||
if (minor >= 0 && entry->minor != minor)
|
||||
continue;
|
||||
|
||||
if (pid >= 0 && entry->pid != pid)
|
||||
continue;
|
||||
|
||||
if (type && (!entry->type || strcmp(entry->type, type)))
|
||||
continue;
|
||||
|
||||
if (file && (!entry->path || strcmp(entry->path, file)))
|
||||
continue;
|
||||
|
||||
if (tty)
|
||||
tap_ctl_list_row(entry);
|
||||
else
|
||||
tap_ctl_list_dict(entry);
|
||||
}
|
||||
|
||||
tap_ctl_free_list(list);
|
||||
|
||||
return 0;
|
||||
|
||||
usage:
|
||||
tap_cli_list_usage(stderr);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
static void
|
||||
tap_cli_allocate_usage(FILE *stream)
|
||||
{
|
||||
fprintf(stream, "usage: allocate [-d device name]>\n");
|
||||
}
|
||||
|
||||
static int
|
||||
tap_cli_allocate(int argc, char **argv)
|
||||
{
|
||||
char *devname;
|
||||
int c, minor, err;
|
||||
|
||||
devname = NULL;
|
||||
|
||||
optind = 0;
|
||||
while ((c = getopt(argc, argv, "d:h")) != -1) {
|
||||
switch (c) {
|
||||
case 'd':
|
||||
devname = optarg;
|
||||
break;
|
||||
case '?':
|
||||
goto usage;
|
||||
case 'h':
|
||||
tap_cli_allocate_usage(stdout);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
err = tap_ctl_allocate(&minor, &devname);
|
||||
if (!err)
|
||||
printf("%s\n", devname);
|
||||
|
||||
return err;
|
||||
|
||||
usage:
|
||||
tap_cli_allocate_usage(stderr);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
static void
|
||||
tap_cli_free_usage(FILE *stream)
|
||||
{
|
||||
fprintf(stream, "usage: free <-m minor>\n");
|
||||
}
|
||||
|
||||
static int
|
||||
tap_cli_free(int argc, char **argv)
|
||||
{
|
||||
int c, minor;
|
||||
|
||||
minor = -1;
|
||||
|
||||
optind = 0;
|
||||
while ((c = getopt(argc, argv, "m:h")) != -1) {
|
||||
switch (c) {
|
||||
case 'm':
|
||||
minor = atoi(optarg);
|
||||
break;
|
||||
case '?':
|
||||
goto usage;
|
||||
case 'h':
|
||||
tap_cli_free_usage(stdout);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (minor == -1)
|
||||
goto usage;
|
||||
|
||||
return tap_ctl_free(minor);
|
||||
|
||||
usage:
|
||||
tap_cli_free_usage(stderr);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
static void
|
||||
tap_cli_create_usage(FILE *stream)
|
||||
{
|
||||
fprintf(stream, "usage: create <-a args> [-d device name]\n");
|
||||
}
|
||||
|
||||
static int
|
||||
tap_cli_create(int argc, char **argv)
|
||||
{
|
||||
int c, err;
|
||||
char *args, *devname;
|
||||
|
||||
args = NULL;
|
||||
devname = NULL;
|
||||
|
||||
optind = 0;
|
||||
while ((c = getopt(argc, argv, "a:d:h")) != -1) {
|
||||
switch (c) {
|
||||
case 'a':
|
||||
args = optarg;
|
||||
break;
|
||||
case 'd':
|
||||
devname = optarg;
|
||||
break;
|
||||
case '?':
|
||||
goto usage;
|
||||
case 'h':
|
||||
tap_cli_create_usage(stdout);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!args)
|
||||
goto usage;
|
||||
|
||||
err = tap_ctl_create(args, &devname);
|
||||
if (!err)
|
||||
printf("%s\n", devname);
|
||||
|
||||
return err;
|
||||
|
||||
usage:
|
||||
tap_cli_create_usage(stderr);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
static void
|
||||
tap_cli_destroy_usage(FILE *stream)
|
||||
{
|
||||
fprintf(stream, "usage: destroy <-p pid> <-m minor>\n");
|
||||
}
|
||||
|
||||
static int
|
||||
tap_cli_destroy(int argc, char **argv)
|
||||
{
|
||||
int c, pid, minor;
|
||||
|
||||
pid = -1;
|
||||
minor = -1;
|
||||
|
||||
optind = 0;
|
||||
while ((c = getopt(argc, argv, "p:m:h")) != -1) {
|
||||
switch (c) {
|
||||
case 'p':
|
||||
pid = atoi(optarg);
|
||||
break;
|
||||
case 'm':
|
||||
minor = atoi(optarg);
|
||||
break;
|
||||
case '?':
|
||||
goto usage;
|
||||
case 'h':
|
||||
tap_cli_destroy_usage(stdout);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (pid == -1 || minor == -1)
|
||||
goto usage;
|
||||
|
||||
return tap_ctl_destroy(pid, minor);
|
||||
|
||||
usage:
|
||||
tap_cli_destroy_usage(stderr);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
static void
|
||||
tap_cli_spawn_usage(FILE *stream)
|
||||
{
|
||||
fprintf(stream, "usage: spawn\n");
|
||||
}
|
||||
|
||||
static int
|
||||
tap_cli_spawn(int argc, char **argv)
|
||||
{
|
||||
int c;
|
||||
pid_t task;
|
||||
|
||||
optind = 0;
|
||||
while ((c = getopt(argc, argv, "h")) != -1) {
|
||||
switch (c) {
|
||||
case '?':
|
||||
goto usage;
|
||||
case 'h':
|
||||
tap_cli_spawn_usage(stdout);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
task = tap_ctl_spawn();
|
||||
if (task < 0) {
|
||||
printf("spawn failed: %d\n", errno);
|
||||
return task;
|
||||
}
|
||||
|
||||
printf("tapdisk spawned with pid %d\n", task);
|
||||
return 0;
|
||||
|
||||
usage:
|
||||
tap_cli_spawn_usage(stderr);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
static void
|
||||
tap_cli_attach_usage(FILE *stream)
|
||||
{
|
||||
fprintf(stream, "usage: attach <-p pid> <-m minor>\n");
|
||||
}
|
||||
|
||||
static int
|
||||
tap_cli_attach(int argc, char **argv)
|
||||
{
|
||||
int c, pid, minor;
|
||||
|
||||
pid = -1;
|
||||
minor = -1;
|
||||
|
||||
optind = 0;
|
||||
while ((c = getopt(argc, argv, "p:m:h")) != -1) {
|
||||
switch (c) {
|
||||
case 'p':
|
||||
pid = atoi(optarg);
|
||||
break;
|
||||
case 'm':
|
||||
minor = atoi(optarg);
|
||||
break;
|
||||
case '?':
|
||||
goto usage;
|
||||
case 'h':
|
||||
tap_cli_attach_usage(stderr);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (pid == -1 || minor == -1)
|
||||
goto usage;
|
||||
|
||||
return tap_ctl_attach(pid, minor);
|
||||
|
||||
usage:
|
||||
tap_cli_attach_usage(stderr);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
static void
|
||||
tap_cli_detach_usage(FILE *stream)
|
||||
{
|
||||
fprintf(stream, "usage: detach <-p pid> <-m minor>\n");
|
||||
}
|
||||
|
||||
static int
|
||||
tap_cli_detach(int argc, char **argv)
|
||||
{
|
||||
int c, pid, minor;
|
||||
|
||||
pid = -1;
|
||||
minor = -1;
|
||||
|
||||
optind = 0;
|
||||
while ((c = getopt(argc, argv, "p:m:h")) != -1) {
|
||||
switch (c) {
|
||||
case 'p':
|
||||
pid = atoi(optarg);
|
||||
break;
|
||||
case 'm':
|
||||
minor = atoi(optarg);
|
||||
break;
|
||||
case '?':
|
||||
goto usage;
|
||||
case 'h':
|
||||
tap_cli_detach_usage(stdout);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (pid == -1 || minor == -1)
|
||||
goto usage;
|
||||
|
||||
return tap_ctl_detach(pid, minor);
|
||||
|
||||
usage:
|
||||
tap_cli_detach_usage(stderr);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
static void
|
||||
tap_cli_close_usage(FILE *stream)
|
||||
{
|
||||
fprintf(stream, "usage: close <-p pid> <-m minor> [-f force]\n");
|
||||
}
|
||||
|
||||
static int
|
||||
tap_cli_close(int argc, char **argv)
|
||||
{
|
||||
int c, pid, minor, force;
|
||||
|
||||
pid = -1;
|
||||
minor = -1;
|
||||
force = 0;
|
||||
|
||||
optind = 0;
|
||||
while ((c = getopt(argc, argv, "p:m:fh")) != -1) {
|
||||
switch (c) {
|
||||
case 'p':
|
||||
pid = atoi(optarg);
|
||||
break;
|
||||
case 'm':
|
||||
minor = atoi(optarg);
|
||||
break;
|
||||
case 'f':
|
||||
force = -1;
|
||||
break;
|
||||
case '?':
|
||||
goto usage;
|
||||
case 'h':
|
||||
tap_cli_close_usage(stdout);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (pid == -1 || minor == -1)
|
||||
goto usage;
|
||||
|
||||
return tap_ctl_close(pid, minor, force);
|
||||
|
||||
usage:
|
||||
tap_cli_close_usage(stderr);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
static void
|
||||
tap_cli_pause_usage(FILE *stream)
|
||||
{
|
||||
fprintf(stream, "usage: pause <-p pid> <-m minor>\n");
|
||||
}
|
||||
|
||||
static int
|
||||
tap_cli_pause(int argc, char **argv)
|
||||
{
|
||||
int c, pid, minor;
|
||||
|
||||
pid = -1;
|
||||
minor = -1;
|
||||
|
||||
optind = 0;
|
||||
while ((c = getopt(argc, argv, "p:m:h")) != -1) {
|
||||
switch (c) {
|
||||
case 'p':
|
||||
pid = atoi(optarg);
|
||||
break;
|
||||
case 'm':
|
||||
minor = atoi(optarg);
|
||||
break;
|
||||
case '?':
|
||||
goto usage;
|
||||
case 'h':
|
||||
tap_cli_pause_usage(stdout);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (pid == -1 || minor == -1)
|
||||
goto usage;
|
||||
|
||||
return tap_ctl_pause(pid, minor);
|
||||
|
||||
usage:
|
||||
tap_cli_pause_usage(stderr);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
static void
|
||||
tap_cli_unpause_usage(FILE *stream)
|
||||
{
|
||||
fprintf(stream, "usage: unpause <-p pid> <-m minor> [-a args]\n");
|
||||
}
|
||||
|
||||
int
|
||||
tap_cli_unpause(int argc, char **argv)
|
||||
{
|
||||
const char *args;
|
||||
int c, pid, minor;
|
||||
|
||||
pid = -1;
|
||||
minor = -1;
|
||||
args = NULL;
|
||||
|
||||
optind = 0;
|
||||
while ((c = getopt(argc, argv, "p:m:a:h")) != -1) {
|
||||
switch (c) {
|
||||
case 'p':
|
||||
pid = atoi(optarg);
|
||||
break;
|
||||
case 'm':
|
||||
minor = atoi(optarg);
|
||||
break;
|
||||
case 'a':
|
||||
args = optarg;
|
||||
break;
|
||||
case '?':
|
||||
goto usage;
|
||||
case 'h':
|
||||
tap_cli_unpause_usage(stdout);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (pid == -1 || minor == -1)
|
||||
goto usage;
|
||||
|
||||
return tap_ctl_unpause(pid, minor, args);
|
||||
|
||||
usage:
|
||||
tap_cli_unpause_usage(stderr);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
static void
|
||||
tap_cli_major_usage(FILE *stream)
|
||||
{
|
||||
fprintf(stream, "usage: major [-h]\n");
|
||||
}
|
||||
|
||||
static int
|
||||
tap_cli_major(int argc, char **argv)
|
||||
{
|
||||
int c, chr, major;
|
||||
|
||||
chr = 0;
|
||||
|
||||
while ((c = getopt(argc, argv, "bch")) != -1) {
|
||||
switch (c) {
|
||||
case 'b':
|
||||
chr = 0;
|
||||
break;
|
||||
case 'c':
|
||||
chr = 1;
|
||||
break;
|
||||
case '?':
|
||||
goto usage;
|
||||
case 'h':
|
||||
tap_cli_major_usage(stdout);
|
||||
return 0;
|
||||
default:
|
||||
goto usage;
|
||||
}
|
||||
}
|
||||
|
||||
if (chr)
|
||||
major = -EINVAL;
|
||||
else
|
||||
major = tap_ctl_blk_major();
|
||||
|
||||
if (major < 0)
|
||||
return -major;
|
||||
|
||||
printf("%d\n", major);
|
||||
|
||||
return 0;
|
||||
|
||||
usage:
|
||||
tap_cli_major_usage(stderr);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
static void
|
||||
tap_cli_open_usage(FILE *stream)
|
||||
{
|
||||
fprintf(stream, "usage: open <-p pid> <-m minor> <-a args>\n");
|
||||
}
|
||||
|
||||
static int
|
||||
tap_cli_open(int argc, char **argv)
|
||||
{
|
||||
const char *args;
|
||||
int c, pid, minor;
|
||||
|
||||
pid = -1;
|
||||
minor = -1;
|
||||
args = NULL;
|
||||
|
||||
optind = 0;
|
||||
while ((c = getopt(argc, argv, "a:m:p:h")) != -1) {
|
||||
switch (c) {
|
||||
case 'p':
|
||||
pid = atoi(optarg);
|
||||
break;
|
||||
case 'm':
|
||||
minor = atoi(optarg);
|
||||
break;
|
||||
case 'a':
|
||||
args = optarg;
|
||||
break;
|
||||
case '?':
|
||||
goto usage;
|
||||
case 'h':
|
||||
tap_cli_open_usage(stdout);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (pid == -1 || minor == -1 || !args)
|
||||
goto usage;
|
||||
|
||||
return tap_ctl_open(pid, minor, args);
|
||||
|
||||
usage:
|
||||
tap_cli_open_usage(stderr);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
static void
|
||||
tap_cli_check_usage(FILE *stream)
|
||||
{
|
||||
fprintf(stream, "usage: check\n"
|
||||
"(checks whether environment is suitable for tapdisk2)\n");
|
||||
}
|
||||
|
||||
static int
|
||||
tap_cli_check(int argc, char **argv)
|
||||
{
|
||||
int err;
|
||||
const char *msg;
|
||||
|
||||
if (argc != 1)
|
||||
goto usage;
|
||||
|
||||
err = tap_ctl_check(&msg);
|
||||
printf("%s\n", msg);
|
||||
|
||||
return err;
|
||||
|
||||
usage:
|
||||
tap_cli_check_usage(stderr);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
struct command commands[] = {
|
||||
{ .name = "list", .func = tap_cli_list },
|
||||
{ .name = "allocate", .func = tap_cli_allocate },
|
||||
{ .name = "free", .func = tap_cli_free },
|
||||
{ .name = "create", .func = tap_cli_create },
|
||||
{ .name = "destroy", .func = tap_cli_destroy },
|
||||
{ .name = "spawn", .func = tap_cli_spawn },
|
||||
{ .name = "attach", .func = tap_cli_attach },
|
||||
{ .name = "detach", .func = tap_cli_detach },
|
||||
{ .name = "open", .func = tap_cli_open },
|
||||
{ .name = "close", .func = tap_cli_close },
|
||||
{ .name = "pause", .func = tap_cli_pause },
|
||||
{ .name = "unpause", .func = tap_cli_unpause },
|
||||
{ .name = "major", .func = tap_cli_major },
|
||||
{ .name = "check", .func = tap_cli_check },
|
||||
};
|
||||
|
||||
#define print_commands() \
|
||||
do { \
|
||||
int i, n; \
|
||||
n = sizeof(commands) / sizeof(struct command); \
|
||||
printf("COMMAND := { "); \
|
||||
printf("%s", commands[0].name); \
|
||||
for (i = 1; i < n; i++) \
|
||||
printf(" | %s", commands[i].name); \
|
||||
printf(" }\n"); \
|
||||
} while (0)
|
||||
|
||||
void
|
||||
help(void)
|
||||
{
|
||||
printf("usage: tap-ctl COMMAND [OPTIONS]\n");
|
||||
print_commands();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
struct command *
|
||||
get_command(char *command)
|
||||
{
|
||||
int i, n;
|
||||
|
||||
if (strnlen(command, 25) >= 25)
|
||||
return NULL;
|
||||
|
||||
n = sizeof(commands) / sizeof (struct command);
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
if (!strcmp(command, commands[i].name))
|
||||
return &commands[i];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
char **cargv;
|
||||
const char *msg;
|
||||
struct command *cmd;
|
||||
int cargc, i, cnt, ret;
|
||||
|
||||
#ifdef CORE_DUMP
|
||||
#include <sys/resource.h>
|
||||
struct rlimit rlim;
|
||||
rlim.rlim_cur = RLIM_INFINITY;
|
||||
rlim.rlim_max = RLIM_INFINITY;
|
||||
if (setrlimit(RLIMIT_CORE, &rlim) < 0)
|
||||
PERROR("setrlimit failed");
|
||||
#endif
|
||||
|
||||
ret = 0;
|
||||
|
||||
if (argc < 2)
|
||||
help();
|
||||
|
||||
cargc = argc - 1;
|
||||
cmd = get_command(argv[1]);
|
||||
if (!cmd) {
|
||||
EPRINTF("invalid COMMAND %s", argv[1]);
|
||||
help();
|
||||
}
|
||||
|
||||
ret = tap_ctl_check(&msg);
|
||||
if (ret) {
|
||||
printf("%s\n", msg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
cargv = malloc(sizeof(char *) * cargc);
|
||||
if (!cargv)
|
||||
exit(ENOMEM);
|
||||
|
||||
cnt = 1;
|
||||
cargv[0] = cmd->name;
|
||||
for (i = 1; i < cargc; i++) {
|
||||
char *arg = argv[i + (argc - cargc)];
|
||||
|
||||
if (!strcmp(arg, "--debug")) {
|
||||
tap_ctl_debug = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
cargv[cnt++] = arg;
|
||||
}
|
||||
|
||||
ret = cmd->func(cnt, cargv);
|
||||
|
||||
free(cargv);
|
||||
|
||||
return (ret >= 0 ? ret : -ret);
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef __TAP_CTL_H__
|
||||
#define __TAP_CTL_H__
|
||||
|
||||
#include <syslog.h>
|
||||
#include <errno.h>
|
||||
#include <tapdisk-message.h>
|
||||
|
||||
extern int tap_ctl_debug;
|
||||
|
||||
#ifdef TAPCTL
|
||||
#define DBG(_f, _a...) \
|
||||
do { \
|
||||
if (tap_ctl_debug) \
|
||||
printf(_f, ##_a); \
|
||||
} while (0)
|
||||
|
||||
#define DPRINTF(_f, _a...) syslog(LOG_INFO, _f, ##_a)
|
||||
#define EPRINTF(_f, _a...) syslog(LOG_ERR, "tap-err:%s: " _f, __func__, ##_a)
|
||||
#define PERROR(_f, _a...) syslog(LOG_ERR, "tap-err:%s: " _f ": %s", __func__, ##_a, \
|
||||
strerror(errno))
|
||||
#endif
|
||||
|
||||
void tap_ctl_version(int *major, int *minor);
|
||||
int tap_ctl_kernel_version(int *major, int *minor);
|
||||
|
||||
int tap_ctl_check_blktap(const char **message);
|
||||
int tap_ctl_check_version(const char **message);
|
||||
int tap_ctl_check(const char **message);
|
||||
|
||||
int tap_ctl_connect(const char *path, int *socket);
|
||||
int tap_ctl_connect_id(int id, int *socket);
|
||||
int tap_ctl_read_message(int fd, tapdisk_message_t *message, int timeout);
|
||||
int tap_ctl_write_message(int fd, tapdisk_message_t *message, int timeout);
|
||||
int tap_ctl_send_and_receive(int fd, tapdisk_message_t *message, int timeout);
|
||||
int tap_ctl_connect_send_and_receive(int id,
|
||||
tapdisk_message_t *message, int timeout);
|
||||
char *tap_ctl_socket_name(int id);
|
||||
|
||||
typedef struct {
|
||||
int id;
|
||||
pid_t pid;
|
||||
int minor;
|
||||
int state;
|
||||
char *type;
|
||||
char *path;
|
||||
} tap_list_t;
|
||||
|
||||
int tap_ctl_get_driver_id(const char *handle);
|
||||
|
||||
int tap_ctl_list(tap_list_t ***list);
|
||||
void tap_ctl_free_list(tap_list_t **list);
|
||||
int tap_ctl_find(const char *type, const char *path, tap_list_t *tap);
|
||||
|
||||
int tap_ctl_allocate(int *minor, char **devname);
|
||||
int tap_ctl_free(const int minor);
|
||||
|
||||
int tap_ctl_create(const char *params, char **devname);
|
||||
int tap_ctl_destroy(const int id, const int minor);
|
||||
|
||||
int tap_ctl_spawn(void);
|
||||
pid_t tap_ctl_get_pid(const int id);
|
||||
|
||||
int tap_ctl_attach(const int id, const int minor);
|
||||
int tap_ctl_detach(const int id, const int minor);
|
||||
|
||||
int tap_ctl_open(const int id, const int minor, const char *params);
|
||||
int tap_ctl_close(const int id, const int minor, const int force);
|
||||
|
||||
int tap_ctl_pause(const int id, const int minor);
|
||||
int tap_ctl_unpause(const int id, const int minor, const char *params);
|
||||
|
||||
int tap_ctl_blk_major(void);
|
||||
|
||||
#endif
|
||||
@@ -1,113 +0,0 @@
|
||||
XEN_ROOT=$(CURDIR)/../../..
|
||||
BLKTAP_ROOT= ..
|
||||
include $(XEN_ROOT)/tools/Rules.mk
|
||||
|
||||
LIBVHDDIR = $(BLKTAP_ROOT)/vhd/lib
|
||||
|
||||
IBIN = tapdisk2 td-util tapdisk-client tapdisk-stream tapdisk-diff
|
||||
QCOW_UTIL = img2qcow qcow-create qcow2raw
|
||||
LOCK_UTIL = lock-util
|
||||
INST_DIR = $(sbindir)
|
||||
|
||||
CFLAGS += -Werror
|
||||
CFLAGS += -Wno-unused
|
||||
CFLAGS += -fno-strict-aliasing
|
||||
CFLAGS += -I$(BLKTAP_ROOT)/include -I$(BLKTAP_ROOT)/drivers
|
||||
CFLAGS += $(CFLAGS_libxenctrl)
|
||||
CFLAGS += -D_GNU_SOURCE
|
||||
CFLAGS += -DUSE_NFS_LOCKS
|
||||
# drivers/block-log.c incorrectly uses libxc internals
|
||||
CFLAGS += -I$(XEN_ROOT)/tools/libxc
|
||||
|
||||
ifeq ($(CONFIG_X86_64),y)
|
||||
CFLAGS += -fPIC
|
||||
endif
|
||||
|
||||
VHDLIBS := -L$(LIBVHDDIR) -lvhd
|
||||
|
||||
REMUS-OBJS := block-remus.o
|
||||
REMUS-OBJS += hashtable.o
|
||||
REMUS-OBJS += hashtable_itr.o
|
||||
REMUS-OBJS += hashtable_utility.o
|
||||
|
||||
tapdisk2 tapdisk-stream tapdisk-diff $(QCOW_UTIL): AIOLIBS := -laio
|
||||
|
||||
MEMSHRLIBS :=
|
||||
ifeq ($(CONFIG_Linux), __fixme__)
|
||||
MEMSHR_DIR = $(XEN_ROOT)/tools/memshr
|
||||
CFLAGS += -DMEMSHR
|
||||
CFLAGS += -I $(MEMSHR_DIR)
|
||||
MEMSHRLIBS += -L$(XEN_ROOT)/tools/libxc -lxenctrl $(MEMSHR_DIR)/libmemshr.a
|
||||
endif
|
||||
|
||||
ifeq ($(VHD_STATIC),y)
|
||||
td-util: CFLAGS += -static
|
||||
endif
|
||||
|
||||
PORTABLE-OBJS-y :=
|
||||
PORTABLE-OBJS-$(CONFIG_Linux) += blk_linux.o
|
||||
PORTABLE-OBJS-$(CONFIG_NetBSD) += blk_netbsd.o
|
||||
|
||||
TAP-OBJS-y := scheduler.o
|
||||
TAP-OBJS-y += tapdisk-vbd.o
|
||||
TAP-OBJS-y += tapdisk-control.o
|
||||
TAP-OBJS-y += tapdisk-image.o
|
||||
TAP-OBJS-y += tapdisk-driver.o
|
||||
TAP-OBJS-y += tapdisk-disktype.o
|
||||
TAP-OBJS-y += tapdisk-interface.o
|
||||
TAP-OBJS-y += tapdisk-server.o
|
||||
TAP-OBJS-y += tapdisk-queue.o
|
||||
TAP-OBJS-y += tapdisk-filter.o
|
||||
TAP-OBJS-y += tapdisk-log.o
|
||||
TAP-OBJS-y += tapdisk-utils.o
|
||||
TAP-OBJS-y += io-optimize.o
|
||||
TAP-OBJS-y += lock.o
|
||||
TAP-OBJS-y += $(PORTABLE-OBJS-y)
|
||||
|
||||
MISC-OBJS-y := atomicio.o
|
||||
|
||||
BLK-OBJS-y := block-aio.o
|
||||
BLK-OBJS-y += block-ram.o
|
||||
BLK-OBJS-y += block-cache.o
|
||||
BLK-OBJS-y += block-vhd.o
|
||||
BLK-OBJS-y += block-log.o
|
||||
BLK-OBJS-y += block-qcow.o
|
||||
BLK-OBJS-y += aes.o
|
||||
BLK-OBJS-y += md5.o
|
||||
BLK-OBJS-y += $(PORTABLE-OBJS-y)
|
||||
BLK-OBJS-y += $(REMUS-OBJS)
|
||||
|
||||
all: $(IBIN) lock-util qcow-util
|
||||
|
||||
|
||||
tapdisk2: $(TAP-OBJS-y) $(BLK-OBJS-y) $(MISC-OBJS-y) tapdisk2.o
|
||||
$(CC) -o $@ $^ $(LDFLAGS) -lrt -lz $(VHDLIBS) $(AIOLIBS) $(MEMSHRLIBS) -lm $(APPEND_LDFLAGS)
|
||||
|
||||
tapdisk-client: tapdisk-client.o
|
||||
$(CC) -o $@ $^ $(LDFLAGS) -lrt $(APPEND_LDFLAGS)
|
||||
|
||||
tapdisk-stream tapdisk-diff: %: %.o $(TAP-OBJS-y) $(BLK-OBJS-y)
|
||||
$(CC) -o $@ $^ $(LDFLAGS) -lrt -lz $(VHDLIBS) $(AIOLIBS) $(MEMSHRLIBS) -lm $(APPEND_LDFLAGS)
|
||||
|
||||
td-util: td.o tapdisk-utils.o tapdisk-log.o $(PORTABLE-OBJS-y)
|
||||
$(CC) -o $@ $^ $(LDFLAGS) $(VHDLIBS) $(APPEND_LDFLAGS)
|
||||
|
||||
lock-util: lock.c
|
||||
$(CC) $(CFLAGS) -DUTIL -o lock-util lock.c $(LDFLAGS) $(APPEND_LDFLAGS)
|
||||
|
||||
.PHONY: qcow-util
|
||||
qcow-util: img2qcow qcow2raw qcow-create
|
||||
|
||||
img2qcow qcow2raw qcow-create: %: %.o $(TAP-OBJS-y) $(BLK-OBJS-y)
|
||||
$(CC) -o $@ $^ $(LDFLAGS) -lrt -lz $(VHDLIBS) $(AIOLIBS) $(MEMSHRLIBS) -lm $(APPEND_LDFLAGS)
|
||||
|
||||
install: all
|
||||
$(INSTALL_DIR) -p $(DESTDIR)$(INST_DIR)
|
||||
$(INSTALL_PROG) $(IBIN) $(LOCK_UTIL) $(QCOW_UTIL) $(DESTDIR)$(INST_DIR)
|
||||
|
||||
clean:
|
||||
rm -rf .*.d *.o *~ xen TAGS $(IBIN) $(LIB) $(LOCK_UTIL) $(QCOW_UTIL)
|
||||
|
||||
distclean: clean
|
||||
|
||||
.PHONY: clean install distclean
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,28 +0,0 @@
|
||||
#ifndef QEMU_AES_H
|
||||
#define QEMU_AES_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define AES_MAXNR 14
|
||||
#define AES_BLOCK_SIZE 16
|
||||
|
||||
struct aes_key_st {
|
||||
uint32_t rd_key[4 *(AES_MAXNR + 1)];
|
||||
int rounds;
|
||||
};
|
||||
typedef struct aes_key_st AES_KEY;
|
||||
|
||||
int AES_set_encrypt_key(const unsigned char *userKey, const int bits,
|
||||
AES_KEY *key);
|
||||
int AES_set_decrypt_key(const unsigned char *userKey, const int bits,
|
||||
AES_KEY *key);
|
||||
|
||||
void AES_encrypt(const unsigned char *in, unsigned char *out,
|
||||
const AES_KEY *key);
|
||||
void AES_decrypt(const unsigned char *in, unsigned char *out,
|
||||
const AES_KEY *key);
|
||||
void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,
|
||||
const unsigned long length, const AES_KEY *key,
|
||||
unsigned char *ivec, const int enc);
|
||||
|
||||
#endif
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2005 Anil Madhavapeddy. All rights reserved.
|
||||
* Copyright (c) 1995,1999 Theo de Raadt. All rights reserved.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include "atomicio.h"
|
||||
|
||||
/*
|
||||
* ensure all of data on socket comes through. f==read || f==vwrite
|
||||
*/
|
||||
size_t
|
||||
atomicio(f, fd, _s, n)
|
||||
ssize_t (*f) (int, void *, size_t);
|
||||
int fd;
|
||||
void *_s;
|
||||
size_t n;
|
||||
{
|
||||
char *s = _s;
|
||||
size_t pos = 0;
|
||||
ssize_t res;
|
||||
|
||||
while (n > pos) {
|
||||
res = (f) (fd, s + pos, n - pos);
|
||||
switch (res) {
|
||||
case -1:
|
||||
if (errno == EINTR || errno == EAGAIN)
|
||||
continue;
|
||||
return 0;
|
||||
case 0:
|
||||
errno = EPIPE;
|
||||
return pos;
|
||||
default:
|
||||
pos += (size_t)res;
|
||||
}
|
||||
}
|
||||
return (pos);
|
||||
}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
int blk_getimagesize(int fd, uint64_t *size);
|
||||
int blk_getsectorsize(int fd, uint64_t *sector_size);
|
||||
|
||||
#ifndef O_LARGEFILE
|
||||
#define O_LARGEFILE 0
|
||||
#endif
|
||||
@@ -1,43 +0,0 @@
|
||||
#include <inttypes.h>
|
||||
#include <errno.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mount.h>
|
||||
#include "tapdisk.h"
|
||||
#include "blk.h"
|
||||
|
||||
int blk_getimagesize(int fd, uint64_t *size)
|
||||
{
|
||||
int rc;
|
||||
|
||||
*size = 0;
|
||||
rc = ioctl(fd, BLKGETSIZE, size);
|
||||
if (rc) {
|
||||
DPRINTF("ERR: BLKGETSIZE failed, couldn't stat image");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int blk_getsectorsize(int fd, uint64_t *sector_size)
|
||||
{
|
||||
#if defined(BLKSSZGET)
|
||||
int rc;
|
||||
|
||||
*sector_size = DEFAULT_SECTOR_SIZE;
|
||||
rc = ioctl(fd, BLKSSZGET, sector_size);
|
||||
if (rc) {
|
||||
DPRINTF("ERR: BLKSSZGET failed. Falling back to use default sector size");
|
||||
*sector_size = DEFAULT_SECTOR_SIZE;
|
||||
}
|
||||
|
||||
if (*sector_size != DEFAULT_SECTOR_SIZE)
|
||||
DPRINTF("Note: sector size is %"PRIu64" (not %u)\n",
|
||||
*sector_size, DEFAULT_SECTOR_SIZE);
|
||||
#else
|
||||
*sector_size = DEFAULT_SECTOR_SIZE;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
#include <sys/param.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/disklabel.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include "tapdisk.h"
|
||||
#include "blk.h"
|
||||
|
||||
int blk_getimagesize(int fd, uint64_t *size)
|
||||
{
|
||||
int rc;
|
||||
struct disklabel dl;
|
||||
|
||||
*size = 0;
|
||||
rc = ioctl(fd, DIOCGDINFO, &dl);
|
||||
if (rc) {
|
||||
DPRINTF("ERR: DIOCGDINFO failed, couldn't stat image");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*size = dl.d_secsize * dl.d_secpercyl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int blk_getsectorsize(int fd, uint64_t *sector_size)
|
||||
{
|
||||
int rc;
|
||||
struct disklabel dl;
|
||||
|
||||
*sector_size = DEV_BSIZE;
|
||||
rc = ioctl(fd, DIOCGDINFO, &dl);
|
||||
if (rc) {
|
||||
DPRINTF("ERR: DIOCGDINFO failed, couldn't stat image");
|
||||
return 0; /* fallback to DEV_BSIZE */
|
||||
}
|
||||
|
||||
*sector_size = dl.d_secsize;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,258 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2007, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "blk.h"
|
||||
#include "tapdisk.h"
|
||||
#include "tapdisk-driver.h"
|
||||
#include "tapdisk-interface.h"
|
||||
|
||||
#define MAX_AIO_REQS TAPDISK_DATA_REQUESTS
|
||||
|
||||
struct tdaio_state;
|
||||
|
||||
struct aio_request {
|
||||
td_request_t treq;
|
||||
struct tiocb tiocb;
|
||||
struct tdaio_state *state;
|
||||
};
|
||||
|
||||
struct tdaio_state {
|
||||
int fd;
|
||||
td_driver_t *driver;
|
||||
|
||||
int aio_free_count;
|
||||
struct aio_request aio_requests[MAX_AIO_REQS];
|
||||
struct aio_request *aio_free_list[MAX_AIO_REQS];
|
||||
};
|
||||
|
||||
/*Get Image size, secsize*/
|
||||
static int tdaio_get_image_info(int fd, td_disk_info_t *info)
|
||||
{
|
||||
int ret;
|
||||
long size;
|
||||
unsigned long total_size;
|
||||
struct statvfs statBuf;
|
||||
struct stat stat;
|
||||
|
||||
ret = fstat(fd, &stat);
|
||||
if (ret != 0) {
|
||||
DPRINTF("ERROR: fstat failed, Couldn't stat image");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (S_ISBLK(stat.st_mode)) {
|
||||
/*Accessing block device directly*/
|
||||
info->size = 0;
|
||||
if (blk_getimagesize(fd, &info->size) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
DPRINTF("Image size: \n\tpre sector_shift [%llu]\n\tpost "
|
||||
"sector_shift [%llu]\n",
|
||||
(long long unsigned)(info->size << SECTOR_SHIFT),
|
||||
(long long unsigned)info->size);
|
||||
|
||||
/*Get the sector size*/
|
||||
if (blk_getsectorsize(fd, &info->sector_size) != 0)
|
||||
info->sector_size = DEFAULT_SECTOR_SIZE;
|
||||
|
||||
} else {
|
||||
/*Local file? try fstat instead*/
|
||||
info->size = (stat.st_size >> SECTOR_SHIFT);
|
||||
info->sector_size = DEFAULT_SECTOR_SIZE;
|
||||
DPRINTF("Image size: \n\tpre sector_shift [%llu]\n\tpost "
|
||||
"sector_shift [%llu]\n",
|
||||
(long long unsigned)(info->size << SECTOR_SHIFT),
|
||||
(long long unsigned)info->size);
|
||||
}
|
||||
|
||||
if (info->size == 0) {
|
||||
info->size =((uint64_t) 16836057);
|
||||
info->sector_size = DEFAULT_SECTOR_SIZE;
|
||||
}
|
||||
info->info = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Open the disk file and initialize aio state. */
|
||||
int tdaio_open(td_driver_t *driver, const char *name, td_flag_t flags)
|
||||
{
|
||||
int i, fd, ret, o_flags;
|
||||
struct tdaio_state *prv;
|
||||
|
||||
ret = 0;
|
||||
prv = (struct tdaio_state *)driver->data;
|
||||
|
||||
DPRINTF("block-aio open('%s')", name);
|
||||
|
||||
memset(prv, 0, sizeof(struct tdaio_state));
|
||||
|
||||
prv->aio_free_count = MAX_AIO_REQS;
|
||||
for (i = 0; i < MAX_AIO_REQS; i++)
|
||||
prv->aio_free_list[i] = &prv->aio_requests[i];
|
||||
|
||||
/* Open the file */
|
||||
o_flags = O_DIRECT | O_LARGEFILE |
|
||||
((flags & TD_OPEN_RDONLY) ? O_RDONLY : O_RDWR);
|
||||
fd = open(name, o_flags);
|
||||
|
||||
if ( (fd == -1) && (errno == EINVAL) ) {
|
||||
|
||||
/* Maybe O_DIRECT isn't supported. */
|
||||
o_flags &= ~O_DIRECT;
|
||||
fd = open(name, o_flags);
|
||||
if (fd != -1) DPRINTF("WARNING: Accessing image without"
|
||||
"O_DIRECT! (%s)\n", name);
|
||||
|
||||
} else if (fd != -1) DPRINTF("open(%s) with O_DIRECT\n", name);
|
||||
|
||||
if (fd == -1) {
|
||||
DPRINTF("Unable to open [%s] (%d)!\n", name, 0 - errno);
|
||||
ret = 0 - errno;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = tdaio_get_image_info(fd, &driver->info);
|
||||
if (ret) {
|
||||
close(fd);
|
||||
goto done;
|
||||
}
|
||||
|
||||
prv->fd = fd;
|
||||
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void tdaio_complete(void *arg, struct tiocb *tiocb, int err)
|
||||
{
|
||||
struct aio_request *aio = (struct aio_request *)arg;
|
||||
struct tdaio_state *prv = aio->state;
|
||||
|
||||
td_complete_request(aio->treq, err);
|
||||
prv->aio_free_list[prv->aio_free_count++] = aio;
|
||||
}
|
||||
|
||||
void tdaio_queue_read(td_driver_t *driver, td_request_t treq)
|
||||
{
|
||||
int size;
|
||||
uint64_t offset;
|
||||
struct aio_request *aio;
|
||||
struct tdaio_state *prv;
|
||||
|
||||
prv = (struct tdaio_state *)driver->data;
|
||||
size = treq.secs * driver->info.sector_size;
|
||||
offset = treq.sec * (uint64_t)driver->info.sector_size;
|
||||
|
||||
if (prv->aio_free_count == 0)
|
||||
goto fail;
|
||||
|
||||
aio = prv->aio_free_list[--prv->aio_free_count];
|
||||
aio->treq = treq;
|
||||
aio->state = prv;
|
||||
|
||||
td_prep_read(&aio->tiocb, prv->fd, treq.buf,
|
||||
size, offset, tdaio_complete, aio);
|
||||
td_queue_tiocb(driver, &aio->tiocb);
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
td_complete_request(treq, -EBUSY);
|
||||
}
|
||||
|
||||
void tdaio_queue_write(td_driver_t *driver, td_request_t treq)
|
||||
{
|
||||
int size;
|
||||
uint64_t offset;
|
||||
struct aio_request *aio;
|
||||
struct tdaio_state *prv;
|
||||
|
||||
prv = (struct tdaio_state *)driver->data;
|
||||
size = treq.secs * driver->info.sector_size;
|
||||
offset = treq.sec * (uint64_t)driver->info.sector_size;
|
||||
|
||||
if (prv->aio_free_count == 0)
|
||||
goto fail;
|
||||
|
||||
aio = prv->aio_free_list[--prv->aio_free_count];
|
||||
aio->treq = treq;
|
||||
aio->state = prv;
|
||||
|
||||
td_prep_write(&aio->tiocb, prv->fd, treq.buf,
|
||||
size, offset, tdaio_complete, aio);
|
||||
td_queue_tiocb(driver, &aio->tiocb);
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
td_complete_request(treq, -EBUSY);
|
||||
}
|
||||
|
||||
int tdaio_close(td_driver_t *driver)
|
||||
{
|
||||
struct tdaio_state *prv = (struct tdaio_state *)driver->data;
|
||||
|
||||
close(prv->fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tdaio_get_parent_id(td_driver_t *driver, td_disk_id_t *id)
|
||||
{
|
||||
return TD_NO_PARENT;
|
||||
}
|
||||
|
||||
int tdaio_validate_parent(td_driver_t *driver,
|
||||
td_driver_t *pdriver, td_flag_t flags)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
struct tap_disk tapdisk_aio = {
|
||||
.disk_type = "tapdisk_aio",
|
||||
.flags = 0,
|
||||
.private_data_size = sizeof(struct tdaio_state),
|
||||
.td_open = tdaio_open,
|
||||
.td_close = tdaio_close,
|
||||
.td_queue_read = tdaio_queue_read,
|
||||
.td_queue_write = tdaio_queue_write,
|
||||
.td_get_parent_id = tdaio_get_parent_id,
|
||||
.td_validate_parent = tdaio_validate_parent,
|
||||
.td_debug = NULL,
|
||||
};
|
||||
@@ -1,787 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "tapdisk.h"
|
||||
#include "tapdisk-utils.h"
|
||||
#include "tapdisk-driver.h"
|
||||
#include "tapdisk-server.h"
|
||||
#include "tapdisk-interface.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DBG(_f, _a...) tlog_write(TLOG_DBG, _f, ##_a)
|
||||
#else
|
||||
#define DBG(_f, _a...) ((void)0)
|
||||
#endif
|
||||
|
||||
#define WARN(_f, _a...) tlog_write(TLOG_WARN, _f, ##_a)
|
||||
|
||||
#define RADIX_TREE_PAGE_SHIFT 12 /* 4K pages */
|
||||
#define RADIX_TREE_PAGE_SIZE (1 << RADIX_TREE_PAGE_SHIFT)
|
||||
|
||||
#define RADIX_TREE_NODE_SHIFT 9 /* 512B nodes */
|
||||
#define RADIX_TREE_NODE_SIZE (1 << RADIX_TREE_NODE_SHIFT)
|
||||
#define RADIX_TREE_NODE_MASK (RADIX_TREE_NODE_SIZE - 1)
|
||||
|
||||
#define BLOCK_CACHE_NODES_PER_PAGE (1 << (RADIX_TREE_PAGE_SHIFT - RADIX_TREE_NODE_SHIFT))
|
||||
|
||||
#define BLOCK_CACHE_MAX_SIZE (10 << 20) /* 100MB cache */
|
||||
#define BLOCK_CACHE_REQUESTS (TAPDISK_DATA_REQUESTS << 3)
|
||||
#define BLOCK_CACHE_PAGE_IDLETIME 60
|
||||
|
||||
typedef struct radix_tree radix_tree_t;
|
||||
typedef struct radix_tree_node radix_tree_node_t;
|
||||
typedef struct radix_tree_link radix_tree_link_t;
|
||||
typedef struct radix_tree_leaf radix_tree_leaf_t;
|
||||
typedef struct radix_tree_page radix_tree_page_t;
|
||||
|
||||
typedef struct block_cache block_cache_t;
|
||||
typedef struct block_cache_request block_cache_request_t;
|
||||
typedef struct block_cache_stats block_cache_stats_t;
|
||||
|
||||
struct radix_tree_page {
|
||||
char *buf;
|
||||
size_t size;
|
||||
uint64_t sec;
|
||||
radix_tree_link_t *owners[BLOCK_CACHE_NODES_PER_PAGE];
|
||||
};
|
||||
|
||||
struct radix_tree_leaf {
|
||||
radix_tree_page_t *page;
|
||||
char *buf;
|
||||
};
|
||||
|
||||
struct radix_tree_link {
|
||||
uint32_t time;
|
||||
union {
|
||||
radix_tree_node_t *next;
|
||||
radix_tree_leaf_t leaf;
|
||||
} u;
|
||||
};
|
||||
|
||||
struct radix_tree_node {
|
||||
int height;
|
||||
radix_tree_link_t links[RADIX_TREE_NODE_SIZE];
|
||||
};
|
||||
|
||||
struct radix_tree {
|
||||
int height;
|
||||
uint64_t size;
|
||||
uint32_t nodes;
|
||||
radix_tree_node_t *root;
|
||||
|
||||
block_cache_t *cache;
|
||||
};
|
||||
|
||||
struct block_cache_request {
|
||||
int err;
|
||||
char *buf;
|
||||
uint64_t secs;
|
||||
td_request_t treq;
|
||||
block_cache_t *cache;
|
||||
};
|
||||
|
||||
struct block_cache_stats {
|
||||
uint64_t reads;
|
||||
uint64_t hits;
|
||||
uint64_t misses;
|
||||
uint64_t prunes;
|
||||
};
|
||||
|
||||
struct block_cache {
|
||||
int ptype;
|
||||
char *name;
|
||||
|
||||
uint64_t sectors;
|
||||
|
||||
block_cache_request_t requests[BLOCK_CACHE_REQUESTS];
|
||||
block_cache_request_t *request_free_list[BLOCK_CACHE_REQUESTS];
|
||||
int requests_free;
|
||||
|
||||
event_id_t timeout_id;
|
||||
|
||||
radix_tree_t tree;
|
||||
|
||||
block_cache_stats_t stats;
|
||||
};
|
||||
|
||||
static inline uint64_t
|
||||
radix_tree_calculate_size(int height)
|
||||
{
|
||||
return (uint64_t)RADIX_TREE_NODE_SIZE <<
|
||||
(height * RADIX_TREE_NODE_SHIFT);
|
||||
}
|
||||
|
||||
static inline int
|
||||
radix_tree_calculate_height(uint64_t sectors)
|
||||
{
|
||||
int height;
|
||||
uint64_t tree_size;
|
||||
|
||||
height = 1; /* always allocate root node */
|
||||
tree_size = radix_tree_calculate_size(height);
|
||||
while (sectors > tree_size)
|
||||
tree_size = radix_tree_calculate_size(++height);
|
||||
|
||||
return height;
|
||||
}
|
||||
|
||||
static inline int
|
||||
radix_tree_index(radix_tree_node_t *node, uint64_t sector)
|
||||
{
|
||||
return ((sector >> (node->height * RADIX_TREE_NODE_SHIFT)) &
|
||||
RADIX_TREE_NODE_MASK);
|
||||
}
|
||||
|
||||
static inline int
|
||||
radix_tree_node_contains_leaves(radix_tree_t *tree, radix_tree_node_t *node)
|
||||
{
|
||||
return (node->height == 0);
|
||||
}
|
||||
|
||||
static inline int
|
||||
radix_tree_node_is_root(radix_tree_t *tree, radix_tree_node_t *node)
|
||||
{
|
||||
return (node->height == tree->height);
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
radix_tree_size(radix_tree_t *tree)
|
||||
{
|
||||
return tree->size + tree->nodes * sizeof(radix_tree_node_t);
|
||||
}
|
||||
|
||||
static inline void
|
||||
radix_tree_clear_link(radix_tree_link_t *link)
|
||||
{
|
||||
if (link)
|
||||
memset(link, 0, sizeof(radix_tree_link_t));
|
||||
}
|
||||
|
||||
static inline radix_tree_node_t *
|
||||
radix_tree_allocate_node(radix_tree_t *tree, int height)
|
||||
{
|
||||
radix_tree_node_t *node;
|
||||
|
||||
node = calloc(1, sizeof(radix_tree_node_t));
|
||||
if (!node)
|
||||
return NULL;
|
||||
|
||||
node->height = height;
|
||||
tree->nodes++;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static inline radix_tree_node_t *
|
||||
radix_tree_allocate_child_node(radix_tree_t *tree, radix_tree_node_t *parent)
|
||||
{
|
||||
return radix_tree_allocate_node(tree, parent->height - 1);
|
||||
}
|
||||
|
||||
void
|
||||
radix_tree_free_node(radix_tree_t *tree, radix_tree_node_t *node)
|
||||
{
|
||||
if (!node)
|
||||
return;
|
||||
|
||||
free(node);
|
||||
tree->nodes--;
|
||||
}
|
||||
|
||||
static inline radix_tree_page_t *
|
||||
radix_tree_allocate_page(radix_tree_t *tree,
|
||||
char *buf, uint64_t sec, size_t size)
|
||||
{
|
||||
radix_tree_page_t *page;
|
||||
|
||||
page = calloc(1, sizeof(radix_tree_page_t));
|
||||
if (!page)
|
||||
return NULL;
|
||||
|
||||
page->buf = buf;
|
||||
page->sec = sec;
|
||||
page->size = size;
|
||||
tree->size += size;
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
static inline void
|
||||
radix_tree_free_page(radix_tree_t *tree, radix_tree_page_t *page)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < page->size >> RADIX_TREE_NODE_SHIFT; i++)
|
||||
DBG("%s: ejecting sector 0x%llx\n",
|
||||
tree->cache->name, page->sec + i);
|
||||
|
||||
tree->cache->stats.prunes += (page->size >> RADIX_TREE_NODE_SHIFT);
|
||||
tree->size -= page->size;
|
||||
free(page->buf);
|
||||
free(page);
|
||||
}
|
||||
|
||||
/*
|
||||
* remove a leaf and the shared radix_tree_page_t containing its buffer.
|
||||
* leaves are deleted, nodes are not; gc will reap the nodes later.
|
||||
*/
|
||||
static void
|
||||
radix_tree_remove_page(radix_tree_t *tree, radix_tree_page_t *page)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!page)
|
||||
return;
|
||||
|
||||
for (i = 0; i < BLOCK_CACHE_NODES_PER_PAGE; i++)
|
||||
radix_tree_clear_link(page->owners[i]);
|
||||
|
||||
radix_tree_free_page(tree, page);
|
||||
}
|
||||
|
||||
static void
|
||||
radix_tree_insert_leaf(radix_tree_t *tree, radix_tree_link_t *link,
|
||||
radix_tree_page_t *page, off_t off)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (off + RADIX_TREE_NODE_SIZE > page->size)
|
||||
return;
|
||||
|
||||
for (i = 0; i < BLOCK_CACHE_NODES_PER_PAGE; i++) {
|
||||
if (page->owners[i])
|
||||
continue;
|
||||
|
||||
page->owners[i] = link;
|
||||
link->u.leaf.page = page;
|
||||
link->u.leaf.buf = page->buf + off;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static char *
|
||||
radix_tree_find_leaf(radix_tree_t *tree, uint64_t sector)
|
||||
{
|
||||
int idx;
|
||||
struct timeval now;
|
||||
radix_tree_link_t *link;
|
||||
radix_tree_node_t *node;
|
||||
|
||||
node = tree->root;
|
||||
gettimeofday(&now, NULL);
|
||||
|
||||
do {
|
||||
idx = radix_tree_index(node, sector);
|
||||
link = node->links + idx;
|
||||
link->time = now.tv_sec;
|
||||
|
||||
if (radix_tree_node_contains_leaves(tree, node))
|
||||
return link->u.leaf.buf;
|
||||
|
||||
if (!link->u.next)
|
||||
return NULL;
|
||||
|
||||
node = link->u.next;
|
||||
} while (1);
|
||||
}
|
||||
|
||||
static char *
|
||||
radix_tree_add_leaf(radix_tree_t *tree, uint64_t sector,
|
||||
radix_tree_page_t *page, off_t off)
|
||||
{
|
||||
int idx;
|
||||
struct timeval now;
|
||||
radix_tree_link_t *link;
|
||||
radix_tree_node_t *node;
|
||||
|
||||
node = tree->root;
|
||||
gettimeofday(&now, NULL);
|
||||
|
||||
do {
|
||||
idx = radix_tree_index(node, sector);
|
||||
link = node->links + idx;
|
||||
link->time = now.tv_sec;
|
||||
|
||||
if (radix_tree_node_contains_leaves(tree, node)) {
|
||||
radix_tree_remove_page(tree, link->u.leaf.page);
|
||||
radix_tree_insert_leaf(tree, link, page, off);
|
||||
return link->u.leaf.buf;
|
||||
}
|
||||
|
||||
if (!link->u.next) {
|
||||
link->u.next = radix_tree_allocate_child_node(tree,
|
||||
node);
|
||||
if (!link->u.next)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
node = link->u.next;
|
||||
} while (1);
|
||||
}
|
||||
|
||||
static int
|
||||
radix_tree_add_leaves(radix_tree_t *tree, char *buf,
|
||||
uint64_t sector, uint64_t sectors)
|
||||
{
|
||||
int i;
|
||||
radix_tree_page_t *page;
|
||||
|
||||
page = radix_tree_allocate_page(tree, buf, sector,
|
||||
sectors << RADIX_TREE_NODE_SHIFT);
|
||||
if (!page)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < sectors; i++)
|
||||
if (!radix_tree_add_leaf(tree, sector + i,
|
||||
page, (i << RADIX_TREE_NODE_SHIFT)))
|
||||
goto fail;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
page->buf = NULL;
|
||||
radix_tree_remove_page(tree, page);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void
|
||||
radix_tree_delete_branch(radix_tree_t *tree, radix_tree_node_t *node)
|
||||
{
|
||||
int i;
|
||||
radix_tree_link_t *link;
|
||||
|
||||
if (!node)
|
||||
return;
|
||||
|
||||
for (i = 0; i < RADIX_TREE_NODE_SIZE; i++) {
|
||||
link = node->links + i;
|
||||
|
||||
if (radix_tree_node_contains_leaves(tree, node))
|
||||
radix_tree_remove_page(tree, link->u.leaf.page);
|
||||
else
|
||||
radix_tree_delete_branch(tree, link->u.next);
|
||||
|
||||
radix_tree_clear_link(link);
|
||||
}
|
||||
|
||||
radix_tree_free_node(tree, node);
|
||||
}
|
||||
|
||||
static inline void
|
||||
radix_tree_destroy(radix_tree_t *tree)
|
||||
{
|
||||
radix_tree_delete_branch(tree, tree->root);
|
||||
tree->root = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* returns 1 if @node is empty after pruning, 0 otherwise
|
||||
*/
|
||||
static int
|
||||
radix_tree_prune_branch(radix_tree_t *tree,
|
||||
radix_tree_node_t *node, uint32_t now)
|
||||
{
|
||||
int i, empty;
|
||||
radix_tree_link_t *link;
|
||||
|
||||
empty = 1;
|
||||
if (!node)
|
||||
return empty;
|
||||
|
||||
for (i = 0; i < RADIX_TREE_NODE_SIZE; i++) {
|
||||
link = node->links + i;
|
||||
|
||||
if (now - link->time < BLOCK_CACHE_PAGE_IDLETIME) {
|
||||
if (radix_tree_node_contains_leaves(tree, node)) {
|
||||
empty = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (radix_tree_prune_branch(tree, link->u.next, now))
|
||||
radix_tree_clear_link(link);
|
||||
else
|
||||
empty = 0;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (radix_tree_node_contains_leaves(tree, node))
|
||||
radix_tree_remove_page(tree, link->u.leaf.page);
|
||||
else
|
||||
radix_tree_delete_branch(tree, link->u.next);
|
||||
|
||||
radix_tree_clear_link(link);
|
||||
}
|
||||
|
||||
if (empty && !radix_tree_node_is_root(tree, node))
|
||||
radix_tree_free_node(tree, node);
|
||||
|
||||
return empty;
|
||||
}
|
||||
|
||||
/*
|
||||
* walk tree and free any node that has been idle for too long
|
||||
*/
|
||||
static void
|
||||
radix_tree_prune(radix_tree_t *tree)
|
||||
{
|
||||
struct timeval now;
|
||||
|
||||
if (!tree->root)
|
||||
return;
|
||||
|
||||
DPRINTF("tree %s has %"PRIu64" bytes\n",
|
||||
tree->cache->name, tree->size);
|
||||
|
||||
gettimeofday(&now, NULL);
|
||||
radix_tree_prune_branch(tree, tree->root, now.tv_sec);
|
||||
|
||||
DPRINTF("tree %s now has %"PRIu64" bytes\n",
|
||||
tree->cache->name, tree->size);
|
||||
}
|
||||
|
||||
static inline int
|
||||
radix_tree_initialize(radix_tree_t *tree, uint64_t sectors)
|
||||
{
|
||||
tree->height = radix_tree_calculate_height(sectors);
|
||||
tree->root = radix_tree_allocate_node(tree, tree->height);
|
||||
if (!tree->root)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
radix_tree_free(radix_tree_t *tree)
|
||||
{
|
||||
radix_tree_destroy(tree);
|
||||
}
|
||||
|
||||
static void
|
||||
block_cache_prune_event(event_id_t id, char mode, void *private)
|
||||
{
|
||||
radix_tree_t *tree;
|
||||
block_cache_t *cache;
|
||||
|
||||
cache = (block_cache_t *)private;
|
||||
tree = &cache->tree;
|
||||
|
||||
radix_tree_prune(tree);
|
||||
}
|
||||
|
||||
static inline block_cache_request_t *
|
||||
block_cache_get_request(block_cache_t *cache)
|
||||
{
|
||||
if (!cache->requests_free)
|
||||
return NULL;
|
||||
|
||||
return cache->request_free_list[--cache->requests_free];
|
||||
}
|
||||
|
||||
static inline void
|
||||
block_cache_put_request(block_cache_t *cache, block_cache_request_t *breq)
|
||||
{
|
||||
memset(breq, 0, sizeof(block_cache_request_t));
|
||||
cache->request_free_list[cache->requests_free++] = breq;
|
||||
}
|
||||
|
||||
static int
|
||||
block_cache_open(td_driver_t *driver, const char *name, td_flag_t flags)
|
||||
{
|
||||
int i, err;
|
||||
radix_tree_t *tree;
|
||||
block_cache_t *cache;
|
||||
|
||||
if (!td_flag_test(flags, TD_OPEN_RDONLY))
|
||||
return -EINVAL;
|
||||
|
||||
if (driver->info.sector_size != RADIX_TREE_NODE_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
cache = (block_cache_t *)driver->data;
|
||||
err = tapdisk_namedup(&cache->name, (char *)name);
|
||||
if (err)
|
||||
return -ENOMEM;
|
||||
|
||||
cache->sectors = driver->info.size;
|
||||
|
||||
tree = &cache->tree;
|
||||
err = radix_tree_initialize(tree, cache->sectors);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
tree->cache = cache;
|
||||
cache->requests_free = BLOCK_CACHE_REQUESTS;
|
||||
for (i = 0; i < BLOCK_CACHE_REQUESTS; i++)
|
||||
cache->request_free_list[i] = cache->requests + i;
|
||||
|
||||
cache->timeout_id = tapdisk_server_register_event(SCHEDULER_POLL_TIMEOUT,
|
||||
-1, /* dummy fd */
|
||||
BLOCK_CACHE_PAGE_IDLETIME << 1,
|
||||
block_cache_prune_event,
|
||||
cache);
|
||||
if (cache->timeout_id < 0)
|
||||
goto fail;
|
||||
|
||||
DPRINTF("opening cache for %s, sectors: %"PRIu64", "
|
||||
"tree: %p, height: %d\n",
|
||||
cache->name, cache->sectors, tree, tree->height);
|
||||
|
||||
if (mlockall(MCL_CURRENT | MCL_FUTURE))
|
||||
DPRINTF("mlockall failed: %d\n", -errno);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
free(cache->name);
|
||||
radix_tree_free(&cache->tree);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
block_cache_close(td_driver_t *driver)
|
||||
{
|
||||
radix_tree_t *tree;
|
||||
block_cache_t *cache;
|
||||
|
||||
cache = (block_cache_t *)driver->data;
|
||||
tree = &cache->tree;
|
||||
|
||||
DPRINTF("closing cache for %s\n", cache->name);
|
||||
|
||||
tapdisk_server_unregister_event(cache->timeout_id);
|
||||
radix_tree_free(tree);
|
||||
free(cache->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
block_cache_hash(block_cache_t *cache, char *buf)
|
||||
{
|
||||
int i, n;
|
||||
uint64_t cksm, *data;
|
||||
|
||||
return 0;
|
||||
|
||||
cksm = 0;
|
||||
data = (uint64_t *)buf;
|
||||
n = RADIX_TREE_NODE_SIZE / sizeof(uint64_t);
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
cksm += data[i];
|
||||
|
||||
return ~cksm;
|
||||
}
|
||||
|
||||
static void
|
||||
block_cache_hit(block_cache_t *cache, td_request_t treq, char *iov[])
|
||||
{
|
||||
int i;
|
||||
off_t off;
|
||||
|
||||
cache->stats.hits += treq.secs;
|
||||
|
||||
for (i = 0; i < treq.secs; i++) {
|
||||
DBG("%s: block cache hit: sec 0x%08llx, hash: 0x%08llx\n",
|
||||
cache->name, treq.sec + i, block_cache_hash(cache, iov[i]));
|
||||
|
||||
off = i << RADIX_TREE_NODE_SHIFT;
|
||||
memcpy(treq.buf + off, iov[i], RADIX_TREE_NODE_SIZE);
|
||||
}
|
||||
|
||||
td_complete_request(treq, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
block_cache_populate_cache(td_request_t clone, int err)
|
||||
{
|
||||
int i;
|
||||
radix_tree_t *tree;
|
||||
block_cache_t *cache;
|
||||
block_cache_request_t *breq;
|
||||
|
||||
breq = (block_cache_request_t *)clone.cb_data;
|
||||
cache = breq->cache;
|
||||
tree = &cache->tree;
|
||||
breq->secs -= clone.secs;
|
||||
breq->err = (breq->err ? breq->err : err);
|
||||
|
||||
if (breq->secs)
|
||||
return;
|
||||
|
||||
if (breq->err) {
|
||||
free(breq->buf);
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < breq->treq.secs; i++) {
|
||||
off_t off = i << RADIX_TREE_NODE_SHIFT;
|
||||
DBG("%s: populating sec 0x%08llx\n",
|
||||
cache->name, breq->treq.sec + i);
|
||||
memcpy(breq->treq.buf + off,
|
||||
breq->buf + off, RADIX_TREE_NODE_SIZE);
|
||||
}
|
||||
|
||||
if (radix_tree_add_leaves(tree, breq->buf,
|
||||
breq->treq.sec, breq->treq.secs))
|
||||
free(breq->buf);
|
||||
|
||||
out:
|
||||
td_complete_request(breq->treq, breq->err);
|
||||
block_cache_put_request(cache, breq);
|
||||
}
|
||||
|
||||
static void
|
||||
block_cache_miss(block_cache_t *cache, td_request_t treq)
|
||||
{
|
||||
char *buf;
|
||||
size_t size;
|
||||
td_request_t clone;
|
||||
radix_tree_t *tree;
|
||||
block_cache_request_t *breq;
|
||||
|
||||
DBG("%s: block cache miss: sec 0x%08llx\n", cache->name, treq.sec);
|
||||
|
||||
clone = treq;
|
||||
tree = &cache->tree;
|
||||
size = treq.secs << RADIX_TREE_NODE_SHIFT;
|
||||
|
||||
cache->stats.misses += treq.secs;
|
||||
|
||||
if (radix_tree_size(tree) + size >= BLOCK_CACHE_MAX_SIZE)
|
||||
goto out;
|
||||
|
||||
breq = block_cache_get_request(cache);
|
||||
if (!breq)
|
||||
goto out;
|
||||
|
||||
if (posix_memalign((void **)&buf, RADIX_TREE_NODE_SIZE, size)) {
|
||||
block_cache_put_request(cache, breq);
|
||||
goto out;
|
||||
}
|
||||
|
||||
breq->treq = treq;
|
||||
breq->secs = treq.secs;
|
||||
breq->err = 0;
|
||||
breq->buf = buf;
|
||||
breq->cache = cache;
|
||||
|
||||
clone.buf = buf;
|
||||
clone.cb = block_cache_populate_cache;
|
||||
clone.cb_data = breq;
|
||||
|
||||
out:
|
||||
td_forward_request(clone);
|
||||
}
|
||||
|
||||
static void
|
||||
block_cache_queue_read(td_driver_t *driver, td_request_t treq)
|
||||
{
|
||||
int i;
|
||||
radix_tree_t *tree;
|
||||
block_cache_t *cache;
|
||||
char *iov[BLOCK_CACHE_NODES_PER_PAGE];
|
||||
|
||||
cache = (block_cache_t *)driver->data;
|
||||
tree = &cache->tree;
|
||||
|
||||
cache->stats.reads += treq.secs;
|
||||
|
||||
if (treq.secs > BLOCK_CACHE_NODES_PER_PAGE)
|
||||
return td_forward_request(treq);
|
||||
|
||||
for (i = 0; i < treq.secs; i++) {
|
||||
iov[i] = radix_tree_find_leaf(tree, treq.sec + i);
|
||||
if (!iov[i])
|
||||
return block_cache_miss(cache, treq);
|
||||
}
|
||||
|
||||
return block_cache_hit(cache, treq, iov);
|
||||
}
|
||||
|
||||
static void
|
||||
block_cache_queue_write(td_driver_t *driver, td_request_t treq)
|
||||
{
|
||||
td_complete_request(treq, -EPERM);
|
||||
}
|
||||
|
||||
static int
|
||||
block_cache_get_parent_id(td_driver_t *driver, td_disk_id_t *id)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int
|
||||
block_cache_validate_parent(td_driver_t *driver,
|
||||
td_driver_t *pdriver, td_flag_t flags)
|
||||
{
|
||||
block_cache_t *cache;
|
||||
|
||||
if (!td_flag_test(pdriver->state, TD_DRIVER_RDONLY))
|
||||
return -EINVAL;
|
||||
|
||||
cache = (block_cache_t *)driver->data;
|
||||
if (strcmp(driver->name, pdriver->name))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
block_cache_debug(td_driver_t *driver)
|
||||
{
|
||||
block_cache_t *cache;
|
||||
block_cache_stats_t *stats;
|
||||
|
||||
cache = (block_cache_t *)driver->data;
|
||||
stats = &cache->stats;
|
||||
|
||||
WARN("BLOCK CACHE %s\n", cache->name);
|
||||
WARN("reads: %"PRIu64", hits: %"PRIu64", misses: %"PRIu64", prunes: %"PRIu64"\n",
|
||||
stats->reads, stats->hits, stats->misses, stats->prunes);
|
||||
}
|
||||
|
||||
struct tap_disk tapdisk_block_cache = {
|
||||
.disk_type = "tapdisk_block_cache",
|
||||
.flags = 0,
|
||||
.private_data_size = sizeof(block_cache_t),
|
||||
.td_open = block_cache_open,
|
||||
.td_close = block_cache_close,
|
||||
.td_queue_read = block_cache_queue_read,
|
||||
.td_queue_write = block_cache_queue_write,
|
||||
.td_get_parent_id = block_cache_get_parent_id,
|
||||
.td_validate_parent = block_cache_validate_parent,
|
||||
.td_debug = block_cache_debug,
|
||||
};
|
||||
@@ -1,665 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* Driver to sit on top of another disk and log writes, in order
|
||||
* to synchronize two distinct disks
|
||||
*
|
||||
* On receipt of a control request it can export a list of dirty
|
||||
* sectors in the following format:
|
||||
* struct writerange {
|
||||
* u64 sector;
|
||||
* u32 count;
|
||||
* }
|
||||
* terminated by { 0, 0 }
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include "xc_bitops.h"
|
||||
#include "log.h"
|
||||
#include "tapdisk.h"
|
||||
#include "tapdisk-server.h"
|
||||
#include "tapdisk-driver.h"
|
||||
#include "tapdisk-interface.h"
|
||||
|
||||
#define MAX_CONNECTIONS 1
|
||||
|
||||
typedef struct poll_fd {
|
||||
int fd;
|
||||
event_id_t id;
|
||||
} poll_fd_t;
|
||||
|
||||
struct tdlog_state {
|
||||
uint64_t size;
|
||||
|
||||
void* writelog;
|
||||
|
||||
char* ctlpath;
|
||||
poll_fd_t ctl;
|
||||
|
||||
int connected;
|
||||
poll_fd_t connections[MAX_CONNECTIONS];
|
||||
|
||||
char* shmpath;
|
||||
void* shm;
|
||||
|
||||
log_sring_t* sring;
|
||||
log_back_ring_t bring;
|
||||
};
|
||||
|
||||
#define BDPRINTF(_f, _a...) syslog (LOG_DEBUG, "log: " _f "\n", ## _a)
|
||||
|
||||
#define BWPRINTF(_f, _a...) syslog (LOG_WARNING, "log: " _f "\n", ## _a)
|
||||
|
||||
static void ctl_accept(event_id_t, char, void *);
|
||||
static void ctl_request(event_id_t, char, void *);
|
||||
|
||||
/* -- write log -- */
|
||||
|
||||
/* large flat bitmaps don't scale particularly well either in size or scan
|
||||
* time, but they'll do for now */
|
||||
|
||||
static int writelog_create(struct tdlog_state *s)
|
||||
{
|
||||
uint64_t bmsize;
|
||||
|
||||
bmsize = bitmap_size(s->size);
|
||||
|
||||
BDPRINTF("allocating %"PRIu64" bytes for dirty bitmap", bmsize);
|
||||
|
||||
s->writelog = bitmap_alloc(s->size);
|
||||
if (!s->writelog) {
|
||||
BWPRINTF("could not allocate dirty bitmap of size %"PRIu64, bmsize);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int writelog_free(struct tdlog_state *s)
|
||||
{
|
||||
if (s->writelog)
|
||||
free(s->writelog);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int writelog_set(struct tdlog_state* s, uint64_t sector, int count)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
set_bit(sector + i, s->writelog);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* if end is 0, clear to end of disk */
|
||||
int writelog_clear(struct tdlog_state* s, uint64_t start, uint64_t end)
|
||||
{
|
||||
if (!end)
|
||||
end = s->size;
|
||||
|
||||
/* clear to word boundaries */
|
||||
while (BITMAP_SHIFT(start))
|
||||
clear_bit(start++, s->writelog);
|
||||
while (BITMAP_SHIFT(end))
|
||||
clear_bit(end--, s->writelog);
|
||||
|
||||
memset(s->writelog + start / BITS_PER_LONG, 0, (end - start) >> 3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* returns last block exported (may not be end of disk if shm region
|
||||
* overflows) */
|
||||
static uint64_t writelog_export(struct tdlog_state* s)
|
||||
{
|
||||
struct disk_range* range = s->shm;
|
||||
uint64_t i = 0;
|
||||
|
||||
BDPRINTF("sector count: %"PRIu64, s->size);
|
||||
|
||||
for (i = 0; i < s->size; i++) {
|
||||
if (test_bit(i, s->writelog)) {
|
||||
/* range start */
|
||||
range->sector = i;
|
||||
range->count = 1;
|
||||
/* find end */
|
||||
for (i++; i < s->size && test_bit(i, s->writelog); i++)
|
||||
range->count++;
|
||||
|
||||
BDPRINTF("export: dirty extent %"PRIu64":%u",
|
||||
range->sector, range->count);
|
||||
range++;
|
||||
|
||||
/* out of space in shared memory region */
|
||||
if ((void*)range >= bmend(s->shm)) {
|
||||
BDPRINTF("out of space in shm region at sector %"PRIu64, i);
|
||||
return i;
|
||||
}
|
||||
|
||||
/* undo forloop increment */
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
/* NULL-terminate range list */
|
||||
range->sector = 0;
|
||||
range->count = 0;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/* -- communication channel -- */
|
||||
|
||||
/* remove FS special characters in up to len bytes of path */
|
||||
static inline void path_escape(char* path, size_t len) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len && path[i]; i++)
|
||||
if (strchr(":/", path[i]))
|
||||
path[i] = '_';
|
||||
}
|
||||
|
||||
static char* ctl_makepath(const char* name, const char* ext)
|
||||
{
|
||||
char* res;
|
||||
char *file;
|
||||
|
||||
file = strrchr(name, '/');
|
||||
if (!file) {
|
||||
BWPRINTF("invalid name %s\n", name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (asprintf(&res, BLKTAP_CTRL_DIR "/log_%s.%s", file, ext) < 0) {
|
||||
BWPRINTF("could not allocate path");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
path_escape(res + strlen(BLKTAP_CTRL_DIR) + 5, strlen(file));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int shmem_open(struct tdlog_state* s, const char* name)
|
||||
{
|
||||
int i, l, fd;
|
||||
|
||||
/* device name -> path */
|
||||
if (asprintf(&s->shmpath, "/log_%s.wlog", name) < 0) {
|
||||
BWPRINTF("could not allocate shm path");
|
||||
return -1;
|
||||
}
|
||||
|
||||
path_escape(s->shmpath + 5, strlen(name));
|
||||
|
||||
if ((fd = shm_open(s->shmpath, O_CREAT|O_RDWR, 0750)) < 0) {
|
||||
BWPRINTF("could not open shared memory file %s: %s", s->shmpath,
|
||||
strerror(errno));
|
||||
goto err;
|
||||
}
|
||||
if (ftruncate(fd, SHMSIZE) < 0) {
|
||||
BWPRINTF("error truncating shmem to size %u", SHMSIZE);
|
||||
close(fd);
|
||||
goto err;
|
||||
}
|
||||
|
||||
s->shm = mmap(NULL, SHMSIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
close(fd);
|
||||
if (s->shm == MAP_FAILED) {
|
||||
BWPRINTF("could not mmap write log shm: %s", strerror(errno));
|
||||
goto err;
|
||||
}
|
||||
return 0;
|
||||
|
||||
err:
|
||||
s->shm = NULL;
|
||||
free(s->shmpath);
|
||||
s->shmpath = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int shmem_close(struct tdlog_state* s)
|
||||
{
|
||||
if (s->shm) {
|
||||
munmap(s->shm, SHMSIZE);
|
||||
s->shm = NULL;
|
||||
}
|
||||
|
||||
if (s->shmpath) {
|
||||
shm_unlink(s->shmpath);
|
||||
s->shmpath = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* control socket */
|
||||
|
||||
static int ctl_open(struct tdlog_state* s, const char* name)
|
||||
{
|
||||
struct sockaddr_un saddr;
|
||||
|
||||
if (!(s->ctlpath = ctl_makepath(name, "ctl")))
|
||||
return -1;
|
||||
|
||||
if ((s->ctl.fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
|
||||
BWPRINTF("error opening control socket: %s", strerror(errno));
|
||||
goto err;
|
||||
}
|
||||
|
||||
memset(&saddr, 0, sizeof(saddr));
|
||||
saddr.sun_family = AF_UNIX;
|
||||
memcpy(saddr.sun_path, s->ctlpath, strlen(s->ctlpath));
|
||||
if (unlink(s->ctlpath) && errno != ENOENT) {
|
||||
BWPRINTF("error unlinking old socket path %s: %s", s->ctlpath,
|
||||
strerror(errno));
|
||||
goto err_sock;
|
||||
}
|
||||
|
||||
if (bind(s->ctl.fd, (const struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
|
||||
BWPRINTF("error binding control socket to %s: %s", s->ctlpath,
|
||||
strerror(errno));
|
||||
goto err_sock;
|
||||
}
|
||||
|
||||
if (listen(s->ctl.fd, 1) < 0) {
|
||||
BWPRINTF("error listening on control socket: %s", strerror(errno));
|
||||
goto err_sock;
|
||||
}
|
||||
|
||||
s->ctl.id = tapdisk_server_register_event(SCHEDULER_POLL_READ_FD,
|
||||
s->ctl.fd, 0, ctl_accept, s);
|
||||
if (s->ctl.id < 0) {
|
||||
BWPRINTF("error register event handler: %s", strerror(s->ctl.id));
|
||||
goto err_sock;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_sock:
|
||||
close(s->ctl.fd);
|
||||
s->ctl.fd = -1;
|
||||
err:
|
||||
free(s->ctlpath);
|
||||
s->ctlpath = NULL;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int ctl_close(struct tdlog_state* s)
|
||||
{
|
||||
while (s->connected) {
|
||||
s->connected--;
|
||||
tapdisk_server_unregister_event(s->connections[s->connected].id);
|
||||
close(s->connections[s->connected].fd);
|
||||
s->connections[s->connected].fd = -1;
|
||||
s->connections[s->connected].id = 0;
|
||||
}
|
||||
|
||||
if (s->ctl.fd >= 0) {
|
||||
tapdisk_server_unregister_event(s->ctl.id);
|
||||
close(s->ctl.fd);
|
||||
s->ctl.fd = -1;
|
||||
s->ctl.id = 0;
|
||||
}
|
||||
|
||||
if (s->ctlpath) {
|
||||
unlink(s->ctlpath);
|
||||
free(s->ctlpath);
|
||||
s->ctlpath = NULL;
|
||||
}
|
||||
|
||||
/* XXX this must be fixed once requests are actually in flight */
|
||||
/* could just drain the existing ring here first */
|
||||
if (s->sring) {
|
||||
SHARED_RING_INIT(s->sring);
|
||||
BACK_RING_INIT(&s->bring, s->sring, SRINGSIZE);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* walk list of open sockets, close matching fd */
|
||||
static int ctl_close_sock(struct tdlog_state* s, int fd)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->connected; i++) {
|
||||
if (s->connections[i].fd == fd) {
|
||||
tapdisk_server_unregister_event(s->connections[i].id);
|
||||
close(s->connections[i].fd);
|
||||
s->connections[i].fd = -1;
|
||||
s->connections[i].id = 0;
|
||||
s->connected--;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
BWPRINTF("requested to close unknown socket %d", fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void ctl_accept(event_id_t id, char mode, void *private)
|
||||
{
|
||||
struct tdlog_state* s = (struct tdlog_state *)private;
|
||||
int fd;
|
||||
event_id_t cid;
|
||||
|
||||
if ((fd = accept(s->ctl.fd, NULL, NULL)) < 0) {
|
||||
BWPRINTF("error accepting control connection: %s", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->connected) {
|
||||
BWPRINTF("control session in progress, closing new connection");
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
cid = tapdisk_server_register_event(SCHEDULER_POLL_READ_FD,
|
||||
fd, 0, ctl_request, s);
|
||||
if (cid < 0) {
|
||||
BWPRINTF("error registering connection event handler: %s", strerror(cid));
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
s->connections[s->connected].fd = fd;
|
||||
s->connections[s->connected].id = cid;
|
||||
s->connected++;
|
||||
}
|
||||
|
||||
/* response format: 4 bytes shmsize, 0-terminated path */
|
||||
static int ctl_get_shmpath(struct tdlog_state* s, int fd)
|
||||
{
|
||||
char msg[CTLRSPLEN_SHMP + 1];
|
||||
uint32_t sz;
|
||||
int rc;
|
||||
|
||||
BDPRINTF("ctl: sending shared memory parameters (size: %u, path: %s)",
|
||||
SHMSIZE, s->shmpath);
|
||||
|
||||
/* TMP: sanity-check shm */
|
||||
sz = 0xdeadbeef;
|
||||
memcpy(s->shm, &sz, sizeof(sz));
|
||||
|
||||
sz = SHMSIZE;
|
||||
memcpy(msg, &sz, sizeof(sz));
|
||||
snprintf(msg + sizeof(sz), sizeof(msg) - sizeof(sz), "%s", s->shmpath);
|
||||
if ((rc = write(fd, msg, CTLRSPLEN_SHMP)) < 0) {
|
||||
BWPRINTF("error writing shmpath: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ctl_peek_writes(struct tdlog_state* s, int fd)
|
||||
{
|
||||
int rc;
|
||||
|
||||
BDPRINTF("ctl: peeking bitmap");
|
||||
|
||||
writelog_export(s);
|
||||
|
||||
if ((rc = write(fd, "done", CTLRSPLEN_PEEK)) < 0) {
|
||||
BWPRINTF("error writing peek ack: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ctl_clear_writes(struct tdlog_state* s, int fd)
|
||||
{
|
||||
int rc;
|
||||
|
||||
BDPRINTF("ctl: clearing bitmap");
|
||||
|
||||
writelog_clear(s, 0, 0);
|
||||
|
||||
if ((rc = write(fd, "done", CTLRSPLEN_CLEAR)) < 0) {
|
||||
BWPRINTF("error writing clear ack: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* get dirty bitmap and clear it atomically */
|
||||
static int ctl_get_writes(struct tdlog_state* s, int fd)
|
||||
{
|
||||
int rc;
|
||||
|
||||
BDPRINTF("ctl: getting bitmap");
|
||||
|
||||
writelog_export(s);
|
||||
writelog_clear(s, 0, 0);
|
||||
|
||||
if ((rc = write(fd, "done", CTLRSPLEN_GET)) < 0) {
|
||||
BWPRINTF("error writing get ack: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* get requests from ring */
|
||||
static int ctl_kick(struct tdlog_state* s, int fd)
|
||||
{
|
||||
RING_IDX reqstart, reqend;
|
||||
log_request_t req;
|
||||
|
||||
/* XXX testing */
|
||||
RING_IDX rspstart, rspend;
|
||||
log_response_t rsp;
|
||||
struct log_ctlmsg msg;
|
||||
int rc;
|
||||
|
||||
reqstart = s->bring.req_cons;
|
||||
reqend = s->sring->req_prod;
|
||||
|
||||
BDPRINTF("ctl: ring kicked (start = %u, end = %u)", reqstart, reqend);
|
||||
|
||||
while (reqstart != reqend) {
|
||||
/* XXX actually submit these! */
|
||||
memcpy(&req, RING_GET_REQUEST(&s->bring, reqstart), sizeof(req));
|
||||
BDPRINTF("ctl: read request %"PRIu64":%u", req.sector, req.count);
|
||||
s->bring.req_cons = ++reqstart;
|
||||
|
||||
rsp.sector = req.sector;
|
||||
rsp.count = req.count;
|
||||
memcpy(RING_GET_RESPONSE(&s->bring, s->bring.rsp_prod_pvt), &rsp,
|
||||
sizeof(rsp));
|
||||
s->bring.rsp_prod_pvt++;
|
||||
}
|
||||
|
||||
RING_PUSH_RESPONSES(&s->bring);
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
memcpy(msg.msg, LOGCMD_KICK, 4);
|
||||
if ((rc = write(fd, &msg, sizeof(msg))) < 0) {
|
||||
BWPRINTF("error sending notify: %s", strerror(errno));
|
||||
return -1;
|
||||
} else if (rc < sizeof(msg)) {
|
||||
BWPRINTF("short notify write (%d/%zd)", rc, sizeof(msg));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ctl_do_request(struct tdlog_state* s, int fd, struct log_ctlmsg* msg)
|
||||
{
|
||||
if (!strncmp(msg->msg, LOGCMD_SHMP, 4)) {
|
||||
return ctl_get_shmpath(s, fd);
|
||||
} else if (!strncmp(msg->msg, LOGCMD_PEEK, 4)) {
|
||||
return ctl_peek_writes(s, fd);
|
||||
} else if (!strncmp(msg->msg, LOGCMD_CLEAR, 4)) {
|
||||
return ctl_clear_writes(s, fd);
|
||||
} else if (!strncmp(msg->msg, LOGCMD_GET, 4)) {
|
||||
return ctl_get_writes(s, fd);
|
||||
} else if (!strncmp(msg->msg, LOGCMD_KICK, 4)) {
|
||||
return ctl_kick(s, fd);
|
||||
}
|
||||
|
||||
BWPRINTF("unknown control request %.4s", msg->msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int ctl_find_connection(struct tdlog_state *s, event_id_t id)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->connected; i++)
|
||||
if (s->connections[i].id == id)
|
||||
return s->connections[i].fd;
|
||||
|
||||
BWPRINTF("unrecognized event callback id %d", id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void ctl_request(event_id_t id, char mode, void *private)
|
||||
{
|
||||
struct tdlog_state* s = (struct tdlog_state*)private;
|
||||
struct log_ctlmsg msg;
|
||||
int rc, i, fd = -1;
|
||||
|
||||
fd = ctl_find_connection(s, id);
|
||||
if (fd == -1)
|
||||
return;
|
||||
|
||||
if ((rc = read(fd, &msg, sizeof(msg))) < 0) {
|
||||
BWPRINTF("error reading from ctl socket %d, closing: %s", fd,
|
||||
strerror(errno));
|
||||
ctl_close_sock(s, fd);
|
||||
return;
|
||||
} else if (rc == 0) {
|
||||
BDPRINTF("ctl_request: EOF, closing socket");
|
||||
ctl_close_sock(s, fd);
|
||||
return;
|
||||
} else if (rc < sizeof(msg)) {
|
||||
BWPRINTF("short request received (%d/%zd bytes), ignoring", rc,
|
||||
sizeof(msg));
|
||||
return;
|
||||
}
|
||||
|
||||
ctl_do_request(s, fd, &msg);
|
||||
}
|
||||
|
||||
/* -- interface -- */
|
||||
|
||||
static int tdlog_close(td_driver_t*);
|
||||
|
||||
static int tdlog_open(td_driver_t* driver, const char* name, td_flag_t flags)
|
||||
{
|
||||
struct tdlog_state* s = (struct tdlog_state*)driver->data;
|
||||
int rc;
|
||||
|
||||
memset(s, 0, sizeof(*s));
|
||||
|
||||
s->size = driver->info.size;
|
||||
|
||||
if ((rc = writelog_create(s))) {
|
||||
tdlog_close(driver);
|
||||
return rc;
|
||||
}
|
||||
if ((rc = shmem_open(s, name))) {
|
||||
tdlog_close(driver);
|
||||
return rc;
|
||||
}
|
||||
if ((rc = ctl_open(s, name))) {
|
||||
tdlog_close(driver);
|
||||
return rc;
|
||||
}
|
||||
|
||||
s->sring = (log_sring_t*)sringstart(s->shm);
|
||||
SHARED_RING_INIT(s->sring);
|
||||
BACK_RING_INIT(&s->bring, s->sring, SRINGSIZE);
|
||||
|
||||
BDPRINTF("opened ctl socket");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tdlog_close(td_driver_t* driver)
|
||||
{
|
||||
struct tdlog_state* s = (struct tdlog_state*)driver->data;
|
||||
|
||||
ctl_close(s);
|
||||
shmem_close(s);
|
||||
writelog_free(s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tdlog_queue_read(td_driver_t* driver, td_request_t treq)
|
||||
{
|
||||
td_forward_request(treq);
|
||||
}
|
||||
|
||||
static void tdlog_queue_write(td_driver_t* driver, td_request_t treq)
|
||||
{
|
||||
struct tdlog_state* s = (struct tdlog_state*)driver->data;
|
||||
int rc;
|
||||
|
||||
writelog_set(s, treq.sec, treq.secs);
|
||||
td_forward_request(treq);
|
||||
}
|
||||
|
||||
static int tdlog_get_parent_id(td_driver_t* driver, td_disk_id_t* id)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int tdlog_validate_parent(td_driver_t *driver,
|
||||
td_driver_t *parent, td_flag_t flags)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct tap_disk tapdisk_log = {
|
||||
.disk_type = "tapdisk_log",
|
||||
.private_data_size = sizeof(struct tdlog_state),
|
||||
.flags = 0,
|
||||
.td_open = tdlog_open,
|
||||
.td_close = tdlog_close,
|
||||
.td_queue_read = tdlog_queue_read,
|
||||
.td_queue_write = tdlog_queue_write,
|
||||
.td_get_parent_id = tdlog_get_parent_id,
|
||||
.td_validate_parent = tdlog_validate_parent,
|
||||
};
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,256 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2007, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "blk.h"
|
||||
#include "tapdisk.h"
|
||||
#include "tapdisk-driver.h"
|
||||
#include "tapdisk-interface.h"
|
||||
|
||||
char *img;
|
||||
long int disksector_size;
|
||||
long int disksize;
|
||||
long int diskinfo;
|
||||
static int connections = 0;
|
||||
|
||||
struct tdram_state {
|
||||
int fd;
|
||||
};
|
||||
|
||||
/*Get Image size, secsize*/
|
||||
static int get_image_info(int fd, td_disk_info_t *info)
|
||||
{
|
||||
int ret;
|
||||
long size;
|
||||
unsigned long total_size;
|
||||
struct statvfs statBuf;
|
||||
struct stat stat;
|
||||
|
||||
ret = fstat(fd, &stat);
|
||||
if (ret != 0) {
|
||||
DPRINTF("ERROR: fstat failed, Couldn't stat image");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (S_ISBLK(stat.st_mode)) {
|
||||
/*Accessing block device directly*/
|
||||
info->size = 0;
|
||||
if (blk_getimagesize(fd, &info->size) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
DPRINTF("Image size: \n\tpre sector_shift [%llu]\n\tpost "
|
||||
"sector_shift [%llu]\n",
|
||||
(long long unsigned)(info->size << SECTOR_SHIFT),
|
||||
(long long unsigned)info->size);
|
||||
|
||||
/*Get the sector size*/
|
||||
if (blk_getsectorsize(fd, &info->sector_size) != 0)
|
||||
info->sector_size = DEFAULT_SECTOR_SIZE;
|
||||
|
||||
} else {
|
||||
/*Local file? try fstat instead*/
|
||||
info->size = (stat.st_size >> SECTOR_SHIFT);
|
||||
info->sector_size = DEFAULT_SECTOR_SIZE;
|
||||
DPRINTF("Image size: \n\tpre sector_shift [%llu]\n\tpost "
|
||||
"sector_shift [%llu]\n",
|
||||
(long long unsigned)(info->size << SECTOR_SHIFT),
|
||||
(long long unsigned)info->size);
|
||||
}
|
||||
|
||||
if (info->size == 0) {
|
||||
info->size =((uint64_t) MAX_RAMDISK_SIZE);
|
||||
info->sector_size = DEFAULT_SECTOR_SIZE;
|
||||
}
|
||||
info->info = 0;
|
||||
|
||||
/*Store variables locally*/
|
||||
disksector_size = info->sector_size;
|
||||
disksize = info->size;
|
||||
diskinfo = info->info;
|
||||
DPRINTF("Image sector_size: \n\t[%"PRIu64"]\n",
|
||||
info->sector_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Open the disk file and initialize ram state. */
|
||||
int tdram_open (td_driver_t *driver, const char *name, td_flag_t flags)
|
||||
{
|
||||
char *p;
|
||||
uint64_t size;
|
||||
int i, fd, ret = 0, count = 0, o_flags;
|
||||
struct tdram_state *prv = (struct tdram_state *)driver->data;
|
||||
|
||||
connections++;
|
||||
|
||||
if (connections > 1) {
|
||||
driver->info.sector_size = disksector_size;
|
||||
driver->info.size = disksize;
|
||||
driver->info.info = diskinfo;
|
||||
DPRINTF("Image already open, returning parameters:\n");
|
||||
DPRINTF("Image size: \n\tpre sector_shift [%llu]\n\tpost "
|
||||
"sector_shift [%llu]\n",
|
||||
(long long unsigned)(driver->info.size << SECTOR_SHIFT),
|
||||
(long long unsigned)driver->info.size);
|
||||
DPRINTF("Image sector_size: \n\t[%"PRIu64"]\n",
|
||||
driver->info.sector_size);
|
||||
|
||||
prv->fd = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Open the file */
|
||||
o_flags = O_DIRECT | O_LARGEFILE |
|
||||
((flags == TD_OPEN_RDONLY) ? O_RDONLY : O_RDWR);
|
||||
fd = open(name, o_flags);
|
||||
|
||||
if ((fd == -1) && (errno == EINVAL)) {
|
||||
|
||||
/* Maybe O_DIRECT isn't supported. */
|
||||
o_flags &= ~O_DIRECT;
|
||||
fd = open(name, o_flags);
|
||||
if (fd != -1) DPRINTF("WARNING: Accessing image without"
|
||||
"O_DIRECT! (%s)\n", name);
|
||||
|
||||
} else if (fd != -1) DPRINTF("open(%s) with O_DIRECT\n", name);
|
||||
|
||||
if (fd == -1) {
|
||||
DPRINTF("Unable to open [%s]!\n",name);
|
||||
ret = 0 - errno;
|
||||
goto done;
|
||||
}
|
||||
|
||||
prv->fd = fd;
|
||||
|
||||
ret = get_image_info(fd, &driver->info);
|
||||
size = MAX_RAMDISK_SIZE;
|
||||
|
||||
if (driver->info.size > size) {
|
||||
DPRINTF("Disk exceeds limit, must be less than [%d]MB",
|
||||
(MAX_RAMDISK_SIZE<<SECTOR_SHIFT)>>20);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/*Read the image into memory*/
|
||||
if (posix_memalign((void **)&img,
|
||||
DEFAULT_SECTOR_SIZE,
|
||||
driver->info.size << SECTOR_SHIFT)) {
|
||||
DPRINTF("Mem malloc failed\n");
|
||||
return -errno;
|
||||
}
|
||||
p = img;
|
||||
DPRINTF("Reading %llu bytes.......",
|
||||
(long long unsigned)driver->info.size << SECTOR_SHIFT);
|
||||
|
||||
for (i = 0; i < driver->info.size; i++) {
|
||||
ret = read(prv->fd, p, driver->info.sector_size);
|
||||
if (ret != driver->info.sector_size) {
|
||||
DPRINTF("ret = %d, errno = %d\n", ret, errno);
|
||||
ret = 0 - errno;
|
||||
break;
|
||||
} else {
|
||||
count += ret;
|
||||
p = img + count;
|
||||
}
|
||||
}
|
||||
DPRINTF("[%d]\n",count);
|
||||
if (count != driver->info.size << SECTOR_SHIFT) {
|
||||
ret = -1;
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void tdram_queue_read(td_driver_t *driver, td_request_t treq)
|
||||
{
|
||||
struct tdram_state *prv = (struct tdram_state *)driver->data;
|
||||
int size = treq.secs * driver->info.sector_size;
|
||||
uint64_t offset = treq.sec * (uint64_t)driver->info.sector_size;
|
||||
|
||||
memcpy(treq.buf, img + offset, size);
|
||||
|
||||
td_complete_request(treq, 0);
|
||||
}
|
||||
|
||||
void tdram_queue_write(td_driver_t *driver, td_request_t treq)
|
||||
{
|
||||
struct tdram_state *prv = (struct tdram_state *)driver->data;
|
||||
int size = treq.secs * driver->info.sector_size;
|
||||
uint64_t offset = treq.sec * (uint64_t)driver->info.sector_size;
|
||||
|
||||
/* We assume that write access is controlled
|
||||
* at a higher level for multiple disks */
|
||||
memcpy(img + offset, treq.buf, size);
|
||||
|
||||
td_complete_request(treq, 0);
|
||||
}
|
||||
|
||||
int tdram_close(td_driver_t *driver)
|
||||
{
|
||||
struct tdram_state *prv = (struct tdram_state *)driver->data;
|
||||
|
||||
connections--;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tdram_get_parent_id(td_driver_t *driver, td_disk_id_t *id)
|
||||
{
|
||||
return TD_NO_PARENT;
|
||||
}
|
||||
|
||||
int tdram_validate_parent(td_driver_t *driver,
|
||||
td_driver_t *pdriver, td_flag_t flags)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
struct tap_disk tapdisk_ram = {
|
||||
.disk_type = "tapdisk_ram",
|
||||
.flags = 0,
|
||||
.private_data_size = sizeof(struct tdram_state),
|
||||
.td_open = tdram_open,
|
||||
.td_close = tdram_close,
|
||||
.td_queue_read = tdram_queue_read,
|
||||
.td_queue_write = tdram_queue_write,
|
||||
.td_get_parent_id = tdram_get_parent_id,
|
||||
.td_validate_parent = tdram_validate_parent,
|
||||
.td_debug = NULL,
|
||||
};
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -1,179 +0,0 @@
|
||||
#ifndef BSWAP_H
|
||||
#define BSWAP_H
|
||||
|
||||
//#include "config-host.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#if defined(__NetBSD__)
|
||||
#include <sys/endian.h>
|
||||
#include <sys/types.h>
|
||||
#elif defined(__OpenBSD__)
|
||||
#include <machine/endian.h>
|
||||
#define bswap_16(x) swap16(x)
|
||||
#define bswap_32(x) swap32(x)
|
||||
#define bswap_64(x) swap64(x)
|
||||
#elif defined(__linux__)
|
||||
|
||||
#include <endian.h>
|
||||
#include <byteswap.h>
|
||||
|
||||
static inline uint16_t bswap16(uint16_t x)
|
||||
{
|
||||
return bswap_16(x);
|
||||
}
|
||||
|
||||
static inline uint32_t bswap32(uint32_t x)
|
||||
{
|
||||
return bswap_32(x);
|
||||
}
|
||||
|
||||
static inline uint64_t bswap64(uint64_t x)
|
||||
{
|
||||
return bswap_64(x);
|
||||
}
|
||||
|
||||
static inline void bswap16s(uint16_t *s)
|
||||
{
|
||||
*s = bswap16(*s);
|
||||
}
|
||||
|
||||
static inline void bswap32s(uint32_t *s)
|
||||
{
|
||||
*s = bswap32(*s);
|
||||
}
|
||||
|
||||
static inline void bswap64s(uint64_t *s)
|
||||
{
|
||||
*s = bswap64(*s);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(WORDS_BIGENDIAN)
|
||||
#define be_bswap(v, size) (v)
|
||||
#define le_bswap(v, size) bswap ## size(v)
|
||||
#define be_bswaps(v, size)
|
||||
#define le_bswaps(p, size) *p = bswap ## size(*p);
|
||||
#else
|
||||
#define le_bswap(v, size) (v)
|
||||
#define be_bswap(v, size) bswap ## size(v)
|
||||
#define le_bswaps(v, size)
|
||||
#define be_bswaps(p, size) *p = bswap ## size(*p);
|
||||
#endif
|
||||
|
||||
#define CPU_CONVERT(endian, size, type)\
|
||||
static inline type endian ## size ## _to_cpu(type v)\
|
||||
{\
|
||||
return endian ## _bswap(v, size);\
|
||||
}\
|
||||
\
|
||||
static inline type cpu_to_ ## endian ## size(type v)\
|
||||
{\
|
||||
return endian ## _bswap(v, size);\
|
||||
}\
|
||||
\
|
||||
static inline void endian ## size ## _to_cpus(type *p)\
|
||||
{\
|
||||
endian ## _bswaps(p, size)\
|
||||
}\
|
||||
\
|
||||
static inline void cpu_to_ ## endian ## size ## s(type *p)\
|
||||
{\
|
||||
endian ## _bswaps(p, size)\
|
||||
}\
|
||||
\
|
||||
static inline type endian ## size ## _to_cpup(const type *p)\
|
||||
{\
|
||||
return endian ## size ## _to_cpu(*p);\
|
||||
}\
|
||||
\
|
||||
static inline void cpu_to_ ## endian ## size ## w(type *p, type v)\
|
||||
{\
|
||||
*p = cpu_to_ ## endian ## size(v);\
|
||||
}
|
||||
|
||||
CPU_CONVERT(be, 16, uint16_t)
|
||||
CPU_CONVERT(be, 32, uint32_t)
|
||||
CPU_CONVERT(be, 64, uint64_t)
|
||||
|
||||
CPU_CONVERT(le, 16, uint16_t)
|
||||
CPU_CONVERT(le, 32, uint32_t)
|
||||
CPU_CONVERT(le, 64, uint64_t)
|
||||
|
||||
/* unaligned versions (optimized for frequent unaligned accesses)*/
|
||||
|
||||
#if defined(__i386__) || defined(__powerpc__)
|
||||
|
||||
#define cpu_to_le16wu(p, v) cpu_to_le16w(p, v)
|
||||
#define cpu_to_le32wu(p, v) cpu_to_le32w(p, v)
|
||||
#define le16_to_cpupu(p) le16_to_cpup(p)
|
||||
#define le32_to_cpupu(p) le32_to_cpup(p)
|
||||
|
||||
#define cpu_to_be16wu(p, v) cpu_to_be16w(p, v)
|
||||
#define cpu_to_be32wu(p, v) cpu_to_be32w(p, v)
|
||||
|
||||
#else
|
||||
|
||||
static inline void cpu_to_le16wu(uint16_t *p, uint16_t v)
|
||||
{
|
||||
uint8_t *p1 = (uint8_t *)p;
|
||||
|
||||
p1[0] = v;
|
||||
p1[1] = v >> 8;
|
||||
}
|
||||
|
||||
static inline void cpu_to_le32wu(uint32_t *p, uint32_t v)
|
||||
{
|
||||
uint8_t *p1 = (uint8_t *)p;
|
||||
|
||||
p1[0] = v;
|
||||
p1[1] = v >> 8;
|
||||
p1[2] = v >> 16;
|
||||
p1[3] = v >> 24;
|
||||
}
|
||||
|
||||
static inline uint16_t le16_to_cpupu(const uint16_t *p)
|
||||
{
|
||||
const uint8_t *p1 = (const uint8_t *)p;
|
||||
return p1[0] | (p1[1] << 8);
|
||||
}
|
||||
|
||||
static inline uint32_t le32_to_cpupu(const uint32_t *p)
|
||||
{
|
||||
const uint8_t *p1 = (const uint8_t *)p;
|
||||
return p1[0] | (p1[1] << 8) | (p1[2] << 16) | (p1[3] << 24);
|
||||
}
|
||||
|
||||
static inline void cpu_to_be16wu(uint16_t *p, uint16_t v)
|
||||
{
|
||||
uint8_t *p1 = (uint8_t *)p;
|
||||
|
||||
p1[0] = v >> 8;
|
||||
p1[1] = v;
|
||||
}
|
||||
|
||||
static inline void cpu_to_be32wu(uint32_t *p, uint32_t v)
|
||||
{
|
||||
uint8_t *p1 = (uint8_t *)p;
|
||||
|
||||
p1[0] = v >> 24;
|
||||
p1[1] = v >> 16;
|
||||
p1[2] = v >> 8;
|
||||
p1[3] = v;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
#define cpu_to_32wu cpu_to_be32wu
|
||||
#else
|
||||
#define cpu_to_32wu cpu_to_le32wu
|
||||
#endif
|
||||
|
||||
#undef le_bswap
|
||||
#undef be_bswap
|
||||
#undef le_bswaps
|
||||
#undef be_bswaps
|
||||
|
||||
#endif /* BSWAP_H */
|
||||
@@ -1,18 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
cat > .gcrypt.c << EOF
|
||||
#include <gcrypt.h>
|
||||
int main(void)
|
||||
{
|
||||
gcry_md_hash_buffer(GCRY_MD_MD5, NULL, NULL, 0);
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
|
||||
if $1 -o .gcrypt .gcrypt.c -lgcrypt 2>/dev/null ; then
|
||||
echo "yes"
|
||||
else
|
||||
echo "no"
|
||||
fi
|
||||
|
||||
rm -f .gcrypt*
|
||||
@@ -1,279 +0,0 @@
|
||||
/* Copyright (C) 2004 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */
|
||||
|
||||
/*
|
||||
* There are duplicates of this code in:
|
||||
* - tools/xenstore/hashtable.c
|
||||
*/
|
||||
|
||||
#include "hashtable.h"
|
||||
#include "hashtable_private.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
Credit for primes table: Aaron Krowne
|
||||
http://br.endernet.org/~akrowne/
|
||||
http://planetmath.org/encyclopedia/GoodHashTablePrimes.html
|
||||
*/
|
||||
static const unsigned int primes[] = {
|
||||
53, 97, 193, 389,
|
||||
769, 1543, 3079, 6151,
|
||||
12289, 24593, 49157, 98317,
|
||||
196613, 393241, 786433, 1572869,
|
||||
3145739, 6291469, 12582917, 25165843,
|
||||
50331653, 100663319, 201326611, 402653189,
|
||||
805306457, 1610612741
|
||||
};
|
||||
const unsigned int prime_table_length = sizeof(primes)/sizeof(primes[0]);
|
||||
const float max_load_factor = 0.65;
|
||||
|
||||
/*****************************************************************************/
|
||||
struct hashtable *
|
||||
create_hashtable(unsigned int minsize,
|
||||
unsigned int (*hashf) (void*),
|
||||
int (*eqf) (void*,void*))
|
||||
{
|
||||
struct hashtable *h;
|
||||
unsigned int pindex, size = primes[0];
|
||||
/* Check requested hashtable isn't too large */
|
||||
if (minsize > (1u << 30)) return NULL;
|
||||
/* Enforce size as prime */
|
||||
for (pindex=0; pindex < prime_table_length; pindex++) {
|
||||
if (primes[pindex] > minsize) { size = primes[pindex]; break; }
|
||||
}
|
||||
h = (struct hashtable *)malloc(sizeof(struct hashtable));
|
||||
if (NULL == h) return NULL; /*oom*/
|
||||
h->table = (struct entry **)malloc(sizeof(struct entry*) * size);
|
||||
if (NULL == h->table) { free(h); return NULL; } /*oom*/
|
||||
memset(h->table, 0, size * sizeof(struct entry *));
|
||||
h->tablelength = size;
|
||||
h->primeindex = pindex;
|
||||
h->entrycount = 0;
|
||||
h->hashfn = hashf;
|
||||
h->eqfn = eqf;
|
||||
h->loadlimit = (unsigned int) ceil(size * max_load_factor);
|
||||
return h;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
unsigned int
|
||||
hash(struct hashtable *h, void *k)
|
||||
{
|
||||
/* Aim to protect against poor hash functions by adding logic here
|
||||
* - logic taken from java 1.4 hashtable source */
|
||||
unsigned int i = h->hashfn(k);
|
||||
i += ~(i << 9);
|
||||
i ^= ((i >> 14) | (i << 18)); /* >>> */
|
||||
i += (i << 4);
|
||||
i ^= ((i >> 10) | (i << 22)); /* >>> */
|
||||
return i;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
static int
|
||||
hashtable_expand(struct hashtable *h)
|
||||
{
|
||||
/* Double the size of the table to accomodate more entries */
|
||||
struct entry **newtable;
|
||||
struct entry *e;
|
||||
struct entry **pE;
|
||||
unsigned int newsize, i, index;
|
||||
/* Check we're not hitting max capacity */
|
||||
if (h->primeindex == (prime_table_length - 1)) return 0;
|
||||
newsize = primes[++(h->primeindex)];
|
||||
|
||||
newtable = (struct entry **)malloc(sizeof(struct entry*) * newsize);
|
||||
if (NULL != newtable)
|
||||
{
|
||||
memset(newtable, 0, newsize * sizeof(struct entry *));
|
||||
/* This algorithm is not 'stable'. ie. it reverses the list
|
||||
* when it transfers entries between the tables */
|
||||
for (i = 0; i < h->tablelength; i++) {
|
||||
while (NULL != (e = h->table[i])) {
|
||||
h->table[i] = e->next;
|
||||
index = indexFor(newsize,e->h);
|
||||
e->next = newtable[index];
|
||||
newtable[index] = e;
|
||||
}
|
||||
}
|
||||
free(h->table);
|
||||
h->table = newtable;
|
||||
}
|
||||
/* Plan B: realloc instead */
|
||||
else
|
||||
{
|
||||
newtable = (struct entry **)
|
||||
realloc(h->table, newsize * sizeof(struct entry *));
|
||||
if (NULL == newtable) { (h->primeindex)--; return 0; }
|
||||
h->table = newtable;
|
||||
memset(newtable[h->tablelength], 0, newsize - h->tablelength);
|
||||
for (i = 0; i < h->tablelength; i++) {
|
||||
for (pE = &(newtable[i]), e = *pE; e != NULL; e = *pE) {
|
||||
index = indexFor(newsize,e->h);
|
||||
if (index == i)
|
||||
{
|
||||
pE = &(e->next);
|
||||
}
|
||||
else
|
||||
{
|
||||
*pE = e->next;
|
||||
e->next = newtable[index];
|
||||
newtable[index] = e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
h->tablelength = newsize;
|
||||
h->loadlimit = (unsigned int) ceil(newsize * max_load_factor);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
unsigned int
|
||||
hashtable_count(struct hashtable *h)
|
||||
{
|
||||
return h->entrycount;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
int
|
||||
hashtable_insert(struct hashtable *h, void *k, void *v)
|
||||
{
|
||||
/* This method allows duplicate keys - but they shouldn't be used */
|
||||
unsigned int index;
|
||||
struct entry *e;
|
||||
if (++(h->entrycount) > h->loadlimit)
|
||||
{
|
||||
/* Ignore the return value. If expand fails, we should
|
||||
* still try cramming just this value into the existing table
|
||||
* -- we may not have memory for a larger table, but one more
|
||||
* element may be ok. Next time we insert, we'll try expanding again.*/
|
||||
hashtable_expand(h);
|
||||
}
|
||||
e = (struct entry *)malloc(sizeof(struct entry));
|
||||
if (NULL == e) { --(h->entrycount); return 0; } /*oom*/
|
||||
e->h = hash(h,k);
|
||||
index = indexFor(h->tablelength,e->h);
|
||||
e->k = k;
|
||||
e->v = v;
|
||||
e->next = h->table[index];
|
||||
h->table[index] = e;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
void * /* returns value associated with key */
|
||||
hashtable_search(struct hashtable *h, void *k)
|
||||
{
|
||||
struct entry *e;
|
||||
unsigned int hashvalue, index;
|
||||
hashvalue = hash(h,k);
|
||||
index = indexFor(h->tablelength,hashvalue);
|
||||
e = h->table[index];
|
||||
while (NULL != e)
|
||||
{
|
||||
/* Check hash value to short circuit heavier comparison */
|
||||
if ((hashvalue == e->h) && (h->eqfn(k, e->k))) return e->v;
|
||||
e = e->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
void * /* returns value associated with key */
|
||||
hashtable_remove(struct hashtable *h, void *k)
|
||||
{
|
||||
/* TODO: consider compacting the table when the load factor drops enough,
|
||||
* or provide a 'compact' method. */
|
||||
|
||||
struct entry *e;
|
||||
struct entry **pE;
|
||||
void *v;
|
||||
unsigned int hashvalue, index;
|
||||
|
||||
hashvalue = hash(h,k);
|
||||
index = indexFor(h->tablelength,hash(h,k));
|
||||
pE = &(h->table[index]);
|
||||
e = *pE;
|
||||
while (NULL != e)
|
||||
{
|
||||
/* Check hash value to short circuit heavier comparison */
|
||||
if ((hashvalue == e->h) && (h->eqfn(k, e->k)))
|
||||
{
|
||||
*pE = e->next;
|
||||
h->entrycount--;
|
||||
v = e->v;
|
||||
freekey(e->k);
|
||||
free(e);
|
||||
return v;
|
||||
}
|
||||
pE = &(e->next);
|
||||
e = e->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* destroy */
|
||||
void
|
||||
hashtable_destroy(struct hashtable *h, int free_values)
|
||||
{
|
||||
unsigned int i;
|
||||
struct entry *e, *f;
|
||||
struct entry **table = h->table;
|
||||
if (free_values)
|
||||
{
|
||||
for (i = 0; i < h->tablelength; i++)
|
||||
{
|
||||
e = table[i];
|
||||
while (NULL != e)
|
||||
{ f = e; e = e->next; freekey(f->k); free(f->v); free(f); }
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < h->tablelength; i++)
|
||||
{
|
||||
e = table[i];
|
||||
while (NULL != e)
|
||||
{ f = e; e = e->next; freekey(f->k); free(f); }
|
||||
}
|
||||
}
|
||||
free(h->table);
|
||||
free(h);
|
||||
}
|
||||
|
||||
/*
|
||||
* Copyright (c) 2002, Christopher Clark
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the original author; nor the names of any contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
@@ -1,204 +0,0 @@
|
||||
/* Copyright (C) 2002 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */
|
||||
|
||||
/*
|
||||
* There are duplicates of this code in:
|
||||
* - tools/xenstore/hashtable.h
|
||||
*/
|
||||
|
||||
#ifndef __HASHTABLE_CWC22_H__
|
||||
#define __HASHTABLE_CWC22_H__
|
||||
|
||||
struct hashtable;
|
||||
|
||||
/* Example of use:
|
||||
*
|
||||
* struct hashtable *h;
|
||||
* struct some_key *k;
|
||||
* struct some_value *v;
|
||||
*
|
||||
* static unsigned int hash_from_key_fn( void *k );
|
||||
* static int keys_equal_fn ( void *key1, void *key2 );
|
||||
*
|
||||
* h = create_hashtable(16, hash_from_key_fn, keys_equal_fn);
|
||||
* k = (struct some_key *) malloc(sizeof(struct some_key));
|
||||
* v = (struct some_value *) malloc(sizeof(struct some_value));
|
||||
*
|
||||
* (initialise k and v to suitable values)
|
||||
*
|
||||
* if (! hashtable_insert(h,k,v) )
|
||||
* { exit(-1); }
|
||||
*
|
||||
* if (NULL == (found = hashtable_search(h,k) ))
|
||||
* { printf("not found!"); }
|
||||
*
|
||||
* if (NULL == (found = hashtable_remove(h,k) ))
|
||||
* { printf("Not found\n"); }
|
||||
*
|
||||
*/
|
||||
|
||||
/* Macros may be used to define type-safe(r) hashtable access functions, with
|
||||
* methods specialized to take known key and value types as parameters.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* Insert this at the start of your file:
|
||||
*
|
||||
* DEFINE_HASHTABLE_INSERT(insert_some, struct some_key, struct some_value);
|
||||
* DEFINE_HASHTABLE_SEARCH(search_some, struct some_key, struct some_value);
|
||||
* DEFINE_HASHTABLE_REMOVE(remove_some, struct some_key, struct some_value);
|
||||
*
|
||||
* This defines the functions 'insert_some', 'search_some' and 'remove_some'.
|
||||
* These operate just like hashtable_insert etc., with the same parameters,
|
||||
* but their function signatures have 'struct some_key *' rather than
|
||||
* 'void *', and hence can generate compile time errors if your program is
|
||||
* supplying incorrect data as a key (and similarly for value).
|
||||
*
|
||||
* Note that the hash and key equality functions passed to create_hashtable
|
||||
* still take 'void *' parameters instead of 'some key *'. This shouldn't be
|
||||
* a difficult issue as they're only defined and passed once, and the other
|
||||
* functions will ensure that only valid keys are supplied to them.
|
||||
*
|
||||
* The cost for this checking is increased code size and runtime overhead
|
||||
* - if performance is important, it may be worth switching back to the
|
||||
* unsafe methods once your program has been debugged with the safe methods.
|
||||
* This just requires switching to some simple alternative defines - eg:
|
||||
* #define insert_some hashtable_insert
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
* create_hashtable
|
||||
|
||||
* @name create_hashtable
|
||||
* @param minsize minimum initial size of hashtable
|
||||
* @param hashfunction function for hashing keys
|
||||
* @param key_eq_fn function for determining key equality
|
||||
* @return newly created hashtable or NULL on failure
|
||||
*/
|
||||
|
||||
struct hashtable *
|
||||
create_hashtable(unsigned int minsize,
|
||||
unsigned int (*hashfunction) (void*),
|
||||
int (*key_eq_fn) (void*,void*));
|
||||
|
||||
/*****************************************************************************
|
||||
* hashtable_insert
|
||||
|
||||
* @name hashtable_insert
|
||||
* @param h the hashtable to insert into
|
||||
* @param k the key - hashtable claims ownership and will free on removal
|
||||
* @param v the value - does not claim ownership
|
||||
* @return non-zero for successful insertion
|
||||
*
|
||||
* This function will cause the table to expand if the insertion would take
|
||||
* the ratio of entries to table size over the maximum load factor.
|
||||
*
|
||||
* This function does not check for repeated insertions with a duplicate key.
|
||||
* The value returned when using a duplicate key is undefined -- when
|
||||
* the hashtable changes size, the order of retrieval of duplicate key
|
||||
* entries is reversed.
|
||||
* If in doubt, remove before insert.
|
||||
*/
|
||||
|
||||
int
|
||||
hashtable_insert(struct hashtable *h, void *k, void *v);
|
||||
|
||||
#define DEFINE_HASHTABLE_INSERT(fnname, keytype, valuetype) \
|
||||
int fnname (struct hashtable *h, keytype *k, valuetype *v) \
|
||||
{ \
|
||||
return hashtable_insert(h,k,v); \
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* hashtable_search
|
||||
|
||||
* @name hashtable_search
|
||||
* @param h the hashtable to search
|
||||
* @param k the key to search for - does not claim ownership
|
||||
* @return the value associated with the key, or NULL if none found
|
||||
*/
|
||||
|
||||
void *
|
||||
hashtable_search(struct hashtable *h, void *k);
|
||||
|
||||
#define DEFINE_HASHTABLE_SEARCH(fnname, keytype, valuetype) \
|
||||
valuetype * fnname (struct hashtable *h, keytype *k) \
|
||||
{ \
|
||||
return (valuetype *) (hashtable_search(h,k)); \
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* hashtable_remove
|
||||
|
||||
* @name hashtable_remove
|
||||
* @param h the hashtable to remove the item from
|
||||
* @param k the key to search for - does not claim ownership
|
||||
* @return the value associated with the key, or NULL if none found
|
||||
*/
|
||||
|
||||
void * /* returns value */
|
||||
hashtable_remove(struct hashtable *h, void *k);
|
||||
|
||||
#define DEFINE_HASHTABLE_REMOVE(fnname, keytype, valuetype) \
|
||||
valuetype * fnname (struct hashtable *h, keytype *k) \
|
||||
{ \
|
||||
return (valuetype *) (hashtable_remove(h,k)); \
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* hashtable_count
|
||||
|
||||
* @name hashtable_count
|
||||
* @param h the hashtable
|
||||
* @return the number of items stored in the hashtable
|
||||
*/
|
||||
unsigned int
|
||||
hashtable_count(struct hashtable *h);
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* hashtable_destroy
|
||||
|
||||
* @name hashtable_destroy
|
||||
* @param h the hashtable
|
||||
* @param free_values whether to call 'free' on the remaining values
|
||||
*/
|
||||
|
||||
void
|
||||
hashtable_destroy(struct hashtable *h, int free_values);
|
||||
|
||||
#endif /* __HASHTABLE_CWC22_H__ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2002, Christopher Clark
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the original author; nor the names of any contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
@@ -1,195 +0,0 @@
|
||||
/* Copyright (C) 2002, 2004 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */
|
||||
|
||||
#include "hashtable.h"
|
||||
#include "hashtable_private.h"
|
||||
#include "hashtable_itr.h"
|
||||
#include <stdlib.h> /* defines NULL */
|
||||
|
||||
struct hashtable_itr {
|
||||
struct hashtable *h;
|
||||
struct entry *e;
|
||||
struct entry *parent;
|
||||
unsigned int index;
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
/* hashtable_iterator - iterator constructor */
|
||||
|
||||
struct hashtable_itr *
|
||||
hashtable_iterator(struct hashtable *h)
|
||||
{
|
||||
unsigned int i, tablelength;
|
||||
struct hashtable_itr *itr = (struct hashtable_itr *)
|
||||
malloc(sizeof(struct hashtable_itr));
|
||||
if (NULL == itr) return NULL;
|
||||
itr->h = h;
|
||||
itr->e = NULL;
|
||||
itr->parent = NULL;
|
||||
tablelength = h->tablelength;
|
||||
itr->index = tablelength;
|
||||
if (0 == h->entrycount) return itr;
|
||||
|
||||
for (i = 0; i < tablelength; i++)
|
||||
{
|
||||
if (NULL != h->table[i])
|
||||
{
|
||||
itr->e = h->table[i];
|
||||
itr->index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return itr;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* key - return the key of the (key,value) pair at the current position */
|
||||
/* value - return the value of the (key,value) pair at the current position */
|
||||
|
||||
void *
|
||||
hashtable_iterator_key(struct hashtable_itr *i)
|
||||
{ return i->e->k; }
|
||||
|
||||
void *
|
||||
hashtable_iterator_value(struct hashtable_itr *i)
|
||||
{ return i->e->v; }
|
||||
|
||||
/*****************************************************************************/
|
||||
/* advance - advance the iterator to the next element
|
||||
* returns zero if advanced to end of table */
|
||||
|
||||
int
|
||||
hashtable_iterator_advance(struct hashtable_itr *itr)
|
||||
{
|
||||
unsigned int j,tablelength;
|
||||
struct entry **table;
|
||||
struct entry *next;
|
||||
if (NULL == itr->e) return 0; /* stupidity check */
|
||||
|
||||
next = itr->e->next;
|
||||
if (NULL != next)
|
||||
{
|
||||
itr->parent = itr->e;
|
||||
itr->e = next;
|
||||
return -1;
|
||||
}
|
||||
tablelength = itr->h->tablelength;
|
||||
itr->parent = NULL;
|
||||
if (tablelength <= (j = ++(itr->index)))
|
||||
{
|
||||
itr->e = NULL;
|
||||
return 0;
|
||||
}
|
||||
table = itr->h->table;
|
||||
while (NULL == (next = table[j]))
|
||||
{
|
||||
if (++j >= tablelength)
|
||||
{
|
||||
itr->index = tablelength;
|
||||
itr->e = NULL;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
itr->index = j;
|
||||
itr->e = next;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* remove - remove the entry at the current iterator position
|
||||
* and advance the iterator, if there is a successive
|
||||
* element.
|
||||
* If you want the value, read it before you remove:
|
||||
* beware memory leaks if you don't.
|
||||
* Returns zero if end of iteration. */
|
||||
|
||||
int
|
||||
hashtable_iterator_remove(struct hashtable_itr *itr)
|
||||
{
|
||||
struct entry *remember_e, *remember_parent;
|
||||
int ret;
|
||||
|
||||
/* Do the removal */
|
||||
if (NULL == (itr->parent))
|
||||
{
|
||||
/* element is head of a chain */
|
||||
itr->h->table[itr->index] = itr->e->next;
|
||||
} else {
|
||||
/* element is mid-chain */
|
||||
itr->parent->next = itr->e->next;
|
||||
}
|
||||
/* itr->e is now outside the hashtable */
|
||||
remember_e = itr->e;
|
||||
itr->h->entrycount--;
|
||||
freekey(remember_e->k);
|
||||
|
||||
/* Advance the iterator, correcting the parent */
|
||||
remember_parent = itr->parent;
|
||||
ret = hashtable_iterator_advance(itr);
|
||||
if (itr->parent == remember_e) { itr->parent = remember_parent; }
|
||||
free(remember_e);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
int /* returns zero if not found */
|
||||
hashtable_iterator_search(struct hashtable_itr *itr,
|
||||
struct hashtable *h, void *k)
|
||||
{
|
||||
struct entry *e, *parent;
|
||||
unsigned int hashvalue, index;
|
||||
|
||||
hashvalue = hash(h,k);
|
||||
index = indexFor(h->tablelength,hashvalue);
|
||||
|
||||
e = h->table[index];
|
||||
parent = NULL;
|
||||
while (NULL != e)
|
||||
{
|
||||
/* Check hash value to short circuit heavier comparison */
|
||||
if ((hashvalue == e->h) && (h->eqfn(k, e->k)))
|
||||
{
|
||||
itr->index = index;
|
||||
itr->e = e;
|
||||
itr->parent = parent;
|
||||
itr->h = h;
|
||||
return -1;
|
||||
}
|
||||
parent = e;
|
||||
e = e->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Copyright (c) 2002, 2004, Christopher Clark
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the original author; nor the names of any contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
@@ -1,96 +0,0 @@
|
||||
/* Copyright (C) 2002, 2004 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */
|
||||
|
||||
#ifndef __HASHTABLE_ITR_CWC22__
|
||||
#define __HASHTABLE_ITR_CWC22__
|
||||
#include "hashtable.h"
|
||||
#include "hashtable_private.h" /* needed to enable inlining */
|
||||
|
||||
struct hashtable_itr;
|
||||
|
||||
/*****************************************************************************/
|
||||
/* hashtable_iterator
|
||||
*/
|
||||
|
||||
struct hashtable_itr *
|
||||
hashtable_iterator(struct hashtable *h);
|
||||
|
||||
/*****************************************************************************/
|
||||
/* hashtable_iterator_key
|
||||
* - return the value of the (key,value) pair at the current position */
|
||||
|
||||
void *
|
||||
hashtable_iterator_key(struct hashtable_itr *i);
|
||||
|
||||
/*****************************************************************************/
|
||||
/* value - return the value of the (key,value) pair at the current position */
|
||||
|
||||
void *
|
||||
hashtable_iterator_value(struct hashtable_itr *i);
|
||||
|
||||
/*****************************************************************************/
|
||||
/* advance - advance the iterator to the next element
|
||||
* returns zero if advanced to end of table */
|
||||
|
||||
int
|
||||
hashtable_iterator_advance(struct hashtable_itr *itr);
|
||||
|
||||
/*****************************************************************************/
|
||||
/* remove - remove current element and advance the iterator to the next element
|
||||
* NB: if you need the value to free it, read it before
|
||||
* removing. ie: beware memory leaks!
|
||||
* returns zero if advanced to end of table */
|
||||
|
||||
int
|
||||
hashtable_iterator_remove(struct hashtable_itr *itr);
|
||||
|
||||
/*****************************************************************************/
|
||||
/* search - overwrite the supplied iterator, to point to the entry
|
||||
* matching the supplied key.
|
||||
h points to the hashtable to be searched.
|
||||
* returns zero if not found. */
|
||||
int
|
||||
hashtable_iterator_search(struct hashtable_itr *itr,
|
||||
struct hashtable *h, void *k);
|
||||
|
||||
#define DEFINE_HASHTABLE_ITERATOR_SEARCH(fnname, keytype) \
|
||||
int fnname (struct hashtable_itr *i, struct hashtable *h, keytype *k) \
|
||||
{ \
|
||||
return (hashtable_iterator_search(i,h,k)); \
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif /* __HASHTABLE_ITR_CWC22__*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2002, 2004, Christopher Clark
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the original author; nor the names of any contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
@@ -1,90 +0,0 @@
|
||||
/* Copyright (C) 2002, 2004 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */
|
||||
|
||||
/*
|
||||
* There are duplicates of this code in:
|
||||
* - tools/xenstore/hashtable_private.h
|
||||
*/
|
||||
|
||||
#ifndef __HASHTABLE_PRIVATE_CWC22_H__
|
||||
#define __HASHTABLE_PRIVATE_CWC22_H__
|
||||
|
||||
#include "hashtable.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
struct entry
|
||||
{
|
||||
void *k, *v;
|
||||
unsigned int h;
|
||||
struct entry *next;
|
||||
};
|
||||
|
||||
struct hashtable {
|
||||
unsigned int tablelength;
|
||||
struct entry **table;
|
||||
unsigned int entrycount;
|
||||
unsigned int loadlimit;
|
||||
unsigned int primeindex;
|
||||
unsigned int (*hashfn) (void *k);
|
||||
int (*eqfn) (void *k1, void *k2);
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
unsigned int
|
||||
hash(struct hashtable *h, void *k);
|
||||
|
||||
/*****************************************************************************/
|
||||
/* indexFor */
|
||||
static inline unsigned int
|
||||
indexFor(unsigned int tablelength, unsigned int hashvalue) {
|
||||
return (hashvalue % tablelength);
|
||||
};
|
||||
|
||||
/* Only works if tablelength == 2^N */
|
||||
/*static inline unsigned int
|
||||
indexFor(unsigned int tablelength, unsigned int hashvalue)
|
||||
{
|
||||
return (hashvalue & (tablelength - 1u));
|
||||
}
|
||||
*/
|
||||
|
||||
/*****************************************************************************/
|
||||
#define freekey(X) free(X)
|
||||
/*define freekey(X) ; */
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
#endif /* __HASHTABLE_PRIVATE_CWC22_H__*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2002, Christopher Clark
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the original author; nor the names of any contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
@@ -1,71 +0,0 @@
|
||||
/* Copyright (C) 2002 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */
|
||||
|
||||
#include "hashtable.h"
|
||||
#include "hashtable_private.h"
|
||||
#include "hashtable_utility.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/*****************************************************************************/
|
||||
/* hashtable_change
|
||||
*
|
||||
* function to change the value associated with a key, where there already
|
||||
* exists a value bound to the key in the hashtable.
|
||||
* Source due to Holger Schemel.
|
||||
*
|
||||
* */
|
||||
int
|
||||
hashtable_change(struct hashtable *h, void *k, void *v)
|
||||
{
|
||||
struct entry *e;
|
||||
unsigned int hashvalue, index;
|
||||
hashvalue = hash(h,k);
|
||||
index = indexFor(h->tablelength,hashvalue);
|
||||
e = h->table[index];
|
||||
while (NULL != e)
|
||||
{
|
||||
/* Check hash value to short circuit heavier comparison */
|
||||
if ((hashvalue == e->h) && (h->eqfn(k, e->k)))
|
||||
{
|
||||
free(e->v);
|
||||
e->v = v;
|
||||
return -1;
|
||||
}
|
||||
e = e->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Copyright (c) 2002, Christopher Clark
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the original author; nor the names of any contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
@@ -1,55 +0,0 @@
|
||||
/* Copyright (C) 2002 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */
|
||||
|
||||
#ifndef __HASHTABLE_CWC22_UTILITY_H__
|
||||
#define __HASHTABLE_CWC22_UTILITY_H__
|
||||
|
||||
/*****************************************************************************
|
||||
* hashtable_change
|
||||
*
|
||||
* function to change the value associated with a key, where there already
|
||||
* exists a value bound to the key in the hashtable.
|
||||
* Source due to Holger Schemel.
|
||||
*
|
||||
* @name hashtable_change
|
||||
* @param h the hashtable
|
||||
* @param key
|
||||
* @param value
|
||||
*
|
||||
*/
|
||||
int
|
||||
hashtable_change(struct hashtable *h, void *k, void *v);
|
||||
|
||||
#endif /* __HASHTABLE_CWC22_H__ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2002, Christopher Clark
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the original author; nor the names of any contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
@@ -1,316 +0,0 @@
|
||||
/* img2qcow.c
|
||||
*
|
||||
* Generates a qcow format disk and fills it from an existing image.
|
||||
*
|
||||
* (c) 2006 Julian Chesterfield and Andrew Warfield
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation; or, when distributed
|
||||
* separately from the Linux kernel or incorporated into other
|
||||
* software packages, subject to the following license:
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this source file (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy, modify,
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <string.h>
|
||||
#include <zlib.h>
|
||||
#include <inttypes.h>
|
||||
#include <libaio.h>
|
||||
|
||||
#include "bswap.h"
|
||||
#include "aes.h"
|
||||
#include "tapdisk.h"
|
||||
#include "tapdisk-server.h"
|
||||
#include "tapdisk-driver.h"
|
||||
#include "tapdisk-interface.h"
|
||||
#include "tapdisk-disktype.h"
|
||||
#include "qcow.h"
|
||||
#include "blk.h"
|
||||
|
||||
|
||||
#if 1
|
||||
#define DFPRINTF(_f, _a...) fprintf ( stderr, _f , ## _a )
|
||||
#else
|
||||
#define DFPRINTF(_f, _a...) ((void)0)
|
||||
#endif
|
||||
|
||||
/* *BSD has no O_LARGEFILE */
|
||||
#ifndef O_LARGEFILE
|
||||
#define O_LARGEFILE 0
|
||||
#endif
|
||||
|
||||
#define BLOCK_PROCESSSZ 4096
|
||||
#define QCOW_VBD 0
|
||||
#define PROGRESS_QUANT 2
|
||||
|
||||
static int running = 1, complete = 0;
|
||||
static int returned_events = 0, submit_events = 0;
|
||||
static uint32_t read_idx = 0;
|
||||
td_driver_t *ddqcow;
|
||||
td_vbd_t* qcow_vbd;
|
||||
static uint64_t prev = 0, written = 0;
|
||||
static char output[(100/PROGRESS_QUANT) + 5];
|
||||
|
||||
extern tapdisk_server_t server;
|
||||
|
||||
|
||||
static void print_bytes(void *ptr, int length)
|
||||
{
|
||||
int i,k;
|
||||
unsigned char *p = ptr;
|
||||
|
||||
DFPRINTF("Buf dump, length %d:\n",length);
|
||||
for (k = 0; k < length; k++) {
|
||||
DFPRINTF("%x",*p);
|
||||
*p++;
|
||||
if(k % 16 == 0) DFPRINTF("\n");
|
||||
else if(k % 2 == 0) DFPRINTF(" ");
|
||||
}
|
||||
DFPRINTF("\n");
|
||||
return;
|
||||
}
|
||||
|
||||
static void debug_output(uint64_t progress, uint64_t size)
|
||||
{
|
||||
//Output progress every PROGRESS_QUANT
|
||||
uint64_t blocks = size/(100/PROGRESS_QUANT);
|
||||
|
||||
if (progress/blocks > prev) {
|
||||
memcpy(output+prev+1,"=>",2);
|
||||
prev++;
|
||||
DFPRINTF("\r%s %"PRIi64"%%",
|
||||
output, (int64_t)((prev-1)*PROGRESS_QUANT));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static int get_image_info(td_disk_info_t *driver, int fd)
|
||||
{
|
||||
int ret;
|
||||
long size;
|
||||
unsigned long total_size;
|
||||
struct statvfs statBuf;
|
||||
struct stat stat;
|
||||
uint64_t sector_size=DEFAULT_SECTOR_SIZE;
|
||||
|
||||
ret = fstat(fd, &stat);
|
||||
if (ret != 0) {
|
||||
DFPRINTF("ERROR: fstat failed, Couldn't stat image");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (S_ISBLK(stat.st_mode)) {
|
||||
/*Accessing block device directly*/
|
||||
if (blk_getimagesize(fd, &driver->size) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
DFPRINTF("Image size: \n\tpre sector_shift [%"PRIu64"]\n\tpost "
|
||||
"sector_shift [%"PRIu64"]\n",
|
||||
(uint64_t)(driver->size << SECTOR_SHIFT),
|
||||
(uint64_t)driver->size);
|
||||
|
||||
/*Get the sector size*/
|
||||
if (!blk_getsectorsize(fd, §or_size))
|
||||
driver->sector_size = sector_size;
|
||||
|
||||
} else {
|
||||
/*Local file? try fstat instead*/
|
||||
driver->size = (stat.st_size >> SECTOR_SHIFT);
|
||||
driver->sector_size = DEFAULT_SECTOR_SIZE;
|
||||
DFPRINTF("Image size: [%"PRIu64"]\n",
|
||||
(uint64_t)driver->size);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void send_responses(td_request_t treq, int err)
|
||||
{
|
||||
if (err < 0) {
|
||||
DFPRINTF("AIO FAILURE: res [%d]!\n",err);
|
||||
return;
|
||||
}
|
||||
|
||||
returned_events++;
|
||||
|
||||
free(treq.buf);
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int ret = -1, fd, len, err;
|
||||
struct timeval timeout;
|
||||
uint64_t i;
|
||||
char *buf = NULL;
|
||||
td_request_t treq;
|
||||
td_disk_info_t info;
|
||||
td_vbd_request_t* vreq;
|
||||
|
||||
if (argc != 3) {
|
||||
fprintf(stderr, "Qcow-utils: v1.0.0\n");
|
||||
fprintf(stderr, "usage: %s <QCOW FILENAME> <SRC IMAGE>\n",
|
||||
argv[0]);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
|
||||
/*Open image*/
|
||||
fd = open(argv[2], O_RDONLY | O_LARGEFILE);
|
||||
|
||||
if (fd == -1) {
|
||||
DFPRINTF("Unable to open [%s], (err %d)!\n",argv[2],0 - errno);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
get_image_info(&info, fd);
|
||||
|
||||
/*Create qcow file*/
|
||||
ret = qcow_create(argv[1],info.size<<SECTOR_SHIFT,NULL,0);
|
||||
|
||||
if (ret < 0) {
|
||||
DFPRINTF("Unable to create QCOW file\n");
|
||||
exit(-1);
|
||||
} else DFPRINTF("Qcow file created: size %"PRIu64" sectors\n",
|
||||
(uint64_t)info.size);
|
||||
|
||||
/* Open Qcow image*/
|
||||
err = tapdisk_server_initialize();
|
||||
if( err ) {
|
||||
DPRINTF("qcow2raw Couldn't initialize server instance.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err=tapdisk_vbd_initialize(QCOW_VBD);
|
||||
if( err ) {
|
||||
DPRINTF("qcow2raw Couldn't initialize qcow vbd.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
qcow_vbd = tapdisk_server_get_vbd(QCOW_VBD);
|
||||
if (!qcow_vbd) {
|
||||
err = -ENODEV;
|
||||
DPRINTF("qcow2raw Couldn't create qcow vbd.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = tapdisk_vbd_open_vdi(qcow_vbd, argv[1], DISK_TYPE_QCOW,
|
||||
TAPDISK_STORAGE_TYPE_DEFAULT,
|
||||
0);
|
||||
if( err ) {
|
||||
DPRINTF("qcow2raw Couldn't open qcow file.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
ddqcow=(tapdisk_vbd_first_image(qcow_vbd))->driver;
|
||||
|
||||
/*Initialise the output string*/
|
||||
memset(output,0x20,(100/PROGRESS_QUANT)+5);
|
||||
output[0] = '[';
|
||||
output[(100/PROGRESS_QUANT)+2] = ']';
|
||||
output[(100/PROGRESS_QUANT)+3] = '\0';
|
||||
DFPRINTF("%s",output);
|
||||
|
||||
i = 0;
|
||||
while (running) {
|
||||
|
||||
if (!complete) {
|
||||
/*Read sector from image*/
|
||||
if (lseek(fd, i*512, SEEK_SET) == (off_t)-1) {
|
||||
DFPRINTF("Unable to access file offset %"PRIu64"\n",
|
||||
(uint64_t)i*512);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if( (ret = posix_memalign((void **)&buf,
|
||||
BLOCK_PROCESSSZ,
|
||||
BLOCK_PROCESSSZ)) != 0) {
|
||||
DFPRINTF("Unable to read memalign buf (%d)\n",ret);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
/*We attempt to read 4k sized blocks*/
|
||||
len = read(fd, buf, BLOCK_PROCESSSZ);
|
||||
if (len < 512) {
|
||||
DFPRINTF("Unable to read sector %"PRIu64"\n",
|
||||
(uint64_t) (i));
|
||||
complete = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
len = (len >> 9);
|
||||
|
||||
treq.op = TD_OP_WRITE;
|
||||
treq.buf = buf;
|
||||
treq.sec = i;
|
||||
treq.secs = len;
|
||||
treq.image = 0;
|
||||
treq.cb = send_responses;
|
||||
treq.cb_data = buf;
|
||||
treq.id = 0;
|
||||
treq.sidx = 0;
|
||||
vreq = calloc(1, sizeof(td_vbd_request_t));
|
||||
treq.private = vreq;
|
||||
|
||||
vreq->submitting = 1;
|
||||
INIT_LIST_HEAD(&vreq->next);
|
||||
tapdisk_vbd_move_request(treq.private,
|
||||
&qcow_vbd->pending_requests);
|
||||
|
||||
ddqcow->ops->td_queue_write(ddqcow,treq);
|
||||
--vreq->submitting;
|
||||
|
||||
submit_events++;
|
||||
|
||||
i += len;
|
||||
|
||||
if (i == info.size)
|
||||
complete = 1;
|
||||
|
||||
tapdisk_submit_all_tiocbs(&server.aio_queue);
|
||||
debug_output(i,info.size);
|
||||
}
|
||||
|
||||
while(returned_events != submit_events) {
|
||||
ret = scheduler_wait_for_events(&server.scheduler);
|
||||
if (ret < 0) {
|
||||
DFPRINTF("server wait returned %d\n", ret);
|
||||
sleep(2);
|
||||
}
|
||||
}
|
||||
|
||||
if (complete && (returned_events == submit_events))
|
||||
running = 0;
|
||||
}
|
||||
memcpy(output+prev+1,"=",1);
|
||||
DFPRINTF("\r%s 100%%\nTRANSFER COMPLETE\n\n", output);
|
||||
|
||||
ddqcow->ops->td_close(ddqcow);
|
||||
free(ddqcow->data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,671 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "io-optimize.h"
|
||||
#include "tapdisk-log.h"
|
||||
|
||||
#if (!defined(TEST) && defined(DEBUG))
|
||||
#define DBG(ctx, f, a...) tlog_write(TLOG_DBG, f, ##a)
|
||||
#elif defined(TEST)
|
||||
#define DBG(ctx, f, a...) printf(f, ##a)
|
||||
#else
|
||||
#define DBG(ctx, f, a...) ((void)0)
|
||||
#endif
|
||||
|
||||
static void print_merged_iocbs(struct opioctx *ctx,
|
||||
struct iocb **iocbs, int num_iocbs);
|
||||
|
||||
void
|
||||
opio_free(struct opioctx *ctx)
|
||||
{
|
||||
free(ctx->opios);
|
||||
ctx->opios = NULL;
|
||||
|
||||
free(ctx->free_opios);
|
||||
ctx->free_opios = NULL;
|
||||
|
||||
free(ctx->iocb_queue);
|
||||
ctx->iocb_queue = NULL;
|
||||
|
||||
free(ctx->event_queue);
|
||||
ctx->event_queue = NULL;
|
||||
}
|
||||
|
||||
int
|
||||
opio_init(struct opioctx *ctx, int num_iocbs)
|
||||
{
|
||||
int i;
|
||||
|
||||
memset(ctx, 0, sizeof(struct opioctx));
|
||||
|
||||
ctx->num_opios = num_iocbs;
|
||||
ctx->free_opio_cnt = num_iocbs;
|
||||
ctx->opios = calloc(1, sizeof(struct opio) * num_iocbs);
|
||||
ctx->free_opios = calloc(1, sizeof(struct opio *) * num_iocbs);
|
||||
ctx->iocb_queue = calloc(1, sizeof(struct iocb *) * num_iocbs);
|
||||
ctx->event_queue = calloc(1, sizeof(struct io_event) * num_iocbs);
|
||||
|
||||
if (!ctx->opios || !ctx->free_opios ||
|
||||
!ctx->iocb_queue || !ctx->event_queue)
|
||||
goto fail;
|
||||
|
||||
for (i = 0; i < num_iocbs; i++)
|
||||
ctx->free_opios[i] = &ctx->opios[i];
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
opio_free(ctx);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static inline struct opio *
|
||||
alloc_opio(struct opioctx *ctx)
|
||||
{
|
||||
if (ctx->free_opio_cnt <= 0)
|
||||
return NULL;
|
||||
return ctx->free_opios[--ctx->free_opio_cnt];
|
||||
}
|
||||
|
||||
static inline void
|
||||
free_opio(struct opioctx *ctx, struct opio *op)
|
||||
{
|
||||
memset(op, 0, sizeof(struct opio));
|
||||
ctx->free_opios[ctx->free_opio_cnt++] = op;
|
||||
}
|
||||
|
||||
static inline void
|
||||
restore_iocb(struct opio *op)
|
||||
{
|
||||
struct iocb *io = op->iocb;
|
||||
|
||||
io->data = op->data;
|
||||
io->u.c.buf = op->buf;
|
||||
io->u.c.nbytes = op->nbytes;
|
||||
}
|
||||
|
||||
static inline int
|
||||
iocb_optimized(struct opioctx *ctx, struct iocb *io)
|
||||
{
|
||||
unsigned long iop = (unsigned long)io->data;
|
||||
unsigned long start = (unsigned long)ctx->opios;
|
||||
unsigned long end = start + (ctx->num_opios * sizeof(struct opio));
|
||||
|
||||
return (iop >= start && iop < end);
|
||||
}
|
||||
|
||||
static inline int
|
||||
contiguous_sectors(struct iocb *l, struct iocb *r)
|
||||
{
|
||||
return (l->u.c.offset + l->u.c.nbytes == r->u.c.offset);
|
||||
}
|
||||
|
||||
static inline int
|
||||
contiguous_buffers(struct iocb *l, struct iocb *r)
|
||||
{
|
||||
return (l->u.c.buf + l->u.c.nbytes == r->u.c.buf);
|
||||
}
|
||||
|
||||
static inline int
|
||||
contiguous_iocbs(struct iocb *l, struct iocb *r)
|
||||
{
|
||||
return ((l->aio_fildes == r->aio_fildes) &&
|
||||
contiguous_sectors(l, r) &&
|
||||
contiguous_buffers(l, r));
|
||||
}
|
||||
|
||||
static inline void
|
||||
init_opio_list(struct opio *op)
|
||||
{
|
||||
op->list.head = op->list.tail = op;
|
||||
}
|
||||
|
||||
static struct opio *
|
||||
opio_iocb_init(struct opioctx *ctx, struct iocb *io)
|
||||
{
|
||||
struct opio *op;
|
||||
|
||||
op = alloc_opio(ctx);
|
||||
if (!op)
|
||||
return NULL;
|
||||
|
||||
op->buf = io->u.c.buf;
|
||||
op->nbytes = io->u.c.nbytes;
|
||||
op->offset = io->u.c.offset;
|
||||
op->data = io->data;
|
||||
op->iocb = io;
|
||||
io->data = op;
|
||||
|
||||
init_opio_list(op);
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
static inline struct opio *
|
||||
opio_get(struct opioctx *ctx, struct iocb *io)
|
||||
{
|
||||
if (iocb_optimized(ctx, io))
|
||||
return (struct opio *)io->data;
|
||||
else
|
||||
return opio_iocb_init(ctx, io);
|
||||
}
|
||||
|
||||
static int
|
||||
merge_tail(struct opioctx *ctx, struct iocb *head, struct iocb *io)
|
||||
{
|
||||
struct opio *ophead, *opio;
|
||||
|
||||
ophead = opio_get(ctx, head);
|
||||
if (!ophead)
|
||||
return -ENOMEM;
|
||||
|
||||
opio = opio_get(ctx, io);
|
||||
if (!opio)
|
||||
return -ENOMEM;
|
||||
|
||||
opio->head = ophead;
|
||||
head->u.c.nbytes += io->u.c.nbytes;
|
||||
ophead->list.tail = ophead->list.tail->next = opio;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
merge(struct opioctx *ctx, struct iocb *head, struct iocb *io)
|
||||
{
|
||||
if (head->aio_lio_opcode != io->aio_lio_opcode)
|
||||
return -EINVAL;
|
||||
|
||||
if (!contiguous_iocbs(head, io))
|
||||
return -EINVAL;
|
||||
|
||||
return merge_tail(ctx, head, io);
|
||||
}
|
||||
|
||||
int
|
||||
io_merge(struct opioctx *ctx, struct iocb **queue, int num)
|
||||
{
|
||||
int i, on_queue;
|
||||
struct iocb *io, **q;
|
||||
struct opio *ophead;
|
||||
|
||||
if (!num)
|
||||
return 0;
|
||||
|
||||
on_queue = 0;
|
||||
q = ctx->iocb_queue;
|
||||
memcpy(q, queue, num * sizeof(struct iocb *));
|
||||
|
||||
for (i = 1; i < num; i++) {
|
||||
io = q[i];
|
||||
if (merge(ctx, queue[on_queue], io) != 0)
|
||||
queue[++on_queue] = io;
|
||||
}
|
||||
|
||||
#if (defined(TEST) || defined(DEBUG))
|
||||
print_merged_iocbs(ctx, queue, on_queue + 1);
|
||||
#endif
|
||||
|
||||
return ++on_queue;
|
||||
}
|
||||
|
||||
static int
|
||||
expand_iocb(struct opioctx *ctx, struct iocb **queue, struct iocb *io)
|
||||
{
|
||||
int idx;
|
||||
struct opio *op, *next;
|
||||
|
||||
idx = 0;
|
||||
op = (struct opio *)io->data;
|
||||
while (op) {
|
||||
next = op->next;
|
||||
restore_iocb(op);
|
||||
queue[idx++] = op->iocb;
|
||||
free_opio(ctx, op);
|
||||
op = next;
|
||||
}
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
int
|
||||
io_expand_iocbs(struct opioctx *ctx, struct iocb **queue, int idx, int num)
|
||||
{
|
||||
int i, on_queue;
|
||||
struct iocb *io, **q;
|
||||
|
||||
if (!num)
|
||||
return 0;
|
||||
|
||||
on_queue = 0;
|
||||
q = ctx->iocb_queue;
|
||||
memcpy(q, queue, num * sizeof(struct iocb *));
|
||||
|
||||
for (i = idx; i < num; i++) {
|
||||
io = q[i];
|
||||
if (!iocb_optimized(ctx, io))
|
||||
queue[on_queue++] = io;
|
||||
else
|
||||
on_queue += expand_iocb(ctx, queue + on_queue, io);
|
||||
}
|
||||
|
||||
return on_queue;
|
||||
}
|
||||
|
||||
static int
|
||||
expand_event(struct opioctx *ctx,
|
||||
struct io_event *event, struct io_event *queue, int idx)
|
||||
{
|
||||
int err;
|
||||
struct iocb *io;
|
||||
struct io_event *ep;
|
||||
struct opio *ophead, *op, *next;
|
||||
|
||||
io = event->obj;
|
||||
ophead = (struct opio *)io->data;
|
||||
op = ophead;
|
||||
|
||||
if (event->res == io->u.c.nbytes)
|
||||
err = 0;
|
||||
else if ((int)event->res < 0)
|
||||
err = (int)event->res;
|
||||
else
|
||||
err = -EIO;
|
||||
|
||||
while (op) {
|
||||
next = op->next;
|
||||
ep = &queue[idx++];
|
||||
ep->obj = op->iocb;
|
||||
ep->res = (err ? err : op->nbytes);
|
||||
restore_iocb(op);
|
||||
free_opio(ctx, op);
|
||||
op = next;
|
||||
}
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
int
|
||||
io_split(struct opioctx *ctx, struct io_event *events, int num)
|
||||
{
|
||||
int on_queue;
|
||||
struct iocb *io;
|
||||
struct io_event *ep, *q;
|
||||
|
||||
if (!num)
|
||||
return 0;
|
||||
|
||||
on_queue = 0;
|
||||
q = ctx->event_queue;
|
||||
memcpy(q, events, num * sizeof(struct io_event));
|
||||
|
||||
for (ep = q; num-- > 0; ep++) {
|
||||
io = ep->obj;
|
||||
if (!iocb_optimized(ctx, io))
|
||||
events[on_queue++] = *ep;
|
||||
else
|
||||
on_queue = expand_event(ctx, ep, events, on_queue);
|
||||
}
|
||||
|
||||
return on_queue;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
debug print functions
|
||||
******************************************************************************/
|
||||
static inline void
|
||||
__print_iocb(struct opioctx *ctx, struct iocb *io, char *prefix)
|
||||
{
|
||||
char *type;
|
||||
|
||||
type = (io->aio_lio_opcode == IO_CMD_PREAD ? "read" : "write");
|
||||
|
||||
DBG(ctx, "%soff: %08llx, nbytes: %04lx, buf: %p, type: %s, data: %08lx,"
|
||||
" optimized: %d\n", prefix, io->u.c.offset, io->u.c.nbytes,
|
||||
io->u.c.buf, type, (unsigned long)io->data,
|
||||
iocb_optimized(ctx, io));
|
||||
}
|
||||
|
||||
static char *null_prefix = "";
|
||||
#define print_iocb(ctx, io) __print_iocb(ctx, io, null_prefix)
|
||||
|
||||
static void
|
||||
print_iocbs(struct opioctx *ctx, struct iocb **iocbs, int num_iocbs)
|
||||
{
|
||||
int i;
|
||||
char pref[10];
|
||||
struct iocb *io;
|
||||
|
||||
DBG(ctx, "iocbs:\n");
|
||||
for (i = 0; i < num_iocbs; i++) {
|
||||
io = iocbs[i];
|
||||
snprintf(pref, 10, "%d: ", i);
|
||||
__print_iocb(ctx, io, pref);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
print_optimized_iocbs(struct opioctx *ctx, struct opio *op, int *cnt)
|
||||
{
|
||||
char pref[10];
|
||||
|
||||
while (op) {
|
||||
snprintf(pref, 10, " %d: ", (*cnt)++);
|
||||
__print_iocb(ctx, op->iocb, pref);
|
||||
op = op->next;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
print_merged_iocbs(struct opioctx *ctx, struct iocb **iocbs, int num_iocbs)
|
||||
{
|
||||
int i, cnt;
|
||||
char pref[10];
|
||||
struct iocb *io;
|
||||
struct opio *op;
|
||||
|
||||
DBG(ctx, "merged iocbs:\n");
|
||||
for (i = 0, cnt = 0; i < num_iocbs; i++) {
|
||||
io = iocbs[i];
|
||||
snprintf(pref, 10, "%d: ", cnt++);
|
||||
__print_iocb(ctx, io, pref);
|
||||
|
||||
if (iocb_optimized(ctx, io)) {
|
||||
op = (struct opio *)io->data;
|
||||
print_optimized_iocbs(ctx, op->next, &cnt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
print_events(struct opioctx *ctx, struct io_event *events, int num_events)
|
||||
{
|
||||
int i;
|
||||
struct iocb *io;
|
||||
|
||||
for (i = 0; i < num_events; i++) {
|
||||
io = events[i].obj;
|
||||
print_iocb(ctx, io);
|
||||
}
|
||||
}
|
||||
/******************************************************************************
|
||||
end debug print functions
|
||||
******************************************************************************/
|
||||
|
||||
#if defined(TEST)
|
||||
|
||||
#define hmask 0x80000000UL
|
||||
#define smask 0x40000000UL
|
||||
#define make_data(idx, is_head, sparse) \
|
||||
(void *)((idx) | ((is_head) ? hmask : 0) | ((sparse) ? smask : 0))
|
||||
#define data_idx(data) (int)((unsigned long)(data) & (0x0fffffff))
|
||||
#define data_is_head(data) (((unsigned long)(data) & hmask) ? 1 : 0)
|
||||
#define data_is_sparse(data) (((unsigned long)(data) & smask) ? 1 : 0)
|
||||
|
||||
static void
|
||||
usage(void)
|
||||
{
|
||||
fprintf(stderr, "usage: io_optimize [-n num_runs] "
|
||||
"[-i num_iocbs] [-s num_secs] [-r random_seed]\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
static int xalloc_cnt, xfree_cnt;
|
||||
static inline char *
|
||||
xalloc(int size)
|
||||
{
|
||||
char *buf = malloc(size);
|
||||
if (!buf) {
|
||||
fprintf(stderr, "xalloc failed\n");
|
||||
exit(ENOMEM);
|
||||
}
|
||||
xalloc_cnt++;
|
||||
return buf;
|
||||
}
|
||||
|
||||
static inline void
|
||||
xfree(void *buf)
|
||||
{
|
||||
free(buf);
|
||||
xfree_cnt++;
|
||||
}
|
||||
|
||||
static void
|
||||
randomize_iocbs(struct iocb **iocbs, int num_iocbs, int num_secs)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
i = 0;
|
||||
while (i < num_iocbs) {
|
||||
char *buf;
|
||||
short type;
|
||||
int segs, sparse_mem;
|
||||
uint64_t offset, nbytes;
|
||||
|
||||
type = (random() % 10 < 5 ? IO_CMD_PREAD : IO_CMD_PWRITE);
|
||||
offset = ((random() % num_secs) << 9);
|
||||
|
||||
if (random() % 10 < 4) {
|
||||
segs = 1;
|
||||
nbytes = (((random() % 7) + 1) << 9);
|
||||
} else {
|
||||
segs = (random() % 10) + 1;
|
||||
nbytes = 4096;
|
||||
}
|
||||
|
||||
if (i + segs > num_iocbs)
|
||||
segs = (num_iocbs - i);
|
||||
|
||||
sparse_mem = (random() % 10 < 2 ? 1 : 0);
|
||||
|
||||
if (sparse_mem)
|
||||
buf = xalloc(nbytes);
|
||||
else
|
||||
buf = xalloc(segs * nbytes);
|
||||
|
||||
for (j = 0; j < segs; j++) {
|
||||
struct iocb *io = iocbs[i + j];
|
||||
io->aio_lio_opcode = type;
|
||||
io->u.c.nbytes = nbytes;
|
||||
io->u.c.offset = offset;
|
||||
io->u.c.buf = buf;
|
||||
offset += nbytes;
|
||||
|
||||
io->data = make_data(i + j, (j == 0), sparse_mem);
|
||||
|
||||
if (j + 1 < segs && sparse_mem)
|
||||
buf = xalloc(nbytes);
|
||||
else
|
||||
buf += nbytes;
|
||||
}
|
||||
|
||||
i += segs;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
simulate_io(struct iocb **iocbs, struct io_event *events, int num_iocbs)
|
||||
{
|
||||
int i, done;
|
||||
struct iocb *io;
|
||||
struct io_event *ep;
|
||||
|
||||
if (num_iocbs > 1)
|
||||
done = (random() % (num_iocbs - 1)) + 1;
|
||||
else
|
||||
done = num_iocbs;
|
||||
|
||||
for (i = 0; i < done; i++) {
|
||||
io = iocbs[i];
|
||||
ep = &events[i];
|
||||
ep->obj = io;
|
||||
ep->res = (random() % 10 < 8 ? io->u.c.nbytes : 0);
|
||||
}
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
static inline void
|
||||
process_events(struct opioctx *ctx,
|
||||
struct iocb *iocb_list, struct io_event *events, int num)
|
||||
{
|
||||
int i;
|
||||
struct iocb *io;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
io = events[i].obj;
|
||||
print_iocb(ctx, io);
|
||||
if (data_idx(io->data) != (io - iocb_list)) {
|
||||
printf("corrupt data! data_idx = %d, io = %d\n",
|
||||
data_idx(io->data), (io - iocb_list));
|
||||
exit(-1);
|
||||
}
|
||||
if (data_is_head(io->data) || data_is_sparse(io->data))
|
||||
xfree(io->u.c.buf);
|
||||
memset(io, 0, sizeof(struct iocb));
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
init_optest(struct iocb *iocb_list,
|
||||
struct iocb **iocbs, struct io_event *events, int num)
|
||||
{
|
||||
int i;
|
||||
|
||||
memset(iocb_list, 0, num * sizeof(struct iocb));
|
||||
memset(events, 0, num * sizeof(struct io_event));
|
||||
|
||||
for (i = 0; i < num; i++)
|
||||
iocbs[i] = &iocb_list[i];
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
uint64_t num_secs;
|
||||
struct opioctx ctx;
|
||||
struct io_event *events;
|
||||
int i, c, num_runs, num_iocbs, seed;
|
||||
struct iocb *iocb_list, **iocbs, **ioqueue;
|
||||
|
||||
num_runs = 1;
|
||||
num_iocbs = 300;
|
||||
seed = time(NULL);
|
||||
num_secs = ((4ULL << 20) >> 9); /* 4GB disk */
|
||||
|
||||
while ((c = getopt(argc, argv, "n:i:s:r:h")) != -1) {
|
||||
switch (c) {
|
||||
case 'n':
|
||||
num_runs = atoi(optarg);
|
||||
break;
|
||||
case 'i':
|
||||
num_iocbs = atoi(optarg);
|
||||
break;
|
||||
case 's':
|
||||
num_secs = strtoull(optarg, NULL, 10);
|
||||
break;
|
||||
case 'r':
|
||||
seed = atoi(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
usage();
|
||||
case '?':
|
||||
fprintf(stderr, "Unrecognized option: -%c\n", optopt);
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
printf("Running %d tests with %d iocbs on %llu sectors, seed = %d\n",
|
||||
num_runs, num_iocbs, num_secs, seed);
|
||||
|
||||
srand(seed);
|
||||
|
||||
iocb_list = malloc(num_iocbs * sizeof(struct iocb));
|
||||
iocbs = malloc(num_iocbs * sizeof(struct iocb *));
|
||||
events = malloc(num_iocbs * sizeof(struct io_event));
|
||||
|
||||
if (!iocb_list || !iocbs || !events || opio_init(&ctx, num_iocbs)) {
|
||||
fprintf(stderr, "initialization failed\n");
|
||||
exit(ENOMEM);
|
||||
}
|
||||
|
||||
for (i = 0; i < num_runs; i++) {
|
||||
int op_rem, op_done, num_split, num_events, num_done;
|
||||
|
||||
ioqueue = iocbs;
|
||||
init_optest(iocb_list, ioqueue, events, num_iocbs);
|
||||
randomize_iocbs(ioqueue, num_iocbs, num_secs);
|
||||
print_iocbs(&ctx, ioqueue, num_iocbs);
|
||||
|
||||
op_done = 0;
|
||||
num_done = 0;
|
||||
op_rem = io_merge(&ctx, ioqueue, num_iocbs);
|
||||
print_iocbs(&ctx, ioqueue, op_rem);
|
||||
print_merged_iocbs(&ctx, ioqueue, op_rem);
|
||||
|
||||
while (num_done < num_iocbs) {
|
||||
DBG(&ctx, "optimized remaining: %d\n", op_rem);
|
||||
|
||||
DBG(&ctx, "simulating\n");
|
||||
num_events = simulate_io(ioqueue + op_done, events, op_rem);
|
||||
print_events(&ctx, events, num_events);
|
||||
|
||||
DBG(&ctx, "splitting %d\n", num_events);
|
||||
num_split = io_split(&ctx, events, num_events);
|
||||
print_events(&ctx, events, num_split);
|
||||
|
||||
DBG(&ctx, "processing %d\n", num_split);
|
||||
process_events(&ctx, iocb_list, events, num_split);
|
||||
|
||||
op_rem -= num_events;
|
||||
op_done += num_events;
|
||||
num_done += num_split;
|
||||
}
|
||||
|
||||
DBG(&ctx, "run %d: processed: %d, xallocs: %d, xfrees: %d\n",
|
||||
i, num_done, xalloc_cnt, xfree_cnt);
|
||||
if (xalloc_cnt != xfree_cnt)
|
||||
exit(-1);
|
||||
xalloc_cnt = xfree_cnt = 0;
|
||||
}
|
||||
|
||||
free(iocbs);
|
||||
free(events);
|
||||
free(iocb_list);
|
||||
opio_free(&ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __IO_OPTIMIZE_H__
|
||||
#define __IO_OPTIMIZE_H__
|
||||
|
||||
#include <libaio.h>
|
||||
|
||||
struct opio;
|
||||
|
||||
struct opio_list {
|
||||
struct opio *head;
|
||||
struct opio *tail;
|
||||
};
|
||||
|
||||
struct opio {
|
||||
char *buf;
|
||||
unsigned long nbytes;
|
||||
long long offset;
|
||||
void *data;
|
||||
struct iocb *iocb;
|
||||
struct io_event event;
|
||||
struct opio *head;
|
||||
struct opio *next;
|
||||
struct opio_list list;
|
||||
};
|
||||
|
||||
struct opioctx {
|
||||
int num_opios;
|
||||
int free_opio_cnt;
|
||||
struct opio *opios;
|
||||
struct opio **free_opios;
|
||||
struct iocb **iocb_queue;
|
||||
struct io_event *event_queue;
|
||||
};
|
||||
|
||||
int opio_init(struct opioctx *ctx, int num_iocbs);
|
||||
void opio_free(struct opioctx *ctx);
|
||||
int io_merge(struct opioctx *ctx, struct iocb **queue, int num);
|
||||
int io_split(struct opioctx *ctx, struct io_event *events, int num);
|
||||
int io_expand_iocbs(struct opioctx *ctx, struct iocb **queue, int idx, int num);
|
||||
|
||||
#endif
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2010, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public License
|
||||
* as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* kernel 2.6.21 added eventfd(2) support, kernel 2.6.22 eventfds for
|
||||
* aio. libaio 0.3.107 updated the header file, but few systems have
|
||||
* it. define a custom iocb_common struct instead, and work around a
|
||||
* potentially missing sys/eventfd.h. this header should vanish over
|
||||
* time.
|
||||
*/
|
||||
|
||||
#ifndef __LIBAIO_COMPAT
|
||||
#define __LIBAIO_COMPAT
|
||||
|
||||
#include "../../config.h"
|
||||
#include <libaio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
|
||||
struct __compat_io_iocb_common {
|
||||
char __pad_buf[8];
|
||||
char __pad_nbytes[8];
|
||||
long long offset;
|
||||
long long __pad3;
|
||||
unsigned flags;
|
||||
unsigned resfd;
|
||||
};
|
||||
|
||||
static inline void __io_set_eventfd(struct iocb *iocb, int eventfd)
|
||||
{
|
||||
struct __compat_io_iocb_common *c;
|
||||
c = (struct __compat_io_iocb_common*)&iocb->u.c;
|
||||
c->flags |= (1 << 0);
|
||||
c->resfd = eventfd;
|
||||
}
|
||||
|
||||
#ifdef HAVE_SYS_EVENTFD_H
|
||||
|
||||
#include <sys/eventfd.h>
|
||||
|
||||
static inline int tapdisk_sys_eventfd(int initval)
|
||||
{
|
||||
return eventfd(initval, 0);
|
||||
}
|
||||
|
||||
#else /* Fallback */
|
||||
#ifndef SYS_eventfd
|
||||
#ifndef __NR_eventfd
|
||||
# if defined(__alpha__)
|
||||
# define __NR_eventfd 478
|
||||
# elif defined(__arm__)
|
||||
# define __NR_eventfd (__NR_SYSCALL_BASE+351)
|
||||
# elif defined(__ia64__)
|
||||
# define __NR_eventfd 1309
|
||||
# elif defined(__i386__)
|
||||
# define __NR_eventfd 323
|
||||
# elif defined(__m68k__)
|
||||
# define __NR_eventfd 319
|
||||
# elif 0 && defined(__mips__)
|
||||
# error __NR_eventfd?
|
||||
# define __NR_eventfd (__NR_Linux + 319)
|
||||
# define __NR_eventfd (__NR_Linux + 278)
|
||||
# define __NR_eventfd (__NR_Linux + 282)
|
||||
# elif defined(__hppa__)
|
||||
# define __NR_eventfd (__NR_Linux + 304)
|
||||
# elif defined(__PPC__) || defined(__powerpc64__)
|
||||
# define __NR_eventfd 307
|
||||
# elif defined(__s390__) || defined(__s390x__)
|
||||
# define __NR_eventfd 318
|
||||
# elif defined(__sparc__)
|
||||
# define __NR_eventfd 313
|
||||
# elif defined(__x86_64__)
|
||||
# define __NR_eventfd 284
|
||||
# endif
|
||||
#else
|
||||
# error __NR_eventfd?
|
||||
#endif
|
||||
#define SYS_eventfd __NR_eventfd
|
||||
#endif
|
||||
|
||||
static inline int tapdisk_sys_eventfd(int initval)
|
||||
{
|
||||
return syscall(SYS_eventfd, initval, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __LIBAIO_COMPAT */
|
||||
@@ -1,1000 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2007, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This module implements a "dot locking" style advisory file locking algorithm.
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <dirent.h>
|
||||
#include <limits.h>
|
||||
#include "lock.h"
|
||||
|
||||
#define unlikely(x) __builtin_expect(!!(x), 0)
|
||||
|
||||
/* format: xenlk.hostname.uuid.<xf><rw>*/
|
||||
#define LF_POSTFIX ".xenlk"
|
||||
#define LFXL_FORMAT LF_POSTFIX ".%s.%s.x%s"
|
||||
#define LFFL_FORMAT LF_POSTFIX ".%s.%s.f%s"
|
||||
#define RETRY_MAX 16
|
||||
|
||||
#if defined(LOGS)
|
||||
#define LOG(format, args...) printf("%d: ", __LINE__); printf(format, ## args)
|
||||
#else
|
||||
#define LOG(format, args...)
|
||||
#endif
|
||||
|
||||
/* random wait - up to .5 seconds */
|
||||
#define XSLEEP usleep(random() & 0x7ffff)
|
||||
|
||||
typedef int (*eval_func)(char *name, int readonly);
|
||||
|
||||
static char *create_lockfn(char *fn_to_lock)
|
||||
{
|
||||
char *lockfn;
|
||||
|
||||
/* allocate string to hold constructed lock file */
|
||||
lockfn = malloc(strlen(fn_to_lock) + strlen(LF_POSTFIX) + 1);
|
||||
if (unlikely(!lockfn)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* append postfix to file to lock */
|
||||
strcpy(lockfn, fn_to_lock);
|
||||
strcat(lockfn, LF_POSTFIX);
|
||||
|
||||
return lockfn;
|
||||
}
|
||||
|
||||
static char *create_lockfn_link(char *fn_to_lock, char *format,
|
||||
char *uuid, int readonly)
|
||||
{
|
||||
char hostname[128];
|
||||
char *lockfn_link;
|
||||
char *ptr;
|
||||
|
||||
/* get hostname */
|
||||
if (unlikely(gethostname(hostname, sizeof(hostname)) == -1)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* allocate string to hold constructed lock file link */
|
||||
lockfn_link = malloc(strlen(fn_to_lock) + strlen(LF_POSTFIX) +
|
||||
strlen(hostname) + strlen(uuid) + 8);
|
||||
if (unlikely(!lockfn_link)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* construct lock file link with specific format */
|
||||
strcpy(lockfn_link, fn_to_lock);
|
||||
ptr = lockfn_link + strlen(lockfn_link);
|
||||
sprintf(ptr, format, hostname, uuid, readonly ? "r" : "w");
|
||||
|
||||
return lockfn_link;
|
||||
}
|
||||
|
||||
static int NFSnormalizedStatTime(char *fn, struct stat *statnow, int *reterrno)
|
||||
{
|
||||
int result = LOCK_OK;
|
||||
int uniq;
|
||||
char *buf;
|
||||
int fd;
|
||||
int pid = (int)getpid();
|
||||
int clstat;
|
||||
|
||||
*reterrno = 0;
|
||||
|
||||
/* create file to normalize time */
|
||||
srandom((int)time(0) ^ pid);
|
||||
uniq = random() % 0xffffff;
|
||||
buf = malloc(strlen(fn) + 24);
|
||||
if (unlikely(!buf)) { result = LOCK_ENOMEM; goto finish; }
|
||||
|
||||
strcpy(buf, fn);
|
||||
sprintf(buf + strlen(buf), ".xen%08d.tmp", uniq);
|
||||
|
||||
fd = open(buf, O_WRONLY | O_CREAT, 0644);
|
||||
if (fd == -1) { *reterrno = errno; result = LOCK_EOPEN; goto finish; }
|
||||
clstat = close(fd);
|
||||
if (unlikely(clstat == -1)) {
|
||||
LOG("fail on close\n");
|
||||
}
|
||||
if (lstat(buf, statnow) == -1) {
|
||||
unlink(buf);
|
||||
*reterrno = errno;
|
||||
result = LOCK_ESTAT;
|
||||
goto finish;
|
||||
}
|
||||
unlink(buf);
|
||||
|
||||
finish:
|
||||
return result;
|
||||
}
|
||||
|
||||
static int writer_eval(char *name, int readonly)
|
||||
{
|
||||
return name[strlen(name)-1] == 'w';
|
||||
}
|
||||
|
||||
static int reader_eval(char *name, int readonly)
|
||||
{
|
||||
return name[strlen(name)-1] == 'r' && !readonly;
|
||||
}
|
||||
|
||||
static int lock_holder(char *fn, char *lockfn, char *lockfn_link,
|
||||
int force, int readonly, int *stole, eval_func eval,
|
||||
int *elt, int *ioerror)
|
||||
{
|
||||
int status = 0;
|
||||
int ustat;
|
||||
DIR *pd = 0;
|
||||
struct dirent *dptr;
|
||||
char *ptr;
|
||||
char *dirname = malloc(strlen(lockfn));
|
||||
char *uname = malloc(strlen(lockfn_link) + 8);
|
||||
int elt_established = 0;
|
||||
int fd;
|
||||
char tmpbuf[4096];
|
||||
|
||||
*stole = 0;
|
||||
*ioerror = 0;
|
||||
*elt = 0;
|
||||
|
||||
if (!dirname) goto finish;
|
||||
if (!uname) goto finish;
|
||||
|
||||
/* get directory */
|
||||
ptr = strrchr(lockfn, '/');
|
||||
if (!ptr) {
|
||||
strcpy(dirname, ".");
|
||||
} else {
|
||||
int numbytes = ptr - lockfn;
|
||||
strncpy(dirname, lockfn, numbytes);
|
||||
dirname[numbytes] = '\0';
|
||||
}
|
||||
pd = opendir(dirname);
|
||||
if (!pd) {
|
||||
*ioerror = errno ? errno : EIO;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/*
|
||||
* scan through directory entries and use eval function
|
||||
* if we have a match (i.e. reader or writer lock) but
|
||||
* note that if we are forcing, we will remove any and
|
||||
* all locks that appear for target of our lock, regardless
|
||||
* if it a reader/writer owns the lock.
|
||||
*/
|
||||
errno = 0;
|
||||
dptr = readdir(pd);
|
||||
if (!dptr) {
|
||||
*ioerror = EIO;
|
||||
}
|
||||
while (dptr) {
|
||||
char *p1 = strrchr(fn, '/');
|
||||
char *p2 = strrchr(lockfn, '/');
|
||||
char *p3 = strrchr(lockfn_link, '/');
|
||||
if (p1) p1+=1;
|
||||
if (p2) p2+=1;
|
||||
if (p3) p3+=1;
|
||||
if (strcmp(dptr->d_name, p1 ? p1 : fn) &&
|
||||
strcmp(dptr->d_name, p2 ? p2 : lockfn) &&
|
||||
strcmp(dptr->d_name, p3 ? p3 : lockfn_link) &&
|
||||
!strncmp(dptr->d_name, p1 ? p1 : fn, strlen(p1?p1:fn))) {
|
||||
strcpy(uname, dirname);
|
||||
strcat(uname, "/");
|
||||
strcat(uname, dptr->d_name);
|
||||
if (!elt_established) {
|
||||
/* read final lock file and extract lease time */
|
||||
fd = open(uname, O_RDONLY, 0644);
|
||||
memset(tmpbuf, 0, sizeof(tmpbuf));
|
||||
if (read(fd, tmpbuf, sizeof(tmpbuf)) < 0) {
|
||||
*ioerror = errno;
|
||||
status = 1;
|
||||
close(fd);
|
||||
goto finish;
|
||||
}
|
||||
close(fd);
|
||||
ptr = strrchr(tmpbuf, '.');
|
||||
if (ptr) {
|
||||
*elt = atoi(ptr+1);
|
||||
elt_established = 1;
|
||||
}
|
||||
}
|
||||
if (force) {
|
||||
ustat = unlink(uname);
|
||||
if (ustat == -1) {
|
||||
LOG("failed to unlink %s\n", uname);
|
||||
}
|
||||
*stole = 1;
|
||||
*elt = 0;
|
||||
} else {
|
||||
if ((*eval)(dptr->d_name, readonly)) {
|
||||
closedir(pd);
|
||||
status = 1;
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
}
|
||||
dptr = readdir(pd);
|
||||
if (!dptr && errno) {
|
||||
*ioerror = EIO;
|
||||
}
|
||||
}
|
||||
|
||||
closedir(pd);
|
||||
|
||||
finish:
|
||||
free(dirname);
|
||||
free(uname);
|
||||
|
||||
/* if IO error, force a taken status */
|
||||
return (*ioerror) ? 1 : status;
|
||||
}
|
||||
|
||||
int lock(char *fn_to_lock, char *uuid, int force, int readonly, int *lease_time, int *retstatus)
|
||||
{
|
||||
char *lockfn = 0;
|
||||
char *lockfn_xlink = 0;
|
||||
char *lockfn_flink = 0;
|
||||
char *buf = 0;
|
||||
int fd;
|
||||
int status = 0;
|
||||
struct stat stat1, stat2;
|
||||
int retry_attempts = 0;
|
||||
int clstat;
|
||||
int tmpstat;
|
||||
int stealx = 0;
|
||||
int stealw = 0;
|
||||
int stealr = 0;
|
||||
int established_lease_time = 0;
|
||||
char tmpbuf[4096];
|
||||
int ioerr;
|
||||
|
||||
if (!fn_to_lock || !uuid) {
|
||||
*retstatus = LOCK_EBADPARM;
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
*retstatus = 0;
|
||||
|
||||
/* seed random with time/pid combo */
|
||||
srandom((int)time(0) ^ getpid());
|
||||
|
||||
/* build lock file strings */
|
||||
lockfn = create_lockfn(fn_to_lock);
|
||||
if (unlikely(!lockfn)) { status = ENOMEM; *retstatus = LOCK_ENOMEM; goto finish; }
|
||||
|
||||
lockfn_xlink = create_lockfn_link(fn_to_lock, LFXL_FORMAT,
|
||||
uuid, readonly);
|
||||
if (unlikely(!lockfn_xlink)) { status = ENOMEM; *retstatus = LOCK_ENOMEM; goto finish; }
|
||||
|
||||
lockfn_flink = create_lockfn_link(fn_to_lock, LFFL_FORMAT, uuid,
|
||||
readonly);
|
||||
if (unlikely(!lockfn_flink)) { status = ENOMEM; *retstatus = LOCK_ENOMEM; goto finish; }
|
||||
|
||||
try_again:
|
||||
if (retry_attempts++ > RETRY_MAX) {
|
||||
if (*retstatus == LOCK_EXLOCK_OPEN) {
|
||||
struct stat statnow, stat_exlock;
|
||||
int diff;
|
||||
|
||||
if (lstat(lockfn, &stat_exlock) == -1) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (NFSnormalizedStatTime(fn_to_lock, &statnow, &ioerr)) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
diff = (int)statnow.st_mtime - (int)stat_exlock.st_mtime;
|
||||
if (diff > DEFAULT_LEASE_TIME_SECS) {
|
||||
unlink(lockfn);
|
||||
retry_attempts = 0;
|
||||
goto try_again;
|
||||
}
|
||||
}
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* try to open exlusive lockfile */
|
||||
fd = open(lockfn, O_WRONLY | O_CREAT | O_EXCL, 0644);
|
||||
if (fd == -1) {
|
||||
LOG("Initial lockfile creation failed %s force=%d, errno=%d\n",
|
||||
lockfn, force, errno);
|
||||
if (errno == EIO) {
|
||||
*retstatus = LOCK_EXLOCK_OPEN;
|
||||
status = EIO;
|
||||
goto finish;
|
||||
}
|
||||
/* already owned? (hostname & uuid match, skip time bits) */
|
||||
errno = 0;
|
||||
fd = open(lockfn, O_RDWR, 0644);
|
||||
if (fd != -1) {
|
||||
buf = malloc(strlen(lockfn_xlink)+1);
|
||||
if (!buf) {
|
||||
clstat = close(fd);
|
||||
if (unlikely(clstat == -1)) {
|
||||
LOG("fail on close\n");
|
||||
}
|
||||
*retstatus = LOCK_ENOMEM;
|
||||
status = ENOMEM;
|
||||
goto finish;
|
||||
}
|
||||
if (read(fd, buf, strlen(lockfn_xlink)) !=
|
||||
(strlen(lockfn_xlink))) {
|
||||
clstat = close(fd);
|
||||
if (unlikely(clstat == -1)) {
|
||||
LOG("fail on close\n");
|
||||
}
|
||||
free(buf);
|
||||
goto force_lock;
|
||||
}
|
||||
if (!strncmp(buf, lockfn_xlink, strlen(lockfn_xlink)-1)) {
|
||||
LOG("lock owned by us, reasserting\n");
|
||||
/* our lock, reassert by rewriting below */
|
||||
if (lseek(fd, 0, SEEK_SET) == -1) {
|
||||
clstat = close(fd);
|
||||
if (unlikely(clstat == -1)) {
|
||||
LOG("fail on close\n");
|
||||
}
|
||||
goto force_lock;
|
||||
}
|
||||
free(buf);
|
||||
goto skip;
|
||||
}
|
||||
free(buf);
|
||||
clstat = close(fd);
|
||||
if (unlikely(clstat == -1)) {
|
||||
LOG("fail on close\n");
|
||||
}
|
||||
}
|
||||
force_lock:
|
||||
if (errno == EIO) {
|
||||
*retstatus = LOCK_EXLOCK_OPEN;
|
||||
status = EIO;
|
||||
goto finish;
|
||||
}
|
||||
if (force) {
|
||||
/* remove lock file, we are forcing lock, try again */
|
||||
status = unlink(lockfn);
|
||||
if (unlikely(status == -1)) {
|
||||
if (errno == EIO) {
|
||||
*retstatus = LOCK_EXLOCK_OPEN;
|
||||
status = EIO;
|
||||
goto finish;
|
||||
}
|
||||
LOG("force removal of %s lockfile failed, "
|
||||
"errno=%d, trying again\n", lockfn, errno);
|
||||
}
|
||||
stealx = 1;
|
||||
}
|
||||
XSLEEP;
|
||||
*retstatus = LOCK_EXLOCK_OPEN;
|
||||
goto try_again;
|
||||
}
|
||||
|
||||
LOG("lockfile created %s\n", lockfn);
|
||||
|
||||
skip:
|
||||
/*
|
||||
* write into the temporary xlock
|
||||
*/
|
||||
if (write(fd, lockfn_xlink, strlen(lockfn_xlink)) !=
|
||||
strlen(lockfn_xlink)) {
|
||||
if (errno == EIO) {
|
||||
*retstatus = LOCK_EXLOCK_WRITE;
|
||||
status = EIO;
|
||||
goto finish;
|
||||
}
|
||||
status = errno;
|
||||
clstat = close(fd);
|
||||
if (unlikely(clstat == -1)) {
|
||||
LOG("fail on close\n");
|
||||
}
|
||||
XSLEEP;
|
||||
*retstatus = LOCK_EXLOCK_WRITE;
|
||||
if (unlink(lockfn) == -1) {
|
||||
LOG("removal of %s lockfile failed, "
|
||||
"errno=%d, trying again\n", lockfn, errno);
|
||||
}
|
||||
goto try_again;
|
||||
}
|
||||
clstat = close(fd);
|
||||
if (unlikely(clstat == -1)) {
|
||||
LOG("fail on close\n");
|
||||
}
|
||||
|
||||
while (retry_attempts++ < RETRY_MAX) {
|
||||
tmpstat = link(lockfn, lockfn_xlink);
|
||||
LOG("linking %s and %s\n", lockfn, lockfn_xlink);
|
||||
if ((tmpstat == -1) && (errno != EEXIST)) {
|
||||
LOG("link status is %d, errno=%d\n", tmpstat, errno);
|
||||
}
|
||||
|
||||
if ((lstat(lockfn, &stat1) == -1) ||
|
||||
(lstat(lockfn_xlink, &stat2) == -1)) {
|
||||
/* try again, cleanup first */
|
||||
tmpstat = unlink(lockfn);
|
||||
if (unlikely(tmpstat == -1)) {
|
||||
LOG("error removing lock file %s", lockfn);
|
||||
}
|
||||
tmpstat = unlink(lockfn_xlink);
|
||||
if (unlikely(tmpstat == -1)) {
|
||||
LOG("error removing linked lock file %s",
|
||||
lockfn_xlink);
|
||||
}
|
||||
XSLEEP;
|
||||
status = LOCK_ESTAT;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* compare inodes */
|
||||
if (stat1.st_ino == stat2.st_ino) {
|
||||
/* success, inodes are the same */
|
||||
/* should we check that st_nlink's are also 2?? */
|
||||
*retstatus = LOCK_OK;
|
||||
status = 0;
|
||||
tmpstat = unlink(lockfn_xlink);
|
||||
if (unlikely(tmpstat == -1)) {
|
||||
LOG("error removing linked lock file %s",
|
||||
lockfn_xlink);
|
||||
}
|
||||
goto finish;
|
||||
} else {
|
||||
status = errno;
|
||||
/* try again, cleanup first */
|
||||
tmpstat = unlink(lockfn);
|
||||
if (unlikely(tmpstat == -1)) {
|
||||
LOG("error removing lock file %s", lockfn);
|
||||
}
|
||||
tmpstat = unlink(lockfn_xlink);
|
||||
if (unlikely(tmpstat == -1)) {
|
||||
LOG("error removing linked lock file %s",
|
||||
lockfn_xlink);
|
||||
}
|
||||
XSLEEP;
|
||||
*retstatus = LOCK_EINODE;
|
||||
goto try_again;
|
||||
}
|
||||
}
|
||||
|
||||
finish:
|
||||
if (!*retstatus) {
|
||||
|
||||
/* we have exclusive lock */
|
||||
|
||||
status = 0;
|
||||
|
||||
/* fast check, see if we own a final lock and are reasserting */
|
||||
if (!lstat(lockfn_flink, &stat1)) {
|
||||
char *ptr;
|
||||
|
||||
/* set the return value to notice this is a reassert */
|
||||
*retstatus = 1;
|
||||
|
||||
/* read existing lock file and extract
|
||||
established lease time */
|
||||
fd = open(lockfn_flink, O_RDONLY, 0644);
|
||||
memset(tmpbuf, 0, sizeof(tmpbuf));
|
||||
if (read(fd, tmpbuf, sizeof(tmpbuf)) < 0) {
|
||||
if (errno == EIO) {
|
||||
close(fd);
|
||||
*retstatus = LOCK_EINODE;
|
||||
status = EIO;
|
||||
goto skip_scan;
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
ptr = strrchr(tmpbuf, '.');
|
||||
if (ptr) {
|
||||
*lease_time = atoi(ptr+1);
|
||||
} else {
|
||||
*lease_time = 10; /* wkchack */
|
||||
}
|
||||
goto skip_scan;
|
||||
} else {
|
||||
if (errno == EIO) {
|
||||
*retstatus = LOCK_EINODE;
|
||||
status = EIO;
|
||||
goto skip_scan;
|
||||
}
|
||||
}
|
||||
|
||||
/* we allow exclusive writer, or multiple readers */
|
||||
if (lock_holder(fn_to_lock, lockfn, lockfn_flink, force,
|
||||
readonly, &stealw, writer_eval,
|
||||
&established_lease_time, &ioerr)) {
|
||||
if (ioerr) {
|
||||
*retstatus = LOCK_EREAD;
|
||||
status = ioerr;
|
||||
goto skip_scan;
|
||||
}
|
||||
*retstatus = LOCK_EHELD_WR;
|
||||
} else if (lock_holder(fn_to_lock, lockfn, lockfn_flink, force,
|
||||
readonly, &stealr, reader_eval,
|
||||
&established_lease_time, &ioerr)) {
|
||||
if (ioerr) {
|
||||
*retstatus = LOCK_EREAD;
|
||||
status = ioerr;
|
||||
goto skip_scan;
|
||||
}
|
||||
*retstatus = LOCK_EHELD_RD;
|
||||
}
|
||||
if (established_lease_time) *lease_time =
|
||||
established_lease_time;
|
||||
}
|
||||
|
||||
skip_scan:
|
||||
if (*retstatus >= 0) {
|
||||
/* update file, changes last modify time */
|
||||
fd = open(lockfn_flink, O_WRONLY | O_CREAT, 0644);
|
||||
if (fd == -1) {
|
||||
*retstatus = LOCK_EOPEN;
|
||||
status = errno;
|
||||
} else {
|
||||
char tmpbuf[32];
|
||||
int failed_write;
|
||||
memset(tmpbuf, 0, sizeof(tmpbuf));
|
||||
sprintf(tmpbuf, ".%d", *lease_time);
|
||||
failed_write = write(fd, lockfn_flink,
|
||||
strlen(lockfn_flink)) !=
|
||||
strlen(lockfn_flink);
|
||||
if (failed_write) status = errno;
|
||||
failed_write |= write(fd, tmpbuf, strlen(tmpbuf)) !=
|
||||
strlen(tmpbuf);
|
||||
if (failed_write) status = errno;
|
||||
if (failed_write) {
|
||||
clstat = close(fd);
|
||||
if (unlikely(clstat == -1)) {
|
||||
LOG("fail on close\n");
|
||||
}
|
||||
XSLEEP;
|
||||
*retstatus = LOCK_EUPDATE;
|
||||
goto try_again;
|
||||
}
|
||||
}
|
||||
clstat = close(fd);
|
||||
if (unlikely(clstat == -1)) {
|
||||
LOG("fail on close\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (!*retstatus && force && (stealx || stealw || stealr)) {
|
||||
struct timeval timeout;
|
||||
|
||||
/* enforce quiet time on steal */
|
||||
timeout.tv_sec = *lease_time;
|
||||
timeout.tv_usec = 0;
|
||||
select(0, 0, 0, 0, &timeout);
|
||||
}
|
||||
|
||||
/* remove exclusive lock, final read/write locks will hold */
|
||||
tmpstat = unlink(lockfn);
|
||||
if (unlikely(tmpstat == -1)) {
|
||||
LOG("error removing exclusive lock file %s",
|
||||
lockfn);
|
||||
}
|
||||
|
||||
free(lockfn);
|
||||
free(lockfn_xlink);
|
||||
free(lockfn_flink);
|
||||
|
||||
/* set lease time to -1 if error, so no one is apt to use it */
|
||||
if (*retstatus < 0) *lease_time = -1;
|
||||
|
||||
LOG("returning status %d, errno=%d\n", status, errno);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
int unlock(char *fn_to_unlock, char *uuid, int readonly, int *status)
|
||||
{
|
||||
char *lockfn_link = 0;
|
||||
int reterrno = 0;
|
||||
|
||||
if (!fn_to_unlock || !uuid) {
|
||||
*status = LOCK_EBADPARM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
lockfn_link = create_lockfn_link(fn_to_unlock, LFFL_FORMAT, uuid,
|
||||
readonly);
|
||||
if (unlikely(!lockfn_link)) { *status = LOCK_ENOMEM; goto finish; }
|
||||
|
||||
if (unlink(lockfn_link) == -1) {
|
||||
LOG("error removing linked lock file %s", lockfn_link);
|
||||
reterrno = errno;
|
||||
*status = LOCK_ENOLOCK;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
*status = LOCK_OK;
|
||||
|
||||
finish:
|
||||
free(lockfn_link);
|
||||
return reterrno;
|
||||
}
|
||||
|
||||
int lock_delta(char *fn, int *ret_lease, int *max_lease)
|
||||
{
|
||||
int reterrno = 0;
|
||||
DIR *pd = 0;
|
||||
struct dirent *dptr;
|
||||
char *ptr;
|
||||
int result = INT_MAX;
|
||||
struct stat statbuf, statnow;
|
||||
char *dirname = malloc(strlen(fn));
|
||||
char *uname = malloc(strlen(fn) + 8);
|
||||
int elt_established = 0;
|
||||
char *dotptr;
|
||||
char tmpbuf[4096];
|
||||
int fd;
|
||||
|
||||
if (!fn || !dirname || !uname) {
|
||||
*ret_lease = LOCK_EBADPARM;
|
||||
*max_lease = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (NFSnormalizedStatTime(fn, &statnow, &reterrno)) {
|
||||
result = LOCK_ESTAT;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* get directory */
|
||||
ptr = strrchr(fn, '/');
|
||||
if (!ptr) {
|
||||
strcpy(dirname, ".");
|
||||
ptr = fn;
|
||||
} else {
|
||||
int numbytes = ptr - fn;
|
||||
strncpy(dirname, fn, numbytes);
|
||||
ptr += 1;
|
||||
}
|
||||
pd = opendir(dirname);
|
||||
if (!pd) { reterrno = errno; goto finish; }
|
||||
|
||||
dptr = readdir(pd);
|
||||
while (dptr) {
|
||||
if (strcmp(dptr->d_name, ptr) &&
|
||||
!strncmp(dptr->d_name, ptr, strlen(ptr))) {
|
||||
char *fpath = malloc(strlen(dptr->d_name) +
|
||||
strlen(dirname) + 2);
|
||||
if (!fpath) {
|
||||
closedir(pd);
|
||||
result = LOCK_ENOMEM;
|
||||
goto finish;
|
||||
}
|
||||
strcpy(fpath, dirname);
|
||||
strcat(fpath, "/");
|
||||
strcat(fpath, dptr->d_name);
|
||||
if (lstat(fpath, &statbuf) != -1) {
|
||||
int diff = (int)statnow.st_mtime -
|
||||
(int)statbuf.st_mtime;
|
||||
/* adjust diff if someone updated the lock
|
||||
between now and when we created the "now"
|
||||
file
|
||||
*/
|
||||
diff = (diff < 0) ? 0 : diff;
|
||||
result = diff < result ? diff : result;
|
||||
} else {
|
||||
closedir(pd);
|
||||
reterrno = errno;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (!elt_established) {
|
||||
/* read final lock file and extract lease time */
|
||||
fd = open(fpath, O_RDONLY, 0644);
|
||||
memset(tmpbuf, 0, sizeof(tmpbuf));
|
||||
if (read(fd, tmpbuf, sizeof(tmpbuf)) < 0) {
|
||||
/* error on read? */
|
||||
}
|
||||
close(fd);
|
||||
dotptr = strrchr(tmpbuf, '.');
|
||||
if (dotptr) {
|
||||
*max_lease = atoi(dotptr+1);
|
||||
elt_established = 1;
|
||||
}
|
||||
}
|
||||
|
||||
free(fpath);
|
||||
}
|
||||
dptr = readdir(pd);
|
||||
}
|
||||
|
||||
closedir(pd);
|
||||
|
||||
finish:
|
||||
free(dirname);
|
||||
free(uname);
|
||||
|
||||
/* returns smallest lock time, or error */
|
||||
if (result == INT_MAX) result = LOCK_ENOLOCK;
|
||||
|
||||
/* set lease time to -1 if error, so no one is apt to use it */
|
||||
if ((result < 0) || reterrno) *max_lease = -1;
|
||||
*ret_lease = result;
|
||||
return reterrno;
|
||||
}
|
||||
|
||||
#if defined(TEST)
|
||||
/*
|
||||
* the following is for sanity testing.
|
||||
*/
|
||||
|
||||
static void usage(char *prg)
|
||||
{
|
||||
printf("usage %s\n"
|
||||
" dtr <filename>]\n"
|
||||
" p <filename> [num iterations]\n"
|
||||
" u <filename> [0|1] [<uniqid>]\n"
|
||||
" l <filename> [0|1] [0|1] [<uniqid>] [<leasetime>]\n", prg);
|
||||
printf(" p : perf test lock take and reassert\n");
|
||||
printf(" d : delta lock time\n");
|
||||
printf(" t : test the file (after random locks)\n");
|
||||
printf(" r : random lock tests (must ^C)\n");
|
||||
printf(" u : unlock, readonly? uniqID (default is PID)\n");
|
||||
printf(" l : lock, readonly? force?, uniqID (default is PID), lease time\n");
|
||||
}
|
||||
|
||||
static void test_file(char *fn)
|
||||
{
|
||||
FILE *fptr;
|
||||
int prev_count = 0;
|
||||
int count, pid, time;
|
||||
|
||||
fptr = fopen(fn, "r");
|
||||
if (!fptr) {
|
||||
LOG("ERROR on file %s open, errno=%d\n", fn, errno);
|
||||
return;
|
||||
}
|
||||
|
||||
while (!feof(fptr)) {
|
||||
fscanf(fptr, "%d %d %d\n", &count, &pid, &time);
|
||||
if (prev_count != count) {
|
||||
LOG("ERROR: prev_count=%d, count=%d, pid=%d, time=%d\n",
|
||||
prev_count, count, pid, time);
|
||||
}
|
||||
prev_count = count + 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void random_locks(char *fn)
|
||||
{
|
||||
int pid = getpid();
|
||||
int status;
|
||||
char *filebuf = malloc(256);
|
||||
int count = 0;
|
||||
int dummy;
|
||||
int clstat;
|
||||
char uuid[12];
|
||||
int readonly;
|
||||
int lease = DEFAULT_LEASE_TIME_SECS;
|
||||
int err;
|
||||
|
||||
/* this will never return, kill to exit */
|
||||
|
||||
srandom((int)time(0) ^ pid);
|
||||
|
||||
LOG("pid: %d using file %s\n", pid, fn);
|
||||
sprintf(uuid, "%08d", pid);
|
||||
|
||||
while (1) {
|
||||
XSLEEP;
|
||||
readonly = random() & 1;
|
||||
sysstatus = lock(fn, uuid, 0, readonly, &lease, status);
|
||||
if (status == LOCK_OK) {
|
||||
/* got lock, open, read, modify write close file */
|
||||
int fd = open(fn, O_RDWR, 0644);
|
||||
if (fd == -1) {
|
||||
LOG("pid: %d ERROR on file %s open, errno=%d\n",
|
||||
pid, fn, errno);
|
||||
} else {
|
||||
if (!readonly) {
|
||||
/* ugly code to read data in test format */
|
||||
/* format is "%d %d %d" 'count pid time' */
|
||||
struct stat statbuf;
|
||||
int bytes;
|
||||
status = stat(fn, &statbuf);
|
||||
if (status != -1) {
|
||||
if (statbuf.st_size > 256) {
|
||||
lseek(fd, -256, SEEK_END);
|
||||
}
|
||||
memset(filebuf, 0, 256);
|
||||
bytes = read(fd, filebuf, 256);
|
||||
if (bytes) {
|
||||
int bw = bytes-2;
|
||||
while (bw && filebuf[bw]!='\n')
|
||||
bw--;
|
||||
if (!bw) bw = -1;
|
||||
sscanf(&filebuf[bw+1],
|
||||
"%d %d %d",
|
||||
&count, &dummy, &dummy);
|
||||
count += 1;
|
||||
}
|
||||
lseek(fd, 0, SEEK_END);
|
||||
sprintf(filebuf, "%d %d %d\n",
|
||||
count, pid, (int)time(0));
|
||||
write(fd, filebuf, strlen(filebuf));
|
||||
} else {
|
||||
LOG("pid: %d ERROR on file %s stat, "
|
||||
"errno=%d\n", pid, fn, errno);
|
||||
}
|
||||
}
|
||||
clstat = close(fd);
|
||||
if (unlikely(clstat == -1)) {
|
||||
LOG("fail on close\n");
|
||||
}
|
||||
}
|
||||
XSLEEP;
|
||||
err = unlock(fn, uuid, readonly, &status);
|
||||
LOG("unlock status is %d (err=%d)\n", status, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void perf_lock(char *fn, int loops)
|
||||
{
|
||||
int sysstatus;
|
||||
char buf[9];
|
||||
int start = loops;
|
||||
int lease = DEFAULT_LEASE_TIME_SECS;
|
||||
|
||||
sprintf(buf, "%08d", getpid());
|
||||
|
||||
while (loops--) {
|
||||
sysstatus = lock(fn, buf, 0, 0, &lease, &status);
|
||||
if (status < 0) {
|
||||
printf("failed to get lock at iteration %d errno=%d\n",
|
||||
start - loops, errno);
|
||||
return;
|
||||
}
|
||||
}
|
||||
unlock(fn, buf, 0, &status);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int status;
|
||||
char *ptr;
|
||||
char uuid[12];
|
||||
int force;
|
||||
int readonly;
|
||||
int max_lease, cur_lease;
|
||||
int intstatus;
|
||||
int lease = DEFAULT_LEASE_TIME_SECS;
|
||||
|
||||
if (argc < 3) {
|
||||
usage(argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
sprintf(uuid, "%08d", getpid());
|
||||
ptr = uuid;
|
||||
|
||||
if (!strcmp(argv[1],"d")) {
|
||||
status = lock_delta(argv[2], &cur_lease, &max_lease);
|
||||
|
||||
printf("lock delta for %s is %d seconds, max lease is %d\n",
|
||||
argv[2], cur_lease, max_lease);
|
||||
} else if (!strcmp(argv[1],"t")) {
|
||||
test_file(argv[2]);
|
||||
} else if (!strcmp(argv[1],"r")) {
|
||||
random_locks(argv[2]);
|
||||
} else if (!strcmp(argv[1],"p")) {
|
||||
perf_lock(argv[2], argc < 3 ? 100000 : atoi(argv[3]));
|
||||
} else if (!strcmp(argv[1],"l")) {
|
||||
if (argc < 4) force = 0; else force = atoi(argv[3]);
|
||||
if (argc < 5) readonly = 0; else readonly = atoi(argv[4]);
|
||||
if (argc >= 6) ptr = argv[5];
|
||||
if (argc == 7) lease = atoi(argv[6]);
|
||||
status = lock(argv[2], ptr, readonly, force, &lease, &intstatus);
|
||||
printf("lock status = %d\n", status);
|
||||
} else if (!strcmp(argv[1],"u") ) {
|
||||
if (argc < 5) readonly = 0; else readonly = atoi(argv[3]);
|
||||
if (argc == 5) ptr = argv[4];
|
||||
status = unlock(argv[2], ptr, readonly, &intstatus);
|
||||
printf("unlock status = %d\n", intstatus);
|
||||
} else {
|
||||
usage(argv[0]);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
#elif defined(UTIL)
|
||||
/*
|
||||
* the following is used for non-libary, standalone
|
||||
* program utility as a shell program
|
||||
*/
|
||||
|
||||
static void usage(char *prg)
|
||||
{
|
||||
printf("usage %s\n"
|
||||
" delta <filename>\n"
|
||||
" unlock <filename> <r|w> <uniqid>\n"
|
||||
" lock <filename> <r|w> <0|1> <uniqid> <leasetime>\n", prg);
|
||||
printf(" delta : get time since lock last refreshed\n");
|
||||
printf(" returns delta time and max lease time in seconds\n");
|
||||
printf(" unlock: unlock request filename, r|w, uniqID\n");
|
||||
printf(" returns status (success is 0)\n");
|
||||
printf(" lock : lock request filename, r|w, force?, uniqID, lease time request\n");
|
||||
printf(" returns status (success is 0) and established lease time in seconds\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int status = 0;
|
||||
int dlock;
|
||||
char *ptr;
|
||||
int force;
|
||||
int readonly;
|
||||
int cur_lease, max_lease, intstatus;
|
||||
int lease = DEFAULT_LEASE_TIME_SECS;
|
||||
|
||||
if (argc < 3) {
|
||||
if (argc == 2 && !strcmp(argv[1], "-h")) {
|
||||
usage(argv[0]);
|
||||
} else {
|
||||
printf("%d\n", LOCK_EUSAGE);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp(argv[1],"delta") && (argc == 3)) {
|
||||
status = lock_delta(argv[2], &cur_lease, &max_lease);
|
||||
printf("%d %d\n", cur_lease, max_lease);
|
||||
} else if (!strcmp(argv[1],"lock") && (argc == 7)) {
|
||||
readonly = (strcmp(argv[3], "r") == 0) ? 1 : 0;
|
||||
force = atoi(argv[4]);
|
||||
ptr = argv[5];
|
||||
lease = atoi(argv[6]);
|
||||
status = lock(argv[2], ptr, force, readonly, &lease, &intstatus);
|
||||
printf("%d %d\n", intstatus, lease);
|
||||
} else if (!strcmp(argv[1],"unlock") && (argc == 5)) {
|
||||
readonly = (strcmp(argv[3], "r") == 0) ? 1 : 0;
|
||||
ptr = argv[4];
|
||||
status = unlock(argv[2], ptr, readonly, &intstatus);
|
||||
printf("%d\n", intstatus);
|
||||
} else {
|
||||
printf("%d\n", LOCK_EUSAGE);
|
||||
}
|
||||
|
||||
/* this is either 0 or a system defined errno */
|
||||
return status;
|
||||
}
|
||||
#endif
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2007, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#define DEFAULT_LEASE_TIME_SECS 30
|
||||
|
||||
int lock(char *fn_to_lock, char *uuid, int force, int readonly, int *lease_time, int *retstat);
|
||||
int unlock(char *fn_to_unlock, char *uuid, int readonly, int *retstat);
|
||||
int lock_delta(char *fn_to_check, int *cur_lease_time, int *max_lease_time);
|
||||
|
||||
typedef enum {
|
||||
LOCK_OK = 0,
|
||||
LOCK_EBADPARM = -1,
|
||||
LOCK_ENOMEM = -2,
|
||||
LOCK_ESTAT = -3,
|
||||
LOCK_EHELD_WR = -4,
|
||||
LOCK_EHELD_RD = -5,
|
||||
LOCK_EOPEN = -6,
|
||||
LOCK_EXLOCK_OPEN = -7,
|
||||
LOCK_EXLOCK_WRITE= -8,
|
||||
LOCK_EINODE = -9,
|
||||
LOCK_EUPDATE = -10,
|
||||
LOCK_EREAD = -11,
|
||||
LOCK_EREMOVE = -12,
|
||||
LOCK_ENOLOCK = -13,
|
||||
LOCK_EUSAGE = -14,
|
||||
} lock_error;
|
||||
@@ -1,123 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* log.h: API for writelog communication */
|
||||
|
||||
#ifndef __LOG_H__
|
||||
#define __LOG_H__ 1
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <xen/io/ring.h>
|
||||
/* for wmb et al */
|
||||
#include <xenctrl.h>
|
||||
|
||||
#define LOGCMD_SHMP "shmp"
|
||||
#define LOGCMD_PEEK "peek"
|
||||
#define LOGCMD_CLEAR "clrw"
|
||||
#define LOGCMD_GET "getw"
|
||||
#define LOGCMD_KICK "kick"
|
||||
|
||||
#define CTLRSPLEN_SHMP 256
|
||||
#define CTLRSPLEN_PEEK 4
|
||||
#define CTLRSPLEN_CLEAR 4
|
||||
#define CTLRSPLEN_GET 4
|
||||
#define CTLRSPLEN_KICK 0
|
||||
|
||||
/* shmregion is arbitrarily capped at 8 megs for a minimum of
|
||||
* 64 MB of data per read (if there are no contiguous regions)
|
||||
* In the off-chance that there is more dirty data, multiple
|
||||
* reads must be done */
|
||||
#define SHMSIZE (8 * 1024 * 1024)
|
||||
#define SRINGSIZE 4096
|
||||
|
||||
/* The shared memory region is split up into 3 subregions:
|
||||
* The first half is reserved for the dirty bitmap log.
|
||||
* The second half begins with 1 page for read request descriptors,
|
||||
* followed by a big area for supplying read data.
|
||||
*/
|
||||
static inline void* bmstart(void* shm)
|
||||
{
|
||||
return shm;
|
||||
}
|
||||
|
||||
static inline void* bmend(void* shm)
|
||||
{
|
||||
return shm + SHMSIZE/2;
|
||||
}
|
||||
|
||||
static inline void* sringstart(void* shm)
|
||||
{
|
||||
return bmend(shm);
|
||||
}
|
||||
|
||||
static inline void* sdatastart(void* shm)
|
||||
{
|
||||
return sringstart(shm) + SRINGSIZE;
|
||||
}
|
||||
|
||||
static inline void* sdataend(void* shm)
|
||||
{
|
||||
return shm + SHMSIZE;
|
||||
}
|
||||
|
||||
/* format for messages between log client and server */
|
||||
struct log_ctlmsg {
|
||||
char msg[4];
|
||||
char params[16];
|
||||
};
|
||||
|
||||
/* extent descriptor */
|
||||
struct disk_range {
|
||||
uint64_t sector;
|
||||
uint32_t count;
|
||||
};
|
||||
|
||||
/* dirty write logging space. This is an extent ring at the front,
|
||||
* full of disk_ranges plus a pointer into the data area */
|
||||
/* I think I'd rather have the header in front of each data section to
|
||||
* avoid having two separate spaces that can run out, but then I'd either
|
||||
* lose page alignment on the data blocks or spend an entire page on the
|
||||
* header */
|
||||
|
||||
struct log_extent {
|
||||
uint64_t sector;
|
||||
uint32_t count;
|
||||
uint32_t offset; /* offset from start of data area to start of extent */
|
||||
};
|
||||
|
||||
/* struct above should be 16 bytes, or 256 extents/page */
|
||||
|
||||
typedef struct log_extent log_request_t;
|
||||
typedef struct log_extent log_response_t;
|
||||
|
||||
DEFINE_RING_TYPES(log, log_request_t, log_response_t);
|
||||
|
||||
#define LOG_HEADER_PAGES 4
|
||||
|
||||
#endif
|
||||
@@ -1,278 +0,0 @@
|
||||
/* start - public domain MD5 implementation */
|
||||
/*
|
||||
* This code implements the MD5 message-digest algorithm.
|
||||
* The algorithm is due to Ron Rivest. This code was
|
||||
* written by Colin Plumb in 1993, no copyright is claimed.
|
||||
* This code is in the public domain; do with it what you wish.
|
||||
*
|
||||
* Equivalent code is available from RSA Data Security, Inc.
|
||||
* This code has been tested against that, and is equivalent,
|
||||
* except that you don't need to include two pages of legalese
|
||||
* with every copy.
|
||||
*
|
||||
* To compute the message digest of a chunk of bytes, declare an
|
||||
* MD5Context structure, pass it to MD5Init, call MD5Update as
|
||||
* needed on buffers full of bytes, and then call MD5Final, which
|
||||
* will fill a supplied 16-byte array with the digest.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct MD5Context {
|
||||
uint32_t buf[4];
|
||||
uint32_t bits[2];
|
||||
uint8_t in[64];
|
||||
};
|
||||
|
||||
static void MD5Init(struct MD5Context *context);
|
||||
static void MD5Update(struct MD5Context *context, unsigned char const *buf,
|
||||
unsigned len);
|
||||
static void MD5Final(unsigned char digest[16], struct MD5Context *context);
|
||||
static void MD5Transform(uint32_t buf[4], uint32_t const in[16]);
|
||||
|
||||
|
||||
typedef struct MD5Context MD5_CTX;
|
||||
|
||||
|
||||
/**
|
||||
* md5_sum - MD5 hash for a data block
|
||||
* @addr: Pointers to the data area
|
||||
* @len: Lengths of the data block
|
||||
* @mac: Buffer for the hash
|
||||
*/
|
||||
void md5_sum(const uint8_t *addr, const size_t len, uint8_t *mac)
|
||||
{
|
||||
MD5_CTX ctx;
|
||||
|
||||
MD5Init(&ctx);
|
||||
MD5Update(&ctx, addr, len);
|
||||
MD5Final(mac, &ctx);
|
||||
}
|
||||
|
||||
|
||||
#ifndef WORDS_BIGENDIAN
|
||||
#define byteReverse(buf, len) /* Nothing */
|
||||
#else
|
||||
/*
|
||||
* Note: this code is harmless on little-endian machines.
|
||||
*/
|
||||
static void byteReverse(unsigned char *buf, unsigned longs)
|
||||
{
|
||||
uint32_t t;
|
||||
do {
|
||||
t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
|
||||
((unsigned) buf[1] << 8 | buf[0]);
|
||||
*(uint32_t *) buf = t;
|
||||
buf += 4;
|
||||
} while (--longs);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
|
||||
* initialization constants.
|
||||
*/
|
||||
void MD5Init(struct MD5Context *ctx)
|
||||
{
|
||||
ctx->buf[0] = 0x67452301;
|
||||
ctx->buf[1] = 0xefcdab89;
|
||||
ctx->buf[2] = 0x98badcfe;
|
||||
ctx->buf[3] = 0x10325476;
|
||||
|
||||
ctx->bits[0] = 0;
|
||||
ctx->bits[1] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update context to reflect the concatenation of another buffer full
|
||||
* of bytes.
|
||||
*/
|
||||
void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
|
||||
{
|
||||
uint32_t t;
|
||||
|
||||
/* Update bitcount */
|
||||
|
||||
t = ctx->bits[0];
|
||||
if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
|
||||
ctx->bits[1]++; /* Carry from low to high */
|
||||
ctx->bits[1] += len >> 29;
|
||||
|
||||
t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
|
||||
|
||||
/* Handle any leading odd-sized chunks */
|
||||
|
||||
if (t) {
|
||||
unsigned char *p = (unsigned char *) ctx->in + t;
|
||||
|
||||
t = 64 - t;
|
||||
if (len < t) {
|
||||
memcpy(p, buf, len);
|
||||
return;
|
||||
}
|
||||
memcpy(p, buf, t);
|
||||
byteReverse(ctx->in, 16);
|
||||
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
|
||||
buf += t;
|
||||
len -= t;
|
||||
}
|
||||
/* Process data in 64-byte chunks */
|
||||
|
||||
while (len >= 64) {
|
||||
memcpy(ctx->in, buf, 64);
|
||||
byteReverse(ctx->in, 16);
|
||||
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
|
||||
buf += 64;
|
||||
len -= 64;
|
||||
}
|
||||
|
||||
/* Handle any remaining bytes of data. */
|
||||
|
||||
memcpy(ctx->in, buf, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Final wrapup - pad to 64-byte boundary with the bit pattern
|
||||
* 1 0* (64-bit count of bits processed, MSB-first)
|
||||
*/
|
||||
void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
|
||||
{
|
||||
unsigned count;
|
||||
unsigned char *p;
|
||||
|
||||
/* Compute number of bytes mod 64 */
|
||||
count = (ctx->bits[0] >> 3) & 0x3F;
|
||||
|
||||
/* Set the first char of padding to 0x80. This is safe since there is
|
||||
always at least one byte free */
|
||||
p = ctx->in + count;
|
||||
*p++ = 0x80;
|
||||
|
||||
/* Bytes of padding needed to make 64 bytes */
|
||||
count = 64 - 1 - count;
|
||||
|
||||
/* Pad out to 56 mod 64 */
|
||||
if (count < 8) {
|
||||
/* Two lots of padding: Pad the first block to 64 bytes */
|
||||
memset(p, 0, count);
|
||||
byteReverse(ctx->in, 16);
|
||||
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
|
||||
|
||||
/* Now fill the next block with 56 bytes */
|
||||
memset(ctx->in, 0, 56);
|
||||
} else {
|
||||
/* Pad block to 56 bytes */
|
||||
memset(p, 0, count - 8);
|
||||
}
|
||||
byteReverse(ctx->in, 14);
|
||||
|
||||
/* Append length in bits and transform */
|
||||
((uint32_t *) ctx->in)[14] = ctx->bits[0];
|
||||
((uint32_t *) ctx->in)[15] = ctx->bits[1];
|
||||
|
||||
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
|
||||
byteReverse((unsigned char *) ctx->buf, 4);
|
||||
memcpy(digest, ctx->buf, 16);
|
||||
memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */
|
||||
}
|
||||
|
||||
/* The four core functions - F1 is optimized somewhat */
|
||||
|
||||
/* #define F1(x, y, z) (x & y | ~x & z) */
|
||||
#define F1(x, y, z) (z ^ (x & (y ^ z)))
|
||||
#define F2(x, y, z) F1(z, x, y)
|
||||
#define F3(x, y, z) (x ^ y ^ z)
|
||||
#define F4(x, y, z) (y ^ (x | ~z))
|
||||
|
||||
/* This is the central step in the MD5 algorithm. */
|
||||
#define MD5STEP(f, w, x, y, z, data, s) \
|
||||
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
|
||||
|
||||
/*
|
||||
* The core of the MD5 algorithm, this alters an existing MD5 hash to
|
||||
* reflect the addition of 16 longwords of new data. MD5Update blocks
|
||||
* the data and converts bytes into longwords for this routine.
|
||||
*/
|
||||
static void MD5Transform(uint32_t buf[4], uint32_t const in[16])
|
||||
{
|
||||
register uint32_t a, b, c, d;
|
||||
|
||||
a = buf[0];
|
||||
b = buf[1];
|
||||
c = buf[2];
|
||||
d = buf[3];
|
||||
|
||||
MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
|
||||
MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
|
||||
MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
|
||||
MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
|
||||
|
||||
MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
|
||||
MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
|
||||
MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
|
||||
MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
|
||||
|
||||
MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
|
||||
MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
|
||||
MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
|
||||
MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
|
||||
|
||||
MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
|
||||
MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
|
||||
MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
|
||||
MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
|
||||
|
||||
buf[0] += a;
|
||||
buf[1] += b;
|
||||
buf[2] += c;
|
||||
buf[3] += d;
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
#ifndef MD5_H
|
||||
#define MD5_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/**
|
||||
* md5_sum - MD5 hash for a data block
|
||||
* @addr: Pointers to the data area
|
||||
* @len: Lengths of the data block
|
||||
* @mac: Buffer for the hash
|
||||
*/
|
||||
void md5_sum(const uint8_t *addr, const size_t len, uint8_t *mac);
|
||||
|
||||
#endif
|
||||
@@ -1,191 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __TAP_PROFILE_H__
|
||||
#define __TAP_PROFILE_H__
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
//#define PROFILING
|
||||
//#define LOGGING
|
||||
|
||||
#define TAPPROF_IN 1
|
||||
#define TAPPROF_OUT 2
|
||||
|
||||
struct profile_times {
|
||||
char *fn_name;
|
||||
uint64_t in, out_sum, cnt;
|
||||
};
|
||||
|
||||
struct profile_info {
|
||||
FILE *log;
|
||||
int size;
|
||||
char *name;
|
||||
unsigned long long seq;
|
||||
struct profile_times *pt;
|
||||
};
|
||||
|
||||
#ifdef PROFILING
|
||||
|
||||
static inline void
|
||||
tp_open(struct profile_info *prof, char *tap_name, char *log_name, int size)
|
||||
{
|
||||
memset(prof, 0, sizeof(struct profile_info));
|
||||
#ifdef LOGGING
|
||||
prof->log = fopen(log_name, "w");
|
||||
#endif
|
||||
prof->size = size;
|
||||
prof->name = strdup(tap_name);
|
||||
prof->pt = malloc(sizeof(struct profile_times) * prof->size);
|
||||
if (prof->pt)
|
||||
memset(prof->pt, 0, sizeof(struct profile_times) * prof->size);
|
||||
}
|
||||
|
||||
static inline void
|
||||
tp_close(struct profile_info *prof)
|
||||
{
|
||||
int i;
|
||||
struct profile_times *pt;
|
||||
|
||||
for (i = 0; i < prof->size; i++) {
|
||||
pt = &prof->pt[i];
|
||||
if (pt->fn_name) {
|
||||
syslog(LOG_DEBUG, "%s: %s: cnt: %llu, avg time: %llu\n",
|
||||
prof->name, pt->fn_name, pt->cnt,
|
||||
((pt->cnt) ? (pt->out_sum / pt->cnt) : 0));
|
||||
free(pt->fn_name);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef LOGGING
|
||||
if (prof->log)
|
||||
fclose(prof->log);
|
||||
#endif
|
||||
free(prof->name);
|
||||
if (prof->pt)
|
||||
free(prof->pt);
|
||||
}
|
||||
|
||||
static inline u64
|
||||
tp_get_id(struct profile_info *prof)
|
||||
{
|
||||
return prof->seq++;
|
||||
}
|
||||
|
||||
static inline int
|
||||
tp_fn_id(struct profile_info *prof, const char *name)
|
||||
{
|
||||
int i;
|
||||
struct profile_times *pt;
|
||||
|
||||
for (i = 0; i < prof->size; i++) {
|
||||
pt = &prof->pt[i];
|
||||
if (!pt->fn_name)
|
||||
return i;
|
||||
if (!strcmp(pt->fn_name, name))
|
||||
return i;
|
||||
}
|
||||
|
||||
return prof->size - 1;
|
||||
}
|
||||
|
||||
static inline void
|
||||
__tp_in(struct profile_info *prof, const char *func)
|
||||
{
|
||||
long long _time;
|
||||
int idx = tp_fn_id(prof, func);
|
||||
struct profile_times *pt = &prof->pt[idx];
|
||||
|
||||
if (!pt->fn_name)
|
||||
pt->fn_name = strdup(func);
|
||||
|
||||
asm volatile(".byte 0x0f, 0x31" : "=A" (_time));
|
||||
pt->in = _time;
|
||||
}
|
||||
|
||||
#define tp_in(prof) __tp_in(prof, __func__)
|
||||
|
||||
static inline void
|
||||
__tp_out(struct profile_info *prof, const char *func)
|
||||
{
|
||||
long long _time;
|
||||
int idx = tp_fn_id(prof, func);
|
||||
struct profile_times *pt = &prof->pt[idx];
|
||||
|
||||
if (!pt->fn_name || !pt->in)
|
||||
return;
|
||||
|
||||
asm volatile(".byte 0x0f, 0x31" : "=A" (_time));
|
||||
pt->cnt++;
|
||||
pt->out_sum += (_time - pt->in);
|
||||
pt->in = 0;
|
||||
}
|
||||
|
||||
#define tp_out(prof) __tp_out(prof, __func__)
|
||||
|
||||
static inline void
|
||||
__tp_log(struct profile_info *prof, u64 id, const char *func, int direction)
|
||||
{
|
||||
long long _time;
|
||||
asm volatile(".byte 0x0f, 0x31" : "=A" (_time));
|
||||
|
||||
if (direction == TAPPROF_IN)
|
||||
__tp_in(prof, func);
|
||||
else
|
||||
__tp_out(prof, func);
|
||||
|
||||
#ifdef LOGGING
|
||||
if (prof->log)
|
||||
fprintf(prof->log, "%s: %s: %llu, %lld\n", func,
|
||||
((direction == TAPPROF_IN) ? "in" : "out"), id, _time);
|
||||
#endif
|
||||
}
|
||||
|
||||
#define tp_log(prof, id, direction) __tp_log(prof, id, __func__, direction)
|
||||
|
||||
#else
|
||||
#define tp_open(prof, tname, lname, size) ((void)0)
|
||||
#define tp_close(prof) ((void)0)
|
||||
#define tp_in(prof) ((void)0)
|
||||
#define tp_out(prof) ((void)0)
|
||||
#define tp_log(prof, sec, direction) ((void)0)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,121 +0,0 @@
|
||||
/* qcow-create.c
|
||||
*
|
||||
* Generates a qcow format disk.
|
||||
*
|
||||
* (c) 2006 Andrew Warfield and Julian Chesterfield
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation; or, when distributed
|
||||
* separately from the Linux kernel or incorporated into other
|
||||
* software packages, subject to the following license:
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this source file (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy, modify,
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <string.h>
|
||||
#include "tapdisk.h"
|
||||
#include "qcow.h"
|
||||
|
||||
#if 1
|
||||
#define DFPRINTF(_f, _a...) fprintf ( stderr, _f , ## _a )
|
||||
#else
|
||||
#define DFPRINTF(_f, _a...) ((void)0)
|
||||
#endif
|
||||
|
||||
#define MAX_NAME_LEN 1000
|
||||
|
||||
void help(void)
|
||||
{
|
||||
fprintf(stderr, "Qcow-utils: v1.0.0\n");
|
||||
fprintf(stderr,
|
||||
"usage: qcow-create [-h help] [-r reserve] <SIZE(MB)> <FILENAME> "
|
||||
"[<BACKING_FILENAME>]\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int ret = -1, c, backed = 0;
|
||||
int sparse = 1;
|
||||
uint64_t size;
|
||||
char filename[MAX_NAME_LEN], bfilename[MAX_NAME_LEN];
|
||||
|
||||
for(;;) {
|
||||
c = getopt(argc, argv, "hr");
|
||||
if (c == -1)
|
||||
break;
|
||||
switch(c) {
|
||||
case 'h':
|
||||
help();
|
||||
exit(0);
|
||||
break;
|
||||
case 'r':
|
||||
sparse = 0;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unknown option\n");
|
||||
help();
|
||||
}
|
||||
}
|
||||
|
||||
printf("Optind %d, argc %d\n", optind, argc);
|
||||
if ( !(optind == (argc - 2) || optind == (argc - 3)) )
|
||||
help();
|
||||
|
||||
size = atoi(argv[optind++]);
|
||||
size = size << 20;
|
||||
|
||||
if (snprintf(filename, MAX_NAME_LEN, "%s",argv[optind++]) >=
|
||||
MAX_NAME_LEN) {
|
||||
fprintf(stderr,"Device name too long\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (optind != argc) {
|
||||
/*Backing file argument*/
|
||||
backed = 1;
|
||||
if (snprintf(bfilename, MAX_NAME_LEN, "%s",argv[optind++]) >=
|
||||
MAX_NAME_LEN) {
|
||||
fprintf(stderr,"Device name too long\n");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
DFPRINTF("Creating file size %"PRIu64", name %s\n",(uint64_t)size, filename);
|
||||
if (!backed)
|
||||
ret = qcow_create(filename,size,NULL,sparse);
|
||||
else
|
||||
ret = qcow_create(filename,size,bfilename,sparse);
|
||||
|
||||
if (ret < 0)
|
||||
DPRINTF("Unable to create QCOW file\n");
|
||||
else
|
||||
DPRINTF("QCOW file successfully created\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _QCOW_H_
|
||||
#define _QCOW_H_
|
||||
|
||||
#include "aes.h"
|
||||
/**************************************************************/
|
||||
/* QEMU COW block driver with compression and encryption support */
|
||||
|
||||
#define QCOW_MAGIC (('Q' << 24) | ('F' << 16) | ('I' << 8) | 0xfb)
|
||||
#define XEN_MAGIC (('X' << 24) | ('E' << 16) | ('N' << 8) | 0xfb)
|
||||
#define QCOW_VERSION 1
|
||||
|
||||
#define QCOW_CRYPT_NONE 0x00
|
||||
#define QCOW_CRYPT_AES 0x01
|
||||
|
||||
#define QCOW_OFLAG_COMPRESSED (1LL << 63)
|
||||
#define SPARSE_FILE 0x01
|
||||
#define EXTHDR_L1_BIG_ENDIAN 0x02
|
||||
|
||||
#ifndef O_BINARY
|
||||
#define O_BINARY 0
|
||||
#endif
|
||||
|
||||
#define ROUNDUP(l, s) \
|
||||
({ \
|
||||
(uint64_t)( \
|
||||
(l + (s - 1)) - ((l + (s - 1)) % s)); \
|
||||
})
|
||||
|
||||
typedef struct QCowHeader {
|
||||
uint32_t magic;
|
||||
uint32_t version;
|
||||
uint64_t backing_file_offset;
|
||||
uint32_t backing_file_size;
|
||||
uint32_t mtime;
|
||||
uint64_t size; /* in bytes */
|
||||
uint8_t cluster_bits;
|
||||
uint8_t l2_bits;
|
||||
uint32_t crypt_method;
|
||||
uint64_t l1_table_offset;
|
||||
} QCowHeader;
|
||||
|
||||
/*Extended header for Xen enhancements*/
|
||||
typedef struct QCowHeader_ext {
|
||||
uint32_t xmagic;
|
||||
uint32_t cksum;
|
||||
uint32_t min_cluster_alloc;
|
||||
uint32_t flags;
|
||||
} QCowHeader_ext;
|
||||
|
||||
uint32_t gen_cksum(char *ptr, int len);
|
||||
int get_filesize(char *filename, uint64_t *size, struct stat *st);
|
||||
int qtruncate(int fd, off_t length, int sparse);
|
||||
|
||||
#define L2_CACHE_SIZE 16 /*Fixed allocation in Qemu*/
|
||||
|
||||
struct tdqcow_state {
|
||||
int fd; /*Main Qcow file descriptor */
|
||||
uint64_t fd_end; /*Store a local record of file length */
|
||||
char *name; /*Record of the filename*/
|
||||
uint32_t backing_file_size;
|
||||
uint64_t backing_file_offset;
|
||||
uint8_t extended; /*File contains extended header*/
|
||||
int encrypted; /*File contents are encrypted or plain*/
|
||||
int cluster_bits; /*Determines length of cluster as
|
||||
*indicated by file hdr*/
|
||||
int cluster_size; /*Length of cluster*/
|
||||
int cluster_sectors; /*Number of sectors per cluster*/
|
||||
int cluster_alloc; /*Blktap fix for allocating full
|
||||
*extents*/
|
||||
int min_cluster_alloc; /*Blktap historical extent alloc*/
|
||||
int sparse; /*Indicates whether to preserve sparseness*/
|
||||
int l2_bits; /*Size of L2 table entry*/
|
||||
int l2_size; /*Full table size*/
|
||||
int l1_size; /*L1 table size*/
|
||||
uint64_t cluster_offset_mask;
|
||||
uint64_t l1_table_offset; /*L1 table offset from beginning of
|
||||
*file*/
|
||||
uint64_t *l1_table; /*L1 table entries*/
|
||||
uint64_t *l2_cache; /*We maintain a cache of size
|
||||
*L2_CACHE_SIZE of most read entries*/
|
||||
uint64_t l2_cache_offsets[L2_CACHE_SIZE]; /*L2 cache entries*/
|
||||
uint32_t l2_cache_counts[L2_CACHE_SIZE]; /*Cache access record*/
|
||||
uint8_t *cluster_cache;
|
||||
uint8_t *cluster_data;
|
||||
uint64_t cluster_cache_offset; /**/
|
||||
uint32_t crypt_method; /*current crypt method, 0 if no
|
||||
*key yet */
|
||||
uint32_t crypt_method_header; /**/
|
||||
AES_KEY aes_encrypt_key; /*AES key*/
|
||||
AES_KEY aes_decrypt_key; /*AES key*/
|
||||
|
||||
/* libaio state */
|
||||
int aio_free_count;
|
||||
int max_aio_reqs;
|
||||
struct qcow_request *aio_requests;
|
||||
struct qcow_request **aio_free_list;
|
||||
|
||||
};
|
||||
|
||||
int qcow_create(const char *filename, uint64_t total_size,
|
||||
const char *backing_file, int sparse);
|
||||
|
||||
#endif //_QCOW_H_
|
||||
@@ -1,443 +0,0 @@
|
||||
/* qcow2raw.c
|
||||
*
|
||||
* Generates raw image data from an existing qcow image
|
||||
*
|
||||
* (c) 2006 Julian Chesterfield and Andrew Warfield
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation; or, when distributed
|
||||
* separately from the Linux kernel or incorporated into other
|
||||
* software packages, subject to the following license:
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this source file (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy, modify,
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bswap.h"
|
||||
#include "aes.h"
|
||||
#include "blk.h"
|
||||
#include "tapdisk.h"
|
||||
#include "tapdisk-server.h"
|
||||
#include "tapdisk-driver.h"
|
||||
#include "tapdisk-interface.h"
|
||||
#include "tapdisk-disktype.h"
|
||||
#include "qcow.h"
|
||||
|
||||
#if 1
|
||||
#define DFPRINTF(_f, _a...) fprintf ( stderr, _f , ## _a )
|
||||
#else
|
||||
#define DFPRINTF(_f, _a...) ((void)0)
|
||||
#endif
|
||||
|
||||
|
||||
/* *BSD has no O_LARGEFILE */
|
||||
#ifndef O_LARGEFILE
|
||||
#define O_LARGEFILE 0
|
||||
#endif
|
||||
|
||||
#define BLOCK_PROCESSSZ 4096
|
||||
#define QCOW_VBD 0
|
||||
#define AIO_VBD 1
|
||||
#define WINDOW 32
|
||||
#define PROGRESS_QUANT 2
|
||||
|
||||
static int running = 1, complete = 0;
|
||||
static int returned_read_events = 0, returned_write_events = 0;
|
||||
static int submit_events = 0;
|
||||
static uint32_t read_idx = 0;
|
||||
td_driver_t *ddqcow, *ddaio;
|
||||
td_vbd_t* qcow_vbd, *aio_vbd;
|
||||
static uint64_t prev = 0, written = 0;
|
||||
static char output[(100/PROGRESS_QUANT) + 5];
|
||||
|
||||
extern tapdisk_server_t server;
|
||||
|
||||
struct request_info {
|
||||
void* buf;
|
||||
uint64_t logical_sec;
|
||||
int pending;
|
||||
};
|
||||
|
||||
static void print_bytes(void *ptr, int length)
|
||||
{
|
||||
int i,k;
|
||||
unsigned char *p = ptr;
|
||||
|
||||
DFPRINTF("Buf dump, length %d:\n",length);
|
||||
for (k = 0; k < length; k++) {
|
||||
DFPRINTF("%x",*p);
|
||||
*p++;
|
||||
if (k % 16 == 0) DFPRINTF("\n");
|
||||
else if (k % 2 == 0) DFPRINTF(" ");
|
||||
}
|
||||
DFPRINTF("\n");
|
||||
return;
|
||||
}
|
||||
|
||||
static void debug_output(uint64_t progress, uint64_t size)
|
||||
{
|
||||
//Output progress every PROGRESS_QUANT
|
||||
uint64_t blocks = size/(100/PROGRESS_QUANT);
|
||||
|
||||
if (progress/blocks > prev) {
|
||||
memcpy(output+prev+1,"=>",2);
|
||||
prev++;
|
||||
DFPRINTF("\r%s %"PRIu64"%%",
|
||||
output, (uint64_t)((prev-1)*PROGRESS_QUANT));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static void send_write_responses(td_request_t treq, int err)
|
||||
{
|
||||
struct request_info* req;
|
||||
|
||||
if (err < 0) {
|
||||
DFPRINTF("AIO FAILURE: res [%d]!\n",err);
|
||||
return;
|
||||
}
|
||||
returned_write_events+=treq.secs;
|
||||
written += treq.secs;
|
||||
|
||||
req= (struct request_info*)treq.cb_data;
|
||||
|
||||
//Wait for whole request to complete.
|
||||
req->pending-=treq.secs;
|
||||
if(req->pending)
|
||||
return;
|
||||
|
||||
//Whole request has completed, we can free buffers.
|
||||
free(req->buf);
|
||||
free(req);
|
||||
|
||||
debug_output(written, ddaio->info.size);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void send_read_responses(td_request_t treq, int err)
|
||||
{
|
||||
int ret;
|
||||
struct request_info* req;
|
||||
td_vbd_request_t* vreq;
|
||||
|
||||
if (err < 0) {
|
||||
DFPRINTF("AIO FAILURE: res [%d]!\n",err);
|
||||
return;
|
||||
}
|
||||
returned_read_events+=treq.secs;
|
||||
|
||||
req= (struct request_info*)treq.cb_data;
|
||||
|
||||
//do nothing until all fragments complete.
|
||||
req->pending-=treq.secs;
|
||||
|
||||
if(req->pending)
|
||||
return;
|
||||
|
||||
//This read is done.
|
||||
tapdisk_vbd_complete_vbd_request(qcow_vbd, treq.private);
|
||||
|
||||
|
||||
treq.op = TD_OP_WRITE;
|
||||
treq.buf = req->buf;
|
||||
treq.sec = req->logical_sec;
|
||||
treq.secs = BLOCK_PROCESSSZ>>9;
|
||||
treq.image = tapdisk_vbd_first_image(aio_vbd);
|
||||
treq.cb = send_write_responses;
|
||||
treq.id = 0;
|
||||
treq.sidx = 0;
|
||||
|
||||
req->pending = BLOCK_PROCESSSZ>>9;
|
||||
treq.cb_data = req;
|
||||
|
||||
vreq = calloc(1, sizeof(td_vbd_request_t));
|
||||
treq.private = vreq;
|
||||
|
||||
//Put it in the VBD's queue, so we don't lose
|
||||
//track of it.
|
||||
vreq->submitting = 1;
|
||||
INIT_LIST_HEAD(&vreq->next);
|
||||
tapdisk_vbd_move_request(treq.private,
|
||||
&aio_vbd->pending_requests);
|
||||
|
||||
ddaio->ops->td_queue_write(ddaio,treq);
|
||||
--vreq->submitting;
|
||||
|
||||
tapdisk_submit_all_tiocbs(&server.aio_queue);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int ret = -1, fd, len,input;
|
||||
uint64_t size;
|
||||
struct timeval timeout;
|
||||
uint64_t i;
|
||||
char *buf = NULL;
|
||||
struct stat finfo;
|
||||
td_request_t treq;
|
||||
td_vbd_request_t* vreq;
|
||||
struct request_info* req;
|
||||
int err;
|
||||
|
||||
if (argc != 3) {
|
||||
fprintf(stderr, "Qcow-utils: v1.0.0\n");
|
||||
fprintf(stderr, "usage: %s <Dest File descriptor> "
|
||||
"<Qcow SRC IMAGE>\n",
|
||||
argv[0]);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
err = tapdisk_server_initialize();
|
||||
if( err ) {
|
||||
DPRINTF("qcow2raw Couldn't initialize server instance.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err=tapdisk_vbd_initialize(QCOW_VBD);
|
||||
if( err ) {
|
||||
DPRINTF("qcow2raw Couldn't initialize qcow vbd.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
qcow_vbd = tapdisk_server_get_vbd(QCOW_VBD);
|
||||
if (!qcow_vbd) {
|
||||
err = -ENODEV;
|
||||
DPRINTF("qcow2raw Couldn't create qcow vbd.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = tapdisk_vbd_open_vdi(qcow_vbd, argv[2], DISK_TYPE_QCOW,
|
||||
TAPDISK_STORAGE_TYPE_DEFAULT,
|
||||
TD_OPEN_RDONLY);
|
||||
if( err ) {
|
||||
DPRINTF("qcow2raw Couldn't open qcow file.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
ddqcow=(tapdisk_vbd_first_image(qcow_vbd))->driver;
|
||||
|
||||
/*Setup aio destination file*/
|
||||
ret = stat(argv[1],&finfo);
|
||||
if (ret == -1) {
|
||||
/*Check errno*/
|
||||
switch(errno) {
|
||||
case ENOENT:
|
||||
/*File doesn't exist, create*/
|
||||
fd = open(argv[1],
|
||||
O_RDWR | O_LARGEFILE | O_CREAT, 0644);
|
||||
if (fd < 0) {
|
||||
DFPRINTF("ERROR creating file [%s] "
|
||||
"(errno %d)\n",
|
||||
argv[1], 0 - errno);
|
||||
exit(-1);
|
||||
}
|
||||
if (ftruncate(fd, (off_t)ddqcow->info.size<<9) < 0) {
|
||||
DFPRINTF("Unable to create file "
|
||||
"[%s] of size %"PRIu64" (errno %d). "
|
||||
"Exiting...\n",
|
||||
argv[1],
|
||||
(uint64_t)ddqcow->info.size<<9,
|
||||
0 - errno);
|
||||
close(fd);
|
||||
exit(-1);
|
||||
}
|
||||
close(fd);
|
||||
break;
|
||||
case ENXIO:
|
||||
DFPRINTF("ERROR Device [%s] does not exist\n",argv[1]);
|
||||
exit(-1);
|
||||
default:
|
||||
DFPRINTF("An error occurred opening Device [%s] "
|
||||
"(errno %d)\n",
|
||||
argv[1], 0 - errno);
|
||||
exit(-1);
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "WARNING: All existing data in "
|
||||
"%s will be overwritten.\nDo you wish to continue? "
|
||||
"(y or n) ",
|
||||
argv[1]);
|
||||
if (getchar() != 'y') {
|
||||
DFPRINTF("Exiting...\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
/*TODO - Test the existing file or device for adequate space*/
|
||||
fd = open(argv[1], O_RDWR | O_LARGEFILE);
|
||||
if (fd < 0) {
|
||||
DFPRINTF("ERROR: opening file [%s] (errno %d)\n",
|
||||
argv[1], 0 - errno);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (S_ISBLK(finfo.st_mode)) {
|
||||
if (blk_getimagesize(fd, &size) != 0) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (size < ddqcow->info.size<<9) {
|
||||
DFPRINTF("ERROR: Not enough space on device "
|
||||
"%s (%"PRIu64" bytes available, "
|
||||
"%"PRIu64" bytes required\n",
|
||||
argv[1], size,
|
||||
(uint64_t)ddqcow->info.size<<9);
|
||||
close(fd);
|
||||
exit(-1);
|
||||
}
|
||||
} else {
|
||||
if (ftruncate(fd, (off_t)ddqcow->info.size<<9) < 0) {
|
||||
DFPRINTF("Unable to create file "
|
||||
"[%s] of size %"PRIu64" (errno %d). "
|
||||
"Exiting...\n",
|
||||
argv[1],
|
||||
(uint64_t)ddqcow->info.size<<9,
|
||||
0 - errno);
|
||||
close(fd);
|
||||
exit(-1);
|
||||
} else DFPRINTF("File [%s] truncated to length %"PRIu64" "
|
||||
"(%"PRIu64")\n",
|
||||
argv[1],
|
||||
(uint64_t)ddqcow->info.size<<9,
|
||||
(uint64_t)ddqcow->info.size);
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
//Now the output file should be there, reopen it as an aio VBD
|
||||
err=tapdisk_vbd_initialize(AIO_VBD);
|
||||
if( err ) {
|
||||
DPRINTF("qcow2raw Couldn't initialize aio vbd.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
aio_vbd = tapdisk_server_get_vbd(AIO_VBD);
|
||||
if (!aio_vbd) {
|
||||
err = -ENODEV;
|
||||
DPRINTF("qcow2raw Couldn't create aio vbd.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = tapdisk_vbd_open_vdi(aio_vbd, argv[1], DISK_TYPE_AIO,
|
||||
TAPDISK_STORAGE_TYPE_DEFAULT,
|
||||
0);
|
||||
if( err ) {
|
||||
DPRINTF("qcow2raw Couldn't open aio file.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
ddaio=(tapdisk_vbd_first_image(aio_vbd))->driver;
|
||||
|
||||
/*Initialise the output string*/
|
||||
memset(output,0x20,(100/PROGRESS_QUANT)+5);
|
||||
output[0] = '[';
|
||||
output[(100/PROGRESS_QUANT)+2] = ']';
|
||||
output[(100/PROGRESS_QUANT)+3] = '\0';
|
||||
DFPRINTF("%s",output);
|
||||
|
||||
i = 0;
|
||||
while (running) {
|
||||
timeout.tv_sec = 0;
|
||||
|
||||
if (!complete) {
|
||||
/*Read Pages from qcow image*/
|
||||
if ( (ret = posix_memalign((void **)&buf,
|
||||
BLOCK_PROCESSSZ,
|
||||
BLOCK_PROCESSSZ))
|
||||
!= 0) {
|
||||
DFPRINTF("Unable to alloc memory (%d)\n",ret);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
/*Attempt to read 4k sized blocks*/
|
||||
submit_events+=BLOCK_PROCESSSZ>>9;
|
||||
|
||||
//Set up the read request
|
||||
treq.op = TD_OP_READ;
|
||||
treq.buf = buf;
|
||||
treq.sec = i;
|
||||
treq.secs = BLOCK_PROCESSSZ>>9;
|
||||
treq.image = tapdisk_vbd_first_image(qcow_vbd);
|
||||
treq.cb = send_read_responses;
|
||||
treq.id = 0;
|
||||
treq.sidx = 0;
|
||||
|
||||
req = calloc(1, sizeof(struct request_info));
|
||||
req->buf = buf;
|
||||
req->logical_sec = i;
|
||||
req->pending = BLOCK_PROCESSSZ>>9;
|
||||
treq.cb_data = req;
|
||||
|
||||
vreq = calloc(1, sizeof(td_vbd_request_t));
|
||||
treq.private = vreq;
|
||||
|
||||
//Put it in the VBD's queue, so we don't lose
|
||||
//track of it.
|
||||
vreq->submitting = 1;
|
||||
INIT_LIST_HEAD(&vreq->next);
|
||||
tapdisk_vbd_move_request(treq.private,
|
||||
&qcow_vbd->pending_requests);
|
||||
|
||||
ddqcow->ops->td_queue_read(ddqcow, treq);
|
||||
--vreq->submitting;
|
||||
|
||||
i += BLOCK_PROCESSSZ>>9;
|
||||
|
||||
if (i >= ddqcow->info.size)
|
||||
complete = 1;
|
||||
|
||||
|
||||
tapdisk_submit_all_tiocbs(&server.aio_queue);
|
||||
}
|
||||
|
||||
|
||||
while(returned_write_events != submit_events) {
|
||||
ret = scheduler_wait_for_events(&server.scheduler);
|
||||
if (ret < 0) {
|
||||
DFPRINTF("server wait returned %d\n", ret);
|
||||
sleep(2);
|
||||
}
|
||||
}
|
||||
if (complete && (returned_write_events == submit_events))
|
||||
running = 0;
|
||||
}
|
||||
memcpy(output+prev+1,"=",1);
|
||||
DFPRINTF("\r%s 100%%\nTRANSFER COMPLETE\n\n", output);
|
||||
|
||||
ddqcow->ops->td_close(ddqcow);
|
||||
ddaio->ops->td_close(ddaio);
|
||||
free(ddqcow->data);
|
||||
free(ddaio->data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,265 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "scheduler.h"
|
||||
#include "tapdisk-log.h"
|
||||
|
||||
#define DBG(_f, _a...) tlog_write(TLOG_DBG, _f, ##_a)
|
||||
|
||||
#define SCHEDULER_MAX_TIMEOUT 600
|
||||
#define SCHEDULER_POLL_FD (SCHEDULER_POLL_READ_FD | \
|
||||
SCHEDULER_POLL_WRITE_FD | \
|
||||
SCHEDULER_POLL_EXCEPT_FD)
|
||||
|
||||
#define MIN(a, b) ((a) <= (b) ? (a) : (b))
|
||||
#define MAX(a, b) ((a) >= (b) ? (a) : (b))
|
||||
|
||||
#define scheduler_for_each_event(s, event, tmp) \
|
||||
list_for_each_entry_safe(event, tmp, &(s)->events, next)
|
||||
|
||||
typedef struct event {
|
||||
char mode;
|
||||
event_id_t id;
|
||||
|
||||
int fd;
|
||||
int timeout;
|
||||
int deadline;
|
||||
|
||||
event_cb_t cb;
|
||||
void *private;
|
||||
|
||||
struct list_head next;
|
||||
} event_t;
|
||||
|
||||
static void
|
||||
scheduler_prepare_events(scheduler_t *s)
|
||||
{
|
||||
int diff;
|
||||
struct timeval now;
|
||||
event_t *event, *tmp;
|
||||
|
||||
FD_ZERO(&s->read_fds);
|
||||
FD_ZERO(&s->write_fds);
|
||||
FD_ZERO(&s->except_fds);
|
||||
|
||||
s->max_fd = 0;
|
||||
s->timeout = SCHEDULER_MAX_TIMEOUT;
|
||||
|
||||
gettimeofday(&now, NULL);
|
||||
|
||||
scheduler_for_each_event(s, event, tmp) {
|
||||
if (event->mode & SCHEDULER_POLL_READ_FD) {
|
||||
FD_SET(event->fd, &s->read_fds);
|
||||
s->max_fd = MAX(event->fd, s->max_fd);
|
||||
}
|
||||
|
||||
if (event->mode & SCHEDULER_POLL_WRITE_FD) {
|
||||
FD_SET(event->fd, &s->write_fds);
|
||||
s->max_fd = MAX(event->fd, s->max_fd);
|
||||
}
|
||||
|
||||
if (event->mode & SCHEDULER_POLL_EXCEPT_FD) {
|
||||
FD_SET(event->fd, &s->except_fds);
|
||||
s->max_fd = MAX(event->fd, s->max_fd);
|
||||
}
|
||||
|
||||
if (event->mode & SCHEDULER_POLL_TIMEOUT) {
|
||||
diff = event->deadline - now.tv_sec;
|
||||
if (diff > 0)
|
||||
s->timeout = MIN(s->timeout, diff);
|
||||
else
|
||||
s->timeout = 0;
|
||||
}
|
||||
}
|
||||
|
||||
s->timeout = MIN(s->timeout, s->max_timeout);
|
||||
}
|
||||
|
||||
static void
|
||||
scheduler_event_callback(event_t *event, char mode)
|
||||
{
|
||||
if (event->mode & SCHEDULER_POLL_TIMEOUT) {
|
||||
struct timeval now;
|
||||
gettimeofday(&now, NULL);
|
||||
event->deadline = now.tv_sec + event->timeout;
|
||||
}
|
||||
|
||||
event->cb(event->id, mode, event->private);
|
||||
}
|
||||
|
||||
static void
|
||||
scheduler_run_events(scheduler_t *s)
|
||||
{
|
||||
struct timeval now;
|
||||
event_t *event, *tmp;
|
||||
|
||||
gettimeofday(&now, NULL);
|
||||
|
||||
again:
|
||||
s->restart = 0;
|
||||
|
||||
scheduler_for_each_event(s, event, tmp) {
|
||||
if ((event->mode & SCHEDULER_POLL_READ_FD) &&
|
||||
FD_ISSET(event->fd, &s->read_fds)) {
|
||||
FD_CLR(event->fd, &s->read_fds);
|
||||
scheduler_event_callback(event, SCHEDULER_POLL_READ_FD);
|
||||
goto next;
|
||||
}
|
||||
|
||||
if ((event->mode & SCHEDULER_POLL_WRITE_FD) &&
|
||||
FD_ISSET(event->fd, &s->write_fds)) {
|
||||
FD_CLR(event->fd, &s->write_fds);
|
||||
scheduler_event_callback(event, SCHEDULER_POLL_WRITE_FD);
|
||||
goto next;
|
||||
}
|
||||
|
||||
if ((event->mode & SCHEDULER_POLL_EXCEPT_FD) &&
|
||||
FD_ISSET(event->fd, &s->except_fds)) {
|
||||
FD_CLR(event->fd, &s->except_fds);
|
||||
scheduler_event_callback(event, SCHEDULER_POLL_EXCEPT_FD);
|
||||
goto next;
|
||||
}
|
||||
|
||||
if ((event->mode & SCHEDULER_POLL_TIMEOUT) &&
|
||||
(event->deadline <= now.tv_sec))
|
||||
scheduler_event_callback(event, SCHEDULER_POLL_TIMEOUT);
|
||||
|
||||
next:
|
||||
if (s->restart)
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
scheduler_register_event(scheduler_t *s, char mode, int fd,
|
||||
int timeout, event_cb_t cb, void *private)
|
||||
{
|
||||
event_t *event;
|
||||
struct timeval now;
|
||||
|
||||
if (!cb)
|
||||
return -EINVAL;
|
||||
|
||||
if (!(mode & SCHEDULER_POLL_TIMEOUT) && !(mode & SCHEDULER_POLL_FD))
|
||||
return -EINVAL;
|
||||
|
||||
event = calloc(1, sizeof(event_t));
|
||||
if (!event)
|
||||
return -ENOMEM;
|
||||
|
||||
gettimeofday(&now, NULL);
|
||||
|
||||
INIT_LIST_HEAD(&event->next);
|
||||
|
||||
event->mode = mode;
|
||||
event->fd = fd;
|
||||
event->timeout = timeout;
|
||||
event->deadline = now.tv_sec + timeout;
|
||||
event->cb = cb;
|
||||
event->private = private;
|
||||
event->id = s->uuid++;
|
||||
|
||||
if (!s->uuid)
|
||||
s->uuid++;
|
||||
|
||||
list_add_tail(&event->next, &s->events);
|
||||
|
||||
return event->id;
|
||||
}
|
||||
|
||||
void
|
||||
scheduler_unregister_event(scheduler_t *s, event_id_t id)
|
||||
{
|
||||
event_t *event, *tmp;
|
||||
|
||||
if (!id)
|
||||
return;
|
||||
|
||||
scheduler_for_each_event(s, event, tmp)
|
||||
if (event->id == id) {
|
||||
list_del(&event->next);
|
||||
free(event);
|
||||
s->restart = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
scheduler_set_max_timeout(scheduler_t *s, int timeout)
|
||||
{
|
||||
if (timeout >= 0)
|
||||
s->max_timeout = MIN(s->max_timeout, timeout);
|
||||
}
|
||||
|
||||
int
|
||||
scheduler_wait_for_events(scheduler_t *s)
|
||||
{
|
||||
int ret;
|
||||
struct timeval tv;
|
||||
|
||||
scheduler_prepare_events(s);
|
||||
|
||||
tv.tv_sec = s->timeout;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
DBG("timeout: %d, max_timeout: %d\n",
|
||||
s->timeout, s->max_timeout);
|
||||
|
||||
ret = select(s->max_fd + 1, &s->read_fds,
|
||||
&s->write_fds, &s->except_fds, &tv);
|
||||
|
||||
s->restart = 0;
|
||||
s->timeout = SCHEDULER_MAX_TIMEOUT;
|
||||
s->max_timeout = SCHEDULER_MAX_TIMEOUT;
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
scheduler_run_events(s);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
scheduler_initialize(scheduler_t *s)
|
||||
{
|
||||
memset(s, 0, sizeof(scheduler_t));
|
||||
|
||||
s->uuid = 1;
|
||||
|
||||
FD_ZERO(&s->read_fds);
|
||||
FD_ZERO(&s->write_fds);
|
||||
FD_ZERO(&s->except_fds);
|
||||
|
||||
INIT_LIST_HEAD(&s->events);
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef _SCHEDULER_H_
|
||||
#define _SCHEDULER_H_
|
||||
|
||||
#include <sys/select.h>
|
||||
|
||||
#include "list.h"
|
||||
|
||||
#define SCHEDULER_POLL_READ_FD 0x1
|
||||
#define SCHEDULER_POLL_WRITE_FD 0x2
|
||||
#define SCHEDULER_POLL_EXCEPT_FD 0x4
|
||||
#define SCHEDULER_POLL_TIMEOUT 0x8
|
||||
|
||||
typedef int event_id_t;
|
||||
typedef void (*event_cb_t) (event_id_t id, char mode, void *private);
|
||||
|
||||
typedef struct scheduler {
|
||||
fd_set read_fds;
|
||||
fd_set write_fds;
|
||||
fd_set except_fds;
|
||||
|
||||
struct list_head events;
|
||||
|
||||
int uuid;
|
||||
int max_fd;
|
||||
int timeout;
|
||||
int restart;
|
||||
int max_timeout;
|
||||
} scheduler_t;
|
||||
|
||||
void scheduler_initialize(scheduler_t *);
|
||||
event_id_t scheduler_register_event(scheduler_t *, char mode,
|
||||
int fd, int timeout,
|
||||
event_cb_t cb, void *private);
|
||||
void scheduler_unregister_event(scheduler_t *, event_id_t);
|
||||
void scheduler_set_max_timeout(scheduler_t *, int);
|
||||
int scheduler_wait_for_events(scheduler_t *);
|
||||
|
||||
#endif
|
||||
@@ -1,496 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* client harness for tapdisk log */
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include "log.h"
|
||||
|
||||
#define BDPRINTF(_f, _a...) fprintf (stderr, "log: " _f "\n", ## _a)
|
||||
|
||||
#define BWPRINTF(_f, _a...) fprintf (stderr, "log: " _f "\n", ## _a)
|
||||
|
||||
struct writelog {
|
||||
char* shmpath;
|
||||
uint32_t shmsize;
|
||||
void* shm;
|
||||
|
||||
/* next unprocessed item in the writelog */
|
||||
void* cur;
|
||||
unsigned int inflight;
|
||||
|
||||
/* pointer to start and end of free data space for requests */
|
||||
void* dhd;
|
||||
void* dtl;
|
||||
|
||||
log_sring_t* sring;
|
||||
log_front_ring_t fring;
|
||||
};
|
||||
|
||||
/* bytes free on the data ring */
|
||||
static inline unsigned int dring_avail(struct writelog* wl)
|
||||
{
|
||||
/* one byte reserved to distinguish empty from full */
|
||||
if (wl->dhd == wl->dtl)
|
||||
return sdataend(wl->shm) - sdatastart(wl->shm) - 1;
|
||||
|
||||
if (wl->dhd < wl->dtl)
|
||||
return wl->dtl - wl->dhd - 1;
|
||||
|
||||
return (sdataend(wl->shm) - wl->dhd) + (wl->dtl - sdatastart(wl->shm)) - 1;
|
||||
}
|
||||
|
||||
/* advance ring pointer by len bytes */
|
||||
static inline void* dring_advance(struct writelog* wl, void* start, size_t len)
|
||||
{
|
||||
void* next;
|
||||
int dsz = sdataend(wl->shm) - sdatastart(wl->shm);
|
||||
|
||||
next = start + (len % dsz);
|
||||
if (next > sdataend(wl->shm))
|
||||
next -= dsz;
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
fprintf(stderr, "usage: tapdisk-client <sock>\n");
|
||||
}
|
||||
|
||||
/* returns socket file descriptor */
|
||||
static int tdctl_open(const char* sockpath)
|
||||
{
|
||||
struct sockaddr_un saddr;
|
||||
int fd;
|
||||
|
||||
if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
|
||||
BWPRINTF("error creating socket: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&saddr, 0, sizeof(saddr));
|
||||
saddr.sun_family = AF_UNIX;
|
||||
memcpy(saddr.sun_path, sockpath, strlen(sockpath));
|
||||
|
||||
if (connect(fd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
|
||||
BWPRINTF("error connecting to socket %s: %s", sockpath, strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int ctl_talk(int fd, struct log_ctlmsg* msg, char* rsp, int rsplen)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if ((rc = write(fd, msg, sizeof(*msg))) < 0) {
|
||||
BWPRINTF("error sending ctl request: %s", strerror(errno));
|
||||
return -1;
|
||||
} else if (rc < sizeof(*msg)) {
|
||||
BWPRINTF("short ctl write (%d/%zd bytes)", rc, sizeof(*msg));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!rsplen)
|
||||
return 0;
|
||||
|
||||
if ((rc = read(fd, rsp, rsplen)) < 0) {
|
||||
BWPRINTF("error reading ctl response: %s", strerror(errno));
|
||||
return -1;
|
||||
} else if (rc < rsplen) {
|
||||
BWPRINTF("short ctl read (%d/%d bytes)", rc, rsplen);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ctl_get_shmem(int fd, struct writelog* wl)
|
||||
{
|
||||
struct log_ctlmsg req;
|
||||
char rsp[CTLRSPLEN_SHMP + 1];
|
||||
int rc;
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
memset(rsp, 0, sizeof(rsp));
|
||||
|
||||
memcpy(req.msg, LOGCMD_SHMP, 4);
|
||||
if ((rc = ctl_talk(fd, &req, rsp, CTLRSPLEN_SHMP)) < 0) {
|
||||
BWPRINTF("error getting shared memory parameters");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(&wl->shmsize, rsp, sizeof(wl->shmsize));
|
||||
wl->shmpath = strdup(rsp + sizeof(wl->shmsize));
|
||||
|
||||
BDPRINTF("shared memory parameters: size: %u, path: %s",
|
||||
wl->shmsize, wl->shmpath);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ctlmsg_init(struct log_ctlmsg* msg, const char* cmd)
|
||||
{
|
||||
memset(msg, 0, sizeof(*msg));
|
||||
memcpy(msg->msg, cmd, 4);
|
||||
}
|
||||
|
||||
static int ctl_get_writes(int fd)
|
||||
{
|
||||
struct log_ctlmsg req;
|
||||
char rsp[CTLRSPLEN_GET];
|
||||
int rc;
|
||||
|
||||
ctlmsg_init(&req, LOGCMD_GET);
|
||||
|
||||
if ((rc = ctl_talk(fd, &req, rsp, CTLRSPLEN_GET)) < 0) {
|
||||
BWPRINTF("error getting writes");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ctl_peek_writes(int fd)
|
||||
{
|
||||
struct log_ctlmsg req;
|
||||
char rsp[CTLRSPLEN_PEEK];
|
||||
int rc;
|
||||
|
||||
ctlmsg_init(&req, LOGCMD_PEEK);
|
||||
|
||||
if ((rc = ctl_talk(fd, &req, rsp, CTLRSPLEN_PEEK)) < 0) {
|
||||
BWPRINTF("error peeking writes");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* submit pending requests */
|
||||
static int ctl_kick(int fd)
|
||||
{
|
||||
struct log_ctlmsg req;
|
||||
int rc;
|
||||
|
||||
ctlmsg_init(&req, LOGCMD_KICK);
|
||||
|
||||
if ((rc = ctl_talk(fd, &req, NULL, 0)) < 0) {
|
||||
BWPRINTF("error kicking ring");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ctl_clear_writes(int fd)
|
||||
{
|
||||
struct log_ctlmsg req;
|
||||
char rsp[CTLRSPLEN_CLEAR];
|
||||
int rc;
|
||||
|
||||
ctlmsg_init(&req, LOGCMD_CLEAR);
|
||||
|
||||
if ((rc = ctl_talk(fd, &req, rsp, CTLRSPLEN_CLEAR)) < 0) {
|
||||
BWPRINTF("error clearing writes");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int writelog_map(struct writelog* wl)
|
||||
{
|
||||
int fd;
|
||||
void* shm;
|
||||
|
||||
if ((fd = shm_open(wl->shmpath, O_RDWR, 0750)) < 0) {
|
||||
BWPRINTF("could not open shared memory at %s: %s", wl->shmpath,
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
wl->shm = mmap(NULL, wl->shmsize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
close(fd);
|
||||
if (wl->shm == MAP_FAILED) {
|
||||
BWPRINTF("could not mmap write log shm: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
wl->cur = wl->shm;
|
||||
wl->inflight = 0;
|
||||
wl->dhd = wl->dtl = sdatastart(wl->shm);
|
||||
|
||||
BDPRINTF("shm cookie: 0x%x, data size: %u", *((uint32_t*)wl->shm),
|
||||
dring_avail(wl));
|
||||
|
||||
wl->sring = sringstart(wl->shm);
|
||||
/* need some thought about what to do on reconnect */
|
||||
FRONT_RING_INIT(&wl->fring, wl->sring, SRINGSIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int writelog_dump(struct writelog* wl)
|
||||
{
|
||||
struct disk_range* range = wl->shm;
|
||||
|
||||
for (range = wl->shm; (void*)range < bmend(wl->shm); range++) {
|
||||
if (!range->count)
|
||||
break;
|
||||
|
||||
BDPRINTF("dirty extent: %"PRIu64":%u",
|
||||
range->sector, range->count);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* walk dirty map and enqueue read requests.
|
||||
* returns: 0 when entire bitmap has been enqueued,
|
||||
* 1 when the ring is full
|
||||
* -1 on error
|
||||
*/
|
||||
static int writelog_enqueue_requests(struct writelog* wl)
|
||||
{
|
||||
struct disk_range* range = wl->shm;
|
||||
log_request_t* req;
|
||||
|
||||
for (range = wl->cur; (void*)range < bmend(wl->shm); range++) {
|
||||
if (!range->count)
|
||||
break;
|
||||
|
||||
if (RING_FULL(&wl->fring))
|
||||
break;
|
||||
|
||||
/* insert range into request stream */
|
||||
/* 1. get next request slot from ring */
|
||||
/* 2. ensure enough shm space is available */
|
||||
|
||||
BDPRINTF("enqueueing dirty extent: %"PRIu64":%u (ring space: %d/%d)",
|
||||
range->sector, range->count, RING_FREE_REQUESTS(&wl->fring),
|
||||
RING_SIZE(&wl->fring));
|
||||
|
||||
req = RING_GET_REQUEST(&wl->fring, wl->fring.req_prod_pvt);
|
||||
|
||||
req->sector = range->sector;
|
||||
req->count = range->count;
|
||||
/* ... */
|
||||
req->offset = 0;
|
||||
|
||||
wl->fring.req_prod_pvt++;
|
||||
wl->inflight++;
|
||||
}
|
||||
|
||||
wl->cur = range;
|
||||
|
||||
if (range->count)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int writelog_dequeue_responses(struct writelog* wl)
|
||||
{
|
||||
RING_IDX rstart, rend;
|
||||
log_response_t rsp;
|
||||
|
||||
rstart = wl->fring.rsp_cons;
|
||||
rend = wl->sring->rsp_prod;
|
||||
|
||||
BDPRINTF("ring kicked (start = %u, end = %u)", rstart, rend);
|
||||
|
||||
while (rstart != rend) {
|
||||
memcpy(&rsp, RING_GET_RESPONSE(&wl->fring, rstart), sizeof(rsp));
|
||||
BDPRINTF("ctl: read response %"PRIu64":%u", rsp.sector, rsp.count);
|
||||
wl->fring.rsp_cons = ++rstart;
|
||||
wl->inflight--;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int writelog_free(struct writelog* wl)
|
||||
{
|
||||
if (wl->shmpath) {
|
||||
free(wl->shmpath);
|
||||
wl->shmpath = NULL;
|
||||
}
|
||||
if (wl->shm) {
|
||||
munmap(wl->shm, wl->shmsize);
|
||||
wl->shm = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_writes(struct writelog* wl, int fd, int peek)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (peek)
|
||||
rc = ctl_peek_writes(fd);
|
||||
else
|
||||
rc = ctl_get_writes(fd);
|
||||
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
wl->cur = wl->shm;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int await_responses(struct writelog* wl, int fd)
|
||||
{
|
||||
struct log_ctlmsg msg;
|
||||
int rc;
|
||||
|
||||
/* sit on socket waiting for kick */
|
||||
if ((rc = read(fd, &msg, sizeof(msg))) < 0) {
|
||||
BWPRINTF("error reading from control socket: %s", strerror(errno));
|
||||
return -1;
|
||||
} else if (!rc) {
|
||||
BWPRINTF("EOF on control socket");
|
||||
return -1;
|
||||
} else if (rc < sizeof(msg)) {
|
||||
BWPRINTF("short reply (%d/%d bytes)", rc, (int) sizeof(msg));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strncmp(msg.msg, LOGCMD_KICK, 4)) {
|
||||
BWPRINTF("Unknown message received: %.4s", msg.msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (writelog_dequeue_responses(wl) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* read_loop:
|
||||
* 1. extract dirty bitmap
|
||||
* 2. feed as much as possible onto ring
|
||||
* 3. kick
|
||||
* 4. as responses come back, feed more of the dirty bitmap
|
||||
* into the ring
|
||||
* 5. when entire bitmap has been queued, go to 1?
|
||||
*/
|
||||
int read_loop(struct writelog* wl, int fd)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (get_writes(wl, fd, 1) < 0)
|
||||
return -1;
|
||||
writelog_dump(wl);
|
||||
|
||||
do {
|
||||
rc = writelog_enqueue_requests(wl);
|
||||
|
||||
if (RING_FREE_REQUESTS(&wl->fring) < RING_SIZE(&wl->fring))
|
||||
RING_PUSH_REQUESTS(&wl->fring);
|
||||
if (ctl_kick(fd) < 0)
|
||||
return -1;
|
||||
|
||||
/* collect responses */
|
||||
if (wl->inflight && await_responses(wl, fd) < 0)
|
||||
return -1;
|
||||
} while (rc > 0);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
int fd;
|
||||
struct writelog wl;
|
||||
char cmd;
|
||||
|
||||
if (argc < 2) {
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (argc < 3)
|
||||
cmd = 'p';
|
||||
else
|
||||
cmd = argv[2][0];
|
||||
|
||||
fd = tdctl_open(argv[1]);
|
||||
|
||||
if (ctl_get_shmem(fd, &wl) < 0)
|
||||
return 1;
|
||||
|
||||
if (writelog_map(&wl) < 0) {
|
||||
BWPRINTF("Error mapping write log: %s", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case 'p':
|
||||
if (get_writes(&wl, fd, 1) < 0)
|
||||
return 1;
|
||||
writelog_dump(&wl);
|
||||
break;
|
||||
case 'c':
|
||||
if (ctl_clear_writes(fd) < 0)
|
||||
return 1;
|
||||
break;
|
||||
case 'g':
|
||||
if (get_writes(&wl, fd, 0) < 0)
|
||||
return 1;
|
||||
writelog_dump(&wl);
|
||||
break;
|
||||
case 'r':
|
||||
if (read_loop(&wl, fd) < 0)
|
||||
return 1;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
writelog_free(&wl);
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,837 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "list.h"
|
||||
#include "tapdisk.h"
|
||||
#include "blktap2.h"
|
||||
#include "blktaplib.h"
|
||||
#include "tapdisk-vbd.h"
|
||||
#include "tapdisk-utils.h"
|
||||
#include "tapdisk-server.h"
|
||||
#include "tapdisk-message.h"
|
||||
#include "tapdisk-disktype.h"
|
||||
|
||||
struct tapdisk_control {
|
||||
char *path;
|
||||
int socket;
|
||||
int event_id;
|
||||
};
|
||||
|
||||
struct tapdisk_control_connection {
|
||||
int socket;
|
||||
event_id_t event_id;
|
||||
};
|
||||
|
||||
static struct tapdisk_control td_control;
|
||||
|
||||
static void
|
||||
tapdisk_control_initialize(void)
|
||||
{
|
||||
td_control.socket = -1;
|
||||
td_control.event_id = -1;
|
||||
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
}
|
||||
|
||||
void
|
||||
tapdisk_control_close(void)
|
||||
{
|
||||
if (td_control.path) {
|
||||
unlink(td_control.path);
|
||||
free(td_control.path);
|
||||
td_control.path = NULL;
|
||||
}
|
||||
|
||||
if (td_control.socket != -1) {
|
||||
close(td_control.socket);
|
||||
td_control.socket = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static struct tapdisk_control_connection *
|
||||
tapdisk_control_allocate_connection(int fd)
|
||||
{
|
||||
struct tapdisk_control_connection *connection;
|
||||
size_t sz;
|
||||
|
||||
connection = calloc(1, sizeof(*connection));
|
||||
if (!connection) {
|
||||
EPRINTF("calloc");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
connection->socket = fd;
|
||||
return connection;
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_control_close_connection(struct tapdisk_control_connection *connection)
|
||||
{
|
||||
tapdisk_server_unregister_event(connection->event_id);
|
||||
close(connection->socket);
|
||||
free(connection);
|
||||
}
|
||||
|
||||
static int
|
||||
tapdisk_control_read_message(int fd, tapdisk_message_t *message, int timeout)
|
||||
{
|
||||
fd_set readfds;
|
||||
int ret, len, offset;
|
||||
struct timeval tv, *t;
|
||||
|
||||
t = NULL;
|
||||
offset = 0;
|
||||
len = sizeof(tapdisk_message_t);
|
||||
|
||||
if (timeout) {
|
||||
tv.tv_sec = timeout;
|
||||
tv.tv_usec = 0;
|
||||
t = &tv;
|
||||
}
|
||||
|
||||
memset(message, 0, sizeof(tapdisk_message_t));
|
||||
|
||||
while (offset < len) {
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(fd, &readfds);
|
||||
|
||||
ret = select(fd + 1, &readfds, NULL, NULL, t);
|
||||
if (ret == -1)
|
||||
break;
|
||||
else if (FD_ISSET(fd, &readfds)) {
|
||||
ret = read(fd, message + offset, len - offset);
|
||||
if (ret <= 0)
|
||||
break;
|
||||
offset += ret;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
if (offset != len) {
|
||||
EPRINTF("failure reading message (wanted %d but got %d)\n",
|
||||
len, offset);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
DPRINTF("received '%s' message (uuid = %u)\n",
|
||||
tapdisk_message_name(message->type), message->cookie);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
tapdisk_control_write_message(int fd, tapdisk_message_t *message, int timeout)
|
||||
{
|
||||
fd_set writefds;
|
||||
int ret, len, offset;
|
||||
struct timeval tv, *t;
|
||||
|
||||
t = NULL;
|
||||
offset = 0;
|
||||
len = sizeof(tapdisk_message_t);
|
||||
|
||||
if (timeout) {
|
||||
tv.tv_sec = timeout;
|
||||
tv.tv_usec = 0;
|
||||
t = &tv;
|
||||
}
|
||||
|
||||
DPRINTF("sending '%s' message (uuid = %u)\n",
|
||||
tapdisk_message_name(message->type), message->cookie);
|
||||
|
||||
while (offset < len) {
|
||||
FD_ZERO(&writefds);
|
||||
FD_SET(fd, &writefds);
|
||||
|
||||
/* we don't bother reinitializing tv. at worst, it will wait a
|
||||
* bit more time than expected. */
|
||||
|
||||
ret = select(fd + 1, NULL, &writefds, NULL, t);
|
||||
if (ret == -1)
|
||||
break;
|
||||
else if (FD_ISSET(fd, &writefds)) {
|
||||
ret = write(fd, message + offset, len - offset);
|
||||
if (ret <= 0)
|
||||
break;
|
||||
offset += ret;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
if (offset != len) {
|
||||
EPRINTF("failure writing message\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
tapdisk_control_validate_request(tapdisk_message_t *request)
|
||||
{
|
||||
if (strnlen(request->u.params.path,
|
||||
TAPDISK_MESSAGE_MAX_PATH_LENGTH) >=
|
||||
TAPDISK_MESSAGE_MAX_PATH_LENGTH)
|
||||
return EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_control_list_minors(struct tapdisk_control_connection *connection,
|
||||
tapdisk_message_t *request)
|
||||
{
|
||||
int i;
|
||||
td_vbd_t *vbd;
|
||||
struct list_head *head;
|
||||
tapdisk_message_t response;
|
||||
|
||||
i = 0;
|
||||
memset(&response, 0, sizeof(response));
|
||||
|
||||
response.type = TAPDISK_MESSAGE_LIST_MINORS_RSP;
|
||||
response.cookie = request->cookie;
|
||||
|
||||
head = tapdisk_server_get_all_vbds();
|
||||
|
||||
list_for_each_entry(vbd, head, next) {
|
||||
response.u.minors.list[i++] = vbd->minor;
|
||||
if (i >= TAPDISK_MESSAGE_MAX_MINORS) {
|
||||
response.type = TAPDISK_MESSAGE_ERROR;
|
||||
response.u.response.error = ERANGE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
response.u.minors.count = i;
|
||||
tapdisk_control_write_message(connection->socket, &response, 2);
|
||||
tapdisk_control_close_connection(connection);
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_control_list(struct tapdisk_control_connection *connection,
|
||||
tapdisk_message_t *request)
|
||||
{
|
||||
td_vbd_t *vbd;
|
||||
struct list_head *head;
|
||||
tapdisk_message_t response;
|
||||
int count, i;
|
||||
|
||||
memset(&response, 0, sizeof(response));
|
||||
response.type = TAPDISK_MESSAGE_LIST_RSP;
|
||||
response.cookie = request->cookie;
|
||||
|
||||
head = tapdisk_server_get_all_vbds();
|
||||
|
||||
count = 0;
|
||||
list_for_each_entry(vbd, head, next)
|
||||
count++;
|
||||
|
||||
list_for_each_entry(vbd, head, next) {
|
||||
response.u.list.count = count--;
|
||||
response.u.list.minor = vbd->minor;
|
||||
response.u.list.state = vbd->state;
|
||||
response.u.list.path[0] = 0;
|
||||
|
||||
if (!list_empty(&vbd->images)) {
|
||||
td_image_t *image = list_entry(vbd->images.next,
|
||||
td_image_t, next);
|
||||
snprintf(response.u.list.path,
|
||||
sizeof(response.u.list.path),
|
||||
"%s:%s",
|
||||
tapdisk_disk_types[image->type]->name,
|
||||
image->name);
|
||||
}
|
||||
|
||||
tapdisk_control_write_message(connection->socket, &response, 2);
|
||||
}
|
||||
|
||||
response.u.list.count = count;
|
||||
response.u.list.minor = -1;
|
||||
response.u.list.path[0] = 0;
|
||||
|
||||
tapdisk_control_write_message(connection->socket, &response, 2);
|
||||
tapdisk_control_close_connection(connection);
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_control_get_pid(struct tapdisk_control_connection *connection,
|
||||
tapdisk_message_t *request)
|
||||
{
|
||||
tapdisk_message_t response;
|
||||
|
||||
memset(&response, 0, sizeof(response));
|
||||
response.type = TAPDISK_MESSAGE_PID_RSP;
|
||||
response.cookie = request->cookie;
|
||||
response.u.tapdisk_pid = getpid();
|
||||
|
||||
tapdisk_control_write_message(connection->socket, &response, 2);
|
||||
tapdisk_control_close_connection(connection);
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_control_attach_vbd(struct tapdisk_control_connection *connection,
|
||||
tapdisk_message_t *request)
|
||||
{
|
||||
tapdisk_message_t response;
|
||||
char *devname;
|
||||
td_vbd_t *vbd;
|
||||
struct blktap2_params params;
|
||||
image_t image;
|
||||
int minor, err;
|
||||
|
||||
/*
|
||||
* TODO: check for max vbds per process
|
||||
*/
|
||||
|
||||
vbd = tapdisk_server_get_vbd(request->cookie);
|
||||
if (vbd) {
|
||||
err = -EEXIST;
|
||||
goto out;
|
||||
}
|
||||
|
||||
minor = request->cookie;
|
||||
if (minor < 0) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
vbd = tapdisk_vbd_create(minor);
|
||||
if (!vbd) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = asprintf(&devname, BLKTAP2_RING_DEVICE"%d", minor);
|
||||
if (err == -1) {
|
||||
err = -ENOMEM;
|
||||
goto fail_vbd;
|
||||
}
|
||||
|
||||
err = tapdisk_vbd_attach(vbd, devname, minor);
|
||||
free(devname);
|
||||
if (err)
|
||||
goto fail_vbd;
|
||||
|
||||
tapdisk_server_add_vbd(vbd);
|
||||
|
||||
out:
|
||||
memset(&response, 0, sizeof(response));
|
||||
response.type = TAPDISK_MESSAGE_ATTACH_RSP;
|
||||
response.cookie = request->cookie;
|
||||
response.u.response.error = -err;
|
||||
|
||||
tapdisk_control_write_message(connection->socket, &response, 2);
|
||||
tapdisk_control_close_connection(connection);
|
||||
|
||||
return;
|
||||
|
||||
fail_vbd:
|
||||
tapdisk_vbd_detach(vbd);
|
||||
free(vbd);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
tapdisk_control_detach_vbd(struct tapdisk_control_connection *connection,
|
||||
tapdisk_message_t *request)
|
||||
{
|
||||
tapdisk_message_t response;
|
||||
td_vbd_t *vbd;
|
||||
int err;
|
||||
|
||||
vbd = tapdisk_server_get_vbd(request->cookie);
|
||||
if (!vbd) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
tapdisk_vbd_detach(vbd);
|
||||
|
||||
if (list_empty(&vbd->images)) {
|
||||
tapdisk_server_remove_vbd(vbd);
|
||||
free(vbd);
|
||||
}
|
||||
|
||||
err = 0;
|
||||
out:
|
||||
memset(&response, 0, sizeof(response));
|
||||
response.type = TAPDISK_MESSAGE_DETACH_RSP;
|
||||
response.cookie = request->cookie;
|
||||
response.u.response.error = -err;
|
||||
|
||||
tapdisk_control_write_message(connection->socket, &response, 2);
|
||||
tapdisk_control_close_connection(connection);
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_control_open_image(struct tapdisk_control_connection *connection,
|
||||
tapdisk_message_t *request)
|
||||
{
|
||||
int err;
|
||||
image_t image;
|
||||
td_vbd_t *vbd;
|
||||
td_flag_t flags;
|
||||
tapdisk_message_t response;
|
||||
struct blktap2_params params;
|
||||
|
||||
vbd = tapdisk_server_get_vbd(request->cookie);
|
||||
if (!vbd) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (vbd->minor == -1) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (vbd->name) {
|
||||
err = -EALREADY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
flags = 0;
|
||||
if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_RDONLY)
|
||||
flags |= TD_OPEN_RDONLY;
|
||||
if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_SHARED)
|
||||
flags |= TD_OPEN_SHAREABLE;
|
||||
if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_ADD_CACHE)
|
||||
flags |= TD_OPEN_ADD_CACHE;
|
||||
if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_VHD_INDEX)
|
||||
flags |= TD_OPEN_VHD_INDEX;
|
||||
if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_LOG_DIRTY)
|
||||
flags |= TD_OPEN_LOG_DIRTY;
|
||||
|
||||
vbd->name = strndup(request->u.params.path,
|
||||
sizeof(request->u.params.path));
|
||||
if (!vbd->name) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = tapdisk_vbd_parse_stack(vbd, request->u.params.path);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = tapdisk_vbd_open_stack(vbd, request->u.params.storage, flags);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = tapdisk_vbd_get_image_info(vbd, &image);
|
||||
if (err)
|
||||
goto fail_close;
|
||||
|
||||
params.capacity = image.size;
|
||||
params.sector_size = image.secsize;
|
||||
strncpy(params.name, vbd->name, BLKTAP2_MAX_MESSAGE_LEN);
|
||||
|
||||
err = ioctl(vbd->ring.fd, BLKTAP2_IOCTL_CREATE_DEVICE, ¶ms);
|
||||
if (err && errno != EEXIST) {
|
||||
err = -errno;
|
||||
EPRINTF("create device failed: %d\n", err);
|
||||
goto fail_close;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
|
||||
out:
|
||||
memset(&response, 0, sizeof(response));
|
||||
response.cookie = request->cookie;
|
||||
|
||||
if (err) {
|
||||
response.type = TAPDISK_MESSAGE_ERROR;
|
||||
response.u.response.error = -err;
|
||||
} else {
|
||||
response.u.image.sectors = image.size;
|
||||
response.u.image.sector_size = image.secsize;
|
||||
response.u.image.info = image.info;
|
||||
response.type = TAPDISK_MESSAGE_OPEN_RSP;
|
||||
}
|
||||
|
||||
tapdisk_control_write_message(connection->socket, &response, 2);
|
||||
tapdisk_control_close_connection(connection);
|
||||
|
||||
return;
|
||||
|
||||
fail_close:
|
||||
tapdisk_vbd_close_vdi(vbd);
|
||||
free(vbd->name);
|
||||
vbd->name = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_control_close_image(struct tapdisk_control_connection *connection,
|
||||
tapdisk_message_t *request)
|
||||
{
|
||||
tapdisk_message_t response;
|
||||
td_vbd_t *vbd;
|
||||
int err;
|
||||
|
||||
vbd = tapdisk_server_get_vbd(request->cookie);
|
||||
if (!vbd) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!list_empty(&vbd->pending_requests)) {
|
||||
err = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
|
||||
tapdisk_vbd_close_vdi(vbd);
|
||||
|
||||
/* NB. vbd->name free should probably belong into close_vdi,
|
||||
but the current blktap1 reopen-stuff likely depends on a
|
||||
lifetime extended until shutdown. */
|
||||
free(vbd->name);
|
||||
vbd->name = NULL;
|
||||
|
||||
if (vbd->minor == -1) {
|
||||
tapdisk_server_remove_vbd(vbd);
|
||||
tapdisk_vbd_free(vbd);
|
||||
}
|
||||
|
||||
err = 0;
|
||||
out:
|
||||
memset(&response, 0, sizeof(response));
|
||||
response.type = TAPDISK_MESSAGE_CLOSE_RSP;
|
||||
response.cookie = request->cookie;
|
||||
response.u.response.error = -err;
|
||||
|
||||
tapdisk_control_write_message(connection->socket, &response, 2);
|
||||
tapdisk_control_close_connection(connection);
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_control_pause_vbd(struct tapdisk_control_connection *connection,
|
||||
tapdisk_message_t *request)
|
||||
{
|
||||
int err;
|
||||
td_vbd_t *vbd;
|
||||
tapdisk_message_t response;
|
||||
|
||||
memset(&response, 0, sizeof(response));
|
||||
|
||||
response.type = TAPDISK_MESSAGE_PAUSE_RSP;
|
||||
|
||||
vbd = tapdisk_server_get_vbd(request->cookie);
|
||||
if (!vbd) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
do {
|
||||
err = tapdisk_vbd_pause(vbd);
|
||||
|
||||
if (!err || err != -EAGAIN)
|
||||
break;
|
||||
|
||||
tapdisk_server_iterate();
|
||||
} while (1);
|
||||
|
||||
out:
|
||||
response.cookie = request->cookie;
|
||||
response.u.response.error = -err;
|
||||
tapdisk_control_write_message(connection->socket, &response, 2);
|
||||
tapdisk_control_close_connection(connection);
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_control_resume_vbd(struct tapdisk_control_connection *connection,
|
||||
tapdisk_message_t *request)
|
||||
{
|
||||
int err;
|
||||
td_vbd_t *vbd;
|
||||
tapdisk_message_t response;
|
||||
|
||||
memset(&response, 0, sizeof(response));
|
||||
|
||||
response.type = TAPDISK_MESSAGE_RESUME_RSP;
|
||||
|
||||
vbd = tapdisk_server_get_vbd(request->cookie);
|
||||
if (!vbd) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!td_flag_test(vbd->state, TD_VBD_PAUSED)) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (request->u.params.path[0]) {
|
||||
free(vbd->name);
|
||||
vbd->name = strndup(request->u.params.path,
|
||||
sizeof(request->u.params.path));
|
||||
if (!vbd->name) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
} else if (!vbd->name) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = tapdisk_vbd_parse_stack(vbd, vbd->name);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = tapdisk_vbd_resume(vbd, NULL, -1);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
out:
|
||||
response.cookie = request->cookie;
|
||||
response.u.response.error = -err;
|
||||
tapdisk_control_write_message(connection->socket, &response, 2);
|
||||
tapdisk_control_close_connection(connection);
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_control_handle_request(event_id_t id, char mode, void *private)
|
||||
{
|
||||
int err;
|
||||
tapdisk_message_t message;
|
||||
struct tapdisk_control_connection *connection =
|
||||
(struct tapdisk_control_connection *)private;
|
||||
|
||||
if (tapdisk_control_read_message(connection->socket, &message, 2)) {
|
||||
EPRINTF("failed to read message from %d\n", connection->socket);
|
||||
tapdisk_control_close_connection(connection);
|
||||
return;
|
||||
}
|
||||
|
||||
err = tapdisk_control_validate_request(&message);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
switch (message.type) {
|
||||
case TAPDISK_MESSAGE_PID:
|
||||
return tapdisk_control_get_pid(connection, &message);
|
||||
case TAPDISK_MESSAGE_LIST_MINORS:
|
||||
return tapdisk_control_list_minors(connection, &message);
|
||||
case TAPDISK_MESSAGE_LIST:
|
||||
return tapdisk_control_list(connection, &message);
|
||||
case TAPDISK_MESSAGE_ATTACH:
|
||||
return tapdisk_control_attach_vbd(connection, &message);
|
||||
case TAPDISK_MESSAGE_DETACH:
|
||||
return tapdisk_control_detach_vbd(connection, &message);
|
||||
case TAPDISK_MESSAGE_OPEN:
|
||||
return tapdisk_control_open_image(connection, &message);
|
||||
case TAPDISK_MESSAGE_PAUSE:
|
||||
return tapdisk_control_pause_vbd(connection, &message);
|
||||
case TAPDISK_MESSAGE_RESUME:
|
||||
return tapdisk_control_resume_vbd(connection, &message);
|
||||
case TAPDISK_MESSAGE_CLOSE:
|
||||
return tapdisk_control_close_image(connection, &message);
|
||||
default: {
|
||||
tapdisk_message_t response;
|
||||
fail:
|
||||
|
||||
EPRINTF("received unsupported message '%s'\n",
|
||||
tapdisk_message_name(message.type));
|
||||
|
||||
memset(&response, 0, sizeof(response));
|
||||
|
||||
response.type = TAPDISK_MESSAGE_ERROR;
|
||||
response.u.response.error = (err ? -err : EINVAL);
|
||||
tapdisk_control_write_message(connection->socket, &response, 2);
|
||||
|
||||
tapdisk_control_close_connection(connection);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_control_accept(event_id_t id, char mode, void *private)
|
||||
{
|
||||
int err, fd;
|
||||
struct tapdisk_control_connection *connection;
|
||||
|
||||
fd = accept(td_control.socket, NULL, NULL);
|
||||
if (fd == -1) {
|
||||
EPRINTF("failed to accept new control connection: %d\n", errno);
|
||||
return;
|
||||
}
|
||||
|
||||
connection = tapdisk_control_allocate_connection(fd);
|
||||
if (!connection) {
|
||||
close(fd);
|
||||
EPRINTF("failed to allocate new control connection\n");
|
||||
}
|
||||
|
||||
err = tapdisk_server_register_event(SCHEDULER_POLL_READ_FD,
|
||||
connection->socket, 0,
|
||||
tapdisk_control_handle_request,
|
||||
connection);
|
||||
if (err == -1) {
|
||||
close(fd);
|
||||
free(connection);
|
||||
EPRINTF("failed to register new control event: %d\n", err);
|
||||
}
|
||||
|
||||
connection->event_id = err;
|
||||
}
|
||||
|
||||
static int
|
||||
tapdisk_control_mkdir(const char *dir)
|
||||
{
|
||||
int err;
|
||||
char *ptr, *name, *start;
|
||||
|
||||
err = access(dir, W_OK | R_OK);
|
||||
if (!err)
|
||||
return 0;
|
||||
|
||||
name = strdup(dir);
|
||||
if (!name)
|
||||
return -ENOMEM;
|
||||
|
||||
start = name;
|
||||
|
||||
for (;;) {
|
||||
ptr = strchr(start + 1, '/');
|
||||
if (ptr)
|
||||
*ptr = '\0';
|
||||
|
||||
err = mkdir(name, 0755);
|
||||
if (err && errno != EEXIST) {
|
||||
err = -errno;
|
||||
EPRINTF("failed to create directory %s: %d\n",
|
||||
name, err);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ptr)
|
||||
break;
|
||||
else {
|
||||
*ptr = '/';
|
||||
start = ptr + 1;
|
||||
}
|
||||
}
|
||||
|
||||
free(name);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
tapdisk_control_create_socket(char **socket_path)
|
||||
{
|
||||
int err, flags;
|
||||
struct sockaddr_un saddr;
|
||||
|
||||
err = tapdisk_control_mkdir(BLKTAP2_CONTROL_DIR);
|
||||
if (err) {
|
||||
EPRINTF("failed to create directory %s: %d\n",
|
||||
BLKTAP2_CONTROL_DIR, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = asprintf(&td_control.path, "%s/%s%d",
|
||||
BLKTAP2_CONTROL_DIR, BLKTAP2_CONTROL_SOCKET, getpid());
|
||||
if (err == -1) {
|
||||
td_control.path = NULL;
|
||||
err = (errno ? : ENOMEM);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (unlink(td_control.path) && errno != ENOENT) {
|
||||
err = errno;
|
||||
EPRINTF("failed to unlink %s: %d\n", td_control.path, errno);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
td_control.socket = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (td_control.socket == -1) {
|
||||
err = errno;
|
||||
EPRINTF("failed to create control socket: %d\n", err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
memset(&saddr, 0, sizeof(saddr));
|
||||
strncpy(saddr.sun_path, td_control.path, sizeof(saddr.sun_path));
|
||||
saddr.sun_family = AF_UNIX;
|
||||
|
||||
err = bind(td_control.socket,
|
||||
(const struct sockaddr *)&saddr, sizeof(saddr));
|
||||
if (err == -1) {
|
||||
err = errno;
|
||||
EPRINTF("failed to bind to %s: %d\n", saddr.sun_path, err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = listen(td_control.socket, 10);
|
||||
if (err == -1) {
|
||||
err = errno;
|
||||
EPRINTF("failed to listen: %d\n", err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = tapdisk_server_register_event(SCHEDULER_POLL_READ_FD,
|
||||
td_control.socket, 0,
|
||||
tapdisk_control_accept, NULL);
|
||||
if (err < 0) {
|
||||
EPRINTF("failed to add watch: %d\n", err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
td_control.event_id = err;
|
||||
*socket_path = td_control.path;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
tapdisk_control_close();
|
||||
return err;
|
||||
}
|
||||
|
||||
int
|
||||
tapdisk_control_open(char **path)
|
||||
{
|
||||
int err;
|
||||
|
||||
tapdisk_control_initialize();
|
||||
|
||||
return tapdisk_control_create_socket(path);
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __TAPDISK_CONTROL_H__
|
||||
#define __TAPDISK_CONTROL_H__
|
||||
|
||||
int tapdisk_control_open(char **path);
|
||||
void tapdisk_control_close(void);
|
||||
|
||||
#endif
|
||||
@@ -1,802 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <libgen.h> /* for basename(3) */
|
||||
#include <unistd.h>
|
||||
|
||||
#include "list.h"
|
||||
#include "scheduler.h"
|
||||
#include "tapdisk-vbd.h"
|
||||
#include "tapdisk-server.h"
|
||||
#include "tapdisk-disktype.h"
|
||||
#include "tapdisk-utils.h"
|
||||
#include "libvhd.h"
|
||||
|
||||
#define POLL_READ 0
|
||||
#define POLL_WRITE 1
|
||||
|
||||
#define SPB_SHIFT (VHD_BLOCK_SHIFT - SECTOR_SHIFT)
|
||||
|
||||
struct tapdisk_stream_poll {
|
||||
int pipe[2];
|
||||
int set;
|
||||
};
|
||||
|
||||
struct tapdisk_stream_request {
|
||||
uint64_t sec;
|
||||
uint32_t secs;
|
||||
uint64_t seqno;
|
||||
blkif_request_t blkif_req;
|
||||
struct list_head next;
|
||||
};
|
||||
|
||||
struct tapdisk_stream {
|
||||
td_vbd_t *vbd;
|
||||
|
||||
unsigned int id;
|
||||
|
||||
int err;
|
||||
|
||||
uint64_t cur;
|
||||
uint64_t start;
|
||||
uint64_t end;
|
||||
|
||||
uint64_t started;
|
||||
uint64_t completed;
|
||||
|
||||
struct tapdisk_stream_poll poll;
|
||||
event_id_t enqueue_event_id;
|
||||
|
||||
struct list_head free_list;
|
||||
struct list_head pending_list;
|
||||
struct list_head completed_list;
|
||||
|
||||
struct tapdisk_stream_request requests[MAX_REQUESTS];
|
||||
};
|
||||
|
||||
static unsigned int tapdisk_stream_count;
|
||||
|
||||
static void tapdisk_stream_close_image(struct tapdisk_stream *);
|
||||
|
||||
static char *program;
|
||||
static struct tapdisk_stream stream1, stream2;
|
||||
static vhd_context_t vhd1;
|
||||
|
||||
static void
|
||||
usage(FILE *stream)
|
||||
{
|
||||
printf("usage: %s <-n type:/path/to/image> <-m type:/path/to/image>\n",
|
||||
program);
|
||||
}
|
||||
|
||||
static int
|
||||
open_vhd(const char *path, vhd_context_t *vhd)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = vhd_open(vhd, path, VHD_OPEN_RDONLY);
|
||||
if (err) {
|
||||
printf("error opening %s: %d\n", path, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = vhd_get_bat(vhd);
|
||||
if (err)
|
||||
{
|
||||
printf("error reading BAT for %s: %d\n", path, err);
|
||||
vhd_close(vhd);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
tapdisk_stream_poll_initialize(struct tapdisk_stream_poll *p)
|
||||
{
|
||||
p->set = 0;
|
||||
p->pipe[POLL_READ] = p->pipe[POLL_WRITE] = -1;
|
||||
}
|
||||
|
||||
static int
|
||||
tapdisk_stream_poll_open(struct tapdisk_stream_poll *p)
|
||||
{
|
||||
int err;
|
||||
|
||||
tapdisk_stream_poll_initialize(p);
|
||||
|
||||
err = pipe(p->pipe);
|
||||
if (err)
|
||||
return -errno;
|
||||
|
||||
err = fcntl(p->pipe[POLL_READ], F_SETFL, O_NONBLOCK);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = fcntl(p->pipe[POLL_WRITE], F_SETFL, O_NONBLOCK);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
close(p->pipe[POLL_READ]);
|
||||
close(p->pipe[POLL_WRITE]);
|
||||
tapdisk_stream_poll_initialize(p);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_stream_poll_close(struct tapdisk_stream_poll *p)
|
||||
{
|
||||
if (p->pipe[POLL_READ] != -1)
|
||||
close(p->pipe[POLL_READ]);
|
||||
if (p->pipe[POLL_WRITE] != -1)
|
||||
close(p->pipe[POLL_WRITE]);
|
||||
tapdisk_stream_poll_initialize(p);
|
||||
}
|
||||
|
||||
static inline void
|
||||
tapdisk_stream_poll_clear(struct tapdisk_stream_poll *p)
|
||||
{
|
||||
int dummy;
|
||||
|
||||
read_exact(p->pipe[POLL_READ], &dummy, sizeof(dummy));
|
||||
p->set = 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
tapdisk_stream_poll_set(struct tapdisk_stream_poll *p)
|
||||
{
|
||||
int dummy = 0;
|
||||
|
||||
if (!p->set) {
|
||||
write_exact(p->pipe[POLL_WRITE], &dummy, sizeof(dummy));
|
||||
p->set = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int
|
||||
tapdisk_stream_stop(struct tapdisk_stream *s)
|
||||
{
|
||||
return ((s->cur == s->end || s->err) &&
|
||||
list_empty(&s->pending_list) &&
|
||||
list_empty(&s->completed_list));
|
||||
}
|
||||
|
||||
static inline void
|
||||
tapdisk_stream_initialize_request(struct tapdisk_stream_request *req)
|
||||
{
|
||||
memset(req, 0, sizeof(*req));
|
||||
INIT_LIST_HEAD(&req->next);
|
||||
}
|
||||
|
||||
static inline int
|
||||
tapdisk_stream_request_idx(struct tapdisk_stream *s,
|
||||
struct tapdisk_stream_request *req)
|
||||
{
|
||||
return (req - s->requests);
|
||||
}
|
||||
|
||||
static inline struct tapdisk_stream_request *
|
||||
tapdisk_stream_get_request(struct tapdisk_stream *s)
|
||||
{
|
||||
struct tapdisk_stream_request *req;
|
||||
|
||||
if (list_empty(&s->free_list))
|
||||
return NULL;
|
||||
|
||||
req = list_entry(s->free_list.next,
|
||||
struct tapdisk_stream_request, next);
|
||||
|
||||
list_del_init(&req->next);
|
||||
tapdisk_stream_initialize_request(req);
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
static inline void
|
||||
tapdisk_stream_queue_completed(struct tapdisk_stream *s,
|
||||
struct tapdisk_stream_request *sreq)
|
||||
{
|
||||
struct tapdisk_stream_request *itr;
|
||||
|
||||
list_for_each_entry(itr, &s->completed_list, next)
|
||||
if (sreq->seqno < itr->seqno) {
|
||||
list_add_tail(&sreq->next, &itr->next);
|
||||
return;
|
||||
}
|
||||
|
||||
list_add_tail(&sreq->next, &s->completed_list);
|
||||
}
|
||||
|
||||
static int
|
||||
tapdisk_result_compare(struct tapdisk_stream_request *sreq1,
|
||||
struct tapdisk_stream_request *sreq2)
|
||||
{
|
||||
unsigned long idx1, idx2;
|
||||
char *buf1, *buf2;
|
||||
int result;
|
||||
|
||||
assert(sreq1->seqno == sreq2->seqno);
|
||||
assert(sreq1->secs == sreq2->secs);
|
||||
idx1 = (unsigned long)tapdisk_stream_request_idx(&stream1,
|
||||
sreq1);
|
||||
idx2 = (unsigned long)tapdisk_stream_request_idx(&stream2,
|
||||
sreq2);
|
||||
buf1 = (char *)MMAP_VADDR(stream1.vbd->ring.vstart, idx1, 0);
|
||||
buf2 = (char *)MMAP_VADDR(stream2.vbd->ring.vstart, idx2, 0);
|
||||
|
||||
result = memcmp(buf1, buf2, sreq1->secs << SECTOR_SHIFT);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int
|
||||
tapdisk_stream_process_data(void)
|
||||
{
|
||||
struct tapdisk_stream_request *sreq1, *sreq2, *tmp1, *tmp2;
|
||||
int advance_both;
|
||||
int result = 0;
|
||||
|
||||
sreq1 = list_entry(stream1.completed_list.next,
|
||||
struct tapdisk_stream_request, next);
|
||||
sreq2 = list_entry(stream2.completed_list.next,
|
||||
struct tapdisk_stream_request, next);
|
||||
tmp1 = list_entry(sreq1->next.next,
|
||||
struct tapdisk_stream_request, next);
|
||||
tmp2 = list_entry(sreq2->next.next,
|
||||
struct tapdisk_stream_request, next);
|
||||
while (result == 0 &&
|
||||
&sreq1->next != &stream1.completed_list &&
|
||||
&sreq2->next != &stream2.completed_list) {
|
||||
//printf("checking: %llu|%llu\n", sreq1->seqno, sreq2->seqno);
|
||||
advance_both = 1;
|
||||
if (sreq1->seqno < sreq2->seqno) {
|
||||
advance_both = 0;
|
||||
goto advance1;
|
||||
}
|
||||
if (sreq1->seqno > sreq2->seqno)
|
||||
goto advance2;
|
||||
|
||||
result = tapdisk_result_compare(sreq1, sreq2);
|
||||
|
||||
stream1.completed++;
|
||||
stream2.completed++;
|
||||
|
||||
list_del_init(&sreq1->next);
|
||||
list_add_tail(&sreq1->next, &stream1.free_list);
|
||||
list_del_init(&sreq2->next);
|
||||
list_add_tail(&sreq2->next, &stream2.free_list);
|
||||
|
||||
advance1:
|
||||
sreq1 = tmp1;
|
||||
tmp1 = list_entry(tmp1->next.next,
|
||||
struct tapdisk_stream_request, next);
|
||||
if (!advance_both)
|
||||
continue;
|
||||
advance2:
|
||||
sreq2 = tmp2;
|
||||
tmp2 = list_entry(tmp2->next.next,
|
||||
struct tapdisk_stream_request, next);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_stream_dequeue(void *arg, blkif_response_t *rsp)
|
||||
{
|
||||
struct tapdisk_stream *s = (struct tapdisk_stream *)arg;
|
||||
struct tapdisk_stream_request *sreq = s->requests + rsp->id;
|
||||
|
||||
list_del_init(&sreq->next);
|
||||
|
||||
if (rsp->status == BLKIF_RSP_OKAY)
|
||||
tapdisk_stream_queue_completed(s, sreq);
|
||||
else {
|
||||
s->err = EIO;
|
||||
list_add_tail(&sreq->next, &s->free_list);
|
||||
fprintf(stderr, "error reading sector 0x%"PRIx64"\n", sreq->sec);
|
||||
}
|
||||
|
||||
if (tapdisk_stream_process_data()) {
|
||||
fprintf(stderr, "mismatch at sector 0x%"PRIx64"\n",
|
||||
sreq->sec);
|
||||
stream1.err = EINVAL;
|
||||
stream2.err = EINVAL;
|
||||
}
|
||||
|
||||
tapdisk_stream_poll_set(&stream1.poll);
|
||||
tapdisk_stream_poll_set(&stream2.poll);
|
||||
}
|
||||
|
||||
static inline int
|
||||
tapdisk_stream_enqueue_copy(struct tapdisk_stream *s,
|
||||
struct tapdisk_stream_request *r)
|
||||
{
|
||||
td_vbd_t *vbd;
|
||||
blkif_request_t *breq;
|
||||
td_vbd_request_t *vreq;
|
||||
struct tapdisk_stream_request *sreq;
|
||||
int idx;
|
||||
|
||||
vbd = stream2.vbd;
|
||||
sreq = tapdisk_stream_get_request(s);
|
||||
if (!sreq)
|
||||
return 1;
|
||||
|
||||
idx = tapdisk_stream_request_idx(s, sreq);
|
||||
|
||||
sreq->sec = r->sec;
|
||||
sreq->secs = r->secs;
|
||||
sreq->seqno = r->seqno;
|
||||
|
||||
breq = &sreq->blkif_req;
|
||||
breq->id = idx;
|
||||
breq->nr_segments = r->blkif_req.nr_segments;
|
||||
breq->sector_number = r->blkif_req.sector_number;
|
||||
breq->operation = BLKIF_OP_READ;
|
||||
|
||||
for (int i = 0; i < r->blkif_req.nr_segments; i++) {
|
||||
struct blkif_request_segment *seg = breq->seg + i;
|
||||
seg->first_sect = r->blkif_req.seg[i].first_sect;
|
||||
seg->last_sect = r->blkif_req.seg[i].last_sect;
|
||||
}
|
||||
s->cur += sreq->secs;
|
||||
|
||||
vreq = vbd->request_list + idx;
|
||||
assert(list_empty(&vreq->next));
|
||||
assert(vreq->secs_pending == 0);
|
||||
|
||||
memcpy(&vreq->req, breq, sizeof(*breq));
|
||||
vbd->received++;
|
||||
vreq->vbd = vbd;
|
||||
|
||||
tapdisk_vbd_move_request(vreq, &vbd->new_requests);
|
||||
list_add_tail(&sreq->next, &s->pending_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_stream_enqueue1(void)
|
||||
{
|
||||
td_vbd_t *vbd;
|
||||
int i, idx, psize, blk;
|
||||
struct tapdisk_stream *s = &stream1;
|
||||
|
||||
vbd = s->vbd;
|
||||
psize = getpagesize();
|
||||
|
||||
while (s->cur < s->end && !s->err) {
|
||||
blkif_request_t *breq;
|
||||
td_vbd_request_t *vreq;
|
||||
struct tapdisk_stream_request *sreq;
|
||||
|
||||
/* skip any blocks that are not present in this image */
|
||||
blk = s->cur >> SPB_SHIFT;
|
||||
while (s->cur < s->end && vhd1.bat.bat[blk] == DD_BLK_UNUSED) {
|
||||
//printf("skipping block %d\n", blk);
|
||||
blk++;
|
||||
s->cur = blk << SPB_SHIFT;
|
||||
}
|
||||
|
||||
if (s->cur >= s->end)
|
||||
break;
|
||||
|
||||
sreq = tapdisk_stream_get_request(s);
|
||||
if (!sreq)
|
||||
break;
|
||||
|
||||
idx = tapdisk_stream_request_idx(s, sreq);
|
||||
|
||||
sreq->sec = s->cur;
|
||||
sreq->secs = 0;
|
||||
sreq->seqno = s->started++;
|
||||
|
||||
breq = &sreq->blkif_req;
|
||||
breq->id = idx;
|
||||
breq->nr_segments = 0;
|
||||
breq->sector_number = sreq->sec;
|
||||
breq->operation = BLKIF_OP_READ;
|
||||
|
||||
for (i = 0; i < BLKIF_MAX_SEGMENTS_PER_REQUEST; i++) {
|
||||
uint32_t secs;
|
||||
struct blkif_request_segment *seg = breq->seg + i;
|
||||
|
||||
secs = MIN(s->end - s->cur, psize >> SECTOR_SHIFT);
|
||||
secs = MIN(((blk + 1) << SPB_SHIFT) - s->cur, secs);
|
||||
if (!secs)
|
||||
break;
|
||||
|
||||
sreq->secs += secs;
|
||||
s->cur += secs;
|
||||
|
||||
seg->first_sect = 0;
|
||||
seg->last_sect = secs - 1;
|
||||
breq->nr_segments++;
|
||||
}
|
||||
|
||||
vreq = vbd->request_list + idx;
|
||||
|
||||
assert(list_empty(&vreq->next));
|
||||
assert(vreq->secs_pending == 0);
|
||||
|
||||
memcpy(&vreq->req, breq, sizeof(*breq));
|
||||
vbd->received++;
|
||||
vreq->vbd = vbd;
|
||||
|
||||
tapdisk_vbd_move_request(vreq, &vbd->new_requests);
|
||||
list_add_tail(&sreq->next, &s->pending_list);
|
||||
}
|
||||
|
||||
tapdisk_vbd_issue_requests(vbd);
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_stream_enqueue2(void)
|
||||
{
|
||||
td_vbd_t *vbd;
|
||||
int i, blk;
|
||||
struct tapdisk_stream_request *itr;
|
||||
struct tapdisk_stream *s = &stream2;
|
||||
|
||||
vbd = s->vbd;
|
||||
|
||||
/* issue the same requests that we issued on stream1 */
|
||||
list_for_each_entry(itr, &stream1.completed_list, next) {
|
||||
if (itr->sec < s->cur)
|
||||
continue;
|
||||
if (tapdisk_stream_enqueue_copy(s, itr))
|
||||
goto done;
|
||||
}
|
||||
|
||||
list_for_each_entry(itr, &stream1.pending_list, next) {
|
||||
if (itr->sec < s->cur)
|
||||
continue;
|
||||
if (tapdisk_stream_enqueue_copy(s, itr))
|
||||
goto done;
|
||||
}
|
||||
|
||||
stream2.cur = stream1.cur;
|
||||
|
||||
done:
|
||||
tapdisk_vbd_issue_requests(vbd);
|
||||
}
|
||||
|
||||
static inline int
|
||||
tapdisk_diff_done(void)
|
||||
{
|
||||
return (tapdisk_stream_stop(&stream1) && tapdisk_stream_stop(&stream2));
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_diff_stop(void)
|
||||
{
|
||||
tapdisk_stream_close_image(&stream1);
|
||||
tapdisk_stream_close_image(&stream2);
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_stream_enqueue(event_id_t id, char mode, void *arg)
|
||||
{
|
||||
struct tapdisk_stream *s = (struct tapdisk_stream *)arg;
|
||||
|
||||
tapdisk_stream_poll_clear(&s->poll);
|
||||
|
||||
if (tapdisk_diff_done()) {
|
||||
tapdisk_diff_stop();
|
||||
return;
|
||||
}
|
||||
|
||||
if (s == &stream1)
|
||||
tapdisk_stream_enqueue1();
|
||||
else if (s == &stream2)
|
||||
tapdisk_stream_enqueue2();
|
||||
else
|
||||
assert(0);
|
||||
|
||||
if (tapdisk_diff_done()) {
|
||||
// we have to check again for the case when stream1 had no
|
||||
// blocks at all
|
||||
tapdisk_diff_stop();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
tapdisk_stream_open_image(struct tapdisk_stream *s, const char *path, int type)
|
||||
{
|
||||
int err;
|
||||
image_t image;
|
||||
|
||||
s->id = tapdisk_stream_count++;
|
||||
|
||||
err = tapdisk_vbd_initialize(s->id);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
s->vbd = tapdisk_server_get_vbd(s->id);
|
||||
if (!s->vbd) {
|
||||
err = ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
tapdisk_vbd_set_callback(s->vbd, tapdisk_stream_dequeue, s);
|
||||
|
||||
err = tapdisk_vbd_open_vdi(s->vbd, path, type,
|
||||
TAPDISK_STORAGE_TYPE_DEFAULT,
|
||||
TD_OPEN_RDONLY);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
s->vbd->reopened = 1;
|
||||
|
||||
err = tapdisk_vbd_get_image_info(s->vbd, &image);
|
||||
if (err) {
|
||||
fprintf(stderr, "failed getting image size: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
s->start = 0;
|
||||
s->cur = s->start;
|
||||
s->end = image.size;
|
||||
|
||||
err = 0;
|
||||
|
||||
out:
|
||||
if (err)
|
||||
fprintf(stderr, "failed to open image %s: %d\n", path, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_stream_close_image(struct tapdisk_stream *s)
|
||||
{
|
||||
td_vbd_t *vbd;
|
||||
|
||||
vbd = tapdisk_server_get_vbd(s->id);
|
||||
if (vbd) {
|
||||
tapdisk_vbd_close_vdi(vbd);
|
||||
tapdisk_server_remove_vbd(vbd);
|
||||
free((void *)vbd->ring.vstart);
|
||||
free(vbd->name);
|
||||
free(vbd);
|
||||
s->vbd = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
tapdisk_stream_initialize_requests(struct tapdisk_stream *s)
|
||||
{
|
||||
size_t size;
|
||||
td_ring_t *ring;
|
||||
int err, i, psize;
|
||||
|
||||
ring = &s->vbd->ring;
|
||||
psize = getpagesize();
|
||||
size = psize * BLKTAP_MMAP_REGION_SIZE;
|
||||
|
||||
/* sneaky -- set up ring->vstart so tapdisk_vbd will use our buffers */
|
||||
err = posix_memalign((void **)&ring->vstart, psize, size);
|
||||
if (err) {
|
||||
fprintf(stderr, "failed to allocate buffers: %d\n", err);
|
||||
ring->vstart = 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_REQUESTS; i++) {
|
||||
struct tapdisk_stream_request *req = s->requests + i;
|
||||
tapdisk_stream_initialize_request(req);
|
||||
list_add_tail(&req->next, &s->free_list);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
tapdisk_stream_register_enqueue_event(struct tapdisk_stream *s)
|
||||
{
|
||||
int err;
|
||||
struct tapdisk_stream_poll *p = &s->poll;
|
||||
|
||||
err = tapdisk_stream_poll_open(p);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = tapdisk_server_register_event(SCHEDULER_POLL_READ_FD,
|
||||
p->pipe[POLL_READ], 0,
|
||||
tapdisk_stream_enqueue, s);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
s->enqueue_event_id = err;
|
||||
err = 0;
|
||||
|
||||
out:
|
||||
if (err)
|
||||
fprintf(stderr, "failed to register event: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_stream_unregister_enqueue_event(struct tapdisk_stream *s)
|
||||
{
|
||||
if (s->enqueue_event_id) {
|
||||
tapdisk_server_unregister_event(s->enqueue_event_id);
|
||||
s->enqueue_event_id = 0;
|
||||
}
|
||||
tapdisk_stream_poll_close(&s->poll);
|
||||
}
|
||||
|
||||
static inline void
|
||||
tapdisk_stream_initialize(struct tapdisk_stream *s)
|
||||
{
|
||||
memset(s, 0, sizeof(*s));
|
||||
INIT_LIST_HEAD(&s->free_list);
|
||||
INIT_LIST_HEAD(&s->pending_list);
|
||||
INIT_LIST_HEAD(&s->completed_list);
|
||||
}
|
||||
|
||||
static int
|
||||
tapdisk_stream_open(struct tapdisk_stream *s, const char *arg)
|
||||
{
|
||||
int err, type;
|
||||
const char *path;
|
||||
|
||||
type = tapdisk_disktype_parse_params(arg, &path);
|
||||
if (type < 0)
|
||||
return type;
|
||||
|
||||
tapdisk_stream_initialize(s);
|
||||
|
||||
err = tapdisk_stream_open_image(s, path, type);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = tapdisk_stream_initialize_requests(s);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = tapdisk_stream_register_enqueue_event(s);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
tapdisk_stream_enqueue(s->enqueue_event_id,
|
||||
SCHEDULER_POLL_READ_FD, s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_stream_release(struct tapdisk_stream *s)
|
||||
{
|
||||
tapdisk_stream_close_image(s);
|
||||
tapdisk_stream_unregister_enqueue_event(s);
|
||||
}
|
||||
|
||||
static int
|
||||
tapdisk_stream_run(struct tapdisk_stream *s)
|
||||
{
|
||||
tapdisk_stream_enqueue(s->enqueue_event_id, SCHEDULER_POLL_READ_FD, s);
|
||||
tapdisk_server_run();
|
||||
return s->err;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int c, err, type1;
|
||||
const char *arg1 = NULL, *arg2 = NULL;
|
||||
const disk_info_t *info;
|
||||
const char *path1;
|
||||
|
||||
err = 0;
|
||||
|
||||
program = basename(argv[0]);
|
||||
|
||||
while ((c = getopt(argc, argv, "n:m:h")) != -1) {
|
||||
switch (c) {
|
||||
case 'n':
|
||||
arg1 = optarg;
|
||||
break;
|
||||
case 'm':
|
||||
arg2 = optarg;
|
||||
break;
|
||||
case 'h':
|
||||
usage(stdout);
|
||||
return 0;
|
||||
default:
|
||||
goto fail_usage;
|
||||
}
|
||||
}
|
||||
|
||||
if (!arg1 || !arg2)
|
||||
goto fail_usage;
|
||||
|
||||
type1 = tapdisk_disktype_parse_params(arg1, &path1);
|
||||
if (type1 < 0)
|
||||
return type1;
|
||||
|
||||
if (type1 != DISK_TYPE_VHD) {
|
||||
printf("error: first VDI is not VHD\n");
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
err = open_vhd(path1, &vhd1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
tapdisk_start_logging("tapdisk-diff");
|
||||
|
||||
err = tapdisk_server_initialize();
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = tapdisk_stream_open(&stream1, arg1);
|
||||
if (err) {
|
||||
fprintf(stderr, "Failed to open %s: %s\n",
|
||||
arg1, strerror(-err));
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = tapdisk_stream_open(&stream2, arg2);
|
||||
if (err) {
|
||||
fprintf(stderr, "Failed to open %s: %s\n",
|
||||
arg2, strerror(-err));
|
||||
goto out1;
|
||||
}
|
||||
|
||||
if (stream1.end != stream2.end) {
|
||||
fprintf(stderr, "Image sizes differ: %"PRIu64" != %"PRIu64"\n",
|
||||
stream1.end, stream2.end);
|
||||
err = EINVAL;
|
||||
goto out2;
|
||||
}
|
||||
|
||||
tapdisk_server_run();
|
||||
|
||||
out2:
|
||||
tapdisk_stream_release(&stream2);
|
||||
out1:
|
||||
tapdisk_stream_release(&stream1);
|
||||
out:
|
||||
vhd_close(&vhd1);
|
||||
tapdisk_stop_logging();
|
||||
|
||||
return err ? : stream1.err;
|
||||
|
||||
fail_usage:
|
||||
usage(stderr);
|
||||
return 1;
|
||||
}
|
||||
@@ -1,200 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2007, 2010, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "tapdisk-disktype.h"
|
||||
#include "tapdisk-message.h"
|
||||
|
||||
#define ARRAY_SIZE(a) (sizeof (a) / sizeof *(a))
|
||||
|
||||
static const disk_info_t aio_disk = {
|
||||
"aio",
|
||||
"raw image (aio)",
|
||||
0,
|
||||
};
|
||||
|
||||
static const disk_info_t sync_disk = {
|
||||
"sync",
|
||||
"raw image (sync)",
|
||||
0,
|
||||
};
|
||||
|
||||
static const disk_info_t vmdk_disk = {
|
||||
"vmdk",
|
||||
"vmware image (vmdk)",
|
||||
1,
|
||||
};
|
||||
|
||||
static const disk_info_t vhdsync_disk = {
|
||||
"vhdsync",
|
||||
"virtual server image (vhd) - synchronous",
|
||||
1,
|
||||
};
|
||||
|
||||
static const disk_info_t vhd_disk = {
|
||||
"vhd",
|
||||
"virtual server image (vhd)",
|
||||
0,
|
||||
};
|
||||
|
||||
|
||||
static const disk_info_t ram_disk = {
|
||||
"ram",
|
||||
"ramdisk image (ram)",
|
||||
1,
|
||||
};
|
||||
|
||||
static const disk_info_t qcow_disk = {
|
||||
"qcow",
|
||||
"qcow disk (qcow)",
|
||||
0,
|
||||
};
|
||||
|
||||
static const disk_info_t block_cache_disk = {
|
||||
"bc",
|
||||
"block cache image (bc)",
|
||||
1,
|
||||
};
|
||||
|
||||
static const disk_info_t vhd_index_disk = {
|
||||
"vhdi",
|
||||
"vhd index image (vhdi)",
|
||||
1,
|
||||
};
|
||||
|
||||
static const disk_info_t log_disk = {
|
||||
"log",
|
||||
"write logger (log)",
|
||||
0,
|
||||
};
|
||||
|
||||
static const disk_info_t remus_disk = {
|
||||
"remus",
|
||||
"remus disk replicator (remus)",
|
||||
0,
|
||||
};
|
||||
|
||||
const disk_info_t *tapdisk_disk_types[] = {
|
||||
[DISK_TYPE_AIO] = &aio_disk,
|
||||
[DISK_TYPE_SYNC] = &sync_disk,
|
||||
[DISK_TYPE_VMDK] = &vmdk_disk,
|
||||
[DISK_TYPE_VHDSYNC] = &vhdsync_disk,
|
||||
[DISK_TYPE_VHD] = &vhd_disk,
|
||||
[DISK_TYPE_RAM] = &ram_disk,
|
||||
[DISK_TYPE_QCOW] = &qcow_disk,
|
||||
[DISK_TYPE_BLOCK_CACHE] = &block_cache_disk,
|
||||
[DISK_TYPE_LOG] = &log_disk,
|
||||
[DISK_TYPE_VINDEX] = &vhd_index_disk,
|
||||
[DISK_TYPE_REMUS] = &remus_disk,
|
||||
};
|
||||
|
||||
extern struct tap_disk tapdisk_aio;
|
||||
extern struct tap_disk tapdisk_vhdsync;
|
||||
extern struct tap_disk tapdisk_vhd;
|
||||
extern struct tap_disk tapdisk_ram;
|
||||
extern struct tap_disk tapdisk_qcow;
|
||||
extern struct tap_disk tapdisk_block_cache;
|
||||
extern struct tap_disk tapdisk_log;
|
||||
extern struct tap_disk tapdisk_remus;
|
||||
|
||||
const struct tap_disk *tapdisk_disk_drivers[ARRAY_SIZE(tapdisk_disk_types)] = {
|
||||
[DISK_TYPE_AIO] = &tapdisk_aio,
|
||||
[DISK_TYPE_VHD] = &tapdisk_vhd,
|
||||
[DISK_TYPE_RAM] = &tapdisk_ram,
|
||||
[DISK_TYPE_QCOW] = &tapdisk_qcow,
|
||||
[DISK_TYPE_BLOCK_CACHE] = &tapdisk_block_cache,
|
||||
[DISK_TYPE_LOG] = &tapdisk_log,
|
||||
[DISK_TYPE_REMUS] = &tapdisk_remus,
|
||||
};
|
||||
|
||||
int
|
||||
tapdisk_disktype_find(const char *name)
|
||||
{
|
||||
const disk_info_t *info;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tapdisk_disk_types); ++i) {
|
||||
info = tapdisk_disk_types[i];
|
||||
if (!info)
|
||||
continue;
|
||||
|
||||
if (strcmp(name, info->name))
|
||||
continue;
|
||||
|
||||
if (!tapdisk_disk_drivers[i])
|
||||
return -ENOSYS;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
int
|
||||
tapdisk_disktype_parse_params(const char *params, const char **_path)
|
||||
{
|
||||
char name[DISK_TYPE_NAME_MAX], *ptr;
|
||||
size_t len;
|
||||
int type;
|
||||
|
||||
ptr = strchr(params, ':');
|
||||
if (!ptr)
|
||||
return -EINVAL;
|
||||
|
||||
len = ptr - params;
|
||||
|
||||
if (len > sizeof(name) - 1)
|
||||
return -ENAMETOOLONG;
|
||||
|
||||
memset(name, 0, sizeof(name));
|
||||
strncpy(name, params, len);
|
||||
|
||||
type = tapdisk_disktype_find(name);
|
||||
|
||||
if (type >= 0)
|
||||
*_path = params + len + 1;
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
int
|
||||
tapdisk_parse_disk_type(const char *params, const char **_path, int *_type)
|
||||
{
|
||||
int type;
|
||||
|
||||
type = tapdisk_disktype_parse_params(params, _path);
|
||||
if (type < 0)
|
||||
return type;
|
||||
|
||||
*_type = type;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2007, 2010, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __DISKTYPES_H__
|
||||
#define __DISKTYPES_H__
|
||||
|
||||
#define DISK_TYPE_AIO 0
|
||||
#define DISK_TYPE_SYNC 1
|
||||
#define DISK_TYPE_VMDK 2
|
||||
#define DISK_TYPE_VHDSYNC 3
|
||||
#define DISK_TYPE_VHD 4
|
||||
#define DISK_TYPE_RAM 5
|
||||
#define DISK_TYPE_QCOW 6
|
||||
#define DISK_TYPE_BLOCK_CACHE 7
|
||||
#define DISK_TYPE_LOG 8
|
||||
#define DISK_TYPE_REMUS 9
|
||||
#define DISK_TYPE_VINDEX 10
|
||||
|
||||
#define DISK_TYPE_NAME_MAX 32
|
||||
|
||||
typedef struct disk_info {
|
||||
const char *name; /* driver name, e.g. 'aio' */
|
||||
char *desc; /* e.g. "raw image" */
|
||||
unsigned int flags;
|
||||
} disk_info_t;
|
||||
|
||||
extern const disk_info_t *tapdisk_disk_types[];
|
||||
extern const struct tap_disk *tapdisk_disk_drivers[];
|
||||
|
||||
/* one single controller for all instances of disk type */
|
||||
#define DISK_TYPE_SINGLE_CONTROLLER (1<<0)
|
||||
|
||||
int tapdisk_disktype_find(const char *name);
|
||||
int tapdisk_disktype_parse_params(const char *params, const char **_path);
|
||||
int tapdisk_parse_disk_type(const char *, const char **, int *);
|
||||
|
||||
#endif
|
||||
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "tapdisk-driver.h"
|
||||
#include "tapdisk-server.h"
|
||||
#include "tapdisk-disktype.h"
|
||||
|
||||
td_driver_t *
|
||||
tapdisk_driver_allocate(int type, char *name, td_flag_t flags, int storage)
|
||||
{
|
||||
int err;
|
||||
td_driver_t *driver;
|
||||
const struct tap_disk *ops;
|
||||
|
||||
ops = tapdisk_disk_drivers[type];
|
||||
if (!ops)
|
||||
return NULL;
|
||||
|
||||
driver = calloc(1, sizeof(td_driver_t));
|
||||
if (!driver)
|
||||
return NULL;
|
||||
|
||||
err = tapdisk_namedup(&driver->name, name);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
driver->ops = ops;
|
||||
driver->type = type;
|
||||
driver->storage = storage;
|
||||
driver->data = calloc(1, ops->private_data_size);
|
||||
if (!driver->data)
|
||||
goto fail;
|
||||
|
||||
if (td_flag_test(flags, TD_OPEN_RDONLY))
|
||||
td_flag_set(driver->state, TD_DRIVER_RDONLY);
|
||||
|
||||
return driver;
|
||||
|
||||
fail:
|
||||
free(driver->name);
|
||||
free(driver->data);
|
||||
free(driver);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
tapdisk_driver_free(td_driver_t *driver)
|
||||
{
|
||||
if (!driver)
|
||||
return;
|
||||
|
||||
if (driver->refcnt)
|
||||
return;
|
||||
|
||||
if (td_flag_test(driver->state, TD_DRIVER_OPEN))
|
||||
EPRINTF("freeing open driver %s (state 0x%08x)\n",
|
||||
driver->name, driver->state);
|
||||
|
||||
free(driver->name);
|
||||
free(driver->data);
|
||||
free(driver);
|
||||
}
|
||||
|
||||
void
|
||||
tapdisk_driver_queue_tiocb(td_driver_t *driver, struct tiocb *tiocb)
|
||||
{
|
||||
tapdisk_server_queue_tiocb(tiocb);
|
||||
}
|
||||
|
||||
void
|
||||
tapdisk_driver_debug(td_driver_t *driver)
|
||||
{
|
||||
if (driver->ops->td_debug)
|
||||
driver->ops->td_debug(driver);
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef _TAPDISK_DRIVER_H_
|
||||
#define _TAPDISK_DRIVER_H_
|
||||
|
||||
#include "tapdisk.h"
|
||||
#include "scheduler.h"
|
||||
#include "tapdisk-queue.h"
|
||||
|
||||
#define TD_DRIVER_OPEN 0x0001
|
||||
#define TD_DRIVER_RDONLY 0x0002
|
||||
|
||||
struct td_driver_handle {
|
||||
int type;
|
||||
char *name;
|
||||
|
||||
int storage;
|
||||
|
||||
int refcnt;
|
||||
td_flag_t state;
|
||||
|
||||
td_disk_info_t info;
|
||||
|
||||
void *data;
|
||||
const struct tap_disk *ops;
|
||||
|
||||
struct list_head next;
|
||||
};
|
||||
|
||||
td_driver_t *tapdisk_driver_allocate(int, char *, td_flag_t, int);
|
||||
void tapdisk_driver_free(td_driver_t *);
|
||||
|
||||
void tapdisk_driver_queue_tiocb(td_driver_t *, struct tiocb *);
|
||||
|
||||
void tapdisk_driver_debug(td_driver_t *);
|
||||
|
||||
#endif
|
||||
@@ -1,272 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <libaio.h>
|
||||
#include <syslog.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "tapdisk-log.h"
|
||||
#include "tapdisk-filter.h"
|
||||
|
||||
#define RSEED 7
|
||||
#define PRE_CHECK 0
|
||||
#define POST_CHECK 1
|
||||
|
||||
#define WRITE_INTEGRITY "buffer integrity failure after write"
|
||||
#define READ_INTEGRITY "disk integrity failure after read"
|
||||
|
||||
#define DBG(f, a...) tlog_write(TLOG_WARN, f, ##a)
|
||||
|
||||
/*
|
||||
* simulate IO errors by knocking request size to zero before
|
||||
* submitting and restoring original size before returning
|
||||
*/
|
||||
static inline void
|
||||
inject_fault(struct tfilter *filter, struct iocb *io)
|
||||
{
|
||||
struct fiocb *fio;
|
||||
|
||||
if (!filter->ffree)
|
||||
return;
|
||||
|
||||
fio = filter->flist[--filter->ffree];
|
||||
|
||||
fio->bytes = io->u.c.nbytes;
|
||||
fio->data = io->data;
|
||||
io->u.c.nbytes = 0;
|
||||
io->data = fio;
|
||||
}
|
||||
|
||||
static inline int
|
||||
fault_injected(struct tfilter *filter, struct iocb *io)
|
||||
{
|
||||
unsigned long iop = (unsigned long)io->data;
|
||||
unsigned long start = (unsigned long)filter->fiocbs;
|
||||
unsigned long end = start + (filter->iocbs * sizeof(struct fiocb));
|
||||
|
||||
return (iop >= start && iop < end);
|
||||
}
|
||||
|
||||
static inline void
|
||||
recover_fault(struct tfilter *filter, struct iocb *io)
|
||||
{
|
||||
struct fiocb *fio = (struct fiocb *)io->data;
|
||||
|
||||
io->u.c.nbytes = fio->bytes;
|
||||
io->data = fio->data;
|
||||
|
||||
memset(fio, 0, sizeof(struct fiocb));
|
||||
filter->flist[filter->ffree++] = fio;
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
chksum(char *buf)
|
||||
{
|
||||
int i, num = 512 >> 3;
|
||||
uint64_t *p = (uint64_t *)buf;
|
||||
uint64_t sum = 0;
|
||||
|
||||
for (i = 0; i < num; i++)
|
||||
sum += p[i];
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
static inline void
|
||||
check_hash(struct tfilter *filter, uint64_t sec, char *buf, char *type)
|
||||
{
|
||||
uint64_t sum;
|
||||
struct dhash *hash;
|
||||
|
||||
hash = filter->dhash + sec;
|
||||
if (!hash->time.tv_sec)
|
||||
return;
|
||||
|
||||
sum = chksum(buf);
|
||||
if (hash->hash != chksum(buf)) {
|
||||
struct timeval now;
|
||||
gettimeofday(&now, NULL);
|
||||
DBG("%s: hash table: 0x%020" PRIx64 " at %012lu.%06llu, "
|
||||
"from disk: 0x%020" PRIx64 " at %012lu.%06llu\n",
|
||||
type, hash->hash, hash->time.tv_sec,
|
||||
(unsigned long long)hash->time.tv_usec, sum,
|
||||
now.tv_sec, (unsigned long long)now.tv_usec);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
insert_hash(struct tfilter *filter, uint64_t sec, char *buf)
|
||||
{
|
||||
struct dhash *hash;
|
||||
|
||||
hash = filter->dhash + sec;
|
||||
hash->hash = chksum(buf);
|
||||
gettimeofday(&hash->time, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
check_sector(struct tfilter *filter, int type, int rw, uint64_t sec, char *buf)
|
||||
{
|
||||
struct dhash *hash;
|
||||
|
||||
if (sec >= filter->secs)
|
||||
return;
|
||||
|
||||
hash = filter->dhash + sec;
|
||||
|
||||
if (rw) {
|
||||
if (type == PRE_CHECK)
|
||||
insert_hash(filter, sec, buf);
|
||||
else
|
||||
check_hash(filter, sec, buf, WRITE_INTEGRITY);
|
||||
} else if (type == POST_CHECK) {
|
||||
check_hash(filter, sec, buf, READ_INTEGRITY);
|
||||
insert_hash(filter, sec, buf);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
check_data(struct tfilter *filter, int type, struct iocb *io)
|
||||
{
|
||||
int rw;
|
||||
uint64_t i, sec;
|
||||
|
||||
rw = (io->aio_lio_opcode == IO_CMD_PWRITE);
|
||||
|
||||
for (i = 0; i < io->u.c.nbytes; i += 512) {
|
||||
char *buf = io->u.c.buf + i;
|
||||
uint64_t sec = (io->u.c.offset + i) >> 9;
|
||||
check_sector(filter, type, rw, sec, buf);
|
||||
}
|
||||
}
|
||||
|
||||
struct tfilter *
|
||||
tapdisk_init_tfilter(int mode, int iocbs, uint64_t secs)
|
||||
{
|
||||
int i;
|
||||
struct tfilter *filter = NULL;
|
||||
|
||||
if (!mode)
|
||||
return NULL;
|
||||
|
||||
filter = calloc(1, sizeof(struct tfilter));
|
||||
if (!filter)
|
||||
goto fail;
|
||||
|
||||
filter->mode = mode;
|
||||
filter->secs = secs;
|
||||
filter->iocbs = iocbs;
|
||||
|
||||
if (filter->mode & TD_INJECT_FAULTS) {
|
||||
filter->fiocbs = calloc(iocbs, sizeof(struct fiocb));
|
||||
filter->flist = calloc(iocbs, sizeof(struct fiocb *));
|
||||
if (!filter->fiocbs || !filter->flist)
|
||||
filter->mode &= ~TD_INJECT_FAULTS;
|
||||
else {
|
||||
srand(RSEED);
|
||||
filter->ffree = iocbs;
|
||||
for (i = 0; i < iocbs; i++)
|
||||
filter->flist[i] = filter->fiocbs + i;
|
||||
}
|
||||
}
|
||||
|
||||
if (filter->mode & TD_CHECK_INTEGRITY) {
|
||||
filter->dhash = calloc(secs, sizeof(struct dhash));
|
||||
if (!filter->dhash)
|
||||
filter->mode &= ~TD_CHECK_INTEGRITY;
|
||||
}
|
||||
|
||||
syslog(LOG_WARNING, "WARNING: "
|
||||
"FILTERING IN MODE 0x%04x\n", filter->mode);
|
||||
|
||||
return filter;
|
||||
|
||||
fail:
|
||||
tapdisk_free_tfilter(filter);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
tapdisk_free_tfilter(struct tfilter *filter)
|
||||
{
|
||||
if (!filter)
|
||||
return;
|
||||
|
||||
free(filter->dhash);
|
||||
free(filter->flist);
|
||||
free(filter->fiocbs);
|
||||
free(filter);
|
||||
}
|
||||
|
||||
void
|
||||
tapdisk_filter_iocbs(struct tfilter *filter, struct iocb **iocbs, int num)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!filter)
|
||||
return;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
struct iocb *io = iocbs[i];
|
||||
|
||||
if (filter->mode & TD_INJECT_FAULTS) {
|
||||
if ((random() % 100) <= TD_FAULT_RATE) {
|
||||
inject_fault(filter, io);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (filter->mode & TD_CHECK_INTEGRITY)
|
||||
check_data(filter, PRE_CHECK, io);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
tapdisk_filter_events(struct tfilter *filter, struct io_event *events, int num)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!filter)
|
||||
return;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
struct iocb *io = events[i].obj;
|
||||
|
||||
if (filter->mode & TD_INJECT_FAULTS) {
|
||||
if (fault_injected(filter, io)) {
|
||||
recover_fault(filter, io);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (filter->mode & TD_CHECK_INTEGRITY)
|
||||
check_data(filter, POST_CHECK, io);
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef TAPDISK_FILTER_H
|
||||
#define TAPDISK_FILTER_H
|
||||
|
||||
#include <libaio.h>
|
||||
#include <inttypes.h>
|
||||
#include <time.h>
|
||||
|
||||
#define TD_INJECT_FAULTS 0x00001 /* simulate random IO failures */
|
||||
#define TD_CHECK_INTEGRITY 0x00002 /* check data integrity */
|
||||
|
||||
#define TD_FAULT_RATE 5
|
||||
|
||||
struct dhash {
|
||||
uint64_t hash;
|
||||
struct timeval time;
|
||||
};
|
||||
|
||||
struct fiocb {
|
||||
size_t bytes;
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct tfilter {
|
||||
int mode;
|
||||
uint64_t secs;
|
||||
int iocbs;
|
||||
|
||||
struct dhash *dhash;
|
||||
|
||||
int ffree;
|
||||
struct fiocb *fiocbs;
|
||||
struct fiocb **flist;
|
||||
};
|
||||
|
||||
struct tfilter *tapdisk_init_tfilter(int mode, int iocbs, uint64_t secs);
|
||||
void tapdisk_free_tfilter(struct tfilter *);
|
||||
void tapdisk_filter_iocbs(struct tfilter *, struct iocb **, int);
|
||||
void tapdisk_filter_events(struct tfilter *, struct io_event *, int);
|
||||
|
||||
#endif
|
||||
@@ -1,169 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef MEMSHR
|
||||
#include <memshr.h>
|
||||
#endif
|
||||
|
||||
#include "tapdisk-image.h"
|
||||
#include "tapdisk-driver.h"
|
||||
#include "tapdisk-server.h"
|
||||
|
||||
#define ERR(_err, _f, _a...) tlog_error(_err, _f, ##_a)
|
||||
|
||||
td_image_t *
|
||||
tapdisk_image_allocate(const char *file, int type, int storage,
|
||||
td_flag_t flags, void *private)
|
||||
{
|
||||
int err;
|
||||
td_image_t *image;
|
||||
|
||||
image = calloc(1, sizeof(td_image_t));
|
||||
if (!image)
|
||||
return NULL;
|
||||
|
||||
err = tapdisk_namedup(&image->name, file);
|
||||
if (err) {
|
||||
free(image);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
image->type = type;
|
||||
image->flags = flags;
|
||||
image->storage = storage;
|
||||
image->private = private;
|
||||
#ifdef MEMSHR
|
||||
image->memshr_id = memshr_vbd_image_get(file);
|
||||
#endif
|
||||
INIT_LIST_HEAD(&image->next);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
void
|
||||
tapdisk_image_free(td_image_t *image)
|
||||
{
|
||||
if (!image)
|
||||
return;
|
||||
|
||||
list_del(&image->next);
|
||||
|
||||
#ifdef MEMSHR
|
||||
memshr_vbd_image_put(image->memshr_id);
|
||||
#endif
|
||||
free(image->name);
|
||||
tapdisk_driver_free(image->driver);
|
||||
free(image);
|
||||
}
|
||||
|
||||
int
|
||||
tapdisk_image_check_td_request(td_image_t *image, td_request_t treq)
|
||||
{
|
||||
int rdonly;
|
||||
td_driver_t *driver;
|
||||
td_disk_info_t *info;
|
||||
|
||||
driver = image->driver;
|
||||
if (!driver)
|
||||
return -ENODEV;
|
||||
|
||||
info = &driver->info;
|
||||
rdonly = td_flag_test(image->flags, TD_OPEN_RDONLY);
|
||||
|
||||
if (treq.op != TD_OP_READ && treq.op != TD_OP_WRITE)
|
||||
goto fail;
|
||||
|
||||
if (treq.op == TD_OP_WRITE && rdonly)
|
||||
goto fail;
|
||||
|
||||
if (treq.secs <= 0 || treq.sec + treq.secs > info->size)
|
||||
goto fail;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
ERR(-EINVAL, "bad td request on %s (%s, %"PRIu64"): %d at %"PRIu64,
|
||||
image->name, (rdonly ? "ro" : "rw"), info->size, treq.op,
|
||||
treq.sec + treq.secs);
|
||||
return -EINVAL;
|
||||
|
||||
}
|
||||
|
||||
int
|
||||
tapdisk_image_check_ring_request(td_image_t *image, blkif_request_t *req)
|
||||
{
|
||||
td_driver_t *driver;
|
||||
td_disk_info_t *info;
|
||||
int i, psize, rdonly;
|
||||
uint64_t nsects, total;
|
||||
|
||||
driver = image->driver;
|
||||
if (!driver)
|
||||
return -ENODEV;
|
||||
|
||||
nsects = 0;
|
||||
total = 0;
|
||||
info = &driver->info;
|
||||
|
||||
rdonly = td_flag_test(image->flags, TD_OPEN_RDONLY);
|
||||
|
||||
if (req->operation != BLKIF_OP_READ &&
|
||||
req->operation != BLKIF_OP_WRITE)
|
||||
goto fail;
|
||||
|
||||
if (req->operation == BLKIF_OP_WRITE && rdonly)
|
||||
goto fail;
|
||||
|
||||
if (!req->nr_segments || req->nr_segments > MAX_SEGMENTS_PER_REQ)
|
||||
goto fail;
|
||||
|
||||
total = 0;
|
||||
psize = getpagesize();
|
||||
|
||||
for (i = 0; i < req->nr_segments; i++) {
|
||||
nsects = req->seg[i].last_sect - req->seg[i].first_sect + 1;
|
||||
|
||||
if (req->seg[i].last_sect >= psize >> 9 || nsects <= 0)
|
||||
goto fail;
|
||||
|
||||
total += nsects;
|
||||
}
|
||||
|
||||
if (req->sector_number + nsects > info->size)
|
||||
goto fail;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
ERR(-EINVAL, "bad request on %s (%s, %"PRIu64"): id: %"PRIu64": %d at %"PRIu64,
|
||||
image->name, (rdonly ? "ro" : "rw"), info->size, req->id,
|
||||
req->operation, req->sector_number + total);
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef _TAPDISK_IMAGE_H_
|
||||
#define _TAPDISK_IMAGE_H_
|
||||
|
||||
#include "tapdisk.h"
|
||||
#include <xen/io/blkif.h>
|
||||
|
||||
struct td_image_handle {
|
||||
int type;
|
||||
char *name;
|
||||
uint16_t memshr_id;
|
||||
|
||||
td_flag_t flags;
|
||||
int storage;
|
||||
|
||||
td_driver_t *driver;
|
||||
td_disk_info_t info;
|
||||
|
||||
void *private;
|
||||
|
||||
struct list_head next;
|
||||
};
|
||||
|
||||
td_image_t *tapdisk_image_allocate(const char *, int, int, td_flag_t, void *);
|
||||
void tapdisk_image_free(td_image_t *);
|
||||
|
||||
int tapdisk_image_check_td_request(td_image_t *, td_request_t);
|
||||
int tapdisk_image_check_ring_request(td_image_t *, blkif_request_t *);
|
||||
|
||||
#endif
|
||||
@@ -1,259 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <errno.h>
|
||||
|
||||
#include "tapdisk.h"
|
||||
#include "tapdisk-vbd.h"
|
||||
#include "tapdisk-image.h"
|
||||
#include "tapdisk-driver.h"
|
||||
#include "tapdisk-server.h"
|
||||
#include "tapdisk-interface.h"
|
||||
|
||||
int
|
||||
td_load(td_image_t *image)
|
||||
{
|
||||
int err;
|
||||
td_image_t *shared;
|
||||
td_driver_t *driver;
|
||||
|
||||
shared = tapdisk_server_get_shared_image(image);
|
||||
if (!shared)
|
||||
return -ENODEV;
|
||||
|
||||
driver = shared->driver;
|
||||
if (!driver)
|
||||
return -EBADF;
|
||||
|
||||
driver->refcnt++;
|
||||
image->driver = driver;
|
||||
image->info = driver->info;
|
||||
|
||||
DPRINTF("loaded shared image %s (%d users, state: 0x%08x, type: %d)\n",
|
||||
driver->name, driver->refcnt, driver->state, driver->type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
__td_open(td_image_t *image, td_disk_info_t *info)
|
||||
{
|
||||
int err;
|
||||
td_driver_t *driver;
|
||||
|
||||
driver = image->driver;
|
||||
if (!driver) {
|
||||
driver = tapdisk_driver_allocate(image->type,
|
||||
image->name,
|
||||
image->flags,
|
||||
image->storage);
|
||||
if (!driver)
|
||||
return -ENOMEM;
|
||||
|
||||
if (info) /* pre-seed driver->info for virtual drivers */
|
||||
driver->info = *info;
|
||||
}
|
||||
|
||||
if (!td_flag_test(driver->state, TD_DRIVER_OPEN)) {
|
||||
err = driver->ops->td_open(driver, image->name, image->flags);
|
||||
if (err) {
|
||||
if (!image->driver)
|
||||
tapdisk_driver_free(driver);
|
||||
return err;
|
||||
}
|
||||
|
||||
td_flag_set(driver->state, TD_DRIVER_OPEN);
|
||||
DPRINTF("opened image %s (%d users, state: 0x%08x, type: %d)\n",
|
||||
driver->name, driver->refcnt + 1,
|
||||
driver->state, driver->type);
|
||||
}
|
||||
|
||||
image->driver = driver;
|
||||
image->info = driver->info;
|
||||
driver->refcnt++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
td_open(td_image_t *image)
|
||||
{
|
||||
return __td_open(image, NULL);
|
||||
}
|
||||
|
||||
int
|
||||
td_close(td_image_t *image)
|
||||
{
|
||||
td_driver_t *driver;
|
||||
|
||||
driver = image->driver;
|
||||
if (!driver)
|
||||
return -ENODEV;
|
||||
|
||||
driver->refcnt--;
|
||||
if (!driver->refcnt && td_flag_test(driver->state, TD_DRIVER_OPEN)) {
|
||||
driver->ops->td_close(driver);
|
||||
td_flag_clear(driver->state, TD_DRIVER_OPEN);
|
||||
}
|
||||
|
||||
DPRINTF("closed image %s (%d users, state: 0x%08x, type: %d)\n",
|
||||
driver->name, driver->refcnt, driver->state, driver->type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
td_get_parent_id(td_image_t *image, td_disk_id_t *id)
|
||||
{
|
||||
td_driver_t *driver;
|
||||
|
||||
driver = image->driver;
|
||||
if (!driver)
|
||||
return -ENODEV;
|
||||
|
||||
if (!td_flag_test(driver->state, TD_DRIVER_OPEN))
|
||||
return -EBADF;
|
||||
|
||||
return driver->ops->td_get_parent_id(driver, id);
|
||||
}
|
||||
|
||||
int
|
||||
td_validate_parent(td_image_t *image, td_image_t *parent)
|
||||
{
|
||||
td_driver_t *driver, *pdriver;
|
||||
|
||||
driver = image->driver;
|
||||
pdriver = parent->driver;
|
||||
if (!driver || !pdriver)
|
||||
return -ENODEV;
|
||||
|
||||
if (!td_flag_test(driver->state, TD_DRIVER_OPEN) ||
|
||||
!td_flag_test(pdriver->state, TD_DRIVER_OPEN))
|
||||
return -EBADF;
|
||||
|
||||
return 0;
|
||||
return driver->ops->td_validate_parent(driver, pdriver, 0);
|
||||
}
|
||||
|
||||
void
|
||||
td_queue_write(td_image_t *image, td_request_t treq)
|
||||
{
|
||||
int err;
|
||||
td_driver_t *driver;
|
||||
|
||||
driver = image->driver;
|
||||
if (!driver) {
|
||||
err = -ENODEV;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!td_flag_test(driver->state, TD_DRIVER_OPEN)) {
|
||||
err = -EBADF;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = tapdisk_image_check_td_request(image, treq);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
driver->ops->td_queue_write(driver, treq);
|
||||
return;
|
||||
|
||||
fail:
|
||||
td_complete_request(treq, err);
|
||||
}
|
||||
|
||||
void
|
||||
td_queue_read(td_image_t *image, td_request_t treq)
|
||||
{
|
||||
int err;
|
||||
td_driver_t *driver;
|
||||
|
||||
driver = image->driver;
|
||||
if (!driver) {
|
||||
err = -ENODEV;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!td_flag_test(driver->state, TD_DRIVER_OPEN)) {
|
||||
err = -EBADF;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = tapdisk_image_check_td_request(image, treq);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
driver->ops->td_queue_read(driver, treq);
|
||||
return;
|
||||
|
||||
fail:
|
||||
td_complete_request(treq, err);
|
||||
}
|
||||
|
||||
void
|
||||
td_forward_request(td_request_t treq)
|
||||
{
|
||||
tapdisk_vbd_forward_request(treq);
|
||||
}
|
||||
|
||||
void
|
||||
td_complete_request(td_request_t treq, int res)
|
||||
{
|
||||
((td_callback_t)treq.cb)(treq, res);
|
||||
}
|
||||
|
||||
void
|
||||
td_queue_tiocb(td_driver_t *driver, struct tiocb *tiocb)
|
||||
{
|
||||
tapdisk_driver_queue_tiocb(driver, tiocb);
|
||||
}
|
||||
|
||||
void
|
||||
td_prep_read(struct tiocb *tiocb, int fd, char *buf, size_t bytes,
|
||||
long long offset, td_queue_callback_t cb, void *arg)
|
||||
{
|
||||
tapdisk_prep_tiocb(tiocb, fd, 0, buf, bytes, offset, cb, arg);
|
||||
}
|
||||
|
||||
void
|
||||
td_prep_write(struct tiocb *tiocb, int fd, char *buf, size_t bytes,
|
||||
long long offset, td_queue_callback_t cb, void *arg)
|
||||
{
|
||||
tapdisk_prep_tiocb(tiocb, fd, 1, buf, bytes, offset, cb, arg);
|
||||
}
|
||||
|
||||
void
|
||||
td_debug(td_image_t *image)
|
||||
{
|
||||
td_driver_t *driver;
|
||||
|
||||
driver = image->driver;
|
||||
if (!driver || !td_flag_test(driver->state, TD_DRIVER_OPEN))
|
||||
|
||||
return;
|
||||
|
||||
tapdisk_driver_debug(driver);
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef _TAPDISK_INTERFACE_H_
|
||||
#define _TAPDISK_INTERFACE_H_
|
||||
|
||||
#include "tapdisk.h"
|
||||
#include "tapdisk-queue.h"
|
||||
|
||||
int td_open(td_image_t *);
|
||||
int __td_open(td_image_t *, td_disk_info_t *);
|
||||
int td_load(td_image_t *);
|
||||
int td_close(td_image_t *);
|
||||
int td_get_parent_id(td_image_t *, td_disk_id_t *);
|
||||
int td_validate_parent(td_image_t *, td_image_t *);
|
||||
|
||||
void td_queue_write(td_image_t *, td_request_t);
|
||||
void td_queue_read(td_image_t *, td_request_t);
|
||||
void td_forward_request(td_request_t);
|
||||
void td_complete_request(td_request_t, int);
|
||||
|
||||
void td_debug(td_image_t *);
|
||||
|
||||
void td_queue_tiocb(td_driver_t *, struct tiocb *);
|
||||
void td_prep_read(struct tiocb *, int, char *, size_t,
|
||||
long long, td_queue_callback_t, void *);
|
||||
void td_prep_write(struct tiocb *, int, char *, size_t,
|
||||
long long, td_queue_callback_t, void *);
|
||||
|
||||
#endif
|
||||
@@ -1,257 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <syslog.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "tapdisk-log.h"
|
||||
#include "tapdisk-utils.h"
|
||||
|
||||
#define MAX_ENTRY_LEN 512
|
||||
#define MAX_ERROR_MESSAGES 16
|
||||
|
||||
struct error {
|
||||
int cnt;
|
||||
int err;
|
||||
char *func;
|
||||
char msg[MAX_ENTRY_LEN];
|
||||
};
|
||||
|
||||
struct ehandle {
|
||||
int cnt;
|
||||
int dropped;
|
||||
struct error errors[MAX_ERROR_MESSAGES];
|
||||
};
|
||||
|
||||
struct tlog {
|
||||
char *p;
|
||||
int size;
|
||||
uint64_t cnt;
|
||||
char *buf;
|
||||
int level;
|
||||
char *file;
|
||||
int append;
|
||||
};
|
||||
|
||||
static struct ehandle tapdisk_err;
|
||||
static struct tlog tapdisk_log;
|
||||
|
||||
void
|
||||
open_tlog(char *file, size_t bytes, int level, int append)
|
||||
{
|
||||
tapdisk_log.size = ((bytes + 511) & (~511));
|
||||
|
||||
if (asprintf(&tapdisk_log.file, "%s.%d", file, getpid()) == -1)
|
||||
return;
|
||||
|
||||
if (posix_memalign((void **)&tapdisk_log.buf, 512, tapdisk_log.size)) {
|
||||
free(tapdisk_log.file);
|
||||
tapdisk_log.buf = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
memset(tapdisk_log.buf, 0, tapdisk_log.size);
|
||||
|
||||
tapdisk_log.p = tapdisk_log.buf;
|
||||
tapdisk_log.level = level;
|
||||
tapdisk_log.append = append;
|
||||
}
|
||||
|
||||
void
|
||||
close_tlog(void)
|
||||
{
|
||||
if (!tapdisk_log.buf)
|
||||
return;
|
||||
|
||||
if (tapdisk_log.append)
|
||||
tlog_flush();
|
||||
|
||||
free(tapdisk_log.buf);
|
||||
free(tapdisk_log.file);
|
||||
|
||||
memset(&tapdisk_log, 0, sizeof(struct tlog));
|
||||
}
|
||||
|
||||
void
|
||||
__tlog_write(int level, const char *func, const char *fmt, ...)
|
||||
{
|
||||
char *buf;
|
||||
va_list ap;
|
||||
struct timeval t;
|
||||
int ret, len, avail;
|
||||
|
||||
if (!tapdisk_log.buf)
|
||||
return;
|
||||
|
||||
if (level > tapdisk_log.level)
|
||||
return;
|
||||
|
||||
avail = tapdisk_log.size - (tapdisk_log.p - tapdisk_log.buf);
|
||||
if (avail < MAX_ENTRY_LEN) {
|
||||
if (tapdisk_log.append)
|
||||
tlog_flush();
|
||||
tapdisk_log.p = tapdisk_log.buf;
|
||||
}
|
||||
|
||||
buf = tapdisk_log.p;
|
||||
gettimeofday(&t, NULL);
|
||||
len = snprintf(buf, MAX_ENTRY_LEN - 1, "%08"PRIu64":%010ld.%06lld:"
|
||||
"%s ", tapdisk_log.cnt,
|
||||
t.tv_sec, (unsigned long long)t.tv_usec, func);
|
||||
|
||||
va_start(ap, fmt);
|
||||
ret = vsnprintf(buf + len, MAX_ENTRY_LEN - (len + 1), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
len = (ret < MAX_ENTRY_LEN - (len + 1) ?
|
||||
len + ret : MAX_ENTRY_LEN - 1);
|
||||
buf[len] = '\0';
|
||||
|
||||
tapdisk_log.cnt++;
|
||||
tapdisk_log.p += len;
|
||||
}
|
||||
|
||||
void
|
||||
__tlog_error(int err, const char *func, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int i, len, ret;
|
||||
struct error *e;
|
||||
struct timeval t;
|
||||
|
||||
err = (err > 0 ? err : -err);
|
||||
|
||||
for (i = 0; i < tapdisk_err.cnt; i++) {
|
||||
e = &tapdisk_err.errors[i];
|
||||
if (e->err == err && e->func == func) {
|
||||
e->cnt++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (tapdisk_err.cnt >= MAX_ERROR_MESSAGES) {
|
||||
tapdisk_err.dropped++;
|
||||
return;
|
||||
}
|
||||
|
||||
gettimeofday(&t, NULL);
|
||||
e = &tapdisk_err.errors[tapdisk_err.cnt];
|
||||
|
||||
len = snprintf(e->msg, MAX_ENTRY_LEN - 1, "%010ld.%06lld:%s ",
|
||||
t.tv_sec, (unsigned long long)t.tv_usec, func);
|
||||
|
||||
va_start(ap, fmt);
|
||||
ret = vsnprintf(e->msg + len, MAX_ENTRY_LEN - (len + 1), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
len = (ret < MAX_ENTRY_LEN - (len + 1) ?
|
||||
len + ret : MAX_ENTRY_LEN - 1);
|
||||
e->msg[len] = '\0';
|
||||
|
||||
e->cnt++;
|
||||
e->err = err;
|
||||
e->func = (char *)func;
|
||||
tapdisk_err.cnt++;
|
||||
}
|
||||
|
||||
void
|
||||
tlog_print_errors(void)
|
||||
{
|
||||
int i;
|
||||
struct error *e;
|
||||
|
||||
for (i = 0; i < tapdisk_err.cnt; i++) {
|
||||
e = &tapdisk_err.errors[i];
|
||||
syslog(LOG_INFO, "TAPDISK ERROR: errno %d at %s (cnt = %d): "
|
||||
"%s\n", e->err, e->func, e->cnt, e->msg);
|
||||
}
|
||||
|
||||
if (tapdisk_err.dropped)
|
||||
syslog(LOG_INFO, "TAPDISK ERROR: %d other error messages "
|
||||
"dropped\n", tapdisk_err.dropped);
|
||||
}
|
||||
|
||||
void
|
||||
tlog_flush_errors(void)
|
||||
{
|
||||
int i;
|
||||
struct error *e;
|
||||
|
||||
for (i = 0; i < tapdisk_err.cnt; i++) {
|
||||
e = &tapdisk_err.errors[i];
|
||||
tlog_write(TLOG_WARN, "TAPDISK ERROR: errno %d at %s "
|
||||
"(cnt = %d): %s\n", e->err, e->func, e->cnt,
|
||||
e->msg);
|
||||
}
|
||||
|
||||
if (tapdisk_err.dropped)
|
||||
tlog_write(TLOG_WARN, "TAPDISK ERROR: %d other error messages "
|
||||
"dropped\n", tapdisk_err.dropped);
|
||||
}
|
||||
|
||||
void
|
||||
tlog_flush(void)
|
||||
{
|
||||
int fd, flags;
|
||||
size_t size, wsize;
|
||||
|
||||
if (!tapdisk_log.buf)
|
||||
return;
|
||||
|
||||
flags = O_CREAT | O_WRONLY | O_DIRECT | O_NONBLOCK;
|
||||
if (!tapdisk_log.append)
|
||||
flags |= O_TRUNC;
|
||||
|
||||
fd = open(tapdisk_log.file, flags, 0644);
|
||||
if (fd == -1)
|
||||
return;
|
||||
|
||||
if (tapdisk_log.append)
|
||||
if (lseek(fd, 0, SEEK_END) == (off_t)-1)
|
||||
goto out;
|
||||
|
||||
tlog_flush_errors();
|
||||
|
||||
size = tapdisk_log.p - tapdisk_log.buf;
|
||||
wsize = ((size + 511) & (~511));
|
||||
|
||||
memset(tapdisk_log.buf + size, '\n', wsize - size);
|
||||
write_exact(fd, tapdisk_log.buf, wsize);
|
||||
|
||||
tapdisk_log.p = tapdisk_log.buf;
|
||||
|
||||
out:
|
||||
close(fd);
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef _TAPDISK_LOG_H_
|
||||
#define _TAPDISK_LOG_H_
|
||||
|
||||
#define TLOG_WARN 0
|
||||
#define TLOG_INFO 1
|
||||
#define TLOG_DBG 2
|
||||
|
||||
void open_tlog(char *file, size_t bytes, int level, int append);
|
||||
void close_tlog(void);
|
||||
void tlog_flush(void);
|
||||
void tlog_print_errors(void);
|
||||
|
||||
void __tlog_write(int level, const char *func, const char *fmt, ...)
|
||||
__attribute__((format(printf, 3, 4)));
|
||||
void __tlog_error(int err, const char *func, const char *fmt, ...)
|
||||
__attribute__((format(printf, 3, 4)));
|
||||
|
||||
#define tlog_write(_level, _f, _a...) \
|
||||
__tlog_write(_level, __func__, _f, ##_a)
|
||||
|
||||
#define tlog_error(_err, _f, _a...) \
|
||||
__tlog_error(_err, __func__, _f, ##_a)
|
||||
|
||||
#endif
|
||||
@@ -1,743 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <libaio.h>
|
||||
#ifdef __linux__
|
||||
#include <linux/version.h>
|
||||
#endif
|
||||
|
||||
#include "tapdisk.h"
|
||||
#include "tapdisk-log.h"
|
||||
#include "tapdisk-queue.h"
|
||||
#include "tapdisk-filter.h"
|
||||
#include "tapdisk-server.h"
|
||||
#include "tapdisk-utils.h"
|
||||
|
||||
#include "libaio-compat.h"
|
||||
#include "atomicio.h"
|
||||
|
||||
#define WARN(_f, _a...) tlog_write(TLOG_WARN, _f, ##_a)
|
||||
#define DBG(_f, _a...) tlog_write(TLOG_DBG, _f, ##_a)
|
||||
#define ERR(_err, _f, _a...) tlog_error(_err, _f, ##_a)
|
||||
|
||||
/*
|
||||
* We used a kernel patch to return an fd associated with the AIO context
|
||||
* so that we can concurrently poll on synchronous and async descriptors.
|
||||
* This is signalled by passing 1 as the io context to io_setup.
|
||||
*/
|
||||
#define REQUEST_ASYNC_FD ((io_context_t)1)
|
||||
|
||||
static inline void
|
||||
queue_tiocb(struct tqueue *queue, struct tiocb *tiocb)
|
||||
{
|
||||
struct iocb *iocb = &tiocb->iocb;
|
||||
|
||||
if (queue->queued) {
|
||||
struct tiocb *prev = (struct tiocb *)
|
||||
queue->iocbs[queue->queued - 1]->data;
|
||||
prev->next = tiocb;
|
||||
}
|
||||
|
||||
queue->iocbs[queue->queued++] = iocb;
|
||||
}
|
||||
|
||||
static inline int
|
||||
deferred_tiocbs(struct tqueue *queue)
|
||||
{
|
||||
return (queue->deferred.head != NULL);
|
||||
}
|
||||
|
||||
static inline void
|
||||
defer_tiocb(struct tqueue *queue, struct tiocb *tiocb)
|
||||
{
|
||||
struct tlist *list = &queue->deferred;
|
||||
|
||||
if (!list->head)
|
||||
list->head = list->tail = tiocb;
|
||||
else
|
||||
list->tail = list->tail->next = tiocb;
|
||||
|
||||
queue->tiocbs_deferred++;
|
||||
queue->deferrals++;
|
||||
}
|
||||
|
||||
static inline void
|
||||
queue_deferred_tiocb(struct tqueue *queue)
|
||||
{
|
||||
struct tlist *list = &queue->deferred;
|
||||
|
||||
if (list->head) {
|
||||
struct tiocb *tiocb = list->head;
|
||||
|
||||
list->head = tiocb->next;
|
||||
if (!list->head)
|
||||
list->tail = NULL;
|
||||
|
||||
queue_tiocb(queue, tiocb);
|
||||
queue->tiocbs_deferred--;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
queue_deferred_tiocbs(struct tqueue *queue)
|
||||
{
|
||||
while (!tapdisk_queue_full(queue) && deferred_tiocbs(queue))
|
||||
queue_deferred_tiocb(queue);
|
||||
}
|
||||
|
||||
/*
|
||||
* td_complete may queue more tiocbs
|
||||
*/
|
||||
static void
|
||||
complete_tiocb(struct tqueue *queue, struct tiocb *tiocb, unsigned long res)
|
||||
{
|
||||
int err;
|
||||
struct iocb *iocb = &tiocb->iocb;
|
||||
|
||||
if (res == iocb->u.c.nbytes)
|
||||
err = 0;
|
||||
else if ((int)res < 0)
|
||||
err = (int)res;
|
||||
else
|
||||
err = -EIO;
|
||||
|
||||
tiocb->cb(tiocb->arg, tiocb, err);
|
||||
}
|
||||
|
||||
static int
|
||||
cancel_tiocbs(struct tqueue *queue, int err)
|
||||
{
|
||||
int queued;
|
||||
struct tiocb *tiocb;
|
||||
|
||||
if (!queue->queued)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* td_complete may queue more tiocbs, which
|
||||
* will overwrite the contents of queue->iocbs.
|
||||
* use a private linked list to keep track
|
||||
* of the tiocbs we're cancelling.
|
||||
*/
|
||||
tiocb = queue->iocbs[0]->data;
|
||||
queued = queue->queued;
|
||||
queue->queued = 0;
|
||||
|
||||
for (; tiocb != NULL; tiocb = tiocb->next)
|
||||
complete_tiocb(queue, tiocb, err);
|
||||
|
||||
return queued;
|
||||
}
|
||||
|
||||
static int
|
||||
fail_tiocbs(struct tqueue *queue, int succeeded, int total, int err)
|
||||
{
|
||||
ERR(err, "io_submit error: %d of %d failed",
|
||||
total - succeeded, total);
|
||||
|
||||
/* take any non-submitted, merged iocbs
|
||||
* off of the queue, split them, and fail them */
|
||||
queue->queued = io_expand_iocbs(&queue->opioctx,
|
||||
queue->iocbs, succeeded, total);
|
||||
|
||||
return cancel_tiocbs(queue, err);
|
||||
}
|
||||
|
||||
/*
|
||||
* rwio
|
||||
*/
|
||||
|
||||
struct rwio {
|
||||
struct io_event *aio_events;
|
||||
};
|
||||
|
||||
static void
|
||||
tapdisk_rwio_destroy(struct tqueue *queue)
|
||||
{
|
||||
struct rwio *rwio = queue->tio_data;
|
||||
|
||||
if (rwio->aio_events) {
|
||||
free(rwio->aio_events);
|
||||
rwio->aio_events = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
tapdisk_rwio_setup(struct tqueue *queue, int size)
|
||||
{
|
||||
struct rwio *rwio = queue->tio_data;
|
||||
int err;
|
||||
|
||||
rwio->aio_events = calloc(size, sizeof(struct io_event));
|
||||
if (!rwio->aio_events)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline ssize_t
|
||||
tapdisk_rwio_rw(const struct iocb *iocb)
|
||||
{
|
||||
int fd = iocb->aio_fildes;
|
||||
char *buf = iocb->u.c.buf;
|
||||
long long off = iocb->u.c.offset;
|
||||
size_t size = iocb->u.c.nbytes;
|
||||
ssize_t (*func)(int, void *, size_t) =
|
||||
(iocb->aio_lio_opcode == IO_CMD_PWRITE ? vwrite : read);
|
||||
|
||||
if (lseek(fd, off, SEEK_SET) == (off_t)-1)
|
||||
return -errno;
|
||||
|
||||
if (atomicio(func, fd, buf, size) != size)
|
||||
return -errno;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static int
|
||||
tapdisk_rwio_submit(struct tqueue *queue)
|
||||
{
|
||||
struct rwio *rwio = queue->tio_data;
|
||||
int i, merged, split;
|
||||
struct iocb *iocb;
|
||||
struct tiocb *tiocb;
|
||||
struct io_event *ep;
|
||||
|
||||
if (!queue->queued)
|
||||
return 0;
|
||||
|
||||
tapdisk_filter_iocbs(queue->filter, queue->iocbs, queue->queued);
|
||||
merged = io_merge(&queue->opioctx, queue->iocbs, queue->queued);
|
||||
|
||||
queue->queued = 0;
|
||||
|
||||
for (i = 0; i < merged; i++) {
|
||||
ep = rwio->aio_events + i;
|
||||
iocb = queue->iocbs[i];
|
||||
ep->obj = iocb;
|
||||
ep->res = tapdisk_rwio_rw(iocb);
|
||||
}
|
||||
|
||||
split = io_split(&queue->opioctx, rwio->aio_events, merged);
|
||||
tapdisk_filter_events(queue->filter, rwio->aio_events, split);
|
||||
|
||||
for (i = split, ep = rwio->aio_events; i-- > 0; ep++) {
|
||||
iocb = ep->obj;
|
||||
tiocb = iocb->data;
|
||||
complete_tiocb(queue, tiocb, ep->res);
|
||||
}
|
||||
|
||||
queue_deferred_tiocbs(queue);
|
||||
|
||||
return split;
|
||||
}
|
||||
|
||||
static const struct tio td_tio_rwio = {
|
||||
.name = "rwio",
|
||||
.data_size = 0,
|
||||
.tio_setup = NULL,
|
||||
.tio_destroy = NULL,
|
||||
.tio_submit = tapdisk_rwio_submit
|
||||
};
|
||||
|
||||
/*
|
||||
* libaio
|
||||
*/
|
||||
|
||||
struct lio {
|
||||
io_context_t aio_ctx;
|
||||
struct io_event *aio_events;
|
||||
|
||||
int event_fd;
|
||||
int event_id;
|
||||
|
||||
int flags;
|
||||
};
|
||||
|
||||
#define LIO_FLAG_EVENTFD (1<<0)
|
||||
|
||||
static int
|
||||
tapdisk_lio_check_resfd(void)
|
||||
{
|
||||
#if defined(__linux__)
|
||||
return tapdisk_linux_version() >= KERNEL_VERSION(2, 6, 22);
|
||||
#else
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_lio_destroy_aio(struct tqueue *queue)
|
||||
{
|
||||
struct lio *lio = queue->tio_data;
|
||||
|
||||
if (lio->event_fd >= 0) {
|
||||
close(lio->event_fd);
|
||||
lio->event_fd = -1;
|
||||
}
|
||||
|
||||
if (lio->aio_ctx) {
|
||||
io_destroy(lio->aio_ctx);
|
||||
lio->aio_ctx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
__lio_setup_aio_poll(struct tqueue *queue, int qlen)
|
||||
{
|
||||
struct lio *lio = queue->tio_data;
|
||||
int err, fd;
|
||||
|
||||
lio->aio_ctx = REQUEST_ASYNC_FD;
|
||||
|
||||
fd = io_setup(qlen, &lio->aio_ctx);
|
||||
if (fd < 0) {
|
||||
lio->aio_ctx = 0;
|
||||
err = -errno;
|
||||
|
||||
if (err == -EINVAL)
|
||||
goto fail_fd;
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
lio->event_fd = fd;
|
||||
|
||||
return 0;
|
||||
|
||||
fail_fd:
|
||||
DPRINTF("Couldn't get fd for AIO poll support. This is probably "
|
||||
"because your kernel does not have the aio-poll patch "
|
||||
"applied.\n");
|
||||
fail:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
__lio_setup_aio_eventfd(struct tqueue *queue, int qlen)
|
||||
{
|
||||
struct lio *lio = queue->tio_data;
|
||||
int err;
|
||||
|
||||
err = io_setup(qlen, &lio->aio_ctx);
|
||||
if (err < 0) {
|
||||
lio->aio_ctx = 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
lio->event_fd = tapdisk_sys_eventfd(0);
|
||||
if (lio->event_fd < 0)
|
||||
return -errno;
|
||||
|
||||
lio->flags |= LIO_FLAG_EVENTFD;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
tapdisk_lio_setup_aio(struct tqueue *queue, int qlen)
|
||||
{
|
||||
struct lio *lio = queue->tio_data;
|
||||
int err;
|
||||
|
||||
lio->aio_ctx = 0;
|
||||
lio->event_fd = -1;
|
||||
|
||||
/*
|
||||
* prefer the mainline eventfd(2) api, if available.
|
||||
* if not, fall back to the poll fd patch.
|
||||
*/
|
||||
|
||||
err = !tapdisk_lio_check_resfd();
|
||||
if (!err)
|
||||
err = __lio_setup_aio_eventfd(queue, qlen);
|
||||
if (err)
|
||||
err = __lio_setup_aio_poll(queue, qlen);
|
||||
|
||||
if (err == -EAGAIN)
|
||||
goto fail_rsv;
|
||||
fail:
|
||||
return err;
|
||||
|
||||
fail_rsv:
|
||||
DPRINTF("Couldn't setup AIO context. If you are trying to "
|
||||
"concurrently use a large number of blktap-based disks, you may "
|
||||
"need to increase the system-wide aio request limit. "
|
||||
"(e.g. 'echo 1048576 > /proc/sys/fs/aio-max-nr')\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
tapdisk_lio_destroy(struct tqueue *queue)
|
||||
{
|
||||
struct lio *lio = queue->tio_data;
|
||||
|
||||
if (!lio)
|
||||
return;
|
||||
|
||||
if (lio->event_id >= 0) {
|
||||
tapdisk_server_unregister_event(lio->event_id);
|
||||
lio->event_id = -1;
|
||||
}
|
||||
|
||||
tapdisk_lio_destroy_aio(queue);
|
||||
|
||||
if (lio->aio_events) {
|
||||
free(lio->aio_events);
|
||||
lio->aio_events = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_lio_set_eventfd(struct tqueue *queue, int n, struct iocb **iocbs)
|
||||
{
|
||||
struct lio *lio = queue->tio_data;
|
||||
int i;
|
||||
|
||||
if (lio->flags & LIO_FLAG_EVENTFD)
|
||||
for (i = 0; i < n; ++i)
|
||||
__io_set_eventfd(iocbs[i], lio->event_fd);
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_lio_ack_event(struct tqueue *queue)
|
||||
{
|
||||
struct lio *lio = queue->tio_data;
|
||||
uint64_t val;
|
||||
|
||||
if (lio->flags & LIO_FLAG_EVENTFD)
|
||||
read_exact(lio->event_fd, &val, sizeof(val));
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_lio_event(event_id_t id, char mode, void *private)
|
||||
{
|
||||
struct tqueue *queue = private;
|
||||
struct lio *lio;
|
||||
int i, ret, split;
|
||||
struct iocb *iocb;
|
||||
struct tiocb *tiocb;
|
||||
struct io_event *ep;
|
||||
|
||||
tapdisk_lio_ack_event(queue);
|
||||
|
||||
lio = queue->tio_data;
|
||||
ret = io_getevents(lio->aio_ctx, 0,
|
||||
queue->size, lio->aio_events, NULL);
|
||||
split = io_split(&queue->opioctx, lio->aio_events, ret);
|
||||
tapdisk_filter_events(queue->filter, lio->aio_events, split);
|
||||
|
||||
DBG("events: %d, tiocbs: %d\n", ret, split);
|
||||
|
||||
queue->iocbs_pending -= ret;
|
||||
queue->tiocbs_pending -= split;
|
||||
|
||||
for (i = split, ep = lio->aio_events; i-- > 0; ep++) {
|
||||
iocb = ep->obj;
|
||||
tiocb = iocb->data;
|
||||
complete_tiocb(queue, tiocb, ep->res);
|
||||
}
|
||||
|
||||
queue_deferred_tiocbs(queue);
|
||||
}
|
||||
|
||||
static int
|
||||
tapdisk_lio_setup(struct tqueue *queue, int qlen)
|
||||
{
|
||||
struct lio *lio = queue->tio_data;
|
||||
size_t sz;
|
||||
int err;
|
||||
|
||||
lio->event_id = -1;
|
||||
|
||||
err = tapdisk_lio_setup_aio(queue, qlen);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
lio->event_id =
|
||||
tapdisk_server_register_event(SCHEDULER_POLL_READ_FD,
|
||||
lio->event_fd, 0,
|
||||
tapdisk_lio_event,
|
||||
queue);
|
||||
err = lio->event_id;
|
||||
if (err < 0)
|
||||
goto fail;
|
||||
|
||||
lio->aio_events = calloc(qlen, sizeof(struct io_event));
|
||||
if (!lio->aio_events) {
|
||||
err = -errno;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
tapdisk_lio_destroy(queue);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
tapdisk_lio_submit(struct tqueue *queue)
|
||||
{
|
||||
struct lio *lio = queue->tio_data;
|
||||
int merged, submitted, err = 0;
|
||||
|
||||
if (!queue->queued)
|
||||
return 0;
|
||||
|
||||
tapdisk_filter_iocbs(queue->filter, queue->iocbs, queue->queued);
|
||||
merged = io_merge(&queue->opioctx, queue->iocbs, queue->queued);
|
||||
tapdisk_lio_set_eventfd(queue, merged, queue->iocbs);
|
||||
submitted = io_submit(lio->aio_ctx, merged, queue->iocbs);
|
||||
|
||||
DBG("queued: %d, merged: %d, submitted: %d\n",
|
||||
queue->queued, merged, submitted);
|
||||
|
||||
if (submitted < 0) {
|
||||
err = submitted;
|
||||
submitted = 0;
|
||||
} else if (submitted < merged)
|
||||
err = -EIO;
|
||||
|
||||
queue->iocbs_pending += submitted;
|
||||
queue->tiocbs_pending += queue->queued;
|
||||
queue->queued = 0;
|
||||
|
||||
if (err)
|
||||
queue->tiocbs_pending -=
|
||||
fail_tiocbs(queue, submitted, merged, err);
|
||||
|
||||
return submitted;
|
||||
}
|
||||
|
||||
static const struct tio td_tio_lio = {
|
||||
.name = "lio",
|
||||
.data_size = sizeof(struct lio),
|
||||
.tio_setup = tapdisk_lio_setup,
|
||||
.tio_destroy = tapdisk_lio_destroy,
|
||||
.tio_submit = tapdisk_lio_submit,
|
||||
};
|
||||
|
||||
static void
|
||||
tapdisk_queue_free_io(struct tqueue *queue)
|
||||
{
|
||||
if (queue->tio) {
|
||||
if (queue->tio->tio_destroy)
|
||||
queue->tio->tio_destroy(queue);
|
||||
queue->tio = NULL;
|
||||
}
|
||||
|
||||
if (queue->tio_data) {
|
||||
free(queue->tio_data);
|
||||
queue->tio_data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
tapdisk_queue_init_io(struct tqueue *queue, int drv)
|
||||
{
|
||||
const struct tio *tio;
|
||||
int err;
|
||||
|
||||
switch (drv) {
|
||||
case TIO_DRV_LIO:
|
||||
tio = &td_tio_lio;
|
||||
break;
|
||||
case TIO_DRV_RWIO:
|
||||
tio = &td_tio_rwio;
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
queue->tio_data = calloc(1, tio->data_size);
|
||||
if (!queue->tio_data) {
|
||||
PERROR("malloc(%zu)", tio->data_size);
|
||||
err = -errno;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
queue->tio = tio;
|
||||
|
||||
if (tio->tio_setup) {
|
||||
err = tio->tio_setup(queue, queue->size);
|
||||
if (err)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
DPRINTF("I/O queue driver: %s\n", tio->name);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
tapdisk_queue_free_io(queue);
|
||||
return err;
|
||||
}
|
||||
|
||||
int
|
||||
tapdisk_init_queue(struct tqueue *queue, int size,
|
||||
int drv, struct tfilter *filter)
|
||||
{
|
||||
int i, err;
|
||||
|
||||
memset(queue, 0, sizeof(struct tqueue));
|
||||
|
||||
queue->size = size;
|
||||
queue->filter = filter;
|
||||
|
||||
if (!size)
|
||||
return 0;
|
||||
|
||||
err = tapdisk_queue_init_io(queue, drv);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
queue->iocbs = calloc(size, sizeof(struct iocb *));
|
||||
if (!queue->iocbs) {
|
||||
err = -errno;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = opio_init(&queue->opioctx, size);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
tapdisk_free_queue(queue);
|
||||
return err;
|
||||
}
|
||||
|
||||
void
|
||||
tapdisk_free_queue(struct tqueue *queue)
|
||||
{
|
||||
tapdisk_queue_free_io(queue);
|
||||
|
||||
free(queue->iocbs);
|
||||
queue->iocbs = NULL;
|
||||
|
||||
opio_free(&queue->opioctx);
|
||||
}
|
||||
|
||||
void
|
||||
tapdisk_debug_queue(struct tqueue *queue)
|
||||
{
|
||||
struct tiocb *tiocb = queue->deferred.head;
|
||||
|
||||
WARN("TAPDISK QUEUE:\n");
|
||||
WARN("size: %d, tio: %s, queued: %d, iocbs_pending: %d, "
|
||||
"tiocbs_pending: %d, tiocbs_deferred: %d, deferrals: %"PRIx64"\n",
|
||||
queue->size, queue->tio->name, queue->queued, queue->iocbs_pending,
|
||||
queue->tiocbs_pending, queue->tiocbs_deferred, queue->deferrals);
|
||||
|
||||
if (tiocb) {
|
||||
WARN("deferred:\n");
|
||||
for (; tiocb != NULL; tiocb = tiocb->next) {
|
||||
struct iocb *io = &tiocb->iocb;
|
||||
WARN("%s of %lu bytes at %lld\n",
|
||||
(io->aio_lio_opcode == IO_CMD_PWRITE ?
|
||||
"write" : "read"),
|
||||
io->u.c.nbytes, io->u.c.offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
tapdisk_prep_tiocb(struct tiocb *tiocb, int fd, int rw, char *buf, size_t size,
|
||||
long long offset, td_queue_callback_t cb, void *arg)
|
||||
{
|
||||
struct iocb *iocb = &tiocb->iocb;
|
||||
|
||||
if (rw)
|
||||
io_prep_pwrite(iocb, fd, buf, size, offset);
|
||||
else
|
||||
io_prep_pread(iocb, fd, buf, size, offset);
|
||||
|
||||
iocb->data = tiocb;
|
||||
tiocb->cb = cb;
|
||||
tiocb->arg = arg;
|
||||
tiocb->next = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
tapdisk_queue_tiocb(struct tqueue *queue, struct tiocb *tiocb)
|
||||
{
|
||||
if (!tapdisk_queue_full(queue))
|
||||
queue_tiocb(queue, tiocb);
|
||||
else
|
||||
defer_tiocb(queue, tiocb);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* fail_tiocbs may queue more tiocbs
|
||||
*/
|
||||
int
|
||||
tapdisk_submit_tiocbs(struct tqueue *queue)
|
||||
{
|
||||
return queue->tio->tio_submit(queue);
|
||||
}
|
||||
|
||||
int
|
||||
tapdisk_submit_all_tiocbs(struct tqueue *queue)
|
||||
{
|
||||
int submitted = 0;
|
||||
|
||||
do {
|
||||
submitted += tapdisk_submit_tiocbs(queue);
|
||||
} while (!tapdisk_queue_empty(queue));
|
||||
|
||||
return submitted;
|
||||
}
|
||||
|
||||
/*
|
||||
* cancel_tiocbs may queue more tiocbs
|
||||
*/
|
||||
int
|
||||
tapdisk_cancel_tiocbs(struct tqueue *queue)
|
||||
{
|
||||
return cancel_tiocbs(queue, -EIO);
|
||||
}
|
||||
|
||||
int
|
||||
tapdisk_cancel_all_tiocbs(struct tqueue *queue)
|
||||
{
|
||||
int cancelled = 0;
|
||||
|
||||
do {
|
||||
cancelled += tapdisk_cancel_tiocbs(queue);
|
||||
} while (!tapdisk_queue_empty(queue));
|
||||
|
||||
return cancelled;
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef TAPDISK_QUEUE_H
|
||||
#define TAPDISK_QUEUE_H
|
||||
|
||||
#include <libaio.h>
|
||||
|
||||
#include "io-optimize.h"
|
||||
#include "scheduler.h"
|
||||
|
||||
struct tiocb;
|
||||
struct tfilter;
|
||||
|
||||
typedef void (*td_queue_callback_t)(void *arg, struct tiocb *, int err);
|
||||
|
||||
|
||||
struct tiocb {
|
||||
td_queue_callback_t cb;
|
||||
void *arg;
|
||||
|
||||
struct iocb iocb;
|
||||
struct tiocb *next;
|
||||
};
|
||||
|
||||
struct tlist {
|
||||
struct tiocb *head;
|
||||
struct tiocb *tail;
|
||||
};
|
||||
|
||||
struct tqueue {
|
||||
int size;
|
||||
|
||||
const struct tio *tio;
|
||||
void *tio_data;
|
||||
|
||||
struct opioctx opioctx;
|
||||
|
||||
int queued;
|
||||
struct iocb **iocbs;
|
||||
|
||||
/* number of iocbs pending in the aio layer */
|
||||
int iocbs_pending;
|
||||
|
||||
/* number of tiocbs pending in the queue --
|
||||
* this is likely to be larger than iocbs_pending
|
||||
* due to request coalescing */
|
||||
int tiocbs_pending;
|
||||
|
||||
/* iocbs may be deferred if the aio ring is full.
|
||||
* tapdisk_queue_complete will ensure deferred
|
||||
* iocbs are queued as slots become available. */
|
||||
struct tlist deferred;
|
||||
int tiocbs_deferred;
|
||||
|
||||
/* optional tapdisk filter */
|
||||
struct tfilter *filter;
|
||||
|
||||
uint64_t deferrals;
|
||||
};
|
||||
|
||||
struct tio {
|
||||
const char *name;
|
||||
size_t data_size;
|
||||
|
||||
int (*tio_setup) (struct tqueue *queue, int qlen);
|
||||
void (*tio_destroy) (struct tqueue *queue);
|
||||
int (*tio_submit) (struct tqueue *queue);
|
||||
};
|
||||
|
||||
enum {
|
||||
TIO_DRV_LIO = 1,
|
||||
TIO_DRV_RWIO = 2,
|
||||
};
|
||||
|
||||
/*
|
||||
* Interface for request producer (i.e., tapdisk)
|
||||
* NB: the following functions may cause additional tiocbs to be queued:
|
||||
* - tapdisk_submit_tiocbs
|
||||
* - tapdisk_cancel_tiocbs
|
||||
* - tapdisk_complete_tiocbs
|
||||
* The *_all_tiocbs variants will handle the first two cases;
|
||||
* be sure to call submit after calling complete in the third case.
|
||||
*/
|
||||
#define tapdisk_queue_count(q) ((q)->queued)
|
||||
#define tapdisk_queue_empty(q) ((q)->queued == 0)
|
||||
#define tapdisk_queue_full(q) \
|
||||
(((q)->tiocbs_pending + (q)->queued) >= (q)->size)
|
||||
int tapdisk_init_queue(struct tqueue *, int size, int drv, struct tfilter *);
|
||||
void tapdisk_free_queue(struct tqueue *);
|
||||
void tapdisk_debug_queue(struct tqueue *);
|
||||
void tapdisk_queue_tiocb(struct tqueue *, struct tiocb *);
|
||||
int tapdisk_submit_tiocbs(struct tqueue *);
|
||||
int tapdisk_submit_all_tiocbs(struct tqueue *);
|
||||
int tapdisk_cancel_tiocbs(struct tqueue *);
|
||||
int tapdisk_cancel_all_tiocbs(struct tqueue *);
|
||||
void tapdisk_prep_tiocb(struct tiocb *, int, int, char *, size_t,
|
||||
long long, td_queue_callback_t, void *);
|
||||
|
||||
#endif
|
||||
@@ -1,439 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <errno.h>
|
||||
|
||||
#include "tapdisk-ring.h"
|
||||
|
||||
static int
|
||||
tapdisk_uring_create_ctlfd(td_uring_t *ring)
|
||||
{
|
||||
int fd, err;
|
||||
struct sockaddr_un saddr;
|
||||
|
||||
if (strnlen(ring->ctlfd_path, sizeof(saddr.sun_family)) >=
|
||||
sizeof(saddr.sun_family))
|
||||
return -ENAMETOOLONG;
|
||||
|
||||
fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (fd == -1)
|
||||
return -errno;
|
||||
|
||||
memset(&saddr, 0, sizeof(struct sockaddr_un));
|
||||
saddr.sun_family = AF_UNIX;
|
||||
memcpy(saddr.sun_path, ring->ctlfd_path, strlen(ring->ctlfd_path));
|
||||
|
||||
err = unlink(ring->ctlfd_path);
|
||||
if (err == -1 && errno != ENOENT) {
|
||||
err = -errno;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = bind(fd, &saddr, sizeof(struct sockaddr_un));
|
||||
if (err == -1) {
|
||||
err = -errno;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = listen(fd, 1);
|
||||
if (err == -1) {
|
||||
err = -errno;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ring->ctlfd = fd;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
close(fd);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_uring_destroy_ctlfd(td_uring_t *ring)
|
||||
{
|
||||
if (ring->ctlfd) {
|
||||
close(ring->ctlfd);
|
||||
ring->ctlfd = 0;
|
||||
}
|
||||
|
||||
if (ring->ctlfd_path) {
|
||||
unlink(ring->ctlfd_path);
|
||||
free(ring->ctlfd_path);
|
||||
ring->ctlfd_path = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
tapdisk_uring_connect_ctlfd(td_uring_t *ring)
|
||||
{
|
||||
int fd, err;
|
||||
struct sockaddr_un saddr;
|
||||
|
||||
if (strnlen(ring->ctlfd_path, sizeof(saddr.sun_path)) >=
|
||||
sizeof(saddr.sun_path))
|
||||
return -ENAMETOOLONG;
|
||||
|
||||
fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (fd == -1)
|
||||
return -errno;
|
||||
|
||||
memset(&saddr, 0, sizeof(struct sockaddr_un));
|
||||
saddr.sun_family = AF_UNIX;
|
||||
memcpy(saddr.sun_path, ring->ctlfd_path, strlen(ring->ctlfd_path));
|
||||
|
||||
err = connect(fd, &saddr, sizeof(saddr));
|
||||
if (err == -1) {
|
||||
err = -errno;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ring->ctlfd = fd;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
close(fd);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_uring_disconnect_ctlfd(td_uring_t *ring)
|
||||
{
|
||||
if (ring->ctlfd)
|
||||
close(ring->ctlfd);
|
||||
free(ring->ctlfd_path);
|
||||
ring->ctlfd_path = NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
tapdisk_uring_create_shmem(td_uring_t *ring)
|
||||
{
|
||||
int fd, err;
|
||||
|
||||
fd = shm_open(ring->shmem_path, O_CREAT | O_RDWR, 0750);
|
||||
if (fd == -1)
|
||||
return -errno;
|
||||
|
||||
err = ftruncate(fd, ring->shmem_size);
|
||||
if (err == -1) {
|
||||
err = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ring->shmem = mmap(NULL, ring->shmem_size,
|
||||
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (ring->shmem == MAP_FAILED) {
|
||||
ring->shmem = NULL;
|
||||
err = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
|
||||
out:
|
||||
close(fd);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_uring_destroy_shmem(td_uring_t *ring)
|
||||
{
|
||||
if (ring->shmem) {
|
||||
munmap(ring->shmem, ring->shmem_size);
|
||||
ring->shmem = NULL;
|
||||
}
|
||||
|
||||
if (ring->shmem_path) {
|
||||
shm_unlink(ring->shmem_path);
|
||||
free(ring->shmem_path);
|
||||
ring->shmem_path = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
tapdisk_uring_connect_shmem(td_uring_t *ring)
|
||||
{
|
||||
int fd, err;
|
||||
td_uring_header_t header, *p;
|
||||
|
||||
fd = shm_open(ring->shmem_path, O_RDWR);
|
||||
if (fd == -1)
|
||||
return -errno;
|
||||
|
||||
p = mmap(NULL, sizeof(td_uring_header_t),
|
||||
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (p == MAP_FAILED) {
|
||||
err = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(&header, p, sizeof(td_uring_header_t));
|
||||
munmap(p, sizeof(td_uring_header_t));
|
||||
|
||||
if (memcmp(header.cookie,
|
||||
TAPDISK_URING_COOKIE, sizeof(header.cookie))) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (header.version != TD_URING_CURRENT_VERSION) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ring->ring_size = header.ring_size;
|
||||
ring->data_size = header.data_size;
|
||||
ring->shmem_size = header.shmem_size;
|
||||
|
||||
ring->shmem = mmap(NULL, ring->shmem_size,
|
||||
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (ring->shmem == MAP_FAILED) {
|
||||
rint->shmem = NULL;
|
||||
err = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
|
||||
out:
|
||||
close(fd);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_uring_disconnect_shmem(td_uring_t *ring)
|
||||
{
|
||||
if (ring->shmem)
|
||||
munmap(ring->shmem, ring->shmem_size);
|
||||
free(ring->shmem_path);
|
||||
ring->shmem_path = NULL;
|
||||
}
|
||||
|
||||
int
|
||||
tapdisk_uring_create(td_uring_t *ring, const char *location,
|
||||
uint32_t ring_size, uint32_t data_size)
|
||||
{
|
||||
int fd, err;
|
||||
|
||||
memset(ring, 0, sizeof(td_uring_t));
|
||||
|
||||
ring->ring_size = ring_size;
|
||||
ring->data_size = data_size;
|
||||
ring->shmem_size = ring_size + data_size + sizeof(td_uring_header_t);
|
||||
|
||||
err = asprintf(&ring->shmem_path, "%s.shm", location);
|
||||
if (err == -1) {
|
||||
ring->shmem_path = NULL;
|
||||
err = -errno;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = asprintf(&ring->ctlfd_path, "%s.cfd", location);
|
||||
if (err == -1) {
|
||||
ring->ctlfd_path = NULL;
|
||||
err = -errno;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = tapdisk_uring_create_ctlfd(ring);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
err = tapdisk_uring_create_shmem(ring);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
ring->ring_area = (unsigned long)ring->shmem + sizeof(td_uring_header_t);
|
||||
ring->data_area = (unsigned long)ring->ring_area + ring->ring_size;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
tapdisk_uring_destroy(ring);
|
||||
return err;
|
||||
}
|
||||
|
||||
int
|
||||
tapdisk_uring_destroy(td_uring_t *ring)
|
||||
{
|
||||
tapdisk_uring_destroy_shmem(ring);
|
||||
tapdisk_uring_destroy_ctlfd(ring);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
tapdisk_uring_connect(td_uring_t *ring, const char *location)
|
||||
{
|
||||
int fd, err;
|
||||
|
||||
memset(ring, 0, sizeof(td_uring_t));
|
||||
|
||||
err = asprintf(&ring->shmem_path, "%s.shm", location);
|
||||
if (err == -1) {
|
||||
ring->shmem_path = NULL;
|
||||
err = -errno;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = asprintf(&ring->ctlfd_path, "%s.cfd", location);
|
||||
if (err == -1) {
|
||||
ring->ctlfd_path = NULL;
|
||||
err = -errno;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = tapdisk_uring_connect_ctlfd(ring);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
err = tapdisk_uring_connect_shmem(ring);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
err = 0;
|
||||
|
||||
fail:
|
||||
}
|
||||
|
||||
int
|
||||
tapdisk_uring_disconnect(td_uring_t *ring)
|
||||
{
|
||||
tapdisk_uring_disconnect_shmem(ring);
|
||||
tapdisk_uring_disconnect_ctlfd(ring);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
tapdisk_ring_read_message(int fd, td_uring_message_t *message, int timeout)
|
||||
{
|
||||
fd_set readfds;
|
||||
int ret, len, offset;
|
||||
struct timeval tv, *t;
|
||||
|
||||
t = NULL;
|
||||
offset = 0;
|
||||
len = sizeof(td_uring_message_t);
|
||||
|
||||
if (timeout) {
|
||||
tv.tv_sec = timeout;
|
||||
tv.tv_usec = 0;
|
||||
t = &tv;
|
||||
}
|
||||
|
||||
while (offset < len) {
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(fd, &readfds);
|
||||
|
||||
/* we don't bother reinitializing tv. at worst, it will wait a
|
||||
* bit more time than expected. */
|
||||
|
||||
ret = select(fd + 1, &readfds, NULL, NULL, t);
|
||||
if (ret == -1)
|
||||
break;
|
||||
else if (FD_ISSET(fd, &readfds)) {
|
||||
ret = read(fd, message + offset, len - offset);
|
||||
if (ret <= 0)
|
||||
break;
|
||||
offset += ret;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
if (offset != len)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
tapdisk_ring_write_message(int fd, td_uring_message_t *message, int timeout)
|
||||
{
|
||||
fd_set writefds;
|
||||
int ret, len, offset;
|
||||
struct timeval tv, *t;
|
||||
|
||||
t = NULL;
|
||||
offset = 0;
|
||||
len = sizeof(td_uring_message_t);
|
||||
|
||||
if (timeout) {
|
||||
tv.tv_sec = timeout;
|
||||
tv.tv_usec = 0;
|
||||
t = &tv;
|
||||
}
|
||||
|
||||
while (offset < len) {
|
||||
FD_ZERO(&writefds);
|
||||
FD_SET(fd, &writefds);
|
||||
|
||||
/* we don't bother reinitializing tv. at worst, it will wait a
|
||||
* bit more time than expected. */
|
||||
|
||||
ret = select(fd + 1, NULL, &writefds, NULL, t);
|
||||
if (ret == -1)
|
||||
break;
|
||||
else if (FD_ISSET(fd, &writefds)) {
|
||||
ret = write(fd, message + offset, len - offset);
|
||||
if (ret <= 0)
|
||||
break;
|
||||
offset += ret;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
if (offset != len)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
tapdisk_uring_poll(td_uring_t *ring)
|
||||
{
|
||||
int err;
|
||||
td_uring_message_t message;
|
||||
|
||||
err = tapdisk_uring_read_message(ring->ctlfd, &message, 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (message.type != TAPDISK_URING_MESSAGE_KICK)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
tapdisk_uring_kick(td_uring_t *ring)
|
||||
{
|
||||
td_uring_message_t message;
|
||||
|
||||
memset(&message, 0, sizeof(td_uring_message_t));
|
||||
message.type = TAPDISK_URING_MESSAGE_KICK;
|
||||
|
||||
return tapdisk_uring_write_message(ring->ctlfd, &message, 1);
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef _TAPDISK_RING_H_
|
||||
#define _TAPDISK_RING_H_
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <xenctrl.h>
|
||||
#include <xen/io/ring.h>
|
||||
|
||||
typedef struct td_uring td_uring_t;
|
||||
typedef struct td_uring_header td_uring_header_t;
|
||||
typedef struct td_uring_request td_uring_request_t;
|
||||
typedef struct td_uring_response td_uring_response_t;
|
||||
|
||||
struct td_uring {
|
||||
int ctlfd;
|
||||
|
||||
char *shmem_path;
|
||||
char *ctlfd_path;
|
||||
|
||||
void *shmem;
|
||||
void *ring_area;
|
||||
void *data_area;
|
||||
};
|
||||
|
||||
struct td_uring_header {
|
||||
char cookie[8];
|
||||
uint32_t version;
|
||||
uint32_t shmem_size;
|
||||
uint32_t ring_size;
|
||||
uint32_t data_size;
|
||||
char reserved[4064];
|
||||
};
|
||||
|
||||
struct td_uring_request {
|
||||
uint8_t op;
|
||||
uint64_t id;
|
||||
uint64_t sec;
|
||||
uint32_t secs;
|
||||
uint32_t offset;
|
||||
};
|
||||
|
||||
struct td_uring_response {
|
||||
uint8_t op;
|
||||
uint64_t id;
|
||||
uint8_t status;
|
||||
};
|
||||
|
||||
DEFINE_RING_TYPES(td_uring, td_uring_request_t, td_uring_response_t);
|
||||
|
||||
int tapdisk_uring_create(td_uring_t *, const char *location,
|
||||
uint32_t ring_size, uint32_t data_size);
|
||||
int tapdisk_uring_destroy(td_uring_t *);
|
||||
|
||||
int tapdisk_uring_connect(td_uring_t *, const char *location);
|
||||
int tapdisk_uring_disconnect(td_uring_t *);
|
||||
|
||||
int tapdisk_uring_poll(td_uring_t *);
|
||||
int tapdisk_uring_kick(td_uring_t *);
|
||||
|
||||
#endif
|
||||
@@ -1,345 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/signal.h>
|
||||
|
||||
#include "tapdisk-utils.h"
|
||||
#include "tapdisk-server.h"
|
||||
#include "tapdisk-driver.h"
|
||||
#include "tapdisk-interface.h"
|
||||
|
||||
#define DBG(_level, _f, _a...) tlog_write(_level, _f, ##_a)
|
||||
#define ERR(_err, _f, _a...) tlog_error(_err, _f, ##_a)
|
||||
|
||||
tapdisk_server_t server;
|
||||
|
||||
#define tapdisk_server_for_each_vbd(vbd, tmp) \
|
||||
list_for_each_entry_safe(vbd, tmp, &server.vbds, next)
|
||||
|
||||
td_image_t *
|
||||
tapdisk_server_get_shared_image(td_image_t *image)
|
||||
{
|
||||
td_vbd_t *vbd, *tmpv;
|
||||
td_image_t *img, *tmpi;
|
||||
|
||||
if (!td_flag_test(image->flags, TD_OPEN_SHAREABLE))
|
||||
return NULL;
|
||||
|
||||
tapdisk_server_for_each_vbd(vbd, tmpv)
|
||||
tapdisk_vbd_for_each_image(vbd, img, tmpi)
|
||||
if (img->type == image->type &&
|
||||
!strcmp(img->name, image->name))
|
||||
return img;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct list_head *
|
||||
tapdisk_server_get_all_vbds(void)
|
||||
{
|
||||
return &server.vbds;
|
||||
}
|
||||
|
||||
td_vbd_t *
|
||||
tapdisk_server_get_vbd(uint16_t uuid)
|
||||
{
|
||||
td_vbd_t *vbd, *tmp;
|
||||
|
||||
tapdisk_server_for_each_vbd(vbd, tmp)
|
||||
if (vbd->uuid == uuid)
|
||||
return vbd;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
tapdisk_server_add_vbd(td_vbd_t *vbd)
|
||||
{
|
||||
list_add_tail(&vbd->next, &server.vbds);
|
||||
}
|
||||
|
||||
void
|
||||
tapdisk_server_remove_vbd(td_vbd_t *vbd)
|
||||
{
|
||||
list_del(&vbd->next);
|
||||
INIT_LIST_HEAD(&vbd->next);
|
||||
tapdisk_server_check_state();
|
||||
}
|
||||
|
||||
void
|
||||
tapdisk_server_queue_tiocb(struct tiocb *tiocb)
|
||||
{
|
||||
tapdisk_queue_tiocb(&server.aio_queue, tiocb);
|
||||
}
|
||||
|
||||
void
|
||||
tapdisk_server_debug(void)
|
||||
{
|
||||
td_vbd_t *vbd, *tmp;
|
||||
|
||||
tapdisk_debug_queue(&server.aio_queue);
|
||||
|
||||
tapdisk_server_for_each_vbd(vbd, tmp)
|
||||
tapdisk_vbd_debug(vbd);
|
||||
|
||||
tlog_flush();
|
||||
}
|
||||
|
||||
void
|
||||
tapdisk_server_check_state(void)
|
||||
{
|
||||
if (list_empty(&server.vbds))
|
||||
server.run = 0;
|
||||
}
|
||||
|
||||
event_id_t
|
||||
tapdisk_server_register_event(char mode, int fd,
|
||||
int timeout, event_cb_t cb, void *data)
|
||||
{
|
||||
return scheduler_register_event(&server.scheduler,
|
||||
mode, fd, timeout, cb, data);
|
||||
}
|
||||
|
||||
void
|
||||
tapdisk_server_unregister_event(event_id_t event)
|
||||
{
|
||||
return scheduler_unregister_event(&server.scheduler, event);
|
||||
}
|
||||
|
||||
void
|
||||
tapdisk_server_set_max_timeout(int seconds)
|
||||
{
|
||||
scheduler_set_max_timeout(&server.scheduler, seconds);
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_server_assert_locks(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_server_set_retry_timeout(void)
|
||||
{
|
||||
td_vbd_t *vbd, *tmp;
|
||||
|
||||
tapdisk_server_for_each_vbd(vbd, tmp)
|
||||
if (tapdisk_vbd_retry_needed(vbd)) {
|
||||
tapdisk_server_set_max_timeout(TD_VBD_RETRY_INTERVAL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_server_check_progress(void)
|
||||
{
|
||||
struct timeval now;
|
||||
td_vbd_t *vbd, *tmp;
|
||||
|
||||
gettimeofday(&now, NULL);
|
||||
|
||||
tapdisk_server_for_each_vbd(vbd, tmp)
|
||||
tapdisk_vbd_check_progress(vbd);
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_server_submit_tiocbs(void)
|
||||
{
|
||||
tapdisk_submit_all_tiocbs(&server.aio_queue);
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_server_kick_responses(void)
|
||||
{
|
||||
int n;
|
||||
td_vbd_t *vbd, *tmp;
|
||||
|
||||
tapdisk_server_for_each_vbd(vbd, tmp)
|
||||
tapdisk_vbd_kick(vbd);
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_server_check_vbds(void)
|
||||
{
|
||||
td_vbd_t *vbd, *tmp;
|
||||
|
||||
tapdisk_server_for_each_vbd(vbd, tmp)
|
||||
tapdisk_vbd_check_state(vbd);
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_server_stop_vbds(void)
|
||||
{
|
||||
td_vbd_t *vbd, *tmp;
|
||||
|
||||
tapdisk_server_for_each_vbd(vbd, tmp)
|
||||
tapdisk_vbd_kill_queue(vbd);
|
||||
}
|
||||
|
||||
static int
|
||||
tapdisk_server_init_aio(void)
|
||||
{
|
||||
return tapdisk_init_queue(&server.aio_queue, TAPDISK_TIOCBS,
|
||||
TIO_DRV_LIO, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_server_close_aio(void)
|
||||
{
|
||||
tapdisk_free_queue(&server.aio_queue);
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_server_close(void)
|
||||
{
|
||||
tapdisk_server_close_aio();
|
||||
}
|
||||
|
||||
void
|
||||
tapdisk_server_iterate(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
tapdisk_server_assert_locks();
|
||||
tapdisk_server_set_retry_timeout();
|
||||
tapdisk_server_check_progress();
|
||||
|
||||
ret = scheduler_wait_for_events(&server.scheduler);
|
||||
if (ret < 0)
|
||||
DBG(TLOG_WARN, "server wait returned %d\n", ret);
|
||||
|
||||
tapdisk_server_check_vbds();
|
||||
tapdisk_server_submit_tiocbs();
|
||||
tapdisk_server_kick_responses();
|
||||
}
|
||||
|
||||
static void
|
||||
__tapdisk_server_run(void)
|
||||
{
|
||||
while (server.run)
|
||||
tapdisk_server_iterate();
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_server_signal_handler(int signal)
|
||||
{
|
||||
td_vbd_t *vbd, *tmp;
|
||||
static int xfsz_error_sent = 0;
|
||||
|
||||
switch (signal) {
|
||||
case SIGBUS:
|
||||
case SIGINT:
|
||||
tapdisk_server_for_each_vbd(vbd, tmp)
|
||||
tapdisk_vbd_close(vbd);
|
||||
break;
|
||||
|
||||
case SIGXFSZ:
|
||||
ERR(EFBIG, "received SIGXFSZ");
|
||||
tapdisk_server_stop_vbds();
|
||||
if (xfsz_error_sent)
|
||||
break;
|
||||
|
||||
xfsz_error_sent = 1;
|
||||
break;
|
||||
|
||||
case SIGUSR1:
|
||||
tapdisk_server_debug();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
tapdisk_server_init(void)
|
||||
{
|
||||
memset(&server, 0, sizeof(server));
|
||||
INIT_LIST_HEAD(&server.vbds);
|
||||
|
||||
scheduler_initialize(&server.scheduler);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
tapdisk_server_complete(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = tapdisk_server_init_aio();
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
server.run = 1;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
tapdisk_server_close_aio();
|
||||
return err;
|
||||
}
|
||||
|
||||
int
|
||||
tapdisk_server_initialize(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
tapdisk_server_init();
|
||||
|
||||
err = tapdisk_server_complete();
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
tapdisk_server_close();
|
||||
return err;
|
||||
}
|
||||
|
||||
int
|
||||
tapdisk_server_run()
|
||||
{
|
||||
int err;
|
||||
|
||||
err = tapdisk_set_resource_limits();
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
signal(SIGBUS, tapdisk_server_signal_handler);
|
||||
signal(SIGINT, tapdisk_server_signal_handler);
|
||||
signal(SIGUSR1, tapdisk_server_signal_handler);
|
||||
signal(SIGXFSZ, tapdisk_server_signal_handler);
|
||||
|
||||
__tapdisk_server_run();
|
||||
tapdisk_server_close();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef _TAPDISK_SERVER_H_
|
||||
#define _TAPDISK_SERVER_H_
|
||||
|
||||
#include "list.h"
|
||||
#include "tapdisk-vbd.h"
|
||||
#include "tapdisk-queue.h"
|
||||
|
||||
struct tap_disk *tapdisk_server_find_driver_interface(int);
|
||||
|
||||
td_image_t *tapdisk_server_get_shared_image(td_image_t *);
|
||||
|
||||
struct list_head *tapdisk_server_get_all_vbds(void);
|
||||
td_vbd_t *tapdisk_server_get_vbd(td_uuid_t);
|
||||
void tapdisk_server_add_vbd(td_vbd_t *);
|
||||
void tapdisk_server_remove_vbd(td_vbd_t *);
|
||||
|
||||
void tapdisk_server_queue_tiocb(struct tiocb *);
|
||||
|
||||
void tapdisk_server_check_state(void);
|
||||
|
||||
event_id_t tapdisk_server_register_event(char, int, int, event_cb_t, void *);
|
||||
void tapdisk_server_unregister_event(event_id_t);
|
||||
void tapdisk_server_set_max_timeout(int);
|
||||
|
||||
int tapdisk_server_init(void);
|
||||
int tapdisk_server_initialize(void);
|
||||
int tapdisk_server_complete(void);
|
||||
int tapdisk_server_run(void);
|
||||
void tapdisk_server_iterate(void);
|
||||
|
||||
#define TAPDISK_TIOCBS (TAPDISK_DATA_REQUESTS + 50)
|
||||
|
||||
typedef struct tapdisk_server {
|
||||
int run;
|
||||
struct list_head vbds;
|
||||
scheduler_t scheduler;
|
||||
struct tqueue aio_queue;
|
||||
} tapdisk_server_t;
|
||||
|
||||
#endif
|
||||
@@ -1,605 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "list.h"
|
||||
#include "scheduler.h"
|
||||
#include "tapdisk-vbd.h"
|
||||
#include "tapdisk-server.h"
|
||||
#include "tapdisk-disktype.h"
|
||||
#include "tapdisk-utils.h"
|
||||
|
||||
#define POLL_READ 0
|
||||
#define POLL_WRITE 1
|
||||
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
|
||||
struct tapdisk_stream_poll {
|
||||
int pipe[2];
|
||||
int set;
|
||||
};
|
||||
|
||||
struct tapdisk_stream_request {
|
||||
uint64_t sec;
|
||||
uint32_t secs;
|
||||
uint64_t seqno;
|
||||
blkif_request_t blkif_req;
|
||||
struct list_head next;
|
||||
};
|
||||
|
||||
struct tapdisk_stream {
|
||||
td_vbd_t *vbd;
|
||||
|
||||
unsigned int id;
|
||||
int in_fd;
|
||||
int out_fd;
|
||||
|
||||
int err;
|
||||
|
||||
uint64_t cur;
|
||||
uint64_t start;
|
||||
uint64_t end;
|
||||
|
||||
uint64_t started;
|
||||
uint64_t completed;
|
||||
|
||||
struct tapdisk_stream_poll poll;
|
||||
event_id_t enqueue_event_id;
|
||||
|
||||
struct list_head free_list;
|
||||
struct list_head pending_list;
|
||||
struct list_head completed_list;
|
||||
|
||||
struct tapdisk_stream_request requests[MAX_REQUESTS];
|
||||
};
|
||||
|
||||
static unsigned int tapdisk_stream_count;
|
||||
|
||||
static void tapdisk_stream_close_image(struct tapdisk_stream *);
|
||||
|
||||
static void
|
||||
usage(const char *app, int err)
|
||||
{
|
||||
printf("usage: %s <-n type:/path/to/image> "
|
||||
"[-c sector count] [-s skip sectors]\n", app);
|
||||
exit(err);
|
||||
}
|
||||
|
||||
static inline void
|
||||
tapdisk_stream_poll_initialize(struct tapdisk_stream_poll *p)
|
||||
{
|
||||
p->set = 0;
|
||||
p->pipe[POLL_READ] = p->pipe[POLL_WRITE] = -1;
|
||||
}
|
||||
|
||||
static int
|
||||
tapdisk_stream_poll_open(struct tapdisk_stream_poll *p)
|
||||
{
|
||||
int err;
|
||||
|
||||
tapdisk_stream_poll_initialize(p);
|
||||
|
||||
err = pipe(p->pipe);
|
||||
if (err)
|
||||
return -errno;
|
||||
|
||||
err = fcntl(p->pipe[POLL_READ], F_SETFL, O_NONBLOCK);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = fcntl(p->pipe[POLL_WRITE], F_SETFL, O_NONBLOCK);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
close(p->pipe[POLL_READ]);
|
||||
close(p->pipe[POLL_WRITE]);
|
||||
tapdisk_stream_poll_initialize(p);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_stream_poll_close(struct tapdisk_stream_poll *p)
|
||||
{
|
||||
if (p->pipe[POLL_READ] != -1)
|
||||
close(p->pipe[POLL_READ]);
|
||||
if (p->pipe[POLL_WRITE] != -1)
|
||||
close(p->pipe[POLL_WRITE]);
|
||||
tapdisk_stream_poll_initialize(p);
|
||||
}
|
||||
|
||||
static inline void
|
||||
tapdisk_stream_poll_clear(struct tapdisk_stream_poll *p)
|
||||
{
|
||||
int dummy;
|
||||
|
||||
read_exact(p->pipe[POLL_READ], &dummy, sizeof(dummy));
|
||||
p->set = 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
tapdisk_stream_poll_set(struct tapdisk_stream_poll *p)
|
||||
{
|
||||
int dummy = 0;
|
||||
|
||||
if (!p->set) {
|
||||
write_exact(p->pipe[POLL_WRITE], &dummy, sizeof(dummy));
|
||||
p->set = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int
|
||||
tapdisk_stream_stop(struct tapdisk_stream *s)
|
||||
{
|
||||
return (list_empty(&s->pending_list) && (s->cur == s->end || s->err));
|
||||
}
|
||||
|
||||
static inline void
|
||||
tapdisk_stream_initialize_request(struct tapdisk_stream_request *req)
|
||||
{
|
||||
memset(req, 0, sizeof(*req));
|
||||
INIT_LIST_HEAD(&req->next);
|
||||
}
|
||||
|
||||
static inline int
|
||||
tapdisk_stream_request_idx(struct tapdisk_stream *s,
|
||||
struct tapdisk_stream_request *req)
|
||||
{
|
||||
return (req - s->requests);
|
||||
}
|
||||
|
||||
static inline struct tapdisk_stream_request *
|
||||
tapdisk_stream_get_request(struct tapdisk_stream *s)
|
||||
{
|
||||
struct tapdisk_stream_request *req;
|
||||
|
||||
if (list_empty(&s->free_list))
|
||||
return NULL;
|
||||
|
||||
req = list_entry(s->free_list.next,
|
||||
struct tapdisk_stream_request, next);
|
||||
|
||||
list_del_init(&req->next);
|
||||
tapdisk_stream_initialize_request(req);
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_stream_print_request(struct tapdisk_stream *s,
|
||||
struct tapdisk_stream_request *sreq)
|
||||
{
|
||||
unsigned long idx = (unsigned long)tapdisk_stream_request_idx(s, sreq);
|
||||
char *buf = (char *)MMAP_VADDR(s->vbd->ring.vstart, idx, 0);
|
||||
write_exact(s->out_fd, buf, sreq->secs << SECTOR_SHIFT);
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_stream_write_data(struct tapdisk_stream *s)
|
||||
{
|
||||
struct tapdisk_stream_request *sreq, *tmp;
|
||||
|
||||
list_for_each_entry_safe(sreq, tmp, &s->completed_list, next) {
|
||||
if (sreq->seqno != s->completed)
|
||||
break;
|
||||
|
||||
s->completed++;
|
||||
tapdisk_stream_print_request(s, sreq);
|
||||
|
||||
list_del_init(&sreq->next);
|
||||
list_add_tail(&sreq->next, &s->free_list);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
tapdisk_stream_queue_completed(struct tapdisk_stream *s,
|
||||
struct tapdisk_stream_request *sreq)
|
||||
{
|
||||
struct tapdisk_stream_request *itr;
|
||||
|
||||
list_for_each_entry(itr, &s->completed_list, next)
|
||||
if (sreq->seqno < itr->seqno) {
|
||||
list_add_tail(&sreq->next, &itr->next);
|
||||
return;
|
||||
}
|
||||
|
||||
list_add_tail(&sreq->next, &s->completed_list);
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_stream_dequeue(void *arg, blkif_response_t *rsp)
|
||||
{
|
||||
struct tapdisk_stream *s = (struct tapdisk_stream *)arg;
|
||||
struct tapdisk_stream_request *sreq = s->requests + rsp->id;
|
||||
|
||||
list_del_init(&sreq->next);
|
||||
|
||||
if (rsp->status == BLKIF_RSP_OKAY)
|
||||
tapdisk_stream_queue_completed(s, sreq);
|
||||
else {
|
||||
s->err = EIO;
|
||||
list_add_tail(&sreq->next, &s->free_list);
|
||||
fprintf(stderr, "error reading sector 0x%"PRIu64"\n", sreq->sec);
|
||||
}
|
||||
|
||||
tapdisk_stream_write_data(s);
|
||||
tapdisk_stream_poll_set(&s->poll);
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_stream_enqueue(event_id_t id, char mode, void *arg)
|
||||
{
|
||||
td_vbd_t *vbd;
|
||||
int i, idx, psize;
|
||||
struct tapdisk_stream *s = (struct tapdisk_stream *)arg;
|
||||
|
||||
vbd = s->vbd;
|
||||
tapdisk_stream_poll_clear(&s->poll);
|
||||
|
||||
if (tapdisk_stream_stop(s)) {
|
||||
tapdisk_stream_close_image(s);
|
||||
return;
|
||||
}
|
||||
|
||||
psize = getpagesize();
|
||||
|
||||
while (s->cur < s->end && !s->err) {
|
||||
blkif_request_t *breq;
|
||||
td_vbd_request_t *vreq;
|
||||
struct tapdisk_stream_request *sreq;
|
||||
|
||||
sreq = tapdisk_stream_get_request(s);
|
||||
if (!sreq)
|
||||
break;
|
||||
|
||||
idx = tapdisk_stream_request_idx(s, sreq);
|
||||
|
||||
sreq->sec = s->cur;
|
||||
sreq->secs = 0;
|
||||
sreq->seqno = s->started++;
|
||||
|
||||
breq = &sreq->blkif_req;
|
||||
breq->id = idx;
|
||||
breq->nr_segments = 0;
|
||||
breq->sector_number = sreq->sec;
|
||||
breq->operation = BLKIF_OP_READ;
|
||||
|
||||
for (i = 0; i < BLKIF_MAX_SEGMENTS_PER_REQUEST; i++) {
|
||||
uint32_t secs = MIN(s->end - s->cur, psize >> SECTOR_SHIFT);
|
||||
struct blkif_request_segment *seg = breq->seg + i;
|
||||
|
||||
if (!secs)
|
||||
break;
|
||||
|
||||
sreq->secs += secs;
|
||||
s->cur += secs;
|
||||
|
||||
seg->first_sect = 0;
|
||||
seg->last_sect = secs - 1;
|
||||
breq->nr_segments++;
|
||||
}
|
||||
|
||||
vreq = vbd->request_list + idx;
|
||||
|
||||
assert(list_empty(&vreq->next));
|
||||
assert(vreq->secs_pending == 0);
|
||||
|
||||
memcpy(&vreq->req, breq, sizeof(*breq));
|
||||
vbd->received++;
|
||||
vreq->vbd = vbd;
|
||||
|
||||
tapdisk_vbd_move_request(vreq, &vbd->new_requests);
|
||||
list_add_tail(&sreq->next, &s->pending_list);
|
||||
}
|
||||
|
||||
tapdisk_vbd_issue_requests(vbd);
|
||||
}
|
||||
|
||||
static int
|
||||
tapdisk_stream_open_image(struct tapdisk_stream *s, const char *path, int type)
|
||||
{
|
||||
int err;
|
||||
|
||||
s->id = tapdisk_stream_count++;
|
||||
|
||||
err = tapdisk_server_initialize();
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = tapdisk_vbd_initialize(s->id);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
s->vbd = tapdisk_server_get_vbd(s->id);
|
||||
if (!s->vbd) {
|
||||
err = ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
tapdisk_vbd_set_callback(s->vbd, tapdisk_stream_dequeue, s);
|
||||
|
||||
err = tapdisk_vbd_open_vdi(s->vbd, path, type,
|
||||
TAPDISK_STORAGE_TYPE_DEFAULT,
|
||||
TD_OPEN_RDONLY);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
s->vbd->reopened = 1;
|
||||
err = 0;
|
||||
|
||||
out:
|
||||
if (err)
|
||||
fprintf(stderr, "failed to open %s: %d\n", path, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_stream_close_image(struct tapdisk_stream *s)
|
||||
{
|
||||
td_vbd_t *vbd;
|
||||
|
||||
vbd = tapdisk_server_get_vbd(s->id);
|
||||
if (vbd) {
|
||||
tapdisk_vbd_close_vdi(vbd);
|
||||
tapdisk_server_remove_vbd(vbd);
|
||||
free((void *)vbd->ring.vstart);
|
||||
free(vbd->name);
|
||||
free(vbd);
|
||||
s->vbd = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
tapdisk_stream_set_position(struct tapdisk_stream *s,
|
||||
uint64_t count, uint64_t skip)
|
||||
{
|
||||
int err;
|
||||
image_t image;
|
||||
|
||||
err = tapdisk_vbd_get_image_info(s->vbd, &image);
|
||||
if (err) {
|
||||
fprintf(stderr, "failed getting image size: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (count == (uint64_t)-1)
|
||||
count = image.size - skip;
|
||||
|
||||
if (count + skip > image.size) {
|
||||
fprintf(stderr, "0x%"PRIx64" past end of image 0x%"PRIx64"\n",
|
||||
(uint64_t) (count + skip), (uint64_t) image.size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
s->start = skip;
|
||||
s->cur = s->start;
|
||||
s->end = s->start + count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
tapdisk_stream_initialize_requests(struct tapdisk_stream *s)
|
||||
{
|
||||
size_t size;
|
||||
td_ring_t *ring;
|
||||
int err, i, psize;
|
||||
|
||||
ring = &s->vbd->ring;
|
||||
psize = getpagesize();
|
||||
size = psize * BLKTAP_MMAP_REGION_SIZE;
|
||||
|
||||
/* sneaky -- set up ring->vstart so tapdisk_vbd will use our buffers */
|
||||
err = posix_memalign((void **)&ring->vstart, psize, size);
|
||||
if (err) {
|
||||
fprintf(stderr, "failed to allocate buffers: %d\n", err);
|
||||
ring->vstart = 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_REQUESTS; i++) {
|
||||
struct tapdisk_stream_request *req = s->requests + i;
|
||||
tapdisk_stream_initialize_request(req);
|
||||
list_add_tail(&req->next, &s->free_list);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
tapdisk_stream_register_enqueue_event(struct tapdisk_stream *s)
|
||||
{
|
||||
int err;
|
||||
struct tapdisk_stream_poll *p = &s->poll;
|
||||
|
||||
err = tapdisk_stream_poll_open(p);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = tapdisk_server_register_event(SCHEDULER_POLL_READ_FD,
|
||||
p->pipe[POLL_READ], 0,
|
||||
tapdisk_stream_enqueue, s);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
s->enqueue_event_id = err;
|
||||
err = 0;
|
||||
|
||||
out:
|
||||
if (err)
|
||||
fprintf(stderr, "failed to register event: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_stream_unregister_enqueue_event(struct tapdisk_stream *s)
|
||||
{
|
||||
if (s->enqueue_event_id) {
|
||||
tapdisk_server_unregister_event(s->enqueue_event_id);
|
||||
s->enqueue_event_id = 0;
|
||||
}
|
||||
tapdisk_stream_poll_close(&s->poll);
|
||||
}
|
||||
|
||||
static inline void
|
||||
tapdisk_stream_initialize(struct tapdisk_stream *s)
|
||||
{
|
||||
memset(s, 0, sizeof(*s));
|
||||
s->in_fd = s->out_fd = -1;
|
||||
INIT_LIST_HEAD(&s->free_list);
|
||||
INIT_LIST_HEAD(&s->pending_list);
|
||||
INIT_LIST_HEAD(&s->completed_list);
|
||||
}
|
||||
|
||||
static int
|
||||
tapdisk_stream_open_fds(struct tapdisk_stream *s)
|
||||
{
|
||||
s->out_fd = dup(STDOUT_FILENO);
|
||||
if (s->out_fd == -1) {
|
||||
fprintf(stderr, "failed to open output: %d\n", errno);
|
||||
return errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
tapdisk_stream_open(struct tapdisk_stream *s, const char *path,
|
||||
int type, uint64_t count, uint64_t skip)
|
||||
{
|
||||
int err;
|
||||
|
||||
tapdisk_stream_initialize(s);
|
||||
|
||||
err = tapdisk_stream_open_fds(s);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = tapdisk_stream_open_image(s, path, type);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = tapdisk_stream_set_position(s, count, skip);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = tapdisk_stream_initialize_requests(s);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = tapdisk_stream_register_enqueue_event(s);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
tapdisk_stream_release(struct tapdisk_stream *s)
|
||||
{
|
||||
close(s->out_fd);
|
||||
tapdisk_stream_close_image(s);
|
||||
tapdisk_stream_unregister_enqueue_event(s);
|
||||
}
|
||||
|
||||
static int
|
||||
tapdisk_stream_run(struct tapdisk_stream *s)
|
||||
{
|
||||
tapdisk_stream_enqueue(s->enqueue_event_id, SCHEDULER_POLL_READ_FD, s);
|
||||
tapdisk_server_run();
|
||||
return s->err;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int c, err, type;
|
||||
const char *params;
|
||||
const disk_info_t *info;
|
||||
const char *path;
|
||||
uint64_t count, skip;
|
||||
struct tapdisk_stream stream;
|
||||
|
||||
err = 0;
|
||||
skip = 0;
|
||||
count = (uint64_t)-1;
|
||||
params = NULL;
|
||||
|
||||
while ((c = getopt(argc, argv, "n:c:s:h")) != -1) {
|
||||
switch (c) {
|
||||
case 'n':
|
||||
params = optarg;
|
||||
break;
|
||||
case 'c':
|
||||
count = strtoull(optarg, NULL, 10);
|
||||
break;
|
||||
case 's':
|
||||
skip = strtoull(optarg, NULL, 10);
|
||||
break;
|
||||
default:
|
||||
err = EINVAL;
|
||||
case 'h':
|
||||
usage(argv[0], err);
|
||||
}
|
||||
}
|
||||
|
||||
if (!params)
|
||||
usage(argv[0], EINVAL);
|
||||
|
||||
type = tapdisk_disktype_parse_params(params, &path);
|
||||
if (type < 0) {
|
||||
err = type;
|
||||
fprintf(stderr, "invalid argument %s: %d\n", params, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
tapdisk_start_logging("tapdisk-stream");
|
||||
|
||||
err = tapdisk_stream_open(&stream, path, type, count, skip);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = tapdisk_stream_run(&stream);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = 0;
|
||||
|
||||
out:
|
||||
tapdisk_stream_release(&stream);
|
||||
tapdisk_stop_logging();
|
||||
return err;
|
||||
}
|
||||
@@ -1,214 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/utsname.h>
|
||||
#ifdef __linux__
|
||||
#include <linux/version.h>
|
||||
#endif
|
||||
|
||||
#include "blk.h"
|
||||
#include "tapdisk.h"
|
||||
#include "blktaplib.h"
|
||||
#include "tapdisk-log.h"
|
||||
#include "tapdisk-utils.h"
|
||||
|
||||
void
|
||||
tapdisk_start_logging(const char *name)
|
||||
{
|
||||
static char buf[128];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s[%d]", name, getpid());
|
||||
openlog(buf, LOG_CONS | LOG_ODELAY, LOG_DAEMON);
|
||||
open_tlog("/tmp/tapdisk.log", (64 << 10), TLOG_WARN, 0);
|
||||
}
|
||||
|
||||
void
|
||||
tapdisk_stop_logging(void)
|
||||
{
|
||||
closelog();
|
||||
close_tlog();
|
||||
}
|
||||
|
||||
int
|
||||
tapdisk_set_resource_limits(void)
|
||||
{
|
||||
int err;
|
||||
struct rlimit rlim;
|
||||
|
||||
rlim.rlim_cur = RLIM_INFINITY;
|
||||
rlim.rlim_max = RLIM_INFINITY;
|
||||
|
||||
err = setrlimit(RLIMIT_MEMLOCK, &rlim);
|
||||
if (err == -1) {
|
||||
EPRINTF("RLIMIT_MEMLOCK failed: %d\n", errno);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
err = mlockall(MCL_CURRENT | MCL_FUTURE);
|
||||
if (err == -1) {
|
||||
EPRINTF("mlockall failed: %d\n", errno);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
#define CORE_DUMP
|
||||
#if defined(CORE_DUMP)
|
||||
err = setrlimit(RLIMIT_CORE, &rlim);
|
||||
if (err == -1)
|
||||
EPRINTF("RLIMIT_CORE failed: %d\n", errno);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
tapdisk_namedup(char **dup, const char *name)
|
||||
{
|
||||
*dup = NULL;
|
||||
|
||||
if (strnlen(name, MAX_NAME_LEN) >= MAX_NAME_LEN)
|
||||
return -ENAMETOOLONG;
|
||||
|
||||
*dup = strdup(name);
|
||||
if (!*dup)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*Get Image size, secsize*/
|
||||
int
|
||||
tapdisk_get_image_size(int fd, uint64_t *_sectors, uint32_t *_sector_size)
|
||||
{
|
||||
int ret;
|
||||
struct stat stat;
|
||||
uint64_t sectors;
|
||||
uint64_t sector_size;
|
||||
|
||||
sectors = 0;
|
||||
sector_size = 0;
|
||||
*_sectors = 0;
|
||||
*_sector_size = 0;
|
||||
|
||||
if (fstat(fd, &stat)) {
|
||||
DPRINTF("ERROR: fstat failed, Couldn't stat image");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (S_ISBLK(stat.st_mode)) {
|
||||
/*Accessing block device directly*/
|
||||
if (blk_getimagesize(fd, §ors) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
/*Get the sector size*/
|
||||
if (blk_getsectorsize(fd, §or_size) != 0)
|
||||
sector_size = DEFAULT_SECTOR_SIZE;
|
||||
} else {
|
||||
/*Local file? try fstat instead*/
|
||||
sectors = (stat.st_size >> SECTOR_SHIFT);
|
||||
sector_size = DEFAULT_SECTOR_SIZE;
|
||||
}
|
||||
|
||||
if (sectors == 0) {
|
||||
sectors = 16836057ULL;
|
||||
sector_size = DEFAULT_SECTOR_SIZE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
int tapdisk_linux_version(void)
|
||||
{
|
||||
struct utsname uts;
|
||||
unsigned int version, patchlevel, sublevel;
|
||||
int n, err;
|
||||
|
||||
err = uname(&uts);
|
||||
if (err)
|
||||
return -errno;
|
||||
|
||||
n = sscanf(uts.release, "%u.%u.%u", &version, &patchlevel, &sublevel);
|
||||
if (n != 3)
|
||||
return -ENOSYS;
|
||||
|
||||
return KERNEL_VERSION(version, patchlevel, sublevel);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int tapdisk_linux_version(void)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
#endif
|
||||
int read_exact(int fd, void *data, size_t size)
|
||||
{
|
||||
size_t offset = 0;
|
||||
ssize_t len;
|
||||
|
||||
while ( offset < size )
|
||||
{
|
||||
len = read(fd, (char *)data + offset, size - offset);
|
||||
if ( (len == -1) && (errno == EINTR) )
|
||||
continue;
|
||||
if ( len == 0 )
|
||||
errno = 0;
|
||||
if ( len <= 0 )
|
||||
return -1;
|
||||
offset += len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int write_exact(int fd, const void *data, size_t size)
|
||||
{
|
||||
size_t offset = 0;
|
||||
ssize_t len;
|
||||
|
||||
while ( offset < size )
|
||||
{
|
||||
len = write(fd, (const char *)data + offset, size - offset);
|
||||
if ( (len == -1) && (errno == EINTR) )
|
||||
continue;
|
||||
if ( len <= 0 )
|
||||
return -1;
|
||||
offset += len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef _TAPDISK_UTILS_H_
|
||||
#define _TAPDISK_UTILS_H_
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#define MAX_NAME_LEN 1000
|
||||
|
||||
void tapdisk_start_logging(const char *);
|
||||
void tapdisk_stop_logging(void);
|
||||
int tapdisk_set_resource_limits(void);
|
||||
int tapdisk_namedup(char **, const char *);
|
||||
int tapdisk_get_image_size(int, uint64_t *, uint32_t *);
|
||||
int tapdisk_linux_version(void);
|
||||
|
||||
int read_exact(int fd, void *data, size_t size); /* EOF => -1, errno=0 */
|
||||
int write_exact(int fd, const void *data, size_t size);
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,207 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef _TAPDISK_VBD_H_
|
||||
#define _TAPDISK_VBD_H_
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <xenctrl.h>
|
||||
#include <xen/io/blkif.h>
|
||||
|
||||
#include "tapdisk.h"
|
||||
#include "scheduler.h"
|
||||
#include "tapdisk-image.h"
|
||||
|
||||
#define TD_VBD_MAX_RETRIES 100
|
||||
#define TD_VBD_RETRY_INTERVAL 1
|
||||
|
||||
#define TD_VBD_DEAD 0x0001
|
||||
#define TD_VBD_CLOSED 0x0002
|
||||
#define TD_VBD_QUIESCE_REQUESTED 0x0004
|
||||
#define TD_VBD_QUIESCED 0x0008
|
||||
#define TD_VBD_PAUSE_REQUESTED 0x0010
|
||||
#define TD_VBD_PAUSED 0x0020
|
||||
#define TD_VBD_SHUTDOWN_REQUESTED 0x0040
|
||||
#define TD_VBD_LOCKING 0x0080
|
||||
#define TD_VBD_RETRY_NEEDED 0x0100
|
||||
#define TD_VBD_LOG_DROPPED 0x0200
|
||||
|
||||
typedef struct td_ring td_ring_t;
|
||||
typedef struct td_vbd_request td_vbd_request_t;
|
||||
typedef struct td_vbd_driver_info td_vbd_driver_info_t;
|
||||
typedef struct td_vbd_handle td_vbd_t;
|
||||
typedef void (*td_vbd_cb_t) (void *, blkif_response_t *);
|
||||
|
||||
struct td_ring {
|
||||
int fd;
|
||||
char *mem;
|
||||
blkif_sring_t *sring;
|
||||
blkif_back_ring_t fe_ring;
|
||||
unsigned long vstart;
|
||||
};
|
||||
|
||||
struct td_vbd_request {
|
||||
blkif_request_t req;
|
||||
int16_t status;
|
||||
|
||||
int error;
|
||||
int blocked; /* blocked on a dependency */
|
||||
int submitting;
|
||||
int secs_pending;
|
||||
int num_retries;
|
||||
struct timeval last_try;
|
||||
|
||||
td_vbd_t *vbd;
|
||||
struct list_head next;
|
||||
};
|
||||
|
||||
struct td_vbd_driver_info {
|
||||
char *params;
|
||||
int type;
|
||||
struct list_head next;
|
||||
};
|
||||
|
||||
struct td_vbd_handle {
|
||||
char *name;
|
||||
|
||||
td_uuid_t uuid;
|
||||
int minor;
|
||||
|
||||
struct list_head driver_stack;
|
||||
|
||||
int storage;
|
||||
|
||||
uint8_t reopened;
|
||||
uint8_t reactivated;
|
||||
td_flag_t flags;
|
||||
td_flag_t state;
|
||||
|
||||
struct list_head images;
|
||||
|
||||
struct list_head new_requests;
|
||||
struct list_head pending_requests;
|
||||
struct list_head failed_requests;
|
||||
struct list_head completed_requests;
|
||||
|
||||
td_vbd_request_t request_list[MAX_REQUESTS];
|
||||
|
||||
td_ring_t ring;
|
||||
event_id_t ring_event_id;
|
||||
|
||||
td_vbd_cb_t callback;
|
||||
void *argument;
|
||||
|
||||
struct list_head next;
|
||||
|
||||
struct timeval ts;
|
||||
|
||||
uint64_t received;
|
||||
uint64_t returned;
|
||||
uint64_t kicked;
|
||||
uint64_t secs_pending;
|
||||
uint64_t retries;
|
||||
uint64_t errors;
|
||||
};
|
||||
|
||||
#define tapdisk_vbd_for_each_request(vreq, tmp, list) \
|
||||
list_for_each_entry_safe((vreq), (tmp), (list), next)
|
||||
|
||||
#define tapdisk_vbd_for_each_image(vbd, image, tmp) \
|
||||
list_for_each_entry_safe((image), (tmp), &(vbd)->images, next)
|
||||
|
||||
static inline void
|
||||
tapdisk_vbd_move_request(td_vbd_request_t *vreq, struct list_head *dest)
|
||||
{
|
||||
list_del(&vreq->next);
|
||||
INIT_LIST_HEAD(&vreq->next);
|
||||
list_add_tail(&vreq->next, dest);
|
||||
}
|
||||
|
||||
static inline void
|
||||
tapdisk_vbd_add_image(td_vbd_t *vbd, td_image_t *image)
|
||||
{
|
||||
list_add_tail(&image->next, &vbd->images);
|
||||
}
|
||||
|
||||
static inline int
|
||||
tapdisk_vbd_is_last_image(td_vbd_t *vbd, td_image_t *image)
|
||||
{
|
||||
return list_is_last(&image->next, &vbd->images);
|
||||
}
|
||||
|
||||
td_image_t *
|
||||
tapdisk_vbd_first_image(td_vbd_t *vbd);
|
||||
|
||||
static inline td_image_t *
|
||||
tapdisk_vbd_last_image(td_vbd_t *vbd)
|
||||
{
|
||||
return list_entry(vbd->images.prev, td_image_t, next);
|
||||
}
|
||||
|
||||
static inline td_image_t *
|
||||
tapdisk_vbd_next_image(td_image_t *image)
|
||||
{
|
||||
return list_entry(image->next.next, td_image_t, next);
|
||||
}
|
||||
|
||||
td_vbd_t *tapdisk_vbd_create(td_uuid_t);
|
||||
int tapdisk_vbd_initialize(td_uuid_t);
|
||||
void tapdisk_vbd_set_callback(td_vbd_t *, td_vbd_cb_t, void *);
|
||||
int tapdisk_vbd_parse_stack(td_vbd_t *vbd, const char *path);
|
||||
int tapdisk_vbd_open(td_vbd_t *, const char *, uint16_t,
|
||||
uint16_t, int, const char *, td_flag_t);
|
||||
int tapdisk_vbd_close(td_vbd_t *);
|
||||
void tapdisk_vbd_free(td_vbd_t *);
|
||||
void tapdisk_vbd_free_stack(td_vbd_t *);
|
||||
|
||||
int tapdisk_vbd_open_stack(td_vbd_t *, uint16_t, td_flag_t);
|
||||
int tapdisk_vbd_open_vdi(td_vbd_t *, const char *,
|
||||
uint16_t, uint16_t, td_flag_t);
|
||||
void tapdisk_vbd_close_vdi(td_vbd_t *);
|
||||
|
||||
int tapdisk_vbd_attach(td_vbd_t *, const char *, int);
|
||||
void tapdisk_vbd_detach(td_vbd_t *);
|
||||
|
||||
void tapdisk_vbd_forward_request(td_request_t);
|
||||
|
||||
int tapdisk_vbd_get_image_info(td_vbd_t *, image_t *);
|
||||
int tapdisk_vbd_queue_ready(td_vbd_t *);
|
||||
int tapdisk_vbd_retry_needed(td_vbd_t *);
|
||||
int tapdisk_vbd_quiesce_queue(td_vbd_t *);
|
||||
int tapdisk_vbd_start_queue(td_vbd_t *);
|
||||
int tapdisk_vbd_issue_requests(td_vbd_t *);
|
||||
int tapdisk_vbd_kill_queue(td_vbd_t *);
|
||||
int tapdisk_vbd_pause(td_vbd_t *);
|
||||
int tapdisk_vbd_resume(td_vbd_t *, const char *, uint16_t);
|
||||
int tapdisk_vbd_kick(td_vbd_t *);
|
||||
void tapdisk_vbd_check_state(td_vbd_t *);
|
||||
void tapdisk_vbd_check_progress(td_vbd_t *);
|
||||
void tapdisk_vbd_debug(td_vbd_t *);
|
||||
|
||||
void tapdisk_vbd_complete_vbd_request(td_vbd_t *, td_vbd_request_t *);
|
||||
|
||||
#endif
|
||||
@@ -1,169 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2007, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* Some notes on the tap_disk interface:
|
||||
*
|
||||
* tap_disk aims to provide a generic interface to easily implement new
|
||||
* types of image accessors. The structure-of-function-calls is similar
|
||||
* to disk interfaces used in qemu/denali/etc, with the significant
|
||||
* difference being the expectation of asynchronous rather than synchronous
|
||||
* I/O. The asynchronous interface is intended to allow lots of requests to
|
||||
* be pipelined through a disk, without the disk requiring any of its own
|
||||
* threads of control. As such, a batch of requests is delivered to the disk
|
||||
* using:
|
||||
*
|
||||
* td_queue_[read,write]()
|
||||
*
|
||||
* and passing in a completion callback, which the disk is responsible for
|
||||
* tracking. Disks should transform these requests as necessary and return
|
||||
* the resulting iocbs to tapdisk using td_prep_[read,write]() and
|
||||
* td_queue_tiocb().
|
||||
*
|
||||
* NOTE: tapdisk uses the number of sectors submitted per request as a
|
||||
* ref count. Plugins must use the callback function to communicate the
|
||||
* completion -- or error -- of every sector submitted to them.
|
||||
*
|
||||
* td_get_parent_id returns:
|
||||
* 0 if parent id successfully retrieved
|
||||
* TD_NO_PARENT if no parent exists
|
||||
* -errno on error
|
||||
*/
|
||||
|
||||
#ifndef _TAPDISK_H_
|
||||
#define _TAPDISK_H_
|
||||
|
||||
#include <time.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "list.h"
|
||||
#include "blktaplib.h"
|
||||
#include "tapdisk-log.h"
|
||||
#include "tapdisk-utils.h"
|
||||
|
||||
#ifdef MEMSHR
|
||||
#include "memshr.h"
|
||||
#endif
|
||||
|
||||
#define DPRINTF(_f, _a...) syslog(LOG_INFO, _f, ##_a)
|
||||
#define EPRINTF(_f, _a...) syslog(LOG_ERR, "tap-err:%s: " _f, __func__, ##_a)
|
||||
#define PERROR(_f, _a...) EPRINTF(_f ": %s", ##_a, strerror(errno))
|
||||
|
||||
#define MAX_SEGMENTS_PER_REQ 11
|
||||
#define SECTOR_SHIFT 9
|
||||
#define DEFAULT_SECTOR_SIZE 512
|
||||
|
||||
#define TAPDISK_DATA_REQUESTS (MAX_REQUESTS * MAX_SEGMENTS_PER_REQ)
|
||||
|
||||
//#define BLK_NOT_ALLOCATED (-99)
|
||||
#define TD_NO_PARENT 1
|
||||
|
||||
#define MAX_RAMDISK_SIZE 1024000 /*500MB disk limit*/
|
||||
|
||||
#define TD_OP_READ 0
|
||||
#define TD_OP_WRITE 1
|
||||
|
||||
#define TD_OPEN_QUIET 0x00001
|
||||
#define TD_OPEN_QUERY 0x00002
|
||||
#define TD_OPEN_RDONLY 0x00004
|
||||
#define TD_OPEN_STRICT 0x00008
|
||||
#define TD_OPEN_SHAREABLE 0x00010
|
||||
#define TD_OPEN_ADD_CACHE 0x00020
|
||||
#define TD_OPEN_VHD_INDEX 0x00040
|
||||
#define TD_OPEN_LOG_DIRTY 0x00080
|
||||
|
||||
#define TD_CREATE_SPARSE 0x00001
|
||||
#define TD_CREATE_MULTITYPE 0x00002
|
||||
|
||||
#define td_flag_set(word, flag) ((word) |= (flag))
|
||||
#define td_flag_clear(word, flag) ((word) &= ~(flag))
|
||||
#define td_flag_test(word, flag) ((word) & (flag))
|
||||
|
||||
typedef uint16_t td_uuid_t;
|
||||
typedef uint32_t td_flag_t;
|
||||
typedef uint64_t td_sector_t;
|
||||
typedef struct td_disk_id td_disk_id_t;
|
||||
typedef struct td_disk_info td_disk_info_t;
|
||||
typedef struct td_request td_request_t;
|
||||
typedef struct td_driver_handle td_driver_t;
|
||||
typedef struct td_image_handle td_image_t;
|
||||
|
||||
struct td_disk_id {
|
||||
char *name;
|
||||
int drivertype;
|
||||
};
|
||||
|
||||
struct td_disk_info {
|
||||
td_sector_t size;
|
||||
uint64_t sector_size;
|
||||
uint32_t info;
|
||||
};
|
||||
|
||||
struct td_request {
|
||||
int op;
|
||||
char *buf;
|
||||
td_sector_t sec;
|
||||
int secs;
|
||||
|
||||
uint8_t blocked; /* blocked on a dependency */
|
||||
|
||||
td_image_t *image;
|
||||
|
||||
void * /*td_callback_t*/ cb;
|
||||
void *cb_data;
|
||||
|
||||
uint64_t id;
|
||||
int sidx;
|
||||
void *private;
|
||||
|
||||
#ifdef MEMSHR
|
||||
share_tuple_t memshr_hnd;
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* Prototype of the callback to activate as requests complete.
|
||||
*/
|
||||
typedef void (*td_callback_t)(td_request_t, int);
|
||||
|
||||
/*
|
||||
* Structure describing the interface to a virtual disk implementation.
|
||||
* See note at the top of this file describing this interface.
|
||||
*/
|
||||
struct tap_disk {
|
||||
const char *disk_type;
|
||||
td_flag_t flags;
|
||||
int private_data_size;
|
||||
int (*td_open) (td_driver_t *, const char *, td_flag_t);
|
||||
int (*td_close) (td_driver_t *);
|
||||
int (*td_get_parent_id) (td_driver_t *, td_disk_id_t *);
|
||||
int (*td_validate_parent) (td_driver_t *, td_driver_t *, td_flag_t);
|
||||
void (*td_queue_read) (td_driver_t *, td_request_t);
|
||||
void (*td_queue_write) (td_driver_t *, td_request_t);
|
||||
void (*td_debug) (td_driver_t *);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,138 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#ifdef MEMSHR
|
||||
#include <memshr.h>
|
||||
#endif
|
||||
|
||||
#include "tapdisk.h"
|
||||
#include "tapdisk-utils.h"
|
||||
#include "tapdisk-server.h"
|
||||
#include "tapdisk-control.h"
|
||||
|
||||
static void
|
||||
usage(const char *app, int err)
|
||||
{
|
||||
fprintf(stderr, "usage: %s [-D] <-u uuid> <-c control socket>\n", app);
|
||||
exit(err);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
char *control;
|
||||
int c, err, nodaemon;
|
||||
|
||||
control = NULL;
|
||||
nodaemon = 0;
|
||||
|
||||
while ((c = getopt(argc, argv, "s:Dh")) != -1) {
|
||||
switch (c) {
|
||||
case 'D':
|
||||
nodaemon = 1;
|
||||
break;
|
||||
case 'h':
|
||||
usage(argv[0], 0);
|
||||
break;
|
||||
case 's':
|
||||
#ifdef MEMSHR
|
||||
memshr_set_domid(atoi(optarg));
|
||||
#else
|
||||
fprintf(stderr, "MEMSHR support not compiled in.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
usage(argv[0], EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
if (optind != argc)
|
||||
usage(argv[0], EINVAL);
|
||||
|
||||
if (chdir("/")) {
|
||||
DPRINTF("failed to chdir(/): %d\n", errno);
|
||||
err = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
tapdisk_start_logging("tapdisk2");
|
||||
|
||||
err = tapdisk_server_init();
|
||||
if (err) {
|
||||
DPRINTF("failed to initialize server: %d\n", err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!nodaemon) {
|
||||
err = daemon(0, 1);
|
||||
if (err) {
|
||||
DPRINTF("failed to daemonize: %d\n", errno);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
err = tapdisk_control_open(&control);
|
||||
if (err) {
|
||||
DPRINTF("failed to open control socket: %d\n", err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
fprintf(stdout, "%s\n", control);
|
||||
fflush(stdout);
|
||||
|
||||
if (!nodaemon) {
|
||||
int fd;
|
||||
|
||||
fd = open("/dev/null", O_RDWR);
|
||||
if (fd != -1) {
|
||||
dup2(fd, STDIN_FILENO);
|
||||
dup2(fd, STDOUT_FILENO);
|
||||
dup2(fd, STDERR_FILENO);
|
||||
if (fd > 2)
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
err = tapdisk_server_complete();
|
||||
if (err) {
|
||||
DPRINTF("failed to complete server: %d\n", err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = tapdisk_server_run();
|
||||
|
||||
out:
|
||||
tapdisk_control_close();
|
||||
tapdisk_stop_logging();
|
||||
return err;
|
||||
}
|
||||
@@ -1,691 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/resource.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "libvhd.h"
|
||||
#include "vhd-util.h"
|
||||
#include "tapdisk-utils.h"
|
||||
|
||||
#if 1
|
||||
#define DFPRINTF(_f, _a...) fprintf ( stdout, _f , ## _a )
|
||||
#else
|
||||
#define DFPRINTF(_f, _a...) ((void)0)
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
TD_FIELD_HIDDEN = 0,
|
||||
TD_FIELD_INVALID = 1
|
||||
} td_field_t;
|
||||
|
||||
struct vdi_field {
|
||||
char *name;
|
||||
td_field_t id;
|
||||
};
|
||||
|
||||
static struct vdi_field td_vdi_fields[TD_FIELD_INVALID] = {
|
||||
{ .id = TD_FIELD_HIDDEN, .name = "hidden" }
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
TD_CMD_CREATE = 0,
|
||||
TD_CMD_SNAPSHOT,
|
||||
/* TD_CMD_COALESCE, */
|
||||
TD_CMD_QUERY,
|
||||
/* TD_CMD_RESIZE, */
|
||||
TD_CMD_SET,
|
||||
/* TD_CMD_REPAIR, */
|
||||
/* TD_CMD_FILL, */
|
||||
/* TD_CMD_READ, */
|
||||
TD_CMD_INVALID,
|
||||
} td_command_t;
|
||||
|
||||
struct command {
|
||||
td_command_t id;
|
||||
char *name;
|
||||
int needs_type;
|
||||
};
|
||||
|
||||
struct command commands[TD_CMD_INVALID] = {
|
||||
{ .id = TD_CMD_CREATE, .name = "create", .needs_type = 1 },
|
||||
{ .id = TD_CMD_SNAPSHOT, .name = "snapshot", .needs_type = 1 },
|
||||
/* { .id = TD_CMD_COALESCE, .name = "coalesce", .needs_type = 1 }, */
|
||||
{ .id = TD_CMD_QUERY, .name = "query", .needs_type = 1 },
|
||||
/* { .id = TD_CMD_RESIZE, .name = "resize", .needs_type = 1 }, */
|
||||
{ .id = TD_CMD_SET, .name = "set", .needs_type = 1 },
|
||||
/* { .id = TD_CMD_REPAIR, .name = "repair", .needs_type = 1 }, */
|
||||
/* { .id = TD_CMD_FILL, .name = "fill", .needs_type = 1 }, */
|
||||
/* { .id = TD_CMD_READ, .name = "read", .needs_type = 1 }, */
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
TD_TYPE_VHD = 0,
|
||||
TD_TYPE_AIO,
|
||||
TD_TYPE_INVALID,
|
||||
} td_disk_t;
|
||||
|
||||
const char *td_disk_types[TD_TYPE_INVALID] = {
|
||||
"vhd",
|
||||
"aio",
|
||||
};
|
||||
|
||||
#define print_commands() \
|
||||
do { \
|
||||
int i; \
|
||||
fprintf(stderr, "COMMAND := { "); \
|
||||
fprintf(stderr, "%s", commands[0].name); \
|
||||
for (i = 1; i < TD_CMD_INVALID; i++) \
|
||||
fprintf(stderr, " | %s", commands[i].name); \
|
||||
fprintf(stderr, " }\n"); \
|
||||
} while (0)
|
||||
|
||||
#define print_disk_types() \
|
||||
do { \
|
||||
int i; \
|
||||
fprintf(stderr, "TYPE := { "); \
|
||||
fprintf(stderr, "%s", td_disk_types[0]); \
|
||||
for (i = 1; i < TD_TYPE_INVALID; i++) \
|
||||
fprintf(stderr, " | %s", td_disk_types[i]); \
|
||||
fprintf(stderr, " }\n"); \
|
||||
} while (0);
|
||||
|
||||
#define print_field_names() \
|
||||
do { \
|
||||
int i; \
|
||||
fprintf(stderr, "FIELD := { "); \
|
||||
fprintf(stderr, "%s", td_vdi_fields[0].name); \
|
||||
for (i = 1; i < TD_FIELD_INVALID; i++) \
|
||||
fprintf(stderr, " | %s", td_vdi_fields[i].name); \
|
||||
fprintf(stderr, " }\n"); \
|
||||
} while (0)
|
||||
|
||||
void
|
||||
help(void)
|
||||
{
|
||||
fprintf(stderr, "Tapdisk Utilities: v1.0.0\n");
|
||||
fprintf(stderr, "usage: td-util COMMAND [TYPE] [OPTIONS]\n");
|
||||
print_commands();
|
||||
print_disk_types();
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
struct command *
|
||||
get_command(char *command)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < TD_CMD_INVALID; i++)
|
||||
if (!strcmp(command, commands[i].name))
|
||||
return &commands[i];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct vdi_field *
|
||||
get_field(char *field)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < TD_FIELD_INVALID; i++)
|
||||
if (!strcmp(field, td_vdi_fields[i].name))
|
||||
return &td_vdi_fields[i];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
get_driver_type(char *type)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (strnlen(type, 25) >= 25)
|
||||
return -ENAMETOOLONG;
|
||||
|
||||
for (i = 0; i < TD_TYPE_INVALID; i++)
|
||||
if (!strcmp(type, td_disk_types[i]))
|
||||
return i;
|
||||
|
||||
return -TD_TYPE_INVALID;
|
||||
}
|
||||
|
||||
int
|
||||
td_create(int type, int argc, char *argv[])
|
||||
{
|
||||
ssize_t mb;
|
||||
uint64_t size;
|
||||
char *name, *buf;
|
||||
int c, i, fd, sparse = 1, fixedsize = 0;
|
||||
|
||||
while ((c = getopt(argc, argv, "hrb")) != -1) {
|
||||
switch(c) {
|
||||
case 'r':
|
||||
sparse = 0;
|
||||
break;
|
||||
case 'b':
|
||||
fixedsize = 1;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unknown option %c\n", (char)c);
|
||||
case 'h':
|
||||
goto usage;
|
||||
}
|
||||
}
|
||||
|
||||
if (optind != (argc - 2))
|
||||
goto usage;
|
||||
|
||||
mb = 1 << 20;
|
||||
size = atoi(argv[optind++]);
|
||||
size = size << 20;
|
||||
name = argv[optind];
|
||||
|
||||
if (strnlen(name, MAX_NAME_LEN) == MAX_NAME_LEN) {
|
||||
fprintf(stderr, "Device name too long\n");
|
||||
return ENAMETOOLONG;
|
||||
}
|
||||
|
||||
if (type == TD_TYPE_VHD) {
|
||||
int cargc = 0;
|
||||
char sbuf[32], *cargv[10];
|
||||
|
||||
size >>= 20;
|
||||
|
||||
memset(cargv, 0, sizeof(cargv));
|
||||
snprintf(sbuf, sizeof(sbuf) - 1, "%"PRIu64, size);
|
||||
cargv[cargc++] = "create";
|
||||
cargv[cargc++] = "-n";
|
||||
cargv[cargc++] = name;
|
||||
cargv[cargc++] = "-s";
|
||||
cargv[cargc++] = sbuf;
|
||||
if (!sparse)
|
||||
cargv[cargc++] = "-r";
|
||||
if (fixedsize)
|
||||
cargv[cargc++] = "-b";
|
||||
|
||||
return vhd_util_create(cargc, cargv);
|
||||
}
|
||||
|
||||
/* generic create */
|
||||
if (sparse) {
|
||||
fprintf(stderr, "Cannot create sparse %s image\n",
|
||||
td_disk_types[type]);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
buf = calloc(1, mb);
|
||||
if (!buf)
|
||||
return ENOMEM;
|
||||
|
||||
fd = open(name, O_WRONLY | O_DIRECT | O_CREAT | O_TRUNC, 0644);
|
||||
if (fd == -1) {
|
||||
free(buf);
|
||||
return errno;
|
||||
}
|
||||
|
||||
size >>= 20;
|
||||
for (i = 0; i < size; i++)
|
||||
if (write(fd, buf, mb) != mb) {
|
||||
close(fd);
|
||||
unlink(name);
|
||||
free(buf);
|
||||
return EIO;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
free(buf);
|
||||
return 0;
|
||||
|
||||
usage:
|
||||
fprintf(stderr, "usage: td-util create %s [-h help] [-r reserve] "
|
||||
"[-b file_is_fixed_size] <SIZE(MB)> <FILENAME>\n",
|
||||
td_disk_types[type]);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
int
|
||||
td_snapshot(int type, int argc, char *argv[])
|
||||
{
|
||||
char *cargv[10];
|
||||
int c, err, cargc;
|
||||
struct stat stats;
|
||||
char *name, *backing, *limit = NULL;
|
||||
int fixedsize = 0, rawparent = 0;
|
||||
|
||||
if (type != TD_TYPE_VHD) {
|
||||
fprintf(stderr, "Cannot create snapshot of %s image type\n",
|
||||
td_disk_types[type]);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
while ((c = getopt(argc, argv, "hbml:")) != -1) {
|
||||
switch(c) {
|
||||
case 'b':
|
||||
fixedsize = 1;
|
||||
break;
|
||||
case 'm':
|
||||
rawparent = 1;
|
||||
break;
|
||||
case 'l':
|
||||
limit = optarg;
|
||||
break;
|
||||
case 'h':
|
||||
err = 0;
|
||||
goto usage;
|
||||
default:
|
||||
err = EINVAL;
|
||||
goto usage;
|
||||
}
|
||||
}
|
||||
|
||||
if (optind != (argc - 2)) {
|
||||
err = EINVAL;
|
||||
goto usage;
|
||||
}
|
||||
|
||||
name = argv[optind++];
|
||||
backing = argv[optind++];
|
||||
|
||||
if (strnlen(name, MAX_NAME_LEN) == MAX_NAME_LEN ||
|
||||
strnlen(backing, MAX_NAME_LEN) == MAX_NAME_LEN) {
|
||||
fprintf(stderr, "Device name too long\n");
|
||||
return ENAMETOOLONG;
|
||||
}
|
||||
|
||||
if (stat(backing, &stats) == -1) {
|
||||
fprintf(stderr, "File %s not found\n", backing);
|
||||
return errno;
|
||||
}
|
||||
|
||||
cargc = 0;
|
||||
memset(cargv, 0, sizeof(cargv));
|
||||
cargv[cargc++] = "snapshot";
|
||||
cargv[cargc++] = "-n";
|
||||
cargv[cargc++] = name;
|
||||
cargv[cargc++] = "-p";
|
||||
cargv[cargc++] = backing;
|
||||
if (fixedsize)
|
||||
cargv[cargc++] = "-b";
|
||||
if (rawparent)
|
||||
cargv[cargc++] = "-m";
|
||||
if (limit) {
|
||||
cargv[cargc++] = "-l";
|
||||
cargv[cargc++] = limit;
|
||||
}
|
||||
return vhd_util_snapshot(cargc, cargv);
|
||||
|
||||
usage:
|
||||
fprintf(stderr, "usage: td-util snapshot %s [-h help] [-m parent_raw] "
|
||||
"[-b file_is_fixed_size] [-l snapshot depth limit] "
|
||||
"<FILENAME> <BACKING_FILENAME>\n", td_disk_types[type]);
|
||||
return err;
|
||||
}
|
||||
|
||||
int
|
||||
td_coalesce(int type, int argc, char *argv[])
|
||||
{
|
||||
int c, ret, cargc;
|
||||
char *name, *pname, *cargv[3];
|
||||
|
||||
if (type != TD_TYPE_VHD) {
|
||||
fprintf(stderr, "Cannot create snapshot of %s image type\n",
|
||||
td_disk_types[type]);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
while ((c = getopt(argc, argv, "h")) != -1) {
|
||||
switch(c) {
|
||||
default:
|
||||
fprintf(stderr, "Unknown option %c\n", (char)c);
|
||||
case 'h':
|
||||
goto usage;
|
||||
}
|
||||
}
|
||||
|
||||
if (optind != (argc - 1))
|
||||
goto usage;
|
||||
|
||||
name = argv[optind++];
|
||||
|
||||
if (strnlen(name, MAX_NAME_LEN) == MAX_NAME_LEN) {
|
||||
fprintf(stderr, "Device name too long\n");
|
||||
return ENAMETOOLONG;
|
||||
}
|
||||
|
||||
cargc = 0;
|
||||
memset(cargv, 0, sizeof(cargv));
|
||||
cargv[cargc++] = "coalesce";
|
||||
cargv[cargc++] = "-n";
|
||||
cargv[cargc++] = name;
|
||||
ret = vhd_util_coalesce(cargc, cargv);
|
||||
if (ret)
|
||||
printf("coalesce failed: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
|
||||
usage:
|
||||
fprintf(stderr, "usage: td-util coalesce %s [-h help] "
|
||||
"<FILENAME>\n", td_disk_types[type]);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
int
|
||||
td_query(int type, int argc, char *argv[])
|
||||
{
|
||||
char *name;
|
||||
int c, size = 0, parent = 0, fields = 0, depth = 0, err = 0;
|
||||
|
||||
while ((c = getopt(argc, argv, "hvpfd")) != -1) {
|
||||
switch(c) {
|
||||
case 'v':
|
||||
size = 1;
|
||||
break;
|
||||
case 'p':
|
||||
parent = 1;
|
||||
break;
|
||||
case 'f':
|
||||
fields = 1;
|
||||
break;
|
||||
case 'd':
|
||||
depth = 1;
|
||||
break;
|
||||
case 'h':
|
||||
err = 0;
|
||||
goto usage;
|
||||
default:
|
||||
err = EINVAL;
|
||||
goto usage;
|
||||
}
|
||||
}
|
||||
|
||||
if (optind != (argc - 1)) {
|
||||
err = EINVAL;
|
||||
goto usage;
|
||||
}
|
||||
|
||||
name = argv[optind++];
|
||||
|
||||
if (strnlen(name, MAX_NAME_LEN) == MAX_NAME_LEN) {
|
||||
fprintf(stderr, "Device name too long\n");
|
||||
return ENAMETOOLONG;
|
||||
}
|
||||
|
||||
if (type == TD_TYPE_VHD) {
|
||||
vhd_context_t vhd;
|
||||
|
||||
err = vhd_open(&vhd, name, VHD_OPEN_RDONLY);
|
||||
if (err) {
|
||||
printf("failed opening %s: %d\n", name, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (size)
|
||||
printf("%"PRIu64"\n", vhd.footer.curr_size >> 20);
|
||||
|
||||
if (parent) {
|
||||
if (vhd.footer.type != HD_TYPE_DIFF)
|
||||
printf("%s has no parent\n", name);
|
||||
else {
|
||||
char *pname;
|
||||
|
||||
err = vhd_parent_locator_get(&vhd, &pname);
|
||||
if (err)
|
||||
printf("failed getting parent: %d\n",
|
||||
err);
|
||||
else {
|
||||
printf("%s\n", pname);
|
||||
free(pname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fields) {
|
||||
int ret, hidden;
|
||||
|
||||
ret = vhd_hidden(&vhd, &hidden);
|
||||
if (ret) {
|
||||
printf("failed checking 'hidden' field: %d\n",
|
||||
ret);
|
||||
err = (err ? : ret);
|
||||
} else
|
||||
printf("%s: %d\n",
|
||||
td_vdi_fields[TD_FIELD_HIDDEN].name,
|
||||
hidden);
|
||||
}
|
||||
|
||||
if (depth) {
|
||||
int ret, length;
|
||||
|
||||
ret = vhd_chain_depth(&vhd, &length);
|
||||
if (ret)
|
||||
printf("error checking chain depth: %d\n", ret);
|
||||
else
|
||||
printf("chain depth: %d\n", length);
|
||||
|
||||
err = (err ? : ret);
|
||||
}
|
||||
|
||||
vhd_close(&vhd);
|
||||
|
||||
} else if (type == TD_TYPE_AIO) {
|
||||
if (size) {
|
||||
int fd;
|
||||
uint64_t secs;
|
||||
uint32_t ssize;
|
||||
|
||||
fd = open(name, O_RDONLY | O_LARGEFILE);
|
||||
if (fd == -1) {
|
||||
printf("failed opening %s: %d\n", name, errno);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
err = tapdisk_get_image_size(fd, &secs, &ssize);
|
||||
close(fd);
|
||||
|
||||
if (err) {
|
||||
printf("failed getting size for %s: %d\n:",
|
||||
name, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
printf("%"PRIu64"\n", secs >> 11);
|
||||
}
|
||||
|
||||
if (parent)
|
||||
printf("%s has no parent\n", name);
|
||||
|
||||
if (fields) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < TD_FIELD_INVALID; i++)
|
||||
printf("%s: 0\n", td_vdi_fields[i].name);
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
|
||||
usage:
|
||||
fprintf(stderr, "usage: td-util query %s [-h help] [-v virtsize] "
|
||||
"[-p parent] [-f fields] <FILENAME>\n", td_disk_types[type]);
|
||||
return err;
|
||||
}
|
||||
|
||||
int
|
||||
td_set_field(int type, int argc, char *argv[])
|
||||
{
|
||||
int ret, i, c, cargc;
|
||||
struct vdi_field *field;
|
||||
char *name, *value, *cargv[7];
|
||||
|
||||
if (type != TD_TYPE_VHD) {
|
||||
fprintf(stderr, "Cannot set fields of %s images\n",
|
||||
td_disk_types[type]);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
while ((c = getopt(argc, argv, "h")) != -1) {
|
||||
switch(c) {
|
||||
default:
|
||||
fprintf(stderr, "Unknown option %c\n", (char)c);
|
||||
case 'h':
|
||||
goto usage;
|
||||
}
|
||||
}
|
||||
|
||||
if (optind != (argc - 3))
|
||||
goto usage;
|
||||
|
||||
name = argv[optind++];
|
||||
|
||||
field = get_field(argv[optind]);
|
||||
if (!field || field->id != TD_FIELD_HIDDEN) {
|
||||
fprintf(stderr, "Invalid field %s\n", argv[optind]);
|
||||
goto usage;
|
||||
}
|
||||
|
||||
value = argv[++optind];
|
||||
|
||||
cargc = 0;
|
||||
memset(cargv, 0, sizeof(cargv));
|
||||
cargv[cargc++] = "set";
|
||||
cargv[cargc++] = "-n";
|
||||
cargv[cargc++] = name;
|
||||
cargv[cargc++] = "-f";
|
||||
cargv[cargc++] = field->name;
|
||||
cargv[cargc++] = "-v";
|
||||
cargv[cargc++] = value;
|
||||
return vhd_util_set_field(cargc, cargv);
|
||||
|
||||
usage:
|
||||
fprintf(stderr, "usage: td-util set %s [-h help] "
|
||||
"<FILENAME> <FIELD> <VALUE>\n", td_disk_types[type]);
|
||||
print_field_names();
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
char **cargv;
|
||||
struct command *cmd;
|
||||
int cargc, i, type = -1, ret = 0;
|
||||
|
||||
#ifdef CORE_DUMP
|
||||
struct rlimit rlim;
|
||||
rlim.rlim_cur = RLIM_INFINITY;
|
||||
rlim.rlim_max = RLIM_INFINITY;
|
||||
if (setrlimit(RLIMIT_CORE, &rlim) < 0)
|
||||
fprintf(stderr, "setrlimit failed: %d\n", errno);
|
||||
#endif
|
||||
|
||||
if (argc < 2)
|
||||
help();
|
||||
|
||||
cargc = argc - 1;
|
||||
cmd = get_command(argv[1]);
|
||||
if (!cmd) {
|
||||
fprintf(stderr, "invalid COMMAND %s\n", argv[1]);
|
||||
help();
|
||||
}
|
||||
|
||||
if (cmd->needs_type) {
|
||||
if (argc < 3) {
|
||||
fprintf(stderr, "td-util %s requires a TYPE\n",
|
||||
cmd->name);
|
||||
print_disk_types();
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
type = get_driver_type(argv[2]);
|
||||
if (type < 0) {
|
||||
fprintf(stderr, "invalid TYPE '%s'.\n", argv[2]);
|
||||
print_disk_types();
|
||||
exit(-1);
|
||||
}
|
||||
--cargc;
|
||||
}
|
||||
|
||||
cargv = malloc(sizeof(char *) * cargc);
|
||||
if (!cargv)
|
||||
exit(ENOMEM);
|
||||
|
||||
cargv[0] = cmd->name;
|
||||
for (i = 1; i < cargc; i++)
|
||||
cargv[i] = argv[i + (argc - cargc)];
|
||||
|
||||
switch(cmd->id) {
|
||||
case TD_CMD_CREATE:
|
||||
ret = td_create(type, cargc, cargv);
|
||||
break;
|
||||
case TD_CMD_SNAPSHOT:
|
||||
ret = td_snapshot(type, cargc, cargv);
|
||||
break;
|
||||
/*
|
||||
case TD_CMD_COALESCE:
|
||||
ret = td_coalesce(type, cargc, cargv);
|
||||
break;
|
||||
*/
|
||||
case TD_CMD_QUERY:
|
||||
ret = td_query(type, cargc, cargv);
|
||||
break;
|
||||
/*
|
||||
case TD_CMD_RESIZE:
|
||||
ret = td_resize(type, cargc, cargv);
|
||||
break;
|
||||
*/
|
||||
case TD_CMD_SET:
|
||||
ret = td_set_field(type, cargc, cargv);
|
||||
break;
|
||||
/*
|
||||
case TD_CMD_REPAIR:
|
||||
ret = td_repair(type, cargc, cargv);
|
||||
break;
|
||||
case TD_CMD_FILL:
|
||||
ret = td_fill(type, cargc, cargv);
|
||||
break;
|
||||
case TD_CMD_READ:
|
||||
ret = td_read(type, cargc, cargv);
|
||||
break;
|
||||
*/
|
||||
default:
|
||||
case TD_CMD_INVALID:
|
||||
ret = EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
free(cargv);
|
||||
|
||||
return (ret >= 0 ? ret : -ret);
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
usage () { echo "USAGE: xmsnap <VM ID> <Backing File>"; }
|
||||
|
||||
#
|
||||
# Check Usage
|
||||
#
|
||||
if [ -n "$1" ]
|
||||
then
|
||||
vmid=$1
|
||||
else
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -n "$2" ]
|
||||
then
|
||||
target=$2
|
||||
else
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -e "$target" ]
|
||||
then
|
||||
echo "Creating snapshot of file $target for VM $vmid."
|
||||
else
|
||||
usage
|
||||
echo "File $target not found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
#
|
||||
# Find the snapshot name
|
||||
#
|
||||
directory=`dirname "$target"`
|
||||
target=`basename "$target"`
|
||||
|
||||
let maxidx=0
|
||||
if [ -e $directory/${target}.snap1 ]
|
||||
then
|
||||
for idx in $(ls $directory/${target}.snap*)
|
||||
do
|
||||
let idx=${idx#$directory/${target}.snap}
|
||||
if [ "$idx" -gt "$maxidx" ]
|
||||
then
|
||||
let maxidx=$idx
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
snap=${target}.snap`expr $maxidx + 1`
|
||||
|
||||
#
|
||||
# Pause VM
|
||||
#
|
||||
xm pause $vmid
|
||||
if [ "$?" -ne "0" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
#
|
||||
# Snap and reposition the files
|
||||
#
|
||||
mv $directory/$target $directory/$snap
|
||||
if [ "$?" -ne "0" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
qcow-create 0 $directory/$target $directory/$snap
|
||||
|
||||
#
|
||||
# Unpause
|
||||
#
|
||||
xm unpause $vmid
|
||||
|
||||
exit
|
||||
@@ -1,17 +0,0 @@
|
||||
XEN_ROOT := $(CURDIR)/../../..
|
||||
include $(XEN_ROOT)/tools/Rules.mk
|
||||
|
||||
.PHONY: all
|
||||
all:
|
||||
|
||||
.PHONY: install
|
||||
install:
|
||||
$(INSTALL_DIR) -p $(DESTDIR)$(includedir)
|
||||
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@:
|
||||
|
||||
.PHONY: distclean
|
||||
distclean: clean
|
||||
@@ -1,33 +0,0 @@
|
||||
/* $OpenBSD: atomicio.h,v 1.6 2005/05/24 17:32:43 avsm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1995,1999 Theo de Raadt. All rights reserved.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Ensure all of data on socket comes through. f==read || f==vwrite
|
||||
*/
|
||||
size_t atomicio(ssize_t (*)(int, void *, size_t), int, void *, size_t);
|
||||
|
||||
#define vwrite (ssize_t (*)(int, void *, size_t))write
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef _BLKTAP_2_H_
|
||||
#define _BLKTAP_2_H_
|
||||
|
||||
#define BLKTAP2_MAX_MESSAGE_LEN 256
|
||||
|
||||
#define BLKTAP2_RING_MESSAGE_PAUSE 1
|
||||
#define BLKTAP2_RING_MESSAGE_RESUME 2
|
||||
#define BLKTAP2_RING_MESSAGE_CLOSE 3
|
||||
|
||||
#define BLKTAP2_IOCTL_KICK_FE 1
|
||||
#define BLKTAP2_IOCTL_ALLOC_TAP 200
|
||||
#define BLKTAP2_IOCTL_FREE_TAP 201
|
||||
#define BLKTAP2_IOCTL_CREATE_DEVICE 202
|
||||
#define BLKTAP2_IOCTL_SET_PARAMS 203
|
||||
#define BLKTAP2_IOCTL_PAUSE 204
|
||||
#define BLKTAP2_IOCTL_REOPEN 205
|
||||
#define BLKTAP2_IOCTL_RESUME 206
|
||||
|
||||
#define BLKTAP2_SYSFS_DIR "/sys/class/blktap2"
|
||||
#define BLKTAP2_CONTROL_NAME "blktap-control"
|
||||
#define BLKTAP2_CONTROL_DIR "/var/run/"BLKTAP2_CONTROL_NAME
|
||||
#define BLKTAP2_CONTROL_SOCKET "ctl"
|
||||
#define BLKTAP2_DIRECTORY "/dev/xen/blktap-2"
|
||||
#define BLKTAP2_CONTROL_DEVICE BLKTAP2_DIRECTORY"/control"
|
||||
#define BLKTAP2_RING_DEVICE BLKTAP2_DIRECTORY"/blktap"
|
||||
#define BLKTAP2_IO_DEVICE BLKTAP2_DIRECTORY"/tapdev"
|
||||
|
||||
struct blktap2_handle {
|
||||
unsigned int ring;
|
||||
unsigned int device;
|
||||
unsigned int minor;
|
||||
};
|
||||
|
||||
struct blktap2_params {
|
||||
char name[BLKTAP2_MAX_MESSAGE_LEN];
|
||||
unsigned long long capacity;
|
||||
unsigned long sector_size;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,242 +0,0 @@
|
||||
/* blktaplib.h
|
||||
*
|
||||
* Blktap library userspace code.
|
||||
*
|
||||
* Copyright (c) 2007, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __BLKTAPLIB_H__
|
||||
#define __BLKTAPLIB_H__
|
||||
|
||||
#include <syslog.h>
|
||||
#include <sys/time.h>
|
||||
#include <xenctrl.h>
|
||||
#include <xen/io/blkif.h>
|
||||
|
||||
#define BLK_RING_SIZE __CONST_RING_SIZE(blkif, XC_PAGE_SIZE)
|
||||
|
||||
/* size of the extra VMA area to map in attached pages. */
|
||||
#define BLKTAP_VMA_PAGES BLK_RING_SIZE
|
||||
|
||||
/* blktap IOCTLs: These must correspond with the blktap driver ioctls */
|
||||
#define BLKTAP_IOCTL_KICK_FE 1
|
||||
#define BLKTAP_IOCTL_KICK_BE 2
|
||||
#define BLKTAP_IOCTL_SETMODE 3
|
||||
#define BLKTAP_IOCTL_SENDPID 4
|
||||
#define BLKTAP_IOCTL_NEWINTF 5
|
||||
#define BLKTAP_IOCTL_MINOR 6
|
||||
#define BLKTAP_IOCTL_MAJOR 7
|
||||
#define BLKTAP_QUERY_ALLOC_REQS 8
|
||||
#define BLKTAP_IOCTL_FREEINTF 9
|
||||
#define BLKTAP_IOCTL_PRINT_IDXS 100
|
||||
#define BLKTAP_IOCTL_BACKDEV_SETUP 200
|
||||
|
||||
#define PRIO_SPECIAL_IO -9999
|
||||
|
||||
/* blktap switching modes: (Set with BLKTAP_IOCTL_SETMODE) */
|
||||
#define BLKTAP_MODE_PASSTHROUGH 0x00000000 /* default */
|
||||
#define BLKTAP_MODE_INTERCEPT_FE 0x00000001
|
||||
#define BLKTAP_MODE_INTERCEPT_BE 0x00000002
|
||||
|
||||
#define BLKTAP_MODE_INTERPOSE \
|
||||
(BLKTAP_MODE_INTERCEPT_FE | BLKTAP_MODE_INTERCEPT_BE)
|
||||
|
||||
static inline int BLKTAP_MODE_VALID(unsigned long arg)
|
||||
{
|
||||
return (
|
||||
( arg == BLKTAP_MODE_PASSTHROUGH ) ||
|
||||
( arg == BLKTAP_MODE_INTERCEPT_FE ) ||
|
||||
( arg == BLKTAP_MODE_INTERPOSE ) );
|
||||
}
|
||||
|
||||
#define MAX_REQUESTS BLK_RING_SIZE
|
||||
|
||||
#define BLKTAP_IOCTL_KICK 1
|
||||
#define MAX_PENDING_REQS BLK_RING_SIZE
|
||||
#define BLKTAP_DEV_DIR "/dev/xen"
|
||||
#define BLKTAP_DEV_NAME "blktap"
|
||||
#define BACKDEV_NAME "backdev"
|
||||
#define BLKTAP_DEV_MINOR 0
|
||||
#define BLKTAP_CTRL_DIR "/var/run/tap"
|
||||
|
||||
extern int blktap_major;
|
||||
|
||||
#define BLKTAP_RING_PAGES 1 /* Front */
|
||||
#define BLKTAP_MMAP_REGION_SIZE (BLKTAP_RING_PAGES + MMAP_PAGES)
|
||||
|
||||
struct blkif;
|
||||
struct blkif_info;
|
||||
|
||||
typedef struct {
|
||||
blkif_request_t req;
|
||||
int submitting;
|
||||
int secs_pending;
|
||||
int16_t status;
|
||||
int num_retries;
|
||||
struct timeval last_try;
|
||||
} pending_req_t;
|
||||
|
||||
typedef struct blkif {
|
||||
domid_t domid;
|
||||
long int handle;
|
||||
|
||||
long int pdev;
|
||||
long int readonly;
|
||||
|
||||
enum { DISCONNECTED, DISCONNECTING, CONNECTED } state;
|
||||
|
||||
struct blkif_ops *ops;
|
||||
struct blkif *hash_next;
|
||||
|
||||
void *prv; /* device-specific data */
|
||||
struct blkif_info *info; /*Image parameter passing */
|
||||
pending_req_t pending_list[MAX_REQUESTS];
|
||||
int devnum;
|
||||
int fds[2];
|
||||
int be_id;
|
||||
char *backend_path;
|
||||
int major;
|
||||
int minor;
|
||||
pid_t tappid;
|
||||
int drivertype;
|
||||
uint16_t cookie;
|
||||
int err;
|
||||
} blkif_t;
|
||||
|
||||
typedef struct blkif_info {
|
||||
char *params;
|
||||
int readonly;
|
||||
int storage;
|
||||
} blkif_info_t;
|
||||
|
||||
typedef struct tapdev_info {
|
||||
int fd;
|
||||
char *mem;
|
||||
blkif_sring_t *sring;
|
||||
blkif_back_ring_t fe_ring;
|
||||
unsigned long vstart;
|
||||
blkif_t *blkif;
|
||||
} tapdev_info_t;
|
||||
|
||||
typedef struct domid_translate {
|
||||
unsigned short domid;
|
||||
unsigned short busid;
|
||||
} domid_translate_t ;
|
||||
|
||||
typedef struct image {
|
||||
unsigned long long size;
|
||||
unsigned long secsize;
|
||||
unsigned int info;
|
||||
} image_t;
|
||||
|
||||
typedef struct msg_hdr {
|
||||
uint16_t type;
|
||||
uint16_t len;
|
||||
uint16_t drivertype;
|
||||
uint16_t cookie;
|
||||
} msg_hdr_t;
|
||||
|
||||
typedef struct msg_params {
|
||||
uint8_t readonly;
|
||||
int path_off;
|
||||
int path_len;
|
||||
int storage;
|
||||
} msg_params_t;
|
||||
|
||||
typedef struct msg_newdev {
|
||||
uint8_t devnum;
|
||||
uint16_t domid;
|
||||
} msg_newdev_t;
|
||||
|
||||
typedef struct msg_pid {
|
||||
pid_t pid;
|
||||
} msg_pid_t;
|
||||
|
||||
typedef struct msg_cp {
|
||||
int cp_uuid_off;
|
||||
int cp_uuid_len;
|
||||
int cp_drivertype;
|
||||
} msg_cp_t;
|
||||
|
||||
typedef struct msg_lock {
|
||||
int ro;
|
||||
int enforce;
|
||||
int uuid_off;
|
||||
int uuid_len;
|
||||
} msg_lock_t;
|
||||
|
||||
#define READ 0
|
||||
#define WRITE 1
|
||||
|
||||
/*Control Messages between manager and tapdev*/
|
||||
#define CTLMSG_PARAMS 1
|
||||
#define CTLMSG_IMG 2
|
||||
#define CTLMSG_IMG_FAIL 3
|
||||
#define CTLMSG_NEWDEV 4
|
||||
#define CTLMSG_NEWDEV_RSP 5
|
||||
#define CTLMSG_NEWDEV_FAIL 6
|
||||
#define CTLMSG_CLOSE 7
|
||||
#define CTLMSG_CLOSE_RSP 8
|
||||
#define CTLMSG_PID 9
|
||||
#define CTLMSG_PID_RSP 10
|
||||
#define CTLMSG_CHECKPOINT 11
|
||||
#define CTLMSG_CHECKPOINT_RSP 12
|
||||
#define CTLMSG_LOCK 13
|
||||
#define CTLMSG_LOCK_RSP 14
|
||||
#define CTLMSG_PAUSE 15
|
||||
#define CTLMSG_PAUSE_RSP 16
|
||||
#define CTLMSG_RESUME 17
|
||||
#define CTLMSG_RESUME_RSP 18
|
||||
|
||||
#define TAPDISK_STORAGE_TYPE_NFS 1
|
||||
#define TAPDISK_STORAGE_TYPE_EXT 2
|
||||
#define TAPDISK_STORAGE_TYPE_LVM 3
|
||||
#define TAPDISK_STORAGE_TYPE_DEFAULT TAPDISK_STORAGE_TYPE_EXT
|
||||
|
||||
/* Abitrary values, must match the underlying driver... */
|
||||
#define MAX_TAP_DEV 256
|
||||
|
||||
/* Accessing attached data page mappings */
|
||||
#define MMAP_PAGES \
|
||||
(MAX_PENDING_REQS * BLKIF_MAX_SEGMENTS_PER_REQUEST)
|
||||
#define MMAP_VADDR(_vstart,_req,_seg) \
|
||||
((_vstart) + \
|
||||
((_req) * BLKIF_MAX_SEGMENTS_PER_REQUEST * getpagesize()) + \
|
||||
((_seg) * getpagesize()))
|
||||
|
||||
/* Defines that are only used by library clients */
|
||||
|
||||
#ifndef __COMPILING_BLKTAP_LIB
|
||||
|
||||
static char *blkif_op_name[] = {
|
||||
[BLKIF_OP_READ] = "READ",
|
||||
[BLKIF_OP_WRITE] = "WRITE",
|
||||
};
|
||||
|
||||
#endif /* __COMPILING_BLKTAP_LIB */
|
||||
|
||||
#endif /* __BLKTAPLIB_H__ */
|
||||
@@ -1,68 +0,0 @@
|
||||
/* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef _VHD_JOURNAL_H_
|
||||
#define _VHD_JOURNAL_H_
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "libvhd.h"
|
||||
|
||||
#define VHD_JOURNAL_METADATA 0x01
|
||||
#define VHD_JOURNAL_DATA 0x02
|
||||
|
||||
#define VHD_JOURNAL_HEADER_COOKIE "vjournal"
|
||||
#define VHD_JOURNAL_ENTRY_COOKIE 0xaaaa12344321aaaa
|
||||
|
||||
typedef struct vhd_journal_header {
|
||||
char cookie[8];
|
||||
vhd_uuid_t uuid;
|
||||
uint64_t vhd_footer_offset;
|
||||
uint32_t journal_data_entries;
|
||||
uint32_t journal_metadata_entries;
|
||||
uint64_t journal_data_offset;
|
||||
uint64_t journal_metadata_offset;
|
||||
uint64_t journal_eof;
|
||||
char pad[448];
|
||||
} vhd_journal_header_t;
|
||||
|
||||
typedef struct vhd_journal {
|
||||
char *jname;
|
||||
int jfd;
|
||||
int is_block; /* is jfd a block device */
|
||||
vhd_journal_header_t header;
|
||||
vhd_context_t vhd;
|
||||
} vhd_journal_t;
|
||||
|
||||
int vhd_journal_create(vhd_journal_t *, const char *file, const char *jfile);
|
||||
int vhd_journal_open(vhd_journal_t *, const char *file, const char *jfile);
|
||||
int vhd_journal_add_block(vhd_journal_t *, uint32_t block, char mode);
|
||||
int vhd_journal_commit(vhd_journal_t *);
|
||||
int vhd_journal_revert(vhd_journal_t *);
|
||||
int vhd_journal_close(vhd_journal_t *);
|
||||
int vhd_journal_remove(vhd_journal_t *);
|
||||
|
||||
#endif
|
||||
@@ -1,326 +0,0 @@
|
||||
/* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef _VHD_LIB_H_
|
||||
#define _VHD_LIB_H_
|
||||
|
||||
#include <string.h>
|
||||
#if defined(__linux__)
|
||||
#include <endian.h>
|
||||
#include <byteswap.h>
|
||||
#elif defined(__NetBSD__)
|
||||
#include <sys/endian.h>
|
||||
#include <sys/bswap.h>
|
||||
#endif
|
||||
|
||||
#include "vhd-uuid.h"
|
||||
#include "vhd.h"
|
||||
|
||||
#ifndef O_LARGEFILE
|
||||
#define O_LARGEFILE 0
|
||||
#endif
|
||||
|
||||
#if BYTE_ORDER == LITTLE_ENDIAN
|
||||
#if defined(__linux__)
|
||||
#define BE16_IN(foo) (*(foo)) = bswap_16(*(foo))
|
||||
#define BE32_IN(foo) (*(foo)) = bswap_32(*(foo))
|
||||
#define BE64_IN(foo) (*(foo)) = bswap_64(*(foo))
|
||||
#define BE16_OUT(foo) (*(foo)) = bswap_16(*(foo))
|
||||
#define BE32_OUT(foo) (*(foo)) = bswap_32(*(foo))
|
||||
#define BE64_OUT(foo) (*(foo)) = bswap_64(*(foo))
|
||||
#elif defined(__NetBSD__)
|
||||
#define BE16_IN(foo) (*(foo)) = bswap16(*(foo))
|
||||
#define BE32_IN(foo) (*(foo)) = bswap32(*(foo))
|
||||
#define BE64_IN(foo) (*(foo)) = bswap64(*(foo))
|
||||
#define BE16_OUT(foo) (*(foo)) = bswap16(*(foo))
|
||||
#define BE32_OUT(foo) (*(foo)) = bswap32(*(foo))
|
||||
#define BE64_OUT(foo) (*(foo)) = bswap64(*(foo))
|
||||
#endif
|
||||
#else
|
||||
#define BE16_IN(foo)
|
||||
#define BE32_IN(foo)
|
||||
#define BE64_IN(foo)
|
||||
#define BE32_OUT(foo)
|
||||
#define BE32_OUT(foo)
|
||||
#define BE64_OUT(foo)
|
||||
#endif
|
||||
|
||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||||
|
||||
#define VHD_MAX_NAME_LEN 1024
|
||||
|
||||
#define VHD_BLOCK_SHIFT 21
|
||||
#define VHD_BLOCK_SIZE (1ULL << VHD_BLOCK_SHIFT)
|
||||
|
||||
#define UTF_16 "UTF-16"
|
||||
#define UTF_16LE "UTF-16LE"
|
||||
#define UTF_16BE "UTF-16BE"
|
||||
|
||||
#define VHD_OPEN_RDONLY 0x00001
|
||||
#define VHD_OPEN_RDWR 0x00002
|
||||
#define VHD_OPEN_FAST 0x00004
|
||||
#define VHD_OPEN_STRICT 0x00008
|
||||
#define VHD_OPEN_IGNORE_DISABLED 0x00010
|
||||
|
||||
#define VHD_FLAG_CREAT_PARENT_RAW 0x00001
|
||||
|
||||
#define vhd_flag_set(word, flag) ((word) |= (flag))
|
||||
#define vhd_flag_clear(word, flag) ((word) &= ~(flag))
|
||||
#define vhd_flag_test(word, flag) ((word) & (flag))
|
||||
|
||||
|
||||
#define ENABLE_FAILURE_TESTING
|
||||
#define FAIL_REPARENT_BEGIN 0
|
||||
#define FAIL_REPARENT_LOCATOR 1
|
||||
#define FAIL_REPARENT_END 2
|
||||
#define FAIL_RESIZE_BEGIN 3
|
||||
#define FAIL_RESIZE_DATA_MOVED 4
|
||||
#define FAIL_RESIZE_METADATA_MOVED 5
|
||||
#define FAIL_RESIZE_END 6
|
||||
#define NUM_FAIL_TESTS 7
|
||||
|
||||
#ifdef ENABLE_FAILURE_TESTING
|
||||
#define TEST_FAIL_AT(point) \
|
||||
if (TEST_FAIL[point]) { \
|
||||
printf("Failing at %s\n", ENV_VAR_FAIL[point]); exit(EINVAL); }
|
||||
#define TEST_FAIL_EXTERN_VARS \
|
||||
extern const char* ENV_VAR_FAIL[]; \
|
||||
extern int TEST_FAIL[];
|
||||
#else
|
||||
#define TEST_FAIL_AT(point)
|
||||
#define TEST_FAIL_EXTERN_VARS
|
||||
#endif // ENABLE_FAILURE_TESTING
|
||||
|
||||
|
||||
static const char VHD_POISON_COOKIE[] = "v_poison";
|
||||
|
||||
typedef struct hd_ftr vhd_footer_t;
|
||||
typedef struct dd_hdr vhd_header_t;
|
||||
typedef struct vhd_bat vhd_bat_t;
|
||||
typedef struct vhd_batmap vhd_batmap_t;
|
||||
typedef struct dd_batmap_hdr vhd_batmap_header_t;
|
||||
typedef struct prt_loc vhd_parent_locator_t;
|
||||
typedef struct vhd_context vhd_context_t;
|
||||
typedef uint32_t vhd_flag_creat_t;
|
||||
|
||||
struct vhd_bat {
|
||||
uint32_t spb;
|
||||
uint32_t entries;
|
||||
uint32_t *bat;
|
||||
};
|
||||
|
||||
struct vhd_batmap {
|
||||
vhd_batmap_header_t header;
|
||||
char *map;
|
||||
};
|
||||
|
||||
struct vhd_context {
|
||||
int fd;
|
||||
char *file;
|
||||
int oflags;
|
||||
int is_block;
|
||||
|
||||
uint32_t spb;
|
||||
uint32_t bm_secs;
|
||||
|
||||
vhd_header_t header;
|
||||
vhd_footer_t footer;
|
||||
vhd_bat_t bat;
|
||||
vhd_batmap_t batmap;
|
||||
};
|
||||
|
||||
static inline uint32_t
|
||||
secs_round_up(uint64_t bytes)
|
||||
{
|
||||
return ((bytes + (VHD_SECTOR_SIZE - 1)) >> VHD_SECTOR_SHIFT);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
secs_round_up_no_zero(uint64_t bytes)
|
||||
{
|
||||
return (secs_round_up(bytes) ? : 1);
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
vhd_sectors_to_bytes(uint64_t sectors)
|
||||
{
|
||||
return sectors << VHD_SECTOR_SHIFT;
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
vhd_bytes_padded(uint64_t bytes)
|
||||
{
|
||||
return vhd_sectors_to_bytes(secs_round_up_no_zero(bytes));
|
||||
}
|
||||
|
||||
static inline int
|
||||
vhd_type_dynamic(vhd_context_t *ctx)
|
||||
{
|
||||
return (ctx->footer.type == HD_TYPE_DYNAMIC ||
|
||||
ctx->footer.type == HD_TYPE_DIFF);
|
||||
}
|
||||
|
||||
static inline int
|
||||
vhd_creator_tapdisk(vhd_context_t *ctx)
|
||||
{
|
||||
return !strncmp(ctx->footer.crtr_app, "tap", 3);
|
||||
}
|
||||
|
||||
static inline int
|
||||
vhd_disabled(vhd_context_t *ctx)
|
||||
{
|
||||
return (!memcmp(ctx->footer.cookie,
|
||||
VHD_POISON_COOKIE, sizeof(ctx->footer.cookie)));
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
vhd_parent_locator_size(vhd_parent_locator_t *loc)
|
||||
{
|
||||
/*
|
||||
* MICROSOFT_COMPAT
|
||||
* data_space *should* be in sectors,
|
||||
* but sometimes we find it in bytes
|
||||
*/
|
||||
if (loc->data_space < 512)
|
||||
return vhd_sectors_to_bytes(loc->data_space);
|
||||
else if (loc->data_space % 512 == 0)
|
||||
return loc->data_space;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
vhd_parent_raw(vhd_context_t *ctx)
|
||||
{
|
||||
return vhd_uuid_is_nil(&ctx->header.prt_uuid);
|
||||
}
|
||||
|
||||
void libvhd_set_log_level(int);
|
||||
|
||||
int vhd_test_file_fixed(const char *, int *);
|
||||
|
||||
uint32_t vhd_time(time_t time);
|
||||
size_t vhd_time_to_string(uint32_t timestamp, char *target);
|
||||
uint32_t vhd_chs(uint64_t size);
|
||||
|
||||
uint32_t vhd_checksum_footer(vhd_footer_t *);
|
||||
uint32_t vhd_checksum_header(vhd_header_t *);
|
||||
uint32_t vhd_checksum_batmap(vhd_batmap_t *);
|
||||
|
||||
void vhd_footer_in(vhd_footer_t *);
|
||||
void vhd_footer_out(vhd_footer_t *);
|
||||
void vhd_header_in(vhd_header_t *);
|
||||
void vhd_header_out(vhd_header_t *);
|
||||
void vhd_bat_in(vhd_bat_t *);
|
||||
void vhd_bat_out(vhd_bat_t *);
|
||||
void vhd_batmap_header_in(vhd_batmap_t *);
|
||||
void vhd_batmap_header_out(vhd_batmap_t *);
|
||||
|
||||
int vhd_validate_footer(vhd_footer_t *footer);
|
||||
int vhd_validate_header(vhd_header_t *header);
|
||||
int vhd_validate_batmap_header(vhd_batmap_t *batmap);
|
||||
int vhd_validate_batmap(vhd_batmap_t *batmap);
|
||||
int vhd_validate_platform_code(uint32_t code);
|
||||
|
||||
int vhd_open(vhd_context_t *, const char *file, int flags);
|
||||
void vhd_close(vhd_context_t *);
|
||||
int vhd_create(const char *name, uint64_t bytes, int type, vhd_flag_creat_t);
|
||||
/* vhd_snapshot: the bytes parameter is optional and can be 0 if the snapshot
|
||||
* is to have the same size as the (first non-empty) parent */
|
||||
int vhd_snapshot(const char *snapshot, uint64_t bytes, const char *parent,
|
||||
vhd_flag_creat_t);
|
||||
|
||||
int vhd_hidden(vhd_context_t *, int *);
|
||||
int vhd_chain_depth(vhd_context_t *, int *);
|
||||
|
||||
off_t vhd_position(vhd_context_t *);
|
||||
int vhd_seek(vhd_context_t *, off_t, int);
|
||||
int vhd_read(vhd_context_t *, void *, size_t);
|
||||
int vhd_write(vhd_context_t *, void *, size_t);
|
||||
|
||||
int vhd_offset(vhd_context_t *, uint32_t, uint32_t *);
|
||||
|
||||
int vhd_end_of_headers(vhd_context_t *ctx, off_t *off);
|
||||
int vhd_end_of_data(vhd_context_t *ctx, off_t *off);
|
||||
int vhd_batmap_header_offset(vhd_context_t *ctx, off_t *off);
|
||||
|
||||
int vhd_get_header(vhd_context_t *);
|
||||
int vhd_get_footer(vhd_context_t *);
|
||||
int vhd_get_bat(vhd_context_t *);
|
||||
int vhd_get_batmap(vhd_context_t *);
|
||||
|
||||
void vhd_put_header(vhd_context_t *);
|
||||
void vhd_put_footer(vhd_context_t *);
|
||||
void vhd_put_bat(vhd_context_t *);
|
||||
void vhd_put_batmap(vhd_context_t *);
|
||||
|
||||
int vhd_has_batmap(vhd_context_t *);
|
||||
int vhd_batmap_test(vhd_context_t *, vhd_batmap_t *, uint32_t);
|
||||
void vhd_batmap_set(vhd_context_t *, vhd_batmap_t *, uint32_t);
|
||||
void vhd_batmap_clear(vhd_context_t *, vhd_batmap_t *, uint32_t);
|
||||
|
||||
int vhd_get_phys_size(vhd_context_t *, off_t *);
|
||||
int vhd_set_phys_size(vhd_context_t *, off_t);
|
||||
|
||||
int vhd_bitmap_test(vhd_context_t *, char *, uint32_t);
|
||||
void vhd_bitmap_set(vhd_context_t *, char *, uint32_t);
|
||||
void vhd_bitmap_clear(vhd_context_t *, char *, uint32_t);
|
||||
|
||||
int vhd_parent_locator_count(vhd_context_t *);
|
||||
int vhd_parent_locator_get(vhd_context_t *, char **);
|
||||
int vhd_parent_locator_read(vhd_context_t *, vhd_parent_locator_t *, char **);
|
||||
int vhd_find_parent(vhd_context_t *, const char *, char **);
|
||||
int vhd_parent_locator_write_at(vhd_context_t *, const char *,
|
||||
off_t, uint32_t, size_t,
|
||||
vhd_parent_locator_t *);
|
||||
|
||||
int vhd_header_decode_parent(vhd_context_t *, vhd_header_t *, char **);
|
||||
int vhd_change_parent(vhd_context_t *, char *parent_path, int raw);
|
||||
|
||||
int vhd_read_footer(vhd_context_t *, vhd_footer_t *);
|
||||
int vhd_read_footer_at(vhd_context_t *, vhd_footer_t *, off_t);
|
||||
int vhd_read_footer_strict(vhd_context_t *, vhd_footer_t *);
|
||||
int vhd_read_header(vhd_context_t *, vhd_header_t *);
|
||||
int vhd_read_header_at(vhd_context_t *, vhd_header_t *, off_t);
|
||||
int vhd_read_bat(vhd_context_t *, vhd_bat_t *);
|
||||
int vhd_read_batmap(vhd_context_t *, vhd_batmap_t *);
|
||||
int vhd_read_bitmap(vhd_context_t *, uint32_t block, char **bufp);
|
||||
int vhd_read_block(vhd_context_t *, uint32_t block, char **bufp);
|
||||
|
||||
int vhd_write_footer(vhd_context_t *, vhd_footer_t *);
|
||||
int vhd_write_footer_at(vhd_context_t *, vhd_footer_t *, off_t);
|
||||
int vhd_write_header(vhd_context_t *, vhd_header_t *);
|
||||
int vhd_write_header_at(vhd_context_t *, vhd_header_t *, off_t);
|
||||
int vhd_write_bat(vhd_context_t *, vhd_bat_t *);
|
||||
int vhd_write_batmap(vhd_context_t *, vhd_batmap_t *);
|
||||
int vhd_write_bitmap(vhd_context_t *, uint32_t block, char *bitmap);
|
||||
int vhd_write_block(vhd_context_t *, uint32_t block, char *data);
|
||||
|
||||
int vhd_io_read(vhd_context_t *, char *, uint64_t, uint32_t);
|
||||
int vhd_io_write(vhd_context_t *, char *, uint64_t, uint32_t);
|
||||
|
||||
#endif
|
||||
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
* list.h
|
||||
*
|
||||
* This is a subset of linux's list.h intended to be used in user-space.
|
||||
* XXX The namespace conflicts with NetBSD's <sys/queue.h>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __LIST_H__
|
||||
#define __LIST_H__
|
||||
|
||||
#define LIST_POISON1 ((void *) 0x00100100)
|
||||
#define LIST_POISON2 ((void *) 0x00200200)
|
||||
|
||||
struct list_head {
|
||||
struct list_head *next, *prev;
|
||||
};
|
||||
|
||||
/* XXX workaround for conflicts. The list API should use its own
|
||||
* namespace prefix, i.e. BLK_
|
||||
*/
|
||||
#ifdef LIST_HEAD_INIT
|
||||
#undef LIST_HEAD_INIT
|
||||
#endif
|
||||
#ifndef LIST_HEAD
|
||||
#undef LIST_HEAD
|
||||
#endif
|
||||
|
||||
#define LIST_HEAD_INIT(name) { &(name), &(name) }
|
||||
|
||||
#define LIST_HEAD(name) \
|
||||
struct list_head name = LIST_HEAD_INIT(name)
|
||||
|
||||
static inline void INIT_LIST_HEAD(struct list_head *list)
|
||||
{
|
||||
list->next = list;
|
||||
list->prev = list;
|
||||
}
|
||||
|
||||
static inline void __list_add(struct list_head *new,
|
||||
struct list_head *prev,
|
||||
struct list_head *next)
|
||||
{
|
||||
next->prev = new;
|
||||
new->next = next;
|
||||
new->prev = prev;
|
||||
prev->next = new;
|
||||
}
|
||||
|
||||
static inline void list_add(struct list_head *new, struct list_head *head)
|
||||
{
|
||||
__list_add(new, head, head->next);
|
||||
}
|
||||
|
||||
static inline void list_add_tail(struct list_head *new, struct list_head *head)
|
||||
{
|
||||
__list_add(new, head->prev, head);
|
||||
}
|
||||
|
||||
static inline void __list_del(struct list_head * prev, struct list_head * next)
|
||||
{
|
||||
next->prev = prev;
|
||||
prev->next = next;
|
||||
}
|
||||
|
||||
static inline void list_del(struct list_head *entry)
|
||||
{
|
||||
__list_del(entry->prev, entry->next);
|
||||
entry->next = LIST_POISON1;
|
||||
entry->prev = LIST_POISON2;
|
||||
}
|
||||
|
||||
static inline void list_del_init(struct list_head *entry)
|
||||
{
|
||||
__list_del(entry->prev, entry->next);
|
||||
INIT_LIST_HEAD(entry);
|
||||
}
|
||||
|
||||
static inline int list_empty(const struct list_head *head)
|
||||
{
|
||||
return head->next == head;
|
||||
}
|
||||
|
||||
static inline int list_is_last(const struct list_head *list,
|
||||
const struct list_head *head)
|
||||
{
|
||||
return list->next == head;
|
||||
}
|
||||
|
||||
static inline void __list_splice(const struct list_head *list,
|
||||
struct list_head *prev,
|
||||
struct list_head *next)
|
||||
{
|
||||
struct list_head *first = list->next;
|
||||
struct list_head *last = list->prev;
|
||||
|
||||
first->prev = prev;
|
||||
prev->next = first;
|
||||
|
||||
last->next = next;
|
||||
next->prev = last;
|
||||
}
|
||||
|
||||
static inline void list_splice(const struct list_head *list,
|
||||
struct list_head *head)
|
||||
{
|
||||
if (!list_empty(list))
|
||||
__list_splice(list, head, head->next);
|
||||
}
|
||||
|
||||
#define list_entry(ptr, type, member) \
|
||||
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
|
||||
|
||||
#define list_for_each_entry(pos, head, member) \
|
||||
for (pos = list_entry((head)->next, typeof(*pos), member); \
|
||||
&pos->member != (head); \
|
||||
pos = list_entry(pos->member.next, typeof(*pos), member))
|
||||
|
||||
#define list_for_each_entry_safe(pos, n, head, member) \
|
||||
for (pos = list_entry((head)->next, typeof(*pos), member), \
|
||||
n = list_entry(pos->member.next, typeof(*pos), member); \
|
||||
&pos->member != (head); \
|
||||
pos = n, n = list_entry(n->member.next, typeof(*n), member))
|
||||
|
||||
#endif /* __LIST_H__ */
|
||||
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef _LVM_UTIL_H_
|
||||
#define _LVM_UTIL_H_
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#define MAX_NAME_SIZE 256
|
||||
|
||||
#define LVM_SEG_TYPE_LINEAR 1
|
||||
#define LVM_SEG_TYPE_UNKNOWN 2
|
||||
|
||||
struct lv_segment {
|
||||
uint8_t type;
|
||||
char device[MAX_NAME_SIZE];
|
||||
uint64_t pe_start;
|
||||
uint64_t pe_size;
|
||||
};
|
||||
|
||||
struct lv {
|
||||
char name[MAX_NAME_SIZE];
|
||||
uint64_t size;
|
||||
uint32_t segments;
|
||||
struct lv_segment first_segment;
|
||||
};
|
||||
|
||||
struct pv {
|
||||
char name[MAX_NAME_SIZE];
|
||||
uint64_t start;
|
||||
};
|
||||
|
||||
struct vg {
|
||||
char name[MAX_NAME_SIZE];
|
||||
uint64_t extent_size;
|
||||
|
||||
int pv_cnt;
|
||||
struct pv *pvs;
|
||||
|
||||
int lv_cnt;
|
||||
struct lv *lvs;
|
||||
};
|
||||
|
||||
int lvm_scan_vg(const char *vg_name, struct vg *vg);
|
||||
void lvm_free_vg(struct vg *vg);
|
||||
|
||||
#endif
|
||||
@@ -1,43 +0,0 @@
|
||||
/* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef _RELATIVE_PATH_H_
|
||||
#define _RELATIVE_PATH_H_
|
||||
|
||||
#include <syslog.h>
|
||||
|
||||
#define DELIMITER '/'
|
||||
#define MAX_NAME_LEN 1000
|
||||
|
||||
#define EPRINTF(_f, _a...) syslog(LOG_ERR, "tap-err:%s: " _f, __func__, ##_a)
|
||||
|
||||
/*
|
||||
* returns a relative path from @src to @dest
|
||||
* result should be freed
|
||||
*/
|
||||
char *relative_path_to(char *src, char *dest, int *err);
|
||||
|
||||
#endif
|
||||
@@ -1,203 +0,0 @@
|
||||
/* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef _TAPDISK_MESSAGE_H_
|
||||
#define _TAPDISK_MESSAGE_H_
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#define TAPDISK_MESSAGE_MAX_PATH_LENGTH 256
|
||||
#define TAPDISK_MESSAGE_STRING_LENGTH 256
|
||||
|
||||
#define TAPDISK_MESSAGE_MAX_MINORS \
|
||||
((TAPDISK_MESSAGE_MAX_PATH_LENGTH / sizeof(int)) - 1)
|
||||
|
||||
#define TAPDISK_MESSAGE_FLAG_SHARED 0x01
|
||||
#define TAPDISK_MESSAGE_FLAG_RDONLY 0x02
|
||||
#define TAPDISK_MESSAGE_FLAG_ADD_CACHE 0x04
|
||||
#define TAPDISK_MESSAGE_FLAG_VHD_INDEX 0x08
|
||||
#define TAPDISK_MESSAGE_FLAG_LOG_DIRTY 0x10
|
||||
|
||||
typedef struct tapdisk_message tapdisk_message_t;
|
||||
typedef uint8_t tapdisk_message_flag_t;
|
||||
typedef struct tapdisk_message_image tapdisk_message_image_t;
|
||||
typedef struct tapdisk_message_params tapdisk_message_params_t;
|
||||
typedef struct tapdisk_message_string tapdisk_message_string_t;
|
||||
typedef struct tapdisk_message_response tapdisk_message_response_t;
|
||||
typedef struct tapdisk_message_minors tapdisk_message_minors_t;
|
||||
typedef struct tapdisk_message_list tapdisk_message_list_t;
|
||||
|
||||
struct tapdisk_message_params {
|
||||
tapdisk_message_flag_t flags;
|
||||
|
||||
uint8_t storage;
|
||||
uint32_t devnum;
|
||||
uint32_t domid;
|
||||
uint16_t path_len;
|
||||
char path[TAPDISK_MESSAGE_MAX_PATH_LENGTH];
|
||||
};
|
||||
|
||||
struct tapdisk_message_image {
|
||||
uint64_t sectors;
|
||||
uint32_t sector_size;
|
||||
uint32_t info;
|
||||
};
|
||||
|
||||
struct tapdisk_message_string {
|
||||
char text[TAPDISK_MESSAGE_STRING_LENGTH];
|
||||
};
|
||||
|
||||
struct tapdisk_message_response {
|
||||
int error;
|
||||
char message[TAPDISK_MESSAGE_STRING_LENGTH];
|
||||
};
|
||||
|
||||
struct tapdisk_message_minors {
|
||||
int count;
|
||||
int list[TAPDISK_MESSAGE_MAX_MINORS];
|
||||
};
|
||||
|
||||
struct tapdisk_message_list {
|
||||
int count;
|
||||
int minor;
|
||||
int state;
|
||||
char path[TAPDISK_MESSAGE_MAX_PATH_LENGTH];
|
||||
};
|
||||
|
||||
struct tapdisk_message {
|
||||
uint16_t type;
|
||||
uint16_t cookie;
|
||||
|
||||
union {
|
||||
pid_t tapdisk_pid;
|
||||
tapdisk_message_image_t image;
|
||||
tapdisk_message_params_t params;
|
||||
tapdisk_message_string_t string;
|
||||
tapdisk_message_minors_t minors;
|
||||
tapdisk_message_response_t response;
|
||||
tapdisk_message_list_t list;
|
||||
} u;
|
||||
};
|
||||
|
||||
enum tapdisk_message_id {
|
||||
TAPDISK_MESSAGE_ERROR = 1,
|
||||
TAPDISK_MESSAGE_RUNTIME_ERROR,
|
||||
TAPDISK_MESSAGE_PID,
|
||||
TAPDISK_MESSAGE_PID_RSP,
|
||||
TAPDISK_MESSAGE_ATTACH,
|
||||
TAPDISK_MESSAGE_ATTACH_RSP,
|
||||
TAPDISK_MESSAGE_OPEN,
|
||||
TAPDISK_MESSAGE_OPEN_RSP,
|
||||
TAPDISK_MESSAGE_PAUSE,
|
||||
TAPDISK_MESSAGE_PAUSE_RSP,
|
||||
TAPDISK_MESSAGE_RESUME,
|
||||
TAPDISK_MESSAGE_RESUME_RSP,
|
||||
TAPDISK_MESSAGE_CLOSE,
|
||||
TAPDISK_MESSAGE_CLOSE_RSP,
|
||||
TAPDISK_MESSAGE_DETACH,
|
||||
TAPDISK_MESSAGE_DETACH_RSP,
|
||||
TAPDISK_MESSAGE_LIST_MINORS,
|
||||
TAPDISK_MESSAGE_LIST_MINORS_RSP,
|
||||
TAPDISK_MESSAGE_LIST,
|
||||
TAPDISK_MESSAGE_LIST_RSP,
|
||||
TAPDISK_MESSAGE_FORCE_SHUTDOWN,
|
||||
TAPDISK_MESSAGE_EXIT,
|
||||
};
|
||||
|
||||
static inline char *
|
||||
tapdisk_message_name(enum tapdisk_message_id id)
|
||||
{
|
||||
switch (id) {
|
||||
case TAPDISK_MESSAGE_ERROR:
|
||||
return "error";
|
||||
|
||||
case TAPDISK_MESSAGE_PID:
|
||||
return "pid";
|
||||
|
||||
case TAPDISK_MESSAGE_PID_RSP:
|
||||
return "pid response";
|
||||
|
||||
case TAPDISK_MESSAGE_OPEN:
|
||||
return "open";
|
||||
|
||||
case TAPDISK_MESSAGE_OPEN_RSP:
|
||||
return "open response";
|
||||
|
||||
case TAPDISK_MESSAGE_PAUSE:
|
||||
return "pause";
|
||||
|
||||
case TAPDISK_MESSAGE_PAUSE_RSP:
|
||||
return "pause response";
|
||||
|
||||
case TAPDISK_MESSAGE_RESUME:
|
||||
return "resume";
|
||||
|
||||
case TAPDISK_MESSAGE_RESUME_RSP:
|
||||
return "resume response";
|
||||
|
||||
case TAPDISK_MESSAGE_CLOSE:
|
||||
return "close";
|
||||
|
||||
case TAPDISK_MESSAGE_FORCE_SHUTDOWN:
|
||||
return "force shutdown";
|
||||
|
||||
case TAPDISK_MESSAGE_CLOSE_RSP:
|
||||
return "close response";
|
||||
|
||||
case TAPDISK_MESSAGE_ATTACH:
|
||||
return "attach";
|
||||
|
||||
case TAPDISK_MESSAGE_ATTACH_RSP:
|
||||
return "attach response";
|
||||
|
||||
case TAPDISK_MESSAGE_DETACH:
|
||||
return "detach";
|
||||
|
||||
case TAPDISK_MESSAGE_DETACH_RSP:
|
||||
return "detach response";
|
||||
|
||||
case TAPDISK_MESSAGE_LIST_MINORS:
|
||||
return "list minors";
|
||||
|
||||
case TAPDISK_MESSAGE_LIST_MINORS_RSP:
|
||||
return "list minors response";
|
||||
|
||||
case TAPDISK_MESSAGE_LIST:
|
||||
return "list";
|
||||
|
||||
case TAPDISK_MESSAGE_LIST_RSP:
|
||||
return "list response";
|
||||
|
||||
case TAPDISK_MESSAGE_EXIT:
|
||||
return "exit";
|
||||
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,44 +0,0 @@
|
||||
/* Copyright (c) 2008, XenSource Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of XenSource Inc. nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef _VHD_UTIL_H_
|
||||
#define _VHD_UTIL_H_
|
||||
|
||||
int vhd_util_create(int argc, char **argv);
|
||||
int vhd_util_snapshot(int argc, char **argv);
|
||||
int vhd_util_query(int argc, char **argv);
|
||||
int vhd_util_read(int argc, char **argv);
|
||||
int vhd_util_set_field(int argc, char **argv);
|
||||
int vhd_util_repair(int argc, char **argv);
|
||||
int vhd_util_fill(int argc, char **argv);
|
||||
int vhd_util_resize(int argc, char **argv);
|
||||
int vhd_util_coalesce(int argc, char **argv);
|
||||
int vhd_util_modify(int argc, char **argv);
|
||||
int vhd_util_scan(int argc, char **argv);
|
||||
int vhd_util_check(int argc, char **argv);
|
||||
int vhd_util_revert(int argc, char **argv);
|
||||
|
||||
#endif
|
||||
Loaded 100 of 126 files, more files were not shown because too many files have changed in this diff.
Show more