mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
tools: remove blktap1
Now that it is unhooked we can just remove it. Signed-off-by: Ian Campbell <ian.campbell@citrix.com> Acked-by: Ian Jackson <ian.jackson@eu.citrix.com>
This commit is contained in:
1 parent
d97402376e
commit
f6bcc03508
33 files changed
-11533
No files matched your search
@@ -101,11 +101,6 @@ tools/blktap2/drivers/tapdisk2
|
||||
tools/blktap2/drivers/td-util
|
||||
tools/blktap2/vhd/vhd-update
|
||||
tools/blktap2/vhd/vhd-util
|
||||
tools/blktap/drivers/blktapctrl
|
||||
tools/blktap/drivers/img2qcow
|
||||
tools/blktap/drivers/qcow-create
|
||||
tools/blktap/drivers/qcow2raw
|
||||
tools/blktap/drivers/tapdisk
|
||||
tools/console/xenconsole
|
||||
tools/console/xenconsoled
|
||||
tools/debugger/gdb/gdb-6.2.1-linux-i386-xen/*
|
||||
|
||||
@@ -140,11 +140,6 @@
|
||||
^tools/blktap2/drivers/td-util$
|
||||
^tools/blktap2/vhd/vhd-update$
|
||||
^tools/blktap2/vhd/vhd-util$
|
||||
^tools/blktap/drivers/blktapctrl$
|
||||
^tools/blktap/drivers/img2qcow$
|
||||
^tools/blktap/drivers/qcow-create$
|
||||
^tools/blktap/drivers/qcow2raw$
|
||||
^tools/blktap/drivers/tapdisk$
|
||||
^tools/check/\..*$
|
||||
^tools/console/xenconsole$
|
||||
^tools/console/xenconsoled$
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
XEN_ROOT = $(CURDIR)/../..
|
||||
include $(XEN_ROOT)/tools/Rules.mk
|
||||
|
||||
SUBDIRS-y :=
|
||||
SUBDIRS-y += lib
|
||||
SUBDIRS-y += drivers
|
||||
|
||||
.PHONY: all clean install
|
||||
all clean install: %: subdirs-%
|
||||
|
||||
install:
|
||||
$(INSTALL_DIR) $(DESTDIR)$(DOCDIR)
|
||||
$(INSTALL_DATA) README $(DESTDIR)$(DOCDIR)/README.blktap
|
||||
@@ -1,122 +0,0 @@
|
||||
Blktap Userspace Tools + Library
|
||||
================================
|
||||
|
||||
Andrew Warfield and Julian Chesterfield
|
||||
16th June 2006
|
||||
|
||||
{firstname.lastname}@cl.cam.ac.uk
|
||||
|
||||
The blktap userspace toolkit provides a user-level disk I/O
|
||||
interface. The blktap 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, blktap 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 blktap 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 blktap 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 2006 the current supported disk formats are:
|
||||
|
||||
- Raw Images (both on partitions and in image files)
|
||||
- File-backed Qcow disks
|
||||
- Standalone sparse Qcow disks
|
||||
- Fast shareable RAM disk between VMs (requires some form of cluster-based
|
||||
filesystem support e.g. OCFS2 in the guest kernel)
|
||||
- Some VMDK images - your mileage may vary
|
||||
|
||||
Raw and QCow images have asynchronous backends and so should perform
|
||||
fairly well. VMDK is based directly on the qemu vmdk driver, which is
|
||||
synchronous (a.k.a. slow).
|
||||
|
||||
Build and Installation Instructions
|
||||
===================================
|
||||
|
||||
Make to configure the blktap backend driver in your dom0 kernel. It
|
||||
will cooperate fine with the existing backend driver, so you can
|
||||
experiment with tap disks without breaking existing VM configs.
|
||||
|
||||
To build the tools separately, "make && make install" in
|
||||
tools/blktap.
|
||||
|
||||
|
||||
Using the Tools
|
||||
===============
|
||||
|
||||
Prepare the image for booting. For qcow files use the qcow utilities
|
||||
installed earlier. e.g. qcow-create generates a blank standalone image
|
||||
or a file-backed CoW image. img2qcow takes an existing image or
|
||||
partition and creates a sparse, standalone qcow-based file.
|
||||
|
||||
The userspace disk agent is configured to start automatically via xend
|
||||
(alternatively you can start it manually => 'blktapctrl')
|
||||
|
||||
Customise the VM config file to use the 'tap' handler, followed by the
|
||||
driver type. e.g. for a raw image such as a file or partition:
|
||||
|
||||
disk = ['tap:aio:<FILENAME>,sda1,w']
|
||||
|
||||
e.g. for a qcow image:
|
||||
|
||||
disk = ['tap:qcow:<FILENAME>,sda1,w']
|
||||
|
||||
|
||||
Mounting images in Dom0 using the blktap driver
|
||||
===============================================
|
||||
Tap (and blkback) disks are also mountable in Dom0 without requiring an
|
||||
active VM to attach. You will need to build a xenlinux Dom0 kernel that
|
||||
includes the blkfront driver (e.g. the default 'make world' or
|
||||
'make kernels' build. Simply use the xm command-line tool to activate
|
||||
the backend disks, and blkfront will generate a virtual block device that
|
||||
can be accessed in the same way as a loop device or partition:
|
||||
|
||||
e.g. for a raw image file <FILENAME> that would normally be mounted using
|
||||
the loopback driver (such as 'mount -o loop <FILENAME> /mnt/disk'), do the
|
||||
following:
|
||||
|
||||
xm block-attach 0 tap:aio:<FILENAME> /dev/xvda1 w 0
|
||||
mount /dev/xvda1 /mnt/disk <--- don't use loop driver
|
||||
|
||||
In this way, you can use any of the userspace device-type drivers built
|
||||
with the blktap userspace toolkit to open and mount disks such as qcow
|
||||
or vmdk images:
|
||||
|
||||
xm block-attach 0 tap:qcow:<FILENAME> /dev/xvda1 w 0
|
||||
mount /dev/xvda1 /mnt/disk
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
XEN_ROOT = $(CURDIR)/../../..
|
||||
include $(XEN_ROOT)/tools/Rules.mk
|
||||
|
||||
IBIN = blktapctrl tapdisk
|
||||
QCOW_UTIL = img2qcow qcow2raw qcow-create
|
||||
|
||||
CFLAGS += -Werror
|
||||
CFLAGS += -Wno-unused
|
||||
CFLAGS += -I../lib
|
||||
CFLAGS += $(CFLAGS_libxenctrl)
|
||||
CFLAGS += $(CFLAGS_libxenstore)
|
||||
CFLAGS += -D_GNU_SOURCE
|
||||
|
||||
ifeq ($(CONFIG_GCRYPT),y)
|
||||
CFLAGS += -DUSE_GCRYPT
|
||||
CRYPT_LIB := -lgcrypt
|
||||
else
|
||||
CRYPT_LIB := -lcrypto
|
||||
$(warning === libgcrypt not installed: falling back to libcrypto ===)
|
||||
endif
|
||||
|
||||
MEMSHRLIBS :=
|
||||
ifeq ($(CONFIG_Linux), y)
|
||||
MEMSHR_DIR = ../../memshr
|
||||
CFLAGS += -DMEMSHR
|
||||
CFLAGS += -I $(MEMSHR_DIR)
|
||||
MEMSHRLIBS += $(MEMSHR_DIR)/libmemshr.a
|
||||
endif
|
||||
|
||||
AIOLIBS := -laio
|
||||
|
||||
CFLAGS += $(PTHREAD_CFLAGS)
|
||||
LDFLAGS += $(PTHREAD_LDFLAGS)
|
||||
|
||||
LDLIBS_blktapctrl := $(MEMSHRLIBS) $(LDLIBS_libxenctrl) $(LDLIBS_libxenstore) -L../lib -lblktap -lrt -lm $(PTHREAD_LIBS)
|
||||
LDLIBS_img := $(AIOLIBS) $(CRYPT_LIB) $(PTHREAD_LIBS) -lz
|
||||
|
||||
BLK-OBJS-y := block-aio.o
|
||||
BLK-OBJS-y += block-sync.o
|
||||
BLK-OBJS-y += block-vmdk.o
|
||||
BLK-OBJS-y += block-ram.o
|
||||
BLK-OBJS-y += block-qcow.o
|
||||
BLK-OBJS-y += block-qcow2.o
|
||||
BLK-OBJS-y += aes.o
|
||||
BLK-OBJS-y += tapaio.o
|
||||
BLK-OBJS-$(CONFIG_Linux) += blk_linux.o
|
||||
|
||||
BLKTAB-OBJS-y := blktapctrl.o
|
||||
BLKTAB-OBJS-$(CONFIG_Linux) += blktapctrl_linux.o
|
||||
|
||||
all: $(IBIN) qcow-util
|
||||
|
||||
blktapctrl: $(BLKTAB-OBJS-y)
|
||||
$(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS_blktapctrl)
|
||||
|
||||
tapdisk: tapdisk.o $(BLK-OBJS-y)
|
||||
$(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS_img)
|
||||
|
||||
.PHONY: qcow-util
|
||||
qcow-util: img2qcow qcow2raw qcow-create
|
||||
|
||||
img2qcow qcow2raw qcow-create: %: %.o $(BLK-OBJS-y)
|
||||
$(CC) $(LDFLAGS) -o $* $^ $(LDLIBS_img)
|
||||
|
||||
install: all
|
||||
$(INSTALL_PROG) $(IBIN) $(QCOW_UTIL) $(VHD_UTIL) $(DESTDIR)$(SBINDIR)
|
||||
|
||||
clean:
|
||||
rm -rf *.o *~ $(DEPS) xen TAGS $(IBIN) $(LIB) $(QCOW_UTIL) $(VHD_UTIL)
|
||||
|
||||
.PHONY: clean install
|
||||
|
||||
-include $(DEPS)
|
||||
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,3 +0,0 @@
|
||||
|
||||
int blk_getimagesize(int fd, uint64_t *size);
|
||||
int blk_getsectorsize(int fd, uint64_t *sector_size);
|
||||
@@ -1,42 +0,0 @@
|
||||
#include <inttypes.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,937 +0,0 @@
|
||||
/*
|
||||
* blktapctrl.c
|
||||
*
|
||||
* userspace controller for the blktap disks.
|
||||
* As requests for new block devices arrive,
|
||||
* the controller spawns off a separate process
|
||||
* per-disk.
|
||||
*
|
||||
*
|
||||
* Copyright (c) 2005 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <xenstore.h>
|
||||
#include <sys/time.h>
|
||||
#include <syslog.h>
|
||||
#ifdef MEMSHR
|
||||
#include <memshr.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "blktaplib.h"
|
||||
#include "blktapctrl.h"
|
||||
#include "tapdisk.h"
|
||||
#include "list.h"
|
||||
#include "xs_api.h" /* for xs_fire_next_watch() */
|
||||
|
||||
#define PIDFILE "/var/run/blktapctrl.pid"
|
||||
|
||||
#define NUM_POLL_FDS 2
|
||||
#define MSG_SIZE 4096
|
||||
#define MAX_TIMEOUT 10
|
||||
#define MAX_RAND_VAL 0xFFFF
|
||||
#define MAX_ATTEMPTS 10
|
||||
|
||||
int run = 1;
|
||||
int max_timeout = MAX_TIMEOUT;
|
||||
int ctlfd = 0;
|
||||
|
||||
int blktap_major;
|
||||
|
||||
static int open_ctrl_socket(char *devname);
|
||||
static int write_msg(int fd, int msgtype, void *ptr, void *ptr2);
|
||||
static int read_msg(int fd, int msgtype, void *ptr);
|
||||
static driver_list_entry_t *active_disks[MAX_DISK_TYPES];
|
||||
|
||||
|
||||
static unsigned long long tapdisk_get_size(blkif_t *blkif)
|
||||
{
|
||||
image_t *img = (image_t *)blkif->prv;
|
||||
return img->size;
|
||||
}
|
||||
|
||||
static unsigned long tapdisk_get_secsize(blkif_t *blkif)
|
||||
{
|
||||
image_t *img = (image_t *)blkif->prv;
|
||||
return img->secsize;
|
||||
}
|
||||
|
||||
static unsigned int tapdisk_get_info(blkif_t *blkif)
|
||||
{
|
||||
image_t *img = (image_t *)blkif->prv;
|
||||
return img->info;
|
||||
}
|
||||
|
||||
struct blkif_ops tapdisk_ops = {
|
||||
.get_size = tapdisk_get_size,
|
||||
.get_secsize = tapdisk_get_secsize,
|
||||
.get_info = tapdisk_get_info,
|
||||
};
|
||||
|
||||
|
||||
static void init_driver_list(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_DISK_TYPES; i++)
|
||||
active_disks[i] = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
static void init_rng(void)
|
||||
{
|
||||
static uint32_t seed;
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
seed = tv.tv_usec;
|
||||
srand48(seed);
|
||||
return;
|
||||
}
|
||||
|
||||
static int get_tapdisk_pid(blkif_t *blkif)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if ((ret = write_msg(blkif->fds[WRITE], CTLMSG_PID, blkif, NULL))
|
||||
<= 0) {
|
||||
DPRINTF("Write_msg failed - CTLMSG_PID(%d)\n", ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((ret = read_msg(blkif->fds[READ], CTLMSG_PID_RSP, blkif))
|
||||
<= 0) {
|
||||
DPRINTF("Read_msg failure - CTLMSG_PID(%d)\n", ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Look up the disk specified by path:
|
||||
* if found, dev points to the device string in the path
|
||||
* type is the tapdisk driver type id
|
||||
* blkif is the existing interface if this is a shared driver
|
||||
* and NULL otherwise.
|
||||
* return 0 on success, -1 on error.
|
||||
*/
|
||||
|
||||
static int test_path(char *path, char **dev, int *type, blkif_t **blkif,
|
||||
int* use_ioemu)
|
||||
{
|
||||
char *ptr, handle[10];
|
||||
int i, size, found = 0;
|
||||
size_t handle_len;
|
||||
|
||||
size = sizeof(dtypes)/sizeof(disk_info_t *);
|
||||
*type = MAX_DISK_TYPES + 1;
|
||||
*blkif = NULL;
|
||||
|
||||
if (!strncmp(path, "tapdisk:", strlen("tapdisk:"))) {
|
||||
*use_ioemu = 0;
|
||||
path += strlen("tapdisk:");
|
||||
} else if (!strncmp(path, "ioemu:", strlen("ioemu:"))) {
|
||||
*use_ioemu = 1;
|
||||
path += strlen("ioemu:");
|
||||
} else {
|
||||
// Use the default for the image type
|
||||
*use_ioemu = -1;
|
||||
}
|
||||
|
||||
if ( (ptr = strstr(path, ":"))!=NULL) {
|
||||
handle_len = (ptr - path);
|
||||
memcpy(handle, path, handle_len);
|
||||
*dev = ptr + 1;
|
||||
ptr = handle + handle_len;
|
||||
*ptr = '\0';
|
||||
DPRINTF("Detected handle: [%s]\n",handle);
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
if ((strlen(dtypes[i]->handle) == handle_len) &&
|
||||
strncmp(handle, dtypes[i]->handle,
|
||||
handle_len) == 0) {
|
||||
found = 1;
|
||||
}
|
||||
|
||||
if (found) {
|
||||
if (*use_ioemu == -1)
|
||||
*use_ioemu = dtypes[i]->use_ioemu;
|
||||
*type = dtypes[i]->idnum;
|
||||
|
||||
if (dtypes[i]->single_handler == 1) {
|
||||
/* Check whether tapdisk process
|
||||
already exists */
|
||||
if (active_disks[dtypes[i]->idnum] == NULL)
|
||||
*blkif = NULL;
|
||||
else
|
||||
*blkif = active_disks[dtypes[i]
|
||||
->idnum]->blkif;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Fall-through case, we didn't find a disk driver. */
|
||||
DPRINTF("Unknown blktap disk type [%s]!\n",handle);
|
||||
*dev = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static void add_disktype(blkif_t *blkif, int type)
|
||||
{
|
||||
driver_list_entry_t *entry, **pprev;
|
||||
|
||||
if (type > MAX_DISK_TYPES)
|
||||
return;
|
||||
|
||||
entry = malloc(sizeof(driver_list_entry_t));
|
||||
entry->blkif = blkif;
|
||||
entry->next = NULL;
|
||||
|
||||
pprev = &active_disks[type];
|
||||
while (*pprev != NULL)
|
||||
pprev = &(*pprev)->next;
|
||||
|
||||
*pprev = entry;
|
||||
entry->pprev = pprev;
|
||||
}
|
||||
|
||||
static int qemu_instance_has_disks(pid_t pid)
|
||||
{
|
||||
int i;
|
||||
int count = 0;
|
||||
driver_list_entry_t *entry;
|
||||
|
||||
for (i = 0; i < MAX_DISK_TYPES; i++) {
|
||||
entry = active_disks[i];
|
||||
while (entry) {
|
||||
if ((entry->blkif->tappid == pid) && dtypes[i]->use_ioemu)
|
||||
count++;
|
||||
entry = entry->next;
|
||||
}
|
||||
}
|
||||
|
||||
return (count != 0);
|
||||
}
|
||||
|
||||
static int del_disktype(blkif_t *blkif)
|
||||
{
|
||||
driver_list_entry_t *entry, **pprev;
|
||||
int type = blkif->drivertype, count = 0, close = 0;
|
||||
|
||||
if (type > MAX_DISK_TYPES)
|
||||
return 1;
|
||||
|
||||
pprev = &active_disks[type];
|
||||
while ((*pprev != NULL) && ((*pprev)->blkif != blkif))
|
||||
pprev = &(*pprev)->next;
|
||||
|
||||
if ((entry = *pprev) == NULL) {
|
||||
DPRINTF("DEL_DISKTYPE: No match\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
*pprev = entry->next;
|
||||
if (entry->next)
|
||||
entry->next->pprev = pprev;
|
||||
|
||||
DPRINTF("DEL_DISKTYPE: Freeing entry\n");
|
||||
free(entry);
|
||||
|
||||
/*
|
||||
* When using ioemu, all disks of one VM are connected to the same
|
||||
* qemu-dm instance. We may close the file handle only if there is
|
||||
* no other disk left for this domain.
|
||||
*/
|
||||
if (dtypes[type]->use_ioemu)
|
||||
return !qemu_instance_has_disks(blkif->tappid);
|
||||
|
||||
/* Caller should close() if no single controller, or list is empty. */
|
||||
return (!dtypes[type]->single_handler || (active_disks[type] == NULL));
|
||||
}
|
||||
|
||||
static int write_msg(int fd, int msgtype, void *ptr, void *ptr2)
|
||||
{
|
||||
blkif_t *blkif;
|
||||
blkif_info_t *blk;
|
||||
msg_hdr_t *msg;
|
||||
msg_newdev_t *msg_dev;
|
||||
char *p, *buf, *path;
|
||||
int msglen, len, ret;
|
||||
fd_set writefds;
|
||||
struct timeval timeout;
|
||||
image_t *image, *img;
|
||||
uint32_t seed;
|
||||
|
||||
blkif = (blkif_t *)ptr;
|
||||
blk = blkif->info;
|
||||
image = blkif->prv;
|
||||
len = 0;
|
||||
|
||||
switch (msgtype)
|
||||
{
|
||||
case CTLMSG_PARAMS:
|
||||
path = (char *)ptr2;
|
||||
DPRINTF("Write_msg called: CTLMSG_PARAMS, sending [%s, %s]\n",
|
||||
blk->params, path);
|
||||
|
||||
msglen = sizeof(msg_hdr_t) + strlen(path) + 1;
|
||||
buf = malloc(msglen);
|
||||
|
||||
/*Assign header fields*/
|
||||
msg = (msg_hdr_t *)buf;
|
||||
msg->type = CTLMSG_PARAMS;
|
||||
msg->len = msglen;
|
||||
msg->drivertype = blkif->drivertype;
|
||||
msg->readonly = blkif->readonly;
|
||||
|
||||
gettimeofday(&timeout, NULL);
|
||||
msg->cookie = blkif->cookie;
|
||||
DPRINTF("Generated cookie, %d\n",blkif->cookie);
|
||||
|
||||
/*Copy blk->params to msg*/
|
||||
p = buf + sizeof(msg_hdr_t);
|
||||
memcpy(p, path, strlen(path) + 1);
|
||||
|
||||
break;
|
||||
|
||||
case CTLMSG_NEWDEV:
|
||||
DPRINTF("Write_msg called: CTLMSG_NEWDEV\n");
|
||||
|
||||
msglen = sizeof(msg_hdr_t) + sizeof(msg_newdev_t);
|
||||
buf = malloc(msglen);
|
||||
|
||||
/*Assign header fields*/
|
||||
msg = (msg_hdr_t *)buf;
|
||||
msg->type = CTLMSG_NEWDEV;
|
||||
msg->len = msglen;
|
||||
msg->drivertype = blkif->drivertype;
|
||||
msg->cookie = blkif->cookie;
|
||||
|
||||
msg_dev = (msg_newdev_t *)(buf + sizeof(msg_hdr_t));
|
||||
msg_dev->devnum = blkif->minor;
|
||||
msg_dev->domid = blkif->domid;
|
||||
|
||||
break;
|
||||
|
||||
case CTLMSG_CLOSE:
|
||||
DPRINTF("Write_msg called: CTLMSG_CLOSE\n");
|
||||
|
||||
msglen = sizeof(msg_hdr_t);
|
||||
buf = malloc(msglen);
|
||||
|
||||
/*Assign header fields*/
|
||||
msg = (msg_hdr_t *)buf;
|
||||
msg->type = CTLMSG_CLOSE;
|
||||
msg->len = msglen;
|
||||
msg->drivertype = blkif->drivertype;
|
||||
msg->cookie = blkif->cookie;
|
||||
|
||||
break;
|
||||
|
||||
case CTLMSG_PID:
|
||||
DPRINTF("Write_msg called: CTLMSG_PID\n");
|
||||
|
||||
msglen = sizeof(msg_hdr_t);
|
||||
buf = malloc(msglen);
|
||||
|
||||
/*Assign header fields*/
|
||||
msg = (msg_hdr_t *)buf;
|
||||
msg->type = CTLMSG_PID;
|
||||
msg->len = msglen;
|
||||
msg->drivertype = blkif->drivertype;
|
||||
msg->cookie = blkif->cookie;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*Now send the message*/
|
||||
ret = 0;
|
||||
FD_ZERO(&writefds);
|
||||
FD_SET(fd,&writefds);
|
||||
timeout.tv_sec = max_timeout; /*Wait for up to max_timeout seconds*/
|
||||
timeout.tv_usec = 0;
|
||||
if (select(fd+1, (fd_set *) 0, &writefds,
|
||||
(fd_set *) 0, &timeout) > 0) {
|
||||
len = write(fd, buf, msglen);
|
||||
if (len == -1) DPRINTF("Write failed: (%d)\n",errno);
|
||||
}
|
||||
free(buf);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int read_msg(int fd, int msgtype, void *ptr)
|
||||
{
|
||||
blkif_t *blkif;
|
||||
blkif_info_t *blk;
|
||||
msg_hdr_t *msg;
|
||||
msg_pid_t *msg_pid;
|
||||
char *p, *buf;
|
||||
int msglen = MSG_SIZE, len, ret;
|
||||
fd_set readfds;
|
||||
struct timeval timeout;
|
||||
image_t *image, *img;
|
||||
|
||||
|
||||
blkif = (blkif_t *)ptr;
|
||||
blk = blkif->info;
|
||||
image = blkif->prv;
|
||||
|
||||
buf = malloc(MSG_SIZE);
|
||||
|
||||
ret = 0;
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(fd,&readfds);
|
||||
timeout.tv_sec = max_timeout; /*Wait for up to max_timeout seconds*/
|
||||
timeout.tv_usec = 0;
|
||||
if (select(fd+1, &readfds, (fd_set *) 0,
|
||||
(fd_set *) 0, &timeout) > 0) {
|
||||
ret = read(fd, buf, msglen);
|
||||
}
|
||||
if (ret > 0) {
|
||||
msg = (msg_hdr_t *)buf;
|
||||
switch (msg->type)
|
||||
{
|
||||
case CTLMSG_IMG:
|
||||
img = (image_t *)(buf + sizeof(msg_hdr_t));
|
||||
image->size = img->size;
|
||||
image->secsize = img->secsize;
|
||||
image->info = img->info;
|
||||
|
||||
DPRINTF("Received CTLMSG_IMG: %llu, %lu, %u\n",
|
||||
image->size, image->secsize, image->info);
|
||||
if(msgtype != CTLMSG_IMG) ret = 0;
|
||||
break;
|
||||
|
||||
case CTLMSG_IMG_FAIL:
|
||||
DPRINTF("Received CTLMSG_IMG_FAIL, "
|
||||
"unable to open image\n");
|
||||
ret = 0;
|
||||
break;
|
||||
|
||||
case CTLMSG_NEWDEV_RSP:
|
||||
DPRINTF("Received CTLMSG_NEWDEV_RSP\n");
|
||||
if(msgtype != CTLMSG_NEWDEV_RSP) ret = 0;
|
||||
break;
|
||||
|
||||
case CTLMSG_NEWDEV_FAIL:
|
||||
DPRINTF("Received CTLMSG_NEWDEV_FAIL\n");
|
||||
ret = 0;
|
||||
break;
|
||||
|
||||
case CTLMSG_CLOSE_RSP:
|
||||
DPRINTF("Received CTLMSG_CLOSE_RSP\n");
|
||||
if (msgtype != CTLMSG_CLOSE_RSP) ret = 0;
|
||||
break;
|
||||
|
||||
case CTLMSG_PID_RSP:
|
||||
DPRINTF("Received CTLMSG_PID_RSP\n");
|
||||
if (msgtype != CTLMSG_PID_RSP) ret = 0;
|
||||
else {
|
||||
msg_pid = (msg_pid_t *)
|
||||
(buf + sizeof(msg_hdr_t));
|
||||
blkif->tappid = msg_pid->pid;
|
||||
DPRINTF("\tPID: [%d]\n",blkif->tappid);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
DPRINTF("UNKNOWN MESSAGE TYPE RECEIVED\n");
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(buf);
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static int launch_tapdisk_provider(char **argv)
|
||||
{
|
||||
pid_t child;
|
||||
|
||||
if ((child = fork()) < 0)
|
||||
return -1;
|
||||
|
||||
if (!child) {
|
||||
int i;
|
||||
for (i = 0 ; i < sysconf(_SC_OPEN_MAX) ; i++)
|
||||
if (i != STDIN_FILENO &&
|
||||
i != STDOUT_FILENO &&
|
||||
i != STDERR_FILENO)
|
||||
close(i);
|
||||
|
||||
execvp(argv[0], argv);
|
||||
DPRINTF("execvp failed: %d (%s)\n", errno, strerror(errno));
|
||||
DPRINTF("PATH = %s\n", getenv("PATH"));
|
||||
_exit(1);
|
||||
} else {
|
||||
pid_t got;
|
||||
do {
|
||||
got = waitpid(child, NULL, 0);
|
||||
} while (got != child);
|
||||
}
|
||||
return child;
|
||||
}
|
||||
|
||||
static int launch_tapdisk(char *wrctldev, char *rdctldev)
|
||||
{
|
||||
char *argv[] = { "tapdisk", wrctldev, rdctldev, NULL };
|
||||
|
||||
if (launch_tapdisk_provider(argv) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int launch_tapdisk_ioemu(void)
|
||||
{
|
||||
char *argv[] = { "tapdisk-ioemu", NULL };
|
||||
return launch_tapdisk_provider(argv);
|
||||
}
|
||||
|
||||
/*
|
||||
* Connect to an ioemu based disk provider (qemu-dm or tapdisk-ioemu)
|
||||
*
|
||||
* If the domain has a device model, connect to qemu-dm through the
|
||||
* domain specific pipe. Otherwise use a single tapdisk-ioemu instance
|
||||
* which is represented by domid 0 and provides access for Dom0 and
|
||||
* all DomUs without device model.
|
||||
*/
|
||||
static int connect_qemu(blkif_t *blkif, int domid)
|
||||
{
|
||||
char *rdctldev, *wrctldev;
|
||||
|
||||
static int tapdisk_ioemu_pid = 0;
|
||||
static int dom0_readfd = 0;
|
||||
static int dom0_writefd = 0;
|
||||
int refresh_pid = 0;
|
||||
|
||||
if (asprintf(&rdctldev, BLKTAP_CTRL_DIR "/qemu-read-%d", domid) < 0)
|
||||
return -1;
|
||||
|
||||
if (asprintf(&wrctldev, BLKTAP_CTRL_DIR "/qemu-write-%d", domid) < 0) {
|
||||
free(rdctldev);
|
||||
return -1;
|
||||
}
|
||||
|
||||
DPRINTF("Using qemu blktap pipe: %s\n", rdctldev);
|
||||
|
||||
if (domid == 0) {
|
||||
/*
|
||||
* tapdisk-ioemu exits as soon as the last image is
|
||||
* disconnected. Check if it is still running.
|
||||
*/
|
||||
if (tapdisk_ioemu_pid == 0 || kill(tapdisk_ioemu_pid, 0)) {
|
||||
/* No device model and tapdisk-ioemu doesn't run yet */
|
||||
DPRINTF("Launching tapdisk-ioemu\n");
|
||||
launch_tapdisk_ioemu();
|
||||
|
||||
dom0_readfd = open_ctrl_socket(wrctldev);
|
||||
dom0_writefd = open_ctrl_socket(rdctldev);
|
||||
|
||||
refresh_pid = 1;
|
||||
}
|
||||
|
||||
DPRINTF("Using tapdisk-ioemu connection\n");
|
||||
blkif->fds[READ] = dom0_readfd;
|
||||
blkif->fds[WRITE] = dom0_writefd;
|
||||
|
||||
if (refresh_pid) {
|
||||
get_tapdisk_pid(blkif);
|
||||
tapdisk_ioemu_pid = blkif->tappid;
|
||||
}
|
||||
|
||||
} else if (access(rdctldev, R_OK | W_OK) == 0) {
|
||||
/* Use existing pipe to the device model */
|
||||
DPRINTF("Using qemu-dm connection\n");
|
||||
blkif->fds[READ] = open_ctrl_socket(wrctldev);
|
||||
blkif->fds[WRITE] = open_ctrl_socket(rdctldev);
|
||||
} else {
|
||||
/* No device model => try with tapdisk-ioemu */
|
||||
DPRINTF("No device model\n");
|
||||
connect_qemu(blkif, 0);
|
||||
}
|
||||
|
||||
free(rdctldev);
|
||||
free(wrctldev);
|
||||
|
||||
if (blkif->fds[READ] == -1 || blkif->fds[WRITE] == -1)
|
||||
return -1;
|
||||
|
||||
DPRINTF("Attached to qemu blktap pipes\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Launch tapdisk instance */
|
||||
static int connect_tapdisk(blkif_t *blkif, int minor)
|
||||
{
|
||||
char *rdctldev = NULL, *wrctldev = NULL;
|
||||
int ret = -1;
|
||||
|
||||
DPRINTF("tapdisk process does not exist:\n");
|
||||
|
||||
if (asprintf(&rdctldev,
|
||||
"%s/tapctrlread%d", BLKTAP_CTRL_DIR, minor) == -1)
|
||||
goto fail;
|
||||
|
||||
if (asprintf(&wrctldev,
|
||||
"%s/tapctrlwrite%d", BLKTAP_CTRL_DIR, minor) == -1)
|
||||
goto fail;
|
||||
|
||||
blkif->fds[READ] = open_ctrl_socket(rdctldev);
|
||||
blkif->fds[WRITE] = open_ctrl_socket(wrctldev);
|
||||
|
||||
if (blkif->fds[READ] == -1 || blkif->fds[WRITE] == -1)
|
||||
goto fail;
|
||||
|
||||
/*launch the new process*/
|
||||
DPRINTF("Launching process, CMDLINE [tapdisk %s %s]\n",
|
||||
wrctldev, rdctldev);
|
||||
|
||||
if (launch_tapdisk(wrctldev, rdctldev) == -1) {
|
||||
DPRINTF("Unable to fork, cmdline: [tapdisk %s %s]\n",
|
||||
wrctldev, rdctldev);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
fail:
|
||||
if (rdctldev)
|
||||
free(rdctldev);
|
||||
|
||||
if (wrctldev)
|
||||
free(wrctldev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int blktapctrl_new_blkif(blkif_t *blkif)
|
||||
{
|
||||
blkif_info_t *blk;
|
||||
int major, minor, fd_read, fd_write, type, new;
|
||||
char *rdctldev, *wrctldev, *ptr;
|
||||
image_t *image;
|
||||
blkif_t *exist = NULL;
|
||||
static uint16_t next_cookie = 0;
|
||||
int use_ioemu;
|
||||
|
||||
DPRINTF("Received a poll for a new vbd\n");
|
||||
if ( ((blk=blkif->info) != NULL) && (blk->params != NULL) ) {
|
||||
if (blktap_interface_create(ctlfd, &major, &minor, blkif) < 0)
|
||||
return -1;
|
||||
|
||||
if (test_path(blk->params, &ptr, &type, &exist, &use_ioemu) != 0) {
|
||||
DPRINTF("Error in blktap device string(%s).\n",
|
||||
blk->params);
|
||||
goto fail;
|
||||
}
|
||||
blkif->drivertype = type;
|
||||
blkif->cookie = next_cookie++;
|
||||
|
||||
if (!exist) {
|
||||
if (use_ioemu) {
|
||||
if (connect_qemu(blkif, blkif->domid))
|
||||
goto fail;
|
||||
} else {
|
||||
if (connect_tapdisk(blkif, minor))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
} else {
|
||||
DPRINTF("Process exists!\n");
|
||||
blkif->fds[READ] = exist->fds[READ];
|
||||
blkif->fds[WRITE] = exist->fds[WRITE];
|
||||
}
|
||||
|
||||
add_disktype(blkif, type);
|
||||
blkif->major = major;
|
||||
blkif->minor = minor;
|
||||
|
||||
image = (image_t *)malloc(sizeof(image_t));
|
||||
blkif->prv = (void *)image;
|
||||
blkif->ops = &tapdisk_ops;
|
||||
|
||||
/*Retrieve the PID of the new process*/
|
||||
if (get_tapdisk_pid(blkif) <= 0) {
|
||||
DPRINTF("Unable to contact disk process\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Both of the following read and write calls will block up to
|
||||
* max_timeout val*/
|
||||
if (write_msg(blkif->fds[WRITE], CTLMSG_PARAMS, blkif, ptr)
|
||||
<= 0) {
|
||||
DPRINTF("Write_msg failed - CTLMSG_PARAMS\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (read_msg(blkif->fds[READ], CTLMSG_IMG, blkif) <= 0) {
|
||||
DPRINTF("Read_msg failure - CTLMSG_IMG\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
} else return -1;
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
ioctl(ctlfd, BLKTAP_IOCTL_FREEINTF, minor);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int map_new_blktapctrl(blkif_t *blkif)
|
||||
{
|
||||
DPRINTF("Received a poll for a new devmap\n");
|
||||
if (write_msg(blkif->fds[WRITE], CTLMSG_NEWDEV, blkif, NULL) <= 0) {
|
||||
DPRINTF("Write_msg failed - CTLMSG_NEWDEV\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (read_msg(blkif->fds[READ], CTLMSG_NEWDEV_RSP, blkif) <= 0) {
|
||||
DPRINTF("Read_msg failed - CTLMSG_NEWDEV_RSP\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
DPRINTF("Exiting map_new_blktapctrl\n");
|
||||
|
||||
return blkif->minor - 1;
|
||||
}
|
||||
|
||||
static int unmap_blktapctrl(blkif_t *blkif)
|
||||
{
|
||||
DPRINTF("Unmapping vbd\n");
|
||||
|
||||
if (write_msg(blkif->fds[WRITE], CTLMSG_CLOSE, blkif, NULL) <= 0) {
|
||||
DPRINTF("Write_msg failed - CTLMSG_CLOSE\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (del_disktype(blkif)) {
|
||||
DPRINTF("Closing communication pipe to pid %d\n", blkif->tappid);
|
||||
close(blkif->fds[WRITE]);
|
||||
close(blkif->fds[READ]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int open_ctrl_socket(char *devname)
|
||||
{
|
||||
int ret;
|
||||
int ipc_fd;
|
||||
fd_set socks;
|
||||
struct timeval timeout;
|
||||
|
||||
if (mkdir(BLKTAP_CTRL_DIR, 0755) == 0)
|
||||
DPRINTF("Created %s directory\n", BLKTAP_CTRL_DIR);
|
||||
ret = mkfifo(devname,S_IRWXU|S_IRWXG|S_IRWXO);
|
||||
if ( (ret != 0) && (errno != EEXIST) ) {
|
||||
DPRINTF("ERROR: pipe failed (%d)\n", errno);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
ipc_fd = open(devname,O_RDWR|O_NONBLOCK);
|
||||
|
||||
if (ipc_fd < 0) {
|
||||
DPRINTF("FD open failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ipc_fd;
|
||||
}
|
||||
|
||||
static void print_drivers(void)
|
||||
{
|
||||
int i, size;
|
||||
|
||||
size = sizeof(dtypes)/sizeof(disk_info_t *);
|
||||
DPRINTF("blktapctrl: v1.0.0\n");
|
||||
for (i = 0; i < size; i++)
|
||||
DPRINTF("Found driver: [%s]\n",dtypes[i]->name);
|
||||
}
|
||||
|
||||
static void write_pidfile(long pid)
|
||||
{
|
||||
char buf[100];
|
||||
int len;
|
||||
int fd;
|
||||
int flags;
|
||||
|
||||
fd = open(PIDFILE, O_RDWR | O_CREAT, 0600);
|
||||
if (fd == -1) {
|
||||
DPRINTF("Opening pid file failed (%d)\n", errno);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* We exit silently if daemon already running. */
|
||||
if (lockf(fd, F_TLOCK, 0) == -1)
|
||||
exit(0);
|
||||
|
||||
/* Set FD_CLOEXEC, so that tapdisk doesn't get this file
|
||||
descriptor. */
|
||||
if ((flags = fcntl(fd, F_GETFD)) == -1) {
|
||||
DPRINTF("F_GETFD failed (%d)\n", errno);
|
||||
exit(1);
|
||||
}
|
||||
flags |= FD_CLOEXEC;
|
||||
if (fcntl(fd, F_SETFD, flags) == -1) {
|
||||
DPRINTF("F_SETFD failed (%d)\n", errno);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
len = snprintf(buf, sizeof(buf), "%ld\n", pid);
|
||||
if (write(fd, buf, len) != len) {
|
||||
DPRINTF("Writing pid file failed (%d)\n", errno);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char *devname;
|
||||
tapdev_info_t *ctlinfo;
|
||||
int tap_pfd, store_pfd, xs_fd, ret, timeout, pfd_count, count=0;
|
||||
struct xs_handle *h;
|
||||
struct pollfd pfd[NUM_POLL_FDS];
|
||||
pid_t process;
|
||||
char buf[128];
|
||||
|
||||
__init_blkif();
|
||||
snprintf(buf, sizeof(buf), "BLKTAPCTRL[%d]", getpid());
|
||||
openlog(buf, LOG_CONS|LOG_ODELAY, LOG_DAEMON);
|
||||
if (daemon(0,0)) {
|
||||
DPRINTF("daemon failed (%d)\n", errno);
|
||||
goto open_failed;
|
||||
}
|
||||
|
||||
print_drivers();
|
||||
init_driver_list();
|
||||
init_rng();
|
||||
|
||||
register_new_blkif_hook(blktapctrl_new_blkif);
|
||||
register_new_devmap_hook(map_new_blktapctrl);
|
||||
register_new_unmap_hook(unmap_blktapctrl);
|
||||
|
||||
ctlfd = blktap_interface_open();
|
||||
if (ctlfd < 0) {
|
||||
DPRINTF("couldn't open blktap interface\n");
|
||||
goto open_failed;
|
||||
}
|
||||
|
||||
#ifdef MEMSHR
|
||||
memshr_daemon_initialize();
|
||||
#endif
|
||||
|
||||
retry:
|
||||
/* Set up store connection and watch. */
|
||||
h = xs_daemon_open();
|
||||
if (h == NULL) {
|
||||
DPRINTF("xs_daemon_open failed -- "
|
||||
"is xenstore running?\n");
|
||||
if (count < MAX_ATTEMPTS) {
|
||||
count++;
|
||||
sleep(2);
|
||||
goto retry;
|
||||
} else goto open_failed;
|
||||
}
|
||||
|
||||
ret = setup_probe_watch(h);
|
||||
if (ret != 0) {
|
||||
DPRINTF("Failed adding device probewatch\n");
|
||||
xs_daemon_close(h);
|
||||
goto open_failed;
|
||||
}
|
||||
|
||||
ioctl(ctlfd, BLKTAP_IOCTL_SETMODE, BLKTAP_MODE_INTERPOSE );
|
||||
|
||||
process = getpid();
|
||||
write_pidfile(process);
|
||||
ret = ioctl(ctlfd, BLKTAP_IOCTL_SENDPID, process );
|
||||
|
||||
/*Static pollhooks*/
|
||||
pfd_count = 0;
|
||||
tap_pfd = pfd_count++;
|
||||
pfd[tap_pfd].fd = ctlfd;
|
||||
pfd[tap_pfd].events = POLLIN;
|
||||
|
||||
store_pfd = pfd_count++;
|
||||
pfd[store_pfd].fd = xs_fileno(h);
|
||||
pfd[store_pfd].events = POLLIN;
|
||||
|
||||
while (run) {
|
||||
timeout = 1000; /*Milliseconds*/
|
||||
ret = poll(pfd, pfd_count, timeout);
|
||||
|
||||
if (ret > 0) {
|
||||
if (pfd[store_pfd].revents) {
|
||||
ret = xs_fire_next_watch(h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
xs_daemon_close(h);
|
||||
ioctl(ctlfd, BLKTAP_IOCTL_SETMODE, BLKTAP_MODE_PASSTHROUGH );
|
||||
close(ctlfd);
|
||||
closelog();
|
||||
|
||||
return 0;
|
||||
|
||||
open_failed:
|
||||
DPRINTF("Unable to start blktapctrl\n");
|
||||
closelog();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* indent-tabs-mode: t
|
||||
* c-indent-level: 8
|
||||
* c-basic-offset: 8
|
||||
* tab-width: 8
|
||||
* End:
|
||||
*/
|
||||
@@ -1,36 +0,0 @@
|
||||
/* blktapctrl.h
|
||||
*
|
||||
* controller image utils.
|
||||
*
|
||||
* (c) 2004-6 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.
|
||||
*/
|
||||
|
||||
|
||||
int blktap_interface_open(void);
|
||||
|
||||
int blktap_interface_create(int ctlfd, int *major, int *minor, blkif_t *blkif);
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "tapdisk.h"
|
||||
#include "blktaplib.h"
|
||||
#include "blktapctrl.h"
|
||||
|
||||
static void make_blktap_dev(char *devname, int major, int minor)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
if (lstat(devname, &st) != 0) {
|
||||
/*Need to create device*/
|
||||
if (mkdir(BLKTAP_DEV_DIR, 0755) == 0)
|
||||
DPRINTF("Created %s directory\n",BLKTAP_DEV_DIR);
|
||||
if (mknod(devname, S_IFCHR|0600,
|
||||
makedev(major, minor)) == 0)
|
||||
DPRINTF("Created %s device\n",devname);
|
||||
} else {
|
||||
DPRINTF("%s device already exists\n",devname);
|
||||
/* it already exists, but is it the same major number */
|
||||
if (((st.st_rdev>>8) & 0xff) != major) {
|
||||
DPRINTF("%s has old major %d\n",
|
||||
devname,
|
||||
(unsigned int)((st.st_rdev >> 8) & 0xff));
|
||||
/* only try again if we succed in deleting it */
|
||||
if (!unlink(devname))
|
||||
make_blktap_dev(devname, major, minor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int blktap_interface_create(int ctlfd, int *major, int *minor, blkif_t *blkif)
|
||||
{
|
||||
domid_translate_t tr;
|
||||
domid_translate_ext_t tr_ext;
|
||||
int ret;
|
||||
char *devname;
|
||||
|
||||
if (blkif->be_id >= (1<<28)) {
|
||||
/* new-style backend-id, so use the extended structure */
|
||||
tr_ext.domid = blkif->domid;
|
||||
tr_ext.busid = blkif->be_id;
|
||||
ret = ioctl(ctlfd, BLKTAP_IOCTL_NEWINTF_EXT, &tr_ext);
|
||||
DPRINTF("Sent domid %d and be_id %d\n", tr_ext.domid,
|
||||
tr_ext.busid);
|
||||
}
|
||||
else {
|
||||
/* old-style backend-id; use the old structure */
|
||||
tr.domid = blkif->domid;
|
||||
tr.busid = (unsigned short)blkif->be_id;
|
||||
ret = ioctl(ctlfd, BLKTAP_IOCTL_NEWINTF, tr);
|
||||
DPRINTF("Sent domid %d and be_id %d\n", tr.domid, tr.busid);
|
||||
}
|
||||
|
||||
if ( (ret <= 0)||(ret > MAX_TAP_DEV) ) {
|
||||
DPRINTF("Incorrect Dev ID [%d]\n",ret);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*minor = ret;
|
||||
*major = ioctl(ctlfd, BLKTAP_IOCTL_MAJOR, ret );
|
||||
if (*major < 0) {
|
||||
DPRINTF("Incorrect Major ID [%d]\n",*major);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (asprintf(&devname,"%s/%s%d",BLKTAP_DEV_DIR, BLKTAP_DEV_NAME, *minor) == -1)
|
||||
return -1;
|
||||
make_blktap_dev(devname,*major,*minor);
|
||||
DPRINTF("Received device id %d and major %d\n",
|
||||
*minor, *major);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int blktap_interface_open(void)
|
||||
{
|
||||
int ctlfd;
|
||||
|
||||
ctlfd = open(BLKTAP_DEV_DIR "/" BLKTAP_DEV_NAME "0", O_RDWR);
|
||||
if (ctlfd == -1)
|
||||
DPRINTF("blktap0 open failed\n");
|
||||
|
||||
return ctlfd;
|
||||
}
|
||||
@@ -1,259 +0,0 @@
|
||||
/* block-aio.c
|
||||
*
|
||||
* libaio-based raw disk implementation.
|
||||
*
|
||||
* (c) 2006 Andrew Warfield and Julian Chesterfield
|
||||
*
|
||||
* NB: This code is not thread-safe.
|
||||
*
|
||||
* 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 <libaio.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 "tapdisk.h"
|
||||
#include "tapaio.h"
|
||||
#include "blk.h"
|
||||
|
||||
#define MAX_AIO_REQS (MAX_REQUESTS * MAX_SEGMENTS_PER_REQ)
|
||||
|
||||
/* *BSD has no O_LARGEFILE */
|
||||
#ifndef O_LARGEFILE
|
||||
#define O_LARGEFILE 0
|
||||
#endif
|
||||
|
||||
struct tdaio_state {
|
||||
int fd;
|
||||
tap_aio_context_t aio;
|
||||
};
|
||||
|
||||
|
||||
/*Get Image size, secsize*/
|
||||
static int get_image_info(struct td_state *s, int fd)
|
||||
{
|
||||
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*/
|
||||
if (blk_getimagesize(fd, &s->size) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
DPRINTF("Image size: \n\tpre sector_shift [%llu]\n\tpost "
|
||||
"sector_shift [%llu]\n",
|
||||
(long long unsigned)(s->size << SECTOR_SHIFT),
|
||||
(long long unsigned)s->size);
|
||||
|
||||
/*Get the sector size*/
|
||||
if (blk_getsectorsize(fd, &s->sector_size) != 0)
|
||||
s->sector_size = DEFAULT_SECTOR_SIZE;
|
||||
|
||||
} else {
|
||||
/*Local file? try fstat instead*/
|
||||
s->size = (stat.st_size >> SECTOR_SHIFT);
|
||||
s->sector_size = DEFAULT_SECTOR_SIZE;
|
||||
DPRINTF("Image size: \n\tpre sector_shift [%llu]\n\tpost "
|
||||
"sector_shift [%llu]\n",
|
||||
(long long unsigned)(s->size << SECTOR_SHIFT),
|
||||
(long long unsigned)s->size);
|
||||
}
|
||||
|
||||
if (s->size == 0) {
|
||||
s->size =((uint64_t) 16836057);
|
||||
s->sector_size = DEFAULT_SECTOR_SIZE;
|
||||
}
|
||||
s->info = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void init_fds(struct disk_driver *dd)
|
||||
{
|
||||
int i;
|
||||
struct tdaio_state *prv = (struct tdaio_state *)dd->private;
|
||||
|
||||
for(i = 0; i < MAX_IOFD; i++)
|
||||
dd->io_fd[i] = 0;
|
||||
|
||||
dd->io_fd[0] = prv->aio.aio_ctx.pollfd;
|
||||
}
|
||||
|
||||
/* Open the disk file and initialize aio state. */
|
||||
static int tdaio_open (struct disk_driver *dd, const char *name, td_flag_t flags)
|
||||
{
|
||||
int i, fd, ret = 0, o_flags;
|
||||
struct td_state *s = dd->td_state;
|
||||
struct tdaio_state *prv = (struct tdaio_state *)dd->private;
|
||||
|
||||
DPRINTF("block-aio open('%s')", name);
|
||||
|
||||
/* Initialize AIO */
|
||||
ret = tap_aio_init(&prv->aio, 0, MAX_AIO_REQS);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
/* Open the file */
|
||||
o_flags = O_DIRECT | O_LARGEFILE |
|
||||
((flags == TD_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;
|
||||
}
|
||||
|
||||
prv->fd = fd;
|
||||
|
||||
init_fds(dd);
|
||||
ret = get_image_info(s, fd);
|
||||
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tdaio_queue_read(struct disk_driver *dd, uint64_t sector,
|
||||
int nb_sectors, char *buf, td_callback_t cb,
|
||||
int id, void *private)
|
||||
{
|
||||
struct td_state *s = dd->td_state;
|
||||
struct tdaio_state *prv = (struct tdaio_state *)dd->private;
|
||||
int size = nb_sectors * s->sector_size;
|
||||
uint64_t offset = sector * (uint64_t)s->sector_size;
|
||||
|
||||
return tap_aio_read(&prv->aio, prv->fd, size, offset, buf,
|
||||
cb, id, sector, private);
|
||||
}
|
||||
|
||||
static int tdaio_queue_write(struct disk_driver *dd, uint64_t sector,
|
||||
int nb_sectors, char *buf, td_callback_t cb,
|
||||
int id, void *private)
|
||||
{
|
||||
struct td_state *s = dd->td_state;
|
||||
struct tdaio_state *prv = (struct tdaio_state *)dd->private;
|
||||
int size = nb_sectors * s->sector_size;
|
||||
uint64_t offset = sector * (uint64_t)s->sector_size;
|
||||
|
||||
return tap_aio_write(&prv->aio, prv->fd, size, offset, buf,
|
||||
cb, id, sector, private);
|
||||
}
|
||||
|
||||
static int tdaio_submit(struct disk_driver *dd)
|
||||
{
|
||||
struct tdaio_state *prv = (struct tdaio_state *)dd->private;
|
||||
|
||||
return tap_aio_submit(&prv->aio);
|
||||
}
|
||||
|
||||
static int tdaio_close(struct disk_driver *dd)
|
||||
{
|
||||
struct tdaio_state *prv = (struct tdaio_state *)dd->private;
|
||||
|
||||
io_destroy(prv->aio.aio_ctx.aio_ctx);
|
||||
close(prv->fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tdaio_do_callbacks(struct disk_driver *dd, int sid)
|
||||
{
|
||||
int i, nr_events, rsp = 0;
|
||||
struct io_event *ep;
|
||||
struct tdaio_state *prv = (struct tdaio_state *)dd->private;
|
||||
|
||||
nr_events = tap_aio_get_events(&prv->aio.aio_ctx);
|
||||
repeat:
|
||||
for (ep = prv->aio.aio_events, i = nr_events; i-- > 0; ep++) {
|
||||
struct iocb *io = ep->obj;
|
||||
struct pending_aio *pio;
|
||||
|
||||
pio = &prv->aio.pending_aio[(long)io->data];
|
||||
rsp += pio->cb(dd, ep->res == io->u.c.nbytes ? 0 : 1,
|
||||
pio->sector, io->u.c.nbytes >> 9,
|
||||
pio->id, pio->private);
|
||||
|
||||
prv->aio.iocb_free[prv->aio.iocb_free_count++] = io;
|
||||
}
|
||||
|
||||
if (nr_events) {
|
||||
nr_events = tap_aio_more_events(&prv->aio.aio_ctx);
|
||||
goto repeat;
|
||||
}
|
||||
|
||||
tap_aio_continue(&prv->aio.aio_ctx);
|
||||
|
||||
return rsp;
|
||||
}
|
||||
|
||||
static int tdaio_get_parent_id(struct disk_driver *dd, struct disk_id *id)
|
||||
{
|
||||
return TD_NO_PARENT;
|
||||
}
|
||||
|
||||
static int tdaio_validate_parent(struct disk_driver *dd,
|
||||
struct disk_driver *parent, td_flag_t flags)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
struct tap_disk tapdisk_aio = {
|
||||
.disk_type = "tapdisk_aio",
|
||||
.private_data_size = sizeof(struct tdaio_state),
|
||||
.td_open = tdaio_open,
|
||||
.td_queue_read = tdaio_queue_read,
|
||||
.td_queue_write = tdaio_queue_write,
|
||||
.td_submit = tdaio_submit,
|
||||
.td_close = tdaio_close,
|
||||
.td_do_callbacks = tdaio_do_callbacks,
|
||||
.td_get_parent_id = tdaio_get_parent_id,
|
||||
.td_validate_parent = tdaio_validate_parent
|
||||
};
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -1,295 +0,0 @@
|
||||
/* block-ram.c
|
||||
*
|
||||
* Fast Ramdisk implementation.
|
||||
*
|
||||
* (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 <inttypes.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <string.h>
|
||||
#include "tapdisk.h"
|
||||
#include "blk.h"
|
||||
|
||||
#define MAX_DISK_SIZE 1024000 /*500MB disk limit*/
|
||||
|
||||
/* *BSD has no O_LARGEFILE */
|
||||
#ifndef O_LARGEFILE
|
||||
#define O_LARGEFILE 0
|
||||
#endif
|
||||
|
||||
char *img;
|
||||
long int disksector_size;
|
||||
long int disksize;
|
||||
long int diskinfo;
|
||||
static int connections = 0;
|
||||
|
||||
struct tdram_state {
|
||||
int fd;
|
||||
int poll_pipe[2]; /* dummy fd for polling on */
|
||||
};
|
||||
|
||||
/*Get Image size, secsize*/
|
||||
static int get_image_info(struct td_state *s, int fd)
|
||||
{
|
||||
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*/
|
||||
if (blk_getimagesize(fd, &s->size) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
DPRINTF("Image size: \n\tpre sector_shift [%llu]\n\tpost "
|
||||
"sector_shift [%llu]\n",
|
||||
(long long unsigned)(s->size << SECTOR_SHIFT),
|
||||
(long long unsigned)s->size);
|
||||
|
||||
/*Get the sector size*/
|
||||
if (blk_getsectorsize(fd, &s->sector_size) != 0)
|
||||
s->sector_size = DEFAULT_SECTOR_SIZE;
|
||||
|
||||
} else {
|
||||
/*Local file? try fstat instead*/
|
||||
s->size = (stat.st_size >> SECTOR_SHIFT);
|
||||
s->sector_size = DEFAULT_SECTOR_SIZE;
|
||||
DPRINTF("Image size: \n\tpre sector_shift [%llu]\n\tpost "
|
||||
"sector_shift [%llu]\n",
|
||||
(long long unsigned)(s->size << SECTOR_SHIFT),
|
||||
(long long unsigned)s->size);
|
||||
}
|
||||
|
||||
if (s->size == 0) {
|
||||
s->size =((uint64_t) MAX_DISK_SIZE);
|
||||
s->sector_size = DEFAULT_SECTOR_SIZE;
|
||||
}
|
||||
s->info = 0;
|
||||
|
||||
/*Store variables locally*/
|
||||
disksector_size = s->sector_size;
|
||||
disksize = s->size;
|
||||
diskinfo = s->info;
|
||||
DPRINTF("Image sector_size: \n\t[%"PRIu64"]\n",
|
||||
s->sector_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void init_fds(struct disk_driver *dd)
|
||||
{
|
||||
int i;
|
||||
struct tdram_state *prv = (struct tdram_state *)dd->private;
|
||||
|
||||
for(i =0 ; i < MAX_IOFD; i++)
|
||||
dd->io_fd[i] = 0;
|
||||
|
||||
dd->io_fd[0] = prv->poll_pipe[0];
|
||||
}
|
||||
|
||||
/* Open the disk file and initialize ram state. */
|
||||
static int tdram_open (struct disk_driver *dd, const char *name, td_flag_t flags)
|
||||
{
|
||||
char *p;
|
||||
uint64_t size;
|
||||
int i, fd, ret = 0, count = 0, o_flags;
|
||||
struct td_state *s = dd->td_state;
|
||||
struct tdram_state *prv = (struct tdram_state *)dd->private;
|
||||
|
||||
connections++;
|
||||
|
||||
/* set up a pipe so that we can hand back a poll fd that won't fire.*/
|
||||
ret = pipe(prv->poll_pipe);
|
||||
if (ret != 0)
|
||||
return (0 - errno);
|
||||
|
||||
if (connections > 1) {
|
||||
s->sector_size = disksector_size;
|
||||
s->size = disksize;
|
||||
s->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)(s->size << SECTOR_SHIFT),
|
||||
(long long unsigned)s->size);
|
||||
DPRINTF("Image sector_size: \n\t[%"PRIu64"]\n",
|
||||
s->sector_size);
|
||||
|
||||
prv->fd = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Open the file */
|
||||
o_flags = O_DIRECT | O_LARGEFILE |
|
||||
((flags == TD_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(s, fd);
|
||||
size = MAX_DISK_SIZE;
|
||||
|
||||
if (s->size > size) {
|
||||
DPRINTF("Disk exceeds limit, must be less than [%d]MB",
|
||||
(MAX_DISK_SIZE<<SECTOR_SHIFT)>>20);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/*Read the image into memory*/
|
||||
p = img = malloc(s->size << SECTOR_SHIFT);
|
||||
if (img == NULL) {
|
||||
DPRINTF("Mem malloc failed\n");
|
||||
return -1;
|
||||
}
|
||||
DPRINTF("Reading %llu bytes.......",(long long unsigned)s->size << SECTOR_SHIFT);
|
||||
|
||||
for (i = 0; i < s->size; i++) {
|
||||
ret = read(prv->fd, p, s->sector_size);
|
||||
if (ret != s->sector_size) {
|
||||
ret = 0 - errno;
|
||||
break;
|
||||
} else {
|
||||
count += ret;
|
||||
p = img + count;
|
||||
}
|
||||
}
|
||||
DPRINTF("[%d]\n",count);
|
||||
if (count != s->size << SECTOR_SHIFT) {
|
||||
ret = -1;
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
init_fds(dd);
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tdram_queue_read(struct disk_driver *dd, uint64_t sector,
|
||||
int nb_sectors, char *buf, td_callback_t cb,
|
||||
int id, void *private)
|
||||
{
|
||||
struct td_state *s = dd->td_state;
|
||||
struct tdram_state *prv = (struct tdram_state *)dd->private;
|
||||
int size = nb_sectors * s->sector_size;
|
||||
uint64_t offset = sector * (uint64_t)s->sector_size;
|
||||
|
||||
memcpy(buf, img + offset, size);
|
||||
|
||||
return cb(dd, 0, sector, nb_sectors, id, private);
|
||||
}
|
||||
|
||||
static int tdram_queue_write(struct disk_driver *dd, uint64_t sector,
|
||||
int nb_sectors, char *buf, td_callback_t cb,
|
||||
int id, void *private)
|
||||
{
|
||||
struct td_state *s = dd->td_state;
|
||||
struct tdram_state *prv = (struct tdram_state *)dd->private;
|
||||
int size = nb_sectors * s->sector_size;
|
||||
uint64_t offset = sector * (uint64_t)s->sector_size;
|
||||
|
||||
/* We assume that write access is controlled
|
||||
* at a higher level for multiple disks */
|
||||
memcpy(img + offset, buf, size);
|
||||
|
||||
return cb(dd, 0, sector, nb_sectors, id, private);
|
||||
}
|
||||
|
||||
static int tdram_submit(struct disk_driver *dd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tdram_close(struct disk_driver *dd)
|
||||
{
|
||||
struct tdram_state *prv = (struct tdram_state *)dd->private;
|
||||
|
||||
connections--;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tdram_do_callbacks(struct disk_driver *dd, int sid)
|
||||
{
|
||||
/* always ask for a kick */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int tdram_get_parent_id(struct disk_driver *dd, struct disk_id *id)
|
||||
{
|
||||
return TD_NO_PARENT;
|
||||
}
|
||||
|
||||
static int tdram_validate_parent(struct disk_driver *dd,
|
||||
struct disk_driver *parent, td_flag_t flags)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
struct tap_disk tapdisk_ram = {
|
||||
.disk_type = "tapdisk_ram",
|
||||
.private_data_size = sizeof(struct tdram_state),
|
||||
.td_open = tdram_open,
|
||||
.td_queue_read = tdram_queue_read,
|
||||
.td_queue_write = tdram_queue_write,
|
||||
.td_submit = tdram_submit,
|
||||
.td_close = tdram_close,
|
||||
.td_do_callbacks = tdram_do_callbacks,
|
||||
.td_get_parent_id = tdram_get_parent_id,
|
||||
.td_validate_parent = tdram_validate_parent
|
||||
};
|
||||
@@ -1,242 +0,0 @@
|
||||
/* block-sync.c
|
||||
*
|
||||
* simple slow synchronous raw disk implementation.
|
||||
*
|
||||
* (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 "tapdisk.h"
|
||||
#include "blk.h"
|
||||
|
||||
/* *BSD has no O_LARGEFILE */
|
||||
#ifndef O_LARGEFILE
|
||||
#define O_LARGEFILE 0
|
||||
#endif
|
||||
|
||||
struct tdsync_state {
|
||||
int fd;
|
||||
int poll_pipe[2]; /* dummy fd for polling on */
|
||||
};
|
||||
|
||||
/*Get Image size, secsize*/
|
||||
static int get_image_info(struct td_state *s, int fd)
|
||||
{
|
||||
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*/
|
||||
if (blk_getimagesize(fd, &s->size) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
DPRINTF("Image size: \n\tpre sector_shift [%llu]\n\tpost "
|
||||
"sector_shift [%llu]\n",
|
||||
(long long unsigned)(s->size << SECTOR_SHIFT),
|
||||
(long long unsigned)s->size);
|
||||
|
||||
/*Get the sector size*/
|
||||
if (blk_getsectorsize(fd, &s->sector_size) != 0)
|
||||
s->sector_size = DEFAULT_SECTOR_SIZE;
|
||||
|
||||
} else {
|
||||
/*Local file? try fstat instead*/
|
||||
s->size = (stat.st_size >> SECTOR_SHIFT);
|
||||
s->sector_size = DEFAULT_SECTOR_SIZE;
|
||||
DPRINTF("Image size: \n\tpre sector_shift [%lluu]\n\tpost "
|
||||
"sector_shift [%lluu]\n",
|
||||
(long long unsigned)(s->size << SECTOR_SHIFT),
|
||||
(long long unsigned)s->size);
|
||||
}
|
||||
|
||||
if (s->size == 0)
|
||||
return -EINVAL;
|
||||
|
||||
s->info = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void init_fds(struct disk_driver *dd)
|
||||
{
|
||||
int i;
|
||||
struct tdsync_state *prv = (struct tdsync_state *)dd->private;
|
||||
|
||||
for(i = 0; i < MAX_IOFD; i++)
|
||||
dd->io_fd[i] = 0;
|
||||
|
||||
dd->io_fd[0] = prv->poll_pipe[0];
|
||||
}
|
||||
|
||||
/* Open the disk file and initialize aio state. */
|
||||
static int tdsync_open (struct disk_driver *dd, const char *name, td_flag_t flags)
|
||||
{
|
||||
int i, fd, ret = 0, o_flags;
|
||||
struct td_state *s = dd->td_state;
|
||||
struct tdsync_state *prv = (struct tdsync_state *)dd->private;
|
||||
|
||||
/* set up a pipe so that we can hand back a poll fd that won't fire.*/
|
||||
ret = pipe(prv->poll_pipe);
|
||||
if (ret != 0)
|
||||
return (0 - errno);
|
||||
|
||||
/* Open the file */
|
||||
o_flags = O_DIRECT | O_LARGEFILE |
|
||||
((flags == TD_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;
|
||||
|
||||
init_fds(dd);
|
||||
ret = get_image_info(s, fd);
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tdsync_queue_read(struct disk_driver *dd, uint64_t sector,
|
||||
int nb_sectors, char *buf, td_callback_t cb,
|
||||
int id, void *private)
|
||||
{
|
||||
struct td_state *s = dd->td_state;
|
||||
struct tdsync_state *prv = (struct tdsync_state *)dd->private;
|
||||
int size = nb_sectors * s->sector_size;
|
||||
uint64_t offset = sector * (uint64_t)s->sector_size;
|
||||
int ret;
|
||||
|
||||
ret = lseek(prv->fd, offset, SEEK_SET);
|
||||
if (ret != (off_t)-1) {
|
||||
ret = read(prv->fd, buf, size);
|
||||
if (ret != size) {
|
||||
ret = 0 - errno;
|
||||
} else {
|
||||
ret = 1;
|
||||
}
|
||||
} else ret = 0 - errno;
|
||||
|
||||
return cb(dd, (ret < 0) ? ret: 0, sector, nb_sectors, id, private);
|
||||
}
|
||||
|
||||
static int tdsync_queue_write(struct disk_driver *dd, uint64_t sector,
|
||||
int nb_sectors, char *buf, td_callback_t cb,
|
||||
int id, void *private)
|
||||
{
|
||||
struct td_state *s = dd->td_state;
|
||||
struct tdsync_state *prv = (struct tdsync_state *)dd->private;
|
||||
int size = nb_sectors * s->sector_size;
|
||||
uint64_t offset = sector * (uint64_t)s->sector_size;
|
||||
int ret = 0;
|
||||
|
||||
ret = lseek(prv->fd, offset, SEEK_SET);
|
||||
if (ret != (off_t)-1) {
|
||||
ret = write(prv->fd, buf, size);
|
||||
if (ret != size) {
|
||||
ret = 0 - errno;
|
||||
} else {
|
||||
ret = 1;
|
||||
}
|
||||
} else ret = 0 - errno;
|
||||
|
||||
return cb(dd, (ret < 0) ? ret : 0, sector, nb_sectors, id, private);
|
||||
}
|
||||
|
||||
static int tdsync_submit(struct disk_driver *dd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tdsync_close(struct disk_driver *dd)
|
||||
{
|
||||
struct tdsync_state *prv = (struct tdsync_state *)dd->private;
|
||||
|
||||
close(prv->fd);
|
||||
close(prv->poll_pipe[0]);
|
||||
close(prv->poll_pipe[1]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tdsync_do_callbacks(struct disk_driver *dd, int sid)
|
||||
{
|
||||
/* always ask for a kick */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int tdsync_get_parent_id(struct disk_driver *dd, struct disk_id *id)
|
||||
{
|
||||
return TD_NO_PARENT;
|
||||
}
|
||||
|
||||
static int tdsync_validate_parent(struct disk_driver *dd,
|
||||
struct disk_driver *parent, td_flag_t flags)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
struct tap_disk tapdisk_sync = {
|
||||
.disk_type = "tapdisk_sync",
|
||||
.private_data_size = sizeof(struct tdsync_state),
|
||||
.td_open = tdsync_open,
|
||||
.td_queue_read = tdsync_queue_read,
|
||||
.td_queue_write = tdsync_queue_write,
|
||||
.td_submit = tdsync_submit,
|
||||
.td_close = tdsync_close,
|
||||
.td_do_callbacks = tdsync_do_callbacks,
|
||||
.td_get_parent_id = tdsync_get_parent_id,
|
||||
.td_validate_parent = tdsync_validate_parent
|
||||
};
|
||||
@@ -1,428 +0,0 @@
|
||||
/* block-vmdk.c
|
||||
*
|
||||
* VMware Disk format implementation.
|
||||
*
|
||||
* (c) 2006 Andrew Warfield and Julian Chesterfield
|
||||
*
|
||||
* This is largely the same as the vmdk driver in Qemu, I've just twisted it
|
||||
* to match our interfaces. The original (BSDish) Copyright message appears
|
||||
* below:
|
||||
*/
|
||||
|
||||
/*
|
||||
* Block driver for the VMDK format
|
||||
*
|
||||
* Copyright (c) 2004 Fabrice Bellard
|
||||
* Copyright (c) 2005 Filip Navara
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (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 "bswap.h"
|
||||
|
||||
/* *BSD has no O_LARGEFILE */
|
||||
#ifndef O_LARGEFILE
|
||||
#define O_LARGEFILE 0
|
||||
#endif
|
||||
|
||||
#define safer_free(_x) \
|
||||
do { \
|
||||
if (NULL != _x) { \
|
||||
free(_x); \
|
||||
(_x) = NULL; \
|
||||
} \
|
||||
} while (0) ;
|
||||
|
||||
#define VMDK3_MAGIC (('C' << 24) | ('O' << 16) | ('W' << 8) | 'D')
|
||||
#define VMDK4_MAGIC (('K' << 24) | ('D' << 16) | ('M' << 8) | 'V')
|
||||
|
||||
typedef struct {
|
||||
uint32_t version;
|
||||
uint32_t flags;
|
||||
uint32_t disk_sectors;
|
||||
uint32_t granularity;
|
||||
uint32_t l1dir_offset;
|
||||
uint32_t l1dir_size;
|
||||
uint32_t file_sectors;
|
||||
uint32_t cylinders;
|
||||
uint32_t heads;
|
||||
uint32_t sectors_per_track;
|
||||
} VMDK3Header;
|
||||
|
||||
typedef struct {
|
||||
uint32_t version;
|
||||
uint32_t flags;
|
||||
int64_t capacity;
|
||||
int64_t granularity;
|
||||
int64_t desc_offset;
|
||||
int64_t desc_size;
|
||||
int32_t num_gtes_per_gte;
|
||||
int64_t rgd_offset;
|
||||
int64_t gd_offset;
|
||||
int64_t grain_offset;
|
||||
char filler[1];
|
||||
char check_bytes[4];
|
||||
} __attribute__((packed)) VMDK4Header;
|
||||
|
||||
#define L2_CACHE_SIZE 16
|
||||
|
||||
struct tdvmdk_state {
|
||||
int fd;
|
||||
int poll_pipe[2]; /* dummy fd for polling on */
|
||||
|
||||
unsigned int l1_size;
|
||||
int64_t l1_table_offset;
|
||||
int64_t l1_backup_table_offset;
|
||||
uint32_t l1_entry_sectors;
|
||||
unsigned int l2_size;
|
||||
|
||||
uint32_t *l1_table;
|
||||
uint32_t *l1_backup_table;
|
||||
uint32_t *l2_cache;
|
||||
uint32_t l2_cache_offsets[L2_CACHE_SIZE];
|
||||
uint32_t l2_cache_counts[L2_CACHE_SIZE];
|
||||
|
||||
unsigned int cluster_sectors;
|
||||
};
|
||||
|
||||
static inline void init_fds(struct disk_driver *dd)
|
||||
{
|
||||
int i;
|
||||
struct tdvmdk_state *prv = (struct tdvmdk_state *)dd->private;
|
||||
|
||||
for (i = 0; i < MAX_IOFD; i++)
|
||||
dd->io_fd[i] = 0;
|
||||
|
||||
dd->io_fd[0] = prv->poll_pipe[0];
|
||||
}
|
||||
|
||||
/* Open the disk file and initialize aio state. */
|
||||
static int tdvmdk_open (struct disk_driver *dd,
|
||||
const char *name, td_flag_t flags)
|
||||
{
|
||||
int ret, fd;
|
||||
int l1_size, i, o_flags;
|
||||
uint32_t magic;
|
||||
struct td_state *s = dd->td_state;
|
||||
struct tdvmdk_state *prv = (struct tdvmdk_state *)dd->private;
|
||||
|
||||
/* set up a pipe so that we can hand back a poll fd that won't fire.*/
|
||||
ret = pipe(prv->poll_pipe);
|
||||
if (ret != 0)
|
||||
return -1;
|
||||
|
||||
/* Open the file */
|
||||
o_flags = O_DIRECT | O_LARGEFILE |
|
||||
((flags == TD_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;
|
||||
return -1;
|
||||
}
|
||||
|
||||
prv->fd = fd;
|
||||
|
||||
/* Grok the vmdk header. */
|
||||
if ((ret = read(fd, &magic, sizeof(magic))) != sizeof(magic))
|
||||
goto fail;
|
||||
magic = be32_to_cpu(magic);
|
||||
if (magic == VMDK3_MAGIC) {
|
||||
VMDK3Header header;
|
||||
if (read(fd, &header, sizeof(header)) !=
|
||||
sizeof(header))
|
||||
goto fail;
|
||||
prv->cluster_sectors = le32_to_cpu(header.granularity);
|
||||
prv->l2_size = 1 << 9;
|
||||
prv->l1_size = 1 << 6;
|
||||
s->size = le32_to_cpu(header.disk_sectors);
|
||||
prv->l1_table_offset = le32_to_cpu(header.l1dir_offset) << 9;
|
||||
prv->l1_backup_table_offset = 0;
|
||||
prv->l1_entry_sectors = prv->l2_size * prv->cluster_sectors;
|
||||
} else if (magic == VMDK4_MAGIC) {
|
||||
VMDK4Header header;
|
||||
|
||||
if (read(fd, &header, sizeof(header)) != sizeof(header))
|
||||
goto fail;
|
||||
s->size = le32_to_cpu(header.capacity);
|
||||
prv->cluster_sectors = le32_to_cpu(header.granularity);
|
||||
prv->l2_size = le32_to_cpu(header.num_gtes_per_gte);
|
||||
prv->l1_entry_sectors = prv->l2_size * prv->cluster_sectors;
|
||||
if (prv->l1_entry_sectors <= 0)
|
||||
goto fail;
|
||||
prv->l1_size = (s->size + prv->l1_entry_sectors - 1)
|
||||
/ prv->l1_entry_sectors;
|
||||
prv->l1_table_offset = le64_to_cpu(header.rgd_offset) << 9;
|
||||
prv->l1_backup_table_offset =
|
||||
le64_to_cpu(header.gd_offset) << 9;
|
||||
} else {
|
||||
goto fail;
|
||||
}
|
||||
/* read the L1 table */
|
||||
l1_size = prv->l1_size * sizeof(uint32_t);
|
||||
prv->l1_table = malloc(l1_size);
|
||||
if (!prv->l1_table)
|
||||
goto fail;
|
||||
if (lseek(fd, prv->l1_table_offset, SEEK_SET) == -1)
|
||||
goto fail;
|
||||
if (read(fd, prv->l1_table, l1_size) != l1_size)
|
||||
goto fail;
|
||||
for (i = 0; i < prv->l1_size; i++) {
|
||||
le32_to_cpus(&prv->l1_table[i]);
|
||||
}
|
||||
|
||||
if (prv->l1_backup_table_offset) {
|
||||
prv->l1_backup_table = malloc(l1_size);
|
||||
if (!prv->l1_backup_table)
|
||||
goto fail;
|
||||
if (lseek(fd, prv->l1_backup_table_offset, SEEK_SET) == -1)
|
||||
goto fail;
|
||||
if (read(fd, prv->l1_backup_table, l1_size) != l1_size)
|
||||
goto fail;
|
||||
for(i = 0; i < prv->l1_size; i++) {
|
||||
le32_to_cpus(&prv->l1_backup_table[i]);
|
||||
}
|
||||
}
|
||||
|
||||
prv->l2_cache = malloc(prv->l2_size * L2_CACHE_SIZE *sizeof(uint32_t));
|
||||
if (!prv->l2_cache)
|
||||
goto fail;
|
||||
prv->fd = fd;
|
||||
init_fds(dd);
|
||||
DPRINTF("VMDK File opened successfully\n");
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
DPRINTF("VMDK File open failed.\n");
|
||||
safer_free(prv->l1_backup_table);
|
||||
free(prv->l1_table);
|
||||
free(prv->l2_cache);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static uint64_t get_cluster_offset(struct tdvmdk_state *prv,
|
||||
uint64_t offset, int allocate)
|
||||
{
|
||||
unsigned int l1_index, l2_offset, l2_index;
|
||||
int min_index, i, j;
|
||||
uint32_t min_count, *l2_table, tmp;
|
||||
uint64_t cluster_offset;
|
||||
|
||||
l1_index = (offset >> 9) / prv->l1_entry_sectors;
|
||||
if (l1_index >= prv->l1_size)
|
||||
return 0;
|
||||
l2_offset = prv->l1_table[l1_index];
|
||||
if (!l2_offset)
|
||||
return 0;
|
||||
for (i = 0; i < L2_CACHE_SIZE; i++) {
|
||||
if (l2_offset == prv->l2_cache_offsets[i]) {
|
||||
/* increment the hit count */
|
||||
if (++prv->l2_cache_counts[i] == 0xffffffff) {
|
||||
for(j = 0; j < L2_CACHE_SIZE; j++) {
|
||||
prv->l2_cache_counts[j] >>= 1;
|
||||
}
|
||||
}
|
||||
l2_table = prv->l2_cache + (i * prv->l2_size);
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
/* not found: load a new entry in the least used one */
|
||||
min_index = 0;
|
||||
min_count = 0xffffffff;
|
||||
for (i = 0; i < L2_CACHE_SIZE; i++) {
|
||||
if (prv->l2_cache_counts[i] < min_count) {
|
||||
min_count = prv->l2_cache_counts[i];
|
||||
min_index = i;
|
||||
}
|
||||
}
|
||||
l2_table = prv->l2_cache + (min_index * prv->l2_size);
|
||||
lseek(prv->fd, (int64_t)l2_offset * 512, SEEK_SET);
|
||||
if (read(prv->fd, l2_table, prv->l2_size * sizeof(uint32_t)) !=
|
||||
prv->l2_size * sizeof(uint32_t))
|
||||
return 0;
|
||||
prv->l2_cache_offsets[min_index] = l2_offset;
|
||||
prv->l2_cache_counts[min_index] = 1;
|
||||
found:
|
||||
l2_index = ((offset >> 9) / prv->cluster_sectors) % prv->l2_size;
|
||||
cluster_offset = le32_to_cpu(l2_table[l2_index]);
|
||||
if (!cluster_offset) {
|
||||
if (!allocate)
|
||||
return 0;
|
||||
cluster_offset = lseek(prv->fd, 0, SEEK_END);
|
||||
if (ftruncate(prv->fd, cluster_offset +
|
||||
(prv->cluster_sectors << 9)))
|
||||
return 0;
|
||||
cluster_offset >>= 9;
|
||||
/* update L2 table */
|
||||
tmp = cpu_to_le32(cluster_offset);
|
||||
l2_table[l2_index] = tmp;
|
||||
lseek(prv->fd, ((int64_t)l2_offset * 512) +
|
||||
(l2_index * sizeof(tmp)), SEEK_SET);
|
||||
if (write(prv->fd, &tmp, sizeof(tmp)) != sizeof(tmp))
|
||||
return 0;
|
||||
/* update backup L2 table */
|
||||
if (prv->l1_backup_table_offset != 0) {
|
||||
l2_offset = prv->l1_backup_table[l1_index];
|
||||
lseek(prv->fd, ((int64_t)l2_offset * 512) +
|
||||
(l2_index * sizeof(tmp)), SEEK_SET);
|
||||
if (write(prv->fd, &tmp, sizeof(tmp)) != sizeof(tmp))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
cluster_offset <<= 9;
|
||||
return cluster_offset;
|
||||
}
|
||||
|
||||
static int tdvmdk_queue_read(struct disk_driver *dd, uint64_t sector,
|
||||
int nb_sectors, char *buf, td_callback_t cb,
|
||||
int id, void *private)
|
||||
{
|
||||
struct tdvmdk_state *prv = (struct tdvmdk_state *)dd->private;
|
||||
int index_in_cluster, n;
|
||||
uint64_t cluster_offset;
|
||||
int ret = 0;
|
||||
|
||||
while (nb_sectors > 0) {
|
||||
cluster_offset = get_cluster_offset(prv, sector << 9, 0);
|
||||
index_in_cluster = sector % prv->cluster_sectors;
|
||||
n = prv->cluster_sectors - index_in_cluster;
|
||||
if (n > nb_sectors)
|
||||
n = nb_sectors;
|
||||
if (!cluster_offset) {
|
||||
memset(buf, 0, 512 * n);
|
||||
} else {
|
||||
lseek(prv->fd, cluster_offset + index_in_cluster * 512,
|
||||
SEEK_SET);
|
||||
ret = read(prv->fd, buf, n * 512);
|
||||
if (ret != n * 512) {
|
||||
ret = -1;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
nb_sectors -= n;
|
||||
sector += n;
|
||||
buf += n * 512;
|
||||
}
|
||||
done:
|
||||
return cb(dd, ret == -1 ? -1 : 0, sector, nb_sectors, id, private);
|
||||
}
|
||||
|
||||
static int tdvmdk_queue_write(struct disk_driver *dd, uint64_t sector,
|
||||
int nb_sectors, char *buf, td_callback_t cb,
|
||||
int id, void *private)
|
||||
{
|
||||
struct tdvmdk_state *prv = (struct tdvmdk_state *)dd->private;
|
||||
int index_in_cluster, n;
|
||||
uint64_t cluster_offset;
|
||||
int ret = 0;
|
||||
|
||||
while (nb_sectors > 0) {
|
||||
index_in_cluster = sector & (prv->cluster_sectors - 1);
|
||||
n = prv->cluster_sectors - index_in_cluster;
|
||||
if (n > nb_sectors)
|
||||
n = nb_sectors;
|
||||
cluster_offset = get_cluster_offset(prv, sector << 9, 1);
|
||||
if (!cluster_offset) {
|
||||
ret = -1;
|
||||
goto done;
|
||||
}
|
||||
lseek(prv->fd, cluster_offset + index_in_cluster * 512,
|
||||
SEEK_SET);
|
||||
ret = write(prv->fd, buf, n * 512);
|
||||
if (ret != n * 512) {
|
||||
ret = -1;
|
||||
goto done;
|
||||
}
|
||||
nb_sectors -= n;
|
||||
sector += n;
|
||||
buf += n * 512;
|
||||
}
|
||||
done:
|
||||
return cb(dd, ret == -1 ? -1 : 0, sector, nb_sectors, id, private);
|
||||
}
|
||||
|
||||
static int tdvmdk_submit(struct disk_driver *dd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tdvmdk_close(struct disk_driver *dd)
|
||||
{
|
||||
struct tdvmdk_state *prv = (struct tdvmdk_state *)dd->private;
|
||||
|
||||
safer_free(prv->l1_table);
|
||||
safer_free(prv->l1_backup_table);
|
||||
safer_free(prv->l2_cache);
|
||||
close(prv->fd);
|
||||
close(prv->poll_pipe[0]);
|
||||
close(prv->poll_pipe[1]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tdvmdk_do_callbacks(struct disk_driver *dd, int sid)
|
||||
{
|
||||
/* always ask for a kick */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int tdvmdk_get_parent_id(struct disk_driver *dd, struct disk_id *id)
|
||||
{
|
||||
return TD_NO_PARENT;
|
||||
}
|
||||
|
||||
static int tdvmdk_validate_parent(struct disk_driver *dd,
|
||||
struct disk_driver *parent, td_flag_t flags)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
struct tap_disk tapdisk_vmdk = {
|
||||
.disk_type = "tapdisk_vmdk",
|
||||
.private_data_size = sizeof(struct tdvmdk_state),
|
||||
.td_open = tdvmdk_open,
|
||||
.td_queue_read = tdvmdk_queue_read,
|
||||
.td_queue_write = tdvmdk_queue_write,
|
||||
.td_submit = tdvmdk_submit,
|
||||
.td_close = tdvmdk_close,
|
||||
.td_do_callbacks = tdvmdk_do_callbacks,
|
||||
.td_get_parent_id = tdvmdk_get_parent_id,
|
||||
.td_validate_parent = tdvmdk_validate_parent
|
||||
};
|
||||
@@ -1,178 +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 <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,282 +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 "tapdisk.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 TAPDISK 1
|
||||
#define BLOCK_PROCESSSZ 4096
|
||||
|
||||
static int maxfds, *io_fd, running = 1, complete = 0;
|
||||
static int returned_events = 0, submit_events = 0;
|
||||
static uint64_t prev = 0;
|
||||
static char output[25];
|
||||
|
||||
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)
|
||||
{
|
||||
uint64_t blocks = size/20;
|
||||
|
||||
/*Output progress every 5% */
|
||||
if (progress/blocks > prev) {
|
||||
memcpy(output+prev+1,"=>",2);
|
||||
prev++;
|
||||
DFPRINTF("\r%s %llu%%", output,
|
||||
(long long)(prev-1)*5);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static inline void LOCAL_FD_SET(fd_set *readfds)
|
||||
{
|
||||
FD_SET(io_fd[0], readfds);
|
||||
maxfds = io_fd[0] + 1;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int get_image_info(struct td_state *s, int fd)
|
||||
{
|
||||
int ret;
|
||||
long size;
|
||||
unsigned long total_size;
|
||||
struct statvfs statBuf;
|
||||
struct stat stat;
|
||||
|
||||
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, &s->size) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
DFPRINTF("Image size: \n\tpre sector_shift [%llu]\n\tpost "
|
||||
"sector_shift [%llu]\n",
|
||||
(long long unsigned)(s->size << SECTOR_SHIFT),
|
||||
(long long unsigned)s->size);
|
||||
|
||||
/*Get the sector size*/
|
||||
if (blk_getsectorsize(fd, &s->sector_size) != 0)
|
||||
s->sector_size = DEFAULT_SECTOR_SIZE;
|
||||
|
||||
} else {
|
||||
/*Local file? try fstat instead*/
|
||||
s->size = (stat.st_size >> SECTOR_SHIFT);
|
||||
s->sector_size = DEFAULT_SECTOR_SIZE;
|
||||
DFPRINTF("Image size: [%llu]\n",
|
||||
(long long unsigned)s->size);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int send_responses(struct disk_driver *dd, int res, uint64_t sec,
|
||||
int nr_secs, int idx, void *private)
|
||||
{
|
||||
if (res < 0) DFPRINTF("AIO FAILURE: res [%d]!\n",res);
|
||||
|
||||
returned_events++;
|
||||
|
||||
free(private);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct disk_driver dd;
|
||||
struct td_state *s;
|
||||
int ret = -1, fd, len;
|
||||
fd_set readfds;
|
||||
struct timeval timeout;
|
||||
uint64_t i;
|
||||
char *buf;
|
||||
|
||||
if (argc != 3) {
|
||||
fprintf(stderr, "Qcow-utils: v1.0.0\n");
|
||||
fprintf(stderr, "usage: %s <QCOW FILENAME> <SRC IMAGE>\n",
|
||||
argv[0]);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
s = malloc(sizeof(struct td_state));
|
||||
|
||||
/*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(s, fd);
|
||||
|
||||
/*Create qcow file*/
|
||||
ret = qcow_create(argv[1],s->size<<SECTOR_SHIFT,NULL,0);
|
||||
|
||||
if (ret < 0) {
|
||||
DFPRINTF("Unable to create QCOW file\n");
|
||||
exit(-1);
|
||||
} else DFPRINTF("Qcow file created: size %llu sectors\n",
|
||||
(long long unsigned)s->size);
|
||||
|
||||
dd.td_state = s;
|
||||
dd.drv = &tapdisk_qcow;
|
||||
dd.private = malloc(dd.drv->private_data_size);
|
||||
|
||||
/*Open qcow file*/
|
||||
if (dd.drv->td_open(&dd, argv[1], 0)!=0) {
|
||||
DFPRINTF("Unable to open Qcow file [%s]\n",argv[1]);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
io_fd = dd.io_fd;
|
||||
|
||||
/*Initialise the output string*/
|
||||
memset(output,0x20,25);
|
||||
output[0] = '[';
|
||||
output[22] = ']';
|
||||
output[23] = '\0';
|
||||
DFPRINTF("%s",output);
|
||||
|
||||
i = 0;
|
||||
while (running) {
|
||||
timeout.tv_sec = 0;
|
||||
|
||||
if (!complete) {
|
||||
/*Read sector from image*/
|
||||
if (lseek(fd, i, SEEK_SET) == (off_t)-1) {
|
||||
DFPRINTF("Unable to access file offset %llu\n",
|
||||
(long long)i);
|
||||
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 %llu\n",
|
||||
(long long unsigned) (i >> 9));
|
||||
complete = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (len % 512) {
|
||||
len = (len >> 9) << 9;
|
||||
}
|
||||
|
||||
ret = dd.drv->td_queue_write(&dd, i >> 9,
|
||||
len >> 9, buf,
|
||||
send_responses, 0, buf);
|
||||
|
||||
if (!ret) submit_events++;
|
||||
|
||||
if (ret < 0) {
|
||||
DFPRINTF("UNABLE TO WRITE block [%llu]\n",
|
||||
(long long unsigned) (i >> 9));
|
||||
} else i += len;
|
||||
|
||||
if (i >> 9 == s->size) complete = 1;
|
||||
|
||||
debug_output(i,s->size << 9);
|
||||
|
||||
if ((submit_events % 10 == 0) || complete)
|
||||
dd.drv->td_submit(&dd);
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
} else {
|
||||
timeout.tv_usec = 1000;
|
||||
if (!submit_events) running = 0;
|
||||
}
|
||||
|
||||
|
||||
/*Check AIO FD*/
|
||||
LOCAL_FD_SET(&readfds);
|
||||
ret = select(maxfds + 1, &readfds, (fd_set *) 0,
|
||||
(fd_set *) 0, &timeout);
|
||||
|
||||
if (ret > 0) dd.drv->td_do_callbacks(&dd, 0);
|
||||
if (complete && (returned_events == submit_events))
|
||||
running = 0;
|
||||
}
|
||||
memcpy(output+prev+1,"=",1);
|
||||
DFPRINTF("\r%s 100%%\nTRANSFER COMPLETE\n\n", output);
|
||||
dd.drv->td_close(&dd);
|
||||
free(dd.private);
|
||||
free(s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,130 +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"
|
||||
|
||||
#if 1
|
||||
#define DFPRINTF(_f, _a...) fprintf ( stderr, _f , ## _a )
|
||||
#else
|
||||
#define DFPRINTF(_f, _a...) ((void)0)
|
||||
#endif
|
||||
|
||||
#define MAX_NAME_LEN 1000
|
||||
|
||||
static void help(void)
|
||||
{
|
||||
fprintf(stderr, "Qcow-utils: v1.0.0\n");
|
||||
fprintf(stderr,
|
||||
"usage: qcow-create [-h help] [-r reserve] [-f format] <SIZE(MB)> <FILENAME> "
|
||||
"[<BACKING_FILENAME>]\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int ret = -1, c, backed = 0;
|
||||
int sparse = 1;
|
||||
char *fmt = "qcow";
|
||||
uint64_t size;
|
||||
char filename[MAX_NAME_LEN], bfilename[MAX_NAME_LEN];
|
||||
char *tmpfile;
|
||||
|
||||
for(;;) {
|
||||
c = getopt(argc, argv, "hrf");
|
||||
if (c == -1)
|
||||
break;
|
||||
switch(c) {
|
||||
case 'h':
|
||||
help();
|
||||
exit(0);
|
||||
break;
|
||||
case 'f':
|
||||
fmt = argv[optind++];
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
tmpfile = backed ? bfilename: NULL;
|
||||
if (!strcmp(fmt, "qcow")) {
|
||||
ret = qcow_create(filename, size, tmpfile, sparse);
|
||||
} else if(!strcmp(fmt, "qcow2")) {
|
||||
ret = qcow2_create(filename, size, tmpfile, sparse);
|
||||
} else {
|
||||
fprintf(stderr,"Unsupport format:%s\n", fmt);
|
||||
exit(-1);
|
||||
}
|
||||
DFPRINTF("Creating file size %llu, name %s\n",(long long unsigned)size, filename);
|
||||
|
||||
if (ret < 0)
|
||||
DPRINTF("Unable to create QCOW file\n");
|
||||
else
|
||||
DPRINTF("QCOW file successfully created\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,348 +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 "tapdisk.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 TAPDISK 1
|
||||
#define BLOCK_PROCESSSZ 4096
|
||||
|
||||
static int maxfds, *qcowio_fd, *aio_fd, 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, write_idx = 0;
|
||||
struct disk_driver ddqcow, ddaio;
|
||||
static uint64_t prev = 0, written = 0;
|
||||
static char output[25];
|
||||
|
||||
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 5% */
|
||||
uint64_t blocks = size/20;
|
||||
|
||||
if (progress/blocks > prev) {
|
||||
memcpy(output+prev+1,"=>",2);
|
||||
prev++;
|
||||
DFPRINTF("\r%s %llu%%",
|
||||
output, (long long)((prev-1)*5));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static inline void LOCAL_FD_SET(fd_set *readfds)
|
||||
{
|
||||
FD_SET(qcowio_fd[0], readfds);
|
||||
FD_SET(aio_fd[0], readfds);
|
||||
|
||||
maxfds = (qcowio_fd[0] > aio_fd[0] ? qcowio_fd[0] : aio_fd[0]) + 1;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int send_write_responses(struct disk_driver *dd, int res, uint64_t sec,
|
||||
int nr_secs, int idx, void *private)
|
||||
{
|
||||
if (res < 0) {
|
||||
DFPRINTF("AIO FAILURE: res [%d]!\n",res);
|
||||
return 0;
|
||||
}
|
||||
written += BLOCK_PROCESSSZ;
|
||||
returned_write_events++;
|
||||
write_idx = idx;
|
||||
|
||||
debug_output(written, dd->td_state->size << 9);
|
||||
free(private);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int send_read_responses(struct disk_driver *dd, int res, uint64_t sec,
|
||||
int nr_secs, int idx, void *private)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (res < 0) DFPRINTF("AIO FAILURE: res [%d]!\n",res);
|
||||
|
||||
returned_read_events++;
|
||||
read_idx = idx;
|
||||
|
||||
ret = ddaio.drv->td_queue_write(&ddaio, idx, BLOCK_PROCESSSZ>>9, private,
|
||||
send_write_responses, idx, private);
|
||||
if (ret != 0) {
|
||||
DFPRINTF("ERROR in submitting queue write!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ( (returned_read_events == submit_events) ||
|
||||
(returned_read_events % 10 == 0) ) {
|
||||
ddaio.drv->td_submit(&ddaio);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int ret = -1, fd, len,input;
|
||||
uint64_t size;
|
||||
fd_set readfds;
|
||||
struct timeval timeout;
|
||||
uint64_t i;
|
||||
char *buf;
|
||||
struct stat finfo;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
ddqcow.td_state = malloc(sizeof(struct td_state));
|
||||
ddaio.td_state = malloc(sizeof(struct td_state));
|
||||
|
||||
/*Open qcow source file*/
|
||||
ddqcow.drv = &tapdisk_qcow;
|
||||
ddqcow.private = malloc(ddqcow.drv->private_data_size);
|
||||
|
||||
if (ddqcow.drv->td_open(&ddqcow, argv[2], TD_RDONLY)!=0) {
|
||||
DFPRINTF("Unable to open Qcow file [%s]\n",argv[2]);
|
||||
exit(-1);
|
||||
} else DFPRINTF("QCOW file opened, size %llu\n",
|
||||
(long long unsigned)ddqcow.td_state->size);
|
||||
|
||||
qcowio_fd = ddqcow.io_fd;
|
||||
|
||||
/*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.td_state->size<<9) < 0) {
|
||||
DFPRINTF("Unable to create file "
|
||||
"[%s] of size %llu (errno %d). "
|
||||
"Exiting...\n",
|
||||
argv[1],
|
||||
(long long unsigned)ddqcow.td_state->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.td_state->size<<9) {
|
||||
DFPRINTF("ERROR: Not enough space on device "
|
||||
"%s (%"PRIu64" bytes available, "
|
||||
"%llu bytes required\n",
|
||||
argv[1], size,
|
||||
(long long unsigned)ddqcow.td_state->size<<9);
|
||||
close(fd);
|
||||
exit(-1);
|
||||
}
|
||||
} else {
|
||||
if (ftruncate(fd, (off_t)ddqcow.td_state->size<<9) < 0) {
|
||||
DFPRINTF("Unable to create file "
|
||||
"[%s] of size %llu (errno %d). "
|
||||
"Exiting...\n",
|
||||
argv[1],
|
||||
(long long unsigned)ddqcow.td_state->size<<9,
|
||||
0 - errno);
|
||||
close(fd);
|
||||
exit(-1);
|
||||
} else DFPRINTF("File [%s] truncated to length %llu "
|
||||
"(%llu)\n",
|
||||
argv[1],
|
||||
(long long unsigned)ddqcow.td_state->size<<9,
|
||||
(long long unsigned)ddqcow.td_state->size);
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
/*Open aio destination file*/
|
||||
ddaio.drv = &tapdisk_aio;
|
||||
ddaio.private = malloc(ddaio.drv->private_data_size);
|
||||
|
||||
if (ddaio.drv->td_open(&ddaio, argv[1], 0)!=0) {
|
||||
DFPRINTF("Unable to open Qcow file [%s]\n", argv[1]);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
aio_fd = ddaio.io_fd;
|
||||
|
||||
/*Initialise the output string*/
|
||||
memset(output,0x20,25);
|
||||
output[0] = '[';
|
||||
output[22] = ']';
|
||||
output[23] = '\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++;
|
||||
ret = ddqcow.drv->td_queue_read(&ddqcow, i>>9,
|
||||
BLOCK_PROCESSSZ>>9, buf,
|
||||
send_read_responses, i>>9, buf);
|
||||
|
||||
if (ret < 0) {
|
||||
DFPRINTF("UNABLE TO READ block [%llu]\n",
|
||||
(long long unsigned)i);
|
||||
exit(-1);
|
||||
} else {
|
||||
i += BLOCK_PROCESSSZ;
|
||||
}
|
||||
|
||||
if (i >= ddqcow.td_state->size<<9) {
|
||||
complete = 1;
|
||||
}
|
||||
|
||||
if ((submit_events % 10 == 0) || complete)
|
||||
ddqcow.drv->td_submit(&ddqcow);
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
} else {
|
||||
timeout.tv_usec = 1000;
|
||||
if (!submit_events) running = 0;
|
||||
}
|
||||
|
||||
|
||||
/*Check AIO FD*/
|
||||
LOCAL_FD_SET(&readfds);
|
||||
ret = select(maxfds + 1, &readfds, (fd_set *) 0,
|
||||
(fd_set *) 0, &timeout);
|
||||
|
||||
if (ret > 0) {
|
||||
if (FD_ISSET(qcowio_fd[0], &readfds))
|
||||
ddqcow.drv->td_do_callbacks(&ddqcow, 0);
|
||||
if (FD_ISSET(aio_fd[0], &readfds))
|
||||
ddaio.drv->td_do_callbacks(&ddaio, 0);
|
||||
}
|
||||
if (complete && (returned_write_events == submit_events))
|
||||
running = 0;
|
||||
}
|
||||
memcpy(output+prev+1,"=",1);
|
||||
DFPRINTF("\r%s 100%%\nTRANSFER COMPLETE\n\n", output);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,357 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2006 Andrew Warfield and Julian Chesterfield
|
||||
* Copyright (c) 2007 Red Hat, Inc.
|
||||
*
|
||||
* 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 "tapaio.h"
|
||||
#include "tapdisk.h"
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/**
|
||||
* 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 1
|
||||
|
||||
/*
|
||||
* If we don't have any way to do epoll on aio events in a normal kernel,
|
||||
* wait for aio events in a separate thread and return completion status
|
||||
* that via a pipe that can be waited on normally.
|
||||
*
|
||||
* To keep locking problems between the completion thread and the submit
|
||||
* thread to a minimum, there's a handshake which allows only one thread
|
||||
* to be doing work on the completion queue at a time:
|
||||
*
|
||||
* 1) main thread sends completion thread a command via the command pipe;
|
||||
* 2) completion thread waits for aio events and returns the number
|
||||
* received on the completion pipe
|
||||
* 3) main thread processes the received ctx->aio_events events
|
||||
* 4) loop back to 1) to let the completion thread refill the aio_events
|
||||
* buffer.
|
||||
*
|
||||
* This workaround needs to disappear once the kernel provides a single
|
||||
* mechanism for waiting on both aio and normal fd wakeups.
|
||||
*/
|
||||
static void *
|
||||
tap_aio_completion_thread(void *arg)
|
||||
{
|
||||
tap_aio_internal_context_t *ctx = (tap_aio_internal_context_t *) arg;
|
||||
int command;
|
||||
int nr_events;
|
||||
int rc;
|
||||
|
||||
while (1) {
|
||||
rc = read(ctx->command_fd[0], &command, sizeof(command));
|
||||
|
||||
do {
|
||||
rc = io_getevents(ctx->aio_ctx, 1,
|
||||
ctx->max_aio_events, ctx->aio_events,
|
||||
NULL);
|
||||
if (rc) {
|
||||
nr_events = rc;
|
||||
rc = write(ctx->completion_fd[1], &nr_events,
|
||||
sizeof(nr_events));
|
||||
}
|
||||
} while (!rc);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
tap_aio_continue(tap_aio_internal_context_t *ctx)
|
||||
{
|
||||
int cmd = 0;
|
||||
|
||||
if (!ctx->poll_in_thread)
|
||||
return;
|
||||
|
||||
if (write(ctx->command_fd[1], &cmd, sizeof(cmd)) < 0)
|
||||
DPRINTF("Cannot write to command pipe\n");
|
||||
}
|
||||
|
||||
static int
|
||||
tap_aio_setup(tap_aio_internal_context_t *ctx,
|
||||
struct io_event *aio_events,
|
||||
int max_aio_events)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ctx->aio_events = aio_events;
|
||||
ctx->max_aio_events = max_aio_events;
|
||||
ctx->poll_in_thread = 0;
|
||||
|
||||
ctx->aio_ctx = (io_context_t) REQUEST_ASYNC_FD;
|
||||
ret = io_setup(ctx->max_aio_events, &ctx->aio_ctx);
|
||||
if (ret < 0 && ret != -EINVAL)
|
||||
return ret;
|
||||
else if (ret > 0) {
|
||||
ctx->pollfd = ret;
|
||||
return ctx->pollfd;
|
||||
}
|
||||
|
||||
ctx->aio_ctx = (io_context_t) 0;
|
||||
ret = io_setup(ctx->max_aio_events, &ctx->aio_ctx);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if ((ret = pipe(ctx->command_fd)) < 0) {
|
||||
DPRINTF("Unable to create command pipe\n");
|
||||
return -1;
|
||||
}
|
||||
if ((ret = pipe(ctx->completion_fd)) < 0) {
|
||||
DPRINTF("Unable to create completion pipe\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((ret = pthread_create(&ctx->aio_thread, NULL,
|
||||
tap_aio_completion_thread, ctx)) != 0) {
|
||||
DPRINTF("Unable to create completion thread\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctx->pollfd = ctx->completion_fd[0];
|
||||
ctx->poll_in_thread = 1;
|
||||
|
||||
tap_aio_continue(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
tap_aio_get_events(tap_aio_internal_context_t *ctx)
|
||||
{
|
||||
int nr_events = 0;
|
||||
|
||||
if (!ctx->poll_in_thread)
|
||||
nr_events = io_getevents(ctx->aio_ctx, 1,
|
||||
ctx->max_aio_events, ctx->aio_events, NULL);
|
||||
else {
|
||||
int r;
|
||||
r = read(ctx->completion_fd[0], &nr_events, sizeof(nr_events));
|
||||
if (r < 0) {
|
||||
if (errno == EAGAIN || errno == EINTR)
|
||||
return 0;
|
||||
/* This is pretty bad, we'll probably spin */
|
||||
DPRINTF("Aargh, read completion_fd failed: %s",
|
||||
strerror(errno));
|
||||
} else if (r != sizeof(nr_events)) {
|
||||
/* Should never happen because sizeof(nr_events)
|
||||
* fits in the guaranteed atomic pipe write size.
|
||||
* Blundering on is slightly nicer than asserting */
|
||||
DPRINTF("Aargh, read completion_fd short read %d", r);
|
||||
}
|
||||
}
|
||||
|
||||
return nr_events;
|
||||
}
|
||||
|
||||
int tap_aio_more_events(tap_aio_internal_context_t *ctx)
|
||||
{
|
||||
return io_getevents(ctx->aio_ctx, 0,
|
||||
ctx->max_aio_events, ctx->aio_events, NULL);
|
||||
}
|
||||
|
||||
int tap_aio_init(tap_aio_context_t *ctx, uint64_t sectors,
|
||||
int max_aio_reqs)
|
||||
{
|
||||
int i, ret;
|
||||
long ioidx;
|
||||
|
||||
ctx->iocb_list = NULL;
|
||||
ctx->pending_aio = NULL;
|
||||
ctx->aio_events = NULL;
|
||||
ctx->iocb_free = NULL;
|
||||
ctx->iocb_queue = NULL;
|
||||
|
||||
/*Initialize Locking bitmap*/
|
||||
ctx->sector_lock = calloc(1, sectors);
|
||||
|
||||
if (!ctx->sector_lock) {
|
||||
DPRINTF("Failed to allocate sector lock\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
/* Initialize AIO */
|
||||
ctx->max_aio_reqs = max_aio_reqs;
|
||||
ctx->iocb_free_count = ctx->max_aio_reqs;
|
||||
ctx->iocb_queued = 0;
|
||||
|
||||
if (!(ctx->iocb_list = malloc(sizeof(struct iocb) * ctx->max_aio_reqs)) ||
|
||||
!(ctx->pending_aio = malloc(sizeof(struct pending_aio) * ctx->max_aio_reqs)) ||
|
||||
!(ctx->aio_events = malloc(sizeof(struct io_event) * ctx->max_aio_reqs)) ||
|
||||
!(ctx->iocb_free = malloc(sizeof(struct iocb *) * ctx->max_aio_reqs)) ||
|
||||
!(ctx->iocb_queue = malloc(sizeof(struct iocb *) * ctx->max_aio_reqs)))
|
||||
{
|
||||
DPRINTF("Failed to allocate AIO structs (max_aio_reqs = %d)\n",
|
||||
ctx->max_aio_reqs);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = tap_aio_setup(&ctx->aio_ctx, ctx->aio_events, ctx->max_aio_reqs);
|
||||
if (ret < 0) {
|
||||
if (ret == -EAGAIN) {
|
||||
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 echo 1048576 > /proc/sys/fs/"
|
||||
"aio-max-nr')\n");
|
||||
} else {
|
||||
DPRINTF("Couldn't setup AIO context.\n");
|
||||
}
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (i=0;i<ctx->max_aio_reqs;i++)
|
||||
ctx->iocb_free[i] = &ctx->iocb_list[i];
|
||||
|
||||
DPRINTF("AIO state initialised\n");
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
void tap_aio_free(tap_aio_context_t *ctx)
|
||||
{
|
||||
if (ctx->sector_lock)
|
||||
free(ctx->sector_lock);
|
||||
if (ctx->iocb_list)
|
||||
free(ctx->iocb_list);
|
||||
if (ctx->pending_aio)
|
||||
free(ctx->pending_aio);
|
||||
if (ctx->aio_events)
|
||||
free(ctx->aio_events);
|
||||
if (ctx->iocb_free)
|
||||
free(ctx->iocb_free);
|
||||
if (ctx->iocb_queue)
|
||||
free(ctx->iocb_queue);
|
||||
}
|
||||
|
||||
/*TODO: Fix sector span!*/
|
||||
int tap_aio_can_lock(tap_aio_context_t *ctx, uint64_t sector)
|
||||
{
|
||||
return (ctx->sector_lock[sector] ? 0 : 1);
|
||||
}
|
||||
|
||||
int tap_aio_lock(tap_aio_context_t *ctx, uint64_t sector)
|
||||
{
|
||||
return ++ctx->sector_lock[sector];
|
||||
}
|
||||
|
||||
void tap_aio_unlock(tap_aio_context_t *ctx, uint64_t sector)
|
||||
{
|
||||
if (!ctx->sector_lock[sector]) return;
|
||||
|
||||
--ctx->sector_lock[sector];
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
int tap_aio_read(tap_aio_context_t *ctx, int fd, int size,
|
||||
uint64_t offset, char *buf, td_callback_t cb,
|
||||
int id, uint64_t sector, void *private)
|
||||
{
|
||||
struct iocb *io;
|
||||
struct pending_aio *pio;
|
||||
long ioidx;
|
||||
|
||||
if (ctx->iocb_free_count == 0)
|
||||
return -ENOMEM;
|
||||
|
||||
io = ctx->iocb_free[--ctx->iocb_free_count];
|
||||
|
||||
ioidx = IOCB_IDX(ctx, io);
|
||||
pio = &ctx->pending_aio[ioidx];
|
||||
pio->cb = cb;
|
||||
pio->id = id;
|
||||
pio->private = private;
|
||||
pio->nb_sectors = size/512;
|
||||
pio->buf = buf;
|
||||
pio->sector = sector;
|
||||
|
||||
io_prep_pread(io, fd, buf, size, offset);
|
||||
io->data = (void *)ioidx;
|
||||
|
||||
ctx->iocb_queue[ctx->iocb_queued++] = io;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tap_aio_write(tap_aio_context_t *ctx, int fd, int size,
|
||||
uint64_t offset, char *buf, td_callback_t cb,
|
||||
int id, uint64_t sector, void *private)
|
||||
{
|
||||
struct iocb *io;
|
||||
struct pending_aio *pio;
|
||||
long ioidx;
|
||||
|
||||
if (ctx->iocb_free_count == 0)
|
||||
return -ENOMEM;
|
||||
|
||||
io = ctx->iocb_free[--ctx->iocb_free_count];
|
||||
|
||||
ioidx = IOCB_IDX(ctx, io);
|
||||
pio = &ctx->pending_aio[ioidx];
|
||||
pio->cb = cb;
|
||||
pio->id = id;
|
||||
pio->private = private;
|
||||
pio->nb_sectors = size/512;
|
||||
pio->buf = buf;
|
||||
pio->sector = sector;
|
||||
|
||||
io_prep_pwrite(io, fd, buf, size, offset);
|
||||
io->data = (void *)ioidx;
|
||||
|
||||
ctx->iocb_queue[ctx->iocb_queued++] = io;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tap_aio_submit(tap_aio_context_t *ctx)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!ctx->iocb_queued)
|
||||
return 0;
|
||||
|
||||
ret = io_submit(ctx->aio_ctx.aio_ctx, ctx->iocb_queued, ctx->iocb_queue);
|
||||
|
||||
/* XXX: TODO: Handle error conditions here. */
|
||||
|
||||
/* Success case: */
|
||||
ctx->iocb_queued = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2006 Andrew Warfield and Julian Chesterfield
|
||||
* Copyright (c) 2007 Red Hat, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __TAPAIO_H__
|
||||
#define __TAPAIO_H__
|
||||
|
||||
#include <pthread.h>
|
||||
#include <libaio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "tapdisk.h"
|
||||
|
||||
#define IOCB_IDX(_ctx, _io) ((_io) - (_ctx)->iocb_list)
|
||||
|
||||
struct tap_aio_internal_context {
|
||||
io_context_t aio_ctx;
|
||||
|
||||
struct io_event *aio_events;
|
||||
int max_aio_events;
|
||||
|
||||
pthread_t aio_thread;
|
||||
int command_fd[2];
|
||||
int completion_fd[2];
|
||||
int pollfd;
|
||||
unsigned int poll_in_thread : 1;
|
||||
};
|
||||
|
||||
|
||||
typedef struct tap_aio_internal_context tap_aio_internal_context_t;
|
||||
|
||||
|
||||
struct pending_aio {
|
||||
td_callback_t cb;
|
||||
int id;
|
||||
void *private;
|
||||
int nb_sectors;
|
||||
char *buf;
|
||||
uint64_t sector;
|
||||
};
|
||||
|
||||
|
||||
struct tap_aio_context {
|
||||
tap_aio_internal_context_t aio_ctx;
|
||||
|
||||
int max_aio_reqs;
|
||||
struct iocb *iocb_list;
|
||||
struct iocb **iocb_free;
|
||||
struct pending_aio *pending_aio;
|
||||
int iocb_free_count;
|
||||
struct iocb **iocb_queue;
|
||||
int iocb_queued;
|
||||
struct io_event *aio_events;
|
||||
|
||||
/* Locking bitmap for AIO reads/writes */
|
||||
uint8_t *sector_lock;
|
||||
};
|
||||
|
||||
typedef struct tap_aio_context tap_aio_context_t;
|
||||
|
||||
void tap_aio_continue (tap_aio_internal_context_t *ctx);
|
||||
int tap_aio_get_events (tap_aio_internal_context_t *ctx);
|
||||
int tap_aio_more_events(tap_aio_internal_context_t *ctx);
|
||||
|
||||
|
||||
int tap_aio_init(tap_aio_context_t *ctx, uint64_t sectors,
|
||||
int max_aio_reqs);
|
||||
void tap_aio_free(tap_aio_context_t *ctx);
|
||||
|
||||
int tap_aio_can_lock(tap_aio_context_t *ctx, uint64_t sector);
|
||||
int tap_aio_lock(tap_aio_context_t *ctx, uint64_t sector);
|
||||
void tap_aio_unlock(tap_aio_context_t *ctx, uint64_t sector);
|
||||
|
||||
|
||||
int tap_aio_read(tap_aio_context_t *ctx, int fd, int size,
|
||||
uint64_t offset, char *buf, td_callback_t cb,
|
||||
int id, uint64_t sector, void *private);
|
||||
int tap_aio_write(tap_aio_context_t *ctx, int fd, int size,
|
||||
uint64_t offset, char *buf, td_callback_t cb,
|
||||
int id, uint64_t sector, void *private);
|
||||
int tap_aio_submit(tap_aio_context_t *ctx);
|
||||
|
||||
#endif /* __TAPAIO_H__ */
|
||||
@@ -1,872 +0,0 @@
|
||||
/* tapdisk.c
|
||||
*
|
||||
* separate disk process, spawned by blktapctrl. Inherits code from driver
|
||||
* plugins
|
||||
*
|
||||
* Copyright (c) 2005 Julian Chesterfield and Andrew Warfield.
|
||||
*
|
||||
*/
|
||||
|
||||
#define MSG_SIZE 4096
|
||||
#define TAPDISK
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/poll.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <time.h>
|
||||
#include <err.h>
|
||||
#include <poll.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include "blktaplib.h"
|
||||
#include "tapdisk.h"
|
||||
|
||||
#if 1
|
||||
#define ASSERT(_p) \
|
||||
if ( !(_p) ) { DPRINTF("Assertion '%s' failed, line %d, file %s", #_p , \
|
||||
__LINE__, __FILE__); *(int*)0=0; }
|
||||
#else
|
||||
#define ASSERT(_p) ((void)0)
|
||||
#endif
|
||||
|
||||
#define INPUT 0
|
||||
#define OUTPUT 1
|
||||
|
||||
static int maxfds, fds[2], run = 1;
|
||||
|
||||
static pid_t process;
|
||||
int connected_disks = 0;
|
||||
fd_list_entry_t *fd_start = NULL;
|
||||
|
||||
int do_cow_read(struct disk_driver *dd, blkif_request_t *req,
|
||||
int sidx, uint64_t sector, int nr_secs);
|
||||
|
||||
#define td_for_each_disk(tds, drv) \
|
||||
for (drv = tds->disks; drv != NULL; drv = drv->next)
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
fprintf(stderr, "blktap-utils: v1.0.0\n");
|
||||
fprintf(stderr, "usage: tapdisk <READ fifo> <WRITE fifo>\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
static void daemonize(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (getppid()==1) return; /* already a daemon */
|
||||
if (fork() != 0) exit(0);
|
||||
|
||||
#if 0
|
||||
/*Set new program session ID and close all descriptors*/
|
||||
setsid();
|
||||
for (i = getdtablesize(); i >= 0; --i) close(i);
|
||||
|
||||
/*Send all I/O to /dev/null */
|
||||
i = open("/dev/null",O_RDWR);
|
||||
dup(i);
|
||||
dup(i);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
static void free_driver(struct disk_driver *d)
|
||||
{
|
||||
if (d->name)
|
||||
free(d->name);
|
||||
if (d->private)
|
||||
free(d->private);
|
||||
free(d);
|
||||
}
|
||||
|
||||
static void unmap_disk(struct td_state *s)
|
||||
{
|
||||
tapdev_info_t *info = s->ring_info;
|
||||
struct disk_driver *dd, *tmp;
|
||||
fd_list_entry_t *entry;
|
||||
|
||||
dd = s->disks;
|
||||
while (dd) {
|
||||
tmp = dd->next;
|
||||
dd->drv->td_close(dd);
|
||||
free_driver(dd);
|
||||
dd = tmp;
|
||||
}
|
||||
|
||||
if (info != NULL && info->mem > 0)
|
||||
munmap(info->mem, getpagesize() * BLKTAP_MMAP_REGION_SIZE);
|
||||
|
||||
entry = s->fd_entry;
|
||||
*entry->pprev = entry->next;
|
||||
if (entry->next)
|
||||
entry->next->pprev = entry->pprev;
|
||||
|
||||
close(info->fd);
|
||||
|
||||
free(s->fd_entry);
|
||||
free(s->blkif);
|
||||
free(s->ring_info);
|
||||
free(s);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void sig_handler(int sig)
|
||||
{
|
||||
/*Received signal to close. If no disks are active, we close app.*/
|
||||
|
||||
if (connected_disks < 1) run = 0;
|
||||
}
|
||||
|
||||
static inline int LOCAL_FD_SET(fd_set *readfds)
|
||||
{
|
||||
fd_list_entry_t *ptr;
|
||||
struct disk_driver *dd;
|
||||
|
||||
ptr = fd_start;
|
||||
while (ptr != NULL) {
|
||||
if (ptr->tap_fd) {
|
||||
FD_SET(ptr->tap_fd, readfds);
|
||||
td_for_each_disk(ptr->s, dd) {
|
||||
if (dd->io_fd[READ])
|
||||
FD_SET(dd->io_fd[READ], readfds);
|
||||
maxfds = (dd->io_fd[READ] > maxfds ?
|
||||
dd->io_fd[READ] : maxfds);
|
||||
}
|
||||
maxfds = (ptr->tap_fd > maxfds ? ptr->tap_fd : maxfds);
|
||||
}
|
||||
ptr = ptr->next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline fd_list_entry_t *add_fd_entry(int tap_fd, struct td_state *s)
|
||||
{
|
||||
fd_list_entry_t **pprev, *entry;
|
||||
int i;
|
||||
|
||||
DPRINTF("Adding fd_list_entry\n");
|
||||
|
||||
/*Add to linked list*/
|
||||
s->fd_entry = entry = malloc(sizeof(fd_list_entry_t));
|
||||
entry->tap_fd = tap_fd;
|
||||
entry->s = s;
|
||||
entry->next = NULL;
|
||||
|
||||
pprev = &fd_start;
|
||||
while (*pprev != NULL)
|
||||
pprev = &(*pprev)->next;
|
||||
|
||||
*pprev = entry;
|
||||
entry->pprev = pprev;
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
static inline struct td_state *get_state(int cookie)
|
||||
{
|
||||
fd_list_entry_t *ptr;
|
||||
|
||||
ptr = fd_start;
|
||||
while (ptr != NULL) {
|
||||
if (ptr->cookie == cookie) return ptr->s;
|
||||
ptr = ptr->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct tap_disk *get_driver(int drivertype)
|
||||
{
|
||||
/* blktapctrl has passed us the driver type */
|
||||
|
||||
return dtypes[drivertype]->drv;
|
||||
}
|
||||
|
||||
static struct td_state *state_init(void)
|
||||
{
|
||||
int i;
|
||||
struct td_state *s;
|
||||
blkif_t *blkif;
|
||||
|
||||
s = malloc(sizeof(struct td_state));
|
||||
blkif = s->blkif = malloc(sizeof(blkif_t));
|
||||
s->ring_info = calloc(1, sizeof(tapdev_info_t));
|
||||
|
||||
for (i = 0; i < MAX_REQUESTS; i++) {
|
||||
blkif->pending_list[i].secs_pending = 0;
|
||||
blkif->pending_list[i].submitting = 0;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static int map_new_dev(struct td_state *s, int minor)
|
||||
{
|
||||
int tap_fd;
|
||||
tapdev_info_t *info = s->ring_info;
|
||||
char *devname;
|
||||
fd_list_entry_t *ptr;
|
||||
int page_size;
|
||||
|
||||
if (asprintf(&devname,"%s/%s%d", BLKTAP_DEV_DIR, BLKTAP_DEV_NAME, minor) == -1)
|
||||
return -1;
|
||||
tap_fd = open(devname, O_RDWR);
|
||||
if (tap_fd == -1)
|
||||
{
|
||||
DPRINTF("open failed on dev %s!",devname);
|
||||
goto fail;
|
||||
}
|
||||
info->fd = tap_fd;
|
||||
|
||||
/*Map the shared memory*/
|
||||
page_size = getpagesize();
|
||||
info->mem = mmap(0, page_size * BLKTAP_MMAP_REGION_SIZE,
|
||||
PROT_READ | PROT_WRITE, MAP_SHARED, info->fd, 0);
|
||||
if ((long int)info->mem == -1)
|
||||
{
|
||||
DPRINTF("mmap failed on dev %s!\n",devname);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* assign the rings to the mapped memory */
|
||||
info->sring = (blkif_sring_t *)((unsigned long)info->mem);
|
||||
BACK_RING_INIT(&info->fe_ring, info->sring, page_size);
|
||||
|
||||
info->vstart =
|
||||
(unsigned long)info->mem + (BLKTAP_RING_PAGES * page_size);
|
||||
|
||||
ioctl(info->fd, BLKTAP_IOCTL_SENDPID, process );
|
||||
ioctl(info->fd, BLKTAP_IOCTL_SETMODE, BLKTAP_MODE_INTERPOSE );
|
||||
free(devname);
|
||||
|
||||
/*Update the fd entry*/
|
||||
ptr = fd_start;
|
||||
while (ptr != NULL) {
|
||||
if (s == ptr->s) {
|
||||
ptr->tap_fd = tap_fd;
|
||||
break;
|
||||
}
|
||||
ptr = ptr->next;
|
||||
}
|
||||
|
||||
return minor;
|
||||
|
||||
fail:
|
||||
free(devname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static struct disk_driver *disk_init(struct td_state *s,
|
||||
struct tap_disk *drv,
|
||||
char *name, td_flag_t flags)
|
||||
{
|
||||
struct disk_driver *dd;
|
||||
|
||||
dd = calloc(1, sizeof(struct disk_driver));
|
||||
if (!dd)
|
||||
return NULL;
|
||||
|
||||
dd->private = malloc(drv->private_data_size);
|
||||
if (!dd->private) {
|
||||
free(dd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dd->drv = drv;
|
||||
dd->td_state = s;
|
||||
dd->name = name;
|
||||
dd->flags = flags;
|
||||
|
||||
return dd;
|
||||
}
|
||||
|
||||
static int open_disk(struct td_state *s,
|
||||
struct tap_disk *drv, char *path, td_flag_t flags)
|
||||
{
|
||||
int err;
|
||||
char *dup;
|
||||
td_flag_t pflags;
|
||||
struct disk_id id;
|
||||
struct disk_driver *d;
|
||||
|
||||
dup = strdup(path);
|
||||
if (!dup)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(&id, 0, sizeof(struct disk_id));
|
||||
s->disks = d = disk_init(s, drv, dup, flags);
|
||||
if (!d)
|
||||
return -ENOMEM;
|
||||
|
||||
err = drv->td_open(d, path, flags);
|
||||
if (err) {
|
||||
free_driver(d);
|
||||
s->disks = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
pflags = flags | TD_RDONLY;
|
||||
|
||||
/* load backing files as necessary */
|
||||
while ((err = d->drv->td_get_parent_id(d, &id)) == 0) {
|
||||
struct disk_driver *new;
|
||||
|
||||
if (id.drivertype > MAX_DISK_TYPES ||
|
||||
!get_driver(id.drivertype) || !id.name)
|
||||
goto fail;
|
||||
|
||||
dup = strdup(id.name);
|
||||
if (!dup)
|
||||
goto fail;
|
||||
|
||||
new = disk_init(s, get_driver(id.drivertype), dup, pflags);
|
||||
if (!new)
|
||||
goto fail;
|
||||
|
||||
err = new->drv->td_open(new, new->name, pflags);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
err = d->drv->td_validate_parent(d, new, 0);
|
||||
if (err) {
|
||||
d->next = new;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
d = d->next = new;
|
||||
free(id.name);
|
||||
}
|
||||
|
||||
s->info |= ((flags & TD_RDONLY) ? VDISK_READONLY : 0);
|
||||
|
||||
if (err >= 0)
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
DPRINTF("failed opening disk\n");
|
||||
if (id.name)
|
||||
free(id.name);
|
||||
d = s->disks;
|
||||
while (d) {
|
||||
struct disk_driver *tmp = d->next;
|
||||
d->drv->td_close(d);
|
||||
free_driver(d);
|
||||
d = tmp;
|
||||
}
|
||||
s->disks = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int read_msg(char *buf)
|
||||
{
|
||||
int length, len, msglen, tap_fd, *io_fd;
|
||||
char *ptr, *path;
|
||||
image_t *img;
|
||||
msg_hdr_t *msg;
|
||||
msg_newdev_t *msg_dev;
|
||||
msg_pid_t *msg_pid;
|
||||
struct tap_disk *drv;
|
||||
int ret = -1;
|
||||
struct td_state *s = NULL;
|
||||
fd_list_entry_t *entry;
|
||||
|
||||
length = read(fds[READ], buf, MSG_SIZE);
|
||||
|
||||
if (length > 0 && length >= sizeof(msg_hdr_t))
|
||||
{
|
||||
msg = (msg_hdr_t *)buf;
|
||||
DPRINTF("Tapdisk: Received msg, len %d, type %d, UID %d\n",
|
||||
length,msg->type,msg->cookie);
|
||||
|
||||
switch (msg->type) {
|
||||
case CTLMSG_PARAMS:
|
||||
ptr = buf + sizeof(msg_hdr_t);
|
||||
len = (length - sizeof(msg_hdr_t));
|
||||
path = calloc(1, len);
|
||||
|
||||
memcpy(path, ptr, len);
|
||||
DPRINTF("Received CTLMSG_PARAMS: [%s]\n", path);
|
||||
|
||||
/*Assign driver*/
|
||||
drv = get_driver(msg->drivertype);
|
||||
if (drv == NULL)
|
||||
goto params_done;
|
||||
|
||||
DPRINTF("Loaded driver: name [%s], type [%d]\n",
|
||||
drv->disk_type, msg->drivertype);
|
||||
|
||||
/* Allocate the disk structs */
|
||||
s = state_init();
|
||||
if (s == NULL)
|
||||
goto params_done;
|
||||
|
||||
/*Open file*/
|
||||
ret = open_disk(s, drv, path,
|
||||
((msg->readonly) ? TD_RDONLY : 0));
|
||||
if (ret)
|
||||
goto params_done;
|
||||
|
||||
entry = add_fd_entry(0, s);
|
||||
entry->cookie = msg->cookie;
|
||||
DPRINTF("Entered cookie %d\n", entry->cookie);
|
||||
|
||||
memset(buf, 0x00, MSG_SIZE);
|
||||
|
||||
params_done:
|
||||
if (ret == 0) {
|
||||
msglen = sizeof(msg_hdr_t) + sizeof(image_t);
|
||||
msg->type = CTLMSG_IMG;
|
||||
img = (image_t *)(buf + sizeof(msg_hdr_t));
|
||||
img->size = s->size;
|
||||
img->secsize = s->sector_size;
|
||||
img->info = s->info;
|
||||
} else {
|
||||
msglen = sizeof(msg_hdr_t);
|
||||
msg->type = CTLMSG_IMG_FAIL;
|
||||
msg->len = msglen;
|
||||
}
|
||||
len = write(fds[WRITE], buf, msglen);
|
||||
free(path);
|
||||
return 1;
|
||||
|
||||
case CTLMSG_NEWDEV:
|
||||
msg_dev = (msg_newdev_t *)(buf + sizeof(msg_hdr_t));
|
||||
|
||||
s = get_state(msg->cookie);
|
||||
DPRINTF("Retrieving state, cookie %d.....[%s]\n",
|
||||
msg->cookie, (s == NULL ? "FAIL":"OK"));
|
||||
if (s != NULL) {
|
||||
ret = ((map_new_dev(s, msg_dev->devnum)
|
||||
== msg_dev->devnum ? 0: -1));
|
||||
connected_disks++;
|
||||
}
|
||||
|
||||
memset(buf, 0x00, MSG_SIZE);
|
||||
msglen = sizeof(msg_hdr_t);
|
||||
msg->type = (ret == 0 ? CTLMSG_NEWDEV_RSP
|
||||
: CTLMSG_NEWDEV_FAIL);
|
||||
msg->len = msglen;
|
||||
|
||||
len = write(fds[WRITE], buf, msglen);
|
||||
return 1;
|
||||
|
||||
case CTLMSG_CLOSE:
|
||||
s = get_state(msg->cookie);
|
||||
if (s) unmap_disk(s);
|
||||
|
||||
connected_disks--;
|
||||
sig_handler(SIGINT);
|
||||
|
||||
return 1;
|
||||
|
||||
case CTLMSG_PID:
|
||||
memset(buf, 0x00, MSG_SIZE);
|
||||
msglen = sizeof(msg_hdr_t) + sizeof(msg_pid_t);
|
||||
msg->type = CTLMSG_PID_RSP;
|
||||
msg->len = msglen;
|
||||
|
||||
msg_pid = (msg_pid_t *)(buf + sizeof(msg_hdr_t));
|
||||
process = getpid();
|
||||
msg_pid->pid = process;
|
||||
|
||||
len = write(fds[WRITE], buf, msglen);
|
||||
return 1;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int write_rsp_to_ring(struct td_state *s, blkif_response_t *rsp)
|
||||
{
|
||||
tapdev_info_t *info = s->ring_info;
|
||||
blkif_response_t *rsp_d;
|
||||
|
||||
rsp_d = RING_GET_RESPONSE(&info->fe_ring, info->fe_ring.rsp_prod_pvt);
|
||||
memcpy(rsp_d, rsp, sizeof(blkif_response_t));
|
||||
info->fe_ring.rsp_prod_pvt++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void kick_responses(struct td_state *s)
|
||||
{
|
||||
tapdev_info_t *info = s->ring_info;
|
||||
|
||||
if (info->fe_ring.rsp_prod_pvt != info->fe_ring.sring->rsp_prod)
|
||||
{
|
||||
RING_PUSH_RESPONSES(&info->fe_ring);
|
||||
ioctl(info->fd, BLKTAP_IOCTL_KICK_FE);
|
||||
}
|
||||
}
|
||||
|
||||
static void io_done(struct disk_driver *dd, int sid)
|
||||
{
|
||||
struct tap_disk *drv = dd->drv;
|
||||
|
||||
if (!run) return; /*We have received signal to close*/
|
||||
|
||||
if (sid > MAX_IOFD || drv->td_do_callbacks(dd, sid) > 0)
|
||||
kick_responses(dd->td_state);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
segment_start(blkif_request_t *req, int sidx)
|
||||
{
|
||||
int i;
|
||||
uint64_t start = req->sector_number;
|
||||
|
||||
for (i = 0; i < sidx; i++)
|
||||
start += (req->seg[i].last_sect - req->seg[i].first_sect + 1);
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
uint64_t sends, responds;
|
||||
static int send_responses(struct disk_driver *dd, int res,
|
||||
uint64_t sector, int nr_secs, int idx, void *private)
|
||||
{
|
||||
pending_req_t *preq;
|
||||
blkif_request_t *req;
|
||||
int responses_queued = 0;
|
||||
struct td_state *s = dd->td_state;
|
||||
blkif_t *blkif = s->blkif;
|
||||
int sidx = (int)(long)private, secs_done = nr_secs;
|
||||
|
||||
if ( (idx > MAX_REQUESTS-1) )
|
||||
{
|
||||
DPRINTF("invalid index returned(%u)!\n", idx);
|
||||
return 0;
|
||||
}
|
||||
preq = &blkif->pending_list[idx];
|
||||
req = &preq->req;
|
||||
|
||||
if (res == BLK_NOT_ALLOCATED) {
|
||||
res = do_cow_read(dd, req, sidx, sector, nr_secs);
|
||||
if (res >= 0) {
|
||||
secs_done = res;
|
||||
res = 0;
|
||||
} else
|
||||
secs_done = 0;
|
||||
}
|
||||
|
||||
preq->secs_pending -= secs_done;
|
||||
|
||||
if (res == -EBUSY && preq->submitting)
|
||||
return -EBUSY; /* propagate -EBUSY back to higher layers */
|
||||
if (res)
|
||||
preq->status = BLKIF_RSP_ERROR;
|
||||
|
||||
if (!preq->submitting && preq->secs_pending == 0)
|
||||
{
|
||||
blkif_request_t tmp;
|
||||
blkif_response_t *rsp;
|
||||
|
||||
tmp = preq->req;
|
||||
rsp = (blkif_response_t *)req;
|
||||
|
||||
rsp->id = tmp.id;
|
||||
rsp->operation = tmp.operation;
|
||||
rsp->status = preq->status;
|
||||
|
||||
write_rsp_to_ring(s, rsp);
|
||||
responses_queued++;
|
||||
}
|
||||
return responses_queued;
|
||||
}
|
||||
|
||||
int do_cow_read(struct disk_driver *dd, blkif_request_t *req,
|
||||
int sidx, uint64_t sector, int nr_secs)
|
||||
{
|
||||
char *page;
|
||||
int ret, early;
|
||||
uint64_t seg_start, seg_end;
|
||||
struct td_state *s = dd->td_state;
|
||||
tapdev_info_t *info = s->ring_info;
|
||||
struct disk_driver *parent = dd->next;
|
||||
|
||||
seg_start = segment_start(req, sidx);
|
||||
seg_end = seg_start + req->seg[sidx].last_sect + 1;
|
||||
|
||||
ASSERT(sector >= seg_start && sector + nr_secs <= seg_end);
|
||||
|
||||
page = (char *)MMAP_VADDR(info->vstart,
|
||||
(unsigned long)req->id, sidx);
|
||||
page += (req->seg[sidx].first_sect << SECTOR_SHIFT);
|
||||
page += ((sector - seg_start) << SECTOR_SHIFT);
|
||||
|
||||
if (!parent) {
|
||||
memset(page, 0, nr_secs << SECTOR_SHIFT);
|
||||
return nr_secs;
|
||||
}
|
||||
|
||||
/* reissue request to backing file */
|
||||
ret = parent->drv->td_queue_read(parent, sector, nr_secs,
|
||||
page, send_responses,
|
||||
req->id, (void *)(long)sidx);
|
||||
if (ret > 0)
|
||||
parent->early += ret;
|
||||
|
||||
return ((ret >= 0) ? 0 : ret);
|
||||
}
|
||||
|
||||
static void get_io_request(struct td_state *s)
|
||||
{
|
||||
RING_IDX rp, rc, j, i;
|
||||
blkif_request_t *req;
|
||||
int idx, nsects, ret;
|
||||
uint64_t sector_nr;
|
||||
char *page;
|
||||
int early = 0; /* count early completions */
|
||||
struct disk_driver *dd = s->disks;
|
||||
struct tap_disk *drv = dd->drv;
|
||||
blkif_t *blkif = s->blkif;
|
||||
tapdev_info_t *info = s->ring_info;
|
||||
int page_size = getpagesize();
|
||||
|
||||
if (!run) return; /*We have received signal to close*/
|
||||
|
||||
rp = info->fe_ring.sring->req_prod;
|
||||
xen_rmb();
|
||||
for (j = info->fe_ring.req_cons; j != rp; j++)
|
||||
{
|
||||
int done = 0, start_seg = 0;
|
||||
|
||||
req = NULL;
|
||||
req = RING_GET_REQUEST(&info->fe_ring, j);
|
||||
++info->fe_ring.req_cons;
|
||||
|
||||
if (req == NULL) continue;
|
||||
|
||||
idx = req->id;
|
||||
|
||||
if (info->busy.req) {
|
||||
/* continue where we left off last time */
|
||||
ASSERT(info->busy.req == req);
|
||||
start_seg = info->busy.seg_idx;
|
||||
sector_nr = segment_start(req, start_seg);
|
||||
info->busy.seg_idx = 0;
|
||||
info->busy.req = NULL;
|
||||
} else {
|
||||
ASSERT(blkif->pending_list[idx].secs_pending == 0);
|
||||
memcpy(&blkif->pending_list[idx].req,
|
||||
req, sizeof(*req));
|
||||
blkif->pending_list[idx].status = BLKIF_RSP_OKAY;
|
||||
blkif->pending_list[idx].submitting = 1;
|
||||
sector_nr = req->sector_number;
|
||||
}
|
||||
|
||||
if ((dd->flags & TD_RDONLY) &&
|
||||
(req->operation == BLKIF_OP_WRITE)) {
|
||||
blkif->pending_list[idx].status = BLKIF_RSP_ERROR;
|
||||
goto send_response;
|
||||
}
|
||||
|
||||
for (i = start_seg; i < req->nr_segments; i++) {
|
||||
nsects = req->seg[i].last_sect -
|
||||
req->seg[i].first_sect + 1;
|
||||
|
||||
if ((req->seg[i].last_sect >= page_size >> 9) ||
|
||||
(nsects <= 0))
|
||||
continue;
|
||||
|
||||
page = (char *)MMAP_VADDR(info->vstart,
|
||||
(unsigned long)req->id, i);
|
||||
page += (req->seg[i].first_sect << SECTOR_SHIFT);
|
||||
|
||||
if (sector_nr >= s->size) {
|
||||
DPRINTF("Sector request failed:\n");
|
||||
DPRINTF("%s request, idx [%d,%d] size [%llu], "
|
||||
"sector [%llu,%llu]\n",
|
||||
(req->operation == BLKIF_OP_WRITE ?
|
||||
"WRITE" : "READ"),
|
||||
idx,i,
|
||||
(long long unsigned)
|
||||
nsects<<SECTOR_SHIFT,
|
||||
(long long unsigned)
|
||||
sector_nr<<SECTOR_SHIFT,
|
||||
(long long unsigned) sector_nr);
|
||||
continue;
|
||||
}
|
||||
|
||||
blkif->pending_list[idx].secs_pending += nsects;
|
||||
|
||||
switch (req->operation)
|
||||
{
|
||||
case BLKIF_OP_WRITE:
|
||||
ret = drv->td_queue_write(dd, sector_nr,
|
||||
nsects, page,
|
||||
send_responses,
|
||||
idx, (void *)(long)i);
|
||||
if (ret > 0) dd->early += ret;
|
||||
else if (ret == -EBUSY) {
|
||||
/* put req back on queue */
|
||||
--info->fe_ring.req_cons;
|
||||
info->busy.req = req;
|
||||
info->busy.seg_idx = i;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case BLKIF_OP_READ:
|
||||
ret = drv->td_queue_read(dd, sector_nr,
|
||||
nsects, page,
|
||||
send_responses,
|
||||
idx, (void *)(long)i);
|
||||
if (ret > 0) dd->early += ret;
|
||||
else if (ret == -EBUSY) {
|
||||
/* put req back on queue */
|
||||
--info->fe_ring.req_cons;
|
||||
info->busy.req = req;
|
||||
info->busy.seg_idx = i;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
DPRINTF("Unknown block operation\n");
|
||||
break;
|
||||
}
|
||||
sector_nr += nsects;
|
||||
}
|
||||
send_response:
|
||||
blkif->pending_list[idx].submitting = 0;
|
||||
/* force write_rsp_to_ring for synchronous case */
|
||||
if (blkif->pending_list[idx].secs_pending == 0)
|
||||
dd->early += send_responses(dd, 0, 0, 0, idx,
|
||||
(void *)(long)0);
|
||||
}
|
||||
|
||||
out:
|
||||
/*Batch done*/
|
||||
td_for_each_disk(s, dd) {
|
||||
dd->early += dd->drv->td_submit(dd);
|
||||
if (dd->early > 0) {
|
||||
io_done(dd, MAX_IOFD + 1);
|
||||
dd->early = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int len, msglen, ret;
|
||||
char *p, *buf;
|
||||
fd_set readfds, writefds;
|
||||
fd_list_entry_t *ptr;
|
||||
struct td_state *s;
|
||||
char openlogbuf[128];
|
||||
|
||||
if (argc != 3) usage();
|
||||
|
||||
daemonize();
|
||||
|
||||
snprintf(openlogbuf, sizeof(openlogbuf), "TAPDISK[%d]", getpid());
|
||||
openlog(openlogbuf, LOG_CONS|LOG_ODELAY, LOG_DAEMON);
|
||||
/*Setup signal handlers*/
|
||||
signal (SIGBUS, sig_handler);
|
||||
signal (SIGINT, sig_handler);
|
||||
|
||||
/*Open the control channel*/
|
||||
fds[READ] = open(argv[1],O_RDWR|O_NONBLOCK);
|
||||
fds[WRITE] = open(argv[2],O_RDWR|O_NONBLOCK);
|
||||
|
||||
if ( (fds[READ] < 0) || (fds[WRITE] < 0) )
|
||||
{
|
||||
DPRINTF("FD open failed [%d,%d]\n", fds[READ], fds[WRITE]);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
buf = calloc(MSG_SIZE, 1);
|
||||
|
||||
if (buf == NULL)
|
||||
{
|
||||
DPRINTF("ERROR: allocating memory.\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
while (run)
|
||||
{
|
||||
ret = 0;
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(fds[READ], &readfds);
|
||||
maxfds = fds[READ];
|
||||
|
||||
/*Set all tap fds*/
|
||||
LOCAL_FD_SET(&readfds);
|
||||
|
||||
/*Wait for incoming messages*/
|
||||
ret = select(maxfds + 1, &readfds, (fd_set *) 0,
|
||||
(fd_set *) 0, NULL);
|
||||
|
||||
if (ret > 0)
|
||||
{
|
||||
ptr = fd_start;
|
||||
while (ptr != NULL) {
|
||||
int progress_made = 0;
|
||||
struct disk_driver *dd;
|
||||
tapdev_info_t *info = ptr->s->ring_info;
|
||||
|
||||
td_for_each_disk(ptr->s, dd) {
|
||||
if (dd->io_fd[READ] &&
|
||||
FD_ISSET(dd->io_fd[READ],
|
||||
&readfds)) {
|
||||
io_done(dd, READ);
|
||||
progress_made = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* completed io from above may have
|
||||
* queued new requests on chained disks */
|
||||
if (progress_made) {
|
||||
td_for_each_disk(ptr->s, dd) {
|
||||
dd->early +=
|
||||
dd->drv->td_submit(dd);
|
||||
if (dd->early > 0) {
|
||||
io_done(dd,
|
||||
MAX_IOFD + 1);
|
||||
dd->early = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (FD_ISSET(ptr->tap_fd, &readfds) ||
|
||||
(info->busy.req && progress_made))
|
||||
get_io_request(ptr->s);
|
||||
|
||||
ptr = ptr->next;
|
||||
}
|
||||
|
||||
if (FD_ISSET(fds[READ], &readfds))
|
||||
read_msg(buf);
|
||||
}
|
||||
}
|
||||
free(buf);
|
||||
close(fds[READ]);
|
||||
close(fds[WRITE]);
|
||||
|
||||
ptr = fd_start;
|
||||
while (ptr != NULL) {
|
||||
s = ptr->s;
|
||||
unmap_disk(s);
|
||||
close(ptr->tap_fd);
|
||||
ptr = ptr->next;
|
||||
}
|
||||
closelog();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,259 +0,0 @@
|
||||
/* tapdisk.h
|
||||
*
|
||||
* Generic disk interface for blktap-based image adapters.
|
||||
*
|
||||
* (c) 2006 Andrew Warfield and Julian Chesterfield
|
||||
*
|
||||
* 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. The end of a back is marked with a call to:
|
||||
*
|
||||
* td_submit()
|
||||
*
|
||||
* The disk implementation must provide a file handle, which is used to
|
||||
* indicate that it needs to do work. tapdisk will add this file handle
|
||||
* (returned from td_get_fd()) to it's poll set, and will call into the disk
|
||||
* using td_do_callbacks() whenever there is data pending.
|
||||
*
|
||||
* Two disk implementations demonstrate how this interface may be used to
|
||||
* implement disks with both asynchronous and synchronous calls. block-aio.c
|
||||
* maps this interface down onto the linux libaio calls, while block-sync uses
|
||||
* normal posix read/write.
|
||||
*
|
||||
* A few things to realize about the sync case, which doesn't need to defer
|
||||
* io completions:
|
||||
*
|
||||
* - td_queue_[read,write]() call read/write directly, and then call the
|
||||
* callback immediately. The MUST then return a value greater than 0
|
||||
* in order to tell tapdisk that requests have finished early, and to
|
||||
* force responses to be kicked to the clents.
|
||||
*
|
||||
* - The fd used for poll is an otherwise unused pipe, which allows poll to
|
||||
* be safely called without ever returning anything.
|
||||
*
|
||||
* 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 <stdint.h>
|
||||
#include <syslog.h>
|
||||
#include <stdio.h>
|
||||
#include "blktaplib.h"
|
||||
|
||||
/*If enabled, log all debug messages to syslog*/
|
||||
#if 1
|
||||
#define DPRINTF(_f, _a...) syslog( LOG_DEBUG, __FILE__ ":%d: " _f , __LINE__, ## _a )
|
||||
#else
|
||||
#define DPRINTF(_f, _a...) ((void)0)
|
||||
#endif
|
||||
|
||||
/* Things disks need to know about, these should probably be in a higher-level
|
||||
* header. */
|
||||
#define MAX_SEGMENTS_PER_REQ 11
|
||||
#define SECTOR_SHIFT 9
|
||||
#define DEFAULT_SECTOR_SIZE 512
|
||||
|
||||
#define MAX_IOFD 2
|
||||
|
||||
#define BLK_NOT_ALLOCATED 99
|
||||
#define TD_NO_PARENT 1
|
||||
|
||||
typedef uint32_t td_flag_t;
|
||||
|
||||
#define TD_RDONLY 1
|
||||
|
||||
struct td_state;
|
||||
struct tap_disk;
|
||||
|
||||
struct disk_id {
|
||||
char *name;
|
||||
int drivertype;
|
||||
};
|
||||
|
||||
struct disk_driver {
|
||||
int early;
|
||||
char *name;
|
||||
void *private;
|
||||
td_flag_t flags;
|
||||
int io_fd[MAX_IOFD];
|
||||
struct tap_disk *drv;
|
||||
struct td_state *td_state;
|
||||
struct disk_driver *next;
|
||||
};
|
||||
|
||||
/* This structure represents the state of an active virtual disk. */
|
||||
struct td_state {
|
||||
struct disk_driver *disks;
|
||||
void *blkif;
|
||||
void *image;
|
||||
void *ring_info;
|
||||
void *fd_entry;
|
||||
uint64_t sector_size;
|
||||
uint64_t size;
|
||||
unsigned int info;
|
||||
};
|
||||
|
||||
/* Prototype of the callback to activate as requests complete. */
|
||||
typedef int (*td_callback_t)(struct disk_driver *dd, int res, uint64_t sector,
|
||||
int nb_sectors, int id, void *private);
|
||||
|
||||
/* 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;
|
||||
int private_data_size;
|
||||
int (*td_open) (struct disk_driver *dd,
|
||||
const char *name, td_flag_t flags);
|
||||
int (*td_queue_read) (struct disk_driver *dd, uint64_t sector,
|
||||
int nb_sectors, char *buf, td_callback_t cb,
|
||||
int id, void *prv);
|
||||
int (*td_queue_write) (struct disk_driver *dd, uint64_t sector,
|
||||
int nb_sectors, char *buf, td_callback_t cb,
|
||||
int id, void *prv);
|
||||
int (*td_submit) (struct disk_driver *dd);
|
||||
int (*td_close) (struct disk_driver *dd);
|
||||
int (*td_do_callbacks) (struct disk_driver *dd, int sid);
|
||||
int (*td_get_parent_id) (struct disk_driver *dd, struct disk_id *id);
|
||||
int (*td_validate_parent)(struct disk_driver *dd,
|
||||
struct disk_driver *p, td_flag_t flags);
|
||||
};
|
||||
|
||||
typedef struct disk_info {
|
||||
int idnum;
|
||||
char name[50]; /* e.g. "RAMDISK" */
|
||||
char handle[10]; /* xend handle, e.g. 'ram' */
|
||||
int single_handler; /* is there a single controller for all */
|
||||
/* instances of disk type? */
|
||||
int use_ioemu; /* backend provider: 0 = tapdisk; 1 = ioemu */
|
||||
|
||||
#ifdef TAPDISK
|
||||
struct tap_disk *drv;
|
||||
#endif
|
||||
} disk_info_t;
|
||||
|
||||
void debug_fe_ring(struct td_state *s);
|
||||
|
||||
extern struct tap_disk tapdisk_aio;
|
||||
extern struct tap_disk tapdisk_sync;
|
||||
extern struct tap_disk tapdisk_vmdk;
|
||||
extern struct tap_disk tapdisk_ram;
|
||||
extern struct tap_disk tapdisk_qcow;
|
||||
extern struct tap_disk tapdisk_qcow2;
|
||||
|
||||
|
||||
/*Define Individual Disk Parameters here */
|
||||
static disk_info_t aio_disk = {
|
||||
DISK_TYPE_AIO,
|
||||
"raw image (aio)",
|
||||
"aio",
|
||||
0,
|
||||
0,
|
||||
#ifdef TAPDISK
|
||||
&tapdisk_aio,
|
||||
#endif
|
||||
};
|
||||
|
||||
static disk_info_t sync_disk = {
|
||||
DISK_TYPE_SYNC,
|
||||
"raw image (sync)",
|
||||
"sync",
|
||||
0,
|
||||
0,
|
||||
#ifdef TAPDISK
|
||||
&tapdisk_sync,
|
||||
#endif
|
||||
};
|
||||
|
||||
static disk_info_t vmdk_disk = {
|
||||
DISK_TYPE_VMDK,
|
||||
"vmware image (vmdk)",
|
||||
"vmdk",
|
||||
1,
|
||||
0,
|
||||
#ifdef TAPDISK
|
||||
&tapdisk_vmdk,
|
||||
#endif
|
||||
};
|
||||
|
||||
static disk_info_t ram_disk = {
|
||||
DISK_TYPE_RAM,
|
||||
"ramdisk image (ram)",
|
||||
"ram",
|
||||
1,
|
||||
0,
|
||||
#ifdef TAPDISK
|
||||
&tapdisk_ram,
|
||||
#endif
|
||||
};
|
||||
|
||||
static disk_info_t qcow_disk = {
|
||||
DISK_TYPE_QCOW,
|
||||
"qcow disk (qcow)",
|
||||
"qcow",
|
||||
0,
|
||||
0,
|
||||
#ifdef TAPDISK
|
||||
&tapdisk_qcow,
|
||||
#endif
|
||||
};
|
||||
|
||||
static disk_info_t qcow2_disk = {
|
||||
DISK_TYPE_QCOW2,
|
||||
"qcow2 disk (qcow2)",
|
||||
"qcow2",
|
||||
0,
|
||||
0,
|
||||
#ifdef TAPDISK
|
||||
&tapdisk_qcow2,
|
||||
#endif
|
||||
};
|
||||
|
||||
/*Main disk info array */
|
||||
static disk_info_t *dtypes[] = {
|
||||
&aio_disk,
|
||||
&sync_disk,
|
||||
&vmdk_disk,
|
||||
&ram_disk,
|
||||
&qcow_disk,
|
||||
&qcow2_disk,
|
||||
};
|
||||
|
||||
typedef struct driver_list_entry {
|
||||
struct blkif *blkif;
|
||||
struct driver_list_entry **pprev, *next;
|
||||
} driver_list_entry_t;
|
||||
|
||||
typedef struct fd_list_entry {
|
||||
int cookie;
|
||||
int tap_fd;
|
||||
struct td_state *s;
|
||||
struct fd_list_entry **pprev, *next;
|
||||
} fd_list_entry_t;
|
||||
|
||||
int qcow_create(const char *filename, uint64_t total_size,
|
||||
const char *backing_file, int flags);
|
||||
|
||||
int qcow2_create(const char *filename, uint64_t total_size,
|
||||
const char *backing_file, int flags);
|
||||
#endif /*TAPDISK_H_*/
|
||||
@@ -1,60 +0,0 @@
|
||||
XEN_ROOT = $(CURDIR)/../../..
|
||||
include $(XEN_ROOT)/tools/Rules.mk
|
||||
|
||||
MAJOR = 3.0
|
||||
MINOR = 0
|
||||
SONAME = libblktap.so.$(MAJOR)
|
||||
|
||||
CFLAGS += -I.
|
||||
CFLAGS += $(CFLAGS_libxenctrl)
|
||||
CFLAGS += $(CFLAGS_libxenstore)
|
||||
LDLIBS += $(LDLIBS_libxenstore)
|
||||
|
||||
SRCS :=
|
||||
SRCS += xenbus.c blkif.c xs_api.c
|
||||
|
||||
CFLAGS += -Werror
|
||||
CFLAGS += -Wno-unused
|
||||
CFLAGS += -fPIC
|
||||
# get asprintf():
|
||||
CFLAGS += -D _GNU_SOURCE
|
||||
|
||||
OBJS = $(SRCS:.c=.o)
|
||||
OBJS_PIC = $(SRCS:.c=.opic)
|
||||
IBINS :=
|
||||
|
||||
LIB = libblktap.a
|
||||
LIB_SO = libblktap.so.$(MAJOR).$(MINOR)
|
||||
|
||||
.PHONY: all
|
||||
all: $(LIB) $(LIB_SO)
|
||||
|
||||
.PHONY: install
|
||||
install: all
|
||||
$(INSTALL_DIR) $(DESTDIR)$(LIBDIR)
|
||||
$(INSTALL_DIR) $(DESTDIR)$(INCLUDEDIR)
|
||||
$(INSTALL_PROG) $(LIB_SO) $(DESTDIR)$(LIBDIR)
|
||||
$(INSTALL_DATA) $(LIB) $(DESTDIR)$(LIBDIR)
|
||||
ln -sf libblktap.so.$(MAJOR).$(MINOR) $(DESTDIR)$(LIBDIR)/libblktap.so.$(MAJOR)
|
||||
ln -sf libblktap.so.$(MAJOR) $(DESTDIR)$(LIBDIR)/libblktap.so
|
||||
$(INSTALL_DATA) blktaplib.h $(DESTDIR)$(INCLUDEDIR)
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -rf *.a *.so* *.o *.opic *.rpm $(LIB) $(LIB_SO) *~ $(DEPS) xen TAGS
|
||||
|
||||
libblktap.so.$(MAJOR).$(MINOR): $(OBJS_PIC)
|
||||
$(CC) $(LDFLAGS) -Wl,$(SONAME_LDFLAG) -Wl,$(SONAME) $(SHLIB_LDFLAGS) \
|
||||
-o $@ $^ $(LDLIBS)
|
||||
ln -sf libblktap.so.$(MAJOR).$(MINOR) libblktap.so.$(MAJOR)
|
||||
ln -sf libblktap.so.$(MAJOR) libblktap.so
|
||||
|
||||
libblktap.a: $(OBJS)
|
||||
$(AR) rc $@ $^
|
||||
|
||||
.PHONY: TAGS
|
||||
TAGS:
|
||||
etags -t $(SRCS) *.h
|
||||
|
||||
-include $(DEPS)
|
||||
|
||||
@@ -1,185 +0,0 @@
|
||||
/*
|
||||
* tools/blktap_user/blkif.c
|
||||
*
|
||||
* The blkif interface for blktap. A blkif describes an in-use virtual disk.
|
||||
* (c) 2005 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <err.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "blktaplib.h"
|
||||
|
||||
#if 0
|
||||
#define DPRINTF(_f, _a...) printf ( _f , ## _a )
|
||||
#else
|
||||
#define DPRINTF(_f, _a...) ((void)0)
|
||||
#endif
|
||||
|
||||
#define BLKIF_HASHSZ 1024
|
||||
#define BLKIF_HASH(_d,_h) (((int)(_d)^(int)(_h))&(BLKIF_HASHSZ-1))
|
||||
|
||||
static blkif_t *blkif_hash[BLKIF_HASHSZ];
|
||||
|
||||
blkif_t *blkif_find_by_handle(domid_t domid, unsigned int handle)
|
||||
{
|
||||
blkif_t *blkif = blkif_hash[BLKIF_HASH(domid, handle)];
|
||||
while ( (blkif != NULL) &&
|
||||
((blkif->domid != domid) || (blkif->handle != handle)) )
|
||||
blkif = blkif->hash_next;
|
||||
return blkif;
|
||||
}
|
||||
|
||||
blkif_t *alloc_blkif(domid_t domid)
|
||||
{
|
||||
blkif_t *blkif;
|
||||
DPRINTF("Alloc_blkif called [%d]\n",domid);
|
||||
blkif = (blkif_t *)malloc(sizeof(blkif_t));
|
||||
if (!blkif)
|
||||
return NULL;
|
||||
memset(blkif, 0, sizeof(*blkif));
|
||||
blkif->domid = domid;
|
||||
blkif->devnum = -1;
|
||||
return blkif;
|
||||
}
|
||||
|
||||
/*Controller callbacks*/
|
||||
static int (*new_devmap_hook)(blkif_t *blkif) = NULL;
|
||||
void register_new_devmap_hook(int (*fn)(blkif_t *blkif))
|
||||
{
|
||||
new_devmap_hook = fn;
|
||||
}
|
||||
|
||||
static int (*new_unmap_hook)(blkif_t *blkif) = NULL;
|
||||
void register_new_unmap_hook(int (*fn)(blkif_t *blkif))
|
||||
{
|
||||
new_unmap_hook = fn;
|
||||
}
|
||||
|
||||
static int (*new_blkif_hook)(blkif_t *blkif) = NULL;
|
||||
void register_new_blkif_hook(int (*fn)(blkif_t *blkif))
|
||||
{
|
||||
new_blkif_hook = fn;
|
||||
}
|
||||
|
||||
int blkif_init(blkif_t *blkif, long int handle, long int pdev,
|
||||
long int readonly)
|
||||
{
|
||||
domid_t domid;
|
||||
blkif_t **pblkif;
|
||||
int devnum;
|
||||
|
||||
if (blkif == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
domid = blkif->domid;
|
||||
blkif->handle = handle;
|
||||
blkif->pdev = pdev;
|
||||
blkif->readonly = readonly;
|
||||
|
||||
/*
|
||||
* Call out to the new_blkif_hook.
|
||||
* The tap application should define this,
|
||||
* and it should return having set blkif->ops
|
||||
*
|
||||
*/
|
||||
if (new_blkif_hook == NULL)
|
||||
{
|
||||
DPRINTF("Probe detected a new blkif, but no new_blkif_hook!");
|
||||
return -1;
|
||||
}
|
||||
if (new_blkif_hook(blkif)!=0) {
|
||||
DPRINTF("BLKIF: Image open failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Now wire it in. */
|
||||
pblkif = &blkif_hash[BLKIF_HASH(domid, handle)];
|
||||
DPRINTF("Created hash entry: %d [%d,%ld]\n",
|
||||
BLKIF_HASH(domid, handle), domid, handle);
|
||||
|
||||
while ( *pblkif != NULL )
|
||||
{
|
||||
if ( ((*pblkif)->domid == domid) &&
|
||||
((*pblkif)->handle == handle) )
|
||||
{
|
||||
DPRINTF("Could not create blkif: already exists\n");
|
||||
return -1;
|
||||
}
|
||||
pblkif = &(*pblkif)->hash_next;
|
||||
}
|
||||
blkif->hash_next = NULL;
|
||||
*pblkif = blkif;
|
||||
|
||||
if (new_devmap_hook == NULL)
|
||||
{
|
||||
DPRINTF("Probe setting up new blkif but no devmap hook!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
devnum = new_devmap_hook(blkif);
|
||||
if (devnum == -1)
|
||||
return -1;
|
||||
blkif->devnum = devnum;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void free_blkif(blkif_t *blkif)
|
||||
{
|
||||
blkif_t **pblkif, *curs;
|
||||
image_t *image;
|
||||
|
||||
pblkif = &blkif_hash[BLKIF_HASH(blkif->domid, blkif->handle)];
|
||||
while ( (curs = *pblkif) != NULL )
|
||||
{
|
||||
if ( blkif == curs )
|
||||
{
|
||||
*pblkif = curs->hash_next;
|
||||
}
|
||||
pblkif = &curs->hash_next;
|
||||
}
|
||||
if (blkif != NULL) {
|
||||
if ((image=(image_t *)blkif->prv)!=NULL) {
|
||||
free(blkif->prv);
|
||||
}
|
||||
if (blkif->info!=NULL) {
|
||||
free(blkif->info);
|
||||
}
|
||||
if (new_unmap_hook != NULL) new_unmap_hook(blkif);
|
||||
free(blkif);
|
||||
}
|
||||
}
|
||||
|
||||
void __init_blkif(void)
|
||||
{
|
||||
memset(blkif_hash, 0, sizeof(blkif_hash));
|
||||
}
|
||||
@@ -1,240 +0,0 @@
|
||||
/* blktaplib.h
|
||||
*
|
||||
* Blktap library userspace code.
|
||||
*
|
||||
* (c) 2005 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.
|
||||
*/
|
||||
|
||||
#ifndef __BLKTAPLIB_H__
|
||||
#define __BLKTAPLIB_H__
|
||||
|
||||
#include <xenctrl.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/user.h>
|
||||
#include <xen/xen.h>
|
||||
#include <xen/io/blkif.h>
|
||||
#include <xen/io/ring.h>
|
||||
#include <xenstore.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.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_NEWINTF_EXT 50
|
||||
#define BLKTAP_IOCTL_PRINT_IDXS 100
|
||||
|
||||
/* 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 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;
|
||||
|
||||
typedef struct {
|
||||
blkif_request_t req;
|
||||
struct blkif *blkif;
|
||||
int submitting;
|
||||
int secs_pending;
|
||||
int16_t status;
|
||||
} pending_req_t;
|
||||
|
||||
struct blkif_ops {
|
||||
unsigned long long (*get_size)(struct blkif *blkif);
|
||||
unsigned long (*get_secsize)(struct blkif *blkif);
|
||||
unsigned int (*get_info)(struct blkif *blkif);
|
||||
};
|
||||
|
||||
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 */
|
||||
void *info; /*Image parameter passing */
|
||||
pending_req_t pending_list[MAX_REQUESTS];
|
||||
int devnum;
|
||||
int fds[2];
|
||||
int be_id;
|
||||
int major;
|
||||
int minor;
|
||||
pid_t tappid;
|
||||
int drivertype;
|
||||
uint16_t cookie;
|
||||
} blkif_t;
|
||||
|
||||
typedef struct blkif_info {
|
||||
char *params;
|
||||
} blkif_info_t;
|
||||
|
||||
void register_new_devmap_hook(int (*fn)(blkif_t *blkif));
|
||||
void register_new_unmap_hook(int (*fn)(blkif_t *blkif));
|
||||
void register_new_blkif_hook(int (*fn)(blkif_t *blkif));
|
||||
blkif_t *blkif_find_by_handle(domid_t domid, unsigned int handle);
|
||||
blkif_t *alloc_blkif(domid_t domid);
|
||||
int blkif_init(blkif_t *blkif, long int handle, long int pdev,
|
||||
long int readonly);
|
||||
void free_blkif(blkif_t *blkif);
|
||||
void __init_blkif(void);
|
||||
|
||||
typedef struct busy_state {
|
||||
int seg_idx;
|
||||
blkif_request_t *req;
|
||||
} busy_state_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;
|
||||
busy_state_t busy;
|
||||
} tapdev_info_t;
|
||||
|
||||
typedef struct domid_translate {
|
||||
unsigned short domid;
|
||||
unsigned short busid;
|
||||
} domid_translate_t ;
|
||||
|
||||
typedef struct domid_translate_ext {
|
||||
unsigned short domid;
|
||||
uint32_t busid;
|
||||
} domid_translate_ext_t ;
|
||||
|
||||
typedef struct image {
|
||||
unsigned long long size;
|
||||
unsigned long secsize;
|
||||
unsigned int info;
|
||||
} image_t;
|
||||
|
||||
/* 16-byte message header, immediately followed by message payload. */
|
||||
typedef struct msg_hdr {
|
||||
uint16_t type;
|
||||
uint16_t len;
|
||||
uint16_t drivertype;
|
||||
uint16_t cookie;
|
||||
uint8_t readonly;
|
||||
uint8_t pad[7];
|
||||
} msg_hdr_t;
|
||||
|
||||
typedef struct msg_newdev {
|
||||
uint8_t devnum;
|
||||
uint16_t domid;
|
||||
} msg_newdev_t;
|
||||
|
||||
typedef struct msg_pid {
|
||||
pid_t pid;
|
||||
} msg_pid_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
|
||||
|
||||
/* disk driver types */
|
||||
#define MAX_DISK_TYPES 20
|
||||
|
||||
#define DISK_TYPE_AIO 0
|
||||
#define DISK_TYPE_SYNC 1
|
||||
#define DISK_TYPE_VMDK 2
|
||||
#define DISK_TYPE_RAM 3
|
||||
#define DISK_TYPE_QCOW 4
|
||||
#define DISK_TYPE_QCOW2 5
|
||||
|
||||
/* xenstore/xenbus: */
|
||||
#define DOMNAME "Domain-0"
|
||||
int setup_probe_watch(struct xs_handle *h);
|
||||
|
||||
|
||||
/* Abitrary values, must match the underlying driver... */
|
||||
#define MAX_TAP_DEV 100
|
||||
|
||||
/* 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()))
|
||||
|
||||
|
||||
#endif /* __BLKTAPLIB_H__ */
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* list.h
|
||||
*
|
||||
* This is a subset of linux's list.h intended to be used in user-space.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __LIST_H__
|
||||
#define __LIST_H__
|
||||
|
||||
#ifdef LIST_HEAD
|
||||
#undef LIST_HEAD
|
||||
#endif
|
||||
|
||||
#define LIST_POISON1 ((void *) 0x00100100)
|
||||
#define LIST_POISON2 ((void *) 0x00200200)
|
||||
|
||||
struct list_head {
|
||||
struct list_head *next, *prev;
|
||||
};
|
||||
|
||||
#define LIST_HEAD_INIT(name) { &(name), &(name) }
|
||||
|
||||
#define LIST_HEAD(name) \
|
||||
struct list_head name = LIST_HEAD_INIT(name)
|
||||
|
||||
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_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;
|
||||
}
|
||||
#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))
|
||||
|
||||
#endif /* __LIST_H__ */
|
||||
@@ -1,617 +0,0 @@
|
||||
/*
|
||||
* xenbus.c
|
||||
*
|
||||
* xenbus interface to the blocktap.
|
||||
*
|
||||
* this handles the top-half of integration with block devices through the
|
||||
* store -- the tap driver negotiates the device channel etc, while the
|
||||
* userland tap client needs to sort out the disk parameters etc.
|
||||
*
|
||||
* (c) 2005 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <err.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#include <xenstore.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include "blktaplib.h"
|
||||
#include "list.h"
|
||||
#include "xs_api.h"
|
||||
|
||||
#if 0
|
||||
#define DPRINTF(_f, _a...) printf ( _f , ## _a )
|
||||
#else
|
||||
#define DPRINTF(_f, _a...) ((void)0)
|
||||
#endif
|
||||
|
||||
struct backend_info
|
||||
{
|
||||
/* our communications channel */
|
||||
blkif_t *blkif;
|
||||
|
||||
long int frontend_id;
|
||||
long int pdev;
|
||||
long int readonly;
|
||||
|
||||
char *backpath;
|
||||
char *frontpath;
|
||||
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
static LIST_HEAD(belist);
|
||||
|
||||
static int strsep_len(const char *str, char c, unsigned int len)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; str[i]; i++)
|
||||
if (str[i] == c) {
|
||||
if (len == 0)
|
||||
return i;
|
||||
len--;
|
||||
}
|
||||
return (len == 0) ? i : -ERANGE;
|
||||
}
|
||||
|
||||
static int get_be_id(const char *str)
|
||||
{
|
||||
int len,end;
|
||||
const char *ptr;
|
||||
char *tptr, num[10];
|
||||
|
||||
len = strsep_len(str, '/', 6);
|
||||
end = strlen(str);
|
||||
if( (len < 0) || (end < 0) ) return -1;
|
||||
|
||||
ptr = str + len + 1;
|
||||
strncpy(num, ptr, end - len);
|
||||
tptr = num + (end - (len + 1));
|
||||
*tptr = '\0';
|
||||
|
||||
return atoi(num);
|
||||
}
|
||||
|
||||
static int get_be_domid(const char *str)
|
||||
{
|
||||
int len1, len2;
|
||||
const char *ptr;
|
||||
char *tptr, num[10];
|
||||
|
||||
len2 = strsep_len(str, '/', 3);
|
||||
if ( len2 < 0 ) return -1;
|
||||
len1 = strsep_len(str, '/', 2);
|
||||
|
||||
ptr = str + len1 + 1;
|
||||
strncpy(num, ptr, len2 - len1 - 1);
|
||||
tptr = num + (len2 - len1 - 1);
|
||||
*tptr = '\0';
|
||||
|
||||
return atoi(num);
|
||||
}
|
||||
|
||||
static struct backend_info *be_lookup_be(const char *bepath)
|
||||
{
|
||||
struct backend_info *be;
|
||||
|
||||
list_for_each_entry(be, &belist, list)
|
||||
if (strcmp(bepath, be->backpath) == 0)
|
||||
return be;
|
||||
return (struct backend_info *)NULL;
|
||||
}
|
||||
|
||||
static int be_exists_be(const char *bepath)
|
||||
{
|
||||
return (be_lookup_be(bepath) != NULL);
|
||||
}
|
||||
|
||||
static struct backend_info *be_lookup_fe(const char *fepath)
|
||||
{
|
||||
struct backend_info *be;
|
||||
|
||||
list_for_each_entry(be, &belist, list)
|
||||
if (strcmp(fepath, be->frontpath) == 0)
|
||||
return be;
|
||||
return (struct backend_info *)NULL;
|
||||
}
|
||||
|
||||
static int backend_remove(struct xs_handle *h, struct backend_info *be)
|
||||
{
|
||||
/* Unhook from be list. */
|
||||
list_del(&be->list);
|
||||
|
||||
/* Free everything else. */
|
||||
if (be->blkif) {
|
||||
DPRINTF("Freeing blkif dev [%d]\n",be->blkif->devnum);
|
||||
free_blkif(be->blkif);
|
||||
}
|
||||
if (be->frontpath)
|
||||
free(be->frontpath);
|
||||
if (be->backpath)
|
||||
free(be->backpath);
|
||||
free(be);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *get_image_path(const char *path)
|
||||
{
|
||||
const char *tmp;
|
||||
|
||||
/* Strip off the image type */
|
||||
if (!strncmp(path, "tapdisk:", strlen("tapdisk:"))) {
|
||||
path += strlen("tapdisk:");
|
||||
} else if (!strncmp(path, "ioemu:", strlen("ioemu:"))) {
|
||||
path += strlen("ioemu:");
|
||||
}
|
||||
|
||||
tmp = strchr(path, ':');
|
||||
if (tmp != NULL)
|
||||
path = tmp + 1;
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
static int check_sharing(struct xs_handle *h, struct backend_info *be)
|
||||
{
|
||||
char *dom_uuid;
|
||||
char *cur_dom_uuid;
|
||||
char *path;
|
||||
char *mode;
|
||||
char *params;
|
||||
char **domains;
|
||||
char **devices;
|
||||
int i, j;
|
||||
unsigned int num_dom, num_dev;
|
||||
blkif_info_t *info = be->blkif->info;
|
||||
int ret = 0;
|
||||
const char *image_path[2];
|
||||
int be_domid = get_be_domid(be->backpath);
|
||||
|
||||
image_path[0] = get_image_path(info->params);
|
||||
|
||||
/* If the mode contains '!' or doesn't contain 'w' don't check anything */
|
||||
xs_gather(h, be->backpath, "mode", NULL, &mode, NULL);
|
||||
if (strchr(mode, '!'))
|
||||
goto out;
|
||||
if (strchr(mode, 'w') == NULL)
|
||||
goto out;
|
||||
|
||||
/* Get the UUID of the domain we want to attach to */
|
||||
if (asprintf(&path, "/local/domain/%ld", be->frontend_id) == -1)
|
||||
goto fail;
|
||||
xs_gather(h, path, "vm", NULL, &dom_uuid, NULL);
|
||||
free(path);
|
||||
|
||||
/* Iterate through the devices of all VMs */
|
||||
if (asprintf(&path, "/local/domain/%d/backend/tap", be_domid) == -1)
|
||||
goto fail;
|
||||
domains = xs_directory(h, XBT_NULL, path, &num_dom);
|
||||
free(path);
|
||||
if (domains == NULL)
|
||||
num_dom = 0;
|
||||
|
||||
for (i = 0; !ret && (i < num_dom); i++) {
|
||||
|
||||
/* If it's the same VM, no action needed */
|
||||
if (asprintf(&path, "/local/domain/%s", domains[i]) == -1) {
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
cur_dom_uuid = NULL;
|
||||
xs_gather(h, path, "vm", NULL, &cur_dom_uuid, NULL);
|
||||
free(path);
|
||||
if (!cur_dom_uuid)
|
||||
continue;
|
||||
|
||||
if (!strcmp(cur_dom_uuid, dom_uuid)) {
|
||||
free(cur_dom_uuid);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check the devices */
|
||||
if (asprintf(&path, "/local/domain/%d/backend/tap/%s", be_domid, domains[i]) == -1) {
|
||||
ret = -1;
|
||||
free(cur_dom_uuid);
|
||||
break;
|
||||
}
|
||||
devices = xs_directory(h, XBT_NULL, path, &num_dev);
|
||||
if (devices == NULL)
|
||||
num_dev = 0;
|
||||
free(path);
|
||||
|
||||
for (j = 0; !ret && (j < num_dev); j++) {
|
||||
if (asprintf(&path, "/local/domain/%d/backend/tap/%s/%s", be_domid, domains[i], devices[j]) == -1) {
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
params = NULL;
|
||||
xs_gather(h, path, "params", NULL, ¶ms, NULL);
|
||||
free(path);
|
||||
if (!params)
|
||||
continue;
|
||||
|
||||
image_path[1] = get_image_path(params);
|
||||
if (!strcmp(image_path[0], image_path[1])) {
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
free(params);
|
||||
}
|
||||
|
||||
free(cur_dom_uuid);
|
||||
free(devices);
|
||||
}
|
||||
free(domains);
|
||||
free(dom_uuid);
|
||||
goto out;
|
||||
|
||||
fail:
|
||||
ret = -1;
|
||||
out:
|
||||
free(mode);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int check_image(struct xs_handle *h, struct backend_info *be,
|
||||
const char** errmsg)
|
||||
{
|
||||
const char *path;
|
||||
int mode;
|
||||
blkif_t *blkif = be->blkif;
|
||||
blkif_info_t *info = blkif->info;
|
||||
|
||||
path = get_image_path(info->params);
|
||||
|
||||
/* Check if the image exists and access is permitted */
|
||||
mode = R_OK;
|
||||
if (!be->readonly)
|
||||
mode |= W_OK;
|
||||
if (access(path, mode)) {
|
||||
if (errno == ENOENT)
|
||||
*errmsg = "File not found.";
|
||||
else
|
||||
*errmsg = "Insufficient file permissions.";
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check that the image is not attached to a different VM */
|
||||
if (check_sharing(h, be)) {
|
||||
*errmsg = "File already in use by other domain";
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ueblktap_setup(struct xs_handle *h, char *bepath)
|
||||
{
|
||||
struct backend_info *be;
|
||||
char *path = NULL, *p,*dev;
|
||||
int len, er, deverr;
|
||||
long int pdev = 0, handle;
|
||||
blkif_info_t *blk;
|
||||
const char* errmsg = NULL;
|
||||
|
||||
be = be_lookup_be(bepath);
|
||||
if (be == NULL)
|
||||
{
|
||||
DPRINTF("ERROR: backend changed called for nonexistent "
|
||||
"backend! (%s)\n", bepath);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
deverr = xs_gather(h, bepath, "physical-device", "%li", &pdev, NULL);
|
||||
if (!deverr) {
|
||||
DPRINTF("pdev set to %ld\n",pdev);
|
||||
if (be->pdev && be->pdev != pdev) {
|
||||
DPRINTF("changing physical-device not supported");
|
||||
goto fail;
|
||||
}
|
||||
be->pdev = pdev;
|
||||
}
|
||||
|
||||
/* Check to see if device is to be opened read-only. */
|
||||
deverr = xs_gather(h, bepath, "mode", NULL, &path, NULL);
|
||||
if (deverr) {
|
||||
DPRINTF("ERROR: could not find read/write mode\n");
|
||||
goto fail;
|
||||
} else if (path[0] == 'r')
|
||||
be->readonly = 1;
|
||||
|
||||
if (be->blkif == NULL) {
|
||||
/* Front end dir is a number, which is used as the handle. */
|
||||
p = strrchr(be->frontpath, '/') + 1;
|
||||
handle = strtoul(p, NULL, 0);
|
||||
|
||||
be->blkif = alloc_blkif(be->frontend_id);
|
||||
if (be->blkif == NULL)
|
||||
goto fail;
|
||||
|
||||
be->blkif->be_id = get_be_id(bepath);
|
||||
|
||||
/* Insert device specific info, */
|
||||
blk = malloc(sizeof(blkif_info_t));
|
||||
if (!blk) {
|
||||
DPRINTF("Out of memory - blkif_info_t\n");
|
||||
goto fail;
|
||||
}
|
||||
er = xs_gather(h, bepath, "params", NULL, &blk->params, NULL);
|
||||
if (er)
|
||||
goto fail;
|
||||
be->blkif->info = blk;
|
||||
|
||||
if (deverr) {
|
||||
/*Dev number was not available, try to set manually*/
|
||||
pdev = convert_dev_name_to_num(blk->params);
|
||||
be->pdev = pdev;
|
||||
}
|
||||
|
||||
if (check_image(h, be, &errmsg))
|
||||
goto fail;
|
||||
|
||||
er = blkif_init(be->blkif, handle, be->pdev, be->readonly);
|
||||
if (er != 0) {
|
||||
DPRINTF("Unable to open device %s\n",blk->params);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
DPRINTF("[BECHG]: ADDED A NEW BLKIF (%s)\n", bepath);
|
||||
}
|
||||
|
||||
/* Supply the information about the device to xenstore */
|
||||
er = xs_printf(h, be->backpath, "sectors", "%llu",
|
||||
be->blkif->ops->get_size(be->blkif));
|
||||
|
||||
if (er == 0) {
|
||||
DPRINTF("ERROR: Failed writing sectors");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
er = xs_printf(h, be->backpath, "sector-size", "%lu",
|
||||
be->blkif->ops->get_secsize(be->blkif));
|
||||
|
||||
if (er == 0) {
|
||||
DPRINTF("ERROR: Failed writing sector-size");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
er = xs_printf(h, be->backpath, "info", "%u",
|
||||
be->blkif->ops->get_info(be->blkif));
|
||||
|
||||
if (er == 0) {
|
||||
DPRINTF("ERROR: Failed writing info");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
be->blkif->state = CONNECTED;
|
||||
xs_printf(h, be->backpath, "hotplug-status", "connected");
|
||||
|
||||
DPRINTF("[SETUP] Complete\n\n");
|
||||
goto close;
|
||||
|
||||
fail:
|
||||
if (be) {
|
||||
if (errmsg == NULL)
|
||||
errmsg = "Setting up the backend failed. See the log "
|
||||
"files in /var/log/xen/ for details.";
|
||||
xs_printf(h, be->backpath, "hotplug-error", errmsg);
|
||||
xs_printf(h, be->backpath, "hotplug-status", "error");
|
||||
|
||||
backend_remove(h, be);
|
||||
}
|
||||
close:
|
||||
if (path)
|
||||
free(path);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Xenstore watch callback entry point. This code replaces the hotplug scripts,
|
||||
* and as soon as the xenstore backend driver entries are created, this script
|
||||
* gets called.
|
||||
*/
|
||||
static void ueblktap_probe(struct xs_handle *h, struct xenbus_watch *w,
|
||||
const char *bepath_im)
|
||||
{
|
||||
struct backend_info *be = NULL;
|
||||
char *frontend = NULL, *bepath = NULL, *p;
|
||||
int er, len;
|
||||
blkif_t *blkif;
|
||||
|
||||
|
||||
bepath = strdup(bepath_im);
|
||||
|
||||
if (!bepath) {
|
||||
DPRINTF("No path\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
*asserts that xenstore structure is always 7 levels deep
|
||||
*e.g. /local/domain/0/backend/vbd/1/2049
|
||||
*/
|
||||
len = strsep_len(bepath, '/', 7);
|
||||
if (len < 0)
|
||||
goto free_be;
|
||||
if (bepath[len] != '\0')
|
||||
goto free_be;
|
||||
|
||||
be = malloc(sizeof(*be));
|
||||
if (!be) {
|
||||
DPRINTF("ERROR: allocating backend structure\n");
|
||||
goto free_be;
|
||||
}
|
||||
memset(be, 0, sizeof(*be));
|
||||
frontend = NULL;
|
||||
|
||||
er = xs_gather(h, bepath,
|
||||
"frontend-id", "%li", &be->frontend_id,
|
||||
"frontend", NULL, &frontend,
|
||||
NULL);
|
||||
|
||||
if (er) {
|
||||
/*
|
||||
*Unable to find frontend entries,
|
||||
*bus-id is no longer valid
|
||||
*/
|
||||
DPRINTF("ERROR: Frontend-id check failed, removing backend: "
|
||||
"[%s]\n",bepath);
|
||||
|
||||
/**
|
||||
* BE info should already exist,
|
||||
* free new mem and find old entry
|
||||
*/
|
||||
free(be);
|
||||
be = be_lookup_be(bepath);
|
||||
if ( (be != NULL) && (be->blkif != NULL) )
|
||||
backend_remove(h, be);
|
||||
else goto free_be;
|
||||
if (bepath)
|
||||
free(bepath);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Are we already tracking this device? */
|
||||
if (be_exists_be(bepath))
|
||||
goto free_be;
|
||||
|
||||
be->backpath = bepath;
|
||||
be->frontpath = frontend;
|
||||
|
||||
list_add(&be->list, &belist);
|
||||
|
||||
DPRINTF("[PROBE]\tADDED NEW DEVICE (%s)\n", bepath);
|
||||
DPRINTF("\tFRONTEND (%s),(%ld)\n", frontend,be->frontend_id);
|
||||
|
||||
ueblktap_setup(h, bepath);
|
||||
return;
|
||||
|
||||
free_be:
|
||||
if (frontend)
|
||||
free(frontend);
|
||||
if (bepath)
|
||||
free(bepath);
|
||||
if (be)
|
||||
free(be);
|
||||
}
|
||||
|
||||
/**
|
||||
*We set a general watch on the backend vbd directory
|
||||
*ueblktap_probe is called for every update
|
||||
*Our job is to monitor for new entries. As they
|
||||
*are created, we initalise the state and attach a disk.
|
||||
*/
|
||||
|
||||
static int add_blockdevice_probe_watch(struct xs_handle *h, const char *domid)
|
||||
{
|
||||
char *path;
|
||||
struct xenbus_watch *vbd_watch;
|
||||
|
||||
if (asprintf(&path, "/local/domain/%s/backend/tap", domid) == -1)
|
||||
return -ENOMEM;
|
||||
|
||||
vbd_watch = (struct xenbus_watch *)malloc(sizeof(struct xenbus_watch));
|
||||
if (!vbd_watch) {
|
||||
DPRINTF("ERROR: unable to malloc vbd_watch [%s]\n", path);
|
||||
return -EINVAL;
|
||||
}
|
||||
vbd_watch->node = path;
|
||||
vbd_watch->callback = ueblktap_probe;
|
||||
if (register_xenbus_watch(h, vbd_watch) != 0) {
|
||||
DPRINTF("ERROR: adding vbd probe watch %s\n", path);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Asynch callback to check for /local/domain/<DOMID>/name */
|
||||
static void check_dom(struct xs_handle *h, struct xenbus_watch *w,
|
||||
const char *bepath_im)
|
||||
{
|
||||
char *domid;
|
||||
|
||||
domid = get_dom_domid(h);
|
||||
if (domid == NULL)
|
||||
return;
|
||||
|
||||
add_blockdevice_probe_watch(h, domid);
|
||||
free(domid);
|
||||
unregister_xenbus_watch(h, w);
|
||||
}
|
||||
|
||||
/* We must wait for xend to register /local/domain/<DOMID> */
|
||||
static int watch_for_domid(struct xs_handle *h)
|
||||
{
|
||||
struct xenbus_watch *domid_watch;
|
||||
char *path = NULL;
|
||||
|
||||
if (asprintf(&path, "/local/domain") == -1)
|
||||
return -ENOMEM;
|
||||
|
||||
domid_watch = malloc(sizeof(struct xenbus_watch));
|
||||
if (domid_watch == NULL) {
|
||||
DPRINTF("ERROR: unable to malloc domid_watch [%s]\n", path);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
domid_watch->node = path;
|
||||
domid_watch->callback = check_dom;
|
||||
|
||||
if (register_xenbus_watch(h, domid_watch) != 0) {
|
||||
DPRINTF("ERROR: adding vbd probe watch %s\n", path);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
DPRINTF("Set async watch for /local/domain\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int setup_probe_watch(struct xs_handle *h)
|
||||
{
|
||||
char *domid;
|
||||
int ret;
|
||||
|
||||
domid = get_dom_domid(h);
|
||||
if (domid == NULL)
|
||||
return watch_for_domid(h);
|
||||
|
||||
ret = add_blockdevice_probe_watch(h, domid);
|
||||
free(domid);
|
||||
return ret;
|
||||
}
|
||||
@@ -1,360 +0,0 @@
|
||||
/*
|
||||
* xs_api.c
|
||||
*
|
||||
* blocktap interface functions to xenstore
|
||||
*
|
||||
* (c) 2005 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <err.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#include <xenstore.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include "blktaplib.h"
|
||||
#include "list.h"
|
||||
#include "xs_api.h"
|
||||
|
||||
#if 0
|
||||
#define DPRINTF(_f, _a...) printf ( _f , ## _a )
|
||||
#else
|
||||
#define DPRINTF(_f, _a...) ((void)0)
|
||||
#endif
|
||||
|
||||
static LIST_HEAD(watches);
|
||||
#define BASE_DEV_VAL 2048
|
||||
|
||||
int xs_gather(struct xs_handle *xs, const char *dir, ...)
|
||||
{
|
||||
va_list ap;
|
||||
const char *name;
|
||||
char *path, **e;
|
||||
int ret = 0, num,i;
|
||||
unsigned int len;
|
||||
xs_transaction_t xth;
|
||||
|
||||
again:
|
||||
if ( (xth = xs_transaction_start(xs)) == XBT_NULL) {
|
||||
DPRINTF("unable to start xs trasanction\n");
|
||||
ret = ENOMEM;
|
||||
return ret;
|
||||
}
|
||||
|
||||
va_start(ap, dir);
|
||||
while ( (ret == 0) && (name = va_arg(ap, char *)) != NULL) {
|
||||
const char *fmt = va_arg(ap, char *);
|
||||
void *result = va_arg(ap, void *);
|
||||
char *p;
|
||||
|
||||
if (asprintf(&path, "%s/%s", dir, name) == -1)
|
||||
{
|
||||
printf("allocation error in xs_gather!\n");
|
||||
ret = ENOMEM;
|
||||
break;
|
||||
}
|
||||
|
||||
p = xs_read(xs, xth, path, &len);
|
||||
|
||||
|
||||
free(path);
|
||||
if (p == NULL) {
|
||||
ret = ENOENT;
|
||||
break;
|
||||
}
|
||||
if (fmt) {
|
||||
if (sscanf(p, fmt, result) == 0)
|
||||
ret = EINVAL;
|
||||
free(p);
|
||||
} else
|
||||
*(char **)result = p;
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
if (!xs_transaction_end(xs, xth, ret)) {
|
||||
if (ret == 0 && errno == EAGAIN)
|
||||
goto again;
|
||||
else
|
||||
ret = errno;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Single printf and write: returns -errno or 0. */
|
||||
int xs_printf(struct xs_handle *h, const char *dir, const char *node,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
char *buf, *path;
|
||||
va_list ap;
|
||||
int ret;
|
||||
|
||||
va_start(ap, fmt);
|
||||
ret = vasprintf(&buf, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (ret == -1)
|
||||
return ENOMEM;
|
||||
if (asprintf(&path, "%s/%s", dir, node) == -1) {
|
||||
free(buf);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = xs_write(h, XBT_NULL, path, buf, strlen(buf));
|
||||
|
||||
free(buf);
|
||||
free(path);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int xs_exists(struct xs_handle *h, const char *path)
|
||||
{
|
||||
char **d;
|
||||
unsigned int num;
|
||||
xs_transaction_t xth;
|
||||
|
||||
if ( (xth = xs_transaction_start(h)) == XBT_NULL) {
|
||||
printf("unable to start xs trasanction\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
d = xs_directory(h, xth, path, &num);
|
||||
xs_transaction_end(h, xth, 0);
|
||||
if (d == NULL)
|
||||
return 0;
|
||||
free(d);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This assumes that the domain name we are looking for is unique.
|
||||
* Name parameter Domain-0
|
||||
*/
|
||||
char *get_dom_domid(struct xs_handle *h)
|
||||
{
|
||||
char **e, *val, *domid = NULL;
|
||||
unsigned int num, len;
|
||||
int i;
|
||||
char *path;
|
||||
xs_transaction_t xth;
|
||||
|
||||
if ( (xth = xs_transaction_start(h)) == XBT_NULL) {
|
||||
warn("unable to start xs trasanction\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
e = xs_directory(h, xth, "/local/domain", &num);
|
||||
if (e == NULL)
|
||||
goto done;
|
||||
|
||||
for (i = 0; (i < num) && (domid == NULL); i++) {
|
||||
if (asprintf(&path, "/local/domain/%s/name", e[i]) == -1)
|
||||
break;
|
||||
val = xs_read(h, xth, path, &len);
|
||||
free(path);
|
||||
if (val == NULL)
|
||||
continue;
|
||||
|
||||
if (strcmp(val, DOMNAME) == 0) {
|
||||
/* match! */
|
||||
if (asprintf(&path, "/local/domain/%s/domid", e[i]) == -1) {
|
||||
free(val);
|
||||
break;
|
||||
}
|
||||
domid = xs_read(h, xth, path, &len);
|
||||
free(path);
|
||||
}
|
||||
free(val);
|
||||
}
|
||||
done:
|
||||
xs_transaction_end(h, xth, 0);
|
||||
if (e)
|
||||
free(e);
|
||||
return domid;
|
||||
}
|
||||
|
||||
int convert_dev_name_to_num(char *name) {
|
||||
char *p, *ptr;
|
||||
int majors[10] = {3,22,33,34,56,57,88,89,90,91};
|
||||
int maj,i,ret = 0;
|
||||
char *p_sd = "/dev/sd";
|
||||
char *p_hd = "/dev/hd";
|
||||
char *p_xvd = "/dev/xvd";
|
||||
char *p_plx = "plx";
|
||||
char *alpha = "abcdefghijklmnop";
|
||||
|
||||
if (strstr(name, p_sd) != NULL) {
|
||||
p = name + strlen(p_sd);
|
||||
for(i = 0, ptr = alpha; i < strlen(alpha); i++) {
|
||||
if(*ptr == *p)
|
||||
break;
|
||||
*ptr++;
|
||||
}
|
||||
*p++;
|
||||
ret = BASE_DEV_VAL + (16*i) + atoi(p);
|
||||
} else if (strstr(name, p_hd) != NULL) {
|
||||
p = name + strlen(p_hd);
|
||||
for (i = 0, ptr = alpha; i < strlen(alpha); i++) {
|
||||
if(*ptr == *p) break;
|
||||
*ptr++;
|
||||
}
|
||||
*p++;
|
||||
ret = (majors[i/2]*256) + atoi(p);
|
||||
|
||||
} else if (strstr(name, p_xvd) != NULL) {
|
||||
p = name + strlen(p_xvd);
|
||||
for(i = 0, ptr = alpha; i < strlen(alpha); i++) {
|
||||
if(*ptr == *p) break;
|
||||
*ptr++;
|
||||
}
|
||||
*p++;
|
||||
ret = (202*256) + (16*i) + atoi(p);
|
||||
|
||||
} else if (strstr(name, p_plx) != NULL) {
|
||||
p = name + strlen(p_plx);
|
||||
ret = atoi(p);
|
||||
|
||||
} else {
|
||||
DPRINTF("Unknown device type, setting to default.\n");
|
||||
ret = BASE_DEV_VAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* A little paranoia: we don't just trust token.
|
||||
*/
|
||||
static struct xenbus_watch *find_watch(const char *token)
|
||||
{
|
||||
struct xenbus_watch *i, *cmp;
|
||||
|
||||
cmp = (void *)strtoul(token, NULL, 16);
|
||||
|
||||
list_for_each_entry(i, &watches, list)
|
||||
if (i == cmp)
|
||||
return i;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register callback to watch this node.
|
||||
* like xs_watch, return 0 on failure
|
||||
*/
|
||||
int register_xenbus_watch(struct xs_handle *h, struct xenbus_watch *watch)
|
||||
{
|
||||
/* Pointer in ascii is the token. */
|
||||
char token[sizeof(watch) * 2 + 1];
|
||||
|
||||
snprintf(token, sizeof(token), "%lX", (long)watch);
|
||||
if (find_watch(token)) {
|
||||
DPRINTF("watch collision!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!xs_watch(h, watch->node, token)) {
|
||||
DPRINTF("unable to set watch!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
list_add(&watch->list, &watches);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int unregister_xenbus_watch(struct xs_handle *h, struct xenbus_watch *watch)
|
||||
{
|
||||
char token[sizeof(watch) * 2 + 1];
|
||||
|
||||
snprintf(token, sizeof(token), "%lX", (long)watch);
|
||||
if (!find_watch(token)) {
|
||||
DPRINTF("no such watch!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!xs_unwatch(h, watch->node, token))
|
||||
DPRINTF("XENBUS Failed to release watch %s\n",
|
||||
watch->node);
|
||||
|
||||
list_del(&watch->list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-register callbacks to all watches.
|
||||
*/
|
||||
void reregister_xenbus_watches(struct xs_handle *h)
|
||||
{
|
||||
struct xenbus_watch *watch;
|
||||
char token[sizeof(watch) * 2 + 1];
|
||||
|
||||
list_for_each_entry(watch, &watches, list) {
|
||||
snprintf(token, sizeof(token), "%lX", (long)watch);
|
||||
xs_watch(h, watch->node, token);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* based on watch_thread()
|
||||
*/
|
||||
int xs_fire_next_watch(struct xs_handle *h)
|
||||
{
|
||||
char **res;
|
||||
char *token;
|
||||
char *node = NULL;
|
||||
struct xenbus_watch *w;
|
||||
int er;
|
||||
unsigned int num;
|
||||
|
||||
res = xs_read_watch(h, &num);
|
||||
if (res == NULL)
|
||||
return -EAGAIN; /* in O_NONBLOCK, read_watch returns 0... */
|
||||
|
||||
node = res[XS_WATCH_PATH];
|
||||
token = res[XS_WATCH_TOKEN];
|
||||
|
||||
w = find_watch(token);
|
||||
if (w)
|
||||
w->callback(h, w, node);
|
||||
|
||||
free(res);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* xs_api.h
|
||||
*
|
||||
* (c) 2005 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.
|
||||
*/
|
||||
|
||||
struct xenbus_watch
|
||||
{
|
||||
struct list_head list;
|
||||
char *node;
|
||||
void (*callback)(struct xs_handle *h,
|
||||
struct xenbus_watch *,
|
||||
const char *node);
|
||||
};
|
||||
|
||||
int xs_gather(struct xs_handle *xs, const char *dir, ...);
|
||||
int xs_printf(struct xs_handle *h, const char *dir, const char *node,
|
||||
const char *fmt, ...);
|
||||
int xs_exists(struct xs_handle *h, const char *path);
|
||||
char *get_dom_domid(struct xs_handle *h);
|
||||
int convert_dev_name_to_num(char *name);
|
||||
int register_xenbus_watch(struct xs_handle *h, struct xenbus_watch *watch);
|
||||
int unregister_xenbus_watch(struct xs_handle *h, struct xenbus_watch *watch);
|
||||
void reregister_xenbus_watches(struct xs_handle *h);
|
||||
int xs_fire_next_watch(struct xs_handle *h);
|
||||